miRNA and isomiR Differential Expression Analysis in Breast Cancer
Iris Mestres Pascual
In this study, the differential expression analysis of the results
provided by the MIRFLOWZ pipeline is performed. The main goal
is to asses if there are any miRNA species differentially expressed
across normal and tumor tissues. The conclusions are found in the main
manuscript. Here, only the workflow with step-by-step explanations is
reported.
Data Preprocessing
Data Loading and Sample Selection
The MIRFLOWZ pipeline has been ran with a specific set of
samples. Therefore, the metadata for the differential expression
analysis must contain exactly the same ones. This information is
provided in two different files, one containing data per case
and the other one with per sample information.
The per case file, contains data from NCI’s Cancer Genome
Atlas Program (TCGA). Thus, after loading the data frame, only the cases
belonging to the breast cancer (BRCA) study are kept.
raw_case_metadata <- read_excel("case_metadata.xlsx",
sheet = 1,
col_types = NULL)
case_metadata <- raw_case_metadata %>%
select(2:34) %>%
filter(type == "BRCA")
The per sample metadata is already from the TCGA-BRCA study
so, from the data set, a particular set of columns are selected; the
file name (File.Name), the case and sample ID
(Case.ID and Sample.ID respectively), and the
sample’s tissue type (Sample.Type). In addition, the file
name will be formatted as to match the sample names in the counts table
and an extra field with the sample’s plate is added.
raw_sample_metadata <- read.table("./sample_metadata.csv",
sep = "\t",
header = TRUE)
sample_metadata <- raw_sample_metadata %>%
select(c(File.Name, Case.ID, Sample.ID, Sample.Type))
for (i in 1:nrow(sample_metadata)){
sample_metadata$File.Name[i] <- substr(raw_sample_metadata$File.Name[i], 6, 28)
sample_metadata$Plate[i] <- strsplit(sample_metadata$File.Name[i], "-")[[1]][5]
}
Following the sample selection criteria used in the main report,
female cases containing solely a samples coming from a normal tissue and
another one from a primary tumor with no missing data are kept. In
addition, any case with a new tumor event or has died without tumors it
is removed. Moreover, only infiltrating ductal carcinoma bellow
Stage IIB cases are maintained. Finally, the sample’s tissue
types are modified to be labeled as either Normal or
Tumor.
sample_metadata <- sample_metadata %>%
group_by(Case.ID) %>%
filter( n() == 2 ,
any(Sample.Type == "Solid Tissue Normal"),
!Sample.Type == "Metastatic") %>%
arrange(Case.ID)
sample_metadata$Sample.Type[sample_metadata$Sample.Type == "Primary Tumor"] <- "Tumor"
sample_metadata$Sample.Type[sample_metadata$Sample.Type == "Solid Tissue Normal"] <- "Normal"
case_metadata <- case_metadata %>%
filter(bcr_patient_barcode %in% sample_metadata$Case.ID,
!(vital_status == "Dead"& tumor_status == "TUMOR FREE"),
gender == "FEMALE",
!is.na(tumor_status),
is.na(new_tumor_event_type),
race != "[Not Available]",
histological_type == "Infiltrating Ductal Carcinoma",
!ajcc_pathologic_tumor_stage %in% c("Stage IIIB",
"Stage IIIC",
"Stage IV")) %>%
select(bcr_patient_barcode,
age_at_initial_pathologic_diagnosis,
race,
ajcc_pathologic_tumor_stage,
vital_status,
tumor_status,
menopause_status,
initial_pathologic_dx_year,
birth_days_to,
death_days_to)
colnames(case_metadata)[1] <- "Case.ID"
sample_metadata <- sample_metadata %>%
filter(Case.ID %in% case_metadata$Case.ID) %>%
select(File.Name, Case.ID, Sample.Type, Plate) %>%
arrange(File.Name)
The filtered data sets are combined into a single one. The merge
leads to have samples coming from normal tissues to have a cancer stage
assigned thus, they are labeled as having no stage.
metadata <- sample_metadata %>%
inner_join(case_metadata, by = "Case.ID")
metadata <- column_to_rownames(metadata, var = "File.Name")
metadata$ajcc_pathologic_tumor_stage[metadata$Sample.Type == "Normal"] <- "None"
The sample’s count tables are joined together. There are miRNA
species present in one or several samples that are not in others. With
the merging, these counts are changed from NA to 0.
Furthermore, the sample file names are used as column names and, given
that the file names in the metadata and the counts table contain
different separator characters, the column names in the counts table are
changed to match the metadata. Finally, all the loaded information is
stored in an edgeR (Robinson, McCarthy, and
Smyth 2010).
options(scipen=999)
fnames <- list.files("./mirna_samples", full.names = T)
mirna_counts <- read.csv(fnames[1],sep = "\t", header = TRUE)
for (table in fnames[-1]){
table <- read.csv(table, header = TRUE, sep = "\t")
mirna_counts <- mirna_counts %>% full_join(table)
}
mirna_counts[is.na(mirna_counts)] <- 0
rownames(mirna_counts) <- mirna_counts[,1]
mirna_counts <- mirna_counts[,-1]
colnames(mirna_counts) <-gsub("\\.", "-", colnames(mirna_counts))
dge <- DGEList(counts = mirna_counts, group = as.factor(metadata$Sample.Type))
options(digits = 2)
Exploratory Data Analysis
In this exploratory data analysis the nature of the data is analyzed
from different perspectives as to identify trends, relationships and
possible anomalies such as outliers or batch effects.
Data Distribution
The first visualization focuses on the miRNA species counts per
sample as a way to get a glance of the count’s distribution. In the
following plot, each horizontal line represents a miRNA species. For an
easier interpretation, samples are colored.

The most notable aspect of the plot is the presence of low counts
between the different samples. Due to the MIRFLOWZ unambiguous
notation, there are very specific isomiRs present in low frequency in
just a few samples. Hence, this trend was expected. On the other hand, a
second set of miRNA species has a greater presence among the samples
with up to 1000000 counts and only four samples stand out, being the
higher count from the sample E2-A1LH-01A-11R-A14C-13 in the
hsa-miR-21-5p canonical miRNA.
From a statistical point of view, genes with consistently low counts
are very unlikely be assessed as significantly differentially expressed
because low counts do not provide enough statistical evidence for a
reliable judgement to be made. Such genes can therefore be removed from
the analysis without any loss of information.
In order to asses which miRNAs will be kept, the edgeR
function filterByExpr is used. This function bases the
filtering on count-per-million (CPM) values so as to avoid favoring
genes that are expressed in larger libraries over those expressed in
smaller libraries following a similar approach as the one used by Chen, Lun, and Smyth (2016).
mirna2keep <- filterByExpr(dge)
dge <- dge[mirna2keep, , keep.lib.sizes=F]
Furthermore, to eliminate composition biases between libraries, the
trimmed mean of M-values (TMM) variant proposed by Robinson and Oshlack (2010),
“TMM with singleton pairing” ( TMMwsp ), is applied to the data
set with the edgeR function calcNormFactors.
This variant is preferred over the normal TMM as it is intended to
perform better for data with a high proportion of zeros. As a result, an
extra field with the library scaling factors is added to the
DGEList object.
dge <- calcNormFactors(dge, method = "TMMwsp")
Principal Component Analysis
In order to accomplish a greater understanding of the data, a
Principal Component Analysis (PCA) is performed using the R
function prcomp. The PCA must be conducted over the
normalized counts which are obtained with the edgeR
function cpm. In addition, the normalized counts are
returned as their log2 value.
The main goal of this analysis is to assess whether the different
miRNA species are differentially expressed across tissues, each sample
is colored according to it. Mind that as the final plot will be
interactive, all the possible relevant information about each sample
will be displayed when hovering over it.
log_norm_mirna <- cpm(dge, log = T, normalized.lib.sizes = )
PCA <- prcomp(t(log_norm_mirna), scale. = T)
In this second visualization, three outliers are found: samples
BH-A0BZ-01A-31R-A12O-13,
BH-A1FN-01A-11R-A13P-13 and
E2-A1LH-01A-11R-A14C-13. Even if the former is the only one
that has been already spotted as such, all three are removed from the
data set. Prior to the PCA, the filtering of low expressed miRNAs and
the counts normalization are performed again in order to capture the
lacking of these miRNA species.
outliers <- c("BH-A0BZ-01A-31R-A12O-13",
"BH-A1FN-01A-11R-A13P-13",
"E2-A1LH-01A-11R-A14C-13")
dge <- dge[, !rownames(dge$samples) %in% outliers]
metadata <- metadata[!rownames(metadata) %in% outliers, ]
mirna2keep <- filterByExpr(dge)
dge <- dge[mirna2keep, , keep.lib.sizes = F]
dge <- calcNormFactors(dge, method = "TMMwsp")
log_norm_mirna <- cpm(dge, log = T, normalized.lib.sizes = T)
PCA <- prcomp(t(log_norm_mirna), scale. = T)
There is a clear tendency in the 4 different clusters. Some
parameters such as the sample’s vital status had been used in order to
assess if there is any relation between them and the clustering. At the
end, the sample’s origin plate and whether is present or not in both
kind of tissues is the cause of it.
Plates A090 and A057 exhibit this effect
the most followed by A085 (with a single sample) and
A010. From these four plates, plates A057,
A085 and A010 are the ones that were only used
for tumor samples, while plate A090 was only used for
normal tissue samples. It is important to mention that the small sample
set used in this study, could be the reason behind this effect to be so
large.
Differential Expression Analysis
It is of interest to see which miRNA species are expressed at
different levels across tissues. With this goal in mind, a linear model
is going to be fitted, to perform the differential expression
analysis.
Design Matrix and Dispersion Estimation
The first step towards the objective is to build a design matrix. The
design matrix links each tissue (columns) to the samples that belong to
it (rows). Even if a slightly batch effect has been observed due to the
plate of origin, the design in this study is considered to a one-way
design under the assumption that the difference observed is not due to
the plate of origin to be a potential confounder but an effect of the
sample size.
design_mat <- model.matrix(~0 + dge$samples$group)
colnames(design_mat) <- levels(dge$samples$group)
Normal Tumor
1 0 1
2 1 0
3 0 1
4 1 0
5 0 1
6 1 0
Due the nature of the data to be analyzed, there could be biological
variability that may turn the observed counts into noise. Estimating
this variability within the different tissues is going to help on
modeling the statistical uncertainty associated with the observed counts
and on making accurate inferences about differential expression between
them.
The dispersion estimates, are computed with the edgeR
function estimateDisp. This function uses the negative
binomial distribution to model the read counts per gene per sample. With
this function three different dispersion estimates are computed. The
first one is an empirical Bayes moderated dispersion for each individual
miRNA species (tagwise). The second one, the common dispersion,
is a global estimate averaged across all species. Finally, a predicted
dispersion for each miRNA is obtained from its abundance
(trended).
In addition, to guard the tagwise estimates from outlier
miRNAs with remarkably large or small individual dispersions, the
robust = TRUE option is used (Phipson et al.
2016). The dispersion estimates are visualized with the
edgeR function plotBCV. The biological
coefficient of variation in the y-axis, is the square root
of the negative binomial dispersion. In the x-axis the is
the mean of the log normalized counts.
dge <- estimateDisp(dge, design_mat, robust=TRUE)

