Contents

1 Setup and data

source("../utils/utils.R")
   ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
   ✔ dplyr     1.1.2     ✔ readr     2.1.4
   ✔ forcats   1.0.0     ✔ stringr   1.5.0
   ✔ ggplot2   3.4.4     ✔ tibble    3.2.1
   ✔ lubridate 1.9.2     ✔ tidyr     1.3.0
   ✔ purrr     1.0.1     
   ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
   ✖ dplyr::filter() masks stats::filter()
   ✖ dplyr::lag()    masks stats::lag()
   ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
   
   Attaching package: 'magrittr'
   
   
   The following object is masked from 'package:purrr':
   
       set_names
   
   
   The following object is masked from 'package:tidyr':
   
       extract
   
   
   Loading required package: GenomicRanges
   
   Loading required package: stats4
   
   Loading required package: BiocGenerics
   
   
   Attaching package: 'BiocGenerics'
   
   
   The following objects are masked from 'package:lubridate':
   
       intersect, setdiff, union
   
   
   The following objects are masked from 'package:dplyr':
   
       combine, intersect, setdiff, union
   
   
   The following objects are masked from 'package:stats':
   
       IQR, mad, sd, var, xtabs
   
   
   The following objects are masked from 'package:base':
   
       anyDuplicated, aperm, append, as.data.frame, basename, cbind,
       colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
       get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply,
       match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
       Position, rank, rbind, Reduce, rownames, sapply, setdiff, sort,
       table, tapply, union, unique, unsplit, which.max, which.min
   
   
   Loading required package: S4Vectors
   
   
   Attaching package: 'S4Vectors'
   
   
   The following objects are masked from 'package:lubridate':
   
       second, second<-
   
   
   The following objects are masked from 'package:dplyr':
   
       first, rename
   
   
   The following object is masked from 'package:tidyr':
   
       expand
   
   
   The following objects are masked from 'package:base':
   
       expand.grid, I, unname
   
   
   Loading required package: IRanges
   
   
   Attaching package: 'IRanges'
   
   
   The following object is masked from 'package:lubridate':
   
       %within%
   
   
   The following objects are masked from 'package:dplyr':
   
       collapse, desc, slice
   
   
   The following object is masked from 'package:purrr':
   
       reduce
   
   
   Loading required package: GenomeInfoDb
   
   
   Attaching package: 'GenomicRanges'
   
   
   The following object is masked from 'package:magrittr':
   
       subtract
   
   
   Loading required package: grid
   
   Loading required package: Biostrings
   
   Loading required package: XVector
   
   
   Attaching package: 'XVector'
   
   
   The following object is masked from 'package:purrr':
   
       compact
   
   
   
   Attaching package: 'Biostrings'
   
   
   The following object is masked from 'package:grid':
   
       pattern
   
   
   The following object is masked from 'package:base':
   
       strsplit
   
   
   
   Attaching package: 'gridExtra'
   
   
   The following object is masked from 'package:BiocGenerics':
   
       combine
   
   
   The following object is masked from 'package:dplyr':
   
       combine
   
   
   
   Attaching package: 'data.table'
   
   
   The following object is masked from 'package:GenomicRanges':
   
       shift
   
   
   The following object is masked from 'package:IRanges':
   
       shift
   
   
   The following objects are masked from 'package:S4Vectors':
   
       first, second
   
   
   The following objects are masked from 'package:lubridate':
   
       hour, isoweek, mday, minute, month, quarter, second, wday, week,
       yday, year
   
   
   The following objects are masked from 'package:dplyr':
   
       between, first, last
   
   
   The following object is masked from 'package:purrr':
   
       transpose
   
   
   
   
   Registered S3 method overwritten by 'gplots':
     method         from 
     reorder.factor gdata
   
   ChIPseeker v1.34.1  For help: https://guangchuangyu.github.io/software/ChIPseeker
   
   If you use ChIPseeker in published research, please cite:
   Qianwen Wang, Ming Li, Tianzhi Wu, Li Zhan, Lin Li, Meijun Chen, Wenqin Xie, Zijing Xie, Erqiang Hu, Shuangbin Xu, Guangchuang Yu. Exploring epigenomic datasets by ChIPseeker. Current Protocols 2022, 2(10): e585
   
   Loading required package: graph
   
   
   Attaching package: 'graph'
   
   
   The following object is masked from 'package:Biostrings':
   
       complement
   
   
   The following object is masked from 'package:stringr':
   
       boundary
   
   
   Loading required package: Biobase
   
   Welcome to Bioconductor
   
       Vignettes contain introductory material; view with
       'browseVignettes()'. To cite Bioconductor, see
       'citation("Biobase")', and for packages 'citation("pkgname")'.
   
   
   Loading required package: GO.db
   
   Loading required package: AnnotationDbi
   
   
   Attaching package: 'AnnotationDbi'
   
   
   The following object is masked from 'package:dplyr':
   
       select
   
   
   Loading required package: SparseM
   
   
   Attaching package: 'SparseM'
   
   
   The following object is masked from 'package:base':
   
       backsolve
   
   
   
   groupGOTerms:    GOBPTerm, GOMFTerm, GOCCTerm environments built.
   
   
   Attaching package: 'topGO'
   
   
   The following object is masked from 'package:grid':
   
       depth
   
   
   The following object is masked from 'package:IRanges':
   
       members
   
   
   Loading required package: GenomicFeatures
   
   
   Attaching package: 'GenomicFeatures'
   
   
   The following object is masked from 'package:topGO':
   
       genes
config = load_config()

# load CHT results
cht_full = lapply(ab_tp_list, function(ab_tp) load_cht_results(ab_tp, remove_chr = F)) %>% bind_rows()
cht = cht_full %>% filter(!TEST.SNP.CHROM %in% c("chrX", "chrY", "chrM"))
cht_sign = cht %>% filter(signif_strongAI) 

# genes and promoters
genes = load_genes()
promoters = resize(genes, width = 1000, fix = "start")

# combined motif set (all TFs, peaks + alleles)
fimo = get_full_motif_sets(cht, ab_tp_list)
# only alleles
fimo_alleles  = lapply(ab_tp_list, function(ab_tp) parse_motifs_in_two_alleles(ab_tp, cht)) %>% bind_rows() 

2 Figure 5B

samples_correlations = read.table("/g/furlong/project/103_Basenji/models/drosophila_l131k_augmented/acc_log.txt", header=TRUE)
samples_correlations$description = factor(samples_correlations$description, levels = c("F1_Chip-seq", "REMAP_ChIP-seq", "DHS"))
p = ggplot(samples_correlations, aes(x=description, y=pearsonr, fill=description)) + 
  geom_jitter(colour="grey50") +
  geom_boxplot(alpha=0.4, width=0.4, outlier.size = 0, colour="grey15") +
  ylim(0,1) +
  xlab("") +
  ylab("Pearson r correlation") +
  scale_fill_manual(values = c("#33B250", "#0066CC", "#CC4741")) +
  theme_bw() + 
  theme(panel.grid = element_line(colour = "grey80", linewidth = 1), axis.text = element_text(size = 12)) +
  theme(axis.title = element_text(size = 12), plot.title = element_text(size=12)) +
  theme(panel.grid.minor = element_line(linewidth = 0.25), panel.grid.major = element_line(linewidth = 0.5)) +
  theme(legend.position = "NA")

p

outf = file.path(outdir_fig_main, paste0("Fig5B_pearson_correlation_Basenji.pdf"))
ggsave(outf, p, width = 8, height = 6)

3 Figure 5C

twi_24h = read_input("/g/furlong/project/103_Basenji/Mattia/analysis/correlations/Basenji_scores_and_allelic_imbalance_twi.24_annot.txt")
twi_24h_best_basenji_in_peak = take_best_Basenji_in_peak(twi_24h)

