library(purrr)
library(dplyr)
library(magrittr)
library(DirichletMultinomial)
load("shannon_curated_v3.rda")
## group all bootstrap within one file
fit_genus_bootstrap_curated = vector("list",2)
load("fit_genus_list_1_curated_v3.rda")
fit_genus_bootstrap_curated[[1]] = fit_genus_list
load("fit_genus_list_2_curated_v3.rda")
fit_genus_bootstrap_curated[[2]] = fit_genus_list
load("fit_genus_list_3_curated_v3.rda")
fit_genus_bootstrap_curated[[3]] = fit_genus_list
load("fit_genus_list_4_curated_v3.rda")
fit_genus_bootstrap_curated[[4]] = fit_genus_list
load("fit_genus_list_5_curated_v3.rda")
fit_genus_bootstrap_curated[[5]] = fit_genus_list
extract BIC and Laplace coefficients from boostrap models
BIC_list = map(fit_genus_bootstrap_curated, function(z) map(z, function(y) sapply(y, function(x){attr(x,"goodnessOfFit")[["BIC"]]})))
lplc_list = map(fit_genus_bootstrap_curated, function(z) map(z, function(y) sapply(y, function(x){attr(x,"goodnessOfFit")[["Laplace"]]})))
k = map(fit_genus_bootstrap_curated, function(z) map(z, function(y) sapply(y, function(x){attr(x,"mixture")$Weight %>% length})))[[1]][[1]]
BIC_df = NULL
for(i in 1:5) {
for(j in 1:5){
BIC_df=cbind(BIC_df,BIC_list[[i]][[j]])
}
}
lplc_df = NULL
for(i in 1:5) {
for(j in 1:5){
lplc_df=cbind(lplc_df,lplc_list[[i]][[j]])
}
}
plot BIC and laplace results
nb_seed=5
cowplot::plot_grid(
BIC_df %>% as.data.frame %>%
mutate(`Dirichlet component`=k) %>%
reshape2::melt(id.vars="Dirichlet component") %>%
mutate(seed=rep(sort(rep(1:nb_seed, length(k))),5)) %>%
mutate(boot=sort(rep(paste0("boot_",1:5), length(k)*nb_seed))) %>%
ggplot() + geom_line(aes(x=`Dirichlet component`,y=value,group=variable,color=boot)) +
ggtitle("BIC optimun") + #xlab("Dirichlet component") +
theme_bw() + scale_x_continuous(limits = c(1,101), breaks = scales::pretty_breaks(n = 20)),
lplc_df %>% as.data.frame %>%
mutate(`Dirichlet component`=k) %>%
reshape2::melt(id.vars="Dirichlet component") %>%
mutate(seed=rep(sort(rep(1:nb_seed, length(k))),5)) %>%
mutate(boot=sort(rep(paste0("boot_",1:5), length(k)*nb_seed))) %>%
ggplot() + geom_line(aes(x=`Dirichlet component`,y=value,group=variable,color=boot)) +
ggtitle("Laplace optimun") + #xlab("Dirichlet component") +
theme_bw() + scale_x_continuous(limits = c(1,101), breaks = scales::pretty_breaks(n = 20)),
BIC_df %>% as.data.frame %>%
mutate(`Dirichlet component`=k) %>%
reshape2::melt(id.vars="Dirichlet component") %>%
mutate(seed=rep(sort(rep(1:nb_seed, length(k))),5)) %>%
mutate(boot=sort(rep(paste0("boot_",1:5), length(k)*nb_seed))) %>%
group_by(boot,seed) %>%
filter(value==min(value)) %>%
ggplot() + geom_bar(aes(x=`Dirichlet component`)) +
ggtitle("BIC optimun") +
xlab("Dirichlet components")+
theme_bw() + scale_x_continuous(limits = c(1,30), breaks = scales::pretty_breaks(n = 30)),
lplc_df %>% as.data.frame %>%
mutate(`Dirichlet component`=k) %>%
reshape2::melt(id.vars="Dirichlet component") %>%
mutate(seed=rep(sort(rep(1:nb_seed, length(k))),5)) %>%
mutate(boot=sort(rep(paste0("boot_",1:5), length(k)*nb_seed))) %>%
group_by(boot,seed) %>%
filter(value==min(value)) %>%
ggplot() + geom_bar(aes(x=`Dirichlet component`)) +
ggtitle("Laplace optimun") +
xlim(1,30) + xlab("Dirichlet components")+
theme_bw() + scale_x_continuous(limits = c(1,30), breaks = scales::pretty_breaks(n = 30)),
labels = "AUTO"
)
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing
scale.