A characteristic of RNA-seq studies, is the tendency for higher
negative binomial dispersions in genes with very low counts whereas the
dispersion trend decreases asymptoticaly to a constant value for genes
with higher counts (Chen, Lun, and Smyth
2016). Once more, due to the isomiRs naming strategy, the
data contains a lot of low counts even after removing the less
significant miRNA species.

In order to further account for technical variability. the negative
binomial model can be extended with quasi-likelihood methods (Lun, Chen,
and Smyth 2016) (Lund et al.
2012) . In this approach, the negative binomial dispersion
trend describes the biological variability across all miRNAs. This
overall trend is used to select the miRNA-specific variability that lay
above and below it. Individual dispersions (tagwise) are not
used.
Using the edgeR function glmQLFit several
empirical Bayes statistics such as the quasi-likelihood dispersion
trend, the squeezed quasi-likelihood dispersion estimates or the prior
degrees of freedom, as well as the estimated values of the generalized
linear model coefficients for each gene are computed. Incorporating
robust handling of zero counts, especially when the fitted values of
these counts yield useless residual degrees of freedom for the
quasi-likelihood dispersion estimation (Phipson et al.
2016), is a focal point of this function. To obtain the
squeezed quasi-likelihood dispersion estimates, the function tightens
the raw quasi-likelihood dispersion estimates to adhere to a global
trend. This serves to mitigate the uncertainty inherent in the
estimates, thereby enhancing the statistical power of subsequent tests.
Furthermore, the compression magnitude during the estimation is
contingent upon the value of the prior degrees of freedom. Substantial
prior degrees of freedom estimates indicate quasi-likelihood dispersions
with lower variability across miRNA species. In such scenario, it is
advisable to engage in robust empirical Bayes moderation. Conversely,
smaller prior degrees of freedom estimates imply a higher degree of
variability. In these cases, a milder moderation approach is more
appropriate. This modulation, ensures that the empirical Bayes procedure
adapts to the underlying characteristics of the data, yielding more
accurate and meaningful results.
Setting robust = TRUE. as to facilitate the generation
of miRNA-specific prior degrees of freedom estimates This approach
minimizes the likelihood of encountering false positives from miRNA
species with exceedingly high or low raw dispersions. Therefore, it
ensures that the analysis remains resilient against data aberrations
while amplifying its ability to identify significant differential
expression across the majority of miRNAs.
The new dispersion estimates are visualized with the
edgeR function plotQLDisp.
fit <- glmQLFit(dge, design_mat, robust=TRUE)