bin_68h = read_input("/g/furlong/project/103_Basenji/Mattia/analysis/correlations/Basenji_scores_and_allelic_imbalance_bin.68_annot.txt")
bin_68h_best_basenji_in_peak = take_best_Basenji_in_peak(bin_68h)

ctcf_68h = read_input("/g/furlong/project/103_Basenji/Mattia/analysis/correlations/Basenji_scores_and_allelic_imbalance_ctcf.68_annot.txt")
ctcf_68h_best_basenji_in_peak = take_best_Basenji_in_peak(ctcf_68h)

mef2_68h = read_input("/g/furlong/project/103_Basenji/Mattia/analysis/correlations/Basenji_scores_and_allelic_imbalance_mef2.68_annot.txt")
mef2_68h_best_basenji_in_peak = take_best_Basenji_in_peak(mef2_68h)

bin_1012h = read_input("/g/furlong/project/103_Basenji/Mattia/analysis/correlations/Basenji_scores_and_allelic_imbalance_bin.1012_annot.txt")
bin_1012h_best_basenji_in_peak = take_best_Basenji_in_peak(bin_1012h)

mef2_1012h = read_input("/g/furlong/project/103_Basenji/Mattia/analysis/correlations/Basenji_scores_and_allelic_imbalance_mef2.1012_annot.txt")
mef2_1012h_best_basenji_in_peak = take_best_Basenji_in_peak(mef2_1012h)

all_samples = rbind(twi_24h, bin_68h, ctcf_68h, mef2_68h, bin_1012h, mef2_1012h)
all_samples$correct_predict = factor(ifelse(all_samples$AI>0.5 & all_samples$Basenji_AI>0.5, "correct", ifelse(all_samples$AI<0.5 & all_samples$Basenji_AI<0.5, "correct", "incorrect")), levels=c("incorrect", "correct"))

all_samples_best_basenji_in_peak = rbind(twi_24h_best_basenji_in_peak, bin_68h_best_basenji_in_peak, ctcf_68h_best_basenji_in_peak, mef2_68h_best_basenji_in_peak, bin_1012h_best_basenji_in_peak, mef2_1012h_best_basenji_in_peak)
all_samples_best_basenji_in_peak$correct_predict = factor(ifelse(all_samples_best_basenji_in_peak$AI>0.5 & all_samples_best_basenji_in_peak$Basenji_AI>0.5, "correct", ifelse(all_samples_best_basenji_in_peak$AI<0.5 & all_samples_best_basenji_in_peak$Basenji_AI<0.5, "correct", "incorrect")), levels=c("incorrect", "correct"))

all_samples_best_basenji_in_peak_sub = subset(all_samples_best_basenji_in_peak, significant==TRUE)


all_samples_best_basenji_in_peak$best_variant = 1
all_samples_2nd_best_basenji_in_peak = take_next_best_Basenji_in_peak(all_samples, all_samples_best_basenji_in_peak)
all_samples_2nd_best_basenji_in_peak$best_variant = 2
all_samples_3nd_best_basenji_in_peak = take_next_best_Basenji_in_peak(all_samples, rbind(all_samples_best_basenji_in_peak, all_samples_2nd_best_basenji_in_peak))
all_samples_3nd_best_basenji_in_peak$best_variant = 3
all_samples_4nd_best_basenji_in_peak = take_next_best_Basenji_in_peak(all_samples, rbind(all_samples_best_basenji_in_peak, all_samples_2nd_best_basenji_in_peak, all_samples_3nd_best_basenji_in_peak))
all_samples_4nd_best_basenji_in_peak$best_variant = 4
all_samples_5nd_best_basenji_in_peak = take_next_best_Basenji_in_peak(all_samples, rbind(all_samples_best_basenji_in_peak, all_samples_2nd_best_basenji_in_peak, all_samples_3nd_best_basenji_in_peak, all_samples_4nd_best_basenji_in_peak))
all_samples_5nd_best_basenji_in_peak$best_variant = 5

all_samples_sub = subset(all_samples, significant==TRUE)
all_samples_best_basenji_in_peak_sub = subset(all_samples_best_basenji_in_peak, significant==TRUE)
all_samples_2nd_best_basenji_in_peak_sub = subset(all_samples_2nd_best_basenji_in_peak, significant==TRUE)
all_samples_3nd_best_basenji_in_peak_sub = subset(all_samples_3nd_best_basenji_in_peak, significant==TRUE)
all_samples_4nd_best_basenji_in_peak_sub = subset(all_samples_4nd_best_basenji_in_peak, significant==TRUE)
all_samples_5nd_best_basenji_in_peak_sub = subset(all_samples_5nd_best_basenji_in_peak, significant==TRUE)


success_proportion = as.data.frame(t(matrix(c("1st", nrow(subset(all_samples_best_basenji_in_peak_sub, (Basenji_AI > 0.5 & AI > 0.5) | (Basenji_AI < 0.5 & AI < 0.5))) / nrow(all_samples_best_basenji_in_peak_sub), 
  "2nd", nrow(subset(all_samples_2nd_best_basenji_in_peak_sub, (Basenji_AI > 0.5 & AI > 0.5) | (Basenji_AI < 0.5 & AI < 0.5))) / nrow(all_samples_2nd_best_basenji_in_peak_sub), 
  "3rd", nrow(subset(all_samples_3nd_best_basenji_in_peak_sub, (Basenji_AI > 0.5 & AI > 0.5) | (Basenji_AI < 0.5 & AI < 0.5))) / nrow(all_samples_3nd_best_basenji_in_peak_sub), 
  "4th", nrow(subset(all_samples_4nd_best_basenji_in_peak_sub, (Basenji_AI > 0.5 & AI > 0.5) | (Basenji_AI < 0.5 & AI < 0.5))) / nrow(all_samples_4nd_best_basenji_in_peak_sub), 
  "5th", nrow(subset(all_samples_5nd_best_basenji_in_peak_sub, (Basenji_AI > 0.5 & AI > 0.5) | (Basenji_AI < 0.5 & AI < 0.5))) / nrow(all_samples_5nd_best_basenji_in_peak_sub), 
  "all", nrow(subset(all_samples_sub, (Basenji_AI > 0.5 & AI > 0.5) | (Basenji_AI < 0.5 & AI < 0.5))) / nrow(all_samples_sub)), nrow=2)))

colnames(success_proportion) = c("best_Basenji_AI", "proportion_correct")
success_proportion$proportion_correct = as.numeric(success_proportion$proportion_correct)





background_success_proportion_df = data.frame(row.names = c("1st", "2nd", "3rd", "4th", "5th", "all"))

for (i in seq(1, 1000)) {
  
  all_samples_variant = subset(all_samples)
  background_variant_shuff = all_samples_variant %>% 
    select(variant_ID, peak_ID, significant, correct_predict) %>%
    group_by(peak_ID) %>%
    mutate(rank=sample(row_number())) %>%
    ungroup()
  background_best_variant_shuff = subset(background_variant_shuff, rank==1 & significant==TRUE)
  background_2nd_best_variant_shuff = subset(background_variant_shuff, rank==2 & significant==TRUE)
  background_3nd_best_variant_shuff = subset(background_variant_shuff, rank==3 & significant==TRUE)
  background_4nd_best_variant_shuff = subset(background_variant_shuff, rank==4 & significant==TRUE)
  background_5nd_best_variant_shuff = subset(background_variant_shuff, rank==5 & significant==TRUE)

  background_success_proportion = as.data.frame(t(matrix(c("1st", nrow(subset(background_best_variant_shuff, (correct_predict=="correct"))) / nrow(background_best_variant_shuff), 
  "2nd", nrow(subset(background_2nd_best_variant_shuff, (correct_predict=="correct"))) / nrow(background_2nd_best_variant_shuff), 
  "3rd", nrow(subset(background_3nd_best_variant_shuff, (correct_predict=="correct"))) / nrow(background_3nd_best_variant_shuff), 
  "4th", nrow(subset(background_4nd_best_variant_shuff, (correct_predict=="correct"))) / nrow(background_4nd_best_variant_shuff), 
  "5th", nrow(subset(background_5nd_best_variant_shuff, (correct_predict=="correct"))) / nrow(background_5nd_best_variant_shuff), 
  "all", nrow(subset(background_variant_shuff, (correct_predict=="correct"))) / nrow(background_variant_shuff)), nrow=2)))

  colnames(background_success_proportion) = c("best_Basenji_AI", "proportion_correct")
  background_success_proportion$proportion_correct = as.numeric(background_success_proportion$proportion_correct)

  background_success_proportion_df = cbind(background_success_proportion_df, background_success_proportion$proportion_correct)
}


