load genus abundance


genus_path = system.file("data-raw/qiime/generated-files-20190512/taxa/genus.qza", package = "gutzilla")

genus = qiime2R::read_qza(genus_path)$data %>% as.data.frame %>% tibble::rownames_to_column("taxa")  %>% as_tibble()

load metadata (cleaned) and add united nation subregions


country_info = readr::read_tsv(system.file(package="gutzilla", "data-raw/countryInfo.txt"), comment = "#", col_names = FALSE, na = "") %>%
  select(X5,X9) %>%
  dplyr::rename(country="X5", continent="X9" )
Avis : One or more parsing issues, call `problems()` on your data frame for details, e.g.:
  dat <- vroom(...)
  problems(dat)Rows: 252 Columns: 14── Column specification ──────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (12): X1, X2, X3, X4, X5, X6, X9, X10, X11, X12, X13, X14
dbl  (2): X7, X8
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
data("UNSD_countries")

metadata = readr::read_csv2(system.file("data-raw/Metadata_10317_20191022-112414_curatedv4_VSv1.csv", package = "gutzilla"))
ℹ Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
Rows: 24251 Columns: 278── Column specification ──────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ";"
chr  (251): SAMPLE_NAME, SAMPLE_TYPE, SEX, AGE_CAT, BMI_CAT, COUNTRY, ALCOHOL_TYPES_BEERCIDER, ALCOHOL_TYPES_R...
dbl    (7): AGE_YEARS, HEIGHT_CM, WEIGHT_KG, BIRTH_YEAR, HOST_TAXID, TAXON_ID, VIOSCREEN_NBITEMS
num    (3): BMI, VIOSCREEN_KCAL, VIOSCREEN_TIME
lgl   (16): HOST, ALCOHOL_TYPES, ALLERGIC_TO, FERMENTED_CONSUMED, FERMENTED_PRODUCE_COMMERCIAL, FERMENTED_PROD...
dttm   (1): COLLECTION_TIMESTAMP
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#to do: check issue parsing IBD variables

colnames(metadata) = stringr::str_to_lower(colnames(metadata))



metadata %>%
  select(country_of_birth) %>%
  merge(UNSD_countries %>% 
          select(`Country or Area`, `Sub-region Name`, `Region Name`) %>% 
          mutate(`Country or Area` =  gsub("United Kingdom of Great Britain and Northern Ireland", "United Kingdom", `Country or Area`)) %>% 
          mutate(`Country or Area` =  gsub("United States of America", "USA", `Country or Area`)) %>%
          mutate(`Sub-region Name` =  ifelse(`Sub-region Name` %in% c("Central Asia", "Southern Asia"), "Central and Southern Asia",`Sub-region Name`)) %>%
          mutate(`Sub-region Name` =  ifelse(`Sub-region Name` %in% c("Melanesia", "Micronesia","Polynesia"), "Oceania (others)",`Sub-region Name`)), 
        by.x="country_of_birth", by.y="Country or Area") %>% 
  group_by(`Region Name`,`Sub-region Name`) %>% summarise(n=n()) %>%
  ungroup() %>%
  ggplot() + geom_bar(aes(y=n, x=`Sub-region Name`), stat="identity") + scale_y_log10() +
  facet_wrap(~`Region Name`, scales = "free_x", nr=1) + 
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 
  xlab("Region of Birth\n(United Nations-m49)") +
  ylab("Number of stool sample")
`summarise()` has grouped output by 'Region Name'. You can override using the `.groups` argument.
  
ggsave("Region_of_birth.pdf")
Saving 7.29 x 4.5 in image

metadata <- metadata %>%
  merge(UNSD_countries %>% 
          select(`Country or Area`, `Sub-region Name`, `Region Name`) %>% 
          mutate(`Country or Area` =  gsub("United Kingdom of Great Britain and Northern Ireland", "United Kingdom", `Country or Area`)) %>% 
          mutate(`Country or Area` =  gsub("United States of America", "USA", `Country or Area`)) %>%
          mutate(`Sub-region Name` =  ifelse(`Sub-region Name` %in% c("Central Asia", "Southern Asia"), "Central and Southern Asia",`Sub-region Name`)) %>%
          mutate(`Sub-region Name` =  ifelse(`Sub-region Name` %in% c("Melanesia", "Micronesia","Polynesia"), "Oceania (others)",`Sub-region Name`)), 
        by.x="country_of_birth", by.y="Country or Area")
  

table(metadata$`Sub-region Name`)

      Australia and New Zealand       Central and Southern Asia                    Eastern Asia 
                            632                             356                             140 
                 Eastern Europe Latin America and the Caribbean                 Northern Africa 
                            328                             286                              39 
               Northern America                 Northern Europe                Oceania (others) 
                          14244                            5734                              11 
             South-eastern Asia                 Southern Europe              Sub-Saharan Africa 
                            227                             267                             163 
                   Western Asia                  Western Europe 
                            105                             778 

cowplot::plot_grid(

metadata %>%
  mutate(age_cat = age_cat %>% forcats::fct_relevel(c("baby","child","teen"), after=0 )) %>%
  filter(!is.na(age_cat)) %>%
  ggplot() + geom_bar(aes(x=age_cat)) + theme_bw(),

metadata %>%
  filter(!is.na(age_cat), !is.na(sex)) %>%
  ggplot() + geom_bar(aes(x=sex)) + theme_bw(), rel_widths = c(2,1))

NA
NA

import outliers to filter them out and select the top 30 genus based on read mass.

outliers = readLines(con="outliers_samples.txt")

n=30 # n samples per group

metadata %>% 
  filter(!(sample_name %in% outliers)) %>%
  #filter(!(`#SampleID` %in% outliers)) %>%
  merge(country_info, by.x="country_of_birth", by.y="country") %>%
  select(sample_name,age_cat,sex, `Sub-region Name`,country_of_birth) %>% 
  #select(`#SampleID`,age_cat,sex, continent,country_of_birth) %>% 
  filter(sex %in% c("male","female")) %>%
  filter(!is.na(age_cat) & age_cat != "Not provided") %>%
  group_by(age_cat,sex,`Sub-region Name`) %>% 
  sample_n(if(n() < n) n() else n) %>%
  #sample_n(size=30, replace=TRUE) %>%
  #unique() %>% 
  pull(sample_name) -> samples_id_select
Error in h(simpleError(msg, call)) : 
  erreur d'�valuation de l'argument 'y' lors de la s�lection d'une m�thode pour la fonction 'merge' : object 'country_info' not found

load DMM model fit from boostrap

 load("fit_genus_bootstrap.rda")
Avis : cannot open compressed file 'fit_genus_bootstrap.rda', probable reason 'No such file or directory'Error in readChar(con, 5L, useBytes = TRUE) : cannot open the connection

extract BIC and Laplace coefficients from boostrap models

BIC_list = map(fit_genus_bootstrap, function(z) map(z, function(y) sapply(y, function(x){attr(x,"goodnessOfFit")[["BIC"]]}))) 
lplc_list = map(fit_genus_bootstrap, function(z) map(z, function(y) sapply(y, function(x){attr(x,"goodnessOfFit")[["Laplace"]]})))
k = map(fit_genus_bootstrap, function(z) map(z, function(y) sapply(y, function(x){attr(x,"mixture")$Weight %>% length})))[[1]][[1]]
Error in map(fit_genus_bootstrap, function(z) map(z, function(y) sapply(y,  : 
  subscript out of bounds

plot BIC and laplace results

BIC and Laplace have a global optimun among differents seeds and bootstraps. All bootstraps found at least one model optimal fit at k=19. Majority of best model based on Laplace were k=19. For BIC, k=19 arrived in second postition. For downstream, analysis we then choose k=19.

For downstream analysis, we choose models among bootstrap where BIC and Laplace showed k=19 as optimum. Among models, we choose the worst one to avoid model overfitting.

###################################################
### 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))))
sum(meandiff)


###################################################
### 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_gutzilla = 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:19)[m_order]) )  -> genus_alpha_weight_gutzilla

save(genus_alpha_weight_gutzilla, file="genus_alpha_weight_gutzilla.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")) %>%
  #group_by(taxa) %>%
  #mutate(total = sum(value)) %>%
  #mutate(value=value/total) %>%
  ggplot() + 
  geom_tile(aes(x=variable,y=taxa,fill=value)) + 
  scale_fill_viridis_c("DMM model alpha") +
  ylab("taxa") +
  xlab("DMM components")
  

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") +
  cowplot::theme_cowplot() +
  ylab("theta") +
  xlab("DMM partitions") -> figS8E

figS8E

metadata %>%
  merge(enterotypes, by.x="sample_name", by.y="row.names", all.y="TRUE") %>%
  group_by(`Sub-region Name`) %>%
  mutate(total=n()) %>%
  group_by(`Sub-region Name`,Enterotypes_id,total) %>%
  summarise(prop=n()) %>%
  mutate(prop=prop/total) %>%
  ggplot() + geom_tile(aes(x=Enterotypes_id %>% as.character(),y=`Sub-region Name`,fill=prop)) + scale_fill_viridis_c()


metadata %>%
  merge(enterotypes, by.x="sample_name", by.y="row.names", all.y="TRUE") %>%
  filter(age_cat != "baby") %>%
  group_by(age_cat) %>%
  mutate(total=n()) %>%
  group_by(age_cat,Enterotypes_id,total) %>%
  summarise(prop=n()) %>%
  mutate(prop=prop/total) %>%
  ungroup() %>%
  group_by(Enterotypes_id) %>%
  mutate(prop2=sum(prop)) %>%
  mutate(prop=prop/prop2) %>%
  ggplot() + geom_tile(aes(x=Enterotypes_id %>% as.character(),y=age_cat,fill=prop)) + scale_fill_viridis_c()


metadata %>%
  merge(enterotypes, by.x="sample_name", by.y="row.names", all.y="TRUE") %>%
  filter(age_cat != "baby") %>%
  filter(!is.na(bmi_cat), bmi_cat != "Not provided") %>%
  group_by(bmi_cat) %>%
  mutate(total=n()) %>%
  group_by(bmi_cat,Enterotypes_id,total) %>%
  summarise(prop=n()) %>%
  mutate(prop=prop/total) %>%
  ungroup() %>%
  group_by(bmi_cat) %>%
  mutate(prop2=sum(prop)) %>%
  mutate(prop=prop/prop2) %>%
  ggplot() + geom_tile(aes(x=Enterotypes_id %>% as.character(),y=bmi_cat,fill=prop)) + scale_fill_viridis_c()


metadata %>%
  merge(enterotypes, by.x="sample_name", by.y="row.names", all.y="TRUE") %>%
  filter(age_cat != "baby") %>%
  filter(!is.na(gluten), gluten != "Not provided") %>%
  group_by(gluten) %>%
  mutate(total=n()) %>%
  group_by(gluten,Enterotypes_id,total) %>%
  summarise(prop=n()) %>%
  mutate(prop=prop/total) %>%
  ungroup() %>%
  group_by(gluten) %>%
  mutate(prop2=sum(prop)) %>%
  mutate(prop=prop/prop2) %>%
  ggplot() + geom_tile(aes(x=Enterotypes_id %>% as.character(),y=gluten,fill=prop)) + scale_fill_viridis_c()

metadata %>%
  merge(enterotypes, by.x="sample_name", by.y="row.names", all.y="TRUE") %>%
  filter(age_cat != "baby") %>%
  filter(!is.na(types_of_plants), types_of_plants != "Not provided") %>%
  group_by(types_of_plants) %>%
  mutate(total=n()) %>%
  group_by(types_of_plants,Enterotypes_id,total) %>%
  summarise(prop=n()) %>%
  mutate(prop=prop/total) %>%
  ungroup() %>%
  group_by(types_of_plants) %>%
  mutate(prop2=sum(prop)) %>%
  mutate(prop=prop/prop2) %>%
  ggplot() + geom_tile(aes(x=Enterotypes_id %>% as.character(),y=types_of_plants,fill=prop)) + scale_fill_viridis_c()

metadata %>%
  merge(enterotypes, by.x="sample_name", by.y="row.names", all.y="TRUE") %>%
  filter(age_cat != "baby") %>%
  filter(!is.na(diet_type), diet_type != "Not provided") %>%
  group_by(diet_type) %>%
  mutate(total=n()) %>%
  group_by(diet_type,Enterotypes_id,total) %>%
  summarise(prop=n()) %>%
  mutate(prop=prop/total) %>%
  ungroup() %>%
  group_by(diet_type) %>%
  mutate(prop2=sum(prop)) %>%
  mutate(prop=prop/prop2) %>%
  ggplot() + geom_tile(aes(x=Enterotypes_id %>% as.character(),y=diet_type,fill=prop)) + scale_fill_viridis_c()

to do: compute enterotype proportion for all categories and do a PCA analysis with minimal spanning tree


metadata %>%
  merge(enterotypes, by.x="sample_name", by.y="row.names", all.y="TRUE") %>%
  select(sample_name,age_cat,sex,`Sub-region Name`,bmi_cat, Enterotypes_id) %>%
  reshape2::melt(id.vars=c("sample_name","Enterotypes_id")) %>%
  filter(!is.na(value), value != "Not provided") %>%
  select(-variable, -`sample_name`) %>%
  group_by(Enterotypes_id,value) %>%
  filter(!is.na(value)) %>%
  #mutate(total=n()) %>%
  #group_by(Enterotypes_id,value,total) %>%
  #summarise(prop=n()) %>%
  reshape2::dcast(Enterotypes_id~value) %>%
  mutate_at(-1,~./(sum(.))) -> et_repartition

et_repartition %>%
  tibble::column_to_rownames("Enterotypes_id") %>%
  apply(1,function(x) x/sum(x)) %>%
  t %>%
  as.data.frame %>%
  ade4::dudi.pca(.,scan=F, nf=3) -> et_metadata_pca


ade4::scatter(et_metadata_pca)

ade4::s.label(et_metadata_pca$li, yax=2)

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,7,11),] %>%
  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 %>% strsplit(x=.,split=";") %>% sapply(function(x)x[6])  )) + 
  geom_rect() +      
  coord_polar(theta="y") + 
  facet_wrap(~variable) + 
  xlim(c(2, 4)) + theme_void() +
  scale_fill_brewer("",type="qual")

ggsave("agp_microbiota_types.pdf")

reclassify all dataset


# fit_genus_list[[1]][[best_genus_lplc]] %>%
#   mixture(assign=TRUE) %>% as.data.frame %>% set_colnames(c("Enterotypes_id"))

genus_select=rownames(fitted(fit_genus_bootstrap[[1]][[1]][[best_genus_lplc]]))
genus %>%
      select(-outliers) %>%
      tibble::column_to_rownames("taxa") %>% .[genus_select,]-> genus_df

