projDir <- "/mnt/scratcha/bioinformatics/baller01/20200511_FernandesM_ME_crukBiSs2020"
outDirBit <- "AnaWiSce/Attempt1"

In part 1 we gathered the data, aligned reads, checked quality, and normalised read counts. We will now identify genes to focus on, use visualisation to explore the data, collapse the data set, cluster cells by their expression profile and identify genes that best characterise these cell populations. These main steps are shown below [@ANDREWS2018114].

We’ll first explain dimensionality reduction for visualisation, using Principal Component Analysis, t-SNE and UMAP.

1 Dimensionality reduction for visualisation

1.1 Principal Component Analysis

In a single cell RNA-seq (scRNASeq) data set, each cell is described by the expression level of thoushands of genes.

The total number of genes measured is referred to as dimensionality. Each gene measured is one dimension in the space characterising the data set. Many genes will little vary across cells and thus be uninformative when comparing cells. Also, because some genes will have correlated expression patterns, some information is redundant. Moreover, we can represent data in three dimensions, not more. So reducing the number of useful dimensions is necessary.

1.1.1 Description

The data set: a matrix with one row per sample and one variable per column. Here samples are cells and each variable is the normalised read count for a given gene.

The space: each cell is associated to a point in a multi-dimensional space where each gene is a dimension.

The aim: to find a new set of variables defining a space with fewer dimensions while losing as little information as possible.

Out of a set of variables (read counts), PCA defines new variables called Principal Components (PCs) that best capture the variability observed amongst samples (cells), see [@field2012discovering] for example.

The number of variables does not change. Only the fraction of variance captured by each variable differs. The first PC explains the highest proportion of variance possible (bound by prperties of PCA). The second PC explains the highest proportion of variance not explained by the first PC. PCs each explain a decreasing amount of variance not explained by the previous ones. Each PC is a dimension in the new space.

The total amount of variance explained by the first few PCs is usually such that excluding remaining PCs, ie dimensions, loses little information. The stronger the correlation between the initial variables, the stronger the reduction in dimensionality. PCs to keep can be chosen as those capturing at least as much as the average variance per initial variable or using a scree plot, see below.

PCs are linear combinations of the initial variables. PCs represent the same amount of information as the initial set and enable its restoration. The data is not altered. We only look at it in a different way.

About the mapping function from the old to the new space:

  • it is linear
  • it is inverse, to restore the original space
  • it relies on orthogonal PCs so that the total variance remains the same.

Two transformations of the data are necessary:

  • center the data so that the sample mean for each column is 0 so the covariance matrix of the intial matrix takes a simple form
  • scale variance to 1, ie standardize, to avoid PCA loading on variables with large variance.

1.1.2 Example

Here we will make a simple data set of 100 samples and 2 variables, perform PCA and visualise on the initial plane the data set and PCs [@pca_blog_Patcher2014].

library(ggplot2)
fontsize <- theme(axis.text=element_text(size=12), axis.title=element_text(size=16))

Let’s make and plot a data set.

set.seed(123)            #sets the seed for random number generation.
 x <- 1:100              #creates a vector x with numbers from 1 to 100
 ex <- rnorm(100, 0, 30) #100 normally distributed rand. nos. w/ mean=0, s.d.=30
 ey <- rnorm(100, 0, 30) # " " 
 y <- 30 + 2 * x         #sets y to be a vector that is a linear function of x
 x_obs <- x + ex         #adds "noise" to x
 y_obs <- y + ey         #adds "noise" to y
 P <- cbind(x_obs,y_obs) #places points in matrix
 plot(P,asp=1,col=1) #plot points
 points(mean(x_obs),mean(y_obs),col=3, pch=19) #show center

Center the data and compute covariance matrix.

M <- cbind(x_obs - mean(x_obs), y_obs - mean(y_obs)) #centered matrix
MCov <- cov(M)          #creates covariance matrix

Compute the principal axes, ie eigenvectors and corresponding eigenvalues.

An eigenvector is a direction and an eigenvalue is a number measuring the spread of the data in that direction. The eigenvector with the highest eigenvalue is the first principal component.

The eigenvectors of the covariance matrix provide the principal axes, and the eigenvalues quantify the fraction of variance explained in each component.

eigenValues <- eigen(MCov)$values       #compute eigenvalues
eigenVectors <- eigen(MCov)$vectors     #compute eigenvectors

# or use 'singular value decomposition' of the matrix
d <- svd(M)$d          #the singular values
v <- svd(M)$v          #the right singular vectors

Let’s plot the principal axes.

First PC:

# PC 1:
 plot(P,asp=1,col=1) #plot points
 points(mean(x_obs),mean(y_obs),col=3, pch=19) #show center
lines(x_obs,eigenVectors[2,1]/eigenVectors[1,1]*M[x]+mean(y_obs),col=8)

Second PC:

 plot(P,asp=1,col=1) #plot points
 points(mean(x_obs),mean(y_obs),col=3, pch=19) #show center
# PC 1:
lines(x_obs,eigenVectors[2,1]/eigenVectors[1,1]*M[x]+mean(y_obs),col=8)
# PC 2:
lines(x_obs,eigenVectors[2,2]/eigenVectors[1,2]*M[x]+mean(y_obs),col=8)

Add the projections of the points onto the first PC:

plot(P,asp=1,col=1) #plot points
points(mean(x_obs),mean(y_obs),col=3, pch=19) #show center
# PC 1:
lines(x_obs,eigenVectors[2,1]/eigenVectors[1,1]*M[x]+mean(y_obs),col=8)
# PC 2:
lines(x_obs,eigenVectors[2,2]/eigenVectors[1,2]*M[x]+mean(y_obs),col=8)
# add projecions:
trans <- (M%*%v[,1])%*%v[,1] #compute projections of points
P_proj <- scale(trans, center=-cbind(mean(x_obs),mean(y_obs)), scale=FALSE) 
points(P_proj, col=4,pch=19,cex=0.5) #plot projections
segments(x_obs,y_obs,P_proj[,1],P_proj[,2],col=4,lty=2) #connect to points

Compute PCs with prcomp().

pca_res <- prcomp(M)
summary(pca_res)
Importance of components:
                          PC1    PC2
Standard deviation     73.827 28.279
Proportion of Variance  0.872  0.128
Cumulative Proportion   0.872  1.000
var_explained <- pca_res$sdev^2/sum(pca_res$sdev^2)
var_explained
[1] 0.8720537 0.1279463

Check amount of variance captured by PCs on a scree plot.

# Show scree plot:
plot(pca_res)

Plot with ggplot.

df_pc <- data.frame(pca_res$x)
g <- ggplot(df_pc, aes(PC1, PC2)) + 
  geom_point(size=2) +   # draw points
  labs(title="PCA", 
       subtitle="With principal components PC1 and PC2 as X and Y axis") + 
  coord_cartesian(xlim = 1.2 * c(min(df_pc$PC1), max(df_pc$PC1)), 
                  ylim = 1.2 * c(min(df_pc$PC2), max(df_pc$PC2)))
g <- g + geom_hline(yintercept=0)
g <- g + geom_vline(xintercept=0)
g

Or use ggfortify autoplot().

# ggfortify
library(ggfortify)
g <- autoplot(pca_res)
g <- g + geom_hline(yintercept=0)
g <- g + geom_vline(xintercept=0)
g

Going from 2D to 3D (figure from [@nlpcaPlot]):

1.2 Load packages

library(scater) # for QC and plots

1.3 Load data

We will load the R file keeping the SCE object with the normalised counts for 500 cells per sample.

setName <- "caron"
setSuf <- "_5hCellPerSpl"

# Read object in:
tmpFn <- sprintf("%s/%s/Robjects/%s_sce_nz_postDeconv%s.Rds", projDir, outDirBit, setName, setSuf)
print(tmpFn)
[1] "/mnt/scratcha/bioinformatics/baller01/20200511_FernandesM_ME_crukBiSs2020/AnaWiSce/Attempt1/Robjects/caron_sce_nz_postDeconv_5hCellPerSpl.Rds"
if(!file.exists(tmpFn))
{
    knitr::knit_exit()
}
sce <- readRDS(tmpFn)
sce
class: SingleCellExperiment 
dim: 18372 5500 
metadata(0):
assays(2): counts logcounts
rownames(18372): ENSG00000238009 ENSG00000237491 ... ENSG00000275063
  ENSG00000271254
rowData names(11): ensembl_gene_id external_gene_name ... detected
  gene_sparsity