Differential Expression Analysis (p-value)
As a means to retrieve the upcoming results as a comparison between
normal and tumor tissues, a contrast matrix is build using the
limma (Ritchie et al.
2015) function makeContrasts.
contrast_mat <- makeContrasts(Normal - Tumor, levels = design_mat)
All the subsequent results will now be relative to normal tissues.
That is, a positive log2-fold-change (logFC) will designate
an over-expressed miRNA species in normal tissues. Conversely, a
negative logFC will denote a more highly expressed miRNA species in
tumor tissues.
To account for the uncertainty in dispersion estimation in the error
rate control, rather than the usual likelihood ratio test, the
quasi-likelihood F-tests is performed with the edgeR
function glmQLFTest. This approach leads to
p-values greater than or equal to the ones that the likelihood
ratio tests would had provided using the same negative binomial
dispersions.
Finally, the obtained p-values are submitted to a multiple
testing correction as to regulate the false discovery rate (FDR) using
the Benjamin-Hochberg method. The edgeR function
topTags provides a table with all the differentially
expressed miRNA species that have an adjusted p-value smaller
than or equal to 0.01. The decision of setting the significance level to
0.01 instead of the usual 0.05, is made to account for the small size of
the data set and the small batch effect previously seen. This stringent
significance level aims to reduce the likelihood of false positives that
the mentioned facts could originate. Note that the table with the
results is not shown as it is automatically added in the next plot.
res <- glmQLFTest(fit, contrast = contrast_mat)
top <- topTags(res, n = Inf, p.value = 0.01)
Using the Glimma function glimmaVolcano the
results of the previous analysis are displayed in an interactive
volcano-plot.
Differential Expression Analysis (fold-change)
The previous differential expression analysis, was based on
statistical significance. 2131 differentially expressed miRNA species
between the normal and tumor tissues have been found among the 3291
present in the data set. Having this huge amount of differentially
expressed miRNA species, incites narrowing down of the results to keep
only the ones that are more biologically meaningful.
This new approach focuses on spotting whether the differential
expression logFCs are significantly broad than log2(1.5) by
using the edgeR function glmTreat. This
method, prioritizes genes that are likely to have a higher biological
significance with larger changes. It is worth mentioning that
glmTreat does not set a fold change cutoff but evaluates
both, the magnitude of change of expressions values and its variability.
Thus, not only the logFC estimated value must be above the threshold,
but its whole confidence interval must.
res2 <- glmTreat(fit, contrast = contrast_mat, lfc = log2(1.5))
top2 <- topTags(res2, n = Inf, p.value = 0.01)
The final amount of differentially expressed miRNA species is 1440.
Once more, using the Glimma function
glimmaVolcano the results of the analysis are visualized in
an interactive volcano-plot.
Bibliography
Chen, Yunshun, Aaron T. L. Lun, and Gordon K. Smyth. 2016.
“From
Reads to Genes to Pathways: Differential Expression Analysis of
RNA-Seq Experiments Using Rsubread and the
edgeR Quasi-Likelihood Pipeline,” no.
5:1438 (August).
https://doi.org/10.12688/f1000research.8987.2.
Lun, Aaron T. L., Yunshun Chen, and Gordon K. Smyth. 2016.
“It’s
DE-licious: A Recipe for
Differential Expression Analyses of RNA-seq Experiments Using Quasi-Likelihood Methods
in edgeR.” In
Statistical
Genomics: Methods and
Protocols, edited by Ewy Mathé and Sean Davis,
391–416. Methods in
Molecular Biology.
New York,
NY:
Springer.
https://doi.org/10.1007/978-1-4939-3578-9_19.
Lund, Steven P., Dan Nettleton, Davis J. McCarthy, and Gordon K. Smyth.
2012.
“Detecting Differential Expression in RNA-sequence Data Using Quasi-Likelihood with
Shrunken Dispersion Estimates.” Statistical Applications in
Genetics and Molecular Biology 11 (5):
/j/sagmb.2012.11.issue-5/1544-6115.1826/1544-6115.1826.xml.
https://doi.org/10.1515/1544-6115.1826.
Phipson, Belinda, Stanley Lee, Ian J. Majewski, Warren S. Alexander, and
Gordon K. Smyth. 2016.
“Robust Hyperparameter Estimation Protects
Against Hypervariable Genes and Improves Power to Detect Differential
Expression.” The Annals of Applied Statistics 10 (2):
946–63.
https://doi.org/10.1214/16-AOAS920.
Ritchie, Matthew E., Belinda Phipson, Di Wu, Yifang Hu, Charity W. Law,
Wei Shi, and Gordon K. Smyth. 2015.
“Limma Powers Differential
Expression Analyses for RNA-sequencing and
Microarray Studies.” Nucleic Acids Research 43 (7): e47.
https://doi.org/10.1093/nar/gkv007.
Robinson, Mark D., Davis J. McCarthy, and Gordon K. Smyth. 2010.
“edgeR: A Bioconductor
Package for Differential Expression Analysis of Digital Gene Expression
Data.” Bioinformatics 26 (1): 139–40.
https://doi.org/10.1093/bioinformatics/btp616.
Robinson, Mark D., and Alicia Oshlack. 2010.
“A Scaling
Normalization Method for Differential Expression Analysis of RNA-seq Data.” Genome Biology 11
(3): R25.
https://doi.org/10.1186/gb-2010-11-3-r25.
LS0tCmJpYmxpb2dyYXBoeTogLi4vZmRwLmJpYgpsaW5rLWNpdGF0aW9uczogdHJ1ZQpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNzczogYm9vdHN0cmFwLmNzcwogICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgY29tbWVudCA9IE5BLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZWRnZVIpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEdsaW1tYSkKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KHN0YXRtb2QpCmxpYnJhcnkodGlkeXZlcnNlKQoKIyBDaGFuZ2luZyBkZWZhdWx0IHRhYmxlJ3Mgc3R5bGUKa2FibGUgPC0gZnVuY3Rpb24oZGF0YSwgLi4uKXsKICAgICAgICBrbml0cjo6a2FibGUoZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICJodG1sIiwKICAgICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMywKICAgICAgICAgICAgICAgICAgICAgZm9ybWF0LmFyZ3MgPSBsaXN0KHNjaWVudGlmaWMgPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAnYycsCiAgICAgICAgICAgICAgICAgICAgIGxpbmVzZXAgPSAnJykgJT4lCiAgICAgICAga2FibGVfcGFwZXIoKSAlPiUKICAgICAgICBzY3JvbGxfYm94KHdpZHRoID0gIjYwMHB4IiwgaGVpZ2h0ID0gIjQwMHB4IikKfQoKa25pdF9wcmludC5kYXRhLmZyYW1lIDwtIGZ1bmN0aW9uKHgsIC4uLil7CiAgICAgICAgcmVzIDwtIHBhc3RlKGMoJycsICcnLCBrYWJsZSh4KSksCiAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIlxuIikKICAgICAgICBhc2lzX291dHB1dChyZXMpCn0KCnJlZ2lzdGVyUzNtZXRob2QoImtuaXRfcHJpbnQiLCAKICAgICAgICAgICAgICAgICAiZGF0YS5mcmFtZSIsCiAgICAgICAgICAgICAgICAga25pdF9wcmludC5kYXRhLmZyYW1lKQoKIyBQcmludCBub3RoaW5nIGlmIE5BIHdpdGhpbiBhIHRhYmxlCm9wdGlvbnMoa25pdC5rYWJsZS50YWJsZS5OQSA9ICcnKQoKdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKYGBgCgo8YnI+Cgo8aDE+bWlSTkEgYW5kIGlzb21pUiBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBBbmFseXNpcyBpbiBCcmVhc3QgQ2FuY2VyPC9oMT4KCjxoMyBhbGlnbj0icmlnaHQiPklyaXMgTWVzdHJlcyBQYXNjdWFsPC9oMz4KCjxocj4KCjxicj4KCkluIHRoaXMgc3R1ZHksIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvZiB0aGUgcmVzdWx0cyBwcm92aWRlZCBieQp0aGUgKk1JUkZMT1daKiBwaXBlbGluZSBpcyBwZXJmb3JtZWQuIFRoZSBtYWluIGdvYWwgaXMgdG8gYXNzZXMgaWYgdGhlcmUgYXJlIGFueQptaVJOQSBzcGVjaWVzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBhY3Jvc3Mgbm9ybWFsIGFuZCB0dW1vciB0aXNzdWVzLiBUaGUKY29uY2x1c2lvbnMgYXJlIGZvdW5kIGluIHRoZSBtYWluIG1hbnVzY3JpcHQuIEhlcmUsIG9ubHkgdGhlIHdvcmtmbG93IHdpdGgKc3RlcC1ieS1zdGVwIGV4cGxhbmF0aW9ucyBpcyByZXBvcnRlZC4KCjxicj4KCjxoMT5EYXRhIFByZXByb2Nlc3Npbmc8L2gxPgo8aHIgY2xhc3M9ImhhbGYtd2lkdGgiPgoKPGJyPgoKPGgyIGFsaWduPSJyaWdodCI+RGF0YSBMb2FkaW5nIGFuZCBTYW1wbGUgU2VsZWN0aW9uPC9oMj4KClRoZSAqTUlSRkxPV1oqIHBpcGVsaW5lIGhhcyBiZWVuIHJhbiB3aXRoIGEgc3BlY2lmaWMgc2V0IG9mIHNhbXBsZXMuIFRoZXJlZm9yZSwgCnRoZSBtZXRhZGF0YSBmb3IgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG11c3QgY29udGFpbiBleGFjdGx5IHRoZSAKc2FtZSBvbmVzLiBUaGlzIGluZm9ybWF0aW9uIGlzIHByb3ZpZGVkIGluIHR3byBkaWZmZXJlbnQgZmlsZXMsIG9uZSBjb250YWluaW5nIApkYXRhICpwZXIgY2FzZSogYW5kIHRoZSBvdGhlciBvbmUgd2l0aCAqcGVyIHNhbXBsZSogaW5mb3JtYXRpb24uCgpUaGUgKnBlciBjYXNlKiBmaWxlLCBjb250YWlucyBkYXRhIGZyb20gTkNJJ3MgQ2FuY2VyIEdlbm9tZSBBdGxhcyBQcm9ncmFtIAooVENHQSkuIFRodXMsIGFmdGVyIGxvYWRpbmcgdGhlIGRhdGEgZnJhbWUsIG9ubHkgdGhlIGNhc2VzIGJlbG9uZ2luZyB0byB0aGUgCmJyZWFzdCBjYW5jZXIgKEJSQ0EpIHN0dWR5IGFyZSBrZXB0LgoKYGBge3IgY2FzZV9tZXRhZGF0YSwgd2FybmluZz1GQUxTRX0KcmF3X2Nhc2VfbWV0YWRhdGEgPC0gcmVhZF9leGNlbCgiY2FzZV9tZXRhZGF0YS54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfdHlwZXMgPSBOVUxMKQoKY2FzZV9tZXRhZGF0YSA8LSByYXdfY2FzZV9tZXRhZGF0YSAlPiUgCiAgc2VsZWN0KDI6MzQpICU+JSAKICBmaWx0ZXIodHlwZSA9PSAiQlJDQSIpCmBgYAoKPGJyPgoKVGhlICpwZXIgc2FtcGxlKiBtZXRhZGF0YSBpcyBhbHJlYWR5IGZyb20gdGhlIFRDR0EtQlJDQSBzdHVkeSBzbywgZnJvbSB0aGUgZGF0YQpzZXQsIGEgcGFydGljdWxhciBzZXQgb2YgY29sdW1ucyBhcmUgc2VsZWN0ZWQ7IHRoZSBmaWxlIG5hbWUgKGBGaWxlLk5hbWVgKSwgdGhlCmNhc2UgYW5kIHNhbXBsZSBJRCAoYENhc2UuSURgIGFuZCBgU2FtcGxlLklEYCByZXNwZWN0aXZlbHkpLCBhbmQgdGhlIHNhbXBsZSdzCnRpc3N1ZSB0eXBlIChgU2FtcGxlLlR5cGVgKS4gSW4gYWRkaXRpb24sIHRoZSBmaWxlIG5hbWUgd2lsbCBiZSBmb3JtYXR0ZWQgYXMgdG8KbWF0Y2ggdGhlIHNhbXBsZSBuYW1lcyBpbiB0aGUgY291bnRzIHRhYmxlIGFuZCBhbiBleHRyYSBmaWVsZCB3aXRoIHRoZSBzYW1wbGUncwpwbGF0ZSBpcyBhZGRlZC4KCmBgYHtyIHNhbXBsZV9tZXRhZGF0YX0KcmF3X3NhbXBsZV9tZXRhZGF0YSA8LSByZWFkLnRhYmxlKCIuL3NhbXBsZV9tZXRhZGF0YS5jc3YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUpCgpzYW1wbGVfbWV0YWRhdGEgPC0gcmF3X3NhbXBsZV9tZXRhZGF0YSAlPiUgCiAgc2VsZWN0KGMoRmlsZS5OYW1lLCBDYXNlLklELCBTYW1wbGUuSUQsIFNhbXBsZS5UeXBlKSkKCmZvciAoaSBpbiAxOm5yb3coc2FtcGxlX21ldGFkYXRhKSl7CiAgc2FtcGxlX21ldGFkYXRhJEZpbGUuTmFtZVtpXSA8LSBzdWJzdHIocmF3X3NhbXBsZV9tZXRhZGF0YSRGaWxlLk5hbWVbaV0sIDYsIDI4KQogIHNhbXBsZV9tZXRhZGF0YSRQbGF0ZVtpXSA8LSBzdHJzcGxpdChzYW1wbGVfbWV0YWRhdGEkRmlsZS5OYW1lW2ldLCAiLSIpW1sxXV1bNV0KfQpgYGAKCjxicj4KCkZvbGxvd2luZyB0aGUgc2FtcGxlIHNlbGVjdGlvbiBjcml0ZXJpYSB1c2VkIGluIHRoZSBtYWluIHJlcG9ydCwgZmVtYWxlIGNhc2VzCmNvbnRhaW5pbmcgc29sZWx5IGEgc2FtcGxlcyBjb21pbmcgZnJvbSBhIG5vcm1hbCB0aXNzdWUgYW5kIGFub3RoZXIgb25lIGZyb20gYQpwcmltYXJ5IHR1bW9yIHdpdGggbm8gbWlzc2luZyBkYXRhIGFyZSBrZXB0LiBJbiBhZGRpdGlvbiwgYW55IGNhc2Ugd2l0aCBhIG5ldwp0dW1vciBldmVudCBvciBoYXMgZGllZCB3aXRob3V0IHR1bW9ycyBpdCBpcyByZW1vdmVkLiBNb3Jlb3Zlciwgb25seQppbmZpbHRyYXRpbmcgZHVjdGFsIGNhcmNpbm9tYSBiZWxsb3cgKlN0YWdlIElJQiogY2FzZXMgYXJlIG1haW50YWluZWQuIEZpbmFsbHksCnRoZSBzYW1wbGUncyB0aXNzdWUgdHlwZXMgYXJlIG1vZGlmaWVkIHRvIGJlIGxhYmVsZWQgYXMgZWl0aGVyIGBOb3JtYWxgIG9yCmBUdW1vcmAuCgpgYGB7ciBzYW1wbGVfc2VsZWN0aW9ufQpzYW1wbGVfbWV0YWRhdGEgPC0gc2FtcGxlX21ldGFkYXRhICU+JSAKICBncm91cF9ieShDYXNlLklEKSAlPiUgCiAgZmlsdGVyKCBuKCkgPT0gMiAsCiAgICAgICAgICBhbnkoU2FtcGxlLlR5cGUgPT0gIlNvbGlkIFRpc3N1ZSBOb3JtYWwiKSwgCiAgICAgICAgICAhU2FtcGxlLlR5cGUgPT0gIk1ldGFzdGF0aWMiKSAlPiUKICBhcnJhbmdlKENhc2UuSUQpCgpzYW1wbGVfbWV0YWRhdGEkU2FtcGxlLlR5cGVbc2FtcGxlX21ldGFkYXRhJFNhbXBsZS5UeXBlID09ICJQcmltYXJ5IFR1bW9yIl0gPC0gIlR1bW9yIgpzYW1wbGVfbWV0YWRhdGEkU2FtcGxlLlR5cGVbc2FtcGxlX21ldGFkYXRhJFNhbXBsZS5UeXBlID09ICJTb2xpZCBUaXNzdWUgTm9ybWFsIl0gPC0gIk5vcm1hbCIKCmNhc2VfbWV0YWRhdGEgPC0gY2FzZV9tZXRhZGF0YSAlPiUKICBmaWx0ZXIoYmNyX3BhdGllbnRfYmFyY29kZSAlaW4lIHNhbXBsZV9tZXRhZGF0YSRDYXNlLklELAogICAgICAgICAhKHZpdGFsX3N0YXR1cyA9PSAiRGVhZCImIHR1bW9yX3N0YXR1cyA9PSAiVFVNT1IgRlJFRSIpLAogICAgICAgICBnZW5kZXIgPT0gIkZFTUFMRSIsCiAgICAgICAgICFpcy5uYSh0dW1vcl9zdGF0dXMpLAogICAgICAgICBpcy5uYShuZXdfdHVtb3JfZXZlbnRfdHlwZSksCiAgICAgICAgIHJhY2UgIT0gIltOb3QgQXZhaWxhYmxlXSIsCiAgICAgICAgIGhpc3RvbG9naWNhbF90eXBlID09ICJJbmZpbHRyYXRpbmcgRHVjdGFsIENhcmNpbm9tYSIsCiAgICAgICAgICFhamNjX3BhdGhvbG9naWNfdHVtb3Jfc3RhZ2UgJWluJSBjKCJTdGFnZSBJSUlCIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTdGFnZSBJSUlDIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTdGFnZSBJViIpKSAlPiUKICBzZWxlY3QoYmNyX3BhdGllbnRfYmFyY29kZSwgCiAgICAgICAgIGFnZV9hdF9pbml0aWFsX3BhdGhvbG9naWNfZGlhZ25vc2lzLAogICAgICAgICByYWNlLCAKICAgICAgICAgYWpjY19wYXRob2xvZ2ljX3R1bW9yX3N0YWdlLAogICAgICAgICB2aXRhbF9zdGF0dXMsCiAgICAgICAgIHR1bW9yX3N0YXR1cywgCiAgICAgICAgIG1lbm9wYXVzZV9zdGF0dXMsIAogICAgICAgICBpbml0aWFsX3BhdGhvbG9naWNfZHhfeWVhciwgCiAgICAgICAgIGJpcnRoX2RheXNfdG8sIAogICAgICAgICBkZWF0aF9kYXlzX3RvKSAKY29sbmFtZXMoY2FzZV9tZXRhZGF0YSlbMV0gPC0gIkNhc2UuSUQiCgpzYW1wbGVfbWV0YWRhdGEgPC0gc2FtcGxlX21ldGFkYXRhICU+JQogIGZpbHRlcihDYXNlLklEICVpbiUgY2FzZV9tZXRhZGF0YSRDYXNlLklEKSAlPiUKICBzZWxlY3QoRmlsZS5OYW1lLCBDYXNlLklELCBTYW1wbGUuVHlwZSwgUGxhdGUpICU+JQogIGFycmFuZ2UoRmlsZS5OYW1lKQpgYGAKCjxicj4KClRoZSBmaWx0ZXJlZCBkYXRhIHNldHMgYXJlIGNvbWJpbmVkIGludG8gYSBzaW5nbGUgb25lLiBUaGUgbWVyZ2UgbGVhZHMgdG8gaGF2ZQpzYW1wbGVzIGNvbWluZyBmcm9tIG5vcm1hbCB0aXNzdWVzIHRvIGhhdmUgYSBjYW5jZXIgc3RhZ2UgYXNzaWduZWQgdGh1cywgdGhleQphcmUgbGFiZWxlZCBhcyBoYXZpbmcgbm8gc3RhZ2UuCgpgYGB7ciBtZXJnZV9tZXRhZGF0YX0KbWV0YWRhdGEgPC0gc2FtcGxlX21ldGFkYXRhICU+JSAKICBpbm5lcl9qb2luKGNhc2VfbWV0YWRhdGEsIGJ5ID0gIkNhc2UuSUQiKQoKbWV0YWRhdGEgPC0gY29sdW1uX3RvX3Jvd25hbWVzKG1ldGFkYXRhLCB2YXIgPSAiRmlsZS5OYW1lIikKCm1ldGFkYXRhJGFqY2NfcGF0aG9sb2dpY190dW1vcl9zdGFnZVttZXRhZGF0YSRTYW1wbGUuVHlwZSA9PSAiTm9ybWFsIl0gPC0gIk5vbmUiCmBgYAoKPGJyPgoKVGhlIHNhbXBsZSdzIGNvdW50IHRhYmxlcyBhcmUgam9pbmVkIHRvZ2V0aGVyLiBUaGVyZSBhcmUgbWlSTkEgc3BlY2llcyBwcmVzZW50CmluIG9uZSBvciBzZXZlcmFsIHNhbXBsZXMgdGhhdCBhcmUgbm90IGluIG90aGVycy4gV2l0aCB0aGUgbWVyZ2luZywgdGhlc2UgY291bnRzCmFyZSBjaGFuZ2VkIGZyb20gYE5BYCB0byAwLiBGdXJ0aGVybW9yZSwgdGhlIHNhbXBsZSBmaWxlIG5hbWVzIGFyZSB1c2VkIGFzCmNvbHVtbiBuYW1lcyBhbmQsIGdpdmVuIHRoYXQgdGhlIGZpbGUgbmFtZXMgaW4gdGhlIG1ldGFkYXRhIGFuZCB0aGUgY291bnRzIHRhYmxlCmNvbnRhaW4gZGlmZmVyZW50IHNlcGFyYXRvciBjaGFyYWN0ZXJzLCB0aGUgY29sdW1uIG5hbWVzIGluIHRoZSBjb3VudHMgdGFibGUgYXJlCmNoYW5nZWQgdG8gbWF0Y2ggdGhlIG1ldGFkYXRhLiBGaW5hbGx5LCBhbGwgdGhlIGxvYWRlZCBpbmZvcm1hdGlvbiBpcyBzdG9yZWQgaW4KYW4gYGVkZ2VSYCBbQHJvYmluc29uRWRnZVJCaW9jb25kdWN0b3JQYWNrYWdlMjAxMF0uCgpgYGB7ciBlZGdlUl9taXItdGFibGVzfQpvcHRpb25zKHNjaXBlbj05OTkpCgpmbmFtZXMgPC0gbGlzdC5maWxlcygiLi9taXJuYV9zYW1wbGVzIiwgZnVsbC5uYW1lcyA9IFQpCm1pcm5hX2NvdW50cyA8LSAgcmVhZC5jc3YoZm5hbWVzWzFdLHNlcCA9ICJcdCIsIGhlYWRlciA9IFRSVUUpCgpmb3IgKHRhYmxlIGluIGZuYW1lc1stMV0pewogIHRhYmxlIDwtIHJlYWQuY3N2KHRhYmxlLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiKQogIG1pcm5hX2NvdW50cyA8LSBtaXJuYV9jb3VudHMgJT4lIGZ1bGxfam9pbih0YWJsZSkKfQoKbWlybmFfY291bnRzW2lzLm5hKG1pcm5hX2NvdW50cyldIDwtIDAKCnJvd25hbWVzKG1pcm5hX2NvdW50cykgPC0gbWlybmFfY291bnRzWywxXQptaXJuYV9jb3VudHMgPC0gbWlybmFfY291bnRzWywtMV0KCmNvbG5hbWVzKG1pcm5hX2NvdW50cykgPC1nc3ViKCJcXC4iLCAiLSIsIGNvbG5hbWVzKG1pcm5hX2NvdW50cykpCgpkZ2UgPC0gREdFTGlzdChjb3VudHMgPSBtaXJuYV9jb3VudHMsIGdyb3VwID0gYXMuZmFjdG9yKG1ldGFkYXRhJFNhbXBsZS5UeXBlKSkKb3B0aW9ucyhkaWdpdHMgPSAyKQpgYGAKCjxicj4KCjxoMiBhbGlnbj0icmlnaHQiPkV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXM8L2gyPgoKSW4gdGhpcyBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEgaXMgYW5hbHl6ZWQgZnJvbQpkaWZmZXJlbnQgcGVyc3BlY3RpdmVzIGFzIHRvIGlkZW50aWZ5IHRyZW5kcywgcmVsYXRpb25zaGlwcyBhbmQgcG9zc2libGUKYW5vbWFsaWVzIHN1Y2ggYXMgb3V0bGllcnMgb3IgYmF0Y2ggZWZmZWN0cy4KCjxoMyBhbGlnbj0icmlnaHQiPkRhdGEgRGlzdHJpYnV0aW9uPC9oMz4KClRoZSBmaXJzdCB2aXN1YWxpemF0aW9uIGZvY3VzZXMgb24gdGhlIG1pUk5BIHNwZWNpZXMgY291bnRzIHBlciBzYW1wbGUgYXMgYQp3YXkgdG8gZ2V0IGEgZ2xhbmNlIG9mIHRoZSBjb3VudCdzIGRpc3RyaWJ1dGlvbi4gSW4gdGhlIGZvbGxvd2luZyBwbG90LCBlYWNoCmhvcml6b250YWwgbGluZSByZXByZXNlbnRzIGEgbWlSTkEgc3BlY2llcy4gRm9yIGFuIGVhc2llciBpbnRlcnByZXRhdGlvbiwKc2FtcGxlcyBhcmUgY29sb3JlZC4KCmBgYHtyIGNvdW50cGVyc2FtcGxlLCBlY2hvPUYsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQptaXJuYV9tZWx0IDwtIG1lbHQoYXMubWF0cml4KGRnZSRjb3VudHMpKQoKdCA8LSBnZ3Bsb3QobWlybmFfbWVsdCwgYWVzKHggPSBWYXIyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB2YWx1ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IFZhcjEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBWYXIyKSkgKwogIGdlb21fbGluZShzdGF0ID0gImlkZW50aXR5IikgKwogIGxhYnMoIHRpdGxlID0gIm1pUk5BIFNwZWNpZXMgQ291bnRzIHBlciBTYW1wbGUiLCAKICAgICAgICB4ID0gIlNhbXBsZSIsIHkgPSAiQ291bnRzIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCnQKYGBgCgo8YnI+CgpUaGUgbW9zdCBub3RhYmxlIGFzcGVjdCBvZiB0aGUgcGxvdCBpcyB0aGUgcHJlc2VuY2Ugb2YgbG93IGNvdW50cyBiZXR3ZWVuIHRoZQpkaWZmZXJlbnQgc2FtcGxlcy4gRHVlIHRvIHRoZSAqTUlSRkxPV1oqIHVuYW1iaWd1b3VzIG5vdGF0aW9uLCB0aGVyZSBhcmUgdmVyeQpzcGVjaWZpYyBpc29taVJzIHByZXNlbnQgaW4gbG93IGZyZXF1ZW5jeSBpbiBqdXN0IGEgZmV3IHNhbXBsZXMuIEhlbmNlLCB0aGlzCnRyZW5kIHdhcyBleHBlY3RlZC4gT24gdGhlIG90aGVyIGhhbmQsIGEgc2Vjb25kIHNldCBvZiBtaVJOQSBzcGVjaWVzIGhhcyBhCmdyZWF0ZXIgcHJlc2VuY2UgYW1vbmcgdGhlIHNhbXBsZXMgd2l0aCB1cCB0byAxMDAwMDAwIGNvdW50cyBhbmQgb25seSBmb3VyCnNhbXBsZXMgc3RhbmQgb3V0LCBiZWluZyB0aGUgaGlnaGVyIGNvdW50IGZyb20gdGhlIHNhbXBsZQpgRTItQTFMSC0wMUEtMTFSLUExNEMtMTNgIGluIHRoZSBgaHNhLW1pUi0yMS01cGAgY2Fub25pY2FsIG1pUk5BLgoKRnJvbSBhIHN0YXRpc3RpY2FsIHBvaW50IG9mIHZpZXcsIGdlbmVzIHdpdGggY29uc2lzdGVudGx5IGxvdyBjb3VudHMgYXJlIHZlcnkKdW5saWtlbHkgYmUgYXNzZXNzZWQgYXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmVjYXVzZSBsb3cKY291bnRzIGRvIG5vdCBwcm92aWRlIGVub3VnaCBzdGF0aXN0aWNhbCBldmlkZW5jZSBmb3IgYSByZWxpYWJsZSBqdWRnZW1lbnQgdG8gYmUKbWFkZS4gU3VjaCBnZW5lcyBjYW4gdGhlcmVmb3JlIGJlIHJlbW92ZWQgZnJvbSB0aGUgYW5hbHlzaXMgd2l0aG91dCBhbnkgbG9zcyBvZgppbmZvcm1hdGlvbi4KCkluIG9yZGVyIHRvIGFzc2VzIHdoaWNoIG1pUk5BcyB3aWxsIGJlIGtlcHQsIHRoZSBgZWRnZVJgIGZ1bmN0aW9uIGBmaWx0ZXJCeUV4cHJgCmlzIHVzZWQuIFRoaXMgZnVuY3Rpb24gYmFzZXMgdGhlIGZpbHRlcmluZyBvbiBjb3VudC1wZXItbWlsbGlvbiAoQ1BNKSB2YWx1ZXMgc28KYXMgdG8gYXZvaWQgZmF2b3JpbmcgZ2VuZXMgdGhhdCBhcmUgZXhwcmVzc2VkIGluIGxhcmdlciBsaWJyYXJpZXMgb3ZlciB0aG9zZQpleHByZXNzZWQgaW4gc21hbGxlciBsaWJyYXJpZXMgZm9sbG93aW5nIGEgc2ltaWxhciBhcHByb2FjaCBhcyB0aGUgb25lIHVzZWQgYnkKQGNoZW5SZWFkc0dlbmVzUGF0aHdheXMyMDE2LgoKYGBge3IgbWlybmEya2VlcH0KbWlybmEya2VlcCA8LSBmaWx0ZXJCeUV4cHIoZGdlKQpkZ2UgPC0gZGdlW21pcm5hMmtlZXAsICwga2VlcC5saWIuc2l6ZXM9Rl0KYGBgCgo8YnI+CgpGdXJ0aGVybW9yZSwgdG8gZWxpbWluYXRlIGNvbXBvc2l0aW9uIGJpYXNlcyBiZXR3ZWVuIGxpYnJhcmllcywgdGhlIHRyaW1tZWQgbWVhbgpvZiBNLXZhbHVlcyAoVE1NKSB2YXJpYW50IHByb3Bvc2VkIGJ5IEByb2JpbnNvblNjYWxpbmdOb3JtYWxpemF0aW9uTWV0aG9kMjAxMGEsCiJUTU0gd2l0aCBzaW5nbGV0b24gcGFpcmluZyIgKCAqVE1Nd3NwKiApLCBpcyBhcHBsaWVkIHRvIHRoZSBkYXRhIHNldCB3aXRoIHRoZQpgZWRnZVJgIGZ1bmN0aW9uIGBjYWxjTm9ybUZhY3RvcnNgLiBUaGlzIHZhcmlhbnQgaXMgcHJlZmVycmVkIG92ZXIgdGhlIG5vcm1hbApUTU0gYXMgaXQgaXMgaW50ZW5kZWQgdG8gcGVyZm9ybSBiZXR0ZXIgZm9yIGRhdGEgd2l0aCBhIGhpZ2ggcHJvcG9ydGlvbiBvZgp6ZXJvcy4gQXMgYSByZXN1bHQsIGFuIGV4dHJhIGZpZWxkIHdpdGggdGhlIGxpYnJhcnkgc2NhbGluZyBmYWN0b3JzIGlzIGFkZGVkIHRvCnRoZSBgREdFTGlzdGAgb2JqZWN0LgoKYGBge3Igbm9ybWFsaXphdGlvbn0KZGdlIDwtIGNhbGNOb3JtRmFjdG9ycyhkZ2UsIG1ldGhvZCA9ICJUTU13c3AiKQpgYGAKCjxicj4KCjxoMyBhbGlnbj0icmlnaHQiPlByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXM8L2gzPgoKSW4gb3JkZXIgdG8gYWNjb21wbGlzaCBhIGdyZWF0ZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGF0YSwgYSBQcmluY2lwYWwKQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpIGlzIHBlcmZvcm1lZCB1c2luZyB0aGUgYFJgIGZ1bmN0aW9uIGBwcmNvbXBgLiBUaGUgUENBCm11c3QgYmUgY29uZHVjdGVkIG92ZXIgdGhlIG5vcm1hbGl6ZWQgY291bnRzIHdoaWNoIGFyZSBvYnRhaW5lZCB3aXRoIHRoZSBgZWRnZVJgCmZ1bmN0aW9uIGBjcG1gLiBJbiBhZGRpdGlvbiwgdGhlIG5vcm1hbGl6ZWQgY291bnRzIGFyZSByZXR1cm5lZCBhcyB0aGVpciBsb2d+Mn4KdmFsdWUuCgpUaGUgbWFpbiBnb2FsIG9mIHRoaXMgYW5hbHlzaXMgaXMgdG8gYXNzZXNzIHdoZXRoZXIgdGhlIGRpZmZlcmVudCBtaVJOQSBzcGVjaWVzCmFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYWNyb3NzIHRpc3N1ZXMsIGVhY2ggc2FtcGxlIGlzIGNvbG9yZWQgYWNjb3JkaW5nIHRvCml0LiBNaW5kIHRoYXQgYXMgdGhlIGZpbmFsIHBsb3Qgd2lsbCBiZSBpbnRlcmFjdGl2ZSwgYWxsIHRoZSBwb3NzaWJsZSByZWxldmFudAppbmZvcm1hdGlvbiBhYm91dCBlYWNoIHNhbXBsZSB3aWxsIGJlIGRpc3BsYXllZCB3aGVuIGhvdmVyaW5nIG92ZXIgaXQuCgpgYGB7ciB0aXNzdWUgUENBfQpsb2dfbm9ybV9taXJuYSA8LSBjcG0oZGdlLCBsb2cgPSBULCBub3JtYWxpemVkLmxpYi5zaXplcyA9ICkKUENBIDwtIHByY29tcCh0KGxvZ19ub3JtX21pcm5hKSwgc2NhbGUuID0gVCkKYGBgCgpgYGB7ciBwbG90X3Rpc3N1ZS1QQ0EsIGVjaG89RiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CmNvbXBvbmVudHMgPC0gZGF0YS5mcmFtZShQQ0EkeCkKCkNhc2UgPC0gbWV0YWRhdGEkQ2FzZS5JRApTYW1wbGUgPC0gcm93bmFtZXMoY29tcG9uZW50cykKU3RhZ2UgPC0gbWV0YWRhdGEkYWpjY19wYXRob2xvZ2ljX3R1bW9yX3N0YWdlClRpc3N1ZSA8LSBtZXRhZGF0YSRTYW1wbGUuVHlwZQoiVml0YWwgc3RhdHVzIiA8LSBtZXRhZGF0YSR2aXRhbF9zdGF0dXMKCmdnIDwtIGdncGxvdChjb21wb25lbnRzLGFlcyh4ID0gUEMxLCB5PSBQQzIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSA9IENhc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGUgPSBTYW1wbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IFRpc3N1ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFRpc3N1ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFnZSA9IFN0YWdlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpdGFsID0gYFZpdGFsIHN0YXR1c2ApKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC44LCBzaXplID0gNCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIiwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MCwgbGluZXR5cGU9ImRhc2hlZCIsIGFscGhhID0gMC4yKSArCiAgbGFicyh0aXRsZSA9ICJtaVJOQSBFeHByZXNzaW9uIEFjcm9zcyBTYW1wbGVzIFBDQSIsIAogICAgICAgeCA9ICJQQzEgKDIzLjAxJSkiLCAKICAgICAgIHkgPSAiUEMyICg5LjM1JSkiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjNDBFMEQwIiwiIzhBMkJFMiIpKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMSwgNSkpCgpnZ3Bsb3RseShnZykKYGBgCgo8YnI+CgpJbiB0aGlzIHNlY29uZCB2aXN1YWxpemF0aW9uLCB0aHJlZSBvdXRsaWVycyBhcmUgZm91bmQ6IHNhbXBsZXMKYEJILUEwQlotMDFBLTMxUi1BMTJPLTEzYCwgYEJILUExRk4tMDFBLTExUi1BMTNQLTEzYCBhbmQKYEUyLUExTEgtMDFBLTExUi1BMTRDLTEzYC4gRXZlbiBpZiB0aGUgZm9ybWVyIGlzIHRoZSBvbmx5IG9uZSB0aGF0IGhhcyBiZWVuCmFscmVhZHkgc3BvdHRlZCBhcyBzdWNoLCBhbGwgdGhyZWUgYXJlIHJlbW92ZWQgZnJvbSB0aGUgZGF0YSBzZXQuIFByaW9yIHRvIHRoZQpQQ0EsIHRoZSBmaWx0ZXJpbmcgb2YgbG93IGV4cHJlc3NlZCBtaVJOQXMgYW5kIHRoZSBjb3VudHMgbm9ybWFsaXphdGlvbiBhcmUKcGVyZm9ybWVkIGFnYWluIGluIG9yZGVyIHRvIGNhcHR1cmUgdGhlIGxhY2tpbmcgb2YgdGhlc2UgbWlSTkEgc3BlY2llcy4KCmBgYHtyIHJlbW92ZU91dGxpZXJzfQpvdXRsaWVycyA8LSAgYygiQkgtQTBCWi0wMUEtMzFSLUExMk8tMTMiLAogICAgICAgICAgICAgICAiQkgtQTFGTi0wMUEtMTFSLUExM1AtMTMiLAogICAgICAgICAgICAgICAiRTItQTFMSC0wMUEtMTFSLUExNEMtMTMiKQoKZGdlIDwtIGRnZVssICFyb3duYW1lcyhkZ2Ukc2FtcGxlcykgJWluJSBvdXRsaWVyc10KbWV0YWRhdGEgPC0gbWV0YWRhdGFbIXJvd25hbWVzKG1ldGFkYXRhKSAlaW4lIG91dGxpZXJzLCBdCgptaXJuYTJrZWVwIDwtIGZpbHRlckJ5RXhwcihkZ2UpCmRnZSA8LSBkZ2VbbWlybmEya2VlcCwgLCBrZWVwLmxpYi5zaXplcyA9IEZdCgpkZ2UgPC0gY2FsY05vcm1GYWN0b3JzKGRnZSwgbWV0aG9kID0gIlRNTXdzcCIpCmxvZ19ub3JtX21pcm5hIDwtIGNwbShkZ2UsIGxvZyA9IFQsIG5vcm1hbGl6ZWQubGliLnNpemVzID0gVCkKClBDQSA8LSBwcmNvbXAodChsb2dfbm9ybV9taXJuYSksIHNjYWxlLiA9IFQpCmBgYAoKYGBge3IgcGxvdF9yZW1vdmVPdXRsaWVycywgZWNobz1GLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KY29tcG9uZW50cyA8LSBkYXRhLmZyYW1lKFBDQSR4KQoKQ2FzZSA8LSBtZXRhZGF0YSRDYXNlLklEClNhbXBsZSA8LSByb3duYW1lcyhjb21wb25lbnRzKQpTdGFnZSA8LSBtZXRhZGF0YSRhamNjX3BhdGhvbG9naWNfdHVtb3Jfc3RhZ2UKVGlzc3VlIDwtIG1ldGFkYXRhJFNhbXBsZS5UeXBlCiJWaXRhbCBzdGF0dXMiIDwtIG1ldGFkYXRhJHZpdGFsX3N0YXR1cwoKZ2cgPC0gZ2dwbG90KGNvbXBvbmVudHMsYWVzKHggPSBQQzEsIHk9IFBDMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlID0gQ2FzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gVGlzc3VlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gVGlzc3VlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YWdlID0gU3RhZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdml0YWwgPSBgVml0YWwgc3RhdHVzYCkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjgsIHNpemUgPSA0KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGxpbmV0eXBlPSJkYXNoZWQiLCBhbHBoYSA9IDAuMikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIiwgYWxwaGEgPSAwLjIpICsKICBsYWJzKHRpdGxlID0gIm1pUk5BIEV4cHJlc3Npb24gQWNyb3NzIFNhbXBsZXMgUENBXG53aXRob3V0IG91dGxpZXJzIiwgCiAgICAgICB4ID0gIlBDMSAoMjcuMzclKSIsIAogICAgICAgeSA9ICJQQzIgKDE0LjAyJSkiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjNDBFMEQwIiwiIzhBMkJFMiIpKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMSwgNSkpCgpnZ3Bsb3RseShnZykKYGBgCgo8YnI+CgpUaGVyZSBpcyBhIGNsZWFyIHRlbmRlbmN5IGluIHRoZSA0IGRpZmZlcmVudCBjbHVzdGVycy4gU29tZSBwYXJhbWV0ZXJzIHN1Y2ggYXMKdGhlIHNhbXBsZSdzIHZpdGFsIHN0YXR1cyBoYWQgYmVlbiB1c2VkIGluIG9yZGVyIHRvIGFzc2VzcyBpZiB0aGVyZSBpcyBhbnkKcmVsYXRpb24gYmV0d2VlbiB0aGVtIGFuZCB0aGUgY2x1c3RlcmluZy4gQXQgdGhlIGVuZCwgdGhlIHNhbXBsZSdzIG9yaWdpbiBwbGF0ZQphbmQgd2hldGhlciBpcyBwcmVzZW50IG9yIG5vdCBpbiBib3RoIGtpbmQgb2YgdGlzc3VlcyBpcyB0aGUgY2F1c2Ugb2YgaXQuCgpgYGB7ciBwbG90X3BsYXRlLVBDQSwgZWNobz1GLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KUGxhdGUgPC0gbWV0YWRhdGEkUGxhdGUKCnBsYXRlc19jb2wgPSBjKCAiIzRCMDA4MiIsICIjMDBCRkZGIiwgIiNGRjAwMDAiLCAiI0VCMjJCRSIsICIjN0NGQzAwIiwKICAgICAgICAgICAgICAgICIjRkYxNDkzIiwgIiM4QTJCRTIiLCAiIzIyOEIyMiIsICIjQzcxNTg1IiwgIiNGRjhDMDAiLAogICAgICAgICAgICAgICAgIiM5OTAwQ0MiLCAiI0E1MkEyQSIsICIjMDBGRkZGIiwgIiNGRkQ3MDAiLCAiIzAwRkE5QSIpCgpnZyA8LSBnZ3Bsb3QoY29tcG9uZW50cyxhZXMoeCA9IFBDMSwgeT0gUEMyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgPSBDYXNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlID0gU2FtcGxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhZ2UgPSBTdGFnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBsYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBUaXNzdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aXRhbCA9IGBWaXRhbCBzdGF0dXNgKSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuOCwgc2l6ZSA9IDQpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgbGluZXR5cGU9ImRhc2hlZCIsIGFscGhhID0gMC4yKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAsIGxpbmV0eXBlPSJkYXNoZWQiLCBhbHBoYSA9IDAuMikgKwogIGxhYnModGl0bGUgPSAibWlSTkEgRXhwcmVzc2lvbiBBY3Jvc3MgU2FtcGxlcyBwZXIgUGxhdGUgUENBIiwgCiAgICAgICB4ID0gIlBDMSAoMjcuMzclKSIsIAogICAgICAgeSA9ICJQQzIgKDE0LjAyJSkiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBsYXRlc19jb2wpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxLCA1KSkgCgpnZ3Bsb3RseShnZykKYGBgCgo8YnI+CgpQbGF0ZXMgYEEwOTBgIGFuZCBgQTA1N2AgZXhoaWJpdCB0aGlzIGVmZmVjdCB0aGUgbW9zdCBmb2xsb3dlZCBieSBgQTA4NWAgKHdpdGggYQpzaW5nbGUgc2FtcGxlKSBhbmQgYEEwMTBgLiBGcm9tIHRoZXNlIGZvdXIgcGxhdGVzLCBwbGF0ZXMgYEEwNTdgLCBgQTA4NWAgYW5kIApgQTAxMGAgYXJlIHRoZSBvbmVzIHRoYXQgd2VyZSBvbmx5IHVzZWQgZm9yIHR1bW9yIHNhbXBsZXMsIHdoaWxlIHBsYXRlIGBBMDkwYAp3YXMgb25seSB1c2VkIGZvciBub3JtYWwgdGlzc3VlIHNhbXBsZXMuIEl0IGlzIGltcG9ydGFudCB0byBtZW50aW9uIHRoYXQgdGhlCnNtYWxsIHNhbXBsZSBzZXQgdXNlZCBpbiB0aGlzIHN0dWR5LCBjb3VsZCBiZSB0aGUgcmVhc29uIGJlaGluZCB0aGlzIGVmZmVjdCB0bwpiZSBzbyBsYXJnZS4KCjxicj4KCjxoMT5EaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBBbmFseXNpczwvaDE+CjxociBjbGFzcz0iaGFsZi13aWR0aDIiPgoKSXQgaXMgb2YgaW50ZXJlc3QgdG8gc2VlIHdoaWNoIG1pUk5BIHNwZWNpZXMgYXJlIGV4cHJlc3NlZCBhdCBkaWZmZXJlbnQgbGV2ZWxzCmFjcm9zcyB0aXNzdWVzLiBXaXRoIHRoaXMgZ29hbCBpbiBtaW5kLCBhIGxpbmVhciBtb2RlbCBpcyBnb2luZyB0byBiZSBmaXR0ZWQsIHRvCnBlcmZvcm0gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgoKPGJyPgoKPGgyIGFsaWduPSJyaWdodCI+RGVzaWduIE1hdHJpeCBhbmQgRGlzcGVyc2lvbiBFc3RpbWF0aW9uPC9oMj4KClRoZSBmaXJzdCBzdGVwIHRvd2FyZHMgdGhlIG9iamVjdGl2ZSBpcyB0byBidWlsZCBhIGRlc2lnbiBtYXRyaXguIFRoZSBkZXNpZ24KbWF0cml4IGxpbmtzIGVhY2ggdGlzc3VlIChjb2x1bW5zKSB0byB0aGUgc2FtcGxlcyB0aGF0IGJlbG9uZyB0byBpdCAocm93cykuIEV2ZW4KaWYgYSBzbGlnaHRseSBiYXRjaCBlZmZlY3QgaGFzIGJlZW4gb2JzZXJ2ZWQgZHVlIHRvIHRoZSBwbGF0ZSBvZiBvcmlnaW4sIHRoZQpkZXNpZ24gaW4gdGhpcyBzdHVkeSBpcyBjb25zaWRlcmVkIHRvIGEgb25lLXdheSBkZXNpZ24gdW5kZXIgdGhlIGFzc3VtcHRpb24gdGhhdAp0aGUgZGlmZmVyZW5jZSBvYnNlcnZlZCBpcyBub3QgZHVlIHRvIHRoZSBwbGF0ZSBvZiBvcmlnaW4gdG8gYmUgYSBwb3RlbnRpYWwKY29uZm91bmRlciBidXQgYW4gZWZmZWN0IG9mIHRoZSBzYW1wbGUgc2l6ZS4KCmBgYHtyIGRlc2lnbiBtYXQudjJ9CmRlc2lnbl9tYXQgPC0gbW9kZWwubWF0cml4KH4wICsgZGdlJHNhbXBsZXMkZ3JvdXApCmNvbG5hbWVzKGRlc2lnbl9tYXQpIDwtIGxldmVscyhkZ2Ukc2FtcGxlcyRncm91cCkKYGBgCgpgYGB7ciB2aWV3X2Rlc2lnbi1tYXQsIGVjaG89Rn0KaGVhZChkZXNpZ25fbWF0KQpgYGAKCjxicj4KCkR1ZSB0aGUgbmF0dXJlIG9mIHRoZSBkYXRhIHRvIGJlIGFuYWx5emVkLCB0aGVyZSBjb3VsZCBiZSBiaW9sb2dpY2FsIHZhcmlhYmlsaXR5CnRoYXQgbWF5IHR1cm4gdGhlIG9ic2VydmVkIGNvdW50cyBpbnRvIG5vaXNlLiBFc3RpbWF0aW5nIHRoaXMgdmFyaWFiaWxpdHkgd2l0aGluCnRoZSBkaWZmZXJlbnQgdGlzc3VlcyBpcyBnb2luZyB0byBoZWxwIG9uIG1vZGVsaW5nIHRoZSBzdGF0aXN0aWNhbCB1bmNlcnRhaW50eQphc3NvY2lhdGVkIHdpdGggdGhlIG9ic2VydmVkIGNvdW50cyBhbmQgb24gbWFraW5nIGFjY3VyYXRlIGluZmVyZW5jZXMgYWJvdXQKZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYmV0d2VlbiB0aGVtLgoKVGhlIGRpc3BlcnNpb24gZXN0aW1hdGVzLCBhcmUgY29tcHV0ZWQgd2l0aCB0aGUgYGVkZ2VSYCBmdW5jdGlvbiBgZXN0aW1hdGVEaXNwYC4KVGhpcyBmdW5jdGlvbiB1c2VzIHRoZSBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24gdG8gbW9kZWwgdGhlIHJlYWQgY291bnRzCnBlciBnZW5lIHBlciBzYW1wbGUuIFdpdGggdGhpcyBmdW5jdGlvbiB0aHJlZSBkaWZmZXJlbnQgZGlzcGVyc2lvbiBlc3RpbWF0ZXMgYXJlCmNvbXB1dGVkLiBUaGUgZmlyc3Qgb25lIGlzIGFuIGVtcGlyaWNhbCBCYXllcyBtb2RlcmF0ZWQgZGlzcGVyc2lvbiBmb3IgZWFjaAppbmRpdmlkdWFsIG1pUk5BIHNwZWNpZXMgKCp0YWd3aXNlKikuIFRoZSBzZWNvbmQgb25lLCB0aGUgY29tbW9uIGRpc3BlcnNpb24sIGlzCmEgZ2xvYmFsIGVzdGltYXRlIGF2ZXJhZ2VkIGFjcm9zcyBhbGwgc3BlY2llcy4gRmluYWxseSwgYSBwcmVkaWN0ZWQgZGlzcGVyc2lvbgpmb3IgZWFjaCBtaVJOQSBpcyBvYnRhaW5lZCBmcm9tIGl0cyBhYnVuZGFuY2UgKCp0cmVuZGVkKikuCgpJbiBhZGRpdGlvbiwgdG8gZ3VhcmQgdGhlICp0YWd3aXNlKiBlc3RpbWF0ZXMgZnJvbSBvdXRsaWVyIG1pUk5BcyB3aXRoCnJlbWFya2FibHkgbGFyZ2Ugb3Igc21hbGwgaW5kaXZpZHVhbCBkaXNwZXJzaW9ucywgdGhlIGByb2J1c3QgPSBUUlVFYCBvcHRpb24gaXMKdXNlZCBbQHBoaXBzb25Sb2J1c3RIeXBlcnBhcmFtZXRlckVzdGltYXRpb24yMDE2XS4gVGhlIGRpc3BlcnNpb24gZXN0aW1hdGVzIGFyZQp2aXN1YWxpemVkIHdpdGggdGhlIGBlZGdlUmAgZnVuY3Rpb24gYHBsb3RCQ1ZgLiBUaGUgYmlvbG9naWNhbCBjb2VmZmljaWVudCBvZgp2YXJpYXRpb24gaW4gdGhlIGB5LWF4aXNgLCBpcyB0aGUgc3F1YXJlIHJvb3Qgb2YgdGhlIG5lZ2F0aXZlIGJpbm9taWFsCmRpc3BlcnNpb24uIEluIHRoZSBgeC1heGlzYCB0aGUgaXMgdGhlIG1lYW4gb2YgdGhlIGxvZyBub3JtYWxpemVkIGNvdW50cy4KCmBgYHtyIGRpc3AuZXN0aW1hdH0KZGdlIDwtIGVzdGltYXRlRGlzcChkZ2UsIGRlc2lnbl9tYXQsIHJvYnVzdD1UUlVFKQpgYGAKCmBgYHtyIHBsb3RfZGlzLmVzdGltYXQsIGVjaG89RiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CnBsb3RCQ1YoZGdlLCBtYWluID0gIm1pUk5BIENvdW50cyBEaXNwZXJzaW9uIEVzdGltYXRlcyIsIAogICAgICAgIGZyYW1lLnBsb3Q9RkFMU0UgLCAKICAgICAgICBjb2wudHJlbmQgPSAiIzhBMkJFMiIsIGNvbC5jb21tb24gPSAiIzAwQkZGRiIpCmJveChidHk9ImwiKQpheGlzKDIpCmF4aXMoMSkKYGBgCgo8YnI+CgpBIGNoYXJhY3RlcmlzdGljIG9mIFJOQS1zZXEgc3R1ZGllcywgaXMgdGhlIHRlbmRlbmN5IGZvciBoaWdoZXIgbmVnYXRpdmUKYmlub21pYWwgZGlzcGVyc2lvbnMgaW4gZ2VuZXMgd2l0aCB2ZXJ5IGxvdyBjb3VudHMgd2hlcmVhcyB0aGUgZGlzcGVyc2lvbiB0cmVuZApkZWNyZWFzZXMgYXN5bXB0b3RpY2FseSB0byBhIGNvbnN0YW50IHZhbHVlIGZvciBnZW5lcyB3aXRoIGhpZ2hlciBjb3VudHMKW0BjaGVuUmVhZHNHZW5lc1BhdGh3YXlzMjAxNl0uIE9uY2UgbW9yZSwgZHVlIHRvIHRoZSBpc29taVJzIG5hbWluZyBzdHJhdGVneSwKdGhlIGRhdGEgY29udGFpbnMgYSBsb3Qgb2YgbG93IGNvdW50cyBldmVuIGFmdGVyIHJlbW92aW5nIHRoZSBsZXNzIHNpZ25pZmljYW50Cm1pUk5BIHNwZWNpZXMuCgpgYGB7ciBwbG90X2NvdW50cy1kaXN0LCBlY2hvPUYsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQptaXJuYV9tZWx0IDwtIG1lbHQobG9nX25vcm1fbWlybmEpCgpnZ3Bsb3QobWlybmFfbWVsdCwgYWVzKHggPSB2YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuNSwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiI0ZGRkZGRiIsIAogICAgICAgICAgICAgICAgIGZpbGwgPSAiIzk5MDBDQyIsCiAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcpICsKICBsYWJzKCB0aXRsZSA9ICJOb3JtYWxpemVkIGxvZyBDb3VudHMgRGlzdHJpYnV0aW9uIiwgCiAgICAgICAgeCA9ICJDb3VudHMiLCB5ID0gIm1pUk5BIHNwZWNpZXMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCjxicj4KCkluIG9yZGVyIHRvIGZ1cnRoZXIgYWNjb3VudCBmb3IgdGVjaG5pY2FsIHZhcmlhYmlsaXR5LiB0aGUgbmVnYXRpdmUgYmlub21pYWwKbW9kZWwgY2FuIGJlIGV4dGVuZGVkIHdpdGggcXVhc2ktbGlrZWxpaG9vZCBtZXRob2RzIFtAbHVuSXRERWxpY2lvdXNSZWNpcGUyMDE2YV0KW0BsdW5kRGV0ZWN0aW5nRGlmZmVyZW50aWFsRXhwcmVzc2lvbjIwMTJdIC4gSW4gdGhpcyBhcHByb2FjaCwgdGhlIG5lZ2F0aXZlCmJpbm9taWFsIGRpc3BlcnNpb24gdHJlbmQgZGVzY3JpYmVzIHRoZSBiaW9sb2dpY2FsIHZhcmlhYmlsaXR5IGFjcm9zcyBhbGwKbWlSTkFzLiBUaGlzIG92ZXJhbGwgdHJlbmQgaXMgdXNlZCB0byBzZWxlY3QgdGhlIG1pUk5BLXNwZWNpZmljIHZhcmlhYmlsaXR5IHRoYXQKbGF5IGFib3ZlIGFuZCBiZWxvdyBpdC4gSW5kaXZpZHVhbCBkaXNwZXJzaW9ucyAoKnRhZ3dpc2UqKSBhcmUgbm90IHVzZWQuCgpVc2luZyB0aGUgYGVkZ2VSYCBmdW5jdGlvbiBgZ2xtUUxGaXRgIHNldmVyYWwgZW1waXJpY2FsIEJheWVzIHN0YXRpc3RpY3Mgc3VjaCBhcwp0aGUgcXVhc2ktbGlrZWxpaG9vZCBkaXNwZXJzaW9uIHRyZW5kLCB0aGUgc3F1ZWV6ZWQgcXVhc2ktbGlrZWxpaG9vZCBkaXNwZXJzaW9uCmVzdGltYXRlcyBvciB0aGUgcHJpb3IgZGVncmVlcyBvZiBmcmVlZG9tLCBhcyB3ZWxsIGFzIHRoZSBlc3RpbWF0ZWQgdmFsdWVzIG9mCnRoZSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwgY29lZmZpY2llbnRzIGZvciBlYWNoIGdlbmUgYXJlIGNvbXB1dGVkLgpJbmNvcnBvcmF0aW5nIHJvYnVzdCBoYW5kbGluZyBvZiB6ZXJvIGNvdW50cywgZXNwZWNpYWxseSB3aGVuIHRoZSBmaXR0ZWQgdmFsdWVzCm9mIHRoZXNlIGNvdW50cyB5aWVsZCB1c2VsZXNzIHJlc2lkdWFsIGRlZ3JlZXMgb2YgZnJlZWRvbSBmb3IgdGhlCnF1YXNpLWxpa2VsaWhvb2QgZGlzcGVyc2lvbiBlc3RpbWF0aW9uIApbQHBoaXBzb25Sb2J1c3RIeXBlcnBhcmFtZXRlckVzdGltYXRpb24yMDE2XSwgaXMgYSBmb2NhbCBwb2ludCBvZiB0aGlzIGZ1bmN0aW9uLgpUbyBvYnRhaW4gdGhlIHNxdWVlemVkIHF1YXNpLWxpa2VsaWhvb2QgZGlzcGVyc2lvbiBlc3RpbWF0ZXMsIHRoZSBmdW5jdGlvbgp0aWdodGVucyB0aGUgcmF3IHF1YXNpLWxpa2VsaWhvb2QgZGlzcGVyc2lvbiBlc3RpbWF0ZXMgdG8gYWRoZXJlIHRvIGEgZ2xvYmFsCnRyZW5kLiBUaGlzIHNlcnZlcyB0byBtaXRpZ2F0ZSB0aGUgdW5jZXJ0YWludHkgaW5oZXJlbnQgaW4gdGhlIGVzdGltYXRlcywKdGhlcmVieSBlbmhhbmNpbmcgdGhlIHN0YXRpc3RpY2FsIHBvd2VyIG9mIHN1YnNlcXVlbnQgdGVzdHMuIEZ1cnRoZXJtb3JlLCB0aGUKY29tcHJlc3Npb24gbWFnbml0dWRlIGR1cmluZyB0aGUgZXN0aW1hdGlvbiBpcyBjb250aW5nZW50IHVwb24gdGhlIHZhbHVlIG9mIHRoZQpwcmlvciBkZWdyZWVzIG9mIGZyZWVkb20uIFN1YnN0YW50aWFsIHByaW9yIGRlZ3JlZXMgb2YgZnJlZWRvbSBlc3RpbWF0ZXMKaW5kaWNhdGUgcXVhc2ktbGlrZWxpaG9vZCBkaXNwZXJzaW9ucyB3aXRoIGxvd2VyIHZhcmlhYmlsaXR5IGFjcm9zcyBtaVJOQQpzcGVjaWVzLiBJbiBzdWNoIHNjZW5hcmlvLCBpdCBpcyBhZHZpc2FibGUgdG8gZW5nYWdlIGluIHJvYnVzdCBlbXBpcmljYWwgQmF5ZXMKbW9kZXJhdGlvbi4gQ29udmVyc2VseSwgc21hbGxlciBwcmlvciBkZWdyZWVzIG9mIGZyZWVkb20gZXN0aW1hdGVzIGltcGx5IGEgCmhpZ2hlciBkZWdyZWUgb2YgdmFyaWFiaWxpdHkuIEluIHRoZXNlIGNhc2VzLCBhIG1pbGRlciBtb2RlcmF0aW9uIGFwcHJvYWNoIGlzIAptb3JlIGFwcHJvcHJpYXRlLiBUaGlzIG1vZHVsYXRpb24sIGVuc3VyZXMgdGhhdCB0aGUgZW1waXJpY2FsIEJheWVzIHByb2NlZHVyZQphZGFwdHMgdG8gdGhlIHVuZGVybHlpbmcgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBkYXRhLCB5aWVsZGluZyBtb3JlIGFjY3VyYXRlIGFuZAptZWFuaW5nZnVsIHJlc3VsdHMuCgpTZXR0aW5nIGByb2J1c3QgPSBUUlVFYC4gYXMgdG8gZmFjaWxpdGF0ZSB0aGUgZ2VuZXJhdGlvbiBvZiBtaVJOQS1zcGVjaWZpYyBwcmlvcgpkZWdyZWVzIG9mIGZyZWVkb20gZXN0aW1hdGVzIFRoaXMgYXBwcm9hY2ggbWluaW1pemVzIHRoZSBsaWtlbGlob29kIG9mIAplbmNvdW50ZXJpbmcgZmFsc2UgcG9zaXRpdmVzIGZyb20gbWlSTkEgc3BlY2llcyB3aXRoIGV4Y2VlZGluZ2x5IGhpZ2ggb3IgbG93IHJhdwpkaXNwZXJzaW9ucy4gVGhlcmVmb3JlLCBpdCBlbnN1cmVzIHRoYXQgdGhlIGFuYWx5c2lzIHJlbWFpbnMgcmVzaWxpZW50IGFnYWluc3QgCmRhdGEgYWJlcnJhdGlvbnMgd2hpbGUgYW1wbGlmeWluZyBpdHMgYWJpbGl0eSB0byBpZGVudGlmeSBzaWduaWZpY2FudCAKZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYWNyb3NzIHRoZSBtYWpvcml0eSBvZiBtaVJOQXMuCgpUaGUgbmV3IGRpc3BlcnNpb24gZXN0aW1hdGVzIGFyZSB2aXN1YWxpemVkIHdpdGggdGhlIGBlZGdlUmAgZnVuY3Rpb24KYHBsb3RRTERpc3BgLgoKYGBge3IgcXVhc2ktbGlrZWxpRml0fQpmaXQgPC0gZ2xtUUxGaXQoZGdlLCBkZXNpZ25fbWF0LCByb2J1c3Q9VFJVRSkKYGBgCgpgYGB7ciBwbG90X3F1YXNpLWxpa2VsaUZpdCwgZWNobz1GLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KcGxvdFFMRGlzcChmaXQsICBtYWluID0gIm1pUk5BIENvdW50cyBRdWFzaS1MaWtlbGlob29kIERpc3BlcnNpb24gRXN0aW1hdGVzIiwgCiAgICAgICAgICAgZnJhbWUucGxvdD1GQUxTRSAsIAogICAgICAgICAgIGNvbC50cmVuZCA9ICIjOEEyQkUyIiwgY29sLnNocnVuayA9ICIjMDBCRkZGIikKYm94KGJ0eT0ibCIpCmF4aXMoMikKYXhpcygxKQpgYGAKCjxicj4KCjxoMiBhbGlnbj0icmlnaHQiPkRpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzIChwLXZhbHVlKTwvaDI+CgpBcyBhIG1lYW5zIHRvIHJldHJpZXZlIHRoZSB1cGNvbWluZyByZXN1bHRzIGFzIGEgY29tcGFyaXNvbiBiZXR3ZWVuIG5vcm1hbCBhbmQKdHVtb3IgdGlzc3VlcywgYSBjb250cmFzdCBtYXRyaXggaXMgYnVpbGQgdXNpbmcgdGhlIGBsaW1tYWAKW0ByaXRjaGllTGltbWFQb3dlcnNEaWZmZXJlbnRpYWwyMDE1XSBmdW5jdGlvbiBgbWFrZUNvbnRyYXN0c2AuCgpgYGB7ciBjb250cmFzdF9tYXR9CmNvbnRyYXN0X21hdCA8LSBtYWtlQ29udHJhc3RzKE5vcm1hbCAtIFR1bW9yLCBsZXZlbHMgPSBkZXNpZ25fbWF0KQpgYGAKCjxicj4KCkFsbCB0aGUgc3Vic2VxdWVudCByZXN1bHRzIHdpbGwgbm93IGJlIHJlbGF0aXZlIHRvIG5vcm1hbCB0aXNzdWVzLiBUaGF0IGlzLCBhCnBvc2l0aXZlIGxvZ34yfi1mb2xkLWNoYW5nZSAobG9nRkMpIHdpbGwgZGVzaWduYXRlIGFuIG92ZXItZXhwcmVzc2VkIG1pUk5BIApzcGVjaWVzIGluIG5vcm1hbCB0aXNzdWVzLiBDb252ZXJzZWx5LCBhIG5lZ2F0aXZlIGxvZ0ZDIHdpbGwgZGVub3RlIGEgbW9yZQpoaWdobHkgZXhwcmVzc2VkIG1pUk5BIHNwZWNpZXMgaW4gdHVtb3IgdGlzc3Vlcy4KClRvIGFjY291bnQgZm9yIHRoZSB1bmNlcnRhaW50eSBpbiBkaXNwZXJzaW9uIGVzdGltYXRpb24gaW4gdGhlIGVycm9yIHJhdGUKY29udHJvbCwgcmF0aGVyIHRoYW4gdGhlIHVzdWFsIGxpa2VsaWhvb2QgcmF0aW8gdGVzdCwgdGhlIHF1YXNpLWxpa2VsaWhvb2QKRi10ZXN0cyBpcyBwZXJmb3JtZWQgd2l0aCB0aGUgYGVkZ2VSYCBmdW5jdGlvbiBgZ2xtUUxGVGVzdGAuIFRoaXMgYXBwcm9hY2ggbGVhZHMKdG8gKnAtdmFsdWVzKiBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gdGhlIG9uZXMgdGhhdCB0aGUgbGlrZWxpaG9vZCByYXRpbyB0ZXN0cwp3b3VsZCBoYWQgcHJvdmlkZWQgdXNpbmcgdGhlIHNhbWUgbmVnYXRpdmUgYmlub21pYWwgZGlzcGVyc2lvbnMuCgpGaW5hbGx5LCB0aGUgb2J0YWluZWQgKnAtdmFsdWVzKiBhcmUgc3VibWl0dGVkIHRvIGEgbXVsdGlwbGUgdGVzdGluZyBjb3JyZWN0aW9uCmFzIHRvIHJlZ3VsYXRlIHRoZSBmYWxzZSBkaXNjb3ZlcnkgcmF0ZSAoRkRSKSB1c2luZyB0aGUgQmVuamFtaW4tSG9jaGJlcmcKbWV0aG9kLiBUaGUgYGVkZ2VSYCBmdW5jdGlvbiBgdG9wVGFnc2AgcHJvdmlkZXMgYSB0YWJsZSB3aXRoIGFsbCB0aGUKZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIG1pUk5BIHNwZWNpZXMgdGhhdCBoYXZlIGFuIGFkanVzdGVkICpwLXZhbHVlKiBzbWFsbGVyCnRoYW4gb3IgZXF1YWwgdG8gMC4wMS4gVGhlIGRlY2lzaW9uIG9mIHNldHRpbmcgdGhlIHNpZ25pZmljYW5jZSBsZXZlbCB0byAwLjAxCmluc3RlYWQgb2YgdGhlIHVzdWFsIDAuMDUsIGlzIG1hZGUgdG8gYWNjb3VudCBmb3IgdGhlIHNtYWxsIHNpemUgb2YgdGhlIGRhdGEgc2V0CmFuZCB0aGUgc21hbGwgYmF0Y2ggZWZmZWN0IHByZXZpb3VzbHkgc2Vlbi4gVGhpcyBzdHJpbmdlbnQgc2lnbmlmaWNhbmNlIGxldmVsCmFpbXMgdG8gcmVkdWNlIHRoZSBsaWtlbGlob29kIG9mIGZhbHNlIHBvc2l0aXZlcyB0aGF0IHRoZSBtZW50aW9uZWQgZmFjdHMgY291bGQKb3JpZ2luYXRlLiBOb3RlIHRoYXQgdGhlIHRhYmxlIHdpdGggdGhlIHJlc3VsdHMgaXMgbm90IHNob3duIGFzIGl0IGlzCmF1dG9tYXRpY2FsbHkgYWRkZWQgaW4gdGhlIG5leHQgcGxvdC4KCmBgYHtyIHFsZlRlc3R9CnJlcyA8LSBnbG1RTEZUZXN0KGZpdCwgY29udHJhc3QgPSBjb250cmFzdF9tYXQpCnRvcCA8LSB0b3BUYWdzKHJlcywgbiA9IEluZiwgcC52YWx1ZSA9IDAuMDEpCmBgYAoKPGJyPgoKVXNpbmcgdGhlIGBHbGltbWFgIGZ1bmN0aW9uIGBnbGltbWFWb2xjYW5vYCB0aGUgcmVzdWx0cyBvZiB0aGUgcHJldmlvdXMgYW5hbHlzaXMKYXJlIGRpc3BsYXllZCBpbiBhbiBpbnRlcmFjdGl2ZSB2b2xjYW5vLXBsb3QuCgpgYGB7ciB2b2xjYW5vLCBlY2hvPUYsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQpnbGltbWFWb2xjYW5vKHJlcywgZGdlID0gZml0LCAKICAgICAgICAgICAgICBzdGF0dXMgPSBkZWNpZGVUZXN0c0RHRShyZXMsIHAudmFsdWUgPSAwLjAxKSwKICAgICAgICAgICAgICBtYWluID0gIk5vcm1hbCB2cyBUdW1vciIpCmBgYAoKPGJyPgoKPGgyIGFsaWduPSJyaWdodCI+RGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMgKGZvbGQtY2hhbmdlKTwvaDI+CgpUaGUgcHJldmlvdXMgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMsIHdhcyBiYXNlZCBvbiBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UuIDIxMzEgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIG1pUk5BIHNwZWNpZXMgYmV0d2VlbiB0aGUgbm9ybWFsIGFuZAp0dW1vciB0aXNzdWVzIGhhdmUgYmVlbiBmb3VuZCBhbW9uZyB0aGUgMzI5MSBwcmVzZW50IGluIHRoZSBkYXRhIHNldC4gSGF2aW5nCnRoaXMgaHVnZSBhbW91bnQgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIG1pUk5BIHNwZWNpZXMsIGluY2l0ZXMgbmFycm93aW5nCmRvd24gb2YgdGhlIHJlc3VsdHMgdG8ga2VlcCBvbmx5IHRoZSBvbmVzIHRoYXQgYXJlIG1vcmUgYmlvbG9naWNhbGx5IG1lYW5pbmdmdWwuCgpUaGlzIG5ldyBhcHByb2FjaCBmb2N1c2VzIG9uIHNwb3R0aW5nIHdoZXRoZXIgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGxvZ0ZDcwphcmUgc2lnbmlmaWNhbnRseSBicm9hZCB0aGFuIGxvZ34yfigxLjUpIGJ5IHVzaW5nIHRoZSBgZWRnZVJgIGZ1bmN0aW9uCmBnbG1UcmVhdGAuIFRoaXMgbWV0aG9kLCBwcmlvcml0aXplcyBnZW5lcyB0aGF0IGFyZSBsaWtlbHkgdG8gaGF2ZSBhIGhpZ2hlcgpiaW9sb2dpY2FsIHNpZ25pZmljYW5jZSB3aXRoIGxhcmdlciBjaGFuZ2VzLiBJdCBpcyB3b3J0aCBtZW50aW9uaW5nIHRoYXQKYGdsbVRyZWF0YCBkb2VzIG5vdCBzZXQgYSBmb2xkIGNoYW5nZSBjdXRvZmYgYnV0IGV2YWx1YXRlcyBib3RoLCB0aGUgbWFnbml0dWRlCm9mIGNoYW5nZSBvZiBleHByZXNzaW9ucyB2YWx1ZXMgYW5kIGl0cyB2YXJpYWJpbGl0eS4gVGh1cywgbm90IG9ubHkgdGhlIGxvZ0ZDCmVzdGltYXRlZCB2YWx1ZSBtdXN0IGJlIGFib3ZlIHRoZSB0aHJlc2hvbGQsIGJ1dCBpdHMgd2hvbGUgY29uZmlkZW5jZSBpbnRlcnZhbAptdXN0LgoKYGBge3IgZ2xtVHJlYXR9CnJlczIgPC0gZ2xtVHJlYXQoZml0LCBjb250cmFzdCA9IGNvbnRyYXN0X21hdCwgbGZjID0gbG9nMigxLjUpKQp0b3AyIDwtIHRvcFRhZ3MocmVzMiwgbiA9IEluZiwgcC52YWx1ZSA9IDAuMDEpCmBgYAoKPGJyPgoKVGhlIGZpbmFsIGFtb3VudCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgbWlSTkEgc3BlY2llcyBpcyAxNDQwLiBPbmNlIG1vcmUsCnVzaW5nIHRoZSBgR2xpbW1hYCBmdW5jdGlvbiBgZ2xpbW1hVm9sY2Fub2AgdGhlIHJlc3VsdHMgb2YgdGhlIGFuYWx5c2lzIGFyZQp2aXN1YWxpemVkIGluIGFuIGludGVyYWN0aXZlIHZvbGNhbm8tcGxvdC4KCmBgYHtyIHZvbGNhbm8yLCBlY2hvPUYsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQpnbGltbWFWb2xjYW5vKHJlczIsIGRnZT1maXQsIAogICAgICAgICAgICAgIHN0YXR1cyA9IGRlY2lkZVRlc3RzREdFKHJlczIsIHAudmFsdWUgPSAwLjAxKSwgCiAgICAgICAgICAgICAgbWFpbiA9ICJOb3JtYWwgdnMgVHVtb3IiKQpgYGAKCjxicj4KCiMgQmlibGlvZ3JhcGh5Cgo8aHIgY2xhc3M9ImhhbGYtd2lkdGgiPgoKPGJyPgo=