Introduction

Motivation and Background Work

Acute myeloid leukemia (AML) is the most common acute hematologic malignancy in adults, and is characterized by infiltration of the bone marrow and blood by highly proliferative, abnormally differentiated promyeloid cells (1). The standard practices for treating AML have been consistent for the past three decades. The first stage of treatment is induction, where patients undergo chemotherapy in order to stimulate the complete differentiation of immature, cancerous promyelocytes (2). A commonly used chemotherapeutic inducing agent is all trans-retinoic acid (ATRA), which stimulates granulocytic differentiation and prevents promyelocyte proliferation with extremely high efficacy. While ATRA has been used as a standard AML treatment for decades, the underlying mechanism has not been fully established (3). This is an important area of AML research, because understanding the mechanism of ATRA-induced differentiation would help clinicians investigate the root-causes which prevent normal hematopoietic stem cell differentiation.

Since their discovery in 1993, there has been a significant amount of research showing the importance of miRNAs in cancer development (4). To analyze the role of miRNA in AML, several published studies have used bulk microarray analyses to describe the miRNA expression patterns associated with ATRA-induced differentiation. These papers have shown that there are a few key miRNAs— such as miR-663, miR-223, and miR-494— which become significantly upregulated in response to ATRA-induced differentiation (5, 6). However, a more in-depth analysis using next-generation sequencing techniques is required to achieve a better understanding of these mechanisms.

This project will analyze single-cell miRNA sequencing data from HL-60 cells— a promyeloblast cell-line which is commonly used as a differentiation model— along a 7-day time-course of ATRA treatment. Our overall objective is to utilize next-generation sequencing data to investigate the role of miRNA in ATRA-induced differentiation. In doing so, we hope to gain further insight into which molecular pathways are important in this process and determine whether temporal changes in miRNA expression are involved. Furthermore, we will determine whether ATRA-induced differentiation leads to a homogenous culture of granulocytes, or if there are separate differentiation pathways which result in significant cellular heterogeneity. Our general hypothesis is that the HL-60 miRNA expression signatures will change as the cells differentiate relative to time. More specifically, we predict that the expression of well-known differentiation-associated miRNAs— such as miR-663, miR-223, and miR-494— will become increasingly upregulated as treatment progresses (5, 6). Lastly, due to the heterogeneity of cancer cells, we expect to observe distinct subpopulations of HL-60 cells following ATRA-induced differentiation; this may provide evidence suggesting multiple biological pathways are taking place.

Dataset

Our data set for the 7-day time-course of ATRA treatment on HL-60 cells includes data for 319 single cells: 92 cells at Day 0 of treatment, 89 cells at Day 1, 85 cells at Day 3, and 53 cells at Day 7. On each day, individual cells were collected and isolated in a microfluidic chip, and then run through a single-cell miRNA sequencing protocol similar to the one described by Faridani et al. in 2016 (5). The resulting libraries were sequenced on an Illumina MiSeq platform. (For additional information, reference our group’s Data Folder)

Aims and Methodology

The principal biological question that we hope to investigate is whether the HL-60 cells have distinct miRNA expression signatures at different time points of ATRA treatment. Additionally, it will be interesting to measure the heterogeneity of these populations. If distinct subpopulations are present, is there evidence of multiple ATRA-induced differentiation pathways in effect? Are the expression patterns of these miRNAs consistent throughout treatment, or do they follow a specific pattern with respect to time? Which miRNAs and which biological mechanisms are involved in ATRA-induced differentiation? In order to answer these questions, we will take advantage of several computational/statistical approaches. We will use multiple linear regression in conjunction with p-value histograms to both identify differences in miRNA expression signatures across the time points and determine whether or not these identified differences are truly significant. In order to investigate heterogeneity of subpopulations and the differentiation pathways, we will use hierarchical clustering functions and t-SNE plots to help visualize and analyze the evolution of the HL-60 cells (8). Lastly, in order to determine which biological mechanisms are involved in ATRA-induced granulocytic differentiation, we will use “guilt-by-association” algorithms such as gene set enrichment analysis, functional enrichment analysis and GeneMANIA (9, 10).

Analysis

Setting up the environment

# Packages
library(ggplot2)
library(dplyr)
library(plotly)
library(tidyr)
library(RColorBrewer)
library(limma)
library(gplots)
library(edgeR)
library(devtools)
library(ggbiplot)
library(DESeq)
library(geneplotter)
library(Rtsne)
library(NMF)
library(gridExtra)
set.seed(1234) # makes everything reproductible 
meta_data <- read.table("../Data/raw_data/meta_data.txt", header = TRUE)
dataInitial <- read.table("../Data/raw_data/expn_matrix.txt", header = TRUE, row.names = 1)
spike_data <- read.table("../Data/raw_data/expn_matrix_spike.txt", header = TRUE, row.names = 1)

Normalization

Normalization is essential in RNAseq. Unlike mRNA, there are very few constitutively active miRNA genes or “house-keeping” miRNAs that are expressed at consistent levels. As a result, we cannot use standard techniques such as quantile normalization. To deal with this issue, we rely heavily on the spike-in size fractions to normalize the data and account for potential batch effects. Although spike-in data have been shown not to be satisfying enough for RNA-seq (11) (12). It is common in miRNAseq because of the “house-keeping” issue discussed above (13).

The normalization is broken up into 2 main parts. First we scale the counts of each sample by the counts of spike RNA in this sample. Secondly we remove all cells that are dead.

Note: When we will analyse the differential expression of each miRNA, we will additionaly normalize by the library size, indeed that is done by default in Voom.

# finding sum of column and adding it as new row
spike_data <- rbind(spike_data, "Totals" = colSums(spike_data))
scaled_totals <- spike_data["Totals", ]
average_reads <- rowMeans(scaled_totals)
# NORMALIZING BY SPIKE-RNA
scaled_totals <- rbind(scaled_totals, "Scale" = scaled_totals[1, ] / average_reads) 
# setting dim_names for upcoming matrix
dim_names <- list(row.names(dataInitial))       
dim_names[2] <- list(colnames((dataInitial)))
# pre-making a matrix
normalized_data <- matrix(nrow = nrow(dataInitial), ncol = ncol(dataInitial), dimnames = dim_names) 
# for loop which scales data by the spike miRNA and saves it as normalized_data
for(i in 1: ncol(dataInitial)){   
  normalized_data[ , i] <- dataInitial[ , i] / (scaled_totals[2, i])
}
normalized_data <- as.data.frame(normalized_data)
total_data <- rbind(spike_data, "Total Spike" = colSums(spike_data), "Total Reads Norm" = colSums(normalized_data), "Total Reads Init" = colSums(dataInitial))
### KEEEP???
if (FALSE){
total_cellular <- total_data["Total Reads Norm", ]
average_reads <- rowMeans(total_cellular)
cell_scales <- rbind(total_cellular, "Scale" = total_cellular[1, ] / average_reads) #dividing total spike values by average spike values
dim_names <- list(row.names(dataInitial))       #setting dim_names for upcoming matrix
dim_names[2] <- list(colnames((dataInitial)))
normalized_data2 <- matrix(nrow = nrow(dataInitial), ncol = ncol(dataInitial), dimnames = dim_names) #pre-making a matrix
for(i in 1: ncol(dataInitial)){   #for loop which scales data and saves it as normalized_data
  normalized_data2[ , i] <- normalized_data[ , i] / (cell_scales[2, i])
}
normalized_data <- as.data.frame(normalized_data2)
}
####
# DELETING DEAD CELLS
alive_indexes <- list() #initializing lists
k <- 1  #initializing counter
for(i in 1:ncol(total_data)){
  if(total_data["Total Reads Init", i] > 20000){
    alive_indexes[k] <- i
    k <- k + 1
  }
}
alive_data <- normalized_data[ , as.numeric(alive_indexes)]
alive_meta_data <- meta_data[as.numeric(alive_indexes), ]
# saving completely normalized data as n_data and n_meta_data
n_data <- alive_data
n_meta_data <- alive_meta_data
# makes group and design matrix
data_day <- as.character(n_meta_data$Population)
data_day <- recode(data_day, "HL60D0" = "Day 0", "HL60D1" = "Day 1", "HL60D3" = "Day 3", "HL60D7" = "Day 7")
data_day <- factor(data_day)
design <- model.matrix(~0 + n_meta_data$Population)
# deletes "n_meta_data$Population" from name of columns
colnames(design) <- gsub("n_meta_data\\$PopulationHL60", "", colnames(design))
# Keeps genes without all zeros miRNA
dataNZ <- n_data[which(rowSums(n_data) > 0),] 
# making the contrast matrix for comparing each group
contrastMatrix <- makeContrasts(D1-D0,
                                D3-D0,
                                D7-D0,
                                D1-D3,
                                D1-D7,
                                D3-D7,
                                levels = c("D0","D1","D3","D7"))
# Removes unnecessary big data
remove(normalized_data)
remove(n_data)
remove(alive_data)
remove(alive_meta_data)
remove(dataInitial)

Data exploration

PCA

For all of our PCA plots we will first use a log transformation to make the data approximatively follow the homoscedasticity assumption.

PCA standardized

Knowing the importance of having standardized data for PCA, we could think of standardizing ours. This in general is a good idea, as variables are often not on the same scale and thus cannot be compared directly. Here, however, we have dimensions that are comparable as they each represent the number of miRNA in the sample. It is therefore a good idea not to standrdize, as it will unnecessarily decrease the variance explained. More information on this can be found on the following forums: here or here. Out of curiosity we will still plot the standardized data:

#Log transform
log_normalized_dataNZ <- log(dataNZ)
log_normalized_dataNZ[log_normalized_dataNZ == "-Inf"] <- 0
data_pcaNZ <- prcomp(t(log_normalized_dataNZ), center = TRUE, scale. = TRUE)
ggbiplot(data_pcaNZ, 
              scale = 1, 
              obs.scale = 1, 
              varname.abbrev = FALSE,
              var.axes = FALSE,
              pc.biplot =TRUE,
              circle = TRUE, 
              groups = data_day, 
              ellipse= TRUE) +
  ggtitle("PCA with standardization") + 
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

# will not be needed: don't keep big data
remove(data_pcaNZ)
remove(log_normalized_dataNZ)

From this plot we see that as we’ve supposed, the variance explained is lower than in the first one. We thus conclude that we should use a non standardized PCA.

PCA no standardization

# log transform
log_normalized_data <- log(dataNZ)
log_normalized_data[log_normalized_data == "-Inf"] <- 0
data_pca <- prcomp(t(log_normalized_data))
ggbiplot(data_pca, 
              scale = 1, 
              obs.scale = 1, 
              varname.abbrev = FALSE,
              var.axes = FALSE,
              pc.biplot =TRUE,
              circle = TRUE, 
              groups = data_day, 
              ellipse= TRUE) +
  ggtitle("PCA without standardization") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5))  

# will not be needed: don't keep big data
remove(data_pca)
remove(log_normalized_data)

Note: The PCA graph is interesting as it shows that after each day the clusters seem to go down in PC2 and extend their variance in PC1. PC2 is probably mainly a single miRNA which gets less expressed as days pass.

PCA top 50

Let’s now only plot the PCA of the 50 genes. As we’ve discussed before, we will not standardize the data.

# arranging data based on total expression
indexTopFifty <- sort(rowSums(dataNZ), index=T, decreasing=TRUE)$ix[1:50]
topFifty <- dataNZ[indexTopFifty,]
# Log transform
log_topFifty <- log(topFifty)
log_topFifty[log_topFifty == "-Inf"] <- 0
# PCA
data_topFifty <- prcomp(t(log_topFifty))
ggbiplot(data_topFifty, 
              scale = 1, 
              obs.scale = 1, 
              varname.abbrev = FALSE,
              var.axes = FALSE,
              pc.biplot =TRUE,
              circle = TRUE, 
              groups = data_day, 
              ellipse= TRUE) +
  ggtitle("PCA top 50 genes") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

# will not be needed: don't keep big data
remove(data_topFifty)
remove(log_topFifty)
remove(topFifty)

Correlation heatmap

# computing sample-sample correlations
normalized_data_cor <- cor(dataNZ)  
cor_dat <- as.data.frame(normalized_data_cor)
labs <- list(Population = n_meta_data$Population)
NMF::aheatmap(cor_dat, 
              annCol = labs, annRow = labs, Rowv = NA, Colv = NA, labRow = NA, labCol = NA, 
              main = "Sample-Sample Correlation Matrix")

#will not be needed: don't keep big data
remove(normalized_data_cor)

On this correlation heatmap we’ve scaled the values to be centered around zero. This explains why the “observed correlation” has values that can be negative. In reality, all the the sample are relatively strongly correlated. This is not suprising as many miRNA are not differentially expressed between samples.

In this heat map we clearly see the high correlation between the samples of day 0 and 1 and between the samples of day 3 and 7.

t-SNE plots

General t-SNE

Let’s plot the t-SNE plots to see if we can find clusters and / or outliers.

# ordering data by expn
alive_tsne_data <- cbind(dataNZ, "avg_expn" = rowMeans(dataNZ), "miRNA" = row.names(dataNZ)) 
alive_tsne_data <- dplyr::arrange(alive_tsne_data,desc(avg_expn))
alive_tsne_data <- t(alive_tsne_data)
colnames(alive_tsne_data) <- alive_tsne_data["miRNA", ]
#removes rows that were added for ordering
alive_tsne_data <- alive_tsne_data[!rownames(alive_tsne_data) %in% c("miRNA","avg_expn"), ]
# Runs tsne
tsne_out <- Rtsne(dist(alive_tsne_data))
# Show the objects in the 2D tsne representation
qplot(x=tsne_out$Y[,1], y=tsne_out$Y[,2],color=data_day) + 
  labs(colour = "Cell Type", x = "tsne1", y = "tsne2")+
  ggtitle("t-SNE alive cells and non zero genes") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

#will not be needed: don't keep big data
remove(tsne_out)

As with the correlation heatmap, we see that the samples from day 0 and 1 are clustered together while those from day 3 and 7 are clustered together. There also seems to be a small difference between days of the same cluster, but these difference are smaller than between clusters.

t-SNE of Day 0 and Day 1

Now that we have seen that the samples from day 0 and 1 are closely related. Let’s try to take only those 2 in order to see if we can separate them from each other.

# tsne on Day 0/Day 1 cells only
indexDay01 <- which(data_day %in% c("Day 0","Day 1"))
# selecting only day 0 and day 1 cells
group_one_data <- alive_tsne_data[indexDay01, ] 
# Run TSNE
tsne_out <- Rtsne(dist(group_one_data)) 
# Show the objects in the 2D tsne representation
qplot(x=tsne_out$Y[,1], y=tsne_out$Y[,2],color=data_day[indexDay01]) +
  labs(colour = "Cell Type", x = "tsne1", y = "tsne2") +
  ggtitle("t-SNE only day 0 and 1") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

#will not be needed: don't keep big data
remove(tsne_out)

The t-SNE can not cleraly separate the 2 samples from each other. Finally let’s investigate whether they can be separated with a t-SNE in 3 dimension:

tsne_out <- Rtsne(dist(group_one_data), dims = 3) # Run TSNE in 3 dimensions
plot_ly(x=tsne_out$Y[,1],
        y=tsne_out$Y[,2],
        z=tsne_out$Y[,3],
        type="scatter3d",
        color=data_day[indexDay01],
        mode="markers") %>% 
  layout(title = 't-SNE day 0 and 1 3D')

#will not be needed: don't keep big data
remove(tsne_out)
remove(group_one_data)

Even with the 3 dimensional t-SNE, it is hard / impossible to distinguish between day 0 and 1. Indeed, we need to remember that the colours were added and not found by the t-sne plots. This really shows how similar day 0 and 1 seem to be.

t-SNE of Day 3 and Day 7

Now let’s look at the cluster Day 3 and 7, to see if they can be distinguished.

#tsne on Day 3/Day 7 cells only
indexDay37 <- which(data_day %in% c("Day 3","Day 7"))
#selecting only day 3 and day 7 cells
group_two_data <- alive_tsne_data[indexDay37, ] 
# Run TSNE
tsne_out <- Rtsne(dist(group_two_data)) 
# Show the objects in the 2D tsne representation
qplot(x=tsne_out$Y[,1], y=tsne_out$Y[,2],color=data_day[indexDay37]) +
  labs(colour = "Cell Type", x = "tsne1", y = "tsne2") +
  ggtitle("t-SNE only day 3 and 7") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

#will not be needed: don't keep big data
remove(tsne_out)
remove(alive_tsne_data)

As before, the 2 type of cells cannot be distinguished with a t-SNE plot in 2D. They will probably not be distinguishable in 3D but let’s be sure:

tsne_out <- Rtsne(dist(group_two_data), dims = 3) # Run TSNE in 3 dimensions
plot_ly(x=tsne_out$Y[,1],
        y=tsne_out$Y[,2],
        z=tsne_out$Y[,3],
        type="scatter3d",
        color=data_day[indexDay37],
        mode="markers") %>% 
  layout(title = 't-SNE day 3 and 7 3D')

#will not be needed: don't keep big data
remove(tsne_out)
remove(group_two_data)

The second cluster cannot be fully seperated further with 3 dimensional t-SNE, but it is already closer than in 2D.

Comparing the expression of miRNA

In order to find differentially expressed genes we will be fitting a linear model.

Note that for fitting linear models, we cannot use the DATASEQ library as it requires counts (i.e. discrete values). We now have continuous values because of they way we normalized the data.

Mean-Variance trend

It would be an error to directly fit a linear model to our data, indeed RNA-seq is count data which has strong heteroscedasticity. To prove this, let’s notice that if we randomly pick \(R_{lib}\) RNA given that the abundance (propotion) of a gene \(g\) is \(\alpha\). Then we will see \(R_g\) such RNA where: \[R_g \sim\ \mathcal{Bin}(R_{lib},\alpha)\] Which can be approximated by \[R_g \sim\ \mathcal{Pois}(R_{lib} * \alpha)\] Taking \(lim_{R_{lib} * \alpha \rightarrow \infty}\) \[R_g \sim\ \mathcal{N}(R_{lib} * \alpha,R_{lib} * \alpha)\] Which clearly shows that count data violates the assumption of homoscedasticity. The problem is that ordinary least squares assumes the same variance at each point. If the variance is higher in certain regions than these regions would get more weights than they should. Indeed \(Ttest \propto \frac{1}{\sigma}\).

There are many possible ways to take this problem into account. We chose to go with limma voom method as it is a competitive method (14) that works well with low count RNAseq (15). The idea is simply to fit a weighted regression, with the weights inversely proportional to the variance of the point. The variance of the point is estimated using a lowess smoother in a log(mean) - sqrt(variance) trend.

Initial Mean-Variance Trend

With this explanation we see the importance of a mean - variance plot, let’s now plot it.

dge <- DGEList(counts=dataNZ, group = data_day)
# applies TMM normalization to dge
#dge <- calcNormFactors(dge)
data_voomed <- voom(dge,design,plot=TRUE)

#will not be needed: don't keep big data
remove(dge)

The mean-variance trend is suprising: it doesn’t look flat enough. This could indicate a problem with the normalization of our data, but after further analysis we didn’t find any issue. Note that the data may well be strange due to the nature of miRNA-seq data, indeed in RNAseq we often have high value of counts but in miRNA the number of counts are much lower. This could cause the spike we see below x = zero (i.e. very low mean).

Lets check whether the strange mean variance is due to the clusters that we have.

df <- data_voomed$E
dataf<- mutate(data.frame(t(df)), group = data_day) %>% 
  gather(mirna, exp, - group) 
ggplot(dataf, aes(x=exp)) + 
  geom_histogram(aes(fill = group), colour = 'white')+
  ggtitle("Distribution of counts with respect to the groups") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

#will not be needed: don't keep big data
remove(dataf)
remove(df)

We see that our model seems to be bimodal but that the bimodality doesn’t seem to be due to the clusters.

Filtered Mean-Variance Trend

After reading in details limma’s manual (16), we realized that we should fiter out rows that have at least 10 counts for a worthwhile number of samples (paragraph 15.5).

We will be using \(30\%\) as a “worthwhile number of samples”.

# filtering out rows that have non zero counts in less than 30% of the samples
dataNZFilterd <-dataNZ[rowSums(dataNZ>=10)>=round(0.3*ncol(dataNZ)),]
dge <- DGEList(counts=dataNZFilterd, group = data_day)
#dge <- dge[isExpr,]
#dge <- calcNormFactors(dge)
data_voomed <- voom(dge,design,plot=TRUE)

#will not be needed: don't keep big data
remove(dge)
remove(dataNZFilterd)

It is now already a bit better. We also see that the mean variance trend seams to be decreasing very quickly and steadily, we can thus conclude that the experiment seem to be of low biological variation (17) (see “Removing heteroscedascity from count data”).

Here is an example of the data we have:

data_voomed[1:2,1:10]
An object of class "EList"
$targets
               group lib.size norm.factors
HL60D0.C01_S1  Day 0 53032.61            1
HL60D0.C02_S2  Day 0 73912.31            1
HL60D0.C03_S3  Day 0 58936.19            1
HL60D0.C04_S4  Day 0 77685.09            1
HL60D0.C05_S5  Day 0 69622.15            1
HL60D0.C06_S6  Day 0 50381.05            1
HL60D0.C07_S7  Day 0 90892.81            1
HL60D0.C08_S8  Day 0 68475.75            1
HL60D0.C09_S10 Day 0 98894.08            1
HL60D0.C10_S11 Day 0 49798.08            1

$E
              HL60D0.C01_S1 HL60D0.C02_S2 HL60D0.C03_S3 HL60D0.C04_S4 HL60D0.C05_S5 HL60D0.C06_S6 HL60D0.C07_S7 HL60D0.C08_S8
hsa-let-7a-5p     13.367749     12.802981      12.94953       7.63448      13.26644      13.24661     12.918862      13.72853
hsa-let-7a-3p      3.236949      2.758022      10.14525       2.68620      10.34223      10.26194      2.459674      10.00305
              HL60D0.C09_S10 HL60D0.C10_S11
hsa-let-7a-5p      12.851334       13.52684
hsa-let-7a-3p       9.489963       10.08661

$weights
          [,1]      [,2]      [,3]      [,4]      [,5]      [,6]      [,7]      [,8]      [,9]     [,10]
[1,] 0.6802643 0.8991194 0.7420669 0.9388426 0.8540838 0.6525014 1.0793471 0.8420643 1.1654145 0.6463449
[2,] 0.1247133 0.1417794 0.1292231 0.1447899 0.1382662 0.1227933 0.1547812 0.1373167 0.1611457 0.1223628

$design
   D0 D1 D3 D7
1   1  0  0  0
2   1  0  0  0
3   1  0  0  0
4   1  0  0  0
5   1  0  0  0
6   1  0  0  0
7   1  0  0  0
8   1  0  0  0
9   1  0  0  0
10  1  0  0  0

More Natural Filtered Mean-Variance Trend

Thinking about it, we shouldn’t filter on having as good percentage of the samples that are non zero but rather having a majority of non zero in at least one time point we have. Indeed that’s what really interests us at the end, because we are going to compare the miRNAseq between days! For example imagine we had an miRNA which was highly expressed in every sample of Day 0, but equal to zero in all other samples, then it would get filtered out because it would be non zero in only \(25\%\) of the total samples. This is of course not desired.

We will thus only keep the miRNA in which at least \(50\%\) of the samples in one group have count greater than 10.

indexDay0 <- which(data_day == "Day 0")
indexDay1 <- which(data_day == "Day 1")
indexDay3 <- which(data_day == "Day 3")
indexDay7 <- which(data_day == "Day 7")
dataNZFilterdBis <- dataNZ[rowSums(dataNZ[,indexDay0]>=10)>=round(0.50*length(indexDay0)) |
                          rowSums(dataNZ[,indexDay1]>=10)>=round(0.50*length(indexDay1)) |
                          rowSums(dataNZ[,indexDay3]>=10)>=round(0.50*length(indexDay3)) |
                          rowSums(dataNZ[,indexDay7]>=10)>=round(0.50*length(indexDay7)) ,]
dge <- DGEList(counts=dataNZFilterdBis, group = data_day)
#dge <- calcNormFactors(dge)
data_voomed <- voom(dge,design,plot=TRUE)

We see that it is very close to before but at least now there’s no issue with genes being filtered out even if they have a high expression in most samples of a group. This also reduces the subjectivity of what is “a worthile number of samples need to have more than 10 counts for that miRNA”.

Lets look now at the histogram to see how it changed it:

df <- data_voomed$E
dataf<- mutate(data.frame(t(df)), group = data_day) %>% 
  gather(mirna, exp, - group) 
ggplot(dataf, aes(x=exp)) + 
  geom_histogram(aes(fill = group), colour = 'white') +
  ggtitle("Distribution of filtered counts with respect to the groups") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

#will not be needed: don't keep big data
remove(dataf)
remove(df)

The bimodality of the plot doesn’t seem to be due to clusters with this histogram. But that doesn’t rule out completely the effect of the groups. Indeed some miRNA may only be highly expressed in one cluster and othesr in the second cluster. We thus need to look at the interaction between miRNA and cluster.

Distribution of expression with respect to miRNA and clusters
data_group <- as.character(data_day)
data_group[data_group == "Day 0" | data_group == "Day 1"] <- "Day01"
data_group[data_group == "Day 3" | data_group == "Day 7"] <- "Day37"
#makes a data frame with all information
df <- data_voomed$E
dataf4 <- data.frame(df)
dataf4$mirna <- as.factor(rownames(dataf4))
dataf4 <- gather(dataf4,samples, exp,-mirna);
dataf4$group <-  as.factor(data_group);
dataf4$day <-  data_day;
dataf4 <- mutate(dataf4, interGroup = paste(as.character(dataf4$mirna),data_group) )
dataf4 <- mutate(dataf4, interGroup2 = paste(as.character(dataf4$mirna),as.character(data_day)) )
dataf4$exp2 <- 2^dataf4$exp-0.5
randRNA <- unique(dataf4$mirna)[sample(1:length(unique(dataf4$mirna)), 12, replace=FALSE)]
ggplot(dataf4[dataf4$mirna %in% randRNA,], aes(x=exp, fill = group)) + 
  geom_density(alpha=0.5) +
  ggtitle("Random density plot for interaction miRNA-group") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) +
  theme(legend.position="none") + xlim(0,30) +
  facet_wrap(~mirna,nrow = 4,ncol = 3)

#will not be needed: don't keep big data
remove(df)

With this we clearly see that there is no link between the clusters and miRNA and the bimodality of the distribution. The bimodality is nearly always present for all miRNA.

This may seem suprising at first, but after further consideration the bimodality is amplified by the log transformation of the data. Indeed, the log transformation of the expression data makes a wider range of high expression seem smaller. Here is a plot that shows the same data but not logtransformed.

