Introduction

This dataset GSE60450 contains the gene expression profiles of luminal and basal cells from different developmental stages.
Luminal and basal cells were harvested from the mammary glands of virgin, 18.5 day pregnant and 2 day lactating mice (2 mice per stage).

The original article was published HERE

Task

We will perform a differential expression analysis (DEA) to identify the transcriptional changes associated with the development stage in luminal cells.

For the purpose of this assignment We will use the lima-voom (linear model) approach which requires that the data is normally distributed.

PLAN

  1. Data Preparation
  2. Data filtering - filterByExpr()
  3. Normalize data - using TMM
  4. Design matrix definition - model.matrix()
  5. Voom transformation - voom()
  6. Lineal model fit - lmFit()
  7. Statistic test - eBayes()

Data Preparation

Set working directory
Load libraries
Input/Load data

#Set working directory
setwd("C:/Users/Lenovo/MIB Assignments/NOTES/Bioinformatica/Bioinformatics Practice R/GSE60450 RNASeqDATA")
getwd()
[1] "C:/Users/Lenovo/MIB Assignments/NOTES/Bioinformatica/Bioinformatics Practice R/GSE60450 RNASeqDATA"
#Load libraries 
library(ggplot2)
library(limma)
suppressPackageStartupMessages(library(ComplexHeatmap))
library(edgeR)
library(reshape2)
Warning: package ‘reshape2’ was built under R version 4.4.2
library(org.Mm.eg.db)
Loading required package: AnnotationDbi
Loading required package: stats4
Loading required package: BiocGenerics

Attaching package: ‘BiocGenerics’

The following object is masked from ‘package:limma’:

    plotMA

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, aperm, append, as.data.frame,
    basename, cbind, colnames, dirname, do.call,
    duplicated, eval, evalq, Filter, Find, get, grep,
    grepl, intersect, is.unsorted, lapply, Map, mapply,
    match, mget, order, paste, pmax, pmax.int, pmin,
    pmin.int, Position, rank, rbind, Reduce, rownames,
    sapply, setdiff, table, tapply, union, unique,
    unsplit, which.max, which.min

Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with
    'browseVignettes()'. To cite Bioconductor, see
    'citation("Biobase")', and for packages
    'citation("pkgname")'.

Loading required package: IRanges
Loading required package: S4Vectors

Attaching package: ‘S4Vectors’

The following object is masked from ‘package:utils’:

    findMatches

The following objects are masked from ‘package:base’:

    expand.grid, I, unname


Attaching package: ‘IRanges’

The following object is masked from ‘package:grDevices’:

    windows
#Input or load data

# Count matrix:
counts <- read.table("count_data_GSE60450.txt", sep=";")
View(counts)

# Sample metadata:
sample_metadata <- read.table("sample_metadata_GSE60450.txt", sep=";")
View(sample_metadata)

# Feature (genes) metadata:
gene_metadata <- read.table("gene_metadata_GSE60450.txt", sep=";", header = TRUE, fill = TRUE)
View(gene_metadata)

Our focus will be the luminal cells data. Therefore we will create a luminal cells only counts matrix.

#Create counts_luminal only matrix

counts_luminal <- counts[,sample_metadata$cell_type=="luminal cell population"]
dim(counts_luminal) #27179   by  6
[1] 27179     6
sample_metadata_luminal <- sample_metadata[sample_metadata$cell_type=="luminal cell population",]
View(sample_metadata_luminal)

View(counts_luminal)

We will also create an object to group later by the development stage (reference to objective)


#group_dvst <- sample_metadata_luminal$developmental_stage
#table(group_dvst)

#changed due to downstream analysis (contrasts)
group_dvst <- factor(sample_metadata_luminal$developmental_stage)
levels(group_dvst)
[1] "18.5 day pregnancy" "2 day lactation"   
[3] "virgin"            
table(group_dvst)
group_dvst
18.5 day pregnancy    2 day lactation             virgin 
                 2                  2                  2 

Data filtering:

We will filter the data using the filterByExpr() in which the minimum count for each gene is 10. This function requires the edgeR library installed. Filtered data will be named counts_luminal_filtered

dim(counts_luminal_filtered) #14582   by  6... FANTASTIC!
[1] 14582     6

PAUSE: Show Me GRAPHICS!

What do we have up to this point?


Use par(mfrow) to display all the graphics generated in a single row.

1.depth plot


#depth plot

lumcounts_to_plot <- data.frame(sample_id=colnames(counts_luminal_filtered),depth=colSums(counts_luminal_filtered))

View(lumcounts_to_plot)

library(ggplot2)

ggplot(lumcounts_to_plot,aes(x=sample_id, y=depth, fill=sample_id)) +
  geom_col() +
  theme_classic() +
  theme(axis.text.x = element_text(angle=90))

NA
NA

2.Box plot (Distribution)

Normalise data:

Now that data has been filtered, we will normalize it before using the linear model.

We will normalize this data using the TMM method as it compensates for more dominant genes unlike the CPM (total counts only).

The TMM method requires the edgeR() library and requires that data be transformed into a DGE list first.


#Normalizing data using TMM 

library(edgeR) #load required library for TMM

dge <- DGEList(counts = counts_luminal_filtered, group= group_dvst)

#?calcNormFactors

counts_luminal_normalized <- calcNormFactors(dge, method = "TMM") 

Question: Why have we normalized the way we have with calcNormFactors instead of manually with the code below?

counts_luminal_normalized <- normLibSizes(dge, method = "TMM") 

counts_luminal_normalized <- cpm(counts_luminal_normalized, normalized.lib.sizes = TRUE) 
counts_luminal_normalized_log2 <- log2(counts_luminal_normalized + 1)

Answer:

This is because the method we are using to call DEA (Differential Gene Expression) - limma-voom requires that data be fit onto a linear model.

calcNormFactors() and normLibSizes() are two TMM normalisation approaches with different outputs.

normLibSizes() normally used for data exploration as it normalises reads counts (lib.sizes). You would also have to perform cpm() in addition to this commmand.

calcNormFactors() accounts for the expression of each gene relative to the other and therefore is more suitable for DGE analysis.

For Voom transformation, the most suitable output is by calcNormFactors() which is then transformed and log2CPM is applied all within the voom() function.

Voom transformation:

voom() stands for “variance modeling at the observational level.” It transforms raw RNA-seq counts into log2 counts-per-million (logCPM) while accounting for mean-variance relationships in the data.

RNA-seq count data is not normally distributed, however, lmFit() assumes normally distributed values therfore voom() estimates mean-variance relationships and transforms data to satisfy this assumption.

A design matrix of the microarray experiment is needed. It contains rows corresponding to samples and columns to coefficients to be estimated. And defines intergroup comparisons.

How will the groups be compared?
Which should be the Control group? Do we need one?

#normalised data transformation

#create design matrix (essential for voom() and lmFit())
#?model.matrix

#design_matrix <- model.matrix(~ group_dvst)
#colnames(design_matrix) 

#** Outputs 
#*[1] "(Intercept)"               "group_dvst2 day lactation"
#*[3] "group_dvstvirgin" 


#WHO SHOULD BE THE CONTROL GROUP??? The model matrix chose the "18.5 day pregnancy" as the baseline group... Why not the virgin? 

design_matrix <- model.matrix(~ 0 + group_dvst) #removes intercept
colnames(design_matrix) 
[1] "group_dvst18.5 day pregnancy" "group_dvst2 day lactation"   
[3] "group_dvstvirgin"            
#voom transformation
voom_transformed <- voom(counts_luminal_normalized, design_matrix, plot = TRUE)

Each dot represents a gene.
X axis represents the log-tranformed gene expression.
y axis represnts variance (sd)

Take away: Variance decreases with increasing gene expression (Red trend line).
The higher the gene is expressed the more sure we are it is there.

Normalised Data - Box plot

Linear model fit:

We will fit our voom-transformed data on the linear model and we will visualise our fitted data. For this we have to use the model matrix in the lmFit() function.

#?lmFit

#create fit data
fit <- lmFit(voom_transformed, design =  design_matrix)

#class(fit) #fit is "MArrayLM" - MicroArray Linear Model

ADJUSTED MEAN?? METHOD USED (TMM, BONFERONI, HOLM, BH)

Statistic test:

We will use the eBayes() test:

Given a linear model fit from lmFit, we can compute moderated t-statistics, moderated F-statistic, and log-odds of differential expression by empirical Bayes moderation of the standard errors towards a global value.

This function is used to rank genes in order of evidence for differential expression. It uses an empirical Bayes method to squeeze the genewise-wise residual variances towards a common value (or towards a global trend) (Smyth, 2004; Phipson et al, 2016).

#?eBayes

fit_ebayes <- eBayes(fit)

Output Matrix:

We will generate the output matrix and save it as limma_GSE60450.rds using the saveRDS() function.

#output matrix with fold change and adj.p-value<=0.05 = gene expression significant

#topTable function is part of the limma library
#We will use it to extract the output or result matrix 

