1 Primary transcript question of interest

When comparing the het/ko data, a specific set of genes was observed; what are the set of transcripts icluded among them of interest? This is in contrast to Najib’s question: are there transcripts not included among the genes observed.

Likely annotation columns: ensembl_gene_id, ensembl_transcript_id, ensembl_peptide_id, ensembl_exon_id, description, chromosome, transcript_start, transcript_end, transcript_length, strand, external_gene_name, external_transcript_name, external_synonym, phenotype_description, refseq_mrna.

2 Set some parameters which will be used later

## Adjusted p-value cutoff when working on 'normal' contrasts.
adjp_cutoff <- 0.05
## Adjusted p-value cutoff when working on the inclusion sets.
filter_adjp_cutoff <- 0.1
default_fstring <- "~ 0 + condition"
## Allow 10 GO categories to be displayed when plotting.
go_categories <- 14
## Allow groups higher up in the tree for clusterProfiler results.
go_level <- 2
tx_label_column <- "external_transcript_name" ## Set the column used to extract gene symbols rather than ENSG.....
gene_label_column <- "external_synonym"
## LogFC cutoff when working on the inclusion sets.
filter_lfc_cutoff <- 0.1
## LFC cutoff for 'normal' contrasts.
lfc_cutoff <- 1.0
## Increase the maximum allowed group size when working with clusterProfiler
## This should bring out some of the more general groups like 'cellbody'
max_groupsize <- 2000
## Speed up clusterProfiler by choosing the correct keytypes
orgdb_from <- "ENSEMBLTRANS"
## MA plot point outlines
outline <- FALSE
sample_sheet <- "sample_sheets/20240606_only_umd_sequenced_modified.xlsx"
#annotation_columns <- c(
#  "ensembl_gene_id", "ensembl_transcript_id","ensembl_peptide_id", "ensembl_exon_id",
#  "description", "chromosome_name", "transcript_start", "transcript_end", "transcript_length",
#  "strand", "external_gene_name", "external_transcript_name", "external_synonym",
#  "phenotype_description", "refseq_mrna")
annotation_columns <- c("ensembl_gene_id", "ensembl_transcript_id", "description", "external_synonym", "external_transcript_name")

3 Introduction

This document is a copy of the iprgc_analyses_202408.Rmd document, with a few important differences:

  1. I will not be spending time to compare the various (de)duplication methods/results.
  2. This will create, visualize, and analyze transcript-level abundances rather than genes.
  3. I have not yet decided, but I might play with the wt filtering to try to make it clearer.
  4. I will likely remove some comments and text describing what is going on.
  5. All output directories get a suffix ’_tx’

With that in mind, the goal is to repeat our gene-level analyses, but using the transcript quantifications derived from featureCounts. This is in response to a review who suggested that some genes have potentially interesting and variable transcripts. That is a simple enough query to address, so let us see what we get!

4 A note about implementation

I would like to improve this document by comparing/contrasting the methodologies performed by other groups and those performed by me in it. I never fully appreciated the suite of computational methods applied by previous groups when examining TRAP data; I instead simply followed Theresa’s notebook without considering other possibilities.

I therefore spent a little time stepping through her thesis and pulling out the relevant papers in the hopes of learning these various methods. I should therefore be able soon to compare/contrast the various methods employed by other labs in addition to copying Theresa’s logic.

4.1 The following block cannot work in the container

The following block assumes the full tree of preprocessed data with the logs from the trimmer, mapping, umi deduplication, counting, etc. As a result it cannot work in the container which has only the various count tables.

As a result, I am including a copy of this sheet after running the following block in my working tree. I suppose for the moment you will have to trust that it worked. (for right now, when testing out this container, I am just sending the R working directory to my tree for this block, then moving it back.

I will need to manually edit one column though, the symlink column from Theresa has a series of paths which do not work in the container.

umi_spec <- make_rnaseq_spec(umi = TRUE)
iprgc_2022_meta <- gather_preprocessing_metadata("sample_sheets/20240606_only_umd_sequenced.xlsx",
                                                 spec = umi_spec, species = "mm39_112", verbose = TRUE,
                                                 basedir = "preprocessing/umd_sequenced")
colnames(iprgc_2022_meta[["new_meta"]])
head(iprgc_2022_meta[["new_meta"]])
msigdb <- "reference/msigdb_v2024.1.Mm.db"
msig_data <- NULL
make_transparent <- function() {
  ggplot2::theme(
    panel.background = element_rect(fill = 'transparent'),
    plot.background = element_rect(fill = 'transparent', color = NA),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.background = element_rect(fill = 'transparent'),
    legend.box.background = element_rect(fill = 'transparent'))
}

5 Collect the count tables

I used salmon/featurecounts/etc with CDS/mRNA features on our compute cluster and used the above gather function to add a column to the metadata containing the filenames produced. The following reads that column and creates a tarball of the files. One thing I need to do: modify that to accept a vetor of columns so I can easily collect r1+r2 and stuff like that.

As the name suggests, these are mRNA counts following the deduplication marking and removal from the initial hisat alignments via umitools.

## all the input files are already xz compressed
mrna_counts <- tar_meta_column(sample_sheet, column = "umi_dedup_mrna_count", compress = NULL)

I will figure out if I can leave mSigDB M2 in this image; if not, then any analyses depending on those gene sets will fail.

m2_gsc <- try(load_gmt_signatures(signatures = msigdb,
                                  signature_category = "M2"), silent = TRUE)
## I do not think I have permission to load the msigdb in the container
## So, if this fails, just load it from GSVAData, oh wait no, GSVAdata is human.
if ("try-error" %in% class(m2_gsc)) {
  warning("Unable to load the M2 MsigDB data.")
}
## Warning: Unable to load the M2 MsigDB data.

6 Annotation data

I am pulling this from Theresa’s anxontrapR_pipeline.Rmd, primarily because it looks similar to the other documents, but was modified more recently. I will change it slightly, primarily because I grabbed a new mmusculus assembly and therefore I will pull the mmusculus annotations from a specific biomart (@smedleyBioMartBiologicalQueries2009) archive that should match it.

A note from the future: multiple ensembl archive servers have been taken offline since last I ran this. Let us see if Feb. 2023 still works.

6.1 An important note!

In the recent past, ensembl queries have become inconsistent, failing much more often than ever in the past. I do not think this is the fault of ensembl; but I think I need a fallback mechanism for collecting annotation information.

In the case of ensembl, it should be trivial (but less fun) to use a combination of the locally installed orgdb and txdb databases.

This does open a risk that the set of genes with annotations will be different depending on when the container is run due to differences between the orgdb/txdb instance and the Feb 2023 biomart. I am not sure there is much I can do about that except to bundle the set of annotations I downloaded in the container – since load_biomart_annotations() does save a rda copy of its download.

ok, I did both. If you, dear reader, wish to download your own annotations, and ensembl is having troubles, the following should work without a problem; in addition the rda annotations are in /data of the container and should get loaded.

tx_gene_map <- data.frame()
mm_annot <- try(load_biomart_annotations(species = "mmusculus", host = "useast.ensembl.org",
                                         archive = FALSE, overwrite = FALSE,
                                         gene_requests = annotation_columns))
## The biomart annotations file already exists, loading from it.
fields <- c("ACCNUM", "ENSEMBL", "ENSEMBLTRANS", "ENTEZID", "GENENAME", "SYMBOL")
orgdb_annot <- load_orgdb_annotations("org.Mm.eg.db", fields = fields)
## Loading required package: AnnotationDbi
## Loading required package: stats4
## Loading required package: BiocGenerics
## Loading required package: generics
## 
## Attaching package: 'generics'
## The following object is masked from 'package:dplyr':
## 
##     explain
## The following objects are masked from 'package:base':
## 
##     as.difftime, as.factor, as.ordered, intersect, is.element, setdiff,
##     setequal, union
## 
## Attaching package: 'BiocGenerics'
## The following objects are masked from 'package:hpgltools':
## 
##     IQR, conditions, conditions<-, mad, sd, var
## The following object is masked from 'package:dplyr':
## 
##     combine
## The following objects are masked from 'package:stats':
## 
##     IQR, mad, sd, var, xtabs
## The following objects are masked from 'package:base':
## 
##     Filter, Find, Map, Position, Reduce, anyDuplicated, aperm, append,
##     as.data.frame, basename, cbind, colnames, dirname, do.call,
##     duplicated, eval, evalq, get, grep, grepl, is.unsorted, lapply,
##     mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
##     rank, rbind, rownames, sapply, saveRDS, table, tapply, unique,
##     unsplit, which.max, which.min
## Loading required package: Biobase
## Welcome to Bioconductor
## 
##     Vignettes contain introductory material; view with
##     'browseVignettes()'. To cite Bioconductor, see
##     'citation("Biobase")', and for packages 'citation("pkgname")'.
## 
## Attaching package: 'Biobase'
## The following objects are masked from 'package:hpgltools':
## 
##     exprs<-, notes, pData<-, sampleNames<-
## Loading required package: IRanges
## Loading required package: S4Vectors
## 
## Attaching package: 'S4Vectors'
## The following objects are masked from 'package:dplyr':
## 
##     first, rename
## The following object is masked from 'package:utils':
## 
##     findMatches
## The following objects are masked from 'package:base':
## 
##     I, expand.grid, unname
## 
## Attaching package: 'IRanges'
## The following objects are masked from 'package:dplyr':
## 
##     collapse, desc, slice
## 
## Attaching package: 'AnnotationDbi'
## The following object is masked from 'package:dplyr':
## 
##     select
## 
## Unable to find CDSNAME, setting it to GENENAME.
## Unable to find GENE_TYPE, setting it to GENETYPE.
## Unable to find CDSCHROM in the db, removing it.
## Unable to find CDSSTRAND in the db, removing it.
## Unable to find CDSSTART in the db, removing it.
## Unable to find CDSEND in the db, removing it.
## Some requested columns are not available: ENTEZID.
## The following are available: ACCNUM, ALIAS, ENSEMBL, ENSEMBLPROT, ENSEMBLTRANS, ENTREZID, ENZYME, EVIDENCE, EVIDENCEALL, GENENAME, GENETYPE, GO, GOALL, IPI, MGI, ONTOLOGY, ONTOLOGYALL, PATH, PFAM, PMID, PROSITE, REFSEQ, SYMBOL, UNIPROT
## Extracted all gene ids.
## Attempting to select: GENENAME, GENETYPE, ACCNUM, ENSEMBL, ENSEMBLTRANS, GENENAME, SYMBOL
## 'select()' returned 1:many mapping between keys and columns
gene_info <- orgdb_annot[["genes"]]

## Note, there are a bunch of variants of the txdb package one might use.
## I do not think it matters a lot for our purposes, but I suspect that if we used
## a mismatched BSgenome and tried to pull CDS sequences, that might end badly.
pkg <- "TxDb.Mmusculus.UCSC.mm10.knownGene"
tx_annot <- load_txdb_annotations(pkg)
## Error in `library()`:
## ! there is no package called 'TxDb.Mmusculus.UCSC.mm10.knownGene'
transcripts <- tx_annot[["TX"]]
## Error:
## ! object 'tx_annot' not found
transcripts[["tx"]] <- gsub(x = transcripts[["TXNAME"]],
                            pattern = "\\.\\d+$", replacement = "")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'gsub': object 'transcripts' not found
mm_annot_orgdb <- merge(gene_info, transcripts, by.x = "ensembltrans", by.y = "tx")
## Error in `h()`:
## ! error in evaluating the argument 'y' in selecting a method for function 'merge': object 'transcripts' not found
rownames(mm_annot_orgdb) <- make.names(mm_annot_orgdb[["ensembl"]], unique = TRUE)
## Error:
## ! object 'mm_annot_orgdb' not found
mm_annot_combined <- merge(mm_annot[["annotation"]], mm_annot_orgdb, by.x = "ensembl_transcript_id", by.y = "ensembltrans", all.x = TRUE)
## Error in `h()`:
## ! error in evaluating the argument 'y' in selecting a method for function 'merge': object 'mm_annot_orgdb' not found
mm_annot_genes <- mm_annot_combined
## Error:
## ! object 'mm_annot_combined' not found
rownames(mm_annot_genes) <- make.names(mm_annot_genes[["ensembl_gene_id"]], unique = TRUE)
## Error:
## ! object 'mm_annot_genes' not found
mm_annot_tx <- mm_annot_combined
## Error:
## ! object 'mm_annot_combined' not found
rownames(mm_annot_tx) <- make.names(mm_annot_tx[["ensembl_transcript_id"]], unique = TRUE)
## Error:
## ! object 'mm_annot_tx' not found
tx_gene_map <- mm_annot_genes[, c("TXID", "ensembl_gene_id")]
## Error:
## ! object 'mm_annot_genes' not found

7 Hisat2 summarizedExperiments by transcript

7.1 Color choices and reused parameters

color_choices <- list(
  "all" = list(
    "p08_het_dlgn" = "#E7298A",
    "p15_het_dlgn" = "#E7298A",
    "p08_het_retina" = "#238B45",
    "p15_het_retina" = "#238B45",
    "p08_het_scn" = "#4292C6",
    "p15_het_scn" = "#4292C6",
    "p08_ko_dlgn" = "#C994C7",
    "p15_ko_dlgn" = "#C994C7",
    "p08_ko_retina" = "#74c476",
    "p15_ko_retina" = "#74c476",
    "p08_ko_scn" = "#9BCAE1",
    "p15_ko_scn" = "#9BCAE1",
    "p08_wt_dlgn" = "#980043",
    "p15_wt_dlgn" = "#980043",
    "p08_wt_retina" = "#004008",
    "p15_wt_retina" = "#004008",
    "p08_wt_scn" = "#08519C",
    "p15_wt_scn" = "#08519C",
    "p60_wt_dlgn" = "#333333",
    "p60_wt_retina" = "#222222",
    "p60_wt_scn" = "#111111"),
  "geno_loc" = list(
    "het_dlgn" = "#E7298A",
    "het_retina" = "#238B45",
    "het_scn" = "#4292C6",
    "ko_dlgn" = "#C994C7",
    "ko_retina" = "#74c476",
    "ko_scn" = "#9BCAE1",
    "wt_dlgn" = "#980043",
    "wt_retina" = "#004008",
    "wt_scn" = "#08519C"),
  "location" = list(
    "retina" = "#004008",
    "dlgn" = "#980043",
    "scn" = "#08519C"),
  "genotype" = list(
    "wt" = "#74c476",
    "het" = "#238B45",
    "ko" = "#006D2C"),
  "time" = list(
    "p08" = "#5E104B",
    "p15" = "#4E9231"))
colors <- color_choices[["geno_loc"]]
time_colors <- list(
  "p08_het_dlgn" = "#E7298A",
  "p15_het_dlgn" = "#8a1852",
  "p08_het_retina" = "#238B45",
  "p15_het_retina" = "#155329",
  "p08_het_scn" = "#4292C6",
  "p15_het_scn" = "#275776",
  "p08_ko_dlgn" = "#C994C7",
  "p15_ko_dlgn" = "#785877",
  "p08_ko_retina" = "#74C476",
  "p15_ko_retina" = "#457546",
  "p08_ko_scn" = "#9BCAE1",
  "p15_ko_scn" = "#5d7987")

7.2 The Transcript abundances

This perhaps deserves a little bit of explanation: the process for using UMIs goes as follows:

  1. Examine all the raw reads, extract the UMI IDs, and add them as comments to the fastq file (or rewrite the read ID)
  2. Align all reads against the genome.
  3. Take the resulting bam file and find reads at the same location with the same ID(s) and filter the alignment so that there remains only 1 representative from each ID.
  4. Count up the features of interest.

I therefore invoked featureCounts on the umi deduplicated bam alignments (found in column ‘umi_dedup_output_bam’ BP in my copy of the automagically generated sample sheet) using the requisite mm38 gff file with transcript as the feature type and ID as the tag.

Oh, that reminds me, I need to re-key the annotations to use that and I might need to check that the IDs do or do not have a prefix like ‘transcript:’ or whatever…

mm38_hisat_tx <- create_se(sample_sheet, gene_info = mm_tx_annot,
                           file_column = "umi_dedup_mrna_count") |>
  set_conditions(fact = "geno_loc_atb") |>
  set_batches(fact = "time_atb") |>
  set_colors(color_choices[["geno_loc"]])
## Reading the sample metadata.
## Checking the state of the condition column.
## Checking the state of the batch column.
## Checking the condition factor.
## The sample definitions comprises: 69 rows(samples) and 59 columns(metadata fields).
## Warning in create_se(sample_sheet, gene_info = mm_tx_annot, file_column =
## "umi_dedup_mrna_count"): Some samples were removed when cross referencing the
## samples against the count data.
## Error in `h()`:
## ! error in evaluating the argument 'exp' in selecting a method for function 'set_colors': error in evaluating the argument 'exp' in selecting a method for function 'set_batches': error in evaluating the argument 'exp' in selecting a method for function 'set_conditions': object 'mm_tx_annot' not found
mm38_hisat_tx
## Error:
## ! object 'mm38_hisat_tx' not found
## Now that we created the data structure with IDs that include the transcript: prefix,
## remove it...
new_ids <- gsub(x = rownames(assay(mm38_hisat_tx)), pattern = "^transcript:", replacement = "")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'gsub': error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'x' in selecting a method for function 'assay': object 'mm38_hisat_tx' not found
rownames(mm38_hisat_tx) <- new_ids
## Error:
## ! object 'new_ids' not found
rownames(mm_tx_annot) <- gsub(x = rownames(mm_tx_annot), pattern = "^transcript:", replacement = "")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'gsub': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'mm_tx_annot' not found
all_fact <- paste0(colData(mm38_hisat_tx)[["time_atb"]], "_",
                   colData(mm38_hisat_tx)[["geno_loc_atb"]])
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'mm38_hisat_tx' not found
colData(mm38_hisat_tx)[["time_geno_loc"]] <- all_fact
## Error:
## ! object 'all_fact' not found
dim(assay(mm38_hisat_tx))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'assay': object 'mm38_hisat_tx' not found
head(assay(mm38_hisat_tx))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'head': error in evaluating the argument 'x' in selecting a method for function 'assay': object 'mm38_hisat_tx' not found
head(rowData(mm38_hisat_tx))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'head': error in evaluating the argument 'x' in selecting a method for function 'rowData': object 'mm38_hisat_tx' not found

8 Non-zero Counts per Sample

A neat little change I just made: when null set the y-intercept to the mean percentage of units observed (transcripts in this case).

tx_nonzero  <- plot_nonzero(mm38_hisat_tx)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_nonzero': object 'mm38_hisat_tx' not found
pp(file = "01diagnostic_images_tx/nonzero_tx_unfiltered.pdf", image = tx_nonzero[["plot"]])
## Error:
## ! object 'tx_nonzero' not found
tx_libsize <- plot_libsize(mm38_hisat_tx)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_quantreads': object 'mm38_hisat_tx' not found
pp(file = "01diagnostic_images_tx/quantsize_unfiltered.pdf", image = tx_libsize[["plot"]])
## Error:
## ! object 'tx_libsize' not found

8.1 Exclude p60

mm38_hisat_tx <- subset_se(mm38_hisat_tx, subset = "time_atb!='p60'")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'mm38_hisat_tx' not found

8.2 Replot the nonzero gene plots

tx_nonzero_filt <- plot_nonzero(mm38_hisat_tx, plot_labels = FALSE)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_nonzero': object 'mm38_hisat_tx' not found
pp(file = "01diagnostic_images_tx/nonzero_tx_filt.pdf", image = tx_nonzero_filt[["plot"]])
## Error:
## ! object 'tx_nonzero_filt' not found

9 Quick PCA, then return to Theresa’s document

tx_norm <- normalize(mm38_hisat_tx, transform = "log2", convert = "cpm",
                     norm = "quant", filter = TRUE)
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'normalize': object 'mm38_hisat_tx' not found
tx_norm_pca <- plot_pca(tx_norm)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_pca': object 'tx_norm' not found
pp(file = "01diagnostic_images_tx/tx_norm_pca.pdf", image = tx_norm_pca[["plot"]])
## Error:
## ! object 'tx_norm_pca' not found
tx_loc_geno <- set_conditions(mm38_hisat_tx, fact = "location_atb",
                              colors = color_choices[["location"]]) |>
  set_batches(fact = "genotype_atb")
## Error in `h()`:
## ! error in evaluating the argument 'exp' in selecting a method for function 'set_batches': error in evaluating the argument 'exp' in selecting a method for function 'set_conditions': object 'mm38_hisat_tx' not found

9.1 The associated PCA

At different times, it appears to me that Theresa has preferred slightly different normalization methods, primarily a mix of TMM and quantile.

Thus I will use different suffix letters to denote various normalizations employed, and if they turn out the same I will pick one arbitrarily.

loc_geno_nq <- normalize(tx_loc_geno, transform = "log2", convert = "cpm",
                         filter = TRUE, norm = "quant")
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'normalize': object 'tx_loc_geno' not found
location_genotype_pca <- plot_pca(loc_geno_nq)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_pca': object 'loc_geno_nq' not found
pp(file = "01diagnostic_images_tx/location_genotype_norm_pca.pdf",
   image = location_genotype_pca[["plot"]])
## Error:
## ! object 'location_genotype_pca' not found
loc_geno_nt <- normalize(tx_loc_geno, transform = "log2", convert = "cpm",
                         filter = TRUE, norm = "tmm")
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'normalize': object 'tx_loc_geno' not found
location_genotype_tmm_pca <- plot_pca(loc_geno_nt)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_pca': object 'loc_geno_nt' not found
pp(file = "01diagnostic_images_tx/location_genotype_tmm_pca.pdf", image = location_genotype_tmm_pca[["plot"]])
## Error:
## ! object 'location_genotype_tmm_pca' not found
sample_sankey <- plot_meta_sankey(tx_loc_geno, color_choices = color_choices,
                                  factors = c("genotype_atb", "location_atb", "time_atb"))
## Error in `h()`:
## ! error in evaluating the argument 'design' in selecting a method for function 'plot_meta_sankey': object 'tx_loc_geno' not found
pp(file = "01diagnostic_images_tx/design_sankey.pdf", image = sample_sankey[["plot"]])
## Error:
## ! object 'sample_sankey' not found

Re-purpose these expression plots to examine one Opn4 transcript. This will by some necessity be arbitrary.

It is at this moment that I realized (as I am almost certain I told myself when I started) using the hisat-derived tx counts is wrong because hisat->featureCounts has no good way to allocate multi-hits to multiple transcripts and will therefore discount them. If I want to do this in any sort of reliable way, I really need to re-extract the deduplicate reads from the umitools bam file and run salmon/kallisto. This is somewhat annoying because the various tools I have for extract bam reads really hate the way umitools formats the bam file and freak out. I can force it, but I think I will lose a potentially significant number of reads, let us go look…

(If you with to follow along, go back to the preprocessing document which I should have included in the singularity container) I will do one of the following:

  1. Use gatk or bamtools to create deduplicated fastq files and use them with salmon.
  2. Use salmon with the deduplicated bam files from hisat.

I am not sure right now which I want to do. I kind of like the idea of having deduplicated fastq files so that I can try some other stuff with them (kraken, for example)

## ggstatsplot has appeared and disappeared a few times from cran recently,
## I am not sure if it will work therefore, so figured I should disable this block
## for the moment.

## I did a search at ensembl for the various opn4 transcript IDs:
## ENSMUST00000022331, ENSMUST00000168444, ENSMUST00000226806
ids <- c("ENSMUST00000022331", "ENSMUST00000168444", "ENSMUST00000226806")
ids %in% rownames(loc_geno_nt)
ids <- c("ENSMUST00000022331", "ENSMUST00000168444")

opn4_exprs <- data.frame(combined = colData(loc_geno_nt)[["geno_loc_atb"]],
                         location = colData(loc_geno_nt)[["location_atb"]],
                         genotype = colData(loc_geno_nt)[["genotype_atb"]],
                         opn4 = assay(tx_loc_geno)[ids[1], ])

groupedstats::grouped_summary(opn4_exprs, location, opn)
opn4_location <- ggstatsplot::ggbetweenstats(data = opn4_exprs, x = location, y = opn)
pp(file = "images_tx/ggbetween_location.pdf")
opn4_location
plotted <- dev.off()
opn4_location

10 Two noteworthy genes with potentially different transcript abundances

One reviewer suggested checking out the transcript-level abundances and noted two genes in particular as potentially interesting: Nlgn3 and Ncam1. Let us take a moment and see what they look like across the various metadata factors of interest.

10.1 Grab the tx annotations for them

nlgn3_idx <- grepl(x = rowData(mm38_hisat_tx)[["external_transcript_name"]], pattern = "^Nlgn3\\-")
sum(nlgn3_idx)
nlgn3_annot <- rowData(mm38_hisat_tx)[nlgn3_idx, ]
nlgn3_ids <- rownames(rowData(mm38_hisat_tx))[nlgn3_idx]
assay(mm38_hisat_tx)[nlgn3_ids, ]
## Looks like

ncam1_idx <- grepl(x = rowData(mm38_hisat_tx)[["external_transcript_name"]], pattern = "^Ncam1\\-")
sum(ncam1_idx)
rowData(rowData(mm38_hisat_tx))[ncam1_idx, ]
ncam1_annot <- as.data.frame(rowData(mm38_hisat_tx))[ncam1_idx, ]
ncam1_ids <- rownames(rowData(mm38_hisat_tx))[ncam1_idx]
assay(mm38_hisat_tx)[ncam1_ids, ]

ok, so I plotted the question a bit differently, but got the same answer.

Here is the text of Theresa’s notebook following this analysis:

“Ugh oh, looks like there is at least one retina KO sample that has some melanopsin expression in it. Turns out ipRGC_07 is a bad egg which is supposed to be a KO but has melanopsin expression. It’s friends which were pooled from the same mice are iprgc_06 and iprgc_08, so we need to exclude all these samples.”

I am also seeing some knockout expression with some caveats: I do not have the affected samples in my dataset (iprgc_07) and the levels I am seeing are quite low – I will look in IGV to double check, but I strongly suspect that these are some piddly reads near the UTRs.

Onward!

11 PCA plots

11.1 PCA of all genes by location

Theresa’s first pca was of log2 cpm values. I might add quantile/tmm to this?

tx_location <- set_conditions(mm38_hisat_tx, fact = "location_atb") |>
  set_batches(fact = "genotype_atb") |>
  set_colors(color_choices[["location"]])
## Error in `h()`:
## ! error in evaluating the argument 'exp' in selecting a method for function 'set_colors': error in evaluating the argument 'exp' in selecting a method for function 'set_batches': error in evaluating the argument 'exp' in selecting a method for function 'set_conditions': object 'mm38_hisat_tx' not found
tx_location_norm <- normalize(tx_location, filter = TRUE, norm = "quant",
                                   transform = "log2", convert = "cpm")
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'normalize': object 'tx_location' not found
tx_location_pca <- plot_pca(tx_location_norm)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_pca': object 'tx_location_norm' not found
pp(file = "01diagnostic_images_tx/tx_location_norm_pca.pdf")
## Warning in pp(file = "01diagnostic_images_tx/tx_location_norm_pca.pdf"): The
## directory: 01diagnostic_images_tx does not exist, will attempt to create it.
tx_location_pca
## Error:
## ! object 'tx_location_pca' not found
dev.off()
## png 
##   2
tx_location_pca
## Error:
## ! object 'tx_location_pca' not found

Once again we see that samples iprgc_66 and iprgc_130 are likely actually DLGN and not SCN. I am therefore going to add a column to the sample sheet noting this, and remove them from the expressionset.

I will thus replot the data after removing those two. If we want to see what it looks like with the re-attributed locations, we can do so.

Theresa has a nice change to the PCA plotter in which she sets the alpha channel as an additional visual queue for a metadata factor…

mm38_hisat_tx <- subset_se(mm38_hisat_tx, subset="sampleid!='iprgc_130'") |>
  subset_se(subset="sampleid!='iprgc_66'")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': error in evaluating the argument 'x' in selecting a method for function 'colData': object 'mm38_hisat_tx' not found
tx_location <- set_conditions(mm38_hisat_tx, fact = "location_atb") |>
  set_batches(fact = "genotype_atb") |>
  set_colors(color_choices[["location"]])
## Error in `h()`:
## ! error in evaluating the argument 'exp' in selecting a method for function 'set_colors': error in evaluating the argument 'exp' in selecting a method for function 'set_batches': error in evaluating the argument 'exp' in selecting a method for function 'set_conditions': object 'mm38_hisat_tx' not found
tx_location_norm <- normalize(tx_location, filter = TRUE, norm = "quant",
                                   transform = "log2", convert = "cpm")
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'normalize': object 'tx_location' not found
filtered_location_pca <- plot_pca(tx_location_norm)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_pca': object 'tx_location_norm' not found
pp(file = "02filtered_images_tx/filtered_location_pca.pdf")
## Warning in pp(file = "02filtered_images_tx/filtered_location_pca.pdf"): The
## directory: 02filtered_images_tx does not exist, will attempt to create it.
filtered_location_pca[["plot"]]
## Error:
## ! object 'filtered_location_pca' not found
plotted <- dev.off()
filtered_location_pca
## Error:
## ! object 'filtered_location_pca' not found
removed_sankey <- plot_meta_sankey(tx_location, color_choices = color_choices,
                                   factors = c("genotype_atb", "location_atb", "time_atb"))
## Error in `h()`:
## ! error in evaluating the argument 'design' in selecting a method for function 'plot_meta_sankey': object 'tx_location' not found
pp(file = "02filtered_images_tx/filtered_sankey.pdf", image = removed_sankey[["plot"]])
## Error:
## ! object 'removed_sankey' not found

Here is Theresa’s text, recall once again that I do not have some of these older samples (iprgc_62):

PC1 vs PC2 identifies retina vs axon is still the main component of variation. We do see though that in the PC2 direction, we see with the new samples added, we don’t see separation based on axonal targets (dLGN vs SCN). In the PC1 vs PC3 plot, we see that it’s PC3 where we start to see variation correlated with axonal compartment. Let’s look at PC1 vs PC2 colored by batch (when they were processed/sequenced) to see if that is what is contributing so much variation in PC2.

Side note: ipRGC 62 seems like an odd ball. This seems to me like it should have been a dLGN P08 sample. Is there any possibility this got mislabeled early on? I went back and double checked to see if all my processing is correct and it indeed was labeled an SCN P15 from the time I got the samples, and it is indeed.

12 DE

I now switched to Theresa’s document ‘WORKING_axonTRAP…’ and will start pulling sections from it. I am reasonably certain I have reasonably similar sample distributions, so I presume I can invoke similar/identical calls for DESeq and friends.

12.1 p8 retinas

In the block immediately before the DE analyses, Theresa created a subset expressionset of only p08 retinas. Thus this initial DE I assume will be used to subtract for the SCN/DLGN analyses that follow. (I guess I could read ahead and find out, but no! I want to be a blank slate)

Theresa’s primary workflow makes heavy use of DESeq2 (@loveModeratedEstimationFold2014) and sva (@leekSVAPackageRemoving2012). In some(most?) of Theresa’s invocations of the all_pairwise() function, she excludes the other methods that it performs. In this workbook, I left those methods on, thus we can evaluate the relative performance DESeq2 vs. some (all? I may have disabled EBSeq/dream because they were taking too long) of the following:

  • limma: (@ritchieLimmaPowersDifferential2015) (among other references) originally written for microarrays.
  • EdgeR: (@robinsonEdgeRBioconductorPackage2010), which shares many assumptions with DESeq2.
  • EBSeq: (@lengEBSeqEmpiricalBayes2013), because I have a soft spot for any Bayesian method.
  • Noiseq: (@tarazonaNOIseqRNAseqDifferential2011), which seeks to directly model variance in an RNASeq dataset and use that to improve the sensitivity of the result, much like:
  • Dream: (@hoffmanDreamPowerfulDifferential2020), written by the same authors (and uses very similar logic) as one of my favorite tools, variancePartition(@hoffmanVariancePartitionInterpretingDrivers2016).
mm38_p8_retina <- subset_se(mm38_hisat_tx, subset = "time_atb=='p08' & location_atb=='retina'")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'mm38_hisat_tx' not found
mm_normal_p8_ret_de <- all_pairwise(mm38_p8_retina, model_svs = "svaseq",
                                    model_fstring = "~ 0 + condition", filter = TRUE)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'mm38_p8_retina' not found
mm_normal_p8_ret_de
## Error:
## ! object 'mm_normal_p8_ret_de' not found

There seems to be a discrepency with previous iterations of this. Let us simplify to just doing deseq and find what is causing it. In my previous iteration, I got 3632 genes in the unique(c()) or het+ko.

deseq_only <- deseq_pairwise(mm38_p8_retina, model_svs = "svaseq",
                             model_fstring = default_fstring, filter = TRUE)
## Error in `h()`:
## ! error in evaluating the argument 'input' in selecting a method for function 'state': object 'mm38_p8_retina' not found
deseq_hetkeeper_tx <- deseq_only$all_tables$wt_retina_vs_het_retina |>
  filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error:
## ! object 'deseq_only' not found
deseq_kokeeper_tx <- deseq_only$all_tables$wt_retina_vs_ko_retina |>
  filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error:
## ! object 'deseq_only' not found
deseq_keepertx <- unique(c(rownames(deseq_hetkeeper_tx),
                        rownames(deseq_kokeeper_tx)))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'deseq_hetkeeper_tx' not found
length(deseq_keepertx)
## Error:
## ! object 'deseq_keepertx' not found
deseq_pair_hetkeeper_tx <- mm_normal_p8_ret_de$deseq$all_tables$wt_retina_vs_het_retina |>
  filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error:
## ! object 'mm_normal_p8_ret_de' not found
deseq_pair_kokeeper_tx <- mm_normal_p8_ret_de$deseq$all_tables$wt_retina_vs_ko_retina |>
  filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error:
## ! object 'mm_normal_p8_ret_de' not found
deseq_pair_keepertx <- unique(c(rownames(deseq_pair_hetkeeper_tx),
                        rownames(deseq_pair_kokeeper_tx)))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'deseq_pair_hetkeeper_tx' not found
length(deseq_pair_keepertx)
## Error:
## ! object 'deseq_pair_keepertx' not found

The following invocation performed by Theresa filters the wt/het comparison for only those genes which increased by at least 0.25 logFC with a significant adjusted p-value. I assume that this is to use the wt samples as a translational control for the ket/ko comparisons; I am therefore thinking that for my purposes, I will therefore separate the contrasts from all_pairwise do this in a stepwise fashion…

The block of code immediately following Theresa’s all_pairwise() invocation is a little confusing for me and warrants some explanation by me to me in the hopes that I do not misunderstand what is happening and the goals therein.

I think I can safely assume that the goal here is to pull out the IDs which increased in het with respect to wild type; even if by a small margin, as long as it is statistically significant vis a vis the adjusted p-value.

I am going to perform what I think is the same thing in a slightly different fashion so that I can share a copy of the results with whomever is interested. I will also repeat Theresa’s invocation and prove to myself that I understood and got the same answer.

wt_het_keeper <- list("het_vs_wt" = c("het_retina", "wt_retina"))
het_wt_table <- combine_de_tables(
  mm_normal_p8_ret_de, keepers = wt_het_keeper, label_column = tx_label_column,
  excel = "03theresa_comparison_excel_tx/het_retina_control.xlsx")
## Error:
## ! object 'mm_normal_p8_ret_de' not found
wanted_sig <- extract_significant_genes(
  het_wt_table, lfc = 0.25, according_to = "deseq",
  excel = "03theresa_comparison_excel_tx/het_retina_control-sig.xlsx")
## Error:
## ! object 'het_wt_table' not found
wanted_het_increased <- wanted_sig[["deseq"]][["ups"]][["het_vs_wt"]]
## Error:
## ! object 'wanted_sig' not found
increased_het_genes <- rownames(wanted_het_increased)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'wanted_het_increased' not found

Here are Theresa’s next lines:

mm_de_normal_p8_ret <- mm_normal_p8_ret_de
## Error:
## ! object 'mm_normal_p8_ret_de' not found
hetkeeper_tx <- mm_de_normal_p8_ret$deseq$all_tables$wt_retina_vs_het_retina |>
  filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error:
## ! object 'mm_de_normal_p8_ret' not found
kokeeper_tx <- mm_de_normal_p8_ret$deseq$all_tables$wt_retina_vs_ko_retina |>
  filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error:
## ! object 'mm_de_normal_p8_ret' not found
keepertx <- unique(c(rownames(hetkeeper_tx),
                        rownames(kokeeper_tx)))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'hetkeeper_tx' not found
## We know a priori that Opn4 is ENSMUSG00000021799
## I do not expect to see it in this set, it should be higher in wt
## retina vs ko retina by a significant margin.
## "ENSMUSG00000021799" %in% keepertx
## Oooohhh but it _is_ higher in het vs. wt, as we saw in
## the violin plot earlier.

I think Rashmi made a compelling point which illustrates why we likely should expect the expression of Opn4 to significantly higher in the heterozygotes vs wild-type:

  1. Recall that the assay is using the immunopurification to extract the RNAs.
  2. The wt samples do not have the cre recombinase and therefore no HA and therefore everything we observe is due to non-specific binding.
  3. The set of genes observed due to non-specific binding is different than het/ko (presumably a larger number of relatively small values), therefore the divisor performed in the cpm is likely relativly large resulting in normalized values getting shifted down to some degree.
  4. On the other hand, the set of genes observed in het/ko are more likely to be only the specific binders and therefore smaller (I can test this) resulting in a smaller divisor and slight shifting up in the cpm values.

This makes me wonder if any normalization methods exist which do something like multiply the values by some value related to the proportion of observed genes; and/or if this is a good/bad/indifferent idea.

Also, just a note for me to remember: RPL22, not RPS22, for some reason I keep thinking the small subunit.

12.2 Prove I understood

hetkeeper_tx <- mm_normal_p8_ret_de$deseq$all_tables$wt_retina_vs_het_retina |>
  filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error:
## ! object 'mm_normal_p8_ret_de' not found
testthat::expect_true(nrow(hetkeeper_tx) == length(increased_het_genes))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'nrow': object 'hetkeeper_tx' not found
taa_keepers <- sort(rownames(hetkeeper_tx))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'sort': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'hetkeeper_tx' not found
atb_keepers <- sort(increased_het_genes)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'sort': object 'increased_het_genes' not found
testthat::expect_equal(taa_keepers, atb_keepers)
## Error:
## ! object 'taa_keepers' not found

Yay! I can read! Now let us repeat for the KO vs wt

wt_ko_keeper <- list("ko_vs_wt" = c("ko_retina", "wt_retina"))
ko_wt_table <- combine_de_tables(
  mm_normal_p8_ret_de, keepers = wt_ko_keeper, label_column = tx_label_column,
  excel = "03theresa_comparison_excel_tx/ko_retina_control.xlsx")
## Error:
## ! object 'mm_normal_p8_ret_de' not found
wanted_sig <- extract_significant_genes(
  ko_wt_table, lfc = 0.25, according_to = "deseq",
  excel = "03theresa_comparison_excel_tx/ko_retina_control-sig.xlsx")
## Error:
## ! object 'ko_wt_table' not found
wanted_ko_increased <- wanted_sig[["deseq"]][["ups"]][["ko_vs_wt"]]
## Error:
## ! object 'wanted_sig' not found
increased_ko_genes <- rownames(wanted_ko_increased)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'wanted_ko_increased' not found

The next thing performed in Theresa’s document is a unique(concatenation of these two gene groups), thus sucking up every gene which was significantly higher in either the knockout or heterzyous samples with respect to wild-type.

This was followed by a couple of merge operations of a little bit of the annotation data; I am not sure I understand the goal yet…

Here is her code. I copied the annotation ‘mgi_symbol’ column to ‘external_gene_name’ so that I need not change any of her code. I am assuming this is the appropriate column of interest, I do not know this for certain, but it seems quite likely.

While I am at it, here is the set_sig_limma() function from Theresa’s helpers.R

set_sig_limma <- function(limma_tbl, factors = NULL) {
  if (is.null(factors)) {
    #set significance for plotting colors
    limma_tbl$Significance <- NA
    limma_tbl[abs(limma_tbl$logFC) <= 1.0 | limma_tbl$adj.P.Val >= 0.05, "Significance"] <- "Not \nEnriched"
    limma_tbl[limma_tbl$logFC >= 1.0  & limma_tbl$adj.P.Val <= 0.05, ][["Significance"]] <- "Disease \nUpregulated"
    limma_tbl[limma_tbl$logFC <= -1.0  & limma_tbl$adj.P.Val <= 0.05, ][["Significance"]] <- "Disease \nDownregulated"
    limma_tbl$Significance <- factor(limma_tbl$Significance, levels = c("Upregulated", "Downregulated",  "Not \nEnriched"))
  } else {
    limma_tbl$Significance <- NA
    limma_tbl[abs(limma_tbl$logFC) <= 1.0 | limma_tbl$adj.P.Val >= 0.05, "Significance"] <- "Not \nEnriched"
    if(nrow(limma_tbl[limma_tbl$logFC >= 1.0  & limma_tbl$adj.P.Val <= 0.05, ]) != 0) {
      limma_tbl[limma_tbl$logFC >= 1.0  & limma_tbl$adj.P.Val <= 0.05, ][["Significance"]] <- factors[1]
    }
    if (nrow(limma_tbl[limma_tbl$logFC <= -1.0 & limma_tbl$adj.P.Val <= 0.05, ]) != 0) {
      limma_tbl[limma_tbl$logFC <= -1.0  & limma_tbl$adj.P.Val <= 0.05, ][["Significance"]] <- factors[2]
    }
    limma_tbl$Significance <- factor(limma_tbl$Significance, levels = c(factors,  "Not \nEnriched"))
  }
  return(limma_tbl)
}

12.2.1 Combining het/wt and ko/wt

keepertx <- unique(c(rownames(hetkeeper_tx), rownames(kokeeper_tx)))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'hetkeeper_tx' not found
length(keepertx)
## Error:
## ! object 'keepertx' not found
df <- mm_de_normal_p8_ret$deseq$all_tables$ko_retina_vs_het_retina |>
  dplyr::mutate(logFC = -logFC) |>
  set_sig_limma(factors = c("Het Enriched", "KO Enriched"))
## Error:
## ! object 'mm_de_normal_p8_ret' not found

My version of the above task makes use of the excludes option of combine_de_tabes. Given the set of unique gene IDs increased in the het/ko, I can ask to exlude anything not in that set. I could also have more parsimoniously directly excluded any gene ID increased in the wt samples. But, Theresa already provided the code to do the former, so it will be less typing/opportunity for silly mistakes to just do that.

both_increased_genes <- unique(c(increased_het_genes, increased_ko_genes))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'unique': object 'increased_het_genes' not found
## arbitrairly grab all genes from one of my data structures.
all_genes <- rownames(assay(mm38_hisat_tx))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'x' in selecting a method for function 'assay': object 'mm38_hisat_tx' not found
exclude_idx <- all_genes %in% both_increased_genes
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function '%in%': object 'all_genes' not found
summary(exclude_idx)
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'summary': object 'exclude_idx' not found
exclude_increased_genes <- all_genes[exclude_idx]
## Error:
## ! object 'all_genes' not found
retina_keepers <- list(
  "het_vs_wt" = c("het_retina", "wt_retina"),
  "ko_vs_wt" = c("ko_retina", "wt_retina"),
  "ko_vs_het" = c("ko_retina", "het_retina"))
## A reminder to myself: there is also a parameter 'wanted_genes'
## which does effectively the same thing as excludes in this context;
## excludes was originally written to allow flexible, keyword-based
## exclusion.
p8_retina_tables <- combine_de_tables(
  mm_normal_p8_ret_de, keepers = retina_keepers,
  wanted_genes = both_increased_genes, label_column = tx_label_column,
  excel = glue("03theresa_comparison_excel_tx/p8_retina_kept_tx_increased_in_wt_tables-v{ver}.xlsx"))
## Error:
## ! object 'mm_normal_p8_ret_de' not found
p8_retina_sig <- extract_significant_genes(
  p8_retina_tables, according_to = "deseq",
  excel = glue("03theresa_comparison_excel_tx/p8_retina_kept_genes_increased_in_wt_sig-v{ver}.xlsx"))
## Error:
## ! object 'p8_retina_tables' not found
opposite_p8_retina_tables <- combine_de_tables(
  mm_normal_p8_ret_de, keepers = retina_keepers,
  excludes = both_increased_genes, label_column = tx_label_column,
  excel = glue("03theresa_comparison_excel_tx/p8_retina_removed_genes_increased_in_wt_tables-v{ver}.xlsx"))
## Error:
## ! object 'mm_normal_p8_ret_de' not found
opposite_p8_retina_sig <- extract_significant_genes(
  p8_retina_tables, according_to = "deseq",
  excel = glue("03theresa_comparison_excel_tx/p8_retina_removed_genes_increased_in_wt_sig-v{ver}.xlsx"))
## Error:
## ! object 'p8_retina_tables' not found

13 Filtering out non-specific genes and examining the results

The following is a copy/paste from Theresa containing the remaining tasks she performed and will provide the template for implementation of the final tasks.

This picks up with the lines from her notebook immediately following the invocation of ‘set_sig_limma(factors = c(“Het Enriched” …’.

For all of the remaining blocks I will copy in her code, turn off its evaluation, run the blocks manually, compare them to her notebook output, then enable each block as I ensure I understand it.

I will likely therefore introduce some small formatting changes and add some additional GSEA/enrichment tasks once the non-specific filtering is complete.

df <- df |>
  filter(rownames(df) %in% keepertx)
## Error in `UseMethod()`:
## ! no applicable method for 'filter' applied to an object of class "function"
labels_ups <- df |>
  filter(adj.P.Val <= 0.05 & abs(logFC) >= 1.0) |>
  arrange(logFC) |>
  head(n = 9)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'head': no applicable method for 'filter' applied to an object of class "function"
labels_downs <- df |>
  filter(adj.P.Val <= 0.05 & abs(logFC) >= 1.0) |>
  arrange(-logFC) |>
  head(n = 11)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'head': no applicable method for 'filter' applied to an object of class "function"
labels <- rbind(labels_ups, labels_downs)
## Error:
## ! object 'labels_ups' not found
res_tbl <- df
DEplot <- ggplot(res_tbl, aes(x = logFC, y = -log10(adj.P.Val))) +
  geom_point(aes(colour = Significance), size = 4) +
  geom_vline(xintercept = c(-1, 1)) +
  geom_hline(yintercept = -log10(0.05)) +
  theme_classic(base_size = 20) +
  xlab("log2(FC)") +
  ylab("-log10(p-value)") +
  theme(legend.position = "right") +
  scale_color_manual(values = c("#F8766D", "#00BFC4", "Grey")) +
  xlim(c(-3, 6))
## Error in `ggplot()`:
## ! `data` cannot be a function.
## i Have you misspelled the `data` argument in `ggplot()`?
pp(file = "03theresa_comparison_images_tx/p08_retina_DE_1312024.pdf")
## Warning in pp(file =
## "03theresa_comparison_images_tx/p08_retina_DE_1312024.pdf"): The directory:
## 03theresa_comparison_images_tx does not exist, will attempt to create it.
DEplot
## Error:
## ! object 'DEplot' not found
plotted <- dev.off()
DEplot
## Error:
## ! object 'DEplot' not found
write_xlsx(df, excel = "excel_tx/retinahet_vs_retinako_WTfiltered.xlsx")
## This function is intended to write xlsx files.
## It was passed an object of type function and does not know what to do with it.
## NULL

13.1 How many ups/downs

ko_enriched <- df |>
  filter(Significance == "KO Enriched")
## Error in `UseMethod()`:
## ! no applicable method for 'filter' applied to an object of class "function"
nrow(ko_enriched)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'nrow': object 'ko_enriched' not found
het_enriched <-  df |>
  filter(Significance == "Het Enriched")
## Error in `UseMethod()`:
## ! no applicable method for 'filter' applied to an object of class "function"
nrow(het_enriched)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'nrow': object 'het_enriched' not found

13.2 category enrichment/GSEA

regulated_genes <- res_tbl |>
  filter(adj.P.Val <= 0.05) |>
  arrange(logFC) |>
  filter(abs(logFC) >= 1.0)
## Error in `UseMethod()`:
## ! no applicable method for 'filter' applied to an object of class "function"
## gsea_result_ko <- gost(query = ko_genes$external_gene_name,
##                        organism = "mmusculus",
##                        evcodes = TRUE,
##                        ordered_query = TRUE)
gsea_result_het <- gost(query = het_enriched[["Row.names"]],
                        organism = "mmusculus",
                        evcodes = TRUE,
                        ordered_query = TRUE)
## Error:
## ! object 'het_enriched' not found
##gsea_result_alldysregulated <- gost(query = alldysregulated_genes$external_gene_name,
##                                    organism = "mmusculus",
##                                    evcodes = TRUE,
##                                    ordered_query = TRUE)

I have a function in my package which seeks to make gProfiler queries a bit more complete and easy. Let us see how similar the result is…

rownames(alldysregulated_genes) <- alldysregulated_genes[["Row.names"]]
alldysregulated_genes[["Row.names"]] <- NULL

het_gp <- simple_gprofiler(rownames(alldysregulated_genes),
                           species = "mmusculus",
                           excel = glue("excel_tx/het_gprofiler-v{ver}.xlsx"))
het_gp
enrichplot::dotplot(het_gp[["BP_enrich"]])
gp_pair <- enrichplot::pairwise_termsim(het_gp[["BP_enrich"]])
enrichplot::emapplot(gp_pair)
enrichplot::ssplot(gp_pair)
enrichplot::treeplot(gp_pair)
upsetplot(het_gp[["BP_enrich"]])

enrichplot::dotplot(het_gp[["REAC_enrich"]])
gp_pair <- enrichplot::pairwise_termsim(het_gp[["REAC_enrich"]])
enrichplot::emapplot(gp_pair)
enrichplot::ssplot(gp_pair)
enrichplot::treeplot(gp_pair)
upsetplot(het_gp[["REAC_enrich"]])

I make a somewhat arbitrary distinction between the concepts of over-enrichment analyses and GSEA: the former (as performed by gprofiler) (@raudvereProfilerWebServer2019) seeks to find groups of genes overrepresented in GO/reactome/etc. These groups of genes are taken exclusively from the top-n/bottom-n genes with respect to fold-change between conditions of interest; in this case most different than wt in the p08 retina ko or het samples.

With that in mind, I can invoke a similar function using the full table of DE results to get what I call the GSEA result using clusterProfiler (@yuIntroductionBiomedicalKnowledge). In the following block I will use the ‘all_cprofiler’ function on the data structures named ‘p8_retina_tables’ and ‘opposite_p8_retina_tables’ in order to get these GSEA results for each contrast performed (het/wt, ko/wt, het/ko). I will follow that up with ‘all_gprofiler’ which does the same, but uses gProfiler’s enrichment analyses (it will therefore include what we just looked at).

p08_retina_all_cp <- all_cprofiler(
  p8_retina_sig, p8_retina_tables, orgdb = "org.Mm.eg.db", orgdb_from = orgdb_from,
  excel = "03theresa_comparison_excel_tx/cprofiler_p08_retina.xlsx")
## Error:
## ! object 'p8_retina_sig' not found
enrichplot::dotplot(p08_retina_all_cp[["ko_vs_het_up"]][["go_data"]][["MF_enrich"]])
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'dotplot': object 'p08_retina_all_cp' not found
p08_topn_gsea <- plot_topn_gsea(p08_retina_all_cp)
## Error in `h()`:
## ! error in evaluating the argument 'gse' in selecting a method for function 'plot_topn_gsea': object 'p08_retina_all_cp' not found
pp(file = "03theresa_comparison_images_tx/gsea_p08_retina_ko_vs_het_top_hit.pdf",)
p08_topn_gsea[["GO_ko_vs_het_up"]][[1]]
## Error:
## ! object 'p08_topn_gsea' not found
dev.off()
## png 
##   2
p08_topn_gsea[["GO_ko_vs_het_up"]][[1]]
## Error:
## ! object 'p08_topn_gsea' not found
p08_topn_gsea[["GO_ko_vs_het_up"]][[2]]
## Error:
## ! object 'p08_topn_gsea' not found
p08_topn_gsea[["GO_ko_vs_het_up"]][[3]]
## Error:
## ! object 'p08_topn_gsea' not found
p08_topn_gsea[["GO_ko_vs_het_up"]][[4]]
## Error:
## ! object 'p08_topn_gsea' not found
p08_topn_gsea[["GO_ko_vs_het_up"]][[5]]
## Error:
## ! object 'p08_topn_gsea' not found
#gsea_ko <-  gsea_result_ko[["result"]] |>
#    select(term_name, p_value, term_size, intersection_size, recall, source, intersection) |>
#    arrange(desc(recall)) |>
#    head(n = 10)
#  gsea_plots_ko <- ggplot(gsea_ko, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
#  geom_bar(stat = "identity")+
#  scale_fill_continuous(low = "blue", high = "red") +
#  theme_bw()+
#  ylab("") +
#  xlab("GSEA Score")
gsea_het <-  gsea_result_het[["result"]] |>
  dplyr::select(term_name, p_value, term_size, intersection_size, recall, source, intersection) |>
  arrange(desc(recall)) |>
  head(n = 10)
gsea_plots_het <- ggplot(gsea_het, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
  geom_bar(stat = "identity") +
  scale_fill_continuous(low = "blue", high = "red") +
  theme_bw() +
  ylab("") +
  xlab("Over Representation Score")
pp(file = "03theresa_comparison_images_tx/GSEA_p08_axontrap_retinahet_upregulated_vs_retinako.pdf")
gsea_plots_het
plotted <- dev.off()
gsea_plots_het

14 SCN Het vs KO

It is only now that I realized we are splitting the data by location for each set of comparisons. I think that, left to my own devices, I would prefer to keep the input data structure intact, perform the somewhat larger number of contrasts, and then split up the results. Ideally this will slightly improve the fidelity of the results returned by DESeq2 and friends. But, I will run the state of Theresa’s notebook with as few changes as possible first, then add this.

14.1 PCA

I am going to skip this PCA plot for a couple of reasons: I already did a superset of it, and the subset Theresa performed is not valid given the set of samples included in my sample sheet, and figuring out the actually corresponding subset will take me forever… In addition, I want to use my mm38_hisat_tx for everything…

scn_samples <- subset_se(mm38_hisat_tx,
                           subset = "location_atb == 'scn'") |>
  set_batches(fact = "location_atb") |>
  set_conditions(fact = "genotype_atb", colors = color_choices[["genotype"]])
## Error in `h()`:
## ! error in evaluating the argument 'exp' in selecting a method for function 'set_conditions': error in evaluating the argument 'exp' in selecting a method for function 'set_batches': error in evaluating the argument 'x' in selecting a method for function 'colData': object 'mm38_hisat_tx' not found
scn_norm <- normalize(scn_samples, filter = TRUE, convert = "cpm",
                           transform = "log2", batch = "svaseq")
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'normalize': object 'scn_samples' not found
scn_norm_pca <- plot_pca(scn_norm)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_pca': object 'scn_norm' not found
scn_norm_pca
## Error:
## ! object 'scn_norm_pca' not found

15 Library sizes post-deduplication

Theresa’s next operation was to perform libsize/nonzero plots. I already did the pre/post deduplication nonzero, here is the analagous libsize.

v2 is pre-deduplication and v3 is post.

post_filter_nonzero <- plot_quantreads(mm38_hisat_tx, text = FALSE)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_quantreads': object 'mm38_hisat_tx' not found
pp(file = "01diagnostic_images_tx/post_all_filteres_nonzero.pdf")
post_filter_nonzero[["plot"]]
## Error:
## ! object 'post_filter_nonzero' not found
plotted <- dev.off()
post_filter_nonzero
## Error:
## ! object 'post_filter_nonzero' not found

Theresa also produced a density/sample plot, that might prove quite useful for these due to their significantly larger variance across samples (due to deduplication).

pp(file = "01diagnostic_images_tx/sample_density.pdf")
mm38_density <- plot_density(loc_geno_nt)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_density': object 'loc_geno_nt' not found
mm38_density[["plot"]] +
  theme(legend.position = "none")
## Error:
## ! object 'mm38_density' not found
plot_boxplot(loc_geno_nt)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_boxplot': object 'loc_geno_nt' not found
dev.off()
## png 
##   2
mm38_density[["plot"]] +
  theme(legend.position = "none")
## Error:
## ! object 'mm38_density' not found
box <- plot_boxplot(loc_geno_nt)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'plot_boxplot': object 'loc_geno_nt' not found
pp(file = "01diagnostic_images_tx/sample_boxplot.pdf")
box
## function (which = "plot", lty = "solid", ...) 
## {
##     which <- pmatch(which[1L], c("plot", "figure", "inner", "outer"))
##     .External.graphics(C_box, which = which, lty = lty, ...)
##     invisible()
## }
## <bytecode: 0x562a6b962ae8>
## <environment: namespace:graphics>
dev.off()
## png 
##   2
box
## function (which = "plot", lty = "solid", ...) 
## {
##     which <- pmatch(which[1L], c("plot", "figure", "inner", "outer"))
##     .External.graphics(C_box, which = which, lty = lty, ...)
##     invisible()
## }
## <bytecode: 0x562a6b962ae8>
## <environment: namespace:graphics>

There is some difference across sample densities, but it is not too crazytown.

16 Diverging a little

At this point in the document I read ahead a bit and came to the conclusion that it repeats the above logic of taking the union of wt comparisons to remove genes from the appropriate het/ko or p15/p08 or location comparisons. This seems quite reasonable to me, but I would prefer to not separate all the data, so I will attempt to duplicate and slightly streamline this logic on the full dataset. Thus I am going to skip down to the end and attempt to implement this.

17 My version of the global analysis

I want to have an invocation of all_pairwise() which uses all samples, in the following block I will set that up using a set of ‘keepers’ which will be named by time, location, then 2 letters for the numerator/denominator: w for WT, h for het, d for delta; thus “p08_retina_hw” is comparing the het/wt for the p08 retina samples.

If they are of interest, I will have a separate set which follows the same convention with names like “p08_ko_sr” to compare p08 deltas with SCN as the numerator and retina as the denominator.

17.1 Set up the exclusion dataset

The most peculiar aspect of this analysis resides in the choices around choosing which genes to consider when comparing the genotypes/locations/times. The general idea is pretty clear: find the genes which are non-specifically being pulled down in the WT samples and either exclude or discount them. The various potential methods for performing this are confusing:

  1. Which set of comparisons of wt/ko wt/het do we use to exclude/discount genes?
    1. Should it be a combination of all samples wt vs. x?
    2. Should it be only the ‘relevant’ comparison, e.g. if we are comparing p08_dlgn_het vs. p08_scn_het; do we remove genes observed in (p08_dlgn_het/wt && p08_scn_het/wt)
  2. Do we instead attempt to use this x/wt information to normalize the expression values in the other conditions and keep those genes?

Theresa’s current worksheet implements a version of 1b in which she separated the various input gene sets to define the exclusion genes. I am going to repeat this, but leave the starting data structure intact.

In this first iteration, I will do that by creating a simplified model of the data which combines the time/genotype/location and using sva. In my next iteration I will use a full statistical model containing each of those factors (and probably also using sva).

Note: my color choices are kind of garbage.

In addition, the exclusion dataset is the same as the analysis dataset, it is really only the contrasts which will be different.

tx_pairwise_input <- set_conditions(mm38_hisat_tx, fact = "time_geno_loc",
                                    colors = color_choices[["all"]])
## Error in `h()`:
## ! error in evaluating the argument 'exp' in selecting a method for function 'set_conditions': object 'mm38_hisat_tx' not found

17.2 A heatmap of specific genes across all conditions

all_cond_gene_heatmap_start <- normalize(tx_pairwise_input, filter = "simple",
                                         length_column = "cds_length",
                                         convert = "rpkm", transform = "log2")
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'normalize': object 'tx_pairwise_input' not found
all_cond_gene_heatmap_input <- median_by_factor(all_cond_gene_heatmap_start)
## Error in `h()`:
## ! error in evaluating the argument 'data' in selecting a method for function 'median_by_factor': object 'all_cond_gene_heatmap_start' not found
all_cond_mtrx <- all_cond_gene_heatmap_input[["medians"]]
## Error:
## ! object 'all_cond_gene_heatmap_input' not found
color_order <- colnames(all_cond_mtrx)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colnames': object 'all_cond_mtrx' not found
na_idx <- is.na(all_cond_mtrx)
## Error:
## ! object 'all_cond_mtrx' not found
all_cond_mtrx[na_idx] <- 0
## Error:
## ! object 'all_cond_mtrx' not found
variances <- matrixStats::rowVars(as.matrix(all_cond_mtrx))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'as.matrix': object 'all_cond_mtrx' not found
variant_genes <- variances > 6.2
## Error:
## ! object 'variances' not found
input_mtrx <- all_cond_mtrx[variant_genes, ]
## Error:
## ! object 'all_cond_mtrx' not found
cond_colors <- get_colors_by_condition(tx_pairwise_input, levels = color_order)
## Error in `h()`:
## ! error in evaluating the argument 'exp' in selecting a method for function 'get_colors_by_condition': object 'tx_pairwise_input' not found
dim(input_mtrx)
## Error:
## ! object 'input_mtrx' not found
pp(file = "04inclusion_comparisons_tx/top_104_variant_rpkm_genes_heatmap.pdf")
## Warning in pp(file =
## "04inclusion_comparisons_tx/top_104_variant_rpkm_genes_heatmap.pdf"): The
## directory: 04inclusion_comparisons_tx does not exist, will attempt to create
## it.
gplots::heatmap.2(as.matrix(input_mtrx), scale = "none", trace = "none",
                  ColSideColors = cond_colors)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'as.matrix': object 'input_mtrx' not found
dev.off()
## png 
##   2

18 TODO: Change the above to subtract wt

Rashmi suggested we should do the above plot after subtracting the wt counts. This is a good idea, but it will have to wait until we finish the current set.

18.1 Set up the contrasts

In the following few blocks I will set up the various comparisons of interest. Starting with the set of genes to exclude because they were observed to bind non-specifically in the wt samples.

18.1.1 Inclusion contrasts

In each exclusion I will have the contrast first followed by the pair of contrasts which will be used to define the gene set to exclude.

  • p15_het_dlgn/p08_het_dlgn: p15_wt_dlgn/p15_het_dlgn, p08_wt_dlgn/p08_het_dlgn; remove the genes increased in wt.
  • p15_ko_scn/p08_ko_scn: p15_wt_scn/p15_ko_scn, p08_wt_scn/p15_ko_scn
  • p15_het_retina/p08_het_retina: I think you get it, wt/het for both p15 retinas and p08 retinas…

Put slightly differently, for every term of interest I will create a contrast with the wt as numerator and the desired term as denominator, then pull out the genes increased in wt.

inclusions <- list(
  ## I like alphabetizing things, start with dlgn
  "p15_het_dlgn" = c("p15_het_dlgn", "p15_wt_dlgn"),
  "p08_het_dlgn" = c("p08_het_dlgn", "p08_wt_dlgn"),
  "p15_ko_dlgn" = c("p15_ko_dlgn", "p15_wt_dlgn"),
  "p08_ko_dlgn" = c("p08_ko_dlgn", "p08_wt_dlgn"),
  ## Then retinas
  "p15_het_retina" = c("p15_het_retina", "p15_wt_retina"),
  "p08_het_retina" = c("p08_het_retina", "p08_wt_retina"),
  "p15_ko_retina" = c("p15_ko_retina", "p15_wt_retina"),
  "p08_ko_retina" = c("p08_ko_retina", "p08_wt_retina"),
  ## Then scn
  "p15_het_scn" = c("p15_het_scn", "p15_wt_scn"),
  "p08_het_scn" = c("p08_het_scn", "p08_wt_scn"),
  "p15_ko_scn" = c("p15_ko_scn", "p15_wt_scn"),
  "p08_ko_scn" = c("p08_ko_scn", "p08_wt_scn"))

18.1.2 Time contrasts

For each location/genotype of interest, let us compare p15/p08

time_keepers <- list(
  ## DLGN
  "t_het_dlgn" = c("p15_het_dlgn", "p08_het_dlgn"),
  "t_ko_dlgn" = c("p15_ko_dlgn", "p08_ko_dlgn"),
  ## Retina
  "t_het_retina" = c("p15_het_retina", "p08_het_retina"),
  "t_ko_retina" = c("p15_ko_retina", "p08_ko_retina"),
  ## SCN
  "t_het_scn" = c("p15_het_scn", "p08_het_scn"),
  "t_ko_scn" = c("p15_ko_scn", "p08_ko_scn"))

18.1.3 Location contrasts

Compare locations and keep time/genotype consistent. I will use the location initials to define numerator/denominator.

location_keepers <- list(
  ## dlgn/retina
  "dr_p08_het" = c("p08_het_dlgn", "p08_het_retina"),
  "dr_p15_het" = c("p15_het_dlgn", "p15_het_retina"),
  "dr_p08_ko" = c("p08_ko_dlgn", "p08_ko_retina"),
  "dr_p15_ko" = c("p15_ko_dlgn", "p15_ko_retina"),
  ## scn/retina
  "sr_p08_het" = c("p08_het_scn", "p08_het_retina"),
  "sr_p15_het" = c("p15_het_scn", "p15_het_retina"),
  "sr_p08_ko" = c("p08_ko_scn", "p08_ko_retina"),
  "sr_p15_ko" = c("p15_ko_scn", "p15_ko_retina"),
  ## dlgn/scn
  "ds_p08_het" = c("p08_het_dlgn", "p08_het_scn"),
  "ds_p15_het" = c("p15_het_dlgn", "p15_het_scn"),
  "ds_p08_ko" = c("p08_ko_dlgn", "p08_ko_scn"),
  "ds_p15_ko" = c("p15_ko_dlgn", "p15_ko_scn"))

18.1.4 Genotype contrasts

Compare ko/het while keeping time/location constant. Similarly, use the initials to denote numerator/denominator, which will always be kh.

genotype_keepers <- list(
  ## DLGN
  "kh_p08_dlgn" = c("p08_ko_dlgn", "p08_het_dlgn"),
  "kh_p15_dlgn" = c("p15_ko_dlgn", "p15_het_dlgn"),
  ## Retina
  "kh_p08_retina" = c("p08_ko_retina", "p08_het_retina"),
  "kh_p15_retina" = c("p15_ko_retina", "p15_het_retina"),
  ## SCN
  "kh_p08_scn" = c("p08_ko_scn", "p08_het_scn"),
  "kh_p15_scn" = c("p15_ko_scn", "p15_het_scn"))

18.2 Perform the exclusion comparison

My all_pairwise() function now has a parameter which allows me to choose which contrasts to perform instead of literally doing every possible comparison. That is well suited for these operations:

In a container, the following appears to fail with:

“error code 1 from Lapack routine ‘dgesdd’”

Running it manually outside the container results in it working without error. I assume therefore that the problem lies in the compilation flags of LAPACK in the container.

Note: This problem was fixed by removing some parallelization.

inclusion_de <- all_pairwise(
  tx_pairwise_input, filter = "simple", model_fstring = default_fstring,
  keepers = inclusions, model_svs = "svaseq")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
inclusion_de
## Error:
## ! object 'inclusion_de' not found
inclusion_tables <- combine_de_tables(
  inclusion_de, keepers = inclusions, label_column = tx_label_column,
  excel = glue("04inclusion_comparisons_tx/inclusion_tables-v{ver}.xlsx"))
## Error:
## ! object 'inclusion_de' not found
inclusion_tables
## Error:
## ! object 'inclusion_tables' not found
## 202603: I successfully recapitulated previous non-container result.

inclusion_sig <- extract_significant_genes(
  inclusion_tables, lfc = filter_lfc_cutoff, p = filter_adjp_cutoff, according_to = "deseq",
  excel = glue("04inclusion_comparisons_tx/inclusion_sig-v{ver}.xlsx"))
## Error:
## ! object 'inclusion_tables' not found
inclusion_sig
## Error:
## ! object 'inclusion_sig' not found

202505: A strange thing happened here in this iteration: the plot of the significant genes is the exact same as the previous iteration; but the table of numbers of genes looks different.

For example, the previous table showed: p15_het_dlgn with 2067 up and 2381 down. The plot shows exactly that; but the new table shows 607 up and 1229 down. Let us check the actual data structure and see what is up?

I think I get it: when we do the extract_significant_genes above, we explicitly set a non-standard p-value and logFC because we are explicitly attempting to use a very loose definition of the set of genes which are in greater abundance than their most similar wild-type. However, when I create the barplot of significant genes; those values are explicitly set to 0,1,2 logFCs and p-value 0.05. Therefore, what I need to do, in order to check consistency, is to repeat this call but with the default FC/p values and see what the numbers look like.

test_inclusion <- extract_significant_genes(
  inclusion_tables, according_to = "deseq", excel = "04inclusion_comparisons_tx/default_inclusion_sig.xlsx")
## Error:
## ! object 'inclusion_tables' not found
test_inclusion
## Error:
## ! object 'test_inclusion' not found

Yeah, I think this makes sense; what I need to do: change the significant bar plot so that it uses the lfc cutoff argument as the second of its 3 cutoffs. That should ensure that these numbers are consistent across analyses and parameters provided.

dim(inclusion_sig$deseq$ups$p15_het_dlgn)
## Error:
## ! object 'inclusion_sig' not found
test_all_up <- inclusion_tables$data$p15_het_dlgn[["deseq_logfc"]] >= 0.1 &
  inclusion_tables$data$p15_het_dlgn[["deseq_adjp"]] <= 0.1
## Error:
## ! object 'inclusion_tables' not found
summary(test_all_up)
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'summary': object 'test_all_up' not found

Ohh, I get it, when I was testing this out manually, I set the logFC to 1.0 instead of the very minimal 0.1 we have been using for this!

19 MA/Volcano plots of x vs wt

Rashmi asked to see the comparisons against wt; I will name each file xw to show that it is x vs wt. for whatever other parameters are being examined. It is likely that some colors will be wrong because this is my first time creating these plots and we are doing them manually.

19.1 p15_het_dlgn

allc <- color_choices[["all"]]
table_name <- "p15_het_dlgn"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p15_het_dlgn"
denom <- "p15_wt_dlgn"
hw_p15_dlgn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/hw_p15_dlgn_volcano.pdf", width = 9, height = 9)
## Warning in pp(file = "05inclusion_volcano_ma_tx/hw_p15_dlgn_volcano.pdf", : The
## directory: 05inclusion_volcano_ma_tx does not exist, will attempt to create it.
hw_p15_dlgn_volcano[["plot"]]
## Error:
## ! object 'hw_p15_dlgn_volcano' not found
plotted <- dev.off()
hw_p15_dlgn_volcano[["plot"]]
## Error:
## ! object 'hw_p15_dlgn_volcano' not found
hw_p15_dlgn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/hw_p15_dlgn_ma.pdf", width = 9, height = 9)
hw_p15_dlgn_ma[["plot"]]
## Error:
## ! object 'hw_p15_dlgn_ma' not found
plotted <- dev.off()
hw_p15_dlgn_ma[["plot"]]
## Error:
## ! object 'hw_p15_dlgn_ma' not found

19.2 p08_het_dlgn

table_name <- "p08_het_dlgn"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p08_het_dlgn"
denom <- "p08_wt_dlgn"
hw_p08_dlgn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/hw_p08_dlgn_volcano.pdf", width = 9, height = 9)
hw_p08_dlgn_volcano[["plot"]]
## Error:
## ! object 'hw_p08_dlgn_volcano' not found
plotted <- dev.off()
hw_p08_dlgn_volcano[["plot"]]
## Error:
## ! object 'hw_p08_dlgn_volcano' not found
hw_p08_dlgn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/hw_p08_dlgn_ma.pdf", width = 9, height = 9)
hw_p08_dlgn_ma[["plot"]]
## Error:
## ! object 'hw_p08_dlgn_ma' not found
plotted <- dev.off()
hw_p08_dlgn_ma[["plot"]]
## Error:
## ! object 'hw_p08_dlgn_ma' not found

19.3 p15_ko_dlgn

table_name <- "p15_ko_dlgn"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p15_ko_dlgn"
denom <- "p15_wt_dlgn"
kw_p15_dlgn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/kw_p15_dlgn_volcano.pdf", width = 9, height = 9)
kw_p15_dlgn_volcano[["plot"]]
## Error:
## ! object 'kw_p15_dlgn_volcano' not found
plotted <- dev.off()
kw_p15_dlgn_volcano[["plot"]]
## Error:
## ! object 'kw_p15_dlgn_volcano' not found
kw_p15_dlgn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/kw_p15_dlgn_ma.pdf", width = 9, height = 9)
kw_p15_dlgn_ma[["plot"]]
## Error:
## ! object 'kw_p15_dlgn_ma' not found
plotted <- dev.off()
kw_p15_dlgn_ma[["plot"]]
## Error:
## ! object 'kw_p15_dlgn_ma' not found

19.4 p08_ko_dlgn

table_name <- "p08_ko_dlgn"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p08_ko_dlgn"
denom <- "p08_wt_dlgn"
kw_p08_dlgn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/kw_p08_dlgn_volcano.pdf", width = 9, height = 9)
kw_p08_dlgn_volcano[["plot"]]
## Error:
## ! object 'kw_p08_dlgn_volcano' not found
plotted <- dev.off()
kw_p08_dlgn_volcano[["plot"]]
## Error:
## ! object 'kw_p08_dlgn_volcano' not found
kw_p08_dlgn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/kw_p08_dlgn_ma.pdf", width = 9, height = 9)
kw_p08_dlgn_ma[["plot"]]
## Error:
## ! object 'kw_p08_dlgn_ma' not found
plotted <- dev.off()
kw_p08_dlgn_ma[["plot"]]
## Error:
## ! object 'kw_p08_dlgn_ma' not found

19.5 p15_het_retina

table_name <- "p15_het_retina"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p15_het_retina"
denom <- "p15_wt_retina"
hw_p15_retina_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/hw_p15_retina_volcano.pdf", width = 9, height = 9)
hw_p15_retina_volcano[["plot"]]
## Error:
## ! object 'hw_p15_retina_volcano' not found
plotted <- dev.off()
hw_p15_retina_volcano[["plot"]]
## Error:
## ! object 'hw_p15_retina_volcano' not found
hw_p15_retina_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/hw_p15_retina_ma.pdf", width = 9, height = 9)
hw_p15_retina_ma[["plot"]]
## Error:
## ! object 'hw_p15_retina_ma' not found
plotted <- dev.off()
hw_p15_retina_ma[["plot"]]
## Error:
## ! object 'hw_p15_retina_ma' not found

19.6 p08_het_retina

table_name <- "p08_het_retina"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p08_het_retina"
denom <- "p08_wt_retina"
hw_p08_retina_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/hw_p08_retina_volcano.pdf", width = 9, height = 9)
hw_p08_retina_volcano[["plot"]]
## Error:
## ! object 'hw_p08_retina_volcano' not found
plotted <- dev.off()
hw_p08_retina_volcano[["plot"]]
## Error:
## ! object 'hw_p08_retina_volcano' not found
hw_p08_retina_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/hw_p08_retina_ma.pdf", width = 9, height = 9)
hw_p08_retina_ma[["plot"]]
## Error:
## ! object 'hw_p08_retina_ma' not found
plotted <- dev.off()
hw_p08_retina_ma[["plot"]]
## Error:
## ! object 'hw_p08_retina_ma' not found

19.7 p15_ko_retina

table_name <- "p15_ko_retina"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p15_ko_retina"
denom <- "p15_wt_retina"
kw_p15_retina_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/kw_p15_retina_volcano.pdf", width = 9, height = 9)
kw_p15_retina_volcano[["plot"]]
## Error:
## ! object 'kw_p15_retina_volcano' not found
plotted <- dev.off()
kw_p15_retina_volcano[["plot"]]
## Error:
## ! object 'kw_p15_retina_volcano' not found
kw_p15_retina_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/kw_p15_retina_ma.pdf", width = 9, height = 9)
kw_p15_retina_ma[["plot"]]
## Error:
## ! object 'kw_p15_retina_ma' not found
plotted <- dev.off()
kw_p15_retina_ma[["plot"]]
## Error:
## ! object 'kw_p15_retina_ma' not found

19.8 p08_ko_retina

table_name <- "p08_ko_retina"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p08_ko_retina"
denom <- "p08_wt_retina"
kw_p08_retina_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/kw_p08_retina_volcano.pdf", width = 9, height = 9)
kw_p08_retina_volcano[["plot"]]
## Error:
## ! object 'kw_p08_retina_volcano' not found
plotted <- dev.off()
kw_p08_retina_volcano[["plot"]]
## Error:
## ! object 'kw_p08_retina_volcano' not found
kw_p08_retina_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/kw_p08_retina_ma.pdf", width = 9, height = 9)
kw_p08_retina_ma[["plot"]]
## Error:
## ! object 'kw_p08_retina_ma' not found
plotted <- dev.off()
kw_p08_retina_ma[["plot"]]
## Error:
## ! object 'kw_p08_retina_ma' not found

19.9 p15_het_scn

table_name <- "p15_het_scn"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p15_het_scn"
denom <- "p15_wt_scn"
hw_p15_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/hw_p15_scn_volcano.pdf", width = 9, height = 9)
hw_p15_scn_volcano[["plot"]]
## Error:
## ! object 'hw_p15_scn_volcano' not found
plotted <- dev.off()
hw_p15_scn_volcano[["plot"]]
## Error:
## ! object 'hw_p15_scn_volcano' not found
hw_p15_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/hw_p15_scn_ma.pdf", width = 9, height = 9)
hw_p15_scn_ma[["plot"]]
## Error:
## ! object 'hw_p15_scn_ma' not found
plotted <- dev.off()
hw_p15_scn_ma[["plot"]]
## Error:
## ! object 'hw_p15_scn_ma' not found

19.10 p08_het_scn

table_name <- "p08_het_scn"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p08_het_scn"
denom <- "p08_wt_scn"
hw_p08_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/hw_p08_scn_volcano.pdf", width = 9, height = 9)
hw_p08_scn_volcano[["plot"]]
## Error:
## ! object 'hw_p08_scn_volcano' not found
plotted <- dev.off()
hw_p08_scn_volcano[["plot"]]
## Error:
## ! object 'hw_p08_scn_volcano' not found
hw_p08_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/hw_p08_scn_ma.pdf", width = 9, height = 9)
hw_p08_scn_ma[["plot"]]
## Error:
## ! object 'hw_p08_scn_ma' not found
plotted <- dev.off()
hw_p08_scn_ma[["plot"]]
## Error:
## ! object 'hw_p08_scn_ma' not found

19.11 p15_ko_scn

table_name <- "p15_ko_scn"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p15_ko_scn"
denom <- "p15_wt_scn"
kw_p15_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/kw_p15_scn_volcano.pdf", width = 9, height = 9)
kw_p15_scn_volcano[["plot"]]
## Error:
## ! object 'kw_p15_scn_volcano' not found
plotted <- dev.off()
kw_p15_scn_volcano[["plot"]]
## Error:
## ! object 'kw_p15_scn_volcano' not found
kw_p15_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/kw_p15_scn_ma.pdf", width = 9, height = 9)
kw_p15_scn_ma[["plot"]]
## Error:
## ! object 'kw_p15_scn_ma' not found
plotted <- dev.off()
kw_p15_scn_ma[["plot"]]
## Error:
## ! object 'kw_p15_scn_ma' not found

19.12 p08_ko_scn

table_name <- "p08_ko_scn"
table <- inclusion_tables[["data"]][[table_name]]
## Error:
## ! object 'inclusion_tables' not found
num <- "p08_ko_scn"
denom <- "p08_wt_scn"
kw_p08_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = allc[[denom]], color_high = allc[[num]],
  label_column = "external_transcript_name", label = 10, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "05inclusion_volcano_ma_tx/kw_p08_scn_volcano.pdf", width = 9, height = 9)
kw_p08_scn_volcano[["plot"]]
## Error:
## ! object 'kw_p08_scn_volcano' not found
plotted <- dev.off()
kw_p08_scn_volcano[["plot"]]
## Error:
## ! object 'kw_p08_scn_volcano' not found
kw_p08_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = allc[[denom]], color_high = allc[[num]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = 10)
## Error in `input[[expr_col]]`:
## ! object of type 'closure' is not subsettable
pp(file = "05inclusion_volcano_ma_tx/kw_p08_scn_ma.pdf", width = 9, height = 9)
kw_p08_scn_ma[["plot"]]
## Error:
## ! object 'kw_p08_scn_ma' not found
plotted <- dev.off()
kw_p08_scn_ma[["plot"]]
## Error:
## ! object 'kw_p08_scn_ma' not found

See the shared/unique genes in these sets.

inclusion_upsets <- upsetr_sig(inclusion_sig)
## Error:
## ! object 'inclusion_sig' not found
inclusion_intersects <- write_upset_groups(
  inclusion_upsets, excel = "04inclusion_comparison_tx/inclusion_gene_groups.xlsx")
## Error:
## ! object 'inclusion_upsets' not found

19.12.1 Extract genes included for each set of contrasts

Now, using that function, pull out the gene IDs of genes we do not trust because they were too high in wt for every contrast we are likely to perform.

The following was a modified version of the inclusion function which is somewhat more restrictive.

extract_inclusions_strict <- function(inclusion_sig, inclusion_tables, inclusions, keepers,
                                      all_genes, according_to = "deseq", which = "ups") {
  retlist <- list()
  table_names <- names(inclusion_sig[[according_to]][[which]])
  for (c_num in seq_along(keepers)) {
    contrast <- names(keepers)[c_num]
    numerator_name <- keepers[[c_num]][1]
    denominator_name <- keepers[[c_num]][2]
    ## In my new branch I cleaned up the sanitizer function for contrasts so this is not needed.
    ## The following two lines are no longer needed because of the cleanups I performed.
    ##numerator_name <- gsub(x = numerator_name, pattern = "(het|ko|wt)", replacement = "_\\1_")
    ##denominator_name <- gsub(x = denominator_name, pattern = "(het|ko|wt)", replacement = "_\\1_")
    numerator_table <- inclusion_sig[[according_to]][[which]][[numerator_name]]
    numerator_genes <- rownames(numerator_table)
    denominator_table <- inclusion_sig[[according_to]][[which]][[denominator_name]]
    denominator_genes <- rownames(denominator_table)
    df_columns <- paste0("deseq_", c("logfc", "adjp", "den"))
    included_num <- inclusion_tables[["data"]][[numerator_name]][, df_columns]
    colnames(included_num) <- c("numerator_vs_wt_logfc", "numerator_vs_wt_adjp", "num_wt_mean_exprs")
    included_den <- inclusion_tables[["data"]][[denominator_name]][, df_columns]
    colnames(included_den) <- c("denominator_vs_wt_logfc", "denominator_vs_wt_adjp", "den_wt_mean_exprs")
    ## I think this is where things went wrong,
    ## compare this modified line to the original to prove it.
    included_df <- merge(included_num, included_den, by = "row.names")
    ## Previously, I did not specify the merge action, all = FALSE by default.
    ## This then will result in a difference in the rows observed
    ## included_df <- merge(included_num, included_den, by = "row.names", all = FALSE)
    rownames(included_df) <- included_df[["Row.names"]]
    included_df[["Row.names"]] <- NULL
    concatenated_genes <- c(numerator_genes, denominator_genes)
    both_gene_idx <- duplicated(concatenated_genes)
    genes_in_both <- concatenated_genes[both_gene_idx]
    message("The set of unique genes higher in ", numerator_name,
            " vs. wt is ", length(numerator_genes), ".")
    message("The set of unique genes higher in ", denominator_name,
            " vs. wt is ", length(denominator_genes), ".")
    message("The intersection of them is ", length(genes_in_both), " genes.")
    include_name <- paste0("inc_", contrast)
    include_idx <- all_genes %in% genes_in_both
    include_genes <- all_genes[include_idx]
    df_name <- paste0("df_", contrast)
    retlist[[df_name]] <- included_df
    written_inclusion <- write_xlsx(
      data = included_df,
      excel = glue("07included_strict_genes_excel_tx/{include_name}-v{ver}.xlsx"))
    retlist[[include_name]] <- include_genes
    retlist[[contrast]] <- include_genes
  }
  return(retlist)
}

This is the pre-202505 version of this function.

extract_inclusions <- function(inclusion_sig, inclusion_tables, inclusions, keepers, all_genes,
                               according_to = "deseq", which = "ups") {
  retlist <- list()
  table_names <- names(inclusion_sig[[according_to]][[which]])
  for (c_num in seq_along(keepers)) {
    contrast <- names(keepers)[c_num]
    numerator_name <- keepers[[c_num]][1]
    denominator_name <- keepers[[c_num]][2]
    ## In my new branch I cleaned up the sanitizer function for contrasts so this is not needed.
    ## The following two lines are no longer needed because of the cleanups I performed.
    ##numerator_name <- gsub(x = numerator_name, pattern = "(het|ko|wt)", replacement = "_\\1_")
    ##denominator_name <- gsub(x = denominator_name, pattern = "(het|ko|wt)", replacement = "_\\1_")
    numerator_table <- inclusion_sig[[according_to]][[which]][[numerator_name]]
    numerator_genes <- rownames(numerator_table)
    denominator_table <- inclusion_sig[[according_to]][[which]][[denominator_name]]
    denominator_genes <- rownames(denominator_table)
    df_columns <- paste0("deseq_", c("logfc", "adjp", "den"))
    included_num <- inclusion_tables[["data"]][[numerator_name]][, df_columns]
    colnames(included_num) <- c("numerator_vs_wt_logfc", "numerator_vs_wt_adjp", "num_wt_mean_exprs")
    included_den <- inclusion_tables[["data"]][[denominator_name]][, df_columns]
    colnames(included_den) <- c("denominator_vs_wt_logfc", "denominator_vs_wt_adjp", "den_wt_mean_exprs")
    included_df <- merge(included_num, included_den, by = "row.names")
    rownames(included_df) <- included_df[["Row.names"]]
    included_df[["Row.names"]] <- NULL
    include_genes <- unique(c(numerator_genes, denominator_genes))
    message("The set of unique genes higher in ", numerator_name,
            " vs. wt is ", length(numerator_genes), ".")
    message("The set of unique genes higher in ", denominator_name,
            " vs. wt is ", length(denominator_genes), ".")
    message("The unique union of them is ", length(include_genes), " genes.")
    include_name <- paste0("inc_", contrast)
    include_idx <- all_genes %in% include_genes
    include_genes <- all_genes[include_idx]
    df_name <- paste0("df_", contrast)
    retlist[[df_name]] <- included_df
    written_inclusion <- write_xlsx(data = included_df,
                                    excel = glue("07included_genes_tx/{include_name}-v{ver}.xlsx"))
    retlist[[include_name]] <- include_genes
    retlist[[contrast]] <- include_genes
  }
  return(retlist)
}

20 ‘Normal’ Inclusion extraction

Here is the full set of gene IDs

all_genes <- rownames(assay(tx_pairwise_input))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'x' in selecting a method for function 'assay': object 'tx_pairwise_input' not found

In the following blocks I am including the union of genes observed higher than wt in either of the numerator or denominator for each contrast.

20.1 Time

time_inclusions <- extract_inclusions(inclusion_sig, inclusion_tables, inclusions,
                                      time_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found

20.2 Location

location_inclusions <- extract_inclusions(inclusion_sig, inclusion_tables, inclusions,
                                          location_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found

20.3 Genotype

genotype_inclusions <- extract_inclusions(inclusion_sig, inclusion_tables, inclusions,
                                          genotype_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found

21 The strict ‘inclusion’ set

21.1 Time

time_inclusions_strict <- extract_inclusions_strict(inclusion_sig, inclusion_tables, inclusions,
                                                    time_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found

21.2 Location

location_inclusions_strict <- extract_inclusions_strict(inclusion_sig, inclusion_tables, inclusions,
                                                        location_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found

21.3 Genotype

genotype_inclusions_strict <- extract_inclusions_strict(inclusion_sig, inclusion_tables, inclusions,
                                                        genotype_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found

21.3.1 Check vs Theresa’s filter

Up above Theresa performed a 0.25 log2FC and 0.05 adjp filter which provided a set of 2,640 genes observed higher in the p08 het retinas vs. wt retinas. I should see that in this inclusion_sig data structure.

There is an important caveat though: in Theresa’s filter above, she did a DE of only the retina samples but I did all samples. I expected that this would result in basically the same result (I actually assumed I would get a few more genes), but instead it appears to have retrieved a significantly smaller number of genes (about 1/2, happily they pretty much all appear in the previous filter). As a result, I am going to try relaxing my constraints slightly to see if I can recapitulate her filter (which would match Theresa’s later filter, though I guess that in turn will lead to a smaller set of genes compared to her later, relaxed 0.1 filter).

comparison <- inclusion_sig[["deseq"]][["ups"]][["p08_het_retina"]]
## Error:
## ! object 'inclusion_sig' not found
comp <- list(
  "taa" = taa_keepers,
  "new" = rownames(comparison))
## Error:
## ! object 'taa_keepers' not found
test_comparison <- Vennerable::Venn(comp)
## Error:
## ! object 'comp' not found
Vennerable::plot(test_comparison)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'plot': object 'test_comparison' not found

I want to have a little function which, given a contrast of interest, will extract the gene sets which should be included/excluded given the above.

write_all_cp <- function(all_cp, prefix = "12", suffix = "") {
  all_written <- list()
  for (g in seq_len(length(all_cp))) {
    name <- names(all_cp)[g]
    datum <- all_cp[[name]]
    filename <- glue("{prefix}enrichment_excel_tx/{name}_cprofiler{suffix}-v{ver}.xlsx")
    written <- sm(write_cp_data(datum, excel = filename))
    all_written[[g]] <- written
  }
  return(all_written)
}
write_all_gp <- function(all_gp, prefix = "13", suffix = "") {
  all_written <- list()
  for (g in seq_len(length(all_gp))) {
    name <- names(all_gp)[g]
    datum <- all_gp[[name]]
    filename <- glue("{prefix}enrichment_excel_tx/{name}_gprofiler{suffix}-v{ver}.xlsx")
    written <- sm(write_gprofiler_data(datum, excel = filename))
    all_written[[g]] <- written
  }
  return(all_written)
}
write_all_en <- function(all_en, prefix = "14", suffix = "") {
  all_written <- list()
  for (e in seq_len(length(all_en))) {
    name <- names(all_en)[e]
    datum <- all_en[[name]]
    filename <- glue("{prefix}enrichment_excel_tx/{name}_enricher{suffix}-v{ver}.xlsx")
    written <- sm(write_enricher_data(datum, excel = filename))
    all_written[[e]] <- written
  }
  return(all_written)
}

21.3.2 Extract genes included for each set of contrasts

Now, using that function, pull out the gene IDs of genes we do not trust because they were too high in wt for every contrast we are likely to perform.

all_genes <- rownames(assay(tx_pairwise_input))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'x' in selecting a method for function 'assay': object 'tx_pairwise_input' not found
time_inclusions <- extract_inclusions(inclusion_sig, inclusion_tables, inclusions,
                                      time_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found
location_inclusions <- extract_inclusions(inclusion_sig, inclusion_tables, inclusions,
                                          location_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found
genotype_inclusions <- extract_inclusions(inclusion_sig, inclusion_tables, inclusions,
                                          genotype_keepers, all_genes)
## Error:
## ! object 'inclusion_sig' not found

21.4 Perform the DE analyses and exclude the target genes

21.4.1 Genotype

genotype_de <- all_pairwise(tx_pairwise_input, filter = TRUE, model_fstring = default_fstring,
                            keepers = genotype_keepers, model_svs = "svaseq")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
genotype_de
## Error:
## ! object 'genotype_de' not found

21.4.2 Location

location_de <- all_pairwise(tx_pairwise_input, filter = TRUE, model_fstring = default_fstring,
                            keepers = location_keepers, model_svs = "svaseq")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
location_de
## Error:
## ! object 'location_de' not found

21.4.3 Time

time_de <- all_pairwise(tx_pairwise_input, filter = TRUE, model_fstring = default_fstring,
                        keepers = time_keepers, model_svs = "svaseq")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
time_de
## Error:
## ! object 'time_de' not found

It is near here when the computer sometimes fails with no more tempfiles. In another window I am messing with tempfile() in R to try to understand where it is going off the rails…

21.5 Extract the relevant tables and include genes lower in wt

21.5.1 Genotype contrasts

I will start with the tables and no inclusions so I can check my work.

In this first block I will explain a little more thoroughly what is going on:

  1. Dump the full table of the contrasts I defined above comparing the 3 genotypes across time/location.
  2. Iterate over each of those contrasts and do the following:
    1. Extract the name of the contrast, ‘kh_p08_dlgn’ for example
    2. Yank out that specific entry from the keeper list and its name
    3. Yank out the corresponding set of genes to include from the inclusions data structure.
    4. Create a filename given the name in (a) above and the logFC cutoff chosen for the inclusions (I am assuming we may change this)
    5. Given (b), (c), and (d), extract the corresponding table from the differential expression analysis and include the appropriate genes.
    FIXME: my gprofiler function just assumes human and so if passed mmusculus will incorrectly attempt to connect to non-existant databases. Let us fix that now.
genotype_tables_full <- combine_de_tables(
  genotype_de, keepers = genotype_keepers, label_column = tx_label_column,
  fancy = TRUE,
  excel = glue("08full_contrasts_excel_tx/genotype_full_tables-v{ver}.xlsx"))
## Error:
## ! object 'genotype_de' not found
genotype_tables_full
## Error:
## ! object 'genotype_tables_full' not found
genotype_sig_full <- extract_significant_genes(
  genotype_tables_full, according_to = "deseq",
  excel = glue("08full_contrasts_excel_tx/genotype_full_sig-v{ver}.xlsx"))
## Error:
## ! object 'genotype_tables_full' not found
genotype_sig_full
## Error:
## ! object 'genotype_sig_full' not found

21.5.2 Search Enrichment of these sets

In this run, we will search the full set of genes, next we will only do the inclusions.

genotype_full_gp <- all_gprofiler(genotype_sig_full, species = "mmusculus",
                                  excel = "09full_contrasts_enrich_tx/genotype_full_gprofiler.xlsx")
## Error:
## ! object 'genotype_sig_full' not found
genotype_full_cp <- all_cprofiler(genotype_sig_full, genotype_tables_full,
                                  orgdb = "org.Mm.eg.db", go_level = go_level, organism = "mouse",
                                  orgdb_from = orgdb_from, max_groupsize = max_groupsize,
                                  excel = "09full_contrasts_enrich_tx/genotype_full_cprofiler.xlsx")
## Error:
## ! object 'genotype_sig_full' not found
genotype_full_upset <- upsetr_sig(genotype_sig_full)
## Error:
## ! object 'genotype_sig_full' not found
genotype_full_intersects <- write_upset_groups(genotype_full_upset,
                                               excel = "09full_contrasts_intersections_tx/genotype_full_gene_groups.xlsx")
## Error:
## ! object 'genotype_full_upset' not found

Now separate the various genotype tables and perform the inclusions of the genes with relatively low wt values.

21.5.2.1 Extract filtered genes and pass to gprofiler2/clusterprofiler

genotype_tables <- list()
genotype_sig <- list()
genotype_gp <- list()
genotype_cp <- list()
genotype_en <- list()
for (k in seq_along(genotype_keepers)) {
  name <- names(genotype_keepers)[k]
  message("Examining ", name)
  keeper <- genotype_keepers[name]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- genotype_inclusions[[include_df_name]]
  includes <- genotype_inclusions[[include_name]]
  summary(rownames(genotype_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
  include_filename <- glue("10genotype_contrasts_excel_tx/{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
  include_sig_filename <- glue("10genotype_contrasts_excel_tx/{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
  genotype_tables[[name]] <- combine_de_tables(
    genotype_de, extra_annot = include_df,
    keepers = keeper, label_column = tx_label_column,
    excel = include_filename, wanted_genes = includes)
  print(genotype_tables[[name]])
  genotype_sig[[name]] <- extract_significant_genes(
    genotype_tables[[name]], according_to = "deseq",
    excel = include_sig_filename)
  print(genotype_sig[[name]])
  num_rows <- nrow(genotype_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(genotype_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  if (num_rows >= 10) {
    message("Performing gprofiler/clusterProfiler.")
    genotype_gp[[name]] <- all_gprofiler(genotype_sig[[name]], species = "mmusculus")
    genotype_cp[[name]] <- all_cprofiler(
      genotype_sig[[name]], genotype_tables[[name]],
      orgdb = "org.Mm.eg.db", orgdb_from = orgdb_from,
      go_level = go_level, max_groupsize = max_groupsize, organism = "mouse")
    #if (!is.null(get0("m2_gsc"))) {
    #  genotype_en[[name]] <- all_enricher(genotype_sig[[name]], gsc = m2_gsc,
    #                                      orgdb = "org.Mm.eg.db", from = "ENSEMBL", to = "SYMBOL")
    #}
    gp_written <- write_all_gp(genotype_gp[[name]], prefix = "11")
    cp_written <- write_all_cp(genotype_cp[[name]], prefix = "11")
    #en_written <- write_all_en(genotype_en[[name]])
  } else {
    warning("There are less than 10 genes up and down in the ", name, " comparison.")
    message("There are less than 10 genes up and down in the ", name, " comparison.")
  }
}
## Examining kh_p08_dlgn
## Error:
## ! object 'genotype_inclusions' not found

Plot the results separately.

for (k in seq_along(genotype_keepers)) {
  name <- names(genotype_keepers)[k]
  message("Examining ", name)
  keeper <- genotype_keepers[name]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- genotype_inclusions[[include_df_name]]
  includes <- genotype_inclusions[[include_name]]
  summary(rownames(genotype_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
  num_rows <- nrow(genotype_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(genotype_sig[[name]][["deseq"]][["downs"]][[name]])
  nrow(genotype_sig[[name]][["deseq"]][["ups"]][[name]])
  nrow(genotype_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  ## #1 is up and #2 is down, avoiding typeos here.
  num_objects <- length(genotype_cp[[name]])
  if (num_objects == 0) {
    warning("Something went wrong in all_cprofiler.")
  } else {
    upp <- which(grepl(x = names(genotype_cp[[name]]), pattern = "_up$"))
    downp <- which(grepl(x = names(genotype_cp[[name]]), pattern = "_down$"))
    if (length(upp) > 0) {
      mf_sig <- genotype_cp[[name]][[upp]][["go_data"]][["MF_enrich"]]
      cc_sig <- genotype_cp[[name]][[upp]][["go_data"]][["CC_enrich"]]
      bp_sig <- genotype_cp[[name]][[upp]][["go_data"]][["BP_enrich"]]
      mf_plots_up <- plot_enrichresult(mf_sig)
      mf_tree_up_filename <- glue("12clusterProfiler_plots_tx/{name}_up_mf_sig_tree.pdf")
      pp(file = mf_tree_up_filename)
      try(print(mf_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_up_filename <- glue("12clusterProfiler_plots_tx/{name}_up_mf_sig_dot.pdf")
      pp(file = mf_bar_up_filename)
      try(print(mf_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_up <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_up_filename <- glue("12clusterProfiler_plots_tx/{name}_up_cc_sig_tree.pdf")
      pp(file = cc_tree_up_filename)
      try(print(cc_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_up_filename <- glue("12clusterProfiler_plots_tx/{name}_up_cc_sig_dot.pdf")
      pp(file = cc_bar_up_filename)
      try(print(cc_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_up_filename <- glue("12clusterProfiler_plots_tx/{name}_up_bp_sig_tree.pdf")
      pp(file = bp_tree_up_filename)
      try(print(bp_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_up_filename <- glue("12clusterProfiler_plots_tx/{name}_up_bp_sig_dot.pdf")
      pp(file = bp_bar_up_filename)
      try(print(bp_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
    if (length(downp) > 0) {
      mf_sig <- genotype_cp[[name]][[downp]][["go_data"]][["MF_enrich"]]
      cc_sig <- genotype_cp[[name]][[downp]][["go_data"]][["CC_enrich"]]
      bp_sig <- genotype_cp[[name]][[downp]][["go_data"]][["BP_enrich"]]
      mf_plots_down <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_down_filename <- glue("12clusterProfiler_plots_tx/{name}_down_mf_sig_tree.pdf")
      pp(file = mf_tree_down_filename)
      try(print(mf_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_down_filename <- glue("12clusterProfiler_plots_tx/{name}_down_mf_sig_dot.pdf")
      pp(file = mf_bar_down_filename)
      try(print(mf_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_down <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_down_filename <- glue("12clusterProfiler_plots_tx/{name}_down_cc_sig_tree.pdf")
      pp(file = cc_tree_down_filename)
      try(print(cc_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_down_filename <- glue("12clusterProfiler_plots_tx/{name}_down_cc_sig_dot.pdf")
      pp(file = cc_bar_down_filename)
      try(print(cc_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_down_filename <- glue("12clusterProfiler_plots_tx/{name}_down_bp_sig_tree.pdf")
      pp(file = bp_tree_down_filename)
      try(print(bp_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, cateogories = go_categories)
      bp_bar_down_filename <- glue("12clusterProfiler_plots_tx/{name}_down_bp_sig_dot.pdf")
      pp(file = bp_bar_down_filename)
      try(print(bp_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
  }
}
## Examining kh_p08_dlgn
## Error:
## ! object 'genotype_inclusions' not found

A few specific plots of interest: Colenso asked to label a few genes for the knockout/het p08_retinas, p08_scn, and p08_dlgn: either the top-15 or all significant. I am pretty sure if I tell it 15 and there are not that many, it will just do the significant? Let us find out!

21.5.2.2 ko/het for p08 retinas

table_name <- "kh_p08_retina"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting <- c("Opn4", "Gm9008", "Lrr1", "Cnbd1")
kh_p08_retina_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = colors[["het_retina"]], color_high = colors[["ko_retina"]],
  label_column = tx_label_column, label = interesting, alpha = 1.0,
  outline = outline, size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "13genotype_ma_volcano_tx/kh_p08_retina_volcano.pdf", width = 9, height = 9)
## Warning in pp(file = "13genotype_ma_volcano_tx/kh_p08_retina_volcano.pdf", :
## The directory: 13genotype_ma_volcano_tx does not exist, will attempt to create
## it.
kh_p08_retina_volcano[["plot"]]
## Error:
## ! object 'kh_p08_retina_volcano' not found
plotted <- dev.off()
kh_p08_retina_volcano[["plot"]]
## Error:
## ! object 'kh_p08_retina_volcano' not found
kh_p08_retina_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_retina"]], color_high = colors[["het_retina"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", outline = outline,
  label = interesting)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "13genotype_ma_volcano_tx/kh_p08_retina_ma.pdf", width = 9, height = 9)
kh_p08_retina_ma[["plot"]]
## Error:
## ! object 'kh_p08_retina_ma' not found
plotted <- dev.off()
kh_p08_retina_ma[["plot"]]
## Error:
## ! object 'kh_p08_retina_ma' not found

21.5.2.3 ko/het p08 SCN

I am going to make an executive decision for this plot, 15 is too many and makes it crazy cluttered.

21.5.3 Repeat this with two sets of genes

table_name <- "kh_p08_scn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting_genes <- c("Fign", "Nrn1", "Dpysl2", "Actb", "Fgf9", "Otx2", "Sec23",
                       "Ncam1", "Map4", "Sec22b", "Nlgn3", "Marcks", "Cd47",
                       "Dpysl3", "Lin7c", "Cadm1", "Snx12", "Rhoa", "Inpp5f",
                       "Atg12", "Set", "Gsk3b", "Pdcd4", "Gabra2", "Tmco1", "Anapc16")
kh_p08_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  label_column = "external_transcript_name", label = interesting_genes, size = 4, alpha = 1.0,
  outline = outline, color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]])
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "13genotype_ma_volcano_tx/kh_p08_scn_volcano.pdf", width = 9, height = 9)
kh_p08_scn_volcano[["plot"]]
## Error:
## ! object 'kh_p08_scn_volcano' not found
plotted <- dev.off()
kh_p08_scn_volcano[["plot"]]
## Error:
## ! object 'kh_p08_scn_volcano' not found
## why in the crap is it double-labelling!?
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p08_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
  outline = outline, p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "13genotype_ma_volcano_tx/kh_p08_scn_ma.pdf", width = 9, height = 9)
kh_p08_scn_ma[["plot"]]
## Error:
## ! object 'kh_p08_scn_ma' not found
plotted <- dev.off()
kh_p08_scn_ma[["plot"]]
## Error:
## ! object 'kh_p08_scn_ma' not found

21.5.3.1 Same plot but a different set of labeled genes

table_name <- "kh_p08_scn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting_genes <- c(
  "Anapc16", "Gabra2", "Tmco1", "Sod2", "Fgf9", "Pdcd4", "Rhoa", "Gsk3b", "Foxp1",
  "Ncam1", "Marcks", "Fign", "Dpysl3", "Inpp5f", "Cadm1", "Map4", "Ugcg", "Elovl4",
  "Elavl1", "Cfl2", "Tnnt1", "Gnb1", "Impact", "Nrn1", "Nlgn3", "Actb", "Cd47",
  "Sec22b", "Slc17a7", "Vglut1", "Actb", "B4galt5", "Foxp1", "Otx2", "Lin7c",
  "Snx12", "Atg12", "Set")
kh_p08_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
  outline = outline, label_column = "external_transcript_name", label = interesting_genes, size = 4, alpha = 1.0)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "13genotype_ma_volcano_tx/kh_p08_scn_volcano_v2.pdf", width = 9, height = 9)
kh_p08_scn_volcano[["plot"]]
## Error:
## ! object 'kh_p08_scn_volcano' not found
plotted <- dev.off()
kh_p08_scn_volcano[["plot"]]
## Error:
## ! object 'kh_p08_scn_volcano' not found
## why in the crap is it double-labelling!?
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p08_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
  outline = outline, p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "13genotype_ma_volcano_tx/kh_p08_scn_ma_v2.pdf", width = 9, height = 9)
kh_p08_scn_ma[["plot"]]
## Error:
## ! object 'kh_p08_scn_ma' not found
plotted <- dev.off()
kh_p08_scn_ma[["plot"]]
## Error:
## ! object 'kh_p08_scn_ma' not found

21.5.3.2 ko/het p08 dLGN

table_name <- "kh_p08_dlgn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
kh_p08_dlgn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["ko_dlgn"]], color_high = colors[["het_dlgn"]],
  outline = outline, label_column = "external_transcript_name", label = 10, size = 4, alpha = 1.0)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "13genotype_ma_volcano_tx/kh_p08_dlgn_volcano.pdf", width = 9, height = 9)
kh_p08_dlgn_volcano[["plot"]]
## Error:
## ! object 'kh_p08_dlgn_volcano' not found
plotted <- dev.off()
kh_p08_dlgn_volcano[["plot"]]
## Error:
## ! object 'kh_p08_dlgn_volcano' not found
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p08_dlgn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_dlgn"]], color_high = colors[["het_dlgn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = 10, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "13genotype_ma_volcano_tx/kh_p08_dlgn_ma.pdf", width = 9, height = 9)
kh_p08_dlgn_ma[["plot"]]
## Error:
## ! object 'kh_p08_dlgn_ma' not found
plotted <- dev.off()
kh_p08_dlgn_ma[["plot"]]
## Error:
## ! object 'kh_p08_dlgn_ma' not found

21.5.3.3 ko/het for p15 retinas

When last I ran this manually, it did not double-label, hopefully that remains true in the container.

table_name <- "kh_p15_retina"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting <- c("Opn4", "Gm9008", "Lrr1", "Cnbd1")
kh_p15_retina_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
  color_low = colors[["ko_retina"]], color_high = colors[["het_retina"]],
  label_column = "external_transcript_name", label = interesting, alpha = 1.0,
  outline = outline, size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "13genotype_ma_volcano_tx/kh_p15_retina_volcano.pdf", width = 9, height = 9)
kh_p15_retina_volcano[["plot"]]
## Error:
## ! object 'kh_p15_retina_volcano' not found
plotted <- dev.off()
kh_p15_retina_volcano[["plot"]]
## Error:
## ! object 'kh_p15_retina_volcano' not found
## why in the crap is it double-labelling!?
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p15_retina_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_retina"]], color_high = colors[["het_retina"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "13genotype_ma_volcano_tx/kh_p15_retina_ma.pdf", width = 9, height = 9)
kh_p15_retina_ma[["plot"]]
## Error:
## ! object 'kh_p15_retina_ma' not found
plotted <- dev.off()
kh_p15_retina_ma[["plot"]]
## Error:
## ! object 'kh_p15_retina_ma' not found

21.5.3.4 ko/het p15 SCN

21.5.4 Repeat this with two sets of genes

table_name <- "kh_p15_scn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting_genes <- c("Fign", "Nrn1", "Dpysl2", "Actb", "Fgf9", "Otx2", "Sec23",
                       "Ncam1", "Map4", "Sec22b", "Nlgn3", "Marcks", "Cd47",
                       "Dpysl3", "Lin7c", "Cadm1", "Snx12", "Rhoa", "Inpp5f",
                       "Atg12", "Set", "Gsk3b", "Pdcd4", "Gabra2", "Tmco1", "Anapc16")
kh_p15_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  label_column = "external_transcript_name", size = 4, alpha = 1.0,
  outline = outline, color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]])
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "13genotype_ma_volcano_tx/kh_p15_scn_volcano.pdf", width = 9, height = 9)
kh_p15_scn_volcano[["plot"]]
## Error:
## ! object 'kh_p15_scn_volcano' not found
plotted <- dev.off()
kh_p15_scn_volcano[["plot"]]
## Error:
## ! object 'kh_p15_scn_volcano' not found
kh_p15_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "13genotype_ma_volcano_tx/kh_p15_scn_ma.pdf", width = 9, height = 9)
kh_p15_scn_ma[["plot"]]
## Error:
## ! object 'kh_p15_scn_ma' not found
plotted <- dev.off()
kh_p15_scn_ma[["plot"]]
## Error:
## ! object 'kh_p15_scn_ma' not found

Round 2 with a separate gene set.

table_name <- "kh_p15_scn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting_genes <- c(
  "Anapc16", "Gabra2", "Tmco1", "Sod2", "Fgf9", "Pdcd4", "Rhoa", "Gsk3b", "Foxp1",
  "Ncam1", "Marcks", "Fign", "Dpysl3", "Inpp5f", "Cadm1", "Map4", "Ugcg", "Elovl4",
  "Elavl1", "Cfl2", "Tnnt1", "Gnb1", "Impact", "Nrn1", "Nlgn3", "Actb", "Cd47",
  "Sec22b", "Slc17a7", "Vglut1", "Actb", "B4galt5", "Foxp1", "Otx2", "Lin7c",
  "Snx12", "Atg12", "Set")
kh_p15_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
  outline = outline, label_column = "external_transcript_name", label = interesting_genes, size = 4, alpha = 1.0)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "13genotype_ma_volcano_tx/kh_p15_scn_volcano_v2.pdf", width = 9, height = 9)
kh_p15_scn_volcano[["plot"]]
## Error:
## ! object 'kh_p15_scn_volcano' not found
plotted <- dev.off()
kh_p15_scn_volcano[["plot"]]
## Error:
## ! object 'kh_p15_scn_volcano' not found
kh_p15_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "13genotype_ma_volcano_tx/kh_p15_scn_ma_v2.pdf", width = 9, height = 9)
kh_p15_scn_ma[["plot"]]
## Error:
## ! object 'kh_p15_scn_ma' not found
plotted <- dev.off()
kh_p15_scn_ma[["plot"]]
## Error:
## ! object 'kh_p15_scn_ma' not found

21.5.4.1 ko/het p15 dLGN

table_name <- "kh_p15_dlgn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
kh_p15_dlgn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["ko_dlgn"]], color_high = colors[["het_dlgn"]],
  outline = outline, label_column = "external_transcript_name", label = 10, size = 4, alpha = 1.0)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "13genotype_ma_volcano_tx/kh_p15_dlgn_volcano.pdf", width = 9, height = 9)
kh_p15_dlgn_volcano[["plot"]]
## Error:
## ! object 'kh_p15_dlgn_volcano' not found
plotted <- dev.off()
kh_p15_dlgn_volcano[["plot"]]
## Error:
## ! object 'kh_p15_dlgn_volcano' not found
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p15_dlgn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_dlgn"]], color_high = colors[["het_dlgn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = 10, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "13genotype_ma_volcano_tx/kh_p15_dlgn_ma.pdf", width = 9, height = 9)
kh_p15_dlgn_ma[["plot"]]
## Error:
## ! object 'kh_p15_dlgn_ma' not found
plotted <- dev.off()
kh_p15_dlgn_ma[["plot"]]
## Error:
## ! object 'kh_p15_dlgn_ma' not found

A query from Rashmi:

“I was discussing with Dr. Speer about the dLGN data and we found mostly retinal genes in dLGN Het/KO or time point comparison. Please check if those are not retina samples.”

I checked the samples etc and everything looks ok to me; perhaps I can use the results to look at this question in another way:

I will therefore load the p08_het_dlgn/p08_ko_dlgn table and compare it to the p08_het_retina/p08_ko_retina table directly. I think that if these turn out to be identical, then the hypothesis suggested by this query is correct.

Note, in order to do this, I must use the full tables, not the post-inclusion tables because I cannot guarantee that they will have identical gene IDs.

retina_table <- genotype_tables_full[["data"]][["kh_p08_retina"]]
## Error:
## ! object 'genotype_tables_full' not found
dlgn_table <- genotype_tables_full[["data"]][["kh_p08_dlgn"]]
## Error:
## ! object 'genotype_tables_full' not found
retina_subset <- retina_table[, c("ensembl_gene_id", "deseq_logfc")]
## Error:
## ! object 'retina_table' not found
colnames(retina_subset) <- c("ID", "retina_logfc")
## Error:
## ! object 'retina_subset' not found
dlgn_subset <- dlgn_table[, c("ensembl_gene_id", "deseq_logfc")]
## Error:
## ! object 'dlgn_table' not found
colnames(dlgn_subset) <- c("ID", "dlgn_logfc")
## Error:
## ! object 'dlgn_subset' not found
merged <- merge(retina_subset, dlgn_subset, by = "ID")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'merge': object 'retina_subset' not found
rownames(merged) <- make.names(merged[["ID"]], unique = TRUE)
## Error:
## ! object 'merged' not found
merged[["ID"]] <- NULL
## Error:
## ! object 'merged' not found
plotted <- plot_linear_scatter(merged)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'merged' not found
pp(file = "images_tx/kh_p08_retina_vs_dlgn_deseq_logfc_values.png")
## Warning in pp(file = "images_tx/kh_p08_retina_vs_dlgn_deseq_logfc_values.png"):
## The directory: images_tx does not exist, will attempt to create it.
plotted[["scatter"]]
## Error in `plotted[["scatter"]]`:
## ! subscript out of bounds
dev.off()
## png 
##   2
plotted[["scatter"]]
## Error in `plotted[["scatter"]]`:
## ! subscript out of bounds

Rashmi asked if we could also do the p15 for this comparison:

retina_table <- genotype_tables_full[["data"]][["kh_p15_retina"]]
## Error:
## ! object 'genotype_tables_full' not found
dlgn_table <- genotype_tables_full[["data"]][["kh_p15_dlgn"]]
## Error:
## ! object 'genotype_tables_full' not found
retina_subset <- retina_table[, c("ensembl_gene_id", "deseq_logfc")]
## Error:
## ! object 'retina_table' not found
colnames(retina_subset) <- c("ID", "retina_logfc")
## Error:
## ! object 'retina_subset' not found
dlgn_subset <- dlgn_table[, c("ensembl_gene_id", "deseq_logfc")]
## Error:
## ! object 'dlgn_table' not found
colnames(dlgn_subset) <- c("ID", "dlgn_logfc")
## Error:
## ! object 'dlgn_subset' not found
merged <- merge(retina_subset, dlgn_subset, by = "ID")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'merge': object 'retina_subset' not found
rownames(merged) <- make.names(merged[["ID"]], unique = TRUE)
## Error:
## ! object 'merged' not found
merged[["ID"]] <- NULL
## Error:
## ! object 'merged' not found
plotted <- plot_linear_scatter(merged)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'merged' not found
pp(file = "images_tx/kh_p15_retina_vs_dlgn_deseq_logfc_values.png")
plotted[["scatter"]]
## Error in `plotted[["scatter"]]`:
## ! subscript out of bounds
dev.off()
## png 
##   2
plotted[["scatter"]]
## Error in `plotted[["scatter"]]`:
## ! subscript out of bounds

21.5.4.2 Repeat with the strict filter

genotype_strict_tables <- list()
genotype_strict_sig <- list()
genotype_strict_gp <- list()
genotype_strict_cp <- list()
genotype_strict_en <- list()
for (k in seq_along(genotype_keepers)) {
  name <- names(genotype_keepers)[k]
  message("Examining ", name)
  keeper <- genotype_keepers[name]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_strict_df <- genotype_inclusions_strict[[include_df_name]]
  includes_strict <- genotype_inclusions_strict[[include_name]]
  summary(rownames(genotype_sig_full[["deseq"]][["ups"]][[name]]) %in% includes_strict)
  include_filename <- glue("14genotype_strict_contrasts_excel_tx/{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
  include_sig_filename <- glue("14genotype_strict_contrasts_excel_tx/{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
  genotype_strict_tables[[name]] <- combine_de_tables(
    genotype_de, extra_annot = include_strict_df,
    keepers = keeper, label_column = tx_label_column,
    excel = include_filename, wanted_genes = includes_strict)
  print(genotype_strict_tables[[name]])
  genotype_strict_sig[[name]] <- extract_significant_genes(
    genotype_strict_tables[[name]], according_to = "deseq",
    excel = include_sig_filename)
  print(genotype_strict_sig[[name]])
  num_rows <- nrow(genotype_strict_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(genotype_strict_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  if (num_rows >= 10) {
    message("Performing gprofiler/clusterProfiler.")
    genotype_strict_gp[[name]] <- all_gprofiler(genotype_strict_sig[[name]], species = "mmusculus")
    genotype_strict_cp[[name]] <- all_cprofiler(
      genotype_strict_sig[[name]], genotype_strict_tables[[name]],
      orgdb = "org.Mm.eg.db", go_level = go_level,
      orgdb_from = orgdb_from, max_groupsize = max_groupsize, organism = "mouse")
    #if (!is.null(get0("m2_gsc"))) {
    #  genotype_strict_en[[name]] <- all_enricher(genotype_strict_sig[[name]], gsc = m2_gsc,
    #                                      orgdb = "org.Mm.eg.db", from = "ENSEMBL", to = "SYMBOL")
    #}
    gp_written <- write_all_gp(genotype_strict_gp[[name]], prefix = "15", suffix = "strict")
    cp_written <- write_all_cp(genotype_strict_cp[[name]], prefix = "15", suffix = "strict")
    #en_written <- write_all_en(genotype_strict_en[[name]])
  } else {
    warning("There are less than 10 genes up and down in the ", name, " comparison.")
    message("There are less than 10 genes up and down in the ", name, " comparison.")
  }
}
## Examining kh_p08_dlgn
## Error:
## ! object 'genotype_inclusions_strict' not found

22 Genotype Strict plots

for (k in seq_along(genotype_keepers)) {
  name <- names(genotype_keepers)[k]
  message("Examining ", name)
  keeper <- genotype_keepers[name]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_strict_df <- genotype_inclusions_strict[[include_df_name]]
  includes_strict <- genotype_inclusions_strict[[include_name]]
  summary(rownames(genotype_sig_full[["deseq"]][["ups"]][[name]]) %in% includes_strict)
  num_rows <- nrow(genotype_strict_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(genotype_strict_sig[[name]][["deseq"]][["downs"]][[name]])
  nrow(genotype_strict_sig[[name]][["deseq"]][["ups"]][[name]])
  nrow(genotype_strict_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  ## #1 is up and #2 is down, avoiding typeos here.
  num_objects <- length(genotype_strict_cp[[name]])
  if (num_objects == 0) {
    warning("Something went wrong in all_cprofiler.")
  } else {
    upp <- which(grepl(x = names(genotype_strict_cp[[name]]), pattern = "_up$"))
    downp <- which(grepl(x = names(genotype_strict_cp[[name]]), pattern = "_down$"))
    if (length(upp) > 0) {
      mf_sig <- genotype_strict_cp[[name]][[upp]][["go_data"]][["MF_enrich"]]
      cc_sig <- genotype_strict_cp[[name]][[upp]][["go_data"]][["CC_enrich"]]
      bp_sig <- genotype_strict_cp[[name]][[upp]][["go_data"]][["BP_enrich"]]
      mf_plots_up <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_up_filename <- glue("16clusterProfiler_plots_tx/{name}_up_mf_sig_tree.pdf")
      pp(file = mf_tree_up_filename)
      try(print(mf_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_up_filename <- glue("16clusterProfiler_plots_tx/{name}_up_mf_sig_dot.pdf")
      pp(file = mf_bar_up_filename)
      try(print(mf_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_up <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_up_filename <- glue("16clusterProfiler_plots_tx/{name}_up_cc_sig_tree.pdf")
      pp(file = cc_tree_up_filename)
      try(print(cc_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_up_filename <- glue("16clusterProfiler_plots_tx/{name}_up_cc_sig_dot.pdf")
      pp(file = cc_bar_up_filename)
      try(print(cc_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_up_filename <- glue("16clusterProfiler_plots_tx/{name}_up_bp_sig_tree.pdf")
      pp(file = bp_tree_up_filename)
      try(print(bp_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_up_filename <- glue("16clusterProfiler_plots_tx/{name}_up_bp_sig_dot.pdf")
      pp(file = bp_bar_up_filename)
      try(print(bp_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
    if (length(downp) > 0) {
      mf_sig <- genotype_strict_cp[[name]][[downp]][["go_data"]][["MF_enrich"]]
      cc_sig <- genotype_strict_cp[[name]][[downp]][["go_data"]][["CC_enrich"]]
      bp_sig <- genotype_strict_cp[[name]][[downp]][["go_data"]][["BP_enrich"]]
      mf_plots_down <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_down_filename <- glue("16clusterProfiler_plots_tx/{name}_down_mf_sig_tree.pdf")
      pp(file = mf_tree_down_filename)
      try(print(mf_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_down_filename <- glue("16clusterProfiler_plots_tx/{name}_down_mf_sig_dot.pdf")
      pp(file = mf_bar_down_filename)
      try(print(mf_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_down <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_down_filename <- glue("16clusterProfiler_plots_tx/{name}_down_cc_sig_tree.pdf")
      pp(file = cc_tree_down_filename)
      try(print(cc_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_down_filename <- glue("16clusterProfiler_plots_tx/{name}_down_cc_sig_dot.pdf")
      pp(file = cc_bar_down_filename)
      try(print(cc_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_down_filename <- glue("16clusterProfiler_plots_tx/{name}_down_bp_sig_tree.pdf")
      pp(file = bp_tree_down_filename)
      try(print(bp_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, cateogories = go_categories)
      bp_bar_down_filename <- glue("16clusterProfiler_plots_tx/{name}_down_bp_sig_dot.pdf")
      pp(file = bp_bar_down_filename)
      try(print(bp_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
  }
}
## Examining kh_p08_dlgn
## Error:
## ! object 'genotype_inclusions_strict' not found

Given this stricter filter, I think no genes pass in the genome comparisons.

22.0.1 Location contrasts with genes removed/kept

We will now repeat the above tasks seeking location differences instead of genotype; essentially I copy/pasted the above with s/genotype/location/g.

location_tables_full <- combine_de_tables(
  location_de, keepers = location_keepers, label_column = tx_label_column,
  excel = glue("17full_location_contrasts_tx/location_full_tables-v{ver}.xlsx"))
## Error:
## ! object 'location_de' not found
location_tables_full
## Error:
## ! object 'location_tables_full' not found
location_sig_full <- extract_significant_genes(
  location_tables_full, according_to = "deseq",
  excel = glue("17full_location_contrasts_tx/location_full_sig-v{ver}.xlsx"))
## Error:
## ! object 'location_tables_full' not found
location_sig_full
## Error:
## ! object 'location_sig_full' not found
location_full_upset <- upsetr_sig(location_sig_full)
## Error:
## ! object 'location_sig_full' not found
location_full_intersects <- write_upset_groups(
  location_full_upset,
  excel = "excel/17full_location_contrasts_tx/location_full_gene_groups.xlsx")
## Error:
## ! object 'location_full_upset' not found
location_tables <- list()
location_sig <- list()
location_gp <- list()
location_cp <- list()
for (k in seq_along(location_keepers)) {
  name <- names(location_keepers)[k]
  message("Examining ", name)
  keeper <- location_keepers[name]
  includes <- location_inclusions[[name]]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- location_inclusions[[include_df_name]]
  includes <- location_inclusions[[include_name]]
  summary(rownames(location_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
  include_filename <- glue("18location_contrasts_tx/{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
  include_sig_filename <- glue("18location_contrasts_tx/{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
  location_tables[[name]] <- combine_de_tables(
    location_de, extra_annot = include_df,
    keepers = keeper, label_column = tx_label_column,
    excel = include_filename, wanted_genes = includes)
  print(location_tables[[name]])
  location_sig[[name]] <- extract_significant_genes(
    location_tables[[name]], according_to = "deseq",
    excel = include_sig_filename)
  print(location_sig[[name]])
  num_rows <- nrow(location_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(location_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  if (num_rows > 10) {
    location_gp[[name]] <- all_gprofiler(location_sig[[name]], species = "mmusculus")
    location_cp[[name]] <- all_cprofiler(
      location_sig[[name]], location_tables[[name]],
      orgdb = "org.Mm.eg.db", go_level = go_level, orgdb_from = orgdb_from,
      max_groupsize = max_groupsize, organism = "mouse")
    cp_written <- write_all_cp(location_cp[[name]], prefix = "19")
    gp_written <- write_all_gp(location_gp[[name]], prefix = "19")
  }
}
## Examining dr_p08_het
## Error:
## ! object 'location_inclusions' not found

Print out all the plots in a separate block.

for (k in seq_along(location_keepers)) {
  name <- names(location_keepers)[k]
  message("Examining ", name)
  keeper <- location_keepers[name]
  includes <- location_inclusions[[name]]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- location_inclusions[[include_df_name]]
  includes <- location_inclusions[[include_name]]
  summary(rownames(location_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
  num_rows <- nrow(location_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(location_sig[[name]][["deseq"]][["downs"]][[name]])
  nrow(location_sig[[name]][["deseq"]][["ups"]][[name]])
  nrow(location_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  num_objects <- length(location_cp[[name]])
  if (num_objects == 0) {
    warning("Something went wrong in all_cprofiler.")
  } else {
    upp <- which(grepl(x = names(location_cp[[name]]), pattern = "_up$"))
    downp <- which(grepl(x = names(location_cp[[name]]), pattern = "_down$"))
    if (length(upp) > 0) {
      mf_sig <- location_cp[[name]][[upp]][["go_data"]][["MF_enrich"]]
      cc_sig <- location_cp[[name]][[upp]][["go_data"]][["CC_enrich"]]
      bp_sig <- location_cp[[name]][[upp]][["go_data"]][["BP_enrich"]]
      mf_plots_up <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_up_filename <- glue("19clusterProfiler_plots_tx/{name}_up_mf_sig_tree.pdf")
      pp(file = mf_tree_up_filename)
      try(print(mf_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_up_filename <- glue("19clusterProfiler_plots_tx/{name}_up_mf_sig_dot.pdf")
      pp(file = mf_bar_up_filename)
      try(print(mf_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_up <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_up_filename <- glue("19clusterProfiler_plots_tx/{name}_up_cc_sig_tree.pdf")
      pp(file = cc_tree_up_filename)
      try(print(cc_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_up_filename <- glue("19clusterProfiler_plots_tx/{name}_up_cc_sig_dot.pdf")
      pp(file = cc_bar_up_filename)
      try(print(cc_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_up_filename <- glue("19clusterProfiler_plots_tx/{name}_up_bp_sig_tree.pdf")
      pp(file = bp_tree_up_filename)
      try(print(bp_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_up_filename <- glue("19clusterProfiler_plots_tx/{name}_up_bp_sig_dot.pdf")
      pp(file = bp_bar_up_filename)
      try(print(bp_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
    if (length(downp) > 0) {
      mf_sig <- location_cp[[name]][[downp]][["go_data"]][["MF_enrich"]]
      cc_sig <- location_cp[[name]][[downp]][["go_data"]][["CC_enrich"]]
      bp_sig <- location_cp[[name]][[downp]][["go_data"]][["BP_enrich"]]
      mf_plots_down <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_down_filename <- glue("19clusterProfiler_plots_tx/{name}_down_mf_sig_tree.pdf")
      pp(file = mf_tree_down_filename)
      try(print(mf_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_down_filename <- glue("19clusterProfiler_plots_tx/{name}_down_mf_sig_dot.pdf")
      pp(file = mf_bar_down_filename)
      try(print(mf_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_down <- plot_enrichresult(cc_sig, showCategory = 12)
      cc_tree_down_filename <- glue("19clusterProfiler_plots_tx/{name}_down_cc_sig_tree.pdf")
      pp(file = cc_tree_down_filename)
      try(print(cc_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_down_filename <- glue("19clusterProfiler_plots_tx/{name}_down_cc_sig_dot.pdf")
      pp(file = cc_bar_down_filename)
      try(print(cc_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_down_filename <- glue("19clusterProfiler_plots_tx/{name}_down_bp_sig_tree.pdf")
      pp(file = bp_tree_down_filename)
      try(print(bp_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_down_filename <- glue("19clusterProfiler_plots_tx/{name}_down_bp_sig_dot.pdf")
      pp(file = bp_bar_down_filename)
      try(print(bp_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
  }
}
## Examining dr_p08_het
## Error:
## ! object 'location_inclusions' not found

Colenso sent a specific query of interest, comparing SCN vs. Retinas at p08 in the heterozygotes including a set of genes of particular interest. Perhaps I can use some of these as markers to quality control my work in the future?

Here are the genes:

Opn4, Eomes, Trpc7, Oprm1, Nr4a3, Tbx20, Irx6, AW551984, Pcdh19, Adcyap1, Baiap3, Chl1, Grin3a, Igf1, Gria1, Grin2d, Grin3a, Chrna6, Chrna3, Htr5a, Htr2a, Htr7, Irx4, PlxnC1, Sema6d, Sema4f, Sema4a, Sema6b, Lrrc4b, Lrrc58, Lrrc3b, Wnt4, Wnt9b, Ctxn3, Tenm1, Gna14, Rgs4, Rgs6, Rgs5

table_input <- location_tables[["sr_p08_het"]]
table_name <- "sr_p08_het"
table <- table_input[["data"]][[table_name]]
interesting_genes <- c("Opn4", "Eomes", "Trpc7", "Oprm1", "Nr4a3", "Tbx20",
                       "Irx6", "AW551984", "Pcdh19", "Adcyap1r1", "Baiap3",
                       "Chl1", "Grin3a", "Igf1", "Gria1", "Grin2d", "Grin3a",
                       "Chrna6", "Chrna3", "Htr5a", "Htr2a", "Htr7", "Irx4",
                       "PlxnC1", "Sema6d", "Sema4f", "Sema4a", "Sema6b", "Lrrc4b",
                       "Lrrc58", "Lrrc3b", "Wnt4", "Wnt9b", "Ctxn3", "Tenm1", "Gna14",
                       "Rgs4", "Rgs6", "Rgs5", "Pou4f2", "Chrnb3", "Bcan")
sr_p08_het_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["het_retina"]], color_high = colors[["het_scn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "20location_ma_volcano_tx/sr_p08_het_volcano.pdf", width = 9, height = 9)
## Warning in pp(file = "20location_ma_volcano_tx/sr_p08_het_volcano.pdf", : The
## directory: 20location_ma_volcano_tx does not exist, will attempt to create it.
sr_p08_het_volcano[["plot"]]
## Error:
## ! object 'sr_p08_het_volcano' not found
plotted <- dev.off()
sr_p08_het_volcano[["plot"]]
## Error:
## ! object 'sr_p08_het_volcano' not found
sr_p08_het_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["het_retina"]], color_high = colors[["het_scn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "20location_ma_volcano_tx/sr_p08_het_ma.pdf", width = 9, height = 9)
sr_p08_het_ma[["plot"]]
## Error:
## ! object 'sr_p08_het_ma' not found
plotted <- dev.off()
sr_p08_het_ma[["plot"]]
## Error:
## ! object 'sr_p08_het_ma' not found

22.0.2 Also the ko

table_input <- location_tables[["sr_p08_ko"]]
table_name <- "sr_p08_ko"
table <- table_input[["data"]][[table_name]]
sr_p08_ko_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["ko_retina"]], color_high = colors[["ko_scn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "20location_ma_volcano_tx/sr_p08_ko_volcano.pdf", width = 9, height = 9)
sr_p08_ko_volcano[["plot"]]
## Error:
## ! object 'sr_p08_ko_volcano' not found
plotted <- dev.off()
sr_p08_ko_volcano[["plot"]]
## Error:
## ! object 'sr_p08_ko_volcano' not found
sr_p08_ko_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_retina"]], color_high = colors[["ko_scn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "20location_ma_volcano_tx/sr_p08_ko_ma.pdf", width = 9, height = 9)
sr_p08_ko_ma[["plot"]]
## Error:
## ! object 'sr_p08_ko_ma' not found
plotted <- dev.off()
sr_p08_ko_ma[["plot"]]
## Error:
## ! object 'sr_p08_ko_ma' not found
table_input <- location_tables[["sr_p15_het"]]
table_name <- "sr_p15_het"
table <- table_input[["data"]][[table_name]]
interesting_genes <- c("Opn4", "Eomes", "Trpc7", "Oprm1", "Nr4a3", "Tbx20",
                       "Irx6", "AW551984", "Pcdh19", "Adcyap1r1", "Baiap3",
                       "Chl1", "Grin3a", "Igf1", "Gria1", "Grin2d", "Grin3a",
                       "Chrna6", "Chrna3", "Htr5a", "Htr2a", "Htr7", "Irx4",
                       "PlxnC1", "Sema6d", "Sema4f", "Sema4a", "Sema6b", "Lrrc4b",
                       "Lrrc58", "Lrrc3b", "Wnt4", "Wnt9b", "Ctxn3", "Tenm1", "Gna14",
                       "Rgs4", "Rgs6", "Rgs5", "Pou4f2", "Chrnb3", "Bcan")
sr_p15_het_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["het_retina"]], color_high = colors[["het_scn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "20location_ma_volcano_tx/sr_p15_het_volcano.pdf", width = 9, height = 9)
sr_p15_het_volcano[["plot"]]
## Error:
## ! object 'sr_p15_het_volcano' not found
plotted <- dev.off()
sr_p15_het_volcano[["plot"]]
## Error:
## ! object 'sr_p15_het_volcano' not found
sr_p15_het_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["het_retina"]], color_high = colors[["het_scn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "20location_ma_volcano_tx/sr_p15_het_ma.pdf", width = 9, height = 9)
sr_p15_het_ma[["plot"]]
## Error:
## ! object 'sr_p15_het_ma' not found
plotted <- dev.off()
sr_p15_het_ma[["plot"]]
## Error:
## ! object 'sr_p15_het_ma' not found

22.0.3 Also the ko

table_input <- location_tables[["sr_p15_ko"]]
table_name <- "sr_p15_ko"
table <- table_input[["data"]][[table_name]]
sr_p15_ko_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["ko_retina"]], color_high = colors[["ko_scn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4, min.segment.length = 0, point.padding = 0.2)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "20location_ma_volcano_tx/sr_p15_ko_volcano.pdf", width = 12, height = 12)
sr_p15_ko_volcano[["plot"]]
## Error:
## ! object 'sr_p15_ko_volcano' not found
plotted <- dev.off()
sr_p15_ko_volcano[["plot"]]
## Error:
## ! object 'sr_p15_ko_volcano' not found
sr_p15_ko_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_retina"]], color_high = colors[["ko_scn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "20location_ma_volcano_tx/sr_p15_ko_ma.pdf", width = 9, height = 9)
sr_p15_ko_ma[["plot"]]
## Error:
## ! object 'sr_p15_ko_ma' not found
plotted <- dev.off()
sr_p15_ko_ma[["plot"]]
## Error:
## ! object 'sr_p15_ko_ma' not found

22.0.3.1 Test a specific location query for duplicated IDs

Let us see if any Ensembl gene IDs and/or MGI IDs are shared in the worksheet location_sr_p08_ko_including_wt_0.1_decreased_sig up/down.

test_table_up <- location_sig[["sr_p08_ko"]][["deseq"]][["ups"]][[1]]
test_table_down <- location_sig[["sr_p08_ko"]][["deseq"]][["downs"]][[1]]

query <- list("up" = rownames(test_table_up),
              "down" = rownames(test_table_down))
query_upset <- UpSetR::fromList(query)
UpSetR::upset(query_upset)
## Error in `start_col:end_col`:
## ! argument of length 0
query <- list("up" = test_table_up[["external_transcript_name"]],
              "down" = test_table_down[["external_transcript_name"]])
query_upset <- UpSetR::fromList(query)
UpSetR::upset(query_upset)
## Error in `start_col:end_col`:
## ! argument of length 0
## ok, good.

22.0.3.2 Repeat with the strict filter

location_strict_tables <- list()
location_strict_sig <- list()
location_strict_gp <- list()
location_strict_cp <- list()
for (k in seq_along(location_keepers)) {
  name <- names(location_keepers)[k]
  message("Examining ", name)
  keeper <- location_keepers[name]
  includes <- location_inclusions_strict[[name]]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- location_inclusions_strict[[include_df_name]]
  includes <- location_inclusions_strict[[include_name]]
  found_includes <- rownames(location_sig_full[["deseq"]][["ups"]][[name]]) %in% includes
  summary(found_includes)
  if (sum(found_includes) == 0) {
    next
  }
  include_filename <- glue("21location_strict_contrasts_tx/{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
  include_sig_filename <- glue("21location_strict_contrasts_tx/{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
  location_strict_tables[[name]] <- combine_de_tables(
    location_de, extra_annot = include_df,
    keepers = keeper, label_column = tx_label_column,
    excel = include_filename, wanted_genes = includes)
  print(location_strict_tables[[name]])
  location_strict_sig[[name]] <- extract_significant_genes(
    location_strict_tables[[name]], according_to = "deseq",
    excel = include_sig_filename)
  print(location_strict_sig[[name]])
  num_rows <- nrow(location_strict_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(location_strict_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  if (num_rows > 10) {
    location_strict_gp[[name]] <- all_gprofiler(location_strict_sig[[name]], species = "mmusculus")
    location_strict_cp[[name]] <- all_cprofiler(
      location_strict_sig[[name]], location_strict_tables[[name]],
      orgdb = "org.Mm.eg.db", go_level = go_level, orgdb_from = orgdb_from,
      max_groupsize = max_groupsize, organism = "mouse")
    cp_written <- write_all_cp(location_strict_cp[[name]], prefix = "22", suffix = "strict")
    gp_written <- write_all_gp(location_strict_gp[[name]], prefix = "22", suffix = "strict")
  }
}
## Examining dr_p08_het
## Error:
## ! object 'location_inclusions_strict' not found

Print out all the plots in a separate block.

for (k in seq_along(location_keepers)) {
  name <- names(location_keepers)[k]
  message("Examining ", name)
  keeper <- location_keepers[name]
  includes <- location_inclusions_strict[[name]]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- location_inclusions_strict[[include_df_name]]
  includes <- location_inclusions_strict[[include_name]]
  summary(rownames(location_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
  num_rows <- nrow(location_strict_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(location_strict_sig[[name]][["deseq"]][["downs"]][[name]])
  nrow(location_strict_sig[[name]][["deseq"]][["ups"]][[name]])
  nrow(location_strict_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  num_objects <- length(location_strict_cp[[name]])
  if (num_objects == 0) {
    warning("Something went wrong in all_cprofiler.")
  } else {
    upp <- which(grepl(x = names(location_strict_cp[[name]]), pattern = "_up$"))
    downp <- which(grepl(x = names(location_strict_cp[[name]]), pattern = "_down$"))
    if (length(upp) > 0) {
      mf_sig <- location_strict_cp[[name]][[upp]][["go_data"]][["MF_enrich"]]
      cc_sig <- location_strict_cp[[name]][[upp]][["go_data"]][["CC_enrich"]]
      bp_sig <- location_strict_cp[[name]][[upp]][["go_data"]][["BP_enrich"]]
      mf_plots_up <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_up_filename <- glue("23clusterProfiler_plots_tx/{name}_up_mf_sig_tree.pdf")
      pp(file = mf_tree_up_filename)
      try(print(mf_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_up_filename <- glue("23clusterProfiler_plots_tx/{name}_up_mf_sig_dot.pdf")
      pp(file = mf_bar_up_filename)
      try(print(mf_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_up <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_up_filename <- glue("23clusterProfiler_plots_tx/{name}_up_cc_sig_tree.pdf")
      pp(file = cc_tree_up_filename)
      try(print(cc_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_up_filename <- glue("23clusterProfiler_plots_tx/{name}_up_cc_sig_dot.pdf")
      pp(file = cc_bar_up_filename)
      try(print(cc_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_up_filename <- glue("23clusterProfiler_plots_tx/{name}_up_bp_sig_tree.pdf")
      pp(file = bp_tree_up_filename)
      try(print(bp_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_up_filename <- glue("23clusterProfiler_plots_tx/{name}_up_bp_sig_dot.pdf")
      pp(file = bp_bar_up_filename)
      try(print(bp_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
    if (length(downp) > 0) {
      mf_sig <- location_strict_cp[[name]][[downp]][["go_data"]][["MF_enrich"]]
      cc_sig <- location_strict_cp[[name]][[downp]][["go_data"]][["CC_enrich"]]
      bp_sig <- location_strict_cp[[name]][[downp]][["go_data"]][["BP_enrich"]]
      mf_plots_down <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_down_filename <- glue("23clusterProfiler_plots_tx/{name}_down_mf_sig_tree.pdf")
      pp(file = mf_tree_down_filename)
      try(print(mf_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_down_filename <- glue("23clusterProfiler_plots_tx/{name}_down_mf_sig_dot.pdf")
      pp(file = mf_bar_down_filename)
      try(print(mf_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_down <- plot_enrichresult(cc_sig, showCategory = 12)
      cc_tree_down_filename <- glue("23clusterProfiler_plots_tx/{name}_down_cc_sig_tree.pdf")
      pp(file = cc_tree_down_filename)
      try(print(cc_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_down_filename <- glue("23clusterProfiler_plots_tx/{name}_down_cc_sig_dot.pdf")
      pp(file = cc_bar_down_filename)
      try(print(cc_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_down_filename <- glue("23clusterProfiler_plots_tx/{name}_down_bp_sig_tree.pdf")
      pp(file = bp_tree_down_filename)
      try(print(bp_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_down_filename <- glue("23clusterProfiler_plots_tx/{name}_down_bp_sig_dot.pdf")
      pp(file = bp_bar_down_filename)
      try(print(bp_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
  }
}
## Examining dr_p08_het
## Error:
## ! object 'location_inclusions_strict' not found

Colenso sent a specific query of interest, comparing SCN vs. Retinas at p08 in the heterozygotes including a set of genes of particular interest. Perhaps I can use some of these as markers to quality control my work in the future?

Here are the genes:

Opn4, Eomes, Trpc7, Oprm1, Nr4a3, Tbx20, Irx6, AW551984, Pcdh19, Adcyap1, Baiap3, Chl1, Grin3a, Igf1, Gria1, Grin2d, Grin3a, Chrna6, Chrna3, Htr5a, Htr2a, Htr7, Irx4, PlxnC1, Sema6d, Sema4f, Sema4a, Sema6b, Lrrc4b, Lrrc58, Lrrc3b, Wnt4, Wnt9b, Ctxn3, Tenm1, Gna14, Rgs4, Rgs6, Rgs5

table_input <- location_strict_tables[["sr_p08_het"]]
table_name <- "sr_p08_het"
table <- table_input[["data"]][[table_name]]
interesting_genes <- c("Opn4", "Eomes", "Trpc7", "Oprm1", "Nr4a3", "Tbx20",
                       "Irx6", "AW551984", "Pcdh19", "Adcyap1r1", "Baiap3",
                       "Chl1", "Grin3a", "Igf1", "Gria1", "Grin2d", "Grin3a",
                       "Chrna6", "Chrna3", "Htr5a", "Htr2a", "Htr7", "Irx4",
                       "PlxnC1", "Sema6d", "Sema4f", "Sema4a", "Sema6b", "Lrrc4b",
                       "Lrrc58", "Lrrc3b", "Wnt4", "Wnt9b", "Ctxn3", "Tenm1", "Gna14",
                       "Rgs4", "Rgs6", "Rgs5", "Pou4f2", "Chrnb3", "Bcan")
sr_p08_het_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["het_retina"]], color_high = colors[["het_scn"]],
  label_column = tx_label_column, label = interesting_genes, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "24location_ma_volcano_strict_tx/sr_p08_het_volcano.pdf", width = 9, height = 9)
## Warning in pp(file = "24location_ma_volcano_strict_tx/sr_p08_het_volcano.pdf",
## : The directory: 24location_ma_volcano_strict_tx does not exist, will attempt
## to create it.
sr_p08_het_volcano[["plot"]]
## Error:
## ! object 'sr_p08_het_volcano' not found
plotted <- dev.off()
sr_p08_het_volcano[["plot"]]
## Error:
## ! object 'sr_p08_het_volcano' not found
sr_p08_het_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["het_retina"]], color_high = colors[["het_scn"]],
  p_col = "deseq_adjp", label_column = tx_label_column, label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "24location_ma_volcano_strict_tx/sr_p08_het_ma.pdf", width = 9, height = 9)
sr_p08_het_ma[["plot"]]
## Error:
## ! object 'sr_p08_het_ma' not found
plotted <- dev.off()
sr_p08_het_ma[["plot"]]
## Error:
## ! object 'sr_p08_het_ma' not found

22.0.4 Also the ko

table_input <- location_strict_tables[["sr_p08_ko"]]
table_name <- "sr_p08_ko"
table <- table_input[["data"]][[table_name]]
sr_p08_ko_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["ko_retina"]], color_high = colors[["ko_scn"]],
  label_column = tx_label_column, label = interesting_genes, alpha = 1.0,
  size = 4)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "24location_ma_volcano_strict_tx/sr_p08_ko_volcano.pdf", width = 9, height = 9)
sr_p08_ko_volcano[["plot"]]
## Error:
## ! object 'sr_p08_ko_volcano' not found
plotted <- dev.off()
sr_p08_ko_volcano[["plot"]]
## Error:
## ! object 'sr_p08_ko_volcano' not found
sr_p08_ko_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_retina"]], color_high = colors[["ko_scn"]],
  p_col = "deseq_adjp", label_column = tx_label_column, label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "24location_ma_volcano_strict_tx/sr_p08_ko_ma.pdf", width = 9, height = 9)
sr_p08_ko_ma[["plot"]]
## Error:
## ! object 'sr_p08_ko_ma' not found
plotted <- dev.off()
sr_p08_ko_ma[["plot"]]
## Error:
## ! object 'sr_p08_ko_ma' not found

22.0.5 Also the ko

table_input <- location_tables[["sr_p15_ko"]]
table_name <- "sr_p15_ko"
table <- table_input[["data"]][[table_name]]
sr_p15_ko_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = colors[["ko_retina"]], color_high = colors[["ko_scn"]],
  label_column = tx_label_column, label = interesting_genes, alpha = 1.0,
  size = 4, min.segment.length = 0, point.padding = 0.2)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "24location_ma_volcano_strict_tx/sr_p15_ko_volcano.pdf", width = 12, height = 12)
sr_p15_ko_volcano[["plot"]]
## Error:
## ! object 'sr_p15_ko_volcano' not found
plotted <- dev.off()
sr_p15_ko_volcano[["plot"]]
## Error:
## ! object 'sr_p15_ko_volcano' not found
sr_p15_ko_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = colors[["ko_retina"]], color_high = colors[["ko_scn"]],
  p_col = "deseq_adjp", label_column = tx_label_column, label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "24location_ma_volcano_strict_tx/sr_p15_ko_ma.pdf", width = 9, height = 9)
sr_p15_ko_ma[["plot"]]
## Error:
## ! object 'sr_p15_ko_ma' not found
plotted <- dev.off()
sr_p15_ko_ma[["plot"]]
## Error:
## ! object 'sr_p15_ko_ma' not found

22.0.6 And time

time_tables_full <- combine_de_tables(
  time_de, keepers = time_keepers,
  label_column = tx_label_column,
  excel = glue("25full_contrasts_time_tx/full_tables-v{ver}.xlsx"))
## Error:
## ! object 'time_de' not found
time_sig_full <- extract_significant_genes(
  time_tables_full, according_to = "deseq",
  excel = glue("25full_contrasts_time_tx/full_sig-v{ver}.xlsx"))
## Error:
## ! object 'time_tables_full' not found
time_tables <- list()
time_sig <- list()
time_gp <- list()
time_cp <- list()
for (k in seq_along(time_keepers)) {
  name <- names(time_keepers)[k]
  message("Examining ", name)
  keeper <- time_keepers[name]
  includes <- time_inclusions[[name]]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- time_inclusions[[include_df_name]]
  includes <- time_inclusions[[include_name]]
  summary(rownames(time_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
  include_filename <- glue("26time_contrasts_tx/{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
  include_sig_filename <- glue("26time_contrasts_tx/{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
  time_tables[[name]] <- combine_de_tables(
    time_de, extra_annot = include_df,
    keepers = keeper, label_column = tx_label_column,
    excel = include_filename, wanted_genes = includes)
  print(time_tables[[name]])
  time_sig[[name]] <- extract_significant_genes(
    time_tables[[name]], according_to = "deseq",
    excel = include_sig_filename)
  print(time_sig[[name]])
  num_rows <- nrow(time_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(time_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  if (num_rows > 10) {
    time_gp[[name]] <- all_gprofiler(time_sig[[name]], species = "mmusculus")
    gp_written <- write_all_gp(time_gp[[name]])
    time_cp[[name]] <- all_cprofiler(
      time_sig[[name]], time_tables[[name]], orgdb = "org.Mm.eg.db", organism = "mouse",
      orgdb_from = orgdb_from, go_level = go_level, max_groupsize = max_groupsize)
    cp_written <- write_all_cp(time_cp[[name]], prefix = "27")
    gp_written <- write_all_gp(time_gp[[name]], prefix = "27")
  }
}
## Examining t_het_dlgn
## Error:
## ! object 'time_inclusions' not found

Send the plots separately.

for (k in seq_along(time_keepers)) {
  name <- names(time_keepers)[k]
  message("Examining ", name)
  keeper <- time_keepers[name]
  includes <- time_inclusions[[name]]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- time_inclusions[[include_df_name]]
  includes <- time_inclusions[[include_name]]
  num_rows <- nrow(time_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(time_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  num_objects <- length(time_cp[[name]])
  if (num_objects == 0) {
    warning("Something failed in all_cprofiler.")
  } else {
    upp <- which(grepl(x = names(time_cp[[name]]), pattern = "_up$"))
    downp <- which(grepl(x = names(time_cp[[name]]), pattern = "_down$"))
    if (length(upp) > 0) {
      mf_sig <- time_cp[[name]][[upp]][["go_data"]][["MF_enrich"]]
      cc_sig <- time_cp[[name]][[upp]][["go_data"]][["CC_enrich"]]
      bp_sig <- time_cp[[name]][[upp]][["go_data"]][["BP_enrich"]]
      mf_plots_up <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_up_filename <- glue("28clusterProfiler_plots_tx/{name}_up_mf_sig_tree.pdf")
      pp(file = mf_tree_up_filename)
      try(print(mf_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_up_filename <- glue("28clusterProfiler_plots_tx/{name}_up_mf_sig_dot.pdf")
      pp(file = mf_bar_up_filename)
      try(print(mf_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_up <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_up_filename <- glue("28clusterProfiler_plots_tx/{name}_up_cc_sig_tree.pdf")
      pp(file = cc_tree_up_filename)
      try(print(cc_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_up_filename <- glue("28clusterProfiler_plots_tx/{name}_up_cc_sig_dot.pdf")
      pp(file = cc_bar_up_filename)
      try(print(cc_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_up_filename <- glue("28clusterProfiler_plots_tx/{name}_up_bp_sig_tree.pdf")
      pp(file = bp_tree_up_filename)
      try(print(bp_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_up_filename <- glue("28clusterProfiler_plots_tx/{name}_up_bp_sig_dot.pdf")
      pp(file = bp_bar_up_filename)
      try(print(bp_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
    if (length(downp) > 0) {
      mf_sig <- time_cp[[name]][[downp]][["go_data"]][["MF_enrich"]]
      cc_sig <- time_cp[[name]][[downp]][["go_data"]][["CC_enrich"]]
      bp_sig <- time_cp[[name]][[downp]][["go_data"]][["BP_enrich"]]
      mf_plots_down <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_down_filename <- glue("28clusterProfiler_plots_tx/{name}_down_mf_sig_tree.pdf")
      pp(file = mf_tree_down_filename)
      try(print(mf_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_down_filename <- glue("28clusterProfiler_plots_tx/{name}_down_mf_sig_dot.pdf")
      pp(file = mf_bar_down_filename)
      try(print(mf_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_down <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_down_filename <- glue("28clusterProfiler_plots_tx/{name}_down_cc_sig_tree.pdf")
      pp(file = cc_tree_down_filename)
      try(print(cc_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_down_filename <- glue("28clusterProfiler_plots_tx/{name}_down_cc_sig_dot.pdf")
      pp(file = cc_bar_down_filename)
      try(print(cc_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_down_filename <- glue("28clusterProfiler_plots_tx/{name}_down_bp_sig_tree.pdf")
      pp(file = bp_tree_down_filename)
      try(print(bp_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_down_filename <- glue("28clusterProfiler_plots_tx/{name}_down_bp_sig_dot.pdf")
      pp(file = bp_bar_down_filename)
      try(print(bp_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
  }
}
## Examining t_het_dlgn
## Error:
## ! object 'time_inclusions' not found

22.1 Volcano plots by time

22.1.1 t_het_dlgn

table_name <- "t_het_dlgn"
table_input <- time_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
t_het_dlgn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4, min.segment.length = 0, point.padding = 0.2)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "29time_ma_volcano_tx/t_het_dlgn_volcano.pdf", width = 12, height = 12)
## Warning in pp(file = "29time_ma_volcano_tx/t_het_dlgn_volcano.pdf", width = 12,
## : The directory: 29time_ma_volcano_tx does not exist, will attempt to create
## it.
t_het_dlgn_volcano[["plot"]]
## Error:
## ! object 't_het_dlgn_volcano' not found
plotted <- dev.off()
t_het_dlgn_volcano[["plot"]]
## Error:
## ! object 't_het_dlgn_volcano' not found
t_het_dlgn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "29time_ma_volcano_tx/t_het_dlgn_ma.pdf", width = 9, height = 9)
t_het_dlgn_ma[["plot"]]
## Error:
## ! object 't_het_dlgn_ma' not found
plotted <- dev.off()
t_het_dlgn_ma[["plot"]]
## Error:
## ! object 't_het_dlgn_ma' not found

22.1.2 t_ko_dlgn

table_name <- "t_ko_dlgn"
table_input <- time_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
t_ko_dlgn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4, min.segment.length = 0, point.padding = 0.2)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "29time_ma_volcano_tx/t_ko_dlgn_volcano.pdf", width = 12, height = 12)
t_ko_dlgn_volcano[["plot"]]
## Error:
## ! object 't_ko_dlgn_volcano' not found
plotted <- dev.off()
t_ko_dlgn_volcano[["plot"]]
## Error:
## ! object 't_ko_dlgn_volcano' not found
t_ko_dlgn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "29time_ma_volcano_tx/t_ko_dlgn_ma.pdf", width = 9, height = 9)
t_ko_dlgn_ma[["plot"]]
## Error:
## ! object 't_ko_dlgn_ma' not found
plotted <- dev.off()
t_ko_dlgn_ma[["plot"]]
## Error:
## ! object 't_ko_dlgn_ma' not found

22.1.3 t_het_retina

table_name <- "t_het_retina"
table_input <- time_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
t_het_retina_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = time_colors[["p08_het_retina"]], color_high = time_colors[["p15_het_retina"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4, min.segment.length = 0, point.padding = 0.2)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "29time_ma_volcano_tx/t_het_retina_volcano.pdf", width = 12, height = 12)
t_het_retina_volcano[["plot"]]
## Error:
## ! object 't_het_retina_volcano' not found
plotted <- dev.off()
t_het_retina_volcano[["plot"]]
## Error:
## ! object 't_het_retina_volcano' not found
t_het_retina_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = time_colors[["p08_het_retina"]], color_high = time_colors[["p15_het_retina"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "29time_ma_volcano_tx/t_het_retina_ma.pdf", width = 9, height = 9)
t_het_retina_ma[["plot"]]
## Error:
## ! object 't_het_retina_ma' not found
plotted <- dev.off()
t_het_retina_ma[["plot"]]
## Error:
## ! object 't_het_retina_ma' not found

22.1.4 t_ko_retina

table_name <- "t_ko_retina"
table_input <- time_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
t_ko_retina_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4, min.segment.length = 0, point.padding = 0.2)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "29time_ma_volcano_tx/t_ko_retina_volcano.pdf", width = 12, height = 12)
t_ko_retina_volcano[["plot"]]
## Error:
## ! object 't_ko_retina_volcano' not found
plotted <- dev.off()
t_ko_retina_volcano[["plot"]]
## Error:
## ! object 't_ko_retina_volcano' not found
t_ko_retina_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "29time_ma_volcano_tx/t_ko_retina_ma.pdf", width = 9, height = 9)
t_ko_retina_ma[["plot"]]
## Error:
## ! object 't_ko_retina_ma' not found
plotted <- dev.off()
t_ko_retina_ma[["plot"]]
## Error:
## ! object 't_ko_retina_ma' not found

22.2 t_het_scn

table_name <- "t_het_scn"
table_input <- time_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
t_het_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4, min.segment.length = 0, point.padding = 0.2)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "29time_ma_volcano_tx/t_het_scn_volcano.pdf", width = 12, height = 12)
t_het_scn_volcano[["plot"]]
## Error:
## ! object 't_het_scn_volcano' not found
plotted <- dev.off()
t_het_scn_volcano[["plot"]]
## Error:
## ! object 't_het_scn_volcano' not found
t_het_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "29time_ma_volcano_tx/t_het_scn_ma.pdf", width = 9, height = 9)
t_het_scn_ma[["plot"]]
## Error:
## ! object 't_het_scn_ma' not found
plotted <- dev.off()
t_het_scn_ma[["plot"]]
## Error:
## ! object 't_het_scn_ma' not found

22.3 t_ko_scn

table_name <- "t_ko_scn"
table_input <- time_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
t_ko_scn_volcano <- plot_volcano_condition_de(
  table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  label_column = "external_transcript_name", label = interesting_genes, alpha = 1.0,
  size = 4, min.segment.length = 0, point.padding = 0.2)
## Error in `plot_volcano_condition_de()`:
## ! Column: deseq_logfc is not in the table.
pp(file = "29time_ma_volcano_tx/t_ko_scn_volcano.pdf", width = 12, height = 12)
t_ko_scn_volcano[["plot"]]
## Error:
## ! object 't_ko_scn_volcano' not found
plotted <- dev.off()
t_ko_scn_volcano[["plot"]]
## Error:
## ! object 't_ko_scn_volcano' not found
t_ko_scn_ma <- plot_ma_condition_de(
  table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
  color_low = time_colors[["p08_het_dlgn"]], color_high = time_colors[["p15_het_dlgn"]],
  p_col = "deseq_adjp", label_column = "external_transcript_name", label = interesting_genes, outline = outline)
## The column: external_transcript_name is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean",
## : NAs introduced by coercion
## Error in `[[<-.data.frame`:
## ! replacement has 1 row, data has 0
pp(file = "29time_ma_volcano_tx/t_ko_scn_ma.pdf", width = 9, height = 9)
t_ko_scn_ma[["plot"]]
## Error:
## ! object 't_ko_scn_ma' not found
plotted <- dev.off()
t_ko_scn_ma[["plot"]]
## Error:
## ! object 't_ko_scn_ma' not found

22.3.0.1 Repeat with the strict filter

time_strict_tables <- list()
time_strict_sig <- list()
time_strict_gp <- list()
time_strict_cp <- list()
time_strict_en <- list()
for (k in seq_along(time_keepers)) {
  name <- names(time_keepers)[k]
  message("Examining ", name)
  keeper <- time_keepers[name]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- time_inclusions_strict[[include_df_name]]
  includes <- time_inclusions_strict[[include_name]]
  found_includes <- rownames(time_sig_full[["deseq"]][["ups"]][[name]]) %in% includes
  summary(found_includes)
  if (sum(found_includes) == 0) {
    next
  }
  include_filename <- glue("30time_strict_contrasts_excel_tx/{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
  include_sig_filename <- glue("30time_strict_contrasts_excel_tx/{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
  time_strict_tables[[name]] <- combine_de_tables(
    time_de, extra_annot = include_df,
    keepers = keeper, label_column = label_column,
    excel = include_filename, wanted_genes = includes)
  print(time_strict_tables[[name]])
  time_strict_sig[[name]] <- extract_significant_genes(
    time_strict_tables[[name]], according_to = "deseq",
    excel = include_sig_filename)
  print(time_strict_sig[[name]])
  num_rows <- nrow(time_strict_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(time_strict_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  if (num_rows >= 10) {
    message("Performing gprofiler/clusterProfiler.")
    time_strict_gp[[name]] <- all_gprofiler(time_strict_sig[[name]], species = "mmusculus")
    time_strict_cp[[name]] <- all_cprofiler(
      time_strict_sig[[name]], time_strict_tables[[name]],
      orgdb = "org.Mm.eg.db", go_level = go_level, orgdb_from = orgdb_from,
      max_groupsize = max_groupsize, organism = "mouse")
    #if (!is.null(get0("m2_gsc"))) {
    #  time_strict_en[[name]] <- all_enricher(time_strict_sig[[name]], gsc = m2_gsc,
    #                                      orgdb = "org.Mm.eg.db", from = "ENSEMBL", to = "SYMBOL")
    #}
    gp_written <- write_all_gp(time_strict_gp[[name]], prefix = "31", suffix = "strict")
    cp_written <- write_all_cp(time_strict_cp[[name]], prefix = "31", suffix = "strict")
    #en_written <- write_all_en(time_strict_en[[name]])
  } else {
    warning("There are less than 10 genes up and down in the ", name, " comparison.")
    message("There are less than 10 genes up and down in the ", name, " comparison.")
  }
}
## Examining t_het_dlgn
## Error:
## ! object 'time_inclusions_strict' not found

Send the plots separately.

for (k in seq_along(time_keepers)) {
  name <- names(time_keepers)[k]
  message("Examining ", name)
  keeper <- time_keepers[name]
  includes <- time_inclusions[[name]]
  include_name <- paste0("inc_", name)
  include_df_name <- paste0("df_", name)
  include_df <- time_inclusions[[include_df_name]]
  includes <- time_inclusions[[include_name]]
  num_rows <- nrow(time_sig[[name]][["deseq"]][["ups"]][[name]]) +
    nrow(time_sig[[name]][["deseq"]][["downs"]][[name]])
  message("There are ", num_rows, " significant up and down genes.")
  num_objects <- length(time_cp[[name]])
  if (num_objects == 0) {
    warning("Something failed in all_cprofiler.")
  } else {
    upp <- which(grepl(x = names(time_cp[[name]]), pattern = "_up$"))
    downp <- which(grepl(x = names(time_cp[[name]]), pattern = "_down$"))
    if (length(upp) > 0) {
      mf_sig <- time_cp[[name]][[upp]][["go_data"]][["MF_enrich"]]
      cc_sig <- time_cp[[name]][[upp]][["go_data"]][["CC_enrich"]]
      bp_sig <- time_cp[[name]][[upp]][["go_data"]][["BP_enrich"]]
      mf_plots_up <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_up_filename <- glue("32cp_trees_tx/{name}_up_mf_sig_tree.pdf")
      pp(file = mf_tree_up_filename)
      try(print(mf_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_up_filename <- glue("32cp_bar_tx/{name}_up_mf_sig_dot.pdf")
      pp(file = mf_bar_up_filename)
      try(print(mf_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_up <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_up_filename <- glue("32cp_trees_tx/{name}_up_cc_sig_tree.pdf")
      pp(file = cc_tree_up_filename)
      try(print(cc_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_up_filename <- glue("32cp_bar_tx/{name}_up_cc_sig_dot.pdf")
      pp(file = cc_bar_up_filename)
      try(print(cc_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_up_filename <- glue("32cp_trees_tx/{name}_up_bp_sig_tree.pdf")
      pp(file = bp_tree_up_filename)
      try(print(bp_plots_up[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_up <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_up_filename <- glue("32cp_bar_tx/{name}_up_bp_sig_dot.pdf")
      pp(file = bp_bar_up_filename)
      try(print(bp_plots_up[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
    if (length(downp) > 0) {
      mf_sig <- time_cp[[name]][[downp]][["go_data"]][["MF_enrich"]]
      cc_sig <- time_cp[[name]][[downp]][["go_data"]][["CC_enrich"]]
      bp_sig <- time_cp[[name]][[downp]][["go_data"]][["BP_enrich"]]
      mf_plots_down <- plot_enrichresult(mf_sig, showCategory = go_categories)
      mf_tree_down_filename <- glue("32cp_trees_tx/{name}_down_mf_sig_tree.pdf")
      pp(file = mf_tree_down_filename)
      try(print(mf_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      mf_bar_down_filename <- glue("32cp_bar_tx/{name}_down_mf_sig_dot.pdf")
      pp(file = mf_bar_down_filename)
      try(print(mf_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      cc_plots_down <- plot_enrichresult(cc_sig, showCategory = go_categories)
      cc_tree_down_filename <- glue("32cp_trees_tx/{name}_down_cc_sig_tree.pdf")
      pp(file = cc_tree_down_filename)
      try(print(cc_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      cc_bar_down_filename <- glue("32cp_bar_tx/{name}_down_cc_sig_dot.pdf")
      pp(file = cc_bar_down_filename)
      try(print(cc_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_tree_down_filename <- glue("32cp_trees_tx/{name}_down_bp_sig_tree.pdf")
      pp(file = bp_tree_down_filename)
      try(print(bp_plots_down[["tree"]]), silent = TRUE)
      plotted <- dev.off()
      bp_plots_down <- plot_enrichresult(bp_sig, showCategory = go_categories)
      bp_bar_down_filename <- glue("32cp_bar_tx/{name}_down_bp_sig_dot.pdf")
      pp(file = bp_bar_down_filename)
      try(print(bp_plots_down[["dot"]]), silent = TRUE)
      plotted <- dev.off()
    }
  }
}
## Examining t_het_dlgn
## Error:
## ! object 'time_inclusions' not found

23 Translatome queries

In conversation with Colenso, he spoke about a series of contrasts which would be interesting to attempt in order to query the changes across both locations and genotypes and/or both locations and time, thus:

(p08_het_scn / p08_het_retina) / (p08_ko_scn / p08_ko_retina)

as an example. We can definitely do these, but they do not work for all methods employed (I think they work best with limma and edgeR).

Lets find out!

23.1 Two scn/retina comparisons

  • (p08_het_scn / p08_het_retina) / (p08_ko_scn / p08_ko_retina)
  • (p15_het_scn / p15_het_retina) / (p15_ko_scn / p15_ko_retina)
scn_extra <- glue("\\
  p08het = (conditionp08_het_scn - conditionp08_het_retina), \\
  p08ko = (conditionp08_ko_scn - conditionp08_ko_retina), \\
  p08het_vs_p08ko = (conditionp08_het_scn - conditionp08_het_retina) - (conditionp08_ko_scn - conditionp08_ko_retina), \\
  p15het = (conditionp15_het_scn - conditionp15_het_retina), \\
  p15ko = (conditionp15_ko_scn - conditionp15_ko_retina), \\
  p15het_vs_p15ko = (conditionp15_het_scn - conditionp15_het_retina) - (conditionp15_ko_scn - conditionp15_ko_retina)")
scn_translatome_de_keepers <- list(
  "p08het" = c("p08_het_scn", "p08_het_retina"),
  "p08ko" = c("p08_ko_scn", "p08_ko_retina"),
  "p15het" = c("p15_het_scn", "p15_het_retina"),
  "p15ko" = c("p15_ko_scn", "p15_ko_retina"))
scn_translatome_keepers <- list(
  "p08het" = c("p08_het_scn", "p08_het_retina"),
  "p08ko" = c("p08_ko_scn", "p08_ko_retina"),
  "p08_scn_translatome" = c("p08het", "p08ko"),
  "p15het" = c("p15_het_scn", "p15_het_retina"),
  "p15ko" = c("p15_ko_scn", "p15_ko_retina"),
  "p15_scn_translatome" = c("p15het", "p15ko"))
filt <- normalize(tx_pairwise_input, filter = TRUE)
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'normalize': object 'tx_pairwise_input' not found
limma_test <- limma_pairwise(filt,
                             keepers = scn_translatome_de_keepers,
                             model_fstring = "~ 0 + condition",
                             model_svs = FALSE, extra_contrastrs = scn_extra)
## Error in `h()`:
## ! error in evaluating the argument 'input' in selecting a method for function 'state': object 'filt' not found
edger_test <- edger_pairwise(filt,
                             keepers = scn_translatome_de_keepers,
                             model_fstring = "~ 0 + condition",
                             model_svs = FALSE, extra_contrasts = scn_extra)
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'filt' not found
scn_translatome_de <- all_pairwise(tx_pairwise_input, filter = TRUE,
                                   keepers = scn_translatome_de_keepers,
                                   model_svs = FALSE,
                                   model_fstring = "~ 0 + condition",
                                   do_basic = FALSE, do_dream = FALSE,
                                   do_noiseq = FALSE, do_ebseq = FALSE,
                                   extra_contrasts = scn_extra)
## Warning in all_pairwise(tx_pairwise_input, filter = TRUE, keepers =
## scn_translatome_de_keepers, : This will likely fail because of how the keepers
## and extra contrasts are evaluated.
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
scn_combined_test <- combine_de_tables(
  scn_translatome_de, keepers = scn_translatome_keepers,
  excel = glue("33translatome_xlsx_tx/test_scn_translatome_unfiltered_nosva-v{ver}.xlsx"))
## Error:
## ! object 'scn_translatome_de' not found
scn_translatome_de_sva <- all_pairwise(tx_pairwise_input, filter = TRUE,
                                       keepers = scn_translatome_de_keepers,
                                       model_svs = "svaseq",
                                       model_fstring = "~ 0 + condition",
                                       do_basic = FALSE, do_dream = FALSE,
                                       do_noiseq = FALSE, do_ebseq = FALSE,
                                       extra_contrasts = scn_extra)
## Warning in all_pairwise(tx_pairwise_input, filter = TRUE, keepers =
## scn_translatome_de_keepers, : This will likely fail because of how the keepers
## and extra contrasts are evaluated.
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
scn_combined_test_sva <- combine_de_tables(
  scn_translatome_de_sva, keepers = scn_translatome_keepers,
  excel = glue("33translatome_xlsx_tx/test_scn_translatome_unfiltered_sva-v{ver}.xlsx"))
## Error:
## ! object 'scn_translatome_de_sva' not found

23.1.1 Subtracting DESeq2 results: p08 scn het vs ko

p08_scn_combined_deseq <- subtract_de_results(
  first_table = scn_combined_test[["data"]][["p08het"]],
  second_table = scn_combined_test[["data"]][["p08ko"]],
  first_lfc = "deseq_logfc", second_lfc = "deseq_logfc",
  first_p = "deseq_adjp", second_p = "deseq_adjp",
  first_name = "het", second_name = "ko",
  excel = glue("33translatome_xlsx_tx/translatome_p08_scn_combined_deseq-v{ver}.xlsx"))
## Error:
## ! object 'scn_combined_test' not found

23.1.2 Subtracting DESeq2 results: p15 scn het vs ko

p15_scn_combined_deseq <- subtract_de_results(
  first_table = scn_combined_test[["data"]][["p15het"]],
  second_table = scn_combined_test[["data"]][["p15ko"]],
  first_lfc = "deseq_logfc", second_lfc = "deseq_logfc",
  first_p = "deseq_adjp", second_p = "deseq_adjp",
  first_name = "het", second_name = "ko",
  excel = glue("34translatome_deseqsub_xlsx_tx/translatome_p15_scn_combined_deseq-v{ver}.xlsx"))
## Error:
## ! object 'scn_combined_test' not found

23.2 One dlgn/retina comparison

  • (p08_het_dlgn / p08_het_retina) / (p08_ko_dlgn / p08_ko_retina)
p08_dlgn_extra <- "p08het_vs_p08ko = (conditionp08_het_dlgn - conditionp08_het_retina) - (conditionp08_ko_dlgn - conditionp08_ko_retina)"
p08_dlgn_translatome_de_keepers <- list(
  "p08het" = c("p08_het_dlgn", "p08_het_retina"),
  "p08ko" = c("p08_ko_dlgn", "p08_ko_retina"))
p08_dlgn_translatome_keepers <- list(
  "p08_het_dlgn_vs_retina" = c("p08_het_dlgn", "p08_het_retina"),
  "p08_ko_dlgn_vs_retina" = c("p08_ko_dlgn", "p08_ko_retina"),
  "p08_dlgn_translatome" = c("p08het", "p08ko"))
p08_dlgn_translatome_de <- all_pairwise(tx_pairwise_input, filter = TRUE,
                                        keepers = p08_dlgn_translatome_de_keepers,
                                        model_svs = FALSE,
                                        model_fstring = "~ 0 + condition",
                                        do_basic = FALSE, do_dream = FALSE,
                                        do_noiseq = FALSE, do_ebseq = FALSE,
                                        extra_contrasts = p08_dlgn_extra)
## Warning in all_pairwise(tx_pairwise_input, filter = TRUE, keepers =
## p08_dlgn_translatome_de_keepers, : This will likely fail because of how the
## keepers and extra contrasts are evaluated.
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
p08_dlgn_combined_test <- combine_de_tables(
  p08_dlgn_translatome_de, keepers = p08_dlgn_translatome_keepers,
  label_column = label_column,
  excel = glue("33translatome_xlsx_tx/test_p08_dlgn_translatome_unfiltered_nosva-v{ver}.xlsx"))
## Error:
## ! object 'p08_dlgn_translatome_de' not found
p08_dlgn_translatome_de_sva <- all_pairwise(tx_pairwise_input, filter = TRUE,
                                            keepers = p08_dlgn_translatome_de_keepers,
                                            model_svs = "svaseq",
                                            model_fstring = "~ 0 + condition",
                                            do_basic = FALSE, do_dream = FALSE,
                                            do_noiseq = FALSE, do_ebseq = FALSE,
                                            extra_contrasts = p08_dlgn_extra)
## Warning in all_pairwise(tx_pairwise_input, filter = TRUE, keepers =
## p08_dlgn_translatome_de_keepers, : This will likely fail because of how the
## keepers and extra contrasts are evaluated.
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
p08_dlgn_combined_test_sva <- combine_de_tables(
  p08_dlgn_translatome_de_sva, keepers = p08_dlgn_translatome_keepers,
  label_column = label_column,
  excel = glue("33translatome_xlsx_tx/test_p08_dlgn_translatome_unfiltered_sva-v{ver}.xlsx"))
## Error:
## ! object 'p08_dlgn_translatome_de_sva' not found

23.2.1 Subtracting the DESeq2 results

23.3 Two scn/retina comparisons (p15/p08 across het/ko)

  • (p15_het_scn / p15_het_retina) / (p08_het_scn / p08_het_retina)
  • (p15_ko_scn / p15_ko_retina) / (p08_ko_scn / p08_ko_retina)
time_scn_extra <- glue("\\
  p15het = (conditionp15_het_scn - conditionp15_het_retina), \\
  p08het = (conditionp08_het_scn - conditionp08_het_retina), \\
  p15het_vs_p08het = (conditionp15_het_scn - conditionp15_het_retina) - (conditionp08_het_scn - conditionp08_het_retina),
  p15ko = (conditionp15_ko_scn - conditionp15_ko_retina), \\
  p08ko = (conditionp08_ko_scn - conditionp08_ko_retina), \\
  p15ko_vs_p08ko = (conditionp15_ko_scn - conditionp15_ko_retina) - (conditionp08_ko_scn - conditionp08_ko_retina)")
time_scn_translatome_de_keepers <- list(
  "p15het" = c("p15_het_scn", "p15_het_retina"),
  "p08het" = c("p08_het_scn", "p08_het_retina"),
  "p15ko" = c("p15_ko_scn", "p15_ko_retina"),
  "p08ko" = c("p08_ko_scn", "p08_ko_retina"))
time_scn_translatome_keepers <- list(
  "p15het" = c("p15_het_scn", "p15_het_retina"),
  "p08het" = c("p08_het_scn", "p08_het_retina"),
  "p15ko" = c("p15_ko_scn", "p15_ko_retina"),
  "p08ko" = c("p08_ko_scn", "p08_ko_retina"),
  "p15_het_sc_vs_retina" = c("p15_het_scn", "p15_het_retina"),
  "p08_het_sc_vs_retina" = c("p08_het_scn", "p08_het_retina"),
  "scn_het_translatome" = c("p15het", "p08het"),
  "scn_ko_translatome" = c("p15ko", "p08ko"))
time_scn_translatome_de <- all_pairwise(tx_pairwise_input, filter = TRUE,
                                        keepers = time_scn_translatome_de_keepers,
                                        model_svs = FALSE,
                                        model_fstring = "~ 0 + condition",
                                        do_basic = FALSE, do_dream = FALSE,
                                        do_noiseq = FALSE, do_ebseq = FALSE,
                                        extra_contrasts = time_scn_extra)
## Warning in all_pairwise(tx_pairwise_input, filter = TRUE, keepers =
## time_scn_translatome_de_keepers, : This will likely fail because of how the
## keepers and extra contrasts are evaluated.
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
time_scn_translatome_test <- combine_de_tables(
  time_scn_translatome_de,
  keepers = time_scn_translatome_keepers,
  label_column = label_column,
  excel = glue("33translatome_xlsx_tx/test_time_scn_translatome_unfiltered_nosva-v{ver}.xlsx"))
## Error:
## ! object 'time_scn_translatome_de' not found
time_scn_translatome_de_sva <- all_pairwise(tx_pairwise_input, filter = TRUE,
                                            keepers = time_scn_translatome_de_keepers,
                                            model_svs = "svaseq",
                                            model_fstring = "~ 0 + condition",
                                            do_basic = FALSE, do_dream = FALSE,
                                            do_noiseq = FALSE, do_ebseq = FALSE,
                                            extra_contrasts = time_scn_extra)
## Warning in all_pairwise(tx_pairwise_input, filter = TRUE, keepers =
## time_scn_translatome_de_keepers, : This will likely fail because of how the
## keepers and extra contrasts are evaluated.
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'colData': object 'tx_pairwise_input' not found
time_scn_translatome_test_sva <- combine_de_tables(
  time_scn_translatome_de_sva,
  keepers = time_scn_translatome_keepers,
  label_column = label_column,
  excel = glue("33translatome_xlsx_tx/test_time_scn_translatome_unfiltered_sva-v{ver}.xlsx"))
## Error:
## ! object 'time_scn_translatome_de_sva' not found

Next step: Perform the retina filter; need to think about the proper union/intersection of the retina/x expression values

In the previous block, we are making 2 global comparisons, here is one of them:

(p15hetscn/p15hetret)/(p08hetscn/p08hetret)

I therefore want to extract the most logical set of genes higher in some/all of these conditions with respect to the corresponding wt conditions. Previously, in section ‘Extract genes included for each set of contrasts’, I attempted to perform this operation for 2 specific wt conditions. When this was performed, it took the unique(union) of the two sets. Thus it stands to reason that I want to take the unique(union) of all 4 in this instance? e.g.:

(p15hetscn > p15wtscn) | (p15hetret > p15wtret) | (p08hetscn > p08wtscn) | (p08hetret > p08wtret)

I kind of think it should be:

((p15hetscn > p15wtscn) | (p15hetret > p15wtret)) & ((p08hetscn > p08wtscn) | (p08hetret > p08wtret))

gross, perhaps I should just do this manually, given that there are only a few putative translatomes to query?

24 Quick and dirty DESeq2 contrast of contrasts

In a fashion similar to how Hector handled the effect of phagocytosis with Laura and Najib a long time ago, I propose to do a simple subtraction of the results of our two contrasts which comprise the translatome query (I was thinking about this last week, thus the inclusion of them in the de tables above). Similarly to the phagocytosis effect, I will simply take the worst posible adjusted p-value. I will repeat this with limma/EdgeR and see how similar the final results are to what those methods provide in the (a/b)/(c/d) comparisons. I am reasonably certain that DESeq2’s results() function has the ability to perform these odd contrasts, but I have never figured out how; perhaps I will use this as a chance to revisit that…

Let us test this idea with the p08 dlgn query, which seeks to compare:

(p08_het_dlgn / p08_het_retina) / (p08_ko_dlgn / p08_ko_retina)

These are maintained in the de_table with the names ‘p08_het_dlgn_vs_retina’ and ‘p08_ko_dlgn_vs_retina’

24.1 p08 dlgn het vs ko

p08_dlgn_combined_deseq <- subtract_de_results(
  first_table = p08_dlgn_combined_test[["data"]][["p08_het_dlgn_vs_retina"]],
  second_table = p08_dlgn_combined_test[["data"]][["p08_ko_dlgn_vs_retina"]],
  first_lfc = "deseq_logfc", second_lfc = "deseq_logfc",
  first_p = "deseq_adjp", second_p = "deseq_adjp",
  first_name = "het", second_name = "ko",
  excel = glue("34translatome_deseqsub_xlsx_tx/translatome_p08_dlgn_combined_deseq-v{ver}.xlsx"))
## Error:
## ! object 'p08_dlgn_combined_test' not found

See how similar these results are to those obtained from limma/edger.

test_columns <- c("edger_logfc", "limma_logfc", "edger_adjp", "limma_adjp")
test_df <- p08_dlgn_combined_test[["data"]][["p08_dlgn_translatome"]][, test_columns]
## Error:
## ! object 'p08_dlgn_combined_test' not found
test_df <- merge(test_df, p08_dlgn_combined_deseq, by = "row.names")
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'merge': object 'test_df' not found
rownames(test_df) <- test_df[["Row.names"]]
## Error:
## ! object 'test_df' not found
test_df[["Row.names"]] <- NULL
## Error:
## ! object 'test_df' not found
cor.test(test_df[["limma_logfc"]], test_df[["het_vs_ko_logfc"]])
## Error:
## ! object 'test_df' not found
cor.test(test_df[["edger_logfc"]], test_df[["het_vs_ko_logfc"]])
## Error:
## ! object 'test_df' not found
tt <- plot_linear_scatter(test_df[, c("limma_logfc", "het_vs_ko_logfc")])
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'test_df' not found
tt[["scatter"]]
## NULL
tt <- plot_linear_scatter(test_df[, c("edger_adjp", "het_vs_ko_p")])
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'test_df' not found
tt[["scatter"]]
## NULL
## So, using the maximum p-value is a complete failure; but the extreme similarities
## between this and edgeR suggest to me that it is likely possible to use the results
## from edgeR without concern (or limma for that matter, it was also extremely similar)
## Or I can spend a little time and collect the numbers on each side of the division
## and calculate a t statistic myself.

25 Non-Specific filtering of the translatome data

I have on hand

  • p08het_vs_p08ko : (p08_het_scn - p08_het_retina) - (p08_ko_scn - p08_ko_retina)
  • p15het_vs_p15ko : (p15_het_scn - p15_het_retina) - (p15_ko_scn - p15_ko_retina)
  • p08het_vs_p08ko : (p08_het_dlgn - p08_het_retina) - (p08_ko_dlgn - p08_ko_retina)
  • p15het_vs_p08het : (p15_het_scn - p15_het_retina) - (p08_het_scn - p08_het_retina)
  • p15ko_vs_p08ko : (p15_ko_scn - p15_ko_retina) - (p08_ko_scn - p08_ko_retina)

I have gene sets up above which define the genes suitable for each of these pieces. There are only 5 comparisons, let us step through them.

25.1 SCN translatome het/ko at p08

The data for this contrast resides in scn_combined_test\(data\)p08_scn_translatome or the same slot of scn_combined_test_sva

  • p08_het_scn - p08_het_retina) - (p08_ko_scn - p08_ko_retina)

Thus, the inclusion_sig portions to extract are found in: inclusion_sig[[“deseq”]][[“ups”]], and are named exactly as written above!

p08_het_vs_ko_translatome_unfilt <- scn_combined_test[["data"]][["p08_scn_translatome"]]
## Error:
## ! object 'scn_combined_test' not found
num_union <- unique(c(rownames(inclusion_sig[["deseq"]][["ups"]][["p08_het_scn"]]),
                      rownames(inclusion_sig[["deseq"]][["ups"]][["p08_het_retina"]])))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'inclusion_sig' not found
length(num_union)
## Error:
## ! object 'num_union' not found
den_union <- unique(c(rownames(inclusion_sig[["deseq"]][["ups"]][["p08_ko_scn"]]),
                      rownames(inclusion_sig[["deseq"]][["ups"]][["p08_ko_retina"]])))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'inclusion_sig' not found
length(den_union)
## Error:
## ! object 'den_union' not found
both_union <- unique(c(num_union, den_union))
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function 'unique': object 'num_union' not found
length(both_union)
## Error:
## ! object 'both_union' not found
both_inter_idx <- num_union %in% den_union
## Error in `h()`:
## ! error in evaluating the argument 'x' in selecting a method for function '%in%': object 'num_union' not found
both_inter <- num_union[both_inter_idx]
## Error:
## ! object 'num_union' not found
length(both_inter)
## Error:
## ! object 'both_inter' not found
keeper <- list("p08_scn_translatome" = c("p08het", "p08ko"))
p08_scn_translatome_union_filtered <- combine_de_tables(
  scn_translatome_de, keepers = keeper,
  label_column = label_column,
  excel = glue("35translatome_union_tx/p08_scn_translatome_union_filtered_nosva-v{ver}.xlsx"),
  wanted_genes = both_union)
## Error:
## ! object 'scn_translatome_de' not found
p08_scn_translatome_inter_filtered <- combine_de_tables(
  scn_translatome_de, keepers = keeper,
  label_column = label_column,
  excel = glue("35translatome_union_tx/p08_scn_translatome_intersect_filtered_nosva-v{ver}.xlsx"),
  wanted_genes = both_inter)
## Error:
## ! object 'scn_translatome_de' not found
p08_scn_translatome_union_filtered_sva <- combine_de_tables(
  scn_translatome_de_sva, keepers = keeper,
  label_column = label_column,
  excel = glue("35translatome_union_tx/p08_scn_translatome_union_filtered_sva-v{ver}.xlsx"),
  wanted_genes = both_union)
## Error:
## ! object 'scn_translatome_de_sva' not found
p08_scn_translatome_union_filtered <- combine_de_tables(
  scn_translatome_de, keepers = keeper,
  label_column = label_column,
  excel = glue("35translatome_union_tx/p08_scn_translatome_intersect_filtered_sva-v{ver}.xlsx"),
  wanted_genes = both_inter)
## Error:
## ! object 'scn_translatome_de' not found

26 Venn/UpSet of Retina, SCN, and dLGN DE Genes

Here is a snippet from Rashmi which expresses nicely the DE-result comparisons she is most interested:

Since, I want to know the number of DEG expressed in Retina, SCN and dLGN with respect to genotype, Location and time. I prepared the venn diagram for these comparison:

  • Genotype: P8 Ret Het vs KO, P15 Ret Het vs KO, P8 SCN Het vs KO, P15 SCN Het vs KO, P8 dLGN Het vs KO, P15 dLGN Het vs KO
  • Location: P8_het Ret vs SCN, P8_KO Ret vs SCN, P15_het Ret vs SCN, P15_KO Ret vs SCN, P8_het Ret vs dLGN, P8_KO Ret vs dLGN, P15_het Ret vs dLGN, P15_KO Ret vs dLGN, P8_het SCN vs dLGN, P8_KO SCN vs dLGN, P15_het SCN vs dLGN, P15_KO SCN vs dLGN.

Since I was interested in understanding the change in local translatome according to Location for different developmental time points for Het and KO. Hence, I tried to generate a venn diagram for Location (Ret and SCN) at developmental time points P8 and P15 for genotype het and KO. So the venn diagram / upset plot will be for location where some genes will be shared/unique for P8_Ret_het, P8_SCN_Het, P15_Ret_HET, P15_SCN_HET. We can prepare an upset plot for P8_Ret_KO, P8_SCN_KO, P15_Ret_KO and P15_SCN_KO also. Or can generate an upset plot by combining both P8_Ret_het, P8_SCN_Het, P15_Ret_HET and P15_SCN_HET and P8_Ret_KO, P8_SCN_KO, P15_Ret_KO and P15_SCN_KO.

Ok, let us see if I can implement this, starting with the genotype query

  • Genotype: P8 Ret Het vs KO, P15 Ret Het vs KO, P8 SCN Het vs KO, P15 SCN Het vs KO, P8 dLGN Het vs KO, P15 dLGN Het vs KO

26.1 ko vs het; all locations and times

## The appropriate data structure is 'genotype_tables',
## and the tables of interest are:
table_names <- c("kh_p08_retina", "kh_p15_retina", "kh_p08_scn",
                 "kh_p15_scn", "kh_p08_dlgn", "kh_p15_dlgn")
table_names %in% names(genotype_sig)
## [1] FALSE FALSE FALSE FALSE FALSE FALSE
newsig <- genotype_sig[[1]]
## Error in `genotype_sig[[1]]`:
## ! subscript out of bounds
for (sig in 2:length(table_names)) {
  name <- table_names[sig]
  newsig[["deseq"]][["ups"]][[name]] <- genotype_sig[[name]][["deseq"]][["ups"]][[name]]
  newsig[["deseq"]][["downs"]][[name]] <- genotype_sig[[name]][["deseq"]][["downs"]][[name]]
}
## Error:
## ! object 'newsig' not found
genotype_upsetr <- upsetr_sig(newsig)
## Error:
## ! object 'newsig' not found
genotype_upset_written <- write_upset_groups(genotype_upsetr, excel = "36upset_genotype_tx/genotype_upset_groups.xlsx")
## Error:
## ! object 'genotype_upsetr' not found
genotype_upsetr[["all_plot"]]
## Error:
## ! object 'genotype_upsetr' not found
pp(file = "36upset_genotype_tx/test_genotype_upset.pdf")
## Warning in pp(file = "36upset_genotype_tx/test_genotype_upset.pdf"): The
## directory: 36upset_genotype_tx does not exist, will attempt to create it.
print(genotype_upsetr[["all_plot"]])
## Error:
## ! object 'genotype_upsetr' not found
plotted <- dev.off()

Now let us try the location-specific comparisons

26.2 scn vs retina, p08

## The appropriate data structure is 'genotype_tables',
## and the tables of interest are:
table_names <- c("sr_p08_het", "sr_p08_ko")
table_names %in% names(location_sig)
## [1] FALSE FALSE
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
  name <- table_names[sig]
  newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
  newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in `xtfrm.data.frame()`:
## ! cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "36upset_genotype_tx/sr_p08_hetko_upset_groups.xlsx")
## Error:
## ! object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error:
## ! object 'location_upsetr' not found
pp(file = "36upset_genotype_tx/test_location_sr_p08_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error:
## ! object 'location_upsetr' not found
plotted <- dev.off()

I am reasonably certain that Rashmi would like a table of the genes shared among increased scn ko and het in the above plot along with the increased retina (e.g. the 269 and 103 gene sets).

26.3 scn vs retina, p15

table_names <- c("sr_p15_het", "sr_p15_ko")
table_names %in% names(location_sig)
## [1] FALSE FALSE
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
  name <- table_names[sig]
  newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
  newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in `xtfrm.data.frame()`:
## ! cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "36upset_genotype_tx/sr_p15_hetko_upset_groups.xlsx")
## Error:
## ! object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error:
## ! object 'location_upsetr' not found
print(scn_retina_p15_upset_result)
## Error:
## ! object 'scn_retina_p15_upset_result' not found
pp(file = "36upset_genotype_tx/test_location_sr_p15_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error:
## ! object 'location_upsetr' not found
plotted <- dev.off()

26.4 dlgn vs retina, p08

## The appropriate data structure is 'genotype_tables',
## and the tables of interest are:
table_names <- c("dr_p08_het", "dr_p08_ko")
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
  name <- table_names[sig]
  newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
  newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in `xtfrm.data.frame()`:
## ! cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "3upset_genotype_tx/dr_p08_hetko_upset_groups.xlsx")
## Error:
## ! object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error:
## ! object 'location_upsetr' not found
pp(file = "36upset_genotype_tx/test_location_dr_p08_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error:
## ! object 'location_upsetr' not found
plotted <- dev.off()

26.5 dlgn vs retina, p15

## The appropriate data structure is 'genotype_tables',
## and the tables of interest are:
table_names <- c("dr_p15_het", "dr_p15_ko")
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
  name <- table_names[sig]
  newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
  newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in `xtfrm.data.frame()`:
## ! cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "37upset_location_tx/dr_p15_hetko_upset_groups.xlsx")
## Error:
## ! object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error:
## ! object 'location_upsetr' not found
pp(file = "37upset_location_tx/test_location_dr_p15_hetko_upset.pdf")
## Warning in pp(file =
## "37upset_location_tx/test_location_dr_p15_hetko_upset.pdf"): The directory:
## 37upset_location_tx does not exist, will attempt to create it.
print(location_upsetr[["all_plot"]])
## Error:
## ! object 'location_upsetr' not found
plotted <- dev.off()

26.6 dlgn vs scn, p08

table_names <- c("ds_p08_het", "ds_p08_ko")
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
  name <- table_names[sig]
  newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
  newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in `xtfrm.data.frame()`:
## ! cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "37upset_location_tx/ds_p08_hetko_upset_groups.xlsx")
## Error:
## ! object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error:
## ! object 'location_upsetr' not found
pp(file = "37upset_location_tx/test_location_ds_p08_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error:
## ! object 'location_upsetr' not found
plotted <- dev.off()

26.7 dlgn vs scn, p15

table_names <- c("ds_p15_het", "ds_p15_ko")
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
  name <- table_names[sig]
  newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
  newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in `xtfrm.data.frame()`:
## ! cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "37upset_location_tx/ds_p15_hetko_upset_groups.xlsx")
## Error:
## ! object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error:
## ! object 'location_upsetr' not found
pp(file = "37upset_location_tx/test_location_ds_p15_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error:
## ! object 'location_upsetr' not found
plotted <- dev.off()

27 Shared and unique gene sets across x/wt

In this block I want to find the unique and shared genes between:

  1. scn p8 het/wt and retina p8 het/wt: hwp08scninc, hwp08retinc, hwp08scndec, hwp08retdec
  2. scn p15 het/wt and retina p15 het/wt: hwp15scninc, hwp15retinc, hwp15scndec, hwp15retdec
  3. #1 and #2 together: 8 catgories above
  4. scn p8 ko/wt and retina p8 ko/wt
  5. scn p15 ko/wt and retina p15 ko/wt
  6. #4 and #5 together

The comparisons of het/wt are found in the ‘inclusion_sig’ dataset; because they are providing our cutoffs for nonspecific binding.

27.1 Number 1 above: p08_het vs wt for scn and retina.

table_names <- c("p08_het_scn", "p08_het_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error:
## ! object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "37upset_locations_tx/rs_p08_het_inclusion_upset_groups.xlsx")
## Error:
## ! object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error:
## ! object 'inclusion_upsetr' not found
pp(file = "37upset_locations_tx/inclusion_sr_p08_upset.pdf")
## Warning in pp(file = "37upset_locations_tx/inclusion_sr_p08_upset.pdf"): The
## directory: 37upset_locations_tx does not exist, will attempt to create it.
print(inclusion_upsetr[["all_plot"]])
## Error:
## ! object 'inclusion_upsetr' not found
plotted <- dev.off()

27.2 Number 2 above: p15_het vs wt for scn and retina.

table_names <- c("p15_het_scn", "p15_het_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error:
## ! object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "37upset_locations_tx/rs_p15_het_inclusion_upset_groups.xlsx")
## Error:
## ! object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error:
## ! object 'inclusion_upsetr' not found
pp(file = "37upset_locations_tx/inclusion_sr_p15_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error:
## ! object 'inclusion_upsetr' not found
plotted <- dev.off()

27.3 Number 3 above: combination of #1 and #2

table_names <- c("p08_het_scn", "p08_het_retina", "p15_het_scn", "p15_het_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error:
## ! object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "37upset_locations_tx/rs_p08p15_het_inclusion_upset_groups.xlsx")
## Error:
## ! object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error:
## ! object 'inclusion_upsetr' not found
pp(file = "37upset_locations_tx/inclusion_sr_p08p15_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error:
## ! object 'inclusion_upsetr' not found
plotted <- dev.off()

27.4 Number 4 above p08_ko vs wt for scn and retina.

table_names <- c("p08_ko_scn", "p08_ko_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error:
## ! object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "37upset_locations_tx/rs_p08_ko_inclusion_upset_groups.xlsx")
## Error:
## ! object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error:
## ! object 'inclusion_upsetr' not found
pp(file = "37upset_locations_tx/inclusion_sr_p08_ko_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error:
## ! object 'inclusion_upsetr' not found
plotted <- dev.off()

27.5 Number 5 above p15_ko vs wt for scn and retina.

table_names <- c("p15_ko_scn", "p15_ko_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error:
## ! object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "37upset_locations_tx/rs_p15_ko_inclusion_upset_groups.xlsx")
## Error:
## ! object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error:
## ! object 'inclusion_upsetr' not found
pp(file = "37upset_locations_tx/inclusion_sr_p15_ko_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error:
## ! object 'inclusion_upsetr' not found
plotted <- dev.off()

27.6 Number 6 above: Combining #4 and #5

table_names <- c("p08_ko_scn", "p08_ko_retina", "p15_ko_scn", "p15_ko_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error:
## ! object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "37upset_locations_tx/rs_p08p15_ko_inclusion_upset_groups.xlsx")
## Error:
## ! object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error:
## ! object 'inclusion_upsetr' not found
pp(file = "37upset_locations_tx/inclusion_sr_p08p15_ko_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error:
## ! object 'inclusion_upsetr' not found
plotted <- dev.off()

28 GSVA

msigdb <- "reference/msigdb_v2024.1.Mm.db"
if (file.exists(msigdb)) {
  tx_h_gsva <- simple_gsva(tx_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "mh",
                           signatures = msigdb, id_source = "fdata",
                           required_id = "external_transcript_name")
  tx_h_gsva

  tx_h_gsva_sig <- get_sig_gsva_categories(
    tx_h_gsva, excel = "38msigdb_tx/gsva_sig_hallmark_categories.xlsx")
  tx_h_gsva_sig

  tx_m1_gsva <- simple_gsva(tx_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m1",
                            signatures = msigdb, id_source = "fdata",
                            required_id = "external_transcript_name")
  tx_m1_gsva
  tx_m1_gsva_sig <- get_sig_gsva_categories(
    tx_m1_gsva, excel = "38msigdb_tx/gsva_sig_positional_categories.xlsx")
  tx_m1_gsva_sig

  tx_m2_gsva <- simple_gsva(tx_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m2",
                            signatures = msigdb, id_source = "fdata",
                            required_id = "external_transcript_name")
  tx_m2_gsva
  tx_m2_gsva_sig <- get_sig_gsva_categories(
    tx_m2_gsva, excel = "38msigdb_tx/gsva_sig_curated_categories.xlsx")
  tx_m2_gsva_sig

  tx_m3_gsva <- simple_gsva(tx_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m3",
                            signatures = msigdb, id_source = "fdata",
                            required_id = "external_transcript_name")
  tx_m3_gsva
  tx_m3_gsva_sig <- get_sig_gsva_categories(
    tx_m3_gsva, excel = "38msigdb_tx/gsva_sig_regulatory_categories.xlsx")
  tx_m3_gsva_sig

  tx_m5_gsva <- simple_gsva(tx_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m5",
                            signatures = msigdb, id_source = "fdata",
                            required_id = "external_transcript_name")
  tx_m5_gsva
  tx_m5_gsva_sig <- get_sig_gsva_categories(
    tx_m5_gsva, excel = "38msigdb_tx/gsva_sig_ontology_categories.xlsx")
  tx_m5_gsva_sig

  tx_m8_gsva <- simple_gsva(tx_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m8",
                            signatures = msigdb, id_source = "fdata",
                            required_id = "external_transcript_name")
  tx_m8_gsva
  tx_m8_gsva_sig <- get_sig_gsva_categories(
    tx_m8_gsva, excel = "38msigdb_tx/gsva_sig_celltype_categories.xlsx")
  tx_m8_gsva_sig
}
## Error in `h()`:
## ! error in evaluating the argument 'se' in selecting a method for function 'simple_gsva': object 'tx_pairwise_input' not found

29 GSEA images

Up above I created a fairly large set of enrichment/GSEA analyses. Let us pull some of the most interesting results here and look at them.

Here are the specific queries from Rashmi:

  • Genotype (het vs ko):
    • P8 het and ko for Ret
    • SCN (P8 het vs KO SCN
    • P8 het vs KO Ret)
    • P15 het and ko for Ret
  • Location (somal vs axonal):
    • SR_P08_KO
    • SR_P08_Het
    • SR_P15_KO
    • SR_P15_Het
  • Time(p8vs p15):
    • t_het_Ret_ po8-p15
    • t_KO_Ret_C po8-p15
    • t_het_SCN_po8-p15
    • t_KO_SCN_po8-p15

29.1 Genotype

Let us take a moment and see for which contrasts I acquired results:

I need to make a little summary for clusterprofiler too so that I can easily see how many hits there are for each contrast.

summary(genotype_full_gp)
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'summary': object 'genotype_full_gp' not found
for (i in names(genotype_full_gp)) {
  print(i)
  print(genotype_full_gp[[i]][["num_hits"]])
}
## Error:
## ! object 'genotype_full_gp' not found
summary(genotype_full_cp)
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'summary': object 'genotype_full_cp' not found
for (i in names(genotype_full_cp)) {
  print(i)
  print(nrow(genotype_full_cp[[i]][["gse_go"]]))
}
## Error:
## ! object 'genotype_full_cp' not found

29.1.1 p8 het/ko for retina:

This contrast, even before filtering away the high-wt genes, only has 8 genes in the set of up and down genes combined. As a result, my function which performs gProfiler/clusterProfiler skips it, and also skips the p15 het/ko for retina samples.

29.1.2 p8 het/ko for scn:

This has a bunch more genes: 51 up and 128 down. Unfortunately, gProfiler sees no significant over-representation in the up category of genes. The down category has

The up/down sets from clusterProfiler have enrich_go, gse_go, and go_data to look at.

genotype_full_gp$kh_p08_scn_up$num_hits
## Error:
## ! object 'genotype_full_gp' not found
genotype_full_gp$kh_p08_scn_down$num_hits
## Error:
## ! object 'genotype_full_gp' not found
plots <- plot_enrichresult(genotype_full_gp$kh_p08_scn_down[["BP_enrich"]])
## Error:
## ! object 'genotype_full_gp' not found
plots[["dot"]]
## Error:
## ! object 'plots' not found
plots[["tree"]]
## Error:
## ! object 'plots' not found

Perhaps I should just ask the question: for which categories did I get results back?

summary(genotype_full_gp)
## Error in `h()`:
## ! error in evaluating the argument 'object' in selecting a method for function 'summary': object 'genotype_full_gp' not found

kh_p08_dlgn_up: No significant gProfiler results. kh_p15_dlgn_up: Significant BP, HP, KEGG, MF, REAC, TF kh_p08_scn_up: No significant gProfiler results. kh_p08_scn_down: Significant BP, MiRNA, MF, TF kh_p15_scn_down: Significant BP, MF

plots <- plot_enrichresult(genotype_full_gp[["kh_p15_dlgn_up"]][["BP_enrich"]])
## Error:
## ! object 'genotype_full_gp' not found
plots[["dot"]]
## Error:
## ! object 'plots' not found

29.2 Location

29.2.1 Scn vs retina ko, p08

plots <- plot_enrichresult(location_gp[["sr_p08_ko"]][["sr_p08_ko_up"]][["BP_enrich"]])
plots[["dot"]]
## NULL
plots <- plot_enrichresult(location_gp[["sr_p08_ko"]][["sr_p08_ko_down"]][["BP_enrich"]])
plots[["dot"]]
## NULL

29.2.2 scn vs retina, het, p08

Enriched groups: BP, KEGG, MF, TF, CC

summary(location_gp[["sr_p08_het"]][["sr_p08_het_up"]])
## Length  Class   Mode 
##      0   NULL   NULL
plots <- plot_enrichresult(location_gp[["sr_p08_het"]][["sr_p08_het_up"]][["BP_enrich"]])
plots[["dot"]]
## NULL
plots <- plot_enrichresult(location_gp[["sr_p08_het"]][["sr_p08_het_up"]][["CC_enrich"]])
plots[["dot"]]
## NULL
plots <- plot_enrichresult(location_gp[["sr_p08_het"]][["sr_p08_het_down"]][["BP_enrich"]])
plots[["dot"]]
## NULL
sr_p08_het_topn_gsea <- plot_topn_gsea(location_cp[[""]])
## Error in `if (nrow(gse) < topn) ...`:
## ! argument is of length zero

29.2.3 Scn vs retina ko, p15

plots <- plot_enrichresult(location_gp[["sr_p15_ko"]][["sr_p15_ko_up"]][["BP_enrich"]])
plots[["dot"]]
## NULL
plots <- plot_enrichresult(location_gp[["sr_p15_ko"]][["sr_p15_ko_down"]][["BP_enrich"]])
plots[["dot"]]
## NULL

29.2.4 scn vs retina, het, p15

plots <- plot_enrichresult(location_gp[["sr_p15_het"]][["sr_p15_het_up"]][["BP_enrich"]])
plots[["dot"]]
## NULL
plots <- plot_enrichresult(location_gp[["sr_p15_het"]][["sr_p15_het_down"]][["BP_enrich"]])
plots[["dot"]]
## NULL

29.3 Time

29.3.1 het retina

Ups: significant results for BP, MF, TF Downs: BP, MF, REAC, TF, WP

plots <- plot_enrichresult(time_gp[["t_het_retina"]][["t_het_retina_up"]][["BP_enrich"]])
plots[["dot"]]
## NULL
plots <- plot_enrichresult(time_gp[["t_het_retina"]][["t_het_retina_down"]][["BP_enrich"]])
plots[["dot"]]
## NULL

29.3.2 ko retina

Up: BP, MiRNA, MF Down: BP, MF, REAC, TF

plots <- plot_enrichresult(time_gp[["t_ko_retina"]][["t_ko_retina_up"]][["BP_enrich"]])
plots[["dot"]]
## NULL
plots <- plot_enrichresult(time_gp[["t_ko_retina"]][["t_ko_retina_down"]][["BP_enrich"]])
plots[["dot"]]
## NULL

29.3.3 het scn

Neither of the SCN gProfiler queries provided any results.

30 Bibliography

pander::pander(sessionInfo())
message(paste0("This is hpgltools commit: ", get_git_commit()))
message(paste0("Saving to ", savefile))
tmp <- sm(saveme(filename = savefile))
tmp <- loadme(filename = savefile)
LS0tCnRpdGxlOiAiQW5hbHlzZXMgb2YgdGhlIElQUkdDIHRyYW5zY3JpcHQgYWJ1bmRhbmNlcy4iCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgICAgIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOgogICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgICAgICBkZl9wcmludDogcGFnZWQKICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgIGZpZ19oZWlnaHQ6IDcKICAgICAgICBmaWdfd2lkdGg6IDcKICAgICAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgICAgICB3aWR0aDogMzAwCiAgICAgICAga2VlcF9tZDogZmFsc2UKICAgICAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OgogICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgICAgICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgZmlnX2hlaWdodDogNwogICAgICAgICAgZmlnX3dpZHRoOiA3CiAgICAgICAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgICAgICAgIGtlZXBfbWQ6IGZhbHNlCiAgICAgICAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICAgICAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgZm9udC1zaXplOiAxNnB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDE2cHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDE2cHgKfQpib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiAxNjAwcHg7Cn0KPC9zdHlsZT4KCmBgYHtyIG9wdGlvbnMsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoaHBnbHRvb2xzKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGVucmljaHBsb3QpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGdwcm9maWxlcjIpCnR0IDwtIHRyeShkZXZ0b29sczo6bG9hZF9hbGwoIn4vaHBnbHRvb2xzIikpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KAogIHByb2dyZXNzID0gVFJVRSwgdmVyYm9zZSA9IFRSVUUsIHdpZHRoID0gOTAsIGVjaG8gPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZXJyb3IgPSBUUlVFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOCwgZmlnLnJldGluYSA9IDIsCiAgb3V0LndpZHRoID0gIjEwMCUiLCBkZXYgPSAicG5nIiwKICBkZXYuYXJncyA9IGxpc3QocG5nID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKSkKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxMikpCnZlciA8LSAiMjAyNDA4IgpwcmV2aW91c19maWxlIDwtICIiCnZlciA8LSBmb3JtYXQoU3lzLkRhdGUoKSwgIiVZJW0lZCIpCgojI3RtcCA8LSBzbShsb2FkbWUoZmlsZW5hbWU9cGFzdGUwKGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iIiwgeD1wcmV2aW91c19maWxlKSwgIi12IiwgdmVyLCAiLnJkYS54eiIpKSkKcm1kX2ZpbGUgPC0gImlwcmdjX2FuYWx5c2VzXzIwMjQwOC5SbWQiCnNhdmVmaWxlIDwtIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLCByZXBsYWNlID0gIlxcLnJkYVxcLnh6IiwgeCA9IHJtZF9maWxlKQojIyBObyBvbmUgd2lsbCBldmVyIHJlYWQgdGhpcyBJIHN1c3BlY3QsIGJ1dCB0aGlzIGhhcyBfbm9fIExMTS1wcm9kdWNlZCB0ZXh0LgpgYGAKCiMgUHJpbWFyeSB0cmFuc2NyaXB0IHF1ZXN0aW9uIG9mIGludGVyZXN0CgpXaGVuIGNvbXBhcmluZyB0aGUgaGV0L2tvIGRhdGEsIGEgc3BlY2lmaWMgc2V0IG9mIGdlbmVzIHdhcyBvYnNlcnZlZDsKd2hhdCBhcmUgdGhlIHNldCBvZiB0cmFuc2NyaXB0cyBpY2x1ZGVkIGFtb25nIHRoZW0gb2YgaW50ZXJlc3Q/IFRoaXMKaXMgaW4gY29udHJhc3QgdG8gTmFqaWIncyBxdWVzdGlvbjogYXJlIHRoZXJlIHRyYW5zY3JpcHRzIG5vdCBpbmNsdWRlZAphbW9uZyB0aGUgZ2VuZXMgb2JzZXJ2ZWQuCgpMaWtlbHkgYW5ub3RhdGlvbiBjb2x1bW5zOiBlbnNlbWJsX2dlbmVfaWQsIGVuc2VtYmxfdHJhbnNjcmlwdF9pZCwKZW5zZW1ibF9wZXB0aWRlX2lkLCBlbnNlbWJsX2V4b25faWQsIGRlc2NyaXB0aW9uLCBjaHJvbW9zb21lLAp0cmFuc2NyaXB0X3N0YXJ0LCB0cmFuc2NyaXB0X2VuZCwgdHJhbnNjcmlwdF9sZW5ndGgsIHN0cmFuZCwKZXh0ZXJuYWxfZ2VuZV9uYW1lLCBleHRlcm5hbF90cmFuc2NyaXB0X25hbWUsIGV4dGVybmFsX3N5bm9ueW0sCnBoZW5vdHlwZV9kZXNjcmlwdGlvbiwgcmVmc2VxX21ybmEuCgojIFNldCBzb21lIHBhcmFtZXRlcnMgd2hpY2ggd2lsbCBiZSB1c2VkIGxhdGVyCgpgYGB7cn0KIyMgQWRqdXN0ZWQgcC12YWx1ZSBjdXRvZmYgd2hlbiB3b3JraW5nIG9uICdub3JtYWwnIGNvbnRyYXN0cy4KYWRqcF9jdXRvZmYgPC0gMC4wNQojIyBBZGp1c3RlZCBwLXZhbHVlIGN1dG9mZiB3aGVuIHdvcmtpbmcgb24gdGhlIGluY2x1c2lvbiBzZXRzLgpmaWx0ZXJfYWRqcF9jdXRvZmYgPC0gMC4xCmRlZmF1bHRfZnN0cmluZyA8LSAifiAwICsgY29uZGl0aW9uIgojIyBBbGxvdyAxMCBHTyBjYXRlZ29yaWVzIHRvIGJlIGRpc3BsYXllZCB3aGVuIHBsb3R0aW5nLgpnb19jYXRlZ29yaWVzIDwtIDE0CiMjIEFsbG93IGdyb3VwcyBoaWdoZXIgdXAgaW4gdGhlIHRyZWUgZm9yIGNsdXN0ZXJQcm9maWxlciByZXN1bHRzLgpnb19sZXZlbCA8LSAyCnR4X2xhYmVsX2NvbHVtbiA8LSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiAjIyBTZXQgdGhlIGNvbHVtbiB1c2VkIHRvIGV4dHJhY3QgZ2VuZSBzeW1ib2xzIHJhdGhlciB0aGFuIEVOU0cuLi4uLgpnZW5lX2xhYmVsX2NvbHVtbiA8LSAiZXh0ZXJuYWxfc3lub255bSIKIyMgTG9nRkMgY3V0b2ZmIHdoZW4gd29ya2luZyBvbiB0aGUgaW5jbHVzaW9uIHNldHMuCmZpbHRlcl9sZmNfY3V0b2ZmIDwtIDAuMQojIyBMRkMgY3V0b2ZmIGZvciAnbm9ybWFsJyBjb250cmFzdHMuCmxmY19jdXRvZmYgPC0gMS4wCiMjIEluY3JlYXNlIHRoZSBtYXhpbXVtIGFsbG93ZWQgZ3JvdXAgc2l6ZSB3aGVuIHdvcmtpbmcgd2l0aCBjbHVzdGVyUHJvZmlsZXIKIyMgVGhpcyBzaG91bGQgYnJpbmcgb3V0IHNvbWUgb2YgdGhlIG1vcmUgZ2VuZXJhbCBncm91cHMgbGlrZSAnY2VsbGJvZHknCm1heF9ncm91cHNpemUgPC0gMjAwMAojIyBTcGVlZCB1cCBjbHVzdGVyUHJvZmlsZXIgYnkgY2hvb3NpbmcgdGhlIGNvcnJlY3Qga2V5dHlwZXMKb3JnZGJfZnJvbSA8LSAiRU5TRU1CTFRSQU5TIgojIyBNQSBwbG90IHBvaW50IG91dGxpbmVzCm91dGxpbmUgPC0gRkFMU0UKc2FtcGxlX3NoZWV0IDwtICJzYW1wbGVfc2hlZXRzLzIwMjQwNjA2X29ubHlfdW1kX3NlcXVlbmNlZF9tb2RpZmllZC54bHN4IgojYW5ub3RhdGlvbl9jb2x1bW5zIDwtIGMoCiMgICJlbnNlbWJsX2dlbmVfaWQiLCAiZW5zZW1ibF90cmFuc2NyaXB0X2lkIiwiZW5zZW1ibF9wZXB0aWRlX2lkIiwgImVuc2VtYmxfZXhvbl9pZCIsCiMgICJkZXNjcmlwdGlvbiIsICJjaHJvbW9zb21lX25hbWUiLCAidHJhbnNjcmlwdF9zdGFydCIsICJ0cmFuc2NyaXB0X2VuZCIsICJ0cmFuc2NyaXB0X2xlbmd0aCIsCiMgICJzdHJhbmQiLCAiZXh0ZXJuYWxfZ2VuZV9uYW1lIiwgImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsICJleHRlcm5hbF9zeW5vbnltIiwKIyAgInBoZW5vdHlwZV9kZXNjcmlwdGlvbiIsICJyZWZzZXFfbXJuYSIpCmFubm90YXRpb25fY29sdW1ucyA8LSBjKCJlbnNlbWJsX2dlbmVfaWQiLCAiZW5zZW1ibF90cmFuc2NyaXB0X2lkIiwgImRlc2NyaXB0aW9uIiwgImV4dGVybmFsX3N5bm9ueW0iLCAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIikKYGBgCgojIEludHJvZHVjdGlvbgoKVGhpcyBkb2N1bWVudCBpcyBhIGNvcHkgb2YgdGhlIGlwcmdjX2FuYWx5c2VzXzIwMjQwOC5SbWQgZG9jdW1lbnQsCndpdGggYSBmZXcgaW1wb3J0YW50IGRpZmZlcmVuY2VzOgoKMS4gIEkgd2lsbCBub3QgYmUgc3BlbmRpbmcgdGltZSB0byBjb21wYXJlIHRoZSB2YXJpb3VzIChkZSlkdXBsaWNhdGlvbgogICAgbWV0aG9kcy9yZXN1bHRzLgoyLiAgVGhpcyB3aWxsIGNyZWF0ZSwgdmlzdWFsaXplLCBhbmQgYW5hbHl6ZSB0cmFuc2NyaXB0LWxldmVsCiAgICBhYnVuZGFuY2VzIHJhdGhlciB0aGFuIGdlbmVzLgozLiAgSSBoYXZlIG5vdCB5ZXQgZGVjaWRlZCwgYnV0IEkgbWlnaHQgcGxheSB3aXRoIHRoZSB3dCBmaWx0ZXJpbmcgdG8KICAgIHRyeSB0byBtYWtlIGl0IGNsZWFyZXIuCjQuICBJIHdpbGwgbGlrZWx5IHJlbW92ZSBzb21lIGNvbW1lbnRzIGFuZCB0ZXh0IGRlc2NyaWJpbmcgd2hhdAogICAgaXMgZ29pbmcgb24uCjUuICBBbGwgb3V0cHV0IGRpcmVjdG9yaWVzIGdldCBhIHN1ZmZpeCAnX3R4JwoKV2l0aCB0aGF0IGluIG1pbmQsIHRoZSBnb2FsIGlzIHRvIHJlcGVhdCBvdXIgZ2VuZS1sZXZlbCBhbmFseXNlcywgYnV0CnVzaW5nIHRoZSB0cmFuc2NyaXB0IHF1YW50aWZpY2F0aW9ucyBkZXJpdmVkIGZyb20gZmVhdHVyZUNvdW50cy4gIFRoaXMKaXMgaW4gcmVzcG9uc2UgdG8gYSByZXZpZXcgd2hvIHN1Z2dlc3RlZCB0aGF0IHNvbWUgZ2VuZXMgaGF2ZQpwb3RlbnRpYWxseSBpbnRlcmVzdGluZyBhbmQgdmFyaWFibGUgdHJhbnNjcmlwdHMuICBUaGF0IGlzIGEgc2ltcGxlCmVub3VnaCBxdWVyeSB0byBhZGRyZXNzLCBzbyBsZXQgdXMgc2VlIHdoYXQgd2UgZ2V0IQoKIyBBIG5vdGUgYWJvdXQgaW1wbGVtZW50YXRpb24KCkkgd291bGQgbGlrZSB0byBpbXByb3ZlIHRoaXMgZG9jdW1lbnQgYnkgY29tcGFyaW5nL2NvbnRyYXN0aW5nIHRoZQptZXRob2RvbG9naWVzIHBlcmZvcm1lZCBieSBvdGhlciBncm91cHMgYW5kIHRob3NlIHBlcmZvcm1lZCBieSBtZSBpbgppdC4gIEkgbmV2ZXIgZnVsbHkgYXBwcmVjaWF0ZWQgdGhlIHN1aXRlIG9mIGNvbXB1dGF0aW9uYWwgbWV0aG9kcwphcHBsaWVkIGJ5IHByZXZpb3VzIGdyb3VwcyB3aGVuIGV4YW1pbmluZyBUUkFQIGRhdGE7IEkgaW5zdGVhZCBzaW1wbHkKZm9sbG93ZWQgVGhlcmVzYSdzIG5vdGVib29rIHdpdGhvdXQgY29uc2lkZXJpbmcgb3RoZXIgcG9zc2liaWxpdGllcy4KCkkgdGhlcmVmb3JlIHNwZW50IGEgbGl0dGxlIHRpbWUgc3RlcHBpbmcgdGhyb3VnaCBoZXIgdGhlc2lzIGFuZApwdWxsaW5nIG91dCB0aGUgcmVsZXZhbnQgcGFwZXJzIGluIHRoZSBob3BlcyBvZiBsZWFybmluZyB0aGVzZSB2YXJpb3VzCm1ldGhvZHMuICBJIHNob3VsZCB0aGVyZWZvcmUgYmUgYWJsZSBzb29uIHRvIGNvbXBhcmUvY29udHJhc3QgdGhlCnZhcmlvdXMgbWV0aG9kcyBlbXBsb3llZCBieSBvdGhlciBsYWJzIGluIGFkZGl0aW9uIHRvIGNvcHlpbmcKVGhlcmVzYSdzIGxvZ2ljLgoKIyMgVGhlIGZvbGxvd2luZyBibG9jayBjYW5ub3Qgd29yayBpbiB0aGUgY29udGFpbmVyCgpUaGUgZm9sbG93aW5nIGJsb2NrIGFzc3VtZXMgdGhlIGZ1bGwgdHJlZSBvZiBwcmVwcm9jZXNzZWQgZGF0YSB3aXRoCnRoZSBsb2dzIGZyb20gdGhlIHRyaW1tZXIsIG1hcHBpbmcsIHVtaSBkZWR1cGxpY2F0aW9uLCBjb3VudGluZywgZXRjLgpBcyBhIHJlc3VsdCBpdCBjYW5ub3Qgd29yayBpbiB0aGUgY29udGFpbmVyIHdoaWNoIGhhcyBvbmx5IHRoZSB2YXJpb3VzCmNvdW50IHRhYmxlcy4KCkFzIGEgcmVzdWx0LCBJIGFtIGluY2x1ZGluZyBhIGNvcHkgb2YgdGhpcyBzaGVldCBhZnRlciBydW5uaW5nIHRoZQpmb2xsb3dpbmcgYmxvY2sgaW4gbXkgd29ya2luZyB0cmVlLiAgSSBzdXBwb3NlIGZvciB0aGUgbW9tZW50IHlvdSB3aWxsCmhhdmUgdG8gdHJ1c3QgdGhhdCBpdCB3b3JrZWQuICAoZm9yIHJpZ2h0IG5vdywgd2hlbiB0ZXN0aW5nIG91dCB0aGlzCmNvbnRhaW5lciwgSSBhbSBqdXN0IHNlbmRpbmcgdGhlIFIgd29ya2luZyBkaXJlY3RvcnkgdG8gbXkgdHJlZSBmb3IKdGhpcyBibG9jaywgdGhlbiBtb3ZpbmcgaXQgYmFjay4KCkkgd2lsbCBuZWVkIHRvIG1hbnVhbGx5IGVkaXQgb25lIGNvbHVtbiB0aG91Z2gsIHRoZSBzeW1saW5rIGNvbHVtbgpmcm9tIFRoZXJlc2EgaGFzIGEgc2VyaWVzIG9mIHBhdGhzIHdoaWNoIGRvIG5vdCB3b3JrIGluIHRoZSBjb250YWluZXIuCgoKYGBge3IsIGV2YWw9RkFMU0V9CnVtaV9zcGVjIDwtIG1ha2Vfcm5hc2VxX3NwZWModW1pID0gVFJVRSkKaXByZ2NfMjAyMl9tZXRhIDwtIGdhdGhlcl9wcmVwcm9jZXNzaW5nX21ldGFkYXRhKCJzYW1wbGVfc2hlZXRzLzIwMjQwNjA2X29ubHlfdW1kX3NlcXVlbmNlZC54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWMgPSB1bWlfc3BlYywgc3BlY2llcyA9ICJtbTM5XzExMiIsIHZlcmJvc2UgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFzZWRpciA9ICJwcmVwcm9jZXNzaW5nL3VtZF9zZXF1ZW5jZWQiKQpjb2xuYW1lcyhpcHJnY18yMDIyX21ldGFbWyJuZXdfbWV0YSJdXSkKaGVhZChpcHJnY18yMDIyX21ldGFbWyJuZXdfbWV0YSJdXSkKYGBgCgpgYGB7cn0KbXNpZ2RiIDwtICJyZWZlcmVuY2UvbXNpZ2RiX3YyMDI0LjEuTW0uZGIiCm1zaWdfZGF0YSA8LSBOVUxMCm1ha2VfdHJhbnNwYXJlbnQgPC0gZnVuY3Rpb24oKSB7CiAgZ2dwbG90Mjo6dGhlbWUoCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAndHJhbnNwYXJlbnQnKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3RyYW5zcGFyZW50JywgY29sb3IgPSBOQSksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAndHJhbnNwYXJlbnQnKSwKICAgIGxlZ2VuZC5ib3guYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3RyYW5zcGFyZW50JykpCn0KYGBgCgojIENvbGxlY3QgdGhlIGNvdW50IHRhYmxlcwoKSSB1c2VkIHNhbG1vbi9mZWF0dXJlY291bnRzL2V0YyB3aXRoIENEUy9tUk5BIGZlYXR1cmVzIG9uIG91ciBjb21wdXRlCmNsdXN0ZXIgYW5kIHVzZWQgdGhlIGFib3ZlIGdhdGhlciBmdW5jdGlvbiB0byBhZGQgYSBjb2x1bW4gdG8gdGhlCm1ldGFkYXRhIGNvbnRhaW5pbmcgdGhlIGZpbGVuYW1lcyBwcm9kdWNlZC4gIFRoZSBmb2xsb3dpbmcgcmVhZHMgdGhhdApjb2x1bW4gYW5kIGNyZWF0ZXMgYSB0YXJiYWxsIG9mIHRoZSBmaWxlcy4gIE9uZSB0aGluZyBJIG5lZWQgdG8gZG86Cm1vZGlmeSB0aGF0IHRvIGFjY2VwdCBhIHZldG9yIG9mIGNvbHVtbnMgc28gSSBjYW4gZWFzaWx5IGNvbGxlY3QgcjErcjIKYW5kIHN0dWZmIGxpa2UgdGhhdC4KCkFzIHRoZSBuYW1lIHN1Z2dlc3RzLCB0aGVzZSBhcmUgbVJOQSBjb3VudHMgZm9sbG93aW5nIHRoZQpkZWR1cGxpY2F0aW9uIG1hcmtpbmcgYW5kIHJlbW92YWwgZnJvbSB0aGUgaW5pdGlhbCBoaXNhdCBhbGlnbm1lbnRzCnZpYSB1bWl0b29scy4KCmBgYHtyLCBldmFsPUZBTFNFfQojIyBhbGwgdGhlIGlucHV0IGZpbGVzIGFyZSBhbHJlYWR5IHh6IGNvbXByZXNzZWQKbXJuYV9jb3VudHMgPC0gdGFyX21ldGFfY29sdW1uKHNhbXBsZV9zaGVldCwgY29sdW1uID0gInVtaV9kZWR1cF9tcm5hX2NvdW50IiwgY29tcHJlc3MgPSBOVUxMKQpgYGAKCkkgd2lsbCBmaWd1cmUgb3V0IGlmIEkgY2FuIGxlYXZlIG1TaWdEQiBNMiBpbiB0aGlzIGltYWdlOyBpZiBub3QsIHRoZW4KYW55IGFuYWx5c2VzIGRlcGVuZGluZyBvbiB0aG9zZSBnZW5lIHNldHMgd2lsbCBmYWlsLgoKYGBge3J9Cm0yX2dzYyA8LSB0cnkobG9hZF9nbXRfc2lnbmF0dXJlcyhzaWduYXR1cmVzID0gbXNpZ2RiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlX2NhdGVnb3J5ID0gIk0yIiksIHNpbGVudCA9IFRSVUUpCiMjIEkgZG8gbm90IHRoaW5rIEkgaGF2ZSBwZXJtaXNzaW9uIHRvIGxvYWQgdGhlIG1zaWdkYiBpbiB0aGUgY29udGFpbmVyCiMjIFNvLCBpZiB0aGlzIGZhaWxzLCBqdXN0IGxvYWQgaXQgZnJvbSBHU1ZBRGF0YSwgb2ggd2FpdCBubywgR1NWQWRhdGEgaXMgaHVtYW4uCmlmICgidHJ5LWVycm9yIiAlaW4lIGNsYXNzKG0yX2dzYykpIHsKICB3YXJuaW5nKCJVbmFibGUgdG8gbG9hZCB0aGUgTTIgTXNpZ0RCIGRhdGEuIikKfQpgYGAKCiMgQW5ub3RhdGlvbiBkYXRhCgpJIGFtIHB1bGxpbmcgdGhpcyBmcm9tIFRoZXJlc2EncyBhbnhvbnRyYXBSX3BpcGVsaW5lLlJtZCwgcHJpbWFyaWx5CmJlY2F1c2UgaXQgbG9va3Mgc2ltaWxhciB0byB0aGUgb3RoZXIgZG9jdW1lbnRzLCBidXQgd2FzIG1vZGlmaWVkIG1vcmUKcmVjZW50bHkuICBJIHdpbGwgY2hhbmdlIGl0IHNsaWdodGx5LCBwcmltYXJpbHkgYmVjYXVzZSBJIGdyYWJiZWQgYQpuZXcgbW11c2N1bHVzIGFzc2VtYmx5IGFuZCB0aGVyZWZvcmUgSSB3aWxsIHB1bGwgdGhlIG1tdXNjdWx1cwphbm5vdGF0aW9ucyBmcm9tIGEgc3BlY2lmaWMgYmlvbWFydAooQHNtZWRsZXlCaW9NYXJ0QmlvbG9naWNhbFF1ZXJpZXMyMDA5KSBhcmNoaXZlIHRoYXQgc2hvdWxkIG1hdGNoIGl0LgoKQSBub3RlIGZyb20gdGhlIGZ1dHVyZTogbXVsdGlwbGUgZW5zZW1ibCBhcmNoaXZlIHNlcnZlcnMgaGF2ZSBiZWVuCnRha2VuIG9mZmxpbmUgc2luY2UgbGFzdCBJIHJhbiB0aGlzLiAgTGV0IHVzIHNlZSBpZiBGZWIuIDIwMjMgc3RpbGwKd29ya3MuCgojIyBBbiBpbXBvcnRhbnQgbm90ZSEKCkluIHRoZSByZWNlbnQgcGFzdCwgZW5zZW1ibCBxdWVyaWVzIGhhdmUgYmVjb21lIGluY29uc2lzdGVudCwgZmFpbGluZwptdWNoIG1vcmUgb2Z0ZW4gdGhhbiBldmVyIGluIHRoZSBwYXN0LiAgSSBkbyBub3QgdGhpbmsgdGhpcyBpcyB0aGUKZmF1bHQgb2YgZW5zZW1ibDsgYnV0IEkgdGhpbmsgSSBuZWVkIGEgZmFsbGJhY2sgbWVjaGFuaXNtIGZvcgpjb2xsZWN0aW5nIGFubm90YXRpb24gaW5mb3JtYXRpb24uCgpJbiB0aGUgY2FzZSBvZiBlbnNlbWJsLCBpdCBzaG91bGQgYmUgdHJpdmlhbCAoYnV0IGxlc3MgZnVuKSB0byB1c2UgYQpjb21iaW5hdGlvbiBvZiB0aGUgbG9jYWxseSBpbnN0YWxsZWQgb3JnZGIgYW5kIHR4ZGIgZGF0YWJhc2VzLgoKVGhpcyBkb2VzIG9wZW4gYSByaXNrIHRoYXQgdGhlIHNldCBvZiBnZW5lcyB3aXRoIGFubm90YXRpb25zIHdpbGwgYmUKZGlmZmVyZW50IGRlcGVuZGluZyBvbiB3aGVuIHRoZSBjb250YWluZXIgaXMgcnVuIGR1ZSB0byBkaWZmZXJlbmNlcwpiZXR3ZWVuIHRoZSBvcmdkYi90eGRiIGluc3RhbmNlIGFuZCB0aGUgRmViIDIwMjMgYmlvbWFydC4gIEkgYW0gbm90CnN1cmUgdGhlcmUgaXMgbXVjaCBJIGNhbiBkbyBhYm91dCB0aGF0IGV4Y2VwdCB0byBidW5kbGUgdGhlIHNldCBvZgphbm5vdGF0aW9ucyBJIGRvd25sb2FkZWQgaW4gdGhlIGNvbnRhaW5lciAtLSBzaW5jZQpsb2FkX2Jpb21hcnRfYW5ub3RhdGlvbnMoKSBkb2VzIHNhdmUgYSByZGEgY29weSBvZiBpdHMgZG93bmxvYWQuCgpvaywgSSBkaWQgYm90aC4gIElmIHlvdSwgZGVhciByZWFkZXIsIHdpc2ggdG8gZG93bmxvYWQgeW91ciBvd24KYW5ub3RhdGlvbnMsIGFuZCBlbnNlbWJsIGlzIGhhdmluZyB0cm91YmxlcywgdGhlIGZvbGxvd2luZyBzaG91bGQgd29yawp3aXRob3V0IGEgcHJvYmxlbTsgaW4gYWRkaXRpb24gdGhlIHJkYSBhbm5vdGF0aW9ucyBhcmUgaW4gL2RhdGEgb2YgdGhlCmNvbnRhaW5lciBhbmQgc2hvdWxkIGdldCBsb2FkZWQuCgpgYGB7cn0KdHhfZ2VuZV9tYXAgPC0gZGF0YS5mcmFtZSgpCm1tX2Fubm90IDwtIHRyeShsb2FkX2Jpb21hcnRfYW5ub3RhdGlvbnMoc3BlY2llcyA9ICJtbXVzY3VsdXMiLCBob3N0ID0gInVzZWFzdC5lbnNlbWJsLm9yZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJjaGl2ZSA9IEZBTFNFLCBvdmVyd3JpdGUgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3JlcXVlc3RzID0gYW5ub3RhdGlvbl9jb2x1bW5zKSkKZmllbGRzIDwtIGMoIkFDQ05VTSIsICJFTlNFTUJMIiwgIkVOU0VNQkxUUkFOUyIsICJFTlRFWklEIiwgIkdFTkVOQU1FIiwgIlNZTUJPTCIpCm9yZ2RiX2Fubm90IDwtIGxvYWRfb3JnZGJfYW5ub3RhdGlvbnMoIm9yZy5NbS5lZy5kYiIsIGZpZWxkcyA9IGZpZWxkcykKZ2VuZV9pbmZvIDwtIG9yZ2RiX2Fubm90W1siZ2VuZXMiXV0KCiMjIE5vdGUsIHRoZXJlIGFyZSBhIGJ1bmNoIG9mIHZhcmlhbnRzIG9mIHRoZSB0eGRiIHBhY2thZ2Ugb25lIG1pZ2h0IHVzZS4KIyMgSSBkbyBub3QgdGhpbmsgaXQgbWF0dGVycyBhIGxvdCBmb3Igb3VyIHB1cnBvc2VzLCBidXQgSSBzdXNwZWN0IHRoYXQgaWYgd2UgdXNlZAojIyBhIG1pc21hdGNoZWQgQlNnZW5vbWUgYW5kIHRyaWVkIHRvIHB1bGwgQ0RTIHNlcXVlbmNlcywgdGhhdCBtaWdodCBlbmQgYmFkbHkuCnBrZyA8LSAiVHhEYi5NbXVzY3VsdXMuVUNTQy5tbTEwLmtub3duR2VuZSIKdHhfYW5ub3QgPC0gbG9hZF90eGRiX2Fubm90YXRpb25zKHBrZykKdHJhbnNjcmlwdHMgPC0gdHhfYW5ub3RbWyJUWCJdXQp0cmFuc2NyaXB0c1tbInR4Il1dIDwtIGdzdWIoeCA9IHRyYW5zY3JpcHRzW1siVFhOQU1FIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJcXC5cXGQrJCIsIHJlcGxhY2VtZW50ID0gIiIpCm1tX2Fubm90X29yZ2RiIDwtIG1lcmdlKGdlbmVfaW5mbywgdHJhbnNjcmlwdHMsIGJ5LnggPSAiZW5zZW1ibHRyYW5zIiwgYnkueSA9ICJ0eCIpCnJvd25hbWVzKG1tX2Fubm90X29yZ2RiKSA8LSBtYWtlLm5hbWVzKG1tX2Fubm90X29yZ2RiW1siZW5zZW1ibCJdXSwgdW5pcXVlID0gVFJVRSkKCm1tX2Fubm90X2NvbWJpbmVkIDwtIG1lcmdlKG1tX2Fubm90W1siYW5ub3RhdGlvbiJdXSwgbW1fYW5ub3Rfb3JnZGIsIGJ5LnggPSAiZW5zZW1ibF90cmFuc2NyaXB0X2lkIiwgYnkueSA9ICJlbnNlbWJsdHJhbnMiLCBhbGwueCA9IFRSVUUpCgptbV9hbm5vdF9nZW5lcyA8LSBtbV9hbm5vdF9jb21iaW5lZApyb3duYW1lcyhtbV9hbm5vdF9nZW5lcykgPC0gbWFrZS5uYW1lcyhtbV9hbm5vdF9nZW5lc1tbImVuc2VtYmxfZ2VuZV9pZCJdXSwgdW5pcXVlID0gVFJVRSkKCm1tX2Fubm90X3R4IDwtIG1tX2Fubm90X2NvbWJpbmVkCnJvd25hbWVzKG1tX2Fubm90X3R4KSA8LSBtYWtlLm5hbWVzKG1tX2Fubm90X3R4W1siZW5zZW1ibF90cmFuc2NyaXB0X2lkIl1dLCB1bmlxdWUgPSBUUlVFKQoKdHhfZ2VuZV9tYXAgPC0gbW1fYW5ub3RfZ2VuZXNbLCBjKCJUWElEIiwgImVuc2VtYmxfZ2VuZV9pZCIpXQpgYGAKCiMgSGlzYXQyIHN1bW1hcml6ZWRFeHBlcmltZW50cyBieSB0cmFuc2NyaXB0CgojIyBDb2xvciBjaG9pY2VzIGFuZCByZXVzZWQgcGFyYW1ldGVycwoKYGBge3J9CmNvbG9yX2Nob2ljZXMgPC0gbGlzdCgKICAiYWxsIiA9IGxpc3QoCiAgICAicDA4X2hldF9kbGduIiA9ICIjRTcyOThBIiwKICAgICJwMTVfaGV0X2RsZ24iID0gIiNFNzI5OEEiLAogICAgInAwOF9oZXRfcmV0aW5hIiA9ICIjMjM4QjQ1IiwKICAgICJwMTVfaGV0X3JldGluYSIgPSAiIzIzOEI0NSIsCiAgICAicDA4X2hldF9zY24iID0gIiM0MjkyQzYiLAogICAgInAxNV9oZXRfc2NuIiA9ICIjNDI5MkM2IiwKICAgICJwMDhfa29fZGxnbiIgPSAiI0M5OTRDNyIsCiAgICAicDE1X2tvX2RsZ24iID0gIiNDOTk0QzciLAogICAgInAwOF9rb19yZXRpbmEiID0gIiM3NGM0NzYiLAogICAgInAxNV9rb19yZXRpbmEiID0gIiM3NGM0NzYiLAogICAgInAwOF9rb19zY24iID0gIiM5QkNBRTEiLAogICAgInAxNV9rb19zY24iID0gIiM5QkNBRTEiLAogICAgInAwOF93dF9kbGduIiA9ICIjOTgwMDQzIiwKICAgICJwMTVfd3RfZGxnbiIgPSAiIzk4MDA0MyIsCiAgICAicDA4X3d0X3JldGluYSIgPSAiIzAwNDAwOCIsCiAgICAicDE1X3d0X3JldGluYSIgPSAiIzAwNDAwOCIsCiAgICAicDA4X3d0X3NjbiIgPSAiIzA4NTE5QyIsCiAgICAicDE1X3d0X3NjbiIgPSAiIzA4NTE5QyIsCiAgICAicDYwX3d0X2RsZ24iID0gIiMzMzMzMzMiLAogICAgInA2MF93dF9yZXRpbmEiID0gIiMyMjIyMjIiLAogICAgInA2MF93dF9zY24iID0gIiMxMTExMTEiKSwKICAiZ2Vub19sb2MiID0gbGlzdCgKICAgICJoZXRfZGxnbiIgPSAiI0U3Mjk4QSIsCiAgICAiaGV0X3JldGluYSIgPSAiIzIzOEI0NSIsCiAgICAiaGV0X3NjbiIgPSAiIzQyOTJDNiIsCiAgICAia29fZGxnbiIgPSAiI0M5OTRDNyIsCiAgICAia29fcmV0aW5hIiA9ICIjNzRjNDc2IiwKICAgICJrb19zY24iID0gIiM5QkNBRTEiLAogICAgInd0X2RsZ24iID0gIiM5ODAwNDMiLAogICAgInd0X3JldGluYSIgPSAiIzAwNDAwOCIsCiAgICAid3Rfc2NuIiA9ICIjMDg1MTlDIiksCiAgImxvY2F0aW9uIiA9IGxpc3QoCiAgICAicmV0aW5hIiA9ICIjMDA0MDA4IiwKICAgICJkbGduIiA9ICIjOTgwMDQzIiwKICAgICJzY24iID0gIiMwODUxOUMiKSwKICAiZ2Vub3R5cGUiID0gbGlzdCgKICAgICJ3dCIgPSAiIzc0YzQ3NiIsCiAgICAiaGV0IiA9ICIjMjM4QjQ1IiwKICAgICJrbyIgPSAiIzAwNkQyQyIpLAogICJ0aW1lIiA9IGxpc3QoCiAgICAicDA4IiA9ICIjNUUxMDRCIiwKICAgICJwMTUiID0gIiM0RTkyMzEiKSkKY29sb3JzIDwtIGNvbG9yX2Nob2ljZXNbWyJnZW5vX2xvYyJdXQp0aW1lX2NvbG9ycyA8LSBsaXN0KAogICJwMDhfaGV0X2RsZ24iID0gIiNFNzI5OEEiLAogICJwMTVfaGV0X2RsZ24iID0gIiM4YTE4NTIiLAogICJwMDhfaGV0X3JldGluYSIgPSAiIzIzOEI0NSIsCiAgInAxNV9oZXRfcmV0aW5hIiA9ICIjMTU1MzI5IiwKICAicDA4X2hldF9zY24iID0gIiM0MjkyQzYiLAogICJwMTVfaGV0X3NjbiIgPSAiIzI3NTc3NiIsCiAgInAwOF9rb19kbGduIiA9ICIjQzk5NEM3IiwKICAicDE1X2tvX2RsZ24iID0gIiM3ODU4NzciLAogICJwMDhfa29fcmV0aW5hIiA9ICIjNzRDNDc2IiwKICAicDE1X2tvX3JldGluYSIgPSAiIzQ1NzU0NiIsCiAgInAwOF9rb19zY24iID0gIiM5QkNBRTEiLAogICJwMTVfa29fc2NuIiA9ICIjNWQ3OTg3IikKYGBgCgojIyBUaGUgVHJhbnNjcmlwdCBhYnVuZGFuY2VzCgpUaGlzIHBlcmhhcHMgZGVzZXJ2ZXMgYSBsaXR0bGUgYml0IG9mIGV4cGxhbmF0aW9uOiB0aGUgcHJvY2VzcyBmb3IKdXNpbmcgVU1JcyBnb2VzIGFzIGZvbGxvd3M6CgoxLiAgRXhhbWluZSBhbGwgdGhlIHJhdyByZWFkcywgZXh0cmFjdCB0aGUgVU1JIElEcywgYW5kIGFkZCB0aGVtIGFzCiAgICBjb21tZW50cyB0byB0aGUgZmFzdHEgZmlsZSAob3IgcmV3cml0ZSB0aGUgcmVhZCBJRCkKMi4gIEFsaWduIGFsbCByZWFkcyBhZ2FpbnN0IHRoZSBnZW5vbWUuCjMuICBUYWtlIHRoZSByZXN1bHRpbmcgYmFtIGZpbGUgYW5kIGZpbmQgcmVhZHMgYXQgdGhlIHNhbWUgbG9jYXRpb24KICAgIHdpdGggdGhlIHNhbWUgSUQocykgYW5kIGZpbHRlciB0aGUgYWxpZ25tZW50IHNvIHRoYXQgdGhlcmUgcmVtYWlucwogICAgb25seSAxIHJlcHJlc2VudGF0aXZlIGZyb20gZWFjaCBJRC4KNC4gIENvdW50IHVwIHRoZSBmZWF0dXJlcyBvZiBpbnRlcmVzdC4KCkkgdGhlcmVmb3JlIGludm9rZWQgZmVhdHVyZUNvdW50cyBvbiB0aGUgdW1pIGRlZHVwbGljYXRlZCBiYW0KYWxpZ25tZW50cyAoZm91bmQgaW4gY29sdW1uICd1bWlfZGVkdXBfb3V0cHV0X2JhbScgQlAgaW4gbXkgY29weSBvZgp0aGUgYXV0b21hZ2ljYWxseSBnZW5lcmF0ZWQgc2FtcGxlIHNoZWV0KSB1c2luZyB0aGUgcmVxdWlzaXRlIG1tMzggZ2ZmCmZpbGUgd2l0aCB0cmFuc2NyaXB0IGFzIHRoZSBmZWF0dXJlIHR5cGUgYW5kIElEIGFzIHRoZSB0YWcuCgpPaCwgdGhhdCByZW1pbmRzIG1lLCBJIG5lZWQgdG8gcmUta2V5IHRoZSBhbm5vdGF0aW9ucyB0byB1c2UgdGhhdCBhbmQKSSBtaWdodCBuZWVkIHRvIGNoZWNrIHRoYXQgdGhlIElEcyBkbyBvciBkbyBub3QgaGF2ZSBhIHByZWZpeCBsaWtlCid0cmFuc2NyaXB0Oicgb3Igd2hhdGV2ZXIuLi4KCmBgYHtyfQptbTM4X2hpc2F0X3R4IDwtIGNyZWF0ZV9zZShzYW1wbGVfc2hlZXQsIGdlbmVfaW5mbyA9IG1tX3R4X2Fubm90LAogICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlX2NvbHVtbiA9ICJ1bWlfZGVkdXBfbXJuYV9jb3VudCIpIHw+CiAgc2V0X2NvbmRpdGlvbnMoZmFjdCA9ICJnZW5vX2xvY19hdGIiKSB8PgogIHNldF9iYXRjaGVzKGZhY3QgPSAidGltZV9hdGIiKSB8PgogIHNldF9jb2xvcnMoY29sb3JfY2hvaWNlc1tbImdlbm9fbG9jIl1dKQptbTM4X2hpc2F0X3R4CgojIyBOb3cgdGhhdCB3ZSBjcmVhdGVkIHRoZSBkYXRhIHN0cnVjdHVyZSB3aXRoIElEcyB0aGF0IGluY2x1ZGUgdGhlIHRyYW5zY3JpcHQ6IHByZWZpeCwKIyMgcmVtb3ZlIGl0Li4uCm5ld19pZHMgPC0gZ3N1Yih4ID0gcm93bmFtZXMoYXNzYXkobW0zOF9oaXNhdF90eCkpLCBwYXR0ZXJuID0gIl50cmFuc2NyaXB0OiIsIHJlcGxhY2VtZW50ID0gIiIpCnJvd25hbWVzKG1tMzhfaGlzYXRfdHgpIDwtIG5ld19pZHMKcm93bmFtZXMobW1fdHhfYW5ub3QpIDwtIGdzdWIoeCA9IHJvd25hbWVzKG1tX3R4X2Fubm90KSwgcGF0dGVybiA9ICJedHJhbnNjcmlwdDoiLCByZXBsYWNlbWVudCA9ICIiKQphbGxfZmFjdCA8LSBwYXN0ZTAoY29sRGF0YShtbTM4X2hpc2F0X3R4KVtbInRpbWVfYXRiIl1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICBjb2xEYXRhKG1tMzhfaGlzYXRfdHgpW1siZ2Vub19sb2NfYXRiIl1dKQpjb2xEYXRhKG1tMzhfaGlzYXRfdHgpW1sidGltZV9nZW5vX2xvYyJdXSA8LSBhbGxfZmFjdApkaW0oYXNzYXkobW0zOF9oaXNhdF90eCkpCmhlYWQoYXNzYXkobW0zOF9oaXNhdF90eCkpCmhlYWQocm93RGF0YShtbTM4X2hpc2F0X3R4KSkKYGBgCgojIE5vbi16ZXJvIENvdW50cyBwZXIgU2FtcGxlCgpBIG5lYXQgbGl0dGxlIGNoYW5nZSBJIGp1c3QgbWFkZTogd2hlbiBudWxsIHNldCB0aGUgeS1pbnRlcmNlcHQgdG8gdGhlCm1lYW4gcGVyY2VudGFnZSBvZiB1bml0cyBvYnNlcnZlZCAodHJhbnNjcmlwdHMgaW4gdGhpcyBjYXNlKS4KCmBgYHtyfQp0eF9ub256ZXJvICA8LSBwbG90X25vbnplcm8obW0zOF9oaXNhdF90eCkKcHAoZmlsZSA9ICIwMWRpYWdub3N0aWNfaW1hZ2VzX3R4L25vbnplcm9fdHhfdW5maWx0ZXJlZC5wZGYiLCBpbWFnZSA9IHR4X25vbnplcm9bWyJwbG90Il1dKQoKdHhfbGlic2l6ZSA8LSBwbG90X2xpYnNpemUobW0zOF9oaXNhdF90eCkKcHAoZmlsZSA9ICIwMWRpYWdub3N0aWNfaW1hZ2VzX3R4L3F1YW50c2l6ZV91bmZpbHRlcmVkLnBkZiIsIGltYWdlID0gdHhfbGlic2l6ZVtbInBsb3QiXV0pCmBgYAoKIyMgRXhjbHVkZSBwNjAKCmBgYHtyfQptbTM4X2hpc2F0X3R4IDwtIHN1YnNldF9zZShtbTM4X2hpc2F0X3R4LCBzdWJzZXQgPSAidGltZV9hdGIhPSdwNjAnIikKYGBgCgojIyBSZXBsb3QgdGhlIG5vbnplcm8gZ2VuZSBwbG90cwoKYGBge3J9CnR4X25vbnplcm9fZmlsdCA8LSBwbG90X25vbnplcm8obW0zOF9oaXNhdF90eCwgcGxvdF9sYWJlbHMgPSBGQUxTRSkKcHAoZmlsZSA9ICIwMWRpYWdub3N0aWNfaW1hZ2VzX3R4L25vbnplcm9fdHhfZmlsdC5wZGYiLCBpbWFnZSA9IHR4X25vbnplcm9fZmlsdFtbInBsb3QiXV0pCmBgYAoKIyBRdWljayBQQ0EsIHRoZW4gcmV0dXJuIHRvIFRoZXJlc2EncyBkb2N1bWVudAoKYGBge3J9CnR4X25vcm0gPC0gbm9ybWFsaXplKG1tMzhfaGlzYXRfdHgsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICBub3JtID0gInF1YW50IiwgZmlsdGVyID0gVFJVRSkKdHhfbm9ybV9wY2EgPC0gcGxvdF9wY2EodHhfbm9ybSkKcHAoZmlsZSA9ICIwMWRpYWdub3N0aWNfaW1hZ2VzX3R4L3R4X25vcm1fcGNhLnBkZiIsIGltYWdlID0gdHhfbm9ybV9wY2FbWyJwbG90Il1dKQpgYGAKCmBgYHtyfQp0eF9sb2NfZ2VubyA8LSBzZXRfY29uZGl0aW9ucyhtbTM4X2hpc2F0X3R4LCBmYWN0ID0gImxvY2F0aW9uX2F0YiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IGNvbG9yX2Nob2ljZXNbWyJsb2NhdGlvbiJdXSkgfD4KICBzZXRfYmF0Y2hlcyhmYWN0ID0gImdlbm90eXBlX2F0YiIpCmBgYAoKIyMgVGhlIGFzc29jaWF0ZWQgUENBCgpBdCBkaWZmZXJlbnQgdGltZXMsIGl0IGFwcGVhcnMgdG8gbWUgdGhhdCBUaGVyZXNhIGhhcyBwcmVmZXJyZWQKc2xpZ2h0bHkgZGlmZmVyZW50IG5vcm1hbGl6YXRpb24gbWV0aG9kcywgcHJpbWFyaWx5IGEgbWl4IG9mIFRNTSBhbmQKcXVhbnRpbGUuCgpUaHVzIEkgd2lsbCB1c2UgZGlmZmVyZW50IHN1ZmZpeCBsZXR0ZXJzIHRvIGRlbm90ZSB2YXJpb3VzCm5vcm1hbGl6YXRpb25zIGVtcGxveWVkLCBhbmQgaWYgdGhleSB0dXJuIG91dCB0aGUgc2FtZSBJIHdpbGwgcGljayBvbmUgYXJiaXRyYXJpbHkuCgpgYGB7cn0KbG9jX2dlbm9fbnEgPC0gbm9ybWFsaXplKHR4X2xvY19nZW5vLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsIG5vcm0gPSAicXVhbnQiKQpsb2NhdGlvbl9nZW5vdHlwZV9wY2EgPC0gcGxvdF9wY2EobG9jX2dlbm9fbnEpCnBwKGZpbGUgPSAiMDFkaWFnbm9zdGljX2ltYWdlc190eC9sb2NhdGlvbl9nZW5vdHlwZV9ub3JtX3BjYS5wZGYiLAogICBpbWFnZSA9IGxvY2F0aW9uX2dlbm90eXBlX3BjYVtbInBsb3QiXV0pCgpsb2NfZ2Vub19udCA8LSBub3JtYWxpemUodHhfbG9jX2dlbm8sIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwgbm9ybSA9ICJ0bW0iKQpsb2NhdGlvbl9nZW5vdHlwZV90bW1fcGNhIDwtIHBsb3RfcGNhKGxvY19nZW5vX250KQpwcChmaWxlID0gIjAxZGlhZ25vc3RpY19pbWFnZXNfdHgvbG9jYXRpb25fZ2Vub3R5cGVfdG1tX3BjYS5wZGYiLCBpbWFnZSA9IGxvY2F0aW9uX2dlbm90eXBlX3RtbV9wY2FbWyJwbG90Il1dKQpgYGAKCmBgYHtyfQpzYW1wbGVfc2Fua2V5IDwtIHBsb3RfbWV0YV9zYW5rZXkodHhfbG9jX2dlbm8sIGNvbG9yX2Nob2ljZXMgPSBjb2xvcl9jaG9pY2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjdG9ycyA9IGMoImdlbm90eXBlX2F0YiIsICJsb2NhdGlvbl9hdGIiLCAidGltZV9hdGIiKSkKcHAoZmlsZSA9ICIwMWRpYWdub3N0aWNfaW1hZ2VzX3R4L2Rlc2lnbl9zYW5rZXkucGRmIiwgaW1hZ2UgPSBzYW1wbGVfc2Fua2V5W1sicGxvdCJdXSkKYGBgCgpSZS1wdXJwb3NlIHRoZXNlIGV4cHJlc3Npb24gcGxvdHMgdG8gZXhhbWluZSBvbmUgT3BuNCB0cmFuc2NyaXB0LgpUaGlzIHdpbGwgYnkgc29tZSBuZWNlc3NpdHkgYmUgYXJiaXRyYXJ5LgoKSXQgaXMgYXQgdGhpcyBtb21lbnQgdGhhdCBJIHJlYWxpemVkIChhcyBJIGFtIGFsbW9zdCBjZXJ0YWluIEkgdG9sZApteXNlbGYgd2hlbiBJIHN0YXJ0ZWQpIHVzaW5nIHRoZSBoaXNhdC1kZXJpdmVkIHR4IGNvdW50cyBpcyB3cm9uZwpiZWNhdXNlIGhpc2F0LT5mZWF0dXJlQ291bnRzIGhhcyBubyBnb29kIHdheSB0byBhbGxvY2F0ZSBtdWx0aS1oaXRzIHRvCm11bHRpcGxlIHRyYW5zY3JpcHRzIGFuZCB3aWxsIHRoZXJlZm9yZSBkaXNjb3VudCB0aGVtLiAgSWYgSSB3YW50IHRvCmRvIHRoaXMgaW4gYW55IHNvcnQgb2YgcmVsaWFibGUgd2F5LCBJIHJlYWxseSBuZWVkIHRvIHJlLWV4dHJhY3QgdGhlCmRlZHVwbGljYXRlIHJlYWRzIGZyb20gdGhlIHVtaXRvb2xzIGJhbSBmaWxlIGFuZCBydW4gc2FsbW9uL2thbGxpc3RvLgpUaGlzIGlzIHNvbWV3aGF0IGFubm95aW5nIGJlY2F1c2UgdGhlIHZhcmlvdXMgdG9vbHMgSSBoYXZlIGZvciBleHRyYWN0CmJhbSByZWFkcyByZWFsbHkgaGF0ZSB0aGUgd2F5IHVtaXRvb2xzIGZvcm1hdHMgdGhlIGJhbSBmaWxlIGFuZCBmcmVhawpvdXQuICBJIGNhbiBmb3JjZSBpdCwgYnV0IEkgdGhpbmsgSSB3aWxsIGxvc2UgYSBwb3RlbnRpYWxseQpzaWduaWZpY2FudCBudW1iZXIgb2YgcmVhZHMsIGxldCB1cyBnbyBsb29rLi4uCgooSWYgeW91IHdpdGggdG8gZm9sbG93IGFsb25nLCBnbyBiYWNrIHRvIHRoZSBwcmVwcm9jZXNzaW5nIGRvY3VtZW50CndoaWNoIEkgc2hvdWxkIGhhdmUgaW5jbHVkZWQgaW4gdGhlIHNpbmd1bGFyaXR5IGNvbnRhaW5lcikgIEkgd2lsbCBkbwpvbmUgb2YgdGhlIGZvbGxvd2luZzoKCjEuICBVc2UgZ2F0ayBvciBiYW10b29scyB0byBjcmVhdGUgZGVkdXBsaWNhdGVkIGZhc3RxIGZpbGVzIGFuZCB1c2UKICAgIHRoZW0gd2l0aCBzYWxtb24uCjIuICBVc2Ugc2FsbW9uIHdpdGggdGhlIGRlZHVwbGljYXRlZCBiYW0gZmlsZXMgZnJvbSBoaXNhdC4KCkkgYW0gbm90IHN1cmUgcmlnaHQgbm93IHdoaWNoIEkgd2FudCB0byBkby4gIEkga2luZCBvZiBsaWtlIHRoZSBpZGVhCm9mIGhhdmluZyBkZWR1cGxpY2F0ZWQgZmFzdHEgZmlsZXMgc28gdGhhdCBJIGNhbiB0cnkgc29tZSBvdGhlciBzdHVmZgp3aXRoIHRoZW0gKGtyYWtlbiwgZm9yIGV4YW1wbGUpCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyMgZ2dzdGF0c3Bsb3QgaGFzIGFwcGVhcmVkIGFuZCBkaXNhcHBlYXJlZCBhIGZldyB0aW1lcyBmcm9tIGNyYW4gcmVjZW50bHksCiMjIEkgYW0gbm90IHN1cmUgaWYgaXQgd2lsbCB3b3JrIHRoZXJlZm9yZSwgc28gZmlndXJlZCBJIHNob3VsZCBkaXNhYmxlIHRoaXMgYmxvY2sKIyMgZm9yIHRoZSBtb21lbnQuCgojIyBJIGRpZCBhIHNlYXJjaCBhdCBlbnNlbWJsIGZvciB0aGUgdmFyaW91cyBvcG40IHRyYW5zY3JpcHQgSURzOgojIyBFTlNNVVNUMDAwMDAwMjIzMzEsIEVOU01VU1QwMDAwMDE2ODQ0NCwgRU5TTVVTVDAwMDAwMjI2ODA2CmlkcyA8LSBjKCJFTlNNVVNUMDAwMDAwMjIzMzEiLCAiRU5TTVVTVDAwMDAwMTY4NDQ0IiwgIkVOU01VU1QwMDAwMDIyNjgwNiIpCmlkcyAlaW4lIHJvd25hbWVzKGxvY19nZW5vX250KQppZHMgPC0gYygiRU5TTVVTVDAwMDAwMDIyMzMxIiwgIkVOU01VU1QwMDAwMDE2ODQ0NCIpCgpvcG40X2V4cHJzIDwtIGRhdGEuZnJhbWUoY29tYmluZWQgPSBjb2xEYXRhKGxvY19nZW5vX250KVtbImdlbm9fbG9jX2F0YiJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uID0gY29sRGF0YShsb2NfZ2Vub19udClbWyJsb2NhdGlvbl9hdGIiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICBnZW5vdHlwZSA9IGNvbERhdGEobG9jX2dlbm9fbnQpW1siZ2Vub3R5cGVfYXRiIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgb3BuNCA9IGFzc2F5KHR4X2xvY19nZW5vKVtpZHNbMV0sIF0pCgpncm91cGVkc3RhdHM6Omdyb3VwZWRfc3VtbWFyeShvcG40X2V4cHJzLCBsb2NhdGlvbiwgb3BuKQpvcG40X2xvY2F0aW9uIDwtIGdnc3RhdHNwbG90OjpnZ2JldHdlZW5zdGF0cyhkYXRhID0gb3BuNF9leHBycywgeCA9IGxvY2F0aW9uLCB5ID0gb3BuKQpwcChmaWxlID0gImltYWdlc190eC9nZ2JldHdlZW5fbG9jYXRpb24ucGRmIikKb3BuNF9sb2NhdGlvbgpwbG90dGVkIDwtIGRldi5vZmYoKQpvcG40X2xvY2F0aW9uCmBgYAoKIyBUd28gbm90ZXdvcnRoeSBnZW5lcyB3aXRoIHBvdGVudGlhbGx5IGRpZmZlcmVudCB0cmFuc2NyaXB0IGFidW5kYW5jZXMKCk9uZSByZXZpZXdlciBzdWdnZXN0ZWQgY2hlY2tpbmcgb3V0IHRoZSB0cmFuc2NyaXB0LWxldmVsIGFidW5kYW5jZXMgYW5kIG5vdGVkIHR3bwpnZW5lcyBpbiBwYXJ0aWN1bGFyIGFzIHBvdGVudGlhbGx5IGludGVyZXN0aW5nOiBObGduMyBhbmQgTmNhbTEuICBMZXQgdXMgdGFrZSBhCm1vbWVudCBhbmQgc2VlIHdoYXQgdGhleSBsb29rIGxpa2UgYWNyb3NzIHRoZSB2YXJpb3VzIG1ldGFkYXRhIGZhY3RvcnMgb2YgaW50ZXJlc3QuCgojIyBHcmFiIHRoZSB0eCBhbm5vdGF0aW9ucyBmb3IgdGhlbQoKYGBge3IsIGV2YWw9RkFMU0V9Cm5sZ24zX2lkeCA8LSBncmVwbCh4ID0gcm93RGF0YShtbTM4X2hpc2F0X3R4KVtbImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSJdXSwgcGF0dGVybiA9ICJeTmxnbjNcXC0iKQpzdW0obmxnbjNfaWR4KQpubGduM19hbm5vdCA8LSByb3dEYXRhKG1tMzhfaGlzYXRfdHgpW25sZ24zX2lkeCwgXQpubGduM19pZHMgPC0gcm93bmFtZXMocm93RGF0YShtbTM4X2hpc2F0X3R4KSlbbmxnbjNfaWR4XQphc3NheShtbTM4X2hpc2F0X3R4KVtubGduM19pZHMsIF0KIyMgTG9va3MgbGlrZQoKbmNhbTFfaWR4IDwtIGdyZXBsKHggPSByb3dEYXRhKG1tMzhfaGlzYXRfdHgpW1siZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIl1dLCBwYXR0ZXJuID0gIl5OY2FtMVxcLSIpCnN1bShuY2FtMV9pZHgpCnJvd0RhdGEocm93RGF0YShtbTM4X2hpc2F0X3R4KSlbbmNhbTFfaWR4LCBdCm5jYW0xX2Fubm90IDwtIGFzLmRhdGEuZnJhbWUocm93RGF0YShtbTM4X2hpc2F0X3R4KSlbbmNhbTFfaWR4LCBdCm5jYW0xX2lkcyA8LSByb3duYW1lcyhyb3dEYXRhKG1tMzhfaGlzYXRfdHgpKVtuY2FtMV9pZHhdCmFzc2F5KG1tMzhfaGlzYXRfdHgpW25jYW0xX2lkcywgXQpgYGAKCm9rLCBzbyBJIHBsb3R0ZWQgdGhlIHF1ZXN0aW9uIGEgYml0IGRpZmZlcmVudGx5LCBidXQgZ290IHRoZSBzYW1lCmFuc3dlci4KCkhlcmUgaXMgdGhlIHRleHQgb2YgVGhlcmVzYSdzIG5vdGVib29rIGZvbGxvd2luZyB0aGlzIGFuYWx5c2lzOgoKIlVnaCBvaCwgbG9va3MgbGlrZSB0aGVyZSBpcyBhdCBsZWFzdCBvbmUgcmV0aW5hIEtPIHNhbXBsZSB0aGF0IGhhcwpzb21lIG1lbGFub3BzaW4gZXhwcmVzc2lvbiBpbiBpdC4gVHVybnMgb3V0IGlwUkdDXzA3IGlzIGEgYmFkIGVnZwp3aGljaCBpcyBzdXBwb3NlZCB0byBiZSBhIEtPIGJ1dCBoYXMgbWVsYW5vcHNpbiBleHByZXNzaW9uLiBJdOKAmXMKZnJpZW5kcyB3aGljaCB3ZXJlIHBvb2xlZCBmcm9tIHRoZSBzYW1lIG1pY2UgYXJlIGlwcmdjXzA2IGFuZAppcHJnY18wOCwgc28gd2UgbmVlZCB0byBleGNsdWRlIGFsbCB0aGVzZSBzYW1wbGVzLiIKCkkgYW0gYWxzbyBzZWVpbmcgc29tZSBrbm9ja291dCBleHByZXNzaW9uIHdpdGggc29tZSBjYXZlYXRzOiBJIGRvIG5vdApoYXZlIHRoZSBhZmZlY3RlZCBzYW1wbGVzIGluIG15IGRhdGFzZXQgKGlwcmdjXzA3KSBhbmQgdGhlIGxldmVscyBJIGFtCnNlZWluZyBhcmUgcXVpdGUgbG93IC0tIEkgd2lsbCBsb29rIGluIElHViB0byBkb3VibGUgY2hlY2ssIGJ1dCBJCnN0cm9uZ2x5IHN1c3BlY3QgdGhhdCB0aGVzZSBhcmUgc29tZSBwaWRkbHkgcmVhZHMgbmVhciB0aGUgVVRScy4KCk9ud2FyZCEKCiMgUENBIHBsb3RzCgojIyBQQ0Egb2YgYWxsIGdlbmVzIGJ5IGxvY2F0aW9uCgpUaGVyZXNhJ3MgZmlyc3QgcGNhIHdhcyBvZiBsb2cyIGNwbSB2YWx1ZXMuICBJIG1pZ2h0IGFkZCBxdWFudGlsZS90bW0KdG8gdGhpcz8KCmBgYHtyfQp0eF9sb2NhdGlvbiA8LSBzZXRfY29uZGl0aW9ucyhtbTM4X2hpc2F0X3R4LCBmYWN0ID0gImxvY2F0aW9uX2F0YiIpIHw+CiAgc2V0X2JhdGNoZXMoZmFjdCA9ICJnZW5vdHlwZV9hdGIiKSB8PgogIHNldF9jb2xvcnMoY29sb3JfY2hvaWNlc1tbImxvY2F0aW9uIl1dKQp0eF9sb2NhdGlvbl9ub3JtIDwtIG5vcm1hbGl6ZSh0eF9sb2NhdGlvbiwgZmlsdGVyID0gVFJVRSwgbm9ybSA9ICJxdWFudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIpCnR4X2xvY2F0aW9uX3BjYSA8LSBwbG90X3BjYSh0eF9sb2NhdGlvbl9ub3JtKQpwcChmaWxlID0gIjAxZGlhZ25vc3RpY19pbWFnZXNfdHgvdHhfbG9jYXRpb25fbm9ybV9wY2EucGRmIikKdHhfbG9jYXRpb25fcGNhCmRldi5vZmYoKQp0eF9sb2NhdGlvbl9wY2EKYGBgCgpPbmNlIGFnYWluIHdlIHNlZSB0aGF0IHNhbXBsZXMgaXByZ2NfNjYgYW5kIGlwcmdjXzEzMCBhcmUgbGlrZWx5CmFjdHVhbGx5IERMR04gYW5kIG5vdCBTQ04uICBJIGFtIHRoZXJlZm9yZSBnb2luZyB0byBhZGQgYSBjb2x1bW4gdG8KdGhlIHNhbXBsZSBzaGVldCBub3RpbmcgdGhpcywgYW5kIHJlbW92ZSB0aGVtIGZyb20gdGhlIGV4cHJlc3Npb25zZXQuCgpJIHdpbGwgdGh1cyByZXBsb3QgdGhlIGRhdGEgYWZ0ZXIgcmVtb3ZpbmcgdGhvc2UgdHdvLiAgSWYgd2Ugd2FudCB0bwpzZWUgd2hhdCBpdCBsb29rcyBsaWtlIHdpdGggdGhlIHJlLWF0dHJpYnV0ZWQgbG9jYXRpb25zLCB3ZSBjYW4gZG8gc28uCgpUaGVyZXNhIGhhcyBhIG5pY2UgY2hhbmdlIHRvIHRoZSBQQ0EgcGxvdHRlciBpbiB3aGljaCBzaGUgc2V0cyB0aGUKYWxwaGEgY2hhbm5lbCBhcyBhbiBhZGRpdGlvbmFsIHZpc3VhbCBxdWV1ZSBmb3IgYSBtZXRhZGF0YSBmYWN0b3IuLi4KCmBgYHtyfQptbTM4X2hpc2F0X3R4IDwtIHN1YnNldF9zZShtbTM4X2hpc2F0X3R4LCBzdWJzZXQ9InNhbXBsZWlkIT0naXByZ2NfMTMwJyIpIHw+CiAgc3Vic2V0X3NlKHN1YnNldD0ic2FtcGxlaWQhPSdpcHJnY182NiciKQp0eF9sb2NhdGlvbiA8LSBzZXRfY29uZGl0aW9ucyhtbTM4X2hpc2F0X3R4LCBmYWN0ID0gImxvY2F0aW9uX2F0YiIpIHw+CiAgc2V0X2JhdGNoZXMoZmFjdCA9ICJnZW5vdHlwZV9hdGIiKSB8PgogIHNldF9jb2xvcnMoY29sb3JfY2hvaWNlc1tbImxvY2F0aW9uIl1dKQoKdHhfbG9jYXRpb25fbm9ybSA8LSBub3JtYWxpemUodHhfbG9jYXRpb24sIGZpbHRlciA9IFRSVUUsIG5vcm0gPSAicXVhbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iKQpmaWx0ZXJlZF9sb2NhdGlvbl9wY2EgPC0gcGxvdF9wY2EodHhfbG9jYXRpb25fbm9ybSkKcHAoZmlsZSA9ICIwMmZpbHRlcmVkX2ltYWdlc190eC9maWx0ZXJlZF9sb2NhdGlvbl9wY2EucGRmIikKZmlsdGVyZWRfbG9jYXRpb25fcGNhW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpmaWx0ZXJlZF9sb2NhdGlvbl9wY2EKCnJlbW92ZWRfc2Fua2V5IDwtIHBsb3RfbWV0YV9zYW5rZXkodHhfbG9jYXRpb24sIGNvbG9yX2Nob2ljZXMgPSBjb2xvcl9jaG9pY2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY3RvcnMgPSBjKCJnZW5vdHlwZV9hdGIiLCAibG9jYXRpb25fYXRiIiwgInRpbWVfYXRiIikpCnBwKGZpbGUgPSAiMDJmaWx0ZXJlZF9pbWFnZXNfdHgvZmlsdGVyZWRfc2Fua2V5LnBkZiIsIGltYWdlID0gcmVtb3ZlZF9zYW5rZXlbWyJwbG90Il1dKQpgYGAKCkhlcmUgaXMgVGhlcmVzYSdzIHRleHQsIHJlY2FsbCBvbmNlIGFnYWluIHRoYXQgSSBkbyBub3QgaGF2ZSBzb21lIG9mCnRoZXNlIG9sZGVyIHNhbXBsZXMgKGlwcmdjXzYyKToKClBDMSB2cyBQQzIgaWRlbnRpZmllcyByZXRpbmEgdnMgYXhvbiBpcyBzdGlsbCB0aGUgbWFpbiBjb21wb25lbnQgb2YKdmFyaWF0aW9uLiBXZSBkbyBzZWUgdGhvdWdoIHRoYXQgaW4gdGhlIFBDMiBkaXJlY3Rpb24sIHdlIHNlZSB3aXRoIHRoZQpuZXcgc2FtcGxlcyBhZGRlZCwgd2UgZG9u4oCZdCBzZWUgc2VwYXJhdGlvbiBiYXNlZCBvbiBheG9uYWwgdGFyZ2V0cwooZExHTiB2cyBTQ04pLiBJbiB0aGUgUEMxIHZzIFBDMyBwbG90LCB3ZSBzZWUgdGhhdCBpdOKAmXMgUEMzIHdoZXJlIHdlCnN0YXJ0IHRvIHNlZSB2YXJpYXRpb24gY29ycmVsYXRlZCB3aXRoIGF4b25hbCBjb21wYXJ0bWVudC4gTGV04oCZcyBsb29rCmF0IFBDMSB2cyBQQzIgY29sb3JlZCBieSBiYXRjaCAod2hlbiB0aGV5IHdlcmUgcHJvY2Vzc2VkL3NlcXVlbmNlZCkgdG8Kc2VlIGlmIHRoYXQgaXMgd2hhdCBpcyBjb250cmlidXRpbmcgc28gbXVjaCB2YXJpYXRpb24gaW4gUEMyLgoKU2lkZSBub3RlOiBpcFJHQyA2MiBzZWVtcyBsaWtlIGFuIG9kZCBiYWxsLiBUaGlzIHNlZW1zIHRvIG1lIGxpa2UgaXQKc2hvdWxkIGhhdmUgYmVlbiBhIGRMR04gUDA4IHNhbXBsZS4gSXMgdGhlcmUgYW55IHBvc3NpYmlsaXR5IHRoaXMgZ290Cm1pc2xhYmVsZWQgZWFybHkgb24/IEkgd2VudCBiYWNrIGFuZCBkb3VibGUgY2hlY2tlZCB0byBzZWUgaWYgYWxsIG15CnByb2Nlc3NpbmcgaXMgY29ycmVjdCBhbmQgaXQgaW5kZWVkIHdhcyBsYWJlbGVkIGFuIFNDTiBQMTUgZnJvbSB0aGUKdGltZSBJIGdvdCB0aGUgc2FtcGxlcywgYW5kIGl0IGlzIGluZGVlZC4KCiMgREUKCkkgbm93IHN3aXRjaGVkIHRvIFRoZXJlc2EncyBkb2N1bWVudCAnV09SS0lOR19heG9uVFJBUC4uLicgYW5kIHdpbGwKc3RhcnQgcHVsbGluZyBzZWN0aW9ucyBmcm9tIGl0LiAgSSBhbSByZWFzb25hYmx5IGNlcnRhaW4gSSBoYXZlCnJlYXNvbmFibHkgc2ltaWxhciBzYW1wbGUgZGlzdHJpYnV0aW9ucywgc28gSSBwcmVzdW1lIEkgY2FuIGludm9rZQpzaW1pbGFyL2lkZW50aWNhbCBjYWxscyBmb3IgREVTZXEgYW5kIGZyaWVuZHMuCgojIyBwOCByZXRpbmFzCgpJbiB0aGUgYmxvY2sgaW1tZWRpYXRlbHkgYmVmb3JlIHRoZSBERSBhbmFseXNlcywgVGhlcmVzYSBjcmVhdGVkIGEKc3Vic2V0IGV4cHJlc3Npb25zZXQgb2Ygb25seSBwMDggcmV0aW5hcy4gIFRodXMgdGhpcyBpbml0aWFsIERFIEkKYXNzdW1lIHdpbGwgYmUgdXNlZCB0byBzdWJ0cmFjdCBmb3IgdGhlIFNDTi9ETEdOIGFuYWx5c2VzIHRoYXQgZm9sbG93LgooSSBndWVzcyBJIGNvdWxkIHJlYWQgYWhlYWQgYW5kIGZpbmQgb3V0LCBidXQgbm8hIEkgd2FudCB0byBiZSBhCmJsYW5rIHNsYXRlKQoKVGhlcmVzYSdzIHByaW1hcnkgd29ya2Zsb3cgbWFrZXMgaGVhdnkgdXNlIG9mIERFU2VxMgooQGxvdmVNb2RlcmF0ZWRFc3RpbWF0aW9uRm9sZDIwMTQpIGFuZCBzdmEKKEBsZWVrU1ZBUGFja2FnZVJlbW92aW5nMjAxMikuICBJbiBzb21lKG1vc3Q/KSBvZiBUaGVyZXNhJ3MKaW52b2NhdGlvbnMgb2YgdGhlIGFsbF9wYWlyd2lzZSgpIGZ1bmN0aW9uLCBzaGUgZXhjbHVkZXMgdGhlIG90aGVyCm1ldGhvZHMgdGhhdCBpdCBwZXJmb3Jtcy4gIEluIHRoaXMgd29ya2Jvb2ssIEkgbGVmdCB0aG9zZSBtZXRob2RzIG9uLAp0aHVzIHdlIGNhbiBldmFsdWF0ZSB0aGUgcmVsYXRpdmUgcGVyZm9ybWFuY2UgREVTZXEyIHZzLiBzb21lIChhbGw/IEkKbWF5IGhhdmUgZGlzYWJsZWQgRUJTZXEvZHJlYW0gYmVjYXVzZSB0aGV5IHdlcmUgdGFraW5nIHRvbyBsb25nKQpvZiB0aGUgZm9sbG93aW5nOgoKKiBsaW1tYTogKEByaXRjaGllTGltbWFQb3dlcnNEaWZmZXJlbnRpYWwyMDE1KSAoYW1vbmcgb3RoZXIKICByZWZlcmVuY2VzKSBvcmlnaW5hbGx5IHdyaXR0ZW4gZm9yIG1pY3JvYXJyYXlzLgoqIEVkZ2VSOiAoQHJvYmluc29uRWRnZVJCaW9jb25kdWN0b3JQYWNrYWdlMjAxMCksIHdoaWNoIHNoYXJlcyBtYW55CiAgYXNzdW1wdGlvbnMgd2l0aCBERVNlcTIuCiogRUJTZXE6IChAbGVuZ0VCU2VxRW1waXJpY2FsQmF5ZXMyMDEzKSwgYmVjYXVzZSBJIGhhdmUgYSBzb2Z0IHNwb3QKICBmb3IgYW55IEJheWVzaWFuIG1ldGhvZC4KKiBOb2lzZXE6IChAdGFyYXpvbmFOT0lzZXFSTkFzZXFEaWZmZXJlbnRpYWwyMDExKSwgd2hpY2ggc2Vla3MgdG8KICBkaXJlY3RseSBtb2RlbCB2YXJpYW5jZSBpbiBhbiBSTkFTZXEgZGF0YXNldCBhbmQgdXNlIHRoYXQgdG8gaW1wcm92ZQogIHRoZSBzZW5zaXRpdml0eSBvZiB0aGUgcmVzdWx0LCBtdWNoIGxpa2U6CiogRHJlYW06IChAaG9mZm1hbkRyZWFtUG93ZXJmdWxEaWZmZXJlbnRpYWwyMDIwKSwgd3JpdHRlbiBieSB0aGUgc2FtZQogIGF1dGhvcnMgKGFuZCB1c2VzIHZlcnkgc2ltaWxhciBsb2dpYykgYXMgb25lIG9mIG15IGZhdm9yaXRlIHRvb2xzLAogIHZhcmlhbmNlUGFydGl0aW9uKEBob2ZmbWFuVmFyaWFuY2VQYXJ0aXRpb25JbnRlcnByZXRpbmdEcml2ZXJzMjAxNikuCgpgYGB7cn0KbW0zOF9wOF9yZXRpbmEgPC0gc3Vic2V0X3NlKG1tMzhfaGlzYXRfdHgsIHN1YnNldCA9ICJ0aW1lX2F0Yj09J3AwOCcgJiBsb2NhdGlvbl9hdGI9PSdyZXRpbmEnIikKbW1fbm9ybWFsX3A4X3JldF9kZSA8LSBhbGxfcGFpcndpc2UobW0zOF9wOF9yZXRpbmEsIG1vZGVsX3N2cyA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9mc3RyaW5nID0gIn4gMCArIGNvbmRpdGlvbiIsIGZpbHRlciA9IFRSVUUpCm1tX25vcm1hbF9wOF9yZXRfZGUKYGBgCgpUaGVyZSBzZWVtcyB0byBiZSBhIGRpc2NyZXBlbmN5IHdpdGggcHJldmlvdXMgaXRlcmF0aW9ucyBvZiB0aGlzLgpMZXQgdXMgc2ltcGxpZnkgdG8ganVzdCBkb2luZyBkZXNlcSBhbmQgZmluZCB3aGF0IGlzIGNhdXNpbmcgaXQuCkluIG15IHByZXZpb3VzIGl0ZXJhdGlvbiwgSSBnb3QgMzYzMiBnZW5lcyBpbiB0aGUgdW5pcXVlKGMoKSkgb3IgaGV0K2tvLgoKYGBge3J9CmRlc2VxX29ubHkgPC0gZGVzZXFfcGFpcndpc2UobW0zOF9wOF9yZXRpbmEsIG1vZGVsX3N2cyA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2ZzdHJpbmcgPSBkZWZhdWx0X2ZzdHJpbmcsIGZpbHRlciA9IFRSVUUpCmRlc2VxX2hldGtlZXBlcl90eCA8LSBkZXNlcV9vbmx5JGFsbF90YWJsZXMkd3RfcmV0aW5hX3ZzX2hldF9yZXRpbmEgfD4KICBmaWx0ZXIobG9nRkMgPD0gLTAuMjUgJiBhZGouUC5WYWwgPD0gMC4wNSkKZGVzZXFfa29rZWVwZXJfdHggPC0gZGVzZXFfb25seSRhbGxfdGFibGVzJHd0X3JldGluYV92c19rb19yZXRpbmEgfD4KICBmaWx0ZXIobG9nRkMgPD0gLTAuMjUgJiBhZGouUC5WYWwgPD0gMC4wNSkKZGVzZXFfa2VlcGVydHggPC0gdW5pcXVlKGMocm93bmFtZXMoZGVzZXFfaGV0a2VlcGVyX3R4KSwKICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXMoZGVzZXFfa29rZWVwZXJfdHgpKSkKbGVuZ3RoKGRlc2VxX2tlZXBlcnR4KQoKZGVzZXFfcGFpcl9oZXRrZWVwZXJfdHggPC0gbW1fbm9ybWFsX3A4X3JldF9kZSRkZXNlcSRhbGxfdGFibGVzJHd0X3JldGluYV92c19oZXRfcmV0aW5hIHw+CiAgZmlsdGVyKGxvZ0ZDIDw9IC0wLjI1ICYgYWRqLlAuVmFsIDw9IDAuMDUpCmRlc2VxX3BhaXJfa29rZWVwZXJfdHggPC0gbW1fbm9ybWFsX3A4X3JldF9kZSRkZXNlcSRhbGxfdGFibGVzJHd0X3JldGluYV92c19rb19yZXRpbmEgfD4KICBmaWx0ZXIobG9nRkMgPD0gLTAuMjUgJiBhZGouUC5WYWwgPD0gMC4wNSkKZGVzZXFfcGFpcl9rZWVwZXJ0eCA8LSB1bmlxdWUoYyhyb3duYW1lcyhkZXNlcV9wYWlyX2hldGtlZXBlcl90eCksCiAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKGRlc2VxX3BhaXJfa29rZWVwZXJfdHgpKSkKbGVuZ3RoKGRlc2VxX3BhaXJfa2VlcGVydHgpCmBgYAoKVGhlIGZvbGxvd2luZyBpbnZvY2F0aW9uIHBlcmZvcm1lZCBieSBUaGVyZXNhIGZpbHRlcnMgdGhlIHd0L2hldApjb21wYXJpc29uIGZvciBvbmx5IHRob3NlIGdlbmVzIHdoaWNoIGluY3JlYXNlZCBieSBhdCBsZWFzdCAwLjI1IGxvZ0ZDCndpdGggYSBzaWduaWZpY2FudCBhZGp1c3RlZCBwLXZhbHVlLiAgSSBhc3N1bWUgdGhhdCB0aGlzIGlzIHRvIHVzZSB0aGUKd3Qgc2FtcGxlcyBhcyBhIHRyYW5zbGF0aW9uYWwgY29udHJvbCBmb3IgdGhlIGtldC9rbyBjb21wYXJpc29uczsgSSBhbQp0aGVyZWZvcmUgdGhpbmtpbmcgdGhhdCBmb3IgbXkgcHVycG9zZXMsIEkgd2lsbCB0aGVyZWZvcmUgc2VwYXJhdGUgdGhlCmNvbnRyYXN0cyBmcm9tIGFsbF9wYWlyd2lzZSBkbyB0aGlzIGluIGEgc3RlcHdpc2UgZmFzaGlvbi4uLgoKVGhlIGJsb2NrIG9mIGNvZGUgaW1tZWRpYXRlbHkgZm9sbG93aW5nIFRoZXJlc2EncyBhbGxfcGFpcndpc2UoKQppbnZvY2F0aW9uIGlzIGEgbGl0dGxlIGNvbmZ1c2luZyBmb3IgbWUgYW5kIHdhcnJhbnRzIHNvbWUgZXhwbGFuYXRpb24KYnkgbWUgdG8gbWUgaW4gdGhlIGhvcGVzIHRoYXQgSSBkbyBub3QgbWlzdW5kZXJzdGFuZCB3aGF0IGlzIGhhcHBlbmluZwphbmQgdGhlIGdvYWxzIHRoZXJlaW4uCgpJIHRoaW5rIEkgY2FuIHNhZmVseSBhc3N1bWUgdGhhdCB0aGUgZ29hbCBoZXJlIGlzIHRvIHB1bGwgb3V0IHRoZSBJRHMKd2hpY2ggaW5jcmVhc2VkIGluIGhldCB3aXRoIHJlc3BlY3QgdG8gd2lsZCB0eXBlOyBldmVuIGlmIGJ5IGEgc21hbGwKbWFyZ2luLCBhcyBsb25nIGFzIGl0IGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgdmlzIGEgdmlzIHRoZQphZGp1c3RlZCBwLXZhbHVlLgoKSSBhbSBnb2luZyB0byBwZXJmb3JtIHdoYXQgSSB0aGluayBpcyB0aGUgc2FtZSB0aGluZyBpbiBhIHNsaWdodGx5CmRpZmZlcmVudCBmYXNoaW9uIHNvIHRoYXQgSSBjYW4gc2hhcmUgYSBjb3B5IG9mIHRoZSByZXN1bHRzIHdpdGgKd2hvbWV2ZXIgaXMgaW50ZXJlc3RlZC4gIEkgd2lsbCBhbHNvIHJlcGVhdCBUaGVyZXNhJ3MgaW52b2NhdGlvbiBhbmQKcHJvdmUgdG8gbXlzZWxmIHRoYXQgSSB1bmRlcnN0b29kIGFuZCBnb3QgdGhlIHNhbWUgYW5zd2VyLgoKYGBge3J9Cnd0X2hldF9rZWVwZXIgPC0gbGlzdCgiaGV0X3ZzX3d0IiA9IGMoImhldF9yZXRpbmEiLCAid3RfcmV0aW5hIikpCmhldF93dF90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBtbV9ub3JtYWxfcDhfcmV0X2RlLCBrZWVwZXJzID0gd3RfaGV0X2tlZXBlciwgbGFiZWxfY29sdW1uID0gdHhfbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gIjAzdGhlcmVzYV9jb21wYXJpc29uX2V4Y2VsX3R4L2hldF9yZXRpbmFfY29udHJvbC54bHN4IikKd2FudGVkX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhldF93dF90YWJsZSwgbGZjID0gMC4yNSwgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICBleGNlbCA9ICIwM3RoZXJlc2FfY29tcGFyaXNvbl9leGNlbF90eC9oZXRfcmV0aW5hX2NvbnRyb2wtc2lnLnhsc3giKQp3YW50ZWRfaGV0X2luY3JlYXNlZCA8LSB3YW50ZWRfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJoZXRfdnNfd3QiXV0KaW5jcmVhc2VkX2hldF9nZW5lcyA8LSByb3duYW1lcyh3YW50ZWRfaGV0X2luY3JlYXNlZCkKYGBgCgpIZXJlIGFyZSBUaGVyZXNhJ3MgbmV4dCBsaW5lczoKCmBgYHtyfQptbV9kZV9ub3JtYWxfcDhfcmV0IDwtIG1tX25vcm1hbF9wOF9yZXRfZGUKaGV0a2VlcGVyX3R4IDwtIG1tX2RlX25vcm1hbF9wOF9yZXQkZGVzZXEkYWxsX3RhYmxlcyR3dF9yZXRpbmFfdnNfaGV0X3JldGluYSB8PgogIGZpbHRlcihsb2dGQyA8PSAtMC4yNSAmIGFkai5QLlZhbCA8PSAwLjA1KQprb2tlZXBlcl90eCA8LSBtbV9kZV9ub3JtYWxfcDhfcmV0JGRlc2VxJGFsbF90YWJsZXMkd3RfcmV0aW5hX3ZzX2tvX3JldGluYSB8PgogIGZpbHRlcihsb2dGQyA8PSAtMC4yNSAmIGFkai5QLlZhbCA8PSAwLjA1KQprZWVwZXJ0eCA8LSB1bmlxdWUoYyhyb3duYW1lcyhoZXRrZWVwZXJfdHgpLAogICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhrb2tlZXBlcl90eCkpKQojIyBXZSBrbm93IGEgcHJpb3JpIHRoYXQgT3BuNCBpcyBFTlNNVVNHMDAwMDAwMjE3OTkKIyMgSSBkbyBub3QgZXhwZWN0IHRvIHNlZSBpdCBpbiB0aGlzIHNldCwgaXQgc2hvdWxkIGJlIGhpZ2hlciBpbiB3dAojIyByZXRpbmEgdnMga28gcmV0aW5hIGJ5IGEgc2lnbmlmaWNhbnQgbWFyZ2luLgojIyAiRU5TTVVTRzAwMDAwMDIxNzk5IiAlaW4lIGtlZXBlcnR4CiMjIE9vb29oaGggYnV0IGl0IF9pc18gaGlnaGVyIGluIGhldCB2cy4gd3QsIGFzIHdlIHNhdyBpbgojIyB0aGUgdmlvbGluIHBsb3QgZWFybGllci4KYGBgCgpJIHRoaW5rIFJhc2htaSBtYWRlIGEgY29tcGVsbGluZyBwb2ludCB3aGljaCBpbGx1c3RyYXRlcyB3aHkgd2UgbGlrZWx5CnNob3VsZCBleHBlY3QgdGhlIGV4cHJlc3Npb24gb2YgT3BuNCB0byBzaWduaWZpY2FudGx5IGhpZ2hlciBpbiB0aGUKaGV0ZXJvenlnb3RlcyB2cyB3aWxkLXR5cGU6CgoxLiAgUmVjYWxsIHRoYXQgdGhlIGFzc2F5IGlzIHVzaW5nIHRoZSBpbW11bm9wdXJpZmljYXRpb24gdG8gZXh0cmFjdAogICAgdGhlIFJOQXMuCjIuICBUaGUgd3Qgc2FtcGxlcyBkbyBub3QgaGF2ZSB0aGUgY3JlIHJlY29tYmluYXNlIGFuZCB0aGVyZWZvcmUgbm8gSEEKICAgIGFuZCB0aGVyZWZvcmUgZXZlcnl0aGluZyB3ZSBvYnNlcnZlIGlzIGR1ZSB0byBub24tc3BlY2lmaWMKICAgIGJpbmRpbmcuCjMuICBUaGUgc2V0IG9mIGdlbmVzIG9ic2VydmVkIGR1ZSB0byBub24tc3BlY2lmaWMgYmluZGluZyBpcyBkaWZmZXJlbnQKICAgIHRoYW4gaGV0L2tvIChwcmVzdW1hYmx5IGEgbGFyZ2VyIG51bWJlciBvZiByZWxhdGl2ZWx5IHNtYWxsCiAgICB2YWx1ZXMpLCB0aGVyZWZvcmUgdGhlIGRpdmlzb3IgcGVyZm9ybWVkIGluIHRoZSBjcG0gaXMgbGlrZWx5CiAgICByZWxhdGl2bHkgbGFyZ2UgcmVzdWx0aW5nIGluIG5vcm1hbGl6ZWQgdmFsdWVzIGdldHRpbmcgc2hpZnRlZAogICAgZG93biB0byBzb21lIGRlZ3JlZS4KNC4gIE9uIHRoZSBvdGhlciBoYW5kLCB0aGUgc2V0IG9mIGdlbmVzIG9ic2VydmVkIGluIGhldC9rbyBhcmUgbW9yZQogICAgbGlrZWx5IHRvIGJlIG9ubHkgdGhlIHNwZWNpZmljIGJpbmRlcnMgYW5kIHRoZXJlZm9yZSBzbWFsbGVyIChJCiAgICBjYW4gdGVzdCB0aGlzKSByZXN1bHRpbmcgaW4gYSBzbWFsbGVyIGRpdmlzb3IgYW5kIHNsaWdodCBzaGlmdGluZwogICAgdXAgaW4gdGhlIGNwbSB2YWx1ZXMuCgpUaGlzIG1ha2VzIG1lIHdvbmRlciBpZiBhbnkgbm9ybWFsaXphdGlvbiBtZXRob2RzIGV4aXN0IHdoaWNoIGRvCnNvbWV0aGluZyBsaWtlIG11bHRpcGx5IHRoZSB2YWx1ZXMgYnkgc29tZSB2YWx1ZSByZWxhdGVkIHRvIHRoZQpwcm9wb3J0aW9uIG9mIG9ic2VydmVkIGdlbmVzOyBhbmQvb3IgaWYgdGhpcyBpcyBhIGdvb2QvYmFkL2luZGlmZmVyZW50CmlkZWEuCgpBbHNvLCBqdXN0IGEgbm90ZSBmb3IgbWUgdG8gcmVtZW1iZXI6IFJQTDIyLCBub3QgUlBTMjIsIGZvciBzb21lCnJlYXNvbiBJIGtlZXAgdGhpbmtpbmcgdGhlIHNtYWxsIHN1YnVuaXQuCgojIyBQcm92ZSBJIHVuZGVyc3Rvb2QKCmBgYHtyfQpoZXRrZWVwZXJfdHggPC0gbW1fbm9ybWFsX3A4X3JldF9kZSRkZXNlcSRhbGxfdGFibGVzJHd0X3JldGluYV92c19oZXRfcmV0aW5hIHw+CiAgZmlsdGVyKGxvZ0ZDIDw9IC0wLjI1ICYgYWRqLlAuVmFsIDw9IDAuMDUpCnRlc3R0aGF0OjpleHBlY3RfdHJ1ZShucm93KGhldGtlZXBlcl90eCkgPT0gbGVuZ3RoKGluY3JlYXNlZF9oZXRfZ2VuZXMpKQp0YWFfa2VlcGVycyA8LSBzb3J0KHJvd25hbWVzKGhldGtlZXBlcl90eCkpCmF0Yl9rZWVwZXJzIDwtIHNvcnQoaW5jcmVhc2VkX2hldF9nZW5lcykKdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbCh0YWFfa2VlcGVycywgYXRiX2tlZXBlcnMpCmBgYAoKWWF5ISBJIGNhbiByZWFkISAgTm93IGxldCB1cyByZXBlYXQgZm9yIHRoZSBLTyB2cyB3dAoKYGBge3J9Cnd0X2tvX2tlZXBlciA8LSBsaXN0KCJrb192c193dCIgPSBjKCJrb19yZXRpbmEiLCAid3RfcmV0aW5hIikpCmtvX3d0X3RhYmxlIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIG1tX25vcm1hbF9wOF9yZXRfZGUsIGtlZXBlcnMgPSB3dF9rb19rZWVwZXIsIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwKICBleGNlbCA9ICIwM3RoZXJlc2FfY29tcGFyaXNvbl9leGNlbF90eC9rb19yZXRpbmFfY29udHJvbC54bHN4IikKd2FudGVkX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGtvX3d0X3RhYmxlLCBsZmMgPSAwLjI1LCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLAogIGV4Y2VsID0gIjAzdGhlcmVzYV9jb21wYXJpc29uX2V4Y2VsX3R4L2tvX3JldGluYV9jb250cm9sLXNpZy54bHN4IikKCndhbnRlZF9rb19pbmNyZWFzZWQgPC0gd2FudGVkX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1sia29fdnNfd3QiXV0KaW5jcmVhc2VkX2tvX2dlbmVzIDwtIHJvd25hbWVzKHdhbnRlZF9rb19pbmNyZWFzZWQpCmBgYAoKVGhlIG5leHQgdGhpbmcgcGVyZm9ybWVkIGluIFRoZXJlc2EncyBkb2N1bWVudCBpcyBhIHVuaXF1ZShjb25jYXRlbmF0aW9uIG9mCnRoZXNlIHR3byBnZW5lIGdyb3VwcyksIHRodXMgc3Vja2luZyB1cCBldmVyeSBnZW5lIHdoaWNoIHdhcwpzaWduaWZpY2FudGx5IGhpZ2hlciBpbiBlaXRoZXIgdGhlIGtub2Nrb3V0IF9vcl8gaGV0ZXJ6eW91cyBzYW1wbGVzCndpdGggcmVzcGVjdCB0byB3aWxkLXR5cGUuCgpUaGlzIHdhcyBmb2xsb3dlZCBieSBhIGNvdXBsZSBvZiBtZXJnZSBvcGVyYXRpb25zIG9mIGEgbGl0dGxlIGJpdCBvZgp0aGUgYW5ub3RhdGlvbiBkYXRhOyBJIGFtIG5vdCBzdXJlIEkgdW5kZXJzdGFuZCB0aGUgZ29hbCB5ZXQuLi4KCkhlcmUgaXMgaGVyIGNvZGUuIEkgY29waWVkIHRoZSBhbm5vdGF0aW9uICdtZ2lfc3ltYm9sJyBjb2x1bW4gdG8KJ2V4dGVybmFsX2dlbmVfbmFtZScgc28gdGhhdCBJIG5lZWQgbm90IGNoYW5nZSBhbnkgb2YgaGVyIGNvZGUuICBJIGFtCmFzc3VtaW5nIHRoaXMgaXMgdGhlIGFwcHJvcHJpYXRlIGNvbHVtbiBvZiBpbnRlcmVzdCwgSSBkbyBub3Qga25vdwp0aGlzIGZvciBjZXJ0YWluLCBidXQgaXQgc2VlbXMgcXVpdGUgbGlrZWx5LgoKV2hpbGUgSSBhbSBhdCBpdCwgaGVyZSBpcyB0aGUgc2V0X3NpZ19saW1tYSgpIGZ1bmN0aW9uIGZyb20gVGhlcmVzYSdzIGhlbHBlcnMuUgoKYGBge3J9CnNldF9zaWdfbGltbWEgPC0gZnVuY3Rpb24obGltbWFfdGJsLCBmYWN0b3JzID0gTlVMTCkgewogIGlmIChpcy5udWxsKGZhY3RvcnMpKSB7CiAgICAjc2V0IHNpZ25pZmljYW5jZSBmb3IgcGxvdHRpbmcgY29sb3JzCiAgICBsaW1tYV90YmwkU2lnbmlmaWNhbmNlIDwtIE5BCiAgICBsaW1tYV90YmxbYWJzKGxpbW1hX3RibCRsb2dGQykgPD0gMS4wIHwgbGltbWFfdGJsJGFkai5QLlZhbCA+PSAwLjA1LCAiU2lnbmlmaWNhbmNlIl0gPC0gIk5vdCBcbkVucmljaGVkIgogICAgbGltbWFfdGJsW2xpbW1hX3RibCRsb2dGQyA+PSAxLjAgICYgbGltbWFfdGJsJGFkai5QLlZhbCA8PSAwLjA1LCBdW1siU2lnbmlmaWNhbmNlIl1dIDwtICJEaXNlYXNlIFxuVXByZWd1bGF0ZWQiCiAgICBsaW1tYV90YmxbbGltbWFfdGJsJGxvZ0ZDIDw9IC0xLjAgICYgbGltbWFfdGJsJGFkai5QLlZhbCA8PSAwLjA1LCBdW1siU2lnbmlmaWNhbmNlIl1dIDwtICJEaXNlYXNlIFxuRG93bnJlZ3VsYXRlZCIKICAgIGxpbW1hX3RibCRTaWduaWZpY2FuY2UgPC0gZmFjdG9yKGxpbW1hX3RibCRTaWduaWZpY2FuY2UsIGxldmVscyA9IGMoIlVwcmVndWxhdGVkIiwgIkRvd25yZWd1bGF0ZWQiLCAgIk5vdCBcbkVucmljaGVkIikpCiAgfSBlbHNlIHsKICAgIGxpbW1hX3RibCRTaWduaWZpY2FuY2UgPC0gTkEKICAgIGxpbW1hX3RibFthYnMobGltbWFfdGJsJGxvZ0ZDKSA8PSAxLjAgfCBsaW1tYV90YmwkYWRqLlAuVmFsID49IDAuMDUsICJTaWduaWZpY2FuY2UiXSA8LSAiTm90IFxuRW5yaWNoZWQiCiAgICBpZihucm93KGxpbW1hX3RibFtsaW1tYV90YmwkbG9nRkMgPj0gMS4wICAmIGxpbW1hX3RibCRhZGouUC5WYWwgPD0gMC4wNSwgXSkgIT0gMCkgewogICAgICBsaW1tYV90YmxbbGltbWFfdGJsJGxvZ0ZDID49IDEuMCAgJiBsaW1tYV90YmwkYWRqLlAuVmFsIDw9IDAuMDUsIF1bWyJTaWduaWZpY2FuY2UiXV0gPC0gZmFjdG9yc1sxXQogICAgfQogICAgaWYgKG5yb3cobGltbWFfdGJsW2xpbW1hX3RibCRsb2dGQyA8PSAtMS4wICYgbGltbWFfdGJsJGFkai5QLlZhbCA8PSAwLjA1LCBdKSAhPSAwKSB7CiAgICAgIGxpbW1hX3RibFtsaW1tYV90YmwkbG9nRkMgPD0gLTEuMCAgJiBsaW1tYV90YmwkYWRqLlAuVmFsIDw9IDAuMDUsIF1bWyJTaWduaWZpY2FuY2UiXV0gPC0gZmFjdG9yc1syXQogICAgfQogICAgbGltbWFfdGJsJFNpZ25pZmljYW5jZSA8LSBmYWN0b3IobGltbWFfdGJsJFNpZ25pZmljYW5jZSwgbGV2ZWxzID0gYyhmYWN0b3JzLCAgIk5vdCBcbkVucmljaGVkIikpCiAgfQogIHJldHVybihsaW1tYV90YmwpCn0KYGBgCgojIyMgQ29tYmluaW5nIGhldC93dCBhbmQga28vd3QKCmBgYHtyfQprZWVwZXJ0eCA8LSB1bmlxdWUoYyhyb3duYW1lcyhoZXRrZWVwZXJfdHgpLCByb3duYW1lcyhrb2tlZXBlcl90eCkpKQpsZW5ndGgoa2VlcGVydHgpCmRmIDwtIG1tX2RlX25vcm1hbF9wOF9yZXQkZGVzZXEkYWxsX3RhYmxlcyRrb19yZXRpbmFfdnNfaGV0X3JldGluYSB8PgogIGRwbHlyOjptdXRhdGUobG9nRkMgPSAtbG9nRkMpIHw+CiAgc2V0X3NpZ19saW1tYShmYWN0b3JzID0gYygiSGV0IEVucmljaGVkIiwgIktPIEVucmljaGVkIikpCmBgYAoKTXkgdmVyc2lvbiBvZiB0aGUgYWJvdmUgdGFzayBtYWtlcyB1c2Ugb2YgdGhlIGV4Y2x1ZGVzIG9wdGlvbiBvZgpjb21iaW5lX2RlX3RhYmVzLiAgR2l2ZW4gdGhlIHNldCBvZiB1bmlxdWUgZ2VuZSBJRHMgaW5jcmVhc2VkIGluIHRoZQpoZXQva28sIEkgY2FuIGFzayB0byBleGx1ZGUgYW55dGhpbmcgbm90IGluIHRoYXQgc2V0LiAgSSBjb3VsZCBhbHNvCmhhdmUgbW9yZSBwYXJzaW1vbmlvdXNseSBkaXJlY3RseSBleGNsdWRlZCBhbnkgZ2VuZSBJRCBpbmNyZWFzZWQgaW4KdGhlIHd0IHNhbXBsZXMuICBCdXQsIFRoZXJlc2EgYWxyZWFkeSBwcm92aWRlZCB0aGUgY29kZSB0byBkbyB0aGUKZm9ybWVyLCBzbyBpdCB3aWxsIGJlIGxlc3MgdHlwaW5nL29wcG9ydHVuaXR5IGZvciBzaWxseSBtaXN0YWtlcyB0bwpqdXN0IGRvIHRoYXQuCgpgYGB7cn0KYm90aF9pbmNyZWFzZWRfZ2VuZXMgPC0gdW5pcXVlKGMoaW5jcmVhc2VkX2hldF9nZW5lcywgaW5jcmVhc2VkX2tvX2dlbmVzKSkKIyMgYXJiaXRyYWlybHkgZ3JhYiBhbGwgZ2VuZXMgZnJvbSBvbmUgb2YgbXkgZGF0YSBzdHJ1Y3R1cmVzLgphbGxfZ2VuZXMgPC0gcm93bmFtZXMoYXNzYXkobW0zOF9oaXNhdF90eCkpCmV4Y2x1ZGVfaWR4IDwtIGFsbF9nZW5lcyAlaW4lIGJvdGhfaW5jcmVhc2VkX2dlbmVzCnN1bW1hcnkoZXhjbHVkZV9pZHgpCmBgYAoKYGBge3J9CmV4Y2x1ZGVfaW5jcmVhc2VkX2dlbmVzIDwtIGFsbF9nZW5lc1tleGNsdWRlX2lkeF0KcmV0aW5hX2tlZXBlcnMgPC0gbGlzdCgKICAiaGV0X3ZzX3d0IiA9IGMoImhldF9yZXRpbmEiLCAid3RfcmV0aW5hIiksCiAgImtvX3ZzX3d0IiA9IGMoImtvX3JldGluYSIsICJ3dF9yZXRpbmEiKSwKICAia29fdnNfaGV0IiA9IGMoImtvX3JldGluYSIsICJoZXRfcmV0aW5hIikpCiMjIEEgcmVtaW5kZXIgdG8gbXlzZWxmOiB0aGVyZSBpcyBhbHNvIGEgcGFyYW1ldGVyICd3YW50ZWRfZ2VuZXMnCiMjIHdoaWNoIGRvZXMgZWZmZWN0aXZlbHkgdGhlIHNhbWUgdGhpbmcgYXMgZXhjbHVkZXMgaW4gdGhpcyBjb250ZXh0OwojIyBleGNsdWRlcyB3YXMgb3JpZ2luYWxseSB3cml0dGVuIHRvIGFsbG93IGZsZXhpYmxlLCBrZXl3b3JkLWJhc2VkCiMjIGV4Y2x1c2lvbi4KcDhfcmV0aW5hX3RhYmxlcyA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBtbV9ub3JtYWxfcDhfcmV0X2RlLCBrZWVwZXJzID0gcmV0aW5hX2tlZXBlcnMsCiAgd2FudGVkX2dlbmVzID0gYm90aF9pbmNyZWFzZWRfZ2VuZXMsIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoIjAzdGhlcmVzYV9jb21wYXJpc29uX2V4Y2VsX3R4L3A4X3JldGluYV9rZXB0X3R4X2luY3JlYXNlZF9pbl93dF90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKCnA4X3JldGluYV9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBwOF9yZXRpbmFfdGFibGVzLCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLAogIGV4Y2VsID0gZ2x1ZSgiMDN0aGVyZXNhX2NvbXBhcmlzb25fZXhjZWxfdHgvcDhfcmV0aW5hX2tlcHRfZ2VuZXNfaW5jcmVhc2VkX2luX3d0X3NpZy12e3Zlcn0ueGxzeCIpKQoKb3Bwb3NpdGVfcDhfcmV0aW5hX3RhYmxlcyA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBtbV9ub3JtYWxfcDhfcmV0X2RlLCBrZWVwZXJzID0gcmV0aW5hX2tlZXBlcnMsCiAgZXhjbHVkZXMgPSBib3RoX2luY3JlYXNlZF9nZW5lcywgbGFiZWxfY29sdW1uID0gdHhfbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgiMDN0aGVyZXNhX2NvbXBhcmlzb25fZXhjZWxfdHgvcDhfcmV0aW5hX3JlbW92ZWRfZ2VuZXNfaW5jcmVhc2VkX2luX3d0X3RhYmxlcy12e3Zlcn0ueGxzeCIpKQoKb3Bwb3NpdGVfcDhfcmV0aW5hX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHA4X3JldGluYV90YWJsZXMsIGFjY29yZGluZ190byA9ICJkZXNlcSIsCiAgZXhjZWwgPSBnbHVlKCIwM3RoZXJlc2FfY29tcGFyaXNvbl9leGNlbF90eC9wOF9yZXRpbmFfcmVtb3ZlZF9nZW5lc19pbmNyZWFzZWRfaW5fd3Rfc2lnLXZ7dmVyfS54bHN4IikpCmBgYAoKIyBGaWx0ZXJpbmcgb3V0IG5vbi1zcGVjaWZpYyBnZW5lcyBhbmQgZXhhbWluaW5nIHRoZSByZXN1bHRzCgpUaGUgZm9sbG93aW5nIGlzIGEgY29weS9wYXN0ZSBmcm9tIFRoZXJlc2EgY29udGFpbmluZyB0aGUgcmVtYWluaW5nCnRhc2tzIHNoZSBwZXJmb3JtZWQgYW5kIHdpbGwgcHJvdmlkZSB0aGUgdGVtcGxhdGUgZm9yIGltcGxlbWVudGF0aW9uCm9mIHRoZSBmaW5hbCB0YXNrcy4KClRoaXMgcGlja3MgdXAgd2l0aCB0aGUgbGluZXMgZnJvbSBoZXIgbm90ZWJvb2sgaW1tZWRpYXRlbHkgZm9sbG93aW5nCnRoZSBpbnZvY2F0aW9uIG9mICdzZXRfc2lnX2xpbW1hKGZhY3RvcnMgPSBjKCJIZXQgRW5yaWNoZWQiIC4uLicuCgpGb3IgYWxsIG9mIHRoZSByZW1haW5pbmcgYmxvY2tzIEkgd2lsbCBjb3B5IGluIGhlciBjb2RlLCB0dXJuIG9mZiBpdHMKZXZhbHVhdGlvbiwgcnVuIHRoZSBibG9ja3MgbWFudWFsbHksIGNvbXBhcmUgdGhlbSB0byBoZXIgbm90ZWJvb2sKb3V0cHV0LCB0aGVuIGVuYWJsZSBlYWNoIGJsb2NrIGFzIEkgZW5zdXJlIEkgdW5kZXJzdGFuZCBpdC4KCkkgd2lsbCBsaWtlbHkgdGhlcmVmb3JlIGludHJvZHVjZSBzb21lIHNtYWxsIGZvcm1hdHRpbmcgY2hhbmdlcyBhbmQKYWRkIHNvbWUgYWRkaXRpb25hbCBHU0VBL2VucmljaG1lbnQgdGFza3Mgb25jZSB0aGUgbm9uLXNwZWNpZmljCmZpbHRlcmluZyBpcyBjb21wbGV0ZS4KCmBgYHtyfQpkZiA8LSBkZiB8PgogIGZpbHRlcihyb3duYW1lcyhkZikgJWluJSBrZWVwZXJ0eCkKbGFiZWxzX3VwcyA8LSBkZiB8PgogIGZpbHRlcihhZGouUC5WYWwgPD0gMC4wNSAmIGFicyhsb2dGQykgPj0gMS4wKSB8PgogIGFycmFuZ2UobG9nRkMpIHw+CiAgaGVhZChuID0gOSkKbGFiZWxzX2Rvd25zIDwtIGRmIHw+CiAgZmlsdGVyKGFkai5QLlZhbCA8PSAwLjA1ICYgYWJzKGxvZ0ZDKSA+PSAxLjApIHw+CiAgYXJyYW5nZSgtbG9nRkMpIHw+CiAgaGVhZChuID0gMTEpCmxhYmVscyA8LSByYmluZChsYWJlbHNfdXBzLCBsYWJlbHNfZG93bnMpCnJlc190YmwgPC0gZGYKREVwbG90IDwtIGdncGxvdChyZXNfdGJsLCBhZXMoeCA9IGxvZ0ZDLCB5ID0gLWxvZzEwKGFkai5QLlZhbCkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gU2lnbmlmaWNhbmNlKSwgc2l6ZSA9IDQpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC0xLCAxKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMjApICsKICB4bGFiKCJsb2cyKEZDKSIpICsKICB5bGFiKCItbG9nMTAocC12YWx1ZSkiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRjg3NjZEIiwgIiMwMEJGQzQiLCAiR3JleSIpKSArCiAgeGxpbShjKC0zLCA2KSkKCnBwKGZpbGUgPSAiMDN0aGVyZXNhX2NvbXBhcmlzb25faW1hZ2VzX3R4L3AwOF9yZXRpbmFfREVfMTMxMjAyNC5wZGYiKQpERXBsb3QKcGxvdHRlZCA8LSBkZXYub2ZmKCkKREVwbG90CndyaXRlX3hsc3goZGYsIGV4Y2VsID0gImV4Y2VsX3R4L3JldGluYWhldF92c19yZXRpbmFrb19XVGZpbHRlcmVkLnhsc3giKQpgYGAKCiMjIEhvdyBtYW55IHVwcy9kb3ducwoKYGBge3J9CmtvX2VucmljaGVkIDwtIGRmIHw+CiAgZmlsdGVyKFNpZ25pZmljYW5jZSA9PSAiS08gRW5yaWNoZWQiKQpucm93KGtvX2VucmljaGVkKQpoZXRfZW5yaWNoZWQgPC0gIGRmIHw+CiAgZmlsdGVyKFNpZ25pZmljYW5jZSA9PSAiSGV0IEVucmljaGVkIikKbnJvdyhoZXRfZW5yaWNoZWQpCmBgYAoKIyMgY2F0ZWdvcnkgZW5yaWNobWVudC9HU0VBCgpgYGB7cn0KcmVndWxhdGVkX2dlbmVzIDwtIHJlc190YmwgfD4KICBmaWx0ZXIoYWRqLlAuVmFsIDw9IDAuMDUpIHw+CiAgYXJyYW5nZShsb2dGQykgfD4KICBmaWx0ZXIoYWJzKGxvZ0ZDKSA+PSAxLjApCiMjIGdzZWFfcmVzdWx0X2tvIDwtIGdvc3QocXVlcnkgPSBrb19nZW5lcyRleHRlcm5hbF9nZW5lX25hbWUsCiMjICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAibW11c2N1bHVzIiwKIyMgICAgICAgICAgICAgICAgICAgICAgICBldmNvZGVzID0gVFJVRSwKIyMgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkX3F1ZXJ5ID0gVFJVRSkKZ3NlYV9yZXN1bHRfaGV0IDwtIGdvc3QocXVlcnkgPSBoZXRfZW5yaWNoZWRbWyJSb3cubmFtZXMiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gIm1tdXNjdWx1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGV2Y29kZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkX3F1ZXJ5ID0gVFJVRSkKIyNnc2VhX3Jlc3VsdF9hbGxkeXNyZWd1bGF0ZWQgPC0gZ29zdChxdWVyeSA9IGFsbGR5c3JlZ3VsYXRlZF9nZW5lcyRleHRlcm5hbF9nZW5lX25hbWUsCiMjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAibW11c2N1bHVzIiwKIyMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmNvZGVzID0gVFJVRSwKIyMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkX3F1ZXJ5ID0gVFJVRSkKYGBgCgpJIGhhdmUgYSBmdW5jdGlvbiBpbiBteSBwYWNrYWdlIHdoaWNoIHNlZWtzIHRvIG1ha2UgZ1Byb2ZpbGVyIHF1ZXJpZXMKYSBiaXQgbW9yZSBjb21wbGV0ZSBhbmQgZWFzeS4gIExldCB1cyBzZWUgaG93IHNpbWlsYXIgdGhlIHJlc3VsdCBpcy4uLgoKYGBge3IsIGV2YWw9RkFMU0V9CnJvd25hbWVzKGFsbGR5c3JlZ3VsYXRlZF9nZW5lcykgPC0gYWxsZHlzcmVndWxhdGVkX2dlbmVzW1siUm93Lm5hbWVzIl1dCmFsbGR5c3JlZ3VsYXRlZF9nZW5lc1tbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCgpoZXRfZ3AgPC0gc2ltcGxlX2dwcm9maWxlcihyb3duYW1lcyhhbGxkeXNyZWd1bGF0ZWRfZ2VuZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzcGVjaWVzID0gIm1tdXNjdWx1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsID0gZ2x1ZSgiZXhjZWxfdHgvaGV0X2dwcm9maWxlci12e3Zlcn0ueGxzeCIpKQpoZXRfZ3AKZW5yaWNocGxvdDo6ZG90cGxvdChoZXRfZ3BbWyJCUF9lbnJpY2giXV0pCmdwX3BhaXIgPC0gZW5yaWNocGxvdDo6cGFpcndpc2VfdGVybXNpbShoZXRfZ3BbWyJCUF9lbnJpY2giXV0pCmVucmljaHBsb3Q6OmVtYXBwbG90KGdwX3BhaXIpCmVucmljaHBsb3Q6OnNzcGxvdChncF9wYWlyKQplbnJpY2hwbG90Ojp0cmVlcGxvdChncF9wYWlyKQp1cHNldHBsb3QoaGV0X2dwW1siQlBfZW5yaWNoIl1dKQoKZW5yaWNocGxvdDo6ZG90cGxvdChoZXRfZ3BbWyJSRUFDX2VucmljaCJdXSkKZ3BfcGFpciA8LSBlbnJpY2hwbG90OjpwYWlyd2lzZV90ZXJtc2ltKGhldF9ncFtbIlJFQUNfZW5yaWNoIl1dKQplbnJpY2hwbG90OjplbWFwcGxvdChncF9wYWlyKQplbnJpY2hwbG90Ojpzc3Bsb3QoZ3BfcGFpcikKZW5yaWNocGxvdDo6dHJlZXBsb3QoZ3BfcGFpcikKdXBzZXRwbG90KGhldF9ncFtbIlJFQUNfZW5yaWNoIl1dKQpgYGAKCkkgbWFrZSBhIHNvbWV3aGF0IGFyYml0cmFyeSBkaXN0aW5jdGlvbiBiZXR3ZWVuIHRoZSBjb25jZXB0cyBvZgpvdmVyLWVucmljaG1lbnQgYW5hbHlzZXMgYW5kIEdTRUE6IHRoZSBmb3JtZXIgKGFzIHBlcmZvcm1lZCBieQpncHJvZmlsZXIpIChAcmF1ZHZlcmVQcm9maWxlcldlYlNlcnZlcjIwMTkpIHNlZWtzIHRvIGZpbmQgZ3JvdXBzIG9mCmdlbmVzIG92ZXJyZXByZXNlbnRlZCBpbiBHTy9yZWFjdG9tZS9ldGMuICBUaGVzZSBncm91cHMgb2YgZ2VuZXMgYXJlCnRha2VuIGV4Y2x1c2l2ZWx5IGZyb20gdGhlIHRvcC1uL2JvdHRvbS1uIGdlbmVzIHdpdGggcmVzcGVjdCB0bwpmb2xkLWNoYW5nZSBiZXR3ZWVuIGNvbmRpdGlvbnMgb2YgaW50ZXJlc3Q7IGluIHRoaXMgY2FzZSBtb3N0CmRpZmZlcmVudCB0aGFuIHd0IGluIHRoZSBwMDggcmV0aW5hIGtvIG9yIGhldCBzYW1wbGVzLgoKV2l0aCB0aGF0IGluIG1pbmQsIEkgY2FuIGludm9rZSBhIHNpbWlsYXIgZnVuY3Rpb24gdXNpbmcgdGhlIGZ1bGwKdGFibGUgb2YgREUgcmVzdWx0cyB0byBnZXQgd2hhdCBJIGNhbGwgdGhlIEdTRUEgcmVzdWx0IHVzaW5nCmNsdXN0ZXJQcm9maWxlciAoQHl1SW50cm9kdWN0aW9uQmlvbWVkaWNhbEtub3dsZWRnZSkuICBJbiB0aGUKZm9sbG93aW5nIGJsb2NrIEkgd2lsbCB1c2UgdGhlICdhbGxfY3Byb2ZpbGVyJyBmdW5jdGlvbiBvbiB0aGUgZGF0YQpzdHJ1Y3R1cmVzIG5hbWVkICdwOF9yZXRpbmFfdGFibGVzJyBhbmQgJ29wcG9zaXRlX3A4X3JldGluYV90YWJsZXMnIGluCm9yZGVyIHRvIGdldCB0aGVzZSBHU0VBIHJlc3VsdHMgZm9yIGVhY2ggY29udHJhc3QgcGVyZm9ybWVkIChoZXQvd3QsCmtvL3d0LCBoZXQva28pLiAgSSB3aWxsIGZvbGxvdyB0aGF0IHVwIHdpdGggJ2FsbF9ncHJvZmlsZXInIHdoaWNoIGRvZXMKdGhlIHNhbWUsIGJ1dCB1c2VzIGdQcm9maWxlcidzIGVucmljaG1lbnQgYW5hbHlzZXMgKGl0IHdpbGwgdGhlcmVmb3JlCmluY2x1ZGUgd2hhdCB3ZSBqdXN0IGxvb2tlZCBhdCkuCgpgYGB7cn0KcDA4X3JldGluYV9hbGxfY3AgPC0gYWxsX2Nwcm9maWxlcigKICBwOF9yZXRpbmFfc2lnLCBwOF9yZXRpbmFfdGFibGVzLCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBvcmdkYl9mcm9tID0gb3JnZGJfZnJvbSwKICBleGNlbCA9ICIwM3RoZXJlc2FfY29tcGFyaXNvbl9leGNlbF90eC9jcHJvZmlsZXJfcDA4X3JldGluYS54bHN4IikKCmVucmljaHBsb3Q6OmRvdHBsb3QocDA4X3JldGluYV9hbGxfY3BbWyJrb192c19oZXRfdXAiXV1bWyJnb19kYXRhIl1dW1siTUZfZW5yaWNoIl1dKQpwMDhfdG9wbl9nc2VhIDwtIHBsb3RfdG9wbl9nc2VhKHAwOF9yZXRpbmFfYWxsX2NwKQoKcHAoZmlsZSA9ICIwM3RoZXJlc2FfY29tcGFyaXNvbl9pbWFnZXNfdHgvZ3NlYV9wMDhfcmV0aW5hX2tvX3ZzX2hldF90b3BfaGl0LnBkZiIsKQpwMDhfdG9wbl9nc2VhW1siR09fa29fdnNfaGV0X3VwIl1dW1sxXV0KZGV2Lm9mZigpCgpwMDhfdG9wbl9nc2VhW1siR09fa29fdnNfaGV0X3VwIl1dW1sxXV0KcDA4X3RvcG5fZ3NlYVtbIkdPX2tvX3ZzX2hldF91cCJdXVtbMl1dCnAwOF90b3BuX2dzZWFbWyJHT19rb192c19oZXRfdXAiXV1bWzNdXQpwMDhfdG9wbl9nc2VhW1siR09fa29fdnNfaGV0X3VwIl1dW1s0XV0KcDA4X3RvcG5fZ3NlYVtbIkdPX2tvX3ZzX2hldF91cCJdXVtbNV1dCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CiNnc2VhX2tvIDwtICBnc2VhX3Jlc3VsdF9rb1tbInJlc3VsdCJdXSB8PgojICAgIHNlbGVjdCh0ZXJtX25hbWUsIHBfdmFsdWUsIHRlcm1fc2l6ZSwgaW50ZXJzZWN0aW9uX3NpemUsIHJlY2FsbCwgc291cmNlLCBpbnRlcnNlY3Rpb24pIHw+CiMgICAgYXJyYW5nZShkZXNjKHJlY2FsbCkpIHw+CiMgICAgaGVhZChuID0gMTApCiMgIGdzZWFfcGxvdHNfa28gPC0gZ2dwbG90KGdzZWFfa28sIGFlcyh4ID0gcmVjYWxsLCB5ID0gcmVvcmRlcih0ZXJtX25hbWUsIHJlY2FsbCksIGZpbGwgPSBwX3ZhbHVlKSkgKwojICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrCiMgIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikgKwojICB0aGVtZV9idygpKwojICB5bGFiKCIiKSArCiMgIHhsYWIoIkdTRUEgU2NvcmUiKQpnc2VhX2hldCA8LSAgZ3NlYV9yZXN1bHRfaGV0W1sicmVzdWx0Il1dIHw+CiAgZHBseXI6OnNlbGVjdCh0ZXJtX25hbWUsIHBfdmFsdWUsIHRlcm1fc2l6ZSwgaW50ZXJzZWN0aW9uX3NpemUsIHJlY2FsbCwgc291cmNlLCBpbnRlcnNlY3Rpb24pIHw+CiAgYXJyYW5nZShkZXNjKHJlY2FsbCkpIHw+CiAgaGVhZChuID0gMTApCmdzZWFfcGxvdHNfaGV0IDwtIGdncGxvdChnc2VhX2hldCwgYWVzKHggPSByZWNhbGwsIHkgPSByZW9yZGVyKHRlcm1fbmFtZSwgcmVjYWxsKSwgZmlsbCA9IHBfdmFsdWUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX2NvbnRpbnVvdXMobG93ID0gImJsdWUiLCBoaWdoID0gInJlZCIpICsKICB0aGVtZV9idygpICsKICB5bGFiKCIiKSArCiAgeGxhYigiT3ZlciBSZXByZXNlbnRhdGlvbiBTY29yZSIpCnBwKGZpbGUgPSAiMDN0aGVyZXNhX2NvbXBhcmlzb25faW1hZ2VzX3R4L0dTRUFfcDA4X2F4b250cmFwX3JldGluYWhldF91cHJlZ3VsYXRlZF92c19yZXRpbmFrby5wZGYiKQpnc2VhX3Bsb3RzX2hldApwbG90dGVkIDwtIGRldi5vZmYoKQpnc2VhX3Bsb3RzX2hldApgYGAKCiMgU0NOIEhldCB2cyBLTwoKSXQgaXMgb25seSBub3cgdGhhdCBJIHJlYWxpemVkIHdlIGFyZSBzcGxpdHRpbmcgdGhlIGRhdGEgYnkgbG9jYXRpb24KZm9yIGVhY2ggc2V0IG9mIGNvbXBhcmlzb25zLiAgSSB0aGluayB0aGF0LCBsZWZ0IHRvIG15IG93biBkZXZpY2VzLCBJCndvdWxkIHByZWZlciB0byBrZWVwIHRoZSBpbnB1dCBkYXRhIHN0cnVjdHVyZSBpbnRhY3QsIHBlcmZvcm0gdGhlCnNvbWV3aGF0IGxhcmdlciBudW1iZXIgb2YgY29udHJhc3RzLCBhbmQgdGhlbiBzcGxpdCB1cCB0aGUgcmVzdWx0cy4KSWRlYWxseSB0aGlzIHdpbGwgc2xpZ2h0bHkgaW1wcm92ZSB0aGUgZmlkZWxpdHkgb2YgdGhlIHJlc3VsdHMKcmV0dXJuZWQgYnkgREVTZXEyIGFuZCBmcmllbmRzLiAgQnV0LCBJIHdpbGwgcnVuIHRoZSBzdGF0ZSBvZgpUaGVyZXNhJ3Mgbm90ZWJvb2sgd2l0aCBhcyBmZXcgY2hhbmdlcyBhcyBwb3NzaWJsZSBmaXJzdCwgdGhlbiBhZGQKdGhpcy4KCiMjIFBDQQoKSSBhbSBnb2luZyB0byBza2lwIHRoaXMgUENBIHBsb3QgZm9yIGEgY291cGxlIG9mIHJlYXNvbnM6IEkgYWxyZWFkeQpkaWQgYSBzdXBlcnNldCBvZiBpdCwgYW5kIHRoZSBzdWJzZXQgVGhlcmVzYSBwZXJmb3JtZWQgaXMgbm90IHZhbGlkCmdpdmVuIHRoZSBzZXQgb2Ygc2FtcGxlcyBpbmNsdWRlZCBpbiBteSBzYW1wbGUgc2hlZXQsIGFuZCBmaWd1cmluZyBvdXQKdGhlIGFjdHVhbGx5IGNvcnJlc3BvbmRpbmcgc3Vic2V0IHdpbGwgdGFrZSBtZSBmb3JldmVyLi4uICBJbgphZGRpdGlvbiwgSSB3YW50IHRvIHVzZSBteSBtbTM4X2hpc2F0X3R4IGZvciBldmVyeXRoaW5nLi4uCgoKYGBge3J9CnNjbl9zYW1wbGVzIDwtIHN1YnNldF9zZShtbTM4X2hpc2F0X3R4LAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSAibG9jYXRpb25fYXRiID09ICdzY24nIikgfD4KICBzZXRfYmF0Y2hlcyhmYWN0ID0gImxvY2F0aW9uX2F0YiIpIHw+CiAgc2V0X2NvbmRpdGlvbnMoZmFjdCA9ICJnZW5vdHlwZV9hdGIiLCBjb2xvcnMgPSBjb2xvcl9jaG9pY2VzW1siZ2Vub3R5cGUiXV0pCnNjbl9ub3JtIDwtIG5vcm1hbGl6ZShzY25fc2FtcGxlcywgZmlsdGVyID0gVFJVRSwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2Zvcm0gPSAibG9nMiIsIGJhdGNoID0gInN2YXNlcSIpCnNjbl9ub3JtX3BjYSA8LSBwbG90X3BjYShzY25fbm9ybSkKc2NuX25vcm1fcGNhCmBgYAoKIyBMaWJyYXJ5IHNpemVzIHBvc3QtZGVkdXBsaWNhdGlvbgoKVGhlcmVzYSdzIG5leHQgb3BlcmF0aW9uIHdhcyB0byBwZXJmb3JtIGxpYnNpemUvbm9uemVybyBwbG90cy4gIEkKYWxyZWFkeSBkaWQgdGhlIHByZS9wb3N0IGRlZHVwbGljYXRpb24gbm9uemVybywgaGVyZSBpcyB0aGUgYW5hbGFnb3VzCmxpYnNpemUuCgp2MiBpcyBwcmUtZGVkdXBsaWNhdGlvbiBhbmQgdjMgaXMgcG9zdC4KCmBgYHtyfQpwb3N0X2ZpbHRlcl9ub256ZXJvIDwtIHBsb3RfcXVhbnRyZWFkcyhtbTM4X2hpc2F0X3R4LCB0ZXh0ID0gRkFMU0UpCnBwKGZpbGUgPSAiMDFkaWFnbm9zdGljX2ltYWdlc190eC9wb3N0X2FsbF9maWx0ZXJlc19ub256ZXJvLnBkZiIpCnBvc3RfZmlsdGVyX25vbnplcm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnBvc3RfZmlsdGVyX25vbnplcm8KYGBgCgpUaGVyZXNhIGFsc28gcHJvZHVjZWQgYSBkZW5zaXR5L3NhbXBsZSBwbG90LCB0aGF0IG1pZ2h0IHByb3ZlIHF1aXRlCnVzZWZ1bCBmb3IgdGhlc2UgZHVlIHRvIHRoZWlyIHNpZ25pZmljYW50bHkgbGFyZ2VyIHZhcmlhbmNlIGFjcm9zcwpzYW1wbGVzIChkdWUgdG8gZGVkdXBsaWNhdGlvbikuCgpgYGB7cn0KcHAoZmlsZSA9ICIwMWRpYWdub3N0aWNfaW1hZ2VzX3R4L3NhbXBsZV9kZW5zaXR5LnBkZiIpCm1tMzhfZGVuc2l0eSA8LSBwbG90X2RlbnNpdHkobG9jX2dlbm9fbnQpCm1tMzhfZGVuc2l0eVtbInBsb3QiXV0gKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKcGxvdF9ib3hwbG90KGxvY19nZW5vX250KQpkZXYub2ZmKCkKbW0zOF9kZW5zaXR5W1sicGxvdCJdXSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpib3ggPC0gcGxvdF9ib3hwbG90KGxvY19nZW5vX250KQpwcChmaWxlID0gIjAxZGlhZ25vc3RpY19pbWFnZXNfdHgvc2FtcGxlX2JveHBsb3QucGRmIikKYm94CmRldi5vZmYoKQpib3gKYGBgCgpUaGVyZSBpcyBzb21lIGRpZmZlcmVuY2UgYWNyb3NzIHNhbXBsZSBkZW5zaXRpZXMsIGJ1dCBpdCBpcyBub3QgdG9vCmNyYXp5dG93bi4KCiMgRGl2ZXJnaW5nIGEgbGl0dGxlCgpBdCB0aGlzIHBvaW50IGluIHRoZSBkb2N1bWVudCBJIHJlYWQgYWhlYWQgYSBiaXQgYW5kIGNhbWUgdG8gdGhlCmNvbmNsdXNpb24gdGhhdCBpdCByZXBlYXRzIHRoZSBhYm92ZSBsb2dpYyBvZiB0YWtpbmcgdGhlIHVuaW9uIG9mIHd0CmNvbXBhcmlzb25zIHRvIHJlbW92ZSBnZW5lcyBmcm9tIHRoZSBhcHByb3ByaWF0ZSBoZXQva28gb3IgcDE1L3AwOCBvcgpsb2NhdGlvbiBjb21wYXJpc29ucy4gIFRoaXMgc2VlbXMgcXVpdGUgcmVhc29uYWJsZSB0byBtZSwgYnV0IEkgd291bGQKcHJlZmVyIHRvIG5vdCBzZXBhcmF0ZSBhbGwgdGhlIGRhdGEsIHNvIEkgd2lsbCBhdHRlbXB0IHRvIGR1cGxpY2F0ZQphbmQgc2xpZ2h0bHkgc3RyZWFtbGluZSB0aGlzIGxvZ2ljIG9uIHRoZSBmdWxsIGRhdGFzZXQuICBUaHVzIEkgYW0KZ29pbmcgdG8gc2tpcCBkb3duIHRvIHRoZSBlbmQgYW5kIGF0dGVtcHQgdG8gaW1wbGVtZW50IHRoaXMuCgojIE15IHZlcnNpb24gb2YgdGhlIGdsb2JhbCBhbmFseXNpcwoKSSB3YW50IHRvIGhhdmUgYW4gaW52b2NhdGlvbiBvZiBhbGxfcGFpcndpc2UoKSB3aGljaCB1c2VzIGFsbCBzYW1wbGVzLAppbiB0aGUgZm9sbG93aW5nIGJsb2NrIEkgd2lsbCBzZXQgdGhhdCB1cCB1c2luZyBhIHNldCBvZiAna2VlcGVycycKd2hpY2ggd2lsbCBiZSBuYW1lZCBieSB0aW1lLCBsb2NhdGlvbiwgdGhlbiAyIGxldHRlcnMgZm9yIHRoZQpudW1lcmF0b3IvZGVub21pbmF0b3I6IHcgZm9yIFdULCBoIGZvciBoZXQsIGQgZm9yIGRlbHRhOyB0aHVzCiJwMDhfcmV0aW5hX2h3IiBpcyBjb21wYXJpbmcgdGhlIGhldC93dCBmb3IgdGhlIHAwOCByZXRpbmEgc2FtcGxlcy4KCklmIHRoZXkgYXJlIG9mIGludGVyZXN0LCBJIHdpbGwgaGF2ZSBhIHNlcGFyYXRlIHNldCB3aGljaCBmb2xsb3dzIHRoZQpzYW1lIGNvbnZlbnRpb24gd2l0aCBuYW1lcyBsaWtlICJwMDhfa29fc3IiIHRvIGNvbXBhcmUgcDA4IGRlbHRhcyB3aXRoClNDTiBhcyB0aGUgbnVtZXJhdG9yIGFuZCByZXRpbmEgYXMgdGhlIGRlbm9taW5hdG9yLgoKIyMgU2V0IHVwIHRoZSBleGNsdXNpb24gZGF0YXNldAoKVGhlIG1vc3QgcGVjdWxpYXIgYXNwZWN0IG9mIHRoaXMgYW5hbHlzaXMgcmVzaWRlcyBpbiB0aGUgY2hvaWNlcwphcm91bmQgY2hvb3Npbmcgd2hpY2ggZ2VuZXMgdG8gY29uc2lkZXIgd2hlbiBjb21wYXJpbmcgdGhlCmdlbm90eXBlcy9sb2NhdGlvbnMvdGltZXMuICBUaGUgZ2VuZXJhbCBpZGVhIGlzIHByZXR0eSBjbGVhcjogZmluZCB0aGUKZ2VuZXMgd2hpY2ggYXJlIG5vbi1zcGVjaWZpY2FsbHkgYmVpbmcgcHVsbGVkIGRvd24gaW4gdGhlIFdUIHNhbXBsZXMKYW5kIGVpdGhlciBleGNsdWRlIG9yIGRpc2NvdW50IHRoZW0uICBUaGUgdmFyaW91cyBwb3RlbnRpYWwgbWV0aG9kcwpmb3IgcGVyZm9ybWluZyB0aGlzIGFyZSBjb25mdXNpbmc6CgoxLiAgV2hpY2ggc2V0IG9mIGNvbXBhcmlzb25zIG9mIHd0L2tvIHd0L2hldCBkbyB3ZSB1c2UgdG8KICAgIGV4Y2x1ZGUvZGlzY291bnQgZ2VuZXM/CiAgICBhLiAgU2hvdWxkIGl0IGJlIGEgY29tYmluYXRpb24gb2YgYWxsIHNhbXBsZXMgd3QgdnMuIHg/CiAgICBiLiAgU2hvdWxkIGl0IGJlIG9ubHkgdGhlICdyZWxldmFudCcgY29tcGFyaXNvbiwgZS5nLiBpZiB3ZSBhcmUKICAgIGNvbXBhcmluZyBwMDhfZGxnbl9oZXQgdnMuIHAwOF9zY25faGV0OyBkbyB3ZSByZW1vdmUgZ2VuZXMKICAgIG9ic2VydmVkIGluIChwMDhfZGxnbl9oZXQvd3QgJiYgcDA4X3Njbl9oZXQvd3QpCjIuICBEbyB3ZSBpbnN0ZWFkIGF0dGVtcHQgdG8gdXNlIHRoaXMgeC93dCBpbmZvcm1hdGlvbiB0byBub3JtYWxpemUKICAgIHRoZSBleHByZXNzaW9uIHZhbHVlcyBpbiB0aGUgb3RoZXIgY29uZGl0aW9ucyBhbmQga2VlcCB0aG9zZQogICAgZ2VuZXM/CgpUaGVyZXNhJ3MgY3VycmVudCB3b3Jrc2hlZXQgaW1wbGVtZW50cyBhIHZlcnNpb24gb2YgMWIgaW4gd2hpY2ggc2hlCnNlcGFyYXRlZCB0aGUgdmFyaW91cyBpbnB1dCBnZW5lIHNldHMgdG8gZGVmaW5lIHRoZSBleGNsdXNpb24gZ2VuZXMuCkkgYW0gZ29pbmcgdG8gcmVwZWF0IHRoaXMsIGJ1dCBsZWF2ZSB0aGUgc3RhcnRpbmcgZGF0YSBzdHJ1Y3R1cmUKaW50YWN0LgoKSW4gdGhpcyBmaXJzdCBpdGVyYXRpb24sIEkgd2lsbCBkbyB0aGF0IGJ5IGNyZWF0aW5nIGEgc2ltcGxpZmllZCBtb2RlbApvZiB0aGUgZGF0YSB3aGljaCBjb21iaW5lcyB0aGUgdGltZS9nZW5vdHlwZS9sb2NhdGlvbiBhbmQgdXNpbmcgc3ZhLgpJbiBteSBuZXh0IGl0ZXJhdGlvbiBJIHdpbGwgdXNlIGEgZnVsbCBzdGF0aXN0aWNhbCBtb2RlbCBjb250YWluaW5nCmVhY2ggb2YgdGhvc2UgZmFjdG9ycyAoYW5kIHByb2JhYmx5IGFsc28gdXNpbmcgc3ZhKS4KCk5vdGU6IG15IGNvbG9yIGNob2ljZXMgYXJlIGtpbmQgb2YgZ2FyYmFnZS4KCkluIGFkZGl0aW9uLCB0aGUgZXhjbHVzaW9uIGRhdGFzZXQgaXMgdGhlIHNhbWUgYXMgdGhlIGFuYWx5c2lzCmRhdGFzZXQsIGl0IGlzIHJlYWxseSBvbmx5IHRoZSBjb250cmFzdHMgd2hpY2ggd2lsbCBiZSBkaWZmZXJlbnQuCgpgYGB7cn0KdHhfcGFpcndpc2VfaW5wdXQgPC0gc2V0X2NvbmRpdGlvbnMobW0zOF9oaXNhdF90eCwgZmFjdCA9ICJ0aW1lX2dlbm9fbG9jIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfY2hvaWNlc1tbImFsbCJdXSkKYGBgCgojIyBBIGhlYXRtYXAgb2Ygc3BlY2lmaWMgZ2VuZXMgYWNyb3NzIGFsbCBjb25kaXRpb25zCgpgYGB7cn0KYWxsX2NvbmRfZ2VuZV9oZWF0bWFwX3N0YXJ0IDwtIG5vcm1hbGl6ZSh0eF9wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gInNpbXBsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoX2NvbHVtbiA9ICJjZHNfbGVuZ3RoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gInJwa20iLCB0cmFuc2Zvcm0gPSAibG9nMiIpCmFsbF9jb25kX2dlbmVfaGVhdG1hcF9pbnB1dCA8LSBtZWRpYW5fYnlfZmFjdG9yKGFsbF9jb25kX2dlbmVfaGVhdG1hcF9zdGFydCkKYWxsX2NvbmRfbXRyeCA8LSBhbGxfY29uZF9nZW5lX2hlYXRtYXBfaW5wdXRbWyJtZWRpYW5zIl1dCmNvbG9yX29yZGVyIDwtIGNvbG5hbWVzKGFsbF9jb25kX210cngpCm5hX2lkeCA8LSBpcy5uYShhbGxfY29uZF9tdHJ4KQphbGxfY29uZF9tdHJ4W25hX2lkeF0gPC0gMAp2YXJpYW5jZXMgPC0gbWF0cml4U3RhdHM6OnJvd1ZhcnMoYXMubWF0cml4KGFsbF9jb25kX210cngpKQp2YXJpYW50X2dlbmVzIDwtIHZhcmlhbmNlcyA+IDYuMgppbnB1dF9tdHJ4IDwtIGFsbF9jb25kX210cnhbdmFyaWFudF9nZW5lcywgXQpjb25kX2NvbG9ycyA8LSBnZXRfY29sb3JzX2J5X2NvbmRpdGlvbih0eF9wYWlyd2lzZV9pbnB1dCwgbGV2ZWxzID0gY29sb3Jfb3JkZXIpCmRpbShpbnB1dF9tdHJ4KQpwcChmaWxlID0gIjA0aW5jbHVzaW9uX2NvbXBhcmlzb25zX3R4L3RvcF8xMDRfdmFyaWFudF9ycGttX2dlbmVzX2hlYXRtYXAucGRmIikKZ3Bsb3RzOjpoZWF0bWFwLjIoYXMubWF0cml4KGlucHV0X210cngpLCBzY2FsZSA9ICJub25lIiwgdHJhY2UgPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgIENvbFNpZGVDb2xvcnMgPSBjb25kX2NvbG9ycykKZGV2Lm9mZigpCmBgYAoKIyBUT0RPOiBDaGFuZ2UgdGhlIGFib3ZlIHRvIHN1YnRyYWN0IHd0CgpSYXNobWkgc3VnZ2VzdGVkIHdlIHNob3VsZCBkbyB0aGUgYWJvdmUgcGxvdCBhZnRlciBzdWJ0cmFjdGluZyB0aGUgd3QKY291bnRzLiAgVGhpcyBpcyBhIGdvb2QgaWRlYSwgYnV0IGl0IHdpbGwgaGF2ZSB0byB3YWl0IHVudGlsIHdlIGZpbmlzaAp0aGUgY3VycmVudCBzZXQuCgojIyBTZXQgdXAgdGhlIGNvbnRyYXN0cwoKSW4gdGhlIGZvbGxvd2luZyBmZXcgYmxvY2tzIEkgd2lsbCBzZXQgdXAgdGhlIHZhcmlvdXMgY29tcGFyaXNvbnMgb2YKaW50ZXJlc3QuICBTdGFydGluZyB3aXRoIHRoZSBzZXQgb2YgZ2VuZXMgdG8gZXhjbHVkZSBiZWNhdXNlIHRoZXkgd2VyZQpvYnNlcnZlZCB0byBiaW5kIG5vbi1zcGVjaWZpY2FsbHkgaW4gdGhlIHd0IHNhbXBsZXMuCgojIyMgSW5jbHVzaW9uIGNvbnRyYXN0cwoKSW4gZWFjaCBleGNsdXNpb24gSSB3aWxsIGhhdmUgdGhlIGNvbnRyYXN0IGZpcnN0IGZvbGxvd2VkIGJ5IHRoZSBwYWlyCm9mIGNvbnRyYXN0cyB3aGljaCB3aWxsIGJlIHVzZWQgdG8gZGVmaW5lIHRoZSBnZW5lIHNldCB0byBleGNsdWRlLgoKKiBwMTVfaGV0X2RsZ24vcDA4X2hldF9kbGduOiBwMTVfd3RfZGxnbi9wMTVfaGV0X2RsZ24sCiAgcDA4X3d0X2RsZ24vcDA4X2hldF9kbGduOyByZW1vdmUgdGhlIGdlbmVzIGluY3JlYXNlZCBpbiB3dC4KKiBwMTVfa29fc2NuL3AwOF9rb19zY246IHAxNV93dF9zY24vcDE1X2tvX3NjbiwgcDA4X3d0X3Njbi9wMTVfa29fc2NuCiogcDE1X2hldF9yZXRpbmEvcDA4X2hldF9yZXRpbmE6IEkgdGhpbmsgeW91IGdldCBpdCwgd3QvaGV0IGZvciBib3RoCiAgcDE1IHJldGluYXMgYW5kIHAwOCByZXRpbmFzLi4uCgpQdXQgc2xpZ2h0bHkgZGlmZmVyZW50bHksIGZvciBldmVyeSB0ZXJtIG9mIGludGVyZXN0IEkgd2lsbCBjcmVhdGUgYQpjb250cmFzdCB3aXRoIHRoZSB3dCBhcyBudW1lcmF0b3IgYW5kIHRoZSBkZXNpcmVkIHRlcm0gYXMgZGVub21pbmF0b3IsCnRoZW4gcHVsbCBvdXQgdGhlIGdlbmVzIGluY3JlYXNlZCBpbiB3dC4KCmBgYHtyfQppbmNsdXNpb25zIDwtIGxpc3QoCiAgIyMgSSBsaWtlIGFscGhhYmV0aXppbmcgdGhpbmdzLCBzdGFydCB3aXRoIGRsZ24KICAicDE1X2hldF9kbGduIiA9IGMoInAxNV9oZXRfZGxnbiIsICJwMTVfd3RfZGxnbiIpLAogICJwMDhfaGV0X2RsZ24iID0gYygicDA4X2hldF9kbGduIiwgInAwOF93dF9kbGduIiksCiAgInAxNV9rb19kbGduIiA9IGMoInAxNV9rb19kbGduIiwgInAxNV93dF9kbGduIiksCiAgInAwOF9rb19kbGduIiA9IGMoInAwOF9rb19kbGduIiwgInAwOF93dF9kbGduIiksCiAgIyMgVGhlbiByZXRpbmFzCiAgInAxNV9oZXRfcmV0aW5hIiA9IGMoInAxNV9oZXRfcmV0aW5hIiwgInAxNV93dF9yZXRpbmEiKSwKICAicDA4X2hldF9yZXRpbmEiID0gYygicDA4X2hldF9yZXRpbmEiLCAicDA4X3d0X3JldGluYSIpLAogICJwMTVfa29fcmV0aW5hIiA9IGMoInAxNV9rb19yZXRpbmEiLCAicDE1X3d0X3JldGluYSIpLAogICJwMDhfa29fcmV0aW5hIiA9IGMoInAwOF9rb19yZXRpbmEiLCAicDA4X3d0X3JldGluYSIpLAogICMjIFRoZW4gc2NuCiAgInAxNV9oZXRfc2NuIiA9IGMoInAxNV9oZXRfc2NuIiwgInAxNV93dF9zY24iKSwKICAicDA4X2hldF9zY24iID0gYygicDA4X2hldF9zY24iLCAicDA4X3d0X3NjbiIpLAogICJwMTVfa29fc2NuIiA9IGMoInAxNV9rb19zY24iLCAicDE1X3d0X3NjbiIpLAogICJwMDhfa29fc2NuIiA9IGMoInAwOF9rb19zY24iLCAicDA4X3d0X3NjbiIpKQpgYGAKCiMjIyBUaW1lIGNvbnRyYXN0cwoKRm9yIGVhY2ggbG9jYXRpb24vZ2Vub3R5cGUgb2YgaW50ZXJlc3QsIGxldCB1cyBjb21wYXJlIHAxNS9wMDgKCmBgYHtyfQp0aW1lX2tlZXBlcnMgPC0gbGlzdCgKICAjIyBETEdOCiAgInRfaGV0X2RsZ24iID0gYygicDE1X2hldF9kbGduIiwgInAwOF9oZXRfZGxnbiIpLAogICJ0X2tvX2RsZ24iID0gYygicDE1X2tvX2RsZ24iLCAicDA4X2tvX2RsZ24iKSwKICAjIyBSZXRpbmEKICAidF9oZXRfcmV0aW5hIiA9IGMoInAxNV9oZXRfcmV0aW5hIiwgInAwOF9oZXRfcmV0aW5hIiksCiAgInRfa29fcmV0aW5hIiA9IGMoInAxNV9rb19yZXRpbmEiLCAicDA4X2tvX3JldGluYSIpLAogICMjIFNDTgogICJ0X2hldF9zY24iID0gYygicDE1X2hldF9zY24iLCAicDA4X2hldF9zY24iKSwKICAidF9rb19zY24iID0gYygicDE1X2tvX3NjbiIsICJwMDhfa29fc2NuIikpCmBgYAoKIyMjIExvY2F0aW9uIGNvbnRyYXN0cwoKQ29tcGFyZSBsb2NhdGlvbnMgYW5kIGtlZXAgdGltZS9nZW5vdHlwZSBjb25zaXN0ZW50LiAgSSB3aWxsIHVzZSB0aGUKbG9jYXRpb24gaW5pdGlhbHMgdG8gZGVmaW5lIG51bWVyYXRvci9kZW5vbWluYXRvci4KCmBgYHtyfQpsb2NhdGlvbl9rZWVwZXJzIDwtIGxpc3QoCiAgIyMgZGxnbi9yZXRpbmEKICAiZHJfcDA4X2hldCIgPSBjKCJwMDhfaGV0X2RsZ24iLCAicDA4X2hldF9yZXRpbmEiKSwKICAiZHJfcDE1X2hldCIgPSBjKCJwMTVfaGV0X2RsZ24iLCAicDE1X2hldF9yZXRpbmEiKSwKICAiZHJfcDA4X2tvIiA9IGMoInAwOF9rb19kbGduIiwgInAwOF9rb19yZXRpbmEiKSwKICAiZHJfcDE1X2tvIiA9IGMoInAxNV9rb19kbGduIiwgInAxNV9rb19yZXRpbmEiKSwKICAjIyBzY24vcmV0aW5hCiAgInNyX3AwOF9oZXQiID0gYygicDA4X2hldF9zY24iLCAicDA4X2hldF9yZXRpbmEiKSwKICAic3JfcDE1X2hldCIgPSBjKCJwMTVfaGV0X3NjbiIsICJwMTVfaGV0X3JldGluYSIpLAogICJzcl9wMDhfa28iID0gYygicDA4X2tvX3NjbiIsICJwMDhfa29fcmV0aW5hIiksCiAgInNyX3AxNV9rbyIgPSBjKCJwMTVfa29fc2NuIiwgInAxNV9rb19yZXRpbmEiKSwKICAjIyBkbGduL3NjbgogICJkc19wMDhfaGV0IiA9IGMoInAwOF9oZXRfZGxnbiIsICJwMDhfaGV0X3NjbiIpLAogICJkc19wMTVfaGV0IiA9IGMoInAxNV9oZXRfZGxnbiIsICJwMTVfaGV0X3NjbiIpLAogICJkc19wMDhfa28iID0gYygicDA4X2tvX2RsZ24iLCAicDA4X2tvX3NjbiIpLAogICJkc19wMTVfa28iID0gYygicDE1X2tvX2RsZ24iLCAicDE1X2tvX3NjbiIpKQpgYGAKCiMjIyBHZW5vdHlwZSBjb250cmFzdHMKCkNvbXBhcmUga28vaGV0IHdoaWxlIGtlZXBpbmcgdGltZS9sb2NhdGlvbiBjb25zdGFudC4gIFNpbWlsYXJseSwgdXNlCnRoZSBpbml0aWFscyB0byBkZW5vdGUgbnVtZXJhdG9yL2Rlbm9taW5hdG9yLCB3aGljaCB3aWxsIGFsd2F5cyBiZSBraC4KCmBgYHtyfQpnZW5vdHlwZV9rZWVwZXJzIDwtIGxpc3QoCiAgIyMgRExHTgogICJraF9wMDhfZGxnbiIgPSBjKCJwMDhfa29fZGxnbiIsICJwMDhfaGV0X2RsZ24iKSwKICAia2hfcDE1X2RsZ24iID0gYygicDE1X2tvX2RsZ24iLCAicDE1X2hldF9kbGduIiksCiAgIyMgUmV0aW5hCiAgImtoX3AwOF9yZXRpbmEiID0gYygicDA4X2tvX3JldGluYSIsICJwMDhfaGV0X3JldGluYSIpLAogICJraF9wMTVfcmV0aW5hIiA9IGMoInAxNV9rb19yZXRpbmEiLCAicDE1X2hldF9yZXRpbmEiKSwKICAjIyBTQ04KICAia2hfcDA4X3NjbiIgPSBjKCJwMDhfa29fc2NuIiwgInAwOF9oZXRfc2NuIiksCiAgImtoX3AxNV9zY24iID0gYygicDE1X2tvX3NjbiIsICJwMTVfaGV0X3NjbiIpKQpgYGAKCiMjIFBlcmZvcm0gdGhlIGV4Y2x1c2lvbiBjb21wYXJpc29uCgpNeSBhbGxfcGFpcndpc2UoKSBmdW5jdGlvbiBub3cgaGFzIGEgcGFyYW1ldGVyIHdoaWNoIGFsbG93cyBtZSB0bwpjaG9vc2Ugd2hpY2ggY29udHJhc3RzIHRvIHBlcmZvcm0gaW5zdGVhZCBvZiBsaXRlcmFsbHkgZG9pbmcgZXZlcnkKcG9zc2libGUgY29tcGFyaXNvbi4gIFRoYXQgaXMgd2VsbCBzdWl0ZWQgZm9yIHRoZXNlIG9wZXJhdGlvbnM6CgpJbiBhIGNvbnRhaW5lciwgdGhlIGZvbGxvd2luZyBhcHBlYXJzIHRvIGZhaWwgd2l0aDoKCiJlcnJvciBjb2RlIDEgZnJvbSBMYXBhY2sgcm91dGluZSAnZGdlc2RkJyIKClJ1bm5pbmcgaXQgbWFudWFsbHkgb3V0c2lkZSB0aGUgY29udGFpbmVyIHJlc3VsdHMgaW4gaXQgd29ya2luZwp3aXRob3V0IGVycm9yLiAgSSBhc3N1bWUgdGhlcmVmb3JlIHRoYXQgdGhlIHByb2JsZW0gbGllcyBpbiB0aGUKY29tcGlsYXRpb24gZmxhZ3Mgb2YgTEFQQUNLIGluIHRoZSBjb250YWluZXIuCgpOb3RlOiBUaGlzIHByb2JsZW0gd2FzIGZpeGVkIGJ5IHJlbW92aW5nIHNvbWUgcGFyYWxsZWxpemF0aW9uLgoKYGBge3J9CmluY2x1c2lvbl9kZSA8LSBhbGxfcGFpcndpc2UoCiAgdHhfcGFpcndpc2VfaW5wdXQsIGZpbHRlciA9ICJzaW1wbGUiLCBtb2RlbF9mc3RyaW5nID0gZGVmYXVsdF9mc3RyaW5nLAogIGtlZXBlcnMgPSBpbmNsdXNpb25zLCBtb2RlbF9zdnMgPSAic3Zhc2VxIikKaW5jbHVzaW9uX2RlCgppbmNsdXNpb25fdGFibGVzIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIGluY2x1c2lvbl9kZSwga2VlcGVycyA9IGluY2x1c2lvbnMsIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoIjA0aW5jbHVzaW9uX2NvbXBhcmlzb25zX3R4L2luY2x1c2lvbl90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKaW5jbHVzaW9uX3RhYmxlcwojIyAyMDI2MDM6IEkgc3VjY2Vzc2Z1bGx5IHJlY2FwaXR1bGF0ZWQgcHJldmlvdXMgbm9uLWNvbnRhaW5lciByZXN1bHQuCgppbmNsdXNpb25fc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgaW5jbHVzaW9uX3RhYmxlcywgbGZjID0gZmlsdGVyX2xmY19jdXRvZmYsIHAgPSBmaWx0ZXJfYWRqcF9jdXRvZmYsIGFjY29yZGluZ190byA9ICJkZXNlcSIsCiAgZXhjZWwgPSBnbHVlKCIwNGluY2x1c2lvbl9jb21wYXJpc29uc190eC9pbmNsdXNpb25fc2lnLXZ7dmVyfS54bHN4IikpCmluY2x1c2lvbl9zaWcKYGBgCgoyMDI1MDU6IEEgc3RyYW5nZSB0aGluZyBoYXBwZW5lZCBoZXJlIGluIHRoaXMgaXRlcmF0aW9uOiB0aGUgcGxvdCBvZgp0aGUgc2lnbmlmaWNhbnQgZ2VuZXMgaXMgdGhlIGV4YWN0IHNhbWUgYXMgdGhlIHByZXZpb3VzIGl0ZXJhdGlvbjsgYnV0CnRoZSB0YWJsZSBvZiBudW1iZXJzIG9mIGdlbmVzIGxvb2tzIGRpZmZlcmVudC4KCkZvciBleGFtcGxlLCB0aGUgcHJldmlvdXMgdGFibGUgc2hvd2VkOiBwMTVfaGV0X2RsZ24gd2l0aCAyMDY3IHVwIGFuZAoyMzgxIGRvd24uICBUaGUgcGxvdCBzaG93cyBleGFjdGx5IHRoYXQ7IGJ1dCB0aGUgbmV3IHRhYmxlIHNob3dzIDYwNwp1cCBhbmQgMTIyOSBkb3duLiAgTGV0IHVzIGNoZWNrIHRoZSBhY3R1YWwgZGF0YSBzdHJ1Y3R1cmUgYW5kIHNlZSB3aGF0CmlzIHVwPwoKSSB0aGluayBJIGdldCBpdDogd2hlbiB3ZSBkbyB0aGUgZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcyBhYm92ZSwgd2UKZXhwbGljaXRseSBzZXQgYSBub24tc3RhbmRhcmQgcC12YWx1ZSBhbmQgbG9nRkMgYmVjYXVzZSB3ZSBhcmUKZXhwbGljaXRseSBhdHRlbXB0aW5nIHRvIHVzZSBhIHZlcnkgbG9vc2UgZGVmaW5pdGlvbiBvZiB0aGUgc2V0IG9mCmdlbmVzIHdoaWNoIGFyZSBpbiBncmVhdGVyIGFidW5kYW5jZSB0aGFuIHRoZWlyIG1vc3Qgc2ltaWxhcgp3aWxkLXR5cGUuIEhvd2V2ZXIsIHdoZW4gSSBjcmVhdGUgdGhlIGJhcnBsb3Qgb2Ygc2lnbmlmaWNhbnQgZ2VuZXM7CnRob3NlIHZhbHVlcyBhcmUgZXhwbGljaXRseSBzZXQgdG8gMCwxLDIgbG9nRkNzIGFuZCBwLXZhbHVlIDAuMDUuClRoZXJlZm9yZSwgd2hhdCBJIG5lZWQgdG8gZG8sIGluIG9yZGVyIHRvIGNoZWNrIGNvbnNpc3RlbmN5LCBpcyB0bwpyZXBlYXQgdGhpcyBjYWxsIGJ1dCB3aXRoIHRoZSBkZWZhdWx0IEZDL3AgdmFsdWVzIGFuZCBzZWUgd2hhdCB0aGUKbnVtYmVycyBsb29rIGxpa2UuCgpgYGB7cn0KdGVzdF9pbmNsdXNpb24gPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBpbmNsdXNpb25fdGFibGVzLCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLCBleGNlbCA9ICIwNGluY2x1c2lvbl9jb21wYXJpc29uc190eC9kZWZhdWx0X2luY2x1c2lvbl9zaWcueGxzeCIpCnRlc3RfaW5jbHVzaW9uCmBgYAoKWWVhaCwgSSB0aGluayB0aGlzIG1ha2VzIHNlbnNlOyB3aGF0IEkgbmVlZCB0byBkbzogY2hhbmdlIHRoZQpzaWduaWZpY2FudCBiYXIgcGxvdCBzbyB0aGF0IGl0IHVzZXMgdGhlIGxmYyBjdXRvZmYgYXJndW1lbnQgYXMgdGhlCnNlY29uZCBvZiBpdHMgMyBjdXRvZmZzLiAgVGhhdCBzaG91bGQgZW5zdXJlIHRoYXQgdGhlc2UgbnVtYmVycyBhcmUKY29uc2lzdGVudCBhY3Jvc3MgYW5hbHlzZXMgYW5kIHBhcmFtZXRlcnMgcHJvdmlkZWQuCgpgYGB7cn0KZGltKGluY2x1c2lvbl9zaWckZGVzZXEkdXBzJHAxNV9oZXRfZGxnbikKdGVzdF9hbGxfdXAgPC0gaW5jbHVzaW9uX3RhYmxlcyRkYXRhJHAxNV9oZXRfZGxnbltbImRlc2VxX2xvZ2ZjIl1dID49IDAuMSAmCiAgaW5jbHVzaW9uX3RhYmxlcyRkYXRhJHAxNV9oZXRfZGxnbltbImRlc2VxX2FkanAiXV0gPD0gMC4xCnN1bW1hcnkodGVzdF9hbGxfdXApCmBgYAoKT2hoLCBJIGdldCBpdCwgd2hlbiBJIHdhcyB0ZXN0aW5nIHRoaXMgb3V0IG1hbnVhbGx5LCBJIHNldCB0aGUgbG9nRkMKdG8gMS4wIGluc3RlYWQgb2YgdGhlIHZlcnkgbWluaW1hbCAwLjEgd2UgaGF2ZSBiZWVuIHVzaW5nIGZvciB0aGlzIQoKIyBNQS9Wb2xjYW5vIHBsb3RzIG9mIHggdnMgd3QKClJhc2htaSBhc2tlZCB0byBzZWUgdGhlIGNvbXBhcmlzb25zIGFnYWluc3Qgd3Q7IEkgd2lsbCBuYW1lIGVhY2ggZmlsZQp4dyB0byBzaG93IHRoYXQgaXQgaXMgeCB2cyB3dC4gZm9yIHdoYXRldmVyIG90aGVyIHBhcmFtZXRlcnMgYXJlIGJlaW5nCmV4YW1pbmVkLiAgSXQgaXMgbGlrZWx5IHRoYXQgc29tZSBjb2xvcnMgd2lsbCBiZSB3cm9uZyBiZWNhdXNlIHRoaXMgaXMKbXkgZmlyc3QgdGltZSBjcmVhdGluZyB0aGVzZSBwbG90cyBhbmQgd2UgYXJlIGRvaW5nIHRoZW0gbWFudWFsbHkuCgojIyBwMTVfaGV0X2RsZ24KCmBgYHtyfQphbGxjIDwtIGNvbG9yX2Nob2ljZXNbWyJhbGwiXV0KdGFibGVfbmFtZSA8LSAicDE1X2hldF9kbGduIgp0YWJsZSA8LSBpbmNsdXNpb25fdGFibGVzW1siZGF0YSJdXVtbdGFibGVfbmFtZV1dCm51bSA8LSAicDE1X2hldF9kbGduIgpkZW5vbSA8LSAicDE1X3d0X2RsZ24iCmh3X3AxNV9kbGduX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGZpbGwgPSAiYmxhY2siLAogIGNvbG9yX2xvdyA9IGFsbGNbW2Rlbm9tXV0sIGNvbG9yX2hpZ2ggPSBhbGxjW1tudW1dXSwKICBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSAxMCwgYWxwaGEgPSAxLjAsCiAgc2l6ZSA9IDQpCgpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDE1X2RsZ25fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmh3X3AxNV9kbGduX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmh3X3AxNV9kbGduX3ZvbGNhbm9bWyJwbG90Il1dCgpod19wMTVfZGxnbl9tYSA8LSBwbG90X21hX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZXhwcl9jb2wgPSAiZGVzZXFfYmFzZW1lYW4iLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLAogIGNvbG9yX2xvdyA9IGFsbGNbW2Rlbm9tXV0sIGNvbG9yX2hpZ2ggPSBhbGxjW1tudW1dXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIG91dGxpbmUgPSBvdXRsaW5lLAogIGxhYmVsID0gMTApCgpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDE1X2RsZ25fbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpod19wMTVfZGxnbl9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKaHdfcDE1X2RsZ25fbWFbWyJwbG90Il1dCmBgYAoKIyMgcDA4X2hldF9kbGduCgpgYGB7cn0KdGFibGVfbmFtZSA8LSAicDA4X2hldF9kbGduIgp0YWJsZSA8LSBpbmNsdXNpb25fdGFibGVzW1siZGF0YSJdXVtbdGFibGVfbmFtZV1dCm51bSA8LSAicDA4X2hldF9kbGduIgpkZW5vbSA8LSAicDA4X3d0X2RsZ24iCmh3X3AwOF9kbGduX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGZpbGwgPSAiYmxhY2siLAogIGNvbG9yX2xvdyA9IGFsbGNbW2Rlbm9tXV0sIGNvbG9yX2hpZ2ggPSBhbGxjW1tudW1dXSwKICBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSAxMCwgYWxwaGEgPSAxLjAsCiAgc2l6ZSA9IDQpCnBwKGZpbGUgPSAiMDVpbmNsdXNpb25fdm9sY2Fub19tYV90eC9od19wMDhfZGxnbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKaHdfcDA4X2RsZ25fdm9sY2Fub1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKaHdfcDA4X2RsZ25fdm9sY2Fub1tbInBsb3QiXV0KaHdfcDA4X2RsZ25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDA4X2RsZ25fbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpod19wMDhfZGxnbl9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKaHdfcDA4X2RsZ25fbWFbWyJwbG90Il1dCmBgYAoKIyMgcDE1X2tvX2RsZ24KCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJwMTVfa29fZGxnbiIKdGFibGUgPC0gaW5jbHVzaW9uX3RhYmxlc1tbImRhdGEiXV1bW3RhYmxlX25hbWVdXQpudW0gPC0gInAxNV9rb19kbGduIgpkZW5vbSA8LSAicDE1X3d0X2RsZ24iCmt3X3AxNV9kbGduX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGZpbGwgPSAiYmxhY2siLAogIGNvbG9yX2xvdyA9IGFsbGNbW2Rlbm9tXV0sIGNvbG9yX2hpZ2ggPSBhbGxjW1tudW1dXSwKICBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSAxMCwgYWxwaGEgPSAxLjAsCiAgc2l6ZSA9IDQpCnBwKGZpbGUgPSAiMDVpbmNsdXNpb25fdm9sY2Fub19tYV90eC9rd19wMTVfZGxnbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa3dfcDE1X2RsZ25fdm9sY2Fub1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa3dfcDE1X2RsZ25fdm9sY2Fub1tbInBsb3QiXV0Ka3dfcDE1X2RsZ25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDE1X2RsZ25fbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQprd19wMTVfZGxnbl9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa3dfcDE1X2RsZ25fbWFbWyJwbG90Il1dCmBgYAoKIyMgcDA4X2tvX2RsZ24KCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJwMDhfa29fZGxnbiIKdGFibGUgPC0gaW5jbHVzaW9uX3RhYmxlc1tbImRhdGEiXV1bW3RhYmxlX25hbWVdXQpudW0gPC0gInAwOF9rb19kbGduIgpkZW5vbSA8LSAicDA4X3d0X2RsZ24iCmt3X3AwOF9kbGduX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGZpbGwgPSAiYmxhY2siLAogIGNvbG9yX2xvdyA9IGFsbGNbW2Rlbm9tXV0sIGNvbG9yX2hpZ2ggPSBhbGxjW1tudW1dXSwKICBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSAxMCwgYWxwaGEgPSAxLjAsCiAgc2l6ZSA9IDQpCnBwKGZpbGUgPSAiMDVpbmNsdXNpb25fdm9sY2Fub19tYV90eC9rd19wMDhfZGxnbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa3dfcDA4X2RsZ25fdm9sY2Fub1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa3dfcDA4X2RsZ25fdm9sY2Fub1tbInBsb3QiXV0Ka3dfcDA4X2RsZ25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDA4X2RsZ25fbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQprd19wMDhfZGxnbl9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa3dfcDA4X2RsZ25fbWFbWyJwbG90Il1dCmBgYAoKIyMgcDE1X2hldF9yZXRpbmEKCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJwMTVfaGV0X3JldGluYSIKdGFibGUgPC0gaW5jbHVzaW9uX3RhYmxlc1tbImRhdGEiXV1bW3RhYmxlX25hbWVdXQpudW0gPC0gInAxNV9oZXRfcmV0aW5hIgpkZW5vbSA8LSAicDE1X3d0X3JldGluYSIKaHdfcDE1X3JldGluYV92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBmaWxsID0gImJsYWNrIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDE1X3JldGluYV92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKaHdfcDE1X3JldGluYV92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpod19wMTVfcmV0aW5hX3ZvbGNhbm9bWyJwbG90Il1dCmh3X3AxNV9yZXRpbmFfbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDE1X3JldGluYV9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmh3X3AxNV9yZXRpbmFfbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmh3X3AxNV9yZXRpbmFfbWFbWyJwbG90Il1dCmBgYAoKIyMgcDA4X2hldF9yZXRpbmEKCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJwMDhfaGV0X3JldGluYSIKdGFibGUgPC0gaW5jbHVzaW9uX3RhYmxlc1tbImRhdGEiXV1bW3RhYmxlX25hbWVdXQpudW0gPC0gInAwOF9oZXRfcmV0aW5hIgpkZW5vbSA8LSAicDA4X3d0X3JldGluYSIKaHdfcDA4X3JldGluYV92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBmaWxsID0gImJsYWNrIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDA4X3JldGluYV92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKaHdfcDA4X3JldGluYV92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpod19wMDhfcmV0aW5hX3ZvbGNhbm9bWyJwbG90Il1dCmh3X3AwOF9yZXRpbmFfbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDA4X3JldGluYV9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmh3X3AwOF9yZXRpbmFfbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmh3X3AwOF9yZXRpbmFfbWFbWyJwbG90Il1dCmBgYAoKIyMgcDE1X2tvX3JldGluYQoKYGBge3J9CnRhYmxlX25hbWUgPC0gInAxNV9rb19yZXRpbmEiCnRhYmxlIDwtIGluY2x1c2lvbl90YWJsZXNbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KbnVtIDwtICJwMTVfa29fcmV0aW5hIgpkZW5vbSA8LSAicDE1X3d0X3JldGluYSIKa3dfcDE1X3JldGluYV92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBmaWxsID0gImJsYWNrIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDE1X3JldGluYV92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa3dfcDE1X3JldGluYV92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQprd19wMTVfcmV0aW5hX3ZvbGNhbm9bWyJwbG90Il1dCmt3X3AxNV9yZXRpbmFfbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDE1X3JldGluYV9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmt3X3AxNV9yZXRpbmFfbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmt3X3AxNV9yZXRpbmFfbWFbWyJwbG90Il1dCmBgYAoKIyMgcDA4X2tvX3JldGluYQoKYGBge3J9CnRhYmxlX25hbWUgPC0gInAwOF9rb19yZXRpbmEiCnRhYmxlIDwtIGluY2x1c2lvbl90YWJsZXNbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KbnVtIDwtICJwMDhfa29fcmV0aW5hIgpkZW5vbSA8LSAicDA4X3d0X3JldGluYSIKa3dfcDA4X3JldGluYV92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBmaWxsID0gImJsYWNrIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDA4X3JldGluYV92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa3dfcDA4X3JldGluYV92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQprd19wMDhfcmV0aW5hX3ZvbGNhbm9bWyJwbG90Il1dCmt3X3AwOF9yZXRpbmFfbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDA4X3JldGluYV9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmt3X3AwOF9yZXRpbmFfbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmt3X3AwOF9yZXRpbmFfbWFbWyJwbG90Il1dCmBgYAoKIyMgcDE1X2hldF9zY24KCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJwMTVfaGV0X3NjbiIKdGFibGUgPC0gaW5jbHVzaW9uX3RhYmxlc1tbImRhdGEiXV1bW3RhYmxlX25hbWVdXQpudW0gPC0gInAxNV9oZXRfc2NuIgpkZW5vbSA8LSAicDE1X3d0X3NjbiIKaHdfcDE1X3Njbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBmaWxsID0gImJsYWNrIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDE1X3Njbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKaHdfcDE1X3Njbl92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpod19wMTVfc2NuX3ZvbGNhbm9bWyJwbG90Il1dCmh3X3AxNV9zY25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDE1X3Njbl9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmh3X3AxNV9zY25fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmh3X3AxNV9zY25fbWFbWyJwbG90Il1dCmBgYAoKIyMgcDA4X2hldF9zY24KCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJwMDhfaGV0X3NjbiIKdGFibGUgPC0gaW5jbHVzaW9uX3RhYmxlc1tbImRhdGEiXV1bW3RhYmxlX25hbWVdXQpudW0gPC0gInAwOF9oZXRfc2NuIgpkZW5vbSA8LSAicDA4X3d0X3NjbiIKaHdfcDA4X3Njbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBmaWxsID0gImJsYWNrIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDA4X3Njbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKaHdfcDA4X3Njbl92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpod19wMDhfc2NuX3ZvbGNhbm9bWyJwbG90Il1dCmh3X3AwOF9zY25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgvaHdfcDA4X3Njbl9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmh3X3AwOF9zY25fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmh3X3AwOF9zY25fbWFbWyJwbG90Il1dCmBgYAoKIyMgcDE1X2tvX3NjbgoKYGBge3J9CnRhYmxlX25hbWUgPC0gInAxNV9rb19zY24iCnRhYmxlIDwtIGluY2x1c2lvbl90YWJsZXNbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KbnVtIDwtICJwMTVfa29fc2NuIgpkZW5vbSA8LSAicDE1X3d0X3NjbiIKa3dfcDE1X3Njbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBmaWxsID0gImJsYWNrIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDE1X3Njbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa3dfcDE1X3Njbl92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQprd19wMTVfc2NuX3ZvbGNhbm9bWyJwbG90Il1dCmt3X3AxNV9zY25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDE1X3Njbl9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmt3X3AxNV9zY25fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmt3X3AxNV9zY25fbWFbWyJwbG90Il1dCmBgYAoKIyMgcDA4X2tvX3NjbgoKYGBge3J9CnRhYmxlX25hbWUgPC0gInAwOF9rb19zY24iCnRhYmxlIDwtIGluY2x1c2lvbl90YWJsZXNbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KbnVtIDwtICJwMDhfa29fc2NuIgpkZW5vbSA8LSAicDA4X3d0X3NjbiIKa3dfcDA4X3Njbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBmaWxsID0gImJsYWNrIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDA4X3Njbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa3dfcDA4X3Njbl92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQprd19wMDhfc2NuX3ZvbGNhbm9bWyJwbG90Il1dCmt3X3AwOF9zY25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBhbGxjW1tkZW5vbV1dLCBjb2xvcl9oaWdoID0gYWxsY1tbbnVtXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBvdXRsaW5lID0gb3V0bGluZSwKICBsYWJlbCA9IDEwKQpwcChmaWxlID0gIjA1aW5jbHVzaW9uX3ZvbGNhbm9fbWFfdHgva3dfcDA4X3Njbl9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmt3X3AwOF9zY25fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmt3X3AwOF9zY25fbWFbWyJwbG90Il1dCmBgYAoKU2VlIHRoZSBzaGFyZWQvdW5pcXVlIGdlbmVzIGluIHRoZXNlIHNldHMuCgpgYGB7cn0KaW5jbHVzaW9uX3Vwc2V0cyA8LSB1cHNldHJfc2lnKGluY2x1c2lvbl9zaWcpCmluY2x1c2lvbl9pbnRlcnNlY3RzIDwtIHdyaXRlX3Vwc2V0X2dyb3VwcygKICBpbmNsdXNpb25fdXBzZXRzLCBleGNlbCA9ICIwNGluY2x1c2lvbl9jb21wYXJpc29uX3R4L2luY2x1c2lvbl9nZW5lX2dyb3Vwcy54bHN4IikKYGBgCgojIyMgRXh0cmFjdCBnZW5lcyBpbmNsdWRlZCBmb3IgZWFjaCBzZXQgb2YgY29udHJhc3RzCgpOb3csIHVzaW5nIHRoYXQgZnVuY3Rpb24sIHB1bGwgb3V0IHRoZSBnZW5lIElEcyBvZiBnZW5lcyB3ZSBkbyBub3QKdHJ1c3QgYmVjYXVzZSB0aGV5IHdlcmUgdG9vIGhpZ2ggaW4gd3QgZm9yIGV2ZXJ5IGNvbnRyYXN0IHdlIGFyZQpsaWtlbHkgdG8gcGVyZm9ybS4KClRoZSBmb2xsb3dpbmcgd2FzIGEgbW9kaWZpZWQgdmVyc2lvbiBvZiB0aGUgaW5jbHVzaW9uIGZ1bmN0aW9uIHdoaWNoIGlzIHNvbWV3aGF0IG1vcmUgcmVzdHJpY3RpdmUuCgpgYGB7cn0KZXh0cmFjdF9pbmNsdXNpb25zX3N0cmljdCA8LSBmdW5jdGlvbihpbmNsdXNpb25fc2lnLCBpbmNsdXNpb25fdGFibGVzLCBpbmNsdXNpb25zLCBrZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbF9nZW5lcywgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwgd2hpY2ggPSAidXBzIikgewogIHJldGxpc3QgPC0gbGlzdCgpCiAgdGFibGVfbmFtZXMgPC0gbmFtZXMoaW5jbHVzaW9uX3NpZ1tbYWNjb3JkaW5nX3RvXV1bW3doaWNoXV0pCiAgZm9yIChjX251bSBpbiBzZXFfYWxvbmcoa2VlcGVycykpIHsKICAgIGNvbnRyYXN0IDwtIG5hbWVzKGtlZXBlcnMpW2NfbnVtXQogICAgbnVtZXJhdG9yX25hbWUgPC0ga2VlcGVyc1tbY19udW1dXVsxXQogICAgZGVub21pbmF0b3JfbmFtZSA8LSBrZWVwZXJzW1tjX251bV1dWzJdCiAgICAjIyBJbiBteSBuZXcgYnJhbmNoIEkgY2xlYW5lZCB1cCB0aGUgc2FuaXRpemVyIGZ1bmN0aW9uIGZvciBjb250cmFzdHMgc28gdGhpcyBpcyBub3QgbmVlZGVkLgogICAgIyMgVGhlIGZvbGxvd2luZyB0d28gbGluZXMgYXJlIG5vIGxvbmdlciBuZWVkZWQgYmVjYXVzZSBvZiB0aGUgY2xlYW51cHMgSSBwZXJmb3JtZWQuCiAgICAjI251bWVyYXRvcl9uYW1lIDwtIGdzdWIoeCA9IG51bWVyYXRvcl9uYW1lLCBwYXR0ZXJuID0gIihoZXR8a298d3QpIiwgcmVwbGFjZW1lbnQgPSAiX1xcMV8iKQogICAgIyNkZW5vbWluYXRvcl9uYW1lIDwtIGdzdWIoeCA9IGRlbm9taW5hdG9yX25hbWUsIHBhdHRlcm4gPSAiKGhldHxrb3x3dCkiLCByZXBsYWNlbWVudCA9ICJfXFwxXyIpCiAgICBudW1lcmF0b3JfdGFibGUgPC0gaW5jbHVzaW9uX3NpZ1tbYWNjb3JkaW5nX3RvXV1bW3doaWNoXV1bW251bWVyYXRvcl9uYW1lXV0KICAgIG51bWVyYXRvcl9nZW5lcyA8LSByb3duYW1lcyhudW1lcmF0b3JfdGFibGUpCiAgICBkZW5vbWluYXRvcl90YWJsZSA8LSBpbmNsdXNpb25fc2lnW1thY2NvcmRpbmdfdG9dXVtbd2hpY2hdXVtbZGVub21pbmF0b3JfbmFtZV1dCiAgICBkZW5vbWluYXRvcl9nZW5lcyA8LSByb3duYW1lcyhkZW5vbWluYXRvcl90YWJsZSkKICAgIGRmX2NvbHVtbnMgPC0gcGFzdGUwKCJkZXNlcV8iLCBjKCJsb2dmYyIsICJhZGpwIiwgImRlbiIpKQogICAgaW5jbHVkZWRfbnVtIDwtIGluY2x1c2lvbl90YWJsZXNbWyJkYXRhIl1dW1tudW1lcmF0b3JfbmFtZV1dWywgZGZfY29sdW1uc10KICAgIGNvbG5hbWVzKGluY2x1ZGVkX251bSkgPC0gYygibnVtZXJhdG9yX3ZzX3d0X2xvZ2ZjIiwgIm51bWVyYXRvcl92c193dF9hZGpwIiwgIm51bV93dF9tZWFuX2V4cHJzIikKICAgIGluY2x1ZGVkX2RlbiA8LSBpbmNsdXNpb25fdGFibGVzW1siZGF0YSJdXVtbZGVub21pbmF0b3JfbmFtZV1dWywgZGZfY29sdW1uc10KICAgIGNvbG5hbWVzKGluY2x1ZGVkX2RlbikgPC0gYygiZGVub21pbmF0b3JfdnNfd3RfbG9nZmMiLCAiZGVub21pbmF0b3JfdnNfd3RfYWRqcCIsICJkZW5fd3RfbWVhbl9leHBycyIpCiAgICAjIyBJIHRoaW5rIHRoaXMgaXMgd2hlcmUgdGhpbmdzIHdlbnQgd3JvbmcsCiAgICAjIyBjb21wYXJlIHRoaXMgbW9kaWZpZWQgbGluZSB0byB0aGUgb3JpZ2luYWwgdG8gcHJvdmUgaXQuCiAgICBpbmNsdWRlZF9kZiA8LSBtZXJnZShpbmNsdWRlZF9udW0sIGluY2x1ZGVkX2RlbiwgYnkgPSAicm93Lm5hbWVzIikKICAgICMjIFByZXZpb3VzbHksIEkgZGlkIG5vdCBzcGVjaWZ5IHRoZSBtZXJnZSBhY3Rpb24sIGFsbCA9IEZBTFNFIGJ5IGRlZmF1bHQuCiAgICAjIyBUaGlzIHRoZW4gd2lsbCByZXN1bHQgaW4gYSBkaWZmZXJlbmNlIGluIHRoZSByb3dzIG9ic2VydmVkCiAgICAjIyBpbmNsdWRlZF9kZiA8LSBtZXJnZShpbmNsdWRlZF9udW0sIGluY2x1ZGVkX2RlbiwgYnkgPSAicm93Lm5hbWVzIiwgYWxsID0gRkFMU0UpCiAgICByb3duYW1lcyhpbmNsdWRlZF9kZikgPC0gaW5jbHVkZWRfZGZbWyJSb3cubmFtZXMiXV0KICAgIGluY2x1ZGVkX2RmW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKICAgIGNvbmNhdGVuYXRlZF9nZW5lcyA8LSBjKG51bWVyYXRvcl9nZW5lcywgZGVub21pbmF0b3JfZ2VuZXMpCiAgICBib3RoX2dlbmVfaWR4IDwtIGR1cGxpY2F0ZWQoY29uY2F0ZW5hdGVkX2dlbmVzKQogICAgZ2VuZXNfaW5fYm90aCA8LSBjb25jYXRlbmF0ZWRfZ2VuZXNbYm90aF9nZW5lX2lkeF0KICAgIG1lc3NhZ2UoIlRoZSBzZXQgb2YgdW5pcXVlIGdlbmVzIGhpZ2hlciBpbiAiLCBudW1lcmF0b3JfbmFtZSwKICAgICAgICAgICAgIiB2cy4gd3QgaXMgIiwgbGVuZ3RoKG51bWVyYXRvcl9nZW5lcyksICIuIikKICAgIG1lc3NhZ2UoIlRoZSBzZXQgb2YgdW5pcXVlIGdlbmVzIGhpZ2hlciBpbiAiLCBkZW5vbWluYXRvcl9uYW1lLAogICAgICAgICAgICAiIHZzLiB3dCBpcyAiLCBsZW5ndGgoZGVub21pbmF0b3JfZ2VuZXMpLCAiLiIpCiAgICBtZXNzYWdlKCJUaGUgaW50ZXJzZWN0aW9uIG9mIHRoZW0gaXMgIiwgbGVuZ3RoKGdlbmVzX2luX2JvdGgpLCAiIGdlbmVzLiIpCiAgICBpbmNsdWRlX25hbWUgPC0gcGFzdGUwKCJpbmNfIiwgY29udHJhc3QpCiAgICBpbmNsdWRlX2lkeCA8LSBhbGxfZ2VuZXMgJWluJSBnZW5lc19pbl9ib3RoCiAgICBpbmNsdWRlX2dlbmVzIDwtIGFsbF9nZW5lc1tpbmNsdWRlX2lkeF0KICAgIGRmX25hbWUgPC0gcGFzdGUwKCJkZl8iLCBjb250cmFzdCkKICAgIHJldGxpc3RbW2RmX25hbWVdXSA8LSBpbmNsdWRlZF9kZgogICAgd3JpdHRlbl9pbmNsdXNpb24gPC0gd3JpdGVfeGxzeCgKICAgICAgZGF0YSA9IGluY2x1ZGVkX2RmLAogICAgICBleGNlbCA9IGdsdWUoIjA3aW5jbHVkZWRfc3RyaWN0X2dlbmVzX2V4Y2VsX3R4L3tpbmNsdWRlX25hbWV9LXZ7dmVyfS54bHN4IikpCiAgICByZXRsaXN0W1tpbmNsdWRlX25hbWVdXSA8LSBpbmNsdWRlX2dlbmVzCiAgICByZXRsaXN0W1tjb250cmFzdF1dIDwtIGluY2x1ZGVfZ2VuZXMKICB9CiAgcmV0dXJuKHJldGxpc3QpCn0KYGBgCgpUaGlzIGlzIHRoZSBwcmUtMjAyNTA1IHZlcnNpb24gb2YgdGhpcyBmdW5jdGlvbi4KCmBgYHtyfQpleHRyYWN0X2luY2x1c2lvbnMgPC0gZnVuY3Rpb24oaW5jbHVzaW9uX3NpZywgaW5jbHVzaW9uX3RhYmxlcywgaW5jbHVzaW9ucywga2VlcGVycywgYWxsX2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwgd2hpY2ggPSAidXBzIikgewogIHJldGxpc3QgPC0gbGlzdCgpCiAgdGFibGVfbmFtZXMgPC0gbmFtZXMoaW5jbHVzaW9uX3NpZ1tbYWNjb3JkaW5nX3RvXV1bW3doaWNoXV0pCiAgZm9yIChjX251bSBpbiBzZXFfYWxvbmcoa2VlcGVycykpIHsKICAgIGNvbnRyYXN0IDwtIG5hbWVzKGtlZXBlcnMpW2NfbnVtXQogICAgbnVtZXJhdG9yX25hbWUgPC0ga2VlcGVyc1tbY19udW1dXVsxXQogICAgZGVub21pbmF0b3JfbmFtZSA8LSBrZWVwZXJzW1tjX251bV1dWzJdCiAgICAjIyBJbiBteSBuZXcgYnJhbmNoIEkgY2xlYW5lZCB1cCB0aGUgc2FuaXRpemVyIGZ1bmN0aW9uIGZvciBjb250cmFzdHMgc28gdGhpcyBpcyBub3QgbmVlZGVkLgogICAgIyMgVGhlIGZvbGxvd2luZyB0d28gbGluZXMgYXJlIG5vIGxvbmdlciBuZWVkZWQgYmVjYXVzZSBvZiB0aGUgY2xlYW51cHMgSSBwZXJmb3JtZWQuCiAgICAjI251bWVyYXRvcl9uYW1lIDwtIGdzdWIoeCA9IG51bWVyYXRvcl9uYW1lLCBwYXR0ZXJuID0gIihoZXR8a298d3QpIiwgcmVwbGFjZW1lbnQgPSAiX1xcMV8iKQogICAgIyNkZW5vbWluYXRvcl9uYW1lIDwtIGdzdWIoeCA9IGRlbm9taW5hdG9yX25hbWUsIHBhdHRlcm4gPSAiKGhldHxrb3x3dCkiLCByZXBsYWNlbWVudCA9ICJfXFwxXyIpCiAgICBudW1lcmF0b3JfdGFibGUgPC0gaW5jbHVzaW9uX3NpZ1tbYWNjb3JkaW5nX3RvXV1bW3doaWNoXV1bW251bWVyYXRvcl9uYW1lXV0KICAgIG51bWVyYXRvcl9nZW5lcyA8LSByb3duYW1lcyhudW1lcmF0b3JfdGFibGUpCiAgICBkZW5vbWluYXRvcl90YWJsZSA8LSBpbmNsdXNpb25fc2lnW1thY2NvcmRpbmdfdG9dXVtbd2hpY2hdXVtbZGVub21pbmF0b3JfbmFtZV1dCiAgICBkZW5vbWluYXRvcl9nZW5lcyA8LSByb3duYW1lcyhkZW5vbWluYXRvcl90YWJsZSkKICAgIGRmX2NvbHVtbnMgPC0gcGFzdGUwKCJkZXNlcV8iLCBjKCJsb2dmYyIsICJhZGpwIiwgImRlbiIpKQogICAgaW5jbHVkZWRfbnVtIDwtIGluY2x1c2lvbl90YWJsZXNbWyJkYXRhIl1dW1tudW1lcmF0b3JfbmFtZV1dWywgZGZfY29sdW1uc10KICAgIGNvbG5hbWVzKGluY2x1ZGVkX251bSkgPC0gYygibnVtZXJhdG9yX3ZzX3d0X2xvZ2ZjIiwgIm51bWVyYXRvcl92c193dF9hZGpwIiwgIm51bV93dF9tZWFuX2V4cHJzIikKICAgIGluY2x1ZGVkX2RlbiA8LSBpbmNsdXNpb25fdGFibGVzW1siZGF0YSJdXVtbZGVub21pbmF0b3JfbmFtZV1dWywgZGZfY29sdW1uc10KICAgIGNvbG5hbWVzKGluY2x1ZGVkX2RlbikgPC0gYygiZGVub21pbmF0b3JfdnNfd3RfbG9nZmMiLCAiZGVub21pbmF0b3JfdnNfd3RfYWRqcCIsICJkZW5fd3RfbWVhbl9leHBycyIpCiAgICBpbmNsdWRlZF9kZiA8LSBtZXJnZShpbmNsdWRlZF9udW0sIGluY2x1ZGVkX2RlbiwgYnkgPSAicm93Lm5hbWVzIikKICAgIHJvd25hbWVzKGluY2x1ZGVkX2RmKSA8LSBpbmNsdWRlZF9kZltbIlJvdy5uYW1lcyJdXQogICAgaW5jbHVkZWRfZGZbWyJSb3cubmFtZXMiXV0gPC0gTlVMTAogICAgaW5jbHVkZV9nZW5lcyA8LSB1bmlxdWUoYyhudW1lcmF0b3JfZ2VuZXMsIGRlbm9taW5hdG9yX2dlbmVzKSkKICAgIG1lc3NhZ2UoIlRoZSBzZXQgb2YgdW5pcXVlIGdlbmVzIGhpZ2hlciBpbiAiLCBudW1lcmF0b3JfbmFtZSwKICAgICAgICAgICAgIiB2cy4gd3QgaXMgIiwgbGVuZ3RoKG51bWVyYXRvcl9nZW5lcyksICIuIikKICAgIG1lc3NhZ2UoIlRoZSBzZXQgb2YgdW5pcXVlIGdlbmVzIGhpZ2hlciBpbiAiLCBkZW5vbWluYXRvcl9uYW1lLAogICAgICAgICAgICAiIHZzLiB3dCBpcyAiLCBsZW5ndGgoZGVub21pbmF0b3JfZ2VuZXMpLCAiLiIpCiAgICBtZXNzYWdlKCJUaGUgdW5pcXVlIHVuaW9uIG9mIHRoZW0gaXMgIiwgbGVuZ3RoKGluY2x1ZGVfZ2VuZXMpLCAiIGdlbmVzLiIpCiAgICBpbmNsdWRlX25hbWUgPC0gcGFzdGUwKCJpbmNfIiwgY29udHJhc3QpCiAgICBpbmNsdWRlX2lkeCA8LSBhbGxfZ2VuZXMgJWluJSBpbmNsdWRlX2dlbmVzCiAgICBpbmNsdWRlX2dlbmVzIDwtIGFsbF9nZW5lc1tpbmNsdWRlX2lkeF0KICAgIGRmX25hbWUgPC0gcGFzdGUwKCJkZl8iLCBjb250cmFzdCkKICAgIHJldGxpc3RbW2RmX25hbWVdXSA8LSBpbmNsdWRlZF9kZgogICAgd3JpdHRlbl9pbmNsdXNpb24gPC0gd3JpdGVfeGxzeChkYXRhID0gaW5jbHVkZWRfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsID0gZ2x1ZSgiMDdpbmNsdWRlZF9nZW5lc190eC97aW5jbHVkZV9uYW1lfS12e3Zlcn0ueGxzeCIpKQogICAgcmV0bGlzdFtbaW5jbHVkZV9uYW1lXV0gPC0gaW5jbHVkZV9nZW5lcwogICAgcmV0bGlzdFtbY29udHJhc3RdXSA8LSBpbmNsdWRlX2dlbmVzCiAgfQogIHJldHVybihyZXRsaXN0KQp9CmBgYAoKIyAnTm9ybWFsJyBJbmNsdXNpb24gZXh0cmFjdGlvbgoKSGVyZSBpcyB0aGUgZnVsbCBzZXQgb2YgZ2VuZSBJRHMKCmBgYHtyfQphbGxfZ2VuZXMgPC0gcm93bmFtZXMoYXNzYXkodHhfcGFpcndpc2VfaW5wdXQpKQpgYGAKCkluIHRoZSBmb2xsb3dpbmcgYmxvY2tzIEkgYW0gaW5jbHVkaW5nIHRoZSB1bmlvbiBvZiBnZW5lcyBvYnNlcnZlZApoaWdoZXIgdGhhbiB3dCBpbiBlaXRoZXIgb2YgdGhlIG51bWVyYXRvciBvciBkZW5vbWluYXRvciBmb3IgZWFjaApjb250cmFzdC4KCiMjIFRpbWUKCmBgYHtyfQp0aW1lX2luY2x1c2lvbnMgPC0gZXh0cmFjdF9pbmNsdXNpb25zKGluY2x1c2lvbl9zaWcsIGluY2x1c2lvbl90YWJsZXMsIGluY2x1c2lvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZV9rZWVwZXJzLCBhbGxfZ2VuZXMpCmBgYAoKIyMgTG9jYXRpb24KCmBgYHtyfQpsb2NhdGlvbl9pbmNsdXNpb25zIDwtIGV4dHJhY3RfaW5jbHVzaW9ucyhpbmNsdXNpb25fc2lnLCBpbmNsdXNpb25fdGFibGVzLCBpbmNsdXNpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbl9rZWVwZXJzLCBhbGxfZ2VuZXMpCmBgYAoKIyMgR2Vub3R5cGUKCmBgYHtyfQpnZW5vdHlwZV9pbmNsdXNpb25zIDwtIGV4dHJhY3RfaW5jbHVzaW9ucyhpbmNsdXNpb25fc2lnLCBpbmNsdXNpb25fdGFibGVzLCBpbmNsdXNpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5vdHlwZV9rZWVwZXJzLCBhbGxfZ2VuZXMpCmBgYAoKIyBUaGUgc3RyaWN0ICdpbmNsdXNpb24nIHNldAoKIyMgVGltZQoKYGBge3J9CnRpbWVfaW5jbHVzaW9uc19zdHJpY3QgPC0gZXh0cmFjdF9pbmNsdXNpb25zX3N0cmljdChpbmNsdXNpb25fc2lnLCBpbmNsdXNpb25fdGFibGVzLCBpbmNsdXNpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZV9rZWVwZXJzLCBhbGxfZ2VuZXMpCmBgYAoKIyMgTG9jYXRpb24KCmBgYHtyfQpsb2NhdGlvbl9pbmNsdXNpb25zX3N0cmljdCA8LSBleHRyYWN0X2luY2x1c2lvbnNfc3RyaWN0KGluY2x1c2lvbl9zaWcsIGluY2x1c2lvbl90YWJsZXMsIGluY2x1c2lvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jYXRpb25fa2VlcGVycywgYWxsX2dlbmVzKQpgYGAKCiMjIEdlbm90eXBlCgpgYGB7cn0KZ2Vub3R5cGVfaW5jbHVzaW9uc19zdHJpY3QgPC0gZXh0cmFjdF9pbmNsdXNpb25zX3N0cmljdChpbmNsdXNpb25fc2lnLCBpbmNsdXNpb25fdGFibGVzLCBpbmNsdXNpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbm90eXBlX2tlZXBlcnMsIGFsbF9nZW5lcykKYGBgCgojIyMgQ2hlY2sgdnMgVGhlcmVzYSdzIGZpbHRlcgoKVXAgYWJvdmUgVGhlcmVzYSBwZXJmb3JtZWQgYSAwLjI1IGxvZzJGQyBhbmQgMC4wNSBhZGpwIGZpbHRlciB3aGljaApwcm92aWRlZCBhIHNldCBvZiAyLDY0MCBnZW5lcyBvYnNlcnZlZCBoaWdoZXIgaW4gdGhlIHAwOCBoZXQgcmV0aW5hcwp2cy4gd3QgcmV0aW5hcy4gIEkgc2hvdWxkIHNlZSB0aGF0IGluIHRoaXMgaW5jbHVzaW9uX3NpZyBkYXRhIHN0cnVjdHVyZS4KClRoZXJlIGlzIGFuIGltcG9ydGFudCBjYXZlYXQgdGhvdWdoOiBpbiBUaGVyZXNhJ3MgZmlsdGVyIGFib3ZlLCBzaGUKZGlkIGEgREUgb2YgX29ubHlfIHRoZSByZXRpbmEgc2FtcGxlcyBidXQgSSBkaWQgYWxsIHNhbXBsZXMuICBJCmV4cGVjdGVkIHRoYXQgdGhpcyB3b3VsZCByZXN1bHQgaW4gYmFzaWNhbGx5IHRoZSBzYW1lIHJlc3VsdCAoSQphY3R1YWxseSBhc3N1bWVkIEkgd291bGQgZ2V0IGEgZmV3IG1vcmUgZ2VuZXMpLCBidXQgaW5zdGVhZCBpdCBhcHBlYXJzCnRvIGhhdmUgcmV0cmlldmVkIGEgc2lnbmlmaWNhbnRseSBzbWFsbGVyIG51bWJlciBvZiBnZW5lcyAoYWJvdXQgMS8yLApoYXBwaWx5IHRoZXkgcHJldHR5IG11Y2ggYWxsIGFwcGVhciBpbiB0aGUgcHJldmlvdXMgZmlsdGVyKS4gIEFzIGEKcmVzdWx0LCBJIGFtIGdvaW5nIHRvIHRyeSByZWxheGluZyBteSBjb25zdHJhaW50cyBzbGlnaHRseSB0byBzZWUgaWYgSQpjYW4gcmVjYXBpdHVsYXRlIGhlciBmaWx0ZXIgKHdoaWNoIHdvdWxkIG1hdGNoIFRoZXJlc2EncyBsYXRlciBmaWx0ZXIsCnRob3VnaCBJIGd1ZXNzIHRoYXQgaW4gdHVybiB3aWxsIGxlYWQgdG8gYSBzbWFsbGVyIHNldCBvZiBnZW5lcwpjb21wYXJlZCB0byBoZXIgbGF0ZXIsIHJlbGF4ZWQgMC4xIGZpbHRlcikuCgpgYGB7cn0KY29tcGFyaXNvbiA8LSBpbmNsdXNpb25fc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJwMDhfaGV0X3JldGluYSJdXQpjb21wIDwtIGxpc3QoCiAgInRhYSIgPSB0YWFfa2VlcGVycywKICAibmV3IiA9IHJvd25hbWVzKGNvbXBhcmlzb24pKQp0ZXN0X2NvbXBhcmlzb24gPC0gVmVubmVyYWJsZTo6VmVubihjb21wKQpWZW5uZXJhYmxlOjpwbG90KHRlc3RfY29tcGFyaXNvbikKYGBgCgpJIHdhbnQgdG8gaGF2ZSBhIGxpdHRsZSBmdW5jdGlvbiB3aGljaCwgZ2l2ZW4gYSBjb250cmFzdCBvZiBpbnRlcmVzdCwKd2lsbCBleHRyYWN0IHRoZSBnZW5lIHNldHMgd2hpY2ggc2hvdWxkIGJlIGluY2x1ZGVkL2V4Y2x1ZGVkIGdpdmVuIHRoZQphYm92ZS4KCmBgYHtyfQp3cml0ZV9hbGxfY3AgPC0gZnVuY3Rpb24oYWxsX2NwLCBwcmVmaXggPSAiMTIiLCBzdWZmaXggPSAiIikgewogIGFsbF93cml0dGVuIDwtIGxpc3QoKQogIGZvciAoZyBpbiBzZXFfbGVuKGxlbmd0aChhbGxfY3ApKSkgewogICAgbmFtZSA8LSBuYW1lcyhhbGxfY3ApW2ddCiAgICBkYXR1bSA8LSBhbGxfY3BbW25hbWVdXQogICAgZmlsZW5hbWUgPC0gZ2x1ZSgie3ByZWZpeH1lbnJpY2htZW50X2V4Y2VsX3R4L3tuYW1lfV9jcHJvZmlsZXJ7c3VmZml4fS12e3Zlcn0ueGxzeCIpCiAgICB3cml0dGVuIDwtIHNtKHdyaXRlX2NwX2RhdGEoZGF0dW0sIGV4Y2VsID0gZmlsZW5hbWUpKQogICAgYWxsX3dyaXR0ZW5bW2ddXSA8LSB3cml0dGVuCiAgfQogIHJldHVybihhbGxfd3JpdHRlbikKfQp3cml0ZV9hbGxfZ3AgPC0gZnVuY3Rpb24oYWxsX2dwLCBwcmVmaXggPSAiMTMiLCBzdWZmaXggPSAiIikgewogIGFsbF93cml0dGVuIDwtIGxpc3QoKQogIGZvciAoZyBpbiBzZXFfbGVuKGxlbmd0aChhbGxfZ3ApKSkgewogICAgbmFtZSA8LSBuYW1lcyhhbGxfZ3ApW2ddCiAgICBkYXR1bSA8LSBhbGxfZ3BbW25hbWVdXQogICAgZmlsZW5hbWUgPC0gZ2x1ZSgie3ByZWZpeH1lbnJpY2htZW50X2V4Y2VsX3R4L3tuYW1lfV9ncHJvZmlsZXJ7c3VmZml4fS12e3Zlcn0ueGxzeCIpCiAgICB3cml0dGVuIDwtIHNtKHdyaXRlX2dwcm9maWxlcl9kYXRhKGRhdHVtLCBleGNlbCA9IGZpbGVuYW1lKSkKICAgIGFsbF93cml0dGVuW1tnXV0gPC0gd3JpdHRlbgogIH0KICByZXR1cm4oYWxsX3dyaXR0ZW4pCn0Kd3JpdGVfYWxsX2VuIDwtIGZ1bmN0aW9uKGFsbF9lbiwgcHJlZml4ID0gIjE0Iiwgc3VmZml4ID0gIiIpIHsKICBhbGxfd3JpdHRlbiA8LSBsaXN0KCkKICBmb3IgKGUgaW4gc2VxX2xlbihsZW5ndGgoYWxsX2VuKSkpIHsKICAgIG5hbWUgPC0gbmFtZXMoYWxsX2VuKVtlXQogICAgZGF0dW0gPC0gYWxsX2VuW1tuYW1lXV0KICAgIGZpbGVuYW1lIDwtIGdsdWUoIntwcmVmaXh9ZW5yaWNobWVudF9leGNlbF90eC97bmFtZX1fZW5yaWNoZXJ7c3VmZml4fS12e3Zlcn0ueGxzeCIpCiAgICB3cml0dGVuIDwtIHNtKHdyaXRlX2VucmljaGVyX2RhdGEoZGF0dW0sIGV4Y2VsID0gZmlsZW5hbWUpKQogICAgYWxsX3dyaXR0ZW5bW2VdXSA8LSB3cml0dGVuCiAgfQogIHJldHVybihhbGxfd3JpdHRlbikKfQpgYGAKCiMjIyBFeHRyYWN0IGdlbmVzIGluY2x1ZGVkIGZvciBlYWNoIHNldCBvZiBjb250cmFzdHMKCk5vdywgdXNpbmcgdGhhdCBmdW5jdGlvbiwgcHVsbCBvdXQgdGhlIGdlbmUgSURzIG9mIGdlbmVzIHdlIGRvIG5vdAp0cnVzdCBiZWNhdXNlIHRoZXkgd2VyZSB0b28gaGlnaCBpbiB3dCBmb3IgZXZlcnkgY29udHJhc3Qgd2UgYXJlCmxpa2VseSB0byBwZXJmb3JtLgoKYGBge3J9CmFsbF9nZW5lcyA8LSByb3duYW1lcyhhc3NheSh0eF9wYWlyd2lzZV9pbnB1dCkpCnRpbWVfaW5jbHVzaW9ucyA8LSBleHRyYWN0X2luY2x1c2lvbnMoaW5jbHVzaW9uX3NpZywgaW5jbHVzaW9uX3RhYmxlcywgaW5jbHVzaW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lX2tlZXBlcnMsIGFsbF9nZW5lcykKbG9jYXRpb25faW5jbHVzaW9ucyA8LSBleHRyYWN0X2luY2x1c2lvbnMoaW5jbHVzaW9uX3NpZywgaW5jbHVzaW9uX3RhYmxlcywgaW5jbHVzaW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jYXRpb25fa2VlcGVycywgYWxsX2dlbmVzKQpnZW5vdHlwZV9pbmNsdXNpb25zIDwtIGV4dHJhY3RfaW5jbHVzaW9ucyhpbmNsdXNpb25fc2lnLCBpbmNsdXNpb25fdGFibGVzLCBpbmNsdXNpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5vdHlwZV9rZWVwZXJzLCBhbGxfZ2VuZXMpCmBgYAoKIyMgUGVyZm9ybSB0aGUgREUgYW5hbHlzZXMgYW5kIGV4Y2x1ZGUgdGhlIHRhcmdldCBnZW5lcwoKCiMjIyBHZW5vdHlwZQoKYGBge3J9Cmdlbm90eXBlX2RlIDwtIGFsbF9wYWlyd2lzZSh0eF9wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwgbW9kZWxfZnN0cmluZyA9IGRlZmF1bHRfZnN0cmluZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBnZW5vdHlwZV9rZWVwZXJzLCBtb2RlbF9zdnMgPSAic3Zhc2VxIikKZ2Vub3R5cGVfZGUKYGBgCgojIyMgTG9jYXRpb24KCmBgYHtyfQpsb2NhdGlvbl9kZSA8LSBhbGxfcGFpcndpc2UodHhfcGFpcndpc2VfaW5wdXQsIGZpbHRlciA9IFRSVUUsIG1vZGVsX2ZzdHJpbmcgPSBkZWZhdWx0X2ZzdHJpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwZXJzID0gbG9jYXRpb25fa2VlcGVycywgbW9kZWxfc3ZzID0gInN2YXNlcSIpCmxvY2F0aW9uX2RlCmBgYAoKIyMjIFRpbWUKCmBgYHtyfQp0aW1lX2RlIDwtIGFsbF9wYWlyd2lzZSh0eF9wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwgbW9kZWxfZnN0cmluZyA9IGRlZmF1bHRfZnN0cmluZywKICAgICAgICAgICAgICAgICAgICAgICAga2VlcGVycyA9IHRpbWVfa2VlcGVycywgbW9kZWxfc3ZzID0gInN2YXNlcSIpCnRpbWVfZGUKYGBgCgpJdCBpcyBuZWFyIGhlcmUgd2hlbiB0aGUgY29tcHV0ZXIgc29tZXRpbWVzIGZhaWxzIHdpdGggbm8gbW9yZQp0ZW1wZmlsZXMuICBJbiBhbm90aGVyIHdpbmRvdyBJIGFtIG1lc3Npbmcgd2l0aCB0ZW1wZmlsZSgpIGluIFIgdG8gdHJ5CnRvIHVuZGVyc3RhbmQgd2hlcmUgaXQgaXMgZ29pbmcgb2ZmIHRoZSByYWlscy4uLgoKIyMgRXh0cmFjdCB0aGUgcmVsZXZhbnQgdGFibGVzIGFuZCBpbmNsdWRlIGdlbmVzIGxvd2VyIGluIHd0CgojIyMgR2Vub3R5cGUgY29udHJhc3RzCgpJIHdpbGwgc3RhcnQgd2l0aCB0aGUgdGFibGVzIGFuZCBubyBpbmNsdXNpb25zIHNvIEkgY2FuIGNoZWNrIG15IHdvcmsuCgpJbiB0aGlzIGZpcnN0IGJsb2NrIEkgd2lsbCBleHBsYWluIGEgbGl0dGxlIG1vcmUgdGhvcm91Z2hseSB3aGF0IGlzCmdvaW5nIG9uOgoKMS4gIER1bXAgdGhlIGZ1bGwgdGFibGUgb2YgdGhlIGNvbnRyYXN0cyBJIGRlZmluZWQgYWJvdmUgY29tcGFyaW5nIHRoZQogICAgMyBnZW5vdHlwZXMgYWNyb3NzIHRpbWUvbG9jYXRpb24uCjIuICBJdGVyYXRlIG92ZXIgZWFjaCBvZiB0aG9zZSBjb250cmFzdHMgYW5kIGRvIHRoZSBmb2xsb3dpbmc6CiAgICBhLiAgRXh0cmFjdCB0aGUgbmFtZSBvZiB0aGUgY29udHJhc3QsICdraF9wMDhfZGxnbicgZm9yIGV4YW1wbGUKICAgIGIuICBZYW5rIG91dCB0aGF0IHNwZWNpZmljIGVudHJ5IGZyb20gdGhlIGtlZXBlciBsaXN0IGFuZCBpdHMgbmFtZQogICAgYy4gIFlhbmsgb3V0IHRoZSBjb3JyZXNwb25kaW5nIHNldCBvZiBnZW5lcyB0byBpbmNsdWRlIGZyb20gdGhlCiAgICAgICAgaW5jbHVzaW9ucyBkYXRhIHN0cnVjdHVyZS4KICAgIGQuICBDcmVhdGUgYSBmaWxlbmFtZSBnaXZlbiB0aGUgbmFtZSBpbiAoYSkgYWJvdmUgYW5kIHRoZSBsb2dGQwogICAgICAgIGN1dG9mZiBjaG9zZW4gZm9yIHRoZSBpbmNsdXNpb25zIChJIGFtIGFzc3VtaW5nIHdlIG1heSBjaGFuZ2UKICAgICAgICB0aGlzKQogICAgZS4gIEdpdmVuIChiKSwgKGMpLCBhbmQgKGQpLCBleHRyYWN0IHRoZSBjb3JyZXNwb25kaW5nIHRhYmxlIGZyb20KICAgICAgICB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgYW5kIGluY2x1ZGUgdGhlIGFwcHJvcHJpYXRlCiAgICAgICAgZ2VuZXMuCgogICAgRklYTUU6IG15IGdwcm9maWxlciBmdW5jdGlvbiBqdXN0IGFzc3VtZXMgaHVtYW4gYW5kIHNvIGlmIHBhc3NlZCBtbXVzY3VsdXMgd2lsbCBpbmNvcnJlY3RseQogICAgYXR0ZW1wdCB0byBjb25uZWN0IHRvIG5vbi1leGlzdGFudCBkYXRhYmFzZXMuICBMZXQgdXMgZml4IHRoYXQgbm93LgoKYGBge3J9Cmdlbm90eXBlX3RhYmxlc19mdWxsIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIGdlbm90eXBlX2RlLCBrZWVwZXJzID0gZ2Vub3R5cGVfa2VlcGVycywgbGFiZWxfY29sdW1uID0gdHhfbGFiZWxfY29sdW1uLAogIGZhbmN5ID0gVFJVRSwKICBleGNlbCA9IGdsdWUoIjA4ZnVsbF9jb250cmFzdHNfZXhjZWxfdHgvZ2Vub3R5cGVfZnVsbF90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKZ2Vub3R5cGVfdGFibGVzX2Z1bGwKZ2Vub3R5cGVfc2lnX2Z1bGwgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBnZW5vdHlwZV90YWJsZXNfZnVsbCwgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICBleGNlbCA9IGdsdWUoIjA4ZnVsbF9jb250cmFzdHNfZXhjZWxfdHgvZ2Vub3R5cGVfZnVsbF9zaWctdnt2ZXJ9Lnhsc3giKSkKZ2Vub3R5cGVfc2lnX2Z1bGwKYGBgCgojIyMgU2VhcmNoIEVucmljaG1lbnQgb2YgdGhlc2Ugc2V0cwoKSW4gdGhpcyBydW4sIHdlIHdpbGwgc2VhcmNoIHRoZSBmdWxsIHNldCBvZiBnZW5lcywgbmV4dCB3ZSB3aWxsIG9ubHkKZG8gdGhlIGluY2x1c2lvbnMuCgpgYGB7cn0KZ2Vub3R5cGVfZnVsbF9ncCA8LSBhbGxfZ3Byb2ZpbGVyKGdlbm90eXBlX3NpZ19mdWxsLCBzcGVjaWVzID0gIm1tdXNjdWx1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9ICIwOWZ1bGxfY29udHJhc3RzX2VucmljaF90eC9nZW5vdHlwZV9mdWxsX2dwcm9maWxlci54bHN4IikKZ2Vub3R5cGVfZnVsbF9jcCA8LSBhbGxfY3Byb2ZpbGVyKGdlbm90eXBlX3NpZ19mdWxsLCBnZW5vdHlwZV90YWJsZXNfZnVsbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2RiID0gIm9yZy5NbS5lZy5kYiIsIGdvX2xldmVsID0gZ29fbGV2ZWwsIG9yZ2FuaXNtID0gIm1vdXNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2RiX2Zyb20gPSBvcmdkYl9mcm9tLCBtYXhfZ3JvdXBzaXplID0gbWF4X2dyb3Vwc2l6ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsID0gIjA5ZnVsbF9jb250cmFzdHNfZW5yaWNoX3R4L2dlbm90eXBlX2Z1bGxfY3Byb2ZpbGVyLnhsc3giKQpnZW5vdHlwZV9mdWxsX3Vwc2V0IDwtIHVwc2V0cl9zaWcoZ2Vub3R5cGVfc2lnX2Z1bGwpCmdlbm90eXBlX2Z1bGxfaW50ZXJzZWN0cyA8LSB3cml0ZV91cHNldF9ncm91cHMoZ2Vub3R5cGVfZnVsbF91cHNldCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9ICIwOWZ1bGxfY29udHJhc3RzX2ludGVyc2VjdGlvbnNfdHgvZ2Vub3R5cGVfZnVsbF9nZW5lX2dyb3Vwcy54bHN4IikKYGBgCgpOb3cgc2VwYXJhdGUgdGhlIHZhcmlvdXMgZ2Vub3R5cGUgdGFibGVzIGFuZCBwZXJmb3JtIHRoZSBpbmNsdXNpb25zIG9mCnRoZSBnZW5lcyB3aXRoIHJlbGF0aXZlbHkgbG93IHd0IHZhbHVlcy4KCiMjIyMgRXh0cmFjdCBmaWx0ZXJlZCBnZW5lcyBhbmQgcGFzcyB0byBncHJvZmlsZXIyL2NsdXN0ZXJwcm9maWxlcgoKYGBge3J9Cmdlbm90eXBlX3RhYmxlcyA8LSBsaXN0KCkKZ2Vub3R5cGVfc2lnIDwtIGxpc3QoKQpnZW5vdHlwZV9ncCA8LSBsaXN0KCkKZ2Vub3R5cGVfY3AgPC0gbGlzdCgpCmdlbm90eXBlX2VuIDwtIGxpc3QoKQpmb3IgKGsgaW4gc2VxX2Fsb25nKGdlbm90eXBlX2tlZXBlcnMpKSB7CiAgbmFtZSA8LSBuYW1lcyhnZW5vdHlwZV9rZWVwZXJzKVtrXQogIG1lc3NhZ2UoIkV4YW1pbmluZyAiLCBuYW1lKQogIGtlZXBlciA8LSBnZW5vdHlwZV9rZWVwZXJzW25hbWVdCiAgaW5jbHVkZV9uYW1lIDwtIHBhc3RlMCgiaW5jXyIsIG5hbWUpCiAgaW5jbHVkZV9kZl9uYW1lIDwtIHBhc3RlMCgiZGZfIiwgbmFtZSkKICBpbmNsdWRlX2RmIDwtIGdlbm90eXBlX2luY2x1c2lvbnNbW2luY2x1ZGVfZGZfbmFtZV1dCiAgaW5jbHVkZXMgPC0gZ2Vub3R5cGVfaW5jbHVzaW9uc1tbaW5jbHVkZV9uYW1lXV0KICBzdW1tYXJ5KHJvd25hbWVzKGdlbm90eXBlX3NpZ19mdWxsW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgJWluJSBpbmNsdWRlcykKICBpbmNsdWRlX2ZpbGVuYW1lIDwtIGdsdWUoIjEwZ2Vub3R5cGVfY29udHJhc3RzX2V4Y2VsX3R4L3tuYW1lfV9pbmNsdWRpbmdfd3Rfe2xmY19jdXRvZmZ9X2RlY3JlYXNlZF90YWJsZS12e3Zlcn0ueGxzeCIpCiAgaW5jbHVkZV9zaWdfZmlsZW5hbWUgPC0gZ2x1ZSgiMTBnZW5vdHlwZV9jb250cmFzdHNfZXhjZWxfdHgve25hbWV9X2luY2x1ZGluZ193dF97bGZjX2N1dG9mZn1fZGVjcmVhc2VkX3NpZy12e3Zlcn0ueGxzeCIpCiAgZ2Vub3R5cGVfdGFibGVzW1tuYW1lXV0gPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICBnZW5vdHlwZV9kZSwgZXh0cmFfYW5ub3QgPSBpbmNsdWRlX2RmLAogICAga2VlcGVycyA9IGtlZXBlciwgbGFiZWxfY29sdW1uID0gdHhfbGFiZWxfY29sdW1uLAogICAgZXhjZWwgPSBpbmNsdWRlX2ZpbGVuYW1lLCB3YW50ZWRfZ2VuZXMgPSBpbmNsdWRlcykKICBwcmludChnZW5vdHlwZV90YWJsZXNbW25hbWVdXSkKICBnZW5vdHlwZV9zaWdbW25hbWVdXSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgZ2Vub3R5cGVfdGFibGVzW1tuYW1lXV0sIGFjY29yZGluZ190byA9ICJkZXNlcSIsCiAgICBleGNlbCA9IGluY2x1ZGVfc2lnX2ZpbGVuYW1lKQogIHByaW50KGdlbm90eXBlX3NpZ1tbbmFtZV1dKQogIG51bV9yb3dzIDwtIG5yb3coZ2Vub3R5cGVfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSArCiAgICBucm93KGdlbm90eXBlX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dKQogIG1lc3NhZ2UoIlRoZXJlIGFyZSAiLCBudW1fcm93cywgIiBzaWduaWZpY2FudCB1cCBhbmQgZG93biBnZW5lcy4iKQogIGlmIChudW1fcm93cyA+PSAxMCkgewogICAgbWVzc2FnZSgiUGVyZm9ybWluZyBncHJvZmlsZXIvY2x1c3RlclByb2ZpbGVyLiIpCiAgICBnZW5vdHlwZV9ncFtbbmFtZV1dIDwtIGFsbF9ncHJvZmlsZXIoZ2Vub3R5cGVfc2lnW1tuYW1lXV0sIHNwZWNpZXMgPSAibW11c2N1bHVzIikKICAgIGdlbm90eXBlX2NwW1tuYW1lXV0gPC0gYWxsX2Nwcm9maWxlcigKICAgICAgZ2Vub3R5cGVfc2lnW1tuYW1lXV0sIGdlbm90eXBlX3RhYmxlc1tbbmFtZV1dLAogICAgICBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBvcmdkYl9mcm9tID0gb3JnZGJfZnJvbSwKICAgICAgZ29fbGV2ZWwgPSBnb19sZXZlbCwgbWF4X2dyb3Vwc2l6ZSA9IG1heF9ncm91cHNpemUsIG9yZ2FuaXNtID0gIm1vdXNlIikKICAgICNpZiAoIWlzLm51bGwoZ2V0MCgibTJfZ3NjIikpKSB7CiAgICAjICBnZW5vdHlwZV9lbltbbmFtZV1dIDwtIGFsbF9lbnJpY2hlcihnZW5vdHlwZV9zaWdbW25hbWVdXSwgZ3NjID0gbTJfZ3NjLAogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnZGIgPSAib3JnLk1tLmVnLmRiIiwgZnJvbSA9ICJFTlNFTUJMIiwgdG8gPSAiU1lNQk9MIikKICAgICN9CiAgICBncF93cml0dGVuIDwtIHdyaXRlX2FsbF9ncChnZW5vdHlwZV9ncFtbbmFtZV1dLCBwcmVmaXggPSAiMTEiKQogICAgY3Bfd3JpdHRlbiA8LSB3cml0ZV9hbGxfY3AoZ2Vub3R5cGVfY3BbW25hbWVdXSwgcHJlZml4ID0gIjExIikKICAgICNlbl93cml0dGVuIDwtIHdyaXRlX2FsbF9lbihnZW5vdHlwZV9lbltbbmFtZV1dKQogIH0gZWxzZSB7CiAgICB3YXJuaW5nKCJUaGVyZSBhcmUgbGVzcyB0aGFuIDEwIGdlbmVzIHVwIGFuZCBkb3duIGluIHRoZSAiLCBuYW1lLCAiIGNvbXBhcmlzb24uIikKICAgIG1lc3NhZ2UoIlRoZXJlIGFyZSBsZXNzIHRoYW4gMTAgZ2VuZXMgdXAgYW5kIGRvd24gaW4gdGhlICIsIG5hbWUsICIgY29tcGFyaXNvbi4iKQogIH0KfQpgYGAKClBsb3QgdGhlIHJlc3VsdHMgc2VwYXJhdGVseS4KCmBgYHtyfQpmb3IgKGsgaW4gc2VxX2Fsb25nKGdlbm90eXBlX2tlZXBlcnMpKSB7CiAgbmFtZSA8LSBuYW1lcyhnZW5vdHlwZV9rZWVwZXJzKVtrXQogIG1lc3NhZ2UoIkV4YW1pbmluZyAiLCBuYW1lKQogIGtlZXBlciA8LSBnZW5vdHlwZV9rZWVwZXJzW25hbWVdCiAgaW5jbHVkZV9uYW1lIDwtIHBhc3RlMCgiaW5jXyIsIG5hbWUpCiAgaW5jbHVkZV9kZl9uYW1lIDwtIHBhc3RlMCgiZGZfIiwgbmFtZSkKICBpbmNsdWRlX2RmIDwtIGdlbm90eXBlX2luY2x1c2lvbnNbW2luY2x1ZGVfZGZfbmFtZV1dCiAgaW5jbHVkZXMgPC0gZ2Vub3R5cGVfaW5jbHVzaW9uc1tbaW5jbHVkZV9uYW1lXV0KICBzdW1tYXJ5KHJvd25hbWVzKGdlbm90eXBlX3NpZ19mdWxsW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgJWluJSBpbmNsdWRlcykKICBudW1fcm93cyA8LSBucm93KGdlbm90eXBlX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgKwogICAgbnJvdyhnZW5vdHlwZV9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSkKICBucm93KGdlbm90eXBlX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkKICBucm93KGdlbm90eXBlX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dKQogIG1lc3NhZ2UoIlRoZXJlIGFyZSAiLCBudW1fcm93cywgIiBzaWduaWZpY2FudCB1cCBhbmQgZG93biBnZW5lcy4iKQogICMjICMxIGlzIHVwIGFuZCAjMiBpcyBkb3duLCBhdm9pZGluZyB0eXBlb3MgaGVyZS4KICBudW1fb2JqZWN0cyA8LSBsZW5ndGgoZ2Vub3R5cGVfY3BbW25hbWVdXSkKICBpZiAobnVtX29iamVjdHMgPT0gMCkgewogICAgd2FybmluZygiU29tZXRoaW5nIHdlbnQgd3JvbmcgaW4gYWxsX2Nwcm9maWxlci4iKQogIH0gZWxzZSB7CiAgICB1cHAgPC0gd2hpY2goZ3JlcGwoeCA9IG5hbWVzKGdlbm90eXBlX2NwW1tuYW1lXV0pLCBwYXR0ZXJuID0gIl91cCQiKSkKICAgIGRvd25wIDwtIHdoaWNoKGdyZXBsKHggPSBuYW1lcyhnZW5vdHlwZV9jcFtbbmFtZV1dKSwgcGF0dGVybiA9ICJfZG93biQiKSkKICAgIGlmIChsZW5ndGgodXBwKSA+IDApIHsKICAgICAgbWZfc2lnIDwtIGdlbm90eXBlX2NwW1tuYW1lXV1bW3VwcF1dW1siZ29fZGF0YSJdXVtbIk1GX2VucmljaCJdXQogICAgICBjY19zaWcgPC0gZ2Vub3R5cGVfY3BbW25hbWVdXVtbdXBwXV1bWyJnb19kYXRhIl1dW1siQ0NfZW5yaWNoIl1dCiAgICAgIGJwX3NpZyA8LSBnZW5vdHlwZV9jcFtbbmFtZV1dW1t1cHBdXVtbImdvX2RhdGEiXV1bWyJCUF9lbnJpY2giXV0KICAgICAgbWZfcGxvdHNfdXAgPC0gcGxvdF9lbnJpY2hyZXN1bHQobWZfc2lnKQogICAgICBtZl90cmVlX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjEyY2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9tZl9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gbWZfdHJlZV91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KG1mX3Bsb3RzX3VwW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIG1mX2Jhcl91cF9maWxlbmFtZSA8LSBnbHVlKCIxMmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfbWZfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gbWZfYmFyX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfdXBbWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChjY19zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGNjX3RyZWVfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMTJjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX2NjX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBjY190cmVlX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoY2NfcGxvdHNfdXBbWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgY2NfYmFyX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjEyY2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9jY19zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBjY19iYXJfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChjY19wbG90c191cFtbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGJwX3Bsb3RzX3VwIDwtIHBsb3RfZW5yaWNocmVzdWx0KGJwX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgYnBfdHJlZV91cF9maWxlbmFtZSA8LSBnbHVlKCIxMmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfYnBfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGJwX3RyZWVfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c191cFtbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX2Jhcl91cF9maWxlbmFtZSA8LSBnbHVlKCIxMmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfYnBfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gYnBfYmFyX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoYnBfcGxvdHNfdXBbWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgfQogICAgaWYgKGxlbmd0aChkb3ducCkgPiAwKSB7CiAgICAgIG1mX3NpZyA8LSBnZW5vdHlwZV9jcFtbbmFtZV1dW1tkb3ducF1dW1siZ29fZGF0YSJdXVtbIk1GX2VucmljaCJdXQogICAgICBjY19zaWcgPC0gZ2Vub3R5cGVfY3BbW25hbWVdXVtbZG93bnBdXVtbImdvX2RhdGEiXV1bWyJDQ19lbnJpY2giXV0KICAgICAgYnBfc2lnIDwtIGdlbm90eXBlX2NwW1tuYW1lXV1bW2Rvd25wXV1bWyJnb19kYXRhIl1dW1siQlBfZW5yaWNoIl1dCiAgICAgIG1mX3Bsb3RzX2Rvd24gPC0gcGxvdF9lbnJpY2hyZXN1bHQobWZfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBtZl90cmVlX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTJjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fbWZfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IG1mX3RyZWVfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KG1mX3Bsb3RzX2Rvd25bWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgbWZfYmFyX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTJjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fbWZfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gbWZfYmFyX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChtZl9wbG90c19kb3duW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgY2NfcGxvdHNfZG93biA8LSBwbG90X2VucmljaHJlc3VsdChjY19zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGNjX3RyZWVfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIxMmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9jY19zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gY2NfdHJlZV9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoY2NfcGxvdHNfZG93bltbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19iYXJfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIxMmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9jY19zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBjY19iYXJfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX2Rvd25bWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c19kb3duIDwtIHBsb3RfZW5yaWNocmVzdWx0KGJwX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgYnBfdHJlZV9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjEyY2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV9kb3duX2JwX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF90cmVlX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c19kb3duW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGJwX3Bsb3RzX2Rvd24gPC0gcGxvdF9lbnJpY2hyZXN1bHQoYnBfc2lnLCBjYXRlb2dvcmllcyA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX2Jhcl9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjEyY2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV9kb3duX2JwX3NpZ19kb3QucGRmIikKICAgICAgcHAoZmlsZSA9IGJwX2Jhcl9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoYnBfcGxvdHNfZG93bltbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICB9CiAgfQp9CmBgYAoKQSBmZXcgc3BlY2lmaWMgcGxvdHMgb2YgaW50ZXJlc3Q6IENvbGVuc28gYXNrZWQgdG8gbGFiZWwgYSBmZXcgZ2VuZXMKZm9yIHRoZSBrbm9ja291dC9oZXQgcDA4X3JldGluYXMsIHAwOF9zY24sIGFuZCBwMDhfZGxnbjogZWl0aGVyIHRoZQp0b3AtMTUgb3IgYWxsIHNpZ25pZmljYW50LiAgSSBhbSBwcmV0dHkgc3VyZSBpZiBJIHRlbGwgaXQgMTUgYW5kIHRoZXJlCmFyZSBub3QgdGhhdCBtYW55LCBpdCB3aWxsIGp1c3QgZG8gdGhlIHNpZ25pZmljYW50PyAgTGV0IHVzIGZpbmQgb3V0IQoKIyMjIyBrby9oZXQgZm9yIHAwOCByZXRpbmFzCgpgYGB7cn0KdGFibGVfbmFtZSA8LSAia2hfcDA4X3JldGluYSIKdGFibGVfaW5wdXQgPC0gZ2Vub3R5cGVfdGFibGVzW1t0YWJsZV9uYW1lXV0KdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KaW50ZXJlc3RpbmcgPC0gYygiT3BuNCIsICJHbTkwMDgiLCAiTHJyMSIsICJDbmJkMSIpCmtoX3AwOF9yZXRpbmFfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgZmlsbCA9ICJibGFjayIsCiAgY29sb3JfbG93ID0gY29sb3JzW1siaGV0X3JldGluYSJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImtvX3JldGluYSJdXSwKICBsYWJlbF9jb2x1bW4gPSB0eF9sYWJlbF9jb2x1bW4sIGxhYmVsID0gaW50ZXJlc3RpbmcsIGFscGhhID0gMS4wLAogIG91dGxpbmUgPSBvdXRsaW5lLCBzaXplID0gNCkKcHAoZmlsZSA9ICIxM2dlbm90eXBlX21hX3ZvbGNhbm9fdHgva2hfcDA4X3JldGluYV92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDA4X3JldGluYV92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpraF9wMDhfcmV0aW5hX3ZvbGNhbm9bWyJwbG90Il1dCmtoX3AwOF9yZXRpbmFfbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19yZXRpbmEiXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJoZXRfcmV0aW5hIl1dLAogIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgb3V0bGluZSA9IG91dGxpbmUsCiAgbGFiZWwgPSBpbnRlcmVzdGluZykKcHAoZmlsZSA9ICIxM2dlbm90eXBlX21hX3ZvbGNhbm9fdHgva2hfcDA4X3JldGluYV9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmtoX3AwOF9yZXRpbmFfbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmtoX3AwOF9yZXRpbmFfbWFbWyJwbG90Il1dCmBgYAoKIyMjIyBrby9oZXQgcDA4IFNDTgoKSSBhbSBnb2luZyB0byBtYWtlIGFuIGV4ZWN1dGl2ZSBkZWNpc2lvbiBmb3IgdGhpcyBwbG90LCAxNSBpcyB0b28gbWFueQphbmQgbWFrZXMgaXQgY3JhenkgY2x1dHRlcmVkLgoKIyMjIFJlcGVhdCB0aGlzIHdpdGggdHdvIHNldHMgb2YgZ2VuZXMKCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJraF9wMDhfc2NuIgp0YWJsZV9pbnB1dCA8LSBnZW5vdHlwZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQppbnRlcmVzdGluZ19nZW5lcyA8LSBjKCJGaWduIiwgIk5ybjEiLCAiRHB5c2wyIiwgIkFjdGIiLCAiRmdmOSIsICJPdHgyIiwgIlNlYzIzIiwKICAgICAgICAgICAgICAgICAgICAgICAiTmNhbTEiLCAiTWFwNCIsICJTZWMyMmIiLCAiTmxnbjMiLCAiTWFyY2tzIiwgIkNkNDciLAogICAgICAgICAgICAgICAgICAgICAgICJEcHlzbDMiLCAiTGluN2MiLCAiQ2FkbTEiLCAiU254MTIiLCAiUmhvYSIsICJJbnBwNWYiLAogICAgICAgICAgICAgICAgICAgICAgICJBdGcxMiIsICJTZXQiLCAiR3NrM2IiLCAiUGRjZDQiLCAiR2FicmEyIiwgIlRtY28xIiwgIkFuYXBjMTYiKQpraF9wMDhfc2NuX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIHNpemUgPSA0LCBhbHBoYSA9IDEuMCwKICBvdXRsaW5lID0gb3V0bGluZSwgY29sb3JfbG93ID0gY29sb3JzW1sia29fc2NuIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3NjbiJdXSkKcHAoZmlsZSA9ICIxM2dlbm90eXBlX21hX3ZvbGNhbm9fdHgva2hfcDA4X3Njbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDA4X3Njbl92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpraF9wMDhfc2NuX3ZvbGNhbm9bWyJwbG90Il1dCiMjIHdoeSBpbiB0aGUgY3JhcCBpcyBpdCBkb3VibGUtbGFiZWxsaW5nIT8KIyMgTXkgTUEgcGxvdHRlciBpc24ndCBhcyBzbWFydCBhcyB0aGUgdm9sY2FubyBwbG90dGVyLCB0aGUgZ2VuZXMgYXJlOgpraF9wMDhfc2NuX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fc2NuIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3NjbiJdXSwKICBvdXRsaW5lID0gb3V0bGluZSwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzKQpwcChmaWxlID0gIjEzZ2Vub3R5cGVfbWFfdm9sY2Fub190eC9raF9wMDhfc2NuX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDA4X3Njbl9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa2hfcDA4X3Njbl9tYVtbInBsb3QiXV0KYGBgCgojIyMjIFNhbWUgcGxvdCBidXQgYSBkaWZmZXJlbnQgc2V0IG9mIGxhYmVsZWQgZ2VuZXMKCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJraF9wMDhfc2NuIgp0YWJsZV9pbnB1dCA8LSBnZW5vdHlwZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQppbnRlcmVzdGluZ19nZW5lcyA8LSBjKAogICJBbmFwYzE2IiwgIkdhYnJhMiIsICJUbWNvMSIsICJTb2QyIiwgIkZnZjkiLCAiUGRjZDQiLCAiUmhvYSIsICJHc2szYiIsICJGb3hwMSIsCiAgIk5jYW0xIiwgIk1hcmNrcyIsICJGaWduIiwgIkRweXNsMyIsICJJbnBwNWYiLCAiQ2FkbTEiLCAiTWFwNCIsICJVZ2NnIiwgIkVsb3ZsNCIsCiAgIkVsYXZsMSIsICJDZmwyIiwgIlRubnQxIiwgIkduYjEiLCAiSW1wYWN0IiwgIk5ybjEiLCAiTmxnbjMiLCAiQWN0YiIsICJDZDQ3IiwKICAiU2VjMjJiIiwgIlNsYzE3YTciLCAiVmdsdXQxIiwgIkFjdGIiLCAiQjRnYWx0NSIsICJGb3hwMSIsICJPdHgyIiwgIkxpbjdjIiwKICAiU254MTIiLCAiQXRnMTIiLCAiU2V0IikKa2hfcDA4X3Njbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImtvX3NjbiJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9zY24iXV0sCiAgb3V0bGluZSA9IG91dGxpbmUsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBzaXplID0gNCwgYWxwaGEgPSAxLjApCnBwKGZpbGUgPSAiMTNnZW5vdHlwZV9tYV92b2xjYW5vX3R4L2toX3AwOF9zY25fdm9sY2Fub192Mi5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmtoX3AwOF9zY25fdm9sY2Fub1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa2hfcDA4X3Njbl92b2xjYW5vW1sicGxvdCJdXQojIyB3aHkgaW4gdGhlIGNyYXAgaXMgaXQgZG91YmxlLWxhYmVsbGluZyE/CiMjIE15IE1BIHBsb3R0ZXIgaXNuJ3QgYXMgc21hcnQgYXMgdGhlIHZvbGNhbm8gcGxvdHRlciwgdGhlIGdlbmVzIGFyZToKa2hfcDA4X3Njbl9tYSA8LSBwbG90X21hX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZXhwcl9jb2wgPSAiZGVzZXFfYmFzZW1lYW4iLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImtvX3NjbiJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9zY24iXV0sCiAgb3V0bGluZSA9IG91dGxpbmUsIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcykKcHAoZmlsZSA9ICIxM2dlbm90eXBlX21hX3ZvbGNhbm9fdHgva2hfcDA4X3Njbl9tYV92Mi5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmtoX3AwOF9zY25fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmtoX3AwOF9zY25fbWFbWyJwbG90Il1dCmBgYAoKIyMjIyBrby9oZXQgcDA4IGRMR04KCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJraF9wMDhfZGxnbiIKdGFibGVfaW5wdXQgPC0gZ2Vub3R5cGVfdGFibGVzW1t0YWJsZV9uYW1lXV0KdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0Ka2hfcDA4X2RsZ25fdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19kbGduIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X2RsZ24iXV0sCiAgb3V0bGluZSA9IG91dGxpbmUsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IDEwLCBzaXplID0gNCwgYWxwaGEgPSAxLjApCnBwKGZpbGUgPSAiMTNnZW5vdHlwZV9tYV92b2xjYW5vX3R4L2toX3AwOF9kbGduX3ZvbGNhbm8ucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpraF9wMDhfZGxnbl92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpraF9wMDhfZGxnbl92b2xjYW5vW1sicGxvdCJdXQojIyBNeSBNQSBwbG90dGVyIGlzbid0IGFzIHNtYXJ0IGFzIHRoZSB2b2xjYW5vIHBsb3R0ZXIsIHRoZSBnZW5lcyBhcmU6CmtoX3AwOF9kbGduX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fZGxnbiJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9kbGduIl1dLAogIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSAxMCwgb3V0bGluZSA9IG91dGxpbmUpCnBwKGZpbGUgPSAiMTNnZW5vdHlwZV9tYV92b2xjYW5vX3R4L2toX3AwOF9kbGduX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDA4X2RsZ25fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmtoX3AwOF9kbGduX21hW1sicGxvdCJdXQoKYGBgCgojIyMjIGtvL2hldCBmb3IgcDE1IHJldGluYXMKCldoZW4gbGFzdCBJIHJhbiB0aGlzIG1hbnVhbGx5LCBpdCBkaWQgbm90IGRvdWJsZS1sYWJlbCwgaG9wZWZ1bGx5IHRoYXQKcmVtYWlucyB0cnVlIGluIHRoZSBjb250YWluZXIuCgpgYGB7cn0KdGFibGVfbmFtZSA8LSAia2hfcDE1X3JldGluYSIKdGFibGVfaW5wdXQgPC0gZ2Vub3R5cGVfdGFibGVzW1t0YWJsZV9uYW1lXV0KdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KaW50ZXJlc3RpbmcgPC0gYygiT3BuNCIsICJHbTkwMDgiLCAiTHJyMSIsICJDbmJkMSIpCmtoX3AxNV9yZXRpbmFfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgZmlsbCA9ICJibGFjayIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fcmV0aW5hIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3JldGluYSJdXSwKICBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZywgYWxwaGEgPSAxLjAsCiAgb3V0bGluZSA9IG91dGxpbmUsIHNpemUgPSA0KQpwcChmaWxlID0gIjEzZ2Vub3R5cGVfbWFfdm9sY2Fub190eC9raF9wMTVfcmV0aW5hX3ZvbGNhbm8ucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpraF9wMTVfcmV0aW5hX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmtoX3AxNV9yZXRpbmFfdm9sY2Fub1tbInBsb3QiXV0KIyMgd2h5IGluIHRoZSBjcmFwIGlzIGl0IGRvdWJsZS1sYWJlbGxpbmchPwojIyBNeSBNQSBwbG90dGVyIGlzbid0IGFzIHNtYXJ0IGFzIHRoZSB2b2xjYW5vIHBsb3R0ZXIsIHRoZSBnZW5lcyBhcmU6CmtoX3AxNV9yZXRpbmFfbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19yZXRpbmEiXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJoZXRfcmV0aW5hIl1dLAogIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZywgb3V0bGluZSA9IG91dGxpbmUpCnBwKGZpbGUgPSAiMTNnZW5vdHlwZV9tYV92b2xjYW5vX3R4L2toX3AxNV9yZXRpbmFfbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpraF9wMTVfcmV0aW5hX21hW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpraF9wMTVfcmV0aW5hX21hW1sicGxvdCJdXQpgYGAKCiMjIyMga28vaGV0IHAxNSBTQ04KCiMjIyBSZXBlYXQgdGhpcyB3aXRoIHR3byBzZXRzIG9mIGdlbmVzCgpgYGB7cn0KdGFibGVfbmFtZSA8LSAia2hfcDE1X3NjbiIKdGFibGVfaW5wdXQgPC0gZ2Vub3R5cGVfdGFibGVzW1t0YWJsZV9uYW1lXV0KdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KaW50ZXJlc3RpbmdfZ2VuZXMgPC0gYygiRmlnbiIsICJOcm4xIiwgIkRweXNsMiIsICJBY3RiIiwgIkZnZjkiLCAiT3R4MiIsICJTZWMyMyIsCiAgICAgICAgICAgICAgICAgICAgICAgIk5jYW0xIiwgIk1hcDQiLCAiU2VjMjJiIiwgIk5sZ24zIiwgIk1hcmNrcyIsICJDZDQ3IiwKICAgICAgICAgICAgICAgICAgICAgICAiRHB5c2wzIiwgIkxpbjdjIiwgIkNhZG0xIiwgIlNueDEyIiwgIlJob2EiLCAiSW5wcDVmIiwKICAgICAgICAgICAgICAgICAgICAgICAiQXRnMTIiLCAiU2V0IiwgIkdzazNiIiwgIlBkY2Q0IiwgIkdhYnJhMiIsICJUbWNvMSIsICJBbmFwYzE2IikKa2hfcDE1X3Njbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBzaXplID0gNCwgYWxwaGEgPSAxLjAsCiAgb3V0bGluZSA9IG91dGxpbmUsIGNvbG9yX2xvdyA9IGNvbG9yc1tbImtvX3NjbiJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9zY24iXV0pCnBwKGZpbGUgPSAiMTNnZW5vdHlwZV9tYV92b2xjYW5vX3R4L2toX3AxNV9zY25fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmtoX3AxNV9zY25fdm9sY2Fub1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa2hfcDE1X3Njbl92b2xjYW5vW1sicGxvdCJdXQpraF9wMTVfc2NuX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fc2NuIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3NjbiJdXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIG91dGxpbmUgPSBvdXRsaW5lKQpwcChmaWxlID0gIjEzZ2Vub3R5cGVfbWFfdm9sY2Fub190eC9raF9wMTVfc2NuX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDE1X3Njbl9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa2hfcDE1X3Njbl9tYVtbInBsb3QiXV0KYGBgCgpSb3VuZCAyIHdpdGggYSBzZXBhcmF0ZSBnZW5lIHNldC4KCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJraF9wMTVfc2NuIgp0YWJsZV9pbnB1dCA8LSBnZW5vdHlwZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQppbnRlcmVzdGluZ19nZW5lcyA8LSBjKAogICJBbmFwYzE2IiwgIkdhYnJhMiIsICJUbWNvMSIsICJTb2QyIiwgIkZnZjkiLCAiUGRjZDQiLCAiUmhvYSIsICJHc2szYiIsICJGb3hwMSIsCiAgIk5jYW0xIiwgIk1hcmNrcyIsICJGaWduIiwgIkRweXNsMyIsICJJbnBwNWYiLCAiQ2FkbTEiLCAiTWFwNCIsICJVZ2NnIiwgIkVsb3ZsNCIsCiAgIkVsYXZsMSIsICJDZmwyIiwgIlRubnQxIiwgIkduYjEiLCAiSW1wYWN0IiwgIk5ybjEiLCAiTmxnbjMiLCAiQWN0YiIsICJDZDQ3IiwKICAiU2VjMjJiIiwgIlNsYzE3YTciLCAiVmdsdXQxIiwgIkFjdGIiLCAiQjRnYWx0NSIsICJGb3hwMSIsICJPdHgyIiwgIkxpbjdjIiwKICAiU254MTIiLCAiQXRnMTIiLCAiU2V0IikKa2hfcDE1X3Njbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImtvX3NjbiJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9zY24iXV0sCiAgb3V0bGluZSA9IG91dGxpbmUsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBzaXplID0gNCwgYWxwaGEgPSAxLjApCnBwKGZpbGUgPSAiMTNnZW5vdHlwZV9tYV92b2xjYW5vX3R4L2toX3AxNV9zY25fdm9sY2Fub192Mi5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmtoX3AxNV9zY25fdm9sY2Fub1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa2hfcDE1X3Njbl92b2xjYW5vW1sicGxvdCJdXQpraF9wMTVfc2NuX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fc2NuIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3NjbiJdXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIG91dGxpbmUgPSBvdXRsaW5lKQpwcChmaWxlID0gIjEzZ2Vub3R5cGVfbWFfdm9sY2Fub190eC9raF9wMTVfc2NuX21hX3YyLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDE1X3Njbl9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa2hfcDE1X3Njbl9tYVtbInBsb3QiXV0KYGBgCgojIyMjIGtvL2hldCBwMTUgZExHTgoKYGBge3J9CnRhYmxlX25hbWUgPC0gImtoX3AxNV9kbGduIgp0YWJsZV9pbnB1dCA8LSBnZW5vdHlwZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQpraF9wMTVfZGxnbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImtvX2RsZ24iXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJoZXRfZGxnbiJdXSwKICBvdXRsaW5lID0gb3V0bGluZSwgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gMTAsIHNpemUgPSA0LCBhbHBoYSA9IDEuMCkKcHAoZmlsZSA9ICIxM2dlbm90eXBlX21hX3ZvbGNhbm9fdHgva2hfcDE1X2RsZ25fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmtoX3AxNV9kbGduX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmtoX3AxNV9kbGduX3ZvbGNhbm9bWyJwbG90Il1dCiMjIE15IE1BIHBsb3R0ZXIgaXNuJ3QgYXMgc21hcnQgYXMgdGhlIHZvbGNhbm8gcGxvdHRlciwgdGhlIGdlbmVzIGFyZToKa2hfcDE1X2RsZ25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19kbGduIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X2RsZ24iXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IDEwLCBvdXRsaW5lID0gb3V0bGluZSkKcHAoZmlsZSA9ICIxM2dlbm90eXBlX21hX3ZvbGNhbm9fdHgva2hfcDE1X2RsZ25fbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpraF9wMTVfZGxnbl9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKa2hfcDE1X2RsZ25fbWFbWyJwbG90Il1dCmBgYAoKQSBxdWVyeSBmcm9tIFJhc2htaToKCiJJIHdhcyBkaXNjdXNzaW5nIHdpdGggRHIuIFNwZWVyIGFib3V0IHRoZSBkTEdOIGRhdGEgYW5kIHdlIGZvdW5kIG1vc3RseQpyZXRpbmFsIGdlbmVzIGluIGRMR04gSGV0L0tPIG9yIHRpbWUgcG9pbnQgY29tcGFyaXNvbi4gUGxlYXNlIGNoZWNrIGlmCnRob3NlIGFyZSBub3QgcmV0aW5hIHNhbXBsZXMuIgoKSSBjaGVja2VkIHRoZSBzYW1wbGVzIGV0YyBhbmQgZXZlcnl0aGluZyBsb29rcyBvayB0byBtZTsgcGVyaGFwcyBJIGNhbgp1c2UgdGhlIHJlc3VsdHMgdG8gbG9vayBhdCB0aGlzIHF1ZXN0aW9uIGluIGFub3RoZXIgd2F5OgoKSSB3aWxsIHRoZXJlZm9yZSBsb2FkIHRoZSBwMDhfaGV0X2RsZ24vcDA4X2tvX2RsZ24gdGFibGUgYW5kIGNvbXBhcmUKaXQgdG8gdGhlIHAwOF9oZXRfcmV0aW5hL3AwOF9rb19yZXRpbmEgdGFibGUgZGlyZWN0bHkuICBJIHRoaW5rIHRoYXQKaWYgdGhlc2UgdHVybiBvdXQgdG8gYmUgaWRlbnRpY2FsLCB0aGVuIHRoZSBoeXBvdGhlc2lzIHN1Z2dlc3RlZCBieQp0aGlzIHF1ZXJ5IGlzIGNvcnJlY3QuCgpOb3RlLCBpbiBvcmRlciB0byBkbyB0aGlzLCBJIG11c3QgdXNlIHRoZSBmdWxsIHRhYmxlcywgbm90IHRoZQpwb3N0LWluY2x1c2lvbiB0YWJsZXMgYmVjYXVzZSBJIGNhbm5vdCBndWFyYW50ZWUgdGhhdCB0aGV5IHdpbGwgaGF2ZQppZGVudGljYWwgZ2VuZSBJRHMuCgpgYGB7cn0KcmV0aW5hX3RhYmxlIDwtIGdlbm90eXBlX3RhYmxlc19mdWxsW1siZGF0YSJdXVtbImtoX3AwOF9yZXRpbmEiXV0KZGxnbl90YWJsZSA8LSBnZW5vdHlwZV90YWJsZXNfZnVsbFtbImRhdGEiXV1bWyJraF9wMDhfZGxnbiJdXQpyZXRpbmFfc3Vic2V0IDwtIHJldGluYV90YWJsZVssIGMoImVuc2VtYmxfZ2VuZV9pZCIsICJkZXNlcV9sb2dmYyIpXQpjb2xuYW1lcyhyZXRpbmFfc3Vic2V0KSA8LSBjKCJJRCIsICJyZXRpbmFfbG9nZmMiKQpkbGduX3N1YnNldCA8LSBkbGduX3RhYmxlWywgYygiZW5zZW1ibF9nZW5lX2lkIiwgImRlc2VxX2xvZ2ZjIildCmNvbG5hbWVzKGRsZ25fc3Vic2V0KSA8LSBjKCJJRCIsICJkbGduX2xvZ2ZjIikKbWVyZ2VkIDwtIG1lcmdlKHJldGluYV9zdWJzZXQsIGRsZ25fc3Vic2V0LCBieSA9ICJJRCIpCnJvd25hbWVzKG1lcmdlZCkgPC0gbWFrZS5uYW1lcyhtZXJnZWRbWyJJRCJdXSwgdW5pcXVlID0gVFJVRSkKbWVyZ2VkW1siSUQiXV0gPC0gTlVMTApwbG90dGVkIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIobWVyZ2VkKQpwcChmaWxlID0gImltYWdlc190eC9raF9wMDhfcmV0aW5hX3ZzX2RsZ25fZGVzZXFfbG9nZmNfdmFsdWVzLnBuZyIpCnBsb3R0ZWRbWyJzY2F0dGVyIl1dCmRldi5vZmYoKQpwbG90dGVkW1sic2NhdHRlciJdXQpgYGAKClJhc2htaSBhc2tlZCBpZiB3ZSBjb3VsZCBhbHNvIGRvIHRoZSBwMTUgZm9yIHRoaXMgY29tcGFyaXNvbjoKCmBgYHtyfQpyZXRpbmFfdGFibGUgPC0gZ2Vub3R5cGVfdGFibGVzX2Z1bGxbWyJkYXRhIl1dW1sia2hfcDE1X3JldGluYSJdXQpkbGduX3RhYmxlIDwtIGdlbm90eXBlX3RhYmxlc19mdWxsW1siZGF0YSJdXVtbImtoX3AxNV9kbGduIl1dCnJldGluYV9zdWJzZXQgPC0gcmV0aW5hX3RhYmxlWywgYygiZW5zZW1ibF9nZW5lX2lkIiwgImRlc2VxX2xvZ2ZjIildCmNvbG5hbWVzKHJldGluYV9zdWJzZXQpIDwtIGMoIklEIiwgInJldGluYV9sb2dmYyIpCmRsZ25fc3Vic2V0IDwtIGRsZ25fdGFibGVbLCBjKCJlbnNlbWJsX2dlbmVfaWQiLCAiZGVzZXFfbG9nZmMiKV0KY29sbmFtZXMoZGxnbl9zdWJzZXQpIDwtIGMoIklEIiwgImRsZ25fbG9nZmMiKQptZXJnZWQgPC0gbWVyZ2UocmV0aW5hX3N1YnNldCwgZGxnbl9zdWJzZXQsIGJ5ID0gIklEIikKcm93bmFtZXMobWVyZ2VkKSA8LSBtYWtlLm5hbWVzKG1lcmdlZFtbIklEIl1dLCB1bmlxdWUgPSBUUlVFKQptZXJnZWRbWyJJRCJdXSA8LSBOVUxMCnBsb3R0ZWQgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihtZXJnZWQpCnBwKGZpbGUgPSAiaW1hZ2VzX3R4L2toX3AxNV9yZXRpbmFfdnNfZGxnbl9kZXNlcV9sb2dmY192YWx1ZXMucG5nIikKcGxvdHRlZFtbInNjYXR0ZXIiXV0KZGV2Lm9mZigpCnBsb3R0ZWRbWyJzY2F0dGVyIl1dCmBgYAoKIyMjIyBSZXBlYXQgd2l0aCB0aGUgc3RyaWN0IGZpbHRlcgoKYGBge3J9Cmdlbm90eXBlX3N0cmljdF90YWJsZXMgPC0gbGlzdCgpCmdlbm90eXBlX3N0cmljdF9zaWcgPC0gbGlzdCgpCmdlbm90eXBlX3N0cmljdF9ncCA8LSBsaXN0KCkKZ2Vub3R5cGVfc3RyaWN0X2NwIDwtIGxpc3QoKQpnZW5vdHlwZV9zdHJpY3RfZW4gPC0gbGlzdCgpCmZvciAoayBpbiBzZXFfYWxvbmcoZ2Vub3R5cGVfa2VlcGVycykpIHsKICBuYW1lIDwtIG5hbWVzKGdlbm90eXBlX2tlZXBlcnMpW2tdCiAgbWVzc2FnZSgiRXhhbWluaW5nICIsIG5hbWUpCiAga2VlcGVyIDwtIGdlbm90eXBlX2tlZXBlcnNbbmFtZV0KICBpbmNsdWRlX25hbWUgPC0gcGFzdGUwKCJpbmNfIiwgbmFtZSkKICBpbmNsdWRlX2RmX25hbWUgPC0gcGFzdGUwKCJkZl8iLCBuYW1lKQogIGluY2x1ZGVfc3RyaWN0X2RmIDwtIGdlbm90eXBlX2luY2x1c2lvbnNfc3RyaWN0W1tpbmNsdWRlX2RmX25hbWVdXQogIGluY2x1ZGVzX3N0cmljdCA8LSBnZW5vdHlwZV9pbmNsdXNpb25zX3N0cmljdFtbaW5jbHVkZV9uYW1lXV0KICBzdW1tYXJ5KHJvd25hbWVzKGdlbm90eXBlX3NpZ19mdWxsW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgJWluJSBpbmNsdWRlc19zdHJpY3QpCiAgaW5jbHVkZV9maWxlbmFtZSA8LSBnbHVlKCIxNGdlbm90eXBlX3N0cmljdF9jb250cmFzdHNfZXhjZWxfdHgve25hbWV9X2luY2x1ZGluZ193dF97bGZjX2N1dG9mZn1fZGVjcmVhc2VkX3RhYmxlLXZ7dmVyfS54bHN4IikKICBpbmNsdWRlX3NpZ19maWxlbmFtZSA8LSBnbHVlKCIxNGdlbm90eXBlX3N0cmljdF9jb250cmFzdHNfZXhjZWxfdHgve25hbWV9X2luY2x1ZGluZ193dF97bGZjX2N1dG9mZn1fZGVjcmVhc2VkX3NpZy12e3Zlcn0ueGxzeCIpCiAgZ2Vub3R5cGVfc3RyaWN0X3RhYmxlc1tbbmFtZV1dIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgZ2Vub3R5cGVfZGUsIGV4dHJhX2Fubm90ID0gaW5jbHVkZV9zdHJpY3RfZGYsCiAgICBrZWVwZXJzID0ga2VlcGVyLCBsYWJlbF9jb2x1bW4gPSB0eF9sYWJlbF9jb2x1bW4sCiAgICBleGNlbCA9IGluY2x1ZGVfZmlsZW5hbWUsIHdhbnRlZF9nZW5lcyA9IGluY2x1ZGVzX3N0cmljdCkKICBwcmludChnZW5vdHlwZV9zdHJpY3RfdGFibGVzW1tuYW1lXV0pCiAgZ2Vub3R5cGVfc3RyaWN0X3NpZ1tbbmFtZV1dIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICBnZW5vdHlwZV9zdHJpY3RfdGFibGVzW1tuYW1lXV0sIGFjY29yZGluZ190byA9ICJkZXNlcSIsCiAgICBleGNlbCA9IGluY2x1ZGVfc2lnX2ZpbGVuYW1lKQogIHByaW50KGdlbm90eXBlX3N0cmljdF9zaWdbW25hbWVdXSkKICBudW1fcm93cyA8LSBucm93KGdlbm90eXBlX3N0cmljdF9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pICsKICAgIG5yb3coZ2Vub3R5cGVfc3RyaWN0X3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dKQogIG1lc3NhZ2UoIlRoZXJlIGFyZSAiLCBudW1fcm93cywgIiBzaWduaWZpY2FudCB1cCBhbmQgZG93biBnZW5lcy4iKQogIGlmIChudW1fcm93cyA+PSAxMCkgewogICAgbWVzc2FnZSgiUGVyZm9ybWluZyBncHJvZmlsZXIvY2x1c3RlclByb2ZpbGVyLiIpCiAgICBnZW5vdHlwZV9zdHJpY3RfZ3BbW25hbWVdXSA8LSBhbGxfZ3Byb2ZpbGVyKGdlbm90eXBlX3N0cmljdF9zaWdbW25hbWVdXSwgc3BlY2llcyA9ICJtbXVzY3VsdXMiKQogICAgZ2Vub3R5cGVfc3RyaWN0X2NwW1tuYW1lXV0gPC0gYWxsX2Nwcm9maWxlcigKICAgICAgZ2Vub3R5cGVfc3RyaWN0X3NpZ1tbbmFtZV1dLCBnZW5vdHlwZV9zdHJpY3RfdGFibGVzW1tuYW1lXV0sCiAgICAgIG9yZ2RiID0gIm9yZy5NbS5lZy5kYiIsIGdvX2xldmVsID0gZ29fbGV2ZWwsCiAgICAgIG9yZ2RiX2Zyb20gPSBvcmdkYl9mcm9tLCBtYXhfZ3JvdXBzaXplID0gbWF4X2dyb3Vwc2l6ZSwgb3JnYW5pc20gPSAibW91c2UiKQogICAgI2lmICghaXMubnVsbChnZXQwKCJtMl9nc2MiKSkpIHsKICAgICMgIGdlbm90eXBlX3N0cmljdF9lbltbbmFtZV1dIDwtIGFsbF9lbnJpY2hlcihnZW5vdHlwZV9zdHJpY3Rfc2lnW1tuYW1lXV0sIGdzYyA9IG0yX2dzYywKICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2RiID0gIm9yZy5NbS5lZy5kYiIsIGZyb20gPSAiRU5TRU1CTCIsIHRvID0gIlNZTUJPTCIpCiAgICAjfQogICAgZ3Bfd3JpdHRlbiA8LSB3cml0ZV9hbGxfZ3AoZ2Vub3R5cGVfc3RyaWN0X2dwW1tuYW1lXV0sIHByZWZpeCA9ICIxNSIsIHN1ZmZpeCA9ICJzdHJpY3QiKQogICAgY3Bfd3JpdHRlbiA8LSB3cml0ZV9hbGxfY3AoZ2Vub3R5cGVfc3RyaWN0X2NwW1tuYW1lXV0sIHByZWZpeCA9ICIxNSIsIHN1ZmZpeCA9ICJzdHJpY3QiKQogICAgI2VuX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2VuKGdlbm90eXBlX3N0cmljdF9lbltbbmFtZV1dKQogIH0gZWxzZSB7CiAgICB3YXJuaW5nKCJUaGVyZSBhcmUgbGVzcyB0aGFuIDEwIGdlbmVzIHVwIGFuZCBkb3duIGluIHRoZSAiLCBuYW1lLCAiIGNvbXBhcmlzb24uIikKICAgIG1lc3NhZ2UoIlRoZXJlIGFyZSBsZXNzIHRoYW4gMTAgZ2VuZXMgdXAgYW5kIGRvd24gaW4gdGhlICIsIG5hbWUsICIgY29tcGFyaXNvbi4iKQogIH0KfQpgYGAKCiMgR2Vub3R5cGUgU3RyaWN0IHBsb3RzCgpgYGB7cn0KZm9yIChrIGluIHNlcV9hbG9uZyhnZW5vdHlwZV9rZWVwZXJzKSkgewogIG5hbWUgPC0gbmFtZXMoZ2Vub3R5cGVfa2VlcGVycylba10KICBtZXNzYWdlKCJFeGFtaW5pbmcgIiwgbmFtZSkKICBrZWVwZXIgPC0gZ2Vub3R5cGVfa2VlcGVyc1tuYW1lXQogIGluY2x1ZGVfbmFtZSA8LSBwYXN0ZTAoImluY18iLCBuYW1lKQogIGluY2x1ZGVfZGZfbmFtZSA8LSBwYXN0ZTAoImRmXyIsIG5hbWUpCiAgaW5jbHVkZV9zdHJpY3RfZGYgPC0gZ2Vub3R5cGVfaW5jbHVzaW9uc19zdHJpY3RbW2luY2x1ZGVfZGZfbmFtZV1dCiAgaW5jbHVkZXNfc3RyaWN0IDwtIGdlbm90eXBlX2luY2x1c2lvbnNfc3RyaWN0W1tpbmNsdWRlX25hbWVdXQogIHN1bW1hcnkocm93bmFtZXMoZ2Vub3R5cGVfc2lnX2Z1bGxbWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSAlaW4lIGluY2x1ZGVzX3N0cmljdCkKICBudW1fcm93cyA8LSBucm93KGdlbm90eXBlX3N0cmljdF9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pICsKICAgIG5yb3coZ2Vub3R5cGVfc3RyaWN0X3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dKQogIG5yb3coZ2Vub3R5cGVfc3RyaWN0X3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkKICBucm93KGdlbm90eXBlX3N0cmljdF9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSkKICBtZXNzYWdlKCJUaGVyZSBhcmUgIiwgbnVtX3Jvd3MsICIgc2lnbmlmaWNhbnQgdXAgYW5kIGRvd24gZ2VuZXMuIikKICAjIyAjMSBpcyB1cCBhbmQgIzIgaXMgZG93biwgYXZvaWRpbmcgdHlwZW9zIGhlcmUuCiAgbnVtX29iamVjdHMgPC0gbGVuZ3RoKGdlbm90eXBlX3N0cmljdF9jcFtbbmFtZV1dKQogIGlmIChudW1fb2JqZWN0cyA9PSAwKSB7CiAgICB3YXJuaW5nKCJTb21ldGhpbmcgd2VudCB3cm9uZyBpbiBhbGxfY3Byb2ZpbGVyLiIpCiAgfSBlbHNlIHsKICAgIHVwcCA8LSB3aGljaChncmVwbCh4ID0gbmFtZXMoZ2Vub3R5cGVfc3RyaWN0X2NwW1tuYW1lXV0pLCBwYXR0ZXJuID0gIl91cCQiKSkKICAgIGRvd25wIDwtIHdoaWNoKGdyZXBsKHggPSBuYW1lcyhnZW5vdHlwZV9zdHJpY3RfY3BbW25hbWVdXSksIHBhdHRlcm4gPSAiX2Rvd24kIikpCiAgICBpZiAobGVuZ3RoKHVwcCkgPiAwKSB7CiAgICAgIG1mX3NpZyA8LSBnZW5vdHlwZV9zdHJpY3RfY3BbW25hbWVdXVtbdXBwXV1bWyJnb19kYXRhIl1dW1siTUZfZW5yaWNoIl1dCiAgICAgIGNjX3NpZyA8LSBnZW5vdHlwZV9zdHJpY3RfY3BbW25hbWVdXVtbdXBwXV1bWyJnb19kYXRhIl1dW1siQ0NfZW5yaWNoIl1dCiAgICAgIGJwX3NpZyA8LSBnZW5vdHlwZV9zdHJpY3RfY3BbW25hbWVdXVtbdXBwXV1bWyJnb19kYXRhIl1dW1siQlBfZW5yaWNoIl1dCiAgICAgIG1mX3Bsb3RzX3VwIDwtIHBsb3RfZW5yaWNocmVzdWx0KG1mX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgbWZfdHJlZV91cF9maWxlbmFtZSA8LSBnbHVlKCIxNmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfbWZfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IG1mX3RyZWVfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChtZl9wbG90c191cFtbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBtZl9iYXJfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMTZjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX21mX3NpZ19kb3QucGRmIikKICAgICAgcHAoZmlsZSA9IG1mX2Jhcl91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KG1mX3Bsb3RzX3VwW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgY2NfcGxvdHNfdXAgPC0gcGxvdF9lbnJpY2hyZXN1bHQoY2Nfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBjY190cmVlX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjE2Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9jY19zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gY2NfdHJlZV91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX3VwW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGNjX2Jhcl91cF9maWxlbmFtZSA8LSBnbHVlKCIxNmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfY2Nfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gY2NfYmFyX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoY2NfcGxvdHNfdXBbWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX3RyZWVfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMTZjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX2JwX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF90cmVlX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoYnBfcGxvdHNfdXBbWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgYnBfcGxvdHNfdXAgPC0gcGxvdF9lbnJpY2hyZXN1bHQoYnBfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBicF9iYXJfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMTZjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX2JwX3NpZ19kb3QucGRmIikKICAgICAgcHAoZmlsZSA9IGJwX2Jhcl91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGJwX3Bsb3RzX3VwW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgIH0KICAgIGlmIChsZW5ndGgoZG93bnApID4gMCkgewogICAgICBtZl9zaWcgPC0gZ2Vub3R5cGVfc3RyaWN0X2NwW1tuYW1lXV1bW2Rvd25wXV1bWyJnb19kYXRhIl1dW1siTUZfZW5yaWNoIl1dCiAgICAgIGNjX3NpZyA8LSBnZW5vdHlwZV9zdHJpY3RfY3BbW25hbWVdXVtbZG93bnBdXVtbImdvX2RhdGEiXV1bWyJDQ19lbnJpY2giXV0KICAgICAgYnBfc2lnIDwtIGdlbm90eXBlX3N0cmljdF9jcFtbbmFtZV1dW1tkb3ducF1dW1siZ29fZGF0YSJdXVtbIkJQX2VucmljaCJdXQogICAgICBtZl9wbG90c19kb3duIDwtIHBsb3RfZW5yaWNocmVzdWx0KG1mX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgbWZfdHJlZV9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjE2Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV9kb3duX21mX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl90cmVlX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChtZl9wbG90c19kb3duW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIG1mX2Jhcl9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjE2Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV9kb3duX21mX3NpZ19kb3QucGRmIikKICAgICAgcHAoZmlsZSA9IG1mX2Jhcl9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfZG93bltbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGNjX3Bsb3RzX2Rvd24gPC0gcGxvdF9lbnJpY2hyZXN1bHQoY2Nfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBjY190cmVlX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTZjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fY2Nfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGNjX3RyZWVfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX2Rvd25bWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgY2NfYmFyX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTZjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fY2Nfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gY2NfYmFyX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChjY19wbG90c19kb3duW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgYnBfcGxvdHNfZG93biA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX3RyZWVfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIxNmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9icF9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gYnBfdHJlZV9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoYnBfcGxvdHNfZG93bltbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c19kb3duIDwtIHBsb3RfZW5yaWNocmVzdWx0KGJwX3NpZywgY2F0ZW9nb3JpZXMgPSBnb19jYXRlZ29yaWVzKQogICAgICBicF9iYXJfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIxNmNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9icF9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF9iYXJfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGJwX3Bsb3RzX2Rvd25bWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgfQogIH0KfQpgYGAKCkdpdmVuIHRoaXMgc3RyaWN0ZXIgZmlsdGVyLCBJIHRoaW5rIG5vIGdlbmVzIHBhc3MgaW4gdGhlIGdlbm9tZQpjb21wYXJpc29ucy4KCiMjIyBMb2NhdGlvbiBjb250cmFzdHMgd2l0aCBnZW5lcyByZW1vdmVkL2tlcHQKCldlIHdpbGwgbm93IHJlcGVhdCB0aGUgYWJvdmUgdGFza3Mgc2Vla2luZyBsb2NhdGlvbiBkaWZmZXJlbmNlcwppbnN0ZWFkIG9mIGdlbm90eXBlOyBlc3NlbnRpYWxseSBJIGNvcHkvcGFzdGVkIHRoZSBhYm92ZSB3aXRoCnMvZ2Vub3R5cGUvbG9jYXRpb24vZy4KCmBgYHtyfQpsb2NhdGlvbl90YWJsZXNfZnVsbCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBsb2NhdGlvbl9kZSwga2VlcGVycyA9IGxvY2F0aW9uX2tlZXBlcnMsIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoIjE3ZnVsbF9sb2NhdGlvbl9jb250cmFzdHNfdHgvbG9jYXRpb25fZnVsbF90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKbG9jYXRpb25fdGFibGVzX2Z1bGwKbG9jYXRpb25fc2lnX2Z1bGwgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBsb2NhdGlvbl90YWJsZXNfZnVsbCwgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICBleGNlbCA9IGdsdWUoIjE3ZnVsbF9sb2NhdGlvbl9jb250cmFzdHNfdHgvbG9jYXRpb25fZnVsbF9zaWctdnt2ZXJ9Lnhsc3giKSkKbG9jYXRpb25fc2lnX2Z1bGwKbG9jYXRpb25fZnVsbF91cHNldCA8LSB1cHNldHJfc2lnKGxvY2F0aW9uX3NpZ19mdWxsKQpsb2NhdGlvbl9mdWxsX2ludGVyc2VjdHMgPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKAogIGxvY2F0aW9uX2Z1bGxfdXBzZXQsCiAgZXhjZWwgPSAiZXhjZWwvMTdmdWxsX2xvY2F0aW9uX2NvbnRyYXN0c190eC9sb2NhdGlvbl9mdWxsX2dlbmVfZ3JvdXBzLnhsc3giKQpgYGAKCmBgYHtyfQpsb2NhdGlvbl90YWJsZXMgPC0gbGlzdCgpCmxvY2F0aW9uX3NpZyA8LSBsaXN0KCkKbG9jYXRpb25fZ3AgPC0gbGlzdCgpCmxvY2F0aW9uX2NwIDwtIGxpc3QoKQpmb3IgKGsgaW4gc2VxX2Fsb25nKGxvY2F0aW9uX2tlZXBlcnMpKSB7CiAgbmFtZSA8LSBuYW1lcyhsb2NhdGlvbl9rZWVwZXJzKVtrXQogIG1lc3NhZ2UoIkV4YW1pbmluZyAiLCBuYW1lKQogIGtlZXBlciA8LSBsb2NhdGlvbl9rZWVwZXJzW25hbWVdCiAgaW5jbHVkZXMgPC0gbG9jYXRpb25faW5jbHVzaW9uc1tbbmFtZV1dCiAgaW5jbHVkZV9uYW1lIDwtIHBhc3RlMCgiaW5jXyIsIG5hbWUpCiAgaW5jbHVkZV9kZl9uYW1lIDwtIHBhc3RlMCgiZGZfIiwgbmFtZSkKICBpbmNsdWRlX2RmIDwtIGxvY2F0aW9uX2luY2x1c2lvbnNbW2luY2x1ZGVfZGZfbmFtZV1dCiAgaW5jbHVkZXMgPC0gbG9jYXRpb25faW5jbHVzaW9uc1tbaW5jbHVkZV9uYW1lXV0KICBzdW1tYXJ5KHJvd25hbWVzKGxvY2F0aW9uX3NpZ19mdWxsW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgJWluJSBpbmNsdWRlcykKICBpbmNsdWRlX2ZpbGVuYW1lIDwtIGdsdWUoIjE4bG9jYXRpb25fY29udHJhc3RzX3R4L3tuYW1lfV9pbmNsdWRpbmdfd3Rfe2xmY19jdXRvZmZ9X2RlY3JlYXNlZF90YWJsZS12e3Zlcn0ueGxzeCIpCiAgaW5jbHVkZV9zaWdfZmlsZW5hbWUgPC0gZ2x1ZSgiMThsb2NhdGlvbl9jb250cmFzdHNfdHgve25hbWV9X2luY2x1ZGluZ193dF97bGZjX2N1dG9mZn1fZGVjcmVhc2VkX3NpZy12e3Zlcn0ueGxzeCIpCiAgbG9jYXRpb25fdGFibGVzW1tuYW1lXV0gPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICBsb2NhdGlvbl9kZSwgZXh0cmFfYW5ub3QgPSBpbmNsdWRlX2RmLAogICAga2VlcGVycyA9IGtlZXBlciwgbGFiZWxfY29sdW1uID0gdHhfbGFiZWxfY29sdW1uLAogICAgZXhjZWwgPSBpbmNsdWRlX2ZpbGVuYW1lLCB3YW50ZWRfZ2VuZXMgPSBpbmNsdWRlcykKICBwcmludChsb2NhdGlvbl90YWJsZXNbW25hbWVdXSkKICBsb2NhdGlvbl9zaWdbW25hbWVdXSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgbG9jYXRpb25fdGFibGVzW1tuYW1lXV0sIGFjY29yZGluZ190byA9ICJkZXNlcSIsCiAgICBleGNlbCA9IGluY2x1ZGVfc2lnX2ZpbGVuYW1lKQogIHByaW50KGxvY2F0aW9uX3NpZ1tbbmFtZV1dKQogIG51bV9yb3dzIDwtIG5yb3cobG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSArCiAgICBucm93KGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dKQogIG1lc3NhZ2UoIlRoZXJlIGFyZSAiLCBudW1fcm93cywgIiBzaWduaWZpY2FudCB1cCBhbmQgZG93biBnZW5lcy4iKQogIGlmIChudW1fcm93cyA+IDEwKSB7CiAgICBsb2NhdGlvbl9ncFtbbmFtZV1dIDwtIGFsbF9ncHJvZmlsZXIobG9jYXRpb25fc2lnW1tuYW1lXV0sIHNwZWNpZXMgPSAibW11c2N1bHVzIikKICAgIGxvY2F0aW9uX2NwW1tuYW1lXV0gPC0gYWxsX2Nwcm9maWxlcigKICAgICAgbG9jYXRpb25fc2lnW1tuYW1lXV0sIGxvY2F0aW9uX3RhYmxlc1tbbmFtZV1dLAogICAgICBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBnb19sZXZlbCA9IGdvX2xldmVsLCBvcmdkYl9mcm9tID0gb3JnZGJfZnJvbSwKICAgICAgbWF4X2dyb3Vwc2l6ZSA9IG1heF9ncm91cHNpemUsIG9yZ2FuaXNtID0gIm1vdXNlIikKICAgIGNwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2NwKGxvY2F0aW9uX2NwW1tuYW1lXV0sIHByZWZpeCA9ICIxOSIpCiAgICBncF93cml0dGVuIDwtIHdyaXRlX2FsbF9ncChsb2NhdGlvbl9ncFtbbmFtZV1dLCBwcmVmaXggPSAiMTkiKQogIH0KfQpgYGAKClByaW50IG91dCBhbGwgdGhlIHBsb3RzIGluIGEgc2VwYXJhdGUgYmxvY2suCgpgYGB7cn0KZm9yIChrIGluIHNlcV9hbG9uZyhsb2NhdGlvbl9rZWVwZXJzKSkgewogIG5hbWUgPC0gbmFtZXMobG9jYXRpb25fa2VlcGVycylba10KICBtZXNzYWdlKCJFeGFtaW5pbmcgIiwgbmFtZSkKICBrZWVwZXIgPC0gbG9jYXRpb25fa2VlcGVyc1tuYW1lXQogIGluY2x1ZGVzIDwtIGxvY2F0aW9uX2luY2x1c2lvbnNbW25hbWVdXQogIGluY2x1ZGVfbmFtZSA8LSBwYXN0ZTAoImluY18iLCBuYW1lKQogIGluY2x1ZGVfZGZfbmFtZSA8LSBwYXN0ZTAoImRmXyIsIG5hbWUpCiAgaW5jbHVkZV9kZiA8LSBsb2NhdGlvbl9pbmNsdXNpb25zW1tpbmNsdWRlX2RmX25hbWVdXQogIGluY2x1ZGVzIDwtIGxvY2F0aW9uX2luY2x1c2lvbnNbW2luY2x1ZGVfbmFtZV1dCiAgc3VtbWFyeShyb3duYW1lcyhsb2NhdGlvbl9zaWdfZnVsbFtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pICVpbiUgaW5jbHVkZXMpCiAgbnVtX3Jvd3MgPC0gbnJvdyhsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pICsKICAgIG5yb3cobG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0pCiAgbnJvdyhsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pCiAgbnJvdyhsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSkKICBtZXNzYWdlKCJUaGVyZSBhcmUgIiwgbnVtX3Jvd3MsICIgc2lnbmlmaWNhbnQgdXAgYW5kIGRvd24gZ2VuZXMuIikKICBudW1fb2JqZWN0cyA8LSBsZW5ndGgobG9jYXRpb25fY3BbW25hbWVdXSkKICBpZiAobnVtX29iamVjdHMgPT0gMCkgewogICAgd2FybmluZygiU29tZXRoaW5nIHdlbnQgd3JvbmcgaW4gYWxsX2Nwcm9maWxlci4iKQogIH0gZWxzZSB7CiAgICB1cHAgPC0gd2hpY2goZ3JlcGwoeCA9IG5hbWVzKGxvY2F0aW9uX2NwW1tuYW1lXV0pLCBwYXR0ZXJuID0gIl91cCQiKSkKICAgIGRvd25wIDwtIHdoaWNoKGdyZXBsKHggPSBuYW1lcyhsb2NhdGlvbl9jcFtbbmFtZV1dKSwgcGF0dGVybiA9ICJfZG93biQiKSkKICAgIGlmIChsZW5ndGgodXBwKSA+IDApIHsKICAgICAgbWZfc2lnIDwtIGxvY2F0aW9uX2NwW1tuYW1lXV1bW3VwcF1dW1siZ29fZGF0YSJdXVtbIk1GX2VucmljaCJdXQogICAgICBjY19zaWcgPC0gbG9jYXRpb25fY3BbW25hbWVdXVtbdXBwXV1bWyJnb19kYXRhIl1dW1siQ0NfZW5yaWNoIl1dCiAgICAgIGJwX3NpZyA8LSBsb2NhdGlvbl9jcFtbbmFtZV1dW1t1cHBdXVtbImdvX2RhdGEiXV1bWyJCUF9lbnJpY2giXV0KICAgICAgbWZfcGxvdHNfdXAgPC0gcGxvdF9lbnJpY2hyZXN1bHQobWZfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBtZl90cmVlX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjE5Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9tZl9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gbWZfdHJlZV91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KG1mX3Bsb3RzX3VwW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIG1mX2Jhcl91cF9maWxlbmFtZSA8LSBnbHVlKCIxOWNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfbWZfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gbWZfYmFyX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfdXBbWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChjY19zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGNjX3RyZWVfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMTljbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX2NjX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBjY190cmVlX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoY2NfcGxvdHNfdXBbWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgY2NfYmFyX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjE5Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9jY19zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBjY19iYXJfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChjY19wbG90c191cFtbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGJwX3Bsb3RzX3VwIDwtIHBsb3RfZW5yaWNocmVzdWx0KGJwX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgYnBfdHJlZV91cF9maWxlbmFtZSA8LSBnbHVlKCIxOWNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfYnBfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGJwX3RyZWVfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c191cFtbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX2Jhcl91cF9maWxlbmFtZSA8LSBnbHVlKCIxOWNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfYnBfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gYnBfYmFyX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoYnBfcGxvdHNfdXBbWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgfQogICAgaWYgKGxlbmd0aChkb3ducCkgPiAwKSB7CiAgICAgIG1mX3NpZyA8LSBsb2NhdGlvbl9jcFtbbmFtZV1dW1tkb3ducF1dW1siZ29fZGF0YSJdXVtbIk1GX2VucmljaCJdXQogICAgICBjY19zaWcgPC0gbG9jYXRpb25fY3BbW25hbWVdXVtbZG93bnBdXVtbImdvX2RhdGEiXV1bWyJDQ19lbnJpY2giXV0KICAgICAgYnBfc2lnIDwtIGxvY2F0aW9uX2NwW1tuYW1lXV1bW2Rvd25wXV1bWyJnb19kYXRhIl1dW1siQlBfZW5yaWNoIl1dCiAgICAgIG1mX3Bsb3RzX2Rvd24gPC0gcGxvdF9lbnJpY2hyZXN1bHQobWZfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBtZl90cmVlX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTljbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fbWZfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IG1mX3RyZWVfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KG1mX3Bsb3RzX2Rvd25bWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgbWZfYmFyX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTljbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fbWZfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gbWZfYmFyX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChtZl9wbG90c19kb3duW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgY2NfcGxvdHNfZG93biA8LSBwbG90X2VucmljaHJlc3VsdChjY19zaWcsIHNob3dDYXRlZ29yeSA9IDEyKQogICAgICBjY190cmVlX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTljbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fY2Nfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGNjX3RyZWVfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX2Rvd25bWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgY2NfYmFyX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTljbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fY2Nfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gY2NfYmFyX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChjY19wbG90c19kb3duW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgYnBfcGxvdHNfZG93biA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX3RyZWVfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIxOWNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9icF9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gYnBfdHJlZV9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoYnBfcGxvdHNfZG93bltbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c19kb3duIDwtIHBsb3RfZW5yaWNocmVzdWx0KGJwX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgYnBfYmFyX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMTljbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fYnBfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gYnBfYmFyX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c19kb3duW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgIH0KICB9Cn0KYGBgCgpDb2xlbnNvIHNlbnQgYSBzcGVjaWZpYyBxdWVyeSBvZiBpbnRlcmVzdCwgY29tcGFyaW5nIFNDTiB2cy4gUmV0aW5hcwphdCBwMDggaW4gdGhlIGhldGVyb3p5Z290ZXMgaW5jbHVkaW5nIGEgc2V0IG9mIGdlbmVzIG9mIHBhcnRpY3VsYXIKaW50ZXJlc3QuICBQZXJoYXBzIEkgY2FuIHVzZSBzb21lIG9mIHRoZXNlIGFzIG1hcmtlcnMgdG8gcXVhbGl0eQpjb250cm9sIG15IHdvcmsgaW4gdGhlIGZ1dHVyZT8KCkhlcmUgYXJlIHRoZSBnZW5lczoKCk9wbjQsIEVvbWVzLCBUcnBjNywgT3BybTEsIE5yNGEzLCBUYngyMCwgSXJ4NiwgQVc1NTE5ODQsIFBjZGgxOSwKQWRjeWFwMSwgQmFpYXAzLCBDaGwxLCBHcmluM2EsIElnZjEsIEdyaWExLCBHcmluMmQsIEdyaW4zYSwgQ2hybmE2LApDaHJuYTMsIEh0cjVhLCBIdHIyYSwgSHRyNywgSXJ4NCwgUGx4bkMxLCBTZW1hNmQsIFNlbWE0ZiwgU2VtYTRhLApTZW1hNmIsIExycmM0YiwgTHJyYzU4LCBMcnJjM2IsIFdudDQsIFdudDliLCBDdHhuMywgVGVubTEsIEduYTE0LApSZ3M0LCBSZ3M2LCBSZ3M1CgpgYGB7cn0KdGFibGVfaW5wdXQgPC0gbG9jYXRpb25fdGFibGVzW1sic3JfcDA4X2hldCJdXQp0YWJsZV9uYW1lIDwtICJzcl9wMDhfaGV0Igp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQppbnRlcmVzdGluZ19nZW5lcyA8LSBjKCJPcG40IiwgIkVvbWVzIiwgIlRycGM3IiwgIk9wcm0xIiwgIk5yNGEzIiwgIlRieDIwIiwKICAgICAgICAgICAgICAgICAgICAgICAiSXJ4NiIsICJBVzU1MTk4NCIsICJQY2RoMTkiLCAiQWRjeWFwMXIxIiwgIkJhaWFwMyIsCiAgICAgICAgICAgICAgICAgICAgICAgIkNobDEiLCAiR3JpbjNhIiwgIklnZjEiLCAiR3JpYTEiLCAiR3JpbjJkIiwgIkdyaW4zYSIsCiAgICAgICAgICAgICAgICAgICAgICAgIkNocm5hNiIsICJDaHJuYTMiLCAiSHRyNWEiLCAiSHRyMmEiLCAiSHRyNyIsICJJcng0IiwKICAgICAgICAgICAgICAgICAgICAgICAiUGx4bkMxIiwgIlNlbWE2ZCIsICJTZW1hNGYiLCAiU2VtYTRhIiwgIlNlbWE2YiIsICJMcnJjNGIiLAogICAgICAgICAgICAgICAgICAgICAgICJMcnJjNTgiLCAiTHJyYzNiIiwgIldudDQiLCAiV250OWIiLCAiQ3R4bjMiLCAiVGVubTEiLCAiR25hMTQiLAogICAgICAgICAgICAgICAgICAgICAgICJSZ3M0IiwgIlJnczYiLCAiUmdzNSIsICJQb3U0ZjIiLCAiQ2hybmIzIiwgIkJjYW4iKQpzcl9wMDhfaGV0X3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgY29sb3JfbG93ID0gY29sb3JzW1siaGV0X3JldGluYSJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9zY24iXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIGFscGhhID0gMS4wLAogIHNpemUgPSA0KQpwcChmaWxlID0gIjIwbG9jYXRpb25fbWFfdm9sY2Fub190eC9zcl9wMDhfaGV0X3ZvbGNhbm8ucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpzcl9wMDhfaGV0X3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnNyX3AwOF9oZXRfdm9sY2Fub1tbInBsb3QiXV0Kc3JfcDA4X2hldF9tYSA8LSBwbG90X21hX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZXhwcl9jb2wgPSAiZGVzZXFfYmFzZW1lYW4iLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImhldF9yZXRpbmEiXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJoZXRfc2NuIl1dLAogIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgb3V0bGluZSA9IG91dGxpbmUpCnBwKGZpbGUgPSAiMjBsb2NhdGlvbl9tYV92b2xjYW5vX3R4L3NyX3AwOF9oZXRfbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpzcl9wMDhfaGV0X21hW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpzcl9wMDhfaGV0X21hW1sicGxvdCJdXQpgYGAKCiMjIyBBbHNvIHRoZSBrbwoKYGBge3J9CnRhYmxlX2lucHV0IDwtIGxvY2F0aW9uX3RhYmxlc1tbInNyX3AwOF9rbyJdXQp0YWJsZV9uYW1lIDwtICJzcl9wMDhfa28iCnRhYmxlIDwtIHRhYmxlX2lucHV0W1siZGF0YSJdXVtbdGFibGVfbmFtZV1dCnNyX3AwOF9rb192b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImtvX3JldGluYSJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImtvX3NjbiJdXSwKICBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgYWxwaGEgPSAxLjAsCiAgc2l6ZSA9IDQpCnBwKGZpbGUgPSAiMjBsb2NhdGlvbl9tYV92b2xjYW5vX3R4L3NyX3AwOF9rb192b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKc3JfcDA4X2tvX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnNyX3AwOF9rb192b2xjYW5vW1sicGxvdCJdXQpzcl9wMDhfa29fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19yZXRpbmEiXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJrb19zY24iXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBvdXRsaW5lID0gb3V0bGluZSkKcHAoZmlsZSA9ICIyMGxvY2F0aW9uX21hX3ZvbGNhbm9fdHgvc3JfcDA4X2tvX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKc3JfcDA4X2tvX21hW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpzcl9wMDhfa29fbWFbWyJwbG90Il1dCmBgYAoKYGBge3J9CnRhYmxlX2lucHV0IDwtIGxvY2F0aW9uX3RhYmxlc1tbInNyX3AxNV9oZXQiXV0KdGFibGVfbmFtZSA8LSAic3JfcDE1X2hldCIKdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KaW50ZXJlc3RpbmdfZ2VuZXMgPC0gYygiT3BuNCIsICJFb21lcyIsICJUcnBjNyIsICJPcHJtMSIsICJOcjRhMyIsICJUYngyMCIsCiAgICAgICAgICAgICAgICAgICAgICAgIklyeDYiLCAiQVc1NTE5ODQiLCAiUGNkaDE5IiwgIkFkY3lhcDFyMSIsICJCYWlhcDMiLAogICAgICAgICAgICAgICAgICAgICAgICJDaGwxIiwgIkdyaW4zYSIsICJJZ2YxIiwgIkdyaWExIiwgIkdyaW4yZCIsICJHcmluM2EiLAogICAgICAgICAgICAgICAgICAgICAgICJDaHJuYTYiLCAiQ2hybmEzIiwgIkh0cjVhIiwgIkh0cjJhIiwgIkh0cjciLCAiSXJ4NCIsCiAgICAgICAgICAgICAgICAgICAgICAgIlBseG5DMSIsICJTZW1hNmQiLCAiU2VtYTRmIiwgIlNlbWE0YSIsICJTZW1hNmIiLCAiTHJyYzRiIiwKICAgICAgICAgICAgICAgICAgICAgICAiTHJyYzU4IiwgIkxycmMzYiIsICJXbnQ0IiwgIldudDliIiwgIkN0eG4zIiwgIlRlbm0xIiwgIkduYTE0IiwKICAgICAgICAgICAgICAgICAgICAgICAiUmdzNCIsICJSZ3M2IiwgIlJnczUiLCAiUG91NGYyIiwgIkNocm5iMyIsICJCY2FuIikKc3JfcDE1X2hldF92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImhldF9yZXRpbmEiXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJoZXRfc2NuIl1dLAogIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBhbHBoYSA9IDEuMCwKICBzaXplID0gNCkKcHAoZmlsZSA9ICIyMGxvY2F0aW9uX21hX3ZvbGNhbm9fdHgvc3JfcDE1X2hldF92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKc3JfcDE1X2hldF92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpzcl9wMTVfaGV0X3ZvbGNhbm9bWyJwbG90Il1dCnNyX3AxNV9oZXRfbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJoZXRfcmV0aW5hIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3NjbiJdXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIG91dGxpbmUgPSBvdXRsaW5lKQpwcChmaWxlID0gIjIwbG9jYXRpb25fbWFfdm9sY2Fub190eC9zcl9wMTVfaGV0X21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKc3JfcDE1X2hldF9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKc3JfcDE1X2hldF9tYVtbInBsb3QiXV0KYGBgCgojIyMgQWxzbyB0aGUga28KCmBgYHtyfQp0YWJsZV9pbnB1dCA8LSBsb2NhdGlvbl90YWJsZXNbWyJzcl9wMTVfa28iXV0KdGFibGVfbmFtZSA8LSAic3JfcDE1X2tvIgp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQpzcl9wMTVfa29fdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19yZXRpbmEiXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJrb19zY24iXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIGFscGhhID0gMS4wLAogIHNpemUgPSA0LCBtaW4uc2VnbWVudC5sZW5ndGggPSAwLCBwb2ludC5wYWRkaW5nID0gMC4yKQpwcChmaWxlID0gIjIwbG9jYXRpb25fbWFfdm9sY2Fub190eC9zcl9wMTVfa29fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKc3JfcDE1X2tvX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnNyX3AxNV9rb192b2xjYW5vW1sicGxvdCJdXQpzcl9wMTVfa29fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19yZXRpbmEiXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJrb19zY24iXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBvdXRsaW5lID0gb3V0bGluZSkKcHAoZmlsZSA9ICIyMGxvY2F0aW9uX21hX3ZvbGNhbm9fdHgvc3JfcDE1X2tvX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKc3JfcDE1X2tvX21hW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpzcl9wMTVfa29fbWFbWyJwbG90Il1dCmBgYAoKIyMjIyBUZXN0IGEgc3BlY2lmaWMgbG9jYXRpb24gcXVlcnkgZm9yIGR1cGxpY2F0ZWQgSURzCgpMZXQgdXMgc2VlIGlmIGFueSBFbnNlbWJsIGdlbmUgSURzIGFuZC9vciBNR0kgSURzIGFyZSBzaGFyZWQgaW4gdGhlCndvcmtzaGVldCBsb2NhdGlvbl9zcl9wMDhfa29faW5jbHVkaW5nX3d0XzAuMV9kZWNyZWFzZWRfc2lnIHVwL2Rvd24uCgpgYGB7cn0KdGVzdF90YWJsZV91cCA8LSBsb2NhdGlvbl9zaWdbWyJzcl9wMDhfa28iXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dCnRlc3RfdGFibGVfZG93biA8LSBsb2NhdGlvbl9zaWdbWyJzcl9wMDhfa28iXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0KCnF1ZXJ5IDwtIGxpc3QoInVwIiA9IHJvd25hbWVzKHRlc3RfdGFibGVfdXApLAogICAgICAgICAgICAgICJkb3duIiA9IHJvd25hbWVzKHRlc3RfdGFibGVfZG93bikpCnF1ZXJ5X3Vwc2V0IDwtIFVwU2V0Ujo6ZnJvbUxpc3QocXVlcnkpClVwU2V0Ujo6dXBzZXQocXVlcnlfdXBzZXQpCgpxdWVyeSA8LSBsaXN0KCJ1cCIgPSB0ZXN0X3RhYmxlX3VwW1siZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIl1dLAogICAgICAgICAgICAgICJkb3duIiA9IHRlc3RfdGFibGVfZG93bltbImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSJdXSkKcXVlcnlfdXBzZXQgPC0gVXBTZXRSOjpmcm9tTGlzdChxdWVyeSkKVXBTZXRSOjp1cHNldChxdWVyeV91cHNldCkKIyMgb2ssIGdvb2QuCmBgYAoKIyMjIyBSZXBlYXQgd2l0aCB0aGUgc3RyaWN0IGZpbHRlcgoKYGBge3J9CmxvY2F0aW9uX3N0cmljdF90YWJsZXMgPC0gbGlzdCgpCmxvY2F0aW9uX3N0cmljdF9zaWcgPC0gbGlzdCgpCmxvY2F0aW9uX3N0cmljdF9ncCA8LSBsaXN0KCkKbG9jYXRpb25fc3RyaWN0X2NwIDwtIGxpc3QoKQpmb3IgKGsgaW4gc2VxX2Fsb25nKGxvY2F0aW9uX2tlZXBlcnMpKSB7CiAgbmFtZSA8LSBuYW1lcyhsb2NhdGlvbl9rZWVwZXJzKVtrXQogIG1lc3NhZ2UoIkV4YW1pbmluZyAiLCBuYW1lKQogIGtlZXBlciA8LSBsb2NhdGlvbl9rZWVwZXJzW25hbWVdCiAgaW5jbHVkZXMgPC0gbG9jYXRpb25faW5jbHVzaW9uc19zdHJpY3RbW25hbWVdXQogIGluY2x1ZGVfbmFtZSA8LSBwYXN0ZTAoImluY18iLCBuYW1lKQogIGluY2x1ZGVfZGZfbmFtZSA8LSBwYXN0ZTAoImRmXyIsIG5hbWUpCiAgaW5jbHVkZV9kZiA8LSBsb2NhdGlvbl9pbmNsdXNpb25zX3N0cmljdFtbaW5jbHVkZV9kZl9uYW1lXV0KICBpbmNsdWRlcyA8LSBsb2NhdGlvbl9pbmNsdXNpb25zX3N0cmljdFtbaW5jbHVkZV9uYW1lXV0KICBmb3VuZF9pbmNsdWRlcyA8LSByb3duYW1lcyhsb2NhdGlvbl9zaWdfZnVsbFtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pICVpbiUgaW5jbHVkZXMKICBzdW1tYXJ5KGZvdW5kX2luY2x1ZGVzKQogIGlmIChzdW0oZm91bmRfaW5jbHVkZXMpID09IDApIHsKICAgIG5leHQKICB9CiAgaW5jbHVkZV9maWxlbmFtZSA8LSBnbHVlKCIyMWxvY2F0aW9uX3N0cmljdF9jb250cmFzdHNfdHgve25hbWV9X2luY2x1ZGluZ193dF97bGZjX2N1dG9mZn1fZGVjcmVhc2VkX3RhYmxlLXZ7dmVyfS54bHN4IikKICBpbmNsdWRlX3NpZ19maWxlbmFtZSA8LSBnbHVlKCIyMWxvY2F0aW9uX3N0cmljdF9jb250cmFzdHNfdHgve25hbWV9X2luY2x1ZGluZ193dF97bGZjX2N1dG9mZn1fZGVjcmVhc2VkX3NpZy12e3Zlcn0ueGxzeCIpCiAgbG9jYXRpb25fc3RyaWN0X3RhYmxlc1tbbmFtZV1dIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgbG9jYXRpb25fZGUsIGV4dHJhX2Fubm90ID0gaW5jbHVkZV9kZiwKICAgIGtlZXBlcnMgPSBrZWVwZXIsIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwKICAgIGV4Y2VsID0gaW5jbHVkZV9maWxlbmFtZSwgd2FudGVkX2dlbmVzID0gaW5jbHVkZXMpCiAgcHJpbnQobG9jYXRpb25fc3RyaWN0X3RhYmxlc1tbbmFtZV1dKQogIGxvY2F0aW9uX3N0cmljdF9zaWdbW25hbWVdXSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgbG9jYXRpb25fc3RyaWN0X3RhYmxlc1tbbmFtZV1dLCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLAogICAgZXhjZWwgPSBpbmNsdWRlX3NpZ19maWxlbmFtZSkKICBwcmludChsb2NhdGlvbl9zdHJpY3Rfc2lnW1tuYW1lXV0pCiAgbnVtX3Jvd3MgPC0gbnJvdyhsb2NhdGlvbl9zdHJpY3Rfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSArCiAgICBucm93KGxvY2F0aW9uX3N0cmljdF9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSkKICBtZXNzYWdlKCJUaGVyZSBhcmUgIiwgbnVtX3Jvd3MsICIgc2lnbmlmaWNhbnQgdXAgYW5kIGRvd24gZ2VuZXMuIikKICBpZiAobnVtX3Jvd3MgPiAxMCkgewogICAgbG9jYXRpb25fc3RyaWN0X2dwW1tuYW1lXV0gPC0gYWxsX2dwcm9maWxlcihsb2NhdGlvbl9zdHJpY3Rfc2lnW1tuYW1lXV0sIHNwZWNpZXMgPSAibW11c2N1bHVzIikKICAgIGxvY2F0aW9uX3N0cmljdF9jcFtbbmFtZV1dIDwtIGFsbF9jcHJvZmlsZXIoCiAgICAgIGxvY2F0aW9uX3N0cmljdF9zaWdbW25hbWVdXSwgbG9jYXRpb25fc3RyaWN0X3RhYmxlc1tbbmFtZV1dLAogICAgICBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBnb19sZXZlbCA9IGdvX2xldmVsLCBvcmdkYl9mcm9tID0gb3JnZGJfZnJvbSwKICAgICAgbWF4X2dyb3Vwc2l6ZSA9IG1heF9ncm91cHNpemUsIG9yZ2FuaXNtID0gIm1vdXNlIikKICAgIGNwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2NwKGxvY2F0aW9uX3N0cmljdF9jcFtbbmFtZV1dLCBwcmVmaXggPSAiMjIiLCBzdWZmaXggPSAic3RyaWN0IikKICAgIGdwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2dwKGxvY2F0aW9uX3N0cmljdF9ncFtbbmFtZV1dLCBwcmVmaXggPSAiMjIiLCBzdWZmaXggPSAic3RyaWN0IikKICB9Cn0KYGBgCgpQcmludCBvdXQgYWxsIHRoZSBwbG90cyBpbiBhIHNlcGFyYXRlIGJsb2NrLgoKYGBge3J9CmZvciAoayBpbiBzZXFfYWxvbmcobG9jYXRpb25fa2VlcGVycykpIHsKICBuYW1lIDwtIG5hbWVzKGxvY2F0aW9uX2tlZXBlcnMpW2tdCiAgbWVzc2FnZSgiRXhhbWluaW5nICIsIG5hbWUpCiAga2VlcGVyIDwtIGxvY2F0aW9uX2tlZXBlcnNbbmFtZV0KICBpbmNsdWRlcyA8LSBsb2NhdGlvbl9pbmNsdXNpb25zX3N0cmljdFtbbmFtZV1dCiAgaW5jbHVkZV9uYW1lIDwtIHBhc3RlMCgiaW5jXyIsIG5hbWUpCiAgaW5jbHVkZV9kZl9uYW1lIDwtIHBhc3RlMCgiZGZfIiwgbmFtZSkKICBpbmNsdWRlX2RmIDwtIGxvY2F0aW9uX2luY2x1c2lvbnNfc3RyaWN0W1tpbmNsdWRlX2RmX25hbWVdXQogIGluY2x1ZGVzIDwtIGxvY2F0aW9uX2luY2x1c2lvbnNfc3RyaWN0W1tpbmNsdWRlX25hbWVdXQogIHN1bW1hcnkocm93bmFtZXMobG9jYXRpb25fc2lnX2Z1bGxbWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSAlaW4lIGluY2x1ZGVzKQogIG51bV9yb3dzIDwtIG5yb3cobG9jYXRpb25fc3RyaWN0X3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgKwogICAgbnJvdyhsb2NhdGlvbl9zdHJpY3Rfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0pCiAgbnJvdyhsb2NhdGlvbl9zdHJpY3Rfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKQogIG5yb3cobG9jYXRpb25fc3RyaWN0X3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dKQogIG1lc3NhZ2UoIlRoZXJlIGFyZSAiLCBudW1fcm93cywgIiBzaWduaWZpY2FudCB1cCBhbmQgZG93biBnZW5lcy4iKQogIG51bV9vYmplY3RzIDwtIGxlbmd0aChsb2NhdGlvbl9zdHJpY3RfY3BbW25hbWVdXSkKICBpZiAobnVtX29iamVjdHMgPT0gMCkgewogICAgd2FybmluZygiU29tZXRoaW5nIHdlbnQgd3JvbmcgaW4gYWxsX2Nwcm9maWxlci4iKQogIH0gZWxzZSB7CiAgICB1cHAgPC0gd2hpY2goZ3JlcGwoeCA9IG5hbWVzKGxvY2F0aW9uX3N0cmljdF9jcFtbbmFtZV1dKSwgcGF0dGVybiA9ICJfdXAkIikpCiAgICBkb3ducCA8LSB3aGljaChncmVwbCh4ID0gbmFtZXMobG9jYXRpb25fc3RyaWN0X2NwW1tuYW1lXV0pLCBwYXR0ZXJuID0gIl9kb3duJCIpKQogICAgaWYgKGxlbmd0aCh1cHApID4gMCkgewogICAgICBtZl9zaWcgPC0gbG9jYXRpb25fc3RyaWN0X2NwW1tuYW1lXV1bW3VwcF1dW1siZ29fZGF0YSJdXVtbIk1GX2VucmljaCJdXQogICAgICBjY19zaWcgPC0gbG9jYXRpb25fc3RyaWN0X2NwW1tuYW1lXV1bW3VwcF1dW1siZ29fZGF0YSJdXVtbIkNDX2VucmljaCJdXQogICAgICBicF9zaWcgPC0gbG9jYXRpb25fc3RyaWN0X2NwW1tuYW1lXV1bW3VwcF1dW1siZ29fZGF0YSJdXVtbIkJQX2VucmljaCJdXQogICAgICBtZl9wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChtZl9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIG1mX3RyZWVfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMjNjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX21mX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl90cmVlX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfdXBbWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgbWZfYmFyX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjIzY2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9tZl9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl9iYXJfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChtZl9wbG90c191cFtbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGNjX3Bsb3RzX3VwIDwtIHBsb3RfZW5yaWNocmVzdWx0KGNjX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgY2NfdHJlZV91cF9maWxlbmFtZSA8LSBnbHVlKCIyM2NsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfY2Nfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGNjX3RyZWVfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChjY19wbG90c191cFtbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19iYXJfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMjNjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX2NjX3NpZ19kb3QucGRmIikKICAgICAgcHAoZmlsZSA9IGNjX2Jhcl91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX3VwW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgYnBfcGxvdHNfdXAgPC0gcGxvdF9lbnJpY2hyZXN1bHQoYnBfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBicF90cmVlX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjIzY2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9icF9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gYnBfdHJlZV91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGJwX3Bsb3RzX3VwW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGJwX3Bsb3RzX3VwIDwtIHBsb3RfZW5yaWNocmVzdWx0KGJwX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgYnBfYmFyX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjIzY2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9icF9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF9iYXJfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c191cFtbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICB9CiAgICBpZiAobGVuZ3RoKGRvd25wKSA+IDApIHsKICAgICAgbWZfc2lnIDwtIGxvY2F0aW9uX3N0cmljdF9jcFtbbmFtZV1dW1tkb3ducF1dW1siZ29fZGF0YSJdXVtbIk1GX2VucmljaCJdXQogICAgICBjY19zaWcgPC0gbG9jYXRpb25fc3RyaWN0X2NwW1tuYW1lXV1bW2Rvd25wXV1bWyJnb19kYXRhIl1dW1siQ0NfZW5yaWNoIl1dCiAgICAgIGJwX3NpZyA8LSBsb2NhdGlvbl9zdHJpY3RfY3BbW25hbWVdXVtbZG93bnBdXVtbImdvX2RhdGEiXV1bWyJCUF9lbnJpY2giXV0KICAgICAgbWZfcGxvdHNfZG93biA8LSBwbG90X2VucmljaHJlc3VsdChtZl9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIG1mX3RyZWVfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIyM2NsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9tZl9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gbWZfdHJlZV9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfZG93bltbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBtZl9iYXJfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIyM2NsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9tZl9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl9iYXJfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KG1mX3Bsb3RzX2Rvd25bWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19wbG90c19kb3duIDwtIHBsb3RfZW5yaWNocmVzdWx0KGNjX3NpZywgc2hvd0NhdGVnb3J5ID0gMTIpCiAgICAgIGNjX3RyZWVfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIyM2NsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9jY19zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gY2NfdHJlZV9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoY2NfcGxvdHNfZG93bltbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19iYXJfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIyM2NsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9jY19zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBjY19iYXJfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX2Rvd25bWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c19kb3duIDwtIHBsb3RfZW5yaWNocmVzdWx0KGJwX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgYnBfdHJlZV9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjIzY2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV9kb3duX2JwX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF90cmVlX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c19kb3duW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGJwX3Bsb3RzX2Rvd24gPC0gcGxvdF9lbnJpY2hyZXN1bHQoYnBfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBicF9iYXJfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIyM2NsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9icF9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF9iYXJfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGJwX3Bsb3RzX2Rvd25bWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgfQogIH0KfQpgYGAKCkNvbGVuc28gc2VudCBhIHNwZWNpZmljIHF1ZXJ5IG9mIGludGVyZXN0LCBjb21wYXJpbmcgU0NOIHZzLiBSZXRpbmFzCmF0IHAwOCBpbiB0aGUgaGV0ZXJvenlnb3RlcyBpbmNsdWRpbmcgYSBzZXQgb2YgZ2VuZXMgb2YgcGFydGljdWxhcgppbnRlcmVzdC4gIFBlcmhhcHMgSSBjYW4gdXNlIHNvbWUgb2YgdGhlc2UgYXMgbWFya2VycyB0byBxdWFsaXR5CmNvbnRyb2wgbXkgd29yayBpbiB0aGUgZnV0dXJlPwoKSGVyZSBhcmUgdGhlIGdlbmVzOgoKT3BuNCwgRW9tZXMsIFRycGM3LCBPcHJtMSwgTnI0YTMsIFRieDIwLCBJcng2LCBBVzU1MTk4NCwgUGNkaDE5LApBZGN5YXAxLCBCYWlhcDMsIENobDEsIEdyaW4zYSwgSWdmMSwgR3JpYTEsIEdyaW4yZCwgR3JpbjNhLCBDaHJuYTYsCkNocm5hMywgSHRyNWEsIEh0cjJhLCBIdHI3LCBJcng0LCBQbHhuQzEsIFNlbWE2ZCwgU2VtYTRmLCBTZW1hNGEsClNlbWE2YiwgTHJyYzRiLCBMcnJjNTgsIExycmMzYiwgV250NCwgV250OWIsIEN0eG4zLCBUZW5tMSwgR25hMTQsClJnczQsIFJnczYsIFJnczUKCmBgYHtyfQp0YWJsZV9pbnB1dCA8LSBsb2NhdGlvbl9zdHJpY3RfdGFibGVzW1sic3JfcDA4X2hldCJdXQp0YWJsZV9uYW1lIDwtICJzcl9wMDhfaGV0Igp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQppbnRlcmVzdGluZ19nZW5lcyA8LSBjKCJPcG40IiwgIkVvbWVzIiwgIlRycGM3IiwgIk9wcm0xIiwgIk5yNGEzIiwgIlRieDIwIiwKICAgICAgICAgICAgICAgICAgICAgICAiSXJ4NiIsICJBVzU1MTk4NCIsICJQY2RoMTkiLCAiQWRjeWFwMXIxIiwgIkJhaWFwMyIsCiAgICAgICAgICAgICAgICAgICAgICAgIkNobDEiLCAiR3JpbjNhIiwgIklnZjEiLCAiR3JpYTEiLCAiR3JpbjJkIiwgIkdyaW4zYSIsCiAgICAgICAgICAgICAgICAgICAgICAgIkNocm5hNiIsICJDaHJuYTMiLCAiSHRyNWEiLCAiSHRyMmEiLCAiSHRyNyIsICJJcng0IiwKICAgICAgICAgICAgICAgICAgICAgICAiUGx4bkMxIiwgIlNlbWE2ZCIsICJTZW1hNGYiLCAiU2VtYTRhIiwgIlNlbWE2YiIsICJMcnJjNGIiLAogICAgICAgICAgICAgICAgICAgICAgICJMcnJjNTgiLCAiTHJyYzNiIiwgIldudDQiLCAiV250OWIiLCAiQ3R4bjMiLCAiVGVubTEiLCAiR25hMTQiLAogICAgICAgICAgICAgICAgICAgICAgICJSZ3M0IiwgIlJnczYiLCAiUmdzNSIsICJQb3U0ZjIiLCAiQ2hybmIzIiwgIkJjYW4iKQpzcl9wMDhfaGV0X3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgY29sb3JfbG93ID0gY29sb3JzW1siaGV0X3JldGluYSJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9zY24iXV0sCiAgbGFiZWxfY29sdW1uID0gdHhfbGFiZWxfY29sdW1uLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBhbHBoYSA9IDEuMCwKICBzaXplID0gNCkKcHAoZmlsZSA9ICIyNGxvY2F0aW9uX21hX3ZvbGNhbm9fc3RyaWN0X3R4L3NyX3AwOF9oZXRfdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCnNyX3AwOF9oZXRfdm9sY2Fub1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKc3JfcDA4X2hldF92b2xjYW5vW1sicGxvdCJdXQpzcl9wMDhfaGV0X21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gY29sb3JzW1siaGV0X3JldGluYSJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9zY24iXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgb3V0bGluZSA9IG91dGxpbmUpCnBwKGZpbGUgPSAiMjRsb2NhdGlvbl9tYV92b2xjYW5vX3N0cmljdF90eC9zcl9wMDhfaGV0X21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKc3JfcDA4X2hldF9tYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKc3JfcDA4X2hldF9tYVtbInBsb3QiXV0KYGBgCgojIyMgQWxzbyB0aGUga28KCmBgYHtyfQp0YWJsZV9pbnB1dCA8LSBsb2NhdGlvbl9zdHJpY3RfdGFibGVzW1sic3JfcDA4X2tvIl1dCnRhYmxlX25hbWUgPC0gInNyX3AwOF9rbyIKdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0Kc3JfcDA4X2tvX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fcmV0aW5hIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1sia29fc2NuIl1dLAogIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgYWxwaGEgPSAxLjAsCiAgc2l6ZSA9IDQpCnBwKGZpbGUgPSAiMjRsb2NhdGlvbl9tYV92b2xjYW5vX3N0cmljdF90eC9zcl9wMDhfa29fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCnNyX3AwOF9rb192b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpzcl9wMDhfa29fdm9sY2Fub1tbInBsb3QiXV0Kc3JfcDA4X2tvX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fcmV0aW5hIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1sia29fc2NuIl1dLAogIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSB0eF9sYWJlbF9jb2x1bW4sIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIG91dGxpbmUgPSBvdXRsaW5lKQpwcChmaWxlID0gIjI0bG9jYXRpb25fbWFfdm9sY2Fub19zdHJpY3RfdHgvc3JfcDA4X2tvX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKc3JfcDA4X2tvX21hW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpzcl9wMDhfa29fbWFbWyJwbG90Il1dCmBgYAoKIyMjIEFsc28gdGhlIGtvCgpgYGB7cn0KdGFibGVfaW5wdXQgPC0gbG9jYXRpb25fdGFibGVzW1sic3JfcDE1X2tvIl1dCnRhYmxlX25hbWUgPC0gInNyX3AxNV9rbyIKdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0Kc3JfcDE1X2tvX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fcmV0aW5hIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1sia29fc2NuIl1dLAogIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgYWxwaGEgPSAxLjAsCiAgc2l6ZSA9IDQsIG1pbi5zZWdtZW50Lmxlbmd0aCA9IDAsIHBvaW50LnBhZGRpbmcgPSAwLjIpCnBwKGZpbGUgPSAiMjRsb2NhdGlvbl9tYV92b2xjYW5vX3N0cmljdF90eC9zcl9wMTVfa29fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKc3JfcDE1X2tvX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnNyX3AxNV9rb192b2xjYW5vW1sicGxvdCJdXQpzcl9wMTVfa29fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19yZXRpbmEiXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJrb19zY24iXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9IHR4X2xhYmVsX2NvbHVtbiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgb3V0bGluZSA9IG91dGxpbmUpCnBwKGZpbGUgPSAiMjRsb2NhdGlvbl9tYV92b2xjYW5vX3N0cmljdF90eC9zcl9wMTVfa29fbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpzcl9wMTVfa29fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnNyX3AxNV9rb19tYVtbInBsb3QiXV0KYGBgCgojIyMgQW5kIHRpbWUKCmBgYHtyfQp0aW1lX3RhYmxlc19mdWxsIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRpbWVfZGUsIGtlZXBlcnMgPSB0aW1lX2tlZXBlcnMsCiAgbGFiZWxfY29sdW1uID0gdHhfbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgiMjVmdWxsX2NvbnRyYXN0c190aW1lX3R4L2Z1bGxfdGFibGVzLXZ7dmVyfS54bHN4IikpCnRpbWVfc2lnX2Z1bGwgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0aW1lX3RhYmxlc19mdWxsLCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLAogIGV4Y2VsID0gZ2x1ZSgiMjVmdWxsX2NvbnRyYXN0c190aW1lX3R4L2Z1bGxfc2lnLXZ7dmVyfS54bHN4IikpCmBgYAoKYGBge3J9CnRpbWVfdGFibGVzIDwtIGxpc3QoKQp0aW1lX3NpZyA8LSBsaXN0KCkKdGltZV9ncCA8LSBsaXN0KCkKdGltZV9jcCA8LSBsaXN0KCkKZm9yIChrIGluIHNlcV9hbG9uZyh0aW1lX2tlZXBlcnMpKSB7CiAgbmFtZSA8LSBuYW1lcyh0aW1lX2tlZXBlcnMpW2tdCiAgbWVzc2FnZSgiRXhhbWluaW5nICIsIG5hbWUpCiAga2VlcGVyIDwtIHRpbWVfa2VlcGVyc1tuYW1lXQogIGluY2x1ZGVzIDwtIHRpbWVfaW5jbHVzaW9uc1tbbmFtZV1dCiAgaW5jbHVkZV9uYW1lIDwtIHBhc3RlMCgiaW5jXyIsIG5hbWUpCiAgaW5jbHVkZV9kZl9uYW1lIDwtIHBhc3RlMCgiZGZfIiwgbmFtZSkKICBpbmNsdWRlX2RmIDwtIHRpbWVfaW5jbHVzaW9uc1tbaW5jbHVkZV9kZl9uYW1lXV0KICBpbmNsdWRlcyA8LSB0aW1lX2luY2x1c2lvbnNbW2luY2x1ZGVfbmFtZV1dCiAgc3VtbWFyeShyb3duYW1lcyh0aW1lX3NpZ19mdWxsW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgJWluJSBpbmNsdWRlcykKICBpbmNsdWRlX2ZpbGVuYW1lIDwtIGdsdWUoIjI2dGltZV9jb250cmFzdHNfdHgve25hbWV9X2luY2x1ZGluZ193dF97bGZjX2N1dG9mZn1fZGVjcmVhc2VkX3RhYmxlLXZ7dmVyfS54bHN4IikKICBpbmNsdWRlX3NpZ19maWxlbmFtZSA8LSBnbHVlKCIyNnRpbWVfY29udHJhc3RzX3R4L3tuYW1lfV9pbmNsdWRpbmdfd3Rfe2xmY19jdXRvZmZ9X2RlY3JlYXNlZF9zaWctdnt2ZXJ9Lnhsc3giKQogIHRpbWVfdGFibGVzW1tuYW1lXV0gPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICB0aW1lX2RlLCBleHRyYV9hbm5vdCA9IGluY2x1ZGVfZGYsCiAgICBrZWVwZXJzID0ga2VlcGVyLCBsYWJlbF9jb2x1bW4gPSB0eF9sYWJlbF9jb2x1bW4sCiAgICBleGNlbCA9IGluY2x1ZGVfZmlsZW5hbWUsIHdhbnRlZF9nZW5lcyA9IGluY2x1ZGVzKQogIHByaW50KHRpbWVfdGFibGVzW1tuYW1lXV0pCiAgdGltZV9zaWdbW25hbWVdXSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgdGltZV90YWJsZXNbW25hbWVdXSwgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICAgIGV4Y2VsID0gaW5jbHVkZV9zaWdfZmlsZW5hbWUpCiAgcHJpbnQodGltZV9zaWdbW25hbWVdXSkKICBudW1fcm93cyA8LSBucm93KHRpbWVfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSArCiAgICBucm93KHRpbWVfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0pCiAgbWVzc2FnZSgiVGhlcmUgYXJlICIsIG51bV9yb3dzLCAiIHNpZ25pZmljYW50IHVwIGFuZCBkb3duIGdlbmVzLiIpCiAgaWYgKG51bV9yb3dzID4gMTApIHsKICAgIHRpbWVfZ3BbW25hbWVdXSA8LSBhbGxfZ3Byb2ZpbGVyKHRpbWVfc2lnW1tuYW1lXV0sIHNwZWNpZXMgPSAibW11c2N1bHVzIikKICAgIGdwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2dwKHRpbWVfZ3BbW25hbWVdXSkKICAgIHRpbWVfY3BbW25hbWVdXSA8LSBhbGxfY3Byb2ZpbGVyKAogICAgICB0aW1lX3NpZ1tbbmFtZV1dLCB0aW1lX3RhYmxlc1tbbmFtZV1dLCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBvcmdhbmlzbSA9ICJtb3VzZSIsCiAgICAgIG9yZ2RiX2Zyb20gPSBvcmdkYl9mcm9tLCBnb19sZXZlbCA9IGdvX2xldmVsLCBtYXhfZ3JvdXBzaXplID0gbWF4X2dyb3Vwc2l6ZSkKICAgIGNwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2NwKHRpbWVfY3BbW25hbWVdXSwgcHJlZml4ID0gIjI3IikKICAgIGdwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2dwKHRpbWVfZ3BbW25hbWVdXSwgcHJlZml4ID0gIjI3IikKICB9Cn0KYGBgCgpTZW5kIHRoZSBwbG90cyBzZXBhcmF0ZWx5LgoKYGBge3J9CmZvciAoayBpbiBzZXFfYWxvbmcodGltZV9rZWVwZXJzKSkgewogIG5hbWUgPC0gbmFtZXModGltZV9rZWVwZXJzKVtrXQogIG1lc3NhZ2UoIkV4YW1pbmluZyAiLCBuYW1lKQogIGtlZXBlciA8LSB0aW1lX2tlZXBlcnNbbmFtZV0KICBpbmNsdWRlcyA8LSB0aW1lX2luY2x1c2lvbnNbW25hbWVdXQogIGluY2x1ZGVfbmFtZSA8LSBwYXN0ZTAoImluY18iLCBuYW1lKQogIGluY2x1ZGVfZGZfbmFtZSA8LSBwYXN0ZTAoImRmXyIsIG5hbWUpCiAgaW5jbHVkZV9kZiA8LSB0aW1lX2luY2x1c2lvbnNbW2luY2x1ZGVfZGZfbmFtZV1dCiAgaW5jbHVkZXMgPC0gdGltZV9pbmNsdXNpb25zW1tpbmNsdWRlX25hbWVdXQogIG51bV9yb3dzIDwtIG5yb3codGltZV9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pICsKICAgIG5yb3codGltZV9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSkKICBtZXNzYWdlKCJUaGVyZSBhcmUgIiwgbnVtX3Jvd3MsICIgc2lnbmlmaWNhbnQgdXAgYW5kIGRvd24gZ2VuZXMuIikKICBudW1fb2JqZWN0cyA8LSBsZW5ndGgodGltZV9jcFtbbmFtZV1dKQogIGlmIChudW1fb2JqZWN0cyA9PSAwKSB7CiAgICB3YXJuaW5nKCJTb21ldGhpbmcgZmFpbGVkIGluIGFsbF9jcHJvZmlsZXIuIikKICB9IGVsc2UgewogICAgdXBwIDwtIHdoaWNoKGdyZXBsKHggPSBuYW1lcyh0aW1lX2NwW1tuYW1lXV0pLCBwYXR0ZXJuID0gIl91cCQiKSkKICAgIGRvd25wIDwtIHdoaWNoKGdyZXBsKHggPSBuYW1lcyh0aW1lX2NwW1tuYW1lXV0pLCBwYXR0ZXJuID0gIl9kb3duJCIpKQogICAgaWYgKGxlbmd0aCh1cHApID4gMCkgewogICAgICBtZl9zaWcgPC0gdGltZV9jcFtbbmFtZV1dW1t1cHBdXVtbImdvX2RhdGEiXV1bWyJNRl9lbnJpY2giXV0KICAgICAgY2Nfc2lnIDwtIHRpbWVfY3BbW25hbWVdXVtbdXBwXV1bWyJnb19kYXRhIl1dW1siQ0NfZW5yaWNoIl1dCiAgICAgIGJwX3NpZyA8LSB0aW1lX2NwW1tuYW1lXV1bW3VwcF1dW1siZ29fZGF0YSJdXVtbIkJQX2VucmljaCJdXQogICAgICBtZl9wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChtZl9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIG1mX3RyZWVfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMjhjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX21mX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl90cmVlX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfdXBbWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgbWZfYmFyX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjI4Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9tZl9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl9iYXJfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChtZl9wbG90c191cFtbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGNjX3Bsb3RzX3VwIDwtIHBsb3RfZW5yaWNocmVzdWx0KGNjX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgY2NfdHJlZV91cF9maWxlbmFtZSA8LSBnbHVlKCIyOGNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fdXBfY2Nfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGNjX3RyZWVfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChjY19wbG90c191cFtbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19iYXJfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMjhjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X3VwX2NjX3NpZ19kb3QucGRmIikKICAgICAgcHAoZmlsZSA9IGNjX2Jhcl91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX3VwW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgYnBfcGxvdHNfdXAgPC0gcGxvdF9lbnJpY2hyZXN1bHQoYnBfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBicF90cmVlX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjI4Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9icF9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gYnBfdHJlZV91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGJwX3Bsb3RzX3VwW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGJwX3Bsb3RzX3VwIDwtIHBsb3RfZW5yaWNocmVzdWx0KGJwX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgYnBfYmFyX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjI4Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV91cF9icF9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF9iYXJfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c191cFtbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICB9CiAgICBpZiAobGVuZ3RoKGRvd25wKSA+IDApIHsKICAgICAgbWZfc2lnIDwtIHRpbWVfY3BbW25hbWVdXVtbZG93bnBdXVtbImdvX2RhdGEiXV1bWyJNRl9lbnJpY2giXV0KICAgICAgY2Nfc2lnIDwtIHRpbWVfY3BbW25hbWVdXVtbZG93bnBdXVtbImdvX2RhdGEiXV1bWyJDQ19lbnJpY2giXV0KICAgICAgYnBfc2lnIDwtIHRpbWVfY3BbW25hbWVdXVtbZG93bnBdXVtbImdvX2RhdGEiXV1bWyJCUF9lbnJpY2giXV0KICAgICAgbWZfcGxvdHNfZG93biA8LSBwbG90X2VucmljaHJlc3VsdChtZl9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIG1mX3RyZWVfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIyOGNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9tZl9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gbWZfdHJlZV9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfZG93bltbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBtZl9iYXJfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIyOGNsdXN0ZXJQcm9maWxlcl9wbG90c190eC97bmFtZX1fZG93bl9tZl9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl9iYXJfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KG1mX3Bsb3RzX2Rvd25bWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19wbG90c19kb3duIDwtIHBsb3RfZW5yaWNocmVzdWx0KGNjX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgY2NfdHJlZV9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjI4Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV9kb3duX2NjX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBjY190cmVlX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChjY19wbG90c19kb3duW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGNjX2Jhcl9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjI4Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV9kb3duX2NjX3NpZ19kb3QucGRmIikKICAgICAgcHAoZmlsZSA9IGNjX2Jhcl9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoY2NfcGxvdHNfZG93bltbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGJwX3Bsb3RzX2Rvd24gPC0gcGxvdF9lbnJpY2hyZXN1bHQoYnBfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBicF90cmVlX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMjhjbHVzdGVyUHJvZmlsZXJfcGxvdHNfdHgve25hbWV9X2Rvd25fYnBfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGJwX3RyZWVfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGJwX3Bsb3RzX2Rvd25bWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgYnBfcGxvdHNfZG93biA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX2Jhcl9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjI4Y2x1c3RlclByb2ZpbGVyX3Bsb3RzX3R4L3tuYW1lfV9kb3duX2JwX3NpZ19kb3QucGRmIikKICAgICAgcHAoZmlsZSA9IGJwX2Jhcl9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoYnBfcGxvdHNfZG93bltbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICB9CiAgfQp9CmBgYAoKIyMgVm9sY2FubyBwbG90cyBieSB0aW1lCgojIyMgdF9oZXRfZGxnbgoKYGBge3J9CnRhYmxlX25hbWUgPC0gInRfaGV0X2RsZ24iCnRhYmxlX2lucHV0IDwtIHRpbWVfdGFibGVzW1t0YWJsZV9uYW1lXV0KdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KdF9oZXRfZGxnbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGNvbG9yX2xvdyA9IHRpbWVfY29sb3JzW1sicDA4X2hldF9kbGduIl1dLCBjb2xvcl9oaWdoID0gdGltZV9jb2xvcnNbWyJwMTVfaGV0X2RsZ24iXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIGFscGhhID0gMS4wLAogIHNpemUgPSA0LCBtaW4uc2VnbWVudC5sZW5ndGggPSAwLCBwb2ludC5wYWRkaW5nID0gMC4yKQpwcChmaWxlID0gIjI5dGltZV9tYV92b2xjYW5vX3R4L3RfaGV0X2RsZ25fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKdF9oZXRfZGxnbl92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQp0X2hldF9kbGduX3ZvbGNhbm9bWyJwbG90Il1dCnRfaGV0X2RsZ25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSB0aW1lX2NvbG9yc1tbInAwOF9oZXRfZGxnbiJdXSwgY29sb3JfaGlnaCA9IHRpbWVfY29sb3JzW1sicDE1X2hldF9kbGduIl1dLAogIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgb3V0bGluZSA9IG91dGxpbmUpCnBwKGZpbGUgPSAiMjl0aW1lX21hX3ZvbGNhbm9fdHgvdF9oZXRfZGxnbl9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCnRfaGV0X2RsZ25fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnRfaGV0X2RsZ25fbWFbWyJwbG90Il1dCmBgYAoKIyMjIHRfa29fZGxnbgoKYGBge3J9CnRhYmxlX25hbWUgPC0gInRfa29fZGxnbiIKdGFibGVfaW5wdXQgPC0gdGltZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQp0X2tvX2RsZ25fdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBjb2xvcl9sb3cgPSB0aW1lX2NvbG9yc1tbInAwOF9oZXRfZGxnbiJdXSwgY29sb3JfaGlnaCA9IHRpbWVfY29sb3JzW1sicDE1X2hldF9kbGduIl1dLAogIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBhbHBoYSA9IDEuMCwKICBzaXplID0gNCwgbWluLnNlZ21lbnQubGVuZ3RoID0gMCwgcG9pbnQucGFkZGluZyA9IDAuMikKcHAoZmlsZSA9ICIyOXRpbWVfbWFfdm9sY2Fub190eC90X2tvX2RsZ25fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKdF9rb19kbGduX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnRfa29fZGxnbl92b2xjYW5vW1sicGxvdCJdXQp0X2tvX2RsZ25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSB0aW1lX2NvbG9yc1tbInAwOF9oZXRfZGxnbiJdXSwgY29sb3JfaGlnaCA9IHRpbWVfY29sb3JzW1sicDE1X2hldF9kbGduIl1dLAogIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgb3V0bGluZSA9IG91dGxpbmUpCnBwKGZpbGUgPSAiMjl0aW1lX21hX3ZvbGNhbm9fdHgvdF9rb19kbGduX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKdF9rb19kbGduX21hW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQp0X2tvX2RsZ25fbWFbWyJwbG90Il1dCmBgYAoKIyMjIHRfaGV0X3JldGluYQoKYGBge3J9CnRhYmxlX25hbWUgPC0gInRfaGV0X3JldGluYSIKdGFibGVfaW5wdXQgPC0gdGltZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQp0X2hldF9yZXRpbmFfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBjb2xvcl9sb3cgPSB0aW1lX2NvbG9yc1tbInAwOF9oZXRfcmV0aW5hIl1dLCBjb2xvcl9oaWdoID0gdGltZV9jb2xvcnNbWyJwMTVfaGV0X3JldGluYSJdXSwKICBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgYWxwaGEgPSAxLjAsCiAgc2l6ZSA9IDQsIG1pbi5zZWdtZW50Lmxlbmd0aCA9IDAsIHBvaW50LnBhZGRpbmcgPSAwLjIpCnBwKGZpbGUgPSAiMjl0aW1lX21hX3ZvbGNhbm9fdHgvdF9oZXRfcmV0aW5hX3ZvbGNhbm8ucGRmIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnRfaGV0X3JldGluYV92b2xjYW5vW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQp0X2hldF9yZXRpbmFfdm9sY2Fub1tbInBsb3QiXV0KdF9oZXRfcmV0aW5hX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gdGltZV9jb2xvcnNbWyJwMDhfaGV0X3JldGluYSJdXSwgY29sb3JfaGlnaCA9IHRpbWVfY29sb3JzW1sicDE1X2hldF9yZXRpbmEiXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBvdXRsaW5lID0gb3V0bGluZSkKcHAoZmlsZSA9ICIyOXRpbWVfbWFfdm9sY2Fub190eC90X2hldF9yZXRpbmFfbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQp0X2hldF9yZXRpbmFfbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnRfaGV0X3JldGluYV9tYVtbInBsb3QiXV0KYGBgCgojIyMgdF9rb19yZXRpbmEKCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJ0X2tvX3JldGluYSIKdGFibGVfaW5wdXQgPC0gdGltZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQp0X2tvX3JldGluYV92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGNvbG9yX2xvdyA9IHRpbWVfY29sb3JzW1sicDA4X2hldF9kbGduIl1dLCBjb2xvcl9oaWdoID0gdGltZV9jb2xvcnNbWyJwMTVfaGV0X2RsZ24iXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIGFscGhhID0gMS4wLAogIHNpemUgPSA0LCBtaW4uc2VnbWVudC5sZW5ndGggPSAwLCBwb2ludC5wYWRkaW5nID0gMC4yKQpwcChmaWxlID0gIjI5dGltZV9tYV92b2xjYW5vX3R4L3Rfa29fcmV0aW5hX3ZvbGNhbm8ucGRmIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnRfa29fcmV0aW5hX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnRfa29fcmV0aW5hX3ZvbGNhbm9bWyJwbG90Il1dCnRfa29fcmV0aW5hX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gdGltZV9jb2xvcnNbWyJwMDhfaGV0X2RsZ24iXV0sIGNvbG9yX2hpZ2ggPSB0aW1lX2NvbG9yc1tbInAxNV9oZXRfZGxnbiJdXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIG91dGxpbmUgPSBvdXRsaW5lKQpwcChmaWxlID0gIjI5dGltZV9tYV92b2xjYW5vX3R4L3Rfa29fcmV0aW5hX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKdF9rb19yZXRpbmFfbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnRfa29fcmV0aW5hX21hW1sicGxvdCJdXQpgYGAKCiMjIHRfaGV0X3NjbgoKYGBge3J9CnRhYmxlX25hbWUgPC0gInRfaGV0X3NjbiIKdGFibGVfaW5wdXQgPC0gdGltZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQp0X2hldF9zY25fdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBjb2xvcl9sb3cgPSB0aW1lX2NvbG9yc1tbInAwOF9oZXRfZGxnbiJdXSwgY29sb3JfaGlnaCA9IHRpbWVfY29sb3JzW1sicDE1X2hldF9kbGduIl1dLAogIGxhYmVsX2NvbHVtbiA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBhbHBoYSA9IDEuMCwKICBzaXplID0gNCwgbWluLnNlZ21lbnQubGVuZ3RoID0gMCwgcG9pbnQucGFkZGluZyA9IDAuMikKcHAoZmlsZSA9ICIyOXRpbWVfbWFfdm9sY2Fub190eC90X2hldF9zY25fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKdF9oZXRfc2NuX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnRfaGV0X3Njbl92b2xjYW5vW1sicGxvdCJdXQp0X2hldF9zY25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSB0aW1lX2NvbG9yc1tbInAwOF9oZXRfZGxnbiJdXSwgY29sb3JfaGlnaCA9IHRpbWVfY29sb3JzW1sicDE1X2hldF9kbGduIl1dLAogIHBfY29sID0gImRlc2VxX2FkanAiLCBsYWJlbF9jb2x1bW4gPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgb3V0bGluZSA9IG91dGxpbmUpCnBwKGZpbGUgPSAiMjl0aW1lX21hX3ZvbGNhbm9fdHgvdF9oZXRfc2NuX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKdF9oZXRfc2NuX21hW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQp0X2hldF9zY25fbWFbWyJwbG90Il1dCmBgYAoKIyMgdF9rb19zY24KCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJ0X2tvX3NjbiIKdGFibGVfaW5wdXQgPC0gdGltZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQp0X2tvX3Njbl92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLAogIGNvbG9yX2xvdyA9IHRpbWVfY29sb3JzW1sicDA4X2hldF9kbGduIl1dLCBjb2xvcl9oaWdoID0gdGltZV9jb2xvcnNbWyJwMTVfaGV0X2RsZ24iXV0sCiAgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIGFscGhhID0gMS4wLAogIHNpemUgPSA0LCBtaW4uc2VnbWVudC5sZW5ndGggPSAwLCBwb2ludC5wYWRkaW5nID0gMC4yKQpwcChmaWxlID0gIjI5dGltZV9tYV92b2xjYW5vX3R4L3Rfa29fc2NuX3ZvbGNhbm8ucGRmIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnRfa29fc2NuX3ZvbGNhbm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnRfa29fc2NuX3ZvbGNhbm9bWyJwbG90Il1dCnRfa29fc2NuX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gdGltZV9jb2xvcnNbWyJwMDhfaGV0X2RsZ24iXV0sIGNvbG9yX2hpZ2ggPSB0aW1lX2NvbG9yc1tbInAxNV9oZXRfZGxnbiJdXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIsIGxhYmVsID0gaW50ZXJlc3RpbmdfZ2VuZXMsIG91dGxpbmUgPSBvdXRsaW5lKQpwcChmaWxlID0gIjI5dGltZV9tYV92b2xjYW5vX3R4L3Rfa29fc2NuX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKdF9rb19zY25fbWFbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnRfa29fc2NuX21hW1sicGxvdCJdXQpgYGAKCiMjIyMgUmVwZWF0IHdpdGggdGhlIHN0cmljdCBmaWx0ZXIKCmBgYHtyfQp0aW1lX3N0cmljdF90YWJsZXMgPC0gbGlzdCgpCnRpbWVfc3RyaWN0X3NpZyA8LSBsaXN0KCkKdGltZV9zdHJpY3RfZ3AgPC0gbGlzdCgpCnRpbWVfc3RyaWN0X2NwIDwtIGxpc3QoKQp0aW1lX3N0cmljdF9lbiA8LSBsaXN0KCkKZm9yIChrIGluIHNlcV9hbG9uZyh0aW1lX2tlZXBlcnMpKSB7CiAgbmFtZSA8LSBuYW1lcyh0aW1lX2tlZXBlcnMpW2tdCiAgbWVzc2FnZSgiRXhhbWluaW5nICIsIG5hbWUpCiAga2VlcGVyIDwtIHRpbWVfa2VlcGVyc1tuYW1lXQogIGluY2x1ZGVfbmFtZSA8LSBwYXN0ZTAoImluY18iLCBuYW1lKQogIGluY2x1ZGVfZGZfbmFtZSA8LSBwYXN0ZTAoImRmXyIsIG5hbWUpCiAgaW5jbHVkZV9kZiA8LSB0aW1lX2luY2x1c2lvbnNfc3RyaWN0W1tpbmNsdWRlX2RmX25hbWVdXQogIGluY2x1ZGVzIDwtIHRpbWVfaW5jbHVzaW9uc19zdHJpY3RbW2luY2x1ZGVfbmFtZV1dCiAgZm91bmRfaW5jbHVkZXMgPC0gcm93bmFtZXModGltZV9zaWdfZnVsbFtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pICVpbiUgaW5jbHVkZXMKICBzdW1tYXJ5KGZvdW5kX2luY2x1ZGVzKQogIGlmIChzdW0oZm91bmRfaW5jbHVkZXMpID09IDApIHsKICAgIG5leHQKICB9CiAgaW5jbHVkZV9maWxlbmFtZSA8LSBnbHVlKCIzMHRpbWVfc3RyaWN0X2NvbnRyYXN0c19leGNlbF90eC97bmFtZX1faW5jbHVkaW5nX3d0X3tsZmNfY3V0b2ZmfV9kZWNyZWFzZWRfdGFibGUtdnt2ZXJ9Lnhsc3giKQogIGluY2x1ZGVfc2lnX2ZpbGVuYW1lIDwtIGdsdWUoIjMwdGltZV9zdHJpY3RfY29udHJhc3RzX2V4Y2VsX3R4L3tuYW1lfV9pbmNsdWRpbmdfd3Rfe2xmY19jdXRvZmZ9X2RlY3JlYXNlZF9zaWctdnt2ZXJ9Lnhsc3giKQogIHRpbWVfc3RyaWN0X3RhYmxlc1tbbmFtZV1dIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgdGltZV9kZSwgZXh0cmFfYW5ub3QgPSBpbmNsdWRlX2RmLAogICAga2VlcGVycyA9IGtlZXBlciwgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogICAgZXhjZWwgPSBpbmNsdWRlX2ZpbGVuYW1lLCB3YW50ZWRfZ2VuZXMgPSBpbmNsdWRlcykKICBwcmludCh0aW1lX3N0cmljdF90YWJsZXNbW25hbWVdXSkKICB0aW1lX3N0cmljdF9zaWdbW25hbWVdXSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgdGltZV9zdHJpY3RfdGFibGVzW1tuYW1lXV0sIGFjY29yZGluZ190byA9ICJkZXNlcSIsCiAgICBleGNlbCA9IGluY2x1ZGVfc2lnX2ZpbGVuYW1lKQogIHByaW50KHRpbWVfc3RyaWN0X3NpZ1tbbmFtZV1dKQogIG51bV9yb3dzIDwtIG5yb3codGltZV9zdHJpY3Rfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSArCiAgICBucm93KHRpbWVfc3RyaWN0X3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dKQogIG1lc3NhZ2UoIlRoZXJlIGFyZSAiLCBudW1fcm93cywgIiBzaWduaWZpY2FudCB1cCBhbmQgZG93biBnZW5lcy4iKQogIGlmIChudW1fcm93cyA+PSAxMCkgewogICAgbWVzc2FnZSgiUGVyZm9ybWluZyBncHJvZmlsZXIvY2x1c3RlclByb2ZpbGVyLiIpCiAgICB0aW1lX3N0cmljdF9ncFtbbmFtZV1dIDwtIGFsbF9ncHJvZmlsZXIodGltZV9zdHJpY3Rfc2lnW1tuYW1lXV0sIHNwZWNpZXMgPSAibW11c2N1bHVzIikKICAgIHRpbWVfc3RyaWN0X2NwW1tuYW1lXV0gPC0gYWxsX2Nwcm9maWxlcigKICAgICAgdGltZV9zdHJpY3Rfc2lnW1tuYW1lXV0sIHRpbWVfc3RyaWN0X3RhYmxlc1tbbmFtZV1dLAogICAgICBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBnb19sZXZlbCA9IGdvX2xldmVsLCBvcmdkYl9mcm9tID0gb3JnZGJfZnJvbSwKICAgICAgbWF4X2dyb3Vwc2l6ZSA9IG1heF9ncm91cHNpemUsIG9yZ2FuaXNtID0gIm1vdXNlIikKICAgICNpZiAoIWlzLm51bGwoZ2V0MCgibTJfZ3NjIikpKSB7CiAgICAjICB0aW1lX3N0cmljdF9lbltbbmFtZV1dIDwtIGFsbF9lbnJpY2hlcih0aW1lX3N0cmljdF9zaWdbW25hbWVdXSwgZ3NjID0gbTJfZ3NjLAogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnZGIgPSAib3JnLk1tLmVnLmRiIiwgZnJvbSA9ICJFTlNFTUJMIiwgdG8gPSAiU1lNQk9MIikKICAgICN9CiAgICBncF93cml0dGVuIDwtIHdyaXRlX2FsbF9ncCh0aW1lX3N0cmljdF9ncFtbbmFtZV1dLCBwcmVmaXggPSAiMzEiLCBzdWZmaXggPSAic3RyaWN0IikKICAgIGNwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2NwKHRpbWVfc3RyaWN0X2NwW1tuYW1lXV0sIHByZWZpeCA9ICIzMSIsIHN1ZmZpeCA9ICJzdHJpY3QiKQogICAgI2VuX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2VuKHRpbWVfc3RyaWN0X2VuW1tuYW1lXV0pCiAgfSBlbHNlIHsKICAgIHdhcm5pbmcoIlRoZXJlIGFyZSBsZXNzIHRoYW4gMTAgZ2VuZXMgdXAgYW5kIGRvd24gaW4gdGhlICIsIG5hbWUsICIgY29tcGFyaXNvbi4iKQogICAgbWVzc2FnZSgiVGhlcmUgYXJlIGxlc3MgdGhhbiAxMCBnZW5lcyB1cCBhbmQgZG93biBpbiB0aGUgIiwgbmFtZSwgIiBjb21wYXJpc29uLiIpCiAgfQp9CmBgYAoKU2VuZCB0aGUgcGxvdHMgc2VwYXJhdGVseS4KCmBgYHtyfQpmb3IgKGsgaW4gc2VxX2Fsb25nKHRpbWVfa2VlcGVycykpIHsKICBuYW1lIDwtIG5hbWVzKHRpbWVfa2VlcGVycylba10KICBtZXNzYWdlKCJFeGFtaW5pbmcgIiwgbmFtZSkKICBrZWVwZXIgPC0gdGltZV9rZWVwZXJzW25hbWVdCiAgaW5jbHVkZXMgPC0gdGltZV9pbmNsdXNpb25zW1tuYW1lXV0KICBpbmNsdWRlX25hbWUgPC0gcGFzdGUwKCJpbmNfIiwgbmFtZSkKICBpbmNsdWRlX2RmX25hbWUgPC0gcGFzdGUwKCJkZl8iLCBuYW1lKQogIGluY2x1ZGVfZGYgPC0gdGltZV9pbmNsdXNpb25zW1tpbmNsdWRlX2RmX25hbWVdXQogIGluY2x1ZGVzIDwtIHRpbWVfaW5jbHVzaW9uc1tbaW5jbHVkZV9uYW1lXV0KICBudW1fcm93cyA8LSBucm93KHRpbWVfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSArCiAgICBucm93KHRpbWVfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0pCiAgbWVzc2FnZSgiVGhlcmUgYXJlICIsIG51bV9yb3dzLCAiIHNpZ25pZmljYW50IHVwIGFuZCBkb3duIGdlbmVzLiIpCiAgbnVtX29iamVjdHMgPC0gbGVuZ3RoKHRpbWVfY3BbW25hbWVdXSkKICBpZiAobnVtX29iamVjdHMgPT0gMCkgewogICAgd2FybmluZygiU29tZXRoaW5nIGZhaWxlZCBpbiBhbGxfY3Byb2ZpbGVyLiIpCiAgfSBlbHNlIHsKICAgIHVwcCA8LSB3aGljaChncmVwbCh4ID0gbmFtZXModGltZV9jcFtbbmFtZV1dKSwgcGF0dGVybiA9ICJfdXAkIikpCiAgICBkb3ducCA8LSB3aGljaChncmVwbCh4ID0gbmFtZXModGltZV9jcFtbbmFtZV1dKSwgcGF0dGVybiA9ICJfZG93biQiKSkKICAgIGlmIChsZW5ndGgodXBwKSA+IDApIHsKICAgICAgbWZfc2lnIDwtIHRpbWVfY3BbW25hbWVdXVtbdXBwXV1bWyJnb19kYXRhIl1dW1siTUZfZW5yaWNoIl1dCiAgICAgIGNjX3NpZyA8LSB0aW1lX2NwW1tuYW1lXV1bW3VwcF1dW1siZ29fZGF0YSJdXVtbIkNDX2VucmljaCJdXQogICAgICBicF9zaWcgPC0gdGltZV9jcFtbbmFtZV1dW1t1cHBdXVtbImdvX2RhdGEiXV1bWyJCUF9lbnJpY2giXV0KICAgICAgbWZfcGxvdHNfdXAgPC0gcGxvdF9lbnJpY2hyZXN1bHQobWZfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBtZl90cmVlX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjMyY3BfdHJlZXNfdHgve25hbWV9X3VwX21mX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl90cmVlX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfdXBbWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgbWZfYmFyX3VwX2ZpbGVuYW1lIDwtIGdsdWUoIjMyY3BfYmFyX3R4L3tuYW1lfV91cF9tZl9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl9iYXJfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChtZl9wbG90c191cFtbImRvdCJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGNjX3Bsb3RzX3VwIDwtIHBsb3RfZW5yaWNocmVzdWx0KGNjX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgY2NfdHJlZV91cF9maWxlbmFtZSA8LSBnbHVlKCIzMmNwX3RyZWVzX3R4L3tuYW1lfV91cF9jY19zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gY2NfdHJlZV91cF9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX3VwW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGNjX2Jhcl91cF9maWxlbmFtZSA8LSBnbHVlKCIzMmNwX2Jhcl90eC97bmFtZX1fdXBfY2Nfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gY2NfYmFyX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoY2NfcGxvdHNfdXBbWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX3RyZWVfdXBfZmlsZW5hbWUgPC0gZ2x1ZSgiMzJjcF90cmVlc190eC97bmFtZX1fdXBfYnBfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGJwX3RyZWVfdXBfZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c191cFtbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBicF9wbG90c191cCA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX2Jhcl91cF9maWxlbmFtZSA8LSBnbHVlKCIzMmNwX2Jhcl90eC97bmFtZX1fdXBfYnBfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gYnBfYmFyX3VwX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQoYnBfcGxvdHNfdXBbWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgfQogICAgaWYgKGxlbmd0aChkb3ducCkgPiAwKSB7CiAgICAgIG1mX3NpZyA8LSB0aW1lX2NwW1tuYW1lXV1bW2Rvd25wXV1bWyJnb19kYXRhIl1dW1siTUZfZW5yaWNoIl1dCiAgICAgIGNjX3NpZyA8LSB0aW1lX2NwW1tuYW1lXV1bW2Rvd25wXV1bWyJnb19kYXRhIl1dW1siQ0NfZW5yaWNoIl1dCiAgICAgIGJwX3NpZyA8LSB0aW1lX2NwW1tuYW1lXV1bW2Rvd25wXV1bWyJnb19kYXRhIl1dW1siQlBfZW5yaWNoIl1dCiAgICAgIG1mX3Bsb3RzX2Rvd24gPC0gcGxvdF9lbnJpY2hyZXN1bHQobWZfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBtZl90cmVlX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMzJjcF90cmVlc190eC97bmFtZX1fZG93bl9tZl9zaWdfdHJlZS5wZGYiKQogICAgICBwcChmaWxlID0gbWZfdHJlZV9kb3duX2ZpbGVuYW1lKQogICAgICB0cnkocHJpbnQobWZfcGxvdHNfZG93bltbInRyZWUiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBtZl9iYXJfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIzMmNwX2Jhcl90eC97bmFtZX1fZG93bl9tZl9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBtZl9iYXJfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KG1mX3Bsb3RzX2Rvd25bWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgICBjY19wbG90c19kb3duIDwtIHBsb3RfZW5yaWNocmVzdWx0KGNjX3NpZywgc2hvd0NhdGVnb3J5ID0gZ29fY2F0ZWdvcmllcykKICAgICAgY2NfdHJlZV9kb3duX2ZpbGVuYW1lIDwtIGdsdWUoIjMyY3BfdHJlZXNfdHgve25hbWV9X2Rvd25fY2Nfc2lnX3RyZWUucGRmIikKICAgICAgcHAoZmlsZSA9IGNjX3RyZWVfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGNjX3Bsb3RzX2Rvd25bWyJ0cmVlIl1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgY2NfYmFyX2Rvd25fZmlsZW5hbWUgPC0gZ2x1ZSgiMzJjcF9iYXJfdHgve25hbWV9X2Rvd25fY2Nfc2lnX2RvdC5wZGYiKQogICAgICBwcChmaWxlID0gY2NfYmFyX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChjY19wbG90c19kb3duW1siZG90Il1dKSwgc2lsZW50ID0gVFJVRSkKICAgICAgcGxvdHRlZCA8LSBkZXYub2ZmKCkKICAgICAgYnBfcGxvdHNfZG93biA8LSBwbG90X2VucmljaHJlc3VsdChicF9zaWcsIHNob3dDYXRlZ29yeSA9IGdvX2NhdGVnb3JpZXMpCiAgICAgIGJwX3RyZWVfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIzMmNwX3RyZWVzX3R4L3tuYW1lfV9kb3duX2JwX3NpZ190cmVlLnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF90cmVlX2Rvd25fZmlsZW5hbWUpCiAgICAgIHRyeShwcmludChicF9wbG90c19kb3duW1sidHJlZSJdXSksIHNpbGVudCA9IFRSVUUpCiAgICAgIHBsb3R0ZWQgPC0gZGV2Lm9mZigpCiAgICAgIGJwX3Bsb3RzX2Rvd24gPC0gcGxvdF9lbnJpY2hyZXN1bHQoYnBfc2lnLCBzaG93Q2F0ZWdvcnkgPSBnb19jYXRlZ29yaWVzKQogICAgICBicF9iYXJfZG93bl9maWxlbmFtZSA8LSBnbHVlKCIzMmNwX2Jhcl90eC97bmFtZX1fZG93bl9icF9zaWdfZG90LnBkZiIpCiAgICAgIHBwKGZpbGUgPSBicF9iYXJfZG93bl9maWxlbmFtZSkKICAgICAgdHJ5KHByaW50KGJwX3Bsb3RzX2Rvd25bWyJkb3QiXV0pLCBzaWxlbnQgPSBUUlVFKQogICAgICBwbG90dGVkIDwtIGRldi5vZmYoKQogICAgfQogIH0KfQpgYGAKCiMgVHJhbnNsYXRvbWUgcXVlcmllcwoKSW4gY29udmVyc2F0aW9uIHdpdGggQ29sZW5zbywgaGUgc3Bva2UgYWJvdXQgYSBzZXJpZXMgb2YgY29udHJhc3RzCndoaWNoIHdvdWxkIGJlIGludGVyZXN0aW5nIHRvIGF0dGVtcHQgaW4gb3JkZXIgdG8gcXVlcnkgdGhlIGNoYW5nZXMKYWNyb3NzIGJvdGggbG9jYXRpb25zIGFuZCBnZW5vdHlwZXMgYW5kL29yIGJvdGggbG9jYXRpb25zIGFuZCB0aW1lLAp0aHVzOgoKKHAwOF9oZXRfc2NuIC8gcDA4X2hldF9yZXRpbmEpIC8gKHAwOF9rb19zY24gLyBwMDhfa29fcmV0aW5hKQoKYXMgYW4gZXhhbXBsZS4gIFdlIGNhbiBkZWZpbml0ZWx5IGRvIHRoZXNlLCBidXQgdGhleSBkbyBub3Qgd29yayBmb3IKYWxsIG1ldGhvZHMgZW1wbG95ZWQgKEkgdGhpbmsgdGhleSB3b3JrIGJlc3Qgd2l0aCBsaW1tYSBhbmQgZWRnZVIpLgoKTGV0cyBmaW5kIG91dCEKCiMjIFR3byBzY24vcmV0aW5hIGNvbXBhcmlzb25zCgoqIChwMDhfaGV0X3NjbiAvIHAwOF9oZXRfcmV0aW5hKSAvIChwMDhfa29fc2NuIC8gcDA4X2tvX3JldGluYSkKKiAocDE1X2hldF9zY24gLyBwMTVfaGV0X3JldGluYSkgLyAocDE1X2tvX3NjbiAvIHAxNV9rb19yZXRpbmEpCgpgYGB7cn0Kc2NuX2V4dHJhIDwtIGdsdWUoIlxcCiAgcDA4aGV0ID0gKGNvbmRpdGlvbnAwOF9oZXRfc2NuIC0gY29uZGl0aW9ucDA4X2hldF9yZXRpbmEpLCBcXAogIHAwOGtvID0gKGNvbmRpdGlvbnAwOF9rb19zY24gLSBjb25kaXRpb25wMDhfa29fcmV0aW5hKSwgXFwKICBwMDhoZXRfdnNfcDA4a28gPSAoY29uZGl0aW9ucDA4X2hldF9zY24gLSBjb25kaXRpb25wMDhfaGV0X3JldGluYSkgLSAoY29uZGl0aW9ucDA4X2tvX3NjbiAtIGNvbmRpdGlvbnAwOF9rb19yZXRpbmEpLCBcXAogIHAxNWhldCA9IChjb25kaXRpb25wMTVfaGV0X3NjbiAtIGNvbmRpdGlvbnAxNV9oZXRfcmV0aW5hKSwgXFwKICBwMTVrbyA9IChjb25kaXRpb25wMTVfa29fc2NuIC0gY29uZGl0aW9ucDE1X2tvX3JldGluYSksIFxcCiAgcDE1aGV0X3ZzX3AxNWtvID0gKGNvbmRpdGlvbnAxNV9oZXRfc2NuIC0gY29uZGl0aW9ucDE1X2hldF9yZXRpbmEpIC0gKGNvbmRpdGlvbnAxNV9rb19zY24gLSBjb25kaXRpb25wMTVfa29fcmV0aW5hKSIpCnNjbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzIDwtIGxpc3QoCiAgInAwOGhldCIgPSBjKCJwMDhfaGV0X3NjbiIsICJwMDhfaGV0X3JldGluYSIpLAogICJwMDhrbyIgPSBjKCJwMDhfa29fc2NuIiwgInAwOF9rb19yZXRpbmEiKSwKICAicDE1aGV0IiA9IGMoInAxNV9oZXRfc2NuIiwgInAxNV9oZXRfcmV0aW5hIiksCiAgInAxNWtvIiA9IGMoInAxNV9rb19zY24iLCAicDE1X2tvX3JldGluYSIpKQpzY25fdHJhbnNsYXRvbWVfa2VlcGVycyA8LSBsaXN0KAogICJwMDhoZXQiID0gYygicDA4X2hldF9zY24iLCAicDA4X2hldF9yZXRpbmEiKSwKICAicDA4a28iID0gYygicDA4X2tvX3NjbiIsICJwMDhfa29fcmV0aW5hIiksCiAgInAwOF9zY25fdHJhbnNsYXRvbWUiID0gYygicDA4aGV0IiwgInAwOGtvIiksCiAgInAxNWhldCIgPSBjKCJwMTVfaGV0X3NjbiIsICJwMTVfaGV0X3JldGluYSIpLAogICJwMTVrbyIgPSBjKCJwMTVfa29fc2NuIiwgInAxNV9rb19yZXRpbmEiKSwKICAicDE1X3Njbl90cmFuc2xhdG9tZSIgPSBjKCJwMTVoZXQiLCAicDE1a28iKSkKZmlsdCA8LSBub3JtYWxpemUodHhfcGFpcndpc2VfaW5wdXQsIGZpbHRlciA9IFRSVUUpCmxpbW1hX3Rlc3QgPC0gbGltbWFfcGFpcndpc2UoZmlsdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwZXJzID0gc2NuX3RyYW5zbGF0b21lX2RlX2tlZXBlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfZnN0cmluZyA9ICJ+IDAgKyBjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX3N2cyA9IEZBTFNFLCBleHRyYV9jb250cmFzdHJzID0gc2NuX2V4dHJhKQplZGdlcl90ZXN0IDwtIGVkZ2VyX3BhaXJ3aXNlKGZpbHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2VlcGVycyA9IHNjbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2ZzdHJpbmcgPSAifiAwICsgY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9zdnMgPSBGQUxTRSwgZXh0cmFfY29udHJhc3RzID0gc2NuX2V4dHJhKQpzY25fdHJhbnNsYXRvbWVfZGUgPC0gYWxsX3BhaXJ3aXNlKHR4X3BhaXJ3aXNlX2lucHV0LCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBzY25fdHJhbnNsYXRvbWVfZGVfa2VlcGVycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9zdnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9mc3RyaW5nID0gIn4gMCArIGNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fYmFzaWMgPSBGQUxTRSwgZG9fZHJlYW0gPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19ub2lzZXEgPSBGQUxTRSwgZG9fZWJzZXEgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRyYV9jb250cmFzdHMgPSBzY25fZXh0cmEpCnNjbl9jb21iaW5lZF90ZXN0IDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHNjbl90cmFuc2xhdG9tZV9kZSwga2VlcGVycyA9IHNjbl90cmFuc2xhdG9tZV9rZWVwZXJzLAogIGV4Y2VsID0gZ2x1ZSgiMzN0cmFuc2xhdG9tZV94bHN4X3R4L3Rlc3Rfc2NuX3RyYW5zbGF0b21lX3VuZmlsdGVyZWRfbm9zdmEtdnt2ZXJ9Lnhsc3giKSkKc2NuX3RyYW5zbGF0b21lX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodHhfcGFpcndpc2VfaW5wdXQsIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBzY25fdHJhbnNsYXRvbWVfZGVfa2VlcGVycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfc3ZzID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2ZzdHJpbmcgPSAifiAwICsgY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fYmFzaWMgPSBGQUxTRSwgZG9fZHJlYW0gPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fbm9pc2VxID0gRkFMU0UsIGRvX2Vic2VxID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2NvbnRyYXN0cyA9IHNjbl9leHRyYSkKc2NuX2NvbWJpbmVkX3Rlc3Rfc3ZhIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHNjbl90cmFuc2xhdG9tZV9kZV9zdmEsIGtlZXBlcnMgPSBzY25fdHJhbnNsYXRvbWVfa2VlcGVycywKICBleGNlbCA9IGdsdWUoIjMzdHJhbnNsYXRvbWVfeGxzeF90eC90ZXN0X3Njbl90cmFuc2xhdG9tZV91bmZpbHRlcmVkX3N2YS12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIyBTdWJ0cmFjdGluZyBERVNlcTIgcmVzdWx0czogcDA4IHNjbiBoZXQgdnMga28KCmBgYHtyfQpwMDhfc2NuX2NvbWJpbmVkX2Rlc2VxIDwtIHN1YnRyYWN0X2RlX3Jlc3VsdHMoCiAgZmlyc3RfdGFibGUgPSBzY25fY29tYmluZWRfdGVzdFtbImRhdGEiXV1bWyJwMDhoZXQiXV0sCiAgc2Vjb25kX3RhYmxlID0gc2NuX2NvbWJpbmVkX3Rlc3RbWyJkYXRhIl1dW1sicDA4a28iXV0sCiAgZmlyc3RfbGZjID0gImRlc2VxX2xvZ2ZjIiwgc2Vjb25kX2xmYyA9ICJkZXNlcV9sb2dmYyIsCiAgZmlyc3RfcCA9ICJkZXNlcV9hZGpwIiwgc2Vjb25kX3AgPSAiZGVzZXFfYWRqcCIsCiAgZmlyc3RfbmFtZSA9ICJoZXQiLCBzZWNvbmRfbmFtZSA9ICJrbyIsCiAgZXhjZWwgPSBnbHVlKCIzM3RyYW5zbGF0b21lX3hsc3hfdHgvdHJhbnNsYXRvbWVfcDA4X3Njbl9jb21iaW5lZF9kZXNlcS12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIyBTdWJ0cmFjdGluZyBERVNlcTIgcmVzdWx0czogcDE1IHNjbiBoZXQgdnMga28KCmBgYHtyfQpwMTVfc2NuX2NvbWJpbmVkX2Rlc2VxIDwtIHN1YnRyYWN0X2RlX3Jlc3VsdHMoCiAgZmlyc3RfdGFibGUgPSBzY25fY29tYmluZWRfdGVzdFtbImRhdGEiXV1bWyJwMTVoZXQiXV0sCiAgc2Vjb25kX3RhYmxlID0gc2NuX2NvbWJpbmVkX3Rlc3RbWyJkYXRhIl1dW1sicDE1a28iXV0sCiAgZmlyc3RfbGZjID0gImRlc2VxX2xvZ2ZjIiwgc2Vjb25kX2xmYyA9ICJkZXNlcV9sb2dmYyIsCiAgZmlyc3RfcCA9ICJkZXNlcV9hZGpwIiwgc2Vjb25kX3AgPSAiZGVzZXFfYWRqcCIsCiAgZmlyc3RfbmFtZSA9ICJoZXQiLCBzZWNvbmRfbmFtZSA9ICJrbyIsCiAgZXhjZWwgPSBnbHVlKCIzNHRyYW5zbGF0b21lX2Rlc2Vxc3ViX3hsc3hfdHgvdHJhbnNsYXRvbWVfcDE1X3Njbl9jb21iaW5lZF9kZXNlcS12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIE9uZSBkbGduL3JldGluYSBjb21wYXJpc29uCgoqIChwMDhfaGV0X2RsZ24gLyBwMDhfaGV0X3JldGluYSkgLyAocDA4X2tvX2RsZ24gLyBwMDhfa29fcmV0aW5hKQoKYGBge3J9CnAwOF9kbGduX2V4dHJhIDwtICJwMDhoZXRfdnNfcDA4a28gPSAoY29uZGl0aW9ucDA4X2hldF9kbGduIC0gY29uZGl0aW9ucDA4X2hldF9yZXRpbmEpIC0gKGNvbmRpdGlvbnAwOF9rb19kbGduIC0gY29uZGl0aW9ucDA4X2tvX3JldGluYSkiCnAwOF9kbGduX3RyYW5zbGF0b21lX2RlX2tlZXBlcnMgPC0gbGlzdCgKICAicDA4aGV0IiA9IGMoInAwOF9oZXRfZGxnbiIsICJwMDhfaGV0X3JldGluYSIpLAogICJwMDhrbyIgPSBjKCJwMDhfa29fZGxnbiIsICJwMDhfa29fcmV0aW5hIikpCnAwOF9kbGduX3RyYW5zbGF0b21lX2tlZXBlcnMgPC0gbGlzdCgKICAicDA4X2hldF9kbGduX3ZzX3JldGluYSIgPSBjKCJwMDhfaGV0X2RsZ24iLCAicDA4X2hldF9yZXRpbmEiKSwKICAicDA4X2tvX2RsZ25fdnNfcmV0aW5hIiA9IGMoInAwOF9rb19kbGduIiwgInAwOF9rb19yZXRpbmEiKSwKICAicDA4X2RsZ25fdHJhbnNsYXRvbWUiID0gYygicDA4aGV0IiwgInAwOGtvIikpCnAwOF9kbGduX3RyYW5zbGF0b21lX2RlIDwtIGFsbF9wYWlyd2lzZSh0eF9wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfc3ZzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9mc3RyaW5nID0gIn4gMCArIGNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19iYXNpYyA9IEZBTFNFLCBkb19kcmVhbSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fbm9pc2VxID0gRkFMU0UsIGRvX2Vic2VxID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRyYV9jb250cmFzdHMgPSBwMDhfZGxnbl9leHRyYSkKcDA4X2RsZ25fY29tYmluZWRfdGVzdCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBwMDhfZGxnbl90cmFuc2xhdG9tZV9kZSwga2VlcGVycyA9IHAwOF9kbGduX3RyYW5zbGF0b21lX2tlZXBlcnMsCiAgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgiMzN0cmFuc2xhdG9tZV94bHN4X3R4L3Rlc3RfcDA4X2RsZ25fdHJhbnNsYXRvbWVfdW5maWx0ZXJlZF9ub3N2YS12e3Zlcn0ueGxzeCIpKQpwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHR4X3BhaXJ3aXNlX2lucHV0LCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX3N2cyA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2ZzdHJpbmcgPSAifiAwICsgY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19iYXNpYyA9IEZBTFNFLCBkb19kcmVhbSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX25vaXNlcSA9IEZBTFNFLCBkb19lYnNlcSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2NvbnRyYXN0cyA9IHAwOF9kbGduX2V4dHJhKQpwMDhfZGxnbl9jb21iaW5lZF90ZXN0X3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9zdmEsIGtlZXBlcnMgPSBwMDhfZGxnbl90cmFuc2xhdG9tZV9rZWVwZXJzLAogIGxhYmVsX2NvbHVtbiA9IGxhYmVsX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoIjMzdHJhbnNsYXRvbWVfeGxzeF90eC90ZXN0X3AwOF9kbGduX3RyYW5zbGF0b21lX3VuZmlsdGVyZWRfc3ZhLXZ7dmVyfS54bHN4IikpCmBgYAoKIyMjIFN1YnRyYWN0aW5nIHRoZSBERVNlcTIgcmVzdWx0cwoKIyMgVHdvIHNjbi9yZXRpbmEgY29tcGFyaXNvbnMgKHAxNS9wMDggYWNyb3NzIGhldC9rbykKCiogKHAxNV9oZXRfc2NuIC8gcDE1X2hldF9yZXRpbmEpIC8gKHAwOF9oZXRfc2NuIC8gcDA4X2hldF9yZXRpbmEpCiogKHAxNV9rb19zY24gLyBwMTVfa29fcmV0aW5hKSAvIChwMDhfa29fc2NuIC8gcDA4X2tvX3JldGluYSkKCmBgYHtyfQp0aW1lX3Njbl9leHRyYSA8LSBnbHVlKCJcXAogIHAxNWhldCA9IChjb25kaXRpb25wMTVfaGV0X3NjbiAtIGNvbmRpdGlvbnAxNV9oZXRfcmV0aW5hKSwgXFwKICBwMDhoZXQgPSAoY29uZGl0aW9ucDA4X2hldF9zY24gLSBjb25kaXRpb25wMDhfaGV0X3JldGluYSksIFxcCiAgcDE1aGV0X3ZzX3AwOGhldCA9IChjb25kaXRpb25wMTVfaGV0X3NjbiAtIGNvbmRpdGlvbnAxNV9oZXRfcmV0aW5hKSAtIChjb25kaXRpb25wMDhfaGV0X3NjbiAtIGNvbmRpdGlvbnAwOF9oZXRfcmV0aW5hKSwKICBwMTVrbyA9IChjb25kaXRpb25wMTVfa29fc2NuIC0gY29uZGl0aW9ucDE1X2tvX3JldGluYSksIFxcCiAgcDA4a28gPSAoY29uZGl0aW9ucDA4X2tvX3NjbiAtIGNvbmRpdGlvbnAwOF9rb19yZXRpbmEpLCBcXAogIHAxNWtvX3ZzX3AwOGtvID0gKGNvbmRpdGlvbnAxNV9rb19zY24gLSBjb25kaXRpb25wMTVfa29fcmV0aW5hKSAtIChjb25kaXRpb25wMDhfa29fc2NuIC0gY29uZGl0aW9ucDA4X2tvX3JldGluYSkiKQp0aW1lX3Njbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzIDwtIGxpc3QoCiAgInAxNWhldCIgPSBjKCJwMTVfaGV0X3NjbiIsICJwMTVfaGV0X3JldGluYSIpLAogICJwMDhoZXQiID0gYygicDA4X2hldF9zY24iLCAicDA4X2hldF9yZXRpbmEiKSwKICAicDE1a28iID0gYygicDE1X2tvX3NjbiIsICJwMTVfa29fcmV0aW5hIiksCiAgInAwOGtvIiA9IGMoInAwOF9rb19zY24iLCAicDA4X2tvX3JldGluYSIpKQp0aW1lX3Njbl90cmFuc2xhdG9tZV9rZWVwZXJzIDwtIGxpc3QoCiAgInAxNWhldCIgPSBjKCJwMTVfaGV0X3NjbiIsICJwMTVfaGV0X3JldGluYSIpLAogICJwMDhoZXQiID0gYygicDA4X2hldF9zY24iLCAicDA4X2hldF9yZXRpbmEiKSwKICAicDE1a28iID0gYygicDE1X2tvX3NjbiIsICJwMTVfa29fcmV0aW5hIiksCiAgInAwOGtvIiA9IGMoInAwOF9rb19zY24iLCAicDA4X2tvX3JldGluYSIpLAogICJwMTVfaGV0X3NjX3ZzX3JldGluYSIgPSBjKCJwMTVfaGV0X3NjbiIsICJwMTVfaGV0X3JldGluYSIpLAogICJwMDhfaGV0X3NjX3ZzX3JldGluYSIgPSBjKCJwMDhfaGV0X3NjbiIsICJwMDhfaGV0X3JldGluYSIpLAogICJzY25faGV0X3RyYW5zbGF0b21lIiA9IGMoInAxNWhldCIsICJwMDhoZXQiKSwKICAic2NuX2tvX3RyYW5zbGF0b21lIiA9IGMoInAxNWtvIiwgInAwOGtvIikpCnRpbWVfc2NuX3RyYW5zbGF0b21lX2RlIDwtIGFsbF9wYWlyd2lzZSh0eF9wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSB0aW1lX3Njbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfc3ZzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9mc3RyaW5nID0gIn4gMCArIGNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19iYXNpYyA9IEZBTFNFLCBkb19kcmVhbSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fbm9pc2VxID0gRkFMU0UsIGRvX2Vic2VxID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRyYV9jb250cmFzdHMgPSB0aW1lX3Njbl9leHRyYSkKdGltZV9zY25fdHJhbnNsYXRvbWVfdGVzdCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0aW1lX3Njbl90cmFuc2xhdG9tZV9kZSwKICBrZWVwZXJzID0gdGltZV9zY25fdHJhbnNsYXRvbWVfa2VlcGVycywKICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCIzM3RyYW5zbGF0b21lX3hsc3hfdHgvdGVzdF90aW1lX3Njbl90cmFuc2xhdG9tZV91bmZpbHRlcmVkX25vc3ZhLXZ7dmVyfS54bHN4IikpCnRpbWVfc2NuX3RyYW5zbGF0b21lX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodHhfcGFpcndpc2VfaW5wdXQsIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2VlcGVycyA9IHRpbWVfc2NuX3RyYW5zbGF0b21lX2RlX2tlZXBlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfc3ZzID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfZnN0cmluZyA9ICJ+IDAgKyBjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX2Jhc2ljID0gRkFMU0UsIGRvX2RyZWFtID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fbm9pc2VxID0gRkFMU0UsIGRvX2Vic2VxID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXh0cmFfY29udHJhc3RzID0gdGltZV9zY25fZXh0cmEpCnRpbWVfc2NuX3RyYW5zbGF0b21lX3Rlc3Rfc3ZhIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRpbWVfc2NuX3RyYW5zbGF0b21lX2RlX3N2YSwKICBrZWVwZXJzID0gdGltZV9zY25fdHJhbnNsYXRvbWVfa2VlcGVycywKICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCIzM3RyYW5zbGF0b21lX3hsc3hfdHgvdGVzdF90aW1lX3Njbl90cmFuc2xhdG9tZV91bmZpbHRlcmVkX3N2YS12e3Zlcn0ueGxzeCIpKQpgYGAKCk5leHQgc3RlcDogUGVyZm9ybSB0aGUgcmV0aW5hIGZpbHRlcjsgbmVlZCB0byB0aGluayBhYm91dCB0aGUgcHJvcGVyCnVuaW9uL2ludGVyc2VjdGlvbiBvZiB0aGUgcmV0aW5hL3ggZXhwcmVzc2lvbiB2YWx1ZXMKCkluIHRoZSBwcmV2aW91cyBibG9jaywgd2UgYXJlIG1ha2luZyAyIGdsb2JhbCBjb21wYXJpc29ucywgaGVyZSBpcyBvbmUKb2YgdGhlbToKCihwMTVoZXRzY24vcDE1aGV0cmV0KS8ocDA4aGV0c2NuL3AwOGhldHJldCkKCkkgdGhlcmVmb3JlIHdhbnQgdG8gZXh0cmFjdCB0aGUgbW9zdCBsb2dpY2FsIHNldCBvZiBnZW5lcyBoaWdoZXIgaW4Kc29tZS9hbGwgb2YgdGhlc2UgY29uZGl0aW9ucyB3aXRoIHJlc3BlY3QgdG8gdGhlIGNvcnJlc3BvbmRpbmcgd3QKY29uZGl0aW9ucy4gIFByZXZpb3VzbHksIGluIHNlY3Rpb24gJ0V4dHJhY3QgZ2VuZXMgaW5jbHVkZWQgZm9yIGVhY2gKc2V0IG9mIGNvbnRyYXN0cycsIEkgYXR0ZW1wdGVkIHRvIHBlcmZvcm0gdGhpcyBvcGVyYXRpb24gZm9yIDIKc3BlY2lmaWMgd3QgY29uZGl0aW9ucy4gIFdoZW4gdGhpcyB3YXMgcGVyZm9ybWVkLCBpdCB0b29rIHRoZQp1bmlxdWUodW5pb24pIG9mIHRoZSB0d28gc2V0cy4gIFRodXMgaXQgc3RhbmRzIHRvIHJlYXNvbiB0aGF0IEkgd2FudAp0byB0YWtlIHRoZSB1bmlxdWUodW5pb24pIG9mIGFsbCA0IGluIHRoaXMgaW5zdGFuY2U/ICBlLmcuOgoKKHAxNWhldHNjbiA+IHAxNXd0c2NuKSB8IChwMTVoZXRyZXQgPiBwMTV3dHJldCkgfAogIChwMDhoZXRzY24gPiBwMDh3dHNjbikgfCAocDA4aGV0cmV0ID4gcDA4d3RyZXQpCgpJIGtpbmQgb2YgdGhpbmsgaXQgc2hvdWxkIGJlOgoKKChwMTVoZXRzY24gPiBwMTV3dHNjbikgfCAocDE1aGV0cmV0ID4gcDE1d3RyZXQpKSAmCiAgKChwMDhoZXRzY24gPiBwMDh3dHNjbikgfCAocDA4aGV0cmV0ID4gcDA4d3RyZXQpKQoKZ3Jvc3MsIHBlcmhhcHMgSSBzaG91bGQganVzdCBkbyB0aGlzIG1hbnVhbGx5LCBnaXZlbiB0aGF0IHRoZXJlIGFyZQpvbmx5IGEgZmV3IHB1dGF0aXZlIHRyYW5zbGF0b21lcyB0byBxdWVyeT8KCiMgUXVpY2sgYW5kIGRpcnR5IERFU2VxMiBjb250cmFzdCBvZiBjb250cmFzdHMKCkluIGEgZmFzaGlvbiBzaW1pbGFyIHRvIGhvdyBIZWN0b3IgaGFuZGxlZCB0aGUgZWZmZWN0IG9mIHBoYWdvY3l0b3Npcwp3aXRoIExhdXJhIGFuZCBOYWppYiBhIGxvbmcgdGltZSBhZ28sIEkgcHJvcG9zZSB0byBkbyBhIHNpbXBsZQpzdWJ0cmFjdGlvbiBvZiB0aGUgcmVzdWx0cyBvZiBvdXIgdHdvIGNvbnRyYXN0cyB3aGljaCBjb21wcmlzZSB0aGUKdHJhbnNsYXRvbWUgcXVlcnkgKEkgd2FzIHRoaW5raW5nIGFib3V0IHRoaXMgbGFzdCB3ZWVrLCB0aHVzIHRoZQppbmNsdXNpb24gb2YgdGhlbSBpbiB0aGUgZGUgdGFibGVzIGFib3ZlKS4gIFNpbWlsYXJseSB0byB0aGUKcGhhZ29jeXRvc2lzIGVmZmVjdCwgSSB3aWxsIHNpbXBseSB0YWtlIHRoZSB3b3JzdCBwb3NpYmxlIGFkanVzdGVkCnAtdmFsdWUuICBJIHdpbGwgcmVwZWF0IHRoaXMgd2l0aCBsaW1tYS9FZGdlUiBhbmQgc2VlIGhvdyBzaW1pbGFyIHRoZQpmaW5hbCByZXN1bHRzIGFyZSB0byB3aGF0IHRob3NlIG1ldGhvZHMgcHJvdmlkZSBpbiB0aGUgKGEvYikvKGMvZCkKY29tcGFyaXNvbnMuICBJIGFtIHJlYXNvbmFibHkgY2VydGFpbiB0aGF0IERFU2VxMidzIHJlc3VsdHMoKSBmdW5jdGlvbgpoYXMgdGhlIGFiaWxpdHkgdG8gcGVyZm9ybSB0aGVzZSBvZGQgY29udHJhc3RzLCBidXQgSSBoYXZlIG5ldmVyCmZpZ3VyZWQgb3V0IGhvdzsgcGVyaGFwcyBJIHdpbGwgdXNlIHRoaXMgYXMgYSBjaGFuY2UgdG8gcmV2aXNpdCB0aGF0Li4uCgpMZXQgdXMgdGVzdCB0aGlzIGlkZWEgd2l0aCB0aGUgcDA4IGRsZ24gcXVlcnksIHdoaWNoIHNlZWtzIHRvIGNvbXBhcmU6CgoocDA4X2hldF9kbGduIC8gcDA4X2hldF9yZXRpbmEpIC8gKHAwOF9rb19kbGduIC8gcDA4X2tvX3JldGluYSkKClRoZXNlIGFyZSBtYWludGFpbmVkIGluIHRoZSBkZV90YWJsZSB3aXRoIHRoZSBuYW1lcwoncDA4X2hldF9kbGduX3ZzX3JldGluYScgYW5kICdwMDhfa29fZGxnbl92c19yZXRpbmEnCgojIyBwMDggZGxnbiBoZXQgdnMga28KCmBgYHtyfQpwMDhfZGxnbl9jb21iaW5lZF9kZXNlcSA8LSBzdWJ0cmFjdF9kZV9yZXN1bHRzKAogIGZpcnN0X3RhYmxlID0gcDA4X2RsZ25fY29tYmluZWRfdGVzdFtbImRhdGEiXV1bWyJwMDhfaGV0X2RsZ25fdnNfcmV0aW5hIl1dLAogIHNlY29uZF90YWJsZSA9IHAwOF9kbGduX2NvbWJpbmVkX3Rlc3RbWyJkYXRhIl1dW1sicDA4X2tvX2RsZ25fdnNfcmV0aW5hIl1dLAogIGZpcnN0X2xmYyA9ICJkZXNlcV9sb2dmYyIsIHNlY29uZF9sZmMgPSAiZGVzZXFfbG9nZmMiLAogIGZpcnN0X3AgPSAiZGVzZXFfYWRqcCIsIHNlY29uZF9wID0gImRlc2VxX2FkanAiLAogIGZpcnN0X25hbWUgPSAiaGV0Iiwgc2Vjb25kX25hbWUgPSAia28iLAogIGV4Y2VsID0gZ2x1ZSgiMzR0cmFuc2xhdG9tZV9kZXNlcXN1Yl94bHN4X3R4L3RyYW5zbGF0b21lX3AwOF9kbGduX2NvbWJpbmVkX2Rlc2VxLXZ7dmVyfS54bHN4IikpCmBgYAoKU2VlIGhvdyBzaW1pbGFyIHRoZXNlIHJlc3VsdHMgYXJlIHRvIHRob3NlIG9idGFpbmVkIGZyb20gbGltbWEvZWRnZXIuCgpgYGB7cn0KdGVzdF9jb2x1bW5zIDwtIGMoImVkZ2VyX2xvZ2ZjIiwgImxpbW1hX2xvZ2ZjIiwgImVkZ2VyX2FkanAiLCAibGltbWFfYWRqcCIpCnRlc3RfZGYgPC0gcDA4X2RsZ25fY29tYmluZWRfdGVzdFtbImRhdGEiXV1bWyJwMDhfZGxnbl90cmFuc2xhdG9tZSJdXVssIHRlc3RfY29sdW1uc10KdGVzdF9kZiA8LSBtZXJnZSh0ZXN0X2RmLCBwMDhfZGxnbl9jb21iaW5lZF9kZXNlcSwgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXModGVzdF9kZikgPC0gdGVzdF9kZltbIlJvdy5uYW1lcyJdXQp0ZXN0X2RmW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKY29yLnRlc3QodGVzdF9kZltbImxpbW1hX2xvZ2ZjIl1dLCB0ZXN0X2RmW1siaGV0X3ZzX2tvX2xvZ2ZjIl1dKQpjb3IudGVzdCh0ZXN0X2RmW1siZWRnZXJfbG9nZmMiXV0sIHRlc3RfZGZbWyJoZXRfdnNfa29fbG9nZmMiXV0pCnR0IDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIodGVzdF9kZlssIGMoImxpbW1hX2xvZ2ZjIiwgImhldF92c19rb19sb2dmYyIpXSkKdHRbWyJzY2F0dGVyIl1dCnR0IDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIodGVzdF9kZlssIGMoImVkZ2VyX2FkanAiLCAiaGV0X3ZzX2tvX3AiKV0pCnR0W1sic2NhdHRlciJdXQojIyBTbywgdXNpbmcgdGhlIG1heGltdW0gcC12YWx1ZSBpcyBhIGNvbXBsZXRlIGZhaWx1cmU7IGJ1dCB0aGUgZXh0cmVtZSBzaW1pbGFyaXRpZXMKIyMgYmV0d2VlbiB0aGlzIGFuZCBlZGdlUiBzdWdnZXN0IHRvIG1lIHRoYXQgaXQgaXMgbGlrZWx5IHBvc3NpYmxlIHRvIHVzZSB0aGUgcmVzdWx0cwojIyBmcm9tIGVkZ2VSIHdpdGhvdXQgY29uY2VybiAob3IgbGltbWEgZm9yIHRoYXQgbWF0dGVyLCBpdCB3YXMgYWxzbyBleHRyZW1lbHkgc2ltaWxhcikKIyMgT3IgSSBjYW4gc3BlbmQgYSBsaXR0bGUgdGltZSBhbmQgY29sbGVjdCB0aGUgbnVtYmVycyBvbiBlYWNoIHNpZGUgb2YgdGhlIGRpdmlzaW9uCiMjIGFuZCBjYWxjdWxhdGUgYSB0IHN0YXRpc3RpYyBteXNlbGYuCmBgYAoKIyBOb24tU3BlY2lmaWMgZmlsdGVyaW5nIG9mIHRoZSB0cmFuc2xhdG9tZSBkYXRhCgpJIGhhdmUgb24gaGFuZAoKKiBwMDhoZXRfdnNfcDA4a28gOiAocDA4X2hldF9zY24gLSBwMDhfaGV0X3JldGluYSkgLSAocDA4X2tvX3NjbiAtIHAwOF9rb19yZXRpbmEpCiogcDE1aGV0X3ZzX3AxNWtvIDogKHAxNV9oZXRfc2NuIC0gcDE1X2hldF9yZXRpbmEpIC0gKHAxNV9rb19zY24gLSBwMTVfa29fcmV0aW5hKQoqIHAwOGhldF92c19wMDhrbyA6IChwMDhfaGV0X2RsZ24gLSBwMDhfaGV0X3JldGluYSkgLSAocDA4X2tvX2RsZ24gLSBwMDhfa29fcmV0aW5hKQoqIHAxNWhldF92c19wMDhoZXQgOiAocDE1X2hldF9zY24gLSBwMTVfaGV0X3JldGluYSkgLSAocDA4X2hldF9zY24gLSBwMDhfaGV0X3JldGluYSkKKiBwMTVrb192c19wMDhrbyA6IChwMTVfa29fc2NuIC0gcDE1X2tvX3JldGluYSkgLSAocDA4X2tvX3NjbiAtIHAwOF9rb19yZXRpbmEpCgpJIGhhdmUgZ2VuZSBzZXRzIHVwIGFib3ZlIHdoaWNoIGRlZmluZSB0aGUgZ2VuZXMgc3VpdGFibGUgZm9yIGVhY2ggb2YKdGhlc2UgcGllY2VzLiAgVGhlcmUgYXJlIG9ubHkgNSBjb21wYXJpc29ucywgbGV0IHVzIHN0ZXAgdGhyb3VnaCB0aGVtLgoKIyMgU0NOIHRyYW5zbGF0b21lIGhldC9rbyBhdCBwMDgKClRoZSBkYXRhIGZvciB0aGlzIGNvbnRyYXN0IHJlc2lkZXMgaW4Kc2NuX2NvbWJpbmVkX3Rlc3QkZGF0YSRwMDhfc2NuX3RyYW5zbGF0b21lIG9yIHRoZSBzYW1lIHNsb3Qgb2YKc2NuX2NvbWJpbmVkX3Rlc3Rfc3ZhCgoqIHAwOF9oZXRfc2NuIC0gcDA4X2hldF9yZXRpbmEpIC0gKHAwOF9rb19zY24gLSBwMDhfa29fcmV0aW5hKQoKVGh1cywgdGhlIGluY2x1c2lvbl9zaWcgcG9ydGlvbnMgdG8gZXh0cmFjdCBhcmUgZm91bmQgaW46CmluY2x1c2lvbl9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXSwgYW5kIGFyZSBuYW1lZCBleGFjdGx5IGFzIHdyaXR0ZW4gYWJvdmUhCgpgYGB7cn0KcDA4X2hldF92c19rb190cmFuc2xhdG9tZV91bmZpbHQgPC0gc2NuX2NvbWJpbmVkX3Rlc3RbWyJkYXRhIl1dW1sicDA4X3Njbl90cmFuc2xhdG9tZSJdXQpudW1fdW5pb24gPC0gdW5pcXVlKGMocm93bmFtZXMoaW5jbHVzaW9uX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1sicDA4X2hldF9zY24iXV0pLAogICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXMoaW5jbHVzaW9uX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1sicDA4X2hldF9yZXRpbmEiXV0pKSkKbGVuZ3RoKG51bV91bmlvbikKZGVuX3VuaW9uIDwtIHVuaXF1ZShjKHJvd25hbWVzKGluY2x1c2lvbl9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbInAwOF9rb19zY24iXV0pLAogICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXMoaW5jbHVzaW9uX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1sicDA4X2tvX3JldGluYSJdXSkpKQpsZW5ndGgoZGVuX3VuaW9uKQpib3RoX3VuaW9uIDwtIHVuaXF1ZShjKG51bV91bmlvbiwgZGVuX3VuaW9uKSkKbGVuZ3RoKGJvdGhfdW5pb24pCmJvdGhfaW50ZXJfaWR4IDwtIG51bV91bmlvbiAlaW4lIGRlbl91bmlvbgpib3RoX2ludGVyIDwtIG51bV91bmlvbltib3RoX2ludGVyX2lkeF0KbGVuZ3RoKGJvdGhfaW50ZXIpCmtlZXBlciA8LSBsaXN0KCJwMDhfc2NuX3RyYW5zbGF0b21lIiA9IGMoInAwOGhldCIsICJwMDhrbyIpKQpwMDhfc2NuX3RyYW5zbGF0b21lX3VuaW9uX2ZpbHRlcmVkIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHNjbl90cmFuc2xhdG9tZV9kZSwga2VlcGVycyA9IGtlZXBlciwKICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCIzNXRyYW5zbGF0b21lX3VuaW9uX3R4L3AwOF9zY25fdHJhbnNsYXRvbWVfdW5pb25fZmlsdGVyZWRfbm9zdmEtdnt2ZXJ9Lnhsc3giKSwKICB3YW50ZWRfZ2VuZXMgPSBib3RoX3VuaW9uKQpwMDhfc2NuX3RyYW5zbGF0b21lX2ludGVyX2ZpbHRlcmVkIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHNjbl90cmFuc2xhdG9tZV9kZSwga2VlcGVycyA9IGtlZXBlciwKICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCIzNXRyYW5zbGF0b21lX3VuaW9uX3R4L3AwOF9zY25fdHJhbnNsYXRvbWVfaW50ZXJzZWN0X2ZpbHRlcmVkX25vc3ZhLXZ7dmVyfS54bHN4IiksCiAgd2FudGVkX2dlbmVzID0gYm90aF9pbnRlcikKcDA4X3Njbl90cmFuc2xhdG9tZV91bmlvbl9maWx0ZXJlZF9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgc2NuX3RyYW5zbGF0b21lX2RlX3N2YSwga2VlcGVycyA9IGtlZXBlciwKICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCIzNXRyYW5zbGF0b21lX3VuaW9uX3R4L3AwOF9zY25fdHJhbnNsYXRvbWVfdW5pb25fZmlsdGVyZWRfc3ZhLXZ7dmVyfS54bHN4IiksCiAgd2FudGVkX2dlbmVzID0gYm90aF91bmlvbikKcDA4X3Njbl90cmFuc2xhdG9tZV91bmlvbl9maWx0ZXJlZCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBzY25fdHJhbnNsYXRvbWVfZGUsIGtlZXBlcnMgPSBrZWVwZXIsCiAgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgiMzV0cmFuc2xhdG9tZV91bmlvbl90eC9wMDhfc2NuX3RyYW5zbGF0b21lX2ludGVyc2VjdF9maWx0ZXJlZF9zdmEtdnt2ZXJ9Lnhsc3giKSwKICB3YW50ZWRfZ2VuZXMgPSBib3RoX2ludGVyKQpgYGAKCiMgVmVubi9VcFNldCBvZiBSZXRpbmEsIFNDTiwgYW5kIGRMR04gREUgR2VuZXMKCkhlcmUgaXMgYSBzbmlwcGV0IGZyb20gUmFzaG1pIHdoaWNoIGV4cHJlc3NlcyBuaWNlbHkgdGhlIERFLXJlc3VsdApjb21wYXJpc29ucyBzaGUgaXMgbW9zdCBpbnRlcmVzdGVkOgoKU2luY2UsIEkgd2FudCB0byBrbm93IHRoZSBudW1iZXIgb2YgREVHIGV4cHJlc3NlZCBpbiBSZXRpbmEsIFNDTiBhbmQKZExHTiB3aXRoIHJlc3BlY3QgdG8gZ2Vub3R5cGUsIExvY2F0aW9uIGFuZCB0aW1lLiBJIHByZXBhcmVkIHRoZSB2ZW5uCmRpYWdyYW0gZm9yIHRoZXNlIGNvbXBhcmlzb246CgoqIEdlbm90eXBlOiBQOCBSZXQgSGV0IHZzIEtPLCBQMTUgUmV0IEhldCB2cyBLTywgUDggU0NOIEhldCB2cyBLTywKICAgICAgICAgICAgUDE1IFNDTiBIZXQgdnMgS08sIFA4IGRMR04gSGV0IHZzIEtPLCBQMTUgZExHTiBIZXQgdnMgS08KKiBMb2NhdGlvbjogUDhfaGV0IFJldCB2cyBTQ04sIFA4X0tPIFJldCB2cyBTQ04sIFAxNV9oZXQgUmV0IHZzIFNDTiwKICAgICAgICAgICAgUDE1X0tPIFJldCB2cyBTQ04sIFA4X2hldCBSZXQgdnMgZExHTiwgUDhfS08gUmV0IHZzIGRMR04sCiAgICAgICAgICAgIFAxNV9oZXQgUmV0IHZzIGRMR04sIFAxNV9LTyBSZXQgdnMgZExHTiwgUDhfaGV0IFNDTiB2cyBkTEdOLAogICAgICAgICAgICBQOF9LTyBTQ04gdnMgZExHTiwgUDE1X2hldCBTQ04gdnMgZExHTiwgUDE1X0tPIFNDTiB2cyBkTEdOLgoKU2luY2UgSSB3YXMgaW50ZXJlc3RlZCBpbiB1bmRlcnN0YW5kaW5nIHRoZSBjaGFuZ2UgaW4gbG9jYWwgdHJhbnNsYXRvbWUKYWNjb3JkaW5nIHRvIExvY2F0aW9uIGZvciBkaWZmZXJlbnQgZGV2ZWxvcG1lbnRhbCB0aW1lIHBvaW50cyBmb3IgSGV0CmFuZCBLTy4gSGVuY2UsIEkgdHJpZWQgdG8gZ2VuZXJhdGUgYSB2ZW5uIGRpYWdyYW0gZm9yIExvY2F0aW9uIChSZXQKYW5kIFNDTikgYXQgZGV2ZWxvcG1lbnRhbCB0aW1lIHBvaW50cyBQOCBhbmQgUDE1IGZvciBnZW5vdHlwZSBoZXQgYW5kCktPLiAgU28gdGhlIHZlbm4gZGlhZ3JhbSAvIHVwc2V0IHBsb3Qgd2lsbCBiZSBmb3IgbG9jYXRpb24gd2hlcmUgc29tZQpnZW5lcyB3aWxsIGJlIHNoYXJlZC91bmlxdWUgZm9yIFA4X1JldF9oZXQsIFA4X1NDTl9IZXQsIFAxNV9SZXRfSEVULApQMTVfU0NOX0hFVC4gIFdlIGNhbiBwcmVwYXJlIGFuIHVwc2V0IHBsb3QgZm9yIFA4X1JldF9LTywKUDhfU0NOX0tPLCBQMTVfUmV0X0tPIGFuZCBQMTVfU0NOX0tPIGFsc28uIE9yIGNhbiBnZW5lcmF0ZSBhbiB1cHNldApwbG90IGJ5IGNvbWJpbmluZyBib3RoIFA4X1JldF9oZXQsIFA4X1NDTl9IZXQsIFAxNV9SZXRfSEVUIGFuZApQMTVfU0NOX0hFVCBhbmQgUDhfUmV0X0tPLCBQOF9TQ05fS08sIFAxNV9SZXRfS08gYW5kIFAxNV9TQ05fS08uCgpPaywgbGV0IHVzIHNlZSBpZiBJIGNhbiBpbXBsZW1lbnQgdGhpcywgc3RhcnRpbmcgd2l0aCB0aGUgZ2Vub3R5cGUgcXVlcnkKCiogR2Vub3R5cGU6IFA4IFJldCBIZXQgdnMgS08sIFAxNSBSZXQgSGV0IHZzIEtPLCBQOCBTQ04gSGV0IHZzIEtPLAogICAgICAgICAgICBQMTUgU0NOIEhldCB2cyBLTywgUDggZExHTiBIZXQgdnMgS08sIFAxNSBkTEdOIEhldCB2cyBLTwoKIyMga28gdnMgaGV0OyBhbGwgbG9jYXRpb25zIGFuZCB0aW1lcwoKYGBge3J9CiMjIFRoZSBhcHByb3ByaWF0ZSBkYXRhIHN0cnVjdHVyZSBpcyAnZ2Vub3R5cGVfdGFibGVzJywKIyMgYW5kIHRoZSB0YWJsZXMgb2YgaW50ZXJlc3QgYXJlOgp0YWJsZV9uYW1lcyA8LSBjKCJraF9wMDhfcmV0aW5hIiwgImtoX3AxNV9yZXRpbmEiLCAia2hfcDA4X3NjbiIsCiAgICAgICAgICAgICAgICAgImtoX3AxNV9zY24iLCAia2hfcDA4X2RsZ24iLCAia2hfcDE1X2RsZ24iKQp0YWJsZV9uYW1lcyAlaW4lIG5hbWVzKGdlbm90eXBlX3NpZykKbmV3c2lnIDwtIGdlbm90eXBlX3NpZ1tbMV1dCmZvciAoc2lnIGluIDI6bGVuZ3RoKHRhYmxlX25hbWVzKSkgewogIG5hbWUgPC0gdGFibGVfbmFtZXNbc2lnXQogIG5ld3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0gPC0gZ2Vub3R5cGVfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dIDwtIGdlbm90eXBlX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dCn0KZ2Vub3R5cGVfdXBzZXRyIDwtIHVwc2V0cl9zaWcobmV3c2lnKQpnZW5vdHlwZV91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3VwcyhnZW5vdHlwZV91cHNldHIsIGV4Y2VsID0gIjM2dXBzZXRfZ2Vub3R5cGVfdHgvZ2Vub3R5cGVfdXBzZXRfZ3JvdXBzLnhsc3giKQpnZW5vdHlwZV91cHNldHJbWyJhbGxfcGxvdCJdXQpwcChmaWxlID0gIjM2dXBzZXRfZ2Vub3R5cGVfdHgvdGVzdF9nZW5vdHlwZV91cHNldC5wZGYiKQpwcmludChnZW5vdHlwZV91cHNldHJbWyJhbGxfcGxvdCJdXSkKcGxvdHRlZCA8LSBkZXYub2ZmKCkKYGBgCgpOb3cgbGV0IHVzIHRyeSB0aGUgbG9jYXRpb24tc3BlY2lmaWMgY29tcGFyaXNvbnMKCiMjIHNjbiB2cyByZXRpbmEsIHAwOAoKYGBge3J9CiMjIFRoZSBhcHByb3ByaWF0ZSBkYXRhIHN0cnVjdHVyZSBpcyAnZ2Vub3R5cGVfdGFibGVzJywKIyMgYW5kIHRoZSB0YWJsZXMgb2YgaW50ZXJlc3QgYXJlOgp0YWJsZV9uYW1lcyA8LSBjKCJzcl9wMDhfaGV0IiwgInNyX3AwOF9rbyIpCnRhYmxlX25hbWVzICVpbiUgbmFtZXMobG9jYXRpb25fc2lnKQpsb2NhdGlvbl91cHNldF9pbnB1dCA8LSBsaXN0KCkKZmlyc3RfdGFibGUgPC0gdGFibGVfbmFtZXNbMV0KbmV3c2lnIDwtIGxvY2F0aW9uX3NpZ1tbZmlyc3RfdGFibGVdXQpmb3IgKHNpZyBpbiAyOmxlbmd0aCh0YWJsZV9uYW1lcykpIHsKICBuYW1lIDwtIHRhYmxlX25hbWVzW3NpZ10KICBuZXdzaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXQogIG5ld3NpZ1tbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSA8LSBsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXQp9CmxvY2F0aW9uX3Vwc2V0ciA8LSB1cHNldHJfc2lnKG5ld3NpZykKbG9jYXRpb25fdXBzZXRfd3JpdHRlbiA8LSB3cml0ZV91cHNldF9ncm91cHMobG9jYXRpb25fdXBzZXRyLCBleGNlbCA9ICIzNnVwc2V0X2dlbm90eXBlX3R4L3NyX3AwOF9oZXRrb191cHNldF9ncm91cHMueGxzeCIpCmxvY2F0aW9uX3Vwc2V0cltbImFsbF9wbG90Il1dCnBwKGZpbGUgPSAiMzZ1cHNldF9nZW5vdHlwZV90eC90ZXN0X2xvY2F0aW9uX3NyX3AwOF9oZXRrb191cHNldC5wZGYiKQpwcmludChsb2NhdGlvbl91cHNldHJbWyJhbGxfcGxvdCJdXSkKcGxvdHRlZCA8LSBkZXYub2ZmKCkKYGBgCgpJIGFtIHJlYXNvbmFibHkgY2VydGFpbiB0aGF0IFJhc2htaSB3b3VsZCBsaWtlIGEgdGFibGUgb2YgdGhlIGdlbmVzCnNoYXJlZCBhbW9uZyBpbmNyZWFzZWQgc2NuIGtvIGFuZCBoZXQgaW4gdGhlIGFib3ZlIHBsb3QgYWxvbmcgd2l0aCB0aGUKaW5jcmVhc2VkIHJldGluYSAoZS5nLiB0aGUgMjY5IGFuZCAxMDMgZ2VuZSBzZXRzKS4KCiMjIHNjbiB2cyByZXRpbmEsIHAxNQoKYGBge3J9CnRhYmxlX25hbWVzIDwtIGMoInNyX3AxNV9oZXQiLCAic3JfcDE1X2tvIikKdGFibGVfbmFtZXMgJWluJSBuYW1lcyhsb2NhdGlvbl9zaWcpCmxvY2F0aW9uX3Vwc2V0X2lucHV0IDwtIGxpc3QoKQpmaXJzdF90YWJsZSA8LSB0YWJsZV9uYW1lc1sxXQpuZXdzaWcgPC0gbG9jYXRpb25fc2lnW1tmaXJzdF90YWJsZV1dCmZvciAoc2lnIGluIDI6bGVuZ3RoKHRhYmxlX25hbWVzKSkgewogIG5hbWUgPC0gdGFibGVfbmFtZXNbc2lnXQogIG5ld3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dCn0KbG9jYXRpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcobmV3c2lnKQpsb2NhdGlvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3Vwcyhsb2NhdGlvbl91cHNldHIsIGV4Y2VsID0gIjM2dXBzZXRfZ2Vub3R5cGVfdHgvc3JfcDE1X2hldGtvX3Vwc2V0X2dyb3Vwcy54bHN4IikKbG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHJpbnQoc2NuX3JldGluYV9wMTVfdXBzZXRfcmVzdWx0KQpwcChmaWxlID0gIjM2dXBzZXRfZ2Vub3R5cGVfdHgvdGVzdF9sb2NhdGlvbl9zcl9wMTVfaGV0a29fdXBzZXQucGRmIikKcHJpbnQobG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0pCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmBgYAoKIyMgZGxnbiB2cyByZXRpbmEsIHAwOAoKYGBge3J9CiMjIFRoZSBhcHByb3ByaWF0ZSBkYXRhIHN0cnVjdHVyZSBpcyAnZ2Vub3R5cGVfdGFibGVzJywKIyMgYW5kIHRoZSB0YWJsZXMgb2YgaW50ZXJlc3QgYXJlOgp0YWJsZV9uYW1lcyA8LSBjKCJkcl9wMDhfaGV0IiwgImRyX3AwOF9rbyIpCmxvY2F0aW9uX3Vwc2V0X2lucHV0IDwtIGxpc3QoKQpmaXJzdF90YWJsZSA8LSB0YWJsZV9uYW1lc1sxXQpuZXdzaWcgPC0gbG9jYXRpb25fc2lnW1tmaXJzdF90YWJsZV1dCmZvciAoc2lnIGluIDI6bGVuZ3RoKHRhYmxlX25hbWVzKSkgewogIG5hbWUgPC0gdGFibGVfbmFtZXNbc2lnXQogIG5ld3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dCn0KbG9jYXRpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcobmV3c2lnKQpsb2NhdGlvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3Vwcyhsb2NhdGlvbl91cHNldHIsIGV4Y2VsID0gIjN1cHNldF9nZW5vdHlwZV90eC9kcl9wMDhfaGV0a29fdXBzZXRfZ3JvdXBzLnhsc3giKQpsb2NhdGlvbl91cHNldHJbWyJhbGxfcGxvdCJdXQpwcChmaWxlID0gIjM2dXBzZXRfZ2Vub3R5cGVfdHgvdGVzdF9sb2NhdGlvbl9kcl9wMDhfaGV0a29fdXBzZXQucGRmIikKcHJpbnQobG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0pCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmBgYAoKIyMgZGxnbiB2cyByZXRpbmEsIHAxNQoKYGBge3J9CiMjIFRoZSBhcHByb3ByaWF0ZSBkYXRhIHN0cnVjdHVyZSBpcyAnZ2Vub3R5cGVfdGFibGVzJywKIyMgYW5kIHRoZSB0YWJsZXMgb2YgaW50ZXJlc3QgYXJlOgp0YWJsZV9uYW1lcyA8LSBjKCJkcl9wMTVfaGV0IiwgImRyX3AxNV9rbyIpCmxvY2F0aW9uX3Vwc2V0X2lucHV0IDwtIGxpc3QoKQpmaXJzdF90YWJsZSA8LSB0YWJsZV9uYW1lc1sxXQpuZXdzaWcgPC0gbG9jYXRpb25fc2lnW1tmaXJzdF90YWJsZV1dCmZvciAoc2lnIGluIDI6bGVuZ3RoKHRhYmxlX25hbWVzKSkgewogIG5hbWUgPC0gdGFibGVfbmFtZXNbc2lnXQogIG5ld3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dCn0KbG9jYXRpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcobmV3c2lnKQpsb2NhdGlvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3Vwcyhsb2NhdGlvbl91cHNldHIsIGV4Y2VsID0gIjM3dXBzZXRfbG9jYXRpb25fdHgvZHJfcDE1X2hldGtvX3Vwc2V0X2dyb3Vwcy54bHN4IikKbG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICIzN3Vwc2V0X2xvY2F0aW9uX3R4L3Rlc3RfbG9jYXRpb25fZHJfcDE1X2hldGtvX3Vwc2V0LnBkZiIpCnByaW50KGxvY2F0aW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpwbG90dGVkIDwtIGRldi5vZmYoKQpgYGAKCiMjIGRsZ24gdnMgc2NuLCBwMDgKCmBgYHtyfQp0YWJsZV9uYW1lcyA8LSBjKCJkc19wMDhfaGV0IiwgImRzX3AwOF9rbyIpCmxvY2F0aW9uX3Vwc2V0X2lucHV0IDwtIGxpc3QoKQpmaXJzdF90YWJsZSA8LSB0YWJsZV9uYW1lc1sxXQpuZXdzaWcgPC0gbG9jYXRpb25fc2lnW1tmaXJzdF90YWJsZV1dCmZvciAoc2lnIGluIDI6bGVuZ3RoKHRhYmxlX25hbWVzKSkgewogIG5hbWUgPC0gdGFibGVfbmFtZXNbc2lnXQogIG5ld3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dCn0KbG9jYXRpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcobmV3c2lnKQpsb2NhdGlvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3Vwcyhsb2NhdGlvbl91cHNldHIsIGV4Y2VsID0gIjM3dXBzZXRfbG9jYXRpb25fdHgvZHNfcDA4X2hldGtvX3Vwc2V0X2dyb3Vwcy54bHN4IikKbG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICIzN3Vwc2V0X2xvY2F0aW9uX3R4L3Rlc3RfbG9jYXRpb25fZHNfcDA4X2hldGtvX3Vwc2V0LnBkZiIpCnByaW50KGxvY2F0aW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpwbG90dGVkIDwtIGRldi5vZmYoKQpgYGAKCiMjIGRsZ24gdnMgc2NuLCBwMTUKCmBgYHtyfQp0YWJsZV9uYW1lcyA8LSBjKCJkc19wMTVfaGV0IiwgImRzX3AxNV9rbyIpCmxvY2F0aW9uX3Vwc2V0X2lucHV0IDwtIGxpc3QoKQpmaXJzdF90YWJsZSA8LSB0YWJsZV9uYW1lc1sxXQpuZXdzaWcgPC0gbG9jYXRpb25fc2lnW1tmaXJzdF90YWJsZV1dCmZvciAoc2lnIGluIDI6bGVuZ3RoKHRhYmxlX25hbWVzKSkgewogIG5hbWUgPC0gdGFibGVfbmFtZXNbc2lnXQogIG5ld3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dCn0KbG9jYXRpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcobmV3c2lnKQpsb2NhdGlvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3Vwcyhsb2NhdGlvbl91cHNldHIsIGV4Y2VsID0gIjM3dXBzZXRfbG9jYXRpb25fdHgvZHNfcDE1X2hldGtvX3Vwc2V0X2dyb3Vwcy54bHN4IikKbG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICIzN3Vwc2V0X2xvY2F0aW9uX3R4L3Rlc3RfbG9jYXRpb25fZHNfcDE1X2hldGtvX3Vwc2V0LnBkZiIpCnByaW50KGxvY2F0aW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpwbG90dGVkIDwtIGRldi5vZmYoKQpgYGAKCiMgU2hhcmVkIGFuZCB1bmlxdWUgZ2VuZSBzZXRzIGFjcm9zcyB4L3d0CgpJbiB0aGlzIGJsb2NrIEkgd2FudCB0byBmaW5kIHRoZSB1bmlxdWUgYW5kIHNoYXJlZCBnZW5lcyBiZXR3ZWVuOgoKMS4gIHNjbiBwOCBoZXQvd3QgYW5kIHJldGluYSBwOCBoZXQvd3Q6IGh3cDA4c2NuaW5jLCBod3AwOHJldGluYywgaHdwMDhzY25kZWMsIGh3cDA4cmV0ZGVjCjIuICBzY24gcDE1IGhldC93dCBhbmQgcmV0aW5hIHAxNSBoZXQvd3Q6IGh3cDE1c2NuaW5jLCBod3AxNXJldGluYywgaHdwMTVzY25kZWMsIGh3cDE1cmV0ZGVjCjMuICAjMSBhbmQgIzIgdG9nZXRoZXI6ICA4IGNhdGdvcmllcyBhYm92ZQo0LiAgc2NuIHA4IGtvL3d0IGFuZCByZXRpbmEgcDgga28vd3QKNS4gIHNjbiBwMTUga28vd3QgYW5kIHJldGluYSBwMTUga28vd3QKNi4gICM0IGFuZCAjNSB0b2dldGhlcgoKVGhlIGNvbXBhcmlzb25zIG9mIGhldC93dCBhcmUgZm91bmQgaW4gdGhlICdpbmNsdXNpb25fc2lnJyBkYXRhc2V0OwpiZWNhdXNlIHRoZXkgYXJlIHByb3ZpZGluZyBvdXIgY3V0b2ZmcyBmb3Igbm9uc3BlY2lmaWMgYmluZGluZy4KCiMjIE51bWJlciAxIGFib3ZlOiBwMDhfaGV0IHZzIHd0IGZvciBzY24gYW5kIHJldGluYS4KCmBgYHtyfQp0YWJsZV9uYW1lcyA8LSBjKCJwMDhfaGV0X3NjbiIsICJwMDhfaGV0X3JldGluYSIpCmluY2x1c2lvbl91cHNldHIgPC0gdXBzZXRyX3NpZyhpbmNsdXNpb25fc2lnLCBjb250cmFzdHMgPSB0YWJsZV9uYW1lcykKaW5jbHVzaW9uX3Vwc2V0X3dyaXR0ZW4gPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKGluY2x1c2lvbl91cHNldHIsIGV4Y2VsID0gIjM3dXBzZXRfbG9jYXRpb25zX3R4L3JzX3AwOF9oZXRfaW5jbHVzaW9uX3Vwc2V0X2dyb3Vwcy54bHN4IikKaW5jbHVzaW9uX3Vwc2V0cltbImFsbF9wbG90Il1dCnBwKGZpbGUgPSAiMzd1cHNldF9sb2NhdGlvbnNfdHgvaW5jbHVzaW9uX3NyX3AwOF91cHNldC5wZGYiKQpwcmludChpbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0pCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmBgYAoKIyMgTnVtYmVyIDIgYWJvdmU6IHAxNV9oZXQgdnMgd3QgZm9yIHNjbiBhbmQgcmV0aW5hLgoKYGBge3J9CnRhYmxlX25hbWVzIDwtIGMoInAxNV9oZXRfc2NuIiwgInAxNV9oZXRfcmV0aW5hIikKaW5jbHVzaW9uX3Vwc2V0ciA8LSB1cHNldHJfc2lnKGluY2x1c2lvbl9zaWcsIGNvbnRyYXN0cyA9IHRhYmxlX25hbWVzKQppbmNsdXNpb25fdXBzZXRfd3JpdHRlbiA8LSB3cml0ZV91cHNldF9ncm91cHMoaW5jbHVzaW9uX3Vwc2V0ciwgZXhjZWwgPSAiMzd1cHNldF9sb2NhdGlvbnNfdHgvcnNfcDE1X2hldF9pbmNsdXNpb25fdXBzZXRfZ3JvdXBzLnhsc3giKQppbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICIzN3Vwc2V0X2xvY2F0aW9uc190eC9pbmNsdXNpb25fc3JfcDE1X3Vwc2V0LnBkZiIpCnByaW50KGluY2x1c2lvbl91cHNldHJbWyJhbGxfcGxvdCJdXSkKcGxvdHRlZCA8LSBkZXYub2ZmKCkKYGBgCgojIyBOdW1iZXIgMyBhYm92ZTogY29tYmluYXRpb24gb2YgIzEgYW5kICMyCgpgYGB7cn0KdGFibGVfbmFtZXMgPC0gYygicDA4X2hldF9zY24iLCAicDA4X2hldF9yZXRpbmEiLCAicDE1X2hldF9zY24iLCAicDE1X2hldF9yZXRpbmEiKQppbmNsdXNpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcoaW5jbHVzaW9uX3NpZywgY29udHJhc3RzID0gdGFibGVfbmFtZXMpCmluY2x1c2lvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3VwcyhpbmNsdXNpb25fdXBzZXRyLCBleGNlbCA9ICIzN3Vwc2V0X2xvY2F0aW9uc190eC9yc19wMDhwMTVfaGV0X2luY2x1c2lvbl91cHNldF9ncm91cHMueGxzeCIpCmluY2x1c2lvbl91cHNldHJbWyJhbGxfcGxvdCJdXQpwcChmaWxlID0gIjM3dXBzZXRfbG9jYXRpb25zX3R4L2luY2x1c2lvbl9zcl9wMDhwMTVfdXBzZXQucGRmIikKcHJpbnQoaW5jbHVzaW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpwbG90dGVkIDwtIGRldi5vZmYoKQpgYGAKCiMjIE51bWJlciA0IGFib3ZlIHAwOF9rbyB2cyB3dCBmb3Igc2NuIGFuZCByZXRpbmEuCgpgYGB7cn0KdGFibGVfbmFtZXMgPC0gYygicDA4X2tvX3NjbiIsICJwMDhfa29fcmV0aW5hIikKaW5jbHVzaW9uX3Vwc2V0ciA8LSB1cHNldHJfc2lnKGluY2x1c2lvbl9zaWcsIGNvbnRyYXN0cyA9IHRhYmxlX25hbWVzKQppbmNsdXNpb25fdXBzZXRfd3JpdHRlbiA8LSB3cml0ZV91cHNldF9ncm91cHMoaW5jbHVzaW9uX3Vwc2V0ciwgZXhjZWwgPSAiMzd1cHNldF9sb2NhdGlvbnNfdHgvcnNfcDA4X2tvX2luY2x1c2lvbl91cHNldF9ncm91cHMueGxzeCIpCmluY2x1c2lvbl91cHNldHJbWyJhbGxfcGxvdCJdXQpwcChmaWxlID0gIjM3dXBzZXRfbG9jYXRpb25zX3R4L2luY2x1c2lvbl9zcl9wMDhfa29fdXBzZXQucGRmIikKcHJpbnQoaW5jbHVzaW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpwbG90dGVkIDwtIGRldi5vZmYoKQpgYGAKCiMjIE51bWJlciA1IGFib3ZlIHAxNV9rbyB2cyB3dCBmb3Igc2NuIGFuZCByZXRpbmEuCgpgYGB7cn0KdGFibGVfbmFtZXMgPC0gYygicDE1X2tvX3NjbiIsICJwMTVfa29fcmV0aW5hIikKaW5jbHVzaW9uX3Vwc2V0ciA8LSB1cHNldHJfc2lnKGluY2x1c2lvbl9zaWcsIGNvbnRyYXN0cyA9IHRhYmxlX25hbWVzKQppbmNsdXNpb25fdXBzZXRfd3JpdHRlbiA8LSB3cml0ZV91cHNldF9ncm91cHMoaW5jbHVzaW9uX3Vwc2V0ciwgZXhjZWwgPSAiMzd1cHNldF9sb2NhdGlvbnNfdHgvcnNfcDE1X2tvX2luY2x1c2lvbl91cHNldF9ncm91cHMueGxzeCIpCmluY2x1c2lvbl91cHNldHJbWyJhbGxfcGxvdCJdXQpwcChmaWxlID0gIjM3dXBzZXRfbG9jYXRpb25zX3R4L2luY2x1c2lvbl9zcl9wMTVfa29fdXBzZXQucGRmIikKcHJpbnQoaW5jbHVzaW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpwbG90dGVkIDwtIGRldi5vZmYoKQpgYGAKCiMjIE51bWJlciA2IGFib3ZlOiBDb21iaW5pbmcgIzQgYW5kICM1CgpgYGB7cn0KdGFibGVfbmFtZXMgPC0gYygicDA4X2tvX3NjbiIsICJwMDhfa29fcmV0aW5hIiwgInAxNV9rb19zY24iLCAicDE1X2tvX3JldGluYSIpCmluY2x1c2lvbl91cHNldHIgPC0gdXBzZXRyX3NpZyhpbmNsdXNpb25fc2lnLCBjb250cmFzdHMgPSB0YWJsZV9uYW1lcykKaW5jbHVzaW9uX3Vwc2V0X3dyaXR0ZW4gPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKGluY2x1c2lvbl91cHNldHIsIGV4Y2VsID0gIjM3dXBzZXRfbG9jYXRpb25zX3R4L3JzX3AwOHAxNV9rb19pbmNsdXNpb25fdXBzZXRfZ3JvdXBzLnhsc3giKQppbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICIzN3Vwc2V0X2xvY2F0aW9uc190eC9pbmNsdXNpb25fc3JfcDA4cDE1X2tvX3Vwc2V0LnBkZiIpCnByaW50KGluY2x1c2lvbl91cHNldHJbWyJhbGxfcGxvdCJdXSkKcGxvdHRlZCA8LSBkZXYub2ZmKCkKYGBgCgojIEdTVkEKCmBgYHtyfQptc2lnZGIgPC0gInJlZmVyZW5jZS9tc2lnZGJfdjIwMjQuMS5NbS5kYiIKaWYgKGZpbGUuZXhpc3RzKG1zaWdkYikpIHsKICB0eF9oX2dzdmEgPC0gc2ltcGxlX2dzdmEodHhfcGFpcndpc2VfaW5wdXQsIG9yZ2RiID0gIm9yZy5NbS5lZy5kYiIsIHNpZ25hdHVyZV9jYXRlZ29yeSA9ICJtaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25hdHVyZXMgPSBtc2lnZGIsIGlkX3NvdXJjZSA9ICJmZGF0YSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2lkID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIpCiAgdHhfaF9nc3ZhCgogIHR4X2hfZ3N2YV9zaWcgPC0gZ2V0X3NpZ19nc3ZhX2NhdGVnb3JpZXMoCiAgICB0eF9oX2dzdmEsIGV4Y2VsID0gIjM4bXNpZ2RiX3R4L2dzdmFfc2lnX2hhbGxtYXJrX2NhdGVnb3JpZXMueGxzeCIpCiAgdHhfaF9nc3ZhX3NpZwoKICB0eF9tMV9nc3ZhIDwtIHNpbXBsZV9nc3ZhKHR4X3BhaXJ3aXNlX2lucHV0LCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBzaWduYXR1cmVfY2F0ZWdvcnkgPSAibTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcyA9IG1zaWdkYiwgaWRfc291cmNlID0gImZkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2lkID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIpCiAgdHhfbTFfZ3N2YQogIHR4X20xX2dzdmFfc2lnIDwtIGdldF9zaWdfZ3N2YV9jYXRlZ29yaWVzKAogICAgdHhfbTFfZ3N2YSwgZXhjZWwgPSAiMzhtc2lnZGJfdHgvZ3N2YV9zaWdfcG9zaXRpb25hbF9jYXRlZ29yaWVzLnhsc3giKQogIHR4X20xX2dzdmFfc2lnCgogIHR4X20yX2dzdmEgPC0gc2ltcGxlX2dzdmEodHhfcGFpcndpc2VfaW5wdXQsIG9yZ2RiID0gIm9yZy5NbS5lZy5kYiIsIHNpZ25hdHVyZV9jYXRlZ29yeSA9ICJtMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWduYXR1cmVzID0gbXNpZ2RiLCBpZF9zb3VyY2UgPSAiZmRhdGEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWRfaWQgPSAiZXh0ZXJuYWxfdHJhbnNjcmlwdF9uYW1lIikKICB0eF9tMl9nc3ZhCiAgdHhfbTJfZ3N2YV9zaWcgPC0gZ2V0X3NpZ19nc3ZhX2NhdGVnb3JpZXMoCiAgICB0eF9tMl9nc3ZhLCBleGNlbCA9ICIzOG1zaWdkYl90eC9nc3ZhX3NpZ19jdXJhdGVkX2NhdGVnb3JpZXMueGxzeCIpCiAgdHhfbTJfZ3N2YV9zaWcKCiAgdHhfbTNfZ3N2YSA8LSBzaW1wbGVfZ3N2YSh0eF9wYWlyd2lzZV9pbnB1dCwgb3JnZGIgPSAib3JnLk1tLmVnLmRiIiwgc2lnbmF0dXJlX2NhdGVnb3J5ID0gIm0zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25hdHVyZXMgPSBtc2lnZGIsIGlkX3NvdXJjZSA9ICJmZGF0YSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1aXJlZF9pZCA9ICJleHRlcm5hbF90cmFuc2NyaXB0X25hbWUiKQogIHR4X20zX2dzdmEKICB0eF9tM19nc3ZhX3NpZyA8LSBnZXRfc2lnX2dzdmFfY2F0ZWdvcmllcygKICAgIHR4X20zX2dzdmEsIGV4Y2VsID0gIjM4bXNpZ2RiX3R4L2dzdmFfc2lnX3JlZ3VsYXRvcnlfY2F0ZWdvcmllcy54bHN4IikKICB0eF9tM19nc3ZhX3NpZwoKICB0eF9tNV9nc3ZhIDwtIHNpbXBsZV9nc3ZhKHR4X3BhaXJ3aXNlX2lucHV0LCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBzaWduYXR1cmVfY2F0ZWdvcnkgPSAibTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcyA9IG1zaWdkYiwgaWRfc291cmNlID0gImZkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2lkID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIpCiAgdHhfbTVfZ3N2YQogIHR4X201X2dzdmFfc2lnIDwtIGdldF9zaWdfZ3N2YV9jYXRlZ29yaWVzKAogICAgdHhfbTVfZ3N2YSwgZXhjZWwgPSAiMzhtc2lnZGJfdHgvZ3N2YV9zaWdfb250b2xvZ3lfY2F0ZWdvcmllcy54bHN4IikKICB0eF9tNV9nc3ZhX3NpZwoKICB0eF9tOF9nc3ZhIDwtIHNpbXBsZV9nc3ZhKHR4X3BhaXJ3aXNlX2lucHV0LCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBzaWduYXR1cmVfY2F0ZWdvcnkgPSAibTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcyA9IG1zaWdkYiwgaWRfc291cmNlID0gImZkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2lkID0gImV4dGVybmFsX3RyYW5zY3JpcHRfbmFtZSIpCiAgdHhfbThfZ3N2YQogIHR4X204X2dzdmFfc2lnIDwtIGdldF9zaWdfZ3N2YV9jYXRlZ29yaWVzKAogICAgdHhfbThfZ3N2YSwgZXhjZWwgPSAiMzhtc2lnZGJfdHgvZ3N2YV9zaWdfY2VsbHR5cGVfY2F0ZWdvcmllcy54bHN4IikKICB0eF9tOF9nc3ZhX3NpZwp9CmBgYAoKIyBHU0VBIGltYWdlcwoKVXAgYWJvdmUgSSBjcmVhdGVkIGEgZmFpcmx5IGxhcmdlIHNldCBvZiBlbnJpY2htZW50L0dTRUEgYW5hbHlzZXMuCkxldCB1cyBwdWxsIHNvbWUgb2YgdGhlIG1vc3QgaW50ZXJlc3RpbmcgcmVzdWx0cyBoZXJlIGFuZCBsb29rIGF0CnRoZW0uCgpIZXJlIGFyZSB0aGUgc3BlY2lmaWMgcXVlcmllcyBmcm9tIFJhc2htaToKCiogR2Vub3R5cGUgKGhldCB2cyBrbyk6CiAgKiBQOCBoZXQgYW5kIGtvIGZvciBSZXQKICAqIFNDTiAoUDggaGV0IHZzIEtPIFNDTgogICogUDggaGV0IHZzIEtPIFJldCkKICAqIFAxNSBoZXQgYW5kIGtvIGZvciBSZXQKKiBMb2NhdGlvbiAoc29tYWwgdnMgYXhvbmFsKToKICAqIFNSX1AwOF9LTwogICogU1JfUDA4X0hldAogICogU1JfUDE1X0tPCiAgKiBTUl9QMTVfSGV0CiogVGltZShwOHZzIHAxNSk6CiAgKiB0X2hldF9SZXRfIHBvOC1wMTUKICAqIHRfS09fUmV0X0MgcG84LXAxNQogICogdF9oZXRfU0NOX3BvOC1wMTUKICAqIHRfS09fU0NOX3BvOC1wMTUKCiMjIEdlbm90eXBlCgpMZXQgdXMgdGFrZSBhIG1vbWVudCBhbmQgc2VlIGZvciB3aGljaCBjb250cmFzdHMgSSBhY3F1aXJlZCByZXN1bHRzOgoKSSBuZWVkIHRvIG1ha2UgYSBsaXR0bGUgc3VtbWFyeSBmb3IgY2x1c3RlcnByb2ZpbGVyIHRvbyBzbyB0aGF0IEkgY2FuIGVhc2lseSBzZWUgaG93IG1hbnkgaGl0cyB0aGVyZQphcmUgZm9yIGVhY2ggY29udHJhc3QuCgpgYGB7cn0Kc3VtbWFyeShnZW5vdHlwZV9mdWxsX2dwKQpmb3IgKGkgaW4gbmFtZXMoZ2Vub3R5cGVfZnVsbF9ncCkpIHsKICBwcmludChpKQogIHByaW50KGdlbm90eXBlX2Z1bGxfZ3BbW2ldXVtbIm51bV9oaXRzIl1dKQp9CnN1bW1hcnkoZ2Vub3R5cGVfZnVsbF9jcCkKZm9yIChpIGluIG5hbWVzKGdlbm90eXBlX2Z1bGxfY3ApKSB7CiAgcHJpbnQoaSkKICBwcmludChucm93KGdlbm90eXBlX2Z1bGxfY3BbW2ldXVtbImdzZV9nbyJdXSkpCn0KYGBgCgojIyMgcDggaGV0L2tvIGZvciByZXRpbmE6CgpUaGlzIGNvbnRyYXN0LCBldmVuIGJlZm9yZSBmaWx0ZXJpbmcgYXdheSB0aGUgaGlnaC13dCBnZW5lcywgb25seSBoYXMgOCBnZW5lcyBpbiB0aGUgc2V0IG9mIHVwIGFuZApkb3duIGdlbmVzIGNvbWJpbmVkLiAgQXMgYSByZXN1bHQsIG15IGZ1bmN0aW9uIHdoaWNoIHBlcmZvcm1zIGdQcm9maWxlci9jbHVzdGVyUHJvZmlsZXIgc2tpcHMgaXQsCmFuZCBhbHNvIHNraXBzIHRoZSBwMTUgaGV0L2tvIGZvciByZXRpbmEgc2FtcGxlcy4KCiMjIyBwOCBoZXQva28gZm9yIHNjbjoKClRoaXMgaGFzIGEgYnVuY2ggbW9yZSBnZW5lczogNTEgdXAgYW5kIDEyOCBkb3duLgpVbmZvcnR1bmF0ZWx5LCBnUHJvZmlsZXIgc2VlcyBubyBzaWduaWZpY2FudCBvdmVyLXJlcHJlc2VudGF0aW9uIGluIHRoZSB1cCBjYXRlZ29yeSBvZiBnZW5lcy4KVGhlIGRvd24gY2F0ZWdvcnkgaGFzCgpUaGUgdXAvZG93biBzZXRzIGZyb20gY2x1c3RlclByb2ZpbGVyIGhhdmUgZW5yaWNoX2dvLCBnc2VfZ28sIGFuZCBnb19kYXRhIHRvIGxvb2sgYXQuCgpgYGB7cn0KZ2Vub3R5cGVfZnVsbF9ncCRraF9wMDhfc2NuX3VwJG51bV9oaXRzCmdlbm90eXBlX2Z1bGxfZ3Aka2hfcDA4X3Njbl9kb3duJG51bV9oaXRzCgpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChnZW5vdHlwZV9mdWxsX2dwJGtoX3AwOF9zY25fZG93bltbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KcGxvdHNbWyJ0cmVlIl1dCmBgYAoKUGVyaGFwcyBJIHNob3VsZCBqdXN0IGFzayB0aGUgcXVlc3Rpb246IGZvciB3aGljaCBjYXRlZ29yaWVzIGRpZCBJIGdldCByZXN1bHRzIGJhY2s/CgpgYGB7cn0Kc3VtbWFyeShnZW5vdHlwZV9mdWxsX2dwKQpgYGAKCmtoX3AwOF9kbGduX3VwOiBObyBzaWduaWZpY2FudCBnUHJvZmlsZXIgcmVzdWx0cy4Ka2hfcDE1X2RsZ25fdXA6IFNpZ25pZmljYW50IEJQLCBIUCwgS0VHRywgTUYsIFJFQUMsIFRGCmtoX3AwOF9zY25fdXA6IE5vIHNpZ25pZmljYW50IGdQcm9maWxlciByZXN1bHRzLgpraF9wMDhfc2NuX2Rvd246IFNpZ25pZmljYW50IEJQLCBNaVJOQSwgTUYsIFRGCmtoX3AxNV9zY25fZG93bjogU2lnbmlmaWNhbnQgQlAsIE1GCgoKYGBge3J9CnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KGdlbm90eXBlX2Z1bGxfZ3BbWyJraF9wMTVfZGxnbl91cCJdXVtbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KYGBgCgojIyBMb2NhdGlvbgoKIyMjIFNjbiB2cyByZXRpbmEga28sIHAwOAoKYGBge3J9CgpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChsb2NhdGlvbl9ncFtbInNyX3AwOF9rbyJdXVtbInNyX3AwOF9rb191cCJdXVtbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KCnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KGxvY2F0aW9uX2dwW1sic3JfcDA4X2tvIl1dW1sic3JfcDA4X2tvX2Rvd24iXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCmBgYAoKIyMjIHNjbiB2cyByZXRpbmEsIGhldCwgcDA4CgpFbnJpY2hlZCBncm91cHM6IEJQLCBLRUdHLCBNRiwgVEYsIENDCgpgYGB7cn0Kc3VtbWFyeShsb2NhdGlvbl9ncFtbInNyX3AwOF9oZXQiXV1bWyJzcl9wMDhfaGV0X3VwIl1dKQpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChsb2NhdGlvbl9ncFtbInNyX3AwOF9oZXQiXV1bWyJzcl9wMDhfaGV0X3VwIl1dW1siQlBfZW5yaWNoIl1dKQpwbG90c1tbImRvdCJdXQoKcGxvdHMgPC0gcGxvdF9lbnJpY2hyZXN1bHQobG9jYXRpb25fZ3BbWyJzcl9wMDhfaGV0Il1dW1sic3JfcDA4X2hldF91cCJdXVtbIkNDX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KCnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KGxvY2F0aW9uX2dwW1sic3JfcDA4X2hldCJdXVtbInNyX3AwOF9oZXRfZG93biJdXVtbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KCnNyX3AwOF9oZXRfdG9wbl9nc2VhIDwtIHBsb3RfdG9wbl9nc2VhKGxvY2F0aW9uX2NwW1siIl1dKQoKYGBgCgojIyMgU2NuIHZzIHJldGluYSBrbywgcDE1CgpgYGB7cn0KcGxvdHMgPC0gcGxvdF9lbnJpY2hyZXN1bHQobG9jYXRpb25fZ3BbWyJzcl9wMTVfa28iXV1bWyJzcl9wMTVfa29fdXAiXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCgpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChsb2NhdGlvbl9ncFtbInNyX3AxNV9rbyJdXVtbInNyX3AxNV9rb19kb3duIl1dW1siQlBfZW5yaWNoIl1dKQpwbG90c1tbImRvdCJdXQpgYGAKCiMjIyBzY24gdnMgcmV0aW5hLCBoZXQsIHAxNQoKYGBge3J9CnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KGxvY2F0aW9uX2dwW1sic3JfcDE1X2hldCJdXVtbInNyX3AxNV9oZXRfdXAiXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCgpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChsb2NhdGlvbl9ncFtbInNyX3AxNV9oZXQiXV1bWyJzcl9wMTVfaGV0X2Rvd24iXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCmBgYAoKIyMgVGltZQoKIyMjIGhldCByZXRpbmEKClVwczogc2lnbmlmaWNhbnQgcmVzdWx0cyBmb3IgQlAsIE1GLCBURgpEb3duczogQlAsIE1GLCBSRUFDLCBURiwgV1AKCmBgYHtyfQpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdCh0aW1lX2dwW1sidF9oZXRfcmV0aW5hIl1dW1sidF9oZXRfcmV0aW5hX3VwIl1dW1siQlBfZW5yaWNoIl1dKQpwbG90c1tbImRvdCJdXQoKcGxvdHMgPC0gcGxvdF9lbnJpY2hyZXN1bHQodGltZV9ncFtbInRfaGV0X3JldGluYSJdXVtbInRfaGV0X3JldGluYV9kb3duIl1dW1siQlBfZW5yaWNoIl1dKQpwbG90c1tbImRvdCJdXQpgYGAKCiMjIyBrbyByZXRpbmEKClVwOiBCUCwgTWlSTkEsIE1GCkRvd246IEJQLCBNRiwgUkVBQywgVEYKCmBgYHtyfQpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdCh0aW1lX2dwW1sidF9rb19yZXRpbmEiXV1bWyJ0X2tvX3JldGluYV91cCJdXVtbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KCnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KHRpbWVfZ3BbWyJ0X2tvX3JldGluYSJdXVtbInRfa29fcmV0aW5hX2Rvd24iXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCmBgYAoKIyMjIGhldCBzY24KCk5laXRoZXIgb2YgdGhlIFNDTiBnUHJvZmlsZXIgcXVlcmllcyBwcm92aWRlZCBhbnkgcmVzdWx0cy4KCiMgQmlibGlvZ3JhcGh5CgpgYGB7ciBzYXZlbWUsIGV2YWw9RkFMU0V9CnBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCm1lc3NhZ2UocGFzdGUwKCJUaGlzIGlzIGhwZ2x0b29scyBjb21taXQ6ICIsIGdldF9naXRfY29tbWl0KCkpKQptZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHNhdmVmaWxlKSkKdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZSA9IHNhdmVmaWxlKSkKYGBgCgpgYGB7ciBsb2FkbWVfYWZ0ZXIsIGV2YWw9RkFMU0V9CnRtcCA8LSBsb2FkbWUoZmlsZW5hbWUgPSBzYXZlZmlsZSkKYGBgCg==