ggplot(dataf4[dataf4$mirna %in% randRNA,], aes(x=exp2, fill = group)) + 
  geom_density(alpha=0.5) +
  ggtitle("Random density plot for interaction miRNA-group: no log") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) +
  theme(legend.position="none") + xlim(-500,10000) +
  facet_wrap(~mirna,nrow = 5,ncol = 3)

We clearly see that the non tranformed data is not as bimodal as it seemed to be. The miRNA are simply off or on, but there’s a wide range of “on states”. Note: it is suprising to see that for the random genes we took, there doesn’t seem to be any link between groups and “on-off” states.

Fit linear model

For comparing the expression of multiple miRNA we will use a linear model. In order to gain statistical power we will be using emirical bayes method to “borrow information across all miRNA”. The idea of bayes method is to shrink the estimated sample variance towardsa pooled estimate, which results in more stable inference for samples with smaller size (18). We will be using Limma’s package to implement empirical bayes linear model.

Note: The strength of empirical bayes, is that compared to classical bayes method, it doesn’t require the input of prior. Indeed, it will find the prior from the data.

Linear model: day as a numeric

Let’s now look at linear model where the day is considered as numeric and not a factor.

# Convert population to a numeric variable.
Pop.num<-as.numeric(substr(n_meta_data$Population,6,6))
n_meta_data<-cbind(n_meta_data,Pop.num)
des.pop.num<-model.matrix(~Pop.num,n_meta_data)
v.pop.num<-voom(dge,des.pop.num, plot=FALSE)
# Next, fit linear model with day as a factor.
fit.pop.num <- lmFit(v.pop.num,des.pop.num)
efit.pop.num <- eBayes(fit.pop.num)
# store result 
top.pop.num<-topTable(efit.pop.num,number=Inf)
Removing intercept from test coefficients
# Look at table with top genes.
top.pop.num[1:10,]
fdr.pop.num<-nrow(subset(top.pop.num,adj.P.Val<0.01))
remove(v.pop.num)

There are now 68 genes at a false discovery rate of 0.01.

Finally, as before, lets make a histogram of p-values.

hist(top.pop.num$adj.P.Val,breaks=29,main="Q-value Histogram - Day as numeric",xlab="Q-Value")

hist(top.pop.num$P.Value,breaks=29,main="P-value Histogram - Day as numeric",xlab="P-Value")

remove(top.pop.num)

This histogramm looks perfect for us! This clearly shows that most of our miRNA live in the alternative hypothesis. It also shows a approximatively uniformly distribution of p-values under the null hypothesis (on the right), as we would think. Note: this histogramm seems stangely “too good” in the sense that the number of miRNA differntially expressed seems huge. Note that this is possible because we are looking at miRNA seq with a “on” or “off” differntiation.

Linear model: day as a factor

des.pop.char<-model.matrix(~Population,n_meta_data)
v.pop.char<-voom(dge,des.pop.char, plot=FALSE)
fit.pop.char <- lmFit(v.pop.char,des.pop.char)
efit.pop.char <- eBayes(fit.pop.char)
top.pop.char<-topTable(efit.pop.char,number=Inf)
Removing intercept from test coefficients
plotSA(efit.pop.char,main="Final Mean-Variance plot")

#will not be needed: don't keep big data
remove(fit)
object 'fit' not found
remove(v.pop.char)

On this plot we see how the data is after having transformed it with limma. We note that the variance is now nearly independent to the expression (although this doesn’t seem true for the last few points). This shows us that the data is homoscedatic enough to fit a linear model.

Let’s now have a look at table with top genes.

top.pop.char[1:10,]
P-values and FDR

Get some probe names and really low p-values (i.e. P-Value < 0.01).

fdr.pop.char<-nrow(subset(top.pop.char,adj.P.Val<0.01))

There are 115 genes at a false discovery rate of 0.01.

Finally, make a histogram of p-values.

hist(top.pop.char$adj.P.Val,breaks=29,main="Q-value Histogram - Population as a factor",xlab="Q-Value")

hist(top.pop.char$P.Value,breaks=29,main="P-value Histogram - Population as a factor",xlab="P-Value")

remove(top.pop.char)

As before the p-value histograms are really nice and show that our experiment has a lot of miRNA that live in the alternative hypothesis. This plot is a bit strange in the sense that there is even less miRNA in the null hypothesis.

Linear model: cluster as a factor

Compare days 0 and 3 to days 3 and 7

gsub("HL60D0","Day 0 or 1",n_meta_data$Population)
levels(n_meta_data$Population)
cluster<-character()
for(x in n_meta_data$Population){
  if(x == "HL60D0"){
    cluster<-append(cluster,"Day 0 or 1")
  }
  if(x == "HL60D1"){
    cluster<-append(cluster,"Day 0 or 1")
  }
  if(x == "HL60D3"){
    cluster<-append(cluster,"Day 3 or 7")
  }
  if(x == "HL60D7"){
    cluster<-append(cluster,"Day 3 or 7")
  }
}
n_meta_data<-cbind(n_meta_data,cluster) #make factor for cluster

Design matrix, voom and fit linear model

des.cluster<-model.matrix(~cluster,n_meta_data)
v.cluster<-voom(dge,des.cluster, plot=FALSE)
fit.cluster <- lmFit(v.cluster,des.cluster)
efit.cluster <- eBayes(fit.cluster)
top.cluster<-topTable(efit.cluster,number=Inf)
Removing intercept from test coefficients
fdr.cluster<-subset(top.cluster,adj.P.Val<0.01)
top.cluster[1:10,]
remove(v.cluster)
remove(efit.cluster)

There are 61 genes at a false discovery rate of 0.01.

Make another set of histograms.

hist(top.cluster$adj.P.Val,breaks=19,main="Q-value Histogram - Cluster as a factor",xlab="Q-Value")

hist(top.cluster$P.Value,breaks=19,main="P-value Histogram - Cluster as a factor",xlab="P-Value")

Look at top few genes

# Define plotting function
cluster1<-gsub("HL60D0","Day 0 or 1",n_meta_data$Population)
cluster1<-gsub("HL60D1","Day 0 or 1",cluster1)
cluster1<-gsub("HL60D3","Day 3 or 7",cluster1)
cluster1<-gsub("HL60D7","Day 3 or 7",cluster1)
meta2<-cbind(n_meta_data,cluster1)
#function which combines expression data with metaData
prepareData2 <- function(genes) {   
  miniDat <-  subset(dataNZ, rownames(dataNZ) %in% genes)
  miniDat <- data.frame(gExp = as.vector(t(as.matrix(miniDat))),
                        gene = factor(rep(rownames(miniDat), each = ncol(miniDat)),
                                      levels = genes))
  miniDat <- suppressWarnings(data.frame(meta2, miniDat))
  
  return(miniDat)
}
topGenes <- rownames(top.cluster[1:10,])
prepDatTop<-prepareData2(topGenes) #format data
ggplot(data=subset(prepDatTop,prepDatTop$gene==topGenes[1]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(topGenes[1]) +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

  
remove(top.cluster)

Linear model: day as a factor without intercept

For the following analysis we will be using the day as factors.

Examining the number of DE genes

Let’s now look at differential expression levels of genes.

Note that we will be using an adjusted p-value cutoff is 5%.

fit <- lmFit(data_voomed, design)
fit <- contrasts.fit(fit, contrasts=contrastMatrix)
efit <- eBayes(fit)
dt <- decideTests(efit)
summary(dt)
   D1 - D0 D3 - D0 D7 - D0 D1 - D3 D1 - D7 D3 - D7
-1      39      24      67      71      27      18
0       77      69      33      40      61      23
1       11      34      27      16      39      86

Although we see that most miRNA are not differentially expressed between day 1 and day 0, we can be suprised by the amount of differentially expressed between day 3 and day 7. From this table we could hypothesize that the majority of miRNA doesn’t have the “time” to get differentially expressed between day 0 and day 1. Often these genes get differentially expressed betweeen day 1 and 3, which could have multiple effect on the whole cell. Finally between day 3 and day 7, these same genes could get differentially expressed to “return” to a state close to the initial one. From this table we could even go one step further and say that often then miRNA get upregulated from day 1 to day 3 and then downregulated back to a “normal state” from day 3 to day 7. Of course we would need more proof to support this hypthesis, but that would be a plausible interpretation of the table.

Many miRNA are downregulated between day 1 and 3 and then upregulated again at day 7

If our previous hypothesis were correct, that would mean that most of the differentially expressed gene between day 1 and 3 are also differentially expressed in day 7. Let’s look at this with a nice venn diagramm:

nInversD1D3D7 <- nrow(dt[(dt[,4] * dt[,6]== -1), ])
nSameD1D3D7 <- nrow(dt[(dt[,4] * dt[,6]== 1), ])
nTotalD1D3D7 <- nrow(dt[dt[,4] != 0 | dt[,6] != 0,c(4,6)])
vennDiagram(dt[dt[,4] != 0 | dt[,6] != 0,c(4,6)], circle.col=c("turquoise", "salmon", "green","yellow"), main = "Venn diagramm differentially expressed D1-D3 and D3-7")

As hypothesised, we see that most of the miRNA are differntailly expressed in both cases. Now to go one step further we can compute the number of miRNA that are differentially expressed in the “opposite direction” between day1-day3 and day3-day7 . Indeed, if day 3 were a “transition state” as supposed, then most of the miRNA’s would get differntially expressed in one direction from day1 to day3 and then come back to “normal” from day3 to day7.

Out of the 120 differentially expressed genes in either D1-D3, D3-D7 or both, there are 58 miRNA that are differentially expressed in the other direction (while only 13 in the same direction)! That could be an indication that the hypothesis is correct, but would need further analysis.

As the hypothesis seems plausible, it would be interesting to look at these miRNA

# Finding genes that are differntailly expressed in the opposite direction between d1-3 and d3-7
commonDEgenesInverseDay1337 <- which(dt[,4]*dt[,6] == -1)
names(commonDEgenesInverseDay1337)
 [1] "hsa-let-7a-3p"   "hsa-let-7d-5p"   "hsa-let-7g-5p"   "hsa-miR-103a-3p" "hsa-miR-106b-5p" "hsa-miR-107"    
 [7] "hsa-miR-1226-3p" "hsa-miR-128-3p"  "hsa-miR-1307-5p" "hsa-miR-142-5p"  "hsa-miR-148b-3p" "hsa-miR-15a-5p" 
[13] "hsa-miR-16-5p"   "hsa-miR-16-2-3p" "hsa-miR-181a-5p" "hsa-miR-181b-5p" "hsa-miR-181b-3p" "hsa-miR-185-5p" 
[19] "hsa-miR-18a-5p"  "hsa-miR-199a-3p" "hsa-miR-199b-3p" "hsa-miR-199b-5p" "hsa-miR-21-3p"   "hsa-miR-221-3p" 
[25] "hsa-miR-222-3p"  "hsa-miR-23b-3p"  "hsa-miR-25-3p"   "hsa-miR-26a-5p"  "hsa-miR-26b-5p"  "hsa-miR-27b-3p" 
[31] "hsa-miR-29b-3p"  "hsa-miR-29c-3p"  "hsa-miR-301a-3p" "hsa-miR-30a-5p"  "hsa-miR-30b-5p"  "hsa-miR-30c-5p" 
[37] "hsa-miR-30d-5p"  "hsa-miR-30e-5p"  "hsa-miR-30e-3p"  "hsa-miR-324-3p"  "hsa-miR-331-3p"  "hsa-miR-339-5p" 
[43] "hsa-miR-342-3p"  "hsa-miR-361-5p"  "hsa-miR-361-3p"  "hsa-miR-374b-5p" "hsa-miR-423-3p"  "hsa-miR-425-5p" 
[49] "hsa-miR-425-3p"  "hsa-miR-484"     "hsa-miR-505-3p"  "hsa-miR-590-3p"  "hsa-miR-618"     "hsa-miR-652-3p" 
[55] "hsa-miR-766-3p"  "hsa-miR-769-5p"  "hsa-miR-93-3p"   "hsa-miR-98-3p"  

To further subset the miRNA, it is very probable (although would need additional tests) that the miRnA of interest are not differentially expressed between day 0 and day 1. Indeed we would like to find the miRNA that cause the difference between the groups but come back to normal at day 7.

# Finding genes that are differntailly expressed in the opposite direction between d1-3 and d3-7
commonDEgenesInverseDay1337not01 <- which(dt[,4]*dt[,6] == -1 & dt[,1] == 0)
names(commonDEgenesInverseDay1337not01)
 [1] "hsa-miR-103a-3p" "hsa-miR-106b-5p" "hsa-miR-107"     "hsa-miR-1226-3p" "hsa-miR-148b-3p" "hsa-miR-15a-5p" 
 [7] "hsa-miR-16-5p"   "hsa-miR-185-5p"  "hsa-miR-21-3p"   "hsa-miR-222-3p"  "hsa-miR-23b-3p"  "hsa-miR-25-3p"  
[13] "hsa-miR-29b-3p"  "hsa-miR-301a-3p" "hsa-miR-324-3p"  "hsa-miR-342-3p"  "hsa-miR-361-5p"  "hsa-miR-374b-5p"
[19] "hsa-miR-423-3p"  "hsa-miR-425-5p"  "hsa-miR-425-3p"  "hsa-miR-505-3p"  "hsa-miR-652-3p"  "hsa-miR-766-3p" 
[25] "hsa-miR-769-5p"  "hsa-miR-93-3p"   "hsa-miR-98-3p"  

Let’s plot a random subset of these miRNA

randRNA <- unique(names(commonDEgenesInverseDay1337not01))[sample(1:length(unique(names(commonDEgenesInverseDay1337not01))), 4, replace=FALSE)]
prepDatTop<-prepareData2(randRNA) #format data
f1 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==randRNA[1]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(randRNA[1]) 
f2 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==randRNA[2]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(randRNA[2])#106b-5p
f3 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==randRNA[3]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(randRNA[3])
f4 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==randRNA[4]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(randRNA[4]) 
grid.arrange(f1,f2,f3,f4,nrow=2) #put plots together

Finding genes that are often differentially between day 0/1 and day 3/7

From all our plots that clearly showed clusters between day 0, day 1 and day 3, day 7, we could want to ask ourselves the simple question of “what miRNA are differentially expressed between the clusters” ?

# Finding genes that are not zero in at least one comparaison between the groups
commonDEgenesDay0137 <- which(dt[,2]!=0 & dt[,3]!=0 & dt[,4]!=0 & dt[,5]!=0)
names(commonDEgenesDay0137)
 [1] "hsa-let-7d-3p"   "hsa-let-7e-5p"   "hsa-let-7i-5p"   "hsa-miR-106b-3p" "hsa-miR-106b-5p" "hsa-miR-125a-5p"
 [7] "hsa-miR-1307-3p" "hsa-miR-132-3p"  "hsa-miR-143-3p"  "hsa-miR-145-5p"  "hsa-miR-181b-5p" "hsa-miR-181b-3p"
[13] "hsa-miR-192-5p"  "hsa-miR-194-5p"  "hsa-miR-223-3p"  "hsa-miR-23a-3p"  "hsa-miR-27a-3p"  "hsa-miR-425-5p" 
[19] "hsa-miR-660-5p"  "hsa-miR-92a-3p"  "hsa-miR-93-5p"   "hsa-miR-942-5p" 

Let’s visualise it with a nice venn diagramm

vennDiagram(dt[,2:5], circle.col=c("turquoise", "salmon", "green","yellow"), main = "Venn diagramm showing differences between clusters")

PLot miRNA
#List of genes different between the two clusters
topGenes <- names(commonDEgenesDay0137)
#topGenes<-c("hsa-let-7d-3p","hsa-let-7i-5p","hsa-miR-125a-5p","hsa-miR-132-3p","hsa-miR-143-3p","hsa-miR-145-5p","hsa-miR-181b-3p","hsa-miR-185-5p","hsa-miR-192-5p","hsa-miR-19b-3p","hsa-miR-223-3p","hsa-miR-23a-3p","hsa-miR-26a-5p","hsa-miR-30e-5p","hsa-miR-425-5p","hsa-miR-425-3p","hsa-miR-92a-3p")
prepDatTop<-prepareData2(topGenes) #format data
#make dataset for probe 125a - remove cell with top expression
p125<-subset(prepDatTop,prepDatTop$gene==topGenes[6])
p125<-p125[-which(p125$gExp==max(p125$gExp)),]
f1 <- ggplot(data=p125,aes(cluster1,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(topGenes[6]) + ylim(-100,2000)
f2 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==topGenes[1]),aes(cluster1,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(topGenes[16]) + ylim(-100,2000)
f3 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==topGenes[2]),aes(cluster1,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(topGenes[5])+ ylim(-100,2000)#106b-5p
f4 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==topGenes[3]),aes(cluster1,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(topGenes[1]) + ylim(-100,2000)
grid.arrange(f1,f2,f3,f4,nrow=2) #put plots together

Finding genes that are differentially expressed between clusters but not whithin clusters

Finally what we are really interested in are genes that are differentially expressed between clusters but not whithin clusters: here are these genes:

# Finding genes that are differentially expressed between clusters but not whithin clusters
commonDEgenesBetweenNotWhithin <- which(dt[,2]!=0 & dt[,3]!=0 & dt[,4]!=0 & dt[,5]!=0 & dt[,1]==0 & dt[,6]==0)
names(commonDEgenesBetweenNotWhithin)
[1] "hsa-let-7d-3p"  "hsa-let-7i-5p"  "hsa-miR-192-5p" "hsa-miR-194-5p" "hsa-miR-660-5p" "hsa-miR-942-5p"

To have a better idea of the “results”, let’s randomly choose a miRNA which is differentially expressed between clusters but not whithin.

First let’s plot the log transformed data.

ggplot(dataf4[dataf4$mirna %in% names(commonDEgenesBetweenNotWhithin),], aes(x=exp, fill = group)) + 
  geom_density(alpha=0.5) +
  ggtitle("Random point plot for interaction miRNA-group") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) +
  theme(legend.position="none") + xlim(0,30) +
  facet_wrap(~mirna,ncol = 3)

To see “the real data” let’s also plot the same genes not log transformed.

ggplot(dataf4[dataf4$mirna %in% names(commonDEgenesBetweenNotWhithin),], aes(x=exp2, fill = group)) + 
  geom_density(alpha=0.5) +
  ggtitle("Random density plot for interaction miRNA-group: no log") +
  theme_bw() +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) +
  theme(legend.position="none") + xlim(-500,10000) +
  facet_wrap(~mirna,ncol = 3)

Let’s plot a random subset of these miRNA

randRNA <- unique(names(commonDEgenesBetweenNotWhithin))[sample(1:length(unique(names(commonDEgenesBetweenNotWhithin))), 4, replace=FALSE)]
prepDatTop<-prepareData2(randRNA) #format data
f1 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==randRNA[1]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(randRNA[1]) 
f2 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==randRNA[2]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(randRNA[2])#106b-5p
f3 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==randRNA[3]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(randRNA[3])
f4 <- ggplot(data=subset(prepDatTop,prepDatTop$gene==randRNA[4]),aes(cluster,gExp,colour=Population))+
  geom_jitter()+
  ggtitle(randRNA[4]) 
grid.arrange(f1,f2,f3,f4,nrow=2) #put plots together

References

  1. Dohner, H., Weisdorf, D.J., Bloomfield, C.D. Acute myeloid leukemia. N Eng J Med 373, 1136-1152 (2015).
  2. Hoseini, S.S., Cheung, N.K. Acute myeloid leukemia targets for bispecific antibodies. Blood Cancer Journal 7, e522 (2017).
  3. Sunami, Y., Araki, M., Kan, S., Ito, A., Hironaka, Y., Imai, M., Morishita, S., Ohsaka, A., Komatsu, N. Histone acetyltransferase PCAF is required for all-trans retinoic acid-induced granulocytic differentiation in leukemia cells. J Biol Chem, doi: 10.1074/jbc.M116.745398 (2017).
  4. Bartel, D.P. MicroRNAs: Genomics, biogenesis, mechanism, and function. Cell 116, 281-297 (2004).
  5. Jian, P., Li, ZW., Fang, TY., Jian, W., Zhuan, Z., Mei, LX., Yan, WS., Jian, N. Retinoic acid induces HL-60 differentiation via the upregulation of miR-663. J Hematol Oncol 4 doi: 10.1186/1756-8722-4-20 (2011).
  6. Garzon, R., Pichiorri, F., et al. MicroRNA gene expression during retinoic acid-induced differentiation of human acute promyelocytic leukemia. Oncogene 26, 4148-57 (2007).
  7. Faridani, OR., Abdullayev, I., Hagemann-Jensen, M., Schell, JP., Lanner, F., Sandberg, R. Single-cell sequencing of the small-RNA transcriptome. Nat Biotechnol 34, 1264-1266 (2016).
  8. Van der Maaten, LJP., Hinton, GE. Visualizing high-dimensional data using t-SNE. Journal of Machine Learning Research 9, 2579-2605 (2008).
  9. Warde-Farley, D., Donaldson, SL., et al. The GeneMANIA prediction server: biological network integration for gene prioritization and predicting gene function. Nucleic Acids Res 38, 214-20 (2010).
  10. Subramanian, A. et al. Gene set enrichment analysis: a knowledge-based approach for interpreting genome-wide expression profiles. Proc Natl Acad Sci USA 102, 15545-50 (2005).
  11. Risso, Davide, et al. “Normalization of RNA-seq data using factor analysis of control genes or samples.” Nature biotechnology 32.9 (2014): 896-902.
  12. Seqc/Maqc-Iii Consortium. “A comprehensive assessment of RNA-seq accuracy, reproducibility and information content by the Sequencing Quality Control Consortium.” Nature biotechnology 32.9 (2014): 903-914.
  13. Brase, Jan C., et al. “Circulating miRNAs are correlated with tumor progression in prostate cancer.” International journal of cancer 128.3 (2011): 608-616.
  14. Law, Charity W., et al. “Voom: precision weights unlock linear model analysis tools for RNA-seq read counts.” Genome biology 15.2 (2014): R29.
  15. Lun, Aaron TL, Davis J. McCarthy, and John C. Marioni. “A step-by-step workflow for low-level analysis of single-cell RNA-seq data with Bioconductor.” F1000Research 5 (2016).
  16. Smyth, Gordon K., et al. “limma: Linear Models for Microarray and RNA-Seq Data User’s Guide.” (2002).
  17. Law, Charity W., et al. “RNA-seq analysis is easy as 1-2-3 with limma, Glimma and edgeR.” F1000Research 5 (2016).
  18. Smyth, Gordon K. “Linear models and empirical bayes methods for assessing differential expression in microarray experiments.” Stat Appl Genet Mol Biol 3.1 (2004): 3.

Additional work that have been disregarded

General normalization

To be sure that the normalization is good I will continue the analysis with a more general normalization. The following is based on this page. By general I mean not specific to pikes.

dataInitial <- read.table("../Data/raw_data/expn_matrix.txt", header = TRUE, row.names = 1)
data_dayDESeq <- as.character(meta_data$Population)
data_dayDESeq <- recode(data_dayDESeq, "HL60D0" = "Day 0", "HL60D1" = "Day 1", "HL60D3" = "Day 3", "HL60D7" = "Day 7")
data_dayDESeq <-factor(data_dayDESeq)
deSeqDat <- newCountDataSet(dataInitial, data_dayDESeq)
# Note: actually it's not a real normalisation. Rather computing size factors depending on ratio of medians 
# if all size factors are roughly equal to one, the libraries have been sequenced equally deeply.
deSeqDat <- estimateSizeFactors(deSeqDat)
head(sizeFactors(deSeqDat))
HL60D0.C01_S1 HL60D0.C02_S2 HL60D0.C03_S3 HL60D0.C04_S4 HL60D0.C05_S5 HL60D0.C06_S6 
    0.5254052     1.1318130     0.9646220     0.3233751     1.3896389     0.9596427 
idx.nz <- apply(counts(deSeqDat), 1, function(x) { all(x > 0)})
nNZsamples <- sum(idx.nz)
#will not be needed: don't keep big data
remove(dataInitial)

We see that the number of non zero samples in all genes is very low compared to traditional RNA-seq: 5 nomrally in thousands. This may be a good reason to stick with the normalization with skie RNA.

#plotting the estimated dispersions against the mean normalized counts
deSeqDat <- estimateDispersions(deSeqDat)
plotDispEsts(deSeqDat)

multidensity( counts(deSeqDat, normalized = T),xlab="mean counts", xlim=c(0, 1000))

 multiecdf( counts(deSeqDat, normalized = T),xlab="mean counts", xlim=c(0, 1000))

The two charts above clearly shows that the second normalisation isn’t convincing. Indeed, the second chart assesses whether the normalization has worked, and the densities should overlapp since most of the genes are heavily affected by the experimental conditions. Note: The strange density chart could also be due to the fact that miRNA are very rarely expressed in every sample (as we have seen before).

Not using log transoformed data

Mean variance trend no log

Note: Seeing the strange mean variance plot above makes us think that maybe our count data doesn’t need to be log transformed. Let’s investigate the relationship between the mean and the variance of ou data.

#working ... HOW TO USE DATADESEQ ON NORMALIZED => NOT COUNTS ANY MORE?? + IS IT WORTH IT ? cAN we round ?
#dataDESeq <- rlog(as.matrix(data), blind=FALSE )
#takes only genes that are non zero in many samples
#t <- n_data[which(rowSums(n_data) > 300),] 
#tries plotting directly without voom
mSqrt4 <- rowMeans(dataNZFilterdBis^0.4)
RowSigmaSqrt16 <- function(x) {
  (rowSums((x - rowMeans(x))^2)/(dim(x)[2] - 1))^(1/16)
}
v <- RowSigmaSqrt16(dataNZFilterdBis^0.4)
dataNoLog <- data.frame(meanSqrt4 = mSqrt4, sigmaSqrt16 = v)
ggplot(dataNoLog, aes(x=meanSqrt4, y=sigmaSqrt16)) +
    geom_point(shape=1) +    # Use hollow circles
    geom_smooth(method=loess,   # Add loess regression line
                se=FALSE,
                colour = "red") + # Don't add shaded confidence region
  ylim(0, 2) + 
  xlim(0,20) + 
  ggtitle("Mean variance relationship not Voom") +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) 

From this plot we clearly see that our variance grows with 16th poqer of our 4th mean. The reasoning behind this different relationship is still unclear to us , but it could indicate that we maybe shouldn’t work with the default limma package and that we should maybe do the analysis manually.

Note: using a log tranformation as in limma can help to make the data closer to normal, but as we’ve seen before it makes it bimodal here which is not really better. The real question to investigate is whether the errors of the linear model are normal with the non-logged transformed data. If they are, then we should maybe not log transform it.

Without log transformation

data_voomed$E <- (2^data_voomed$E - 0.5)^0.4
loessFit <- loess ( sigmaSqrt16~meanSqrt4, dataNoLog) 
loessPredict  <- predict (loessFit, dataNoLog$meanSqrt4) 
weights <- 1/(loessPredict)^16
data_voomed$weights <- weights
fit <- lmFit(data_voomed, design)
fit <- contrasts.fit(fit, contrasts=contrastMatrix)
efit <- eBayes(fit)
topTable(efit, coef=colnames(coef(efit)))
plotSA(efit)

#will not be needed: don't keep big data
remove(fit)
remove(data_voomed)

Examining the number of DE genes

Let’s now look at differential expression levels of genes. Note that the adjusted p-value cutoff is 5%.

dt <- decideTests(efit)
summary(dt)
   D1 - D0 D3 - D0 D7 - D0 D1 - D3 D1 - D7 D3 - D7
-1      37      26      60      57      30      21
0       73      61      41      50      58      32
1       17      40      26      20      39      74

As we have already previously seen in the multiple plots of the previous section: most differential expressed genes are between day 0/1 and 3/7. We also note that a lot of genes in Day 3 and 7 are downregulated (compared to day 0 and 1).

Finding genes that are often differentially expressed compared to day 0

Let’s look at the genes that are always differentially expressed compared to day 0

# Finding genes that are not zero in every comparaison with day 0
commonDEgenesDay0 <- which(dt[,1]!=0 & dt[,2]!=0 & dt[,3]!=0)
names(commonDEgenesDay0)
 [1] "hsa-let-7a-5p"   "hsa-let-7a-3p"   "hsa-let-7d-3p"   "hsa-miR-132-3p"  "hsa-miR-143-3p"  "hsa-miR-146a-5p"
 [7] "hsa-miR-17-5p"   "hsa-miR-17-3p"   "hsa-miR-181a-3p" "hsa-miR-181b-5p" "hsa-miR-181b-3p" "hsa-miR-19a-3p" 
[13] "hsa-miR-19b-3p"  "hsa-miR-20a-5p"  "hsa-miR-22-3p"   "hsa-miR-223-3p"  "hsa-miR-27a-3p"  "hsa-miR-28-3p"  
[19] "hsa-miR-30c-5p"  "hsa-miR-629-5p"  "hsa-miR-99b-5p" 

Let’s visualise it with a nice venn diagramm

vennDiagram(dt[,1:3], circle.col=c("turquoise", "salmon", "green"), main = "Venn diagramm showing differences with day 0")

Finding genes that are often differentially between day 0/1 and day 3/7

Let’s look at the genes that are always differentially between day 0/1 and 3/7 as they seem to be the ones of interest.

# Finding genes that are not zero in every comparaison with day 0
commonDEgenesDay0137 <- which(dt[,2]!=0 & dt[,3]!=0 & dt[,4]!=0 & dt[,5]!=0)
names(commonDEgenesDay0137)
 [1] "hsa-let-7e-5p"   "hsa-let-7i-5p"   "hsa-miR-103a-3p" "hsa-miR-106b-3p" "hsa-miR-106b-5p" "hsa-miR-125a-5p"
 [7] "hsa-miR-1307-3p" "hsa-miR-132-3p"  "hsa-miR-143-3p"  "hsa-miR-145-5p"  "hsa-miR-155-5p"  "hsa-miR-194-5p" 
[13] "hsa-miR-20a-5p"  "hsa-miR-222-3p"  "hsa-miR-223-3p"  "hsa-miR-23a-3p"  "hsa-miR-26a-5p"  "hsa-miR-27a-3p" 
[19] "hsa-miR-30c-5p"  "hsa-miR-425-5p"  "hsa-miR-660-5p"  "hsa-miR-92a-3p"  "hsa-miR-93-5p"   "hsa-miR-942-5p" 

Let’s visualise it with a nice venn diagramm

vennDiagram(dt[,2:5], circle.col=c("turquoise", "salmon", "green","yellow"),main = "Venn diagramm showing differences between clusters")

Finding genes that are differentially expressed between clusters but not whithin clusters

Finally what we are really interested in are genes that are differentially expressed between clusters but not whithin clusters: here are these genes:

# Finding genes that are differentially expressed between clusters but not whithin clusters
commonDEgenesBetweenNotWhithin <- which(dt[,2]!=0 & dt[,3]!=0 & dt[,4]!=0 & dt[,5]!=0 & dt[,1]==0 & dt[,6]==0)
names(commonDEgenesBetweenNotWhithin)
[1] "hsa-let-7i-5p"   "hsa-miR-106b-3p" "hsa-miR-194-5p"  "hsa-miR-26a-5p"  "hsa-miR-660-5p"  "hsa-miR-92a-3p"  "hsa-miR-942-5p" 
ggplot(dataf4[dataf4$mirna %in% names(commonDEgenesBetweenNotWhithin),], aes(x=exp2, fill = group)) + 
  geom_density(alpha=0.5) +
  ggtitle("Random point plot for interaction miRNA-group") +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) +
  theme(legend.position="none") + xlim(0,30) +
  facet_wrap(~mirna,ncol = 3)