ggsave(filename = "figures/figS3.pdf")
Saving 12 x 6 in image
BIC and Laplace have a global optimun among differents seeds and
bootstraps. All bootstraps found at least one model optimal fit at k=24.
Majority of best model based on Laplace were k=24. For BIC, k=24 was
also found as an optimum. For downstream, analysis we then choose
k=24.
boot=1
seed=1
fit_genus_list = fit_genus_bootstrap_curated[[boot]]
best_genus_lplc = 24
best_genus_lplc_idx = which(sapply(fit_genus_list[[seed]], function(x) attr(x,"mixture")$Weight %>% length)==best_genus_lplc) # 19th items from list corresponded to k=24 here
enterotypes_curated =
fit_genus_list[[seed]][[best_genus_lplc_idx]] %>%
mixture(assign=TRUE) %>% as.data.frame %>% set_colnames(c("Enterotypes_id"))
###################################################
### code chunk number 11: posterior-mean-diff
###################################################
p0 <- fitted(fit_genus_list[[seed]][[1]], scale=TRUE) # scale by theta
pbest <- fitted(fit_genus_list[[seed]][[best_genus_lplc_idx]], scale=TRUE)
colnames(pbest) <- paste("m", 1:best_genus_lplc, sep="")
(meandiff <- colSums(abs(pbest - as.vector(p0))))
m1 m2 m3 m4 m5 m6 m7 m8 m9 m10
0.5150830 0.6794278 0.7418365 1.1821217 1.1740381 0.5320536 0.4839814 0.5281410 0.3535401 0.5849210
m11 m12 m13 m14 m15 m16 m17 m18 m19 m20
0.6168881 1.0230422 1.1717559 0.7744601 0.5246573 1.5678386 0.7076409 0.4040203 1.6276143 1.0186103
m21 m22 m23 m24
1.4157758 1.0864123 0.9955640 1.2710302
sum(meandiff)
[1] 20.98045
###################################################
### code chunk number 12: table-1
###################################################
diff <- rowSums(abs(pbest - as.vector(p0)))
o <- order(diff, decreasing=TRUE)
cdiff <- cumsum(diff[o]) / sum(diff)
df <- head(cbind(Mean=p0[o], pbest[o,], diff=diff[o], cdiff), 30)
df %>% as.data.frame()
df_curated_v3 = df %>% as.data.frame()
m_order = df %>% as.data.frame() %>% select(-Mean, -diff,-cdiff) %>% BiotypeR::dist.JSD() %>% hclust(method="complete") %>% unclass %>% .$order
#m_order = df %>% as.data.frame() %>% select(-Mean, -diff,-cdiff) %>% t %>% dist() %>% hclust(method="complete") %>% unclass %>% .$order
df %>% as.data.frame() %>%
tibble::rownames_to_column("taxa") %>%
mutate(taxa=factor(taxa, levels=row.names(df))) %>%
reshape2::melt(id.vars="taxa") %>%
filter(!variable %in% c("Mean","diff","cdiff")) %>%
mutate(variable = variable %>% as.character) %>%
mutate(variable = variable %>% factor(levels=paste0("m",1:24)[m_order]) ) -> genus_alpha_weight_curated
save(genus_alpha_weight_curated, file="genus_alpha_weight_curated.rda")
df %>% as.data.frame() %>%
tibble::rownames_to_column("taxa") %>%
mutate(taxa=factor(taxa, levels=row.names(df))) %>%
reshape2::melt(id.vars="taxa") %>%
filter(variable %in% c("Mean","diff","cdiff"))
df %>% as.data.frame() %>%
tibble::rownames_to_column("taxa") %>%
mutate(taxa=factor(taxa, levels=row.names(df))) %>%
reshape2::melt(id.vars="taxa") %>%
filter(!variable %in% c("Mean","diff","cdiff")) %>%
mutate(variable = variable %>% as.character) %>%
mutate(variable = variable %>% factor(levels=paste0("m",1:24)[m_order]) ) %>% #pull(variable)
#group_by(taxa) %>%
#mutate(total = sum(value)) %>%
#mutate(value=value/total) %>%
ggplot() +
geom_tile(aes(x=variable,y=taxa,fill=log10(value))) +
scale_fill_viridis_c("DMM model alpha") +
ylab("taxa") +
xlab("DMM components")

NA
NA
correlation between DMM alpha_weight and shannon alpha
diversity
df %>% as.data.frame() %>%
tibble::rownames_to_column("taxa") %>%
mutate(taxa=factor(taxa, levels=row.names(df))) %>%
reshape2::melt(id.vars="taxa") %>%
filter(!variable %in% c("Mean","diff","cdiff")) %>%
mutate(variable = variable %>% as.character) %>%
mutate(variable = variable %>% factor(levels=paste0("m",1:24)[m_order]) ) %>%
merge(
shannon_curated_v3 %>%
merge(enterotypes_curated, by="row.names") %>%
mutate(Enterotypes_id=paste0("m",Enterotypes_id)) %>%
group_by(Enterotypes_id) %>%
summarise(m=median(shannon)),
by.x="variable", by.y="Enterotypes_id"
) %>%
group_by(taxa) %>%
do(broom::tidy(cor.test(.$value, .$m, method="spearman"))) %>%
ungroup() %>%
mutate(p.value = p.adjust(p.value, method = "fdr") %>% round(4)) %>%
arrange(desc(abs(estimate))) %>%
select(taxa,estimate,p.value)-> cor_alpha_table
write.csv2(cor_alpha_table, file="cor_alpha_table.csv")
# df_curated_v3 %>%
# select(Mean,diff) %>%
# merge(cor_alpha_table,.,by.x="taxa", by.y="row.names") %>%
# ggplot(aes(x=abs(estimate),y=diff)) + geom_point()
# #ggrepel::geom_label_repel(aes(label=taxa), max.overlaps = 20)
look at theta per component
#fitted(fit_genus_list[[1]][[best_genus_lplc]])
mixturewt(fit_genus_list[[seed]][[1]])
mixturewt(fit_genus_list[[seed]][[best_genus_lplc_idx]])
mixturewt(fit_genus_list[[seed]][[best_genus_lplc_idx]]) %>%
tibble::rowid_to_column("k") %>%
ggplot() +
geom_bar(aes(x=k,y=theta),stat="identity") +
geom_hline(yintercept = mixturewt(fit_genus_list[[seed]][[1]])$theta, col="red") +
xlab("DMM partitions") + ylab("theta extracted from DMM modelling") +
scale_x_continuous(breaks = 1:24) +
cowplot::theme_cowplot() -> figS4A
figS4A

df %>% as.data.frame() %>%
select(-diff,-cdiff,-Mean) %>%
mutate_at(1:19,~log10(.)) %>%
t %>%
as.data.frame %>%
ade4::dudi.pca(scannf = F,nf=3) %>% .$li %>% ade4::s.label()

df %>% as.data.frame() %>%
select(-diff,-cdiff,-Mean) %>%
tibble::rownames_to_column("taxa") %>%
#mutate(taxa2=taxa) %>%
#mutate(taxa2=ifelse())
as_tibble %>%
.[c(1:3,8,11,30),] %>%
reshape2::melt(id.vars="taxa") %>%
group_by(variable) %>%
mutate(ymax=cumsum(value)) %>%
mutate(ymin=c(0, head(ymax, n=-1))) %>%
#ggplot() + geom_bar(aes(y=value,x=variable,fill=taxa),stat="identity")
ggplot(aes(ymax=ymax, ymin=ymin, xmax=4, xmin=3, fill=taxa)) +
geom_rect() +
coord_polar(theta="y") +
facet_wrap(~variable) +
xlim(c(2, 4)) + theme_void() +
scale_fill_brewer("",type="qual")

ggsave("curated_v3_microbiota_types.pdf")
Saving 15 x 8 in image
classify data with the choosen model
load("curated_v3_genus_count.rda")
predict(fit_genus_list[[1]][[best_genus_lplc_idx]], t(curated_v3_genus_count), assign = TRUE) %>%
reshape2::melt() %>%
group_by(Var1) %>%
filter(value==max(value)) %>%
dplyr::rename(`sample_name`="Var1", Enterotypes_id="Var2") %>%
mutate(set=ifelse(sample_name %in% row.names(attr(fit_genus_list[[1]][[best_genus_lplc_idx]],"group")), "train", "predicted")) -> enterotypes_prediction
table(enterotypes_prediction$value > 0.50) %>% prop.table()
FALSE TRUE
0.03160195 0.96839805
table(enterotypes_prediction$value > 0.80) %>% prop.table()
FALSE TRUE
0.2524794 0.7475206
enterotypes_prediction %>%
ggplot() + geom_histogram(aes(x=value), bins = 50) +
#facet_wrap(~set) +
scale_x_continuous(limits = c(0,1.05), labels = scales::percent) +
xlab("classification confidence") +
ylab("number of samples") +
cowplot::theme_cowplot() -> figS4B
figS4B