background_success_proportion_summary = data.frame(background_mean = rowMeans(background_success_proportion_df),
                                                   background_std = apply(background_success_proportion_df, 1, sd, na.rm = TRUE))

success_proportion = cbind(success_proportion, background_success_proportion_summary)
success_proportion = success_proportion[c(1,2,3,4,5), ]



p = ggplot(success_proportion, aes(x=best_Basenji_AI, y=proportion_correct, group=1)) +
    geom_point(colour="orange2", size=4) +
    geom_line(colour="orange2", linewidth=1) +
    geom_line(aes(y = background_mean), color = "grey40", linewidth = 1) + 
    geom_ribbon(aes(y = background_mean, ymin = background_mean - background_std * 2, ymax = background_mean + background_std * 2), fill = "grey30", alpha = .2) +
    ylim(0.47, 1) +
    geom_hline(yintercept = 0.5, colour = "#C92B27", linetype="dashed") +
    geom_text(aes(x = best_Basenji_AI, y = proportion_correct, label=round(proportion_correct, 3)),colour="grey20", fontface = 2, size = 4, vjust=-2) +
    xlab("Best variant order (Basenji AI)") +
    ylab("Proportion of correct predictions (AI direction)") +
    theme_bw() + 
    theme(panel.grid = element_line(colour = "grey80", linewidth = 1), axis.text = element_text(size = 12)) +
    theme(axis.title = element_text(size = 12), plot.title = element_text(size=12)) +
    theme(panel.grid.minor = element_line(linewidth = 0.25), panel.grid.major = element_line(linewidth = 0.5)) +
    theme(legend.position = "none")

p

outf = file.path(outdir_fig_main, paste0("Fig5C_Variants_priority_order_performance.pdf"))
ggsave(outf, p, width = 6, height = 3)

4 Figure 5D

5 Figure 5E

all_samples_best_basenji_in_peak_sub = subset(all_samples_best_basenji_in_peak, significant==TRUE & (Basenji_abs_AI > 0.1))
all_samples_best_basenji_in_peak_sub$condition = factor(all_samples_best_basenji_in_peak_sub$condition, levels=c("twi.24", "ctcf.68", "mef2.68", "mef2.1012", "bin.68", "bin.1012"))

p = plot_correlation(all_samples_best_basenji_in_peak_sub, "All experiments significant AI best Basenji score if > 0.1 AI") + 
  geom_point(aes(colour=condition), size=2) +
  scale_colour_manual(values = c("#E21F26", "#397FB9", "#4EAF49", "#68C3A6", "#984F9F", "#E38CBB")) +
  theme(legend.position = "right") 
p
   `geom_smooth()` using formula = 'y ~ x'

outf = file.path(outdir_fig_main, paste0("Fig5E_correlation_strong_guesses_significant_by_experiment.pdf"))
ggsave(outf, p, width = 7, height = 6)
   `geom_smooth()` using formula = 'y ~ x'

6 Figure 5F

p = plot_counts_barplot(all_samples_best_basenji_in_peak_sub, "condition", "correct_predict")
p

outf = file.path(outdir_fig_main, paste0("Fig5F_counts_guesses_significant_by_experiment.pdf"))
ggsave(outf, p, width = 7, height = 6)

7 Figure 5G

saturation_scores_predictions = read.table("/g/furlong/project/103_Basenji/Mattia/analysis/saturation_scores/Basenji_DataTable_predictions.txt", header=TRUE)

saturation_scores_predictions$motif_on_variant = ifelse(saturation_scores_predictions$variant_in_self_motif == 1, "self_motif", ifelse(saturation_scores_predictions$variant_in_other_motif == 1, "cofactor_motif", ifelse(saturation_scores_predictions$Basenji_predict == "no_prediction", "no_prediction", "no_motif")))
saturation_scores_predictions$motif_on_variant = factor(saturation_scores_predictions$motif_on_variant, levels=c("self_motif", "cofactor_motif","no_motif", "no_prediction"))
saturation_scores_predictions$condition = factor(saturation_scores_predictions$condition, levels=c("twi.24", "ctcf.68", "mef2.68", "mef2.1012", "bin.68", "bin.1012"))

p = plot_counts_barplot(saturation_scores_predictions, "condition", "motif_on_variant") +
  scale_fill_manual(values = c("#FF2341", "#FFA736", "grey70", "grey15")) +
  geom_text(aes(label=counts),  position = position_stack(vjust = 0.5), colour="white") +
  labs(fill="variant_on_motif")
   Scale for fill is already present.
   Adding another scale for fill, which will replace the existing scale.
outf = file.path(outdir_fig_main, paste0("Fig5G_counts_guesses_significant_by_motif.pdf"))
p

ggsave(outf, p, width = 7, height = 6)

8 Figure 5H

# counts per line
ll_ctcf = get_counts_per_line("ctcf/68") 
   [1] "399_399_1"
   [1] "399_399_2"
   [1] "vgn_28_1"
   [1] "vgn_28_2"
   [1] "vgn_307_1"
   [1] "vgn_307_2"
   [1] "vgn_399_1"
   [1] "vgn_399_2"
   [1] "vgn_57_1"
   [1] "vgn_57_2"
   [1] "vgn_639_1"
   [1] "vgn_639_2"
   [1] "vgn_712_1"
   [1] "vgn_712_2"
   [1] "vgn_714_1"
   [1] "vgn_714_2"
   [1] "vgn_852_1"
   [1] "vgn_852_2"
   [1] "vgn_vgn_1"
   [1] "vgn_vgn_2"
vid = "chr2R_15733144"

l = plot_ai_and_read_depth_for_variant(cht %>% filter(condition == "ctcf/68"), ll_ctcf, vid) 
   Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
   ℹ Please use `linewidth` instead.
   This warning is displayed once every 8 hours.
   Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
   generated.
outf = file.path(outdir_fig_main, paste0("Fig5H_example_ctcf_allele_ratios.pdf"))
print(l[[2]])