colnames: NULL
colData names(20): Sample Barcode ... cell_sparsity sizeFactor
reducedDimNames(0):
altExpNames(0):
head(rowData(sce))
DataFrame with 6 rows and 11 columns
                ensembl_gene_id external_gene_name chromosome_name
                    <character>        <character>     <character>
ENSG00000238009 ENSG00000238009         AL627309.1               1
ENSG00000237491 ENSG00000237491          LINC01409               1
ENSG00000225880 ENSG00000225880          LINC00115               1
ENSG00000230368 ENSG00000230368             FAM41C               1
ENSG00000230699 ENSG00000230699         AL645608.2               1
ENSG00000188976 ENSG00000188976              NOC2L               1
                start_position end_position    strand      Symbol
                     <integer>    <integer> <integer> <character>
ENSG00000238009          89295       133723        -1  AL627309.1
ENSG00000237491         778747       810065         1  AL669831.5
ENSG00000225880         826206       827522        -1   LINC00115
ENSG00000230368         868071       876903        -1      FAM41C
ENSG00000230699         911435       914948         1  AL645608.3
ENSG00000188976         944203       959309        -1       NOC2L
                           Type        mean   detected gene_sparsity
                    <character>   <numeric>  <numeric>     <numeric>
ENSG00000238009 Gene Expression 0.000815739  0.0815739      0.999256
ENSG00000237491 Gene Expression 0.033200190  3.1966535      0.979330
ENSG00000225880 Gene Expression 0.013530022  1.3200511      0.985855
ENSG00000230368 Gene Expression 0.019421026  1.8757987      0.981826
ENSG00000230699 Gene Expression 0.000642947  0.0638929      0.998270
ENSG00000188976 Gene Expression 0.168154822 13.2828888      0.831308
#any(duplicated(rowData(nz.sce)$ensembl_gene_id))
# some function(s) used below complain about 'strand' already being used in row data,
# so rename that column now:
colnames(rowData(sce))[colnames(rowData(sce)) == "strand"] <- "strandNum"

1.4 PCA

Perform PCA, keep outcome in same object.

nbPcToComp <- 50
# compute PCA:
#sce <- runPCA(sce, ncomponents = nbPcToComp, method = "irlba")
sce <- runPCA(sce, ncomponents = nbPcToComp)

Display scree plot.

# with reducedDim
sce.pca <- reducedDim(sce, "PCA")
attributes(sce.pca)$percentVar
 [1] 15.2999156  9.3354845  4.5969248  3.7569827  2.2441693  1.8559946
 [7]  1.4766071  1.3096737  1.1098978  0.9504496  0.8836664  0.7173686
[13]  0.6430277  0.6102293  0.5772604  0.4536958  0.4116736  0.3936074
[19]  0.3782046  0.3523188  0.3268835  0.3112065  0.2935900  0.2818532
[25]  0.2781618  0.2690670  0.2621160  0.2521110  0.2453925  0.2417972
[31]  0.2382387  0.2329139  0.2317321  0.2285999  0.2248257  0.2241382
[37]  0.2231744  0.2198212  0.2189487  0.2175669  0.2165470  0.2152846
[43]  0.2138223  0.2115537  0.2089565  0.2077544  0.2067207  0.2042463
[49]  0.2040119  0.2030388
barplot(attributes(sce.pca)$percentVar,
        main=sprintf("Scree plot for the %s first PCs", nbPcToComp),
        names.arg=1:nbPcToComp,
        cex.names = 0.8)

Display cells on a plot for the first 2 PCs, colouring by ‘Sample’ and setting size to match ‘total_features’.

The proximity of cells reflects the similarity of their expression profiles.

g <- plotPCA(sce,
        colour_by = "Sample.Name",
        size_by = "sum"
)         
g

One can also split the plot by sample.

g <- g +  facet_wrap(sce$source_name ~ .)
g

Or plot several PCs at once, using plotReducedDim():

plotReducedDim(sce, dimred="PCA", ncomponents=3, 
        colour_by = "Sample.Name") + fontsize

1.4.1 Correlation between PCs and the total number of features detected

The PCA plot above shows cells as symbols whose size depends on the total number of features or library size. It suggests there may be a correlation between PCs and these variables. Let’s check:

r2mat <- getExplanatoryPCs(sce)
Warning in .get_variance_explained(assay(x, exprs_values), variables =
variables, : ignoring 'setName' with fewer than 2 unique levels
Warning in .get_variance_explained(assay(x, exprs_values), variables =
variables, : ignoring 'discard' with fewer than 2 unique levels
r2mat
       Sample Barcode      Run Sample.Name source_name      block         sum
PC1  32.38640     100 32.38640    32.38640   8.4986462  8.4986462  0.58110841
PC2  47.80688     100 47.80688    47.80688  35.3418361 35.3418361  1.85421786
PC3  32.75978     100 32.75978    32.75978  14.6767195 14.6767195 44.34756096
PC4  55.99486     100 55.99486    55.99486  38.9799200 38.9799200  3.03070349
PC5  28.24749     100 28.24749    28.24749   7.8050559  7.8050559  3.72489579
PC6  41.88543     100 41.88543    41.88543  31.4250588 31.4250588  0.42052937
PC7  67.52140     100 67.52140    67.52140  55.9836036 55.9836036  0.90308302
PC8  34.71980     100 34.71980    34.71980  17.0250718 17.0250718  2.59669739
PC9  14.28924     100 14.28924    14.28924   0.7097667  0.7097667  0.78226768
PC10 40.42089     100 40.42089    40.42089   4.9130312  4.9130312  0.08101985
        detected percent_top_50 percent_top_100 percent_top_200 percent_top_500
PC1   5.10606516    37.12952734     20.24995118     14.44253398      7.54488853
PC2   2.70714393    44.10224023     58.18308601     57.72742188     29.01344442
PC3  49.60355537     2.22943741      2.66854556      6.19758430     25.85336598
PC4   1.34946212     1.24120434      2.19054145      1.19146786      0.99305621
PC5   6.15721522     1.90417550      2.45830122      3.37462685      6.50580537
PC6   1.37903530     0.07333369      0.29860955      1.26890679      5.51767508
PC7   0.09688156     2.60892250      2.64570737      2.31569588      1.57043418
PC8   2.84075090     0.66151948      0.83007858      0.90919973      1.15000522
PC9   0.70593085     0.07163045      0.04900552      0.08439033      0.00211341
PC10  0.04951289     0.12988712      0.19737690      0.32858007      0.31489508
     subsets_Mito_sum subsets_Mito_detected subsets_Mito_percent       total
PC1       6.031782854            16.1920172            9.8272620  0.58110841
PC2       0.374532007             2.8225720           10.5752699  1.85421786
PC3      25.627700690            14.4055366            0.3414742 44.34756096
PC4       6.966131262             3.7827424            2.7006483  3.03070349
PC5       5.373659131             9.9353426            4.7529521  3.72489579
PC6       0.320199729             6.0125147            1.3606729  0.42052937
PC7       3.216854677             1.7328138            6.3300506  0.90308302
PC8       0.001606446             1.0963393           12.4357818  2.59669739
PC9       8.263537293            14.6306191           31.2286456  0.78226768
PC10      0.114705832             0.2138113            0.5220398  0.08101985
     setName discard cell_sparsity   sizeFactor
PC1       NA      NA    5.10580877  4.625288747
PC2       NA      NA    2.70789745  1.199340088
PC3       NA      NA   49.60164087 42.776596791
PC4       NA      NA    1.35174775  0.438052834
PC5       NA      NA    6.15802883  4.108075966
PC6       NA      NA    1.37842626  0.365211036
PC7       NA      NA    0.09681848  0.158464104
PC8       NA      NA    2.84093898  2.989803463
PC9       NA      NA    0.70638354  1.077646110
PC10      NA      NA    0.04948949  0.004907248
dat <- cbind(colData(sce)[,c("Sample.Name",
                 "source_name",
                 "sum",
                 "detected",
                 "percent_top_200",
                 "subsets_Mito_percent")],
                 reducedDim(sce,"PCA"))
dat <- data.frame(dat)
dat$sum <- log2(dat$sum)
ggplot(dat, aes(x=sum, y=PC1, shape=source_name, col=Sample.Name)) +
    geom_point() +
    geom_smooth(method=lm, inherit.aes = FALSE, aes(x=sum, y=PC1)) 
`geom_smooth()` using formula 'y ~ x'

ggplot(dat, aes(x=percent_top_200, y=PC2, shape=source_name, col=Sample.Name)) +
    geom_point() +
    geom_smooth(method=lm, inherit.aes = FALSE, aes(x=percent_top_200, y=PC2)) 
`geom_smooth()` using formula 'y ~ x'

ggplot(dat, aes(x=detected, y=PC3, shape=source_name, col=Sample.Name)) +
    geom_point() +
    geom_smooth(method=lm, inherit.aes = FALSE, aes(x=detected, y=PC3)) 
`geom_smooth()` using formula 'y ~ x'

ggplot(dat, aes(x=subsets_Mito_percent, y=PC2, shape=source_name, col=Sample.Name)) +
    geom_point() +
    geom_smooth(method=lm, inherit.aes = FALSE, aes(x=subsets_Mito_percent, y=PC2)) 
`geom_smooth()` using formula 'y ~ x'

ggplot(dat, aes(x=source_name, y=PC7, shape=source_name, col=Sample.Name)) +
    geom_boxplot()

1.5 t-SNE: t-Distributed Stochastic Neighbor Embedding

The Stochastic Neighbor Embedding (SNE) approach address two shortcomings of PCA that captures the global covariance structure with a linear combination of initial variables: by preserving the local structure allowing for non-linear projections. It uses two distributions of the pairwise similarities between data points: in the input data set and in the low-dimensional space.

SNE aims at preserving neighbourhoods. For each points, it computes probabilities of chosing each other point as its neighbour based on a Normal distribution depending on 1) the distance matrix and 2) the size of the neighbourhood (perplexity). SNE aims at finding a low-dimension space (eg 2D-plane) such that the similarity matrix deriving from it is as similar as possible as that from the high-dimension space. To address the fact that in low dimension, points are brought together, the similarity matrix in the low-dimension is allowed to follow a t-distribution.

