packages

Vous avez besoin des packages suivants.

library(lubridate)

Attachement du package : ‘lubridate’

The following object is masked from ‘package:base’:

    date

Cox model

Stanford Heart Transplant data

Survival of patients on the waiting list for the Stanford heart transplant program.

  • accept.dt: acceptance into program

  • fustat: dead or alive

  • surgery: prior bypass surgery

  • age: age (in years)

  • futime: followup time

  • wait.time: time before transplant

  • transplant: transplant indicator

On sélectionne les variables accept.dt, fustat, surgery,age,futime,wait.time,transplant

On transforme la variable accept.dt en gardant seulement l’année.

jasa = dplyr::mutate(jasa, accept.yr = year(ymd(accept.dt)))
jasa = dplyr::select(jasa,   fustat, surgery,age,futime,wait.time,transplant,accept.yr)

head(jasa)

Un premier modèle avec les variables non-dépendantes du temps.

coxph(Surv(futime,fustat) ~ accept.yr + surgery + age, data = jasa)
Call:
coxph(formula = Surv(futime, fustat) ~ accept.yr + surgery + 
    age, data = jasa)

             coef exp(coef) se(coef)     z     p
accept.yr -0.1320    0.8764   0.0681 -1.94 0.053
surgery   -0.6427    0.5259   0.3673 -1.75 0.080
age        0.0276    1.0280   0.0134  2.06 0.039

Likelihood ratio test=14.5  on 3 df, p=0.00226
n= 103, number of events= 75 

Avec les variables dépendantes du temps.

Mauvaise paramétrisation avec la variable transplant

autoplot(survfit(Surv(futime,fustat) ~transplant , data = jasa))

coxph(Surv(futime,fustat) ~ surgery + transplant + age , data = jasa)
Call:
coxph(formula = Surv(futime, fustat) ~ surgery + transplant + 
    age, data = jasa)

              coef exp(coef) se(coef)     z       p
surgery    -0.4190    0.6577   0.3712 -1.13    0.26
transplant -1.7171    0.1796   0.2785 -6.16 7.1e-10
age         0.0589    1.0607   0.0150  3.91 9.1e-05

Likelihood ratio test=45.9  on 3 df, p=6.11e-10
n= 103, number of events= 75 

Un nouveau format pour gérer les variables “time-dependent”

Pour un individu sans transplantation

jasa[1,]
jasa1[1,]

Pour un individu avec transplantation

jasa[4,]
jasa1[jasa1$id==4,]
coxph(Surv(start, stop, event) ~ age + surgery + transplant + year ,jasa1)
Call:
coxph(formula = Surv(start, stop, event) ~ age + surgery + transplant + 
    year, data = jasa1)

              coef exp(coef) se(coef)     z     p
age         0.0272    1.0276   0.0137  1.98 0.047
surgery    -0.6371    0.5288   0.3672 -1.73 0.083
transplant -0.0129    0.9872   0.3133 -0.04 0.967
year       -0.1464    0.8638   0.0705 -2.08 0.038

Likelihood ratio test=15.1  on 4 df, p=0.00447
n= 170, number of events= 75 

Quand \(p\) grandit : on régularise, par exemple via l’elasticnet

data("nki70")

model_matrix = model.matrix( ~ as.factor(Grade)  + . - Grade - 1   , data = nki70[3:77])


X = model_matrix[,-1]


elasticnet_solution = cv.glmnet(X,Surv(nki70$time, nki70$event), family = "cox" , alpha = 0.5,penalty.factor = c(rep(0,6),rep(1,70)))

coef(elasticnet_solution)

Quand \(n\) grandit : les problèmes se compliquent

Un exemple sur simulations

n = 100
p = 20
X = matrix(rnorm(n*p,0,0.1),n,p)
beta = runif(p)
Xbeta = X%*%beta

Dans un modèle logistique

Y_logistic = rbinom(n=n,size=1,prob = exp(Xbeta)/(1+exp(Xbeta)))

Dans un modèle de Cox