results_allgenes <- topTable(fit_ebayes, coef = ncol(design_matrix), number = Inf, adjust.method = "BH")

head(results_allgenes)

coef: Specifies condition to compare.
number = Inf, Retuens all genes.
adjust.method = “BH”, Benjamini-Hochberg to adjust p values.

SAVING RESULTS

As txt file write.table() and as rds saveRDS()

write.table(results_allgenes, file="limma_differential_expr_results.txt", sep="\t", quote=FALSE)

#?saveRDS
saveRDS(results_allgenes, file="DEA_limma_GSE60450.rds")

Significantly DIFFERENTIALY EXPRESSED GENES

How many genes are differentially expressed if we define a threshold of: adj.p-value<=0.05 and |log2(FCh)|>0.58 ?

These will be saved as sigGenes


sigGenes <- results_allgenes[results_allgenes$adj.P.Val<=0.05 & results_allgenes$logFC>0.58, ] #it is the rows that we are filtering based on the defined criteria but we want all columns to come back.

dim(sigGenes)
[1] 12144     6

Therefore 12144 genes are significantly differentially expressed with that defined threshold.

●○●○●○●○●

TOP 50 Differentially Expressed Genes

For this we have to first organise the results by the adjusted p values.


sigGenes <- sigGenes[order(sigGenes$adj.P.Val), ] #from most significant to least significant

t50_sigGenes <- head(sigGenes, n=50)

t50_sigGenes <- gene_metadata[rownames(t50_sigGenes), c("SYMBOL", "GENENAME")]

t50_sigGenes

As seen above our gene metadata is not fully annotated therefore we will annotate it to have all genes identified.
For that we will use the library(org.Mm.eg.db) which contains all the information related with the Mus musculus genome.

library(org.Mm.eg.db)
#columns(org.Mm.eg.db)

gene_metadata_annotated <- select(org.Mm.eg.db,keys=rownames(gene_metadata),columns=c("ENTREZID","SYMBOL","GENENAME"))
'select()' returned 1:1 mapping between keys and columns
#View(gene_metadata_annnotated) 
#This ommited the Length column but filled the symbols and gene names
#We can now use this to identify the missing columns 
selection <- head(sigGenes, n=50)

t50_sigGenes_complete <- gene_metadata_annotated[gene_metadata_annotated$ENTREZID %in% rownames(head(sigGenes, n=50)), c("SYMBOL", "GENENAME")]

The table below shows the top 23/50 differentially expressed genes across all developmental stages. 27 gene IDs were not found.

t50_sigGenes_complete

Result Visualization:

Principal Component
To see developmental stage differences in Samples.


pca <- prcomp(t(voom_transformed$E), scale. = TRUE)
pca_data_to_plot <- data.frame(PC1 = pca$x[,1], PC2 = pca$x[,2], group = group_dvst)

ggplot(pca_data_to_plot, aes(x = PC1, y = PC2, color = group_dvst)) +
  geom_point(size=4) +
  theme_minimal() +
  labs(title = "PCA - RNA-seq Samples", x = "PC1", y = "PC2")

Volcano plot

Biological relevance vs Statistical significance.


#Volcano Plot
ggplot(results_allgenes, aes(x = logFC, y = -log10(adj.P.Val))) + 
  # All genes = BLACK (These don't change)
  geom_point(color = "black", alpha = 0.3) +
  
  #Overexpressed genes = RED
  geom_point(data=results_allgenes[results_allgenes$logFC > 0.58 & results_allgenes$adj.P.Val <= 0.05,], 
             aes(x = logFC, y = -log10(adj.P.Val)), color="red", alpha = 0.7) +
  
  #Under expressed genes = BLUE
  geom_point(data=results_allgenes[results_allgenes$logFC < -0.58 & results_allgenes$adj.P.Val <= 0.05,], 
             aes(x = logFC, y = -log10(adj.P.Val)), color="blue", alpha = 0.7) +
  
#ref lines
  geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "blue") +
  geom_vline(xintercept = c(0.58, -0.58), linetype = "dashed", color = "gray") +
  
  theme_minimal() +
  
  labs(title = "Volcano Plot - Diferential Expression",
       x = "Log2 Fold Change", 
       y = "-Log10 P-value")

x axis = whether a expression of gene changes in between groups

y axis = whether the gene change is significant between groups.

Contrasts with a Heatmap

Top 50 genes differentially expressed genes in each development stage are visualized below with a heatmap.

This was easier to do in regards to statistical methods of comparing group levels against each other.

Visual representation of this data requires that a z_score be obtained from the voom transformed (normalised data), this sets the mean of each sample to 0 and enables us to see the minor changes in gene expression visually (both above and below zero)

For each of the developmental stages (samples), It is possible to extract overexpressed genes and underexpressed genes by using a selection criteria of z> 1.5 and z< -1.5 respectively.

Alternativeley, contrasts can be made to do the same.

Conclusions

We will report our findings based on the objective of this assignment, to identify the transcriptional changes associated with the development stage in luminal cells.

  1. A total of 27179 genes were sequence for the luminal cell population.

  2. Of these 14582 (53.65%) were retained after filtering (default filterByExpr())

  3. 12144 (44.68%) genes were differentially expressed (adj.p-value <=0.05 and log2(FCh) >0.58).

Acknowledgements

The commented scripts by Teacher Nuria were helpful in reusing the code. We also used chatGPT to debug the code and understand arguments and for clarity of some concepts.

