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.

If you have problems running these steps, you can re-load a pre-processed object from the course materials

dds <- readRDS("Robjects/dds_BACKUP.rds")

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")

If your study involves knocking-out a particular gene, or you have some positive controls that are known in advance, it would be a good idea to visualise their expression level with plotCounts.

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
      1. extracting their p-values
      1. 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

LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgUk5BLXNlcSBkYXRhIGluIFIiCmF1dGhvcjogIk1vZHVsZSBDb29yZGluYXRvciBNYXJrIER1bm5pbmciCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBjc3M6IHN0eWxlc2hlZXRzL3N0eWxlcy5jc3MKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZyA9IEZBTFNFKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0eGltcG9ydCkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KERFU2VxMikpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHJlYWRyKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZHBseXIpKQpgYGAKCgoKIyBMZWFybmluZyBPYmplY3RpdmVzCgotIFdoaWNoIHN0YXRpc3RpY2FsIHRlc3RzIGFyZSBhcHByb3ByaWF0ZSBmb3IgUk5BLXNlcSBkYXRhCi0gVXNpbmcgdGhlIERFU2VxMiBwYWNrYWdlIHRvIGRldGVjdCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgotIEJhc2ljIHZpc3VhbGlzYXRpb24gb2YgUk5BLXNlcSBjb3VudHMKLSBVc2luZyBhbm5vdGF0aW9uIGRhdGFiYXNlcyB0byBtYXAgYmV0d2VlbiBnZW5lIGlkZW50aWZlcnMKCiMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2l0aCBgREVTZXEyYAoKTm93IHRoYXQgd2UgYXJlIGhhcHB5IHRoYXQgd2UgaGF2ZSBub3JtYWxpc2VkIHRoZSBkYXRhIGFuZCB0aGF0IHRoZSBxdWFsaXR5IGxvb2tzIGdvb2QsIHdlIGNhbiBjb250aW51ZSB0byB0ZXN0aW5nIGZvciBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFRoZXJlIGFyZSBhIG51bWJlciBvZiBwYWNrYWdlcyB0byBhbmFseXNlIFJOQS1TZXEgZGF0YS4gTW9zdCBwZW9wbGUgdXNlIGBERVNlcTJgIG9yIGBlZGdlUmAuIFdlIHdpbGwgdXNlIGBERVNlcTJgIGZvciB0aGUgcmVzdCBvZiB0aGlzIHByYWN0aWNhbC4KCiMjIyBSZWNhcCBvZiBwcmUtcHJvY2Vzc2luZwoKVGhlIHByZXZpb3VzIHNlY3Rpb24gd2Fsa2VkLXRocm91Z2ggdGhlIHByZS1wcm9jZXNzaW5nIGFuZCB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgY291bnQgZGF0YS4gSGVyZSwgZm9yIGNvbXBsZXRlbmVzcywgd2UgbGlzdCB0aGUgbWluaW1hbCBzdGVwcyByZXF1aXJlZCB0byBwcm9jZXNzIHRoZSBkYXRhIHByaW9yIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgoKTm90ZSB0aGF0IGFsdGhvdWdoIHdlIHNwZW50IHNvbWUgdGltZSBsb29raW5nIGF0IHRoZSBxdWFsaXR5IG9mIG91ciBkYXRhICwgdGhlc2Ugc3RlcHMgYXJlIG5vdCByZXF1aXJlZCBwcmlvciB0byBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHNvIGFyZSBub3Qgc2hvd24gaGVyZS4gUmVtZW1iZXIsIGBERVNlcTJgIFtyZXF1aXJlcyByYXcgY291bnRzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwjd2h5LXVuLW5vcm1hbGl6ZWQtY291bnRzKSBzbyB0aGUgYHZzdGAgdHJhbnNmb3JtYXRpb24gaXMgbm90IHNob3duIGFzIHBhcnQgb2YgdGhpcyBiYXNpYyBwcm90b2NvbC4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodHhpbXBvcnQpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KHJlYWRyKQpkaXJzIDwtIGxpc3QuZmlsZXMoInNhbG1vbl9xdWFudC8iKQpxdWFudF9maWxlcyA8LSBsaXN0LmZpbGVzKCJzYWxtb25fcXVhbnQvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuPSJxdWFudC5zZi5neiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjdXJzaXZlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkKbmFtZXMocXVhbnRfZmlsZXMpIDwtIGRpcnMKCnR4MmdlbmUgPC0gcmVhZF9jc3YoInR4MmdlbmUuY3N2Iixjb2xfbmFtZXMgPSBGQUxTRSkKCnR4aSA8LSB0eGltcG9ydChxdWFudF9maWxlcywKICAgICAgICAgICAgICAgIHR5cGU9InNhbG1vbiIsCiAgICAgICAgICAgICAgICB0eDJnZW5lID0gdHgyZ2VuZSkKCnNhbXBsZWluZm8gPC0gcmVhZF90c3YoIm1ldGFfZGF0YS9zYW1wbGVJbmZvX2NvcnJlY3RlZC50eHQiKQoKCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tVHhpbXBvcnQodHhpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlaW5mbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+VHJlYXRlZCkKZGRzJGNvbmRpdGlvbiA8LSBhcy5mYWN0b3IoZGRzJGNvbmRpdGlvbikKYGBgCgpJdCB3b3VsZCBiZSBhIGdvb2QgaWRlYSB0byBzYXZlIHRoZSByZXN1bHRzIG9mIHRoZSBwcmUtcHJvY2Vzc2luZyBzbyB3ZSBkb24ndCBoYXZlIHRvIHJlcGVhdCBpdCBldmVyeSB0aW1lLgoKYGBge3J9CmRpci5jcmVhdGUoIlJvYmplY3RzLyIsc2hvd1dhcm5pbmdzID0gRkFMU0UpCnNhdmVSRFMoZGRzLCBmaWxlPSJSb2JqZWN0cy9kZHMucmRzIikKYGBgCgoKV2Ugd2lsbCBiZSB1c2luZyB0aGVzZSByYXcgY291bnRzIHRocm91Z2hvdXQgdGhlIHdvcmtzaG9wIGFuZCB0cmFuc2Zvcm1pbmcgdGhlbSB1c2luZyBtZXRob2RzIGluIHRoZSBgREVTZXEyYCBwYWNrYWdlLiBJZiB5b3Ugd2FudCB0byBrbm93IGFib3V0IGFsdGVybmF0aXZlIG1ldGhvZHMgZm9yIGNvdW50IG5vcm1hbGlzYXRpb24gdGhleSBhcmUgY292ZXJlZCBvbiBbdGhpcyBwYWdlXShodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wMl9ER0VfY291bnRfbm9ybWFsaXphdGlvbi5odG1sKS4KCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KSWYgeW91IGhhdmUgcHJvYmxlbXMgcnVubmluZyB0aGVzZSBzdGVwcywgeW91IGNhbiByZS1sb2FkIGEgcHJlLXByb2Nlc3NlZCBvYmplY3QgZnJvbSB0aGUgY291cnNlIG1hdGVyaWFscwoKYGBge3IgZXZhbD1GQUxTRX0KZGRzIDwtIHJlYWRSRFMoIlJvYmplY3RzL2Rkc19CQUNLVVAucmRzIikKYGBgCgoKPC9kaXY+CgoKCiMgVGhlIERFU2VxIHdvcmtmbG93IGluIGJyaWVmCgpXZSBoYXZlIHByZXZpb3VzbHkgZGVmaW5lZCB0aGUgdGVzdCBjb25kaXRpb24gdXNpbmcgdGhlIGBkZXNpZ25gIGFyZ3VtZW50IHdoZW4gd2UgY3JlYXRlZCB0aGUgb2JqZWN0LiBUaGlzIGNhbiBiZSBjaGVja2VkIHVzaW5nIHRoZSBgZGVzaWduYCBmdW5jdGlvbi4KClR5cGljYWxseSB3ZSBkZWNpZGUgdGhlIGRlc2lnbiBmb3IgdGhlIGFuYWx5c2lzIHdoZW4gd2UgY3JlYXRlIHRoZSBERVNlcTIgb2JqZWN0cywgYnV0IGl0IGNhbiBiZSBtb2RpZmllZCBwcmlvciB0byB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuIFRoZSBkZXNpZ24gdGVsbHMgYERFU2VxMmAgd2hpY2ggc2FtcGxlIGdyb3VwcyB0byBjb21wYXJlIGluIHRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMuIFRoZSBuYW1lIHNwZWNpZmllZCBtdXN0IGNvcnJlc3BvbmQgdG8gYSBjb2x1bW4gaW4gdGhlIHNhbXBsZSBpbmZvcm1hdGlvbi4KCmBgYHtyfQpjb2xEYXRhKGRkcykKZGVzaWduKGRkcykgPC0gflRyZWF0ZWQKYGBgCgpUaGUgY291bnRzIHRoYXQgd2UgaGF2ZSBvYnRhaW5lZCB2aWEgc2VxdWVuY2luZyBhcmUgc3ViamVjdCB0byByYW5kb20gc291cmNlcyBvZiB2YXJpYXRpb24uIFRoZSBwdXJwb3NlIG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGlzIHRvIGRldGVybWluZSBpZiBwb3RlbnRpYWwgc291cmNlcyBvZiBiaW9sb2dpY2FsIHZhcmlhdGlvbiAoZS5nLiBjb3VudHMgb2JzZXJ2ZWQgZnJvbSBkaWZmZXJlbnQgc2FtcGxlIGdyb3VwcykgYXJlIGdyZWF0ZXIgdGhhbiByYW5kb20gbm9pc2UuCgoKVGhlIGBERVNlcWAgZnVuY3Rpb24gcnVucyBhIGNvdXBsZSBvZiBwcm9jZXNzaW5nIHN0ZXBzIGF1dG9tYXRpY2FsbHkgdG8gYWRqdXN0IGZvciBkaWZmZXJlbnQgbGlicmFyeSBzaXplIGFuZCBnZW5lLXdpc2UgdmFyaWFiaWxpdHksIHdoaWNoIHlvdSBjYW4gcmVhZCBhYm91dCBpbiB0aGUgW0RFU2VxMiB2aWduZXR0ZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3RoZS1kZXNlcTItbW9kZWwpIGFuZCBydW4gc29tZSBleGFtcGxlIGNvZGUgYXQgdGhlIFtlbmQgb2YgdGhpcyBzZXNzaW9uXSgjRnVsbF9ERVNlcV93b3JrZmxvdykuIAoKCmBgYHtyfQpkZV90cmVhdGVkPC0gREVTZXEoZGRzKQpkZV90cmVhdGVkCmBgYAoKCgpUaGUgcmVzdWx0cyBvZiB0aGUgYW5hbHlzaXMgYXJlIG5vdCBpbW1lZGlhdGVseSBhY2Nlc3NpYmxlLCBidXQgY2FuIGJlIG9idGFpbmVkIHVzaW5nIHRoZSBgcmVzdWx0c2AgZnVuY3Rpb24uIEVhY2ggcm93IGlzIGEgcGFydGljdWxhciBnZW5lIG1lYXN1cmVkIGluIHRoZSBzdHVkeSAoaS5lLiBhbGwgZ2VuZXMgaW4gdGhlIG9yZ2FuaXNtIGJlaW5nIHN0dWRpZWQpIGFuZCBlYWNoIGNvbHVtbiByZXBvcnRzIHNvbWUgYXNwZWN0IG9mIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBmb3IgdGhhdCBnZW5lLiBOb3RlIHRoYXQgKiphbGwgZ2VuZXMqKiBhcmUgcmVwb3J0ZWQuIEF0IHRoaXMgc3RhZ2UgdGhlIGdlbmUgaWRlbnRpZmllcnMgYXJlIG5vdCB2ZXJ5IGluZm9ybWF0aXZlLCBzb21ldGhpbmcgd2Ugd2lsbCBmaXggaW4gdGhlIG5leHQgc2VjdGlvbi4gRnVydGhlcm1vcmUsIHRoZSBgcmVzdWx0c2AgZnVuY3Rpb24gZGlzcGxheXMgcmVzdWx0cyBpbiBhIGZvcm1hdCB3aGljaCBpcyBub3QgY29tcGF0aWJsZSB3aXRoIHN0YW5kYXJkIGRhdGEgbWFuaXB1bGF0aW9uIHRvb2xzIChpLmUuIGB0aWR5dmVyc2VgKSwgc28gd2Ugd2lsbCBoYXZlIHRvIGNvbnZlcnQuCgpgYGB7cn0KcmVzdWx0cyhkZV90cmVhdGVkKQpgYGAKCiMgUHJvY2Vzc2luZyB0aGUgREUgcmVzdWx0cyB1c2luZyB0aWR5dmVyc2UKClRoZSBvdXRwdXQgY2FuIGJlIGNvbnZlcnRlZCBpbnRvIGEgZGF0YSBmcmFtZSBhbmQgbWFuaXB1bGF0ZWQgaW4gdGhlIHVzdWFsIG1hbm5lci4gSXQgaXMgcmVjb21tZW5kZWQgdG8gdXNlIGBkcGx5cmAgdG8gbWFuaXB1bGF0ZSB0aGUgZGF0YSBmcmFtZXMgd2l0aCB0aGUgc3RhbmRhcmQgc2V0IG9mIG9wZXJhdGlvbnMgZGV0YWlsZWQgb24gdGhlIFtkcGx5ciBjaGVhdHNoZWV0XShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNS8wMi9kYXRhLXdyYW5nbGluZy1jaGVhdHNoZWV0LnBkZikKCi0gYHNlbGVjdGAgdG8gcGljayB3aGljaCBjb2x1bW5zIHRvIGRpc3BsYXkKLSBgZmlsdGVyYCB0byByZXN0cmljdCB0aGUgcm93cwotIGBtdXRhdGVgIHRvIGFkZCBuZXcgdmFyaWFibGVzIHRvIHRoZSBkYXRhIGZyYW1lCi0gYGFycmFuZ2VgIHRvIG9yZGVyIHRoZSBkYXRhIGZyYW1lIGFjY29yZGluZyB0byB2YWx1ZXMgb2YgYSBjb2x1bW4KClRoZSBgJT4lYCBzeW1ib2wgcmVmZXJzIHRvIHRoZSBbcGlwaW5nIG9wZXJhdGlvbl0oaHR0cHM6Ly9zYmMuc2hlZi5hYy51ay9yLW9ubGluZS9wYXJ0Mi5uYi5odG1sIyVFMiU4MCU5Q1BpcGluZyVFMiU4MCU5RCkgaW4gUiwgd2hpY2ggaXMgYSB3YXkgb2YgY2hhaW5pbmcgb3BlcmF0aW9ucyB0b2dldGhlci4gCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQogcmVzdWx0cyhkZV90cmVhdGVkLCB0aWR5PVRSVUUpCmBgYAoKV2UgY2FuIHNvcnQgdGhlIHJvd3MgYnkgYWRqdXN0ZWQgcC12YWx1ZSBhbmQgdGhlbiBwcmludCB0aGUgZmlyc3QgMTAgcm93cy4KCmBgYHtyfQpyZXN1bHRzKGRlX3RyZWF0ZWQsdGlkeT1UUlVFKSAlPiUKICBhcnJhbmdlKHBhZGopICU+JSAgCiAgaGVhZChuPTEwKQpgYGAKCk9yIHdlIGNhbiBzb3J0IHRoZSByb3dzIGFuZCB0aGVuIHdyaXRlIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZSB0byBhIGZpbGUuCgpgYGB7cn0KZGlyLmNyZWF0ZSgiZGVfYW5hbHlzaXMiLHNob3dXYXJuaW5ncyA9IEZBTFNFKQogcmVzdWx0cyhkZV90cmVhdGVkLHRpZHk9VFJVRSkgJT4lCiAgYXJyYW5nZShwYWRqKSAlPiUgCiAgIHdyaXRlX2NzdigiZGVfYW5hbHlzaXMvdHJlYXRlZF9ZX3ZzX05fREVTZXFfYWxsLmNzdiIpCmBgYAoKRmlsdGVyaW5nIHRvIHRoZSBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgZ2VuZXMgY2FuIGJlIGFjaGlldmVkIHVzaW5nIHRoZSBgZmlsdGVyYCBmdW5jdGlvbiBmcm9tIGBkcGx5cmAuCgpgYGB7cn0KIHJlc3VsdHMoZGVfdHJlYXRlZCx0aWR5PVRSVUUpICU+JQogIGZpbHRlcihwYWRqIDwgMC4wNSkgJT4lIAogIHdyaXRlLmNzdigiZGVfYW5hbHlzaXMvdHJlYXRlZF9ZX3ZzX05fREVTZXFfc2lnLmNzdiIpCmBgYAoKSXQgaXMgYWxzbyBhIGdvb2QgaWRlYSB0byBzYXZlIHRoZSByZXN1bHRzIG9iamVjdCBpdHNlbGYgc28gd2UgY2FuIHJlLXVzZSBsYXRlci4KCmBgYHtyfQpzYXZlUkRTKGRlX3RyZWF0ZWQsIGZpbGU9IlJvYmplY3RzL2RlX3RyZWF0ZWQucmRzIikKYGBgCgpXZSBjYW4gZGlzY292ZXIgaG93IG1hbnkgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkIGdlbmVzIChhdCBhIHBhcnRpY3VsYXIgcC12YWx1ZSBjdXQtb2ZmKSB1c2luZyB0aGUgYGNvdW50YCBmdW5jdGlvbgoKYGBge3J9CnJlc3VsdHMoZGVfdHJlYXRlZCx0aWR5PVRSVUUpICU+JQpjb3VudChwYWRqIDwgMC4wNSkKYGBgCgpBbm90aGVyIG92ZXJ2aWV3IG9mIHRoZSByZXN1bHRzIGlzIHRvIHVzZSB0aGUgYHBsb3RNQWAgZnVuY3Rpb24uIEVhY2ggcG9pbnQgb24gdGhpcyBwbG90IHJlcHJlc2VudHMgYW5kIGluZGl2aWR1YWwgZ2VuZSB3aXRoIHRoZSB4LSBhbmQgeS1heGVzIGJlaW5nIHRoZSBvdmVyYWxsIGV4cHJlc3Npb24gbGV2ZWwgYW5kIG1hZ25pdHVkZSBvZiBkaWZmZXJlbmNlIHJlc3BlY3RpdmVseS4gU2lnbmlmaWNhbnQgZ2VuZXMgYXJlIGF1dG9tYXRpY2FsbHkgaGlnaGxpZ2h0ZWQuIFRoZSBmYW5uaW5nIGVmZmVjdCBhdCBsb3cgZXhwcmVzc2lvbiBsZXZlbHMgaXMgb2Z0ZW4gc2VlbiBkdWUgdG8gaGlnaCByZWxhdGl2ZSBmb2xkLWNoYW5nZSBhdCBsb3cgZXhwcmVzc2lvbiBsZXZlbHMuIAoKYGBge3J9CnBsb3RNQShkZV90cmVhdGVkKQpgYGAKCkl0IGlzIGFsc28gaW5zdHJ1Y3RpdmUgdG8gcGVyZm9ybSBhICJzYW5pdHkiIGNoZWNrIGFuZCBwbG90IHRoZSBzYW1wbGUtbGV2ZWwgY291bnRzIGZvciBnZW5lcyB3aXRoIGhpZ2ggc2lnbmlmaWNhbmNlLiBUaGlzIGNvdWxkIGhpZ2hsaWdodCBhbnkgb3RoZXIgdGVjaG5pY2FsIGZhY3RvcnMgdGhhdCB3ZSBhcmUgbm90IGN1cnJlbnRseSB0YWtpbmcgaW50byBhY2NvdW50LiBUaGUgcGxvdCBpcyBub3QgcGFydGljdWxhcmx5IGF0dHJhY3RpdmUsIGJ1dCBpcyBhIGdvb2QgcXVpY2sgZGlhZ25vc3RpYy4KCmBgYHtyfQpwbG90Q291bnRzKGRkcywiRU5TRzAwMDAwMTU4MjU4IixpbnRncm91cCA9ICJUcmVhdGVkIikKYGBgCgpgYGB7cn0KcGxvdENvdW50cyhkZHMsIkVOU0cwMDAwMDEzNjk5OSIsaW50Z3JvdXAgPSAiVHJlYXRlZCIpCmBgYAoKCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KSWYgeW91ciBzdHVkeSBpbnZvbHZlcyBrbm9ja2luZy1vdXQgYSBwYXJ0aWN1bGFyIGdlbmUsIG9yIHlvdSBoYXZlIHNvbWUgcG9zaXRpdmUgY29udHJvbHMgdGhhdCBhcmUga25vd24gaW4gYWR2YW5jZSwgaXQgd291bGQgYmUgYSBnb29kIGlkZWEgdG8gdmlzdWFsaXNlIHRoZWlyIGV4cHJlc3Npb24gbGV2ZWwgd2l0aCBgcGxvdENvdW50c2AuCjwvZGl2PgoKIyBFeGVyY2lzZQoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgotIFJlLXJ1biB0aGUgYW5hbHlzaXMgdG8gZmluZCBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgZ2VuZXMgYmV0d2VlbiB0aGUgYFRHRmAgdHJlYXRlZCBzYW1wbGVzIGFuZCBgQ1RSTGAKLSBXcml0ZSBhIGNzdiBmaWxlIHRoYXQgY29udGFpbnMgcmVzdWx0cyBmb3IgdGhlIGdlbmVzIHRoYXQgaGF2ZSBhbiAqYWRqdXN0ZWQqIHAtdmFsdWUgbGVzcyB0aGFuIDAuMDUgYW5kIGEgbG9nMiBmb2xkIGNoYW5nZSBtb3JlIHRoYW4gMSwgb3IgbGVzcyB0aGFuIC0xIGluIHRoZSBjb250cmFzdCBvZiBUR0YgdnMgQ1RSTC4KICAtIEhJTlQ6IFNvIHRoYXQgd2UgZG9uJ3Qgb3ZlcndyaXRlIG91ciByZXN1bHRzIHNvIGZhciwgaXQgbWF5IGJlIGNvbnZlbmllbnQgdG8gY3JlYXRlIGEgbmV3IGBERVNlcURhdGFTZXRgIG9iamVjdCBmb3IgdGhlIG5ldyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4gQ2hlY2sgdGhlIGBjb2xEYXRhYCB0byBzZWUgd2hpY2ggYW5hbHlzZXMgY2FuIGJlIG1hZGUKLSBVc2UgdGhlIGBwbG90Q291bnRzYCBmdW5jdGlvbiB0byB2aXN1YWxseS1pbnNwZWN0IHRoZSBtb3N0IHN0YXRpc3RpY2FsbHktc2lnbmlmaWNhbnQgZ2VuZSBpZGVudGlmaWVkCgpgYGB7ciBldmFsPUZBTFNFfQoKZGRzX2NvbmRpdGlvbjwtIGRkcwpjb2xEYXRhKGRkc19jb25kaXRpb24pCiMjIFlvdSB3aWxsIG5lZWQgdG8gY2hhbmdlIHRoaXMgbGluZSB0byBjaG9vc2UgdGhlIGNvcnJlY3QgY29tcGFyaXNvbgpkZXNpZ24oZGRzX2NvbmRpdGlvbikgPC0gfi4uLgpgYGAKCjwvZGl2PgoKCgojIENoYW5naW5nIHRoZSBkaXJlY3Rpb24gb2YgdGhlIGNvbnRyYXN0CgpJbiB0aGlzIGluaXRpYWwgYW5hbHlzaXMgYERFU2VxMmAgaGFzIGF1dG9tYXRpY2FsbHkgZGVjaWRlZCB3aGljaCBtZW1iZXIgb2Ygb3VyIHNhbXBsZSBncm91cHMgdG8gdXNlIGFzIG91ciBiYXNlbGluZSAoYENUUmAgaW4gdGhpcyBjYXNlKS4gSWYgdGhlIGxvZzIgZm9sZCBjaGFuZ2VzIGhhcyBhIHBvc2l0aXZlIHZhbHVlIHRoaXMgbWVhbnMgaGlnaGVyIGV4cHJlc3Npb24gaW4gYFRyZWF0ZWRgIHNhbXBsZS4gV2UgY2FuIGFsdGVyIHRoaXMgYnkgY2hhbmdpbmcgdGhlIGBjb250cmFzdGAgYXJndW1lbnQgaW4gdGhlIGByZXN1bHRzYCBmdW5jdGlvbgoKCmBgYHtyIGV2YWw9RkFMU0V9CiMjIFRoaXMgc2hvdWxkIGdpdmUgdGhlIHNhbWUgYXMgdGhlIHRhYmxlIGFib3ZlCnJlc3VsdHMoZGVfdHJlYXRlZCwgY29udHJhc3Q9YygiVHJlYXRlZCIsIlkiLCJOIikpCiMjIENoYW5naW5nIHRoZSBkaXJlY3Rpb24gb2YgdGhlIGNvbnRyYXN0CnJlc3VsdHMoZGVfdHJlYXRlZCwgY29udHJhc3Q9YygiVHJlYXRlZCIsIk4iLCJZIikpCmBgYAoKSWYgd2Ugd2FudCB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG9uIHRoZSBgY29uZGl0aW9uYCB2YXJpYWJsZSB0aGVuIHRoZXJlIGFyZSB2YXJpb3VzIGNvbnRyYXN0cyB0aGF0IGNhbiBiZSBtYWRlOyBgSVJgIHZzIGBDVFJgLCBgVEdGYCB2cyBgQ1RSYCBldGMuIFdoZW4gdGhlIGByZXN1bHRzYCBmdW5jdGlvbiBpcyBydW4gd2l0aCBubyBgY29udHJhc3RgIGFyZ3VtZW50IHNwZWNpZmllZCwgdGhlIHRhYmxlIHRoYXQgaXMgZGlzcGxheWVkIGlzIGZvciB0aGUgY29udHJhc3QgYFRHRiB2cyBDVFJgLiBUaGUgYHJlc3VsdHNOYW1lc2AgZnVuY3Rpb24gY2FuIHRlbGwgdXMgd2hpY2ggb3RoZXIgY29udHJhc3RzIHdlIGNhbiBhY2Nlc3MuCgoKYGBge3IgfQpkZHNfY29uZGl0aW9uPC0gZGRzCmRlc2lnbihkZHNfY29uZGl0aW9uKSA8LSB+Y29uZGl0aW9uCmRlX2NvbmRpdGlvbiA8LSBERVNlcShkZHNfY29uZGl0aW9uKQpyZXN1bHRzKGRlX2NvbmRpdGlvbiwgY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJJUiIsIkNUUiIpLHRpZHk9VFJVRSkgICU+JQogIGFycmFuZ2UocGFkaikgCmBgYAoKYGBge3J9CnJlc3VsdHMoZGVfY29uZGl0aW9uLCBjb250cmFzdCA9IGMoImNvbmRpdGlvbiIsIklSIiwiVEdGIiksdGlkeT1UUlVFKSAlPiUKICBhcnJhbmdlKHBhZGopIApgYGAKCgojIE1vcmUgY29tcGxleCBkZXNpZ25zCgpUaGUgZXhhbXBsZXMgd2UgaGF2ZSB1c2VkIHNvIGZhciBoYXZlIHBlcmZvcm1lZCBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHVzaW5nIGEgbmFtZWQgY29sdW1uIGluIHRoZSBgY29sRGF0YWAgb2JqZWN0LiBUaGUgYERFU2VxMmAgcGFja2FnZSBpcyBjYXBhYmxlIG9mIHBlcmZvcm1pbmcgbW9yZSBjb21wbGV4IGFuYWx5c2VzIHRoYXQgY2FuIHRha2UgbXVsdGlwbGUgZmFjdG9ycyBpbnRvIGNvbnNpZGVyYXRpb24gYXQgdGhlIHNhbWUgdGltZTsgc28tY2FsbGVkICJtdWx0aS1mYWN0b3IgZGVzaWducyIKCi0gW011bHRpLWZhY3RvciBkZXNpZ25zIGluIERFU2VxMl0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI211bHRpLWZhY3Rvci1kZXNpZ25zKQoKVGhlIHVzZSBvZiBzdWNoIGEgZGVzaWduIGNvdWxkIGJlIG1vdGl2YXRlZCBieSBkaXNjb3ZlcmluZyBzb3VyY2VzIG9mIHRlY2huaWNhbCB2YXJpYXRpb24gaW4gb3VyIGRhdGEgdGhhdCBtaWdodCBvYnNjdXJlIHRoZSBiaW9sb2dpY2FsIGRpZmZlcmVuY2VzIHdlIHdvdWxkIGxpa2UgdG8gY29tcGFyZS4gZS5nLgoKIVtdKGltYWdlcy9iYXRjaF9lZmZlY3QucG5nKQoKSW4gdGhlIGV4YW1wbGUgaW1hZ2UgYWJvdmUgdGhlIG1haW4gc291cmNlIG9mIHZhcmlhdGlvbiBpcyB0aGUgYmF0Y2ggaW4gd2hpY2ggdGhlIHNhbXBsZXMgd2VyZSBzZXF1ZW5jZWQuIEEgbXVsdGktZmFjdG9yIGFuYWx5c2lzIHRvIGNvbXBhcmUgdGhlIHZhcmlvdXMgY29uZGl0aW9ucywgYnV0ICJjb3JyZWN0IiBmb3IgZGlmZmVyZW5jZXMgaW4gYmF0Y2gsIHdvdWxkIGJlIGFzIGZvbGxvd3MuCgpgYGB7ciBldmFsPUZBTFNFfQojIyMgRG9uJ3QgcnVuIHRoaXMuIEl0J3MganVzdCBhIGNvZGUgZXhhbXBsZQpkZXNpZ24oTVlfREFUQSkgPC0gfiBiYXRjaCArIGNvbmRpdGlvbgpgYGAKCkxpa2V3aXNlLCBpZiB3ZSBoYXZlIGRpZmZlcmVudCB0cmVhdG1lbnRzIGFwcGxpZWQgdG8gZGlmZmVyZW5jZSBjZWxsLWxpbmVzLCBidXQgdGhlIG1haW4gc291cmNlIG9mIHZhcmlhdGlvbiBpcyB0aGUgY2VsbCBsaW5lIHRoZSBmb2xsb3dpbmcgY291bGQgYmUgdXNlZC4KCmBgYHtyIGV2YWw9RkFMU0V9CiMjIyBEb24ndCBydW4gdGhpcy4gSXQncyBqdXN0IGEgY29kZSBleGFtcGxlCmRlc2lnbihNWV9EQVRBKSA8LSB+Y2VsbF9saW5lICsgdHJlYXRtZW50CmBgYAoKCgojIEFkZGluZyBhbm5vdGF0aW9uIHRvIHRoZSBERVNlcTIgcmVzdWx0cwoKV2Ugd291bGQgbG92ZSB0byBzaGFyZSB0aGVzZSByZXN1bHRzIHdpdGggb3VyIGNvbGxhYm9yYXRvcnMsIG9yIHNlYXJjaCBmb3Igb3VyIGZhdm91cml0ZSBnZW5lIGluIHRoZSByZXN1bHRzLiBIb3dldmVyLCB0aGUgcmVzdWx0cyBhcmUgbm90IHZlcnkgdXNlZnVsIGluIHRoZXJlIGN1cnJlbnQgZm9ybSBhcyBlYWNoIHJvdyBpcyBuYW1lZCBhY2NvcmRpbmcgdG8gYW4gKkVuc2VtYmwqIGlkZW50aWZpZXIuIFdoaWxzdCBnZW5lIHN5bWJvbHMgYXJlIHByb2JsZW1hdGljIGFuZCBjYW4gY2hhbmdlIG92ZXIgdGltZSwgdGhleSBhcmUgdGhlIG5hbWVzIHRoYXQgYXJlIG1vc3QgcmVjb2duaXNhYmxlIGFuZCBtYWtlIHRoZSByZXN1bHRzIGVhc2llciB0byBuYXZpZ2F0ZS4KClRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHRvIGFkZCBhbm5vdGF0aW9uLCBidXQgd2Ugd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG8gZG8gdGhpcyB1c2luZyB0aGUgKm9yZy5Icy5lZy5kYiogcGFja2FnZS4gVGhpcyBwYWNrYWdlIGlzIG9uZSBvZiBzZXZlcmFsICpvcmdhbmlzbS1sZXZlbCogcGFja2FnZXMgaW4gQmlvY29uZHVjdG9yIHRoYXQgYXJlIHJlLWJ1aWx0IGV2ZXJ5IDYgbW9udGhzLiBUaGVzZSBwYWNrYWdlcyBhcmUgbGlzdGVkIG9uIHRoZSBbYW5ub3RhdGlvbiBzZWN0aW9uXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL0Jpb2NWaWV3cy5odG1sI19fX0Fubm90YXRpb25EYXRhKSBvZiB0aGUgQmlvY29uZHVjdG9yLCBhbmQgYXJlIGluc3RhbGxlZCBpbiB0aGUgc2FtZSB3YXkgYXMgcmVndWxhciBCaW9jb25kdWN0b3IgcGFja2FnZXMuIEFuIGFsdGVybmF0aXZlIGFwcHJvYWNoIGlzIHRvIHVzZSBgYmlvbWFSdGAsIGFuIGludGVyZmFjZSB0byB0aGUgW0Jpb01hcnRdKGh0dHA6Ly93d3cuYmlvbWFydC5vcmcvKSByZXNvdXJjZS4gQmlvTWFydCBpcyBtdWNoIG1vcmUgY29tcHJlaGVuc2l2ZSwgYnV0IHRoZSBvcmdhbmlzbSBwYWNrYWdlcyBkbyBub3QgcmVxdWlyZSBvbmxpbmUgYWNjZXNzIG9uY2UgZG93bmxvYWRlZC4KCgpgYGB7ciBldmFsPUZBTFNFfQojIyMgT25seSBleGVjdXRlIHdoZW4geW91IG5lZWQgdG8gaW5zdGFsbCB0aGUgcGFja2FnZQppbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJvcmcuSHMuZWcuZGIiKQojIEZvciBIdW1hbgpCaW9jTWFuYWdlcjo6aW5zdGFsbCgib3JnLkhzLmVnLmRiIikKYGBgCgpUaGUgcGFja2FnZXMgYXJlIGxhcmdlciBpbiBzaXplIHRoYXQgQmlvY29uZHVjdG9yIHNvZnR3YXJlIHBhY2thZ2VzLCBidXQgZXNzZW50aWFsbHkgdGhleSBhcmUgZGF0YWJhc2VzIHRoYXQgY2FuIGJlIHVzZWQgdG8gbWFrZSAqb2ZmbGluZSogcXVlcmllcy4gQW4gYWx0ZXJuYXR2ZSAoYGJpb21hUnRgKSB0aGF0IGNvbm5lY3RzIHRvIHRoZSBlbnNlbWJsIFtiaW9tYXJ0XShodHRwczovL3d3dy5lbnNlbWJsLm9yZy9pbmZvL2RhdGEvYmlvbWFydC9pbmRleC5odG1sKSByZXNvdXJjZSB3aWxsIGJlIGRpc2N1c3NlZCBsYXRlci4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkob3JnLkhzLmVnLmRiKQpgYGAKCgoKRmlyc3Qgd2UgbmVlZCB0byBkZWNpZGUgd2hhdCBpbmZvcm1hdGlvbiB3ZSB3YW50LiBJbiBvcmRlciB0byBzZWUgd2hhdCB3ZSBjYW4gZXh0cmFjdCB3ZSBjYW4gcnVuIHRoZSBgY29sdW1uc2AgZnVuY3Rpb24gb24gdGhlIGFubm90YXRpb24gZGF0YWJhc2UuCgpgYGB7cn0KY29sdW1ucyhvcmcuSHMuZWcuZGIpCmBgYAoKV2UgYXJlIGdvaW5nIHRvIGZpbHRlciB0aGUgZGF0YWJhc2UgYnkgYSBrZXkgb3Igc2V0IG9mIGtleXMgaW4gb3JkZXIgdG8gZXh0cmFjdCB0aGUgaW5mb3JtYXRpb24gd2Ugd2FudC4gVmFsaWQgbmFtZXMgZm9yIHRoZSBrZXkgY2FuIGJlIHJldHJpZXZlZCB3aXRoIHRoZSBga2V5dHlwZXNgIGZ1bmN0aW9uLgoKYGBge3J9CmtleXR5cGVzKG9yZy5Icy5lZy5kYikKYGBgCgpXZSBzaG91bGQgc2VlIGBFTlNFTUJMYCwgd2hpY2ggaXMgdGhlIHR5cGUgb2Yga2V5IHdlIGFyZSBnb2luZyB0byB1c2UgaW4gdGhpcyBjYXNlLiBJZiB3ZSBhcmUgdW5zdXJlIHdoYXQgdmFsdWVzIGFyZSBhY2NlcHRhYmxlIGZvciB0aGUga2V5LCB3ZSBjYW4gY2hlY2sgd2hhdCBrZXlzIGFyZSB2YWxpZCB3aXRoIGBrZXlzYAoKYGBge3J9CmtleXMob3JnLkhzLmVnLmRiLCBrZXl0eXBlPSJFTlNFTUJMIilbMToxMF0KYGBgCgpGb3IgdGhlIHRvcCBnZW5lIGluIG91ciBhbmFseXNpcyB0aGUgY2FsbCB0byB0aGUgZnVuY3Rpb24gd291bGQgYmU6LQoKYGBge3IgZXZhbD1GQUxTRX0Kc2VsZWN0KG9yZy5Icy5lZy5kYiwga2V5cz0iRU5TRzAwMDAwMTU4MjU4IiwKICAgICAgIGtleXR5cGUgPSAiRU5TRU1CTCIsY29sdW1ucz1jKCJTWU1CT0wiLCJHRU5FTkFNRSIpCikKYGBgCgpVbmZvcnR1bmF0ZWx5LCB0aGUgYXV0aG9ycyBvZiBgZHBseXJgIGFuZCBgQW5ub3RhdGlvbkRiaWAgaGF2ZSBib3RoIGRlY2lkZWQgdG8gdXNlIHRoZSBuYW1lIGBzZWxlY3RgIGluIHRoZWlyIHBhY2thZ2VzLiBUbyBhdm9pZCBjb25mdXNpb24gYW5kIGVycm9ycywgdGhlIGZvbGxvd2luZyBjb2RlIGlzIHNvbWV0aW1lcyB1c2VkOi0KCmBgYHtyfQpBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiLCBrZXlzPSJFTlNHMDAwMDAxNTgyNTgiLGtleXR5cGUgPSAiRU5TRU1CTCIsY29sdW1ucz1jKCJTWU1CT0wiLCJHRU5FTkFNRSIpKQpgYGAKCgpUbyBhbm5vdGF0ZSBvdXIgcmVzdWx0cywgd2UgZGVmaW5pdGVseSB3YW50IGdlbmUgc3ltYm9scyBhbmQgcGVyaGFwcyB0aGUgZnVsbCBnZW5lIG5hbWUuIExldCdzIGJ1aWxkIHVwIG91ciBhbm5vdGF0aW9uIGluZm9ybWF0aW9uIGludG8gYSBuZXcgZGF0YSBmcmFtZSB1c2luZyB0aGUgYHNlbGVjdGAgZnVuY3Rpb24uCgpgYGB7cn0KYW5ubyA8LSBBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiLGtleXM9cm93bmFtZXMoZGRzKSwKICAgICAgICAgICAgICBjb2x1bW5zPWMoIlNZTUJPTCIsIkdFTkVOQU1FIiksCiAgICAgICAgICAgICAga2V5dHlwZT0iRU5TRU1CTCIpCiMgSGF2ZSBhIGxvb2sgYXQgdGhlIGFubm90YXRpb24KaGVhZChhbm5vKQpgYGAKCkhvd2V2ZXIsIHdlIGhhdmUgYSBwcm9ibGVtIHRoYXQgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGhhcyBtb3JlIHJvd3MgdGhhbiBvdXIgcmVzdWx0cyB0YWJsZS4gVGhpcyBpcyBkdWUgdG8gdGhlICpvbmUtdG8tbWFueSogcmVsYXRpb25zaGlwcyB0aGF0IG9mdGVuIG9jY3VyIHdoZW4gbWFwcGluZyBiZXR3ZWVuIHZhcmlvdXMgaWRlbnRpZmllcnMuCgpgYGB7cn0KZGltKGFubm8pCmRpbShkZHMpCmBgYAoKU3VjaCBkdXBsaWNhdGVkIGVudHJpZXMgY2FuIGJlIGlkZW50aWZpZWQgdXNpbmcgdGhlIGBkdXBsaWNhdGVkYCBmdW5jdGlvbi4gRm9ydHVuYXRlbHksIHRoZXJlIGFyZSBub3QgdG9vIG1hbnkgc28gaG9wZWZ1bGx5IHdlIHdvbid0IGxvc2UgdG9vIG11Y2ggaW5mb3JtYXRpb24gaWYgd2UgZGlzY2FyZCB0aGUgZW50cmllcyB0aGF0IGFyZSBkdXBsaWNhdGVkLiBUaGUgZmlyc3Qgb2NjdXJyZW5jZSBvZiB0aGUgZHVwbGljYXRlZCBJRCB3aWxsIHN0aWxsIGJlIGluY2x1ZGVkIGluIHRoZSB0YWJsZS4KCmBgYHtyfQphbm5vIDwtIEFubm90YXRpb25EYmk6OnNlbGVjdChvcmcuSHMuZWcuZGIsa2V5cz1yb3duYW1lcyhkZHMpLAogICAgICAgICAgICBjb2x1bW5zPWMoIkVOU0VNQkwiLCJTWU1CT0wiLCJHRU5FTkFNRSIsIkVOVFJFWklEIiksCiAgICAgICAgICAgIGtleXR5cGU9IkVOU0VNQkwiKSAlPiUgCmZpbHRlcighZHVwbGljYXRlZChFTlNFTUJMKSkKZGltKGFubm8pCmBgYAoKCldlIGNhbiBiaW5kIGluIHRoZSBhbm5vdGF0aW9uIGluZm9ybWF0aW9uIHRvIHRoZSBgcmVzdWx0c2AgZGF0YSBmcmFtZS4gCgpgYGB7cn0KcmVzdWx0c19hbm5vdGF0ZWQgPC0gcmVzdWx0cyhkZV90cmVhdGVkLHRpZHk9VFJVRSkgJT4lIAogIGxlZnRfam9pbihhbm5vLCBieT1jKCJyb3ciPSJFTlNFTUJMIikpCgpoZWFkKHJlc3VsdHNfYW5ub3RhdGVkKQpgYGAKCgpXZSBjYW4gc2F2ZSB0aGUgcmVzdWx0cyB0YWJsZSB1c2luZyB0aGUgYHdyaXRlLmNzdmAgZnVuY3Rpb24sIHdoaWNoIHdyaXRlcyB0aGUgcmVzdWx0cyBvdXQgdG8gYSBjc3YgZmlsZSB0aGF0IHlvdSBjYW4gb3BlbiBpbiBleGNlbC4KCmBgYHtyfQp3cml0ZS5jc3YocmVzdWx0c19hbm5vdGF0ZWQsZmlsZT0iZGVfYW5hbHlzaXMvdHJlYXRtZW50X1lfdnNfTl9ERVNlcV9hbm5vdGF0ZWQuY3N2Iixyb3cubmFtZXM9RkFMU0UpCnNhdmVSRFMocmVzdWx0c19hbm5vdGF0ZWQsIGZpbGU9IlJvYmplY3RzL3RyZWF0bWVudF9ZX3ZzX05fREVTZXFfYW5ub3RhdGVkLnJkcyIpCmBgYAoKCiMgRXhlcmNpc2UKCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KCi0gSm9pbiB0aGUgYW5ub3RhdGlvbiB0YWJsZSB0byB5b3VyIHJlc3VsdHMgZnJvbSB0aGUgREVTZXEgYW5hbHlzaXMgb2YgYFRHRmAgdnMgYENUUmAuIFNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGFzIGEgY3N2IGZpbGUuIGUuZy4gYFJvYmplY3RzL3Jlc3VsdHNfVEdGX3ZzX0NUUl9hbm5vdGF0ZWQuY3N2YAotIFRoZSBwdWJsaWNhdGlvbiBnaXZlcyBleGFtcGxlcyBvZiBgQ09MMUExYCwgYENPTDFBMmAgYW5kIGBDT0wzQTFgIGFzIGdlbmVzIHRoYXQgYXJlICp1cC1yZWd1bGF0ZWQqIGluIFRHRi10cmVhdGVkIHNhbXBsZXMgdnMgY29udHJvbHMgKEZpZ3VyZSA2QykuIFVzZSB5b3VyIGRhdGEgdG8gdmVyaWZ5IHRoaXMgYnkgCiAgKyBpKSBleHRyYWN0aW5nIHRoZWlyIHAtdmFsdWVzCiAgKyBpaSkgcGxvdHRpbmcgdGhlIGNvdW50cyBmb3IgdGhlc2UgZ2VuZXMgCgo8L2Rpdj4KCmBgYHtyfQoKCmBgYAoKCgoKIyMjIEV4cG9ydGluZyBub3JtYWxpemVkIGNvdW50cwoKVGhlIGBERVNlcWAgd29ya2Zsb3cgYXBwbGllcyAqbWVkaWFuIG9mIHJhdGlvcyBub3JtYWxpemF0aW9uKiB0aGF0IGFjY291bnRzIGZvciBkaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoIGJldHdlZW4gc2FtcGxlcy4gVGhlIHVzZXIgZG9lcyBub3QgdXN1YWxseSBuZWVkIHRvIHJ1biB0aGlzIHN0ZXAuIEhvd2V2ZXIsIGlmIHlvdSB3YW50IGEgbWF0cml4IG9mIGNvdW50cyBmb3Igc29tZSBhcHBsaWNhdGlvbiBvdXRzaWRlIG9mIEJpb2NvbmR1Y3RvciB0aGUgdmFsdWVzIGNhbiBiZSBleHRyYWN0ZWQgZnJvbSB0aGUgYGRkc2Agb2JqZWN0LgoKYGBge3J9CmRkcyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkcykgCmNvdW50TWF0cml4IDwtY291bnRzKGRkcywgbm9ybWFsaXplZD1UUlVFKSAKaGVhZChjb3VudE1hdHJpeCkKd3JpdGUuY3N2KGNvdW50TWF0cml4LGZpbGU9Im5vcm1hbGl6ZWRfY291bnRzLmNzdiIpCmBgYAoKCiMgRnVsbCBERVNlcSB3b3JrZmxvdwoKVGhlIG1lZGlhbiBvZiByYXRpb3Mgbm9ybWFsaXNhdGlvbiBtZXRob2QgaXMgZW1wbG95ZWQgaW4gREVTZXEyIHRvIGFjY291bnQgZm9yICpzZXF1ZW5jaW5nIGRlcHRoKiBhbmQgKlJOQSBjb21wb3NpdGlvbiouIExldCdzIGdvIHRocm91Z2ggYSBzaG9ydCB3b3JrZWQgZXhhbXBsZSAoY291cnRlc3kgb2YgW2h0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzAyX0RHRV9jb3VudF9ub3JtYWxpemF0aW9uLmh0bWxdKGhlcmUpKSB0byBleHBsYWluIHRoZSBwcm9jZXNzLgoKYGBge3J9CiMjIGNyZWF0ZSBhIHNtYWxsIGV4YW1wbGUgbWF0cml4IG9mICJjb3VudHMiCnRlc3RfZGF0YSA8LSBtYXRyaXgoYygxNDg5LDIyLDc5Myw3Niw1MjEsOTA2LDEzLDQxMCw0MiwxMTk2KSxucm93PTUpCnJvd25hbWVzKHRlc3RfZGF0YSkgPC0gYygiRUYyQSIsIkFCQ0QxIiwiTUVGViIsIkJBRzEiLCJNT1YxMCIpCmNvbG5hbWVzKHRlc3RfZGF0YSkgPC0gYygiU2FtcGxlQSIsIlNhbXBsZUIiKQp0ZXN0X2RhdGEKYGBgCgpGaXJzdGx5LCBhbiAiYXZlcmFnZSIgb3IgcmVmZXJlbmNlIHNhbXBsZSBpcyBjcmVhdGVkIHRoYXQgcmVwcmVzZW50cyB0aGUgY291bnRzIG9uIGEgdHlwaWNhbCBzYW1wbGUgaW4gdGhlIGRhdGFzZXQuIFRoZSAqZ2VvbWV0cmljIG1lYW4qIGlzIHVzZWQgcmF0aGVyIHRoYW4gdGhlICphcml0aG1ldGljKiBtZWFuLiBJbiBvdGhlciB3b3JkcyB0aGUgaW5kaXZpZHVhbCBjb3VudHMgYXJlIG11bHRpcGxpZWQgcmF0aGVyIHRoYW4gc3VtbWVkIGFuZCB0aGUgbWVhc3VyZSBzaG91bGQgYmUgbW9yZSByb2J1c3QgdG8gb3V0bGllcnMuCgoKYGBge3J9CnBzdWVkb19yZWYgPC0gc3FydChyb3dQcm9kcyh0ZXN0X2RhdGEpKQpwc3VlZG9fcmVmCmBgYAoKQSByYXRpb3Mgb2Ygc2FtcGxlIHRvICJwc3VlZG8gcmVmZXJlbmNlIiBhcmUgdGhlbiBjYWxjdWxhdGVkIGZvciBlYWNoIGdlbmUuIFdlIGFyZSBhc3N1bWluZyB0aGF0IG1vc3QgZ2VuZXMgYXJlIG5vdCBjaGFuZ2luZyBkcmFtYXRpY2FsbHksIHNvIHRoaXMgcmF0aW8gc2hvdWxkIGJlIHNvbWV3aGVyZSBhcm91bmQgMS4KCmBgYHtyfQp0ZXN0X2RhdGEvcHN1ZWRvX3JlZgpgYGAKCmBERVNlcTJgIGRlZmluZXMgc2l6ZSBmYWN0b3JzIGFzIGJlaW5nIHRoZSAqbWVkaWFuKiBvZiB0aGVzZSByYXRpb3MgZm9yIGVhY2ggc2FtcGxlIChtZWRpYW4gaXMgdXNlZCBzbyBhbnkgb3V0bGllciBnZW5lcyB3aWxsIG5vdCBhZmZlY3QgdGhlIG5vcm1hbGlzYXRpb24pLiAKCmBgYHtyfQpub3JtX2ZhY3RvcnMgPC0gY29sTWVkaWFucyh0ZXN0X2RhdGEvcHN1ZWRvX3JlZikKbm9ybV9mYWN0b3JzCmBgYAoKSW5kaXZpZHVhbCBzYW1wbGVzIGNhbiB0aGVuIG5vcm1hbGlzZWQgYnkgZGl2aWRpbmcgdGhlIGNvdW50IGZvciBlYWNoIGdlbmUgYnkgdGhlIGNvcnJlc3BvbmRpbmcgbm9ybWFsaXphdGlvbiBmYWN0b3IuCgpgYGB7cn0KdGVzdF9kYXRhWywxXSAvIG5vcm1fZmFjdG9yc1sxXQpgYGAKCmFuZCBmb3IgdGhlIHNlY29uZCBzYW1wbGUuLi4KCmBgYHtyfQp0ZXN0X2RhdGFbLDJdIC8gbm9ybV9mYWN0b3JzWzJdCgpgYGAKClRoZSBzaXplIGZhY3RvcnMgZm9yIGVhY2ggc2FtcGxlIGluIG91ciBkYXRhc2V0IGNhbiBiZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBgZXN0aW1hdGVTaXplRmFjdG9yc0Zvck1hdHJpeGAgZnVuY3Rpb24uCgpgYGB7cn0Kc2YgPC0gZXN0aW1hdGVTaXplRmFjdG9yc0Zvck1hdHJpeChhc3NheShkZHMpKQpzZgpgYGAKClRoZSBlc3RpbWF0aW9uIG9mIHRoZXNlIGZhY3RvcnMgY2FuIGFsc28gdGFrZSBnZW5lLWxlbmd0aHMgaW50byBhY2NvdW50LCBhbmQgdGhpcyBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgYGVzdGltYXRlU2l6ZUZhY3RvcnNgIGZ1bmN0aW9uLiBFeHRyYSBub3JtYWxpemF0aW9uIGZhY3RvciBkYXRhIGlzIGFkZGVkIHRvIHRoZSBgZGRzYCBvYmplY3QuCgoKCmBgYHtyIGV2YWw9RkFMU0V9CmRkcyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkcykKZGRzCmBgYAoKSW4gcHJlcGFyYXRpb24gZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIERFU2VxMiBhbHNvIG5lZWQgYSByZWxpYWJsZSBlc3RpbWF0ZSBvZiB0aGUgdmFyaWFiaWxpdHkgb2YgZWFjaCBnZW5lOyB3aGljaCBpdCBjYWxscyAqZGlzcGVyc2lvbiouIAoKYGBge3IgZXZhbD1GQUxTRX0KZGRzIDwtIGVzdGltYXRlRGlzcGVyc2lvbnMoZGRzKQpkZHMKCmBgYAoKQSBzdGF0aXN0aWNhbCB0ZXN0IGNhbiB0aGVuIGJlIGFwcGxpZWQuIEFzIHRoZSBkYXRhIGFyZSBjb3VudC1iYXNlZCBhbmQgbm90IG5vcm1hbGx5LWRpc3RyaWJ1dGVkIGEgdC10ZXN0IHdvdWxkIG5vdCBiZSBhcHByb3ByaWF0ZS4gTW9zdCB0ZXN0cyBhcmUgYmFzZWQgb24gYSAqUG9pc3Nvbiogb3IgKm5lZ2F0aXZlLWJpbm9taWFsKiBkaXN0cmlidXRpb247IG5lZ2F0aXZlIGJpbm9taWFsIGluIHRoZSBjYXNlIG9mIGBERVNlcTJgLiBBbHRob3VnaCB5b3UgbWlnaHQgbm90IGJlIGZhbWlsaWFyIHdpdGggdGhlIG5lZ2F0aXZlIGJpbm9taWFsLCB0aGUgcmVzdWx0cyBzaG91bGQgYmUgaW4gYSBmYW1pbGlhciBmb3JtIHdpdGggZm9sZC1jaGFuZ2VzIGFuZCBwLXZhbHVlcyBmb3IgZWFjaCBnZW5lLgoKYGBge3IgZXZhbD1GQUxTRX0KZGRzIDwtIG5iaW5vbVdhbGRUZXN0KGRkcykKYGBgCgpJdCBtYXkgc2VlbSBsaWtlIHRoZXJlIGlzIGEgbG90IHRvIHJlbWVtYmVyLCBidXQgZm9ydHVuYXRlbHkgdGhlcmUgaXMgb25lIGNvbnZlbmllbnQgZnVuY3Rpb24gdGhhdCB3aWxsIGFwcGx5IHRoZSB0aHJlZSBzdGVwcyAoYERFU2VxYCkuIFRoZSBtZXNzYWdlcyBwcmludGVkIHNlcnZlIGFzIHJlbWluZGVycyBvZiB0aGUgc3RlcHMgaW5jbHVkZWQuCgoKIyBBY2tub3dsZWRnZW1lbnRzCgoqKk9yaWdpbmFsIEF1dGhvcnM6IEJlbGluZGEgUGhpcHNvbiwgQW5uYSBUcmlnb3MsIE1hdHQgUml0Y2hpZSwgTWFyaWEgRG95bGUsIEhhcnJpZXQgRGFzaG5vdywgQ2hhcml0eSBMYXcqKiwgKipTdGVwaGFuZSBCYWxsZXJlYXUsIE9zY2FyIFJ1ZWRhLCBBc2hsZXkgU2F3bGUqKgpCYXNlZCBvbiB0aGUgY291cnNlIFtSTkFzZXEgYW5hbHlzaXMgaW4gUl0oaHR0cDovL2NvbWJpbmUtYXVzdHJhbGlhLmdpdGh1Yi5pby8yMDE2LTA1LTExLVJOQXNlcS8pIGRlbGl2ZXJlZCBvbiBNYXkgMTEvMTJ0aCAyMDE2IGFuZCBtb2RpZmllZCBieSBDYW5jZXIgUmVzZWFyY2ggVWsgQ2FtYnJpZGdlIENlbnRyZSBmb3IgdGhlIFtGdW5jdGlvbmFsIEdlbm9taWNzIEF1dHVtbiBTY2hvb2wgMjAxN10oaHR0cHM6Ly9iaW9pbmZvcm1hdGljcy1jb3JlLXNoYXJlZC10cmFpbmluZy5naXRodWIuaW8vY3J1ay1hdXR1bW4tc2Nob29sLTIwMTcvKQoKIyMgUmVzb3VyY2VzIGFuZCBkYXRhIGZpbGVzCgpUaGlzIG1hdGVyaWFsIGhhcyBiZWVuIGNyZWF0ZWQgdXNpbmcgdGhlIGZvbGxvd2luZyByZXNvdXJjZXM6ICAKCi0gaHR0cDovL3d3dy5zdGF0c2NpLm9yZy9zbXl0aC9wdWJzL1FMZWRnZVJQcmVwcmludC5wZGYgCi0gaHR0cDovL21vbmFzaGJpb2luZm9ybWF0aWNzcGxhdGZvcm0uZ2l0aHViLmlvL1JOQXNlcS1ERS1hbmFseXNpcy13aXRoLVIvOTktUk5Bc2VxX0RFX2FuYWx5c2lzX3dpdGhfUi5odG1sICAKLSBodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwKLSBodHRwczovL2Jpb2NvbmR1Y3Rvci5naXRodWIuaW8vQmlvY1dvcmtzaG9wcy9ybmEtc2VxLWRhdGEtYW5hbHlzaXMtd2l0aC1kZXNlcTIuaHRtbA==