Y_cox = rexp(n=n, exp(-Xbeta))

Timings

n_s = c(100,1000,10000,100000)
# times_logistic = matrix(0,5,50)
# times_cox = matrix(0,5,50)
# for (m in 1:50){
# k=1
# for (n in n_s){
#   X = matrix(rnorm(n*p,0,0.1),n,p)
#   beta = runif(p)
#   Xbeta = X%*%beta
#   Y_logistic = rbinom(n=n,size=1,prob = exp(Xbeta)/(1+exp(Xbeta)))
#   Y_cox = rexp(n=n, exp(-Xbeta))
#   # Z = rexp(n=n, 1)
#   # Y = min(Y_cox,Z)
#   # delta = (Y <= Z)
#   start.time <- Sys.time()
#   cv.glmnet(X,Y_logistic, family = "binomial" , alpha = 0.8)
#   end.time <- Sys.time()
#   times_logistic[k,m] <- end.time - start.time
#   start.time <- Sys.time()
#   cv.glmnet(X,Surv(Y_cox,rep(1,n)), family = "cox" , alpha = 0.8)
#   end.time <- Sys.time()
#   times_cox[k,m] <- end.time - start.time
#   k = k+1 
# }}
# write.table(times_logistic,"times_logistic")
# write.table(times_cox,"times_cox")

Représentation graphique

times_cox = read.table("times_cox")
times_logistic = read.table("times_logistic")
plot(n_s,rowMeans(times_cox)[1:4],type="l",ylab="temps de calcul")
lines(n_s,rowMeans(times_logistic)[1:4],col="red")
legend(x =0, y=15,lty = c(1,1),legend = c("Cox","logistic"), col = c("black","red"))

rowMeans(times_cox)[1:4]
         1          2          3          4 
 0.1096696  0.2761387  1.8763669 22.2677560 
rowMeans(times_logistic)[1:4]
         1          2          3          4 
 0.1209730  0.2581053  1.4016656 15.3722102 

Régression de Poisson et de Poisson conditionelle

Récupérer les données simulées

Elles contiennent des observations pour 10000 individus sur 5 ans avec une mesure par semaine. J’ai reproduit des données de santé de type pharmacovigilance.

On s’intéresse à la survenue d’une pathologie. On suspecte qu’un traitement augmente ce risque, proportionnellement à la dose cumulée reçue par l’individu.

Chaque individu commence (à un moment aléatoire sur les 5 ans) le traitement.

Les autres variables mesurées sont le sexe de l’individu et une variable continue (qui dans la simulation n’a pas d’influence sur le risque).

Pour chaque individu et chaque semaine soit \(10 000*5*52 = 2 600 000\) lignes de données, ont été enregistrés

  • l’identifiant de l’individu (ind)

  • le numéro de la semaine (week)

  • la dose cumulée (cum_dose)

  • le sexe (sex)

  • la variable continue (var)

  • le nombre d’évènements survenus dans la semaine (nevent)

  • une variable d’identification de la ligne (id)

paste(c(yourpath,"data_example_poisson.Rdata"),collapse = "")
[1] "~/Dropbox/Exposes/Formation IA/Rexamples/DATA/data_example_poisson.Rdata"

Seuls 26 individus ont développé (au moins une fois) la pathologie.

data_sum = data %>%  group_by(ind) %>% summarise (neventS = sum(nevent))
sum(data_sum$neventS >0)  
[1] 26

En régression de Poisson simple

start.time <- Sys.time()
outcomes  = select(data,id,ind,week,nevent)
#head(outcomes)
colnames(outcomes) = c("rowId","stratumId","time","y")
#head(outcomes)
covariates = select(data,id,ind,cum_dose,var,sex)
covariates = gather(covariates , "covariateId","covariateValue" ,2:4)
covariates$covariateId = 1*(covariates$covariateId =="cum_dose") + 2 * ((covariates$covariateId == "var")) +  3 * ((covariates$covariateId == "sex"))
colnames(covariates) = c("rowId","stratumId","covariateId","covariateValue")
cyclopsData <- convertToCyclopsData(outcomes, covariates, modelType = "pr",addIntercept = FALSE)
fit <- fitCyclopsModel(cyclopsData, prior = createPrior("laplace"))
coefs = coef(fit)
names(coefs) = c("cum_dose","var","sex")
print(coefs)
    cum_dose          var          sex 
 -0.27576300 -53.41001632  -0.07756173 
