Introduction
This is intended to contain analyses which logically follow our
transciptomic analyses. It is therefore a bit of a grab bag, it may
eventually comprise the variant search; but currently that is
intertwined with the DE results. As a result, this and the
‘pre_visualization’ document are currently very redundant.
Creating trees of
important strains
I have a few methods of creating (phylogenetic) trees describing the
relationship of the strains of interest in this data.
- Calculate matrices of the variants in the data and use tools like
euclidean distance and hclust to calculate the degree of
similarity.
- Create a kmer index of each genome and use ape to calculate distance
metrics and neighbor joining trees describing them.
- Extract CDS sequences of known interest (likely zymodeme genes,
since they are stuff like GP6/GAPDH) and perform a MSA->tree
operation.
my_genes <- c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
"LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300",
"other")
my_names <- c("ALAT", "ASAT", "G6PD", "NHv1", "NHv2", "MPI", "other")
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.
files <- paste0("compare_strains/",
c("lbraziliensis_2904_v46.fasta", "lpanamensis_v36.fasta", "strain_12588_modified_z21.fasta",
"strain_2272_modified_z22.fasta", "lpanamensis_psc1_v46.fasta", "strain_12444_modified_z24.fasta",
"leishmania_guyanensis_202209.fasta", "strain_2168_modified_z23.fasta"))
count_lst <- list()
sequence_vectors <- list()
sequence_set <- c()
filenames <- c()
count <- 0
for (f in files) {
count <- count + 1
filename <- gsub(x=f, pattern="\\.fna", replacement="")
message("Reading ", filename)
raw <- Rsamtools::FaFile(f)
seq <- Biostrings::getSeq(raw)
test <- as.vector(seq)
single_vector <- ""
for (v in test) {
single_vector <- paste0(single_vector, v)
}
sequence_vectors[[filename]] <- single_vector
sequence_set <- c(sequence_set, single_vector)
filenames <- c(filenames, filename)
}
## Reading compare_strains/lbraziliensis_2904_v46.fasta
## Reading compare_strains/lpanamensis_v36.fasta
## Reading compare_strains/strain_12588_modified_z21.fasta
## Reading compare_strains/strain_2272_modified_z22.fasta
## Reading compare_strains/lpanamensis_psc1_v46.fasta
## Reading compare_strains/strain_12444_modified_z24.fasta
## Reading compare_strains/leishmania_guyanensis_202209.fasta
## Reading compare_strains/strain_2168_modified_z23.fasta
dnastring_test <- Biostrings::DNAStringSet(sequence_set)
shortened <- gsub(x=filenames, pattern="\\.fasta$", replacement="") %>%
gsub(pattern="^compare_strains/", replacement="")
names(dnastring_test) <- shortened
dnastring_dnabin <- ape::as.DNAbin(dnastring_test)
test_dist <- kmer::kdistance(dnastring_dnabin, k=7)
test_phy <- ape::nj(test_dist) %>%
ape::root("lpanamensis_v36") %>%
ape::ladderize()
test_dnd <- as.dendrogram(test_phy)
test_phylo <- ape::as.phylo(test_dnd) %>%
ape::compute.brlen()
plot(test_phylo, type = "cladogram")

