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
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=