end.time <- Sys.time()
time.taken <- end.time - start.time
print(time.taken)
Time difference of 17.34703 secs

En régression de Poisson conditionelle

start.time <- Sys.time()
data_cond = filter(data,ind %in% data_sum$ind[data_sum$neventS >0])
outcomes  = select(data_cond,id,ind,week,nevent)
colnames(outcomes) = c("rowId","stratumId","time","y")
covariates = select(data_cond,id,ind,cum_dose,var)
covariates = gather(covariates , "covariateId","covariateValue" ,3:4)
covariates$covariateId = 1*(covariates$covariateId =="cum_dose") + 2 * ((covariates$covariateId == "var"))
colnames(covariates) = c("rowId","stratumId","covariateId","covariateValue")
cyclopsData <- convertToCyclopsData(outcomes, covariates, modelType = "cpr",addIntercept = FALSE)
fit <- fitCyclopsModel(cyclopsData, prior = createPrior("laplace"))
coefs = coef(fit)
names(coefs) = c("cum_dose","sex")
print(coefs)
  cum_dose        sex 
-0.7105996  0.0000000 
end.time <- Sys.time()
time.taken <- end.time - start.time
print(time.taken)
Time difference of 0.328902 secs
LS0tCnRpdGxlOiAiRXhhbXBsZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgo8IS0tIC0tLSAtLT4KPCEtLSB0aXRsZTogIkV4YW1wbGVzIiAtLT4KPCEtLSBvdXRwdXQ6IC0tPgo8IS0tICAgYmVhbWVyX3ByZXNlbnRhdGlvbjogLS0+CjwhLS0gICAgIGtlZXBfdGV4OiB0cnVlIC0tPgo8IS0tIC0tLSAtLT4KIyMjIHBhY2thZ2VzClZvdXMgYXZleiBiZXNvaW4gZGVzIHBhY2thZ2VzIHN1aXZhbnRzLgpgYGB7cn0KbGlicmFyeShzdXJ2aXZhbCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dmb3J0aWZ5KQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShnbG1uZXQpCmxpYnJhcnkocGVuYWxpemVkKQpsaWJyYXJ5KEN5Y2xvcHMpCmBgYAoKCiMgQ294IG1vZGVsCiMjIyBTdGFuZm9yZCBIZWFydCBUcmFuc3BsYW50IGRhdGEKU3Vydml2YWwgb2YgcGF0aWVudHMgb24gdGhlIHdhaXRpbmcgbGlzdCBmb3IgdGhlIFN0YW5mb3JkIGhlYXJ0IHRyYW5zcGxhbnQgcHJvZ3JhbS4KCiAgKyBhY2NlcHQuZHQ6CWFjY2VwdGFuY2UgaW50byBwcm9ncmFtCiAgCiAgKyBmdXN0YXQ6CWRlYWQgb3IgYWxpdmUKCiAgKyBzdXJnZXJ5OiBwcmlvciBieXBhc3Mgc3VyZ2VyeQogIAogICsgYWdlOglhZ2UgKGluIHllYXJzKQoKICArIGZ1dGltZToJZm9sbG93dXAgdGltZQoKICArIHdhaXQudGltZToJdGltZSBiZWZvcmUgdHJhbnNwbGFudAoKICArIHRyYW5zcGxhbnQ6CXRyYW5zcGxhbnQgaW5kaWNhdG9yCiAgCgojIwpPbiBzw6lsZWN0aW9ubmUgbGVzIHZhcmlhYmxlcyBgYWNjZXB0LmR0LCAgZnVzdGF0LCBzdXJnZXJ5LGFnZSxmdXRpbWUsd2FpdC50aW1lLHRyYW5zcGxhbnRgCmBgYHtyfQpkYXRhKGhlYXJ0KQpqYXNhID0gZHBseXI6OnNlbGVjdChqYXNhLCBhY2NlcHQuZHQsICBmdXN0YXQsIHN1cmdlcnksYWdlLGZ1dGltZSx3YWl0LnRpbWUsdHJhbnNwbGFudCkKaGVhZChqYXNhKQpgYGAKIyMKT24gdHJhbnNmb3JtZSBsYSB2YXJpYWJsZSBgYWNjZXB0LmR0YCBlbiBnYXJkYW50IHNldWxlbWVudCBsJ2FubsOpZS4KYGBge3J9Cmphc2EgPSBkcGx5cjo6bXV0YXRlKGphc2EsIGFjY2VwdC55ciA9IHllYXIoeW1kKGFjY2VwdC5kdCkpKQpqYXNhID0gZHBseXI6OnNlbGVjdChqYXNhLCAgIGZ1c3RhdCwgc3VyZ2VyeSxhZ2UsZnV0aW1lLHdhaXQudGltZSx0cmFuc3BsYW50LGFjY2VwdC55cikKCmhlYWQoamFzYSkKYGBgCgoKIyMgVW4gcHJlbWllciBtb2TDqGxlIGF2ZWMgbGVzIHZhcmlhYmxlcyBub24tZMOpcGVuZGFudGVzIGR1IHRlbXBzLgoKYGBge3J9CmNveHBoKFN1cnYoZnV0aW1lLGZ1c3RhdCkgfiBhY2NlcHQueXIgKyBzdXJnZXJ5ICsgYWdlLCBkYXRhID0gamFzYSkKYGBgCgoKIyMgQXZlYyBsZXMgdmFyaWFibGVzIGTDqXBlbmRhbnRlcyBkdSB0ZW1wcy4KIyMjIE1hdXZhaXNlIHBhcmFtw6l0cmlzYXRpb24gYXZlYyBsYSB2YXJpYWJsZSBgdHJhbnNwbGFudGAKYGBge3J9CmF1dG9wbG90KHN1cnZmaXQoU3VydihmdXRpbWUsZnVzdGF0KSB+dHJhbnNwbGFudCAsIGRhdGEgPSBqYXNhKSkKYGBgCgojIwpgYGB7cn0KY294cGgoU3VydihmdXRpbWUsZnVzdGF0KSB+IHN1cmdlcnkgKyB0cmFuc3BsYW50ICsgYWdlICwgZGF0YSA9IGphc2EpCmBgYAoKCiMjIFVuIG5vdXZlYXUgZm9ybWF0IHBvdXIgZ8OpcmVyIGxlcyB2YXJpYWJsZXMgInRpbWUtZGVwZW5kZW50IgpgYGB7cn0KaGVhZChqYXNhMSkKYGBgCgojIwpQb3VyIHVuIGluZGl2aWR1IHNhbnMgdHJhbnNwbGFudGF0aW9uCmBgYHtyfQpqYXNhWzEsXQpqYXNhMVsxLF0KYGBgCgojIwpQb3VyIHVuIGluZGl2aWR1IGF2ZWMgdHJhbnNwbGFudGF0aW9uCmBgYHtyfQpqYXNhWzQsXQpqYXNhMVtqYXNhMSRpZD09NCxdCmBgYAoKYGBge3J9CmNveHBoKFN1cnYoc3RhcnQsIHN0b3AsIGV2ZW50KSB+IGFnZSArIHN1cmdlcnkgKyB0cmFuc3BsYW50ICsgeWVhciAsamFzYTEpCmBgYAoKIyBRdWFuZCAkcCQgZ3JhbmRpdCA6IG9uIHLDqWd1bGFyaXNlLCBwYXIgZXhlbXBsZSB2aWEgbCdlbGFzdGljbmV0CmBgYHtyfQpkYXRhKCJua2k3MCIpCgptb2RlbF9tYXRyaXggPSBtb2RlbC5tYXRyaXgoIH4gYXMuZmFjdG9yKEdyYWRlKSAgKyAuIC0gR3JhZGUgLSAxICAgLCBkYXRhID0gbmtpNzBbMzo3N10pCgoKWCA9IG1vZGVsX21hdHJpeFssLTFdCgoKZWxhc3RpY25ldF9zb2x1dGlvbiA9IGN2LmdsbW5ldChYLFN1cnYobmtpNzAkdGltZSwgbmtpNzAkZXZlbnQpLCBmYW1pbHkgPSAiY294IiAsIGFscGhhID0gMC41LHBlbmFsdHkuZmFjdG9yID0gYyhyZXAoMCw2KSxyZXAoMSw3MCkpKQoKY29lZihlbGFzdGljbmV0X3NvbHV0aW9uKQoKYGBgCgojIFF1YW5kICRuJCBncmFuZGl0IDogbGVzIHByb2Jsw6htZXMgc2UgY29tcGxpcXVlbnQKCiMjIFVuIGV4ZW1wbGUgc3VyIHNpbXVsYXRpb25zCmBgYHtyfQpuID0gMTAwCnAgPSAyMApYID0gbWF0cml4KHJub3JtKG4qcCwwLDAuMSksbixwKQpiZXRhID0gcnVuaWYocCkKWGJldGEgPSBYJSolYmV0YQpgYGAKCiMjIERhbnMgdW4gbW9kw6hsZSBsb2dpc3RpcXVlCmBgYHtyfQpZX2xvZ2lzdGljID0gcmJpbm9tKG49bixzaXplPTEscHJvYiA9IGV4cChYYmV0YSkvKDErZXhwKFhiZXRhKSkpCmBgYAoKIyMgRGFucyB1biBtb2TDqGxlIGRlIENveCAKYGBge3J9CllfY294ID0gcmV4cChuPW4sIGV4cCgtWGJldGEpKQpgYGAKCiMjIFRpbWluZ3MKYGBge3J9Cm5fcyA9IGMoMTAwLDEwMDAsMTAwMDAsMTAwMDAwKQojIHRpbWVzX2xvZ2lzdGljID0gbWF0cml4KDAsNSw1MCkKIyB0aW1lc19jb3ggPSBtYXRyaXgoMCw1LDUwKQojIGZvciAobSBpbiAxOjUwKXsKIyBrPTEKIyBmb3IgKG4gaW4gbl9zKXsKIyAgIFggPSBtYXRyaXgocm5vcm0obipwLDAsMC4xKSxuLHApCiMgICBiZXRhID0gcnVuaWYocCkKIyAgIFhiZXRhID0gWCUqJWJldGEKIyAgIFlfbG9naXN0aWMgPSByYmlub20obj1uLHNpemU9MSxwcm9iID0gZXhwKFhiZXRhKS8oMStleHAoWGJldGEpKSkKIyAgIFlfY294ID0gcmV4cChuPW4sIGV4cCgtWGJldGEpKQojICAgIyBaID0gcmV4cChuPW4sIDEpCiMgICAjIFkgPSBtaW4oWV9jb3gsWikKIyAgICMgZGVsdGEgPSAoWSA8PSBaKQojICAgc3RhcnQudGltZSA8LSBTeXMudGltZSgpCiMgICBjdi5nbG1uZXQoWCxZX2xvZ2lzdGljLCBmYW1pbHkgPSAiYmlub21pYWwiICwgYWxwaGEgPSAwLjgpCiMgICBlbmQudGltZSA8LSBTeXMudGltZSgpCiMgICB0aW1lc19sb2dpc3RpY1trLG1dIDwtIGVuZC50aW1lIC0gc3RhcnQudGltZQojICAgc3RhcnQudGltZSA8LSBTeXMudGltZSgpCiMgICBjdi5nbG1uZXQoWCxTdXJ2KFlfY294LHJlcCgxLG4pKSwgZmFtaWx5ID0gImNveCIgLCBhbHBoYSA9IDAuOCkKIyAgIGVuZC50aW1lIDwtIFN5cy50aW1lKCkKIyAgIHRpbWVzX2NveFtrLG1dIDwtIGVuZC50aW1lIC0gc3RhcnQudGltZQojICAgayA9IGsrMSAKIyB9fQojIHdyaXRlLnRhYmxlKHRpbWVzX2xvZ2lzdGljLCJ0aW1lc19sb2dpc3RpYyIpCiMgd3JpdGUudGFibGUodGltZXNfY294LCJ0aW1lc19jb3giKQpgYGAKCiMjIFJlcHLDqXNlbnRhdGlvbiBncmFwaGlxdWUKYGBge3J9CnRpbWVzX2NveCA9IHJlYWQudGFibGUoInRpbWVzX2NveCIpCnRpbWVzX2xvZ2lzdGljID0gcmVhZC50YWJsZSgidGltZXNfbG9naXN0aWMiKQpwbG90KG5fcyxyb3dNZWFucyh0aW1lc19jb3gpWzE6NF0sdHlwZT0ibCIseWxhYj0idGVtcHMgZGUgY2FsY3VsIikKbGluZXMobl9zLHJvd01lYW5zKHRpbWVzX2xvZ2lzdGljKVsxOjRdLGNvbD0icmVkIikKbGVnZW5kKHggPTAsIHk9MTUsbHR5ID0gYygxLDEpLGxlZ2VuZCA9IGMoIkNveCIsImxvZ2lzdGljIiksIGNvbCA9IGMoImJsYWNrIiwicmVkIikpCmBgYAoKYGBge3J9CnJvd01lYW5zKHRpbWVzX2NveClbMTo0XQpyb3dNZWFucyh0aW1lc19sb2dpc3RpYylbMTo0XQpgYGAKCgojIFLDqWdyZXNzaW9uIGRlIFBvaXNzb24gZXQgZGUgUG9pc3NvbiBjb25kaXRpb25lbGxlCgojIyBSw6ljdXDDqXJlciBsZXMgZG9ubsOpZXMgc2ltdWzDqWVzCkVsbGVzIGNvbnRpZW5uZW50IGRlcyBvYnNlcnZhdGlvbnMgcG91ciAxMDAwMCBpbmRpdmlkdXMgc3VyIDUgYW5zIGF2ZWMgdW5lIG1lc3VyZSBwYXIgc2VtYWluZS4gSidhaSByZXByb2R1aXQgZGVzIGRvbm7DqWVzIGRlIHNhbnTDqSBkZSB0eXBlIHBoYXJtYWNvdmlnaWxhbmNlLiAKCk9uIHMnaW50w6lyZXNzZSDDoCBsYSBzdXJ2ZW51ZSBkJ3VuZSBwYXRob2xvZ2llLiBPbiBzdXNwZWN0ZSBxdSd1biB0cmFpdGVtZW50IGF1Z21lbnRlIGNlIHJpc3F1ZSwgcHJvcG9ydGlvbm5lbGxlbWVudCDDoCBsYSBkb3NlIGN1bXVsw6llIHJlw6d1ZSBwYXIgbCdpbmRpdmlkdS4KCkNoYXF1ZSBpbmRpdmlkdSBjb21tZW5jZSAow6AgdW4gbW9tZW50IGFsw6lhdG9pcmUgc3VyIGxlcyA1IGFucykgbGUgdHJhaXRlbWVudC4KCkxlcyBhdXRyZXMgdmFyaWFibGVzIG1lc3Vyw6llcyBzb250IGxlIHNleGUgZGUgbCdpbmRpdmlkdSBldCB1bmUgdmFyaWFibGUgY29udGludWUgKHF1aSBkYW5zIGxhIHNpbXVsYXRpb24gbidhIHBhcyBkJ2luZmx1ZW5jZSBzdXIgbGUgcmlzcXVlKS4KClBvdXIgY2hhcXVlIGluZGl2aWR1IGV0IGNoYXF1ZSBzZW1haW5lIHNvaXQgJDEwIDAwMCo1KjUyID0gMiA2MDAgMDAwJCBsaWduZXMgZGUgZG9ubsOpZXMsIG9udCDDqXTDqSBlbnJlZ2lzdHLDqXMKCiAgKyBs4oCZaWRlbnRpZmlhbnQgZGUgbCdpbmRpdmlkdSAoYGluZGApCiAgCiAgKyBsZSBudW3DqXJvIGRlIGxhIHNlbWFpbmUgKGB3ZWVrYCkKICAKICArIGxhIGRvc2UgY3VtdWzDqWUgKGBjdW1fZG9zZWApCiAgCiAgKyBsZSBzZXhlIChgc2V4YCkKICAKICArIGxhIHZhcmlhYmxlIGNvbnRpbnVlIChgdmFyYCkKICAKICArIGxlIG5vbWJyZSBk4oCZw6l2w6huZW1lbnRzIHN1cnZlbnVzIGRhbnMgbGEgc2VtYWluZSAoYG5ldmVudGApCiAgCiAgKyB1bmUgdmFyaWFibGUgZCdpZGVudGlmaWNhdGlvbiBkZSBsYSBsaWduZSAoYGlkYCkKCgpgYGB7cn0KeW91cnBhdGggPSAifi9Ecm9wYm94L0V4cG9zZXMvRm9ybWF0aW9uIElBL1JleGFtcGxlcy9EQVRBLyIKCmRhdGEgPSByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZShjKHlvdXJwYXRoLCJkYXRhX2V4YW1wbGVfcG9pc3Nvbi5SZGF0YSIpLGNvbGxhcHNlID0gIiIpKQpoZWFkKGRhdGEpCmBgYAoKCiMjClNldWxzIDI2IGluZGl2aWR1cyBvbnQgZMOpdmVsb3Bww6kgKGF1IG1vaW5zIHVuZSBmb2lzKSBsYSBwYXRob2xvZ2llLgpgYGB7cn0KZGF0YV9zdW0gPSBkYXRhICU+JSAgZ3JvdXBfYnkoaW5kKSAlPiUgc3VtbWFyaXNlIChuZXZlbnRTID0gc3VtKG5ldmVudCkpCnN1bShkYXRhX3N1bSRuZXZlbnRTID4wKSAgCmBgYAoKIyMgRW4gcsOpZ3Jlc3Npb24gZGUgUG9pc3NvbiBzaW1wbGUKYGBge3J9CgpzdGFydC50aW1lIDwtIFN5cy50aW1lKCkKb3V0Y29tZXMgID0gc2VsZWN0KGRhdGEsaWQsaW5kLHdlZWssbmV2ZW50KQojaGVhZChvdXRjb21lcykKY29sbmFtZXMob3V0Y29tZXMpID0gYygicm93SWQiLCJzdHJhdHVtSWQiLCJ0aW1lIiwieSIpCiNoZWFkKG91dGNvbWVzKQpjb3ZhcmlhdGVzID0gc2VsZWN0KGRhdGEsaWQsaW5kLGN1bV9kb3NlLHZhcixzZXgpCmNvdmFyaWF0ZXMgPSBnYXRoZXIoY292YXJpYXRlcyAsICJjb3ZhcmlhdGVJZCIsImNvdmFyaWF0ZVZhbHVlIiAsMjo0KQpjb3ZhcmlhdGVzJGNvdmFyaWF0ZUlkID0gMSooY292YXJpYXRlcyRjb3ZhcmlhdGVJZCA9PSJjdW1fZG9zZSIpICsgMiAqICgoY292YXJpYXRlcyRjb3ZhcmlhdGVJZCA9PSAidmFyIikpICsgIDMgKiAoKGNvdmFyaWF0ZXMkY292YXJpYXRlSWQgPT0gInNleCIpKQpjb2xuYW1lcyhjb3ZhcmlhdGVzKSA9IGMoInJvd0lkIiwic3RyYXR1bUlkIiwiY292YXJpYXRlSWQiLCJjb3ZhcmlhdGVWYWx1ZSIpCmN5Y2xvcHNEYXRhIDwtIGNvbnZlcnRUb0N5Y2xvcHNEYXRhKG91dGNvbWVzLCBjb3ZhcmlhdGVzLCBtb2RlbFR5cGUgPSAicHIiLGFkZEludGVyY2VwdCA9IEZBTFNFKQpmaXQgPC0gZml0Q3ljbG9wc01vZGVsKGN5Y2xvcHNEYXRhLCBwcmlvciA9IGNyZWF0ZVByaW9yKCJsYXBsYWNlIikpCmNvZWZzID0gY29lZihmaXQpCm5hbWVzKGNvZWZzKSA9IGMoImN1bV9kb3NlIiwidmFyIiwic2V4IikKcHJpbnQoY29lZnMpCmVuZC50aW1lIDwtIFN5cy50aW1lKCkKdGltZS50YWtlbiA8LSBlbmQudGltZSAtIHN0YXJ0LnRpbWUKcHJpbnQodGltZS50YWtlbikKYGBgCgoKIyMgRW4gcsOpZ3Jlc3Npb24gZGUgUG9pc3NvbiBjb25kaXRpb25lbGxlCmBgYHtyfQoKc3RhcnQudGltZSA8LSBTeXMudGltZSgpCmRhdGFfY29uZCA9IGZpbHRlcihkYXRhLGluZCAlaW4lIGRhdGFfc3VtJGluZFtkYXRhX3N1bSRuZXZlbnRTID4wXSkKb3V0Y29tZXMgID0gc2VsZWN0KGRhdGFfY29uZCxpZCxpbmQsd2VlayxuZXZlbnQpCmNvbG5hbWVzKG91dGNvbWVzKSA9IGMoInJvd0lkIiwic3RyYXR1bUlkIiwidGltZSIsInkiKQoKY292YXJpYXRlcyA9IHNlbGVjdChkYXRhX2NvbmQsaWQsaW5kLGN1bV9kb3NlLHZhcikKY292YXJpYXRlcyA9IGdhdGhlcihjb3ZhcmlhdGVzICwgImNvdmFyaWF0ZUlkIiwiY292YXJpYXRlVmFsdWUiICwzOjQpCgpjb3ZhcmlhdGVzJGNvdmFyaWF0ZUlkID0gMSooY292YXJpYXRlcyRjb3ZhcmlhdGVJZCA9PSJjdW1fZG9zZSIpICsgMiAqICgoY292YXJpYXRlcyRjb3ZhcmlhdGVJZCA9PSAidmFyIikpCmNvbG5hbWVzKGNvdmFyaWF0ZXMpID0gYygicm93SWQiLCJzdHJhdHVtSWQiLCJjb3ZhcmlhdGVJZCIsImNvdmFyaWF0ZVZhbHVlIikKY3ljbG9wc0RhdGEgPC0gY29udmVydFRvQ3ljbG9wc0RhdGEob3V0Y29tZXMsIGNvdmFyaWF0ZXMsIG1vZGVsVHlwZSA9ICJjcHIiLGFkZEludGVyY2VwdCA9IEZBTFNFKQpmaXQgPC0gZml0Q3ljbG9wc01vZGVsKGN5Y2xvcHNEYXRhLCBwcmlvciA9IGNyZWF0ZVByaW9yKCJsYXBsYWNlIikpCmNvZWZzID0gY29lZihmaXQpCm5hbWVzKGNvZWZzKSA9IGMoImN1bV9kb3NlIiwic2V4IikKcHJpbnQoY29lZnMpCmVuZC50aW1lIDwtIFN5cy50aW1lKCkKdGltZS50YWtlbiA8LSBlbmQudGltZSAtIHN0YXJ0LnRpbWUKcHJpbnQodGltZS50YWtlbikKYGBgCgo=