genus_df %>% colnames() %>% data.frame(sample_name=.) -> enterotypes_clusters_df


 k=1
for(i in 1:5) {
  
  for(j in 1:5){
    

   
    
    predict(fit_genus_bootstrap[[i]][[j]][[best_genus_lplc]], t(genus_df), assign = TRUE) %>%
      reshape2::melt() %>%
      group_by(Var1) %>%
      filter(value==max(value)) %>%
      dplyr::rename(`#SampleID`="Var1", Enterotypes_id="Var2") %>%
      select(`#SampleID`,Enterotypes_id) -> enterotypes_prediction_max
    
    colnames(enterotypes_prediction_max) = c("sample_name",paste0("Enterotypes_id_",k))
    
    enterotypes_clusters_df=
    enterotypes_clusters_df %>%
      merge(enterotypes_prediction_max, by="sample_name")
    
    cat(k,"\n")
    k=k+1
   
    
  }
  
  
}

 enterotypes_clusters_dist = matrix(nr=25,nc=25)  

 for(i in 1:25) {
     for (j in 1:25) {
     
     enterotypes_clusters_dist[i,j] = mclust::adjustedRandIndex(enterotypes_clusters_df[-1][,i],enterotypes_clusters_df[-1][,j])
     
     
   }
 }
 
 

classify data with the choosen model




predict(fit_genus_list[[1]][[14]], t(genus_df), 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]][[14]],"group")), "train", "predicted")) -> enterotypes_prediction


enterotypes_prediction %>%
  ggplot() + geom_histogram(aes(x=value, fill=value>0.80), bins = 50) + 
  #facet_wrap(~set) + 
  scale_x_continuous(limits = c(0,1.05), labels = scales::percent)

enterotypes_prediction %>%
  ggplot() + 
  geom_histogram(aes(x=value), bins = 50) + 
  #facet_wrap(~set) + 
  scale_x_continuous(limits = c(0,1.05), labels = scales::percent) +
  cowplot::theme_cowplot() -> figS8F

export enterotypes with outliers


enterotypes_prediction %>%
  as.data.frame %>%
  rbind(data.frame(sample_name = outliers,Enterotypes_id=NA,value=NA,set="outliers", stringsAsFactors = FALSE)) %>%
  write.csv2(file="enterotypes_prediction_outliers.csv")

Enterotypes vs Age and Richness

Age


metadata %>%
  merge(enterotypes, by.x="sample_name", by.y="row.names", all.y="TRUE") %>%
  select(Enterotypes_id, age_years) %>% ggplot() + geom_boxplot(aes(x=Enterotypes_id%>% as.character, y=age_years))
  group_by(Enterotypes_id) %>%
  summarise(age_years=mode(age_years))
  

richness


shannon_path = system.file("data-raw/qiime/generated-files-20190512/alpha/shannon.qza", package = "gutzilla")
shannon = qiime2R::read_qza(shannon_path)$data %>% as.data.frame

merge(enterotypes_prediction,shannon,by.x="sample_name", by.y="row.names")  -> enterotypes_alpha_div
  
enterotypes_alpha_div %>% 
  ggplot()   + 
  geom_boxplot(aes(x=paste0("M",Enterotypes_id %>% as.character),y=shannon, fill=set)) +
  xlab("DMM partitions") + ylab("alpha-diversity (shannon)") +
  cowplot::theme_cowplot() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) -> figS8G
figS8G

figure supp 8


(figS8ABCD/(figS8E+figS8F+figS8G + plot_layout(widths = c(1,1,1.5)))) + plot_layout(heights = c(2,1)) + plot_annotation(tag_levels = 'a') -> figS8

figS8
ggsave("figS8.pdf")

check fibres


fibre_path = system.file("data-raw/vioscreen/FICHIER_JULIEN_FIBER_HOSTSUBJECTID_2.xlsx", package = "agp")

readxl::read_xlsx(fibre_path) %>%
  select(-FIBER...6) %>%
  dplyr::rename(FIBER="FIBER...4") -> fibre_df
  

colnames(fibre_df) = colnames(fibre_df) %>% stringr::str_to_lower()

n=1275 with fiber and enterotypes data


fibre_df %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  ggplot() + 
  geom_boxplot(aes(y=shannon,x=paste0("Q",quartile_fiber),fill=paste0("Q",quartile_fiber))) +
  xlab("fiber\nquartiles") + ylab("alpha-diversity (shannon)") +
  scale_fill_brewer("fiber\nquartiles",type="div",palette = 2)


fibre_df %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  ggplot() + 
  geom_boxplot(aes(y=shannon,x=paste0("m",Enterotypes_id %>% as.character),fill=paste0("Q",quartile_fiber))) +
  xlab("Microbiota DMM based clusters") + ylab("alpha-diversity (shannon)") +
  scale_fill_brewer("fiber\nquartiles",type="div",palette = 2)


fibre_df %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  group_by(Enterotypes_id) %>%
  do(with(.,kruskal.test(.$shannon,.$quartile_fiber, method="spearman") %>% broom::tidy() ))

fibre_df %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  group_by(Enterotypes_id) %>%
  summarise(n=n())

fibre_df %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  filter(Enterotypes_id %in% c(2,5,6,7,13)) %>%
  ggplot() + 
  geom_boxplot(aes(y=shannon,x=paste0("m",Enterotypes_id %>% as.character),fill=paste0("Q",quartile_fiber))) +
  xlab("Microbiota DMM based clusters") + ylab("alpha-diversity (shannon)") +
  scale_fill_brewer("fiber\nquartiles",type="div",palette = 2)

check type of plant




metadata %>%
  select(sample_name,types_of_plants) %>%
   mutate(types_of_plants=types_of_plants%>% forcats::fct_rev() %>% forcats::fct_relevel("More than 30", after=Inf) %>% forcats::fct_relevel("21 to 30", after=3)) %>%
  na.omit() %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  ggplot() + 
  geom_boxplot(aes(y=shannon,x=types_of_plants,fill=types_of_plants)) +
  xlab("nb type of plants per week") + ylab("alpha-diversity (shannon)") +
  scale_fill_brewer("Type of plants",type="div",palette = 2) +
  xlab("Type of plants") + ylab("alpha-diversity (shannon)") + ggtitle("all pop")

metadata %>%
  select(sample_name,types_of_plants) %>%
  merge(fibre_df, by="sample_name") %>%
  mutate(types_of_plants=types_of_plants%>% forcats::fct_rev() %>% forcats::fct_relevel("More than 30", after=Inf) %>% forcats::fct_relevel("21 to 30", after=3)) %>%
  na.omit() %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  ggplot() + 
  geom_boxplot(aes(y=shannon,x=types_of_plants,fill=types_of_plants)) +
  xlab("nb type of plants per week") + ylab("alpha-diversity (shannon)") +
  scale_fill_brewer("Type of plants",type="div",palette = 2) +
  xlab("Type of plants ") + ylab("alpha-diversity (shannon)") + ggtitle("with vioscreened only")



metadata %>%
  select(sample_name,types_of_plants) %>%
   mutate(types_of_plants=types_of_plants%>% forcats::fct_rev() %>% forcats::fct_relevel("More than 30", after=Inf) %>% forcats::fct_relevel("21 to 30", after=3)) %>%
  na.omit() %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  ggplot() + 
  geom_boxplot(aes(y=shannon,x=paste0("m",Enterotypes_id %>% as.character),fill=types_of_plants)) +
  xlab("Microbiota DMM based clusters") + ylab("alpha-diversity (shannon)") +
  scale_fill_brewer("Type of\nplant",type="div",palette = 2) + ggtitle("all pop")



metadata %>%
  select(sample_name,types_of_plants) %>%
  merge(fibre_df, by="sample_name") %>%
   mutate(types_of_plants=types_of_plants%>% forcats::fct_rev() %>% forcats::fct_relevel("More than 30", after=Inf) %>% forcats::fct_relevel("21 to 30", after=3)) %>%
  na.omit() %>%
  merge(enterotypes_alpha_div, by="sample_name") %>% 
  ggplot() + 
  geom_boxplot(aes(y=shannon,x=paste0("m",Enterotypes_id %>% as.character),fill=types_of_plants)) +
  xlab("Microbiota DMM based clusters") + ylab("alpha-diversity (shannon)") +
  scale_fill_brewer("Type of\nplant",type="div",palette = 2) + ggtitle("with vioscreened only")

metadata %>%
  select(sample_name,types_of_plants) %>%
  merge(fibre_df, by="sample_name") %>%
   mutate(types_of_plants=types_of_plants%>% forcats::fct_rev() %>% forcats::fct_relevel("More than 30", after=Inf) %>% forcats::fct_relevel("21 to 30", after=3)) %>%
  na.omit() %>%
  merge(enterotypes_alpha_div, by="sample_name") %>%
  filter(Enterotypes_id %in% c(1,2,5,7,13,15)) %>%
  ggplot() + geom_point(aes(x=fiber,y=shannon,col=types_of_plants)) + 
  scale_color_brewer("Type of\nplant",type="div",palette = 2) + 
  facet_wrap(~paste0("m",Enterotypes_id), scales = "free_y") + theme_dark() +
  ggtitle("example of enterotypes")

rescale enterotypes using world demographic data

we will use UNSD subregion to rescale enterotypes proportion observed


data("UNSD_pop_subregion")

UNSD_pop_subregion %>%
          mutate(Subregion = ifelse(Subregion %in% c("Melanesia","Micronesia","Polynesia"), "Oceania (others)",Subregion)) %>%
          mutate(Subregion =  ifelse(Subregion %in% c("Central Asia", "Southern Asia"), "Central and Southern Asia",Subregion)) %>%
          mutate(Subregion =  ifelse(Subregion %in% c("South-Eastern Asia"), "South-eastern Asia",Subregion)) %>%
  ggplot() + geom_bar(aes(y=n_pop_2020, x=Subregion), stat="identity") +
  theme(axis.text.x = element_text(angle = -90, vjust=0.5))



metadata %>%
  merge(enterotypes_prediction, by.x="sample_name", all.y="TRUE") %>%
  select(Enterotypes_id,`Sub-region Name`) %>%
  na.omit %>%
  group_by(`Sub-region Name`) %>%
  summarise(total=n()) %>%
  ggplot() + geom_bar(aes(y=total, x=`Sub-region Name`), stat="identity") +
  theme(axis.text.x = element_text(angle = -90, vjust=0.5))

metadata %>%
  merge(enterotypes_prediction, by.x="sample_name",  all.y="TRUE") %>%
  select(`Sub-region Name`) %>%
  na.omit %>%
  group_by(`Sub-region Name`) %>%
  summarise(total=n()) %>%
  merge(UNSD_pop_subregion %>%
          mutate(Subregion = ifelse(Subregion %in% c("Melanesia","Micronesia","Polynesia"), "Oceania (others)",Subregion)) %>%
          mutate(Subregion =  ifelse(Subregion %in% c("Central Asia", "Southern Asia"), "Central and Southern Asia",Subregion)) %>%
          mutate(Subregion =  ifelse(Subregion %in% c("South-Eastern Asia"), "South-eastern Asia",Subregion)) %>%
                   group_by(Subregion) %>%
                   summarise(n_pop_2020 = sum(n_pop_2020)),
  by.x="Sub-region Name", by.y="Subregion", all.x=TRUE) %>%
  mutate_if(is.numeric,function(x) x/sum(x) ) %>%
  dplyr::rename(n_pop_agp_birth="total") -> region_n_pop_agp_unsd

  region_n_pop_agp_unsd %>%
  reshape2::melt(id.vars="Sub-region Name") %>%
  ggplot() + 
  geom_bar(aes(x=`Sub-region Name`, y=value, fill=variable), stat="identity", position = "dodge") +
  theme(axis.text.x = element_text(angle = -90, vjust=0.5)) 

  
  
  

metadata %>%
  merge(enterotypes_prediction, by.x="sample_name",  all.y="TRUE") %>%
  select(`Sub-region Name`,Enterotypes_id,set) %>%
  ggplot() + geom_bar(aes(x=Enterotypes_id,fill=set)) 

metadata %>%
  merge(enterotypes_prediction, by.x="sample_name",  all.y="TRUE") %>%
  select(`Sub-region Name`,Enterotypes_id,set) %>%
  na.omit %>%
  group_by(`Sub-region Name`,Enterotypes_id) %>%
  summarise(n_per_et=n()) %>%
  ungroup() %>%
  group_by(`Sub-region Name`) %>%
  mutate(n_per_region=sum(n_per_et)) %>%
  ungroup() %>%
  #mutate(n=n %>% (function(x) x / sum(x)) ) %>%
  merge(UNSD_pop_subregion %>%
          filter(!(Subregion == "Latin America and the Caribbean" & Type =="Region")) %>%
          mutate(Subregion = ifelse(Subregion %in% "Oceania (Excluding Australia and New Zealand)", "Oceania (others)",Subregion)) %>%
          #mutate(Subregion =  ifelse(Subregion %in% c("Central Asia", "Southern Asia"), "Central and Southern Asia",Subregion)) %>%
          mutate(Subregion =  ifelse(Subregion %in% c("South-Eastern Asia"), "South-eastern Asia",Subregion)) ,
                   
                   #summarise(n_pop_2020 = sum(n_pop_2020)),
  by.x="Sub-region Name", by.y="Subregion", all.x=TRUE) %>%
  mutate(n_estimated_per_pop = (n_per_et * n_pop_2020)/n_per_region) %>%
  select(-n_per_region,-Type,-n_pop_2020) %>%
  mutate(p_per_et=n_per_et/sum(n_per_et), p_estimated_per_pop=n_estimated_per_pop/sum(n_estimated_per_pop))  -> enterotypes_by_UNSD_regions

write.csv2(enterotypes_by_UNSD_regions, file="enterotypes_by_UNSD_regions.csv")
  
  
enterotypes_by_UNSD_regions %>%
  group_by(Enterotypes_id) %>%
  summarise_if(is.numeric,sum) %>%
  select(Enterotypes_id,p_per_et,p_estimated_per_pop) %>%
  reshape2::melt(id.vars="Enterotypes_id") %>%
  ggplot() + geom_bar(aes(x=Enterotypes_id,y=value,fill=variable), stat="identity", position = "dodge")
