kmer based
comparison
The following block was generated via the following methods:
The hisat2-based alignments were passed to freebayes2[ref], which
generated a set of high-confidence transcriptome-based vcf files for
each sample of sufficient coverage. These were sorted, compressed, and
indexed via bcftools[ref]; and high-confidence variants (more than 80%
with coverage higher than 5 reads per position) were extracted into a
table variants as well as used to modify the reference genome to
represent these filtered variants. These modified genomes were passed to
ape[ref], indexed, and used to create a kmer index and distance matrix;
these distances were used by ape to create a neighbor joining tree.
Simplified version of
the above
I wrote a little function which in theory should make the above a bit
simpler and more robust for future analyses. Lets see if it works. It
currently takes a directory containing the fasta files of the sequences
to compare and an optional root.
The following block requires copying a group of fasta files into the
‘compare_strains/’ directory, which in our container image is not
available, so turning this off.
Ok, so I added the various fasta files used to create these trees to
the container in compressed form. I probably should have just made a
tarball, but I was concerned that I would need to change which files are
included, so did not. Thus, if one wishes to play with the following
blocks, one will first need to run:
Note that I included two cd commands because one may go there from
outside or inside the copied container output directory.
## cd ${OUTPUT_DIR}/compare_strains
## cd compare_strains
xz -d *.xz
strain_tree <- genomic_kmer_dist("compare_strains")
plot(strain_tree$phylo)
Phylogenetic analysis
of genes of interest
In order to perform this, I will use the same fasta files, but
extract the zymodeme genes from them and write out a set of fasta files
containing their sequences. I therefore wrote a function which takes in
the annotation data and fasta files in order to extract the data of
interest.
Sadly, I will need to read in the annotations for
braziliensis/panamensis panama and any other sequences. But the
sequences which are directly extracted from panamensis colombia I will
be able to use the same annotations.
wanted_ids <- c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
"LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300")
reference <- write_cds_entries("compare_strains/lpanamensis_v36.fasta", all_lp_annot,
ids = wanted_ids, output = "compare_strains/lpanamensis_cds.fasta")
modified_12588 <- write_cds_entries("compare_strains/strain_12588_modified_z21.fasta", all_lp_annot,
name_prefix = "z21", ids = wanted_ids,
output = "compare_strains/lpanamensis_z21_cds.fasta")
modified_2272 <- write_cds_entries("compare_strains/strain_2272_modified_z22.fasta", all_lp_annot,
name_prefix = "z22", ids = wanted_ids,
output = "compare_strains/lpanamensis_z22_cds.fasta")
modified_2168 <- write_cds_entries("compare_strains/strain_2168_modified_z23.fasta", all_lp_annot,
name_prefix = "z23", ids = wanted_ids,
output = "compare_strains/lpanamensis_z23_cds.fasta")
modified_12444 <- write_cds_entries("compare_strains/strain_12444_modified_z24.fasta", all_lp_annot,
name_prefix = "z24", ids = wanted_ids,
output = "compare_strains/lpanamensis_z24_cds.fasta")
Having written these files, I concatenated the zymodeme CDS sequences
into individual sequences/strain and performed a MSA and MP tree using
clustalo[ref] and PhyML[ref] via seaview[ref]. Sadly, there were only 12
informative sites in the 6 zymodeme defining genes. Happily, the tree
generated looks pretty much exactly like my genome-based tree. Also, I
didn’t bother to add the other genomes because with only 12 variant
positions it did not feel interesting.
SNP profiles
Over the last couple of weeks, I redid all the variant searches with
a newer, (I think) more sensitive and more specific variant tool. In
addition I changed my script which interprets the results so that it is
able to extract any tags from it, instead of just the one or two that my
previous script handled. In addition, at least in theory it is now able
to provide the set of amino acid substitutions for every gene in species
without or with introns (not really relevant for Leishmania
panamensis).
both_norm <- set_expt_conditions(both_snps, fact = "knnv2classification")
## strains <- both_norm[["design"]][["strain"]]
both_strain <- set_expt_conditions(both_norm, fact = "strain")
The data structure ‘both_norm’ now contains our 2016 data along with
the newer data collected since 2019.
Plot of SNP profiles
for zymodemes
The following plot shows the SNP profiles of all samples (old and
new) where the colors at the top show either the 2.2 strains (orange),
2.3 strains (green), the previous samples (purple), or the various lab
strains (pink etc).
new_variant_heatmap <- plot_corheat(new_snps)
dev <- pp(file = "images/raw_snp_corheat.png", height = 12, width = 12)
new_variant_heatmap$plot
closed <- dev.off()
new_variant_heatmap$plot

