In this lecture we will introduce the main principles of working with count data, and how to model these using generalized linear models (GLMs). We focus on introducing the concept of generalized linear models, and how they can be applied to genomics data analysis.

1 The Poisson distribution

  • The Poisson distribution is a typical count distribution that is generally popular and fairly easy to work with. It is defined by a single parameter: its mean \(\mu\). For a Poisson distributed random variable \(Y_i\) with observations \(i \in \{1, \ldots, n\}\), its variance is equal to its mean. That is, if

\[Y_i \sim Poi(\mu)\] , then

\[E(Y_i) = Var(Y_i) = \mu.\]

  • This immediately shows an important feature of count data: the mean-variance relationship. Indeed, in count data, the variance will always be a function of the mean.

  • This is quite intuitive. Consider the following example. You have two bird cages, where in one bird cage there are \(10\) birds, while in the other there are \(100\) birds. You let a sample of people look to the number of birds in either one of the cages. It seems unlikely that a person in front of the 10-bird cage would come up with an estimate of \(3\), while it seems likely that someone in front of the 100-bird cage would come up with an estimate of \(80\). Even though the difference from the true value is the same, the exact value has an impact on the plausible deviation around it.

set.seed(11)
y1 <- rpois(n=500, lambda=10)
y2 <- rpois(n=500, lambda=100)

par(mfrow = c(1,2))
qplot(y1, main="Poisson(10)", geom = "histogram", binwidth=.5,center=0)

qplot(y2, main="Poisson(100)", geom = "histogram", binwidth=.5,center=0)

1.1 The Poisson distribution in RNA-seq

  • In RNA-seq, technical replicates represent different aliquots of the same sample being sequenced repeatedly. The underlying true expression of a gene can hence safely be assumed to be equal across these technical replicates.
  • Marioni et al. (2008) have shown that, for most genes, the distribution of observed gene expression counts across technical replicates follow a Poisson distribution. A small proportion of genes (\(\sim 0.5\%\)) do not follow this Poisson model, however, and actually show evidence for ‘extra-Poisson variation’.
Figure: Technical replication in RNA-seq. Figures from Marioni et al. (2008).

Figure: Technical replication in RNA-seq. Figures from Marioni et al. (2008).

1.2 Relative uncertainty for Poisson distributed random variables

Take a minute to consider the following question:

  • Suppose that we have a solid tumor sample from a cancer patient, as well as a sample of surrounding healthy tissue. For each sample, we have three technical replicates at our disposal. Let \(Y_{grt}\) denote the observed gene expression values of gene \(g\) in replicate \(r \in \{1,2,3\}\) from tissue \(t \in \{0,1\}\), where \(t=0\) denotes healthy tissue and \(t=1\) denotes tumoral tissue.
  • We then know that the random variables \(Y_{gr0}\) and \(Y_{gr1}\) follow a Poisson distribution, and we would estimate its mean as \(\bar{Y}_{g0} = \frac{1}{3} \sum_{r=1}^3 Y_{gr0}\) and \(\bar{Y}_{g1} = \frac{1}{3} \sum_{r=1}^3 Y_{gr1}\), respectively.
  • Similar, for another gene \(k\), we observe \(Y_{krt}\), and estimate \(\bar{Y}_{k0}\) and \(\bar{Y}_{k1}\) correspondingly.
  • Now suppose that \(\beta_{k} = \bar{Y}_{k1} / \bar{Y}_{k0} = 5\), but also \(\beta_g = \bar{Y}_{g1} / \bar{Y}_{g0} = 5\), i.e., the two genes have the same average expression ratio (also often called a fold-change) across samples. However, they are differently expressed as \(\bar{Y}_{k1} = 100\), and \(\bar{Y}_{g1} = 10\) (making \(\bar{Y}_{k0} = 20\), and \(\bar{Y}_{g0} = 2\)).
  • For which of the two genes is the uncertainty on the expression ratio the highest? In other words, do we trust \(\beta_k\) more or do we trust \(\beta_g\) more?

Let’s approximate the uncertainty in \(beta_g\) and \(\beta_k\) using simulation:

N <- 1e3
beta_g <- beta_k <- vector(length=N)
for(ii in 1:N){
  ygr1 <- rpois(n=3, lambda=10)
  ygr0 <- rpois(n=3, lambda=2)
  ykr1 <- rpois(n=3, lambda=100)
  ykr0 <- rpois(n=3, lambda=20)
  beta_g[ii] <- mean(ygr1) / mean(ygr0)
  beta_k[ii] <- mean(ykr1) / mean(ykr0)
}

par(mfrow=c(1,2), mar=c(4,2,3,1))
hist(beta_g, breaks=seq(0,50,by=1), xlim=c(0,50))
hist(beta_k, breaks=seq(0,50,by=1), xlim=c(0,50))


We clearly see that the uncertainty on \(\beta_k\) is much lower than on \(\beta_g\). Even though the variance on the counts of gene \(k\) is higher, since its mean is higher and it is distributed as a Poisson variable. How do we explain this?

  • We may explain this by considering the relative uncertainty on the mean. Relative uncertainty may be defined as the coefficient of variation \(CV = \frac{\sigma}{\mu}\) (this is, the standard deviation divided by the mean). Indeed, the CV describes the relative deviation of the distribution relative to its mean, where a low CV indicates low dispersion with respect to the mean.
  • Calculating the CV shows that the relative uncertainty for gene \(k\) than for gene \(g\), even though the variance on the raw counts is higher for gene \(k\) than for gene \(g\).
  • This lower relative uncertainty on the mean then propagates further to a lower uncertainty on the fold-change. This basic result will be essential for understanding the results of a differential expression analysis!
sqrt(100)/100 #CV for gene k
## [1] 0.1
sqrt(10)/10 #CV for gene g
## [1] 0.3162278

2 Modeling count data: Generalized linear models

Just like we have modeled protein abundances in the proteomics module of this course in order to assess differential protein abundance, we can model gene expression counts to identify genes with differences in average expression between groups of samples.

2.1 Why we can(’t) use linear models to model count data

  • If we are using a linear model to model a response \(Y_i\), with \(i \in \{1, \ldots, n\}\) in function of a single covariate \(X_i\), the linear model can be defined as follows:

\[ \left\{ \begin{array}{ccc} Y_i & = & \beta_0 + \beta_1 x_i + \epsilon_i \\ Y_i | x_i & \sim & N(\beta_0 + \beta_1 x_i, \sigma^2 ). \end{array} \right. \]

  • Or, equivalently, we can write it in matrix form as \[ \left\{ \begin{array}{ccc} Y & = & \mathbf{X}^T \boldsymbol{\beta} + \epsilon \\ Y | \mathbf{X} & \sim & N(\mathbf{X} \boldsymbol{\beta}, \sigma^2 \mathbf{I}), \end{array} \right. \] where \(\mathbf{X}\) now represents our \(n \times p\) design matrix, with row \(i\) corresponding to observation \(i\).

  • The variance-covariance matrix of \(\mathbf{Y}\) is assumed a diagonal matrix with \(\sigma^2\) on the diagonal elements and zero everywhere else. This means that the data points are uncorrelated, and that every observation has the same variance \(\sigma^2\), also referred to as homoscedasticity.

  • The latter does not hold for count data, due to the mean-variance relationship. This makes linear models, in its basic form, unsuitable to model count data.

  • In addition, count data are non-negative, while there are no such constraints in the standard linear model to make sure that our estimates will be non-negative. Indeed, \(\hat{Y}_i = \hat\mu_i = \mathbf{X}^T_i \hat{\beta} \in ] -\infty, \infty[\).

2.2 Generalized linear models

  • As the name suggests, generalized linear models (GLMs) extend linear models. In GLMs, we extend two things with respect to the linear model:
    • The conditional distribution of the response variable \(Y_i | X_i\) can be assumed to follow any distribution that belongs to the exponential family of distributions, which includes the Gaussian but also other commonly known distributions, such as the Binomial, Gamma and Poisson distribution.
    • The linear model assumed a linear relationship between \(Y_i\) and \(X_i\), since we assumed that \(E(Y_i | X_i) = \mathbf{X}^T_i \beta\). In GLMs, we will allow a link function \(g()\) that links the conditional mean to the covariates. Hence, in GLMs we have that \(g(E(Y_i | X_i)) = \mathbf{X}^T_i \beta\). Note that each family has got a canonical link function, which is the identity link function \(g(\mu) = \mu\) for Gaussian, the log link function \(g(\mu) = \log \mu\) for Poisson, or the logit link function \(g(\mu) = \log(\frac{\mu}{1-\mu})\) for Binomial.

2.2.1 A Poisson GLM

  • We can define a Poisson GLM as follows \[ \left\{ \begin{array}{ccc} Y_i & \sim & Poi(\mu_i) \\ \log \mu_i & = & \eta_i \\ \eta_i & = & \mathbf{X}^T_i \beta \\ \end{array} \right. \] where \(Y_i\) is the response variable, with mean \(\mu_i\), \(\eta_i\) is the linear predictor, \(\mathbf{X}\) is the \(n \times p\) model matrix and \(\beta\) is the \(p \times 1\) matrix of regression coefficients.
  • It is insightful to compare this model to a linear model where \(Y_i\) is log-transformed. Indeed, in the linear model case, we would be modeling \(E(\log Y_i )\), while in the GLM we are modeling \(\log E(Y_i)\).
  • This shows that in the GLM setting we are modeling a transformed version of the expected value, and after retransforming we can interpret the fit in terms of the mean of our response variable. In the transformed linear model, however, we are working with the expected value of a transformed version of our response variable, and we will not be able to interpret the fit in terms of the mean (because \(E( \log Y_i) \ne \log E(Y_i)\). In this specific case, we would have to resort to interpreting changes in terms of a geometric mean.
  • Also note that \(\mathbf{X}^T_i \beta \in ]-\infty, \infty[\), while \(Y_i\) must be non-negative \([0, \infty[\). The link function helps with this, since the exponential function transforms any real number to a non-negative number, i.e., \(\exp(\mathbf{X}^T_i \beta) \in [0, \infty[\).

3 Sequencing Data

In this lecture we will use a subset of the real bulk RNA-seq dataset from Haglund et al. (2012).

Lets try to work out the experimental design using the following paragraph from the Methods section of the paper.

Figure: A paragraph from the Methods section.

Figure: A paragraph from the Methods section.

3.1 Technical repeats

There are technical repeats in the data!

data("parathyroidGenesSE", package="parathyroidSE")
se <- parathyroidGenesSE
rm(parathyroidGenesSE)
colData(se) %>% knitr::kable(.)
run experiment patient treatment time submission study sample
SRR479052 SRX140503 1 Control 24h SRA051611 SRP012167 SRS308865
SRR479053 SRX140504 1 Control 48h SRA051611 SRP012167 SRS308866
SRR479054 SRX140505 1 DPN 24h SRA051611 SRP012167 SRS308867
SRR479055 SRX140506 1 DPN 48h SRA051611 SRP012167 SRS308868
SRR479056 SRX140507 1 OHT 24h SRA051611 SRP012167 SRS308869
SRR479057 SRX140508 1 OHT 48h SRA051611 SRP012167 SRS308870
SRR479058 SRX140509 2 Control 24h SRA051611 SRP012167 SRS308871
SRR479059 SRX140510 2 Control 48h SRA051611 SRP012167 SRS308872
SRR479060 SRX140511 2 DPN 24h SRA051611 SRP012167 SRS308873
SRR479061 SRX140511 2 DPN 24h SRA051611 SRP012167 SRS308873
SRR479062 SRX140512 2 DPN 48h SRA051611 SRP012167 SRS308874
SRR479063 SRX140513 2 OHT 24h SRA051611 SRP012167 SRS308875
SRR479064 SRX140513 2 OHT 24h SRA051611 SRP012167 SRS308875
SRR479065 SRX140514 2 OHT 48h SRA051611 SRP012167 SRS308876
SRR479066 SRX140515 3 Control 24h SRA051611 SRP012167 SRS308877
SRR479067 SRX140516 3 Control 48h SRA051611 SRP012167 SRS308878
SRR479068 SRX140517 3 DPN 24h SRA051611 SRP012167 SRS308879
SRR479069 SRX140518 3 DPN 48h SRA051611 SRP012167 SRS308880
SRR479070 SRX140519 3 OHT 24h SRA051611 SRP012167 SRS308881
SRR479071 SRX140520 3 OHT 48h SRA051611 SRP012167 SRS308882
SRR479072 SRX140521 4 Control 48h SRA051611 SRP012167 SRS308883
SRR479073 SRX140522 4 DPN 24h SRA051611 SRP012167 SRS308884
SRR479074 SRX140523 4 DPN 48h SRA051611 SRP012167 SRS308885
SRR479075 SRX140523 4 DPN 48h SRA051611 SRP012167 SRS308885
SRR479076 SRX140524 4 OHT 24h SRA051611 SRP012167 SRS308886
SRR479077 SRX140525 4 OHT 48h SRA051611 SRP012167 SRS308887
SRR479078 SRX140525 4 OHT 48h SRA051611 SRP012167 SRS308887
se %>% 
  colData %>%
  as.data.frame %>%
  pull(sample) %>% 
  nlevels
## [1] 23
se %>% 
  colData %>% 
  nrow
## [1] 27

Extract sample names of duplicates!

dupl <- which(table(colData(se)$sample) > 1) %>% names
techreps <- assays(se)$counts[,colData(se)$sample==dupl[1]]

3.1.1 Explore the data of two technical repeats

We expect the counts to be very similar. Indeed, they are based on the same cell culture, stimulated with the same stimulus and incubated for the same time.

qplot(techreps[,1],techreps[,2]) +
  geom_abline(intercept = 0, slope = 1) + 
  xlab("Technical repeat 1") +
  ylab("Technical repeat 2")

  • The plot shows a systematic difference between the counts of both technical repeats!

  • However, plotting the data on the original count scale does not give a good overview of the data. The plot is dominated by a few very abundant genes, i.e. genes with very high counts.

3.1.2 Explore the data of two technical repeats

qplot(techreps[,1],techreps[,2], log="xy") +
  geom_abline(intercept = 0, slope = 1) + 
  xlab("Technical repeat 1") +
  ylab("Technical repeat 2")
## Warning: Transformation introduced infinite values in continuous x-axis
## Warning: Transformation introduced infinite values in continuous y-axis

  • The plot clearly shows that the counts for all genes are very different in the two technical repeats.

  • However, we have to look at the plot via the diagonal.

  • In omics we therefore often use MA plots. With M (log ratio) and A (log average).

A <- techreps %>% log2 %>% rowMeans
M <- techreps %>% log2 %>% apply(.,1,diff)
w <- techreps[,1]==min(techreps[,1]) | techreps[,2]==min(techreps[,2])
if (any(w)) {
            A[w] <- runif(sum(w), min = -1, max = .1)
            M[w] <- log2(techreps[w,2] + 1) - log2(techreps[w,1] + 1)
}

MAplot <- qplot(A, M, col=w) +
  theme(legend.position = "none") + 
  scale_color_manual(values = c("black","orange")) +
  xlab("A: log2 Average") +
  ylab("M: log2 Fold Change")

MAplot +
  geom_abline(intercept=0,slope=0)

The MA plot clearly shows that the counts of one technical repeat are systematically higher than that of the other technical repeats.

colSums(techreps)
## [1]  5251911 19332369
logFCdepth <- colSums(techreps) %>% log2 %>% diff
logFCdepth
## [1] 1.880104
2^logFCdepth
## [1] 3.681016

The technical repeats differ with a factor 3.7 in sequencing depth!

MAplot + geom_abline(intercept = logFCdepth, slope = 0, color="red")

  • This log2 fold change in sequencing depth is also the baseline around which the log2 fold changes between technical repeats of individual genes are fluctuating!

Hence, we will have to correct for differences in sequencing depth.

3.2 Count scaling using GLM offsets

  • We have previously discussed count scaling transformations such as CPM and TPM.

  • A more appropriate and natural way when working with GLMs is through the use of offsets. The general use of an offset is to account for the ‘effort’ performed in order to gather that observation of the response variable.

\(\rightarrow\) i.e. sample being sequenced deeper contains more information as compared to a sample being sequenced relatively shallow. We have more confidence in a count from a deeply sequenced sample than that from a shallowly sequenced sample. We can therefore use the sequencing depth \(N_i=\sum_g Y_{gi}\) as offset in the model.

  • Adding an offset to the model is different from adding a new variable to the model. For each new variable we add, we will estimate its average effect \(\beta\) on the response variable. When adding an offset, however, we implicitly set \(\beta=1\).

  • Offsets are typically added on the scale of the linear predictor. Suppose we have a gene g and sample i specific offset \(O_{gi}\), then we can define our GLM including the offset as

    \[ \left\{ \begin{array}{ccc} Y_{gi} & \sim & Poi(\mu_{gi}) \\ \log \mu_{gi} & = & \eta_{gi} \\ \eta_{gi} & = & \mathbf{X}^T_i \beta_g + log(O_{gi}) \\ \end{array} \right. \]

When we would like to correct for the overall sequencing depth \(O_{gi}=N_i\). However, better offsets exist!

Note, that

\[ \mu_{ig}= \exp(\mathbf{X}^T_i \beta_g) \times O_{gi} \] or \[ \log\left(\frac{\mu_{ig}}{O_{gi}}\right)= \mathbf{X}^T_i \beta_g \]

3.3 Biological repeats

We extract the four biological repeats from the study for the control treatment at time 24h.

bioreps <- colData(se)$treatment=="Control" & colData(se)$time=="24h"
biorepCounts <- assays(se)$counts[,bioreps]
qplot(rowMeans(biorepCounts),rowVars(biorepCounts),log="xy") +
  geom_abline(intercept = 0,slope = 1,col="red") +
  geom_smooth(col="orange")
## Warning: Transformation introduced infinite values in continuous x-axis
## Warning: Transformation introduced infinite values in continuous y-axis
## Warning: Transformation introduced infinite values in continuous x-axis
## Warning: Transformation introduced infinite values in continuous y-axis
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
## Warning: Removed 37702 rows containing non-finite values (stat_smooth).

  • Having data on thousands of genes provides the opportunity to empirically assess the mean-variance relationship.

  • It is clear that the data is overdispersed with respect to the Poisson distribution (red y=x line). There also seems to be a quadratic trend of the variance as a function of the mean.

4 Negative binomial distribution

The observed quadratic mean variance trend has motivated the use of the negative binomial distribution to model (bulk) RNA-seq gene expression data.

\[ \left\{ \begin{array}{ccc} Y_{gi} & \sim & NB(\mu_{gi},\phi_g) \\ \log \mu_{gi} & = & \eta_{gi} \\ \eta_{gi} & = & \mathbf{X}^T_i \beta_g + log(O_{gi}) \\ \end{array} \right. \]

with

\[ \text{var}[Y_{gi}] = \mu_{gi} + \phi_g \mu_{gi}^2 \]

\(\quad\) Seq. technology \(\quad\) real expression
total variability = technical variability + biological variability
\(\text{var}[Y_{gi}]\) = \(\mu_{gi}\) + \(\phi_g \mu_{gi}^2\)
total CV\(^2\) = \(\frac{1}{\mu_{gi}}\) + \(\phi_g\)

4.1 What about technical repeats?

  • Technical repeats are Poisson distributed.
  • The sum of two Poisson distributed counts is again Poisson. So we can summarize over technical repeats by taking the sum of the counts. This enables us to collapse the technical repeats to the level of biological repeats.
  • We will illustrate that in the tutorial session.

4.2 Estimating the overdispersion?

  • Gene wise: for every single gene, very unstable due to the lack of data
  • Common dispersion for all genes
  • Trended dispersion
  • Tag-wise: EB shrinkage to a common (trended) dispersion: Borrow strength across genes (McCarthy & Smyth, 2012, DOI: 10.1093/nar/gks042)

In the tutorials we will analyse the entire study and we will focus on assessing the main research questions: i.e. comparing the early and late effects of the stimuli, and if the effect of stimuli is changes over time.

Here, we illustrate the estimation using the edgeR tool on a subset of control treatment for patients 1, 2 and 3. For this subset only biological repeats are sequenced and a measurement on time 24 and 48h has been taken for cells of each patient.
So we can model the study using a simple block design with a time effect and a block effect for patient.

  1. Setup the data
seSub <- se[,colData(se)$treatment=="Control"&colData(se)$patient%in%1:3]
colData(seSub)$patient <- colData(seSub)$patient %>% 
  as.double %>% 
  as.factor
y <- DGEList(counts = assays(seSub)$counts)

design <- model.matrix(~time+patient,colData(seSub))
  1. Typically lowly expressed genes are filtered.
keep <- filterByExpr(y,design)
y <- y[keep,]
  1. Normalisation to correct for differences in library size.
y <- calcNormFactors(y)
  1. Estimate the dispersions using empirical Bayes (EB)
y <- estimateDisp(y, design)
  1. Estimate gene-wise dispersions without shrinkage for comparison purposes
yNoEB <- estimateDisp(y, design, prior.df = 0)
  1. Visualisation and comparison of tag-wise (EB) and genewise dispersion
o <- order(y$AveLogCPM)
data.frame(
  AveLogCPM=rep(yNoEB$AveLogCPM[o],2),
  BCV=sqrt(
    c(yNoEB$tagwise.dispersion[o],
      y$tagwise.dispersion[o])
    ), 
  method=rep(c("genewise","tagwise"), each=nrow(y))) %>%
  ggplot(aes(AveLogCPM,BCV,color=method)) +
  geom_point(size = .2) +
  geom_line(
    data = data.frame(
      AveLogCPM=y$AveLogCPM[o],
      BCV = sqrt(y$trended.dispersion[o]),
      method="trended")) +
    scale_colour_manual(values =c('black','orange','blue'))+
  ylab("Biological Coefficient of Variation") +
  xlab("Average log CPM")

Note, that

  • The gene-wise dispersion is very variable (black dots).
  • There is a dispersion - mean trend (blue line)
  • The tag-wise dispersions (orange dots) are the result of shrinking the gene-wise dispersion (black dots) to the trend (blue line)

By default edgeR provides the following plot

plotBCV(y)

In the methods paper of edgeR different dispersion estimators were compared and the tag-wise dispersions seemed to provide the best goodness of fit.

QQ-plots of goodness of fit statistics using common, trended or empirical Bayes. Image from McCarthy and Smyth, 2012, NAR, DOI: 10.1093/nar/gks042

QQ-plots of goodness of fit statistics using common, trended or empirical Bayes. Image from McCarthy and Smyth, 2012, NAR, DOI: 10.1093/nar/gks042

5 Statistical Inference

  • Asymptotic statistical tests exist to test if (contrasts of the) parameters of the GLM are different from zero.

  • Implemented in edgeR and DESeq2.

  • Again we have to correct for multiple testing !!! FDR correction is done by default in the edgeR and DESeq2 output

  1. Fit the model
fit <- glmFit(y,design)
head(fit$coefficients)
##                 (Intercept)     time48h   patient2   patient3
## ENSG00000000003   -9.349871  0.13638138 -0.5613559 -0.7834884
## ENSG00000000419  -10.391095 -0.09800617  0.1057271  0.1356434
## ENSG00000000457  -10.928625 -0.08069712  0.4491010 -0.1289007
## ENSG00000000460  -10.041597 -0.81035512  0.5044914 -0.5433122
## ENSG00000000938  -14.614695  0.39307169  1.4719170  0.1349905
## ENSG00000000971  -13.512986  0.46252084 -0.2107229  0.5042261

Interpretation of model parameters?

  1. Statistical test to assess the time effect
lrt <- glmLRT(fit, coef = "time48h")
topTags(lrt)
## Coefficient:  time48h 
##                     logFC   logCPM       LR       PValue          FDR
## ENSG00000164089  4.284786 3.099739 213.7650 2.072422e-48 3.294944e-44
## ENSG00000148795  3.026969 3.686056 183.3434 9.025281e-42 7.174647e-38
## ENSG00000133110  1.293005 7.185439 164.0009 1.512056e-37 8.013393e-34
## ENSG00000211445  1.450374 8.966014 162.1696 3.798734e-37 1.509902e-33
## ENSG00000107796  1.398750 6.002617 146.0886 1.241600e-33 3.865574e-30
## ENSG00000169239  1.557744 7.377027 145.7683 1.458799e-33 3.865574e-30
## ENSG00000188404 -1.590059 9.851890 141.9293 1.007722e-32 2.288824e-29
## ENSG00000136235  1.375796 5.149565 139.4353 3.537613e-32 7.030564e-29
## ENSG00000163631  1.835300 4.898878 135.1416 3.074462e-31 5.431208e-28
## ENSG00000005189 -1.702284 5.615167 134.7941 3.662515e-31 5.823032e-28
LS0tCnRpdGxlOiAnSW50cm9kdWN0aW9uIHRvIFNlcXVlbmNpbmc6IFdvcmtpbmcgd2l0aCBSTkEtc2VxIGNvdW50IGRhdGEnCmF1dGhvcjogIktvZW4gVmFuIGRlbiBCZXJnZSAmIExpZXZlbiBDbGVtZW50IgpkYXRlOiAiTGFzdCBlZGl0ZWQgb24gYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGxhdGV4X2VuZ2luZTogeGVsYXRleAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2RlcHRoOiA0Ci0tLQoKYGBge3IgZnVuY3Rpb25zLCBpbmNsdWRlPUZBTFNFfQojIEEgZnVuY3Rpb24gZm9yIGNhcHRpb25pbmcgYW5kIHJlZmVyZW5jaW5nIGltYWdlcwpmaWcgPC0gbG9jYWwoewogICAgaSA8LSAwCiAgICByZWYgPC0gbGlzdCgpCiAgICBsaXN0KAogICAgICAgIGNhcD1mdW5jdGlvbihyZWZOYW1lLCB0ZXh0KSB7CiAgICAgICAgICAgIGkgPDwtIGkgKyAxCiAgICAgICAgICAgIHJlZltbcmVmTmFtZV1dIDw8LSBpCiAgICAgICAgICAgIHBhc3RlKCJGaWd1cmUgIiwgaSwgIjogIiwgdGV4dCwgc2VwPSIiKQogICAgICAgIH0sCiAgICAgICAgcmVmPWZ1bmN0aW9uKHJlZk5hbWUpIHsKICAgICAgICAgICAgcmVmW1tyZWZOYW1lXV0KICAgICAgICB9KQp9KQpgYGAgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoa25pdHIpCiAgbGlicmFyeShybWFya2Rvd24pCiAgbGlicmFyeShnZ3Bsb3QyKQogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCiAgbGlicmFyeShlZGdlUikKICBpZighInBhcmF0aHlyb2lkU0UiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKSBCaW9jTWFuYWdlcjo6aW5zdGFsbCgicGFyYXRoeXJvaWRTRSIpCn0pCmBgYAoKSW4gdGhpcyBsZWN0dXJlIHdlIHdpbGwgaW50cm9kdWNlIHRoZSBtYWluIHByaW5jaXBsZXMgb2Ygd29ya2luZyB3aXRoIGNvdW50IGRhdGEsIGFuZCBob3cgdG8gbW9kZWwgdGhlc2UgdXNpbmcgZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVscyAoR0xNcykuIFdlIGZvY3VzIG9uIGludHJvZHVjaW5nIHRoZSBjb25jZXB0IG9mIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMsIGFuZCBob3cgIHRoZXkgY2FuIGJlIGFwcGxpZWQgdG8gZ2Vub21pY3MgZGF0YSBhbmFseXNpcy4KCiMgVGhlIFBvaXNzb24gZGlzdHJpYnV0aW9uCgogLSBUaGUgUG9pc3NvbiBkaXN0cmlidXRpb24gaXMgYSB0eXBpY2FsIGNvdW50IGRpc3RyaWJ1dGlvbiB0aGF0IGlzIGdlbmVyYWxseSBwb3B1bGFyIGFuZCBmYWlybHkgZWFzeSB0byB3b3JrIHdpdGguIEl0IGlzIGRlZmluZWQgYnkgYSBzaW5nbGUgcGFyYW1ldGVyOiBpdHMgbWVhbiAkXG11JC4gRm9yIGEgUG9pc3NvbiBkaXN0cmlidXRlZCByYW5kb20gdmFyaWFibGUgJFlfaSQgd2l0aCBvYnNlcnZhdGlvbnMgJGkgXGluIFx7MSwgXGxkb3RzLCBuXH0kLCBpdHMgdmFyaWFuY2UgaXMgZXF1YWwgdG8gaXRzIG1lYW4uIFRoYXQgaXMsIGlmIAogCiAkJFlfaSBcc2ltIFBvaShcbXUpJCQKICwgdGhlbiAKIAogJCRFKFlfaSkgPSBWYXIoWV9pKSA9IFxtdS4kJAogCiAtIFRoaXMgaW1tZWRpYXRlbHkgc2hvd3MgYW4gaW1wb3J0YW50IGZlYXR1cmUgb2YgY291bnQgZGF0YTogdGhlICoqbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAqKi4gSW5kZWVkLCBpbiBjb3VudCBkYXRhLCB0aGUgdmFyaWFuY2Ugd2lsbCBhbHdheXMgYmUgYSBmdW5jdGlvbiBvZiB0aGUgbWVhbi4KIAogLSBUaGlzIGlzIHF1aXRlIGludHVpdGl2ZS4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBleGFtcGxlLiBZb3UgaGF2ZSB0d28gYmlyZCBjYWdlcywgd2hlcmUgaW4gb25lIGJpcmQgY2FnZSB0aGVyZSBhcmUgJDEwJCBiaXJkcywgd2hpbGUgaW4gdGhlIG90aGVyIHRoZXJlIGFyZSAkMTAwJCBiaXJkcy4gWW91IGxldCBhIHNhbXBsZSBvZiBwZW9wbGUgbG9vayB0byB0aGUgbnVtYmVyIG9mIGJpcmRzIGluIGVpdGhlciBvbmUgb2YgdGhlIGNhZ2VzLiBJdCBzZWVtcyB1bmxpa2VseSB0aGF0IGEgcGVyc29uIGluIGZyb250IG9mIHRoZSAxMC1iaXJkIGNhZ2Ugd291bGQgY29tZSB1cCB3aXRoIGFuIGVzdGltYXRlIG9mICQzJCwgd2hpbGUgaXQgc2VlbXMgbGlrZWx5IHRoYXQgc29tZW9uZSBpbiBmcm9udCBvZiB0aGUgMTAwLWJpcmQgY2FnZSB3b3VsZCBjb21lIHVwIHdpdGggYW4gZXN0aW1hdGUgb2YgJDgwJC4gRXZlbiB0aG91Z2ggdGhlIGRpZmZlcmVuY2UgZnJvbSB0aGUgdHJ1ZSB2YWx1ZSBpcyB0aGUgc2FtZSwgdGhlIGV4YWN0IHZhbHVlIGhhcyBhbiBpbXBhY3Qgb24gdGhlIHBsYXVzaWJsZSBkZXZpYXRpb24gYXJvdW5kIGl0LgogCmBgYHtyfQpzZXQuc2VlZCgxMSkKeTEgPC0gcnBvaXMobj01MDAsIGxhbWJkYT0xMCkKeTIgPC0gcnBvaXMobj01MDAsIGxhbWJkYT0xMDApCgpwYXIobWZyb3cgPSBjKDEsMikpCnFwbG90KHkxLCBtYWluPSJQb2lzc29uKDEwKSIsIGdlb20gPSAiaGlzdG9ncmFtIiwgYmlud2lkdGg9LjUsY2VudGVyPTApCnFwbG90KHkyLCBtYWluPSJQb2lzc29uKDEwMCkiLCBnZW9tID0gImhpc3RvZ3JhbSIsIGJpbndpZHRoPS41LGNlbnRlcj0wKQpgYGAKCiMjIFRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiBpbiBSTkEtc2VxCgogLSBJbiBSTkEtc2VxLCB0ZWNobmljYWwgcmVwbGljYXRlcyByZXByZXNlbnQgZGlmZmVyZW50IGFsaXF1b3RzIG9mIHRoZSBzYW1lIHNhbXBsZSBiZWluZyBzZXF1ZW5jZWQgcmVwZWF0ZWRseS4gVGhlIHVuZGVybHlpbmcgdHJ1ZSBleHByZXNzaW9uIG9mIGEgZ2VuZSBjYW4gaGVuY2Ugc2FmZWx5IGJlIGFzc3VtZWQgdG8gYmUgZXF1YWwgYWNyb3NzIHRoZXNlIHRlY2huaWNhbCByZXBsaWNhdGVzLgogLSBbTWFyaW9uaSAqZXQgYWwuKiAoMjAwOCldKGh0dHBzOi8vZ2Vub21lLmNzaGxwLm9yZy9jb250ZW50LzE4LzkvMTUwOSkgaGF2ZSBzaG93biB0aGF0LCBmb3IgbW9zdCBnZW5lcywgdGhlIGRpc3RyaWJ1dGlvbiBvZiBvYnNlcnZlZCBnZW5lIGV4cHJlc3Npb24gY291bnRzIGFjcm9zcyB0ZWNobmljYWwgcmVwbGljYXRlcyBmb2xsb3cgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbi4gQSBzbWFsbCBwcm9wb3J0aW9uIG9mIGdlbmVzICgkXHNpbSAwLjVcJSQpIGRvIG5vdCBmb2xsb3cgdGhpcyBQb2lzc29uIG1vZGVsLCBob3dldmVyLCBhbmQgYWN0dWFsbHkgc2hvdyBldmlkZW5jZSBmb3IgKidleHRyYS1Qb2lzc29uIHZhcmlhdGlvbicqLgogCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPXBhc3RlKCJGaWd1cmU6IFRlY2huaWNhbCByZXBsaWNhdGlvbiBpbiBSTkEtc2VxLiBGaWd1cmVzIGZyb20gTWFyaW9uaSBldCBhbC4gKDIwMDgpLiIpfQojIEFsbCBkZWZhdWx0cwppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL21hcmlvbmlGaWdzX2Nyb3BwZWQucG5nIikKYGBgCgojIyBSZWxhdGl2ZSB1bmNlcnRhaW50eSBmb3IgUG9pc3NvbiBkaXN0cmlidXRlZCByYW5kb20gdmFyaWFibGVzCgpUYWtlIGEgbWludXRlIHRvIGNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb246CgogLSBTdXBwb3NlIHRoYXQgd2UgaGF2ZSBhIHNvbGlkIHR1bW9yIHNhbXBsZSBmcm9tIGEgY2FuY2VyIHBhdGllbnQsIGFzIHdlbGwgYXMgYSBzYW1wbGUgb2Ygc3Vycm91bmRpbmcgaGVhbHRoeSB0aXNzdWUuIEZvciBlYWNoIHNhbXBsZSwgd2UgaGF2ZSB0aHJlZSB0ZWNobmljYWwgcmVwbGljYXRlcyBhdCBvdXIgZGlzcG9zYWwuIExldCAkWV97Z3J0fSQgZGVub3RlIHRoZSBvYnNlcnZlZCBnZW5lIGV4cHJlc3Npb24gdmFsdWVzIG9mIGdlbmUgJGckIGluIHJlcGxpY2F0ZSAkciBcaW4gXHsxLDIsM1x9JCBmcm9tIHRpc3N1ZSAkdCBcaW4gXHswLDFcfSQsIHdoZXJlICR0PTAkIGRlbm90ZXMgaGVhbHRoeSB0aXNzdWUgYW5kICR0PTEkIGRlbm90ZXMgdHVtb3JhbCB0aXNzdWUuIAogLSBXZSB0aGVuIGtub3cgdGhhdCB0aGUgcmFuZG9tIHZhcmlhYmxlcyAkWV97Z3IwfSQgYW5kICRZX3tncjF9JCBmb2xsb3cgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbiwgYW5kIHdlIHdvdWxkIGVzdGltYXRlIGl0cyBtZWFuIGFzICRcYmFye1l9X3tnMH0gPSBcZnJhY3sxfXszfSBcc3VtX3tyPTF9XjMgWV97Z3IwfSQgYW5kICRcYmFye1l9X3tnMX0gPSBcZnJhY3sxfXszfSBcc3VtX3tyPTF9XjMgWV97Z3IxfSQsIHJlc3BlY3RpdmVseS4gCiAtIFNpbWlsYXIsIGZvciBhbm90aGVyIGdlbmUgJGskLCB3ZSBvYnNlcnZlICRZX3trcnR9JCwgYW5kIGVzdGltYXRlICRcYmFye1l9X3trMH0kIGFuZCAkXGJhcntZfV97azF9JCBjb3JyZXNwb25kaW5nbHkuCiAtIE5vdyBzdXBwb3NlIHRoYXQgJFxiZXRhX3trfSA9IFxiYXJ7WX1fe2sxfSAvIFxiYXJ7WX1fe2swfSA9IDUkLCBidXQgYWxzbyAkXGJldGFfZyA9IFxiYXJ7WX1fe2cxfSAvIFxiYXJ7WX1fe2cwfSA9IDUkLCBpLmUuLCB0aGUgdHdvIGdlbmVzIGhhdmUgdGhlIHNhbWUgYXZlcmFnZSBleHByZXNzaW9uIHJhdGlvIChhbHNvIG9mdGVuIGNhbGxlZCBhIGZvbGQtY2hhbmdlKSBhY3Jvc3Mgc2FtcGxlcy4gSG93ZXZlciwgdGhleSBhcmUgZGlmZmVyZW50bHkgZXhwcmVzc2VkIGFzICRcYmFye1l9X3trMX0gPSAxMDAkLCBhbmQgJFxiYXJ7WX1fe2cxfSA9IDEwJCAobWFraW5nICRcYmFye1l9X3trMH0gPSAyMCQsIGFuZCAkXGJhcntZfV97ZzB9ID0gMiQpLgogLSBGb3Igd2hpY2ggb2YgdGhlIHR3byBnZW5lcyBpcyB0aGUgdW5jZXJ0YWludHkgb24gdGhlIGV4cHJlc3Npb24gcmF0aW8gdGhlIGhpZ2hlc3Q/IEluIG90aGVyIHdvcmRzLCBkbyB3ZSB0cnVzdCAkXGJldGFfayQgbW9yZSBvciBkbyB3ZSB0cnVzdCAkXGJldGFfZyQgbW9yZT8KIAogLS0tCgpMZXQncyBhcHByb3hpbWF0ZSB0aGUgdW5jZXJ0YWludHkgaW4gJGJldGFfZyQgYW5kICRcYmV0YV9rJCB1c2luZyBzaW11bGF0aW9uOgogCmBgYHtyfQpOIDwtIDFlMwpiZXRhX2cgPC0gYmV0YV9rIDwtIHZlY3RvcihsZW5ndGg9TikKZm9yKGlpIGluIDE6Til7CiAgeWdyMSA8LSBycG9pcyhuPTMsIGxhbWJkYT0xMCkKICB5Z3IwIDwtIHJwb2lzKG49MywgbGFtYmRhPTIpCiAgeWtyMSA8LSBycG9pcyhuPTMsIGxhbWJkYT0xMDApCiAgeWtyMCA8LSBycG9pcyhuPTMsIGxhbWJkYT0yMCkKICBiZXRhX2dbaWldIDwtIG1lYW4oeWdyMSkgLyBtZWFuKHlncjApCiAgYmV0YV9rW2lpXSA8LSBtZWFuKHlrcjEpIC8gbWVhbih5a3IwKQp9CgpwYXIobWZyb3c9YygxLDIpLCBtYXI9Yyg0LDIsMywxKSkKaGlzdChiZXRhX2csIGJyZWFrcz1zZXEoMCw1MCxieT0xKSwgeGxpbT1jKDAsNTApKQpoaXN0KGJldGFfaywgYnJlYWtzPXNlcSgwLDUwLGJ5PTEpLCB4bGltPWMoMCw1MCkpCmBgYAogCiAtLS0gCiAKIFdlIGNsZWFybHkgc2VlIHRoYXQgdGhlIHVuY2VydGFpbnR5IG9uICRcYmV0YV9rJCBpcyBtdWNoIGxvd2VyIHRoYW4gb24gJFxiZXRhX2ckLiBFdmVuIHRob3VnaCB0aGUgdmFyaWFuY2Ugb24gdGhlIGNvdW50cyBvZiBnZW5lICRrJCBpcyBoaWdoZXIsIHNpbmNlIGl0cyBtZWFuIGlzIGhpZ2hlciBhbmQgaXQgaXMgZGlzdHJpYnV0ZWQgYXMgYSBQb2lzc29uIHZhcmlhYmxlLiBIb3cgZG8gd2UgZXhwbGFpbiB0aGlzPwogCiAtIFdlIG1heSBleHBsYWluIHRoaXMgYnkgY29uc2lkZXJpbmcgdGhlIHJlbGF0aXZlIHVuY2VydGFpbnR5IG9uIHRoZSBtZWFuLiBSZWxhdGl2ZSB1bmNlcnRhaW50eSBtYXkgYmUgZGVmaW5lZCBhcyB0aGUgY29lZmZpY2llbnQgb2YgdmFyaWF0aW9uICRDViA9IFxmcmFje1xzaWdtYX17XG11fSQgKHRoaXMgaXMsIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gZGl2aWRlZCBieSB0aGUgbWVhbikuIEluZGVlZCwgdGhlIENWIGRlc2NyaWJlcyB0aGUgcmVsYXRpdmUgZGV2aWF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gcmVsYXRpdmUgdG8gaXRzIG1lYW4sIHdoZXJlIGEgbG93IENWIGluZGljYXRlcyBsb3cgZGlzcGVyc2lvbiB3aXRoIHJlc3BlY3QgdG8gdGhlIG1lYW4uCiAtIENhbGN1bGF0aW5nIHRoZSBDViBzaG93cyB0aGF0ICoqdGhlIHJlbGF0aXZlIHVuY2VydGFpbnR5IGZvciBnZW5lICRrJCB0aGFuIGZvciBnZW5lICRnJCwgZXZlbiB0aG91Z2ggdGhlIHZhcmlhbmNlIG9uIHRoZSByYXcgY291bnRzIGlzIGhpZ2hlciBmb3IgZ2VuZSAkayQgdGhhbiBmb3IgZ2VuZSAkZyQqKi4gCiAtIFRoaXMgbG93ZXIgcmVsYXRpdmUgdW5jZXJ0YWludHkgb24gdGhlIG1lYW4gdGhlbiBwcm9wYWdhdGVzIGZ1cnRoZXIgdG8gYSBsb3dlciB1bmNlcnRhaW50eSBvbiB0aGUgZm9sZC1jaGFuZ2UuIFRoaXMgYmFzaWMgcmVzdWx0IHdpbGwgYmUgZXNzZW50aWFsIGZvciB1bmRlcnN0YW5kaW5nIHRoZSByZXN1bHRzIG9mIGEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMhCiAKYGBge3J9CnNxcnQoMTAwKS8xMDAgI0NWIGZvciBnZW5lIGsKCnNxcnQoMTApLzEwICNDViBmb3IgZ2VuZSBnCmBgYAogCiMgTW9kZWxpbmcgY291bnQgZGF0YTogR2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVscwoKSnVzdCBsaWtlIHdlIGhhdmUgbW9kZWxlZCBwcm90ZWluIGFidW5kYW5jZXMgaW4gdGhlIHByb3Rlb21pY3MgbW9kdWxlIG9mIHRoaXMgY291cnNlIGluIG9yZGVyIHRvIGFzc2VzcyBkaWZmZXJlbnRpYWwgcHJvdGVpbiBhYnVuZGFuY2UsIHdlIGNhbiBtb2RlbCBnZW5lIGV4cHJlc3Npb24gY291bnRzIHRvIGlkZW50aWZ5IGdlbmVzIHdpdGggZGlmZmVyZW5jZXMgaW4gYXZlcmFnZSBleHByZXNzaW9uIGJldHdlZW4gZ3JvdXBzIG9mIHNhbXBsZXMuCgojIyBXaHkgd2UgY2FuKCd0KSB1c2UgbGluZWFyIG1vZGVscyB0byBtb2RlbCBjb3VudCBkYXRhCgogLSBJZiB3ZSBhcmUgdXNpbmcgYSBsaW5lYXIgbW9kZWwgdG8gbW9kZWwgYSByZXNwb25zZSAkWV9pJCwgd2l0aCAkaSBcaW4gXHsxLCBcbGRvdHMsIG5cfSQgaW4gZnVuY3Rpb24gb2YgYSBzaW5nbGUgY292YXJpYXRlICRYX2kkLCB0aGUgbGluZWFyIG1vZGVsIGNhbiBiZSBkZWZpbmVkIGFzIGZvbGxvd3M6CgpcWwpcbGVmdFx7ClxiZWdpbnthcnJheX17Y2NjfQpZX2kgJiA9ICYgXGJldGFfMCArIFxiZXRhXzEgeF9pICsgXGVwc2lsb25faSBcXApZX2kgfCB4X2kgJiBcc2ltICYgTihcYmV0YV8wICsgXGJldGFfMSB4X2ksIFxzaWdtYV4yICkuClxlbmR7YXJyYXl9ClxyaWdodC4KXF0KCiAtIE9yLCBlcXVpdmFsZW50bHksIHdlIGNhbiB3cml0ZSBpdCBpbiBtYXRyaXggZm9ybSBhcwogICQkCiAgXGxlZnRcewogIFxiZWdpbnthcnJheX17Y2NjfQogIFkgJiA9ICYgXG1hdGhiZntYfV5UIFxib2xkc3ltYm9se1xiZXRhfSArIFxlcHNpbG9uIFxcCiAgWSB8IFxtYXRoYmZ7WH0gJiBcc2ltICYgTihcbWF0aGJme1h9IFxib2xkc3ltYm9se1xiZXRhfSwgXHNpZ21hXjIgXG1hdGhiZntJfSksCiAgXGVuZHthcnJheX0KICBccmlnaHQuCiAgJCQKd2hlcmUgJFxtYXRoYmZ7WH0kIG5vdyByZXByZXNlbnRzIG91ciAkbiBcdGltZXMgcCQgZGVzaWduIG1hdHJpeCwgd2l0aCByb3cgJGkkIGNvcnJlc3BvbmRpbmcgdG8gb2JzZXJ2YXRpb24gJGkkLgoKIC0tLQoKIC0gVGhlIHZhcmlhbmNlLWNvdmFyaWFuY2UgbWF0cml4IG9mICRcbWF0aGJme1l9JCBpcyBhc3N1bWVkIGEgZGlhZ29uYWwgbWF0cml4IHdpdGggJFxzaWdtYV4yJCBvbiB0aGUgZGlhZ29uYWwgZWxlbWVudHMgYW5kIHplcm8gZXZlcnl3aGVyZSBlbHNlLiBUaGlzIG1lYW5zIHRoYXQgdGhlIGRhdGEgcG9pbnRzIGFyZSB1bmNvcnJlbGF0ZWQsIGFuZCB0aGF0IGV2ZXJ5IG9ic2VydmF0aW9uIGhhcyB0aGUgc2FtZSB2YXJpYW5jZSAkXHNpZ21hXjIkLCBhbHNvIHJlZmVycmVkIHRvIGFzIGhvbW9zY2VkYXN0aWNpdHkuIAogCiAtIFRoZSBsYXR0ZXIgZG9lcyBub3QgaG9sZCBmb3IgY291bnQgZGF0YSwgZHVlIHRvIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4gVGhpcyBtYWtlcyBsaW5lYXIgbW9kZWxzLCBpbiBpdHMgYmFzaWMgZm9ybSwgdW5zdWl0YWJsZSB0byBtb2RlbCBjb3VudCBkYXRhLgogCiAtIEluIGFkZGl0aW9uLCBjb3VudCBkYXRhIGFyZSBub24tbmVnYXRpdmUsIHdoaWxlIHRoZXJlIGFyZSBubyBzdWNoIGNvbnN0cmFpbnRzIGluIHRoZSBzdGFuZGFyZCBsaW5lYXIgbW9kZWwgdG8gbWFrZSBzdXJlIHRoYXQgb3VyIGVzdGltYXRlcyB3aWxsIGJlIG5vbi1uZWdhdGl2ZS4gCiBJbmRlZWQsICRcaGF0e1l9X2kgPSBcaGF0XG11X2kgPSBcbWF0aGJme1h9XlRfaSBcaGF0e1xiZXRhfSBcaW4gXSAtXGluZnR5LCBcaW5mdHlbJC4KCiMjIEdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMKCiAtIEFzIHRoZSBuYW1lIHN1Z2dlc3RzLCBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWxzIChHTE1zKSBleHRlbmQgbGluZWFyIG1vZGVscy4gSW4gR0xNcywgd2UgZXh0ZW5kIHR3byB0aGluZ3Mgd2l0aCByZXNwZWN0IHRvIHRoZSBsaW5lYXIgbW9kZWw6CiAgICAtIFRoZSAqKmNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgJFlfaSB8IFhfaSQqKiBjYW4gYmUgYXNzdW1lZCB0byBmb2xsb3cgYW55IGRpc3RyaWJ1dGlvbiB0aGF0IGJlbG9uZ3MgdG8gdGhlICoqZXhwb25lbnRpYWwgZmFtaWx5Kiogb2YgZGlzdHJpYnV0aW9ucywgd2hpY2ggaW5jbHVkZXMgdGhlIEdhdXNzaWFuIGJ1dCBhbHNvIG90aGVyIGNvbW1vbmx5IGtub3duIGRpc3RyaWJ1dGlvbnMsIHN1Y2ggYXMgdGhlIEJpbm9taWFsLCBHYW1tYSBhbmQgUG9pc3NvbiBkaXN0cmlidXRpb24uCiAgICAtIFRoZSBsaW5lYXIgbW9kZWwgYXNzdW1lZCBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiAkWV9pJCBhbmQgJFhfaSQsIHNpbmNlIHdlIGFzc3VtZWQgdGhhdCAkRShZX2kgfCBYX2kpID0gXG1hdGhiZntYfV5UX2kgXGJldGEkLiBJbiBHTE1zLCB3ZSB3aWxsIGFsbG93IGEgKipsaW5rIGZ1bmN0aW9uKiogJGcoKSQgdGhhdCBsaW5rcyB0aGUgY29uZGl0aW9uYWwgbWVhbiB0byB0aGUgY292YXJpYXRlcy4gSGVuY2UsIGluIEdMTXMgd2UgaGF2ZSB0aGF0ICRnKEUoWV9pIHwgWF9pKSkgPSBcbWF0aGJme1h9XlRfaSBcYmV0YSQuIE5vdGUgdGhhdCBlYWNoIGZhbWlseSBoYXMgZ290IGEgY2Fub25pY2FsIGxpbmsgZnVuY3Rpb24sIHdoaWNoIGlzIHRoZSBpZGVudGl0eSBsaW5rIGZ1bmN0aW9uICRnKFxtdSkgPSBcbXUkIGZvciBHYXVzc2lhbiwgdGhlIGxvZyBsaW5rIGZ1bmN0aW9uICRnKFxtdSkgPSBcbG9nIFxtdSQgZm9yIFBvaXNzb24sIG9yIHRoZSBsb2dpdCBsaW5rIGZ1bmN0aW9uICRnKFxtdSkgPSBcbG9nKFxmcmFje1xtdX17MS1cbXV9KSQgZm9yIEJpbm9taWFsLgogICAgCiMjIyBBIFBvaXNzb24gR0xNCgogLSBXZSBjYW4gZGVmaW5lIGEgUG9pc3NvbiBHTE0gYXMgZm9sbG93cwogICQkCiAgXGxlZnRcewogIFxiZWdpbnthcnJheX17Y2NjfQogIFlfaSAmIFxzaW0gJiBQb2koXG11X2kpIFxcCiAgXGxvZyBcbXVfaSAmID0gJiBcZXRhX2kgXFwKICBcZXRhX2kgJiA9ICYgXG1hdGhiZntYfV5UX2kgXGJldGEgXFwKICBcZW5ke2FycmF5fQogIFxyaWdodC4KICAkJAp3aGVyZSAkWV9pJCBpcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUsIHdpdGggbWVhbiAkXG11X2kkLCAkXGV0YV9pJCBpcyB0aGUgbGluZWFyIHByZWRpY3RvciwgJFxtYXRoYmZ7WH0kIGlzIHRoZSAkbiBcdGltZXMgcCQgbW9kZWwgbWF0cml4IGFuZCAkXGJldGEkIGlzIHRoZSAkcCBcdGltZXMgMSQgbWF0cml4IG9mIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLgogIC0gSXQgaXMgaW5zaWdodGZ1bCB0byBjb21wYXJlIHRoaXMgbW9kZWwgdG8gYSBsaW5lYXIgbW9kZWwgd2hlcmUgJFlfaSQgaXMgbG9nLXRyYW5zZm9ybWVkLiBJbmRlZWQsIGluIHRoZSBsaW5lYXIgbW9kZWwgY2FzZSwgd2Ugd291bGQgYmUgbW9kZWxpbmcgJEUoXGxvZyBZX2kgKSQsIHdoaWxlIGluIHRoZSBHTE0gd2UgYXJlIG1vZGVsaW5nICRcbG9nIEUoWV9pKSQuCiAgLSBUaGlzIHNob3dzIHRoYXQgaW4gdGhlIEdMTSBzZXR0aW5nIHdlIGFyZSBtb2RlbGluZyBhIHRyYW5zZm9ybWVkIHZlcnNpb24gb2YgdGhlIGV4cGVjdGVkIHZhbHVlLCBhbmQgYWZ0ZXIgcmV0cmFuc2Zvcm1pbmcgd2UgY2FuIGludGVycHJldCB0aGUgZml0IGluIHRlcm1zIG9mIHRoZSBtZWFuIG9mIG91ciByZXNwb25zZSB2YXJpYWJsZS4gSW4gdGhlIHRyYW5zZm9ybWVkIGxpbmVhciBtb2RlbCwgaG93ZXZlciwgd2UgYXJlIHdvcmtpbmcgd2l0aCB0aGUgZXhwZWN0ZWQgdmFsdWUgb2YgYSB0cmFuc2Zvcm1lZCB2ZXJzaW9uIG9mIG91ciByZXNwb25zZSB2YXJpYWJsZSwgYW5kIHdlIHdpbGwgbm90IGJlIGFibGUgdG8gaW50ZXJwcmV0IHRoZSBmaXQgaW4gdGVybXMgb2YgdGhlIG1lYW4gKGJlY2F1c2UgJEUoIFxsb2cgWV9pKSBcbmUgXGxvZyBFKFlfaSkkLiBJbiB0aGlzIHNwZWNpZmljIGNhc2UsIHdlIHdvdWxkIGhhdmUgdG8gcmVzb3J0IHRvIGludGVycHJldGluZyBjaGFuZ2VzIGluIHRlcm1zIG9mIGEgZ2VvbWV0cmljIG1lYW4uCiAgLSBBbHNvIG5vdGUgdGhhdCAkXG1hdGhiZntYfV5UX2kgXGJldGEgXGluIF0tXGluZnR5LCBcaW5mdHlbJCwgd2hpbGUgJFlfaSQgbXVzdCBiZSBub24tbmVnYXRpdmUgJFswLCBcaW5mdHlbJC4gVGhlIGxpbmsgZnVuY3Rpb24gaGVscHMgd2l0aCB0aGlzLCBzaW5jZSB0aGUgZXhwb25lbnRpYWwgZnVuY3Rpb24gdHJhbnNmb3JtcyBhbnkgcmVhbCBudW1iZXIgdG8gYSBub24tbmVnYXRpdmUgbnVtYmVyLCBpLmUuLCAkXGV4cChcbWF0aGJme1h9XlRfaSBcYmV0YSkgXGluIFswLCBcaW5mdHlbJC4gCgojIFNlcXVlbmNpbmcgRGF0YQoKSW4gdGhpcyBsZWN0dXJlIHdlIHdpbGwgdXNlIGEgc3Vic2V0IG9mIHRoZSByZWFsIGJ1bGsgUk5BLXNlcSBkYXRhc2V0IGZyb20gW0hhZ2x1bmQgKmV0IGFsLiogKDIwMTIpXShodHRwczovL2FjYWRlbWljLm91cC5jb20vamNlbS9hcnRpY2xlLzk3LzEyLzQ2MzEvMjUzNjU3MykuCgpMZXRzIHRyeSB0byB3b3JrIG91dCB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbiB1c2luZyB0aGUgZm9sbG93aW5nIHBhcmFncmFwaCBmcm9tIHRoZSBNZXRob2RzIHNlY3Rpb24gb2YgdGhlIHBhcGVyLgoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5jYXA9cGFzdGUoIkZpZ3VyZTogQSBwYXJhZ3JhcGggZnJvbSB0aGUgTWV0aG9kcyBzZWN0aW9uLiIpfQojIEFsbCBkZWZhdWx0cwppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL2V4cERlc2lnbl9wYXJhLnBuZyIpCmBgYAoKIyMgVGVjaG5pY2FsIHJlcGVhdHMgCgpUaGVyZSBhcmUgdGVjaG5pY2FsIHJlcGVhdHMgaW4gdGhlIGRhdGEhIAoKYGBge3J9CmRhdGEoInBhcmF0aHlyb2lkR2VuZXNTRSIsIHBhY2thZ2U9InBhcmF0aHlyb2lkU0UiKQpzZSA8LSBwYXJhdGh5cm9pZEdlbmVzU0UKcm0ocGFyYXRoeXJvaWRHZW5lc1NFKQpjb2xEYXRhKHNlKSAlPiUga25pdHI6OmthYmxlKC4pCmBgYAoKYGBge3J9CnNlICU+JSAKICBjb2xEYXRhICU+JQogIGFzLmRhdGEuZnJhbWUgJT4lCiAgcHVsbChzYW1wbGUpICU+JSAKICBubGV2ZWxzCgpzZSAlPiUgCiAgY29sRGF0YSAlPiUgCiAgbnJvdwpgYGAKCkV4dHJhY3Qgc2FtcGxlIG5hbWVzIG9mIGR1cGxpY2F0ZXMhIAoKYGBge3J9CmR1cGwgPC0gd2hpY2godGFibGUoY29sRGF0YShzZSkkc2FtcGxlKSA+IDEpICU+JSBuYW1lcwp0ZWNocmVwcyA8LSBhc3NheXMoc2UpJGNvdW50c1ssY29sRGF0YShzZSkkc2FtcGxlPT1kdXBsWzFdXQpgYGAKCiMjIyBFeHBsb3JlIHRoZSBkYXRhIG9mIHR3byB0ZWNobmljYWwgcmVwZWF0cwoKV2UgZXhwZWN0IHRoZSBjb3VudHMgdG8gYmUgdmVyeSBzaW1pbGFyLiBJbmRlZWQsIHRoZXkgYXJlIGJhc2VkIG9uIHRoZSBzYW1lIGNlbGwgY3VsdHVyZSwgc3RpbXVsYXRlZCB3aXRoIHRoZSBzYW1lIHN0aW11bHVzIGFuZCBpbmN1YmF0ZWQgZm9yIHRoZSBzYW1lIHRpbWUuIAoKYGBge3J9CnFwbG90KHRlY2hyZXBzWywxXSx0ZWNocmVwc1ssMl0pICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEpICsgCiAgeGxhYigiVGVjaG5pY2FsIHJlcGVhdCAxIikgKwogIHlsYWIoIlRlY2huaWNhbCByZXBlYXQgMiIpCmBgYAoKLSBUaGUgcGxvdCBzaG93cyBhIHN5c3RlbWF0aWMgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBjb3VudHMgb2YgYm90aCB0ZWNobmljYWwgcmVwZWF0cyEKCi0gSG93ZXZlciwgcGxvdHRpbmcgdGhlIGRhdGEgb24gdGhlIG9yaWdpbmFsIGNvdW50IHNjYWxlIGRvZXMgbm90IGdpdmUgYSBnb29kIG92ZXJ2aWV3IG9mIHRoZSBkYXRhLiBUaGUgcGxvdCBpcyBkb21pbmF0ZWQgYnkgYSBmZXcgdmVyeSBhYnVuZGFudCBnZW5lcywgaS5lLiBnZW5lcyB3aXRoIHZlcnkgaGlnaCBjb3VudHMuIAoKIyMjIEV4cGxvcmUgdGhlIGRhdGEgb2YgdHdvIHRlY2huaWNhbCByZXBlYXRzCgoKYGBge3J9CnFwbG90KHRlY2hyZXBzWywxXSx0ZWNocmVwc1ssMl0sIGxvZz0ieHkiKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArIAogIHhsYWIoIlRlY2huaWNhbCByZXBlYXQgMSIpICsKICB5bGFiKCJUZWNobmljYWwgcmVwZWF0IDIiKQpgYGAKCi0gVGhlIHBsb3QgY2xlYXJseSBzaG93cyB0aGF0IHRoZSBjb3VudHMgZm9yIGFsbCBnZW5lcyBhcmUgdmVyeSBkaWZmZXJlbnQgaW4gdGhlIHR3byB0ZWNobmljYWwgcmVwZWF0cy4gCgotIEhvd2V2ZXIsIHdlIGhhdmUgdG8gbG9vayBhdCB0aGUgcGxvdCB2aWEgdGhlIGRpYWdvbmFsLiAKCi0gSW4gb21pY3Mgd2UgdGhlcmVmb3JlIG9mdGVuIHVzZSBNQSBwbG90cy4gV2l0aCBNIChsb2cgcmF0aW8pIGFuZCBBIChsb2cgYXZlcmFnZSkuIAoKYGBge3J9CkEgPC0gdGVjaHJlcHMgJT4lIGxvZzIgJT4lIHJvd01lYW5zCk0gPC0gdGVjaHJlcHMgJT4lIGxvZzIgJT4lIGFwcGx5KC4sMSxkaWZmKQp3IDwtIHRlY2hyZXBzWywxXT09bWluKHRlY2hyZXBzWywxXSkgfCB0ZWNocmVwc1ssMl09PW1pbih0ZWNocmVwc1ssMl0pCmlmIChhbnkodykpIHsKICAgICAgICAgICAgQVt3XSA8LSBydW5pZihzdW0odyksIG1pbiA9IC0xLCBtYXggPSAuMSkKICAgICAgICAgICAgTVt3XSA8LSBsb2cyKHRlY2hyZXBzW3csMl0gKyAxKSAtIGxvZzIodGVjaHJlcHNbdywxXSArIDEpCn0KCk1BcGxvdCA8LSBxcGxvdChBLCBNLCBjb2w9dykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJvcmFuZ2UiKSkgKwogIHhsYWIoIkE6IGxvZzIgQXZlcmFnZSIpICsKICB5bGFiKCJNOiBsb2cyIEZvbGQgQ2hhbmdlIikKCk1BcGxvdCArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0PTAsc2xvcGU9MCkKYGBgCgpUaGUgTUEgcGxvdCBjbGVhcmx5IHNob3dzIHRoYXQgdGhlIGNvdW50cyBvZiBvbmUgdGVjaG5pY2FsIHJlcGVhdCBhcmUgc3lzdGVtYXRpY2FsbHkgaGlnaGVyIHRoYW4gdGhhdCBvZiB0aGUgb3RoZXIgdGVjaG5pY2FsIHJlcGVhdHMuIAoKYGBge3J9CmNvbFN1bXModGVjaHJlcHMpCmxvZ0ZDZGVwdGggPC0gY29sU3Vtcyh0ZWNocmVwcykgJT4lIGxvZzIgJT4lIGRpZmYKbG9nRkNkZXB0aAoyXmxvZ0ZDZGVwdGgKYGBgCgpUaGUgdGVjaG5pY2FsIHJlcGVhdHMgZGlmZmVyIHdpdGggYSBmYWN0b3IgYHIgcm91bmQoMl5sb2dGQ2RlcHRoLDEpYCBpbiBzZXF1ZW5jaW5nIGRlcHRoISAKCgpgYGB7cn0KTUFwbG90ICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gbG9nRkNkZXB0aCwgc2xvcGUgPSAwLCBjb2xvcj0icmVkIikKYGBgCgotIFRoaXMgbG9nMiBmb2xkIGNoYW5nZSBpbiBzZXF1ZW5jaW5nIGRlcHRoIGlzIGFsc28gdGhlIGJhc2VsaW5lIGFyb3VuZCB3aGljaCB0aGUgbG9nMiBmb2xkIGNoYW5nZXMgYmV0d2VlbiB0ZWNobmljYWwgcmVwZWF0cyBvZiBpbmRpdmlkdWFsIGdlbmVzIGFyZSBmbHVjdHVhdGluZyEgCgpIZW5jZSwgd2Ugd2lsbCBoYXZlIHRvIGNvcnJlY3QgZm9yIGRpZmZlcmVuY2VzIGluIHNlcXVlbmNpbmcgZGVwdGguIAoKIyMgQ291bnQgc2NhbGluZyB1c2luZyBHTE0gb2Zmc2V0cyAKCi0gV2UgaGF2ZSBwcmV2aW91c2x5IGRpc2N1c3NlZCBjb3VudCBzY2FsaW5nIHRyYW5zZm9ybWF0aW9ucyBzdWNoIGFzIENQTSBhbmQgVFBNLgoKLSBBIG1vcmUgYXBwcm9wcmlhdGUgYW5kIG5hdHVyYWwgd2F5IHdoZW4gd29ya2luZyB3aXRoIEdMTXMgaXMgdGhyb3VnaCB0aGUgdXNlIG9mIG9mZnNldHMuIFRoZSBnZW5lcmFsIHVzZSBvZiBhbiBvZmZzZXQgaXMgdG8gYWNjb3VudCBmb3IgdGhlIOKAmGVmZm9ydOKAmSBwZXJmb3JtZWQgaW4gb3JkZXIgdG8gZ2F0aGVyIHRoYXQgb2JzZXJ2YXRpb24gb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLgoKJFxyaWdodGFycm93JCBpLmUuIHNhbXBsZSBiZWluZyBzZXF1ZW5jZWQgZGVlcGVyIGNvbnRhaW5zIG1vcmUgaW5mb3JtYXRpb24gYXMgY29tcGFyZWQgdG8gYSBzYW1wbGUgYmVpbmcgc2VxdWVuY2VkIHJlbGF0aXZlbHkgc2hhbGxvdy4gV2UgaGF2ZSBtb3JlIGNvbmZpZGVuY2UgaW4gYSBjb3VudCBmcm9tIGEgZGVlcGx5IHNlcXVlbmNlZCBzYW1wbGUgdGhhbiB0aGF0IGZyb20gYSBzaGFsbG93bHkgc2VxdWVuY2VkIHNhbXBsZS4gV2UgY2FuIHRoZXJlZm9yZSB1c2UgdGhlIHNlcXVlbmNpbmcgZGVwdGggJE5faT1cc3VtX2cgWV97Z2l9JCBhcyBvZmZzZXQgaW4gdGhlIG1vZGVsLgoKCi0gQWRkaW5nIGFuIG9mZnNldCB0byB0aGUgbW9kZWwgaXMgZGlmZmVyZW50IGZyb20gYWRkaW5nIGEgbmV3IHZhcmlhYmxlIHRvIHRoZSBtb2RlbC4gRm9yIGVhY2ggbmV3IHZhcmlhYmxlIHdlIGFkZCwgd2Ugd2lsbCBlc3RpbWF0ZSBpdHMgYXZlcmFnZSBlZmZlY3QgJFxiZXRhJCBvbiB0aGUgcmVzcG9uc2UgdmFyaWFibGUuIFdoZW4gYWRkaW5nIGFuIG9mZnNldCwgaG93ZXZlciwgd2UgaW1wbGljaXRseSBzZXQgJFxiZXRhPTEkLgoKLSBPZmZzZXRzIGFyZSB0eXBpY2FsbHkgYWRkZWQgb24gdGhlIHNjYWxlIG9mIHRoZSBsaW5lYXIgcHJlZGljdG9yLiBTdXBwb3NlIHdlIGhhdmUgYSBnZW5lIGcgYW5kIHNhbXBsZSBpIHNwZWNpZmljIG9mZnNldCAkT197Z2l9JCwgdGhlbiB3ZSBjYW4gZGVmaW5lIG91ciBHTE0gaW5jbHVkaW5nIHRoZSBvZmZzZXQgYXMKCiAgJCQKICBcbGVmdFx7CiAgXGJlZ2lue2FycmF5fXtjY2N9CiAgWV97Z2l9ICYgXHNpbSAmIFBvaShcbXVfe2dpfSkgXFwKICBcbG9nIFxtdV97Z2l9ICYgPSAmIFxldGFfe2dpfSBcXAogIFxldGFfe2dpfSAmID0gJiBcbWF0aGJme1h9XlRfaSBcYmV0YV9nICsgbG9nKE9fe2dpfSkgXFwKICBcZW5ke2FycmF5fQogIFxyaWdodC4KICAkJAoKV2hlbiB3ZSB3b3VsZCBsaWtlIHRvIGNvcnJlY3QgZm9yIHRoZSBvdmVyYWxsIHNlcXVlbmNpbmcgZGVwdGggJE9fe2dpfT1OX2kkLiBIb3dldmVyLCBiZXR0ZXIgb2Zmc2V0cyBleGlzdCEKCk5vdGUsIHRoYXQgCgokJApcbXVfe2lnfT0gXGV4cChcbWF0aGJme1h9XlRfaSBcYmV0YV9nKSBcdGltZXMgT197Z2l9CiQkCm9yIAokJApcbG9nXGxlZnQoXGZyYWN7XG11X3tpZ319e09fe2dpfX1ccmlnaHQpPSBcbWF0aGJme1h9XlRfaSBcYmV0YV9nCiQkCgojIyBCaW9sb2dpY2FsIHJlcGVhdHMKCldlIGV4dHJhY3QgdGhlIGZvdXIgYmlvbG9naWNhbCByZXBlYXRzIGZyb20gdGhlIHN0dWR5IGZvciB0aGUgY29udHJvbCB0cmVhdG1lbnQgYXQgdGltZSAyNGguIAoKYGBge3J9CmJpb3JlcHMgPC0gY29sRGF0YShzZSkkdHJlYXRtZW50PT0iQ29udHJvbCIgJiBjb2xEYXRhKHNlKSR0aW1lPT0iMjRoIgpiaW9yZXBDb3VudHMgPC0gYXNzYXlzKHNlKSRjb3VudHNbLGJpb3JlcHNdCnFwbG90KHJvd01lYW5zKGJpb3JlcENvdW50cykscm93VmFycyhiaW9yZXBDb3VudHMpLGxvZz0ieHkiKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCxzbG9wZSA9IDEsY29sPSJyZWQiKSArCiAgZ2VvbV9zbW9vdGgoY29sPSJvcmFuZ2UiKQpgYGAKCi0gSGF2aW5nIGRhdGEgb24gdGhvdXNhbmRzIG9mIGdlbmVzIHByb3ZpZGVzIHRoZSBvcHBvcnR1bml0eSB0byBlbXBpcmljYWxseSBhc3Nlc3MgdGhlIG1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwLgoKLSBJdCBpcyBjbGVhciB0aGF0IHRoZSBkYXRhIGlzIG92ZXJkaXNwZXJzZWQgd2l0aCByZXNwZWN0IHRvIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiAocmVkIHk9eCBsaW5lKS4gVGhlcmUgYWxzbyBzZWVtcyB0byBiZSBhIHF1YWRyYXRpYyB0cmVuZCBvZiB0aGUgdmFyaWFuY2UgYXMgYSBmdW5jdGlvbiBvZiB0aGUgbWVhbi4gCgojIE5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbgoKVGhlIG9ic2VydmVkIHF1YWRyYXRpYyBtZWFuIHZhcmlhbmNlIHRyZW5kIGhhcyBtb3RpdmF0ZWQgdGhlIHVzZSBvZiB0aGUgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uIHRvIG1vZGVsIChidWxrKSBSTkEtc2VxIGdlbmUgZXhwcmVzc2lvbiBkYXRhLgoKJCQKICBcbGVmdFx7CiAgXGJlZ2lue2FycmF5fXtjY2N9CiAgWV97Z2l9ICYgXHNpbSAmIE5CKFxtdV97Z2l9LFxwaGlfZykgXFwKICBcbG9nIFxtdV97Z2l9ICYgPSAmIFxldGFfe2dpfSBcXAogIFxldGFfe2dpfSAmID0gJiBcbWF0aGJme1h9XlRfaSBcYmV0YV9nICsgbG9nKE9fe2dpfSkgXFwKICBcZW5ke2FycmF5fQogIFxyaWdodC4KICAkJAoKd2l0aCAKCiQkIApcdGV4dHt2YXJ9W1lfe2dpfV0gPSBcbXVfe2dpfSArIFxwaGlfZyBcbXVfe2dpfV4yIAokJAoKfCAgIHwgJFxxdWFkJCB8IFNlcS4gdGVjaG5vbG9neSAgfCAkXHF1YWQkIHwgcmVhbCBleHByZXNzaW9uICB8IAp8LS0tfC0tLXwtLS18LS0tfC0tLXwKfCB0b3RhbCB2YXJpYWJpbGl0eSB8ID0gfCB0ZWNobmljYWwgdmFyaWFiaWxpdHkgfCArIHwgYmlvbG9naWNhbCB2YXJpYWJpbGl0eSAgIHwKfCAkXHRleHR7dmFyfVtZX3tnaX1dJCB8ID0gfCAkXG11X3tnaX0kICB8ICsgfCAkXHBoaV9nIFxtdV97Z2l9XjIkIHwgfCB0b3RhbCBDViReMiQgfCA9IHwgdGVjaG5pY2FsIENWJF4yJCB8ICsgfCBiaW9sb2dpY2FsIENWJF4yJCB8CnwgdG90YWwgQ1YkXjIkIHwgPSB8ICRcZnJhY3sxfXtcbXVfe2dpfX0kIHwgKyB8ICRccGhpX2ckICB8CgojIyAgV2hhdCBhYm91dCB0ZWNobmljYWwgcmVwZWF0cz8gCgotIFRlY2huaWNhbCByZXBlYXRzIGFyZSBQb2lzc29uIGRpc3RyaWJ1dGVkLgotIFRoZSBzdW0gb2YgdHdvIFBvaXNzb24gZGlzdHJpYnV0ZWQgY291bnRzIGlzIGFnYWluIFBvaXNzb24uIFNvIHdlIGNhbiBzdW1tYXJpemUgb3ZlciB0ZWNobmljYWwgcmVwZWF0cyBieSB0YWtpbmcgdGhlIHN1bSBvZiB0aGUgY291bnRzLiBUaGlzIGVuYWJsZXMgdXMgdG8gY29sbGFwc2UgdGhlIHRlY2huaWNhbCByZXBlYXRzIHRvIHRoZSBsZXZlbCBvZiBiaW9sb2dpY2FsIHJlcGVhdHMuIAotIFdlIHdpbGwgaWxsdXN0cmF0ZSB0aGF0IGluIHRoZSB0dXRvcmlhbCBzZXNzaW9uLgoKIyMgRXN0aW1hdGluZyB0aGUgb3ZlcmRpc3BlcnNpb24/CgotIEdlbmUgd2lzZTogZm9yIGV2ZXJ5IHNpbmdsZSBnZW5lLCB2ZXJ5IHVuc3RhYmxlIGR1ZSB0byB0aGUgbGFjayBvZiBkYXRhIAotIENvbW1vbiBkaXNwZXJzaW9uIGZvciBhbGwgZ2VuZXMgCi0gVHJlbmRlZCBkaXNwZXJzaW9uCi0gVGFnLXdpc2U6IEVCIHNocmlua2FnZSB0byBhIGNvbW1vbiAodHJlbmRlZCkgZGlzcGVyc2lvbjogQm9ycm93IHN0cmVuZ3RoIGFjcm9zcyBnZW5lcyAoTWNDYXJ0aHkgJiBTbXl0aCwgMjAxMiwgW0RPSTogMTAuMTA5My9uYXIvZ2tzMDQyXShodHRwczovL2RvaS5vcmcvMTAuMTA5My9uYXIvZ2tzMDQyKSkKCkluIHRoZSB0dXRvcmlhbHMgd2Ugd2lsbCBhbmFseXNlIHRoZSBlbnRpcmUgc3R1ZHkgYW5kIHdlIHdpbGwgZm9jdXMgb24gYXNzZXNzaW5nIHRoZSBtYWluIHJlc2VhcmNoIHF1ZXN0aW9uczogaS5lLiBjb21wYXJpbmcgdGhlIGVhcmx5IGFuZCBsYXRlIGVmZmVjdHMgb2YgdGhlIHN0aW11bGksIGFuZCBpZiB0aGUgZWZmZWN0IG9mIHN0aW11bGkgaXMgY2hhbmdlcyBvdmVyIHRpbWUuIAoKSGVyZSwgd2UgaWxsdXN0cmF0ZSB0aGUgZXN0aW1hdGlvbiB1c2luZyB0aGUgZWRnZVIgdG9vbCBvbiBhIHN1YnNldCBvZiBjb250cm9sIHRyZWF0bWVudCBmb3IgcGF0aWVudHMgMSwgMiBhbmQgMy4gRm9yIHRoaXMgc3Vic2V0IG9ubHkgYmlvbG9naWNhbCByZXBlYXRzIGFyZSBzZXF1ZW5jZWQgYW5kIGEgbWVhc3VyZW1lbnQgb24gdGltZSAyNCBhbmQgNDhoIGhhcyBiZWVuIHRha2VuIGZvciBjZWxscyBvZiBlYWNoIHBhdGllbnQuICAKU28gd2UgY2FuIG1vZGVsIHRoZSBzdHVkeSB1c2luZyBhIHNpbXBsZSBibG9jayBkZXNpZ24gd2l0aCBhIHRpbWUgZWZmZWN0IGFuZCBhIGJsb2NrIGVmZmVjdCBmb3IgcGF0aWVudC4KCjEuIFNldHVwIHRoZSBkYXRhCiAKCmBgYHtyfQpzZVN1YiA8LSBzZVssY29sRGF0YShzZSkkdHJlYXRtZW50PT0iQ29udHJvbCImY29sRGF0YShzZSkkcGF0aWVudCVpbiUxOjNdCmNvbERhdGEoc2VTdWIpJHBhdGllbnQgPC0gY29sRGF0YShzZVN1YikkcGF0aWVudCAlPiUgCiAgYXMuZG91YmxlICU+JSAKICBhcy5mYWN0b3IKeSA8LSBER0VMaXN0KGNvdW50cyA9IGFzc2F5cyhzZVN1YikkY291bnRzKQoKZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+dGltZStwYXRpZW50LGNvbERhdGEoc2VTdWIpKQpgYGAKCjIuIFR5cGljYWxseSBsb3dseSBleHByZXNzZWQgZ2VuZXMgYXJlIGZpbHRlcmVkLiAKCmBgYHtyfQprZWVwIDwtIGZpbHRlckJ5RXhwcih5LGRlc2lnbikKeSA8LSB5W2tlZXAsXQpgYGAKCjMuIE5vcm1hbGlzYXRpb24gdG8gY29ycmVjdCBmb3IgZGlmZmVyZW5jZXMgaW4gbGlicmFyeSBzaXplLgoKYGBge3J9CnkgPC0gY2FsY05vcm1GYWN0b3JzKHkpCmBgYAoKNC4gRXN0aW1hdGUgdGhlIGRpc3BlcnNpb25zIHVzaW5nIGVtcGlyaWNhbCBCYXllcyAoRUIpCgpgYGB7cn0KeSA8LSBlc3RpbWF0ZURpc3AoeSwgZGVzaWduKQpgYGAKCjUuIEVzdGltYXRlIGdlbmUtd2lzZSBkaXNwZXJzaW9ucyB3aXRob3V0IHNocmlua2FnZSBmb3IgY29tcGFyaXNvbiBwdXJwb3NlcyAKCmBgYHtyfQp5Tm9FQiA8LSBlc3RpbWF0ZURpc3AoeSwgZGVzaWduLCBwcmlvci5kZiA9IDApCmBgYAoKNi4gVmlzdWFsaXNhdGlvbiBhbmQgY29tcGFyaXNvbiBvZiB0YWctd2lzZSAoRUIpIGFuZCBnZW5ld2lzZSBkaXNwZXJzaW9uIAoKYGBge3J9Cm8gPC0gb3JkZXIoeSRBdmVMb2dDUE0pCmRhdGEuZnJhbWUoCiAgQXZlTG9nQ1BNPXJlcCh5Tm9FQiRBdmVMb2dDUE1bb10sMiksCiAgQkNWPXNxcnQoCiAgICBjKHlOb0VCJHRhZ3dpc2UuZGlzcGVyc2lvbltvXSwKICAgICAgeSR0YWd3aXNlLmRpc3BlcnNpb25bb10pCiAgICApLCAKICBtZXRob2Q9cmVwKGMoImdlbmV3aXNlIiwidGFnd2lzZSIpLCBlYWNoPW5yb3coeSkpKSAlPiUKICBnZ3Bsb3QoYWVzKEF2ZUxvZ0NQTSxCQ1YsY29sb3I9bWV0aG9kKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IC4yKSArCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IGRhdGEuZnJhbWUoCiAgICAgIEF2ZUxvZ0NQTT15JEF2ZUxvZ0NQTVtvXSwKICAgICAgQkNWID0gc3FydCh5JHRyZW5kZWQuZGlzcGVyc2lvbltvXSksCiAgICAgIG1ldGhvZD0idHJlbmRlZCIpKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9YygnYmxhY2snLCdvcmFuZ2UnLCdibHVlJykpKwogIHlsYWIoIkJpb2xvZ2ljYWwgQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIikgKwogIHhsYWIoIkF2ZXJhZ2UgbG9nIENQTSIpCmBgYAoKTm90ZSwgdGhhdCAKCi0gVGhlIGdlbmUtd2lzZSBkaXNwZXJzaW9uIGlzIHZlcnkgdmFyaWFibGUgKGJsYWNrIGRvdHMpLgotIFRoZXJlIGlzIGEgZGlzcGVyc2lvbiAtIG1lYW4gdHJlbmQgKGJsdWUgbGluZSkKLSBUaGUgdGFnLXdpc2UgZGlzcGVyc2lvbnMgKG9yYW5nZSBkb3RzKSBhcmUgdGhlIHJlc3VsdCBvZiBzaHJpbmtpbmcgdGhlIGdlbmUtd2lzZSBkaXNwZXJzaW9uIChibGFjayBkb3RzKSB0byB0aGUgdHJlbmQgKGJsdWUgbGluZSkgCgpCeSBkZWZhdWx0IGVkZ2VSIHByb3ZpZGVzIHRoZSBmb2xsb3dpbmcgcGxvdCAKCmBgYHtyfQpwbG90QkNWKHkpCmBgYAoKSW4gdGhlIG1ldGhvZHMgcGFwZXIgb2YgZWRnZVIgZGlmZmVyZW50IGRpc3BlcnNpb24gZXN0aW1hdG9ycyB3ZXJlIGNvbXBhcmVkIGFuZCB0aGUgdGFnLXdpc2UgZGlzcGVyc2lvbnMgc2VlbWVkIHRvIHByb3ZpZGUgdGhlIGJlc3QgZ29vZG5lc3Mgb2YgZml0LiAKCmBgYHtyIHJlZkdlbm9tZSwgZWNobz1GQUxTRSwgZmlnLmNhcD0iUVEtcGxvdHMgb2YgZ29vZG5lc3Mgb2YgZml0IHN0YXRpc3RpY3MgdXNpbmcgY29tbW9uLCB0cmVuZGVkIG9yIGVtcGlyaWNhbCBCYXllcy4gSW1hZ2UgZnJvbSBNY0NhcnRoeSBhbmQgU215dGgsIDIwMTIsIE5BUiwgRE9JOiAxMC4xMDkzL25hci9na3MwNDIifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9OQVJEaXNwZXJzaW9uLnBuZyIpCmBgYAoKIyBTdGF0aXN0aWNhbCBJbmZlcmVuY2UgCgotIEFzeW1wdG90aWMgc3RhdGlzdGljYWwgdGVzdHMgZXhpc3QgdG8gdGVzdCBpZiAoY29udHJhc3RzIG9mIHRoZSkgcGFyYW1ldGVycyBvZiB0aGUgR0xNIGFyZSBkaWZmZXJlbnQgZnJvbSB6ZXJvLgoKLSBJbXBsZW1lbnRlZCBpbiBlZGdlUiBhbmQgREVTZXEyLgoKLSBBZ2FpbiB3ZSBoYXZlIHRvIGNvcnJlY3QgZm9yIG11bHRpcGxlIHRlc3RpbmcgISEhIEZEUiBjb3JyZWN0aW9uIGlzIGRvbmUgYnkgIGRlZmF1bHQgaW4gdGhlIGVkZ2VSIGFuZCBERVNlcTIgb3V0cHV0IAoKMS4gRml0IHRoZSBtb2RlbAoKYGBge3J9CmZpdCA8LSBnbG1GaXQoeSxkZXNpZ24pCmhlYWQoZml0JGNvZWZmaWNpZW50cykKYGBgCgpJbnRlcnByZXRhdGlvbiBvZiBtb2RlbCBwYXJhbWV0ZXJzPyAKCgoyLiBTdGF0aXN0aWNhbCB0ZXN0IHRvIGFzc2VzcyB0aGUgdGltZSBlZmZlY3QgCgpgYGB7cn0KbHJ0IDwtIGdsbUxSVChmaXQsIGNvZWYgPSAidGltZTQ4aCIpCnRvcFRhZ3MobHJ0KQpgYGAKCg==