1 Preamble: installation of Bioconductor libraries

# install BiocManager package if not installed yet.
# BiocManager is the package installer for Bioconductor software.
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")

# install packages if not yet installed.
pkgs <- c("SingleCellExperiment",
          "ExperimentHub",
          "edgeR",
          "biomaRt",
          "DropletUtils", 
          "scRNAseq", 
          "scater", 
          "scuttle", 
          "scran",
          "scry",
          "BiocSingular", 
          "scDblFinder",
          "Seurat",
          "PCAtools",
          "glmpca",
          "genefilter",
          "pheatmap",
          "tidyverse",
          "mclust",
          "ggplot2",
          "SingleR")
notInstalled <- pkgs[!pkgs %in% installed.packages()[,1]]
if(length(notInstalled) > 0){
  BiocManager::install(notInstalled)
}
## 'getOption("repos")' replaces Bioconductor standard repositories, see
## '?repositories' for details
## 
## replacement repositories:
##     CRAN: https://cran.rstudio.com
## Bioconductor version 3.14 (BiocManager 1.30.16), R 4.1.2 (2021-11-01)
## Installing package(s) 'genefilter', 'pheatmap', 'tidyverse', 'mclust',
##   'SingleR'
## also installing the dependencies 'ps', 'rematch', 'processx', 'backports', 'gargle', 'uuid', 'cellranger', 'ids', 'rematch2', 'clipr', 'vroom', 'tzdb', 'callr', 'selectr', 'annotate', 'broom', 'dtplyr', 'forcats', 'googledrive', 'googlesheets4', 'haven', 'lubridate', 'modelr', 'readr', 'readxl', 'reprex', 'rstudioapi', 'rvest'
## 
##   There is a binary version available but the source version is later:
##           binary source needs_compilation
## backports  1.4.0  1.4.1              TRUE
## 
## 
## The downloaded binary packages are in
##  /var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T//RtmpBfBvZd/downloaded_packages
## installing the source package 'backports'
## Old packages: 'crayon', 'digest', 'glue', 'knitr', 'lattice', 'lifecycle',
##   'mgcv', 'mime', 'nlme', 'pillar', 'rlang', 'rmarkdown', 'stringi', 'tibble',
##   'tinytex', 'withr', 'xfun', 'Matrix'

Steps taken in first two lab sessions:

  1. Import the Macosko dataset as SingleCellExperiment object from the scRNAseq Bioconductor package.

  2. Include ENSEMBL gene identifiers in the rowData

  3. Remove very lowly expressed genes

  4. Remove low quality cells 4.1. Cells with outlying library size 4.2. Cells with outlying transcriptome complexity 4.3. Cells with outlying percentage of mitochondrial reads 4.4. Empty droplets 4.5. Doublets

  5. Normalization 5.1. Compute log-normalized counts 5.2. Compute scaling factor to correct for differences in library size

  6. Feature selection 6.1. Genes with high variance 6.2. Genes with high variance with respect to their mean expression 6.3. Genes with high deviance 6.4. Genes with high variance after variance-stabilizing transformation (VST)

  7. Dimensionality reduction 7.1. Based on two most variable genes from step 6.2. 7.2. PCA 7.3. GLM-PCA 7.4. T-SNE 7.5. UMAP

2 The Macosko dataset

In this workshop session, we will analyze the single-cell RNA-seq dataset from the publication by Macosko et al., Cell 161, 1202–1214 from 2015 (link). This is the manuscript in which the droplet scRNA-seq technology Drop-seq was introduced. Six years after the original publication, drop-seq is still one of the most commonly adopted scRNA-seq protocols, as evidenced by the large number of citations for Macosko et al. (4.303 citations at November 3, 2021).

The basic idea behind the Drop-seq protocol can be taken from the graphical abstract of the publication.

knitr::include_graphics("macosko_graphicalAbstract.jpeg")

The success of Drop-seq can be explained by the following advantageous features:

  • The use of unique molecular identifiers (UMIs). By working with UMIs, one count corresponds to one observed mRNA molecule present in the cell. Thanks to the use of UMI barcodes, PCR artifacts are reduced.

  • Scalability: microfluidics technology allows for performing the library prep reactions inside nanodroplets, in which single cells may be contained. Library prep occurs across all droplets simultaneously.

  • Cost: the experiment costs around 6.5 cents (USD) per cell.

  • Speed: The very large dataset that we will be working with today was generated in an experiment that took only 4 days.

In this particular experiment, Macosko et al. sequenced 49,300 cells from the mouse retina, identifying 39 transcriptionally distinct cell populations. The experiment was performed in 7 batches.

3 Data availability

3.1 SRA

The Sequence Read Archive (SRA) is the largest publicly available repository of high-throughput sequencing data. The data are stored by the National Center for Biotechnology Information (NCBI) services and multiple cloud storage providers. From this website “raw” sequencing data can be retrieved. In practice, these are usually .sra files, which can be downloaded and converted into FASTQ files using functions from the sratoolkit software.

For our dataset, the FASTQ data can be retrieved from this link. The data are stored as one file per sequencing batch, with each file approximately 20Gb. As such, it will be unfeasible to download and process these FASTQ files in this practical session.

Instead, for demonstrative purposes, we have taken a subsample of the FASTQ file for the first sequencing batch for you to work with. On this subsample, we may perform all the tasks that we would have performed on the full dataset. The steps that are required for downloading and quantifying drop-seq data can be found in a Shell script on our companion GitHub repository.

3.2 GEO

The dataset from Macosko et al. was also uploaded by the authors on the Gene Expression Omnibus (GEO) platform under accession number GSE63472, from which raw and readily-processed data files may be retrieved, including:

  1. GSE63472_RAW.tar, a 90.6Gb object that contains the “raw data” for the experiment. In the scRNA-seq context, FASTQ files are often considered the raw data format.

  2. GSE63472_P14Retina_merged_digital_expression.txt.gz, a 50.7Mb matrix that stores the gene expression values for each cell. These values are integer counts, that did not undergo any type of preprocessing or normalization.

  3. GSE63472_mm10_reference_metadata.tar.gz, a 862.9Mb compressed folder containing information on the reference genome to which the scRNA-seq reads were aligned (see theory slides).

  4. GSE63472_P14Retina_logDGE.txt.gz, a 316.8Mb compressed text file, not clear what it contains (results from a differential gene expression analysis, but with log-transformation, so log-fold changes maybe?).

As such, by downloading GSE63472_P14Retina_merged_digital_expression.txt.gz, we avoid re-quantifying the data, i.e., the translation from reads from the FASTQ files into a gene-level expression values for each cell.

One issue that often arises from data downloaded from GEO, is that there is no strict requirements for which data should be included in the upload by the authors. As such, from my personal experience, it can often be the case that important information like metadata are missing, or the content of the submitted files is unclear. Even if all the required data are available, as is the case here, we would still need to piece all the information together from different files and file formats before we can use them.

3.3 ExperimentHub

The Bioconductor ExperimentHub web resource, which can be accessed using the ExperimentHub R package, provides a central location where curated data from experiments, publications or training courses can be accessed. While it contains far less datasets than the SRA or GEO (4965 records to date), these datasets all follow the tidy data format of Bioconductor. Note that the ExperimentHub contains several types of data, like bulk and single-cell transcriptomics data, microarrays and more.

The Macosko dataset is available from ExperimentHub and can be accessed as follows:

library(ExperimentHub)
edb <- ExperimentHub()

edb[grep("Macosko", edb$title)] # find accession number (can be inefficient)
## ExperimentHub with 2 records
## # snapshotDate(): 2021-10-19
## # $dataprovider: Steve McCarroll, GEO
## # $species: Mus musculus
## # $rdataclass: dgCMatrix, DataFrame
## # additional mcols(): taxonomyid, genome, description,
## #   coordinate_1_based, maintainer, rdatadateadded, preparerclass, tags,
## #   rdatapath, sourceurl, sourcetype 
## # retrieve records with, e.g., 'object[["EH2690"]]' 
## 
##            title                 
##   EH2690 | Macosko retina counts 
##   EH2691 | Macosko retina colData
edb_counts <- edb[["EH2690"]]
edb_counts[1:5,1:5]
## 5 x 5 sparse Matrix of class "dgCMatrix"
##               r1_GGCCGCAGTCCG r1_CTTGTGCGGGAA r1_GCGCAACTGCTC r1_GATTGGGAGGCA
## KITL                        .               .               1               .
## TMTC3                       3               .               .               .
## CEP290                      1               3               .               2
## 4930430F08RIK               2               1               2               .
## 1700017N19RIK               .               .               .               .
##               r1_CCTCCTAGTTGG
## KITL                        .
## TMTC3                       2
## CEP290                      1
## 4930430F08RIK               1
## 1700017N19RIK               .
edb_coldata <- edb[["EH2691"]]
edb_coldata[1:5,]
## DataFrame with 5 rows and 2 columns
##           cell.id   cluster
##       <character> <integer>
## 1 r1_GGCCGCAGTCCG         2
## 2 r1_CTTGTGCGGGAA         2
## 3 r1_GCGCAACTGCTC         2
## 4 r1_GATTGGGAGGCA         2
## 5 r1_CCTCCTAGTTGG        NA
rm(edb, edb_counts, edb_coldata)

3.4 scRNASeq

In addition to ExperimentHub, Bioconductor provides the package scRNAseq. This package provides an even more user-friendly client to access (only) scRNA-seq datasets from the ExperimentHub web resource. Data retrieved using the scRNAseq package are stored as user-friendly SingleCellExperiment objects, with the expression data, gene-level information, cell-level information and experiment metadata all in place in one data object. The scRNA-seq package currently holds 61 datasets, including the data from Macosko et al.:

library(scRNAseq)
scRNAseq::MacoskoRetinaData()
## class: SingleCellExperiment 
## dim: 24658 49300 
## metadata(0):
## assays(1): counts
## rownames(24658): KITL TMTC3 ... 1110059M19RIK GM20861
## rowData names(0):
## colnames(49300): r1_GGCCGCAGTCCG r1_CTTGTGCGGGAA ... p1_TAACGCGCTCCT
##   p1_ATTCTTGTTCTT
## colData names(2): cell.id cluster
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):

4 Import data

The scRNAseq package provides convenient access to several datasets. See the package Bioconductor page for more information.

# Code below might ask you to create an ExperimentHub directory. 
# Type 'yes' and hit Enter, to allow this.
suppressPackageStartupMessages(library(scRNAseq))
sce <- MacoskoRetinaData()
## snapshotDate(): 2021-10-19
## see ?scRNAseq and browseVignettes('scRNAseq') for documentation
## loading from cache
## see ?scRNAseq and browseVignettes('scRNAseq') for documentation
## loading from cache

5 A SingleCellExperiment object

sce
## class: SingleCellExperiment 
## dim: 24658 49300 
## metadata(0):
## assays(1): counts
## rownames(24658): KITL TMTC3 ... 1110059M19RIK GM20861
## rowData names(0):
## colnames(49300): r1_GGCCGCAGTCCG r1_CTTGTGCGGGAA ... p1_TAACGCGCTCCT
##   p1_ATTCTTGTTCTT
## colData names(2): cell.id cluster
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):

5.1 Accessing data from a SingleCellExperiment object

Please see Figure 4.1 in OSCA for an overview of a SingleCellExperiment object.

# Data: assays
assays(sce)
## List of length 1
## names(1): counts
assays(sce)$counts[1:5, 1:5]
## 5 x 5 sparse Matrix of class "dgCMatrix"
##               r1_GGCCGCAGTCCG r1_CTTGTGCGGGAA r1_GCGCAACTGCTC r1_GATTGGGAGGCA
## KITL                        .               .               1               .
## TMTC3                       3               .               .               .
## CEP290                      1               3               .               2
## 4930430F08RIK               2               1               2               .
## 1700017N19RIK               .               .               .               .
##               r1_CCTCCTAGTTGG
## KITL                        .
## TMTC3                       2
## CEP290                      1
## 4930430F08RIK               1
## 1700017N19RIK               .
# Feature metadata: rowData
rowData(sce) # empty for now
## DataFrame with 24658 rows and 0 columns
# Cell metadata: colData
colData(sce)
## DataFrame with 49300 rows and 2 columns
##                         cell.id   cluster
##                     <character> <integer>
## r1_GGCCGCAGTCCG r1_GGCCGCAGTCCG         2
## r1_CTTGTGCGGGAA r1_CTTGTGCGGGAA         2
## r1_GCGCAACTGCTC r1_GCGCAACTGCTC         2
## r1_GATTGGGAGGCA r1_GATTGGGAGGCA         2
## r1_CCTCCTAGTTGG r1_CCTCCTAGTTGG        NA
## ...                         ...       ...
## p1_TCAAAAGCCGGG p1_TCAAAAGCCGGG        24
## p1_ATTAAGTTCCAA p1_ATTAAGTTCCAA        34
## p1_CTGTCTGAGACC p1_CTGTCTGAGACC         2
## p1_TAACGCGCTCCT p1_TAACGCGCTCCT        24
## p1_ATTCTTGTTCTT p1_ATTCTTGTTCTT        24
# Reduced dimensions: reducedDims
reducedDims(sce) # empty for now
## List of length 0
## names(0):

5.2 Creating a new SingleCellExperiment object

sceNew <- SingleCellExperiment(assays = list(counts = assays(sce)$counts))
sceNew
## class: SingleCellExperiment 
## dim: 24658 49300 
## metadata(0):
## assays(1): counts
## rownames(24658): KITL TMTC3 ... 1110059M19RIK GM20861
## rowData names(0):
## colnames(49300): r1_GGCCGCAGTCCG r1_CTTGTGCGGGAA ... p1_TAACGCGCTCCT
##   p1_ATTCTTGTTCTT
## colData names(0):
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):
rm(sceNew)

5.3 Storing (meta)data in a SingleCellExperiment object

fakeGeneNames <- paste0("gene", 1:nrow(sce))
rowData(sce)$fakeName <- fakeGeneNames
head(rowData(sce))
## DataFrame with 6 rows and 1 column
##                  fakeName
##               <character>
## KITL                gene1
## TMTC3               gene2
## CEP290              gene3
## 4930430F08RIK       gene4
## 1700017N19RIK       gene5
## MGAT4C              gene6
# Remove again by setting to NULL
rowData(sce)$fakeName <- NULL

assays(sce)$logCounts <- log1p(assays(sce)$counts)
assays(sce)
## List of length 2
## names(2): counts logCounts
assays(sce)$logCounts[1:5, 1:5]
## 5 x 5 sparse Matrix of class "dgCMatrix"
##               r1_GGCCGCAGTCCG r1_CTTGTGCGGGAA r1_GCGCAACTGCTC r1_GATTGGGAGGCA
## KITL                .               .               0.6931472        .       
## TMTC3               1.3862944       .               .                .       
## CEP290              0.6931472       1.3862944       .                1.098612
## 4930430F08RIK       1.0986123       0.6931472       1.0986123        .       
## 1700017N19RIK       .               .               .                .       
##               r1_CCTCCTAGTTGG
## KITL                .        
## TMTC3               1.0986123
## CEP290              0.6931472
## 4930430F08RIK       0.6931472
## 1700017N19RIK       .
assays(sce)$logCounts <- NULL

6 Obtaining and including rowData

The rowData slot of a SingleCellExperiment object allows for storing information on the features, i.e. the genes, in a dataset. In our object, the rowData slot is empty.

rowData(sce)
## DataFrame with 24658 rows and 0 columns

As such, the only information we have on the genes are their names, which can be retrieved as the rownames of the expression matrix.

head(rownames(sce))
## [1] "KITL"          "TMTC3"         "CEP290"        "4930430F08RIK"
## [5] "1700017N19RIK" "MGAT4C"

These are the gene names (symbols). Note that it may be useful to include additional information in the rowData slot. For instance, we may want to store:

  • Unambiguous gene identifiers (e.g. from ENSEMBL)
  • On which chromosome the gene is located
  • Gene length (genomic start position and end position)
  • Others…
library("biomaRt")

ensembl75 <- useEnsembl(biomart = 'genes', 
                        dataset = 'mmusculus_gene_ensembl',
                        version = 75)

head(listAttributes(ensembl75)) # potential info to extract
geneInfo <- getBM(attributes = c("ensembl_gene_id", # ENSEMBL unambiguous identifier
                                 "mgi_symbol", # Gene symbol (to link with SCE rownames),
                                 "chromosome_name", # On which chromosome
                                 "start_position", # Start position
                                 "end_position"),# End position
                  mart = ensembl75)

head(geneInfo)
geneInfo$mgi_symbol_upper <- toupper(geneInfo$mgi_symbol) 
# match between gene info and rownames

sum(rownames(sce) %in% geneInfo$mgi_symbol_upper)
## [1] 24360
sum(!rownames(sce) %in% geneInfo$mgi_symbol_upper) # lost in conversion :(
## [1] 298
rowData(sce) <- geneInfo[match(rownames(sce),geneInfo$mgi_symbol_upper),]
head(rowData(sce))
## DataFrame with 6 rows and 6 columns
##                  ensembl_gene_id    mgi_symbol chromosome_name start_position
##                      <character>   <character>     <character>      <integer>
## KITL          ENSMUSG00000019966          Kitl              10      100015630
## TMTC3         ENSMUSG00000036676         Tmtc3              10      100443902
## CEP290        ENSMUSG00000019971        Cep290              10      100488289
## 4930430F08RIK ENSMUSG00000046567 4930430F08Rik              10      100572274
## 1700017N19RIK ENSMUSG00000056912 1700017N19Rik              10      100592386
## MGAT4C        ENSMUSG00000019888        Mgat4c              10      101681487
##               end_position mgi_symbol_upper
##                  <integer>      <character>
## KITL             100100413             KITL
## TMTC3            100487350            TMTC3
## CEP290           100573655           CEP290
## 4930430F08RIK    100589259    4930430F08RIK
## 1700017N19RIK    100618391    1700017N19RIK
## MGAT4C           102391469           MGAT4C

7 Filtering non-informative genes

library(edgeR)
## Loading required package: limma
## 
## Attaching package: 'limma'
## The following object is masked from 'package:BiocGenerics':
## 
##     plotMA
## 
## Attaching package: 'edgeR'
## The following object is masked from 'package:SingleCellExperiment':
## 
##     cpm
# A very simple strategy: remove all genes that are expressed in less than 10
# out of 49300 cells -> note that this a very lenient filtering criterium
keep <- rowSums(assays(sce)$counts > 0) > 10
table(keep)
## keep
## FALSE  TRUE 
##  6771 17887
sce <- sce[keep,]

Note that dedicated functions for filtering out lowly expressed genes exist. One such function is the filterByExpr function of the edgeR R package. In brief, the strategy keeps genes that have at least “min.count” reads in a worthwhile number samples.

More precisely, the filtering keeps genes that have count-per-million (CPM) above k in n samples.

k is determined by the min.count argument to the function, and by the sample library sizes.

n is determined by the design matrix. n can be seen as the smallest group sample size. A group of samples/cells can be defined as cells that are more similar to one another, e.g., from the same sequencing batch, the same patient…. Here we could also use the cluster assignment (cell type) for each cell as the grouping variable; note however, that this usually is not available until a later stage in the analysis pipeline (i.e., after dimension reduction and clustering, topics we will cover next session.)

If all the group sizes are larger than the large.n argument of the filterByExpr function, which defaults to 10, then n will be taken as min.prop* the the number of samples/cells in the smallest group.

Note that all the group sizes will often be larger than the large.n in case of single-cell data.

# Slow (more than 1min) -> do not run
# keep2 <- filterByExpr(sce,
#                       group = sce$cluster,
#                       min.count = 1,
#                       min.total.count = 15,
#                       min.prop = 0.2)
# table(keep2)

8 Quality control

In quality control (QC), we check the quality of our dataset. In particular, we investigate undesirable oddities, such as low-quality cells, empty droplets or doublets.

8.1 Identifying and removing low-quality cells

There are several distinguishing features of low-quality cells that can be used in order to identify them. As described in the OSCA book book):

  1. The library size is defined as the total sum of counts across all relevant features for each cell. Here, we will consider the relevant features to be the endogenous genes. Cells with small library sizes are of low quality as the RNA has been lost at some point during library preparation, either due to cell lysis or inefficient cDNA capture and amplification.

  2. The number of expressed features in each cell is defined as the number of endogenous genes with non-zero counts for that cell. Any cell with very few expressed genes is likely to be of poor quality as the diverse transcript population has not been successfully captured.

  3. We sometimes have spike-in (ERCC) transcripts available. The proportion of reads mapped to spike-in transcripts is calculated relative to the total count across all features (including spike-ins) for each cell. As the same amount of spike-in RNA should have been added to each cell, any enrichment in spike-in counts is symptomatic of loss of endogenous RNA.

  4. In the absence of spike-in transcripts, the proportion of reads mapped to genes in the mitochondrial genome can be used. High proportions are indicative of poor-quality cells (Islam et al. 2014; Ilicic et al. 2016), presumably because of loss of cytoplasmic RNA from perforated cells. The reasoning is that, in the presence of modest damage, the holes in the cell membrane permit efflux of individual transcript molecules but are too small to allow mitochondria to escape, leading to a relative enrichment of mitochondrial transcripts. For single-nuclei RNA-seq experiments, high proportions are also useful as they can mark cells where the cytoplasm has not been successfully stripped.

8.2 Calculate QC variables

This function calculates useful QC metrics for identification and removal of potentially problematic cells. Default per-cell metrics are the sum of counts (i.e., the library size) and the number of detected features. The percentage of counts in the top features also provides a measure of library complexity.

If subsets is specified, these statistics are also computed for each subset of features. This is useful for investigating gene sets of interest, e.g., mitochondrial genes.

library(scater)

# check ERCC spike-in transcripts
sum(grepl("^ERCC-", rownames(sce))) # no spike-in transcripts available
## [1] 0
# check mitochondrial genes
sum(rowData(sce)$chromosome_name=="MT",na.rm = TRUE) # 28 mitochondrial genes
## [1] 28
sum(grepl("^MT-", rownames(sce))) # alternatively
## [1] 28
is.mito <- grepl("^MT-", rownames(sce))

## calculate QC metrics
df <- perCellQCMetrics(sce, subsets=list(Mito=is.mito))
head(df)
## DataFrame with 6 rows and 6 columns
##                       sum  detected subsets_Mito_sum subsets_Mito_detected
##                 <numeric> <integer>        <numeric>             <integer>
## r1_GGCCGCAGTCCG     37478      7235              427                    14
## r1_CTTGTGCGGGAA     32034      6921              503                    15
## r1_GCGCAACTGCTC     28140      6390              460                    13
## r1_GATTGGGAGGCA     20352      5727              326                    11
## r1_CCTCCTAGTTGG     19550      5769              264                     9
## r1_AGTCAAGCCCTC     19176      5217              253                    12
##                 subsets_Mito_percent     total
##                            <numeric> <numeric>
## r1_GGCCGCAGTCCG              1.13934     37478
## r1_CTTGTGCGGGAA              1.57021     32034
## r1_GCGCAACTGCTC              1.63468     28140
## r1_GATTGGGAGGCA              1.60181     20352
## r1_CCTCCTAGTTGG              1.35038     19550
## r1_AGTCAAGCCCTC              1.31936     19176
# add QC variables to sce object
colData(sce) <- cbind(colData(sce), df)

# the QC variables have now been added to the colData of our SCE object.
head(colData(sce))
## DataFrame with 6 rows and 8 columns
##                         cell.id   cluster       sum  detected subsets_Mito_sum
##                     <character> <integer> <numeric> <integer>        <numeric>
## r1_GGCCGCAGTCCG r1_GGCCGCAGTCCG         2     37478      7235              427
## r1_CTTGTGCGGGAA r1_CTTGTGCGGGAA         2     32034      6921              503
## r1_GCGCAACTGCTC r1_GCGCAACTGCTC         2     28140      6390              460
## r1_GATTGGGAGGCA r1_GATTGGGAGGCA         2     20352      5727              326
## r1_CCTCCTAGTTGG r1_CCTCCTAGTTGG        NA     19550      5769              264
## r1_AGTCAAGCCCTC r1_AGTCAAGCCCTC        NA     19176      5217              253
##                 subsets_Mito_detected subsets_Mito_percent     total
##                             <integer>            <numeric> <numeric>
## r1_GGCCGCAGTCCG                    14              1.13934     37478
## r1_CTTGTGCGGGAA                    15              1.57021     32034
## r1_GCGCAACTGCTC                    13              1.63468     28140
## r1_GATTGGGAGGCA                    11              1.60181     20352
## r1_CCTCCTAGTTGG                     9              1.35038     19550
## r1_AGTCAAGCCCTC                    12              1.31936     19176

8.3 Exploratory data analysis

In the figure below, we see that several cells have a very low number of expressed genes, and where most of the molecules are derived from mitochondrial genes. This indicates likely damaged cells, presumably because of loss of cytoplasmic RNA from perforated cells, so we should remove these for the downstream analysis.

# Number of genes vs library size
plotColData(sce, x = "sum", y="detected", colour_by="cluster") 

# Mitochondrial genes
plotColData(sce, x = "detected", y="subsets_Mito_percent")

8.4 QC using adaptive thresholds

Below, we remove cells that are outlying with respect to

  1. A low sequencing depth (number of UMIs);
  2. A low number of genes detected;
  3. A high percentage of reads from mitochondrial genes.

Here we will remove cells for QC based on adaptive thresholds related to the three points from above. Adaptive trhesholds are used as opposed to fixed thresholds.

With fixed thresholds, we use fixed cut-off values for each cell to pass QC, e.g., we might consider cells to be low quality if they have library sizes below 100,000 reads; express fewer than 5,000 genes; have spike-in proportions above 10%; or have mitochondrial proportions above 10%.

With adaptive thresholds, we assume that most of the dataset consists of high-quality cells. We then identify cells that are outliers for the various QC metrics, based on the median absolute deviation (MAD) from the median value of each metric across all cells. By default, we consider a value to be anoutlier if it is more than 3 MADs from the median in the “problematic” direction. This is loosely motivated by the fact that such a filter will retain 99% of non-outlier values that follow a normal distribution. We demonstrate adopting adaptive thresholds on the Macosko dataset:

lowLib <- isOutlier(df$sum, type="lower", log=TRUE)
lowFeatures <- isOutlier(df$detected, type="lower", log=TRUE)
highMito <- isOutlier(df$subsets_Mito_percent, type="higher")

table(lowLib)
## lowLib
## FALSE 
## 49300
table(lowFeatures)
## lowFeatures
## FALSE  TRUE 
## 49287    13
table(highMito)
## highMito
## FALSE  TRUE 
## 45890  3410
discardCells <- (lowLib | lowFeatures | highMito)
table(discardCells)
## discardCells
## FALSE  TRUE 
## 45877  3423
colData(sce)$discardCells <- discardCells

# visualize cells to be removed
# plotColData(sce, x = "sum", y="detected", colour_by="discardCells")
# plotColData(sce, x = "detected", y="subsets_Mito_percent", colour_by = "discardCells")

We removed a total of \(3423\) cells, most of which because of an outlyingly high percentage of reads from mitochondrial genes.

8.5 Identifying and removing empty droplets

Note that the removal of cells with low sequencing depth using the adaptive threshold procedure above is a way of removing empty droplets. Other approaches are possible, e.g., removing cells by statistical testing using emtpyDrops. This does require us to specify a lower bound on the total number of UMIs, below which all cells are considered to correspond to empty droplets. This lower bound may not be trivial to derive, but the barcodeRanks function can be useful to identify an elbow/knee point.

In brief, the steps taken by the emtpyDrops function can be summarized as follows:

  1. Define threshold T of total UMI counts (e.g. with the help of the barcodeRanks function), below which cells may be considered to be from empty droplets. Call this set of cells E.

  2. Define \(A_g\) as the total gene expression across all cells in E.

  3. Define \(pi_g\) as the relative contribution of gene g to the ambient profile.

  4. Calculate p-value for each cell to have a transcriptional profile similar to the ambient solution. Intuitively, a p-value below the requested alpha level would correspond to a cell for which the observed count profile strongly deviates from the count profile observed in the cells with a library size below threshold T, i.e., a non-empty droplet.

library(DropletUtils)
bcrank <- barcodeRanks(counts(sce))

# Only showing unique points for plotting speed. Duplicated ranks are a 
# consequence of ties in the ranks, i.e., when cells have an equal library size.
sum(duplicated(bcrank$rank))
## [1] 43864
uniq <- !duplicated(bcrank$rank)

plot(bcrank$rank[uniq], bcrank$total[uniq], log="xy",
    xlab="Rank", ylab="Total UMI count", cex.lab=1.2)
abline(h=metadata(bcrank)$inflection, col="darkgreen", lty=2)
abline(h=metadata(bcrank)$knee, col="dodgerblue", lty=2)
abline(h=350, col="orange", lty=2) # picked visually myself
legend("topright", 
       legend=c("Inflection", "Knee", "Empirical knee point"), 
       col=c("darkgreen", "dodgerblue", "orange"), 
       lty=2, 
       cex=1.2)

set.seed(100)
limit <- 350   
all.out <- emptyDrops(counts(sce), lower=limit, test.ambient=TRUE)
# p-values for cells with total UMI count under the lower bound.
hist(all.out$PValue[all.out$Total <= limit & all.out$Total > 0],
    xlab="P-value", main="", col="grey80", breaks=20)

# but note that it would remove a very high number of cells
length(which(all.out$FDR <= 0.01)) # retained
## [1] 27230
# so we stick to the more lenient adaptive filtering strategy
# remove cells identified using adaptive thresholds
sce <- sce[, !colData(sce)$discardCells]

8.6 Identifying and removing doublets

We will use scDblFinder to detect doublet cells.

As discussed in the theory session of last week, the steps taken by scDblFinder can be summarized as follows:

  1. Perform principal components analysis (PCA) on log-normalized expression counts. This allows for projecting each cell in the dataset into a 2D space (for more details on PCA, see next weeks session).

  2. Randomly select two cells, sum their counts and normalize, and project into PCA space from step 1. In other words, artificially generate doublets and see where they are located in the 2D space.

  3. Repeat step 2 many times (generate many artificial doublets).

  4. Generate neighbor network in the 2D space. The network is then used to estimate a number of characteristics for each cell, in particular the proportion f artificial doublets among the nearest neighbors.

  5. Use this information, along with other predictors, to train a classifier (gradient boosted tree) that allows for distinguishing doublets from singlets.

Note: only classifies for identifying doublets for which the two cells are from different cell type clusters.

## perform doublet detection
library(scDblFinder)
set.seed(211103)
sampleID <- unlist(lapply(strsplit(colData(sce)$cell.id, split="_"), "[[", 1))
table(sampleID)
## sampleID
##   p1   r1   r2   r3   r4   r5   r6 
## 3942 5953 8414 5319 7015 7487 7747
sce <- scDblFinder(sce, 
                   returnType="table",
                   samples = factor(sampleID))
table(sce$scDblFinder.class)
## 
## singlet doublet 
##   41036    4841
## visualize these scores
## explore doublet score wrt original cluster labels
boxplot(log1p(sce$scDblFinder.score) ~ factor(colData(sce)$cluster, exclude=NULL))

tab <- table(sce$scDblFinder.class, sce$cluster, 
      exclude=NULL)
tab
##          
##               1     2     3     4     5     6     7     8     9    10    11
##   singlet   212   364   254    64    66   174   263   142   310   164   175
##   doublet     3    13    21     8     8    31    54     7    27    25    32
##          
##              12    13    14    15    16    17    18    19    20    21    22
##   singlet   206    45    86    52   196   312    64   109   320   173   204
##   doublet    48     5    22    20    55    59    16    16    62    74    57
##          
##              23    24    25    26    27    28    29    30    31    32    33
##   singlet   215 24994  1549  1937   472   343   460   511   412   269   662
##   doublet    44  1949   224   224   157   138   121   119    90    49   151
##          
##              34    35    36    37    38    39  <NA>
##   singlet  1418    36    41   228    57    62  3415
##   doublet   146     7     4    12     2     4   737
t(t(tab) / colSums(tab))
##          
##                    1          2          3          4          5          6
##   singlet 0.98604651 0.96551724 0.92363636 0.88888889 0.89189189 0.84878049
##   doublet 0.01395349 0.03448276 0.07636364 0.11111111 0.10810811 0.15121951
##          
##                    7          8          9         10         11         12
##   singlet 0.82965300 0.95302013 0.91988131 0.86772487 0.84541063 0.81102362
##   doublet 0.17034700 0.04697987 0.08011869 0.13227513 0.15458937 0.18897638
##          
##                   13         14         15         16         17         18
##   singlet 0.90000000 0.79629630 0.72222222 0.78087649 0.84097035 0.80000000
##   doublet 0.10000000 0.20370370 0.27777778 0.21912351 0.15902965 0.20000000
##          
##                   19         20         21         22         23         24
##   singlet 0.87200000 0.83769634 0.70040486 0.78160920 0.83011583 0.92766210
##   doublet 0.12800000 0.16230366 0.29959514 0.21839080 0.16988417 0.07233790
##          
##                   25         26         27         28         29         30
##   singlet 0.87366046 0.89634429 0.75039746 0.71309771 0.79173838 0.81111111
##   doublet 0.12633954 0.10365571 0.24960254 0.28690229 0.20826162 0.18888889
##          
##                   31         32         33         34         35         36
##   singlet 0.82071713 0.84591195 0.81426814 0.90664962 0.83720930 0.91111111
##   doublet 0.17928287 0.15408805 0.18573186 0.09335038 0.16279070 0.08888889
##          
##                   37         38         39       <NA>
##   singlet 0.95000000 0.96610169 0.93939394 0.82249518
##   doublet 0.05000000 0.03389831 0.06060606 0.17750482
barplot(t(t(tab) / colSums(tab))[2,],
        xlab = "Cluster", ylab = "Fraction of doublets")

range(sce$scDblFinder.score[sce$scDblFinder.class  == "doublet" & sampleID == "r1"])
## [1] 0.7053791 0.9999142
range(sce$scDblFinder.score[sce$scDblFinder.class  == "singlet" & sampleID == "r1"])
## [1] 0.0000222011 0.7013915181
# remove doublets
sce <- sce[,!sce$scDblFinder.class == "doublet"]

9 Normalization

Normalization aims to remove technical effects such as sequencing depth so that comparisons between cells are not confounded by them. The most commonly used normalization methods methods use scaling, where a scaling factor (also called size factor, normalization factor) is estimated for each cell. These scaling factors (e.g., the effective library size) can be included as an offset to downstream modeling procedures. This effectively allows for performing inference on the relative abundance of a gene in a cell, after accounting for library size and RNA composition differences between cells, which is much more relevant than comparing raw counts

For normalization, the size factors \(s_i\) computed here are simply scaled library sizes: \[ N_i = \sum_g Y_{gi} \] \[ s_i = N_i / \bar{N}_i \]

sce <- logNormCounts(sce)

# note we also returned log counts: see the additional logcounts assay.
sce
## class: SingleCellExperiment 
## dim: 17887 41036 
## metadata(0):
## assays(2): counts logcounts
## rownames(17887): KITL TMTC3 ... GM16012 GM21464
## rowData names(6): ensembl_gene_id mgi_symbol ... end_position
##   mgi_symbol_upper
## colnames(41036): r1_GGCCGCAGTCCG r1_CTTGTGCGGGAA ... p1_TAACGCGCTCCT
##   p1_ATTCTTGTTCTT
## colData names(15): cell.id cluster ... scDblFinder.cxds_score
##   sizeFactor
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):
# you can extract size factors using
sf <- librarySizeFactors(sce)
mean(sf) # equal to 1 due to scaling.
## [1] 1
plot(x= log(colSums(assays(sce)$counts)), 
     y=sf)

From the OSCA book: Alternatively, we may use more sophisticated approaches for variance stabilizing transformations in genomics data, e.g., DESeq2 or sctransform. These aim to remove the mean-variance trend more effectively than the simpler transformations mentioned above, though it could be argued whether this is actually desirable. For low-coverage scRNA-seq data, there will always be a mean-variance trend under any transformation, for the simple reason that the variance must be zero when the mean count is zero. These methods also face the challenge of removing the mean-variance trend while preserving the interesting component of variation, i.e., the log-fold changes between subpopulations; this may or may not be done adequately, depending on the aggressiveness of the algorithm.

In practice, the log-transformation is a good default choice due to its simplicity and interpretability, and is what we will be using for all downstream analyses.


— end lab session 1 —


10 Normalization (continued)

Normalization is necessary to correct for several sources of technical variation:

  • Differences in sequencing depth between samples. Some samples get sequenced deeper in the sense that they consist of more (mapped) reads and therefore can be considered to contain a higher amount of information, which we should be taking into account. In addition, if a sample is sequenced deeper, it is natural that the counts for each gene will be higher, jeopardizing a direct comparison of the expression counts.
  • Differences in RNA population composition between samples. As an extreme example, suppose that two samples have been sequenced to the exact same depth. One sample is contaminated and has a very high concentration of the contaminant cDNA being sequenced, but otherwise the two samples are identical. Since the contaminant will be taking up a significant proportion of the reads being sequenced, the counts will not be directly comparable between the samples. Hence, we may also want to correct for differences in the composition of the RNA population of the samples (see edgeR manual chapter 2.8).
  • Other technical variation such as sample-specific GC-content or transcript length effects may also be accounted for.
table(sce$cluster)
## 
##     1     2     3     4     5     6     7     8     9    10    11    12    13 
##   212   364   254    64    66   174   263   142   310   164   175   206    45 
##    14    15    16    17    18    19    20    21    22    23    24    25    26 
##    86    52   196   312    64   109   320   173   204   215 24994  1549  1937 
##    27    28    29    30    31    32    33    34    35    36    37    38    39 
##   472   343   460   511   412   269   662  1418    36    41   228    57    62

Below, we will visualize the normalization occurring between two cells of the same cell type (which could be considered technical repeats):

select <- sce$cluster == "1"
select[is.na(select)] <- FALSE
cs <- colSums(assays(sce)$counts[,select])
cs[order(cs, decreasing = TRUE)][c(1,10)]
## r2_GTTTACTTCCCC r1_GATTTCCTCTGA 
##           20869           14719

Let’s take a look at how comparable two cells (replicates) of cluster 1 are. We will compare the cell with the highest library size with the cell that has the 10th highest library size using MD-plots (mean-difference plots, as introduced by Dudoit et al. (2002)), also sometimes referred to as MA-plots.

targetCells <- names(cs[order(cs, decreasing = TRUE)][c(1,10)])

M <- rowMeans(assays(sce)$counts[,targetCells])
D <- assays(sce)$counts[,targetCells[2]] / assays(sce)$counts[,targetCells[1]]
plot(x = log(M), y = log2(D),
     pch = 16, cex=1/3,
     main = paste0("Cell ", targetCells[2], " vs cell ", targetCells[1]),
     xlab = "Log mean", ylab = "Log2 fold-change",
     bty = 'l')
abline(h = 0, col="orange", lwd=2)

We see clear bias in the comparison of the 1st and 10th most deeply sequenced cell from cell cluster 1. We see that the log fold-changes are biased downwards. This means that, on average, a gene is higher expressed in cell 1 versus cell 10. Looking at the library sizes, we can indeed see that the library size for cell 1 is 20869 counts, while it is only 14719 counts for cell 10! This is a clear library size effect that we should take into account.

# normalize the count data using the previously computed "size factors"
assay(sce, "normed") <- normalizeCounts(sce, 
                                        log=FALSE,
                                        size.factors=sce$sizeFactor, 
                                        pseudo.count=0)

M <- rowMeans(assays(sce)$normed[,targetCells])
D <- assays(sce)$normed[,targetCells[2]] / assays(sce)$normed[,targetCells[1]]
plot(x = log(M), y = log2(D),
     pch = 16, cex=1/3,
     main = paste0("Cell ", targetCells[2], " vs cell ", targetCells[1]),
     xlab = "Log mean", ylab = "Log2 fold-change",
     bty = 'l')
abline(h = 0, col="orange", lwd=2)

Upon normalizing the data using size factors, we have removed bias as a consequence of differences in sequencing depth.

Note that we computed the normalized count matrix only for demonstrating the effect of such normalization. In a typical workflow, the size factors are used as offsets in the downstream models rather than to perform data transformation. In brief, such transformation would distort the mean-variance relationship in the data. For more details, we refer to the document that was touched upon in the first theory session (link)

11 Feature selection

As dimensions increase, shortest and farthest distances between points become nearly inseparable. In high-dimensional space, it is therefore extremely difficult to separate signal from noise.

In order to recover structure (e.g. setting up a dimension-reduced space to help us find cell-type clusters in the data), we want to move to an informative, lower-dimensional space. We will select genes which we hope are informative for recovering the biological structure. But what defines an informative gene?

11.1 Selecting genes with high variance

The simplest approach to quantifying per-gene variation is to compute the variance of the log-normalized expression values (i.e., “log-counts”) for each gene across all cells.

# calculate variance of log-normalized counts for each gene
geneVars <- genefilter::rowVars(assays(sce)$logcounts)
# select top 1000 highly variable genes
highVarGenes <- names(geneVars)[order(geneVars, decreasing=TRUE)[1:1e3]]
head(highVarGenes)

11.2 Selecting genes with high variation with respect to mean

While calculation of the per-gene variance is simple, feature selection requires modelling of the mean-variance relationship. Indeed, not accounting for the mean-variance structure while selecting highly variable genes will oftentimes boil down to selecting the most highly expressed genes. The log-transformation is a helpful variance stabilizing transformation, however, it is not perfect, meaning that the variance of a gene is not completely independent of its mean. Therefore, feature selection may still be driven by average expression rather than underlying biological heterogeneity.

A (rightly so) popular approach is to select genes that have a high variance with respect to their mean. Often, first an empirical mean-variance trend is fitted, upon which genes with the highest positive residuals are selected. Being intuitive, reasonable and fairly straight-forward, this method is widely used.

To account for the mean-variance effect, we use the modelGeneVar function of the scran package to fit a trend to the variance with respect to abundance across all genes (on log-normalized expression values of the sce object).

library(scran)
dec <- modelGeneVar(sce)
head(dec)
## DataFrame with 6 rows and 6 columns
##                      mean       total        tech          bio   p.value
##                 <numeric>   <numeric>   <numeric>    <numeric> <numeric>
## KITL          0.008777743 0.011468955 0.011567858 -9.89021e-05  0.525710
## TMTC3         0.047808247 0.070339120 0.062648596  7.69052e-03  0.177233
## CEP290        0.538342285 0.797002183 0.654153448  1.42849e-01  0.049759
## 4930430F08RIK 0.048310644 0.063629062 0.063302338  3.26723e-04  0.484472
## 1700017N19RIK 0.000483927 0.000534856 0.000638521 -1.03665e-04  0.889643
## MGAT4C        0.015591625 0.015819602 0.020527248 -4.70765e-03  0.958176
##                     FDR
##               <numeric>
## KITL                  1
## TMTC3                 1
## CEP290                1
## 4930430F08RIK         1
## 1700017N19RIK         1
## MGAT4C                1

The fitted value for each gene is used as a proxy for the technical component of variation for each gene, under the assumption that most genes exhibit a low baseline level of variation that is not biologically interesting. The biological component of variation for each gene is defined as the the residual from the trend. Ranking genes by the biological component enables identification of interesting genes for downstream analyses in a manner that accounts for the mean-variance relationship.

fitRetina <- metadata(dec)
plot(fitRetina$mean, fitRetina$var, 
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(fitRetina$trend(x), col="dodgerblue", add=TRUE, lwd=2)

We are interested in those genes for which the variance in expression is higher than what we would expect for that gene based on its mean expression.

# get 10% most variable genes
hvg <- getTopHVGs(dec, 
                  prop=0.1)
head(hvg)
## [1] "RHO"     "CALM1"   "MEG3"    "GNGT1"   "RPGRIP1" "TRPM1"
# plot these 
plot(fitRetina$mean, fitRetina$var, 
     col = c("orange", "darkseagreen3")[(names(fitRetina$mean) %in% hvg)+1],
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(fitRetina$trend(x), col="dodgerblue", add=TRUE, lwd=2)
legend("topleft", 
       legend = c("Selected", "Not selected"), 
       col = c("darkseagreen3", "orange"),
       pch = 16,
       bty='n')

As a comparison, we could color the genes on this figure according to the selection that was made purely by looking at the raw variance of each gene.

plot(fitRetina$mean, fitRetina$var, 
     col = c("orange", "steelblue")[(names(fitRetina$mean) %in% highVarGenes)+1],
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(fitRetina$trend(x), col="dodgerblue", add=TRUE, lwd=2)
legend("topleft", 
       legend = c("Selected", "Not selected"), 
       col = c("steelblue", "orange"),
       pch = 16,
       bty='n')

As expected, we here simply select genes with a high variance, without recognizing that a high variance is typically driven by a high mean.

11.3 High deviance genes

Here, we will select genes with a high residual deviance. The idea is that we assume a null model of constant expression fraction (i.e., RNA concentration) across all cells. We subsequently calculate a goodness-of-fit statistic for each gene, assessing whether the model is a good approximation to the gene expression of the corresponding gene.

If the model fits poorly, i.e., the gene has a high deviance, the expression fraction varies significantly across the cells in our datasets.Genes with a high deviance will thus most poorly fit a null model where the relative abundance is equal for all cells, which therefore are informative.

#BiocManager::install("scry")
library(scry)
sce <- devianceFeatureSelection(object = sce, 
                                assay = "counts", 
                                sorted = FALSE)

plot(sort(rowData(sce)$binomial_deviance, decreasing = TRUE), 
     type="l", 
     xlab="ranked genes", 
     ylab="binomial deviance", 
     main="Feature Selection with Deviance")
abline(v=2000, lty=2, col="red")

Our plot looks similar to one displayed in the vignette of the scry package. Based on that plot, the authors suggest retaining 2.000 genes (the top 2000 based on the deviance residuals) for downstream dimensionality reduction and clustering.

11.4 Seurat VST

Another very common feature selection strategy is the variance-stabilizing transformation from the Seurat R package, which amounts to calculating Pearson residuals from a regularized negative binomial regression model, with sequencing depth as a covariate.

Intermezzo: interoperability between SingleCellExperiment and Seurat objects

In this lecture series, we always make use of the SingleCellExperiment object and the packages available from Bioconductor. Another very popular toolbox for performing scRNA-seq data analysis is Seurat. However, functions from Seurat cannot be used directly to manipulate SingleCellExperiment objects, and vice versa. Fortunately, efforts have been made to increase the interoperability between the two toolboxes.

library(Seurat)
## Attaching SeuratObject
## 
## Attaching package: 'Seurat'
## The following object is masked from 'package:SummarizedExperiment':
## 
##     Assays
seurat_obj <- as.Seurat(sce)
## Warning: Feature names cannot have underscores ('_'), replacing with dashes
## ('-')

## Warning: Feature names cannot have underscores ('_'), replacing with dashes
## ('-')
seurat_obj # notice the "0 variable features"
## An object of class Seurat 
## 17887 features across 41036 samples within 1 assay 
## Active assay: originalexp (17887 features, 0 variable features)

On this object, we may use functions from the Seurat toolbox. For instance, we may search for highly variable features using Seurat’s VST implementation:

seurat_obj <- Seurat::NormalizeData(seurat_obj, 
                                    normalization.method = "LogNormalize", 
                                    scale.factor = 10000)

seurat_obj <-  FindVariableFeatures(object = seurat_obj,
                                    selection.method = "vst")
seurat_obj  # notice the "2000 variable features" (default)
## An object of class Seurat 
## 17887 features across 41036 samples within 1 assay 
## Active assay: originalexp (17887 features, 2000 variable features)
head(VariableFeatures(seurat_obj)) # here they are
## [1] "CARTPT" "HBB-BS" "RGS5"   "HBA-A1" "HBA-A2" "NEFL"

12 Dimensionality reduction

Note that, below, we color the cells using the known, true cell type label as defined in the metadata, to empirically evaluate the dimensionality reduction. In reality, we don’t know this yet at this stage.

12.1 The most basic DR

Just by looking at the top two genes based on our feature selection criterion, we can already see some separation according to the cell type!

colData(sce)$cluster <- as.factor(colData(sce)$cluster)
cl <- colData(sce)$cluster
par(bty='l')
plot(x = assays(sce)$counts[hvg[1],],
     y = assays(sce)$counts[hvg[2],],
     col = as.numeric(cl),
     pch = 16, cex = 1/3,
     xlab = "Most informative gene",
     ylab = "Second most informative gene",
     main = "Cells colored acc to cell type")

We are able to recover quite some structure. However, many cell populations remain obscure, and the plot is overcrowded.

12.2 Linear dimensionality reduction: PCA

A DR method is linear when the reduced dimensions are a linear function of the original variables. For example, in PCA, each principal component is a linear combination of genes, therefore the DR is a linear function of the original variables.

Typically, PCA is performed on log-transformed normalized counts. The log-transformation helps somewhat, but not completely, to account for the mean-variance relationship. PCA works well for bulk RNA-seq data. However, the structure of scRNA-seq data is often too complex to be visualized by a small number of PCs.

There are several R functions that allow you to perform PCA. Here, we make use of the runPCA function of the scater package, which has been specifically developed for performing PCA on SingleCellExperiment objects. We calculate the top 30 principal components.

12.2.1 PCA with feature selection

set.seed(1234)
sce <- runPCA(sce, 
              ncomponents=30, 
              subset_row=hvg)

PCA has been performed. The PCA information has been automatically stored in the reducedDim slot of the SingleCellExperiment object.

reducedDimNames(sce)
## [1] "PCA"
head(reducedDim(sce,
           type="PCA"))

The plotPCA function of the scater package now allows us to visualize the cells in PCA space, based on the PCA information stored in our object:

plotPCA(sce, 
        colour_by = "cluster")

While the large number of clusters in this dataset makes it hard to distinguish between all the different colors, we can already see that PCA retrieves some, but not all, of the structure in the data that was discovered by the original authors.

How many of the top PCs should we retain for downstream analyses? The choice of the number of PCs is a decision that is analogous to the choice of the number of HVGs to use. Using more PCs will retain more biological signal at the cost of including more noise that might mask said signal. On the other hand, using fewer PCs will introduce competition between different factors of variation, where weaker (but still interesting) factors may be pushed down into lower PCs and inadvertently discarded from downstream analyses.

Most analysts will simply aim to use a “reasonable” but arbitrary value, typically ranging from 10 to 50. This is often satisfactory as the later PCs explain so little variance that their inclusion or omission has no major effect.

percent.var <- attr(reducedDim(sce), "percentVar")
plot(percent.var, log="y", xlab="PC", ylab="Variance explained (%)")

plot(cumsum(percent.var), xlab="PC", ylab="Cumulative variance explained (%)")

Here, retaining ±15PCs seems reasonable. If you really prefer a more data-driven way for determining this, there are procedures available that aim to computationally identify the elbow/knee point in the variance explained per PC plot.

library(PCAtools)
## Loading required package: ggrepel
## 
## Attaching package: 'PCAtools'
## The following object is masked from 'package:scran':
## 
##     parallelPCA
## The following objects are masked from 'package:stats':
## 
##     biplot, screeplot
chosen.elbow <- findElbowPoint(percent.var)
chosen.elbow
## [1] 5

12.2.2 PCA without feature selection

Note: more features -> computationally more intensive!

set.seed(1234)
sceNoFS <- runPCA(sce, 
                  ncomponents=30, 
                  subset_row=1:nrow(sce))
plotPCA(sceNoFS, colour_by = "cluster")
rm(sceNoFS)

While we use more information to make this PCA plot (17.887 genes) as compared to the feature selected PCA plot (642 genes), we seem to retrieve less structure in the data. This is the power of feature selection, an increase in the signal-to-noise ratio!

12.2.3 Effect of feature selection on PCA

First, we compare the different feature selection criteria, using the top 1000 highly variable genes for each method.

# Get top 1000 highly variable features using the highly variable genes
# (w.r.t. mean), high deviance genes and the VST strategy of Seurat

hvg1000 <- getTopHVGs(dec, n = 1000)
hdg1000 <- names(sort(rowData(sce)$binomial_deviance, decreasing = TRUE))[1:1000]
vst1000 <- VariableFeatures(seurat_obj)[1:1000]

# HVG strategy
plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg1000), 
    colour_by = "cluster") +
  ggtitle("Highly variable genes")

# HDG strategy
plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hdg1000), 
    colour_by = "cluster") +
  ggtitle("High-deviance genes")

# VST strategy
plotPCA(
    runPCA(sce,
           ncomponents = 2,
           subset_row = vst1000),
    colour_by = "cluster",
    ) +
  ggtitle("Seurat VST")

Next, we assess the sensitivity on the number of top features for the highly variable genes method;

hvg_all <- getTopHVGs(dec)

hvg2000 <- hvg_all[1:2000]
hvg1000 <- hvg_all[1:1000]
hvg100 <- hvg_all[1:100]
hvg10 <- hvg_all[1:10]
hvg5 <- hvg_all[1:5]

plotPCA(
    runPCA(sce,
           ncomponents = 2), 
    colour_by = "cluster") +
  ggtitle("All genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg2000), 
    colour_by = "cluster") +
  ggtitle("Top 2000 genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg1000), 
    colour_by = "cluster") +
  ggtitle("Top 1000 genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg100), 
    colour_by = "cluster") +
  ggtitle("Top 100 genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg10), 
    colour_by = "cluster") +
  ggtitle("Top 10 genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg5), 
    colour_by = "cluster") +
  ggtitle("Top 5 genes")

12.2.4 Remarks on PCA

Visualizations of reduced dimensions from linear dimensionality reduction methods are often “overcrowded”, and it is hard to see structure (e.g., the PCA plot we just saw). Non-linear dimensionality reduction methods can overcome this problem. As the name suggests, the reduced dimensions are a non-linear function of the observed data. We will not go into detail as to how these work under the hood, but provide a few guidelines for the most popular methods. Often, the top (~10-50) PCs are provided as input.

12.3 A generalization of PCA for exponential family distributions.

PCA is implicitly based on Euclidean distances, corresponding to maximizing a Gaussian likelihood, which is inappropriate for count data such as scRNA-seq. Townes et al. (2019) develop GLM-PCA, a generalization of PCA for exponential family likelihoods. They posit, using negative control data, that the data generative mechanism of UMI count data can be considered to be multinomial.

The GLM-PCA strategy is implemented in the glmpca function of the glmpca package.

Note that this function is quite computationally intensive. For regular PCA on log-transformed normalized counts, the underlying computations can be strongly simplified. Here, we work with the raw counts, which we assume to be Poisson distributed, requiring an iterative optimization scheme.

# runs 2min on my laptop
library(glmpca)
set.seed(211103)
poipca <- glmpca(Y = assays(sce)$counts[hvg,],
                 L = 2, 
                 fam = "poi",
                 minibatch = "stochastic")
reducedDim(sce, "PoiPCA") <- poipca$factors
plotReducedDim(sce, 
               dimred="PoiPCA",
               colour_by = "cluster")

Alternatively, we could adopt the GLM-PCA strategy using only the genes with high deviance.

# Based on the diagnostic plot from the feature selection, we would select the 
# top 2000 genes with highest deviance. However, to reduce the running time of
# the function, I here select the top 500 (which still is quite slow - 5min).
Sys.time()
hdg <- names(sort(rowData(sce)$binomial_deviance, decreasing = TRUE))[1:500]

set.seed(471681)
poipca_dev <- glmpca(Y = assays(sce)$counts[hdg,],
                 L = 2, 
                 fam = "poi",
                 minibatch = "stochastic")
Sys.time()
reducedDim(sce, "PoiPCA_dev") <- poipca_dev$factors
plotReducedDim(sce, 
               dimred="PoiPCA_dev",
               colour_by = "cluster")

The authors of the glmpca package note that GLM-PCA can be slow for large datasets. Therefore, they have implemented a fast approximation of the algorithm, which first fits a null model of constant expression for each gene across all cells, and subsequently fits standard PCA to either the Pearson or deviance residuals from the null model.

However, at least for me the nullResiduals function was extremely slow even slower than the glmpca code above. Therefore, I suggest not running this code, but I leave it in for reference.

sce <- nullResiduals(sce, assay="counts", type="deviance")
sce <- nullResiduals(sce, assay="counts", type="pearson")

pca<-function(Y, L=2, center=TRUE, scale=TRUE){
    #assumes features=rows, observations=cols
    res<-prcomp(as.matrix(t(Y)), center=center, scale.=scale, rank.=L)
    factors<-as.data.frame(res$x)
    colnames(factors)<-paste0("dim", 1:L)
    factors
}
pca_d <- pca(assay(sce[hdg,], "binomial_deviance_residuals"))
pca_d$resid_type <- "deviance_residuals"
pca_p <- pca(assay(sce[hdg,], "binomial_pearson_residuals"))
pca_p$resid_type <- "pearson_residuals"
cm <- as.data.frame(colData(sce[hdg,]))
pd <- rbind(cbind(cm, pca_d), cbind(cm, pca_p))
ggplot(pd, aes(x=dim1, y=dim2, colour=phenoid)) + geom_point() +
  facet_wrap(~resid_type, scales="free", nrow=2) +
  ggtitle("PCA applied to null residuals of high deviance genes")

12.4 Non-linear dimensionality reduction: t-SNE

t-SNE focuses on preserving local rather than global distances. Therefore, distances on a t-SNE reduced dimension plot can only be interpreted locally, i.e., cells that are close together in reduced dimension will have a similar transcriptome, but cells that are far away may not necessarily have a very distinct transcriptome.

Running t-SNE on a SingleCellExperiment object can be achieved with the runTSNE function of the scater package. By default, this function will first perform PCA, and use the top 50 PCs as an input to the actual t-SNE algorithm. Since we already performed PCA, we may set dimred = "PCA" as argument to the function. As such we will be performing a T-SNE on the 30 PCs we computed before. If we would like to run t-SNE only using the top 10 PCs, we could set n_dimred = 10.

In addition, we may wish to set external_neighbors=TRUE, which increases the speed of the algorithm for large datasets by applying a heuristic.

Note: for me this was the slowest function of the analysis (so far). If you feel like your PC/laptop had a lot of trouble with the previous step(s), you may consider not running this code, or running it on a subset of the data (e.g., randomly subsampling cells) for demonstrational purposes. Alternatively, you may consider reducing the input space for the t-SNE algorithm, e.g. by setting n_dimred = 5.

# (For me it takes 3min30 with n_dimred = 5)
sce <- runTSNE(sce, 
               dimred = 'PCA',
               n_dimred = 5,
               external_neighbors=TRUE)
# gg <- plotTSNE(sce,
#          colour_by = "cluster")
# 
# gg

12.5 Non-linear dimensionality reduction: UMAP

It is often suggested that UMAP is better than t-SNE in preserving global differences. Therefore, UMAP is also often used in analyses such as trajectory inference, where this is important.

Running UMAP on a SingleCellExperiment object can be achieved with the runUMAP function of the scater package.

# Using top 10% highly variable genes and top 30 PCs
sce <- runUMAP(sce, 
               dimred = 'PCA', 
               external_neighbors = TRUE)
# gg <- plotUMAP(sce,
#          colour_by = "cluster")
# 
# gg

# Using top 10% highly variable genes and top 10 PCs
# gg <- plotUMAP(runUMAP(sce, 
#                dimred = 'PCA', 
#                external_neighbors = TRUE,
#                n_dimred = 10),
#   colour_by = "cluster")
# 
# gg

— end lab session 2 —


13 Read results from first two sessions

sce <- readRDS("./sce_after_labsession2.rds") # change to YOUR path!

14 Add cluster information from publication

library(tidyverse)
sce$cluster_lowRes <- fct_recode(colData(sce)$cluster, 
           "Horizontal_cells" = "1",
           "Ganglion_cells" = "2",
           "Amacrine" = "3",
           "Amacrine" = "4",
           "Amacrine" = "5",
           "Amacrine" = "6",
           "Amacrine" = "7",
           "Amacrine" = "8",
           "Amacrine" = "9",
           "Amacrine" = "10",
           "Amacrine" = "11",
           "Amacrine" = "12",
           "Amacrine" = "13",
           "Amacrine" = "14",
           "Amacrine" = "15",
           "Amacrine" = "16",
           "Amacrine" = "17",
           "Amacrine" = "18",
           "Amacrine" = "19",
           "Amacrine" = "20",
           "Amacrine" = "21",
           "Amacrine" = "22",
           "Amacrine" = "23",
           "Rods" = "24",
           "Cones" = "25",
           "Bipolar" = "26",
           "Bipolar" = "27",
           "Bipolar" = "28",
           "Bipolar" = "29",
           "Bipolar" = "30",
           "Bipolar" = "31",
           "Bipolar" = "32",
           "Bipolar" = "33",
           "Muller_glia" = "34",
           "Astrocytes" = "35",
           "Fibroblast" = "36",
           "Vascular_endothelium" = "37",
           "Pericytes" = "38",
           "Microglia" = "39")

sce$cluster_highRes <- fct_recode(colData(sce)$cluster, 
           "Horizontal_cells" = "1",
           "Ganglion_cells" = "2",
           "Amacrine_1" = "3",
           "Amacrine_2" = "4",
           "Amacrine_3" = "5",
           "Amacrine_4" = "6",
           "Amacrine_5" = "7",
           "Amacrine_6" = "8",
           "Amacrine_7" = "9",
           "Amacrine_8" = "10",
           "Amacrine_9" = "11",
           "Amacrine_10" = "12",
           "Amacrine_11" = "13",
           "Amacrine_12" = "14",
           "Amacrine_13" = "15",
           "Amacrine_14" = "16",
           "Amacrine_15" = "17",
           "Amacrine_16" = "18",
           "Amacrine_17" = "19",
           "Amacrine_18" = "20",
           "Amacrine_19" = "21",
           "Amacrine_20" = "22",
           "Amacrine_21" = "23",
           "Rods" = "24",
           "Cones" = "25",
           "Bipolar_1" = "26",
           "Bipolar_2" = "27",
           "Bipolar_3" = "28",
           "Bipolar_4" = "29",
           "Bipolar_5" = "30",
           "Bipolar_6" = "31",
           "Bipolar_7" = "32",
           "Bipolar_8" = "33",
           "Muller_glia" = "34",
           "Astrocytes" = "35",
           "Fibroblast" = "36",
           "Vascular_endothelium" = "37",
           "Pericytes" = "38",
           "Microglia" = "39")

15 Clustering

15.1 Graph-based clustering

First, we discuss graph-based clustering methods for scRNA-Seq data. This is very well explained in the OSCA book chapter 5.2.

As written in the OSCA book: “Popularized by its use in Seurat, graph-based clustering is a flexible and scalable technique for clustering large scRNA-seq datasets. We first build a graph where each node is a cell that is connected to its nearest neighbors in the high-dimensional space. Edges are weighted based on the similarity between the cells involved, with higher weight given to cells that are more closely related. We then apply algorithms to identify “communities” of cells that are more connected to cells in the same community than they are to cells of different communities. Each community represents a cluster that we can use for downstream interpretation.

The major advantage of graph-based clustering lies in its scalability. It only requires a k-nearest neighbor search that can be done in log-linear time on average, in contrast to hierarchical clustering methods with runtimes that are quadratic with respect to the number of cells. Graph construction avoids making strong assumptions about the shape of the clusters or the distribution of cells within each cluster, compared to other methods like k-means (that favor spherical clusters) or Gaussian mixture models (that require normality)."

Several graph-based clustering algorithms are implemented in the scran library. The most global wrapper-function in this package is the clusterCells function. Typically, the input to this function is a SingleCellExperiment object with pre-computed principal components; these are used to take advantage of data compression and denoising. If the default settings are adopted, clusterCells will perform two steps under the hood:

  1. Build a shared nearest neighbors (SNN) graph of observations for downstream community detection. The SNN graph is closely related to the more common KNN graph. For each observation, its k-nearest neighbors are identified (k=10 by default), based on distances between their expression profiles (Euclidean distances are used by default) as observed in PCA space. An edge is drawn between all pairs of observations that share at least one neighbor, weighted by the characteristics of the shared nearest neighbors.

  2. The clusterCells function next internally calls the cluster_walktrap function from the igraph library. Based on the SNN graph from step 1, this function tries to find densely connected subgraphs, also called communities in a graph via random walks. The idea is that short random walks tend to stay in the same community.

# Do not run; clusterCells function with default settings, i.e., building an
# SNN graph and finding clusters with the walktrap algorithm.
library(scran)
nn.clusters <- clusterCells(sce, 
                            use.dimred="PCA")
table(nn.clusters)

The disadvantage of using clusterCells is that the default setting of the second step, the cluster_walktrap function, is slow for large datasets. While it is possible to adjust the different arguments of the clusterCells function, it might be more clear and intuitive to simply break up the process in two steps: building the graph and detecting clusters in that graph. For this second step, we may then adopt a faster algorithm.

15.1.1 Build graph (SNN graph)

# Build a shared nearest-neighbor graph from PCA space
graph <- buildSNNGraph(sce, 
                       use.dimred = 'PCA')
# alternative: buildKNNGraph()

15.1.2 Detect clusters on the graph

Two popular graph-based clustering algorithm are the leiden and louvain algorithms, both referring to the location of its developers. A common implementation of the louvainalgorithm is to optimize the modularity, effectively attempting to maximize the difference between the observed number of edges in a community and the expected number of such edges.

However, additional evaluations found that modularity optimization using the louvain algorithm is confined to a resolution limit, and in addition may result in communities that are not well connected. The leiden algorithm, instead, guarantees well-connected communities.

set.seed(464688)
# Walktrap community finding algorithm on the SNN graph
# DO NOT RUN -> takes 20 minutes
# cluster_walktrap <- factor(igraph::cluster_walktrap(g)$membership) #20min

# The `cluster_fast_greedy` function tries to find dense subgraph, also called 
# communities in graphs via directly optimizing a modularity score
# DO NOT RUN -> takes 4 minutes
# cluster_fastGreedy <- factor(igraph::cluster_fast_greedy(graph)$membership) #4min

# Louvain clustering on the SNN graph
cluster_louvain <- factor(igraph::cluster_louvain(graph)$membership) #8sec
nlevels(cluster_louvain) # 11 clusters

# Leiden clustering on the SNN graph
cluster_leiden <- factor(igraph::cluster_leiden(graph)$membership) #10sec
nlevels(cluster_leiden) # 1326 different clusters!

cluster_leiden2 <- factor(igraph::cluster_leiden(graph = graph,
                                                 resolution_parameter = 0.01)$membership) #10sec
nlevels(cluster_leiden2) #14 different clusters

# Add to sce and visualize
colData(sce)$cluster_louvain <- cluster_louvain
colData(sce)$cluster_leiden2 <- cluster_leiden2

15.1.3 Comparing clustering strategies

A direct comparison of the Louvain and Leiden clustering results using a table of the cluster labels, shows good agreement.

table(cluster_louvain, cluster_leiden2)

First, note that Leiden2 clusters 9-14 all contain only 1 cell, which doesn’t make much sense.

Next, we see the following clear correspondences between the cluster labels:

Louvain 1 -> Leiden 1 Louvain 6 -> Leiden 2 Louvain 3 -> Leiden 4 Louvain 7 -> Leiden 6 Louvain 8 -> Leiden 7 Louvain 10 -> Leiden 8

For the remaining clusters, we see that - Leiden cluster 3 contains almost cells of Louvain clusters 2, 9 and 11 - Leiden cluster 5 contains almost cells of Louvain clusters 4 and 5

To make a visualization that gives us very similar information, we may use a heatmap:

library(pheatmap)
pheatmap::pheatmap(table(cluster_louvain, cluster_leiden2))

Alternatively, we may compute a clustering similarity score that captures the agreement between two sets of partitions. The Adjusted Rand Index (ARI) is often used in the literature for this purpose. The ARI is equal to 1 if the two partitions agree perfectly, and it is zero if the two partitions are unrelated. In some cases, the ARI may also be negative if the partitions are much more different than what could be expected by chance.

library(mclust)
mclust::adjustedRandIndex(cluster_louvain[-which(cluster_leiden2 %in% c(9:14))], 
                          cluster_leiden2[-which(cluster_leiden2 %in% c(9:14))])

15.1.4 Visualization

# Visualization. Add the cluster labels to the previously generated TSNE
# coordinates
plotTSNE(sce, 
         colour_by="cluster_louvain")

plotTSNE(sce, 
         colour_by="cluster_leiden2")

15.2 K-means clustering

K-means is a clustering algorithm that has been used in many application areas. In R, it can be applied via the kmeans function. Typically, it is applied to a reduced dimensional representation of the expression data (most often PCA). We need to define the number of clusters in advance. Since the results depend on the initialization of the cluster centers, it is typically recommended to run k-means with multiple starting configurations (via the nstart argument). For reproducibility, we also strongly advise to set a seed.

set.seed(123)

# k=10
clust_kmeans_k10 <- kmeans(reducedDim(sce, "PCA"), centers = 10, nstart = 5)
table(clust_kmeans_k10$cluster)
colData(sce)$kmeans10 <- factor(clust_kmeans_k10$cluster)
plotTSNE(sce, colour_by="kmeans10")

# k=39
clust_kmeans_k39 <- kmeans(reducedDim(sce, "PCA"), centers = 39, nstart = 5)
table(clust_kmeans_k39$cluster)
colData(sce)$kmeans39 <- factor(clust_kmeans_k39$cluster)
plotTSNE(sce, colour_by="kmeans39")

We here arbitrarily performed two k-means clustering analyses, once with k=10 and once with k=39 (the number of clusters communicated by the authors). The choice of the number of clusters k can be guided by known biology, however, it is arbitrary at least to some interval.

15.3 Hierarchical clustering

From OSCA book chapter 5.4:

"Hierarchical clustering is an old technique that arranges samples into a hierarchy based on their relative similarity to each other. Most implementations do so by joining the most similar samples into a new cluster, then joining similar clusters into larger clusters, and so on, until all samples belong to a single cluster. This process yields a dendrogram that defines clusters with progressively increasing granularity. Variants of hierarchical clustering methods primarily differ in how they choose to perform the agglomerations. For example, complete linkage aims to merge clusters with the smallest maximum distance between their elements, while Ward’s method aims to minimize the increase in within-cluster variance.

In the context of scRNA-seq, the main advantage of hierarchical clustering lies in the production of the dendrogram. This is a rich summary that quantitatively captures the relationships between subpopulations at various resolutions. Cutting the dendrogram at high resolution is also guaranteed to yield clusters that are nested within those obtained at a low-resolution cut; this can be helpful for interpretation."

Indeed, low-resolution clusters can typically be interpreted as super-level cell types, like immune cells, neuron cells or endothelial cells. Higher resolution clusters correspond with a higher biological resolution: immune cell -> lymphocyte -> T-cell -> Th1 cell.

However, note that we can also overcluster the data (splitting a homogenous set of cells in multiple clusters), resulting in spurious cell type identification.

The clusterCells function of the scran library also allows for performing hierarchical clustering. This can be implemented as follows:

# takes 4 minutes
library(bluster)
hclust.sce <- clusterCells(x = sce, 
                            use.dimred = "PCA",
                            BLUSPARAM = HclustParam(method="ward.D2"))

Equivalently, we may again split the process in two steps:

  1. Compute the pairwise distances between all cells. These are by default Euclidean distances and, in order to reduce data complexity and increase signal to noise, we may perform this on the top (30) PC’s, just like we did when constructing the SNN graph in graph-based clustering. Calculating a dissimilarity matrix is implemented in the dist function.

  2. Perform a hierarchical clustering on the distances from step 1. In an agglomerative procedure, each cell is first assigned to its own cluster and then the algorithm proceeds iteratively, at each stage joining the two most similar clusters, continuing until there is just a single cluster. Implemented in the hclust function.

Note that the hclust function allows for specifying a “method” argument. The differences between the different methods goes beyond the scope of this session, but a brief description is provided in the function help file. In the context of scRNA-seq, we recommend the use of the "ward.D2" method.

distsce <- dist(reducedDim(sce, "PCA")) #1min
hcl <- hclust(distsce, method = "ward.D2") #3min
plot(hcl, labels = FALSE)

Next, in order to derive a given set of cluster labels, we need to “cut the tree”, i.e., choose at which resolution we want to report the (cell type) clusters. This can be achieved with the cutree function. As an input, cutree takes the dendrogram from the hclust function and a threshold value for cutting the tree. The latter may be either k, the number of clusters we want to report, or h, the height of the dendrogram at which we want to cut the tree.

# cut to get 10 clusters
clust_hcl_k10 <- cutree(hcl, k = 10)
table(clust_hcl_k10)
# cut to get 39 clusters
clust_hcl_k39 <- cutree(hcl, k = 39)
table(clust_hcl_k39)
sce$clust_hcl_k10 <- as.factor(clust_hcl_k10)
sce$clust_hcl_k39 <- as.factor(clust_hcl_k39)

# Visualization. Add the cluster labels to the previously generated TSNE
# coordinates
plotTSNE(sce, 
         colour_by="clust_hcl_k10")

plotTSNE(sce, 
         colour_by ="clust_hcl_k39")

16 Clustering in the original paper

When we compare our cluster labels with those from the original paper, we’ll see that the correspondence is not great. As a demonstration, I make a table and a heatmap comparing the low-resolution cluster labels from the paper with our Louvain, Leiden2 and hierarchical clustering (k=10) labels:

table(sce$cluster_lowRes, sce$cluster_louvain)
table(sce$cluster_lowRes, sce$cluster_leiden2)
table(sce$cluster_lowRes, sce$clust_hcl_k10)
pheatmap::pheatmap(table(sce$cluster_lowRes, sce$cluster_louvain))

pheatmap::pheatmap(table(sce$cluster_lowRes, sce$cluster_leiden2))

pheatmap::pheatmap(table(sce$cluster_lowRes, sce$clust_hcl_k10))

Also, when we look at the t-SNE from the original publication, we observe clearly distinct clusters:

The main reason for this is that the authors of the original paper used quite a different strategy for performing the feature selection and dimension reduction that we have performed in lab session 2.

To demonstrate the pipeline of the original authors, and to make our results more comparable to theirs, We will here mimic their strategy for feature selection and clustering. However, we will do this approximatively! We will take similar steps conceptually, but will remain within the current Bioconductor framework and the range of functions that we have seen in this lecture series.

The authors performed the following steps:

  1. Filtering: The authors first filtered the 49,300-cell dataset to retain only single-cell libraries where at least 900 genes were detected.

  2. Feature selection: The authors first identified the set of genes that were most variable across the selected susbet of cells, after controlling for the relationship between mean expression and variability. To do this, the authors adopted a manual implementation.

-> We will not use the exact same strategy, but the modelGeneVar-getTopHVGs strategy, which is conceptually similar in addressing the mean-variance relationship during feature selection.

  1. Principal component analysis: The authors performed PCA after scaling the data. They next performed a test to determine how many PCs contributed significantly to explaining the variability in the data. Based on this test, they continued the analysis pipeline with the top 32 PCs.

  2. t-SNE: Next, the authors performed a t-SNE on the top 32 PC’s, setting the perplexity parameter of the t-SNE algorithm to 30.

  3. Projection of remaining cells and clustering: Finally, the authors adopted a manually implemented, rather complex strategy to project the remaining cells (where less than 900 different genes were detected) on the t-SNE embedding obtained in step 4. Next, they cluster the cells using a density clustering (DBSCAN algorithm) that was not discussed in this lecture series. Because this 5th step uses techniques beyond the scope of this course, we will simply continue working with the filtered dataset and perform hierarchical clustering. However, we included some code that allows you to do something similar to what the authors did for your reference (note that running
    this code requires installing the snifter R package, which requires a working Python and Conda installation).

# code for steps 1-4
library(scater)
library(genefilter)
library(scran)

# Step 1: Downsampling
sce_900 <- sce[,sce$detected > 900]

# Step 2: Feature selection
sce_900 <- logNormCounts(sce_900)
dec_900 <- modelGeneVar(sce_900)
hvg_900 <- getTopHVGs(dec_900,
                      n = 374) # same number of top features as original paper

# Step 3: PCA
set.seed(1234)
sce_900 <- runPCA(sce_900, 
                  ncomponents = 32, # same number of PCs as original paper
                  subset_row = hvg_900,
                  scale=TRUE) # scale the data like in original paper

# Step 4: T-SNE
set.seed(484854)
sce_900 <- runTSNE(sce_900, 
               dimred = 'PCA',
               n_dimred = 32,
               perplexity = 30) # same perplexity as original paper
# Step 5 authors (just for your reference): project new cells on t-SNE embedding
#BiocManager::install("snifter")
library(snifter)
tsne1 <- snifter::fitsne(reducedDim(sce_900, type="PCA"))
embedding <- reducedDim(sce[hvg_900,sce$detected>900], type="PCA")

ggplot() +
  aes(tsne1[, 1], tsne1[, 2], colour = as.factor(sce[,sce$detected>900]$cluster)) +
  geom_point(pch = 19) +
  scale_colour_discrete(name = "Cluster") +
  labs(x = "t-SNE 1", y = "t-SNE 2") +
  theme_bw()

new_coords <- project(tsne1, 
                      new = reducedDim(sce[,sce$detected<=900], type="PCA"), 
                      old = reducedDim(sce[,sce$detected>900], type="PCA"))
ggplot() +
    geom_point(
        aes(tsne1[, 1], tsne1[, 2],
            colour = as.factor(sce[,sce$detected>900]$cluster),
            shape = "embedding"
        )
    ) +
    geom_point(
        aes(new_coords[, 1], new_coords[, 2], 
            colour = as.factor(sce[,sce$detected<=900]$cluster),
            shape = "projection"
        )
    ) +
    scale_colour_discrete(name = "Cell type") +
    scale_shape_discrete(name = NULL) +
    labs(x = "t-SNE 1", y = "t-SNE 2") +
    theme_bw()
# Step 5 for us: perform hierarchical clustering
distsce <- dist(reducedDim(sce_900, "PCA"))
hcl <- hclust(distsce, method = "ward.D2")

clust_hcl_k10 <- cutree(hcl, k = 10)
clust_hcl_k39 <- cutree(hcl, k = 39)

sce_900$clust_hcl_k10 <- as.factor(clust_hcl_k10)
sce_900$clust_hcl_k39 <- as.factor(clust_hcl_k39)

Visualize our labels and compare with original labels

plotTSNE(sce_900,
         colour_by = "clust_hcl_k10",
         text_by = "clust_hcl_k10")

plotTSNE(sce_900,
         colour_by = "cluster_lowRes",
         text_by = "cluster_lowRes")

For the low resolution labels, we observe a strong correspondence between our clustering and that of the original authors. More specifically:

  • The original rods correspond with our group 5
  • The original cones correspond with our group 2
  • The original pericytes and vascular endothelium correspond with our group 7
  • The original muller glia, astrocytes, fibroblasts and microglia (which are similar cell types, see paper figure 5D) correspond with our group 8
  • The original ganglion and horizontal cells (which are similar cell types, see paper figure 5D) correspond with our group 1
  • The original bipolar cells are split out between our groups 4 and 10
  • The original amacrine cells correspond with our groups 3, 6 and 9
plotTSNE(sce_900,
         colour_by = "clust_hcl_k39",
         text_by = "clust_hcl_k39")

plotTSNE(sce_900,
         colour_by = "cluster",
         text_by = "cluster")

I will not make an extensive comparison here. However, note that we see several scenarios:

  1. Clusters that correspond one-on-one (e.g., our cluster 19 with cluster 16 of the authors)

  2. We split up some clusters in multiple sets (e.g., our clusters 16, 18 and

  1. that are just 1 cluster in the analysis of the authors (cluster 34)
  1. We aggregate some clusters into 1 (e.g. cluster 14) that are split up in the original results (clusters 37 and 38)

Overall, we observe a rather strong correspondence between our clusters and those of the authors. Also note that our t-SNE visualization now resembles the t-SNE map of the original authors much more closely:

knitr::include_graphics("./macosko_figure_5B.jpeg")

17 Cell type annotation

17.1 Supervised: using a limited set of known markers

In the publication, the authors aimed to identify the different clusters in the data by using a set of 12 well-known molecular markers; genes for which the expression profile is typically very specific, i.e., highly expressed only in one specific cell type. They used the following markers:

markers <- c("LHX1", "SLC17A6","PAX6","GAD1","SLC6A9","OPN1MW","VSX2",
             "RLBP1", "GFAP", "PECAM1", "KCNJ8","CX3CR1")

We will here visualize the expression of these markers in t-SNE space. First, we create a “baseline” figure, displaying each cell in 2D space, colored in black.

library(ggplot2)
gg_hlp_data <- data.frame(x = reducedDim(sce_900, type = "TSNE")[,1],
                          y = reducedDim(sce_900, type = "TSNE")[,2],
                          cluster = sce_900$cluster)
gg_base <- ggplot(data = gg_hlp_data[!is.na(sce_900$cluster),],
                  aes(x = x, y = y,)) +
    geom_point(size=0.4) +
    theme_bw() +
    xlab("TSNE 1") +
    ylab("TSNE 2")
gg_base

Next, we obtain the counts of the 12 pre-selected marker genes for all cells.

marker_counts <- assays(sce_900)$counts[markers,]
marker_counts <- as.matrix(t(marker_counts))

Finally, we make one figure for each of the 12 marker genes. The idea is to give each cell that has a non-zero expression of the marker (i.e., for which the marker is expressed) a red coloring.

marker_counts_binary <- marker_counts
marker_counts_binary[which(marker_counts_binary > 0)] <- 1

for (i in seq_along(markers)) {
    gg <- ggplot(data = gg_hlp_data[!is.na(sce_900$cluster),],
                    aes(x = x, y = y)) +
        geom_point(aes(color = as.factor(marker_counts_binary[,i])[!is.na(sce_900$cluster)]), size = 0.4) +
        scale_color_manual(values=c("black","red")) +
        xlab("TSNE 1") +
        ylab("TSNE 2") +
        ggtitle(colnames(marker_counts_binary)[i]) +
        theme_bw() + 
      theme(legend.title = element_blank())
    print(gg)
}
# Visualize our clusters
plotTSNE(sce_900,
         colour_by = "clust_hcl_k39",
         text_by = "clust_hcl_k39")

From figure 5D, we obtain the marker-cell type relationship,

knitr::include_graphics("./macosko_figure_5D.jpeg")

and draw the following conclusions:

  • The LHX1 marker is specific for horizontal cells. As such, our cluster 3 corresponds to horizontal cells.

  • The SLC17A6 marker is specific for ganglion cells. As such, our cluster 1 corresponds to ganglion cells.

  • The PAX6 marker is not specific. It is expressed in horizontal cells, ganglion cells, amacrine cells (all subtypes), muller glia cells and fibroblasts. First, this reconfirms our previous statements about clusters 3 and 1, which indeed should also express PAX6. Second, many clusters are potential candidates for being any of the remaining cell types to express PAX6. The next marker should point us to their more exact identity.

  • The GAD1 marker is specific for amacrine cells. In addition, it is only expressed in “GABAergic” amacrine cells, which affect the neurotransmitter GABA. As such, clusters 4, 5, 6, 8, 9, 11, 13, 29, 30 and 31 are GABAergic amacrine cells. Conversely, the clusters that express PAX6 but not GAD1 (clusters 16, 18, 19, 22, 23, 24, 26, 32, 36) are either non-GABAergic amacrine cells, muller glia cells or fibroblasts.

  • The SLC6A9 marker is specific for glycinergic amacrine cells, i.e., affecting the neurotransmitter glycine. As such, clusters 19, 22, 23, 26 and 36 are the glycinergic amacrine cells. In addition, we have clusters 16, 18, 24 and 32 that express PAX6, but not GAD1 nor SLC6A9. As such, these cells should be either muller glia cells or fibroblasts.

  • The OPN1MW marker is specific for cones. As such, our clusters 2, 34 and 35 corresponds to cones.

  • The VSX2 marker is strongly expressed in bipolar cells. In addition, it is lowly expressed in muller glia cells and fibroblasts. As such, clusters 7, 10, 15, 16, 18, 20, 21, 25, 27, 32, 33, 37 and 38 should belong to one of these cell types.

  • The RLBP1 marker is specific for muller glia cells and astrocytes. Since clusters 16, 18 and 32 express RLBP1 and PAX6, but not GAD1 nor SLC6A9, we now have very strong evidence that these are muller glia cells.

  • The GFAP marker is specific for astrocytes. As such, our clusters 24, which also expresses RLBP1, should correspond to astrocytes.

  • The PECAM1 marker is specific for pericytes As such, our clusters 14, should correspond to pericytes.

  • The PECAM1 marker is specific for vascular endothelium cells. As such, our clusters 14, should correspond to vascular endothelium cells.

  • The KCNJ8 marker is specific for pericytes. As such, our clusters 14, should correspond to pericytes. Note that it thus appears that our clustering procedure was not able to differentiate between vascular endothelium cells and pericytes, which both are likely to be present in cluster 14!

  • The CX3CR1 marker is specific for microglia. However, we do not seem to observe any cells that were identified as microglia by the original publication.

  • Last but not least, the authors have identified the rod cells as cells that do not express any of the aforementioned markers. As such, we are able to identify our clusters 12 and 17 as rod cells.

17.2 Supervised: Using marker genes detected from this data

Sometimes it will be very difficult to set up a panel of known marker genes that would allow us to distinguish between all cell types in our dataset. For instance, sometimes we may not know in advance which cell types to expect, or we may not have good information on relevant markers (if the studied system is not well known).

An alternative strategy is to identify the genes that drive separation between clusters. These marker genes allow us to assign biological meaning to each cluster based on their functional annotation. This strategy is referred to as marker gene detection.

The most straightforward approach to marker gene detection involves testing for differential expression (DE) between clusters. If a gene is strongly DE between clusters, it is likely to have driven the separation of cells in the dimensionality reduction. The general strategy is to compare each pair of clusters and compute scores quantifying the differences in the expression distributions between clusters. The scores for all pairwise comparisons involving a particular cluster are then consolidated into a single data frame for that cluster. This approach is implemented in the scoreMarkers function of the scran package.

library(scran)
marker.info <- scoreMarkers(sce_900, 
                            sce_900$clust_hcl_k39)
marker.info # one dataframe for each of the 39 clusters
colnames(marker.info[["1"]]) # statistics for cluster 1.
head(marker.info[["1"]])

We observe several summary statistics for each gene in the dataframe for cluster 1. We highlight a few:

  • self.average: the average log-normalized expression of the gene in the target cluster (cluster 1)

  • other.average: the average log-normalized expression of the gene in all the other clusters (clusters 2-39)

  • self.detected: the fraction of cells in which the gene was expressed in the target cluster (cluster 1)

  • other.detected: the fraction of cells in which the gene was expressed in all the other clusters (cluster 2-39)

  • mean.AUC: From the OSCA book chapter 6.3: “In the context of marker detection, the area under the curve (AUC) quantifies our ability to distinguish between two distributions in a pairwise comparison. The AUC represents the probability that a randomly chosen observation from our cluster of interest is greater than a randomly chosen observation from the other cluster. A value of 1 corresponds to upregulation, where all values of our cluster of interest are greater than any value from the other cluster; a value of 0.5 means that there is no net difference in the location of the distributions; and a value of 0 corresponds to downregulation. The AUC is closely related to the U statistic in the Wilcoxon ranked sum test (a.k.a., Mann-Whitney U-test).” As such, this a very interesting column to use for selecting marker genes.

Based on the mean.AUC statistic, we may now inspect the top10 markers to distinguish between cells of cluster 1 and cells of the other clusters:

chosen <- marker.info[["1"]]
ordered <- chosen[order(chosen$mean.AUC, decreasing=TRUE),]
head(ordered[,c(1:4,10)],n=10) # showing basic stats only, for brevity.

We can also visualize the log-normalized expression of the top10 markers in each cell, stratified on cluster label, using the plotExpression function of the scater package:

library(scater)
plotExpression(sce_900, 
               features=head(rownames(ordered),n=10), 
               x="clust_hcl_k39", 
               colour_by="clust_hcl_k39")

Two genes pop up as extremely interesting for using as markers specific for cluster 1: NRN1 and SLC17A6. These markers are highly expressed in cluster 1, but have almost no expression in any of the other clusters.

Interestingly, SLC17A6 is exactly the marker the original authors have used as a specific marker for ganglion cells! Note that if we would not know the system under study, i.e. we were unable to select the SLC17A6 marker based on our prior biological knowledge, we would have successfully identified the marker using this strategy. In a next step, we could perform an internet search to see if this marker is known or not.

For the record, NRN1 also is a known marker for identifying ganglion cells.

17.3 Semi-supervised using SingleR

A conceptually straightforward annotation approach is to compare our current scRNA-seq dataset with a previously annotated reference dataset. Labels can then be assigned to each cell in the Macosko dataset based on the most similar reference cells, for some definition of “similar”. This is a standard classification challenge that can be tackled by standard machine learning techniques such as random forests and support vector machines. Any published and labeled RNA-seq dataset (bulk or single-cell) can be used as a reference, though its reliability depends greatly on the expertise of the original authors who assigned the labels in the first place and the closer the reference dataset is to the dataset we would like to annotate (e.g., full-length vs UMI-based protocol), the more accurate the annotation will typically be.

In this section, we will perform such label transfer between the annotated reference dataset from Shekhar et al., which also is a scRNA-seq dataset that studied the mouse retina, and the Macosko dataset.

To perform the actual label transfer, will use the SingleR Bioconductor package. For each “test” cell in the Macosko dataset, SingleR will:

  1. Compute Spearman correlation between the test cell and each reference cell. To improve signal/noise, only marker genes identified using the reference dataset are used for this.

  2. For each label (cell type), set the score as the (default of) 80% quantile of Spearman correlations.

  3. The prediction is then the label with the highest score.

Before we can use singleR to perform label transfer, we will need to import, explore (brief) and wrangle the reference dataset by Shekhar et al..

17.3.1 Import reference data

The dataset by Shekhar et al. can be conveniently imported using the scRNAseq package.

library(scRNAseq)
(ref.data <- scRNAseq::ShekharRetinaData(ensembl = TRUE))

17.3.2 Explore metadata of reference data

head(colData(ref.data))
levels(as.factor(ref.data$CLUSTER))
levels(as.factor(ref.data$`SUB-CLUSTER`))
# both have exactly the same info...
all(ref.data$`SUB-CLUSTER` == ref.data$CLUSTER, na.rm = TRUE)

17.3.3 Process reference data

  1. Remove unlabeled cells
sum(is.na(ref.data$CLUSTER))
ref.data <- ref.data[,-which(is.na(ref.data$CLUSTER))]
  1. Remove doublets/contaminant cells

The original authors already performed quality control; we have cells with cluster label “Doublets/Contaminants”. Let’s remove those:

sum(ref.data$CLUSTER == "Doublets/Contaminants")
ref.data <- ref.data[,-which(ref.data$CLUSTER == "Doublets/Contaminants")]
  1. Make lower resolution cell type levels (for easier interpretation)
ref.data$CLUSTER_lowRes <- fct_recode(ref.data$CLUSTER, 
           "Amacrine_cells" = "AC (Amacrine cell)",
           "Bipolar_cells" = "BC1A",
           "Bipolar_cells" = "BC1B",
           "Bipolar_cells" = "BC2",
           "Bipolar_cells" = "BC3A",
           "Bipolar_cells" = "BC3B",
           "Bipolar_cells" = "BC4",
           "Bipolar_cells" = "BC5A (Cone Bipolar cell 5A)",
           "Bipolar_cells" = "BC5B",
           "Bipolar_cells" = "BC5C",
           "Bipolar_cells" = "BC5D",
           "Bipolar_cells" = "BC6",
           "Bipolar_cells" = "BC7 (Cone Bipolar cell 7)",
           "Bipolar_cells" = "BC8/9 (mixture of BC8 and BC9)",
           "Cones" = "Cone Photoreceptors",
           "Muller_glia" = "MG (Mueller Glia)",
           "Rod_Bipolar_cell" = "RBC (Rod Bipolar cell)",
           "Rod Photoreceptors" = "Rod Photoreceptors")
  1. Remove lowly expressed genes
keep <- rowSums(assays(ref.data)$counts > 0) > 10
table(keep)
ref.data <- ref.data[keep,]
  1. Obtain same gene ID format as in target data

To avoid problems with different version of gene symbols, it is good practice do work with unambiguous gene identifiers like those of ENSEMBL instead.

rownames(sce_900) <- rowData(sce_900)$ensembl_gene_id # use ENSEMBL identifiers instead
sum(rownames(sce_900) %in% rownames(ref.data))
  1. Compute logNormCounts
library(scuttle)
ref.data <- logNormCounts(ref.data)

17.3.4 SingleR at low reference resolution

#BiocManager::install("SingleR")
library(SingleR)

# runs 2min 30sec for me
pred.lowRes <- SingleR(test = sce_900, 
                     ref = ref.data, 
                     labels = ref.data$CLUSTER_lowRes, 
                     de.method = "wilcox")
table(pred.lowRes$labels)

Using the SingleR classifier that was trained on the reference dataset by Shekhar et al., we have labeled 2731 cells from the Macosko dataset as amacrine cells, 1528 cells as bipolar cells, and so on. Again note that we predicted a label for each cell in the Macosko dataset based its similarity (in gene expression) with labeled cells from the Shekhar dataset.

Most importantly, we want to compare our predicted cell labels with the labels that were obtained by Macokso et al., which we could consider to be ground truth labels if we assume that the authors succeeded in their large effort of annotating their cell clusters for their publication.

tab <- table(sce_900$cluster_lowRes, pred.lowRes$labels)
tab

By the naked eye, we immediately observe a strong correspondence between our predicted labels and the labels from the original publication (i.e., most amacrine cells being predicted as amacrine cells, most bipolar cells being predicted as bipolar cells, and so on). Before we dive deeper into this, we may visualize our result using a heatmap:

pheatmap::pheatmap(tab / rowSums(tab))

where we also can observe the correspondence between the predicted (x-axis) and the original (y-axis) labels. Note that we have normalized the values in the heatmap for each row, so that the coloring of the cells in the matrix can be interpreted as the fraction of the original cell types that got assigned to each of the reference categories (e.g. for row 1, 80-90% of the cells got predicted as amacrine cells and the remaining 10% got predicted as rod photoreceptors).

One other visualization strategy is implemented in the plotScoreHeatmap of the SingleR package. Remember, the SingleR classifier assigns each target cell to a cell type label with a certain probability. The plotScoreHeatmap function then allows you to plot, for each cell (columns) the assignment score (which can be thought of as a probability) of that cell belonging to each of the reference label categories (rows).

gg <- plotScoreHeatmap(pred.lowRes[1:20,])
gg

For instance, when we look at the 20th cell (rightmost column), we see that this cell was labeled as a rod photoreceptor. This is because this was the label that was assigned to the cell with highest assignment score. We also see that the second most likely label for this cell is cones.

Finally, we can use these assignment score to filter out cells that could not be unambiguously assigned to one cell type, i.e., only report those cells that we were able to reliably assign:

summary(is.na(pred.lowRes$pruned.labels))

range(rowMaxs(pred.lowRes$scores)) # all assignments
range(rowMaxs(pred.lowRes$scores[!is.na(pred.lowRes$pruned.labels),])) # reliable assignments

table(sce_900$cluster_lowRes[!is.na(pred.lowRes$pruned.labels)], 
      pred.lowRes$labels[!is.na(pred.lowRes$pruned.labels)])

We will interpret the concordance between the predicted and the original labels on this subset. We observe a strong correspondence between the predicted labels and the labels from Macokso et al. We can read this table as follows:

  • Almost all amacrine cells of the Macosko dataset are correctly predicted as amacrine cells (2036/(2036+7+1+41+1) ±= 98% correctly assigned).

  • All horizontal cells and all ganglion cells of the Macosko dataset are predicted as amacrine cells. This makes perfect sense. Since the reference dataset did not contain horizontal/ganglion cell labels, it is impossible to label the target cells with these labels. In stead, the cells were labeled as the cell type that is most closely related to horizontal/ ganglion cells; the amacrine cell type (see figure 5D).

  • Most of the bipolar cells of the Macosko dataset were either predicted as bipolar cells or as rod bipolar cells. The authors of the Macosko paper were not able (in their low-resolution labeling) to distinguish between these two subtypes of bipolar cells, but we succeeded in doing this (although we should in principle need to double-check with markers whether this is a true biological signal and not some spurious effect).

  • 44% (279/(279+354)) of the cones in the Macosko dataset are correctly labeled as cones. The remaining 56% of the cells were labeled as rods. This means that either the SingleR classifier wrongfully labeled these cones as rods, or the original authors mislabeled these rods as cones. Since we do not really have a ground truth here, the best idea is to check this using known markers to distinguish between rods and cones.

  • Almost all Muller glia cells of the Macosko dataset are correctly predicted as Muller glia cells (569/(569+2) ±= 100% correctly assigned). In addition, most of the astrocytes, fibroblast, vascular endothelium, pericytes and microglia cells also got labeled as Muller glia cells. This makes perfect sense. Since the reference dataset did not contain these cell labels, it was impossible to label the target cells with these labels. In stead, the cells were labeled as the cell type that is most closely related; the Muller glia cell type (see figure 5D).

  • Almost all rod cells of the Macosko dataset are correctly predicted as rod cells (1834/(1834+6+4+1) ±= 99% correctly assigned).

We can now also visualize the predicted labels and the original labels on our t-SNE.

sce_900$SingleR_lowRes <- pred.lowRes$labels

# our low resolution labels based on reference
plotTSNE(sce_900,
         colour_by = "SingleR_lowRes",
         text_by = "SingleR_lowRes",
         text_size = 3)

# low resolution labels of the authors
plotTSNE(sce_900,
         colour_by = "cluster_lowRes",
         text_by = "cluster_lowRes",
         text_size = 3)

Comparing these two figures, we come to the same conclusion as above based on the table comparison.

We see how some cells our classifier labeld more clusters as rods. Based on the marker results from the first section of cell annotation, we known that our classifier as misclassified these (i.e., the original labels are correct).

On the other hand, we were here nicely able to distinguish between bipolar cells and rod bipolar cells, which cluster separately.

Altogether, label-transfer was quite successful, and we would have been able to very quickly get high-quality results using this very user-friendly and fast approach implemented in the SingleR package.

17.3.5 Addendum: SingleR at high reference resolution

For the sake of completeness, we may also perform label transfer with SingleR using the more fine-grained labels from the reference dataset.

# Runs 3min 30sec for me
pred.higRes <- SingleR(test = sce_900, 
                     ref = ref.data, 
                     labels = ref.data$CLUSTER, 
                     de.method = "wilcox")
table(pred.higRes$labels)
table(sce_900$cluster_lowRes, pred.higRes$labels)
pheatmap::pheatmap(table(sce_900$cluster_lowRes, pred.higRes$labels))
gg <- plotScoreHeatmap(pred.higRes[1:20,])
# low certainty labels: 751
summary(is.na(pred.higRes$pruned.labels))
table(sce_900$cluster_lowRes[!is.na(pred.higRes$pruned.labels)], 
      pred.higRes$labels[!is.na(pred.higRes$pruned.labels)])
sce_900$SingleR_highRes <- pred.higRes$labels

# our high resolution labels based on reference
plotTSNE(sce_900,
         colour_by = "SingleR_highRes",
         text_by = "SingleR_highRes",
         text_size = 2.5)


# high resolution labels of the authors
plotTSNE(sce_900,
         colour_by = "cluster_highRes",
         text_by = "cluster_highRes",
         text_size = 2.5)
LS0tCnRpdGxlOiAnTGFiMzogQ2x1c3RlcmluZywgY2VsbCB0eXBlIGFubm90YXRpb24sIGRpZmZlcmVudGlhbCBleHByZXNzaW9uJwphdXRob3I6ICJLb2VuIFZhbiBkZW4gQmVyZ2UgYW5kIEplcm9lbiBHaWxpcyIKZGF0ZTogIjYvMTIvMjAyMSIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyBQcmVhbWJsZTogaW5zdGFsbGF0aW9uIG9mIEJpb2NvbmR1Y3RvciBsaWJyYXJpZXMKCmBgYHtyfQojIGluc3RhbGwgQmlvY01hbmFnZXIgcGFja2FnZSBpZiBub3QgaW5zdGFsbGVkIHlldC4KIyBCaW9jTWFuYWdlciBpcyB0aGUgcGFja2FnZSBpbnN0YWxsZXIgZm9yIEJpb2NvbmR1Y3RvciBzb2Z0d2FyZS4KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKCiMgaW5zdGFsbCBwYWNrYWdlcyBpZiBub3QgeWV0IGluc3RhbGxlZC4KcGtncyA8LSBjKCJTaW5nbGVDZWxsRXhwZXJpbWVudCIsCiAgICAgICAgICAiRXhwZXJpbWVudEh1YiIsCiAgICAgICAgICAiZWRnZVIiLAogICAgICAgICAgImJpb21hUnQiLAogICAgICAgICAgIkRyb3BsZXRVdGlscyIsIAogICAgICAgICAgInNjUk5Bc2VxIiwgCiAgICAgICAgICAic2NhdGVyIiwgCiAgICAgICAgICAic2N1dHRsZSIsIAogICAgICAgICAgInNjcmFuIiwKICAgICAgICAgICJzY3J5IiwKICAgICAgICAgICJCaW9jU2luZ3VsYXIiLCAKICAgICAgICAgICJzY0RibEZpbmRlciIsCiAgICAgICAgICAiU2V1cmF0IiwKICAgICAgICAgICJQQ0F0b29scyIsCiAgICAgICAgICAiZ2xtcGNhIiwKICAgICAgICAgICJnZW5lZmlsdGVyIiwKICAgICAgICAgICJwaGVhdG1hcCIsCiAgICAgICAgICAidGlkeXZlcnNlIiwKICAgICAgICAgICJtY2x1c3QiLAogICAgICAgICAgImdncGxvdDIiLAogICAgICAgICAgIlNpbmdsZVIiKQpub3RJbnN0YWxsZWQgPC0gcGtnc1shcGtncyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywxXV0KaWYobGVuZ3RoKG5vdEluc3RhbGxlZCkgPiAwKXsKICBCaW9jTWFuYWdlcjo6aW5zdGFsbChub3RJbnN0YWxsZWQpCn0KYGBgCgpTdGVwcyB0YWtlbiBpbiBmaXJzdCB0d28gbGFiIHNlc3Npb25zOgoKMS4gSW1wb3J0IHRoZSBNYWNvc2tvIGRhdGFzZXQgYXMgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgZnJvbSB0aGUgCmBzY1JOQXNlcWAgQmlvY29uZHVjdG9yIHBhY2thZ2UuCgoyLiBJbmNsdWRlIEVOU0VNQkwgZ2VuZSBpZGVudGlmaWVycyBpbiB0aGUgYHJvd0RhdGFgCgozLiBSZW1vdmUgdmVyeSBsb3dseSBleHByZXNzZWQgZ2VuZXMKCjQuIFJlbW92ZSBsb3cgcXVhbGl0eSBjZWxscyAKICA0LjEuIENlbGxzIHdpdGggb3V0bHlpbmcgbGlicmFyeSBzaXplIAogIDQuMi4gQ2VsbHMgd2l0aCBvdXRseWluZyB0cmFuc2NyaXB0b21lIGNvbXBsZXhpdHkgCiAgNC4zLiBDZWxscyB3aXRoIG91dGx5aW5nIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCByZWFkcyAKICA0LjQuIEVtcHR5IGRyb3BsZXRzIAogIDQuNS4gRG91YmxldHMKCjUuIE5vcm1hbGl6YXRpb24gCiAgNS4xLiBDb21wdXRlIGxvZy1ub3JtYWxpemVkIGNvdW50cyAKICA1LjIuIENvbXB1dGUgc2NhbGluZyBmYWN0b3IgdG8gY29ycmVjdCBmb3IgZGlmZmVyZW5jZXMgaW4gbGlicmFyeSBzaXplCgo2LiBGZWF0dXJlIHNlbGVjdGlvbiAKICA2LjEuIEdlbmVzIHdpdGggaGlnaCB2YXJpYW5jZSAKICA2LjIuIEdlbmVzIHdpdGggaGlnaCB2YXJpYW5jZSB3aXRoIHJlc3BlY3QgdG8gdGhlaXIgbWVhbiBleHByZXNzaW9uIAogIDYuMy4gR2VuZXMgd2l0aCBoaWdoIGRldmlhbmNlIAogIDYuNC4gR2VuZXMgd2l0aCBoaWdoIHZhcmlhbmNlIGFmdGVyIHZhcmlhbmNlLXN0YWJpbGl6aW5nIHRyYW5zZm9ybWF0aW9uIChWU1QpCgo3LiBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gCiAgNy4xLiBCYXNlZCBvbiB0d28gbW9zdCB2YXJpYWJsZSBnZW5lcyBmcm9tIHN0ZXAgNi4yLiAKICA3LjIuIFBDQSAKICA3LjMuIEdMTS1QQ0EgCiAgNy40LiBULVNORSAKICA3LjUuIFVNQVAKCiMgVGhlIE1hY29za28gZGF0YXNldAoKSW4gdGhpcyB3b3Jrc2hvcCBzZXNzaW9uLCB3ZSB3aWxsIGFuYWx5emUgdGhlIHNpbmdsZS1jZWxsIFJOQS1zZXEgZGF0YXNldApmcm9tIHRoZSBwdWJsaWNhdGlvbiBieSBNYWNvc2tvICpldCBhbC4qLCBDZWxsIDE2MSwgMTIwMuKAkzEyMTQgZnJvbSAyMDE1ClsobGluayldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouY2VsbC4yMDE1LjA1LjAwMikuIFRoaXMgaXMgdGhlIG1hbnVzY3JpcHQgaW4Kd2hpY2ggdGhlIGRyb3BsZXQgc2NSTkEtc2VxIHRlY2hub2xvZ3kgKipEcm9wLXNlcSoqIHdhcyBpbnRyb2R1Y2VkLgpTaXggeWVhcnMgYWZ0ZXIgdGhlIG9yaWdpbmFsIHB1YmxpY2F0aW9uLCBkcm9wLXNlcSBpcyBzdGlsbCBvbmUgb2YgdGhlIG1vc3QgCmNvbW1vbmx5IGFkb3B0ZWQgc2NSTkEtc2VxIHByb3RvY29scywgYXMgZXZpZGVuY2VkIGJ5IHRoZQpsYXJnZSBudW1iZXIgb2YgY2l0YXRpb25zIGZvciBNYWNvc2tvICpldCBhbC4qIAooNC4zMDMgY2l0YXRpb25zIGF0IE5vdmVtYmVyIDMsIDIwMjEpLgoKVGhlIGJhc2ljIGlkZWEgYmVoaW5kIHRoZSBEcm9wLXNlcSBwcm90b2NvbCBjYW4gYmUgdGFrZW4gZnJvbSB0aGUgZ3JhcGhpY2FsCmFic3RyYWN0IG9mIHRoZSBwdWJsaWNhdGlvbi4KCmBgYAprbml0cjo6aW5jbHVkZV9ncmFwaGljcygibWFjb3Nrb19ncmFwaGljYWxBYnN0cmFjdC5qcGVnIikKYGBgCgpUaGUgc3VjY2VzcyBvZiBEcm9wLXNlcSBjYW4gYmUgZXhwbGFpbmVkIGJ5IHRoZSBmb2xsb3dpbmcgYWR2YW50YWdlb3VzIApmZWF0dXJlczoKCi0gVGhlIHVzZSBvZiB1bmlxdWUgbW9sZWN1bGFyIGlkZW50aWZpZXJzIChVTUlzKS4gQnkgd29ya2luZyB3aXRoIFVNSXMsIG9uZSBjb3VudApjb3JyZXNwb25kcyB0byBvbmUgb2JzZXJ2ZWQgbVJOQSBtb2xlY3VsZSBwcmVzZW50IGluIHRoZSBjZWxsLiBUaGFua3MgdG8gdGhlIHVzZQpvZiBVTUkgYmFyY29kZXMsIFBDUiBhcnRpZmFjdHMgYXJlIHJlZHVjZWQuCgotIFNjYWxhYmlsaXR5OiBtaWNyb2ZsdWlkaWNzIHRlY2hub2xvZ3kgYWxsb3dzIGZvciBwZXJmb3JtaW5nIHRoZSBsaWJyYXJ5IHByZXAKcmVhY3Rpb25zIGluc2lkZSBuYW5vZHJvcGxldHMsIGluIHdoaWNoIHNpbmdsZSBjZWxscyBtYXkgYmUgY29udGFpbmVkLiBMaWJyYXJ5IHByZXAgb2NjdXJzIGFjcm9zcyBhbGwgZHJvcGxldHMgc2ltdWx0YW5lb3VzbHkuCgotIENvc3Q6IHRoZSBleHBlcmltZW50IGNvc3RzIGFyb3VuZCA2LjUgY2VudHMgKFVTRCkgcGVyIGNlbGwuCgotIFNwZWVkOiBUaGUgdmVyeSBsYXJnZSBkYXRhc2V0IHRoYXQgd2Ugd2lsbCBiZSB3b3JraW5nIHdpdGggdG9kYXkgd2FzIApnZW5lcmF0ZWQgaW4gYW4gZXhwZXJpbWVudCB0aGF0IHRvb2sgb25seSA0IGRheXMuCgpJbiB0aGlzIHBhcnRpY3VsYXIgZXhwZXJpbWVudCwgTWFjb3NrbyAqZXQgYWwuKiBzZXF1ZW5jZWQgNDksMzAwIGNlbGxzIGZyb20gdGhlCm1vdXNlIHJldGluYSwgaWRlbnRpZnlpbmcgMzkgdHJhbnNjcmlwdGlvbmFsbHkgZGlzdGluY3QgY2VsbCBwb3B1bGF0aW9ucy4gVGhlCmV4cGVyaW1lbnQgd2FzIHBlcmZvcm1lZCBpbiA3IGJhdGNoZXMuCgojIERhdGEgYXZhaWxhYmlsaXR5CgojIyBTUkEKClRoZSBbU2VxdWVuY2UgUmVhZCBBcmNoaXZlIChTUkEpXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3NyYSkgaXMgdGhlIApsYXJnZXN0IHB1YmxpY2x5IGF2YWlsYWJsZSByZXBvc2l0b3J5IG9mIGhpZ2gtdGhyb3VnaHB1dCBzZXF1ZW5jaW5nIGRhdGEuClRoZSBkYXRhIGFyZSBzdG9yZWQgYnkgdGhlIE5hdGlvbmFsIENlbnRlciBmb3IgQmlvdGVjaG5vbG9neSBJbmZvcm1hdGlvbiAoTkNCSSkgCnNlcnZpY2VzIGFuZCBtdWx0aXBsZSBjbG91ZCBzdG9yYWdlIHByb3ZpZGVycy4gRnJvbSB0aGlzIHdlYnNpdGUgInJhdyIgCnNlcXVlbmNpbmcgZGF0YSBjYW4gYmUgcmV0cmlldmVkLiBJbiBwcmFjdGljZSwgdGhlc2UgYXJlIHVzdWFsbHkgYC5zcmFgIGZpbGVzLAp3aGljaCBjYW4gYmUgZG93bmxvYWRlZCBhbmQgY29udmVydGVkIGludG8gRkFTVFEgZmlsZXMgdXNpbmcgZnVuY3Rpb25zCmZyb20gdGhlIGBzcmF0b29sa2l0YCBzb2Z0d2FyZS4gCgpGb3Igb3VyIGRhdGFzZXQsIHRoZSBGQVNUUSBkYXRhIGNhbiBiZSByZXRyaWV2ZWQgZnJvbSB0aGlzIApbbGlua10oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9UcmFjZXMvc3R1ZHkvP2FjYz1QUkpOQTI2Nzg1NyZvPWFjY19zJTNBYSkuClRoZSBkYXRhIGFyZSBzdG9yZWQgYXMgb25lIGZpbGUgcGVyIHNlcXVlbmNpbmcgYmF0Y2gsIHdpdGggZWFjaCBmaWxlIAphcHByb3hpbWF0ZWx5IDIwR2IuIEFzIHN1Y2gsIGl0IHdpbGwgYmUgdW5mZWFzaWJsZSB0byBkb3dubG9hZCBhbmQgcHJvY2VzcyB0aGVzZQpGQVNUUSBmaWxlcyBpbiB0aGlzIHByYWN0aWNhbCBzZXNzaW9uLgoKSW5zdGVhZCwgZm9yIGRlbW9uc3RyYXRpdmUgcHVycG9zZXMsIHdlIGhhdmUgdGFrZW4gYSBzdWJzYW1wbGUgb2YgdGhlIEZBU1RRCmZpbGUgZm9yIHRoZSBmaXJzdCBzZXF1ZW5jaW5nIGJhdGNoIGZvciB5b3UgdG8gd29yayB3aXRoLiBPbiB0aGlzIHN1YnNhbXBsZSwgCndlIG1heSBwZXJmb3JtIGFsbCB0aGUgdGFza3MgdGhhdCB3ZSB3b3VsZCBoYXZlIHBlcmZvcm1lZCBvbiB0aGUgZnVsbCBkYXRhc2V0LgpUaGUgc3RlcHMgdGhhdCBhcmUgcmVxdWlyZWQgZm9yIGRvd25sb2FkaW5nIGFuZCBxdWFudGlmeWluZyBkcm9wLXNlcSBkYXRhCmNhbiBiZSBmb3VuZCBpbiBbYSBTaGVsbCBzY3JpcHQgb24gb3VyIGNvbXBhbmlvbiBHaXRIdWIgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL3N0YXRPbWljcy9zaW5nbGVDZWxsQ291cnNlL2Jsb2IvbWFzdGVyL2xhYjFfcHJlcHJvY2Vzc2luZy9wcmVwcm9jZXNzRHJvcHNlcS5zaCkuCgojIyBHRU8KClRoZSBkYXRhc2V0IGZyb20gTWFjb3NrbyAqZXQgYWwuKiB3YXMgYWxzbyB1cGxvYWRlZCBieSB0aGUgYXV0aG9ycyBvbiB0aGUgCkdlbmUgRXhwcmVzc2lvbiBPbW5pYnVzIChHRU8pIHBsYXRmb3JtIHVuZGVyIGFjY2Vzc2lvbiBudW1iZXIgCltHU0U2MzQ3Ml0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1NFNjM0NzIpLCAKZnJvbSB3aGljaCByYXcgYW5kIHJlYWRpbHktcHJvY2Vzc2VkIGRhdGEgZmlsZXMgbWF5IGJlIHJldHJpZXZlZCwgaW5jbHVkaW5nOgoKMS4gKkdTRTYzNDcyX1JBVy50YXIqLCBhIDkwLjZHYiBvYmplY3QgdGhhdCBjb250YWlucyB0aGUgInJhdyBkYXRhIiBmb3IgdGhlCmV4cGVyaW1lbnQuIEluIHRoZSBzY1JOQS1zZXEgY29udGV4dCwgRkFTVFEgZmlsZXMgYXJlIG9mdGVuIGNvbnNpZGVyZWQgdGhlIHJhdwpkYXRhIGZvcm1hdC4KCjIuICpHU0U2MzQ3Ml9QMTRSZXRpbmFfbWVyZ2VkX2RpZ2l0YWxfZXhwcmVzc2lvbi50eHQuZ3oqLCBhIDUwLjdNYiBtYXRyaXggdGhhdApzdG9yZXMgdGhlIGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGVhY2ggY2VsbC4gVGhlc2UgdmFsdWVzIGFyZSBpbnRlZ2VyIApjb3VudHMsIHRoYXQgZGlkIG5vdCB1bmRlcmdvIGFueSB0eXBlIG9mIHByZXByb2Nlc3Npbmcgb3Igbm9ybWFsaXphdGlvbi4KCjMuICpHU0U2MzQ3Ml9tbTEwX3JlZmVyZW5jZV9tZXRhZGF0YS50YXIuZ3oqLCBhIDg2Mi45TWIgY29tcHJlc3NlZCBmb2xkZXIKY29udGFpbmluZyBpbmZvcm1hdGlvbiBvbiB0aGUgcmVmZXJlbmNlIGdlbm9tZSB0byB3aGljaCB0aGUgc2NSTkEtc2VxIHJlYWRzCndlcmUgYWxpZ25lZCAoc2VlIHRoZW9yeSBzbGlkZXMpLgoKNC4gKkdTRTYzNDcyX1AxNFJldGluYV9sb2dER0UudHh0Lmd6KiwgYSAzMTYuOE1iIGNvbXByZXNzZWQgdGV4dCBmaWxlLCBub3QgY2xlYXIKd2hhdCBpdCBjb250YWlucyAocmVzdWx0cyBmcm9tIGEgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcywgYnV0CndpdGggbG9nLXRyYW5zZm9ybWF0aW9uLCBzbyBsb2ctZm9sZCBjaGFuZ2VzIG1heWJlPykuCgpBcyBzdWNoLCBieSBkb3dubG9hZGluZyAqR1NFNjM0NzJfUDE0UmV0aW5hX21lcmdlZF9kaWdpdGFsX2V4cHJlc3Npb24udHh0Lmd6KiwKd2UgYXZvaWQgcmUtcXVhbnRpZnlpbmcgdGhlIGRhdGEsIGkuZS4sIHRoZSB0cmFuc2xhdGlvbiBmcm9tIHJlYWRzIGZyb20gdGhlCkZBU1RRIGZpbGVzIGludG8gYSBnZW5lLWxldmVsIGV4cHJlc3Npb24gdmFsdWVzIGZvciBlYWNoIGNlbGwuIAoKT25lIGlzc3VlIHRoYXQgb2Z0ZW4gYXJpc2VzIGZyb20gZGF0YSBkb3dubG9hZGVkIGZyb20gR0VPLCBpcyB0aGF0IHRoZXJlIGlzIG5vCnN0cmljdCByZXF1aXJlbWVudHMgZm9yIHdoaWNoIGRhdGEgc2hvdWxkIGJlIGluY2x1ZGVkIGluIHRoZSB1cGxvYWQgYnkgdGhlIAphdXRob3JzLiBBcyBzdWNoLCBmcm9tIG15IHBlcnNvbmFsIGV4cGVyaWVuY2UsIGl0IGNhbiBvZnRlbiBiZSB0aGUgY2FzZSB0aGF0CmltcG9ydGFudCBpbmZvcm1hdGlvbiBsaWtlIG1ldGFkYXRhIGFyZSBtaXNzaW5nLCBvciB0aGUgY29udGVudCBvZiB0aGUgc3VibWl0dGVkCmZpbGVzIGlzIHVuY2xlYXIuIEV2ZW4gaWYgYWxsIHRoZSByZXF1aXJlZCBkYXRhIGFyZSBhdmFpbGFibGUsIGFzIGlzIHRoZSBjYXNlIApoZXJlLCB3ZSB3b3VsZCBzdGlsbCBuZWVkIHRvIHBpZWNlIGFsbCB0aGUgaW5mb3JtYXRpb24gdG9nZXRoZXIgZnJvbSBkaWZmZXJlbnQgCmZpbGVzIGFuZCBmaWxlIGZvcm1hdHMgYmVmb3JlIHdlIGNhbiB1c2UgdGhlbS4KCiMjIEV4cGVyaW1lbnRIdWIKClRoZSBCaW9jb25kdWN0b3IgKkV4cGVyaW1lbnRIdWIqIHdlYiByZXNvdXJjZSwgd2hpY2ggY2FuIGJlIGFjY2Vzc2VkIHVzaW5nIHRoZSAKW0V4cGVyaW1lbnRIdWJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9FeHBlcmltZW50SHViLmh0bWwpIApSIHBhY2thZ2UsIHByb3ZpZGVzIGEgY2VudHJhbCBsb2NhdGlvbiB3aGVyZSBjdXJhdGVkIGRhdGEgZnJvbSBleHBlcmltZW50cywgCnB1YmxpY2F0aW9ucyBvciB0cmFpbmluZyBjb3Vyc2VzIGNhbiBiZSBhY2Nlc3NlZC4gV2hpbGUgaXQgY29udGFpbnMgZmFyIGxlc3MKZGF0YXNldHMgdGhhbiB0aGUgU1JBIG9yIEdFTyAoNDk2NSByZWNvcmRzIHRvIGRhdGUpLCB0aGVzZSBkYXRhc2V0cyBhbGwgZm9sbG93IAp0aGUgdGlkeSBkYXRhIGZvcm1hdCBvZiBCaW9jb25kdWN0b3IuIE5vdGUgdGhhdCB0aGUgRXhwZXJpbWVudEh1YiBjb250YWlucyAKc2V2ZXJhbCB0eXBlcyBvZiBkYXRhLCBsaWtlIGJ1bGsgYW5kIHNpbmdsZS1jZWxsIHRyYW5zY3JpcHRvbWljcyBkYXRhLCAKbWljcm9hcnJheXMgYW5kIG1vcmUuCgpUaGUgTWFjb3NrbyBkYXRhc2V0IGlzIGF2YWlsYWJsZSBmcm9tIEV4cGVyaW1lbnRIdWIgYW5kIGNhbiBiZSBhY2Nlc3NlZCBhcwpmb2xsb3dzOgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoRXhwZXJpbWVudEh1YikKZWRiIDwtIEV4cGVyaW1lbnRIdWIoKQoKZWRiW2dyZXAoIk1hY29za28iLCBlZGIkdGl0bGUpXSAjIGZpbmQgYWNjZXNzaW9uIG51bWJlciAoY2FuIGJlIGluZWZmaWNpZW50KQoKZWRiX2NvdW50cyA8LSBlZGJbWyJFSDI2OTAiXV0KZWRiX2NvdW50c1sxOjUsMTo1XQoKZWRiX2NvbGRhdGEgPC0gZWRiW1siRUgyNjkxIl1dCmVkYl9jb2xkYXRhWzE6NSxdCgpybShlZGIsIGVkYl9jb3VudHMsIGVkYl9jb2xkYXRhKQpgYGAKCiMjIHNjUk5BU2VxCgpJbiBhZGRpdGlvbiB0byBFeHBlcmltZW50SHViLCBCaW9jb25kdWN0b3IgcHJvdmlkZXMgdGhlIHBhY2thZ2UKW3NjUk5Bc2VxXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9kYXRhL2V4cGVyaW1lbnQvaHRtbC9zY1JOQXNlcS5odG1sKS4KVGhpcyBwYWNrYWdlIHByb3ZpZGVzIGFuIGV2ZW4gbW9yZSB1c2VyLWZyaWVuZGx5IGNsaWVudCB0byBhY2Nlc3MgKG9ubHkpIApzY1JOQS1zZXEgZGF0YXNldHMgZnJvbSB0aGUgRXhwZXJpbWVudEh1YiB3ZWIgcmVzb3VyY2UuIERhdGEgcmV0cmlldmVkIHVzaW5nIHRoZQpzY1JOQXNlcSBwYWNrYWdlIGFyZSBzdG9yZWQgYXMgdXNlci1mcmllbmRseSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdHMsIAp3aXRoIHRoZSBleHByZXNzaW9uIGRhdGEsIGdlbmUtbGV2ZWwgaW5mb3JtYXRpb24sIGNlbGwtbGV2ZWwgaW5mb3JtYXRpb24gYW5kIApleHBlcmltZW50IG1ldGFkYXRhIGFsbCBpbiBwbGFjZSBpbiBvbmUgZGF0YSBvYmplY3QuIFRoZSBzY1JOQS1zZXEgcGFja2FnZQpjdXJyZW50bHkgaG9sZHMgNjEgZGF0YXNldHMsIGluY2x1ZGluZyB0aGUgZGF0YSBmcm9tICpNYWNvc2tvIGV0IGFsLio6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShzY1JOQXNlcSkKc2NSTkFzZXE6Ok1hY29za29SZXRpbmFEYXRhKCkKYGBgCgojIEltcG9ydCBkYXRhCgpUaGUgYHNjUk5Bc2VxYCBwYWNrYWdlIHByb3ZpZGVzIGNvbnZlbmllbnQgYWNjZXNzIHRvIHNldmVyYWwgZGF0YXNldHMuIFNlZSB0aGUgW3BhY2thZ2UgQmlvY29uZHVjdG9yIHBhZ2VdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvZGF0YS9leHBlcmltZW50L2h0bWwvc2NSTkFzZXEuaHRtbCkgCmZvciBtb3JlIGluZm9ybWF0aW9uLgoKYGBge3J9CiMgQ29kZSBiZWxvdyBtaWdodCBhc2sgeW91IHRvIGNyZWF0ZSBhbiBFeHBlcmltZW50SHViIGRpcmVjdG9yeS4gCiMgVHlwZSAneWVzJyBhbmQgaGl0IEVudGVyLCB0byBhbGxvdyB0aGlzLgpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShzY1JOQXNlcSkpCnNjZSA8LSBNYWNvc2tvUmV0aW5hRGF0YSgpCmBgYAoKIyBBIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0CgpgYGB7cn0Kc2NlCmBgYAoKIyMgQWNjZXNzaW5nIGRhdGEgZnJvbSBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0CgpQbGVhc2Ugc2VlIFtGaWd1cmUgNC4xIGluIE9TQ0FdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzL3JlbGVhc2UvT1NDQS9kYXRhLWluZnJhc3RydWN0dXJlLmh0bWwpIApmb3IgYW4gb3ZlcnZpZXcgb2YgYSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdC4KCmBgYHtyfQojIERhdGE6IGFzc2F5cwphc3NheXMoc2NlKQphc3NheXMoc2NlKSRjb3VudHNbMTo1LCAxOjVdCgojIEZlYXR1cmUgbWV0YWRhdGE6IHJvd0RhdGEKcm93RGF0YShzY2UpICMgZW1wdHkgZm9yIG5vdwoKIyBDZWxsIG1ldGFkYXRhOiBjb2xEYXRhCmNvbERhdGEoc2NlKQoKIyBSZWR1Y2VkIGRpbWVuc2lvbnM6IHJlZHVjZWREaW1zCnJlZHVjZWREaW1zKHNjZSkgIyBlbXB0eSBmb3Igbm93CmBgYAoKIyMgQ3JlYXRpbmcgYSBuZXcgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QKCmBgYHtyfQpzY2VOZXcgPC0gU2luZ2xlQ2VsbEV4cGVyaW1lbnQoYXNzYXlzID0gbGlzdChjb3VudHMgPSBhc3NheXMoc2NlKSRjb3VudHMpKQpzY2VOZXcKCnJtKHNjZU5ldykKYGBgCgojIyBTdG9yaW5nIChtZXRhKWRhdGEgaW4gYSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdAoKYGBge3J9CmZha2VHZW5lTmFtZXMgPC0gcGFzdGUwKCJnZW5lIiwgMTpucm93KHNjZSkpCnJvd0RhdGEoc2NlKSRmYWtlTmFtZSA8LSBmYWtlR2VuZU5hbWVzCmhlYWQocm93RGF0YShzY2UpKQojIFJlbW92ZSBhZ2FpbiBieSBzZXR0aW5nIHRvIE5VTEwKcm93RGF0YShzY2UpJGZha2VOYW1lIDwtIE5VTEwKCmFzc2F5cyhzY2UpJGxvZ0NvdW50cyA8LSBsb2cxcChhc3NheXMoc2NlKSRjb3VudHMpCmFzc2F5cyhzY2UpCmFzc2F5cyhzY2UpJGxvZ0NvdW50c1sxOjUsIDE6NV0KYXNzYXlzKHNjZSkkbG9nQ291bnRzIDwtIE5VTEwKYGBgCgojIE9idGFpbmluZyBhbmQgaW5jbHVkaW5nIHJvd0RhdGEKClRoZSBgcm93RGF0YWAgc2xvdCBvZiBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IGFsbG93cyBmb3Igc3RvcmluZyAKaW5mb3JtYXRpb24gb24gdGhlIGZlYXR1cmVzLCBpLmUuIHRoZSBnZW5lcywgaW4gYSBkYXRhc2V0LiBJbiBvdXIgb2JqZWN0LAp0aGUgYHJvd0RhdGFgIHNsb3QgaXMgZW1wdHkuCgpgYGB7cn0Kcm93RGF0YShzY2UpCmBgYAoKQXMgc3VjaCwgdGhlIG9ubHkgaW5mb3JtYXRpb24gd2UgaGF2ZSBvbiB0aGUgZ2VuZXMgYXJlIHRoZWlyIG5hbWVzLCB3aGljaCBjYW4KYmUgcmV0cmlldmVkIGFzIHRoZSBgcm93bmFtZXNgIG9mIHRoZSBleHByZXNzaW9uIG1hdHJpeC4KCmBgYHtyfQpoZWFkKHJvd25hbWVzKHNjZSkpCmBgYAoKVGhlc2UgYXJlIHRoZSBnZW5lIG5hbWVzIChzeW1ib2xzKS4gTm90ZSB0aGF0IGl0IG1heSBiZSB1c2VmdWwgdG8gaW5jbHVkZSAKYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBpbiB0aGUgYHJvd0RhdGFgIHNsb3QuIEZvciBpbnN0YW5jZSwgd2UgbWF5IHdhbnQgdG8gCnN0b3JlOgoKLSBVbmFtYmlndW91cyBnZW5lIGlkZW50aWZpZXJzIChlLmcuIGZyb20gRU5TRU1CTCkKLSBPbiB3aGljaCBjaHJvbW9zb21lIHRoZSBnZW5lIGlzIGxvY2F0ZWQKLSBHZW5lIGxlbmd0aCAoZ2Vub21pYyBzdGFydCBwb3NpdGlvbiBhbmQgZW5kIHBvc2l0aW9uKQotIE90aGVycy4uLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoImJpb21hUnQiKQoKZW5zZW1ibDc1IDwtIHVzZUVuc2VtYmwoYmlvbWFydCA9ICdnZW5lcycsIAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhc2V0ID0gJ21tdXNjdWx1c19nZW5lX2Vuc2VtYmwnLAogICAgICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uID0gNzUpCgpoZWFkKGxpc3RBdHRyaWJ1dGVzKGVuc2VtYmw3NSkpICMgcG90ZW50aWFsIGluZm8gdG8gZXh0cmFjdAoKZ2VuZUluZm8gPC0gZ2V0Qk0oYXR0cmlidXRlcyA9IGMoImVuc2VtYmxfZ2VuZV9pZCIsICMgRU5TRU1CTCB1bmFtYmlndW91cyBpZGVudGlmaWVyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJtZ2lfc3ltYm9sIiwgIyBHZW5lIHN5bWJvbCAodG8gbGluayB3aXRoIFNDRSByb3duYW1lcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjaHJvbW9zb21lX25hbWUiLCAjIE9uIHdoaWNoIGNocm9tb3NvbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0YXJ0X3Bvc2l0aW9uIiwgIyBTdGFydCBwb3NpdGlvbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZW5kX3Bvc2l0aW9uIiksIyBFbmQgcG9zaXRpb24KICAgICAgICAgICAgICAgICAgbWFydCA9IGVuc2VtYmw3NSkKCmhlYWQoZ2VuZUluZm8pCmBgYAoKYGBge3J9CmdlbmVJbmZvJG1naV9zeW1ib2xfdXBwZXIgPC0gdG91cHBlcihnZW5lSW5mbyRtZ2lfc3ltYm9sKSAKIyBtYXRjaCBiZXR3ZWVuIGdlbmUgaW5mbyBhbmQgcm93bmFtZXMKCnN1bShyb3duYW1lcyhzY2UpICVpbiUgZ2VuZUluZm8kbWdpX3N5bWJvbF91cHBlcikKc3VtKCFyb3duYW1lcyhzY2UpICVpbiUgZ2VuZUluZm8kbWdpX3N5bWJvbF91cHBlcikgIyBsb3N0IGluIGNvbnZlcnNpb24gOigKCnJvd0RhdGEoc2NlKSA8LSBnZW5lSW5mb1ttYXRjaChyb3duYW1lcyhzY2UpLGdlbmVJbmZvJG1naV9zeW1ib2xfdXBwZXIpLF0KYGBgCgpgYGB7cn0KaGVhZChyb3dEYXRhKHNjZSkpCmBgYAoKIyBGaWx0ZXJpbmcgbm9uLWluZm9ybWF0aXZlIGdlbmVzCgpgYGB7cn0KbGlicmFyeShlZGdlUikKCiMgQSB2ZXJ5IHNpbXBsZSBzdHJhdGVneTogcmVtb3ZlIGFsbCBnZW5lcyB0aGF0IGFyZSBleHByZXNzZWQgaW4gbGVzcyB0aGFuIDEwCiMgb3V0IG9mIDQ5MzAwIGNlbGxzIC0+IG5vdGUgdGhhdCB0aGlzIGEgdmVyeSBsZW5pZW50IGZpbHRlcmluZyBjcml0ZXJpdW0Ka2VlcCA8LSByb3dTdW1zKGFzc2F5cyhzY2UpJGNvdW50cyA+IDApID4gMTAKdGFibGUoa2VlcCkKCnNjZSA8LSBzY2Vba2VlcCxdCmBgYAoKTm90ZSB0aGF0IGRlZGljYXRlZCBmdW5jdGlvbnMgZm9yIGZpbHRlcmluZyBvdXQgbG93bHkgZXhwcmVzc2VkIGdlbmVzIGV4aXN0LgpPbmUgc3VjaCBmdW5jdGlvbiBpcyB0aGUgYGZpbHRlckJ5RXhwcmAgZnVuY3Rpb24gb2YgdGhlIGVkZ2VSIFIgcGFja2FnZS4KSW4gYnJpZWYsIHRoZSBzdHJhdGVneSBrZWVwcyBnZW5lcyB0aGF0IGhhdmUgYXQgbGVhc3QgIm1pbi5jb3VudCIgcmVhZHMgaW4gYSAKd29ydGh3aGlsZSBudW1iZXIgc2FtcGxlcy4gCgpNb3JlIHByZWNpc2VseSwgdGhlIGZpbHRlcmluZyBrZWVwcyBnZW5lcyB0aGF0IGhhdmUgY291bnQtcGVyLW1pbGxpb24gKENQTSkgCmFib3ZlIGsgaW4gbiBzYW1wbGVzLiAKCmsgaXMgZGV0ZXJtaW5lZCBieSB0aGUgbWluLmNvdW50IGFyZ3VtZW50IHRvIHRoZSBmdW5jdGlvbiwgYW5kIGJ5IHRoZSBzYW1wbGUgCmxpYnJhcnkgc2l6ZXMuCgpuIGlzIGRldGVybWluZWQgYnkgdGhlIGRlc2lnbiBtYXRyaXguIG4gY2FuIGJlIHNlZW4gYXMgdGhlIHNtYWxsZXN0IGdyb3VwIHNhbXBsZQpzaXplLiBBIGBncm91cGAgb2Ygc2FtcGxlcy9jZWxscyBjYW4gYmUgZGVmaW5lZCBhcyBjZWxscyB0aGF0IGFyZSBtb3JlIHNpbWlsYXIKdG8gb25lIGFub3RoZXIsIGUuZy4sIGZyb20gdGhlIHNhbWUgc2VxdWVuY2luZyBiYXRjaCwgdGhlIHNhbWUgcGF0aWVudC4uLi4KSGVyZSB3ZSBjb3VsZCBhbHNvIHVzZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50IChjZWxsIHR5cGUpIGZvciBlYWNoIGNlbGwgYXMgdGhlCmdyb3VwaW5nIHZhcmlhYmxlOyAqbm90ZSBob3dldmVyLCB0aGF0IHRoaXMgdXN1YWxseSBpcyBub3QgYXZhaWxhYmxlIHVudGlsIGEqCipsYXRlciBzdGFnZSBpbiB0aGUgYW5hbHlzaXMgcGlwZWxpbmUqIChpLmUuLCBhZnRlciBkaW1lbnNpb24gcmVkdWN0aW9uIGFuZCAKY2x1c3RlcmluZywgdG9waWNzIHdlIHdpbGwgY292ZXIgbmV4dCBzZXNzaW9uLikKCklmIGFsbCB0aGUgZ3JvdXAgc2l6ZXMgYXJlIGxhcmdlciB0aGFuIHRoZSBgbGFyZ2UubmAgYXJndW1lbnQgb2YgdGhlIApmaWx0ZXJCeUV4cHIgZnVuY3Rpb24sIHdoaWNoIGRlZmF1bHRzIHRvIDEwLCB0aGVuIG4gd2lsbCBiZSB0YWtlbiBhcwpgbWluLnByb3BgKiB0aGUgdGhlIG51bWJlciBvZiBzYW1wbGVzL2NlbGxzIGluIHRoZSBzbWFsbGVzdCBncm91cC4KCk5vdGUgdGhhdCBhbGwgdGhlIGdyb3VwIHNpemVzIHdpbGwgb2Z0ZW4gYmUgbGFyZ2VyIHRoYW4gdGhlIGBsYXJnZS5uYCBpbiBjYXNlCm9mIHNpbmdsZS1jZWxsIGRhdGEuCgpgYGB7cn0KIyBTbG93IChtb3JlIHRoYW4gMW1pbikgLT4gZG8gbm90IHJ1bgojIGtlZXAyIDwtIGZpbHRlckJ5RXhwcihzY2UsCiMgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gc2NlJGNsdXN0ZXIsCiMgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jb3VudCA9IDEsCiMgICAgICAgICAgICAgICAgICAgICAgIG1pbi50b3RhbC5jb3VudCA9IDE1LAojICAgICAgICAgICAgICAgICAgICAgICBtaW4ucHJvcCA9IDAuMikKIyB0YWJsZShrZWVwMikKYGBgCgoKIyBRdWFsaXR5IGNvbnRyb2wKCkluIHF1YWxpdHkgY29udHJvbCAoUUMpLCB3ZSBjaGVjayB0aGUgcXVhbGl0eSBvZiBvdXIgZGF0YXNldC4gSW4gcGFydGljdWxhciwgCndlIGludmVzdGlnYXRlIHVuZGVzaXJhYmxlIG9kZGl0aWVzLCBzdWNoIGFzIGxvdy1xdWFsaXR5IGNlbGxzLCBlbXB0eSBkcm9wbGV0cwpvciBkb3VibGV0cy4KCiMjIElkZW50aWZ5aW5nIGFuZCByZW1vdmluZyBsb3ctcXVhbGl0eSBjZWxscwoKVGhlcmUgYXJlIHNldmVyYWwgZGlzdGluZ3Vpc2hpbmcgZmVhdHVyZXMgb2YgbG93LXF1YWxpdHkgY2VsbHMgdGhhdCBjYW4gYmUgdXNlZAppbiBvcmRlciB0byBpZGVudGlmeSB0aGVtLiBbQXMgZGVzY3JpYmVkIGluIHRoZSBPU0NBIGJvb2tdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTQvT1NDQS5iYXNpYy9xdWFsaXR5LWNvbnRyb2wuaHRtbCkgCmJvb2spOgoKMS4gVGhlIGxpYnJhcnkgc2l6ZSBpcyBkZWZpbmVkIGFzIHRoZSB0b3RhbCBzdW0gb2YgY291bnRzIGFjcm9zcyBhbGwgCnJlbGV2YW50IGZlYXR1cmVzIGZvciBlYWNoIGNlbGwuIEhlcmUsIHdlIHdpbGwgY29uc2lkZXIgdGhlIHJlbGV2YW50IGZlYXR1cmVzIAp0byBiZSB0aGUgZW5kb2dlbm91cyBnZW5lcy4gQ2VsbHMgd2l0aCBzbWFsbCBsaWJyYXJ5IHNpemVzIGFyZSBvZiBsb3cgcXVhbGl0eSAKYXMgdGhlIFJOQSBoYXMgYmVlbiBsb3N0IGF0IHNvbWUgcG9pbnQgZHVyaW5nIGxpYnJhcnkgcHJlcGFyYXRpb24sIGVpdGhlciBkdWUgCnRvIGNlbGwgbHlzaXMgb3IgaW5lZmZpY2llbnQgY0ROQSBjYXB0dXJlIGFuZCBhbXBsaWZpY2F0aW9uLgoKMi4gVGhlIG51bWJlciBvZiBleHByZXNzZWQgZmVhdHVyZXMgaW4gZWFjaCBjZWxsIGlzIGRlZmluZWQgYXMgdGhlIG51bWJlciBvZiAKZW5kb2dlbm91cyBnZW5lcyB3aXRoIG5vbi16ZXJvIGNvdW50cyBmb3IgdGhhdCBjZWxsLiBBbnkgY2VsbCB3aXRoIHZlcnkgCmZldyBleHByZXNzZWQgZ2VuZXMgaXMgbGlrZWx5IHRvIGJlIG9mIHBvb3IgcXVhbGl0eSBhcyB0aGUgZGl2ZXJzZSB0cmFuc2NyaXB0IApwb3B1bGF0aW9uIGhhcyBub3QgYmVlbiBzdWNjZXNzZnVsbHkgY2FwdHVyZWQuCgozLiBXZSBzb21ldGltZXMgaGF2ZSBzcGlrZS1pbiAoRVJDQykgdHJhbnNjcmlwdHMgYXZhaWxhYmxlLgpUaGUgcHJvcG9ydGlvbiBvZiByZWFkcyBtYXBwZWQgdG8gc3Bpa2UtaW4gdHJhbnNjcmlwdHMgaXMgY2FsY3VsYXRlZCByZWxhdGl2ZSAKdG8gdGhlIHRvdGFsIGNvdW50IGFjcm9zcyBhbGwgZmVhdHVyZXMgKGluY2x1ZGluZyBzcGlrZS1pbnMpIGZvciBlYWNoIGNlbGwuIApBcyB0aGUgc2FtZSBhbW91bnQgb2Ygc3Bpa2UtaW4gUk5BIHNob3VsZCBoYXZlIGJlZW4gYWRkZWQgdG8gZWFjaCBjZWxsLCAKYW55IGVucmljaG1lbnQgaW4gc3Bpa2UtaW4gY291bnRzIGlzIHN5bXB0b21hdGljIG9mIGxvc3Mgb2YgZW5kb2dlbm91cyBSTkEuCgo0LiBJbiB0aGUgYWJzZW5jZSBvZiBzcGlrZS1pbiB0cmFuc2NyaXB0cywgdGhlIHByb3BvcnRpb24gb2YgcmVhZHMgbWFwcGVkIAp0byBnZW5lcyBpbiB0aGUgbWl0b2Nob25kcmlhbCBnZW5vbWUgY2FuIGJlIHVzZWQuIEhpZ2ggcHJvcG9ydGlvbnMgYXJlIAppbmRpY2F0aXZlIG9mIHBvb3ItcXVhbGl0eSBjZWxscyAoSXNsYW0gZXQgYWwuIDIwMTQ7IElsaWNpYyBldCBhbC4gMjAxNiksIApwcmVzdW1hYmx5IGJlY2F1c2Ugb2YgbG9zcyBvZiBjeXRvcGxhc21pYyBSTkEgZnJvbSBwZXJmb3JhdGVkIGNlbGxzLiAKVGhlIHJlYXNvbmluZyBpcyB0aGF0LCBpbiB0aGUgcHJlc2VuY2Ugb2YgbW9kZXN0IGRhbWFnZSwgdGhlIGhvbGVzIGluIHRoZSAKY2VsbCBtZW1icmFuZSBwZXJtaXQgZWZmbHV4IG9mIGluZGl2aWR1YWwgdHJhbnNjcmlwdCBtb2xlY3VsZXMgYnV0IGFyZSB0b28gCnNtYWxsIHRvIGFsbG93IG1pdG9jaG9uZHJpYSB0byBlc2NhcGUsIGxlYWRpbmcgdG8gYSByZWxhdGl2ZSBlbnJpY2htZW50IG9mIAptaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHRzLiBGb3Igc2luZ2xlLW51Y2xlaSBSTkEtc2VxIGV4cGVyaW1lbnRzLCBoaWdoIApwcm9wb3J0aW9ucyBhcmUgYWxzbyB1c2VmdWwgYXMgdGhleSBjYW4gbWFyayBjZWxscyB3aGVyZSB0aGUgY3l0b3BsYXNtIGhhcyAKbm90IGJlZW4gc3VjY2Vzc2Z1bGx5IHN0cmlwcGVkLgoKIyMgQ2FsY3VsYXRlIFFDIHZhcmlhYmxlcwoKVGhpcyBmdW5jdGlvbiBjYWxjdWxhdGVzIHVzZWZ1bCBRQyBtZXRyaWNzIGZvciBpZGVudGlmaWNhdGlvbiBhbmQgcmVtb3ZhbCBvZiAKcG90ZW50aWFsbHkgcHJvYmxlbWF0aWMgY2VsbHMuIERlZmF1bHQgcGVyLWNlbGwgbWV0cmljcyBhcmUgdGhlIHN1bSBvZiBjb3VudHMgCihpLmUuLCB0aGUgbGlicmFyeSBzaXplKSBhbmQgdGhlIG51bWJlciBvZiBkZXRlY3RlZCBmZWF0dXJlcy4gVGhlIHBlcmNlbnRhZ2Ugb2YgCmNvdW50cyBpbiB0aGUgdG9wIGZlYXR1cmVzIGFsc28gcHJvdmlkZXMgYSBtZWFzdXJlIG9mIGxpYnJhcnkgY29tcGxleGl0eS4KCklmIHN1YnNldHMgaXMgc3BlY2lmaWVkLCB0aGVzZSBzdGF0aXN0aWNzIGFyZSBhbHNvIGNvbXB1dGVkIGZvciBlYWNoIHN1YnNldCBvZiAKZmVhdHVyZXMuIFRoaXMgaXMgdXNlZnVsIGZvciBpbnZlc3RpZ2F0aW5nIGdlbmUgc2V0cyBvZiBpbnRlcmVzdCwgZS5nLiwgCm1pdG9jaG9uZHJpYWwgZ2VuZXMuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShzY2F0ZXIpCgojIGNoZWNrIEVSQ0Mgc3Bpa2UtaW4gdHJhbnNjcmlwdHMKc3VtKGdyZXBsKCJeRVJDQy0iLCByb3duYW1lcyhzY2UpKSkgIyBubyBzcGlrZS1pbiB0cmFuc2NyaXB0cyBhdmFpbGFibGUKCiMgY2hlY2sgbWl0b2Nob25kcmlhbCBnZW5lcwpzdW0ocm93RGF0YShzY2UpJGNocm9tb3NvbWVfbmFtZT09Ik1UIixuYS5ybSA9IFRSVUUpICMgMjggbWl0b2Nob25kcmlhbCBnZW5lcwpzdW0oZ3JlcGwoIl5NVC0iLCByb3duYW1lcyhzY2UpKSkgIyBhbHRlcm5hdGl2ZWx5CmlzLm1pdG8gPC0gZ3JlcGwoIl5NVC0iLCByb3duYW1lcyhzY2UpKQoKIyMgY2FsY3VsYXRlIFFDIG1ldHJpY3MKZGYgPC0gcGVyQ2VsbFFDTWV0cmljcyhzY2UsIHN1YnNldHM9bGlzdChNaXRvPWlzLm1pdG8pKQpoZWFkKGRmKQoKIyBhZGQgUUMgdmFyaWFibGVzIHRvIHNjZSBvYmplY3QKY29sRGF0YShzY2UpIDwtIGNiaW5kKGNvbERhdGEoc2NlKSwgZGYpCgojIHRoZSBRQyB2YXJpYWJsZXMgaGF2ZSBub3cgYmVlbiBhZGRlZCB0byB0aGUgY29sRGF0YSBvZiBvdXIgU0NFIG9iamVjdC4KaGVhZChjb2xEYXRhKHNjZSkpCmBgYAoKIyMgRXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcwoKSW4gdGhlIGZpZ3VyZSBiZWxvdywgd2Ugc2VlIHRoYXQgc2V2ZXJhbCBjZWxscyBoYXZlIGEgdmVyeSBsb3cgbnVtYmVyIG9mIApleHByZXNzZWQgZ2VuZXMsIGFuZCB3aGVyZSBtb3N0IG9mIHRoZSBtb2xlY3VsZXMgYXJlIGRlcml2ZWQgZnJvbSAKbWl0b2Nob25kcmlhbCBnZW5lcy4gVGhpcyBpbmRpY2F0ZXMgbGlrZWx5IGRhbWFnZWQgY2VsbHMsIHByZXN1bWFibHkgYmVjYXVzZSAKb2YgbG9zcyBvZiBjeXRvcGxhc21pYyBSTkEgZnJvbSBwZXJmb3JhdGVkIGNlbGxzLCBzbyB3ZSBzaG91bGQgcmVtb3ZlIHRoZXNlIGZvciAKdGhlIGRvd25zdHJlYW0gYW5hbHlzaXMuCgpgYGB7cixldmFsPUZBTFNFfQojIE51bWJlciBvZiBnZW5lcyB2cyBsaWJyYXJ5IHNpemUKcGxvdENvbERhdGEoc2NlLCB4ID0gInN1bSIsIHk9ImRldGVjdGVkIiwgY29sb3VyX2J5PSJjbHVzdGVyIikgCgojIE1pdG9jaG9uZHJpYWwgZ2VuZXMKcGxvdENvbERhdGEoc2NlLCB4ID0gImRldGVjdGVkIiwgeT0ic3Vic2V0c19NaXRvX3BlcmNlbnQiKQpgYGAKCiMjIFFDIHVzaW5nIGFkYXB0aXZlIHRocmVzaG9sZHMKCkJlbG93LCB3ZSByZW1vdmUgY2VsbHMgdGhhdCBhcmUgb3V0bHlpbmcgd2l0aCByZXNwZWN0IHRvCgogMS4gQSBsb3cgc2VxdWVuY2luZyBkZXB0aCAobnVtYmVyIG9mIFVNSXMpOwogMi4gQSBsb3cgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkOwogMy4gQSBoaWdoIHBlcmNlbnRhZ2Ugb2YgcmVhZHMgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzLgogCiBIZXJlIHdlIHdpbGwgcmVtb3ZlIGNlbGxzIGZvciBRQyBiYXNlZCBvbiAqKmFkYXB0aXZlIHRocmVzaG9sZHMqKiByZWxhdGVkIHRvCiB0aGUgdGhyZWUgcG9pbnRzIGZyb20gYWJvdmUuIEFkYXB0aXZlIHRyaGVzaG9sZHMgYXJlIHVzZWQgYXMgb3Bwb3NlZCB0bwogKipmaXhlZCB0aHJlc2hvbGRzKiouIAogCldpdGggZml4ZWQgdGhyZXNob2xkcywgd2UgdXNlIGZpeGVkIGN1dC1vZmYgdmFsdWVzIGZvciBlYWNoIGNlbGwgdG8gcGFzcyBRQywgCmUuZy4sIHdlIG1pZ2h0IGNvbnNpZGVyIGNlbGxzIHRvIGJlIGxvdyBxdWFsaXR5IGlmIHRoZXkgaGF2ZSBsaWJyYXJ5IHNpemVzIApiZWxvdyAxMDAsMDAwIHJlYWRzOyBleHByZXNzIGZld2VyIHRoYW4gNSwwMDAgZ2VuZXM7IGhhdmUgc3Bpa2UtaW4gcHJvcG9ydGlvbnMKYWJvdmUgMTAlOyBvciBoYXZlIG1pdG9jaG9uZHJpYWwgcHJvcG9ydGlvbnMgYWJvdmUgMTAlLgoKV2l0aCBhZGFwdGl2ZSB0aHJlc2hvbGRzLCB3ZSBhc3N1bWUgdGhhdCBtb3N0IG9mIHRoZSBkYXRhc2V0IGNvbnNpc3RzIG9mIApoaWdoLXF1YWxpdHkgY2VsbHMuIFdlIHRoZW4gaWRlbnRpZnkgY2VsbHMgdGhhdCBhcmUgb3V0bGllcnMgZm9yIHRoZSB2YXJpb3VzIApRQyBtZXRyaWNzLCBiYXNlZCBvbiB0aGUgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAoTUFEKSBmcm9tIHRoZSBtZWRpYW4gdmFsdWUKb2YgZWFjaCBtZXRyaWMgYWNyb3NzIGFsbCBjZWxscy4gQnkgZGVmYXVsdCwgd2UgY29uc2lkZXIgYSB2YWx1ZSB0byBiZSBhbm91dGxpZXIgCmlmIGl0IGlzIG1vcmUgdGhhbiAzIE1BRHMgZnJvbSB0aGUgbWVkaWFuIGluIHRoZSDigJxwcm9ibGVtYXRpY+KAnSBkaXJlY3Rpb24uIFRoaXMgCmlzIGxvb3NlbHkgbW90aXZhdGVkIGJ5IHRoZSBmYWN0IHRoYXQgc3VjaCBhIGZpbHRlciB3aWxsIHJldGFpbiA5OSUgb2YgCm5vbi1vdXRsaWVyIHZhbHVlcyB0aGF0IGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFdlIGRlbW9uc3RyYXRlIGFkb3B0aW5nCmFkYXB0aXZlIHRocmVzaG9sZHMgb24gdGhlIE1hY29za28gZGF0YXNldDoKCmBgYHtyfQpsb3dMaWIgPC0gaXNPdXRsaWVyKGRmJHN1bSwgdHlwZT0ibG93ZXIiLCBsb2c9VFJVRSkKbG93RmVhdHVyZXMgPC0gaXNPdXRsaWVyKGRmJGRldGVjdGVkLCB0eXBlPSJsb3dlciIsIGxvZz1UUlVFKQpoaWdoTWl0byA8LSBpc091dGxpZXIoZGYkc3Vic2V0c19NaXRvX3BlcmNlbnQsIHR5cGU9ImhpZ2hlciIpCgp0YWJsZShsb3dMaWIpCnRhYmxlKGxvd0ZlYXR1cmVzKQp0YWJsZShoaWdoTWl0bykKCmRpc2NhcmRDZWxscyA8LSAobG93TGliIHwgbG93RmVhdHVyZXMgfCBoaWdoTWl0bykKdGFibGUoZGlzY2FyZENlbGxzKQpjb2xEYXRhKHNjZSkkZGlzY2FyZENlbGxzIDwtIGRpc2NhcmRDZWxscwoKIyB2aXN1YWxpemUgY2VsbHMgdG8gYmUgcmVtb3ZlZAojIHBsb3RDb2xEYXRhKHNjZSwgeCA9ICJzdW0iLCB5PSJkZXRlY3RlZCIsIGNvbG91cl9ieT0iZGlzY2FyZENlbGxzIikKIyBwbG90Q29sRGF0YShzY2UsIHggPSAiZGV0ZWN0ZWQiLCB5PSJzdWJzZXRzX01pdG9fcGVyY2VudCIsIGNvbG91cl9ieSA9ICJkaXNjYXJkQ2VsbHMiKQpgYGAKCldlIHJlbW92ZWQgYSB0b3RhbCBvZiAkMzQyMyQgY2VsbHMsIG1vc3Qgb2Ygd2hpY2ggYmVjYXVzZSBvZiBhbiBvdXRseWluZ2x5IGhpZ2ggCnBlcmNlbnRhZ2Ugb2YgcmVhZHMgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzLgoKIyMgSWRlbnRpZnlpbmcgYW5kIHJlbW92aW5nIGVtcHR5IGRyb3BsZXRzCgpOb3RlIHRoYXQgdGhlIHJlbW92YWwgb2YgY2VsbHMgd2l0aCBsb3cgc2VxdWVuY2luZyBkZXB0aCB1c2luZyB0aGUgYWRhcHRpdmUgCnRocmVzaG9sZCBwcm9jZWR1cmUgYWJvdmUgaXMgYSB3YXkgb2YgcmVtb3ZpbmcgZW1wdHkgZHJvcGxldHMuIApPdGhlciBhcHByb2FjaGVzIGFyZSBwb3NzaWJsZSwgZS5nLiwgcmVtb3ZpbmcgY2VsbHMgYnkgc3RhdGlzdGljYWwgdGVzdGluZyAKdXNpbmcgYGVtdHB5RHJvcHNgLiBUaGlzIGRvZXMgcmVxdWlyZSB1cyB0byBzcGVjaWZ5IGEgbG93ZXIgYm91bmQgb24gdGhlIHRvdGFsIApudW1iZXIgb2YgVU1JcywgYmVsb3cgd2hpY2ggYWxsIGNlbGxzIGFyZSBjb25zaWRlcmVkIHRvIGNvcnJlc3BvbmQgdG8gZW1wdHkgCmRyb3BsZXRzLiBUaGlzIGxvd2VyIGJvdW5kIG1heSBub3QgYmUgdHJpdmlhbCB0byBkZXJpdmUsIGJ1dCB0aGUgYGJhcmNvZGVSYW5rc2AKZnVuY3Rpb24gY2FuIGJlIHVzZWZ1bCB0byBpZGVudGlmeSBhbiBlbGJvdy9rbmVlIHBvaW50LgoKSW4gYnJpZWYsIHRoZSBzdGVwcyB0YWtlbiBieSB0aGUgYGVtdHB5RHJvcHNgIGZ1bmN0aW9uIGNhbiBiZSBzdW1tYXJpemVkIGFzIApmb2xsb3dzOgoKMS4JRGVmaW5lIHRocmVzaG9sZCBUIG9mIHRvdGFsIFVNSSBjb3VudHMgKGUuZy4gd2l0aCB0aGUgaGVscCBvZiB0aGUgCmBiYXJjb2RlUmFua3NgIGZ1bmN0aW9uKSwgYmVsb3cgd2hpY2ggY2VsbHMgbWF5IGJlIGNvbnNpZGVyZWQgdG8gYmUgZnJvbSBlbXB0eSAKZHJvcGxldHMuIENhbGwgdGhpcyBzZXQgb2YgY2VsbHMgRS4KCjIuCURlZmluZSAkQV9nJCBhcyB0aGUgdG90YWwgZ2VuZSBleHByZXNzaW9uIGFjcm9zcyBhbGwgY2VsbHMgaW4gRS4KCjMuCURlZmluZSAkcGlfZyQgYXMgdGhlIHJlbGF0aXZlIGNvbnRyaWJ1dGlvbiBvZiBnZW5lIGcgdG8gdGhlIGFtYmllbnQgcHJvZmlsZS4KCjQuCUNhbGN1bGF0ZSBwLXZhbHVlIGZvciBlYWNoIGNlbGwgdG8gaGF2ZSBhIHRyYW5zY3JpcHRpb25hbCBwcm9maWxlIHNpbWlsYXIgdG8KdGhlIGFtYmllbnQgc29sdXRpb24uIEludHVpdGl2ZWx5LCBhIHAtdmFsdWUgYmVsb3cgdGhlIHJlcXVlc3RlZCBhbHBoYSBsZXZlbCAKd291bGQgY29ycmVzcG9uZCB0byBhIGNlbGwgZm9yIHdoaWNoIHRoZSBvYnNlcnZlZCBjb3VudCBwcm9maWxlIHN0cm9uZ2x5IApkZXZpYXRlcyBmcm9tIHRoZSBjb3VudCBwcm9maWxlIG9ic2VydmVkIGluIHRoZSBjZWxscyB3aXRoIGEgbGlicmFyeSBzaXplIApiZWxvdyB0aHJlc2hvbGQgVCwgaS5lLiwgYSBub24tZW1wdHkgZHJvcGxldC4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KERyb3BsZXRVdGlscykKYmNyYW5rIDwtIGJhcmNvZGVSYW5rcyhjb3VudHMoc2NlKSkKCiMgT25seSBzaG93aW5nIHVuaXF1ZSBwb2ludHMgZm9yIHBsb3R0aW5nIHNwZWVkLiBEdXBsaWNhdGVkIHJhbmtzIGFyZSBhIAojIGNvbnNlcXVlbmNlIG9mIHRpZXMgaW4gdGhlIHJhbmtzLCBpLmUuLCB3aGVuIGNlbGxzIGhhdmUgYW4gZXF1YWwgbGlicmFyeSBzaXplLgpzdW0oZHVwbGljYXRlZChiY3JhbmskcmFuaykpCnVuaXEgPC0gIWR1cGxpY2F0ZWQoYmNyYW5rJHJhbmspCgpwbG90KGJjcmFuayRyYW5rW3VuaXFdLCBiY3JhbmskdG90YWxbdW5pcV0sIGxvZz0ieHkiLAogICAgeGxhYj0iUmFuayIsIHlsYWI9IlRvdGFsIFVNSSBjb3VudCIsIGNleC5sYWI9MS4yKQphYmxpbmUoaD1tZXRhZGF0YShiY3JhbmspJGluZmxlY3Rpb24sIGNvbD0iZGFya2dyZWVuIiwgbHR5PTIpCmFibGluZShoPW1ldGFkYXRhKGJjcmFuaykka25lZSwgY29sPSJkb2RnZXJibHVlIiwgbHR5PTIpCmFibGluZShoPTM1MCwgY29sPSJvcmFuZ2UiLCBsdHk9MikgIyBwaWNrZWQgdmlzdWFsbHkgbXlzZWxmCmxlZ2VuZCgidG9wcmlnaHQiLCAKICAgICAgIGxlZ2VuZD1jKCJJbmZsZWN0aW9uIiwgIktuZWUiLCAiRW1waXJpY2FsIGtuZWUgcG9pbnQiKSwgCiAgICAgICBjb2w9YygiZGFya2dyZWVuIiwgImRvZGdlcmJsdWUiLCAib3JhbmdlIiksIAogICAgICAgbHR5PTIsIAogICAgICAgY2V4PTEuMikKCnNldC5zZWVkKDEwMCkKbGltaXQgPC0gMzUwICAgCmFsbC5vdXQgPC0gZW1wdHlEcm9wcyhjb3VudHMoc2NlKSwgbG93ZXI9bGltaXQsIHRlc3QuYW1iaWVudD1UUlVFKQojIHAtdmFsdWVzIGZvciBjZWxscyB3aXRoIHRvdGFsIFVNSSBjb3VudCB1bmRlciB0aGUgbG93ZXIgYm91bmQuCmhpc3QoYWxsLm91dCRQVmFsdWVbYWxsLm91dCRUb3RhbCA8PSBsaW1pdCAmIGFsbC5vdXQkVG90YWwgPiAwXSwKICAgIHhsYWI9IlAtdmFsdWUiLCBtYWluPSIiLCBjb2w9ImdyZXk4MCIsIGJyZWFrcz0yMCkKCiMgYnV0IG5vdGUgdGhhdCBpdCB3b3VsZCByZW1vdmUgYSB2ZXJ5IGhpZ2ggbnVtYmVyIG9mIGNlbGxzCmxlbmd0aCh3aGljaChhbGwub3V0JEZEUiA8PSAwLjAxKSkgIyByZXRhaW5lZAoKIyBzbyB3ZSBzdGljayB0byB0aGUgbW9yZSBsZW5pZW50IGFkYXB0aXZlIGZpbHRlcmluZyBzdHJhdGVneQojIHJlbW92ZSBjZWxscyBpZGVudGlmaWVkIHVzaW5nIGFkYXB0aXZlIHRocmVzaG9sZHMKc2NlIDwtIHNjZVssICFjb2xEYXRhKHNjZSkkZGlzY2FyZENlbGxzXQpgYGAKCiMjIElkZW50aWZ5aW5nIGFuZCByZW1vdmluZyBkb3VibGV0cwoKV2Ugd2lsbCB1c2UgCltzY0RibEZpbmRlcl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzLzMuMTQvYmlvYy9odG1sL3NjRGJsRmluZGVyLmh0bWwpIAp0byBkZXRlY3QgZG91YmxldCBjZWxscy4KCkFzIGRpc2N1c3NlZCBpbiB0aGUgdGhlb3J5IHNlc3Npb24gb2YgbGFzdCB3ZWVrLCB0aGUgc3RlcHMgdGFrZW4gYnkgCmBzY0RibEZpbmRlcmAgY2FuIGJlIHN1bW1hcml6ZWQgYXMgZm9sbG93czoKCjEuIFBlcmZvcm0gcHJpbmNpcGFsIGNvbXBvbmVudHMgYW5hbHlzaXMgKFBDQSkgb24gbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiAKY291bnRzLiBUaGlzIGFsbG93cyBmb3IgcHJvamVjdGluZyBlYWNoIGNlbGwgaW4gdGhlIGRhdGFzZXQgaW50byBhIDJEIHNwYWNlCihmb3IgbW9yZSBkZXRhaWxzIG9uIFBDQSwgc2VlIG5leHQgd2Vla3Mgc2Vzc2lvbikuCgoyLiBSYW5kb21seSBzZWxlY3QgdHdvIGNlbGxzLCBzdW0gdGhlaXIgY291bnRzIGFuZCBub3JtYWxpemUsIGFuZCBwcm9qZWN0IAppbnRvIFBDQSBzcGFjZSBmcm9tIHN0ZXAgMS4gSW4gb3RoZXIgd29yZHMsIGFydGlmaWNpYWxseSBnZW5lcmF0ZSBkb3VibGV0cyBhbmQKc2VlIHdoZXJlIHRoZXkgYXJlIGxvY2F0ZWQgaW4gdGhlIDJEIHNwYWNlLgoKMy4gUmVwZWF0IHN0ZXAgMiBtYW55IHRpbWVzIChnZW5lcmF0ZSBtYW55IGFydGlmaWNpYWwgZG91YmxldHMpLgoKNC4gR2VuZXJhdGUgbmVpZ2hib3IgbmV0d29yayBpbiB0aGUgMkQgc3BhY2UuIFRoZSBuZXR3b3JrIGlzIHRoZW4gdXNlZCB0byAKZXN0aW1hdGUgYSBudW1iZXIgb2YgY2hhcmFjdGVyaXN0aWNzIGZvciBlYWNoIGNlbGwsIGluIHBhcnRpY3VsYXIgdGhlIHByb3BvcnRpb24gCmYgYXJ0aWZpY2lhbCBkb3VibGV0cyBhbW9uZyB0aGUgbmVhcmVzdCBuZWlnaGJvcnMuCgo1LiBVc2UgdGhpcyBpbmZvcm1hdGlvbiwgYWxvbmcgd2l0aCBvdGhlciBwcmVkaWN0b3JzLCB0byB0cmFpbiBhIGNsYXNzaWZpZXIKKGdyYWRpZW50IGJvb3N0ZWQgdHJlZSkgdGhhdCBhbGxvd3MgZm9yIGRpc3Rpbmd1aXNoaW5nIGRvdWJsZXRzIGZyb20gc2luZ2xldHMuCgpOb3RlOiBvbmx5IGNsYXNzaWZpZXMgZm9yIGlkZW50aWZ5aW5nIGRvdWJsZXRzIGZvciB3aGljaCB0aGUgdHdvIGNlbGxzCmFyZSBmcm9tIGRpZmZlcmVudCBjZWxsIHR5cGUgY2x1c3RlcnMuCgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIHBlcmZvcm0gZG91YmxldCBkZXRlY3Rpb24KbGlicmFyeShzY0RibEZpbmRlcikKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMjExMTAzKQpzYW1wbGVJRCA8LSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KGNvbERhdGEoc2NlKSRjZWxsLmlkLCBzcGxpdD0iXyIpLCAiW1siLCAxKSkKdGFibGUoc2FtcGxlSUQpCnNjZSA8LSBzY0RibEZpbmRlcihzY2UsIAogICAgICAgICAgICAgICAgICAgcmV0dXJuVHlwZT0idGFibGUiLAogICAgICAgICAgICAgICAgICAgc2FtcGxlcyA9IGZhY3RvcihzYW1wbGVJRCkpCnRhYmxlKHNjZSRzY0RibEZpbmRlci5jbGFzcykKCgojIyB2aXN1YWxpemUgdGhlc2Ugc2NvcmVzCiMjIGV4cGxvcmUgZG91YmxldCBzY29yZSB3cnQgb3JpZ2luYWwgY2x1c3RlciBsYWJlbHMKYm94cGxvdChsb2cxcChzY2Ukc2NEYmxGaW5kZXIuc2NvcmUpIH4gZmFjdG9yKGNvbERhdGEoc2NlKSRjbHVzdGVyLCBleGNsdWRlPU5VTEwpKQoKdGFiIDwtIHRhYmxlKHNjZSRzY0RibEZpbmRlci5jbGFzcywgc2NlJGNsdXN0ZXIsIAogICAgICBleGNsdWRlPU5VTEwpCnRhYgp0KHQodGFiKSAvIGNvbFN1bXModGFiKSkKCmJhcnBsb3QodCh0KHRhYikgLyBjb2xTdW1zKHRhYikpWzIsXSwKICAgICAgICB4bGFiID0gIkNsdXN0ZXIiLCB5bGFiID0gIkZyYWN0aW9uIG9mIGRvdWJsZXRzIikKCnJhbmdlKHNjZSRzY0RibEZpbmRlci5zY29yZVtzY2Ukc2NEYmxGaW5kZXIuY2xhc3MgID09ICJkb3VibGV0IiAmIHNhbXBsZUlEID09ICJyMSJdKQpyYW5nZShzY2Ukc2NEYmxGaW5kZXIuc2NvcmVbc2NlJHNjRGJsRmluZGVyLmNsYXNzICA9PSAic2luZ2xldCIgJiBzYW1wbGVJRCA9PSAicjEiXSkKYGBgCgpgYGB7cn0KIyByZW1vdmUgZG91YmxldHMKc2NlIDwtIHNjZVssIXNjZSRzY0RibEZpbmRlci5jbGFzcyA9PSAiZG91YmxldCJdCmBgYAoKIyBOb3JtYWxpemF0aW9uCgpOb3JtYWxpemF0aW9uIGFpbXMgdG8gcmVtb3ZlIHRlY2huaWNhbCBlZmZlY3RzIHN1Y2ggYXMgc2VxdWVuY2luZyBkZXB0aCBzbyB0aGF0IApjb21wYXJpc29ucyBiZXR3ZWVuIGNlbGxzIGFyZSBub3QgY29uZm91bmRlZCBieSB0aGVtLiBUaGUgbW9zdCBjb21tb25seSB1c2VkIApub3JtYWxpemF0aW9uIG1ldGhvZHMgbWV0aG9kcyB1c2Ugc2NhbGluZywgd2hlcmUgYSBzY2FsaW5nIGZhY3RvciAoYWxzbyBjYWxsZWQgCnNpemUgZmFjdG9yLCBub3JtYWxpemF0aW9uIGZhY3RvcikgaXMgZXN0aW1hdGVkIGZvciBlYWNoIGNlbGwuIFRoZXNlIHNjYWxpbmcgCmZhY3RvcnMgKGUuZy4sIHRoZSBlZmZlY3RpdmUgbGlicmFyeSBzaXplKSBjYW4gYmUgaW5jbHVkZWQgYXMgYW4gb2Zmc2V0CnRvIGRvd25zdHJlYW0gbW9kZWxpbmcgcHJvY2VkdXJlcy4gVGhpcyBlZmZlY3RpdmVseSBhbGxvd3MgZm9yIHBlcmZvcm1pbmcKaW5mZXJlbmNlIG9uIHRoZSByZWxhdGl2ZSBhYnVuZGFuY2Ugb2YgYSBnZW5lIGluIGEgY2VsbCwgYWZ0ZXIgYWNjb3VudGluZyBmb3IgCmxpYnJhcnkgc2l6ZSBhbmQgUk5BIGNvbXBvc2l0aW9uIGRpZmZlcmVuY2VzIGJldHdlZW4gY2VsbHMsIHdoaWNoIGlzIG11Y2ggbW9yZQpyZWxldmFudCB0aGFuIGNvbXBhcmluZyByYXcgY291bnRzCgpGb3Igbm9ybWFsaXphdGlvbiwgdGhlIHNpemUgZmFjdG9ycyAkc19pJCBjb21wdXRlZCBoZXJlIGFyZSBzaW1wbHkgc2NhbGVkIApsaWJyYXJ5IHNpemVzOgpcWyBOX2kgPSBcc3VtX2cgWV97Z2l9IFxdClxbIHNfaSA9IE5faSAvIFxiYXJ7Tn1faSBcXQoKYGBge3J9CnNjZSA8LSBsb2dOb3JtQ291bnRzKHNjZSkKCiMgbm90ZSB3ZSBhbHNvIHJldHVybmVkIGxvZyBjb3VudHM6IHNlZSB0aGUgYWRkaXRpb25hbCBsb2djb3VudHMgYXNzYXkuCnNjZQoKIyB5b3UgY2FuIGV4dHJhY3Qgc2l6ZSBmYWN0b3JzIHVzaW5nCnNmIDwtIGxpYnJhcnlTaXplRmFjdG9ycyhzY2UpCm1lYW4oc2YpICMgZXF1YWwgdG8gMSBkdWUgdG8gc2NhbGluZy4KcGxvdCh4PSBsb2coY29sU3Vtcyhhc3NheXMoc2NlKSRjb3VudHMpKSwgCiAgICAgeT1zZikKYGBgCgpGcm9tIHRoZSBPU0NBIGJvb2s6CkFsdGVybmF0aXZlbHksIHdlIG1heSB1c2UgbW9yZSBzb3BoaXN0aWNhdGVkIGFwcHJvYWNoZXMgZm9yIHZhcmlhbmNlIHN0YWJpbGl6aW5nIAp0cmFuc2Zvcm1hdGlvbnMgaW4gZ2Vub21pY3MgZGF0YSwgZS5nLiwgYERFU2VxMmAgb3IgYHNjdHJhbnNmb3JtYC4gVGhlc2UgYWltIAp0byByZW1vdmUgdGhlIG1lYW4tdmFyaWFuY2UgdHJlbmQgbW9yZSBlZmZlY3RpdmVseSB0aGFuIHRoZSBzaW1wbGVyIAp0cmFuc2Zvcm1hdGlvbnMgbWVudGlvbmVkIGFib3ZlLCB0aG91Z2ggaXQgY291bGQgYmUgYXJndWVkIHdoZXRoZXIgdGhpcyBpcyAKYWN0dWFsbHkgZGVzaXJhYmxlLiBGb3IgbG93LWNvdmVyYWdlIHNjUk5BLXNlcSBkYXRhLCB0aGVyZSB3aWxsIGFsd2F5cyBiZSBhIAptZWFuLXZhcmlhbmNlIHRyZW5kIHVuZGVyIGFueSB0cmFuc2Zvcm1hdGlvbiwgZm9yIHRoZSBzaW1wbGUgcmVhc29uIHRoYXQgdGhlIAp2YXJpYW5jZSBtdXN0IGJlIHplcm8gd2hlbiB0aGUgbWVhbiBjb3VudCBpcyB6ZXJvLiBUaGVzZSBtZXRob2RzIGFsc28gZmFjZSB0aGUgCmNoYWxsZW5nZSBvZiByZW1vdmluZyB0aGUgbWVhbi12YXJpYW5jZSB0cmVuZCB3aGlsZSBwcmVzZXJ2aW5nIHRoZSBpbnRlcmVzdGluZyAKY29tcG9uZW50IG9mIHZhcmlhdGlvbiwgaS5lLiwgdGhlIGxvZy1mb2xkIGNoYW5nZXMgYmV0d2VlbiBzdWJwb3B1bGF0aW9uczsgdGhpcyAKbWF5IG9yIG1heSBub3QgYmUgZG9uZSBhZGVxdWF0ZWx5LCBkZXBlbmRpbmcgb24gdGhlIGFnZ3Jlc3NpdmVuZXNzIG9mIAp0aGUgYWxnb3JpdGhtLgoKSW4gcHJhY3RpY2UsIHRoZSBsb2ctdHJhbnNmb3JtYXRpb24gaXMgYSBnb29kIGRlZmF1bHQgY2hvaWNlIGR1ZSB0byBpdHMgCnNpbXBsaWNpdHkgYW5kIGludGVycHJldGFiaWxpdHksIGFuZCBpcyB3aGF0IHdlIHdpbGwgYmUgdXNpbmcgZm9yIGFsbCAKZG93bnN0cmVhbSBhbmFseXNlcy4KCi0tLQoKLS0tIGVuZCBsYWIgc2Vzc2lvbiAxIC0tLQoKLS0tCgojIE5vcm1hbGl6YXRpb24gKGNvbnRpbnVlZCkKCk5vcm1hbGl6YXRpb24gaXMgbmVjZXNzYXJ5IHRvIGNvcnJlY3QgZm9yIHNldmVyYWwgc291cmNlcyBvZiB0ZWNobmljYWwgCnZhcmlhdGlvbjoKCiAtICoqRGlmZmVyZW5jZXMgaW4gc2VxdWVuY2luZyBkZXB0aCoqIGJldHdlZW4gc2FtcGxlcy4gU29tZSBzYW1wbGVzIGdldCAKIHNlcXVlbmNlZCBkZWVwZXIgaW4gdGhlIHNlbnNlIHRoYXQgdGhleSBjb25zaXN0IG9mIG1vcmUgKG1hcHBlZCkgcmVhZHMgYW5kIAogdGhlcmVmb3JlIGNhbiBiZSBjb25zaWRlcmVkIHRvIGNvbnRhaW4gYSBoaWdoZXIgYW1vdW50IG9mIGluZm9ybWF0aW9uLCB3aGljaCAKIHdlIHNob3VsZCBiZSB0YWtpbmcgaW50byBhY2NvdW50LiBJbiBhZGRpdGlvbiwgaWYgYSBzYW1wbGUgaXMgc2VxdWVuY2VkIGRlZXBlciwgCiBpdCBpcyBuYXR1cmFsIHRoYXQgdGhlIGNvdW50cyBmb3IgZWFjaCBnZW5lIHdpbGwgYmUgaGlnaGVyLCBqZW9wYXJkaXppbmcgYSAKIGRpcmVjdCBjb21wYXJpc29uIG9mIHRoZSBleHByZXNzaW9uIGNvdW50cy4KIC0gKipEaWZmZXJlbmNlcyBpbiBSTkEgcG9wdWxhdGlvbiBjb21wb3NpdGlvbioqIGJldHdlZW4gc2FtcGxlcy4gQXMgYW4gZXh0cmVtZSAKIGV4YW1wbGUsIHN1cHBvc2UgdGhhdCB0d28gc2FtcGxlcyBoYXZlIGJlZW4gc2VxdWVuY2VkIHRvIHRoZSBleGFjdCBzYW1lIGRlcHRoLgogT25lIHNhbXBsZSBpcyBjb250YW1pbmF0ZWQgYW5kIGhhcyBhIHZlcnkgaGlnaCBjb25jZW50cmF0aW9uIG9mIHRoZSAKIGNvbnRhbWluYW50IGNETkEgYmVpbmcgc2VxdWVuY2VkLCBidXQgb3RoZXJ3aXNlIHRoZSB0d28gc2FtcGxlcyBhcmUgaWRlbnRpY2FsLiAKIFNpbmNlIHRoZSBjb250YW1pbmFudCB3aWxsIGJlIHRha2luZyB1cCBhIHNpZ25pZmljYW50IHByb3BvcnRpb24gb2YgdGhlIHJlYWRzIAogYmVpbmcgc2VxdWVuY2VkLCB0aGUgY291bnRzIHdpbGwgbm90IGJlIGRpcmVjdGx5IGNvbXBhcmFibGUgYmV0d2VlbiB0aGUgCiBzYW1wbGVzLiBIZW5jZSwgd2UgbWF5IGFsc28gd2FudCB0byBjb3JyZWN0IGZvciBkaWZmZXJlbmNlcyBpbiB0aGUgY29tcG9zaXRpb24gCiBvZiB0aGUgUk5BIHBvcHVsYXRpb24gb2YgdGhlIHNhbXBsZXMgCiAoc2VlIFtlZGdlUiBtYW51YWwgY2hhcHRlciAyLjhdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9lZGdlUi9pbnN0L2RvYy9lZGdlUlVzZXJzR3VpZGUucGRmKSkuCiAtICoqT3RoZXIgdGVjaG5pY2FsIHZhcmlhdGlvbioqIHN1Y2ggYXMgc2FtcGxlLXNwZWNpZmljIEdDLWNvbnRlbnQgb3IgCiB0cmFuc2NyaXB0IGxlbmd0aCBlZmZlY3RzIG1heSBhbHNvIGJlIGFjY291bnRlZCBmb3IuCiAKYGBge3J9CnRhYmxlKHNjZSRjbHVzdGVyKQpgYGAKCkJlbG93LCB3ZSB3aWxsIHZpc3VhbGl6ZSB0aGUgbm9ybWFsaXphdGlvbiBvY2N1cnJpbmcgYmV0d2VlbiB0d28gY2VsbHMgb2YgdGhlIApzYW1lIGNlbGwgdHlwZSAod2hpY2ggY291bGQgYmUgY29uc2lkZXJlZCB0ZWNobmljYWwgcmVwZWF0cyk6IAoKYGBge3J9CnNlbGVjdCA8LSBzY2UkY2x1c3RlciA9PSAiMSIKc2VsZWN0W2lzLm5hKHNlbGVjdCldIDwtIEZBTFNFCmNzIDwtIGNvbFN1bXMoYXNzYXlzKHNjZSkkY291bnRzWyxzZWxlY3RdKQpjc1tvcmRlcihjcywgZGVjcmVhc2luZyA9IFRSVUUpXVtjKDEsMTApXQpgYGAKCkxldOKAmXMgdGFrZSBhIGxvb2sgYXQgaG93IGNvbXBhcmFibGUgdHdvIGNlbGxzIChyZXBsaWNhdGVzKSBvZiBjbHVzdGVyIDEgYXJlLiAKV2Ugd2lsbCBjb21wYXJlIHRoZSBjZWxsIHdpdGggdGhlIGhpZ2hlc3QgbGlicmFyeSBzaXplIHdpdGggdGhlIGNlbGwgdGhhdCBoYXMgCnRoZSAxMHRoIGhpZ2hlc3QgbGlicmFyeSBzaXplIHVzaW5nIE1ELXBsb3RzIChtZWFuLWRpZmZlcmVuY2UgcGxvdHMsIGFzIAppbnRyb2R1Y2VkIGJ5IApbRHVkb2l0IGV0IGFsLiAoMjAwMildKGh0dHBzOi8vd3d3LmpzdG9yLm9yZy9zdGFibGUvMjQzMDcwMzg/c2VxPTEjbWV0YWRhdGFfaW5mb190YWJfY29udGVudHMpKSwgCmFsc28gc29tZXRpbWVzIHJlZmVycmVkIHRvIGFzIE1BLXBsb3RzLgoKYGBge3J9CnRhcmdldENlbGxzIDwtIG5hbWVzKGNzW29yZGVyKGNzLCBkZWNyZWFzaW5nID0gVFJVRSldW2MoMSwxMCldKQoKTSA8LSByb3dNZWFucyhhc3NheXMoc2NlKSRjb3VudHNbLHRhcmdldENlbGxzXSkKRCA8LSBhc3NheXMoc2NlKSRjb3VudHNbLHRhcmdldENlbGxzWzJdXSAvIGFzc2F5cyhzY2UpJGNvdW50c1ssdGFyZ2V0Q2VsbHNbMV1dCnBsb3QoeCA9IGxvZyhNKSwgeSA9IGxvZzIoRCksCiAgICAgcGNoID0gMTYsIGNleD0xLzMsCiAgICAgbWFpbiA9IHBhc3RlMCgiQ2VsbCAiLCB0YXJnZXRDZWxsc1syXSwgIiB2cyBjZWxsICIsIHRhcmdldENlbGxzWzFdKSwKICAgICB4bGFiID0gIkxvZyBtZWFuIiwgeWxhYiA9ICJMb2cyIGZvbGQtY2hhbmdlIiwKICAgICBidHkgPSAnbCcpCmFibGluZShoID0gMCwgY29sPSJvcmFuZ2UiLCBsd2Q9MikKCmBgYAoKV2Ugc2VlIGNsZWFyIGJpYXMgaW4gdGhlIGNvbXBhcmlzb24gb2YgdGhlIDFzdCBhbmQgMTB0aCBtb3N0IGRlZXBseSBzZXF1ZW5jZWQgCmNlbGwgZnJvbSBjZWxsIGNsdXN0ZXIgMS4gV2Ugc2VlIHRoYXQgdGhlIGxvZyBmb2xkLWNoYW5nZXMgYXJlIGJpYXNlZCBkb3dud2FyZHMuIApUaGlzIG1lYW5zIHRoYXQsIG9uIGF2ZXJhZ2UsIGEgZ2VuZSBpcyBoaWdoZXIgZXhwcmVzc2VkIGluIGNlbGwgMSB2ZXJzdXMgY2VsbCAxMC4KTG9va2luZyBhdCB0aGUgbGlicmFyeSBzaXplcywgd2UgY2FuIGluZGVlZCBzZWUgdGhhdCB0aGUgbGlicmFyeSBzaXplIGZvciAKY2VsbCAxIGlzIDIwODY5IGNvdW50cywgd2hpbGUgaXQgaXMgb25seSAxNDcxOSBjb3VudHMgZm9yIGNlbGwgMTAhIFRoaXMgaXMgYQpjbGVhciBsaWJyYXJ5IHNpemUgZWZmZWN0IHRoYXQgd2Ugc2hvdWxkIHRha2UgaW50byBhY2NvdW50LgoKYGBge3J9CiMgbm9ybWFsaXplIHRoZSBjb3VudCBkYXRhIHVzaW5nIHRoZSBwcmV2aW91c2x5IGNvbXB1dGVkICJzaXplIGZhY3RvcnMiCmFzc2F5KHNjZSwgIm5vcm1lZCIpIDwtIG5vcm1hbGl6ZUNvdW50cyhzY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZS5mYWN0b3JzPXNjZSRzaXplRmFjdG9yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzZXVkby5jb3VudD0wKQoKTSA8LSByb3dNZWFucyhhc3NheXMoc2NlKSRub3JtZWRbLHRhcmdldENlbGxzXSkKRCA8LSBhc3NheXMoc2NlKSRub3JtZWRbLHRhcmdldENlbGxzWzJdXSAvIGFzc2F5cyhzY2UpJG5vcm1lZFssdGFyZ2V0Q2VsbHNbMV1dCnBsb3QoeCA9IGxvZyhNKSwgeSA9IGxvZzIoRCksCiAgICAgcGNoID0gMTYsIGNleD0xLzMsCiAgICAgbWFpbiA9IHBhc3RlMCgiQ2VsbCAiLCB0YXJnZXRDZWxsc1syXSwgIiB2cyBjZWxsICIsIHRhcmdldENlbGxzWzFdKSwKICAgICB4bGFiID0gIkxvZyBtZWFuIiwgeWxhYiA9ICJMb2cyIGZvbGQtY2hhbmdlIiwKICAgICBidHkgPSAnbCcpCmFibGluZShoID0gMCwgY29sPSJvcmFuZ2UiLCBsd2Q9MikKYGBgCgpVcG9uIG5vcm1hbGl6aW5nIHRoZSBkYXRhIHVzaW5nIHNpemUgZmFjdG9ycywgd2UgaGF2ZSByZW1vdmVkIGJpYXMgYXMgYQpjb25zZXF1ZW5jZSBvZiBkaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoLgoKTm90ZSB0aGF0IHdlIGNvbXB1dGVkIHRoZSBub3JtYWxpemVkIGNvdW50IG1hdHJpeCBvbmx5IGZvciBkZW1vbnN0cmF0aW5nIHRoZQplZmZlY3Qgb2Ygc3VjaCBub3JtYWxpemF0aW9uLiBJbiBhIHR5cGljYWwgd29ya2Zsb3csIHRoZSBzaXplIGZhY3RvcnMgYXJlIHVzZWQKYXMgb2Zmc2V0cyBpbiB0aGUgZG93bnN0cmVhbSBtb2RlbHMgcmF0aGVyIHRoYW4gdG8gcGVyZm9ybSBkYXRhIHRyYW5zZm9ybWF0aW9uLgpJbiBicmllZiwgc3VjaCB0cmFuc2Zvcm1hdGlvbiB3b3VsZCBkaXN0b3J0IHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcAppbiB0aGUgZGF0YS4gRm9yIG1vcmUgZGV0YWlscywgd2UgcmVmZXIgdG8gdGhlIGRvY3VtZW50IHRoYXQgd2FzIHRvdWNoZWQgdXBvbgppbiB0aGUgZmlyc3QgdGhlb3J5IHNlc3Npb24KKFtsaW5rXShodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vU0dBMjEvc2VxdWVuY2luZ19zY2FsaW5nTm9ybWFsaXphdGlvbi5odG1sKSkKCiMgRmVhdHVyZSBzZWxlY3Rpb24KCkFzIGRpbWVuc2lvbnMgaW5jcmVhc2UsIHNob3J0ZXN0IGFuZCBmYXJ0aGVzdCBkaXN0YW5jZXMgYmV0d2VlbiBwb2ludHMgYmVjb21lCm5lYXJseSBpbnNlcGFyYWJsZS4gSW4gaGlnaC1kaW1lbnNpb25hbCBzcGFjZSwgaXQgaXMgdGhlcmVmb3JlIGV4dHJlbWVseSAKZGlmZmljdWx0IHRvIHNlcGFyYXRlIHNpZ25hbCBmcm9tIG5vaXNlLgoKSW4gb3JkZXIgdG8gcmVjb3ZlciBzdHJ1Y3R1cmUgKGUuZy4gc2V0dGluZyB1cCBhIGRpbWVuc2lvbi1yZWR1Y2VkCnNwYWNlIHRvIGhlbHAgdXMgZmluZCBjZWxsLXR5cGUgY2x1c3RlcnMgaW4gdGhlIGRhdGEpLCB3ZSB3YW50IHRvIG1vdmUgdG8gYW4KaW5mb3JtYXRpdmUsIGxvd2VyLWRpbWVuc2lvbmFsIHNwYWNlLiBXZSB3aWxsIHNlbGVjdCBnZW5lcyB3aGljaCB3ZSBob3BlIGFyZQppbmZvcm1hdGl2ZSBmb3IgcmVjb3ZlcmluZyB0aGUgYmlvbG9naWNhbCBzdHJ1Y3R1cmUuIEJ1dCB3aGF0IGRlZmluZXMgYW4gCmluZm9ybWF0aXZlIGdlbmU/CgojIyBTZWxlY3RpbmcgZ2VuZXMgd2l0aCBoaWdoIHZhcmlhbmNlCgpUaGUgc2ltcGxlc3QgYXBwcm9hY2ggdG8gcXVhbnRpZnlpbmcgcGVyLWdlbmUgdmFyaWF0aW9uIGlzIHRvIGNvbXB1dGUgdGhlIAp2YXJpYW5jZSBvZiB0aGUgbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgKGkuZS4sICJsb2ctY291bnRzIikgZm9yIAplYWNoIGdlbmUgYWNyb3NzIGFsbCBjZWxscy4gCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBjYWxjdWxhdGUgdmFyaWFuY2Ugb2YgbG9nLW5vcm1hbGl6ZWQgY291bnRzIGZvciBlYWNoIGdlbmUKZ2VuZVZhcnMgPC0gZ2VuZWZpbHRlcjo6cm93VmFycyhhc3NheXMoc2NlKSRsb2djb3VudHMpCiMgc2VsZWN0IHRvcCAxMDAwIGhpZ2hseSB2YXJpYWJsZSBnZW5lcwpoaWdoVmFyR2VuZXMgPC0gbmFtZXMoZ2VuZVZhcnMpW29yZGVyKGdlbmVWYXJzLCBkZWNyZWFzaW5nPVRSVUUpWzE6MWUzXV0KaGVhZChoaWdoVmFyR2VuZXMpCmBgYAoKIyMgU2VsZWN0aW5nIGdlbmVzIHdpdGggaGlnaCB2YXJpYXRpb24gd2l0aCByZXNwZWN0IHRvIG1lYW4KCldoaWxlIGNhbGN1bGF0aW9uIG9mIHRoZSBwZXItZ2VuZSB2YXJpYW5jZSBpcyBzaW1wbGUsIGZlYXR1cmUgc2VsZWN0aW9uIHJlcXVpcmVzCm1vZGVsbGluZyBvZiB0aGUgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAuIEluZGVlZCwgbm90IGFjY291bnRpbmcgZm9yIHRoZSAKbWVhbi12YXJpYW5jZSBzdHJ1Y3R1cmUgd2hpbGUgc2VsZWN0aW5nIGhpZ2hseSB2YXJpYWJsZSBnZW5lcyB3aWxsIG9mdGVudGltZXMgCmJvaWwgZG93biB0byBzZWxlY3RpbmcgdGhlIG1vc3QgaGlnaGx5IGV4cHJlc3NlZCBnZW5lcy4gVGhlIGxvZy10cmFuc2Zvcm1hdGlvbiAKaXMgYSBoZWxwZnVsIHZhcmlhbmNlIHN0YWJpbGl6aW5nIHRyYW5zZm9ybWF0aW9uLCBob3dldmVyLCBpdCBpcyBub3QgcGVyZmVjdCwgCm1lYW5pbmcgdGhhdCB0aGUgdmFyaWFuY2Ugb2YgYSBnZW5lIGlzIG5vdCBjb21wbGV0ZWx5IGluZGVwZW5kZW50IG9mIGl0cyBtZWFuLgpUaGVyZWZvcmUsIGZlYXR1cmUgc2VsZWN0aW9uIG1heSBzdGlsbCBiZSBkcml2ZW4gYnkgYXZlcmFnZSBleHByZXNzaW9uIHJhdGhlciAKdGhhbiB1bmRlcmx5aW5nIGJpb2xvZ2ljYWwgaGV0ZXJvZ2VuZWl0eS4KCkEgKHJpZ2h0bHkgc28pIHBvcHVsYXIgYXBwcm9hY2ggaXMgdG8gc2VsZWN0IGdlbmVzIHRoYXQgaGF2ZSBhIGhpZ2ggdmFyaWFuY2UKd2l0aCByZXNwZWN0IHRvIHRoZWlyIG1lYW4uIE9mdGVuLCBmaXJzdCBhbiBlbXBpcmljYWwgbWVhbi12YXJpYW5jZSB0cmVuZCBpcwpmaXR0ZWQsIHVwb24gd2hpY2ggZ2VuZXMgd2l0aCB0aGUgaGlnaGVzdCBwb3NpdGl2ZSByZXNpZHVhbHMgYXJlIHNlbGVjdGVkLiAKQmVpbmcgaW50dWl0aXZlLCByZWFzb25hYmxlIGFuZCBmYWlybHkgc3RyYWlnaHQtZm9yd2FyZCwgdGhpcyBtZXRob2QgaXMgd2lkZWx5CnVzZWQuCgpUbyBhY2NvdW50IGZvciB0aGUgbWVhbi12YXJpYW5jZSBlZmZlY3QsIHdlIHVzZSB0aGUgYG1vZGVsR2VuZVZhcmAgZnVuY3Rpb24gb2YKdGhlIGBzY3JhbmAgcGFja2FnZSB0byBmaXQgYSB0cmVuZCB0byB0aGUgdmFyaWFuY2Ugd2l0aCByZXNwZWN0IHRvIGFidW5kYW5jZQphY3Jvc3MgYWxsIGdlbmVzIChvbiBsb2ctbm9ybWFsaXplZCBleHByZXNzaW9uIHZhbHVlcyBvZiB0aGUgc2NlIG9iamVjdCkuCgpgYGB7cn0KbGlicmFyeShzY3JhbikKZGVjIDwtIG1vZGVsR2VuZVZhcihzY2UpCmhlYWQoZGVjKQpgYGAKClRoZSBmaXR0ZWQgdmFsdWUgZm9yIGVhY2ggZ2VuZSBpcyB1c2VkIGFzIGEgcHJveHkgZm9yIHRoZSB0ZWNobmljYWwgY29tcG9uZW50IG9mCnZhcmlhdGlvbiBmb3IgZWFjaCBnZW5lLCB1bmRlciB0aGUgYXNzdW1wdGlvbiB0aGF0IG1vc3QgZ2VuZXMgZXhoaWJpdCBhIGxvdwpiYXNlbGluZSBsZXZlbCBvZiB2YXJpYXRpb24gdGhhdCBpcyBub3QgYmlvbG9naWNhbGx5IGludGVyZXN0aW5nLiBUaGUgYmlvbG9naWNhbApjb21wb25lbnQgb2YgdmFyaWF0aW9uIGZvciBlYWNoIGdlbmUgaXMgZGVmaW5lZCBhcyB0aGUgdGhlIHJlc2lkdWFsIGZyb20gdGhlCnRyZW5kLiBSYW5raW5nIGdlbmVzIGJ5IHRoZSBiaW9sb2dpY2FsIGNvbXBvbmVudCBlbmFibGVzIGlkZW50aWZpY2F0aW9uIG9mCmludGVyZXN0aW5nIGdlbmVzIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzIGluIGEgbWFubmVyIHRoYXQgYWNjb3VudHMgZm9yIHRoZQptZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4KCmBgYHtyfQpmaXRSZXRpbmEgPC0gbWV0YWRhdGEoZGVjKQpwbG90KGZpdFJldGluYSRtZWFuLCBmaXRSZXRpbmEkdmFyLCAKICAgICB4bGFiPSJNZWFuIG9mIGxvZy1leHByZXNzaW9uIiwKICAgIHlsYWI9IlZhcmlhbmNlIG9mIGxvZy1leHByZXNzaW9uIikKY3VydmUoZml0UmV0aW5hJHRyZW5kKHgpLCBjb2w9ImRvZGdlcmJsdWUiLCBhZGQ9VFJVRSwgbHdkPTIpCmBgYAoKV2UgYXJlIGludGVyZXN0ZWQgaW4gdGhvc2UgZ2VuZXMgZm9yIHdoaWNoIHRoZSB2YXJpYW5jZSBpbiBleHByZXNzaW9uIGlzIGhpZ2hlcgp0aGFuIHdoYXQgd2Ugd291bGQgZXhwZWN0IGZvciB0aGF0IGdlbmUgYmFzZWQgb24gaXRzIG1lYW4gZXhwcmVzc2lvbi4KCmBgYHtyfQojIGdldCAxMCUgbW9zdCB2YXJpYWJsZSBnZW5lcwpodmcgPC0gZ2V0VG9wSFZHcyhkZWMsIAogICAgICAgICAgICAgICAgICBwcm9wPTAuMSkKaGVhZChodmcpCgojIHBsb3QgdGhlc2UgCnBsb3QoZml0UmV0aW5hJG1lYW4sIGZpdFJldGluYSR2YXIsIAogICAgIGNvbCA9IGMoIm9yYW5nZSIsICJkYXJrc2VhZ3JlZW4zIilbKG5hbWVzKGZpdFJldGluYSRtZWFuKSAlaW4lIGh2ZykrMV0sCiAgICAgeGxhYj0iTWVhbiBvZiBsb2ctZXhwcmVzc2lvbiIsCiAgICB5bGFiPSJWYXJpYW5jZSBvZiBsb2ctZXhwcmVzc2lvbiIpCmN1cnZlKGZpdFJldGluYSR0cmVuZCh4KSwgY29sPSJkb2RnZXJibHVlIiwgYWRkPVRSVUUsIGx3ZD0yKQpsZWdlbmQoInRvcGxlZnQiLCAKICAgICAgIGxlZ2VuZCA9IGMoIlNlbGVjdGVkIiwgIk5vdCBzZWxlY3RlZCIpLCAKICAgICAgIGNvbCA9IGMoImRhcmtzZWFncmVlbjMiLCAib3JhbmdlIiksCiAgICAgICBwY2ggPSAxNiwKICAgICAgIGJ0eT0nbicpCmBgYAoKQXMgYSBjb21wYXJpc29uLCB3ZSBjb3VsZCBjb2xvciB0aGUgZ2VuZXMgb24gdGhpcyBmaWd1cmUgYWNjb3JkaW5nIHRvIHRoZSAKc2VsZWN0aW9uIHRoYXQgd2FzIG1hZGUgcHVyZWx5IGJ5IGxvb2tpbmcgYXQgdGhlIHJhdyB2YXJpYW5jZSBvZiBlYWNoIGdlbmUuCgpgYGB7ciwgZXZhbD1GQUxTRX0KcGxvdChmaXRSZXRpbmEkbWVhbiwgZml0UmV0aW5hJHZhciwgCiAgICAgY29sID0gYygib3JhbmdlIiwgInN0ZWVsYmx1ZSIpWyhuYW1lcyhmaXRSZXRpbmEkbWVhbikgJWluJSBoaWdoVmFyR2VuZXMpKzFdLAogICAgIHhsYWI9Ik1lYW4gb2YgbG9nLWV4cHJlc3Npb24iLAogICAgeWxhYj0iVmFyaWFuY2Ugb2YgbG9nLWV4cHJlc3Npb24iKQpjdXJ2ZShmaXRSZXRpbmEkdHJlbmQoeCksIGNvbD0iZG9kZ2VyYmx1ZSIsIGFkZD1UUlVFLCBsd2Q9MikKbGVnZW5kKCJ0b3BsZWZ0IiwgCiAgICAgICBsZWdlbmQgPSBjKCJTZWxlY3RlZCIsICJOb3Qgc2VsZWN0ZWQiKSwgCiAgICAgICBjb2wgPSBjKCJzdGVlbGJsdWUiLCAib3JhbmdlIiksCiAgICAgICBwY2ggPSAxNiwKICAgICAgIGJ0eT0nbicpCmBgYAoKQXMgZXhwZWN0ZWQsIHdlIGhlcmUgc2ltcGx5IHNlbGVjdCBnZW5lcyB3aXRoIGEgaGlnaCB2YXJpYW5jZSwgd2l0aG91dCAKcmVjb2duaXppbmcgdGhhdCBhIGhpZ2ggdmFyaWFuY2UgaXMgdHlwaWNhbGx5IGRyaXZlbiBieSBhIGhpZ2ggbWVhbi4KCiMjIEhpZ2ggZGV2aWFuY2UgZ2VuZXMKCkhlcmUsIHdlIHdpbGwgc2VsZWN0IGdlbmVzIHdpdGggYSBoaWdoIHJlc2lkdWFsIGRldmlhbmNlLiBUaGUgaWRlYSBpcyB0aGF0IHdlIAphc3N1bWUgYSBudWxsIG1vZGVsIG9mIGNvbnN0YW50IGV4cHJlc3Npb24gZnJhY3Rpb24gKGkuZS4sIFJOQSBjb25jZW50cmF0aW9uKSAKYWNyb3NzIGFsbCBjZWxscy4gV2Ugc3Vic2VxdWVudGx5IGNhbGN1bGF0ZSBhIGdvb2RuZXNzLW9mLWZpdCBzdGF0aXN0aWMgZm9yIAplYWNoIGdlbmUsIGFzc2Vzc2luZyB3aGV0aGVyIHRoZSBtb2RlbCBpcyBhIGdvb2QgYXBwcm94aW1hdGlvbiB0byB0aGUgZ2VuZSAKZXhwcmVzc2lvbiBvZiB0aGUgY29ycmVzcG9uZGluZyBnZW5lLgoKSWYgdGhlIG1vZGVsIGZpdHMgcG9vcmx5LCBpLmUuLCB0aGUgZ2VuZSBoYXMgYSBoaWdoIGRldmlhbmNlLCB0aGUgZXhwcmVzc2lvbiAKZnJhY3Rpb24gdmFyaWVzIHNpZ25pZmljYW50bHkgYWNyb3NzIHRoZSBjZWxscyBpbiBvdXIgZGF0YXNldHMuR2VuZXMgd2l0aCBhIApoaWdoIGRldmlhbmNlIHdpbGwgdGh1cyBtb3N0IHBvb3JseSBmaXQgYSBudWxsIG1vZGVsIHdoZXJlIHRoZSByZWxhdGl2ZQphYnVuZGFuY2UgaXMgZXF1YWwgZm9yIGFsbCBjZWxscywgd2hpY2ggdGhlcmVmb3JlIGFyZSBpbmZvcm1hdGl2ZS4KCmBgYHtyfQojQmlvY01hbmFnZXI6Omluc3RhbGwoInNjcnkiKQpsaWJyYXJ5KHNjcnkpCnNjZSA8LSBkZXZpYW5jZUZlYXR1cmVTZWxlY3Rpb24ob2JqZWN0ID0gc2NlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJjb3VudHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3J0ZWQgPSBGQUxTRSkKCnBsb3Qoc29ydChyb3dEYXRhKHNjZSkkYmlub21pYWxfZGV2aWFuY2UsIGRlY3JlYXNpbmcgPSBUUlVFKSwgCiAgICAgdHlwZT0ibCIsIAogICAgIHhsYWI9InJhbmtlZCBnZW5lcyIsIAogICAgIHlsYWI9ImJpbm9taWFsIGRldmlhbmNlIiwgCiAgICAgbWFpbj0iRmVhdHVyZSBTZWxlY3Rpb24gd2l0aCBEZXZpYW5jZSIpCmFibGluZSh2PTIwMDAsIGx0eT0yLCBjb2w9InJlZCIpCmBgYAoKT3VyIHBsb3QgbG9va3Mgc2ltaWxhciB0byBvbmUgZGlzcGxheWVkIGluIHRoZSAKW3ZpZ25ldHRlIG9mIHRoZSBzY3J5IHBhY2thZ2VdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9zY3J5L2luc3QvZG9jL3NjcnkuaHRtbCkuIApCYXNlZCBvbiB0aGF0IHBsb3QsIHRoZSBhdXRob3JzIHN1Z2dlc3QgcmV0YWluaW5nIDIuMDAwIGdlbmVzICh0aGUgdG9wIDIwMDAgCmJhc2VkIG9uIHRoZSBkZXZpYW5jZSByZXNpZHVhbHMpIGZvciBkb3duc3RyZWFtIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBhbmQgCmNsdXN0ZXJpbmcuCgojIyBTZXVyYXQgVlNUCgpBbm90aGVyIHZlcnkgY29tbW9uIGZlYXR1cmUgc2VsZWN0aW9uIHN0cmF0ZWd5IGlzIHRoZSB2YXJpYW5jZS1zdGFiaWxpemluZwp0cmFuc2Zvcm1hdGlvbiBmcm9tIHRoZSBgU2V1cmF0YCBSIHBhY2thZ2UsIHdoaWNoIGFtb3VudHMgdG8gY2FsY3VsYXRpbmcgUGVhcnNvbgpyZXNpZHVhbHMgZnJvbSBhIHJlZ3VsYXJpemVkIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwsIHdpdGggc2VxdWVuY2luZyAKZGVwdGggYXMgYSBjb3ZhcmlhdGUuCgoqKkludGVybWV6em86IGludGVyb3BlcmFiaWxpdHkgYmV0d2VlbiBTaW5nbGVDZWxsRXhwZXJpbWVudCBhbmQgU2V1cmF0IG9iamVjdHMqKgoKSW4gdGhpcyBsZWN0dXJlIHNlcmllcywgd2UgYWx3YXlzIG1ha2UgdXNlIG9mIHRoZSBTaW5nbGVDZWxsRXhwZXJpbWVudCBvYmplY3QKYW5kIHRoZSBwYWNrYWdlcyBhdmFpbGFibGUgZnJvbSBCaW9jb25kdWN0b3IuIEFub3RoZXIgdmVyeSBwb3B1bGFyIHRvb2xib3gKZm9yIHBlcmZvcm1pbmcgc2NSTkEtc2VxIGRhdGEgYW5hbHlzaXMgaXMgYFNldXJhdGAuIEhvd2V2ZXIsIGZ1bmN0aW9ucyBmcm9tCmBTZXVyYXRgIGNhbm5vdCBiZSB1c2VkIGRpcmVjdGx5IHRvIG1hbmlwdWxhdGUgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3RzLAphbmQgdmljZSB2ZXJzYS4gRm9ydHVuYXRlbHksIGVmZm9ydHMgaGF2ZSBiZWVuIG1hZGUgdG8gaW5jcmVhc2UgdGhlCmludGVyb3BlcmFiaWxpdHkgYmV0d2VlbiB0aGUgdHdvIHRvb2xib3hlcy4KCmBgYHtyfQpsaWJyYXJ5KFNldXJhdCkKc2V1cmF0X29iaiA8LSBhcy5TZXVyYXQoc2NlKQpzZXVyYXRfb2JqICMgbm90aWNlIHRoZSAiMCB2YXJpYWJsZSBmZWF0dXJlcyIKYGBgCgpPbiB0aGlzIG9iamVjdCwgd2UgbWF5IHVzZSBmdW5jdGlvbnMgZnJvbSB0aGUgYFNldXJhdGAgdG9vbGJveC4KRm9yIGluc3RhbmNlLCB3ZSBtYXkgc2VhcmNoIGZvciBoaWdobHkgdmFyaWFibGUgZmVhdHVyZXMgdXNpbmcgYFNldXJhdGAncyBWU1QKaW1wbGVtZW50YXRpb246CgpgYGB7cn0Kc2V1cmF0X29iaiA8LSBTZXVyYXQ6Ok5vcm1hbGl6ZURhdGEoc2V1cmF0X29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS5mYWN0b3IgPSAxMDAwMCkKCnNldXJhdF9vYmogPC0gIEZpbmRWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IikKYGBgCgpgYGB7cn0Kc2V1cmF0X29iaiAgIyBub3RpY2UgdGhlICIyMDAwIHZhcmlhYmxlIGZlYXR1cmVzIiAoZGVmYXVsdCkKaGVhZChWYXJpYWJsZUZlYXR1cmVzKHNldXJhdF9vYmopKSAjIGhlcmUgdGhleSBhcmUKYGBgCgojIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbgoKTm90ZSB0aGF0LCBiZWxvdywgd2UgY29sb3IgdGhlIGNlbGxzIHVzaW5nIHRoZSBrbm93biwgdHJ1ZSBjZWxsIHR5cGUgbGFiZWwgYXMgCmRlZmluZWQgaW4gdGhlIG1ldGFkYXRhLCB0byBlbXBpcmljYWxseSBldmFsdWF0ZSB0aGUgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLiAKSW4gcmVhbGl0eSwgd2UgZG9uJ3Qga25vdyB0aGlzIHlldCBhdCB0aGlzIHN0YWdlLgoKIyMgVGhlIG1vc3QgYmFzaWMgRFIKCkp1c3QgYnkgbG9va2luZyBhdCB0aGUgdG9wIHR3byBnZW5lcyBiYXNlZCBvbiBvdXIgZmVhdHVyZSBzZWxlY3Rpb24gY3JpdGVyaW9uLCAKd2UgY2FuIGFscmVhZHkgc2VlIHNvbWUgc2VwYXJhdGlvbiBhY2NvcmRpbmcgdG8gdGhlIGNlbGwgdHlwZSEKCmBgYHtyfQpjb2xEYXRhKHNjZSkkY2x1c3RlciA8LSBhcy5mYWN0b3IoY29sRGF0YShzY2UpJGNsdXN0ZXIpCmNsIDwtIGNvbERhdGEoc2NlKSRjbHVzdGVyCnBhcihidHk9J2wnKQpwbG90KHggPSBhc3NheXMoc2NlKSRjb3VudHNbaHZnWzFdLF0sCiAgICAgeSA9IGFzc2F5cyhzY2UpJGNvdW50c1todmdbMl0sXSwKICAgICBjb2wgPSBhcy5udW1lcmljKGNsKSwKICAgICBwY2ggPSAxNiwgY2V4ID0gMS8zLAogICAgIHhsYWIgPSAiTW9zdCBpbmZvcm1hdGl2ZSBnZW5lIiwKICAgICB5bGFiID0gIlNlY29uZCBtb3N0IGluZm9ybWF0aXZlIGdlbmUiLAogICAgIG1haW4gPSAiQ2VsbHMgY29sb3JlZCBhY2MgdG8gY2VsbCB0eXBlIikKYGBgCgpXZSBhcmUgYWJsZSB0byByZWNvdmVyIHF1aXRlIHNvbWUgc3RydWN0dXJlLiBIb3dldmVyLCBtYW55IGNlbGwgcG9wdWxhdGlvbnMgCnJlbWFpbiBvYnNjdXJlLCBhbmQgdGhlIHBsb3QgaXMgb3ZlcmNyb3dkZWQuCgojIyBMaW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uOiBQQ0EKCkEgRFIgbWV0aG9kIGlzIGxpbmVhciB3aGVuIHRoZSByZWR1Y2VkIGRpbWVuc2lvbnMgYXJlIGEgbGluZWFyIGZ1bmN0aW9uIG9mIHRoZQpvcmlnaW5hbCB2YXJpYWJsZXMuIEZvciBleGFtcGxlLCBpbiBQQ0EsIGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCBpcyBhIGxpbmVhcgpjb21iaW5hdGlvbiBvZiBnZW5lcywgdGhlcmVmb3JlIHRoZSBEUiBpcyBhIGxpbmVhciBmdW5jdGlvbiBvZiB0aGUgb3JpZ2luYWwKdmFyaWFibGVzLgoKVHlwaWNhbGx5LCBQQ0EgaXMgcGVyZm9ybWVkIG9uIGxvZy10cmFuc2Zvcm1lZCBub3JtYWxpemVkIGNvdW50cy4gClRoZSBsb2ctdHJhbnNmb3JtYXRpb24gaGVscHMgIHNvbWV3aGF0LCBidXQgbm90IGNvbXBsZXRlbHksIHRvIGFjY291bnQgZm9yIHRoZQptZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4gUENBIHdvcmtzIHdlbGwgZm9yIGJ1bGsgUk5BLXNlcSBkYXRhLiBIb3dldmVyLCB0aGUKc3RydWN0dXJlIG9mIHNjUk5BLXNlcSBkYXRhIGlzIG9mdGVuIHRvbyBjb21wbGV4IHRvIGJlIHZpc3VhbGl6ZWQgYnkgYSBzbWFsbCAKbnVtYmVyIG9mIFBDcy4KClRoZXJlIGFyZSBzZXZlcmFsIFIgZnVuY3Rpb25zIHRoYXQgYWxsb3cgeW91IHRvIHBlcmZvcm0gUENBLiBIZXJlLCB3ZSBtYWtlIHVzZQpvZiB0aGUgYHJ1blBDQWAgZnVuY3Rpb24gb2YgdGhlIGBzY2F0ZXJgIHBhY2thZ2UsIHdoaWNoIGhhcyBiZWVuIHNwZWNpZmljYWxseQpkZXZlbG9wZWQgZm9yIHBlcmZvcm1pbmcgUENBIG9uIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0cy4KV2UgY2FsY3VsYXRlIHRoZSB0b3AgMzAgcHJpbmNpcGFsIGNvbXBvbmVudHMuCgojIyMgUENBIHdpdGggZmVhdHVyZSBzZWxlY3Rpb24KCmBgYHtyfQpzZXQuc2VlZCgxMjM0KQpzY2UgPC0gcnVuUENBKHNjZSwgCiAgICAgICAgICAgICAgbmNvbXBvbmVudHM9MzAsIAogICAgICAgICAgICAgIHN1YnNldF9yb3c9aHZnKQpgYGAKClBDQSBoYXMgYmVlbiBwZXJmb3JtZWQuIFRoZSBQQ0EgaW5mb3JtYXRpb24gaGFzIGJlZW4gYXV0b21hdGljYWxseSBzdG9yZWQgaW4gdGhlCipyZWR1Y2VkRGltKiBzbG90IG9mIHRoZSBTaW5nbGVDZWxsRXhwZXJpbWVudCBvYmplY3QuCgpgYGB7cn0KcmVkdWNlZERpbU5hbWVzKHNjZSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KaGVhZChyZWR1Y2VkRGltKHNjZSwKICAgICAgICAgICB0eXBlPSJQQ0EiKSkKYGBgCgpUaGUgYHBsb3RQQ0FgIGZ1bmN0aW9uIG9mIHRoZSBgc2NhdGVyYCBwYWNrYWdlIG5vdyBhbGxvd3MgdXMgdG8gdmlzdWFsaXplCnRoZSBjZWxscyBpbiBQQ0Egc3BhY2UsIGJhc2VkIG9uIHRoZSBQQ0EgaW5mb3JtYXRpb24gc3RvcmVkIGluIG91ciBvYmplY3Q6CgpgYGB7ciwgZXZhbD1GQUxTRX0KcGxvdFBDQShzY2UsIAogICAgICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikKYGBgCgpXaGlsZSB0aGUgbGFyZ2UgbnVtYmVyIG9mIGNsdXN0ZXJzIGluIHRoaXMgZGF0YXNldCBtYWtlcyBpdCBoYXJkIHRvIApkaXN0aW5ndWlzaCBiZXR3ZWVuIGFsbCB0aGUgZGlmZmVyZW50IGNvbG9ycywgd2UgY2FuIGFscmVhZHkgc2VlIHRoYXQgUENBCnJldHJpZXZlcyBzb21lLCBidXQgbm90IGFsbCwgb2YgdGhlIHN0cnVjdHVyZSBpbiB0aGUgZGF0YSB0aGF0IHdhcyBkaXNjb3ZlcmVkCmJ5IHRoZSBvcmlnaW5hbCBhdXRob3JzLgoKSG93IG1hbnkgb2YgdGhlIHRvcCBQQ3Mgc2hvdWxkIHdlIHJldGFpbiBmb3IgZG93bnN0cmVhbSBhbmFseXNlcz8gVGhlIGNob2ljZSBvZgp0aGUgbnVtYmVyIG9mIFBDcyBpcyBhIGRlY2lzaW9uIHRoYXQgaXMgYW5hbG9nb3VzIHRvIHRoZSBjaG9pY2Ugb2YgdGhlIG51bWJlciBvZgpIVkdzIHRvIHVzZS4gVXNpbmcgbW9yZSBQQ3Mgd2lsbCByZXRhaW4gbW9yZSBiaW9sb2dpY2FsIHNpZ25hbCBhdCB0aGUgY29zdCBvZgppbmNsdWRpbmcgbW9yZSBub2lzZSB0aGF0IG1pZ2h0IG1hc2sgc2FpZCBzaWduYWwuIE9uIHRoZSBvdGhlciBoYW5kLCB1c2luZyBmZXdlcgpQQ3Mgd2lsbCBpbnRyb2R1Y2UgY29tcGV0aXRpb24gYmV0d2VlbiBkaWZmZXJlbnQgZmFjdG9ycyBvZiB2YXJpYXRpb24sIHdoZXJlCndlYWtlciAoYnV0IHN0aWxsIGludGVyZXN0aW5nKSBmYWN0b3JzIG1heSBiZSBwdXNoZWQgZG93biBpbnRvIGxvd2VyIFBDcyBhbmQKaW5hZHZlcnRlbnRseSBkaXNjYXJkZWQgZnJvbSBkb3duc3RyZWFtIGFuYWx5c2VzLgoKTW9zdCBhbmFseXN0cyB3aWxsIHNpbXBseSBhaW0gdG8gdXNlIGEg4oCccmVhc29uYWJsZeKAnSBidXQgYXJiaXRyYXJ5IHZhbHVlLAp0eXBpY2FsbHkgcmFuZ2luZyBmcm9tIDEwIHRvIDUwLiBUaGlzIGlzIG9mdGVuIHNhdGlzZmFjdG9yeSBhcyB0aGUgbGF0ZXIgUENzCmV4cGxhaW4gc28gbGl0dGxlIHZhcmlhbmNlIHRoYXQgdGhlaXIgaW5jbHVzaW9uIG9yIG9taXNzaW9uIGhhcyBubyBtYWpvciBlZmZlY3QuCgpgYGB7cn0KcGVyY2VudC52YXIgPC0gYXR0cihyZWR1Y2VkRGltKHNjZSksICJwZXJjZW50VmFyIikKcGxvdChwZXJjZW50LnZhciwgbG9nPSJ5IiwgeGxhYj0iUEMiLCB5bGFiPSJWYXJpYW5jZSBleHBsYWluZWQgKCUpIikKcGxvdChjdW1zdW0ocGVyY2VudC52YXIpLCB4bGFiPSJQQyIsIHlsYWI9IkN1bXVsYXRpdmUgdmFyaWFuY2UgZXhwbGFpbmVkICglKSIpCmBgYAoKSGVyZSwgcmV0YWluaW5nIMKxMTVQQ3Mgc2VlbXMgcmVhc29uYWJsZS4gSWYgeW91IHJlYWxseSBwcmVmZXIgYSBtb3JlIGRhdGEtZHJpdmVuCndheSBmb3IgZGV0ZXJtaW5pbmcgdGhpcywgdGhlcmUgYXJlIHByb2NlZHVyZXMgYXZhaWxhYmxlIHRoYXQgYWltIHRvIApjb21wdXRhdGlvbmFsbHkgaWRlbnRpZnkgdGhlIGVsYm93L2tuZWUgcG9pbnQgaW4gdGhlIHZhcmlhbmNlIGV4cGxhaW5lZCBwZXIgClBDIHBsb3QuCgpgYGB7cn0KbGlicmFyeShQQ0F0b29scykKY2hvc2VuLmVsYm93IDwtIGZpbmRFbGJvd1BvaW50KHBlcmNlbnQudmFyKQpjaG9zZW4uZWxib3cKYGBgCgojIyMgUENBIHdpdGhvdXQgZmVhdHVyZSBzZWxlY3Rpb24KCk5vdGU6IG1vcmUgZmVhdHVyZXMgLT4gY29tcHV0YXRpb25hbGx5IG1vcmUgaW50ZW5zaXZlIQoKYGBge3IsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDEyMzQpCnNjZU5vRlMgPC0gcnVuUENBKHNjZSwgCiAgICAgICAgICAgICAgICAgIG5jb21wb25lbnRzPTMwLCAKICAgICAgICAgICAgICAgICAgc3Vic2V0X3Jvdz0xOm5yb3coc2NlKSkKcGxvdFBDQShzY2VOb0ZTLCBjb2xvdXJfYnkgPSAiY2x1c3RlciIpCnJtKHNjZU5vRlMpCmBgYAoKV2hpbGUgd2UgdXNlIG1vcmUgaW5mb3JtYXRpb24gdG8gbWFrZSB0aGlzIFBDQSBwbG90ICgxNy44ODcgZ2VuZXMpIGFzIGNvbXBhcmVkCnRvIHRoZSBmZWF0dXJlIHNlbGVjdGVkIFBDQSBwbG90ICg2NDIgZ2VuZXMpLCB3ZSBzZWVtIHRvIHJldHJpZXZlIGxlc3Mgc3RydWN0dXJlCmluIHRoZSBkYXRhLiBUaGlzIGlzIHRoZSBwb3dlciBvZiBmZWF0dXJlIHNlbGVjdGlvbiwgYW4gaW5jcmVhc2UgaW4gdGhlCnNpZ25hbC10by1ub2lzZSByYXRpbyEKCiMjIyBFZmZlY3Qgb2YgZmVhdHVyZSBzZWxlY3Rpb24gb24gUENBCgpGaXJzdCwgd2UgY29tcGFyZSB0aGUgZGlmZmVyZW50IGZlYXR1cmUgc2VsZWN0aW9uIGNyaXRlcmlhLCB1c2luZyB0aGUgdG9wIDEwMDAKaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGZvciBlYWNoIG1ldGhvZC4KCmBgYHtyLCBldmFsPUZBTFNFfQojIEdldCB0b3AgMTAwMCBoaWdobHkgdmFyaWFibGUgZmVhdHVyZXMgdXNpbmcgdGhlIGhpZ2hseSB2YXJpYWJsZSBnZW5lcwojICh3LnIudC4gbWVhbiksIGhpZ2ggZGV2aWFuY2UgZ2VuZXMgYW5kIHRoZSBWU1Qgc3RyYXRlZ3kgb2YgU2V1cmF0CgpodmcxMDAwIDwtIGdldFRvcEhWR3MoZGVjLCBuID0gMTAwMCkKaGRnMTAwMCA8LSBuYW1lcyhzb3J0KHJvd0RhdGEoc2NlKSRiaW5vbWlhbF9kZXZpYW5jZSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjEwMDBdCnZzdDEwMDAgPC0gVmFyaWFibGVGZWF0dXJlcyhzZXVyYXRfb2JqKVsxOjEwMDBdCgojIEhWRyBzdHJhdGVneQpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmcxMDAwKSwgCiAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpICsKICBnZ3RpdGxlKCJIaWdobHkgdmFyaWFibGUgZ2VuZXMiKQoKIyBIREcgc3RyYXRlZ3kKcGxvdFBDQSgKICAgIHJ1blBDQShzY2UsCiAgICAgICAgICAgbmNvbXBvbmVudHMgPSAyLCAKICAgICAgICAgICBzdWJzZXRfcm93ID0gaGRnMTAwMCksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiSGlnaC1kZXZpYW5jZSBnZW5lcyIpCgojIFZTVCBzdHJhdGVneQpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IHZzdDEwMDApLAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiLAogICAgKSArCiAgZ2d0aXRsZSgiU2V1cmF0IFZTVCIpCmBgYAoKTmV4dCwgd2UgYXNzZXNzIHRoZSBzZW5zaXRpdml0eSBvbiB0aGUgbnVtYmVyIG9mIHRvcCBmZWF0dXJlcyBmb3IgdGhlIGhpZ2hseQp2YXJpYWJsZSBnZW5lcyBtZXRob2Q7CgpgYGB7ciwgZXZhbD1GQUxTRX0KaHZnX2FsbCA8LSBnZXRUb3BIVkdzKGRlYykKCmh2ZzIwMDAgPC0gaHZnX2FsbFsxOjIwMDBdCmh2ZzEwMDAgPC0gaHZnX2FsbFsxOjEwMDBdCmh2ZzEwMCA8LSBodmdfYWxsWzE6MTAwXQpodmcxMCA8LSBodmdfYWxsWzE6MTBdCmh2ZzUgPC0gaHZnX2FsbFsxOjVdCgpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIpLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIkFsbCBnZW5lcyIpCgpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmcyMDAwKSwgCiAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpICsKICBnZ3RpdGxlKCJUb3AgMjAwMCBnZW5lcyIpCgpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmcxMDAwKSwgCiAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpICsKICBnZ3RpdGxlKCJUb3AgMTAwMCBnZW5lcyIpCgpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmcxMDApLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIlRvcCAxMDAgZ2VuZXMiKQoKcGxvdFBDQSgKICAgIHJ1blBDQShzY2UsCiAgICAgICAgICAgbmNvbXBvbmVudHMgPSAyLCAKICAgICAgICAgICBzdWJzZXRfcm93ID0gaHZnMTApLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIlRvcCAxMCBnZW5lcyIpCgpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmc1KSwgCiAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpICsKICBnZ3RpdGxlKCJUb3AgNSBnZW5lcyIpCmBgYAoKIyMjIFJlbWFya3Mgb24gUENBCgpWaXN1YWxpemF0aW9ucyBvZiByZWR1Y2VkIGRpbWVuc2lvbnMgZnJvbSBsaW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uCm1ldGhvZHMgYXJlIG9mdGVuICJvdmVyY3Jvd2RlZCIsIGFuZCBpdCBpcyBoYXJkIHRvIHNlZSBzdHJ1Y3R1cmUgKGUuZy4sIHRoZSBQQ0EKcGxvdCB3ZSBqdXN0IHNhdykuIE5vbi1saW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIG1ldGhvZHMgY2FuIG92ZXJjb21lIHRoaXMKcHJvYmxlbS4gQXMgdGhlIG5hbWUgc3VnZ2VzdHMsIHRoZSByZWR1Y2VkIGRpbWVuc2lvbnMgYXJlIGEgbm9uLWxpbmVhciBmdW5jdGlvbgpvZiB0aGUgb2JzZXJ2ZWQgZGF0YS4gV2Ugd2lsbCBub3QgZ28gaW50byBkZXRhaWwgYXMgdG8gaG93IHRoZXNlIHdvcmsgdW5kZXIgdGhlCmhvb2QsIGJ1dCBwcm92aWRlIGEgZmV3IGd1aWRlbGluZXMgZm9yIHRoZSBtb3N0IHBvcHVsYXIgbWV0aG9kcy4gT2Z0ZW4sIHRoZSB0b3AKKH4xMC01MCkgUENzIGFyZSBwcm92aWRlZCBhcyBpbnB1dC4KCiMjIEEgZ2VuZXJhbGl6YXRpb24gb2YgUENBIGZvciBleHBvbmVudGlhbCBmYW1pbHkgZGlzdHJpYnV0aW9ucy4KClBDQSBpcyBpbXBsaWNpdGx5IGJhc2VkIG9uIEV1Y2xpZGVhbiBkaXN0YW5jZXMsIGNvcnJlc3BvbmRpbmcgdG8gbWF4aW1pemluZyBhCkdhdXNzaWFuIGxpa2VsaWhvb2QsIHdoaWNoIGlzIGluYXBwcm9wcmlhdGUgZm9yIGNvdW50IGRhdGEgc3VjaCBhcyBzY1JOQS1zZXEuCltUb3duZXMgZXQgYWwuXShodHRwczovL2RvaS5vcmcvMTAuMTE4Ni9zMTMwNTktMDE5LTE4NjEtNikgKDIwMTkpIGRldmVsb3AKR0xNLVBDQSwgYSBnZW5lcmFsaXphdGlvbiBvZiBQQ0EgZm9yIGV4cG9uZW50aWFsIGZhbWlseSBsaWtlbGlob29kcy4gVGhleSBwb3NpdCwKdXNpbmcgbmVnYXRpdmUgY29udHJvbCBkYXRhLCB0aGF0IHRoZSBkYXRhIGdlbmVyYXRpdmUgbWVjaGFuaXNtIG9mIFVNSSBjb3VudApkYXRhIGNhbiBiZSBjb25zaWRlcmVkIHRvIGJlIG11bHRpbm9taWFsLgoKVGhlIEdMTS1QQ0Egc3RyYXRlZ3kgaXMgaW1wbGVtZW50ZWQgaW4gdGhlIGBnbG1wY2FgIGZ1bmN0aW9uIG9mIHRoZSBgZ2xtcGNhYApwYWNrYWdlLgoKTm90ZSB0aGF0IHRoaXMgZnVuY3Rpb24gaXMgcXVpdGUgY29tcHV0YXRpb25hbGx5IGludGVuc2l2ZS4gRm9yIHJlZ3VsYXIgUENBIApvbiBsb2ctdHJhbnNmb3JtZWQgbm9ybWFsaXplZCBjb3VudHMsIHRoZSB1bmRlcmx5aW5nIGNvbXB1dGF0aW9ucyBjYW4gYmUgCnN0cm9uZ2x5IHNpbXBsaWZpZWQuIEhlcmUsIHdlIHdvcmsgd2l0aCB0aGUgcmF3IGNvdW50cywgd2hpY2ggd2UgYXNzdW1lIHRvIApiZSAqUG9pc3NvbiogZGlzdHJpYnV0ZWQsIHJlcXVpcmluZyBhbiBpdGVyYXRpdmUgb3B0aW1pemF0aW9uIHNjaGVtZS4KCmBgYHtyLCBldmFsPUZBTFNFfQojIHJ1bnMgMm1pbiBvbiBteSBsYXB0b3AKbGlicmFyeShnbG1wY2EpCnNldC5zZWVkKDIxMTEwMykKcG9pcGNhIDwtIGdsbXBjYShZID0gYXNzYXlzKHNjZSkkY291bnRzW2h2ZyxdLAogICAgICAgICAgICAgICAgIEwgPSAyLCAKICAgICAgICAgICAgICAgICBmYW0gPSAicG9pIiwKICAgICAgICAgICAgICAgICBtaW5pYmF0Y2ggPSAic3RvY2hhc3RpYyIpCnJlZHVjZWREaW0oc2NlLCAiUG9pUENBIikgPC0gcG9pcGNhJGZhY3RvcnMKcGxvdFJlZHVjZWREaW0oc2NlLCAKICAgICAgICAgICAgICAgZGltcmVkPSJQb2lQQ0EiLAogICAgICAgICAgICAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpCmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgY291bGQgYWRvcHQgdGhlIEdMTS1QQ0Egc3RyYXRlZ3kgdXNpbmcgb25seSB0aGUgZ2VuZXMgd2l0aCAKaGlnaCBkZXZpYW5jZS4KCmBgYHtyLCBldmFsPUZBTFNFfQojIEJhc2VkIG9uIHRoZSBkaWFnbm9zdGljIHBsb3QgZnJvbSB0aGUgZmVhdHVyZSBzZWxlY3Rpb24sIHdlIHdvdWxkIHNlbGVjdCB0aGUgCiMgdG9wIDIwMDAgZ2VuZXMgd2l0aCBoaWdoZXN0IGRldmlhbmNlLiBIb3dldmVyLCB0byByZWR1Y2UgdGhlIHJ1bm5pbmcgdGltZSBvZgojIHRoZSBmdW5jdGlvbiwgSSBoZXJlIHNlbGVjdCB0aGUgdG9wIDUwMCAod2hpY2ggc3RpbGwgaXMgcXVpdGUgc2xvdyAtIDVtaW4pLgpTeXMudGltZSgpCmhkZyA8LSBuYW1lcyhzb3J0KHJvd0RhdGEoc2NlKSRiaW5vbWlhbF9kZXZpYW5jZSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjUwMF0KCnNldC5zZWVkKDQ3MTY4MSkKcG9pcGNhX2RldiA8LSBnbG1wY2EoWSA9IGFzc2F5cyhzY2UpJGNvdW50c1toZGcsXSwKICAgICAgICAgICAgICAgICBMID0gMiwgCiAgICAgICAgICAgICAgICAgZmFtID0gInBvaSIsCiAgICAgICAgICAgICAgICAgbWluaWJhdGNoID0gInN0b2NoYXN0aWMiKQpTeXMudGltZSgpCnJlZHVjZWREaW0oc2NlLCAiUG9pUENBX2RldiIpIDwtIHBvaXBjYV9kZXYkZmFjdG9ycwpwbG90UmVkdWNlZERpbShzY2UsIAogICAgICAgICAgICAgICBkaW1yZWQ9IlBvaVBDQV9kZXYiLAogICAgICAgICAgICAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpCmBgYAoKVGhlIGF1dGhvcnMgb2YgdGhlIGBnbG1wY2FgIHBhY2thZ2Ugbm90ZSB0aGF0IEdMTS1QQ0EgY2FuIGJlIHNsb3cgZm9yIGxhcmdlIApkYXRhc2V0cy4gVGhlcmVmb3JlLCB0aGV5IGhhdmUgaW1wbGVtZW50ZWQgYSBmYXN0IGFwcHJveGltYXRpb24gb2YgdGhlIAphbGdvcml0aG0sIHdoaWNoIGZpcnN0IGZpdHMgYSBudWxsIG1vZGVsIG9mIGNvbnN0YW50IGV4cHJlc3Npb24gZm9yIGVhY2ggZ2VuZSAKYWNyb3NzIGFsbCBjZWxscywgYW5kIHN1YnNlcXVlbnRseSBmaXRzIHN0YW5kYXJkIFBDQSB0byBlaXRoZXIgdGhlIFBlYXJzb24gb3IgCmRldmlhbmNlIHJlc2lkdWFscyBmcm9tIHRoZSBudWxsIG1vZGVsLgoKSG93ZXZlciwgYXQgbGVhc3QgZm9yIG1lIHRoZSBgbnVsbFJlc2lkdWFsc2AgZnVuY3Rpb24gd2FzICoqZXh0cmVtZWx5IHNsb3cqKgpldmVuIHNsb3dlciB0aGFuIHRoZSBgZ2xtcGNhYCBjb2RlIGFib3ZlLiBUaGVyZWZvcmUsICoqSSBzdWdnZXN0IG5vdCBydW5uaW5nKioKdGhpcyBjb2RlLCBidXQgSSBsZWF2ZSBpdCBpbiBmb3IgcmVmZXJlbmNlLgoKYGBgCnNjZSA8LSBudWxsUmVzaWR1YWxzKHNjZSwgYXNzYXk9ImNvdW50cyIsIHR5cGU9ImRldmlhbmNlIikKc2NlIDwtIG51bGxSZXNpZHVhbHMoc2NlLCBhc3NheT0iY291bnRzIiwgdHlwZT0icGVhcnNvbiIpCgpwY2E8LWZ1bmN0aW9uKFksIEw9MiwgY2VudGVyPVRSVUUsIHNjYWxlPVRSVUUpewogICAgI2Fzc3VtZXMgZmVhdHVyZXM9cm93cywgb2JzZXJ2YXRpb25zPWNvbHMKICAgIHJlczwtcHJjb21wKGFzLm1hdHJpeCh0KFkpKSwgY2VudGVyPWNlbnRlciwgc2NhbGUuPXNjYWxlLCByYW5rLj1MKQogICAgZmFjdG9yczwtYXMuZGF0YS5mcmFtZShyZXMkeCkKICAgIGNvbG5hbWVzKGZhY3RvcnMpPC1wYXN0ZTAoImRpbSIsIDE6TCkKICAgIGZhY3RvcnMKfQpwY2FfZCA8LSBwY2EoYXNzYXkoc2NlW2hkZyxdLCAiYmlub21pYWxfZGV2aWFuY2VfcmVzaWR1YWxzIikpCnBjYV9kJHJlc2lkX3R5cGUgPC0gImRldmlhbmNlX3Jlc2lkdWFscyIKcGNhX3AgPC0gcGNhKGFzc2F5KHNjZVtoZGcsXSwgImJpbm9taWFsX3BlYXJzb25fcmVzaWR1YWxzIikpCnBjYV9wJHJlc2lkX3R5cGUgPC0gInBlYXJzb25fcmVzaWR1YWxzIgpjbSA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEoc2NlW2hkZyxdKSkKcGQgPC0gcmJpbmQoY2JpbmQoY20sIHBjYV9kKSwgY2JpbmQoY20sIHBjYV9wKSkKZ2dwbG90KHBkLCBhZXMoeD1kaW0xLCB5PWRpbTIsIGNvbG91cj1waGVub2lkKSkgKyBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAofnJlc2lkX3R5cGUsIHNjYWxlcz0iZnJlZSIsIG5yb3c9MikgKwogIGdndGl0bGUoIlBDQSBhcHBsaWVkIHRvIG51bGwgcmVzaWR1YWxzIG9mIGhpZ2ggZGV2aWFuY2UgZ2VuZXMiKQpgYGAKCiMjIE5vbi1saW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uOiB0LVNORQoKdC1TTkUgZm9jdXNlcyBvbiBwcmVzZXJ2aW5nIGxvY2FsIHJhdGhlciB0aGFuIGdsb2JhbCBkaXN0YW5jZXMuIFRoZXJlZm9yZSwKZGlzdGFuY2VzIG9uIGEgdC1TTkUgcmVkdWNlZCBkaW1lbnNpb24gcGxvdCBjYW4gb25seSBiZSBpbnRlcnByZXRlZCBsb2NhbGx5LAppLmUuLCBjZWxscyB0aGF0IGFyZSBjbG9zZSB0b2dldGhlciBpbiByZWR1Y2VkIGRpbWVuc2lvbiB3aWxsIGhhdmUgYSBzaW1pbGFyCnRyYW5zY3JpcHRvbWUsIGJ1dCBjZWxscyB0aGF0IGFyZSBmYXIgYXdheSBtYXkgbm90IG5lY2Vzc2FyaWx5IGhhdmUgYSB2ZXJ5IGRpc3RpbmN0IHRyYW5zY3JpcHRvbWUuCgpSdW5uaW5nIHQtU05FIG9uIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgY2FuIGJlIGFjaGlldmVkIHdpdGggdGhlCmBydW5UU05FYCBmdW5jdGlvbiBvZiB0aGUgYHNjYXRlcmAgcGFja2FnZS4gQnkgZGVmYXVsdCwgdGhpcyBmdW5jdGlvbiB3aWxsIGZpcnN0CnBlcmZvcm0gUENBLCBhbmQgdXNlIHRoZSB0b3AgNTAgUENzIGFzIGFuIGlucHV0IHRvIHRoZSBhY3R1YWwgdC1TTkUKYWxnb3JpdGhtLiBTaW5jZSB3ZSBhbHJlYWR5IHBlcmZvcm1lZCBQQ0EsIHdlIG1heSBzZXQgYGRpbXJlZCA9ICJQQ0EiYCBhcwphcmd1bWVudCB0byB0aGUgZnVuY3Rpb24uIEFzIHN1Y2ggd2Ugd2lsbCBiZSBwZXJmb3JtaW5nIGEgVC1TTkUgb24gdGhlIDMwIFBDcwp3ZSBjb21wdXRlZCBiZWZvcmUuIElmIHdlIHdvdWxkIGxpa2UgdG8gcnVuIHQtU05FIG9ubHkgdXNpbmcgdGhlIHRvcCAxMApQQ3MsIHdlIGNvdWxkIHNldCBgbl9kaW1yZWQgPSAxMGAuCgpJbiBhZGRpdGlvbiwgd2UgbWF5IHdpc2ggdG8gc2V0IGBleHRlcm5hbF9uZWlnaGJvcnM9VFJVRWAsIHdoaWNoIGluY3JlYXNlcyB0aGUKc3BlZWQgb2YgdGhlIGFsZ29yaXRobSBmb3IgbGFyZ2UgZGF0YXNldHMgYnkgYXBwbHlpbmcgYSBoZXVyaXN0aWMuCgoqKk5vdGU6KiogZm9yIG1lIHRoaXMgd2FzIHRoZSBzbG93ZXN0IGZ1bmN0aW9uIG9mIHRoZSBhbmFseXNpcyAoc28gZmFyKS4gSWYgeW91CmZlZWwgbGlrZSB5b3VyIFBDL2xhcHRvcCBoYWQgYSBsb3Qgb2YgdHJvdWJsZSB3aXRoIHRoZSBwcmV2aW91cyBzdGVwKHMpLCB5b3UKbWF5IGNvbnNpZGVyIG5vdCBydW5uaW5nIHRoaXMgY29kZSwgb3IgcnVubmluZyBpdCBvbiBhIHN1YnNldCBvZiB0aGUgZGF0YQooZS5nLiwgcmFuZG9tbHkgc3Vic2FtcGxpbmcgY2VsbHMpIGZvciBkZW1vbnN0cmF0aW9uYWwgcHVycG9zZXMuIApBbHRlcm5hdGl2ZWx5LCB5b3UgbWF5IGNvbnNpZGVyIHJlZHVjaW5nIHRoZSBpbnB1dCBzcGFjZSBmb3IgdGhlIHQtU05FIAphbGdvcml0aG0sIGUuZy4gYnkgc2V0dGluZyBgbl9kaW1yZWQgPSA1YC4KCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQojIChGb3IgbWUgaXQgdGFrZXMgM21pbjMwIHdpdGggbl9kaW1yZWQgPSA1KQpzY2UgPC0gcnVuVFNORShzY2UsIAogICAgICAgICAgICAgICBkaW1yZWQgPSAnUENBJywKICAgICAgICAgICAgICAgbl9kaW1yZWQgPSA1LAogICAgICAgICAgICAgICBleHRlcm5hbF9uZWlnaGJvcnM9VFJVRSkKIyBnZyA8LSBwbG90VFNORShzY2UsCiMgICAgICAgICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKQojIAojIGdnCmBgYAoKIyMgTm9uLWxpbmVhciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb246IFVNQVAKCkl0IGlzIG9mdGVuIHN1Z2dlc3RlZCB0aGF0IFVNQVAgaXMgYmV0dGVyIHRoYW4gdC1TTkUgaW4gcHJlc2VydmluZyBnbG9iYWwgCmRpZmZlcmVuY2VzLiBUaGVyZWZvcmUsIFVNQVAgaXMgYWxzbyBvZnRlbiB1c2VkIGluIGFuYWx5c2VzIHN1Y2ggYXMgdHJhamVjdG9yeSAKaW5mZXJlbmNlLCB3aGVyZSB0aGlzIGlzIGltcG9ydGFudC4KClJ1bm5pbmcgVU1BUCBvbiBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IGNhbiBiZSBhY2hpZXZlZCB3aXRoIHRoZQpgcnVuVU1BUGAgZnVuY3Rpb24gb2YgdGhlIGBzY2F0ZXJgIHBhY2thZ2UuCgpgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KIyBVc2luZyB0b3AgMTAlIGhpZ2hseSB2YXJpYWJsZSBnZW5lcyBhbmQgdG9wIDMwIFBDcwpzY2UgPC0gcnVuVU1BUChzY2UsIAogICAgICAgICAgICAgICBkaW1yZWQgPSAnUENBJywgCiAgICAgICAgICAgICAgIGV4dGVybmFsX25laWdoYm9ycyA9IFRSVUUpCiMgZ2cgPC0gcGxvdFVNQVAoc2NlLAojICAgICAgICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikKIyAKIyBnZwoKIyBVc2luZyB0b3AgMTAlIGhpZ2hseSB2YXJpYWJsZSBnZW5lcyBhbmQgdG9wIDEwIFBDcwojIGdnIDwtIHBsb3RVTUFQKHJ1blVNQVAoc2NlLCAKIyAgICAgICAgICAgICAgICBkaW1yZWQgPSAnUENBJywgCiMgICAgICAgICAgICAgICAgZXh0ZXJuYWxfbmVpZ2hib3JzID0gVFJVRSwKIyAgICAgICAgICAgICAgICBuX2RpbXJlZCA9IDEwKSwKIyAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikKIyAKIyBnZwpgYGAKCi0tLQoKLS0tIGVuZCBsYWIgc2Vzc2lvbiAyIC0tLQoKLS0tCgojIFJlYWQgcmVzdWx0cyBmcm9tIGZpcnN0IHR3byBzZXNzaW9ucwoKYGBge3IsZXZhbD1GQUxTRX0Kc2NlIDwtIHJlYWRSRFMoIi4vc2NlX2FmdGVyX2xhYnNlc3Npb24yLnJkcyIpICMgY2hhbmdlIHRvIFlPVVIgcGF0aCEKYGBgCgojIEFkZCBjbHVzdGVyIGluZm9ybWF0aW9uIGZyb20gcHVibGljYXRpb24KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKc2NlJGNsdXN0ZXJfbG93UmVzIDwtIGZjdF9yZWNvZGUoY29sRGF0YShzY2UpJGNsdXN0ZXIsIAogICAgICAgICAgICJIb3Jpem9udGFsX2NlbGxzIiA9ICIxIiwKICAgICAgICAgICAiR2FuZ2xpb25fY2VsbHMiID0gIjIiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMyIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICI0IiwKICAgICAgICAgICAiQW1hY3JpbmUiID0gIjUiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiNiIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICI3IiwKICAgICAgICAgICAiQW1hY3JpbmUiID0gIjgiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiOSIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxMCIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxMSIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxMiIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxMyIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxNCIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxNSIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxNiIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxNyIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxOCIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIxOSIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIyMCIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIyMSIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIyMiIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICIyMyIsCiAgICAgICAgICAgIlJvZHMiID0gIjI0IiwKICAgICAgICAgICAiQ29uZXMiID0gIjI1IiwKICAgICAgICAgICAiQmlwb2xhciIgPSAiMjYiLAogICAgICAgICAgICJCaXBvbGFyIiA9ICIyNyIsCiAgICAgICAgICAgIkJpcG9sYXIiID0gIjI4IiwKICAgICAgICAgICAiQmlwb2xhciIgPSAiMjkiLAogICAgICAgICAgICJCaXBvbGFyIiA9ICIzMCIsCiAgICAgICAgICAgIkJpcG9sYXIiID0gIjMxIiwKICAgICAgICAgICAiQmlwb2xhciIgPSAiMzIiLAogICAgICAgICAgICJCaXBvbGFyIiA9ICIzMyIsCiAgICAgICAgICAgIk11bGxlcl9nbGlhIiA9ICIzNCIsCiAgICAgICAgICAgIkFzdHJvY3l0ZXMiID0gIjM1IiwKICAgICAgICAgICAiRmlicm9ibGFzdCIgPSAiMzYiLAogICAgICAgICAgICJWYXNjdWxhcl9lbmRvdGhlbGl1bSIgPSAiMzciLAogICAgICAgICAgICJQZXJpY3l0ZXMiID0gIjM4IiwKICAgICAgICAgICAiTWljcm9nbGlhIiA9ICIzOSIpCgpzY2UkY2x1c3Rlcl9oaWdoUmVzIDwtIGZjdF9yZWNvZGUoY29sRGF0YShzY2UpJGNsdXN0ZXIsIAogICAgICAgICAgICJIb3Jpem9udGFsX2NlbGxzIiA9ICIxIiwKICAgICAgICAgICAiR2FuZ2xpb25fY2VsbHMiID0gIjIiLAogICAgICAgICAgICJBbWFjcmluZV8xIiA9ICIzIiwKICAgICAgICAgICAiQW1hY3JpbmVfMiIgPSAiNCIsCiAgICAgICAgICAgIkFtYWNyaW5lXzMiID0gIjUiLAogICAgICAgICAgICJBbWFjcmluZV80IiA9ICI2IiwKICAgICAgICAgICAiQW1hY3JpbmVfNSIgPSAiNyIsCiAgICAgICAgICAgIkFtYWNyaW5lXzYiID0gIjgiLAogICAgICAgICAgICJBbWFjcmluZV83IiA9ICI5IiwKICAgICAgICAgICAiQW1hY3JpbmVfOCIgPSAiMTAiLAogICAgICAgICAgICJBbWFjcmluZV85IiA9ICIxMSIsCiAgICAgICAgICAgIkFtYWNyaW5lXzEwIiA9ICIxMiIsCiAgICAgICAgICAgIkFtYWNyaW5lXzExIiA9ICIxMyIsCiAgICAgICAgICAgIkFtYWNyaW5lXzEyIiA9ICIxNCIsCiAgICAgICAgICAgIkFtYWNyaW5lXzEzIiA9ICIxNSIsCiAgICAgICAgICAgIkFtYWNyaW5lXzE0IiA9ICIxNiIsCiAgICAgICAgICAgIkFtYWNyaW5lXzE1IiA9ICIxNyIsCiAgICAgICAgICAgIkFtYWNyaW5lXzE2IiA9ICIxOCIsCiAgICAgICAgICAgIkFtYWNyaW5lXzE3IiA9ICIxOSIsCiAgICAgICAgICAgIkFtYWNyaW5lXzE4IiA9ICIyMCIsCiAgICAgICAgICAgIkFtYWNyaW5lXzE5IiA9ICIyMSIsCiAgICAgICAgICAgIkFtYWNyaW5lXzIwIiA9ICIyMiIsCiAgICAgICAgICAgIkFtYWNyaW5lXzIxIiA9ICIyMyIsCiAgICAgICAgICAgIlJvZHMiID0gIjI0IiwKICAgICAgICAgICAiQ29uZXMiID0gIjI1IiwKICAgICAgICAgICAiQmlwb2xhcl8xIiA9ICIyNiIsCiAgICAgICAgICAgIkJpcG9sYXJfMiIgPSAiMjciLAogICAgICAgICAgICJCaXBvbGFyXzMiID0gIjI4IiwKICAgICAgICAgICAiQmlwb2xhcl80IiA9ICIyOSIsCiAgICAgICAgICAgIkJpcG9sYXJfNSIgPSAiMzAiLAogICAgICAgICAgICJCaXBvbGFyXzYiID0gIjMxIiwKICAgICAgICAgICAiQmlwb2xhcl83IiA9ICIzMiIsCiAgICAgICAgICAgIkJpcG9sYXJfOCIgPSAiMzMiLAogICAgICAgICAgICJNdWxsZXJfZ2xpYSIgPSAiMzQiLAogICAgICAgICAgICJBc3Ryb2N5dGVzIiA9ICIzNSIsCiAgICAgICAgICAgIkZpYnJvYmxhc3QiID0gIjM2IiwKICAgICAgICAgICAiVmFzY3VsYXJfZW5kb3RoZWxpdW0iID0gIjM3IiwKICAgICAgICAgICAiUGVyaWN5dGVzIiA9ICIzOCIsCiAgICAgICAgICAgIk1pY3JvZ2xpYSIgPSAiMzkiKQpgYGAKCiMgQ2x1c3RlcmluZwoKIyMgR3JhcGgtYmFzZWQgY2x1c3RlcmluZwoKRmlyc3QsIHdlIGRpc2N1c3MgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBtZXRob2RzIGZvciBzY1JOQS1TZXEgZGF0YS4KVGhpcyBpcyB2ZXJ5IHdlbGwgZXhwbGFpbmVkIGluIHRoZSAKW09TQ0EgYm9vayBjaGFwdGVyIDUuMl0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNC9PU0NBLmJhc2ljL2NsdXN0ZXJpbmcuaHRtbCNjbHVzdGVyaW5nLWdyYXBoKS4KCkFzIHdyaXR0ZW4gaW4gdGhlIE9TQ0EgYm9vazogIlBvcHVsYXJpemVkIGJ5IGl0cyB1c2UgaW4gU2V1cmF0LCBncmFwaC1iYXNlZCAKY2x1c3RlcmluZyBpcyBhIGZsZXhpYmxlIGFuZCBzY2FsYWJsZSB0ZWNobmlxdWUgZm9yIGNsdXN0ZXJpbmcgbGFyZ2Ugc2NSTkEtc2VxIApkYXRhc2V0cy4gV2UgZmlyc3QgYnVpbGQgYSBncmFwaCB3aGVyZSBlYWNoIG5vZGUgaXMgYSBjZWxsIHRoYXQgaXMgY29ubmVjdGVkIAp0byBpdHMgbmVhcmVzdCBuZWlnaGJvcnMgaW4gdGhlIGhpZ2gtZGltZW5zaW9uYWwgc3BhY2UuIEVkZ2VzIGFyZSB3ZWlnaHRlZCAKYmFzZWQgb24gdGhlIHNpbWlsYXJpdHkgYmV0d2VlbiB0aGUgY2VsbHMgaW52b2x2ZWQsIHdpdGggaGlnaGVyIHdlaWdodCBnaXZlbiAKdG8gY2VsbHMgdGhhdCBhcmUgbW9yZSBjbG9zZWx5IHJlbGF0ZWQuIFdlIHRoZW4gYXBwbHkgYWxnb3JpdGhtcyB0byBpZGVudGlmeSAK4oCcY29tbXVuaXRpZXPigJ0gb2YgY2VsbHMgdGhhdCBhcmUgbW9yZSBjb25uZWN0ZWQgdG8gY2VsbHMgaW4gdGhlIHNhbWUgY29tbXVuaXR5IAp0aGFuIHRoZXkgYXJlIHRvIGNlbGxzIG9mIGRpZmZlcmVudCBjb21tdW5pdGllcy4gRWFjaCBjb21tdW5pdHkgcmVwcmVzZW50cyBhIApjbHVzdGVyIHRoYXQgd2UgY2FuIHVzZSBmb3IgZG93bnN0cmVhbSBpbnRlcnByZXRhdGlvbi4KClRoZSBtYWpvciBhZHZhbnRhZ2Ugb2YgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBsaWVzIGluIGl0cyBzY2FsYWJpbGl0eS4gSXQgb25seSAKcmVxdWlyZXMgYSBrLW5lYXJlc3QgbmVpZ2hib3Igc2VhcmNoIHRoYXQgY2FuIGJlIGRvbmUgaW4gbG9nLWxpbmVhciB0aW1lIG9uIAphdmVyYWdlLCBpbiBjb250cmFzdCB0byBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtZXRob2RzIHdpdGggcnVudGltZXMgdGhhdCBhcmUgCnF1YWRyYXRpYyB3aXRoIHJlc3BlY3QgdG8gdGhlIG51bWJlciBvZiBjZWxscy4gR3JhcGggY29uc3RydWN0aW9uIGF2b2lkcyBtYWtpbmcgCnN0cm9uZyBhc3N1bXB0aW9ucyBhYm91dCB0aGUgc2hhcGUgb2YgdGhlIGNsdXN0ZXJzIG9yIHRoZSBkaXN0cmlidXRpb24gb2YgY2VsbHMgCndpdGhpbiBlYWNoIGNsdXN0ZXIsIGNvbXBhcmVkIHRvIG90aGVyIG1ldGhvZHMgbGlrZSBrLW1lYW5zICh0aGF0IGZhdm9yIApzcGhlcmljYWwgY2x1c3RlcnMpIG9yIEdhdXNzaWFuIG1peHR1cmUgbW9kZWxzICh0aGF0IHJlcXVpcmUgbm9ybWFsaXR5KS4iCgpTZXZlcmFsIGdyYXBoLWJhc2VkIGNsdXN0ZXJpbmcgYWxnb3JpdGhtcyBhcmUgaW1wbGVtZW50ZWQgaW4gdGhlIGBzY3JhbmAgbGlicmFyeS4gClRoZSBtb3N0IGdsb2JhbCB3cmFwcGVyLWZ1bmN0aW9uIGluIHRoaXMgcGFja2FnZSBpcyB0aGUgYGNsdXN0ZXJDZWxsc2AgZnVuY3Rpb24uIApUeXBpY2FsbHksIHRoZSBpbnB1dCB0byB0aGlzIGZ1bmN0aW9uIGlzIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3Qgd2l0aCAKcHJlLWNvbXB1dGVkIHByaW5jaXBhbCBjb21wb25lbnRzOyB0aGVzZSBhcmUgdXNlZCB0byB0YWtlIGFkdmFudGFnZSBvZiBkYXRhIApjb21wcmVzc2lvbiBhbmQgZGVub2lzaW5nLiBJZiB0aGUgZGVmYXVsdCBzZXR0aW5ncyBhcmUgYWRvcHRlZCwgYGNsdXN0ZXJDZWxsc2AKd2lsbCBwZXJmb3JtIHR3byBzdGVwcyB1bmRlciB0aGUgaG9vZDoKCjEuIEJ1aWxkIGEgc2hhcmVkIG5lYXJlc3QgbmVpZ2hib3JzIChTTk4pIGdyYXBoIG9mIG9ic2VydmF0aW9ucyBmb3IgZG93bnN0cmVhbSBjb21tdW5pdHkgCmRldGVjdGlvbi4gVGhlIFNOTiBncmFwaCBpcyBjbG9zZWx5IHJlbGF0ZWQgdG8gdGhlIG1vcmUgY29tbW9uIEtOTiBncmFwaC4gRm9yIAplYWNoIG9ic2VydmF0aW9uLCBpdHMgay1uZWFyZXN0IG5laWdoYm9ycyBhcmUgaWRlbnRpZmllZCAoaz0xMCBieSBkZWZhdWx0KSwgCmJhc2VkIG9uIGRpc3RhbmNlcyBiZXR3ZWVuIHRoZWlyIGV4cHJlc3Npb24gcHJvZmlsZXMgKEV1Y2xpZGVhbiBkaXN0YW5jZXMgYXJlIAp1c2VkIGJ5IGRlZmF1bHQpIGFzIG9ic2VydmVkIGluIFBDQSBzcGFjZS4gQW4gZWRnZSBpcyBkcmF3biBiZXR3ZWVuIGFsbCBwYWlycyAKb2Ygb2JzZXJ2YXRpb25zIHRoYXQgc2hhcmUgYXQgbGVhc3Qgb25lIG5laWdoYm9yLCB3ZWlnaHRlZCBieSB0aGUgY2hhcmFjdGVyaXN0aWNzIApvZiB0aGUgc2hhcmVkIG5lYXJlc3QgbmVpZ2hib3JzLgoKMi4gVGhlIGBjbHVzdGVyQ2VsbHNgIGZ1bmN0aW9uIG5leHQgaW50ZXJuYWxseSBjYWxscyB0aGUgYGNsdXN0ZXJfd2Fsa3RyYXBgIGZ1bmN0aW9uIApmcm9tIHRoZSBgaWdyYXBoYCBsaWJyYXJ5LiBCYXNlZCBvbiB0aGUgU05OIGdyYXBoIGZyb20gc3RlcCAxLCB0aGlzIGZ1bmN0aW9uIHRyaWVzCnRvIGZpbmQgZGVuc2VseSBjb25uZWN0ZWQgc3ViZ3JhcGhzLCBhbHNvIGNhbGxlZCBjb21tdW5pdGllcyBpbiBhIGdyYXBoIHZpYSAKcmFuZG9tIHdhbGtzLiBUaGUgaWRlYSBpcyB0aGF0IHNob3J0IHJhbmRvbSB3YWxrcyB0ZW5kIHRvIHN0YXkgaW4gdGhlIHNhbWUgCmNvbW11bml0eS4KCmBgYAojIERvIG5vdCBydW47IGNsdXN0ZXJDZWxscyBmdW5jdGlvbiB3aXRoIGRlZmF1bHQgc2V0dGluZ3MsIGkuZS4sIGJ1aWxkaW5nIGFuCiMgU05OIGdyYXBoIGFuZCBmaW5kaW5nIGNsdXN0ZXJzIHdpdGggdGhlIHdhbGt0cmFwIGFsZ29yaXRobS4KbGlicmFyeShzY3JhbikKbm4uY2x1c3RlcnMgPC0gY2x1c3RlckNlbGxzKHNjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UuZGltcmVkPSJQQ0EiKQp0YWJsZShubi5jbHVzdGVycykKYGBgCgpUaGUgZGlzYWR2YW50YWdlIG9mIHVzaW5nIGBjbHVzdGVyQ2VsbHNgIGlzIHRoYXQgdGhlIGRlZmF1bHQgc2V0dGluZyBvZiB0aGUgCnNlY29uZCBzdGVwLCB0aGUgYGNsdXN0ZXJfd2Fsa3RyYXBgIGZ1bmN0aW9uLCBpcyBzbG93IGZvciBsYXJnZSBkYXRhc2V0cy4gV2hpbGUgCml0IGlzIHBvc3NpYmxlIHRvIGFkanVzdCB0aGUgZGlmZmVyZW50IGFyZ3VtZW50cyBvZiB0aGUgYGNsdXN0ZXJDZWxsc2AgZnVuY3Rpb24sCml0IG1pZ2h0IGJlIG1vcmUgY2xlYXIgYW5kIGludHVpdGl2ZSB0byBzaW1wbHkgYnJlYWsgdXAgdGhlIHByb2Nlc3MgaW4gdHdvIHN0ZXBzOgpidWlsZGluZyB0aGUgZ3JhcGggYW5kIGRldGVjdGluZyBjbHVzdGVycyBpbiB0aGF0IGdyYXBoLiBGb3IgdGhpcyBzZWNvbmQgc3RlcCwgCndlIG1heSB0aGVuIGFkb3B0IGEgZmFzdGVyIGFsZ29yaXRobS4KCiMjIyBCdWlsZCBncmFwaCAoU05OIGdyYXBoKQoKYGBge3IsZXZhbD1GQUxTRX0KIyBCdWlsZCBhIHNoYXJlZCBuZWFyZXN0LW5laWdoYm9yIGdyYXBoIGZyb20gUENBIHNwYWNlCmdyYXBoIDwtIGJ1aWxkU05OR3JhcGgoc2NlLCAKICAgICAgICAgICAgICAgICAgICAgICB1c2UuZGltcmVkID0gJ1BDQScpCiMgYWx0ZXJuYXRpdmU6IGJ1aWxkS05OR3JhcGgoKQpgYGAKCiMjIyBEZXRlY3QgY2x1c3RlcnMgb24gdGhlIGdyYXBoCgpUd28gcG9wdWxhciBncmFwaC1iYXNlZCBjbHVzdGVyaW5nIGFsZ29yaXRobSBhcmUgdGhlIGBsZWlkZW5gIGFuZCBgbG91dmFpbmAgCmFsZ29yaXRobXMsIGJvdGggcmVmZXJyaW5nIHRvIHRoZSBsb2NhdGlvbiBvZiBpdHMgZGV2ZWxvcGVycy4gQSBjb21tb24gCmltcGxlbWVudGF0aW9uIG9mIHRoZSAKW2Bsb3V2YWluYGFsZ29yaXRobV0oaHR0cHM6Ly9pb3BzY2llbmNlLmlvcC5vcmcvYXJ0aWNsZS8xMC4xMDg4LzE3NDItNTQ2OC8yMDA4LzEwL1AxMDAwOCkgCmlzIHRvIG9wdGltaXplIHRoZSBtb2R1bGFyaXR5LCBlZmZlY3RpdmVseSBhdHRlbXB0aW5nIHRvIG1heGltaXplIHRoZSBkaWZmZXJlbmNlIApiZXR3ZWVuIHRoZSBvYnNlcnZlZCBudW1iZXIgb2YgZWRnZXMgaW4gYSBjb21tdW5pdHkgYW5kIHRoZSBleHBlY3RlZCBudW1iZXIgb2YgCnN1Y2ggZWRnZXMuCgpIb3dldmVyLCBhZGRpdGlvbmFsIGV2YWx1YXRpb25zIGZvdW5kIHRoYXQgbW9kdWxhcml0eSBvcHRpbWl6YXRpb24gdXNpbmcgdGhlIApgbG91dmFpbmAgYWxnb3JpdGhtIGlzIGNvbmZpbmVkIHRvIGEgCltyZXNvbHV0aW9uIGxpbWl0XShodHRwczovL3d3dy5wbmFzLm9yZy9jb250ZW50LzEwNC8xLzM2KSwgYW5kIGluIGFkZGl0aW9uIG1heSAKcmVzdWx0IGluIGNvbW11bml0aWVzIHRoYXQgYXJlIG5vdCB3ZWxsIGNvbm5lY3RlZC4gClRoZSBbYGxlaWRlbmAgYWxnb3JpdGhtXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU5OC0wMTktNDE2OTUteiksIAppbnN0ZWFkLCBndWFyYW50ZWVzIHdlbGwtY29ubmVjdGVkIGNvbW11bml0aWVzLgoKYGBge3IsZXZhbD1GQUxTRX0Kc2V0LnNlZWQoNDY0Njg4KQojIFdhbGt0cmFwIGNvbW11bml0eSBmaW5kaW5nIGFsZ29yaXRobSBvbiB0aGUgU05OIGdyYXBoCiMgRE8gTk9UIFJVTiAtPiB0YWtlcyAyMCBtaW51dGVzCiMgY2x1c3Rlcl93YWxrdHJhcCA8LSBmYWN0b3IoaWdyYXBoOjpjbHVzdGVyX3dhbGt0cmFwKGcpJG1lbWJlcnNoaXApICMyMG1pbgoKIyBUaGUgYGNsdXN0ZXJfZmFzdF9ncmVlZHlgIGZ1bmN0aW9uIHRyaWVzIHRvIGZpbmQgZGVuc2Ugc3ViZ3JhcGgsIGFsc28gY2FsbGVkIAojIGNvbW11bml0aWVzIGluIGdyYXBocyB2aWEgZGlyZWN0bHkgb3B0aW1pemluZyBhIG1vZHVsYXJpdHkgc2NvcmUKIyBETyBOT1QgUlVOIC0+IHRha2VzIDQgbWludXRlcwojIGNsdXN0ZXJfZmFzdEdyZWVkeSA8LSBmYWN0b3IoaWdyYXBoOjpjbHVzdGVyX2Zhc3RfZ3JlZWR5KGdyYXBoKSRtZW1iZXJzaGlwKSAjNG1pbgoKIyBMb3V2YWluIGNsdXN0ZXJpbmcgb24gdGhlIFNOTiBncmFwaApjbHVzdGVyX2xvdXZhaW4gPC0gZmFjdG9yKGlncmFwaDo6Y2x1c3Rlcl9sb3V2YWluKGdyYXBoKSRtZW1iZXJzaGlwKSAjOHNlYwpubGV2ZWxzKGNsdXN0ZXJfbG91dmFpbikgIyAxMSBjbHVzdGVycwoKIyBMZWlkZW4gY2x1c3RlcmluZyBvbiB0aGUgU05OIGdyYXBoCmNsdXN0ZXJfbGVpZGVuIDwtIGZhY3RvcihpZ3JhcGg6OmNsdXN0ZXJfbGVpZGVuKGdyYXBoKSRtZW1iZXJzaGlwKSAjMTBzZWMKbmxldmVscyhjbHVzdGVyX2xlaWRlbikgIyAxMzI2IGRpZmZlcmVudCBjbHVzdGVycyEKCmNsdXN0ZXJfbGVpZGVuMiA8LSBmYWN0b3IoaWdyYXBoOjpjbHVzdGVyX2xlaWRlbihncmFwaCA9IGdyYXBoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbl9wYXJhbWV0ZXIgPSAwLjAxKSRtZW1iZXJzaGlwKSAjMTBzZWMKbmxldmVscyhjbHVzdGVyX2xlaWRlbjIpICMxNCBkaWZmZXJlbnQgY2x1c3RlcnMKCiMgQWRkIHRvIHNjZSBhbmQgdmlzdWFsaXplCmNvbERhdGEoc2NlKSRjbHVzdGVyX2xvdXZhaW4gPC0gY2x1c3Rlcl9sb3V2YWluCmNvbERhdGEoc2NlKSRjbHVzdGVyX2xlaWRlbjIgPC0gY2x1c3Rlcl9sZWlkZW4yCmBgYAoKIyMjIENvbXBhcmluZyBjbHVzdGVyaW5nIHN0cmF0ZWdpZXMKCkEgZGlyZWN0IGNvbXBhcmlzb24gb2YgdGhlIExvdXZhaW4gYW5kIExlaWRlbiBjbHVzdGVyaW5nIHJlc3VsdHMgdXNpbmcgYSB0YWJsZSAKb2YgdGhlIGNsdXN0ZXIgbGFiZWxzLCBzaG93cyBnb29kIGFncmVlbWVudC4KCmBgYHtyLGV2YWw9RkFMU0V9CnRhYmxlKGNsdXN0ZXJfbG91dmFpbiwgY2x1c3Rlcl9sZWlkZW4yKQpgYGAKICAgICAgICAgICAgIApGaXJzdCwgbm90ZSB0aGF0IExlaWRlbjIgY2x1c3RlcnMgOS0xNCBhbGwgY29udGFpbiBvbmx5IDEgY2VsbCwgd2hpY2ggZG9lc27igJl0IAptYWtlIG11Y2ggc2Vuc2UuCgpOZXh0LCB3ZSBzZWUgdGhlIGZvbGxvd2luZyBjbGVhciBjb3JyZXNwb25kZW5jZXMgYmV0d2VlbiB0aGUgY2x1c3RlciBsYWJlbHM6CgpMb3V2YWluIDEgLT4gTGVpZGVuIDEKTG91dmFpbiA2IC0+IExlaWRlbiAyCkxvdXZhaW4gMyAtPiBMZWlkZW4gNApMb3V2YWluIDcgLT4gTGVpZGVuIDYKTG91dmFpbiA4IC0+IExlaWRlbiA3CkxvdXZhaW4gMTAgLT4gTGVpZGVuIDgKCkZvciB0aGUgcmVtYWluaW5nIGNsdXN0ZXJzLCB3ZSBzZWUgdGhhdCAKLSBMZWlkZW4gY2x1c3RlciAzIGNvbnRhaW5zIGFsbW9zdCBjZWxscyBvZiBMb3V2YWluIGNsdXN0ZXJzIDIsIDkgYW5kIDExIAotIExlaWRlbiBjbHVzdGVyIDUgY29udGFpbnMgYWxtb3N0IGNlbGxzIG9mIExvdXZhaW4gY2x1c3RlcnMgNCBhbmQgNQoKVG8gbWFrZSBhIHZpc3VhbGl6YXRpb24gdGhhdCBnaXZlcyB1cyB2ZXJ5IHNpbWlsYXIgaW5mb3JtYXRpb24sIHdlIG1heSB1c2UgYSAKaGVhdG1hcDoKCmBgYHtyLGV2YWw9RkFMU0V9CmxpYnJhcnkocGhlYXRtYXApCnBoZWF0bWFwOjpwaGVhdG1hcCh0YWJsZShjbHVzdGVyX2xvdXZhaW4sIGNsdXN0ZXJfbGVpZGVuMikpCmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgbWF5IGNvbXB1dGUgYSBjbHVzdGVyaW5nIHNpbWlsYXJpdHkgc2NvcmUgdGhhdCBjYXB0dXJlcyB0aGUgCmFncmVlbWVudCBiZXR3ZWVuIHR3byBzZXRzIG9mIHBhcnRpdGlvbnMuIFRoZSBBZGp1c3RlZCBSYW5kIEluZGV4IChBUkkpIGlzIG9mdGVuCnVzZWQgaW4gdGhlIGxpdGVyYXR1cmUgZm9yIHRoaXMgcHVycG9zZS4gVGhlIEFSSSBpcyBlcXVhbCB0byAxIGlmIHRoZSB0d28gCnBhcnRpdGlvbnMgYWdyZWUgcGVyZmVjdGx5LCBhbmQgaXQgaXMgemVybyBpZiB0aGUgdHdvIHBhcnRpdGlvbnMgYXJlIHVucmVsYXRlZC4gCkluIHNvbWUgY2FzZXMsIHRoZSBBUkkgbWF5IGFsc28gYmUgbmVnYXRpdmUgaWYgdGhlIHBhcnRpdGlvbnMgYXJlIG11Y2ggbW9yZSAKZGlmZmVyZW50IHRoYW4gd2hhdCBjb3VsZCBiZSBleHBlY3RlZCBieSBjaGFuY2UuCgpgYGB7cixldmFsPUZBTFNFfQpsaWJyYXJ5KG1jbHVzdCkKbWNsdXN0OjphZGp1c3RlZFJhbmRJbmRleChjbHVzdGVyX2xvdXZhaW5bLXdoaWNoKGNsdXN0ZXJfbGVpZGVuMiAlaW4lIGMoOToxNCkpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9sZWlkZW4yWy13aGljaChjbHVzdGVyX2xlaWRlbjIgJWluJSBjKDk6MTQpKV0pCmBgYAoKIyMjIFZpc3VhbGl6YXRpb24KCmBgYHtyLGV2YWw9RkFMU0V9CiMgVmlzdWFsaXphdGlvbi4gQWRkIHRoZSBjbHVzdGVyIGxhYmVscyB0byB0aGUgcHJldmlvdXNseSBnZW5lcmF0ZWQgVFNORQojIGNvb3JkaW5hdGVzCnBsb3RUU05FKHNjZSwgCiAgICAgICAgIGNvbG91cl9ieT0iY2x1c3Rlcl9sb3V2YWluIikKCnBsb3RUU05FKHNjZSwgCiAgICAgICAgIGNvbG91cl9ieT0iY2x1c3Rlcl9sZWlkZW4yIikKYGBgCgojIyBLLW1lYW5zIGNsdXN0ZXJpbmcKCkstbWVhbnMgaXMgYSBjbHVzdGVyaW5nIGFsZ29yaXRobSB0aGF0IGhhcyBiZWVuIHVzZWQgaW4gbWFueSBhcHBsaWNhdGlvbiBhcmVhcy4gCkluIFIsIGl0IGNhbiBiZSBhcHBsaWVkIHZpYSB0aGUgYGttZWFuc2AgZnVuY3Rpb24uIFR5cGljYWxseSwgaXQgaXMgYXBwbGllZCB0byAKYSByZWR1Y2VkIGRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBleHByZXNzaW9uIGRhdGEgKG1vc3Qgb2Z0ZW4gUENBKS4gCldlIG5lZWQgdG8gZGVmaW5lIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgaW4gYWR2YW5jZS4gU2luY2UgdGhlIHJlc3VsdHMgZGVwZW5kIG9uIAp0aGUgaW5pdGlhbGl6YXRpb24gb2YgdGhlIGNsdXN0ZXIgY2VudGVycywgaXQgaXMgdHlwaWNhbGx5IHJlY29tbWVuZGVkIHRvIHJ1biAKay1tZWFucyB3aXRoIG11bHRpcGxlIHN0YXJ0aW5nIGNvbmZpZ3VyYXRpb25zICh2aWEgdGhlIGBuc3RhcnRgIGFyZ3VtZW50KS4KRm9yIHJlcHJvZHVjaWJpbGl0eSwgd2UgYWxzbyBzdHJvbmdseSBhZHZpc2UgdG8gc2V0IGEgc2VlZC4KCmBgYHtyLGV2YWw9RkFMU0V9CnNldC5zZWVkKDEyMykKCiMgaz0xMApjbHVzdF9rbWVhbnNfazEwIDwtIGttZWFucyhyZWR1Y2VkRGltKHNjZSwgIlBDQSIpLCBjZW50ZXJzID0gMTAsIG5zdGFydCA9IDUpCnRhYmxlKGNsdXN0X2ttZWFuc19rMTAkY2x1c3RlcikKY29sRGF0YShzY2UpJGttZWFuczEwIDwtIGZhY3RvcihjbHVzdF9rbWVhbnNfazEwJGNsdXN0ZXIpCnBsb3RUU05FKHNjZSwgY29sb3VyX2J5PSJrbWVhbnMxMCIpCgojIGs9MzkKY2x1c3Rfa21lYW5zX2szOSA8LSBrbWVhbnMocmVkdWNlZERpbShzY2UsICJQQ0EiKSwgY2VudGVycyA9IDM5LCBuc3RhcnQgPSA1KQp0YWJsZShjbHVzdF9rbWVhbnNfazM5JGNsdXN0ZXIpCmNvbERhdGEoc2NlKSRrbWVhbnMzOSA8LSBmYWN0b3IoY2x1c3Rfa21lYW5zX2szOSRjbHVzdGVyKQpwbG90VFNORShzY2UsIGNvbG91cl9ieT0ia21lYW5zMzkiKQpgYGAKCldlIGhlcmUgYXJiaXRyYXJpbHkgcGVyZm9ybWVkIHR3byBrLW1lYW5zIGNsdXN0ZXJpbmcgYW5hbHlzZXMsIG9uY2Ugd2l0aCBrPTEwIAphbmQgb25jZSB3aXRoIGs9MzkgKHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgY29tbXVuaWNhdGVkIGJ5IHRoZSBhdXRob3JzKS4gVGhlIApjaG9pY2Ugb2YgdGhlIG51bWJlciBvZiBjbHVzdGVycyBrIGNhbiBiZSBndWlkZWQgYnkga25vd24gYmlvbG9neSwgaG93ZXZlciwgaXQgCmlzIGFyYml0cmFyeSBhdCBsZWFzdCB0byBzb21lIGludGVydmFsLgoKIyMgSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKCkZyb20gW09TQ0EgYm9vayBjaGFwdGVyIDUuNF0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNC9PU0NBLmJhc2ljL2NsdXN0ZXJpbmcuaHRtbCNoaWVyYXJjaGljYWwtY2x1c3RlcmluZyk6CgoiSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgaXMgYW4gb2xkIHRlY2huaXF1ZSB0aGF0IGFycmFuZ2VzIHNhbXBsZXMgaW50byBhIApoaWVyYXJjaHkgYmFzZWQgb24gdGhlaXIgcmVsYXRpdmUgc2ltaWxhcml0eSB0byBlYWNoIG90aGVyLiBNb3N0IGltcGxlbWVudGF0aW9ucwpkbyBzbyBieSBqb2luaW5nIHRoZSBtb3N0IHNpbWlsYXIgc2FtcGxlcyBpbnRvIGEgbmV3IGNsdXN0ZXIsIHRoZW4gam9pbmluZyAKc2ltaWxhciBjbHVzdGVycyBpbnRvIGxhcmdlciBjbHVzdGVycywgYW5kIHNvIG9uLCB1bnRpbCBhbGwgc2FtcGxlcyBiZWxvbmcgdG8gYSAKc2luZ2xlIGNsdXN0ZXIuIFRoaXMgcHJvY2VzcyB5aWVsZHMgYSBkZW5kcm9ncmFtIHRoYXQgZGVmaW5lcyBjbHVzdGVycyB3aXRoIApwcm9ncmVzc2l2ZWx5IGluY3JlYXNpbmcgZ3JhbnVsYXJpdHkuIFZhcmlhbnRzIG9mIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIAptZXRob2RzIHByaW1hcmlseSBkaWZmZXIgaW4gaG93IHRoZXkgY2hvb3NlIHRvIHBlcmZvcm0gdGhlIGFnZ2xvbWVyYXRpb25zLiAKRm9yIGV4YW1wbGUsIGNvbXBsZXRlIGxpbmthZ2UgYWltcyB0byBtZXJnZSBjbHVzdGVycyB3aXRoIHRoZSBzbWFsbGVzdCBtYXhpbXVtIApkaXN0YW5jZSBiZXR3ZWVuIHRoZWlyIGVsZW1lbnRzLCB3aGlsZSBXYXJk4oCZcyBtZXRob2QgYWltcyB0byBtaW5pbWl6ZSB0aGUgCmluY3JlYXNlIGluIHdpdGhpbi1jbHVzdGVyIHZhcmlhbmNlLgoKSW4gdGhlIGNvbnRleHQgb2Ygc2NSTkEtc2VxLCB0aGUgbWFpbiBhZHZhbnRhZ2Ugb2YgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgbGllcwppbiB0aGUgcHJvZHVjdGlvbiBvZiB0aGUgZGVuZHJvZ3JhbS4gVGhpcyBpcyBhIHJpY2ggc3VtbWFyeSB0aGF0IHF1YW50aXRhdGl2ZWx5CmNhcHR1cmVzIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gc3VicG9wdWxhdGlvbnMgYXQgdmFyaW91cyByZXNvbHV0aW9ucy4gCkN1dHRpbmcgdGhlIGRlbmRyb2dyYW0gYXQgaGlnaCByZXNvbHV0aW9uIGlzIGFsc28gZ3VhcmFudGVlZCB0byB5aWVsZCBjbHVzdGVycyAKdGhhdCBhcmUgbmVzdGVkIHdpdGhpbiB0aG9zZSBvYnRhaW5lZCBhdCBhIGxvdy1yZXNvbHV0aW9uIGN1dDsgdGhpcyBjYW4gYmUKaGVscGZ1bCBmb3IgaW50ZXJwcmV0YXRpb24uIgoKSW5kZWVkLCBsb3ctcmVzb2x1dGlvbiBjbHVzdGVycyBjYW4gdHlwaWNhbGx5IGJlIGludGVycHJldGVkIGFzIHN1cGVyLWxldmVsIGNlbGwKdHlwZXMsIGxpa2UgaW1tdW5lIGNlbGxzLCBuZXVyb24gY2VsbHMgb3IgZW5kb3RoZWxpYWwgY2VsbHMuIEhpZ2hlciByZXNvbHV0aW9uCmNsdXN0ZXJzIGNvcnJlc3BvbmQgd2l0aCBhIGhpZ2hlciBiaW9sb2dpY2FsIHJlc29sdXRpb246IGltbXVuZSBjZWxsIC0+IApseW1waG9jeXRlIC0+IFQtY2VsbCAtPiBUaDEgY2VsbC4KCkhvd2V2ZXIsIG5vdGUgdGhhdCB3ZSBjYW4gYWxzbyBvdmVyY2x1c3RlciB0aGUgZGF0YSAoc3BsaXR0aW5nIGEgaG9tb2dlbm91cyAKc2V0IG9mIGNlbGxzIGluIG11bHRpcGxlIGNsdXN0ZXJzKSwgcmVzdWx0aW5nIGluIHNwdXJpb3VzIGNlbGwgdHlwZSAKaWRlbnRpZmljYXRpb24uCgpUaGUgYGNsdXN0ZXJDZWxsc2AgZnVuY3Rpb24gb2YgdGhlIGBzY3JhbmAgbGlicmFyeSBhbHNvIGFsbG93cyBmb3IgcGVyZm9ybWluZyAKaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcuIFRoaXMgY2FuIGJlIGltcGxlbWVudGVkIGFzIGZvbGxvd3M6CgpgYGAKIyB0YWtlcyA0IG1pbnV0ZXMKbGlicmFyeShibHVzdGVyKQpoY2x1c3Quc2NlIDwtIGNsdXN0ZXJDZWxscyh4ID0gc2NlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZS5kaW1yZWQgPSAiUENBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJMVVNQQVJBTSA9IEhjbHVzdFBhcmFtKG1ldGhvZD0id2FyZC5EMiIpKQpgYGAKCkVxdWl2YWxlbnRseSwgd2UgbWF5IGFnYWluIHNwbGl0IHRoZSBwcm9jZXNzIGluIHR3byBzdGVwczoKCjEuIENvbXB1dGUgdGhlIHBhaXJ3aXNlIGRpc3RhbmNlcyBiZXR3ZWVuIGFsbCBjZWxscy4gVGhlc2UgYXJlIGJ5IGRlZmF1bHQgRXVjbGlkZWFuCmRpc3RhbmNlcyBhbmQsIGluIG9yZGVyIHRvIHJlZHVjZSBkYXRhIGNvbXBsZXhpdHkgYW5kIGluY3JlYXNlIHNpZ25hbCB0byBub2lzZSwgCndlIG1heSBwZXJmb3JtIHRoaXMgb24gdGhlIHRvcCAoMzApIFBD4oCZcywganVzdCBsaWtlIHdlIGRpZCB3aGVuIGNvbnN0cnVjdGluZyAKdGhlIFNOTiBncmFwaCBpbiBncmFwaC1iYXNlZCBjbHVzdGVyaW5nLiBDYWxjdWxhdGluZyBhIGRpc3NpbWlsYXJpdHkgbWF0cml4IAppcyBpbXBsZW1lbnRlZCBpbiB0aGUgYGRpc3RgIGZ1bmN0aW9uLgoKMi4gUGVyZm9ybSBhIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG9uIHRoZSBkaXN0YW5jZXMgZnJvbSBzdGVwIDEuIEluIGFuIAphZ2dsb21lcmF0aXZlIHByb2NlZHVyZSwgZWFjaCBjZWxsIGlzIGZpcnN0IGFzc2lnbmVkIHRvIGl0cyBvd24gY2x1c3RlciBhbmQgCnRoZW4gdGhlIGFsZ29yaXRobSBwcm9jZWVkcyBpdGVyYXRpdmVseSwgYXQgZWFjaCBzdGFnZSBqb2luaW5nIHRoZSB0d28gbW9zdCAKc2ltaWxhciBjbHVzdGVycywgY29udGludWluZyB1bnRpbCB0aGVyZSBpcyBqdXN0IGEgc2luZ2xlIGNsdXN0ZXIuIEltcGxlbWVudGVkIAppbiB0aGUgYGhjbHVzdGAgZnVuY3Rpb24uCgpOb3RlIHRoYXQgdGhlIGBoY2x1c3RgIGZ1bmN0aW9uIGFsbG93cyBmb3Igc3BlY2lmeWluZyBhICJtZXRob2QiIGFyZ3VtZW50LiAKVGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIGRpZmZlcmVudCBtZXRob2RzIGdvZXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIApzZXNzaW9uLCBidXQgYSBicmllZiBkZXNjcmlwdGlvbiBpcyBwcm92aWRlZCBpbiB0aGUgZnVuY3Rpb24gaGVscCBmaWxlLiBJbiAKdGhlIGNvbnRleHQgb2Ygc2NSTkEtc2VxLCB3ZSByZWNvbW1lbmQgdGhlIHVzZSBvZiB0aGUgYCJ3YXJkLkQyImAgbWV0aG9kLgoKYGBge3IsZXZhbD1GQUxTRX0KZGlzdHNjZSA8LSBkaXN0KHJlZHVjZWREaW0oc2NlLCAiUENBIikpICMxbWluCmhjbCA8LSBoY2x1c3QoZGlzdHNjZSwgbWV0aG9kID0gIndhcmQuRDIiKSAjM21pbgpwbG90KGhjbCwgbGFiZWxzID0gRkFMU0UpCmBgYAoKTmV4dCwgaW4gb3JkZXIgdG8gZGVyaXZlIGEgZ2l2ZW4gc2V0IG9mIGNsdXN0ZXIgbGFiZWxzLCB3ZSBuZWVkIHRvIAoiY3V0IHRoZSB0cmVlIiwgaS5lLiwgY2hvb3NlIGF0IHdoaWNoIHJlc29sdXRpb24gd2Ugd2FudCB0byAKcmVwb3J0IHRoZSAoY2VsbCB0eXBlKSBjbHVzdGVycy4gVGhpcyBjYW4gYmUgYWNoaWV2ZWQgd2l0aCB0aGUgYGN1dHJlZWAgCmZ1bmN0aW9uLiBBcyBhbiBpbnB1dCwgYGN1dHJlZWAgdGFrZXMgdGhlIGRlbmRyb2dyYW0gZnJvbSB0aGUgYGhjbHVzdGAgZnVuY3Rpb24gCmFuZCBhIHRocmVzaG9sZCB2YWx1ZSBmb3IgY3V0dGluZyB0aGUgdHJlZS4gVGhlIGxhdHRlciBtYXkgYmUgZWl0aGVyIGBrYCwgdGhlIApudW1iZXIgb2YgY2x1c3RlcnMgd2Ugd2FudCB0byByZXBvcnQsIG9yIGBoYCwgdGhlIGhlaWdodCBvZiB0aGUgZGVuZHJvZ3JhbSBhdCAKd2hpY2ggd2Ugd2FudCB0byBjdXQgdGhlIHRyZWUuCgpgYGB7cixldmFsPUZBTFNFfQojIGN1dCB0byBnZXQgMTAgY2x1c3RlcnMKY2x1c3RfaGNsX2sxMCA8LSBjdXRyZWUoaGNsLCBrID0gMTApCnRhYmxlKGNsdXN0X2hjbF9rMTApCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KIyBjdXQgdG8gZ2V0IDM5IGNsdXN0ZXJzCmNsdXN0X2hjbF9rMzkgPC0gY3V0cmVlKGhjbCwgayA9IDM5KQp0YWJsZShjbHVzdF9oY2xfazM5KQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CnNjZSRjbHVzdF9oY2xfazEwIDwtIGFzLmZhY3RvcihjbHVzdF9oY2xfazEwKQpzY2UkY2x1c3RfaGNsX2szOSA8LSBhcy5mYWN0b3IoY2x1c3RfaGNsX2szOSkKCiMgVmlzdWFsaXphdGlvbi4gQWRkIHRoZSBjbHVzdGVyIGxhYmVscyB0byB0aGUgcHJldmlvdXNseSBnZW5lcmF0ZWQgVFNORQojIGNvb3JkaW5hdGVzCnBsb3RUU05FKHNjZSwgCiAgICAgICAgIGNvbG91cl9ieT0iY2x1c3RfaGNsX2sxMCIpCgpwbG90VFNORShzY2UsIAogICAgICAgICBjb2xvdXJfYnkgPSJjbHVzdF9oY2xfazM5IikKYGBgCgojIENsdXN0ZXJpbmcgaW4gdGhlIG9yaWdpbmFsIHBhcGVyCgpXaGVuIHdlIGNvbXBhcmUgb3VyIGNsdXN0ZXIgbGFiZWxzIHdpdGggdGhvc2UgZnJvbSB0aGUgb3JpZ2luYWwgcGFwZXIsIHdlJ2xsCnNlZSB0aGF0IHRoZSBjb3JyZXNwb25kZW5jZSBpcyBub3QgZ3JlYXQuIEFzIGEgZGVtb25zdHJhdGlvbiwgSSBtYWtlIGEgdGFibGUgYW5kIAphIGhlYXRtYXAgY29tcGFyaW5nIHRoZSBsb3ctcmVzb2x1dGlvbiBjbHVzdGVyIGxhYmVscyBmcm9tIHRoZSBwYXBlciB3aXRoIG91ciAKTG91dmFpbiwgTGVpZGVuMiBhbmQgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgKGs9MTApIGxhYmVsczoKCmBgYHtyLGV2YWw9RkFMU0V9CnRhYmxlKHNjZSRjbHVzdGVyX2xvd1Jlcywgc2NlJGNsdXN0ZXJfbG91dmFpbikKYGBgCgpgYGB7cixldmFsPUZBTFNFfQp0YWJsZShzY2UkY2x1c3Rlcl9sb3dSZXMsIHNjZSRjbHVzdGVyX2xlaWRlbjIpCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KdGFibGUoc2NlJGNsdXN0ZXJfbG93UmVzLCBzY2UkY2x1c3RfaGNsX2sxMCkKYGBgCgpgYGB7cixldmFsPUZBTFNFfQpwaGVhdG1hcDo6cGhlYXRtYXAodGFibGUoc2NlJGNsdXN0ZXJfbG93UmVzLCBzY2UkY2x1c3Rlcl9sb3V2YWluKSkKCnBoZWF0bWFwOjpwaGVhdG1hcCh0YWJsZShzY2UkY2x1c3Rlcl9sb3dSZXMsIHNjZSRjbHVzdGVyX2xlaWRlbjIpKQoKcGhlYXRtYXA6OnBoZWF0bWFwKHRhYmxlKHNjZSRjbHVzdGVyX2xvd1Jlcywgc2NlJGNsdXN0X2hjbF9rMTApKQpgYGAKCkFsc28sIHdoZW4gd2UgbG9vayBhdCB0aGUgdC1TTkUgZnJvbSB0aGUgb3JpZ2luYWwgcHVibGljYXRpb24sIHdlIG9ic2VydmUKY2xlYXJseSBkaXN0aW5jdCBjbHVzdGVyczoKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9tYWNvc2tvX2ZpZ3VyZV81Qi5qcGVnIikKYGBgCgpUaGUgbWFpbiByZWFzb24gZm9yIHRoaXMgaXMgdGhhdCB0aGUgYXV0aG9ycyBvZiB0aGUgb3JpZ2luYWwgcGFwZXIgdXNlZCBxdWl0ZSBhIApkaWZmZXJlbnQgc3RyYXRlZ3kgZm9yIHBlcmZvcm1pbmcgdGhlIGZlYXR1cmUgc2VsZWN0aW9uIGFuZCBkaW1lbnNpb24gcmVkdWN0aW9uIAp0aGF0IHdlIGhhdmUgcGVyZm9ybWVkIGluIGxhYiBzZXNzaW9uIDIuCgpUbyBkZW1vbnN0cmF0ZSB0aGUgcGlwZWxpbmUgb2YgdGhlIG9yaWdpbmFsIGF1dGhvcnMsIGFuZCB0byBtYWtlIG91ciByZXN1bHRzIAptb3JlIGNvbXBhcmFibGUgdG8gdGhlaXJzLCBXZSB3aWxsIGhlcmUgbWltaWMgdGhlaXIgc3RyYXRlZ3kgZm9yIGZlYXR1cmUgCnNlbGVjdGlvbiBhbmQgY2x1c3RlcmluZy4gKipIb3dldmVyLCB3ZSB3aWxsIGRvIHRoaXMgYXBwcm94aW1hdGl2ZWx5ISoqIFdlIHdpbGwgCnRha2Ugc2ltaWxhciBzdGVwcyBjb25jZXB0dWFsbHksIGJ1dCB3aWxsIHJlbWFpbiB3aXRoaW4gdGhlIGN1cnJlbnQgCkJpb2NvbmR1Y3RvciBmcmFtZXdvcmsgYW5kIHRoZSByYW5nZSBvZiBmdW5jdGlvbnMgdGhhdCB3ZSBoYXZlIHNlZW4gaW4gdGhpcyAKbGVjdHVyZSBzZXJpZXMuCgpUaGUgYXV0aG9ycyBwZXJmb3JtZWQgdGhlIGZvbGxvd2luZyBzdGVwczoKCjEuICoqRmlsdGVyaW5nOioqIFRoZSBhdXRob3JzIGZpcnN0IGZpbHRlcmVkIHRoZSA0OSwzMDAtY2VsbCBkYXRhc2V0IHRvIHJldGFpbiAKb25seSBzaW5nbGUtY2VsbCBsaWJyYXJpZXMgd2hlcmUgYXQgbGVhc3QgOTAwIGdlbmVzIHdlcmUgZGV0ZWN0ZWQuCgoyLiAqKkZlYXR1cmUgc2VsZWN0aW9uOioqIFRoZSBhdXRob3JzIGZpcnN0IGlkZW50aWZpZWQgdGhlIHNldCBvZiBnZW5lcyB0aGF0IAp3ZXJlIG1vc3QgdmFyaWFibGUgYWNyb3NzIHRoZSBzZWxlY3RlZCBzdXNiZXQgb2YgY2VsbHMsIGFmdGVyIGNvbnRyb2xsaW5nIGZvciAKdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG1lYW4gZXhwcmVzc2lvbiBhbmQgdmFyaWFiaWxpdHkuIFRvIGRvIHRoaXMsIHRoZSAKYXV0aG9ycyBhZG9wdGVkIGEgbWFudWFsIGltcGxlbWVudGF0aW9uLgoKLT4gV2Ugd2lsbCBub3QgdXNlIHRoZSBleGFjdCBzYW1lIHN0cmF0ZWd5LCBidXQgdGhlIGBtb2RlbEdlbmVWYXItZ2V0VG9wSFZHc2AgCnN0cmF0ZWd5LCB3aGljaCBpcyBjb25jZXB0dWFsbHkgc2ltaWxhciBpbiBhZGRyZXNzaW5nIHRoZSBtZWFuLXZhcmlhbmNlIApyZWxhdGlvbnNoaXAgZHVyaW5nIGZlYXR1cmUgc2VsZWN0aW9uLgoKMy4gKipQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzOioqIFRoZSBhdXRob3JzIHBlcmZvcm1lZCBQQ0EgYWZ0ZXIgc2NhbGluZyAKdGhlIGRhdGEuIFRoZXkgbmV4dCBwZXJmb3JtZWQgYSB0ZXN0IHRvIGRldGVybWluZSBob3cgbWFueSBQQ3MgY29udHJpYnV0ZWQgCnNpZ25pZmljYW50bHkgdG8gZXhwbGFpbmluZyB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEuIEJhc2VkIG9uIHRoaXMgdGVzdCwgCnRoZXkgY29udGludWVkIHRoZSBhbmFseXNpcyBwaXBlbGluZSB3aXRoIHRoZSB0b3AgMzIgUENzLgoKNC4gKip0LVNORToqKiBOZXh0LCB0aGUgYXV0aG9ycyBwZXJmb3JtZWQgYSB0LVNORSBvbiB0aGUgdG9wIDMyIFBD4oCZcywgc2V0dGluZyAKdGhlIGBwZXJwbGV4aXR5YCBwYXJhbWV0ZXIgb2YgdGhlIHQtU05FIGFsZ29yaXRobSB0byAzMC4KCjUuICoqUHJvamVjdGlvbiBvZiByZW1haW5pbmcgY2VsbHMgYW5kIGNsdXN0ZXJpbmc6KiogRmluYWxseSwgdGhlIGF1dGhvcnMgCmFkb3B0ZWQgYSBtYW51YWxseSBpbXBsZW1lbnRlZCwgcmF0aGVyIGNvbXBsZXggc3RyYXRlZ3kgdG8gcHJvamVjdCB0aGUgCnJlbWFpbmluZyBjZWxscyAod2hlcmUgbGVzcyB0aGFuIDkwMCBkaWZmZXJlbnQgZ2VuZXMgd2VyZSBkZXRlY3RlZCkgb24gdGhlIAp0LVNORSBlbWJlZGRpbmcgb2J0YWluZWQgaW4gc3RlcCA0LiBOZXh0LCB0aGV5IGNsdXN0ZXIgdGhlIGNlbGxzIHVzaW5nIGEgCmRlbnNpdHkgY2x1c3RlcmluZyAoREJTQ0FOIGFsZ29yaXRobSkgdGhhdCB3YXMgbm90IGRpc2N1c3NlZCBpbiB0aGlzIGxlY3R1cmUgCnNlcmllcy4gQmVjYXVzZSB0aGlzIDV0aCBzdGVwIHVzZXMgdGVjaG5pcXVlcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgY291cnNlLCAKd2Ugd2lsbCBzaW1wbHkgY29udGludWUgd29ya2luZyB3aXRoIHRoZSBmaWx0ZXJlZCBkYXRhc2V0IGFuZCBwZXJmb3JtIApoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4gSG93ZXZlciwgd2UgaW5jbHVkZWQgc29tZSBjb2RlIHRoYXQgYWxsb3dzIHlvdSB0byBkbyAKc29tZXRoaW5nIHNpbWlsYXIgdG8gd2hhdCB0aGUgYXV0aG9ycyBkaWQgZm9yIHlvdXIgcmVmZXJlbmNlIChub3RlIHRoYXQgcnVubmluZyAgCnRoaXMgY29kZSByZXF1aXJlcyBpbnN0YWxsaW5nIHRoZSBgc25pZnRlcmAgUiBwYWNrYWdlLCB3aGljaCByZXF1aXJlcyBhIHdvcmtpbmcKUHl0aG9uIGFuZCBDb25kYSBpbnN0YWxsYXRpb24pLgoKYGBge3IsZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBjb2RlIGZvciBzdGVwcyAxLTQKbGlicmFyeShzY2F0ZXIpCmxpYnJhcnkoZ2VuZWZpbHRlcikKbGlicmFyeShzY3JhbikKCiMgU3RlcCAxOiBEb3duc2FtcGxpbmcKc2NlXzkwMCA8LSBzY2VbLHNjZSRkZXRlY3RlZCA+IDkwMF0KCiMgU3RlcCAyOiBGZWF0dXJlIHNlbGVjdGlvbgpzY2VfOTAwIDwtIGxvZ05vcm1Db3VudHMoc2NlXzkwMCkKZGVjXzkwMCA8LSBtb2RlbEdlbmVWYXIoc2NlXzkwMCkKaHZnXzkwMCA8LSBnZXRUb3BIVkdzKGRlY185MDAsCiAgICAgICAgICAgICAgICAgICAgICBuID0gMzc0KSAjIHNhbWUgbnVtYmVyIG9mIHRvcCBmZWF0dXJlcyBhcyBvcmlnaW5hbCBwYXBlcgoKIyBTdGVwIDM6IFBDQQpzZXQuc2VlZCgxMjM0KQpzY2VfOTAwIDwtIHJ1blBDQShzY2VfOTAwLCAKICAgICAgICAgICAgICAgICAgbmNvbXBvbmVudHMgPSAzMiwgIyBzYW1lIG51bWJlciBvZiBQQ3MgYXMgb3JpZ2luYWwgcGFwZXIKICAgICAgICAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2Z185MDAsCiAgICAgICAgICAgICAgICAgIHNjYWxlPVRSVUUpICMgc2NhbGUgdGhlIGRhdGEgbGlrZSBpbiBvcmlnaW5hbCBwYXBlcgoKIyBTdGVwIDQ6IFQtU05FCnNldC5zZWVkKDQ4NDg1NCkKc2NlXzkwMCA8LSBydW5UU05FKHNjZV85MDAsIAogICAgICAgICAgICAgICBkaW1yZWQgPSAnUENBJywKICAgICAgICAgICAgICAgbl9kaW1yZWQgPSAzMiwKICAgICAgICAgICAgICAgcGVycGxleGl0eSA9IDMwKSAjIHNhbWUgcGVycGxleGl0eSBhcyBvcmlnaW5hbCBwYXBlcgpgYGAKCmBgYAojIFN0ZXAgNSBhdXRob3JzIChqdXN0IGZvciB5b3VyIHJlZmVyZW5jZSk6IHByb2plY3QgbmV3IGNlbGxzIG9uIHQtU05FIGVtYmVkZGluZwojQmlvY01hbmFnZXI6Omluc3RhbGwoInNuaWZ0ZXIiKQpsaWJyYXJ5KHNuaWZ0ZXIpCnRzbmUxIDwtIHNuaWZ0ZXI6OmZpdHNuZShyZWR1Y2VkRGltKHNjZV85MDAsIHR5cGU9IlBDQSIpKQplbWJlZGRpbmcgPC0gcmVkdWNlZERpbShzY2VbaHZnXzkwMCxzY2UkZGV0ZWN0ZWQ+OTAwXSwgdHlwZT0iUENBIikKCmdncGxvdCgpICsKICBhZXModHNuZTFbLCAxXSwgdHNuZTFbLCAyXSwgY29sb3VyID0gYXMuZmFjdG9yKHNjZVssc2NlJGRldGVjdGVkPjkwMF0kY2x1c3RlcikpICsKICBnZW9tX3BvaW50KHBjaCA9IDE5KSArCiAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKG5hbWUgPSAiQ2x1c3RlciIpICsKICBsYWJzKHggPSAidC1TTkUgMSIsIHkgPSAidC1TTkUgMiIpICsKICB0aGVtZV9idygpCgpuZXdfY29vcmRzIDwtIHByb2plY3QodHNuZTEsIAogICAgICAgICAgICAgICAgICAgICAgbmV3ID0gcmVkdWNlZERpbShzY2VbLHNjZSRkZXRlY3RlZDw9OTAwXSwgdHlwZT0iUENBIiksIAogICAgICAgICAgICAgICAgICAgICAgb2xkID0gcmVkdWNlZERpbShzY2VbLHNjZSRkZXRlY3RlZD45MDBdLCB0eXBlPSJQQ0EiKSkKZ2dwbG90KCkgKwogICAgZ2VvbV9wb2ludCgKICAgICAgICBhZXModHNuZTFbLCAxXSwgdHNuZTFbLCAyXSwKICAgICAgICAgICAgY29sb3VyID0gYXMuZmFjdG9yKHNjZVssc2NlJGRldGVjdGVkPjkwMF0kY2x1c3RlciksCiAgICAgICAgICAgIHNoYXBlID0gImVtYmVkZGluZyIKICAgICAgICApCiAgICApICsKICAgIGdlb21fcG9pbnQoCiAgICAgICAgYWVzKG5ld19jb29yZHNbLCAxXSwgbmV3X2Nvb3Jkc1ssIDJdLCAKICAgICAgICAgICAgY29sb3VyID0gYXMuZmFjdG9yKHNjZVssc2NlJGRldGVjdGVkPD05MDBdJGNsdXN0ZXIpLAogICAgICAgICAgICBzaGFwZSA9ICJwcm9qZWN0aW9uIgogICAgICAgICkKICAgICkgKwogICAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKG5hbWUgPSAiQ2VsbCB0eXBlIikgKwogICAgc2NhbGVfc2hhcGVfZGlzY3JldGUobmFtZSA9IE5VTEwpICsKICAgIGxhYnMoeCA9ICJ0LVNORSAxIiwgeSA9ICJ0LVNORSAyIikgKwogICAgdGhlbWVfYncoKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CiMgU3RlcCA1IGZvciB1czogcGVyZm9ybSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZwpkaXN0c2NlIDwtIGRpc3QocmVkdWNlZERpbShzY2VfOTAwLCAiUENBIikpCmhjbCA8LSBoY2x1c3QoZGlzdHNjZSwgbWV0aG9kID0gIndhcmQuRDIiKQoKY2x1c3RfaGNsX2sxMCA8LSBjdXRyZWUoaGNsLCBrID0gMTApCmNsdXN0X2hjbF9rMzkgPC0gY3V0cmVlKGhjbCwgayA9IDM5KQoKc2NlXzkwMCRjbHVzdF9oY2xfazEwIDwtIGFzLmZhY3RvcihjbHVzdF9oY2xfazEwKQpzY2VfOTAwJGNsdXN0X2hjbF9rMzkgPC0gYXMuZmFjdG9yKGNsdXN0X2hjbF9rMzkpCmBgYAoKVmlzdWFsaXplIG91ciBsYWJlbHMgYW5kIGNvbXBhcmUgd2l0aCBvcmlnaW5hbCBsYWJlbHMKCmBgYHtyLGV2YWw9RkFMU0V9CnBsb3RUU05FKHNjZV85MDAsCiAgICAgICAgIGNvbG91cl9ieSA9ICJjbHVzdF9oY2xfazEwIiwKICAgICAgICAgdGV4dF9ieSA9ICJjbHVzdF9oY2xfazEwIikKCnBsb3RUU05FKHNjZV85MDAsCiAgICAgICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyX2xvd1JlcyIsCiAgICAgICAgIHRleHRfYnkgPSAiY2x1c3Rlcl9sb3dSZXMiKQpgYGAKCkZvciB0aGUgbG93IHJlc29sdXRpb24gbGFiZWxzLCB3ZSBvYnNlcnZlIGEgc3Ryb25nIGNvcnJlc3BvbmRlbmNlIGJldHdlZW4gb3VyCmNsdXN0ZXJpbmcgYW5kIHRoYXQgb2YgdGhlIG9yaWdpbmFsIGF1dGhvcnMuIE1vcmUgc3BlY2lmaWNhbGx5OgoKLSBUaGUgb3JpZ2luYWwgcm9kcyBjb3JyZXNwb25kIHdpdGggb3VyIGdyb3VwIDUKLSBUaGUgb3JpZ2luYWwgY29uZXMgY29ycmVzcG9uZCB3aXRoIG91ciBncm91cCAyCi0gVGhlIG9yaWdpbmFsIHBlcmljeXRlcyBhbmQgdmFzY3VsYXIgZW5kb3RoZWxpdW0gY29ycmVzcG9uZCB3aXRoIG91ciBncm91cCA3Ci0gVGhlIG9yaWdpbmFsIG11bGxlciBnbGlhLCBhc3Ryb2N5dGVzLCBmaWJyb2JsYXN0cyBhbmQgbWljcm9nbGlhICh3aGljaCBhcmUgCnNpbWlsYXIgY2VsbCB0eXBlcywgc2VlIHBhcGVyIGZpZ3VyZSA1RCkgY29ycmVzcG9uZCB3aXRoIG91ciBncm91cCA4Ci0gVGhlIG9yaWdpbmFsIGdhbmdsaW9uIGFuZCBob3Jpem9udGFsIGNlbGxzICh3aGljaCBhcmUgc2ltaWxhciBjZWxsIHR5cGVzLCAKc2VlIHBhcGVyIGZpZ3VyZSA1RCkgY29ycmVzcG9uZCB3aXRoIG91ciBncm91cCAxCi0gVGhlIG9yaWdpbmFsIGJpcG9sYXIgY2VsbHMgYXJlIHNwbGl0IG91dCBiZXR3ZWVuIG91ciBncm91cHMgNCBhbmQgMTAKLSBUaGUgb3JpZ2luYWwgYW1hY3JpbmUgY2VsbHMgY29ycmVzcG9uZCB3aXRoIG91ciBncm91cHMgMywgNiBhbmQgOQoKYGBge3IsZXZhbD1GQUxTRX0KcGxvdFRTTkUoc2NlXzkwMCwKICAgICAgICAgY29sb3VyX2J5ID0gImNsdXN0X2hjbF9rMzkiLAogICAgICAgICB0ZXh0X2J5ID0gImNsdXN0X2hjbF9rMzkiKQoKcGxvdFRTTkUoc2NlXzkwMCwKICAgICAgICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiLAogICAgICAgICB0ZXh0X2J5ID0gImNsdXN0ZXIiKQpgYGAKCkkgd2lsbCBub3QgbWFrZSBhbiBleHRlbnNpdmUgY29tcGFyaXNvbiBoZXJlLiBIb3dldmVyLCBub3RlIHRoYXQgd2Ugc2VlCnNldmVyYWwgc2NlbmFyaW9zOgoKMS4gQ2x1c3RlcnMgdGhhdCBjb3JyZXNwb25kIG9uZS1vbi1vbmUgKGUuZy4sIG91ciBjbHVzdGVyIDE5IHdpdGggY2x1c3RlciAxNiBvZgp0aGUgYXV0aG9ycykKCjIuIFdlIHNwbGl0IHVwIHNvbWUgY2x1c3RlcnMgaW4gbXVsdGlwbGUgc2V0cyAoZS5nLiwgb3VyIGNsdXN0ZXJzIDE2LCAxOCBhbmQKMzIpIHRoYXQgYXJlIGp1c3QgMSBjbHVzdGVyIGluIHRoZSBhbmFseXNpcyBvZiB0aGUgYXV0aG9ycyAoY2x1c3RlciAzNCkKCjMuIFdlIGFnZ3JlZ2F0ZSBzb21lIGNsdXN0ZXJzIGludG8gMSAoZS5nLiBjbHVzdGVyIDE0KSB0aGF0IGFyZSBzcGxpdCB1cCBpbiB0aGUKb3JpZ2luYWwgcmVzdWx0cyAoY2x1c3RlcnMgMzcgYW5kIDM4KQoKKipPdmVyYWxsLCB3ZSBvYnNlcnZlIGEgcmF0aGVyIHN0cm9uZyBjb3JyZXNwb25kZW5jZSBiZXR3ZWVuIG91ciBjbHVzdGVycyBhbmQqKgoqKnRob3NlIG9mIHRoZSBhdXRob3JzLioqIEFsc28gbm90ZSB0aGF0IG91ciB0LVNORSB2aXN1YWxpemF0aW9uIG5vdyByZXNlbWJsZXMKdGhlIHQtU05FIG1hcCBvZiB0aGUgb3JpZ2luYWwgYXV0aG9ycyBtdWNoIG1vcmUgY2xvc2VseToKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9tYWNvc2tvX2ZpZ3VyZV81Qi5qcGVnIikKYGBgCgojIENlbGwgdHlwZSBhbm5vdGF0aW9uCgojIyBTdXBlcnZpc2VkOiB1c2luZyBhIGxpbWl0ZWQgc2V0IG9mIGtub3duIG1hcmtlcnMKCkluIHRoZSBwdWJsaWNhdGlvbiwgdGhlIGF1dGhvcnMgYWltZWQgdG8gaWRlbnRpZnkgdGhlIGRpZmZlcmVudCBjbHVzdGVycyBpbiB0aGUKZGF0YSBieSB1c2luZyBhIHNldCBvZiAxMiB3ZWxsLWtub3duIG1vbGVjdWxhciBtYXJrZXJzOyBnZW5lcyBmb3Igd2hpY2ggdGhlCmV4cHJlc3Npb24gcHJvZmlsZSBpcyB0eXBpY2FsbHkgdmVyeSBzcGVjaWZpYywgaS5lLiwgaGlnaGx5IGV4cHJlc3NlZCBvbmx5IGluCm9uZSBzcGVjaWZpYyBjZWxsIHR5cGUuIFRoZXkgdXNlZCB0aGUgZm9sbG93aW5nIG1hcmtlcnM6IAoKYGBge3IsZXZhbD1GQUxTRX0KbWFya2VycyA8LSBjKCJMSFgxIiwgIlNMQzE3QTYiLCJQQVg2IiwiR0FEMSIsIlNMQzZBOSIsIk9QTjFNVyIsIlZTWDIiLAogICAgICAgICAgICAgIlJMQlAxIiwgIkdGQVAiLCAiUEVDQU0xIiwgIktDTko4IiwiQ1gzQ1IxIikKYGBgCgpXZSB3aWxsIGhlcmUgdmlzdWFsaXplIHRoZSBleHByZXNzaW9uIG9mIHRoZXNlIG1hcmtlcnMgaW4gdC1TTkUgc3BhY2UuCkZpcnN0LCB3ZSBjcmVhdGUgYSAiYmFzZWxpbmUiIGZpZ3VyZSwgZGlzcGxheWluZyBlYWNoIGNlbGwgaW4gMkQgc3BhY2UsIGNvbG9yZWQKaW4gYmxhY2suCgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmdnX2hscF9kYXRhIDwtIGRhdGEuZnJhbWUoeCA9IHJlZHVjZWREaW0oc2NlXzkwMCwgdHlwZSA9ICJUU05FIilbLDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByZWR1Y2VkRGltKHNjZV85MDAsIHR5cGUgPSAiVFNORSIpWywyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyID0gc2NlXzkwMCRjbHVzdGVyKQpnZ19iYXNlIDwtIGdncGxvdChkYXRhID0gZ2dfaGxwX2RhdGFbIWlzLm5hKHNjZV85MDAkY2x1c3RlciksXSwKICAgICAgICAgICAgICAgICAgYWVzKHggPSB4LCB5ID0geSwpKSArCiAgICBnZW9tX3BvaW50KHNpemU9MC40KSArCiAgICB0aGVtZV9idygpICsKICAgIHhsYWIoIlRTTkUgMSIpICsKICAgIHlsYWIoIlRTTkUgMiIpCmdnX2Jhc2UKYGBgCgpOZXh0LCB3ZSBvYnRhaW4gdGhlIGNvdW50cyBvZiB0aGUgMTIgcHJlLXNlbGVjdGVkIG1hcmtlciBnZW5lcyBmb3IgYWxsIGNlbGxzLgoKYGBge3IsZXZhbD1GQUxTRX0KbWFya2VyX2NvdW50cyA8LSBhc3NheXMoc2NlXzkwMCkkY291bnRzW21hcmtlcnMsXQptYXJrZXJfY291bnRzIDwtIGFzLm1hdHJpeCh0KG1hcmtlcl9jb3VudHMpKQpgYGAKCkZpbmFsbHksIHdlIG1ha2Ugb25lIGZpZ3VyZSBmb3IgZWFjaCBvZiB0aGUgMTIgbWFya2VyIGdlbmVzLiBUaGUgaWRlYSBpcyB0bwpnaXZlIGVhY2ggY2VsbCB0aGF0IGhhcyBhIG5vbi16ZXJvIGV4cHJlc3Npb24gb2YgdGhlIG1hcmtlciAoaS5lLiwgZm9yIHdoaWNoCnRoZSBtYXJrZXIgaXMgZXhwcmVzc2VkKSBhIHJlZCBjb2xvcmluZy4KCmBgYHtyLGV2YWw9RkFMU0V9Cm1hcmtlcl9jb3VudHNfYmluYXJ5IDwtIG1hcmtlcl9jb3VudHMKbWFya2VyX2NvdW50c19iaW5hcnlbd2hpY2gobWFya2VyX2NvdW50c19iaW5hcnkgPiAwKV0gPC0gMQoKZm9yIChpIGluIHNlcV9hbG9uZyhtYXJrZXJzKSkgewogICAgZ2cgPC0gZ2dwbG90KGRhdGEgPSBnZ19obHBfZGF0YVshaXMubmEoc2NlXzkwMCRjbHVzdGVyKSxdLAogICAgICAgICAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHkpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhcy5mYWN0b3IobWFya2VyX2NvdW50c19iaW5hcnlbLGldKVshaXMubmEoc2NlXzkwMCRjbHVzdGVyKV0pLCBzaXplID0gMC40KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsInJlZCIpKSArCiAgICAgICAgeGxhYigiVFNORSAxIikgKwogICAgICAgIHlsYWIoIlRTTkUgMiIpICsKICAgICAgICBnZ3RpdGxlKGNvbG5hbWVzKG1hcmtlcl9jb3VudHNfYmluYXJ5KVtpXSkgKwogICAgICAgIHRoZW1lX2J3KCkgKyAKICAgICAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQogICAgcHJpbnQoZ2cpCn0KYGBgCgpgYGB7cixldmFsPUZBTFNFfQojIFZpc3VhbGl6ZSBvdXIgY2x1c3RlcnMKcGxvdFRTTkUoc2NlXzkwMCwKICAgICAgICAgY29sb3VyX2J5ID0gImNsdXN0X2hjbF9rMzkiLAogICAgICAgICB0ZXh0X2J5ID0gImNsdXN0X2hjbF9rMzkiKQpgYGAKCkZyb20gZmlndXJlIDVELCB3ZSBvYnRhaW4gdGhlIG1hcmtlci1jZWxsIHR5cGUgcmVsYXRpb25zaGlwLCAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9tYWNvc2tvX2ZpZ3VyZV81RC5qcGVnIikKYGBgCgphbmQgZHJhdyB0aGUgZm9sbG93aW5nIGNvbmNsdXNpb25zOgoKLSBUaGUgKkxIWDEqIG1hcmtlciBpcyBzcGVjaWZpYyBmb3IgaG9yaXpvbnRhbCBjZWxscy4gQXMgc3VjaCwgb3VyICoqY2x1c3RlciAzKioKY29ycmVzcG9uZHMgdG8gKipob3Jpem9udGFsIGNlbGxzKiouCgotIFRoZSAqU0xDMTdBNiogbWFya2VyIGlzIHNwZWNpZmljIGZvciBnYW5nbGlvbiBjZWxscy4gQXMgc3VjaCwgb3VyICoqY2x1c3RlciAxKioKY29ycmVzcG9uZHMgdG8gKipnYW5nbGlvbiBjZWxscyoqLgoKLSBUaGUgKlBBWDYqIG1hcmtlciBpcyBub3Qgc3BlY2lmaWMuIEl0IGlzIGV4cHJlc3NlZCBpbiBob3Jpem9udGFsIGNlbGxzLCBnYW5nbGlvbgpjZWxscywgYW1hY3JpbmUgY2VsbHMgKGFsbCBzdWJ0eXBlcyksIG11bGxlciBnbGlhIGNlbGxzIGFuZCBmaWJyb2JsYXN0cy4gRmlyc3QsCnRoaXMgcmVjb25maXJtcyBvdXIgcHJldmlvdXMgc3RhdGVtZW50cyBhYm91dCBjbHVzdGVycyAzIGFuZCAxLCB3aGljaCBpbmRlZWQgCnNob3VsZCBhbHNvIGV4cHJlc3MgKlBBWDYqLiBTZWNvbmQsIG1hbnkgY2x1c3RlcnMgYXJlIHBvdGVudGlhbCBjYW5kaWRhdGVzIGZvciAKYmVpbmcgYW55IG9mIHRoZSByZW1haW5pbmcgY2VsbCB0eXBlcyB0byBleHByZXNzICpQQVg2Ki4gVGhlIG5leHQgbWFya2VyIHNob3VsZApwb2ludCB1cyB0byB0aGVpciBtb3JlIGV4YWN0IGlkZW50aXR5LgoKLSBUaGUgKkdBRDEqIG1hcmtlciBpcyBzcGVjaWZpYyBmb3IgYW1hY3JpbmUgY2VsbHMuIEluIGFkZGl0aW9uLCBpdCBpcyBvbmx5IApleHByZXNzZWQgaW4gIkdBQkFlcmdpYyIgYW1hY3JpbmUgY2VsbHMsIHdoaWNoIGFmZmVjdCB0aGUgbmV1cm90cmFuc21pdHRlciBHQUJBLgpBcyBzdWNoLCAqKmNsdXN0ZXJzIDQsIDUsIDYsIDgsIDksIDExLCAxMywgMjksIDMwIGFuZCAzMSBhcmUgR0FCQWVyZ2ljKioKKiphbWFjcmluZSBjZWxscyoqLiBDb252ZXJzZWx5LCB0aGUgY2x1c3RlcnMgdGhhdCBleHByZXNzICpQQVg2KiBidXQgbm90ICpHQUQxKgooY2x1c3RlcnMgMTYsIDE4LCAxOSwgMjIsIDIzLCAyNCwgMjYsIDMyLCAzNikgYXJlIGVpdGhlciBub24tR0FCQWVyZ2ljIAphbWFjcmluZSBjZWxscywgbXVsbGVyIGdsaWEgY2VsbHMgb3IgZmlicm9ibGFzdHMuCgotIFRoZSAqU0xDNkE5KiBtYXJrZXIgaXMgc3BlY2lmaWMgZm9yIGdseWNpbmVyZ2ljIGFtYWNyaW5lIGNlbGxzLCBpLmUuLCAKYWZmZWN0aW5nIHRoZSBuZXVyb3RyYW5zbWl0dGVyIGdseWNpbmUuIEFzIHN1Y2gsICoqY2x1c3RlcnMgMTksIDIyLCAyMywgMjYgYW5kIDM2KioKYXJlIHRoZSBnbHljaW5lcmdpYyBhbWFjcmluZSBjZWxscy4gSW4gYWRkaXRpb24sIHdlIGhhdmUgY2x1c3RlcnMgMTYsIDE4LCAyNCBhbmQKMzIgdGhhdCBleHByZXNzICpQQVg2KiwgYnV0IG5vdCAqR0FEMSogbm9yICpTTEM2QTkqLiBBcyBzdWNoLCB0aGVzZSBjZWxscyAKc2hvdWxkIGJlIGVpdGhlciBtdWxsZXIgZ2xpYSBjZWxscyBvciBmaWJyb2JsYXN0cy4KCi0gVGhlICpPUE4xTVcqIG1hcmtlciBpcyBzcGVjaWZpYyBmb3IgY29uZXMuIEFzIHN1Y2gsIG91ciAqKmNsdXN0ZXJzIDIsIDM0IGFuZCAzNSoqCmNvcnJlc3BvbmRzIHRvICoqY29uZXMqKi4KCi0gVGhlICpWU1gyKiBtYXJrZXIgaXMgc3Ryb25nbHkgZXhwcmVzc2VkIGluICoqYmlwb2xhciBjZWxscyoqLiBJbiBhZGRpdGlvbiwgaXQKaXMgbG93bHkgZXhwcmVzc2VkIGluICoqbXVsbGVyIGdsaWEgY2VsbHMqKiBhbmQgKipmaWJyb2JsYXN0cyoqLiBBcyBzdWNoLCAKKipjbHVzdGVycyA3LCAxMCwgMTUsIDE2LCAxOCwgMjAsIDIxLCAyNSwgMjcsIDMyLCAzMywgMzcgYW5kIDM4Kiogc2hvdWxkIGJlbG9uZwp0byBvbmUgb2YgdGhlc2UgY2VsbCB0eXBlcy4KCi0gVGhlICpSTEJQMSogbWFya2VyIGlzIHNwZWNpZmljIGZvciBtdWxsZXIgZ2xpYSBjZWxscyBhbmQgYXN0cm9jeXRlcy4gU2luY2UKY2x1c3RlcnMgMTYsIDE4IGFuZCAzMiBleHByZXNzICpSTEJQMSogYW5kICpQQVg2KiwgYnV0IG5vdCAqR0FEMSogbm9yICpTTEM2QTkqLAp3ZSBub3cgaGF2ZSB2ZXJ5IHN0cm9uZyBldmlkZW5jZSB0aGF0IHRoZXNlIGFyZSBtdWxsZXIgZ2xpYSBjZWxscy4KCi0gVGhlICpHRkFQKiBtYXJrZXIgaXMgc3BlY2lmaWMgZm9yIGFzdHJvY3l0ZXMuIEFzIHN1Y2gsIG91ciAqKmNsdXN0ZXJzIDI0KiosCndoaWNoIGFsc28gZXhwcmVzc2VzICpSTEJQMSosIHNob3VsZCBjb3JyZXNwb25kIHRvICoqYXN0cm9jeXRlcyoqLgoKLSBUaGUgKlBFQ0FNMSogbWFya2VyIGlzIHNwZWNpZmljIGZvciBwZXJpY3l0ZXMgQXMgc3VjaCwgb3VyICoqY2x1c3RlcnMgMTQqKiwgCnNob3VsZCBjb3JyZXNwb25kIHRvICoqcGVyaWN5dGVzKiouCgotIFRoZSAqUEVDQU0xKiBtYXJrZXIgaXMgc3BlY2lmaWMgZm9yIHZhc2N1bGFyIGVuZG90aGVsaXVtIGNlbGxzLiBBcyBzdWNoLCAKb3VyICoqY2x1c3RlcnMgMTQqKiwgc2hvdWxkIGNvcnJlc3BvbmQgdG8gKip2YXNjdWxhciBlbmRvdGhlbGl1bSBjZWxscyoqLgoKLSBUaGUgKktDTko4KiBtYXJrZXIgaXMgc3BlY2lmaWMgZm9yIHBlcmljeXRlcy4gQXMgc3VjaCwgb3VyICoqY2x1c3RlcnMgMTQqKiwgCnNob3VsZCBjb3JyZXNwb25kIHRvICoqcGVyaWN5dGVzKiouIE5vdGUgdGhhdCBpdCB0aHVzIGFwcGVhcnMgdGhhdCBvdXIgCmNsdXN0ZXJpbmcgcHJvY2VkdXJlIHdhcyBub3QgYWJsZSB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gdmFzY3VsYXIgZW5kb3RoZWxpdW0gCmNlbGxzIGFuZCBwZXJpY3l0ZXMsIHdoaWNoIGJvdGggYXJlIGxpa2VseSB0byBiZSBwcmVzZW50IGluIGNsdXN0ZXIgMTQhCgotIFRoZSAqQ1gzQ1IxKiBtYXJrZXIgaXMgc3BlY2lmaWMgZm9yIG1pY3JvZ2xpYS4gSG93ZXZlciwgd2UgZG8gbm90IHNlZW0gdG8Kb2JzZXJ2ZSBhbnkgY2VsbHMgdGhhdCB3ZXJlIGlkZW50aWZpZWQgYXMgbWljcm9nbGlhIGJ5IHRoZSBvcmlnaW5hbCBwdWJsaWNhdGlvbi4KCi0gTGFzdCBidXQgbm90IGxlYXN0LCB0aGUgYXV0aG9ycyBoYXZlIGlkZW50aWZpZWQgdGhlIHJvZCBjZWxscyBhcyBjZWxscyB0aGF0CmRvIG5vdCBleHByZXNzIGFueSBvZiB0aGUgYWZvcmVtZW50aW9uZWQgbWFya2Vycy4gQXMgc3VjaCwgd2UgYXJlIGFibGUgdG8KaWRlbnRpZnkgb3VyICoqY2x1c3RlcnMgMTIgYW5kIDE3KiogYXMgKipyb2QgY2VsbHMqKi4KCiMjIFN1cGVydmlzZWQ6IFVzaW5nIG1hcmtlciBnZW5lcyBkZXRlY3RlZCBmcm9tIHRoaXMgZGF0YQoKU29tZXRpbWVzIGl0IHdpbGwgYmUgdmVyeSBkaWZmaWN1bHQgdG8gc2V0IHVwIGEgcGFuZWwgb2Yga25vd24gbWFya2VyIGdlbmVzIAp0aGF0IHdvdWxkIGFsbG93IHVzIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gYWxsIGNlbGwgdHlwZXMgaW4gb3VyIGRhdGFzZXQuCkZvciBpbnN0YW5jZSwgc29tZXRpbWVzIHdlIG1heSBub3Qga25vdyBpbiBhZHZhbmNlIHdoaWNoIGNlbGwgdHlwZXMgdG8gZXhwZWN0LApvciB3ZSBtYXkgbm90IGhhdmUgZ29vZCBpbmZvcm1hdGlvbiBvbiByZWxldmFudCBtYXJrZXJzIChpZiB0aGUgc3R1ZGllZCBzeXN0ZW0KaXMgbm90IHdlbGwga25vd24pLgoKQW4gYWx0ZXJuYXRpdmUgc3RyYXRlZ3kgaXMgdG8gaWRlbnRpZnkgdGhlIGdlbmVzIHRoYXQgZHJpdmUgc2VwYXJhdGlvbiBiZXR3ZWVuIApjbHVzdGVycy4gVGhlc2UgbWFya2VyIGdlbmVzIGFsbG93IHVzIHRvIGFzc2lnbiBiaW9sb2dpY2FsIG1lYW5pbmcgdG8gZWFjaCAKY2x1c3RlciBiYXNlZCBvbiB0aGVpciBmdW5jdGlvbmFsIGFubm90YXRpb24uIFRoaXMgc3RyYXRlZ3kgaXMgcmVmZXJyZWQgdG8gYXMgCioqbWFya2VyIGdlbmUgZGV0ZWN0aW9uKiouIAoKVGhlIG1vc3Qgc3RyYWlnaHRmb3J3YXJkIGFwcHJvYWNoIHRvIG1hcmtlciBnZW5lIGRldGVjdGlvbiBpbnZvbHZlcyB0ZXN0aW5nIApmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKSBiZXR3ZWVuIGNsdXN0ZXJzLiBJZiBhIGdlbmUgaXMgc3Ryb25nbHkgREUgCmJldHdlZW4gY2x1c3RlcnMsIGl0IGlzIGxpa2VseSB0byBoYXZlIGRyaXZlbiB0aGUgc2VwYXJhdGlvbiBvZiBjZWxscyBpbiB0aGUgCmRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4gVGhlIGdlbmVyYWwgc3RyYXRlZ3kgaXMgdG8gY29tcGFyZSBlYWNoIHBhaXIgb2YgY2x1c3RlcnMgCmFuZCBjb21wdXRlIHNjb3JlcyBxdWFudGlmeWluZyB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIGV4cHJlc3Npb24gZGlzdHJpYnV0aW9ucyAKYmV0d2VlbiBjbHVzdGVycy4gVGhlIHNjb3JlcyBmb3IgYWxsIHBhaXJ3aXNlIGNvbXBhcmlzb25zIGludm9sdmluZyBhIApwYXJ0aWN1bGFyIGNsdXN0ZXIgYXJlIHRoZW4gY29uc29saWRhdGVkIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZSBmb3IgdGhhdCAKY2x1c3Rlci4gVGhpcyBhcHByb2FjaCBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgYHNjb3JlTWFya2Vyc2AgZnVuY3Rpb24gb2YgdGhlCmBzY3JhbmAgcGFja2FnZS4KCmBgYHtyLGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc2NyYW4pCm1hcmtlci5pbmZvIDwtIHNjb3JlTWFya2VycyhzY2VfOTAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjZV85MDAkY2x1c3RfaGNsX2szOSkKbWFya2VyLmluZm8gIyBvbmUgZGF0YWZyYW1lIGZvciBlYWNoIG9mIHRoZSAzOSBjbHVzdGVycwpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CmNvbG5hbWVzKG1hcmtlci5pbmZvW1siMSJdXSkgIyBzdGF0aXN0aWNzIGZvciBjbHVzdGVyIDEuCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KaGVhZChtYXJrZXIuaW5mb1tbIjEiXV0pCmBgYAoKV2Ugb2JzZXJ2ZSBzZXZlcmFsIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgZWFjaCBnZW5lIGluIHRoZSBkYXRhZnJhbWUgZm9yCmNsdXN0ZXIgMS4gV2UgaGlnaGxpZ2h0IGEgZmV3OgoKLSBgc2VsZi5hdmVyYWdlYDogdGhlIGF2ZXJhZ2UgbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBvZiB0aGUgZ2VuZSBpbiB0aGUKdGFyZ2V0IGNsdXN0ZXIgKGNsdXN0ZXIgMSkKCi0gYG90aGVyLmF2ZXJhZ2VgOiB0aGUgYXZlcmFnZSBsb2ctbm9ybWFsaXplZCBleHByZXNzaW9uIG9mIHRoZSBnZW5lIGluIGFsbCB0aGUKb3RoZXIgY2x1c3RlcnMgKGNsdXN0ZXJzIDItMzkpCgotIGBzZWxmLmRldGVjdGVkYDogdGhlIGZyYWN0aW9uIG9mIGNlbGxzIGluIHdoaWNoIHRoZSBnZW5lIHdhcyBleHByZXNzZWQgaW4gdGhlCnRhcmdldCBjbHVzdGVyIChjbHVzdGVyIDEpCgotIGBvdGhlci5kZXRlY3RlZGA6IHRoZSBmcmFjdGlvbiBvZiBjZWxscyBpbiB3aGljaCB0aGUgZ2VuZSB3YXMgZXhwcmVzc2VkIGluIGFsbAp0aGUgb3RoZXIgY2x1c3RlcnMgKGNsdXN0ZXIgMi0zOSkKCi0gYG1lYW4uQVVDYDogRnJvbSB0aGUgCltPU0NBIGJvb2sgY2hhcHRlciA2LjNdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTQvT1NDQS5iYXNpYy9tYXJrZXItZGV0ZWN0aW9uLmh0bWwjZWZmZWN0LXNpemVzLWZvci1wYWlyd2lzZS1jb21wYXJpc29ucyk6IAoiSW4gdGhlIGNvbnRleHQgb2YgbWFya2VyIGRldGVjdGlvbiwgdGhlIGFyZWEgdW5kZXIgdGhlIGN1cnZlIChBVUMpIHF1YW50aWZpZXMgCm91ciBhYmlsaXR5IHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gdHdvIGRpc3RyaWJ1dGlvbnMgaW4gYSBwYWlyd2lzZSBjb21wYXJpc29uLiAKVGhlIEFVQyByZXByZXNlbnRzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGEgcmFuZG9tbHkgY2hvc2VuIG9ic2VydmF0aW9uIGZyb20gb3VyIApjbHVzdGVyIG9mIGludGVyZXN0IGlzIGdyZWF0ZXIgdGhhbiBhIHJhbmRvbWx5IGNob3NlbiBvYnNlcnZhdGlvbiBmcm9tIHRoZSBvdGhlcgpjbHVzdGVyLiBBIHZhbHVlIG9mIDEgY29ycmVzcG9uZHMgdG8gdXByZWd1bGF0aW9uLCB3aGVyZSBhbGwgdmFsdWVzIG9mIG91ciAKY2x1c3RlciBvZiBpbnRlcmVzdCBhcmUgZ3JlYXRlciB0aGFuIGFueSB2YWx1ZSBmcm9tIHRoZSBvdGhlciBjbHVzdGVyOyBhIAp2YWx1ZSBvZiAwLjUgbWVhbnMgdGhhdCB0aGVyZSBpcyBubyBuZXQgZGlmZmVyZW5jZSBpbiB0aGUgbG9jYXRpb24gb2YgdGhlIApkaXN0cmlidXRpb25zOyBhbmQgYSB2YWx1ZSBvZiAwIGNvcnJlc3BvbmRzIHRvIGRvd25yZWd1bGF0aW9uLiBUaGUgQVVDIGlzIApjbG9zZWx5IHJlbGF0ZWQgdG8gdGhlIFUgc3RhdGlzdGljIGluIHRoZSBXaWxjb3hvbiByYW5rZWQgc3VtIHRlc3QgKGEuay5hLiwgCk1hbm4tV2hpdG5leSBVLXRlc3QpLiIgQXMgc3VjaCwgdGhpcyBhIHZlcnkgaW50ZXJlc3RpbmcgY29sdW1uIHRvIHVzZSBmb3IKc2VsZWN0aW5nIG1hcmtlciBnZW5lcy4KCkJhc2VkIG9uIHRoZSBgbWVhbi5BVUNgIHN0YXRpc3RpYywgd2UgbWF5IG5vdyBpbnNwZWN0IHRoZSB0b3AxMCBtYXJrZXJzIHRvCmRpc3Rpbmd1aXNoIGJldHdlZW4gY2VsbHMgb2YgY2x1c3RlciAxIGFuZCBjZWxscyBvZiB0aGUgb3RoZXIgY2x1c3RlcnM6CgpgYGB7cixldmFsPUZBTFNFfQpjaG9zZW4gPC0gbWFya2VyLmluZm9bWyIxIl1dCm9yZGVyZWQgPC0gY2hvc2VuW29yZGVyKGNob3NlbiRtZWFuLkFVQywgZGVjcmVhc2luZz1UUlVFKSxdCmhlYWQob3JkZXJlZFssYygxOjQsMTApXSxuPTEwKSAjIHNob3dpbmcgYmFzaWMgc3RhdHMgb25seSwgZm9yIGJyZXZpdHkuCmBgYAoKV2UgY2FuIGFsc28gdmlzdWFsaXplIHRoZSBsb2ctbm9ybWFsaXplZCBleHByZXNzaW9uIG9mIHRoZSB0b3AxMCBtYXJrZXJzIGluIAplYWNoIGNlbGwsIHN0cmF0aWZpZWQgb24gY2x1c3RlciBsYWJlbCwgdXNpbmcgdGhlIGBwbG90RXhwcmVzc2lvbmAgZnVuY3Rpb24Kb2YgdGhlIGBzY2F0ZXJgIHBhY2thZ2U6CgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjYXRlcikKcGxvdEV4cHJlc3Npb24oc2NlXzkwMCwgCiAgICAgICAgICAgICAgIGZlYXR1cmVzPWhlYWQocm93bmFtZXMob3JkZXJlZCksbj0xMCksIAogICAgICAgICAgICAgICB4PSJjbHVzdF9oY2xfazM5IiwgCiAgICAgICAgICAgICAgIGNvbG91cl9ieT0iY2x1c3RfaGNsX2szOSIpCmBgYAoKVHdvIGdlbmVzIHBvcCB1cCBhcyBleHRyZW1lbHkgaW50ZXJlc3RpbmcgZm9yIHVzaW5nIGFzIG1hcmtlcnMKc3BlY2lmaWMgZm9yIGNsdXN0ZXIgMTogKk5STjEqIGFuZCAqU0xDMTdBNiouIFRoZXNlIG1hcmtlcnMgYXJlIGhpZ2hseSBleHByZXNzZWQKaW4gY2x1c3RlciAxLCBidXQgaGF2ZSBhbG1vc3Qgbm8gZXhwcmVzc2lvbiBpbiBhbnkgb2YgdGhlIG90aGVyIGNsdXN0ZXJzLgoKSW50ZXJlc3RpbmdseSwgKlNMQzE3QTYqIGlzIGV4YWN0bHkgdGhlIG1hcmtlciB0aGUgb3JpZ2luYWwgYXV0aG9ycyBoYXZlIHVzZWQKYXMgYSBzcGVjaWZpYyBtYXJrZXIgZm9yIGdhbmdsaW9uIGNlbGxzISBOb3RlIHRoYXQgaWYgd2Ugd291bGQgbm90IGtub3cgdGhlCnN5c3RlbSB1bmRlciBzdHVkeSwgaS5lLiB3ZSB3ZXJlIHVuYWJsZSB0byBzZWxlY3QgdGhlICpTTEMxN0E2KiBtYXJrZXIgYmFzZWQKb24gb3VyIHByaW9yIGJpb2xvZ2ljYWwga25vd2xlZGdlLCB3ZSB3b3VsZCBoYXZlIHN1Y2Nlc3NmdWxseSBpZGVudGlmaWVkIHRoZQptYXJrZXIgdXNpbmcgdGhpcyBzdHJhdGVneS4gSW4gYSBuZXh0IHN0ZXAsIHdlIGNvdWxkIHBlcmZvcm0gYW4gaW50ZXJuZXQgc2VhcmNoCnRvIHNlZSBpZiB0aGlzIG1hcmtlciBpcyBrbm93biBvciBub3QuCgpGb3IgdGhlIHJlY29yZCwgKk5STjEqIGFsc28gaXMgYSBrbm93biBtYXJrZXIgZm9yIGlkZW50aWZ5aW5nIGdhbmdsaW9uIGNlbGxzLgoKIyMgU2VtaS1zdXBlcnZpc2VkIHVzaW5nIFNpbmdsZVIKCkEgY29uY2VwdHVhbGx5IHN0cmFpZ2h0Zm9yd2FyZCBhbm5vdGF0aW9uIGFwcHJvYWNoIGlzIHRvIGNvbXBhcmUgb3VyIGN1cnJlbnQKc2NSTkEtc2VxIGRhdGFzZXQgd2l0aCBhIHByZXZpb3VzbHkgYW5ub3RhdGVkIHJlZmVyZW5jZSBkYXRhc2V0LiBMYWJlbHMgY2FuIAp0aGVuIGJlIGFzc2lnbmVkIHRvIGVhY2ggY2VsbCBpbiB0aGUgTWFjb3NrbyBkYXRhc2V0IGJhc2VkIG9uIHRoZSAKbW9zdCBzaW1pbGFyIHJlZmVyZW5jZSBjZWxscywgZm9yIHNvbWUgZGVmaW5pdGlvbiBvZiAic2ltaWxhciIuIFRoaXMgaXMgYSAKc3RhbmRhcmQgY2xhc3NpZmljYXRpb24gY2hhbGxlbmdlIHRoYXQgY2FuIGJlIHRhY2tsZWQgYnkgc3RhbmRhcmQgbWFjaGluZSAKbGVhcm5pbmcgdGVjaG5pcXVlcyBzdWNoIGFzIHJhbmRvbSBmb3Jlc3RzIGFuZCBzdXBwb3J0IHZlY3RvciBtYWNoaW5lcy4gQW55IApwdWJsaXNoZWQgYW5kIGxhYmVsZWQgUk5BLXNlcSBkYXRhc2V0IChidWxrIG9yIHNpbmdsZS1jZWxsKSBjYW4gYmUgdXNlZCBhcyBhIApyZWZlcmVuY2UsIHRob3VnaCBpdHMgcmVsaWFiaWxpdHkgZGVwZW5kcyBncmVhdGx5IG9uIHRoZSBleHBlcnRpc2Ugb2YgdGhlIApvcmlnaW5hbCBhdXRob3JzIHdobyBhc3NpZ25lZCB0aGUgbGFiZWxzIGluIHRoZSBmaXJzdCBwbGFjZSBhbmQgdGhlIGNsb3Nlcgp0aGUgcmVmZXJlbmNlIGRhdGFzZXQgaXMgdG8gdGhlIGRhdGFzZXQgd2Ugd291bGQgbGlrZSB0byBhbm5vdGF0ZSAoZS5nLiwgCmZ1bGwtbGVuZ3RoIHZzIFVNSS1iYXNlZCBwcm90b2NvbCksIHRoZSBtb3JlIGFjY3VyYXRlIHRoZSBhbm5vdGF0aW9uIHdpbGwgdHlwaWNhbGx5IGJlLgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIHBlcmZvcm0gc3VjaCBsYWJlbCB0cmFuc2ZlciBiZXR3ZWVuIHRoZSBhbm5vdGF0ZWQKcmVmZXJlbmNlIGRhdGFzZXQgZnJvbSAKW1NoZWtoYXIgZXQgYWwuXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNlbGwuMjAxNi4wNy4wNTQpLCB3aGljaCBhbHNvIGlzIAphIHNjUk5BLXNlcSBkYXRhc2V0IHRoYXQgc3R1ZGllZCB0aGUgbW91c2UgcmV0aW5hLCBhbmQgdGhlIE1hY29za28gZGF0YXNldC4gCgpUbyBwZXJmb3JtIHRoZSBhY3R1YWwgbGFiZWwgdHJhbnNmZXIsIHdpbGwgdXNlIHRoZSBgU2luZ2xlUmAgQmlvY29uZHVjdG9yIApwYWNrYWdlLiBGb3IgZWFjaCAidGVzdCIgY2VsbCBpbiB0aGUgTWFjb3NrbyBkYXRhc2V0LCBgU2luZ2xlUmAgd2lsbDoKCjEuIENvbXB1dGUgU3BlYXJtYW4gY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdGVzdCBjZWxsIGFuZCBlYWNoIHJlZmVyZW5jZSBjZWxsLiAKVG8gaW1wcm92ZSBzaWduYWwvbm9pc2UsIG9ubHkgbWFya2VyIGdlbmVzIGlkZW50aWZpZWQgdXNpbmcgdGhlIHJlZmVyZW5jZSAKZGF0YXNldCBhcmUgdXNlZCBmb3IgdGhpcy4KCjIuIEZvciBlYWNoIGxhYmVsIChjZWxsIHR5cGUpLCBzZXQgdGhlIHNjb3JlIGFzIHRoZSAoZGVmYXVsdCBvZikgODAlIHF1YW50aWxlIG9mClNwZWFybWFuIGNvcnJlbGF0aW9ucy4KCjMuIFRoZSBwcmVkaWN0aW9uIGlzIHRoZW4gdGhlIGxhYmVsIHdpdGggdGhlIGhpZ2hlc3Qgc2NvcmUuCgpCZWZvcmUgd2UgY2FuIHVzZSBgc2luZ2xlUmAgdG8gcGVyZm9ybSBsYWJlbCB0cmFuc2Zlciwgd2Ugd2lsbCBuZWVkIHRvIGltcG9ydCwKZXhwbG9yZSAoYnJpZWYpIGFuZCB3cmFuZ2xlIHRoZSByZWZlcmVuY2UgZGF0YXNldCBieSBTaGVraGFyICpldCBhbC4qLgoKIyMjIEltcG9ydCByZWZlcmVuY2UgZGF0YQoKVGhlIGRhdGFzZXQgYnkgU2hla2hhciAqZXQgYWwuKiBjYW4gYmUgY29udmVuaWVudGx5IGltcG9ydGVkIHVzaW5nIHRoZSAKYHNjUk5Bc2VxYCBwYWNrYWdlLgoKYGBge3IsZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShzY1JOQXNlcSkKKHJlZi5kYXRhIDwtIHNjUk5Bc2VxOjpTaGVraGFyUmV0aW5hRGF0YShlbnNlbWJsID0gVFJVRSkpCmBgYAoKIyMjIEV4cGxvcmUgbWV0YWRhdGEgb2YgcmVmZXJlbmNlIGRhdGEKCmBgYHtyLGV2YWw9RkFMU0V9CmhlYWQoY29sRGF0YShyZWYuZGF0YSkpCmxldmVscyhhcy5mYWN0b3IocmVmLmRhdGEkQ0xVU1RFUikpCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KbGV2ZWxzKGFzLmZhY3RvcihyZWYuZGF0YSRgU1VCLUNMVVNURVJgKSkKYGBgCgpgYGB7cixldmFsPUZBTFNFfQojIGJvdGggaGF2ZSBleGFjdGx5IHRoZSBzYW1lIGluZm8uLi4KYWxsKHJlZi5kYXRhJGBTVUItQ0xVU1RFUmAgPT0gcmVmLmRhdGEkQ0xVU1RFUiwgbmEucm0gPSBUUlVFKQpgYGAKCiMjIyBQcm9jZXNzIHJlZmVyZW5jZSBkYXRhCgoxLiBSZW1vdmUgdW5sYWJlbGVkIGNlbGxzCgpgYGB7cixldmFsPUZBTFNFfQpzdW0oaXMubmEocmVmLmRhdGEkQ0xVU1RFUikpCnJlZi5kYXRhIDwtIHJlZi5kYXRhWywtd2hpY2goaXMubmEocmVmLmRhdGEkQ0xVU1RFUikpXQpgYGAKCjIuIFJlbW92ZSBkb3VibGV0cy9jb250YW1pbmFudCBjZWxscwoKVGhlIG9yaWdpbmFsIGF1dGhvcnMgYWxyZWFkeSBwZXJmb3JtZWQgcXVhbGl0eSBjb250cm9sOyB3ZSBoYXZlIGNlbGxzIHdpdGggCmNsdXN0ZXIgbGFiZWwgIkRvdWJsZXRzL0NvbnRhbWluYW50cyIuIExldOKAmXMgcmVtb3ZlIHRob3NlOgoKYGBge3IsZXZhbD1GQUxTRX0Kc3VtKHJlZi5kYXRhJENMVVNURVIgPT0gIkRvdWJsZXRzL0NvbnRhbWluYW50cyIpCnJlZi5kYXRhIDwtIHJlZi5kYXRhWywtd2hpY2gocmVmLmRhdGEkQ0xVU1RFUiA9PSAiRG91YmxldHMvQ29udGFtaW5hbnRzIildCmBgYAoKMy4gTWFrZSBsb3dlciByZXNvbHV0aW9uIGNlbGwgdHlwZSBsZXZlbHMgKGZvciBlYXNpZXIgaW50ZXJwcmV0YXRpb24pCgpgYGB7cixldmFsPUZBTFNFfQpyZWYuZGF0YSRDTFVTVEVSX2xvd1JlcyA8LSBmY3RfcmVjb2RlKHJlZi5kYXRhJENMVVNURVIsIAogICAgICAgICAgICJBbWFjcmluZV9jZWxscyIgPSAiQUMgKEFtYWNyaW5lIGNlbGwpIiwKICAgICAgICAgICAiQmlwb2xhcl9jZWxscyIgPSAiQkMxQSIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDMUIiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzIiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzNBIiwKICAgICAgICAgICAiQmlwb2xhcl9jZWxscyIgPSAiQkMzQiIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDNCIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDNUEgKENvbmUgQmlwb2xhciBjZWxsIDVBKSIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDNUIiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzVDIiwKICAgICAgICAgICAiQmlwb2xhcl9jZWxscyIgPSAiQkM1RCIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDNiIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDNyAoQ29uZSBCaXBvbGFyIGNlbGwgNykiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzgvOSAobWl4dHVyZSBvZiBCQzggYW5kIEJDOSkiLAogICAgICAgICAgICJDb25lcyIgPSAiQ29uZSBQaG90b3JlY2VwdG9ycyIsCiAgICAgICAgICAgIk11bGxlcl9nbGlhIiA9ICJNRyAoTXVlbGxlciBHbGlhKSIsCiAgICAgICAgICAgIlJvZF9CaXBvbGFyX2NlbGwiID0gIlJCQyAoUm9kIEJpcG9sYXIgY2VsbCkiLAogICAgICAgICAgICJSb2QgUGhvdG9yZWNlcHRvcnMiID0gIlJvZCBQaG90b3JlY2VwdG9ycyIpCmBgYAoKNC4gUmVtb3ZlIGxvd2x5IGV4cHJlc3NlZCBnZW5lcwoKYGBge3IsZXZhbD1GQUxTRX0Ka2VlcCA8LSByb3dTdW1zKGFzc2F5cyhyZWYuZGF0YSkkY291bnRzID4gMCkgPiAxMAp0YWJsZShrZWVwKQpyZWYuZGF0YSA8LSByZWYuZGF0YVtrZWVwLF0KYGBgCgo1LiBPYnRhaW4gc2FtZSBnZW5lIElEIGZvcm1hdCBhcyBpbiB0YXJnZXQgZGF0YQoKVG8gYXZvaWQgcHJvYmxlbXMgd2l0aCBkaWZmZXJlbnQgdmVyc2lvbiBvZiBnZW5lIHN5bWJvbHMsIGl0IGlzIGdvb2QgcHJhY3RpY2UKZG8gd29yayB3aXRoIHVuYW1iaWd1b3VzIGdlbmUgaWRlbnRpZmllcnMgbGlrZSB0aG9zZSBvZiBFTlNFTUJMIGluc3RlYWQuCgpgYGB7cixldmFsPUZBTFNFfQpyb3duYW1lcyhzY2VfOTAwKSA8LSByb3dEYXRhKHNjZV85MDApJGVuc2VtYmxfZ2VuZV9pZCAjIHVzZSBFTlNFTUJMIGlkZW50aWZpZXJzIGluc3RlYWQKc3VtKHJvd25hbWVzKHNjZV85MDApICVpbiUgcm93bmFtZXMocmVmLmRhdGEpKQpgYGAKCjYuIENvbXB1dGUgYGxvZ05vcm1Db3VudHNgCgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjdXR0bGUpCnJlZi5kYXRhIDwtIGxvZ05vcm1Db3VudHMocmVmLmRhdGEpCmBgYAoKIyMjIGBTaW5nbGVSYCBhdCBsb3cgcmVmZXJlbmNlIHJlc29sdXRpb24KCmBgYHtyLGV2YWw9RkFMU0V9CiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgiU2luZ2xlUiIpCmxpYnJhcnkoU2luZ2xlUikKCiMgcnVucyAybWluIDMwc2VjIGZvciBtZQpwcmVkLmxvd1JlcyA8LSBTaW5nbGVSKHRlc3QgPSBzY2VfOTAwLCAKICAgICAgICAgICAgICAgICAgICAgcmVmID0gcmVmLmRhdGEsIAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSByZWYuZGF0YSRDTFVTVEVSX2xvd1JlcywgCiAgICAgICAgICAgICAgICAgICAgIGRlLm1ldGhvZCA9ICJ3aWxjb3giKQp0YWJsZShwcmVkLmxvd1JlcyRsYWJlbHMpCmBgYAoKVXNpbmcgdGhlIGBTaW5nbGVSYCBjbGFzc2lmaWVyIHRoYXQgd2FzIHRyYWluZWQgb24gdGhlIHJlZmVyZW5jZSBkYXRhc2V0IGJ5ClNoZWtoYXIgKmV0IGFsLiosIHdlIGhhdmUgbGFiZWxlZCAyNzMxIGNlbGxzIGZyb20gdGhlIE1hY29za28gZGF0YXNldCBhcwphbWFjcmluZSBjZWxscywgMTUyOCBjZWxscyBhcyBiaXBvbGFyIGNlbGxzLCBhbmQgc28gb24uIEFnYWluIG5vdGUgdGhhdCB3ZQpwcmVkaWN0ZWQgYSBsYWJlbCBmb3IgZWFjaCBjZWxsIGluIHRoZSBNYWNvc2tvIGRhdGFzZXQgYmFzZWQgaXRzIHNpbWlsYXJpdHkKKGluIGdlbmUgZXhwcmVzc2lvbikgd2l0aCBsYWJlbGVkIGNlbGxzIGZyb20gdGhlIFNoZWtoYXIgZGF0YXNldC4KCioqTW9zdCBpbXBvcnRhbnRseSwqKiB3ZSB3YW50IHRvIGNvbXBhcmUgb3VyIHByZWRpY3RlZCBjZWxsIGxhYmVscyB3aXRoIHRoZQpsYWJlbHMgdGhhdCB3ZXJlIG9idGFpbmVkIGJ5IE1hY29rc28gKmV0IGFsLiosIHdoaWNoIHdlICpjb3VsZCogY29uc2lkZXIgdG8gYmUKZ3JvdW5kIHRydXRoIGxhYmVscyBpZiB3ZSBhc3N1bWUgdGhhdCB0aGUgYXV0aG9ycyBzdWNjZWVkZWQgaW4gdGhlaXIgbGFyZ2UgCmVmZm9ydCBvZiBhbm5vdGF0aW5nIHRoZWlyIGNlbGwgY2x1c3RlcnMgZm9yIHRoZWlyIHB1YmxpY2F0aW9uLgoKYGBge3IsZXZhbD1GQUxTRX0KdGFiIDwtIHRhYmxlKHNjZV85MDAkY2x1c3Rlcl9sb3dSZXMsIHByZWQubG93UmVzJGxhYmVscykKdGFiCmBgYAoKQnkgdGhlIG5ha2VkIGV5ZSwgd2UgaW1tZWRpYXRlbHkgb2JzZXJ2ZSBhIHN0cm9uZyBjb3JyZXNwb25kZW5jZSBiZXR3ZWVuIG91cgpwcmVkaWN0ZWQgbGFiZWxzIGFuZCB0aGUgbGFiZWxzIGZyb20gdGhlIG9yaWdpbmFsIHB1YmxpY2F0aW9uIChpLmUuLCBtb3N0CmFtYWNyaW5lIGNlbGxzIGJlaW5nIHByZWRpY3RlZCBhcyBhbWFjcmluZSBjZWxscywgbW9zdCBiaXBvbGFyIGNlbGxzIGJlaW5nCnByZWRpY3RlZCBhcyBiaXBvbGFyIGNlbGxzLCBhbmQgc28gb24pLiBCZWZvcmUgd2UgZGl2ZSBkZWVwZXIgaW50byB0aGlzLCB3ZQptYXkgdmlzdWFsaXplIG91ciByZXN1bHQgdXNpbmcgYSBoZWF0bWFwOgoKYGBge3IsZXZhbD1GQUxTRX0KcGhlYXRtYXA6OnBoZWF0bWFwKHRhYiAvIHJvd1N1bXModGFiKSkKYGBgCgp3aGVyZSB3ZSBhbHNvIGNhbiBvYnNlcnZlIHRoZSBjb3JyZXNwb25kZW5jZSBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgKHgtYXhpcykgYW5kCnRoZSBvcmlnaW5hbCAoeS1heGlzKSBsYWJlbHMuIE5vdGUgdGhhdCB3ZSBoYXZlIG5vcm1hbGl6ZWQgdGhlIHZhbHVlcyBpbiB0aGUKaGVhdG1hcCBmb3IgZWFjaCByb3csIHNvIHRoYXQgdGhlIGNvbG9yaW5nIG9mIHRoZSBjZWxscyBpbiB0aGUgbWF0cml4IGNhbiBiZQppbnRlcnByZXRlZCBhcyB0aGUgZnJhY3Rpb24gb2YgdGhlIG9yaWdpbmFsIGNlbGwgdHlwZXMgdGhhdCBnb3QgYXNzaWduZWQgdG8gCmVhY2ggb2YgdGhlIHJlZmVyZW5jZSBjYXRlZ29yaWVzIChlLmcuIGZvciByb3cgMSwgODAtOTAlIG9mIHRoZSBjZWxscyBnb3QKcHJlZGljdGVkIGFzIGFtYWNyaW5lIGNlbGxzIGFuZCB0aGUgcmVtYWluaW5nIDEwJSBnb3QgcHJlZGljdGVkIGFzIHJvZApwaG90b3JlY2VwdG9ycykuCgpPbmUgb3RoZXIgdmlzdWFsaXphdGlvbiBzdHJhdGVneSBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgYHBsb3RTY29yZUhlYXRtYXBgIG9mIHRoZQpgU2luZ2xlUmAgcGFja2FnZS4gUmVtZW1iZXIsIHRoZSBgU2luZ2xlUmAgY2xhc3NpZmllciBhc3NpZ25zIGVhY2ggdGFyZ2V0IGNlbGwKdG8gYSBjZWxsIHR5cGUgbGFiZWwgKip3aXRoIGEgY2VydGFpbiBwcm9iYWJpbGl0eSoqLiBUaGUgYHBsb3RTY29yZUhlYXRtYXBgCmZ1bmN0aW9uIHRoZW4gYWxsb3dzIHlvdSB0byBwbG90LCBmb3IgZWFjaCBjZWxsIChjb2x1bW5zKSB0aGUgYXNzaWdubWVudCBzY29yZSAKKHdoaWNoIGNhbiBiZSB0aG91Z2h0IG9mIGFzIGEgcHJvYmFiaWxpdHkpIG9mIHRoYXQgY2VsbCBiZWxvbmdpbmcgdG8gZWFjaCBvZiAKdGhlIHJlZmVyZW5jZSBsYWJlbCBjYXRlZ29yaWVzIChyb3dzKS4KCmBgYHtyLGV2YWw9RkFMU0V9CmdnIDwtIHBsb3RTY29yZUhlYXRtYXAocHJlZC5sb3dSZXNbMToyMCxdKQpnZwpgYGAKCkZvciBpbnN0YW5jZSwgd2hlbiB3ZSBsb29rIGF0IHRoZSAyMHRoIGNlbGwgKHJpZ2h0bW9zdCBjb2x1bW4pLCB3ZSBzZWUgdGhhdCAKdGhpcyBjZWxsIHdhcyBsYWJlbGVkIGFzIGEgcm9kIHBob3RvcmVjZXB0b3IuIFRoaXMgaXMgYmVjYXVzZSB0aGlzCndhcyB0aGUgbGFiZWwgdGhhdCB3YXMgYXNzaWduZWQgdG8gdGhlIGNlbGwgd2l0aCBoaWdoZXN0IGFzc2lnbm1lbnQgc2NvcmUuIFdlIGFsc28gc2VlIHRoYXQgdGhlIHNlY29uZCBtb3N0IGxpa2VseSBsYWJlbCBmb3IgdGhpcyBjZWxsIGlzCmNvbmVzLgoKRmluYWxseSwgd2UgY2FuIHVzZSB0aGVzZSBhc3NpZ25tZW50IHNjb3JlIHRvIGZpbHRlciBvdXQgY2VsbHMgdGhhdCBjb3VsZCBub3QgYmUKdW5hbWJpZ3VvdXNseSBhc3NpZ25lZCB0byBvbmUgY2VsbCB0eXBlLCBpLmUuLCBvbmx5IHJlcG9ydCB0aG9zZSBjZWxscyB0aGF0IHdlCndlcmUgYWJsZSB0byByZWxpYWJseSBhc3NpZ246CgpgYGB7cixldmFsPUZBTFNFfQpzdW1tYXJ5KGlzLm5hKHByZWQubG93UmVzJHBydW5lZC5sYWJlbHMpKQoKcmFuZ2Uocm93TWF4cyhwcmVkLmxvd1JlcyRzY29yZXMpKSAjIGFsbCBhc3NpZ25tZW50cwpyYW5nZShyb3dNYXhzKHByZWQubG93UmVzJHNjb3Jlc1shaXMubmEocHJlZC5sb3dSZXMkcHJ1bmVkLmxhYmVscyksXSkpICMgcmVsaWFibGUgYXNzaWdubWVudHMKCnRhYmxlKHNjZV85MDAkY2x1c3Rlcl9sb3dSZXNbIWlzLm5hKHByZWQubG93UmVzJHBydW5lZC5sYWJlbHMpXSwgCiAgICAgIHByZWQubG93UmVzJGxhYmVsc1shaXMubmEocHJlZC5sb3dSZXMkcHJ1bmVkLmxhYmVscyldKQpgYGAKCldlIHdpbGwgaW50ZXJwcmV0IHRoZSBjb25jb3JkYW5jZSBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgYW5kIHRoZQpvcmlnaW5hbCBsYWJlbHMgb24gdGhpcyBzdWJzZXQuIFdlIG9ic2VydmUgYSBzdHJvbmcgY29ycmVzcG9uZGVuY2UgYmV0d2VlbiB0aGUgCnByZWRpY3RlZCBsYWJlbHMgYW5kIHRoZSBsYWJlbHMgZnJvbSBNYWNva3NvICpldCBhbC4qIFdlIGNhbiByZWFkIHRoaXMgdGFibGUgCmFzIGZvbGxvd3M6CgotIEFsbW9zdCBhbGwgYW1hY3JpbmUgY2VsbHMgb2YgdGhlIE1hY29za28gZGF0YXNldCBhcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcwphbWFjcmluZSBjZWxscyAoMjAzNi8oMjAzNis3KzErNDErMSkgwrE9IDk4JSBjb3JyZWN0bHkgYXNzaWduZWQpLgoKLSBBbGwgaG9yaXpvbnRhbCBjZWxscyBhbmQgYWxsIGdhbmdsaW9uIGNlbGxzIG9mIHRoZSBNYWNvc2tvIGRhdGFzZXQgYXJlIApwcmVkaWN0ZWQgYXMgYW1hY3JpbmUgY2VsbHMuIFRoaXMgbWFrZXMgcGVyZmVjdCBzZW5zZS4gU2luY2UgdGhlCnJlZmVyZW5jZSBkYXRhc2V0IGRpZCBub3QgY29udGFpbiBob3Jpem9udGFsL2dhbmdsaW9uIGNlbGwgbGFiZWxzLCBpdCBpcyAKaW1wb3NzaWJsZSB0byBsYWJlbCB0aGUgdGFyZ2V0IGNlbGxzIHdpdGggdGhlc2UgbGFiZWxzLiBJbiBzdGVhZCwgdGhlIGNlbGxzCndlcmUgbGFiZWxlZCBhcyB0aGUgY2VsbCB0eXBlIHRoYXQgaXMgbW9zdCBjbG9zZWx5IHJlbGF0ZWQgdG8gaG9yaXpvbnRhbC8KZ2FuZ2xpb24gY2VsbHM7IHRoZSBhbWFjcmluZSBjZWxsIHR5cGUgKHNlZSBmaWd1cmUgNUQpLgoKLSBNb3N0IG9mIHRoZSBiaXBvbGFyIGNlbGxzIG9mIHRoZSBNYWNvc2tvIGRhdGFzZXQgd2VyZSBlaXRoZXIgcHJlZGljdGVkIGFzCmJpcG9sYXIgY2VsbHMgb3IgYXMgcm9kIGJpcG9sYXIgY2VsbHMuIFRoZSBhdXRob3JzIG9mIHRoZSBNYWNvc2tvIHBhcGVyIHdlcmUgbm90CmFibGUgKGluIHRoZWlyIGxvdy1yZXNvbHV0aW9uIGxhYmVsaW5nKSB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIHRoZXNlIHR3byAKc3VidHlwZXMgb2YgYmlwb2xhciBjZWxscywgYnV0IHdlIHN1Y2NlZWRlZCBpbiBkb2luZyB0aGlzIChhbHRob3VnaCB3ZSBzaG91bGQKaW4gcHJpbmNpcGxlIG5lZWQgdG8gZG91YmxlLWNoZWNrIHdpdGggbWFya2VycyB3aGV0aGVyIHRoaXMgaXMgYSB0cnVlIGJpb2xvZ2ljYWwgCnNpZ25hbCBhbmQgbm90IHNvbWUgc3B1cmlvdXMgZWZmZWN0KS4gCgotIDQ0JSAoMjc5LygyNzkrMzU0KSkgb2YgdGhlIGNvbmVzIGluIHRoZSBNYWNvc2tvIGRhdGFzZXQgYXJlIGNvcnJlY3RseSBsYWJlbGVkIAphcyBjb25lcy4gVGhlIHJlbWFpbmluZyA1NiUgb2YgdGhlIGNlbGxzIHdlcmUgbGFiZWxlZCBhcyByb2RzLiBUaGlzIG1lYW5zIHRoYXQKZWl0aGVyIHRoZSBgU2luZ2xlUmAgY2xhc3NpZmllciB3cm9uZ2Z1bGx5IGxhYmVsZWQgdGhlc2UgY29uZXMgYXMgcm9kcywgb3IgdGhlIApvcmlnaW5hbCBhdXRob3JzIG1pc2xhYmVsZWQgdGhlc2Ugcm9kcyBhcyBjb25lcy4gU2luY2Ugd2UgZG8gbm90IHJlYWxseSBoYXZlIAphIGdyb3VuZCB0cnV0aCBoZXJlLCB0aGUgYmVzdCBpZGVhIGlzIHRvIGNoZWNrIHRoaXMgdXNpbmcga25vd24gbWFya2VycyB0byAKZGlzdGluZ3Vpc2ggYmV0d2VlbiByb2RzIGFuZCBjb25lcy4gCgotIEFsbW9zdCBhbGwgTXVsbGVyIGdsaWEgY2VsbHMgb2YgdGhlIE1hY29za28gZGF0YXNldCBhcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcwpNdWxsZXIgZ2xpYSBjZWxscyAoNTY5Lyg1NjkrMikgwrE9IDEwMCUgY29ycmVjdGx5IGFzc2lnbmVkKS4gSW4gYWRkaXRpb24sIG1vc3Qgb2YKdGhlIGFzdHJvY3l0ZXMsIGZpYnJvYmxhc3QsIHZhc2N1bGFyIGVuZG90aGVsaXVtLCBwZXJpY3l0ZXMgYW5kIG1pY3JvZ2xpYSBjZWxscwphbHNvIGdvdCBsYWJlbGVkIGFzIE11bGxlciBnbGlhIGNlbGxzLiBUaGlzIG1ha2VzIHBlcmZlY3Qgc2Vuc2UuIFNpbmNlIHRoZQpyZWZlcmVuY2UgZGF0YXNldCBkaWQgbm90IGNvbnRhaW4gdGhlc2UgY2VsbCBsYWJlbHMsIGl0IHdhcyBpbXBvc3NpYmxlIHRvIGxhYmVsIAp0aGUgdGFyZ2V0IGNlbGxzIHdpdGggdGhlc2UgbGFiZWxzLiBJbiBzdGVhZCwgdGhlIGNlbGxzIHdlcmUgbGFiZWxlZCBhcyB0aGUgY2VsbCAKdHlwZSB0aGF0IGlzIG1vc3QgY2xvc2VseSByZWxhdGVkOyB0aGUgTXVsbGVyIGdsaWEgY2VsbCB0eXBlIChzZWUgZmlndXJlIDVEKS4KCi0gQWxtb3N0IGFsbCByb2QgY2VsbHMgb2YgdGhlIE1hY29za28gZGF0YXNldCBhcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcwpyb2QgY2VsbHMgKDE4MzQvKDE4MzQrNis0KzEpIMKxPSA5OSUgY29ycmVjdGx5IGFzc2lnbmVkKS4KCldlIGNhbiBub3cgYWxzbyB2aXN1YWxpemUgdGhlIHByZWRpY3RlZCBsYWJlbHMgYW5kIHRoZSBvcmlnaW5hbCBsYWJlbHMgb24gb3VyCnQtU05FLgoKYGBge3IsZXZhbD1GQUxTRX0Kc2NlXzkwMCRTaW5nbGVSX2xvd1JlcyA8LSBwcmVkLmxvd1JlcyRsYWJlbHMKCiMgb3VyIGxvdyByZXNvbHV0aW9uIGxhYmVscyBiYXNlZCBvbiByZWZlcmVuY2UKcGxvdFRTTkUoc2NlXzkwMCwKICAgICAgICAgY29sb3VyX2J5ID0gIlNpbmdsZVJfbG93UmVzIiwKICAgICAgICAgdGV4dF9ieSA9ICJTaW5nbGVSX2xvd1JlcyIsCiAgICAgICAgIHRleHRfc2l6ZSA9IDMpCgojIGxvdyByZXNvbHV0aW9uIGxhYmVscyBvZiB0aGUgYXV0aG9ycwpwbG90VFNORShzY2VfOTAwLAogICAgICAgICBjb2xvdXJfYnkgPSAiY2x1c3Rlcl9sb3dSZXMiLAogICAgICAgICB0ZXh0X2J5ID0gImNsdXN0ZXJfbG93UmVzIiwKICAgICAgICAgdGV4dF9zaXplID0gMykKYGBgCgpDb21wYXJpbmcgdGhlc2UgdHdvIGZpZ3VyZXMsIHdlIGNvbWUgdG8gdGhlIHNhbWUgY29uY2x1c2lvbiBhcyBhYm92ZSBiYXNlZCBvbiAKdGhlIHRhYmxlIGNvbXBhcmlzb24uCgpXZSBzZWUgaG93IHNvbWUgY2VsbHMgb3VyIGNsYXNzaWZpZXIgbGFiZWxkIG1vcmUgY2x1c3RlcnMgYXMgcm9kcy4gQmFzZWQgb24gdGhlIAptYXJrZXIgcmVzdWx0cyBmcm9tIHRoZSBmaXJzdCBzZWN0aW9uIG9mIGNlbGwgYW5ub3RhdGlvbiwgd2Uga25vd24gdGhhdCBvdXIKY2xhc3NpZmllciBhcyBtaXNjbGFzc2lmaWVkIHRoZXNlIChpLmUuLCB0aGUgb3JpZ2luYWwgbGFiZWxzIGFyZSBjb3JyZWN0KS4KCk9uIHRoZSBvdGhlciBoYW5kLCB3ZSB3ZXJlIGhlcmUgbmljZWx5IGFibGUgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiBiaXBvbGFyIGNlbGxzCmFuZCByb2QgYmlwb2xhciBjZWxscywgd2hpY2ggY2x1c3RlciBzZXBhcmF0ZWx5LgoKKipBbHRvZ2V0aGVyLCBsYWJlbC10cmFuc2ZlciB3YXMgcXVpdGUgc3VjY2Vzc2Z1bCwgYW5kIHdlIHdvdWxkIGhhdmUgYmVlbiBhYmxlKioKKip0byB2ZXJ5IHF1aWNrbHkgZ2V0IGhpZ2gtcXVhbGl0eSByZXN1bHRzIHVzaW5nIHRoaXMgdmVyeSB1c2VyLWZyaWVuZGx5IGFuZCoqIAoqKmZhc3QgYXBwcm9hY2ggaW1wbGVtZW50ZWQgaW4gdGhlIGBTaW5nbGVSYCBwYWNrYWdlLioqCgojIyMgQWRkZW5kdW06IGBTaW5nbGVSYCBhdCBoaWdoIHJlZmVyZW5jZSByZXNvbHV0aW9uCgpGb3IgdGhlIHNha2Ugb2YgY29tcGxldGVuZXNzLCB3ZSBtYXkgYWxzbyBwZXJmb3JtIGxhYmVsIHRyYW5zZmVyIHdpdGggYFNpbmdsZVJgCnVzaW5nIHRoZSBtb3JlIGZpbmUtZ3JhaW5lZCBsYWJlbHMgZnJvbSB0aGUgcmVmZXJlbmNlIGRhdGFzZXQuCgpgYGB7cixldmFsPUZBTFNFfQojIFJ1bnMgM21pbiAzMHNlYyBmb3IgbWUKcHJlZC5oaWdSZXMgPC0gU2luZ2xlUih0ZXN0ID0gc2NlXzkwMCwgCiAgICAgICAgICAgICAgICAgICAgIHJlZiA9IHJlZi5kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcmVmLmRhdGEkQ0xVU1RFUiwgCiAgICAgICAgICAgICAgICAgICAgIGRlLm1ldGhvZCA9ICJ3aWxjb3giKQp0YWJsZShwcmVkLmhpZ1JlcyRsYWJlbHMpCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KdGFibGUoc2NlXzkwMCRjbHVzdGVyX2xvd1JlcywgcHJlZC5oaWdSZXMkbGFiZWxzKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CnBoZWF0bWFwOjpwaGVhdG1hcCh0YWJsZShzY2VfOTAwJGNsdXN0ZXJfbG93UmVzLCBwcmVkLmhpZ1JlcyRsYWJlbHMpKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CmdnIDwtIHBsb3RTY29yZUhlYXRtYXAocHJlZC5oaWdSZXNbMToyMCxdKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CiMgbG93IGNlcnRhaW50eSBsYWJlbHM6IDc1MQpzdW1tYXJ5KGlzLm5hKHByZWQuaGlnUmVzJHBydW5lZC5sYWJlbHMpKQp0YWJsZShzY2VfOTAwJGNsdXN0ZXJfbG93UmVzWyFpcy5uYShwcmVkLmhpZ1JlcyRwcnVuZWQubGFiZWxzKV0sIAogICAgICBwcmVkLmhpZ1JlcyRsYWJlbHNbIWlzLm5hKHByZWQuaGlnUmVzJHBydW5lZC5sYWJlbHMpXSkKYGBgCgoKCmBgYHtyLGV2YWw9RkFMU0V9CnNjZV85MDAkU2luZ2xlUl9oaWdoUmVzIDwtIHByZWQuaGlnUmVzJGxhYmVscwoKIyBvdXIgaGlnaCByZXNvbHV0aW9uIGxhYmVscyBiYXNlZCBvbiByZWZlcmVuY2UKcGxvdFRTTkUoc2NlXzkwMCwKICAgICAgICAgY29sb3VyX2J5ID0gIlNpbmdsZVJfaGlnaFJlcyIsCiAgICAgICAgIHRleHRfYnkgPSAiU2luZ2xlUl9oaWdoUmVzIiwKICAgICAgICAgdGV4dF9zaXplID0gMi41KQoKCiMgaGlnaCByZXNvbHV0aW9uIGxhYmVscyBvZiB0aGUgYXV0aG9ycwpwbG90VFNORShzY2VfOTAwLAogICAgICAgICBjb2xvdXJfYnkgPSAiY2x1c3Rlcl9oaWdoUmVzIiwKICAgICAgICAgdGV4dF9ieSA9ICJjbHVzdGVyX2hpZ2hSZXMiLAogICAgICAgICB0ZXh0X3NpemUgPSAyLjUpCmBgYAoKCg==