Introduction

The purpose of this tutorial is to go over how to import and summarize transcript-level abundance estimates and complete differential expression (DE) analysis using DESeq2. I also include some basic ideas for downstream analysis and visualizing your data. The original article describing these methods can be found below:

Charlotte Soneson, Michael I. Love, Mark D. Robinson (2015): Differential analyses for RNA-seq: transcript-level estimates improve gene-level inferences. F1000Research http://dx.doi.org/10.12688/f1000research.7563.1

Notes

  • If you are primarily interested in how to use DESeq2 or visualizations of RNA-seq results, skip to Part 2: Differential Expression Analysis using DESeq2.
  • Another Bioconductor package, tximeta (Love et al. 2020), extends tximport, offering the same functionality, plus the additional benefit of automatic addition of annotation metadata for commonly used transcriptomes (GENCODE, Ensembl, RefSeq for human and mouse). tximeta also offers easy conversion to data objects used by edgeR and limma with the makeDGEList function.

Installing and Loading packages

The first step is to install and load required packages. Visit https://www.datacamp.com/community/tutorials/r-packages-guide for more information about packages and package repositories. You only need to install packages once - however, you need to keep track of versions and you may need to update packages depending on new releases. Below is an example of how to install a package from the Bioconductor repo.

#install packages 
if (!requireNamespace("BiocManager", quietly = FALSE))
  install.packages("BiocManager")
BiocManager::install("tximport")

install.packages("pheatmap")
#load necessary packages
library(TxDb.Mmusculus.UCSC.mm39.refGene)
library(ggplot2)
library(ggpubr)
library(tximport)
library(DESeq2)
library(GenomicFeatures)
library(tximeta)
library(SummarizedExperiment)
library(org.Mm.eg.db)
library(pheatmap)
library(ggrepel)
library(dplyr)
library(RColorBrewer)
library(EnhancedVolcano)
library(biomaRt)
library(enrichplot)
library(clusterProfiler)
library(scales)
library(readr)
library(ggnewscale)
library(ggridges)
library(M3C)
library(RColorBrewer)
library(viridis)
library(pathview)
library(rWikiPathways)
library(RCy3)
library(UpSetR)
library(ReactomePA)
library(ggupset)
library(knitr)
library(kableExtra)

Transcript quantification for gene-level analysis

You will need a metadata file that includes your sample names as well as any group/explanatory variables. Your sample names need to exactly match your file names (from Salmon), 1 sample per row. I recommend exporting the names.txt file you created when doing the Salmon analysis on the server.

metadata <- read.csv("MattAgingAstrocyte_Metadata2.csv", header = TRUE)
metadata$AgeRegion <- as.character(paste(metadata$Age, metadata$RealRegion, sep= "_"))

Import and Summarize transcript quantification files

The tximeta package has a single function for importing transcript-level estimates. The type argument is used to specify what software was used for estimation. A simple list with matrices, “abundance”, “counts”, and “length”, is returned, where the transcript level information is summarized to the gene-level. Typically, abundance is provided by the quantification tools as TPM (transcripts-per-million), while the counts are estimated counts (possibly fractional), and the “length” matrix contains the effective gene lengths.

#First, create a list, called files, with the file path to the quant.sf (Salmon output) files for each sample
files <- file.path("AgingAstrocyteTranscriptome_mm39out", metadata$FullSampleName, "quant.sf")

#Check to make sure all the files are actually located in the right place
all(file.exists(files)) #This should output "TRUE"
## [1] TRUE
#create a dataframe containing those filepaths, the names of the samples, and any other metadata you would like to include. 
#I include all of the metadata (from the metadata file)
coldata <- data.frame(files, names = metadata$FullSampleName, metadata)
se <- tximeta(coldata, type="salmon")
## importing quantifications
## reading in files with read_tsv
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 
## found matching transcriptome:
## [ Ensembl - Mus musculus - release 105 ]
## loading existing EnsDb created: 2022-03-15 17:25:23
## loading existing transcript ranges created: 2022-03-15 17:25:27
#Code examples to view different outputs from the summarized experiment (se)
#assayNames(se)
#rowRanges(se)
#seqinfo(se)
#se.exons <- addExons(se)
#transcript <- as.data.frame(assay(se))

#Summarize to genes
gse <- summarizeToGene(se)
## loading existing EnsDb created: 2022-03-15 17:25:23
## obtaining transcript-to-gene mapping from database
## loading existing gene ranges created: 2022-03-15 17:35:44
## summarizing abundance
## summarizing counts
## summarizing length
#genes <- as.data.frame(assay(gse))
#You can add other metadata to the gene summarized experiment object (gse) using the following code
columns(org.Mm.eg.db)
##  [1] "ACCNUM"       "ALIAS"        "ENSEMBL"      "ENSEMBLPROT"  "ENSEMBLTRANS"
##  [6] "ENTREZID"     "ENZYME"       "EVIDENCE"     "EVIDENCEALL"  "GENENAME"    
## [11] "GENETYPE"     "GO"           "GOALL"        "IPI"          "MGI"         
## [16] "ONTOLOGY"     "ONTOLOGYALL"  "PATH"         "PFAM"         "PMID"        
## [21] "PROSITE"      "REFSEQ"       "SYMBOL"       "UNIPROT"
#gse <- addIds(gse, "SYMBOL", gene=TRUE)
#gse <- addIds(gse, "ENTREZID", gene=TRUE)
#gse <- addIds(gse, "ENSEMBL", gene=TRUE)

Create tpm table and add gene annotations

annotation <- as.data.frame(mcols(gse))
annotation <- as.data.frame(mcols(gse))

mart <- biomaRt::useDataset("mmusculus_gene_ensembl", useMart("ensembl")) #for default mouse genome

gene_list <- getBM(filters= "ensembl_gene_id",
                   attributes=c("description","ensembl_gene_id"),
                   values= annotation$ENSEMBL,
                   mart= mart,
                   useCache = FALSE)

annotation <-merge(annotation, gene_list, by.x = c('ENSEMBL'), by.y = c('ensembl_gene_id'))

tpm <- as.data.frame(assay(gse, "abundance"))

#To change the column names based on sample names in metadata
#colnames(tpm) <- metadata$PrepID

tpm <- merge(tpm, annotation, by.x = 0, by.y = "gene_id")
tpm <- select(tpm, -1)
rownames(tpm) <- tpm$ENSEMBL

tpm_geneOnly <- filter(tpm, !ENTREZID == "NA")

Differential Expression Analysis using DESeq2

Check out this vignette for detailed information regarding the DESeq2 package. Check out this other vignette for information about multi-group comparisons (e.g Sex + Genotype + Age and interaction terms).

DESeq2 takes in a counts matrix - the number of sequence fragments that have been assigned to each gene. To statistical determine which genes are differentially expressed genes, we need to determine the changes between our conditions compared to the within-condition variability. DESeq2 uses a negative binomial generalized linear model. The normalized counts provided by DESeq2 are called median of ratios. You should not use these normalized counts for within sample comparison - They are primarily used for DE analysis.

First you create a DESeqDataSet (dds) object (contains data + metadata) and then run DE analysis. In my example data, astrocyte mRNA was sequenced from 4-month and 2-year old male mice, from 5 different brain regions, using the Ribotag system original paper.

Note Check out the tutorial above for information regarding multiple conditions and interaction terms. In this case, I would actually make a new variable combining age and region, to keep things simple. Then I would use that new variable in my design (instead of Age as shown below; see the 2nd code chunk for how to do this).

dds <- DESeqDataSet(gse, design = ~ RealRegion + Age) #the ~ are your design variables. If you would like to test for an interaction you would do ~Age + RealRegion + Age:RealRegion

dds$Age <- relevel(dds$Age, ref = "4m") #Set a group as reference; default is alphabetical

dge <- DESeq(dds) #run Diff gene exp analysis

round(colSums(counts(dds))/1e6) #number of reads per sample

DESeq2 with STAR Counts Table (or other table)

If you already have a counts table (MUST be raw counts, un-normalized), then you can create the dds object with the following code:

Note You may need to clean up your counts matrix before running the DESeq2 analysis

#Code if counts matrix (i.e. from STAR) is the input instead of txi from Salmon
dds <- DESeqDataSetFromMatrix(countData = counts,
                              colData = metadata,
                              design = ~ Group)

Here I am creating a new varaible called AgeRegion that I will use as my variable of interest

dds <- DESeqDataSet(gse, design = ~ AgeRegion)
## using counts and average transcript lengths from tximeta
## Warning in DESeqDataSet(gse, design = ~AgeRegion): some variables in design
## formula are characters, converting to factors
#dds$AgeRegion <- relevel(dds$AgeRegion, ref = "4m_CB") #Set a group as reference; default is alphabetical

dds <- estimateSizeFactors(dds)
## using 'avgTxLength' from assays(dds), correcting for library size
counts <- as.data.frame(counts(dds, normalized = TRUE))

keep <- rowSums(counts(dds)) >= 10
dds <- dds[keep,]

dds <- DESeq(dds)
## using pre-existing normalization factors
## estimating dispersions
## gene-wise dispersion estimates
## mean-dispersion relationship
## final dispersion estimates
## fitting model and testing

To check for outliers:

par(mar=c(8,5,2,2))
boxplot(log10(assays(dds)[["cooks"]]), range=0, las=2)

Extracting Results from DE Analysis

