First, let’s start by reading in a list of all blood samples at ISB and annotating them with their public ids.
library(arivale.data.interface)
library(data.table)
proteomics <- get_snapshot("proteomics_raw")
isb <- fread("data/isb_blood_samples.csv")
isb[, sample_id := toupper(sample_id)]
isb <- proteomics[, .(public_client_id, sample_id, days_in_program)][isb, on = "sample_id"]
isb <- isb[!is.na(public_client_id)]
isb
Lets filter that for ones with at least two blood draws.
chem <- get_snapshot("chemistries")[vendor == "LCA"]
chem <- chem[isb, on = c("public_client_id", "days_in_program")]
tab <- chem[, table(public_client_id)]
multiple <- isb[public_client_id %chin% names(tab)[tab > 1]]
setkey(multiple, public_client_id)
multiple[, uniqueN(public_client_id)]
[1] 1500
That leaves us with almost 1.5K individuals.
Now for those we extract the weight data.
weights <- get_snapshot("weight")[
public_client_id %chin% multiple$public_client_id &
!is.na(BMI_CALC) & !is.na(WEIGHT_CALC)]
weights[, "weight" := WEIGHT_CALC]
weights[, uniqueN(public_client_id)]
[1] 1497
setkey(weights, public_client_id)
Let’s also get the microbiome samples and chemistries. We will stick only with the DNAGenotek samples.
mb <- get_snapshot("microbiome_diversity")[vendor_dashboard != "Second Genome"]
setkey(mb, "public_client_id")
Now we will do the heavy lifting and merge the blood draws with weight and microbiome measures. For each unique blood draw we will look for the closest microbiome and weight measurement and connect it to the blood draw. We also track the major weight and microbiome indicators.
find_close <- function(pid, days, sid, cutoff = 30) {
wdists <- abs(days - weights[pid, days_in_program])
mbdists <- abs(days - mb[pid, days_in_program])
w_best <- weights[pid, days_in_program[which.min(wdists)]]
w <- weights[pid, weight[which.min(wdists)]]
bmi <- weights[pid, BMI_CALC[which.min(wdists)]]
mb_best <- mb[pid, days_in_program[which.min(mbdists)]]
mb_id <- mb[pid, vendor_observation_id[which.min(mbdists)]]
shannon <- mb[pid, shannon_20000[which.min(mbdists)]]
return(list(
has_close_weight = any(wdists < cutoff, na.rm = TRUE),
has_close_microbiome = any(mbdists < cutoff, na.rm = TRUE),
weight_days_in_program = w_best,
microbiome_days_in_program = mb_best,
weight_diff_days = min(wdists),
microbiome_diff_days = min(mbdists),
weight = w,
bmi = bmi,
shannon = shannon,
microbiome_id = mb_id,
plasma_id = sid))
}
close <- isb[, find_close(public_client_id, days_in_program, sample_id),
by = c("public_client_id", "days_in_program")]
Item 4 of j's result for group 455 is zero length. This will be filled with 2 NAs to match the longest column in this result. Later groups may have a similar problem but only the first is reported to save filling the warning buffer.
close[has_close_weight == T,
has_close := (.N > 1 &
!any(duplicated(weight_days_in_program))),
by = "public_client_id"]
matched <- close[has_close == TRUE]
matched[, table(table(public_client_id))]
2 3 4 5 6
690 363 126 71 2
Now let’s annotate this with BMI and weight loss. First we will only keep those points with the lowest weight after baseline and only those individuals with at least one microbiome sample. Then we annotate the weight loss.
matched <- matched[order(public_client_id, days_in_program),
.SD[c(1, which.min(weight[2:length(weight)]) + 1)],
by = "public_client_id"]
matched[, microbiome_baseline := has_close_microbiome[1], by = "public_client_id"]
matched <- matched[microbiome_baseline == TRUE]
matched[, weight_change := diff(weight), by = "public_client_id"]
matched[, span_days := diff(weight_days_in_program), by = "public_client_id"]
matched[, weight_change_relative := diff(weight) / weight[1] / span_days * 30.5, by = "public_client_id"]
matched
Finally lets annotate the individuals and check whether we have full genomes.
clients <- get_snapshot("clients")
clients[, "has_full_genome" := !is.na(genome_vendor)]
matched <- clients[, .(public_client_id, has_full_genome, sex, region, age)][matched,
on = "public_client_id"]
Finally we remove the samples that we are not allowed to study.
novo <- fread("data/nn_chem.csv")[spreadsheet_resistance == "Resistant Coach spreadsheet"]
matched <- matched[!public_client_id %chin% novo$public_client_id]
matched[, "since_baseline" := days_in_program - min(days_in_program), by="public_client_id"]
library(ggplot2)
theme_set(theme_minimal())
dens <- data.table(
weight_change_relative = density(matched$weight_change_relative)$x,
d = density(matched$weight_change_relative)$y
)
ggplot(dens, aes(x=weight_change_relative, y=d)) +
geom_vline(xintercept=0, lty="dashed") +
geom_line() +
geom_area(data=dens[weight_change_relative < -0.01], fill="royalblue", alpha=0.5) +
geom_area(data=dens[weight_change_relative > 0 & weight_change_relative < 0.001], fill="salmon", alpha=0.5) +
labs(x="relative weight change [%weight/month]", y="density")