The function get_snp_sets() takes the provided metadata factor (in
this case ‘condition’) and looks for variants which are exclusive to
each element in it. In this case, this is looking for differences
between 2.2 and 2.3, as well as the set shared among them.
snp_sets <- get_snp_sets(both_snps, factor = "knnhclusttogethercall")
snp_sets
Biobase::annotation(lp_previous$expressionset) <- Biobase::annotation(lp_expt$expressionset)
lp_knn <- set_expt_conditions(lp_expt, fact = "knnhclusttogethercall")
both_expt <- combine_expts(lp_knn, lp_previous)
snp_genes <- snps_vs_genes(both_expt, snp_sets, expt_name_col = "chromosome")
## I think we have some metrics here we can plot...s
snp_subset <- snp_subset_genes(
both_expt, both_snps,
genes = c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
"LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300"))
zymo_heat <- plot_sample_heatmap(
normalize_expt(snp_subset, transform = "log2"),
row_label = rownames(exprs(snp_subset)))
zymo_heat
most_variant <- head(snp_genes$count_by_gene, n = 100)
least_variant <- tail(snp_genes$count_by_gene, n = 100)
test <- simple_goseq(names(most_variant), go_db = lp_go, length_db = lp_lengths)
Compare variants to
DE genes
Najib has asked a few times about the relationship between variants
and DE genes. In subsequent conversations I figured out what he really
wants to learn is variants in the UTR (most likely 5’) which might
affect expression of genes. The following explicitly does not help this
question, but is a paralog: is there a relationship between variants in
the CDS and differential expression?
Collect DE
data
In order to do this comparison, we need to reload some of the DE
results.
rda <- glue("rda/zymo_tables_nobatch-v{ver}.rda")
varname <- gsub(x = basename(rda), pattern = "\\.rda", replacement = "")
loaded <- load(file = rda)
zy_df <- get0(varname)[["data"]][["zymodeme"]]
rda <- glue("rda/sus_tables_nobatch-v{ver}.rda")
varname <- gsub(x = basename(rda), pattern = "\\.rda", replacement = "")
loaded <- load(file = rda)
sus_df <- get0(varname)[["data"]][["resistant_sensitive"]]
Create a table of
variants by gene
In the previous section, I did this process using older and newer
data (and disabled it because we do not have all of that data available
for the container; but I still want to do some of these comparisons and
so will do them using only the current/new tree.
snp_sets <- get_snp_sets(new_snps, factor = "knnhclusttogethercall")
## The samples represent the following categories:
##
## z21 z22 z23
## 9 37 42
## Using a proportion of observed variants, converting the data to binary observations.
## The factor unknown has 3 rows.
## The factor z21 has 9 rows.
## The factor z22 has 37 rows.
## The factor z23 has 42 rows.
## Finished iterating over the chromosomes.
## A set of variants observed when cross referencing all variants against
## the samples associated with each metadata factor: knnhclusttogethercall. 4
## categories and 943592 variants were observed with 15
## combinations among them. 725 chromosomes/scaffolds were observed with a
## density of variants ranging from 0.000652315720808871 to 0.115079365079365.
lp_knn <- set_expt_conditions(lp_expt, fact = "knnhclusttogethercall")
## The numbers of samples by condition are:
##
## null z21 z22 z23
## 3 9 37 42
snp_genes <- snps_vs_genes(lp_knn, snp_sets, expt_name_col = "chromosome")
## The snp grange data has 943592 elements.
## There are 437801 overlapping variants and genes.
vars_df <- data.frame(ID = names(snp_genes[["count_by_gene"]]),
variants = as.numeric(snp_genes[["count_by_gene"]]))
vars_df <- merge(vars_df, lp_lengths, by = "ID")
vars_df[["length"]] <- as.numeric(vars_df[["length"]])
vars_df[["var_len"]] <- vars_df[["variants"]] / vars_df[["length"]]
vars_by_de_gene <- merge(zy_df, vars_df, by.x="row.names", by.y="ID")
rownames(vars_by_de_gene) <- vars_by_de_gene[["Row.names"]]
vars_by_de_gene[["Row.names"]] <- NULL
cor.test(vars_by_de_gene$deseq_logfc, vars_by_de_gene$var_len)
##
## Pearson's product-moment correlation
##
## data: vars_by_de_gene$deseq_logfc and vars_by_de_gene$var_len
## t = -4, df = 8532, p-value = 7e-05
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.06415 -0.02180
## sample estimates:
## cor
## -0.043
variants_wrt_logfc <- plot_linear_scatter(vars_by_de_gene, xcol = "deseq_logfc",
ycol = "var_len", text_col = "annotgeneproduct")
scatter <- variants_wrt_logfc$scatter
plotly::ggplotly(scatter, tooltip = c("x", "y", "text"))
## Error in `check_aesthetics()`:
## ! Aesthetics must be either length 1 or the same as the data (8557).
## x Fix the following mappings: `colour`.
## It looks like there might be some genes of interest, even though this is not actually
## the question of interest.
Ok, I think I can do this on a UTR basis.
snp_utrs <- snps_vs_genes_padded(both_expt, snp_sets, expt_name_col = "chromosome")
fivep_vars_df <- data.frame(ID = names(snp_utrs[["count_fivep_by_gene"]]),
variants = as.numeric(snp_utrs[["count_fivep_by_gene"]]))
fivep_vars_by_de_gene <- merge(zy_df, fivep_vars_df, by.x="row.names", by.y="ID")
rownames(fivep_vars_by_de_gene) <- fivep_vars_by_de_gene[["Row.names"]]
fivep_vars_by_de_gene[["Row.names"]] <- NULL
cor.test(fivep_vars_by_de_gene$deseq_logfc, fivep_vars_by_de_gene[["variants"]])
fivep_variants_wrt_logfc <- plot_linear_scatter(fivep_vars_by_de_gene, xcol = "deseq_logfc",
ycol = "variants", text_col = "annotgeneproduct")
scatter <- fivep_variants_wrt_logfc$scatter
scatter
plotly::ggplotly(scatter, tooltip = c("x", "y", "text"))
threep_vars_df <- data.frame(ID = names(snp_utrs[["count_threep_by_gene"]]),
variants = as.numeric(snp_utrs[["count_threep_by_gene"]]))
threep_vars_by_de_gene <- merge(zy_df, threep_vars_df, by.x="row.names", by.y="ID")
rownames(threep_vars_by_de_gene) <- threep_vars_by_de_gene[["Row.names"]]
threep_vars_by_de_gene[["Row.names"]] <- NULL
cor.test(threep_vars_by_de_gene$deseq_logfc, threep_vars_by_de_gene[["variants"]])
threep_variants_wrt_logfc <- plot_linear_scatter(threep_vars_by_de_gene, xcol = "deseq_logfc",
ycol = "variants", text_col = "annotgeneproduct")
scatter <- threep_variants_wrt_logfc$scatter
plotly::ggplotly(scatter, tooltip = c("x", "y", "text"))
Once again, let us do this with only the new data.
snp_utrs <- snps_vs_genes_padded(lp_expt, snp_sets, expt_name_col = "chromosome")
fivep_vars_df <- data.frame(ID = names(snp_utrs[["count_fivep_by_gene"]]),
variants = as.numeric(snp_utrs[["count_fivep_by_gene"]]))
fivep_vars_by_de_gene <- merge(zy_df, fivep_vars_df, by.x="row.names", by.y="ID")
rownames(fivep_vars_by_de_gene) <- fivep_vars_by_de_gene[["Row.names"]]
fivep_vars_by_de_gene[["Row.names"]] <- NULL
cor.test(fivep_vars_by_de_gene$deseq_logfc, fivep_vars_by_de_gene[["variants"]])
fivep_variants_wrt_logfc <- plot_linear_scatter(fivep_vars_by_de_gene, xcol = "deseq_logfc",
ycol = "variants", text_col = "annotgeneproduct")
scatter <- fivep_variants_wrt_logfc$scatter
scatter
plotly::ggplotly(scatter, tooltip = c("x", "y", "text"))
threep_vars_df <- data.frame(ID = names(snp_utrs[["count_threep_by_gene"]]),
variants = as.numeric(snp_utrs[["count_threep_by_gene"]]))
threep_vars_by_de_gene <- merge(zy_df, threep_vars_df, by.x="row.names", by.y="ID")
rownames(threep_vars_by_de_gene) <- threep_vars_by_de_gene[["Row.names"]]
threep_vars_by_de_gene[["Row.names"]] <- NULL
cor.test(threep_vars_by_de_gene$deseq_logfc, threep_vars_by_de_gene[["variants"]])
threep_variants_wrt_logfc <- plot_linear_scatter(threep_vars_by_de_gene, xcol = "deseq_logfc",
ycol = "variants", text_col = "annotgeneproduct")
scatter <- threep_variants_wrt_logfc$scatter
plotly::ggplotly(scatter, tooltip = c("x", "y", "text"))
Didn’t I create a set of densities by chromosome? Oh I think they
come in from get_snp_sets()
SNPS associated with
clinical response in the TMRC samples
clinical_sets <- get_snp_sets(new_snps, factor = "clinicalresponse")
## The samples represent the following categories:
##
## cure failure nd
## 39 34 18
## Using a proportion of observed variants, converting the data to binary observations.
## The factor cure has 39 rows.
## The factor failure has 34 rows.
## The factor nd has 18 rows.
## Finished iterating over the chromosomes.
## A set of variants observed when cross referencing all variants against
## the samples associated with each metadata factor: clinicalresponse. 3
## categories and 943592 variants were observed with 7
## combinations among them. 725 chromosomes/scaffolds were observed with a
## density of variants ranging from 0.000652315720808871 to 0.115079365079365.
density_vec <- clinical_sets[["density"]]
chromosome_idx <- grep(pattern = "LpaL", x = names(density_vec))
density_df <- as.data.frame(density_vec[chromosome_idx])
density_df[["chr"]] <- rownames(density_df)
colnames(density_df) <- c("density_vec", "chr")
## I am bad with quasiquotation and the various non-standard evaluation techniques of tidyr.
## Let us use the following plot to see if I remember how to use bangbang to replace a NSE
## name with a variable, in this case the creatively named 'test'
## I rather think this is gross and generally prefer to just use .data
test <- sym("chr")
ggplot(density_df, aes(x = !!test, y = density_vec)) +
ggplot2::geom_col() +
ggplot2::theme(axis.text = ggplot2::element_text(size = 10, colour = "black"),
axis.text.x = ggplot2::element_text(angle = 90, vjust = 0.5))

## clinical_written <- write_snps(new_snps, output_file = "clinical.aln")
## In my sample sheet, I did not record the variants by gene or a few other data which were
## collected
test_spec <- make_dnaseq_spec()
new_meta <- gather_preprocessing_metadata(pData(new_snps), specification = test_spec)
## Error in dispatch_metadata_extract(meta, entry_type, input_file_spec, : I do not know this spec: hisat_genome_percent
test <- classify_variants(new_meta[["new_meta"]], coverage_column = NULL,
variants_column = "freebayes_variants_by_gene")
## Error in eval(expr, envir, enclos): object 'new_meta' not found
column_colors <- pData(new_snps)[["knnv2classification"]]
red_idx <- column_colors == "z22"
column_colors[red_idx] <- "red"
pink_idx <- column_colors == "z21"
column_colors[pink_idx] <- "salmon"
blue_idx <- column_colors == "z23"
column_colors[blue_idx] <- "darkblue"
blue_idx <- column_colors == "z24"
column_colors[blue_idx] <- "cornflowerblue"
gplots::heatmap.2(as.matrix(test$mutations_by_sample_norm), trace = "none",
ColSideColors = column_colors)
## Error in (function (cond) : error in evaluating the argument 'x' in selecting a method for function 'as.matrix': object of type 'symbol' is not subsettable
Cross reference
these variants by gene
clinical_genes <- snps_vs_genes(lp_expt, clinical_sets, expt_name_col = "chromosome")
## The snp grange data has 943592 elements.
## There are 437801 overlapping variants and genes.
## When the variants observed were cross referenced against annotated genes,
## 8627 genes were observed with at least 1 variant.
## LPAL13_250017600 had the most variants, with 893.
snp_density <- merge(as.data.frame(clinical_genes[["count_by_gene"]]),
as.data.frame(fData(lp_expt)),
by = "row.names")
snp_density <- snp_density[, c(1, 2, 4, 15)]
colnames(snp_density) <- c("name", "snps", "product", "length")
snp_density[["product"]] <- tolower(snp_density[["product"]])
snp_density[["length"]] <- as.numeric(snp_density[["length"]])
snp_density[["density"]] <- snp_density[["snps"]] / snp_density[["length"]]
snp_idx <- order(snp_density[["density"]], decreasing = TRUE)
snp_density <- snp_density[snp_idx, ]
removers <- c("amastin", "gp63", "leishmanolysin")
for (r in removers) {
drop_idx <- grepl(pattern = r, x = snp_density[["product"]])
snp_density <- snp_density[!drop_idx, ]
}
## Filter these for [A|a]mastin gp63 Leishmanolysin
clinical_snps <- snps_intersections(lp_expt, clinical_sets, chr_column = "chromosome")
fail_ref_snps <- as.data.frame(clinical_snps[["inters"]][["failure, reference strain"]])
fail_ref_snps <- rbind(fail_ref_snps,
as.data.frame(clinical_snps[["inters"]][["failure"]]))
cure_snps <- as.data.frame(clinical_snps[["inters"]][["cure"]])
head(fail_ref_snps)
## seqnames start end
## chr_LPAL13-SCAF000063_pos_2573_ref_C_alt_A LPAL13-SCAF000063 2573 2574
## chr_LPAL13-SCAF000165_pos_363_ref_G_alt_C LPAL13-SCAF000165 363 364
## chr_LPAL13-SCAF000627_pos_2267_ref_G_alt_T LPAL13-SCAF000627 2267 2268
## chr_LpaL13-01_pos_26758_ref_T_alt_C LpaL13-01 26758 26759
## chr_LpaL13-03_pos_164108_ref_A_alt_G LpaL13-03 164108 164109
## chr_LpaL13-03_pos_236263_ref_T_alt_C LpaL13-03 236263 236264
## width strand
## chr_LPAL13-SCAF000063_pos_2573_ref_C_alt_A 2 +
## chr_LPAL13-SCAF000165_pos_363_ref_G_alt_C 2 +
## chr_LPAL13-SCAF000627_pos_2267_ref_G_alt_T 2 +
## chr_LpaL13-01_pos_26758_ref_T_alt_C 2 +
## chr_LpaL13-03_pos_164108_ref_A_alt_G 2 +
## chr_LpaL13-03_pos_236263_ref_T_alt_C 2 +
## seqnames start end
## chr_LPAL13-SCAF000627_pos_1869_ref_A_alt_G LPAL13-SCAF000627 1869 1870
## chr_LpaL13-06_pos_2975_ref_T_alt_C LpaL13-06 2975 2976
## chr_LpaL13-09_pos_58219_ref_G_alt_A LpaL13-09 58219 58220
## chr_LpaL13-12_pos_194617_ref_C_alt_T LpaL13-12 194617 194618
## chr_LpaL13-13_pos_19037_ref_A_alt_G LpaL13-13 19037 19038
## chr_LpaL13-13_pos_337854_ref_T_alt_C LpaL13-13 337854 337855
## width strand
## chr_LPAL13-SCAF000627_pos_1869_ref_A_alt_G 2 +
## chr_LpaL13-06_pos_2975_ref_T_alt_C 2 +
## chr_LpaL13-09_pos_58219_ref_G_alt_A 2 +
## chr_LpaL13-12_pos_194617_ref_C_alt_T 2 +
## chr_LpaL13-13_pos_19037_ref_A_alt_G 2 +
## chr_LpaL13-13_pos_337854_ref_T_alt_C 2 +
write.csv(file="csv/cure_variants.txt", x=rownames(cure_snps))
## Warning in file(file, ifelse(append, "a", "w")): cannot open file
## 'csv/cure_variants.txt': No such file or directory
## Error in file(file, ifelse(append, "a", "w")): cannot open the connection
write.csv(file="csv/fail_variants.txt", x=rownames(fail_ref_snps))
## Warning in file(file, ifelse(append, "a", "w")): cannot open file
## 'csv/fail_variants.txt': No such file or directory
## Error in file(file, ifelse(append, "a", "w")): cannot open the connection
annot <- fData(lp_expt)
clinical_interest <- as.data.frame(clinical_snps[["gene_summaries"]][["cure"]])
clinical_interest <- merge(clinical_interest,
as.data.frame(clinical_snps[["gene_summaries"]][["failure, reference strain"]]),
by = "row.names")
rownames(clinical_interest) <- clinical_interest[["Row.names"]]
clinical_interest[["Row.names"]] <- NULL
colnames(clinical_interest) <- c("cure_snps","fail_snps")
## Error in names(x) <- value: 'names' attribute [2] must be the same length as the vector [1]
annot <- merge(annot, clinical_interest, by = "row.names")
rownames(annot) <- annot[["Row.names"]]
annot[["Row.names"]] <- NULL
fData(lp_expt$expressionset) <- annot
Zymodeme for new
samples
The heatmap produced here should show the variants only for the
zymodeme genes.
Hunt for snp
clusters
I am thinking that if we find clusters of locations which are
variant, that might provide some PCR testing possibilities.
## Drop the 2.1, 2.4, unknown, and null
knn_snps <- set_expt_conditions(new_snps, fact = "knnv2classification") %>%
subset_expt(subset="condition=='z22'|condition=='z23'")
knn_sets <- get_snp_sets(knn_snps, factor = "knnv2classification")
knn_sets
summary(knn_sets)
## 1000000: 2.2
## 0100000: 2.3
pruned_snps <- subset_expt(new_snps, subset="condition=='z2.2'|condition=='z2.3'")
new_sets <- get_snp_sets(pruned_snps, factor = "condition")
new_sets
summary(new_sets[["intersections"]][["10"]])
write.csv(file="csv/variants_22.csv", x=new_sets[["intersections"]][["10"]])
summary(new_sets[["intersections"]][["01"]])
write.csv(file="csv/variants_23.csv", x=new_sets[["intersections"]][["01"]])
summary(knn_sets[["intersections"]][["10"]])
write.csv(file="csv/knn_variants_22.csv", x=new_sets[["intersections"]][["10"]])
summary(knn_sets[["intersections"]][["01"]])
write.csv(file="csv/knn_variants_23.csv", x=new_sets[["intersections"]][["01"]])
Thus we see that there are 601 variants associated with 2.2 and
67,171 associated with 2.3.
Search for PCR
primers
The sequential_variants() function searches for variants which are
clustered close together in the hopes that choosing PCR primers focused
on this positions will(not) anneal and may be used as a quick way to
identify strains.
The current set of strains
zymo22_sequentials <- sequential_variants(new_sets, conditions = "z2.2",
minimum = 1, maximum_separation = 2)
## Error in sequential_variants(new_sets, conditions = "z2.2", minimum = 1, : could not find function "sequential_variants"
## Error in eval(expr, envir, enclos): object 'zymo22_sequentials' not found
## 7 candidate regions for zymodeme 2.2 -- thus I am betting that the reference strain is a 2.2
zymo23_sequentials <- sequential_variants(new_sets, conditions = "z2.3",
minimum = 2, maximum_separation = 2)
## Error in sequential_variants(new_sets, conditions = "z2.3", minimum = 2, : could not find function "sequential_variants"
## Error in eval(expr, envir, enclos): object 'zymo23_sequentials' not found
## In contrast, there are lots (587) of interesting regions for 2.3!
knn_zymo22_sequentials <- sequential_variants(knn_sets, conditions = "z2.2",
minimum = 1, maximum_separation = 2)
## Error in sequential_variants(knn_sets, conditions = "z2.2", minimum = 1, : could not find function "sequential_variants"
dim(knn_zymo22_sequentials)
## Error in eval(expr, envir, enclos): object 'knn_zymo22_sequentials' not found
## 7 candidate regions for zymodeme 2.2 -- thus I am betting that the reference strain is a 2.2
knn_zymo23_sequentials <- sequential_variants(knn_new_sets, conditions = "z2.3",
minimum = 2, maximum_separation = 2)
## Error in sequential_variants(knn_new_sets, conditions = "z2.3", minimum = 2, : could not find function "sequential_variants"
dim(knn_zymo23_sequentials)
## Error in eval(expr, envir, enclos): object 'knn_zymo23_sequentials' not found
Go hunting for Sanger
sequencing regions
I made a fun little function which should find regions which have
lots of variants associated with a given experimental factor.
pheno <- subset_expt(lp_expt, subset = "condition=='z2.2'|condition=='z2.3'")
pheno <- subset_expt(pheno, subset = "!is.na(pData(pheno)[['bcftable']])")
pheno_snps <- sm(count_expt_snps(pheno, annot_column = "bcftable"))
fun_stuff <- snp_density_primers(
pheno_snps,
bsgenome = "BSGenome.Leishmania.panamensis.MHOMCOL81L13.v53",
gff = "reference/TriTrypDB-53_LpanamensisMHOMCOL81L13.gff")
drop_scaffolds <- grepl(x = rownames(fun_stuff$favorites), pattern = "SCAF")
favorite_primer_regions <- fun_stuff[["favorites"]][!drop_scaffolds, ]
favorite_primer_regions[["bin"]] <- rownames(favorite_primer_regions)
library(dplyr)
favorite_primer_regions <- favorite_primer_regions %>%
relocate(bin)
Combine this table
with 2.2/2.3 genes
Here is my note from our meeting:
Cross reference primers to DE genes of 2.2/2.3 and/or
resistance/suscpetible, add a column to the primer spreadsheet with the
DE genes (in retrospect I am guessing this actually means to put the
logFC as a column.
One nice thing, I did a semantic removal on the lp_expt, so the set
of logFC/pvalues should not have any of the offending types; thus I
should be able to automagically get rid of them in the merge.
logfc_columns <- zy_df[, c("deseq_logfc", "deseq_adjp")]
colnames(logfc_columns) <- c("z23_logfc", "z23_adjp")
new_table <- merge(favorite_primer_regions, logfc_columns,
by.x = "closest_gene_before_id", by.y = "row.names")
sus_columns <- sus_df[, c("deseq_logfc", "deseq_adjp")]
colnames(sus_columns) <- c("sus_logfc", "sus_adjp")
new_table <- merge(new_table, sus_columns,
by.x = "closest_gene_before_id", by.y = "row.names") %>%
relocate(bin)
written <- write_xlsx(data=new_table,
excel="excel/favorite_primers_xref_zy_sus.xlsx")
Make a heatmap
describing the clustering of variants
We can cross reference the variants against the zymodeme status and
plot a heatmap of the results and hopefully see how they separate.
snp_genes <- snps_vs_genes(lp_expt, new_sets, expt_name_col = "chromosome")
## Error in eval(expr, envir, enclos): object 'new_sets' not found
clinical_colors_v2 <- list(
"z22" = "#0000cc",
"z23" = "#cc0000")
new_zymo_norm <- normalize_expt(pruned_snps, norm = "quant") %>%
set_expt_conditions(fact = "zymodemecategorical") %>%
set_expt_colors(clinical_colors_v2)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'pruned_snps' not found
zymo_heat <- plot_disheat(new_zymo_norm)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt_data' in selecting a method for function 'plot_heatmap': object 'new_zymo_norm' not found
pp(file = "images/onlyz22_z23_snp_heatmap.png", width=12, height=12)
zymo_heat$plot
## Error in eval(expr, envir, enclos): object 'zymo_heat' not found
closed <- dev.off()
zymo_heat[["plot"]]
## Error in eval(expr, envir, enclos): object 'zymo_heat' not found
Annotated heatmap
of variants
Now let us try to make a heatmap which includes some of the
annotation data.
des <- pData(both_norm)
zymo_column <- "zymodemecategorical"
undef_idx <- is.na(des[[zymo_column]])
des[undef_idx, "strain"] <- "unknown"
##hmcols <- colorRampPalette(c("yellow","black","darkblue"))(256)
correlations <- hpgl_cor(exprs(both_norm))
na_idx <- is.na(correlations)
correlations[na_idx] <- 0
zymo_missing_idx <- is.na(des[[zymo_column]])
des[[zymo_column]] <- as.character(des[[zymo_column]])
des[["clinicalcategorical"]] <- as.character(des[["clinicalcategorical"]])
des[zymo_missing_idx, zymo_column] <- "unknown"
mydendro <- list(
"clustfun" = hclust,
"lwd" = 2.0)
col_data <- as.data.frame(des[, c(zymo_column, "clinicalcategorical")])
colnames(col_data) <- c("zymodeme", "outcome")
unknown_clinical <- is.na(col_data[["clinicalcategorical"]])
col_data[unknown_clinical, "outcome"] <- "undefined"
row_data <- as.data.frame(des[, c("sus_category_current", "knnv2classification")])
colnames(row_data) <- c("susceptibility", "mlclass")
myannot <- list(
"Col" = list("data" = col_data),
"Row" = list("data" = row_data))
myclust <- list("cuth" = 1.0,
"col" = BrewerClusterCol)
mylabs <- list(
"Row" = list("nrow" = 4),
"Col" = list("nrow" = 4))
hmcols <- colorRampPalette(c("darkblue", "beige"))(380)
zymo_annot_heat <- annHeatmap2(
correlations,
dendrogram = mydendro,
annotation = myannot,
cluster = myclust,
labels = mylabs,
## The following controls if the picture is symmetric
scale = "none",
col = hmcols)
dev <- pp(file = "images/dendro_heatmap.png", height = 20, width = 20)
plot(zymo_annot_heat)
closed <- dev.off()
plot(zymo_annot_heat)
Print the larger heatmap so that all the labels appear. Keep in mind
that as we get more samples, this image needs to continue getting
bigger.
big heatmap
xref_prop <- table(pheno_snps[["conditions"]])
pheno_snps$conditions
idx_tbl <- exprs(pheno_snps) > 5
new_tbl <- data.frame(row.names = rownames(exprs(pheno_snps)))
for (n in names(xref_prop)) {
new_tbl[[n]] <- 0
idx_cols <- which(pheno_snps[["conditions"]] == n)
prop_col <- rowSums(idx_tbl[, idx_cols]) / xref_prop[n]
new_tbl[n] <- prop_col
}
keepers <- grepl(x = rownames(new_tbl), pattern = "LpaL13")
new_tbl <- new_tbl[keepers, ]
new_tbl[["strong22"]] <- 1.001 - new_tbl[["z2.2"]]
new_tbl[["strong23"]] <- 1.001 - new_tbl[["z2.3"]]
s22_na <- new_tbl[["strong22"]] > 1
new_tbl[s22_na, "strong22"] <- 1
s23_na <- new_tbl[["strong23"]] > 1
new_tbl[s23_na, "strong23"] <- 1
new_tbl[["SNP"]] <- rownames(new_tbl)
new_tbl[["Chromosome"]] <- gsub(x = new_tbl[["SNP"]], pattern = "chr_(.*)_pos_.*", replacement = "\\1")
new_tbl[["Position"]] <- gsub(x = new_tbl[["SNP"]], pattern = ".*_pos_(\\d+)_.*", replacement = "\\1")
new_tbl <- new_tbl[, c("SNP", "Chromosome", "Position", "strong22", "strong23")]
library(CMplot)
simplify <- new_tbl
simplify[["strong22"]] <- NULL
CMplot(simplify, bin.size = 100000)
CMplot(new_tbl, plot.type="m", multracks=TRUE, threshold = c(0.01, 0.05),
threshold.lwd=c(1,1), threshold.col=c("black","grey"),
amplify=TRUE, bin.size=10000,
chr.den.col=c("darkgreen", "yellow", "red"),
signal.col=c("red", "green", "blue"),
signal.cex=1, file="jpg", memo="", dpi=300, file.output=TRUE, verbose=TRUE)
Try out
MatrixEQTL
This tool looks a little opaque, but provides sample data with things
that make sense to me and should be pretty easy to recapitulate in our
data.
- covariates.txt: Columns are samples, rows are things from pData –
the most likely ones of interest for our data would be zymodeme,
sensitivity
- geneloc.txt: columns are ‘geneid’, ‘chr’, ‘left’, ‘right’. I guess I
can assume left and right are start/stop; in which case this is
trivially acquirable from fData.
- ge.txt: This appears to be a log(rpkm/cpm) table with rows as genes
and columns as samples
- snpsloc.txt: columns are ‘snpid’, ‘chr’, ‘pos’
- snps.txt: columns are samples, rows are the ids from snsploc, values
a 0,1,2. I assume 0 is identical and 1..12 are the various A->TGC
T->AGC C->AGT G->ACT
## For this, let us use the 'new_snps' data structure.
## Caveat here: these need to be coerced to numbers.
my_covariates <- pData(new_snps)[, c(zymo_column, "clinicalcategorical")]
for (col in colnames(my_covariates)) {
my_covariates[[col]] <- as.numeric(as.factor(my_covariates[[col]]))
}
my_covariates <- t(my_covariates)
my_geneloc <- fData(lp_expt)[, c("gid", "chromosome", "start", "end")]
colnames(my_geneloc) <- c("geneid", "chr", "left", "right")
my_ge <- exprs(normalize_expt(lp_expt, transform = "log2", filter = TRUE, convert = "cpm"))
used_samples <- tolower(colnames(my_ge)) %in% colnames(exprs(new_snps))
my_ge <- my_ge[, used_samples]
my_snpsloc <- data.frame(rownames = rownames(exprs(new_snps)))
## Oh, caveat here: Because of the way I stored the data,
## I could have duplicate rows which presumably will make matrixEQTL sad
my_snpsloc[["chr"]] <- gsub(pattern = "^chr_(.+)_pos(.+)_ref_.*$", replacement = "\\1",
x = rownames(my_snpsloc))
my_snpsloc[["pos"]] <- gsub(pattern = "^chr_(.+)_pos(.+)_ref_.*$", replacement = "\\2",
x = rownames(my_snpsloc))
test <- duplicated(my_snpsloc)
## Each duplicated row would be another variant at that position;
## so in theory we would do a rle to number them I am guessing
## However, I do not have different variants so I think I can ignore this for the moment
## but will need to make my matrix either 0 or 1.
if (sum(test) > 0) {
message("There are: ", sum(duplicated), " duplicated entries.")
keep_idx <- ! test
my_snpsloc <- my_snpsloc[keep_idx, ]
}
my_snps <- exprs(new_snps)
one_idx <- my_snps > 0
my_snps[one_idx] <- 1
## Ok, at this point I think I have all the pieces which this method wants...
## Oh, no I guess not; it actually wants the data as a set of filenames...
library(MatrixEQTL)
write.table(my_snps, "eqtl/snps.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(my_snps, "eqtl/snps.tsv", )
write.table(my_snpsloc, "eqtl/snpsloc.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(my_snpsloc, "eqtl/snpsloc.tsv")
write.table(as.data.frame(my_ge), "eqtl/ge.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_ge), "eqtl/ge.tsv")
write.table(as.data.frame(my_geneloc), "eqtl/geneloc.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_geneloc), "eqtl/geneloc.tsv")
write.table(as.data.frame(my_covariates), "eqtl/covariates.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_covariates), "eqtl/covariates.tsv")
useModel = modelLINEAR # modelANOVA, modelLINEAR, or modelLINEAR_CROSS
# Genotype file name
SNP_file_name = "eqtl/snps.tsv"
snps_location_file_name = "eqtl/snpsloc.tsv"
expression_file_name = "eqtl/ge.tsv"
gene_location_file_name = "eqtl/geneloc.tsv"
covariates_file_name = "eqtl/covariates.tsv"
# Output file name
output_file_name_cis = tempfile()
output_file_name_tra = tempfile()
# Only associations significant at this level will be saved
pvOutputThreshold_cis = 0.1
pvOutputThreshold_tra = 0.1
# Error covariance matrix
# Set to numeric() for identity.
errorCovariance = numeric()
# errorCovariance = read.table("Sample_Data/errorCovariance.txt");
# Distance for local gene-SNP pairs
cisDist = 1e6
## Load genotype data
snps = SlicedData$new()
snps$fileDelimiter = "\t" # the TAB character
snps$fileOmitCharacters = "NA" # denote missing values;
snps$fileSkipRows = 1 # one row of column labels
snps$fileSkipColumns = 1 # one column of row labels
snps$fileSliceSize = 2000 # read file in slices of 2,000 rows
snps$LoadFile(SNP_file_name)
## Load gene expression data
gene = SlicedData$new()
gene$fileDelimiter = "\t" # the TAB character
gene$fileOmitCharacters = "NA" # denote missing values;
gene$fileSkipRows = 1 # one row of column labels
gene$fileSkipColumns = 1 # one column of row labels
gene$fileSliceSize = 2000 # read file in slices of 2,000 rows
gene$LoadFile(expression_file_name)
## Load covariates
cvrt = SlicedData$new()
cvrt$fileDelimiter = "\t" # the TAB character
cvrt$fileOmitCharacters = "NA" # denote missing values;
cvrt$fileSkipRows = 1 # one row of column labels
cvrt$fileSkipColumns = 1 # one column of row labels
if(length(covariates_file_name) > 0) {
cvrt$LoadFile(covariates_file_name)
}
## Run the analysis
snpspos = read.table(snps_location_file_name, header = TRUE, stringsAsFactors = FALSE)
genepos = read.table(gene_location_file_name, header = TRUE, stringsAsFactors = FALSE)
me = Matrix_eQTL_main(
snps = snps,
gene = gene,
cvrt = cvrt,
output_file_name = output_file_name_tra,
pvOutputThreshold = pvOutputThreshold_tra,
useModel = useModel,
errorCovariance = errorCovariance,
verbose = TRUE,
output_file_name.cis = output_file_name_cis,
pvOutputThreshold.cis = pvOutputThreshold_cis,
snpspos = snpspos,
genepos = genepos,
cisDist = cisDist,
pvalue.hist = "qqplot",
min.pv.by.genesnp = FALSE,
noFDRsaveMemory = FALSE);
if (!isTRUE(get0("skip_load"))) {
pander::pander(sessionInfo())
message("This is hpgltools commit: ", get_git_commit())
message("Saving to ", savefile)
## tmp <- sm(saveme(filename = savefile))
}
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset
## This is hpgltools commit:
## Saving to 04post_visualization_202406.rda.xz
tmp <- loadme(filename = savefile)
LS0tCnRpdGxlOiAiVE1SQzIgYHIgU3lzLmdldGVudignVkVSU0lPTicpYDogVmlzdWFsaXppbmcgQW5hbHlzZXMgZm9sbG93aW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSAubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTYwMHB4Owp9CmJvZHksIHRkIHsKICBmb250LXNpemU6IDE2cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTZweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoSGVhdHBsdXMpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeShocGdsdG9vbHMpCmxpYnJhcnkoZ2dwbG90MikKCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHByb2dyZXNzID0gVFJVRSwgdmVyYm9zZSA9IFRSVUUsIHdpZHRoID0gOTAsIGVjaG8gPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZXJyb3IgPSBUUlVFLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gOSwgZmlnLnJldGluYSA9IDIsCiAgb3V0LndpZHRoID0gIjEwMCUiLCBkZXYgPSAicG5nIiwKICBkZXYuYXJncyA9IGxpc3QocG5nID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKSkKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxMikpCnZlciA8LSBTeXMuZ2V0ZW52KCJWRVJTSU9OIikKcHJldmlvdXNfZmlsZSA8LSAiIgpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQgPSAiJVklbSVkIikKCiMjIHRtcCA8LSB0cnkoc20obG9hZG1lKGZpbGVuYW1lID0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcHJldmlvdXNfZmlsZSkpKSkKcm1kX2ZpbGUgPC0gZ2x1ZSgiMDRwb3N0X3Zpc3VhbGl6YXRpb25fe3Zlcn0uUm1kIikKc2F2ZWZpbGUgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcm1kX2ZpbGUpCmxvYWRlZCA8LSBsb2FkKGZpbGUgPSBnbHVlKCJyZGEvdG1yYzJfZGF0YV9zdHJ1Y3R1cmVzLXZ7dmVyfS5yZGEiKSkKYGBgCgojIEludHJvZHVjdGlvbgoKVGhpcyBpcyBpbnRlbmRlZCB0byBjb250YWluIGFuYWx5c2VzIHdoaWNoIGxvZ2ljYWxseSBmb2xsb3cgb3VyCnRyYW5zY2lwdG9taWMgYW5hbHlzZXMuICBJdCBpcyB0aGVyZWZvcmUgYSBiaXQgb2YgYSBncmFiIGJhZywgaXQgbWF5CmV2ZW50dWFsbHkgY29tcHJpc2UgdGhlIHZhcmlhbnQgc2VhcmNoOyBidXQgY3VycmVudGx5IHRoYXQgaXMKaW50ZXJ0d2luZWQgd2l0aCB0aGUgREUgcmVzdWx0cy4gIEFzIGEgcmVzdWx0LCB0aGlzIGFuZCB0aGUKJ3ByZV92aXN1YWxpemF0aW9uJyBkb2N1bWVudCBhcmUgY3VycmVudGx5IHZlcnkgcmVkdW5kYW50LgoKIyMgQ3JlYXRpbmcgdHJlZXMgb2YgaW1wb3J0YW50IHN0cmFpbnMKCkkgaGF2ZSBhIGZldyBtZXRob2RzIG9mIGNyZWF0aW5nIChwaHlsb2dlbmV0aWMpIHRyZWVzIGRlc2NyaWJpbmcgdGhlCnJlbGF0aW9uc2hpcCBvZiB0aGUgc3RyYWlucyBvZiBpbnRlcmVzdCBpbiB0aGlzIGRhdGEuCgoqIENhbGN1bGF0ZSBtYXRyaWNlcyBvZiB0aGUgdmFyaWFudHMgaW4gdGhlIGRhdGEgYW5kIHVzZSB0b29scyBsaWtlCiAgZXVjbGlkZWFuIGRpc3RhbmNlIGFuZCBoY2x1c3QgdG8gY2FsY3VsYXRlIHRoZSBkZWdyZWUgb2Ygc2ltaWxhcml0eS4KKiBDcmVhdGUgYSBrbWVyIGluZGV4IG9mIGVhY2ggZ2Vub21lIGFuZCB1c2UgYXBlIHRvIGNhbGN1bGF0ZSBkaXN0YW5jZQogIG1ldHJpY3MgYW5kIG5laWdoYm9yIGpvaW5pbmcgdHJlZXMgZGVzY3JpYmluZyB0aGVtLgoqIEV4dHJhY3QgQ0RTIHNlcXVlbmNlcyBvZiBrbm93biBpbnRlcmVzdCAobGlrZWx5IHp5bW9kZW1lIGdlbmVzLAogIHNpbmNlIHRoZXkgYXJlIHN0dWZmIGxpa2UgR1A2L0dBUERIKSBhbmQgcGVyZm9ybSBhIE1TQS0+dHJlZSBvcGVyYXRpb24uCgpgYGB7cn0KbXlfZ2VuZXMgPC0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAgICJMUEFMMTNfMTQwMDA2MTAwIiwgIkxQQUwxM18xODAwMTg1MDAiLCAiTFBBTDEzXzMyMDAyMjMwMCIsCiAgICAgICAgICAgICAgIm90aGVyIikKbXlfbmFtZXMgPC0gYygiQUxBVCIsICJBU0FUIiwgIkc2UEQiLCAiTkh2MSIsICJOSHYyIiwgIk1QSSIsICJvdGhlciIpCmBgYAoKIyBrbWVyIGJhc2VkIGNvbXBhcmlzb24KClRoZSBmb2xsb3dpbmcgYmxvY2sgd2FzIGdlbmVyYXRlZCB2aWEgdGhlIGZvbGxvd2luZyBtZXRob2RzOgoKVGhlIGhpc2F0Mi1iYXNlZCBhbGlnbm1lbnRzIHdlcmUgcGFzc2VkIHRvIGZyZWViYXllczJbcmVmXSwgd2hpY2gKZ2VuZXJhdGVkIGEgc2V0IG9mIGhpZ2gtY29uZmlkZW5jZSB0cmFuc2NyaXB0b21lLWJhc2VkIHZjZiBmaWxlcyBmb3IKZWFjaCBzYW1wbGUgb2Ygc3VmZmljaWVudCBjb3ZlcmFnZS4gIFRoZXNlIHdlcmUgc29ydGVkLCBjb21wcmVzc2VkLAphbmQgaW5kZXhlZCB2aWEgYmNmdG9vbHNbcmVmXTsgYW5kIGhpZ2gtY29uZmlkZW5jZSB2YXJpYW50cyAobW9yZSB0aGFuCjgwJSB3aXRoIGNvdmVyYWdlIGhpZ2hlciB0aGFuIDUgcmVhZHMgcGVyIHBvc2l0aW9uKSB3ZXJlIGV4dHJhY3RlZAppbnRvIGEgdGFibGUgdmFyaWFudHMgYXMgd2VsbCBhcyB1c2VkIHRvIG1vZGlmeSB0aGUgcmVmZXJlbmNlIGdlbm9tZQp0byByZXByZXNlbnQgdGhlc2UgZmlsdGVyZWQgdmFyaWFudHMuICBUaGVzZSBtb2RpZmllZCBnZW5vbWVzIHdlcmUKcGFzc2VkIHRvIGFwZVtyZWZdLCBpbmRleGVkLCBhbmQgdXNlZCB0byBjcmVhdGUgYSBrbWVyIGluZGV4IGFuZApkaXN0YW5jZSBtYXRyaXg7IHRoZXNlIGRpc3RhbmNlcyB3ZXJlIHVzZWQgYnkgYXBlIHRvIGNyZWF0ZSBhIG5laWdoYm9yCmpvaW5pbmcgdHJlZS4KCiMjIFNpbXBsaWZpZWQgdmVyc2lvbiBvZiB0aGUgYWJvdmUKCkkgd3JvdGUgYSBsaXR0bGUgZnVuY3Rpb24gd2hpY2ggaW4gdGhlb3J5IHNob3VsZCBtYWtlIHRoZSBhYm92ZSBhIGJpdApzaW1wbGVyIGFuZCBtb3JlIHJvYnVzdCBmb3IgZnV0dXJlIGFuYWx5c2VzLiAgTGV0cyBzZWUgaWYgaXQgd29ya3MuCkl0IGN1cnJlbnRseSB0YWtlcyBhIGRpcmVjdG9yeSBjb250YWluaW5nIHRoZSBmYXN0YSBmaWxlcyBvZiB0aGUKc2VxdWVuY2VzIHRvIGNvbXBhcmUgYW5kIGFuIG9wdGlvbmFsIHJvb3QuCgpUaGUgZm9sbG93aW5nIGJsb2NrIHJlcXVpcmVzIGNvcHlpbmcgYSBncm91cCBvZiBmYXN0YSBmaWxlcyBpbnRvIHRoZQonY29tcGFyZV9zdHJhaW5zLycgZGlyZWN0b3J5LCB3aGljaCBpbiBvdXIgY29udGFpbmVyIGltYWdlIGlzIG5vdAphdmFpbGFibGUsIHNvIHR1cm5pbmcgdGhpcyBvZmYuCgpPaywgc28gSSBhZGRlZCB0aGUgdmFyaW91cyBmYXN0YSBmaWxlcyB1c2VkIHRvIGNyZWF0ZSB0aGVzZSB0cmVlcyB0bwp0aGUgY29udGFpbmVyIGluIGNvbXByZXNzZWQgZm9ybS4gIEkgcHJvYmFibHkgc2hvdWxkIGhhdmUganVzdCBtYWRlIGEKdGFyYmFsbCwgYnV0IEkgd2FzIGNvbmNlcm5lZCB0aGF0IEkgd291bGQgbmVlZCB0byBjaGFuZ2Ugd2hpY2ggZmlsZXMKYXJlIGluY2x1ZGVkLCBzbyBkaWQgbm90LiAgVGh1cywgaWYgb25lIHdpc2hlcyB0byBwbGF5IHdpdGggdGhlCmZvbGxvd2luZyBibG9ja3MsIG9uZSB3aWxsIGZpcnN0IG5lZWQgdG8gcnVuOgoKTm90ZSB0aGF0IEkgaW5jbHVkZWQgdHdvIGNkIGNvbW1hbmRzIGJlY2F1c2Ugb25lIG1heSBnbyB0aGVyZSBmcm9tCm91dHNpZGUgb3IgaW5zaWRlIHRoZSBjb3BpZWQgY29udGFpbmVyIG91dHB1dCBkaXJlY3RvcnkuCgpgYGB7YmFzaCwgZXZhbD1GQUxTRX0KIyMgY2QgJHtPVVRQVVRfRElSfS9jb21wYXJlX3N0cmFpbnMKIyMgY2QgY29tcGFyZV9zdHJhaW5zCnh6IC1kICoueHoKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc3RyYWluX3RyZWUgPC0gZ2Vub21pY19rbWVyX2Rpc3QoImNvbXBhcmVfc3RyYWlucyIpCnBsb3Qoc3RyYWluX3RyZWUkcGh5bG8pCmBgYAoKIyBQaHlsb2dlbmV0aWMgYW5hbHlzaXMgb2YgZ2VuZXMgb2YgaW50ZXJlc3QKCkluIG9yZGVyIHRvIHBlcmZvcm0gdGhpcywgSSB3aWxsIHVzZSB0aGUgc2FtZSBmYXN0YSBmaWxlcywgYnV0IGV4dHJhY3QKdGhlIHp5bW9kZW1lIGdlbmVzIGZyb20gdGhlbSBhbmQgd3JpdGUgb3V0IGEgc2V0IG9mIGZhc3RhIGZpbGVzCmNvbnRhaW5pbmcgdGhlaXIgc2VxdWVuY2VzLiAgSSB0aGVyZWZvcmUgd3JvdGUgYSBmdW5jdGlvbiB3aGljaCB0YWtlcwppbiB0aGUgYW5ub3RhdGlvbiBkYXRhIGFuZCBmYXN0YSBmaWxlcyBpbiBvcmRlciB0byBleHRyYWN0IHRoZSBkYXRhIG9mCmludGVyZXN0LgoKU2FkbHksIEkgd2lsbCBuZWVkIHRvIHJlYWQgaW4gdGhlIGFubm90YXRpb25zIGZvcgpicmF6aWxpZW5zaXMvcGFuYW1lbnNpcyBwYW5hbWEgYW5kIGFueSBvdGhlciBzZXF1ZW5jZXMuICBCdXQgdGhlCnNlcXVlbmNlcyB3aGljaCBhcmUgZGlyZWN0bHkgZXh0cmFjdGVkIGZyb20gcGFuYW1lbnNpcyBjb2xvbWJpYSBJIHdpbGwKYmUgYWJsZSB0byB1c2UgdGhlIHNhbWUgYW5ub3RhdGlvbnMuCgpgYGB7ciwgZXZhbD1GQUxTRX0Kd2FudGVkX2lkcyA8LSBjKCJMUEFMMTNfMTIwMDEwOTAwIiwgIkxQQUwxM18zNDAwMTMwMDAiLCAiTFBBTDEzXzAwMDA1NDEwMCIsCiAgICAgICAgICAgICAgICAiTFBBTDEzXzE0MDAwNjEwMCIsICJMUEFMMTNfMTgwMDE4NTAwIiwgIkxQQUwxM18zMjAwMjIzMDAiKQpyZWZlcmVuY2UgPC0gd3JpdGVfY2RzX2VudHJpZXMoImNvbXBhcmVfc3RyYWlucy9scGFuYW1lbnNpc192MzYuZmFzdGEiLCBhbGxfbHBfYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZHMgPSB3YW50ZWRfaWRzLCBvdXRwdXQgPSAiY29tcGFyZV9zdHJhaW5zL2xwYW5hbWVuc2lzX2Nkcy5mYXN0YSIpCm1vZGlmaWVkXzEyNTg4IDwtIHdyaXRlX2Nkc19lbnRyaWVzKCJjb21wYXJlX3N0cmFpbnMvc3RyYWluXzEyNTg4X21vZGlmaWVkX3oyMS5mYXN0YSIsIGFsbF9scF9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9wcmVmaXggPSAiejIxIiwgaWRzID0gd2FudGVkX2lkcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0ID0gImNvbXBhcmVfc3RyYWlucy9scGFuYW1lbnNpc196MjFfY2RzLmZhc3RhIikKbW9kaWZpZWRfMjI3MiA8LSB3cml0ZV9jZHNfZW50cmllcygiY29tcGFyZV9zdHJhaW5zL3N0cmFpbl8yMjcyX21vZGlmaWVkX3oyMi5mYXN0YSIsIGFsbF9scF9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lX3ByZWZpeCA9ICJ6MjIiLCBpZHMgPSB3YW50ZWRfaWRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dCA9ICJjb21wYXJlX3N0cmFpbnMvbHBhbmFtZW5zaXNfejIyX2Nkcy5mYXN0YSIpCm1vZGlmaWVkXzIxNjggPC0gd3JpdGVfY2RzX2VudHJpZXMoImNvbXBhcmVfc3RyYWlucy9zdHJhaW5fMjE2OF9tb2RpZmllZF96MjMuZmFzdGEiLCBhbGxfbHBfYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9wcmVmaXggPSAiejIzIiwgaWRzID0gd2FudGVkX2lkcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXQgPSAiY29tcGFyZV9zdHJhaW5zL2xwYW5hbWVuc2lzX3oyM19jZHMuZmFzdGEiKQptb2RpZmllZF8xMjQ0NCA8LSB3cml0ZV9jZHNfZW50cmllcygiY29tcGFyZV9zdHJhaW5zL3N0cmFpbl8xMjQ0NF9tb2RpZmllZF96MjQuZmFzdGEiLCBhbGxfbHBfYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVfcHJlZml4ID0gInoyNCIsIGlkcyA9IHdhbnRlZF9pZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dCA9ICJjb21wYXJlX3N0cmFpbnMvbHBhbmFtZW5zaXNfejI0X2Nkcy5mYXN0YSIpCmBgYAoKSGF2aW5nIHdyaXR0ZW4gdGhlc2UgZmlsZXMsIEkgY29uY2F0ZW5hdGVkIHRoZSB6eW1vZGVtZSBDRFMgc2VxdWVuY2VzCmludG8gaW5kaXZpZHVhbCBzZXF1ZW5jZXMvc3RyYWluIGFuZCBwZXJmb3JtZWQgYSBNU0EgYW5kIE1QIHRyZWUgdXNpbmcKY2x1c3RhbG9bcmVmXSBhbmQgUGh5TUxbcmVmXSB2aWEgc2Vhdmlld1tyZWZdLiBTYWRseSwgdGhlcmUgd2VyZSBvbmx5CjEyIGluZm9ybWF0aXZlIHNpdGVzIGluIHRoZSA2IHp5bW9kZW1lIGRlZmluaW5nIGdlbmVzLiAgSGFwcGlseSwgdGhlCnRyZWUgZ2VuZXJhdGVkIGxvb2tzIHByZXR0eSBtdWNoIGV4YWN0bHkgbGlrZSBteSBnZW5vbWUtYmFzZWQgdHJlZS4KQWxzbywgSSBkaWRuJ3QgYm90aGVyIHRvIGFkZCB0aGUgb3RoZXIgZ2Vub21lcyBiZWNhdXNlIHdpdGggb25seSAxMgp2YXJpYW50IHBvc2l0aW9ucyBpdCBkaWQgbm90IGZlZWwgaW50ZXJlc3RpbmcuCgojIFNOUCBwcm9maWxlcwoKT3ZlciB0aGUgbGFzdCBjb3VwbGUgb2Ygd2Vla3MsIEkgcmVkaWQgYWxsIHRoZSB2YXJpYW50IHNlYXJjaGVzIHdpdGggYQpuZXdlciwgKEkgdGhpbmspIG1vcmUgc2Vuc2l0aXZlIGFuZCBtb3JlIHNwZWNpZmljIHZhcmlhbnQgdG9vbC4gIEluCmFkZGl0aW9uIEkgY2hhbmdlZCBteSBzY3JpcHQgd2hpY2ggaW50ZXJwcmV0cyB0aGUgcmVzdWx0cyBzbyB0aGF0IGl0CmlzIGFibGUgdG8gZXh0cmFjdCBhbnkgdGFncyBmcm9tIGl0LCBpbnN0ZWFkIG9mIGp1c3QgdGhlIG9uZSBvciB0d28KdGhhdCBteSBwcmV2aW91cyBzY3JpcHQgaGFuZGxlZC4gIEluIGFkZGl0aW9uLCBhdCBsZWFzdCBpbiB0aGVvcnkgaXQKaXMgbm93IGFibGUgdG8gcHJvdmlkZSB0aGUgc2V0IG9mIGFtaW5vIGFjaWQgc3Vic3RpdHV0aW9ucyBmb3IgZXZlcnkKZ2VuZSBpbiBzcGVjaWVzIHdpdGhvdXQgb3Igd2l0aCBpbnRyb25zIChub3QgcmVhbGx5IHJlbGV2YW50IGZvcgpMZWlzaG1hbmlhIHBhbmFtZW5zaXMpLgoKYGBge3IsIGV2YWw9RkFMU0V9CmJvdGhfbm9ybSA8LSBzZXRfZXhwdF9jb25kaXRpb25zKGJvdGhfc25wcywgZmFjdCA9ICJrbm52MmNsYXNzaWZpY2F0aW9uIikKCiMjIHN0cmFpbnMgPC0gYm90aF9ub3JtW1siZGVzaWduIl1dW1sic3RyYWluIl1dCmJvdGhfc3RyYWluIDwtIHNldF9leHB0X2NvbmRpdGlvbnMoYm90aF9ub3JtLCBmYWN0ID0gInN0cmFpbiIpCmBgYAoKVGhlIGRhdGEgc3RydWN0dXJlICdib3RoX25vcm0nIG5vdyBjb250YWlucyBvdXIgMjAxNiBkYXRhIGFsb25nIHdpdGgKdGhlIG5ld2VyIGRhdGEgY29sbGVjdGVkIHNpbmNlIDIwMTkuCgojIyBQbG90IG9mIFNOUCBwcm9maWxlcyBmb3Igenltb2RlbWVzCgpUaGUgZm9sbG93aW5nIHBsb3Qgc2hvd3MgdGhlIFNOUCBwcm9maWxlcyBvZiBhbGwgc2FtcGxlcyAob2xkIGFuZCBuZXcpIHdoZXJlIHRoZQpjb2xvcnMgYXQgdGhlIHRvcCBzaG93IGVpdGhlciB0aGUgMi4yIHN0cmFpbnMgKG9yYW5nZSksIDIuMyBzdHJhaW5zIChncmVlbiksIHRoZQpwcmV2aW91cyBzYW1wbGVzIChwdXJwbGUpLCBvciB0aGUgdmFyaW91cyBsYWIgc3RyYWlucyAocGluayBldGMpLgoKYGBge3J9Cm5ld192YXJpYW50X2hlYXRtYXAgPC0gcGxvdF9jb3JoZWF0KG5ld19zbnBzKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvcmF3X3NucF9jb3JoZWF0LnBuZyIsIGhlaWdodCA9IDEyLCB3aWR0aCA9IDEyKQpuZXdfdmFyaWFudF9oZWF0bWFwJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQpuZXdfdmFyaWFudF9oZWF0bWFwJHBsb3QKYGBgCgpUaGUgZnVuY3Rpb24gZ2V0X3NucF9zZXRzKCkgdGFrZXMgdGhlIHByb3ZpZGVkIG1ldGFkYXRhIGZhY3RvciAoaW4KdGhpcyBjYXNlICdjb25kaXRpb24nKSBhbmQgbG9va3MgZm9yIHZhcmlhbnRzIHdoaWNoIGFyZSBleGNsdXNpdmUgdG8KZWFjaCBlbGVtZW50IGluIGl0LiAgSW4gdGhpcyBjYXNlLCB0aGlzIGlzIGxvb2tpbmcgZm9yIGRpZmZlcmVuY2VzCmJldHdlZW4gMi4yIGFuZCAyLjMsIGFzIHdlbGwgYXMgdGhlIHNldCBzaGFyZWQgYW1vbmcgdGhlbS4KCmBgYHtyLCBldmFsPUZBTFNFfQpzbnBfc2V0cyA8LSBnZXRfc25wX3NldHMoYm90aF9zbnBzLCBmYWN0b3IgPSAia25uaGNsdXN0dG9nZXRoZXJjYWxsIikKc25wX3NldHMKQmlvYmFzZTo6YW5ub3RhdGlvbihscF9wcmV2aW91cyRleHByZXNzaW9uc2V0KSA8LSBCaW9iYXNlOjphbm5vdGF0aW9uKGxwX2V4cHQkZXhwcmVzc2lvbnNldCkKbHBfa25uIDwtIHNldF9leHB0X2NvbmRpdGlvbnMobHBfZXhwdCwgZmFjdCA9ICJrbm5oY2x1c3R0b2dldGhlcmNhbGwiKQpib3RoX2V4cHQgPC0gY29tYmluZV9leHB0cyhscF9rbm4sIGxwX3ByZXZpb3VzKQoKc25wX2dlbmVzIDwtIHNucHNfdnNfZ2VuZXMoYm90aF9leHB0LCBzbnBfc2V0cywgZXhwdF9uYW1lX2NvbCA9ICJjaHJvbW9zb21lIikKCiMjIEkgdGhpbmsgd2UgaGF2ZSBzb21lIG1ldHJpY3MgaGVyZSB3ZSBjYW4gcGxvdC4uLnMKc25wX3N1YnNldCA8LSBzbnBfc3Vic2V0X2dlbmVzKAogIGJvdGhfZXhwdCwgYm90aF9zbnBzLAogIGdlbmVzID0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAiTFBBTDEzXzE0MDAwNjEwMCIsICJMUEFMMTNfMTgwMDE4NTAwIiwgIkxQQUwxM18zMjAwMjIzMDAiKSkKenltb19oZWF0IDwtIHBsb3Rfc2FtcGxlX2hlYXRtYXAoCiAgbm9ybWFsaXplX2V4cHQoc25wX3N1YnNldCwgdHJhbnNmb3JtID0gImxvZzIiKSwKICByb3dfbGFiZWwgPSByb3duYW1lcyhleHBycyhzbnBfc3Vic2V0KSkpCnp5bW9faGVhdAoKbW9zdF92YXJpYW50IDwtIGhlYWQoc25wX2dlbmVzJGNvdW50X2J5X2dlbmUsIG4gPSAxMDApCmxlYXN0X3ZhcmlhbnQgPC0gdGFpbChzbnBfZ2VuZXMkY291bnRfYnlfZ2VuZSwgbiA9IDEwMCkKdGVzdCA8LSBzaW1wbGVfZ29zZXEobmFtZXMobW9zdF92YXJpYW50KSwgZ29fZGIgPSBscF9nbywgbGVuZ3RoX2RiID0gbHBfbGVuZ3RocykKYGBgCgojIyBDb21wYXJlIHZhcmlhbnRzIHRvIERFIGdlbmVzCgpOYWppYiBoYXMgYXNrZWQgYSBmZXcgdGltZXMgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhbnRzCmFuZCBERSBnZW5lcy4gIEluIHN1YnNlcXVlbnQgY29udmVyc2F0aW9ucyBJIGZpZ3VyZWQgb3V0IHdoYXQgaGUKcmVhbGx5IHdhbnRzIHRvIGxlYXJuIGlzIHZhcmlhbnRzIGluIHRoZSBVVFIgKG1vc3QgbGlrZWx5IDUnKSB3aGljaAptaWdodCBhZmZlY3QgZXhwcmVzc2lvbiBvZiBnZW5lcy4gIFRoZSBmb2xsb3dpbmcgZXhwbGljaXRseSBkb2VzIG5vdApoZWxwIHRoaXMgcXVlc3Rpb24sIGJ1dCBpcyBhIHBhcmFsb2c6IGlzIHRoZXJlIGEgcmVsYXRpb25zaGlwIGJldHdlZW4KdmFyaWFudHMgaW4gdGhlIENEUyBhbmQgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24/CgojIyMgQ29sbGVjdCBERSBkYXRhCgpJbiBvcmRlciB0byBkbyB0aGlzIGNvbXBhcmlzb24sIHdlIG5lZWQgdG8gcmVsb2FkIHNvbWUgb2YgdGhlIERFIHJlc3VsdHMuCgpgYGB7cn0KcmRhIDwtIGdsdWUoInJkYS96eW1vX3RhYmxlc19ub2JhdGNoLXZ7dmVyfS5yZGEiKQp2YXJuYW1lIDwtIGdzdWIoeCA9IGJhc2VuYW1lKHJkYSksIHBhdHRlcm4gPSAiXFwucmRhIiwgcmVwbGFjZW1lbnQgPSAiIikKbG9hZGVkIDwtIGxvYWQoZmlsZSA9IHJkYSkKenlfZGYgPC0gZ2V0MCh2YXJuYW1lKVtbImRhdGEiXV1bWyJ6eW1vZGVtZSJdXQoKcmRhIDwtIGdsdWUoInJkYS9zdXNfdGFibGVzX25vYmF0Y2gtdnt2ZXJ9LnJkYSIpCnZhcm5hbWUgPC0gZ3N1Yih4ID0gYmFzZW5hbWUocmRhKSwgcGF0dGVybiA9ICJcXC5yZGEiLCByZXBsYWNlbWVudCA9ICIiKQpsb2FkZWQgPC0gbG9hZChmaWxlID0gcmRhKQpzdXNfZGYgPC0gZ2V0MCh2YXJuYW1lKVtbImRhdGEiXV1bWyJyZXNpc3RhbnRfc2Vuc2l0aXZlIl1dCmBgYAoKIyBDcmVhdGUgYSB0YWJsZSBvZiB2YXJpYW50cyBieSBnZW5lCgpJbiB0aGUgcHJldmlvdXMgc2VjdGlvbiwgSSBkaWQgdGhpcyBwcm9jZXNzIHVzaW5nIG9sZGVyIGFuZCBuZXdlciBkYXRhCihhbmQgZGlzYWJsZWQgaXQgYmVjYXVzZSB3ZSBkbyBub3QgaGF2ZSBhbGwgb2YgdGhhdCBkYXRhIGF2YWlsYWJsZSBmb3IKdGhlIGNvbnRhaW5lcjsgYnV0IEkgc3RpbGwgd2FudCB0byBkbyBzb21lIG9mIHRoZXNlIGNvbXBhcmlzb25zIGFuZCBzbwp3aWxsIGRvIHRoZW0gdXNpbmcgb25seSB0aGUgY3VycmVudC9uZXcgdHJlZS4KCmBgYHtyfQpzbnBfc2V0cyA8LSBnZXRfc25wX3NldHMobmV3X3NucHMsIGZhY3RvciA9ICJrbm5oY2x1c3R0b2dldGhlcmNhbGwiKQpzbnBfc2V0cwpscF9rbm4gPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhscF9leHB0LCBmYWN0ID0gImtubmhjbHVzdHRvZ2V0aGVyY2FsbCIpCgpzbnBfZ2VuZXMgPC0gc25wc192c19nZW5lcyhscF9rbm4sIHNucF9zZXRzLCBleHB0X25hbWVfY29sID0gImNocm9tb3NvbWUiKQpgYGAKCmBgYHtyfQp2YXJzX2RmIDwtIGRhdGEuZnJhbWUoSUQgPSBuYW1lcyhzbnBfZ2VuZXNbWyJjb3VudF9ieV9nZW5lIl1dKSwKICAgICAgICAgICAgICAgICAgICAgIHZhcmlhbnRzID0gYXMubnVtZXJpYyhzbnBfZ2VuZXNbWyJjb3VudF9ieV9nZW5lIl1dKSkKdmFyc19kZiA8LSBtZXJnZSh2YXJzX2RmLCBscF9sZW5ndGhzLCBieSA9ICJJRCIpCnZhcnNfZGZbWyJsZW5ndGgiXV0gPC0gYXMubnVtZXJpYyh2YXJzX2RmW1sibGVuZ3RoIl1dKQp2YXJzX2RmW1sidmFyX2xlbiJdXSA8LSB2YXJzX2RmW1sidmFyaWFudHMiXV0gLyB2YXJzX2RmW1sibGVuZ3RoIl1dCnZhcnNfYnlfZGVfZ2VuZSA8LSBtZXJnZSh6eV9kZiwgdmFyc19kZiwgYnkueD0icm93Lm5hbWVzIiwgYnkueT0iSUQiKQpyb3duYW1lcyh2YXJzX2J5X2RlX2dlbmUpIDwtIHZhcnNfYnlfZGVfZ2VuZVtbIlJvdy5uYW1lcyJdXQp2YXJzX2J5X2RlX2dlbmVbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApjb3IudGVzdCh2YXJzX2J5X2RlX2dlbmUkZGVzZXFfbG9nZmMsIHZhcnNfYnlfZGVfZ2VuZSR2YXJfbGVuKQp2YXJpYW50c193cnRfbG9nZmMgPC0gcGxvdF9saW5lYXJfc2NhdHRlcih2YXJzX2J5X2RlX2dlbmUsIHhjb2wgPSAiZGVzZXFfbG9nZmMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5Y29sID0gInZhcl9sZW4iLCB0ZXh0X2NvbCA9ICJhbm5vdGdlbmVwcm9kdWN0IikKc2NhdHRlciA8LSB2YXJpYW50c193cnRfbG9nZmMkc2NhdHRlcgoKcGxvdGx5OjpnZ3Bsb3RseShzY2F0dGVyLCB0b29sdGlwID0gYygieCIsICJ5IiwgInRleHQiKSkKIyMgSXQgbG9va3MgbGlrZSB0aGVyZSBtaWdodCBiZSBzb21lIGdlbmVzIG9mIGludGVyZXN0LCBldmVuIHRob3VnaCB0aGlzIGlzIG5vdCBhY3R1YWxseQojIyB0aGUgcXVlc3Rpb24gb2YgaW50ZXJlc3QuCmBgYAoKT2ssIEkgdGhpbmsgSSBjYW4gZG8gdGhpcyBvbiBhIFVUUiBiYXNpcy4KCmBgYHtyLCBldmFsPUZBTFNFfQpzbnBfdXRycyA8LSBzbnBzX3ZzX2dlbmVzX3BhZGRlZChib3RoX2V4cHQsIHNucF9zZXRzLCBleHB0X25hbWVfY29sID0gImNocm9tb3NvbWUiKQoKZml2ZXBfdmFyc19kZiA8LSBkYXRhLmZyYW1lKElEID0gbmFtZXMoc25wX3V0cnNbWyJjb3VudF9maXZlcF9ieV9nZW5lIl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhbnRzID0gYXMubnVtZXJpYyhzbnBfdXRyc1tbImNvdW50X2ZpdmVwX2J5X2dlbmUiXV0pKQpmaXZlcF92YXJzX2J5X2RlX2dlbmUgPC0gbWVyZ2UoenlfZGYsIGZpdmVwX3ZhcnNfZGYsIGJ5Lng9InJvdy5uYW1lcyIsIGJ5Lnk9IklEIikKcm93bmFtZXMoZml2ZXBfdmFyc19ieV9kZV9nZW5lKSA8LSBmaXZlcF92YXJzX2J5X2RlX2dlbmVbWyJSb3cubmFtZXMiXV0KZml2ZXBfdmFyc19ieV9kZV9nZW5lW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKY29yLnRlc3QoZml2ZXBfdmFyc19ieV9kZV9nZW5lJGRlc2VxX2xvZ2ZjLCBmaXZlcF92YXJzX2J5X2RlX2dlbmVbWyJ2YXJpYW50cyJdXSkKZml2ZXBfdmFyaWFudHNfd3J0X2xvZ2ZjIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIoZml2ZXBfdmFyc19ieV9kZV9nZW5lLCB4Y29sID0gImRlc2VxX2xvZ2ZjIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWNvbCA9ICJ2YXJpYW50cyIsIHRleHRfY29sID0gImFubm90Z2VuZXByb2R1Y3QiKQpzY2F0dGVyIDwtIGZpdmVwX3ZhcmlhbnRzX3dydF9sb2dmYyRzY2F0dGVyCnNjYXR0ZXIKCnBsb3RseTo6Z2dwbG90bHkoc2NhdHRlciwgdG9vbHRpcCA9IGMoIngiLCAieSIsICJ0ZXh0IikpCgp0aHJlZXBfdmFyc19kZiA8LSBkYXRhLmZyYW1lKElEID0gbmFtZXMoc25wX3V0cnNbWyJjb3VudF90aHJlZXBfYnlfZ2VuZSJdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYW50cyA9IGFzLm51bWVyaWMoc25wX3V0cnNbWyJjb3VudF90aHJlZXBfYnlfZ2VuZSJdXSkpCnRocmVlcF92YXJzX2J5X2RlX2dlbmUgPC0gbWVyZ2UoenlfZGYsIHRocmVlcF92YXJzX2RmLCBieS54PSJyb3cubmFtZXMiLCBieS55PSJJRCIpCnJvd25hbWVzKHRocmVlcF92YXJzX2J5X2RlX2dlbmUpIDwtIHRocmVlcF92YXJzX2J5X2RlX2dlbmVbWyJSb3cubmFtZXMiXV0KdGhyZWVwX3ZhcnNfYnlfZGVfZ2VuZVtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmNvci50ZXN0KHRocmVlcF92YXJzX2J5X2RlX2dlbmUkZGVzZXFfbG9nZmMsIHRocmVlcF92YXJzX2J5X2RlX2dlbmVbWyJ2YXJpYW50cyJdXSkKdGhyZWVwX3ZhcmlhbnRzX3dydF9sb2dmYyA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKHRocmVlcF92YXJzX2J5X2RlX2dlbmUsIHhjb2wgPSAiZGVzZXFfbG9nZmMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5Y29sID0gInZhcmlhbnRzIiwgdGV4dF9jb2wgPSAiYW5ub3RnZW5lcHJvZHVjdCIpCnNjYXR0ZXIgPC0gdGhyZWVwX3ZhcmlhbnRzX3dydF9sb2dmYyRzY2F0dGVyCgpwbG90bHk6OmdncGxvdGx5KHNjYXR0ZXIsIHRvb2x0aXAgPSBjKCJ4IiwgInkiLCAidGV4dCIpKQpgYGAKCk9uY2UgYWdhaW4sIGxldCB1cyBkbyB0aGlzIHdpdGggb25seSB0aGUgbmV3IGRhdGEuCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc25wX3V0cnMgPC0gc25wc192c19nZW5lc19wYWRkZWQobHBfZXhwdCwgc25wX3NldHMsIGV4cHRfbmFtZV9jb2wgPSAiY2hyb21vc29tZSIpCgpmaXZlcF92YXJzX2RmIDwtIGRhdGEuZnJhbWUoSUQgPSBuYW1lcyhzbnBfdXRyc1tbImNvdW50X2ZpdmVwX2J5X2dlbmUiXV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFudHMgPSBhcy5udW1lcmljKHNucF91dHJzW1siY291bnRfZml2ZXBfYnlfZ2VuZSJdXSkpCmZpdmVwX3ZhcnNfYnlfZGVfZ2VuZSA8LSBtZXJnZSh6eV9kZiwgZml2ZXBfdmFyc19kZiwgYnkueD0icm93Lm5hbWVzIiwgYnkueT0iSUQiKQpyb3duYW1lcyhmaXZlcF92YXJzX2J5X2RlX2dlbmUpIDwtIGZpdmVwX3ZhcnNfYnlfZGVfZ2VuZVtbIlJvdy5uYW1lcyJdXQpmaXZlcF92YXJzX2J5X2RlX2dlbmVbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApjb3IudGVzdChmaXZlcF92YXJzX2J5X2RlX2dlbmUkZGVzZXFfbG9nZmMsIGZpdmVwX3ZhcnNfYnlfZGVfZ2VuZVtbInZhcmlhbnRzIl1dKQpmaXZlcF92YXJpYW50c193cnRfbG9nZmMgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihmaXZlcF92YXJzX2J5X2RlX2dlbmUsIHhjb2wgPSAiZGVzZXFfbG9nZmMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5Y29sID0gInZhcmlhbnRzIiwgdGV4dF9jb2wgPSAiYW5ub3RnZW5lcHJvZHVjdCIpCnNjYXR0ZXIgPC0gZml2ZXBfdmFyaWFudHNfd3J0X2xvZ2ZjJHNjYXR0ZXIKc2NhdHRlcgoKcGxvdGx5OjpnZ3Bsb3RseShzY2F0dGVyLCB0b29sdGlwID0gYygieCIsICJ5IiwgInRleHQiKSkKCnRocmVlcF92YXJzX2RmIDwtIGRhdGEuZnJhbWUoSUQgPSBuYW1lcyhzbnBfdXRyc1tbImNvdW50X3RocmVlcF9ieV9nZW5lIl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhbnRzID0gYXMubnVtZXJpYyhzbnBfdXRyc1tbImNvdW50X3RocmVlcF9ieV9nZW5lIl1dKSkKdGhyZWVwX3ZhcnNfYnlfZGVfZ2VuZSA8LSBtZXJnZSh6eV9kZiwgdGhyZWVwX3ZhcnNfZGYsIGJ5Lng9InJvdy5uYW1lcyIsIGJ5Lnk9IklEIikKcm93bmFtZXModGhyZWVwX3ZhcnNfYnlfZGVfZ2VuZSkgPC0gdGhyZWVwX3ZhcnNfYnlfZGVfZ2VuZVtbIlJvdy5uYW1lcyJdXQp0aHJlZXBfdmFyc19ieV9kZV9nZW5lW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKY29yLnRlc3QodGhyZWVwX3ZhcnNfYnlfZGVfZ2VuZSRkZXNlcV9sb2dmYywgdGhyZWVwX3ZhcnNfYnlfZGVfZ2VuZVtbInZhcmlhbnRzIl1dKQp0aHJlZXBfdmFyaWFudHNfd3J0X2xvZ2ZjIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIodGhyZWVwX3ZhcnNfYnlfZGVfZ2VuZSwgeGNvbCA9ICJkZXNlcV9sb2dmYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHljb2wgPSAidmFyaWFudHMiLCB0ZXh0X2NvbCA9ICJhbm5vdGdlbmVwcm9kdWN0IikKc2NhdHRlciA8LSB0aHJlZXBfdmFyaWFudHNfd3J0X2xvZ2ZjJHNjYXR0ZXIKCnBsb3RseTo6Z2dwbG90bHkoc2NhdHRlciwgdG9vbHRpcCA9IGMoIngiLCAieSIsICJ0ZXh0IikpCmBgYAoKRGlkbid0IEkgY3JlYXRlIGEgc2V0IG9mIGRlbnNpdGllcyBieSBjaHJvbW9zb21lPwpPaCBJIHRoaW5rIHRoZXkgY29tZSBpbiBmcm9tIGdldF9zbnBfc2V0cygpCgojIyBTTlBTIGFzc29jaWF0ZWQgd2l0aCBjbGluaWNhbCByZXNwb25zZSBpbiB0aGUgVE1SQyBzYW1wbGVzCgpgYGB7cn0KY2xpbmljYWxfc2V0cyA8LSBnZXRfc25wX3NldHMobmV3X3NucHMsIGZhY3RvciA9ICJjbGluaWNhbHJlc3BvbnNlIikKY2xpbmljYWxfc2V0cwoKZGVuc2l0eV92ZWMgPC0gY2xpbmljYWxfc2V0c1tbImRlbnNpdHkiXV0KY2hyb21vc29tZV9pZHggPC0gZ3JlcChwYXR0ZXJuID0gIkxwYUwiLCB4ID0gbmFtZXMoZGVuc2l0eV92ZWMpKQpkZW5zaXR5X2RmIDwtIGFzLmRhdGEuZnJhbWUoZGVuc2l0eV92ZWNbY2hyb21vc29tZV9pZHhdKQpkZW5zaXR5X2RmW1siY2hyIl1dIDwtIHJvd25hbWVzKGRlbnNpdHlfZGYpCmNvbG5hbWVzKGRlbnNpdHlfZGYpIDwtIGMoImRlbnNpdHlfdmVjIiwgImNociIpCiMjIEkgYW0gYmFkIHdpdGggcXVhc2lxdW90YXRpb24gYW5kIHRoZSB2YXJpb3VzIG5vbi1zdGFuZGFyZCBldmFsdWF0aW9uIHRlY2huaXF1ZXMgb2YgdGlkeXIuCiMjIExldCB1cyB1c2UgdGhlIGZvbGxvd2luZyBwbG90IHRvIHNlZSBpZiBJIHJlbWVtYmVyIGhvdyB0byB1c2UgYmFuZ2JhbmcgdG8gcmVwbGFjZSBhIE5TRQojIyBuYW1lIHdpdGggYSB2YXJpYWJsZSwgaW4gdGhpcyBjYXNlIHRoZSBjcmVhdGl2ZWx5IG5hbWVkICd0ZXN0JwojIyBJIHJhdGhlciB0aGluayB0aGlzIGlzIGdyb3NzIGFuZCBnZW5lcmFsbHkgcHJlZmVyIHRvIGp1c3QgdXNlIC5kYXRhCnRlc3QgPC0gc3ltKCJjaHIiKQpnZ3Bsb3QoZGVuc2l0eV9kZiwgYWVzKHggPSAhIXRlc3QsIHkgPSBkZW5zaXR5X3ZlYykpICsKICBnZ3Bsb3QyOjpnZW9tX2NvbCgpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvdXIgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpCiMjIGNsaW5pY2FsX3dyaXR0ZW4gPC0gd3JpdGVfc25wcyhuZXdfc25wcywgb3V0cHV0X2ZpbGUgPSAiY2xpbmljYWwuYWxuIikKCiMjIEluIG15IHNhbXBsZSBzaGVldCwgSSBkaWQgbm90IHJlY29yZCB0aGUgdmFyaWFudHMgYnkgZ2VuZSBvciBhIGZldyBvdGhlciBkYXRhIHdoaWNoIHdlcmUKIyMgY29sbGVjdGVkCnRlc3Rfc3BlYyA8LSBtYWtlX2RuYXNlcV9zcGVjKCkKbmV3X21ldGEgPC0gZ2F0aGVyX3ByZXByb2Nlc3NpbmdfbWV0YWRhdGEocERhdGEobmV3X3NucHMpLCBzcGVjaWZpY2F0aW9uID0gdGVzdF9zcGVjKQp0ZXN0IDwtIGNsYXNzaWZ5X3ZhcmlhbnRzKG5ld19tZXRhW1sibmV3X21ldGEiXV0sIGNvdmVyYWdlX2NvbHVtbiA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFudHNfY29sdW1uID0gImZyZWViYXllc192YXJpYW50c19ieV9nZW5lIikKY29sdW1uX2NvbG9ycyA8LSBwRGF0YShuZXdfc25wcylbWyJrbm52MmNsYXNzaWZpY2F0aW9uIl1dCnJlZF9pZHggPC0gY29sdW1uX2NvbG9ycyA9PSAiejIyIgpjb2x1bW5fY29sb3JzW3JlZF9pZHhdIDwtICJyZWQiCnBpbmtfaWR4IDwtIGNvbHVtbl9jb2xvcnMgPT0gInoyMSIKY29sdW1uX2NvbG9yc1twaW5rX2lkeF0gPC0gInNhbG1vbiIKYmx1ZV9pZHggPC0gY29sdW1uX2NvbG9ycyA9PSAiejIzIgpjb2x1bW5fY29sb3JzW2JsdWVfaWR4XSA8LSAiZGFya2JsdWUiCmJsdWVfaWR4IDwtIGNvbHVtbl9jb2xvcnMgPT0gInoyNCIKY29sdW1uX2NvbG9yc1tibHVlX2lkeF0gPC0gImNvcm5mbG93ZXJibHVlIgpncGxvdHM6OmhlYXRtYXAuMihhcy5tYXRyaXgodGVzdCRtdXRhdGlvbnNfYnlfc2FtcGxlX25vcm0pLCB0cmFjZSA9ICJub25lIiwKICAgICAgICAgICAgICAgICAgQ29sU2lkZUNvbG9ycyA9IGNvbHVtbl9jb2xvcnMpCmBgYAoKIyMjIENyb3NzIHJlZmVyZW5jZSB0aGVzZSB2YXJpYW50cyBieSBnZW5lCgpgYGB7cn0KY2xpbmljYWxfZ2VuZXMgPC0gc25wc192c19nZW5lcyhscF9leHB0LCBjbGluaWNhbF9zZXRzLCBleHB0X25hbWVfY29sID0gImNocm9tb3NvbWUiKQpjbGluaWNhbF9nZW5lcwoKc25wX2RlbnNpdHkgPC0gbWVyZ2UoYXMuZGF0YS5mcmFtZShjbGluaWNhbF9nZW5lc1tbImNvdW50X2J5X2dlbmUiXV0pLAogICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKGZEYXRhKGxwX2V4cHQpKSwKICAgICAgICAgICAgICAgICAgICAgYnkgPSAicm93Lm5hbWVzIikKc25wX2RlbnNpdHkgPC0gc25wX2RlbnNpdHlbLCBjKDEsIDIsIDQsIDE1KV0KY29sbmFtZXMoc25wX2RlbnNpdHkpIDwtIGMoIm5hbWUiLCAic25wcyIsICJwcm9kdWN0IiwgImxlbmd0aCIpCnNucF9kZW5zaXR5W1sicHJvZHVjdCJdXSA8LSB0b2xvd2VyKHNucF9kZW5zaXR5W1sicHJvZHVjdCJdXSkKc25wX2RlbnNpdHlbWyJsZW5ndGgiXV0gPC0gYXMubnVtZXJpYyhzbnBfZGVuc2l0eVtbImxlbmd0aCJdXSkKc25wX2RlbnNpdHlbWyJkZW5zaXR5Il1dIDwtIHNucF9kZW5zaXR5W1sic25wcyJdXSAvIHNucF9kZW5zaXR5W1sibGVuZ3RoIl1dCnNucF9pZHggPC0gb3JkZXIoc25wX2RlbnNpdHlbWyJkZW5zaXR5Il1dLCBkZWNyZWFzaW5nID0gVFJVRSkKc25wX2RlbnNpdHkgPC0gc25wX2RlbnNpdHlbc25wX2lkeCwgXQoKcmVtb3ZlcnMgPC0gYygiYW1hc3RpbiIsICJncDYzIiwgImxlaXNobWFub2x5c2luIikKCmZvciAociBpbiByZW1vdmVycykgewogIGRyb3BfaWR4IDwtIGdyZXBsKHBhdHRlcm4gPSByLCB4ID0gc25wX2RlbnNpdHlbWyJwcm9kdWN0Il1dKQogIHNucF9kZW5zaXR5IDwtIHNucF9kZW5zaXR5WyFkcm9wX2lkeCwgXQp9CiMjIEZpbHRlciB0aGVzZSBmb3IgW0F8YV1tYXN0aW4gZ3A2MyBMZWlzaG1hbm9seXNpbgpgYGAKCgpgYGB7cn0KY2xpbmljYWxfc25wcyA8LSBzbnBzX2ludGVyc2VjdGlvbnMobHBfZXhwdCwgY2xpbmljYWxfc2V0cywgY2hyX2NvbHVtbiA9ICJjaHJvbW9zb21lIikKCmZhaWxfcmVmX3NucHMgPC0gYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siaW50ZXJzIl1dW1siZmFpbHVyZSwgcmVmZXJlbmNlIHN0cmFpbiJdXSkKZmFpbF9yZWZfc25wcyA8LSByYmluZChmYWlsX3JlZl9zbnBzLAogICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfc25wc1tbImludGVycyJdXVtbImZhaWx1cmUiXV0pKQpjdXJlX3NucHMgPC0gYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siaW50ZXJzIl1dW1siY3VyZSJdXSkKCmhlYWQoZmFpbF9yZWZfc25wcykKaGVhZChjdXJlX3NucHMpCndyaXRlLmNzdihmaWxlPSJjc3YvY3VyZV92YXJpYW50cy50eHQiLCB4PXJvd25hbWVzKGN1cmVfc25wcykpCndyaXRlLmNzdihmaWxlPSJjc3YvZmFpbF92YXJpYW50cy50eHQiLCB4PXJvd25hbWVzKGZhaWxfcmVmX3NucHMpKQoKYW5ub3QgPC0gZkRhdGEobHBfZXhwdCkKY2xpbmljYWxfaW50ZXJlc3QgPC0gYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siZ2VuZV9zdW1tYXJpZXMiXV1bWyJjdXJlIl1dKQpjbGluaWNhbF9pbnRlcmVzdCA8LSBtZXJnZShjbGluaWNhbF9pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siZ2VuZV9zdW1tYXJpZXMiXV1bWyJmYWlsdXJlLCByZWZlcmVuY2Ugc3RyYWluIl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMoY2xpbmljYWxfaW50ZXJlc3QpIDwtIGNsaW5pY2FsX2ludGVyZXN0W1siUm93Lm5hbWVzIl1dCmNsaW5pY2FsX2ludGVyZXN0W1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKY29sbmFtZXMoY2xpbmljYWxfaW50ZXJlc3QpIDwtIGMoImN1cmVfc25wcyIsImZhaWxfc25wcyIpCmFubm90IDwtIG1lcmdlKGFubm90LCBjbGluaWNhbF9pbnRlcmVzdCwgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMoYW5ub3QpIDwtIGFubm90W1siUm93Lm5hbWVzIl1dCmFubm90W1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKZkRhdGEobHBfZXhwdCRleHByZXNzaW9uc2V0KSA8LSBhbm5vdApgYGAKCiMgWnltb2RlbWUgZm9yIG5ldyBzYW1wbGVzCgpUaGUgaGVhdG1hcCBwcm9kdWNlZCBoZXJlIHNob3VsZCBzaG93IHRoZSB2YXJpYW50cyBvbmx5IGZvciB0aGUgenltb2RlbWUgZ2VuZXMuCgojIyBIdW50IGZvciBzbnAgY2x1c3RlcnMKCkkgYW0gdGhpbmtpbmcgdGhhdCBpZiB3ZSBmaW5kIGNsdXN0ZXJzIG9mIGxvY2F0aW9ucyB3aGljaCBhcmUgdmFyaWFudCwgdGhhdAptaWdodCBwcm92aWRlIHNvbWUgUENSIHRlc3RpbmcgcG9zc2liaWxpdGllcy4KCmBgYHtyIG5ld196eW1vLCBldmFsPUZBTFNFfQojIyBEcm9wIHRoZSAyLjEsIDIuNCwgdW5rbm93biwgYW5kIG51bGwKa25uX3NucHMgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhuZXdfc25wcywgZmFjdCA9ICJrbm52MmNsYXNzaWZpY2F0aW9uIikgJT4lCiAgc3Vic2V0X2V4cHQoc3Vic2V0PSJjb25kaXRpb249PSd6MjInfGNvbmRpdGlvbj09J3oyMyciKQprbm5fc2V0cyA8LSBnZXRfc25wX3NldHMoa25uX3NucHMsIGZhY3RvciA9ICJrbm52MmNsYXNzaWZpY2F0aW9uIikKa25uX3NldHMKc3VtbWFyeShrbm5fc2V0cykKIyMgMTAwMDAwMDogMi4yCiMjIDAxMDAwMDA6IDIuMwoKcHJ1bmVkX3NucHMgPC0gc3Vic2V0X2V4cHQobmV3X3NucHMsIHN1YnNldD0iY29uZGl0aW9uPT0nejIuMid8Y29uZGl0aW9uPT0nejIuMyciKQpuZXdfc2V0cyA8LSBnZXRfc25wX3NldHMocHJ1bmVkX3NucHMsIGZhY3RvciA9ICJjb25kaXRpb24iKQpuZXdfc2V0cwoKc3VtbWFyeShuZXdfc2V0c1tbImludGVyc2VjdGlvbnMiXV1bWyIxMCJdXSkKd3JpdGUuY3N2KGZpbGU9ImNzdi92YXJpYW50c18yMi5jc3YiLCB4PW5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjEwIl1dKQpzdW1tYXJ5KG5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjAxIl1dKQp3cml0ZS5jc3YoZmlsZT0iY3N2L3ZhcmlhbnRzXzIzLmNzdiIsIHg9bmV3X3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dW1siMDEiXV0pCgpzdW1tYXJ5KGtubl9zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjEwIl1dKQp3cml0ZS5jc3YoZmlsZT0iY3N2L2tubl92YXJpYW50c18yMi5jc3YiLCB4PW5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjEwIl1dKQpzdW1tYXJ5KGtubl9zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjAxIl1dKQp3cml0ZS5jc3YoZmlsZT0iY3N2L2tubl92YXJpYW50c18yMy5jc3YiLCB4PW5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjAxIl1dKQpgYGAKClRodXMgd2Ugc2VlIHRoYXQgdGhlcmUgYXJlIDYwMSB2YXJpYW50cyBhc3NvY2lhdGVkIHdpdGggMi4yIGFuZCA2NywxNzEgYXNzb2NpYXRlZCB3aXRoIDIuMy4KCiMjIyBTZWFyY2ggZm9yIFBDUiBwcmltZXJzCgpUaGUgc2VxdWVudGlhbF92YXJpYW50cygpIGZ1bmN0aW9uIHNlYXJjaGVzIGZvciB2YXJpYW50cyB3aGljaCBhcmUKY2x1c3RlcmVkIGNsb3NlIHRvZ2V0aGVyIGluIHRoZSBob3BlcyB0aGF0IGNob29zaW5nIFBDUiBwcmltZXJzCmZvY3VzZWQgb24gdGhpcyBwb3NpdGlvbnMgd2lsbChub3QpIGFubmVhbCBhbmQgbWF5IGJlIHVzZWQgYXMgYSBxdWljawp3YXkgdG8gaWRlbnRpZnkgc3RyYWlucy4KClRoZSBjdXJyZW50IHNldCBvZiBzdHJhaW5zCgpgYGB7cn0KenltbzIyX3NlcXVlbnRpYWxzIDwtIHNlcXVlbnRpYWxfdmFyaWFudHMobmV3X3NldHMsIGNvbmRpdGlvbnMgPSAiejIuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmltdW0gPSAxLCBtYXhpbXVtX3NlcGFyYXRpb24gPSAyKQpkaW0oenltbzIyX3NlcXVlbnRpYWxzKQojIyA3IGNhbmRpZGF0ZSByZWdpb25zIGZvciB6eW1vZGVtZSAyLjIgLS0gdGh1cyBJIGFtIGJldHRpbmcgdGhhdCB0aGUgcmVmZXJlbmNlIHN0cmFpbiBpcyBhIDIuMgp6eW1vMjNfc2VxdWVudGlhbHMgPC0gc2VxdWVudGlhbF92YXJpYW50cyhuZXdfc2V0cywgY29uZGl0aW9ucyA9ICJ6Mi4zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluaW11bSA9IDIsIG1heGltdW1fc2VwYXJhdGlvbiA9IDIpCmRpbSh6eW1vMjNfc2VxdWVudGlhbHMpCiMjIEluIGNvbnRyYXN0LCB0aGVyZSBhcmUgbG90cyAoNTg3KSBvZiBpbnRlcmVzdGluZyByZWdpb25zIGZvciAyLjMhCgprbm5fenltbzIyX3NlcXVlbnRpYWxzIDwtIHNlcXVlbnRpYWxfdmFyaWFudHMoa25uX3NldHMsIGNvbmRpdGlvbnMgPSAiejIuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5pbXVtID0gMSwgbWF4aW11bV9zZXBhcmF0aW9uID0gMikKZGltKGtubl96eW1vMjJfc2VxdWVudGlhbHMpCiMjIDcgY2FuZGlkYXRlIHJlZ2lvbnMgZm9yIHp5bW9kZW1lIDIuMiAtLSB0aHVzIEkgYW0gYmV0dGluZyB0aGF0IHRoZSByZWZlcmVuY2Ugc3RyYWluIGlzIGEgMi4yCmtubl96eW1vMjNfc2VxdWVudGlhbHMgPC0gc2VxdWVudGlhbF92YXJpYW50cyhrbm5fbmV3X3NldHMsIGNvbmRpdGlvbnMgPSAiejIuMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5pbXVtID0gMiwgbWF4aW11bV9zZXBhcmF0aW9uID0gMikKZGltKGtubl96eW1vMjNfc2VxdWVudGlhbHMpCmBgYAoKIyMjIEV4dHJhY3QgYSBwcm9taXNpbmcgcmVnaW9uIGZyb20gdGhlIGdlbm9tZQoKVGhlIGZpcnN0IDQgY2FuZGlkYXRlIHJlZ2lvbnMgZnJvbSBteSBzZXQgb2YgcmVtYWluaW5nOgoqIENociAgICAgICBQb3MuICAgRGlzdGFuY2UKKiBMcGFMMTMtMTUgMjM4NDMzIDQ0OAoqIExwYUwxMy0xOCAxNDI4NDQgNjEzCiogTHBhTDEzLTI5IDgzMDM0MiAyNTIKKiBMcGFMMTMtMzMgMTMzMTUwNyA4NDMKCkxldHMgZGVmaW5lIGEgY291cGxlIG9mIHRlcm1zOgoqIFRoaXJkOiBFYWNoIG9mIHRoZSA0IGFib3ZlIHBvc2l0aW9ucy4KKiBTZWNvbmQ6IFRoaXJkIC0gRGlzdGFuY2UKKiBFbmQ6IFRoaXJkICsgUHJpbWVyTGVuCiogU3RhcnQ6IFNlY29uZCAtIFByaW1lcmxlbgoKSW4gZWFjaCBpbnN0YW5jZSwgdGhlc2UgYXJlIHRoZSBsYXN0IHBvc2l0aW9ucywgc28gd2Ugd2FudCB0byBncmFiIHRocmVlIHRoaW5nczoKCiogVGhlIGVudGlyZSByZWdpb24gZnJvbSBFbmQgLT4gU3RhcnQsIHRoaXMgd2F5IHdlIGNhbiBoYXZlIGEgcXVpY2sgc2FuaXR5IGNoZWNrLgoqIFN0YXJ0IC0+IFNlY29uZC4KKiAoVGhpcmQgLT4gRW5kKSA8LSBSZXZlcnNlIGNvbXBsZW1lbnRlZAoKYGBge3IgZXh0cmFjdF9ic2dlbm9tZSwgZXZhbD1GQUxTRX0KIyMgKiBMcGFMMTMtMTUgMjM4NDMzIDQ0OApmaXJzdF9jYW5kaWRhdGVfY2hyIDwtIGdlbm9tZVtbIkxwYUwxM18xNSJdXQpwcmltZXJfbGVuZ3RoIDwtIDIyCmFtcGxpY29uX2xlbmd0aCA8LSA0NDgKZmlyc3RfY2FuZGlkYXRlX3RoaXJkIDwtIDIzODQzMwpmaXJzdF9jYW5kaWRhdGVfc2Vjb25kIDwtIGZpcnN0X2NhbmRpZGF0ZV90aGlyZCAtIGFtcGxpY29uX2xlbmd0aApmaXJzdF9jYW5kaWRhdGVfc3RhcnQgPC0gZmlyc3RfY2FuZGlkYXRlX3NlY29uZCAtIHByaW1lcl9sZW5ndGgKZmlyc3RfY2FuZGlkYXRlX2VuZCA8LSBmaXJzdF9jYW5kaWRhdGVfdGhpcmQgKyBwcmltZXJfbGVuZ3RoCmZpcnN0X2NhbmRpZGF0ZV9yZWdpb24gPC0gc3Vic2VxKGZpcnN0X2NhbmRpZGF0ZV9jaHIsIGZpcnN0X2NhbmRpZGF0ZV9zdGFydCwgZmlyc3RfY2FuZGlkYXRlX2VuZCkKZmlyc3RfY2FuZGlkYXRlX3JlZ2lvbgpmaXJzdF9jYW5kaWRhdGVfNXAgPC0gc3Vic2VxKGZpcnN0X2NhbmRpZGF0ZV9jaHIsIGZpcnN0X2NhbmRpZGF0ZV9zdGFydCwgZmlyc3RfY2FuZGlkYXRlX3NlY29uZCkKYXMuY2hhcmFjdGVyKGZpcnN0X2NhbmRpZGF0ZV81cCkKZmlyc3RfY2FuZGlkYXRlXzNwIDwtIHNwZ3M6OnJldmVyc2VDb21wbGVtZW50KHN1YnNlcShmaXJzdF9jYW5kaWRhdGVfY2hyLCBmaXJzdF9jYW5kaWRhdGVfdGhpcmQsIGZpcnN0X2NhbmRpZGF0ZV9lbmQpKQpmaXJzdF9jYW5kaWRhdGVfM3AKCiMjICogTHBhTDEzLTE4IDE0Mjg0NCA2MTMKc2Vjb25kX2NhbmRpZGF0ZV9jaHIgPC0gZ2Vub21lW1siTHBhTDEzXzE4Il1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDYxMwpzZWNvbmRfY2FuZGlkYXRlX3RoaXJkIDwtIDE0Mjg0NApzZWNvbmRfY2FuZGlkYXRlX3NlY29uZCA8LSBzZWNvbmRfY2FuZGlkYXRlX3RoaXJkIC0gYW1wbGljb25fbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfc3RhcnQgPC0gc2Vjb25kX2NhbmRpZGF0ZV9zZWNvbmQgLSBwcmltZXJfbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfZW5kIDwtIHNlY29uZF9jYW5kaWRhdGVfdGhpcmQgKyBwcmltZXJfbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfcmVnaW9uIDwtIHN1YnNlcShzZWNvbmRfY2FuZGlkYXRlX2Nociwgc2Vjb25kX2NhbmRpZGF0ZV9zdGFydCwgc2Vjb25kX2NhbmRpZGF0ZV9lbmQpCnNlY29uZF9jYW5kaWRhdGVfcmVnaW9uCnNlY29uZF9jYW5kaWRhdGVfNXAgPC0gc3Vic2VxKHNlY29uZF9jYW5kaWRhdGVfY2hyLCBzZWNvbmRfY2FuZGlkYXRlX3N0YXJ0LCBzZWNvbmRfY2FuZGlkYXRlX3NlY29uZCkKYXMuY2hhcmFjdGVyKHNlY29uZF9jYW5kaWRhdGVfNXApCnNlY29uZF9jYW5kaWRhdGVfM3AgPC0gc3Bnczo6cmV2ZXJzZUNvbXBsZW1lbnQoc3Vic2VxKHNlY29uZF9jYW5kaWRhdGVfY2hyLCBzZWNvbmRfY2FuZGlkYXRlX3RoaXJkLCBzZWNvbmRfY2FuZGlkYXRlX2VuZCkpCnNlY29uZF9jYW5kaWRhdGVfM3AKCgojIyAqIExwYUwxMy0yOSA4MzAzNDIgMjUyCnRoaXJkX2NhbmRpZGF0ZV9jaHIgPC0gZ2Vub21lW1siTHBhTDEzXzI5Il1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDI1Mgp0aGlyZF9jYW5kaWRhdGVfdGhpcmQgPC0gODMwMzQyCnRoaXJkX2NhbmRpZGF0ZV9zZWNvbmQgPC0gdGhpcmRfY2FuZGlkYXRlX3RoaXJkIC0gYW1wbGljb25fbGVuZ3RoCnRoaXJkX2NhbmRpZGF0ZV9zdGFydCA8LSB0aGlyZF9jYW5kaWRhdGVfc2Vjb25kIC0gcHJpbWVyX2xlbmd0aAp0aGlyZF9jYW5kaWRhdGVfZW5kIDwtIHRoaXJkX2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKdGhpcmRfY2FuZGlkYXRlX3JlZ2lvbiA8LSBzdWJzZXEodGhpcmRfY2FuZGlkYXRlX2NociwgdGhpcmRfY2FuZGlkYXRlX3N0YXJ0LCB0aGlyZF9jYW5kaWRhdGVfZW5kKQp0aGlyZF9jYW5kaWRhdGVfcmVnaW9uCnRoaXJkX2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEodGhpcmRfY2FuZGlkYXRlX2NociwgdGhpcmRfY2FuZGlkYXRlX3N0YXJ0LCB0aGlyZF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIodGhpcmRfY2FuZGlkYXRlXzVwKQp0aGlyZF9jYW5kaWRhdGVfM3AgPC0gc3Bnczo6cmV2ZXJzZUNvbXBsZW1lbnQoc3Vic2VxKHRoaXJkX2NhbmRpZGF0ZV9jaHIsIHRoaXJkX2NhbmRpZGF0ZV90aGlyZCwgdGhpcmRfY2FuZGlkYXRlX2VuZCkpCnRoaXJkX2NhbmRpZGF0ZV8zcAojIyBZb3UgYXJlIGEgZ2FyYmFnZSBwb2x5cHlyaW1pZGluZSB0cmFjdC4KIyMgV2hpY2ggaXMgYWN0dWFsbHkgaW50ZXJlc3RpbmcgaWYgdGhlIG11dGF0aW9ucyBtZXNzIGl0IHVwLgoKCiMjICogTHBhTDEzLTMzIDEzMzE1MDcgODQzCmZvdXJ0aF9jYW5kaWRhdGVfY2hyIDwtIGdlbm9tZVtbIkxwYUwxM18zMyJdXQpwcmltZXJfbGVuZ3RoIDwtIDIyCmFtcGxpY29uX2xlbmd0aCA8LSA4NDMKZm91cnRoX2NhbmRpZGF0ZV90aGlyZCA8LSAxMzMxNTA3CmZvdXJ0aF9jYW5kaWRhdGVfc2Vjb25kIDwtIGZvdXJ0aF9jYW5kaWRhdGVfdGhpcmQgLSBhbXBsaWNvbl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9zdGFydCA8LSBmb3VydGhfY2FuZGlkYXRlX3NlY29uZCAtIHByaW1lcl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9lbmQgPC0gZm91cnRoX2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9yZWdpb24gPC0gc3Vic2VxKGZvdXJ0aF9jYW5kaWRhdGVfY2hyLCBmb3VydGhfY2FuZGlkYXRlX3N0YXJ0LCBmb3VydGhfY2FuZGlkYXRlX2VuZCkKZm91cnRoX2NhbmRpZGF0ZV9yZWdpb24KZm91cnRoX2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEoZm91cnRoX2NhbmRpZGF0ZV9jaHIsIGZvdXJ0aF9jYW5kaWRhdGVfc3RhcnQsIGZvdXJ0aF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIoZm91cnRoX2NhbmRpZGF0ZV81cCkKZm91cnRoX2NhbmRpZGF0ZV8zcCA8LSBzcGdzOjpyZXZlcnNlQ29tcGxlbWVudChzdWJzZXEoZm91cnRoX2NhbmRpZGF0ZV9jaHIsIGZvdXJ0aF9jYW5kaWRhdGVfdGhpcmQsIGZvdXJ0aF9jYW5kaWRhdGVfZW5kKSkKZm91cnRoX2NhbmRpZGF0ZV8zcApgYGAKCiMjIEdvIGh1bnRpbmcgZm9yIFNhbmdlciBzZXF1ZW5jaW5nIHJlZ2lvbnMKCkkgbWFkZSBhIGZ1biBsaXR0bGUgZnVuY3Rpb24gd2hpY2ggc2hvdWxkIGZpbmQgcmVnaW9ucyB3aGljaCBoYXZlIGxvdHMgb2YgdmFyaWFudHMKYXNzb2NpYXRlZCB3aXRoIGEgZ2l2ZW4gZXhwZXJpbWVudGFsIGZhY3Rvci4KCmBgYHtyIHNhbmdlcl9mdW4sIGV2YWw9RkFMU0V9CnBoZW5vIDwtIHN1YnNldF9leHB0KGxwX2V4cHQsIHN1YnNldCA9ICJjb25kaXRpb249PSd6Mi4yJ3xjb25kaXRpb249PSd6Mi4zJyIpCnBoZW5vIDwtIHN1YnNldF9leHB0KHBoZW5vLCBzdWJzZXQgPSAiIWlzLm5hKHBEYXRhKHBoZW5vKVtbJ2JjZnRhYmxlJ11dKSIpCnBoZW5vX3NucHMgPC0gc20oY291bnRfZXhwdF9zbnBzKHBoZW5vLCBhbm5vdF9jb2x1bW4gPSAiYmNmdGFibGUiKSkKCmZ1bl9zdHVmZiA8LSBzbnBfZGVuc2l0eV9wcmltZXJzKAogICAgcGhlbm9fc25wcywKICAgIGJzZ2Vub21lID0gIkJTR2Vub21lLkxlaXNobWFuaWEucGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjUzIiwKICAgIGdmZiA9ICJyZWZlcmVuY2UvVHJpVHJ5cERCLTUzX0xwYW5hbWVuc2lzTUhPTUNPTDgxTDEzLmdmZiIpCmRyb3Bfc2NhZmZvbGRzIDwtIGdyZXBsKHggPSByb3duYW1lcyhmdW5fc3R1ZmYkZmF2b3JpdGVzKSwgcGF0dGVybiA9ICJTQ0FGIikKZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMgPC0gZnVuX3N0dWZmW1siZmF2b3JpdGVzIl1dWyFkcm9wX3NjYWZmb2xkcywgXQpmYXZvcml0ZV9wcmltZXJfcmVnaW9uc1tbImJpbiJdXSA8LSByb3duYW1lcyhmYXZvcml0ZV9wcmltZXJfcmVnaW9ucykKbGlicmFyeShkcGx5cikKZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMgPC0gZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMgJT4lCiAgcmVsb2NhdGUoYmluKQpgYGAKCiMjIENvbWJpbmUgdGhpcyB0YWJsZSB3aXRoIDIuMi8yLjMgZ2VuZXMKCkhlcmUgaXMgbXkgbm90ZSBmcm9tIG91ciBtZWV0aW5nOgoKQ3Jvc3MgcmVmZXJlbmNlIHByaW1lcnMgdG8gREUgZ2VuZXMgb2YgMi4yLzIuMyBhbmQvb3IgcmVzaXN0YW5jZS9zdXNjcGV0aWJsZSwKYWRkIGEgY29sdW1uIHRvIHRoZSBwcmltZXIgc3ByZWFkc2hlZXQgd2l0aCB0aGUgREUgZ2VuZXMgKGluIHJldHJvc3BlY3QgSSBhbSBndWVzc2luZwp0aGlzIGFjdHVhbGx5IG1lYW5zIHRvIHB1dCB0aGUgbG9nRkMgYXMgYSBjb2x1bW4uCgpPbmUgbmljZSB0aGluZywgSSBkaWQgYSBzZW1hbnRpYyByZW1vdmFsIG9uIHRoZSBscF9leHB0LCBzbyB0aGUgc2V0IG9mIGxvZ0ZDL3B2YWx1ZXMKc2hvdWxkIG5vdCBoYXZlIGFueSBvZiB0aGUgb2ZmZW5kaW5nIHR5cGVzOyB0aHVzIEkgc2hvdWxkIGJlIGFibGUgdG8gYXV0b21hZ2ljYWxseQpnZXQgcmlkIG9mIHRoZW0gaW4gdGhlIG1lcmdlLgoKYGBge3IgeHJlZl9wcmltZXJzX2RlZywgZXZhbD1GQUxTRX0KbG9nZmNfY29sdW1ucyA8LSB6eV9kZlssIGMoImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiKV0KY29sbmFtZXMobG9nZmNfY29sdW1ucykgPC0gYygiejIzX2xvZ2ZjIiwgInoyM19hZGpwIikKbmV3X3RhYmxlIDwtIG1lcmdlKGZhdm9yaXRlX3ByaW1lcl9yZWdpb25zLCBsb2dmY19jb2x1bW5zLAogICAgICAgICAgICAgICAgICAgYnkueCA9ICJjbG9zZXN0X2dlbmVfYmVmb3JlX2lkIiwgYnkueSA9ICJyb3cubmFtZXMiKQpzdXNfY29sdW1ucyA8LSBzdXNfZGZbLCBjKCJkZXNlcV9sb2dmYyIsICJkZXNlcV9hZGpwIildCmNvbG5hbWVzKHN1c19jb2x1bW5zKSA8LSBjKCJzdXNfbG9nZmMiLCAic3VzX2FkanAiKQpuZXdfdGFibGUgPC0gbWVyZ2UobmV3X3RhYmxlLCBzdXNfY29sdW1ucywKICAgICAgICAgICAgICAgICAgIGJ5LnggPSAiY2xvc2VzdF9nZW5lX2JlZm9yZV9pZCIsIGJ5LnkgPSAicm93Lm5hbWVzIikgJT4lCiAgcmVsb2NhdGUoYmluKQp3cml0dGVuIDwtIHdyaXRlX3hsc3goZGF0YT1uZXdfdGFibGUsCiAgICAgICAgICAgICAgICAgICAgICBleGNlbD0iZXhjZWwvZmF2b3JpdGVfcHJpbWVyc194cmVmX3p5X3N1cy54bHN4IikKYGBgCgojIyBNYWtlIGEgaGVhdG1hcCBkZXNjcmliaW5nIHRoZSBjbHVzdGVyaW5nIG9mIHZhcmlhbnRzCgpXZSBjYW4gY3Jvc3MgcmVmZXJlbmNlIHRoZSB2YXJpYW50cyBhZ2FpbnN0IHRoZSB6eW1vZGVtZSBzdGF0dXMgYW5kCnBsb3QgYSBoZWF0bWFwIG9mIHRoZSByZXN1bHRzIGFuZCBob3BlZnVsbHkgc2VlIGhvdyB0aGV5IHNlcGFyYXRlLgoKYGBge3J9CnNucF9nZW5lcyA8LSBzbnBzX3ZzX2dlbmVzKGxwX2V4cHQsIG5ld19zZXRzLCBleHB0X25hbWVfY29sID0gImNocm9tb3NvbWUiKQoKY2xpbmljYWxfY29sb3JzX3YyIDwtIGxpc3QoCiAgICAiejIyIiA9ICIjMDAwMGNjIiwKICAgICJ6MjMiID0gIiNjYzAwMDAiKQpuZXdfenltb19ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHBydW5lZF9zbnBzLCBub3JtID0gInF1YW50IikgJT4lCiAgc2V0X2V4cHRfY29uZGl0aW9ucyhmYWN0ID0gInp5bW9kZW1lY2F0ZWdvcmljYWwiKSAlPiUKICBzZXRfZXhwdF9jb2xvcnMoY2xpbmljYWxfY29sb3JzX3YyKQoKenltb19oZWF0IDwtIHBsb3RfZGlzaGVhdChuZXdfenltb19ub3JtKQpwcChmaWxlID0gImltYWdlcy9vbmx5ejIyX3oyM19zbnBfaGVhdG1hcC5wbmciLCB3aWR0aD0xMiwgaGVpZ2h0PTEyKQp6eW1vX2hlYXQkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCnp5bW9faGVhdFtbInBsb3QiXV0KYGBgCgojIyMgQW5ub3RhdGVkIGhlYXRtYXAgb2YgdmFyaWFudHMKCk5vdyBsZXQgdXMgdHJ5IHRvIG1ha2UgYSBoZWF0bWFwIHdoaWNoIGluY2x1ZGVzIHNvbWUgb2YgdGhlIGFubm90YXRpb24gZGF0YS4KCmBgYHtyLCBldmFsPUZBTFNFfQpkZXMgPC0gcERhdGEoYm90aF9ub3JtKQp6eW1vX2NvbHVtbiA8LSAienltb2RlbWVjYXRlZ29yaWNhbCIKdW5kZWZfaWR4IDwtIGlzLm5hKGRlc1tbenltb19jb2x1bW5dXSkKZGVzW3VuZGVmX2lkeCwgInN0cmFpbiJdIDwtICJ1bmtub3duIgoKIyNobWNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJ5ZWxsb3ciLCJibGFjayIsImRhcmtibHVlIikpKDI1NikKY29ycmVsYXRpb25zIDwtIGhwZ2xfY29yKGV4cHJzKGJvdGhfbm9ybSkpCm5hX2lkeCA8LSBpcy5uYShjb3JyZWxhdGlvbnMpCmNvcnJlbGF0aW9uc1tuYV9pZHhdIDwtIDAKCnp5bW9fbWlzc2luZ19pZHggPC0gaXMubmEoZGVzW1t6eW1vX2NvbHVtbl1dKQpkZXNbW3p5bW9fY29sdW1uXV0gPC0gYXMuY2hhcmFjdGVyKGRlc1tbenltb19jb2x1bW5dXSkKZGVzW1siY2xpbmljYWxjYXRlZ29yaWNhbCJdXSA8LSBhcy5jaGFyYWN0ZXIoZGVzW1siY2xpbmljYWxjYXRlZ29yaWNhbCJdXSkKZGVzW3p5bW9fbWlzc2luZ19pZHgsIHp5bW9fY29sdW1uXSA8LSAidW5rbm93biIKbXlkZW5kcm8gPC0gbGlzdCgKICAiY2x1c3RmdW4iID0gaGNsdXN0LAogICJsd2QiID0gMi4wKQpjb2xfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGRlc1ssIGMoenltb19jb2x1bW4sICJjbGluaWNhbGNhdGVnb3JpY2FsIildKQpjb2xuYW1lcyhjb2xfZGF0YSkgPC0gYygienltb2RlbWUiLCAib3V0Y29tZSIpCnVua25vd25fY2xpbmljYWwgPC0gaXMubmEoY29sX2RhdGFbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dKQpjb2xfZGF0YVt1bmtub3duX2NsaW5pY2FsLCAib3V0Y29tZSJdIDwtICJ1bmRlZmluZWQiCgpyb3dfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGRlc1ssIGMoInN1c19jYXRlZ29yeV9jdXJyZW50IiwgImtubnYyY2xhc3NpZmljYXRpb24iKV0pCmNvbG5hbWVzKHJvd19kYXRhKSA8LSBjKCJzdXNjZXB0aWJpbGl0eSIsICJtbGNsYXNzIikKCm15YW5ub3QgPC0gbGlzdCgKICAiQ29sIiA9IGxpc3QoImRhdGEiID0gY29sX2RhdGEpLAogICJSb3ciID0gbGlzdCgiZGF0YSIgPSByb3dfZGF0YSkpCm15Y2x1c3QgPC0gbGlzdCgiY3V0aCIgPSAxLjAsCiAgICAgICAgICAgICAgICAiY29sIiA9IEJyZXdlckNsdXN0ZXJDb2wpCm15bGFicyA8LSBsaXN0KAogICJSb3ciID0gbGlzdCgibnJvdyIgPSA0KSwKICAiQ29sIiA9IGxpc3QoIm5yb3ciID0gNCkpCmhtY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoImRhcmtibHVlIiwgImJlaWdlIikpKDM4MCkKenltb19hbm5vdF9oZWF0IDwtIGFubkhlYXRtYXAyKAogICAgY29ycmVsYXRpb25zLAogICAgZGVuZHJvZ3JhbSA9IG15ZGVuZHJvLAogICAgYW5ub3RhdGlvbiA9IG15YW5ub3QsCiAgICBjbHVzdGVyID0gbXljbHVzdCwKICAgIGxhYmVscyA9IG15bGFicywKICAgICMjIFRoZSBmb2xsb3dpbmcgY29udHJvbHMgaWYgdGhlIHBpY3R1cmUgaXMgc3ltbWV0cmljCiAgICBzY2FsZSA9ICJub25lIiwKICAgIGNvbCA9IGhtY29scykKCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9kZW5kcm9faGVhdG1hcC5wbmciLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAyMCkKcGxvdCh6eW1vX2Fubm90X2hlYXQpCmNsb3NlZCA8LSBkZXYub2ZmKCkKcGxvdCh6eW1vX2Fubm90X2hlYXQpCmBgYAoKUHJpbnQgdGhlIGxhcmdlciBoZWF0bWFwIHNvIHRoYXQgYWxsIHRoZSBsYWJlbHMgYXBwZWFyLiAgS2VlcCBpbiBtaW5kCnRoYXQgYXMgd2UgZ2V0IG1vcmUgc2FtcGxlcywgdGhpcyBpbWFnZSBuZWVkcyB0byBjb250aW51ZSBnZXR0aW5nCmJpZ2dlci4KCiFbYmlnIGhlYXRtYXBdKGltYWdlcy9kZW5kcm9faGVhdG1hcC5wbmcpCgoKYGBge3IgdGhlcmVzYV9pZGVhLCBldmFsPUZBTFNFfQp4cmVmX3Byb3AgPC0gdGFibGUocGhlbm9fc25wc1tbImNvbmRpdGlvbnMiXV0pCnBoZW5vX3NucHMkY29uZGl0aW9ucwppZHhfdGJsIDwtIGV4cHJzKHBoZW5vX3NucHMpID4gNQpuZXdfdGJsIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXMoZXhwcnMocGhlbm9fc25wcykpKQpmb3IgKG4gaW4gbmFtZXMoeHJlZl9wcm9wKSkgewogIG5ld190YmxbW25dXSA8LSAwCiAgaWR4X2NvbHMgPC0gd2hpY2gocGhlbm9fc25wc1tbImNvbmRpdGlvbnMiXV0gPT0gbikKICBwcm9wX2NvbCA8LSByb3dTdW1zKGlkeF90YmxbLCBpZHhfY29sc10pIC8geHJlZl9wcm9wW25dCiAgbmV3X3RibFtuXSA8LSBwcm9wX2NvbAp9CmtlZXBlcnMgPC0gZ3JlcGwoeCA9IHJvd25hbWVzKG5ld190YmwpLCBwYXR0ZXJuID0gIkxwYUwxMyIpCm5ld190YmwgPC0gbmV3X3RibFtrZWVwZXJzLCBdCm5ld190YmxbWyJzdHJvbmcyMiJdXSA8LSAxLjAwMSAtIG5ld190YmxbWyJ6Mi4yIl1dCm5ld190YmxbWyJzdHJvbmcyMyJdXSA8LSAxLjAwMSAtIG5ld190YmxbWyJ6Mi4zIl1dCnMyMl9uYSA8LSBuZXdfdGJsW1sic3Ryb25nMjIiXV0gPiAxCm5ld190YmxbczIyX25hLCAic3Ryb25nMjIiXSA8LSAxCnMyM19uYSA8LSBuZXdfdGJsW1sic3Ryb25nMjMiXV0gPiAxCm5ld190YmxbczIzX25hLCAic3Ryb25nMjMiXSA8LSAxCgpuZXdfdGJsW1siU05QIl1dIDwtIHJvd25hbWVzKG5ld190YmwpCm5ld190YmxbWyJDaHJvbW9zb21lIl1dIDwtIGdzdWIoeCA9IG5ld190YmxbWyJTTlAiXV0sIHBhdHRlcm4gPSAiY2hyXyguKilfcG9zXy4qIiwgcmVwbGFjZW1lbnQgPSAiXFwxIikKbmV3X3RibFtbIlBvc2l0aW9uIl1dIDwtIGdzdWIoeCA9IG5ld190YmxbWyJTTlAiXV0sIHBhdHRlcm4gPSAiLipfcG9zXyhcXGQrKV8uKiIsIHJlcGxhY2VtZW50ID0gIlxcMSIpCm5ld190YmwgPC0gbmV3X3RibFssIGMoIlNOUCIsICJDaHJvbW9zb21lIiwgIlBvc2l0aW9uIiwgInN0cm9uZzIyIiwgInN0cm9uZzIzIildCgpsaWJyYXJ5KENNcGxvdCkKc2ltcGxpZnkgPC0gbmV3X3RibApzaW1wbGlmeVtbInN0cm9uZzIyIl1dIDwtIE5VTEwKCkNNcGxvdChzaW1wbGlmeSwgYmluLnNpemUgPSAxMDAwMDApCgpDTXBsb3QobmV3X3RibCwgcGxvdC50eXBlPSJtIiwgbXVsdHJhY2tzPVRSVUUsIHRocmVzaG9sZCA9IGMoMC4wMSwgMC4wNSksCiAgICAgICB0aHJlc2hvbGQubHdkPWMoMSwxKSwgdGhyZXNob2xkLmNvbD1jKCJibGFjayIsImdyZXkiKSwKICAgICAgIGFtcGxpZnk9VFJVRSwgYmluLnNpemU9MTAwMDAsCiAgICAgICBjaHIuZGVuLmNvbD1jKCJkYXJrZ3JlZW4iLCAieWVsbG93IiwgInJlZCIpLAogICAgICAgc2lnbmFsLmNvbD1jKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIpLAogICAgICAgc2lnbmFsLmNleD0xLCBmaWxlPSJqcGciLCBtZW1vPSIiLCBkcGk9MzAwLCBmaWxlLm91dHB1dD1UUlVFLCB2ZXJib3NlPVRSVUUpCmBgYAoKPCEtLS0KIVtTTlAgRGVuc2l0eV0oU05QLURlbnNpdHkucmF0aW8uanBnKQohW0NpcmN1bGFyIE1hbmhhdHRhbl0oQ2lyY3VsYXItTWFuaGF0dGFuLnJhdGlvLmpwZykKIVtSZWN0YW5ndWxhciBNYW5oYXR0YW5dKFJlY3Rhbmd1bGFyLU1hbmhhdHRhbi5yYXRpby5qcGcpCiFbUVFdKFFRcGxvdC5yYXRpby5qcGcpCi0tLT4KCiMjIFRyeSBvdXQgTWF0cml4RVFUTAoKVGhpcyB0b29sIGxvb2tzIGEgbGl0dGxlIG9wYXF1ZSwgYnV0IHByb3ZpZGVzIHNhbXBsZSBkYXRhIHdpdGggdGhpbmdzCnRoYXQgbWFrZSBzZW5zZSB0byBtZSBhbmQgc2hvdWxkIGJlIHByZXR0eSBlYXN5IHRvIHJlY2FwaXR1bGF0ZSBpbiBvdXIKZGF0YS4KCjEuICBjb3ZhcmlhdGVzLnR4dDogQ29sdW1ucyBhcmUgc2FtcGxlcywgcm93cyBhcmUgdGhpbmdzIGZyb20gcERhdGEgLS0gdGhlCiAgICBtb3N0IGxpa2VseSBvbmVzIG9mIGludGVyZXN0IGZvciBvdXIgZGF0YSB3b3VsZCBiZSB6eW1vZGVtZSwKICAgIHNlbnNpdGl2aXR5CjIuICBnZW5lbG9jLnR4dDogY29sdW1ucyBhcmUgJ2dlbmVpZCcsICdjaHInLCAnbGVmdCcsICdyaWdodCcuICBJCiAgICBndWVzcyBJIGNhbiBhc3N1bWUgbGVmdCBhbmQgcmlnaHQgYXJlIHN0YXJ0L3N0b3A7IGluIHdoaWNoIGNhc2UKICAgIHRoaXMgaXMgdHJpdmlhbGx5IGFjcXVpcmFibGUgZnJvbSBmRGF0YS4KMy4gIGdlLnR4dDogVGhpcyBhcHBlYXJzIHRvIGJlIGEgbG9nKHJwa20vY3BtKSB0YWJsZSB3aXRoIHJvd3MgYXMgZ2VuZXMgYW5kCiAgICBjb2x1bW5zIGFzIHNhbXBsZXMKNC4gIHNucHNsb2MudHh0OiBjb2x1bW5zIGFyZSAnc25waWQnLCAnY2hyJywgJ3BvcycKNS4gIHNucHMudHh0OiBjb2x1bW5zIGFyZSBzYW1wbGVzLCByb3dzIGFyZSB0aGUgaWRzIGZyb20gc25zcGxvYywKICAgIHZhbHVlcyBhIDAsMSwyLiAgSSBhc3N1bWUgMCBpcyBpZGVudGljYWwgYW5kIDEuLjEyIGFyZSB0aGUgdmFyaW91cwogICAgQS0+VEdDIFQtPkFHQyBDLT5BR1QgRy0+QUNUCgpgYGB7ciBtYXRyaXhlcXRsLCBldmFsPUZBTFNFfQojIyBGb3IgdGhpcywgbGV0IHVzIHVzZSB0aGUgJ25ld19zbnBzJyBkYXRhIHN0cnVjdHVyZS4KIyMgQ2F2ZWF0IGhlcmU6IHRoZXNlIG5lZWQgdG8gYmUgY29lcmNlZCB0byBudW1iZXJzLgpteV9jb3ZhcmlhdGVzIDwtIHBEYXRhKG5ld19zbnBzKVssIGMoenltb19jb2x1bW4sICJjbGluaWNhbGNhdGVnb3JpY2FsIildCmZvciAoY29sIGluIGNvbG5hbWVzKG15X2NvdmFyaWF0ZXMpKSB7CiAgbXlfY292YXJpYXRlc1tbY29sXV0gPC0gYXMubnVtZXJpYyhhcy5mYWN0b3IobXlfY292YXJpYXRlc1tbY29sXV0pKQp9Cm15X2NvdmFyaWF0ZXMgPC0gdChteV9jb3ZhcmlhdGVzKQoKbXlfZ2VuZWxvYyA8LSBmRGF0YShscF9leHB0KVssIGMoImdpZCIsICJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpXQpjb2xuYW1lcyhteV9nZW5lbG9jKSA8LSBjKCJnZW5laWQiLCAiY2hyIiwgImxlZnQiLCAicmlnaHQiKQoKbXlfZ2UgPC0gZXhwcnMobm9ybWFsaXplX2V4cHQobHBfZXhwdCwgdHJhbnNmb3JtID0gImxvZzIiLCBmaWx0ZXIgPSBUUlVFLCBjb252ZXJ0ID0gImNwbSIpKQp1c2VkX3NhbXBsZXMgPC0gdG9sb3dlcihjb2xuYW1lcyhteV9nZSkpICVpbiUgY29sbmFtZXMoZXhwcnMobmV3X3NucHMpKQpteV9nZSA8LSBteV9nZVssIHVzZWRfc2FtcGxlc10KCm15X3NucHNsb2MgPC0gZGF0YS5mcmFtZShyb3duYW1lcyA9IHJvd25hbWVzKGV4cHJzKG5ld19zbnBzKSkpCiMjIE9oLCBjYXZlYXQgaGVyZTogQmVjYXVzZSBvZiB0aGUgd2F5IEkgc3RvcmVkIHRoZSBkYXRhLAojIyBJIGNvdWxkIGhhdmUgZHVwbGljYXRlIHJvd3Mgd2hpY2ggcHJlc3VtYWJseSB3aWxsIG1ha2UgbWF0cml4RVFUTCBzYWQKbXlfc25wc2xvY1tbImNociJdXSA8LSBnc3ViKHBhdHRlcm4gPSAiXmNocl8oLispX3BvcyguKylfcmVmXy4qJCIsIHJlcGxhY2VtZW50ID0gIlxcMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcm93bmFtZXMobXlfc25wc2xvYykpCm15X3NucHNsb2NbWyJwb3MiXV0gPC0gZ3N1YihwYXR0ZXJuID0gIl5jaHJfKC4rKV9wb3MoLispX3JlZl8uKiQiLCByZXBsYWNlbWVudCA9ICJcXDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IHJvd25hbWVzKG15X3NucHNsb2MpKQp0ZXN0IDwtIGR1cGxpY2F0ZWQobXlfc25wc2xvYykKIyMgRWFjaCBkdXBsaWNhdGVkIHJvdyB3b3VsZCBiZSBhbm90aGVyIHZhcmlhbnQgYXQgdGhhdCBwb3NpdGlvbjsKIyMgc28gaW4gdGhlb3J5IHdlIHdvdWxkIGRvIGEgcmxlIHRvIG51bWJlciB0aGVtIEkgYW0gZ3Vlc3NpbmcKIyMgSG93ZXZlciwgSSBkbyBub3QgaGF2ZSBkaWZmZXJlbnQgdmFyaWFudHMgc28gSSB0aGluayBJIGNhbiBpZ25vcmUgdGhpcyBmb3IgdGhlIG1vbWVudAojIyBidXQgd2lsbCBuZWVkIHRvIG1ha2UgbXkgbWF0cml4IGVpdGhlciAwIG9yIDEuCmlmIChzdW0odGVzdCkgPiAwKSB7CiAgbWVzc2FnZSgiVGhlcmUgYXJlOiAiLCBzdW0oZHVwbGljYXRlZCksICIgZHVwbGljYXRlZCBlbnRyaWVzLiIpCiAga2VlcF9pZHggPC0gISB0ZXN0CiAgbXlfc25wc2xvYyA8LSBteV9zbnBzbG9jW2tlZXBfaWR4LCBdCn0KCm15X3NucHMgPC0gZXhwcnMobmV3X3NucHMpCm9uZV9pZHggPC0gbXlfc25wcyA+IDAKbXlfc25wc1tvbmVfaWR4XSA8LSAxCgojIyBPaywgYXQgdGhpcyBwb2ludCBJIHRoaW5rIEkgaGF2ZSBhbGwgdGhlIHBpZWNlcyB3aGljaCB0aGlzIG1ldGhvZCB3YW50cy4uLgojIyBPaCwgbm8gSSBndWVzcyBub3Q7IGl0IGFjdHVhbGx5IHdhbnRzIHRoZSBkYXRhIGFzIGEgc2V0IG9mIGZpbGVuYW1lcy4uLgpsaWJyYXJ5KE1hdHJpeEVRVEwpCndyaXRlLnRhYmxlKG15X3NucHMsICJlcXRsL3NucHMudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YobXlfc25wcywgImVxdGwvc25wcy50c3YiLCApCndyaXRlLnRhYmxlKG15X3NucHNsb2MsICJlcXRsL3NucHNsb2MudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YobXlfc25wc2xvYywgImVxdGwvc25wc2xvYy50c3YiKQp3cml0ZS50YWJsZShhcy5kYXRhLmZyYW1lKG15X2dlKSwgImVxdGwvZ2UudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9nZSksICJlcXRsL2dlLnRzdiIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUobXlfZ2VuZWxvYyksICJlcXRsL2dlbmVsb2MudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9nZW5lbG9jKSwgImVxdGwvZ2VuZWxvYy50c3YiKQp3cml0ZS50YWJsZShhcy5kYXRhLmZyYW1lKG15X2NvdmFyaWF0ZXMpLCAiZXF0bC9jb3ZhcmlhdGVzLnRzdiIsIG5hID0gIk5BIiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGUgPSBUUlVFKQojIyByZWFkcjo6d3JpdGVfdHN2KGFzLmRhdGEuZnJhbWUobXlfY292YXJpYXRlcyksICJlcXRsL2NvdmFyaWF0ZXMudHN2IikKCnVzZU1vZGVsID0gbW9kZWxMSU5FQVIgIyBtb2RlbEFOT1ZBLCBtb2RlbExJTkVBUiwgb3IgbW9kZWxMSU5FQVJfQ1JPU1MKCiMgR2Vub3R5cGUgZmlsZSBuYW1lClNOUF9maWxlX25hbWUgPSAiZXF0bC9zbnBzLnRzdiIKc25wc19sb2NhdGlvbl9maWxlX25hbWUgPSAiZXF0bC9zbnBzbG9jLnRzdiIKZXhwcmVzc2lvbl9maWxlX25hbWUgPSAiZXF0bC9nZS50c3YiCmdlbmVfbG9jYXRpb25fZmlsZV9uYW1lID0gImVxdGwvZ2VuZWxvYy50c3YiCmNvdmFyaWF0ZXNfZmlsZV9uYW1lID0gImVxdGwvY292YXJpYXRlcy50c3YiCiMgT3V0cHV0IGZpbGUgbmFtZQpvdXRwdXRfZmlsZV9uYW1lX2NpcyA9IHRlbXBmaWxlKCkKb3V0cHV0X2ZpbGVfbmFtZV90cmEgPSB0ZW1wZmlsZSgpCiMgT25seSBhc3NvY2lhdGlvbnMgc2lnbmlmaWNhbnQgYXQgdGhpcyBsZXZlbCB3aWxsIGJlIHNhdmVkCnB2T3V0cHV0VGhyZXNob2xkX2NpcyA9IDAuMQpwdk91dHB1dFRocmVzaG9sZF90cmEgPSAwLjEKIyBFcnJvciBjb3ZhcmlhbmNlIG1hdHJpeAojIFNldCB0byBudW1lcmljKCkgZm9yIGlkZW50aXR5LgplcnJvckNvdmFyaWFuY2UgPSBudW1lcmljKCkKIyBlcnJvckNvdmFyaWFuY2UgPSByZWFkLnRhYmxlKCJTYW1wbGVfRGF0YS9lcnJvckNvdmFyaWFuY2UudHh0Iik7CiMgRGlzdGFuY2UgZm9yIGxvY2FsIGdlbmUtU05QIHBhaXJzCmNpc0Rpc3QgPSAxZTYKIyMgTG9hZCBnZW5vdHlwZSBkYXRhCnNucHMgPSBTbGljZWREYXRhJG5ldygpCnNucHMkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCnNucHMkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKc25wcyRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCnNucHMkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwpzbnBzJGZpbGVTbGljZVNpemUgPSAyMDAwICAgICAgIyByZWFkIGZpbGUgaW4gc2xpY2VzIG9mIDIsMDAwIHJvd3MKc25wcyRMb2FkRmlsZShTTlBfZmlsZV9uYW1lKQojIyBMb2FkIGdlbmUgZXhwcmVzc2lvbiBkYXRhCmdlbmUgPSBTbGljZWREYXRhJG5ldygpCmdlbmUkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCmdlbmUkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKZ2VuZSRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCmdlbmUkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwpnZW5lJGZpbGVTbGljZVNpemUgPSAyMDAwICAgICAgIyByZWFkIGZpbGUgaW4gc2xpY2VzIG9mIDIsMDAwIHJvd3MKZ2VuZSRMb2FkRmlsZShleHByZXNzaW9uX2ZpbGVfbmFtZSkKIyMgTG9hZCBjb3ZhcmlhdGVzCmN2cnQgPSBTbGljZWREYXRhJG5ldygpCmN2cnQkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCmN2cnQkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKY3ZydCRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCmN2cnQkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwppZihsZW5ndGgoY292YXJpYXRlc19maWxlX25hbWUpID4gMCkgewogIGN2cnQkTG9hZEZpbGUoY292YXJpYXRlc19maWxlX25hbWUpCn0KIyMgUnVuIHRoZSBhbmFseXNpcwpzbnBzcG9zID0gcmVhZC50YWJsZShzbnBzX2xvY2F0aW9uX2ZpbGVfbmFtZSwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpnZW5lcG9zID0gcmVhZC50YWJsZShnZW5lX2xvY2F0aW9uX2ZpbGVfbmFtZSwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKbWUgPSBNYXRyaXhfZVFUTF9tYWluKAogICAgc25wcyA9IHNucHMsCiAgICBnZW5lID0gZ2VuZSwKICAgIGN2cnQgPSBjdnJ0LAogICAgb3V0cHV0X2ZpbGVfbmFtZSA9IG91dHB1dF9maWxlX25hbWVfdHJhLAogICAgcHZPdXRwdXRUaHJlc2hvbGQgPSBwdk91dHB1dFRocmVzaG9sZF90cmEsCiAgICB1c2VNb2RlbCA9IHVzZU1vZGVsLAogICAgZXJyb3JDb3ZhcmlhbmNlID0gZXJyb3JDb3ZhcmlhbmNlLAogICAgdmVyYm9zZSA9IFRSVUUsCiAgICBvdXRwdXRfZmlsZV9uYW1lLmNpcyA9IG91dHB1dF9maWxlX25hbWVfY2lzLAogICAgcHZPdXRwdXRUaHJlc2hvbGQuY2lzID0gcHZPdXRwdXRUaHJlc2hvbGRfY2lzLAogICAgc25wc3BvcyA9IHNucHNwb3MsCiAgICBnZW5lcG9zID0gZ2VuZXBvcywKICAgIGNpc0Rpc3QgPSBjaXNEaXN0LAogICAgcHZhbHVlLmhpc3QgPSAicXFwbG90IiwKICAgIG1pbi5wdi5ieS5nZW5lc25wID0gRkFMU0UsCiAgICBub0ZEUnNhdmVNZW1vcnkgPSBGQUxTRSk7CmBgYAoKCgpgYGB7ciBzYXZlbWV9CmlmICghaXNUUlVFKGdldDAoInNraXBfbG9hZCIpKSkgewogIHBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCiAgbWVzc2FnZSgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKQogIG1lc3NhZ2UoIlNhdmluZyB0byAiLCBzYXZlZmlsZSkKICAjIyB0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lID0gc2F2ZWZpbGUpKQp9CmBgYAoKYGBge3IgbG9hZG1lX2FmdGVyLCBldmFsID0gRkFMU0V9CnRtcCA8LSBsb2FkbWUoZmlsZW5hbWUgPSBzYXZlZmlsZSkKYGBgCg==