ggsave(outf, l[[2]], width = 3, height = 3)
LS0tCnRpdGxlOiAiRmlndXJlIDUiCm91dHB1dDoKICAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OgogICAgICB0b2M6IHRydWUKICAgICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICAgaGlnaGxpZ2h0OiB0YW5nbwojYmlibGlvZ3JhcGh5OiBrbm5fbWxfaW50cm8uYmliCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc3R5bGUsIGVjaG89RkFMU0UsIHJlc3VsdHM9ImFzaXMifQpsaWJyYXJ5KCJrbml0ciIpCm9wdGlvbnMoZGlnaXRzID0gMiwgd2lkdGggPSA4MCkKb3B0aW9ucyhiaXRtYXBUeXBlID0gJ2NhaXJvJykKZ29sZGVuX3JhdGlvIDwtICgxICsgc3FydCg1KSkgLyAyCm9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB0aWR5ID0gRkFMU0UsIGluY2x1ZGUgPSBUUlVFLCBjYWNoZSA9IEZBTFNFLAogICAgICAgICAgICAgICBkZXY9YygncG5nJywgJ3BkZicpLCBjb21tZW50ID0gJyAgJywgZHBpID0gMzAwKQoKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmtuaXRyOjpvcHRzX2NodW5rJHNldChjYWNoZT1GQUxTRSkKb3B0aW9ucyhkaWdpdHMgPSA1KSAgICAgICAgIApgYGAKCiMgU2V0dXAgYW5kIGRhdGEKCmBgYHtyfQpzb3VyY2UoIi4uL3V0aWxzL3V0aWxzLlIiKQpjb25maWcgPSBsb2FkX2NvbmZpZygpCgojIGxvYWQgQ0hUIHJlc3VsdHMKY2h0X2Z1bGwgPSBsYXBwbHkoYWJfdHBfbGlzdCwgZnVuY3Rpb24oYWJfdHApIGxvYWRfY2h0X3Jlc3VsdHMoYWJfdHAsIHJlbW92ZV9jaHIgPSBGKSkgJT4lIGJpbmRfcm93cygpCmNodCA9IGNodF9mdWxsICU+JSBmaWx0ZXIoIVRFU1QuU05QLkNIUk9NICVpbiUgYygiY2hyWCIsICJjaHJZIiwgImNock0iKSkKY2h0X3NpZ24gPSBjaHQgJT4lIGZpbHRlcihzaWduaWZfc3Ryb25nQUkpIAoKIyBnZW5lcyBhbmQgcHJvbW90ZXJzCmdlbmVzID0gbG9hZF9nZW5lcygpCnByb21vdGVycyA9IHJlc2l6ZShnZW5lcywgd2lkdGggPSAxMDAwLCBmaXggPSAic3RhcnQiKQoKIyBjb21iaW5lZCBtb3RpZiBzZXQgKGFsbCBURnMsIHBlYWtzICsgYWxsZWxlcykKZmltbyA9IGdldF9mdWxsX21vdGlmX3NldHMoY2h0LCBhYl90cF9saXN0KQojIG9ubHkgYWxsZWxlcwpmaW1vX2FsbGVsZXMgID0gbGFwcGx5KGFiX3RwX2xpc3QsIGZ1bmN0aW9uKGFiX3RwKSBwYXJzZV9tb3RpZnNfaW5fdHdvX2FsbGVsZXMoYWJfdHAsIGNodCkpICU+JSBiaW5kX3Jvd3MoKSAKCmBgYAoKCgoKIyBGaWd1cmUgNUIKCgpgYGB7ciB9CnNhbXBsZXNfY29ycmVsYXRpb25zID0gcmVhZC50YWJsZSgiL2cvZnVybG9uZy9wcm9qZWN0LzEwM19CYXNlbmppL21vZGVscy9kcm9zb3BoaWxhX2wxMzFrX2F1Z21lbnRlZC9hY2NfbG9nLnR4dCIsIGhlYWRlcj1UUlVFKQpzYW1wbGVzX2NvcnJlbGF0aW9ucyRkZXNjcmlwdGlvbiA9IGZhY3RvcihzYW1wbGVzX2NvcnJlbGF0aW9ucyRkZXNjcmlwdGlvbiwgbGV2ZWxzID0gYygiRjFfQ2hpcC1zZXEiLCAiUkVNQVBfQ2hJUC1zZXEiLCAiREhTIikpCnAgPSBnZ3Bsb3Qoc2FtcGxlc19jb3JyZWxhdGlvbnMsIGFlcyh4PWRlc2NyaXB0aW9uLCB5PXBlYXJzb25yLCBmaWxsPWRlc2NyaXB0aW9uKSkgKyAKICBnZW9tX2ppdHRlcihjb2xvdXI9ImdyZXk1MCIpICsKICBnZW9tX2JveHBsb3QoYWxwaGE9MC40LCB3aWR0aD0wLjQsIG91dGxpZXIuc2l6ZSA9IDAsIGNvbG91cj0iZ3JleTE1IikgKwogIHlsaW0oMCwxKSArCiAgeGxhYigiIikgKwogIHlsYWIoIlBlYXJzb24gciBjb3JyZWxhdGlvbiIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMzNCMjUwIiwgIiMwMDY2Q0MiLCAiI0NDNDc0MSIpKSArCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImdyZXk4MCIsIGxpbmV3aWR0aCA9IDEpLCBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUobGluZXdpZHRoID0gMC4yNSksIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUobGluZXdpZHRoID0gMC41KSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOQSIpCgpwCm91dGYgPSBmaWxlLnBhdGgob3V0ZGlyX2ZpZ19tYWluLCBwYXN0ZTAoIkZpZzVCX3BlYXJzb25fY29ycmVsYXRpb25fQmFzZW5qaS5wZGYiKSkKZ2dzYXZlKG91dGYsIHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKYGBgCgoKKiBtZWRpYW4gcGVhcnNvbiBSIGZvciBSZW1hcDogYHIgbWVkaWFuKHN1YnNldChzYW1wbGVzX2NvcnJlbGF0aW9ucywgZGVzY3JpcHRpb249PSJSRU1BUF9DaElQLXNlcSIpJHBlYXJzb25yKWAKKiBtZWRpYW4gcGVhcnNvbiBSIGZvciBESFM6IGByIG1lZGlhbihzdWJzZXQoc2FtcGxlc19jb3JyZWxhdGlvbnMsIGRlc2NyaXB0aW9uPT0iREhTIikkcGVhcnNvbnIpYAoqIG1lZGlhbiBwZWFyc29uIFIgZm9yIEYxX0NoaXAtc2VxOiBgciBtZWRpYW4oc3Vic2V0KHNhbXBsZXNfY29ycmVsYXRpb25zLCBkZXNjcmlwdGlvbj09IkYxX0NoaXAtc2VxIikkcGVhcnNvbnIpYAoKCgoKIyBGaWd1cmUgNUMKCmBgYHtyIH0KdHdpXzI0aCA9IHJlYWRfaW5wdXQoIi9nL2Z1cmxvbmcvcHJvamVjdC8xMDNfQmFzZW5qaS9NYXR0aWEvYW5hbHlzaXMvY29ycmVsYXRpb25zL0Jhc2Vuamlfc2NvcmVzX2FuZF9hbGxlbGljX2ltYmFsYW5jZV90d2kuMjRfYW5ub3QudHh0IikKdHdpXzI0aF9iZXN0X2Jhc2VuamlfaW5fcGVhayA9IHRha2VfYmVzdF9CYXNlbmppX2luX3BlYWsodHdpXzI0aCkKCmJpbl82OGggPSByZWFkX2lucHV0KCIvZy9mdXJsb25nL3Byb2plY3QvMTAzX0Jhc2VuamkvTWF0dGlhL2FuYWx5c2lzL2NvcnJlbGF0aW9ucy9CYXNlbmppX3Njb3Jlc19hbmRfYWxsZWxpY19pbWJhbGFuY2VfYmluLjY4X2Fubm90LnR4dCIpCmJpbl82OGhfYmVzdF9iYXNlbmppX2luX3BlYWsgPSB0YWtlX2Jlc3RfQmFzZW5qaV9pbl9wZWFrKGJpbl82OGgpCgpjdGNmXzY4aCA9IHJlYWRfaW5wdXQoIi9nL2Z1cmxvbmcvcHJvamVjdC8xMDNfQmFzZW5qaS9NYXR0aWEvYW5hbHlzaXMvY29ycmVsYXRpb25zL0Jhc2Vuamlfc2NvcmVzX2FuZF9hbGxlbGljX2ltYmFsYW5jZV9jdGNmLjY4X2Fubm90LnR4dCIpCmN0Y2ZfNjhoX2Jlc3RfYmFzZW5qaV9pbl9wZWFrID0gdGFrZV9iZXN0X0Jhc2VuamlfaW5fcGVhayhjdGNmXzY4aCkKCm1lZjJfNjhoID0gcmVhZF9pbnB1dCgiL2cvZnVybG9uZy9wcm9qZWN0LzEwM19CYXNlbmppL01hdHRpYS9hbmFseXNpcy9jb3JyZWxhdGlvbnMvQmFzZW5qaV9zY29yZXNfYW5kX2FsbGVsaWNfaW1iYWxhbmNlX21lZjIuNjhfYW5ub3QudHh0IikKbWVmMl82OGhfYmVzdF9iYXNlbmppX2luX3BlYWsgPSB0YWtlX2Jlc3RfQmFzZW5qaV9pbl9wZWFrKG1lZjJfNjhoKQoKYmluXzEwMTJoID0gcmVhZF9pbnB1dCgiL2cvZnVybG9uZy9wcm9qZWN0LzEwM19CYXNlbmppL01hdHRpYS9hbmFseXNpcy9jb3JyZWxhdGlvbnMvQmFzZW5qaV9zY29yZXNfYW5kX2FsbGVsaWNfaW1iYWxhbmNlX2Jpbi4xMDEyX2Fubm90LnR4dCIpCmJpbl8xMDEyaF9iZXN0X2Jhc2VuamlfaW5fcGVhayA9IHRha2VfYmVzdF9CYXNlbmppX2luX3BlYWsoYmluXzEwMTJoKQoKbWVmMl8xMDEyaCA9IHJlYWRfaW5wdXQoIi9nL2Z1cmxvbmcvcHJvamVjdC8xMDNfQmFzZW5qaS9NYXR0aWEvYW5hbHlzaXMvY29ycmVsYXRpb25zL0Jhc2Vuamlfc2NvcmVzX2FuZF9hbGxlbGljX2ltYmFsYW5jZV9tZWYyLjEwMTJfYW5ub3QudHh0IikKbWVmMl8xMDEyaF9iZXN0X2Jhc2VuamlfaW5fcGVhayA9IHRha2VfYmVzdF9CYXNlbmppX2luX3BlYWsobWVmMl8xMDEyaCkKCmFsbF9zYW1wbGVzID0gcmJpbmQodHdpXzI0aCwgYmluXzY4aCwgY3RjZl82OGgsIG1lZjJfNjhoLCBiaW5fMTAxMmgsIG1lZjJfMTAxMmgpCmFsbF9zYW1wbGVzJGNvcnJlY3RfcHJlZGljdCA9IGZhY3RvcihpZmVsc2UoYWxsX3NhbXBsZXMkQUk+MC41ICYgYWxsX3NhbXBsZXMkQmFzZW5qaV9BST4wLjUsICJjb3JyZWN0IiwgaWZlbHNlKGFsbF9zYW1wbGVzJEFJPDAuNSAmIGFsbF9zYW1wbGVzJEJhc2VuamlfQUk8MC41LCAiY29ycmVjdCIsICJpbmNvcnJlY3QiKSksIGxldmVscz1jKCJpbmNvcnJlY3QiLCAiY29ycmVjdCIpKQoKYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWsgPSByYmluZCh0d2lfMjRoX2Jlc3RfYmFzZW5qaV9pbl9wZWFrLCBiaW5fNjhoX2Jlc3RfYmFzZW5qaV9pbl9wZWFrLCBjdGNmXzY4aF9iZXN0X2Jhc2VuamlfaW5fcGVhaywgbWVmMl82OGhfYmVzdF9iYXNlbmppX2luX3BlYWssIGJpbl8xMDEyaF9iZXN0X2Jhc2VuamlfaW5fcGVhaywgbWVmMl8xMDEyaF9iZXN0X2Jhc2VuamlfaW5fcGVhaykKYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWskY29ycmVjdF9wcmVkaWN0ID0gZmFjdG9yKGlmZWxzZShhbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVhayRBST4wLjUgJiBhbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVhayRCYXNlbmppX0FJPjAuNSwgImNvcnJlY3QiLCBpZmVsc2UoYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWskQUk8MC41ICYgYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWskQmFzZW5qaV9BSTwwLjUsICJjb3JyZWN0IiwgImluY29ycmVjdCIpKSwgbGV2ZWxzPWMoImluY29ycmVjdCIsICJjb3JyZWN0IikpCgphbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVha19zdWIgPSBzdWJzZXQoYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWssIHNpZ25pZmljYW50PT1UUlVFKQoKCmFsbF9zYW1wbGVzX2Jlc3RfYmFzZW5qaV9pbl9wZWFrJGJlc3RfdmFyaWFudCA9IDEKYWxsX3NhbXBsZXNfMm5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrID0gdGFrZV9uZXh0X2Jlc3RfQmFzZW5qaV9pbl9wZWFrKGFsbF9zYW1wbGVzLCBhbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVhaykKYWxsX3NhbXBsZXNfMm5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrJGJlc3RfdmFyaWFudCA9IDIKYWxsX3NhbXBsZXNfM25kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrID0gdGFrZV9uZXh0X2Jlc3RfQmFzZW5qaV9pbl9wZWFrKGFsbF9zYW1wbGVzLCByYmluZChhbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVhaywgYWxsX3NhbXBsZXNfMm5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrKSkKYWxsX3NhbXBsZXNfM25kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrJGJlc3RfdmFyaWFudCA9IDMKYWxsX3NhbXBsZXNfNG5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrID0gdGFrZV9uZXh0X2Jlc3RfQmFzZW5qaV9pbl9wZWFrKGFsbF9zYW1wbGVzLCByYmluZChhbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVhaywgYWxsX3NhbXBsZXNfMm5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrLCBhbGxfc2FtcGxlc18zbmRfYmVzdF9iYXNlbmppX2luX3BlYWspKQphbGxfc2FtcGxlc180bmRfYmVzdF9iYXNlbmppX2luX3BlYWskYmVzdF92YXJpYW50ID0gNAphbGxfc2FtcGxlc181bmRfYmVzdF9iYXNlbmppX2luX3BlYWsgPSB0YWtlX25leHRfYmVzdF9CYXNlbmppX2luX3BlYWsoYWxsX3NhbXBsZXMsIHJiaW5kKGFsbF9zYW1wbGVzX2Jlc3RfYmFzZW5qaV9pbl9wZWFrLCBhbGxfc2FtcGxlc18ybmRfYmVzdF9iYXNlbmppX2luX3BlYWssIGFsbF9zYW1wbGVzXzNuZF9iZXN0X2Jhc2VuamlfaW5fcGVhaywgYWxsX3NhbXBsZXNfNG5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrKSkKYWxsX3NhbXBsZXNfNW5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrJGJlc3RfdmFyaWFudCA9IDUKCmFsbF9zYW1wbGVzX3N1YiA9IHN1YnNldChhbGxfc2FtcGxlcywgc2lnbmlmaWNhbnQ9PVRSVUUpCmFsbF9zYW1wbGVzX2Jlc3RfYmFzZW5qaV9pbl9wZWFrX3N1YiA9IHN1YnNldChhbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVhaywgc2lnbmlmaWNhbnQ9PVRSVUUpCmFsbF9zYW1wbGVzXzJuZF9iZXN0X2Jhc2VuamlfaW5fcGVha19zdWIgPSBzdWJzZXQoYWxsX3NhbXBsZXNfMm5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrLCBzaWduaWZpY2FudD09VFJVRSkKYWxsX3NhbXBsZXNfM25kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrX3N1YiA9IHN1YnNldChhbGxfc2FtcGxlc18zbmRfYmVzdF9iYXNlbmppX2luX3BlYWssIHNpZ25pZmljYW50PT1UUlVFKQphbGxfc2FtcGxlc180bmRfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViID0gc3Vic2V0KGFsbF9zYW1wbGVzXzRuZF9iZXN0X2Jhc2VuamlfaW5fcGVhaywgc2lnbmlmaWNhbnQ9PVRSVUUpCmFsbF9zYW1wbGVzXzVuZF9iZXN0X2Jhc2VuamlfaW5fcGVha19zdWIgPSBzdWJzZXQoYWxsX3NhbXBsZXNfNW5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrLCBzaWduaWZpY2FudD09VFJVRSkKCgpzdWNjZXNzX3Byb3BvcnRpb24gPSBhcy5kYXRhLmZyYW1lKHQobWF0cml4KGMoIjFzdCIsIG5yb3coc3Vic2V0KGFsbF9zYW1wbGVzX2Jlc3RfYmFzZW5qaV9pbl9wZWFrX3N1YiwgKEJhc2VuamlfQUkgPiAwLjUgJiBBSSA+IDAuNSkgfCAoQmFzZW5qaV9BSSA8IDAuNSAmIEFJIDwgMC41KSkpIC8gbnJvdyhhbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVha19zdWIpLCAKICAiMm5kIiwgbnJvdyhzdWJzZXQoYWxsX3NhbXBsZXNfMm5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrX3N1YiwgKEJhc2VuamlfQUkgPiAwLjUgJiBBSSA+IDAuNSkgfCAoQmFzZW5qaV9BSSA8IDAuNSAmIEFJIDwgMC41KSkpIC8gbnJvdyhhbGxfc2FtcGxlc18ybmRfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViKSwgCiAgIjNyZCIsIG5yb3coc3Vic2V0KGFsbF9zYW1wbGVzXzNuZF9iZXN0X2Jhc2VuamlfaW5fcGVha19zdWIsIChCYXNlbmppX0FJID4gMC41ICYgQUkgPiAwLjUpIHwgKEJhc2VuamlfQUkgPCAwLjUgJiBBSSA8IDAuNSkpKSAvIG5yb3coYWxsX3NhbXBsZXNfM25kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrX3N1YiksIAogICI0dGgiLCBucm93KHN1YnNldChhbGxfc2FtcGxlc180bmRfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViLCAoQmFzZW5qaV9BSSA+IDAuNSAmIEFJID4gMC41KSB8IChCYXNlbmppX0FJIDwgMC41ICYgQUkgPCAwLjUpKSkgLyBucm93KGFsbF9zYW1wbGVzXzRuZF9iZXN0X2Jhc2VuamlfaW5fcGVha19zdWIpLCAKICAiNXRoIiwgbnJvdyhzdWJzZXQoYWxsX3NhbXBsZXNfNW5kX2Jlc3RfYmFzZW5qaV9pbl9wZWFrX3N1YiwgKEJhc2VuamlfQUkgPiAwLjUgJiBBSSA+IDAuNSkgfCAoQmFzZW5qaV9BSSA8IDAuNSAmIEFJIDwgMC41KSkpIC8gbnJvdyhhbGxfc2FtcGxlc181bmRfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViKSwgCiAgImFsbCIsIG5yb3coc3Vic2V0KGFsbF9zYW1wbGVzX3N1YiwgKEJhc2VuamlfQUkgPiAwLjUgJiBBSSA+IDAuNSkgfCAoQmFzZW5qaV9BSSA8IDAuNSAmIEFJIDwgMC41KSkpIC8gbnJvdyhhbGxfc2FtcGxlc19zdWIpKSwgbnJvdz0yKSkpCgpjb2xuYW1lcyhzdWNjZXNzX3Byb3BvcnRpb24pID0gYygiYmVzdF9CYXNlbmppX0FJIiwgInByb3BvcnRpb25fY29ycmVjdCIpCnN1Y2Nlc3NfcHJvcG9ydGlvbiRwcm9wb3J0aW9uX2NvcnJlY3QgPSBhcy5udW1lcmljKHN1Y2Nlc3NfcHJvcG9ydGlvbiRwcm9wb3J0aW9uX2NvcnJlY3QpCgoKCgoKYmFja2dyb3VuZF9zdWNjZXNzX3Byb3BvcnRpb25fZGYgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGMoIjFzdCIsICIybmQiLCAiM3JkIiwgIjR0aCIsICI1dGgiLCAiYWxsIikpCgpmb3IgKGkgaW4gc2VxKDEsIDEwMDApKSB7CiAgCiAgYWxsX3NhbXBsZXNfdmFyaWFudCA9IHN1YnNldChhbGxfc2FtcGxlcykKICBiYWNrZ3JvdW5kX3ZhcmlhbnRfc2h1ZmYgPSBhbGxfc2FtcGxlc192YXJpYW50ICU+JSAKICAgIHNlbGVjdCh2YXJpYW50X0lELCBwZWFrX0lELCBzaWduaWZpY2FudCwgY29ycmVjdF9wcmVkaWN0KSAlPiUKICAgIGdyb3VwX2J5KHBlYWtfSUQpICU+JQogICAgbXV0YXRlKHJhbms9c2FtcGxlKHJvd19udW1iZXIoKSkpICU+JQogICAgdW5ncm91cCgpCiAgYmFja2dyb3VuZF9iZXN0X3ZhcmlhbnRfc2h1ZmYgPSBzdWJzZXQoYmFja2dyb3VuZF92YXJpYW50X3NodWZmLCByYW5rPT0xICYgc2lnbmlmaWNhbnQ9PVRSVUUpCiAgYmFja2dyb3VuZF8ybmRfYmVzdF92YXJpYW50X3NodWZmID0gc3Vic2V0KGJhY2tncm91bmRfdmFyaWFudF9zaHVmZiwgcmFuaz09MiAmIHNpZ25pZmljYW50PT1UUlVFKQogIGJhY2tncm91bmRfM25kX2Jlc3RfdmFyaWFudF9zaHVmZiA9IHN1YnNldChiYWNrZ3JvdW5kX3ZhcmlhbnRfc2h1ZmYsIHJhbms9PTMgJiBzaWduaWZpY2FudD09VFJVRSkKICBiYWNrZ3JvdW5kXzRuZF9iZXN0X3ZhcmlhbnRfc2h1ZmYgPSBzdWJzZXQoYmFja2dyb3VuZF92YXJpYW50X3NodWZmLCByYW5rPT00ICYgc2lnbmlmaWNhbnQ9PVRSVUUpCiAgYmFja2dyb3VuZF81bmRfYmVzdF92YXJpYW50X3NodWZmID0gc3Vic2V0KGJhY2tncm91bmRfdmFyaWFudF9zaHVmZiwgcmFuaz09NSAmIHNpZ25pZmljYW50PT1UUlVFKQoKICBiYWNrZ3JvdW5kX3N1Y2Nlc3NfcHJvcG9ydGlvbiA9IGFzLmRhdGEuZnJhbWUodChtYXRyaXgoYygiMXN0IiwgbnJvdyhzdWJzZXQoYmFja2dyb3VuZF9iZXN0X3ZhcmlhbnRfc2h1ZmYsIChjb3JyZWN0X3ByZWRpY3Q9PSJjb3JyZWN0IikpKSAvIG5yb3coYmFja2dyb3VuZF9iZXN0X3ZhcmlhbnRfc2h1ZmYpLCAKICAiMm5kIiwgbnJvdyhzdWJzZXQoYmFja2dyb3VuZF8ybmRfYmVzdF92YXJpYW50X3NodWZmLCAoY29ycmVjdF9wcmVkaWN0PT0iY29ycmVjdCIpKSkgLyBucm93KGJhY2tncm91bmRfMm5kX2Jlc3RfdmFyaWFudF9zaHVmZiksIAogICIzcmQiLCBucm93KHN1YnNldChiYWNrZ3JvdW5kXzNuZF9iZXN0X3ZhcmlhbnRfc2h1ZmYsIChjb3JyZWN0X3ByZWRpY3Q9PSJjb3JyZWN0IikpKSAvIG5yb3coYmFja2dyb3VuZF8zbmRfYmVzdF92YXJpYW50X3NodWZmKSwgCiAgIjR0aCIsIG5yb3coc3Vic2V0KGJhY2tncm91bmRfNG5kX2Jlc3RfdmFyaWFudF9zaHVmZiwgKGNvcnJlY3RfcHJlZGljdD09ImNvcnJlY3QiKSkpIC8gbnJvdyhiYWNrZ3JvdW5kXzRuZF9iZXN0X3ZhcmlhbnRfc2h1ZmYpLCAKICAiNXRoIiwgbnJvdyhzdWJzZXQoYmFja2dyb3VuZF81bmRfYmVzdF92YXJpYW50X3NodWZmLCAoY29ycmVjdF9wcmVkaWN0PT0iY29ycmVjdCIpKSkgLyBucm93KGJhY2tncm91bmRfNW5kX2Jlc3RfdmFyaWFudF9zaHVmZiksIAogICJhbGwiLCBucm93KHN1YnNldChiYWNrZ3JvdW5kX3ZhcmlhbnRfc2h1ZmYsIChjb3JyZWN0X3ByZWRpY3Q9PSJjb3JyZWN0IikpKSAvIG5yb3coYmFja2dyb3VuZF92YXJpYW50X3NodWZmKSksIG5yb3c9MikpKQoKICBjb2xuYW1lcyhiYWNrZ3JvdW5kX3N1Y2Nlc3NfcHJvcG9ydGlvbikgPSBjKCJiZXN0X0Jhc2VuamlfQUkiLCAicHJvcG9ydGlvbl9jb3JyZWN0IikKICBiYWNrZ3JvdW5kX3N1Y2Nlc3NfcHJvcG9ydGlvbiRwcm9wb3J0aW9uX2NvcnJlY3QgPSBhcy5udW1lcmljKGJhY2tncm91bmRfc3VjY2Vzc19wcm9wb3J0aW9uJHByb3BvcnRpb25fY29ycmVjdCkKCiAgYmFja2dyb3VuZF9zdWNjZXNzX3Byb3BvcnRpb25fZGYgPSBjYmluZChiYWNrZ3JvdW5kX3N1Y2Nlc3NfcHJvcG9ydGlvbl9kZiwgYmFja2dyb3VuZF9zdWNjZXNzX3Byb3BvcnRpb24kcHJvcG9ydGlvbl9jb3JyZWN0KQp9CgoKYmFja2dyb3VuZF9zdWNjZXNzX3Byb3BvcnRpb25fc3VtbWFyeSA9IGRhdGEuZnJhbWUoYmFja2dyb3VuZF9tZWFuID0gcm93TWVhbnMoYmFja2dyb3VuZF9zdWNjZXNzX3Byb3BvcnRpb25fZGYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kX3N0ZCA9IGFwcGx5KGJhY2tncm91bmRfc3VjY2Vzc19wcm9wb3J0aW9uX2RmLCAxLCBzZCwgbmEucm0gPSBUUlVFKSkKCnN1Y2Nlc3NfcHJvcG9ydGlvbiA9IGNiaW5kKHN1Y2Nlc3NfcHJvcG9ydGlvbiwgYmFja2dyb3VuZF9zdWNjZXNzX3Byb3BvcnRpb25fc3VtbWFyeSkKc3VjY2Vzc19wcm9wb3J0aW9uID0gc3VjY2Vzc19wcm9wb3J0aW9uW2MoMSwyLDMsNCw1KSwgXQoKCgpwID0gZ2dwbG90KHN1Y2Nlc3NfcHJvcG9ydGlvbiwgYWVzKHg9YmVzdF9CYXNlbmppX0FJLCB5PXByb3BvcnRpb25fY29ycmVjdCwgZ3JvdXA9MSkpICsKICAgIGdlb21fcG9pbnQoY29sb3VyPSJvcmFuZ2UyIiwgc2l6ZT00KSArCiAgICBnZW9tX2xpbmUoY29sb3VyPSJvcmFuZ2UyIiwgbGluZXdpZHRoPTEpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGJhY2tncm91bmRfbWVhbiksIGNvbG9yID0gImdyZXk0MCIsIGxpbmV3aWR0aCA9IDEpICsgCiAgICBnZW9tX3JpYmJvbihhZXMoeSA9IGJhY2tncm91bmRfbWVhbiwgeW1pbiA9IGJhY2tncm91bmRfbWVhbiAtIGJhY2tncm91bmRfc3RkICogMiwgeW1heCA9IGJhY2tncm91bmRfbWVhbiArIGJhY2tncm91bmRfc3RkICogMiksIGZpbGwgPSAiZ3JleTMwIiwgYWxwaGEgPSAuMikgKwogICAgeWxpbSgwLjQ3LCAxKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIGNvbG91ciA9ICIjQzkyQjI3IiwgbGluZXR5cGU9ImRhc2hlZCIpICsKICAgIGdlb21fdGV4dChhZXMoeCA9IGJlc3RfQmFzZW5qaV9BSSwgeSA9IHByb3BvcnRpb25fY29ycmVjdCwgbGFiZWw9cm91bmQocHJvcG9ydGlvbl9jb3JyZWN0LCAzKSksY29sb3VyPSJncmV5MjAiLCBmb250ZmFjZSA9IDIsIHNpemUgPSA0LCB2anVzdD0tMikgKwogICAgeGxhYigiQmVzdCB2YXJpYW50IG9yZGVyIChCYXNlbmppIEFJKSIpICsKICAgIHlsYWIoIlByb3BvcnRpb24gb2YgY29ycmVjdCBwcmVkaWN0aW9ucyAoQUkgZGlyZWN0aW9uKSIpICsKICAgIHRoZW1lX2J3KCkgKyAKICAgIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImdyZXk4MCIsIGxpbmV3aWR0aCA9IDEpLCBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkgKwogICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIpKSArCiAgICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGxpbmV3aWR0aCA9IDAuMjUpLCBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGxpbmV3aWR0aCA9IDAuNSkpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnAKb3V0ZiA9IGZpbGUucGF0aChvdXRkaXJfZmlnX21haW4sIHBhc3RlMCgiRmlnNUNfVmFyaWFudHNfcHJpb3JpdHlfb3JkZXJfcGVyZm9ybWFuY2UucGRmIikpCmdnc2F2ZShvdXRmLCBwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDMpCmBgYAoKCiMgRmlndXJlIDVECgpgYGB7ciBwbG90X3N0cm9uZ19wcmVkaWN0ZWRfQUksIGNvbW1lbnQ9TkEsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQgPSA3LCBmaWcud2lkdGggPSA4fQphbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVha19zdWIgPSBzdWJzZXQoYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWssICBCYXNlbmppX2Fic19BSSA+IDAuMSkKYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViJHB2YWx1ZV9zaWduaWZpY2F0ID0gaWZlbHNlKChhbGxfc2FtcGxlc19iZXN0X2Jhc2VuamlfaW5fcGVha19zdWIkcGFkanVzdCA8IDAuMDEpICYgKGFsbF9zYW1wbGVzX2Jlc3RfYmFzZW5qaV9pbl9wZWFrX3N1YiRBSV9hYnMgPiAwLjEpLCBUUlVFLCBGQUxTRSkKcCA9IHBsb3RfY29ycmVsYXRpb24oYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViLCAiQWxsIGV4cGVyaW1lbnRzIGJlc3QgQmFzZW5qaSBzY29yZSBpZiA+IDAuMSBBSSIpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPXB2YWx1ZV9zaWduaWZpY2F0KSwgc2l6ZT0yKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJzaWduaWZpY2FudCBBSSIpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjNjg2ODY4IiwgIiNERjc4NzgiKSkKcAoKb3V0ZiA9IGZpbGUucGF0aChvdXRkaXJfZmlnX21haW4sIHBhc3RlMCgiRmlnNURfY29ycmVsYXRpb25fc3Ryb25nX2d1ZXNzZXNfYWxsLnBkZiIpKQpnZ3NhdmUob3V0ZiwgcCwgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQpgYGAKCiMgRmlndXJlIDVFCgpgYGB7cn0KYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViID0gc3Vic2V0KGFsbF9zYW1wbGVzX2Jlc3RfYmFzZW5qaV9pbl9wZWFrLCBzaWduaWZpY2FudD09VFJVRSAmIChCYXNlbmppX2Fic19BSSA+IDAuMSkpCmFsbF9zYW1wbGVzX2Jlc3RfYmFzZW5qaV9pbl9wZWFrX3N1YiRjb25kaXRpb24gPSBmYWN0b3IoYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViJGNvbmRpdGlvbiwgbGV2ZWxzPWMoInR3aS4yNCIsICJjdGNmLjY4IiwgIm1lZjIuNjgiLCAibWVmMi4xMDEyIiwgImJpbi42OCIsICJiaW4uMTAxMiIpKQoKcCA9IHBsb3RfY29ycmVsYXRpb24oYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViLCAiQWxsIGV4cGVyaW1lbnRzIHNpZ25pZmljYW50IEFJIGJlc3QgQmFzZW5qaSBzY29yZSBpZiA+IDAuMSBBSSIpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPWNvbmRpdGlvbiksIHNpemU9MikgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI0UyMUYyNiIsICIjMzk3RkI5IiwgIiM0RUFGNDkiLCAiIzY4QzNBNiIsICIjOTg0RjlGIiwgIiNFMzhDQkIiKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpIApwCgpvdXRmID0gZmlsZS5wYXRoKG91dGRpcl9maWdfbWFpbiwgcGFzdGUwKCJGaWc1RV9jb3JyZWxhdGlvbl9zdHJvbmdfZ3Vlc3Nlc19zaWduaWZpY2FudF9ieV9leHBlcmltZW50LnBkZiIpKQpnZ3NhdmUob3V0ZiwgcCwgd2lkdGggPSA3LCBoZWlnaHQgPSA2KQpgYGAKCiMgRmlndXJlIDVGCgpgYGB7cn0KcCA9IHBsb3RfY291bnRzX2JhcnBsb3QoYWxsX3NhbXBsZXNfYmVzdF9iYXNlbmppX2luX3BlYWtfc3ViLCAiY29uZGl0aW9uIiwgImNvcnJlY3RfcHJlZGljdCIpCnAKCm91dGYgPSBmaWxlLnBhdGgob3V0ZGlyX2ZpZ19tYWluLCBwYXN0ZTAoIkZpZzVGX2NvdW50c19ndWVzc2VzX3NpZ25pZmljYW50X2J5X2V4cGVyaW1lbnQucGRmIikpCmdnc2F2ZShvdXRmLCBwLCB3aWR0aCA9IDcsIGhlaWdodCA9IDYpCmBgYAoKCiMgRmlndXJlIDVHCgoKYGBge3J9CnNhdHVyYXRpb25fc2NvcmVzX3ByZWRpY3Rpb25zID0gcmVhZC50YWJsZSgiL2cvZnVybG9uZy9wcm9qZWN0LzEwM19CYXNlbmppL01hdHRpYS9hbmFseXNpcy9zYXR1cmF0aW9uX3Njb3Jlcy9CYXNlbmppX0RhdGFUYWJsZV9wcmVkaWN0aW9ucy50eHQiLCBoZWFkZXI9VFJVRSkKCnNhdHVyYXRpb25fc2NvcmVzX3ByZWRpY3Rpb25zJG1vdGlmX29uX3ZhcmlhbnQgPSBpZmVsc2Uoc2F0dXJhdGlvbl9zY29yZXNfcHJlZGljdGlvbnMkdmFyaWFudF9pbl9zZWxmX21vdGlmID09IDEsICJzZWxmX21vdGlmIiwgaWZlbHNlKHNhdHVyYXRpb25fc2NvcmVzX3ByZWRpY3Rpb25zJHZhcmlhbnRfaW5fb3RoZXJfbW90aWYgPT0gMSwgImNvZmFjdG9yX21vdGlmIiwgaWZlbHNlKHNhdHVyYXRpb25fc2NvcmVzX3ByZWRpY3Rpb25zJEJhc2VuamlfcHJlZGljdCA9PSAibm9fcHJlZGljdGlvbiIsICJub19wcmVkaWN0aW9uIiwgIm5vX21vdGlmIikpKQpzYXR1cmF0aW9uX3Njb3Jlc19wcmVkaWN0aW9ucyRtb3RpZl9vbl92YXJpYW50ID0gZmFjdG9yKHNhdHVyYXRpb25fc2NvcmVzX3ByZWRpY3Rpb25zJG1vdGlmX29uX3ZhcmlhbnQsIGxldmVscz1jKCJzZWxmX21vdGlmIiwgImNvZmFjdG9yX21vdGlmIiwibm9fbW90aWYiLCAibm9fcHJlZGljdGlvbiIpKQpzYXR1cmF0aW9uX3Njb3Jlc19wcmVkaWN0aW9ucyRjb25kaXRpb24gPSBmYWN0b3Ioc2F0dXJhdGlvbl9zY29yZXNfcHJlZGljdGlvbnMkY29uZGl0aW9uLCBsZXZlbHM9YygidHdpLjI0IiwgImN0Y2YuNjgiLCAibWVmMi42OCIsICJtZWYyLjEwMTIiLCAiYmluLjY4IiwgImJpbi4xMDEyIikpCgpwID0gcGxvdF9jb3VudHNfYmFycGxvdChzYXR1cmF0aW9uX3Njb3Jlc19wcmVkaWN0aW9ucywgImNvbmRpdGlvbiIsICJtb3RpZl9vbl92YXJpYW50IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNGRjIzNDEiLCAiI0ZGQTczNiIsICJncmV5NzAiLCAiZ3JleTE1IikpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50cyksICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgY29sb3VyPSJ3aGl0ZSIpICsKICBsYWJzKGZpbGw9InZhcmlhbnRfb25fbW90aWYiKQoKb3V0ZiA9IGZpbGUucGF0aChvdXRkaXJfZmlnX21haW4sIHBhc3RlMCgiRmlnNUdfY291bnRzX2d1ZXNzZXNfc2lnbmlmaWNhbnRfYnlfbW90aWYucGRmIikpCnAKZ2dzYXZlKG91dGYsIHAsIHdpZHRoID0gNywgaGVpZ2h0ID0gNikKYGBgCgoKIyBGaWd1cmUgNUgKCmBgYHtyfQojIGNvdW50cyBwZXIgbGluZQpsbF9jdGNmID0gZ2V0X2NvdW50c19wZXJfbGluZSgiY3RjZi82OCIpIAoKdmlkID0gImNocjJSXzE1NzMzMTQ0IgoKbCA9IHBsb3RfYWlfYW5kX3JlYWRfZGVwdGhfZm9yX3ZhcmlhbnQoY2h0ICU+JSBmaWx0ZXIoY29uZGl0aW9uID09ICJjdGNmLzY4IiksIGxsX2N0Y2YsIHZpZCkgCgpvdXRmID0gZmlsZS5wYXRoKG91dGRpcl9maWdfbWFpbiwgcGFzdGUwKCJGaWc1SF9leGFtcGxlX2N0Y2ZfYWxsZWxlX3JhdGlvcy5wZGYiKSkKcHJpbnQobFtbMl1dKQpnZ3NhdmUob3V0ZiwgbFtbMl1dLCB3aWR0aCA9IDMsIGhlaWdodCA9IDMpCmBgYAoKCg==