Our main question of
interest
The data structure hs_macr contains our primary macrophages, which
are, as shown above, the data we can really sink our teeth into.
Note, we expect some errors when running the combine_de_tables()
because not all methods I use are comfortable using the ratio or ratios
contrasts we added in the ‘extras’ argument. As a result, when we
combine them into the larger output tables, those peculiar contrasts
fail. This does not stop it from writing the rest of the results,
however.
#test = deseq_pairwise(normalize_expt(hs_macr, filter=TRUE),
# model_svs = "svaseq", filter = TRUE,
# extra_contrasts = tmrc2_human_extra)
hs_macr_de_noextra <- all_pairwise(hs_macr, model_svs = "svaseq", model_fstring = "~ 0 + condition", filter = TRUE)
## inf_sb_z22 inf_sb_z23 inf_z22 inf_z23 uninf_none
## 12 11 11 12 4
## uninf_sb_none
## 4
## Running normalize_se.
## Removing 9725 low-count genes (11756 remaining).
## Error in h(simpleError(msg, call)) :
## error in evaluating the argument 'x' in selecting a method for function 'colData': object 'se' not found
## This received a matrix of SVs.
## converting counts to integer mode
## gene-wise dispersion estimates
## mean-dispersion relationship
## final dispersion estimates
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
## conditions
## inf_sb_z22 inf_sb_z23 inf_z22 inf_z23 uninf_none
## 12 11 11 12 4
## uninf_sb_none
## 4
## conditions
## inf_sb_z22 inf_sb_z23 inf_z22 inf_z23 uninf_none
## 12 11 11 12 4
## uninf_sb_none
## 4
## conditions
## inf_sb_z22 inf_sb_z23 inf_z22 inf_z23 uninf_none
## 12 11 11 12 4
## uninf_sb_none
## 4

hs_macr_de <- all_pairwise(hs_macr, model_svs = "svaseq", model_fstring = "~ 0 + condition",
filter = TRUE, extra_contrasts = tmrc2_human_extra)
## inf_sb_z22 inf_sb_z23 inf_z22 inf_z23 uninf_none
## 12 11 11 12 4
## uninf_sb_none
## 4
## Running normalize_se.
## Removing 9725 low-count genes (11756 remaining).
## Error in h(simpleError(msg, call)) :
## error in evaluating the argument 'x' in selecting a method for function 'colData': object 'se' not found
## This received a matrix of SVs.
## converting counts to integer mode
## gene-wise dispersion estimates
## mean-dispersion relationship
## final dispersion estimates
## The contrast z23drugnodrug is not in the results.
## If this is not an extra contrast, then this is an error.
## The contrast z23z22drug is not in the results.
## If this is not an extra contrast, then this is an error.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
## conditions
## inf_sb_z22 inf_sb_z23 inf_z22 inf_z23 uninf_none
## 12 11 11 12 4
## uninf_sb_none
## 4
## conditions
## inf_sb_z22 inf_sb_z23 inf_z22 inf_z23 uninf_none
## 12 11 11 12 4
## uninf_sb_none
## 4
## conditions
## inf_sb_z22 inf_sb_z23 inf_z22 inf_z23 uninf_none
## 12 11 11 12 4
## uninf_sb_none
## 4
## A pairwise differential expression with results from: basic, deseq, ebseq, edger, limma, noiseq.
## This used a surrogate/batch estimate from: svaseq.
## The primary analysis performed 15 comparisons.
hs_single_table <- combine_de_tables(
hs_macr_de, keepers = single_tmrc2_keeper,
excel = glue("analyses/macrophage_de/de_tables/hs_macr_drug_zymo_z22sb_sb-v{ver}.xlsx"))
hs_single_table
## A set of combined differential expression results.
## table deseq_sigup deseq_sigdown edger_sigup
## 1 uninf_sb_none_vs_inf_sb_z22-inverted 33 0 32
## edger_sigdown limma_sigup limma_sigdown
## 1 0 2 0
## Only z22sb_vs_sb_up has information, cannot create an UpSet.
## Plot describing unique/shared genes in a differential expression table.
## NULL
hs_macr_table <- combine_de_tables(
hs_macr_de, keepers = tmrc2_human_keepers,
excel = glue("analyses/macrophage_de/de_tables/hs_macr_drug_zymo_table_macr_only-v{ver}.xlsx"))
## Warning in extract_keepers(extracted, keepers, table_names, all_coefficients, :
## The table for extra_z2322 using ebseq does not appear in the pairwise data.
## Warning in extract_keepers(extracted, keepers, table_names, all_coefficients, :
## The table for extra_z2322 using noiseq does not appear in the pairwise data.
## coefficient limma did not find z22drugnodrug or z23drugnodrug.
## coefficient edger did not find conditionz22drugnodrug or conditionz23drugnodrug.
## coefficient limma did not find z22drugnodrug or z23drugnodrug.
## Warning in extract_keepers(extracted, keepers, table_names, all_coefficients, :
## The table for extra_drugnodrug using ebseq does not appear in the pairwise
## data.
## Warning in extract_keepers(extracted, keepers, table_names, all_coefficients, :
## The table for extra_drugnodrug using noiseq does not appear in the pairwise
## data.
## coefficient limma did not find z23z22nodrug or z23z22drug.
## coefficient edger did not find conditionz23z22nodrug or conditionz23z22drug.
## coefficient limma did not find z23z22nodrug or z23z22drug.
## A set of combined differential expression results.
## table deseq_sigup deseq_sigdown edger_sigup
## 1 uninf_none_vs_inf_z23-inverted 478 265 470
## 2 uninf_none_vs_inf_z22-inverted 359 6 340
## 3 inf_z23_vs_inf_z22 349 539 359
## 4 inf_sb_z23_vs_inf_sb_z22 343 252 339
## 5 inf_z23_vs_inf_sb_z23-inverted 619 828 625
## 6 inf_z22_vs_inf_sb_z22-inverted 505 1040 520
## 7 uninf_sb_none_vs_inf_sb_z23-inverted 461 247 461
## 8 uninf_sb_none_vs_inf_sb_z22-inverted 33 0 32
## 9 uninf_none_vs_inf_sb_z23-inverted 839 923 854
## 10 uninf_none_vs_inf_sb_z22-inverted 660 746 672
## 11 uninf_sb_none_vs_uninf_none 561 748 563
## 12 z23drugnodrug_vs_z22drugnodrug 0 0 329
## 13 z23z22drug_vs_z23z22nodrug 0 0 329
## edger_sigdown limma_sigup limma_sigdown
## 1 270 392 251
## 2 6 264 71
## 3 528 450 390
## 4 253 377 215
## 5 821 571 746
## 6 1009 671 925
## 7 249 374 232
## 8 0 2 0
## 9 906 805 914
## 10 733 555 744
## 11 742 513 696
## 12 63 243 135
## 13 63 243 135
## Plot describing unique/shared genes in a differential expression table.

#combined_to_tsv(hs_macr_table, "macrophage")
hs_macr_sig <- extract_significant_genes(
hs_macr_table,
excel = glue("analyses/macrophage_de/sig_tables/hs_macr_drug_zymo_sig-v{ver}.xlsx"))
## There is no deseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no deseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no ebseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no ebseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## A set of genes deemed significant according to limma, edger, deseq, ebseq.
## The parameters defining significant were:
## LFC cutoff: 1 adj P cutoff: 0.05
## limma_up limma_down edger_up edger_down deseq_up deseq_down
## z23nosb_vs_uninf 392 251 470 270 478 265
## z22nosb_vs_uninf 264 71 340 6 359 6
## z23nosb_vs_z22nosb 450 390 359 528 349 539
## z23sb_vs_z22sb 377 215 339 253 343 252
## z23sb_vs_z23nosb 571 746 625 821 619 828
## z22sb_vs_z22nosb 671 925 520 1009 505 1040
## z23sb_vs_sb 374 232 461 249 461 247
## z22sb_vs_sb 2 0 32 0 33 0
## z23sb_vs_uninf 805 914 854 906 839 923
## z22sb_vs_uninf 555 744 672 733 660 746
## sb_vs_uninf 513 696 563 742 561 748
## extra_z2322 243 135 329 63 0 0
## extra_drugnodrug 243 135 329 63 0 0
## ebseq_up ebseq_down
## z23nosb_vs_uninf 111 112
## z22nosb_vs_uninf 160 2
## z23nosb_vs_z22nosb 257 408
## z23sb_vs_z22sb 106 108
## z23sb_vs_z23nosb 412 699
## z22sb_vs_z22nosb 458 886
## z23sb_vs_sb 33 58
## z22sb_vs_sb 25 0
## z23sb_vs_uninf 280 767
## z22sb_vs_uninf 444 551
## sb_vs_uninf 316 495
## extra_z2322 0 0
## extra_drugnodrug 0 0

hs_macr_highsig <- extract_significant_genes(
hs_macr_table, min_mean_exprs = high_expression, exprs_column = high_expression_column,
excel = glue("analyses/macrophage_de/sig_tables/hs_macr_drug_zymo_highsig-v{ver}.xlsx"))
## Warning in get_sig_genes(this_table, lfc = lfc, p = p, z = z, n = n, column =
## this_fc_column, : The column deseq_basemean does not appears to be in the
## table, cannot filter by expression.
## Warning in get_sig_genes(this_table, lfc = lfc, p = p, z = z, n = n, column =
## this_fc_column, : The column deseq_basemean does not appears to be in the
## table, cannot filter by expression.
## Warning in get_sig_genes(this_table, lfc = lfc, p = p, z = z, n = n, column =
## this_fc_column, : The column deseq_basemean does not appears to be in the
## table, cannot filter by expression.
## Warning in get_sig_genes(this_table, lfc = lfc, p = p, z = z, n = n, column =
## this_fc_column, : The column deseq_basemean does not appears to be in the
## table, cannot filter by expression.
## There is no deseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no deseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no ebseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no ebseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## A set of genes deemed significant according to limma, edger, deseq, ebseq.
## The parameters defining significant were:
## LFC cutoff: 1 adj P cutoff: 0.05
## limma_up limma_down edger_up edger_down deseq_up deseq_down
## z23nosb_vs_uninf 269 139 317 139 314 138
## z22nosb_vs_uninf 103 4 110 0 115 0
## z23nosb_vs_z22nosb 221 154 247 174 238 178
## z23sb_vs_z22sb 211 105 210 86 211 84
## z23sb_vs_z23nosb 305 482 306 566 303 570
## z22sb_vs_z22nosb 330 545 301 572 288 598
## z23sb_vs_sb 250 130 278 140 274 140
## z22sb_vs_sb 2 0 9 0 13 0
## z23sb_vs_uninf 499 603 491 605 482 618
## z22sb_vs_uninf 310 479 318 501 303 513
## sb_vs_uninf 291 459 294 495 291 498
## extra_z2322 243 135 329 63 0 0
## extra_drugnodrug 243 135 329 63 0 0
## ebseq_up ebseq_down
## z23nosb_vs_uninf 87 64
## z22nosb_vs_uninf 41 0
## z23nosb_vs_z22nosb 207 140
## z23sb_vs_z22sb 80 37
## z23sb_vs_z23nosb 212 529
## z22sb_vs_z22nosb 276 519
## z23sb_vs_sb 21 28
## z22sb_vs_sb 5 0
## z23sb_vs_uninf 177 550
## z22sb_vs_uninf 235 393
## sb_vs_uninf 191 352
## extra_z2322 0 0
## extra_drugnodrug 0 0

hs_macr_lesssig <- extract_significant_genes(
hs_macr_table, lfc = 0.6,
excel = glue("analyses/macrophage_de/sig_tables/hs_macr_drug_zymo_sig_lfc0.6-v{ver}.xlsx"))
## There is no deseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no deseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no ebseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## There is no ebseq_logfc column in the table.
## The columns are: ensembl_gene_id, ensembl_transcript_id, version, transcript_version, description, gene_biotype, cds_length, chromosome_name, strand, start_position, end_position, hgnc_symbol, transcript, dream_logfc, dream_adjp, edger_logfc, edger_adjp, limma_logfc, limma_adjp, dream_ave, dream_t, dream_p, dream_b, edger_logcpm, edger_lr, edger_p, limma_ave, limma_t, limma_p, limma_b, limma_adjp_fdr, dream_adjp_fdr, edger_adjp_fdr, lfc_meta, lfc_var, lfc_varbymed, p_meta, p_var
## A set of genes deemed significant according to limma, edger, deseq, ebseq.
## The parameters defining significant were:
## LFC cutoff: 0.6 adj P cutoff: 0.05
## limma_up limma_down edger_up edger_down deseq_up deseq_down
## z23nosb_vs_uninf 701 587 856 594 865 587
## z22nosb_vs_uninf 378 128 464 21 505 22
## z23nosb_vs_z22nosb 867 786 746 964 728 981
## z23sb_vs_z22sb 670 587 649 645 655 641
## z23sb_vs_z23nosb 1237 1395 1297 1536 1279 1545
## z22sb_vs_z22nosb 1492 1542 1287 1692 1211 1747
## z23sb_vs_sb 622 643 761 610 772 614
## z22sb_vs_sb 2 0 33 0 34 0
## z23sb_vs_uninf 1516 1656 1595 1671 1557 1700
## z22sb_vs_uninf 1127 1297 1246 1293 1222 1340
## sb_vs_uninf 1037 1148 1055 1262 1042 1291
## extra_z2322 381 288 482 210 0 0
## extra_drugnodrug 381 288 482 210 0 0
## ebseq_up ebseq_down
## z23nosb_vs_uninf 141 196
## z22nosb_vs_uninf 186 4
## z23nosb_vs_z22nosb 463 602
## z23sb_vs_z22sb 144 237
## z23sb_vs_z23nosb 774 1166
## z22sb_vs_z22nosb 1044 1349
## z23sb_vs_sb 39 115
## z22sb_vs_sb 30 0
## z23sb_vs_uninf 458 1259
## z22sb_vs_uninf 746 907
## sb_vs_uninf 495 788
## extra_z2322 0 0
## extra_drugnodrug 0 0

gene group upset
2.3 vs 2.2 up and
down vs. uninfected
This is my version of the Venn diagram which includes the text:
“Differentially expressed genes in macrophages infected with
subpopulations 2.2 or 2.3. Volcano plots contrast of: A. Venn diagram
for upregulated and downregulated genes by infection with 2.3 and 2.2
strains. B. infected cells with 2.3 strains and uninfected cells; C.
infected cells with 2.2 strains and uninfected cells; D. infected cells
with 2.3 strains and infected cells with 2.2 strains”
The following upset plot is currently Figure 2E.
nodrug_upset <- upsetr_combined_de(hs_macr_table,
desired_contrasts = c("z22nosb_vs_uninf", "z23nosb_vs_uninf"))
pp(file = "images/nodrug_upset.svg")
nodrug_upset[["plot"]]
dev.off()
## png
## 2
## Plot describing unique/shared genes in a differential expression table.

A point of
interest while Olga visits Umd
Najib and Olga asked about pulling the 9 gene IDs which are in the
peculiar situation of increased expression in z2.2/uninf and decreased
in z2.3/uninf. In the previous upset plot, these are visible in the 6th
bar. I can access these via the attr() function, which I should admit I
can never remember how to use, so I am going to use the code under the
‘Compare(no)Sb z2.3/z2.2 treatment’ heading to remember how to extract
these genes.
all_groups <- nodrug_upset[["groups"]]
wanted_group <- "z23nosb_vs_uninf_down:z22nosb_vs_uninf_up"
gene_idx <- all_groups[[wanted_group]]
wanted_genes <- attr(all_groups, "elements")[gene_idx]
wanted_genes
## [1] "ENSG00000004846" "ENSG00000111783" "ENSG00000118298" "ENSG00000120738"
## [5] "ENSG00000126217" "ENSG00000163687" "ENSG00000170345" "ENSG00000244242"
## [9] "ENSG00000277481"
gene_symbol_idx <- rownames(fData(hs_macr)) %in% as.character(wanted_genes)
fData(hs_macr)[gene_symbol_idx, "hgnc_symbol"]
## [1] "ABCB5" "RFX4" "CA14" "EGR1" "MCF2L" "DNASE1L3" "FOS"
## [8] "IFITM10" "PKD1L3"
- ABCB5: ATB Binding Cassette Subfamily B Member #5, wide range of
functions in this diverse paralogous family. Associated with skin
diseases (melanoma and Epidermolysis Bullosa; participate in
ATP-dependent transmembrane transport).
- RFX4: Regulatory Factor X #4: transcription factor.
- CA14: Carbonic anhydrase #14: Zync metalloenzyme catalyzes
reversible hydration of CO2. This gene looks pretty neat, but not really
relevant to anything we are likely to care about.
- EGR1: Early Growth Response Protein #1: Another Tx factor
(zinc-finger) – important for cell survival/proliferation/cell death.
Presumably important for healing?
- MCF2L: MCF.2 Cell Line Derived Transforming Sequence Like? guanine
nucleotide exchange factor interacting with GTP-bound Rac1. Apparently
associated with ostroarthritis; potentially relevant to regulation of
RHOA and CDC42 signalling.
- DNASE1L3: Deoxyribonuclease I family member: not inhibited by actin,
breaks down DNA during apoptosis. Important during necrosis.
- FOS: Proto-Oncogene, AP-1 Transcription Factor: leucine zipper
dimerizes with JUN family proteins, forming tx factor complex AP-1.
Important for cell proliferation, differentiation, and
transformation.
- IFITM10: Interferon-Induced Transmembrane Protein #10
- PKD1L3: Polycystin 1 Like #3, Transient Receptor Potential Channel
Interacting: 11 transmembrane domain protein which might help create
cation channels.
As some comparison points, the Venn in the current figure has:
- 387 up z2.3
- 259 up z2.2
- 83 shared up z2.3 and z2.2
- 247 down z2.3
- 3 down z2.2
- 3 shared down z2.3 and z2.2
2.2 and 2.3 with
SbV vs 2.2 and 2.3 without SbV
This is my version of the Venn with the text:
“Differentially expressed genes in macrophages infected with
subpopulations 2.2 or 2.3, in presence of SbV. Volcano plots contrast
of: A. infected cells with 2.3 strains + SbV and infected cells with 2.3
strains; B. infected cells with 2.2 strains + SbV and infected cells
with 2.2 strains; C. infected cells with 2.3 strains + SbV and infected
cells with 2.2 strains + SbV. D. Venn diagram for upregulated and
downregulated genes by infection with 2.3+SbV and 2.2+SbV strains.”
A query from Olga (20240801): Please include in the upset in figure 3
the contrast of uninfected cells + SbV vs uninfected without SbV.
## I keep mis-interpreting this text, it is z2.3/z2.3SbV and z2.2/z2.2SbV
drugnodrug_upset <- upsetr_combined_de(hs_macr_table,
desired_contrasts = c("z23sb_vs_z23nosb", "z22sb_vs_z22nosb"))
pp(file = "images/drugnodrug_upset.pdf")
drugnodrug_upset[["plot"]]
dev.off()
## png
## 2
## Plot describing unique/shared genes in a differential expression table.

drugnodrug_uninf_contrasts <- c("z23sb_vs_z23nosb", "z22sb_vs_z22nosb", "sb_vs_uninf")
drugnodrug_upset_with_uninf <- upsetr_combined_de(hs_macr_table,
desired_contrasts = drugnodrug_uninf_contrasts)
pp(file = "figures/drugnodrug_with_uninf_upset.svg")
drugnodrug_upset_with_uninf[["plot"]]
dev.off()
## png
## 2
drugnodrug_upset_with_uninf
## Plot describing unique/shared genes in a differential expression table.

For some comparison points, the venn image has:
- 222 up z2.3 SbV
- 134 up z2.2 SbV
- 182 down z2.3 SbV
- 396 down z2.2 SbV
- 605 shared down z2.2 and z2.3 SbV
- 34 shared down z2.2 SbV and up z2.3 SbV
- 363 shared up z2.2 SbV and z2.3 SbV
Compare z2.2SbV vs
SbV and z2.3SbV and SbV
drug_upset <- upsetr_combined_de(hs_macr_table,
desired_contrasts = c("z22sb_vs_sb", "z23sb_vs_sb"))
pp(file = "images/drug_upset.pdf")
drug_upset[["plot"]]
dev.off()
## png
## 2
## Plot describing unique/shared genes in a differential expression table.

Significance barplot
of interest
Olga kindly sent a set of particularly interesting contrasts and
colors for a significance barplot, they include the following:
- z2.3 vs. uninfected.
- z2.2 vs. uninfected.
- z2.3 vs z2.2
- z2.3Sbv vs z2.3
- z2.2Sbv vs z2.2
- z2.3Sbv vs z2.2Sbv
- Sbv vs uninfected.
The existing set of ‘keepers’ exvised to these is taken from the
extant set of ‘tmrc2_human_keepers’ and is as follows:
barplot_keepers <- list(
## z2.3 vs uninfected
"z23nosb_vs_uninf" = c("infz23", "uninfnone"),
## z2.2 vs uninfected
"z22nosb_vs_uninf" = c("infz22", "uninfnone"),
## z2.3 vs z2.2
"z23nosb_vs_z22nosb" = c("infz23", "infz22"),
## z2.3Sbv vs z2.3
"z23sb_vs_z23nosb" = c("infsbz23", "infz23"),
## z2.2Sbv vs z2.2
"z22sb_vs_z22nosb" = c("infsbz22", "infz22"),
## z2.3Sbv vs z2.2Sbv
"z23sb_vs_z22sb" = c("infsbz23", "infsbz22"),
## Sbv vs uninfected.
"sb_vs_uninf" = c("uninfsbnone", "uninfnone"))
barplot_combined <- combine_de_tables(
hs_macr_de, keepers = barplot_keepers,
excel = glue("analyses/macrophage_de/de_tables/hs_macr_drug_zymo_7contrasts-v{ver}.xlsx"))
## The keepers has no elements in the coefficients.
## Here are the keepers: infz23, uninfnone, infz22, uninfnone, infz23, infz22, infsbz23, infz23, infsbz22, infz22, infsbz23, infsbz22, uninfsbnone, uninfnone
## Here are the coefficients: z23z22drug, z23z22nodrug, z23drugnodrug, z22drugnodrug, uninf_sb_none, uninf_none, uninf_sb_none, inf_z23, uninf_none, inf_z23, uninf_sb_none, inf_z22, uninf_none, inf_z22, inf_z23, inf_z22, uninf_sb_none, inf_sb_z23, uninf_none, inf_sb_z23, inf_z23, inf_sb_z23, inf_z22, inf_sb_z23, uninf_sb_none, inf_sb_z22, uninf_none, inf_sb_z22, inf_z23, inf_sb_z22, inf_z22, inf_sb_z22, inf_sb_z23, inf_sb_z22
## Error in extract_keepers(extracted, keepers, table_names, all_coefficients, : Unable to find the set of contrasts to keep, fix this and try again.
Now let us use the colors suggested by Olga to make a barplot of
these…
color_list <- c( "#de8bf9", "#ad07e3","#410257", "#ffa0a0", "#f94040", "#a00000")
barplot_sig <- extract_significant_genes(
barplot_combined, color_list = color_list, according_to = "deseq",
excel = glue("analyses/macrophage_de/sig_tables/hs_macr_drug_zymo_7contrasts_sig-v{ver}.xlsx"))
## Error: object 'barplot_combined' not found
## Error: object 'barplot_sig' not found
Primary query
contrasts
The final contrast in this list is interesting because it depends on
the extra contrasts applied to the all_pairwise() above. In my way of
thinking, the primary comparisons to consider are either cross-drug or
cross-strain, but not both. However I think in at least a few instances
Olga is interested in strain+drug / uninfected+nodrug.
Write contrast
results
Now let us write out the xlsx file containing the above contrasts.
The file with the suffix _table-version will therefore contain all genes
and the file with the suffix _sig-version will contain only those deemed
significant via our default criteria of DESeq2 |logFC| >= 1.0 and
adjusted p-value <= 0.05.
Over representation
searches
I decided to make one initially small, but I think quickly big change
to the organization of this document: I am moving the GSEA searches up
to immediately after the DE. I will then move the plots of the gprofiler
results to immediately after the various volcano plots so that it is
easier to interpret them.
I am reasonably certain this is the place to check that z23no drug /
uninfected has the expected set of genes and that there is or is not a
reactome result.
Reproducibility note: Given that this is entirely dependent on an
online service, I must assume that the results will change over time; in
addition their web servers undergo maintenance regularly, which may
result in systematic failure of these analyses. I like gProfiler quite a
lot for this type of stuff, but this is an important caveat.
Conversely, the clusterProfiler results later depend on a consistent
orgdb annotation set (or reactome or whatever); those versions are fixed
by the container installation.
all_gp <- all_gprofiler(hs_macr_sig, enrich_id_column = "hgncsymbol")
for (g in seq_len(length(all_gp))) {
name <- names(all_gp)[g]
datum <- all_gp[[name]]
filename <- glue("analyses/macrophage_de/gprofiler/{name}_gprofiler-v{ver}.xlsx")
written <- sm(write_gprofiler_data(datum, excel = filename))
}
Explicit GSEA search
vis clusterProfiler
all_cp <- all_cprofiler(hs_macr_sig, hs_macr_table)
## Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
## Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
## ReactomePA v1.52.0 Learn more at https://yulab-smu.top/contribution-knowledge-mining/
##
## Please cite:
##
## Guangchuang Yu, Qing-Yu He. ReactomePA: an R/Bioconductor package for
## reactome pathway analysis and visualization. Molecular BioSystems.
## 2016, 12(2):477-479
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(up, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this DOSE organism, leaving it as human.
## Warning in simple_clusterprofiler(down, table, orgdb = orgdb, orgdb_from =
## orgdb_from, : I do not know this mesh organism, leaving it as human.
Specific desires in
Reactome results
In previous analyses (I think by Dr. Colmenares), a specific
Tryptophan biosynthesis pathway was observed. Partciularly in the
2.3/uninfected comparison. I think my gprofiler analysis is too
stringent and therefore not observing this. Olga asked if I could look
at that and see if there are trivial settings I can change to highlight
this pathway. The two most likely things I can change are the
stringencies of the DE analysis and/or gProfiler.
test_z23_uninf_up <- hs_macr_sig[["deseq"]][["ups"]][["z23nosb_vs_uninf"]]
nrow(test_z23_uninf_up)
## [1] 478
test_z23_uninf_down <- hs_macr_sig[["deseq"]][["downs"]][["z23nosb_vs_uninf"]]
nrow(test_z23_uninf_down)
## [1] 265
test_gp_up <- simple_gprofiler(test_z23_uninf_up, enrich_id_column = "hgncsymbol",
threshold = 1.0)
test_gp_up
written_up <- write_gprofiler_data(test_gp_up, excel = "excel/z23_uninf_gp_up_all.xlsx")
test_gp_down <- simple_gprofiler(test_z23_uninf_down, enrich_id_column = "hgncsymbol",
threshold = 1.0)
test_gp_down
written_down <- write_gprofiler_data(test_gp_down, excel = "excel/z23_uninf_gp_down_all.xlsx")
Plot contrasts of
interest
One suggestion I received recently was to set the axes for these
volcano plots to be static rather than let ggplot choose its own. I am
assuming this is only relevant for pairs of contrasts, but that might
not be true.
Individual zymodemes
vs. uninfected
The following blocks will be a lot of repetition. In each case I am
yanking out the volcano plot for a specific contrast and showing the
original followed by a version with different colors/labelling.
Infected with z2.3
no Antimonial vs. Uninfected
plot_colors <- get_expt_colors(hs_macr_table[["input"]][["input"]])
## Error in get_expt_colors(hs_macr_table[["input"]][["input"]]): could not find function "get_expt_colors"
## The original plot from my xlsx file
hs_macr_table[["plots"]][["z23nosb_vs_uninf"]][["deseq_vol_plots"]]

z23nosb_vs_uninf_volcano <- plot_volcano_condition_de(
input = hs_macr_table[["data"]][["z23nosb_vs_uninf"]],
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol",
color_low = plot_colors[["uninfnone"]], color_high = plot_colors[["infz23"]])
## Error: object 'plot_colors' not found
labeled <- z23nosb_vs_uninf_volcano[["plot"]] +
scale_x_continuous(limits = c(-6, 21), breaks = c(-6, -4, -2, 0, 2, 4, 6, 8, 10, 20)) +
ggbreak::scale_x_break(c(10, 19), scales = 0.2, space = 0.02)
## Error: object 'z23nosb_vs_uninf_volcano' not found
pp(file = "figures/fig2a_labeled_with_break.svg")
labeled
## Error: object 'labeled' not found
## png
## 2
## Error: object 'labeled' not found
plotly::ggplotly(z23nosb_vs_uninf_volcano[["plot"]])
## Error: object 'z23nosb_vs_uninf_volcano' not found
The following provides some of the over-representation plots from
gProfiler2.
all_gp[["z23nosb_vs_uninf_up"]][["pvalue_plots"]][["REAC"]]

## Reactome, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_uninf_up"]][["pvalue_plots"]][["KEGG"]]

## KEGG, zymodeme2.3 without drug vs. uninfected without drug, up.
##all_gp[["z23nosb_vs_uninf_up"]][["pvalue_plots"]][["MF"]]
## MF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_uninf_up"]][["pvalue_plots"]][["TF"]]

## TF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_uninf_up"]][["pvalue_plots"]][["WP"]]

## WikiPathways, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_uninf_up"]][["interactive_plots"]][["WP"]]
message("Olga received a query about the following result, I think it is null.")
## Olga received a query about the following result, I think it is null.
all_gp[["z23nosb_vs_uninf_down"]][["pvalue_plots"]][["REAC"]]
## NULL
message("Is the previous plot null?")
## Is the previous plot null?
## Reactome, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z23nosb_vs_uninf_down"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z23nosb_vs_uninf_down"]][["pvalue_plots"]][["TF"]]

## TF, zymodeme2.3 without drug vs. uninfected without drug, down.
We have some other categorical enrichment plots available via
enrichplot, let us try a few out for contrasts of interest and see if
any of them prove helpful.
First, as a reminder, here are the contrasts which are available to
examine, in each case there is an _up and _down enrichment object in the
data. Thus in the following list I am going to arbitrarily print out
some invocations which extract putatively interesting bits of data.
- z23nosb_vs_uninf:
all_gp[[“z23nosb_vs_uninf_up”]][[“BP_enrich”]]
- z22nosb_vs_uninf.
- z23nosb_vs_z22nosb.
- z23sb_vs_z22sb.
- z23sb_vs_z23nosb.
- z22sb_vs_z22nosb.
- z23sb_vs_sb.
- z22sb_vs_sb.
- z23sb_vs_uninf.
- z22sb_vs_uninf.
- sb_vs_uninf.
- extra_z2322.
- extra_drugnodrug.
z23nosb_uninf_up_go <- all_gp[["z23nosb_vs_uninf_up"]][["BP_enrich"]]
z23nosb_uninf_up_go_pair <- pairwise_termsim(z23nosb_uninf_up_go)
dotplot(z23nosb_uninf_up_go)

emapplot(z23nosb_uninf_up_go_pair)

##ssplot(z23nosb_uninf_up_go_pair)
treeplot(z23nosb_uninf_up_go_pair)

upsetplot(z23nosb_uninf_up_go)

cnetplot(z23nosb_uninf_up_go)
## Warning: ggrepel: 5 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

Repeat, but using
a less strict set of ‘significant genes’
I am not entirely certain if the Reactome results Olga showed me
included both up and down genes? I am going to assume for the moment
that it was just up/down, but if that proves intractable I will go back
to the manuscript and read more carefully (e.g. I just remembered where
the picture came from!)
Add a little
topgo
In the process of exploring the various parameters used with
gProfiler2, I found myself thinking that it would be nice to have some
topgo results to compare against. The following block is the result of
that thought.
test_genes_up <- hs_macr_lesssig[["deseq"]][["ups"]][["z23nosb_vs_uninf"]]
test_query_up <- simple_gprofiler(test_genes_up, threshold = 0.1)
test_query_up[["pvalue_plots"]][["REAC"]]

pdf(file = "images/test_query_biological_process_z23_vs_uninf_up.pdf", height = 12, width = 9)
test_query_up[["pvalue_plots"]][["BP"]]
## NULL
## png
## 2
enrichplot::dotplot(test_query_up[["BP_enrich"]])

test_genes_down <- hs_macr_lesssig[["deseq"]][["downs"]][["z23nosb_vs_uninf"]]
test_query_down <- simple_gprofiler(test_genes_down)
test_query_down[["pvalue_plots"]][["REAC"]]
## NULL
## I keep getting all sorts of annoying biomart errors.
hs_go <- try(load_biomart_go(archive = FALSE, overwrite = TRUE))
## Using mart: ENSEMBL_MART_ENSEMBL from host: useast.ensembl.org.
## Successfully connected to the hsapiens_gene_ensembl database.
## Finished downloading ensembl go annotations, saving to hsapiens_go_annotations.rda.
## Saving ontologies to hsapiens_go_annotations.rda.
## Finished save().
if ("try-error" %in% class(hs_go)) {
hs_go <- load_biomart_go(archive = TRUE, month = "04", year = "2020", overwrite = TRUE)
}
test_topgo_up <- simple_topgo(test_genes_up, go_db = hs_go[["go"]], parallel = FALSE)
## Starting fisher.
##
## Building most specific GOs .....
## ( 12141 GO terms found. )
##
## Build GO DAG topology ..........
## ( 15035 GO terms and 33212 relations. )
##
## Annotating nodes ...............
## ( 23807 genes annotated to the GO terms. )
##
## -- Classic Algorithm --
##
## the algorithm is scoring 7108 nontrivial nodes
## parameters:
## test statistic: Fisher test
##
## Building most specific GOs .....
## ( 4728 GO terms found. )
##
## Build GO DAG topology ..........
## ( 5090 GO terms and 6622 relations. )
##
## Annotating nodes ...............
## ( 22925 genes annotated to the GO terms. )
##
## -- Classic Algorithm --
##
## the algorithm is scoring 1514 nontrivial nodes
## parameters:
## test statistic: Fisher test
##
## Building most specific GOs .....
## ( 1839 GO terms found. )
##
## Build GO DAG topology ..........
## ( 1990 GO terms and 3238 relations. )
##
## Annotating nodes ...............
## ( 25399 genes annotated to the GO terms. )
##
## -- Classic Algorithm --
##
## the algorithm is scoring 797 nontrivial nodes
## parameters:
## test statistic: Fisher test
## Starting KS.
##
## Building most specific GOs .....
## ( 12141 GO terms found. )
##
## Build GO DAG topology ..........
## ( 15035 GO terms and 33212 relations. )
##
## Annotating nodes ...............
## ( 23807 genes annotated to the GO terms. )
##
## -- Classic Algorithm --
##
## the algorithm is scoring 15035 nontrivial nodes
## parameters:
## test statistic: KS test
## score order: increasing
##
## Building most specific GOs .....
## ( 4728 GO terms found. )
##
## Build GO DAG topology ..........
## ( 5090 GO terms and 6622 relations. )
##
## Annotating nodes ...............
## ( 22925 genes annotated to the GO terms. )
##
## -- Classic Algorithm --
##
## the algorithm is scoring 5090 nontrivial nodes
## parameters:
## test statistic: KS test
## score order: increasing
##
## Building most specific GOs .....
## ( 1839 GO terms found. )
##
## Build GO DAG topology ..........
## ( 1990 GO terms and 3238 relations. )
##
## Annotating nodes ...............
## ( 25399 genes annotated to the GO terms. )
##
## -- Classic Algorithm --
##
## the algorithm is scoring 1990 nontrivial nodes
## parameters:
## test statistic: KS test
## score order: increasing
## Starting EL.
##
## Building most specific GOs .....
## ( 12141 GO terms found. )
##
## Build GO DAG topology ..........
## ( 15035 GO terms and 33212 relations. )
##
## Annotating nodes ...............
## ( 23807 genes annotated to the GO terms. )
##
## -- Elim Algorithm --
##
## the algorithm is scoring 15035 nontrivial nodes
## parameters:
## test statistic: KS test
## cutOff: 0.05
## score order: increasing
##
## Level 19: 2 nodes to be scored (0 eliminated genes)
##
## Level 18: 29 nodes to be scored (0 eliminated genes)
##
## Level 17: 62 nodes to be scored (2 eliminated genes)
##
## Level 16: 105 nodes to be scored (12 eliminated genes)
##
## Level 15: 159 nodes to be scored (1325 eliminated genes)
##
## Level 14: 276 nodes to be scored (1485 eliminated genes)
##
## Level 13: 653 nodes to be scored (1602 eliminated genes)
##
## Level 12: 1153 nodes to be scored (1714 eliminated genes)
##
## Level 11: 1736 nodes to be scored (4571 eliminated genes)
##
## Level 10: 2021 nodes to be scored (5779 eliminated genes)
##
## Level 9: 2233 nodes to be scored (7005 eliminated genes)
##
## Level 8: 2068 nodes to be scored (9904 eliminated genes)
##
## Level 7: 1810 nodes to be scored (11691 eliminated genes)
##
## Level 6: 1386 nodes to be scored (13910 eliminated genes)
##
## Level 5: 847 nodes to be scored (15790 eliminated genes)
##
## Level 4: 376 nodes to be scored (16838 eliminated genes)
##
## Level 3: 101 nodes to be scored (18453 eliminated genes)
##
## Level 2: 17 nodes to be scored (18857 eliminated genes)
##
## Level 1: 1 nodes to be scored (19063 eliminated genes)
##
## Building most specific GOs .....
## ( 4728 GO terms found. )
##
## Build GO DAG topology ..........
## ( 5090 GO terms and 6622 relations. )
##
## Annotating nodes ...............
## ( 22925 genes annotated to the GO terms. )
##
## -- Elim Algorithm --
##
## the algorithm is scoring 5090 nontrivial nodes
## parameters:
## test statistic: KS test
## cutOff: 0.05
## score order: increasing
##
## Level 13: 2 nodes to be scored (0 eliminated genes)
##
## Level 12: 42 nodes to be scored (0 eliminated genes)
##
## Level 11: 55 nodes to be scored (31 eliminated genes)
##
## Level 10: 139 nodes to be scored (34 eliminated genes)
##
## Level 9: 355 nodes to be scored (111 eliminated genes)
##
## Level 8: 657 nodes to be scored (180 eliminated genes)
##
## Level 7: 1084 nodes to be scored (464 eliminated genes)
##
## Level 6: 1418 nodes to be scored (2074 eliminated genes)
##
## Level 5: 772 nodes to be scored (3738 eliminated genes)
##
## Level 4: 424 nodes to be scored (5640 eliminated genes)
##
## Level 3: 113 nodes to be scored (7799 eliminated genes)
##
## Level 2: 28 nodes to be scored (20138 eliminated genes)
##
## Level 1: 1 nodes to be scored (20138 eliminated genes)
##
## Building most specific GOs .....
## ( 1839 GO terms found. )
##
## Build GO DAG topology ..........
## ( 1990 GO terms and 3238 relations. )
##
## Annotating nodes ...............
## ( 25399 genes annotated to the GO terms. )
##
## -- Elim Algorithm --
##
## the algorithm is scoring 1990 nontrivial nodes
## parameters:
## test statistic: KS test
## cutOff: 0.05
## score order: increasing
##
## Level 13: 3 nodes to be scored (0 eliminated genes)
##
## Level 12: 34 nodes to be scored (0 eliminated genes)
##
## Level 11: 95 nodes to be scored (4 eliminated genes)
##
## Level 10: 244 nodes to be scored (1400 eliminated genes)
##
## Level 9: 323 nodes to be scored (1982 eliminated genes)
##
## Level 8: 321 nodes to be scored (2782 eliminated genes)
##
## Level 7: 262 nodes to be scored (8903 eliminated genes)
##
## Level 6: 245 nodes to be scored (11552 eliminated genes)
##
## Level 5: 209 nodes to be scored (13155 eliminated genes)
##
## Level 4: 146 nodes to be scored (19562 eliminated genes)
##
## Level 3: 105 nodes to be scored (21087 eliminated genes)
##
## Level 2: 2 nodes to be scored (22388 eliminated genes)
##
## Level 1: 1 nodes to be scored (22388 eliminated genes)
## Starting weight.
##
## Building most specific GOs .....
## ( 12141 GO terms found. )
##
## Build GO DAG topology ..........
## ( 15035 GO terms and 33212 relations. )
##
## Annotating nodes ...............
## ( 23807 genes annotated to the GO terms. )
##
## -- Weight Algorithm --
##
## The algorithm is scoring 7108 nontrivial nodes
## parameters:
## test statistic: Fisher test : ratio
##
## Level 19: 2 nodes to be scored.
##
## Level 18: 16 nodes to be scored.
##
## Level 17: 26 nodes to be scored.
##
## Level 16: 35 nodes to be scored.
##
## Level 15: 55 nodes to be scored.
##
## Level 14: 89 nodes to be scored.
##
## Level 13: 199 nodes to be scored.
##
## Level 12: 401 nodes to be scored.
##
## Level 11: 634 nodes to be scored.
##
## Level 10: 822 nodes to be scored.
##
## Level 9: 1012 nodes to be scored.
##
## Level 8: 1063 nodes to be scored.
##
## Level 7: 1005 nodes to be scored.
##
## Level 6: 845 nodes to be scored.
##
## Level 5: 531 nodes to be scored.
##
## Level 4: 273 nodes to be scored.
##
## Level 3: 83 nodes to be scored.
##
## Level 2: 16 nodes to be scored.
##
## Level 1: 1 nodes to be scored.
##
## Building most specific GOs .....
## ( 4728 GO terms found. )
##
## Build GO DAG topology ..........
## ( 5090 GO terms and 6622 relations. )
##
## Annotating nodes ...............
## ( 22925 genes annotated to the GO terms. )
##
## -- Weight Algorithm --
##
## The algorithm is scoring 1514 nontrivial nodes
## parameters:
## test statistic: Fisher test : ratio
##
## Level 12: 25 nodes to be scored.
##
## Level 11: 19 nodes to be scored.
##
## Level 10: 41 nodes to be scored.
##
## Level 9: 104 nodes to be scored.
##
## Level 8: 160 nodes to be scored.
##
## Level 7: 222 nodes to be scored.
##
## Level 6: 326 nodes to be scored.
##
## Level 5: 295 nodes to be scored.
##
## Level 4: 235 nodes to be scored.
##
## Level 3: 64 nodes to be scored.
##
## Level 2: 22 nodes to be scored.
##
## Level 1: 1 nodes to be scored.
##
## Building most specific GOs .....
## ( 1839 GO terms found. )
##
## Build GO DAG topology ..........
## ( 1990 GO terms and 3238 relations. )
##
## Annotating nodes ...............
## ( 25399 genes annotated to the GO terms. )
##
## -- Weight Algorithm --
##
## The algorithm is scoring 797 nontrivial nodes
## parameters:
## test statistic: Fisher test : ratio
##
## Level 12: 3 nodes to be scored.
##
## Level 11: 29 nodes to be scored.
##
## Level 10: 73 nodes to be scored.
##
## Level 9: 121 nodes to be scored.
##
## Level 8: 133 nodes to be scored.
##
## Level 7: 123 nodes to be scored.
##
## Level 6: 110 nodes to be scored.
##
## Level 5: 83 nodes to be scored.
##
## Level 4: 64 nodes to be scored.
##
## Level 3: 55 nodes to be scored.
##
## Level 2: 2 nodes to be scored.
##
## Level 1: 1 nodes to be scored.
## Warning in .genesInNode(graph(object), whichGO): Nodes not present in the
## graph:NANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANANA
## simple_topgo(): Set densities = TRUE for ontology density plots.
## Getting enrichResult for ontology: bp.
## Error in FUN(X[[i]], ...): This currently only understands vectors and dataframes.
written_topgo <- write_topgo_data(
test_topgo_up,
excel = glue("analyses/macrophage_de/ontology_topgo/topgo_z23_uninf_less_strict.xlsx"))
## Error: object 'test_topgo_up' not found
Infected with z2.2
no Antimonial vs. Uninfected
Here is where things will get most repetitive. In each instance I am
creating a couple of volcano plots followed by printing some of the
gProfiler2 results (when I get the itch).
The following should be a slightly improved version of our extant
figure 2B.
## The original plot
hs_macr_table[["plots"]][["z22nosb_vs_uninf"]][["deseq_vol_plots"]]

z22nosb_vs_uninf_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["z22nosb_vs_uninf"]], "z22nosb_vs_uninf",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol",
color_low = plot_colors[["uninfnone"]], color_high = plot_colors[["infz22"]])
## Error: object 'plot_colors' not found
labeled <- z22nosb_vs_uninf_volcano[["plot"]] +
scale_x_continuous(limits = c(-2, 21), breaks = c(-2, 0, 2, 4, 6, 8, 10, 21, 22)) +
ggbreak::scale_x_break(c(11, 20), scales = 0.2, space = 0.02)
## Error: object 'z22nosb_vs_uninf_volcano' not found
pp(file = "figures/fig2b_labeled_with_break.svg")
labeled
## Error: object 'labeled' not found
## png
## 2
## Error: object 'labeled' not found
plotly::ggplotly(z22nosb_vs_uninf_volcano[["plot"]])
## Error: object 'z22nosb_vs_uninf_volcano' not found
Add some pvalue barplots from gProfiler for this contrast.
all_gp[["z22nosb_vs_uninf_up"]][["pvalue_plots"]][["REAC"]]

## Reactome, zymodeme2.2 without drug vs. uninfected without drug, up.
all_gp[["z22nosb_vs_uninf_up"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.2 without drug vs. uninfected without drug, up.
all_gp[["z22nosb_vs_uninf_up"]][["pvalue_plots"]][["TF"]]

## TF, zymodeme2.2 without drug vs. uninfected without drug, up.
all_gp[["z22nosb_vs_uninf_up"]][["pvalue_plots"]][["WP"]]

## WikiPathways, zymodeme2.2 without drug vs. uninfected without drug, up.
all_gp[["z22nosb_vs_uninf_down"]][["pvalue_plots"]][["REAC"]]
## NULL
## Reactome, zymodeme2.2 without drug vs. uninfected without drug, down.
all_gp[["z22nosb_vs_uninf_down"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.2 without drug vs. uninfected without drug, down.
all_gp[["z22nosb_vs_uninf_down"]][["pvalue_plots"]][["TF"]]
## NULL
## TF, zymodeme2.3 without drug vs. uninfected without drug, down.
Infected with z2.3
treated vs. Uninfected treated
I do not think this plot is used at this time.
## The original plot
hs_macr_table[["plots"]][["z23sb_vs_sb"]][["deseq_vol_plots"]]

z23sb_vs_uninfsb_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["z23sb_vs_sb"]], "z23sb_vs_sb",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol",
color_low = plot_colors[["infsbz23"]], color_high = plot_colors[["uninfsbnone"]])
## Error: object 'plot_colors' not found
z23sb_vs_uninfsb_volcano[["plot"]]
## Error: object 'z23sb_vs_uninfsb_volcano' not found
plotly::ggplotly(z23sb_vs_uninfsb_volcano[["plot"]])
## Error: object 'z23sb_vs_uninfsb_volcano' not found
Infected with z2.3
untreated vs. z2.2 untreated
This is figure 2C at this time.
## The original plot
hs_macr_table[["plots"]][["z23nosb_vs_z22nosb"]][["deseq_vol_plots"]]

z23nosb_vs_z22nosb_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["z23nosb_vs_z22nosb"]], "z23nosb_vs_z22nosb",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol",
color_low = plot_colors[["infz23"]], color_high = plot_colors[["infz22"]])
## Error: object 'plot_colors' not found
labeled <- z23nosb_vs_z22nosb_volcano[["plot"]] +
scale_x_continuous(breaks = c(-10, -8, -6, -4, -2, 0, 2, 4, 6))
## Error: object 'z23nosb_vs_z22nosb_volcano' not found
pp(file = "figures/fig2c_labeled.svg")
labeled
## Error: object 'labeled' not found
## png
## 2
## Error: object 'labeled' not found
Infected with z2.3
treated vs. z2.2 treated
This is currently figure 3C.
FIXME: The axis label isn’t quite right for the ggbreak.
## The original plot
hs_macr_table[["plots"]][["z23sb_vs_z22sb"]][["deseq_vol_plots"]]

z23sb_vs_z22sb_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["z23sb_vs_z22sb"]], "z23sb_vs_z22sb",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol",
color_high = plot_colors[["infsbz23"]], color_low = plot_colors[["infsbz22"]])
## Error: object 'plot_colors' not found
labeled <- z23sb_vs_z22sb_volcano[["plot"]] +
scale_x_continuous(breaks = c(-23, -6, -4, -2, 0, 2, 4, 6)) +
ggbreak::scale_x_break(c(-5, -22.5), scales = 10, space = 0.02)
## Error: object 'z23sb_vs_z22sb_volcano' not found
pp(file = "figures/fig3c_labeled_breaks.svg")
labeled
## Error: object 'labeled' not found
## png
## 2
## Error: object 'labeled' not found
Infected with z2.3
SB treated vs. z2.3 untreated
I think this is currently figure 3A.
FIXME: The axis label for the ggbreak isn’t quite right.
## The original plot
hs_macr_table[["plots"]][["z23sb_vs_z23nosb"]][["deseq_vol_plots"]]

z23sb_vs_z23nosb_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["z23sb_vs_z23nosb"]], "z23sb_vs_z23nosb",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol",
color_high = plot_colors[["infsbz23"]], color_low = plot_colors[["infz23"]])
## Error: object 'plot_colors' not found
labeled <- z23sb_vs_z23nosb_volcano[["plot"]] +
scale_x_continuous(limits = c(-19, 6),
breaks = c(-20, -18, -16, -14, -12, -10, -6, -4, -2, 0, 2, 4, 6)) +
ggbreak::scale_x_break(c(-17, -8), scales = 17, space = 0.02)
## Error: object 'z23sb_vs_z23nosb_volcano' not found
pp(file = "figures/fig3a_labeled_with_break.svg")
labeled
## Error: object 'labeled' not found
## png
## 2
## Error: object 'labeled' not found
Infected with z2.3
SB treated vs. z2.3 untreated
## The original plot
hs_macr_table[["plots"]][["z22sb_vs_z22nosb"]][["deseq_vol_plots"]]

z22sb_vs_z22nosb_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["z22sb_vs_z22nosb"]], "z22sb_vs_z22nosb",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol",
color_high = plot_colors[["infsbz22"]], color_low = plot_colors[["infz22"]])
## Error: object 'plot_colors' not found
labeled <- z22sb_vs_z22nosb_volcano[["plot"]] +
scale_x_continuous(breaks = c(-6, -4, -2, 0, 2, 4, 6))
## Error: object 'z22sb_vs_z22nosb_volcano' not found
pp(file = "figures/fig3b_labeled.svg")
labeled
## Error: object 'labeled' not found
## png
## 2
## Error: object 'labeled' not found
Infected with z2.3
SB treated vs. uninfected treated
x_limits <- c(-6, 6)
## The original plot
hs_macr_table[["plots"]][["z23sb_vs_sb"]][["deseq_vol_plots"]]

z23sb_vs_sb_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["z23sb_vs_sb"]], "z23sb_vs_sb",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol", invert = TRUE,
color_low = plot_colors[["infsbz23"]], color_high = plot_colors[["uninfsbnone"]])
## Error: object 'plot_colors' not found
z23sb_vs_sb_volcano[["plot"]]
## Error: object 'z23sb_vs_sb_volcano' not found
Infected with
z2.2 SB treated vs. uninfected treated
## The original plot
hs_macr_table[["plots"]][["z22sb_vs_sb"]][["deseq_vol_plots"]]

z22sb_vs_sb_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["z22sb_vs_sb"]], "z22sb_vs_sb",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol", invert = TRUE,
color_low = plot_colors[["infsbz22"]], color_high = plot_colors[["uninfsbnone"]])
## Error: object 'plot_colors' not found
z22sb_vs_sb_volcano[["plot"]]
## Error: object 'z22sb_vs_sb_volcano' not found
Uninfected+SbV
vs. Uninfected-SbV
This is currently figure 3D.
FIXME: This needs the BOLA2B ggbreak.
## The original plot
hs_macr_table[["plots"]][["sb_vs_uninf"]][["deseq_vol_plots"]]

sb_vs_uninf_volcano <- plot_volcano_condition_de(
hs_macr_table[["data"]][["sb_vs_uninf"]], "sb_vs_uninf",
fc_col = "deseq_logfc", p_col = "deseq_adjp",
label = 10, label_column = "hgncsymbol",
color_high = plot_colors[["uninfsbnone"]], color_low = plot_colors[["uninfnone"]])
## Error: object 'plot_colors' not found
labeled <- sb_vs_uninf_volcano[["plot"]] +
scale_x_continuous(breaks = c(-23, -6, -4, -2, 0, 2, 4, 6)) +
ggbreak::scale_x_break(c(-5, -22.5), scales = 10, space = 0.02)
## Error: object 'sb_vs_uninf_volcano' not found
pp(file = "figures/fig3d_labeled_breaks.svg")
labeled
## Error: object 'labeled' not found
## png
## 2
## Error: object 'labeled' not found
Double-check that
gene counts match my perceptions
Check that my perception of the number of significant up/down genes
matches what the table/venn says. In the following block I am performing
some venn/upset analyses to see if the numbers of genes match what we
have in the current version of the manuscript (plus or minus a gene) and
thus if my interpretation of the figure/legend text matches what I think
it means.
shared <- Vennerable::Venn(list(
"drug" = rownames(hs_macr_sig[["deseq"]][["ups"]][["z23sb_vs_uninf"]]),
"nodrug" = rownames(hs_macr_sig[["deseq"]][["ups"]][["z23nosb_vs_uninf"]])))
pp(file = "images/z23_vs_uninf_venn_up.png")
Vennerable::plot(shared)
dev.off()
## png
## 2

## I see 910 z23sb/uninf and 670 no z23nosb/uninf genes in the venn diagram.
length(shared@IntersectionSets[["10"]]) + length(shared@IntersectionSets[["11"]])
## [1] 839
dim(hs_macr_sig[["deseq"]][["ups"]][["z23sb_vs_uninf"]])
## [1] 839 64
shared <- Vennerable::Venn(list(
"drug" = rownames(hs_macr_sig[["deseq"]][["ups"]][["z22sb_vs_uninf"]]),
"nodrug" = rownames(hs_macr_sig[["deseq"]][["ups"]][["z22nosb_vs_uninf"]])))
pp(file = "images/z22_vs_uninf_venn_up.png")
Vennerable::plot(shared)
dev.off()
## png
## 2

length(shared@IntersectionSets[["10"]]) + length(shared@IntersectionSets[["11"]])
## [1] 660
dim(hs_macr_sig[["deseq"]][["ups"]][["z22sb_vs_uninf"]])
## [1] 660 64
Note to self: There is an error in my volcano plot code
which takes effect when the numerator and denominator of the
all_pairwise contrasts are different than those in combine_de_tables. It
is putting the ups/downs on the correct sides of the plot, but calling
the down genes ‘up’ and vice-versa. The reason for this is that I did a
check for this happening, but used the wrong argument to handle it.
A likely bit of text for these volcano plots:
The set of genes differentially expressed between the zymodeme 2.3
and uninfected samples without druge treatment was quantified with
DESeq2 and included surrogate estimates from SVA. Given the criteria of
significance of a abs(logFC) >= 1.0 and false discovery rate adjusted
p-value <= 0.05, 670 genes were observed as significantly increased
between the infected and uninfected samples and 386 were observed as
decreased. The most increased genes from the uninfected samples include
some which are potentially indicative of a strong innate immune response
and the inflammatory response.
In contrast, when the set of genes differentially expressed between
the zymodeme 2.2 and uninfected samples was visualized, only 7 genes
were observed as decreased and 435 increased. The inflammatory response
was significantly less apparent in this set, but instead included genes
related to transporter activity and oxidoreductases.
Direct zymodeme
comparisons
An orthogonal comparison to that performed above is to directly
compare the zymodeme 2.3 and 2.2 samples with and without antimonial
treatment.
Z2.3 / z2.2
without drug
z23nosb_vs_z22nosb_volcano <- plot_volcano_de(
table = hs_macr_table[["data"]][["z23nosb_vs_z22nosb"]],
fc_col = "deseq_logfc", p_col = "deseq_adjp",
shapes_by_state = FALSE, color_by = "fc", label = 10, label_column = "hgncsymbol")
## Error in plot_volcano_de(table = hs_macr_table[["data"]][["z23nosb_vs_z22nosb"]], : could not find function "plot_volcano_de"
plotly::ggplotly(z23nosb_vs_z22nosb_volcano[["plot"]])
## Error: object 'z23nosb_vs_z22nosb_volcano' not found
z23sb_vs_z22sb_volcano <- plot_volcano_de(
table = hs_macr_table[["data"]][["z23sb_vs_z22sb"]],
fc_col = "deseq_logfc", p_col = "deseq_adjp",
shapes_by_state = FALSE, color_by = "fc", label = 10, label_column = "hgncsymbol")
## Error in plot_volcano_de(table = hs_macr_table[["data"]][["z23sb_vs_z22sb"]], : could not find function "plot_volcano_de"
plotly::ggplotly(z23sb_vs_z22sb_volcano[["plot"]])
## Error: object 'z23sb_vs_z22sb_volcano' not found
z23nosb_vs_z22nosb_volcano[["plot"]] +
xlim(-10, 10) +
ylim(0, 60)
## Error: object 'z23nosb_vs_z22nosb_volcano' not found
pp(file = "images/z23nosb_vs_z22nosb_reactome_up.svg",
image = all_gp[["z23nosb_vs_z22nosb_up"]][["pvalue_plots"]][["REAC"]],
height = 12, width = 9)
## Warning: ImageMagick was built without librsvg which causes poor qualty of SVG rendering.
## For better results use image_read_svg() which uses the rsvg package.
## Error in eval(expr, envir): R: geometry does not contain image `/lab/singularity/tmrc2_macrophage_deb/202509011430_outputs/images/z23nosb_vs_z22nosb_reactome_up.svg' @ warning/attribute.c/GetImageBoundingBox/554
all_gp[["z23nosb_vs_z22nosb_up"]][["pvalue_plots"]][["REAC"]]

## Reactome, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_z22nosb_up"]][["pvalue_plots"]][["KEGG"]]

## KEGG, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_z22nosb_up"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_z22nosb_up"]][["pvalue_plots"]][["TF"]]

## TF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_z22nosb_up"]][["pvalue_plots"]][["WP"]]

## WikiPathways, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23nosb_vs_z22nosb_up"]][["interactive_plots"]][["WP"]]
pp(file = "images/z23nosb_vs_z22nosb_reactome_down.svg",
image = all_gp[["z23nosb_vs_z22nosb_down"]][["pvalue_plots"]][["REAC"]],
height = 12, width = 9)
## Warning: ImageMagick was built without librsvg which causes poor qualty of SVG rendering.
## For better results use image_read_svg() which uses the rsvg package.
## Error in eval(expr, envir): R: geometry does not contain image `/lab/singularity/tmrc2_macrophage_deb/202509011430_outputs/images/z23nosb_vs_z22nosb_reactome_down.svg' @ warning/attribute.c/GetImageBoundingBox/554
all_gp[["z23nosb_vs_z22nosb_down"]][["pvalue_plots"]][["REAC"]]

## Reactome, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z23nosb_vs_z22nosb_down"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z23nosb_vs_z22nosb_down"]][["pvalue_plots"]][["TF"]]

## TF, zymodeme2.3 without drug vs. uninfected without drug, down.
z2.3 / z2.2 with
drug
z23sb_vs_z22sb_volcano[["plot"]] +
xlim(-10, 10) +
ylim(0, 60)
## Error: object 'z23sb_vs_z22sb_volcano' not found
pp(
file = "images/z23sb_vs_z22sb_reactome_up.png",
image = all_gp[["z23sb_vs_z22sb_up"]][["pvalue_plots"]][["REAC"]],
height = 12, width = 9)
## Warning in pp(file = "images/z23sb_vs_z22sb_reactome_up.png", image =
## all_gp[["z23sb_vs_z22sb_up"]][["pvalue_plots"]][["REAC"]], : There is no device
## to shut down.
## Error in eval(expr, envir): R: improper image header `/lab/singularity/tmrc2_macrophage_deb/202509011430_outputs/images/z23sb_vs_z22sb_reactome_up.png' @ error/png.c/ReadPNGImage/3941
all_gp[["z23sb_vs_z22sb_up"]][["pvalue_plots"]][["REAC"]]
## Reactome, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z22sb_up"]][["pvalue_plots"]][["KEGG"]]
## KEGG, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z22sb_up"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z22sb_up"]][["pvalue_plots"]][["TF"]]
## TF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z22sb_up"]][["pvalue_plots"]][["WP"]]
## WikiPathways, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z22sb_up"]][["interactive_plots"]][["WP"]]
all_gp[["z23sb_vs_z22sb_down"]][["pvalue_plots"]][["REAC"]]
## Reactome, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z23sb_vs_z22sb_down"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z23sb_vs_z22sb_down"]][["pvalue_plots"]][["TF"]]
## TF, zymodeme2.3 without drug vs. uninfected without drug, down.
Venn to see
shared/unique genes
Once again I wish to pull out the significant genes and see how my
numbers match against the text.
shared <- Vennerable::Venn(list(
"drug" = rownames(hs_macr_sig[["deseq"]][["ups"]][["z23sb_vs_z22sb"]]),
"nodrug" = rownames(hs_macr_sig[["deseq"]][["ups"]][["z23nosb_vs_z22nosb"]])))
pp(file = "images/drug_nodrug_venn_up.png")
Vennerable::plot(shared)
dev.off()
## png
## 2

shared <- Vennerable::Venn(
list("drug" = rownames(hs_macr_sig[["deseq"]][["downs"]][["z23sb_vs_z22sb"]]),
"nodrug" = rownames(hs_macr_sig[["deseq"]][["downs"]][["z23nosb_vs_z22nosb"]])))
pp(file = "images/drug_nodrug_venn_down.png")
Vennerable::plot(shared)
dev.off()
## png
## 2

A slightly different way of looking at the differences between the
two zymodeme infections is to directly compare the infected samples with
and without drug. Thus, when a volcano plot showing the comparison of
the zymodeme 2.3 vs. 2.2 samples was plotted, 484 genes were observed as
increased and 422 decreased; these groups include many of the same
inflammatory (up) and membrane (down) genes.
Similar patterns were observed when the antimonial was included.
Thus, when a Venn diagram of the two sets of increased genes was
plotted, a significant number of the genes was observed as increased
(313) and decreased (244) in both the untreated and antimonial treated
samples.
Drug effects on each
zymodeme infection
Another likely question is to directly compare the treated vs
untreated samples for each zymodeme infection in order to visualize the
effects of antimonial.
z2.3 with and
without drug
z23sb_vs_z23nosb_volcano <- plot_volcano_de(
table = hs_macr_table[["data"]][["z23sb_vs_z23nosb"]],
fc_col = "deseq_logfc", p_col = "deseq_adjp",
shapes_by_state = FALSE, color_by = "fc", label = 10, label_column = "hgncsymbol")
## Error in plot_volcano_de(table = hs_macr_table[["data"]][["z23sb_vs_z23nosb"]], : could not find function "plot_volcano_de"
plotly::ggplotly(z23sb_vs_z23nosb_volcano[["plot"]])
## Error: object 'z23sb_vs_z23nosb_volcano' not found
z22sb_vs_z22nosb_volcano <- plot_volcano_de(
table = hs_macr_table[["data"]][["z22sb_vs_z22nosb"]],
fc_col = "deseq_logfc", p_col = "deseq_adjp",
shapes_by_state = FALSE, color_by = "fc", label = 10, label_column = "hgncsymbol")
## Error in plot_volcano_de(table = hs_macr_table[["data"]][["z22sb_vs_z22nosb"]], : could not find function "plot_volcano_de"
plotly::ggplotly(z22sb_vs_z22nosb_volcano[["plot"]])
## Error: object 'z22sb_vs_z22nosb_volcano' not found
z23sb_vs_z23nosb_volcano[["plot"]] +
xlim(-8, 8) +
ylim(0, 210)
## Error: object 'z23sb_vs_z23nosb_volcano' not found
pp(file = "images/z23sb_vs_z23nosb_reactome_up.png",
image = all_gp[["z23sb_vs_z23nosb_up"]][["pvalue_plots"]][["REAC"]],
height = 12, width = 9)
## Warning in pp(file = "images/z23sb_vs_z23nosb_reactome_up.png", image =
## all_gp[["z23sb_vs_z23nosb_up"]][["pvalue_plots"]][["REAC"]], : There is no
## device to shut down.
## Error in eval(expr, envir): R: improper image header `/lab/singularity/tmrc2_macrophage_deb/202509011430_outputs/images/z23sb_vs_z23nosb_reactome_up.png' @ error/png.c/ReadPNGImage/3941
all_gp[["z23sb_vs_z23nosb_up"]][["pvalue_plots"]][["REAC"]]
## Reactome, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z23nosb_up"]][["pvalue_plots"]][["KEGG"]]
## KEGG, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z23nosb_up"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z23nosb_up"]][["pvalue_plots"]][["TF"]]
## TF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z23nosb_up"]][["pvalue_plots"]][["WP"]]
## WikiPathways, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z23sb_vs_z23nosb_up"]][["interactive_plots"]][["WP"]]
all_gp[["z23sb_vs_z23nosb_down"]][["pvalue_plots"]][["REAC"]]
## Reactome, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z23sb_vs_z23nosb_down"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z23sb_vs_z23nosb_down"]][["pvalue_plots"]][["TF"]]
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_col()`).
## TF, zymodeme2.3 without drug vs. uninfected without drug, down.
z2.2 with and
without drug
z22sb_vs_z22nosb_volcano[["plot"]] +
xlim(-8, 8) +
ylim(0, 210)
## Error: object 'z22sb_vs_z22nosb_volcano' not found
pp(file = "images/z22sb_vs_z22nosb_reactome_up.png",
image = all_gp[["z22sb_vs_z22nosb_up"]][["pvalue_plots"]][["REAC"]],
height = 12, width = 9)
## Warning in pp(file = "images/z22sb_vs_z22nosb_reactome_up.png", image =
## all_gp[["z22sb_vs_z22nosb_up"]][["pvalue_plots"]][["REAC"]], : There is no
## device to shut down.
## Error in eval(expr, envir): R: improper image header `/lab/singularity/tmrc2_macrophage_deb/202509011430_outputs/images/z22sb_vs_z22nosb_reactome_up.png' @ error/png.c/ReadPNGImage/3941
all_gp[["z22sb_vs_z22nosb_up"]][["pvalue_plots"]][["REAC"]]
## Reactome, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z22sb_vs_z22nosb_up"]][["pvalue_plots"]][["KEGG"]]
## KEGG, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z22sb_vs_z22nosb_up"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z22sb_vs_z22nosb_up"]][["pvalue_plots"]][["TF"]]
## TF, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z22sb_vs_z22nosb_up"]][["pvalue_plots"]][["WP"]]
## WikiPathways, zymodeme2.3 without drug vs. uninfected without drug, up.
all_gp[["z22sb_vs_z22nosb_up"]][["interactive_plots"]][["WP"]]
all_gp[["z22sb_vs_z22nosb_down"]][["pvalue_plots"]][["REAC"]]
## Reactome, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z22sb_vs_z22nosb_down"]][["pvalue_plots"]][["MF"]]
## NULL
## MF, zymodeme2.3 without drug vs. uninfected without drug, down.
all_gp[["z22sb_vs_z22nosb_down"]][["pvalue_plots"]][["TF"]]
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_col()`).
## TF, zymodeme2.3 without drug vs. uninfected without drug, down.
Shared and unique
genes after/before drug
shared <- Vennerable::Venn(list(
"z23" = rownames(hs_macr_sig[["deseq"]][["ups"]][["z23sb_vs_z23nosb"]]),
"z22" = rownames(hs_macr_sig[["deseq"]][["ups"]][["z22sb_vs_z22nosb"]])))
pp(file = "images/z23_z22_drug_venn_up.png")
Vennerable::plot(shared)
dev.off()
## png
## 2

shared <- Vennerable::Venn(list(
"z23" = rownames(hs_macr_sig[["deseq"]][["downs"]][["z23sb_vs_z23nosb"]]),
"z22" = rownames(hs_macr_sig[["deseq"]][["downs"]][["z22sb_vs_z22nosb"]])))
pp(file = "images/z23_z22_drug_venn_down.png")
Vennerable::plot(shared)
dev.off()
## png
## 2

Note: I am settig the x and y-axis boundaries by allowing the plotter
to pick its own axis the first time, writing down the ranges I observe,
and then setting them to the largest of the pair. It is therefore
possible that I missed one or more genes which lies outside that
range.
The previous plotted contrasts sought to show changes between the two
strains z2.3 and z2.2. Conversely, the previous volcano plots seek to
directly compare each strain before/after drug treatment.
Evaluating a log2FC
barplot
Figure 2E is now comprised of a plot which shows log2FC values with
error bars for selected genes and seeks to show differences between
2.3/uninfected and 2.2/uninfected.
Here is the table Olga used to generate it:
I went looking in the xlsx files produced in 202405 and found that
these are the log2FC values and standard errors produced by DESeq2.
It should be noted that in my most recent version of these analyses,
these numbers did shift slightly. I am looking into that now.
** 2.3 vs Uninfected MØ 2.2 vs Uninfected MØ
| Gene | Mean | SEM
| n | Mean | SEM |n |
|IFI27 | 7.224 | 0.5662 |6 | 2.702 | 0.5669 | 6| |RSAD2 | 6.29 |
0.7312 |6 | 1.623 | 0.7303 | 6| |CCL8 | 6.225 | 0.928 |6 | -0.314| 0.941
| 6| |IFI44L| 5.895 | 0.612 |6 | 2.06 | 0.611 | 6| |OASL | 4.726 |
0.4974 |6 | 1.392 | 0.4973 | 6| |USP18 | 3.644 | 0.483 |6 | 0.999 |
0.4826 | 6| |IDO1 | 7.145 | 1.107 |6 | 1.257 | 1.141 | 6| |IDO2 | 3.935
| 1.3 |6 | 2.557 | 1.341 | 6| |KYNU | 1.07 | 0.2186 |6 | 0.0207| 0.2184
| 6| |AHR | 0.9382 | 0.2236 |6 | 0.5032| 0.2239 | 6| |IL4I1 | 2.593 |
0.4623 |6 | 0.039 | 0.4618 | 6| |SOD2 | 2.76 | 0.349 |6 | 0.4241| 0.3528
| 6| |NOTCH1| 0.7572| 0.275 |6 | 1.495 | 0.2744 | 6| |DLL1 | 0.8268|
0.5285 |6 | 3.455 | 0.5228 | 6| |DLL4 | 1.116 | 0.737 |6 | 4.243 | 0.71
| 6| |HES1 | -0.0183| 0.8599 |6 | 6.536 | 0.7973 | 6| |HEY1 | 0.5533|
0.5789 |6 | 4.181 | 0.6273 | 6|
Ok, I think I found a problem: The NOTCH1 value is actually the
adjusted p-value.
- Transporters without drug
** 2.3 vs Uninfected MØ 2.2 vs Uninfected MØ
| Gene | Mean | SEM
| n| Mean | SEM | n|
|ABCB1 | -2.354 | 0.442 | 6| -0.406| 0.431| 6| |ABCG4 | -3.715 |
0.648 | 6| -0.653| 0.630| 6| |ABCB5 | -1.192 | 0.380 | 6| 1.351 | 0.363|
6| |ABCA9 | 1.880 | 0.648 | 6| 3.444 | 0.637| 6| |ABCC2 | 0.454 | 0.321
| 6| 1.818 | 0.314| 6| |AQP2 | -1.191 | 0.529 | 6| 0.745 | 0.514| 6|
|AQP3 | -0.940 | 0.402 | 6| 0.431 | 0.395| 6|
** 2.3 vs Uninfected MØ 2.2 vs Uninfected MØ
|Gene | Mean | SEM |
n| Mean | SEM | n |
|ABCB1 | -0.697| 0.349 | 6| -1.255| 0.337 | 6| |ABCG4 | 1.231 | 0.503
| 6| 0.547 | 0.484 | 6| |AQP2 | 0.816 | 0.399 | 6| 0.043 | 0.387 | 6|
|AQP3 | -1.286| 0.320 | 6| -1.613| 0.309 | 6| |AQP8 | 0.634 | 0.370 | 6|
0.943 | 0.365 | 6|
Let us now see if I can recapitulate the plot…
nodrug_contrasts <- c("z23nosb_vs_uninf", "z22nosb_vs_uninf")
genes_no_drug <- c("IFI27", "RSAD2", "CCL8", "IFI44L", "OASL", "USP18", "IDO1", "IDO2", "KYNU", "AHR", "IL4I1", "SOD2", "NOTCH1", "DLL1", "DLL4", "HES1", "HEY1")
transporters_no_drug <- c("ABCB1", "ABCG4", "ABCB5", "ABCA9", "ABCC2", "AQP2", "AQP3")
drug_contrasts <- c("z23sb_vs_sb", "z22sb_vs_sb")
transporters_drug <- c("ABCB1", "ABCG4", "AQP2", "AQP3", "AQP8")
These values came out of the data structure called
‘hs_macr_table’
z23nosb_uninf_values <- hs_macr_table[["data"]][["z23nosb_vs_uninf"]]
gene_idx <- z23nosb_uninf_values[["hgnc_symbol"]] %in% genes_no_drug
nodrug_rows <- z23nosb_uninf_values[gene_idx, ]
rownames(nodrug_rows) <- nodrug_rows[["hgnc_symbol"]]
z23_nodrug_values <- nodrug_rows[, c("deseq_logfc", "deseq_lfcse")]
z23_nodrug_values
## DataFrame with 17 rows and 2 columns
## deseq_logfc deseq_lfcse
## <numeric> <numeric>
## IL4I1 2.59300 0.4623
## AHR 0.93810 0.2236
## CCL8 6.22500 0.9280
## SOD2 2.76000 0.3490
## HES1 -0.01786 0.8599
## ... ... ...
## HEY1 0.5531 0.6520
## IFI27 7.2240 0.5662
## USP18 3.6440 0.4830
## IDO2 3.9340 1.2990
## DLL1 0.8268 0.5284
z22nosb_uninf_values <- hs_macr_table[["data"]][["z22nosb_vs_uninf"]]
gene_idx <- z22nosb_uninf_values[["hgnc_symbol"]] %in% genes_no_drug
nodrug_rows <- z22nosb_uninf_values[gene_idx, ]
rownames(nodrug_rows) <- nodrug_rows[["hgnc_symbol"]]
z22_nodrug_values <- nodrug_rows[, c("deseq_logfc", "deseq_lfcse")]
z22_nodrug_values
## DataFrame with 17 rows and 2 columns
## deseq_logfc deseq_lfcse
## <numeric> <numeric>
## IL4I1 0.03995 0.4618
## AHR 0.50310 0.2239
## CCL8 -0.31360 0.9406
## SOD2 0.42410 0.3528
## HES1 6.53600 0.7973
## ... ... ...
## HEY1 4.181 0.6273
## IFI27 2.702 0.5669
## USP18 0.999 0.4826
## IDO2 2.557 1.3410
## DLL1 3.455 0.5228
z23_nodrug_values[["state"]] <- "z23_vs_uninfected"
z22_nodrug_values[["state"]] <- "z22_vs_uninfected"
plot_df <- rbind.data.frame(as.data.frame(z23_nodrug_values), as.data.frame(z22_nodrug_values))
plot_df[["gene"]] <- rownames(plot_df)
## I just realized that this is actually just a comparison of z23/z22
## we should just take the adjusted p-values from that contrast for this.
z23_z22_comparison <- hs_macr_table[["data"]][["z23nosb_vs_z22nosb"]]
nodrug_rows <- z23_z22_comparison[gene_idx, ]
nodrug_pvalues <- nodrug_rows[, c("deseq_p", "deseq_adjp")]
rownames(nodrug_pvalues) <- nodrug_rows[["hgnc_symbol"]]
nodrug_pvalues
## DataFrame with 17 rows and 2 columns
## deseq_p deseq_adjp
## <numeric> <numeric>
## IL4I1 1.250e-13 3.949e-12
## AHR 8.308e-03 2.421e-02
## CCL8 3.677e-21 4.197e-19
## SOD2 6.181e-20 5.813e-18
## HES1 9.422e-38 2.215e-34
## ... ... ...
## HEY1 9.854e-17 5.410e-15
## IFI27 6.486e-28 2.310e-25
## USP18 1.772e-13 5.467e-12
## IDO2 1.047e-01 1.895e-01
## DLL1 4.352e-12 1.103e-10
ggplot(plot_df, aes(x = gene, y = deseq_logfc, fill = state)) +
geom_bar(position = position_dodge(), stat = "identity") +
geom_errorbar(aes(ymin = deseq_logfc - deseq_lfcse,
ymax = deseq_logfc + deseq_lfcse),
width = 0.2, position = position_dodge(0.9)) +
scale_fill_manual(values = c("#1B9E77", "#7570B3")) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))

comparison <- c("z23_vs_uninfected", "z22_vs_uninfected")
comparisons <- rep(list(comparison), nrow(plot_df) / 2)
ggplot(plot_df, aes(x = gene, y = deseq_logfc, fill = state, add = deseq_lfcse, facet.by = "state")) +
geom_bar(position = position_dodge(), stat = "identity") +
geom_errorbar(aes(ymin = deseq_logfc - deseq_lfcse,
ymax = deseq_logfc + deseq_lfcse),
width = 0.2, position = position_dodge(0.9)) +
stat_compare_means() +
stat_compare_means(comparisons = comparisons, label.y = rownames(z23_nodrug_values)) +
scale_fill_manual(values = c("#1B9E77", "#7570B3")) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))
## Error in stat_compare_means(): could not find function "stat_compare_means"
Excellent, the values now match up. Now I ust need to figure out why
the stupid hgnc IDs got lost… I can see them in the hs_annot data
structure, so I must have messed up when I regenered the input to the
de. Ok, I got to the same starting point now with identical values. As
soon as I did that, I looked at the resulting plot and realized that we
are actually just comparing z23 / z22.
Here is why: the plot as it stands is a comparison of the log2FC
values of the following two contrasts: z23/uninfected and
z22/uninfected; stated differently, this is (z23/uninf)/(z22/uninf)
which of course cancels out to just z23/z22.
Therefore it is much more parsimonious to just use the values from
z23/z22. I swear I have gone through this exact exercise on so so many
occasions in the past it is terrible.
wanted_genes <- c("IFI27", "RSAD2", "CCL8", "IFI44L", "OASL",
"USP18", "IDO1", "IDO2", "KYNU", "AHR", "IL4I1",
"SOD2", "NOTCH1", "DLL1", "DLL4", "HES1", "HEY1")
ggsignif_plot <- ggsignif_paired_genes(
hs_macr, conditions = c("inf_z23", "inf_z22"), genes = wanted_genes)
## Running normalize_se.
## Warning in normalize_se(exp, ...): Quantile normalization and sva do not always
## play well together.
## Removing 9725 low-count genes (11756 remaining).
## transform_counts: Found 2226 values less than 0.
## Warning in transform_counts(count_table, method = transform, ...): NaNs
## produced
## Setting 34233 entries to zero.
## Error in arrange(., factor(!!sym(name_column), levels = genes)): could not find function "arrange"
## Error: object 'ggsignif_plot' not found
pander::pander(sessionInfo())
## Warning: Your system is mis-configured: '/etc/localtime' is not a symlink
## Warning: It is strongly recommended to set envionment variable TZ to
## 'America/New_York' (or equivalent)
R version 4.5.0 (2025-04-11)
Platform: x86_64-pc-linux-gnu
locale: C
attached base packages: stats4,
stats, graphics, grDevices, utils,
datasets, methods and base
other attached packages:
rWikiPathways(v.1.28.0), pathwayPCA(v.1.24.0),
SparseM(v.1.84-2), topGO(v.2.60.1),
GSVAdata(v.1.44.0), org.Hs.eg.db(v.3.21.0),
AnnotationDbi(v.1.70.0), IRanges(v.2.42.0),
S4Vectors(v.0.46.0), Biobase(v.2.68.0),
BiocGenerics(v.0.54.0), generics(v.0.1.4),
ReactomePA(v.1.52.0), edgeR(v.4.6.3),
ruv(v.0.9.7.1), ggstatsplot(v.0.13.1),
enrichplot(v.1.28.4), tidyr(v.1.3.1),
tibble(v.3.3.0), UpSetR(v.1.4.0),
hpgltools(v.1.2), Heatplus(v.3.16.0),
glue(v.1.8.0), ggplot2(v.3.5.2) and
ggbreak(v.0.1.6)
loaded via a namespace (and not attached):
R.methodsS3(v.1.8.2), dichromat(v.2.0-0.1),
GSEABase(v.1.70.0), progress(v.1.2.3),
Biostrings(v.2.76.0), vctrs(v.0.6.5),
ggtangle(v.0.0.7), shape(v.1.4.6.1),
effectsize(v.1.0.1), digest(v.0.6.37),
png(v.0.1-8), corpcor(v.1.6.10),
DEGreport(v.1.44.0), ggrepel(v.0.9.6),
bayestestR(v.0.17.0), correlation(v.0.8.8),
magick(v.2.8.7), MASS(v.7.3-65),
reshape(v.0.8.10), reshape2(v.1.4.4),
httpuv(v.1.6.16), foreach(v.1.5.2),
qvalue(v.2.40.0), withr(v.3.0.2),
psych(v.2.5.6), xfun(v.0.53), ggfun(v.0.2.0),
survival(v.3.8-3), memoise(v.2.0.1),
clusterProfiler(v.4.16.0), gson(v.0.1.0),
parameters(v.0.28.1), GlobalOptions(v.0.1.2),
tidytree(v.0.4.6), gtools(v.3.9.5),
logging(v.0.10-108), R.oo(v.1.27.1),
DEoptimR(v.1.1-4), prettyunits(v.1.2.0),
datawizard(v.1.2.0), rematch2(v.2.1.2),
KEGGREST(v.1.48.1), promises(v.1.3.3),
httr(v.1.4.7), meshes(v.1.34.0),
UCSC.utils(v.1.4.0), DOSE(v.4.2.0),
reactome.db(v.1.92.0), curl(v.7.0.0),
ggraph(v.2.2.2), polyclip(v.1.10-7),
GenomeInfoDbData(v.1.2.14), SparseArray(v.1.8.1),
RBGL(v.1.84.0), RcppEigen(v.0.3.4.0.2),
doParallel(v.1.0.17), xtable(v.1.8-4),
stringr(v.1.5.1), desc(v.1.4.3),
evaluate(v.1.0.4), S4Arrays(v.1.8.1),
BiocFileCache(v.2.16.1), preprocessCore(v.1.70.0),
hms(v.1.1.3), GenomicRanges(v.1.60.0),
colorspace(v.2.1-1), filelock(v.1.0.3),
magrittr(v.2.0.3), later(v.1.4.3),
viridis(v.0.6.5), ggtree(v.3.17.1.001),
lattice(v.0.22-7), genefilter(v.1.90.0),
robustbase(v.0.99-4-1), XML(v.3.99-0.19),
cowplot(v.1.2.0), matrixStats(v.1.5.0),
ggupset(v.0.4.1), pillar(v.1.11.0),
nlme(v.3.1-168), iterators(v.1.0.14),
caTools(v.1.18.3), compiler(v.4.5.0),
stringi(v.1.8.7), minqa(v.1.2.8),
SummarizedExperiment(v.1.38.1), plyr(v.1.8.9),
crayon(v.1.5.3), abind(v.1.4-8),
ggdendro(v.0.2.0), gridGraphics(v.0.5-1),
locfit(v.1.5-9.12), graphlayouts(v.1.2.2),
bit(v.4.6.0), dplyr(v.1.1.4),
fastmatch(v.1.1-6), codetools(v.0.2-20),
crosstalk(v.1.2.1), bslib(v.0.9.0),
paletteer(v.1.6.0), GetoptLong(v.1.0.5),
plotly(v.4.11.0), remaCor(v.0.0.20),
mime(v.0.13), splines(v.4.5.0),
circlize(v.0.4.16), Rcpp(v.1.1.0),
dbplyr(v.2.5.0), lars(v.1.3), knitr(v.1.50),
blob(v.1.2.4), clue(v.0.3-66),
BiocVersion(v.3.21.1), lme4(v.1.1-37),
fs(v.1.6.6), Rdpack(v.2.6.4), EBSeq(v.2.6.0),
openxlsx(v.4.2.8), ggplotify(v.0.1.2),
Matrix(v.1.7-3), statmod(v.1.5.0),
fANCOVA(v.0.6-1), tweenr(v.2.0.3),
pkgconfig(v.2.0.3), tools(v.4.5.0),
cachem(v.1.1.0), RhpcBLASctl(v.0.23-42),
rbibutils(v.2.3), RSQLite(v.2.4.3),
viridisLite(v.0.4.2), DBI(v.1.2.3),
numDeriv(v.2016.8-1.1), graphite(v.1.54.0),
fastmap(v.1.2.0), rmarkdown(v.2.29),
scales(v.1.4.0), grid(v.4.5.0),
gprofiler2(v.0.2.3), broom(v.1.0.9),
AnnotationHub(v.3.16.1), sass(v.0.4.10),
patchwork(v.1.3.2), BiocManager(v.1.30.26),
insight(v.1.4.1), graph(v.1.86.0),
varhandle(v.2.0.6), farver(v.2.1.2),
reformulas(v.0.4.1), aod(v.1.3.3),
tidygraph(v.1.3.1), mgcv(v.1.9-3),
yaml(v.2.3.10), MatrixGenerics(v.1.20.0),
cli(v.3.6.5), purrr(v.1.1.0),
lifecycle(v.1.0.4), mvtnorm(v.1.3-3),
backports(v.1.5.0), Vennerable(v.3.1.0.9000),
BiocParallel(v.1.42.1), annotate(v.1.86.1),
MeSHDbi(v.1.44.0), rjson(v.0.2.23),
gtable(v.0.3.6), parallel(v.4.5.0),
ape(v.5.8-1), testthat(v.3.2.3),
limma(v.3.64.3), jsonlite(v.2.0.0),
bitops(v.1.0-9), NOISeq(v.2.52.0),
bit64(v.4.6.0-1), brio(v.1.1.5),
yulab.utils(v.0.2.1), zip(v.2.3.3),
RcppParallel(v.5.1.11-1), jquerylib(v.0.1.4),
GOSemSim(v.2.34.0), zeallot(v.0.2.0),
R.utils(v.2.13.0), pbkrtest(v.0.5.5),
lazyeval(v.0.2.2), pander(v.0.6.6),
ConsensusClusterPlus(v.1.72.0), shiny(v.1.11.1),
htmltools(v.0.5.8.1), GO.db(v.3.21.0),
rappdirs(v.0.3.3), blockmodeling(v.1.1.8),
tinytex(v.0.57), httr2(v.1.2.1),
XVector(v.0.48.0), RCurl(v.1.98-1.17),
rprojroot(v.2.1.0), treeio(v.1.32.0),
mnormt(v.2.1.1), gridExtra(v.2.3),
ggsankey(v.0.0.99999), EnvStats(v.3.1.0),
boot(v.1.3-31), igraph(v.2.1.4),
variancePartition(v.1.38.1), R6(v.2.6.1),
sva(v.3.56.0), DESeq2(v.1.48.1),
gplots(v.3.2.0), labeling(v.0.4.3),
cluster(v.2.1.8.1), pkgload(v.1.4.0),
aplot(v.0.2.8), GenomeInfoDb(v.1.44.2),
nloptr(v.2.2.1), rstantools(v.2.5.0),
DelayedArray(v.0.34.1), tidyselect(v.1.2.1),
xml2(v.1.4.0), ggforce(v.0.5.0),
statsExpressions(v.1.7.1), KernSmooth(v.2.23-26),
data.table(v.1.17.8), ComplexHeatmap(v.2.24.1),
htmlwidgets(v.1.6.4), fgsea(v.1.34.2),
RColorBrewer(v.1.1-3), biomaRt(v.2.64.0),
rlang(v.1.1.6), lmerTest(v.3.1-3) and
ggnewscale(v.0.5.2)
message("This is hpgltools commit: ", get_git_commit())
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 6c447f8e7b2eeb8617fc6658c6123b8281032620
## This is hpgltools commit: Mon Sep 1 13:25:21 2025 -0400: 6c447f8e7b2eeb8617fc6658c6123b8281032620
tmp <- saveme(filename = savefile)
## The savefile is: /lab/singularity/tmrc2_macrophage_deb/202509011430_outputs/savefiles/03differential_expression.rda.xz
## The file does not yet exist.
## The save string is: con <- pipe(paste0('pxz > /lab/singularity/tmrc2_macrophage_deb/202509011430_outputs/savefiles/03differential_expression.rda.xz'), 'wb'); save(list = ls(all.names = TRUE, envir = globalenv()),
## envir = globalenv(), file = con, compress = FALSE); close(con)
## Error in save(list = ls(all.names = TRUE, envir = globalenv()), envir = globalenv(), : ignoring SIGPIPE signal
tmp <- loadme(filename = savefile)
devtools::load_all(‘~/hpgltools’)
LS0tCnRpdGxlOiAiVE1SQzIgYHIgU3lzLmdldGVudignVkVSU0lPTicpYDogTWFjcm9waGFnZSBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbi4iCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSAubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTYwMHB4Owp9CmJvZHksIHRkIHsKICBmb250LXNpemU6IDE2cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTZweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoZ2dicmVhaykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdsdWUpCmxpYnJhcnkoSGVhdHBsdXMpCmxpYnJhcnkoaHBnbHRvb2xzKQpsaWJyYXJ5KFVwU2V0UikKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZW5yaWNocGxvdCkKbGlicmFyeShnZ3N0YXRzcGxvdCkKCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHByb2dyZXNzID0gVFJVRSwgdmVyYm9zZSA9IFRSVUUsIHdpZHRoID0gOTAsIGVjaG8gPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZXJyb3IgPSBUUlVFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOCwgZmlnLnJldGluYSA9IDIsCiAgb3V0LndpZHRoID0gIjEwMCUiLCBkZXYgPSAicG5nIiwKICBkZXYuYXJncyA9IGxpc3QocG5nID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKSkKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxMikpCnZlciA8LSBTeXMuZ2V0ZW52KCJWRVJTSU9OIikKcHJldmlvdXNfZmlsZSA8LSAiIgpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQgPSAiJVklbSVkIikKCiMjIHRtcCA8LSB0cnkoc20obG9hZG1lKGZpbGVuYW1lID0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcHJldmlvdXNfZmlsZSkpKSkKcm1kX2ZpbGUgPC0gIjAzZGlmZmVyZW50aWFsX2V4cHJlc3Npb24uUm1kIgpsb2FkZWQgPC0gbG9hZChmaWxlID0gZ2x1ZSgicmRhL3RtcmMyX2RhdGFfc3RydWN0dXJlcy12e3Zlcn0ucmRhIikpCnNhdmVmaWxlIDwtIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLCByZXBsYWNlID0gIlxcLnJkYVxcLnh6IiwgeCA9IHJtZF9maWxlKQpjcmVhdGVkIDwtIGRpci5jcmVhdGUoImFuYWx5c2VzIikKYGBgCgojIENoYW5nZWxvZwoKKiAyMDI0MDEtMjAyNDA1OiBDbGVhbnVwcywgZm9ybWF0dGluZywgZW5zdXJpbmcgdGhhdCBldmVyeXRoaW5nIHdvcmtzIGluIHRoZSBjb250YWluZXIuCiogMjAyMzEwOiBDbGVhbmluZyB1cCB0byBtYWtlIGV2ZXJ5dGhpbmcgcGFzcyB3aXRoaW4gYSBjb250YWluZXJpemVkIGVudmlyb25tZW50LgoqIDIwMjMxMDogUmVjZWl2ZWQgYSBzZXQgb2YgY29sb3JzIGFuZCBjb250cmFzdHMgb2YgaW50ZXJlc3QgZm9yIGEgYmFycGxvdCBvZiBzaWduaWZpY2FuY2UuCiogMjAyMzA0MTA6IE1ha2luZyBzb21lIGNoYW5nZXMgdG8gaW1wcm92ZSB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24KICBwbG90cyBhcyB3ZWxsIGFzIHByZXBhcmUgZm9yIHNvbWUgZGlmZmVyZW50IHBhdGh3YXkvR1NFQS9HU1ZBCiAgYW5hbHlzZXMgb24gdGhlIGRhdGEuCgojIEludHJvZHVjdGlvbgoKSGF2aW5nIGVzdGFibGlzaGVkIHRoYXQgdGhlIFRNUkMyIG1hY3JvcGhhZ2UgZGF0YSBsb29rcyByb2J1c3QgYW5kCmlsbHVzdHJhdGl2ZSBvZiBhIGNvdXBsZSBvZiBpbnRlcmVzdGluZyBxdWVzdGlvbnMsIGxldCB1cyBwZXJmb3JtIGEKY291cGxlIG9mIGRpZmZlcmVudGlhbCBhbmFseXNlcyBvZiBpdC4KCkFsc28gbm90ZSB0aGF0IGFzIG9mIDIwMjIxMiwgd2UgcmVjZWl2ZWQgYSBuZXcgc2V0IG9mIHNhbXBsZXMgd2hpY2gKbm93IGluY2x1ZGUgc29tZSB3aGljaCBhcmUgYSBjb21wbGV0ZWx5IGRpZmZlcmVudCBjZWxsIHR5cGUsClU5MzcuICBBcyB0aGVpciBBVENDIHBhZ2Ugc3RhdGVzLCB0aGV5IGFyZSBtYWxpZ25hbnQgY2VsbHMgdGFrZW4gZnJvbQp0aGUgcGxldXJhbCBlZmZ1c2lvbiBvZiBhIDM3IHllYXIgb2xkIHdoaXRlIG1hbGUgd2l0aCBoaXN0aW9jeXRpYwpseW1waG9tYSBhbmQgd2hpY2ggZXhoaWJpdCB0aGUgbW9ycGhvbG9neSBvZiBtb25vY3l0ZXMuICBUaHVzLCB0aGlzCmRvY3VtZW50IG5vdyBpbmNsdWRlcyBzb21lIGNvbXBhcmlzb25zIG9mIHRoZSBjZWxsIHR5cGVzIGFzIHdlbGwgYXMKdGhlIHZhcmlvdXMgbWFjcm9waGFnZSBkb25vcnMgKGdpdmVuIHRoYXQgdGhlcmUgYXJlIG5vdyBtb3JlIGRvbm9ycwp0b28pLgoKIyMgSHVtYW4gZGF0YQoKSSBhbSBtb3ZpbmcgdGhlIGRhdGFzZXQgbWFuaXB1bGF0aW9ucyBoZXJlIHNvIHRoYXQgSSBjYW4gbG9vayBhdCB0aGVtCmFsbCB0b2dldGhlciBiZWZvcmUgcnVubmluZyB0aGUgdmFyaW91cyBERSBhbmFseXNlcy4KCiMjIENyZWF0ZSBzZXRzIGZvY3VzZWQgb24gZHJ1ZywgY2VsbHR5cGUsIHN0cmFpbiwgYW5kIGNvbWJpbmF0aW9ucwoKTGV0IHVzIHN0YXJ0IGJ5IHBsYXlpbmcgd2l0aCB0aGUgbWV0YWRhdGEgYSBsaXR0bGUgYW5kIGNyZWF0ZSBzZXRzCndpdGggdGhlIGNvbmRpdGlvbiBzZXQgdG86CgoqIERydWcgdHJlYXRtZW50CiogQ2VsbCB0eXBlIChtYWNyb3BoYWdlIG9yIFU5MzcpCiogRG9ub3IKKiBJbmZlY3Rpb24gU3RyYWluCiogU29tZSB1c2VmdWwgY29tYmluYXRpb25zIHRoZXJlb2YKCkluIGFkZGl0aW9uLCBrZWVwIG1lbnRhbCB0cmFjayBvZiB3aGljaCBkYXRhc2V0cyBhcmUgY29tcHJpc2VkIG9mIGFsbApzYW1wbGVzIHZzLiB0aG9zZSB3aGljaCBhcmUgb25seSBtYWNyb3BoYWdlIHZzLiB0aG9zZSB3aGljaCBhcmUgb25seQpVOTM3LiAgKFRodXMsIHRoZSB1c2FnZSBvZiBhbGxfaHVtYW4gdnMuIGhzX21hY3IgdnMuIHU5MzcgYXMgcHJlZml4ZXMKZm9yIHRoZSBkYXRhIHN0cnVjdHVyZXMuKQoKSWRlYWxseSwgdGhlc2UgcmVjcmVhdGlvbnMgb2YgdGhlIGRhdGEgc2hvdWxkIHBlcmhhcHMgYmUgaW4gdGhlCmRhdGFzdHJ1Y3R1cmVzIHdvcmtzaGVldC4KCmBgYHtyfQphbGxfaHVtYW4gPC0gc2FuaXRpemVfbWV0YWRhdGEoaHNfbWFjcm9waGFnZSwgY29sdW1ucyA9ICJkcnVnIikgJT4lCiAgc2V0X2NvbmRpdGlvbnMoZmFjdCA9ICJkcnVnIiwgY29sb3JzID0gY29sb3JfY2hvaWNlc1tbImRydWciXV0pICU+JQogIHNldF9iYXRjaGVzKGZhY3QgPSAidHlwZW9mY2VsbHMiKQoKIyMgVGhlIGZvbGxvd2luZyAzIGxpbmVzIHdlcmUgY29weS9wYXN0ZWQgdG8gZGF0YXN0cnVjdHVyZXMgYW5kIHNob3VsZCBiZSByZW1vdmVkIHNvb24uCm5vX3N0cmFpbl9pZHggPC0gY29sRGF0YShhbGxfaHVtYW4pW1sic3RyYWluaWQiXV0gPT0gIm5vbmUiCiMjcERhdGEoYWxsX2h1bWFuKVtbInN0cmFpbmlkIl1dIDwtIHBhc3RlMCgicyIsIHBEYXRhKGFsbF9odW1hbilbWyJzdHJhaW5pZCJdXSwKIyMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJfIiwgcERhdGEoYWxsX2h1bWFuKVtbIm1hY3JvcGhhZ2V6eW1vZGVtZSJdXSkKY29sRGF0YShhbGxfaHVtYW4pW25vX3N0cmFpbl9pZHgsICJzdHJhaW5pZCJdIDwtICJub25lIgp0YWJsZShjb2xEYXRhKGFsbF9odW1hbilbWyJzdHJhaW5pZCJdXSkKCmFsbF9odW1hbl90eXBlcyA8LSBzZXRfY29uZGl0aW9ucyhhbGxfaHVtYW4sIGZhY3QgPSAidHlwZW9mY2VsbHMiKSAlPiUKICBzZXRfYmF0Y2hlcyhmYWN0ID0gImRydWciKQoKdHlwZV96eW1vX2ZhY3QgPC0gcGFzdGUwKGNvbERhdGEoYWxsX2h1bWFuX3R5cGVzKVtbImNvbmRpdGlvbiJdXSwgIl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YShhbGxfaHVtYW5fdHlwZXMpW1sibWFjcm9waGFnZXp5bW9kZW1lIl1dKQp0eXBlX3p5bW8gPC0gc2V0X2NvbmRpdGlvbnMoYWxsX2h1bWFuX3R5cGVzLCBmYWN0ID0gdHlwZV96eW1vX2ZhY3QpCgp0eXBlX2RydWdfZmFjdCA8LSBwYXN0ZTAoY29sRGF0YShhbGxfaHVtYW5fdHlwZXMpW1siY29uZGl0aW9uIl1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhKGFsbF9odW1hbl90eXBlcylbWyJkcnVnIl1dKQp0eXBlX2RydWcgPC0gc2V0X2NvbmRpdGlvbnMoYWxsX2h1bWFuX3R5cGVzLCBmYWN0ID0gdHlwZV9kcnVnX2ZhY3QpCgpzdHJhaW5fZmFjdCA8LSBjb2xEYXRhKGFsbF9odW1hbl90eXBlcylbWyJzdHJhaW5pZCJdXQp0YWJsZShzdHJhaW5fZmFjdCkKCm5ld19jb25kaXRpb25zIDwtIHBhc3RlMChjb2xEYXRhKGhzX21hY3JvcGhhZ2UpW1sibWFjcm9waGFnZXRyZWF0bWVudCJdXSwgIl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YShoc19tYWNyb3BoYWdlKVtbIm1hY3JvcGhhZ2V6eW1vZGVtZSJdXSkKIyMgTm90ZSB0aGUgc2FuaXRpemUoKSBjYWxsIGlzIHJlZHVuZGFudCB3aXRoIHRoZSBhZGRpdGlvbiBvZiBzYW5pdGl6ZSgpIGluIHRoZQojIyBkYXRhc3RydWN0dXJlcyBmaWxlLCBidXQgSSBkb24ndCB3YW50IHRvIHdhaXQgdG8gcmVydW4gdGhhdC4KaHNfbWFjciA8LSBzZXRfY29uZGl0aW9ucyhoc19tYWNyb3BoYWdlLCBmYWN0ID0gbmV3X2NvbmRpdGlvbnMpICU+JQogIHNhbml0aXplX21ldGFkYXRhKGNvbHVtbiA9ICJkcnVnIikgJT4lCiAgc3Vic2V0X3NlKHN1YnNldCA9ICJ0eXBlb2ZjZWxscyE9J1U5MzcnIikgJT4lCiAgc2V0X3NlX2NvbG9ycyhjb2xvcl9jaG9pY2VzW1sidHJlYXRtZW50X3p5bW8iXV0pCmBgYAoKIyMjIFNlcGFyYXRlIE1hY3JvcGhhZ2Ugc2FtcGxlcwoKT25jZSBhZ2Fpbiwgd2Ugc2hvdWxkIHJlY29uc2lkZXIgd2hlcmUgdGhlIGZvbGxvd2luZyBibG9jayBpcyBwbGFjZWQsCmJ1dCB0aGVzZSBkYXRhc3RydWN0dXJlcyBhcmUgbGlrZWx5IHRvIGJlIHVzZWQgaW4gbWFueSBvZiB0aGUKZm9sbG93aW5nIGFuYWx5c2VzLgoKYGBge3J9CmhzX21hY3JfZHJ1Z19leHB0IDwtIHNldF9jb25kaXRpb25zKGhzX21hY3IsIGZhY3QgPSAiZHJ1ZyIsIGNvbG9ycyA9IGNvbG9yX2Nob2ljZXNbWyJkcnVnIl1dKQoKaHNfbWFjcl9zdHJhaW5fZXhwdCA8LSBzZXRfY29uZGl0aW9ucyhoc19tYWNyLCBmYWN0ID0gIm1hY3JvcGhhZ2V6eW1vZGVtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfY2hvaWNlc1tbInp5bW8iXV0pICU+JQogIHN1YnNldF9zZShzdWJzZXQgPSAibWFjcm9waGFnZXp5bW9kZW1lICE9ICdub25lJyIpCgp0YWJsZShjb2xEYXRhKGhzX21hY3IpW1sic3RyYWluaWQiXV0pCmBgYAoKIyMjIFJlZmFjdG9yIFU5Mzcgc2FtcGxlcwoKVGhlIFU5Mzcgc2FtcGxlcyB3ZXJlIHNlcGFyYXRlZCBpbiB0aGUgZGF0YXN0cnVjdHVyZXMgZmlsZSwgYnV0IHdlCndhbnQgdG8gdXNlIHRoZSBjb21iaW5hdGlvbiBvZiBkcnVnL3p5bW9kZW1lIHdpdGggdGhlbSBwcmV0dHkgbXVjaApleGNsdXNpdmVseS4KCmBgYHtyfQpuZXdfY29uZGl0aW9ucyA8LSBwYXN0ZTAoY29sRGF0YShoc191OTM3KVtbIm1hY3JvcGhhZ2V0cmVhdG1lbnQiXV0sICJfIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEoaHNfdTkzNylbWyJtYWNyb3BoYWdlenltb2RlbWUiXV0pCnU5MzdfZXhwdCA8LSBzZXRfY29uZGl0aW9ucyhoc191OTM3LCBmYWN0ID0gbmV3X2NvbmRpdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSBjb2xvcl9jaG9pY2VzW1sidHJlYXRtZW50X3p5bW8iXV0pCmBgYAoKIyMgQ29udHJhc3RzIHVzZWQgaW4gdGhpcyBkb2N1bWVudAoKR2l2ZW4gdGhlIHZhcmlvdXMgd2F5cyB3ZSBoYXZlIGNob3BwZWQgdXAgdGhpcyBkYXRhc2V0LCB0aGVyZSBhcmUgYQpmZXcgZ2VuZXJhbCB0eXBlcyBvZiBjb250cmFzdHMgd2Ugd2lsbCBwZXJmb3JtLCB3aGljaCB3aWxsIHRoZW4gYmUKY29tYmluZWQgaW50byBncmVhdGVyIGNvbXBsZXhpdHk6CgoqIGRydWcgdHJlYXRtZW50OiBBbnRpbW9uYWwgdHJlYXRlZCBvciBub3QuCiogc3RyYWlucyB1c2VkOiBVbmluZmVjdGVkLCB6Mi4zLCBhbmQgejIuMi4KKiBjZWxsbHR5cGVzOiBVOTM3IG9yIG1hY3JvcGhhZ2UuCiogZG9ub3JzOiBUaGUgcGVyc29uIGZyb20gd2hvbSB0aGUgbWFjcm9waGFnZXMgd2VyZSB0YWtlbi4KCkluIHRoZSBlbmQsIG91ciBhY3R1YWwgZ29hbCBpcyB0byBjb25zaWRlciB0aGUgdmFyaWFibGUgZWZmZWN0cyBvZgpkcnVnK3N0cmFpbiBhbmQgc2VlIGlmIHdlIGNhbiBkaXNjZXJuIHBhdHRlcm5zIHdoaWNoIGxlYWQgdG8gYmV0dGVyIG9yCndvcnNlIGRydWcgdHJlYXRtZW50IG91dGNvbWUuCgpUaGVyZSBpcyBhIHNldCBvZiBjb250cmFzdHMgaW4gd2hpY2ggd2UgYXJlIHByaW1hcmlseSBpbnRlcmVzdGVkIGluCnRoaXMgZGF0YSwgdGhlc2UgZm9sbG93LiAgSSBjcmVhdGVkIG9uZSByYXRpbyBvZiByYXRpb3MgY29udHJhc3Qgd2hpY2gKSSB0aGluayBoYXMgdGhlIHBvdGVudGlhbCB0byBhc2sgb3VyIGJpZ2dlc3QgcXVlc3Rpb24uCgpgYGB7cn0KIyMgRWFjaCBvZiB0aGUgZm9sbG93aW5nIGxpc3RzIGhhcyB0aGUgbmFtZSBvZiB0aGUgY29udHJhc3QgYXMgdGhlIGtleQojIyBmb2xsb3dlZCBieSBhIHR3byBlbGVtZW50IHZlY3RvciBjb21wcmlzZWQgb2YgdGhlIG51bWVyYXRvciBhbmQKIyMgZGVub21pbmF0b3IgYXMgdGhlIHZhbHVlLiAgSW4gdGhlIGNhc2Ugb2YgdGhpcyBmaXJzdCBjb250cmFzdCwgdGhhdAojIyBpcyBjb21wcmlzZWQgb2YgYSBzdHJpbmcgd2hpY2ggbWFudWFsbHkgZGVmaW5lcyBhIHNlcmllcyBvZiBtb3JlCiMjIGNvbXBsZXggY29udHJhc3RzIHRoYW4gdGhlIHVzdWFsL3NpbXBsZSBwYWlyd2lzZS4KdG1yYzJfaHVtYW5fZXh0cmEgPC0gInoyM2RydWdub2RydWdfdnNfejIyZHJ1Z25vZHJ1ZyA9IChjb25kaXRpb25pbmZfc2JfejIzIC0gY29uZGl0aW9uaW5mX3oyMykgLSAoY29uZGl0aW9uaW5mX3NiX3oyMiAtIGNvbmRpdGlvbmluZl96MjIpLCB6MjN6MjJkcnVnX3ZzX3oyM3oyMm5vZHJ1ZyA9IChjb25kaXRpb25pbmZfc2JfejIzIC0gY29uZGl0aW9uaW5mX3NiX3oyMikgLSAoY29uZGl0aW9uaW5mX3oyMyAtIGNvbmRpdGlvbmluZl96MjIpIgp0bXJjMl9odW1hbl9rZWVwZXJzIDwtIGxpc3QoCiAgInoyM25vc2JfdnNfdW5pbmYiID0gYygiaW5mX3oyMyIsICJ1bmluZl9ub25lIiksCiAgInoyMm5vc2JfdnNfdW5pbmYiID0gYygiaW5mX3oyMiIsICJ1bmluZl9ub25lIiksCiAgInoyM25vc2JfdnNfejIybm9zYiIgPSBjKCJpbmZfejIzIiwgImluZl96MjIiKSwKICAiejIzc2JfdnNfejIyc2IiID0gYygiaW5mX3NiX3oyMyIsICJpbmZfc2JfejIyIiksCiAgInoyM3NiX3ZzX3oyM25vc2IiID0gYygiaW5mX3NiX3oyMyIsICJpbmZfejIzIiksCiAgInoyMnNiX3ZzX3oyMm5vc2IiID0gYygiaW5mX3NiX3oyMiIsICJpbmZfejIyIiksCiAgInoyM3NiX3ZzX3NiIiA9IGMoImluZl9zYl96MjMiLCAidW5pbmZfc2Jfbm9uZSIpLAogICJ6MjJzYl92c19zYiIgPSBjKCJpbmZfc2JfejIyIiwgInVuaW5mX3NiX25vbmUiKSwKICAiejIzc2JfdnNfdW5pbmYiID0gYygiaW5mX3NiX3oyMyIsICJ1bmluZl9ub25lIiksCiAgInoyMnNiX3ZzX3VuaW5mIiA9IGMoImluZl9zYl96MjIiLCAidW5pbmZfbm9uZSIpLAogICJzYl92c191bmluZiIgPSBjKCJ1bmluZl9zYl9ub25lIiwgInVuaW5mX25vbmUiKSwKICAiZXh0cmFfejIzMjIiID0gYygiejIzZHJ1Z25vZHJ1ZyIsICJ6MjJkcnVnbm9kcnVnIiksCiAgImV4dHJhX2RydWdub2RydWciID0gYygiejIzejIyZHJ1ZyIsICJ6MjN6MjJub2RydWciKSkKc2luZ2xlX3RtcmMyX2tlZXBlciA8LSBsaXN0KAogICJ6MjJzYl92c19zYiIgPSBjKCJpbmZfc2JfejIyIiwgInVuaW5mX3NiX25vbmUiKSkKdG1yYzJfZHJ1Z19rZWVwZXJzIDwtIGxpc3QoCiAgImRydWciID0gYygiYW50aW1vbnkiLCAibm9uZSIpKQp0bXJjMl90eXBlX2tlZXBlcnMgPC0gbGlzdCgKICAidHlwZSIgPSBjKCJVOTM3IiwgIk1hY3JvcGhhZ2VzIikpCnRtcmMyX3N0cmFpbl9rZWVwZXJzIDwtIGxpc3QoCiAgInN0cmFpbiIgPSBjKCJ6MjMiLCAiejIyIikpCnR5cGVfenltb19leHRyYSA8LSAienltb3NfdnNfdHlwZXMgPSAoY29uZGl0aW9uVTkzN196MjMgLSBjb25kaXRpb25VOTM3X3oyMikgLSAoY29uZGl0aW9uTWFjcm9waGFnZXNfejIzIC0gY29uZGl0aW9uTWFjcm9waGFnZXNfejIyKSIKdG1yYzJfdHlwZXp5bW9fa2VlcGVycyA8LSBsaXN0KAogICJ1OTM3X21hY3IiID0gYygiTWFjcm9waGFnZXNfbm9uZSIsICJVOTM3X25vbmUiKSwKICAienltb19tYWNyIiA9IGMoIk1hY3JvcGhhZ2VzX3oyMyIsICJNYWNyb3BoYWdlc196MjIiKSwKICAienltb191OTM3IiA9IGMoIlU5MzdfejIzIiwgIlU5MzdfejIyIiksCiAgInoyM190eXBlcyIgPSBjKCJVOTM3X3oyMyIsICJNYWNyb3BoYWdlc196MjMiKSwKICAiejIyX3R5cGVzIiA9IGMoIlU5MzdfejIyIiwgIk1hY3JvcGhhZ2VzX3oyMiIpLAogICJ6eW1vc190eXBlcyIgPSBjKCJ6eW1vc192c190eXBlcyIpKQp0bXJjMl90eXBlZHJ1Z19rZWVwZXJzIDwtIGxpc3QoCiAgInR5cGVfbm9kcnVnIiA9IGMoIlU5Mzdfbm9uZSIsICJNYWNyb3BoYWdlc19ub25lIiksCiAgInR5cGVfZHJ1ZyIgPSBjKCJVOTM3X2FudGltb255IiwgIk1hY3JvcGhhZ2VzX2FudGltb255IiksCiAgIm1hY3JfZHJ1Z3MiID0gYygiTWFjcm9waGFnZXNfYW50aW1vbnkiLCAiTWFjcm9waGFnZXNfbm9uZSIpLAogICJ1OTM3X2RydWdzIiA9IGMoIlU5MzdfYW50aW1vbnkiLCAiVTkzN19ub25lIikpCnU5Mzdfa2VlcGVycyA8LSBsaXN0KAogICJ6MjNub3NiX3ZzX3VuaW5mIiA9IGMoImluZl96MjMiLCAidW5pbmZfbm9uZSIpLAogICJ6MjJub3NiX3ZzX3VuaW5mIiA9IGMoImluZl96MjIiLCAidW5pbmZfbm9uZSIpLAogICJ6MjNub3NiX3ZzX3oyMm5vc2IiID0gYygiaW5mX3oyMyIsICJpbmZfejIyIiksCiAgInoyM3NiX3ZzX3oyMnNiIiA9IGMoImluZl9zYl96MjMiLCAiaW5mX3NiX3oyMiIpLAogICJ6MjNzYl92c196MjNub3NiIiA9IGMoImluZl9zYl96MjMiLCAiaW5mX3oyMyIpLAogICJ6MjJzYl92c196MjJub3NiIiA9IGMoImluZl9zYl96MjIiLCAiaW5mX3oyMiIpLAogICJ6MjNzYl92c19zYiIgPSBjKCJpbmZfc2JfejIzIiwgInVuaW5mX3NiX25vbmUiKSwKICAiejIyc2JfdnNfc2IiID0gYygiaW5mX3NiX3oyMiIsICJ1bmluZl9zYl9ub25lIiksCiAgInoyM3NiX3ZzX3VuaW5mIiA9IGMoImluZl9zYl96MjMiLCAidW5pbmZfbm9uZSIpLAogICJ6MjJzYl92c191bmluZiIgPSBjKCJpbmZfc2JfejIyIiwgInVuaW5mX25vbmUiKSwKICAic2JfdnNfdW5pbmYiID0gYygidW5pbmZfc2Jfbm9uZSIsICJ1bmluZl9ub25lIikpCiMjIElmIHNvbWUgY2FzZXMsIHdoZW4gdGhlIHNldCBvZiBzaWduaWZpY2FudCBnZW5lcyB3YXMgY2hvc2VuLCBhbgojIyBhZGRpdGlvbmFsIGZpbHRlciB3YXMgYWRkZWQgdG8gZXhjbHVkZSBnZW5lcyB3aXRoIGV4cHJlc3Npb24gdmFsdWVzCiMjIGxlc3MgdGhhbiAnaGlnaF9leHByZXNzaW9uJyBhY2NvcmRpbmcgdG8gdGhlCiMjICdoaWdoX2V4cHJlc3Npb25fY29sdW1uJyBpbiB0aGUgdGFibGUuCmhpZ2hfZXhwcmVzc2lvbiA8LSAxMjgKaGlnaF9leHByZXNzaW9uX2NvbHVtbiA8LSAiZGVzZXFfYmFzZW1lYW4iCgpjb21iaW5lZF90b190c3YgPC0gZnVuY3Rpb24oY29tYmluZWQsIGNlbGx0eXBlID0gImFsbCIpIHsKICBrZWVwZXJzIDwtIGNvbWJpbmVkW1sia2VlcGVycyJdXQogIGZvciAoayBpbiBzZXFfbGVuKGxlbmd0aChrZWVwZXJzKSkpIHsKICAgIGtuYW1lIDwtIG5hbWVzKGtlZXBlcnMpW2tdCiAgICBudW1lcmF0b3IgPC0ga2VlcGVyc1tba11dWzFdCiAgICBkZW5vbWluYXRvciA8LSBrZWVwZXJzW1trXV1bMl0KICAgIGZpbGVuYW1lIDwtIGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvdHN2X3RhYmxlcy90bXJjMl97Y2VsbHR5cGV9X3trbmFtZX1fbntudW1lcmF0b3J9X2R7ZGVub21pbmF0b3J9LXZ7dmVyfS54bHN4IikKICAgIGtkYXRhIDwtIGNvbWJpbmVkW1siZGF0YSJdXVtba25hbWVdXQogICAgaWYgKGlzLm51bGwoa2RhdGFbWyJiYXNpY19udW0iXV0pKSB7CiAgICAgIG5leHQKICAgIH0KICAgIHdhbnRlZCA8LSBjKCJoZ25jX3N5bWJvbCIsICJkZXNlcV9sb2dmYyIsICJkZXNlcV9hZGpwIiwKICAgICAgICAgICAgICAgICJkZXNlcV9iYXNlbWVhbiIsICJkZXNlcV9udW0iLCAiZGVzZXFfZGVuIikKICAgIHdhbnRlZF9kYXRhIDwtIGtkYXRhWywgd2FudGVkXQogICAgY29sbmFtZXMod2FudGVkX2RhdGEpIDwtIGMoImhnbmNfc3ltYm9sIiwgImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRlc2VxX21lYW4iLCAiZGVzZXFfbnVtZXJhdG9yIiwgImRlc2VxX2Rlbm9taW5hdG9yIikKICAgIHdyaXRlX3hsc3goZGF0YSA9IHdhbnRlZF9kYXRhLCBleGNlbCA9IGZpbGVuYW1lKQogIH0KfQoKd3JpdGVfYWxsX2dwIDwtIGZ1bmN0aW9uKGFsbF9ncCwgc3VmZml4ID0gTlVMTCkgewogIGFsbF93cml0dGVuIDwtIGxpc3QoKQogIGZvciAoZyBpbiBzZXFfbGVuKGxlbmd0aChhbGxfZ3ApKSkgewogICAgbmFtZSA8LSBuYW1lcyhhbGxfZ3ApW2ddCiAgICBkYXR1bSA8LSBhbGxfZ3BbW25hbWVdXQogICAgZmlsZW5hbWUgPC0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9ncHJvZmlsZXIve25hbWV9X2dwcm9maWxlci12e3Zlcn0ueGxzeCIpCiAgICBpZiAoIWlzLm51bGwoc3VmZml4KSkgewogICAgICBmaWxlbmFtZSA8LSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2dwcm9maWxlci97bmFtZX1fZ3Byb2ZpbGVye3N1ZmZpeH0tdnt2ZXJ9Lnhsc3giKQogICAgfQogICAgd3JpdHRlbiA8LSBzbSh3cml0ZV9ncHJvZmlsZXJfZGF0YShkYXR1bSwgZXhjZWwgPSBmaWxlbmFtZSkpCiAgICBhbGxfd3JpdHRlbltbZ11dIDwtIHdyaXR0ZW4KICB9CiAgcmV0dXJuKGFsbF93cml0dGVuKQp9CmBgYAoKIyMgUHJpbWFyeSBxdWVyaWVzCgpUaGVyZSBpcyBhIHNlcmllcyBvZiBpbml0aWFsIHF1ZXN0aW9ucyB3aGljaCBtYWtlIHNvbWUgc2Vuc2UKdG8gbWUsIGJ1dCB0aGVzZSBkbyBub3QgbmVjZXNzYXJpbHkgbWF0Y2ggdGhlIHNldCBvZiBxdWVzdGlvbnMgd2hpY2gKYXJlIG1vc3QgcHJlc3NpbmcuICBJIGFtIGhvcGluZyB0byBwdWxsIGJvdGggb2YgdGhlc2Ugc2V0cyBvZgpxdWVyaWVzIGluIG9uZS4KCkJlZm9yZSBleHRyYWN0aW5nIHRoZXNlIGdyb3VwcyBvZiBxdWVyaWVzLCBsZXQgdXMgaW52b2tlIHRoZQphbGxfcGFpcndpc2UoKSBmdW5jdGlvbiBhbmQgZ2V0IGFsbCBvZiB0aGUgbGlrZWx5IGNvbnRyYXN0cyBhbG9uZyB3aXRoCm9uZSBvciBtb3JlIGV4dHJhcyB0aGF0IG1pZ2h0IHByb3ZlIHVzZWZ1bCAodGhlICdleHRyYScgYXJndW1lbnQpLgoKVGhlIHN0cnVjdHVyZSBvZiB0aGVzZSBibG9ja3Mgd2lsbCBhbGwgYmFzaWNhbGx5IGJlIGlkZW50aWNhbDoKCiogUGVyZm9ybSBhIHNldCBvZiBwYWlyd2lzZSBjb250cmFzdHMgb2YgYWxsIHRoZSBjb25kaXRpb25zIGFnYWluc3QKICBlYWNoIG90aGVyLiAgT3B0aW9uYWxseSB1c2Ugc3ZhLgoqIEdpdmVuIHRoYXQgcmVzdWx0LCBkdW1wIGl0IGluIGl0cyBlbnRpcmV0eSB0byBhbiB4bHN4IGZpbGUgaW4gdGhlCiAgYW5hbHlzZXMvIGRpcmVjdG9yeS4KKiBHaXZlbiB0aG9zZSBjb21iaW5lZCB0YWJsZXMsIGV4dHJhY3QgZnJvbSB0aGVtIHRoZSBzZXQgZGVlbWVkCiAgJ3NpZ25pZmljYW50JyBieSB3aGF0ZXZlciBjcml0ZXJpYSB3ZSB3YW50IHRvIHRyeS4gKFVzdWFsbHkgfGxmY3wgPj0KICAxLjAsIGFkanVzdGVkIHAgPD0gMC4wNTsgYnV0IHBvdGVudGlhbGx5IGFsc28gZXhwcmVzc2lvbiA+PSB4IGFuZAogIHNvbWV0aW1lcyBhIHNldCBvZiBsZXNzIHN0cmluZ2VudCB2YWx1ZXMgKHxsZmN8ID49IDAuNikpCiogR2l2ZW4gb25lIG9yIG1vcmUgZ2VuZSBzZXRzIGRlZW1lZCAnc2lnbmlmaWNhbnQnIHBhc3MgdGhlbSB0bwogIGdQcm9maWxlcjIgYW5kIHNlZSB3aGF0IHBvcHMgb3V0LgoKIyMjIENvbWJpbmVkIFU5MzcgYW5kIE1hY3JvcGhhZ2VzOiBDb21wYXJlIGRydWcgZWZmZWN0cwoKV2hlbiB3ZSBoYXZlIHRoZSB1OTM3IGNlbGxzIGluIHRoZSBzYW1lIGRhdGFzZXQgYXMgdGhlIG1hY3JvcGhhZ2VzLAp0aGF0IHByb3ZpZGVzIGFuIGludGVyZXN0aW5nIG9wcG9ydHVuaXR5IHRvIHNlZSBpZiB3ZSBjYW4gb2JzZXJ2ZQpkcnVnLWRlcGVuZGFudCBlZmZlY3RzIHdoaWNoIGFyZSBzaGFyZWQgYWNyb3NzIGJvdGggY2VsbCB0eXBlcy4KCk5vdGUgdG8gc2VsZjogZ2l2ZW4gdGhlIGNoYW5nZXMgdG8gaHBnbHRvb2xzIEkgbWF5IG5lZWQgdG8gc3BlY2lmeSB0aGUKc3RhdGlzdGljYWwgbW9kZWwgc3RyaW5nIHdoZW4gSSBhbSB1c2luZyBzdmFzZXEgZm9yIHNvbWUvbWFueS9hbGwgb2YKdGhlc2UgY29tcGFyaXNvbnMuCgpgYGB7cn0KZHJ1Z19kZSA8LSBhbGxfcGFpcndpc2UoYWxsX2h1bWFuLCBmaWx0ZXIgPSBUUlVFLCBtb2RlbF9zdnMgPSAic3Zhc2VxIiwgZG9fbm9pc2VxID0gRkFMU0UpCmRydWdfZGUKCmRydWdfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgZHJ1Z19kZSwga2VlcGVycyA9IHRtcmMyX2RydWdfa2VlcGVycywKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvZGVfdGFibGVzL21hY3JvcGhhZ2VfZHJ1Z19jb21wYXJpc29uLXZ7dmVyfS54bHN4IikpCmRydWdfdGFibGUKI2NvbWJpbmVkX3RvX3RzdihkcnVnX3RhYmxlLCBjZWxsdHlwZSA9ICJhbGwiKQoKZHJ1Z19zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBkcnVnX3RhYmxlLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9zaWdfdGFibGVzL21hY3JvcGhhZ2VfZHJ1Z19zaWctdnt2ZXJ9Lnhsc3giKSkKZHJ1Z19zaWcKZHJ1Z19oaWdoc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgZHJ1Z190YWJsZSwgbWluX21lYW5fZXhwcnMgPSBoaWdoX2V4cHJlc3Npb24sIGV4cHJzX2NvbHVtbiA9IGhpZ2hfZXhwcmVzc2lvbl9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvbWFjcm9waGFnZV9kcnVnX2hpZ2hzaWctdnt2ZXJ9Lnhsc3giKSkKZHJ1Z19oaWdoc2lnCmRydWdfbGVzc3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGRydWdfdGFibGUsIGxmYyA9IDAuNiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9tYWNyb3BoYWdlX2RydWdfbGVzc3NpZy12e3Zlcn0ueGxzeCIpKQpkcnVnX2xlc3NzaWcKYGBgCgojIyMjIGdQcm9maWxlcjIgcmVzdWx0cyBvZiB0aGUgc2lnbmlmaWNhbnQgZHJ1ZyBnZW5lcwoKYGBge3J9CmFsbF9kcnVnX2dwIDwtIGFsbF9ncHJvZmlsZXIoZHJ1Z19zaWcsIGVucmljaF9pZF9jb2x1bW4gPSAiaGduY19zeW1ib2wiKQphbGxfZHJ1Z19ncAp3cml0dGVuIDwtIHdyaXRlX2FsbF9ncChhbGxfZHJ1Z19ncCkKCmFsbF9kcnVnX2xlc3NzaWcgPC0gYWxsX2dwcm9maWxlcihkcnVnX2xlc3NzaWcsIGVucmljaF9pZF9jb2x1bW4gPSAiaGduY19zeW1ib2wiKQp3cml0dGVuIDwtIHdyaXRlX2FsbF9ncChhbGxfZHJ1Z19sZXNzc2lnLCBzdWZmaXggPSAiX2xmYzAuNl8iKQpgYGAKCiMjIyBDb21iaW5lZCBVOTM3IGFuZCBNYWNyb3BoYWdlczogY29tcGFyZSBjZWxsIHR5cGVzCgpUaGVyZSBhcmUgYSBjb3VwbGUgb2Ygd2F5cyBvbmUgbWlnaHQgd2FudCB0byBkaXJlY3RseSBjb21wYXJlIHRoZSB0d28KY2VsbCB0eXBlcy4KCiogR2l2ZW4gdGhhdCB0aGUgdmFyaWFuY2UgYmV0d2VlbiB0aGUgdHdvIGNlbGx0eXBlcyBpcyBzbyBodWdlLCBqdXN0CmNvbXBhcmUgYWxsIHNhbXBsZXMuCiogT25lIG1pZ2h0IHdhbnQgdG8gY29tcGFyZSB0aGVtIHdpdGggdGhlIGludGVyYWN0aW9uIGVmZmVjdHMgb2YgZHJ1Zy96eW1vZGVtZS4KCmBgYHtyfQp0eXBlX2RlIDwtIGFsbF9wYWlyd2lzZShhbGxfaHVtYW5fdHlwZXMsIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX3N2cyA9ICJzdmFzZXEiLCBkb19ub2lzZXEgPSBGQUxTRSkKdHlwZV9kZQoKdHlwZV90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0eXBlX2RlLCBrZWVwZXJzID0gdG1yYzJfdHlwZV9rZWVwZXJzLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9kZV90YWJsZXMvbWFjcm9waGFnZV90eXBlX2NvbXBhcmlzb24tdnt2ZXJ9Lnhsc3giKSkKdHlwZV90YWJsZQojY29tYmluZWRfdG9fdHN2KHR5cGVfdGFibGUsIGNlbGx0eXBlID0gImFsbCIpCgp0eXBlX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHR5cGVfdGFibGUsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvbWFjcm9waGFnZV90eXBlX3NpZy12e3Zlcn0ueGxzeCIpKQp0eXBlX3NpZwp0eXBlX2hpZ2hzaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0eXBlX3RhYmxlLCBtaW5fbWVhbl9leHBycyA9IGhpZ2hfZXhwcmVzc2lvbiwgZXhwcnNfY29sdW1uID0gaGlnaF9leHByZXNzaW9uX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9tYWNyb3BoYWdlX3R5cGVfaGlnaHNpZy12e3Zlcn0ueGxzeCIpKQp0eXBlX2hpZ2hzaWcKCnR5cGVfbGVzc3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHR5cGVfdGFibGUsIGxmYyA9IDAuNiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9tYWNyb3BoYWdlX3R5cGVfbGVzc3NpZy12e3Zlcn0ueGxzeCIpKQp0eXBlX3NpZwpgYGAKCiMjIyMgQ29tYmluZWQgZmFjdG9ycyBvZiBpbnRlcmVzdDogY2VsbHR5cGUrenltb2RlbWUKCkdpdmVuIHRoZSBhYm92ZSBleHBsaWNpdCBjb21wYXJpc29uIG9mIGFsbCBzYW1wbGVzIGNvbXByaXNpbmcgdGhlIHR3bwpjZWxsIHR5cGVzLCBub3cgbGV0IHVzIGxvb2sgYXQgdGhlIGRydWcgdHJlYXRtZW50K3p5bW9kZW1lIHN0YXR1cyB3aXRoCmFsbCBzYW1wbGVzLCBtYWNyb3BoYWdlcyBhbmQgVTkzNy4KCmBgYHtyfQp0eXBlX3p5bW9fZGUgPC0gYWxsX3BhaXJ3aXNlKHR5cGVfenltbywgZmlsdGVyID0gVFJVRSwgbW9kZWxfc3ZzID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fbm9pc2VxID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXh0cmFfY29udHJhc3RzID0gdHlwZV96eW1vX2V4dHJhKQp0eXBlX3p5bW9fZGUKCnR5cGVfenltb190YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0eXBlX3p5bW9fZGUsIGtlZXBlcnMgPSB0bXJjMl90eXBlenltb19rZWVwZXJzLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9kZV90YWJsZXMvbWFjcm9waGFnZV90eXBlX3p5bW9fY29tcGFyaXNvbi12e3Zlcn0ueGxzeCIpKQojY29tYmluZWRfdG9fdHN2KHR5cGVfenltb190YWJsZSwgY2VsbHR5cGUgPSAiYWxsIikKCnR5cGVfenltb19zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0eXBlX3p5bW9fdGFibGUsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvbWFjcm9waGFnZV90eXBlX3p5bW9fc2lnLXZ7dmVyfS54bHN4IikpCnR5cGVfenltb19zaWcKdHlwZV96eW1vX2hpZ2hzaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0eXBlX3p5bW9fdGFibGUsIG1pbl9tZWFuX2V4cHJzID0gaGlnaF9leHByZXNzaW9uLCBleHByc19jb2x1bW4gPSBoaWdoX2V4cHJlc3Npb25fY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9zaWdfdGFibGVzL21hY3JvcGhhZ2VfdHlwZV96eW1vX2hpZ2hzaWctdnt2ZXJ9Lnhsc3giKSkKdHlwZV96eW1vX2xlc3NzaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0eXBlX3p5bW9fdGFibGUsIGxmYyA9IDAuNiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9tYWNyb3BoYWdlX3R5cGVfenltb19sZXNzc2lnLXZ7dmVyfS54bHN4IikpCnR5cGVfenltb19sZXNzc2lnCmBgYAoKIyMjIyBDb21iaW5lZCBmYWN0b3JzIG9mIGludGVyZXN0OiBjZWxsdHlwZStkcnVnCgpUaGUgJ3R5cGVfZHJ1ZycgZGF0YXN0cnVjdHVyZSBpcyB0aGUgc2FtZSBhcyBhYm92ZSwgYnV0IHRoZSBjb25kaXRpb24KaXMgY3JlYXRlZCBmcm9tIHRoZSBjb25jYXRlbmF0aW9uIG9mIHRoZSBjZWxsIHR5cGUgYW5kIGRydWcgdHJlYXRtZW50LgoKYGBge3J9CnR5cGVfZHJ1Z19kZSA8LSBhbGxfcGFpcndpc2UodHlwZV9kcnVnLCBmaWx0ZXIgPSBUUlVFLCBtb2RlbF9zdnMgPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9mc3RyaW5nID0gIn4gMCArIGNvbmRpdGlvbiIpCnR5cGVfZHJ1Z19kZQp0eXBlX2RydWdfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdHlwZV9kcnVnX2RlLCBrZWVwZXJzID0gdG1yYzJfdHlwZWRydWdfa2VlcGVycywKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvZGVfdGFibGVzL21hY3JvcGhhZ2VfdHlwZV9kcnVnX2NvbXBhcmlzb24tdnt2ZXJ9Lnhsc3giKSkKdHlwZV9kcnVnX3RhYmxlCgojY29tYmluZWRfdG9fdHN2KHR5cGVfZHJ1Z190YWJsZSwgY2VsbHR5cGUgPSAiYWxsIikKCnR5cGVfZHJ1Z19zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0eXBlX2RydWdfdGFibGUsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvbWFjcm9waGFnZV90eXBlX2RydWdfc2lnLXZ7dmVyfS54bHN4IikpCnR5cGVfZHJ1Z19zaWcKCnR5cGVfZHJ1Z19oaWdoc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdHlwZV9kcnVnX3RhYmxlLCBtaW5fbWVhbl9leHBycyA9IGhpZ2hfZXhwcmVzc2lvbiwgZXhwcnNfY29sdW1uID0gaGlnaF9leHByZXNzaW9uX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9tYWNyb3BoYWdlX3R5cGVfZHJ1Z19oaWdoc2lnLXZ7dmVyfS54bHN4IikpCnR5cGVfZHJ1Z19oaWdoc2lnCgp0eXBlX2RydWdfbGVzc3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHR5cGVfZHJ1Z190YWJsZSwgbGZjID0gMC42LAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9zaWdfdGFibGVzL21hY3JvcGhhZ2VfdHlwZV9kcnVnX2xlc3NzaWctdnt2ZXJ9Lnhsc3giKSkKdHlwZV9kcnVnX2xlc3NzaWcKYGBgCgojIEluZGl2aWR1YWwgY2VsbCB0eXBlcwoKQXQgdGhpcyBwb2ludCwgSSB0aGluayBpdCBpcyBmYWlyIHRvIHNheSB0aGF0IHRoZSB0d28gY2VsbCB0eXBlcyBhcmUKc3VmZmljaWVudGx5IGRpZmZlcmVudCB0aGF0IHRoZXkgZG8gbm90IHJlYWxseSBiZWxvbmcgdG9nZXRoZXIgaW4gYQpzaW5nbGUgYW5hbHlzaXMuCgojIyBkcnVnIG9yIHN0cmFpbiBlZmZlY3RzLCBzaW5nbGUgY2VsbCB0eXBlCgpPbmUgb2YgdGhlIHF1ZXJpZXMgTmFqaWIgYXNrZWQgd2hpY2ggSSB0aGluayBJIG1pc2ludGVycHJldGVkIHdhcyB0bwpsb29rIGF0IGRydWcgYW5kL29yIHN0cmFpbiBlZmZlY3RzLiAgTXkgaW50ZXJwcmV0YXRpb24gaXMgc29tZXdoZXJlCmJlbG93IGFuZCB3YXMgbm90IHdoYXQgaGUgd2FzIGxvb2tpbmcgZm9yLiAgSW5zdGVhZCwgaGUgd2FzIGxvb2tpbmcgdG8Kc2VlIGFsbChtYWNyb3BoYWdlKSBkcnVnL25vZHJ1ZyBhbmQgYWxsKG1hY3JvcGhhZ2UpIHoyMy96MjIgYW5kCmNvbXBhcmUgdGhlbSB0byBlYWNoIG90aGVyLiAgSXQgbWF5IGJlIHRoYXQgdGhpcyBpcyBzdGlsbCBhIHdyb25nCmludGVycHJldGF0aW9uLCBpZiBzbyB0aGUgbW9zdCBsaWtlbHkgY29tcGFyaXNvbiBpcyBlaXRoZXI6CgoqICAoejIzZHJ1Zy96MjJkcnVnKSAvICh6MjNub2RydWcvejIybm9kcnVnKSwgb3IgcGVyaGFwcwoqICAoejIzZHJ1Zy96MjNub2RydWcpIC8gKHoyMmRydWcvejIybm9kcnVnKSwKCkkgYW0gbm90IHN1cmUgdGhvc2UgY29uZnVzZSBtZSwgYW5kIGF0IGxlYXN0IG9uZSBvZiB0aGVtIGlzIGJlbG93CgojIyBNYWNyb3BoYWdlcwoKSW4gdGhlc2UgYmxvY2tzIHdlIHdpbGwgZXhwbGljaXRseSBxdWVyeSBvbmx5IG9uZSBmYWN0b3IgYXQgYSB0aW1lLApkcnVnIGFuZCBzdHJhaW4uICBUaGUgZXZlbnR1YWwgZ29hbCBpcyB0byBsb29rIGZvciBlZmZlY3RzIG9mCmRydWcgdHJlYXRtZW50IGFuZC9vciBzdHJhaW4gdHJlYXRtZW50IHdoaWNoIGFyZSBzaGFyZWQ/CgojIyMgTWFjcm9waGFnZSBEcnVnIG9ubHkKClRodXMgd2Ugd2lsbCBzdGFydCB3aXRoIHRoZSBwdXJlIGRydWcgcXVlcnkuICBJbiB0aGlzIGJsb2NrIHdlIHdpbGwKbG9vayBvbmx5IGF0IHRoZSBkcnVnL25vZHJ1ZyBlZmZlY3QuCgpgYGB7cn0KaHNfbWFjcl9kcnVnX2RlIDwtIGFsbF9wYWlyd2lzZShoc19tYWNyX2RydWdfZXhwdCwgZmlsdGVyID0gVFJVRSwgbW9kZWxfc3ZzID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfZnN0cmluZyA9ICJ+IDAgKyBjb25kaXRpb24iKQpoc19tYWNyX2RydWdfZGUKCmhzX21hY3JfZHJ1Z190YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBoc19tYWNyX2RydWdfZGUsIGtlZXBlcnMgPSB0bXJjMl9kcnVnX2tlZXBlcnMsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2RlX3RhYmxlcy9tYWNyb3BoYWdlX29ubHlkcnVnX3RhYmxlLXZ7dmVyfS54bHN4IikpCmhzX21hY3JfZHJ1Z190YWJsZQoKI2NvbWJpbmVkX3RvX3Rzdihoc19tYWNyX2RydWdfdGFibGUsIGNlbGx0eXBlID0gIm1hY3JvcGhhZ2UiKQoKaHNfbWFjcl9kcnVnX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3JfZHJ1Z190YWJsZSwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9tYWNyb3BoYWdlb25seV9kcnVnX3NpZy12e3Zlcn0ueGxzeCIpKQpoc19tYWNyX2RydWdfc2lnCgpoc19tYWNyX2RydWdfaGlnaHNpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3JfZHJ1Z190YWJsZSwgbWluX21lYW5fZXhwcnMgPSBoaWdoX2V4cHJlc3Npb24sIGV4cHJzX2NvbHVtbiA9IGhpZ2hfZXhwcmVzc2lvbl9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvbWFjcm9waGFnZW9ubHlfZHJ1Z19oaWdoc2lnLXZ7dmVyfS54bHN4IikpCmhzX21hY3JfZHJ1Z19oaWdoc2lnCgojIyBDcmVhdGluZyB0aGUgZm9sbG93aW5nIHRvIHNlZSBob3cgaXQgYWZmZWN0cyBnUHJvZmlsZXIuCmhzX21hY3JfZHJ1Z19sZXNzc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgaHNfbWFjcl9kcnVnX3RhYmxlLCBsZmMgPSAwLjYsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvbWFjcm9waGFnZW9ubHlfZHJ1Z19zaWdfbGZjMC42LXZ7dmVyfS54bHN4IikpCmBgYAoKIyMjIE1hY3JvcGhhZ2UgU3RyYWluIG9ubHkKCkluIGEgc2ltaWxhciBmYXNoaW9uLCBsZXQgdXMgbG9vayBmb3IgZWZmZWN0cyB3aGljaCBhcmUgb2JzZXJ2ZWQgd2hlbgp3ZSBjb25zaWRlciBvbmx5IHRoZSBzdHJhaW4gdXNlZCBkdXJpbmcgaW5mZWN0aW9uLgoKYGBge3J9CmhzX21hY3Jfc3RyYWluX2RlIDwtIGFsbF9wYWlyd2lzZShoc19tYWNyX3N0cmFpbl9leHB0LCBmaWx0ZXIgPSBUUlVFLCBtb2RlbF9zdnMgPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2ZzdHJpbmcgPSAifiAwICsgY29uZGl0aW9uIikKaHNfbWFjcl9zdHJhaW5fZGUKCmhzX21hY3Jfc3RyYWluX3RhYmxlIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIGhzX21hY3Jfc3RyYWluX2RlLCBrZWVwZXJzID0gdG1yYzJfc3RyYWluX2tlZXBlcnMsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2RlX3RhYmxlcy9tYWNyb3BoYWdlX29ubHlzdHJhaW5fdGFibGUtdnt2ZXJ9Lnhsc3giKSkKaHNfbWFjcl9zdHJhaW5fdGFibGUKY29tYmluZWRfdG9fdHN2KGhzX21hY3Jfc3RyYWluX3RhYmxlLCBjZWxsdHlwZSA9ICJtYWNyb3BoYWdlIikKCmhzX21hY3Jfc3RyYWluX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3Jfc3RyYWluX3RhYmxlLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9zaWdfdGFibGVzL21hY3JvcGhhZ2Vvbmx5X29ubHlzdHJhaW5fc2lnLXZ7dmVyfS54bHN4IikpCmhzX21hY3Jfc3RyYWluX3NpZwoKaHNfbWFjcl9zdHJhaW5faGlnaHNpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3Jfc3RyYWluX3RhYmxlLCBtaW5fbWVhbl9leHBycyA9IGhpZ2hfZXhwcmVzc2lvbiwgZXhwcnNfY29sdW1uID0gaGlnaF9leHByZXNzaW9uX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9tYWNyb3BoYWdlb25seV9vbmx5c3RyYWluX2hpZ2hzaWctdnt2ZXJ9Lnhsc3giKSkKaHNfbWFjcl9zdHJhaW5faGlnaHNpZwoKaHNfbWFjcl9zdHJhaW5fbGVzc3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3Jfc3RyYWluX3RhYmxlLCBsZmMgPSAwLjYsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvbWFjcm9waGFnZW9ubHlfb25seXN0cmFpbl9sZXNzc2lnLXZ7dmVyfS54bHN4IikpCmhzX21hY3Jfc3RyYWluX2xlc3NzaWcKYGBgCgojIyMgQ29tcGFyZSBEcnVnIGFuZCBTdHJhaW4gRWZmZWN0cwoKTm93IGxldCB1cyBjb25zaWRlciB0aGUgYWJvdmUgdHdvIGNvbXBhcmlzb25zIHRvZ2V0aGVyLiAgRmlyc3QsIEkgd2lsbApwbG90IHRoZSBsb2dGQyB2YWx1ZXMgb2YgdGhlbSBhZ2FpbnN0IGVhY2ggb3RoZXIgKGRydWcgb24geC1heGlzIGFuZApzdHJhaW4gb24gdGhlIHktYXhpcykuICBUaGVuIHdlIGNhbiBleHRyYWN0IHRoZSBzaWduaWZpY2FudCBnZW5lcyBpbiBhCmZldyBjb21iaW5lZCBjYXRlZ29yaWVzIG9mIGludGVyZXN0LiAgSSBhc3N1bWUgdGhlc2Ugd2lsbCBmb2N1cwpleGNsdXNpdmVseSBvbiB0aGUgY2F0ZWdvcmllcyB3aGljaCBpbmNsdWRlIHRoZSBpbnRyb2R1Y3Rpb24gb2YgdGhlCmRydWcuCgpgYGB7cn0KZHJ1Z19zdHJhaW5fY29tcF9kZiA8LSBtZXJnZShoc19tYWNyX2RydWdfdGFibGVbWyJkYXRhIl1dW1siZHJ1ZyJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoc19tYWNyX3N0cmFpbl90YWJsZVtbImRhdGEiXV1bWyJzdHJhaW4iXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAicm93Lm5hbWVzIikKZHJ1Z19zdHJhaW5fY29tcF9wbG90IDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIoCiAgZHJ1Z19zdHJhaW5fY29tcF9kZlssIGMoImRlc2VxX2xvZ2ZjLngiLCAiZGVzZXFfbG9nZmMueSIpXSkKIyMgQ29udHJhc3RzOiBhbnRpbW9ueS9ub25lLCB6MjMvejIyOyB4LWF4aXM6IGRydWcsIHktYXhpczogc3RyYWluCiMjIHRvcCBsZWZ0OiBoaWdoZXIgbm8gZHJ1ZywgejIzOyB0b3AgcmlnaHQ6IGhpZ2hlciBkcnVnIHoyMwojIyBib3R0b20gbGVmdDogaGlnaGVyIG5vIGRydWcsIHoyMjsgYm90dG9tIHJpZ2h0OiBoaWdoZXIgZHJ1ZyB6MjIKZHJ1Z19zdHJhaW5fY29tcF9wbG90W1sic2NhdHRlciJdXQpgYGAKCkFzIEkgbm90ZWQgaW4gdGhlIGNvbW1lbnRzIGFib3ZlLCBzb21lIHF1YWRyYW50cyBvZiB0aGUgc2NhdHRlciBwbG90CmFyZSBsaWtlbHkgdG8gYmUgb2YgZ3JlYXRlciBpbnRlcmVzdCB0byB1cyB0aGFuIG90aGVycyAodGhlIHJpZ2h0CnNpZGUpLiAgQmVjYXVzZSBJIGdldCBjb25mdXNlZCBzb21ldGltZXMsIHRoZSBmb2xsb3dpbmcgYmxvY2sgd2lsbApleHBsaWNpdGx5IG5hbWUgdGhlIGNhdGVnb3JpZXMgb2YgbGlrZWx5IGludGVyZXN0LCB0aGVuIGFzayB3aGljaApnZW5lcyBhcmUgc2hhcmVkIGFtb25nIHRoZW0sIGFuZCBmaW5hbGx5IHVzZSBVcFNldFIgdG8gZXh0cmFjdCB0aGUKdmFyaW91cyBnZW5lIGludGVyc2VjdGlvbi91bmlvbiBjYXRlZ29yaWVzLgoKYGBge3J9CmhpZ2hlcl9kcnVnIDwtIGhzX21hY3JfZHJ1Z19zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0KaGlnaGVyX25vZHJ1ZyA8LSBoc19tYWNyX2RydWdfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXQpoaWdoZXJfejIzIDwtIGhzX21hY3Jfc3RyYWluX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0KaGlnaGVyX3oyMiA8LSBoc19tYWNyX3N0cmFpbl9zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0Kc3VtKHJvd25hbWVzKGhpZ2hlcl9kcnVnKSAlaW4lIHJvd25hbWVzKGhpZ2hlcl96MjMpKQpzdW0ocm93bmFtZXMoaGlnaGVyX2RydWcpICVpbiUgcm93bmFtZXMoaGlnaGVyX3oyMikpCnN1bShyb3duYW1lcyhoaWdoZXJfbm9kcnVnKSAlaW4lIHJvd25hbWVzKGhpZ2hlcl96MjMpKQpzdW0ocm93bmFtZXMoaGlnaGVyX25vZHJ1ZykgJWluJSByb3duYW1lcyhoaWdoZXJfejIyKSkKCmRydWdfejIzX2xzdCA8LSBsaXN0KCJkcnVnIiA9IHJvd25hbWVzKGhpZ2hlcl9kcnVnKSwKICAgICAgICAgICAgICAgICAgICAgInoyMyIgPSByb3duYW1lcyhoaWdoZXJfejIzKSkKdXBzZXRfaW5wdXQgPC0gVXBTZXRSOjpmcm9tTGlzdChkcnVnX3oyM19sc3QpCmhpZ2hlcl9kcnVnX3oyMyA8LSB1cHNldCh1cHNldF9pbnB1dCwgdGV4dC5zY2FsZSA9IDIpCmhpZ2hlcl9kcnVnX3oyMwoKZHJ1Z196MjNfc2hhcmVkX2dlbmVzIDwtIG92ZXJsYXBfZ3JvdXBzKGRydWdfejIzX2xzdCkKc2hhcmVkX2dlbmVzX2RydWdfejIzIDwtIG92ZXJsYXBfZ2VuZWlkcyhkcnVnX3oyM19zaGFyZWRfZ2VuZXMsICJkcnVnOnoyMyIpCnNoYXJlZF9nZW5lc19kcnVnX3oyMyA8LSBhdHRyKGRydWdfejIzX3NoYXJlZF9nZW5lcywgImVsZW1lbnRzIilbZHJ1Z196MjNfc2hhcmVkX2dlbmVzW1siZHJ1Zzp6MjMiXV1dCgpkcnVnX3oyMl9sc3QgPC0gbGlzdCgiZHJ1ZyIgPSByb3duYW1lcyhoaWdoZXJfZHJ1ZyksCiAgICAgICAgICAgICAgICAgICAgICJ6MjIiID0gcm93bmFtZXMoaGlnaGVyX3oyMikpCmhpZ2hlcl9kcnVnX3oyMiA8LSB1cHNldChVcFNldFI6OmZyb21MaXN0KGRydWdfejIyX2xzdCksIHRleHQuc2NhbGUgPSAyKQpoaWdoZXJfZHJ1Z196MjIKCmRydWdfejIyX3NoYXJlZF9nZW5lcyA8LSBvdmVybGFwX2dyb3VwcyhkcnVnX3oyMl9sc3QpCnNoYXJlZF9nZW5lc19kcnVnX3oyMiA8LSBvdmVybGFwX2dlbmVpZHMoZHJ1Z196MjJfc2hhcmVkX2dlbmVzLCAiZHJ1Zzp6MjIiKQpzaGFyZWRfZ2VuZXNfZHJ1Z196MjIgPC0gYXR0cihkcnVnX3oyMl9zaGFyZWRfZ2VuZXMsICJlbGVtZW50cyIpW2RydWdfejIyX3NoYXJlZF9nZW5lc1tbImRydWc6ejIyIl1dXQpgYGAKCiMjIyBQZXJmb3JtIGdQcm9maWxlciBvbiBkcnVnL3N0cmFpbiBlZmZlY3Qgc2hhcmVkIGdlbmVzCgpOb3cgdGhhdCB3ZSBoYXZlIHNvbWUgcG9wdWxhdGlvbnMgb2YgZ2VuZXMgd2hpY2ggYXJlIHNoYXJlZCBhY3Jvc3MgdGhlCmRydWcvc3RyYWluIGVmZmVjdHMsIGxldCB1cyBwYXNzIHRoZW0gdG8gc29tZSBHU0VBIGFuYWx5c2VzIGFuZCBzZWUKd2hhdCBwb3BzIG91dC4KCmBgYHtyfQp3YW50ZWQgPC0gZHJ1Z196MjNfc2hhcmVkX2dlbmVzW1siZHJ1Zzp6MjMiXV0Kc2hhcmVkX2dlbmVzX2RydWdfejIzIDwtIGF0dHIoZHJ1Z196MjNfc2hhcmVkX2dlbmVzLCAiZWxlbWVudHMiKVt3YW50ZWRdCnNoYXJlZF9kcnVnX3oyM19ncCA8LSBzaW1wbGVfZ3Byb2ZpbGVyKHNoYXJlZF9nZW5lc19kcnVnX3oyMykKc2hhcmVkX2RydWdfejIzX2dwW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0Kc2hhcmVkX2RydWdfejIzX2dwW1sicHZhbHVlX3Bsb3RzIl1dW1siQlAiXV0Kc2hhcmVkX2RydWdfejIzX2dwW1sicHZhbHVlX3Bsb3RzIl1dW1siUkVBQyJdXQoKd2FudGVkIDwtIGRydWdfejIyX3NoYXJlZF9nZW5lc1tbImRydWc6ejIyIl1dCnNoYXJlZF9nZW5lc19kcnVnX3oyMiA8LSBhdHRyKGRydWdfejIyX3NoYXJlZF9nZW5lcywgImVsZW1lbnRzIilbd2FudGVkXQpzaGFyZWRfZHJ1Z196MjJfZ3AgPC0gc2ltcGxlX2dwcm9maWxlcihzaGFyZWRfZ2VuZXNfZHJ1Z196MjIpCnNoYXJlZF9kcnVnX3oyMl9ncFtbInB2YWx1ZV9wbG90cyJdXVtbIkJQIl1dCmBgYAoKIyBPdXIgbWFpbiBxdWVzdGlvbiBvZiBpbnRlcmVzdAoKVGhlIGRhdGEgc3RydWN0dXJlIGhzX21hY3IgY29udGFpbnMgb3VyIHByaW1hcnkgbWFjcm9waGFnZXMsIHdoaWNoCmFyZSwgYXMgc2hvd24gYWJvdmUsIHRoZSBkYXRhIHdlIGNhbiByZWFsbHkgc2luayBvdXIgdGVldGggaW50by4KCk5vdGUsIHdlIGV4cGVjdCBzb21lIGVycm9ycyB3aGVuIHJ1bm5pbmcgdGhlIGNvbWJpbmVfZGVfdGFibGVzKCkKYmVjYXVzZSBub3QgYWxsIG1ldGhvZHMgSSB1c2UgYXJlIGNvbWZvcnRhYmxlIHVzaW5nIHRoZSByYXRpbyBvcgpyYXRpb3MgY29udHJhc3RzIHdlIGFkZGVkIGluIHRoZSAnZXh0cmFzJyBhcmd1bWVudC4gIEFzIGEgcmVzdWx0LCB3aGVuCndlIGNvbWJpbmUgdGhlbSBpbnRvIHRoZSBsYXJnZXIgb3V0cHV0IHRhYmxlcywgdGhvc2UgcGVjdWxpYXIKY29udHJhc3RzIGZhaWwuICBUaGlzIGRvZXMgbm90IHN0b3AgaXQgZnJvbSB3cml0aW5nIHRoZSByZXN0IG9mIHRoZQpyZXN1bHRzLCBob3dldmVyLgoKYGBge3J9CiN0ZXN0ID0gZGVzZXFfcGFpcndpc2Uobm9ybWFsaXplX2V4cHQoaHNfbWFjciwgZmlsdGVyPVRSVUUpLAojICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX3N2cyA9ICJzdmFzZXEiLCBmaWx0ZXIgPSBUUlVFLAojICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2NvbnRyYXN0cyA9IHRtcmMyX2h1bWFuX2V4dHJhKQoKaHNfbWFjcl9kZV9ub2V4dHJhIDwtIGFsbF9wYWlyd2lzZShoc19tYWNyLCBtb2RlbF9zdnMgPSAic3Zhc2VxIiwgbW9kZWxfZnN0cmluZyA9ICJ+IDAgKyBjb25kaXRpb24iLCBmaWx0ZXIgPSBUUlVFKQoKaHNfbWFjcl9kZSA8LSBhbGxfcGFpcndpc2UoaHNfbWFjciwgbW9kZWxfc3ZzID0gInN2YXNlcSIsIG1vZGVsX2ZzdHJpbmcgPSAifiAwICsgY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwgZXh0cmFfY29udHJhc3RzID0gdG1yYzJfaHVtYW5fZXh0cmEpCmhzX21hY3JfZGUKCmhzX3NpbmdsZV90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBoc19tYWNyX2RlLCBrZWVwZXJzID0gc2luZ2xlX3RtcmMyX2tlZXBlciwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvZGVfdGFibGVzL2hzX21hY3JfZHJ1Z196eW1vX3oyMnNiX3NiLXZ7dmVyfS54bHN4IikpCmhzX3NpbmdsZV90YWJsZQpoc19tYWNyX3RhYmxlIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIGhzX21hY3JfZGUsIGtlZXBlcnMgPSB0bXJjMl9odW1hbl9rZWVwZXJzLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9kZV90YWJsZXMvaHNfbWFjcl9kcnVnX3p5bW9fdGFibGVfbWFjcl9vbmx5LXZ7dmVyfS54bHN4IikpCmhzX21hY3JfdGFibGUKI2NvbWJpbmVkX3RvX3Rzdihoc19tYWNyX3RhYmxlLCAibWFjcm9waGFnZSIpCgpoc19tYWNyX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3JfdGFibGUsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvaHNfbWFjcl9kcnVnX3p5bW9fc2lnLXZ7dmVyfS54bHN4IikpCmhzX21hY3Jfc2lnCmhzX21hY3JfaGlnaHNpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3JfdGFibGUsIG1pbl9tZWFuX2V4cHJzID0gaGlnaF9leHByZXNzaW9uLCBleHByc19jb2x1bW4gPSBoaWdoX2V4cHJlc3Npb25fY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9zaWdfdGFibGVzL2hzX21hY3JfZHJ1Z196eW1vX2hpZ2hzaWctdnt2ZXJ9Lnhsc3giKSkKaHNfbWFjcl9oaWdoc2lnCmhzX21hY3JfbGVzc3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3JfdGFibGUsIGxmYyA9IDAuNiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9oc19tYWNyX2RydWdfenltb19zaWdfbGZjMC42LXZ7dmVyfS54bHN4IikpCmhzX21hY3JfbGVzc3NpZwpgYGAKCiMjIGdlbmUgZ3JvdXAgdXBzZXQKCiMjIyAyLjMgdnMgMi4yIHVwIGFuZCBkb3duIHZzLiB1bmluZmVjdGVkCgpUaGlzIGlzIG15IHZlcnNpb24gb2YgdGhlIFZlbm4gZGlhZ3JhbSB3aGljaCBpbmNsdWRlcyB0aGUgdGV4dDoKCiJEaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgaW4gbWFjcm9waGFnZXMgaW5mZWN0ZWQgd2l0aApzdWJwb3B1bGF0aW9ucyAyLjIgb3IgMi4zLiAgVm9sY2FubyBwbG90cyBjb250cmFzdCBvZjogQS4gVmVubiBkaWFncmFtCmZvciB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcyBieSBpbmZlY3Rpb24gd2l0aCAyLjMgYW5kIDIuMgpzdHJhaW5zLiBCLiBpbmZlY3RlZCBjZWxscyB3aXRoIDIuMyBzdHJhaW5zIGFuZCB1bmluZmVjdGVkIGNlbGxzOwpDLiBpbmZlY3RlZCBjZWxscyB3aXRoIDIuMiBzdHJhaW5zIGFuZCB1bmluZmVjdGVkIGNlbGxzOyBELiBpbmZlY3RlZApjZWxscyB3aXRoIDIuMyBzdHJhaW5zIGFuZCBpbmZlY3RlZCBjZWxscyB3aXRoIDIuMiBzdHJhaW5zIgoKVGhlIGZvbGxvd2luZyB1cHNldCBwbG90IGlzIGN1cnJlbnRseSBGaWd1cmUgMkUuCgpgYGB7cn0Kbm9kcnVnX3Vwc2V0IDwtIHVwc2V0cl9jb21iaW5lZF9kZShoc19tYWNyX3RhYmxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lyZWRfY29udHJhc3RzID0gYygiejIybm9zYl92c191bmluZiIsICJ6MjNub3NiX3ZzX3VuaW5mIikpCnBwKGZpbGUgPSAiaW1hZ2VzL25vZHJ1Z191cHNldC5zdmciKQpub2RydWdfdXBzZXRbWyJwbG90Il1dCmRldi5vZmYoKQpub2RydWdfdXBzZXQKYGBgCgojIyMjIEEgcG9pbnQgb2YgaW50ZXJlc3Qgd2hpbGUgT2xnYSB2aXNpdHMgVW1kCgpOYWppYiBhbmQgT2xnYSBhc2tlZCBhYm91dCBwdWxsaW5nIHRoZSA5IGdlbmUgSURzIHdoaWNoIGFyZSBpbiB0aGUKcGVjdWxpYXIgc2l0dWF0aW9uIG9mIGluY3JlYXNlZCBleHByZXNzaW9uIGluIHoyLjIvdW5pbmYgYW5kIGRlY3JlYXNlZAppbiB6Mi4zL3VuaW5mLiAgSW4gdGhlIHByZXZpb3VzIHVwc2V0IHBsb3QsIHRoZXNlIGFyZSB2aXNpYmxlIGluIHRoZQo2dGggYmFyLiAgSSBjYW4gYWNjZXNzIHRoZXNlIHZpYSB0aGUgYXR0cigpIGZ1bmN0aW9uLCB3aGljaCBJIHNob3VsZAphZG1pdCBJIGNhbiBuZXZlciByZW1lbWJlciBob3cgdG8gdXNlLCBzbyBJIGFtIGdvaW5nIHRvIHVzZSB0aGUgY29kZQp1bmRlciB0aGUgJ0NvbXBhcmUobm8pU2IgejIuMy96Mi4yIHRyZWF0bWVudCcgaGVhZGluZyB0byByZW1lbWJlciBob3cKdG8gZXh0cmFjdCB0aGVzZSBnZW5lcy4KCmBgYHtyfQphbGxfZ3JvdXBzIDwtIG5vZHJ1Z191cHNldFtbImdyb3VwcyJdXQp3YW50ZWRfZ3JvdXAgPC0gInoyM25vc2JfdnNfdW5pbmZfZG93bjp6MjJub3NiX3ZzX3VuaW5mX3VwIgpnZW5lX2lkeCA8LSBhbGxfZ3JvdXBzW1t3YW50ZWRfZ3JvdXBdXQp3YW50ZWRfZ2VuZXMgPC0gYXR0cihhbGxfZ3JvdXBzLCAiZWxlbWVudHMiKVtnZW5lX2lkeF0Kd2FudGVkX2dlbmVzCmdlbmVfc3ltYm9sX2lkeCA8LSByb3duYW1lcyhmRGF0YShoc19tYWNyKSkgJWluJSBhcy5jaGFyYWN0ZXIod2FudGVkX2dlbmVzKQpmRGF0YShoc19tYWNyKVtnZW5lX3N5bWJvbF9pZHgsICJoZ25jX3N5bWJvbCJdCmBgYAoKKiBBQkNCNTogQVRCIEJpbmRpbmcgQ2Fzc2V0dGUgU3ViZmFtaWx5IEIgTWVtYmVyICM1LCB3aWRlIHJhbmdlIG9mCiAgZnVuY3Rpb25zIGluIHRoaXMgZGl2ZXJzZSBwYXJhbG9nb3VzIGZhbWlseS4gIEFzc29jaWF0ZWQgd2l0aCBza2luCiAgZGlzZWFzZXMgKG1lbGFub21hIGFuZCBFcGlkZXJtb2x5c2lzIEJ1bGxvc2E7IHBhcnRpY2lwYXRlIGluCiAgQVRQLWRlcGVuZGVudCB0cmFuc21lbWJyYW5lIHRyYW5zcG9ydCkuCiogUkZYNDogUmVndWxhdG9yeSBGYWN0b3IgWCAjNDogdHJhbnNjcmlwdGlvbiBmYWN0b3IuCiogQ0ExNDogQ2FyYm9uaWMgYW5oeWRyYXNlICMxNDogWnluYyBtZXRhbGxvZW56eW1lIGNhdGFseXplcwogIHJldmVyc2libGUgaHlkcmF0aW9uIG9mIENPMi4gIFRoaXMgZ2VuZSBsb29rcyBwcmV0dHkgbmVhdCwgYnV0IG5vdAogIHJlYWxseSByZWxldmFudCB0byBhbnl0aGluZyB3ZSBhcmUgbGlrZWx5IHRvIGNhcmUgYWJvdXQuCiogRUdSMTogRWFybHkgR3Jvd3RoIFJlc3BvbnNlIFByb3RlaW4gIzE6IEFub3RoZXIgVHggZmFjdG9yCiAgKHppbmMtZmluZ2VyKSAtLSBpbXBvcnRhbnQgZm9yIGNlbGwgc3Vydml2YWwvcHJvbGlmZXJhdGlvbi9jZWxsCiAgZGVhdGguICBQcmVzdW1hYmx5IGltcG9ydGFudCBmb3IgaGVhbGluZz8KKiBNQ0YyTDogTUNGLjIgQ2VsbCBMaW5lIERlcml2ZWQgVHJhbnNmb3JtaW5nIFNlcXVlbmNlIExpa2U/ICBndWFuaW5lCiAgbnVjbGVvdGlkZSBleGNoYW5nZSBmYWN0b3IgaW50ZXJhY3Rpbmcgd2l0aCBHVFAtYm91bmQgUmFjMS4KICBBcHBhcmVudGx5IGFzc29jaWF0ZWQgd2l0aCBvc3Ryb2FydGhyaXRpczsgcG90ZW50aWFsbHkgcmVsZXZhbnQgdG8KICByZWd1bGF0aW9uIG9mIFJIT0EgYW5kIENEQzQyIHNpZ25hbGxpbmcuCiogRE5BU0UxTDM6IERlb3h5cmlib251Y2xlYXNlIEkgZmFtaWx5IG1lbWJlcjogbm90IGluaGliaXRlZCBieSBhY3RpbiwKICBicmVha3MgZG93biBETkEgZHVyaW5nIGFwb3B0b3Npcy4gIEltcG9ydGFudCBkdXJpbmcgbmVjcm9zaXMuCiogRk9TOiBQcm90by1PbmNvZ2VuZSwgQVAtMSBUcmFuc2NyaXB0aW9uIEZhY3RvcjogbGV1Y2luZSB6aXBwZXIKICBkaW1lcml6ZXMgd2l0aCBKVU4gZmFtaWx5IHByb3RlaW5zLCBmb3JtaW5nIHR4IGZhY3RvciBjb21wbGV4IEFQLTEuCiAgSW1wb3J0YW50IGZvciBjZWxsIHByb2xpZmVyYXRpb24sIGRpZmZlcmVudGlhdGlvbiwgYW5kCiAgdHJhbnNmb3JtYXRpb24uCiogSUZJVE0xMDogSW50ZXJmZXJvbi1JbmR1Y2VkIFRyYW5zbWVtYnJhbmUgUHJvdGVpbiAjMTAKKiBQS0QxTDM6IFBvbHljeXN0aW4gMSBMaWtlICMzLCBUcmFuc2llbnQgUmVjZXB0b3IgUG90ZW50aWFsIENoYW5uZWwKICBJbnRlcmFjdGluZzogMTEgdHJhbnNtZW1icmFuZSBkb21haW4gcHJvdGVpbiB3aGljaCBtaWdodCBoZWxwIGNyZWF0ZQogIGNhdGlvbiBjaGFubmVscy4KCkFzIHNvbWUgY29tcGFyaXNvbiBwb2ludHMsIHRoZSBWZW5uIGluIHRoZSBjdXJyZW50IGZpZ3VyZSBoYXM6CgoqIDM4NyB1cCB6Mi4zCiogMjU5IHVwIHoyLjIKKiA4MyBzaGFyZWQgdXAgejIuMyBhbmQgejIuMgoqIDI0NyBkb3duIHoyLjMKKiAzIGRvd24gejIuMgoqIDMgc2hhcmVkIGRvd24gejIuMyBhbmQgejIuMgoKIyMjIDIuMiBhbmQgMi4zIHdpdGggU2JWIHZzIDIuMiBhbmQgMi4zIHdpdGhvdXQgU2JWCgpUaGlzIGlzIG15IHZlcnNpb24gb2YgdGhlIFZlbm4gd2l0aCB0aGUgdGV4dDoKCiJEaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgaW4gbWFjcm9waGFnZXMgaW5mZWN0ZWQgd2l0aApzdWJwb3B1bGF0aW9ucyAyLjIgb3IgMi4zLCBpbiBwcmVzZW5jZSBvZiBTYlYuIFZvbGNhbm8gcGxvdHMgY29udHJhc3QKb2Y6IEEuIGluZmVjdGVkIGNlbGxzIHdpdGggMi4zIHN0cmFpbnMgKyBTYlYgYW5kIGluZmVjdGVkIGNlbGxzIHdpdGgKMi4zIHN0cmFpbnM7IEIuIGluZmVjdGVkIGNlbGxzIHdpdGggMi4yIHN0cmFpbnMgKyBTYlYgYW5kIGluZmVjdGVkCmNlbGxzIHdpdGggMi4yIHN0cmFpbnM7IEMuIGluZmVjdGVkIGNlbGxzIHdpdGggMi4zIHN0cmFpbnMgKyBTYlYgYW5kCmluZmVjdGVkIGNlbGxzIHdpdGggMi4yIHN0cmFpbnMgKyBTYlYuIEQuIFZlbm4gZGlhZ3JhbSBmb3IgdXByZWd1bGF0ZWQKYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMgYnkgaW5mZWN0aW9uIHdpdGggMi4zK1NiViBhbmQgMi4yK1NiVgpzdHJhaW5zLiIKCkEgcXVlcnkgZnJvbSBPbGdhICgyMDI0MDgwMSk6IFBsZWFzZSBpbmNsdWRlIGluIHRoZSB1cHNldCBpbiBmaWd1cmUgMwp0aGUgY29udHJhc3Qgb2YgdW5pbmZlY3RlZCBjZWxscyArIFNiViB2cyB1bmluZmVjdGVkIHdpdGhvdXQgU2JWLgoKYGBge3J9CiMjIEkga2VlcCBtaXMtaW50ZXJwcmV0aW5nIHRoaXMgdGV4dCwgaXQgaXMgejIuMy96Mi4zU2JWIGFuZCB6Mi4yL3oyLjJTYlYKZHJ1Z25vZHJ1Z191cHNldCA8LSB1cHNldHJfY29tYmluZWRfZGUoaHNfbWFjcl90YWJsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaXJlZF9jb250cmFzdHMgPSBjKCJ6MjNzYl92c196MjNub3NiIiwgInoyMnNiX3ZzX3oyMm5vc2IiKSkKcHAoZmlsZSA9ICJpbWFnZXMvZHJ1Z25vZHJ1Z191cHNldC5wZGYiKQpkcnVnbm9kcnVnX3Vwc2V0W1sicGxvdCJdXQpkZXYub2ZmKCkKZHJ1Z25vZHJ1Z191cHNldAoKZHJ1Z25vZHJ1Z191bmluZl9jb250cmFzdHMgPC0gYygiejIzc2JfdnNfejIzbm9zYiIsICJ6MjJzYl92c196MjJub3NiIiwgInNiX3ZzX3VuaW5mIikKZHJ1Z25vZHJ1Z191cHNldF93aXRoX3VuaW5mIDwtIHVwc2V0cl9jb21iaW5lZF9kZShoc19tYWNyX3RhYmxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpcmVkX2NvbnRyYXN0cyA9IGRydWdub2RydWdfdW5pbmZfY29udHJhc3RzKQpwcChmaWxlID0gImZpZ3VyZXMvZHJ1Z25vZHJ1Z193aXRoX3VuaW5mX3Vwc2V0LnN2ZyIpCmRydWdub2RydWdfdXBzZXRfd2l0aF91bmluZltbInBsb3QiXV0KZGV2Lm9mZigpCmRydWdub2RydWdfdXBzZXRfd2l0aF91bmluZgpgYGAKCkZvciBzb21lIGNvbXBhcmlzb24gcG9pbnRzLCB0aGUgdmVubiBpbWFnZSBoYXM6CgoqIDIyMiB1cCB6Mi4zIFNiVgoqIDEzNCB1cCB6Mi4yIFNiVgoqIDE4MiBkb3duIHoyLjMgU2JWCiogMzk2IGRvd24gejIuMiBTYlYKKiA2MDUgc2hhcmVkIGRvd24gejIuMiBhbmQgejIuMyBTYlYKKiAzNCBzaGFyZWQgZG93biB6Mi4yIFNiViBhbmQgdXAgejIuMyBTYlYKKiAzNjMgc2hhcmVkIHVwIHoyLjIgU2JWIGFuZCB6Mi4zIFNiVgoKIyMjIENvbXBhcmUgejIuMlNiViB2cyBTYlYgYW5kIHoyLjNTYlYgYW5kIFNiVgoKYGBge3J9CmRydWdfdXBzZXQgPC0gdXBzZXRyX2NvbWJpbmVkX2RlKGhzX21hY3JfdGFibGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lyZWRfY29udHJhc3RzID0gYygiejIyc2JfdnNfc2IiLCAiejIzc2JfdnNfc2IiKSkKcHAoZmlsZSA9ICJpbWFnZXMvZHJ1Z191cHNldC5wZGYiKQpkcnVnX3Vwc2V0W1sicGxvdCJdXQpkZXYub2ZmKCkKZHJ1Z191cHNldApgYGAKCiMjIFNpZ25pZmljYW5jZSBiYXJwbG90IG9mIGludGVyZXN0CgpPbGdhIGtpbmRseSBzZW50IGEgc2V0IG9mIHBhcnRpY3VsYXJseSBpbnRlcmVzdGluZyBjb250cmFzdHMgYW5kCmNvbG9ycyBmb3IgYSBzaWduaWZpY2FuY2UgYmFycGxvdCwgdGhleSBpbmNsdWRlIHRoZSBmb2xsb3dpbmc6CgoqIHoyLjMgdnMuIHVuaW5mZWN0ZWQuCiogejIuMiB2cy4gdW5pbmZlY3RlZC4KKiB6Mi4zIHZzIHoyLjIKKiB6Mi4zU2J2IHZzIHoyLjMKKiB6Mi4yU2J2IHZzIHoyLjIKKiB6Mi4zU2J2IHZzIHoyLjJTYnYKKiBTYnYgdnMgdW5pbmZlY3RlZC4KClRoZSBleGlzdGluZyBzZXQgb2YgJ2tlZXBlcnMnIGV4dmlzZWQgdG8gdGhlc2UgaXMgdGFrZW4gZnJvbSB0aGUKZXh0YW50IHNldCBvZiAndG1yYzJfaHVtYW5fa2VlcGVycycgYW5kIGlzIGFzIGZvbGxvd3M6CgoKYGBge3J9CmJhcnBsb3Rfa2VlcGVycyA8LSBsaXN0KAogICMjIHoyLjMgdnMgdW5pbmZlY3RlZAogICJ6MjNub3NiX3ZzX3VuaW5mIiA9IGMoImluZnoyMyIsICJ1bmluZm5vbmUiKSwKICAjIyB6Mi4yIHZzIHVuaW5mZWN0ZWQKICAiejIybm9zYl92c191bmluZiIgPSBjKCJpbmZ6MjIiLCAidW5pbmZub25lIiksCiAgIyMgejIuMyB2cyB6Mi4yCiAgInoyM25vc2JfdnNfejIybm9zYiIgPSBjKCJpbmZ6MjMiLCAiaW5mejIyIiksCiAgIyMgejIuM1NidiB2cyB6Mi4zCiAgInoyM3NiX3ZzX3oyM25vc2IiID0gYygiaW5mc2J6MjMiLCAiaW5mejIzIiksCiAgIyMgejIuMlNidiB2cyB6Mi4yCiAgInoyMnNiX3ZzX3oyMm5vc2IiID0gYygiaW5mc2J6MjIiLCAiaW5mejIyIiksCiAgIyMgejIuM1NidiB2cyB6Mi4yU2J2CiAgInoyM3NiX3ZzX3oyMnNiIiA9IGMoImluZnNiejIzIiwgImluZnNiejIyIiksCiAgIyMgU2J2IHZzIHVuaW5mZWN0ZWQuCiAgInNiX3ZzX3VuaW5mIiA9IGMoInVuaW5mc2Jub25lIiwgInVuaW5mbm9uZSIpKQpiYXJwbG90X2NvbWJpbmVkIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIGhzX21hY3JfZGUsIGtlZXBlcnMgPSBiYXJwbG90X2tlZXBlcnMsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2RlX3RhYmxlcy9oc19tYWNyX2RydWdfenltb183Y29udHJhc3RzLXZ7dmVyfS54bHN4IikpCmBgYAoKTm93IGxldCB1cyB1c2UgdGhlIGNvbG9ycyBzdWdnZXN0ZWQgYnkgT2xnYSB0byBtYWtlIGEgYmFycGxvdCBvZgp0aGVzZS4uLgoKYGBge3J9CmNvbG9yX2xpc3QgPC0gIGMoICIjZGU4YmY5IiwgIiNhZDA3ZTMiLCIjNDEwMjU3IiwgIiNmZmEwYTAiLCAiI2Y5NDA0MCIsICIjYTAwMDAwIikKYmFycGxvdF9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBiYXJwbG90X2NvbWJpbmVkLCBjb2xvcl9saXN0ID0gY29sb3JfbGlzdCwgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9oc19tYWNyX2RydWdfenltb183Y29udHJhc3RzX3NpZy12e3Zlcn0ueGxzeCIpKQpiYXJwbG90X3NpZwpgYGAKCiMgUFJPUEVSCgpJbiBvdXIgbGFzdCBtZWV0aW5nIHRoZXJlIHdlcmUgc29tZSBxdWVzdGlvbnMgYWJvdXQgdGhlIHN0YXRpc3RpY2FsCnBvd2VyIG9mIGRpZmZlcmVudCBmdXR1cmUgZXhwZXJpbWVudGFsIGRlc2lnbnMuICBPbmUgdGhpbmcgSSBjYW4gZG8gaXMKdG8gdXNlIFBST1BFUiB0byBlc3RpbWF0ZSB0aGUgcG93ZXIgb2YgYW4gZXh0YW50IGRhdGFzZXQgYW5kIGluZmVyCmZyb20gdGhhdCB0aGUgbGlrZWx5IHBvd2VyIG9mIG90aGVyIGRlc2lnbnMuCgpJbiBvcmRlciB0byB1c2UgcHJvcGVyLCBvbmUgbXVzdCBmZWVkIGl0IG9uZSBvciBtb3JlIERFIHRhYmxlcy4KCmBgYHtyfQpwb3dlcl9lc3RpbWF0ZSA8LSBzaW1wbGVfcHJvcGVyKGhzX3NpbmdsZV90YWJsZSkKCnBvd2VyX2VzdGltYXRlW1sxXV1bWyJwb3dlcl9wbG90Il1dCnBvd2VyX2VzdGltYXRlW1sxXV1bWyJwb3dlcnRkX3Bsb3QiXV0KcG93ZXJfZXN0aW1hdGVbWzFdXVtbInBvd2VyZmRfcGxvdCJdXQpgYGAKCiMgT3VyIG1haW4gcXVlc3Rpb25zIGluIFU5MzcKCkxldCB1cyBkbyB0aGUgc2FtZSBjb21wYXJpc29ucyBpbiB0aGUgVTkzNyBzYW1wbGVzLCB0aG91Z2ggSSB3aWxsIG5vdApkbyB0aGUgZXh0cmEgY29udHJhc3RzLCBwcmltYXJpbHkgYmVjYXVzZSBJIHRoaW5rIHRoZSBkYXRhc2V0IGlzIGxlc3MKbGlrZWx5IHRvIHN1cHBvcnQgdGhlbS4KCmBgYHtyfQp1OTM3X2RlIDwtIGFsbF9wYWlyd2lzZSh1OTM3X2V4cHQsIG1vZGVsX3N2cyA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBkb19ub2lzZXEgPSBGQUxTRSkKdTkzN19kZQp1OTM3X3RhYmxlIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHU5MzdfZGUsIGtlZXBlcnMgPSB1OTM3X2tlZXBlcnMsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2RlX3RhYmxlcy91OTM3X2RydWdfenltb190YWJsZS12e3Zlcn0ueGxzeCIpKQp1OTM3X3RhYmxlCmNvbWJpbmVkX3RvX3Rzdih1OTM3X3RhYmxlLCBjZWxsdHlwZSA9ICJ1OTM3IikKCnU5Mzdfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdTkzN190YWJsZSwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy91OTM3X2RydWdfenltb19zaWctdnt2ZXJ9Lnhsc3giKSkKdTkzN19zaWcKdTkzN19oaWdoc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdTkzN190YWJsZSwgbWluX21lYW5fZXhwcnMgPSBoaWdoX2V4cHJlc3Npb24sIGV4cHJzX2NvbHVtbiA9IGhpZ2hfZXhwcmVzc2lvbl9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvdTkzN19kcnVnX3p5bW9faGlnaHNpZy12e3Zlcn0ueGxzeCIpKQp1OTM3X2hpZ2hzaWcKdTkzN19sZXNzc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdTkzN190YWJsZSwgbGZjID0gMC42LAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9zaWdfdGFibGVzL3U5MzdfZHJ1Z196eW1vX2xlc3NzaWctdnt2ZXJ9Lnhsc3giKSkKdTkzN19sZXNzc2lnCmBgYAoKIyBDb21wYXJlIChubylTYiB6Mi4zL3oyLjIgdHJlYXRtZW50cyBhbW9uZyBtYWNyb3BoYWdlcwoKSW4gdGhlIGZvbGxvd2luZyBibG9jaywgSSB3aWxsIGp1bXAgYmFjayB0byB0aGUgbWFjcm9waGFnZSBzYW1wbGVzIGFuZApsb29rIGZvciBnZW5lcyB3aGljaCBhcmUgc2hhcmVkL3VuaXF1ZSB3aGVuIGNvbXBhcmluZyB6Mi4zL3oyLjIKZm9yIHRoZSBkcnVnIHRyZWF0ZWQgc2FtcGxlcyBhbmQgdGhlIHVudHJlYXRlZCBzYW1wbGVzLgoKYGBge3J9CnVwc2V0X3Bsb3RzX2hzX21hY3IgPC0gdXBzZXRyX3NpZygKICBoc19tYWNyX3NpZywgYm90aCA9IFRSVUUsCiAgY29udHJhc3RzID0gYygiejIzc2JfdnNfejIyc2IiLCAiejIzbm9zYl92c196MjJub3NiIikpCnVwc2V0X3Bsb3RzX2hzX21hY3JbWyJib3RoIl1dCmdyb3VwcyA8LSB1cHNldF9wbG90c19oc19tYWNyW1siYm90aF9ncm91cHMiXV0Kc2hhcmVkX2dlbmVzIDwtIGF0dHIoZ3JvdXBzLCAiZWxlbWVudHMiKVtncm91cHNbWzJdXV0gJT4lCiAgZ3N1YihwYXR0ZXJuID0gIl5nZW5lOiIsIHJlcGxhY2VtZW50ID0gIiIpCmxlbmd0aChzaGFyZWRfZ2VuZXMpCgpzaGFyZWRfZ3AgPC0gc2ltcGxlX2dwcm9maWxlcihzaGFyZWRfZ2VuZXMpCnNoYXJlZF9ncFtbInB2YWx1ZV9wbG90cyJdXVtbIk1GIl1dCnNoYXJlZF9ncFtbInB2YWx1ZV9wbG90cyJdXVtbIkJQIl1dCnNoYXJlZF9ncFtbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0KCmRydWdfZ2VuZXMgPC0gYXR0cihncm91cHMsICJlbGVtZW50cyIpW2dyb3Vwc1tbInoyM3NiX3ZzX3oyMnNiIl1dXSAlPiUKICBnc3ViKHBhdHRlcm4gPSAiXmdlbmU6IiwgcmVwbGFjZW1lbnQgPSAiIikKZHJ1Z29ubHlfZ3AgPC0gc2ltcGxlX2dwcm9maWxlcihkcnVnX2dlbmVzKQpkcnVnb25seV9ncFtbInB2YWx1ZV9wbG90cyJdXVtbIkJQIl1dCmBgYAoKSSB3YW50IHRvIHRyeSBzb21ldGhpbmcsIGRpcmVjdGx5IGluY2x1ZGUgdGhlIHU5MzcgZGF0YSBpbiB0aGlzLgpUaHVzLCBpbiB0aGUgZm9sbG93aW5nIGJsb2NrIEkgd2lsbCByZXBlYXQgYnV0IGNvbXBhcmUgYWxsIHNhbXBsZXMgYW5kCnRoZSBVOTM3IHVzaW5nIHRoZSBzYW1lIGxvZ2ljLgoKYGBge3J9CmJvdGhfc2lnIDwtIGhzX21hY3Jfc2lnCm5hbWVzKGJvdGhfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV0pIDwtIHBhc3RlMCgibWFjcl8iLCBuYW1lcyhib3RoX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dKSkKbmFtZXMoYm90aF9zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dKSA8LSBwYXN0ZTAoIm1hY3JfIiwgbmFtZXMoYm90aF9zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dKSkKdTkzN19kZXNlcSA8LSB1OTM3X3NpZ1tbImRlc2VxIl1dCm5hbWVzKHU5MzdfZGVzZXFbWyJ1cHMiXV0pIDwtIHBhc3RlMCgidTkzN18iLCBuYW1lcyh1OTM3X2Rlc2VxW1sidXBzIl1dKSkKbmFtZXModTkzN19kZXNlcVtbImRvd25zIl1dKSA8LSBwYXN0ZTAoInU5MzdfIiwgbmFtZXModTkzN19kZXNlcVtbImRvd25zIl1dKSkKYm90aF9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXSA8LSBjKGJvdGhfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV0sIHU5MzdfZGVzZXFbWyJ1cHMiXV0pCmJvdGhfc2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXSA8LSBjKGJvdGhfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV0sIHU5MzdfZGVzZXFbWyJkb3ducyJdXSkKc3VtbWFyeShib3RoX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dKQoKdXBzZXRfcGxvdHNfYm90aCA8LSB1cHNldHJfc2lnKAogIGJvdGhfc2lnLCBib3RoID0gVFJVRSwKICBjb250cmFzdHMgPSBjKCJtYWNyX3oyM3NiX3ZzX3oyMnNiIiwgIm1hY3JfejIzbm9zYl92c196MjJub3NiIiwKICAgICAgICAgICAgICAgICJ1OTM3X3oyM3NiX3ZzX3oyMnNiIiwgInU5MzdfejIzbm9zYl92c196MjJub3NiIikpCnVwc2V0X3Bsb3RzX2JvdGhbWyJib3RoIl1dCmBgYAoKIyMgQ29tcGFyZSBERSByZXN1bHRzIGZyb20gbWFjcm9waGFnZXMgYW5kIFU5Mzcgc2FtcGxlcwoKTG9va2luZyBhIGJpdCBtb3JlIGNsb3NlbHkgYXQgdGhlc2UsIEkgdGhpbmsgdGhlIHU5MzcgZGF0YSBpcyB0b28Kc3BhcnNlIHRvIGVmZmVjdGl2ZWx5IGNvbXBhcmUuCgpgYGB7cn0KbWFjcl91OTM3X2NvbXBhcmlzb24gPC0gY29tcGFyZV9kZV9yZXN1bHRzKGhzX21hY3JfdGFibGUsIHU5MzdfdGFibGUpCm1hY3JfdTkzN19jb21wYXJpc29uW1sibGZjX2hlYXQiXV0KCm1hY3JfdTkzN192ZW5ucyA8LSBjb21wYXJlX3NpZ25pZmljYW50X2NvbnRyYXN0cyhoc19tYWNyX3NpZywgc2Vjb25kX3NpZ190YWJsZXMgPSB1OTM3X3NpZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0cyA9ICJ6MjNzYl92c196MjNub3NiIikKbWFjcl91OTM3X3Zlbm5zW1sidXBfcGxvdCJdXQptYWNyX3U5MzdfdmVubnNbWyJkb3duX3Bsb3QiXV0KCm1hY3JfdTkzN192ZW5uc192MiA8LSBjb21wYXJlX3NpZ25pZmljYW50X2NvbnRyYXN0cygKICBoc19tYWNyX3NpZywgc2Vjb25kX3NpZ190YWJsZXMgPSB1OTM3X3NpZywgY29udHJhc3RzID0gInoyMnNiX3ZzX3oyMm5vc2IiKQptYWNyX3U5MzdfdmVubnNfdjJbWyJ1cF9wbG90Il1dCm1hY3JfdTkzN192ZW5uc192MltbImRvd25fcGxvdCJdXQoKbWFjcl91OTM3X3Zlbm5zX3YzIDwtIGNvbXBhcmVfc2lnbmlmaWNhbnRfY29udHJhc3RzKAogIGhzX21hY3Jfc2lnLCBzZWNvbmRfc2lnX3RhYmxlcyA9IHU5Mzdfc2lnLCBjb250cmFzdHMgPSAic2JfdnNfdW5pbmYiKQptYWNyX3U5MzdfdmVubnNfdjNbWyJ1cF9wbG90Il1dCm1hY3JfdTkzN192ZW5uc192M1tbImRvd25fcGxvdCJdXQpgYGAKCiMjIENvbXBhcmUgbWFjcm9waGFnZS91OTM3IHdpdGggcmVzcGVjdCB0byB6Mi4zL3oyLjIKCmBgYHtyfQpjb21wYXJpc29uX2RmIDwtIG1lcmdlKGhzX21hY3JfdGFibGVbWyJkYXRhIl1dW1siejIzc2JfdnNfejIyc2IiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgdTkzN190YWJsZVtbImRhdGEiXV1bWyJ6MjNzYl92c196MjJzYiJdXSwKICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJyb3cubmFtZXMiKQptYWNydTkzN196MjN6MjJfcGxvdCA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKGNvbXBhcmlzb25fZGZbLCBjKCJkZXNlcV9sb2dmYy54IiwgImRlc2VxX2xvZ2ZjLnkiKV0pCm1hY3J1OTM3X3oyM3oyMl9wbG90W1sic2NhdHRlciJdXQoKY29tcGFyaXNvbl9kZiA8LSBtZXJnZShoc19tYWNyX3RhYmxlW1siZGF0YSJdXVtbInoyM25vc2JfdnNfejIybm9zYiJdXSwKICAgICAgICAgICAgICAgICAgICAgICB1OTM3X3RhYmxlW1siZGF0YSJdXVtbInoyM25vc2JfdnNfejIybm9zYiJdXSwKICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJyb3cubmFtZXMiKQptYWNydTkzN196MjN6MjJfcGxvdCA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKGNvbXBhcmlzb25fZGZbLCBjKCJkZXNlcV9sb2dmYy54IiwgImRlc2VxX2xvZ2ZjLnkiKV0pCm1hY3J1OTM3X3oyM3oyMl9wbG90W1sic2NhdHRlciJdXQpgYGAKCiMgQWRkIGRvbm9yIHRvIHRoZSBjb250cmFzdHMsIG5vIHN2YQoKSW4gdGhlIGZvbGxvd2luZyBibG9jaywgSSB3aWxsIGNoYW5nZSB0aGUgc2FtcGxlIGNvbmRpdGlvbiB0byBpbmNsdWRlCnRoZSBkb25vci4KCmBgYHtyfQpub19wb3dlcl9mYWN0IDwtIHBhc3RlMChwRGF0YShoc19tYWNyKVtbImRvbm9yIl1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHBEYXRhKGhzX21hY3IpW1siY29uZGl0aW9uIl1dKQp0YWJsZShwRGF0YShoc19tYWNyKVtbImRvbm9yIl1dKQp0YWJsZShub19wb3dlcl9mYWN0KQpoc19ub3Bvd2VyIDwtIHNldF9leHB0X2NvbmRpdGlvbnMoaHNfbWFjciwgZmFjdCA9IG5vX3Bvd2VyX2ZhY3QpCmhzX25vcG93ZXIgPC0gc3Vic2V0X3NlKGhzX25vcG93ZXIsIHN1YnNldCA9ICJtYWNyb3BoYWdlenltb2RlbWUhPSdub25lJyIpCmhzX25vcG93ZXJfbm9zdmFfZGUgPC0gYWxsX3BhaXJ3aXNlKGhzX25vcG93ZXIsIG1vZGVsX3N2cyA9IEZBTFNFLCBmaWx0ZXIgPSBUUlVFKQpub3Bvd2VyX2tlZXBlcnMgPC0gbGlzdCgKICAiZDAxX3p5bW8iID0gYygiZDAxaW5mejIzIiwgImQwMWluZnoyMiIpLAogICJkMDFfc2J6eW1vIiA9IGMoImQwMWluZnNiejIzIiwgImQwMWluZnNiejIyIiksCiAgImQwMl96eW1vIiA9IGMoImQwMmluZnoyMyIsICJkMDJpbmZ6MjIiKSwKICAiZDAyX3NienltbyIgPSBjKCJkMDJpbmZzYnoyMyIsICJkMDJpbmZzYnoyMiIpLAogICJkMDlfenltbyIgPSBjKCJkMDlpbmZ6MjMiLCAiZDA5aW5mejIyIiksCiAgImQwOV9zYnp5bW8iID0gYygiZDA5aW5mc2J6MjMiLCAiZDA5aW5mc2J6MjIiKSwKICAiZDgxX3p5bW8iID0gYygiZDgxaW5mejIzIiwgImQ4MWluZnoyMiIpLAogICJkODFfc2J6eW1vIiA9IGMoImQ4MWluZnNiejIzIiwgImQ4MWluZnNiejIyIikpCmhzX25vcG93ZXJfbm9zdmFfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgaHNfbm9wb3dlcl9ub3N2YV9kZSwga2VlcGVycyA9IG5vcG93ZXJfa2VlcGVycywKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvZGVfdGFibGVzL2hzX25vcG93ZXJfdGFibGUtdnt2ZXJ9Lnhsc3giKSkKIyMgZXh0cmFfY29udHJhc3RzID0gZXh0cmEpCmhzX25vcG93ZXJfbm9zdmFfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgaHNfbm9wb3dlcl9ub3N2YV90YWJsZSwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9oc19ub3Bvd2VyX25vc3ZhX3NpZy12e3Zlcn0ueGxzeCIpKQoKZDAxZDAyX3p5bW9fbm9zdmFfY29tcCA8LSBtZXJnZShoc19ub3Bvd2VyX25vc3ZhX3RhYmxlW1siZGF0YSJdXVtbImQwMV96eW1vIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhzX25vcG93ZXJfbm9zdmFfdGFibGVbWyJkYXRhIl1dW1siZDAyX3p5bW8iXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAicm93Lm5hbWVzIikKZDAxMDJfenltb19ub3N2YV9wbG90IDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIoZDAxZDAyX3p5bW9fbm9zdmFfY29tcFssIGMoImRlc2VxX2xvZ2ZjLngiLCAiZGVzZXFfbG9nZmMueSIpXSkKZDAxMDJfenltb19ub3N2YV9wbG90W1sic2NhdHRlciJdXQpkMDEwMl96eW1vX25vc3ZhX3Bsb3RbWyJjb3JyZWxhdGlvbiJdXQpkMDEwMl96eW1vX25vc3ZhX3Bsb3RbWyJsbV9yc3EiXV0KCmQwOWQ4MV96eW1vX25vc3ZhX2NvbXAgPC0gbWVyZ2UoaHNfbm9wb3dlcl9ub3N2YV90YWJsZVtbImRhdGEiXV1bWyJkMDlfenltbyJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoc19ub3Bvd2VyX25vc3ZhX3RhYmxlW1siZGF0YSJdXVtbImQ4MV96eW1vIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInJvdy5uYW1lcyIpCmQwOTgxX3p5bW9fbm9zdmFfcGxvdCA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKGQwOWQ4MV96eW1vX25vc3ZhX2NvbXBbLCBjKCJkZXNlcV9sb2dmYy54IiwgImRlc2VxX2xvZ2ZjLnkiKV0pCmQwOTgxX3p5bW9fbm9zdmFfcGxvdFtbInNjYXR0ZXIiXV0KZDA5ODFfenltb19ub3N2YV9wbG90W1siY29ycmVsYXRpb24iXV0KZDA5ODFfenltb19ub3N2YV9wbG90W1sibG1fcnNxIl1dCgpkMDFkODFfenltb19ub3N2YV9jb21wIDwtIG1lcmdlKGhzX25vcG93ZXJfbm9zdmFfdGFibGVbWyJkYXRhIl1dW1siZDAxX3p5bW8iXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHNfbm9wb3dlcl9ub3N2YV90YWJsZVtbImRhdGEiXV1bWyJkODFfenltbyJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJyb3cubmFtZXMiKQpkMDE4MV96eW1vX25vc3ZhX3Bsb3QgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihkMDFkODFfenltb19ub3N2YV9jb21wWywgYygiZGVzZXFfbG9nZmMueCIsICJkZXNlcV9sb2dmYy55IildKQpkMDE4MV96eW1vX25vc3ZhX3Bsb3RbWyJzY2F0dGVyIl1dCmQwMTgxX3p5bW9fbm9zdmFfcGxvdFtbImNvcnJlbGF0aW9uIl1dCmQwMTgxX3p5bW9fbm9zdmFfcGxvdFtbImxtX3JzcSJdXQoKdXBzZXRfcGxvdHNfbm9zdmEgPC0gdXBzZXRyX3NpZyhoc19ub3Bvd2VyX25vc3ZhX3NpZywgYm90aCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJhc3RzID0gYygiZDAxX3p5bW8iLCAiZDAyX3p5bW8iLCAiZDA5X3p5bW8iLCAiZDgxX3p5bW8iKSkKdXBzZXRfcGxvdHNfbm9zdmFbWyJ1cCJdXQp1cHNldF9wbG90c19ub3N2YVtbImRvd24iXV0KdXBzZXRfcGxvdHNfbm9zdmFbWyJib3RoIl1dCiMjIFRoZSA3dGggZWxlbWVudCBpbiB0aGUgYm90aCBncm91cHMgbGlzdCBpcyB0aGUgc2V0IHNoYXJlZCBhbW9uZyBhbGwgZG9ub3JzLgojIyBJIGRvbid0IGZlZWwgbGlrZSB3cml0aW5nIG91dCB4Onk6ejphCmdyb3VwcyA8LSB1cHNldF9wbG90c19ub3N2YVtbImJvdGhfZ3JvdXBzIl1dCnNoYXJlZF9nZW5lcyA8LSBhdHRyKGdyb3VwcywgImVsZW1lbnRzIilbZ3JvdXBzW1s3XV1dICU+JQogIGdzdWIocGF0dGVybiA9ICJeZ2VuZToiLCByZXBsYWNlbWVudCA9ICIiKQpzaGFyZWRfZ3AgPC0gc2ltcGxlX2dwcm9maWxlcihzaGFyZWRfZ2VuZXMpCnNoYXJlZF9ncFtbInB2YWx1ZV9wbG90cyJdXVtbIk1GIl1dCnNoYXJlZF9ncFtbInB2YWx1ZV9wbG90cyJdXVtbIkJQIl1dCnNoYXJlZF9ncFtbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0Kc2hhcmVkX2dwW1sicHZhbHVlX3Bsb3RzIl1dW1siV1AiXV0KYGBgCgojIEFkZCBkb25vciB0byB0aGUgY29udHJhc3RzLCBzdmEKClNhbWUgZGVhbCBhcyB0aGUgbGFzdCBibG9jaywgYnV0IHRoaXMgdGltZSBhZGQgU1ZBIGludG8gdGhlIG1peCEKCmBgYHtyfQpoc19ub3Bvd2VyX3N2YV9kZSA8LSBhbGxfcGFpcndpc2UoaHNfbm9wb3dlciwgbW9kZWxfc3ZzID0gInN2YXNlcSIsIGZpbHRlciA9IFRSVUUpCm5vcG93ZXJfa2VlcGVycyA8LSBsaXN0KAogICJkMDFfenltbyIgPSBjKCJkMDFpbmZ6MjMiLCAiZDAxaW5mejIyIiksCiAgImQwMV9zYnp5bW8iID0gYygiZDAxaW5mc2J6MjMiLCAiZDAxaW5mc2J6MjIiKSwKICAiZDAyX3p5bW8iID0gYygiZDAyaW5mejIzIiwgImQwMmluZnoyMiIpLAogICJkMDJfc2J6eW1vIiA9IGMoImQwMmluZnNiejIzIiwgImQwMmluZnNiejIyIiksCiAgImQwOV96eW1vIiA9IGMoImQwOWluZnoyMyIsICJkMDlpbmZ6MjIiKSwKICAiZDA5X3NienltbyIgPSBjKCJkMDlpbmZzYnoyMyIsICJkMDlpbmZzYnoyMiIpLAogICJkODFfenltbyIgPSBjKCJkODFpbmZ6MjMiLCAiZDgxaW5mejIyIiksCiAgImQ4MV9zYnp5bW8iID0gYygiZDgxaW5mc2J6MjMiLCAiZDgxaW5mc2J6MjIiKSkKaHNfbm9wb3dlcl9zdmFfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgaHNfbm9wb3dlcl9zdmFfZGUsIGtlZXBlcnMgPSBub3Bvd2VyX2tlZXBlcnMsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2RlX3RhYmxlcy9oc19ub3Bvd2VyX3RhYmxlLXZ7dmVyfS54bHN4IikpCiMjIGV4dHJhX2NvbnRyYXN0cyA9IGV4dHJhKQpoc19ub3Bvd2VyX3N2YV9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBoc19ub3Bvd2VyX3N2YV90YWJsZSwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvc2lnX3RhYmxlcy9oc19ub3Bvd2VyX3N2YV9zaWctdnt2ZXJ9Lnhsc3giKSkKCmQwMWQwMl96eW1vX3N2YV9jb21wIDwtIG1lcmdlKGhzX25vcG93ZXJfc3ZhX3RhYmxlW1siZGF0YSJdXVtbImQwMV96eW1vIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoc19ub3Bvd2VyX3N2YV90YWJsZVtbImRhdGEiXV1bWyJkMDJfenltbyJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAicm93Lm5hbWVzIikKZDAxMDJfenltb19zdmFfcGxvdCA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKGQwMWQwMl96eW1vX3N2YV9jb21wWywgYygiZGVzZXFfbG9nZmMueCIsICJkZXNlcV9sb2dmYy55IildKQpkMDEwMl96eW1vX3N2YV9wbG90W1sic2NhdHRlciJdXQpkMDEwMl96eW1vX3N2YV9wbG90W1siY29ycmVsYXRpb24iXV0KZDAxMDJfenltb19zdmFfcGxvdFtbImxtX3JzcSJdXQoKZDA5ZDgxX3p5bW9fc3ZhX2NvbXAgPC0gbWVyZ2UoaHNfbm9wb3dlcl9zdmFfdGFibGVbWyJkYXRhIl1dW1siZDA5X3p5bW8iXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhzX25vcG93ZXJfc3ZhX3RhYmxlW1siZGF0YSJdXVtbImQ4MV96eW1vIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJyb3cubmFtZXMiKQpkMDk4MV96eW1vX3N2YV9wbG90IDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIoZDA5ZDgxX3p5bW9fc3ZhX2NvbXBbLCBjKCJkZXNlcV9sb2dmYy54IiwgImRlc2VxX2xvZ2ZjLnkiKV0pCmQwOTgxX3p5bW9fc3ZhX3Bsb3RbWyJzY2F0dGVyIl1dCmQwOTgxX3p5bW9fc3ZhX3Bsb3RbWyJjb3JyZWxhdGlvbiJdXQpkMDk4MV96eW1vX3N2YV9wbG90W1sibG1fcnNxIl1dCgpkMDFkODFfenltb19zdmFfY29tcCA8LSBtZXJnZShoc19ub3Bvd2VyX3N2YV90YWJsZVtbImRhdGEiXV1bWyJkMDFfenltbyJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHNfbm9wb3dlcl9zdmFfdGFibGVbWyJkYXRhIl1dW1siZDgxX3p5bW8iXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInJvdy5uYW1lcyIpCmQwMTgxX3p5bW9fc3ZhX3Bsb3QgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihkMDFkODFfenltb19zdmFfY29tcFssIGMoImRlc2VxX2xvZ2ZjLngiLCAiZGVzZXFfbG9nZmMueSIpXSkKZDAxODFfenltb19zdmFfcGxvdFtbInNjYXR0ZXIiXV0KZDAxODFfenltb19zdmFfcGxvdFtbImNvcnJlbGF0aW9uIl1dCmQwMTgxX3p5bW9fc3ZhX3Bsb3RbWyJsbV9yc3EiXV0KCnVwc2V0X3Bsb3RzX3N2YSA8LSB1cHNldHJfc2lnKGhzX25vcG93ZXJfc3ZhX3NpZywgYm90aCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0cyA9IGMoImQwMV96eW1vIiwgImQwMl96eW1vIiwgImQwOV96eW1vIiwgImQ4MV96eW1vIikpCnVwc2V0X3Bsb3RzX3N2YVtbInVwIl1dCnVwc2V0X3Bsb3RzX3N2YVtbImRvd24iXV0KdXBzZXRfcGxvdHNfc3ZhW1siYm90aCJdXQojIyBUaGUgN3RoIGVsZW1lbnQgaW4gdGhlIGJvdGggZ3JvdXBzIGxpc3QgaXMgdGhlIHNldCBzaGFyZWQgYW1vbmcgYWxsIGRvbm9ycy4KIyMgSSBkb24ndCBmZWVsIGxpa2Ugd3JpdGluZyBvdXQgeDp5Ono6YQpncm91cHMgPC0gdXBzZXRfcGxvdHNfc3ZhW1siYm90aF9ncm91cHMiXV0Kc2hhcmVkX2dlbmVzIDwtIGF0dHIoZ3JvdXBzLCAiZWxlbWVudHMiKVtncm91cHNbWzddXV0gJT4lCiAgZ3N1YihwYXR0ZXJuID0gIl5nZW5lOiIsIHJlcGxhY2VtZW50ID0gIiIpCnNoYXJlZF9ncCA8LSBzaW1wbGVfZ3Byb2ZpbGVyKHNoYXJlZF9nZW5lcykKc2hhcmVkX2dwW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0Kc2hhcmVkX2dwW1sicHZhbHVlX3Bsb3RzIl1dW1siQlAiXV0Kc2hhcmVkX2dwW1sicHZhbHVlX3Bsb3RzIl1dW1siUkVBQyJdXQpzaGFyZWRfZ3BbWyJwdmFsdWVfcGxvdHMiXV1bWyJXUCJdXQpgYGAKCiMgRG9ub3IgY29tcGFyaXNvbgoKTm93IGNvbXBhcmUgdGhlIGRvbm9ycyB0byBlYWNoIG90aGVyIGRpcmVjdGx5LgoKYGBge3J9CmhzX2Rvbm9ycyA8LSBzZXRfZXhwdF9jb25kaXRpb25zKGhzX21hY3IsIGZhY3QgPSAiZG9ub3IiKQpkb25vcl9kZSA8LSBhbGxfcGFpcndpc2UoaHNfZG9ub3JzLCBtb2RlbF9zdnMgPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSkKZG9ub3JfZGUKZG9ub3JfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgZG9ub3JfZGUsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2RlX3RhYmxlcy9kb25vcl90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKZG9ub3JfdGFibGUKZG9ub3Jfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgZG9ub3JfdGFibGUsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3NpZ190YWJsZXMvZG9ub3Jfc2lnLXZ7dmVyfS54bHN4IikpCmRvbm9yX3NpZwpgYGAKCiMgUHJpbWFyeSBxdWVyeSBjb250cmFzdHMKClRoZSBmaW5hbCBjb250cmFzdCBpbiB0aGlzIGxpc3QgaXMgaW50ZXJlc3RpbmcgYmVjYXVzZSBpdCBkZXBlbmRzIG9uCnRoZSBleHRyYSBjb250cmFzdHMgYXBwbGllZCB0byB0aGUgYWxsX3BhaXJ3aXNlKCkgYWJvdmUuICBJbiBteSB3YXkgb2YKdGhpbmtpbmcsIHRoZSBwcmltYXJ5IGNvbXBhcmlzb25zIHRvIGNvbnNpZGVyIGFyZSBlaXRoZXIgY3Jvc3MtZHJ1ZyBvcgpjcm9zcy1zdHJhaW4sIGJ1dCBub3QgYm90aC4gIEhvd2V2ZXIgSSB0aGluayBpbiBhdCBsZWFzdCBhIGZldwppbnN0YW5jZXMgT2xnYSBpcyBpbnRlcmVzdGVkIGluIHN0cmFpbitkcnVnIC8gdW5pbmZlY3RlZCtub2RydWcuCgojIyBXcml0ZSBjb250cmFzdCByZXN1bHRzCgpOb3cgbGV0IHVzIHdyaXRlIG91dCB0aGUgeGxzeCBmaWxlIGNvbnRhaW5pbmcgdGhlIGFib3ZlIGNvbnRyYXN0cy4KVGhlIGZpbGUgd2l0aCB0aGUgc3VmZml4IF90YWJsZS12ZXJzaW9uIHdpbGwgdGhlcmVmb3JlIGNvbnRhaW4gYWxsCmdlbmVzIGFuZCB0aGUgZmlsZSB3aXRoIHRoZSBzdWZmaXggX3NpZy12ZXJzaW9uIHdpbGwgY29udGFpbiBvbmx5CnRob3NlIGRlZW1lZCBzaWduaWZpY2FudCB2aWEgb3VyIGRlZmF1bHQgY3JpdGVyaWEgb2YgREVTZXEyIHxsb2dGQ3wgPj0gMS4wCmFuZCBhZGp1c3RlZCBwLXZhbHVlIDw9IDAuMDUuCgojIyBPdmVyIHJlcHJlc2VudGF0aW9uIHNlYXJjaGVzCgpJIGRlY2lkZWQgdG8gbWFrZSBvbmUgaW5pdGlhbGx5IHNtYWxsLCBidXQgSSB0aGluayBxdWlja2x5IGJpZyBjaGFuZ2UKdG8gdGhlIG9yZ2FuaXphdGlvbiBvZiB0aGlzIGRvY3VtZW50OiAgSSBhbSBtb3ZpbmcgdGhlIEdTRUEgc2VhcmNoZXMKdXAgdG8gaW1tZWRpYXRlbHkgYWZ0ZXIgdGhlIERFLiAgSSB3aWxsIHRoZW4gbW92ZSB0aGUgcGxvdHMgb2YgdGhlCmdwcm9maWxlciByZXN1bHRzIHRvIGltbWVkaWF0ZWx5IGFmdGVyIHRoZSB2YXJpb3VzIHZvbGNhbm8gcGxvdHMgc28KdGhhdCBpdCBpcyBlYXNpZXIgdG8gaW50ZXJwcmV0IHRoZW0uCgpJIGFtIHJlYXNvbmFibHkgY2VydGFpbiB0aGlzIGlzIHRoZSBwbGFjZSB0byBjaGVjayB0aGF0IHoyM25vIGRydWcgLwp1bmluZmVjdGVkIGhhcyB0aGUgZXhwZWN0ZWQgc2V0IG9mIGdlbmVzIGFuZCB0aGF0IHRoZXJlIGlzIG9yIGlzIG5vdCBhCnJlYWN0b21lIHJlc3VsdC4KClJlcHJvZHVjaWJpbGl0eSBub3RlOiBHaXZlbiB0aGF0IHRoaXMgaXMgZW50aXJlbHkgZGVwZW5kZW50IG9uIGFuCm9ubGluZSBzZXJ2aWNlLCBJIG11c3QgYXNzdW1lIHRoYXQgdGhlIHJlc3VsdHMgd2lsbCBjaGFuZ2Ugb3ZlciB0aW1lOwppbiBhZGRpdGlvbiB0aGVpciB3ZWIgc2VydmVycyB1bmRlcmdvIG1haW50ZW5hbmNlIHJlZ3VsYXJseSwgd2hpY2ggbWF5CnJlc3VsdCBpbiBzeXN0ZW1hdGljIGZhaWx1cmUgb2YgdGhlc2UgYW5hbHlzZXMuICBJIGxpa2UgZ1Byb2ZpbGVyCnF1aXRlIGEgbG90IGZvciB0aGlzIHR5cGUgb2Ygc3R1ZmYsIGJ1dCB0aGlzIGlzIGFuIGltcG9ydGFudCBjYXZlYXQuCgpDb252ZXJzZWx5LCB0aGUgY2x1c3RlclByb2ZpbGVyIHJlc3VsdHMgbGF0ZXIgZGVwZW5kIG9uIGEgY29uc2lzdGVudApvcmdkYiBhbm5vdGF0aW9uIHNldCAob3IgcmVhY3RvbWUgb3Igd2hhdGV2ZXIpOyB0aG9zZSB2ZXJzaW9ucyBhcmUKZml4ZWQgYnkgdGhlIGNvbnRhaW5lciBpbnN0YWxsYXRpb24uCgpgYGB7cn0KYWxsX2dwIDwtIGFsbF9ncHJvZmlsZXIoaHNfbWFjcl9zaWcsIGVucmljaF9pZF9jb2x1bW4gPSAiaGduY3N5bWJvbCIpCmZvciAoZyBpbiBzZXFfbGVuKGxlbmd0aChhbGxfZ3ApKSkgewogIG5hbWUgPC0gbmFtZXMoYWxsX2dwKVtnXQogIGRhdHVtIDwtIGFsbF9ncFtbbmFtZV1dCiAgZmlsZW5hbWUgPC0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9ncHJvZmlsZXIve25hbWV9X2dwcm9maWxlci12e3Zlcn0ueGxzeCIpCiAgd3JpdHRlbiA8LSBzbSh3cml0ZV9ncHJvZmlsZXJfZGF0YShkYXR1bSwgZXhjZWwgPSBmaWxlbmFtZSkpCn0KYGBgCgojIyBFeHBsaWNpdCBHU0VBIHNlYXJjaCB2aXMgY2x1c3RlclByb2ZpbGVyCgpgYGB7cn0KYWxsX2NwIDwtIGFsbF9jcHJvZmlsZXIoaHNfbWFjcl9zaWcsIGhzX21hY3JfdGFibGUpCmBgYAoKIyMgU3BlY2lmaWMgZGVzaXJlcyBpbiBSZWFjdG9tZSByZXN1bHRzCgpJbiBwcmV2aW91cyBhbmFseXNlcyAoSSB0aGluayBieSBEci4gQ29sbWVuYXJlcyksIGEgc3BlY2lmaWMKVHJ5cHRvcGhhbiBiaW9zeW50aGVzaXMgcGF0aHdheSB3YXMgb2JzZXJ2ZWQuICBQYXJ0Y2l1bGFybHkgaW4gdGhlCjIuMy91bmluZmVjdGVkIGNvbXBhcmlzb24uICBJIHRoaW5rIG15IGdwcm9maWxlciBhbmFseXNpcyBpcyB0b28Kc3RyaW5nZW50IGFuZCB0aGVyZWZvcmUgbm90IG9ic2VydmluZyB0aGlzLiAgT2xnYSBhc2tlZCBpZiBJIGNvdWxkCmxvb2sgYXQgdGhhdCBhbmQgc2VlIGlmIHRoZXJlIGFyZSB0cml2aWFsIHNldHRpbmdzIEkgY2FuIGNoYW5nZSB0bwpoaWdobGlnaHQgdGhpcyBwYXRod2F5LiAgVGhlIHR3byBtb3N0IGxpa2VseSB0aGluZ3MgSSBjYW4gY2hhbmdlIGFyZQp0aGUgc3RyaW5nZW5jaWVzIG9mIHRoZSBERSBhbmFseXNpcyBhbmQvb3IgZ1Byb2ZpbGVyLgoKYGBge3J9CnRlc3RfejIzX3VuaW5mX3VwIDwtIGhzX21hY3Jfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJ6MjNub3NiX3ZzX3VuaW5mIl1dCm5yb3codGVzdF96MjNfdW5pbmZfdXApCnRlc3RfejIzX3VuaW5mX2Rvd24gPC0gaHNfbWFjcl9zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1siejIzbm9zYl92c191bmluZiJdXQpucm93KHRlc3RfejIzX3VuaW5mX2Rvd24pCgp0ZXN0X2dwX3VwIDwtIHNpbXBsZV9ncHJvZmlsZXIodGVzdF96MjNfdW5pbmZfdXAsIGVucmljaF9pZF9jb2x1bW4gPSAiaGduY3N5bWJvbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJlc2hvbGQgPSAxLjApCnRlc3RfZ3BfdXAKd3JpdHRlbl91cCA8LSB3cml0ZV9ncHJvZmlsZXJfZGF0YSh0ZXN0X2dwX3VwLCBleGNlbCA9ICJleGNlbC96MjNfdW5pbmZfZ3BfdXBfYWxsLnhsc3giKQoKdGVzdF9ncF9kb3duIDwtIHNpbXBsZV9ncHJvZmlsZXIodGVzdF96MjNfdW5pbmZfZG93biwgZW5yaWNoX2lkX2NvbHVtbiA9ICJoZ25jc3ltYm9sIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkID0gMS4wKQp0ZXN0X2dwX2Rvd24Kd3JpdHRlbl9kb3duIDwtIHdyaXRlX2dwcm9maWxlcl9kYXRhKHRlc3RfZ3BfZG93biwgZXhjZWwgPSAiZXhjZWwvejIzX3VuaW5mX2dwX2Rvd25fYWxsLnhsc3giKQpgYGAKCiMjIFBsb3QgY29udHJhc3RzIG9mIGludGVyZXN0CgpPbmUgc3VnZ2VzdGlvbiBJIHJlY2VpdmVkIHJlY2VudGx5IHdhcyB0byBzZXQgdGhlIGF4ZXMgZm9yIHRoZXNlCnZvbGNhbm8gcGxvdHMgdG8gYmUgc3RhdGljIHJhdGhlciB0aGFuIGxldCBnZ3Bsb3QgY2hvb3NlIGl0cyBvd24uICBJCmFtIGFzc3VtaW5nIHRoaXMgaXMgb25seSByZWxldmFudCBmb3IgcGFpcnMgb2YgY29udHJhc3RzLCBidXQgdGhhdAptaWdodCBub3QgYmUgdHJ1ZS4KCiMjIEluZGl2aWR1YWwgenltb2RlbWVzIHZzLiB1bmluZmVjdGVkCgpUaGUgZm9sbG93aW5nIGJsb2NrcyB3aWxsIGJlIGEgbG90IG9mIHJlcGV0aXRpb24uICBJbiBlYWNoIGNhc2UgSSBhbQp5YW5raW5nIG91dCB0aGUgdm9sY2FubyBwbG90IGZvciBhIHNwZWNpZmljIGNvbnRyYXN0IGFuZCBzaG93aW5nIHRoZQpvcmlnaW5hbCBmb2xsb3dlZCBieSBhIHZlcnNpb24gd2l0aCBkaWZmZXJlbnQgY29sb3JzL2xhYmVsbGluZy4KCiMjIyBJbmZlY3RlZCB3aXRoIHoyLjMgbm8gQW50aW1vbmlhbCB2cy4gVW5pbmZlY3RlZAoKYGBge3J9CnBsb3RfY29sb3JzIDwtIGdldF9leHB0X2NvbG9ycyhoc19tYWNyX3RhYmxlW1siaW5wdXQiXV1bWyJpbnB1dCJdXSkKCiMjIFRoZSBvcmlnaW5hbCBwbG90IGZyb20gbXkgeGxzeCBmaWxlCmhzX21hY3JfdGFibGVbWyJwbG90cyJdXVtbInoyM25vc2JfdnNfdW5pbmYiXV1bWyJkZXNlcV92b2xfcGxvdHMiXV0KCnoyM25vc2JfdnNfdW5pbmZfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGlucHV0ID0gaHNfbWFjcl90YWJsZVtbImRhdGEiXV1bWyJ6MjNub3NiX3ZzX3VuaW5mIl1dLAogIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGxhYmVsID0gMTAsIGxhYmVsX2NvbHVtbiA9ICJoZ25jc3ltYm9sIiwKICBjb2xvcl9sb3cgPSBwbG90X2NvbG9yc1tbInVuaW5mbm9uZSJdXSwgY29sb3JfaGlnaCA9IHBsb3RfY29sb3JzW1siaW5mejIzIl1dKQoKbGFiZWxlZCA8LSB6MjNub3NiX3ZzX3VuaW5mX3ZvbGNhbm9bWyJwbG90Il1dICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtNiwgMjEpLCBicmVha3MgPSBjKC02LCAtNCwgLTIsIDAsIDIsIDQsIDYsIDgsIDEwLCAyMCkpICsKICBnZ2JyZWFrOjpzY2FsZV94X2JyZWFrKGMoMTAsIDE5KSwgc2NhbGVzID0gMC4yLCBzcGFjZSA9IDAuMDIpCnBwKGZpbGUgPSAiZmlndXJlcy9maWcyYV9sYWJlbGVkX3dpdGhfYnJlYWsuc3ZnIikKbGFiZWxlZApkZXYub2ZmKCkKbGFiZWxlZAoKcGxvdGx5OjpnZ3Bsb3RseSh6MjNub3NiX3ZzX3VuaW5mX3ZvbGNhbm9bWyJwbG90Il1dKQpgYGAKClRoZSBmb2xsb3dpbmcgcHJvdmlkZXMgc29tZSBvZiB0aGUgb3Zlci1yZXByZXNlbnRhdGlvbiBwbG90cyBmcm9tIGdQcm9maWxlcjIuCgpgYGB7cn0KYWxsX2dwW1siejIzbm9zYl92c191bmluZl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0KIyMgUmVhY3RvbWUsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjNub3NiX3ZzX3VuaW5mX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siS0VHRyJdXQojIyBLRUdHLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KIyNhbGxfZ3BbWyJ6MjNub3NiX3ZzX3VuaW5mX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0KIyMgTUYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjNub3NiX3ZzX3VuaW5mX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siVEYiXV0KIyMgVEYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjNub3NiX3ZzX3VuaW5mX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siV1AiXV0KIyMgV2lraVBhdGh3YXlzLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KYWxsX2dwW1siejIzbm9zYl92c191bmluZl91cCJdXVtbImludGVyYWN0aXZlX3Bsb3RzIl1dW1siV1AiXV0KCm1lc3NhZ2UoIk9sZ2EgcmVjZWl2ZWQgYSBxdWVyeSBhYm91dCB0aGUgZm9sbG93aW5nIHJlc3VsdCwgSSB0aGluayBpdCBpcyBudWxsLiIpCmFsbF9ncFtbInoyM25vc2JfdnNfdW5pbmZfZG93biJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0KbWVzc2FnZSgiSXMgdGhlIHByZXZpb3VzIHBsb3QgbnVsbD8iKQojIyBSZWFjdG9tZSwgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgZG93bi4KYWxsX2dwW1siejIzbm9zYl92c191bmluZl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0KIyMgTUYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIGRvd24uCmFsbF9ncFtbInoyM25vc2JfdnNfdW5pbmZfZG93biJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlRGIl1dCiMjIFRGLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCBkb3duLgpgYGAKCldlIGhhdmUgc29tZSBvdGhlciBjYXRlZ29yaWNhbCBlbnJpY2htZW50IHBsb3RzIGF2YWlsYWJsZSB2aWEKZW5yaWNocGxvdCwgbGV0IHVzIHRyeSBhIGZldyBvdXQgZm9yIGNvbnRyYXN0cyBvZiBpbnRlcmVzdCBhbmQgc2VlIGlmCmFueSBvZiB0aGVtIHByb3ZlIGhlbHBmdWwuCgpGaXJzdCwgYXMgYSByZW1pbmRlciwgaGVyZSBhcmUgdGhlIGNvbnRyYXN0cyB3aGljaCBhcmUgYXZhaWxhYmxlIHRvCmV4YW1pbmUsIGluIGVhY2ggY2FzZSB0aGVyZSBpcyBhbiBfdXAgYW5kIF9kb3duIGVucmljaG1lbnQgb2JqZWN0IGluCnRoZSBkYXRhLiAgVGh1cyBpbiB0aGUgZm9sbG93aW5nIGxpc3QgSSBhbSBnb2luZyB0byBhcmJpdHJhcmlseSBwcmludApvdXQgc29tZSBpbnZvY2F0aW9ucyB3aGljaCBleHRyYWN0IHB1dGF0aXZlbHkgaW50ZXJlc3RpbmcgYml0cyBvZiBkYXRhLgoKKiB6MjNub3NiX3ZzX3VuaW5mOiBhbGxfZ3BbWyJ6MjNub3NiX3ZzX3VuaW5mX3VwIl1dW1siQlBfZW5yaWNoIl1dCiogejIybm9zYl92c191bmluZi4KKiB6MjNub3NiX3ZzX3oyMm5vc2IuCiogejIzc2JfdnNfejIyc2IuCiogejIzc2JfdnNfejIzbm9zYi4KKiB6MjJzYl92c196MjJub3NiLgoqIHoyM3NiX3ZzX3NiLgoqIHoyMnNiX3ZzX3NiLgoqIHoyM3NiX3ZzX3VuaW5mLgoqIHoyMnNiX3ZzX3VuaW5mLgoqIHNiX3ZzX3VuaW5mLgoqIGV4dHJhX3oyMzIyLgoqIGV4dHJhX2RydWdub2RydWcuCgpgYGB7cn0KejIzbm9zYl91bmluZl91cF9nbyA8LSBhbGxfZ3BbWyJ6MjNub3NiX3ZzX3VuaW5mX3VwIl1dW1siQlBfZW5yaWNoIl1dCnoyM25vc2JfdW5pbmZfdXBfZ29fcGFpciA8LSBwYWlyd2lzZV90ZXJtc2ltKHoyM25vc2JfdW5pbmZfdXBfZ28pCmRvdHBsb3QoejIzbm9zYl91bmluZl91cF9nbykKZW1hcHBsb3QoejIzbm9zYl91bmluZl91cF9nb19wYWlyKQojI3NzcGxvdCh6MjNub3NiX3VuaW5mX3VwX2dvX3BhaXIpCnRyZWVwbG90KHoyM25vc2JfdW5pbmZfdXBfZ29fcGFpcikKdXBzZXRwbG90KHoyM25vc2JfdW5pbmZfdXBfZ28pCmNuZXRwbG90KHoyM25vc2JfdW5pbmZfdXBfZ28pCmBgYAoKIyMjIFJlcGVhdCwgYnV0IHVzaW5nIGEgbGVzcyBzdHJpY3Qgc2V0IG9mICdzaWduaWZpY2FudCBnZW5lcycKCkkgYW0gbm90IGVudGlyZWx5IGNlcnRhaW4gaWYgdGhlIFJlYWN0b21lIHJlc3VsdHMgT2xnYSBzaG93ZWQgbWUKaW5jbHVkZWQgYm90aCB1cCBhbmQgZG93biBnZW5lcz8gIEkgYW0gZ29pbmcgdG8gYXNzdW1lIGZvciB0aGUgbW9tZW50CnRoYXQgaXQgd2FzIGp1c3QgdXAvZG93biwgYnV0IGlmIHRoYXQgcHJvdmVzIGludHJhY3RhYmxlIEkgd2lsbCBnbwpiYWNrIHRvIHRoZSBtYW51c2NyaXB0IGFuZCByZWFkIG1vcmUgY2FyZWZ1bGx5IChlLmcuIEkganVzdCByZW1lbWJlcmVkCndoZXJlIHRoZSBwaWN0dXJlIGNhbWUgZnJvbSEpCgojIyMjIEFkZCBhIGxpdHRsZSB0b3BnbwoKSW4gdGhlIHByb2Nlc3Mgb2YgZXhwbG9yaW5nIHRoZSB2YXJpb3VzIHBhcmFtZXRlcnMgdXNlZCB3aXRoCmdQcm9maWxlcjIsIEkgZm91bmQgbXlzZWxmIHRoaW5raW5nIHRoYXQgaXQgd291bGQgYmUgbmljZSB0byBoYXZlIHNvbWUKdG9wZ28gcmVzdWx0cyB0byBjb21wYXJlIGFnYWluc3QuICBUaGUgZm9sbG93aW5nIGJsb2NrIGlzIHRoZSByZXN1bHQKb2YgdGhhdCB0aG91Z2h0LgoKYGBge3J9CnRlc3RfZ2VuZXNfdXAgPC0gaHNfbWFjcl9sZXNzc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJ6MjNub3NiX3ZzX3VuaW5mIl1dCnRlc3RfcXVlcnlfdXAgPC0gc2ltcGxlX2dwcm9maWxlcih0ZXN0X2dlbmVzX3VwLCB0aHJlc2hvbGQgPSAwLjEpCnRlc3RfcXVlcnlfdXBbWyJwdmFsdWVfcGxvdHMiXV1bWyJSRUFDIl1dCnBkZihmaWxlID0gImltYWdlcy90ZXN0X3F1ZXJ5X2Jpb2xvZ2ljYWxfcHJvY2Vzc196MjNfdnNfdW5pbmZfdXAucGRmIiwgaGVpZ2h0ID0gMTIsIHdpZHRoID0gOSkKdGVzdF9xdWVyeV91cFtbInB2YWx1ZV9wbG90cyJdXVtbIkJQIl1dCmRldi5vZmYoKQplbnJpY2hwbG90Ojpkb3RwbG90KHRlc3RfcXVlcnlfdXBbWyJCUF9lbnJpY2giXV0pCnRlc3RfZ2VuZXNfZG93biA8LSBoc19tYWNyX2xlc3NzaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1siejIzbm9zYl92c191bmluZiJdXQp0ZXN0X3F1ZXJ5X2Rvd24gPC0gc2ltcGxlX2dwcm9maWxlcih0ZXN0X2dlbmVzX2Rvd24pCnRlc3RfcXVlcnlfZG93bltbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0KCiMjIEkga2VlcCBnZXR0aW5nIGFsbCBzb3J0cyBvZiBhbm5veWluZyBiaW9tYXJ0IGVycm9ycy4KaHNfZ28gPC0gdHJ5KGxvYWRfYmlvbWFydF9nbyhhcmNoaXZlID0gRkFMU0UsIG92ZXJ3cml0ZSA9IFRSVUUpKQppZiAoInRyeS1lcnJvciIgJWluJSBjbGFzcyhoc19nbykpIHsKICBoc19nbyA8LSBsb2FkX2Jpb21hcnRfZ28oYXJjaGl2ZSA9IFRSVUUsIG1vbnRoID0gIjA0IiwgeWVhciA9ICIyMDIwIiwgb3ZlcndyaXRlID0gVFJVRSkKfQp0ZXN0X3RvcGdvX3VwIDwtIHNpbXBsZV90b3Bnbyh0ZXN0X2dlbmVzX3VwLCBnb19kYiA9IGhzX2dvW1siZ28iXV0sIHBhcmFsbGVsID0gRkFMU0UpCndyaXR0ZW5fdG9wZ28gPC0gd3JpdGVfdG9wZ29fZGF0YSgKICB0ZXN0X3RvcGdvX3VwLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9vbnRvbG9neV90b3Bnby90b3Bnb196MjNfdW5pbmZfbGVzc19zdHJpY3QueGxzeCIpKQpgYGAKCiMjIyBJbmZlY3RlZCB3aXRoIHoyLjIgbm8gQW50aW1vbmlhbCB2cy4gVW5pbmZlY3RlZAoKSGVyZSBpcyB3aGVyZSB0aGluZ3Mgd2lsbCBnZXQgbW9zdCByZXBldGl0aXZlLiAgSW4gZWFjaCBpbnN0YW5jZSBJIGFtCmNyZWF0aW5nIGEgY291cGxlIG9mIHZvbGNhbm8gcGxvdHMgZm9sbG93ZWQgYnkgcHJpbnRpbmcgc29tZSBvZiB0aGUKZ1Byb2ZpbGVyMiByZXN1bHRzICh3aGVuIEkgZ2V0IHRoZSBpdGNoKS4KClRoZSBmb2xsb3dpbmcgc2hvdWxkIGJlIGEgc2xpZ2h0bHkgaW1wcm92ZWQgdmVyc2lvbiBvZiBvdXIgZXh0YW50CmZpZ3VyZSAyQi4KCmBgYHtyfQojIyBUaGUgb3JpZ2luYWwgcGxvdApoc19tYWNyX3RhYmxlW1sicGxvdHMiXV1bWyJ6MjJub3NiX3ZzX3VuaW5mIl1dW1siZGVzZXFfdm9sX3Bsb3RzIl1dCgp6MjJub3NiX3ZzX3VuaW5mX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICBoc19tYWNyX3RhYmxlW1siZGF0YSJdXVtbInoyMm5vc2JfdnNfdW5pbmYiXV0sICJ6MjJub3NiX3ZzX3VuaW5mIiwKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBsYWJlbCA9IDEwLCBsYWJlbF9jb2x1bW4gPSAiaGduY3N5bWJvbCIsCiAgY29sb3JfbG93ID0gcGxvdF9jb2xvcnNbWyJ1bmluZm5vbmUiXV0sIGNvbG9yX2hpZ2ggPSBwbG90X2NvbG9yc1tbImluZnoyMiJdXSkKCmxhYmVsZWQgPC0gejIybm9zYl92c191bmluZl92b2xjYW5vW1sicGxvdCJdXSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTIsIDIxKSwgYnJlYWtzID0gYygtMiwgMCwgMiwgNCwgNiwgOCwgMTAsIDIxLCAyMikpICsKICBnZ2JyZWFrOjpzY2FsZV94X2JyZWFrKGMoMTEsIDIwKSwgc2NhbGVzID0gMC4yLCBzcGFjZSA9IDAuMDIpCnBwKGZpbGUgPSAiZmlndXJlcy9maWcyYl9sYWJlbGVkX3dpdGhfYnJlYWsuc3ZnIikKbGFiZWxlZApkZXYub2ZmKCkKbGFiZWxlZAoKcGxvdGx5OjpnZ3Bsb3RseSh6MjJub3NiX3ZzX3VuaW5mX3ZvbGNhbm9bWyJwbG90Il1dKQpgYGAKCkFkZCBzb21lIHB2YWx1ZSBiYXJwbG90cyBmcm9tIGdQcm9maWxlciBmb3IgdGhpcyBjb250cmFzdC4KCmBgYHtyfQphbGxfZ3BbWyJ6MjJub3NiX3ZzX3VuaW5mX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siUkVBQyJdXQojIyBSZWFjdG9tZSwgenltb2RlbWUyLjIgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyMm5vc2JfdnNfdW5pbmZfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJNRiJdXQojIyBNRiwgenltb2RlbWUyLjIgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyMm5vc2JfdnNfdW5pbmZfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJURiJdXQojIyBURiwgenltb2RlbWUyLjIgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyMm5vc2JfdnNfdW5pbmZfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJXUCJdXQojIyBXaWtpUGF0aHdheXMsIHp5bW9kZW1lMi4yIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgoKYWxsX2dwW1siejIybm9zYl92c191bmluZl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siUkVBQyJdXQojIyBSZWFjdG9tZSwgenltb2RlbWUyLjIgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgZG93bi4KYWxsX2dwW1siejIybm9zYl92c191bmluZl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0KIyMgTUYsIHp5bW9kZW1lMi4yIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIGRvd24uCmFsbF9ncFtbInoyMm5vc2JfdnNfdW5pbmZfZG93biJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlRGIl1dCiMjIFRGLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCBkb3duLgpgYGAKCiMjIyBJbmZlY3RlZCB3aXRoIHoyLjMgdHJlYXRlZCB2cy4gVW5pbmZlY3RlZCB0cmVhdGVkCgpJIGRvIG5vdCB0aGluayB0aGlzIHBsb3QgaXMgdXNlZCBhdCB0aGlzIHRpbWUuCgpgYGB7cn0KIyMgVGhlIG9yaWdpbmFsIHBsb3QKaHNfbWFjcl90YWJsZVtbInBsb3RzIl1dW1siejIzc2JfdnNfc2IiXV1bWyJkZXNlcV92b2xfcGxvdHMiXV0KCnoyM3NiX3ZzX3VuaW5mc2Jfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGhzX21hY3JfdGFibGVbWyJkYXRhIl1dW1siejIzc2JfdnNfc2IiXV0sICJ6MjNzYl92c19zYiIsCiAgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgbGFiZWwgPSAxMCwgbGFiZWxfY29sdW1uID0gImhnbmNzeW1ib2wiLAogIGNvbG9yX2xvdyA9IHBsb3RfY29sb3JzW1siaW5mc2J6MjMiXV0sIGNvbG9yX2hpZ2ggPSBwbG90X2NvbG9yc1tbInVuaW5mc2Jub25lIl1dKQp6MjNzYl92c191bmluZnNiX3ZvbGNhbm9bWyJwbG90Il1dCgpwbG90bHk6OmdncGxvdGx5KHoyM3NiX3ZzX3VuaW5mc2Jfdm9sY2Fub1tbInBsb3QiXV0pCmBgYAoKIyMjIEluZmVjdGVkIHdpdGggejIuMyB1bnRyZWF0ZWQgdnMuIHoyLjIgdW50cmVhdGVkCgpUaGlzIGlzIGZpZ3VyZSAyQyBhdCB0aGlzIHRpbWUuCgpgYGB7cn0KIyMgVGhlIG9yaWdpbmFsIHBsb3QKaHNfbWFjcl90YWJsZVtbInBsb3RzIl1dW1siejIzbm9zYl92c196MjJub3NiIl1dW1siZGVzZXFfdm9sX3Bsb3RzIl1dCgp6MjNub3NiX3ZzX3oyMm5vc2Jfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGhzX21hY3JfdGFibGVbWyJkYXRhIl1dW1siejIzbm9zYl92c196MjJub3NiIl1dLCAiejIzbm9zYl92c196MjJub3NiIiwKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBsYWJlbCA9IDEwLCBsYWJlbF9jb2x1bW4gPSAiaGduY3N5bWJvbCIsCiAgY29sb3JfbG93ID0gcGxvdF9jb2xvcnNbWyJpbmZ6MjMiXV0sIGNvbG9yX2hpZ2ggPSBwbG90X2NvbG9yc1tbImluZnoyMiJdXSkKCmxhYmVsZWQgPC0gejIzbm9zYl92c196MjJub3NiX3ZvbGNhbm9bWyJwbG90Il1dICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygtMTAsIC04LCAtNiwgLTQsIC0yLCAwLCAyLCA0LCA2KSkKCnBwKGZpbGUgPSAiZmlndXJlcy9maWcyY19sYWJlbGVkLnN2ZyIpCmxhYmVsZWQKZGV2Lm9mZigpCmxhYmVsZWQKYGBgCgojIyMgSW5mZWN0ZWQgd2l0aCB6Mi4zIHRyZWF0ZWQgdnMuIHoyLjIgdHJlYXRlZAoKVGhpcyBpcyBjdXJyZW50bHkgZmlndXJlIDNDLgoKRklYTUU6IFRoZSBheGlzIGxhYmVsIGlzbid0IHF1aXRlIHJpZ2h0IGZvciB0aGUgZ2dicmVhay4KCmBgYHtyfQojIyBUaGUgb3JpZ2luYWwgcGxvdApoc19tYWNyX3RhYmxlW1sicGxvdHMiXV1bWyJ6MjNzYl92c196MjJzYiJdXVtbImRlc2VxX3ZvbF9wbG90cyJdXQoKejIzc2JfdnNfejIyc2Jfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGhzX21hY3JfdGFibGVbWyJkYXRhIl1dW1siejIzc2JfdnNfejIyc2IiXV0sICJ6MjNzYl92c196MjJzYiIsCiAgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgbGFiZWwgPSAxMCwgbGFiZWxfY29sdW1uID0gImhnbmNzeW1ib2wiLAogIGNvbG9yX2hpZ2ggPSBwbG90X2NvbG9yc1tbImluZnNiejIzIl1dLCBjb2xvcl9sb3cgPSBwbG90X2NvbG9yc1tbImluZnNiejIyIl1dKQoKbGFiZWxlZCA8LSB6MjNzYl92c196MjJzYl92b2xjYW5vW1sicGxvdCJdXSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoLTIzLCAtNiwgLTQsIC0yLCAwLCAyLCA0LCA2KSkgKwogIGdnYnJlYWs6OnNjYWxlX3hfYnJlYWsoYygtNSwgLTIyLjUpLCBzY2FsZXMgPSAxMCwgc3BhY2UgPSAwLjAyKQpwcChmaWxlID0gImZpZ3VyZXMvZmlnM2NfbGFiZWxlZF9icmVha3Muc3ZnIikKbGFiZWxlZApkZXYub2ZmKCkKbGFiZWxlZApgYGAKCiMjIyBJbmZlY3RlZCB3aXRoIHoyLjMgU0IgdHJlYXRlZCB2cy4gejIuMyB1bnRyZWF0ZWQKCkkgdGhpbmsgdGhpcyBpcyBjdXJyZW50bHkgZmlndXJlIDNBLgoKRklYTUU6IFRoZSBheGlzIGxhYmVsIGZvciB0aGUgZ2dicmVhayBpc24ndCBxdWl0ZSByaWdodC4KCmBgYHtyfQojIyBUaGUgb3JpZ2luYWwgcGxvdApoc19tYWNyX3RhYmxlW1sicGxvdHMiXV1bWyJ6MjNzYl92c196MjNub3NiIl1dW1siZGVzZXFfdm9sX3Bsb3RzIl1dCgp6MjNzYl92c196MjNub3NiX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICBoc19tYWNyX3RhYmxlW1siZGF0YSJdXVtbInoyM3NiX3ZzX3oyM25vc2IiXV0sICJ6MjNzYl92c196MjNub3NiIiwKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBsYWJlbCA9IDEwLCBsYWJlbF9jb2x1bW4gPSAiaGduY3N5bWJvbCIsCiAgY29sb3JfaGlnaCA9IHBsb3RfY29sb3JzW1siaW5mc2J6MjMiXV0sIGNvbG9yX2xvdyA9IHBsb3RfY29sb3JzW1siaW5mejIzIl1dKQoKbGFiZWxlZCA8LSB6MjNzYl92c196MjNub3NiX3ZvbGNhbm9bWyJwbG90Il1dICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtMTksIDYpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKC0yMCwgLTE4LCAtMTYsIC0xNCwgLTEyLCAtMTAsIC02LCAtNCwgLTIsIDAsIDIsIDQsIDYpKSArCiAgZ2dicmVhazo6c2NhbGVfeF9icmVhayhjKC0xNywgLTgpLCBzY2FsZXMgPSAxNywgc3BhY2UgPSAwLjAyKQpwcChmaWxlID0gImZpZ3VyZXMvZmlnM2FfbGFiZWxlZF93aXRoX2JyZWFrLnN2ZyIpCmxhYmVsZWQKZGV2Lm9mZigpCmxhYmVsZWQKYGBgCgojIyMgSW5mZWN0ZWQgd2l0aCB6Mi4zIFNCIHRyZWF0ZWQgdnMuIHoyLjMgdW50cmVhdGVkCgpgYGB7cn0KIyMgVGhlIG9yaWdpbmFsIHBsb3QKaHNfbWFjcl90YWJsZVtbInBsb3RzIl1dW1siejIyc2JfdnNfejIybm9zYiJdXVtbImRlc2VxX3ZvbF9wbG90cyJdXQoKejIyc2JfdnNfejIybm9zYl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgaHNfbWFjcl90YWJsZVtbImRhdGEiXV1bWyJ6MjJzYl92c196MjJub3NiIl1dLCAiejIyc2JfdnNfejIybm9zYiIsCiAgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgbGFiZWwgPSAxMCwgbGFiZWxfY29sdW1uID0gImhnbmNzeW1ib2wiLAogIGNvbG9yX2hpZ2ggPSBwbG90X2NvbG9yc1tbImluZnNiejIyIl1dLCBjb2xvcl9sb3cgPSBwbG90X2NvbG9yc1tbImluZnoyMiJdXSkKCmxhYmVsZWQgPC0gejIyc2JfdnNfejIybm9zYl92b2xjYW5vW1sicGxvdCJdXSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoLTYsIC00LCAtMiwgMCwgMiwgNCwgNikpCgpwcChmaWxlID0gImZpZ3VyZXMvZmlnM2JfbGFiZWxlZC5zdmciKQpsYWJlbGVkCmRldi5vZmYoKQpsYWJlbGVkCmBgYAoKIyMjIEluZmVjdGVkIHdpdGggejIuMyBTQiB0cmVhdGVkIHZzLiB1bmluZmVjdGVkIHRyZWF0ZWQKCmBgYHtyfQp4X2xpbWl0cyA8LSBjKC02LCA2KQojIyBUaGUgb3JpZ2luYWwgcGxvdApoc19tYWNyX3RhYmxlW1sicGxvdHMiXV1bWyJ6MjNzYl92c19zYiJdXVtbImRlc2VxX3ZvbF9wbG90cyJdXQoKejIzc2JfdnNfc2Jfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGhzX21hY3JfdGFibGVbWyJkYXRhIl1dW1siejIzc2JfdnNfc2IiXV0sICJ6MjNzYl92c19zYiIsCiAgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgbGFiZWwgPSAxMCwgbGFiZWxfY29sdW1uID0gImhnbmNzeW1ib2wiLCBpbnZlcnQgPSBUUlVFLAogIGNvbG9yX2xvdyA9IHBsb3RfY29sb3JzW1siaW5mc2J6MjMiXV0sIGNvbG9yX2hpZ2ggPSBwbG90X2NvbG9yc1tbInVuaW5mc2Jub25lIl1dKQp6MjNzYl92c19zYl92b2xjYW5vW1sicGxvdCJdXQpgYGAKCiMjIyBJbmZlY3RlZCB3aXRoIHoyLjIgU0IgdHJlYXRlZCB2cy4gdW5pbmZlY3RlZCB0cmVhdGVkCgpgYGB7cn0KIyMgVGhlIG9yaWdpbmFsIHBsb3QKaHNfbWFjcl90YWJsZVtbInBsb3RzIl1dW1siejIyc2JfdnNfc2IiXV1bWyJkZXNlcV92b2xfcGxvdHMiXV0KCnoyMnNiX3ZzX3NiX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICBoc19tYWNyX3RhYmxlW1siZGF0YSJdXVtbInoyMnNiX3ZzX3NiIl1dLCAiejIyc2JfdnNfc2IiLAogIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGxhYmVsID0gMTAsIGxhYmVsX2NvbHVtbiA9ICJoZ25jc3ltYm9sIiwgaW52ZXJ0ID0gVFJVRSwKICBjb2xvcl9sb3cgPSBwbG90X2NvbG9yc1tbImluZnNiejIyIl1dLCBjb2xvcl9oaWdoID0gcGxvdF9jb2xvcnNbWyJ1bmluZnNibm9uZSJdXSkKejIyc2JfdnNfc2Jfdm9sY2Fub1tbInBsb3QiXV0KYGBgCgojIyMgVW5pbmZlY3RlZCtTYlYgdnMuIFVuaW5mZWN0ZWQtU2JWCgpUaGlzIGlzIGN1cnJlbnRseSBmaWd1cmUgM0QuCgpGSVhNRTogVGhpcyBuZWVkcyB0aGUgQk9MQTJCIGdnYnJlYWsuCgpgYGB7cn0KIyMgVGhlIG9yaWdpbmFsIHBsb3QKaHNfbWFjcl90YWJsZVtbInBsb3RzIl1dW1sic2JfdnNfdW5pbmYiXV1bWyJkZXNlcV92b2xfcGxvdHMiXV0KCnNiX3ZzX3VuaW5mX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICBoc19tYWNyX3RhYmxlW1siZGF0YSJdXVtbInNiX3ZzX3VuaW5mIl1dLCAic2JfdnNfdW5pbmYiLAogIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGxhYmVsID0gMTAsIGxhYmVsX2NvbHVtbiA9ICJoZ25jc3ltYm9sIiwKICBjb2xvcl9oaWdoID0gcGxvdF9jb2xvcnNbWyJ1bmluZnNibm9uZSJdXSwgY29sb3JfbG93ID0gcGxvdF9jb2xvcnNbWyJ1bmluZm5vbmUiXV0pCgpsYWJlbGVkIDwtIHNiX3ZzX3VuaW5mX3ZvbGNhbm9bWyJwbG90Il1dICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygtMjMsIC02LCAtNCwgLTIsIDAsIDIsIDQsIDYpKSArCiAgZ2dicmVhazo6c2NhbGVfeF9icmVhayhjKC01LCAtMjIuNSksIHNjYWxlcyA9IDEwLCBzcGFjZSA9IDAuMDIpCnBwKGZpbGUgPSAiZmlndXJlcy9maWczZF9sYWJlbGVkX2JyZWFrcy5zdmciKQpsYWJlbGVkCmRldi5vZmYoKQpsYWJlbGVkCmBgYAoKIyMgRG91YmxlLWNoZWNrIHRoYXQgZ2VuZSBjb3VudHMgbWF0Y2ggbXkgcGVyY2VwdGlvbnMKCkNoZWNrIHRoYXQgbXkgcGVyY2VwdGlvbiBvZiB0aGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IHVwL2Rvd24gZ2VuZXMKbWF0Y2hlcyB3aGF0IHRoZSB0YWJsZS92ZW5uIHNheXMuICBJbiB0aGUgZm9sbG93aW5nIGJsb2NrIEkgYW0KcGVyZm9ybWluZyBzb21lIHZlbm4vdXBzZXQgYW5hbHlzZXMgdG8gc2VlIGlmIHRoZSBudW1iZXJzIG9mIGdlbmVzCm1hdGNoIHdoYXQgd2UgaGF2ZSBpbiB0aGUgY3VycmVudCB2ZXJzaW9uIG9mIHRoZSBtYW51c2NyaXB0IChwbHVzIG9yCm1pbnVzIGEgZ2VuZSkgYW5kIHRodXMgaWYgbXkgaW50ZXJwcmV0YXRpb24gb2YgdGhlIGZpZ3VyZS9sZWdlbmQgdGV4dAptYXRjaGVzIHdoYXQgSSB0aGluayBpdCBtZWFucy4KCmBgYHtyfQpzaGFyZWQgPC0gVmVubmVyYWJsZTo6VmVubihsaXN0KAogICJkcnVnIiA9IHJvd25hbWVzKGhzX21hY3Jfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJ6MjNzYl92c191bmluZiJdXSksCiAgIm5vZHJ1ZyIgPSByb3duYW1lcyhoc19tYWNyX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1siejIzbm9zYl92c191bmluZiJdXSkpKQpwcChmaWxlID0gImltYWdlcy96MjNfdnNfdW5pbmZfdmVubl91cC5wbmciKQpWZW5uZXJhYmxlOjpwbG90KHNoYXJlZCkKZGV2Lm9mZigpClZlbm5lcmFibGU6OnBsb3Qoc2hhcmVkKQojIyBJIHNlZSA5MTAgejIzc2IvdW5pbmYgYW5kIDY3MCBubyB6MjNub3NiL3VuaW5mIGdlbmVzIGluIHRoZSB2ZW5uIGRpYWdyYW0uCmxlbmd0aChzaGFyZWRASW50ZXJzZWN0aW9uU2V0c1tbIjEwIl1dKSArIGxlbmd0aChzaGFyZWRASW50ZXJzZWN0aW9uU2V0c1tbIjExIl1dKQpkaW0oaHNfbWFjcl9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbInoyM3NiX3ZzX3VuaW5mIl1dKQoKc2hhcmVkIDwtIFZlbm5lcmFibGU6OlZlbm4obGlzdCgKICAiZHJ1ZyIgPSByb3duYW1lcyhoc19tYWNyX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1siejIyc2JfdnNfdW5pbmYiXV0pLAogICJub2RydWciID0gcm93bmFtZXMoaHNfbWFjcl9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbInoyMm5vc2JfdnNfdW5pbmYiXV0pKSkKcHAoZmlsZSA9ICJpbWFnZXMvejIyX3ZzX3VuaW5mX3Zlbm5fdXAucG5nIikKVmVubmVyYWJsZTo6cGxvdChzaGFyZWQpCmRldi5vZmYoKQpWZW5uZXJhYmxlOjpwbG90KHNoYXJlZCkKCmxlbmd0aChzaGFyZWRASW50ZXJzZWN0aW9uU2V0c1tbIjEwIl1dKSArIGxlbmd0aChzaGFyZWRASW50ZXJzZWN0aW9uU2V0c1tbIjExIl1dKQpkaW0oaHNfbWFjcl9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbInoyMnNiX3ZzX3VuaW5mIl1dKQpgYGAKCipOb3RlIHRvIHNlbGYqOiBUaGVyZSBpcyBhbiBlcnJvciBpbiBteSB2b2xjYW5vIHBsb3QgY29kZSB3aGljaCB0YWtlcwplZmZlY3Qgd2hlbiB0aGUgbnVtZXJhdG9yIGFuZCBkZW5vbWluYXRvciBvZiB0aGUgYWxsX3BhaXJ3aXNlCmNvbnRyYXN0cyBhcmUgZGlmZmVyZW50IHRoYW4gdGhvc2UgaW4gY29tYmluZV9kZV90YWJsZXMuICBJdCBpcwpwdXR0aW5nIHRoZSB1cHMvZG93bnMgb24gdGhlIGNvcnJlY3Qgc2lkZXMgb2YgdGhlIHBsb3QsIGJ1dCBjYWxsaW5nCnRoZSBkb3duIGdlbmVzICd1cCcgYW5kIHZpY2UtdmVyc2EuICBUaGUgcmVhc29uIGZvciB0aGlzIGlzIHRoYXQgSSBkaWQKYSBjaGVjayBmb3IgdGhpcyBoYXBwZW5pbmcsIGJ1dCB1c2VkIHRoZSB3cm9uZyBhcmd1bWVudCB0byBoYW5kbGUgaXQuCgpBIGxpa2VseSBiaXQgb2YgdGV4dCBmb3IgdGhlc2Ugdm9sY2FubyBwbG90czoKClRoZSBzZXQgb2YgZ2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gdGhlIHp5bW9kZW1lIDIuMwphbmQgdW5pbmZlY3RlZCBzYW1wbGVzIHdpdGhvdXQgZHJ1Z2UgdHJlYXRtZW50IHdhcyBxdWFudGlmaWVkIHdpdGgKREVTZXEyIGFuZCBpbmNsdWRlZCBzdXJyb2dhdGUgZXN0aW1hdGVzIGZyb20gU1ZBLiAgR2l2ZW4gdGhlIGNyaXRlcmlhCm9mIHNpZ25pZmljYW5jZSBvZiBhIGFicyhsb2dGQykgPj0gMS4wIGFuZCBmYWxzZSBkaXNjb3ZlcnkgcmF0ZQphZGp1c3RlZCBwLXZhbHVlIDw9IDAuMDUsIDY3MCBnZW5lcyB3ZXJlIG9ic2VydmVkIGFzIHNpZ25pZmljYW50bHkKaW5jcmVhc2VkIGJldHdlZW4gdGhlIGluZmVjdGVkIGFuZCB1bmluZmVjdGVkIHNhbXBsZXMgYW5kIDM4NiB3ZXJlCm9ic2VydmVkIGFzIGRlY3JlYXNlZC4gVGhlIG1vc3QgaW5jcmVhc2VkIGdlbmVzIGZyb20gdGhlIHVuaW5mZWN0ZWQKc2FtcGxlcyBpbmNsdWRlIHNvbWUgd2hpY2ggYXJlIHBvdGVudGlhbGx5IGluZGljYXRpdmUgb2YgYSBzdHJvbmcKaW5uYXRlIGltbXVuZSByZXNwb25zZSBhbmQgdGhlIGluZmxhbW1hdG9yeSByZXNwb25zZS4KCkluIGNvbnRyYXN0LCB3aGVuIHRoZSBzZXQgb2YgZ2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4KdGhlIHp5bW9kZW1lIDIuMiBhbmQgdW5pbmZlY3RlZCBzYW1wbGVzIHdhcyB2aXN1YWxpemVkLCBvbmx5IDcgZ2VuZXMKd2VyZSBvYnNlcnZlZCBhcyBkZWNyZWFzZWQgYW5kIDQzNSBpbmNyZWFzZWQuICBUaGUgaW5mbGFtbWF0b3J5CnJlc3BvbnNlIHdhcyBzaWduaWZpY2FudGx5IGxlc3MgYXBwYXJlbnQgaW4gdGhpcyBzZXQsIGJ1dCBpbnN0ZWFkCmluY2x1ZGVkIGdlbmVzIHJlbGF0ZWQgdG8gdHJhbnNwb3J0ZXIgYWN0aXZpdHkgYW5kIG94aWRvcmVkdWN0YXNlcy4KCiMjIERpcmVjdCB6eW1vZGVtZSBjb21wYXJpc29ucwoKQW4gb3J0aG9nb25hbCBjb21wYXJpc29uIHRvIHRoYXQgcGVyZm9ybWVkIGFib3ZlIGlzIHRvIGRpcmVjdGx5CmNvbXBhcmUgdGhlIHp5bW9kZW1lIDIuMyBhbmQgMi4yIHNhbXBsZXMgd2l0aCBhbmQgd2l0aG91dCBhbnRpbW9uaWFsCnRyZWF0bWVudC4KCiMjIyBaMi4zIC8gejIuMiB3aXRob3V0IGRydWcKCmBgYHtyfQp6MjNub3NiX3ZzX3oyMm5vc2Jfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fZGUoCiAgdGFibGUgPSBoc19tYWNyX3RhYmxlW1siZGF0YSJdXVtbInoyM25vc2JfdnNfejIybm9zYiJdXSwKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBzaGFwZXNfYnlfc3RhdGUgPSBGQUxTRSwgY29sb3JfYnkgPSAiZmMiLCAgbGFiZWwgPSAxMCwgbGFiZWxfY29sdW1uID0gImhnbmNzeW1ib2wiKQpwbG90bHk6OmdncGxvdGx5KHoyM25vc2JfdnNfejIybm9zYl92b2xjYW5vW1sicGxvdCJdXSkKCnoyM3NiX3ZzX3oyMnNiX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2RlKAogIHRhYmxlID0gaHNfbWFjcl90YWJsZVtbImRhdGEiXV1bWyJ6MjNzYl92c196MjJzYiJdXSwKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBzaGFwZXNfYnlfc3RhdGUgPSBGQUxTRSwgY29sb3JfYnkgPSAiZmMiLCAgbGFiZWwgPSAxMCwgbGFiZWxfY29sdW1uID0gImhnbmNzeW1ib2wiKQpwbG90bHk6OmdncGxvdGx5KHoyM3NiX3ZzX3oyMnNiX3ZvbGNhbm9bWyJwbG90Il1dKQpgYGAKCmBgYHtyfQp6MjNub3NiX3ZzX3oyMm5vc2Jfdm9sY2Fub1tbInBsb3QiXV0gKwogIHhsaW0oLTEwLCAxMCkgKwogIHlsaW0oMCwgNjApCgpwcChmaWxlID0gImltYWdlcy96MjNub3NiX3ZzX3oyMm5vc2JfcmVhY3RvbWVfdXAuc3ZnIiwKICAgaW1hZ2UgPSBhbGxfZ3BbWyJ6MjNub3NiX3ZzX3oyMm5vc2JfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJSRUFDIl1dLAogICBoZWlnaHQgPSAxMiwgd2lkdGggPSA5KQphbGxfZ3BbWyJ6MjNub3NiX3ZzX3oyMm5vc2JfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJSRUFDIl1dCiMjIFJlYWN0b21lLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KYWxsX2dwW1siejIzbm9zYl92c196MjJub3NiX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siS0VHRyJdXQojIyBLRUdHLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KYWxsX2dwW1siejIzbm9zYl92c196MjJub3NiX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0KIyMgTUYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjNub3NiX3ZzX3oyMm5vc2JfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJURiJdXQojIyBURiwgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyM25vc2JfdnNfejIybm9zYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIldQIl1dCiMjIFdpa2lQYXRod2F5cywgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyM25vc2JfdnNfejIybm9zYl91cCJdXVtbImludGVyYWN0aXZlX3Bsb3RzIl1dW1siV1AiXV0KCnBwKGZpbGUgPSAiaW1hZ2VzL3oyM25vc2JfdnNfejIybm9zYl9yZWFjdG9tZV9kb3duLnN2ZyIsCiAgIGltYWdlID0gYWxsX2dwW1siejIzbm9zYl92c196MjJub3NiX2Rvd24iXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJSRUFDIl1dLAogICBoZWlnaHQgPSAxMiwgd2lkdGggPSA5KQphbGxfZ3BbWyJ6MjNub3NiX3ZzX3oyMm5vc2JfZG93biJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0KIyMgUmVhY3RvbWUsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIGRvd24uCmFsbF9ncFtbInoyM25vc2JfdnNfejIybm9zYl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0KIyMgTUYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIGRvd24uCmFsbF9ncFtbInoyM25vc2JfdnNfejIybm9zYl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siVEYiXV0KIyMgVEYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIGRvd24uCmBgYAoKIyMjIHoyLjMgLyB6Mi4yIHdpdGggZHJ1ZwoKYGBge3J9CnoyM3NiX3ZzX3oyMnNiX3ZvbGNhbm9bWyJwbG90Il1dICsKICB4bGltKC0xMCwgMTApICsKICB5bGltKDAsIDYwKQoKcHAoCiAgZmlsZSA9ICJpbWFnZXMvejIzc2JfdnNfejIyc2JfcmVhY3RvbWVfdXAucG5nIiwKICBpbWFnZSA9IGFsbF9ncFtbInoyM3NiX3ZzX3oyMnNiX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siUkVBQyJdXSwKICBoZWlnaHQgPSAxMiwgd2lkdGggPSA5KQphbGxfZ3BbWyJ6MjNzYl92c196MjJzYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0KIyMgUmVhY3RvbWUsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjNzYl92c196MjJzYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIktFR0ciXV0KIyMgS0VHRywgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyM3NiX3ZzX3oyMnNiX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0KIyMgTUYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjNzYl92c196MjJzYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlRGIl1dCiMjIFRGLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KYWxsX2dwW1siejIzc2JfdnNfejIyc2JfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJXUCJdXQojIyBXaWtpUGF0aHdheXMsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjNzYl92c196MjJzYl91cCJdXVtbImludGVyYWN0aXZlX3Bsb3RzIl1dW1siV1AiXV0KCmFsbF9ncFtbInoyM3NiX3ZzX3oyMnNiX2Rvd24iXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJSRUFDIl1dCiMjIFJlYWN0b21lLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCBkb3duLgphbGxfZ3BbWyJ6MjNzYl92c196MjJzYl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0KIyMgTUYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIGRvd24uCmFsbF9ncFtbInoyM3NiX3ZzX3oyMnNiX2Rvd24iXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJURiJdXQojIyBURiwgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgZG93bi4KYGBgCgojIyMgVmVubiB0byBzZWUgc2hhcmVkL3VuaXF1ZSBnZW5lcwoKT25jZSBhZ2FpbiBJIHdpc2ggdG8gcHVsbCBvdXQgdGhlIHNpZ25pZmljYW50IGdlbmVzIGFuZCBzZWUgaG93IG15Cm51bWJlcnMgbWF0Y2ggYWdhaW5zdCB0aGUgdGV4dC4KCmBgYHtyfQpzaGFyZWQgPC0gVmVubmVyYWJsZTo6VmVubihsaXN0KAogICJkcnVnIiA9IHJvd25hbWVzKGhzX21hY3Jfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJ6MjNzYl92c196MjJzYiJdXSksCiAgIm5vZHJ1ZyIgPSByb3duYW1lcyhoc19tYWNyX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1siejIzbm9zYl92c196MjJub3NiIl1dKSkpCnBwKGZpbGUgPSAiaW1hZ2VzL2RydWdfbm9kcnVnX3Zlbm5fdXAucG5nIikKVmVubmVyYWJsZTo6cGxvdChzaGFyZWQpCmRldi5vZmYoKQpWZW5uZXJhYmxlOjpwbG90KHNoYXJlZCkKCnNoYXJlZCA8LSBWZW5uZXJhYmxlOjpWZW5uKAogIGxpc3QoImRydWciID0gcm93bmFtZXMoaHNfbWFjcl9zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1siejIzc2JfdnNfejIyc2IiXV0pLAogICAgICAgIm5vZHJ1ZyIgPSByb3duYW1lcyhoc19tYWNyX3NpZ1tbImRlc2VxIl1dW1siZG93bnMiXV1bWyJ6MjNub3NiX3ZzX3oyMm5vc2IiXV0pKSkKcHAoZmlsZSA9ICJpbWFnZXMvZHJ1Z19ub2RydWdfdmVubl9kb3duLnBuZyIpClZlbm5lcmFibGU6OnBsb3Qoc2hhcmVkKQpkZXYub2ZmKCkKVmVubmVyYWJsZTo6cGxvdChzaGFyZWQpCmBgYAoKQSBzbGlnaHRseSBkaWZmZXJlbnQgd2F5IG9mIGxvb2tpbmcgYXQgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHR3bwp6eW1vZGVtZSBpbmZlY3Rpb25zIGlzIHRvIGRpcmVjdGx5IGNvbXBhcmUgdGhlIGluZmVjdGVkIHNhbXBsZXMgd2l0aAphbmQgd2l0aG91dCBkcnVnLiAgVGh1cywgd2hlbiBhIHZvbGNhbm8gcGxvdCBzaG93aW5nIHRoZSBjb21wYXJpc29uIG9mCnRoZSB6eW1vZGVtZSAyLjMgdnMuIDIuMiBzYW1wbGVzIHdhcyBwbG90dGVkLCA0ODQgZ2VuZXMgd2VyZSBvYnNlcnZlZAphcyBpbmNyZWFzZWQgYW5kIDQyMiBkZWNyZWFzZWQ7IHRoZXNlIGdyb3VwcyBpbmNsdWRlIG1hbnkgb2YgdGhlIHNhbWUKaW5mbGFtbWF0b3J5ICh1cCkgYW5kIG1lbWJyYW5lIChkb3duKSBnZW5lcy4KClNpbWlsYXIgcGF0dGVybnMgd2VyZSBvYnNlcnZlZCB3aGVuIHRoZSBhbnRpbW9uaWFsIHdhcyBpbmNsdWRlZC4KVGh1cywgd2hlbiBhIFZlbm4gZGlhZ3JhbSBvZiB0aGUgdHdvIHNldHMgb2YgaW5jcmVhc2VkIGdlbmVzIHdhcwpwbG90dGVkLCBhIHNpZ25pZmljYW50IG51bWJlciBvZiB0aGUgZ2VuZXMgd2FzIG9ic2VydmVkIGFzIGluY3JlYXNlZAooMzEzKSBhbmQgZGVjcmVhc2VkICgyNDQpIGluIGJvdGggdGhlIHVudHJlYXRlZCBhbmQgYW50aW1vbmlhbCB0cmVhdGVkCnNhbXBsZXMuCgojIyBEcnVnIGVmZmVjdHMgb24gZWFjaCB6eW1vZGVtZSBpbmZlY3Rpb24KCkFub3RoZXIgbGlrZWx5IHF1ZXN0aW9uIGlzIHRvIGRpcmVjdGx5IGNvbXBhcmUgdGhlIHRyZWF0ZWQgdnMKdW50cmVhdGVkIHNhbXBsZXMgZm9yIGVhY2ggenltb2RlbWUgaW5mZWN0aW9uIGluIG9yZGVyIHRvIHZpc3VhbGl6ZQp0aGUgZWZmZWN0cyBvZiBhbnRpbW9uaWFsLgoKIyMjIHoyLjMgd2l0aCBhbmQgd2l0aG91dCBkcnVnCgpgYGB7cn0KejIzc2JfdnNfejIzbm9zYl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19kZSgKICB0YWJsZSA9IGhzX21hY3JfdGFibGVbWyJkYXRhIl1dW1siejIzc2JfdnNfejIzbm9zYiJdXSwKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBzaGFwZXNfYnlfc3RhdGUgPSBGQUxTRSwgY29sb3JfYnkgPSAiZmMiLCAgbGFiZWwgPSAxMCwgbGFiZWxfY29sdW1uID0gImhnbmNzeW1ib2wiKQpwbG90bHk6OmdncGxvdGx5KHoyM3NiX3ZzX3oyM25vc2Jfdm9sY2Fub1tbInBsb3QiXV0pCnoyMnNiX3ZzX3oyMm5vc2Jfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fZGUoCiAgdGFibGUgPSBoc19tYWNyX3RhYmxlW1siZGF0YSJdXVtbInoyMnNiX3ZzX3oyMm5vc2IiXV0sCiAgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgc2hhcGVzX2J5X3N0YXRlID0gRkFMU0UsIGNvbG9yX2J5ID0gImZjIiwgIGxhYmVsID0gMTAsIGxhYmVsX2NvbHVtbiA9ICJoZ25jc3ltYm9sIikKcGxvdGx5OjpnZ3Bsb3RseSh6MjJzYl92c196MjJub3NiX3ZvbGNhbm9bWyJwbG90Il1dKQpgYGAKCmBgYHtyfQp6MjNzYl92c196MjNub3NiX3ZvbGNhbm9bWyJwbG90Il1dICsKICB4bGltKC04LCA4KSArCiAgeWxpbSgwLCAyMTApCgpwcChmaWxlID0gImltYWdlcy96MjNzYl92c196MjNub3NiX3JlYWN0b21lX3VwLnBuZyIsCiAgIGltYWdlID0gYWxsX2dwW1siejIzc2JfdnNfejIzbm9zYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0sCiAgIGhlaWdodCA9IDEyLCB3aWR0aCA9IDkpCmFsbF9ncFtbInoyM3NiX3ZzX3oyM25vc2JfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJSRUFDIl1dCiMjIFJlYWN0b21lLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KYWxsX2dwW1siejIzc2JfdnNfejIzbm9zYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIktFR0ciXV0KIyMgS0VHRywgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyM3NiX3ZzX3oyM25vc2JfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJNRiJdXQojIyBNRiwgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyM3NiX3ZzX3oyM25vc2JfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJURiJdXQojIyBURiwgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyM3NiX3ZzX3oyM25vc2JfdXAiXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJXUCJdXQojIyBXaWtpUGF0aHdheXMsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjNzYl92c196MjNub3NiX3VwIl1dW1siaW50ZXJhY3RpdmVfcGxvdHMiXV1bWyJXUCJdXQoKYWxsX2dwW1siejIzc2JfdnNfejIzbm9zYl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siUkVBQyJdXQojIyBSZWFjdG9tZSwgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgZG93bi4KYWxsX2dwW1siejIzc2JfdnNfejIzbm9zYl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siTUYiXV0KIyMgTUYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIGRvd24uCmFsbF9ncFtbInoyM3NiX3ZzX3oyM25vc2JfZG93biJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlRGIl1dCiMjIFRGLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCBkb3duLgpgYGAKCiMjIyB6Mi4yIHdpdGggYW5kIHdpdGhvdXQgZHJ1ZwoKYGBge3J9CnoyMnNiX3ZzX3oyMm5vc2Jfdm9sY2Fub1tbInBsb3QiXV0gKwogIHhsaW0oLTgsIDgpICsKICB5bGltKDAsIDIxMCkKCnBwKGZpbGUgPSAiaW1hZ2VzL3oyMnNiX3ZzX3oyMm5vc2JfcmVhY3RvbWVfdXAucG5nIiwKICAgaW1hZ2UgPSBhbGxfZ3BbWyJ6MjJzYl92c196MjJub3NiX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siUkVBQyJdXSwKICAgaGVpZ2h0ID0gMTIsIHdpZHRoID0gOSkKYWxsX2dwW1siejIyc2JfdnNfejIybm9zYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlJFQUMiXV0KIyMgUmVhY3RvbWUsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIHVwLgphbGxfZ3BbWyJ6MjJzYl92c196MjJub3NiX3VwIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siS0VHRyJdXQojIyBLRUdHLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KYWxsX2dwW1siejIyc2JfdnNfejIybm9zYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIk1GIl1dCiMjIE1GLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KYWxsX2dwW1siejIyc2JfdnNfejIybm9zYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIlRGIl1dCiMjIFRGLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCB1cC4KYWxsX2dwW1siejIyc2JfdnNfejIybm9zYl91cCJdXVtbInB2YWx1ZV9wbG90cyJdXVtbIldQIl1dCiMjIFdpa2lQYXRod2F5cywgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgdXAuCmFsbF9ncFtbInoyMnNiX3ZzX3oyMm5vc2JfdXAiXV1bWyJpbnRlcmFjdGl2ZV9wbG90cyJdXVtbIldQIl1dCgphbGxfZ3BbWyJ6MjJzYl92c196MjJub3NiX2Rvd24iXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJSRUFDIl1dCiMjIFJlYWN0b21lLCB6eW1vZGVtZTIuMyB3aXRob3V0IGRydWcgdnMuIHVuaW5mZWN0ZWQgd2l0aG91dCBkcnVnLCBkb3duLgphbGxfZ3BbWyJ6MjJzYl92c196MjJub3NiX2Rvd24iXV1bWyJwdmFsdWVfcGxvdHMiXV1bWyJNRiJdXQojIyBNRiwgenltb2RlbWUyLjMgd2l0aG91dCBkcnVnIHZzLiB1bmluZmVjdGVkIHdpdGhvdXQgZHJ1ZywgZG93bi4KYWxsX2dwW1siejIyc2JfdnNfejIybm9zYl9kb3duIl1dW1sicHZhbHVlX3Bsb3RzIl1dW1siVEYiXV0KIyMgVEYsIHp5bW9kZW1lMi4zIHdpdGhvdXQgZHJ1ZyB2cy4gdW5pbmZlY3RlZCB3aXRob3V0IGRydWcsIGRvd24uCmBgYAoKIyMjIFNoYXJlZCBhbmQgdW5pcXVlIGdlbmVzIGFmdGVyL2JlZm9yZSBkcnVnCgpgYGB7cn0Kc2hhcmVkIDwtIFZlbm5lcmFibGU6OlZlbm4obGlzdCgKICAiejIzIiA9IHJvd25hbWVzKGhzX21hY3Jfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJ6MjNzYl92c196MjNub3NiIl1dKSwKICAiejIyIiA9IHJvd25hbWVzKGhzX21hY3Jfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJ6MjJzYl92c196MjJub3NiIl1dKSkpCnBwKGZpbGUgPSAiaW1hZ2VzL3oyM196MjJfZHJ1Z192ZW5uX3VwLnBuZyIpClZlbm5lcmFibGU6OnBsb3Qoc2hhcmVkKQpkZXYub2ZmKCkKVmVubmVyYWJsZTo6cGxvdChzaGFyZWQpCgpzaGFyZWQgPC0gVmVubmVyYWJsZTo6VmVubihsaXN0KAogICJ6MjMiID0gcm93bmFtZXMoaHNfbWFjcl9zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1siejIzc2JfdnNfejIzbm9zYiJdXSksCiAgInoyMiIgPSByb3duYW1lcyhoc19tYWNyX3NpZ1tbImRlc2VxIl1dW1siZG93bnMiXV1bWyJ6MjJzYl92c196MjJub3NiIl1dKSkpCnBwKGZpbGUgPSAiaW1hZ2VzL3oyM196MjJfZHJ1Z192ZW5uX2Rvd24ucG5nIikKVmVubmVyYWJsZTo6cGxvdChzaGFyZWQpCmRldi5vZmYoKQpWZW5uZXJhYmxlOjpwbG90KHNoYXJlZCkKYGBgCgpOb3RlOiBJIGFtIHNldHRpZyB0aGUgeCBhbmQgeS1heGlzIGJvdW5kYXJpZXMgYnkgYWxsb3dpbmcgdGhlIHBsb3R0ZXIKdG8gcGljayBpdHMgb3duIGF4aXMgdGhlIGZpcnN0IHRpbWUsIHdyaXRpbmcgZG93biB0aGUgcmFuZ2VzIEkKb2JzZXJ2ZSwgYW5kIHRoZW4gc2V0dGluZyB0aGVtIHRvIHRoZSBsYXJnZXN0IG9mIHRoZSBwYWlyLiAgSXQgaXMKdGhlcmVmb3JlIHBvc3NpYmxlIHRoYXQgSSBtaXNzZWQgb25lIG9yIG1vcmUgZ2VuZXMgd2hpY2ggbGllcyBvdXRzaWRlCnRoYXQgcmFuZ2UuCgpUaGUgcHJldmlvdXMgcGxvdHRlZCBjb250cmFzdHMgc291Z2h0IHRvIHNob3cgY2hhbmdlcyBiZXR3ZWVuIHRoZSB0d28Kc3RyYWlucyB6Mi4zIGFuZCB6Mi4yLiAgQ29udmVyc2VseSwgdGhlIHByZXZpb3VzIHZvbGNhbm8gcGxvdHMgc2VlayB0bwpkaXJlY3RseSBjb21wYXJlIGVhY2ggc3RyYWluIGJlZm9yZS9hZnRlciBkcnVnIHRyZWF0bWVudC4KCiMgTFJUIG9mIHRoZSBIdW1hbiBNYWNyb3BoYWdlCgpBIHNsaWdodGx5IGRpZmZlcmVudCB0YWNrIHRvIGV4YW1pbmUgdGhlIGRhdGEgaXMgdG8gcGVyZm9ybSBhCmxpa2VsaWhvb2QgcmF0aW8gdGVzdCBpbiBvcmRlciB0byBsb29rIGZvciB0cmVuZHMgd2hpY2ggYXJlIHNoYXJlZAphbW9uZyBnZW5lcyB3aGVuIGV4YW1pbmluZyBkaWZmZXJlbnQgY29uZGl0aW9ucyBpbiB0aGUgZGF0YS4KCmBgYHtyfQp0bXJjMl9scnRfc3RyYWluX2RydWcgPC0gZGVzZXFfbHJ0KGhzX21hY3IsIGludGVyYWN0b3JfY29sdW1uID0gImRydWciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyZXN0X2NvbHVtbiA9ICJtYWNyb3BoYWdlenltb2RlbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY3RvcnMgPSBjKCJkcnVnIiwgIm1hY3JvcGhhZ2V6eW1vZGVtZSIpKQp0bXJjMl9scnRfc3RyYWluX2RydWdbWyJjbHVzdGVyX2RhdGEiXV1bWyJwbG90Il1dCmBgYAoKIyBQYXJhc2l0ZQoKTGV0IHVzIGNvbnNpZGVyIGZvciBhIG1vbWVudCBkaWZmZXJlbmNlcyBhbW9uZyB0aGUgcGFyYXNpdGUKdHJhbnNjcmlwdG9tZXMgZm9yIHRoZSBzYW1wbGVzIHdoaWNoIHdlcmUgbm90IGRydWcgdHJlYXRlZC4KCk9uZSB0aGluZyBJIGRpZCBpbiB0aGUgaW5pdGlhbCBpbXBsZW1lbnRhdGlvbiBvZiB0aGlzIGRvY3VtZW50IHdhcyB0bwpyZXBlYXQgdGhlIHZhcmlhYmxlICd1cF9nZW5lcycgZm9yIGVhY2ggY29tcGFyaXNvbjsgSSB0aGluayB0aGlzIHRpbWUKSSB3aWxsIG1ha2UgYSBkaWZmZXJlbnQgdmFyaWFibGUgZm9yIGVhY2ggY29tcGFyaXNvbiBzbyBJIGNhbiBwbGF5CndpdGggdGhlbSBhIGxpdHRsZSBmdXJ0aGVyLgoKYGBge3J9CmNvbXBhcmlzb24gPC0gInoyM192c196MjIiCmxwX21hY3JvcGhhZ2VfZGUgPC0gYWxsX3BhaXJ3aXNlKGxwX21hY3JvcGhhZ2Vfbm9zYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfc3ZzID0gInN2YXNlcSIsIGZpbHRlciA9IFRSVUUpCnRtcmMyX3BhcmFzaXRlX2tlZXBlcnMgPC0gbGlzdCgKICAiejIzX3ZzX3oyMiIgPSBjKCJ6MjMiLCAiejIyIikpCmxwX21hY3JvcGhhZ2VfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgbHBfbWFjcm9waGFnZV9kZSwga2VlcGVycyA9IHRtcmMyX3BhcmFzaXRlX2tlZXBlcnMsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2RlX3RhYmxlcy9wYXJhc2l0ZV9pbmZlY3Rpb25fZGUtdnt2ZXJ9Lnhsc3giKSkKbHBfbWFjcm9waGFnZV9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBscF9tYWNyb3BoYWdlX3RhYmxlLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9zaWdfdGFibGVzL3BhcmFzaXRlX3NpZy12e3Zlcn0ueGxzeCIpKQoKbHBfbWFjcm9waGFnZV90YWJsZVtbInBsb3RzIl1dW1tjb21wYXJpc29uXV1bWyJkZXNlcV92b2xfcGxvdHMiXV0KbHBfbWFjcm9waGFnZV90YWJsZVtbInBsb3RzIl1dW1tjb21wYXJpc29uXV1bWyJkZXNlcV9tYV9wbG90cyJdXQoKdXBfZ2VuZXNfejIzejIyIDwtIGxwX21hY3JvcGhhZ2Vfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bW2NvbXBhcmlzb25dXQpkaW0odXBfZ2VuZXNfejIzejIyKQpkb3duX2dlbmVzX3oyM3oyMiA8LSBscF9tYWNyb3BoYWdlX3NpZ1tbImRlc2VxIl1dW1siZG93bnMiXV1bW2NvbXBhcmlzb25dXQpkaW0oZG93bl9nZW5lc196MjN6MjIpCmBgYAoKYGBge3IgcGFyYXNpdGVfdm9sY2Fub30KbHBfejIzc2JfdnNfejIyc2Jfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fZGUoCiAgdGFibGUgPSBscF9tYWNyb3BoYWdlX3RhYmxlW1siZGF0YSJdXVtbInoyM192c196MjIiXV0sCiAgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgc2hhcGVzX2J5X3N0YXRlID0gRkFMU0UsIGNvbG9yX2J5ID0gImZjIiwgIGxhYmVsID0gMTAsIGxhYmVsX2NvbHVtbiA9ICJoZ25jc3ltYm9sIikKcGxvdGx5OjpnZ3Bsb3RseShscF96MjNzYl92c196MjJzYl92b2xjYW5vW1sicGxvdCJdXSkKbHBfejIzc2JfdnNfejIyc2Jfdm9sY2Fub1tbInBsb3QiXV0KYGBgCgojIyBPbnRvbG9neSBzZWFyY2gKCkFuIGltcG9ydGFudCBub3RlLCBJIHJlY2VudGx5IGFkZGVkIGEgbWluaW11bSBjcm9zc3JlZmVyZW5jZSBhcmd1bWVudAooZGVmYXVsdGluZyB0byA0MCBnZW5lcyksIHdoaWNoIGNhdXNlcyBsb3RzIG9mIGNvbXBhcmlzb25zIHRvIGZhaWwgZm9yCnBvb3JseSBhbm5vdGF0ZWQgZ2Vub21lcyAobGlrZSBwYW5hbWVuc2lzLikgIFRodXMsIEkgYW0gcmVsYXhpbmcgdGhhdApjb25zdHJhaW50IGZvciB0aGVzZSBzZWFyY2hlcy4KCmBgYHtyfQpscF9sZW5ndGhzIDwtIGFsbF9scF9hbm5vdFssIGMoImdpZCIsICJhbm5vdF9jZHNfbGVuZ3RoIildCmNvbG5hbWVzKGxwX2xlbmd0aHMpICA8LSBjKCJJRCIsICJsZW5ndGgiKQoKdXBfZ29zZXEgPC0gc2ltcGxlX2dvc2VxKHVwX2dlbmVzX3oyM3oyMiwgZ29fZGIgPSBscF9nbywKICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aF9kYiA9IGxwX2xlbmd0aHMsIG1pbl94cmVmID0gMTUpCiMjIFZpZXcgY2F0ZWdvcmllcyBvdmVyIHJlcHJlc2VudGVkIGluIHRoZSAyLjMgc2FtcGxlcwp1cF9nb3NlcVtbInB2YWx1ZV9wbG90cyJdXVtbImJwcF9wbG90X292ZXIiXV0KZG93bl9nb3NlcSA8LSBzaW1wbGVfZ29zZXEoZG93bl9nZW5lc196MjN6MjIsIGdvX2RiID0gbHBfZ28sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aF9kYiA9IGxwX2xlbmd0aHMsIG1pbl94cmVmID0gMTUpCiMjIFZpZXcgY2F0ZWdvcmllcyBvdmVyIHJlcHJlc2VudGVkIGluIHRoZSAyLjIgc2FtcGxlcwpkb3duX2dvc2VxW1sicHZhbHVlX3Bsb3RzIl1dW1siYnBwX3Bsb3Rfb3ZlciJdXQoKY3JlYXRlZCA8LSBkaXIuY3JlYXRlKGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvZ29zZXFfcGFyYXNpdGUiKSkKd3JpdHRlbl9nb3NlcSA8LSB3cml0ZV9nb3NlcV9kYXRhKAogIHVwX2dvc2VxLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9nb3NlcV9wYXJhc2l0ZS9scF9tYWNyb3BoYWdlX2luY3JlYXNlZF96Mi4zX2dvc2VxLXZ7dmVyfS54bHN4IikpCndyaXR0ZW5fZ29zZXEgPC0gd3JpdGVfZ29zZXFfZGF0YSgKICBkb3duX2dvc2VxLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9nb3NlcV9wYXJhc2l0ZS9scF9tYWNyb3BoYWdlX2luY3JlYXNlZF96Mi4yX2dvc2VxLXZ7dmVyfS54bHN4IikpCmBgYAoKIyBHU1ZBCgpOb3RlOiBUaGUgZm9sbG93aW5nIGJsb2NrIGFzc3VtZXMgb25lIGlzIGFibGUgdG8gZG93bmxvYWQgYSBmcmVzaCBjb3B5Cm9mIG1zaWdEQiwgd2hpY2ggSSBhbSBub3Qgc3VyZSBpcyBwb3NzaWJsZSB3aXRoaW4gdGhlIGNvbnN0cmFpbnRzIG9mIGEKY29udGFpbmVyIChJIG1lYW4gaXQgaXMgdHJpdmlhbCB0byBkbywgYnV0IEkgYW0gbm90IHN1cmUgaWYgaXQgaXMgb2sKZHVlIHRvIGxpY2Vuc2luZykuICBIb3dldmVyLCBCcm9hZCBwcm92aWRlcyBhIGRhdGEgcGFja2FnZSBvZiBhIG1zaWdkYgpyZWxlYXNlLiAgQXMgYSByZXN1bHQsIHRoZSBmb2xsb3dpbmcgYmxvY2sgd2lsbCBiZSByZXBlYXRlZCB1c2luZyB0aGF0LgoKYGBge3IsIGV2YWw9RkFMU0V9CmhzX2luZmVjdGVkIDwtIHN1YnNldF9zZShoc19tYWNyb3BoYWdlLCBzdWJzZXQgPSAibWFjcm9waGFnZXRyZWF0bWVudCE9J3VuaW5mJyIpICU+JQogIHN1YnNldF9zZShzdWJzZXQgPSAibWFjcm9waGFnZXRyZWF0bWVudCE9J3VuaW5mX3NiJyIpCmhzX2dzdmFfYzIgPC0gc2ltcGxlX2dzdmEoaHNfaW5mZWN0ZWQpCmhzX2dzdmFfYzJfbWV0YSA8LSBnZXRfbXNpZ2RiX21ldGFkYXRhKGhzX2dzdmFfYzIsIG1zaWdfeG1sID0gInJlZmVyZW5jZS9tc2lnZGJfdjcuMi54bWwiKQpoc19nc3ZhX2MyX3NpZyA8LSBnZXRfc2lnX2dzdmFfY2F0ZWdvcmllcygKICBoc19nc3ZhX2MyX21ldGEsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL2dzdmEvaHNfbWFjcm9waGFnZV9nc3ZhX2MyX3NpZy54bHN4IikpCmhzX2dzdmFfYzJfc2lnW1sicmF3X3Bsb3QiXV0KCmhzX2dzdmFfYzcgPC0gc2ltcGxlX2dzdmEoaHNfaW5mZWN0ZWQsIHNpZ25hdHVyZV9jYXRlZ29yeSA9ICJjNyIpCmhzX2dzdmFfYzdfbWV0YSA8LSBnZXRfbXNpZ2RiX21ldGFkYXRhKGhzX2dzdmFfYzcsIG1zaWdfeG1sID0gInJlZmVyZW5jZS9tc2lnZGJfdjcuMi54bWwiKQpoc19nc3ZhX2M3X3NpZyA8LSBnZXRfc2lnX2dzdmFfY2F0ZWdvcmllcygKICBoc19nc3ZhX2M3LAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9nc3ZhL2hzX21hY3JvcGhhZ2VfZ3N2YV9jN19zaWcueGxzeCIpKQpoc19nc3ZhX2M3X3NpZ1tbInJhd19wbG90Il1dCmBgYAoKIyMgUmVwZWF0IHVzaW5nIHRoZSBHU1ZBZGF0YSBwYWNrYWdlLgoKYGBge3J9CmhzX2luZmVjdGVkIDwtIHN1YnNldF9zZShoc19tYWNyb3BoYWdlLCBzdWJzZXQgPSAibWFjcm9waGFnZXRyZWF0bWVudCE9J3VuaW5mJyIpICU+JQogIHN1YnNldF9zZShzdWJzZXQgPSAibWFjcm9waGFnZXRyZWF0bWVudCE9J3VuaW5mX3NiJyIpCmhzX2dzdmFfYzIgPC0gc2ltcGxlX2dzdmEoaHNfaW5mZWN0ZWQpCiMjaHNfZ3N2YV9jMl9tZXRhIDwtIGdldF9tc2lnZGJfbWV0YWRhdGEoaHNfZ3N2YV9jMiwgbXNpZ194bWw9InJlZmVyZW5jZS9tc2lnZGJfdjcuMi54bWwiKQpoc19nc3ZhX2MyX3NpZyA8LSBnZXRfc2lnX2dzdmFfY2F0ZWdvcmllcygKICBoc19nc3ZhX2MyLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS9nc3ZhL2hzX21hY3JvcGhhZ2VfZ3N2YV9jMl9zaWcueGxzeCIpKQpoc19nc3ZhX2MyX3NpZ1tbInJhd19wbG90Il1dCgpoc19nc3ZhX2M3IDwtIHNpbXBsZV9nc3ZhKGhzX2luZmVjdGVkLCBzaWduYXR1cmVfY2F0ZWdvcnkgPSAiYzciKQojI2hzX2dzdmFfYzdfbWV0YSA8LSBnZXRfbXNpZ2RiX21ldGFkYXRhKGhzX2dzdmFfYzcsIG1zaWdfeG1sPSJyZWZlcmVuY2UvbXNpZ2RiX3Y3LjIueG1sIikKaHNfZ3N2YV9jN19zaWcgPC0gZ2V0X3NpZ19nc3ZhX2NhdGVnb3JpZXMoCiAgaHNfZ3N2YV9jNywKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUvZ3N2YS9oc19tYWNyb3BoYWdlX2dzdmFfYzdfc2lnLnhsc3giKSkKaHNfZ3N2YV9jN19zaWdbWyJyYXdfcGxvdCJdXQpgYGAKCiMgVHJ5IG91dCBhIG5ldyB0b29sCgpUd28gcmVhc29uczogTmFqaWIgbG92ZXMgaGltIHNvbWUgUENBLCB0aGlzIHVzZXMgd2lraXBhdGh3YXlzLCB3aGljaCBpcyBzb21ldGhpbmcgSSB0aGluayBpcyBuZWF0LgoKT2ssIEkgc3BlbnQgc29tZSB0aW1lIGxvb2tpbmcgdGhyb3VnaCB0aGUgY29kZSBhbmQgSSBoYXZlIHNvbWUKcHJvYmxlbXMgd2l0aCBzb21lIG9mIHRoZSBkZXNpZ24gZGVjaXNpb25zLgoKTW9zdCBpbXBvcnRhbnRseSwgaXQgcmVxdWlyZXMgYSBkYXRhLmZyYW1lKCkgd2hpY2ggaGFzIHRoZSBmb2xsb3dpbmcgZm9ybWF0OgoKMS4gIE5vIHJvd25hbWVzLCBpbnN0ZWFkIGNvbHVtbiAjMSBpcyB0aGUgc2FtcGxlIElELgoyLiAgQ29sdW1ucyAyLW0gYXJlIHRoZSBjYXRlZ29yaWNhbC9zdXJ2aXZhbC9ldGMgbWV0cmljcy4KMy4gIENvbHVtbnMgbS1uIGFyZSAxIGdlbmUtcGVyLWNvbHVtbiB3aXRoIGxvZzIgdmFsdWVzLgoKQnV0IHdoZW4gSSB0aGluayBhYm91dCBpdCBJIHRoaW5rIEkgZ2V0IHRoZSBpZGVhLCB0aGV5IHdhbnQgdG8gYmUgYWJsZSB0byBkbyBtb2RlbGxpbmcgc3R1ZmYKbW9yZSBlYXNpbHkgd2l0aCByZXNwb25zZSBmYWN0b3JzLgoKYGBge3J9CmxpYnJhcnkocGF0aHdheVBDQSkKbGlicmFyeShyV2lraVBhdGh3YXlzKQoKZG93bmxvYWRlZCA8LSBkb3dubG9hZFBhdGh3YXlBcmNoaXZlKG9yZ2FuaXNtID0gIkhvbW8gc2FwaWVucyIsIGZvcm1hdCA9ICJnbXQiKQpkYXRhX3BhdGggPC0gc3lzdGVtLmZpbGUoImV4dGRhdGEiLCBwYWNrYWdlID0gInBhdGh3YXlQQ0EiKQp3aWtpcGF0aHdheXMgPC0gcmVhZF9nbXQocGFzdGUwKGRhdGFfcGF0aCwgIi93aWtpcGF0aHdheXNfaHVtYW5fc3ltYm9sLmdtdCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24gPSBUUlVFKQoKZXhwdCA8LSBzdWJzZXRfc2UoaHNfbWFjcm9waGFnZSwgc3Vic2V0ID0gIm1hY3JvcGhhZ2V0cmVhdG1lbnQhPSd1bmluZiciKSAlPiUKICBzdWJzZXRfc2Uoc3Vic2V0ID0gIm1hY3JvcGhhZ2V0cmVhdG1lbnQhPSd1bmluZl9zYiciKQpleHB0IDwtIHNldF9leHB0X2NvbmRpdGlvbnMoZXhwdCwgZmFjdCA9ICJtYWNyb3BoYWdlenltb2RlbWUiKQpzeW1ib2xfY29sdW1uIDwtICJoZ25jX3N5bWJvbCIKc3ltYm9sX3ZlY3RvciA8LSBmRGF0YShleHB0KVtbc3ltYm9sX2NvbHVtbl1dCm5hbWVzKHN5bWJvbF92ZWN0b3IpIDwtIHJvd25hbWVzKGZEYXRhKGV4cHQpKQpzeW1ib2xfZGYgPC0gYXMuZGF0YS5mcmFtZShzeW1ib2xfdmVjdG9yKQoKYXNzYXlfZGYgPC0gbWVyZ2Uoc3ltYm9sX2RmLCBhcy5kYXRhLmZyYW1lKGV4cHJzKGV4cHQpKSwgYnkgPSAicm93Lm5hbWVzIikKYXNzYXlfZGZbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApyb3duYW1lcyhhc3NheV9kZikgPC0gbWFrZS5uYW1lcyhhc3NheV9kZltbInN5bWJvbF92ZWN0b3IiXV0sIHVuaXF1ZSA9IFRSVUUpCmFzc2F5X2RmW1sic3ltYm9sX3ZlY3RvciJdXSA8LSBOVUxMCmFzc2F5X2RmIDwtIGFzLmRhdGEuZnJhbWUodChhc3NheV9kZikpCmFzc2F5X2RmW1siU2FtcGxlSUQiXV0gPC0gcm93bmFtZXMoYXNzYXlfZGYpCmFzc2F5X2RmIDwtIGRwbHlyOjpzZWxlY3QoYXNzYXlfZGYsICJTYW1wbGVJRCIsIGV2ZXJ5dGhpbmcoKSkKCmZhY3Rvcl9kZiA8LSBhcy5kYXRhLmZyYW1lKHBEYXRhKGV4cHQpKQpmYWN0b3JfZGZbWyJTYW1wbGVJRCJdXSA8LSByb3duYW1lcyhmYWN0b3JfZGYpCmZhY3Rvcl9kZiA8LSBkcGx5cjo6c2VsZWN0KGZhY3Rvcl9kZiwgIlNhbXBsZUlEIiwgZXZlcnl0aGluZygpKQpmYWN0b3JfZGYgPC0gZmFjdG9yX2RmWywgYygiU2FtcGxlSUQiLCBmYWN0b3JzKV0KCnR0IDwtIENyZWF0ZU9taWNzKAogIGFzc2F5RGF0YV9kZiA9IGFzc2F5X2RmLAogIHBhdGh3YXlDb2xsZWN0aW9uX2xzID0gd2lraXBhdGh3YXlzLAogIHJlc3BvbnNlID0gZmFjdG9yX2RmLAogIHJlc3BUeXBlID0gImNhdGVnb3JpY2FsIiwKICBtaW5QYXRoU2l6ZSA9IDUpCgpzdXBlciA8LSBBRVNQQ0FfcFZhbHMoCiAgb2JqZWN0ID0gdHQsCiAgbnVtUENzID0gMiwKICBwYXJhbGxlbCA9IEZBTFNFLAogIG51bUNvcmVzID0gOCwKICBudW1SZXBzID0gMiwKICBhZGp1c3RtZW50ID0gIkJIIikKYGBgCgojIEV2YWx1YXRpbmcgYSBsb2cyRkMgYmFycGxvdAoKRmlndXJlIDJFIGlzIG5vdyBjb21wcmlzZWQgb2YgYSBwbG90IHdoaWNoIHNob3dzIGxvZzJGQyB2YWx1ZXMgd2l0aAplcnJvciBiYXJzIGZvciBzZWxlY3RlZCBnZW5lcyBhbmQgc2Vla3MgdG8gc2hvdyBkaWZmZXJlbmNlcyBiZXR3ZWVuCjIuMy91bmluZmVjdGVkIGFuZCAyLjIvdW5pbmZlY3RlZC4KCkhlcmUgaXMgdGhlIHRhYmxlIE9sZ2EgdXNlZCB0byBnZW5lcmF0ZSBpdDoKCkkgd2VudCBsb29raW5nIGluIHRoZSB4bHN4IGZpbGVzIHByb2R1Y2VkIGluIDIwMjQwNSBhbmQgZm91bmQgdGhhdAp0aGVzZSBhcmUgdGhlIGxvZzJGQyB2YWx1ZXMgYW5kIHN0YW5kYXJkIGVycm9ycyBwcm9kdWNlZCBieSBERVNlcTIuCgpJdCBzaG91bGQgYmUgbm90ZWQgdGhhdCBpbiBteSBtb3N0IHJlY2VudCB2ZXJzaW9uIG9mIHRoZXNlIGFuYWx5c2VzLAp0aGVzZSBudW1iZXJzIGRpZCBzaGlmdCBzbGlnaHRseS4gIEkgYW0gbG9va2luZyBpbnRvIHRoYXQgbm93LgoKKiBEYXRhIHdpdG91dCBkcnVnCgoqKiAyLjMgdnMgVW5pbmZlY3RlZCBNw5ggICAgICAgICAgICAyLjIgdnMgVW5pbmZlY3RlZCBNw5gKCnwgR2VuZSB8IE1lYW4gICB8IFNFTSAgICAgIHwgbiB8IE1lYW4gIHwgU0VNICAgICB8biB8Ci0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KfElGSTI3IHwgIDcuMjI0IHwgIDAuNTY2MiAgfDYgIHwgMi43MDIgfCAgMC41NjY5IHwgNnwKfFJTQUQyIHwgIDYuMjkgIHwgIDAuNzMxMiAgfDYgIHwgMS42MjMgfCAgMC43MzAzIHwgNnwKfENDTDggIHwgIDYuMjI1IHwgIDAuOTI4ICAgfDYgIHwgLTAuMzE0fCAgMC45NDEgIHwgNnwKfElGSTQ0THwgIDUuODk1IHwgIDAuNjEyICAgfDYgIHwgMi4wNiAgfCAgMC42MTEgIHwgNnwKfE9BU0wgIHwgIDQuNzI2IHwgIDAuNDk3NCAgfDYgIHwgMS4zOTIgfCAgMC40OTczIHwgNnwKfFVTUDE4IHwgIDMuNjQ0IHwgIDAuNDgzICAgfDYgIHwgMC45OTkgfCAgMC40ODI2IHwgNnwKfElETzEgIHwgIDcuMTQ1IHwgIDEuMTA3ICAgfDYgIHwgMS4yNTcgfCAgMS4xNDEgIHwgNnwKfElETzIgIHwgIDMuOTM1IHwgIDEuMyAgICAgfDYgIHwgMi41NTcgfCAgMS4zNDEgIHwgNnwKfEtZTlUgIHwgIDEuMDcgIHwgIDAuMjE4NiAgfDYgIHwgMC4wMjA3fCAgMC4yMTg0IHwgNnwKfEFIUiAgIHwgMC45MzgyIHwgMC4yMjM2ICAgfDYgIHwgMC41MDMyfCAgMC4yMjM5IHwgNnwKfElMNEkxIHwgIDIuNTkzIHwgIDAuNDYyMyAgfDYgIHwgMC4wMzkgfCAgMC40NjE4IHwgNnwKfFNPRDIgIHwgIDIuNzYgIHwgIDAuMzQ5ICAgfDYgIHwgMC40MjQxfCAgMC4zNTI4IHwgNnwKfE5PVENIMXwgIDAuNzU3MnwgIDAuMjc1ICAgfDYgIHwgMS40OTUgfCAgMC4yNzQ0IHwgNnwKfERMTDEgIHwgIDAuODI2OHwgIDAuNTI4NSAgfDYgIHwgMy40NTUgfCAgMC41MjI4IHwgNnwKfERMTDQgIHwgIDEuMTE2IHwgIDAuNzM3ICAgfDYgIHwgNC4yNDMgfCAgMC43MSAgIHwgNnwKfEhFUzEgIHwgLTAuMDE4M3wgMC44NTk5ICAgfDYgIHwgNi41MzYgfCAgMC43OTczIHwgNnwKfEhFWTEgIHwgIDAuNTUzM3wgMC41Nzg5ICAgfDYgIHwgNC4xODEgfCAgMC42MjczIHwgNnwKCk9rLCBJIHRoaW5rIEkgZm91bmQgYSBwcm9ibGVtOiBUaGUgTk9UQ0gxIHZhbHVlIGlzIGFjdHVhbGx5IHRoZQphZGp1c3RlZCBwLXZhbHVlLgoKKiBUcmFuc3BvcnRlcnMgd2l0aG91dCBkcnVnCgoqKiAyLjMgdnMgVW5pbmZlY3RlZCBNw5ggICAgICAgICAgICAyLjIgdnMgVW5pbmZlY3RlZCBNw5gKCnwgR2VuZSB8IE1lYW4gICAgfCAgIFNFTSAgfCBufCAgTWVhbiAgIHwgU0VNICAgfCBufAotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnxBQkNCMSB8ICAtMi4zNTQgfCAwLjQ0MiAgfCA2fCAgIC0wLjQwNnwgIDAuNDMxfCA2fAp8QUJDRzQgfCAgLTMuNzE1IHwgMC42NDggIHwgNnwgICAtMC42NTN8ICAwLjYzMHwgNnwKfEFCQ0I1IHwgIC0xLjE5MiB8IDAuMzgwICB8IDZ8ICAgMS4zNTEgfCAgMC4zNjN8IDZ8CnxBQkNBOSB8ICAxLjg4MCAgfCAwLjY0OCAgfCA2fCAgIDMuNDQ0IHwgIDAuNjM3fCA2fAp8QUJDQzIgfCAgMC40NTQgIHwgMC4zMjEgIHwgNnwgICAxLjgxOCB8ICAwLjMxNHwgNnwKfEFRUDIgIHwgIC0xLjE5MSB8IDAuNTI5ICB8IDZ8ICAgMC43NDUgfCAgMC41MTR8IDZ8CnxBUVAzICB8ICAtMC45NDAgfCAwLjQwMiAgfCA2fCAgIDAuNDMxIHwgIDAuMzk1fCA2fAoKKiBUcmFuc3BvcnRlcnMgd2l0aCBkcnVnCgoqKiAyLjMgdnMgVW5pbmZlY3RlZCBNw5ggICAgICAgICAgICAyLjIgdnMgVW5pbmZlY3RlZCBNw5gKCnxHZW5lICB8IE1lYW4gICB8ICAgIFNFTSB8IG58IE1lYW4gIHwgIFNFTSAgIHwgbiB8Ci0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQp8QUJDQjEgfCAgLTAuNjk3fCAgMC4zNDkgfCA2fCAtMS4yNTV8ICAwLjMzNyB8ICA2fAp8QUJDRzQgfCAgMS4yMzEgfCAgMC41MDMgfCA2fCAwLjU0NyB8ICAwLjQ4NCB8ICA2fAp8QVFQMiAgfCAgMC44MTYgfCAgMC4zOTkgfCA2fCAwLjA0MyB8ICAwLjM4NyB8ICA2fAp8QVFQMyAgfCAgLTEuMjg2fCAgMC4zMjAgfCA2fCAtMS42MTN8ICAwLjMwOSB8ICA2fAp8QVFQOCAgfCAgMC42MzQgfCAgMC4zNzAgfCA2fCAwLjk0MyB8ICAwLjM2NSB8ICA2fAoKTGV0IHVzIG5vdyBzZWUgaWYgSSBjYW4gcmVjYXBpdHVsYXRlIHRoZSBwbG90Li4uCgpgYGB7cn0Kbm9kcnVnX2NvbnRyYXN0cyA8LSBjKCJ6MjNub3NiX3ZzX3VuaW5mIiwgInoyMm5vc2JfdnNfdW5pbmYiKQpnZW5lc19ub19kcnVnIDwtIGMoIklGSTI3IiwgIlJTQUQyIiwgIkNDTDgiLCAiSUZJNDRMIiwgIk9BU0wiLCAiVVNQMTgiLCAiSURPMSIsICJJRE8yIiwgIktZTlUiLCAiQUhSIiwgIklMNEkxIiwgIlNPRDIiLCAiTk9UQ0gxIiwgIkRMTDEiLCAiRExMNCIsICJIRVMxIiwgIkhFWTEiKQp0cmFuc3BvcnRlcnNfbm9fZHJ1ZyA8LSBjKCJBQkNCMSIsICJBQkNHNCIsICJBQkNCNSIsICJBQkNBOSIsICJBQkNDMiIsICJBUVAyIiwgIkFRUDMiKQpkcnVnX2NvbnRyYXN0cyA8LSBjKCJ6MjNzYl92c19zYiIsICJ6MjJzYl92c19zYiIpCnRyYW5zcG9ydGVyc19kcnVnIDwtIGMoIkFCQ0IxIiwgIkFCQ0c0IiwgIkFRUDIiLCAiQVFQMyIsICJBUVA4IikKYGBgCgpUaGVzZSB2YWx1ZXMgY2FtZSBvdXQgb2YgdGhlIGRhdGEgc3RydWN0dXJlIGNhbGxlZCAnaHNfbWFjcl90YWJsZScKCmBgYHtyfQp6MjNub3NiX3VuaW5mX3ZhbHVlcyA8LSBoc19tYWNyX3RhYmxlW1siZGF0YSJdXVtbInoyM25vc2JfdnNfdW5pbmYiXV0KZ2VuZV9pZHggPC0gejIzbm9zYl91bmluZl92YWx1ZXNbWyJoZ25jX3N5bWJvbCJdXSAlaW4lIGdlbmVzX25vX2RydWcKbm9kcnVnX3Jvd3MgPC0gIHoyM25vc2JfdW5pbmZfdmFsdWVzW2dlbmVfaWR4LCBdCnJvd25hbWVzKG5vZHJ1Z19yb3dzKSA8LSBub2RydWdfcm93c1tbImhnbmNfc3ltYm9sIl1dCnoyM19ub2RydWdfdmFsdWVzIDwtIG5vZHJ1Z19yb3dzWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfbGZjc2UiKV0KejIzX25vZHJ1Z192YWx1ZXMKCnoyMm5vc2JfdW5pbmZfdmFsdWVzIDwtIGhzX21hY3JfdGFibGVbWyJkYXRhIl1dW1siejIybm9zYl92c191bmluZiJdXQpnZW5lX2lkeCA8LSB6MjJub3NiX3VuaW5mX3ZhbHVlc1tbImhnbmNfc3ltYm9sIl1dICVpbiUgZ2VuZXNfbm9fZHJ1Zwpub2RydWdfcm93cyA8LSAgejIybm9zYl91bmluZl92YWx1ZXNbZ2VuZV9pZHgsIF0Kcm93bmFtZXMobm9kcnVnX3Jvd3MpIDwtIG5vZHJ1Z19yb3dzW1siaGduY19zeW1ib2wiXV0KejIyX25vZHJ1Z192YWx1ZXMgPC0gbm9kcnVnX3Jvd3NbLCBjKCJkZXNlcV9sb2dmYyIsICJkZXNlcV9sZmNzZSIpXQp6MjJfbm9kcnVnX3ZhbHVlcwoKejIzX25vZHJ1Z192YWx1ZXNbWyJzdGF0ZSJdXSA8LSAiejIzX3ZzX3VuaW5mZWN0ZWQiCnoyMl9ub2RydWdfdmFsdWVzW1sic3RhdGUiXV0gPC0gInoyMl92c191bmluZmVjdGVkIgpwbG90X2RmIDwtIHJiaW5kLmRhdGEuZnJhbWUoYXMuZGF0YS5mcmFtZSh6MjNfbm9kcnVnX3ZhbHVlcyksIGFzLmRhdGEuZnJhbWUoejIyX25vZHJ1Z192YWx1ZXMpKQpwbG90X2RmW1siZ2VuZSJdXSA8LSByb3duYW1lcyhwbG90X2RmKQoKIyMgSSBqdXN0IHJlYWxpemVkIHRoYXQgdGhpcyBpcyBhY3R1YWxseSBqdXN0IGEgY29tcGFyaXNvbiBvZiB6MjMvejIyCiMjIHdlIHNob3VsZCBqdXN0IHRha2UgdGhlIGFkanVzdGVkIHAtdmFsdWVzIGZyb20gdGhhdCBjb250cmFzdCBmb3IgdGhpcy4KejIzX3oyMl9jb21wYXJpc29uIDwtIGhzX21hY3JfdGFibGVbWyJkYXRhIl1dW1siejIzbm9zYl92c196MjJub3NiIl1dCm5vZHJ1Z19yb3dzIDwtIHoyM196MjJfY29tcGFyaXNvbltnZW5lX2lkeCwgXQpub2RydWdfcHZhbHVlcyA8LSBub2RydWdfcm93c1ssIGMoImRlc2VxX3AiLCAiZGVzZXFfYWRqcCIpXQpyb3duYW1lcyhub2RydWdfcHZhbHVlcykgPC0gbm9kcnVnX3Jvd3NbWyJoZ25jX3N5bWJvbCJdXQpub2RydWdfcHZhbHVlcwoKZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0gZ2VuZSwgeSA9IGRlc2VxX2xvZ2ZjLCBmaWxsID0gc3RhdGUpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpLCBzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBkZXNlcV9sb2dmYyAtIGRlc2VxX2xmY3NlLAogICAgICAgICAgICAgICAgICAgIHltYXggPSBkZXNlcV9sb2dmYyArIGRlc2VxX2xmY3NlKSwKICAgICAgICAgICAgICAgIHdpZHRoID0gMC4yLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMUI5RTc3IiwgIiM3NTcwQjMiKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41KSkKCmNvbXBhcmlzb24gPC0gYygiejIzX3ZzX3VuaW5mZWN0ZWQiLCAiejIyX3ZzX3VuaW5mZWN0ZWQiKQpjb21wYXJpc29ucyA8LSByZXAobGlzdChjb21wYXJpc29uKSwgbnJvdyhwbG90X2RmKSAvIDIpCmdncGxvdChwbG90X2RmLCBhZXMoeCA9IGdlbmUsIHkgPSBkZXNlcV9sb2dmYywgZmlsbCA9IHN0YXRlLCBhZGQgPSBkZXNlcV9sZmNzZSwgZmFjZXQuYnkgPSAic3RhdGUiKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gZGVzZXFfbG9nZmMgLSBkZXNlcV9sZmNzZSwKICAgICAgICAgICAgICAgICAgICB5bWF4ID0gZGVzZXFfbG9nZmMgKyBkZXNlcV9sZmNzZSksCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjkpKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKCkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IGNvbXBhcmlzb25zLCBsYWJlbC55ID0gcm93bmFtZXMoejIzX25vZHJ1Z192YWx1ZXMpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzFCOUU3NyIsICIjNzU3MEIzIikpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpCgpgYGAKCkV4Y2VsbGVudCwgdGhlIHZhbHVlcyBub3cgbWF0Y2ggdXAuICBOb3cgSSB1c3QgbmVlZCB0byBmaWd1cmUgb3V0IHdoeQp0aGUgc3R1cGlkIGhnbmMgSURzIGdvdCBsb3N0Li4uICBJIGNhbiBzZWUgdGhlbSBpbiB0aGUgaHNfYW5ub3QgZGF0YQpzdHJ1Y3R1cmUsIHNvIEkgbXVzdCBoYXZlIG1lc3NlZCB1cCB3aGVuIEkgcmVnZW5lcmVkIHRoZSBpbnB1dCB0byB0aGUKZGUuICBPaywgSSBnb3QgdG8gdGhlIHNhbWUgc3RhcnRpbmcgcG9pbnQgbm93IHdpdGggaWRlbnRpY2FsIHZhbHVlcy4KQXMgc29vbiBhcyBJIGRpZCB0aGF0LCBJIGxvb2tlZCBhdCB0aGUgcmVzdWx0aW5nIHBsb3QgYW5kIHJlYWxpemVkCnRoYXQgd2UgYXJlIGFjdHVhbGx5IGp1c3QgY29tcGFyaW5nIHoyMyAvIHoyMi4KCkhlcmUgaXMgd2h5OiB0aGUgcGxvdCBhcyBpdCBzdGFuZHMgaXMgYSBjb21wYXJpc29uIG9mIHRoZSBsb2cyRkMKdmFsdWVzIG9mIHRoZSBmb2xsb3dpbmcgdHdvIGNvbnRyYXN0czogejIzL3VuaW5mZWN0ZWQgYW5kCnoyMi91bmluZmVjdGVkOyBzdGF0ZWQgZGlmZmVyZW50bHksIHRoaXMgaXMgKHoyMy91bmluZikvKHoyMi91bmluZikKd2hpY2ggb2YgY291cnNlIGNhbmNlbHMgb3V0IHRvIGp1c3QgejIzL3oyMi4KClRoZXJlZm9yZSBpdCBpcyBtdWNoIG1vcmUgcGFyc2ltb25pb3VzIHRvIGp1c3QgdXNlIHRoZSB2YWx1ZXMgZnJvbQp6MjMvejIyLiAgSSBzd2VhciBJIGhhdmUgZ29uZSB0aHJvdWdoIHRoaXMgZXhhY3QgZXhlcmNpc2Ugb24gc28gc28KbWFueSBvY2Nhc2lvbnMgaW4gdGhlIHBhc3QgaXQgaXMgdGVycmlibGUuCgpgYGB7cn0Kd2FudGVkX2dlbmVzIDwtIGMoIklGSTI3IiwgIlJTQUQyIiwgIkNDTDgiLCAiSUZJNDRMIiwgIk9BU0wiLAogICAgICAgICAgICAgICAgICAiVVNQMTgiLCAiSURPMSIsICJJRE8yIiwgIktZTlUiLCAiQUhSIiwgIklMNEkxIiwKICAgICAgICAgICAgICAgICAgIlNPRDIiLCAiTk9UQ0gxIiwgIkRMTDEiLCAiRExMNCIsICJIRVMxIiwgIkhFWTEiKQpnZ3NpZ25pZl9wbG90IDwtIGdnc2lnbmlmX3BhaXJlZF9nZW5lcygKICBoc19tYWNyLCBjb25kaXRpb25zID0gYygiaW5mX3oyMyIsICJpbmZfejIyIiksIGdlbmVzID0gd2FudGVkX2dlbmVzKQpnZ3NpZ25pZl9wbG90CmBgYAoKYGBge3J9CnBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCm1lc3NhZ2UoIlRoaXMgaXMgaHBnbHRvb2xzIGNvbW1pdDogIiwgZ2V0X2dpdF9jb21taXQoKSkKdG1wIDwtIHNhdmVtZShmaWxlbmFtZSA9IHNhdmVmaWxlKQpgYGAKCmBgYHtyIGxvYWRtZV9hZnRlciwgZXZhbD1GQUxTRX0KdG1wIDwtIGxvYWRtZShmaWxlbmFtZSA9IHNhdmVmaWxlKQpgYGAKZGV2dG9vbHM6OmxvYWRfYWxsKCd+L2hwZ2x0b29scycpCg==