NA
NA
cowplot::plot_grid(figS4A,figS4B, labels = "AUTO", rel_widths = c(1.7,1))
Avis : Removed 2 rows containing missing values (geom_bar).

ggsave("figures/figS4.pdf", h=5, w=10)
enterotypes_prediction %>%
#as.data.frame %>%
#rbind(data.frame(sample_name = outliers,Enterotypes_id=NA,value=NA,set="outliers", stringsAsFactors = FALSE)) %>%
write.csv2(file="enterotypes_curated_v3_prediction.csv")
combine CMD and AGP DMM weights


permanova en AGP/curated dmm partitions
anova(vegan::betadisper(microbiota_types_jsd, microbiota_annotation$dataset ))
vegan::adonis(microbiota_types_jsd~microbiota_annotation$dataset)
LS0tDQp0aXRsZTogImN1cmF0ZWQgbWV0YWdlbm9taWNzIGRhdGEgVjMgZW50ZXJvdHlpbmciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkoRGlyaWNobGV0TXVsdGlub21pYWwpDQoNCmxvYWQoInNoYW5ub25fY3VyYXRlZF92My5yZGEiKQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KDQojIyBncm91cCBhbGwgYm9vdHN0cmFwIHdpdGhpbiBvbmUgZmlsZQ0KZml0X2dlbnVzX2Jvb3RzdHJhcF9jdXJhdGVkID0gdmVjdG9yKCJsaXN0IiwyKQ0KbG9hZCgiZml0X2dlbnVzX2xpc3RfMV9jdXJhdGVkX3YzLnJkYSIpDQpmaXRfZ2VudXNfYm9vdHN0cmFwX2N1cmF0ZWRbWzFdXSA9IGZpdF9nZW51c19saXN0DQpsb2FkKCJmaXRfZ2VudXNfbGlzdF8yX2N1cmF0ZWRfdjMucmRhIikNCmZpdF9nZW51c19ib290c3RyYXBfY3VyYXRlZFtbMl1dID0gZml0X2dlbnVzX2xpc3QNCmxvYWQoImZpdF9nZW51c19saXN0XzNfY3VyYXRlZF92My5yZGEiKQ0KZml0X2dlbnVzX2Jvb3RzdHJhcF9jdXJhdGVkW1szXV0gPSBmaXRfZ2VudXNfbGlzdA0KbG9hZCgiZml0X2dlbnVzX2xpc3RfNF9jdXJhdGVkX3YzLnJkYSIpDQpmaXRfZ2VudXNfYm9vdHN0cmFwX2N1cmF0ZWRbWzRdXSA9IGZpdF9nZW51c19saXN0DQpsb2FkKCJmaXRfZ2VudXNfbGlzdF81X2N1cmF0ZWRfdjMucmRhIikNCmZpdF9nZW51c19ib290c3RyYXBfY3VyYXRlZFtbNV1dID0gZml0X2dlbnVzX2xpc3QNCg0KYGBgDQoNCmV4dHJhY3QgQklDIGFuZCBMYXBsYWNlIGNvZWZmaWNpZW50cyBmcm9tIGJvb3N0cmFwIG1vZGVscyAgDQoNCmBgYHtyfQ0KQklDX2xpc3QgPSBtYXAoZml0X2dlbnVzX2Jvb3RzdHJhcF9jdXJhdGVkLCBmdW5jdGlvbih6KSBtYXAoeiwgZnVuY3Rpb24oeSkgc2FwcGx5KHksIGZ1bmN0aW9uKHgpe2F0dHIoeCwiZ29vZG5lc3NPZkZpdCIpW1siQklDIl1dfSkpKSANCmxwbGNfbGlzdCA9IG1hcChmaXRfZ2VudXNfYm9vdHN0cmFwX2N1cmF0ZWQsIGZ1bmN0aW9uKHopIG1hcCh6LCBmdW5jdGlvbih5KSBzYXBwbHkoeSwgZnVuY3Rpb24oeCl7YXR0cih4LCJnb29kbmVzc09mRml0IilbWyJMYXBsYWNlIl1dfSkpKQ0KayA9IG1hcChmaXRfZ2VudXNfYm9vdHN0cmFwX2N1cmF0ZWQsIGZ1bmN0aW9uKHopIG1hcCh6LCBmdW5jdGlvbih5KSBzYXBwbHkoeSwgZnVuY3Rpb24oeCl7YXR0cih4LCJtaXh0dXJlIikkV2VpZ2h0ICU+JSBsZW5ndGh9KSkpW1sxXV1bWzFdXQ0KDQoNCkJJQ19kZiA9IE5VTEwNCg0KZm9yKGkgaW4gMTo1KSB7DQogIGZvcihqIGluIDE6NSl7DQogICAgDQogICAgQklDX2RmPWNiaW5kKEJJQ19kZixCSUNfbGlzdFtbaV1dW1tqXV0pDQogICAgDQogIH0NCn0NCg0KDQoNCmxwbGNfZGYgPSBOVUxMDQoNCmZvcihpIGluIDE6NSkgew0KICBmb3IoaiBpbiAxOjUpew0KICAgIA0KICAgIGxwbGNfZGY9Y2JpbmQobHBsY19kZixscGxjX2xpc3RbW2ldXVtbal1dKQ0KICAgIA0KICB9DQp9DQpgYGANCg0KDQpwbG90IEJJQyBhbmQgbGFwbGFjZSByZXN1bHRzDQpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMn0NCg0KbmJfc2VlZD01DQoNCmNvd3Bsb3Q6OnBsb3RfZ3JpZCgNCg0KQklDX2RmICU+JSBhcy5kYXRhLmZyYW1lICU+JSANCiAgbXV0YXRlKGBEaXJpY2hsZXQgY29tcG9uZW50YD1rKSAlPiUNCiAgcmVzaGFwZTI6Om1lbHQoaWQudmFycz0iRGlyaWNobGV0IGNvbXBvbmVudCIpICU+JSANCiAgbXV0YXRlKHNlZWQ9cmVwKHNvcnQocmVwKDE6bmJfc2VlZCwgbGVuZ3RoKGspKSksNSkpICU+JQ0KICBtdXRhdGUoYm9vdD1zb3J0KHJlcChwYXN0ZTAoImJvb3RfIiwxOjUpLCBsZW5ndGgoaykqbmJfc2VlZCkpKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX2xpbmUoYWVzKHg9YERpcmljaGxldCBjb21wb25lbnRgLHk9dmFsdWUsZ3JvdXA9dmFyaWFibGUsY29sb3I9Ym9vdCkpICsNCiAgZ2d0aXRsZSgiQklDIG9wdGltdW4iKSArICN4bGFiKCJEaXJpY2hsZXQgY29tcG9uZW50IikgKw0KICB0aGVtZV9idygpICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwxMDEpLCBicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDIwKSksDQoNCmxwbGNfZGYgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIA0KICBtdXRhdGUoYERpcmljaGxldCBjb21wb25lbnRgPWspICU+JQ0KICByZXNoYXBlMjo6bWVsdChpZC52YXJzPSJEaXJpY2hsZXQgY29tcG9uZW50IikgJT4lIA0KICBtdXRhdGUoc2VlZD1yZXAoc29ydChyZXAoMTpuYl9zZWVkLCBsZW5ndGgoaykpKSw1KSkgJT4lDQogIG11dGF0ZShib290PXNvcnQocmVwKHBhc3RlMCgiYm9vdF8iLDE6NSksIGxlbmd0aChrKSpuYl9zZWVkKSkpICU+JQ0KICBnZ3Bsb3QoKSArIGdlb21fbGluZShhZXMoeD1gRGlyaWNobGV0IGNvbXBvbmVudGAseT12YWx1ZSxncm91cD12YXJpYWJsZSxjb2xvcj1ib290KSkgKw0KICBnZ3RpdGxlKCJMYXBsYWNlIG9wdGltdW4iKSArICN4bGFiKCJEaXJpY2hsZXQgY29tcG9uZW50IikgKw0KICB0aGVtZV9idygpICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwxMDEpLCBicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDIwKSksDQoNCg0KDQpCSUNfZGYgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIA0KICBtdXRhdGUoYERpcmljaGxldCBjb21wb25lbnRgPWspICU+JQ0KICByZXNoYXBlMjo6bWVsdChpZC52YXJzPSJEaXJpY2hsZXQgY29tcG9uZW50IikgJT4lIA0KICBtdXRhdGUoc2VlZD1yZXAoc29ydChyZXAoMTpuYl9zZWVkLCBsZW5ndGgoaykpKSw1KSkgJT4lDQogIG11dGF0ZShib290PXNvcnQocmVwKHBhc3RlMCgiYm9vdF8iLDE6NSksIGxlbmd0aChrKSpuYl9zZWVkKSkpICU+JQ0KICBncm91cF9ieShib290LHNlZWQpICU+JQ0KICBmaWx0ZXIodmFsdWU9PW1pbih2YWx1ZSkpICU+JQ0KICBnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PWBEaXJpY2hsZXQgY29tcG9uZW50YCkpICsgDQogIGdndGl0bGUoIkJJQyBvcHRpbXVuIikgKyANCiAgeGxhYigiRGlyaWNobGV0IGNvbXBvbmVudHMiKSsNCiAgdGhlbWVfYncoKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDEsMzApLCBicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDMwKSksDQoNCg0KbHBsY19kZiAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgDQogIG11dGF0ZShgRGlyaWNobGV0IGNvbXBvbmVudGA9aykgJT4lDQogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnM9IkRpcmljaGxldCBjb21wb25lbnQiKSAlPiUgDQogIG11dGF0ZShzZWVkPXJlcChzb3J0KHJlcCgxOm5iX3NlZWQsIGxlbmd0aChrKSkpLDUpKSAlPiUNCiAgbXV0YXRlKGJvb3Q9c29ydChyZXAocGFzdGUwKCJib290XyIsMTo1KSwgbGVuZ3RoKGspKm5iX3NlZWQpKSkgJT4lDQogIGdyb3VwX2J5KGJvb3Qsc2VlZCkgJT4lDQogIGZpbHRlcih2YWx1ZT09bWluKHZhbHVlKSkgJT4lDQogIGdncGxvdCgpICsgZ2VvbV9iYXIoYWVzKHg9YERpcmljaGxldCBjb21wb25lbnRgKSkgKyANCiAgZ2d0aXRsZSgiTGFwbGFjZSBvcHRpbXVuIikgKyANCiAgeGxpbSgxLDMwKSArICB4bGFiKCJEaXJpY2hsZXQgY29tcG9uZW50cyIpKw0KICB0aGVtZV9idygpICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwzMCksIGJyZWFrcyA9IHNjYWxlczo6cHJldHR5X2JyZWFrcyhuID0gMzApKSwNCmxhYmVscyA9ICJBVVRPIg0KKQ0KDQpnZ3NhdmUoZmlsZW5hbWUgPSAiZmlndXJlcy9maWdTMy5wZGYiKQ0KDQpgYGANCg0KQklDIGFuZCBMYXBsYWNlIGhhdmUgYSBnbG9iYWwgb3B0aW11biBhbW9uZyBkaWZmZXJlbnRzIHNlZWRzIGFuZCBib290c3RyYXBzLiBBbGwgYm9vdHN0cmFwcyBmb3VuZCBhdCBsZWFzdCBvbmUgbW9kZWwgb3B0aW1hbCBmaXQgYXQgaz0yNC4gTWFqb3JpdHkgb2YgYmVzdCBtb2RlbCBiYXNlZCBvbiBMYXBsYWNlIHdlcmUgaz0yNC4gRm9yIEJJQywgaz0yNCB3YXMgYWxzbyBmb3VuZCBhcyBhbiBvcHRpbXVtLiBGb3IgZG93bnN0cmVhbSwgYW5hbHlzaXMgd2UgdGhlbiBjaG9vc2Ugaz0yNC4NCg0KYGBge3J9DQoNCmJvb3Q9MQ0Kc2VlZD0xDQpmaXRfZ2VudXNfbGlzdCA9IGZpdF9nZW51c19ib290c3RyYXBfY3VyYXRlZFtbYm9vdF1dDQpiZXN0X2dlbnVzX2xwbGMgPSAyNCANCmJlc3RfZ2VudXNfbHBsY19pZHggPSB3aGljaChzYXBwbHkoZml0X2dlbnVzX2xpc3RbW3NlZWRdXSwgZnVuY3Rpb24oeCkgYXR0cih4LCJtaXh0dXJlIikkV2VpZ2h0ICU+JSBsZW5ndGgpPT1iZXN0X2dlbnVzX2xwbGMpICMgMTl0aCBpdGVtcyBmcm9tIGxpc3QgY29ycmVzcG9uZGVkIHRvIGs9MjQgaGVyZQ0KDQoNCmVudGVyb3R5cGVzX2N1cmF0ZWQgPQ0KDQogIGZpdF9nZW51c19saXN0W1tzZWVkXV1bW2Jlc3RfZ2VudXNfbHBsY19pZHhdXSAlPiUNCiAgbWl4dHVyZShhc3NpZ249VFJVRSkgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHNldF9jb2xuYW1lcyhjKCJFbnRlcm90eXBlc19pZCIpKQ0KDQoNCg0KDQoNCg0KDQpgYGANCg0KDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTE0fQ0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIGNvZGUgY2h1bmsgbnVtYmVyIDExOiBwb3N0ZXJpb3ItbWVhbi1kaWZmDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCnAwIDwtIGZpdHRlZChmaXRfZ2VudXNfbGlzdFtbc2VlZF1dW1sxXV0sIHNjYWxlPVRSVUUpICAgICAjIHNjYWxlIGJ5IHRoZXRhDQpwYmVzdCA8LSBmaXR0ZWQoZml0X2dlbnVzX2xpc3RbW3NlZWRdXVtbYmVzdF9nZW51c19scGxjX2lkeF1dLCBzY2FsZT1UUlVFKQ0KY29sbmFtZXMocGJlc3QpIDwtIHBhc3RlKCJtIiwgMTpiZXN0X2dlbnVzX2xwbGMsIHNlcD0iIikNCihtZWFuZGlmZiA8LSBjb2xTdW1zKGFicyhwYmVzdCAtIGFzLnZlY3RvcihwMCkpKSkNCnN1bShtZWFuZGlmZikNCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIyBjb2RlIGNodW5rIG51bWJlciAxMjogdGFibGUtMQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpkaWZmIDwtIHJvd1N1bXMoYWJzKHBiZXN0IC0gYXMudmVjdG9yKHAwKSkpDQpvIDwtIG9yZGVyKGRpZmYsIGRlY3JlYXNpbmc9VFJVRSkNCmNkaWZmIDwtIGN1bXN1bShkaWZmW29dKSAvIHN1bShkaWZmKQ0KZGYgPC0gaGVhZChjYmluZChNZWFuPXAwW29dLCBwYmVzdFtvLF0sIGRpZmY9ZGlmZltvXSwgY2RpZmYpLCAzMCkNCmRmICU+JSBhcy5kYXRhLmZyYW1lKCkNCg0KZGZfY3VyYXRlZF92MyA9IGRmICU+JSBhcy5kYXRhLmZyYW1lKCkNCg0KDQptX29yZGVyID0gZGYgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgc2VsZWN0KC1NZWFuLCAtZGlmZiwtY2RpZmYpICAlPiUgQmlvdHlwZVI6OmRpc3QuSlNEKCkgJT4lIGhjbHVzdChtZXRob2Q9ImNvbXBsZXRlIikgJT4lIHVuY2xhc3MgJT4lIC4kb3JkZXINCiNtX29yZGVyID0gZGYgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgc2VsZWN0KC1NZWFuLCAtZGlmZiwtY2RpZmYpICAlPiUgdCAlPiUgZGlzdCgpICU+JSBoY2x1c3QobWV0aG9kPSJjb21wbGV0ZSIpICU+JSB1bmNsYXNzICU+JSAuJG9yZGVyIA0KDQoNCmRmICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJ0YXhhIikgJT4lDQogIG11dGF0ZSh0YXhhPWZhY3Rvcih0YXhhLCBsZXZlbHM9cm93Lm5hbWVzKGRmKSkpICU+JSANCiAgcmVzaGFwZTI6Om1lbHQoaWQudmFycz0idGF4YSIpICU+JQ0KICBmaWx0ZXIoIXZhcmlhYmxlICVpbiUgYygiTWVhbiIsImRpZmYiLCJjZGlmZiIpKSAlPiUNCiAgbXV0YXRlKHZhcmlhYmxlID0gdmFyaWFibGUgJT4lIGFzLmNoYXJhY3RlcikgJT4lIA0KICBtdXRhdGUodmFyaWFibGUgPSB2YXJpYWJsZSAlPiUgZmFjdG9yKGxldmVscz1wYXN0ZTAoIm0iLDE6MjQpW21fb3JkZXJdKSApICAtPiBnZW51c19hbHBoYV93ZWlnaHRfY3VyYXRlZA0KDQpzYXZlKGdlbnVzX2FscGhhX3dlaWdodF9jdXJhdGVkLCBmaWxlPSJnZW51c19hbHBoYV93ZWlnaHRfY3VyYXRlZC5yZGEiKQ0KDQoNCmRmICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJ0YXhhIikgJT4lDQogIG11dGF0ZSh0YXhhPWZhY3Rvcih0YXhhLCBsZXZlbHM9cm93Lm5hbWVzKGRmKSkpICU+JSANCiAgcmVzaGFwZTI6Om1lbHQoaWQudmFycz0idGF4YSIpICU+JQ0KICBmaWx0ZXIodmFyaWFibGUgJWluJSBjKCJNZWFuIiwiZGlmZiIsImNkaWZmIikpDQoNCg0KZGYgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oInRheGEiKSAlPiUNCiAgbXV0YXRlKHRheGE9ZmFjdG9yKHRheGEsIGxldmVscz1yb3cubmFtZXMoZGYpKSkgJT4lIA0KICByZXNoYXBlMjo6bWVsdChpZC52YXJzPSJ0YXhhIikgJT4lDQogIGZpbHRlcighdmFyaWFibGUgJWluJSBjKCJNZWFuIiwiZGlmZiIsImNkaWZmIikpICU+JQ0KICBtdXRhdGUodmFyaWFibGUgPSB2YXJpYWJsZSAlPiUgYXMuY2hhcmFjdGVyKSAlPiUgDQogIG11dGF0ZSh2YXJpYWJsZSA9IHZhcmlhYmxlICU+JSBmYWN0b3IobGV2ZWxzPXBhc3RlMCgibSIsMToyNClbbV9vcmRlcl0pICkgJT4lICNwdWxsKHZhcmlhYmxlKQ0KICAjZ3JvdXBfYnkodGF4YSkgJT4lDQogICNtdXRhdGUodG90YWwgPSBzdW0odmFsdWUpKSAlPiUNCiAgI211dGF0ZSh2YWx1ZT12YWx1ZS90b3RhbCkgJT4lDQogIGdncGxvdCgpICsgDQogIGdlb21fdGlsZShhZXMoeD12YXJpYWJsZSx5PXRheGEsZmlsbD1sb2cxMCh2YWx1ZSkpKSArIA0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygiRE1NIG1vZGVsIGFscGhhIikgKw0KICB5bGFiKCJ0YXhhIikgKw0KICB4bGFiKCJETU0gY29tcG9uZW50cyIpDQogIA0KICANCmBgYA0KDQoNCiMjIyBjb3JyZWxhdGlvbiBiZXR3ZWVuIERNTSBhbHBoYV93ZWlnaHQgYW5kIHNoYW5ub24gYWxwaGEgZGl2ZXJzaXR5DQoNCmBgYHtyfQ0KDQoNCg0KDQoNCg0KZGYgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oInRheGEiKSAlPiUNCiAgbXV0YXRlKHRheGE9ZmFjdG9yKHRheGEsIGxldmVscz1yb3cubmFtZXMoZGYpKSkgJT4lIA0KICByZXNoYXBlMjo6bWVsdChpZC52YXJzPSJ0YXhhIikgJT4lDQogIGZpbHRlcighdmFyaWFibGUgJWluJSBjKCJNZWFuIiwiZGlmZiIsImNkaWZmIikpICU+JQ0KICBtdXRhdGUodmFyaWFibGUgPSB2YXJpYWJsZSAlPiUgYXMuY2hhcmFjdGVyKSAlPiUgDQogIG11dGF0ZSh2YXJpYWJsZSA9IHZhcmlhYmxlICU+JSBmYWN0b3IobGV2ZWxzPXBhc3RlMCgibSIsMToyNClbbV9vcmRlcl0pICkgJT4lDQogIA0KICBtZXJnZSgNCiAgICANCiAgICBzaGFubm9uX2N1cmF0ZWRfdjMgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzX2N1cmF0ZWQsIGJ5PSJyb3cubmFtZXMiKSAlPiUNCiAgbXV0YXRlKEVudGVyb3R5cGVzX2lkPXBhc3RlMCgibSIsRW50ZXJvdHlwZXNfaWQpKSAlPiUNCiAgZ3JvdXBfYnkoRW50ZXJvdHlwZXNfaWQpICU+JQ0KICBzdW1tYXJpc2UobT1tZWRpYW4oc2hhbm5vbikpLA0KICANCiAgYnkueD0idmFyaWFibGUiLCBieS55PSJFbnRlcm90eXBlc19pZCINCiAgICANCiAgICANCiAgICANCiAgKSAlPiUNCiAgZ3JvdXBfYnkodGF4YSkgJT4lDQogIGRvKGJyb29tOjp0aWR5KGNvci50ZXN0KC4kdmFsdWUsIC4kbSwgbWV0aG9kPSJzcGVhcm1hbiIpKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgbXV0YXRlKHAudmFsdWUgPSBwLmFkanVzdChwLnZhbHVlLCBtZXRob2QgPSAiZmRyIikgJT4lIHJvdW5kKDQpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGFicyhlc3RpbWF0ZSkpKSAlPiUNCiAgc2VsZWN0KHRheGEsZXN0aW1hdGUscC52YWx1ZSktPiBjb3JfYWxwaGFfdGFibGUNCiAgDQp3cml0ZS5jc3YyKGNvcl9hbHBoYV90YWJsZSwgZmlsZT0iY29yX2FscGhhX3RhYmxlLmNzdiIpDQoNCg0KDQojIGRmX2N1cmF0ZWRfdjMgJT4lDQojICAgc2VsZWN0KE1lYW4sZGlmZikgJT4lDQojICAgbWVyZ2UoY29yX2FscGhhX3RhYmxlLC4sYnkueD0idGF4YSIsIGJ5Lnk9InJvdy5uYW1lcyIpICU+JQ0KIyAgIGdncGxvdChhZXMoeD1hYnMoZXN0aW1hdGUpLHk9ZGlmZikpICsgZ2VvbV9wb2ludCgpDQojICAgI2dncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsPXRheGEpLCBtYXgub3ZlcmxhcHMgPSAyMCkNCg0KDQpgYGANCg0KDQoNCg0KDQpsb29rIGF0IHRoZXRhIHBlciBjb21wb25lbnQNCmBgYHtyfQ0KDQojZml0dGVkKGZpdF9nZW51c19saXN0W1sxXV1bW2Jlc3RfZ2VudXNfbHBsY11dKQ0KDQptaXh0dXJld3QoZml0X2dlbnVzX2xpc3RbW3NlZWRdXVtbMV1dKQ0KDQptaXh0dXJld3QoZml0X2dlbnVzX2xpc3RbW3NlZWRdXVtbYmVzdF9nZW51c19scGxjX2lkeF1dKQ0KDQptaXh0dXJld3QoZml0X2dlbnVzX2xpc3RbW3NlZWRdXVtbYmVzdF9nZW51c19scGxjX2lkeF1dKSAlPiUgDQogIHRpYmJsZTo6cm93aWRfdG9fY29sdW1uKCJrIikgJT4lIA0KICBnZ3Bsb3QoKSArIA0KICBnZW9tX2JhcihhZXMoeD1rLHk9dGhldGEpLHN0YXQ9ImlkZW50aXR5IikgKyANCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWl4dHVyZXd0KGZpdF9nZW51c19saXN0W1tzZWVkXV1bWzFdXSkkdGhldGEsIGNvbD0icmVkIikgKyANCiAgeGxhYigiRE1NIHBhcnRpdGlvbnMiKSArIHlsYWIoInRoZXRhIGV4dHJhY3RlZCBmcm9tIERNTSBtb2RlbGxpbmciKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjI0KSArDQogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSAtPiBmaWdTNEENCiAgDQpmaWdTNEEgIA0KDQpgYGANCg0KDQoNCg0KDQoNCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE1fQ0KDQpkZiAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBzZWxlY3QoLWRpZmYsLWNkaWZmLC1NZWFuKSAlPiUNCiAgbXV0YXRlX2F0KDE6MTksfmxvZzEwKC4pKSAlPiUNCiAgdCAlPiUNCiAgYXMuZGF0YS5mcmFtZSAlPiUNCiAgYWRlNDo6ZHVkaS5wY2Eoc2Nhbm5mID0gRixuZj0zKSAlPiUgLiRsaSAlPiUgYWRlNDo6cy5sYWJlbCgpDQoNCg0KDQoNCmRmICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIHNlbGVjdCgtZGlmZiwtY2RpZmYsLU1lYW4pICU+JQ0KICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigidGF4YSIpICU+JQ0KICAjbXV0YXRlKHRheGEyPXRheGEpICU+JQ0KICAjbXV0YXRlKHRheGEyPWlmZWxzZSgpKQ0KICAgIGFzX3RpYmJsZSAlPiUNCiAgLltjKDE6Myw4LDExLDMwKSxdICU+JQ0KICByZXNoYXBlMjo6bWVsdChpZC52YXJzPSJ0YXhhIikgJT4lDQogIGdyb3VwX2J5KHZhcmlhYmxlKSAlPiUNCiAgbXV0YXRlKHltYXg9Y3Vtc3VtKHZhbHVlKSkgJT4lDQogIG11dGF0ZSh5bWluPWMoMCwgaGVhZCh5bWF4LCBuPS0xKSkpICU+JQ0KICAjZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeT12YWx1ZSx4PXZhcmlhYmxlLGZpbGw9dGF4YSksc3RhdD0iaWRlbnRpdHkiKSANCiAgZ2dwbG90KGFlcyh5bWF4PXltYXgsIHltaW49eW1pbiwgeG1heD00LCB4bWluPTMsIGZpbGw9dGF4YSkpICsgDQogIGdlb21fcmVjdCgpICsgICAgICANCiAgY29vcmRfcG9sYXIodGhldGE9InkiKSArIA0KICBmYWNldF93cmFwKH52YXJpYWJsZSkgKyANCiAgeGxpbShjKDIsIDQpKSArIHRoZW1lX3ZvaWQoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKCIiLHR5cGU9InF1YWwiKQ0KDQpnZ3NhdmUoImN1cmF0ZWRfdjNfbWljcm9iaW90YV90eXBlcy5wZGYiKQ0KDQoNCmBgYA0KDQoNCg0KDQojIyMgY2xhc3NpZnkgZGF0YSB3aXRoIHRoZSBjaG9vc2VuIG1vZGVsDQoNCg0KYGBge3J9DQpsb2FkKCJjdXJhdGVkX3YzX2dlbnVzX2NvdW50LnJkYSIpDQoNCg0KcHJlZGljdChmaXRfZ2VudXNfbGlzdFtbMV1dW1tiZXN0X2dlbnVzX2xwbGNfaWR4XV0sIHQoY3VyYXRlZF92M19nZW51c19jb3VudCksIGFzc2lnbiA9IFRSVUUpICU+JQ0KICByZXNoYXBlMjo6bWVsdCgpICU+JQ0KICBncm91cF9ieShWYXIxKSAlPiUNCiAgZmlsdGVyKHZhbHVlPT1tYXgodmFsdWUpKSAlPiUNCiAgZHBseXI6OnJlbmFtZShgc2FtcGxlX25hbWVgPSJWYXIxIiwgRW50ZXJvdHlwZXNfaWQ9IlZhcjIiKSAlPiUNCiAgbXV0YXRlKHNldD1pZmVsc2Uoc2FtcGxlX25hbWUgJWluJSByb3cubmFtZXMoYXR0cihmaXRfZ2VudXNfbGlzdFtbMV1dW1tiZXN0X2dlbnVzX2xwbGNfaWR4XV0sImdyb3VwIikpLCAidHJhaW4iLCAicHJlZGljdGVkIikpIC0+IGVudGVyb3R5cGVzX3ByZWRpY3Rpb24NCg0KdGFibGUoZW50ZXJvdHlwZXNfcHJlZGljdGlvbiR2YWx1ZSA+IDAuNTApICU+JSBwcm9wLnRhYmxlKCkNCnRhYmxlKGVudGVyb3R5cGVzX3ByZWRpY3Rpb24kdmFsdWUgPiAwLjgwKSAlPiUgcHJvcC50YWJsZSgpDQoNCmVudGVyb3R5cGVzX3ByZWRpY3Rpb24gJT4lDQogIGdncGxvdCgpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9dmFsdWUpLCBiaW5zID0gNTApICsgDQogICNmYWNldF93cmFwKH5zZXQpICsgDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMS4wNSksIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB4bGFiKCJjbGFzc2lmaWNhdGlvbiBjb25maWRlbmNlIikgKw0KICB5bGFiKCJudW1iZXIgb2Ygc2FtcGxlcyIpICsNCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpIC0+IGZpZ1M0Qg0KDQpmaWdTNEINCg0KDQpgYGANCg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMH0NCg0KDQpjb3dwbG90OjpwbG90X2dyaWQoZmlnUzRBLGZpZ1M0QiwgbGFiZWxzID0gIkFVVE8iLCByZWxfd2lkdGhzID0gYygxLjcsMSkpDQoNCmdnc2F2ZSgiZmlndXJlcy9maWdTNC5wZGYiLCBoPTUsIHc9MTApDQoNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCg0KZW50ZXJvdHlwZXNfcHJlZGljdGlvbiAlPiUNCiAgI2FzLmRhdGEuZnJhbWUgJT4lDQogICNyYmluZChkYXRhLmZyYW1lKHNhbXBsZV9uYW1lID0gb3V0bGllcnMsRW50ZXJvdHlwZXNfaWQ9TkEsdmFsdWU9TkEsc2V0PSJvdXRsaWVycyIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkpICU+JQ0KICB3cml0ZS5jc3YyKGZpbGU9ImVudGVyb3R5cGVzX2N1cmF0ZWRfdjNfcHJlZGljdGlvbi5jc3YiKQ0KDQoNCg0KDQoNCg0KYGBgDQoNCg0KDQoNCg0KDQojIyBjb21iaW5lIENNRCBhbmQgQUdQIERNTSB3ZWlnaHRzDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQ0KDQpsb2FkKCIuLi9kZl9ndXR6aWxsYS5yZGEiKQ0KDQpyb3cubmFtZXMoZGZfZ3V0emlsbGEpID0gc2FwcGx5KHJvdy5uYW1lcyhkZl9ndXR6aWxsYSkgJT4lIHN0cnNwbGl0KHNwbGl0PSJnX18iKSwgZnVuY3Rpb24oeCkgaWZlbHNlKGlzLm5hKHhbMl0pLCBwYXN0ZTAoc2FtcGxlKExFVFRFUlMsIDEpLHNhbXBsZShMRVRURVJTLCAxKSksIHhbMl0pKQ0KDQpjb2xuYW1lcyhkZl9ndXR6aWxsYSkgPSBjb2xuYW1lcyhkZl9ndXR6aWxsYSkgJT4lIGdzdWIoIm0iLCJNIiwgLikgDQoNCm1lcmdlKGRmX2N1cmF0ZWRfdjMgJT4lIHNlbGVjdCgtbTE5LC1tMjIsLW0yMywtbTI0KSxkZl9ndXR6aWxsYSwgYnk9InJvdy5uYW1lcyIpICU+JSANCiAgc2VsZWN0KGNvbnRhaW5zKCJtIixpZ25vcmUuY2FzZSA9IFRSVUUpKSAlPiUgDQogIHNlbGVjdCgtTWVhbi54LC1NZWFuLnksIC1Sb3cubmFtZXMpICU+JSANCiAgY29sbmFtZXMoKSAlPiUNCiAgdGliYmxlOjp0aWJibGUoTWljcm9iaW90YT0uKSAlPiUNCiAgbXV0YXRlKGRhdGFzZXQ9aWZlbHNlKE1pY3JvYmlvdGEgJT4lIGdyZXBsKCJcXE0iLCAuKSwgIkFHUCIsIkN1cmF0ZWRNR2RiIikpICU+JQ0KICAjbXV0YXRlKHJhbmRvbT1ybm9ybSgxOCsxOSkpICU+JQ0KICAjbWVyZ2UobWljcm9iaW90YV90eXBlc19hbHBoYV9kaXZlcnNpdHksIGJ5Lng9Ik1pY3JvYmlvdGEiLCBieS55PSJFbnRlcm90eXBlc19pZCIpICU+JQ0KICAjc2VsZWN0KC1yYW5rLCAtU2hhbm5vbikgJT4lDQogICNzZWxlY3QoLXJhbmspICU+JQ0KICAjZHBseXI6OnJlbmFtZShgU2hhbm5vbiBzY2FsZWRgPSJzY2FsZSIpICU+JQ0KICAjbWVyZ2UoZGlldF9jb2EsIGJ5Lng9Ik1pY3JvYmlvdGEiLCBieS55ID0icm93Lm5hbWVzIiwgYWxsID0gVFJVRSApICU+JQ0KICAjc2VsZWN0KC1BeGlzMikgJT4lDQogICNkcGx5cjo6cmVuYW1lDQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJNaWNyb2Jpb3RhIikgLT4gbWljcm9iaW90YV9hbm5vdGF0aW9uDQoNCnNhdmUobWljcm9iaW90YV9hbm5vdGF0aW9uLCBmaWxlPSJmaWd1cmVzL21pY3JvYmlvdGFfYW5ub3RhdGlvbi5yZGEiKQ0KDQoNCm1lcmdlKGRmX2N1cmF0ZWRfdjMlPiUgc2VsZWN0KC1tMTksLW0yMiwtbTIzLC1tMjQpLGRmX2d1dHppbGxhLCBieT0icm93Lm5hbWVzIikgJT4lIA0KICBzZWxlY3QoY29udGFpbnMoIm0iLGlnbm9yZS5jYXNlID0gVFJVRSkpICU+JSANCiAgc2VsZWN0KC1NZWFuLngsLU1lYW4ueSkgJT4lDQogICNtdXRhdGUoUm93Lm5hbWVzID0gUm93Lm5hbWVzICU+JSBzdHJfc3BsaXRfZml4ZWQocGF0dGVybiA9IjsiLCAgbiA9IDYpICU+JSAuWyw2XSAlPiUgZ3N1YigiZ19fIiwiIiwuKSkgJT4lDQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJSb3cubmFtZXMiKSAlPiUgYXMubWF0cml4KCkgLT4gbWljcm9iaW90YV90eXBlcw0KDQpzYXZlKG1pY3JvYmlvdGFfdHlwZXMsIGZpbGU9ImZpZ3VyZXMvbWljcm9iaW90YV90eXBlcy5yZGEiKQ0KDQoNCg0KbWljcm9iaW90YV90eXBlc19qc2QgPSBSZmFzdDo6RGlzdCh0KG1pY3JvYmlvdGFfdHlwZXMpKSANCmNvbG5hbWVzKG1pY3JvYmlvdGFfdHlwZXNfanNkKSA9IHJvdy5uYW1lcyhtaWNyb2Jpb3RhX3R5cGVzX2pzZCkgPSBjb2xuYW1lcyhtaWNyb2Jpb3RhX3R5cGVzKQ0KDQptaWNyb2Jpb3RhX3R5cGVzX2pzZCA9IG1pY3JvYmlvdGFfdHlwZXNfanNkICU+JSBhcy5kaXN0KCkgJT4lIHNxcnQoKQ0KDQphbm5fY29sb3JzID0gbGlzdCgNCiAgICBkYXRhc2V0ID0gYyhBR1A9InBpbmsiLCBDdXJhdGVkTUdkYj0ibGlnaHRibHVlIiksDQogICAgYFNoYW5ub24gc2NhbGVkYCA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg0LCBuYW1lPSJPclJkIiksDQogICAgQXhpczEgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoNCwgbmFtZT0iQnJCRyIpLA0KICAgIEF4aXMyID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDQsIG5hbWU9IkJyQkciKQ0KKQ0KDQoNCiAgI2FzLm1hdHJpeCAlPiUgI2xvZzEwKCkgJT4lDQogIHBoZWF0bWFwOjpwaGVhdG1hcChtYXQ9bG9nMTAobWljcm9iaW90YV90eXBlcyksIGN1dHJlZV9jb2xzID0gNywgYW5ub3RhdGlvbl9jb2wgPSBtaWNyb2Jpb3RhX2Fubm90YXRpb24sIGNlbGx3aWR0aCA9IDEwLCBjZWxsaGVpZ2h0ID0gMjAsIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9IG1pY3JvYmlvdGFfdHlwZXNfanNkLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubl9jb2xvcnMsIGNvbG9yPXZpcmlkaXM6OnZpcmlkaXNfcGFsKCkoMTAwKSkgJT4lIGdncGxvdGlmeTo6YXMuZ2dwbG90KCkgLT4gZmlnM0ENCmZpZzNBDQpnZ3NhdmUoImRtbV90eXBlc19jbHVzdGVyaW5nX2N1cmF0ZWRfYWdwLnBkZiIpDQoNCmBgYA0KDQojIyMgcGVybWFub3ZhIGVuIEFHUC9jdXJhdGVkIGRtbSBwYXJ0aXRpb25zDQoNCmBgYHtyfQ0KYW5vdmEodmVnYW46OmJldGFkaXNwZXIobWljcm9iaW90YV90eXBlc19qc2QsIG1pY3JvYmlvdGFfYW5ub3RhdGlvbiRkYXRhc2V0ICkpDQoNCg0KdmVnYW46OmFkb25pcyhtaWNyb2Jpb3RhX3R5cGVzX2pzZH5taWNyb2Jpb3RhX2Fubm90YXRpb24kZGF0YXNldCkNCg0KDQpgYGANCg0KDQoNCg==