1 Notes:

1.1 Querying Dr. Mosser 20230123

  • Question from Najib: Connection between 5’ libraries and VDJ, tags are in fact shared from VDJ/surface to the ‘parent’ GEM. April: Do GEM generation, then split the resulting cDNA at cleanup step to the protein barcode set (at bead cleanup), then go into regular 5’ expression library, from which a set of specific primers are used to enrich the VDJ portion. Thus all three have the same parental cell tag.
  • Question from April: Do we need to change the inputs; from how many cells did we recover reads? He wants 10,000 cells / animal. April pooled according to 10x recommendations: 1:4 VDJ:expression library. Surface protein libraries have not yet been run, these have separate indexes and are much shorter; in contrast the VDJ/expression libraries are relatively similar (550 vs 630 nt).
  • From Najib: why 5’ vs 3’ capture? Not entirely certain, but 5’ kit is used for immunology usually, apparently.
  • Questions about the cell preparations: what was the status of the cells at GEM generation? Can we learn if that affected the number of cells observed, reads/cell? Can April tweak the sequencing library inputs to help even out the differences across samples for future runs (given that there are 3(I think) coming up)? Conversely, should I reprocess the samples so that all samples are perceived as having the least number of cells observed?
  • Quick primer of recombination and V(D)J: each receptor is heterodimer, heavy chain has the constant, V, D, and J. Conversely the light chain has the constant and the VJ. In T-cells, allelic exclusion results in a single resulting expresison event. In B-cells there may be many. In-toto there is a near-infinite random set of possibilities; these are all randomly generated during development. B-cells are constantly making new combinations during the lifespan – T-cells are long-lived and constant (Dave explained an experiment where someone transferred T-cells from mouse to mouse over the course of many years). (orientation 5’->3’? constant->D->J?)
  • Every cell that was sequenced is in theory a T-cell (“lung infected T-cells from mice), so should all have VDJ sequences. A small set of immune cells (NK) do not have these.

1.2 Checkin meeting with Dr. Park 20230124

  • April describing observations, cell #s etc.
  • Definitely not FACS, performed negative enrichment which in theory gets >= 90% pure samples.
  • Najib query: limit to VDJ cells only? Unlikely.
  • It sounds to me that the likely tasks I perform are not needed by Dr. Park.

2 TODO:

  • Check expression of the samples for levels of CD3.
  • Print out filtered amounts on a per-sample basis.
  • Query mSigDB categories of interest (currently I grabbed the ~98 influenza categories arbitrarily): e.g. figure out which ones are actually relevant.

3 Changelog

4 Preprocessing with cellranger

I downloaded a new version of cellranger along with the various reference files provided by 10x for the VD(J) references etc. I got a bit distracted by the pipeline language implemented by 10x called ‘martian’. I have the feeling that it might prove a good thing to play with.

Here are the commands I ran to separate the samples and perform the alignments. There are 4 sample names and each was done with one run of the ‘normal’ GEX scRNASeq method and one of the (new to me) V(D)J library.

5 Rerun the pipeline with multi

April kindly sent some information from 10x which shows that I should have used the multi pipline when preprocessing the data.

Intra-Muscular vs. Nasal

I wrote 4 separate configuration csv files using the templates I downloaded and following a little reading. It seemed to me that I should be able to process them all as a single csv file, but when I attempted that, cellranger did not react well. It also took a few tries before I got the various reference/library options correct.

Note that once cellranger successfully ran for the samples I moved them to the multi/ directory so that I can compare the outputs to when I simply did the ‘count’ operation.

The following invocations of cellranger all appear to work without any problems. Ideally I would like them to be done in a single run, though.

5.0.1 Shenanigans for combined VDJ samples

My attempts so far to use the csv configuration to concatenate multiple vdj libraries have not worked, so I chose to do it the stupid way, which is what I should have just done to begin with. Caveat, it works fine for the gex libraries to do it the way the documentation suggests.

cd preprocessing
for i in R1 R2; do
    for j in Control Mock_Mex09 IM_Mex09 IN_Mex09; do
        A_file=$(/bin/ls A_${j}_VDJ*_${i}_001.fastq.gz)
        B_file=$(/bin/ls B_${j}_VDJ*_${i}_001.fastq.gz)
        out_file="Concat_${j}_VDJ_${i}.fastq.gz"
        cp_cmd="cp ${A_file} ${out_file}"
        echo "Running: ${cp_cmd}."
        eval $cp_cmd
        cat_cmd="cat ${B_file} >> ${out_file}"
        echo "Running: ${cat_cmd}."
        eval $cat_cmd
    done
done
module add cellranger
cellranger multi --id control --csv sample_sheets/multi_config_try05_control.csv
cellranger multi --id mock --csv sample_sheets/multi_config_try05_mock.csv
cellranger multi --id m --csv sample_sheets/multi_config_try05_m.csv
cellranger multi --id n --csv sample_sheets/multi_config_try05_n.csv

mv control mock m n 01multi_combined/

6 Annotations

annotations <- load_biomart_annotations()$annotation
## The biomart annotations file already exists, loading from it.
brief <- unique(annotations[, c("hgnc_symbol", "description")])

7 Set prefix of the data

prefix <- "multi"
# prefix <- "01multi_combined"

8 Some stolen code

Here is a snippet of code copy-pasted from:

https://ucdavis-bioinformatics-training.github.io/2020-Advanced_Single_Cell_RNA_Seq/data_analysis/VDJ_Analysis_fixed

which, if I read it correctly, will read in the vdj output and add it as a set of annotations to the seurat object. Note, I made some changes to it because I simply cannot help myself.

I think some variant of this function might be useful for the hpgltools if I think I will use it again…

add_clonotype <- function(seurat_obj, name="control", type="t", prefix=prefix) {
  vdj_directory <- glue::glue("{prefix}/{name}/outs/per_sample_outs/{name}/vdj_t")
  vdj_csv <- glue::glue("{vdj_directory}/filtered_contig_annotations.csv")
  reference_csv <- glue::glue("{vdj_directory}/clonotypes.csv")
  tcr <- readr::read_csv(vdj_csv, show_col_types = FALSE)
  ref <- readr::read_csv(reference_csv, show_col_types = FALSE)

  tcr_duplicate_barcode_idx <- duplicated(tcr[["barcode"]])
  tcr_nodup <- tcr[!tcr_duplicate_barcode_idx, ]

  both <- as.data.frame(merge(tcr_nodup, ref, by.x="raw_clonotype_id", by.y="clonotype_id"))
  rownames(both) <- both[["barcode"]]
  both[["barcode"]] <- NULL

  new <- Seurat::AddMetaData(object=seurat_obj, metadata=both)
  return(new)
}

9 Load the data into Seurat and poke at it

The following block is mostly a cut/paste of itself where I set the (over)simplified name of each sample. This then becomes the template for the path and parameters used to read the data, create a seurat object, and add the clonotype data from the vdj run.

name <- "control"
path <- glue::glue("{prefix}/{name}/outs/per_sample_outs/{name}/count/sample_filtered_feature_bc_matrix")
a_control <- Seurat::Read10X(path) %>%
  CreateSeuratObject(project = name) %>%
  add_clonotype(name = name)
## Error in eval(parse(text = text, keep.source = FALSE), envir): promise already under evaluation: recursive default argument reference or earlier problems?
name <- "mock"
path <- glue::glue("{prefix}/{name}/outs/per_sample_outs/{name}/count/sample_filtered_feature_bc_matrix")
a_mock <- Seurat::Read10X(path) %>%
  CreateSeuratObject(project = name) %>%
  add_clonotype(name = name)
## Error in eval(parse(text = text, keep.source = FALSE), envir): promise already under evaluation: recursive default argument reference or earlier problems?
name <- "m"
path <- glue::glue("{prefix}/{name}/outs/per_sample_outs/{name}/count/sample_filtered_feature_bc_matrix")
a_m <- Seurat::Read10X(path) %>%
  CreateSeuratObject(project = name) %>%
  add_clonotype(name = name)
## Error in eval(parse(text = text, keep.source = FALSE), envir): promise already under evaluation: recursive default argument reference or earlier problems?
name <- "n"
path <- glue::glue("{prefix}/{name}/outs/per_sample_outs/{name}/count/sample_filtered_feature_bc_matrix")
a_n <- Seurat::Read10X(path) %>%
  CreateSeuratObject(project = name) %>%
  add_clonotype(name = name)
## Error in eval(parse(text = text, keep.source = FALSE), envir): promise already under evaluation: recursive default argument reference or earlier problems?

For the moment I want to be able to play with the individual samples as well as the aggregate so that I can better understand the data. So I guess it works out that I didn’t figure out how to run all the samples at the same time via ‘cellranger multi’.

I am pretty sure Seurat’s merge() overload allows one to just do ‘merge(a,b,c,d,e…)’ but I am not using that.

all <- merge(a_control, a_mock)  %>%
  merge(a_m) %>%
  merge(a_n)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': error in evaluating the argument 'x' in selecting a method for function 'merge': error in evaluating the argument 'x' in selecting a method for function 'merge': object 'a_control' not found

10 Query for clonotypes in the individual samples and the full dataset.

control_clono <- !is.na(a_control[["raw_clonotype_id"]])
## Error in eval(expr, envir, enclos): object 'a_control' not found
summary(control_clono)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'control_clono' not found
mock_clono <- !is.na(a_mock[["raw_clonotype_id"]])
## Error in eval(expr, envir, enclos): object 'a_mock' not found
summary(mock_clono)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'mock_clono' not found
m_clono <- !is.na(a_m[["raw_clonotype_id"]])
## Error in eval(expr, envir, enclos): object 'a_m' not found
summary(m_clono)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'm_clono' not found
n_clono <- !is.na(a_n[["raw_clonotype_id"]])
## Error in eval(expr, envir, enclos): object 'a_n' not found
summary(n_clono)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'n_clono' not found

11 Initial Clusters

I want to take a couple minutes to add some annotations to the seurat object, notably I want to state the identity relationships with some sort of name.

Thus I will make a vector of the the sample IDs and for each one make a category of self/not-self. Note that Seurat comes with a function ‘FindConservedMarkers()’ or something like that which compares each self to all other samples, so this may be redundant; but it is kind of nice to be able to see the categories as a set of binary indexes.

cluster_letters <- as.factor(LETTERS[Idents(object=all)])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.factor': no applicable method for 'Idents' applied to an object of class "function"
names(cluster_letters) <- colnames(x=all)
## Error in names(cluster_letters) <- colnames(x = all): object 'cluster_letters' not found
control_ids <- as.character(cluster_letters)
## Error in eval(expr, envir, enclos): object 'cluster_letters' not found
mock_ids <- control
## Error in eval(expr, envir, enclos): object 'control' not found
m_vaccine_ids <- control
## Error in eval(expr, envir, enclos): object 'control' not found
n_vaccine_ids <- control
## Error in eval(expr, envir, enclos): object 'control' not found

Now that I have 4 identical vectors, fill them with my chosen names for the samples and whether they do(nt) have that identity.

control_idx <- control == "A"
## Error in eval(expr, envir, enclos): object 'control' not found
control[control_idx] <- "Control"
## Error in control[control_idx] <- "Control": object 'control' not found
control[!control_idx] <- "Stimulated"
## Error in control[!control_idx] <- "Stimulated": object 'control' not found
mock_idx <- mock == "B"
## Error in eval(expr, envir, enclos): object 'mock' not found
mock[mock_idx] <- "Mock"
## Error in mock[mock_idx] <- "Mock": object 'mock' not found
mock[!mock_idx] <- "Not mock"
## Error in mock[!mock_idx] <- "Not mock": object 'mock' not found
m_vaccine_idx <- m_vaccine == "C"
## Error in eval(expr, envir, enclos): object 'm_vaccine' not found
m_vaccine[m_vaccine_idx] <- "Muscular"
## Error in m_vaccine[m_vaccine_idx] <- "Muscular": object 'm_vaccine' not found
m_vaccine[!m_vaccine_idx] <- "Not Muscular"
## Error in m_vaccine[!m_vaccine_idx] <- "Not Muscular": object 'm_vaccine' not found
n_vaccine_idx <- n_vaccine == "D"
## Error in eval(expr, envir, enclos): object 'n_vaccine' not found
n_vaccine[n_vaccine_idx] <- "Nasal"
## Error in n_vaccine[n_vaccine_idx] <- "Nasal": object 'n_vaccine' not found
n_vaccine[!n_vaccine_idx] <- "Not Nasal"
## Error in n_vaccine[!n_vaccine_idx] <- "Not Nasal": object 'n_vaccine' not found

Now add these categories to the sample metadata. I think this is a good place to consdier having a sample sheet from Dr. Park with whatever other random information might prove interesting about the samples.

all <- AddMetaData(
    object=all,
    metadata=cluster_letters,
    col.name="cluster_letters")
## Error in UseMethod(generic = "AddMetaData", object = object): no applicable method for 'AddMetaData' applied to an object of class "function"
all <- AddMetaData(
    object=all,
    metadata=control,
    col.name="control")
## Error in UseMethod(generic = "AddMetaData", object = object): no applicable method for 'AddMetaData' applied to an object of class "function"
all <- AddMetaData(
    object=all,
    metadata=mock,
    col.name="mock")
## Error in UseMethod(generic = "AddMetaData", object = object): no applicable method for 'AddMetaData' applied to an object of class "function"
all <- AddMetaData(
    object=all,
    metadata=m_vaccine,
    col.name="muscular")
## Error in UseMethod(generic = "AddMetaData", object = object): no applicable method for 'AddMetaData' applied to an object of class "function"
all <- AddMetaData(
    object=all,
    metadata=n_vaccine,
    col.name="nasal")
## Error in UseMethod(generic = "AddMetaData", object = object): no applicable method for 'AddMetaData' applied to an object of class "function"

12 Filters and QC

Let us start filtering the data, leading off with a definition of the minimum number of RNAs, minimum amount of rRNA, and maximum mitochondrial. In addition, let us print how much of each are observed before filtering. Before we can print/filter these attributes, we must use the PercentageFeatureSet() to get the numbers…

min_num_rna <- 200
min_pct_ribo <- 5
max_pct_mito <- 20

all[["percent_mt"]] <- PercentageFeatureSet(all, pattern="^mt-")
## Error in UseMethod(generic = "DefaultAssay", object = object): no applicable method for 'DefaultAssay' applied to an object of class "function"
all[["percent_ribo"]] <- PercentageFeatureSet(all, pattern="^Rp[sl]")
## Error in UseMethod(generic = "DefaultAssay", object = object): no applicable method for 'DefaultAssay' applied to an object of class "function"

Show the state before filtering on a per-cell basis across all samples. Start with the number of cells

sample_summaries <- as_tibble(data.frame(
    "id" = c("control", "mock", "muscular", "nasal"),
    "start_cells" = c(
        sum(all@meta.data[["orig.ident"]] == "control"),
        sum(all@meta.data[["orig.ident"]] == "mock"),
        sum(all@meta.data[["orig.ident"]] == "m"),
        sum(all@meta.data[["orig.ident"]] == "n"))))
## Error in as_tibble(data.frame(id = c("control", "mock", "muscular", "nasal"), : could not find function "as_tibble"
skim(all[["percent_mt"]])
## Error in all[["percent_mt"]]: object of type 'builtin' is not subsettable
skim(all[["percent_ribo"]])
## Error in all[["percent_ribo"]]: object of type 'builtin' is not subsettable
skim(all[["nFeature_RNA"]])
## Error in all[["nFeature_RNA"]]: object of type 'builtin' is not subsettable
skim(all[["nCount_RNA"]])
## Error in all[["nCount_RNA"]]: object of type 'builtin' is not subsettable
## Length and reads are for only those cells with clonotypes.
skim(all[["reads"]])
## Error in all[["reads"]]: object of type 'builtin' is not subsettable
skim(all[["length"]])
## Error in all[["length"]]: object of type 'builtin' is not subsettable
## How many cells have specific chains associated with them
sum(!is.na(all$chain))
## Error in all$chain: object of type 'builtin' is not subsettable

And on a per-sample basis with (new to me) skimr, which provides a pretty summary of the category of interest. The way I wrote the following stanzas should also append new columns to my sample_summaries table comprised of the mean values for these elements.

add_summaries <- function(sample_summaries, meta,
                          new_column = "unknown", group_column = "orig.ident",
                          meta_query = "percent_mt", summary_query = "numeric.mean") {
  sample_summaries[[new_column]] <- meta %>%
    group_by(!!rlang::sym(group_column)) %>%
    skim_tee(meta_query) %>%
    skim(meta_query) %>%
    dplyr::select(summary_query)
  return(sample_summaries)
}
sample_summaries <- sample_summaries %>%
  add_summaries(all@meta.data, new_column = "start_rna", meta_query = "nFeature_RNA") %>%
  add_summaries(all@meta.data, new_column = "nCount_RNA", meta_query = "nCount_RNA") %>%
  add_summaries(all@meta.data, new_column = "start_mt", meta_query = "percent_mt") %>%
  add_summaries(all@meta.data, new_column = "start_ribo", meta_query = "percent_ribo") %>%
  add_summaries(all@meta.data, new_column = "start_clono", meta_query = "reads")
## Error in group_by(., !!rlang::sym(group_column)): trying to get slot "meta.data" from an object of a basic class ("function") with no slots
sample_summaries
## Error in eval(expr, envir, enclos): object 'sample_summaries' not found

Ok, that was fun; lets look at this information as a series of plots:

VlnPlot(all, features="nFeature_RNA", pt.size=0)
## Error in UseMethod(generic = "DefaultAssay", object = object): no applicable method for 'DefaultAssay' applied to an object of class "function"
VlnPlot(all, features="percent_mt", pt.size=0)
## Error in UseMethod(generic = "DefaultAssay", object = object): no applicable method for 'DefaultAssay' applied to an object of class "function"
VlnPlot(all, features="percent_ribo", pt.size=0)
## Error in UseMethod(generic = "DefaultAssay", object = object): no applicable method for 'DefaultAssay' applied to an object of class "function"
VlnPlot(all, features="nCount_RNA", pt.size=0)
## Error in UseMethod(generic = "DefaultAssay", object = object): no applicable method for 'DefaultAssay' applied to an object of class "function"
VlnPlot(all, features="reads", pt.size=0)
## Error in UseMethod(generic = "DefaultAssay", object = object): no applicable method for 'DefaultAssay' applied to an object of class "function"
## I am curious about the length of the clonotype sequences.
VlnPlot(all, features="length", pt.size=0)
## Error in UseMethod(generic = "DefaultAssay", object = object): no applicable method for 'DefaultAssay' applied to an object of class "function"
FeatureScatter(all, "percent_ribo", "percent_mt")
## Error in UseMethod(generic = "Idents", object = object): no applicable method for 'Idents' applied to an object of class "function"
FeatureScatter(all, "nCount_RNA", "nFeature_RNA")
## Error in UseMethod(generic = "Idents", object = object): no applicable method for 'Idents' applied to an object of class "function"
FeatureScatter(all, "nCount_RNA", "percent_ribo")
## Error in UseMethod(generic = "Idents", object = object): no applicable method for 'Idents' applied to an object of class "function"
FeatureScatter(all, "nCount_RNA", "percent_mt")
## Error in UseMethod(generic = "Idents", object = object): no applicable method for 'Idents' applied to an object of class "function"

13 Filter for only cells with a sufficient number of RNAs

sufficient_rna_observed <- WhichCells(all, expression = nFeature_RNA >= min_num_rna)
## Error in UseMethod(generic = "WhichCells", object = object): no applicable method for 'WhichCells' applied to an object of class "function"
filt <- subset(all, cells = sufficient_rna_observed)
## Error in subset.default(all, cells = sufficient_rna_observed): argument "subset" is missing, with no default
sample_summaries[["ncells_filtrna"]] <- c(
    sum(filt@meta.data[["orig.ident"]] == "control"),
    sum(filt@meta.data[["orig.ident"]] == "mock"),
    sum(filt@meta.data[["orig.ident"]] == "m"),
    sum(filt@meta.data[["orig.ident"]] == "n"))
## Error in eval(expr, envir, enclos): object 'filt' not found
sample_summaries %>%
  add_summaries(filt@meta.data, new_column = "filt1_rna", meta_query = "nFeature_RNA") %>%
  add_summaries(filt@meta.data, new_column = "filt1_RNA", meta_query = "nCount_RNA") %>%
  add_summaries(filt@meta.data, new_column = "filt1_mt", meta_query = "percent_mt") %>%
  add_summaries(filt@meta.data, new_column = "filt1_ribo", meta_query = "percent_ribo") %>%
  add_summaries(filt@meta.data, new_column = "filt1_clono", meta_query = "reads") ->
  sample_summaries
## Error in group_by(., !!rlang::sym(group_column)): object 'filt' not found

In the next I will check that the number of reads/rna across cells is sufficient, that filter does nothing currently, which I think is good.

## I think this filter does nothing in its current form.
sufficiently_observed_idx <- rowSums(filt) > 3
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rowSums': object 'filt' not found
summary(sufficiently_observed_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'sufficiently_observed_idx' not found
dim(filt)
## Error in eval(expr, envir, enclos): object 'filt' not found
filt <- subset(filt, features = rownames(filt)[sufficiently_observed_idx])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'subset': object 'filt' not found
dim(filt)
## Error in eval(expr, envir, enclos): object 'filt' not found
## Keep cells with at least some ribosomal reads
## Note the Percent function above actually puts in a floating point
## number from 0-100, not (as I assumed from 0-1).
high_ribosomal <- WhichCells(filt, expression = percent_ribo >= min_pct_ribo)
## Error in WhichCells(filt, expression = percent_ribo >= min_pct_ribo): object 'filt' not found
filt <- subset(filt, cells = high_ribosomal)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'subset': object 'filt' not found
sample_summaries[["ncells_filtribo"]] <- c(
    sum(filt@meta.data[["orig.ident"]] == "control"),
    sum(filt@meta.data[["orig.ident"]] == "mock"),
    sum(filt@meta.data[["orig.ident"]] == "m"),
    sum(filt@meta.data[["orig.ident"]] == "n"))
## Error in eval(expr, envir, enclos): object 'filt' not found
sample_summaries %>%
  add_summaries(filt@meta.data, new_column = "filt2_rna", meta_query = "nFeature_RNA") %>%
  add_summaries(filt@meta.data, new_column = "filt2_RNA", meta_query = "nCount_RNA") %>%
  add_summaries(filt@meta.data, new_column = "filt2_mt", meta_query = "percent_mt") %>%
  add_summaries(filt@meta.data, new_column = "filt2_ribo", meta_query = "percent_ribo") %>%
  add_summaries(filt@meta.data, new_column = "filt2_clono", meta_query = "reads") ->
  sample_summaries
## Error in group_by(., !!rlang::sym(group_column)): object 'filt' not found

Exclude cells with too much mitochondrial RNA

13.1 Now drop mitochondrial genes

low_mitochondrial <- WhichCells(filt, expression = percent_mt <= max_pct_mito)
## Error in WhichCells(filt, expression = percent_mt <= max_pct_mito): object 'filt' not found
filt <- subset(filt, cells = low_mitochondrial)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'subset': object 'filt' not found
dim(filt)
## Error in eval(expr, envir, enclos): object 'filt' not found
sample_summaries[["ncells_filtmito"]] <- c(
    sum(filt@meta.data[["orig.ident"]] == "control"),
    sum(filt@meta.data[["orig.ident"]] == "mock"),
    sum(filt@meta.data[["orig.ident"]] == "m"),
    sum(filt@meta.data[["orig.ident"]] == "n"))
## Error in eval(expr, envir, enclos): object 'filt' not found
sample_summaries %>%
  add_summaries(filt@meta.data, new_column = "filt3_rna", meta_query = "nFeature_RNA") %>%
  add_summaries(filt@meta.data, new_column = "filt3_RNA", meta_query = "nCount_RNA") %>%
  add_summaries(filt@meta.data, new_column = "filt3_mt", meta_query = "percent_mt") %>%
  add_summaries(filt@meta.data, new_column = "filt3_ribo", meta_query = "percent_ribo") %>%
  add_summaries(filt@meta.data, new_column = "filt3_clono", meta_query = "reads") ->
  sample_summaries
## Error in group_by(., !!rlang::sym(group_column)): object 'filt' not found
as.matrix(sample_summaries)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.matrix': object 'sample_summaries' not found

14 Distribution

14.1 Before filtering

all_norm <- NormalizeData(object=all) %>%
  FindVariableFeatures() %>%
  ScaleData() %>%
  RunPCA() %>%
  FindNeighbors() %>%
  FindClusters() %>%
  RunTSNE() %>%
  RunUMAP(reduction = "pca", dims = 1:10)
## Error in UseMethod(generic = "as.sparse", object = x): no applicable method for 'as.sparse' applied to an object of class "function"
DimPlot(object=all_norm, reduction="tsne")
## Error in is.data.frame(x): object 'all_norm' not found
plotted <- DimPlot(all_norm, reduction="umap", group.by="cluster_letters", label=TRUE)
## Error in is.data.frame(x): object 'all_norm' not found
plotted
## Error in eval(expr, envir, enclos): object 'plotted' not found

14.2 After filtering

filt_norm <- NormalizeData(object=filt) %>%
  FindVariableFeatures() %>%
  ScaleData() %>%
  RunPCA() %>%
  FindNeighbors() %>%
  FindClusters() %>%
  RunTSNE() %>%
  RunUMAP(reduction = "pca", dims = 1:10)
## Error in NormalizeData(object = filt): object 'filt' not found
DimPlot(object=filt_norm, reduction="tsne")
## Error in is.data.frame(x): object 'filt_norm' not found
plotted <- DimPlot(filt_norm, reduction="umap", group.by="cluster_letters", label=TRUE)
## Error in is.data.frame(x): object 'filt_norm' not found
plotted
## Error in eval(expr, envir, enclos): object 'plotted' not found
filt_norm <- JackStraw(filt_norm, num.replicate=10)
## Error in DefaultAssay(object = object): object 'filt_norm' not found
filt_norm <- ScoreJackStraw(filt_norm)
## Error in ScoreJackStraw(filt_norm): object 'filt_norm' not found
JackStrawPlot(filt_norm)
## Error in JS(object = object[[reduction]], slot = "empirical"): object 'filt_norm' not found
ElbowPlot(filt_norm)
## Error in Stdev(object = object, reduction = reduction): object 'filt_norm' not found
## So I am thinking maybe 4-10?
wanted_dims <- 6

filt_norm <- FindNeighbors(filt_norm, dims=1:wanted_dims) %>%
  FindClusters(resolution=0.5) %>%
  StashIdent(save.name="res0p5_clusters")
## Error in FindNeighbors(filt_norm, dims = 1:wanted_dims): object 'filt_norm' not found
RunUMAP(filt_norm, dims=1:9)
## Error in RunUMAP(filt_norm, dims = 1:9): object 'filt_norm' not found
DimPlot(filt_norm, label=TRUE)
## Error in is(x, "classRepresentation"): object 'filt_norm' not found
filt_norm <- FindClusters(filt_norm, resolution=0.1) %>%
  FindNeighbors(k.param=6) %>%
  StashIdent(save.name="res0p1_clusters")
## Error in FindClusters(filt_norm, resolution = 0.1): object 'filt_norm' not found
RunUMAP(filt_norm, dims=1:9)
## Error in RunUMAP(filt_norm, dims = 1:9): object 'filt_norm' not found
DimPlot(filt_norm, label=TRUE)
## Error in is(x, "classRepresentation"): object 'filt_norm' not found

Add into the metadata a concatenation of the sample ID and the cluster ID

identity_vector <- filt_norm[["orig.ident"]][["orig.ident"]]
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
class(identity_vector)
## Error in eval(expr, envir, enclos): object 'identity_vector' not found
cluster_vector <- as.character(filt_norm[["res0p1_clusters"]][["res0p1_clusters"]])
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
concatenated_vector <- paste0(identity_vector, "_", cluster_vector)
## Error in paste0(identity_vector, "_", cluster_vector): object 'identity_vector' not found
filt_norm[["cluster_sample"]] <- concatenated_vector
## Error in eval(expr, envir, enclos): object 'concatenated_vector' not found

15 Variable features

var <- FindVariableFeatures(filt_norm)
## Error in FindVariableFeatures(filt_norm): object 'filt_norm' not found
most_var <- head(VariableFeatures(var), 30)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': no applicable method for 'VariableFeatures' applied to an object of class "function"
variable_plot <- VariableFeaturePlot(var)
## Error in UseMethod(generic = "HVFInfo", object = object): no applicable method for 'HVFInfo' applied to an object of class "function"
variable_plot <- LabelPoints(plot=variable_plot, points=most_var, repel=TRUE)
## Error in lapply(X = X, FUN = FUN, ...): object 'variable_plot' not found
variable_plot
## Error in eval(expr, envir, enclos): object 'variable_plot' not found

16 Various marker searches

16.1 All Markers

16.1.1 By sample

Question: Is it smart enough to use the raw data if I give FindAllMarkers the normalized data? For the moment I do not think I will risk it.

combined_markers <- FindAllMarkers(filt, only.pos=TRUE, logfc.threshold = 0.5)
## Error in Idents(object = object): object 'filt' not found
head(combined_markers)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'combined_markers' not found
brief <- unique(annotations[, c("hgnc_symbol", "description")])
combined <- as.data.frame(combined_markers)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'combined_markers' not found
rownames(combined) <- toupper(rownames(combined))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'toupper': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'combined' not found
annotated_markers <- merge(combined, brief, by.x="row.names", by.y="hgnc_symbol",
                           all.x=TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'combined' not found

16.1.2 By sample

combined_markers <- FindAllMarkers(filt, only.pos=TRUE, logfc.threshold=0.5)
## Error in Idents(object = object): object 'filt' not found
combined <- as.data.frame(combined_markers)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'combined_markers' not found
rownames(combined) <- toupper(rownames(combined))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'toupper': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'combined' not found
annotated_markers <- merge(combined, brief, by.x="row.names", by.y="hgnc_symbol",
                           all.x=TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'combined' not found
head(annotated_markers)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'annotated_markers' not found

16.1.3 By Cluster

Since I am not using the filt_norm data structure, I will need to pull the cluster information from the normalized copy…

clusters <- filt
## Error in eval(expr, envir, enclos): object 'filt' not found
Idents(clusters) <- filt_norm[["res0p1_clusters"]]
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
cluster_markers <- FindAllMarkers(clusters, only.pos=TRUE, logfc.threshold=0.5)
## Error in Idents(object = object): object 'clusters' not found
clusters <- as.data.frame(cluster_markers)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'cluster_markers' not found
rownames(clusters) <- toupper(rownames(clusters))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'toupper': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'clusters' not found
annotated_clusters <- merge(clusters, brief, by.x="row.names", by.y="hgnc_symbol",
                           all.x=TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'clusters' not found
head(annotated_clusters)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'annotated_clusters' not found
annotated_clusters %>%
  group_by(cluster) %>%
  dplyr::top_n(n=10, wt=avg_log2FC) %>%
  as.data.frame()
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'annotated_clusters' not found
sum(filt_norm[["res0p1_clusters"]] == "0")
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "0" &
    !is.na(filt_norm[["raw_clonotype_id"]]))
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "1")
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "1" &
    !is.na(filt_norm[["raw_clonotype_id"]]))
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "2")
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "2" &
    !is.na(filt_norm[["raw_clonotype_id"]]))
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "3")
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "3" &
    !is.na(filt_norm[["raw_clonotype_id"]]))
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "4")
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "4" &
    !is.na(filt_norm[["raw_clonotype_id"]]))
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "5")
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "5" &
    !is.na(filt_norm[["raw_clonotype_id"]]))
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "6")
## Error in eval(expr, envir, enclos): object 'filt_norm' not found
sum(filt_norm[["res0p1_clusters"]] == "6" &
    !is.na(filt_norm[["raw_clonotype_id"]]))
## Error in eval(expr, envir, enclos): object 'filt_norm' not found

Clusters 0 and 5 have a great majority of the clonotypes. 0 has something like 90%, 5 has ~ 30%, the others ~ 10%

16.2 Compare specific clusters

16.2.1 Look at cluster 0, Nasal vs. Control

