Learning Objectives
- Which statistical tests are appropriate for RNA-seq data
- Using the DESeq2 package to detect differential expression
- Basic visualisation of RNA-seq counts
- Using annotation databases to map between gene identifers
Differential expression with DESeq2
Now that we are happy that we have normalised the data and that the
quality looks good, we can continue to testing for differentially
expressed genes. There are a number of packages to analyse RNA-Seq data.
Most people use DESeq2
or edgeR
. We will use
DESeq2
for the rest of this practical.
Recap of pre-processing
The previous section walked-through the pre-processing and
transformation of the count data. Here, for completeness, we list the
minimal steps required to process the data prior to differential
expression analysis.
Note that although we spent some time looking at the quality of our
data , these steps are not required prior to performing differential
expression so are not shown here. Remember, DESeq2
requires
raw counts so the vst
transformation is not shown as
part of this basic protocol.
library(tximport)
library(DESeq2)
library(readr)
dirs <- list.files("salmon_quant/")
quant_files <- list.files("salmon_quant/",
pattern="quant.sf.gz",
recursive = TRUE,
full.names = TRUE)
names(quant_files) <- dirs
tx2gene <- read_csv("tx2gene.csv",col_names = FALSE)
txi <- tximport(quant_files,
type="salmon",
tx2gene = tx2gene)
sampleinfo <- read_tsv("meta_data/sampleInfo_corrected.txt")
dds <- DESeqDataSetFromTximport(txi,
colData = sampleinfo,
design = ~Treated)
dds$condition <- as.factor(dds$condition)
It would be a good idea to save the results of the pre-processing so
we don’t have to repeat it every time.
dir.create("Robjects/",showWarnings = FALSE)
saveRDS(dds, file="Robjects/dds.rds")
We will be using these raw counts throughout the workshop and
transforming them using methods in the DESeq2
package. If
you want to know about alternative methods for count normalisation they
are covered on this
page.
The DESeq workflow in brief
We have previously defined the test condition using the
design
argument when we created the object. This can be
checked using the design
function.
Typically we decide the design for the analysis when we create the
DESeq2 objects, but it can be modified prior to the differential
expression analysis. The design tells DESeq2
which sample
groups to compare in the differential analysis. The name specified must
correspond to a column in the sample information.
colData(dds)
DataFrame with 9 rows and 5 columns
Run condition Name Replicate
<character> <factor> <character> <numeric>
1_CTR_BC_2 1_CTR_BC_2 CTR CTR_1 1
2_TGF_BC_4 2_TGF_BC_4 TGF TGF_1 1
3_IR_BC_5 3_IR_BC_5 IR IR_1 1
4_CTR_BC_6 4_CTR_BC_6 CTR CTR_2 2
5_TGF_BC_7 5_TGF_BC_7 TGF TGF_2 2
6_IR_BC_12 6_IR_BC_12 IR IR_2 2
7_CTR_BC_13 7_CTR_BC_13 CTR CTR_3 3
8_TGF_BC_14 8_TGF_BC_14 TGF TGF_3 3
9_IR_BC_15 9_IR_BC_15 IR IR_3 3
Treated
<factor>
1_CTR_BC_2 N
2_TGF_BC_4 Y
3_IR_BC_5 Y
4_CTR_BC_6 N
5_TGF_BC_7 Y
6_IR_BC_12 Y
7_CTR_BC_13 N
8_TGF_BC_14 Y
9_IR_BC_15 Y
design(dds) <- ~Treated
The counts that we have obtained via sequencing are subject to random
sources of variation. The purpose of differential expression is to
determine if potential sources of biological variation (e.g. counts
observed from different sample groups) are greater than random
noise.
The DESeq
function runs a couple of processing steps
automatically to adjust for different library size and gene-wise
variability, which you can read about in the DESeq2
vignette and run some example code at the end of this session.
de_treated<- DESeq(dds)
de_treated
class: DESeqDataSet
dim: 57914 9
metadata(1): version
assays(6): counts avgTxLength ... H cooks
rownames(57914): ENSG00000000003 ENSG00000000005 ...
ENSG00000284747 ENSG00000284748
rowData names(22): baseMean baseVar ... deviance
maxCooks
colnames(9): 1_CTR_BC_2 2_TGF_BC_4 ... 8_TGF_BC_14
9_IR_BC_15
colData names(5): Run condition Name Replicate Treated
The results of the analysis are not immediately accessible, but can
be obtained using the results
function. Each row is a
particular gene measured in the study (i.e. all genes in the organism
being studied) and each column reports some aspect of the differential
expression analysis for that gene. Note that all genes
are reported. At this stage the gene identifiers are not very
informative, something we will fix in the next section. Furthermore, the
results
function displays results in a format which is not
compatible with standard data manipulation tools
(i.e. tidyverse
), so we will have to convert.
results(de_treated)
log2 fold change (MLE): Treated Y vs N
Wald test p-value: Treated Y vs N
DataFrame with 57914 rows and 6 columns
baseMean log2FoldChange lfcSE stat
<numeric> <numeric> <numeric> <numeric>
ENSG00000000003 1447.265802 -0.2051187 0.121418 -1.689365
ENSG00000000005 0.113575 0.5069982 3.533803 0.143471
ENSG00000000419 1814.108471 0.0773555 0.189163 0.408936
ENSG00000000457 645.735472 -0.1603703 0.124985 -1.283112
ENSG00000000460 221.065743 -0.4662569 0.423195 -1.101753
... ... ... ... ...
ENSG00000284744 8.377622 0.381924 0.672894 0.567584
ENSG00000284745 0.000000 NA NA NA
ENSG00000284746 0.102422 -0.935671 3.533803 -0.264777
ENSG00000284747 29.068925 -0.333544 0.374367 -0.890956
ENSG00000284748 0.548454 2.181462 3.495590 0.624061
pvalue padj
<numeric> <numeric>
ENSG00000000003 0.0911494 0.574408
ENSG00000000005 0.8859182 NA
ENSG00000000419 0.6825867 0.950327
ENSG00000000457 0.1994530 0.740864
ENSG00000000460 0.2705689 0.800604
... ... ...
ENSG00000284744 0.570318 0.924953
ENSG00000284745 NA NA
ENSG00000284746 0.791181 NA
ENSG00000284747 0.372953 0.861365
ENSG00000284748 0.532587 NA
Processing the DE results using tidyverse
The output can be converted into a data frame and manipulated in the
usual manner. It is recommended to use dplyr
to manipulate
the data frames with the standard set of operations detailed on the dplyr
cheatsheet
select
to pick which columns to display
filter
to restrict the rows
mutate
to add new variables to the data frame
arrange
to order the data frame according to values of
a column
The %>%
symbol refers to the piping
operation in R, which is a way of chaining operations together.
library(dplyr)
results(de_treated, tidy=TRUE)
We can sort the rows by adjusted p-value and then print the first 10
rows.
results(de_treated,tidy=TRUE) %>%
arrange(padj) %>%
head(n=10)
Or we can sort the rows and then write the resulting data frame to a
file.
dir.create("de_analysis",showWarnings = FALSE)
results(de_treated,tidy=TRUE) %>%
arrange(padj) %>%
write_csv("de_analysis/treated_Y_vs_N_DESeq_all.csv")
Filtering to the differentially-expressed genes can be achieved using
the filter
function from dplyr
.
results(de_treated,tidy=TRUE) %>%
filter(padj < 0.05) %>%
write.csv("de_analysis/treated_Y_vs_N_DESeq_sig.csv")
It is also a good idea to save the results object itself so we can
re-use later.
saveRDS(de_treated, file="Robjects/de_treated.rds")
We can discover how many differentially-expressed genes (at a
particular p-value cut-off) using the count
function
results(de_treated,tidy=TRUE) %>%
count(padj < 0.05)
Another overview of the results is to use the plotMA
function. Each point on this plot represents and individual gene with
the x- and y-axes being the overall expression level and magnitude of
difference respectively. Significant genes are automatically
highlighted. The fanning effect at low expression levels is often seen
due to high relative fold-change at low expression levels.
plotMA(de_treated)
It is also instructive to perform a “sanity” check and plot the
sample-level counts for genes with high significance. This could
highlight any other technical factors that we are not currently taking
into account. The plot is not particularly attractive, but is a good
quick diagnostic.
plotCounts(dds,"ENSG00000158258",intgroup = "Treated")
plotCounts(dds,"ENSG00000136999",intgroup = "Treated")
Exercise
- Re-run the analysis to find differentially-expressed genes between
the
TGF
treated samples and CTRL
- Write a csv file that contains results for the genes that have an
adjusted p-value less than 0.05 and a log2 fold change more
than 1, or less than -1 in the contrast of TGF vs CTRL.
- HINT: So that we don’t overwrite our results so far, it may be
convenient to create a new
DESeqDataSet
object for the new
differential expression analysis. Check the colData
to see
which analyses can be made
- Use the
plotCounts
function to visually-inspect the
most statistically-significant gene identified
dds_condition<- dds
colData(dds_condition)
## You will need to change this line to choose the correct comparison
design(dds_condition) <- ~...
Changing the direction of the contrast
In this initial analysis DESeq2
has automatically
decided which member of our sample groups to use as our baseline
(CTR
in this case). If the log2 fold changes has a positive
value this means higher expression in Treated
sample. We
can alter this by changing the contrast
argument in the
results
function
## This should give the same as the table above
results(de_treated, contrast=c("Treated","Y","N"))
## Changing the direction of the contrast
results(de_treated, contrast=c("Treated","N","Y"))
If we want to perform differential expression analysis on the
condition
variable then there are various contrasts that
can be made; IR
vs CTR
, TGF
vs
CTR
etc. When the results
function is run with
no contrast
argument specified, the table that is displayed
is for the contrast TGF vs CTR
. The
resultsNames
function can tell us which other contrasts we
can access.
dds_condition<- dds
design(dds_condition) <- ~condition
de_condition <- DESeq(dds_condition)
results(de_condition, contrast = c("condition","IR","CTR"),tidy=TRUE) %>%
arrange(padj)
results(de_condition, contrast = c("condition","IR","TGF"),tidy=TRUE) %>%
arrange(padj)
More complex designs
The examples we have used so far have performed a differential
expression analysis using a named column in the colData
object. The DESeq2
package is capable of performing more
complex analyses that can take multiple factors into consideration at
the same time; so-called “multi-factor designs”
The use of such a design could be motivated by discovering sources of
technical variation in our data that might obscure the biological
differences we would like to compare. e.g.
In the example image above the main source of variation is the batch
in which the samples were sequenced. A multi-factor analysis to compare
the various conditions, but “correct” for differences in batch, would be
as follows.
### Don't run this. It's just a code example
design(MY_DATA) <- ~ batch + condition
Likewise, if we have different treatments applied to difference
cell-lines, but the main source of variation is the cell line the
following could be used.
### Don't run this. It's just a code example
design(MY_DATA) <- ~cell_line + treatment
Adding annotation to the DESeq2 results
We would love to share these results with our collaborators, or
search for our favourite gene in the results. However, the results are
not very useful in there current form as each row is named according to
an Ensembl identifier. Whilst gene symbols are problematic and
can change over time, they are the names that are most recognisable and
make the results easier to navigate.
There are a number of ways to add annotation, but we will demonstrate
how to do this using the org.Hs.eg.db package. This package is
one of several organism-level packages in Bioconductor that are
re-built every 6 months. These packages are listed on the annotation
section of the Bioconductor, and are installed in the same way as
regular Bioconductor packages. An alternative approach is to use
biomaRt
, an interface to the BioMart resource. BioMart is much
more comprehensive, but the organism packages do not require online
access once downloaded.
### Only execute when you need to install the package
install.packages("BiocManager")
BiocManager::install("org.Hs.eg.db")
# For Human
BiocManager::install("org.Hs.eg.db")
The packages are larger in size that Bioconductor software packages,
but essentially they are databases that can be used to make
offline queries. An alternatve (biomaRt
) that
connects to the ensembl biomart
resource will be discussed later.
library(org.Hs.eg.db)
First we need to decide what information we want. In order to see
what we can extract we can run the columns
function on the
annotation database.
columns(org.Hs.eg.db)
[1] "ACCNUM" "ALIAS" "ENSEMBL"
[4] "ENSEMBLPROT" "ENSEMBLTRANS" "ENTREZID"
[7] "ENZYME" "EVIDENCE" "EVIDENCEALL"
[10] "GENENAME" "GENETYPE" "GO"
[13] "GOALL" "IPI" "MAP"
[16] "OMIM" "ONTOLOGY" "ONTOLOGYALL"
[19] "PATH" "PFAM" "PMID"
[22] "PROSITE" "REFSEQ" "SYMBOL"
[25] "UCSCKG" "UNIPROT"
We are going to filter the database by a key or set of keys in order
to extract the information we want. Valid names for the key can be
retrieved with the keytypes
function.
keytypes(org.Hs.eg.db)
[1] "ACCNUM" "ALIAS" "ENSEMBL"
[4] "ENSEMBLPROT" "ENSEMBLTRANS" "ENTREZID"
[7] "ENZYME" "EVIDENCE" "EVIDENCEALL"
[10] "GENENAME" "GENETYPE" "GO"
[13] "GOALL" "IPI" "MAP"
[16] "OMIM" "ONTOLOGY" "ONTOLOGYALL"
[19] "PATH" "PFAM" "PMID"
[22] "PROSITE" "REFSEQ" "SYMBOL"
[25] "UCSCKG" "UNIPROT"
We should see ENSEMBL
, which is the type of key we are
going to use in this case. If we are unsure what values are acceptable
for the key, we can check what keys are valid with keys
keys(org.Hs.eg.db, keytype="ENSEMBL")[1:10]
[1] "ENSG00000121410" "ENSG00000175899" "ENSG00000291190"
[4] "ENSG00000171428" "ENSG00000156006" "ENSG00000196136"
[7] "ENSG00000114771" "ENSG00000127837" "ENSG00000129673"
[10] "ENSG00000090861"
For the top gene in our analysis the call to the function would
be:-
select(org.Hs.eg.db, keys="ENSG00000158258",
keytype = "ENSEMBL",columns=c("SYMBOL","GENENAME")
)
Unfortunately, the authors of dplyr
and
AnnotationDbi
have both decided to use the name
select
in their packages. To avoid confusion and errors,
the following code is sometimes used:-
AnnotationDbi::select(org.Hs.eg.db, keys="ENSG00000158258",keytype = "ENSEMBL",columns=c("SYMBOL","GENENAME"))
To annotate our results, we definitely want gene symbols and perhaps
the full gene name. Let’s build up our annotation information into a new
data frame using the select
function.
anno <- AnnotationDbi::select(org.Hs.eg.db,keys=rownames(dds),
columns=c("SYMBOL","GENENAME"),
keytype="ENSEMBL")
# Have a look at the annotation
head(anno)
However, we have a problem that the resulting data frame has more
rows than our results table. This is due to the one-to-many
relationships that often occur when mapping between various
identifiers.
dim(anno)
[1] 58859 3
dim(dds)
[1] 57914 9
Such duplicated entries can be identified using the
duplicated
function. Fortunately, there are not too many so
hopefully we won’t lose too much information if we discard the entries
that are duplicated. The first occurrence of the duplicated ID will
still be included in the table.
anno <- AnnotationDbi::select(org.Hs.eg.db,keys=rownames(dds),
columns=c("ENSEMBL","SYMBOL","GENENAME","ENTREZID"),
keytype="ENSEMBL") %>%
filter(!duplicated(ENSEMBL))
dim(anno)
[1] 57914 4
We can bind in the annotation information to the results
data frame.
results_annotated <- results(de_treated,tidy=TRUE) %>%
left_join(anno, by=c("row"="ENSEMBL"))
head(results_annotated)
We can save the results table using the write.csv
function, which writes the results out to a csv file that you can open
in excel.
write.csv(results_annotated,file="de_analysis/treatment_Y_vs_N_DESeq_annotated.csv",row.names=FALSE)
saveRDS(results_annotated, file="Robjects/treatment_Y_vs_N_DESeq_annotated.rds")
Exercise
- Join the annotation table to your results from the DESeq analysis of
TGF
vs CTR
. Save the resulting data frame as a
csv file.
e.g. Robjects/results_TGF_vs_CTR_annotated.csv
- The publication gives examples of
COL1A1
,
COL1A2
and COL3A1
as genes that are
up-regulated in TGF-treated samples vs controls (Figure 6C).
Use your data to verify this by
- extracting their p-values
- plotting the counts for these genes
Exporting normalized counts
The DESeq
workflow applies median of ratios
normalization that accounts for differences in sequencing depth
between samples. The user does not usually need to run this step.
However, if you want a matrix of counts for some application outside of
Bioconductor the values can be extracted from the dds
object.
dds <- estimateSizeFactors(dds)
countMatrix <-counts(dds, normalized=TRUE)
head(countMatrix)
1_CTR_BC_2 2_TGF_BC_4 3_IR_BC_5 4_CTR_BC_6
ENSG00000000003 1494.338259 1288.200211 1509.16643 1510.967861
ENSG00000000005 0.000000 0.000000 0.00000 0.000000
ENSG00000000419 1595.714814 1513.050580 2052.08122 1709.346140
ENSG00000000457 654.585928 536.353587 610.56771 680.178955
ENSG00000000460 247.768049 257.534062 82.94258 238.025591
ENSG00000000938 7.914116 3.633936 12.66761 4.767613
5_TGF_BC_7 6_IR_BC_12 7_CTR_BC_13
ENSG00000000003 1447.006807 1460.331190 1757.32486
ENSG00000000005 0.000000 0.000000 0.00000
ENSG00000000419 1882.326459 2161.853195 1943.82932
ENSG00000000457 672.369676 657.377968 747.96824
ENSG00000000460 300.401172 84.589046 326.58254
ENSG00000000938 2.013035 2.009128 25.63447
8_TGF_BC_14 9_IR_BC_15
ENSG00000000003 1315.399310 1242.657289
ENSG00000000005 0.000000 1.022172
ENSG00000000419 1342.397617 2126.376893
ENSG00000000457 599.670748 652.546430
ENSG00000000460 290.916429 160.832221
ENSG00000000938 3.196483 6.971344
write.csv(countMatrix,file="normalized_counts.csv")
Full DESeq workflow
The median of ratios normalisation method is employed in DESeq2 to
account for sequencing depth and RNA composition.
Let’s go through a short worked example (courtesy of https://hbctraining.github.io/DGE_workshop/lessons/02_DGE_count_normalization.html)
to explain the process.
## create a small example matrix of "counts"
test_data <- matrix(c(1489,22,793,76,521,906,13,410,42,1196),nrow=5)
rownames(test_data) <- c("EF2A","ABCD1","MEFV","BAG1","MOV10")
colnames(test_data) <- c("SampleA","SampleB")
test_data
SampleA SampleB
EF2A 1489 906
ABCD1 22 13
MEFV 793 410
BAG1 76 42
MOV10 521 1196
Firstly, an “average” or reference sample is created that represents
the counts on a typical sample in the dataset. The geometric
mean is used rather than the arithmetic mean. In other
words the individual counts are multiplied rather than summed and the
measure should be more robust to outliers.
psuedo_ref <- sqrt(rowProds(test_data))
psuedo_ref
EF2A ABCD1 MEFV BAG1 MOV10
1161.47923 16.91153 570.20172 56.49779 789.37697
A ratios of sample to “psuedo reference” are then calculated for each
gene. We are assuming that most genes are not changing dramatically, so
this ratio should be somewhere around 1.
test_data/psuedo_ref
SampleA SampleB
EF2A 1.2819859 0.7800398
ABCD1 1.3008873 0.7687061
MEFV 1.3907359 0.7190438
BAG1 1.3451854 0.7433919
MOV10 0.6600142 1.5151189
DESeq2
defines size factors as being the median
of these ratios for each sample (median is used so any outlier genes
will not affect the normalisation).
norm_factors <- colMedians(test_data/psuedo_ref)
norm_factors
SampleA SampleB
1.3008873 0.7687061
Individual samples can then normalised by dividing the count for each
gene by the corresponding normalization factor.
test_data[,1] / norm_factors[1]
EF2A ABCD1 MEFV BAG1 MOV10
1144.60340 16.91153 609.58395 58.42166 400.49589
and for the second sample…
test_data[,2] / norm_factors[2]
EF2A ABCD1 MEFV BAG1 MOV10
1178.60387 16.91153 533.36378 54.63727 1555.86118
The size factors for each sample in our dataset can be calculated
using the estimateSizeFactorsForMatrix
function.
sf <- estimateSizeFactorsForMatrix(assay(dds))
sf
1_CTR_BC_2 2_TGF_BC_4 3_IR_BC_5 4_CTR_BC_6 5_TGF_BC_7
1.0549852 1.1812947 0.8928435 1.0990482 1.0518069
6_IR_BC_12 7_CTR_BC_13 8_TGF_BC_14 9_IR_BC_15
0.8626010 0.9491286 1.0643407 0.9783918
The estimation of these factors can also take gene-lengths into
account, and this is implemented in the estimateSizeFactors
function. Extra normalization factor data is added to the
dds
object.
dds <- estimateSizeFactors(dds)
dds
In preparation for differential expression DESeq2 also need a
reliable estimate of the variability of each gene; which it calls
dispersion.
dds <- estimateDispersions(dds)
dds
A statistical test can then be applied. As the data are count-based
and not normally-distributed a t-test would not be appropriate. Most
tests are based on a Poisson or negative-binomial
distribution; negative binomial in the case of DESeq2
.
Although you might not be familiar with the negative binomial, the
results should be in a familiar form with fold-changes and p-values for
each gene.
dds <- nbinomWaldTest(dds)
It may seem like there is a lot to remember, but fortunately there is
one convenient function that will apply the three steps
(DESeq
). The messages printed serve as reminders of the
steps included.
Acknowledgements
Original Authors: Belinda Phipson, Anna Trigos, Matt Ritchie,
Maria Doyle, Harriet Dashnow, Charity Law, Stephane
Ballereau, Oscar Rueda, Ashley Sawle Based on the course RNAseq
analysis in R delivered on May 11/12th 2016 and modified by Cancer
Research Uk Cambridge Centre for the Functional
Genomics Autumn School 2017
Resources and data files
This material has been created using the following resources:
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgUk5BLXNlcSBkYXRhIGluIFIiCmF1dGhvcjogIk1vZHVsZSBDb29yZGluYXRvciBNYXJrIER1bm5pbmciCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBjc3M6IHN0eWxlc2hlZXRzL3N0eWxlcy5jc3MKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZyA9IEZBTFNFKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0eGltcG9ydCkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KERFU2VxMikpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHJlYWRyKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZHBseXIpKQpgYGAKCgoKIyBMZWFybmluZyBPYmplY3RpdmVzCgotIFdoaWNoIHN0YXRpc3RpY2FsIHRlc3RzIGFyZSBhcHByb3ByaWF0ZSBmb3IgUk5BLXNlcSBkYXRhCi0gVXNpbmcgdGhlIERFU2VxMiBwYWNrYWdlIHRvIGRldGVjdCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgotIEJhc2ljIHZpc3VhbGlzYXRpb24gb2YgUk5BLXNlcSBjb3VudHMKLSBVc2luZyBhbm5vdGF0aW9uIGRhdGFiYXNlcyB0byBtYXAgYmV0d2VlbiBnZW5lIGlkZW50aWZlcnMKCiMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2l0aCBgREVTZXEyYAoKTm93IHRoYXQgd2UgYXJlIGhhcHB5IHRoYXQgd2UgaGF2ZSBub3JtYWxpc2VkIHRoZSBkYXRhIGFuZCB0aGF0IHRoZSBxdWFsaXR5IGxvb2tzIGdvb2QsIHdlIGNhbiBjb250aW51ZSB0byB0ZXN0aW5nIGZvciBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFRoZXJlIGFyZSBhIG51bWJlciBvZiBwYWNrYWdlcyB0byBhbmFseXNlIFJOQS1TZXEgZGF0YS4gTW9zdCBwZW9wbGUgdXNlIGBERVNlcTJgIG9yIGBlZGdlUmAuIFdlIHdpbGwgdXNlIGBERVNlcTJgIGZvciB0aGUgcmVzdCBvZiB0aGlzIHByYWN0aWNhbC4KCiMjIyBSZWNhcCBvZiBwcmUtcHJvY2Vzc2luZwoKVGhlIHByZXZpb3VzIHNlY3Rpb24gd2Fsa2VkLXRocm91Z2ggdGhlIHByZS1wcm9jZXNzaW5nIGFuZCB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgY291bnQgZGF0YS4gSGVyZSwgZm9yIGNvbXBsZXRlbmVzcywgd2UgbGlzdCB0aGUgbWluaW1hbCBzdGVwcyByZXF1aXJlZCB0byBwcm9jZXNzIHRoZSBkYXRhIHByaW9yIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgoKTm90ZSB0aGF0IGFsdGhvdWdoIHdlIHNwZW50IHNvbWUgdGltZSBsb29raW5nIGF0IHRoZSBxdWFsaXR5IG9mIG91ciBkYXRhICwgdGhlc2Ugc3RlcHMgYXJlIG5vdCByZXF1aXJlZCBwcmlvciB0byBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHNvIGFyZSBub3Qgc2hvd24gaGVyZS4gUmVtZW1iZXIsIGBERVNlcTJgIFtyZXF1aXJlcyByYXcgY291bnRzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwjd2h5LXVuLW5vcm1hbGl6ZWQtY291bnRzKSBzbyB0aGUgYHZzdGAgdHJhbnNmb3JtYXRpb24gaXMgbm90IHNob3duIGFzIHBhcnQgb2YgdGhpcyBiYXNpYyBwcm90b2NvbC4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodHhpbXBvcnQpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KHJlYWRyKQpkaXJzIDwtIGxpc3QuZmlsZXMoInNhbG1vbl9xdWFudC8iKQpxdWFudF9maWxlcyA8LSBsaXN0LmZpbGVzKCJzYWxtb25fcXVhbnQvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuPSJxdWFudC5zZi5neiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjdXJzaXZlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkKbmFtZXMocXVhbnRfZmlsZXMpIDwtIGRpcnMKCnR4MmdlbmUgPC0gcmVhZF9jc3YoInR4MmdlbmUuY3N2Iixjb2xfbmFtZXMgPSBGQUxTRSkKCnR4aSA8LSB0eGltcG9ydChxdWFudF9maWxlcywKICAgICAgICAgICAgICAgIHR5cGU9InNhbG1vbiIsCiAgICAgICAgICAgICAgICB0eDJnZW5lID0gdHgyZ2VuZSkKCnNhbXBsZWluZm8gPC0gcmVhZF90c3YoIm1ldGFfZGF0YS9zYW1wbGVJbmZvX2NvcnJlY3RlZC50eHQiKQoKCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tVHhpbXBvcnQodHhpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlaW5mbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+VHJlYXRlZCkKZGRzJGNvbmRpdGlvbiA8LSBhcy5mYWN0b3IoZGRzJGNvbmRpdGlvbikKYGBgCgpJdCB3b3VsZCBiZSBhIGdvb2QgaWRlYSB0byBzYXZlIHRoZSByZXN1bHRzIG9mIHRoZSBwcmUtcHJvY2Vzc2luZyBzbyB3ZSBkb24ndCBoYXZlIHRvIHJlcGVhdCBpdCBldmVyeSB0aW1lLgoKYGBge3J9CmRpci5jcmVhdGUoIlJvYmplY3RzLyIsc2hvd1dhcm5pbmdzID0gRkFMU0UpCnNhdmVSRFMoZGRzLCBmaWxlPSJSb2JqZWN0cy9kZHMucmRzIikKYGBgCgoKV2Ugd2lsbCBiZSB1c2luZyB0aGVzZSByYXcgY291bnRzIHRocm91Z2hvdXQgdGhlIHdvcmtzaG9wIGFuZCB0cmFuc2Zvcm1pbmcgdGhlbSB1c2luZyBtZXRob2RzIGluIHRoZSBgREVTZXEyYCBwYWNrYWdlLiBJZiB5b3Ugd2FudCB0byBrbm93IGFib3V0IGFsdGVybmF0aXZlIG1ldGhvZHMgZm9yIGNvdW50IG5vcm1hbGlzYXRpb24gdGhleSBhcmUgY292ZXJlZCBvbiBbdGhpcyBwYWdlXShodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wMl9ER0VfY291bnRfbm9ybWFsaXphdGlvbi5odG1sKS4KCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KSWYgeW91IGhhdmUgcHJvYmxlbXMgcnVubmluZyB0aGVzZSBzdGVwcywgeW91IGNhbiByZS1sb2FkIGEgcHJlLXByb2Nlc3NlZCBvYmplY3QgZnJvbSB0aGUgY291cnNlIG1hdGVyaWFscwoKYGBge3IgZXZhbD1GQUxTRX0KZGRzIDwtIHJlYWRSRFMoIlJvYmplY3RzL2Rkc19CQUNLVVAucmRzIikKYGBgCgoKPC9kaXY+CgoKCiMgVGhlIERFU2VxIHdvcmtmbG93IGluIGJyaWVmCgpXZSBoYXZlIHByZXZpb3VzbHkgZGVmaW5lZCB0aGUgdGVzdCBjb25kaXRpb24gdXNpbmcgdGhlIGBkZXNpZ25gIGFyZ3VtZW50IHdoZW4gd2UgY3JlYXRlZCB0aGUgb2JqZWN0LiBUaGlzIGNhbiBiZSBjaGVja2VkIHVzaW5nIHRoZSBgZGVzaWduYCBmdW5jdGlvbi4KClR5cGljYWxseSB3ZSBkZWNpZGUgdGhlIGRlc2lnbiBmb3IgdGhlIGFuYWx5c2lzIHdoZW4gd2UgY3JlYXRlIHRoZSBERVNlcTIgb2JqZWN0cywgYnV0IGl0IGNhbiBiZSBtb2RpZmllZCBwcmlvciB0byB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuIFRoZSBkZXNpZ24gdGVsbHMgYERFU2VxMmAgd2hpY2ggc2FtcGxlIGdyb3VwcyB0byBjb21wYXJlIGluIHRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMuIFRoZSBuYW1lIHNwZWNpZmllZCBtdXN0IGNvcnJlc3BvbmQgdG8gYSBjb2x1bW4gaW4gdGhlIHNhbXBsZSBpbmZvcm1hdGlvbi4KCmBgYHtyfQpjb2xEYXRhKGRkcykKZGVzaWduKGRkcykgPC0gflRyZWF0ZWQKYGBgCgpUaGUgY291bnRzIHRoYXQgd2UgaGF2ZSBvYnRhaW5lZCB2aWEgc2VxdWVuY2luZyBhcmUgc3ViamVjdCB0byByYW5kb20gc291cmNlcyBvZiB2YXJpYXRpb24uIFRoZSBwdXJwb3NlIG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGlzIHRvIGRldGVybWluZSBpZiBwb3RlbnRpYWwgc291cmNlcyBvZiBiaW9sb2dpY2FsIHZhcmlhdGlvbiAoZS5nLiBjb3VudHMgb2JzZXJ2ZWQgZnJvbSBkaWZmZXJlbnQgc2FtcGxlIGdyb3VwcykgYXJlIGdyZWF0ZXIgdGhhbiByYW5kb20gbm9pc2UuCgoKVGhlIGBERVNlcWAgZnVuY3Rpb24gcnVucyBhIGNvdXBsZSBvZiBwcm9jZXNzaW5nIHN0ZXBzIGF1dG9tYXRpY2FsbHkgdG8gYWRqdXN0IGZvciBkaWZmZXJlbnQgbGlicmFyeSBzaXplIGFuZCBnZW5lLXdpc2UgdmFyaWFiaWxpdHksIHdoaWNoIHlvdSBjYW4gcmVhZCBhYm91dCBpbiB0aGUgW0RFU2VxMiB2aWduZXR0ZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3RoZS1kZXNlcTItbW9kZWwpIGFuZCBydW4gc29tZSBleGFtcGxlIGNvZGUgYXQgdGhlIFtlbmQgb2YgdGhpcyBzZXNzaW9uXSgjRnVsbF9ERVNlcV93b3JrZmxvdykuIAoKCmBgYHtyfQpkZV90cmVhdGVkPC0gREVTZXEoZGRzKQpkZV90cmVhdGVkCmBgYAoKCgpUaGUgcmVzdWx0cyBvZiB0aGUgYW5hbHlzaXMgYXJlIG5vdCBpbW1lZGlhdGVseSBhY2Nlc3NpYmxlLCBidXQgY2FuIGJlIG9idGFpbmVkIHVzaW5nIHRoZSBgcmVzdWx0c2AgZnVuY3Rpb24uIEVhY2ggcm93IGlzIGEgcGFydGljdWxhciBnZW5lIG1lYXN1cmVkIGluIHRoZSBzdHVkeSAoaS5lLiBhbGwgZ2VuZXMgaW4gdGhlIG9yZ2FuaXNtIGJlaW5nIHN0dWRpZWQpIGFuZCBlYWNoIGNvbHVtbiByZXBvcnRzIHNvbWUgYXNwZWN0IG9mIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBmb3IgdGhhdCBnZW5lLiBOb3RlIHRoYXQgKiphbGwgZ2VuZXMqKiBhcmUgcmVwb3J0ZWQuIEF0IHRoaXMgc3RhZ2UgdGhlIGdlbmUgaWRlbnRpZmllcnMgYXJlIG5vdCB2ZXJ5IGluZm9ybWF0aXZlLCBzb21ldGhpbmcgd2Ugd2lsbCBmaXggaW4gdGhlIG5leHQgc2VjdGlvbi4gRnVydGhlcm1vcmUsIHRoZSBgcmVzdWx0c2AgZnVuY3Rpb24gZGlzcGxheXMgcmVzdWx0cyBpbiBhIGZvcm1hdCB3aGljaCBpcyBub3QgY29tcGF0aWJsZSB3aXRoIHN0YW5kYXJkIGRhdGEgbWFuaXB1bGF0aW9uIHRvb2xzIChpLmUuIGB0aWR5dmVyc2VgKSwgc28gd2Ugd2lsbCBoYXZlIHRvIGNvbnZlcnQuCgpgYGB7cn0KcmVzdWx0cyhkZV90cmVhdGVkKQpgYGAKCiMgUHJvY2Vzc2luZyB0aGUgREUgcmVzdWx0cyB1c2luZyB0aWR5dmVyc2UKClRoZSBvdXRwdXQgY2FuIGJlIGNvbnZlcnRlZCBpbnRvIGEgZGF0YSBmcmFtZSBhbmQgbWFuaXB1bGF0ZWQgaW4gdGhlIHVzdWFsIG1hbm5lci4gSXQgaXMgcmVjb21tZW5kZWQgdG8gdXNlIGBkcGx5cmAgdG8gbWFuaXB1bGF0ZSB0aGUgZGF0YSBmcmFtZXMgd2l0aCB0aGUgc3RhbmRhcmQgc2V0IG9mIG9wZXJhdGlvbnMgZGV0YWlsZWQgb24gdGhlIFtkcGx5ciBjaGVhdHNoZWV0XShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNS8wMi9kYXRhLXdyYW5nbGluZy1jaGVhdHNoZWV0LnBkZikKCi0gYHNlbGVjdGAgdG8gcGljayB3aGljaCBjb2x1bW5zIHRvIGRpc3BsYXkKLSBgZmlsdGVyYCB0byByZXN0cmljdCB0aGUgcm93cwotIGBtdXRhdGVgIHRvIGFkZCBuZXcgdmFyaWFibGVzIHRvIHRoZSBkYXRhIGZyYW1lCi0gYGFycmFuZ2VgIHRvIG9yZGVyIHRoZSBkYXRhIGZyYW1lIGFjY29yZGluZyB0byB2YWx1ZXMgb2YgYSBjb2x1bW4KClRoZSBgJT4lYCBzeW1ib2wgcmVmZXJzIHRvIHRoZSBbcGlwaW5nIG9wZXJhdGlvbl0oaHR0cHM6Ly9zYmMuc2hlZi5hYy51ay9yLW9ubGluZS9wYXJ0Mi5uYi5odG1sIyVFMiU4MCU5Q1BpcGluZyVFMiU4MCU5RCkgaW4gUiwgd2hpY2ggaXMgYSB3YXkgb2YgY2hhaW5pbmcgb3BlcmF0aW9ucyB0b2dldGhlci4gCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQogcmVzdWx0cyhkZV90cmVhdGVkLCB0aWR5PVRSVUUpCmBgYAoKV2UgY2FuIHNvcnQgdGhlIHJvd3MgYnkgYWRqdXN0ZWQgcC12YWx1ZSBhbmQgdGhlbiBwcmludCB0aGUgZmlyc3QgMTAgcm93cy4KCmBgYHtyfQpyZXN1bHRzKGRlX3RyZWF0ZWQsdGlkeT1UUlVFKSAlPiUKICBhcnJhbmdlKHBhZGopICU+JSAgCiAgaGVhZChuPTEwKQpgYGAKCk9yIHdlIGNhbiBzb3J0IHRoZSByb3dzIGFuZCB0aGVuIHdyaXRlIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZSB0byBhIGZpbGUuCgpgYGB7cn0KZGlyLmNyZWF0ZSgiZGVfYW5hbHlzaXMiLHNob3dXYXJuaW5ncyA9IEZBTFNFKQogcmVzdWx0cyhkZV90cmVhdGVkLHRpZHk9VFJVRSkgJT4lCiAgYXJyYW5nZShwYWRqKSAlPiUgCiAgIHdyaXRlX2NzdigiZGVfYW5hbHlzaXMvdHJlYXRlZF9ZX3ZzX05fREVTZXFfYWxsLmNzdiIpCmBgYAoKRmlsdGVyaW5nIHRvIHRoZSBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgZ2VuZXMgY2FuIGJlIGFjaGlldmVkIHVzaW5nIHRoZSBgZmlsdGVyYCBmdW5jdGlvbiBmcm9tIGBkcGx5cmAuCgpgYGB7cn0KIHJlc3VsdHMoZGVfdHJlYXRlZCx0aWR5PVRSVUUpICU+JQogIGZpbHRlcihwYWRqIDwgMC4wNSkgJT4lIAogIHdyaXRlLmNzdigiZGVfYW5hbHlzaXMvdHJlYXRlZF9ZX3ZzX05fREVTZXFfc2lnLmNzdiIpCmBgYAoKSXQgaXMgYWxzbyBhIGdvb2QgaWRlYSB0byBzYXZlIHRoZSByZXN1bHRzIG9iamVjdCBpdHNlbGYgc28gd2UgY2FuIHJlLXVzZSBsYXRlci4KCmBgYHtyfQpzYXZlUkRTKGRlX3RyZWF0ZWQsIGZpbGU9IlJvYmplY3RzL2RlX3RyZWF0ZWQucmRzIikKYGBgCgpXZSBjYW4gZGlzY292ZXIgaG93IG1hbnkgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkIGdlbmVzIChhdCBhIHBhcnRpY3VsYXIgcC12YWx1ZSBjdXQtb2ZmKSB1c2luZyB0aGUgYGNvdW50YCBmdW5jdGlvbgoKYGBge3J9CnJlc3VsdHMoZGVfdHJlYXRlZCx0aWR5PVRSVUUpICU+JQpjb3VudChwYWRqIDwgMC4wNSkKYGBgCgpBbm90aGVyIG92ZXJ2aWV3IG9mIHRoZSByZXN1bHRzIGlzIHRvIHVzZSB0aGUgYHBsb3RNQWAgZnVuY3Rpb24uIEVhY2ggcG9pbnQgb24gdGhpcyBwbG90IHJlcHJlc2VudHMgYW5kIGluZGl2aWR1YWwgZ2VuZSB3aXRoIHRoZSB4LSBhbmQgeS1heGVzIGJlaW5nIHRoZSBvdmVyYWxsIGV4cHJlc3Npb24gbGV2ZWwgYW5kIG1hZ25pdHVkZSBvZiBkaWZmZXJlbmNlIHJlc3BlY3RpdmVseS4gU2lnbmlmaWNhbnQgZ2VuZXMgYXJlIGF1dG9tYXRpY2FsbHkgaGlnaGxpZ2h0ZWQuIFRoZSBmYW5uaW5nIGVmZmVjdCBhdCBsb3cgZXhwcmVzc2lvbiBsZXZlbHMgaXMgb2Z0ZW4gc2VlbiBkdWUgdG8gaGlnaCByZWxhdGl2ZSBmb2xkLWNoYW5nZSBhdCBsb3cgZXhwcmVzc2lvbiBsZXZlbHMuIAoKYGBge3J9CnBsb3RNQShkZV90cmVhdGVkKQpgYGAKCkl0IGlzIGFsc28gaW5zdHJ1Y3RpdmUgdG8gcGVyZm9ybSBhICJzYW5pdHkiIGNoZWNrIGFuZCBwbG90IHRoZSBzYW1wbGUtbGV2ZWwgY291bnRzIGZvciBnZW5lcyB3aXRoIGhpZ2ggc2lnbmlmaWNhbmNlLiBUaGlzIGNvdWxkIGhpZ2hsaWdodCBhbnkgb3RoZXIgdGVjaG5pY2FsIGZhY3RvcnMgdGhhdCB3ZSBhcmUgbm90IGN1cnJlbnRseSB0YWtpbmcgaW50byBhY2NvdW50LiBUaGUgcGxvdCBpcyBub3QgcGFydGljdWxhcmx5IGF0dHJhY3RpdmUsIGJ1dCBpcyBhIGdvb2QgcXVpY2sgZGlhZ25vc3RpYy4KCmBgYHtyfQpwbG90Q291bnRzKGRkcywiRU5TRzAwMDAwMTU4MjU4IixpbnRncm91cCA9ICJUcmVhdGVkIikKYGBgCgpgYGB7cn0KcGxvdENvdW50cyhkZHMsIkVOU0cwMDAwMDEzNjk5OSIsaW50Z3JvdXAgPSAiVHJlYXRlZCIpCmBgYAoKCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KSWYgeW91ciBzdHVkeSBpbnZvbHZlcyBrbm9ja2luZy1vdXQgYSBwYXJ0aWN1bGFyIGdlbmUsIG9yIHlvdSBoYXZlIHNvbWUgcG9zaXRpdmUgY29udHJvbHMgdGhhdCBhcmUga25vd24gaW4gYWR2YW5jZSwgaXQgd291bGQgYmUgYSBnb29kIGlkZWEgdG8gdmlzdWFsaXNlIHRoZWlyIGV4cHJlc3Npb24gbGV2ZWwgd2l0aCBgcGxvdENvdW50c2AuCjwvZGl2PgoKIyBFeGVyY2lzZQoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgotIFJlLXJ1biB0aGUgYW5hbHlzaXMgdG8gZmluZCBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgZ2VuZXMgYmV0d2VlbiB0aGUgYFRHRmAgdHJlYXRlZCBzYW1wbGVzIGFuZCBgQ1RSTGAKLSBXcml0ZSBhIGNzdiBmaWxlIHRoYXQgY29udGFpbnMgcmVzdWx0cyBmb3IgdGhlIGdlbmVzIHRoYXQgaGF2ZSBhbiAqYWRqdXN0ZWQqIHAtdmFsdWUgbGVzcyB0aGFuIDAuMDUgYW5kIGEgbG9nMiBmb2xkIGNoYW5nZSBtb3JlIHRoYW4gMSwgb3IgbGVzcyB0aGFuIC0xIGluIHRoZSBjb250cmFzdCBvZiBUR0YgdnMgQ1RSTC4KICAtIEhJTlQ6IFNvIHRoYXQgd2UgZG9uJ3Qgb3ZlcndyaXRlIG91ciByZXN1bHRzIHNvIGZhciwgaXQgbWF5IGJlIGNvbnZlbmllbnQgdG8gY3JlYXRlIGEgbmV3IGBERVNlcURhdGFTZXRgIG9iamVjdCBmb3IgdGhlIG5ldyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4gQ2hlY2sgdGhlIGBjb2xEYXRhYCB0byBzZWUgd2hpY2ggYW5hbHlzZXMgY2FuIGJlIG1hZGUKLSBVc2UgdGhlIGBwbG90Q291bnRzYCBmdW5jdGlvbiB0byB2aXN1YWxseS1pbnNwZWN0IHRoZSBtb3N0IHN0YXRpc3RpY2FsbHktc2lnbmlmaWNhbnQgZ2VuZSBpZGVudGlmaWVkCgpgYGB7ciBldmFsPUZBTFNFfQoKZGRzX2NvbmRpdGlvbjwtIGRkcwpjb2xEYXRhKGRkc19jb25kaXRpb24pCiMjIFlvdSB3aWxsIG5lZWQgdG8gY2hhbmdlIHRoaXMgbGluZSB0byBjaG9vc2UgdGhlIGNvcnJlY3QgY29tcGFyaXNvbgpkZXNpZ24oZGRzX2NvbmRpdGlvbikgPC0gfi4uLgpgYGAKCjwvZGl2PgoKCgojIENoYW5naW5nIHRoZSBkaXJlY3Rpb24gb2YgdGhlIGNvbnRyYXN0CgpJbiB0aGlzIGluaXRpYWwgYW5hbHlzaXMgYERFU2VxMmAgaGFzIGF1dG9tYXRpY2FsbHkgZGVjaWRlZCB3aGljaCBtZW1iZXIgb2Ygb3VyIHNhbXBsZSBncm91cHMgdG8gdXNlIGFzIG91ciBiYXNlbGluZSAoYENUUmAgaW4gdGhpcyBjYXNlKS4gSWYgdGhlIGxvZzIgZm9sZCBjaGFuZ2VzIGhhcyBhIHBvc2l0aXZlIHZhbHVlIHRoaXMgbWVhbnMgaGlnaGVyIGV4cHJlc3Npb24gaW4gYFRyZWF0ZWRgIHNhbXBsZS4gV2UgY2FuIGFsdGVyIHRoaXMgYnkgY2hhbmdpbmcgdGhlIGBjb250cmFzdGAgYXJndW1lbnQgaW4gdGhlIGByZXN1bHRzYCBmdW5jdGlvbgoKCmBgYHtyIGV2YWw9RkFMU0V9CiMjIFRoaXMgc2hvdWxkIGdpdmUgdGhlIHNhbWUgYXMgdGhlIHRhYmxlIGFib3ZlCnJlc3VsdHMoZGVfdHJlYXRlZCwgY29udHJhc3Q9YygiVHJlYXRlZCIsIlkiLCJOIikpCiMjIENoYW5naW5nIHRoZSBkaXJlY3Rpb24gb2YgdGhlIGNvbnRyYXN0CnJlc3VsdHMoZGVfdHJlYXRlZCwgY29udHJhc3Q9YygiVHJlYXRlZCIsIk4iLCJZIikpCmBgYAoKSWYgd2Ugd2FudCB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG9uIHRoZSBgY29uZGl0aW9uYCB2YXJpYWJsZSB0aGVuIHRoZXJlIGFyZSB2YXJpb3VzIGNvbnRyYXN0cyB0aGF0IGNhbiBiZSBtYWRlOyBgSVJgIHZzIGBDVFJgLCBgVEdGYCB2cyBgQ1RSYCBldGMuIFdoZW4gdGhlIGByZXN1bHRzYCBmdW5jdGlvbiBpcyBydW4gd2l0aCBubyBgY29udHJhc3RgIGFyZ3VtZW50IHNwZWNpZmllZCwgdGhlIHRhYmxlIHRoYXQgaXMgZGlzcGxheWVkIGlzIGZvciB0aGUgY29udHJhc3QgYFRHRiB2cyBDVFJgLiBUaGUgYHJlc3VsdHNOYW1lc2AgZnVuY3Rpb24gY2FuIHRlbGwgdXMgd2hpY2ggb3RoZXIgY29udHJhc3RzIHdlIGNhbiBhY2Nlc3MuCgoKYGBge3IgfQpkZHNfY29uZGl0aW9uPC0gZGRzCmRlc2lnbihkZHNfY29uZGl0aW9uKSA8LSB+Y29uZGl0aW9uCmRlX2NvbmRpdGlvbiA8LSBERVNlcShkZHNfY29uZGl0aW9uKQpyZXN1bHRzKGRlX2NvbmRpdGlvbiwgY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJJUiIsIkNUUiIpLHRpZHk9VFJVRSkgICU+JQogIGFycmFuZ2UocGFkaikgCmBgYAoKYGBge3J9CnJlc3VsdHMoZGVfY29uZGl0aW9uLCBjb250cmFzdCA9IGMoImNvbmRpdGlvbiIsIklSIiwiVEdGIiksdGlkeT1UUlVFKSAlPiUKICBhcnJhbmdlKHBhZGopIApgYGAKCgojIE1vcmUgY29tcGxleCBkZXNpZ25zCgpUaGUgZXhhbXBsZXMgd2UgaGF2ZSB1c2VkIHNvIGZhciBoYXZlIHBlcmZvcm1lZCBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHVzaW5nIGEgbmFtZWQgY29sdW1uIGluIHRoZSBgY29sRGF0YWAgb2JqZWN0LiBUaGUgYERFU2VxMmAgcGFja2FnZSBpcyBjYXBhYmxlIG9mIHBlcmZvcm1pbmcgbW9yZSBjb21wbGV4IGFuYWx5c2VzIHRoYXQgY2FuIHRha2UgbXVsdGlwbGUgZmFjdG9ycyBpbnRvIGNvbnNpZGVyYXRpb24gYXQgdGhlIHNhbWUgdGltZTsgc28tY2FsbGVkICJtdWx0aS1mYWN0b3IgZGVzaWducyIKCi0gW011bHRpLWZhY3RvciBkZXNpZ25zIGluIERFU2VxMl0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI211bHRpLWZhY3Rvci1kZXNpZ25zKQoKVGhlIHVzZSBvZiBzdWNoIGEgZGVzaWduIGNvdWxkIGJlIG1vdGl2YXRlZCBieSBkaXNjb3ZlcmluZyBzb3VyY2VzIG9mIHRlY2huaWNhbCB2YXJpYXRpb24gaW4gb3VyIGRhdGEgdGhhdCBtaWdodCBvYnNjdXJlIHRoZSBiaW9sb2dpY2FsIGRpZmZlcmVuY2VzIHdlIHdvdWxkIGxpa2UgdG8gY29tcGFyZS4gZS5nLgoKIVtdKGltYWdlcy9iYXRjaF9lZmZlY3QucG5nKQoKSW4gdGhlIGV4YW1wbGUgaW1hZ2UgYWJvdmUgdGhlIG1haW4gc291cmNlIG9mIHZhcmlhdGlvbiBpcyB0aGUgYmF0Y2ggaW4gd2hpY2ggdGhlIHNhbXBsZXMgd2VyZSBzZXF1ZW5jZWQuIEEgbXVsdGktZmFjdG9yIGFuYWx5c2lzIHRvIGNvbXBhcmUgdGhlIHZhcmlvdXMgY29uZGl0aW9ucywgYnV0ICJjb3JyZWN0IiBmb3IgZGlmZmVyZW5jZXMgaW4gYmF0Y2gsIHdvdWxkIGJlIGFzIGZvbGxvd3MuCgpgYGB7ciBldmFsPUZBTFNFfQojIyMgRG9uJ3QgcnVuIHRoaXMuIEl0J3MganVzdCBhIGNvZGUgZXhhbXBsZQpkZXNpZ24oTVlfREFUQSkgPC0gfiBiYXRjaCArIGNvbmRpdGlvbgpgYGAKCkxpa2V3aXNlLCBpZiB3ZSBoYXZlIGRpZmZlcmVudCB0cmVhdG1lbnRzIGFwcGxpZWQgdG8gZGlmZmVyZW5jZSBjZWxsLWxpbmVzLCBidXQgdGhlIG1haW4gc291cmNlIG9mIHZhcmlhdGlvbiBpcyB0aGUgY2VsbCBsaW5lIHRoZSBmb2xsb3dpbmcgY291bGQgYmUgdXNlZC4KCmBgYHtyIGV2YWw9RkFMU0V9CiMjIyBEb24ndCBydW4gdGhpcy4gSXQncyBqdXN0IGEgY29kZSBleGFtcGxlCmRlc2lnbihNWV9EQVRBKSA8LSB+Y2VsbF9saW5lICsgdHJlYXRtZW50CmBgYAoKCgojIEFkZGluZyBhbm5vdGF0aW9uIHRvIHRoZSBERVNlcTIgcmVzdWx0cwoKV2Ugd291bGQgbG92ZSB0byBzaGFyZSB0aGVzZSByZXN1bHRzIHdpdGggb3VyIGNvbGxhYm9yYXRvcnMsIG9yIHNlYXJjaCBmb3Igb3VyIGZhdm91cml0ZSBnZW5lIGluIHRoZSByZXN1bHRzLiBIb3dldmVyLCB0aGUgcmVzdWx0cyBhcmUgbm90IHZlcnkgdXNlZnVsIGluIHRoZXJlIGN1cnJlbnQgZm9ybSBhcyBlYWNoIHJvdyBpcyBuYW1lZCBhY2NvcmRpbmcgdG8gYW4gKkVuc2VtYmwqIGlkZW50aWZpZXIuIFdoaWxzdCBnZW5lIHN5bWJvbHMgYXJlIHByb2JsZW1hdGljIGFuZCBjYW4gY2hhbmdlIG92ZXIgdGltZSwgdGhleSBhcmUgdGhlIG5hbWVzIHRoYXQgYXJlIG1vc3QgcmVjb2duaXNhYmxlIGFuZCBtYWtlIHRoZSByZXN1bHRzIGVhc2llciB0byBuYXZpZ2F0ZS4KClRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHRvIGFkZCBhbm5vdGF0aW9uLCBidXQgd2Ugd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG8gZG8gdGhpcyB1c2luZyB0aGUgKm9yZy5Icy5lZy5kYiogcGFja2FnZS4gVGhpcyBwYWNrYWdlIGlzIG9uZSBvZiBzZXZlcmFsICpvcmdhbmlzbS1sZXZlbCogcGFja2FnZXMgaW4gQmlvY29uZHVjdG9yIHRoYXQgYXJlIHJlLWJ1aWx0IGV2ZXJ5IDYgbW9udGhzLiBUaGVzZSBwYWNrYWdlcyBhcmUgbGlzdGVkIG9uIHRoZSBbYW5ub3RhdGlvbiBzZWN0aW9uXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL0Jpb2NWaWV3cy5odG1sI19fX0Fubm90YXRpb25EYXRhKSBvZiB0aGUgQmlvY29uZHVjdG9yLCBhbmQgYXJlIGluc3RhbGxlZCBpbiB0aGUgc2FtZSB3YXkgYXMgcmVndWxhciBCaW9jb25kdWN0b3IgcGFja2FnZXMuIEFuIGFsdGVybmF0aXZlIGFwcHJvYWNoIGlzIHRvIHVzZSBgYmlvbWFSdGAsIGFuIGludGVyZmFjZSB0byB0aGUgW0Jpb01hcnRdKGh0dHA6Ly93d3cuYmlvbWFydC5vcmcvKSByZXNvdXJjZS4gQmlvTWFydCBpcyBtdWNoIG1vcmUgY29tcHJlaGVuc2l2ZSwgYnV0IHRoZSBvcmdhbmlzbSBwYWNrYWdlcyBkbyBub3QgcmVxdWlyZSBvbmxpbmUgYWNjZXNzIG9uY2UgZG93bmxvYWRlZC4KCgpgYGB7ciBldmFsPUZBTFNFfQojIyMgT25seSBleGVjdXRlIHdoZW4geW91IG5lZWQgdG8gaW5zdGFsbCB0aGUgcGFja2FnZQppbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJvcmcuSHMuZWcuZGIiKQojIEZvciBIdW1hbgpCaW9jTWFuYWdlcjo6aW5zdGFsbCgib3JnLkhzLmVnLmRiIikKYGBgCgpUaGUgcGFja2FnZXMgYXJlIGxhcmdlciBpbiBzaXplIHRoYXQgQmlvY29uZHVjdG9yIHNvZnR3YXJlIHBhY2thZ2VzLCBidXQgZXNzZW50aWFsbHkgdGhleSBhcmUgZGF0YWJhc2VzIHRoYXQgY2FuIGJlIHVzZWQgdG8gbWFrZSAqb2ZmbGluZSogcXVlcmllcy4gQW4gYWx0ZXJuYXR2ZSAoYGJpb21hUnRgKSB0aGF0IGNvbm5lY3RzIHRvIHRoZSBlbnNlbWJsIFtiaW9tYXJ0XShodHRwczovL3d3dy5lbnNlbWJsLm9yZy9pbmZvL2RhdGEvYmlvbWFydC9pbmRleC5odG1sKSByZXNvdXJjZSB3aWxsIGJlIGRpc2N1c3NlZCBsYXRlci4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkob3JnLkhzLmVnLmRiKQpgYGAKCgoKRmlyc3Qgd2UgbmVlZCB0byBkZWNpZGUgd2hhdCBpbmZvcm1hdGlvbiB3ZSB3YW50LiBJbiBvcmRlciB0byBzZWUgd2hhdCB3ZSBjYW4gZXh0cmFjdCB3ZSBjYW4gcnVuIHRoZSBgY29sdW1uc2AgZnVuY3Rpb24gb24gdGhlIGFubm90YXRpb24gZGF0YWJhc2UuCgpgYGB7cn0KY29sdW1ucyhvcmcuSHMuZWcuZGIpCmBgYAoKV2UgYXJlIGdvaW5nIHRvIGZpbHRlciB0aGUgZGF0YWJhc2UgYnkgYSBrZXkgb3Igc2V0IG9mIGtleXMgaW4gb3JkZXIgdG8gZXh0cmFjdCB0aGUgaW5mb3JtYXRpb24gd2Ugd2FudC4gVmFsaWQgbmFtZXMgZm9yIHRoZSBrZXkgY2FuIGJlIHJldHJpZXZlZCB3aXRoIHRoZSBga2V5dHlwZXNgIGZ1bmN0aW9uLgoKYGBge3J9CmtleXR5cGVzKG9yZy5Icy5lZy5kYikKYGBgCgpXZSBzaG91bGQgc2VlIGBFTlNFTUJMYCwgd2hpY2ggaXMgdGhlIHR5cGUgb2Yga2V5IHdlIGFyZSBnb2luZyB0byB1c2UgaW4gdGhpcyBjYXNlLiBJZiB3ZSBhcmUgdW5zdXJlIHdoYXQgdmFsdWVzIGFyZSBhY2NlcHRhYmxlIGZvciB0aGUga2V5LCB3ZSBjYW4gY2hlY2sgd2hhdCBrZXlzIGFyZSB2YWxpZCB3aXRoIGBrZXlzYAoKYGBge3J9CmtleXMob3JnLkhzLmVnLmRiLCBrZXl0eXBlPSJFTlNFTUJMIilbMToxMF0KYGBgCgpGb3IgdGhlIHRvcCBnZW5lIGluIG91ciBhbmFseXNpcyB0aGUgY2FsbCB0byB0aGUgZnVuY3Rpb24gd291bGQgYmU6LQoKYGBge3IgZXZhbD1GQUxTRX0Kc2VsZWN0KG9yZy5Icy5lZy5kYiwga2V5cz0iRU5TRzAwMDAwMTU4MjU4IiwKICAgICAgIGtleXR5cGUgPSAiRU5TRU1CTCIsY29sdW1ucz1jKCJTWU1CT0wiLCJHRU5FTkFNRSIpCikKYGBgCgpVbmZvcnR1bmF0ZWx5LCB0aGUgYXV0aG9ycyBvZiBgZHBseXJgIGFuZCBgQW5ub3RhdGlvbkRiaWAgaGF2ZSBib3RoIGRlY2lkZWQgdG8gdXNlIHRoZSBuYW1lIGBzZWxlY3RgIGluIHRoZWlyIHBhY2thZ2VzLiBUbyBhdm9pZCBjb25mdXNpb24gYW5kIGVycm9ycywgdGhlIGZvbGxvd2luZyBjb2RlIGlzIHNvbWV0aW1lcyB1c2VkOi0KCmBgYHtyfQpBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiLCBrZXlzPSJFTlNHMDAwMDAxNTgyNTgiLGtleXR5cGUgPSAiRU5TRU1CTCIsY29sdW1ucz1jKCJTWU1CT0wiLCJHRU5FTkFNRSIpKQpgYGAKCgpUbyBhbm5vdGF0ZSBvdXIgcmVzdWx0cywgd2UgZGVmaW5pdGVseSB3YW50IGdlbmUgc3ltYm9scyBhbmQgcGVyaGFwcyB0aGUgZnVsbCBnZW5lIG5hbWUuIExldCdzIGJ1aWxkIHVwIG91ciBhbm5vdGF0aW9uIGluZm9ybWF0aW9uIGludG8gYSBuZXcgZGF0YSBmcmFtZSB1c2luZyB0aGUgYHNlbGVjdGAgZnVuY3Rpb24uCgpgYGB7cn0KYW5ubyA8LSBBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiLGtleXM9cm93bmFtZXMoZGRzKSwKICAgICAgICAgICAgICBjb2x1bW5zPWMoIlNZTUJPTCIsIkdFTkVOQU1FIiksCiAgICAgICAgICAgICAga2V5dHlwZT0iRU5TRU1CTCIpCiMgSGF2ZSBhIGxvb2sgYXQgdGhlIGFubm90YXRpb24KaGVhZChhbm5vKQpgYGAKCkhvd2V2ZXIsIHdlIGhhdmUgYSBwcm9ibGVtIHRoYXQgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGhhcyBtb3JlIHJvd3MgdGhhbiBvdXIgcmVzdWx0cyB0YWJsZS4gVGhpcyBpcyBkdWUgdG8gdGhlICpvbmUtdG8tbWFueSogcmVsYXRpb25zaGlwcyB0aGF0IG9mdGVuIG9jY3VyIHdoZW4gbWFwcGluZyBiZXR3ZWVuIHZhcmlvdXMgaWRlbnRpZmllcnMuCgpgYGB7cn0KZGltKGFubm8pCmRpbShkZHMpCmBgYAoKU3VjaCBkdXBsaWNhdGVkIGVudHJpZXMgY2FuIGJlIGlkZW50aWZpZWQgdXNpbmcgdGhlIGBkdXBsaWNhdGVkYCBmdW5jdGlvbi4gRm9ydHVuYXRlbHksIHRoZXJlIGFyZSBub3QgdG9vIG1hbnkgc28gaG9wZWZ1bGx5IHdlIHdvbid0IGxvc2UgdG9vIG11Y2ggaW5mb3JtYXRpb24gaWYgd2UgZGlzY2FyZCB0aGUgZW50cmllcyB0aGF0IGFyZSBkdXBsaWNhdGVkLiBUaGUgZmlyc3Qgb2NjdXJyZW5jZSBvZiB0aGUgZHVwbGljYXRlZCBJRCB3aWxsIHN0aWxsIGJlIGluY2x1ZGVkIGluIHRoZSB0YWJsZS4KCmBgYHtyfQphbm5vIDwtIEFubm90YXRpb25EYmk6OnNlbGVjdChvcmcuSHMuZWcuZGIsa2V5cz1yb3duYW1lcyhkZHMpLAogICAgICAgICAgICBjb2x1bW5zPWMoIkVOU0VNQkwiLCJTWU1CT0wiLCJHRU5FTkFNRSIsIkVOVFJFWklEIiksCiAgICAgICAgICAgIGtleXR5cGU9IkVOU0VNQkwiKSAlPiUgCmZpbHRlcighZHVwbGljYXRlZChFTlNFTUJMKSkKZGltKGFubm8pCmBgYAoKCldlIGNhbiBiaW5kIGluIHRoZSBhbm5vdGF0aW9uIGluZm9ybWF0aW9uIHRvIHRoZSBgcmVzdWx0c2AgZGF0YSBmcmFtZS4gCgpgYGB7cn0KcmVzdWx0c19hbm5vdGF0ZWQgPC0gcmVzdWx0cyhkZV90cmVhdGVkLHRpZHk9VFJVRSkgJT4lIAogIGxlZnRfam9pbihhbm5vLCBieT1jKCJyb3ciPSJFTlNFTUJMIikpCgpoZWFkKHJlc3VsdHNfYW5ub3RhdGVkKQpgYGAKCgpXZSBjYW4gc2F2ZSB0aGUgcmVzdWx0cyB0YWJsZSB1c2luZyB0aGUgYHdyaXRlLmNzdmAgZnVuY3Rpb24sIHdoaWNoIHdyaXRlcyB0aGUgcmVzdWx0cyBvdXQgdG8gYSBjc3YgZmlsZSB0aGF0IHlvdSBjYW4gb3BlbiBpbiBleGNlbC4KCmBgYHtyfQp3cml0ZS5jc3YocmVzdWx0c19hbm5vdGF0ZWQsZmlsZT0iZGVfYW5hbHlzaXMvdHJlYXRtZW50X1lfdnNfTl9ERVNlcV9hbm5vdGF0ZWQuY3N2Iixyb3cubmFtZXM9RkFMU0UpCnNhdmVSRFMocmVzdWx0c19hbm5vdGF0ZWQsIGZpbGU9IlJvYmplY3RzL3RyZWF0bWVudF9ZX3ZzX05fREVTZXFfYW5ub3RhdGVkLnJkcyIpCmBgYAoKCiMgRXhlcmNpc2UKCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KCi0gSm9pbiB0aGUgYW5ub3RhdGlvbiB0YWJsZSB0byB5b3VyIHJlc3VsdHMgZnJvbSB0aGUgREVTZXEgYW5hbHlzaXMgb2YgYFRHRmAgdnMgYENUUmAuIFNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGFzIGEgY3N2IGZpbGUuIGUuZy4gYFJvYmplY3RzL3Jlc3VsdHNfVEdGX3ZzX0NUUl9hbm5vdGF0ZWQuY3N2YAotIFRoZSBwdWJsaWNhdGlvbiBnaXZlcyBleGFtcGxlcyBvZiBgQ09MMUExYCwgYENPTDFBMmAgYW5kIGBDT0wzQTFgIGFzIGdlbmVzIHRoYXQgYXJlICp1cC1yZWd1bGF0ZWQqIGluIFRHRi10cmVhdGVkIHNhbXBsZXMgdnMgY29udHJvbHMgKEZpZ3VyZSA2QykuIFVzZSB5b3VyIGRhdGEgdG8gdmVyaWZ5IHRoaXMgYnkgCiAgKyBpKSBleHRyYWN0aW5nIHRoZWlyIHAtdmFsdWVzCiAgKyBpaSkgcGxvdHRpbmcgdGhlIGNvdW50cyBmb3IgdGhlc2UgZ2VuZXMgCgo8L2Rpdj4KCmBgYHtyfQoKCmBgYAoKCgoKIyMjIEV4cG9ydGluZyBub3JtYWxpemVkIGNvdW50cwoKVGhlIGBERVNlcWAgd29ya2Zsb3cgYXBwbGllcyAqbWVkaWFuIG9mIHJhdGlvcyBub3JtYWxpemF0aW9uKiB0aGF0IGFjY291bnRzIGZvciBkaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoIGJldHdlZW4gc2FtcGxlcy4gVGhlIHVzZXIgZG9lcyBub3QgdXN1YWxseSBuZWVkIHRvIHJ1biB0aGlzIHN0ZXAuIEhvd2V2ZXIsIGlmIHlvdSB3YW50IGEgbWF0cml4IG9mIGNvdW50cyBmb3Igc29tZSBhcHBsaWNhdGlvbiBvdXRzaWRlIG9mIEJpb2NvbmR1Y3RvciB0aGUgdmFsdWVzIGNhbiBiZSBleHRyYWN0ZWQgZnJvbSB0aGUgYGRkc2Agb2JqZWN0LgoKYGBge3J9CmRkcyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkcykgCmNvdW50TWF0cml4IDwtY291bnRzKGRkcywgbm9ybWFsaXplZD1UUlVFKSAKaGVhZChjb3VudE1hdHJpeCkKd3JpdGUuY3N2KGNvdW50TWF0cml4LGZpbGU9Im5vcm1hbGl6ZWRfY291bnRzLmNzdiIpCmBgYAoKCiMgRnVsbCBERVNlcSB3b3JrZmxvdwoKVGhlIG1lZGlhbiBvZiByYXRpb3Mgbm9ybWFsaXNhdGlvbiBtZXRob2QgaXMgZW1wbG95ZWQgaW4gREVTZXEyIHRvIGFjY291bnQgZm9yICpzZXF1ZW5jaW5nIGRlcHRoKiBhbmQgKlJOQSBjb21wb3NpdGlvbiouIExldCdzIGdvIHRocm91Z2ggYSBzaG9ydCB3b3JrZWQgZXhhbXBsZSAoY291cnRlc3kgb2YgW2h0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzAyX0RHRV9jb3VudF9ub3JtYWxpemF0aW9uLmh0bWxdKGhlcmUpKSB0byBleHBsYWluIHRoZSBwcm9jZXNzLgoKYGBge3J9CiMjIGNyZWF0ZSBhIHNtYWxsIGV4YW1wbGUgbWF0cml4IG9mICJjb3VudHMiCnRlc3RfZGF0YSA8LSBtYXRyaXgoYygxNDg5LDIyLDc5Myw3Niw1MjEsOTA2LDEzLDQxMCw0MiwxMTk2KSxucm93PTUpCnJvd25hbWVzKHRlc3RfZGF0YSkgPC0gYygiRUYyQSIsIkFCQ0QxIiwiTUVGViIsIkJBRzEiLCJNT1YxMCIpCmNvbG5hbWVzKHRlc3RfZGF0YSkgPC0gYygiU2FtcGxlQSIsIlNhbXBsZUIiKQp0ZXN0X2RhdGEKYGBgCgpGaXJzdGx5LCBhbiAiYXZlcmFnZSIgb3IgcmVmZXJlbmNlIHNhbXBsZSBpcyBjcmVhdGVkIHRoYXQgcmVwcmVzZW50cyB0aGUgY291bnRzIG9uIGEgdHlwaWNhbCBzYW1wbGUgaW4gdGhlIGRhdGFzZXQuIFRoZSAqZ2VvbWV0cmljIG1lYW4qIGlzIHVzZWQgcmF0aGVyIHRoYW4gdGhlICphcml0aG1ldGljKiBtZWFuLiBJbiBvdGhlciB3b3JkcyB0aGUgaW5kaXZpZHVhbCBjb3VudHMgYXJlIG11bHRpcGxpZWQgcmF0aGVyIHRoYW4gc3VtbWVkIGFuZCB0aGUgbWVhc3VyZSBzaG91bGQgYmUgbW9yZSByb2J1c3QgdG8gb3V0bGllcnMuCgoKYGBge3J9CnBzdWVkb19yZWYgPC0gc3FydChyb3dQcm9kcyh0ZXN0X2RhdGEpKQpwc3VlZG9fcmVmCmBgYAoKQSByYXRpb3Mgb2Ygc2FtcGxlIHRvICJwc3VlZG8gcmVmZXJlbmNlIiBhcmUgdGhlbiBjYWxjdWxhdGVkIGZvciBlYWNoIGdlbmUuIFdlIGFyZSBhc3N1bWluZyB0aGF0IG1vc3QgZ2VuZXMgYXJlIG5vdCBjaGFuZ2luZyBkcmFtYXRpY2FsbHksIHNvIHRoaXMgcmF0aW8gc2hvdWxkIGJlIHNvbWV3aGVyZSBhcm91bmQgMS4KCmBgYHtyfQp0ZXN0X2RhdGEvcHN1ZWRvX3JlZgpgYGAKCmBERVNlcTJgIGRlZmluZXMgc2l6ZSBmYWN0b3JzIGFzIGJlaW5nIHRoZSAqbWVkaWFuKiBvZiB0aGVzZSByYXRpb3MgZm9yIGVhY2ggc2FtcGxlIChtZWRpYW4gaXMgdXNlZCBzbyBhbnkgb3V0bGllciBnZW5lcyB3aWxsIG5vdCBhZmZlY3QgdGhlIG5vcm1hbGlzYXRpb24pLiAKCmBgYHtyfQpub3JtX2ZhY3RvcnMgPC0gY29sTWVkaWFucyh0ZXN0X2RhdGEvcHN1ZWRvX3JlZikKbm9ybV9mYWN0b3JzCmBgYAoKSW5kaXZpZHVhbCBzYW1wbGVzIGNhbiB0aGVuIG5vcm1hbGlzZWQgYnkgZGl2aWRpbmcgdGhlIGNvdW50IGZvciBlYWNoIGdlbmUgYnkgdGhlIGNvcnJlc3BvbmRpbmcgbm9ybWFsaXphdGlvbiBmYWN0b3IuCgpgYGB7cn0KdGVzdF9kYXRhWywxXSAvIG5vcm1fZmFjdG9yc1sxXQpgYGAKCmFuZCBmb3IgdGhlIHNlY29uZCBzYW1wbGUuLi4KCmBgYHtyfQp0ZXN0X2RhdGFbLDJdIC8gbm9ybV9mYWN0b3JzWzJdCgpgYGAKClRoZSBzaXplIGZhY3RvcnMgZm9yIGVhY2ggc2FtcGxlIGluIG91ciBkYXRhc2V0IGNhbiBiZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBgZXN0aW1hdGVTaXplRmFjdG9yc0Zvck1hdHJpeGAgZnVuY3Rpb24uCgpgYGB7cn0Kc2YgPC0gZXN0aW1hdGVTaXplRmFjdG9yc0Zvck1hdHJpeChhc3NheShkZHMpKQpzZgpgYGAKClRoZSBlc3RpbWF0aW9uIG9mIHRoZXNlIGZhY3RvcnMgY2FuIGFsc28gdGFrZSBnZW5lLWxlbmd0aHMgaW50byBhY2NvdW50LCBhbmQgdGhpcyBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgYGVzdGltYXRlU2l6ZUZhY3RvcnNgIGZ1bmN0aW9uLiBFeHRyYSBub3JtYWxpemF0aW9uIGZhY3RvciBkYXRhIGlzIGFkZGVkIHRvIHRoZSBgZGRzYCBvYmplY3QuCgoKCmBgYHtyIGV2YWw9RkFMU0V9CmRkcyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkcykKZGRzCmBgYAoKSW4gcHJlcGFyYXRpb24gZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIERFU2VxMiBhbHNvIG5lZWQgYSByZWxpYWJsZSBlc3RpbWF0ZSBvZiB0aGUgdmFyaWFiaWxpdHkgb2YgZWFjaCBnZW5lOyB3aGljaCBpdCBjYWxscyAqZGlzcGVyc2lvbiouIAoKYGBge3IgZXZhbD1GQUxTRX0KZGRzIDwtIGVzdGltYXRlRGlzcGVyc2lvbnMoZGRzKQpkZHMKCmBgYAoKQSBzdGF0aXN0aWNhbCB0ZXN0IGNhbiB0aGVuIGJlIGFwcGxpZWQuIEFzIHRoZSBkYXRhIGFyZSBjb3VudC1iYXNlZCBhbmQgbm90IG5vcm1hbGx5LWRpc3RyaWJ1dGVkIGEgdC10ZXN0IHdvdWxkIG5vdCBiZSBhcHByb3ByaWF0ZS4gTW9zdCB0ZXN0cyBhcmUgYmFzZWQgb24gYSAqUG9pc3Nvbiogb3IgKm5lZ2F0aXZlLWJpbm9taWFsKiBkaXN0cmlidXRpb247IG5lZ2F0aXZlIGJpbm9taWFsIGluIHRoZSBjYXNlIG9mIGBERVNlcTJgLiBBbHRob3VnaCB5b3UgbWlnaHQgbm90IGJlIGZhbWlsaWFyIHdpdGggdGhlIG5lZ2F0aXZlIGJpbm9taWFsLCB0aGUgcmVzdWx0cyBzaG91bGQgYmUgaW4gYSBmYW1pbGlhciBmb3JtIHdpdGggZm9sZC1jaGFuZ2VzIGFuZCBwLXZhbHVlcyBmb3IgZWFjaCBnZW5lLgoKYGBge3IgZXZhbD1GQUxTRX0KZGRzIDwtIG5iaW5vbVdhbGRUZXN0KGRkcykKYGBgCgpJdCBtYXkgc2VlbSBsaWtlIHRoZXJlIGlzIGEgbG90IHRvIHJlbWVtYmVyLCBidXQgZm9ydHVuYXRlbHkgdGhlcmUgaXMgb25lIGNvbnZlbmllbnQgZnVuY3Rpb24gdGhhdCB3aWxsIGFwcGx5IHRoZSB0aHJlZSBzdGVwcyAoYERFU2VxYCkuIFRoZSBtZXNzYWdlcyBwcmludGVkIHNlcnZlIGFzIHJlbWluZGVycyBvZiB0aGUgc3RlcHMgaW5jbHVkZWQuCgoKIyBBY2tub3dsZWRnZW1lbnRzCgoqKk9yaWdpbmFsIEF1dGhvcnM6IEJlbGluZGEgUGhpcHNvbiwgQW5uYSBUcmlnb3MsIE1hdHQgUml0Y2hpZSwgTWFyaWEgRG95bGUsIEhhcnJpZXQgRGFzaG5vdywgQ2hhcml0eSBMYXcqKiwgKipTdGVwaGFuZSBCYWxsZXJlYXUsIE9zY2FyIFJ1ZWRhLCBBc2hsZXkgU2F3bGUqKgpCYXNlZCBvbiB0aGUgY291cnNlIFtSTkFzZXEgYW5hbHlzaXMgaW4gUl0oaHR0cDovL2NvbWJpbmUtYXVzdHJhbGlhLmdpdGh1Yi5pby8yMDE2LTA1LTExLVJOQXNlcS8pIGRlbGl2ZXJlZCBvbiBNYXkgMTEvMTJ0aCAyMDE2IGFuZCBtb2RpZmllZCBieSBDYW5jZXIgUmVzZWFyY2ggVWsgQ2FtYnJpZGdlIENlbnRyZSBmb3IgdGhlIFtGdW5jdGlvbmFsIEdlbm9taWNzIEF1dHVtbiBTY2hvb2wgMjAxN10oaHR0cHM6Ly9iaW9pbmZvcm1hdGljcy1jb3JlLXNoYXJlZC10cmFpbmluZy5naXRodWIuaW8vY3J1ay1hdXR1bW4tc2Nob29sLTIwMTcvKQoKIyMgUmVzb3VyY2VzIGFuZCBkYXRhIGZpbGVzCgpUaGlzIG1hdGVyaWFsIGhhcyBiZWVuIGNyZWF0ZWQgdXNpbmcgdGhlIGZvbGxvd2luZyByZXNvdXJjZXM6ICAKCi0gaHR0cDovL3d3dy5zdGF0c2NpLm9yZy9zbXl0aC9wdWJzL1FMZWRnZVJQcmVwcmludC5wZGYgCi0gaHR0cDovL21vbmFzaGJpb2luZm9ybWF0aWNzcGxhdGZvcm0uZ2l0aHViLmlvL1JOQXNlcS1ERS1hbmFseXNpcy13aXRoLVIvOTktUk5Bc2VxX0RFX2FuYWx5c2lzX3dpdGhfUi5odG1sICAKLSBodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwKLSBodHRwczovL2Jpb2NvbmR1Y3Rvci5naXRodWIuaW8vQmlvY1dvcmtzaG9wcy9ybmEtc2VxLWRhdGEtYW5hbHlzaXMtd2l0aC1kZXNlcTIuaHRtbA==