resultsNames(dds) #view names for the different combinations you tested for - but only for the ones against reference level. Since we have multiple groups (and no single baseline/control), use the contrast option (2nd option below)
##  [1] "Intercept"                 "AgeRegion_2y_HTH_vs_2y_CB"
##  [3] "AgeRegion_2y_MC_vs_2y_CB"  "AgeRegion_2y_SSC_vs_2y_CB"
##  [5] "AgeRegion_2y_VC_vs_2y_CB"  "AgeRegion_4m_CB_vs_2y_CB" 
##  [7] "AgeRegion_4m_HTH_vs_2y_CB" "AgeRegion_4m_MC_vs_2y_CB" 
##  [9] "AgeRegion_4m_SSC_vs_2y_CB" "AgeRegion_4m_VC_vs_2y_CB"
summary(results(dds, name = "AgeRegion_4m_VC_vs_2y_CB")) #this option won't give you all of your possible comparisons, just those against your reference level
## 
## out of 19363 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0 (up)       : 3865, 20%
## LFC < 0 (down)     : 3875, 20%
## outliers [1]       : 17, 0.088%
## low counts [2]     : 2253, 12%
## (mean count < 1)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
summary(results(dds, contrast =c("AgeRegion", "2y_VC","4m_VC"))) #use this to obtain all possible comparisons
## 
## out of 19363 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0 (up)       : 189, 0.98%
## LFC < 0 (down)     : 166, 0.86%
## outliers [1]       : 17, 0.088%
## low counts [2]     : 3750, 19%
## (mean count < 4)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
res_VC <- results(dds, contrast =c("AgeRegion", "2y_VC","4m_VC")) #select results as table, needed for visualizations below
res_MC <- results(dds, contrast =c("AgeRegion", "2y_MC","4m_MC")) 
res_HTH <- results(dds, contrast =c("AgeRegion", "2y_HTH","4m_HTH")) 
res_SSC <- results(dds, contrast =c("AgeRegion", "2y_SSC","4m_SSC")) 
res_CB <- results(dds, contrast =c("AgeRegion", "2y_CB","4m_CB")) 

#make dataframes of all contrasts
res_VC.df <- as.data.frame(res_VC) 
res_MC.df <- as.data.frame(res_MC)
res_CB.df <- as.data.frame(res_CB)
res_HTH.df <- as.data.frame(res_HTH)
res_SSC.df <- as.data.frame(res_SSC)

#all in one step
#res_CB <- as.data.frame(results(dds, contrast =c("AgeRegion", "2y_CB","4m_CB")))

#write.csv(res_VC.df, "AgingAstroTranscriptome_Results_VC.csv", row.names = TRUE) #to export as csv file

Visualizations

MA Plot

the function plotMA shows the log2 fold changes attributable to a given variable over the mean of normalized counts for all the samples in the DESeqDataSet.

plotMA(res_CB, ylim=c(-2,2))
abline(h=c(-1,1), col="dodgerblue", lwd=2) #add horizontal line

par(mfrow=c(1,4), mar=c(2,2,2,2))
plotMA(res_VC, ylim=c(-2,2), main = "VC")
plotMA(res_MC, ylim=c(-2,2), main = "MC")
plotMA(res_HTH, ylim=c(-2,2), main = "HTH")
plotMA(res_CB, ylim=c(-2,2), main = "CB")

### PCA

There are several different options for making PCA plots in R. Here I show an example with plotPCA and ggplot. The other popular option is PCAtools

vsd <- vst(dds) #1st you need to transform data. I use variance stabilization transformation

data <- plotPCA(vsd, intgroup = "Age", returnData = TRUE)

percent_var <- round(100 * attr(data, "percentVar"))

ggplot(data, aes(x = PC1, y = PC2, color = metadata$RealRegion, shape = metadata$Age)) + 
  geom_point(size = 5) +
  xlab(paste("PC1: ", percent_var[1], "%variance")) +
  ylab(paste("PC2: ", percent_var[2], "%variance")) + 
  theme_classic(base_size = 14) + theme(legend.title = element_blank()) 

Here is another PCA plot, but with a few more modifications. The commented lines are not included in the code to generate the plot (included here as an example, if you wanted to add labels to each point).

pca <- ggplot(data, aes(x = PC1, y = PC2, 
                        color = metadata$Age, shape = metadata$RealRegion))+ 
      geom_point(size = 3) + 
      # geom_text_repel(aes(label = 'labelname',
      #                 nudge_x = .5, nudge_y = 0.5,
      #                 fontface = 2,
      #                 check_overlap = TRUE,
      #                 size = 4) +
      xlab(paste("PC1: ", percent_var[1], "%variance")) +
      ylab(paste("PC2: ", percent_var[2], "%variance")) + 
      labs(shape = "Brain Region", color = "Age") +
      scale_shape_discrete(labels=c("Cerebellum", "Hypothalamus", "Motor Cortex",   "Somatosensory", "Visual Cortex")) +
      scale_color_discrete(labels=c("2 year old", "4 month old")) +
      scale_color_manual(values = c("deeppink1","deepskyblue1"))+
      theme_classic(base_size = 12)  +    
      theme(plot.title = element_text(hjust = 0.5, size=14, face="bold", margin = margin(0,0,10,0))) +
      ggtitle("PCA: Aging Astrocyte Transcriptome")
## Scale for 'colour' is already present. Adding another scale for 'colour',
## which will replace the existing scale.
pca

#Code to calculate ellipses around data - not possible with this dataset due to low sample size per group
# pca + stat_ellipse(aes(group = metadata$Region),
#                                   type = "t",
#                                   linetype = 2,
#                                   lwd = 1)

Heatmaps

There are several options for heatmaps as well. I like to use pheatmap because the default options are pretty good. You can plot a heatmap with all your genes, but it takes a long time and is not super informative. Alternatively, try plotting a heatmap of the most variable genes or a subset of genes of interest (examples shown below). The color scale is based on Z-scores of the transformed counts matrix.

#Example 1: Selecting most variable genes
topVarGenes <- head(order(-rowVars(assay(vsd))),100) # select top 100 most variable genes (across all samples)

pheatmap(assay(vsd)[topVarGenes,]) #basic heatmap

sampleInfo <- as.data.frame(colData(dds)[,c("Age","RealRegion")]) #include annotations for plot based on metadata

map1 <- pheatmap(assay(vsd)[topVarGenes,], 
           annotation_col = sampleInfo,
           scale = "row",
           color = colorRampPalette(c("navy", "white", "firebrick3"))(50),
           clustering_distance_rows = "euclidean",
           show_rownames = FALSE, 
           show_colnames = FALSE,
           cluster_rows = TRUE,
           cluster_cols =  FALSE)

#Example 2: Selecting significant DEGs for just Cerebellum 2y vs 4m 
sigGenes <- subset(as.data.frame(res_CB), padj <=0.01) 
sigGenes <- subset(sigGenes, abs(log2FoldChange) > 1)

#Create custom colors for your labels 
my_colour = list(
  Age = c("2y" = "steelblue2", "4m" = "brown2"),
  RealRegion = c( CB = "cyan", VC = "mediumpurple1", MC = "orange1", SSC = "orchid", HTH = "aquamarine"))

map2 <- pheatmap(assay(vsd)[row.names(sigGenes),], 
           annotation_col = sampleInfo, 
           annotation_colors = my_colour,
           scale = "row",
           color = colorRampPalette(c("navy", "white", "firebrick3"))(50),
           clustering_distance_rows = "euclidean",
           show_rownames = FALSE, 
           show_colnames = FALSE,
           cluster_rows = TRUE,
           cluster_cols =  TRUE,
           cutree_cols = 2)

cluster <- as.data.frame(sort(cutree(map2$tree_row, k=3))) #Export clusters based on heatmap.

#to export figures
#ggsave("fig1.pdf", height = 5, width = 7, unit = "in", dpi = 500)

Another type of visualization option is to plot a heatmap of sample-to-sample distances. A heatmap of this distance matrix gives us an overview over similarities and dissimilarities between samples. We have to provide a hierarchical clustering hc to the heatmap function based on the sample distances, or else the heatmap function would calculate a clustering based on the distances between the rows/columns of the distance matrix.

sampleDists <- dist(t(assay(vsd)))
sampleDistMatrix <- as.matrix(sampleDists)
rownames(sampleDistMatrix) <- paste(vsd$Age, vsd$RealRegion, sep="-")
colnames(sampleDistMatrix) <- NULL
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)

pheatmap(sampleDistMatrix,
         clustering_distance_rows=sampleDists,
         clustering_distance_cols=sampleDists,
         col=colors)

Volcano Plots

The plots below are made using Enhanced Volcano package

EnhancedVolcano(res_CB,
    lab = rownames(res_CB),
    x = 'log2FoldChange',
    y = 'pvalue')

EnhancedVolcano(res_CB,
    lab = rownames(res_CB),
    x = 'log2FoldChange',
    y = 'pvalue',
    title = '4m vs 2y CB',
    pCutoff = 0.01,
    FCcutoff = 0.5,
    pointSize = 3.0,
    labSize = 6.0)

res_CB.df <- merge(res_CB.df, annotation, by = 0)
res_CB.df <- res_CB.df[!is.na(res_CB.df$symbol),]

#Custom shape and color 
keyvals <- ifelse(
           res_CB.df$log2FoldChange < -2.5, 'royalblue',
           ifelse(res_CB.df$log2FoldChange > 2.5, 'gold','black'))

keyvals[is.na(keyvals)] <- 'black'
names(keyvals)[keyvals == 'gold'] <- 'high'
names(keyvals)[keyvals == 'black'] <- 'mid'
names(keyvals)[keyvals == 'royalblue'] <- 'low'

Volcano1 <- EnhancedVolcano(res_CB.df,
            lab = res_CB.df$symbol,
            x = 'log2FoldChange',
            y = 'pvalue',
            selectLab = res_CB.df$symbol[which(names(keyvals) %in% c('high', 'low'))],
            xlab = bquote(~Log[2]~ 'fold change'),
            title = '4m vs 2y CB',
            subtitle = '',
            legendLabels=c('Not sig.',~Log[2]~ 'fold change >|1|','padj<0.01','Both'),
            pCutoff = 0.01,
            FCcutoff = 1.0,
            pointSize = 3.5,
            labSize = 4.5,
            shape = c(6, 4, 2, 11),
            colCustom = keyvals,
            colAlpha = 1,
            legendPosition = 'left',
            legendLabSize = 15,
            legendIconSize = 5.0,
            drawConnectors = TRUE,
            widthConnectors = 1.0,
            colConnectors = 'black',
            arrowheads = FALSE,
            gridlines.major = TRUE,
            gridlines.minor = FALSE,
            border = 'partial',
            borderWidth = 1.5,
            borderColour = 'black')