References

  1. Fu, N. Y., Rios, A. C., Pal, B., Soetanto, R., Lun, A. T. L., Liu, K., Beck, T., Best, S. A., Vaillant, F., Bouillet, P., Strasser, A., Preiss, T., Smyth, G. K., Lindeman, G. J., & Visvader, J. E. (2015). EGF-mediated induction of Mcl-1 at the switch to lactation is essential for alveolar cell survival. Nature Cell Biology 2014 17:4, 17(4), 365–375. https://doi.org/10.1038/ncb3117

  2. GSE60450 (GEO data) - https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE60450

  3. limma - voom ( https://rpubs.com/jrgonzalezISGlobal/transcriptomic_analyses )

  4. edgeR (https://www.bioconductor.org/packages/devel/bioc/vignettes/edgeR/inst/doc/edgeRUsersGuide.pdf, points 2.10, 2.11)

  5. Identifying differentially expressed genes using linear models (part 1) - https://gtk-teaching.github.io/Microarrays-R/06-DifferentialGeneExpression/index.html

  6. Use par mfrow to split screen - https://r-graph-gallery.com/71-split-screen-with-par-mfrow.html#:~:text=The%20par()%20function%20allows%20to%20set%20parameters%20to%20the,rows%20and%20number%20of%20columns.

  7. Phipson B, Lee S, Majewski IJ, Alexander WS, Smyth GK (2016). Robust hyperparameter estimation protects against hypervariable genes and improves power to detect differential expression. Annals of Applied Statistics 10, 946-963. doi:10.1214/16-AOAS920

  8. Smyth GK (2004). Linear models and empirical Bayes methods for assessing differential expression in microarray experiments. Statistical Applications in Genetics and Molecular Biology Volume 3, Issue 1, Article 3. doi:10.2202/1544-6115.1027. See also the Preprint Version https://gksmyth.github.io/pubs/ebayes.pdf incorporating corrections to 30 June 2009.

  9. OpenAI. (2025). ChatGPT (Feb 14 version) [Large language model]. Retrieved from https://openai.com

LS0tDQp0aXRsZTogIlJOQVNlcSBERUEgVGFyZWE6IFRyYW5zY3JpcHRpb25hbCBDaGFuZ2VzIEFzc29jaWF0ZWQgd2l0aCB0aGUgRGV2ZWxvcG1lbnQgU3RhZ2UgSW4gTHVtaW5hbCBDZWxscy4iDQphdXRob3I6ICJHaW9yZGFuYSAsICBLcmlzdGVsbCBQYW50YSwgSmFzb24gTHViZWdhIg0KZGF0ZTogIjEyLkZlYi4yMDI1Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIGNzczogcm1kc3R5bGUyLmNzcw0KLS0tDQoNCiMjICoqSW50cm9kdWN0aW9uKioNCg0KVGhpcyBkYXRhc2V0IFtHU0U2MDQ1MF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1NFNjA0NTApIGNvbnRhaW5zIHRoZSBnZW5lIGV4cHJlc3Npb24gcHJvZmlsZXMgb2YgbHVtaW5hbCBhbmQgYmFzYWwgY2VsbHMgZnJvbSBkaWZmZXJlbnQgZGV2ZWxvcG1lbnRhbCBzdGFnZXMuXA0KTHVtaW5hbCBhbmQgYmFzYWwgY2VsbHMgd2VyZSBoYXJ2ZXN0ZWQgZnJvbSB0aGUgbWFtbWFyeSBnbGFuZHMgb2YgdmlyZ2luLCAxOC41IGRheSBwcmVnbmFudCBhbmQgMiBkYXkgbGFjdGF0aW5nIG1pY2UgKDIgbWljZSBwZXIgc3RhZ2UpLg0KDQpUaGUgb3JpZ2luYWwgYXJ0aWNsZSB3YXMgcHVibGlzaGVkIFtIRVJFXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25jYjMxMTcpDQoNCiMjICoqVGFzayoqDQoNCldlIHdpbGwgcGVyZm9ybSBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIChERUEpIHRvIGlkZW50aWZ5IHRoZSB0cmFuc2NyaXB0aW9uYWwgY2hhbmdlcyBhc3NvY2lhdGVkIHdpdGggdGhlIGRldmVsb3BtZW50IHN0YWdlIGluICoqbHVtaW5hbCBjZWxscy4qKg0KDQpGb3IgdGhlIHB1cnBvc2Ugb2YgdGhpcyBhc3NpZ25tZW50IFdlIHdpbGwgdXNlIHRoZSAqKmxpbWEtdm9vbSoqIChsaW5lYXIgbW9kZWwpIGFwcHJvYWNoIHdoaWNoIHJlcXVpcmVzIHRoYXQgdGhlIGRhdGEgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuDQoNCiMjICoqUExBTioqDQoNCjEuICBEYXRhIFByZXBhcmF0aW9uDQoyLiAgRGF0YSBmaWx0ZXJpbmcgLSBmaWx0ZXJCeUV4cHIoKQ0KMy4gIE5vcm1hbGl6ZSBkYXRhIC0gdXNpbmcgVE1NDQo0LiAgRGVzaWduIG1hdHJpeCBkZWZpbml0aW9uIC0gbW9kZWwubWF0cml4KCkNCjUuICBWb29tIHRyYW5zZm9ybWF0aW9uIC0gdm9vbSgpDQo2LiAgTGluZWFsIG1vZGVsIGZpdCAtIGxtRml0KCkNCjcuICBTdGF0aXN0aWMgdGVzdCAtIGVCYXllcygpDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgKipEYXRhIFByZXBhcmF0aW9uKioNCg0KU2V0IHdvcmtpbmcgZGlyZWN0b3J5XA0KTG9hZCBsaWJyYXJpZXNcDQpJbnB1dC9Mb2FkIGRhdGENCg0KYGBge3IgZXZhbD1UUlVFLCBlY2hvPVRSVUV9DQojU2V0IHdvcmtpbmcgZGlyZWN0b3J5DQpzZXR3ZCgiQzovVXNlcnMvTGVub3ZvL01JQiBBc3NpZ25tZW50cy9OT1RFUy9CaW9pbmZvcm1hdGljYS9CaW9pbmZvcm1hdGljcyBQcmFjdGljZSBSL0dTRTYwNDUwIFJOQVNlcURBVEEiKQ0KZ2V0d2QoKQ0KDQojTG9hZCBsaWJyYXJpZXMgDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGxpbW1hKQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoQ29tcGxleEhlYXRtYXApKQ0KbGlicmFyeShlZGdlUikNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KG9yZy5NbS5lZy5kYikNCg0KI0lucHV0IG9yIGxvYWQgZGF0YQ0KDQojIENvdW50IG1hdHJpeDoNCmNvdW50cyA8LSByZWFkLnRhYmxlKCJjb3VudF9kYXRhX0dTRTYwNDUwLnR4dCIsIHNlcD0iOyIpDQpWaWV3KGNvdW50cykNCg0KIyBTYW1wbGUgbWV0YWRhdGE6DQpzYW1wbGVfbWV0YWRhdGEgPC0gcmVhZC50YWJsZSgic2FtcGxlX21ldGFkYXRhX0dTRTYwNDUwLnR4dCIsIHNlcD0iOyIpDQpWaWV3KHNhbXBsZV9tZXRhZGF0YSkNCg0KIyBGZWF0dXJlIChnZW5lcykgbWV0YWRhdGE6DQpnZW5lX21ldGFkYXRhIDwtIHJlYWQudGFibGUoImdlbmVfbWV0YWRhdGFfR1NFNjA0NTAudHh0Iiwgc2VwPSI7IiwgaGVhZGVyID0gVFJVRSwgZmlsbCA9IFRSVUUpDQpWaWV3KGdlbmVfbWV0YWRhdGEpDQoNCmBgYA0KDQpPdXIgZm9jdXMgd2lsbCBiZSB0aGUgbHVtaW5hbCBjZWxscyBkYXRhLiBUaGVyZWZvcmUgd2Ugd2lsbCBjcmVhdGUgYSBsdW1pbmFsIGNlbGxzIG9ubHkgY291bnRzIG1hdHJpeC4NCg0KYGBge3IgZXZhbD1UUlVFLCBlY2hvPVRSVUV9DQojQ3JlYXRlIGNvdW50c19sdW1pbmFsIG9ubHkgbWF0cml4DQoNCmNvdW50c19sdW1pbmFsIDwtIGNvdW50c1ssc2FtcGxlX21ldGFkYXRhJGNlbGxfdHlwZT09Imx1bWluYWwgY2VsbCBwb3B1bGF0aW9uIl0NCmRpbShjb3VudHNfbHVtaW5hbCkgIzI3MTc5ICAgYnkgIDYNCg0Kc2FtcGxlX21ldGFkYXRhX2x1bWluYWwgPC0gc2FtcGxlX21ldGFkYXRhW3NhbXBsZV9tZXRhZGF0YSRjZWxsX3R5cGU9PSJsdW1pbmFsIGNlbGwgcG9wdWxhdGlvbiIsXQ0KVmlldyhzYW1wbGVfbWV0YWRhdGFfbHVtaW5hbCkNCg0KVmlldyhjb3VudHNfbHVtaW5hbCkNCg0KDQpgYGANCg0KV2Ugd2lsbCBhbHNvIGNyZWF0ZSBhbiBvYmplY3QgdG8gZ3JvdXAgbGF0ZXIgYnkgdGhlIGRldmVsb3BtZW50IHN0YWdlIChyZWZlcmVuY2UgdG8gb2JqZWN0aXZlKQ0KDQpgYGB7ciBldmFsPVRSVUUsIGVjaG89VFJVRX0NCg0KI2dyb3VwX2R2c3QgPC0gc2FtcGxlX21ldGFkYXRhX2x1bWluYWwkZGV2ZWxvcG1lbnRhbF9zdGFnZQ0KI3RhYmxlKGdyb3VwX2R2c3QpDQoNCiNjaGFuZ2VkIGR1ZSB0byBkb3duc3RyZWFtIGFuYWx5c2lzIChjb250cmFzdHMpDQpncm91cF9kdnN0IDwtIGZhY3RvcihzYW1wbGVfbWV0YWRhdGFfbHVtaW5hbCRkZXZlbG9wbWVudGFsX3N0YWdlKQ0KbGV2ZWxzKGdyb3VwX2R2c3QpDQp0YWJsZShncm91cF9kdnN0KQ0KDQpgYGANCg0KIyMjICoqRGF0YSBmaWx0ZXJpbmc6KioNCg0KV2Ugd2lsbCBmaWx0ZXIgdGhlIGRhdGEgdXNpbmcgdGhlICoqZmlsdGVyQnlFeHByKCkqKiBpbiB3aGljaCB0aGUgbWluaW11bSBjb3VudCBmb3IgZWFjaCBnZW5lIGlzIDEwLiBUaGlzIGZ1bmN0aW9uIHJlcXVpcmVzIHRoZSBlZGdlUiBsaWJyYXJ5IGluc3RhbGxlZC4gRmlsdGVyZWQgZGF0YSB3aWxsIGJlIG5hbWVkICoqY291bnRzX2x1bWluYWxfZmlsdGVyZWQqKg0KDQpgYGB7ciBldmFsPVRSVUUsIGVjaG89VFJVRX0NCmxpYnJhcnkoZWRnZVIpDQoNCnByaW50KG1heChjb3VudHNfbHVtaW5hbCkpICMgbWF4aW11bSByZWFkID0gMjg4Nzk2OQ0KDQpwcmludChtaW4oY291bnRzX2x1bWluYWwpKSAjbWluaW11bSByZWFkID0gMA0KDQo/ZmlsdGVyQnlFeHByDQoNCg0KI2NyZWF0aW5nIGZpbHRlcmluZyBjcml0ZXJpYQ0KbXlnZV9maWx0ZXIgPC0gZmlsdGVyQnlFeHByKGNvdW50c19sdW1pbmFsLCBkZXNpZ24gPSBOVUxMLCBncm91cCA9IGdyb3VwX2R2c3QsIG1pbi5jb3VudCA9IDEwLCBtaW4udG90YWwuY291bnQgPSAxNSkNCg0KdGFibGUobXlnZV9maWx0ZXIpICANCiMqKg0KIypGQUxTRSAgVFJVRSANCiMqMTI1OTcgMTQ1ODIgDQoNCiMjIFVzaW5nIGZpbHRlciB0byBrZWVwIG9ubHkgVFJVRSB2YWx1ZXMNCmNvdW50c19sdW1pbmFsX2ZpbHRlcmVkIDwtIGNvdW50c19sdW1pbmFsW215Z2VfZmlsdGVyLCBdDQpkaW0oY291bnRzX2x1bWluYWxfZmlsdGVyZWQpICMxNDU4MiAgIGJ5ICA2Li4uIEZBTlRBU1RJQyENCg0KDQojY2hlY2tpbmcgaWYgZmlsdGVyIHdvcmtlZA0KcHJpbnQobWF4KGNvdW50c19sdW1pbmFsX2ZpbHRlcmVkKSkgIyBtYXhpbXVtIHJlYWQgPSAyODg3OTY5DQpwcmludChtaW4oY291bnRzX2x1bWluYWxfZmlsdGVyZWQpKSAjbWluaW11bSByZWFkID0gMCAhISE/Pz8/IG1pbiBzaG91bGQgYmUgMTAgDQoNCiMqIEZvciB0aGUgbWluaW11bSByZWFkLCB3ZSBnb3QgMCwgYmVjYXVzZSBzb21lIGdlbmVzIGRvIG5vdCBleHByZXNzIHVudGlsIGEgY2VydGFpbiBzdGFnZSAod2hlcmUgdGhlIG51bWJlciBvZiByZWFkcyBpbmNyZWFzZXMgYW5kIGlzIGV2ZW50dWFsbHkgZ3JlYXRlciB0aGFuIHRoZSBmaWx0ZXIpLiANCiMqIFlvdSBjYW4gY2hlY2sgdGhpcyBieSBleHBsb3JpbmcgdGhlIGZpbHRlcmVkIG1hdHJpeCB3aXRoIFZpZXcoY291bnRzX2x1bWluYWxfZmlsdGVyZWQpDQoNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAqKlBBVVNFOiBTaG93IE1lIEdSQVBISUNTISoqDQoNCldoYXQgZG8gd2UgaGF2ZSB1cCB0byB0aGlzIHBvaW50Pw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KVXNlICpwYXIobWZyb3cpKiB0byBkaXNwbGF5IGFsbCB0aGUgZ3JhcGhpY3MgZ2VuZXJhdGVkIGluIGEgc2luZ2xlIHJvdy4NCg0KMS5kZXB0aCBwbG90XA0KDQpgYGB7ciBldmFsPVRSVUUsIGVjaG89VFJVRX0NCg0KI2RlcHRoIHBsb3QNCg0KbHVtY291bnRzX3RvX3Bsb3QgPC0gZGF0YS5mcmFtZShzYW1wbGVfaWQ9Y29sbmFtZXMoY291bnRzX2x1bWluYWxfZmlsdGVyZWQpLGRlcHRoPWNvbFN1bXMoY291bnRzX2x1bWluYWxfZmlsdGVyZWQpKQ0KDQpWaWV3KGx1bWNvdW50c190b19wbG90KQ0KDQpsaWJyYXJ5KGdncGxvdDIpDQoNCmdncGxvdChsdW1jb3VudHNfdG9fcGxvdCxhZXMoeD1zYW1wbGVfaWQsIHk9ZGVwdGgsIGZpbGw9c2FtcGxlX2lkKSkgKw0KICBnZW9tX2NvbCgpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTApKQ0KDQoNCmBgYA0KDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjIuQm94IHBsb3QgKERpc3RyaWJ1dGlvbilcDQoNCmBgYHtyfQ0KIyMgZmlsdGVyZWQgQ291bnRzIGRpc3RyaWJ1dGlvbg0KDQpsdW1jb3VudHNfdG9fcGxvdCA8LSBzdGFjayhjb3VudHNfbHVtaW5hbF9maWx0ZXJlZCkgI3RvIHJlb3JnYW5pc2UgZGF0YSBhcyBmcmVxdWVuY3kgcGVyIFNhbXBsZSANCg0KZ2dwbG90KGx1bWNvdW50c190b19wbG90LGFlcyh4PWluZCwgeT12YWx1ZXMpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIEx1bWluYWwgQ291bnRzIChmaWx0ZXJlZCkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwKSkgKw0KICBzY2FsZV95X2xvZzEwKCkgKw0KICB4bGFiKCJMdW1pbmFsIFNhbXBsZXMiKSArDQogIHlsYWIoImxvZzEwKFZhbHVlcykiKQ0KDQoNCmBgYA0KDQoNCg0KIyMjICoqTm9ybWFsaXNlIGRhdGE6KioNCg0KTm93IHRoYXQgZGF0YSBoYXMgYmVlbiBmaWx0ZXJlZCwgd2Ugd2lsbCBub3JtYWxpemUgaXQgYmVmb3JlIHVzaW5nIHRoZSBsaW5lYXIgbW9kZWwuDQoNCldlIHdpbGwgbm9ybWFsaXplIHRoaXMgZGF0YSB1c2luZyB0aGUgKipUTU0gbWV0aG9kKiogYXMgaXQgY29tcGVuc2F0ZXMgZm9yIG1vcmUgZG9taW5hbnQgZ2VuZXMgdW5saWtlIHRoZSBDUE0gKHRvdGFsIGNvdW50cyBvbmx5KS4gDQoNClRoZSBUTU0gbWV0aG9kIHJlcXVpcmVzIHRoZSAqZWRnZVIoKSogbGlicmFyeSBhbmQgcmVxdWlyZXMgdGhhdCBkYXRhIGJlIHRyYW5zZm9ybWVkIGludG8gYSBER0UgbGlzdCBmaXJzdC4gDQoNCmBgYHtyIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KDQojTm9ybWFsaXppbmcgZGF0YSB1c2luZyBUTU0gDQoNCmxpYnJhcnkoZWRnZVIpICNsb2FkIHJlcXVpcmVkIGxpYnJhcnkgZm9yIFRNTQ0KDQpkZ2UgPC0gREdFTGlzdChjb3VudHMgPSBjb3VudHNfbHVtaW5hbF9maWx0ZXJlZCwgZ3JvdXA9IGdyb3VwX2R2c3QpDQoNCiM/Y2FsY05vcm1GYWN0b3JzDQoNCmNvdW50c19sdW1pbmFsX25vcm1hbGl6ZWQgPC0gY2FsY05vcm1GYWN0b3JzKGRnZSwgbWV0aG9kID0gIlRNTSIpIA0KDQpgYGANCg0KDQoNCioqUXVlc3Rpb246IFdoeSBoYXZlIHdlIG5vcm1hbGl6ZWQgdGhlIHdheSB3ZSBoYXZlIHdpdGggKmNhbGNOb3JtRmFjdG9ycyogaW5zdGVhZCBvZiBtYW51YWxseSB3aXRoIHRoZSBjb2RlIGJlbG93PyoqDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0NCmNvdW50c19sdW1pbmFsX25vcm1hbGl6ZWQgPC0gbm9ybUxpYlNpemVzKGRnZSwgbWV0aG9kID0gIlRNTSIpIA0KDQpjb3VudHNfbHVtaW5hbF9ub3JtYWxpemVkIDwtIGNwbShjb3VudHNfbHVtaW5hbF9ub3JtYWxpemVkLCBub3JtYWxpemVkLmxpYi5zaXplcyA9IFRSVUUpIA0KY291bnRzX2x1bWluYWxfbm9ybWFsaXplZF9sb2cyIDwtIGxvZzIoY291bnRzX2x1bWluYWxfbm9ybWFsaXplZCArIDEpDQpgYGANCg0KKipBbnN3ZXI6KiogDQoNClRoaXMgaXMgYmVjYXVzZSB0aGUgbWV0aG9kIHdlIGFyZSB1c2luZyB0byBjYWxsIERFQSAoRGlmZmVyZW50aWFsIEdlbmUgRXhwcmVzc2lvbikgLSAqKmxpbW1hLXZvb20qKiByZXF1aXJlcyB0aGF0IGRhdGEgYmUgZml0IG9udG8gYSBsaW5lYXIgbW9kZWwuDQoNCmNhbGNOb3JtRmFjdG9ycygpIGFuZCBub3JtTGliU2l6ZXMoKSBhcmUgdHdvIFRNTSBub3JtYWxpc2F0aW9uIGFwcHJvYWNoZXMgd2l0aCBkaWZmZXJlbnQgb3V0cHV0cy4gDQoNCm5vcm1MaWJTaXplcygpIG5vcm1hbGx5IHVzZWQgZm9yIGRhdGEgZXhwbG9yYXRpb24gYXMgaXQgbm9ybWFsaXNlcyByZWFkcyBjb3VudHMgKGxpYi5zaXplcykuIFlvdSB3b3VsZCBhbHNvIGhhdmUgdG8gcGVyZm9ybSBjcG0oKSBpbiBhZGRpdGlvbiB0byB0aGlzIGNvbW1tYW5kLiANCg0KY2FsY05vcm1GYWN0b3JzKCkgYWNjb3VudHMgZm9yIHRoZSBleHByZXNzaW9uIG9mIGVhY2ggZ2VuZSByZWxhdGl2ZSB0byB0aGUgb3RoZXIgYW5kIHRoZXJlZm9yZSBpcyBtb3JlIHN1aXRhYmxlIGZvciBER0UgYW5hbHlzaXMuIA0KDQpGb3IgVm9vbSB0cmFuc2Zvcm1hdGlvbiwgdGhlIG1vc3Qgc3VpdGFibGUgb3V0cHV0IGlzIGJ5IGNhbGNOb3JtRmFjdG9ycygpIHdoaWNoIGlzIHRoZW4gdHJhbnNmb3JtZWQgYW5kIGxvZzJDUE0gaXMgYXBwbGllZCBhbGwgd2l0aGluIHRoZSB2b29tKCkgZnVuY3Rpb24uDQoNCg0KIyMjICoqVm9vbSB0cmFuc2Zvcm1hdGlvbjoqKg0KDQoqdm9vbSgpKiBzdGFuZHMgZm9yICJ2YXJpYW5jZSBtb2RlbGluZyBhdCB0aGUgb2JzZXJ2YXRpb25hbCBsZXZlbC4iIEl0IHRyYW5zZm9ybXMgcmF3IFJOQS1zZXEgY291bnRzIGludG8gbG9nMiBjb3VudHMtcGVyLW1pbGxpb24gKGxvZ0NQTSkgd2hpbGUgYWNjb3VudGluZyBmb3IgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXBzIGluIHRoZSBkYXRhLg0KDQpSTkEtc2VxIGNvdW50IGRhdGEgaXMgbm90ICpub3JtYWxseSBkaXN0cmlidXRlZCosIGhvd2V2ZXIsICoqbG1GaXQoKSoqIGFzc3VtZXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgdmFsdWVzIHRoZXJmb3JlICoqdm9vbSgpKiogZXN0aW1hdGVzIG1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwcyBhbmQgdHJhbnNmb3JtcyBkYXRhIHRvIHNhdGlzZnkgdGhpcyBhc3N1bXB0aW9uLg0KDQpBICoqZGVzaWduIG1hdHJpeCoqIG9mIHRoZSBtaWNyb2FycmF5IGV4cGVyaW1lbnQgaXMgbmVlZGVkLiBJdCBjb250YWlucyByb3dzIGNvcnJlc3BvbmRpbmcgdG8gc2FtcGxlcyBhbmQgY29sdW1ucyB0byBjb2VmZmljaWVudHMgdG8gYmUgZXN0aW1hdGVkLiBBbmQgZGVmaW5lcyBpbnRlcmdyb3VwIGNvbXBhcmlzb25zLg0KDQoNCj4gSG93IHdpbGwgdGhlIGdyb3VwcyBiZSBjb21wYXJlZD8gIA0KV2hpY2ggc2hvdWxkIGJlIHRoZSBDb250cm9sIGdyb3VwPyAgRG8gd2UgbmVlZCBvbmU/ICANCg0KDQpgYGB7ciBldmFsPVRSVUUsIGVjaG89VFJVRX0NCiNub3JtYWxpc2VkIGRhdGEgdHJhbnNmb3JtYXRpb24NCg0KI2NyZWF0ZSBkZXNpZ24gbWF0cml4IChlc3NlbnRpYWwgZm9yIHZvb20oKSBhbmQgbG1GaXQoKSkNCiM/bW9kZWwubWF0cml4DQoNCiNkZXNpZ25fbWF0cml4IDwtIG1vZGVsLm1hdHJpeCh+IGdyb3VwX2R2c3QpDQojY29sbmFtZXMoZGVzaWduX21hdHJpeCkgDQoNCiMqKiBPdXRwdXRzIA0KIypbMV0gIihJbnRlcmNlcHQpIiAgICAgICAgICAgICAgICJncm91cF9kdnN0MiBkYXkgbGFjdGF0aW9uIg0KIypbM10gImdyb3VwX2R2c3R2aXJnaW4iIA0KDQoNCiNXSE8gU0hPVUxEIEJFIFRIRSBDT05UUk9MIEdST1VQPz8/IFRoZSBtb2RlbCBtYXRyaXggY2hvc2UgdGhlICIxOC41IGRheSBwcmVnbmFuY3kiIGFzIHRoZSBiYXNlbGluZSBncm91cC4uLiBXaHkgbm90IHRoZSB2aXJnaW4/IA0KDQpkZXNpZ25fbWF0cml4IDwtIG1vZGVsLm1hdHJpeCh+IDAgKyBncm91cF9kdnN0KSAjcmVtb3ZlcyBpbnRlcmNlcHQNCmNvbG5hbWVzKGRlc2lnbl9tYXRyaXgpIA0KDQojdm9vbSB0cmFuc2Zvcm1hdGlvbg0Kdm9vbV90cmFuc2Zvcm1lZCA8LSB2b29tKGNvdW50c19sdW1pbmFsX25vcm1hbGl6ZWQsIGRlc2lnbl9tYXRyaXgsIHBsb3QgPSBUUlVFKQ0KDQpgYGANCg0KRWFjaCBkb3QgcmVwcmVzZW50cyBhIGdlbmUuICANClggYXhpcyByZXByZXNlbnRzIHRoZSBsb2ctdHJhbmZvcm1lZCBnZW5lIGV4cHJlc3Npb24uICANCnkgYXhpcyByZXByZXNudHMgdmFyaWFuY2UgKHNkKSAgDQoNCioqVGFrZSBhd2F5OioqIFZhcmlhbmNlIGRlY3JlYXNlcyB3aXRoIGluY3JlYXNpbmcgZ2VuZSBleHByZXNzaW9uIChSZWQgdHJlbmQgbGluZSkuICANClRoZSBoaWdoZXIgdGhlIGdlbmUgaXMgZXhwcmVzc2VkICp0aGUgbW9yZSBzdXJlKiB3ZSBhcmUgaXQgaXMgdGhlcmUuDQoNCioqTm9ybWFsaXNlZCBEYXRhIC0gQm94IHBsb3QqKlwNCg0KYGBge3IgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0NCiMjIE5vcm1hbGl6ZWQgQ291bnRzIGRpc3RyaWJ1dGlvbg0KDQpsaWJyYXJ5KHRpZHlyKQ0KDQojIENvbnZlcnQgdm9vbSRFIHRvIGRhdGEgZnJhbWUNCnZvb21FX2RmIDwtIGFzLmRhdGEuZnJhbWUodm9vbV90cmFuc2Zvcm1lZCRFKQ0KDQojIEFkZCBzYW1wbGUgbmFtZXMNCnZvb21FX2RmJEdlbmUgPC0gcm93bmFtZXModm9vbV90cmFuc2Zvcm1lZCRFKQ0KDQojIENvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQp2b29tRV9kZl9sb25nIDwtIHBpdm90X2xvbmdlcih2b29tRV9kZiwgY29scyA9IC1HZW5lLCBuYW1lc190byA9ICJTYW1wbGUiLCB2YWx1ZXNfdG8gPSAiRXhwcmVzc2lvbiIpDQoNCnZvb21FX2RmX2xvbmckRXhwcmVzc2lvbiA8LSBhcy5udW1lcmljKHZvb21FX2RmX2xvbmckRXhwcmVzc2lvbikNCg0KIyBDcmVhdGUgYm94cGxvdA0KZ2dwbG90KHZvb21FX2RmX2xvbmcsIGFlcyh4PSAiU2FtcGxlIiwgeT0iRXhwcmVzc2lvbiIpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgZ2d0aXRsZSgiTm9ybWFsaXplZCBEaXN0cmlidXRpb24gb2YgTHVtaW5hbCBDb3VudHMgKGFmdGVyIFZvb20pIikgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkrDQogIHNjYWxlX3lfbG9nMTAoKSsNCiAgeGxhYigiTHVtaW5hbCBTYW1wbGVzIikgKw0KICB5bGFiKCJsb2cxMChFeHByZXNzaW9uKSIpDQoNCmhlYWQodm9vbV90cmFuc2Zvcm1lZCRFLCBuPTUpDQpgYGANCg0KIyMjICoqTGluZWFyIG1vZGVsIGZpdDoqKg0KDQpXZSB3aWxsIGZpdCBvdXIgdm9vbS10cmFuc2Zvcm1lZCBkYXRhIG9uIHRoZSBsaW5lYXIgbW9kZWwgYW5kIHdlIHdpbGwgdmlzdWFsaXNlIG91ciBmaXR0ZWQgZGF0YS4gRm9yIHRoaXMgd2UgaGF2ZSB0byB1c2UgdGhlIG1vZGVsIG1hdHJpeCBpbiB0aGUgKipsbUZpdCgpKiogZnVuY3Rpb24uDQoNCmBgYHtyIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KIz9sbUZpdA0KDQojY3JlYXRlIGZpdCBkYXRhDQpmaXQgPC0gbG1GaXQodm9vbV90cmFuc2Zvcm1lZCwgZGVzaWduID0gIGRlc2lnbl9tYXRyaXgpDQoNCiNjbGFzcyhmaXQpICNmaXQgaXMgIk1BcnJheUxNIiAtIE1pY3JvQXJyYXkgTGluZWFyIE1vZGVsDQpgYGANCg0KQURKVVNURUQgTUVBTj8/IE1FVEhPRCBVU0VEIChUTU0sIEJPTkZFUk9OSSwgSE9MTSwgKipCSCoqKQ0KDQojIyMgKipTdGF0aXN0aWMgdGVzdDoqKg0KDQpXZSB3aWxsIHVzZSB0aGUgZUJheWVzKCkgdGVzdDoNCg0KR2l2ZW4gYSBsaW5lYXIgbW9kZWwgKmZpdCogZnJvbSAqbG1GaXQqLCB3ZSBjYW4gY29tcHV0ZSBtb2RlcmF0ZWQgdC1zdGF0aXN0aWNzLCBtb2RlcmF0ZWQgRi1zdGF0aXN0aWMsIGFuZCBsb2ctb2RkcyBvZiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBieSBlbXBpcmljYWwgQmF5ZXMgbW9kZXJhdGlvbiBvZiB0aGUgc3RhbmRhcmQgZXJyb3JzIHRvd2FyZHMgYSBnbG9iYWwgdmFsdWUuDQoNClRoaXMgZnVuY3Rpb24gaXMgdXNlZCB0byByYW5rIGdlbmVzIGluIG9yZGVyIG9mIGV2aWRlbmNlIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gSXQgdXNlcyBhbiBlbXBpcmljYWwgQmF5ZXMgbWV0aG9kIHRvIHNxdWVlemUgdGhlIGdlbmV3aXNlLXdpc2UgcmVzaWR1YWwgdmFyaWFuY2VzIHRvd2FyZHMgYSBjb21tb24gdmFsdWUgKG9yIHRvd2FyZHMgYSBnbG9iYWwgdHJlbmQpIChTbXl0aCwgMjAwNDsgUGhpcHNvbiBldCBhbCwgMjAxNikuIA0KDQoNCmBgYHtyIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KIz9lQmF5ZXMNCg0KZml0X2ViYXllcyA8LSBlQmF5ZXMoZml0KQ0KDQpgYGANCg0KDQojIyMgT3V0cHV0IE1hdHJpeDoNCg0KV2Ugd2lsbCBnZW5lcmF0ZSB0aGUgb3V0cHV0IG1hdHJpeCBhbmQgc2F2ZSBpdCBhcyBsaW1tYV9HU0U2MDQ1MC5yZHMgdXNpbmcgdGhlIHNhdmVSRFMoKSBmdW5jdGlvbi4NCg0KYGBge3IgZXZhbD1UUlVFLCBlY2hvPVRSVUV9DQojb3V0cHV0IG1hdHJpeCB3aXRoIGZvbGQgY2hhbmdlIGFuZCBhZGoucC12YWx1ZTw9MC4wNSA9IGdlbmUgZXhwcmVzc2lvbiBzaWduaWZpY2FudA0KDQojdG9wVGFibGUgZnVuY3Rpb24gaXMgcGFydCBvZiB0aGUgbGltbWEgbGlicmFyeQ0KI1dlIHdpbGwgdXNlIGl0IHRvIGV4dHJhY3QgdGhlIG91dHB1dCBvciByZXN1bHQgbWF0cml4IA0KDQpyZXN1bHRzX2FsbGdlbmVzIDwtIHRvcFRhYmxlKGZpdF9lYmF5ZXMsIGNvZWYgPSBuY29sKGRlc2lnbl9tYXRyaXgpLCBudW1iZXIgPSBJbmYsIGFkanVzdC5tZXRob2QgPSAiQkgiKQ0KDQpoZWFkKHJlc3VsdHNfYWxsZ2VuZXMpDQpgYGANCg0KDQpjb2VmOiBTcGVjaWZpZXMgY29uZGl0aW9uIHRvIGNvbXBhcmUuICAgDQpudW1iZXIgPSBJbmYsIFJldHVlbnMgYWxsIGdlbmVzLiAgIA0KYWRqdXN0Lm1ldGhvZCA9IOKAnEJI4oCdLCBCZW5qYW1pbmktSG9jaGJlcmcgdG8gYWRqdXN0IHAgdmFsdWVzLiAgDQoNCg0KDQoNCioqU0FWSU5HIFJFU1VMVFMqKiAgDQoNCkFzIHR4dCBmaWxlICp3cml0ZS50YWJsZSgpKiBhbmQgYXMgcmRzICpzYXZlUkRTKCkqDQoNCmBgYHtyIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0Kd3JpdGUudGFibGUocmVzdWx0c19hbGxnZW5lcywgZmlsZT0ibGltbWFfZGlmZmVyZW50aWFsX2V4cHJfcmVzdWx0cy50eHQiLCBzZXA9Ilx0IiwgcXVvdGU9RkFMU0UpDQoNCiM/c2F2ZVJEUw0Kc2F2ZVJEUyhyZXN1bHRzX2FsbGdlbmVzLCBmaWxlPSJERUFfbGltbWFfR1NFNjA0NTAucmRzIikNCg0KYGBgDQoNCg0KDQoqKiogIA0KDQoNCioqU2lnbmlmaWNhbnRseSBESUZGRVJFTlRJQUxZIEVYUFJFU1NFRCBHRU5FUyoqIA0KDQpIb3cgbWFueSBnZW5lcyBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGlmIHdlIGRlZmluZSBhIHRocmVzaG9sZCBvZjogYWRqLnAtdmFsdWVcPD0wLjA1IGFuZCBcfGxvZzIoRkNoKVx8XD4wLjU4ID8NCg0KVGhlc2Ugd2lsbCBiZSBzYXZlZCBhcyAqc2lnR2VuZXMqIA0KDQpgYGB7ciBldmFsPVRSVUUsIGVjaG89VFJVRX0NCg0Kc2lnR2VuZXMgPC0gcmVzdWx0c19hbGxnZW5lc1tyZXN1bHRzX2FsbGdlbmVzJGFkai5QLlZhbDw9MC4wNSAmIHJlc3VsdHNfYWxsZ2VuZXMkbG9nRkM+MC41OCwgXSAjaXQgaXMgdGhlIHJvd3MgdGhhdCB3ZSBhcmUgZmlsdGVyaW5nIGJhc2VkIG9uIHRoZSBkZWZpbmVkIGNyaXRlcmlhIGJ1dCB3ZSB3YW50IGFsbCBjb2x1bW5zIHRvIGNvbWUgYmFjay4NCg0KZGltKHNpZ0dlbmVzKQ0KDQpgYGANClRoZXJlZm9yZSAqKjEyMTQ0KiogZ2VuZXMgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIHdpdGggdGhhdCBkZWZpbmVkIHRocmVzaG9sZC4gIA0KDQogIA0KIA0KIOKXj+KXi+KXj+KXi+KXj+KXi+KXj+KXi+KXjyAgDQogDQogDQoqKlRPUCA1MCBEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMqKiAgDQoNCkZvciB0aGlzIHdlIGhhdmUgdG8gZmlyc3Qgb3JnYW5pc2UgdGhlIHJlc3VsdHMgKmJ5IHRoZSBhZGp1c3RlZCBwIHZhbHVlcy4qICAgDQoNCmBgYHtyIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KDQpzaWdHZW5lcyA8LSBzaWdHZW5lc1tvcmRlcihzaWdHZW5lcyRhZGouUC5WYWwpLCBdICNmcm9tIG1vc3Qgc2lnbmlmaWNhbnQgdG8gbGVhc3Qgc2lnbmlmaWNhbnQNCg0KdDUwX3NpZ0dlbmVzIDwtIGhlYWQoc2lnR2VuZXMsIG49NTApDQoNCnQ1MF9zaWdHZW5lcyA8LSBnZW5lX21ldGFkYXRhW3Jvd25hbWVzKHQ1MF9zaWdHZW5lcyksIGMoIlNZTUJPTCIsICJHRU5FTkFNRSIpXQ0KDQp0NTBfc2lnR2VuZXMNCmBgYA0KQXMgc2VlbiBhYm92ZSBvdXIgZ2VuZSBtZXRhZGF0YSBpcyBub3QgZnVsbHkgYW5ub3RhdGVkIHRoZXJlZm9yZSB3ZSB3aWxsIGFubm90YXRlIGl0IHRvIGhhdmUgYWxsIGdlbmVzIGlkZW50aWZpZWQuICAgDQpGb3IgdGhhdCB3ZSB3aWxsIHVzZSB0aGUgKmxpYnJhcnkob3JnLk1tLmVnLmRiKSogd2hpY2ggY29udGFpbnMgYWxsIHRoZSBpbmZvcm1hdGlvbiByZWxhdGVkIHdpdGggdGhlIE11cyBtdXNjdWx1cyBnZW5vbWUuDQoNCmBgYHtyIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KbGlicmFyeShvcmcuTW0uZWcuZGIpDQojY29sdW1ucyhvcmcuTW0uZWcuZGIpDQoNCmdlbmVfbWV0YWRhdGFfYW5ub3RhdGVkIDwtIHNlbGVjdChvcmcuTW0uZWcuZGIsa2V5cz1yb3duYW1lcyhnZW5lX21ldGFkYXRhKSxjb2x1bW5zPWMoIkVOVFJFWklEIiwiU1lNQk9MIiwiR0VORU5BTUUiKSkNCg0KI1ZpZXcoZ2VuZV9tZXRhZGF0YV9hbm5ub3RhdGVkKSANCiNUaGlzIG9tbWl0ZWQgdGhlIExlbmd0aCBjb2x1bW4gYnV0IGZpbGxlZCB0aGUgc3ltYm9scyBhbmQgZ2VuZSBuYW1lcw0KI1dlIGNhbiBub3cgdXNlIHRoaXMgdG8gaWRlbnRpZnkgdGhlIG1pc3NpbmcgY29sdW1ucyANCnNlbGVjdGlvbiA8LSBoZWFkKHNpZ0dlbmVzLCBuPTUwKQ0KDQp0NTBfc2lnR2VuZXNfY29tcGxldGUgPC0gZ2VuZV9tZXRhZGF0YV9hbm5vdGF0ZWRbZ2VuZV9tZXRhZGF0YV9hbm5vdGF0ZWQkRU5UUkVaSUQgJWluJSByb3duYW1lcyhoZWFkKHNpZ0dlbmVzLCBuPTUwKSksIGMoIlNZTUJPTCIsICJHRU5FTkFNRSIpXQ0KDQpgYGANClRoZSB0YWJsZSBiZWxvdyBzaG93cyB0aGUgdG9wIDIzLzUwIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhY3Jvc3MgYWxsIGRldmVsb3BtZW50YWwgc3RhZ2VzLiAyNyBnZW5lIElEcyB3ZXJlIG5vdCBmb3VuZC4NCg0KYGBge3IgZXZhbD1UUlVFLCBlY2hvPVRSVUV9DQp0NTBfc2lnR2VuZXNfY29tcGxldGUNCmBgYA0KDQoNCiMjIyAqKlJlc3VsdCBWaXN1YWxpemF0aW9uOioqDQoNCioqUHJpbmNpcGFsIENvbXBvbmVudCoqXA0KVG8gc2VlIGRldmVsb3BtZW50YWwgc3RhZ2UgZGlmZmVyZW5jZXMgaW4gU2FtcGxlcy4gIA0KDQpgYGB7ciBldmFsPVRSVUUsIGVjaG89VFJVRX0NCg0KcGNhIDwtIHByY29tcCh0KHZvb21fdHJhbnNmb3JtZWQkRSksIHNjYWxlLiA9IFRSVUUpDQpwY2FfZGF0YV90b19wbG90IDwtIGRhdGEuZnJhbWUoUEMxID0gcGNhJHhbLDFdLCBQQzIgPSBwY2EkeFssMl0sIGdyb3VwID0gZ3JvdXBfZHZzdCkNCg0KZ2dwbG90KHBjYV9kYXRhX3RvX3Bsb3QsIGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvciA9IGdyb3VwX2R2c3QpKSArDQogIGdlb21fcG9pbnQoc2l6ZT00KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiUENBIC0gUk5BLXNlcSBTYW1wbGVzIiwgeCA9ICJQQzEiLCB5ID0gIlBDMiIpDQoNCmBgYA0KDQoNCg0KKipWb2xjYW5vIHBsb3QqKlwNCg0KQmlvbG9naWNhbCByZWxldmFuY2UgdnMgU3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlLiAgDQoNCmBgYHtyIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KDQojVm9sY2FubyBQbG90DQpnZ3Bsb3QocmVzdWx0c19hbGxnZW5lcywgYWVzKHggPSBsb2dGQywgeSA9IC1sb2cxMChhZGouUC5WYWwpKSkgKyANCiAgIyBBbGwgZ2VuZXMgPSBCTEFDSyAoVGhlc2UgZG9uJ3QgY2hhbmdlKQ0KICBnZW9tX3BvaW50KGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjMpICsNCiAgDQogICNPdmVyZXhwcmVzc2VkIGdlbmVzID0gUkVEDQogIGdlb21fcG9pbnQoZGF0YT1yZXN1bHRzX2FsbGdlbmVzW3Jlc3VsdHNfYWxsZ2VuZXMkbG9nRkMgPiAwLjU4ICYgcmVzdWx0c19hbGxnZW5lcyRhZGouUC5WYWwgPD0gMC4wNSxdLCANCiAgICAgICAgICAgICBhZXMoeCA9IGxvZ0ZDLCB5ID0gLWxvZzEwKGFkai5QLlZhbCkpLCBjb2xvcj0icmVkIiwgYWxwaGEgPSAwLjcpICsNCiAgDQogICNVbmRlciBleHByZXNzZWQgZ2VuZXMgPSBCTFVFDQogIGdlb21fcG9pbnQoZGF0YT1yZXN1bHRzX2FsbGdlbmVzW3Jlc3VsdHNfYWxsZ2VuZXMkbG9nRkMgPCAtMC41OCAmIHJlc3VsdHNfYWxsZ2VuZXMkYWRqLlAuVmFsIDw9IDAuMDUsXSwgDQogICAgICAgICAgICAgYWVzKHggPSBsb2dGQywgeSA9IC1sb2cxMChhZGouUC5WYWwpKSwgY29sb3I9ImJsdWUiLCBhbHBoYSA9IDAuNykgKw0KICANCiNyZWYgbGluZXMNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibHVlIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKDAuNTgsIC0wLjU4KSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIpICsNCiAgDQogIHRoZW1lX21pbmltYWwoKSArDQogIA0KICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gUGxvdCAtIERpZmVyZW50aWFsIEV4cHJlc3Npb24iLA0KICAgICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSIsIA0KICAgICAgIHkgPSAiLUxvZzEwIFAtdmFsdWUiKQ0KDQpgYGANCnggYXhpcyA9ICp3aGV0aGVyIGEgZXhwcmVzc2lvbiBvZiBnZW5lIGNoYW5nZXMgaW4gYmV0d2VlbiBncm91cHMqDQoNCnkgYXhpcyA9ICp3aGV0aGVyIHRoZSBnZW5lIGNoYW5nZSBpcyBzaWduaWZpY2FudCBiZXR3ZWVuIGdyb3Vwcy4qDQoNCg0KIyMgKipDb250cmFzdHMgd2l0aCBhIEhlYXRtYXAqKg0KVG9wIDUwIGdlbmVzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpbiBlYWNoIGRldmVsb3BtZW50IHN0YWdlIGFyZSB2aXN1YWxpemVkIGJlbG93IHdpdGggYSBoZWF0bWFwLiANCg0KVGhpcyB3YXMgZWFzaWVyIHRvIGRvIGluIHJlZ2FyZHMgdG8gc3RhdGlzdGljYWwgbWV0aG9kcyBvZiBjb21wYXJpbmcgZ3JvdXAgbGV2ZWxzIGFnYWluc3QgZWFjaCBvdGhlci4NCg0KVmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoaXMgZGF0YSByZXF1aXJlcyB0aGF0IGEgKip6X3Njb3JlKiogYmUgb2J0YWluZWQgZnJvbSB0aGUgdm9vbSB0cmFuc2Zvcm1lZCAobm9ybWFsaXNlZCBkYXRhKSwgdGhpcyBzZXRzIHRoZSBtZWFuIG9mIGVhY2ggc2FtcGxlIHRvIDAgYW5kIGVuYWJsZXMgdXMgdG8gc2VlIHRoZSBtaW5vciBjaGFuZ2VzIGluIGdlbmUgZXhwcmVzc2lvbiB2aXN1YWxseSAoYm90aCBhYm92ZSBhbmQgYmVsb3cgemVybykgDQpcDQoNCmBgYHtyIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KDQojIFNpbmNlIHdlIGFscmVhZHkgVm9vbSB0cmFuc2Zvcm1lZCB0aGUgZGF0YSwgd2UgY2FuIHogdHJhbnNmb3JtIGl0IHdpdGhvdXQgcmVub3JtYWxpemluZy4NClpfc2NvcmVfdm9vbSA8LSB0KHNjYWxlKHQodm9vbV90cmFuc2Zvcm1lZCRFKSwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBUUlVFKSkNCg0KI09yZGVyIGJ5IEZjIGZyb20gQmlnIHRvIFNtYWxsDQpzaWdHZW5lc19vcmRGY2ggPC0gc2lnR2VuZXNbb3JkZXIoc2lnR2VuZXMkbG9nRkMsIGRlY3JlYXNpbmcgPSBUUlVFKSxdDQoNCnN0YWdlX2NvbG9ycyA8LSBjKA0KICAiMTguNSBkYXkgcHJlZ25hbmN5IiA9ICIjRUU0RDA5IiwNCiAgInZpcmdpbiIgPSAiIzIyQkExQSIsICAgICAgICAgICAgDQogICIyIGRheSBsYWN0YXRpb24iID0gIiNBQjIwODciKSAgIA0KDQojIENvbHVtbiBhbm5vdGF0aW9uDQpjb2x1bW5faGEgPC0gSGVhdG1hcEFubm90YXRpb24oDQogIERldmVsb3BtZW50YWxfU3RhZ2UgPSBncm91cF9kdnN0LCAgI2RldiBzdGFnZSBncm91cGluZw0KICBjb2wgPSBsaXN0KERldmVsb3BtZW50YWxfU3RhZ2UgPSBzdGFnZV9jb2xvcnMpICAjZGV2IHN0YWdlIGNvbG9yaW5nDQopDQoNCg0KIyBoZWF0bWFwIGZvciB0b3AgNTAgREUgZ2VuZXMNCkhlYXRtYXAoWl9zY29yZV92b29tW3Jvd25hbWVzKHNpZ0dlbmVzX29yZEZjaClbMTo1MF0sXSwgDQogICAgICAgIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSA1KSwgICAgICAgIA0KICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGNvbHVtbl9oYSAgICAgICAgICAgICAgICAgDQogICAgICAgKQ0KDQoNCmBgYA0KDQpGb3IgZWFjaCBvZiB0aGUgZGV2ZWxvcG1lbnRhbCBzdGFnZXMgKHNhbXBsZXMpLCBJdCBpcyBwb3NzaWJsZSB0byBleHRyYWN0IG92ZXJleHByZXNzZWQgZ2VuZXMgYW5kIHVuZGVyZXhwcmVzc2VkIGdlbmVzIGJ5IHVzaW5nIGEgc2VsZWN0aW9uIGNyaXRlcmlhIG9mIHo+IDEuNSBhbmQgejwgLTEuNSByZXNwZWN0aXZlbHkuIA0KDQpBbHRlcm5hdGl2ZWxleSwgY29udHJhc3RzIGNhbiBiZSBtYWRlIHRvIGRvIHRoZSBzYW1lLiAgDQoNCg0KIyMgKipDb25jbHVzaW9ucyoqDQoNCldlIHdpbGwgcmVwb3J0IG91ciBmaW5kaW5ncyBiYXNlZCBvbiB0aGUgb2JqZWN0aXZlIG9mIHRoaXMgYXNzaWdubWVudCwgKip0byBpZGVudGlmeSB0aGUgdHJhbnNjcmlwdGlvbmFsIGNoYW5nZXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBkZXZlbG9wbWVudCBzdGFnZSBpbiBsdW1pbmFsIGNlbGxzLioqDQoNCjEuIEEgdG90YWwgb2YgKioyNzE3OSoqIGdlbmVzIHdlcmUgc2VxdWVuY2UgZm9yIHRoZSBsdW1pbmFsIGNlbGwgcG9wdWxhdGlvbi4gIA0KDQoyLiBPZiB0aGVzZSAqKjE0NTgyICg1My42NSUpKiogd2VyZSByZXRhaW5lZCBhZnRlciBmaWx0ZXJpbmcgKGRlZmF1bHQgZmlsdGVyQnlFeHByKCkpICAgDQoNCjMuICoqMTIxNDQgKDQ0LjY4JSkqKiBnZW5lcyB3ZXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCAoYWRqLnAtdmFsdWUgPD0wLjA1IGFuZCBsb2cyKEZDaCkgPjAuNTgpLiAgDQoNCioqQWNrbm93bGVkZ2VtZW50cyoqICANCg0KVGhlIGNvbW1lbnRlZCBzY3JpcHRzIGJ5IFRlYWNoZXIgTnVyaWEgd2VyZSBoZWxwZnVsIGluIHJldXNpbmcgdGhlIGNvZGUuDQpXZSBhbHNvIHVzZWQgY2hhdEdQVCB0byBkZWJ1ZyB0aGUgY29kZSBhbmQgdW5kZXJzdGFuZCBhcmd1bWVudHMgYW5kIGZvciBjbGFyaXR5IG9mIHNvbWUgY29uY2VwdHMuIA0KDQoNCiMjICoqUmVmZXJlbmNlcyoqDQoNCjEuICAqRnUsIE4uIFkuLCBSaW9zLCBBLiBDLiwgUGFsLCBCLiwgU29ldGFudG8sIFIuLCBMdW4sIEEuIFQuIEwuLCBMaXUsIEsuLCBCZWNrLCBULiwgQmVzdCwgUy4gQS4sIFZhaWxsYW50LCBGLiwgQm91aWxsZXQsIFAuLCBTdHJhc3NlciwgQS4sIFByZWlzcywgVC4sIFNteXRoLCBHLiBLLiwgTGluZGVtYW4sIEcuIEouLCAmIFZpc3ZhZGVyLCBKLiBFLiAoMjAxNSkuIEVHRi1tZWRpYXRlZCBpbmR1Y3Rpb24gb2YgTWNsLTEgYXQgdGhlIHN3aXRjaCB0byBsYWN0YXRpb24gaXMgZXNzZW50aWFsIGZvciBhbHZlb2xhciBjZWxsIHN1cnZpdmFsLiBOYXR1cmUgQ2VsbCBCaW9sb2d5IDIwMTQgMTc6NCwgMTcoNCksIDM2NeKAkzM3NS4gPGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L25jYjMxMTc+Kg0KDQoyLiAgKkdTRTYwNDUwIChHRU8gZGF0YSkgLSA8aHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1NFNjA0NTA+Kg0KDQozLiAgKmxpbW1hIC0gdm9vbSAoIDxodHRwczovL3JwdWJzLmNvbS9qcmdvbnphbGV6SVNHbG9iYWwvdHJhbnNjcmlwdG9taWNfYW5hbHlzZXM+ICkqDQoNCjQuICAqZWRnZVIgKDxodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL2VkZ2VSL2luc3QvZG9jL2VkZ2VSVXNlcnNHdWlkZS5wZGY+LCBwb2ludHMgMi4xMCwgMi4xMSkqDQoNCjUuICpJZGVudGlmeWluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgdXNpbmcgbGluZWFyIG1vZGVscyAocGFydCAxKSAtIGh0dHBzOi8vZ3RrLXRlYWNoaW5nLmdpdGh1Yi5pby9NaWNyb2FycmF5cy1SLzA2LURpZmZlcmVudGlhbEdlbmVFeHByZXNzaW9uL2luZGV4Lmh0bWwqICAgIA0KDQo2LiAqVXNlIHBhciBtZnJvdyB0byBzcGxpdCBzY3JlZW4gLSBodHRwczovL3ItZ3JhcGgtZ2FsbGVyeS5jb20vNzEtc3BsaXQtc2NyZWVuLXdpdGgtcGFyLW1mcm93Lmh0bWwjOn46dGV4dD1UaGUlMjBwYXIoKSUyMGZ1bmN0aW9uJTIwYWxsb3dzJTIwdG8lMjBzZXQlMjBwYXJhbWV0ZXJzJTIwdG8lMjB0aGUscm93cyUyMGFuZCUyMG51bWJlciUyMG9mJTIwY29sdW1ucy4qICANCg0KNy4gKlBoaXBzb24gQiwgTGVlIFMsIE1hamV3c2tpIElKLCBBbGV4YW5kZXIgV1MsIFNteXRoIEdLICgyMDE2KS4gUm9idXN0IGh5cGVycGFyYW1ldGVyIGVzdGltYXRpb24gcHJvdGVjdHMgYWdhaW5zdCBoeXBlcnZhcmlhYmxlIGdlbmVzIGFuZCBpbXByb3ZlcyBwb3dlciB0byBkZXRlY3QgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIEFubmFscyBvZiBBcHBsaWVkIFN0YXRpc3RpY3MgMTAsIDk0Ni05NjMuIGRvaToxMC4xMjE0LzE2LUFPQVM5MjAqIA0KDQo4LiAqU215dGggR0sgKDIwMDQpLiBMaW5lYXIgbW9kZWxzIGFuZCBlbXBpcmljYWwgQmF5ZXMgbWV0aG9kcyBmb3IgYXNzZXNzaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGluIG1pY3JvYXJyYXkgZXhwZXJpbWVudHMuIFN0YXRpc3RpY2FsIEFwcGxpY2F0aW9ucyBpbiBHZW5ldGljcyBhbmQgTW9sZWN1bGFyIEJpb2xvZ3kgVm9sdW1lIDMsIElzc3VlIDEsIEFydGljbGUgMy4gZG9pOjEwLjIyMDIvMTU0NC02MTE1LjEwMjcuIFNlZSBhbHNvIHRoZSBQcmVwcmludCBWZXJzaW9uIGh0dHBzOi8vZ2tzbXl0aC5naXRodWIuaW8vcHVicy9lYmF5ZXMucGRmIGluY29ycG9yYXRpbmcgY29ycmVjdGlvbnMgdG8gMzAgSnVuZSAyMDA5LioNCjkuICpPcGVuQUkuICgyMDI1KS4gQ2hhdEdQVCAoRmViIDE0IHZlcnNpb24pIFtMYXJnZSBsYW5ndWFnZSBtb2RlbF0uIFJldHJpZXZlZCBmcm9tIGh0dHBzOi8vb3BlbmFpLmNvbSo=