LS0tDQp0aXRsZTogImhpZXJhcmNoaWNhbCBlbnRlcm90eXBpbmciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCg0KbG9hZCBnZW51cyBhYnVuZGFuY2UNCmBgYHtyfQ0KDQpnZW51c19wYXRoID0gc3lzdGVtLmZpbGUoImRhdGEtcmF3L3FpaW1lL2dlbmVyYXRlZC1maWxlcy0yMDE5MDUxMi90YXhhL2dlbnVzLnF6YSIsIHBhY2thZ2UgPSAiZ3V0emlsbGEiKQ0KDQpnZW51cyA9IHFpaW1lMlI6OnJlYWRfcXphKGdlbnVzX3BhdGgpJGRhdGEgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJ0YXhhIikgICU+JSBhc190aWJibGUoKQ0KDQoNCmBgYA0KDQpsb2FkIG1ldGFkYXRhIChjbGVhbmVkKSBhbmQgYWRkIHVuaXRlZCBuYXRpb24gc3VicmVnaW9ucw0KYGBge3J9DQoNCmNvdW50cnlfaW5mbyA9IHJlYWRyOjpyZWFkX3RzdihzeXN0ZW0uZmlsZShwYWNrYWdlPSJndXR6aWxsYSIsICJkYXRhLXJhdy9jb3VudHJ5SW5mby50eHQiKSwgY29tbWVudCA9ICIjIiwgY29sX25hbWVzID0gRkFMU0UsIG5hID0gIiIpICU+JQ0KICBzZWxlY3QoWDUsWDkpICU+JQ0KICBkcGx5cjo6cmVuYW1lKGNvdW50cnk9Ilg1IiwgY29udGluZW50PSJYOSIgKQ0KDQoNCmRhdGEoIlVOU0RfY291bnRyaWVzIikNCg0KbWV0YWRhdGEgPSByZWFkcjo6cmVhZF9jc3YyKHN5c3RlbS5maWxlKCJkYXRhLXJhdy9NZXRhZGF0YV8xMDMxN18yMDE5MTAyMi0xMTI0MTRfY3VyYXRlZHY0X1ZTdjEuY3N2IiwgcGFja2FnZSA9ICJndXR6aWxsYSIpKQ0KDQojdG8gZG86IGNoZWNrIGlzc3VlIHBhcnNpbmcgSUJEIHZhcmlhYmxlcw0KDQpjb2xuYW1lcyhtZXRhZGF0YSkgPSBzdHJpbmdyOjpzdHJfdG9fbG93ZXIoY29sbmFtZXMobWV0YWRhdGEpKQ0KDQoNCg0KbWV0YWRhdGEgJT4lDQogIHNlbGVjdChjb3VudHJ5X29mX2JpcnRoKSAlPiUNCiAgbWVyZ2UoVU5TRF9jb3VudHJpZXMgJT4lIA0KICAgICAgICAgIHNlbGVjdChgQ291bnRyeSBvciBBcmVhYCwgYFN1Yi1yZWdpb24gTmFtZWAsIGBSZWdpb24gTmFtZWApICU+JSANCiAgICAgICAgICBtdXRhdGUoYENvdW50cnkgb3IgQXJlYWAgPSAgZ3N1YigiVW5pdGVkIEtpbmdkb20gb2YgR3JlYXQgQnJpdGFpbiBhbmQgTm9ydGhlcm4gSXJlbGFuZCIsICJVbml0ZWQgS2luZ2RvbSIsIGBDb3VudHJ5IG9yIEFyZWFgKSkgJT4lIA0KICAgICAgICAgIG11dGF0ZShgQ291bnRyeSBvciBBcmVhYCA9ICBnc3ViKCJVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EiLCAiVVNBIiwgYENvdW50cnkgb3IgQXJlYWApKSAlPiUNCiAgICAgICAgICBtdXRhdGUoYFN1Yi1yZWdpb24gTmFtZWAgPSAgaWZlbHNlKGBTdWItcmVnaW9uIE5hbWVgICVpbiUgYygiQ2VudHJhbCBBc2lhIiwgIlNvdXRoZXJuIEFzaWEiKSwgIkNlbnRyYWwgYW5kIFNvdXRoZXJuIEFzaWEiLGBTdWItcmVnaW9uIE5hbWVgKSkgJT4lDQogICAgICAgICAgbXV0YXRlKGBTdWItcmVnaW9uIE5hbWVgID0gIGlmZWxzZShgU3ViLXJlZ2lvbiBOYW1lYCAlaW4lIGMoIk1lbGFuZXNpYSIsICJNaWNyb25lc2lhIiwiUG9seW5lc2lhIiksICJPY2VhbmlhIChvdGhlcnMpIixgU3ViLXJlZ2lvbiBOYW1lYCkpLCANCiAgICAgICAgYnkueD0iY291bnRyeV9vZl9iaXJ0aCIsIGJ5Lnk9IkNvdW50cnkgb3IgQXJlYSIpICU+JSANCiAgZ3JvdXBfYnkoYFJlZ2lvbiBOYW1lYCxgU3ViLXJlZ2lvbiBOYW1lYCkgJT4lIHN1bW1hcmlzZShuPW4oKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeT1uLCB4PWBTdWItcmVnaW9uIE5hbWVgKSwgc3RhdD0iaWRlbnRpdHkiKSArIHNjYWxlX3lfbG9nMTAoKSArDQogIGZhY2V0X3dyYXAofmBSZWdpb24gTmFtZWAsIHNjYWxlcyA9ICJmcmVlX3giLCBucj0xKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArIA0KICB4bGFiKCJSZWdpb24gb2YgQmlydGhcbihVbml0ZWQgTmF0aW9ucy1tNDkpIikgKw0KICB5bGFiKCJOdW1iZXIgb2Ygc3Rvb2wgc2FtcGxlIikNCiAgDQpnZ3NhdmUoIlJlZ2lvbl9vZl9iaXJ0aC5wZGYiKQ0KDQptZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUNCiAgbWVyZ2UoVU5TRF9jb3VudHJpZXMgJT4lIA0KICAgICAgICAgIHNlbGVjdChgQ291bnRyeSBvciBBcmVhYCwgYFN1Yi1yZWdpb24gTmFtZWAsIGBSZWdpb24gTmFtZWApICU+JSANCiAgICAgICAgICBtdXRhdGUoYENvdW50cnkgb3IgQXJlYWAgPSAgZ3N1YigiVW5pdGVkIEtpbmdkb20gb2YgR3JlYXQgQnJpdGFpbiBhbmQgTm9ydGhlcm4gSXJlbGFuZCIsICJVbml0ZWQgS2luZ2RvbSIsIGBDb3VudHJ5IG9yIEFyZWFgKSkgJT4lIA0KICAgICAgICAgIG11dGF0ZShgQ291bnRyeSBvciBBcmVhYCA9ICBnc3ViKCJVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EiLCAiVVNBIiwgYENvdW50cnkgb3IgQXJlYWApKSAlPiUNCiAgICAgICAgICBtdXRhdGUoYFN1Yi1yZWdpb24gTmFtZWAgPSAgaWZlbHNlKGBTdWItcmVnaW9uIE5hbWVgICVpbiUgYygiQ2VudHJhbCBBc2lhIiwgIlNvdXRoZXJuIEFzaWEiKSwgIkNlbnRyYWwgYW5kIFNvdXRoZXJuIEFzaWEiLGBTdWItcmVnaW9uIE5hbWVgKSkgJT4lDQogICAgICAgICAgbXV0YXRlKGBTdWItcmVnaW9uIE5hbWVgID0gIGlmZWxzZShgU3ViLXJlZ2lvbiBOYW1lYCAlaW4lIGMoIk1lbGFuZXNpYSIsICJNaWNyb25lc2lhIiwiUG9seW5lc2lhIiksICJPY2VhbmlhIChvdGhlcnMpIixgU3ViLXJlZ2lvbiBOYW1lYCkpLCANCiAgICAgICAgYnkueD0iY291bnRyeV9vZl9iaXJ0aCIsIGJ5Lnk9IkNvdW50cnkgb3IgQXJlYSIpDQogIA0KYGBgDQoNCg0KDQpgYGB7cn0NCg0KdGFibGUobWV0YWRhdGEkYFN1Yi1yZWdpb24gTmFtZWApDQoNCg0KYGBgDQoNCg0KYGBge3J9DQoNCmNvd3Bsb3Q6OnBsb3RfZ3JpZCgNCg0KbWV0YWRhdGEgJT4lDQogIG11dGF0ZShhZ2VfY2F0ID0gYWdlX2NhdCAlPiUgZm9yY2F0czo6ZmN0X3JlbGV2ZWwoYygiYmFieSIsImNoaWxkIiwidGVlbiIpLCBhZnRlcj0wICkpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGFnZV9jYXQpKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeD1hZ2VfY2F0KSkgKyB0aGVtZV9idygpLA0KDQptZXRhZGF0YSAlPiUNCiAgZmlsdGVyKCFpcy5uYShhZ2VfY2F0KSwgIWlzLm5hKHNleCkpICU+JQ0KICBnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PXNleCkpICsgdGhlbWVfYncoKSwgcmVsX3dpZHRocyA9IGMoMiwxKSkNCg0KDQpgYGANCg0KDQoNCmltcG9ydCBvdXRsaWVycyB0byBmaWx0ZXIgdGhlbSBvdXQgYW5kIHNlbGVjdCB0aGUgdG9wIDMwIGdlbnVzIGJhc2VkIG9uIHJlYWQgbWFzcy4NCg0KYGBge3J9DQpvdXRsaWVycyA9IHJlYWRMaW5lcyhjb249Im91dGxpZXJzX3NhbXBsZXMudHh0IikNCg0Kbj0zMCAjIG4gc2FtcGxlcyBwZXIgZ3JvdXANCg0KbWV0YWRhdGEgJT4lIA0KICBmaWx0ZXIoIShzYW1wbGVfbmFtZSAlaW4lIG91dGxpZXJzKSkgJT4lDQogICNmaWx0ZXIoIShgI1NhbXBsZUlEYCAlaW4lIG91dGxpZXJzKSkgJT4lDQogIG1lcmdlKGNvdW50cnlfaW5mbywgYnkueD0iY291bnRyeV9vZl9iaXJ0aCIsIGJ5Lnk9ImNvdW50cnkiKSAlPiUNCiAgc2VsZWN0KHNhbXBsZV9uYW1lLGFnZV9jYXQsc2V4LCBgU3ViLXJlZ2lvbiBOYW1lYCxjb3VudHJ5X29mX2JpcnRoKSAlPiUgDQogICNzZWxlY3QoYCNTYW1wbGVJRGAsYWdlX2NhdCxzZXgsIGNvbnRpbmVudCxjb3VudHJ5X29mX2JpcnRoKSAlPiUgDQogIGZpbHRlcihzZXggJWluJSBjKCJtYWxlIiwiZmVtYWxlIikpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGFnZV9jYXQpICYgYWdlX2NhdCAhPSAiTm90IHByb3ZpZGVkIikgJT4lDQogIGdyb3VwX2J5KGFnZV9jYXQsc2V4LGBTdWItcmVnaW9uIE5hbWVgKSAlPiUgDQogIHNhbXBsZV9uKGlmKG4oKSA8IG4pIG4oKSBlbHNlIG4pICU+JQ0KICAjc2FtcGxlX24oc2l6ZT0zMCwgcmVwbGFjZT1UUlVFKSAlPiUNCiAgI3VuaXF1ZSgpICU+JSANCiAgcHVsbChzYW1wbGVfbmFtZSkgLT4gc2FtcGxlc19pZF9zZWxlY3QNCiAgI3N1bW1hcmlzZShuPW4oKSkNCg0KbGVuZ3RoKHNhbXBsZXNfaWRfc2VsZWN0KQ0KDQoNCm1ldGFkYXRhICU+JSANCiAgZmlsdGVyKHNhbXBsZV9uYW1lICVpbiUgc2FtcGxlc19pZF9zZWxlY3QpICU+JQ0KICBtZXJnZShjb3VudHJ5X2luZm8sIGJ5Lng9ImNvdW50cnlfb2ZfYmlydGgiLCBieS55PSJjb3VudHJ5IikgJT4lDQogIHNlbGVjdChzYW1wbGVfbmFtZSxhZ2VfY2F0LHNleCwgYFN1Yi1yZWdpb24gTmFtZWAsY291bnRyeV9vZl9iaXJ0aCwgYm1pX2NhdCxnbHV0ZW4sdHlwZXNfb2ZfcGxhbnRzLGRpZXRfdHlwZSAgKSAlPiUgDQogIGZpbHRlcihzZXggJWluJSBjKCJtYWxlIiwiZmVtYWxlIikpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGFnZV9jYXQpICYgYWdlX2NhdCAhPSAiTm90IHByb3ZpZGVkIikgJT4lICMtPiBtZXRhZGF0YV9zZWxlY3QNCiAgd2l0aCguLCB4dGFicyh+YFN1Yi1yZWdpb24gTmFtZWArYWdlX2NhdCwgZGF0YT0uKSkNCg0KDQptZXRhZGF0YSAlPiUgDQogIGZpbHRlcihzYW1wbGVfbmFtZSAlaW4lIHNhbXBsZXNfaWRfc2VsZWN0KSAlPiUNCiAgbWVyZ2UoY291bnRyeV9pbmZvLCBieS54PSJjb3VudHJ5X29mX2JpcnRoIiwgYnkueT0iY291bnRyeSIpICU+JQ0KICBzZWxlY3Qoc2FtcGxlX25hbWUsYWdlX2NhdCxzZXgsIGBTdWItcmVnaW9uIE5hbWVgLGNvdW50cnlfb2ZfYmlydGgsIGJtaV9jYXQsZ2x1dGVuLHR5cGVzX29mX3BsYW50cyxkaWV0X3R5cGUgICkgJT4lIA0KICBmaWx0ZXIoc2V4ICVpbiUgYygibWFsZSIsImZlbWFsZSIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShhZ2VfY2F0KSAmIGFnZV9jYXQgIT0gIk5vdCBwcm92aWRlZCIpIC0+IG1ldGFkYXRhX3NlbGVjdA0KDQp0b3BfZ2VudXNfbWFzcyA9IA0KICBnZW51c19wcm9wICU+JSANCiAgc2VsZWN0KC1vdXRsaWVycykgJT4lDQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJ0YXhhIikgJT4lIA0KICBhcy5tYXRyaXggJT4lIA0KICBhcHBseSgxLHN1bSkgJT4lIA0KICBzb3J0ICU+JSByZXYgJT4lIGhlYWQoMzApDQoNCnNhdmUodG9wX2dlbnVzX21hc3MsIGZpbGU9InRvcF9nZW51c19tYXNzLnJkYSIpDQoNCiMgZ2VudXNfcHJvcCAlPiUgDQojICAgc2VsZWN0KC1vdXRsaWVycykgJT4lDQojICAgZmlsdGVyKHRheGEgJWluJSBuYW1lcyh0b3BfZ2VudXNfbWFzcykpICU+JQ0KIyAgIHN1bW1hcmlzZV9hdCgtMSwgc3VtKSAlPiUgdCAlPiUgaGlzdCgpDQoNCg0KDQoNCmBgYA0KDQoNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCg0KZ2VudXM9Z2VudXNbLGNvbG5hbWVzKGdlbnVzKSAlaW4lIHNhbXBsZXNfaWRfc2VsZWN0XQ0Kc291cmNlKCIuLi9ub3RlYm9vay9lbnRlcm90eXBpbmcuUiIpDQoNCnNhdmUoZml0X2dlbnVzX2xpc3QsIGZpbGU9ImZpdF9nZW51c19saXN0LnJkYSIpDQoNCg0KYGBgDQoNCmxvYWQgRE1NIG1vZGVsIGZpdCBmcm9tIGJvb3N0cmFwDQpgYGB7cn0NCg0KI3NvdXJjZSgiZW50ZXJvdHlwZXNfYm9vdHN0cmFwLlIiKQ0KDQoNCiAjIGZpdF9nZW51c19ib290c3RyYXAgPSB2ZWN0b3IoImxpc3QiLDUpDQogIyBsb2FkKCJmaXRfZ2VudXNfbGlzdF8xLnJkYSIpDQogIyBmaXRfZ2VudXNfYm9vdHN0cmFwW1sxXV0gPSBmaXRfZ2VudXNfbGlzdA0KICMgbG9hZCgiZml0X2dlbnVzX2xpc3RfMi5yZGEiKQ0KICMgZml0X2dlbnVzX2Jvb3RzdHJhcFtbMl1dID0gZml0X2dlbnVzX2xpc3QNCiAjIGxvYWQoImZpdF9nZW51c19saXN0XzMucmRhIikNCiAjIGZpdF9nZW51c19ib290c3RyYXBbWzNdXSA9IGZpdF9nZW51c19saXN0DQogIyBsb2FkKCJmaXRfZ2VudXNfbGlzdF80LnJkYSIpDQogIyBmaXRfZ2VudXNfYm9vdHN0cmFwW1s0XV0gPSBmaXRfZ2VudXNfbGlzdA0KICMgbG9hZCgiZml0X2dlbnVzX2xpc3RfNS5yZGEiKQ0KICMgZml0X2dlbnVzX2Jvb3RzdHJhcFtbNV1dID0gZml0X2dlbnVzX2xpc3QNCg0KIGxvYWQoIi4uL2ZpdF9nZW51c19ib290c3RyYXAucmRhIikNCg0KYGBgDQoNCg0KZXh0cmFjdCBCSUMgYW5kIExhcGxhY2UgY29lZmZpY2llbnRzIGZyb20gYm9vc3RyYXAgbW9kZWxzICANCg0KYGBge3J9DQpCSUNfbGlzdCA9IG1hcChmaXRfZ2VudXNfYm9vdHN0cmFwLCBmdW5jdGlvbih6KSBtYXAoeiwgZnVuY3Rpb24oeSkgc2FwcGx5KHksIGZ1bmN0aW9uKHgpe2F0dHIoeCwiZ29vZG5lc3NPZkZpdCIpW1siQklDIl1dfSkpKSANCmxwbGNfbGlzdCA9IG1hcChmaXRfZ2VudXNfYm9vdHN0cmFwLCBmdW5jdGlvbih6KSBtYXAoeiwgZnVuY3Rpb24oeSkgc2FwcGx5KHksIGZ1bmN0aW9uKHgpe2F0dHIoeCwiZ29vZG5lc3NPZkZpdCIpW1siTGFwbGFjZSJdXX0pKSkNCmsgPSBtYXAoZml0X2dlbnVzX2Jvb3RzdHJhcCwgZnVuY3Rpb24oeikgbWFwKHosIGZ1bmN0aW9uKHkpIHNhcHBseSh5LCBmdW5jdGlvbih4KXthdHRyKHgsIm1peHR1cmUiKSRXZWlnaHQgJT4lIGxlbmd0aH0pKSlbWzFdXVtbMV1dDQoNCg0KQklDX2RmID0gTlVMTA0KDQpmb3IoaSBpbiAxOjUpIHsNCiAgZm9yKGogaW4gMTo1KXsNCiAgICANCiAgICBCSUNfZGY9Y2JpbmQoQklDX2RmLEJJQ19saXN0W1tpXV1bW2pdXSkNCiAgICANCiAgfQ0KfQ0KDQoNCg0KbHBsY19kZiA9IE5VTEwNCg0KZm9yKGkgaW4gMTo1KSB7DQogIGZvcihqIGluIDE6NSl7DQogICAgDQogICAgbHBsY19kZj1jYmluZChscGxjX2RmLGxwbGNfbGlzdFtbaV1dW1tqXV0pDQogICAgDQogIH0NCn0NCmBgYA0KDQoNCnBsb3QgQklDIGFuZCBsYXBsYWNlIHJlc3VsdHMNCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEyfQ0KDQpjb3dwbG90OjpwbG90X2dyaWQoDQoNCkJJQ19kZiAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgDQogIG11dGF0ZShgRGlyaWNobGV0IGNvbXBvbmVudGA9aykgJT4lDQogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnM9IkRpcmljaGxldCBjb21wb25lbnQiKSAlPiUgDQogIG11dGF0ZShzZWVkPXJlcChzb3J0KHJlcCgxOjUsIGxlbmd0aChrKSkpLDUpKSAlPiUNCiAgbXV0YXRlKGJvb3Q9c29ydChyZXAocGFzdGUwKCJib290XyIsMTo1KSwgbGVuZ3RoKGspKjUpKSkgJT4lDQogIGdncGxvdCgpICsgZ2VvbV9saW5lKGFlcyh4PWBEaXJpY2hsZXQgY29tcG9uZW50YCx5PXZhbHVlLGdyb3VwPXZhcmlhYmxlLGNvbG9yPWJvb3QpKSArDQogIGdndGl0bGUoIkJJQyBvcHRpbXVuIikgKyAjeGxhYigiRGlyaWNobGV0IGNvbXBvbmVudCIpICsNCiAgdGhlbWVfYncoKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDEsMTAxKSwgYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG4gPSAyMCkpLA0KDQpscGxjX2RmICU+JSBhcy5kYXRhLmZyYW1lICU+JSANCiAgbXV0YXRlKGBEaXJpY2hsZXQgY29tcG9uZW50YD1rKSAlPiUNCiAgcmVzaGFwZTI6Om1lbHQoaWQudmFycz0iRGlyaWNobGV0IGNvbXBvbmVudCIpICU+JSANCiAgbXV0YXRlKHNlZWQ9cmVwKHNvcnQocmVwKDE6NSwgbGVuZ3RoKGspKSksNSkpICU+JQ0KICBtdXRhdGUoYm9vdD1zb3J0KHJlcChwYXN0ZTAoImJvb3RfIiwxOjUpLCBsZW5ndGgoaykqNSkpKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX2xpbmUoYWVzKHg9YERpcmljaGxldCBjb21wb25lbnRgLHk9dmFsdWUsZ3JvdXA9dmFyaWFibGUsY29sb3I9Ym9vdCkpICsNCiAgZ2d0aXRsZSgiTGFwbGFjZSBvcHRpbXVuIikgKyAjeGxhYigiRGlyaWNobGV0IGNvbXBvbmVudCIpICsNCiAgdGhlbWVfYncoKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDEsMTAxKSwgYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG4gPSAyMCkpLA0KDQoNCg0KQklDX2RmICU+JSBhcy5kYXRhLmZyYW1lICU+JSANCiAgbXV0YXRlKGBEaXJpY2hsZXQgY29tcG9uZW50YD1rKSAlPiUNCiAgcmVzaGFwZTI6Om1lbHQoaWQudmFycz0iRGlyaWNobGV0IGNvbXBvbmVudCIpICU+JSANCiAgbXV0YXRlKHNlZWQ9cmVwKHNvcnQocmVwKDE6NSwgbGVuZ3RoKGspKSksNSkpICU+JQ0KICBtdXRhdGUoYm9vdD1zb3J0KHJlcChwYXN0ZTAoImJvb3RfIiwxOjUpLCBsZW5ndGgoaykqNSkpKSAlPiUNCiAgZ3JvdXBfYnkoYm9vdCxzZWVkKSAlPiUNCiAgZmlsdGVyKHZhbHVlPT1taW4odmFsdWUpKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeD1gRGlyaWNobGV0IGNvbXBvbmVudGApKSArIA0KICBnZ3RpdGxlKCJCSUMgb3B0aW11biIpICsgDQogIHhsYWIoIkRpcmljaGxldCBjb21wb25lbnRzIikrDQogIHRoZW1lX2J3KCkgKyBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygxLDMwKSwgYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG4gPSAzMCkpLA0KDQoNCmxwbGNfZGYgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIA0KICBtdXRhdGUoYERpcmljaGxldCBjb21wb25lbnRgPWspICU+JQ0KICByZXNoYXBlMjo6bWVsdChpZC52YXJzPSJEaXJpY2hsZXQgY29tcG9uZW50IikgJT4lIA0KICBtdXRhdGUoc2VlZD1yZXAoc29ydChyZXAoMTo1LCBsZW5ndGgoaykpKSw1KSkgJT4lDQogIG11dGF0ZShib290PXNvcnQocmVwKHBhc3RlMCgiYm9vdF8iLDE6NSksIGxlbmd0aChrKSo1KSkpICU+JQ0KICBncm91cF9ieShib290LHNlZWQpICU+JQ0KICBmaWx0ZXIodmFsdWU9PW1pbih2YWx1ZSkpICU+JQ0KICBnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PWBEaXJpY2hsZXQgY29tcG9uZW50YCkpICsgDQogIGdndGl0bGUoIkxhcGxhY2Ugb3B0aW11biIpICsgDQogIHhsaW0oMSwzMCkgKyAgeGxhYigiRGlyaWNobGV0IGNvbXBvbmVudHMiKSsNCiAgdGhlbWVfYncoKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDEsMzApLCBicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDMwKSksDQpsYWJlbHMgPSAiIg0KKSAtPiBmaWdTOEFCQ0QNCg0KZmlnUzhBQkNEDQoNCmBgYA0KDQoNCkJJQyBhbmQgTGFwbGFjZSBoYXZlIGEgZ2xvYmFsIG9wdGltdW4gYW1vbmcgZGlmZmVyZW50cyBzZWVkcyBhbmQgYm9vdHN0cmFwcy4gQWxsIGJvb3RzdHJhcHMgZm91bmQgYXQgbGVhc3Qgb25lIG1vZGVsIG9wdGltYWwgZml0IGF0IGs9MTkuIE1ham9yaXR5IG9mIGJlc3QgbW9kZWwgYmFzZWQgb24gTGFwbGFjZSB3ZXJlIGs9MTkuIEZvciBCSUMsIGs9MTkgYXJyaXZlZCBpbiBzZWNvbmQgcG9zdGl0aW9uLiBGb3IgZG93bnN0cmVhbSwgYW5hbHlzaXMgd2UgdGhlbiBjaG9vc2Ugaz0xOS4NCg0KDQpgYGB7cn0NCg0KcmJpbmQobHBsY19kZiAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgDQogIG11dGF0ZShgRGlyaWNobGV0IGNvbXBvbmVudGA9aywgZml0PSJMYXBsYWNlIikgJT4lDQogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnM9YygiRGlyaWNobGV0IGNvbXBvbmVudCIsImZpdCIpKSAlPiUgDQogIG11dGF0ZShzZWVkPXJlcChzb3J0KHJlcCgxOjUsIGxlbmd0aChrKSkpLDUpKSAlPiUNCiAgbXV0YXRlKGJvb3Q9c29ydChyZXAocGFzdGUwKCJib290XyIsMTo1KSwgbGVuZ3RoKGspKjUpKSkgLA0KDQoNCkJJQ19kZiAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgDQogIG11dGF0ZShgRGlyaWNobGV0IGNvbXBvbmVudGA9aywgZml0PSJCSUMiKSAlPiUNCiAgcmVzaGFwZTI6Om1lbHQoaWQudmFycz1jKCJEaXJpY2hsZXQgY29tcG9uZW50IiwiZml0IikpICU+JSANCiAgbXV0YXRlKHNlZWQ9cmVwKHNvcnQocmVwKDE6NSwgbGVuZ3RoKGspKSksNSkpICU+JQ0KICBtdXRhdGUoYm9vdD1zb3J0KHJlcChwYXN0ZTAoImJvb3RfIiwxOjUpLCBsZW5ndGgoaykqNSkpKSApICU+JQ0KICBncm91cF9ieShib290LHNlZWQsZml0KSAlPiUNCiAgZmlsdGVyKHZhbHVlPT1taW4odmFsdWUpKSAlPiUNCiAgZmlsdGVyKGBEaXJpY2hsZXQgY29tcG9uZW50YD09MTkpICU+JQ0KICBhcnJhbmdlKGRlc2ModmFsdWUpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBncm91cF9ieShmaXQpICU+JQ0KICAjZmlsdGVyKGJvb3QgIT0gImJvb3RfMSIpDQogIHRvcF9uKDMsdmFsdWUpDQoNCmBgYA0KDQpGb3IgZG93bnN0cmVhbSBhbmFseXNpcywgd2UgY2hvb3NlIG1vZGVscyBhbW9uZyBib290c3RyYXAgd2hlcmUgQklDIGFuZCBMYXBsYWNlIHNob3dlZCBrPTE5IGFzIG9wdGltdW0uIEFtb25nIG1vZGVscywgd2UgY2hvb3NlIHRoZSB3b3JzdCBvbmUgdG8gYXZvaWQgbW9kZWwgb3ZlcmZpdHRpbmcuDQoNCg0KYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTJ9DQpib290PTENCnNlZWQ9MQ0KZml0X2dlbnVzX2xpc3QgPSBmaXRfZ2VudXNfYm9vdHN0cmFwW1tib290XV0NCmJlc3RfZ2VudXNfbHBsYyA9IDE5IA0KYmVzdF9nZW51c19scGxjX2lkeCA9IHdoaWNoKHNhcHBseShmaXRfZ2VudXNfbGlzdFtbMV1dLCBmdW5jdGlvbih4KSBhdHRyKHgsIm1peHR1cmUiKSRXZWlnaHQgJT4lIGxlbmd0aCk9PTE5KSAjIDE0dGggaXRlbXMgZnJvbSBsaXN0IGNvcnJlc3BvbmRlZCB0byBrPTE5IGhlcmUNCg0KDQplbnRlcm90eXBlcyA9DQoNCiAgZml0X2dlbnVzX2xpc3RbW3NlZWRdXVtbYmVzdF9nZW51c19scGxjX2lkeF1dICU+JQ0KICBtaXh0dXJlKGFzc2lnbj1UUlVFKSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgc2V0X2NvbG5hbWVzKGMoIkVudGVyb3R5cGVzX2lkIikpDQoNCg0KbWV0YWRhdGEgJT4lDQogIHNlbGVjdChzYW1wbGVfbmFtZSxgUmVnaW9uIE5hbWVgLGNvdW50cnlfb2ZfYmlydGgsc2V4LGFnZV9jYXQsYWdlX3llYXJzLGBTdWItcmVnaW9uIE5hbWVgKSAlPiUNCiAgZmlsdGVyKHNhbXBsZV9uYW1lICVpbiUgcm93bmFtZXMoZW50ZXJvdHlwZXMpKSAlPiUNCiAgd2l0aCguLCB4dGFicyh+YFN1Yi1yZWdpb24gTmFtZWArYWdlX2NhdCwgZGF0YT0uKSkNCg0KZ2VudXMgJT4lDQogIHNlbGVjdCgtb3V0bGllcnMpICU+JQ0KICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygidGF4YSIpICU+JSAuW25hbWVzKHRvcF9nZW51c19tYXNzKSxdLT4gZ2VudXNfZGYNCg0KDQojaGVhdG1hcGRtbih0KGdlbnVzX2RmWyxjb2xuYW1lcyhnZW51c19kZikgJWluJSByb3duYW1lcyhlbnRlcm90eXBlcyldKSxmaXRfZ2VudXNfbGlzdFtbMV1dW1sxXV0sZml0X2dlbnVzX2xpc3RbWzFdXVtbYmVzdF9nZW51c19scGxjXV0pDQpgYGANCg0KDQoNCg0KYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTR9DQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIyBjb2RlIGNodW5rIG51bWJlciAxMTogcG9zdGVyaW9yLW1lYW4tZGlmZg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpwMCA8LSBmaXR0ZWQoZml0X2dlbnVzX2xpc3RbW3NlZWRdXVtbMV1dLCBzY2FsZT1UUlVFKSAgICAgIyBzY2FsZSBieSB0aGV0YQ0KcGJlc3QgPC0gZml0dGVkKGZpdF9nZW51c19saXN0W1tzZWVkXV1bW2Jlc3RfZ2VudXNfbHBsY19pZHhdXSwgc2NhbGU9VFJVRSkNCmNvbG5hbWVzKHBiZXN0KSA8LSBwYXN0ZSgibSIsIDE6YmVzdF9nZW51c19scGxjLCBzZXA9IiIpDQoobWVhbmRpZmYgPC0gY29sU3VtcyhhYnMocGJlc3QgLSBhcy52ZWN0b3IocDApKSkpDQpzdW0obWVhbmRpZmYpDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyMgY29kZSBjaHVuayBudW1iZXIgMTI6IHRhYmxlLTENCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KZGlmZiA8LSByb3dTdW1zKGFicyhwYmVzdCAtIGFzLnZlY3RvcihwMCkpKQ0KbyA8LSBvcmRlcihkaWZmLCBkZWNyZWFzaW5nPVRSVUUpDQpjZGlmZiA8LSBjdW1zdW0oZGlmZltvXSkgLyBzdW0oZGlmZikNCmRmIDwtIGhlYWQoY2JpbmQoTWVhbj1wMFtvXSwgcGJlc3RbbyxdLCBkaWZmPWRpZmZbb10sIGNkaWZmKSwgMzApDQpkZiAlPiUgYXMuZGF0YS5mcmFtZSgpDQoNCmRmX2d1dHppbGxhID0gZGYgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KDQptX29yZGVyID0gZGYgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgc2VsZWN0KC1NZWFuLCAtZGlmZiwtY2RpZmYpICAlPiUgQmlvdHlwZVI6OmRpc3QuSlNEKCkgJT4lIGhjbHVzdChtZXRob2Q9ImNvbXBsZXRlIikgJT4lIHVuY2xhc3MgJT4lIC4kb3JkZXINCiNtX29yZGVyID0gZGYgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgc2VsZWN0KC1NZWFuLCAtZGlmZiwtY2RpZmYpICAlPiUgdCAlPiUgZGlzdCgpICU+JSBoY2x1c3QobWV0aG9kPSJjb21wbGV0ZSIpICU+JSB1bmNsYXNzICU+JSAuJG9yZGVyIA0KDQoNCmRmICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJ0YXhhIikgJT4lDQogIG11dGF0ZSh0YXhhPWZhY3Rvcih0YXhhLCBsZXZlbHM9cm93Lm5hbWVzKGRmKSkpICU+JSANCiAgcmVzaGFwZTI6Om1lbHQoaWQudmFycz0idGF4YSIpICU+JQ0KICBmaWx0ZXIoIXZhcmlhYmxlICVpbiUgYygiTWVhbiIsImRpZmYiLCJjZGlmZiIpKSAlPiUNCiAgbXV0YXRlKHZhcmlhYmxlID0gdmFyaWFibGUgJT4lIGFzLmNoYXJhY3RlcikgJT4lIA0KICBtdXRhdGUodmFyaWFibGUgPSB2YXJpYWJsZSAlPiUgZmFjdG9yKGxldmVscz1wYXN0ZTAoIm0iLDE6MTkpW21fb3JkZXJdKSApICAtPiBnZW51c19hbHBoYV93ZWlnaHRfZ3V0emlsbGENCg0Kc2F2ZShnZW51c19hbHBoYV93ZWlnaHRfZ3V0emlsbGEsIGZpbGU9ImdlbnVzX2FscGhhX3dlaWdodF9ndXR6aWxsYS5yZGEiKQ0KDQpkZiAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigidGF4YSIpICU+JQ0KICBtdXRhdGUodGF4YT1mYWN0b3IodGF4YSwgbGV2ZWxzPXJvdy5uYW1lcyhkZikpKSAlPiUgDQogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnM9InRheGEiKSAlPiUNCiAgZmlsdGVyKCF2YXJpYWJsZSAlaW4lIGMoIk1lYW4iLCJkaWZmIiwiY2RpZmYiKSkgJT4lDQogICNncm91cF9ieSh0YXhhKSAlPiUNCiAgI211dGF0ZSh0b3RhbCA9IHN1bSh2YWx1ZSkpICU+JQ0KICAjbXV0YXRlKHZhbHVlPXZhbHVlL3RvdGFsKSAlPiUNCiAgZ2dwbG90KCkgKyANCiAgZ2VvbV90aWxlKGFlcyh4PXZhcmlhYmxlLHk9dGF4YSxmaWxsPXZhbHVlKSkgKyANCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoIkRNTSBtb2RlbCBhbHBoYSIpICsNCiAgeWxhYigidGF4YSIpICsNCiAgeGxhYigiRE1NIGNvbXBvbmVudHMiKQ0KICANCg0KDQpgYGANCg0KDQoNCmxvb2sgYXQgdGhldGEgcGVyIGNvbXBvbmVudA0KYGBge3J9DQoNCiNmaXR0ZWQoZml0X2dlbnVzX2xpc3RbWzFdXVtbYmVzdF9nZW51c19scGxjXV0pDQoNCm1peHR1cmV3dChmaXRfZ2VudXNfbGlzdFtbc2VlZF1dW1sxXV0pDQoNCm1peHR1cmV3dChmaXRfZ2VudXNfbGlzdFtbc2VlZF1dW1tiZXN0X2dlbnVzX2xwbGNfaWR4XV0pDQoNCm1peHR1cmV3dChmaXRfZ2VudXNfbGlzdFtbc2VlZF1dW1tiZXN0X2dlbnVzX2xwbGNfaWR4XV0pICU+JSANCiAgdGliYmxlOjpyb3dpZF90b19jb2x1bW4oImsiKSAlPiUgDQogIGdncGxvdCgpICsgDQogIGdlb21fYmFyKGFlcyh4PWsseT10aGV0YSksc3RhdD0iaWRlbnRpdHkiKSArIA0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtaXh0dXJld3QoZml0X2dlbnVzX2xpc3RbW3NlZWRdXVtbMV1dKSR0aGV0YSwgY29sPSJyZWQiKSArDQogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArDQogIHlsYWIoInRoZXRhIikgKw0KICB4bGFiKCJETU0gcGFydGl0aW9ucyIpIC0+IGZpZ1M4RQ0KDQpmaWdTOEUNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCg0KbWV0YWRhdGEgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzLCBieS54PSJzYW1wbGVfbmFtZSIsIGJ5Lnk9InJvdy5uYW1lcyIsIGFsbC55PSJUUlVFIikgJT4lDQogIGdyb3VwX2J5KGBTdWItcmVnaW9uIE5hbWVgKSAlPiUNCiAgbXV0YXRlKHRvdGFsPW4oKSkgJT4lDQogIGdyb3VwX2J5KGBTdWItcmVnaW9uIE5hbWVgLEVudGVyb3R5cGVzX2lkLHRvdGFsKSAlPiUNCiAgc3VtbWFyaXNlKHByb3A9bigpKSAlPiUNCiAgbXV0YXRlKHByb3A9cHJvcC90b3RhbCkgJT4lDQogIGdncGxvdCgpICsgZ2VvbV90aWxlKGFlcyh4PUVudGVyb3R5cGVzX2lkICU+JSBhcy5jaGFyYWN0ZXIoKSx5PWBTdWItcmVnaW9uIE5hbWVgLGZpbGw9cHJvcCkpICsgc2NhbGVfZmlsbF92aXJpZGlzX2MoKQ0KDQoNCm1ldGFkYXRhICU+JQ0KICBtZXJnZShlbnRlcm90eXBlcywgYnkueD0ic2FtcGxlX25hbWUiLCBieS55PSJyb3cubmFtZXMiLCBhbGwueT0iVFJVRSIpICU+JQ0KICBmaWx0ZXIoYWdlX2NhdCAhPSAiYmFieSIpICU+JQ0KICBncm91cF9ieShhZ2VfY2F0KSAlPiUNCiAgbXV0YXRlKHRvdGFsPW4oKSkgJT4lDQogIGdyb3VwX2J5KGFnZV9jYXQsRW50ZXJvdHlwZXNfaWQsdG90YWwpICU+JQ0KICBzdW1tYXJpc2UocHJvcD1uKCkpICU+JQ0KICBtdXRhdGUocHJvcD1wcm9wL3RvdGFsKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBncm91cF9ieShFbnRlcm90eXBlc19pZCkgJT4lDQogIG11dGF0ZShwcm9wMj1zdW0ocHJvcCkpICU+JQ0KICBtdXRhdGUocHJvcD1wcm9wL3Byb3AyKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX3RpbGUoYWVzKHg9RW50ZXJvdHlwZXNfaWQgJT4lIGFzLmNoYXJhY3RlcigpLHk9YWdlX2NhdCxmaWxsPXByb3ApKSArIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkNCg0KDQptZXRhZGF0YSAlPiUNCiAgbWVyZ2UoZW50ZXJvdHlwZXMsIGJ5Lng9InNhbXBsZV9uYW1lIiwgYnkueT0icm93Lm5hbWVzIiwgYWxsLnk9IlRSVUUiKSAlPiUNCiAgZmlsdGVyKGFnZV9jYXQgIT0gImJhYnkiKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShibWlfY2F0KSwgYm1pX2NhdCAhPSAiTm90IHByb3ZpZGVkIikgJT4lDQogIGdyb3VwX2J5KGJtaV9jYXQpICU+JQ0KICBtdXRhdGUodG90YWw9bigpKSAlPiUNCiAgZ3JvdXBfYnkoYm1pX2NhdCxFbnRlcm90eXBlc19pZCx0b3RhbCkgJT4lDQogIHN1bW1hcmlzZShwcm9wPW4oKSkgJT4lDQogIG11dGF0ZShwcm9wPXByb3AvdG90YWwpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGdyb3VwX2J5KGJtaV9jYXQpICU+JQ0KICBtdXRhdGUocHJvcDI9c3VtKHByb3ApKSAlPiUNCiAgbXV0YXRlKHByb3A9cHJvcC9wcm9wMikgJT4lDQogIGdncGxvdCgpICsgZ2VvbV90aWxlKGFlcyh4PUVudGVyb3R5cGVzX2lkICU+JSBhcy5jaGFyYWN0ZXIoKSx5PWJtaV9jYXQsZmlsbD1wcm9wKSkgKyBzY2FsZV9maWxsX3ZpcmlkaXNfYygpDQoNCg0KbWV0YWRhdGEgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzLCBieS54PSJzYW1wbGVfbmFtZSIsIGJ5Lnk9InJvdy5uYW1lcyIsIGFsbC55PSJUUlVFIikgJT4lDQogIGZpbHRlcihhZ2VfY2F0ICE9ICJiYWJ5IikgJT4lDQogIGZpbHRlcighaXMubmEoZ2x1dGVuKSwgZ2x1dGVuICE9ICJOb3QgcHJvdmlkZWQiKSAlPiUNCiAgZ3JvdXBfYnkoZ2x1dGVuKSAlPiUNCiAgbXV0YXRlKHRvdGFsPW4oKSkgJT4lDQogIGdyb3VwX2J5KGdsdXRlbixFbnRlcm90eXBlc19pZCx0b3RhbCkgJT4lDQogIHN1bW1hcmlzZShwcm9wPW4oKSkgJT4lDQogIG11dGF0ZShwcm9wPXByb3AvdG90YWwpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGdyb3VwX2J5KGdsdXRlbikgJT4lDQogIG11dGF0ZShwcm9wMj1zdW0ocHJvcCkpICU+JQ0KICBtdXRhdGUocHJvcD1wcm9wL3Byb3AyKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX3RpbGUoYWVzKHg9RW50ZXJvdHlwZXNfaWQgJT4lIGFzLmNoYXJhY3RlcigpLHk9Z2x1dGVuLGZpbGw9cHJvcCkpICsgc2NhbGVfZmlsbF92aXJpZGlzX2MoKQ0KDQptZXRhZGF0YSAlPiUNCiAgbWVyZ2UoZW50ZXJvdHlwZXMsIGJ5Lng9InNhbXBsZV9uYW1lIiwgYnkueT0icm93Lm5hbWVzIiwgYWxsLnk9IlRSVUUiKSAlPiUNCiAgZmlsdGVyKGFnZV9jYXQgIT0gImJhYnkiKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh0eXBlc19vZl9wbGFudHMpLCB0eXBlc19vZl9wbGFudHMgIT0gIk5vdCBwcm92aWRlZCIpICU+JQ0KICBncm91cF9ieSh0eXBlc19vZl9wbGFudHMpICU+JQ0KICBtdXRhdGUodG90YWw9bigpKSAlPiUNCiAgZ3JvdXBfYnkodHlwZXNfb2ZfcGxhbnRzLEVudGVyb3R5cGVzX2lkLHRvdGFsKSAlPiUNCiAgc3VtbWFyaXNlKHByb3A9bigpKSAlPiUNCiAgbXV0YXRlKHByb3A9cHJvcC90b3RhbCkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZ3JvdXBfYnkodHlwZXNfb2ZfcGxhbnRzKSAlPiUNCiAgbXV0YXRlKHByb3AyPXN1bShwcm9wKSkgJT4lDQogIG11dGF0ZShwcm9wPXByb3AvcHJvcDIpICU+JQ0KICBnZ3Bsb3QoKSArIGdlb21fdGlsZShhZXMoeD1FbnRlcm90eXBlc19pZCAlPiUgYXMuY2hhcmFjdGVyKCkseT10eXBlc19vZl9wbGFudHMsZmlsbD1wcm9wKSkgKyBzY2FsZV9maWxsX3ZpcmlkaXNfYygpDQoNCm1ldGFkYXRhICU+JQ0KICBtZXJnZShlbnRlcm90eXBlcywgYnkueD0ic2FtcGxlX25hbWUiLCBieS55PSJyb3cubmFtZXMiLCBhbGwueT0iVFJVRSIpICU+JQ0KICBmaWx0ZXIoYWdlX2NhdCAhPSAiYmFieSIpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGRpZXRfdHlwZSksIGRpZXRfdHlwZSAhPSAiTm90IHByb3ZpZGVkIikgJT4lDQogIGdyb3VwX2J5KGRpZXRfdHlwZSkgJT4lDQogIG11dGF0ZSh0b3RhbD1uKCkpICU+JQ0KICBncm91cF9ieShkaWV0X3R5cGUsRW50ZXJvdHlwZXNfaWQsdG90YWwpICU+JQ0KICBzdW1tYXJpc2UocHJvcD1uKCkpICU+JQ0KICBtdXRhdGUocHJvcD1wcm9wL3RvdGFsKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBncm91cF9ieShkaWV0X3R5cGUpICU+JQ0KICBtdXRhdGUocHJvcDI9c3VtKHByb3ApKSAlPiUNCiAgbXV0YXRlKHByb3A9cHJvcC9wcm9wMikgJT4lDQogIGdncGxvdCgpICsgZ2VvbV90aWxlKGFlcyh4PUVudGVyb3R5cGVzX2lkICU+JSBhcy5jaGFyYWN0ZXIoKSx5PWRpZXRfdHlwZSxmaWxsPXByb3ApKSArIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkNCg0KYGBgDQoNCg0KdG8gZG86IGNvbXB1dGUgZW50ZXJvdHlwZSBwcm9wb3J0aW9uIGZvciBhbGwgY2F0ZWdvcmllcyBhbmQgZG8gYSBQQ0EgYW5hbHlzaXMgd2l0aCBtaW5pbWFsIHNwYW5uaW5nIHRyZWUNCg0KDQpgYGB7cn0NCg0KbWV0YWRhdGEgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzLCBieS54PSJzYW1wbGVfbmFtZSIsIGJ5Lnk9InJvdy5uYW1lcyIsIGFsbC55PSJUUlVFIikgJT4lDQogIHNlbGVjdChzYW1wbGVfbmFtZSxhZ2VfY2F0LHNleCxgU3ViLXJlZ2lvbiBOYW1lYCxibWlfY2F0LCBFbnRlcm90eXBlc19pZCkgJT4lDQogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnM9Yygic2FtcGxlX25hbWUiLCJFbnRlcm90eXBlc19pZCIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh2YWx1ZSksIHZhbHVlICE9ICJOb3QgcHJvdmlkZWQiKSAlPiUNCiAgc2VsZWN0KC12YXJpYWJsZSwgLWBzYW1wbGVfbmFtZWApICU+JQ0KICBncm91cF9ieShFbnRlcm90eXBlc19pZCx2YWx1ZSkgJT4lDQogIGZpbHRlcighaXMubmEodmFsdWUpKSAlPiUNCiAgI211dGF0ZSh0b3RhbD1uKCkpICU+JQ0KICAjZ3JvdXBfYnkoRW50ZXJvdHlwZXNfaWQsdmFsdWUsdG90YWwpICU+JQ0KICAjc3VtbWFyaXNlKHByb3A9bigpKSAlPiUNCiAgcmVzaGFwZTI6OmRjYXN0KEVudGVyb3R5cGVzX2lkfnZhbHVlKSAlPiUNCiAgbXV0YXRlX2F0KC0xLH4uLyhzdW0oLikpKSAtPiBldF9yZXBhcnRpdGlvbg0KDQpldF9yZXBhcnRpdGlvbiAlPiUNCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoIkVudGVyb3R5cGVzX2lkIikgJT4lDQogIGFwcGx5KDEsZnVuY3Rpb24oeCkgeC9zdW0oeCkpICU+JQ0KICB0ICU+JQ0KICBhcy5kYXRhLmZyYW1lICU+JQ0KICBhZGU0OjpkdWRpLnBjYSguLHNjYW49RiwgbmY9MykgLT4gZXRfbWV0YWRhdGFfcGNhDQoNCg0KYWRlNDo6c2NhdHRlcihldF9tZXRhZGF0YV9wY2EpDQoNCmFkZTQ6OnMubGFiZWwoZXRfbWV0YWRhdGFfcGNhJGxpLCB5YXg9MikNCg0KYGBgDQoNCg0KYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTJ9DQoNCmRmICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIHNlbGVjdCgtZGlmZiwtY2RpZmYsLU1lYW4pICU+JQ0KICBtdXRhdGVfYXQoMToxOSx+bG9nMTAoLikpICU+JQ0KICB0ICU+JQ0KICBhcy5kYXRhLmZyYW1lICU+JQ0KICBhZGU0OjpkdWRpLnBjYShzY2FubmYgPSBGLG5mPTMpICU+JSAuJGxpICU+JSBhZGU0OjpzLmxhYmVsKCkNCg0KDQoNCg0KZGYgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgc2VsZWN0KC1kaWZmLC1jZGlmZiwtTWVhbikgJT4lDQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJ0YXhhIikgJT4lDQogICNtdXRhdGUodGF4YTI9dGF4YSkgJT4lDQogICNtdXRhdGUodGF4YTI9aWZlbHNlKCkpDQogICAgYXNfdGliYmxlICU+JQ0KICAuW2MoMTozLDcsMTEpLF0gJT4lDQogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnM9InRheGEiKSAlPiUNCiAgZ3JvdXBfYnkodmFyaWFibGUpICU+JQ0KICBtdXRhdGUoeW1heD1jdW1zdW0odmFsdWUpKSAlPiUNCiAgbXV0YXRlKHltaW49YygwLCBoZWFkKHltYXgsIG49LTEpKSkgJT4lDQogICNnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh5PXZhbHVlLHg9dmFyaWFibGUsZmlsbD10YXhhKSxzdGF0PSJpZGVudGl0eSIpIA0KICBnZ3Bsb3QoYWVzKHltYXg9eW1heCwgeW1pbj15bWluLCB4bWF4PTQsIHhtaW49MywgZmlsbD10YXhhICU+JSBzdHJzcGxpdCh4PS4sc3BsaXQ9IjsiKSAlPiUgc2FwcGx5KGZ1bmN0aW9uKHgpeFs2XSkgICkpICsgDQogIGdlb21fcmVjdCgpICsgICAgICANCiAgY29vcmRfcG9sYXIodGhldGE9InkiKSArIA0KICBmYWNldF93cmFwKH52YXJpYWJsZSkgKyANCiAgeGxpbShjKDIsIDQpKSArIHRoZW1lX3ZvaWQoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKCIiLHR5cGU9InF1YWwiKQ0KDQpnZ3NhdmUoImFncF9taWNyb2Jpb3RhX3R5cGVzLnBkZiIpDQoNCmBgYA0KDQpyZWNsYXNzaWZ5IGFsbCBkYXRhc2V0DQpgYGB7cn0NCg0KIyBmaXRfZ2VudXNfbGlzdFtbMV1dW1tiZXN0X2dlbnVzX2xwbGNdXSAlPiUNCiMgICBtaXh0dXJlKGFzc2lnbj1UUlVFKSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgc2V0X2NvbG5hbWVzKGMoIkVudGVyb3R5cGVzX2lkIikpDQoNCmdlbnVzX3NlbGVjdD1yb3duYW1lcyhmaXR0ZWQoZml0X2dlbnVzX2Jvb3RzdHJhcFtbMV1dW1sxXV1bW2Jlc3RfZ2VudXNfbHBsY11dKSkNCmdlbnVzICU+JQ0KICAgICAgc2VsZWN0KC1vdXRsaWVycykgJT4lDQogICAgICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygidGF4YSIpICU+JSAuW2dlbnVzX3NlbGVjdCxdLT4gZ2VudXNfZGYNCg0KZ2VudXNfZGYgJT4lIGNvbG5hbWVzKCkgJT4lIGRhdGEuZnJhbWUoc2FtcGxlX25hbWU9LikgLT4gZW50ZXJvdHlwZXNfY2x1c3RlcnNfZGYNCg0KDQogaz0xDQpmb3IoaSBpbiAxOjUpIHsNCiAgDQogIGZvcihqIGluIDE6NSl7DQogICAgDQoNCiAgIA0KICAgIA0KICAgIHByZWRpY3QoZml0X2dlbnVzX2Jvb3RzdHJhcFtbaV1dW1tqXV1bW2Jlc3RfZ2VudXNfbHBsY11dLCB0KGdlbnVzX2RmKSwgYXNzaWduID0gVFJVRSkgJT4lDQogICAgICByZXNoYXBlMjo6bWVsdCgpICU+JQ0KICAgICAgZ3JvdXBfYnkoVmFyMSkgJT4lDQogICAgICBmaWx0ZXIodmFsdWU9PW1heCh2YWx1ZSkpICU+JQ0KICAgICAgZHBseXI6OnJlbmFtZShgI1NhbXBsZUlEYD0iVmFyMSIsIEVudGVyb3R5cGVzX2lkPSJWYXIyIikgJT4lDQogICAgICBzZWxlY3QoYCNTYW1wbGVJRGAsRW50ZXJvdHlwZXNfaWQpIC0+IGVudGVyb3R5cGVzX3ByZWRpY3Rpb25fbWF4DQogICAgDQogICAgY29sbmFtZXMoZW50ZXJvdHlwZXNfcHJlZGljdGlvbl9tYXgpID0gYygic2FtcGxlX25hbWUiLHBhc3RlMCgiRW50ZXJvdHlwZXNfaWRfIixrKSkNCiAgICANCiAgICBlbnRlcm90eXBlc19jbHVzdGVyc19kZj0NCiAgICBlbnRlcm90eXBlc19jbHVzdGVyc19kZiAlPiUNCiAgICAgIG1lcmdlKGVudGVyb3R5cGVzX3ByZWRpY3Rpb25fbWF4LCBieT0ic2FtcGxlX25hbWUiKQ0KICAgIA0KICAgIGNhdChrLCJcbiIpDQogICAgaz1rKzENCiAgIA0KICAgIA0KICB9DQogIA0KICANCn0NCg0KIGVudGVyb3R5cGVzX2NsdXN0ZXJzX2Rpc3QgPSBtYXRyaXgobnI9MjUsbmM9MjUpICANCg0KIGZvcihpIGluIDE6MjUpIHsNCiAgICAgZm9yIChqIGluIDE6MjUpIHsNCiAgICAgDQogICAgIGVudGVyb3R5cGVzX2NsdXN0ZXJzX2Rpc3RbaSxqXSA9IG1jbHVzdDo6YWRqdXN0ZWRSYW5kSW5kZXgoZW50ZXJvdHlwZXNfY2x1c3RlcnNfZGZbLTFdWyxpXSxlbnRlcm90eXBlc19jbHVzdGVyc19kZlstMV1bLGpdKQ0KICAgICANCiAgICAgDQogICB9DQogfQ0KIA0KIA0KDQpgYGANCg0KIyMjIGNsYXNzaWZ5IGRhdGEgd2l0aCB0aGUgY2hvb3NlbiBtb2RlbA0KDQoNCmBgYHtyfQ0KDQoNCg0KcHJlZGljdChmaXRfZ2VudXNfbGlzdFtbMV1dW1sxNF1dLCB0KGdlbnVzX2RmKSwgYXNzaWduID0gVFJVRSkgJT4lDQogIHJlc2hhcGUyOjptZWx0KCkgJT4lDQogIGdyb3VwX2J5KFZhcjEpICU+JQ0KICBmaWx0ZXIodmFsdWU9PW1heCh2YWx1ZSkpICU+JQ0KICBkcGx5cjo6cmVuYW1lKGBzYW1wbGVfbmFtZWA9IlZhcjEiLCBFbnRlcm90eXBlc19pZD0iVmFyMiIpICU+JQ0KICBtdXRhdGUoc2V0PWlmZWxzZShzYW1wbGVfbmFtZSAlaW4lIHJvdy5uYW1lcyhhdHRyKGZpdF9nZW51c19saXN0W1sxXV1bWzE0XV0sImdyb3VwIikpLCAidHJhaW4iLCAicHJlZGljdGVkIikpIC0+IGVudGVyb3R5cGVzX3ByZWRpY3Rpb24NCg0KDQplbnRlcm90eXBlc19wcmVkaWN0aW9uICU+JQ0KICBnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGFlcyh4PXZhbHVlLCBmaWxsPXZhbHVlPjAuODApLCBiaW5zID0gNTApICsgDQogICNmYWNldF93cmFwKH5zZXQpICsgDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMS4wNSksIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkNCg0KZW50ZXJvdHlwZXNfcHJlZGljdGlvbiAlPiUNCiAgZ2dwbG90KCkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9dmFsdWUpLCBiaW5zID0gNTApICsgDQogICNmYWNldF93cmFwKH5zZXQpICsgDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMS4wNSksIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgLT4gZmlnUzhGDQoNCg0KYGBgDQoNCiMjIyBleHBvcnQgZW50ZXJvdHlwZXMgd2l0aCBvdXRsaWVycw0KDQpgYGB7cn0NCg0KZW50ZXJvdHlwZXNfcHJlZGljdGlvbiAlPiUNCiAgYXMuZGF0YS5mcmFtZSAlPiUNCiAgcmJpbmQoZGF0YS5mcmFtZShzYW1wbGVfbmFtZSA9IG91dGxpZXJzLEVudGVyb3R5cGVzX2lkPU5BLHZhbHVlPU5BLHNldD0ib3V0bGllcnMiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpKSAlPiUNCiAgd3JpdGUuY3N2MihmaWxlPSJlbnRlcm90eXBlc19wcmVkaWN0aW9uX291dGxpZXJzLmNzdiIpDQoNCg0KYGBgDQoNCg0KDQoNCg0KDQojIyBFbnRlcm90eXBlcyB2cyBBZ2UgYW5kIFJpY2huZXNzDQoNCg0KIyMjIEFnZQ0KDQoNCmBgYHtyfQ0KDQptZXRhZGF0YSAlPiUNCiAgbWVyZ2UoZW50ZXJvdHlwZXMsIGJ5Lng9InNhbXBsZV9uYW1lIiwgYnkueT0icm93Lm5hbWVzIiwgYWxsLnk9IlRSVUUiKSAlPiUNCiAgc2VsZWN0KEVudGVyb3R5cGVzX2lkLCBhZ2VfeWVhcnMpICU+JSBnZ3Bsb3QoKSArIGdlb21fYm94cGxvdChhZXMoeD1FbnRlcm90eXBlc19pZCU+JSBhcy5jaGFyYWN0ZXIsIHk9YWdlX3llYXJzKSkNCiAgZ3JvdXBfYnkoRW50ZXJvdHlwZXNfaWQpICU+JQ0KICBzdW1tYXJpc2UoYWdlX3llYXJzPW1vZGUoYWdlX3llYXJzKSkNCiAgDQoNCmBgYA0KDQoNCg0KIyMgcmljaG5lc3MNCg0KDQpgYGB7cn0NCg0Kc2hhbm5vbl9wYXRoID0gc3lzdGVtLmZpbGUoImRhdGEtcmF3L3FpaW1lL2dlbmVyYXRlZC1maWxlcy0yMDE5MDUxMi9hbHBoYS9zaGFubm9uLnF6YSIsIHBhY2thZ2UgPSAiZ3V0emlsbGEiKQ0Kc2hhbm5vbiA9IHFpaW1lMlI6OnJlYWRfcXphKHNoYW5ub25fcGF0aCkkZGF0YSAlPiUgYXMuZGF0YS5mcmFtZQ0KDQptZXJnZShlbnRlcm90eXBlc19wcmVkaWN0aW9uLHNoYW5ub24sYnkueD0ic2FtcGxlX25hbWUiLCBieS55PSJyb3cubmFtZXMiKSAgLT4gZW50ZXJvdHlwZXNfYWxwaGFfZGl2DQogIA0KZW50ZXJvdHlwZXNfYWxwaGFfZGl2ICU+JSANCiAgZ2dwbG90KCkgICArIA0KICBnZW9tX2JveHBsb3QoYWVzKHg9cGFzdGUwKCJNIixFbnRlcm90eXBlc19pZCAlPiUgYXMuY2hhcmFjdGVyKSx5PXNoYW5ub24sIGZpbGw9c2V0KSkgKw0KICB4bGFiKCJETU0gcGFydGl0aW9ucyIpICsgeWxhYigiYWxwaGEtZGl2ZXJzaXR5IChzaGFubm9uKSIpICsNCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKSAtPiBmaWdTOEcNCmZpZ1M4Rw0KDQoNCg0KYGBgDQoNCiMjIGZpZ3VyZSBzdXBwIDgNCg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTZ9DQoNCihmaWdTOEFCQ0QvKGZpZ1M4RStmaWdTOEYrZmlnUzhHICsgcGxvdF9sYXlvdXQod2lkdGhzID0gYygxLDEsMS41KSkpKSArIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDIsMSkpICsgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAnYScpIC0+IGZpZ1M4DQoNCmZpZ1M4DQpnZ3NhdmUoImZpZ1M4LnBkZiIpDQoNCg0KDQpgYGANCg0KDQoNCiMjIGNoZWNrIGZpYnJlcw0KDQoNCmBgYHtyfQ0KDQpmaWJyZV9wYXRoID0gc3lzdGVtLmZpbGUoImRhdGEtcmF3L3Zpb3NjcmVlbi9GSUNISUVSX0pVTElFTl9GSUJFUl9IT1NUU1VCSkVDVElEXzIueGxzeCIsIHBhY2thZ2UgPSAiYWdwIikNCg0KcmVhZHhsOjpyZWFkX3hsc3goZmlicmVfcGF0aCkgJT4lDQogIHNlbGVjdCgtRklCRVIuLi42KSAlPiUNCiAgZHBseXI6OnJlbmFtZShGSUJFUj0iRklCRVIuLi40IikgLT4gZmlicmVfZGYNCiAgDQoNCmNvbG5hbWVzKGZpYnJlX2RmKSA9IGNvbG5hbWVzKGZpYnJlX2RmKSAlPiUgc3RyaW5ncjo6c3RyX3RvX2xvd2VyKCkNCg0KYGBgDQoNCm49MTI3NSB3aXRoIGZpYmVyIGFuZCBlbnRlcm90eXBlcyBkYXRhDQoNCmBgYHtyfQ0KDQpmaWJyZV9kZiAlPiUNCiAgbWVyZ2UoZW50ZXJvdHlwZXNfYWxwaGFfZGl2LCBieT0ic2FtcGxlX25hbWUiKSAlPiUgDQogIGdncGxvdCgpICsgDQogIGdlb21fYm94cGxvdChhZXMoeT1zaGFubm9uLHg9cGFzdGUwKCJRIixxdWFydGlsZV9maWJlciksZmlsbD1wYXN0ZTAoIlEiLHF1YXJ0aWxlX2ZpYmVyKSkpICsNCiAgeGxhYigiZmliZXJcbnF1YXJ0aWxlcyIpICsgeWxhYigiYWxwaGEtZGl2ZXJzaXR5IChzaGFubm9uKSIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIoImZpYmVyXG5xdWFydGlsZXMiLHR5cGU9ImRpdiIscGFsZXR0ZSA9IDIpDQoNCg0KZmlicmVfZGYgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzX2FscGhhX2RpdiwgYnk9InNhbXBsZV9uYW1lIikgJT4lIA0KICBnZ3Bsb3QoKSArIA0KICBnZW9tX2JveHBsb3QoYWVzKHk9c2hhbm5vbix4PXBhc3RlMCgibSIsRW50ZXJvdHlwZXNfaWQgJT4lIGFzLmNoYXJhY3RlciksZmlsbD1wYXN0ZTAoIlEiLHF1YXJ0aWxlX2ZpYmVyKSkpICsNCiAgeGxhYigiTWljcm9iaW90YSBETU0gYmFzZWQgY2x1c3RlcnMiKSArIHlsYWIoImFscGhhLWRpdmVyc2l0eSAoc2hhbm5vbikiKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKCJmaWJlclxucXVhcnRpbGVzIix0eXBlPSJkaXYiLHBhbGV0dGUgPSAyKQ0KDQoNCmZpYnJlX2RmICU+JQ0KICBtZXJnZShlbnRlcm90eXBlc19hbHBoYV9kaXYsIGJ5PSJzYW1wbGVfbmFtZSIpICU+JSANCiAgZ3JvdXBfYnkoRW50ZXJvdHlwZXNfaWQpICU+JQ0KICBkbyh3aXRoKC4sa3J1c2thbC50ZXN0KC4kc2hhbm5vbiwuJHF1YXJ0aWxlX2ZpYmVyLCBtZXRob2Q9InNwZWFybWFuIikgJT4lIGJyb29tOjp0aWR5KCkgKSkNCg0KZmlicmVfZGYgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzX2FscGhhX2RpdiwgYnk9InNhbXBsZV9uYW1lIikgJT4lIA0KICBncm91cF9ieShFbnRlcm90eXBlc19pZCkgJT4lDQogIHN1bW1hcmlzZShuPW4oKSkNCg0KZmlicmVfZGYgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzX2FscGhhX2RpdiwgYnk9InNhbXBsZV9uYW1lIikgJT4lIA0KICBmaWx0ZXIoRW50ZXJvdHlwZXNfaWQgJWluJSBjKDIsNSw2LDcsMTMpKSAlPiUNCiAgZ2dwbG90KCkgKyANCiAgZ2VvbV9ib3hwbG90KGFlcyh5PXNoYW5ub24seD1wYXN0ZTAoIm0iLEVudGVyb3R5cGVzX2lkICU+JSBhcy5jaGFyYWN0ZXIpLGZpbGw9cGFzdGUwKCJRIixxdWFydGlsZV9maWJlcikpKSArDQogIHhsYWIoIk1pY3JvYmlvdGEgRE1NIGJhc2VkIGNsdXN0ZXJzIikgKyB5bGFiKCJhbHBoYS1kaXZlcnNpdHkgKHNoYW5ub24pIikgKw0KICBzY2FsZV9maWxsX2JyZXdlcigiZmliZXJcbnF1YXJ0aWxlcyIsdHlwZT0iZGl2IixwYWxldHRlID0gMikNCg0KDQoNCg0KYGBgDQoNCiMjIGNoZWNrIHR5cGUgb2YgcGxhbnQNCg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQ0KDQoNCg0KbWV0YWRhdGEgJT4lDQogIHNlbGVjdChzYW1wbGVfbmFtZSx0eXBlc19vZl9wbGFudHMpICU+JQ0KICAgbXV0YXRlKHR5cGVzX29mX3BsYW50cz10eXBlc19vZl9wbGFudHMlPiUgZm9yY2F0czo6ZmN0X3JldigpICU+JSBmb3JjYXRzOjpmY3RfcmVsZXZlbCgiTW9yZSB0aGFuIDMwIiwgYWZ0ZXI9SW5mKSAlPiUgZm9yY2F0czo6ZmN0X3JlbGV2ZWwoIjIxIHRvIDMwIiwgYWZ0ZXI9MykpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzX2FscGhhX2RpdiwgYnk9InNhbXBsZV9uYW1lIikgJT4lIA0KICBnZ3Bsb3QoKSArIA0KICBnZW9tX2JveHBsb3QoYWVzKHk9c2hhbm5vbix4PXR5cGVzX29mX3BsYW50cyxmaWxsPXR5cGVzX29mX3BsYW50cykpICsNCiAgeGxhYigibmIgdHlwZSBvZiBwbGFudHMgcGVyIHdlZWsiKSArIHlsYWIoImFscGhhLWRpdmVyc2l0eSAoc2hhbm5vbikiKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKCJUeXBlIG9mIHBsYW50cyIsdHlwZT0iZGl2IixwYWxldHRlID0gMikgKw0KICB4bGFiKCJUeXBlIG9mIHBsYW50cyIpICsgeWxhYigiYWxwaGEtZGl2ZXJzaXR5IChzaGFubm9uKSIpICsgZ2d0aXRsZSgiYWxsIHBvcCIpDQoNCm1ldGFkYXRhICU+JQ0KICBzZWxlY3Qoc2FtcGxlX25hbWUsdHlwZXNfb2ZfcGxhbnRzKSAlPiUNCiAgbWVyZ2UoZmlicmVfZGYsIGJ5PSJzYW1wbGVfbmFtZSIpICU+JQ0KICBtdXRhdGUodHlwZXNfb2ZfcGxhbnRzPXR5cGVzX29mX3BsYW50cyU+JSBmb3JjYXRzOjpmY3RfcmV2KCkgJT4lIGZvcmNhdHM6OmZjdF9yZWxldmVsKCJNb3JlIHRoYW4gMzAiLCBhZnRlcj1JbmYpICU+JSBmb3JjYXRzOjpmY3RfcmVsZXZlbCgiMjEgdG8gMzAiLCBhZnRlcj0zKSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgbWVyZ2UoZW50ZXJvdHlwZXNfYWxwaGFfZGl2LCBieT0ic2FtcGxlX25hbWUiKSAlPiUgDQogIGdncGxvdCgpICsgDQogIGdlb21fYm94cGxvdChhZXMoeT1zaGFubm9uLHg9dHlwZXNfb2ZfcGxhbnRzLGZpbGw9dHlwZXNfb2ZfcGxhbnRzKSkgKw0KICB4bGFiKCJuYiB0eXBlIG9mIHBsYW50cyBwZXIgd2VlayIpICsgeWxhYigiYWxwaGEtZGl2ZXJzaXR5IChzaGFubm9uKSIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIoIlR5cGUgb2YgcGxhbnRzIix0eXBlPSJkaXYiLHBhbGV0dGUgPSAyKSArDQogIHhsYWIoIlR5cGUgb2YgcGxhbnRzICIpICsgeWxhYigiYWxwaGEtZGl2ZXJzaXR5IChzaGFubm9uKSIpICsgZ2d0aXRsZSgid2l0aCB2aW9zY3JlZW5lZCBvbmx5IikNCg0KDQoNCm1ldGFkYXRhICU+JQ0KICBzZWxlY3Qoc2FtcGxlX25hbWUsdHlwZXNfb2ZfcGxhbnRzKSAlPiUNCiAgIG11dGF0ZSh0eXBlc19vZl9wbGFudHM9dHlwZXNfb2ZfcGxhbnRzJT4lIGZvcmNhdHM6OmZjdF9yZXYoKSAlPiUgZm9yY2F0czo6ZmN0X3JlbGV2ZWwoIk1vcmUgdGhhbiAzMCIsIGFmdGVyPUluZikgJT4lIGZvcmNhdHM6OmZjdF9yZWxldmVsKCIyMSB0byAzMCIsIGFmdGVyPTMpKSAlPiUNCiAgbmEub21pdCgpICU+JQ0KICBtZXJnZShlbnRlcm90eXBlc19hbHBoYV9kaXYsIGJ5PSJzYW1wbGVfbmFtZSIpICU+JSANCiAgZ2dwbG90KCkgKyANCiAgZ2VvbV9ib3hwbG90KGFlcyh5PXNoYW5ub24seD1wYXN0ZTAoIm0iLEVudGVyb3R5cGVzX2lkICU+JSBhcy5jaGFyYWN0ZXIpLGZpbGw9dHlwZXNfb2ZfcGxhbnRzKSkgKw0KICB4bGFiKCJNaWNyb2Jpb3RhIERNTSBiYXNlZCBjbHVzdGVycyIpICsgeWxhYigiYWxwaGEtZGl2ZXJzaXR5IChzaGFubm9uKSIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIoIlR5cGUgb2ZcbnBsYW50Iix0eXBlPSJkaXYiLHBhbGV0dGUgPSAyKSArIGdndGl0bGUoImFsbCBwb3AiKQ0KDQoNCg0KbWV0YWRhdGEgJT4lDQogIHNlbGVjdChzYW1wbGVfbmFtZSx0eXBlc19vZl9wbGFudHMpICU+JQ0KICBtZXJnZShmaWJyZV9kZiwgYnk9InNhbXBsZV9uYW1lIikgJT4lDQogICBtdXRhdGUodHlwZXNfb2ZfcGxhbnRzPXR5cGVzX29mX3BsYW50cyU+JSBmb3JjYXRzOjpmY3RfcmV2KCkgJT4lIGZvcmNhdHM6OmZjdF9yZWxldmVsKCJNb3JlIHRoYW4gMzAiLCBhZnRlcj1JbmYpICU+JSBmb3JjYXRzOjpmY3RfcmVsZXZlbCgiMjEgdG8gMzAiLCBhZnRlcj0zKSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgbWVyZ2UoZW50ZXJvdHlwZXNfYWxwaGFfZGl2LCBieT0ic2FtcGxlX25hbWUiKSAlPiUgDQogIGdncGxvdCgpICsgDQogIGdlb21fYm94cGxvdChhZXMoeT1zaGFubm9uLHg9cGFzdGUwKCJtIixFbnRlcm90eXBlc19pZCAlPiUgYXMuY2hhcmFjdGVyKSxmaWxsPXR5cGVzX29mX3BsYW50cykpICsNCiAgeGxhYigiTWljcm9iaW90YSBETU0gYmFzZWQgY2x1c3RlcnMiKSArIHlsYWIoImFscGhhLWRpdmVyc2l0eSAoc2hhbm5vbikiKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKCJUeXBlIG9mXG5wbGFudCIsdHlwZT0iZGl2IixwYWxldHRlID0gMikgKyBnZ3RpdGxlKCJ3aXRoIHZpb3NjcmVlbmVkIG9ubHkiKQ0KDQoNCg0KDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQptZXRhZGF0YSAlPiUNCiAgc2VsZWN0KHNhbXBsZV9uYW1lLHR5cGVzX29mX3BsYW50cykgJT4lDQogIG1lcmdlKGZpYnJlX2RmLCBieT0ic2FtcGxlX25hbWUiKSAlPiUNCiAgIG11dGF0ZSh0eXBlc19vZl9wbGFudHM9dHlwZXNfb2ZfcGxhbnRzJT4lIGZvcmNhdHM6OmZjdF9yZXYoKSAlPiUgZm9yY2F0czo6ZmN0X3JlbGV2ZWwoIk1vcmUgdGhhbiAzMCIsIGFmdGVyPUluZikgJT4lIGZvcmNhdHM6OmZjdF9yZWxldmVsKCIyMSB0byAzMCIsIGFmdGVyPTMpKSAlPiUNCiAgbmEub21pdCgpICU+JQ0KICBtZXJnZShlbnRlcm90eXBlc19hbHBoYV9kaXYsIGJ5PSJzYW1wbGVfbmFtZSIpICU+JQ0KICBmaWx0ZXIoRW50ZXJvdHlwZXNfaWQgJWluJSBjKDEsMiw1LDcsMTMsMTUpKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX3BvaW50KGFlcyh4PWZpYmVyLHk9c2hhbm5vbixjb2w9dHlwZXNfb2ZfcGxhbnRzKSkgKyANCiAgc2NhbGVfY29sb3JfYnJld2VyKCJUeXBlIG9mXG5wbGFudCIsdHlwZT0iZGl2IixwYWxldHRlID0gMikgKyANCiAgZmFjZXRfd3JhcCh+cGFzdGUwKCJtIixFbnRlcm90eXBlc19pZCksIHNjYWxlcyA9ICJmcmVlX3kiKSArIHRoZW1lX2RhcmsoKSArDQogIGdndGl0bGUoImV4YW1wbGUgb2YgZW50ZXJvdHlwZXMiKQ0KDQoNCg0KDQpgYGANCg0KDQoNCg0KIyMgcmVzY2FsZSBlbnRlcm90eXBlcyB1c2luZyB3b3JsZCBkZW1vZ3JhcGhpYyBkYXRhDQoNCndlIHdpbGwgdXNlIFVOU0Qgc3VicmVnaW9uIHRvIHJlc2NhbGUgZW50ZXJvdHlwZXMgcHJvcG9ydGlvbiBvYnNlcnZlZA0KDQpgYGB7cn0NCg0KZGF0YSgiVU5TRF9wb3Bfc3VicmVnaW9uIikNCg0KVU5TRF9wb3Bfc3VicmVnaW9uICU+JQ0KICAgICAgICAgIG11dGF0ZShTdWJyZWdpb24gPSBpZmVsc2UoU3VicmVnaW9uICVpbiUgYygiTWVsYW5lc2lhIiwiTWljcm9uZXNpYSIsIlBvbHluZXNpYSIpLCAiT2NlYW5pYSAob3RoZXJzKSIsU3VicmVnaW9uKSkgJT4lDQogICAgICAgICAgbXV0YXRlKFN1YnJlZ2lvbiA9ICBpZmVsc2UoU3VicmVnaW9uICVpbiUgYygiQ2VudHJhbCBBc2lhIiwgIlNvdXRoZXJuIEFzaWEiKSwgIkNlbnRyYWwgYW5kIFNvdXRoZXJuIEFzaWEiLFN1YnJlZ2lvbikpICU+JQ0KICAgICAgICAgIG11dGF0ZShTdWJyZWdpb24gPSAgaWZlbHNlKFN1YnJlZ2lvbiAlaW4lIGMoIlNvdXRoLUVhc3Rlcm4gQXNpYSIpLCAiU291dGgtZWFzdGVybiBBc2lhIixTdWJyZWdpb24pKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeT1uX3BvcF8yMDIwLCB4PVN1YnJlZ2lvbiksIHN0YXQ9ImlkZW50aXR5IikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IC05MCwgdmp1c3Q9MC41KSkNCg0KDQoNCm1ldGFkYXRhICU+JQ0KICBtZXJnZShlbnRlcm90eXBlc19wcmVkaWN0aW9uLCBieS54PSJzYW1wbGVfbmFtZSIsIGFsbC55PSJUUlVFIikgJT4lDQogIHNlbGVjdChFbnRlcm90eXBlc19pZCxgU3ViLXJlZ2lvbiBOYW1lYCkgJT4lDQogIG5hLm9taXQgJT4lDQogIGdyb3VwX2J5KGBTdWItcmVnaW9uIE5hbWVgKSAlPiUNCiAgc3VtbWFyaXNlKHRvdGFsPW4oKSkgJT4lDQogIGdncGxvdCgpICsgZ2VvbV9iYXIoYWVzKHk9dG90YWwsIHg9YFN1Yi1yZWdpb24gTmFtZWApLCBzdGF0PSJpZGVudGl0eSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAtOTAsIHZqdXN0PTAuNSkpDQoNCm1ldGFkYXRhICU+JQ0KICBtZXJnZShlbnRlcm90eXBlc19wcmVkaWN0aW9uLCBieS54PSJzYW1wbGVfbmFtZSIsICBhbGwueT0iVFJVRSIpICU+JQ0KICBzZWxlY3QoYFN1Yi1yZWdpb24gTmFtZWApICU+JQ0KICBuYS5vbWl0ICU+JQ0KICBncm91cF9ieShgU3ViLXJlZ2lvbiBOYW1lYCkgJT4lDQogIHN1bW1hcmlzZSh0b3RhbD1uKCkpICU+JQ0KICBtZXJnZShVTlNEX3BvcF9zdWJyZWdpb24gJT4lDQogICAgICAgICAgbXV0YXRlKFN1YnJlZ2lvbiA9IGlmZWxzZShTdWJyZWdpb24gJWluJSBjKCJNZWxhbmVzaWEiLCJNaWNyb25lc2lhIiwiUG9seW5lc2lhIiksICJPY2VhbmlhIChvdGhlcnMpIixTdWJyZWdpb24pKSAlPiUNCiAgICAgICAgICBtdXRhdGUoU3VicmVnaW9uID0gIGlmZWxzZShTdWJyZWdpb24gJWluJSBjKCJDZW50cmFsIEFzaWEiLCAiU291dGhlcm4gQXNpYSIpLCAiQ2VudHJhbCBhbmQgU291dGhlcm4gQXNpYSIsU3VicmVnaW9uKSkgJT4lDQogICAgICAgICAgbXV0YXRlKFN1YnJlZ2lvbiA9ICBpZmVsc2UoU3VicmVnaW9uICVpbiUgYygiU291dGgtRWFzdGVybiBBc2lhIiksICJTb3V0aC1lYXN0ZXJuIEFzaWEiLFN1YnJlZ2lvbikpICU+JQ0KICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KFN1YnJlZ2lvbikgJT4lDQogICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG5fcG9wXzIwMjAgPSBzdW0obl9wb3BfMjAyMCkpLA0KICBieS54PSJTdWItcmVnaW9uIE5hbWUiLCBieS55PSJTdWJyZWdpb24iLCBhbGwueD1UUlVFKSAlPiUNCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsZnVuY3Rpb24oeCkgeC9zdW0oeCkgKSAlPiUNCiAgZHBseXI6OnJlbmFtZShuX3BvcF9hZ3BfYmlydGg9InRvdGFsIikgLT4gcmVnaW9uX25fcG9wX2FncF91bnNkDQoNCiAgcmVnaW9uX25fcG9wX2FncF91bnNkICU+JQ0KICByZXNoYXBlMjo6bWVsdChpZC52YXJzPSJTdWItcmVnaW9uIE5hbWUiKSAlPiUNCiAgZ2dwbG90KCkgKyANCiAgZ2VvbV9iYXIoYWVzKHg9YFN1Yi1yZWdpb24gTmFtZWAsIHk9dmFsdWUsIGZpbGw9dmFyaWFibGUpLCBzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IC05MCwgdmp1c3Q9MC41KSkgDQoNCiAgDQogIA0KICANCg0KDQoNCmBgYA0KDQpgYGB7cn0NCg0KbWV0YWRhdGEgJT4lDQogIG1lcmdlKGVudGVyb3R5cGVzX3ByZWRpY3Rpb24sIGJ5Lng9InNhbXBsZV9uYW1lIiwgIGFsbC55PSJUUlVFIikgJT4lDQogIHNlbGVjdChgU3ViLXJlZ2lvbiBOYW1lYCxFbnRlcm90eXBlc19pZCxzZXQpICU+JQ0KICBnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PUVudGVyb3R5cGVzX2lkLGZpbGw9c2V0KSkgDQoNCm1ldGFkYXRhICU+JQ0KICBtZXJnZShlbnRlcm90eXBlc19wcmVkaWN0aW9uLCBieS54PSJzYW1wbGVfbmFtZSIsICBhbGwueT0iVFJVRSIpICU+JQ0KICBzZWxlY3QoYFN1Yi1yZWdpb24gTmFtZWAsRW50ZXJvdHlwZXNfaWQsc2V0KSAlPiUNCiAgbmEub21pdCAlPiUNCiAgZ3JvdXBfYnkoYFN1Yi1yZWdpb24gTmFtZWAsRW50ZXJvdHlwZXNfaWQpICU+JQ0KICBzdW1tYXJpc2Uobl9wZXJfZXQ9bigpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBncm91cF9ieShgU3ViLXJlZ2lvbiBOYW1lYCkgJT4lDQogIG11dGF0ZShuX3Blcl9yZWdpb249c3VtKG5fcGVyX2V0KSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgI211dGF0ZShuPW4gJT4lIChmdW5jdGlvbih4KSB4IC8gc3VtKHgpKSApICU+JQ0KICBtZXJnZShVTlNEX3BvcF9zdWJyZWdpb24gJT4lDQogICAgICAgICAgZmlsdGVyKCEoU3VicmVnaW9uID09ICJMYXRpbiBBbWVyaWNhIGFuZCB0aGUgQ2FyaWJiZWFuIiAmIFR5cGUgPT0iUmVnaW9uIikpICU+JQ0KICAgICAgICAgIG11dGF0ZShTdWJyZWdpb24gPSBpZmVsc2UoU3VicmVnaW9uICVpbiUgIk9jZWFuaWEgKEV4Y2x1ZGluZyBBdXN0cmFsaWEgYW5kIE5ldyBaZWFsYW5kKSIsICJPY2VhbmlhIChvdGhlcnMpIixTdWJyZWdpb24pKSAlPiUNCiAgICAgICAgICAjbXV0YXRlKFN1YnJlZ2lvbiA9ICBpZmVsc2UoU3VicmVnaW9uICVpbiUgYygiQ2VudHJhbCBBc2lhIiwgIlNvdXRoZXJuIEFzaWEiKSwgIkNlbnRyYWwgYW5kIFNvdXRoZXJuIEFzaWEiLFN1YnJlZ2lvbikpICU+JQ0KICAgICAgICAgIG11dGF0ZShTdWJyZWdpb24gPSAgaWZlbHNlKFN1YnJlZ2lvbiAlaW4lIGMoIlNvdXRoLUVhc3Rlcm4gQXNpYSIpLCAiU291dGgtZWFzdGVybiBBc2lhIixTdWJyZWdpb24pKSAsDQogICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgI3N1bW1hcmlzZShuX3BvcF8yMDIwID0gc3VtKG5fcG9wXzIwMjApKSwNCiAgYnkueD0iU3ViLXJlZ2lvbiBOYW1lIiwgYnkueT0iU3VicmVnaW9uIiwgYWxsLng9VFJVRSkgJT4lDQogIG11dGF0ZShuX2VzdGltYXRlZF9wZXJfcG9wID0gKG5fcGVyX2V0ICogbl9wb3BfMjAyMCkvbl9wZXJfcmVnaW9uKSAlPiUNCiAgc2VsZWN0KC1uX3Blcl9yZWdpb24sLVR5cGUsLW5fcG9wXzIwMjApICU+JQ0KICBtdXRhdGUocF9wZXJfZXQ9bl9wZXJfZXQvc3VtKG5fcGVyX2V0KSwgcF9lc3RpbWF0ZWRfcGVyX3BvcD1uX2VzdGltYXRlZF9wZXJfcG9wL3N1bShuX2VzdGltYXRlZF9wZXJfcG9wKSkgIC0+IGVudGVyb3R5cGVzX2J5X1VOU0RfcmVnaW9ucw0KDQp3cml0ZS5jc3YyKGVudGVyb3R5cGVzX2J5X1VOU0RfcmVnaW9ucywgZmlsZT0iZW50ZXJvdHlwZXNfYnlfVU5TRF9yZWdpb25zLmNzdiIpDQogIA0KICANCmVudGVyb3R5cGVzX2J5X1VOU0RfcmVnaW9ucyAlPiUNCiAgZ3JvdXBfYnkoRW50ZXJvdHlwZXNfaWQpICU+JQ0KICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYyxzdW0pICU+JQ0KICBzZWxlY3QoRW50ZXJvdHlwZXNfaWQscF9wZXJfZXQscF9lc3RpbWF0ZWRfcGVyX3BvcCkgJT4lDQogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnM9IkVudGVyb3R5cGVzX2lkIikgJT4lDQogIGdncGxvdCgpICsgZ2VvbV9iYXIoYWVzKHg9RW50ZXJvdHlwZXNfaWQseT12YWx1ZSxmaWxsPXZhcmlhYmxlKSwgc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpDQoNCg0KYGBgDQoNCg0KDQoNCg==