Volcano2 <- EnhancedVolcano(res_CB.df,
            lab = res_CB.df$symbol,
            x = "log2FoldChange",
            y = "pvalue",
            pCutoff = 0.01,
            FCcutoff = 1.5,
            pointSize = c(ifelse(res_CB.df$log2FoldChange>2, 2, 1)),
            labSize = 2.0,
            shape = c(6, 6, 19, 16),
            title = "DESeq2 results",
            subtitle = "Differential expression",
            caption = bquote(~Log[2]~ "fold change cutoff, 2; p-value cutoff, 0.01"),
            legendPosition = "right",
            legendLabSize = 14,
            legendLabels=c('Not sig.',~Log[2]~ 'fold change >|1|','padj<0.01','Both'),
            col = c("grey30", "forestgreen", "royalblue", "red2"),
            colAlpha = 0.9,
            drawConnectors = TRUE,
            hline = c(10e-8),
            widthConnectors = 0.5)

Volcano1
## Warning: ggrepel: 537 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

Volcano2
## Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please
## use `guide = "none"` instead.
## Warning: ggrepel: 190 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

Plot Individual Counts

You can modify these plots with the usual ggplot2. Below is a just a basic example.

d <- plotCounts(dds, gene=which.min(res_CB$padj), intgroup="AgeRegion", 
                returnData=TRUE)

ggplot(d, aes(x=AgeRegion, y=count)) + 
  geom_point(position=position_jitter(w=0.1,h=0)) + 
  scale_y_log10(breaks=c(25,100,400))

#How to show only a subset of groups
d <- plotCounts(dds[ , dds$AgeRegion %in% c("2y_CB","4m_CB")], gene="ENSMUSG00000002985", intgroup="AgeRegion", returnData=TRUE)

#d$SampleNames <- metadata$simpleID


ggplot(d, aes(x = AgeRegion, y = count, fill = AgeRegion)) +
            geom_boxplot()+
            #geom_point(aes(color = SampleNames), size = 2,position=position_jitter(w = 0.1,h = 0)) +
            #geom_text_repel(aes(label = d$SampleNames)) + 
            ggtitle("RNA: APOE") +
            ylab("Normalized Count (Log10 Scale)")+
            expand_limits(y = 100000)+
            scale_y_continuous(trans = 'log10',
                               expand = expansion(mult = c(.1, 0.1)), labels = comma)+
            scale_fill_manual(values = c( "grey60","darkorange", "black",  "firebrick3"))+
            theme_classic(base_size = 16)+
            theme(plot.title = element_text(hjust = 0.5), axis.text.x=element_text(angle = 45, hjust =1),
                  #axis.title.y=element_text(size=14),
                  axis.title.x=element_blank(),
                  legend.position="right") 

DESeq2 Report Tool

This function creates example graphs and tables with your data.

dir.create("DESeq2Report-example", showWarnings = FALSE, recursive = TRUE)

## Generate the HTML report
report <- DESeq2Report(dge, "DESeq2-example", c("Age", "RealRegion"),
    outdir = "DESeq2Report-example"
)

