Note, I am using my previous worksheet from when last I worked with Rezia as a template for this (I copied it from there and am modifying it now).

1 S.pyogenes 5448 rofA RNASeq version: 20190916

1.1 Preprocessing

I used my cyoa tool to process these samples by doing the following:

  1. Copying the data from the sequencer into the directory ‘preprocessing/’
  2. Used a slightly involved shell command to create a directory for each sample and copy the reads for it to the ‘unprocessed/’ subdirectory within it.
  3. invoked the following:
cd preprocessing
start=$(pwd)
for i in $(/bin/ls -d ./*)
do
  cd $i
  rm -rf outputs scripts
  cyoa --task pipe --method prnas --species spyogenes_5448 \
       --gff_type gene --gff_tag locus_tag \
       --input $(/bin/ls unprocessed/* | tr '\n' ':' | sed 's/:$//g')
  cd $start
done

The above for loop goes into each sample and does the following:

  1. Trims the data, heavily compresses the outputs.
  2. Runs fastqc
  3. Runs hisat2 using my spyogenes_5448 indices.
  4. Converts the sam alignment to sorted/indexed bam.
  5. Makes a couple of extra copies of it with some filters.
  6. Compresses the aligned/unaligned reads.
  7. Runs htseq-count on the alignments to count reads/gene.

Note the following steps were not actually run because I had a speeling error. But since they are not necessary for the explicitly RNASeq analyses I first want to do, I ignored it. I am curious though to see if there are other mutations in these strains, so I will likely run those portions manually.

  1. Runs freebayes on the alignments to look for variants.
  2. Sorts/compresses the freebayes output.
  3. Does some parsing of the freebayes output and provides some tables about where mutations were observed.

1.2 Collect annotation information

Same two primary annotation sources, the gff file used for mapping/counting, and microbesonline.org. Note that since I moved to just downloading the material from the web interface, I no longer have a handy method to get the taxon ID, so I go there and hunt down the taxId manually.

Now that I am thinking about it, my 5448 genome/annotations are kind of old, I will ask and check to see if there is anything newer.

Also, 5448 does not have an entry at microbesonline.org, a fact which I forgot. I need to go poking in my notes to reconnect 5005 and 5448.

gff_annot <- load_gff_annotations("reference/spyogenes_5448.gff", type = "gene")
## Trying attempt: rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = TRUE)
## Trying attempt: rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = FALSE)
## Had a successful gff import with rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = FALSE)
## Returning a df with 14 columns and 1814 rows.
rownames(gff_annot) <- gff_annot[["locus_tag"]]
head(gff_annot)
##              seqnames start  end width strand                 source type score
## SP5448_00005 CP008776   232 1587  1356      + EMBL/GenBank/SwissProt gene    NA
## SP5448_00010 CP008776  1742 2878  1137      + EMBL/GenBank/SwissProt gene    NA
## SP5448_00015 CP008776  2953 3150   198      + EMBL/GenBank/SwissProt gene    NA
## SP5448_00020 CP008776  3480 4595  1116      + EMBL/GenBank/SwissProt gene    NA
## SP5448_00025 CP008776  4665 5234   570      + EMBL/GenBank/SwissProt gene    NA
## SP5448_00030 CP008776  5237 8740  3504      + EMBL/GenBank/SwissProt gene    NA
##              phase    locus_tag gene gene_synonym note pseudo
## SP5448_00005     1 SP5448_00005 <NA>              <NA>   <NA>
## SP5448_00010     1 SP5448_00010 <NA>              <NA>   <NA>
## SP5448_00015     1 SP5448_00015 <NA>              <NA>   <NA>
## SP5448_00020     1 SP5448_00020 <NA>              <NA>   <NA>
## SP5448_00025     1 SP5448_00025 <NA>              <NA>   <NA>
## SP5448_00030     1 SP5448_00030 <NA>              <NA>   <NA>
mgas_data <- load_genbank_annotations(accession="CP008776")
## Loading required namespace: rentrez
## Done Parsing raw GenBank file text. [ 14.023 seconds ]
## 2022-04-25 15:51:33 Starting creation of gene GRanges
## 2022-04-25 15:51:36 Starting creation of CDS GRanges
## 2022-04-25 15:51:43 Starting creation of exon GRanges
## No exons read from genbank file. Assuming sections of CDS are full exons
## 2022-04-25 15:51:45 Starting creation of variant VRanges
## 2022-04-25 15:51:45 Starting creation of transcript GRanges
## No transcript features (mRNA) found, using spans of CDSs
## 2022-04-25 15:51:46 Starting creation of misc feature GRanges
## Warning in fill_stack_df(feats[!typs %in% c("gene", "exon", "CDS",
## "variation", : Got unexpected multi-value field(s) [ inference ]. The resulting
## column(s) will be of class CharacterList, rather than vector(s). Please contact
## the maintainer if multi-valuedness is expected/meaningful for the listed
## field(s).
## 2022-04-25 15:51:46 - Done creating GenBankRecord object [ 13.159 seconds ]
genome_size <- GenomicRanges::width(mgas_data$seq)  ## This fails on travis?
mgas_cds <- as.data.frame(mgas_data$cds)
## Get rid of amino acid sequence
rownames(mgas_cds) <- mgas_cds[["locus_tag"]]
wanted <- ! colnames(mgas_cds) %in% c("translation", "type", "strand", "seqnames", "start", "end", "locus_tag", "note", "gene", "gene_synonym", "width")
mgas_cds <- mgas_cds[, wanted]
## And EC_number because wtf is that?
mgas_annot <- merge(mgas_cds, gff_annot, by="row.names")
rownames(mgas_annot) <- mgas_annot[["Row.names"]]
mgas_annot[["Row.names"]] <- NULL

1.3 Create expressionSet

Note that I did the following to the sample sheet provided by Dr. McIver:

  1. Changed the dP_R20_4 sample to dP_R20_2 (The original sample name is still there are the original column).
  2. Added columns at the end containing the count table locations.
  3. Added columns ‘short_media’, ‘growth_phase’, ‘genotype’ which hopefully contain the relevant metadata extracted from the sample descriptions.
  4. Added a column ‘Experiment’ which is either ‘rofA’ or ‘pdxR’.
rofa_expt <- create_expt(metadata = "sample_sheets/all_samples.xlsx",
                        gene_info = gff_annot, file_column = "spyogenes5448genecounts") %>%
  subset_expt(subset="experiment=='rofA'") %>%
  set_expt_conditions(fact="genotype") %>%
  set_expt_batches(fact="growthphase")
## Reading the sample metadata.
## Did not find the condition column in the sample sheet.
## Filling it in as undefined.
## Did not find the batch column in the sample sheet.
## Filling it in as undefined.
## The sample definitions comprises: 72 rows(samples) and 27 columns(metadata fields).
## Matched 1814 annotations and counts.
## Bringing together the count matrix and gene information.
## Some annotations were lost in merging, setting them to 'undefined'.
## Saving the expressionset to 'expt.rda'.
## The final expressionset has 1814 rows and 72 columns.
## subset_expt(): There were 72, now there are 36 samples.

I have 36 samples to play with, let us see what they look like.

1.4 Poke expressionSet

rofa_libsize <- plot_libsize(rofa_expt)
rofa_libsize$plot

rofa_filter_plot <- plot_libsize_prepost(rofa_expt)
rofa_filter_plot$count_plot

rofa_filter_plot$lowgene_plot
## Warning: Using alpha for a discrete variable is not advised.

rofa_nonzero <- plot_nonzero(rofa_expt)
rofa_nonzero$plot
## Warning: ggrepel: 17 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## awesome, there is a range of ~ 25 genes.

written <- write_expt(rofa_expt, excel = glue::glue("excel/rofA_expt-v{ver}.xlsx"))
## Deleting the file excel/rofA_expt-v20190916.xlsx before writing the tables.
## Writing the first sheet, containing a legend and some summary data.
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## The following object is masked from 'package:S4Vectors':
## 
##     expand
## 
## Total:13 s
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## 
## Total:13 s

1.5 Quick visualizations

1.5.1 Without considering time

Let us start with some views of the data without thinking about batch effects.

rofa_norm <- normalize_expt(rofa_expt, filter=TRUE, norm="quant", convert="cpm", transform="log2")
## Removing 40 low-count genes (1774 remaining).
rofa_pca <- plot_pca(rofa_norm)
rofa_pca$plot

rofa_heatmap <- plot_disheat(rofa_norm)
rofa_heatmap$plot

1.5.2 Considering time

Repeat the previous plots, but this time using limma’s batch removal method (which is just a residuals).

rofa_time <- set_expt_conditions(rofa_expt, fact="growthphase") %>%
  set_expt_batches(fact="genotype")

rofa_time_norm <- normalize_expt(rofa_time, filter=TRUE, norm="quant", convert="cpm",
                                 transform="log2")
## Removing 40 low-count genes (1774 remaining).
rofa_time_pca <- plot_pca(rofa_time_norm)
rofa_time_pca$plot

rofa_time_nb <- normalize_expt(rofa_time, filter=TRUE, norm="quant", convert="cpm",
                             transform="log2", batch="limma")
## Removing 40 low-count genes (1774 remaining).
## If you receive a warning: 'NANs produced', one potential reason is that the data was quantile normalized.
## Setting 28 low elements to zero.
## transform_counts: Found 28 values equal to 0, adding 1 to the matrix.
rofa_time_nb_pca <- plot_pca(rofa_time_nb)
rofa_time_nb_pca$plot

Well, it is pretty clear that time is the dominant factor.

1.6 Differential Expression analyses

I am going to do the DE in 4 separate pieces:

  1. Only compare strains
  2. Only compare times
  3. Compare the concatenation of strains and times.

1.6.1 Strain only comparisons

strain_de <- all_pairwise(rofa_expt, model_batch=TRUE, filter=TRUE)
## Using limma's removeBatchEffect to visualize with(out) batch inclusion.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

strain_keepers <- list(
    "delta_wt" = c("delta", "WT"),
    "revert_wt" = c("revert", "WT"),
    "delta_revert" = c("delta", "revert"))
strain_tables <- combine_de_tables(
    strain_de, keepers = strain_keepers,
    excel = glue::glue("excel/rofa_strain_tables-v{ver}.xlsx"))
## Deleting the file excel/rofa_strain_tables-v20190916.xlsx before writing the tables.
strain_sig <- extract_significant_genes(
    strain_tables,
    excel = glue::glue("excel/rofa_strain_sig-v{ver}.xlsx"))
## Deleting the file excel/rofa_strain_sig-v20190916.xlsx before writing the tables.

1.6.2 Time only comparisons

time_de <- all_pairwise(rofa_time, model_batch=TRUE)
## Using limma's removeBatchEffect to visualize with(out) batch inclusion.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

time_keepers <- list(
    "transition_exponential" = c("transition", "exponential"),
    "stationary_exponential" = c("stationary", "exponential"),
    "stationary_transition" = c("stationary", "transition"))
time_tables <- combine_de_tables(
    time_de, keepers = time_keepers,
    excel = glue::glue("excel/rofa_time_tables-v{ver}.xlsx"))
## Deleting the file excel/rofa_time_tables-v20190916.xlsx before writing the tables.
time_sig <- extract_significant_genes(
    time_tables,
    excel = glue::glue("excel/rofa_time_sig-v{ver}.xlsx"))
## Deleting the file excel/rofa_time_sig-v20190916.xlsx before writing the tables.

1.6.2.1 Compare times vs strains

I always worry that comparing a data set across multiple conditions results in not what I think it will. Let us therefore plot the logFCs of likely contrasts against each other.

x_axis <- time_tables[["data"]][["transition_exponential"]][, c("deseq_logfc", "deseq_adjp")]
y_axis <- strain_tables[["data"]][["delta_wt"]][, c("deseq_logfc", "deseq_adjp")]
both <- merge(x_axis, y_axis, by="row.names")
rownames(both) <- both[["Row.names"]]
both[["Row.names"]] <- NULL
cor.test(both[["deseq_logfc.x"]], both[["deseq_logfc.y"]], method="spearman")
## Warning in cor.test.default(both[["deseq_logfc.x"]], both[["deseq_logfc.y"]], :
## Cannot compute exact p-value with ties
## 
##  Spearman's rank correlation rho
## 
## data:  both[["deseq_logfc.x"]] and both[["deseq_logfc.y"]]
## S = 5.3e+08, p-value <2e-16
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##    rho 
## 0.4274
plotted <- plot_linear_scatter(both[, c("deseq_logfc.x", "deseq_logfc.y")])
## Warning in plot_multihistogram(df): NAs introduced by coercion
plotted$scatter

1.6.3 Interaction model

I want to make sure that my methods of performing interaction models work as I think it does, and this data set looks to me to be a perfect place to test that.

combined_factors <- paste0(pData(rofa_expt)[["genotype"]], "_",
                           pData(rofa_expt)[["growthphase"]])
combined_expt <- set_expt_conditions(rofa_expt, fact=combined_factors) %>%

combined_de <- all_pairwise(combined_expt, model_batch="svaseq", filter=TRUE)

combined_keepers <- list(
    "exponential_delta_vs_wt" = c("deltaexponential", "WTexponential"),
    "exponential_delta_vs_revert" = c("deltaexponential", "revertexponential"),
    "stationary_delta_vs_wt" = c("deltastationary", "WTstationary"),
    "stationary_delta_vs_revert" = c("deltastationary", "revertstationary"),
    "transition_delta_vs_wt" = c("deltatransition", "WTtransition"),
    "transition_delta_vs_revert" = c("deltatransition", "reverttransition"),
    "WT_exponential_vs_transition" = c("WTexponential", "WTtransition"),
    "delta_exponential_vs_transition" = c("deltaexponential", "deltatransition"),
    "revert_exponential_vs_transition" = c("revertexponential", "reverttransition"),
    "WT_stationary_vs_transition" = c("WTstationary", "WTtransition"),
    "delta_stationary_vs_transition" = c("deltastationary", "deltatransition"),
    "revert_stationary_vs_transition" = c("revertstationary", "reverttransition"),
    "WT_stationary_vs_exponential" = c("WTstationary", "WTexponential"),
    "delta_stationary_vs_exponential" = c("deltastationary", "deltaexponential"),
    "revert_stationary_vs_exponential" = c("revertstationary", "revertexponential"))


combined_tables <- combine_de_tables(
    combined_de, keepers = combined_keepers,
    excel = glue::glue("excel/rofa_combined_tables-v{ver}.xlsx"))
combined_sig <- extract_significant_genes(
    combined_tables,
    excel = glue::glue("excel/rofa_combined_sig-v{ver}.xlsx"))
if (!isTRUE(get0("skip_load"))) {
  pander::pander(sessionInfo())
  message(paste0("This is hpgltools commit: ", get_git_commit()))
  this_save <- paste0(gsub(pattern = "\\.Rmd", replace = "", x = rmd_file), "-v", ver, ".rda.xz")
  message(paste0("Saving to ", this_save))
  tmp <- sm(saveme(filename = this_save))
}
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 09b2f72b4cb5e7f7f74b7a7970f5b2f7f3609e41
## This is hpgltools commit: Tue Apr 19 18:55:02 2022 -0400: 09b2f72b4cb5e7f7f74b7a7970f5b2f7f3609e41
## Saving to index-v20190916.rda.xz
LS0tCnRpdGxlOiAiUy5weW9nZW5lcyA1NDQ4IHJuYXNlcSBjb21wYXJpbmcgcm9mQSBzdHJhaW5zIGFjcm9zcyB0aW1lLiIKYXV0aG9yOiAiYXRiIGFiZWxld0BnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogaHRtbF9kb2N1bWVudDoKICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgY29kZV9mb2xkaW5nOiBzaG93CiAgZmlnX2NhcHRpb246IHRydWUKICBmaWdfaGVpZ2h0OiA3CiAgZmlnX3dpZHRoOiA3CiAgaGlnaGxpZ2h0OiBkZWZhdWx0CiAga2VlcF9tZDogZmFsc2UKICBtb2RlOiBzZWxmY29udGFpbmVkCiAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgc2VsZl9jb250YWluZWQ6IHRydWUKICB0aGVtZTogcmVhZGFibGUKICB0b2M6IHRydWUKICB0b2NfZmxvYXQ6CiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCjxzdHlsZT4KICBib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgICBtYXgtd2lkdGg6IDE2MDBweDsKICB9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoaHBnbHRvb2xzKQp0dCA8LSBkZXZ0b29sczo6bG9hZF9hbGwoIn4vaHBnbHRvb2xzIikKa25pdHI6Om9wdHNfa25pdCRzZXQocHJvZ3Jlc3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSA5MCwKICAgICAgICAgICAgICAgICAgICAgZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSA4LAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDgsCiAgICAgICAgICAgICAgICAgICAgICBkcGkgPSA5NikKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTApKQpzZXQuc2VlZCgxKQp2ZXIgPC0gIjIwMTkwOTE2IgpybWRfZmlsZSA8LSAiaW5kZXguUm1kIgpgYGAKCk5vdGUsIEkgYW0gdXNpbmcgbXkgcHJldmlvdXMgd29ya3NoZWV0IGZyb20gd2hlbiBsYXN0IEkgd29ya2VkIHdpdGgKUmV6aWEgYXMgYSB0ZW1wbGF0ZSBmb3IgdGhpcyAoSSBjb3BpZWQgaXQgZnJvbSB0aGVyZSBhbmQgYW0gbW9kaWZ5aW5nCml0IG5vdykuCgojIFMucHlvZ2VuZXMgNTQ0OCByb2ZBIFJOQVNlcSB2ZXJzaW9uOiBgciB2ZXJgCgojIyBQcmVwcm9jZXNzaW5nCgpJIHVzZWQgbXkgY3lvYSB0b29sIHRvIHByb2Nlc3MgdGhlc2Ugc2FtcGxlcyBieSBkb2luZyB0aGUgZm9sbG93aW5nOgoKMS4gIENvcHlpbmcgdGhlIGRhdGEgZnJvbSB0aGUgc2VxdWVuY2VyIGludG8gdGhlIGRpcmVjdG9yeSAncHJlcHJvY2Vzc2luZy8nCjIuICBVc2VkIGEgc2xpZ2h0bHkgaW52b2x2ZWQgc2hlbGwgY29tbWFuZCB0byBjcmVhdGUgYSBkaXJlY3RvcnkgZm9yCiAgICBlYWNoIHNhbXBsZSBhbmQgY29weSB0aGUgcmVhZHMgZm9yIGl0IHRvIHRoZSAndW5wcm9jZXNzZWQvJwogICAgc3ViZGlyZWN0b3J5IHdpdGhpbiBpdC4KMy4gIGludm9rZWQgdGhlIGZvbGxvd2luZzoKCmBgYHtiYXNoLCBldmFsPUZBTFNFfQpjZCBwcmVwcm9jZXNzaW5nCnN0YXJ0PSQocHdkKQpmb3IgaSBpbiAkKC9iaW4vbHMgLWQgLi8qKQpkbwogIGNkICRpCiAgcm0gLXJmIG91dHB1dHMgc2NyaXB0cwogIGN5b2EgLS10YXNrIHBpcGUgLS1tZXRob2QgcHJuYXMgLS1zcGVjaWVzIHNweW9nZW5lc181NDQ4IFwKICAgICAgIC0tZ2ZmX3R5cGUgZ2VuZSAtLWdmZl90YWcgbG9jdXNfdGFnIFwKICAgICAgIC0taW5wdXQgJCgvYmluL2xzIHVucHJvY2Vzc2VkLyogfCB0ciAnXG4nICc6JyB8IHNlZCAncy86JC8vZycpCiAgY2QgJHN0YXJ0CmRvbmUKYGBgCgpUaGUgYWJvdmUgZm9yIGxvb3AgZ29lcyBpbnRvIGVhY2ggc2FtcGxlIGFuZCBkb2VzIHRoZSBmb2xsb3dpbmc6CgoxLiAgVHJpbXMgdGhlIGRhdGEsIGhlYXZpbHkgY29tcHJlc3NlcyB0aGUgb3V0cHV0cy4KMi4gIFJ1bnMgZmFzdHFjCjMuICBSdW5zIGhpc2F0MiB1c2luZyBteSBzcHlvZ2VuZXNfNTQ0OCBpbmRpY2VzLgo0LiAgQ29udmVydHMgdGhlIHNhbSBhbGlnbm1lbnQgdG8gc29ydGVkL2luZGV4ZWQgYmFtLgo1LiAgTWFrZXMgYSBjb3VwbGUgb2YgZXh0cmEgY29waWVzIG9mIGl0IHdpdGggc29tZSBmaWx0ZXJzLgo2LiAgQ29tcHJlc3NlcyB0aGUgYWxpZ25lZC91bmFsaWduZWQgcmVhZHMuCjcuICBSdW5zIGh0c2VxLWNvdW50IG9uIHRoZSBhbGlnbm1lbnRzIHRvIGNvdW50IHJlYWRzL2dlbmUuCgogIE5vdGUgdGhlIGZvbGxvd2luZyBzdGVwcyB3ZXJlIG5vdCBhY3R1YWxseSBydW4gYmVjYXVzZSBJIGhhZCBhCiAgc3BlZWxpbmcgZXJyb3IuICBCdXQgc2luY2UgdGhleSBhcmUgbm90IG5lY2Vzc2FyeSBmb3IgdGhlIGV4cGxpY2l0bHkKICBSTkFTZXEgYW5hbHlzZXMgSSBmaXJzdCB3YW50IHRvIGRvLCBJIGlnbm9yZWQgaXQuICBJIGFtIGN1cmlvdXMKICB0aG91Z2ggdG8gc2VlIGlmIHRoZXJlIGFyZSBvdGhlciBtdXRhdGlvbnMgaW4gdGhlc2Ugc3RyYWlucywgc28gSQogIHdpbGwgbGlrZWx5IHJ1biB0aG9zZSBwb3J0aW9ucyBtYW51YWxseS4KCjguICBSdW5zIGZyZWViYXllcyBvbiB0aGUgYWxpZ25tZW50cyB0byBsb29rIGZvciB2YXJpYW50cy4KOS4gIFNvcnRzL2NvbXByZXNzZXMgdGhlIGZyZWViYXllcyBvdXRwdXQuCjEwLiBEb2VzIHNvbWUgcGFyc2luZyBvZiB0aGUgZnJlZWJheWVzIG91dHB1dCBhbmQgcHJvdmlkZXMgc29tZSB0YWJsZXMKICAgIGFib3V0IHdoZXJlIG11dGF0aW9ucyB3ZXJlIG9ic2VydmVkLgoKIyMgQ29sbGVjdCBhbm5vdGF0aW9uIGluZm9ybWF0aW9uCgpTYW1lIHR3byBwcmltYXJ5IGFubm90YXRpb24gc291cmNlcywgdGhlIGdmZiBmaWxlIHVzZWQgZm9yIG1hcHBpbmcvY291bnRpbmcsCmFuZCBtaWNyb2Jlc29ubGluZS5vcmcuICBOb3RlIHRoYXQgc2luY2UgSSBtb3ZlZCB0byBqdXN0IGRvd25sb2FkaW5nIHRoZQptYXRlcmlhbCBmcm9tIHRoZSB3ZWIgaW50ZXJmYWNlLCBJIG5vIGxvbmdlciBoYXZlIGEgaGFuZHkgbWV0aG9kIHRvIGdldCB0aGUKdGF4b24gSUQsIHNvIEkgZ28gdGhlcmUgYW5kIGh1bnQgZG93biB0aGUgdGF4SWQgbWFudWFsbHkuCgpOb3cgdGhhdCBJIGFtIHRoaW5raW5nIGFib3V0IGl0LCBteSA1NDQ4IGdlbm9tZS9hbm5vdGF0aW9ucyBhcmUga2luZApvZiBvbGQsIEkgd2lsbCBhc2sgYW5kIGNoZWNrIHRvIHNlZSBpZiB0aGVyZSBpcyBhbnl0aGluZyBuZXdlci4KCkFsc28sIDU0NDggZG9lcyBub3QgaGF2ZSBhbiBlbnRyeSBhdCBtaWNyb2Jlc29ubGluZS5vcmcsIGEgZmFjdCB3aGljaApJIGZvcmdvdC4gIEkgbmVlZCB0byBnbyBwb2tpbmcgaW4gbXkgbm90ZXMgdG8gcmVjb25uZWN0IDUwMDUgYW5kIDU0NDguCgpgYGB7ciBhbm5vdGF0aW9ufQpnZmZfYW5ub3QgPC0gbG9hZF9nZmZfYW5ub3RhdGlvbnMoInJlZmVyZW5jZS9zcHlvZ2VuZXNfNTQ0OC5nZmYiLCB0eXBlID0gImdlbmUiKQpyb3duYW1lcyhnZmZfYW5ub3QpIDwtIGdmZl9hbm5vdFtbImxvY3VzX3RhZyJdXQpoZWFkKGdmZl9hbm5vdCkKCm1nYXNfZGF0YSA8LSBsb2FkX2dlbmJhbmtfYW5ub3RhdGlvbnMoYWNjZXNzaW9uPSJDUDAwODc3NiIpCmdlbm9tZV9zaXplIDwtIEdlbm9taWNSYW5nZXM6OndpZHRoKG1nYXNfZGF0YSRzZXEpICAjIyBUaGlzIGZhaWxzIG9uIHRyYXZpcz8KbWdhc19jZHMgPC0gYXMuZGF0YS5mcmFtZShtZ2FzX2RhdGEkY2RzKQojIyBHZXQgcmlkIG9mIGFtaW5vIGFjaWQgc2VxdWVuY2UKcm93bmFtZXMobWdhc19jZHMpIDwtIG1nYXNfY2RzW1sibG9jdXNfdGFnIl1dCndhbnRlZCA8LSAhIGNvbG5hbWVzKG1nYXNfY2RzKSAlaW4lIGMoInRyYW5zbGF0aW9uIiwgInR5cGUiLCAic3RyYW5kIiwgInNlcW5hbWVzIiwgInN0YXJ0IiwgImVuZCIsICJsb2N1c190YWciLCAibm90ZSIsICJnZW5lIiwgImdlbmVfc3lub255bSIsICJ3aWR0aCIpCm1nYXNfY2RzIDwtIG1nYXNfY2RzWywgd2FudGVkXQojIyBBbmQgRUNfbnVtYmVyIGJlY2F1c2Ugd3RmIGlzIHRoYXQ/Cm1nYXNfYW5ub3QgPC0gbWVyZ2UobWdhc19jZHMsIGdmZl9hbm5vdCwgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKG1nYXNfYW5ub3QpIDwtIG1nYXNfYW5ub3RbWyJSb3cubmFtZXMiXV0KbWdhc19hbm5vdFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmBgYAoKIyMgQ3JlYXRlIGV4cHJlc3Npb25TZXQKCk5vdGUgdGhhdCBJIGRpZCB0aGUgZm9sbG93aW5nIHRvIHRoZSBzYW1wbGUgc2hlZXQgcHJvdmlkZWQgYnkKRHIuIE1jSXZlcjoKCjEuICBDaGFuZ2VkIHRoZSBkUF9SMjBfNCBzYW1wbGUgdG8gZFBfUjIwXzIgKFRoZSBvcmlnaW5hbCBzYW1wbGUgbmFtZQogICAgaXMgc3RpbGwgdGhlcmUgYXJlIHRoZSBvcmlnaW5hbCBjb2x1bW4pLgoyLiAgQWRkZWQgY29sdW1ucyBhdCB0aGUgZW5kIGNvbnRhaW5pbmcgdGhlIGNvdW50IHRhYmxlIGxvY2F0aW9ucy4KMy4gIEFkZGVkIGNvbHVtbnMgJ3Nob3J0X21lZGlhJywgJ2dyb3d0aF9waGFzZScsICdnZW5vdHlwZScgd2hpY2gKICAgIGhvcGVmdWxseSBjb250YWluIHRoZSByZWxldmFudCBtZXRhZGF0YSBleHRyYWN0ZWQgZnJvbSB0aGUgc2FtcGxlCiAgICBkZXNjcmlwdGlvbnMuCjQuICBBZGRlZCBhIGNvbHVtbiAnRXhwZXJpbWVudCcgd2hpY2ggaXMgZWl0aGVyICdyb2ZBJyBvciAncGR4UicuCgpgYGB7ciBleHB0fQpyb2ZhX2V4cHQgPC0gY3JlYXRlX2V4cHQobWV0YWRhdGEgPSAic2FtcGxlX3NoZWV0cy9hbGxfc2FtcGxlcy54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9pbmZvID0gZ2ZmX2Fubm90LCBmaWxlX2NvbHVtbiA9ICJzcHlvZ2VuZXM1NDQ4Z2VuZWNvdW50cyIpICU+JQogIHN1YnNldF9leHB0KHN1YnNldD0iZXhwZXJpbWVudD09J3JvZkEnIikgJT4lCiAgc2V0X2V4cHRfY29uZGl0aW9ucyhmYWN0PSJnZW5vdHlwZSIpICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdD0iZ3Jvd3RocGhhc2UiKQpgYGAKCkkgaGF2ZSAzNiBzYW1wbGVzIHRvIHBsYXkgd2l0aCwgbGV0IHVzIHNlZSB3aGF0IHRoZXkgbG9vayBsaWtlLgoKIyMgUG9rZSBleHByZXNzaW9uU2V0CgpgYGB7ciBwb2tlfQpyb2ZhX2xpYnNpemUgPC0gcGxvdF9saWJzaXplKHJvZmFfZXhwdCkKcm9mYV9saWJzaXplJHBsb3QKCnJvZmFfZmlsdGVyX3Bsb3QgPC0gcGxvdF9saWJzaXplX3ByZXBvc3Qocm9mYV9leHB0KQpyb2ZhX2ZpbHRlcl9wbG90JGNvdW50X3Bsb3QKcm9mYV9maWx0ZXJfcGxvdCRsb3dnZW5lX3Bsb3QKCnJvZmFfbm9uemVybyA8LSBwbG90X25vbnplcm8ocm9mYV9leHB0KQpyb2ZhX25vbnplcm8kcGxvdAojIyBhd2Vzb21lLCB0aGVyZSBpcyBhIHJhbmdlIG9mIH4gMjUgZ2VuZXMuCgp3cml0dGVuIDwtIHdyaXRlX2V4cHQocm9mYV9leHB0LCBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3JvZkFfZXhwdC12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIFF1aWNrIHZpc3VhbGl6YXRpb25zCgojIyMgV2l0aG91dCBjb25zaWRlcmluZyB0aW1lCgpMZXQgdXMgc3RhcnQgd2l0aCBzb21lIHZpZXdzIG9mIHRoZSBkYXRhIHdpdGhvdXQgdGhpbmtpbmcgYWJvdXQgYmF0Y2ggZWZmZWN0cy4KCmBgYHtyIHZpZXdfbm9iYXRjaH0Kcm9mYV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHJvZmFfZXhwdCwgZmlsdGVyPVRSVUUsIG5vcm09InF1YW50IiwgY29udmVydD0iY3BtIiwgdHJhbnNmb3JtPSJsb2cyIikKcm9mYV9wY2EgPC0gcGxvdF9wY2Eocm9mYV9ub3JtKQpyb2ZhX3BjYSRwbG90Cgpyb2ZhX2hlYXRtYXAgPC0gcGxvdF9kaXNoZWF0KHJvZmFfbm9ybSkKcm9mYV9oZWF0bWFwJHBsb3QKYGBgCgojIyMgQ29uc2lkZXJpbmcgdGltZQoKUmVwZWF0IHRoZSBwcmV2aW91cyBwbG90cywgYnV0IHRoaXMgdGltZSB1c2luZyBsaW1tYSdzIGJhdGNoIHJlbW92YWwKbWV0aG9kICh3aGljaCBpcyBqdXN0IGEgcmVzaWR1YWxzKS4KCmBgYHtyIG5iX3ZpZXd9CnJvZmFfdGltZSA8LSBzZXRfZXhwdF9jb25kaXRpb25zKHJvZmFfZXhwdCwgZmFjdD0iZ3Jvd3RocGhhc2UiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3Q9Imdlbm90eXBlIikKCnJvZmFfdGltZV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHJvZmFfdGltZSwgZmlsdGVyPVRSVUUsIG5vcm09InF1YW50IiwgY29udmVydD0iY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJsb2cyIikKcm9mYV90aW1lX3BjYSA8LSBwbG90X3BjYShyb2ZhX3RpbWVfbm9ybSkKcm9mYV90aW1lX3BjYSRwbG90Cgpyb2ZhX3RpbWVfbmIgPC0gbm9ybWFsaXplX2V4cHQocm9mYV90aW1lLCBmaWx0ZXI9VFJVRSwgbm9ybT0icXVhbnQiLCBjb252ZXJ0PSJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0ibG9nMiIsIGJhdGNoPSJsaW1tYSIpCnJvZmFfdGltZV9uYl9wY2EgPC0gcGxvdF9wY2Eocm9mYV90aW1lX25iKQpyb2ZhX3RpbWVfbmJfcGNhJHBsb3QKYGBgCgpXZWxsLCBpdCBpcyBwcmV0dHkgY2xlYXIgdGhhdCB0aW1lIGlzIHRoZSBkb21pbmFudCBmYWN0b3IuCgojIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBhbmFseXNlcwoKSSBhbSBnb2luZyB0byBkbyB0aGUgREUgaW4gNCBzZXBhcmF0ZSBwaWVjZXM6CgoxLiAgT25seSBjb21wYXJlIHN0cmFpbnMKMi4gIE9ubHkgY29tcGFyZSB0aW1lcwozLiAgQ29tcGFyZSB0aGUgY29uY2F0ZW5hdGlvbiBvZiBzdHJhaW5zIGFuZCB0aW1lcy4KCiMjIyBTdHJhaW4gb25seSBjb21wYXJpc29ucwoKYGBge3IgcGFpcndpc2Vfc3RyYWlufQpzdHJhaW5fZGUgPC0gYWxsX3BhaXJ3aXNlKHJvZmFfZXhwdCwgbW9kZWxfYmF0Y2g9VFJVRSwgZmlsdGVyPVRSVUUpCnN0cmFpbl9rZWVwZXJzIDwtIGxpc3QoCiAgICAiZGVsdGFfd3QiID0gYygiZGVsdGEiLCAiV1QiKSwKICAgICJyZXZlcnRfd3QiID0gYygicmV2ZXJ0IiwgIldUIiksCiAgICAiZGVsdGFfcmV2ZXJ0IiA9IGMoImRlbHRhIiwgInJldmVydCIpKQpzdHJhaW5fdGFibGVzIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgc3RyYWluX2RlLCBrZWVwZXJzID0gc3RyYWluX2tlZXBlcnMsCiAgICBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3JvZmFfc3RyYWluX3RhYmxlcy12e3Zlcn0ueGxzeCIpKQpzdHJhaW5fc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICBzdHJhaW5fdGFibGVzLAogICAgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9yb2ZhX3N0cmFpbl9zaWctdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIyMgVGltZSBvbmx5IGNvbXBhcmlzb25zCgpgYGB7ciBwYWlyd2lzZV90aW1lfQp0aW1lX2RlIDwtIGFsbF9wYWlyd2lzZShyb2ZhX3RpbWUsIG1vZGVsX2JhdGNoPVRSVUUpCnRpbWVfa2VlcGVycyA8LSBsaXN0KAogICAgInRyYW5zaXRpb25fZXhwb25lbnRpYWwiID0gYygidHJhbnNpdGlvbiIsICJleHBvbmVudGlhbCIpLAogICAgInN0YXRpb25hcnlfZXhwb25lbnRpYWwiID0gYygic3RhdGlvbmFyeSIsICJleHBvbmVudGlhbCIpLAogICAgInN0YXRpb25hcnlfdHJhbnNpdGlvbiIgPSBjKCJzdGF0aW9uYXJ5IiwgInRyYW5zaXRpb24iKSkKdGltZV90YWJsZXMgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICB0aW1lX2RlLCBrZWVwZXJzID0gdGltZV9rZWVwZXJzLAogICAgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9yb2ZhX3RpbWVfdGFibGVzLXZ7dmVyfS54bHN4IikpCnRpbWVfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICB0aW1lX3RhYmxlcywKICAgIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvcm9mYV90aW1lX3NpZy12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIyMgQ29tcGFyZSB0aW1lcyB2cyBzdHJhaW5zCgpJIGFsd2F5cyB3b3JyeSB0aGF0IGNvbXBhcmluZyBhIGRhdGEgc2V0IGFjcm9zcyBtdWx0aXBsZSBjb25kaXRpb25zCnJlc3VsdHMgaW4gbm90IHdoYXQgSSB0aGluayBpdCB3aWxsLiAgTGV0IHVzIHRoZXJlZm9yZSBwbG90IHRoZSBsb2dGQ3MKb2YgbGlrZWx5IGNvbnRyYXN0cyBhZ2FpbnN0IGVhY2ggb3RoZXIuCgpgYGB7ciBjb21wYXJlX3RpbWVzX3ZzX3N0cmFpbnN9CnhfYXhpcyA8LSB0aW1lX3RhYmxlc1tbImRhdGEiXV1bWyJ0cmFuc2l0aW9uX2V4cG9uZW50aWFsIl1dWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQp5X2F4aXMgPC0gc3RyYWluX3RhYmxlc1tbImRhdGEiXV1bWyJkZWx0YV93dCJdXVssIGMoImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiKV0KYm90aCA8LSBtZXJnZSh4X2F4aXMsIHlfYXhpcywgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKGJvdGgpIDwtIGJvdGhbWyJSb3cubmFtZXMiXV0KYm90aFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmNvci50ZXN0KGJvdGhbWyJkZXNlcV9sb2dmYy54Il1dLCBib3RoW1siZGVzZXFfbG9nZmMueSJdXSwgbWV0aG9kPSJzcGVhcm1hbiIpCnBsb3R0ZWQgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihib3RoWywgYygiZGVzZXFfbG9nZmMueCIsICJkZXNlcV9sb2dmYy55IildKQpwbG90dGVkJHNjYXR0ZXIKYGBgCgojIyMgSW50ZXJhY3Rpb24gbW9kZWwKCkkgd2FudCB0byBtYWtlIHN1cmUgdGhhdCBteSBtZXRob2RzIG9mIHBlcmZvcm1pbmcgaW50ZXJhY3Rpb24gbW9kZWxzCndvcmsgYXMgSSB0aGluayBpdCBkb2VzLCBhbmQgdGhpcyBkYXRhIHNldCBsb29rcyB0byBtZSB0byBiZSBhIHBlcmZlY3QKcGxhY2UgdG8gdGVzdCB0aGF0LgoKYGBge3IgY29tYmluZWRfZGUsIGV2YWw9RkFMU0V9CmNvbWJpbmVkX2ZhY3RvcnMgPC0gcGFzdGUwKHBEYXRhKHJvZmFfZXhwdClbWyJnZW5vdHlwZSJdXSwgIl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwRGF0YShyb2ZhX2V4cHQpW1siZ3Jvd3RocGhhc2UiXV0pCmNvbWJpbmVkX2V4cHQgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhyb2ZhX2V4cHQsIGZhY3Q9Y29tYmluZWRfZmFjdG9ycykgJT4lCgpjb21iaW5lZF9kZSA8LSBhbGxfcGFpcndpc2UoY29tYmluZWRfZXhwdCwgbW9kZWxfYmF0Y2g9InN2YXNlcSIsIGZpbHRlcj1UUlVFKQoKY29tYmluZWRfa2VlcGVycyA8LSBsaXN0KAogICAgImV4cG9uZW50aWFsX2RlbHRhX3ZzX3d0IiA9IGMoImRlbHRhZXhwb25lbnRpYWwiLCAiV1RleHBvbmVudGlhbCIpLAogICAgImV4cG9uZW50aWFsX2RlbHRhX3ZzX3JldmVydCIgPSBjKCJkZWx0YWV4cG9uZW50aWFsIiwgInJldmVydGV4cG9uZW50aWFsIiksCiAgICAic3RhdGlvbmFyeV9kZWx0YV92c193dCIgPSBjKCJkZWx0YXN0YXRpb25hcnkiLCAiV1RzdGF0aW9uYXJ5IiksCiAgICAic3RhdGlvbmFyeV9kZWx0YV92c19yZXZlcnQiID0gYygiZGVsdGFzdGF0aW9uYXJ5IiwgInJldmVydHN0YXRpb25hcnkiKSwKICAgICJ0cmFuc2l0aW9uX2RlbHRhX3ZzX3d0IiA9IGMoImRlbHRhdHJhbnNpdGlvbiIsICJXVHRyYW5zaXRpb24iKSwKICAgICJ0cmFuc2l0aW9uX2RlbHRhX3ZzX3JldmVydCIgPSBjKCJkZWx0YXRyYW5zaXRpb24iLCAicmV2ZXJ0dHJhbnNpdGlvbiIpLAogICAgIldUX2V4cG9uZW50aWFsX3ZzX3RyYW5zaXRpb24iID0gYygiV1RleHBvbmVudGlhbCIsICJXVHRyYW5zaXRpb24iKSwKICAgICJkZWx0YV9leHBvbmVudGlhbF92c190cmFuc2l0aW9uIiA9IGMoImRlbHRhZXhwb25lbnRpYWwiLCAiZGVsdGF0cmFuc2l0aW9uIiksCiAgICAicmV2ZXJ0X2V4cG9uZW50aWFsX3ZzX3RyYW5zaXRpb24iID0gYygicmV2ZXJ0ZXhwb25lbnRpYWwiLCAicmV2ZXJ0dHJhbnNpdGlvbiIpLAogICAgIldUX3N0YXRpb25hcnlfdnNfdHJhbnNpdGlvbiIgPSBjKCJXVHN0YXRpb25hcnkiLCAiV1R0cmFuc2l0aW9uIiksCiAgICAiZGVsdGFfc3RhdGlvbmFyeV92c190cmFuc2l0aW9uIiA9IGMoImRlbHRhc3RhdGlvbmFyeSIsICJkZWx0YXRyYW5zaXRpb24iKSwKICAgICJyZXZlcnRfc3RhdGlvbmFyeV92c190cmFuc2l0aW9uIiA9IGMoInJldmVydHN0YXRpb25hcnkiLCAicmV2ZXJ0dHJhbnNpdGlvbiIpLAogICAgIldUX3N0YXRpb25hcnlfdnNfZXhwb25lbnRpYWwiID0gYygiV1RzdGF0aW9uYXJ5IiwgIldUZXhwb25lbnRpYWwiKSwKICAgICJkZWx0YV9zdGF0aW9uYXJ5X3ZzX2V4cG9uZW50aWFsIiA9IGMoImRlbHRhc3RhdGlvbmFyeSIsICJkZWx0YWV4cG9uZW50aWFsIiksCiAgICAicmV2ZXJ0X3N0YXRpb25hcnlfdnNfZXhwb25lbnRpYWwiID0gYygicmV2ZXJ0c3RhdGlvbmFyeSIsICJyZXZlcnRleHBvbmVudGlhbCIpKQoKCmNvbWJpbmVkX3RhYmxlcyA8LSBjb21iaW5lX2RlX3RhYmxlcygKICAgIGNvbWJpbmVkX2RlLCBrZWVwZXJzID0gY29tYmluZWRfa2VlcGVycywKICAgIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvcm9mYV9jb21iaW5lZF90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKY29tYmluZWRfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICBjb21iaW5lZF90YWJsZXMsCiAgICBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3JvZmFfY29tYmluZWRfc2lnLXZ7dmVyfS54bHN4IikpCmBgYAoKCmBgYHtyIHNhdmVtZX0KaWYgKCFpc1RSVUUoZ2V0MCgic2tpcF9sb2FkIikpKSB7CiAgcGFuZGVyOjpwYW5kZXIoc2Vzc2lvbkluZm8oKSkKICBtZXNzYWdlKHBhc3RlMCgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKSkKICB0aGlzX3NhdmUgPC0gcGFzdGUwKGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLCByZXBsYWNlID0gIiIsIHggPSBybWRfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKQogIG1lc3NhZ2UocGFzdGUwKCJTYXZpbmcgdG8gIiwgdGhpc19zYXZlKSkKICB0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lID0gdGhpc19zYXZlKSkKfQpgYGAK