ggplot(dataf4[dataf4$mirna %in% names(commonDEgenesBetweenNotWhithin),], aes(x=exp2, fill = group)) + 
  geom_density(alpha=0.5) +
  ggtitle("Random density plot for interaction miRNA-group: no log") +
  theme(text = element_text(size=14),plot.title = element_text(hjust = 0.5)) +
  theme(legend.position="none") + xlim(-500,10000) +
  facet_wrap(~mirna,ncol = 3)

#will not be needed: don't keep big data
remove(dataf4)
LS0tCnRpdGxlOiAiU3VtbWFyeSB3b3JrIgphdXRob3I6IEtldmluIEplcHNvbiwgU3RlcGhhbmllIEJsYWluLCBNYWNrZW56aWUgS2lubmV5LCBHYXJyZXR0IE1jQ2FydGh5LCBLZW5uZXRoCiAgQXNrZWxzb24gYW5kIFlhbm4gRHVib2lzCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKaGVhZGVyLWluY2x1ZGVzOiBcdXNlcGFja2FnZXthbXNtYXRofQotLS0KCiMgSW50cm9kdWN0aW9uIAoKIyMgTW90aXZhdGlvbiBhbmQgQmFja2dyb3VuZCBXb3JrCgpBY3V0ZSBteWVsb2lkIGxldWtlbWlhIChBTUwpIGlzIHRoZSBtb3N0IGNvbW1vbiBhY3V0ZSBoZW1hdG9sb2dpYyBtYWxpZ25hbmN5IGluIGFkdWx0cywgYW5kIGlzIGNoYXJhY3Rlcml6ZWQgYnkgaW5maWx0cmF0aW9uIG9mIHRoZSBib25lIG1hcnJvdyBhbmQgYmxvb2QgYnkgaGlnaGx5IHByb2xpZmVyYXRpdmUsIGFibm9ybWFsbHkgZGlmZmVyZW50aWF0ZWQgcHJvbXllbG9pZCBjZWxscyAoMSkuIFRoZSBzdGFuZGFyZCBwcmFjdGljZXMgZm9yIHRyZWF0aW5nIEFNTCBoYXZlIGJlZW4gY29uc2lzdGVudCBmb3IgdGhlIHBhc3QgdGhyZWUgZGVjYWRlcy4gVGhlIGZpcnN0IHN0YWdlIG9mIHRyZWF0bWVudCBpcyBpbmR1Y3Rpb24sIHdoZXJlIHBhdGllbnRzIHVuZGVyZ28gY2hlbW90aGVyYXB5IGluIG9yZGVyIHRvIHN0aW11bGF0ZSB0aGUgY29tcGxldGUgZGlmZmVyZW50aWF0aW9uIG9mIGltbWF0dXJlLCBjYW5jZXJvdXMgcHJvbXllbG9jeXRlcyAoMikuIEEgY29tbW9ubHkgdXNlZCBjaGVtb3RoZXJhcGV1dGljIGluZHVjaW5nIGFnZW50IGlzIGFsbCB0cmFucy1yZXRpbm9pYyBhY2lkIChBVFJBKSwgd2hpY2ggc3RpbXVsYXRlcyBncmFudWxvY3l0aWMgZGlmZmVyZW50aWF0aW9uIGFuZCBwcmV2ZW50cyBwcm9teWVsb2N5dGUgcHJvbGlmZXJhdGlvbiB3aXRoIGV4dHJlbWVseSBoaWdoIGVmZmljYWN5LiBXaGlsZSBBVFJBIGhhcyBiZWVuIHVzZWQgYXMgYSBzdGFuZGFyZCBBTUwgdHJlYXRtZW50IGZvciBkZWNhZGVzLCB0aGUgdW5kZXJseWluZyBtZWNoYW5pc20gaGFzIG5vdCBiZWVuIGZ1bGx5IGVzdGFibGlzaGVkICgzKS4gVGhpcyBpcyBhbiBpbXBvcnRhbnQgYXJlYSBvZiBBTUwgcmVzZWFyY2gsIGJlY2F1c2UgdW5kZXJzdGFuZGluZyB0aGUgbWVjaGFuaXNtIG9mIEFUUkEtaW5kdWNlZCBkaWZmZXJlbnRpYXRpb24gd291bGQgaGVscCBjbGluaWNpYW5zIGludmVzdGlnYXRlIHRoZSByb290LWNhdXNlcyB3aGljaCBwcmV2ZW50IG5vcm1hbCBoZW1hdG9wb2lldGljIHN0ZW0gY2VsbCBkaWZmZXJlbnRpYXRpb24uCgpTaW5jZSB0aGVpciBkaXNjb3ZlcnkgaW4gMTk5MywgdGhlcmUgaGFzIGJlZW4gYSBzaWduaWZpY2FudCBhbW91bnQgb2YgcmVzZWFyY2ggc2hvd2luZyB0aGUgaW1wb3J0YW5jZSBvZiBtaVJOQXMgaW4gY2FuY2VyIGRldmVsb3BtZW50ICg0KS4gVG8gYW5hbHl6ZSB0aGUgcm9sZSBvZiBtaVJOQSBpbiBBTUwsIHNldmVyYWwgcHVibGlzaGVkIHN0dWRpZXMgaGF2ZSB1c2VkIGJ1bGsgbWljcm9hcnJheSBhbmFseXNlcyB0byBkZXNjcmliZSB0aGUgbWlSTkEgZXhwcmVzc2lvbiBwYXR0ZXJucyBhc3NvY2lhdGVkIHdpdGggQVRSQS1pbmR1Y2VkIGRpZmZlcmVudGlhdGlvbi4gVGhlc2UgcGFwZXJzIGhhdmUgc2hvd24gdGhhdCB0aGVyZSBhcmUgYSBmZXcga2V5IG1pUk5Bc+KAlCBzdWNoIGFzIG1pUi02NjMsIG1pUi0yMjMsIGFuZCBtaVItNDk04oCUIHdoaWNoIGJlY29tZSBzaWduaWZpY2FudGx5IHVwcmVndWxhdGVkIGluIHJlc3BvbnNlIHRvIEFUUkEtaW5kdWNlZCBkaWZmZXJlbnRpYXRpb24gKDUsIDYpLiBIb3dldmVyLCBhIG1vcmUgaW4tZGVwdGggYW5hbHlzaXMgdXNpbmcgbmV4dC1nZW5lcmF0aW9uIHNlcXVlbmNpbmcgdGVjaG5pcXVlcyBpcyByZXF1aXJlZCB0byBhY2hpZXZlIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlc2UgbWVjaGFuaXNtcy4KClRoaXMgcHJvamVjdCB3aWxsIGFuYWx5emUgc2luZ2xlLWNlbGwgbWlSTkEgc2VxdWVuY2luZyBkYXRhIGZyb20gSEwtNjAgY2VsbHPigJQgYSBwcm9teWVsb2JsYXN0IGNlbGwtbGluZSB3aGljaCBpcyBjb21tb25seSB1c2VkIGFzIGEgZGlmZmVyZW50aWF0aW9uIG1vZGVs4oCUIGFsb25nIGEgNy1kYXkgdGltZS1jb3Vyc2Ugb2YgQVRSQSB0cmVhdG1lbnQuIE91ciBvdmVyYWxsIG9iamVjdGl2ZSBpcyB0byB1dGlsaXplIG5leHQtZ2VuZXJhdGlvbiBzZXF1ZW5jaW5nIGRhdGEgdG8gaW52ZXN0aWdhdGUgdGhlIHJvbGUgb2YgbWlSTkEgaW4gQVRSQS1pbmR1Y2VkIGRpZmZlcmVudGlhdGlvbi4gSW4gZG9pbmcgc28sIHdlIGhvcGUgdG8gZ2FpbiBmdXJ0aGVyIGluc2lnaHQgaW50byB3aGljaCBtb2xlY3VsYXIgcGF0aHdheXMgYXJlIGltcG9ydGFudCBpbiB0aGlzIHByb2Nlc3MgYW5kIGRldGVybWluZSB3aGV0aGVyIHRlbXBvcmFsIGNoYW5nZXMgaW4gbWlSTkEgZXhwcmVzc2lvbiBhcmUgaW52b2x2ZWQuIEZ1cnRoZXJtb3JlLCB3ZSB3aWxsIGRldGVybWluZSB3aGV0aGVyIEFUUkEtaW5kdWNlZCBkaWZmZXJlbnRpYXRpb24gbGVhZHMgdG8gYSBob21vZ2Vub3VzIGN1bHR1cmUgb2YgZ3JhbnVsb2N5dGVzLCBvciBpZiB0aGVyZSBhcmUgc2VwYXJhdGUgZGlmZmVyZW50aWF0aW9uIHBhdGh3YXlzIHdoaWNoIHJlc3VsdCBpbiBzaWduaWZpY2FudCBjZWxsdWxhciBoZXRlcm9nZW5laXR5LiBPdXIgZ2VuZXJhbCBoeXBvdGhlc2lzIGlzIHRoYXQgdGhlIEhMLTYwIG1pUk5BIGV4cHJlc3Npb24gc2lnbmF0dXJlcyB3aWxsIGNoYW5nZSBhcyB0aGUgY2VsbHMgZGlmZmVyZW50aWF0ZSByZWxhdGl2ZSB0byB0aW1lLiBNb3JlIHNwZWNpZmljYWxseSwgd2UgcHJlZGljdCB0aGF0IHRoZSBleHByZXNzaW9uIG9mIHdlbGwta25vd24gZGlmZmVyZW50aWF0aW9uLWFzc29jaWF0ZWQgbWlSTkFz4oCUIHN1Y2ggYXMgbWlSLTY2MywgbWlSLTIyMywgYW5kIG1pUi00OTTigJQgd2lsbCBiZWNvbWUgaW5jcmVhc2luZ2x5IHVwcmVndWxhdGVkIGFzIHRyZWF0bWVudCBwcm9ncmVzc2VzICg1LCA2KS4gTGFzdGx5LCBkdWUgdG8gdGhlIGhldGVyb2dlbmVpdHkgb2YgY2FuY2VyIGNlbGxzLCB3ZSBleHBlY3QgdG8gb2JzZXJ2ZSBkaXN0aW5jdCBzdWJwb3B1bGF0aW9ucyBvZiBITC02MCBjZWxscyBmb2xsb3dpbmcgQVRSQS1pbmR1Y2VkIGRpZmZlcmVudGlhdGlvbjsgdGhpcyBtYXkgcHJvdmlkZSBldmlkZW5jZSBzdWdnZXN0aW5nIG11bHRpcGxlIGJpb2xvZ2ljYWwgcGF0aHdheXMgYXJlIHRha2luZyBwbGFjZS4KCiMjIERhdGFzZXQKCk91ciBkYXRhIHNldCBmb3IgdGhlIDctZGF5IHRpbWUtY291cnNlIG9mIEFUUkEgdHJlYXRtZW50IG9uIEhMLTYwIGNlbGxzIGluY2x1ZGVzIGRhdGEgZm9yIDMxOSBzaW5nbGUgY2VsbHM6IDkyIGNlbGxzIGF0IERheSAwIG9mIHRyZWF0bWVudCwgODkgY2VsbHMgYXQgRGF5IDEsIDg1IGNlbGxzIGF0IERheSAzLCBhbmQgNTMgY2VsbHMgYXQgRGF5IDcuIE9uIGVhY2ggZGF5LCBpbmRpdmlkdWFsIGNlbGxzIHdlcmUgY29sbGVjdGVkIGFuZCBpc29sYXRlZCBpbiBhIG1pY3JvZmx1aWRpYyBjaGlwLCBhbmQgdGhlbiBydW4gdGhyb3VnaCBhIHNpbmdsZS1jZWxsIG1pUk5BIHNlcXVlbmNpbmcgcHJvdG9jb2wgc2ltaWxhciB0byB0aGUgb25lIGRlc2NyaWJlZCBieSBGYXJpZGFuaSBldCBhbC4gaW4gMjAxNiAoNSkuIFRoZSByZXN1bHRpbmcgbGlicmFyaWVzIHdlcmUgc2VxdWVuY2VkIG9uIGFuIElsbHVtaW5hIE1pU2VxIHBsYXRmb3JtLiAoRm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24sIHJlZmVyZW5jZSBvdXIgZ3JvdXAncyBbRGF0YSBGb2xkZXJdKGh0dHBzOi8vZ2l0aHViLmNvbS9TVEFUNTQwLVVCQy90ZWFtX1RoZS1ab28tQ3Jldy90cmVlL21hc3Rlci9EYXRhKSkKCiMjIEFpbXMgYW5kIE1ldGhvZG9sb2d5CgpUaGUgcHJpbmNpcGFsIGJpb2xvZ2ljYWwgcXVlc3Rpb24gdGhhdCB3ZSBob3BlIHRvIGludmVzdGlnYXRlIGlzIHdoZXRoZXIgdGhlIEhMLTYwIGNlbGxzIGhhdmUgZGlzdGluY3QgbWlSTkEgZXhwcmVzc2lvbiBzaWduYXR1cmVzIGF0IGRpZmZlcmVudCB0aW1lIHBvaW50cyBvZiBBVFJBIHRyZWF0bWVudC4gQWRkaXRpb25hbGx5LCBpdCB3aWxsIGJlIGludGVyZXN0aW5nIHRvIG1lYXN1cmUgdGhlIGhldGVyb2dlbmVpdHkgb2YgdGhlc2UgcG9wdWxhdGlvbnMuIElmIGRpc3RpbmN0IHN1YnBvcHVsYXRpb25zIGFyZSBwcmVzZW50LCBpcyB0aGVyZSBldmlkZW5jZSBvZiBtdWx0aXBsZSBBVFJBLWluZHVjZWQgZGlmZmVyZW50aWF0aW9uIHBhdGh3YXlzIGluIGVmZmVjdD8gQXJlIHRoZSBleHByZXNzaW9uIHBhdHRlcm5zIG9mIHRoZXNlIG1pUk5BcyBjb25zaXN0ZW50IHRocm91Z2hvdXQgdHJlYXRtZW50LCBvciBkbyB0aGV5IGZvbGxvdyBhIHNwZWNpZmljIHBhdHRlcm4gd2l0aCByZXNwZWN0IHRvIHRpbWU/IFdoaWNoIG1pUk5BcyBhbmQgd2hpY2ggYmlvbG9naWNhbCBtZWNoYW5pc21zIGFyZSBpbnZvbHZlZCBpbiBBVFJBLWluZHVjZWQgZGlmZmVyZW50aWF0aW9uPyBJbiBvcmRlciB0byBhbnN3ZXIgdGhlc2UgcXVlc3Rpb25zLCB3ZSB3aWxsIHRha2UgYWR2YW50YWdlIG9mIHNldmVyYWwgY29tcHV0YXRpb25hbC9zdGF0aXN0aWNhbCBhcHByb2FjaGVzLiBXZSB3aWxsIHVzZSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBpbiBjb25qdW5jdGlvbiB3aXRoIHAtdmFsdWUgaGlzdG9ncmFtcyB0byBib3RoIGlkZW50aWZ5IGRpZmZlcmVuY2VzIGluIG1pUk5BIGV4cHJlc3Npb24gc2lnbmF0dXJlcyBhY3Jvc3MgdGhlIHRpbWUgcG9pbnRzIGFuZCBkZXRlcm1pbmUgd2hldGhlciBvciBub3QgdGhlc2UgaWRlbnRpZmllZCBkaWZmZXJlbmNlcyBhcmUgdHJ1bHkgc2lnbmlmaWNhbnQuIEluIG9yZGVyIHRvIGludmVzdGlnYXRlIGhldGVyb2dlbmVpdHkgb2Ygc3VicG9wdWxhdGlvbnMgYW5kIHRoZSBkaWZmZXJlbnRpYXRpb24gcGF0aHdheXMsIHdlIHdpbGwgdXNlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGZ1bmN0aW9ucyBhbmQgdC1TTkUgcGxvdHMgdG8gaGVscCB2aXN1YWxpemUgYW5kIGFuYWx5emUgdGhlIGV2b2x1dGlvbiBvZiB0aGUgSEwtNjAgY2VsbHMgKDgpLiBMYXN0bHksIGluIG9yZGVyIHRvIGRldGVybWluZSB3aGljaCBiaW9sb2dpY2FsIG1lY2hhbmlzbXMgYXJlIGludm9sdmVkIGluIEFUUkEtaW5kdWNlZCBncmFudWxvY3l0aWMgZGlmZmVyZW50aWF0aW9uLCB3ZSB3aWxsIHVzZSDigJxndWlsdC1ieS1hc3NvY2lhdGlvbuKAnSBhbGdvcml0aG1zIHN1Y2ggYXMgZ2VuZSBzZXQgZW5yaWNobWVudCBhbmFseXNpcywgZnVuY3Rpb25hbCBlbnJpY2htZW50IGFuYWx5c2lzIGFuZCBHZW5lTUFOSUEgKDksIDEwKS4gCgojIEFuYWx5c2lzCgojIyBTZXR0aW5nIHVwIHRoZSBlbnZpcm9ubWVudAoKYGBge3IgbG9hZGluZ19kYXRhLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBQYWNrYWdlcwpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShsaW1tYSkKbGlicmFyeShncGxvdHMpCmxpYnJhcnkoZWRnZVIpCmxpYnJhcnkoZGV2dG9vbHMpCmxpYnJhcnkoZ2diaXBsb3QpCmxpYnJhcnkoREVTZXEpCmxpYnJhcnkoZ2VuZXBsb3R0ZXIpCmxpYnJhcnkoUnRzbmUpCmxpYnJhcnkoTk1GKQpsaWJyYXJ5KGdyaWRFeHRyYSkKCnNldC5zZWVkKDEyMzQpICMgbWFrZXMgZXZlcnl0aGluZyByZXByb2R1Y3RpYmxlIAoKbWV0YV9kYXRhIDwtIHJlYWQudGFibGUoIi4uL0RhdGEvcmF3X2RhdGEvbWV0YV9kYXRhLnR4dCIsIGhlYWRlciA9IFRSVUUpCmRhdGFJbml0aWFsIDwtIHJlYWQudGFibGUoIi4uL0RhdGEvcmF3X2RhdGEvZXhwbl9tYXRyaXgudHh0IiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkKc3Bpa2VfZGF0YSA8LSByZWFkLnRhYmxlKCIuLi9EYXRhL3Jhd19kYXRhL2V4cG5fbWF0cml4X3NwaWtlLnR4dCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpCmBgYAoKIyMgTm9ybWFsaXphdGlvbiAKCk5vcm1hbGl6YXRpb24gaXMgZXNzZW50aWFsIGluIFJOQXNlcS4gVW5saWtlIG1STkEsIHRoZXJlIGFyZSB2ZXJ5IGZldyBjb25zdGl0dXRpdmVseSBhY3RpdmUgbWlSTkEgZ2VuZXMgb3Ig4oCcaG91c2Uta2VlcGluZ+KAnSBtaVJOQXMgdGhhdCBhcmUgZXhwcmVzc2VkIGF0IGNvbnNpc3RlbnQgbGV2ZWxzLiBBcyBhIHJlc3VsdCwgd2UgY2Fubm90IHVzZSBzdGFuZGFyZCB0ZWNobmlxdWVzIHN1Y2ggYXMgcXVhbnRpbGUgbm9ybWFsaXphdGlvbi4gVG8gZGVhbCB3aXRoIHRoaXMgaXNzdWUsIHdlIHJlbHkgaGVhdmlseSBvbiB0aGUgc3Bpa2UtaW4gc2l6ZSBmcmFjdGlvbnMgdG8gbm9ybWFsaXplIHRoZSBkYXRhIGFuZCBhY2NvdW50IGZvciBwb3RlbnRpYWwgYmF0Y2ggZWZmZWN0cy4gQWx0aG91Z2ggc3Bpa2UtaW4gZGF0YSBoYXZlIGJlZW4gc2hvd24gbm90IHRvIGJlIHNhdGlzZnlpbmcgZW5vdWdoIGZvciBSTkEtc2VxIFsoMTEpXShodHRwOi8vd3d3Lm5hdHVyZS5jb20vbmJ0L2pvdXJuYWwvdjMyL245L2Z1bGwvbmJ0LjI5MzEuaHRtbCkgWygxMildKGh0dHA6Ly93d3cubmF0dXJlLmNvbS9uYnQvam91cm5hbC92MzIvbjkvZnVsbC9uYnQuMjk1Ny5odG1sKS4gSXQgaXMgY29tbW9uIGluIG1pUk5Bc2VxIGJlY2F1c2Ugb2YgdGhlICJob3VzZS1rZWVwaW5nIiBpc3N1ZSBkaXNjdXNzZWQgYWJvdmUgWygxMyldKGh0dHA6Ly9vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvMTAuMTAwMi9pamMuMjUzNzYvZnVsbCkuCgpUaGUgbm9ybWFsaXphdGlvbiBpcyBicm9rZW4gdXAgaW50byAyIG1haW4gcGFydHMuIEZpcnN0IHdlIHNjYWxlIHRoZSBjb3VudHMgb2YgZWFjaCBzYW1wbGUgYnkgdGhlIGNvdW50cyBvZiBzcGlrZSBSTkEgaW4gdGhpcyBzYW1wbGUuIFNlY29uZGx5IHdlIHJlbW92ZSBhbGwgY2VsbHMgdGhhdCBhcmUgZGVhZC4KCk5vdGU6IFdoZW4gd2Ugd2lsbCBhbmFseXNlIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiBlYWNoIG1pUk5BLCB3ZSB3aWxsIGFkZGl0aW9uYWx5IG5vcm1hbGl6ZSBieSB0aGUgbGlicmFyeSBzaXplLCBpbmRlZWQgdGhhdCBpcyBkb25lIGJ5IGRlZmF1bHQgaW4gVm9vbS4KCmBgYHtyIG5vcm1hbGl6ZV9rZXZpbn0KIyBmaW5kaW5nIHN1bSBvZiBjb2x1bW4gYW5kIGFkZGluZyBpdCBhcyBuZXcgcm93CnNwaWtlX2RhdGEgPC0gcmJpbmQoc3Bpa2VfZGF0YSwgIlRvdGFscyIgPSBjb2xTdW1zKHNwaWtlX2RhdGEpKQpzY2FsZWRfdG90YWxzIDwtIHNwaWtlX2RhdGFbIlRvdGFscyIsIF0KYXZlcmFnZV9yZWFkcyA8LSByb3dNZWFucyhzY2FsZWRfdG90YWxzKQoKIyBOT1JNQUxJWklORyBCWSBTUElLRS1STkEKc2NhbGVkX3RvdGFscyA8LSByYmluZChzY2FsZWRfdG90YWxzLCAiU2NhbGUiID0gc2NhbGVkX3RvdGFsc1sxLCBdIC8gYXZlcmFnZV9yZWFkcykgCgojIHNldHRpbmcgZGltX25hbWVzIGZvciB1cGNvbWluZyBtYXRyaXgKZGltX25hbWVzIDwtIGxpc3Qocm93Lm5hbWVzKGRhdGFJbml0aWFsKSkgICAgICAgCmRpbV9uYW1lc1syXSA8LSBsaXN0KGNvbG5hbWVzKChkYXRhSW5pdGlhbCkpKQoKIyBwcmUtbWFraW5nIGEgbWF0cml4Cm5vcm1hbGl6ZWRfZGF0YSA8LSBtYXRyaXgobnJvdyA9IG5yb3coZGF0YUluaXRpYWwpLCBuY29sID0gbmNvbChkYXRhSW5pdGlhbCksIGRpbW5hbWVzID0gZGltX25hbWVzKSAKCiMgZm9yIGxvb3Agd2hpY2ggc2NhbGVzIGRhdGEgYnkgdGhlIHNwaWtlIG1pUk5BIGFuZCBzYXZlcyBpdCBhcyBub3JtYWxpemVkX2RhdGEKZm9yKGkgaW4gMTogbmNvbChkYXRhSW5pdGlhbCkpeyAgIAogIG5vcm1hbGl6ZWRfZGF0YVsgLCBpXSA8LSBkYXRhSW5pdGlhbFsgLCBpXSAvIChzY2FsZWRfdG90YWxzWzIsIGldKQp9Cm5vcm1hbGl6ZWRfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKG5vcm1hbGl6ZWRfZGF0YSkKCnRvdGFsX2RhdGEgPC0gcmJpbmQoc3Bpa2VfZGF0YSwgIlRvdGFsIFNwaWtlIiA9IGNvbFN1bXMoc3Bpa2VfZGF0YSksICJUb3RhbCBSZWFkcyBOb3JtIiA9IGNvbFN1bXMobm9ybWFsaXplZF9kYXRhKSwgIlRvdGFsIFJlYWRzIEluaXQiID0gY29sU3VtcyhkYXRhSW5pdGlhbCkpCgojIyMgS0VFRVA/Pz8KaWYgKEZBTFNFKXsKdG90YWxfY2VsbHVsYXIgPC0gdG90YWxfZGF0YVsiVG90YWwgUmVhZHMgTm9ybSIsIF0KYXZlcmFnZV9yZWFkcyA8LSByb3dNZWFucyh0b3RhbF9jZWxsdWxhcikKY2VsbF9zY2FsZXMgPC0gcmJpbmQodG90YWxfY2VsbHVsYXIsICJTY2FsZSIgPSB0b3RhbF9jZWxsdWxhclsxLCBdIC8gYXZlcmFnZV9yZWFkcykgI2RpdmlkaW5nIHRvdGFsIHNwaWtlIHZhbHVlcyBieSBhdmVyYWdlIHNwaWtlIHZhbHVlcwpkaW1fbmFtZXMgPC0gbGlzdChyb3cubmFtZXMoZGF0YUluaXRpYWwpKSAgICAgICAjc2V0dGluZyBkaW1fbmFtZXMgZm9yIHVwY29taW5nIG1hdHJpeApkaW1fbmFtZXNbMl0gPC0gbGlzdChjb2xuYW1lcygoZGF0YUluaXRpYWwpKSkKbm9ybWFsaXplZF9kYXRhMiA8LSBtYXRyaXgobnJvdyA9IG5yb3coZGF0YUluaXRpYWwpLCBuY29sID0gbmNvbChkYXRhSW5pdGlhbCksIGRpbW5hbWVzID0gZGltX25hbWVzKSAjcHJlLW1ha2luZyBhIG1hdHJpeApmb3IoaSBpbiAxOiBuY29sKGRhdGFJbml0aWFsKSl7ICAgI2ZvciBsb29wIHdoaWNoIHNjYWxlcyBkYXRhIGFuZCBzYXZlcyBpdCBhcyBub3JtYWxpemVkX2RhdGEKICBub3JtYWxpemVkX2RhdGEyWyAsIGldIDwtIG5vcm1hbGl6ZWRfZGF0YVsgLCBpXSAvIChjZWxsX3NjYWxlc1syLCBpXSkKfQpub3JtYWxpemVkX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShub3JtYWxpemVkX2RhdGEyKQp9CiMjIyMKCiMgREVMRVRJTkcgREVBRCBDRUxMUwphbGl2ZV9pbmRleGVzIDwtIGxpc3QoKSAjaW5pdGlhbGl6aW5nIGxpc3RzCmsgPC0gMSAgI2luaXRpYWxpemluZyBjb3VudGVyCmZvcihpIGluIDE6bmNvbCh0b3RhbF9kYXRhKSl7CiAgaWYodG90YWxfZGF0YVsiVG90YWwgUmVhZHMgSW5pdCIsIGldID4gMjAwMDApewogICAgYWxpdmVfaW5kZXhlc1trXSA8LSBpCiAgICBrIDwtIGsgKyAxCiAgfQp9CgphbGl2ZV9kYXRhIDwtIG5vcm1hbGl6ZWRfZGF0YVsgLCBhcy5udW1lcmljKGFsaXZlX2luZGV4ZXMpXQphbGl2ZV9tZXRhX2RhdGEgPC0gbWV0YV9kYXRhW2FzLm51bWVyaWMoYWxpdmVfaW5kZXhlcyksIF0KCiMgc2F2aW5nIGNvbXBsZXRlbHkgbm9ybWFsaXplZCBkYXRhIGFzIG5fZGF0YSBhbmQgbl9tZXRhX2RhdGEKbl9kYXRhIDwtIGFsaXZlX2RhdGEKbl9tZXRhX2RhdGEgPC0gYWxpdmVfbWV0YV9kYXRhCgojIG1ha2VzIGdyb3VwIGFuZCBkZXNpZ24gbWF0cml4CmRhdGFfZGF5IDwtIGFzLmNoYXJhY3RlcihuX21ldGFfZGF0YSRQb3B1bGF0aW9uKQpkYXRhX2RheSA8LSByZWNvZGUoZGF0YV9kYXksICJITDYwRDAiID0gIkRheSAwIiwgIkhMNjBEMSIgPSAiRGF5IDEiLCAiSEw2MEQzIiA9ICJEYXkgMyIsICJITDYwRDciID0gIkRheSA3IikKZGF0YV9kYXkgPC0gZmFjdG9yKGRhdGFfZGF5KQoKZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+MCArIG5fbWV0YV9kYXRhJFBvcHVsYXRpb24pCiMgZGVsZXRlcyAibl9tZXRhX2RhdGEkUG9wdWxhdGlvbiIgZnJvbSBuYW1lIG9mIGNvbHVtbnMKY29sbmFtZXMoZGVzaWduKSA8LSBnc3ViKCJuX21ldGFfZGF0YVxcJFBvcHVsYXRpb25ITDYwIiwgIiIsIGNvbG5hbWVzKGRlc2lnbikpCgojIEtlZXBzIGdlbmVzIHdpdGhvdXQgYWxsIHplcm9zIG1pUk5BCmRhdGFOWiA8LSBuX2RhdGFbd2hpY2gocm93U3VtcyhuX2RhdGEpID4gMCksXSAKCiMgbWFraW5nIHRoZSBjb250cmFzdCBtYXRyaXggZm9yIGNvbXBhcmluZyBlYWNoIGdyb3VwCmNvbnRyYXN0TWF0cml4IDwtIG1ha2VDb250cmFzdHMoRDEtRDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRDMtRDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRDctRDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRDEtRDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRDEtRDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRDMtRDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiRDAiLCJEMSIsIkQzIiwiRDciKSkKCiMgUmVtb3ZlcyB1bm5lY2Vzc2FyeSBiaWcgZGF0YQpyZW1vdmUobm9ybWFsaXplZF9kYXRhKQpyZW1vdmUobl9kYXRhKQpyZW1vdmUoYWxpdmVfZGF0YSkKcmVtb3ZlKGFsaXZlX21ldGFfZGF0YSkKcmVtb3ZlKGRhdGFJbml0aWFsKQpgYGAKCiMjIERhdGEgZXhwbG9yYXRpb24KCiMjIyBQQ0EKCkZvciBhbGwgb2Ygb3VyIFBDQSBwbG90cyB3ZSB3aWxsIGZpcnN0IHVzZSBhIGxvZyB0cmFuc2Zvcm1hdGlvbiB0byBtYWtlIHRoZSBkYXRhIGFwcHJveGltYXRpdmVseSBmb2xsb3cgdGhlIGhvbW9zY2VkYXN0aWNpdHkgYXNzdW1wdGlvbi4gCgojIyMjIFBDQSBzdGFuZGFyZGl6ZWQKCktub3dpbmcgdGhlIGltcG9ydGFuY2Ugb2YgaGF2aW5nIHN0YW5kYXJkaXplZCBkYXRhIGZvciBQQ0EsIHdlIGNvdWxkIHRoaW5rIG9mIHN0YW5kYXJkaXppbmcgb3Vycy4gVGhpcyBpbiBnZW5lcmFsIGlzIGEgZ29vZCBpZGVhLCBhcyB2YXJpYWJsZXMgYXJlIG9mdGVuIG5vdCBvbiB0aGUgc2FtZSBzY2FsZSBhbmQgdGh1cyBjYW5ub3QgYmUgY29tcGFyZWQgZGlyZWN0bHkuIEhlcmUsIGhvd2V2ZXIsIHdlIGhhdmUgZGltZW5zaW9ucyB0aGF0IGFyZSBjb21wYXJhYmxlIGFzIHRoZXkgZWFjaCByZXByZXNlbnQgdGhlIG51bWJlciBvZiBtaVJOQSBpbiB0aGUgc2FtcGxlLiBJdCBpcyB0aGVyZWZvcmUgYSBnb29kIGlkZWEgbm90IHRvIHN0YW5kcmRpemUsIGFzIGl0IHdpbGwgdW5uZWNlc3NhcmlseSBkZWNyZWFzZSB0aGUgdmFyaWFuY2UgZXhwbGFpbmVkLiBNb3JlIGluZm9ybWF0aW9uIG9uIHRoaXMgY2FuIGJlIGZvdW5kIG9uIHRoZSBmb2xsb3dpbmcgZm9ydW1zOiBbaGVyZV0oaHR0cDovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8xMDU1OTIvbm90LW5vcm1hbGl6aW5nLWRhdGEtYmVmb3JlLXBjYS1naXZlcy1iZXR0ZXItZXhwbGFpbmVkLXZhcmlhbmNlLXJhdGlvKSBvciBbaGVyZV0oaHR0cDovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy82OTE1Ny93aHktZG8td2UtbmVlZC10by1ub3JtYWxpemUtZGF0YS1iZWZvcmUtYW5hbHlzaXMpLiBPdXQgb2YgY3VyaW9zaXR5IHdlIHdpbGwgc3RpbGwgcGxvdCB0aGUgc3RhbmRhcmRpemVkIGRhdGE6CgpgYGB7ciBQQ0FfTlp9CiNMb2cgdHJhbnNmb3JtCmxvZ19ub3JtYWxpemVkX2RhdGFOWiA8LSBsb2coZGF0YU5aKQpsb2dfbm9ybWFsaXplZF9kYXRhTlpbbG9nX25vcm1hbGl6ZWRfZGF0YU5aID09ICItSW5mIl0gPC0gMAoKZGF0YV9wY2FOWiA8LSBwcmNvbXAodChsb2dfbm9ybWFsaXplZF9kYXRhTlopLCBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFKQpnZ2JpcGxvdChkYXRhX3BjYU5aLCAKICAgICAgICAgICAgICBzY2FsZSA9IDEsIAogICAgICAgICAgICAgIG9icy5zY2FsZSA9IDEsIAogICAgICAgICAgICAgIHZhcm5hbWUuYWJicmV2ID0gRkFMU0UsCiAgICAgICAgICAgICAgdmFyLmF4ZXMgPSBGQUxTRSwKICAgICAgICAgICAgICBwYy5iaXBsb3QgPVRSVUUsCiAgICAgICAgICAgICAgY2lyY2xlID0gVFJVRSwgCiAgICAgICAgICAgICAgZ3JvdXBzID0gZGF0YV9kYXksIAogICAgICAgICAgICAgIGVsbGlwc2U9IFRSVUUpICsKICBnZ3RpdGxlKCJQQ0Egd2l0aCBzdGFuZGFyZGl6YXRpb24iKSArIAogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIAoKIyB3aWxsIG5vdCBiZSBuZWVkZWQ6IGRvbid0IGtlZXAgYmlnIGRhdGEKcmVtb3ZlKGRhdGFfcGNhTlopCnJlbW92ZShsb2dfbm9ybWFsaXplZF9kYXRhTlopCmBgYAoKRnJvbSB0aGlzIHBsb3Qgd2Ugc2VlIHRoYXQgYXMgd2UndmUgc3VwcG9zZWQsIHRoZSB2YXJpYW5jZSBleHBsYWluZWQgaXMgbG93ZXIgdGhhbiBpbiB0aGUgZmlyc3Qgb25lLiBXZSB0aHVzIGNvbmNsdWRlIHRoYXQgd2Ugc2hvdWxkIHVzZSBhIG5vbiBzdGFuZGFyZGl6ZWQgUENBLgoKIyMjIyBQQ0Egbm8gc3RhbmRhcmRpemF0aW9uCgpgYGB7ciBQQ0F9CiMgbG9nIHRyYW5zZm9ybQpsb2dfbm9ybWFsaXplZF9kYXRhIDwtIGxvZyhkYXRhTlopCmxvZ19ub3JtYWxpemVkX2RhdGFbbG9nX25vcm1hbGl6ZWRfZGF0YSA9PSAiLUluZiJdIDwtIDAKCmRhdGFfcGNhIDwtIHByY29tcCh0KGxvZ19ub3JtYWxpemVkX2RhdGEpKQpnZ2JpcGxvdChkYXRhX3BjYSwgCiAgICAgICAgICAgICAgc2NhbGUgPSAxLCAKICAgICAgICAgICAgICBvYnMuc2NhbGUgPSAxLCAKICAgICAgICAgICAgICB2YXJuYW1lLmFiYnJldiA9IEZBTFNFLAogICAgICAgICAgICAgIHZhci5heGVzID0gRkFMU0UsCiAgICAgICAgICAgICAgcGMuYmlwbG90ID1UUlVFLAogICAgICAgICAgICAgIGNpcmNsZSA9IFRSVUUsIAogICAgICAgICAgICAgIGdyb3VwcyA9IGRhdGFfZGF5LCAKICAgICAgICAgICAgICBlbGxpcHNlPSBUUlVFKSArCiAgZ2d0aXRsZSgiUENBIHdpdGhvdXQgc3RhbmRhcmRpemF0aW9uIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICAKCiMgd2lsbCBub3QgYmUgbmVlZGVkOiBkb24ndCBrZWVwIGJpZyBkYXRhCnJlbW92ZShkYXRhX3BjYSkKcmVtb3ZlKGxvZ19ub3JtYWxpemVkX2RhdGEpCmBgYApOb3RlOiBUaGUgUENBIGdyYXBoIGlzIGludGVyZXN0aW5nIGFzIGl0IHNob3dzIHRoYXQgYWZ0ZXIgZWFjaCBkYXkgdGhlIGNsdXN0ZXJzIHNlZW0gdG8gZ28gZG93biBpbiBQQzIgYW5kIGV4dGVuZCB0aGVpciB2YXJpYW5jZSBpbiBQQzEuIFBDMiBpcyBwcm9iYWJseSBtYWlubHkgYSBzaW5nbGUgbWlSTkEgd2hpY2ggZ2V0cyBsZXNzIGV4cHJlc3NlZCBhcyBkYXlzIHBhc3MuCgojIyMjIFBDQSB0b3AgNTAgCgpMZXQncyBub3cgb25seSBwbG90IHRoZSBQQ0Egb2YgdGhlIDUwIGdlbmVzLiBBcyB3ZSd2ZSBkaXNjdXNzZWQgYmVmb3JlLCB3ZSB3aWxsIG5vdCBzdGFuZGFyZGl6ZSB0aGUgZGF0YS4KCmBgYHtyIFBDQV90b3A1MH0KIyBhcnJhbmdpbmcgZGF0YSBiYXNlZCBvbiB0b3RhbCBleHByZXNzaW9uCmluZGV4VG9wRmlmdHkgPC0gc29ydChyb3dTdW1zKGRhdGFOWiksIGluZGV4PVQsIGRlY3JlYXNpbmc9VFJVRSkkaXhbMTo1MF0KdG9wRmlmdHkgPC0gZGF0YU5aW2luZGV4VG9wRmlmdHksXQoKIyBMb2cgdHJhbnNmb3JtCmxvZ190b3BGaWZ0eSA8LSBsb2codG9wRmlmdHkpCmxvZ190b3BGaWZ0eVtsb2dfdG9wRmlmdHkgPT0gIi1JbmYiXSA8LSAwCgojIFBDQQpkYXRhX3RvcEZpZnR5IDwtIHByY29tcCh0KGxvZ190b3BGaWZ0eSkpCmdnYmlwbG90KGRhdGFfdG9wRmlmdHksIAogICAgICAgICAgICAgIHNjYWxlID0gMSwgCiAgICAgICAgICAgICAgb2JzLnNjYWxlID0gMSwgCiAgICAgICAgICAgICAgdmFybmFtZS5hYmJyZXYgPSBGQUxTRSwKICAgICAgICAgICAgICB2YXIuYXhlcyA9IEZBTFNFLAogICAgICAgICAgICAgIHBjLmJpcGxvdCA9VFJVRSwKICAgICAgICAgICAgICBjaXJjbGUgPSBUUlVFLCAKICAgICAgICAgICAgICBncm91cHMgPSBkYXRhX2RheSwgCiAgICAgICAgICAgICAgZWxsaXBzZT0gVFJVRSkgKwogIGdndGl0bGUoIlBDQSB0b3AgNTAgZ2VuZXMiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSxwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgCgojIHdpbGwgbm90IGJlIG5lZWRlZDogZG9uJ3Qga2VlcCBiaWcgZGF0YQpyZW1vdmUoZGF0YV90b3BGaWZ0eSkKcmVtb3ZlKGxvZ190b3BGaWZ0eSkKcmVtb3ZlKHRvcEZpZnR5KQpgYGAKCiMjIyBDb3JyZWxhdGlvbiBoZWF0bWFwCgpgYGB7ciBjb3JyZWxhdGlvbl9oZWF0bWFwfQojIGNvbXB1dGluZyBzYW1wbGUtc2FtcGxlIGNvcnJlbGF0aW9ucwpub3JtYWxpemVkX2RhdGFfY29yIDwtIGNvcihkYXRhTlopICAKCmNvcl9kYXQgPC0gYXMuZGF0YS5mcmFtZShub3JtYWxpemVkX2RhdGFfY29yKQoKbGFicyA8LSBsaXN0KFBvcHVsYXRpb24gPSBuX21ldGFfZGF0YSRQb3B1bGF0aW9uKQoKTk1GOjphaGVhdG1hcChjb3JfZGF0LCAKICAgICAgICAgICAgICBhbm5Db2wgPSBsYWJzLCBhbm5Sb3cgPSBsYWJzLCBSb3d2ID0gTkEsIENvbHYgPSBOQSwgbGFiUm93ID0gTkEsIGxhYkNvbCA9IE5BLCAKICAgICAgICAgICAgICBtYWluID0gIlNhbXBsZS1TYW1wbGUgQ29ycmVsYXRpb24gTWF0cml4IikKCiN3aWxsIG5vdCBiZSBuZWVkZWQ6IGRvbid0IGtlZXAgYmlnIGRhdGEKcmVtb3ZlKG5vcm1hbGl6ZWRfZGF0YV9jb3IpCmBgYAoKT24gdGhpcyBjb3JyZWxhdGlvbiBoZWF0bWFwIHdlJ3ZlIHNjYWxlZCB0aGUgdmFsdWVzIHRvIGJlIGNlbnRlcmVkIGFyb3VuZCB6ZXJvLiBUaGlzIGV4cGxhaW5zIHdoeSB0aGUgIm9ic2VydmVkIGNvcnJlbGF0aW9uIiBoYXMgdmFsdWVzIHRoYXQgY2FuIGJlIG5lZ2F0aXZlLiBJbiByZWFsaXR5LCBhbGwgdGhlIHRoZSBzYW1wbGUgYXJlIHJlbGF0aXZlbHkgc3Ryb25nbHkgY29ycmVsYXRlZC4gVGhpcyBpcyBub3Qgc3VwcmlzaW5nIGFzIG1hbnkgbWlSTkEgYXJlIG5vdCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBzYW1wbGVzLgoKSW4gdGhpcyBoZWF0IG1hcCB3ZSBjbGVhcmx5IHNlZSB0aGUgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBzYW1wbGVzIG9mIGRheSAwIGFuZCAxIGFuZCBiZXR3ZWVuIHRoZSBzYW1wbGVzIG9mIGRheSAzIGFuZCA3LiAKCiMjIyB0LVNORSBwbG90cwoKIyMjIyBHZW5lcmFsIHQtU05FCgpMZXQncyBwbG90IHRoZSB0LVNORSBwbG90cyB0byBzZWUgaWYgd2UgY2FuIGZpbmQgY2x1c3RlcnMgYW5kIC8gb3Igb3V0bGllcnMuCgpgYGB7ciB0c25lX0dlbmVyYWx9CgojIG9yZGVyaW5nIGRhdGEgYnkgZXhwbgphbGl2ZV90c25lX2RhdGEgPC0gY2JpbmQoZGF0YU5aLCAiYXZnX2V4cG4iID0gcm93TWVhbnMoZGF0YU5aKSwgIm1pUk5BIiA9IHJvdy5uYW1lcyhkYXRhTlopKSAKYWxpdmVfdHNuZV9kYXRhIDwtIGRwbHlyOjphcnJhbmdlKGFsaXZlX3RzbmVfZGF0YSxkZXNjKGF2Z19leHBuKSkKYWxpdmVfdHNuZV9kYXRhIDwtIHQoYWxpdmVfdHNuZV9kYXRhKQpjb2xuYW1lcyhhbGl2ZV90c25lX2RhdGEpIDwtIGFsaXZlX3RzbmVfZGF0YVsibWlSTkEiLCBdCiNyZW1vdmVzIHJvd3MgdGhhdCB3ZXJlIGFkZGVkIGZvciBvcmRlcmluZwphbGl2ZV90c25lX2RhdGEgPC0gYWxpdmVfdHNuZV9kYXRhWyFyb3duYW1lcyhhbGl2ZV90c25lX2RhdGEpICVpbiUgYygibWlSTkEiLCJhdmdfZXhwbiIpLCBdCgojIFJ1bnMgdHNuZQp0c25lX291dCA8LSBSdHNuZShkaXN0KGFsaXZlX3RzbmVfZGF0YSkpCgojIFNob3cgdGhlIG9iamVjdHMgaW4gdGhlIDJEIHRzbmUgcmVwcmVzZW50YXRpb24KcXBsb3QoeD10c25lX291dCRZWywxXSwgeT10c25lX291dCRZWywyXSxjb2xvcj1kYXRhX2RheSkgKyAKICBsYWJzKGNvbG91ciA9ICJDZWxsIFR5cGUiLCB4ID0gInRzbmUxIiwgeSA9ICJ0c25lMiIpKwogIGdndGl0bGUoInQtU05FIGFsaXZlIGNlbGxzIGFuZCBub24gemVybyBnZW5lcyIpICsKICB0aGVtZV9idygpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSAKCiN3aWxsIG5vdCBiZSBuZWVkZWQ6IGRvbid0IGtlZXAgYmlnIGRhdGEKcmVtb3ZlKHRzbmVfb3V0KQpgYGAKCgpBcyB3aXRoIHRoZSBjb3JyZWxhdGlvbiBoZWF0bWFwLCB3ZSBzZWUgdGhhdCAgdGhlIHNhbXBsZXMgZnJvbSBkYXkgMCBhbmQgMSBhcmUgY2x1c3RlcmVkIHRvZ2V0aGVyIHdoaWxlIHRob3NlIGZyb20gZGF5IDMgYW5kIDcgYXJlIGNsdXN0ZXJlZCB0b2dldGhlci4gVGhlcmUgYWxzbyBzZWVtcyB0byBiZSBhIHNtYWxsIGRpZmZlcmVuY2UgYmV0d2VlbiBkYXlzIG9mIHRoZSBzYW1lIGNsdXN0ZXIsIGJ1dCB0aGVzZSBkaWZmZXJlbmNlIGFyZSBzbWFsbGVyIHRoYW4gYmV0d2VlbiBjbHVzdGVycy4KCiMjIyMgdC1TTkUgb2YgRGF5IDAgYW5kIERheSAxCgpOb3cgdGhhdCB3ZSBoYXZlIHNlZW4gdGhhdCB0aGUgc2FtcGxlcyBmcm9tIGRheSAwIGFuZCAxIGFyZSBjbG9zZWx5IHJlbGF0ZWQuIExldCdzIHRyeSB0byB0YWtlIG9ubHkgdGhvc2UgMiBpbiBvcmRlciB0byBzZWUgaWYgd2UgY2FuIHNlcGFyYXRlIHRoZW0gZnJvbSBlYWNoIG90aGVyLiAKCmBgYHtyIHRzbmVfZGF5MDF9CiMgdHNuZSBvbiBEYXkgMC9EYXkgMSBjZWxscyBvbmx5CmluZGV4RGF5MDEgPC0gd2hpY2goZGF0YV9kYXkgJWluJSBjKCJEYXkgMCIsIkRheSAxIikpCiMgc2VsZWN0aW5nIG9ubHkgZGF5IDAgYW5kIGRheSAxIGNlbGxzCmdyb3VwX29uZV9kYXRhIDwtIGFsaXZlX3RzbmVfZGF0YVtpbmRleERheTAxLCBdIAoKIyBSdW4gVFNORQp0c25lX291dCA8LSBSdHNuZShkaXN0KGdyb3VwX29uZV9kYXRhKSkgCgojIFNob3cgdGhlIG9iamVjdHMgaW4gdGhlIDJEIHRzbmUgcmVwcmVzZW50YXRpb24KcXBsb3QoeD10c25lX291dCRZWywxXSwgeT10c25lX291dCRZWywyXSxjb2xvcj1kYXRhX2RheVtpbmRleERheTAxXSkgKwogIGxhYnMoY29sb3VyID0gIkNlbGwgVHlwZSIsIHggPSAidHNuZTEiLCB5ID0gInRzbmUyIikgKwogIGdndGl0bGUoInQtU05FIG9ubHkgZGF5IDAgYW5kIDEiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSxwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgCgojd2lsbCBub3QgYmUgbmVlZGVkOiBkb24ndCBrZWVwIGJpZyBkYXRhCnJlbW92ZSh0c25lX291dCkKYGBgCgpUaGUgdC1TTkUgY2FuIG5vdCBjbGVyYWx5IHNlcGFyYXRlIHRoZSAyIHNhbXBsZXMgZnJvbSBlYWNoIG90aGVyLiBGaW5hbGx5IGxldCdzIGludmVzdGlnYXRlIHdoZXRoZXIgdGhleSBjYW4gYmUgc2VwYXJhdGVkIHdpdGggYSB0LVNORSBpbiAzIGRpbWVuc2lvbjoKCmBgYHtyIHRzbmVfZGF5MDFfM0R9CnRzbmVfb3V0IDwtIFJ0c25lKGRpc3QoZ3JvdXBfb25lX2RhdGEpLCBkaW1zID0gMykgIyBSdW4gVFNORSBpbiAzIGRpbWVuc2lvbnMKCnBsb3RfbHkoeD10c25lX291dCRZWywxXSwKICAgICAgICB5PXRzbmVfb3V0JFlbLDJdLAogICAgICAgIHo9dHNuZV9vdXQkWVssM10sCiAgICAgICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgICAgICBjb2xvcj1kYXRhX2RheVtpbmRleERheTAxXSwKICAgICAgICBtb2RlPSJtYXJrZXJzIikgJT4lIAogIGxheW91dCh0aXRsZSA9ICd0LVNORSBkYXkgMCBhbmQgMSAzRCcpCgojd2lsbCBub3QgYmUgbmVlZGVkOiBkb24ndCBrZWVwIGJpZyBkYXRhCnJlbW92ZSh0c25lX291dCkKcmVtb3ZlKGdyb3VwX29uZV9kYXRhKQpgYGAKCkV2ZW4gd2l0aCB0aGUgMyBkaW1lbnNpb25hbCB0LVNORSwgaXQgaXMgaGFyZCAvIGltcG9zc2libGUgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiBkYXkgMCBhbmQgMS4gSW5kZWVkLCB3ZSBuZWVkIHRvIHJlbWVtYmVyIHRoYXQgdGhlIGNvbG91cnMgd2VyZSBhZGRlZCBhbmQgbm90IGZvdW5kIGJ5IHRoZSB0LXNuZSBwbG90cy4gVGhpcyByZWFsbHkgc2hvd3MgaG93IHNpbWlsYXIgZGF5IDAgYW5kIDEgc2VlbSB0byBiZS4KCiMjIyMgdC1TTkUgb2YgRGF5IDMgYW5kIERheSA3CgpOb3cgbGV0J3MgbG9vayBhdCB0aGUgY2x1c3RlciBEYXkgMyBhbmQgNywgdG8gc2VlIGlmIHRoZXkgY2FuIGJlIGRpc3Rpbmd1aXNoZWQuCgpgYGB7ciB0c25lX2RheTM3fQojdHNuZSBvbiBEYXkgMy9EYXkgNyBjZWxscyBvbmx5CmluZGV4RGF5MzcgPC0gd2hpY2goZGF0YV9kYXkgJWluJSBjKCJEYXkgMyIsIkRheSA3IikpCiNzZWxlY3Rpbmcgb25seSBkYXkgMyBhbmQgZGF5IDcgY2VsbHMKZ3JvdXBfdHdvX2RhdGEgPC0gYWxpdmVfdHNuZV9kYXRhW2luZGV4RGF5MzcsIF0gCgojIFJ1biBUU05FCnRzbmVfb3V0IDwtIFJ0c25lKGRpc3QoZ3JvdXBfdHdvX2RhdGEpKSAKIyBTaG93IHRoZSBvYmplY3RzIGluIHRoZSAyRCB0c25lIHJlcHJlc2VudGF0aW9uCnFwbG90KHg9dHNuZV9vdXQkWVssMV0sIHk9dHNuZV9vdXQkWVssMl0sY29sb3I9ZGF0YV9kYXlbaW5kZXhEYXkzN10pICsKICBsYWJzKGNvbG91ciA9ICJDZWxsIFR5cGUiLCB4ID0gInRzbmUxIiwgeSA9ICJ0c25lMiIpICsKICBnZ3RpdGxlKCJ0LVNORSBvbmx5IGRheSAzIGFuZCA3IikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIAoKI3dpbGwgbm90IGJlIG5lZWRlZDogZG9uJ3Qga2VlcCBiaWcgZGF0YQpyZW1vdmUodHNuZV9vdXQpCnJlbW92ZShhbGl2ZV90c25lX2RhdGEpCmBgYAoKQXMgYmVmb3JlLCB0aGUgMiB0eXBlIG9mIGNlbGxzIGNhbm5vdCBiZSBkaXN0aW5ndWlzaGVkIHdpdGggYSB0LVNORSBwbG90IGluIDJELiBUaGV5IHdpbGwgcHJvYmFibHkgbm90IGJlIGRpc3Rpbmd1aXNoYWJsZSBpbiAzRCBidXQgbGV0J3MgYmUgc3VyZToKCmBgYHtyIHRzbmVfZGF5MzdfM0R9CnRzbmVfb3V0IDwtIFJ0c25lKGRpc3QoZ3JvdXBfdHdvX2RhdGEpLCBkaW1zID0gMykgIyBSdW4gVFNORSBpbiAzIGRpbWVuc2lvbnMKCnBsb3RfbHkoeD10c25lX291dCRZWywxXSwKICAgICAgICB5PXRzbmVfb3V0JFlbLDJdLAogICAgICAgIHo9dHNuZV9vdXQkWVssM10sCiAgICAgICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgICAgICBjb2xvcj1kYXRhX2RheVtpbmRleERheTM3XSwKICAgICAgICBtb2RlPSJtYXJrZXJzIikgJT4lIAogIGxheW91dCh0aXRsZSA9ICd0LVNORSBkYXkgMyBhbmQgNyAzRCcpCgojd2lsbCBub3QgYmUgbmVlZGVkOiBkb24ndCBrZWVwIGJpZyBkYXRhCnJlbW92ZSh0c25lX291dCkKcmVtb3ZlKGdyb3VwX3R3b19kYXRhKQpgYGAKClRoZSBzZWNvbmQgY2x1c3RlciBjYW5ub3QgYmUgZnVsbHkgc2VwZXJhdGVkIGZ1cnRoZXIgd2l0aCAzIGRpbWVuc2lvbmFsIHQtU05FLCBidXQgaXQgaXMgYWxyZWFkeSBjbG9zZXIgdGhhbiBpbiAyRC4KCiMjIENvbXBhcmluZyB0aGUgZXhwcmVzc2lvbiBvZiBtaVJOQQoKSW4gb3JkZXIgdG8gZmluZCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgd2Ugd2lsbCBiZSBmaXR0aW5nIGEgbGluZWFyIG1vZGVsLiAKCioqTm90ZSoqIHRoYXQgZm9yIGZpdHRpbmcgbGluZWFyIG1vZGVscywgd2UgY2Fubm90IHVzZSB0aGUgREFUQVNFUSBsaWJyYXJ5IGFzIGl0IHJlcXVpcmVzIGNvdW50cyAoaS5lLiBkaXNjcmV0ZSB2YWx1ZXMpLiBXZSBub3cgaGF2ZSBjb250aW51b3VzIHZhbHVlcyBiZWNhdXNlIG9mIHRoZXkgd2F5IHdlIG5vcm1hbGl6ZWQgdGhlIGRhdGEuCgojIyMgTWVhbi1WYXJpYW5jZSB0cmVuZAoKSXQgd291bGQgYmUgYW4gZXJyb3IgdG8gZGlyZWN0bHkgZml0IGEgbGluZWFyIG1vZGVsIHRvIG91ciBkYXRhLCBpbmRlZWQgUk5BLXNlcSBpcyBjb3VudCBkYXRhIHdoaWNoIGhhcyBzdHJvbmcgaGV0ZXJvc2NlZGFzdGljaXR5LiBUbyBwcm92ZSB0aGlzLCBsZXQncyBub3RpY2UgdGhhdCBpZiB3ZSByYW5kb21seSBwaWNrICRSX3tsaWJ9JCBSTkEgZ2l2ZW4gdGhhdCB0aGUgYWJ1bmRhbmNlIChwcm9wb3Rpb24pIG9mIGEgZ2VuZSAkZyQgaXMgJFxhbHBoYSQuIFRoZW4gd2Ugd2lsbCBzZWUgJFJfZyQgc3VjaCBSTkEgd2hlcmU6CiQkUl9nIFxzaW1cIFxtYXRoY2Fse0Jpbn0oUl97bGlifSxcYWxwaGEpJCQKV2hpY2ggY2FuIGJlIGFwcHJveGltYXRlZCBieSAKJCRSX2cgXHNpbVwgXG1hdGhjYWx7UG9pc30oUl97bGlifSAqIFxhbHBoYSkkJApUYWtpbmcgJGxpbV97Ul97bGlifSAqIFxhbHBoYSBccmlnaHRhcnJvdyBcaW5mdHl9JAokJFJfZyBcc2ltXCBcbWF0aGNhbHtOfShSX3tsaWJ9ICogXGFscGhhLFJfe2xpYn0gKiBcYWxwaGEpJCQKV2hpY2ggY2xlYXJseSBzaG93cyB0aGF0IGNvdW50IGRhdGEgdmlvbGF0ZXMgdGhlIGFzc3VtcHRpb24gb2YgaG9tb3NjZWRhc3RpY2l0eS4gVGhlIHByb2JsZW0gaXMgdGhhdCBvcmRpbmFyeSBsZWFzdCBzcXVhcmVzIGFzc3VtZXMgdGhlIHNhbWUgdmFyaWFuY2UgYXQgZWFjaCBwb2ludC4gSWYgdGhlIHZhcmlhbmNlIGlzIGhpZ2hlciBpbiBjZXJ0YWluIHJlZ2lvbnMgdGhhbiB0aGVzZSByZWdpb25zIHdvdWxkIGdldCBtb3JlIHdlaWdodHMgdGhhbiB0aGV5IHNob3VsZC4gSW5kZWVkICRUdGVzdCBccHJvcHRvIFxmcmFjezF9e1xzaWdtYX0kLiAKClRoZXJlIGFyZSBtYW55IHBvc3NpYmxlIHdheXMgdG8gdGFrZSB0aGlzIHByb2JsZW0gaW50byBhY2NvdW50LiBXZSBjaG9zZSB0byBnbyB3aXRoIGxpbW1hIHZvb20gbWV0aG9kIGFzIGl0IGlzIGEgY29tcGV0aXRpdmUgbWV0aG9kIFsoMTQpXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9nYi0yMDE0LTE1LTItcjI5KSB0aGF0IHdvcmtzIHdlbGwgd2l0aCBsb3cgY291bnQgUk5Bc2VxIFsoMTUpXShodHRwczovL2YxMDAwcmVzZWFyY2guY29tL2FydGljbGVzLzUtMjEyMi92MSkuClRoZSBpZGVhIGlzIHNpbXBseSB0byBmaXQgYSB3ZWlnaHRlZCByZWdyZXNzaW9uLCB3aXRoIHRoZSB3ZWlnaHRzIGludmVyc2VseSBwcm9wb3J0aW9uYWwgdG8gdGhlIHZhcmlhbmNlIG9mIHRoZSBwb2ludC4gVGhlIHZhcmlhbmNlIG9mIHRoZSBwb2ludCBpcyBlc3RpbWF0ZWQgdXNpbmcgYSBsb3dlc3Mgc21vb3RoZXIgaW4gYSBsb2cobWVhbikgLSBzcXJ0KHZhcmlhbmNlKSB0cmVuZC4KCgojIyMjIEluaXRpYWwgTWVhbi1WYXJpYW5jZSBUcmVuZAoKV2l0aCB0aGlzIGV4cGxhbmF0aW9uIHdlIHNlZSB0aGUgaW1wb3J0YW5jZSBvZiBhIG1lYW4gLSB2YXJpYW5jZSBwbG90LCBsZXQncyBub3cgcGxvdCBpdC4KCmBgYHtyIGludGlhbF9tZWFuX3ZhcmlhbmNlfQpkZ2UgPC0gREdFTGlzdChjb3VudHM9ZGF0YU5aLCBncm91cCA9IGRhdGFfZGF5KQoKIyBhcHBsaWVzIFRNTSBub3JtYWxpemF0aW9uIHRvIGRnZQojZGdlIDwtIGNhbGNOb3JtRmFjdG9ycyhkZ2UpCmRhdGFfdm9vbWVkIDwtIHZvb20oZGdlLGRlc2lnbixwbG90PVRSVUUpCgojd2lsbCBub3QgYmUgbmVlZGVkOiBkb24ndCBrZWVwIGJpZyBkYXRhCnJlbW92ZShkZ2UpCmBgYAoKVGhlIG1lYW4tdmFyaWFuY2UgdHJlbmQgaXMgc3VwcmlzaW5nOiBpdCBkb2Vzbid0IGxvb2sgZmxhdCBlbm91Z2guIFRoaXMgY291bGQgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggdGhlIG5vcm1hbGl6YXRpb24gb2Ygb3VyIGRhdGEsIGJ1dCBhZnRlciBmdXJ0aGVyIGFuYWx5c2lzIHdlIGRpZG4ndCBmaW5kIGFueSBpc3N1ZS4gTm90ZSB0aGF0IHRoZSBkYXRhIG1heSB3ZWxsIGJlIHN0cmFuZ2UgZHVlIHRvIHRoZSBuYXR1cmUgb2YgbWlSTkEtc2VxIGRhdGEsIGluZGVlZCBpbiBSTkFzZXEgd2Ugb2Z0ZW4gaGF2ZSBoaWdoIHZhbHVlIG9mIGNvdW50cyBidXQgaW4gbWlSTkEgdGhlIG51bWJlciBvZiBjb3VudHMgYXJlIG11Y2ggbG93ZXIuIFRoaXMgY291bGQgY2F1c2UgdGhlIHNwaWtlIHdlIHNlZSBiZWxvdyB4ID0gemVybyAoaS5lLiB2ZXJ5IGxvdyBtZWFuKS4KCkxldHMgY2hlY2sgd2hldGhlciB0aGUgc3RyYW5nZSBtZWFuIHZhcmlhbmNlIGlzIGR1ZSB0byB0aGUgY2x1c3RlcnMgdGhhdCB3ZSBoYXZlLgoKYGBge3IgaGlzdG9ncmFtbV92b29tfQpkZiA8LSBkYXRhX3Zvb21lZCRFCmRhdGFmPC0gbXV0YXRlKGRhdGEuZnJhbWUodChkZikpLCBncm91cCA9IGRhdGFfZGF5KSAlPiUgCiAgZ2F0aGVyKG1pcm5hLCBleHAsIC0gZ3JvdXApIAoKZ2dwbG90KGRhdGFmLCBhZXMoeD1leHApKSArIAogIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsID0gZ3JvdXApLCBjb2xvdXIgPSAnd2hpdGUnKSsKICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgY291bnRzIHdpdGggcmVzcGVjdCB0byB0aGUgZ3JvdXBzIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIAoKI3dpbGwgbm90IGJlIG5lZWRlZDogZG9uJ3Qga2VlcCBiaWcgZGF0YQpyZW1vdmUoZGF0YWYpCnJlbW92ZShkZikKYGBgCgpXZSBzZWUgdGhhdCBvdXIgbW9kZWwgc2VlbXMgdG8gYmUgYmltb2RhbCBidXQgdGhhdCB0aGUgYmltb2RhbGl0eSBkb2Vzbid0IHNlZW0gdG8gYmUgZHVlIHRvIHRoZSBjbHVzdGVycy4KCiMjIyMgRmlsdGVyZWQgTWVhbi1WYXJpYW5jZSBUcmVuZAoKQWZ0ZXIgcmVhZGluZyBpbiBkZXRhaWxzIGxpbW1hJ3MgbWFudWFsIFsoMTYpXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL2xpbW1hL2luc3QvZG9jL3VzZXJzZ3VpZGUucGRmKSwgd2UgcmVhbGl6ZWQgdGhhdCB3ZSBzaG91bGQgZml0ZXIgb3V0IHJvd3MgdGhhdCBoYXZlIGF0IGxlYXN0IDEwIGNvdW50cyBmb3IgYSB3b3J0aHdoaWxlIG51bWJlciBvZiBzYW1wbGVzIChwYXJhZ3JhcGggMTUuNSkuIAoKV2Ugd2lsbCBiZSB1c2luZyAkMzBcJSQgYXMgYSAid29ydGh3aGlsZSBudW1iZXIgb2Ygc2FtcGxlcyIuCgpgYGB7ciBmaW5hbF9tZWFuX3ZhcmlhbmNlfQojIGZpbHRlcmluZyBvdXQgcm93cyB0aGF0IGhhdmUgbm9uIHplcm8gY291bnRzIGluIGxlc3MgdGhhbiAzMCUgb2YgdGhlIHNhbXBsZXMKZGF0YU5aRmlsdGVyZCA8LWRhdGFOWltyb3dTdW1zKGRhdGFOWj49MTApPj1yb3VuZCgwLjMqbmNvbChkYXRhTlopKSxdCgpkZ2UgPC0gREdFTGlzdChjb3VudHM9ZGF0YU5aRmlsdGVyZCwgZ3JvdXAgPSBkYXRhX2RheSkKCiNkZ2UgPC0gZGdlW2lzRXhwcixdCiNkZ2UgPC0gY2FsY05vcm1GYWN0b3JzKGRnZSkKZGF0YV92b29tZWQgPC0gdm9vbShkZ2UsZGVzaWduLHBsb3Q9VFJVRSkKCiN3aWxsIG5vdCBiZSBuZWVkZWQ6IGRvbid0IGtlZXAgYmlnIGRhdGEKcmVtb3ZlKGRnZSkKcmVtb3ZlKGRhdGFOWkZpbHRlcmQpCmBgYAoKSXQgaXMgbm93IGFscmVhZHkgYSBiaXQgYmV0dGVyLiBXZSBhbHNvIHNlZSB0aGF0IHRoZSBtZWFuIHZhcmlhbmNlIHRyZW5kIHNlYW1zIHRvIGJlIGRlY3JlYXNpbmcgdmVyeSBxdWlja2x5IGFuZCBzdGVhZGlseSwgd2UgY2FuIHRodXMgY29uY2x1ZGUgdGhhdCB0aGUgZXhwZXJpbWVudCBzZWVtIHRvIGJlIG9mIGxvdyBiaW9sb2dpY2FsIHZhcmlhdGlvbiBbKDE3KV0oaHR0cHM6Ly9mMTAwMHJlc2VhcmNoLmNvbS9hcnRpY2xlcy81LTE0MDgpIChzZWUgIlJlbW92aW5nIGhldGVyb3NjZWRhc2NpdHkgZnJvbSBjb3VudCBkYXRhIikuIAoKSGVyZSBpcyBhbiBleGFtcGxlIG9mIHRoZSBkYXRhIHdlIGhhdmU6CgpgYGB7ciB9CmRhdGFfdm9vbWVkWzE6MiwxOjEwXQpgYGAKCiMjIyMgTW9yZSBOYXR1cmFsIEZpbHRlcmVkIE1lYW4tVmFyaWFuY2UgVHJlbmQKClRoaW5raW5nIGFib3V0IGl0LCB3ZSBzaG91bGRuJ3QgZmlsdGVyIG9uIGhhdmluZyBhcyBnb29kIHBlcmNlbnRhZ2Ugb2YgdGhlIHNhbXBsZXMgdGhhdCBhcmUgbm9uIHplcm8gYnV0IHJhdGhlciBoYXZpbmcgYSBtYWpvcml0eSBvZiBub24gemVybyBpbiBhdCBsZWFzdCBvbmUgdGltZSBwb2ludCB3ZSBoYXZlLiBJbmRlZWQgdGhhdCdzIHdoYXQgcmVhbGx5IGludGVyZXN0cyB1cyBhdCB0aGUgZW5kLCBiZWNhdXNlIHdlIGFyZSBnb2luZyB0byBjb21wYXJlIHRoZSBtaVJOQXNlcSBiZXR3ZWVuIGRheXMhIEZvciBleGFtcGxlIGltYWdpbmUgd2UgaGFkIGFuIG1pUk5BIHdoaWNoIHdhcyBoaWdobHkgZXhwcmVzc2VkIGluIGV2ZXJ5IHNhbXBsZSBvZiBEYXkgMCwgYnV0IGVxdWFsIHRvIHplcm8gaW4gYWxsIG90aGVyIHNhbXBsZXMsIHRoZW4gaXQgd291bGQgZ2V0IGZpbHRlcmVkIG91dCBiZWNhdXNlIGl0IHdvdWxkIGJlIG5vbiB6ZXJvIGluIG9ubHkgJDI1XCUkIG9mIHRoZSB0b3RhbCBzYW1wbGVzLiBUaGlzIGlzIG9mIGNvdXJzZSBub3QgZGVzaXJlZC4gCgpXZSB3aWxsIHRodXMgb25seSBrZWVwIHRoZSBtaVJOQSBpbiB3aGljaCBhdCBsZWFzdCAkNTBcJSQgb2YgdGhlIHNhbXBsZXMgaW4gb25lIGdyb3VwIGhhdmUgY291bnQgZ3JlYXRlciB0aGFuIDEwLgoKYGBge3IgZmluYWxfbWVhbl92YXJpYW5jZV9iaXN9CmluZGV4RGF5MCA8LSB3aGljaChkYXRhX2RheSA9PSAiRGF5IDAiKQppbmRleERheTEgPC0gd2hpY2goZGF0YV9kYXkgPT0gIkRheSAxIikKaW5kZXhEYXkzIDwtIHdoaWNoKGRhdGFfZGF5ID09ICJEYXkgMyIpCmluZGV4RGF5NyA8LSB3aGljaChkYXRhX2RheSA9PSAiRGF5IDciKQpkYXRhTlpGaWx0ZXJkQmlzIDwtIGRhdGFOWltyb3dTdW1zKGRhdGFOWlssaW5kZXhEYXkwXT49MTApPj1yb3VuZCgwLjUwKmxlbmd0aChpbmRleERheTApKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgcm93U3VtcyhkYXRhTlpbLGluZGV4RGF5MV0+PTEwKT49cm91bmQoMC41MCpsZW5ndGgoaW5kZXhEYXkxKSkgfAogICAgICAgICAgICAgICAgICAgICAgICAgIHJvd1N1bXMoZGF0YU5aWyxpbmRleERheTNdPj0xMCk+PXJvdW5kKDAuNTAqbGVuZ3RoKGluZGV4RGF5MykpIHwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3dTdW1zKGRhdGFOWlssaW5kZXhEYXk3XT49MTApPj1yb3VuZCgwLjUwKmxlbmd0aChpbmRleERheTcpKSAsXQoKZGdlIDwtIERHRUxpc3QoY291bnRzPWRhdGFOWkZpbHRlcmRCaXMsIGdyb3VwID0gZGF0YV9kYXkpCgojZGdlIDwtIGNhbGNOb3JtRmFjdG9ycyhkZ2UpCmRhdGFfdm9vbWVkIDwtIHZvb20oZGdlLGRlc2lnbixwbG90PVRSVUUpCmBgYAoKV2Ugc2VlIHRoYXQgaXQgaXMgdmVyeSBjbG9zZSB0byBiZWZvcmUgYnV0IGF0IGxlYXN0IG5vdyB0aGVyZSdzIG5vIGlzc3VlIHdpdGggZ2VuZXMgYmVpbmcgZmlsdGVyZWQgb3V0IGV2ZW4gaWYgdGhleSBoYXZlIGEgaGlnaCBleHByZXNzaW9uIGluIG1vc3Qgc2FtcGxlcyBvZiBhIGdyb3VwLiBUaGlzIGFsc28gcmVkdWNlcyB0aGUgc3ViamVjdGl2aXR5IG9mIHdoYXQgaXMgImEgd29ydGhpbGUgbnVtYmVyIG9mIHNhbXBsZXMgbmVlZCB0byBoYXZlIG1vcmUgdGhhbiAxMCBjb3VudHMgZm9yIHRoYXQgbWlSTkEiLgoKTGV0cyBsb29rIG5vdyBhdCB0aGUgaGlzdG9ncmFtIHRvIHNlZSBob3cgaXQgY2hhbmdlZCBpdDoKCmBgYHtyIEZpbmFsX2hpc3RvZ3JhbW1fdm9vbX0KZGYgPC0gZGF0YV92b29tZWQkRQpkYXRhZjwtIG11dGF0ZShkYXRhLmZyYW1lKHQoZGYpKSwgZ3JvdXAgPSBkYXRhX2RheSkgJT4lIAogIGdhdGhlcihtaXJuYSwgZXhwLCAtIGdyb3VwKSAKCmdncGxvdChkYXRhZiwgYWVzKHg9ZXhwKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbCA9IGdyb3VwKSwgY29sb3VyID0gJ3doaXRlJykgKwogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBmaWx0ZXJlZCBjb3VudHMgd2l0aCByZXNwZWN0IHRvIHRoZSBncm91cHMiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSxwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgCgoKI3dpbGwgbm90IGJlIG5lZWRlZDogZG9uJ3Qga2VlcCBiaWcgZGF0YQpyZW1vdmUoZGF0YWYpCnJlbW92ZShkZikKYGBgCgpUaGUgYmltb2RhbGl0eSBvZiB0aGUgcGxvdCBkb2Vzbid0IHNlZW0gdG8gYmUgZHVlIHRvIGNsdXN0ZXJzIHdpdGggdGhpcyBoaXN0b2dyYW0uIEJ1dCB0aGF0IGRvZXNuJ3QgcnVsZSBvdXQgY29tcGxldGVseSB0aGUgZWZmZWN0IG9mIHRoZSBncm91cHMuIEluZGVlZCBzb21lIG1pUk5BIG1heSBvbmx5IGJlIGhpZ2hseSBleHByZXNzZWQgaW4gb25lIGNsdXN0ZXIgYW5kIG90aGVzciBpbiB0aGUgc2Vjb25kIGNsdXN0ZXIuIFdlIHRodXMgbmVlZCB0byBsb29rIGF0IHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIG1pUk5BIGFuZCBjbHVzdGVyLiAKCiMjIyMjIERpc3RyaWJ1dGlvbiBvZiBleHByZXNzaW9uIHdpdGggcmVzcGVjdCB0byBtaVJOQSBhbmQgY2x1c3RlcnMKCmBgYHtyICBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD0xMH0KZGF0YV9ncm91cCA8LSBhcy5jaGFyYWN0ZXIoZGF0YV9kYXkpCmRhdGFfZ3JvdXBbZGF0YV9ncm91cCA9PSAiRGF5IDAiIHwgZGF0YV9ncm91cCA9PSAiRGF5IDEiXSA8LSAiRGF5MDEiCmRhdGFfZ3JvdXBbZGF0YV9ncm91cCA9PSAiRGF5IDMiIHwgZGF0YV9ncm91cCA9PSAiRGF5IDciXSA8LSAiRGF5MzciCgojbWFrZXMgYSBkYXRhIGZyYW1lIHdpdGggYWxsIGluZm9ybWF0aW9uCmRmIDwtIGRhdGFfdm9vbWVkJEUKZGF0YWY0IDwtIGRhdGEuZnJhbWUoZGYpCmRhdGFmNCRtaXJuYSA8LSBhcy5mYWN0b3Iocm93bmFtZXMoZGF0YWY0KSkKZGF0YWY0IDwtIGdhdGhlcihkYXRhZjQsc2FtcGxlcywgZXhwLC1taXJuYSk7CmRhdGFmNCRncm91cCA8LSAgYXMuZmFjdG9yKGRhdGFfZ3JvdXApOwpkYXRhZjQkZGF5IDwtICBkYXRhX2RheTsKZGF0YWY0IDwtIG11dGF0ZShkYXRhZjQsIGludGVyR3JvdXAgPSBwYXN0ZShhcy5jaGFyYWN0ZXIoZGF0YWY0JG1pcm5hKSxkYXRhX2dyb3VwKSApCmRhdGFmNCA8LSBtdXRhdGUoZGF0YWY0LCBpbnRlckdyb3VwMiA9IHBhc3RlKGFzLmNoYXJhY3RlcihkYXRhZjQkbWlybmEpLGFzLmNoYXJhY3RlcihkYXRhX2RheSkpICkKZGF0YWY0JGV4cDIgPC0gMl5kYXRhZjQkZXhwLTAuNQpyYW5kUk5BIDwtIHVuaXF1ZShkYXRhZjQkbWlybmEpW3NhbXBsZSgxOmxlbmd0aCh1bmlxdWUoZGF0YWY0JG1pcm5hKSksIDEyLCByZXBsYWNlPUZBTFNFKV0KCmdncGxvdChkYXRhZjRbZGF0YWY0JG1pcm5hICVpbiUgcmFuZFJOQSxdLCBhZXMoeD1leHAsIGZpbGwgPSBncm91cCkpICsgCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSkgKwogIGdndGl0bGUoIlJhbmRvbSBkZW5zaXR5IHBsb3QgZm9yIGludGVyYWN0aW9uIG1pUk5BLWdyb3VwIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHhsaW0oMCwzMCkgKwogIGZhY2V0X3dyYXAofm1pcm5hLG5yb3cgPSA0LG5jb2wgPSAzKQoKI3dpbGwgbm90IGJlIG5lZWRlZDogZG9uJ3Qga2VlcCBiaWcgZGF0YQpyZW1vdmUoZGYpCmBgYAoKV2l0aCB0aGlzIHdlIGNsZWFybHkgc2VlIHRoYXQgdGhlcmUgaXMgbm8gbGluayBiZXR3ZWVuIHRoZSBjbHVzdGVycyBhbmQgbWlSTkEgYW5kIHRoZSBiaW1vZGFsaXR5IG9mIHRoZSBkaXN0cmlidXRpb24uIFRoZSBiaW1vZGFsaXR5IGlzIG5lYXJseSBhbHdheXMgcHJlc2VudCBmb3IgYWxsIG1pUk5BLiAKClRoaXMgbWF5IHNlZW0gc3VwcmlzaW5nIGF0IGZpcnN0LCBidXQgYWZ0ZXIgZnVydGhlciBjb25zaWRlcmF0aW9uIHRoZSBiaW1vZGFsaXR5IGlzIGFtcGxpZmllZCBieSB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSBkYXRhLiBJbmRlZWQsIHRoZSBsb2cgdHJhbnNmb3JtYXRpb24gb2YgdGhlIGV4cHJlc3Npb24gZGF0YSBtYWtlcyBhIHdpZGVyIHJhbmdlIG9mIGhpZ2ggZXhwcmVzc2lvbiBzZWVtIHNtYWxsZXIuIEhlcmUgaXMgYSBwbG90IHRoYXQgc2hvd3MgdGhlIHNhbWUgZGF0YSBidXQgbm90IGxvZ3RyYW5zZm9ybWVkLgoKYGBge3IgIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QoZGF0YWY0W2RhdGFmNCRtaXJuYSAlaW4lIHJhbmRSTkEsXSwgYWVzKHg9ZXhwMiwgZmlsbCA9IGdyb3VwKSkgKyAKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41KSArCiAgZ2d0aXRsZSgiUmFuZG9tIGRlbnNpdHkgcGxvdCBmb3IgaW50ZXJhY3Rpb24gbWlSTkEtZ3JvdXA6IG5vIGxvZyIpICsKICB0aGVtZV9idygpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyB4bGltKC01MDAsMTAwMDApICsKICBmYWNldF93cmFwKH5taXJuYSxucm93ID0gNSxuY29sID0gMykKCmBgYAoKV2UgY2xlYXJseSBzZWUgdGhhdCB0aGUgbm9uIHRyYW5mb3JtZWQgZGF0YSBpcyBub3QgYXMgYmltb2RhbCBhcyBpdCBzZWVtZWQgdG8gYmUuIFRoZSBtaVJOQSBhcmUgc2ltcGx5IG9mZiBvciBvbiwgYnV0IHRoZXJlJ3MgYSB3aWRlIHJhbmdlIG9mICJvbiBzdGF0ZXMiLiBOb3RlOiBpdCBpcyBzdXByaXNpbmcgdG8gc2VlIHRoYXQgZm9yIHRoZSByYW5kb20gZ2VuZXMgd2UgdG9vaywgdGhlcmUgZG9lc24ndCBzZWVtIHRvIGJlIGFueSBsaW5rIGJldHdlZW4gZ3JvdXBzIGFuZCAib24tb2ZmIiBzdGF0ZXMuCgojIyMgRml0IGxpbmVhciBtb2RlbCAKCkZvciBjb21wYXJpbmcgdGhlIGV4cHJlc3Npb24gb2YgbXVsdGlwbGUgbWlSTkEgd2Ugd2lsbCB1c2UgYSBsaW5lYXIgbW9kZWwuIEluIG9yZGVyIHRvIGdhaW4gc3RhdGlzdGljYWwgcG93ZXIgd2Ugd2lsbCBiZSB1c2luZyBlbWlyaWNhbCBiYXllcyBtZXRob2QgdG8gImJvcnJvdyBpbmZvcm1hdGlvbiBhY3Jvc3MgYWxsIG1pUk5BIi4gVGhlIGlkZWEgb2YgYmF5ZXMgbWV0aG9kIGlzIHRvIHNocmluayB0aGUgZXN0aW1hdGVkIHNhbXBsZSB2YXJpYW5jZSB0b3dhcmRzYSBwb29sZWQgZXN0aW1hdGUsIHdoaWNoIHJlc3VsdHMgaW4gbW9yZSBzdGFibGUgaW5mZXJlbmNlIGZvciBzYW1wbGVzIHdpdGggc21hbGxlciBzaXplIFsoMTgpXShodHRwOi8vd3d3LnN0YXRzY2kub3JnL3NteXRoL3B1YnMvZWJheWVzLnBkZikuIFdlIHdpbGwgYmUgdXNpbmcgTGltbWEncyBwYWNrYWdlIHRvIGltcGxlbWVudCBlbXBpcmljYWwgYmF5ZXMgbGluZWFyIG1vZGVsLiAKCioqTm90ZSoqOiBUaGUgc3RyZW5ndGggb2YgZW1waXJpY2FsIGJheWVzLCBpcyB0aGF0IGNvbXBhcmVkIHRvIGNsYXNzaWNhbCBiYXllcyBtZXRob2QsIGl0IGRvZXNuJ3QgcmVxdWlyZSB0aGUgaW5wdXQgb2YgcHJpb3IuIEluZGVlZCwgaXQgd2lsbCBmaW5kIHRoZSBwcmlvciBmcm9tIHRoZSBkYXRhLgoKIyMjIyBMaW5lYXIgbW9kZWw6IGRheSBhcyBhIG51bWVyaWMKCkxldCdzIG5vdyBsb29rIGF0IGxpbmVhciBtb2RlbCB3aGVyZSB0aGUgZGF5IGlzIGNvbnNpZGVyZWQgYXMgbnVtZXJpYyBhbmQgbm90IGEgZmFjdG9yLgoKYGBge3J9CiMgQ29udmVydCBwb3B1bGF0aW9uIHRvIGEgbnVtZXJpYyB2YXJpYWJsZS4KUG9wLm51bTwtYXMubnVtZXJpYyhzdWJzdHIobl9tZXRhX2RhdGEkUG9wdWxhdGlvbiw2LDYpKQpuX21ldGFfZGF0YTwtY2JpbmQobl9tZXRhX2RhdGEsUG9wLm51bSkKCmRlcy5wb3AubnVtPC1tb2RlbC5tYXRyaXgoflBvcC5udW0sbl9tZXRhX2RhdGEpCnYucG9wLm51bTwtdm9vbShkZ2UsZGVzLnBvcC5udW0sIHBsb3Q9RkFMU0UpCgojIE5leHQsIGZpdCBsaW5lYXIgbW9kZWwgd2l0aCBkYXkgYXMgYSBmYWN0b3IuCmZpdC5wb3AubnVtIDwtIGxtRml0KHYucG9wLm51bSxkZXMucG9wLm51bSkKZWZpdC5wb3AubnVtIDwtIGVCYXllcyhmaXQucG9wLm51bSkKCiMgc3RvcmUgcmVzdWx0IAp0b3AucG9wLm51bTwtdG9wVGFibGUoZWZpdC5wb3AubnVtLG51bWJlcj1JbmYpCgojIExvb2sgYXQgdGFibGUgd2l0aCB0b3AgZ2VuZXMuCnRvcC5wb3AubnVtWzE6MTAsXQoKZmRyLnBvcC5udW08LW5yb3coc3Vic2V0KHRvcC5wb3AubnVtLGFkai5QLlZhbDwwLjAxKSkKCnJlbW92ZSh2LnBvcC5udW0pCgpgYGAKClRoZXJlIGFyZSBub3cgYHIgZmRyLnBvcC5udW1gIGdlbmVzIGF0IGEgZmFsc2UgZGlzY292ZXJ5IHJhdGUgb2YgMC4wMS4KCkZpbmFsbHksIGFzIGJlZm9yZSwgbGV0cyBtYWtlIGEgaGlzdG9ncmFtIG9mIHAtdmFsdWVzLgpgYGB7cn0KaGlzdCh0b3AucG9wLm51bSRhZGouUC5WYWwsYnJlYWtzPTI5LG1haW49IlEtdmFsdWUgSGlzdG9ncmFtIC0gRGF5IGFzIG51bWVyaWMiLHhsYWI9IlEtVmFsdWUiKQpoaXN0KHRvcC5wb3AubnVtJFAuVmFsdWUsYnJlYWtzPTI5LG1haW49IlAtdmFsdWUgSGlzdG9ncmFtIC0gRGF5IGFzIG51bWVyaWMiLHhsYWI9IlAtVmFsdWUiKQoKcmVtb3ZlKHRvcC5wb3AubnVtKQpgYGAKClRoaXMgaGlzdG9ncmFtbSBsb29rcyBwZXJmZWN0IGZvciB1cyEgVGhpcyBjbGVhcmx5IHNob3dzIHRoYXQgbW9zdCBvZiBvdXIgbWlSTkEgbGl2ZSBpbiB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcy4gSXQgYWxzbyBzaG93cyBhIGFwcHJveGltYXRpdmVseSB1bmlmb3JtbHkgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIHVuZGVyIHRoZSBudWxsIGh5cG90aGVzaXMgKG9uIHRoZSByaWdodCksIGFzIHdlIHdvdWxkIHRoaW5rLiBOb3RlOiB0aGlzIGhpc3RvZ3JhbW0gc2VlbXMgc3RhbmdlbHkgInRvbyBnb29kIiBpbiB0aGUgc2Vuc2UgdGhhdCB0aGUgbnVtYmVyIG9mIG1pUk5BIGRpZmZlcm50aWFsbHkgZXhwcmVzc2VkIHNlZW1zIGh1Z2UuIE5vdGUgdGhhdCB0aGlzIGlzIHBvc3NpYmxlIGJlY2F1c2Ugd2UgYXJlIGxvb2tpbmcgYXQgbWlSTkEgc2VxIHdpdGggYSAib24iIG9yICJvZmYiIGRpZmZlcm50aWF0aW9uLgoKIyMjIyBMaW5lYXIgbW9kZWw6IGRheSBhcyBhIGZhY3RvcgpgYGB7ciB0ZXN0X2xpbmVhcl9tb2RlbCB9CmRlcy5wb3AuY2hhcjwtbW9kZWwubWF0cml4KH5Qb3B1bGF0aW9uLG5fbWV0YV9kYXRhKQp2LnBvcC5jaGFyPC12b29tKGRnZSxkZXMucG9wLmNoYXIsIHBsb3Q9RkFMU0UpCmZpdC5wb3AuY2hhciA8LSBsbUZpdCh2LnBvcC5jaGFyLGRlcy5wb3AuY2hhcikKZWZpdC5wb3AuY2hhciA8LSBlQmF5ZXMoZml0LnBvcC5jaGFyKQoKdG9wLnBvcC5jaGFyPC10b3BUYWJsZShlZml0LnBvcC5jaGFyLG51bWJlcj1JbmYpCnBsb3RTQShlZml0LnBvcC5jaGFyLG1haW49IkZpbmFsIE1lYW4tVmFyaWFuY2UgcGxvdCIpCgojd2lsbCBub3QgYmUgbmVlZGVkOiBkb24ndCBrZWVwIGJpZyBkYXRhCnJlbW92ZShmaXQpCnJlbW92ZSh2LnBvcC5jaGFyKQpgYGAKT24gdGhpcyBwbG90IHdlIHNlZSBob3cgdGhlIGRhdGEgaXMgYWZ0ZXIgaGF2aW5nIHRyYW5zZm9ybWVkIGl0IHdpdGggbGltbWEuIFdlIG5vdGUgdGhhdCB0aGUgdmFyaWFuY2UgaXMgbm93IG5lYXJseSBpbmRlcGVuZGVudCB0byB0aGUgZXhwcmVzc2lvbiAoYWx0aG91Z2ggdGhpcyBkb2Vzbid0IHNlZW0gdHJ1ZSBmb3IgdGhlIGxhc3QgZmV3IHBvaW50cykuIFRoaXMgc2hvd3MgdXMgdGhhdCB0aGUgZGF0YSBpcyBob21vc2NlZGF0aWMgZW5vdWdoIHRvIGZpdCBhIGxpbmVhciBtb2RlbC4gCgpMZXQncyBub3cgaGF2ZSBhIGxvb2sgYXQgdGFibGUgd2l0aCB0b3AgZ2VuZXMuCmBgYHtyfQp0b3AucG9wLmNoYXJbMToxMCxdCmBgYAoKIyMjIyMgUC12YWx1ZXMgYW5kIEZEUgoKR2V0IHNvbWUgcHJvYmUgbmFtZXMgYW5kIHJlYWxseSBsb3cgcC12YWx1ZXMgKGkuZS4gUC1WYWx1ZSA8IDAuMDEpLgoKYGBge3J9CmZkci5wb3AuY2hhcjwtbnJvdyhzdWJzZXQodG9wLnBvcC5jaGFyLGFkai5QLlZhbDwwLjAxKSkKYGBgCgpUaGVyZSBhcmUgYHIgZmRyLnBvcC5jaGFyYCBnZW5lcyBhdCBhIGZhbHNlIGRpc2NvdmVyeSByYXRlIG9mIDAuMDEuCgpGaW5hbGx5LCBtYWtlIGEgaGlzdG9ncmFtIG9mIHAtdmFsdWVzLgpgYGB7cn0KaGlzdCh0b3AucG9wLmNoYXIkYWRqLlAuVmFsLGJyZWFrcz0yOSxtYWluPSJRLXZhbHVlIEhpc3RvZ3JhbSAtIFBvcHVsYXRpb24gYXMgYSBmYWN0b3IiLHhsYWI9IlEtVmFsdWUiKQpoaXN0KHRvcC5wb3AuY2hhciRQLlZhbHVlLGJyZWFrcz0yOSxtYWluPSJQLXZhbHVlIEhpc3RvZ3JhbSAtIFBvcHVsYXRpb24gYXMgYSBmYWN0b3IiLHhsYWI9IlAtVmFsdWUiKQoKcmVtb3ZlKHRvcC5wb3AuY2hhcikKYGBgCgpBcyBiZWZvcmUgdGhlIHAtdmFsdWUgaGlzdG9ncmFtcyBhcmUgcmVhbGx5IG5pY2UgYW5kIHNob3cgdGhhdCBvdXIgZXhwZXJpbWVudCBoYXMgYSBsb3Qgb2YgbWlSTkEgdGhhdCBsaXZlIGluIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzLiBUaGlzIHBsb3QgaXMgYSBiaXQgc3RyYW5nZSBpbiB0aGUgc2Vuc2UgdGhhdCB0aGVyZSBpcyBldmVuIGxlc3MgbWlSTkEgaW4gdGhlIG51bGwgaHlwb3RoZXNpcy4KCiMjIyMgTGluZWFyIG1vZGVsOiBjbHVzdGVyIGFzIGEgZmFjdG9yCkNvbXBhcmUgZGF5cyAwIGFuZCAzIHRvIGRheXMgMyBhbmQgNwpgYGB7ciByZXN1bHRzPSdoaWRlJ30KZ3N1YigiSEw2MEQwIiwiRGF5IDAgb3IgMSIsbl9tZXRhX2RhdGEkUG9wdWxhdGlvbikKbGV2ZWxzKG5fbWV0YV9kYXRhJFBvcHVsYXRpb24pCmNsdXN0ZXI8LWNoYXJhY3RlcigpCmZvcih4IGluIG5fbWV0YV9kYXRhJFBvcHVsYXRpb24pewogIGlmKHggPT0gIkhMNjBEMCIpewogICAgY2x1c3RlcjwtYXBwZW5kKGNsdXN0ZXIsIkRheSAwIG9yIDEiKQogIH0KICBpZih4ID09ICJITDYwRDEiKXsKICAgIGNsdXN0ZXI8LWFwcGVuZChjbHVzdGVyLCJEYXkgMCBvciAxIikKICB9CiAgaWYoeCA9PSAiSEw2MEQzIil7CiAgICBjbHVzdGVyPC1hcHBlbmQoY2x1c3RlciwiRGF5IDMgb3IgNyIpCiAgfQogIGlmKHggPT0gIkhMNjBENyIpewogICAgY2x1c3RlcjwtYXBwZW5kKGNsdXN0ZXIsIkRheSAzIG9yIDciKQogIH0KfQpuX21ldGFfZGF0YTwtY2JpbmQobl9tZXRhX2RhdGEsY2x1c3RlcikgI21ha2UgZmFjdG9yIGZvciBjbHVzdGVyCmBgYAoKRGVzaWduIG1hdHJpeCwgdm9vbSBhbmQgZml0IGxpbmVhciBtb2RlbApgYGB7cn0KZGVzLmNsdXN0ZXI8LW1vZGVsLm1hdHJpeCh+Y2x1c3RlcixuX21ldGFfZGF0YSkKdi5jbHVzdGVyPC12b29tKGRnZSxkZXMuY2x1c3RlciwgcGxvdD1GQUxTRSkKZml0LmNsdXN0ZXIgPC0gbG1GaXQodi5jbHVzdGVyLGRlcy5jbHVzdGVyKQplZml0LmNsdXN0ZXIgPC0gZUJheWVzKGZpdC5jbHVzdGVyKQp0b3AuY2x1c3RlcjwtdG9wVGFibGUoZWZpdC5jbHVzdGVyLG51bWJlcj1JbmYpCgpmZHIuY2x1c3Rlcjwtc3Vic2V0KHRvcC5jbHVzdGVyLGFkai5QLlZhbDwwLjAxKQp0b3AuY2x1c3RlclsxOjEwLF0KCnJlbW92ZSh2LmNsdXN0ZXIpCnJlbW92ZShlZml0LmNsdXN0ZXIpCmBgYAoKVGhlcmUgYXJlIGByIG5yb3coZmRyLmNsdXN0ZXIpYCBnZW5lcyBhdCBhIGZhbHNlIGRpc2NvdmVyeSByYXRlIG9mIDAuMDEuCgpNYWtlIGFub3RoZXIgc2V0IG9mIGhpc3RvZ3JhbXMuCmBgYHtyfQpoaXN0KHRvcC5jbHVzdGVyJGFkai5QLlZhbCxicmVha3M9MTksbWFpbj0iUS12YWx1ZSBIaXN0b2dyYW0gLSBDbHVzdGVyIGFzIGEgZmFjdG9yIix4bGFiPSJRLVZhbHVlIikKaGlzdCh0b3AuY2x1c3RlciRQLlZhbHVlLGJyZWFrcz0xOSxtYWluPSJQLXZhbHVlIEhpc3RvZ3JhbSAtIENsdXN0ZXIgYXMgYSBmYWN0b3IiLHhsYWI9IlAtVmFsdWUiKQpgYGAKCgpMb29rIGF0IHRvcCBmZXcgZ2VuZXMKYGBge3J9CiMgRGVmaW5lIHBsb3R0aW5nIGZ1bmN0aW9uCmNsdXN0ZXIxPC1nc3ViKCJITDYwRDAiLCJEYXkgMCBvciAxIixuX21ldGFfZGF0YSRQb3B1bGF0aW9uKQpjbHVzdGVyMTwtZ3N1YigiSEw2MEQxIiwiRGF5IDAgb3IgMSIsY2x1c3RlcjEpCmNsdXN0ZXIxPC1nc3ViKCJITDYwRDMiLCJEYXkgMyBvciA3IixjbHVzdGVyMSkKY2x1c3RlcjE8LWdzdWIoIkhMNjBENyIsIkRheSAzIG9yIDciLGNsdXN0ZXIxKQptZXRhMjwtY2JpbmQobl9tZXRhX2RhdGEsY2x1c3RlcjEpCgojZnVuY3Rpb24gd2hpY2ggY29tYmluZXMgZXhwcmVzc2lvbiBkYXRhIHdpdGggbWV0YURhdGEKcHJlcGFyZURhdGEyIDwtIGZ1bmN0aW9uKGdlbmVzKSB7ICAgCiAgbWluaURhdCA8LSAgc3Vic2V0KGRhdGFOWiwgcm93bmFtZXMoZGF0YU5aKSAlaW4lIGdlbmVzKQogIG1pbmlEYXQgPC0gZGF0YS5mcmFtZShnRXhwID0gYXMudmVjdG9yKHQoYXMubWF0cml4KG1pbmlEYXQpKSksCiAgICAgICAgICAgICAgICAgICAgICAgIGdlbmUgPSBmYWN0b3IocmVwKHJvd25hbWVzKG1pbmlEYXQpLCBlYWNoID0gbmNvbChtaW5pRGF0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gZ2VuZXMpKQogIG1pbmlEYXQgPC0gc3VwcHJlc3NXYXJuaW5ncyhkYXRhLmZyYW1lKG1ldGEyLCBtaW5pRGF0KSkKICAKICByZXR1cm4obWluaURhdCkKfQoKdG9wR2VuZXMgPC0gcm93bmFtZXModG9wLmNsdXN0ZXJbMToxMCxdKQpwcmVwRGF0VG9wPC1wcmVwYXJlRGF0YTIodG9wR2VuZXMpICNmb3JtYXQgZGF0YQoKZ2dwbG90KGRhdGE9c3Vic2V0KHByZXBEYXRUb3AscHJlcERhdFRvcCRnZW5lPT10b3BHZW5lc1sxXSksYWVzKGNsdXN0ZXIsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZSh0b3BHZW5lc1sxXSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIAogIApyZW1vdmUodG9wLmNsdXN0ZXIpCmBgYAoKIyMjIyBMaW5lYXIgbW9kZWw6IGRheSBhcyBhIGZhY3RvciB3aXRob3V0IGludGVyY2VwdAoKRm9yIHRoZSBmb2xsb3dpbmcgYW5hbHlzaXMgd2Ugd2lsbCBiZSB1c2luZyB0aGUgZGF5IGFzIGZhY3RvcnMuCgojIyMjIyBFeGFtaW5pbmcgdGhlIG51bWJlciBvZiBERSBnZW5lcwoKTGV0J3Mgbm93IGxvb2sgYXQgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gbGV2ZWxzIG9mIGdlbmVzLiAKCk5vdGUgdGhhdCB3ZSB3aWxsIGJlIHVzaW5nIGFuIGFkanVzdGVkIHAtdmFsdWUgY3V0b2ZmIGlzIDUlLgoKYGBge3IgdGVzdF9saW5lYXJfbW9kZWxfc3VtbWFyeSB9CmZpdCA8LSBsbUZpdChkYXRhX3Zvb21lZCwgZGVzaWduKQpmaXQgPC0gY29udHJhc3RzLmZpdChmaXQsIGNvbnRyYXN0cz1jb250cmFzdE1hdHJpeCkKZWZpdCA8LSBlQmF5ZXMoZml0KQoKZHQgPC0gZGVjaWRlVGVzdHMoZWZpdCkKc3VtbWFyeShkdCkKYGBgCgpBbHRob3VnaCB3ZSBzZWUgdGhhdCBtb3N0IG1pUk5BIGFyZSBub3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gZGF5IDEgYW5kIGRheSAwLCB3ZSBjYW4gYmUgc3VwcmlzZWQgYnkgdGhlIGFtb3VudCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBkYXkgMyBhbmQgZGF5IDcuIEZyb20gdGhpcyB0YWJsZSB3ZSBjb3VsZCBoeXBvdGhlc2l6ZSB0aGF0IHRoZSBtYWpvcml0eSBvZiBtaVJOQSBkb2Vzbid0IGhhdmUgdGhlICJ0aW1lIiB0byBnZXQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gZGF5IDAgYW5kIGRheSAxLiBPZnRlbiB0aGVzZSBnZW5lcyBnZXQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZWVuIGRheSAxIGFuZCAzLCB3aGljaCBjb3VsZCBoYXZlIG11bHRpcGxlIGVmZmVjdCBvbiB0aGUgd2hvbGUgY2VsbC4gRmluYWxseSBiZXR3ZWVuIGRheSAzIGFuZCBkYXkgNywgdGhlc2Ugc2FtZSBnZW5lcyBjb3VsZCBnZXQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIHRvICJyZXR1cm4iIHRvIGEgc3RhdGUgY2xvc2UgdG8gdGhlIGluaXRpYWwgb25lLiBGcm9tIHRoaXMgdGFibGUgd2UgY291bGQgZXZlbiBnbyBvbmUgc3RlcCBmdXJ0aGVyIGFuZCBzYXkgdGhhdCBvZnRlbiB0aGVuIG1pUk5BIGdldCB1cHJlZ3VsYXRlZCBmcm9tIGRheSAxIHRvIGRheSAzIGFuZCB0aGVuIGRvd25yZWd1bGF0ZWQgYmFjayB0byBhICJub3JtYWwgc3RhdGUiIGZyb20gZGF5IDMgdG8gZGF5IDcuCk9mIGNvdXJzZSB3ZSB3b3VsZCBuZWVkIG1vcmUgcHJvb2YgdG8gc3VwcG9ydCB0aGlzIGh5cHRoZXNpcywgYnV0IHRoYXQgd291bGQgYmUgYSBwbGF1c2libGUgaW50ZXJwcmV0YXRpb24gb2YgdGhlIHRhYmxlLgoKIyMjIyMjIE1hbnkgbWlSTkEgYXJlIGRvd25yZWd1bGF0ZWQgYmV0d2VlbiBkYXkgMSBhbmQgMyBhbmQgdGhlbiB1cHJlZ3VsYXRlZCBhZ2FpbiBhdCBkYXkgNwoKSWYgb3VyIHByZXZpb3VzIGh5cG90aGVzaXMgd2VyZSBjb3JyZWN0LCB0aGF0IHdvdWxkIG1lYW4gdGhhdCBtb3N0IG9mIHRoZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZSBiZXR3ZWVuIGRheSAxIGFuZCAzIGFyZSBhbHNvIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiBkYXkgNy4gTGV0J3MgbG9vayBhdCB0aGlzIHdpdGggYSBuaWNlIHZlbm4gZGlhZ3JhbW06CgpgYGB7ciB2ZW5uX2h5cG90aGVzaXN9Cm5JbnZlcnNEMUQzRDcgPC0gbnJvdyhkdFsoZHRbLDRdICogZHRbLDZdPT0gLTEpLCBdKQpuU2FtZUQxRDNENyA8LSBucm93KGR0WyhkdFssNF0gKiBkdFssNl09PSAxKSwgXSkKblRvdGFsRDFEM0Q3IDwtIG5yb3coZHRbZHRbLDRdICE9IDAgfCBkdFssNl0gIT0gMCxjKDQsNildKQp2ZW5uRGlhZ3JhbShkdFtkdFssNF0gIT0gMCB8IGR0Wyw2XSAhPSAwLGMoNCw2KV0sIGNpcmNsZS5jb2w9YygidHVycXVvaXNlIiwgInNhbG1vbiIsICJncmVlbiIsInllbGxvdyIpLCBtYWluID0gIlZlbm4gZGlhZ3JhbW0gZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIEQxLUQzIGFuZCBEMy03IikKYGBgCgpBcyBoeXBvdGhlc2lzZWQsIHdlIHNlZSB0aGF0IG1vc3Qgb2YgdGhlIG1pUk5BIGFyZSBkaWZmZXJudGFpbGx5IGV4cHJlc3NlZCBpbiBib3RoIGNhc2VzLiBOb3cgdG8gZ28gb25lIHN0ZXAgZnVydGhlciB3ZSBjYW4gY29tcHV0ZSB0aGUgbnVtYmVyIG9mIG1pUk5BIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiB0aGUgIm9wcG9zaXRlIGRpcmVjdGlvbiIgYmV0d2VlbiBkYXkxLWRheTMgYW5kIGRheTMtZGF5NyAuIEluZGVlZCwgaWYgZGF5IDMgd2VyZSBhICJ0cmFuc2l0aW9uIHN0YXRlIiBhcyBzdXBwb3NlZCwgdGhlbiBtb3N0IG9mIHRoZSBtaVJOQSdzIHdvdWxkIGdldCBkaWZmZXJudGlhbGx5IGV4cHJlc3NlZCBpbiBvbmUgZGlyZWN0aW9uIGZyb20gZGF5MSB0byBkYXkzIGFuZCB0aGVuIGNvbWUgYmFjayB0byAibm9ybWFsIiBmcm9tIGRheTMgdG8gZGF5Ny4gCgpPdXQgb2YgdGhlIGByIG5Ub3RhbEQxRDNEN2AgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGluIGVpdGhlciBEMS1EMywgRDMtRDcgb3IgYm90aCwgdGhlcmUgYXJlIGByIG5JbnZlcnNEMUQzRDdgIG1pUk5BIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiB0aGUgb3RoZXIgZGlyZWN0aW9uICh3aGlsZSBvbmx5IGByIG5TYW1lRDFEM0Q3YCBpbiB0aGUgc2FtZSBkaXJlY3Rpb24pISBUaGF0IGNvdWxkIGJlIGFuIGluZGljYXRpb24gdGhhdCB0aGUgaHlwb3RoZXNpcyBpcyBjb3JyZWN0LCBidXQgd291bGQgbmVlZCBmdXJ0aGVyIGFuYWx5c2lzLgoKQXMgdGhlIGh5cG90aGVzaXMgc2VlbXMgcGxhdXNpYmxlLCBpdCB3b3VsZCBiZSBpbnRlcmVzdGluZyB0byBsb29rIGF0IHRoZXNlIG1pUk5BIApgYGB7ciBjb21tb25fREVfZ2VuZXNfaW52ZXJzZV9kYXkxMy0zN30KIyBGaW5kaW5nIGdlbmVzIHRoYXQgYXJlIGRpZmZlcm50YWlsbHkgZXhwcmVzc2VkIGluIHRoZSBvcHBvc2l0ZSBkaXJlY3Rpb24gYmV0d2VlbiBkMS0zIGFuZCBkMy03CmNvbW1vbkRFZ2VuZXNJbnZlcnNlRGF5MTMzNyA8LSB3aGljaChkdFssNF0qZHRbLDZdID09IC0xKQpuYW1lcyhjb21tb25ERWdlbmVzSW52ZXJzZURheTEzMzcpCmBgYAoKVG8gZnVydGhlciBzdWJzZXQgdGhlIG1pUk5BLCBpdCBpcyB2ZXJ5IHByb2JhYmxlIChhbHRob3VnaCB3b3VsZCBuZWVkIGFkZGl0aW9uYWwgdGVzdHMpIHRoYXQgdGhlIG1pUm5BIG9mIGludGVyZXN0IGFyZSBub3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gZGF5IDAgYW5kIGRheSAxLiBJbmRlZWQgd2Ugd291bGQgbGlrZSB0byBmaW5kIHRoZSBtaVJOQSB0aGF0IGNhdXNlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGdyb3VwcyBidXQgY29tZSBiYWNrIHRvIG5vcm1hbCBhdCBkYXkgNy4KCmBgYHtyIGNvbW1vbl9ERV9nZW5lc19pbnZlcnNlX2RheTEzLTM3X25vdDAxfQojIEZpbmRpbmcgZ2VuZXMgdGhhdCBhcmUgZGlmZmVybnRhaWxseSBleHByZXNzZWQgaW4gdGhlIG9wcG9zaXRlIGRpcmVjdGlvbiBiZXR3ZWVuIGQxLTMgYW5kIGQzLTcKY29tbW9uREVnZW5lc0ludmVyc2VEYXkxMzM3bm90MDEgPC0gd2hpY2goZHRbLDRdKmR0Wyw2XSA9PSAtMSAmIGR0WywxXSA9PSAwKQpuYW1lcyhjb21tb25ERWdlbmVzSW52ZXJzZURheTEzMzdub3QwMSkKYGBgCgpMZXQncyBwbG90IGEgcmFuZG9tIHN1YnNldCBvZiB0aGVzZSBtaVJOQQoKYGBge3J9CnJhbmRSTkEgPC0gdW5pcXVlKG5hbWVzKGNvbW1vbkRFZ2VuZXNJbnZlcnNlRGF5MTMzN25vdDAxKSlbc2FtcGxlKDE6bGVuZ3RoKHVuaXF1ZShuYW1lcyhjb21tb25ERWdlbmVzSW52ZXJzZURheTEzMzdub3QwMSkpKSwgNCwgcmVwbGFjZT1GQUxTRSldCnByZXBEYXRUb3A8LXByZXBhcmVEYXRhMihyYW5kUk5BKSAjZm9ybWF0IGRhdGEKCmYxIDwtIGdncGxvdChkYXRhPXN1YnNldChwcmVwRGF0VG9wLHByZXBEYXRUb3AkZ2VuZT09cmFuZFJOQVsxXSksYWVzKGNsdXN0ZXIsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZShyYW5kUk5BWzFdKSAKCmYyIDwtIGdncGxvdChkYXRhPXN1YnNldChwcmVwRGF0VG9wLHByZXBEYXRUb3AkZ2VuZT09cmFuZFJOQVsyXSksYWVzKGNsdXN0ZXIsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZShyYW5kUk5BWzJdKSMxMDZiLTVwCgpmMyA8LSBnZ3Bsb3QoZGF0YT1zdWJzZXQocHJlcERhdFRvcCxwcmVwRGF0VG9wJGdlbmU9PXJhbmRSTkFbM10pLGFlcyhjbHVzdGVyLGdFeHAsY29sb3VyPVBvcHVsYXRpb24pKSsKICBnZW9tX2ppdHRlcigpKwogIGdndGl0bGUocmFuZFJOQVszXSkKCmY0IDwtIGdncGxvdChkYXRhPXN1YnNldChwcmVwRGF0VG9wLHByZXBEYXRUb3AkZ2VuZT09cmFuZFJOQVs0XSksYWVzKGNsdXN0ZXIsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZShyYW5kUk5BWzRdKSAKCmdyaWQuYXJyYW5nZShmMSxmMixmMyxmNCxucm93PTIpICNwdXQgcGxvdHMgdG9nZXRoZXIKCmBgYAojIyMjIyMgRmluZGluZyBnZW5lcyB0aGF0IGFyZSBvZnRlbiBkaWZmZXJlbnRpYWxseSBiZXR3ZWVuIGRheSAwLzEgYW5kIGRheSAzLzcKCkZyb20gYWxsIG91ciBwbG90cyB0aGF0IGNsZWFybHkgc2hvd2VkIGNsdXN0ZXJzIGJldHdlZW4gZGF5IDAsIGRheSAxIGFuZCBkYXkgMywgZGF5IDcsIHdlIGNvdWxkIHdhbnQgdG8gYXNrIG91cnNlbHZlcyB0aGUgc2ltcGxlIHF1ZXN0aW9uIG9mICJ3aGF0IG1pUk5BIGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiB0aGUgY2x1c3RlcnMiID8gIAoKYGBge3IgY29tbW9uX0RFX2dlbmVzX2RheTAxLTM3IH0KIyBGaW5kaW5nIGdlbmVzIHRoYXQgYXJlIG5vdCB6ZXJvIGluIGF0IGxlYXN0IG9uZSBjb21wYXJhaXNvbiBiZXR3ZWVuIHRoZSBncm91cHMKY29tbW9uREVnZW5lc0RheTAxMzcgPC0gd2hpY2goZHRbLDJdIT0wICYgZHRbLDNdIT0wICYgZHRbLDRdIT0wICYgZHRbLDVdIT0wKQpuYW1lcyhjb21tb25ERWdlbmVzRGF5MDEzNykKYGBgCgpMZXQncyB2aXN1YWxpc2UgaXQgd2l0aCBhIG5pY2UgdmVubiBkaWFncmFtbQoKYGBge3IgZGF5MDEzN19ERV92ZW5uIH0KdmVubkRpYWdyYW0oZHRbLDI6NV0sIGNpcmNsZS5jb2w9YygidHVycXVvaXNlIiwgInNhbG1vbiIsICJncmVlbiIsInllbGxvdyIpLCBtYWluID0gIlZlbm4gZGlhZ3JhbW0gc2hvd2luZyBkaWZmZXJlbmNlcyBiZXR3ZWVuIGNsdXN0ZXJzIikKYGBgCgojIyMjIyMgUExvdCBtaVJOQQoKYGBge3IgfQoKI0xpc3Qgb2YgZ2VuZXMgZGlmZmVyZW50IGJldHdlZW4gdGhlIHR3byBjbHVzdGVycwp0b3BHZW5lcyA8LSBuYW1lcyhjb21tb25ERWdlbmVzRGF5MDEzNykKI3RvcEdlbmVzPC1jKCJoc2EtbGV0LTdkLTNwIiwiaHNhLWxldC03aS01cCIsImhzYS1taVItMTI1YS01cCIsImhzYS1taVItMTMyLTNwIiwiaHNhLW1pUi0xNDMtM3AiLCJoc2EtbWlSLTE0NS01cCIsImhzYS1taVItMTgxYi0zcCIsImhzYS1taVItMTg1LTVwIiwiaHNhLW1pUi0xOTItNXAiLCJoc2EtbWlSLTE5Yi0zcCIsImhzYS1taVItMjIzLTNwIiwiaHNhLW1pUi0yM2EtM3AiLCJoc2EtbWlSLTI2YS01cCIsImhzYS1taVItMzBlLTVwIiwiaHNhLW1pUi00MjUtNXAiLCJoc2EtbWlSLTQyNS0zcCIsImhzYS1taVItOTJhLTNwIikKcHJlcERhdFRvcDwtcHJlcGFyZURhdGEyKHRvcEdlbmVzKSAjZm9ybWF0IGRhdGEKCiNtYWtlIGRhdGFzZXQgZm9yIHByb2JlIDEyNWEgLSByZW1vdmUgY2VsbCB3aXRoIHRvcCBleHByZXNzaW9uCnAxMjU8LXN1YnNldChwcmVwRGF0VG9wLHByZXBEYXRUb3AkZ2VuZT09dG9wR2VuZXNbNl0pCnAxMjU8LXAxMjVbLXdoaWNoKHAxMjUkZ0V4cD09bWF4KHAxMjUkZ0V4cCkpLF0KCmYxIDwtIGdncGxvdChkYXRhPXAxMjUsYWVzKGNsdXN0ZXIxLGdFeHAsY29sb3VyPVBvcHVsYXRpb24pKSsKICBnZW9tX2ppdHRlcigpKwogIGdndGl0bGUodG9wR2VuZXNbNl0pICsgeWxpbSgtMTAwLDIwMDApCgpmMiA8LSBnZ3Bsb3QoZGF0YT1zdWJzZXQocHJlcERhdFRvcCxwcmVwRGF0VG9wJGdlbmU9PXRvcEdlbmVzWzFdKSxhZXMoY2x1c3RlcjEsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZSh0b3BHZW5lc1sxNl0pICsgeWxpbSgtMTAwLDIwMDApCgpmMyA8LSBnZ3Bsb3QoZGF0YT1zdWJzZXQocHJlcERhdFRvcCxwcmVwRGF0VG9wJGdlbmU9PXRvcEdlbmVzWzJdKSxhZXMoY2x1c3RlcjEsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZSh0b3BHZW5lc1s1XSkrIHlsaW0oLTEwMCwyMDAwKSMxMDZiLTVwCgpmNCA8LSBnZ3Bsb3QoZGF0YT1zdWJzZXQocHJlcERhdFRvcCxwcmVwRGF0VG9wJGdlbmU9PXRvcEdlbmVzWzNdKSxhZXMoY2x1c3RlcjEsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZSh0b3BHZW5lc1sxXSkgKyB5bGltKC0xMDAsMjAwMCkKCmdyaWQuYXJyYW5nZShmMSxmMixmMyxmNCxucm93PTIpICNwdXQgcGxvdHMgdG9nZXRoZXIKCmBgYAoKIyMjIyMjIEZpbmRpbmcgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gY2x1c3RlcnMgYnV0IG5vdCB3aGl0aGluIGNsdXN0ZXJzCgpGaW5hbGx5IHdoYXQgd2UgYXJlIHJlYWxseSBpbnRlcmVzdGVkIGluIGFyZSBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBjbHVzdGVycyBidXQgbm90IHdoaXRoaW4gY2x1c3RlcnM6IGhlcmUgYXJlIHRoZXNlIGdlbmVzOgoKYGBge3IgY29tbW9uX0RFX2dlbmVzX2JldHdlZW5DTHVzdGVyc19ub3RXaGl0aGluIH0KIyBGaW5kaW5nIGdlbmVzIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBiZXR3ZWVuIGNsdXN0ZXJzIGJ1dCBub3Qgd2hpdGhpbiBjbHVzdGVycwpjb21tb25ERWdlbmVzQmV0d2Vlbk5vdFdoaXRoaW4gPC0gd2hpY2goZHRbLDJdIT0wICYgZHRbLDNdIT0wICYgZHRbLDRdIT0wICYgZHRbLDVdIT0wICYgZHRbLDFdPT0wICYgZHRbLDZdPT0wKQpuYW1lcyhjb21tb25ERWdlbmVzQmV0d2Vlbk5vdFdoaXRoaW4pCmBgYAoKVG8gaGF2ZSBhIGJldHRlciBpZGVhIG9mIHRoZSAicmVzdWx0cyIsIGxldCdzIHJhbmRvbWx5IGNob29zZSBhIG1pUk5BIHdoaWNoIGlzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBiZXR3ZWVuIGNsdXN0ZXJzIGJ1dCBub3Qgd2hpdGhpbi4KCkZpcnN0IGxldCdzIHBsb3QgdGhlIGxvZyB0cmFuc2Zvcm1lZCBkYXRhLgoKYGBge3Igc2VlX3JhbmRvbV9taVJOQV9iZXR3ZWVuQ0x1c3RlcnNfbm90V2hpdGhpbn0KZ2dwbG90KGRhdGFmNFtkYXRhZjQkbWlybmEgJWluJSBuYW1lcyhjb21tb25ERWdlbmVzQmV0d2Vlbk5vdFdoaXRoaW4pLF0sIGFlcyh4PWV4cCwgZmlsbCA9IGdyb3VwKSkgKyAKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41KSArCiAgZ2d0aXRsZSgiUmFuZG9tIHBvaW50IHBsb3QgZm9yIGludGVyYWN0aW9uIG1pUk5BLWdyb3VwIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHhsaW0oMCwzMCkgKwogIGZhY2V0X3dyYXAofm1pcm5hLG5jb2wgPSAzKQpgYGAKClRvIHNlZSAidGhlIHJlYWwgZGF0YSIgbGV0J3MgYWxzbyBwbG90IHRoZSBzYW1lIGdlbmVzIG5vdCBsb2cgdHJhbnNmb3JtZWQuCgpgYGB7ciBzZWVfcmFuZG9tX21pUk5BX2JldHdlZW5DbHVzdGVyc19ub3RXaGl0aGlufQpnZ3Bsb3QoZGF0YWY0W2RhdGFmNCRtaXJuYSAlaW4lIG5hbWVzKGNvbW1vbkRFZ2VuZXNCZXR3ZWVuTm90V2hpdGhpbiksXSwgYWVzKHg9ZXhwMiwgZmlsbCA9IGdyb3VwKSkgKyAKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41KSArCiAgZ2d0aXRsZSgiUmFuZG9tIGRlbnNpdHkgcGxvdCBmb3IgaW50ZXJhY3Rpb24gbWlSTkEtZ3JvdXA6IG5vIGxvZyIpICsKICB0aGVtZV9idygpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyB4bGltKC01MDAsMTAwMDApICsKICBmYWNldF93cmFwKH5taXJuYSxuY29sID0gMykKYGBgCgoKTGV0J3MgcGxvdCBhIHJhbmRvbSBzdWJzZXQgb2YgdGhlc2UgbWlSTkEKCmBgYHtyfQpyYW5kUk5BIDwtIHVuaXF1ZShuYW1lcyhjb21tb25ERWdlbmVzQmV0d2Vlbk5vdFdoaXRoaW4pKVtzYW1wbGUoMTpsZW5ndGgodW5pcXVlKG5hbWVzKGNvbW1vbkRFZ2VuZXNCZXR3ZWVuTm90V2hpdGhpbikpKSwgNCwgcmVwbGFjZT1GQUxTRSldCnByZXBEYXRUb3A8LXByZXBhcmVEYXRhMihyYW5kUk5BKSAjZm9ybWF0IGRhdGEKCmYxIDwtIGdncGxvdChkYXRhPXN1YnNldChwcmVwRGF0VG9wLHByZXBEYXRUb3AkZ2VuZT09cmFuZFJOQVsxXSksYWVzKGNsdXN0ZXIsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZShyYW5kUk5BWzFdKSAKCmYyIDwtIGdncGxvdChkYXRhPXN1YnNldChwcmVwRGF0VG9wLHByZXBEYXRUb3AkZ2VuZT09cmFuZFJOQVsyXSksYWVzKGNsdXN0ZXIsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZShyYW5kUk5BWzJdKSMxMDZiLTVwCgpmMyA8LSBnZ3Bsb3QoZGF0YT1zdWJzZXQocHJlcERhdFRvcCxwcmVwRGF0VG9wJGdlbmU9PXJhbmRSTkFbM10pLGFlcyhjbHVzdGVyLGdFeHAsY29sb3VyPVBvcHVsYXRpb24pKSsKICBnZW9tX2ppdHRlcigpKwogIGdndGl0bGUocmFuZFJOQVszXSkKCmY0IDwtIGdncGxvdChkYXRhPXN1YnNldChwcmVwRGF0VG9wLHByZXBEYXRUb3AkZ2VuZT09cmFuZFJOQVs0XSksYWVzKGNsdXN0ZXIsZ0V4cCxjb2xvdXI9UG9wdWxhdGlvbikpKwogIGdlb21faml0dGVyKCkrCiAgZ2d0aXRsZShyYW5kUk5BWzRdKSAKCmdyaWQuYXJyYW5nZShmMSxmMixmMyxmNCxucm93PTIpICNwdXQgcGxvdHMgdG9nZXRoZXIKCmBgYAoKIyBSZWZlcmVuY2VzCgoxLglEb2huZXIsIEguLCBXZWlzZG9yZiwgRC5KLiwgQmxvb21maWVsZCwgQy5ELiBBY3V0ZSBteWVsb2lkIGxldWtlbWlhLiBfTiBFbmcgSiBNZWRfIF9fMzczX18sIDExMzYtMTE1MiAoMjAxNSkuCjIuCUhvc2VpbmksIFMuUy4sIENoZXVuZywgTi5LLiBBY3V0ZSBteWVsb2lkIGxldWtlbWlhIHRhcmdldHMgZm9yIGJpc3BlY2lmaWMgYW50aWJvZGllcy4gX0Jsb29kIENhbmNlciBKb3VybmFsXyBfXzdfXywgZTUyMiAoMjAxNykuIAozLglTdW5hbWksIFkuLCBBcmFraSwgTS4sIEthbiwgUy4sIEl0bywgQS4sIEhpcm9uYWthLCBZLiwgSW1haSwgTS4sIE1vcmlzaGl0YSwgUy4sIE9oc2FrYSwgQS4sIEtvbWF0c3UsIE4uIEhpc3RvbmUgYWNldHlsdHJhbnNmZXJhc2UgUENBRiBpcyByZXF1aXJlZCBmb3IgYWxsLXRyYW5zIHJldGlub2ljIGFjaWQtaW5kdWNlZCBncmFudWxvY3l0aWMgZGlmZmVyZW50aWF0aW9uIGluIGxldWtlbWlhIGNlbGxzLiBfSiBCaW9sIENoZW1fLCBkb2k6IDEwLjEwNzQvamJjLk0xMTYuNzQ1Mzk4ICgyMDE3KS4KNC4JQmFydGVsLCBELlAuIE1pY3JvUk5BczogR2Vub21pY3MsIGJpb2dlbmVzaXMsIG1lY2hhbmlzbSwgYW5kIGZ1bmN0aW9uLiBfQ2VsbF8gX18xMTZfXywgMjgxLTI5NyAoMjAwNCkuCjUuCUppYW4sIFAuLCBMaSwgWlcuLCBGYW5nLCBUWS4sIEppYW4sIFcuLCBaaHVhbiwgWi4sIE1laSwgTFguLCBZYW4sIFdTLiwgSmlhbiwgTi4gUmV0aW5vaWMgYWNpZCBpbmR1Y2VzIEhMLTYwIGRpZmZlcmVudGlhdGlvbiB2aWEgIHRoZSB1cHJlZ3VsYXRpb24gb2YgbWlSLTY2My4gX0ogSGVtYXRvbCBPbmNvbF8gX180X18gZG9pOiAxMC4xMTg2LzE3NTYtODcyMi00LTIwICAoMjAxMSkuIAo2LglHYXJ6b24sIFIuLCBQaWNoaW9ycmksIEYuLCBfZXQgYWxfLiBNaWNyb1JOQSBnZW5lIGV4cHJlc3Npb24gZHVyaW5nIHJldGlub2ljIGFjaWQtaW5kdWNlZCBkaWZmZXJlbnRpYXRpb24gb2YgaHVtYW4gYWN1dGUgcHJvbXllbG9jeXRpYyBsZXVrZW1pYS4gX09uY29nZW5lXyBfXzI2X18sIDQxNDgtNTcgKDIwMDcpLgo3LglGYXJpZGFuaSwgT1IuLCBBYmR1bGxheWV2LCBJLiwgSGFnZW1hbm4tSmVuc2VuLCBNLiwgU2NoZWxsLCBKUC4sIExhbm5lciwgRi4sIFNhbmRiZXJnLCBSLiBTaW5nbGUtY2VsbCBzZXF1ZW5jaW5nIG9mIHRoZSBzbWFsbC1STkEgdHJhbnNjcmlwdG9tZS4gX05hdCBCaW90ZWNobm9sXyBfXzM0X18sIDEyNjQtMTI2NiAoMjAxNikuIAo4LglWYW4gZGVyIE1hYXRlbiwgTEpQLiwgSGludG9uLCBHRS4gVmlzdWFsaXppbmcgaGlnaC1kaW1lbnNpb25hbCBkYXRhIHVzaW5nIHQtU05FLiBfSm91cm5hbCBvZiBNYWNoaW5lIExlYXJuaW5nIFJlc2VhcmNoXyBfXzlfXywgMjU3OS0yNjA1ICgyMDA4KS4gCjkuCVdhcmRlLUZhcmxleSwgRC4sIERvbmFsZHNvbiwgU0wuLCBldCBhbC4gVGhlIEdlbmVNQU5JQSBwcmVkaWN0aW9uIHNlcnZlcjogYmlvbG9naWNhbCBuZXR3b3JrIGludGVncmF0aW9uIGZvciBnZW5lIHByaW9yaXRpemF0aW9uIGFuZCBwcmVkaWN0aW5nIGdlbmUgZnVuY3Rpb24uIF9OdWNsZWljIEFjaWRzIFJlc18gX18zOF9fLCAyMTQtMjAgKDIwMTApLiAKMTAuCVN1YnJhbWFuaWFuLCBBLiBldCBhbC4gR2VuZSBzZXQgZW5yaWNobWVudCBhbmFseXNpczogYSBrbm93bGVkZ2UtYmFzZWQgYXBwcm9hY2ggZm9yIGludGVycHJldGluZyBnZW5vbWUtd2lkZSBleHByZXNzaW9uIHByb2ZpbGVzLiBfUHJvYyBOYXRsIEFjYWQgU2NpIFVTQV8gX18xMDJfXywgMTU1NDUtNTAgKDIwMDUpLiAKMTEuIFJpc3NvLCBEYXZpZGUsIGV0IGFsLiAiTm9ybWFsaXphdGlvbiBvZiBSTkEtc2VxIGRhdGEgdXNpbmcgZmFjdG9yIGFuYWx5c2lzIG9mIGNvbnRyb2wgZ2VuZXMgb3Igc2FtcGxlcy4iIE5hdHVyZSBiaW90ZWNobm9sb2d5IDMyLjkgKDIwMTQpOiA4OTYtOTAyLgoxMi4gU2VxYy9NYXFjLUlpaSBDb25zb3J0aXVtLiAiQSBjb21wcmVoZW5zaXZlIGFzc2Vzc21lbnQgb2YgUk5BLXNlcSBhY2N1cmFjeSwgcmVwcm9kdWNpYmlsaXR5IGFuZCBpbmZvcm1hdGlvbiBjb250ZW50IGJ5IHRoZSBTZXF1ZW5jaW5nIFF1YWxpdHkgQ29udHJvbCBDb25zb3J0aXVtLiIgTmF0dXJlIGJpb3RlY2hub2xvZ3kgMzIuOSAoMjAxNCk6IDkwMy05MTQuCjEzLiBCcmFzZSwgSmFuIEMuLCBldCBhbC4gIkNpcmN1bGF0aW5nIG1pUk5BcyBhcmUgY29ycmVsYXRlZCB3aXRoIHR1bW9yIHByb2dyZXNzaW9uIGluIHByb3N0YXRlIGNhbmNlci4iIEludGVybmF0aW9uYWwgam91cm5hbCBvZiBjYW5jZXIgMTI4LjMgKDIwMTEpOiA2MDgtNjE2LgoxNC4gTGF3LCBDaGFyaXR5IFcuLCBldCBhbC4gIlZvb206IHByZWNpc2lvbiB3ZWlnaHRzIHVubG9jayBsaW5lYXIgbW9kZWwgYW5hbHlzaXMgdG9vbHMgZm9yIFJOQS1zZXEgcmVhZCBjb3VudHMuIiBHZW5vbWUgYmlvbG9neSAxNS4yICgyMDE0KTogUjI5LgoxNS4gTHVuLCBBYXJvbiBUTCwgRGF2aXMgSi4gTWNDYXJ0aHksIGFuZCBKb2huIEMuIE1hcmlvbmkuICJBIHN0ZXAtYnktc3RlcCB3b3JrZmxvdyBmb3IgbG93LWxldmVsIGFuYWx5c2lzIG9mIHNpbmdsZS1jZWxsIFJOQS1zZXEgZGF0YSB3aXRoIEJpb2NvbmR1Y3Rvci4iIEYxMDAwUmVzZWFyY2ggNSAoMjAxNikuCjE2LiBTbXl0aCwgR29yZG9uIEsuLCBldCBhbC4gImxpbW1hOiBMaW5lYXIgTW9kZWxzIGZvciBNaWNyb2FycmF5IGFuZCBSTkEtU2VxIERhdGEgVXNlcuKAmXMgR3VpZGUuIiAoMjAwMikuCjE3LiBMYXcsIENoYXJpdHkgVy4sIGV0IGFsLiAiUk5BLXNlcSBhbmFseXNpcyBpcyBlYXN5IGFzIDEtMi0zIHdpdGggbGltbWEsIEdsaW1tYSBhbmQgZWRnZVIuIiBGMTAwMFJlc2VhcmNoIDUgKDIwMTYpLgoxOC4gU215dGgsIEdvcmRvbiBLLiAiTGluZWFyIG1vZGVscyBhbmQgZW1waXJpY2FsIGJheWVzIG1ldGhvZHMgZm9yIGFzc2Vzc2luZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBpbiBtaWNyb2FycmF5IGV4cGVyaW1lbnRzLiIgU3RhdCBBcHBsIEdlbmV0IE1vbCBCaW9sIDMuMSAoMjAwNCk6IDMuCgoKIyBBZGRpdGlvbmFsIHdvcmsgdGhhdCBoYXZlIGJlZW4gZGlzcmVnYXJkZWQKCiMjR2VuZXJhbCBub3JtYWxpemF0aW9uIAoKVG8gYmUgc3VyZSB0aGF0IHRoZSBub3JtYWxpemF0aW9uIGlzIGdvb2QgSSB3aWxsIGNvbnRpbnVlIHRoZSBhbmFseXNpcyB3aXRoIGEgbW9yZSBnZW5lcmFsIG5vcm1hbGl6YXRpb24uIFRoZSBmb2xsb3dpbmcgaXMgYmFzZWQgb24gW3RoaXMgcGFnZV0oaHR0cDovL3d3dy1odWJlci5lbWJsLmRlL3VzZXJzL2tsYXVzL1RlYWNoaW5nL0RFU2VxMlByZWRvYzIwMTQuaHRtbCNwY2EtYW5kLXNhbXBsZS1oZWF0bWFwcykuIEJ5IGdlbmVyYWwgSSBtZWFuIG5vdCBzcGVjaWZpYyB0byBwaWtlcy4KCmBgYHtyIG5vcm1hbGl6ZV9ERVNlcX0KZGF0YUluaXRpYWwgPC0gcmVhZC50YWJsZSgiLi4vRGF0YS9yYXdfZGF0YS9leHBuX21hdHJpeC50eHQiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKQoKZGF0YV9kYXlERVNlcSA8LSBhcy5jaGFyYWN0ZXIobWV0YV9kYXRhJFBvcHVsYXRpb24pCmRhdGFfZGF5REVTZXEgPC0gcmVjb2RlKGRhdGFfZGF5REVTZXEsICJITDYwRDAiID0gIkRheSAwIiwgIkhMNjBEMSIgPSAiRGF5IDEiLCAiSEw2MEQzIiA9ICJEYXkgMyIsICJITDYwRDciID0gIkRheSA3IikKZGF0YV9kYXlERVNlcSA8LWZhY3RvcihkYXRhX2RheURFU2VxKQoKZGVTZXFEYXQgPC0gbmV3Q291bnREYXRhU2V0KGRhdGFJbml0aWFsLCBkYXRhX2RheURFU2VxKQoKIyBOb3RlOiBhY3R1YWxseSBpdCdzIG5vdCBhIHJlYWwgbm9ybWFsaXNhdGlvbi4gUmF0aGVyIGNvbXB1dGluZyBzaXplIGZhY3RvcnMgZGVwZW5kaW5nIG9uIHJhdGlvIG9mIG1lZGlhbnMgCiMgaWYgYWxsIHNpemUgZmFjdG9ycyBhcmUgcm91Z2hseSBlcXVhbCB0byBvbmUsIHRoZSBsaWJyYXJpZXMgaGF2ZSBiZWVuIHNlcXVlbmNlZCBlcXVhbGx5IGRlZXBseS4KCmRlU2VxRGF0IDwtIGVzdGltYXRlU2l6ZUZhY3RvcnMoZGVTZXFEYXQpCmhlYWQoc2l6ZUZhY3RvcnMoZGVTZXFEYXQpKQoKaWR4Lm56IDwtIGFwcGx5KGNvdW50cyhkZVNlcURhdCksIDEsIGZ1bmN0aW9uKHgpIHsgYWxsKHggPiAwKX0pCm5OWnNhbXBsZXMgPC0gc3VtKGlkeC5ueikKCiN3aWxsIG5vdCBiZSBuZWVkZWQ6IGRvbid0IGtlZXAgYmlnIGRhdGEKcmVtb3ZlKGRhdGFJbml0aWFsKQpgYGAKCldlIHNlZSB0aGF0IHRoZSBudW1iZXIgb2Ygbm9uIHplcm8gc2FtcGxlcyBpbiBhbGwgZ2VuZXMgaXMgdmVyeSBsb3cgY29tcGFyZWQgdG8gdHJhZGl0aW9uYWwgUk5BLXNlcTogYHIgbk5ac2FtcGxlc2Agbm9tcmFsbHkgaW4gdGhvdXNhbmRzLiBUaGlzIG1heSBiZSBhIGdvb2QgcmVhc29uIHRvIHN0aWNrIHdpdGggdGhlIG5vcm1hbGl6YXRpb24gd2l0aCBza2llIFJOQS4KCmBgYHtyIHBsb3R0X2Rpc3BlcnNpb259CiNwbG90dGluZyB0aGUgZXN0aW1hdGVkIGRpc3BlcnNpb25zIGFnYWluc3QgdGhlIG1lYW4gbm9ybWFsaXplZCBjb3VudHMKZGVTZXFEYXQgPC0gZXN0aW1hdGVEaXNwZXJzaW9ucyhkZVNlcURhdCkKcGxvdERpc3BFc3RzKGRlU2VxRGF0KQptdWx0aWRlbnNpdHkoIGNvdW50cyhkZVNlcURhdCwgbm9ybWFsaXplZCA9IFQpLHhsYWI9Im1lYW4gY291bnRzIiwgeGxpbT1jKDAsIDEwMDApKQpgYGAKCmBgYHtyIHBsb3R0X2RlbnNpdHl9CiBtdWx0aWVjZGYoIGNvdW50cyhkZVNlcURhdCwgbm9ybWFsaXplZCA9IFQpLHhsYWI9Im1lYW4gY291bnRzIiwgeGxpbT1jKDAsIDEwMDApKQpgYGAKClRoZSB0d28gY2hhcnRzIGFib3ZlIGNsZWFybHkgc2hvd3MgdGhhdCB0aGUgc2Vjb25kIG5vcm1hbGlzYXRpb24gaXNuJ3QgY29udmluY2luZy4gSW5kZWVkLCB0aGUgc2Vjb25kIGNoYXJ0IGFzc2Vzc2VzIHdoZXRoZXIgdGhlIG5vcm1hbGl6YXRpb24gaGFzIHdvcmtlZCwgYW5kIHRoZSBkZW5zaXRpZXMgc2hvdWxkIG92ZXJsYXBwIHNpbmNlIG1vc3Qgb2YgdGhlIGdlbmVzIGFyZSBoZWF2aWx5IGFmZmVjdGVkIGJ5IHRoZSBleHBlcmltZW50YWwgY29uZGl0aW9ucy4gTm90ZTogVGhlIHN0cmFuZ2UgZGVuc2l0eSBjaGFydCBjb3VsZCBhbHNvIGJlIGR1ZSB0byB0aGUgZmFjdCB0aGF0IG1pUk5BIGFyZSB2ZXJ5IHJhcmVseSBleHByZXNzZWQgaW4gZXZlcnkgc2FtcGxlIChhcyB3ZSBoYXZlIHNlZW4gYmVmb3JlKS4KCiMjIE5vdCB1c2luZyBsb2cgdHJhbnNvZm9ybWVkIGRhdGEgCgojIyMgTWVhbiB2YXJpYW5jZSB0cmVuZCBubyBsb2cKCk5vdGU6IFNlZWluZyB0aGUgc3RyYW5nZSBtZWFuIHZhcmlhbmNlIHBsb3QgYWJvdmUgbWFrZXMgdXMgdGhpbmsgdGhhdCBtYXliZSBvdXIgY291bnQgZGF0YSBkb2Vzbid0IG5lZWQgdG8gYmUgbG9nIHRyYW5zZm9ybWVkLiBMZXQncyBpbnZlc3RpZ2F0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG1lYW4gYW5kIHRoZSB2YXJpYW5jZSBvZiBvdSBkYXRhLgoKYGBge3Igc3FydDRfbWVhbi12YXJpYW5jZX0KI3dvcmtpbmcgLi4uIEhPVyBUTyBVU0UgREFUQURFU0VRIE9OIE5PUk1BTElaRUQgPT4gTk9UIENPVU5UUyBBTlkgTU9SRT8/ICsgSVMgSVQgV09SVEggSVQgPyBjQU4gd2Ugcm91bmQgPwojZGF0YURFU2VxIDwtIHJsb2coYXMubWF0cml4KGRhdGEpLCBibGluZD1GQUxTRSApCgojdGFrZXMgb25seSBnZW5lcyB0aGF0IGFyZSBub24gemVybyBpbiBtYW55IHNhbXBsZXMKI3QgPC0gbl9kYXRhW3doaWNoKHJvd1N1bXMobl9kYXRhKSA+IDMwMCksXSAKCiN0cmllcyBwbG90dGluZyBkaXJlY3RseSB3aXRob3V0IHZvb20KbVNxcnQ0IDwtIHJvd01lYW5zKGRhdGFOWkZpbHRlcmRCaXNeMC40KQpSb3dTaWdtYVNxcnQxNiA8LSBmdW5jdGlvbih4KSB7CiAgKHJvd1N1bXMoKHggLSByb3dNZWFucyh4KSleMikvKGRpbSh4KVsyXSAtIDEpKV4oMS8xNikKfQp2IDwtIFJvd1NpZ21hU3FydDE2KGRhdGFOWkZpbHRlcmRCaXNeMC40KQoKZGF0YU5vTG9nIDwtIGRhdGEuZnJhbWUobWVhblNxcnQ0ID0gbVNxcnQ0LCBzaWdtYVNxcnQxNiA9IHYpCgpnZ3Bsb3QoZGF0YU5vTG9nLCBhZXMoeD1tZWFuU3FydDQsIHk9c2lnbWFTcXJ0MTYpKSArCiAgICBnZW9tX3BvaW50KHNoYXBlPTEpICsgICAgIyBVc2UgaG9sbG93IGNpcmNsZXMKICAgIGdlb21fc21vb3RoKG1ldGhvZD1sb2VzcywgICAjIEFkZCBsb2VzcyByZWdyZXNzaW9uIGxpbmUKICAgICAgICAgICAgICAgIHNlPUZBTFNFLAogICAgICAgICAgICAgICAgY29sb3VyID0gInJlZCIpICsgIyBEb24ndCBhZGQgc2hhZGVkIGNvbmZpZGVuY2UgcmVnaW9uCiAgeWxpbSgwLCAyKSArIAogIHhsaW0oMCwyMCkgKyAKICBnZ3RpdGxlKCJNZWFuIHZhcmlhbmNlIHJlbGF0aW9uc2hpcCBub3QgVm9vbSIpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSAKCmBgYAoKRnJvbSB0aGlzIHBsb3Qgd2UgY2xlYXJseSBzZWUgdGhhdCBvdXIgdmFyaWFuY2UgZ3Jvd3Mgd2l0aCAxNnRoIHBvcWVyIG9mIG91ciA0dGggbWVhbi4gVGhlIHJlYXNvbmluZyBiZWhpbmQgdGhpcyBkaWZmZXJlbnQgcmVsYXRpb25zaGlwIGlzIHN0aWxsIHVuY2xlYXIgdG8gdXMgLCBidXQgaXQgY291bGQgaW5kaWNhdGUgdGhhdCB3ZSBtYXliZSBzaG91bGRuJ3Qgd29yayB3aXRoIHRoZSBkZWZhdWx0IGxpbW1hIHBhY2thZ2UgYW5kIHRoYXQgd2Ugc2hvdWxkIG1heWJlIGRvIHRoZSBhbmFseXNpcyBtYW51YWxseS4gIAoKTm90ZTogdXNpbmcgYSBsb2cgdHJhbmZvcm1hdGlvbiBhcyBpbiBsaW1tYSBjYW4gaGVscCB0byBtYWtlIHRoZSBkYXRhIGNsb3NlciB0byBub3JtYWwsIGJ1dCBhcyB3ZSd2ZSBzZWVuIGJlZm9yZSBpdCBtYWtlcyBpdCBiaW1vZGFsIGhlcmUgd2hpY2ggaXMgbm90IHJlYWxseSBiZXR0ZXIuIFRoZSByZWFsIHF1ZXN0aW9uIHRvIGludmVzdGlnYXRlIGlzIHdoZXRoZXIgdGhlIGVycm9ycyBvZiB0aGUgbGluZWFyIG1vZGVsIGFyZSBub3JtYWwgd2l0aCB0aGUgbm9uLWxvZ2dlZCB0cmFuc2Zvcm1lZCBkYXRhLiBJZiB0aGV5IGFyZSwgdGhlbiB3ZSBzaG91bGQgbWF5YmUgbm90IGxvZyB0cmFuc2Zvcm0gaXQuCgojIyMgV2l0aG91dCBsb2cgdHJhbnNmb3JtYXRpb24KCmBgYHtyIHRlc3RfbGluZWFyX21vZGVsX25vTG9nIH0KZGF0YV92b29tZWQkRSA8LSAoMl5kYXRhX3Zvb21lZCRFIC0gMC41KV4wLjQKCmxvZXNzRml0IDwtIGxvZXNzICggc2lnbWFTcXJ0MTZ+bWVhblNxcnQ0LCBkYXRhTm9Mb2cpIApsb2Vzc1ByZWRpY3QgIDwtIHByZWRpY3QgKGxvZXNzRml0LCBkYXRhTm9Mb2ckbWVhblNxcnQ0KSAKd2VpZ2h0cyA8LSAxLyhsb2Vzc1ByZWRpY3QpXjE2CgpkYXRhX3Zvb21lZCR3ZWlnaHRzIDwtIHdlaWdodHMKCmZpdCA8LSBsbUZpdChkYXRhX3Zvb21lZCwgZGVzaWduKQpmaXQgPC0gY29udHJhc3RzLmZpdChmaXQsIGNvbnRyYXN0cz1jb250cmFzdE1hdHJpeCkKZWZpdCA8LSBlQmF5ZXMoZml0KQp0b3BUYWJsZShlZml0LCBjb2VmPWNvbG5hbWVzKGNvZWYoZWZpdCkpKQpwbG90U0EoZWZpdCkKCiN3aWxsIG5vdCBiZSBuZWVkZWQ6IGRvbid0IGtlZXAgYmlnIGRhdGEKcmVtb3ZlKGZpdCkKcmVtb3ZlKGRhdGFfdm9vbWVkKQpgYGAKCgojIyMgRXhhbWluaW5nIHRoZSBudW1iZXIgb2YgREUgZ2VuZXMKCkxldCdzIG5vdyBsb29rIGF0IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGxldmVscyBvZiBnZW5lcy4gTm90ZSB0aGF0IHRoZSBhZGp1c3RlZCBwLXZhbHVlIGN1dG9mZiBpcyA1JS4KCmBgYHtyIHRlc3RfbGluZWFyX21vZGVsX3N1bW1hcnlfbm9Mb2cgfQpkdCA8LSBkZWNpZGVUZXN0cyhlZml0KQpzdW1tYXJ5KGR0KQpgYGAKCgpBcyB3ZSBoYXZlIGFscmVhZHkgcHJldmlvdXNseSBzZWVuIGluIHRoZSBtdWx0aXBsZSBwbG90cyBvZiB0aGUgcHJldmlvdXMgc2VjdGlvbjogbW9zdCBkaWZmZXJlbnRpYWwgZXhwcmVzc2VkIGdlbmVzIGFyZSBiZXR3ZWVuIGRheSAwLzEgYW5kIDMvNy4gV2UgYWxzbyBub3RlIHRoYXQgYSBsb3Qgb2YgZ2VuZXMgaW4gRGF5IDMgYW5kIDcgYXJlIGRvd25yZWd1bGF0ZWQgKGNvbXBhcmVkIHRvIGRheSAwIGFuZCAxKS4KCiMjIyMgRmluZGluZyBnZW5lcyB0aGF0IGFyZSBvZnRlbiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgY29tcGFyZWQgdG8gZGF5IDAKCkxldCdzIGxvb2sgYXQgdGhlIGdlbmVzIHRoYXQgYXJlIGFsd2F5cyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgY29tcGFyZWQgdG8gZGF5IDAgCgpgYGB7ciBjb21tb25fREVfZ2VuZXNfZGF5MF9ub0xvZyB9CiMgRmluZGluZyBnZW5lcyB0aGF0IGFyZSBub3QgemVybyBpbiBldmVyeSBjb21wYXJhaXNvbiB3aXRoIGRheSAwCmNvbW1vbkRFZ2VuZXNEYXkwIDwtIHdoaWNoKGR0WywxXSE9MCAmIGR0WywyXSE9MCAmIGR0WywzXSE9MCkKbmFtZXMoY29tbW9uREVnZW5lc0RheTApCmBgYAoKTGV0J3MgdmlzdWFsaXNlIGl0IHdpdGggYSBuaWNlIHZlbm4gZGlhZ3JhbW0KCmBgYHtyIGRheTBfREVfdmVubl9ub0xvZyB9CnZlbm5EaWFncmFtKGR0WywxOjNdLCBjaXJjbGUuY29sPWMoInR1cnF1b2lzZSIsICJzYWxtb24iLCAiZ3JlZW4iKSwgbWFpbiA9ICJWZW5uIGRpYWdyYW1tIHNob3dpbmcgZGlmZmVyZW5jZXMgd2l0aCBkYXkgMCIpCmBgYAoKCiMjIyMgRmluZGluZyBnZW5lcyB0aGF0IGFyZSBvZnRlbiBkaWZmZXJlbnRpYWxseSBiZXR3ZWVuIGRheSAwLzEgYW5kIGRheSAzLzcKCkxldCdzIGxvb2sgYXQgdGhlIGdlbmVzIHRoYXQgYXJlIGFsd2F5cyBkaWZmZXJlbnRpYWxseSBiZXR3ZWVuIGRheSAwLzEgYW5kIDMvNyBhcyB0aGV5IHNlZW0gdG8gYmUgdGhlIG9uZXMgb2YgaW50ZXJlc3QuCgpgYGB7ciBjb21tb25fREVfZ2VuZXNfZGF5MDEtMzdfbm9Mb2cgfQojIEZpbmRpbmcgZ2VuZXMgdGhhdCBhcmUgbm90IHplcm8gaW4gZXZlcnkgY29tcGFyYWlzb24gd2l0aCBkYXkgMApjb21tb25ERWdlbmVzRGF5MDEzNyA8LSB3aGljaChkdFssMl0hPTAgJiBkdFssM10hPTAgJiBkdFssNF0hPTAgJiBkdFssNV0hPTApCm5hbWVzKGNvbW1vbkRFZ2VuZXNEYXkwMTM3KQpgYGAKCkxldCdzIHZpc3VhbGlzZSBpdCB3aXRoIGEgbmljZSB2ZW5uIGRpYWdyYW1tCgpgYGB7ciBkYXkwMTM3X0RFX3Zlbm5fbm9Mb2cgfQp2ZW5uRGlhZ3JhbShkdFssMjo1XSwgY2lyY2xlLmNvbD1jKCJ0dXJxdW9pc2UiLCAic2FsbW9uIiwgImdyZWVuIiwieWVsbG93IiksbWFpbiA9ICJWZW5uIGRpYWdyYW1tIHNob3dpbmcgZGlmZmVyZW5jZXMgYmV0d2VlbiBjbHVzdGVycyIpCmBgYAoKIyMjIyBGaW5kaW5nIGdlbmVzIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBiZXR3ZWVuIGNsdXN0ZXJzIGJ1dCBub3Qgd2hpdGhpbiBjbHVzdGVycwoKRmluYWxseSB3aGF0IHdlIGFyZSByZWFsbHkgaW50ZXJlc3RlZCBpbiBhcmUgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gY2x1c3RlcnMgYnV0IG5vdCB3aGl0aGluIGNsdXN0ZXJzOiBoZXJlIGFyZSB0aGVzZSBnZW5lczoKCmBgYHtyIGNvbW1vbl9ERV9nZW5lc19iZXR3ZWVuQ0x1c3RlcnNfbm90V2hpdGhpbl9ub0xvZyB9CiMgRmluZGluZyBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBjbHVzdGVycyBidXQgbm90IHdoaXRoaW4gY2x1c3RlcnMKY29tbW9uREVnZW5lc0JldHdlZW5Ob3RXaGl0aGluIDwtIHdoaWNoKGR0WywyXSE9MCAmIGR0WywzXSE9MCAmIGR0Wyw0XSE9MCAmIGR0Wyw1XSE9MCAmIGR0WywxXT09MCAmIGR0Wyw2XT09MCkKbmFtZXMoY29tbW9uREVnZW5lc0JldHdlZW5Ob3RXaGl0aGluKQoKZ2dwbG90KGRhdGFmNFtkYXRhZjQkbWlybmEgJWluJSBuYW1lcyhjb21tb25ERWdlbmVzQmV0d2Vlbk5vdFdoaXRoaW4pLF0sIGFlcyh4PWV4cDIsIGZpbGwgPSBncm91cCkpICsgCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSkgKwogIGdndGl0bGUoIlJhbmRvbSBwb2ludCBwbG90IGZvciBpbnRlcmFjdGlvbiBtaVJOQS1ncm91cCIpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyB4bGltKDAsMzApICsKICBmYWNldF93cmFwKH5taXJuYSxuY29sID0gMykKCmdncGxvdChkYXRhZjRbZGF0YWY0JG1pcm5hICVpbiUgbmFtZXMoY29tbW9uREVnZW5lc0JldHdlZW5Ob3RXaGl0aGluKSxdLCBhZXMoeD1leHAyLCBmaWxsID0gZ3JvdXApKSArIAogIGdlb21fZGVuc2l0eShhbHBoYT0wLjUpICsKICBnZ3RpdGxlKCJSYW5kb20gZGVuc2l0eSBwbG90IGZvciBpbnRlcmFjdGlvbiBtaVJOQS1ncm91cDogbm8gbG9nIikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHhsaW0oLTUwMCwxMDAwMCkgKwogIGZhY2V0X3dyYXAofm1pcm5hLG5jb2wgPSAzKQoKI3dpbGwgbm90IGJlIG5lZWRlZDogZG9uJ3Qga2VlcCBiaWcgZGF0YQpyZW1vdmUoZGF0YWY0KQpgYGAKCgoKCgoKCgoKCgoKCg==