if (interactive()) {
    ## Browse the report
    browseURL(report)
}
LS0tDQp0aXRsZTogIlJOQS1zZXF1ZW5jaW5nIEFuYWx5c2lzIFR1dG9yaWFsIg0Kc3VidGl0bGU6ICJTYWxtb24gd2l0aCBERVNlcTIiDQphdXRob3I6ICJKaWxseWJldGggQnVyZ2FkbyINCmRhdGU6ICJMYXN0IFVwZGF0ZWQ6IEF1Z3VzdCAyMDIyIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIGNvZGVfZm9sZGluZzogInNob3ciDQogIGdpdGh1Yl9kb2N1bWVudDoNCiAgICBmaWdfd2lkdGg6IDcNCiAgICBmaWdfaGVpZ2h0OiA1DQogICAgZGV2OiAicG5nIg0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDMNCi0tLQ0KIyBJbnRyb2R1Y3Rpb24NClRoZSBwdXJwb3NlIG9mIHRoaXMgdHV0b3JpYWwgaXMgdG8gZ28gb3ZlciBob3cgdG8gaW1wb3J0IGFuZCBzdW1tYXJpemUgdHJhbnNjcmlwdC1sZXZlbCBhYnVuZGFuY2UgZXN0aW1hdGVzIGFuZCBjb21wbGV0ZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAoREUpIGFuYWx5c2lzIHVzaW5nIERFU2VxMi4gSSBhbHNvIGluY2x1ZGUgc29tZSBiYXNpYyBpZGVhcyBmb3IgZG93bnN0cmVhbSBhbmFseXNpcyBhbmQgdmlzdWFsaXppbmcgeW91ciBkYXRhLiBUaGUgb3JpZ2luYWwgYXJ0aWNsZSBkZXNjcmliaW5nIHRoZXNlIG1ldGhvZHMgY2FuIGJlIGZvdW5kIGJlbG93OiANCg0KPiBDaGFybG90dGUgU29uZXNvbiwgTWljaGFlbCBJLiBMb3ZlLCBNYXJrIEQuIFJvYmluc29uICgyMDE1KTogRGlmZmVyZW50aWFsIGFuYWx5c2VzIGZvciBSTkEtc2VxOiB0cmFuc2NyaXB0LWxldmVsIGVzdGltYXRlcyBpbXByb3ZlIGdlbmUtbGV2ZWwgaW5mZXJlbmNlcy4gRjEwMDBSZXNlYXJjaCBodHRwOi8vZHguZG9pLm9yZy8xMC4xMjY4OC9mMTAwMHJlc2VhcmNoLjc1NjMuMQ0KDQoNCioqTm90ZXMqKg0KDQotIElmIHlvdSBhcmUgcHJpbWFyaWx5IGludGVyZXN0ZWQgaW4gaG93IHRvIHVzZSBERVNlcTIgb3IgdmlzdWFsaXphdGlvbnMgb2YgUk5BLXNlcSByZXN1bHRzLCBza2lwIHRvIFBhcnQgMjogRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMgdXNpbmcgREVTZXEyLg0KLSBBbm90aGVyIEJpb2NvbmR1Y3RvciBwYWNrYWdlLCB0eGltZXRhIChMb3ZlIGV0IGFsLiAyMDIwKSwgZXh0ZW5kcyB0eGltcG9ydCwgb2ZmZXJpbmcgdGhlIHNhbWUgZnVuY3Rpb25hbGl0eSwgcGx1cyB0aGUgYWRkaXRpb25hbCBiZW5lZml0IG9mIGF1dG9tYXRpYyBhZGRpdGlvbiBvZiBhbm5vdGF0aW9uIG1ldGFkYXRhIGZvciBjb21tb25seSB1c2VkIHRyYW5zY3JpcHRvbWVzIChHRU5DT0RFLCBFbnNlbWJsLCBSZWZTZXEgZm9yIGh1bWFuIGFuZCBtb3VzZSkuIHR4aW1ldGEgYWxzbyBvZmZlcnMgZWFzeSBjb252ZXJzaW9uIHRvIGRhdGEgb2JqZWN0cyB1c2VkIGJ5IGVkZ2VSIGFuZCBsaW1tYSB3aXRoIHRoZSBtYWtlREdFTGlzdCBmdW5jdGlvbi4NCg0KIyBJbnN0YWxsaW5nIGFuZCBMb2FkaW5nIHBhY2thZ2VzICANClRoZSBmaXJzdCBzdGVwIGlzIHRvIGluc3RhbGwgYW5kIGxvYWQgcmVxdWlyZWQgcGFja2FnZXMuIFZpc2l0IDxodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY29tbXVuaXR5L3R1dG9yaWFscy9yLXBhY2thZ2VzLWd1aWRlPiBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCANCnBhY2thZ2VzIGFuZCBwYWNrYWdlIHJlcG9zaXRvcmllcy4gWW91IG9ubHkgbmVlZCB0byBpbnN0YWxsIHBhY2thZ2VzIG9uY2UgLSBob3dldmVyLCB5b3UgbmVlZCB0byBrZWVwIHRyYWNrIG9mIHZlcnNpb25zIGFuZCB5b3UgbWF5IG5lZWQgdG8gdXBkYXRlIHBhY2thZ2VzIGRlcGVuZGluZyBvbiBuZXcgcmVsZWFzZXMuIEJlbG93IGlzIGFuIGV4YW1wbGUgb2YgaG93IHRvIGluc3RhbGwgYSBwYWNrYWdlIGZyb20gdGhlIEJpb2NvbmR1Y3RvciByZXBvLiANCg0KYGBge3IgZXZhbD1GQUxTRX0gDQoNCiNpbnN0YWxsIHBhY2thZ2VzIA0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBGQUxTRSkpDQogIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJ0eGltcG9ydCIpDQoNCmluc3RhbGwucGFja2FnZXMoInBoZWF0bWFwIikNCg0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9DQojbG9hZCBuZWNlc3NhcnkgcGFja2FnZXMNCmxpYnJhcnkoVHhEYi5NbXVzY3VsdXMuVUNTQy5tbTM5LnJlZkdlbmUpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkodHhpbXBvcnQpDQpsaWJyYXJ5KERFU2VxMikNCmxpYnJhcnkoR2Vub21pY0ZlYXR1cmVzKQ0KbGlicmFyeSh0eGltZXRhKQ0KbGlicmFyeShTdW1tYXJpemVkRXhwZXJpbWVudCkNCmxpYnJhcnkob3JnLk1tLmVnLmRiKQ0KbGlicmFyeShwaGVhdG1hcCkNCmxpYnJhcnkoZ2dyZXBlbCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQ0KbGlicmFyeShiaW9tYVJ0KQ0KbGlicmFyeShlbnJpY2hwbG90KQ0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGdnbmV3c2NhbGUpDQpsaWJyYXJ5KGdncmlkZ2VzKQ0KbGlicmFyeShNM0MpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkocGF0aHZpZXcpDQpsaWJyYXJ5KHJXaWtpUGF0aHdheXMpDQpsaWJyYXJ5KFJDeTMpDQpsaWJyYXJ5KFVwU2V0UikNCmxpYnJhcnkoUmVhY3RvbWVQQSkNCmxpYnJhcnkoZ2d1cHNldCkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQoNCmBgYA0KDQojIFRyYW5zY3JpcHQgcXVhbnRpZmljYXRpb24gZm9yIGdlbmUtbGV2ZWwgYW5hbHlzaXMNCg0KWW91IHdpbGwgbmVlZCBhIG1ldGFkYXRhIGZpbGUgdGhhdCBpbmNsdWRlcyB5b3VyIHNhbXBsZSBuYW1lcyBhcyB3ZWxsIGFzIGFueSBncm91cC9leHBsYW5hdG9yeSB2YXJpYWJsZXMuIFlvdXIgc2FtcGxlIG5hbWVzIG5lZWQgdG8gZXhhY3RseSBtYXRjaCB5b3VyIGZpbGUgbmFtZXMgKGZyb20gU2FsbW9uKSwgMSBzYW1wbGUgcGVyIHJvdy4gSSByZWNvbW1lbmQgZXhwb3J0aW5nIHRoZSBuYW1lcy50eHQgZmlsZSB5b3UgY3JlYXRlZCB3aGVuIGRvaW5nIHRoZSBTYWxtb24gYW5hbHlzaXMgb24gdGhlIHNlcnZlci4gDQoNCmBgYHtyfQ0KbWV0YWRhdGEgPC0gcmVhZC5jc3YoIk1hdHRBZ2luZ0FzdHJvY3l0ZV9NZXRhZGF0YTIuY3N2IiwgaGVhZGVyID0gVFJVRSkNCm1ldGFkYXRhJEFnZVJlZ2lvbiA8LSBhcy5jaGFyYWN0ZXIocGFzdGUobWV0YWRhdGEkQWdlLCBtZXRhZGF0YSRSZWFsUmVnaW9uLCBzZXA9ICJfIikpDQoNCmBgYA0KDQojIyBJbXBvcnQgYW5kIFN1bW1hcml6ZSB0cmFuc2NyaXB0IHF1YW50aWZpY2F0aW9uIGZpbGVzDQoNClRoZSB0eGltZXRhIHBhY2thZ2UgaGFzIGEgc2luZ2xlIGZ1bmN0aW9uIGZvciBpbXBvcnRpbmcgdHJhbnNjcmlwdC1sZXZlbCBlc3RpbWF0ZXMuIFRoZSB0eXBlIGFyZ3VtZW50IGlzIHVzZWQgdG8gc3BlY2lmeSB3aGF0IHNvZnR3YXJlIHdhcyB1c2VkIGZvciBlc3RpbWF0aW9uLiBBIHNpbXBsZSBsaXN0IHdpdGggbWF0cmljZXMsICJhYnVuZGFuY2UiLCAiY291bnRzIiwgYW5kICJsZW5ndGgiLCBpcyByZXR1cm5lZCwgd2hlcmUgdGhlIHRyYW5zY3JpcHQgbGV2ZWwgaW5mb3JtYXRpb24gaXMgc3VtbWFyaXplZCB0byB0aGUgZ2VuZS1sZXZlbC4gVHlwaWNhbGx5LCBhYnVuZGFuY2UgaXMgcHJvdmlkZWQgYnkgdGhlIHF1YW50aWZpY2F0aW9uIHRvb2xzIGFzIFRQTSAodHJhbnNjcmlwdHMtcGVyLW1pbGxpb24pLCB3aGlsZSB0aGUgY291bnRzIGFyZSBlc3RpbWF0ZWQgY291bnRzIChwb3NzaWJseSBmcmFjdGlvbmFsKSwgYW5kIHRoZSAibGVuZ3RoIiBtYXRyaXggY29udGFpbnMgdGhlIGVmZmVjdGl2ZSBnZW5lIGxlbmd0aHMuIA0KDQpgYGB7cn0NCiNGaXJzdCwgY3JlYXRlIGEgbGlzdCwgY2FsbGVkIGZpbGVzLCB3aXRoIHRoZSBmaWxlIHBhdGggdG8gdGhlIHF1YW50LnNmIChTYWxtb24gb3V0cHV0KSBmaWxlcyBmb3IgZWFjaCBzYW1wbGUNCmZpbGVzIDwtIGZpbGUucGF0aCgiQWdpbmdBc3Ryb2N5dGVUcmFuc2NyaXB0b21lX21tMzlvdXQiLCBtZXRhZGF0YSRGdWxsU2FtcGxlTmFtZSwgInF1YW50LnNmIikNCg0KI0NoZWNrIHRvIG1ha2Ugc3VyZSBhbGwgdGhlIGZpbGVzIGFyZSBhY3R1YWxseSBsb2NhdGVkIGluIHRoZSByaWdodCBwbGFjZQ0KYWxsKGZpbGUuZXhpc3RzKGZpbGVzKSkgI1RoaXMgc2hvdWxkIG91dHB1dCAiVFJVRSINCg0KI2NyZWF0ZSBhIGRhdGFmcmFtZSBjb250YWluaW5nIHRob3NlIGZpbGVwYXRocywgdGhlIG5hbWVzIG9mIHRoZSBzYW1wbGVzLCBhbmQgYW55IG90aGVyIG1ldGFkYXRhIHlvdSB3b3VsZCBsaWtlIHRvIGluY2x1ZGUuIA0KI0kgaW5jbHVkZSBhbGwgb2YgdGhlIG1ldGFkYXRhIChmcm9tIHRoZSBtZXRhZGF0YSBmaWxlKQ0KY29sZGF0YSA8LSBkYXRhLmZyYW1lKGZpbGVzLCBuYW1lcyA9IG1ldGFkYXRhJEZ1bGxTYW1wbGVOYW1lLCBtZXRhZGF0YSkNCnNlIDwtIHR4aW1ldGEoY29sZGF0YSwgdHlwZT0ic2FsbW9uIikNCg0KI0NvZGUgZXhhbXBsZXMgdG8gdmlldyBkaWZmZXJlbnQgb3V0cHV0cyBmcm9tIHRoZSBzdW1tYXJpemVkIGV4cGVyaW1lbnQgKHNlKQ0KI2Fzc2F5TmFtZXMoc2UpDQojcm93UmFuZ2VzKHNlKQ0KI3NlcWluZm8oc2UpDQojc2UuZXhvbnMgPC0gYWRkRXhvbnMoc2UpDQojdHJhbnNjcmlwdCA8LSBhcy5kYXRhLmZyYW1lKGFzc2F5KHNlKSkNCg0KI1N1bW1hcml6ZSB0byBnZW5lcw0KZ3NlIDwtIHN1bW1hcml6ZVRvR2VuZShzZSkNCiNnZW5lcyA8LSBhcy5kYXRhLmZyYW1lKGFzc2F5KGdzZSkpDQojWW91IGNhbiBhZGQgb3RoZXIgbWV0YWRhdGEgdG8gdGhlIGdlbmUgc3VtbWFyaXplZCBleHBlcmltZW50IG9iamVjdCAoZ3NlKSB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGUNCmNvbHVtbnMob3JnLk1tLmVnLmRiKQ0KI2dzZSA8LSBhZGRJZHMoZ3NlLCAiU1lNQk9MIiwgZ2VuZT1UUlVFKQ0KI2dzZSA8LSBhZGRJZHMoZ3NlLCAiRU5UUkVaSUQiLCBnZW5lPVRSVUUpDQojZ3NlIDwtIGFkZElkcyhnc2UsICJFTlNFTUJMIiwgZ2VuZT1UUlVFKQ0KYGBgDQoNCiMjIENyZWF0ZSB0cG0gdGFibGUgYW5kIGFkZCBnZW5lIGFubm90YXRpb25zDQoNCmBgYHtyfQ0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKG1jb2xzKGdzZSkpDQoNCmBgYA0KDQoNCmBgYHtyIGV2YWwgPSBGQUxTRX0NCmFubm90YXRpb24gPC0gYXMuZGF0YS5mcmFtZShtY29scyhnc2UpKQ0KDQptYXJ0IDwtIGJpb21hUnQ6OnVzZURhdGFzZXQoIm1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiLCB1c2VNYXJ0KCJlbnNlbWJsIikpICNmb3IgZGVmYXVsdCBtb3VzZSBnZW5vbWUNCg0KZ2VuZV9saXN0IDwtIGdldEJNKGZpbHRlcnM9ICJlbnNlbWJsX2dlbmVfaWQiLA0KICAgICAgICAgICAgICAgICAgIGF0dHJpYnV0ZXM9YygiZGVzY3JpcHRpb24iLCJlbnNlbWJsX2dlbmVfaWQiKSwNCiAgICAgICAgICAgICAgICAgICB2YWx1ZXM9IGFubm90YXRpb24kRU5TRU1CTCwNCiAgICAgICAgICAgICAgICAgICBtYXJ0PSBtYXJ0LA0KICAgICAgICAgICAgICAgICAgIHVzZUNhY2hlID0gRkFMU0UpDQoNCmFubm90YXRpb24gPC1tZXJnZShhbm5vdGF0aW9uLCBnZW5lX2xpc3QsIGJ5LnggPSBjKCdFTlNFTUJMJyksIGJ5LnkgPSBjKCdlbnNlbWJsX2dlbmVfaWQnKSkNCg0KdHBtIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkoZ3NlLCAiYWJ1bmRhbmNlIikpDQoNCiNUbyBjaGFuZ2UgdGhlIGNvbHVtbiBuYW1lcyBiYXNlZCBvbiBzYW1wbGUgbmFtZXMgaW4gbWV0YWRhdGENCiNjb2xuYW1lcyh0cG0pIDwtIG1ldGFkYXRhJFByZXBJRA0KDQp0cG0gPC0gbWVyZ2UodHBtLCBhbm5vdGF0aW9uLCBieS54ID0gMCwgYnkueSA9ICJnZW5lX2lkIikNCnRwbSA8LSBzZWxlY3QodHBtLCAtMSkNCnJvd25hbWVzKHRwbSkgPC0gdHBtJEVOU0VNQkwNCg0KdHBtX2dlbmVPbmx5IDwtIGZpbHRlcih0cG0sICFFTlRSRVpJRCA9PSAiTkEiKQ0KDQpgYGANCg0KIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBBbmFseXNpcyB1c2luZyBERVNlcTINCg0KQ2hlY2sgb3V0IFt0aGlzIHZpZ25ldHRlXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkgZm9yIGRldGFpbGVkIGluZm9ybWF0aW9uIHJlZ2FyZGluZyB0aGUgREVTZXEyIHBhY2thZ2UuIA0KQ2hlY2sgb3V0IFt0aGlzIG90aGVyIHZpZ25ldHRlXShodHRwczovL3JzdHVkaW8tcHVicy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS8zMjkwMjdfNTkzMDQ2ZmI2ZDdhNDI3ZGE2YjJjNTM4Y2FmNjAxZTEuaHRtbCkgZm9yIGluZm9ybWF0aW9uIGFib3V0IG11bHRpLWdyb3VwIGNvbXBhcmlzb25zIChlLmcgU2V4ICsgR2Vub3R5cGUgKyBBZ2UgYW5kIGludGVyYWN0aW9uIHRlcm1zKS4NCg0KREVTZXEyIHRha2VzIGluIGEgY291bnRzIG1hdHJpeCAtIHRoZSBudW1iZXIgb2Ygc2VxdWVuY2UgZnJhZ21lbnRzIHRoYXQgaGF2ZSBiZWVuIGFzc2lnbmVkIHRvIGVhY2ggZ2VuZS4gVG8gc3RhdGlzdGljYWwgZGV0ZXJtaW5lIHdoaWNoIGdlbmVzIGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMsIHdlIG5lZWQgdG8gZGV0ZXJtaW5lIHRoZSBjaGFuZ2VzIGJldHdlZW4gb3VyIGNvbmRpdGlvbnMgY29tcGFyZWQgdG8gdGhlIHdpdGhpbi1jb25kaXRpb24gdmFyaWFiaWxpdHkuIERFU2VxMiB1c2VzIGEgbmVnYXRpdmUgYmlub21pYWwgZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVsLiANClRoZSBub3JtYWxpemVkIGNvdW50cyBwcm92aWRlZCBieSBERVNlcTIgYXJlIGNhbGxlZCAqbWVkaWFuIG9mIHJhdGlvcyouIFlvdSBzaG91bGQgbm90IHVzZSB0aGVzZSBub3JtYWxpemVkIGNvdW50cyBmb3Igd2l0aGluIHNhbXBsZSBjb21wYXJpc29uIC0gVGhleSBhcmUgcHJpbWFyaWx5IHVzZWQgZm9yIERFIGFuYWx5c2lzLiANCg0KRmlyc3QgeW91IGNyZWF0ZSBhIERFU2VxRGF0YVNldCAoZGRzKSBvYmplY3QgKGNvbnRhaW5zIGRhdGEgKyBtZXRhZGF0YSkgYW5kIHRoZW4gcnVuIERFIGFuYWx5c2lzLiANCkluIG15IGV4YW1wbGUgZGF0YSwgYXN0cm9jeXRlIG1STkEgd2FzIHNlcXVlbmNlZCBmcm9tIDQtbW9udGggYW5kIDIteWVhciBvbGQgbWFsZSBtaWNlLCBmcm9tIDUgZGlmZmVyZW50IGJyYWluIHJlZ2lvbnMsIHVzaW5nIHRoZSBSaWJvdGFnIHN5c3RlbSBbb3JpZ2luYWwgcGFwZXJdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzU3ODMyMDAvKS4gDQoNCioqTm90ZSoqIENoZWNrIG91dCB0aGUgdHV0b3JpYWwgYWJvdmUgZm9yIGluZm9ybWF0aW9uIHJlZ2FyZGluZyBtdWx0aXBsZSBjb25kaXRpb25zIGFuZCBpbnRlcmFjdGlvbiB0ZXJtcy4gSW4gdGhpcyBjYXNlLCBJIHdvdWxkIGFjdHVhbGx5IG1ha2UgYSBuZXcgdmFyaWFibGUgY29tYmluaW5nIGFnZSBhbmQgcmVnaW9uLCB0byBrZWVwIHRoaW5ncyBzaW1wbGUuIFRoZW4gSSB3b3VsZCB1c2UgdGhhdCBuZXcgdmFyaWFibGUgaW4gbXkgZGVzaWduIChpbnN0ZWFkIG9mICpBZ2UqIGFzIHNob3duIGJlbG93OyBzZWUgdGhlIDJuZCBjb2RlIGNodW5rIGZvciBob3cgdG8gZG8gdGhpcykuDQoNCmBgYHtyIGV2YWwgPSBGQUxTRX0NCmRkcyA8LSBERVNlcURhdGFTZXQoZ3NlLCBkZXNpZ24gPSB+IFJlYWxSZWdpb24gKyBBZ2UpICN0aGUgfiBhcmUgeW91ciBkZXNpZ24gdmFyaWFibGVzLiBJZiB5b3Ugd291bGQgbGlrZSB0byB0ZXN0IGZvciBhbiBpbnRlcmFjdGlvbiB5b3Ugd291bGQgZG8gfkFnZSArIFJlYWxSZWdpb24gKyBBZ2U6UmVhbFJlZ2lvbg0KDQpkZHMkQWdlIDwtIHJlbGV2ZWwoZGRzJEFnZSwgcmVmID0gIjRtIikgI1NldCBhIGdyb3VwIGFzIHJlZmVyZW5jZTsgZGVmYXVsdCBpcyBhbHBoYWJldGljYWwNCg0KZGdlIDwtIERFU2VxKGRkcykgI3J1biBEaWZmIGdlbmUgZXhwIGFuYWx5c2lzDQoNCnJvdW5kKGNvbFN1bXMoY291bnRzKGRkcykpLzFlNikgI251bWJlciBvZiByZWFkcyBwZXIgc2FtcGxlDQpgYGANCg0KIyMgREVTZXEyIHdpdGggU1RBUiBDb3VudHMgVGFibGUgKG9yIG90aGVyIHRhYmxlKSANCg0KSWYgeW91IGFscmVhZHkgaGF2ZSBhIGNvdW50cyB0YWJsZSAoTVVTVCBiZSByYXcgY291bnRzLCB1bi1ub3JtYWxpemVkKSwgdGhlbiB5b3UgY2FuIGNyZWF0ZSB0aGUgZGRzIG9iamVjdCB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZTogDQoNCipOb3RlKiBZb3UgbWF5IG5lZWQgdG8gY2xlYW4gdXAgeW91ciBjb3VudHMgbWF0cml4IGJlZm9yZSBydW5uaW5nIHRoZSBERVNlcTIgYW5hbHlzaXMNCg0KYGBge3IgZXZhbCA9IEZBTFNFfQ0KI0NvZGUgaWYgY291bnRzIG1hdHJpeCAoaS5lLiBmcm9tIFNUQVIpIGlzIHRoZSBpbnB1dCBpbnN0ZWFkIG9mIHR4aSBmcm9tIFNhbG1vbg0KZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gY291bnRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IG1ldGFkYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfiBHcm91cCkNCmBgYA0KDQojIyBIZXJlIEkgYW0gY3JlYXRpbmcgYSBuZXcgdmFyYWlibGUgY2FsbGVkICpBZ2VSZWdpb24qIHRoYXQgSSB3aWxsIHVzZSBhcyBteSB2YXJpYWJsZSBvZiBpbnRlcmVzdA0KDQpgYGB7cn0NCmRkcyA8LSBERVNlcURhdGFTZXQoZ3NlLCBkZXNpZ24gPSB+IEFnZVJlZ2lvbikNCg0KI2RkcyRBZ2VSZWdpb24gPC0gcmVsZXZlbChkZHMkQWdlUmVnaW9uLCByZWYgPSAiNG1fQ0IiKSAjU2V0IGEgZ3JvdXAgYXMgcmVmZXJlbmNlOyBkZWZhdWx0IGlzIGFscGhhYmV0aWNhbA0KDQpkZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZHMpDQpjb3VudHMgPC0gYXMuZGF0YS5mcmFtZShjb3VudHMoZGRzLCBub3JtYWxpemVkID0gVFJVRSkpDQoNCmtlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzKSkgPj0gMTANCmRkcyA8LSBkZHNba2VlcCxdDQoNCmRkcyA8LSBERVNlcShkZHMpDQpgYGANCg0KIyMgVG8gY2hlY2sgZm9yIG91dGxpZXJzOiANCg0KYGBge3J9DQpwYXIobWFyPWMoOCw1LDIsMikpDQpib3hwbG90KGxvZzEwKGFzc2F5cyhkZHMpW1siY29va3MiXV0pLCByYW5nZT0wLCBsYXM9MikNCmBgYA0KDQoNCiMjIEV4dHJhY3RpbmcgUmVzdWx0cyBmcm9tIERFIEFuYWx5c2lzIA0KDQpgYGB7cn0NCnJlc3VsdHNOYW1lcyhkZHMpICN2aWV3IG5hbWVzIGZvciB0aGUgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyB5b3UgdGVzdGVkIGZvciAtIGJ1dCBvbmx5IGZvciB0aGUgb25lcyBhZ2FpbnN0IHJlZmVyZW5jZSBsZXZlbC4gU2luY2Ugd2UgaGF2ZSBtdWx0aXBsZSBncm91cHMgKGFuZCBubyBzaW5nbGUgYmFzZWxpbmUvY29udHJvbCksIHVzZSB0aGUgY29udHJhc3Qgb3B0aW9uICgybmQgb3B0aW9uIGJlbG93KQ0KDQpzdW1tYXJ5KHJlc3VsdHMoZGRzLCBuYW1lID0gIkFnZVJlZ2lvbl80bV9WQ192c18yeV9DQiIpKSAjdGhpcyBvcHRpb24gd29uJ3QgZ2l2ZSB5b3UgYWxsIG9mIHlvdXIgcG9zc2libGUgY29tcGFyaXNvbnMsIGp1c3QgdGhvc2UgYWdhaW5zdCB5b3VyIHJlZmVyZW5jZSBsZXZlbA0KDQpzdW1tYXJ5KHJlc3VsdHMoZGRzLCBjb250cmFzdCA9YygiQWdlUmVnaW9uIiwgIjJ5X1ZDIiwiNG1fVkMiKSkpICN1c2UgdGhpcyB0byBvYnRhaW4gYWxsIHBvc3NpYmxlIGNvbXBhcmlzb25zDQoNCg0KDQpyZXNfVkMgPC0gcmVzdWx0cyhkZHMsIGNvbnRyYXN0ID1jKCJBZ2VSZWdpb24iLCAiMnlfVkMiLCI0bV9WQyIpKSAjc2VsZWN0IHJlc3VsdHMgYXMgdGFibGUsIG5lZWRlZCBmb3IgdmlzdWFsaXphdGlvbnMgYmVsb3cNCnJlc19NQyA8LSByZXN1bHRzKGRkcywgY29udHJhc3QgPWMoIkFnZVJlZ2lvbiIsICIyeV9NQyIsIjRtX01DIikpIA0KcmVzX0hUSCA8LSByZXN1bHRzKGRkcywgY29udHJhc3QgPWMoIkFnZVJlZ2lvbiIsICIyeV9IVEgiLCI0bV9IVEgiKSkgDQpyZXNfU1NDIDwtIHJlc3VsdHMoZGRzLCBjb250cmFzdCA9YygiQWdlUmVnaW9uIiwgIjJ5X1NTQyIsIjRtX1NTQyIpKSANCnJlc19DQiA8LSByZXN1bHRzKGRkcywgY29udHJhc3QgPWMoIkFnZVJlZ2lvbiIsICIyeV9DQiIsIjRtX0NCIikpIA0KDQojbWFrZSBkYXRhZnJhbWVzIG9mIGFsbCBjb250cmFzdHMNCnJlc19WQy5kZiA8LSBhcy5kYXRhLmZyYW1lKHJlc19WQykgDQpyZXNfTUMuZGYgPC0gYXMuZGF0YS5mcmFtZShyZXNfTUMpDQpyZXNfQ0IuZGYgPC0gYXMuZGF0YS5mcmFtZShyZXNfQ0IpDQpyZXNfSFRILmRmIDwtIGFzLmRhdGEuZnJhbWUocmVzX0hUSCkNCnJlc19TU0MuZGYgPC0gYXMuZGF0YS5mcmFtZShyZXNfU1NDKQ0KDQojYWxsIGluIG9uZSBzdGVwDQojcmVzX0NCIDwtIGFzLmRhdGEuZnJhbWUocmVzdWx0cyhkZHMsIGNvbnRyYXN0ID1jKCJBZ2VSZWdpb24iLCAiMnlfQ0IiLCI0bV9DQiIpKSkNCg0KI3dyaXRlLmNzdihyZXNfVkMuZGYsICJBZ2luZ0FzdHJvVHJhbnNjcmlwdG9tZV9SZXN1bHRzX1ZDLmNzdiIsIHJvdy5uYW1lcyA9IFRSVUUpICN0byBleHBvcnQgYXMgY3N2IGZpbGUNCg0KYGBgDQoNCg0KIyMgVmlzdWFsaXphdGlvbnMgDQoNCiMjIyBNQSBQbG90DQp0aGUgZnVuY3Rpb24gcGxvdE1BIHNob3dzIHRoZSBsb2cyIGZvbGQgY2hhbmdlcyBhdHRyaWJ1dGFibGUgdG8gYSBnaXZlbiB2YXJpYWJsZSBvdmVyIHRoZSBtZWFuIG9mIG5vcm1hbGl6ZWQgY291bnRzIGZvciBhbGwgdGhlIHNhbXBsZXMgaW4gdGhlIERFU2VxRGF0YVNldC4NCmBgYHtyfQ0KDQpwbG90TUEocmVzX0NCLCB5bGltPWMoLTIsMikpDQphYmxpbmUoaD1jKC0xLDEpLCBjb2w9ImRvZGdlcmJsdWUiLCBsd2Q9MikgI2FkZCBob3Jpem9udGFsIGxpbmUNCg0KcGFyKG1mcm93PWMoMSw0KSwgbWFyPWMoMiwyLDIsMikpDQpwbG90TUEocmVzX1ZDLCB5bGltPWMoLTIsMiksIG1haW4gPSAiVkMiKQ0KcGxvdE1BKHJlc19NQywgeWxpbT1jKC0yLDIpLCBtYWluID0gIk1DIikNCnBsb3RNQShyZXNfSFRILCB5bGltPWMoLTIsMiksIG1haW4gPSAiSFRIIikNCnBsb3RNQShyZXNfQ0IsIHlsaW09YygtMiwyKSwgbWFpbiA9ICJDQiIpDQoNCmBgYA0KIyMjIFBDQQ0KDQpUaGVyZSBhcmUgc2V2ZXJhbCBkaWZmZXJlbnQgb3B0aW9ucyBmb3IgbWFraW5nIFBDQSBwbG90cyBpbiBSLiBIZXJlIEkgc2hvdyBhbiBleGFtcGxlIHdpdGggcGxvdFBDQSBhbmQgZ2dwbG90LiBUaGUgb3RoZXIgcG9wdWxhciBvcHRpb24gaXMgW1BDQXRvb2xzXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvUENBdG9vbHMvaW5zdC9kb2MvUENBdG9vbHMuaHRtbCkgDQoNCmBgYHtyfQ0KDQp2c2QgPC0gdnN0KGRkcykgIzFzdCB5b3UgbmVlZCB0byB0cmFuc2Zvcm0gZGF0YS4gSSB1c2UgdmFyaWFuY2Ugc3RhYmlsaXphdGlvbiB0cmFuc2Zvcm1hdGlvbg0KDQpkYXRhIDwtIHBsb3RQQ0EodnNkLCBpbnRncm91cCA9ICJBZ2UiLCByZXR1cm5EYXRhID0gVFJVRSkNCg0KcGVyY2VudF92YXIgPC0gcm91bmQoMTAwICogYXR0cihkYXRhLCAicGVyY2VudFZhciIpKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG9yID0gbWV0YWRhdGEkUmVhbFJlZ2lvbiwgc2hhcGUgPSBtZXRhZGF0YSRBZ2UpKSArIA0KICBnZW9tX3BvaW50KHNpemUgPSA1KSArDQogIHhsYWIocGFzdGUoIlBDMTogIiwgcGVyY2VudF92YXJbMV0sICIldmFyaWFuY2UiKSkgKw0KICB5bGFiKHBhc3RlKCJQQzI6ICIsIHBlcmNlbnRfdmFyWzJdLCAiJXZhcmlhbmNlIikpICsgDQogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpICsgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSANCg0KDQpgYGANCg0KSGVyZSBpcyBhbm90aGVyIFBDQSBwbG90LCBidXQgd2l0aCBhIGZldyBtb3JlIG1vZGlmaWNhdGlvbnMuIFRoZSBjb21tZW50ZWQgbGluZXMgYXJlIG5vdCBpbmNsdWRlZCBpbiB0aGUgY29kZSB0byBnZW5lcmF0ZSB0aGUgcGxvdCAoaW5jbHVkZWQgaGVyZSBhcyBhbiBleGFtcGxlLCBpZiB5b3Ugd2FudGVkIHRvIGFkZCBsYWJlbHMgdG8gZWFjaCBwb2ludCkuDQoNCmBgYHtyfQ0KcGNhIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgDQogICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IG1ldGFkYXRhJEFnZSwgc2hhcGUgPSBtZXRhZGF0YSRSZWFsUmVnaW9uKSkrIA0KICAgICAgZ2VvbV9wb2ludChzaXplID0gMykgKyANCiAgICAgICMgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9ICdsYWJlbG5hbWUnLA0KICAgICAgIyAgICAgICAgICAgICAgICAgbnVkZ2VfeCA9IC41LCBudWRnZV95ID0gMC41LA0KICAgICAgIyAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAyLA0KICAgICAgIyAgICAgICAgICAgICAgICAgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsDQogICAgICAjICAgICAgICAgICAgICAgICBzaXplID0gNCkgKw0KICAgICAgeGxhYihwYXN0ZSgiUEMxOiAiLCBwZXJjZW50X3ZhclsxXSwgIiV2YXJpYW5jZSIpKSArDQogICAgICB5bGFiKHBhc3RlKCJQQzI6ICIsIHBlcmNlbnRfdmFyWzJdLCAiJXZhcmlhbmNlIikpICsgDQogICAgICBsYWJzKHNoYXBlID0gIkJyYWluIFJlZ2lvbiIsIGNvbG9yID0gIkFnZSIpICsNCiAgICAgIHNjYWxlX3NoYXBlX2Rpc2NyZXRlKGxhYmVscz1jKCJDZXJlYmVsbHVtIiwgIkh5cG90aGFsYW11cyIsICJNb3RvciBDb3J0ZXgiLCAgICJTb21hdG9zZW5zb3J5IiwgIlZpc3VhbCBDb3J0ZXgiKSkgKw0KICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUobGFiZWxzPWMoIjIgeWVhciBvbGQiLCAiNCBtb250aCBvbGQiKSkgKw0KICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRlZXBwaW5rMSIsImRlZXBza3libHVlMSIpKSsNCiAgICAgIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTIpICArICAgIA0KICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZT0xNCwgZmFjZT0iYm9sZCIsIG1hcmdpbiA9IG1hcmdpbigwLDAsMTAsMCkpKSArDQogICAgICBnZ3RpdGxlKCJQQ0E6IEFnaW5nIEFzdHJvY3l0ZSBUcmFuc2NyaXB0b21lIikNCiAgICAgIA0KcGNhDQoNCiNDb2RlIHRvIGNhbGN1bGF0ZSBlbGxpcHNlcyBhcm91bmQgZGF0YSAtIG5vdCBwb3NzaWJsZSB3aXRoIHRoaXMgZGF0YXNldCBkdWUgdG8gbG93IHNhbXBsZSBzaXplIHBlciBncm91cA0KIyBwY2EgKyBzdGF0X2VsbGlwc2UoYWVzKGdyb3VwID0gbWV0YWRhdGEkUmVnaW9uKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAidCIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsd2QgPSAxKQ0KDQpgYGANCg0KDQoNCiMjIyBIZWF0bWFwcw0KDQpUaGVyZSBhcmUgc2V2ZXJhbCBvcHRpb25zIGZvciBoZWF0bWFwcyBhcyB3ZWxsLiBJIGxpa2UgdG8gdXNlIFtwaGVhdG1hcF0oaHR0cHM6Ly9kYXZldGFuZy5vcmcvbXVzZS8yMDE4LzA1LzE1L21ha2luZy1hLWhlYXRtYXAtaW4tci13aXRoLXRoZS1waGVhdG1hcC1wYWNrYWdlLykgYmVjYXVzZSB0aGUgZGVmYXVsdCBvcHRpb25zIGFyZSBwcmV0dHkgZ29vZC4gWW91IGNhbiBwbG90IGEgaGVhdG1hcCB3aXRoIGFsbCB5b3VyIGdlbmVzLCBidXQgaXQgdGFrZXMgYSBsb25nIHRpbWUgYW5kIGlzIG5vdCBzdXBlciBpbmZvcm1hdGl2ZS4gQWx0ZXJuYXRpdmVseSwgdHJ5IHBsb3R0aW5nIGEgaGVhdG1hcCBvZiB0aGUgbW9zdCB2YXJpYWJsZSBnZW5lcyBvciBhIHN1YnNldCBvZiBnZW5lcyBvZiBpbnRlcmVzdCAoZXhhbXBsZXMgc2hvd24gYmVsb3cpLiBUaGUgY29sb3Igc2NhbGUgaXMgYmFzZWQgb24gWi1zY29yZXMgb2YgdGhlIHRyYW5zZm9ybWVkIGNvdW50cyBtYXRyaXguIA0KDQpgYGB7cn0NCiNFeGFtcGxlIDE6IFNlbGVjdGluZyBtb3N0IHZhcmlhYmxlIGdlbmVzDQp0b3BWYXJHZW5lcyA8LSBoZWFkKG9yZGVyKC1yb3dWYXJzKGFzc2F5KHZzZCkpKSwxMDApICMgc2VsZWN0IHRvcCAxMDAgbW9zdCB2YXJpYWJsZSBnZW5lcyAoYWNyb3NzIGFsbCBzYW1wbGVzKQ0KDQpwaGVhdG1hcChhc3NheSh2c2QpW3RvcFZhckdlbmVzLF0pICNiYXNpYyBoZWF0bWFwDQoNCnNhbXBsZUluZm8gPC0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKGRkcylbLGMoIkFnZSIsIlJlYWxSZWdpb24iKV0pICNpbmNsdWRlIGFubm90YXRpb25zIGZvciBwbG90IGJhc2VkIG9uIG1ldGFkYXRhDQoNCm1hcDEgPC0gcGhlYXRtYXAoYXNzYXkodnNkKVt0b3BWYXJHZW5lcyxdLCANCiAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBzYW1wbGVJbmZvLA0KICAgICAgICAgICBzY2FsZSA9ICJyb3ciLA0KICAgICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygibmF2eSIsICJ3aGl0ZSIsICJmaXJlYnJpY2szIikpKDUwKSwNCiAgICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImV1Y2xpZGVhbiIsDQogICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGQUxTRSwgDQogICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwNCiAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gIEZBTFNFKQ0KDQojRXhhbXBsZSAyOiBTZWxlY3Rpbmcgc2lnbmlmaWNhbnQgREVHcyBmb3IganVzdCBDZXJlYmVsbHVtIDJ5IHZzIDRtIA0Kc2lnR2VuZXMgPC0gc3Vic2V0KGFzLmRhdGEuZnJhbWUocmVzX0NCKSwgcGFkaiA8PTAuMDEpIA0Kc2lnR2VuZXMgPC0gc3Vic2V0KHNpZ0dlbmVzLCBhYnMobG9nMkZvbGRDaGFuZ2UpID4gMSkNCg0KI0NyZWF0ZSBjdXN0b20gY29sb3JzIGZvciB5b3VyIGxhYmVscyANCm15X2NvbG91ciA9IGxpc3QoDQogIEFnZSA9IGMoIjJ5IiA9ICJzdGVlbGJsdWUyIiwgIjRtIiA9ICJicm93bjIiKSwNCiAgUmVhbFJlZ2lvbiA9IGMoIENCID0gImN5YW4iLCBWQyA9ICJtZWRpdW1wdXJwbGUxIiwgTUMgPSAib3JhbmdlMSIsIFNTQyA9ICJvcmNoaWQiLCBIVEggPSAiYXF1YW1hcmluZSIpKQ0KDQptYXAyIDwtIHBoZWF0bWFwKGFzc2F5KHZzZClbcm93Lm5hbWVzKHNpZ0dlbmVzKSxdLCANCiAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBzYW1wbGVJbmZvLCANCiAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBteV9jb2xvdXIsDQogICAgICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJuYXZ5IiwgIndoaXRlIiwgImZpcmVicmljazMiKSkoNTApLA0KICAgICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIiwNCiAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLCANCiAgICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEZBTFNFLA0KICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLA0KICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSAgVFJVRSwNCiAgICAgICAgICAgY3V0cmVlX2NvbHMgPSAyKQ0KDQoNCmNsdXN0ZXIgPC0gYXMuZGF0YS5mcmFtZShzb3J0KGN1dHJlZShtYXAyJHRyZWVfcm93LCBrPTMpKSkgI0V4cG9ydCBjbHVzdGVycyBiYXNlZCBvbiBoZWF0bWFwLg0KDQojdG8gZXhwb3J0IGZpZ3VyZXMNCiNnZ3NhdmUoImZpZzEucGRmIiwgaGVpZ2h0ID0gNSwgd2lkdGggPSA3LCB1bml0ID0gImluIiwgZHBpID0gNTAwKQ0KYGBgDQoNCkFub3RoZXIgdHlwZSBvZiB2aXN1YWxpemF0aW9uIG9wdGlvbiBpcyB0byBwbG90IGEgaGVhdG1hcCBvZiBzYW1wbGUtdG8tc2FtcGxlIGRpc3RhbmNlcy4gDQpBIGhlYXRtYXAgb2YgdGhpcyBkaXN0YW5jZSBtYXRyaXggZ2l2ZXMgdXMgYW4gb3ZlcnZpZXcgb3ZlciBzaW1pbGFyaXRpZXMgYW5kIGRpc3NpbWlsYXJpdGllcyBiZXR3ZWVuIHNhbXBsZXMuIFdlIGhhdmUgdG8gcHJvdmlkZSBhIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGhjIHRvIHRoZSBoZWF0bWFwIGZ1bmN0aW9uIGJhc2VkIG9uIHRoZSBzYW1wbGUgZGlzdGFuY2VzLCBvciBlbHNlIHRoZSBoZWF0bWFwIGZ1bmN0aW9uIHdvdWxkIGNhbGN1bGF0ZSBhIGNsdXN0ZXJpbmcgYmFzZWQgb24gdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIHRoZSByb3dzL2NvbHVtbnMgb2YgdGhlIGRpc3RhbmNlIG1hdHJpeC4NCg0KYGBge3J9DQoNCnNhbXBsZURpc3RzIDwtIGRpc3QodChhc3NheSh2c2QpKSkNCnNhbXBsZURpc3RNYXRyaXggPC0gYXMubWF0cml4KHNhbXBsZURpc3RzKQ0Kcm93bmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gcGFzdGUodnNkJEFnZSwgdnNkJFJlYWxSZWdpb24sIHNlcD0iLSIpDQpjb2xuYW1lcyhzYW1wbGVEaXN0TWF0cml4KSA8LSBOVUxMDQpjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZSggcmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQ0KDQpwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4LA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzPXNhbXBsZURpc3RzLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzPXNhbXBsZURpc3RzLA0KICAgICAgICAgY29sPWNvbG9ycykNCg0KYGBgDQoNCiMjIyBWb2xjYW5vIFBsb3RzDQoNClRoZSBwbG90cyBiZWxvdyBhcmUgbWFkZSB1c2luZyBbRW5oYW5jZWQgVm9sY2FubyBwYWNrYWdlXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvRW5oYW5jZWRWb2xjYW5vL2luc3QvZG9jL0VuaGFuY2VkVm9sY2Fuby5odG1sKSANCg0KYGBge3J9DQpFbmhhbmNlZFZvbGNhbm8ocmVzX0NCLA0KICAgIGxhYiA9IHJvd25hbWVzKHJlc19DQiksDQogICAgeCA9ICdsb2cyRm9sZENoYW5nZScsDQogICAgeSA9ICdwdmFsdWUnKQ0KDQpFbmhhbmNlZFZvbGNhbm8ocmVzX0NCLA0KICAgIGxhYiA9IHJvd25hbWVzKHJlc19DQiksDQogICAgeCA9ICdsb2cyRm9sZENoYW5nZScsDQogICAgeSA9ICdwdmFsdWUnLA0KICAgIHRpdGxlID0gJzRtIHZzIDJ5IENCJywNCiAgICBwQ3V0b2ZmID0gMC4wMSwNCiAgICBGQ2N1dG9mZiA9IDAuNSwNCiAgICBwb2ludFNpemUgPSAzLjAsDQogICAgbGFiU2l6ZSA9IDYuMCkNCg0KcmVzX0NCLmRmIDwtIG1lcmdlKHJlc19DQi5kZiwgYW5ub3RhdGlvbiwgYnkgPSAwKQ0KcmVzX0NCLmRmIDwtIHJlc19DQi5kZlshaXMubmEocmVzX0NCLmRmJHN5bWJvbCksXQ0KDQojQ3VzdG9tIHNoYXBlIGFuZCBjb2xvciANCmtleXZhbHMgPC0gaWZlbHNlKA0KICAgICAgICAgICByZXNfQ0IuZGYkbG9nMkZvbGRDaGFuZ2UgPCAtMi41LCAncm95YWxibHVlJywNCiAgICAgICAgICAgaWZlbHNlKHJlc19DQi5kZiRsb2cyRm9sZENoYW5nZSA+IDIuNSwgJ2dvbGQnLCdibGFjaycpKQ0KDQprZXl2YWxzW2lzLm5hKGtleXZhbHMpXSA8LSAnYmxhY2snDQpuYW1lcyhrZXl2YWxzKVtrZXl2YWxzID09ICdnb2xkJ10gPC0gJ2hpZ2gnDQpuYW1lcyhrZXl2YWxzKVtrZXl2YWxzID09ICdibGFjayddIDwtICdtaWQnDQpuYW1lcyhrZXl2YWxzKVtrZXl2YWxzID09ICdyb3lhbGJsdWUnXSA8LSAnbG93Jw0KDQpWb2xjYW5vMSA8LSBFbmhhbmNlZFZvbGNhbm8ocmVzX0NCLmRmLA0KICAgICAgICAgICAgbGFiID0gcmVzX0NCLmRmJHN5bWJvbCwNCiAgICAgICAgICAgIHggPSAnbG9nMkZvbGRDaGFuZ2UnLA0KICAgICAgICAgICAgeSA9ICdwdmFsdWUnLA0KICAgICAgICAgICAgc2VsZWN0TGFiID0gcmVzX0NCLmRmJHN5bWJvbFt3aGljaChuYW1lcyhrZXl2YWxzKSAlaW4lIGMoJ2hpZ2gnLCAnbG93JykpXSwNCiAgICAgICAgICAgIHhsYWIgPSBicXVvdGUofkxvZ1syXX4gJ2ZvbGQgY2hhbmdlJyksDQogICAgICAgICAgICB0aXRsZSA9ICc0bSB2cyAyeSBDQicsDQogICAgICAgICAgICBzdWJ0aXRsZSA9ICcnLA0KICAgICAgICAgICAgbGVnZW5kTGFiZWxzPWMoJ05vdCBzaWcuJyx+TG9nWzJdfiAnZm9sZCBjaGFuZ2UgPnwxfCcsJ3BhZGo8MC4wMScsJ0JvdGgnKSwNCiAgICAgICAgICAgIHBDdXRvZmYgPSAwLjAxLA0KICAgICAgICAgICAgRkNjdXRvZmYgPSAxLjAsDQogICAgICAgICAgICBwb2ludFNpemUgPSAzLjUsDQogICAgICAgICAgICBsYWJTaXplID0gNC41LA0KICAgICAgICAgICAgc2hhcGUgPSBjKDYsIDQsIDIsIDExKSwNCiAgICAgICAgICAgIGNvbEN1c3RvbSA9IGtleXZhbHMsDQogICAgICAgICAgICBjb2xBbHBoYSA9IDEsDQogICAgICAgICAgICBsZWdlbmRQb3NpdGlvbiA9ICdsZWZ0JywNCiAgICAgICAgICAgIGxlZ2VuZExhYlNpemUgPSAxNSwNCiAgICAgICAgICAgIGxlZ2VuZEljb25TaXplID0gNS4wLA0KICAgICAgICAgICAgZHJhd0Nvbm5lY3RvcnMgPSBUUlVFLA0KICAgICAgICAgICAgd2lkdGhDb25uZWN0b3JzID0gMS4wLA0KICAgICAgICAgICAgY29sQ29ubmVjdG9ycyA9ICdibGFjaycsDQogICAgICAgICAgICBhcnJvd2hlYWRzID0gRkFMU0UsDQogICAgICAgICAgICBncmlkbGluZXMubWFqb3IgPSBUUlVFLA0KICAgICAgICAgICAgZ3JpZGxpbmVzLm1pbm9yID0gRkFMU0UsDQogICAgICAgICAgICBib3JkZXIgPSAncGFydGlhbCcsDQogICAgICAgICAgICBib3JkZXJXaWR0aCA9IDEuNSwNCiAgICAgICAgICAgIGJvcmRlckNvbG91ciA9ICdibGFjaycpDQoNCg0KVm9sY2FubzIgPC0gRW5oYW5jZWRWb2xjYW5vKHJlc19DQi5kZiwNCiAgICAgICAgICAgIGxhYiA9IHJlc19DQi5kZiRzeW1ib2wsDQogICAgICAgICAgICB4ID0gImxvZzJGb2xkQ2hhbmdlIiwNCiAgICAgICAgICAgIHkgPSAicHZhbHVlIiwNCiAgICAgICAgICAgIHBDdXRvZmYgPSAwLjAxLA0KICAgICAgICAgICAgRkNjdXRvZmYgPSAxLjUsDQogICAgICAgICAgICBwb2ludFNpemUgPSBjKGlmZWxzZShyZXNfQ0IuZGYkbG9nMkZvbGRDaGFuZ2U+MiwgMiwgMSkpLA0KICAgICAgICAgICAgbGFiU2l6ZSA9IDIuMCwNCiAgICAgICAgICAgIHNoYXBlID0gYyg2LCA2LCAxOSwgMTYpLA0KICAgICAgICAgICAgdGl0bGUgPSAiREVTZXEyIHJlc3VsdHMiLA0KICAgICAgICAgICAgc3VidGl0bGUgPSAiRGlmZmVyZW50aWFsIGV4cHJlc3Npb24iLA0KICAgICAgICAgICAgY2FwdGlvbiA9IGJxdW90ZSh+TG9nWzJdfiAiZm9sZCBjaGFuZ2UgY3V0b2ZmLCAyOyBwLXZhbHVlIGN1dG9mZiwgMC4wMSIpLA0KICAgICAgICAgICAgbGVnZW5kUG9zaXRpb24gPSAicmlnaHQiLA0KICAgICAgICAgICAgbGVnZW5kTGFiU2l6ZSA9IDE0LA0KICAgICAgICAgICAgbGVnZW5kTGFiZWxzPWMoJ05vdCBzaWcuJyx+TG9nWzJdfiAnZm9sZCBjaGFuZ2UgPnwxfCcsJ3BhZGo8MC4wMScsJ0JvdGgnKSwNCiAgICAgICAgICAgIGNvbCA9IGMoImdyZXkzMCIsICJmb3Jlc3RncmVlbiIsICJyb3lhbGJsdWUiLCAicmVkMiIpLA0KICAgICAgICAgICAgY29sQWxwaGEgPSAwLjksDQogICAgICAgICAgICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsDQogICAgICAgICAgICBobGluZSA9IGMoMTBlLTgpLA0KICAgICAgICAgICAgd2lkdGhDb25uZWN0b3JzID0gMC41KQ0KDQpWb2xjYW5vMQ0KVm9sY2FubzINCg0KYGBgDQoNCiMjIyBQbG90IEluZGl2aWR1YWwgQ291bnRzIA0KDQpZb3UgY2FuIG1vZGlmeSB0aGVzZSBwbG90cyB3aXRoIHRoZSB1c3VhbCBnZ3Bsb3QyLiBCZWxvdyBpcyBhIGp1c3QgYSBiYXNpYyBleGFtcGxlLg0KYGBge3J9DQpkIDwtIHBsb3RDb3VudHMoZGRzLCBnZW5lPXdoaWNoLm1pbihyZXNfQ0IkcGFkaiksIGludGdyb3VwPSJBZ2VSZWdpb24iLCANCiAgICAgICAgICAgICAgICByZXR1cm5EYXRhPVRSVUUpDQoNCmdncGxvdChkLCBhZXMoeD1BZ2VSZWdpb24sIHk9Y291bnQpKSArIA0KICBnZW9tX3BvaW50KHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcih3PTAuMSxoPTApKSArIA0KICBzY2FsZV95X2xvZzEwKGJyZWFrcz1jKDI1LDEwMCw0MDApKQ0KYGBgDQoNCg0KYGBge3J9DQoNCiNIb3cgdG8gc2hvdyBvbmx5IGEgc3Vic2V0IG9mIGdyb3Vwcw0KZCA8LSBwbG90Q291bnRzKGRkc1sgLCBkZHMkQWdlUmVnaW9uICVpbiUgYygiMnlfQ0IiLCI0bV9DQiIpXSwgZ2VuZT0iRU5TTVVTRzAwMDAwMDAyOTg1IiwgaW50Z3JvdXA9IkFnZVJlZ2lvbiIsIHJldHVybkRhdGE9VFJVRSkNCg0KI2QkU2FtcGxlTmFtZXMgPC0gbWV0YWRhdGEkc2ltcGxlSUQNCg0KDQpnZ3Bsb3QoZCwgYWVzKHggPSBBZ2VSZWdpb24sIHkgPSBjb3VudCwgZmlsbCA9IEFnZVJlZ2lvbikpICsNCiAgICAgICAgICAgIGdlb21fYm94cGxvdCgpKw0KICAgICAgICAgICAgI2dlb21fcG9pbnQoYWVzKGNvbG9yID0gU2FtcGxlTmFtZXMpLCBzaXplID0gMixwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIodyA9IDAuMSxoID0gMCkpICsNCiAgICAgICAgICAgICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gZCRTYW1wbGVOYW1lcykpICsgDQogICAgICAgICAgICBnZ3RpdGxlKCJSTkE6IEFQT0UiKSArDQogICAgICAgICAgICB5bGFiKCJOb3JtYWxpemVkIENvdW50IChMb2cxMCBTY2FsZSkiKSsNCiAgICAgICAgICAgIGV4cGFuZF9saW1pdHMoeSA9IDEwMDAwMCkrDQogICAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAnbG9nMTAnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYyguMSwgMC4xKSksIGxhYmVscyA9IGNvbW1hKSsNCiAgICAgICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoICJncmV5NjAiLCJkYXJrb3JhbmdlIiwgImJsYWNrIiwgICJmaXJlYnJpY2szIikpKw0KICAgICAgICAgICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikrDQogICAgICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0xKSwNCiAgICAgICAgICAgICAgICAgICNheGlzLnRpdGxlLnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSANCmBgYA0KDQojIyMgREVTZXEyIFJlcG9ydCBUb29sIA0KVGhpcyBbZnVuY3Rpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9yZWdpb25SZXBvcnQvdmVyc2lvbnMvMS42LjUvdG9waWNzL0RFU2VxMlJlcG9ydCkgY3JlYXRlcyBleGFtcGxlIGdyYXBocyBhbmQgdGFibGVzIHdpdGggeW91ciBkYXRhLiANCg0KYGBge3IgZXZhbD0gRkFMU0V9DQpkaXIuY3JlYXRlKCJERVNlcTJSZXBvcnQtZXhhbXBsZSIsIHNob3dXYXJuaW5ncyA9IEZBTFNFLCByZWN1cnNpdmUgPSBUUlVFKQ0KDQojIyBHZW5lcmF0ZSB0aGUgSFRNTCByZXBvcnQNCnJlcG9ydCA8LSBERVNlcTJSZXBvcnQoZGdlLCAiREVTZXEyLWV4YW1wbGUiLCBjKCJBZ2UiLCAiUmVhbFJlZ2lvbiIpLA0KICAgIG91dGRpciA9ICJERVNlcTJSZXBvcnQtZXhhbXBsZSINCikNCg0KaWYgKGludGVyYWN0aXZlKCkpIHsNCiAgICAjIyBCcm93c2UgdGhlIHJlcG9ydA0KICAgIGJyb3dzZVVSTChyZXBvcnQpDQp9DQoNCmBgYA0K