pp(file = "images/strain_genome_clustering.png")
plot(test_phylo, type = "phylogram", root.edge=TRUE)
dev.off()
## png
## 2
plot(test_phylo, type = "phylogram", root.edge=TRUE)

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")
## Warning in `[<-.factor`(`*tmp*`, iseq, value = c("undef", "undef", "undef", :
## invalid factor level, NA generated
## Found 6 in the annotation data.
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")
## Warning in `[<-.factor`(`*tmp*`, iseq, value = c("undef", "undef", "undef", :
## invalid factor level, NA generated
## Found 6 in the annotation data.
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")
## Warning in `[<-.factor`(`*tmp*`, iseq, value = c("undef", "undef", "undef", :
## invalid factor level, NA generated
## Found 6 in the annotation data.
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")
## Warning in `[<-.factor`(`*tmp*`, iseq, value = c("undef", "undef", "undef", :
## invalid factor level, NA generated
## Found 6 in the annotation data.
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")
## Warning in `[<-.factor`(`*tmp*`, iseq, value = c("undef", "undef", "undef", :
## invalid factor level, NA generated
## Found 6 in the annotation data.
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).
However, as of this writing, I have not re-performed the same tasks
with the 2016 data, primarily because it will require remapping all of
the samples. As a result, for the moment I cannot combine the older and
newer samples. Thus, any of the following blocks which use the 2016 data
are currently disabled.
Note that the creation of the old_snps and new_snps datastructures
has been moved to the datastructures file.
both_norm <- set_expt_conditions(both_snps, fact = "knnv2classification")
## strains <- both_norm[["design"]][["strain"]]
both_strain <- set_expt_conditions(both_norm, fact = "strain")
I am having trouble reconciling my ‘by-eye’ observations for sample
TMRC20001 and those provided by the plots which follow. I think that
sample should completely cluster with the other 2.3 samples, but it does
not.
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_disheat.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 = "condition")
## The factor z2.3 has 41 rows.
## The factor z2.2 has 43 rows.
## The factor unknown has 2 rows.
## The factor z1.0 has only 1 row.
## The factor b2904 has only 1 row.
## The factor z3.0 has only 1 row.
## The factor z2.0 has only 1 row.
## The factor z1.5 has only 1 row.
## The factor z2.1 has 7 rows.
## The factor z2.4 has 2 rows.
## The factor z3.2 has only 1 row.
## The factor sh has 13 rows.
## The factor chr has 14 rows.
## The factor inf has 6 rows.
Biobase::annotation(lp_previous$expressionset) = Biobase::annotation(lp_expt$expressionset)
both_expt <- combine_expts(lp_expt, lp_previous)
##
## z2.3 z2.2 unknown z1.0 b2904 z3.0 z2.0 z1.5 z2.1 z2.4
## 41 43 2 1 1 1 1 1 7 2
## z3.2 sh chr inf
## 1 13 14 6
snp_genes <- sm(snps_vs_genes(both_expt, snp_sets, expt_name_col = "chromosome"))
## I think we have some metrics here we can plot...
snp_subset <- snp_subset_genes(
both_expt, both_snps,
genes = c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
"LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300"))
## remove_genes_expt(), before removal, there were 1514127 genes, now there are 179.
## There are 134 samples which kept less than 90 percent counts.
## tmrc20001 tmrc20065 tmrc20005 tmrc20007 tmrc20008 tmrc20027 tmrc20028 tmrc20032
## 0.0363994 0.0284342 0.0446300 0.0678958 0.0000000 0.0594351 0.0753021 0.0353544
## tmrc20040 tmrc20066 tmrc20039 tmrc20037 tmrc20038 tmrc20067 tmrc20068 tmrc20041
## 0.0204152 0.0244539 0.0218095 0.0228205 0.0244650 0.0259861 0.0275633 0.0084708
## tmrc20015 tmrc20009 tmrc20010 tmrc20016 tmrc20011 tmrc20012 tmrc20013 tmrc20017
## 0.0249880 0.0000000 0.0278667 0.0232143 0.0243409 0.0778398 0.0294979 0.0102837
## tmrc20014 tmrc20018 tmrc20019 tmrc20070 tmrc20020 tmrc20021 tmrc20022 tmrc20025
## 0.0191370 0.0239034 0.0282985 0.0274939 0.0235432 0.0286477 0.0000000 0.0624989
## tmrc20024 tmrc20036 tmrc20069 tmrc20033 tmrc20026 tmrc20031 tmrc20076 tmrc20073
## 0.0212984 0.0089603 0.0270318 0.0019682 0.0352553 0.0199682 0.0270505 0.0282364
## tmrc20055 tmrc20079 tmrc20071 tmrc20078 tmrc20094 tmrc20042 tmrc20058 tmrc20072
## 0.0395199 0.0280768 0.0247556 0.0177539 0.0279169 0.0398656 0.0256906 0.0158464
## tmrc20059 tmrc20048 tmrc20057 tmrc20088 tmrc20056 tmrc20060 tmrc20077 tmrc20074
## 0.0251221 0.0238737 0.0062348 0.0349161 0.0003009 0.0294943 0.0340198 0.0282781
## tmrc20063 tmrc20053 tmrc20052 tmrc20064 tmrc20075 tmrc20051 tmrc20050 tmrc20049
## 0.0017690 0.0202252 0.0274156 0.0280939 0.0236363 0.0297070 0.0324800 0.0338522
## tmrc20062 tmrc20110 tmrc20080 tmrc20043 tmrc20083 tmrc20054 tmrc20085 tmrc20046
## 0.0314505 0.0350400 0.0288995 0.0273341 0.0121428 0.0298779 0.0251593 0.0054459
## tmrc20093 tmrc20089 tmrc20047 tmrc20090 tmrc20044 tmrc20045 tmrc20061 tmrc20105
## 0.0065508 0.0269888 0.0299476 0.0273432 0.0316706 0.0052405 0.0263012 0.0303013
## tmrc20108 tmrc20109 tmrc20098 tmrc20096 tmrc20097 tmrc20101 tmrc20092 tmrc20082
## 0.0267368 0.0184037 0.0265627 0.0202590 0.0107502 0.0282691 0.0024627 0.0012027
## tmrc20102 tmrc20099 tmrc20100 tmrc20091 tmrc20084 tmrc20087 tmrc20103 tmrc20104
## 0.0250142 0.0291960 0.0266043 0.0274472 0.0063494 0.0291851 0.0065616 0.0265140
## tmrc20086 tmrc20107 tmrc20081 tmrc20106 tmrc20095 hpgl0242 hpgl0243 hpgl0244
## 0.0251956 0.0200527 0.0133293 0.0134896 0.0136510 0.0000000 0.0291184 0.0277722
## hpgl0245 hpgl0246 hpgl0247 hpgl0248 hpgl0316 hpgl0318 hpgl0320 hpgl0322
## 0.0092573 0.0281687 0.0690201 0.0000000 0.0135501 0.1068376 0.0581666 0.0520414
## hpgl0631 hpgl0632 hpgl0633 hpgl0634 hpgl0635 hpgl0636 hpgl0638 hpgl0639
## 0.0838203 0.0000000 0.0320164 0.0482116 0.0307934 0.0000000 0.0000000 0.0296412
## hpgl0641 hpgl0643 hpgl0651 hpgl0652 hpgl0653 hpgl0654 hpgl0655 hpgl0656
## 0.0249169 0.1094691 0.0864779 0.0000000 0.0367420 0.0405186 0.0353871 0.0000000
## hpgl0658 hpgl0659 hpgl0660 hpgl0661 hpgl0662 hpgl0663
## 0.0849825 0.0000000 0.0381134 0.0333671 0.0289599 0.0000000
zymo_heat <- plot_sample_heatmap(snp_subset, row_label = rownames(exprs(snp_subset)))
zymo_heat

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::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"]]
vars_df <- data.frame(ID = names(snp_genes$summary_by_gene), variants = as.numeric(snp_genes$summary_by_gene))
vars_df[["variants"]] <- log2(vars_df[["variants"]] + 1)
vars_by_de_gene <- merge(zy_df, vars_df, by.x="row.names", by.y="ID")
cor.test(vars_by_de_gene$deseq_logfc, vars_by_de_gene$variants)
variants_wrt_logfc <- plot_linear_scatter(vars_by_de_gene[, c("deseq_logfc", "variants")])
variants_wrt_logfc$scatter
## It looks like there might be some genes of interest, even though this is not actually
## the question of interest.
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")
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")
ggplot(density_df, aes_string(x = "chr", 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_variants(new_snps)
Cross reference
these variants by gene
clinical_genes <- snps_vs_genes(lp_expt, clinical_sets, expt_name_col = "chromosome")
snp_density <- merge(as.data.frame(clinical_genes[["summary_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)
head(cure_snps)
write.csv(file="csv/cure_variants.txt", x=rownames(cure_snps))
write.csv(file="csv/fail_variants.txt", x=rownames(fail_ref_snps))
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")
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
pruned_snps <- subset_expt(new_snps, subset="condition=='z2.2'|condition=='z2.3'")
new_sets <- get_snp_sets(pruned_snps, factor = "zymodemecategorical")
summary(new_sets)
## 1000000: 2.2
## 0100000: 2.3
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"]])
Thus we see that there are 3,553 variants associated with 2.2 and
81,589 associated with 2.3.
A small function
for searching for potential PCR primers
The following function uses the positional data to look for
sequential mismatches associated with zymodeme in the hopes that there
will be some regions which would provide good potential targets for a
PCR-based assay.
sequential_variants <- function(snp_sets, conditions = NULL, minimum = 3, maximum_separation = 3) {
if (is.null(conditions)) {
conditions <- 1
}
intersection_sets <- snp_sets[["intersections"]]
intersection_names <- snp_sets[["set_names"]]
chosen_intersection <- 1
if (is.numeric(conditions)) {
chosen_intersection <- conditions
} else {
intersection_idx <- intersection_names == conditions
chosen_intersection <- names(intersection_names)[intersection_idx]
}
possible_positions <- intersection_sets[[chosen_intersection]]
position_table <- data.frame(row.names = possible_positions)
pat <- "^chr_(.+)_pos_(.+)_ref_.*$"
position_table[["chr"]] <- gsub(pattern = pat, replacement = "\\1", x = rownames(position_table))
position_table[["pos"]] <- as.numeric(gsub(pattern = pat, replacement = "\\2", x = rownames(position_table)))
position_idx <- order(position_table[, "chr"], position_table[, "pos"])
position_table <- position_table[position_idx, ]
position_table[["dist"]] <- 0
last_chr <- ""
for (r in 1:nrow(position_table)) {
this_chr <- position_table[r, "chr"]
if (r == 1) {
position_table[r, "dist"] <- position_table[r, "pos"]
last_chr <- this_chr
next
}
if (this_chr == last_chr) {
position_table[r, "dist"] <- position_table[r, "pos"] - position_table[r - 1, "pos"]
} else {
position_table[r, "dist"] <- position_table[r, "pos"]
}
last_chr <- this_chr
}
## Working interactively here.
doubles <- position_table[["dist"]] == 1
doubles <- position_table[doubles, ]
write.csv(doubles, "doubles.csv")
one_away <- position_table[["dist"]] == 2
one_away <- position_table[one_away, ]
write.csv(one_away, "one_away.csv")
two_away <- position_table[["dist"]] == 3
two_away <- position_table[two_away, ]
write.csv(two_away, "two_away.csv")
combined <- rbind(doubles, one_away)
combined <- rbind(combined, two_away)
position_idx <- order(combined[, "chr"], combined[, "pos"])
combined <- combined[position_idx, ]
this_chr <- ""
for (r in 1:nrow(combined)) {
this_chr <- combined[r, "chr"]
if (r == 1) {
combined[r, "dist_pair"] <- combined[r, "pos"]
last_chr <- this_chr
next
}
if (this_chr == last_chr) {
combined[r, "dist_pair"] <- combined[r, "pos"] - combined[r - 1, "pos"]
} else {
combined[r, "dist_pair"] <- combined[r, "pos"]
}
last_chr <- this_chr
}
dist_pair_maximum <- 1000
dist_pair_minimum <- 200
dist_pair_idx <- combined[["dist_pair"]] <= dist_pair_maximum &
combined[["dist_pair"]] >= dist_pair_minimum
remaining <- combined[dist_pair_idx, ]
no_weak_idx <- grepl(pattern="ref_(G|C)", x=rownames(remaining))
remaining <- remaining[no_weak_idx, ]
print(head(table(position_table[["dist"]])))
sequentials <- position_table[["dist"]] <= maximum_separation
message("There are ", sum(sequentials), " candidate regions.")
## The following can tell me how many runs of each length occurred, that is not quite what I want.
## Now use run length encoding to find the set of sequential sequentials!
rle_result <- rle(sequentials)
rle_values <- rle_result[["values"]]
## The following line is equivalent to just leaving values alone:
## true_values <- rle_result[["values"]] == TRUE
rle_lengths <- rle_result[["lengths"]]
true_sequentials <- rle_lengths[rle_values]
rle_idx <- cumsum(rle_lengths)[which(rle_values)]
position_table[["last_sequential"]] <- 0
count <- 0
for (r in rle_idx) {
count <- count + 1
position_table[r, "last_sequential"] <- true_sequentials[count]
}
message("The maximum sequential set is: ", max(position_table[["last_sequential"]]), ".")
wanted_idx <- position_table[["last_sequential"]] >= minimum
wanted <- position_table[wanted_idx, c("chr", "pos")]
return(wanted)
}
zymo22_sequentials <- sequential_variants(new_sets, conditions = "z22", minimum=1, maximum_separation=2)
dim(zymo22_sequentials)
## 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 = "z23",
minimum = 2, maximum_separation = 2)
dim(zymo23_sequentials)
## In contrast, there are lots (587) of interesting regions for 2.3!
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 <- zy_table_sva[["data"]][["z23_vs_z22"]]
logfc_columns <- logfc[, 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 <- sus_table_sva[["data"]][["sensitive_vs_resistant"]]
sus_columns <- sus[, 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 <- sm(snps_vs_genes(lp_expt, new_sets, expt_name_col = "chromosome"))
clinical_colors_v2 <- list(
"z22" = "#0000cc",
"z23" = "#cc0000")
new_zymo_norm <- normalize_expt(pruned_snps, normq = "quant") %>%
set_expt_conditions(fact = "zymodemecategorical") %>%
set_expt_colors(clinical_colors_v2)
zymo_heat <- plot_disheat(new_zymo_norm)
dev <- pp(file = "images/onlyz22_z23_snp_heatmap.pdf", width=12, height=12)
zymo_heat[["plot"]]
closed <- dev.off()
zymo_heat[["plot"]]
Annotated heatmap
of variants
Now let us try to make a heatmap which includes some of the
annotation data.
tt <- plot_pca(both_norm)
tt$plot
des <- pData(both_norm)
zymo_column <- "knnv2classification"
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")])
unknown_clinical <- is.na(col_data[["clinicalcategorical"]])
row_data <- as.data.frame(des[, c("strain")])
colnames(col_data) <- c("zymodeme", "outcome")
col_data[unknown_clinical, "outcome"] <- "undefined"
colnames(row_data) <- c("strain")
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"))(240)
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.
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 911e7d4beebdc73267ec4be631a305574289efd3
## This is hpgltools commit: Tue Jan 17 10:36:44 2023 -0500: 911e7d4beebdc73267ec4be631a305574289efd3
## Saving to tmrc2_post_visualization_202301.rda.xz
tmp <- loadme(filename = savefile)
LS0tCnRpdGxlOiAiVE1SQzIgMjAyMzAxOiBWaXN1YWxpemluZyBBbmFseXNlcyBmb2xsb3dpbmcgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24iCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKIGh0bWxfZG9jdW1lbnQ6CiAgY29kZV9kb3dubG9hZDogdHJ1ZQogIGNvZGVfZm9sZGluZzogc2hvdwogIGZpZ19jYXB0aW9uOiB0cnVlCiAgZmlnX2hlaWdodDogNwogIGZpZ193aWR0aDogNwogIGhpZ2hsaWdodDogZGVmYXVsdAogIGtlZXBfbWQ6IGZhbHNlCiAgbW9kZTogc2VsZmNvbnRhaW5lZAogIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgdGhlbWU6IHJlYWRhYmxlCiAgdG9jOiB0cnVlCiAgdG9jX2Zsb2F0OgogICBjb2xsYXBzZWQ6IGZhbHNlCiAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlPgogIGJvZHkgLm1haW4tY29udGFpbmVyIHsKICAgIG1heC13aWR0aDogMTYwMHB4OwogIH0KPC9zdHlsZT4KCmBgYHtyIG9wdGlvbnMsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeShIZWF0cGx1cykKbGlicmFyeShocGdsdG9vbHMpCnR0IDwtIGRldnRvb2xzOjpsb2FkX2FsbCgifi9ocGdsdG9vbHMiKQprbml0cjo6b3B0c19rbml0JHNldChwcm9ncmVzcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDkwLAogICAgICAgICAgICAgICAgICAgICBlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDgsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gOCwKICAgICAgICAgICAgICAgICAgICAgIGRwaSA9IDk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cyA9IDQsCiAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgIGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxMikpCnZlciA8LSAiMjAyMzAxIgpwcmV2aW91c19maWxlIDwtICIiCnJ1bmRhdGUgPC0gZm9ybWF0KFN5cy5EYXRlKCksIGZvcm1hdCA9ICIlWSVtJWQiKQoKIyMgdG1wIDwtIHRyeShzbShsb2FkbWUoZmlsZW5hbWUgPSBnc3ViKHBhdHRlcm4gPSAiXFwuUm1kIiwgcmVwbGFjZSA9ICJcXC5yZGFcXC54eiIsIHggPSBwcmV2aW91c19maWxlKSkpKQpybWRfZmlsZSA8LSBnbHVlOjpnbHVlKCJ0bXJjMl9wb3N0X3Zpc3VhbGl6YXRpb25fe3Zlcn0uUm1kIikKc2F2ZWZpbGUgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcm1kX2ZpbGUpCmxvYWRlZCA8LSBsb2FkKGZpbGU9Z2x1ZTo6Z2x1ZSgicmRhL3RtcmMyX2RhdGFfc3RydWN0dXJlcy12e3Zlcn0ucmRhIikpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgaXMgaW50ZW5kZWQgdG8gY29udGFpbiBhbmFseXNlcyB3aGljaCBsb2dpY2FsbHkgZm9sbG93IG91cgp0cmFuc2NpcHRvbWljIGFuYWx5c2VzLiAgSXQgaXMgdGhlcmVmb3JlIGEgYml0IG9mIGEgZ3JhYiBiYWcsIGl0IG1heQpldmVudHVhbGx5IGNvbXByaXNlIHRoZSB2YXJpYW50IHNlYXJjaDsgYnV0IGN1cnJlbnRseSB0aGF0IGlzCmludGVydHdpbmVkIHdpdGggdGhlIERFIHJlc3VsdHMuICBBcyBhIHJlc3VsdCwgdGhpcyBhbmQgdGhlCidwcmVfdmlzdWFsaXphdGlvbicgZG9jdW1lbnQgYXJlIGN1cnJlbnRseSB2ZXJ5IHJlZHVuZGFudC4KCiMjIENyZWF0aW5nIHRyZWVzIG9mIGltcG9ydGFudCBzdHJhaW5zCgpJIGhhdmUgYSBmZXcgbWV0aG9kcyBvZiBjcmVhdGluZyAocGh5bG9nZW5ldGljKSB0cmVlcyBkZXNjcmliaW5nIHRoZQpyZWxhdGlvbnNoaXAgb2YgdGhlIHN0cmFpbnMgb2YgaW50ZXJlc3QgaW4gdGhpcyBkYXRhLgoKKiBDYWxjdWxhdGUgbWF0cmljZXMgb2YgdGhlIHZhcmlhbnRzIGluIHRoZSBkYXRhIGFuZCB1c2UgdG9vbHMgbGlrZQogIGV1Y2xpZGVhbiBkaXN0YW5jZSBhbmQgaGNsdXN0IHRvIGNhbGN1bGF0ZSB0aGUgZGVncmVlIG9mIHNpbWlsYXJpdHkuCiogQ3JlYXRlIGEga21lciBpbmRleCBvZiBlYWNoIGdlbm9tZSBhbmQgdXNlIGFwZSB0byBjYWxjdWxhdGUgZGlzdGFuY2UKICBtZXRyaWNzIGFuZCBuZWlnaGJvciBqb2luaW5nIHRyZWVzIGRlc2NyaWJpbmcgdGhlbS4KKiBFeHRyYWN0IENEUyBzZXF1ZW5jZXMgb2Yga25vd24gaW50ZXJlc3QgKGxpa2VseSB6eW1vZGVtZSBnZW5lcywKICBzaW5jZSB0aGV5IGFyZSBzdHVmZiBsaWtlIEdQNi9HQVBESCkgYW5kIHBlcmZvcm0gYSBNU0EtPnRyZWUgb3BlcmF0aW9uLgoKYGBge3Igenltb2RlbWVzfQpteV9nZW5lcyA8LSBjKCJMUEFMMTNfMTIwMDEwOTAwIiwgIkxQQUwxM18zNDAwMTMwMDAiLCAiTFBBTDEzXzAwMDA1NDEwMCIsCiAgICAgICAgICAgICAgIkxQQUwxM18xNDAwMDYxMDAiLCAiTFBBTDEzXzE4MDAxODUwMCIsICJMUEFMMTNfMzIwMDIyMzAwIiwKICAgICAgICAgICAgICAib3RoZXIiKQpteV9uYW1lcyA8LSBjKCJBTEFUIiwgIkFTQVQiLCAiRzZQRCIsICJOSHYxIiwgIk5IdjIiLCAiTVBJIiwgIm90aGVyIikKYGBgCgojIGttZXIgYmFzZWQgY29tcGFyaXNvbgoKVGhlIGZvbGxvd2luZyBibG9jayB3YXMgZ2VuZXJhdGVkIHZpYSB0aGUgZm9sbG93aW5nIG1ldGhvZHM6CgpUaGUgaGlzYXQyLWJhc2VkIGFsaWdubWVudHMgd2VyZSBwYXNzZWQgdG8gZnJlZWJheWVzMltyZWZdLCB3aGljaApnZW5lcmF0ZWQgYSBzZXQgb2YgaGlnaC1jb25maWRlbmNlIHRyYW5zY3JpcHRvbWUtYmFzZWQgdmNmIGZpbGVzIGZvcgplYWNoIHNhbXBsZSBvZiBzdWZmaWNpZW50IGNvdmVyYWdlLiAgVGhlc2Ugd2VyZSBzb3J0ZWQsIGNvbXByZXNzZWQsCmFuZCBpbmRleGVkIHZpYSBiY2Z0b29sc1tyZWZdOyBhbmQgaGlnaC1jb25maWRlbmNlIHZhcmlhbnRzIChtb3JlIHRoYW4KODAlIHdpdGggY292ZXJhZ2UgaGlnaGVyIHRoYW4gNSByZWFkcyBwZXIgcG9zaXRpb24pIHdlcmUgZXh0cmFjdGVkCmludG8gYSB0YWJsZSB2YXJpYW50cyBhcyB3ZWxsIGFzIHVzZWQgdG8gbW9kaWZ5IHRoZSByZWZlcmVuY2UgZ2Vub21lCnRvIHJlcHJlc2VudCB0aGVzZSBmaWx0ZXJlZCB2YXJpYW50cy4gIFRoZXNlIG1vZGlmaWVkIGdlbm9tZXMgd2VyZQpwYXNzZWQgdG8gYXBlW3JlZl0sIGluZGV4ZWQsIGFuZCB1c2VkIHRvIGNyZWF0ZSBhIGttZXIgaW5kZXggYW5kCmRpc3RhbmNlIG1hdHJpeDsgdGhlc2UgZGlzdGFuY2VzIHdlcmUgdXNlZCBieSBhcGUgdG8gY3JlYXRlIGEgbmVpZ2hib3IKam9pbmluZyB0cmVlLgoKYGBge3Iga21lcl90cmVlfQpmaWxlcyA8LSBwYXN0ZTAoImNvbXBhcmVfc3RyYWlucy8iLAogICAgICAgICAgICAgICAgYygibGJyYXppbGllbnNpc18yOTA0X3Y0Ni5mYXN0YSIsICJscGFuYW1lbnNpc192MzYuZmFzdGEiLCAic3RyYWluXzEyNTg4X21vZGlmaWVkX3oyMS5mYXN0YSIsCiAgICAgICAgICAgICAgICAgICJzdHJhaW5fMjI3Ml9tb2RpZmllZF96MjIuZmFzdGEiLCAibHBhbmFtZW5zaXNfcHNjMV92NDYuZmFzdGEiLCAic3RyYWluXzEyNDQ0X21vZGlmaWVkX3oyNC5mYXN0YSIsCiAgICAgICAgICAgICAgICAgICJsZWlzaG1hbmlhX2d1eWFuZW5zaXNfMjAyMjA5LmZhc3RhIiwgInN0cmFpbl8yMTY4X21vZGlmaWVkX3oyMy5mYXN0YSIpKQoKY291bnRfbHN0IDwtIGxpc3QoKQpzZXF1ZW5jZV92ZWN0b3JzIDwtIGxpc3QoKQpzZXF1ZW5jZV9zZXQgPC0gYygpCmZpbGVuYW1lcyA8LSBjKCkKY291bnQgPC0gMApmb3IgKGYgaW4gZmlsZXMpIHsKICBjb3VudCA8LSBjb3VudCArIDEKICBmaWxlbmFtZSA8LSBnc3ViKHg9ZiwgcGF0dGVybj0iXFwuZm5hIiwgcmVwbGFjZW1lbnQ9IiIpCiAgbWVzc2FnZSgiUmVhZGluZyAiLCBmaWxlbmFtZSkKICByYXcgPC0gUnNhbXRvb2xzOjpGYUZpbGUoZikKICBzZXEgPC0gQmlvc3RyaW5nczo6Z2V0U2VxKHJhdykKICB0ZXN0IDwtIGFzLnZlY3RvcihzZXEpCiAgc2luZ2xlX3ZlY3RvciA8LSAiIgogIGZvciAodiBpbiB0ZXN0KSB7CiAgICBzaW5nbGVfdmVjdG9yIDwtIHBhc3RlMChzaW5nbGVfdmVjdG9yLCB2KQogIH0KICBzZXF1ZW5jZV92ZWN0b3JzW1tmaWxlbmFtZV1dIDwtIHNpbmdsZV92ZWN0b3IKICBzZXF1ZW5jZV9zZXQgPC0gYyhzZXF1ZW5jZV9zZXQsIHNpbmdsZV92ZWN0b3IpCiAgZmlsZW5hbWVzIDwtIGMoZmlsZW5hbWVzLCBmaWxlbmFtZSkKfQoKZG5hc3RyaW5nX3Rlc3QgPC0gQmlvc3RyaW5nczo6RE5BU3RyaW5nU2V0KHNlcXVlbmNlX3NldCkKc2hvcnRlbmVkIDwtIGdzdWIoeD1maWxlbmFtZXMsIHBhdHRlcm49IlxcLmZhc3RhJCIsIHJlcGxhY2VtZW50PSIiKSAlPiUKICBnc3ViKHBhdHRlcm49Il5jb21wYXJlX3N0cmFpbnMvIiwgcmVwbGFjZW1lbnQ9IiIpCm5hbWVzKGRuYXN0cmluZ190ZXN0KSA8LSBzaG9ydGVuZWQKZG5hc3RyaW5nX2RuYWJpbiA8LSBhcGU6OmFzLkROQWJpbihkbmFzdHJpbmdfdGVzdCkKdGVzdF9kaXN0IDwtIGttZXI6OmtkaXN0YW5jZShkbmFzdHJpbmdfZG5hYmluLCBrPTcpCnRlc3RfcGh5IDwtIGFwZTo6bmoodGVzdF9kaXN0KSAlPiUKICBhcGU6OnJvb3QoImxwYW5hbWVuc2lzX3YzNiIpICU+JQogIGFwZTo6bGFkZGVyaXplKCkKCnRlc3RfZG5kIDwtIGFzLmRlbmRyb2dyYW0odGVzdF9waHkpCnRlc3RfcGh5bG8gPC0gYXBlOjphcy5waHlsbyh0ZXN0X2RuZCkgJT4lCiAgYXBlOjpjb21wdXRlLmJybGVuKCkKCnBsb3QodGVzdF9waHlsbywgdHlwZSA9ICJjbGFkb2dyYW0iKQpwcChmaWxlID0gImltYWdlcy9zdHJhaW5fZ2Vub21lX2NsdXN0ZXJpbmcucG5nIikKcGxvdCh0ZXN0X3BoeWxvLCB0eXBlID0gInBoeWxvZ3JhbSIsIHJvb3QuZWRnZT1UUlVFKQpkZXYub2ZmKCkKcGxvdCh0ZXN0X3BoeWxvLCB0eXBlID0gInBoeWxvZ3JhbSIsIHJvb3QuZWRnZT1UUlVFKQpgYGAKCiMgUGh5bG9nZW5ldGljIGFuYWx5c2lzIG9mIGdlbmVzIG9mIGludGVyZXN0CgpJbiBvcmRlciB0byBwZXJmb3JtIHRoaXMsIEkgd2lsbCB1c2UgdGhlIHNhbWUgZmFzdGEgZmlsZXMsIGJ1dCBleHRyYWN0CnRoZSB6eW1vZGVtZSBnZW5lcyBmcm9tIHRoZW0gYW5kIHdyaXRlIG91dCBhIHNldCBvZiBmYXN0YSBmaWxlcwpjb250YWluaW5nIHRoZWlyIHNlcXVlbmNlcy4gIEkgdGhlcmVmb3JlIHdyb3RlIGEgZnVuY3Rpb24gd2hpY2ggdGFrZXMKaW4gdGhlIGFubm90YXRpb24gZGF0YSBhbmQgZmFzdGEgZmlsZXMgaW4gb3JkZXIgdG8gZXh0cmFjdCB0aGUgZGF0YSBvZgppbnRlcmVzdC4KClNhZGx5LCBJIHdpbGwgbmVlZCB0byByZWFkIGluIHRoZSBhbm5vdGF0aW9ucyBmb3IKYnJhemlsaWVuc2lzL3BhbmFtZW5zaXMgcGFuYW1hIGFuZCBhbnkgb3RoZXIgc2VxdWVuY2VzLiAgQnV0IHRoZQpzZXF1ZW5jZXMgd2hpY2ggYXJlIGRpcmVjdGx5IGV4dHJhY3RlZCBmcm9tIHBhbmFtZW5zaXMgY29sb21iaWEgSSB3aWxsCmJlIGFibGUgdG8gdXNlIHRoZSBzYW1lIGFubm90YXRpb25zLgoKYGBge3Igd3JpdGVfc3RyYWluc30Kd2FudGVkX2lkcyA8LSBjKCJMUEFMMTNfMTIwMDEwOTAwIiwgIkxQQUwxM18zNDAwMTMwMDAiLCAiTFBBTDEzXzAwMDA1NDEwMCIsCiAgICAgICAgICAgICAgICAiTFBBTDEzXzE0MDAwNjEwMCIsICJMUEFMMTNfMTgwMDE4NTAwIiwgIkxQQUwxM18zMjAwMjIzMDAiKQpyZWZlcmVuY2UgPC0gd3JpdGVfY2RzX2VudHJpZXMoImNvbXBhcmVfc3RyYWlucy9scGFuYW1lbnNpc192MzYuZmFzdGEiLCBhbGxfbHBfYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZHMgPSB3YW50ZWRfaWRzLCBvdXRwdXQgPSAiY29tcGFyZV9zdHJhaW5zL2xwYW5hbWVuc2lzX2Nkcy5mYXN0YSIpCm1vZGlmaWVkXzEyNTg4IDwtIHdyaXRlX2Nkc19lbnRyaWVzKCJjb21wYXJlX3N0cmFpbnMvc3RyYWluXzEyNTg4X21vZGlmaWVkX3oyMS5mYXN0YSIsIGFsbF9scF9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9wcmVmaXggPSAiejIxIiwgaWRzID0gd2FudGVkX2lkcywgb3V0cHV0ID0gImNvbXBhcmVfc3RyYWlucy9scGFuYW1lbnNpc196MjFfY2RzLmZhc3RhIikKbW9kaWZpZWRfMjI3MiA8LSB3cml0ZV9jZHNfZW50cmllcygiY29tcGFyZV9zdHJhaW5zL3N0cmFpbl8yMjcyX21vZGlmaWVkX3oyMi5mYXN0YSIsIGFsbF9scF9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lX3ByZWZpeCA9ICJ6MjIiLCBpZHMgPSB3YW50ZWRfaWRzLCBvdXRwdXQgPSAiY29tcGFyZV9zdHJhaW5zL2xwYW5hbWVuc2lzX3oyMl9jZHMuZmFzdGEiKQptb2RpZmllZF8yMTY4IDwtIHdyaXRlX2Nkc19lbnRyaWVzKCJjb21wYXJlX3N0cmFpbnMvc3RyYWluXzIxNjhfbW9kaWZpZWRfejIzLmZhc3RhIiwgYWxsX2xwX2Fubm90LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVfcHJlZml4ID0gInoyMyIsIGlkcyA9IHdhbnRlZF9pZHMsIG91dHB1dCA9ICJjb21wYXJlX3N0cmFpbnMvbHBhbmFtZW5zaXNfejIzX2Nkcy5mYXN0YSIpCm1vZGlmaWVkXzEyNDQ0IDwtIHdyaXRlX2Nkc19lbnRyaWVzKCJjb21wYXJlX3N0cmFpbnMvc3RyYWluXzEyNDQ0X21vZGlmaWVkX3oyNC5mYXN0YSIsIGFsbF9scF9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9wcmVmaXggPSAiejI0IiwgaWRzID0gd2FudGVkX2lkcywgb3V0cHV0ID0gImNvbXBhcmVfc3RyYWlucy9scGFuYW1lbnNpc196MjRfY2RzLmZhc3RhIikKYGBgCgpIYXZpbmcgd3JpdHRlbiB0aGVzZSBmaWxlcywgSSBjb25jYXRlbmF0ZWQgdGhlIHp5bW9kZW1lIENEUyBzZXF1ZW5jZXMKaW50byBpbmRpdmlkdWFsIHNlcXVlbmNlcy9zdHJhaW4gYW5kIHBlcmZvcm1lZCBhIE1TQSBhbmQgTVAgdHJlZSB1c2luZwpjbHVzdGFsb1tyZWZdIGFuZCBQaHlNTFtyZWZdIHZpYSBzZWF2aWV3W3JlZl0uIFNhZGx5LCB0aGVyZSB3ZXJlIG9ubHkKMTIgaW5mb3JtYXRpdmUgc2l0ZXMgaW4gdGhlIDYgenltb2RlbWUgZGVmaW5pbmcgZ2VuZXMuICBIYXBwaWx5LCB0aGUKdHJlZSBnZW5lcmF0ZWQgbG9va3MgcHJldHR5IG11Y2ggZXhhY3RseSBsaWtlIG15IGdlbm9tZS1iYXNlZCB0cmVlLgpBbHNvLCBJIGRpZG4ndCBib3RoZXIgdG8gYWRkIHRoZSBvdGhlciBnZW5vbWVzIGJlY2F1c2Ugd2l0aCBvbmx5IDEyCnZhcmlhbnQgcG9zaXRpb25zIGl0IGRpZCBub3QgZmVlbCBpbnRlcmVzdGluZy4KCiFbUGh5TUwgYmFzZWQgdHJlZSBvZiB0aGUgenltb2RlbWUgc2VxdWVuY2VzXShjb21wYXJlX3N0cmFpbnMvYWxsLVBoeU1MX3RyZWUuc3ZnKQoKIyBTTlAgcHJvZmlsZXMKCk92ZXIgdGhlIGxhc3QgY291cGxlIG9mIHdlZWtzLCBJIHJlZGlkIGFsbCB0aGUgdmFyaWFudCBzZWFyY2hlcyB3aXRoIGEKbmV3ZXIsIChJIHRoaW5rKSBtb3JlIHNlbnNpdGl2ZSBhbmQgbW9yZSBzcGVjaWZpYyB2YXJpYW50IHRvb2wuICBJbgphZGRpdGlvbiBJIGNoYW5nZWQgbXkgc2NyaXB0IHdoaWNoIGludGVycHJldHMgdGhlIHJlc3VsdHMgc28gdGhhdCBpdAppcyBhYmxlIHRvIGV4dHJhY3QgYW55IHRhZ3MgZnJvbSBpdCwgaW5zdGVhZCBvZiBqdXN0IHRoZSBvbmUgb3IgdHdvCnRoYXQgbXkgcHJldmlvdXMgc2NyaXB0IGhhbmRsZWQuICBJbiBhZGRpdGlvbiwgYXQgbGVhc3QgaW4gdGhlb3J5IGl0CmlzIG5vdyBhYmxlIHRvIHByb3ZpZGUgdGhlIHNldCBvZiBhbWlubyBhY2lkIHN1YnN0aXR1dGlvbnMgZm9yIGV2ZXJ5CmdlbmUgaW4gc3BlY2llcyB3aXRob3V0IG9yIHdpdGggaW50cm9ucyAobm90IHJlYWxseSByZWxldmFudCBmb3IKTGVpc2htYW5pYSBwYW5hbWVuc2lzKS4KCkhvd2V2ZXIsIGFzIG9mIHRoaXMgd3JpdGluZywgSSBoYXZlIG5vdCByZS1wZXJmb3JtZWQgdGhlIHNhbWUgdGFza3MKd2l0aCB0aGUgMjAxNiBkYXRhLCBwcmltYXJpbHkgYmVjYXVzZSBpdCB3aWxsIHJlcXVpcmUgcmVtYXBwaW5nIGFsbCBvZgp0aGUgc2FtcGxlcy4gIEFzIGEgcmVzdWx0LCBmb3IgdGhlIG1vbWVudCBJIGNhbm5vdCBjb21iaW5lIHRoZSBvbGRlcgphbmQgbmV3ZXIgc2FtcGxlcy4gIFRodXMsIGFueSBvZiB0aGUgZm9sbG93aW5nIGJsb2NrcyB3aGljaCB1c2UgdGhlCjIwMTYgZGF0YSBhcmUgY3VycmVudGx5IGRpc2FibGVkLgoKTm90ZSB0aGF0IHRoZSBjcmVhdGlvbiBvZiB0aGUgb2xkX3NucHMgYW5kIG5ld19zbnBzIGRhdGFzdHJ1Y3R1cmVzIGhhcwpiZWVuIG1vdmVkIHRvIHRoZSBkYXRhc3RydWN0dXJlcyBmaWxlLgoKYGBge3IgY29tYmluZV9vbGRfc25wcywgZXZhbD1GQUxTRX0KYm90aF9ub3JtIDwtIHNldF9leHB0X2NvbmRpdGlvbnMoYm90aF9zbnBzLCBmYWN0ID0gImtubnYyY2xhc3NpZmljYXRpb24iKQoKIyMgc3RyYWlucyA8LSBib3RoX25vcm1bWyJkZXNpZ24iXV1bWyJzdHJhaW4iXV0KYm90aF9zdHJhaW4gPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhib3RoX25vcm0sIGZhY3QgPSAic3RyYWluIikKYGBgCgpJIGFtIGhhdmluZyB0cm91YmxlIHJlY29uY2lsaW5nIG15ICdieS1leWUnIG9ic2VydmF0aW9ucyBmb3Igc2FtcGxlClRNUkMyMDAwMSBhbmQgdGhvc2UgcHJvdmlkZWQgYnkgdGhlIHBsb3RzIHdoaWNoIGZvbGxvdy4gIEkgdGhpbmsgdGhhdApzYW1wbGUgc2hvdWxkIGNvbXBsZXRlbHkgY2x1c3RlciB3aXRoIHRoZSBvdGhlciAyLjMgc2FtcGxlcywgYnV0IGl0CmRvZXMgbm90LgoKYGBge3IgdHJhY2VfdG1yYzIwMDAwMX0KCmBgYAoKVGhlIGRhdGEgc3RydWN0dXJlICdib3RoX25vcm0nIG5vdyBjb250YWlucyBvdXIgMjAxNiBkYXRhIGFsb25nIHdpdGgKdGhlIG5ld2VyIGRhdGEgY29sbGVjdGVkIHNpbmNlIDIwMTkuCgojIyBQbG90IG9mIFNOUCBwcm9maWxlcyBmb3Igenltb2RlbWVzCgpUaGUgZm9sbG93aW5nIHBsb3Qgc2hvd3MgdGhlIFNOUCBwcm9maWxlcyBvZiBhbGwgc2FtcGxlcyAob2xkIGFuZCBuZXcpIHdoZXJlIHRoZQpjb2xvcnMgYXQgdGhlIHRvcCBzaG93IGVpdGhlciB0aGUgMi4yIHN0cmFpbnMgKG9yYW5nZSksIDIuMyBzdHJhaW5zIChncmVlbiksIHRoZQpwcmV2aW91cyBzYW1wbGVzIChwdXJwbGUpLCBvciB0aGUgdmFyaW91cyBsYWIgc3RyYWlucyAocGluayBldGMpLgoKYGBge3IgcGxvdHRpbmdfdmFyaWFudHMsIGV2YWw9RkFMU0V9Cm5ld192YXJpYW50X2hlYXRtYXAgPC0gcGxvdF9jb3JoZWF0KG5ld19zbnBzKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvcmF3X3NucF9kaXNoZWF0LnBuZyIsIGhlaWdodCA9IDEyLCB3aWR0aCA9IDEyKQpuZXdfdmFyaWFudF9oZWF0bWFwJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQpuZXdfdmFyaWFudF9oZWF0bWFwJHBsb3QKYGBgCgpUaGUgZnVuY3Rpb24gZ2V0X3NucF9zZXRzKCkgdGFrZXMgdGhlIHByb3ZpZGVkIG1ldGFkYXRhIGZhY3RvciAoaW4KdGhpcyBjYXNlICdjb25kaXRpb24nKSBhbmQgbG9va3MgZm9yIHZhcmlhbnRzIHdoaWNoIGFyZSBleGNsdXNpdmUgdG8KZWFjaCBlbGVtZW50IGluIGl0LiAgSW4gdGhpcyBjYXNlLCB0aGlzIGlzIGxvb2tpbmcgZm9yIGRpZmZlcmVuY2VzCmJldHdlZW4gMi4yIGFuZCAyLjMsIGFzIHdlbGwgYXMgdGhlIHNldCBzaGFyZWQgYW1vbmcgdGhlbS4KCmBgYHtyIGdldF9zbnBfc2V0czF9CnNucF9zZXRzIDwtIGdldF9zbnBfc2V0cyhib3RoX3NucHMsIGZhY3RvciA9ICJjb25kaXRpb24iKQpCaW9iYXNlOjphbm5vdGF0aW9uKGxwX3ByZXZpb3VzJGV4cHJlc3Npb25zZXQpID0gQmlvYmFzZTo6YW5ub3RhdGlvbihscF9leHB0JGV4cHJlc3Npb25zZXQpCmJvdGhfZXhwdCA8LSBjb21iaW5lX2V4cHRzKGxwX2V4cHQsIGxwX3ByZXZpb3VzKQoKc25wX2dlbmVzIDwtIHNtKHNucHNfdnNfZ2VuZXMoYm90aF9leHB0LCBzbnBfc2V0cywgZXhwdF9uYW1lX2NvbCA9ICJjaHJvbW9zb21lIikpCiMjIEkgdGhpbmsgd2UgaGF2ZSBzb21lIG1ldHJpY3MgaGVyZSB3ZSBjYW4gcGxvdC4uLgpzbnBfc3Vic2V0IDwtIHNucF9zdWJzZXRfZ2VuZXMoCiAgYm90aF9leHB0LCBib3RoX3NucHMsCiAgZ2VuZXMgPSBjKCJMUEFMMTNfMTIwMDEwOTAwIiwgIkxQQUwxM18zNDAwMTMwMDAiLCAiTFBBTDEzXzAwMDA1NDEwMCIsCiAgICAgICAgICAgICJMUEFMMTNfMTQwMDA2MTAwIiwgIkxQQUwxM18xODAwMTg1MDAiLCAiTFBBTDEzXzMyMDAyMjMwMCIpKQp6eW1vX2hlYXQgPC0gcGxvdF9zYW1wbGVfaGVhdG1hcChzbnBfc3Vic2V0LCByb3dfbGFiZWwgPSByb3duYW1lcyhleHBycyhzbnBfc3Vic2V0KSkpCnp5bW9faGVhdApgYGAKCiMjIENvbXBhcmUgdmFyaWFudHMgdG8gREUgZ2VuZXMKCk5hamliIGhhcyBhc2tlZCBhIGZldyB0aW1lcyBhYm91dCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFudHMKYW5kIERFIGdlbmVzLiAgSW4gc3Vic2VxdWVudCBjb252ZXJzYXRpb25zIEkgZmlndXJlZCBvdXQgd2hhdCBoZQpyZWFsbHkgd2FudHMgdG8gbGVhcm4gaXMgdmFyaWFudHMgaW4gdGhlIFVUUiAobW9zdCBsaWtlbHkgNScpIHdoaWNoCm1pZ2h0IGFmZmVjdCBleHByZXNzaW9uIG9mIGdlbmVzLiAgVGhlIGZvbGxvd2luZyBleHBsaWNpdGx5IGRvZXMgbm90CmhlbHAgdGhpcyBxdWVzdGlvbiwgYnV0IGlzIGEgcGFyYWxvZzogaXMgdGhlcmUgYSByZWxhdGlvbnNoaXAgYmV0d2Vlbgp2YXJpYW50cyBpbiB0aGUgQ0RTIGFuZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbj8KCiMjIyBDb2xsZWN0IERFIGRhdGEKCkluIG9yZGVyIHRvIGRvIHRoaXMgY29tcGFyaXNvbiwgd2UgbmVlZCB0byByZWxvYWQgc29tZSBvZiB0aGUgREUgcmVzdWx0cy4KCmBgYHtyIHJlbG9hZF9kZV9yZXN1bHRzLCBldmFsPUZBTFNFfQpyZGEgPC0gZ2x1ZTo6Z2x1ZSgicmRhL3p5bW9fdGFibGVzX25vYmF0Y2gtdnt2ZXJ9LnJkYSIpCnZhcm5hbWUgPC0gZ3N1Yih4ID0gYmFzZW5hbWUocmRhKSwgcGF0dGVybiA9ICJcXC5yZGEiLCByZXBsYWNlbWVudCA9ICIiKQpsb2FkZWQgPC0gbG9hZChmaWxlID0gcmRhKQp6eV9kZiA8LSBnZXQwKHZhcm5hbWUpW1siZGF0YSJdXVtbInp5bW9kZW1lIl1dCmBgYAoKYGBge3IgdmFyaWFudHNfdnNfZGUsIGV2YWw9RkFMU0V9CnZhcnNfZGYgPC0gZGF0YS5mcmFtZShJRCA9IG5hbWVzKHNucF9nZW5lcyRzdW1tYXJ5X2J5X2dlbmUpLCB2YXJpYW50cyA9IGFzLm51bWVyaWMoc25wX2dlbmVzJHN1bW1hcnlfYnlfZ2VuZSkpCnZhcnNfZGZbWyJ2YXJpYW50cyJdXSA8LSBsb2cyKHZhcnNfZGZbWyJ2YXJpYW50cyJdXSArIDEpCnZhcnNfYnlfZGVfZ2VuZSA8LSBtZXJnZSh6eV9kZiwgdmFyc19kZiwgYnkueD0icm93Lm5hbWVzIiwgYnkueT0iSUQiKQpjb3IudGVzdCh2YXJzX2J5X2RlX2dlbmUkZGVzZXFfbG9nZmMsIHZhcnNfYnlfZGVfZ2VuZSR2YXJpYW50cykKdmFyaWFudHNfd3J0X2xvZ2ZjIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIodmFyc19ieV9kZV9nZW5lWywgYygiZGVzZXFfbG9nZmMiLCAidmFyaWFudHMiKV0pCnZhcmlhbnRzX3dydF9sb2dmYyRzY2F0dGVyCiMjIEl0IGxvb2tzIGxpa2UgdGhlcmUgbWlnaHQgYmUgc29tZSBnZW5lcyBvZiBpbnRlcmVzdCwgZXZlbiB0aG91Z2ggdGhpcyBpcyBub3QgYWN0dWFsbHkKIyMgdGhlIHF1ZXN0aW9uIG9mIGludGVyZXN0LgpgYGAKCkRpZG4ndCBJIGNyZWF0ZSBhIHNldCBvZiBkZW5zaXRpZXMgYnkgY2hyb21vc29tZT8KT2ggSSB0aGluayB0aGV5IGNvbWUgaW4gZnJvbSBnZXRfc25wX3NldHMoKQoKIyMgU05QUyBhc3NvY2lhdGVkIHdpdGggY2xpbmljYWwgcmVzcG9uc2UgaW4gdGhlIFRNUkMgc2FtcGxlcwoKYGBge3Igc25wX2NsaW5pY2FsLCBldmFsPUZBTFNFfQpjbGluaWNhbF9zZXRzIDwtIGdldF9zbnBfc2V0cyhuZXdfc25wcywgZmFjdG9yID0gImNsaW5pY2FscmVzcG9uc2UiKQoKZGVuc2l0eV92ZWMgPC0gY2xpbmljYWxfc2V0c1tbImRlbnNpdHkiXV0KY2hyb21vc29tZV9pZHggPC0gZ3JlcChwYXR0ZXJuID0gIkxwYUwiLCB4ID0gbmFtZXMoZGVuc2l0eV92ZWMpKQpkZW5zaXR5X2RmIDwtIGFzLmRhdGEuZnJhbWUoZGVuc2l0eV92ZWNbY2hyb21vc29tZV9pZHhdKQpkZW5zaXR5X2RmW1siY2hyIl1dIDwtIHJvd25hbWVzKGRlbnNpdHlfZGYpCmNvbG5hbWVzKGRlbnNpdHlfZGYpIDwtIGMoImRlbnNpdHlfdmVjIiwgImNociIpCmdncGxvdChkZW5zaXR5X2RmLCBhZXNfc3RyaW5nKHggPSAiY2hyIiwgeSA9ICJkZW5zaXR5X3ZlYyIpKSArCiAgZ2dwbG90Mjo6Z2VvbV9jb2woKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3VyID0gImJsYWNrIiksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUpKQojIyBjbGluaWNhbF93cml0dGVuIDwtIHdyaXRlX3ZhcmlhbnRzKG5ld19zbnBzKQpgYGAKCiMjIyBDcm9zcyByZWZlcmVuY2UgdGhlc2UgdmFyaWFudHMgYnkgZ2VuZQoKYGBge3Igc25wX2NsYXNzaWZpY2F0aW9ucywgZXZhbD1GQUxTRX0KY2xpbmljYWxfZ2VuZXMgPC0gc25wc192c19nZW5lcyhscF9leHB0LCBjbGluaWNhbF9zZXRzLCBleHB0X25hbWVfY29sID0gImNocm9tb3NvbWUiKQoKc25wX2RlbnNpdHkgPC0gbWVyZ2UoYXMuZGF0YS5mcmFtZShjbGluaWNhbF9nZW5lc1tbInN1bW1hcnlfYnlfZ2VuZSJdXSksCiAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoZkRhdGEobHBfZXhwdCkpLAogICAgICAgICAgICAgICAgICAgICBieSA9ICJyb3cubmFtZXMiKQpzbnBfZGVuc2l0eSA8LSBzbnBfZGVuc2l0eVssIGMoMSwgMiwgNCwgMTUpXQpjb2xuYW1lcyhzbnBfZGVuc2l0eSkgPC0gYygibmFtZSIsICJzbnBzIiwgInByb2R1Y3QiLCAibGVuZ3RoIikKc25wX2RlbnNpdHlbWyJwcm9kdWN0Il1dIDwtIHRvbG93ZXIoc25wX2RlbnNpdHlbWyJwcm9kdWN0Il1dKQpzbnBfZGVuc2l0eVtbImxlbmd0aCJdXSA8LSBhcy5udW1lcmljKHNucF9kZW5zaXR5W1sibGVuZ3RoIl1dKQpzbnBfZGVuc2l0eVtbImRlbnNpdHkiXV0gPC0gc25wX2RlbnNpdHlbWyJzbnBzIl1dIC8gc25wX2RlbnNpdHlbWyJsZW5ndGgiXV0Kc25wX2lkeCA8LSBvcmRlcihzbnBfZGVuc2l0eVtbImRlbnNpdHkiXV0sIGRlY3JlYXNpbmcgPSBUUlVFKQpzbnBfZGVuc2l0eSA8LSBzbnBfZGVuc2l0eVtzbnBfaWR4LCBdCgpyZW1vdmVycyA8LSBjKCJhbWFzdGluIiwgImdwNjMiLCAibGVpc2htYW5vbHlzaW4iKQpmb3IgKHIgaW4gcmVtb3ZlcnMpIHsKICBkcm9wX2lkeCA8LSBncmVwbChwYXR0ZXJuID0gciwgeCA9IHNucF9kZW5zaXR5W1sicHJvZHVjdCJdXSkKICBzbnBfZGVuc2l0eSA8LSBzbnBfZGVuc2l0eVshZHJvcF9pZHgsIF0KfQojIyBGaWx0ZXIgdGhlc2UgZm9yIFtBfGFdbWFzdGluIGdwNjMgTGVpc2htYW5vbHlzaW4KYGBgCgoKYGBge3Igc25wX2ludGVyc2VjdGlvbnMsIGV2YWw9RkFMU0V9CmNsaW5pY2FsX3NucHMgPC0gc25wc19pbnRlcnNlY3Rpb25zKGxwX2V4cHQsIGNsaW5pY2FsX3NldHMsIGNocl9jb2x1bW4gPSAiY2hyb21vc29tZSIpCgpmYWlsX3JlZl9zbnBzIDwtIGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfc25wc1tbImludGVycyJdXVtbImZhaWx1cmUsIHJlZmVyZW5jZSBzdHJhaW4iXV0pCmZhaWxfcmVmX3NucHMgPC0gcmJpbmQoZmFpbF9yZWZfc25wcywKICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJpbnRlcnMiXV1bWyJmYWlsdXJlIl1dKSkKY3VyZV9zbnBzIDwtIGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfc25wc1tbImludGVycyJdXVtbImN1cmUiXV0pCgpoZWFkKGZhaWxfcmVmX3NucHMpCmhlYWQoY3VyZV9zbnBzKQp3cml0ZS5jc3YoZmlsZT0iY3N2L2N1cmVfdmFyaWFudHMudHh0IiwgeD1yb3duYW1lcyhjdXJlX3NucHMpKQp3cml0ZS5jc3YoZmlsZT0iY3N2L2ZhaWxfdmFyaWFudHMudHh0IiwgeD1yb3duYW1lcyhmYWlsX3JlZl9zbnBzKSkKCmFubm90IDwtIGZEYXRhKGxwX2V4cHQpCmNsaW5pY2FsX2ludGVyZXN0IDwtIGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfc25wc1tbImdlbmVfc3VtbWFyaWVzIl1dW1siY3VyZSJdXSkKY2xpbmljYWxfaW50ZXJlc3QgPC0gbWVyZ2UoY2xpbmljYWxfaW50ZXJlc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfc25wc1tbImdlbmVfc3VtbWFyaWVzIl1dW1siZmFpbHVyZSwgcmVmZXJlbmNlIHN0cmFpbiJdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKGNsaW5pY2FsX2ludGVyZXN0KSA8LSBjbGluaWNhbF9pbnRlcmVzdFtbIlJvdy5uYW1lcyJdXQpjbGluaWNhbF9pbnRlcmVzdFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmNvbG5hbWVzKGNsaW5pY2FsX2ludGVyZXN0KSA8LSBjKCJjdXJlX3NucHMiLCJmYWlsX3NucHMiKQphbm5vdCA8LSBtZXJnZShhbm5vdCwgY2xpbmljYWxfaW50ZXJlc3QsIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKGFubm90KSA8LSBhbm5vdFtbIlJvdy5uYW1lcyJdXQphbm5vdFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmZEYXRhKGxwX2V4cHQkZXhwcmVzc2lvbnNldCkgPC0gYW5ub3QKYGBgCgojIFp5bW9kZW1lIGZvciBuZXcgc2FtcGxlcwoKVGhlIGhlYXRtYXAgcHJvZHVjZWQgaGVyZSBzaG91bGQgc2hvdyB0aGUgdmFyaWFudHMgb25seSBmb3IgdGhlIHp5bW9kZW1lIGdlbmVzLgoKIyMgSHVudCBmb3Igc25wIGNsdXN0ZXJzCgpJIGFtIHRoaW5raW5nIHRoYXQgaWYgd2UgZmluZCBjbHVzdGVycyBvZiBsb2NhdGlvbnMgd2hpY2ggYXJlIHZhcmlhbnQsIHRoYXQKbWlnaHQgcHJvdmlkZSBzb21lIFBDUiB0ZXN0aW5nIHBvc3NpYmlsaXRpZXMuCgpgYGB7ciBuZXdfenltbywgZXZhbD1GQUxTRX0KIyMgRHJvcCB0aGUgMi4xLCAyLjQsIHVua25vd24sIGFuZCBudWxsCnBydW5lZF9zbnBzIDwtIHN1YnNldF9leHB0KG5ld19zbnBzLCBzdWJzZXQ9ImNvbmRpdGlvbj09J3oyLjInfGNvbmRpdGlvbj09J3oyLjMnIikKbmV3X3NldHMgPC0gZ2V0X3NucF9zZXRzKHBydW5lZF9zbnBzLCBmYWN0b3IgPSAienltb2RlbWVjYXRlZ29yaWNhbCIpCnN1bW1hcnkobmV3X3NldHMpCiMjIDEwMDAwMDA6IDIuMgojIyAwMTAwMDAwOiAyLjMKCnN1bW1hcnkobmV3X3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dW1siMTAiXV0pCndyaXRlLmNzdihmaWxlPSJjc3YvdmFyaWFudHNfMjIuY3N2IiwgeD1uZXdfc2V0c1tbImludGVyc2VjdGlvbnMiXV1bWyIxMCJdXSkKc3VtbWFyeShuZXdfc2V0c1tbImludGVyc2VjdGlvbnMiXV1bWyIwMSJdXSkKd3JpdGUuY3N2KGZpbGU9ImNzdi92YXJpYW50c18yMy5jc3YiLCB4PW5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjAxIl1dKQpgYGAKClRodXMgd2Ugc2VlIHRoYXQgdGhlcmUgYXJlIDMsNTUzIHZhcmlhbnRzIGFzc29jaWF0ZWQgd2l0aCAyLjIgYW5kIDgxLDU4OSBhc3NvY2lhdGVkIHdpdGggMi4zLgoKIyMjIEEgc21hbGwgZnVuY3Rpb24gZm9yIHNlYXJjaGluZyBmb3IgcG90ZW50aWFsIFBDUiBwcmltZXJzCgpUaGUgZm9sbG93aW5nIGZ1bmN0aW9uIHVzZXMgdGhlIHBvc2l0aW9uYWwgZGF0YSB0byBsb29rIGZvciBzZXF1ZW50aWFsCm1pc21hdGNoZXMgYXNzb2NpYXRlZCB3aXRoIHp5bW9kZW1lIGluIHRoZSBob3BlcyB0aGF0IHRoZXJlIHdpbGwgYmUKc29tZSByZWdpb25zIHdoaWNoIHdvdWxkIHByb3ZpZGUgZ29vZCBwb3RlbnRpYWwgdGFyZ2V0cyBmb3IgYQpQQ1ItYmFzZWQgYXNzYXkuCgpgYGB7ciBzZXF1ZW50aWFsX3NlYXJjaCwgZXZhbD1GQUxTRX0Kc2VxdWVudGlhbF92YXJpYW50cyA8LSBmdW5jdGlvbihzbnBfc2V0cywgY29uZGl0aW9ucyA9IE5VTEwsIG1pbmltdW0gPSAzLCBtYXhpbXVtX3NlcGFyYXRpb24gPSAzKSB7CiAgaWYgKGlzLm51bGwoY29uZGl0aW9ucykpIHsKICAgIGNvbmRpdGlvbnMgPC0gMQogIH0KICBpbnRlcnNlY3Rpb25fc2V0cyA8LSBzbnBfc2V0c1tbImludGVyc2VjdGlvbnMiXV0KICBpbnRlcnNlY3Rpb25fbmFtZXMgPC0gc25wX3NldHNbWyJzZXRfbmFtZXMiXV0KICBjaG9zZW5faW50ZXJzZWN0aW9uIDwtIDEKICBpZiAoaXMubnVtZXJpYyhjb25kaXRpb25zKSkgewogICAgY2hvc2VuX2ludGVyc2VjdGlvbiA8LSBjb25kaXRpb25zCiAgfSBlbHNlIHsKICAgIGludGVyc2VjdGlvbl9pZHggPC0gaW50ZXJzZWN0aW9uX25hbWVzID09IGNvbmRpdGlvbnMKICAgIGNob3Nlbl9pbnRlcnNlY3Rpb24gPC0gbmFtZXMoaW50ZXJzZWN0aW9uX25hbWVzKVtpbnRlcnNlY3Rpb25faWR4XQogIH0KCiAgcG9zc2libGVfcG9zaXRpb25zIDwtIGludGVyc2VjdGlvbl9zZXRzW1tjaG9zZW5faW50ZXJzZWN0aW9uXV0KICBwb3NpdGlvbl90YWJsZSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IHBvc3NpYmxlX3Bvc2l0aW9ucykKICBwYXQgPC0gIl5jaHJfKC4rKV9wb3NfKC4rKV9yZWZfLiokIgogIHBvc2l0aW9uX3RhYmxlW1siY2hyIl1dIDwtIGdzdWIocGF0dGVybiA9IHBhdCwgcmVwbGFjZW1lbnQgPSAiXFwxIiwgeCA9IHJvd25hbWVzKHBvc2l0aW9uX3RhYmxlKSkKICBwb3NpdGlvbl90YWJsZVtbInBvcyJdXSA8LSBhcy5udW1lcmljKGdzdWIocGF0dGVybiA9IHBhdCwgcmVwbGFjZW1lbnQgPSAiXFwyIiwgeCA9IHJvd25hbWVzKHBvc2l0aW9uX3RhYmxlKSkpCiAgcG9zaXRpb25faWR4IDwtIG9yZGVyKHBvc2l0aW9uX3RhYmxlWywgImNociJdLCBwb3NpdGlvbl90YWJsZVssICJwb3MiXSkKICBwb3NpdGlvbl90YWJsZSA8LSBwb3NpdGlvbl90YWJsZVtwb3NpdGlvbl9pZHgsIF0KICBwb3NpdGlvbl90YWJsZVtbImRpc3QiXV0gPC0gMAoKICBsYXN0X2NociA8LSAiIgogIGZvciAociBpbiAxOm5yb3cocG9zaXRpb25fdGFibGUpKSB7CiAgICB0aGlzX2NociA8LSBwb3NpdGlvbl90YWJsZVtyLCAiY2hyIl0KICAgIGlmIChyID09IDEpIHsKICAgICAgcG9zaXRpb25fdGFibGVbciwgImRpc3QiXSA8LSBwb3NpdGlvbl90YWJsZVtyLCAicG9zIl0KICAgICAgbGFzdF9jaHIgPC0gdGhpc19jaHIKICAgICAgbmV4dAogICAgfQogICAgaWYgKHRoaXNfY2hyID09IGxhc3RfY2hyKSB7CiAgICAgIHBvc2l0aW9uX3RhYmxlW3IsICJkaXN0Il0gPC0gcG9zaXRpb25fdGFibGVbciwgInBvcyJdIC0gcG9zaXRpb25fdGFibGVbciAtIDEsICJwb3MiXQogICAgfSBlbHNlIHsKICAgICAgcG9zaXRpb25fdGFibGVbciwgImRpc3QiXSA8LSBwb3NpdGlvbl90YWJsZVtyLCAicG9zIl0KICAgIH0KICAgIGxhc3RfY2hyIDwtIHRoaXNfY2hyCiAgfQoKICAjIyBXb3JraW5nIGludGVyYWN0aXZlbHkgaGVyZS4KCiAgZG91YmxlcyA8LSBwb3NpdGlvbl90YWJsZVtbImRpc3QiXV0gPT0gMQogIGRvdWJsZXMgPC0gcG9zaXRpb25fdGFibGVbZG91YmxlcywgXQogIHdyaXRlLmNzdihkb3VibGVzLCAiZG91Ymxlcy5jc3YiKQoKICBvbmVfYXdheSA8LSBwb3NpdGlvbl90YWJsZVtbImRpc3QiXV0gPT0gMgogIG9uZV9hd2F5IDwtIHBvc2l0aW9uX3RhYmxlW29uZV9hd2F5LCBdCiAgd3JpdGUuY3N2KG9uZV9hd2F5LCAib25lX2F3YXkuY3N2IikKCiAgdHdvX2F3YXkgPC0gcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dID09IDMKICB0d29fYXdheSA8LSBwb3NpdGlvbl90YWJsZVt0d29fYXdheSwgXQogIHdyaXRlLmNzdih0d29fYXdheSwgInR3b19hd2F5LmNzdiIpCgogIGNvbWJpbmVkIDwtIHJiaW5kKGRvdWJsZXMsIG9uZV9hd2F5KQogIGNvbWJpbmVkIDwtIHJiaW5kKGNvbWJpbmVkLCB0d29fYXdheSkKICBwb3NpdGlvbl9pZHggPC0gb3JkZXIoY29tYmluZWRbLCAiY2hyIl0sIGNvbWJpbmVkWywgInBvcyJdKQogIGNvbWJpbmVkIDwtIGNvbWJpbmVkW3Bvc2l0aW9uX2lkeCwgXQoKICB0aGlzX2NociA8LSAiIgogIGZvciAociBpbiAxOm5yb3coY29tYmluZWQpKSB7CiAgICB0aGlzX2NociA8LSBjb21iaW5lZFtyLCAiY2hyIl0KICAgIGlmIChyID09IDEpIHsKICAgICAgY29tYmluZWRbciwgImRpc3RfcGFpciJdIDwtIGNvbWJpbmVkW3IsICJwb3MiXQogICAgICBsYXN0X2NociA8LSB0aGlzX2NocgogICAgICBuZXh0CiAgICB9CiAgICBpZiAodGhpc19jaHIgPT0gbGFzdF9jaHIpIHsKICAgICAgY29tYmluZWRbciwgImRpc3RfcGFpciJdIDwtIGNvbWJpbmVkW3IsICJwb3MiXSAtIGNvbWJpbmVkW3IgLSAxLCAicG9zIl0KICAgIH0gZWxzZSB7CiAgICAgIGNvbWJpbmVkW3IsICJkaXN0X3BhaXIiXSA8LSBjb21iaW5lZFtyLCAicG9zIl0KICAgIH0KICAgIGxhc3RfY2hyIDwtIHRoaXNfY2hyCiAgfQoKICBkaXN0X3BhaXJfbWF4aW11bSA8LSAxMDAwCiAgZGlzdF9wYWlyX21pbmltdW0gPC0gMjAwCiAgZGlzdF9wYWlyX2lkeCA8LSBjb21iaW5lZFtbImRpc3RfcGFpciJdXSA8PSBkaXN0X3BhaXJfbWF4aW11bSAmCiAgICBjb21iaW5lZFtbImRpc3RfcGFpciJdXSA+PSBkaXN0X3BhaXJfbWluaW11bQogIHJlbWFpbmluZyA8LSBjb21iaW5lZFtkaXN0X3BhaXJfaWR4LCBdCiAgbm9fd2Vha19pZHggPC0gZ3JlcGwocGF0dGVybj0icmVmXyhHfEMpIiwgeD1yb3duYW1lcyhyZW1haW5pbmcpKQogIHJlbWFpbmluZyA8LSByZW1haW5pbmdbbm9fd2Vha19pZHgsIF0KCiAgcHJpbnQoaGVhZCh0YWJsZShwb3NpdGlvbl90YWJsZVtbImRpc3QiXV0pKSkKICBzZXF1ZW50aWFscyA8LSBwb3NpdGlvbl90YWJsZVtbImRpc3QiXV0gPD0gbWF4aW11bV9zZXBhcmF0aW9uCiAgbWVzc2FnZSgiVGhlcmUgYXJlICIsIHN1bShzZXF1ZW50aWFscyksICIgY2FuZGlkYXRlIHJlZ2lvbnMuIikKCiAgIyMgVGhlIGZvbGxvd2luZyBjYW4gdGVsbCBtZSBob3cgbWFueSBydW5zIG9mIGVhY2ggbGVuZ3RoIG9jY3VycmVkLCB0aGF0IGlzIG5vdCBxdWl0ZSB3aGF0IEkgd2FudC4KICAjIyBOb3cgdXNlIHJ1biBsZW5ndGggZW5jb2RpbmcgdG8gZmluZCB0aGUgc2V0IG9mIHNlcXVlbnRpYWwgc2VxdWVudGlhbHMhCiAgcmxlX3Jlc3VsdCA8LSBybGUoc2VxdWVudGlhbHMpCiAgcmxlX3ZhbHVlcyA8LSBybGVfcmVzdWx0W1sidmFsdWVzIl1dCiAgIyMgVGhlIGZvbGxvd2luZyBsaW5lIGlzIGVxdWl2YWxlbnQgdG8ganVzdCBsZWF2aW5nIHZhbHVlcyBhbG9uZToKICAjIyB0cnVlX3ZhbHVlcyA8LSBybGVfcmVzdWx0W1sidmFsdWVzIl1dID09IFRSVUUKICBybGVfbGVuZ3RocyA8LSBybGVfcmVzdWx0W1sibGVuZ3RocyJdXQogIHRydWVfc2VxdWVudGlhbHMgPC0gcmxlX2xlbmd0aHNbcmxlX3ZhbHVlc10KICBybGVfaWR4IDwtIGN1bXN1bShybGVfbGVuZ3Rocylbd2hpY2gocmxlX3ZhbHVlcyldCgogIHBvc2l0aW9uX3RhYmxlW1sibGFzdF9zZXF1ZW50aWFsIl1dIDwtIDAKICBjb3VudCA8LSAwCiAgZm9yIChyIGluIHJsZV9pZHgpIHsKICAgIGNvdW50IDwtIGNvdW50ICsgMQogICAgcG9zaXRpb25fdGFibGVbciwgImxhc3Rfc2VxdWVudGlhbCJdIDwtIHRydWVfc2VxdWVudGlhbHNbY291bnRdCiAgfQogIG1lc3NhZ2UoIlRoZSBtYXhpbXVtIHNlcXVlbnRpYWwgc2V0IGlzOiAiLCBtYXgocG9zaXRpb25fdGFibGVbWyJsYXN0X3NlcXVlbnRpYWwiXV0pLCAiLiIpCgogIHdhbnRlZF9pZHggPC0gcG9zaXRpb25fdGFibGVbWyJsYXN0X3NlcXVlbnRpYWwiXV0gPj0gbWluaW11bQogIHdhbnRlZCA8LSBwb3NpdGlvbl90YWJsZVt3YW50ZWRfaWR4LCBjKCJjaHIiLCAicG9zIildCiAgcmV0dXJuKHdhbnRlZCkKfQoKenltbzIyX3NlcXVlbnRpYWxzIDwtIHNlcXVlbnRpYWxfdmFyaWFudHMobmV3X3NldHMsIGNvbmRpdGlvbnMgPSAiejIyIiwgbWluaW11bT0xLCBtYXhpbXVtX3NlcGFyYXRpb249MikKZGltKHp5bW8yMl9zZXF1ZW50aWFscykKIyMgNyBjYW5kaWRhdGUgcmVnaW9ucyBmb3Igenltb2RlbWUgMi4yIC0tIHRodXMgSSBhbSBiZXR0aW5nIHRoYXQgdGhlIHJlZmVyZW5jZSBzdHJhaW4gaXMgYSAyLjIKenltbzIzX3NlcXVlbnRpYWxzIDwtIHNlcXVlbnRpYWxfdmFyaWFudHMobmV3X3NldHMsIGNvbmRpdGlvbnMgPSAiejIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluaW11bSA9IDIsIG1heGltdW1fc2VwYXJhdGlvbiA9IDIpCmRpbSh6eW1vMjNfc2VxdWVudGlhbHMpCiMjIEluIGNvbnRyYXN0LCB0aGVyZSBhcmUgbG90cyAoNTg3KSBvZiBpbnRlcmVzdGluZyByZWdpb25zIGZvciAyLjMhCmBgYAoKIyMjIEV4dHJhY3QgYSBwcm9taXNpbmcgcmVnaW9uIGZyb20gdGhlIGdlbm9tZQoKVGhlIGZpcnN0IDQgY2FuZGlkYXRlIHJlZ2lvbnMgZnJvbSBteSBzZXQgb2YgcmVtYWluaW5nOgoqIENociAgICAgICBQb3MuICAgRGlzdGFuY2UKKiBMcGFMMTMtMTUgMjM4NDMzIDQ0OAoqIExwYUwxMy0xOCAxNDI4NDQgNjEzCiogTHBhTDEzLTI5IDgzMDM0MiAyNTIKKiBMcGFMMTMtMzMgMTMzMTUwNyA4NDMKCkxldHMgZGVmaW5lIGEgY291cGxlIG9mIHRlcm1zOgoqIFRoaXJkOiBFYWNoIG9mIHRoZSA0IGFib3ZlIHBvc2l0aW9ucy4KKiBTZWNvbmQ6IFRoaXJkIC0gRGlzdGFuY2UKKiBFbmQ6IFRoaXJkICsgUHJpbWVyTGVuCiogU3RhcnQ6IFNlY29uZCAtIFByaW1lcmxlbgoKSW4gZWFjaCBpbnN0YW5jZSwgdGhlc2UgYXJlIHRoZSBsYXN0IHBvc2l0aW9ucywgc28gd2Ugd2FudCB0byBncmFiIHRocmVlIHRoaW5nczoKCiogVGhlIGVudGlyZSByZWdpb24gZnJvbSBFbmQgLT4gU3RhcnQsIHRoaXMgd2F5IHdlIGNhbiBoYXZlIGEgcXVpY2sgc2FuaXR5IGNoZWNrLgoqIFN0YXJ0IC0+IFNlY29uZC4KKiAoVGhpcmQgLT4gRW5kKSA8LSBSZXZlcnNlIGNvbXBsZW1lbnRlZAoKYGBge3IgZXh0cmFjdF9ic2dlbm9tZSwgZXZhbD1GQUxTRX0KIyMgKiBMcGFMMTMtMTUgMjM4NDMzIDQ0OApmaXJzdF9jYW5kaWRhdGVfY2hyIDwtIGdlbm9tZVtbIkxwYUwxM18xNSJdXQpwcmltZXJfbGVuZ3RoIDwtIDIyCmFtcGxpY29uX2xlbmd0aCA8LSA0NDgKZmlyc3RfY2FuZGlkYXRlX3RoaXJkIDwtIDIzODQzMwpmaXJzdF9jYW5kaWRhdGVfc2Vjb25kIDwtIGZpcnN0X2NhbmRpZGF0ZV90aGlyZCAtIGFtcGxpY29uX2xlbmd0aApmaXJzdF9jYW5kaWRhdGVfc3RhcnQgPC0gZmlyc3RfY2FuZGlkYXRlX3NlY29uZCAtIHByaW1lcl9sZW5ndGgKZmlyc3RfY2FuZGlkYXRlX2VuZCA8LSBmaXJzdF9jYW5kaWRhdGVfdGhpcmQgKyBwcmltZXJfbGVuZ3RoCmZpcnN0X2NhbmRpZGF0ZV9yZWdpb24gPC0gc3Vic2VxKGZpcnN0X2NhbmRpZGF0ZV9jaHIsIGZpcnN0X2NhbmRpZGF0ZV9zdGFydCwgZmlyc3RfY2FuZGlkYXRlX2VuZCkKZmlyc3RfY2FuZGlkYXRlX3JlZ2lvbgpmaXJzdF9jYW5kaWRhdGVfNXAgPC0gc3Vic2VxKGZpcnN0X2NhbmRpZGF0ZV9jaHIsIGZpcnN0X2NhbmRpZGF0ZV9zdGFydCwgZmlyc3RfY2FuZGlkYXRlX3NlY29uZCkKYXMuY2hhcmFjdGVyKGZpcnN0X2NhbmRpZGF0ZV81cCkKZmlyc3RfY2FuZGlkYXRlXzNwIDwtIHNwZ3M6OnJldmVyc2VDb21wbGVtZW50KHN1YnNlcShmaXJzdF9jYW5kaWRhdGVfY2hyLCBmaXJzdF9jYW5kaWRhdGVfdGhpcmQsIGZpcnN0X2NhbmRpZGF0ZV9lbmQpKQpmaXJzdF9jYW5kaWRhdGVfM3AKCiMjICogTHBhTDEzLTE4IDE0Mjg0NCA2MTMKc2Vjb25kX2NhbmRpZGF0ZV9jaHIgPC0gZ2Vub21lW1siTHBhTDEzXzE4Il1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDYxMwpzZWNvbmRfY2FuZGlkYXRlX3RoaXJkIDwtIDE0Mjg0NApzZWNvbmRfY2FuZGlkYXRlX3NlY29uZCA8LSBzZWNvbmRfY2FuZGlkYXRlX3RoaXJkIC0gYW1wbGljb25fbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfc3RhcnQgPC0gc2Vjb25kX2NhbmRpZGF0ZV9zZWNvbmQgLSBwcmltZXJfbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfZW5kIDwtIHNlY29uZF9jYW5kaWRhdGVfdGhpcmQgKyBwcmltZXJfbGVuZ3RoCnNlY29uZF9jYW5kaWRhdGVfcmVnaW9uIDwtIHN1YnNlcShzZWNvbmRfY2FuZGlkYXRlX2Nociwgc2Vjb25kX2NhbmRpZGF0ZV9zdGFydCwgc2Vjb25kX2NhbmRpZGF0ZV9lbmQpCnNlY29uZF9jYW5kaWRhdGVfcmVnaW9uCnNlY29uZF9jYW5kaWRhdGVfNXAgPC0gc3Vic2VxKHNlY29uZF9jYW5kaWRhdGVfY2hyLCBzZWNvbmRfY2FuZGlkYXRlX3N0YXJ0LCBzZWNvbmRfY2FuZGlkYXRlX3NlY29uZCkKYXMuY2hhcmFjdGVyKHNlY29uZF9jYW5kaWRhdGVfNXApCnNlY29uZF9jYW5kaWRhdGVfM3AgPC0gc3Bnczo6cmV2ZXJzZUNvbXBsZW1lbnQoc3Vic2VxKHNlY29uZF9jYW5kaWRhdGVfY2hyLCBzZWNvbmRfY2FuZGlkYXRlX3RoaXJkLCBzZWNvbmRfY2FuZGlkYXRlX2VuZCkpCnNlY29uZF9jYW5kaWRhdGVfM3AKCgojIyAqIExwYUwxMy0yOSA4MzAzNDIgMjUyCnRoaXJkX2NhbmRpZGF0ZV9jaHIgPC0gZ2Vub21lW1siTHBhTDEzXzI5Il1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDI1Mgp0aGlyZF9jYW5kaWRhdGVfdGhpcmQgPC0gODMwMzQyCnRoaXJkX2NhbmRpZGF0ZV9zZWNvbmQgPC0gdGhpcmRfY2FuZGlkYXRlX3RoaXJkIC0gYW1wbGljb25fbGVuZ3RoCnRoaXJkX2NhbmRpZGF0ZV9zdGFydCA8LSB0aGlyZF9jYW5kaWRhdGVfc2Vjb25kIC0gcHJpbWVyX2xlbmd0aAp0aGlyZF9jYW5kaWRhdGVfZW5kIDwtIHRoaXJkX2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKdGhpcmRfY2FuZGlkYXRlX3JlZ2lvbiA8LSBzdWJzZXEodGhpcmRfY2FuZGlkYXRlX2NociwgdGhpcmRfY2FuZGlkYXRlX3N0YXJ0LCB0aGlyZF9jYW5kaWRhdGVfZW5kKQp0aGlyZF9jYW5kaWRhdGVfcmVnaW9uCnRoaXJkX2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEodGhpcmRfY2FuZGlkYXRlX2NociwgdGhpcmRfY2FuZGlkYXRlX3N0YXJ0LCB0aGlyZF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIodGhpcmRfY2FuZGlkYXRlXzVwKQp0aGlyZF9jYW5kaWRhdGVfM3AgPC0gc3Bnczo6cmV2ZXJzZUNvbXBsZW1lbnQoc3Vic2VxKHRoaXJkX2NhbmRpZGF0ZV9jaHIsIHRoaXJkX2NhbmRpZGF0ZV90aGlyZCwgdGhpcmRfY2FuZGlkYXRlX2VuZCkpCnRoaXJkX2NhbmRpZGF0ZV8zcAojIyBZb3UgYXJlIGEgZ2FyYmFnZSBwb2x5cHlyaW1pZGluZSB0cmFjdC4KIyMgV2hpY2ggaXMgYWN0dWFsbHkgaW50ZXJlc3RpbmcgaWYgdGhlIG11dGF0aW9ucyBtZXNzIGl0IHVwLgoKCiMjICogTHBhTDEzLTMzIDEzMzE1MDcgODQzCmZvdXJ0aF9jYW5kaWRhdGVfY2hyIDwtIGdlbm9tZVtbIkxwYUwxM18zMyJdXQpwcmltZXJfbGVuZ3RoIDwtIDIyCmFtcGxpY29uX2xlbmd0aCA8LSA4NDMKZm91cnRoX2NhbmRpZGF0ZV90aGlyZCA8LSAxMzMxNTA3CmZvdXJ0aF9jYW5kaWRhdGVfc2Vjb25kIDwtIGZvdXJ0aF9jYW5kaWRhdGVfdGhpcmQgLSBhbXBsaWNvbl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9zdGFydCA8LSBmb3VydGhfY2FuZGlkYXRlX3NlY29uZCAtIHByaW1lcl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9lbmQgPC0gZm91cnRoX2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKZm91cnRoX2NhbmRpZGF0ZV9yZWdpb24gPC0gc3Vic2VxKGZvdXJ0aF9jYW5kaWRhdGVfY2hyLCBmb3VydGhfY2FuZGlkYXRlX3N0YXJ0LCBmb3VydGhfY2FuZGlkYXRlX2VuZCkKZm91cnRoX2NhbmRpZGF0ZV9yZWdpb24KZm91cnRoX2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEoZm91cnRoX2NhbmRpZGF0ZV9jaHIsIGZvdXJ0aF9jYW5kaWRhdGVfc3RhcnQsIGZvdXJ0aF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIoZm91cnRoX2NhbmRpZGF0ZV81cCkKZm91cnRoX2NhbmRpZGF0ZV8zcCA8LSBzcGdzOjpyZXZlcnNlQ29tcGxlbWVudChzdWJzZXEoZm91cnRoX2NhbmRpZGF0ZV9jaHIsIGZvdXJ0aF9jYW5kaWRhdGVfdGhpcmQsIGZvdXJ0aF9jYW5kaWRhdGVfZW5kKSkKZm91cnRoX2NhbmRpZGF0ZV8zcApgYGAKCiMjIEdvIGh1bnRpbmcgZm9yIFNhbmdlciBzZXF1ZW5jaW5nIHJlZ2lvbnMKCkkgbWFkZSBhIGZ1biBsaXR0bGUgZnVuY3Rpb24gd2hpY2ggc2hvdWxkIGZpbmQgcmVnaW9ucyB3aGljaCBoYXZlIGxvdHMgb2YgdmFyaWFudHMKYXNzb2NpYXRlZCB3aXRoIGEgZ2l2ZW4gZXhwZXJpbWVudGFsIGZhY3Rvci4KCmBgYHtyIHNhbmdlcl9mdW4sIGV2YWw9RkFMU0V9CnBoZW5vIDwtIHN1YnNldF9leHB0KGxwX2V4cHQsIHN1YnNldCA9ICJjb25kaXRpb249PSd6Mi4yJ3xjb25kaXRpb249PSd6Mi4zJyIpCnBoZW5vIDwtIHN1YnNldF9leHB0KHBoZW5vLCBzdWJzZXQgPSAiIWlzLm5hKHBEYXRhKHBoZW5vKVtbJ2JjZnRhYmxlJ11dKSIpCnBoZW5vX3NucHMgPC0gc20oY291bnRfZXhwdF9zbnBzKHBoZW5vLCBhbm5vdF9jb2x1bW4gPSAiYmNmdGFibGUiKSkKCmZ1bl9zdHVmZiA8LSBzbnBfZGVuc2l0eV9wcmltZXJzKAogICAgcGhlbm9fc25wcywKICAgIGJzZ2Vub21lID0gIkJTR2Vub21lLkxlaXNobWFuaWEucGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjUzIiwKICAgIGdmZiA9ICJyZWZlcmVuY2UvVHJpVHJ5cERCLTUzX0xwYW5hbWVuc2lzTUhPTUNPTDgxTDEzLmdmZiIpCmRyb3Bfc2NhZmZvbGRzIDwtIGdyZXBsKHggPSByb3duYW1lcyhmdW5fc3R1ZmYkZmF2b3JpdGVzKSwgcGF0dGVybiA9ICJTQ0FGIikKZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMgPC0gZnVuX3N0dWZmW1siZmF2b3JpdGVzIl1dWyFkcm9wX3NjYWZmb2xkcywgXQpmYXZvcml0ZV9wcmltZXJfcmVnaW9uc1tbImJpbiJdXSA8LSByb3duYW1lcyhmYXZvcml0ZV9wcmltZXJfcmVnaW9ucykKbGlicmFyeShkcGx5cikKZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMgPC0gZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMgJT4lCiAgcmVsb2NhdGUoYmluKQpgYGAKCiMjIENvbWJpbmUgdGhpcyB0YWJsZSB3aXRoIDIuMi8yLjMgZ2VuZXMKCkhlcmUgaXMgbXkgbm90ZSBmcm9tIG91ciBtZWV0aW5nOgoKQ3Jvc3MgcmVmZXJlbmNlIHByaW1lcnMgdG8gREUgZ2VuZXMgb2YgMi4yLzIuMyBhbmQvb3IgcmVzaXN0YW5jZS9zdXNjcGV0aWJsZSwKYWRkIGEgY29sdW1uIHRvIHRoZSBwcmltZXIgc3ByZWFkc2hlZXQgd2l0aCB0aGUgREUgZ2VuZXMgKGluIHJldHJvc3BlY3QgSSBhbSBndWVzc2luZwp0aGlzIGFjdHVhbGx5IG1lYW5zIHRvIHB1dCB0aGUgbG9nRkMgYXMgYSBjb2x1bW4uCgpPbmUgbmljZSB0aGluZywgSSBkaWQgYSBzZW1hbnRpYyByZW1vdmFsIG9uIHRoZSBscF9leHB0LCBzbyB0aGUgc2V0IG9mIGxvZ0ZDL3B2YWx1ZXMKc2hvdWxkIG5vdCBoYXZlIGFueSBvZiB0aGUgb2ZmZW5kaW5nIHR5cGVzOyB0aHVzIEkgc2hvdWxkIGJlIGFibGUgdG8gYXV0b21hZ2ljYWxseQpnZXQgcmlkIG9mIHRoZW0gaW4gdGhlIG1lcmdlLgoKYGBge3IgeHJlZl9wcmltZXJzX2RlZywgZXZhbD1GQUxTRX0KbG9nZmMgPC0genlfdGFibGVfc3ZhW1siZGF0YSJdXVtbInoyM192c196MjIiXV0KbG9nZmNfY29sdW1ucyA8LSBsb2dmY1ssIGMoImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiKV0KY29sbmFtZXMobG9nZmNfY29sdW1ucykgPC0gYygiejIzX2xvZ2ZjIiwgInoyM19hZGpwIikKbmV3X3RhYmxlIDwtIG1lcmdlKGZhdm9yaXRlX3ByaW1lcl9yZWdpb25zLCBsb2dmY19jb2x1bW5zLAogICAgICAgICAgICAgICAgICAgYnkueCA9ICJjbG9zZXN0X2dlbmVfYmVmb3JlX2lkIiwgYnkueSA9ICJyb3cubmFtZXMiKQpzdXMgPC0gc3VzX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJzZW5zaXRpdmVfdnNfcmVzaXN0YW50Il1dCnN1c19jb2x1bW5zIDwtIHN1c1ssIGMoImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiKV0KY29sbmFtZXMoc3VzX2NvbHVtbnMpIDwtIGMoInN1c19sb2dmYyIsICJzdXNfYWRqcCIpCm5ld190YWJsZSA8LSBtZXJnZShuZXdfdGFibGUsIHN1c19jb2x1bW5zLAogICAgICAgICAgICAgICAgICAgYnkueCA9ICJjbG9zZXN0X2dlbmVfYmVmb3JlX2lkIiwgYnkueSA9ICJyb3cubmFtZXMiKSAlPiUKICByZWxvY2F0ZShiaW4pCndyaXR0ZW4gPC0gd3JpdGVfeGxzeChkYXRhPW5ld190YWJsZSwKICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsPSJleGNlbC9mYXZvcml0ZV9wcmltZXJzX3hyZWZfenlfc3VzLnhsc3giKQpgYGAKCgojIyBNYWtlIGEgaGVhdG1hcCBkZXNjcmliaW5nIHRoZSBjbHVzdGVyaW5nIG9mIHZhcmlhbnRzCgpXZSBjYW4gY3Jvc3MgcmVmZXJlbmNlIHRoZSB2YXJpYW50cyBhZ2FpbnN0IHRoZSB6eW1vZGVtZSBzdGF0dXMgYW5kCnBsb3QgYSBoZWF0bWFwIG9mIHRoZSByZXN1bHRzIGFuZCBob3BlZnVsbHkgc2VlIGhvdyB0aGV5IHNlcGFyYXRlLgoKYGBge3Igenltb19oZWF0bWFwcywgZXZhbD1GQUxTRX0Kc25wX2dlbmVzIDwtIHNtKHNucHNfdnNfZ2VuZXMobHBfZXhwdCwgbmV3X3NldHMsIGV4cHRfbmFtZV9jb2wgPSAiY2hyb21vc29tZSIpKQoKY2xpbmljYWxfY29sb3JzX3YyIDwtIGxpc3QoCiAgICAiejIyIiA9ICIjMDAwMGNjIiwKICAgICJ6MjMiID0gIiNjYzAwMDAiKQpuZXdfenltb19ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHBydW5lZF9zbnBzLCBub3JtcSA9ICJxdWFudCIpICU+JQogIHNldF9leHB0X2NvbmRpdGlvbnMoZmFjdCA9ICJ6eW1vZGVtZWNhdGVnb3JpY2FsIikgJT4lCiAgc2V0X2V4cHRfY29sb3JzKGNsaW5pY2FsX2NvbG9yc192MikKCnp5bW9faGVhdCA8LSBwbG90X2Rpc2hlYXQobmV3X3p5bW9fbm9ybSkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL29ubHl6MjJfejIzX3NucF9oZWF0bWFwLnBkZiIsIHdpZHRoPTEyLCBoZWlnaHQ9MTIpCnp5bW9faGVhdFtbInBsb3QiXV0KY2xvc2VkIDwtIGRldi5vZmYoKQp6eW1vX2hlYXRbWyJwbG90Il1dCmBgYAoKIyMjIEFubm90YXRlZCBoZWF0bWFwIG9mIHZhcmlhbnRzCgpOb3cgbGV0IHVzIHRyeSB0byBtYWtlIGEgaGVhdG1hcCB3aGljaCBpbmNsdWRlcyBzb21lIG9mIHRoZSBhbm5vdGF0aW9uIGRhdGEuCgpgYGB7ciB6eW1vX2hlYXRfcGFuZWxfZ2VuZXMsIGV2YWw9RkFMU0V9Cgp0dCA8LSBwbG90X3BjYShib3RoX25vcm0pCnR0JHBsb3QKCmRlcyA8LSBwRGF0YShib3RoX25vcm0pCnp5bW9fY29sdW1uIDwtICJrbm52MmNsYXNzaWZpY2F0aW9uIgp1bmRlZl9pZHggPC0gaXMubmEoZGVzW1t6eW1vX2NvbHVtbl1dKQpkZXNbdW5kZWZfaWR4LCAic3RyYWluIl0gPC0gInVua25vd24iCgojI2htY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInllbGxvdyIsImJsYWNrIiwiZGFya2JsdWUiKSkoMjU2KQpjb3JyZWxhdGlvbnMgPC0gaHBnbF9jb3IoZXhwcnMoYm90aF9ub3JtKSkKbmFfaWR4IDwtIGlzLm5hKGNvcnJlbGF0aW9ucykKY29ycmVsYXRpb25zW25hX2lkeF0gPC0gMAoKenltb19taXNzaW5nX2lkeCA8LSBpcy5uYShkZXNbW3p5bW9fY29sdW1uXV0pCmRlc1tbenltb19jb2x1bW5dXSA8LSBhcy5jaGFyYWN0ZXIoZGVzW1t6eW1vX2NvbHVtbl1dKQpkZXNbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dIDwtIGFzLmNoYXJhY3RlcihkZXNbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dKQpkZXNbenltb19taXNzaW5nX2lkeCwgenltb19jb2x1bW5dIDwtICJ1bmtub3duIgpteWRlbmRybyA8LSBsaXN0KAogICJjbHVzdGZ1biIgPSBoY2x1c3QsCiAgImx3ZCIgPSAyLjApCmNvbF9kYXRhIDwtIGFzLmRhdGEuZnJhbWUoZGVzWywgYyh6eW1vX2NvbHVtbiwgImNsaW5pY2FsY2F0ZWdvcmljYWwiKV0pCgp1bmtub3duX2NsaW5pY2FsIDwtIGlzLm5hKGNvbF9kYXRhW1siY2xpbmljYWxjYXRlZ29yaWNhbCJdXSkKcm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShkZXNbLCBjKCJzdHJhaW4iKV0pCmNvbG5hbWVzKGNvbF9kYXRhKSA8LSBjKCJ6eW1vZGVtZSIsICJvdXRjb21lIikKY29sX2RhdGFbdW5rbm93bl9jbGluaWNhbCwgIm91dGNvbWUiXSA8LSAidW5kZWZpbmVkIgoKY29sbmFtZXMocm93X2RhdGEpIDwtIGMoInN0cmFpbiIpCm15YW5ub3QgPC0gbGlzdCgKICAiQ29sIiA9IGxpc3QoImRhdGEiID0gY29sX2RhdGEpLAogICJSb3ciID0gbGlzdCgiZGF0YSIgPSByb3dfZGF0YSkpCm15Y2x1c3QgPC0gbGlzdCgiY3V0aCIgPSAxLjAsCiAgICAgICAgICAgICAgICAiY29sIiA9IEJyZXdlckNsdXN0ZXJDb2wpCm15bGFicyA8LSBsaXN0KAogICJSb3ciID0gbGlzdCgibnJvdyIgPSA0KSwKICAiQ29sIiA9IGxpc3QoIm5yb3ciID0gNCkpCmhtY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoImRhcmtibHVlIiwgImJlaWdlIikpKDI0MCkKenltb19hbm5vdF9oZWF0IDwtIGFubkhlYXRtYXAyKAogICAgY29ycmVsYXRpb25zLAogICAgZGVuZHJvZ3JhbSA9IG15ZGVuZHJvLAogICAgYW5ub3RhdGlvbiA9IG15YW5ub3QsCiAgICBjbHVzdGVyID0gbXljbHVzdCwKICAgIGxhYmVscyA9IG15bGFicywKICAgICMjIFRoZSBmb2xsb3dpbmcgY29udHJvbHMgaWYgdGhlIHBpY3R1cmUgaXMgc3ltbWV0cmljCiAgICBzY2FsZSA9ICJub25lIiwKICAgIGNvbCA9IGhtY29scykKCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9kZW5kcm9faGVhdG1hcC5wbmciLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAyMCkKcGxvdCh6eW1vX2Fubm90X2hlYXQpCmNsb3NlZCA8LSBkZXYub2ZmKCkKcGxvdCh6eW1vX2Fubm90X2hlYXQpCmBgYAoKUHJpbnQgdGhlIGxhcmdlciBoZWF0bWFwIHNvIHRoYXQgYWxsIHRoZSBsYWJlbHMgYXBwZWFyLiAgS2VlcCBpbiBtaW5kCnRoYXQgYXMgd2UgZ2V0IG1vcmUgc2FtcGxlcywgdGhpcyBpbWFnZSBuZWVkcyB0byBjb250aW51ZSBnZXR0aW5nCmJpZ2dlci4KCiFbYmlnIGhlYXRtYXBdKGltYWdlcy9kZW5kcm9faGVhdG1hcC5wbmcpCgoKYGBge3IgdGhlcmVzYV9pZGVhLCBldmFsPUZBTFNFfQp4cmVmX3Byb3AgPC0gdGFibGUocGhlbm9fc25wc1tbImNvbmRpdGlvbnMiXV0pCnBoZW5vX3NucHMkY29uZGl0aW9ucwppZHhfdGJsIDwtIGV4cHJzKHBoZW5vX3NucHMpID4gNQpuZXdfdGJsIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXMoZXhwcnMocGhlbm9fc25wcykpKQpmb3IgKG4gaW4gbmFtZXMoeHJlZl9wcm9wKSkgewogIG5ld190YmxbW25dXSA8LSAwCiAgaWR4X2NvbHMgPC0gd2hpY2gocGhlbm9fc25wc1tbImNvbmRpdGlvbnMiXV0gPT0gbikKICBwcm9wX2NvbCA8LSByb3dTdW1zKGlkeF90YmxbLCBpZHhfY29sc10pIC8geHJlZl9wcm9wW25dCiAgbmV3X3RibFtuXSA8LSBwcm9wX2NvbAp9CmtlZXBlcnMgPC0gZ3JlcGwoeCA9IHJvd25hbWVzKG5ld190YmwpLCBwYXR0ZXJuID0gIkxwYUwxMyIpCm5ld190YmwgPC0gbmV3X3RibFtrZWVwZXJzLCBdCm5ld190YmxbWyJzdHJvbmcyMiJdXSA8LSAxLjAwMSAtIG5ld190YmxbWyJ6Mi4yIl1dCm5ld190YmxbWyJzdHJvbmcyMyJdXSA8LSAxLjAwMSAtIG5ld190YmxbWyJ6Mi4zIl1dCnMyMl9uYSA8LSBuZXdfdGJsW1sic3Ryb25nMjIiXV0gPiAxCm5ld190YmxbczIyX25hLCAic3Ryb25nMjIiXSA8LSAxCnMyM19uYSA8LSBuZXdfdGJsW1sic3Ryb25nMjMiXV0gPiAxCm5ld190YmxbczIzX25hLCAic3Ryb25nMjMiXSA8LSAxCgpuZXdfdGJsW1siU05QIl1dIDwtIHJvd25hbWVzKG5ld190YmwpCm5ld190YmxbWyJDaHJvbW9zb21lIl1dIDwtIGdzdWIoeCA9IG5ld190YmxbWyJTTlAiXV0sIHBhdHRlcm4gPSAiY2hyXyguKilfcG9zXy4qIiwgcmVwbGFjZW1lbnQgPSAiXFwxIikKbmV3X3RibFtbIlBvc2l0aW9uIl1dIDwtIGdzdWIoeCA9IG5ld190YmxbWyJTTlAiXV0sIHBhdHRlcm4gPSAiLipfcG9zXyhcXGQrKV8uKiIsIHJlcGxhY2VtZW50ID0gIlxcMSIpCm5ld190YmwgPC0gbmV3X3RibFssIGMoIlNOUCIsICJDaHJvbW9zb21lIiwgIlBvc2l0aW9uIiwgInN0cm9uZzIyIiwgInN0cm9uZzIzIildCgpsaWJyYXJ5KENNcGxvdCkKc2ltcGxpZnkgPC0gbmV3X3RibApzaW1wbGlmeVtbInN0cm9uZzIyIl1dIDwtIE5VTEwKCkNNcGxvdChzaW1wbGlmeSwgYmluLnNpemUgPSAxMDAwMDApCgpDTXBsb3QobmV3X3RibCwgcGxvdC50eXBlPSJtIiwgbXVsdHJhY2tzPVRSVUUsIHRocmVzaG9sZCA9IGMoMC4wMSwgMC4wNSksCiAgICAgICB0aHJlc2hvbGQubHdkPWMoMSwxKSwgdGhyZXNob2xkLmNvbD1jKCJibGFjayIsImdyZXkiKSwKICAgICAgIGFtcGxpZnk9VFJVRSwgYmluLnNpemU9MTAwMDAsCiAgICAgICBjaHIuZGVuLmNvbD1jKCJkYXJrZ3JlZW4iLCAieWVsbG93IiwgInJlZCIpLAogICAgICAgc2lnbmFsLmNvbD1jKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIpLAogICAgICAgc2lnbmFsLmNleD0xLCBmaWxlPSJqcGciLCBtZW1vPSIiLCBkcGk9MzAwLCBmaWxlLm91dHB1dD1UUlVFLCB2ZXJib3NlPVRSVUUpCmBgYAoKPCEtLS0KIVtTTlAgRGVuc2l0eV0oU05QLURlbnNpdHkucmF0aW8uanBnKQohW0NpcmN1bGFyIE1hbmhhdHRhbl0oQ2lyY3VsYXItTWFuaGF0dGFuLnJhdGlvLmpwZykKIVtSZWN0YW5ndWxhciBNYW5oYXR0YW5dKFJlY3Rhbmd1bGFyLU1hbmhhdHRhbi5yYXRpby5qcGcpCiFbUVFdKFFRcGxvdC5yYXRpby5qcGcpCi0tLT4KCiMjIFRyeSBvdXQgTWF0cml4RVFUTAoKVGhpcyB0b29sIGxvb2tzIGEgbGl0dGxlIG9wYXF1ZSwgYnV0IHByb3ZpZGVzIHNhbXBsZSBkYXRhIHdpdGggdGhpbmdzCnRoYXQgbWFrZSBzZW5zZSB0byBtZSBhbmQgc2hvdWxkIGJlIHByZXR0eSBlYXN5IHRvIHJlY2FwaXR1bGF0ZSBpbiBvdXIKZGF0YS4KCjEuICBjb3ZhcmlhdGVzLnR4dDogQ29sdW1ucyBhcmUgc2FtcGxlcywgcm93cyBhcmUgdGhpbmdzIGZyb20gcERhdGEgLS0gdGhlCiAgICBtb3N0IGxpa2VseSBvbmVzIG9mIGludGVyZXN0IGZvciBvdXIgZGF0YSB3b3VsZCBiZSB6eW1vZGVtZSwKICAgIHNlbnNpdGl2aXR5CjIuICBnZW5lbG9jLnR4dDogY29sdW1ucyBhcmUgJ2dlbmVpZCcsICdjaHInLCAnbGVmdCcsICdyaWdodCcuICBJCiAgICBndWVzcyBJIGNhbiBhc3N1bWUgbGVmdCBhbmQgcmlnaHQgYXJlIHN0YXJ0L3N0b3A7IGluIHdoaWNoIGNhc2UKICAgIHRoaXMgaXMgdHJpdmlhbGx5IGFjcXVpcmFibGUgZnJvbSBmRGF0YS4KMy4gIGdlLnR4dDogVGhpcyBhcHBlYXJzIHRvIGJlIGEgbG9nKHJwa20vY3BtKSB0YWJsZSB3aXRoIHJvd3MgYXMgZ2VuZXMgYW5kCiAgICBjb2x1bW5zIGFzIHNhbXBsZXMKNC4gIHNucHNsb2MudHh0OiBjb2x1bW5zIGFyZSAnc25waWQnLCAnY2hyJywgJ3BvcycKNS4gIHNucHMudHh0OiBjb2x1bW5zIGFyZSBzYW1wbGVzLCByb3dzIGFyZSB0aGUgaWRzIGZyb20gc25zcGxvYywKICAgIHZhbHVlcyBhIDAsMSwyLiAgSSBhc3N1bWUgMCBpcyBpZGVudGljYWwgYW5kIDEuLjEyIGFyZSB0aGUgdmFyaW91cwogICAgQS0+VEdDIFQtPkFHQyBDLT5BR1QgRy0+QUNUCgpgYGB7ciBtYXRyaXhlcXRsLCBldmFsPUZBTFNFfQojIyBGb3IgdGhpcywgbGV0IHVzIHVzZSB0aGUgJ25ld19zbnBzJyBkYXRhIHN0cnVjdHVyZS4KIyMgQ2F2ZWF0IGhlcmU6IHRoZXNlIG5lZWQgdG8gYmUgY29lcmNlZCB0byBudW1iZXJzLgpteV9jb3ZhcmlhdGVzIDwtIHBEYXRhKG5ld19zbnBzKVssIGMoenltb19jb2x1bW4sICJjbGluaWNhbGNhdGVnb3JpY2FsIildCmZvciAoY29sIGluIGNvbG5hbWVzKG15X2NvdmFyaWF0ZXMpKSB7CiAgbXlfY292YXJpYXRlc1tbY29sXV0gPC0gYXMubnVtZXJpYyhhcy5mYWN0b3IobXlfY292YXJpYXRlc1tbY29sXV0pKQp9Cm15X2NvdmFyaWF0ZXMgPC0gdChteV9jb3ZhcmlhdGVzKQoKbXlfZ2VuZWxvYyA8LSBmRGF0YShscF9leHB0KVssIGMoImdpZCIsICJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpXQpjb2xuYW1lcyhteV9nZW5lbG9jKSA8LSBjKCJnZW5laWQiLCAiY2hyIiwgImxlZnQiLCAicmlnaHQiKQoKbXlfZ2UgPC0gZXhwcnMobm9ybWFsaXplX2V4cHQobHBfZXhwdCwgdHJhbnNmb3JtID0gImxvZzIiLCBmaWx0ZXIgPSBUUlVFLCBjb252ZXJ0ID0gImNwbSIpKQp1c2VkX3NhbXBsZXMgPC0gdG9sb3dlcihjb2xuYW1lcyhteV9nZSkpICVpbiUgY29sbmFtZXMoZXhwcnMobmV3X3NucHMpKQpteV9nZSA8LSBteV9nZVssIHVzZWRfc2FtcGxlc10KCm15X3NucHNsb2MgPC0gZGF0YS5mcmFtZShyb3duYW1lcyA9IHJvd25hbWVzKGV4cHJzKG5ld19zbnBzKSkpCiMjIE9oLCBjYXZlYXQgaGVyZTogQmVjYXVzZSBvZiB0aGUgd2F5IEkgc3RvcmVkIHRoZSBkYXRhLAojIyBJIGNvdWxkIGhhdmUgZHVwbGljYXRlIHJvd3Mgd2hpY2ggcHJlc3VtYWJseSB3aWxsIG1ha2UgbWF0cml4RVFUTCBzYWQKbXlfc25wc2xvY1tbImNociJdXSA8LSBnc3ViKHBhdHRlcm4gPSAiXmNocl8oLispX3BvcyguKylfcmVmXy4qJCIsIHJlcGxhY2VtZW50ID0gIlxcMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcm93bmFtZXMobXlfc25wc2xvYykpCm15X3NucHNsb2NbWyJwb3MiXV0gPC0gZ3N1YihwYXR0ZXJuID0gIl5jaHJfKC4rKV9wb3MoLispX3JlZl8uKiQiLCByZXBsYWNlbWVudCA9ICJcXDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IHJvd25hbWVzKG15X3NucHNsb2MpKQp0ZXN0IDwtIGR1cGxpY2F0ZWQobXlfc25wc2xvYykKIyMgRWFjaCBkdXBsaWNhdGVkIHJvdyB3b3VsZCBiZSBhbm90aGVyIHZhcmlhbnQgYXQgdGhhdCBwb3NpdGlvbjsKIyMgc28gaW4gdGhlb3J5IHdlIHdvdWxkIGRvIGEgcmxlIHRvIG51bWJlciB0aGVtIEkgYW0gZ3Vlc3NpbmcKIyMgSG93ZXZlciwgSSBkbyBub3QgaGF2ZSBkaWZmZXJlbnQgdmFyaWFudHMgc28gSSB0aGluayBJIGNhbiBpZ25vcmUgdGhpcyBmb3IgdGhlIG1vbWVudAojIyBidXQgd2lsbCBuZWVkIHRvIG1ha2UgbXkgbWF0cml4IGVpdGhlciAwIG9yIDEuCmlmIChzdW0odGVzdCkgPiAwKSB7CiAgbWVzc2FnZSgiVGhlcmUgYXJlOiAiLCBzdW0oZHVwbGljYXRlZCksICIgZHVwbGljYXRlZCBlbnRyaWVzLiIpCiAga2VlcF9pZHggPC0gISB0ZXN0CiAgbXlfc25wc2xvYyA8LSBteV9zbnBzbG9jW2tlZXBfaWR4LCBdCn0KCm15X3NucHMgPC0gZXhwcnMobmV3X3NucHMpCm9uZV9pZHggPC0gbXlfc25wcyA+IDAKbXlfc25wc1tvbmVfaWR4XSA8LSAxCgojIyBPaywgYXQgdGhpcyBwb2ludCBJIHRoaW5rIEkgaGF2ZSBhbGwgdGhlIHBpZWNlcyB3aGljaCB0aGlzIG1ldGhvZCB3YW50cy4uLgojIyBPaCwgbm8gSSBndWVzcyBub3Q7IGl0IGFjdHVhbGx5IHdhbnRzIHRoZSBkYXRhIGFzIGEgc2V0IG9mIGZpbGVuYW1lcy4uLgpsaWJyYXJ5KE1hdHJpeEVRVEwpCndyaXRlLnRhYmxlKG15X3NucHMsICJlcXRsL3NucHMudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YobXlfc25wcywgImVxdGwvc25wcy50c3YiLCApCndyaXRlLnRhYmxlKG15X3NucHNsb2MsICJlcXRsL3NucHNsb2MudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YobXlfc25wc2xvYywgImVxdGwvc25wc2xvYy50c3YiKQp3cml0ZS50YWJsZShhcy5kYXRhLmZyYW1lKG15X2dlKSwgImVxdGwvZ2UudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9nZSksICJlcXRsL2dlLnRzdiIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUobXlfZ2VuZWxvYyksICJlcXRsL2dlbmVsb2MudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9nZW5lbG9jKSwgImVxdGwvZ2VuZWxvYy50c3YiKQp3cml0ZS50YWJsZShhcy5kYXRhLmZyYW1lKG15X2NvdmFyaWF0ZXMpLCAiZXF0bC9jb3ZhcmlhdGVzLnRzdiIsIG5hID0gIk5BIiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGUgPSBUUlVFKQojIyByZWFkcjo6d3JpdGVfdHN2KGFzLmRhdGEuZnJhbWUobXlfY292YXJpYXRlcyksICJlcXRsL2NvdmFyaWF0ZXMudHN2IikKCnVzZU1vZGVsID0gbW9kZWxMSU5FQVIgIyBtb2RlbEFOT1ZBLCBtb2RlbExJTkVBUiwgb3IgbW9kZWxMSU5FQVJfQ1JPU1MKCiMgR2Vub3R5cGUgZmlsZSBuYW1lClNOUF9maWxlX25hbWUgPSAiZXF0bC9zbnBzLnRzdiIKc25wc19sb2NhdGlvbl9maWxlX25hbWUgPSAiZXF0bC9zbnBzbG9jLnRzdiIKZXhwcmVzc2lvbl9maWxlX25hbWUgPSAiZXF0bC9nZS50c3YiCmdlbmVfbG9jYXRpb25fZmlsZV9uYW1lID0gImVxdGwvZ2VuZWxvYy50c3YiCmNvdmFyaWF0ZXNfZmlsZV9uYW1lID0gImVxdGwvY292YXJpYXRlcy50c3YiCiMgT3V0cHV0IGZpbGUgbmFtZQpvdXRwdXRfZmlsZV9uYW1lX2NpcyA9IHRlbXBmaWxlKCkKb3V0cHV0X2ZpbGVfbmFtZV90cmEgPSB0ZW1wZmlsZSgpCiMgT25seSBhc3NvY2lhdGlvbnMgc2lnbmlmaWNhbnQgYXQgdGhpcyBsZXZlbCB3aWxsIGJlIHNhdmVkCnB2T3V0cHV0VGhyZXNob2xkX2NpcyA9IDAuMQpwdk91dHB1dFRocmVzaG9sZF90cmEgPSAwLjEKIyBFcnJvciBjb3ZhcmlhbmNlIG1hdHJpeAojIFNldCB0byBudW1lcmljKCkgZm9yIGlkZW50aXR5LgplcnJvckNvdmFyaWFuY2UgPSBudW1lcmljKCkKIyBlcnJvckNvdmFyaWFuY2UgPSByZWFkLnRhYmxlKCJTYW1wbGVfRGF0YS9lcnJvckNvdmFyaWFuY2UudHh0Iik7CiMgRGlzdGFuY2UgZm9yIGxvY2FsIGdlbmUtU05QIHBhaXJzCmNpc0Rpc3QgPSAxZTYKIyMgTG9hZCBnZW5vdHlwZSBkYXRhCnNucHMgPSBTbGljZWREYXRhJG5ldygpCnNucHMkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCnNucHMkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKc25wcyRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCnNucHMkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwpzbnBzJGZpbGVTbGljZVNpemUgPSAyMDAwICAgICAgIyByZWFkIGZpbGUgaW4gc2xpY2VzIG9mIDIsMDAwIHJvd3MKc25wcyRMb2FkRmlsZShTTlBfZmlsZV9uYW1lKQojIyBMb2FkIGdlbmUgZXhwcmVzc2lvbiBkYXRhCmdlbmUgPSBTbGljZWREYXRhJG5ldygpCmdlbmUkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCmdlbmUkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKZ2VuZSRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCmdlbmUkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwpnZW5lJGZpbGVTbGljZVNpemUgPSAyMDAwICAgICAgIyByZWFkIGZpbGUgaW4gc2xpY2VzIG9mIDIsMDAwIHJvd3MKZ2VuZSRMb2FkRmlsZShleHByZXNzaW9uX2ZpbGVfbmFtZSkKIyMgTG9hZCBjb3ZhcmlhdGVzCmN2cnQgPSBTbGljZWREYXRhJG5ldygpCmN2cnQkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCmN2cnQkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKY3ZydCRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCmN2cnQkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwppZihsZW5ndGgoY292YXJpYXRlc19maWxlX25hbWUpID4gMCkgewogIGN2cnQkTG9hZEZpbGUoY292YXJpYXRlc19maWxlX25hbWUpCn0KIyMgUnVuIHRoZSBhbmFseXNpcwpzbnBzcG9zID0gcmVhZC50YWJsZShzbnBzX2xvY2F0aW9uX2ZpbGVfbmFtZSwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpnZW5lcG9zID0gcmVhZC50YWJsZShnZW5lX2xvY2F0aW9uX2ZpbGVfbmFtZSwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKbWUgPSBNYXRyaXhfZVFUTF9tYWluKAogICAgc25wcyA9IHNucHMsCiAgICBnZW5lID0gZ2VuZSwKICAgIGN2cnQgPSBjdnJ0LAogICAgb3V0cHV0X2ZpbGVfbmFtZSA9IG91dHB1dF9maWxlX25hbWVfdHJhLAogICAgcHZPdXRwdXRUaHJlc2hvbGQgPSBwdk91dHB1dFRocmVzaG9sZF90cmEsCiAgICB1c2VNb2RlbCA9IHVzZU1vZGVsLAogICAgZXJyb3JDb3ZhcmlhbmNlID0gZXJyb3JDb3ZhcmlhbmNlLAogICAgdmVyYm9zZSA9IFRSVUUsCiAgICBvdXRwdXRfZmlsZV9uYW1lLmNpcyA9IG91dHB1dF9maWxlX25hbWVfY2lzLAogICAgcHZPdXRwdXRUaHJlc2hvbGQuY2lzID0gcHZPdXRwdXRUaHJlc2hvbGRfY2lzLAogICAgc25wc3BvcyA9IHNucHNwb3MsCiAgICBnZW5lcG9zID0gZ2VuZXBvcywKICAgIGNpc0Rpc3QgPSBjaXNEaXN0LAogICAgcHZhbHVlLmhpc3QgPSAicXFwbG90IiwKICAgIG1pbi5wdi5ieS5nZW5lc25wID0gRkFMU0UsCiAgICBub0ZEUnNhdmVNZW1vcnkgPSBGQUxTRSk7CmBgYAoKCgpgYGB7ciBzYXZlbWV9CmlmICghaXNUUlVFKGdldDAoInNraXBfbG9hZCIpKSkgewogIHBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCiAgbWVzc2FnZSgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKQogIG1lc3NhZ2UoIlNhdmluZyB0byAiLCBzYXZlZmlsZSkKICB0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lID0gc2F2ZWZpbGUpKQp9CmBgYAoKYGBge3IgbG9hZG1lX2FmdGVyLCBldmFsID0gRkFMU0V9CnRtcCA8LSBsb2FkbWUoZmlsZW5hbWUgPSBzYXZlZmlsZSkKYGBgCg==