controln_0 <- FindMarkers(
    filt, group.by="cluster_sample",
    ident.1="control_0", ident.2="n_0") %>%
  as.data.frame()
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'filt' not found
head(controln_0)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'controln_0' not found
rownames(controln_0) <- toupper(rownames(controln_0))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'toupper': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'controln_0' not found
controln_0 <- merge(controln_0, brief, by="row.names", by.y="hgnc_symbol",
                    all.x=TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'controln_0' not found
annotated_clusters %>%
  group_by(cluster) %>%
  dplyr::top_n(n=10, wt=avg_log2FC) %>%
  as.data.frame()
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'annotated_clusters' not found

16.2.2 Cluster 0 Nasal vs. Mock

mockvsn_0 <- FindMarkers(
    filt_norm, group.by="cluster_sample",
    ident.1="n_0", ident.2="mock_0") %>%
  as.data.frame()
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'filt_norm' not found
head(mockvsn_0)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'mockvsn_0' not found
rownames(mockvsn_0) <- toupper(rownames(mockvsn_0))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'toupper': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'mockvsn_0' not found
mockvsn_0 <- merge(mockvsn_0, brief, by="row.names", by.y="hgnc_symbol",
                   all.x=TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'mockvsn_0' not found
mockvsm_0 <- FindMarkers(
    filt_norm, group.by="cluster_sample",
    ident.1="m_0", ident.2="mock_0") %>%
  as.data.frame()
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'filt_norm' not found
head(mockvsm_0)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'mockvsm_0' not found
rownames(mockvsm_0) <- toupper(rownames(mockvsm_0))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'toupper': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'mockvsm_0' not found
mockvsm_0 <- merge(mockvsm_0, brief, by="row.names", by.y="hgnc_symbol",
                   all.x=TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'mockvsm_0' not found
head(mockvsm_0, n=30)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'mockvsm_0' not found

16.3 Find Conserved Markers

This function makes no sense.

DefaultAssay(filt_norm) <- "RNA"
## Error in DefaultAssay(filt_norm) <- "RNA": object 'filt_norm' not found
conserved_markers <- FindConservedMarkers(
    filt_norm, ident.1=c(0, 1), ident.2=c(2,3,4),
    grouping.var="sample", only.pos=TRUE,
    verbose=TRUE)
## Error in FetchData(object = object, vars = grouping.var): object 'filt_norm' not found
mock_vs_control <- FindMarkers(filt_norm, group.by = "orig.ident",
                               ident.1 = "mock",
                               ident.2 = "control")
## Error in FindMarkers(filt_norm, group.by = "orig.ident", ident.1 = "mock", : object 'filt_norm' not found
head(mock_vs_control)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'mock_vs_control' not found
muscular_vs_mock <- FindMarkers(filt_norm, group.by = "orig.ident",
                               ident.1 = "m",
                               ident.2 = "mock")
## Error in FindMarkers(filt_norm, group.by = "orig.ident", ident.1 = "m", : object 'filt_norm' not found
summary(muscular_vs_mock)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'muscular_vs_mock' not found
nasal_vs_mock <- FindMarkers(filt, group.by = "orig.ident",
                             min.pct = 0.25, ident.1 = "n",
                             ident.2 = "mock")
## Error in FindMarkers(filt, group.by = "orig.ident", min.pct = 0.25, ident.1 = "n", : object 'filt' not found
summary(nasal_vs_mock)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'nasal_vs_mock' not found
FeaturePlot(filt, features=c("Rgcc"),
            split.by="orig.ident", max.cutoff=3, cols=c("darkgreen", "darkred"))
## Error in is(x, "classRepresentation"): object 'filt' not found

17 Scan for likely cell cycle genes

This is a neat idea, I think we can repurpose it to immunology gene sets.

filt <- CellCycleScoring(
    object = filt_norm,
    g2m.features = cc.genes$g2m.genes,
    s.features = cc.genes$s.genes)
## Error in DefaultAssay(object = object): object 'filt_norm' not found
VlnPlot(filt, features = c("S.Score", "G2M.Score"),
        group.by = "orig.ident",
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'filt' not found

Having written the following I realized I used an older version of my mSigDB reference… FIXME: Redo it with the 7.5+ data.

broad_types <- load_gmt_signatures(signatures = "reference/m8.all.v2022.1.Mm.symbols.gmt")
broad_list <- list()
for (i in names(broad_types)) {
  broad_list[[i]] <- geneIds(broad_types[[i]])
}
wtf <- AddModuleScore(object = filt_norm, features = broad_list,
                      name = "m8")
## Error in DefaultAssay(object = object): object 'filt_norm' not found
chosen <- c(3, 9, 11, 36, 43, 42, 14)
names(broad_types)[chosen]
## [1] "DESCARTES_ORGANOGENESIS_HEPATOCYTES"                  
## [2] "DESCARTES_ORGANOGENESIS_WHITE_BLOOD_CELLS"            
## [3] "DESCARTES_ORGANOGENESIS_EPITHELIAL_CELLS"             
## [4] "DESCARTES_ORGANOGENESIS_NUETROPHILS"                  
## [5] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_T_CELL_AGEING"
## [6] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_B_CELL_AGEING"
## [7] "DESCARTES_ORGANOGENESIS_JAW_AND_TOOTH_PROGENITORS"
columns <- paste0("m8", chosen)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
chosen <- c(50, 51, 60, 61, 62, 65, 66)
names(broad_types)[chosen]
## [1] "TABULA_MURIS_SENIS_BLADDER_LEUKOCYTE_AGEING"            
## [2] "TABULA_MURIS_SENIS_BRAIN_MYELOID_MACROPHAGE_AGEING"     
## [3] "TABULA_MURIS_SENIS_DIAPHRAGM_B_CELL_AGEING"             
## [4] "TABULA_MURIS_SENIS_DIAPHRAGM_ENDOTHELIAL_CELL_AGEING"   
## [5] "TABULA_MURIS_SENIS_DIAPHRAGM_MACROPHAGE_AGEING"         
## [6] "TABULA_MURIS_SENIS_GONADAL_ADIPOSE_TISSUE_B_CELL_AGEING"
## [7] "TABULA_MURIS_SENIS_GONADAL_ADIPOSE_TISSUE_T_CELL_AGEING"
columns <- paste0("m8", chosen)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
chosen <- c(115, 118, 119, 120, 121, 125, 128)
names(broad_types)[chosen]
## [1] "TABULA_MURIS_SENIS_LIVER_MATURE_NK_T_CELL_AGEING"             
## [2] "TABULA_MURIS_SENIS_LUNG_CD4_POSITIVE_ALPHA_BETA_T_CELL_AGEING"
## [3] "TABULA_MURIS_SENIS_LUNG_CD8_POSITIVE_ALPHA_BETA_T_CELL_AGEING"
## [4] "TABULA_MURIS_SENIS_LUNG_NK_CELL_AGEING"                       
## [5] "TABULA_MURIS_SENIS_LUNG_T_CELL_AGEING"                        
## [6] "TABULA_MURIS_SENIS_LUNG_CLASSICAL_MONOCYTE_AGEING"            
## [7] "TABULA_MURIS_SENIS_LUNG_INTERMEDIATE_MONOCYTE_AGEING"
columns <- paste0("m8", chosen)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
chosen <- c(212, 211, 210, 209)
names(broad_types)[chosen]
## [1] "TABULA_MURIS_SENIS_TRACHEA_GRANULOCYTE_AGEING"                                   
## [2] "TABULA_MURIS_SENIS_TRACHEA_FIBROBLAST_AGEING"                                    
## [3] "TABULA_MURIS_SENIS_TRACHEA_ENDOTHELIAL_CELL_AGEING"                              
## [4] "TABULA_MURIS_SENIS_TRACHEA_BASAL_EPITHELIAL_CELL_OF_TRACHEOBRONCHIAL_TREE_AGEING"
columns <- paste0("m8", chosen)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
chosen <- c(176:182)
names(broad_types)[chosen]
## [1] "TABULA_MURIS_SENIS_PANCREAS_PANCREATIC_DELTA_CELL_AGEING"              
## [2] "TABULA_MURIS_SENIS_PANCREAS_PANCREATIC_POLYPEPTIDE_CELL_AGEING"        
## [3] "TABULA_MURIS_SENIS_PANCREAS_PANCREATIC_ACINAR_CELL_AGEING"             
## [4] "TABULA_MURIS_SENIS_PANCREAS_PANCREATIC_DUCTAL_CELL_AGEING"             
## [5] "TABULA_MURIS_SENIS_PANCREAS_PANCREATIC_STELLATE_CELL_AGEING"           
## [6] "TABULA_MURIS_SENIS_SUBCUTANEOUS_ADIPOSE_TISSUE_B_CELL_AGEING"          
## [7] "TABULA_MURIS_SENIS_SUBCUTANEOUS_ADIPOSE_TISSUE_ENDOTHELIAL_CELL_AGEING"
columns <- paste0("m8", chosen)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
chosen <- c(42:47)
names(broad_types)[chosen]
## [1] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_B_CELL_AGEING"                          
## [2] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_T_CELL_AGEING"                          
## [3] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_ENDOTHELIAL_CELL_AGEING"                
## [4] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_MESENCHYMAL_STEM_CELL_OF_ADIPOSE_AGEING"
## [5] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_MYELOID_CELL_AGEING"                    
## [6] "TABULA_MURIS_SENIS_BLADDER_BLADDER_CELL_AGEING"
columns <- paste0("m8", chosen)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
t_groups_idx <- grepl(pattern="_T_CELL", x=names(broad_types))
t_groups <- names(broad_types)[t_groups_idx]
t_nums <- which(t_groups_idx, broad_types)
columns <- paste0("m8", t_nums)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
t_groups
##  [1] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_T_CELL_AGEING"                             
##  [2] "TABULA_MURIS_SENIS_GONADAL_ADIPOSE_TISSUE_T_CELL_AGEING"                           
##  [3] "TABULA_MURIS_SENIS_HEART_T_CELL_AGEING"                                            
##  [4] "TABULA_MURIS_SENIS_KIDNEY_T_CELL_AGEING"                                           
##  [5] "TABULA_MURIS_SENIS_LIMB_MUSCLE_T_CELL_AGEING"                                      
##  [6] "TABULA_MURIS_SENIS_LIVER_MATURE_NK_T_CELL_AGEING"                                  
##  [7] "TABULA_MURIS_SENIS_LUNG_CD4_POSITIVE_ALPHA_BETA_T_CELL_AGEING"                     
##  [8] "TABULA_MURIS_SENIS_LUNG_CD8_POSITIVE_ALPHA_BETA_T_CELL_AGEING"                     
##  [9] "TABULA_MURIS_SENIS_LUNG_T_CELL_AGEING"                                             
## [10] "TABULA_MURIS_SENIS_LUNG_MATURE_NK_T_CELL_AGEING"                                   
## [11] "TABULA_MURIS_SENIS_MESENTERIC_ADIPOSE_TISSUE_CD4_POSITIVE_ALPHA_BETA_T_CELL_AGEING"
## [12] "TABULA_MURIS_SENIS_MESENTERIC_ADIPOSE_TISSUE_CD8_POSITIVE_ALPHA_BETA_T_CELL_AGEING"
## [13] "TABULA_MURIS_SENIS_MAMMARY_GLAND_T_CELL_AGEING"                                    
## [14] "TABULA_MURIS_SENIS_MARROW_CD4_POSITIVE_ALPHA_BETA_T_CELL_AGEING"                   
## [15] "TABULA_MURIS_SENIS_MARROW_MATURE_ALPHA_BETA_T_CELL_AGEING"                         
## [16] "TABULA_MURIS_SENIS_MARROW_NAIVE_T_CELL_AGEING"                                     
## [17] "TABULA_MURIS_SENIS_SPLEEN_CD4_POSITIVE_ALPHA_BETA_T_CELL_AGEING"                   
## [18] "TABULA_MURIS_SENIS_SPLEEN_CD8_POSITIVE_ALPHA_BETA_T_CELL_AGEING"                   
## [19] "TABULA_MURIS_SENIS_SPLEEN_T_CELL_AGEING"                                           
## [20] "TABULA_MURIS_SENIS_SPLEEN_MATURE_NK_T_CELL_AGEING"                                 
## [21] "TABULA_MURIS_SENIS_THYMUS_IMMATURE_T_CELL_AGEING"                                  
## [22] "TABULA_MURIS_SENIS_TRACHEA_T_CELL_AGEING"
t_groups_idx <- grepl(pattern="_EPITHELIAL_", x=names(broad_types))
t_groups <- names(broad_types)[t_groups_idx]
t_nums <- which(t_groups_idx, broad_types)
columns <- paste0("m8", t_nums)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
t_groups
##  [1] "DESCARTES_ORGANOGENESIS_EPITHELIAL_CELLS"                                                  
##  [2] "TABULA_MURIS_SENIS_KIDNEY_EPITHELIAL_CELL_OF_PROXIMAL_TUBULE_AGEING"                       
##  [3] "TABULA_MURIS_SENIS_KIDNEY_KIDNEY_COLLECTING_DUCT_EPITHELIAL_CELL_AGEING"                   
##  [4] "TABULA_MURIS_SENIS_KIDNEY_KIDNEY_DISTAL_CONVOLUTED_TUBULE_EPITHELIAL_CELL_AGEING"          
##  [5] "TABULA_MURIS_SENIS_KIDNEY_KIDNEY_LOOP_OF_HENLE_ASCENDING_LIMB_EPITHELIAL_CELL_AGEING"      
##  [6] "TABULA_MURIS_SENIS_KIDNEY_KIDNEY_LOOP_OF_HENLE_THICK_ASCENDING_LIMB_EPITHELIAL_CELL_AGEING"
##  [7] "TABULA_MURIS_SENIS_KIDNEY_KIDNEY_PROXIMAL_CONVOLUTED_TUBULE_EPITHELIAL_CELL_AGEING"        
##  [8] "TABULA_MURIS_SENIS_MAMMARY_GLAND_LUMINAL_EPITHELIAL_CELL_OF_MAMMARY_GLAND_AGEING"          
##  [9] "TABULA_MURIS_SENIS_SUBCUTANEOUS_ADIPOSE_TISSUE_EPITHELIAL_CELL_AGEING"                     
## [10] "TABULA_MURIS_SENIS_TRACHEA_BASAL_EPITHELIAL_CELL_OF_TRACHEOBRONCHIAL_TREE_AGEING"
t_groups_idx <- grepl(pattern="_ENDOTHELIAL_", x=names(broad_types))
t_groups <- names(broad_types)[t_groups_idx]
t_nums <- which(t_groups_idx, broad_types)
columns <- paste0("m8", t_nums)
VlnPlot(wtf, features = columns,
        group.by = "res0p1_clusters", same.y.lims = TRUE,
        ncol = 4, pt.size = 0)
## Error in DefaultAssay(object = object): object 'wtf' not found
t_groups
##  [1] "DESCARTES_ORGANOGENESIS_ENDOTHELIAL_CELLS"                                    
##  [2] "TABULA_MURIS_SENIS_AORTA_AORTIC_ENDOTHELIAL_CELL_AGEING"                      
##  [3] "TABULA_MURIS_SENIS_BROWN_ADIPOSE_TISSUE_ENDOTHELIAL_CELL_AGEING"              
##  [4] "TABULA_MURIS_SENIS_BLADDER_ENDOTHELIAL_CELL_AGEING"                           
##  [5] "TABULA_MURIS_SENIS_BRAIN_NON_MYELOID_ENDOTHELIAL_CELL_AGEING"                 
##  [6] "TABULA_MURIS_SENIS_DIAPHRAGM_ENDOTHELIAL_CELL_AGEING"                         
##  [7] "TABULA_MURIS_SENIS_GONADAL_ADIPOSE_TISSUE_ENDOTHELIAL_CELL_AGEING"            
##  [8] "TABULA_MURIS_SENIS_HEART_ENDOTHELIAL_CELL_OF_CORONARY_ARTERY_AGEING"          
##  [9] "TABULA_MURIS_SENIS_HEART_AND_AORTA_ENDOTHELIAL_CELL_OF_CORONARY_ARTERY_AGEING"
## [10] "TABULA_MURIS_SENIS_LIMB_MUSCLE_ENDOTHELIAL_CELL_AGEING"                       
## [11] "TABULA_MURIS_SENIS_LIVER_ENDOTHELIAL_CELL_OF_HEPATIC_SINUSOID_AGEING"         
## [12] "TABULA_MURIS_SENIS_LUNG_ENDOTHELIAL_CELL_OF_LYMPHATIC_VESSEL_AGEING"          
## [13] "TABULA_MURIS_SENIS_LUNG_VEIN_ENDOTHELIAL_CELL_AGEING"                         
## [14] "TABULA_MURIS_SENIS_MESENTERIC_ADIPOSE_TISSUE_ENDOTHELIAL_CELL_AGEING"         
## [15] "TABULA_MURIS_SENIS_MAMMARY_GLAND_ENDOTHELIAL_CELL_AGEING"                     
## [16] "TABULA_MURIS_SENIS_PANCREAS_ENDOTHELIAL_CELL_AGEING"                          
## [17] "TABULA_MURIS_SENIS_SUBCUTANEOUS_ADIPOSE_TISSUE_ENDOTHELIAL_CELL_AGEING"       
## [18] "TABULA_MURIS_SENIS_TRACHEA_ENDOTHELIAL_CELL_AGEING"
pander::pander(sessionInfo())

R version 4.2.0 (2022-04-22)

Platform: x86_64-pc-linux-gnu (64-bit)

locale: LC_CTYPE=en_US.UTF-8, LC_NUMERIC=C, LC_TIME=en_US.UTF-8, LC_COLLATE=en_US.UTF-8, LC_MONETARY=en_US.UTF-8, LC_MESSAGES=en_US.UTF-8, LC_PAPER=en_US.UTF-8, LC_NAME=C, LC_ADDRESS=C, LC_TELEPHONE=C, LC_MEASUREMENT=en_US.UTF-8 and LC_IDENTIFICATION=C

attached base packages: stats4, stats, graphics, grDevices, utils, datasets, methods and base

other attached packages: GSEABase(v.1.60.0), graph(v.1.76.0), annotate(v.1.76.0), XML(v.3.99-0.13), AnnotationDbi(v.1.60.0), skimr(v.2.1.5), purrr(v.1.0.0), ggplot2(v.3.4.0), SeuratObject(v.4.1.3), Seurat(v.4.3.0), hpgltools(v.1.0), testthat(v.3.1.6), reticulate(v.1.26), SummarizedExperiment(v.1.28.0), GenomicRanges(v.1.50.2), GenomeInfoDb(v.1.34.6), IRanges(v.2.32.0), S4Vectors(v.0.36.1), MatrixGenerics(v.1.10.0), matrixStats(v.0.63.0), Biobase(v.2.58.0) and BiocGenerics(v.0.44.0)

loaded via a namespace (and not attached): ica(v.1.0-3), ps(v.1.7.2), Rsamtools(v.2.14.0), foreach(v.1.5.2), lmtest(v.0.9-40), rprojroot(v.2.0.3), crayon(v.1.5.2), rbibutils(v.2.2.11), MASS(v.7.3-58.1), nlme(v.3.1-161), backports(v.1.4.1), sva(v.3.46.0), GOSemSim(v.2.24.0), rlang(v.1.0.6), XVector(v.0.38.0), HDO.db(v.0.99.1), ROCR(v.1.0-11), irlba(v.2.3.5.1), nloptr(v.2.0.3), callr(v.3.7.3), limma(v.3.54.0), filelock(v.1.0.2), BiocParallel(v.1.32.5), rjson(v.0.2.21), bit64(v.4.0.5), glue(v.1.6.2), sctransform(v.0.3.5), pbkrtest(v.0.5.1), parallel(v.4.2.0), processx(v.3.8.0), spatstat.sparse(v.3.0-0), DOSE(v.3.24.2), spatstat.geom(v.3.0-3), tidyselect(v.1.2.0), usethis(v.2.1.6), fitdistrplus(v.1.1-8), variancePartition(v.1.28.0), tidyr(v.1.2.1), zoo(v.1.8-11), qqconf(v.1.3.1), GenomicAlignments(v.1.34.0), xtable(v.1.8-4), magrittr(v.2.0.3), evaluate(v.0.19), Rdpack(v.2.4), cli(v.3.5.0), zlibbioc(v.1.44.0), sn(v.2.1.0), rstudioapi(v.0.14), miniUI(v.0.1.1.1), sp(v.1.5-1), bslib(v.0.4.2), mathjaxr(v.1.6-0), fastmatch(v.1.1-3), aod(v.1.3.2), treeio(v.1.22.0), shiny(v.1.7.4), xfun(v.0.36), multtest(v.2.54.0), pkgbuild(v.1.4.0), gson(v.0.0.9), cluster(v.2.1.4), caTools(v.1.18.2), tidygraph(v.1.2.2), KEGGREST(v.1.38.0), tibble(v.3.1.8), ggrepel(v.0.9.2), ape(v.5.6-2), listenv(v.0.9.0), Biostrings(v.2.66.0), png(v.0.1-8), future(v.1.30.0), withr(v.2.5.0), bitops(v.1.0-7), ggforce(v.0.4.1), plyr(v.1.8.8), pillar(v.1.8.1), gplots(v.3.1.3), cachem(v.1.0.6), GenomicFeatures(v.1.50.3), multcomp(v.1.4-20), fs(v.1.5.2), clusterProfiler(v.4.6.0), vctrs(v.0.5.1), ellipsis(v.0.3.2), generics(v.0.1.3), devtools(v.2.4.5), metap(v.1.8), tools(v.4.2.0), munsell(v.0.5.0), tweenr(v.2.0.2), fgsea(v.1.24.0), DelayedArray(v.0.24.0), fastmap(v.1.1.0), compiler(v.4.2.0), pkgload(v.1.3.2), abind(v.1.4-5), httpuv(v.1.6.7), rtracklayer(v.1.58.0), sessioninfo(v.1.2.2), plotly(v.4.10.1), GenomeInfoDbData(v.1.2.9), gridExtra(v.2.3), edgeR(v.3.40.1), lattice(v.0.20-45), deldir(v.1.0-6), mutoss(v.0.1-12), utf8(v.1.2.2), later(v.1.3.0), dplyr(v.1.0.10), BiocFileCache(v.2.6.0), jsonlite(v.1.8.4), scales(v.1.2.1), tidytree(v.0.4.2), pbapply(v.1.6-0), genefilter(v.1.80.2), lazyeval(v.0.2.2), promises(v.1.2.0.1), doParallel(v.1.0.17), goftest(v.1.2-3), spatstat.utils(v.3.0-1), sandwich(v.3.0-2), rmarkdown(v.2.19), cowplot(v.1.1.1), Rtsne(v.0.16), pander(v.0.6.5), downloader(v.0.4), uwot(v.0.1.14), igraph(v.1.3.5), plotrix(v.3.8-2), survival(v.3.4-0), numDeriv(v.2016.8-1.1), yaml(v.2.3.6), htmltools(v.0.5.4), memoise(v.2.0.1), profvis(v.0.3.7), BiocIO(v.1.8.0), locfit(v.1.5-9.7), graphlayouts(v.0.8.4), viridisLite(v.0.4.1), digest(v.0.6.31), assertthat(v.0.2.1), RhpcBLASctl(v.0.21-247.1), mime(v.0.12), rappdirs(v.0.3.3), repr(v.1.1.4), RSQLite(v.2.2.20), yulab.utils(v.0.0.6), future.apply(v.1.10.0), remotes(v.2.4.2), data.table(v.1.14.6), urlchecker(v.1.0.1), blob(v.1.2.3), splines(v.4.2.0), RCurl(v.1.98-1.9), broom(v.1.0.2), hms(v.1.1.2), colorspace(v.2.0-3), base64enc(v.0.1-3), mnormt(v.2.1.1), aplot(v.0.1.9), sass(v.0.4.4), Rcpp(v.1.0.9), RANN(v.2.6.1), mvtnorm(v.1.1-3), enrichplot(v.1.18.3), fansi(v.1.0.3), brio(v.1.1.3), parallelly(v.1.33.0), R6(v.2.5.1), grid(v.4.2.0), ggridges(v.0.5.4), lifecycle(v.1.0.3), TFisher(v.0.2.0), curl(v.4.3.3), minqa(v.1.2.5), leiden(v.0.4.3), jquerylib(v.0.1.4), PROPER(v.1.30.0), Matrix(v.1.5-3), qvalue(v.2.30.0), TH.data(v.1.1-1), desc(v.1.4.2), RcppAnnoy(v.0.0.20), RColorBrewer(v.1.1-3), iterators(v.1.0.14), spatstat.explore(v.3.0-5), stringr(v.1.5.0), htmlwidgets(v.1.6.0), polyclip(v.1.10-4), biomaRt(v.2.54.0), shadowtext(v.0.1.2), gridGraphics(v.0.5-1), mgcv(v.1.8-41), globals(v.0.16.2), patchwork(v.1.1.2), spatstat.random(v.3.0-1), progressr(v.0.12.0), codetools(v.0.2-18), GO.db(v.3.16.0), gtools(v.3.9.4), prettyunits(v.1.1.1), dbplyr(v.2.2.1), gtable(v.0.3.1), DBI(v.1.1.3), ggfun(v.0.0.9), tensor(v.1.5), httr(v.1.4.4), KernSmooth(v.2.23-20), stringi(v.1.7.8), progress(v.1.2.2), reshape2(v.1.4.4), farver(v.2.1.1), viridis(v.0.6.2), ggtree(v.3.6.2), xml2(v.1.3.3), boot(v.1.3-28.1), lme4(v.1.1-31), restfulr(v.0.0.15), ggplotify(v.0.1.0), scattermore(v.0.8), bit(v.4.0.5), scatterpie(v.0.1.8), spatstat.data(v.3.0-0), ggraph(v.2.1.0), pkgconfig(v.2.0.3) and knitr(v.1.41)

message(paste0("This is hpgltools commit: ", get_git_commit()))
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 911e7d4beebdc73267ec4be631a305574289efd3
## This is hpgltools commit: Tue Jan 17 10:36:44 2023 -0500: 911e7d4beebdc73267ec4be631a305574289efd3
this_save <- paste0(gsub(pattern="\\.Rmd", replace="", x=rmd_file), "-v", ver, ".rda.xz")
##message(paste0("Saving to ", this_save))
##tmp <- sm(saveme(filename=this_save))
LS0tCnRpdGxlOiAiUGxheWluZyB3aXRoIHNvbWUgbmV3IHNjUk5BU2VxIGRhdGE6IEErQiAyMDIzMDEuIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIHRoZW1lOiByZWFkYWJsZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgZm9udC1zaXplOiAxNnB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDE2cHg7Cn0KcHJlIHsKIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCJocGdsdG9vbHMiKQpsaWJyYXJ5KCJyZXRpY3VsYXRlIikKdHQgPC0gZGV2dG9vbHM6OmxvYWRfYWxsKCJ+L2hwZ2x0b29scyIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHdpZHRoPTEyMCwKICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3M9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQpsdWFfZmlsdGVycyA8LSBybWFya2Rvd246OnBhbmRvY19sdWFfZmlsdGVyX2FyZ3MoInBhbmRvYy16b3R4dC5sdWEiKQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cz00LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemU9MTApKQpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQ9IiVZJW0lZCIpCnByZXZpb3VzX2ZpbGUgPC0gIiIKdmVyIDwtICIyMDIzMDEiCgojI3RtcCA8LSBzbShsb2FkbWUoZmlsZW5hbWU9cGFzdGUwKGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iIiwgeD1wcmV2aW91c19maWxlKSwgIi12IiwgdmVyLCAiLnJkYS54eiIpKSkKcm1kX2ZpbGUgPC0gImluZGV4LlJtZCIKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwdXJycikKbGlicmFyeShza2ltcikKbGlicmFyeShHU0VBQmFzZSkKYGBgCgojIE5vdGVzOgoKIyMgUXVlcnlpbmcgRHIuIE1vc3NlciAyMDIzMDEyMwoKKiBRdWVzdGlvbiBmcm9tIE5hamliOiBDb25uZWN0aW9uIGJldHdlZW4gNScgbGlicmFyaWVzIGFuZCBWREosIHRhZ3MKICBhcmUgaW4gZmFjdCBzaGFyZWQgZnJvbSBWREovc3VyZmFjZSB0byB0aGUgJ3BhcmVudCcgR0VNLiAgQXByaWw6IERvCiAgR0VNIGdlbmVyYXRpb24sIHRoZW4gc3BsaXQgdGhlIHJlc3VsdGluZyBjRE5BIGF0IGNsZWFudXAgc3RlcCB0byB0aGUKICBwcm90ZWluIGJhcmNvZGUgc2V0IChhdCBiZWFkIGNsZWFudXApLCB0aGVuIGdvIGludG8gcmVndWxhciA1JwogIGV4cHJlc3Npb24gbGlicmFyeSwgZnJvbSB3aGljaCBhIHNldCBvZiBzcGVjaWZpYyBwcmltZXJzIGFyZSB1c2VkIHRvCiAgZW5yaWNoIHRoZSBWREogcG9ydGlvbi4gIFRodXMgYWxsIHRocmVlIGhhdmUgdGhlIHNhbWUgcGFyZW50YWwgY2VsbAogIHRhZy4KKiBRdWVzdGlvbiBmcm9tIEFwcmlsOiBEbyB3ZSBuZWVkIHRvIGNoYW5nZSB0aGUgaW5wdXRzOyBmcm9tIGhvdyBtYW55IGNlbGxzCiAgZGlkIHdlIHJlY292ZXIgcmVhZHM/ICBIZSB3YW50cyAxMCwwMDAgY2VsbHMgLyBhbmltYWwuICBBcHJpbCBwb29sZWQKICBhY2NvcmRpbmcgdG8gMTB4IHJlY29tbWVuZGF0aW9uczogMTo0IFZESjpleHByZXNzaW9uIGxpYnJhcnkuCiAgU3VyZmFjZSBwcm90ZWluIGxpYnJhcmllcyBoYXZlIG5vdCB5ZXQgYmVlbiBydW4sIHRoZXNlIGhhdmUgc2VwYXJhdGUKICBpbmRleGVzIGFuZCBhcmUgbXVjaCBzaG9ydGVyOyBpbiBjb250cmFzdCB0aGUgVkRKL2V4cHJlc3Npb24KICBsaWJyYXJpZXMgYXJlIHJlbGF0aXZlbHkgc2ltaWxhciAoNTUwIHZzIDYzMCBudCkuCiogRnJvbSBOYWppYjogd2h5IDUnIHZzIDMnIGNhcHR1cmU/ICBOb3QgZW50aXJlbHkgY2VydGFpbiwgYnV0IDUnIGtpdAogIGlzIHVzZWQgZm9yIGltbXVub2xvZ3kgdXN1YWxseSwgYXBwYXJlbnRseS4KKiBRdWVzdGlvbnMgYWJvdXQgdGhlIGNlbGwgcHJlcGFyYXRpb25zOiB3aGF0IHdhcyB0aGUgc3RhdHVzIG9mIHRoZQogIGNlbGxzIGF0IEdFTSBnZW5lcmF0aW9uPyAgQ2FuIHdlIGxlYXJuIGlmIHRoYXQgYWZmZWN0ZWQgdGhlIG51bWJlcgogIG9mIGNlbGxzIG9ic2VydmVkLCByZWFkcy9jZWxsPyAgQ2FuIEFwcmlsIHR3ZWFrIHRoZSBzZXF1ZW5jaW5nCiAgbGlicmFyeSBpbnB1dHMgdG8gaGVscCBldmVuIG91dCB0aGUgZGlmZmVyZW5jZXMgYWNyb3NzIHNhbXBsZXMgZm9yCiAgZnV0dXJlIHJ1bnMgKGdpdmVuIHRoYXQgdGhlcmUgYXJlIDMoSSB0aGluaykgY29taW5nIHVwKT8KICBDb252ZXJzZWx5LCBzaG91bGQgSSByZXByb2Nlc3MgdGhlIHNhbXBsZXMgc28gdGhhdCBhbGwgc2FtcGxlcyBhcmUKICBwZXJjZWl2ZWQgYXMgaGF2aW5nIHRoZSBsZWFzdCBudW1iZXIgb2YgY2VsbHMgb2JzZXJ2ZWQ/CiogUXVpY2sgcHJpbWVyIG9mIHJlY29tYmluYXRpb24gYW5kIFYoRClKOiBlYWNoIHJlY2VwdG9yIGlzCiAgaGV0ZXJvZGltZXIsIGhlYXZ5IGNoYWluIGhhcyB0aGUgY29uc3RhbnQsIFYsIEQsIGFuZCBKLiAgQ29udmVyc2VseSB0aGUKICBsaWdodCBjaGFpbiBoYXMgdGhlIGNvbnN0YW50IGFuZCB0aGUgVkouICBJbiBULWNlbGxzLCBhbGxlbGljCiAgZXhjbHVzaW9uIHJlc3VsdHMgaW4gYSBzaW5nbGUgcmVzdWx0aW5nIGV4cHJlc2lzb24gZXZlbnQuICBJbgogIEItY2VsbHMgdGhlcmUgbWF5IGJlIG1hbnkuICBJbi10b3RvIHRoZXJlIGlzIGEgbmVhci1pbmZpbml0ZSByYW5kb20KICBzZXQgb2YgcG9zc2liaWxpdGllczsgdGhlc2UgYXJlIGFsbCByYW5kb21seSBnZW5lcmF0ZWQgZHVyaW5nCiAgZGV2ZWxvcG1lbnQuICBCLWNlbGxzIGFyZSBjb25zdGFudGx5IG1ha2luZyBuZXcgY29tYmluYXRpb25zIGR1cmluZwogIHRoZSBsaWZlc3BhbiAtLSBULWNlbGxzIGFyZSBsb25nLWxpdmVkIGFuZCBjb25zdGFudCAoRGF2ZSBleHBsYWluZWQKICBhbiBleHBlcmltZW50IHdoZXJlIHNvbWVvbmUgdHJhbnNmZXJyZWQgVC1jZWxscyBmcm9tIG1vdXNlIHRvIG1vdXNlCiAgb3ZlciB0aGUgY291cnNlIG9mIG1hbnkgeWVhcnMpLiAgKG9yaWVudGF0aW9uIDUnLT4zJz8gIGNvbnN0YW50LT5ELT5KPykKKiBFdmVyeSBjZWxsIHRoYXQgd2FzIHNlcXVlbmNlZCBpcyBpbiB0aGVvcnkgYSBULWNlbGwgKCJsdW5nIGluZmVjdGVkCiAgVC1jZWxscyBmcm9tIG1pY2UpLCBzbyBzaG91bGQgYWxsIGhhdmUgVkRKIHNlcXVlbmNlcy4gIEEgc21hbGwgc2V0CiAgb2YgaW1tdW5lIGNlbGxzIChOSykgZG8gbm90IGhhdmUgdGhlc2UuCgojIyBDaGVja2luIG1lZXRpbmcgd2l0aCBEci4gUGFyayAyMDIzMDEyNAoKKiBBcHJpbCBkZXNjcmliaW5nIG9ic2VydmF0aW9ucywgY2VsbCAjcyBldGMuCiogRGVmaW5pdGVseSBub3QgRkFDUywgcGVyZm9ybWVkIG5lZ2F0aXZlIGVucmljaG1lbnQgd2hpY2ggaW4gdGhlb3J5IGdldHMgPj0gOTAlIHB1cmUgc2FtcGxlcy4KKiBOYWppYiBxdWVyeTogbGltaXQgdG8gVkRKIGNlbGxzIG9ubHk/ICBVbmxpa2VseS4KKiBJdCBzb3VuZHMgdG8gbWUgdGhhdCB0aGUgbGlrZWx5IHRhc2tzIEkgcGVyZm9ybSBhcmUgbm90IG5lZWRlZCBieSBEci4gUGFyay4KCiMgVE9ETzoKCiogQ2hlY2sgZXhwcmVzc2lvbiBvZiB0aGUgc2FtcGxlcyBmb3IgbGV2ZWxzIG9mIENEMy4KKiBQcmludCBvdXQgZmlsdGVyZWQgYW1vdW50cyBvbiBhIHBlci1zYW1wbGUgYmFzaXMuCiogUXVlcnkgbVNpZ0RCIGNhdGVnb3JpZXMgb2YgaW50ZXJlc3QgKGN1cnJlbnRseSBJIGdyYWJiZWQgdGhlIH45OAogIGluZmx1ZW56YSBjYXRlZ29yaWVzIGFyYml0cmFyaWx5KTogZS5nLiBmaWd1cmUgb3V0IHdoaWNoIG9uZXMgYXJlCiAgYWN0dWFsbHkgcmVsZXZhbnQuCgojIENoYW5nZWxvZwoKIyBQcmVwcm9jZXNzaW5nIHdpdGggY2VsbHJhbmdlcgoKSSBkb3dubG9hZGVkIGEgbmV3IHZlcnNpb24gb2YgY2VsbHJhbmdlciBhbG9uZyB3aXRoIHRoZSB2YXJpb3VzCnJlZmVyZW5jZSBmaWxlcyBwcm92aWRlZCBieSAxMHggZm9yIHRoZSBWRChKKSByZWZlcmVuY2VzIGV0Yy4gIEkgZ290IGEKYml0IGRpc3RyYWN0ZWQgYnkgdGhlIHBpcGVsaW5lIGxhbmd1YWdlIGltcGxlbWVudGVkIGJ5IDEweCBjYWxsZWQKJ21hcnRpYW4nLiAgIEkgaGF2ZSB0aGUgZmVlbGluZyB0aGF0IGl0IG1pZ2h0IHByb3ZlIGEgZ29vZCB0aGluZyB0bwpwbGF5IHdpdGguCgpIZXJlIGFyZSB0aGUgY29tbWFuZHMgSSByYW4gdG8gc2VwYXJhdGUgdGhlIHNhbXBsZXMgYW5kIHBlcmZvcm0gdGhlCmFsaWdubWVudHMuICBUaGVyZSBhcmUgNCBzYW1wbGUgbmFtZXMgYW5kIGVhY2ggd2FzIGRvbmUgd2l0aCBvbmUgcnVuCm9mIHRoZSAnbm9ybWFsJyBHRVggc2NSTkFTZXEgbWV0aG9kIGFuZCBvbmUgb2YgdGhlIChuZXcgdG8gbWUpIFYoRClKCmxpYnJhcnkuCgojIFJlcnVuIHRoZSBwaXBlbGluZSB3aXRoIG11bHRpCgpBcHJpbCBraW5kbHkgc2VudCBzb21lIGluZm9ybWF0aW9uIGZyb20gMTB4IHdoaWNoIHNob3dzIHRoYXQgSSBzaG91bGQKaGF2ZSB1c2VkIHRoZSBtdWx0aSBwaXBsaW5lIHdoZW4gcHJlcHJvY2Vzc2luZyB0aGUgZGF0YS4KCkludHJhLU11c2N1bGFyIHZzLiBOYXNhbAoKSSB3cm90ZSA0IHNlcGFyYXRlIGNvbmZpZ3VyYXRpb24gY3N2IGZpbGVzIHVzaW5nIHRoZSB0ZW1wbGF0ZXMgSQpkb3dubG9hZGVkIGFuZCBmb2xsb3dpbmcgYSBsaXR0bGUgcmVhZGluZy4gIEl0IHNlZW1lZCB0byBtZSB0aGF0IEkKc2hvdWxkIGJlIGFibGUgdG8gcHJvY2VzcyB0aGVtIGFsbCBhcyBhIHNpbmdsZSBjc3YgZmlsZSwgYnV0IHdoZW4gSQphdHRlbXB0ZWQgdGhhdCwgY2VsbHJhbmdlciBkaWQgbm90IHJlYWN0IHdlbGwuICBJdCBhbHNvIHRvb2sgYSBmZXcKdHJpZXMgYmVmb3JlIEkgZ290IHRoZSB2YXJpb3VzIHJlZmVyZW5jZS9saWJyYXJ5IG9wdGlvbnMgY29ycmVjdC4KCk5vdGUgdGhhdCBvbmNlIGNlbGxyYW5nZXIgc3VjY2Vzc2Z1bGx5IHJhbiBmb3IgdGhlIHNhbXBsZXMgSSBtb3ZlZAp0aGVtIHRvIHRoZSBtdWx0aS8gZGlyZWN0b3J5IHNvIHRoYXQgSSBjYW4gY29tcGFyZSB0aGUgb3V0cHV0cyB0byB3aGVuCkkgc2ltcGx5IGRpZCB0aGUgJ2NvdW50JyBvcGVyYXRpb24uCgpUaGUgZm9sbG93aW5nIGludm9jYXRpb25zIG9mIGNlbGxyYW5nZXIgYWxsIGFwcGVhciB0byB3b3JrIHdpdGhvdXQgYW55CnByb2JsZW1zLiAgSWRlYWxseSBJIHdvdWxkIGxpa2UgdGhlbSB0byBiZSBkb25lIGluIGEgc2luZ2xlIHJ1biwgdGhvdWdoLgoKIyMjIFNoZW5hbmlnYW5zIGZvciBjb21iaW5lZCBWREogc2FtcGxlcwoKTXkgYXR0ZW1wdHMgc28gZmFyIHRvIHVzZSB0aGUgY3N2IGNvbmZpZ3VyYXRpb24gdG8gY29uY2F0ZW5hdGUKbXVsdGlwbGUgdmRqIGxpYnJhcmllcyBoYXZlIG5vdCB3b3JrZWQsIHNvIEkgY2hvc2UgdG8gZG8gaXQgdGhlIHN0dXBpZAp3YXksIHdoaWNoIGlzIHdoYXQgSSBzaG91bGQgaGF2ZSBqdXN0IGRvbmUgdG8gYmVnaW4gd2l0aC4gIENhdmVhdCwgaXQKd29ya3MgZmluZSBmb3IgdGhlIGdleCBsaWJyYXJpZXMgdG8gZG8gaXQgdGhlIHdheSB0aGUgZG9jdW1lbnRhdGlvbgpzdWdnZXN0cy4KCmBgYHtiYXNoIHNoZW5hbmlnYW5zLCBldmFsPUZBTFNFfQpjZCBwcmVwcm9jZXNzaW5nCmZvciBpIGluIFIxIFIyOyBkbwogICAgZm9yIGogaW4gQ29udHJvbCBNb2NrX01leDA5IElNX01leDA5IElOX01leDA5OyBkbwogICAgICAgIEFfZmlsZT0kKC9iaW4vbHMgQV8ke2p9X1ZESipfJHtpfV8wMDEuZmFzdHEuZ3opCiAgICAgICAgQl9maWxlPSQoL2Jpbi9scyBCXyR7an1fVkRKKl8ke2l9XzAwMS5mYXN0cS5neikKICAgICAgICBvdXRfZmlsZT0iQ29uY2F0XyR7an1fVkRKXyR7aX0uZmFzdHEuZ3oiCiAgICAgICAgY3BfY21kPSJjcCAke0FfZmlsZX0gJHtvdXRfZmlsZX0iCiAgICAgICAgZWNobyAiUnVubmluZzogJHtjcF9jbWR9LiIKICAgICAgICBldmFsICRjcF9jbWQKICAgICAgICBjYXRfY21kPSJjYXQgJHtCX2ZpbGV9ID4+ICR7b3V0X2ZpbGV9IgogICAgICAgIGVjaG8gIlJ1bm5pbmc6ICR7Y2F0X2NtZH0uIgogICAgICAgIGV2YWwgJGNhdF9jbWQKICAgIGRvbmUKZG9uZQpgYGAKCmBgYHtiYXNoIGNlbGxyYW5nZXJ2MiwgZXZhbD1GQUxTRX0KbW9kdWxlIGFkZCBjZWxscmFuZ2VyCmNlbGxyYW5nZXIgbXVsdGkgLS1pZCBjb250cm9sIC0tY3N2IHNhbXBsZV9zaGVldHMvbXVsdGlfY29uZmlnX3RyeTA1X2NvbnRyb2wuY3N2CmNlbGxyYW5nZXIgbXVsdGkgLS1pZCBtb2NrIC0tY3N2IHNhbXBsZV9zaGVldHMvbXVsdGlfY29uZmlnX3RyeTA1X21vY2suY3N2CmNlbGxyYW5nZXIgbXVsdGkgLS1pZCBtIC0tY3N2IHNhbXBsZV9zaGVldHMvbXVsdGlfY29uZmlnX3RyeTA1X20uY3N2CmNlbGxyYW5nZXIgbXVsdGkgLS1pZCBuIC0tY3N2IHNhbXBsZV9zaGVldHMvbXVsdGlfY29uZmlnX3RyeTA1X24uY3N2CgptdiBjb250cm9sIG1vY2sgbSBuIDAxbXVsdGlfY29tYmluZWQvCmBgYAoKIyBBbm5vdGF0aW9ucwoKYGBge3IgaGczOF9mdW59CmFubm90YXRpb25zIDwtIGxvYWRfYmlvbWFydF9hbm5vdGF0aW9ucygpJGFubm90YXRpb24KYnJpZWYgPC0gdW5pcXVlKGFubm90YXRpb25zWywgYygiaGduY19zeW1ib2wiLCAiZGVzY3JpcHRpb24iKV0pCmBgYAoKIyBTZXQgcHJlZml4IG9mIHRoZSBkYXRhCgpgYGB7ciBwcmVmaXh9CnByZWZpeCA8LSAibXVsdGkiCiMgcHJlZml4IDwtICIwMW11bHRpX2NvbWJpbmVkIgpgYGAKCiMgU29tZSBzdG9sZW4gY29kZQoKSGVyZSBpcyBhIHNuaXBwZXQgb2YgY29kZSBjb3B5LXBhc3RlZCBmcm9tOgoKaHR0cHM6Ly91Y2RhdmlzLWJpb2luZm9ybWF0aWNzLXRyYWluaW5nLmdpdGh1Yi5pby8yMDIwLUFkdmFuY2VkX1NpbmdsZV9DZWxsX1JOQV9TZXEvZGF0YV9hbmFseXNpcy9WREpfQW5hbHlzaXNfZml4ZWQKCndoaWNoLCBpZiBJIHJlYWQgaXQgY29ycmVjdGx5LCB3aWxsIHJlYWQgaW4gdGhlIHZkaiBvdXRwdXQgYW5kIGFkZCBpdAphcyBhIHNldCBvZiBhbm5vdGF0aW9ucyB0byB0aGUgc2V1cmF0IG9iamVjdC4gTm90ZSwgSSBtYWRlIHNvbWUKY2hhbmdlcyB0byBpdCBiZWNhdXNlIEkgc2ltcGx5IGNhbm5vdCBoZWxwIG15c2VsZi4KCkkgdGhpbmsgc29tZSB2YXJpYW50IG9mIHRoaXMgZnVuY3Rpb24gbWlnaHQgYmUgdXNlZnVsIGZvciB0aGUKaHBnbHRvb2xzIGlmIEkgdGhpbmsgSSB3aWxsIHVzZSBpdCBhZ2Fpbi4uLgoKYGBge3Igc3RvbGVuX2NvZGV9CmFkZF9jbG9ub3R5cGUgPC0gZnVuY3Rpb24oc2V1cmF0X29iaiwgbmFtZT0iY29udHJvbCIsIHR5cGU9InQiLCBwcmVmaXg9cHJlZml4KSB7CiAgdmRqX2RpcmVjdG9yeSA8LSBnbHVlOjpnbHVlKCJ7cHJlZml4fS97bmFtZX0vb3V0cy9wZXJfc2FtcGxlX291dHMve25hbWV9L3Zkal90IikKICB2ZGpfY3N2IDwtIGdsdWU6OmdsdWUoInt2ZGpfZGlyZWN0b3J5fS9maWx0ZXJlZF9jb250aWdfYW5ub3RhdGlvbnMuY3N2IikKICByZWZlcmVuY2VfY3N2IDwtIGdsdWU6OmdsdWUoInt2ZGpfZGlyZWN0b3J5fS9jbG9ub3R5cGVzLmNzdiIpCiAgdGNyIDwtIHJlYWRyOjpyZWFkX2Nzdih2ZGpfY3N2LCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQogIHJlZiA8LSByZWFkcjo6cmVhZF9jc3YocmVmZXJlbmNlX2Nzdiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKCiAgdGNyX2R1cGxpY2F0ZV9iYXJjb2RlX2lkeCA8LSBkdXBsaWNhdGVkKHRjcltbImJhcmNvZGUiXV0pCiAgdGNyX25vZHVwIDwtIHRjclshdGNyX2R1cGxpY2F0ZV9iYXJjb2RlX2lkeCwgXQoKICBib3RoIDwtIGFzLmRhdGEuZnJhbWUobWVyZ2UodGNyX25vZHVwLCByZWYsIGJ5Lng9InJhd19jbG9ub3R5cGVfaWQiLCBieS55PSJjbG9ub3R5cGVfaWQiKSkKICByb3duYW1lcyhib3RoKSA8LSBib3RoW1siYmFyY29kZSJdXQogIGJvdGhbWyJiYXJjb2RlIl1dIDwtIE5VTEwKCiAgbmV3IDwtIFNldXJhdDo6QWRkTWV0YURhdGEob2JqZWN0PXNldXJhdF9vYmosIG1ldGFkYXRhPWJvdGgpCiAgcmV0dXJuKG5ldykKfQpgYGAKCiMgTG9hZCB0aGUgZGF0YSBpbnRvIFNldXJhdCBhbmQgcG9rZSBhdCBpdAoKVGhlIGZvbGxvd2luZyBibG9jayBpcyBtb3N0bHkgYSBjdXQvcGFzdGUgb2YgaXRzZWxmIHdoZXJlIEkgc2V0IHRoZQoob3ZlcilzaW1wbGlmaWVkIG5hbWUgb2YgZWFjaCBzYW1wbGUuICBUaGlzIHRoZW4gYmVjb21lcyB0aGUgdGVtcGxhdGUKZm9yIHRoZSBwYXRoIGFuZCBwYXJhbWV0ZXJzIHVzZWQgdG8gcmVhZCB0aGUgZGF0YSwgY3JlYXRlIGEgc2V1cmF0Cm9iamVjdCwgYW5kIGFkZCB0aGUgY2xvbm90eXBlIGRhdGEgZnJvbSB0aGUgdmRqIHJ1bi4KCmBgYHtyIHNldXJhdF9sb2FkaW5nfQpuYW1lIDwtICJjb250cm9sIgpwYXRoIDwtIGdsdWU6OmdsdWUoIntwcmVmaXh9L3tuYW1lfS9vdXRzL3Blcl9zYW1wbGVfb3V0cy97bmFtZX0vY291bnQvc2FtcGxlX2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4IikKYV9jb250cm9sIDwtIFNldXJhdDo6UmVhZDEwWChwYXRoKSAlPiUKICBDcmVhdGVTZXVyYXRPYmplY3QocHJvamVjdCA9IG5hbWUpICU+JQogIGFkZF9jbG9ub3R5cGUobmFtZSA9IG5hbWUpCgpuYW1lIDwtICJtb2NrIgpwYXRoIDwtIGdsdWU6OmdsdWUoIntwcmVmaXh9L3tuYW1lfS9vdXRzL3Blcl9zYW1wbGVfb3V0cy97bmFtZX0vY291bnQvc2FtcGxlX2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4IikKYV9tb2NrIDwtIFNldXJhdDo6UmVhZDEwWChwYXRoKSAlPiUKICBDcmVhdGVTZXVyYXRPYmplY3QocHJvamVjdCA9IG5hbWUpICU+JQogIGFkZF9jbG9ub3R5cGUobmFtZSA9IG5hbWUpCgpuYW1lIDwtICJtIgpwYXRoIDwtIGdsdWU6OmdsdWUoIntwcmVmaXh9L3tuYW1lfS9vdXRzL3Blcl9zYW1wbGVfb3V0cy97bmFtZX0vY291bnQvc2FtcGxlX2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4IikKYV9tIDwtIFNldXJhdDo6UmVhZDEwWChwYXRoKSAlPiUKICBDcmVhdGVTZXVyYXRPYmplY3QocHJvamVjdCA9IG5hbWUpICU+JQogIGFkZF9jbG9ub3R5cGUobmFtZSA9IG5hbWUpCgpuYW1lIDwtICJuIgpwYXRoIDwtIGdsdWU6OmdsdWUoIntwcmVmaXh9L3tuYW1lfS9vdXRzL3Blcl9zYW1wbGVfb3V0cy97bmFtZX0vY291bnQvc2FtcGxlX2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4IikKYV9uIDwtIFNldXJhdDo6UmVhZDEwWChwYXRoKSAlPiUKICBDcmVhdGVTZXVyYXRPYmplY3QocHJvamVjdCA9IG5hbWUpICU+JQogIGFkZF9jbG9ub3R5cGUobmFtZSA9IG5hbWUpCmBgYAoKRm9yIHRoZSBtb21lbnQgSSB3YW50IHRvIGJlIGFibGUgdG8gcGxheSB3aXRoIHRoZSBpbmRpdmlkdWFsCnNhbXBsZXMgYXMgd2VsbCBhcyB0aGUgYWdncmVnYXRlIHNvIHRoYXQgSSBjYW4gYmV0dGVyIHVuZGVyc3RhbmQgdGhlCmRhdGEuICBTbyBJIGd1ZXNzIGl0IHdvcmtzIG91dCB0aGF0IEkgZGlkbid0IGZpZ3VyZSBvdXQgaG93IHRvIHJ1biBhbGwKdGhlIHNhbXBsZXMgYXQgdGhlIHNhbWUgdGltZSB2aWEgJ2NlbGxyYW5nZXIgbXVsdGknLgoKSSBhbSBwcmV0dHkgc3VyZSBTZXVyYXQncyBtZXJnZSgpIG92ZXJsb2FkIGFsbG93cyBvbmUgdG8ganVzdCBkbwonbWVyZ2UoYSxiLGMsZCxlLi4uKScgYnV0IEkgYW0gbm90IHVzaW5nIHRoYXQuCgpgYGB7ciBtZXJnZV9zYW1wbGVzfQphbGwgPC0gbWVyZ2UoYV9jb250cm9sLCBhX21vY2spICAlPiUKICBtZXJnZShhX20pICU+JQogIG1lcmdlKGFfbikKYGBgCgojIFF1ZXJ5IGZvciBjbG9ub3R5cGVzIGluIHRoZSBpbmRpdmlkdWFsIHNhbXBsZXMgYW5kIHRoZSBmdWxsIGRhdGFzZXQuCgpgYGB7ciBxdWVyeV9jbG9ub3R5cGVzfQpjb250cm9sX2Nsb25vIDwtICFpcy5uYShhX2NvbnRyb2xbWyJyYXdfY2xvbm90eXBlX2lkIl1dKQpzdW1tYXJ5KGNvbnRyb2xfY2xvbm8pCgptb2NrX2Nsb25vIDwtICFpcy5uYShhX21vY2tbWyJyYXdfY2xvbm90eXBlX2lkIl1dKQpzdW1tYXJ5KG1vY2tfY2xvbm8pCgptX2Nsb25vIDwtICFpcy5uYShhX21bWyJyYXdfY2xvbm90eXBlX2lkIl1dKQpzdW1tYXJ5KG1fY2xvbm8pCgpuX2Nsb25vIDwtICFpcy5uYShhX25bWyJyYXdfY2xvbm90eXBlX2lkIl1dKQpzdW1tYXJ5KG5fY2xvbm8pCmBgYAoKIyBJbml0aWFsIENsdXN0ZXJzCgpJIHdhbnQgdG8gdGFrZSBhIGNvdXBsZSBtaW51dGVzIHRvIGFkZCBzb21lIGFubm90YXRpb25zIHRvIHRoZSBzZXVyYXQKb2JqZWN0LCBub3RhYmx5IEkgd2FudCB0byBzdGF0ZSB0aGUgaWRlbnRpdHkgcmVsYXRpb25zaGlwcyB3aXRoIHNvbWUKc29ydCBvZiBuYW1lLgoKVGh1cyBJIHdpbGwgbWFrZSBhIHZlY3RvciBvZiB0aGUgdGhlIHNhbXBsZSBJRHMgYW5kIGZvciBlYWNoIG9uZSBtYWtlCmEgY2F0ZWdvcnkgb2Ygc2VsZi9ub3Qtc2VsZi4gIE5vdGUgdGhhdCBTZXVyYXQgY29tZXMgd2l0aCBhIGZ1bmN0aW9uCidGaW5kQ29uc2VydmVkTWFya2VycygpJyBvciBzb21ldGhpbmcgbGlrZSB0aGF0IHdoaWNoIGNvbXBhcmVzIGVhY2gKc2VsZiB0byBhbGwgb3RoZXIgc2FtcGxlcywgc28gdGhpcyBtYXkgYmUgcmVkdW5kYW50OyBidXQgaXQgaXMga2luZCBvZgpuaWNlIHRvIGJlIGFibGUgdG8gc2VlIHRoZSBjYXRlZ29yaWVzIGFzIGEgc2V0IG9mIGJpbmFyeSBpbmRleGVzLgoKYGBge3IgaW5pdGlhbF9jbHVzdGVyc30KY2x1c3Rlcl9sZXR0ZXJzIDwtIGFzLmZhY3RvcihMRVRURVJTW0lkZW50cyhvYmplY3Q9YWxsKV0pCm5hbWVzKGNsdXN0ZXJfbGV0dGVycykgPC0gY29sbmFtZXMoeD1hbGwpCmNvbnRyb2xfaWRzIDwtIGFzLmNoYXJhY3RlcihjbHVzdGVyX2xldHRlcnMpCm1vY2tfaWRzIDwtIGNvbnRyb2wKbV92YWNjaW5lX2lkcyA8LSBjb250cm9sCm5fdmFjY2luZV9pZHMgPC0gY29udHJvbApgYGAKCk5vdyB0aGF0IEkgaGF2ZSA0IGlkZW50aWNhbCB2ZWN0b3JzLCBmaWxsIHRoZW0gd2l0aCBteSBjaG9zZW4gbmFtZXMKZm9yIHRoZSBzYW1wbGVzIGFuZCB3aGV0aGVyIHRoZXkgZG8obnQpIGhhdmUgdGhhdCBpZGVudGl0eS4KCmBgYHtyIHNlbGZfbm90c2VsZn0KY29udHJvbF9pZHggPC0gY29udHJvbCA9PSAiQSIKY29udHJvbFtjb250cm9sX2lkeF0gPC0gIkNvbnRyb2wiCmNvbnRyb2xbIWNvbnRyb2xfaWR4XSA8LSAiU3RpbXVsYXRlZCIKbW9ja19pZHggPC0gbW9jayA9PSAiQiIKbW9ja1ttb2NrX2lkeF0gPC0gIk1vY2siCm1vY2tbIW1vY2tfaWR4XSA8LSAiTm90IG1vY2siCm1fdmFjY2luZV9pZHggPC0gbV92YWNjaW5lID09ICJDIgptX3ZhY2NpbmVbbV92YWNjaW5lX2lkeF0gPC0gIk11c2N1bGFyIgptX3ZhY2NpbmVbIW1fdmFjY2luZV9pZHhdIDwtICJOb3QgTXVzY3VsYXIiCm5fdmFjY2luZV9pZHggPC0gbl92YWNjaW5lID09ICJEIgpuX3ZhY2NpbmVbbl92YWNjaW5lX2lkeF0gPC0gIk5hc2FsIgpuX3ZhY2NpbmVbIW5fdmFjY2luZV9pZHhdIDwtICJOb3QgTmFzYWwiCmBgYAoKTm93IGFkZCB0aGVzZSBjYXRlZ29yaWVzIHRvIHRoZSBzYW1wbGUgbWV0YWRhdGEuICBJIHRoaW5rIHRoaXMgaXMgYQpnb29kIHBsYWNlIHRvIGNvbnNkaWVyIGhhdmluZyBhIHNhbXBsZSBzaGVldCBmcm9tIERyLiBQYXJrIHdpdGgKd2hhdGV2ZXIgb3RoZXIgcmFuZG9tIGluZm9ybWF0aW9uIG1pZ2h0IHByb3ZlIGludGVyZXN0aW5nIGFib3V0IHRoZQpzYW1wbGVzLgoKYGBge3IgYWRkX2lkZW50aXR5X21ldGF9CmFsbCA8LSBBZGRNZXRhRGF0YSgKICAgIG9iamVjdD1hbGwsCiAgICBtZXRhZGF0YT1jbHVzdGVyX2xldHRlcnMsCiAgICBjb2wubmFtZT0iY2x1c3Rlcl9sZXR0ZXJzIikKYWxsIDwtIEFkZE1ldGFEYXRhKAogICAgb2JqZWN0PWFsbCwKICAgIG1ldGFkYXRhPWNvbnRyb2wsCiAgICBjb2wubmFtZT0iY29udHJvbCIpCmFsbCA8LSBBZGRNZXRhRGF0YSgKICAgIG9iamVjdD1hbGwsCiAgICBtZXRhZGF0YT1tb2NrLAogICAgY29sLm5hbWU9Im1vY2siKQphbGwgPC0gQWRkTWV0YURhdGEoCiAgICBvYmplY3Q9YWxsLAogICAgbWV0YWRhdGE9bV92YWNjaW5lLAogICAgY29sLm5hbWU9Im11c2N1bGFyIikKYWxsIDwtIEFkZE1ldGFEYXRhKAogICAgb2JqZWN0PWFsbCwKICAgIG1ldGFkYXRhPW5fdmFjY2luZSwKICAgIGNvbC5uYW1lPSJuYXNhbCIpCmBgYAoKIyBGaWx0ZXJzIGFuZCBRQwoKTGV0IHVzIHN0YXJ0IGZpbHRlcmluZyB0aGUgZGF0YSwgbGVhZGluZyBvZmYgd2l0aCBhIGRlZmluaXRpb24gb2YgdGhlCm1pbmltdW0gbnVtYmVyIG9mIFJOQXMsIG1pbmltdW0gYW1vdW50IG9mIHJSTkEsIGFuZCBtYXhpbXVtCm1pdG9jaG9uZHJpYWwuICBJbiBhZGRpdGlvbiwgbGV0IHVzIHByaW50IGhvdyBtdWNoIG9mIGVhY2ggYXJlCm9ic2VydmVkIGJlZm9yZSBmaWx0ZXJpbmcuICBCZWZvcmUgd2UgY2FuIHByaW50L2ZpbHRlciB0aGVzZQphdHRyaWJ1dGVzLCB3ZSBtdXN0IHVzZSB0aGUgUGVyY2VudGFnZUZlYXR1cmVTZXQoKSB0byBnZXQgdGhlCm51bWJlcnMuLi4KCmBgYHtyIGZpbHRlcl90aHJlc2hvbGRzfQptaW5fbnVtX3JuYSA8LSAyMDAKbWluX3BjdF9yaWJvIDwtIDUKbWF4X3BjdF9taXRvIDwtIDIwCgphbGxbWyJwZXJjZW50X210Il1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KGFsbCwgcGF0dGVybj0iXm10LSIpCmFsbFtbInBlcmNlbnRfcmlibyJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChhbGwsIHBhdHRlcm49Il5ScFtzbF0iKQpgYGAKClNob3cgdGhlIHN0YXRlIGJlZm9yZSBmaWx0ZXJpbmcgb24gYSBwZXItY2VsbCBiYXNpcyBhY3Jvc3MgYWxsCnNhbXBsZXMuIFN0YXJ0IHdpdGggdGhlIG51bWJlciBvZiBjZWxscwoKCmBgYHtyIHNob3dfbnVtX2NlbGxzfQpzYW1wbGVfc3VtbWFyaWVzIDwtIGFzX3RpYmJsZShkYXRhLmZyYW1lKAogICAgImlkIiA9IGMoImNvbnRyb2wiLCAibW9jayIsICJtdXNjdWxhciIsICJuYXNhbCIpLAogICAgInN0YXJ0X2NlbGxzIiA9IGMoCiAgICAgICAgc3VtKGFsbEBtZXRhLmRhdGFbWyJvcmlnLmlkZW50Il1dID09ICJjb250cm9sIiksCiAgICAgICAgc3VtKGFsbEBtZXRhLmRhdGFbWyJvcmlnLmlkZW50Il1dID09ICJtb2NrIiksCiAgICAgICAgc3VtKGFsbEBtZXRhLmRhdGFbWyJvcmlnLmlkZW50Il1dID09ICJtIiksCiAgICAgICAgc3VtKGFsbEBtZXRhLmRhdGFbWyJvcmlnLmlkZW50Il1dID09ICJuIikpKSkKYGBgCgpgYGB7ciBzaG93X2FsbF9zdW1tYXJpZXN9CnNraW0oYWxsW1sicGVyY2VudF9tdCJdXSkKc2tpbShhbGxbWyJwZXJjZW50X3JpYm8iXV0pCnNraW0oYWxsW1sibkZlYXR1cmVfUk5BIl1dKQpza2ltKGFsbFtbIm5Db3VudF9STkEiXV0pCiMjIExlbmd0aCBhbmQgcmVhZHMgYXJlIGZvciBvbmx5IHRob3NlIGNlbGxzIHdpdGggY2xvbm90eXBlcy4Kc2tpbShhbGxbWyJyZWFkcyJdXSkKc2tpbShhbGxbWyJsZW5ndGgiXV0pCgojIyBIb3cgbWFueSBjZWxscyBoYXZlIHNwZWNpZmljIGNoYWlucyBhc3NvY2lhdGVkIHdpdGggdGhlbQpzdW0oIWlzLm5hKGFsbCRjaGFpbikpCmBgYAoKQW5kIG9uIGEgcGVyLXNhbXBsZSBiYXNpcyB3aXRoIChuZXcgdG8gbWUpIHNraW1yLCB3aGljaCBwcm92aWRlcyBhCnByZXR0eSBzdW1tYXJ5IG9mIHRoZSBjYXRlZ29yeSBvZiBpbnRlcmVzdC4gIFRoZSB3YXkgSSB3cm90ZSB0aGUKZm9sbG93aW5nIHN0YW56YXMgc2hvdWxkIGFsc28gYXBwZW5kIG5ldyBjb2x1bW5zIHRvIG15CnNhbXBsZV9zdW1tYXJpZXMgdGFibGUgY29tcHJpc2VkIG9mIHRoZSBtZWFuIHZhbHVlcyBmb3IgdGhlc2UKZWxlbWVudHMuCgpgYGB7ciBzdW1tYXJ5X2FkZGl0aW9ufQphZGRfc3VtbWFyaWVzIDwtIGZ1bmN0aW9uKHNhbXBsZV9zdW1tYXJpZXMsIG1ldGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3X2NvbHVtbiA9ICJ1bmtub3duIiwgZ3JvdXBfY29sdW1uID0gIm9yaWcuaWRlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFfcXVlcnkgPSAicGVyY2VudF9tdCIsIHN1bW1hcnlfcXVlcnkgPSAibnVtZXJpYy5tZWFuIikgewogIHNhbXBsZV9zdW1tYXJpZXNbW25ld19jb2x1bW5dXSA8LSBtZXRhICU+JQogICAgZ3JvdXBfYnkoISFybGFuZzo6c3ltKGdyb3VwX2NvbHVtbikpICU+JQogICAgc2tpbV90ZWUobWV0YV9xdWVyeSkgJT4lCiAgICBza2ltKG1ldGFfcXVlcnkpICU+JQogICAgZHBseXI6OnNlbGVjdChzdW1tYXJ5X3F1ZXJ5KQogIHJldHVybihzYW1wbGVfc3VtbWFyaWVzKQp9CmBgYAoKYGBge3Igc2hvd19zdGF0ZV9zYW1wbGVzfQpzYW1wbGVfc3VtbWFyaWVzIDwtIHNhbXBsZV9zdW1tYXJpZXMgJT4lCiAgYWRkX3N1bW1hcmllcyhhbGxAbWV0YS5kYXRhLCBuZXdfY29sdW1uID0gInN0YXJ0X3JuYSIsIG1ldGFfcXVlcnkgPSAibkZlYXR1cmVfUk5BIikgJT4lCiAgYWRkX3N1bW1hcmllcyhhbGxAbWV0YS5kYXRhLCBuZXdfY29sdW1uID0gIm5Db3VudF9STkEiLCBtZXRhX3F1ZXJ5ID0gIm5Db3VudF9STkEiKSAlPiUKICBhZGRfc3VtbWFyaWVzKGFsbEBtZXRhLmRhdGEsIG5ld19jb2x1bW4gPSAic3RhcnRfbXQiLCBtZXRhX3F1ZXJ5ID0gInBlcmNlbnRfbXQiKSAlPiUKICBhZGRfc3VtbWFyaWVzKGFsbEBtZXRhLmRhdGEsIG5ld19jb2x1bW4gPSAic3RhcnRfcmlibyIsIG1ldGFfcXVlcnkgPSAicGVyY2VudF9yaWJvIikgJT4lCiAgYWRkX3N1bW1hcmllcyhhbGxAbWV0YS5kYXRhLCBuZXdfY29sdW1uID0gInN0YXJ0X2Nsb25vIiwgbWV0YV9xdWVyeSA9ICJyZWFkcyIpCgpzYW1wbGVfc3VtbWFyaWVzCmBgYAoKT2ssIHRoYXQgd2FzIGZ1bjsgbGV0cyBsb29rIGF0IHRoaXMgaW5mb3JtYXRpb24gYXMgYSBzZXJpZXMgb2YgcGxvdHM6CgpgYGB7ciBmaWx0ZXJzX3FjfQpWbG5QbG90KGFsbCwgZmVhdHVyZXM9Im5GZWF0dXJlX1JOQSIsIHB0LnNpemU9MCkKVmxuUGxvdChhbGwsIGZlYXR1cmVzPSJwZXJjZW50X210IiwgcHQuc2l6ZT0wKQpWbG5QbG90KGFsbCwgZmVhdHVyZXM9InBlcmNlbnRfcmlibyIsIHB0LnNpemU9MCkKVmxuUGxvdChhbGwsIGZlYXR1cmVzPSJuQ291bnRfUk5BIiwgcHQuc2l6ZT0wKQpWbG5QbG90KGFsbCwgZmVhdHVyZXM9InJlYWRzIiwgcHQuc2l6ZT0wKQojIyBJIGFtIGN1cmlvdXMgYWJvdXQgdGhlIGxlbmd0aCBvZiB0aGUgY2xvbm90eXBlIHNlcXVlbmNlcy4KVmxuUGxvdChhbGwsIGZlYXR1cmVzPSJsZW5ndGgiLCBwdC5zaXplPTApCgpGZWF0dXJlU2NhdHRlcihhbGwsICJwZXJjZW50X3JpYm8iLCAicGVyY2VudF9tdCIpCkZlYXR1cmVTY2F0dGVyKGFsbCwgIm5Db3VudF9STkEiLCAibkZlYXR1cmVfUk5BIikKRmVhdHVyZVNjYXR0ZXIoYWxsLCAibkNvdW50X1JOQSIsICJwZXJjZW50X3JpYm8iKQpGZWF0dXJlU2NhdHRlcihhbGwsICJuQ291bnRfUk5BIiwgInBlcmNlbnRfbXQiKQpgYGAKCiMgRmlsdGVyIGZvciBvbmx5IGNlbGxzIHdpdGggYSBzdWZmaWNpZW50IG51bWJlciBvZiBSTkFzCgpgYGB7ciBmaWx0ZXJfbWluX3JuYXN9CnN1ZmZpY2llbnRfcm5hX29ic2VydmVkIDwtIFdoaWNoQ2VsbHMoYWxsLCBleHByZXNzaW9uID0gbkZlYXR1cmVfUk5BID49IG1pbl9udW1fcm5hKQpmaWx0IDwtIHN1YnNldChhbGwsIGNlbGxzID0gc3VmZmljaWVudF9ybmFfb2JzZXJ2ZWQpCgpzYW1wbGVfc3VtbWFyaWVzW1sibmNlbGxzX2ZpbHRybmEiXV0gPC0gYygKICAgIHN1bShmaWx0QG1ldGEuZGF0YVtbIm9yaWcuaWRlbnQiXV0gPT0gImNvbnRyb2wiKSwKICAgIHN1bShmaWx0QG1ldGEuZGF0YVtbIm9yaWcuaWRlbnQiXV0gPT0gIm1vY2siKSwKICAgIHN1bShmaWx0QG1ldGEuZGF0YVtbIm9yaWcuaWRlbnQiXV0gPT0gIm0iKSwKICAgIHN1bShmaWx0QG1ldGEuZGF0YVtbIm9yaWcuaWRlbnQiXV0gPT0gIm4iKSkKCnNhbXBsZV9zdW1tYXJpZXMgJT4lCiAgYWRkX3N1bW1hcmllcyhmaWx0QG1ldGEuZGF0YSwgbmV3X2NvbHVtbiA9ICJmaWx0MV9ybmEiLCBtZXRhX3F1ZXJ5ID0gIm5GZWF0dXJlX1JOQSIpICU+JQogIGFkZF9zdW1tYXJpZXMoZmlsdEBtZXRhLmRhdGEsIG5ld19jb2x1bW4gPSAiZmlsdDFfUk5BIiwgbWV0YV9xdWVyeSA9ICJuQ291bnRfUk5BIikgJT4lCiAgYWRkX3N1bW1hcmllcyhmaWx0QG1ldGEuZGF0YSwgbmV3X2NvbHVtbiA9ICJmaWx0MV9tdCIsIG1ldGFfcXVlcnkgPSAicGVyY2VudF9tdCIpICU+JQogIGFkZF9zdW1tYXJpZXMoZmlsdEBtZXRhLmRhdGEsIG5ld19jb2x1bW4gPSAiZmlsdDFfcmlibyIsIG1ldGFfcXVlcnkgPSAicGVyY2VudF9yaWJvIikgJT4lCiAgYWRkX3N1bW1hcmllcyhmaWx0QG1ldGEuZGF0YSwgbmV3X2NvbHVtbiA9ICJmaWx0MV9jbG9ubyIsIG1ldGFfcXVlcnkgPSAicmVhZHMiKSAtPgogIHNhbXBsZV9zdW1tYXJpZXMKYGBgCgpJbiB0aGUgbmV4dCBJIHdpbGwgY2hlY2sgdGhhdCB0aGUgbnVtYmVyIG9mIHJlYWRzL3JuYSBhY3Jvc3MgY2VsbHMgaXMKc3VmZmljaWVudCwgdGhhdCBmaWx0ZXIgZG9lcyBub3RoaW5nIGN1cnJlbnRseSwgd2hpY2ggSSB0aGluayBpcyBnb29kLgoKYGBge3IgZmlsdGVyX3JpYm9zb21hbH0KIyMgSSB0aGluayB0aGlzIGZpbHRlciBkb2VzIG5vdGhpbmcgaW4gaXRzIGN1cnJlbnQgZm9ybS4Kc3VmZmljaWVudGx5X29ic2VydmVkX2lkeCA8LSByb3dTdW1zKGZpbHQpID4gMwpzdW1tYXJ5KHN1ZmZpY2llbnRseV9vYnNlcnZlZF9pZHgpCmRpbShmaWx0KQpmaWx0IDwtIHN1YnNldChmaWx0LCBmZWF0dXJlcyA9IHJvd25hbWVzKGZpbHQpW3N1ZmZpY2llbnRseV9vYnNlcnZlZF9pZHhdKQpkaW0oZmlsdCkKCiMjIEtlZXAgY2VsbHMgd2l0aCBhdCBsZWFzdCBzb21lIHJpYm9zb21hbCByZWFkcwojIyBOb3RlIHRoZSBQZXJjZW50IGZ1bmN0aW9uIGFib3ZlIGFjdHVhbGx5IHB1dHMgaW4gYSBmbG9hdGluZyBwb2ludAojIyBudW1iZXIgZnJvbSAwLTEwMCwgbm90IChhcyBJIGFzc3VtZWQgZnJvbSAwLTEpLgpoaWdoX3JpYm9zb21hbCA8LSBXaGljaENlbGxzKGZpbHQsIGV4cHJlc3Npb24gPSBwZXJjZW50X3JpYm8gPj0gbWluX3BjdF9yaWJvKQpmaWx0IDwtIHN1YnNldChmaWx0LCBjZWxscyA9IGhpZ2hfcmlib3NvbWFsKQoKc2FtcGxlX3N1bW1hcmllc1tbIm5jZWxsc19maWx0cmlibyJdXSA8LSBjKAogICAgc3VtKGZpbHRAbWV0YS5kYXRhW1sib3JpZy5pZGVudCJdXSA9PSAiY29udHJvbCIpLAogICAgc3VtKGZpbHRAbWV0YS5kYXRhW1sib3JpZy5pZGVudCJdXSA9PSAibW9jayIpLAogICAgc3VtKGZpbHRAbWV0YS5kYXRhW1sib3JpZy5pZGVudCJdXSA9PSAibSIpLAogICAgc3VtKGZpbHRAbWV0YS5kYXRhW1sib3JpZy5pZGVudCJdXSA9PSAibiIpKQoKc2FtcGxlX3N1bW1hcmllcyAlPiUKICBhZGRfc3VtbWFyaWVzKGZpbHRAbWV0YS5kYXRhLCBuZXdfY29sdW1uID0gImZpbHQyX3JuYSIsIG1ldGFfcXVlcnkgPSAibkZlYXR1cmVfUk5BIikgJT4lCiAgYWRkX3N1bW1hcmllcyhmaWx0QG1ldGEuZGF0YSwgbmV3X2NvbHVtbiA9ICJmaWx0Ml9STkEiLCBtZXRhX3F1ZXJ5ID0gIm5Db3VudF9STkEiKSAlPiUKICBhZGRfc3VtbWFyaWVzKGZpbHRAbWV0YS5kYXRhLCBuZXdfY29sdW1uID0gImZpbHQyX210IiwgbWV0YV9xdWVyeSA9ICJwZXJjZW50X210IikgJT4lCiAgYWRkX3N1bW1hcmllcyhmaWx0QG1ldGEuZGF0YSwgbmV3X2NvbHVtbiA9ICJmaWx0Ml9yaWJvIiwgbWV0YV9xdWVyeSA9ICJwZXJjZW50X3JpYm8iKSAlPiUKICBhZGRfc3VtbWFyaWVzKGZpbHRAbWV0YS5kYXRhLCBuZXdfY29sdW1uID0gImZpbHQyX2Nsb25vIiwgbWV0YV9xdWVyeSA9ICJyZWFkcyIpIC0+CiAgc2FtcGxlX3N1bW1hcmllcwpgYGAKCkV4Y2x1ZGUgY2VsbHMgd2l0aCB0b28gbXVjaCBtaXRvY2hvbmRyaWFsIFJOQQoKIyMgTm93IGRyb3AgbWl0b2Nob25kcmlhbCBnZW5lcwoKYGBge3IgZmlsdGVyX21pdG99Cmxvd19taXRvY2hvbmRyaWFsIDwtIFdoaWNoQ2VsbHMoZmlsdCwgZXhwcmVzc2lvbiA9IHBlcmNlbnRfbXQgPD0gbWF4X3BjdF9taXRvKQpmaWx0IDwtIHN1YnNldChmaWx0LCBjZWxscyA9IGxvd19taXRvY2hvbmRyaWFsKQpkaW0oZmlsdCkKCnNhbXBsZV9zdW1tYXJpZXNbWyJuY2VsbHNfZmlsdG1pdG8iXV0gPC0gYygKICAgIHN1bShmaWx0QG1ldGEuZGF0YVtbIm9yaWcuaWRlbnQiXV0gPT0gImNvbnRyb2wiKSwKICAgIHN1bShmaWx0QG1ldGEuZGF0YVtbIm9yaWcuaWRlbnQiXV0gPT0gIm1vY2siKSwKICAgIHN1bShmaWx0QG1ldGEuZGF0YVtbIm9yaWcuaWRlbnQiXV0gPT0gIm0iKSwKICAgIHN1bShmaWx0QG1ldGEuZGF0YVtbIm9yaWcuaWRlbnQiXV0gPT0gIm4iKSkKCnNhbXBsZV9zdW1tYXJpZXMgJT4lCiAgYWRkX3N1bW1hcmllcyhmaWx0QG1ldGEuZGF0YSwgbmV3X2NvbHVtbiA9ICJmaWx0M19ybmEiLCBtZXRhX3F1ZXJ5ID0gIm5GZWF0dXJlX1JOQSIpICU+JQogIGFkZF9zdW1tYXJpZXMoZmlsdEBtZXRhLmRhdGEsIG5ld19jb2x1bW4gPSAiZmlsdDNfUk5BIiwgbWV0YV9xdWVyeSA9ICJuQ291bnRfUk5BIikgJT4lCiAgYWRkX3N1bW1hcmllcyhmaWx0QG1ldGEuZGF0YSwgbmV3X2NvbHVtbiA9ICJmaWx0M19tdCIsIG1ldGFfcXVlcnkgPSAicGVyY2VudF9tdCIpICU+JQogIGFkZF9zdW1tYXJpZXMoZmlsdEBtZXRhLmRhdGEsIG5ld19jb2x1bW4gPSAiZmlsdDNfcmlibyIsIG1ldGFfcXVlcnkgPSAicGVyY2VudF9yaWJvIikgJT4lCiAgYWRkX3N1bW1hcmllcyhmaWx0QG1ldGEuZGF0YSwgbmV3X2NvbHVtbiA9ICJmaWx0M19jbG9ubyIsIG1ldGFfcXVlcnkgPSAicmVhZHMiKSAtPgogIHNhbXBsZV9zdW1tYXJpZXMKCmFzLm1hdHJpeChzYW1wbGVfc3VtbWFyaWVzKQpgYGAKCiMgRGlzdHJpYnV0aW9uCgojIyBCZWZvcmUgZmlsdGVyaW5nCgpgYGB7ciBkaXN0cmlidXRpb259CmFsbF9ub3JtIDwtIE5vcm1hbGl6ZURhdGEob2JqZWN0PWFsbCkgJT4lCiAgRmluZFZhcmlhYmxlRmVhdHVyZXMoKSAlPiUKICBTY2FsZURhdGEoKSAlPiUKICBSdW5QQ0EoKSAlPiUKICBGaW5kTmVpZ2hib3JzKCkgJT4lCiAgRmluZENsdXN0ZXJzKCkgJT4lCiAgUnVuVFNORSgpICU+JQogIFJ1blVNQVAocmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjEwKQoKRGltUGxvdChvYmplY3Q9YWxsX25vcm0sIHJlZHVjdGlvbj0idHNuZSIpCnBsb3R0ZWQgPC0gRGltUGxvdChhbGxfbm9ybSwgcmVkdWN0aW9uPSJ1bWFwIiwgZ3JvdXAuYnk9ImNsdXN0ZXJfbGV0dGVycyIsIGxhYmVsPVRSVUUpCnBsb3R0ZWQKYGBgCgojIyBBZnRlciBmaWx0ZXJpbmcKCmBgYHtyIGRpc3RyaWJ1dGlvbl9wb3N0fQpmaWx0X25vcm0gPC0gTm9ybWFsaXplRGF0YShvYmplY3Q9ZmlsdCkgJT4lCiAgRmluZFZhcmlhYmxlRmVhdHVyZXMoKSAlPiUKICBTY2FsZURhdGEoKSAlPiUKICBSdW5QQ0EoKSAlPiUKICBGaW5kTmVpZ2hib3JzKCkgJT4lCiAgRmluZENsdXN0ZXJzKCkgJT4lCiAgUnVuVFNORSgpICU+JQogIFJ1blVNQVAocmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjEwKQoKRGltUGxvdChvYmplY3Q9ZmlsdF9ub3JtLCByZWR1Y3Rpb249InRzbmUiKQpwbG90dGVkIDwtIERpbVBsb3QoZmlsdF9ub3JtLCByZWR1Y3Rpb249InVtYXAiLCBncm91cC5ieT0iY2x1c3Rlcl9sZXR0ZXJzIiwgbGFiZWw9VFJVRSkKcGxvdHRlZApgYGAKCmBgYHtyIGNob29zZV9kaW1zfQpmaWx0X25vcm0gPC0gSmFja1N0cmF3KGZpbHRfbm9ybSwgbnVtLnJlcGxpY2F0ZT0xMCkKZmlsdF9ub3JtIDwtIFNjb3JlSmFja1N0cmF3KGZpbHRfbm9ybSkKSmFja1N0cmF3UGxvdChmaWx0X25vcm0pCkVsYm93UGxvdChmaWx0X25vcm0pCiMjIFNvIEkgYW0gdGhpbmtpbmcgbWF5YmUgNC0xMD8Kd2FudGVkX2RpbXMgPC0gNgoKZmlsdF9ub3JtIDwtIEZpbmROZWlnaGJvcnMoZmlsdF9ub3JtLCBkaW1zPTE6d2FudGVkX2RpbXMpICU+JQogIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uPTAuNSkgJT4lCiAgU3Rhc2hJZGVudChzYXZlLm5hbWU9InJlczBwNV9jbHVzdGVycyIpClJ1blVNQVAoZmlsdF9ub3JtLCBkaW1zPTE6OSkKRGltUGxvdChmaWx0X25vcm0sIGxhYmVsPVRSVUUpCgpmaWx0X25vcm0gPC0gRmluZENsdXN0ZXJzKGZpbHRfbm9ybSwgcmVzb2x1dGlvbj0wLjEpICU+JQogIEZpbmROZWlnaGJvcnMoay5wYXJhbT02KSAlPiUKICBTdGFzaElkZW50KHNhdmUubmFtZT0icmVzMHAxX2NsdXN0ZXJzIikKUnVuVU1BUChmaWx0X25vcm0sIGRpbXM9MTo5KQpEaW1QbG90KGZpbHRfbm9ybSwgbGFiZWw9VFJVRSkKYGBgCgpBZGQgaW50byB0aGUgbWV0YWRhdGEgYSBjb25jYXRlbmF0aW9uIG9mIHRoZSBzYW1wbGUgSUQgYW5kIHRoZSBjbHVzdGVyIElECgpgYGB7ciBjb21wYXJlX2NsdXN0ZXJzfQppZGVudGl0eV92ZWN0b3IgPC0gZmlsdF9ub3JtW1sib3JpZy5pZGVudCJdXVtbIm9yaWcuaWRlbnQiXV0KY2xhc3MoaWRlbnRpdHlfdmVjdG9yKQpjbHVzdGVyX3ZlY3RvciA8LSBhcy5jaGFyYWN0ZXIoZmlsdF9ub3JtW1sicmVzMHAxX2NsdXN0ZXJzIl1dW1sicmVzMHAxX2NsdXN0ZXJzIl1dKQpjb25jYXRlbmF0ZWRfdmVjdG9yIDwtIHBhc3RlMChpZGVudGl0eV92ZWN0b3IsICJfIiwgY2x1c3Rlcl92ZWN0b3IpCmZpbHRfbm9ybVtbImNsdXN0ZXJfc2FtcGxlIl1dIDwtIGNvbmNhdGVuYXRlZF92ZWN0b3IKYGBgCgojIFZhcmlhYmxlIGZlYXR1cmVzCgpgYGB7ciB2YXJpYWJsZV9mZWF0dXJlc30KdmFyIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKGZpbHRfbm9ybSkKbW9zdF92YXIgPC0gaGVhZChWYXJpYWJsZUZlYXR1cmVzKHZhciksIDMwKQp2YXJpYWJsZV9wbG90IDwtIFZhcmlhYmxlRmVhdHVyZVBsb3QodmFyKQp2YXJpYWJsZV9wbG90IDwtIExhYmVsUG9pbnRzKHBsb3Q9dmFyaWFibGVfcGxvdCwgcG9pbnRzPW1vc3RfdmFyLCByZXBlbD1UUlVFKQp2YXJpYWJsZV9wbG90CmBgYAoKIyBWYXJpb3VzIG1hcmtlciBzZWFyY2hlcwoKIyMgQWxsIE1hcmtlcnMKCiMjIyBCeSBzYW1wbGUKClF1ZXN0aW9uOiBJcyBpdCBzbWFydCBlbm91Z2ggdG8gdXNlIHRoZSByYXcgZGF0YSBpZiBJIGdpdmUKRmluZEFsbE1hcmtlcnMgdGhlIG5vcm1hbGl6ZWQgZGF0YT8gIEZvciB0aGUgbW9tZW50IEkgZG8gbm90IHRoaW5rIEkKd2lsbCByaXNrIGl0LgoKYGBge3IgZmluZGFsbG1hcmtlcnMwMX0KY29tYmluZWRfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhmaWx0LCBvbmx5LnBvcz1UUlVFLCBsb2dmYy50aHJlc2hvbGQgPSAwLjUpCmhlYWQoY29tYmluZWRfbWFya2VycykKCmJyaWVmIDwtIHVuaXF1ZShhbm5vdGF0aW9uc1ssIGMoImhnbmNfc3ltYm9sIiwgImRlc2NyaXB0aW9uIildKQpjb21iaW5lZCA8LSBhcy5kYXRhLmZyYW1lKGNvbWJpbmVkX21hcmtlcnMpCnJvd25hbWVzKGNvbWJpbmVkKSA8LSB0b3VwcGVyKHJvd25hbWVzKGNvbWJpbmVkKSkKYW5ub3RhdGVkX21hcmtlcnMgPC0gbWVyZ2UoY29tYmluZWQsIGJyaWVmLCBieS54PSJyb3cubmFtZXMiLCBieS55PSJoZ25jX3N5bWJvbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbC54PVRSVUUpCmBgYAoKIyMjIEJ5IHNhbXBsZQoKYGBge3IgZmluZGFsbG1hcmtlcnMwMn0KY29tYmluZWRfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhmaWx0LCBvbmx5LnBvcz1UUlVFLCBsb2dmYy50aHJlc2hvbGQ9MC41KQoKY29tYmluZWQgPC0gYXMuZGF0YS5mcmFtZShjb21iaW5lZF9tYXJrZXJzKQpyb3duYW1lcyhjb21iaW5lZCkgPC0gdG91cHBlcihyb3duYW1lcyhjb21iaW5lZCkpCmFubm90YXRlZF9tYXJrZXJzIDwtIG1lcmdlKGNvbWJpbmVkLCBicmllZiwgYnkueD0icm93Lm5hbWVzIiwgYnkueT0iaGduY19zeW1ib2wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhbGwueD1UUlVFKQpoZWFkKGFubm90YXRlZF9tYXJrZXJzKQpgYGAKCiMjIyBCeSBDbHVzdGVyCgpTaW5jZSBJIGFtIG5vdCB1c2luZyB0aGUgZmlsdF9ub3JtIGRhdGEgc3RydWN0dXJlLCBJIHdpbGwgbmVlZCB0byBwdWxsCnRoZSBjbHVzdGVyIGluZm9ybWF0aW9uIGZyb20gdGhlIG5vcm1hbGl6ZWQgY29weS4uLgoKYGBge3IgbWFya2Vyc19ieV9jbHVzdGVyfQpjbHVzdGVycyA8LSBmaWx0CklkZW50cyhjbHVzdGVycykgPC0gZmlsdF9ub3JtW1sicmVzMHAxX2NsdXN0ZXJzIl1dCmNsdXN0ZXJfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhjbHVzdGVycywgb25seS5wb3M9VFJVRSwgbG9nZmMudGhyZXNob2xkPTAuNSkKCmNsdXN0ZXJzIDwtIGFzLmRhdGEuZnJhbWUoY2x1c3Rlcl9tYXJrZXJzKQpyb3duYW1lcyhjbHVzdGVycykgPC0gdG91cHBlcihyb3duYW1lcyhjbHVzdGVycykpCmFubm90YXRlZF9jbHVzdGVycyA8LSBtZXJnZShjbHVzdGVycywgYnJpZWYsIGJ5Lng9InJvdy5uYW1lcyIsIGJ5Lnk9ImhnbmNfc3ltYm9sIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsLng9VFJVRSkKaGVhZChhbm5vdGF0ZWRfY2x1c3RlcnMpCgphbm5vdGF0ZWRfY2x1c3RlcnMgJT4lCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgZHBseXI6OnRvcF9uKG49MTAsIHd0PWF2Z19sb2cyRkMpICU+JQogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCmBgYHtyIGNvdW50X2NsdXN0ZXJfY2xvbm99CnN1bShmaWx0X25vcm1bWyJyZXMwcDFfY2x1c3RlcnMiXV0gPT0gIjAiKQpzdW0oZmlsdF9ub3JtW1sicmVzMHAxX2NsdXN0ZXJzIl1dID09ICIwIiAmCiAgICAhaXMubmEoZmlsdF9ub3JtW1sicmF3X2Nsb25vdHlwZV9pZCJdXSkpCgpzdW0oZmlsdF9ub3JtW1sicmVzMHAxX2NsdXN0ZXJzIl1dID09ICIxIikKc3VtKGZpbHRfbm9ybVtbInJlczBwMV9jbHVzdGVycyJdXSA9PSAiMSIgJgogICAgIWlzLm5hKGZpbHRfbm9ybVtbInJhd19jbG9ub3R5cGVfaWQiXV0pKQoKc3VtKGZpbHRfbm9ybVtbInJlczBwMV9jbHVzdGVycyJdXSA9PSAiMiIpCnN1bShmaWx0X25vcm1bWyJyZXMwcDFfY2x1c3RlcnMiXV0gPT0gIjIiICYKICAgICFpcy5uYShmaWx0X25vcm1bWyJyYXdfY2xvbm90eXBlX2lkIl1dKSkKCnN1bShmaWx0X25vcm1bWyJyZXMwcDFfY2x1c3RlcnMiXV0gPT0gIjMiKQpzdW0oZmlsdF9ub3JtW1sicmVzMHAxX2NsdXN0ZXJzIl1dID09ICIzIiAmCiAgICAhaXMubmEoZmlsdF9ub3JtW1sicmF3X2Nsb25vdHlwZV9pZCJdXSkpCgpzdW0oZmlsdF9ub3JtW1sicmVzMHAxX2NsdXN0ZXJzIl1dID09ICI0IikKc3VtKGZpbHRfbm9ybVtbInJlczBwMV9jbHVzdGVycyJdXSA9PSAiNCIgJgogICAgIWlzLm5hKGZpbHRfbm9ybVtbInJhd19jbG9ub3R5cGVfaWQiXV0pKQoKc3VtKGZpbHRfbm9ybVtbInJlczBwMV9jbHVzdGVycyJdXSA9PSAiNSIpCnN1bShmaWx0X25vcm1bWyJyZXMwcDFfY2x1c3RlcnMiXV0gPT0gIjUiICYKICAgICFpcy5uYShmaWx0X25vcm1bWyJyYXdfY2xvbm90eXBlX2lkIl1dKSkKCnN1bShmaWx0X25vcm1bWyJyZXMwcDFfY2x1c3RlcnMiXV0gPT0gIjYiKQpzdW0oZmlsdF9ub3JtW1sicmVzMHAxX2NsdXN0ZXJzIl1dID09ICI2IiAmCiAgICAhaXMubmEoZmlsdF9ub3JtW1sicmF3X2Nsb25vdHlwZV9pZCJdXSkpCmBgYAoKQ2x1c3RlcnMgMCBhbmQgNSBoYXZlIGEgZ3JlYXQgbWFqb3JpdHkgb2YgdGhlIGNsb25vdHlwZXMuCjAgaGFzIHNvbWV0aGluZyBsaWtlIDkwJSwgNSBoYXMgfiAzMCUsIHRoZSBvdGhlcnMgfiAxMCUKCiMjIENvbXBhcmUgc3BlY2lmaWMgY2x1c3RlcnMKCiMjIyBMb29rIGF0IGNsdXN0ZXIgMCwgTmFzYWwgdnMuIENvbnRyb2wKCmBgYHtyIHNwZWNpZmljX2NsdXN0ZXJfbWFya2Vyc30KY29udHJvbG5fMCA8LSBGaW5kTWFya2VycygKICAgIGZpbHQsIGdyb3VwLmJ5PSJjbHVzdGVyX3NhbXBsZSIsCiAgICBpZGVudC4xPSJjb250cm9sXzAiLCBpZGVudC4yPSJuXzAiKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKaGVhZChjb250cm9sbl8wKQpyb3duYW1lcyhjb250cm9sbl8wKSA8LSB0b3VwcGVyKHJvd25hbWVzKGNvbnRyb2xuXzApKQpjb250cm9sbl8wIDwtIG1lcmdlKGNvbnRyb2xuXzAsIGJyaWVmLCBieT0icm93Lm5hbWVzIiwgYnkueT0iaGduY19zeW1ib2wiLAogICAgICAgICAgICAgICAgICAgIGFsbC54PVRSVUUpCgphbm5vdGF0ZWRfY2x1c3RlcnMgJT4lCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgZHBseXI6OnRvcF9uKG49MTAsIHd0PWF2Z19sb2cyRkMpICU+JQogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCiMjIyBDbHVzdGVyIDAgTmFzYWwgdnMuIE1vY2sKCmBgYHtyIHNwZWNpZmljXzBfbm1vY2t9Cm1vY2t2c25fMCA8LSBGaW5kTWFya2VycygKICAgIGZpbHRfbm9ybSwgZ3JvdXAuYnk9ImNsdXN0ZXJfc2FtcGxlIiwKICAgIGlkZW50LjE9Im5fMCIsIGlkZW50LjI9Im1vY2tfMCIpICU+JQogIGFzLmRhdGEuZnJhbWUoKQpoZWFkKG1vY2t2c25fMCkKcm93bmFtZXMobW9ja3Zzbl8wKSA8LSB0b3VwcGVyKHJvd25hbWVzKG1vY2t2c25fMCkpCm1vY2t2c25fMCA8LSBtZXJnZShtb2NrdnNuXzAsIGJyaWVmLCBieT0icm93Lm5hbWVzIiwgYnkueT0iaGduY19zeW1ib2wiLAogICAgICAgICAgICAgICAgICAgYWxsLng9VFJVRSkKCm1vY2t2c21fMCA8LSBGaW5kTWFya2VycygKICAgIGZpbHRfbm9ybSwgZ3JvdXAuYnk9ImNsdXN0ZXJfc2FtcGxlIiwKICAgIGlkZW50LjE9Im1fMCIsIGlkZW50LjI9Im1vY2tfMCIpICU+JQogIGFzLmRhdGEuZnJhbWUoKQpoZWFkKG1vY2t2c21fMCkKcm93bmFtZXMobW9ja3ZzbV8wKSA8LSB0b3VwcGVyKHJvd25hbWVzKG1vY2t2c21fMCkpCm1vY2t2c21fMCA8LSBtZXJnZShtb2NrdnNtXzAsIGJyaWVmLCBieT0icm93Lm5hbWVzIiwgYnkueT0iaGduY19zeW1ib2wiLAogICAgICAgICAgICAgICAgICAgYWxsLng9VFJVRSkKaGVhZChtb2NrdnNtXzAsIG49MzApCmBgYAoKIyMgRmluZCBDb25zZXJ2ZWQgTWFya2VycwoKVGhpcyBmdW5jdGlvbiBtYWtlcyBubyBzZW5zZS4KCmBgYHtyIGZpbmRfY29uc2VydmVkX21hcmtlcnN9CkRlZmF1bHRBc3NheShmaWx0X25vcm0pIDwtICJSTkEiCmNvbnNlcnZlZF9tYXJrZXJzIDwtIEZpbmRDb25zZXJ2ZWRNYXJrZXJzKAogICAgZmlsdF9ub3JtLCBpZGVudC4xPWMoMCwgMSksIGlkZW50LjI9YygyLDMsNCksCiAgICBncm91cGluZy52YXI9InNhbXBsZSIsIG9ubHkucG9zPVRSVUUsCiAgICB2ZXJib3NlPVRSVUUpCgptb2NrX3ZzX2NvbnRyb2wgPC0gRmluZE1hcmtlcnMoZmlsdF9ub3JtLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjEgPSAibW9jayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4yID0gImNvbnRyb2wiKQpoZWFkKG1vY2tfdnNfY29udHJvbCkKbXVzY3VsYXJfdnNfbW9jayA8LSBGaW5kTWFya2VycyhmaWx0X25vcm0sIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMSA9ICJtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjIgPSAibW9jayIpCnN1bW1hcnkobXVzY3VsYXJfdnNfbW9jaykKbmFzYWxfdnNfbW9jayA8LSBGaW5kTWFya2VycyhmaWx0LCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ucGN0ID0gMC4yNSwgaWRlbnQuMSA9ICJuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4yID0gIm1vY2siKQpzdW1tYXJ5KG5hc2FsX3ZzX21vY2spCgpGZWF0dXJlUGxvdChmaWx0LCBmZWF0dXJlcz1jKCJSZ2NjIiksCiAgICAgICAgICAgIHNwbGl0LmJ5PSJvcmlnLmlkZW50IiwgbWF4LmN1dG9mZj0zLCBjb2xzPWMoImRhcmtncmVlbiIsICJkYXJrcmVkIikpCmBgYAoKIyBTY2FuIGZvciBsaWtlbHkgY2VsbCBjeWNsZSBnZW5lcwoKVGhpcyBpcyBhIG5lYXQgaWRlYSwgSSB0aGluayB3ZSBjYW4gcmVwdXJwb3NlIGl0IHRvIGltbXVub2xvZ3kgZ2VuZSBzZXRzLgoKYGBge3IgY2VsbF9jeWNsZX0KZmlsdCA8LSBDZWxsQ3ljbGVTY29yaW5nKAogICAgb2JqZWN0ID0gZmlsdF9ub3JtLAogICAgZzJtLmZlYXR1cmVzID0gY2MuZ2VuZXMkZzJtLmdlbmVzLAogICAgcy5mZWF0dXJlcyA9IGNjLmdlbmVzJHMuZ2VuZXMpClZsblBsb3QoZmlsdCwgZmVhdHVyZXMgPSBjKCJTLlNjb3JlIiwgIkcyTS5TY29yZSIpLAogICAgICAgIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLAogICAgICAgIG5jb2wgPSA0LCBwdC5zaXplID0gMCkKYGBgCgpIYXZpbmcgd3JpdHRlbiB0aGUgZm9sbG93aW5nIEkgcmVhbGl6ZWQgSSB1c2VkIGFuIG9sZGVyIHZlcnNpb24gb2YgbXkKbVNpZ0RCIHJlZmVyZW5jZS4uLiAgRklYTUU6IFJlZG8gaXQgd2l0aCB0aGUgNy41KyBkYXRhLgoKYGBge3IgbXNpZ2RifQpicm9hZF90eXBlcyA8LSBsb2FkX2dtdF9zaWduYXR1cmVzKHNpZ25hdHVyZXMgPSAicmVmZXJlbmNlL204LmFsbC52MjAyMi4xLk1tLnN5bWJvbHMuZ210IikKYnJvYWRfbGlzdCA8LSBsaXN0KCkKZm9yIChpIGluIG5hbWVzKGJyb2FkX3R5cGVzKSkgewogIGJyb2FkX2xpc3RbW2ldXSA8LSBnZW5lSWRzKGJyb2FkX3R5cGVzW1tpXV0pCn0Kd3RmIDwtIEFkZE1vZHVsZVNjb3JlKG9iamVjdCA9IGZpbHRfbm9ybSwgZmVhdHVyZXMgPSBicm9hZF9saXN0LAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJtOCIpCgpjaG9zZW4gPC0gYygzLCA5LCAxMSwgMzYsIDQzLCA0MiwgMTQpCm5hbWVzKGJyb2FkX3R5cGVzKVtjaG9zZW5dCmNvbHVtbnMgPC0gcGFzdGUwKCJtOCIsIGNob3NlbikKVmxuUGxvdCh3dGYsIGZlYXR1cmVzID0gY29sdW1ucywKICAgICAgICBncm91cC5ieSA9ICJyZXMwcDFfY2x1c3RlcnMiLCBzYW1lLnkubGltcyA9IFRSVUUsCiAgICAgICAgbmNvbCA9IDQsIHB0LnNpemUgPSAwKQoKCmNob3NlbiA8LSBjKDUwLCA1MSwgNjAsIDYxLCA2MiwgNjUsIDY2KQpuYW1lcyhicm9hZF90eXBlcylbY2hvc2VuXQpjb2x1bW5zIDwtIHBhc3RlMCgibTgiLCBjaG9zZW4pClZsblBsb3Qod3RmLCBmZWF0dXJlcyA9IGNvbHVtbnMsCiAgICAgICAgZ3JvdXAuYnkgPSAicmVzMHAxX2NsdXN0ZXJzIiwgc2FtZS55LmxpbXMgPSBUUlVFLAogICAgICAgIG5jb2wgPSA0LCBwdC5zaXplID0gMCkKCgpjaG9zZW4gPC0gYygxMTUsIDExOCwgMTE5LCAxMjAsIDEyMSwgMTI1LCAxMjgpCm5hbWVzKGJyb2FkX3R5cGVzKVtjaG9zZW5dCmNvbHVtbnMgPC0gcGFzdGUwKCJtOCIsIGNob3NlbikKVmxuUGxvdCh3dGYsIGZlYXR1cmVzID0gY29sdW1ucywKICAgICAgICBncm91cC5ieSA9ICJyZXMwcDFfY2x1c3RlcnMiLCBzYW1lLnkubGltcyA9IFRSVUUsCiAgICAgICAgbmNvbCA9IDQsIHB0LnNpemUgPSAwKQoKY2hvc2VuIDwtIGMoMjEyLCAyMTEsIDIxMCwgMjA5KQpuYW1lcyhicm9hZF90eXBlcylbY2hvc2VuXQpjb2x1bW5zIDwtIHBhc3RlMCgibTgiLCBjaG9zZW4pClZsblBsb3Qod3RmLCBmZWF0dXJlcyA9IGNvbHVtbnMsCiAgICAgICAgZ3JvdXAuYnkgPSAicmVzMHAxX2NsdXN0ZXJzIiwgc2FtZS55LmxpbXMgPSBUUlVFLAogICAgICAgIG5jb2wgPSA0LCBwdC5zaXplID0gMCkKCmNob3NlbiA8LSBjKDE3NjoxODIpCm5hbWVzKGJyb2FkX3R5cGVzKVtjaG9zZW5dCmNvbHVtbnMgPC0gcGFzdGUwKCJtOCIsIGNob3NlbikKVmxuUGxvdCh3dGYsIGZlYXR1cmVzID0gY29sdW1ucywKICAgICAgICBncm91cC5ieSA9ICJyZXMwcDFfY2x1c3RlcnMiLCBzYW1lLnkubGltcyA9IFRSVUUsCiAgICAgICAgbmNvbCA9IDQsIHB0LnNpemUgPSAwKQoKY2hvc2VuIDwtIGMoNDI6NDcpCm5hbWVzKGJyb2FkX3R5cGVzKVtjaG9zZW5dCmNvbHVtbnMgPC0gcGFzdGUwKCJtOCIsIGNob3NlbikKVmxuUGxvdCh3dGYsIGZlYXR1cmVzID0gY29sdW1ucywKICAgICAgICBncm91cC5ieSA9ICJyZXMwcDFfY2x1c3RlcnMiLCBzYW1lLnkubGltcyA9IFRSVUUsCiAgICAgICAgbmNvbCA9IDQsIHB0LnNpemUgPSAwKQoKdF9ncm91cHNfaWR4IDwtIGdyZXBsKHBhdHRlcm49Il9UX0NFTEwiLCB4PW5hbWVzKGJyb2FkX3R5cGVzKSkKdF9ncm91cHMgPC0gbmFtZXMoYnJvYWRfdHlwZXMpW3RfZ3JvdXBzX2lkeF0KdF9udW1zIDwtIHdoaWNoKHRfZ3JvdXBzX2lkeCwgYnJvYWRfdHlwZXMpCmNvbHVtbnMgPC0gcGFzdGUwKCJtOCIsIHRfbnVtcykKVmxuUGxvdCh3dGYsIGZlYXR1cmVzID0gY29sdW1ucywKICAgICAgICBncm91cC5ieSA9ICJyZXMwcDFfY2x1c3RlcnMiLCBzYW1lLnkubGltcyA9IFRSVUUsCiAgICAgICAgbmNvbCA9IDQsIHB0LnNpemUgPSAwKQp0X2dyb3VwcwoKdF9ncm91cHNfaWR4IDwtIGdyZXBsKHBhdHRlcm49Il9FUElUSEVMSUFMXyIsIHg9bmFtZXMoYnJvYWRfdHlwZXMpKQp0X2dyb3VwcyA8LSBuYW1lcyhicm9hZF90eXBlcylbdF9ncm91cHNfaWR4XQp0X251bXMgPC0gd2hpY2godF9ncm91cHNfaWR4LCBicm9hZF90eXBlcykKY29sdW1ucyA8LSBwYXN0ZTAoIm04IiwgdF9udW1zKQpWbG5QbG90KHd0ZiwgZmVhdHVyZXMgPSBjb2x1bW5zLAogICAgICAgIGdyb3VwLmJ5ID0gInJlczBwMV9jbHVzdGVycyIsIHNhbWUueS5saW1zID0gVFJVRSwKICAgICAgICBuY29sID0gNCwgcHQuc2l6ZSA9IDApCnRfZ3JvdXBzCgp0X2dyb3Vwc19pZHggPC0gZ3JlcGwocGF0dGVybj0iX0VORE9USEVMSUFMXyIsIHg9bmFtZXMoYnJvYWRfdHlwZXMpKQp0X2dyb3VwcyA8LSBuYW1lcyhicm9hZF90eXBlcylbdF9ncm91cHNfaWR4XQp0X251bXMgPC0gd2hpY2godF9ncm91cHNfaWR4LCBicm9hZF90eXBlcykKY29sdW1ucyA8LSBwYXN0ZTAoIm04IiwgdF9udW1zKQpWbG5QbG90KHd0ZiwgZmVhdHVyZXMgPSBjb2x1bW5zLAogICAgICAgIGdyb3VwLmJ5ID0gInJlczBwMV9jbHVzdGVycyIsIHNhbWUueS5saW1zID0gVFJVRSwKICAgICAgICBuY29sID0gNCwgcHQuc2l6ZSA9IDApCnRfZ3JvdXBzCmBgYAoKYGBge3Igc2F2ZW1lfQpwYW5kZXI6OnBhbmRlcihzZXNzaW9uSW5mbygpKQptZXNzYWdlKHBhc3RlMCgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKSkKdGhpc19zYXZlIDwtIHBhc3RlMChnc3ViKHBhdHRlcm49IlxcLlJtZCIsIHJlcGxhY2U9IiIsIHg9cm1kX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikKIyNtZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHRoaXNfc2F2ZSkpCiMjdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZT10aGlzX3NhdmUpKQpgYGAK