Two characteristics matter:

  • perplexity, to indicate the relative importance of the local and global patterns in structure of the data set, usually use a value of 50,
  • stochasticity; running the analysis will produce a different map every time, unless the seed is set.

See misread-tsne.

1.5.1 Perplexity

Compute t-SNE with default perplexity, ie 50.

# runTSNE default perpexity if min(50, floor(ncol(object)/5))
sce <- runTSNE(sce, dimred="PCA", perplexity=50, rand_seed=123)

Plot t-SNE:

tsne50 <- plotTSNE(sce,
           colour_by="Sample.Name",
           size_by="sum") + 
           fontsize + 
           ggtitle("Perplexity = 50")
tsne50

Compute t-SNE for several perplexity values:

tsne5.run <- runTSNE(sce, use_dimred="PCA", perplexity=5, rand_seed=123)
tsne5 <- plotTSNE(tsne5.run, colour_by="Sample.Name") + fontsize + ggtitle("Perplexity = 5")

#tsne200.run <- runTSNE(sce, use_dimred="PCA", perplexity=200, rand_seed=123)
#tsne200 <- plotTSNE(tsne200.run, colour_by="Sample.Name") + fontsize + ggtitle("Perplexity = 200")

tsne500.run <- runTSNE(sce, use_dimred="PCA", perplexity=500, rand_seed=123)
tsne500 <- plotTSNE(tsne500.run, colour_by="Sample.Name") + fontsize + ggtitle("Perplexity = 500")

#tsne1000.run <- runTSNE(sce, use_dimred="PCA", perplexity=1000, rand_seed=123)
#tsne1000 <- plotTSNE(tsne1000.run, colour_by="Sample.Name") + fontsize + ggtitle("Perplexity = 1000")
tsne5

#tsne50
#tsne200
tsne500

1.5.2 Stochasticity

Use a different seed with the same perplexity 50.

tsne50.b <- runTSNE(sce, use_dimred="PCA", perplexity=50, rand_seed=456)

tsne50.b <- plotTSNE(tsne50.b,
           colour_by="Sample.Name",
           size_by="sum") + 
         fontsize + 
         ggtitle("Perplexity = 50, seed 456")
tsne50.b

1.6 UMAP

Another neighbour graph method. Similar to t-SNE, but that is determistic, faster and claims to preserve both local and global structures.

Compute UMAP.

set.seed(123)
sce <- runUMAP(sce, dimred="PCA")

Plot UMAP:

sce.umap <- plotUMAP(sce,
           colour_by="Sample.Name",
           size_by="sum") + 
           fontsize + 
           ggtitle("UMAP")
sce.umap

Save SCE object:

tmpFn <- sprintf("%s/%s/Robjects/%s_sce_nz_postDeconv%s_dimRed.Rds", projDir, outDirBit, setName, setSuf)
saveRDS(sce, tmpFn)
LS0tCnRpdGxlOiAiQ1JVSyBDSSBTdW1tZXIgU2Nob29sIDIwMjAgLSBpbnRyb2R1Y3Rpb24gdG8gc2luZ2xlLWNlbGwgUk5BLXNlcSBhbmFseXNpcyIKc3VidGl0bGU6ICdEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gZm9yIHZpc3VhbGlzYXRpb24nCgphdXRob3I6ICJTdGVwaGFuZSBCYWxsZXJlYXUsIFpleW5lcCBLYWxlbmRlciBBdGFrLCBLYXRhcnp5bmEgS2FuaWEiCiNkYXRlOiAnYHIgc3RyZnRpbWUoU3lzLnRpbWUoKSwgZm9ybWF0ID0gIiVCICVkLCAlWSIpYCcKZGF0ZTogSnVseSAyMDIwCiNiaWJsaW9ncmFwaHk6IGJpYmxpb2dyYXBoeS5iaWIKI2NzbDogYmlvbWVkLWNlbnRyYWwuY3NsCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgZmlnX2NhcHRpb246IHllcwogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIGZpZ193aWR0aDogNgogICAgZmlnX2hlaWdodDogNAotLS0KCjwhLS0KU2hvdWxkIGhhdmUgQ2Fyb24gYW5kIEhDQSBzZXBhcmF0ZWx5Ci0tPgoKYGBge3J9CnByb2pEaXIgPC0gIi9tbnQvc2NyYXRjaGEvYmlvaW5mb3JtYXRpY3MvYmFsbGVyMDEvMjAyMDA1MTFfRmVybmFuZGVzTV9NRV9jcnVrQmlTczIwMjAiCm91dERpckJpdCA8LSAiQW5hV2lTY2UvQXR0ZW1wdDEiCmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIGVjaG89RkFMU0V9CiMgRmlyc3QsIHNldCBzb21lIHZhcmlhYmxlczoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKc2V0LnNlZWQoMTIzKSAjIGZvciByZXByb2R1Y2liaWxpdHkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWwgPSBUUlVFKSAKYGBgCgpJbiBwYXJ0IDEgd2UgZ2F0aGVyZWQgdGhlIGRhdGEsIGFsaWduZWQgcmVhZHMsIGNoZWNrZWQgcXVhbGl0eSwgYW5kIG5vcm1hbGlzZWQgcmVhZCBjb3VudHMuIFdlIHdpbGwgbm93IGlkZW50aWZ5IGdlbmVzIHRvIGZvY3VzIG9uLCB1c2UgdmlzdWFsaXNhdGlvbiB0byBleHBsb3JlIHRoZSBkYXRhLCBjb2xsYXBzZSB0aGUgZGF0YSBzZXQsIGNsdXN0ZXIgY2VsbHMgYnkgdGhlaXIgZXhwcmVzc2lvbiBwcm9maWxlIGFuZCBpZGVudGlmeSBnZW5lcyB0aGF0IGJlc3QgY2hhcmFjdGVyaXNlIHRoZXNlIGNlbGwgcG9wdWxhdGlvbnMuIFRoZXNlIG1haW4gc3RlcHMgYXJlIHNob3duIGJlbG93IFtAQU5EUkVXUzIwMTgxMTRdLiAKCjxpbWcgc3JjPSIuLi8uLi9JbWFnZXMvQW5kcmV3czIwMTdfRmlnMS5wbmciIHN0eWxlPSJtYXJnaW46YXV0bzsgZGlzcGxheTpibG9jayIgLz4KCldlJ2xsIGZpcnN0IGV4cGxhaW4gZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGZvciB2aXN1YWxpc2F0aW9uLCB1c2luZyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzLCB0LVNORSBhbmQgVU1BUC4KCiMgRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGZvciB2aXN1YWxpc2F0aW9uCgojIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzCgpJbiBhIHNpbmdsZSBjZWxsIFJOQS1zZXEgKHNjUk5BU2VxKSBkYXRhIHNldCwgZWFjaCBjZWxsIGlzIGRlc2NyaWJlZCBieSB0aGUgZXhwcmVzc2lvbiBsZXZlbCBvZiB0aG91c2hhbmRzIG9mIGdlbmVzLgoKVGhlIHRvdGFsIG51bWJlciBvZiBnZW5lcyBtZWFzdXJlZCBpcyByZWZlcnJlZCB0byBhcyBkaW1lbnNpb25hbGl0eS4gRWFjaCBnZW5lIG1lYXN1cmVkIGlzIG9uZSBkaW1lbnNpb24gaW4gdGhlIHNwYWNlIGNoYXJhY3RlcmlzaW5nIHRoZSBkYXRhIHNldC4gTWFueSBnZW5lcyB3aWxsIGxpdHRsZSB2YXJ5IGFjcm9zcyBjZWxscyBhbmQgdGh1cyBiZSB1bmluZm9ybWF0aXZlIHdoZW4gY29tcGFyaW5nIGNlbGxzLiBBbHNvLCBiZWNhdXNlIHNvbWUgZ2VuZXMgd2lsbCBoYXZlIGNvcnJlbGF0ZWQgZXhwcmVzc2lvbiBwYXR0ZXJucywgc29tZSBpbmZvcm1hdGlvbiBpcyByZWR1bmRhbnQuIE1vcmVvdmVyLCB3ZSBjYW4gcmVwcmVzZW50IGRhdGEgaW4gdGhyZWUgZGltZW5zaW9ucywgbm90IG1vcmUuIFNvIHJlZHVjaW5nIHRoZSBudW1iZXIgb2YgdXNlZnVsIGRpbWVuc2lvbnMgaXMgbmVjZXNzYXJ5LgoKIyMjIERlc2NyaXB0aW9uCgpUaGUgZGF0YSBzZXQ6IGEgbWF0cml4IHdpdGggb25lIHJvdyBwZXIgc2FtcGxlIGFuZCBvbmUgdmFyaWFibGUgcGVyIGNvbHVtbi4gSGVyZSBzYW1wbGVzIGFyZSBjZWxscyBhbmQgZWFjaCB2YXJpYWJsZSBpcyB0aGUgbm9ybWFsaXNlZCByZWFkIGNvdW50IGZvciBhIGdpdmVuIGdlbmUuCgpUaGUgc3BhY2U6IGVhY2ggY2VsbCBpcyBhc3NvY2lhdGVkIHRvIGEgcG9pbnQgaW4gYSBtdWx0aS1kaW1lbnNpb25hbCBzcGFjZSB3aGVyZSBlYWNoIGdlbmUgaXMgYSBkaW1lbnNpb24uCgpUaGUgYWltOiB0byBmaW5kIGEgbmV3IHNldCBvZiB2YXJpYWJsZXMgZGVmaW5pbmcgYSBzcGFjZSB3aXRoIGZld2VyIGRpbWVuc2lvbnMgd2hpbGUgbG9zaW5nIGFzIGxpdHRsZSBpbmZvcm1hdGlvbiBhcyBwb3NzaWJsZS4KCk91dCBvZiBhIHNldCBvZiB2YXJpYWJsZXMgKHJlYWQgY291bnRzKSwgUENBIGRlZmluZXMgbmV3IHZhcmlhYmxlcyBjYWxsZWQgUHJpbmNpcGFsIENvbXBvbmVudHMgKFBDcykgdGhhdCBiZXN0IGNhcHR1cmUgdGhlIHZhcmlhYmlsaXR5IG9ic2VydmVkIGFtb25nc3Qgc2FtcGxlcyAoY2VsbHMpLCBzZWUgW0BmaWVsZDIwMTJkaXNjb3ZlcmluZ10gZm9yIGV4YW1wbGUuCgpUaGUgbnVtYmVyIG9mIHZhcmlhYmxlcyBkb2VzIG5vdCBjaGFuZ2UuIE9ubHkgdGhlIGZyYWN0aW9uIG9mIHZhcmlhbmNlIGNhcHR1cmVkIGJ5IGVhY2ggdmFyaWFibGUgZGlmZmVycy4KVGhlIGZpcnN0IFBDIGV4cGxhaW5zIHRoZSBoaWdoZXN0IHByb3BvcnRpb24gb2YgdmFyaWFuY2UgcG9zc2libGUgKGJvdW5kIGJ5IHBycGVydGllcyBvZiBQQ0EpLgpUaGUgc2Vjb25kIFBDIGV4cGxhaW5zIHRoZSBoaWdoZXN0IHByb3BvcnRpb24gb2YgdmFyaWFuY2Ugbm90IGV4cGxhaW5lZCBieSB0aGUgZmlyc3QgUEMuClBDcyBlYWNoIGV4cGxhaW4gYSBkZWNyZWFzaW5nIGFtb3VudCBvZiB2YXJpYW5jZSBub3QgZXhwbGFpbmVkIGJ5IHRoZSBwcmV2aW91cyBvbmVzLgpFYWNoIFBDIGlzIGEgZGltZW5zaW9uIGluIHRoZSBuZXcgc3BhY2UuCgpUaGUgdG90YWwgYW1vdW50IG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBieSB0aGUgZmlyc3QgZmV3IFBDcyBpcyB1c3VhbGx5IHN1Y2ggdGhhdCBleGNsdWRpbmcgcmVtYWluaW5nIFBDcywgaWUgZGltZW5zaW9ucywgbG9zZXMgbGl0dGxlIGluZm9ybWF0aW9uLiBUaGUgc3Ryb25nZXIgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGluaXRpYWwgdmFyaWFibGVzLCB0aGUgc3Ryb25nZXIgdGhlIHJlZHVjdGlvbiBpbiBkaW1lbnNpb25hbGl0eS4gUENzIHRvIGtlZXAgY2FuIGJlIGNob3NlbiBhcyB0aG9zZSBjYXB0dXJpbmcgYXQgbGVhc3QgYXMgbXVjaCBhcyB0aGUgYXZlcmFnZSB2YXJpYW5jZSBwZXIgaW5pdGlhbCB2YXJpYWJsZSBvciB1c2luZyBhIHNjcmVlIHBsb3QsIHNlZSBiZWxvdy4KClBDcyBhcmUgbGluZWFyIGNvbWJpbmF0aW9ucyBvZiB0aGUgaW5pdGlhbCB2YXJpYWJsZXMuIFBDcyByZXByZXNlbnQgdGhlIHNhbWUgYW1vdW50IG9mIGluZm9ybWF0aW9uIGFzIHRoZSBpbml0aWFsIHNldCBhbmQgZW5hYmxlIGl0cyByZXN0b3JhdGlvbi4gVGhlIGRhdGEgaXMgbm90IGFsdGVyZWQuIFdlIG9ubHkgbG9vayBhdCBpdCBpbiBhIGRpZmZlcmVudCB3YXkuCgpBYm91dCB0aGUgbWFwcGluZyBmdW5jdGlvbiBmcm9tIHRoZSBvbGQgdG8gdGhlIG5ldyBzcGFjZToKCi0gaXQgaXMgbGluZWFyCi0gaXQgaXMgaW52ZXJzZSwgdG8gcmVzdG9yZSB0aGUgb3JpZ2luYWwgc3BhY2UKLSBpdCByZWxpZXMgb24gb3J0aG9nb25hbCBQQ3Mgc28gdGhhdCB0aGUgdG90YWwgdmFyaWFuY2UgcmVtYWlucyB0aGUgc2FtZS4KClR3byB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIGRhdGEgYXJlIG5lY2Vzc2FyeToKCi0gY2VudGVyIHRoZSBkYXRhIHNvIHRoYXQgdGhlIHNhbXBsZSBtZWFuIGZvciBlYWNoIGNvbHVtbiBpcyAwIHNvIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBvZiB0aGUgaW50aWFsIG1hdHJpeCB0YWtlcyBhIHNpbXBsZSBmb3JtCi0gc2NhbGUgdmFyaWFuY2UgdG8gMSwgaWUgc3RhbmRhcmRpemUsIHRvIGF2b2lkIFBDQSBsb2FkaW5nIG9uIHZhcmlhYmxlcyB3aXRoIGxhcmdlIHZhcmlhbmNlLgoKIyMjIEV4YW1wbGUKCkhlcmUgd2Ugd2lsbCBtYWtlIGEgc2ltcGxlIGRhdGEgc2V0IG9mIDEwMCBzYW1wbGVzIGFuZCAyIHZhcmlhYmxlcywgcGVyZm9ybSBQQ0EgYW5kIHZpc3VhbGlzZSBvbiB0aGUgaW5pdGlhbCBwbGFuZSB0aGUgZGF0YSBzZXQgYW5kIFBDcyBbQHBjYV9ibG9nX1BhdGNoZXIyMDE0XS4KCmBgYHtyIGxvYWRfcGFja2FnZXMsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKZm9udHNpemUgPC0gdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNikpCmBgYAoKTGV0J3MgbWFrZSBhbmQgcGxvdCBhIGRhdGEgc2V0LgoKYGBge3IgcGNhX3RveV9zZXR9CnNldC5zZWVkKDEyMykgICAgICAgICAgICAjc2V0cyB0aGUgc2VlZCBmb3IgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uLgogeCA8LSAxOjEwMCAgICAgICAgICAgICAgI2NyZWF0ZXMgYSB2ZWN0b3IgeCB3aXRoIG51bWJlcnMgZnJvbSAxIHRvIDEwMAogZXggPC0gcm5vcm0oMTAwLCAwLCAzMCkgIzEwMCBub3JtYWxseSBkaXN0cmlidXRlZCByYW5kLiBub3MuIHcvIG1lYW49MCwgcy5kLj0zMAogZXkgPC0gcm5vcm0oMTAwLCAwLCAzMCkgIyAiICIgCiB5IDwtIDMwICsgMiAqIHggICAgICAgICAjc2V0cyB5IHRvIGJlIGEgdmVjdG9yIHRoYXQgaXMgYSBsaW5lYXIgZnVuY3Rpb24gb2YgeAogeF9vYnMgPC0geCArIGV4ICAgICAgICAgI2FkZHMgIm5vaXNlIiB0byB4CiB5X29icyA8LSB5ICsgZXkgICAgICAgICAjYWRkcyAibm9pc2UiIHRvIHkKIFAgPC0gY2JpbmQoeF9vYnMseV9vYnMpICNwbGFjZXMgcG9pbnRzIGluIG1hdHJpeAogcGxvdChQLGFzcD0xLGNvbD0xKSAjcGxvdCBwb2ludHMKIHBvaW50cyhtZWFuKHhfb2JzKSxtZWFuKHlfb2JzKSxjb2w9MywgcGNoPTE5KSAjc2hvdyBjZW50ZXIKYGBgCgpDZW50ZXIgdGhlIGRhdGEgYW5kIGNvbXB1dGUgY292YXJpYW5jZSBtYXRyaXguCgpgYGB7ciBwY2FfY292X3Zhcn0KTSA8LSBjYmluZCh4X29icyAtIG1lYW4oeF9vYnMpLCB5X29icyAtIG1lYW4oeV9vYnMpKSAjY2VudGVyZWQgbWF0cml4Ck1Db3YgPC0gY292KE0pICAgICAgICAgICNjcmVhdGVzIGNvdmFyaWFuY2UgbWF0cml4CmBgYAoKQ29tcHV0ZSB0aGUgcHJpbmNpcGFsIGF4ZXMsIGllIGVpZ2VudmVjdG9ycyBhbmQgY29ycmVzcG9uZGluZyBlaWdlbnZhbHVlcy4KCkFuIGVpZ2VudmVjdG9yIGlzIGEgZGlyZWN0aW9uIGFuZCBhbiBlaWdlbnZhbHVlIGlzIGEgbnVtYmVyIG1lYXN1cmluZyB0aGUgc3ByZWFkIG9mIHRoZSBkYXRhIGluIHRoYXQgZGlyZWN0aW9uLiBUaGUgZWlnZW52ZWN0b3Igd2l0aCB0aGUgaGlnaGVzdCBlaWdlbnZhbHVlIGlzIHRoZSBmaXJzdCBwcmluY2lwYWwgY29tcG9uZW50LgoKVGhlIGVpZ2VudmVjdG9ycyBvZiB0aGUgY292YXJpYW5jZSBtYXRyaXggcHJvdmlkZSB0aGUgcHJpbmNpcGFsIGF4ZXMsIGFuZCB0aGUgZWlnZW52YWx1ZXMgcXVhbnRpZnkgdGhlIGZyYWN0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBpbiBlYWNoIGNvbXBvbmVudC4KCmBgYHtyIHBjYV9laWdlbn0KZWlnZW5WYWx1ZXMgPC0gZWlnZW4oTUNvdikkdmFsdWVzICAgICAgICNjb21wdXRlIGVpZ2VudmFsdWVzCmVpZ2VuVmVjdG9ycyA8LSBlaWdlbihNQ292KSR2ZWN0b3JzICAgICAjY29tcHV0ZSBlaWdlbnZlY3RvcnMKCiMgb3IgdXNlICdzaW5ndWxhciB2YWx1ZSBkZWNvbXBvc2l0aW9uJyBvZiB0aGUgbWF0cml4CmQgPC0gc3ZkKE0pJGQgICAgICAgICAgI3RoZSBzaW5ndWxhciB2YWx1ZXMKdiA8LSBzdmQoTSkkdiAgICAgICAgICAjdGhlIHJpZ2h0IHNpbmd1bGFyIHZlY3RvcnMKYGBgCgpMZXQncyBwbG90IHRoZSBwcmluY2lwYWwgYXhlcy4KCkZpcnN0IFBDOgoKYGBge3IgcGNhX3Nob3dfUEMxfQojIFBDIDE6CiBwbG90KFAsYXNwPTEsY29sPTEpICNwbG90IHBvaW50cwogcG9pbnRzKG1lYW4oeF9vYnMpLG1lYW4oeV9vYnMpLGNvbD0zLCBwY2g9MTkpICNzaG93IGNlbnRlcgpsaW5lcyh4X29icyxlaWdlblZlY3RvcnNbMiwxXS9laWdlblZlY3RvcnNbMSwxXSpNW3hdK21lYW4oeV9vYnMpLGNvbD04KQpgYGAKClNlY29uZCBQQzoKCmBgYHtyIHBjYV9zaG93X1BDMn0KIHBsb3QoUCxhc3A9MSxjb2w9MSkgI3Bsb3QgcG9pbnRzCiBwb2ludHMobWVhbih4X29icyksbWVhbih5X29icyksY29sPTMsIHBjaD0xOSkgI3Nob3cgY2VudGVyCiMgUEMgMToKbGluZXMoeF9vYnMsZWlnZW5WZWN0b3JzWzIsMV0vZWlnZW5WZWN0b3JzWzEsMV0qTVt4XSttZWFuKHlfb2JzKSxjb2w9OCkKIyBQQyAyOgpsaW5lcyh4X29icyxlaWdlblZlY3RvcnNbMiwyXS9laWdlblZlY3RvcnNbMSwyXSpNW3hdK21lYW4oeV9vYnMpLGNvbD04KQpgYGAKCkFkZCB0aGUgcHJvamVjdGlvbnMgb2YgdGhlIHBvaW50cyBvbnRvIHRoZSBmaXJzdCBQQzoKCmBgYHtyIHBjYV9hZGRfcHJvamVjdGlvbl9vbnRvX1BDMX0KcGxvdChQLGFzcD0xLGNvbD0xKSAjcGxvdCBwb2ludHMKcG9pbnRzKG1lYW4oeF9vYnMpLG1lYW4oeV9vYnMpLGNvbD0zLCBwY2g9MTkpICNzaG93IGNlbnRlcgojIFBDIDE6CmxpbmVzKHhfb2JzLGVpZ2VuVmVjdG9yc1syLDFdL2VpZ2VuVmVjdG9yc1sxLDFdKk1beF0rbWVhbih5X29icyksY29sPTgpCiMgUEMgMjoKbGluZXMoeF9vYnMsZWlnZW5WZWN0b3JzWzIsMl0vZWlnZW5WZWN0b3JzWzEsMl0qTVt4XSttZWFuKHlfb2JzKSxjb2w9OCkKIyBhZGQgcHJvamVjaW9uczoKdHJhbnMgPC0gKE0lKiV2WywxXSklKiV2WywxXSAjY29tcHV0ZSBwcm9qZWN0aW9ucyBvZiBwb2ludHMKUF9wcm9qIDwtIHNjYWxlKHRyYW5zLCBjZW50ZXI9LWNiaW5kKG1lYW4oeF9vYnMpLG1lYW4oeV9vYnMpKSwgc2NhbGU9RkFMU0UpIApwb2ludHMoUF9wcm9qLCBjb2w9NCxwY2g9MTksY2V4PTAuNSkgI3Bsb3QgcHJvamVjdGlvbnMKc2VnbWVudHMoeF9vYnMseV9vYnMsUF9wcm9qWywxXSxQX3Byb2pbLDJdLGNvbD00LGx0eT0yKSAjY29ubmVjdCB0byBwb2ludHMKYGBgCgpDb21wdXRlIFBDcyB3aXRoIHByY29tcCgpLgoKYGBge3IgcGNhX3ByY29tcH0KcGNhX3JlcyA8LSBwcmNvbXAoTSkKYGBgCgpgYGB7ciBwY2Ffc3VtbWFyeX0Kc3VtbWFyeShwY2FfcmVzKQpgYGAKCmBgYHtyIHBjYV92YXJFeHBsYWluZWR9CnZhcl9leHBsYWluZWQgPC0gcGNhX3JlcyRzZGV2XjIvc3VtKHBjYV9yZXMkc2Rldl4yKQp2YXJfZXhwbGFpbmVkCmBgYAoKQ2hlY2sgYW1vdW50IG9mIHZhcmlhbmNlIGNhcHR1cmVkIGJ5IFBDcyBvbiBhIHNjcmVlIHBsb3QuCgpgYGB7ciBwY2Ffc2NyZWV9CiMgU2hvdyBzY3JlZSBwbG90OgpwbG90KHBjYV9yZXMpCmBgYAoKUGxvdCB3aXRoIGdncGxvdC4KCmBgYHtyIHBjYV9zaG93X1BDX3BsYW5lX3dpdGhfZ2dwbG90fQpkZl9wYyA8LSBkYXRhLmZyYW1lKHBjYV9yZXMkeCkKZyA8LSBnZ3Bsb3QoZGZfcGMsIGFlcyhQQzEsIFBDMikpICsgCiAgZ2VvbV9wb2ludChzaXplPTIpICsgICAjIGRyYXcgcG9pbnRzCiAgbGFicyh0aXRsZT0iUENBIiwgCiAgICAgICBzdWJ0aXRsZT0iV2l0aCBwcmluY2lwYWwgY29tcG9uZW50cyBQQzEgYW5kIFBDMiBhcyBYIGFuZCBZIGF4aXMiKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gMS4yICogYyhtaW4oZGZfcGMkUEMxKSwgbWF4KGRmX3BjJFBDMSkpLCAKICAgICAgICAgICAgICAgICAgeWxpbSA9IDEuMiAqIGMobWluKGRmX3BjJFBDMiksIG1heChkZl9wYyRQQzIpKSkKZyA8LSBnICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTApCmcgPC0gZyArIGdlb21fdmxpbmUoeGludGVyY2VwdD0wKQpnCmBgYAoKT3IgdXNlIGdnZm9ydGlmeSBhdXRvcGxvdCgpLgoKYGBge3IgcGNhX3Nob3dfUENfcGxhbmVfd2l0aF9nZ2ZvcnRpZnl9CiMgZ2dmb3J0aWZ5CmxpYnJhcnkoZ2dmb3J0aWZ5KQpnIDwtIGF1dG9wbG90KHBjYV9yZXMpCmcgPC0gZyArIGdlb21faGxpbmUoeWludGVyY2VwdD0wKQpnIDwtIGcgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MCkKZwpgYGAKCkdvaW5nIGZyb20gMkQgdG8gM0QgKGZpZ3VyZSBmcm9tIFtAbmxwY2FQbG90XSk6Cgo8aW1nIHNyYz0iLi4vLi4vSW1hZ2VzL2hlbWJlcmdfcGNhLnBuZyIgc3R5bGU9Im1hcmdpbjphdXRvOyBkaXNwbGF5OmJsb2NrIiAvPgoKIyMgTG9hZCBwYWNrYWdlcwoKYGBge3IgcGFja2FnZXMsIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjYXRlcikgIyBmb3IgUUMgYW5kIHBsb3RzCmBgYAoKIyMgTG9hZCBkYXRhCgpXZSB3aWxsIGxvYWQgdGhlIFIgZmlsZSBrZWVwaW5nIHRoZSBTQ0Ugb2JqZWN0IHdpdGggdGhlIG5vcm1hbGlzZWQgY291bnRzIGZvciA1MDAgY2VsbHMgcGVyIHNhbXBsZS4KCmBgYHtyfQpzZXROYW1lIDwtICJjYXJvbiIKc2V0U3VmIDwtICJfNWhDZWxsUGVyU3BsIgoKIyBSZWFkIG9iamVjdCBpbjoKdG1wRm4gPC0gc3ByaW50ZigiJXMvJXMvUm9iamVjdHMvJXNfc2NlX256X3Bvc3REZWNvbnYlcy5SZHMiLCBwcm9qRGlyLCBvdXREaXJCaXQsIHNldE5hbWUsIHNldFN1ZikKcHJpbnQodG1wRm4pCmlmKCFmaWxlLmV4aXN0cyh0bXBGbikpCnsKCWtuaXRyOjprbml0X2V4aXQoKQp9CnNjZSA8LSByZWFkUkRTKHRtcEZuKQpzY2UKCmhlYWQocm93RGF0YShzY2UpKQoKI2FueShkdXBsaWNhdGVkKHJvd0RhdGEobnouc2NlKSRlbnNlbWJsX2dlbmVfaWQpKQojIHNvbWUgZnVuY3Rpb24ocykgdXNlZCBiZWxvdyBjb21wbGFpbiBhYm91dCAnc3RyYW5kJyBhbHJlYWR5IGJlaW5nIHVzZWQgaW4gcm93IGRhdGEsCiMgc28gcmVuYW1lIHRoYXQgY29sdW1uIG5vdzoKY29sbmFtZXMocm93RGF0YShzY2UpKVtjb2xuYW1lcyhyb3dEYXRhKHNjZSkpID09ICJzdHJhbmQiXSA8LSAic3RyYW5kTnVtIgpgYGAKCiMjIFBDQQoKClBlcmZvcm0gUENBLCBrZWVwIG91dGNvbWUgaW4gc2FtZSBvYmplY3QuCgpgYGB7ciBzY2VfcGNhX2NvbXB9Cm5iUGNUb0NvbXAgPC0gNTAKIyBjb21wdXRlIFBDQToKI3NjZSA8LSBydW5QQ0Eoc2NlLCBuY29tcG9uZW50cyA9IG5iUGNUb0NvbXAsIG1ldGhvZCA9ICJpcmxiYSIpCnNjZSA8LSBydW5QQ0Eoc2NlLCBuY29tcG9uZW50cyA9IG5iUGNUb0NvbXApCmBgYAoKRGlzcGxheSBzY3JlZSBwbG90LgoKYGBge3Igc2NlX3BjYV9zY3JlZV9wbG90fQojIHdpdGggcmVkdWNlZERpbQpzY2UucGNhIDwtIHJlZHVjZWREaW0oc2NlLCAiUENBIikKYXR0cmlidXRlcyhzY2UucGNhKSRwZXJjZW50VmFyCmJhcnBsb3QoYXR0cmlidXRlcyhzY2UucGNhKSRwZXJjZW50VmFyLAogICAgICAgIG1haW49c3ByaW50ZigiU2NyZWUgcGxvdCBmb3IgdGhlICVzIGZpcnN0IFBDcyIsIG5iUGNUb0NvbXApLAogICAgICAgIG5hbWVzLmFyZz0xOm5iUGNUb0NvbXAsCiAgICAgICAgY2V4Lm5hbWVzID0gMC44KQpgYGAKCkRpc3BsYXkgY2VsbHMgb24gYSBwbG90IGZvciB0aGUgZmlyc3QgMiBQQ3MsIGNvbG91cmluZyBieSAnU2FtcGxlJyBhbmQgc2V0dGluZyBzaXplIHRvIG1hdGNoICd0b3RhbF9mZWF0dXJlcycuCgpUaGUgcHJveGltaXR5IG9mIGNlbGxzIHJlZmxlY3RzIHRoZSBzaW1pbGFyaXR5IG9mIHRoZWlyIGV4cHJlc3Npb24gcHJvZmlsZXMuCgpgYGB7ciBzY2VfcGNhX3Bsb3RDb2xvckJ5U2FtcGxlLCBpbmNsdWRlPVRSVUV9CmcgPC0gcGxvdFBDQShzY2UsCgkJY29sb3VyX2J5ID0gIlNhbXBsZS5OYW1lIiwKCQlzaXplX2J5ID0gInN1bSIKKSAgICAgICAgIApnCmBgYAoKT25lIGNhbiBhbHNvIHNwbGl0IHRoZSBwbG90IGJ5IHNhbXBsZS4KCmBgYHtyIHNjZV9wY2FfcGxvdENvbG9yQnlTYW1wbGVfZmFjZXRCeVNhbXBsZSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpnIDwtIGcgKyAgZmFjZXRfd3JhcChzY2Ukc291cmNlX25hbWUgfiAuKQpnCmBgYAoKT3IgcGxvdCBzZXZlcmFsIFBDcyBhdCBvbmNlLCB1c2luZyBwbG90UmVkdWNlZERpbSgpOgoKYGBge3Igc2NlX3BjYV9wbG90UmVkdWNlZERpbX0KcGxvdFJlZHVjZWREaW0oc2NlLCBkaW1yZWQ9IlBDQSIsIG5jb21wb25lbnRzPTMsIAoJCWNvbG91cl9ieSA9ICJTYW1wbGUuTmFtZSIpICsgZm9udHNpemUKYGBgCgojIyMgQ29ycmVsYXRpb24gYmV0d2VlbiBQQ3MgYW5kIHRoZSB0b3RhbCBudW1iZXIgb2YgZmVhdHVyZXMgZGV0ZWN0ZWQKClRoZSBQQ0EgcGxvdCBhYm92ZSBzaG93cyBjZWxscyBhcyBzeW1ib2xzIHdob3NlIHNpemUgZGVwZW5kcyBvbiB0aGUgdG90YWwgbnVtYmVyIG9mIGZlYXR1cmVzIG9yIGxpYnJhcnkgc2l6ZS4gSXQgc3VnZ2VzdHMgdGhlcmUgbWF5IGJlIGEgY29ycmVsYXRpb24gYmV0d2VlbiBQQ3MgYW5kIHRoZXNlIHZhcmlhYmxlcy4gTGV0J3MgY2hlY2s6CgpgYGB7ciBzY2VfcGNhX3Bsb3RRQ190b3RhbF9mZWF0dXJlc30KcjJtYXQgPC0gZ2V0RXhwbGFuYXRvcnlQQ3Moc2NlKQoKcjJtYXQKCmRhdCA8LSBjYmluZChjb2xEYXRhKHNjZSlbLGMoIlNhbXBsZS5OYW1lIiwKCQkJICAgICAic291cmNlX25hbWUiLAoJCQkgICAgICJzdW0iLAoJCQkgICAgICJkZXRlY3RlZCIsCgkJCSAgICAgInBlcmNlbnRfdG9wXzIwMCIsCgkJCSAgICAgInN1YnNldHNfTWl0b19wZXJjZW50IildLAoJCQkgICAgIHJlZHVjZWREaW0oc2NlLCJQQ0EiKSkKZGF0IDwtIGRhdGEuZnJhbWUoZGF0KQpkYXQkc3VtIDwtIGxvZzIoZGF0JHN1bSkKZ2dwbG90KGRhdCwgYWVzKHg9c3VtLCB5PVBDMSwgc2hhcGU9c291cmNlX25hbWUsIGNvbD1TYW1wbGUuTmFtZSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGluaGVyaXQuYWVzID0gRkFMU0UsIGFlcyh4PXN1bSwgeT1QQzEpKSAKZ2dwbG90KGRhdCwgYWVzKHg9cGVyY2VudF90b3BfMjAwLCB5PVBDMiwgc2hhcGU9c291cmNlX25hbWUsIGNvbD1TYW1wbGUuTmFtZSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGluaGVyaXQuYWVzID0gRkFMU0UsIGFlcyh4PXBlcmNlbnRfdG9wXzIwMCwgeT1QQzIpKSAKZ2dwbG90KGRhdCwgYWVzKHg9ZGV0ZWN0ZWQsIHk9UEMzLCBzaGFwZT1zb3VyY2VfbmFtZSwgY29sPVNhbXBsZS5OYW1lKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwgYWVzKHg9ZGV0ZWN0ZWQsIHk9UEMzKSkgCmdncGxvdChkYXQsIGFlcyh4PXN1YnNldHNfTWl0b19wZXJjZW50LCB5PVBDMiwgc2hhcGU9c291cmNlX25hbWUsIGNvbD1TYW1wbGUuTmFtZSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGluaGVyaXQuYWVzID0gRkFMU0UsIGFlcyh4PXN1YnNldHNfTWl0b19wZXJjZW50LCB5PVBDMikpIAoKZ2dwbG90KGRhdCwgYWVzKHg9c291cmNlX25hbWUsIHk9UEM3LCBzaGFwZT1zb3VyY2VfbmFtZSwgY29sPVNhbXBsZS5OYW1lKSkgKwogICAgZ2VvbV9ib3hwbG90KCkKYGBgCgojIyB0LVNORTogdC1EaXN0cmlidXRlZCBTdG9jaGFzdGljIE5laWdoYm9yIEVtYmVkZGluZwoKPCEtLSBodHRwczovL2Jpb2NlbGxnZW4tcHVibGljLnN2aS5lZHUuYXUvbWlnXzIwMTlfc2NybmFzZXEtd29ya3Nob3AvcHVibGljL2xhdGVudC1zcGFjZXMuaHRtbCAtLT4KClRoZSBTdG9jaGFzdGljIE5laWdoYm9yIEVtYmVkZGluZyAoU05FKSBhcHByb2FjaCBhZGRyZXNzIHR3byBzaG9ydGNvbWluZ3Mgb2YgUENBIHRoYXQgY2FwdHVyZXMgdGhlIGdsb2JhbCBjb3ZhcmlhbmNlIHN0cnVjdHVyZSB3aXRoIGEgbGluZWFyIGNvbWJpbmF0aW9uIG9mIGluaXRpYWwgdmFyaWFibGVzOiBieSBwcmVzZXJ2aW5nIHRoZSBsb2NhbCBzdHJ1Y3R1cmUgYWxsb3dpbmcgZm9yIG5vbi1saW5lYXIgcHJvamVjdGlvbnMuIEl0IHVzZXMgdHdvIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIHBhaXJ3aXNlIHNpbWlsYXJpdGllcyBiZXR3ZWVuIGRhdGEgcG9pbnRzOiBpbiB0aGUgaW5wdXQgZGF0YSBzZXQgYW5kIGluIHRoZSBsb3ctZGltZW5zaW9uYWwgc3BhY2UuCgpTTkUgYWltcyBhdCBwcmVzZXJ2aW5nIG5laWdoYm91cmhvb2RzLiBGb3IgZWFjaCBwb2ludHMsIGl0IGNvbXB1dGVzIHByb2JhYmlsaXRpZXMgb2YgY2hvc2luZyBlYWNoIG90aGVyIHBvaW50IGFzIGl0cyBuZWlnaGJvdXIgYmFzZWQgb24gYSBOb3JtYWwgZGlzdHJpYnV0aW9uIGRlcGVuZGluZyBvbiAxKSB0aGUgZGlzdGFuY2UgbWF0cml4IGFuZCAyKSB0aGUgc2l6ZSBvZiB0aGUgbmVpZ2hib3VyaG9vZCAocGVycGxleGl0eSkuIFNORSBhaW1zIGF0IGZpbmRpbmcgYSBsb3ctZGltZW5zaW9uIHNwYWNlIChlZyAyRC1wbGFuZSkgc3VjaCB0aGF0IHRoZSBzaW1pbGFyaXR5IG1hdHJpeCBkZXJpdmluZyBmcm9tIGl0IGlzIGFzIHNpbWlsYXIgYXMgcG9zc2libGUgYXMgdGhhdCBmcm9tIHRoZSBoaWdoLWRpbWVuc2lvbiBzcGFjZS4gVG8gYWRkcmVzcyB0aGUgZmFjdCB0aGF0IGluIGxvdyBkaW1lbnNpb24sIHBvaW50cyBhcmUgYnJvdWdodCB0b2dldGhlciwgdGhlIHNpbWlsYXJpdHkgbWF0cml4IGluIHRoZSBsb3ctZGltZW5zaW9uIGlzIGFsbG93ZWQgdG8gZm9sbG93IGEgdC1kaXN0cmlidXRpb24uCgpUd28gY2hhcmFjdGVyaXN0aWNzIG1hdHRlcjoKCi0gcGVycGxleGl0eSwgdG8gaW5kaWNhdGUgdGhlIHJlbGF0aXZlIGltcG9ydGFuY2Ugb2YgdGhlIGxvY2FsIGFuZCBnbG9iYWwgcGF0dGVybnMgaW4gc3RydWN0dXJlIG9mIHRoZSBkYXRhIHNldCwgdXN1YWxseSB1c2UgYSB2YWx1ZSBvZiA1MCwKLSBzdG9jaGFzdGljaXR5OyBydW5uaW5nIHRoZSBhbmFseXNpcyB3aWxsIHByb2R1Y2UgYSBkaWZmZXJlbnQgbWFwIGV2ZXJ5IHRpbWUsIHVubGVzcyB0aGUgc2VlZCBpcyBzZXQuCgpTZWUgW21pc3JlYWQtdHNuZV0oaHR0cHM6Ly9kaXN0aWxsLnB1Yi8yMDE2L21pc3JlYWQtdHNuZS8pLgoKIyMjIFBlcnBsZXhpdHkKCkNvbXB1dGUgdC1TTkUgd2l0aCBkZWZhdWx0IHBlcnBsZXhpdHksIGllIDUwLgoKYGBge3IgcnVuVFNORV9wZXJwNTB9CiMgcnVuVFNORSBkZWZhdWx0IHBlcnBleGl0eSBpZiBtaW4oNTAsIGZsb29yKG5jb2wob2JqZWN0KS81KSkKc2NlIDwtIHJ1blRTTkUoc2NlLCBkaW1yZWQ9IlBDQSIsIHBlcnBsZXhpdHk9NTAsIHJhbmRfc2VlZD0xMjMpCmBgYAoKUGxvdCB0LVNORToKCmBgYHtyIHBsb3RUU05FX3BlcnA1MH0KdHNuZTUwIDwtIHBsb3RUU05FKHNjZSwKCQkgICBjb2xvdXJfYnk9IlNhbXBsZS5OYW1lIiwKCQkgICBzaXplX2J5PSJzdW0iKSArIAoJCSAgIGZvbnRzaXplICsgCgkJICAgZ2d0aXRsZSgiUGVycGxleGl0eSA9IDUwIikKdHNuZTUwCmBgYAoKPCEtLSBTcGxpdCBieSBzYW1wbGUgdHlwZTogLS0+CgpgYGB7ciBwbG90VFNORV9wZXJwNTBfZmFjZXRCeVNhbXBsZSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpnIDwtIHRzbmU1MCArIGZhY2V0X3dyYXAoLiB+IHNjZSRzb3VyY2VfbmFtZSkKZwpgYGAKCkNvbXB1dGUgdC1TTkUgZm9yIHNldmVyYWwgcGVycGxleGl0eSB2YWx1ZXM6IAoKYGBge3IgcnVuVFNORV9wZXJwUmFuZ2V9CnRzbmU1LnJ1biA8LSBydW5UU05FKHNjZSwgdXNlX2RpbXJlZD0iUENBIiwgcGVycGxleGl0eT01LCByYW5kX3NlZWQ9MTIzKQp0c25lNSA8LSBwbG90VFNORSh0c25lNS5ydW4sIGNvbG91cl9ieT0iU2FtcGxlLk5hbWUiKSArIGZvbnRzaXplICsgZ2d0aXRsZSgiUGVycGxleGl0eSA9IDUiKQoKI3RzbmUyMDAucnVuIDwtIHJ1blRTTkUoc2NlLCB1c2VfZGltcmVkPSJQQ0EiLCBwZXJwbGV4aXR5PTIwMCwgcmFuZF9zZWVkPTEyMykKI3RzbmUyMDAgPC0gcGxvdFRTTkUodHNuZTIwMC5ydW4sIGNvbG91cl9ieT0iU2FtcGxlLk5hbWUiKSArIGZvbnRzaXplICsgZ2d0aXRsZSgiUGVycGxleGl0eSA9IDIwMCIpCgp0c25lNTAwLnJ1biA8LSBydW5UU05FKHNjZSwgdXNlX2RpbXJlZD0iUENBIiwgcGVycGxleGl0eT01MDAsIHJhbmRfc2VlZD0xMjMpCnRzbmU1MDAgPC0gcGxvdFRTTkUodHNuZTUwMC5ydW4sIGNvbG91cl9ieT0iU2FtcGxlLk5hbWUiKSArIGZvbnRzaXplICsgZ2d0aXRsZSgiUGVycGxleGl0eSA9IDUwMCIpCgojdHNuZTEwMDAucnVuIDwtIHJ1blRTTkUoc2NlLCB1c2VfZGltcmVkPSJQQ0EiLCBwZXJwbGV4aXR5PTEwMDAsIHJhbmRfc2VlZD0xMjMpCiN0c25lMTAwMCA8LSBwbG90VFNORSh0c25lMTAwMC5ydW4sIGNvbG91cl9ieT0iU2FtcGxlLk5hbWUiKSArIGZvbnRzaXplICsgZ2d0aXRsZSgiUGVycGxleGl0eSA9IDEwMDAiKQoKYGBgCgpgYGB7ciBwbG90VFNORV9wZXJwUmFuZ2UsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTZ9CnRzbmU1CiN0c25lNTAKI3RzbmUyMDAKdHNuZTUwMApgYGAKCiMjIyBTdG9jaGFzdGljaXR5CgpVc2UgYSBkaWZmZXJlbnQgc2VlZCB3aXRoIHRoZSBzYW1lIHBlcnBsZXhpdHkgNTAuCgpgYGB7ciBwbG90VFNORV9zdG9jaGF9CnRzbmU1MC5iIDwtIHJ1blRTTkUoc2NlLCB1c2VfZGltcmVkPSJQQ0EiLCBwZXJwbGV4aXR5PTUwLCByYW5kX3NlZWQ9NDU2KQoKdHNuZTUwLmIgPC0gcGxvdFRTTkUodHNuZTUwLmIsCgkJICAgY29sb3VyX2J5PSJTYW1wbGUuTmFtZSIsCgkJICAgc2l6ZV9ieT0ic3VtIikgKyAKCSAgICAgZm9udHNpemUgKyAKCSAgICAgZ2d0aXRsZSgiUGVycGxleGl0eSA9IDUwLCBzZWVkIDQ1NiIpCnRzbmU1MC5iCmBgYAoKIyMgVU1BUAoKQW5vdGhlciBuZWlnaGJvdXIgZ3JhcGggbWV0aG9kLiBTaW1pbGFyIHRvIHQtU05FLCBidXQgdGhhdCBpcyBkZXRlcm1pc3RpYywgZmFzdGVyIGFuZCBjbGFpbXMgdG8gcHJlc2VydmUgYm90aCBsb2NhbCBhbmQgZ2xvYmFsIHN0cnVjdHVyZXMuCgpDb21wdXRlIFVNQVAuCgpgYGB7ciBydW5VTUFQfQpzZXQuc2VlZCgxMjMpCnNjZSA8LSBydW5VTUFQKHNjZSwgZGltcmVkPSJQQ0EiKQpgYGAKClBsb3QgVU1BUDoKCmBgYHtyIHBsb3RVTUFQfQpzY2UudW1hcCA8LSBwbG90VU1BUChzY2UsCgkJICAgY29sb3VyX2J5PSJTYW1wbGUuTmFtZSIsCgkJICAgc2l6ZV9ieT0ic3VtIikgKyAKCQkgICBmb250c2l6ZSArIAoJCSAgIGdndGl0bGUoIlVNQVAiKQpzY2UudW1hcApgYGAKCjwhLS0gU3BsaXQgYnkgc2FtcGxlOiAtLT4KCmBgYHtyIHBsb3RVTUFQX2ZhY2V0QnlTYW1wbGUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMiwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KZyA8LSBzY2UudW1hcCArIGZhY2V0X3dyYXAoLiB+IHNjZSRzb3VyY2VfbmFtZSkKZwpgYGAKClNhdmUgU0NFIG9iamVjdDogCgpgYGB7cn0KdG1wRm4gPC0gc3ByaW50ZigiJXMvJXMvUm9iamVjdHMvJXNfc2NlX256X3Bvc3REZWNvbnYlc19kaW1SZWQuUmRzIiwgcHJvakRpciwgb3V0RGlyQml0LCBzZXROYW1lLCBzZXRTdWYpCnNhdmVSRFMoc2NlLCB0bXBGbikKYGBgCg==