ggsave("figures/wc_density.svg", width=5, height=3)
So how many individuals with significant weight loss do we have?
lost <- matched[weight_change_relative < -0.01][order(weight_change_relative)]
fwrite(lost, "successful_weight_loss.csv")
print(lost[, uniqueN(public_client_id)])
[1] 48
lost[, table(sex)]
sex
F M
52 44
To select controls we can use persons with a pretty stable weight. We should avoid the ones with exactly no change in weight since that are often incorrect self-entries of the participants (did not update weight).
controls <- matched[weight_change_relative > 0][order(abs(weight_change_relative))]
fwrite(controls, "no_weight_loss.csv")
And the sample lists for DNAGenotek:
mblost <- lost[seq(1, 30, by = 2),
.(public_client_id, age, sex, microbiome_days_in_program, microbiome_id)]
mblost[, "subset" := "weight loss"]
mbcontrol <- controls[seq(1, 20, by = 2),
.(public_client_id, age, sex, microbiome_days_in_program, microbiome_id)]
mbcontrol[, "subset" := "controls"]
fwrite(rbind(mblost, mbcontrol), "dna_genotek.csv")
And for the blood samples:
plasma_lost <- lost[1:30,
.(public_client_id, age, sex, days_in_program, plasma_id)]
plasma_lost[, "subset" := "weight loss"]
plasma_control <- controls[1:20,
.(public_client_id, age, sex, days_in_program, plasma_id)]
plasma_control[, "subset" := "controls"]
plasma <- rbind(plasma_lost, plasma_control)
plasma <- isb[plasma, on = c(
public_client_id = "public_client_id",
sample_id = "plasma_id",
days_in_program = "days_in_program")]
fwrite(plasma, "plasma_samples.csv")
LS0tCnRpdGxlOiAiSW5ub3ZhdG9yIHNhbXBsZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkZpcnN0LCBsZXQncyBzdGFydCBieSByZWFkaW5nIGluIGEgbGlzdCBvZiBhbGwgYmxvb2Qgc2FtcGxlcyBhdCBJU0IgYW5kIGFubm90YXRpbmcgdGhlbSB3aXRoIHRoZWlyCnB1YmxpYyBpZHMuCgpgYGB7cn0KbGlicmFyeShhcml2YWxlLmRhdGEuaW50ZXJmYWNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCgpwcm90ZW9taWNzIDwtIGdldF9zbmFwc2hvdCgicHJvdGVvbWljc19yYXciKQppc2IgPC0gZnJlYWQoImRhdGEvaXNiX2Jsb29kX3NhbXBsZXMuY3N2IikKaXNiWywgc2FtcGxlX2lkIDo9IHRvdXBwZXIoc2FtcGxlX2lkKV0KaXNiIDwtIHByb3Rlb21pY3NbLCAuKHB1YmxpY19jbGllbnRfaWQsIHNhbXBsZV9pZCwgZGF5c19pbl9wcm9ncmFtKV1baXNiLCBvbiA9ICJzYW1wbGVfaWQiXQppc2IgPC0gaXNiWyFpcy5uYShwdWJsaWNfY2xpZW50X2lkKV0KaXNiCmBgYAoKTGV0cyBmaWx0ZXIgdGhhdCBmb3Igb25lcyB3aXRoIGF0IGxlYXN0IHR3byBibG9vZCBkcmF3cy4KCmBgYHtyfQpjaGVtIDwtIGdldF9zbmFwc2hvdCgiY2hlbWlzdHJpZXMiKVt2ZW5kb3IgPT0gIkxDQSJdCmNoZW0gPC0gY2hlbVtpc2IsIG9uID0gYygicHVibGljX2NsaWVudF9pZCIsICJkYXlzX2luX3Byb2dyYW0iKV0KdGFiIDwtIGNoZW1bLCB0YWJsZShwdWJsaWNfY2xpZW50X2lkKV0KbXVsdGlwbGUgPC0gaXNiW3B1YmxpY19jbGllbnRfaWQgJWNoaW4lIG5hbWVzKHRhYilbdGFiID4gMV1dCnNldGtleShtdWx0aXBsZSwgcHVibGljX2NsaWVudF9pZCkKbXVsdGlwbGVbLCB1bmlxdWVOKHB1YmxpY19jbGllbnRfaWQpXQpgYGAKClRoYXQgbGVhdmVzIHVzIHdpdGggYWxtb3N0IDEuNUsgaW5kaXZpZHVhbHMuCgpOb3cgZm9yIHRob3NlIHdlIGV4dHJhY3QgdGhlIHdlaWdodCBkYXRhLgoKYGBge3J9CndlaWdodHMgPC0gZ2V0X3NuYXBzaG90KCJ3ZWlnaHQiKVsKICBwdWJsaWNfY2xpZW50X2lkICVjaGluJSBtdWx0aXBsZSRwdWJsaWNfY2xpZW50X2lkICYKICAhaXMubmEoQk1JX0NBTEMpICYgIWlzLm5hKFdFSUdIVF9DQUxDKV0Kd2VpZ2h0c1ssICJ3ZWlnaHQiIDo9IFdFSUdIVF9DQUxDXQp3ZWlnaHRzWywgdW5pcXVlTihwdWJsaWNfY2xpZW50X2lkKV0Kc2V0a2V5KHdlaWdodHMsIHB1YmxpY19jbGllbnRfaWQpCmBgYAoKTGV0J3MgYWxzbyBnZXQgdGhlIG1pY3JvYmlvbWUgc2FtcGxlcyBhbmQgY2hlbWlzdHJpZXMuIFdlIHdpbGwgc3RpY2sgb25seSB3aXRoIHRoZSBETkFHZW5vdGVrIHNhbXBsZXMuCgpgYGB7cn0KbWIgPC0gZ2V0X3NuYXBzaG90KCJtaWNyb2Jpb21lX2RpdmVyc2l0eSIpW3ZlbmRvcl9kYXNoYm9hcmQgIT0gIlNlY29uZCBHZW5vbWUiXQpzZXRrZXkobWIsICJwdWJsaWNfY2xpZW50X2lkIikKYGBgCgpOb3cgd2Ugd2lsbCBkbyB0aGUgaGVhdnkgbGlmdGluZyBhbmQgbWVyZ2UgdGhlIGJsb29kIGRyYXdzIHdpdGggd2VpZ2h0IGFuZCBtaWNyb2Jpb21lIG1lYXN1cmVzLiBGb3IgZWFjaCB1bmlxdWUKYmxvb2QgZHJhdyB3ZSB3aWxsIGxvb2sgZm9yIHRoZSBjbG9zZXN0IG1pY3JvYmlvbWUgYW5kIHdlaWdodCBtZWFzdXJlbWVudCBhbmQgY29ubmVjdCBpdCB0byB0aGUgYmxvb2QgZHJhdy4KV2UgYWxzbyB0cmFjayB0aGUgbWFqb3Igd2VpZ2h0IGFuZCBtaWNyb2Jpb21lIGluZGljYXRvcnMuCgpgYGB7cn0KZmluZF9jbG9zZSA8LSBmdW5jdGlvbihwaWQsIGRheXMsIHNpZCwgY3V0b2ZmID0gMzApIHsKICB3ZGlzdHMgPC0gYWJzKGRheXMgLSB3ZWlnaHRzW3BpZCwgZGF5c19pbl9wcm9ncmFtXSkKICBtYmRpc3RzIDwtIGFicyhkYXlzIC0gbWJbcGlkLCBkYXlzX2luX3Byb2dyYW1dKQogIHdfYmVzdCA8LSB3ZWlnaHRzW3BpZCwgZGF5c19pbl9wcm9ncmFtW3doaWNoLm1pbih3ZGlzdHMpXV0KICB3IDwtIHdlaWdodHNbcGlkLCB3ZWlnaHRbd2hpY2gubWluKHdkaXN0cyldXQogIGJtaSA8LSB3ZWlnaHRzW3BpZCwgQk1JX0NBTENbd2hpY2gubWluKHdkaXN0cyldXQogIG1iX2Jlc3QgPC0gbWJbcGlkLCBkYXlzX2luX3Byb2dyYW1bd2hpY2gubWluKG1iZGlzdHMpXV0KICBtYl9pZCA8LSBtYltwaWQsIHZlbmRvcl9vYnNlcnZhdGlvbl9pZFt3aGljaC5taW4obWJkaXN0cyldXQogIHNoYW5ub24gPC0gbWJbcGlkLCBzaGFubm9uXzIwMDAwW3doaWNoLm1pbihtYmRpc3RzKV1dCiAgcmV0dXJuKGxpc3QoCiAgICBoYXNfY2xvc2Vfd2VpZ2h0ID0gYW55KHdkaXN0cyA8IGN1dG9mZiwgbmEucm0gPSBUUlVFKSwKICAgIGhhc19jbG9zZV9taWNyb2Jpb21lID0gYW55KG1iZGlzdHMgPCBjdXRvZmYsIG5hLnJtID0gVFJVRSksCiAgICB3ZWlnaHRfZGF5c19pbl9wcm9ncmFtID0gd19iZXN0LAogICAgbWljcm9iaW9tZV9kYXlzX2luX3Byb2dyYW0gPSBtYl9iZXN0LAogICAgd2VpZ2h0X2RpZmZfZGF5cyA9IG1pbih3ZGlzdHMpLAogICAgbWljcm9iaW9tZV9kaWZmX2RheXMgPSBtaW4obWJkaXN0cyksCiAgICB3ZWlnaHQgPSB3LAogICAgYm1pID0gYm1pLAogICAgc2hhbm5vbiA9IHNoYW5ub24sCiAgICBtaWNyb2Jpb21lX2lkID0gbWJfaWQsCiAgICBwbGFzbWFfaWQgPSBzaWQpKQp9CgoKY2xvc2UgPC0gaXNiWywgZmluZF9jbG9zZShwdWJsaWNfY2xpZW50X2lkLCBkYXlzX2luX3Byb2dyYW0sIHNhbXBsZV9pZCksIAogICAgICAgICAgICAgIGJ5ID0gYygicHVibGljX2NsaWVudF9pZCIsICJkYXlzX2luX3Byb2dyYW0iKV0KY2xvc2VbaGFzX2Nsb3NlX3dlaWdodCA9PSBULCAKICAgICAgaGFzX2Nsb3NlIDo9ICguTiA+IDEgJgogICAgICAgICFhbnkoZHVwbGljYXRlZCh3ZWlnaHRfZGF5c19pbl9wcm9ncmFtKSkpLCAKICAgICAgICBieSA9ICJwdWJsaWNfY2xpZW50X2lkIl0KbWF0Y2hlZCA8LSBjbG9zZVtoYXNfY2xvc2UgPT0gVFJVRV0KbWF0Y2hlZFssIHRhYmxlKHRhYmxlKHB1YmxpY19jbGllbnRfaWQpKV0KYGBgCgoKCk5vdyBsZXQncyBhbm5vdGF0ZSB0aGlzIHdpdGggQk1JIGFuZCB3ZWlnaHQgbG9zcy4gRmlyc3Qgd2Ugd2lsbCBvbmx5IGtlZXAgdGhvc2UgcG9pbnRzIHdpdGggdGhlIGxvd2VzdCB3ZWlnaHQgYWZ0ZXIKYmFzZWxpbmUgYW5kIG9ubHkgdGhvc2UgaW5kaXZpZHVhbHMgd2l0aCBhdCBsZWFzdCBvbmUgbWljcm9iaW9tZSBzYW1wbGUuIFRoZW4gd2UgYW5ub3RhdGUgdGhlIHdlaWdodCBsb3NzLgoKYGBge3J9Cm1hdGNoZWQgPC0gbWF0Y2hlZFtvcmRlcihwdWJsaWNfY2xpZW50X2lkLCBkYXlzX2luX3Byb2dyYW0pLCAKICAgICAgICAgICAgICAgICAgIC5TRFtjKDEsIHdoaWNoLm1pbih3ZWlnaHRbMjpsZW5ndGgod2VpZ2h0KV0pICsgMSldLCAKICAgICAgICAgICAgICAgICAgIGJ5ID0gInB1YmxpY19jbGllbnRfaWQiXQptYXRjaGVkWywgbWljcm9iaW9tZV9iYXNlbGluZSA6PSBoYXNfY2xvc2VfbWljcm9iaW9tZVsxXSwgYnkgPSAicHVibGljX2NsaWVudF9pZCJdCm1hdGNoZWQgPC0gbWF0Y2hlZFttaWNyb2Jpb21lX2Jhc2VsaW5lID09IFRSVUVdCm1hdGNoZWRbLCB3ZWlnaHRfY2hhbmdlIDo9IGRpZmYod2VpZ2h0KSwgYnkgPSAicHVibGljX2NsaWVudF9pZCJdCm1hdGNoZWRbLCBzcGFuX2RheXMgOj0gZGlmZih3ZWlnaHRfZGF5c19pbl9wcm9ncmFtKSwgYnkgPSAicHVibGljX2NsaWVudF9pZCJdCm1hdGNoZWRbLCB3ZWlnaHRfY2hhbmdlX3JlbGF0aXZlIDo9IGRpZmYod2VpZ2h0KSAvIHdlaWdodFsxXSAvIHNwYW5fZGF5cyAqIDMwLjUsIGJ5ID0gInB1YmxpY19jbGllbnRfaWQiXQptYXRjaGVkCmBgYAoKRmluYWxseSBsZXRzIGFubm90YXRlIHRoZSBpbmRpdmlkdWFscyBhbmQgY2hlY2sgd2hldGhlciB3ZSBoYXZlIGZ1bGwgZ2Vub21lcy4KCmBgYHtyfQpjbGllbnRzIDwtIGdldF9zbmFwc2hvdCgiY2xpZW50cyIpCmNsaWVudHNbLCAiaGFzX2Z1bGxfZ2Vub21lIiA6PSAhaXMubmEoZ2Vub21lX3ZlbmRvcildCm1hdGNoZWQgPC0gY2xpZW50c1ssIC4ocHVibGljX2NsaWVudF9pZCwgaGFzX2Z1bGxfZ2Vub21lLCBzZXgsIHJlZ2lvbiwgYWdlKV1bbWF0Y2hlZCwKICAgICAgICAgICAgICAgICAgIG9uID0gInB1YmxpY19jbGllbnRfaWQiXQpgYGAKCkZpbmFsbHkgd2UgcmVtb3ZlIHRoZSBzYW1wbGVzIHRoYXQgd2UgYXJlIG5vdCBhbGxvd2VkIHRvIHN0dWR5LgoKYGBge3J9Cm5vdm8gPC0gZnJlYWQoImRhdGEvbm5fY2hlbS5jc3YiKVtzcHJlYWRzaGVldF9yZXNpc3RhbmNlID09ICJSZXNpc3RhbnQgQ29hY2ggc3ByZWFkc2hlZXQiXQptYXRjaGVkIDwtIG1hdGNoZWRbIXB1YmxpY19jbGllbnRfaWQgJWNoaW4lIG5vdm8kcHVibGljX2NsaWVudF9pZF0KCm1hdGNoZWRbLCAic2luY2VfYmFzZWxpbmUiIDo9IGRheXNfaW5fcHJvZ3JhbSAtIG1pbihkYXlzX2luX3Byb2dyYW0pLCBieT0icHVibGljX2NsaWVudF9pZCJdCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0zfQpsaWJyYXJ5KGdncGxvdDIpCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpCgpkZW5zIDwtIGRhdGEudGFibGUoCiAgICB3ZWlnaHRfY2hhbmdlX3JlbGF0aXZlID0gZGVuc2l0eShtYXRjaGVkJHdlaWdodF9jaGFuZ2VfcmVsYXRpdmUpJHgsIAogICAgZCA9IGRlbnNpdHkobWF0Y2hlZCR3ZWlnaHRfY2hhbmdlX3JlbGF0aXZlKSR5CikKCmdncGxvdChkZW5zLCBhZXMoeD13ZWlnaHRfY2hhbmdlX3JlbGF0aXZlLCB5PWQpKSArIAogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAsIGx0eT0iZGFzaGVkIikgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZ2VvbV9hcmVhKGRhdGE9ZGVuc1t3ZWlnaHRfY2hhbmdlX3JlbGF0aXZlIDwgLTAuMDFdLCBmaWxsPSJyb3lhbGJsdWUiLCBhbHBoYT0wLjUpICsKICAgIGdlb21fYXJlYShkYXRhPWRlbnNbd2VpZ2h0X2NoYW5nZV9yZWxhdGl2ZSA+IDAgJiB3ZWlnaHRfY2hhbmdlX3JlbGF0aXZlIDwgMC4wMDFdLCBmaWxsPSJzYWxtb24iLCBhbHBoYT0wLjUpICsKICAgIGxhYnMoeD0icmVsYXRpdmUgd2VpZ2h0IGNoYW5nZSBbJXdlaWdodC9tb250aF0iLCB5PSJkZW5zaXR5IikKZ2dzYXZlKCJmaWd1cmVzL3djX2RlbnNpdHkuc3ZnIiwgd2lkdGg9NSwgaGVpZ2h0PTMpCiAgICAKYGBgCgpTbyBob3cgbWFueSBpbmRpdmlkdWFscyB3aXRoIHNpZ25pZmljYW50IHdlaWdodCBsb3NzIGRvIHdlIGhhdmU/CgpgYGB7cn0KbG9zdCA8LSBtYXRjaGVkW3dlaWdodF9jaGFuZ2VfcmVsYXRpdmUgPCAtMC4wMV1bb3JkZXIod2VpZ2h0X2NoYW5nZV9yZWxhdGl2ZSldCmZ3cml0ZShsb3N0LCAic3VjY2Vzc2Z1bF93ZWlnaHRfbG9zcy5jc3YiKQpwcmludChsb3N0WywgdW5pcXVlTihwdWJsaWNfY2xpZW50X2lkKV0pCmxvc3RbLCB0YWJsZShzZXgpXQpgYGAKCgpUbyBzZWxlY3QgY29udHJvbHMgd2UgY2FuIHVzZSBwZXJzb25zIHdpdGggYSBwcmV0dHkgc3RhYmxlIHdlaWdodC4gV2Ugc2hvdWxkIGF2b2lkIHRoZSBvbmVzIHdpdGggZXhhY3RseSBubwpjaGFuZ2UgaW4gd2VpZ2h0IHNpbmNlIHRoYXQgYXJlIG9mdGVuIGluY29ycmVjdCBzZWxmLWVudHJpZXMgb2YgdGhlIHBhcnRpY2lwYW50cyAoZGlkIG5vdCB1cGRhdGUgd2VpZ2h0KS4KCmBgYHtyfQpjb250cm9scyA8LSBtYXRjaGVkW3dlaWdodF9jaGFuZ2VfcmVsYXRpdmUgPiAwXVtvcmRlcihhYnMod2VpZ2h0X2NoYW5nZV9yZWxhdGl2ZSkpXQpmd3JpdGUoY29udHJvbHMsICJub193ZWlnaHRfbG9zcy5jc3YiKQpgYGAKCkFuZCB0aGUgc2FtcGxlIGxpc3RzIGZvciBETkFHZW5vdGVrOgoKYGBge3J9Cm1ibG9zdCA8LSBsb3N0W3NlcSgxLCAzMCwgYnkgPSAyKSwgCiAgICAgICAgICAgICAgIC4ocHVibGljX2NsaWVudF9pZCwgYWdlLCBzZXgsIG1pY3JvYmlvbWVfZGF5c19pbl9wcm9ncmFtLCBtaWNyb2Jpb21lX2lkKV0KbWJsb3N0WywgInN1YnNldCIgOj0gIndlaWdodCBsb3NzIl0KbWJjb250cm9sIDwtIGNvbnRyb2xzW3NlcSgxLCAyMCwgYnkgPSAyKSwKICAgICAgICAgICAgICAgICAgICAgIC4ocHVibGljX2NsaWVudF9pZCwgYWdlLCBzZXgsIG1pY3JvYmlvbWVfZGF5c19pbl9wcm9ncmFtLCBtaWNyb2Jpb21lX2lkKV0KbWJjb250cm9sWywgInN1YnNldCIgOj0gImNvbnRyb2xzIl0KCmZ3cml0ZShyYmluZChtYmxvc3QsIG1iY29udHJvbCksICJkbmFfZ2Vub3Rlay5jc3YiKQpgYGAKCgpBbmQgZm9yIHRoZSBibG9vZCBzYW1wbGVzOgoKYGBge3J9CnBsYXNtYV9sb3N0IDwtIGxvc3RbMTozMCwgCiAgICAgICAgICAgICAgIC4ocHVibGljX2NsaWVudF9pZCwgYWdlLCBzZXgsIGRheXNfaW5fcHJvZ3JhbSwgcGxhc21hX2lkKV0KcGxhc21hX2xvc3RbLCAic3Vic2V0IiA6PSAid2VpZ2h0IGxvc3MiXQpwbGFzbWFfY29udHJvbCA8LSBjb250cm9sc1sxOjIwLAogICAgICAgICAgICAgICAgICAuKHB1YmxpY19jbGllbnRfaWQsIGFnZSwgc2V4LCBkYXlzX2luX3Byb2dyYW0sIHBsYXNtYV9pZCldCnBsYXNtYV9jb250cm9sWywgInN1YnNldCIgOj0gImNvbnRyb2xzIl0KcGxhc21hIDwtIHJiaW5kKHBsYXNtYV9sb3N0LCBwbGFzbWFfY29udHJvbCkKcGxhc21hIDwtIGlzYltwbGFzbWEsIG9uID0gYygKICBwdWJsaWNfY2xpZW50X2lkID0gInB1YmxpY19jbGllbnRfaWQiLCAKICBzYW1wbGVfaWQgPSAicGxhc21hX2lkIiwgCiAgZGF5c19pbl9wcm9ncmFtID0gImRheXNfaW5fcHJvZ3JhbSIpXQpmd3JpdGUocGxhc21hLCAicGxhc21hX3NhbXBsZXMuY3N2IikKYGBg