PLAN
- Data Preparation
- Data filtering - filterByExpr()
- Normalize data - using TMM
- Design matrix definition - model.matrix()
- Voom transformation - voom()
- Lineal model fit - lmFit()
- 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.
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.
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=