1 Parameter Estimation and Inference in Generalized linear models

1.1 Simulate Poisson Data

  • We simulate data for 100 observations.
  • Covariates x are simulated from normal distribution
  • The \(\beta\) are chosen at \(\beta_0=2\), \(\beta_1=0.8\), \(\beta_2=1.2\)
set.seed(300)
xhlp<-cbind(1,rnorm(100),rnorm(100))
betasTrue<-c(2,0.8,1.2)
etaTrue<-xhlp%*%betasTrue
y<-rpois(100,exp(etaTrue))
data.frame(coef =0:2,betasTrue=betasTrue) %>% 
  ggplot(aes(x=coef,y=betasTrue)) + 
  geom_point() +
  geom_line() +
  ylab("parameter value") +
  xlab("beta") +
  ylim(0,4) +
  theme_bw() +
  scale_x_continuous(breaks=c(0,1,2))

1.2 Exponential Family

\[ f(y_i\vert \theta_i,\phi)=\exp\left\{ \frac{y_i\theta_i- b(\theta_i)}{a(\phi)}+c(y_i,\phi)\right\} \]

with

  • \(\theta_i\): canonical parameters
  • \(\phi\): dispersion parameter
  • \(a(.)\), \(b(.)\), \(c(.)\): specific functions that depend on the distribution,

e.g. for normal distribution

  • \(\phi=\sigma^2\),
  • \(\theta=\mu\),
  • \(a(\phi)=\phi=\sigma^2\),
  • \(b(\theta_i)=\theta_i^2/2\),
  • \(c(y_i,\phi)=-\frac{1}{2}[y^2/\phi+\log(2\pi\phi)]\)

1.2.1 Poisson Distribution

\[ y_i \sim \frac{\mu_i^{y_i}e^{-\mu_i}}{y_i!} \]

In format of exponential family:

\[ y_i \sim \exp\left\{y_i\log(\mu_i) - \mu_i - \log(y_i!)\right\} \]

1.3 Components of Generalized Linear Model

\[ \left\{\begin{array}{ccc} y_i\vert x_i&\sim&f(y_i\vert{\theta}_i,\phi)\\\\ \text{E}\left[ y_i\vert \mathbf{x}_i\right]&=&\mu_i\\\\ g(\mu_i)&=&\eta(\mathbf{x}_i)\\\\ \eta(\mathbf{x}_i)&=&\mathbf{x}_i^T\boldsymbol{\beta} \end{array}\right., \] with \(g(.)\) the link function, e.g. 

  • \(g(.)=.\) : identity link for Normal distribution
  • \(g(.)=\log(.)\) : canonical link for Poisson distribution
  • \(g(.)=\text{logit}(.)=\log\left[\frac{(.)}{(1-.)}\right]\) : canonical link for Bernouilli distribution.

1.3.1 Poisson GLM

\[\left\{\begin{array}{lcr} y_i &\sim& Poisson(\mu_i)\\ E[y_i]&=&\mu_i\\ \log(\mu_i)&=&\eta_i\\ \eta_i&=&\mathbf{x}_i\boldsymbol{\beta} \end{array}\right.\]

1.4 Likelihood

We start from a sample, and consider it as fixed and known.

  • In particular we do NOT consider the sample observations as random variables.

  • Therefore we write the observed sample as \(y_i, . . . , y_n\)

  • The theory is based on the likelihood function, which can be interpreted as a measure for the probability that the given sample is observed as a realisation of a sequence of random variables \(Y_1, \ldots Y_n\)

  • The random variables \(Y_i\) are characterized by a distribution or density function which has typically unknown parameters, e.g. a Poisson distribution \(f(Y_i)\sim \text{Poisson}(\theta_i)\).

  • When the subjects are mutually independent the joint likelihood to observe \(y_1, \ldots, y_n\) equals \[\prod\limits_{i=1}^n f(y_i,\theta_i,\phi)\]

  • The densities are actually also a function of the parameters \(\theta_i, \phi\). To stress this, we indicated that in the density formulation.

  • The likelihood function is a function of all parameters

\[ L(\boldsymbol{\theta},\phi\vert \boldsymbol{y})=\prod\limits_{i=1}^n f(y_i,\theta_i,\phi) \]

  • The log-likelihood function is often used, which is defined as \[ l(\boldsymbol{\theta},\phi\vert \boldsymbol{y}) = \log L(\boldsymbol{\theta},\phi\vert \boldsymbol{y}) = \sum\limits_{i=1}^n \log f(y_i,\theta_i,\phi) \]

1.4.1 Poisson Example

The log-likelihood for our simulated dataset given the real model parameters is:

For one observation: \[l(\mu_i \vert y_i) = y_i \log \mu_i - \mu_i - \log y_i!\]

  • Verify in R.
muTrue <- exp(etaTrue)
loglikPois <- dpois(y,muTrue,log = TRUE)
logLikSelf <- y*log(muTrue) - muTrue - lfactorial(y)
qplot(loglikPois,logLikSelf) + theme_bw() + geom_abline(intercept = 0, slope=1)

The log-likelihood can also be written in terms of the canonical model parameters \(\theta\)

\[l(\mu_i \vert y_i) = y_i \theta_i - e^{\theta_i} - \log y_i!\]

  • Note that \(\theta_i = \eta_i\). The canonical parameter for the poisson equals the linear predictor! \[\theta_i=\eta_i=\mathbf{x}_i^t\boldsymbol{\beta}\]

Log-likelihood for all observations, given that they are independent: \[l(\boldsymbol{\mu} \vert \mathbf{y}) = \sum\limits_{i=1}^n \left\{ y_i \theta_i - e^{\theta_i} - \log y_i!\right\}\]

  • Calculate in R:
dpois(y,lambda = muTrue, log = TRUE) %>% 
  sum()
## [1] -260.485

1.4.2 Properties of the Log-Likelihood

\[ l(\theta_i,\phi\vert y_i)=\left\{ \frac{y_i\theta_i- b(\theta_i)}{a(\phi)}+c(y_i,\phi)\right\} \]

  • \(E[y_i]=\mu_i=b^\prime(\theta_i)\)
  • \(\text{var}[y_i]=b^{\prime\prime}(\theta_i) a(\phi)\)

Note that,

  • Variance \(\text{var}[y_i]\) depends on mean!
  • Often there is no dispersion parameter e.g. Bernouilli: \(\text{var}[y_i]=\mu_i(1-\mu_i)\),

1.4.3 Poisson Example

  • Canonical model parameter \(\theta_i=\log{\mu_i}\).
  • \(b(\theta_i) = \exp(\theta_i)\)
  • \(c(y_i,\phi) = - \log(y_i!)\)
  • \(\phi = 1\)
  • \(a(\phi)= 1\)
  • \(\mu_i =\frac{\partial b(\theta_i)}{\partial \theta_i}= \frac{\partial \exp(\theta_i)}{\partial \theta_i}=\exp(\theta_i)\)
  • \(\text{Var}\left[y_i \right]= a(\phi)\frac{\partial^2 b(\theta_i)}{(\partial \theta_i)^2}= \frac{\partial^2 \exp(\theta_i)}{\partial \theta_i^2}=\exp(\theta_i)\).
  • Mean is equal to variance for Poisson!

1.5 Parameter Estimation: Maximum Likelihood

Choose the parameters \(\boldsymbol{\beta}\) so that the likelihood to observe the sample under the statistical model is maximized.

It is easier and equivalent to maximize the log-likihood

\[\text{argmax}_{\boldsymbol{\beta}} l(\boldsymbol{\mu} \vert \mathbf{y})\]

\[ \frac{\partial l(\boldsymbol{\mu} \vert \mathbf{y})}{ \partial{\boldsymbol{\beta}}}=0 \]

\(\frac{\partial l(\boldsymbol{\mu} \vert \mathbf{y})}{ \partial{\boldsymbol{\beta}}}\) is also referred to as the score function.

1.5.1 Score function

\[ S_i(\theta_i)= \frac{\partial l(\theta_i,\phi\vert y_i)}{\partial \theta_i} \]

\[ S_i(\theta_i)= \frac{\partial l(\theta_i,\phi\vert y_i)}{\partial \theta_i}=\frac{y_i - \mu_i}{a(\phi)} \]

when canonical link function is used:

  • \(\mu_i=b^\prime(\theta_i)\)

Regression (chain rule and \(i=1,\ldots,n\) i.i.d observations)

\[\begin{eqnarray*} S(\boldsymbol{\beta})&=&\frac{\partial \sum\limits_{i=1}^n \left\{ \frac{y_i \theta_i - b(\theta_i)}{a(\phi)}+c(y_i,\phi)\right\}}{\partial \beta}\\ &=&\sum\limits_{i=1}^n\frac{\partial \left\{ \frac{y_i \theta_i - b(\theta_i)}{a(\phi)}+c(y_i,\phi)\right\}}{\partial \theta}\frac{\partial \theta}{\partial\mu}\frac{\partial\mu}{\partial\eta}\frac{\partial\eta}{\partial\beta}\\ &=&\sum\limits_{i=1}^n\frac{y_i-\mu_i}{a(\phi)}\frac{1}{b^{\prime\prime}(\theta)}\frac{\partial\mu}{\partial\eta}\mathbf{x}^t\\ &=&\mathbf{X}^T\mathbf{A}\left(\mathbf{y}-\boldsymbol{\mu}\right) \end{eqnarray*}\]

  • \(\boldsymbol{A}\) is a diagonal matrix: \(a_{ii}=\left(\text{var}[y_i]\frac{\partial \eta_i}{\partial \mu_i}\right)^{-1}\), \(\boldsymbol{y}=[y_1,\ldots, y_n]^T\), \(\boldsymbol{\mu}=[\mu_1,\ldots,\mu_n]^T\)

1.5.1.1 Poisson Example

For poisson data: \(a(\phi)b^{\prime\prime}(\theta)=\mu\) and \(\frac{\partial \mu}{\partial \eta}=\mu\). So \(\mathbf{A}=\mathbf{I}\) and

\[ S(\beta)=\mathbf{X}^T \left\{\mathbf{Y}-\boldsymbol{\mu}\right\}\]

1.5.2 Solve Score Equations

Find parameter estimator \(\hat{\boldsymbol{\beta}}\) so that \[ S(\boldsymbol{\beta}) = \mathbf{0}\] \[\mathbf{X}^T\mathbf{A}\left(\mathbf{y}-\boldsymbol{\mu}\right) =\mathbf{0}\] Problem! Non linear in \(\beta\) due to

  • link function: \(\boldsymbol{\mu}=h^{-1}(\eta)\)
  • \(a_{ii}=\left(\text{var}[y_i]\frac{\partial \eta_i}{\partial \mu_i}\right)^{-1}\)

\(\rightarrow\) Find roots of score equation by using Newton-Raphson method.

1.5.3 Newton-Raphson

Newton-Raphson algorithm: The algorithm starts at an initial guess bk for the root of the score function S. It walks along the tangent line of the score function in S(bk)  to move into the direction where the score function becomes zero (and where the log-likelihood function is maximal). The new estimate of the root is taken at bk+1 where the tangent line becomes zero. It then calculates the score for the updated parameter estimator and the whole process is repeated until the root is found.

Newton-Raphson algorithm: The algorithm starts at an initial guess bk for the root of the score function S. It walks along the tangent line of the score function in S(bk) to move into the direction where the score function becomes zero (and where the log-likelihood function is maximal). The new estimate of the root is taken at bk+1 where the tangent line becomes zero. It then calculates the score for the updated parameter estimator and the whole process is repeated until the root is found.

Newton Raphson algorithm to find the root of the score function.

  1. Choose initial parameter estimate \(\boldsymbol{\beta}^k=\boldsymbol{\beta}^0\)
  2. Calculate score \(S(\boldsymbol{\beta})\vert_{\boldsymbol{\beta}=\boldsymbol{\beta}^k}\)
  3. Calculate derivative of the function for which you want to calculate the roots
  4. Walk along first derivative until line (plane) of the derivative crosses zero
  5. Update the betas \(\boldsymbol{\beta}^{k+1}\)
  6. Iterate from step 2 - 5 until convergence.

1.5.3.1 Derivative of Score

We have to implement an interative algorithm for optimisation. To make things tractable we will act as if \(\mathbf{A}\) is known and fix it using the current values of \(\boldsymbol{\beta}^k\). Note, that for Poisson regression \(\mathbf{A}=\mathbf{I}\).

\[\begin{eqnarray*} \frac{\partial S(\boldsymbol{\beta})}{\partial \boldsymbol{\beta}} &=& \frac{ \mathbf{X}^T\mathbf{A} \left\{\mathbf{Y}-\boldsymbol{\mu}\right\}}{\partial \boldsymbol{\beta}}\\ &=& - \mathbf{X}^T \mathbf{A}\left[ \begin{array}{cccc} \frac{\partial \mu_1}{\partial \eta_1} &0&\ldots&0\\ 0&\frac{\partial \mu_2}{\partial \eta_2} &\ldots&0\\ \vdots&\vdots&\vdots&\vdots\\ 0&0&\ldots& \frac{\partial \mu_n}{\partial \eta_n}\\ \end{array}\right] \frac{\partial \boldsymbol{\eta}}{\partial \boldsymbol{\beta}}\\ &=&-\mathbf{X}^T\mathbf{WX} \end{eqnarray*}\]

1.5.3.2 Define equation of Tangent Line (Plane)

  • We know two points of the tangent plane \((\boldsymbol{\beta}^k,S(\boldsymbol{\beta}^k))\) and \((\boldsymbol{\beta}^{k+1},0)\)

  • We know the direction of the plane \(S^\prime(\boldsymbol{\beta})=\frac{\partial S(\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}\)

  • Equation of Plane: \[S(\boldsymbol{\beta})={\alpha}_0+S^\prime\vert_{\boldsymbol{\beta}^k} \boldsymbol{\beta}\]

  • Get \(\boldsymbol{\beta}_{k+1}\) \[\begin{eqnarray*} \mathbf{0}&=&{\alpha}_0+S^\prime\vert_{\boldsymbol{\beta}^{k}} \boldsymbol{\beta}^{k+1}\\ \boldsymbol{\beta}^{k+1}&=&-\left(S^{\prime}\vert_{\boldsymbol{\beta}^{k}}\right)^{-1}{\alpha}_0\\ \end{eqnarray*}\]

  • Get \({\alpha}_0\) \[\begin{eqnarray*} S(\boldsymbol{\beta}^k)&=&\boldsymbol{\alpha}_0+S^\prime\vert_{\boldsymbol{\beta}^k} \boldsymbol{\beta}^k\\ {\alpha}_0&=&-S^\prime\vert_{\boldsymbol{\beta}^k} \boldsymbol{\beta}^k + S(\boldsymbol{\beta}^k)\\ \end{eqnarray*}\]

  • Get \(\boldsymbol{\beta}_{k+1}\)

\[\begin{eqnarray*} \boldsymbol{\beta}^{k+1}&=&\boldsymbol{\beta}^k-\left(S^{\prime}\vert_{\boldsymbol{\beta}^{k}}\right)^{-1}S(\boldsymbol{\beta}^k)\\ \boldsymbol{\beta}^{k+1}&=&\boldsymbol{\beta}^k+ \left(\mathbf{X}^T\mathbf{WX}\right)^{-1} S(\boldsymbol{\beta}^k) \end{eqnarray*}\]

With \(J(\boldsymbol{\beta})=I(\boldsymbol{\beta})=\mathbf{X}^T\mathbf{WX}\) the Fisher information matrix.

1.5.4 Fisher Scoring

Because we use the canonical model parameters the observed Fisher information matrix equals the expected Fisher information matrix \(J(\boldsymbol{\beta})=I(\boldsymbol{\beta})\). Indeed, the observed Fisher information matrix is not depending on the observations, but only on the design and the variance of the data (via the weights).

Hence, Newton-Raphson is equivalent to Fisher scoring when the canonical link function is used.

Note, that the Fisher matrix, minus second derivative (or hessian) of the likelihood to the model parameters, is also the inverse of the variance covariance matrix of the model parameters. It is thus related to the precision.

1.5.5 Iteratively Reweighted Least Squares (IRLS).

We can rewrite Newton Raphson or Fisher scoring as IRLS.

\[\begin{eqnarray*} \boldsymbol{\beta}^{k+1}&=&\boldsymbol{\beta}^k+ \left(\mathbf{X}^T\mathbf{WX}\right)^{-1} S(\boldsymbol{\beta}^k)\\ \boldsymbol{\beta}^{k+1}&=&\boldsymbol{\beta}^k+ \left(\mathbf{X}^T\mathbf{WX}\right)^{-1} \mathbf{X}^T\mathbf{A} \left(\mathbf{Y}-\boldsymbol{\mu}\right)\\ \boldsymbol{\beta}^{k+1}&=& \left(\mathbf{X}^T\mathbf{WX}\right)^{-1}\mathbf{X}^T\mathbf{WX}\boldsymbol{\beta}^k+ \left(\boldsymbol{X}^T\mathbf{WX}\right)^{-1} \mathbf{X}^T \mathbf{W}\frac{\partial \eta}{\partial \mu} \left(\mathbf{Y}-\boldsymbol{\mu}\right)\\ \boldsymbol{\beta}^{k+1}&=& \left(\mathbf{X}^T\mathbf{WX}\right)^{-1}\mathbf{X}^T\mathbf{W} \left[\mathbf{X}\boldsymbol{\beta}^k + \frac{\partial \eta}{\partial \mu} \left(\mathbf{Y}-\boldsymbol{\mu}\right) \right]\\ \boldsymbol{\beta}^{k+1}&=& \left(\mathbf{X}^T\mathbf{WX}\right)^{-1}\mathbf{X}^T\mathbf{Wz} \end{eqnarray*}\]

with \(\mathbf{z}=\left[\mathbf{X}\boldsymbol{\beta}^k + \frac{\partial \eta}{\partial \mu} \left(\mathbf{Y}-\boldsymbol{\mu}\right)\right]\)

So we can fit the model by performing iterative regressions of the pseudo data \(\mathbf{z}\) on \(\mathbf{X}\). In each iteration we will update \(\mathbf{z}\), the weights \(\mathbf{W}\) and the model parameters.

For Poisson data

  • \(\frac{\partial \eta}{\partial \mu}=\frac{\partial\log \mu}{\partial\mu}=\frac{1}{\mu}=\exp(-\eta)\)
  • \(\mathbf{W}=\mathbf{A}\frac{\partial{\mu}}{{\partial \eta}}\) is a diagonal matrix with \([\frac{\partial{\mu_i}}{{\partial \eta_i}}]_{ii}=[\mu_i]_{ii}=[\exp(\eta_i)]_{ii}\) on its diagonal elements.

1.5.6 Variance-Covariance Matrix of Mean Model Parameters?

In the IRWLS algorithm, the data is weighted according to the variance of \(\mathbf{Y}\). We correct for the fact that the data are heteroscedastic.

Count data have a mean variance relation (e.g. in Poisson case \(\text{E}\left[Y \right]=\text{var}\left[Y \right]=\mu\)). The IRWLS also corrects for the scale parameter \(\phi\) in \(\mathbf{W}\). (Note that the scale parameter for Poisson is \(\phi=1\)).

So IRWLS the variance-covariance matrix for the model parameter equals \[\mathbf{\Sigma}_{\hat\beta}=\left(\mathbf{X}^T\mathbf{WX}\right)^{-1}.\]

Note, that the Fisher Information Matrix equals the inverse of the variance-covariance matrix of the experiment. The larger the Fisher Information Matrix the more information we have on the experiment to estimate the model parameters. FIM \(\uparrow\), precision \(\uparrow\), \(\text{SE}\downarrow\)

1.6 Poisson Example

1.6.1 Initial Estimate

This is a very poor initial estimate used to illustrate the algorithm. Otherwise convergence for this simple example is way too quick

iteration=0
betas<-c(log(mean(y)),0,0)
plot(betasTrue,
     ylab=expression(beta),
     ylim=c(0,4),
     pch=19,
     type="b", 
     main=paste0("likelihood real beta=",
                 round(sum(dpois(y,exp(etaTrue),log=TRUE)),1),"\nlikelihood fit=", round(sum(dpois(y,exp(xhlp%*%betas),log=TRUE)),1))
)
lines(betas,type="b",lty=2 )

1.6.2 Iteratively Reweighted Least Squares

1.6.2.1 Pseudo Data

\[z_i= \eta_i + \frac{\partial \eta_i}{\partial \mu_i}(y_i -\mu_i)\] \[z_i= \eta_i + e^{-\eta_i} y_i -1\]

1.6.2.2 Weight Matrix?

\[[w_{ii}]= var_{y_i}^{-1} \left(\frac{\partial \mu}{\partial \eta}\right)^2\] \[[w_{ii}]= e^{\eta_i}\]

1.6.2.3 Run Update Step Multiple Times

First 3 times (colors are black 0, red iteration 1, green iteration 2, blue iteration 3)

plot(betasTrue,ylab=expression(beta),ylim=c(0,4),pch=19,type="b")
lines(betas,type="b",lty=2)

cat("\nlikelihood TRUE=", round(sum(dpois(y,exp(xhlp%*%betasTrue),log=TRUE)),1))
## 
## likelihood TRUE= -260.5
cat("\nlikelihood initial fit=", round(sum(dpois(y,exp(xhlp%*%betas),log=TRUE)),1))
## 
## likelihood initial fit= -2891.5
#Calculate current eta
eta<-xhlp%*%betas

iteration=0
for (i in 1:3)
{
#start IRLS UPDATE STEP
iteration=iteration+1
#calculate pseudo data based on current betas
z=eta+exp(-eta)*(y-exp(eta))
#calculate new weights: diagonal elements
w<-c(exp(eta))

#update betas
lmUpdate<-lm(z~-1+xhlp,weight=w)
#eta<-xhlp%*%betas
eta<-lmUpdate$fitted
betas<-lmUpdate$coef
lines(betas,type="b",col=iteration+1,pch=iteration,lty=2)
cat("\nlikelihood current fit=", round(sum(dpois(y,exp(xhlp%*%betas),log=TRUE)),1))
}

## 
## likelihood current fit= -1883.9
## likelihood current fit= -431.8
## likelihood current fit= -261.7

1.7 Comparison with GLM Function

1.7.1 Smarter Initialisation

z<-log(y+.5)
betas<-lm(z~-1+xhlp)$coef
plot(betasTrue,ylab=expression(beta),ylim=c(0,4),pch=19,type="b")
lines(betas,col=2,type="b",lty=2)

#calculate current eta
eta<-xhlp%*%betas

cat("\nlikelihood TRUE=", round(sum(dpois(y,exp(xhlp%*%betasTrue),log=TRUE)),1))
## 
## likelihood TRUE= -260.5
cat("\nlikelihood initial fit=", round(sum(dpois(y,exp(xhlp%*%betas),log=TRUE)),1))
## 
## likelihood initial fit= -263.8

1.7.2 Evaluation Stopping Criterion

  • Residual deviance: Is 2 log of LR between best possible fit and current fit \[LR=\frac{L_\text{best}}{L_\text{current}}\] \[D=2 (\log L_\text{best}- \log L_\text{current})\] \[D=2 (l_\text{best}-l_\text{current})\]
  • Best fit: \(\mu=y\)
  • Optimal poisson: \[ l_\text{best}=\sum\left[y_i \log(y_i) - y_i - \log\left(y_i!\right)\right]\]
  • Current fit \[ l_\text{current}=\sum \left[y_i \eta_i -e^{\eta_i} - log\left(y_i!\right)\right]\]
  • Deviance D: \[D = 2 \sum \left[ y_i log(y_i) - y_i \eta_i - (y_i -e^{\eta_i})\right]\]
  • Problem to calculate it if y=0 but by apply l’Hopital’s rule we know \[\lim_{y_i \to 0} y_i \log(y_i) =0\]
ylogy<-function(y)
{
return(ifelse(y==0,rep(0,length(y)),y*log(y)))
}

deviance<-2*sum(ylogy(y)-y*eta-(y-exp(eta)))

devianceOld<-1e30

1.7.3 Run Update Step until Convergence

plot(betasTrue,ylab=expression(beta),ylim=c(0,4),pch=19,type="b")
lines(betas,type="b",lty=2)

tol<-1e-6
iteration=0
while(((devianceOld-deviance)/devianceOld)>tol)
{
#start IRLS UPDATE STEP
iteration=iteration+1
#calculate pseudo data based on current betas
z=eta+exp(-eta)*(y-exp(eta))
#calculate new weights: diagonal elements
w<-c(exp(eta))

#update betas
lmUpdate<-lm(z~-1+xhlp,weight=w)
#eta<-xhlp%*%betas
eta<-lmUpdate$fitted
betas<-lmUpdate$coef
lines(betas,type="b",col=iteration+1,pch=iteration,lty=2)

#criterion for convergence
devianceOld<-deviance
deviance<-2*sum(ylogy(y)-y*eta-(y-exp(eta)))
cat("iteration",iteration,"Deviance Old",devianceOld,"Deviance", deviance,"\n")
}

## iteration 1 Deviance Old 129.1127 Deviance 114.3748 
## iteration 2 Deviance Old 114.3748 Deviance 114.3374 
## iteration 3 Deviance Old 114.3374 Deviance 114.3374

1.7.4 Variance \(\beta\)?

\[\Sigma_{\beta}=\left(\mathbf{X}^T\mathbf{W} \mathbf{X}\right)^{-1}\]

varBeta=solve(t(xhlp)%*%diag(w)%*%xhlp)

1.7.5 Comparison with GLM fit

Use -1 because intercept is already in xhlp

glmfit=glm(y~-1+xhlp,family=poisson)
comp=data.frame(glmfit=c(glmfit$deviance,glmfit$coef,summary(glmfit)$coef[,2]),ourFit=c(deviance,betas,sqrt(diag(varBeta))))
row.names(comp)=c("deviance",paste("beta",1:3,sep=""),paste("se",1:3,sep=""))
comp
glmfit ourFit
deviance 114.3373950 114.3373950
beta1 1.9680569 1.9680569
beta2 0.7613664 0.7613664
beta3 1.2333003 1.2333003
se1 0.0381438 0.0381438
se2 0.0189121 0.0189120
se3 0.0255667 0.0255666

1.8 Hypothesis testing: Large sample theory

1.8.1 Wald test

  • Follows immediately from the information matrix for generalized linear models \[I(\boldsymbol{\beta}) = \mathbf{X}^T\mathbf{WX}\]

so large sample distribution of the maximum likelihood estimator \(\hat{\boldsymbol{\beta}}\) is multivariate normal \[ \hat{\boldsymbol{\beta}} \sim MVN\left[\boldsymbol{\beta},\left(\mathbf{X}^T\mathbf{WX}\right)^{-1}\right] \]

We can perform a Wald Test for a single model parameter

\[ W = \frac{\hat\beta_m}{\hat{\text{se}}_{\hat\beta_m}} \approx N(0,1)\vert H_0 \] to test for

\[ H_0: \beta_p=0 \leftrightarrow H_1:\beta_p\neq0 \]

Again, we can also assess contrasts! Indeed, linear combinations of model parameter estimators also follow a normal distribution.

\[ \mathbf{L}^T\hat{\boldsymbol{\beta}} \sim N\left[\mathbf{L}^T\boldsymbol{\beta},\mathbf{L}^T\hat{\boldsymbol{\Sigma}}_{\hat{\boldsymbol{\beta}}}\mathbf{L}\right] \]

With \(\mathbf{L}\) a vector for a single contrast.

\[ W = \frac{\mathbf{L}^T\hat{\boldsymbol{\beta}}}{\hat{\text{se}}_{\mathbf{L}^T\hat{\boldsymbol{\beta}}}} \approx N(0,1)\vert H_0 \] testing for \[ H_0: \mathbf{L}^T\boldsymbol{\beta}=0 \leftrightarrow H_1:\mathbf{L}^T\boldsymbol{\beta}\neq0 \]

We can also test for multiple contrasts simultaneously, e.g. by assuming that multiple model parameters are zero. Suppose that \(\mathbf{L}\) is the contrast matrix that corresponds testing for \(c\) model parameters, simultaneously. Then

\[ \mathbf{L}^T\hat{\boldsymbol{\beta}} \sim MVN\left[\mathbf{L}^T\boldsymbol{\beta},\mathbf{L}^T\hat{\boldsymbol{\Sigma}}_{\hat{\boldsymbol{\beta}}}\mathbf{L}\right] \]

and

\[ W = \mathbf{L}^T\hat{\boldsymbol{\beta}}\left(\mathbf{L}^T\hat{\boldsymbol{\Sigma}}_{\hat{\boldsymbol{\beta}}}\mathbf{L}\right)^{-1}\hat{\boldsymbol{\beta}}\mathbf{L} \sim \chi^2_c\vert H_0 \] to test for

\[ H_0: \mathbf{L}^T\boldsymbol{\beta}=\mathbf{0} \leftrightarrow H_1:\mathbf{L}^T\boldsymbol{\beta}=\mathbf{0}\neq0 \]

In general, when we test for \(c\geq1\) contrasts, then the test statistic \(W∼\chi^2_r|H_0\), with \(r\) the rank of the contrast matrix.

1.8.2 Likelihood ratio test

The likelihood ratio test (LRT) measures the discrepancy in log-likelihood between our current model (sometimes also referred to as full model) and a reduced model (sometimes also referred to as null or alternative model).

The reduced model must be nested in (and therefore of lower dimension as compared to) the full model.

While adding more covariates will always explain more variability in our response variable, the LRT tests whether this is actually significant.

For example, in the example of gene differential expression between healthy versus tumoral tissue, the full model could be a GLM where the mean is modeled according to an intercept and a tissue indicator variable (healthy / tumor), while the alternative model could be a GLM with just an intercept. Indeed, if the gene is similarly expressed between healthy and tumor tissue, the log-likelihood of the alternative model will decrease only a little as compared to the full model.

As the name suggests, the likelihood ratio test assesses whether the ratio of the log-likelihoods provides sufficient evidence for a worse fit of the alternative versus full model

\[ \lambda=2\left[l(\hat{\boldsymbol{\beta}}_\text{full})-2l(\hat{\boldsymbol{\beta}}_\text{0})\right] \]

Asymptotically, under the null hypothesis it can be shown that

\[ L \sim \chi_c^2 | H_0, \] with \(c\) the number of parameters dropped in the alternative model versus the full model.

Let \(\mathbf{C}\) denote the \(c \times p\) contrast matrix denoting the contrast for the parameters being dropped, the null and alternative hypothesis are as in the Wald test setting: \[ H_0: \mathbf{C} \beta = 0\] \[ H_1: \mathbf{C} \beta \ne 0\]

  • It is important to keep in mind that standard statistical inference theory in GLMs works asymptotically in terms of the sample size.

Thus we need many data points in order for the theory to hold in practice. In order for the \(p\)-values to be correct, our parametric (distributional) assumptions as well as the independence assumption, must also hold.

  • In bulk RNA-seq, we are often working with a limited number of samples and so we typically do not expect asymptotic theory to hold yet. In single-cell RNA-seq, we often perform several preprocessing steps before calculating \(p\)-values for each gene and so we may be ‘using the data multiple times’. Rather than attaching strong probabilistic interpretations to the \(p\)-values, we therefore advice to view the \(p\)-values simply as useful numerical summaries for ranking the genes for further inspection in genomics applications.

3 EdgeR - Quasi-Likelihood

Lund et al. 2012

For quasi-likelihood we do not specify the full distribution, only the first two moments: the mean and variance.

\[ \left\{ \begin{array}{lcl} E[y_{ig}\vert \mathbf{x}_{ig}]&=&\mu_{ig}\\ log(\mu_{ig})&=&\eta_{ig}\\ \eta_{ig}&=& \mathbf{x}_{i}^T\boldsymbol{\beta}+ \log N_i\\ \text{Var}[y_{ig}\vert \mathbf{x}_{ig}]&=&\sigma^2_g\left(\mu_{ig}+\phi\mu_{ig}^2\right) \end{array}\right. \]

We will look-up the details in the paper.

4 Limma - Voom

Law et al. (2013). Genome Biology

  • Count models vs transformation: Poisson counts, \(\sqrt(y)\) stabilises the variance, insufficient for negative binomial. Log transformation: the transformed data are still heteroscedastic.\(\rightarrow\) limma-voom
  • Use normalized log-cpm Limma pipeline for sequencing

Problem: counts have a mean variance relationship: heteroscedastic

How do we deal with heteroscedasticity in traditional linear models?

Two stage approach:

  1. Stage I
  • OLS
  • Estimate variances at each data point
  • Use variances as weights: \(W=\text{diag}[1/\hat\sigma_i^2]\)
  1. Stage II WLS \(\text{argmin}_{\boldsymbol{\beta}} \{ (\mathbf{y}-\mathbf{X}\boldsymbol{\beta})^T\mathbf{W} (\mathbf{y}-\mathbf{X}\boldsymbol{\beta})\}\)

Port this idea to RNA-seq pipeline

5 Independent Filtering

Independent filtering is a strategy to remove features (in this case, genes) prior to the analysis. Removal of these features may lower the multiple testing correction for other genes that pass the filter. We try to remove genes that have a low power to be found statistically significant, and/or that are biologically less relevant. A common filtering strategy is to remove genes with a generally low expression, as low counts have lower relative uncertainty (hence lower statistical power), and may be considered biologically less relevant.

Implementation in edgeR.

?filterByExpr
## No documentation for 'filterByExpr' in specified packages and libraries
suppressPackageStartupMessages({
  library(limma)
  library(edgeR)
  library(DESeq2)
})

dds <- makeExampleDESeqDataSet()
simCounts <-counts(dds)
group <- dds$condition
dge <- edgeR::DGEList(simCounts)
design <- model.matrix(~group)
keep <- filterByExpr(dge, design)
table(keep)
FALSE TRUE
373 627
lib.size <- dge$samples$lib.size * dge$samples$norm.factors
cpmMinCount <- 10/median(lib.size)*1e6
summary(group)
## A B 
## 6 6
minSampSize <- min(summary(group))
minSampSize
## [1] 6
keep <- rowSums(cpm(dge) > cpmMinCount) >= minSampSize
table(keep)
FALSE TRUE
373 627
leverage <- design%*% solve(t(design)%*%design)%*%t(design) %>%diag()
1/leverage
##  1  2  3  4  5  6  7  8  9 10 11 12 
##  6  6  6  6  6  6  6  6  6  6  6  6
min(1/leverage)
## [1] 6

Independent filtering has been formalized by Bourgon et al. (2010).

Figure 1 from Bourgon *et al.* (2010).

Figure 1 from Bourgon et al. (2010).


The concept of independent filtering can be summarized as follows:

  • For each feature we calculate two statistics, \(S_F\) and \(S_T\), respectively used for two stages: filtering and testing (e.g., differential expression).
  • In order for a feature to be deemed significant, both of its statistics must be greater than some cut-off.
  • We want to control the type I error rate of the second stage (testing). But note that the second stage is conditional on the first stage, as we only test features passing the filter, and basically ignore the fact that filtering was performed. Indeed, one criticism is that computing and correcting the \(p\)-values as if filtering had not been performed may lead to overoptimistic adjusted \(p\)-values.
  • Bourgon et al. (2010) show that filtering is only appropriate (i.e., does not inflate type I error rate) if the conditional null distribution of test statistics for features passing the filter is the same as the unconditional null distribution. Therefore, filtering is appropriate if the statistic used for filtering is independent of the statistic used for testing under the null hypothesis.
Figure 2 from Bourgon *et al.* (2010).

Figure 2 from Bourgon et al. (2010).


Let’s try a couple of examples to get some intuition using simulated data.

suppressPackageStartupMessages(library(DESeq2))
set.seed(24)
dds <- DESeq2::makeExampleDESeqDataSet()
simCounts <- counts(dds)
group <- dds$condition

5.1 A Dependent Test Statistic

filterStatEffectSize <- abs(rowMeans(simCounts[,group == "A"]) - rowMeans(simCounts[,group == "B"]))
testStat <- genefilter::rowttests(simCounts, group)

## unconditional distribution
plot(density(testStat$statistic, na.rm=TRUE),
     xlab = "Test statistic",
     main = "Unconditional distribution")

## conditional distribution: very different!
mean(filterStatEffectSize > 1)
## [1] 0.792
hist(filterStatEffectSize, breaks=40)
abline(v=1, col="red")

keepEffectSize <- filterStatEffectSize > 1
plot(density(testStat$statistic[keepEffectSize], na.rm=TRUE),
     xlab = "Test statistic",
     main = "Conditional distribution")

5.2 An Independent Test Statistic

filterStatGlobalMean <- rowMeans(simCounts)
mean(filterStatGlobalMean > 5) # we remove a similar fraction
## [1] 0.771
keepGlobalMean <- filterStatGlobalMean > 5

## unconditional distribution
plot(density(testStat$statistic, na.rm=TRUE),
     xlab = "Test statistic",
     main = "Unconditional distribution")

## conditional distribution: the same.
plot(density(testStat$statistic[keepGlobalMean], na.rm=TRUE),
     xlab = "Test statistic",
     main = "Conditional distribution")

6 Normalization

Normalization is necessary to correct for several sources of technical variation:

  • Differences in sequencing depth between samples. Some samples get sequenced deeper in the sense that they consist of more (mapped) reads and therefore can be considered to contain a higher amount of information, which we should be taking into account. In addition, if a sample is sequenced deeper, it is natural that the counts for each gene will be higher, jeopardizing a direct comparison of the expression counts.
  • Differences in RNA population composition between samples. As an extreme example, suppose that two samples have been sequenced to the exact same depth. One sample is contaminated and has a very high concentration of the contaminant cDNA being sequenced, but otherwise the two samples are identical. Since the contaminant will be taking up a significant proportion of the reads being sequenced, the counts will not be directly comparable between the samples. Hence, we may also want to correct for differences in the composition of the RNA population of the samples.
  • Other technical variation such as sample-specific GC-content or transcript length effects may also be accounted for.
data("parathyroidGenesSE", package="parathyroidSE")
se1 <- parathyroidGenesSE
rm(parathyroidGenesSE)

colData(se1) %>% 
  as.data.frame() %>% 
  filter(duplicated(experiment)) 
run experiment patient treatment time submission study sample
SRR479061 SRX140511 2 DPN 24h SRA051611 SRP012167 SRS308873
SRR479064 SRX140513 2 OHT 24h SRA051611 SRP012167 SRS308875
SRR479075 SRX140523 4 DPN 48h SRA051611 SRP012167 SRS308885
SRR479078 SRX140525 4 OHT 48h SRA051611 SRP012167 SRS308887

There are technical repeats in the data.

We mentioned previous lectures that we can sum over technical repeats, because techical repeats are Poisson and the sum of two Poisson variables is again Poisson.

dupExps <- colData(se1) %>% 
  as.data.frame() %>% 
  filter(duplicated(experiment))  %>% 
  pull(experiment)

counts <- assays(se1)$counts
newCounts <- counts
cd <- colData(se1)
for(ss in 1:length(dupExps)){
  # check which samples are duplicates
  relevantId <- which(colData(se1)$experiment == dupExps[ss])
  # sum counts
  newCounts[,relevantId[1]] <- rowSums(counts[,relevantId])
  # keep which columns / rows to remove.
  if(ss == 1){
    toRemove <- relevantId[2]
  } else {
    toRemove <- c(toRemove, relevantId[2])
  }
}

# remove after summing counts (otherwise IDs get mixed up)
newCounts <- newCounts[,-toRemove]
newCD <- cd[-toRemove,]

# Create new SummarizedExperiment
se <- SummarizedExperiment(assays = list("counts" = newCounts),
                            colData = newCD,
                            metadata = metadata(se1))

treatment <- colData(se)$treatment
table(treatment)
Control DPN OHT
7 8 8
qplot(colSums(assays(se)$counts)/1e6, geom="histogram", bins=10,col="black") +
  theme(legend.position = "none") +
  xlab("libsize (million reads)")

qplot(
  colData(se)$treatment:colData(se)$time,
  colSums(assays(se)$counts)/1e6,geom="boxplot"
  ) +
  xlab("treatment")+
  ylab("libsize (million reads)")

qplot(
  colData(se)$patient,
  colSums(assays(se)$counts)/1e6,geom="boxplot"
  ) +
  xlab("Patient")+
  ylab("libsize (million reads)")

ma2Samp <- function(countMx) {
stopifnot("`countMx` is not a matrix with two columns" = ncol(countMx) == 2)
A <- countMx %>% log2 %>% rowMeans
M <- countMx %>% log2 %>% apply(.,1,diff)
w <- countMx[,1]==min(countMx[,1]) | countMx[,2]==min(countMx[,2])
if (any(w)) {
            A[w] <- runif(sum(w), min = -1, max = .1)
            M[w] <- log2(countMx[w,2] + 1) - log2(countMx[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,col="red")
}

Let’s take a look at how comparable different replicates are in the Control condition at 48h in our dataset. We will investigate this using MD-plots (mean-difference plots as introduced by Dudoit et al. (2002)), also sometimes referred to as MA-plots.

ids <- which(colData(se)$treatment =="Control" & colData(se)$time == "48h")
ids
## [1]  2  8 14 19
colSums(assays(se)$counts[,ids]) / 1e6
## [1] 10.827109  6.844144  8.064268  7.701432
pairComb <- combn(
  ids, 
  m=2)
plots <- apply(pairComb,2,function(x) ma2Samp(assay(se)[,x]) + ggtitle(paste("samples",x[2],"vs", x[1])))
do.call("grid.arrange",c(plots,ncol=3))

We see clear bias for some pairwise comparisons. For example, in the first plot comparing sample 8 versus sample 2, the log fold-changes are biased downwards. This means that, on average, a gene is lower expressed in sample 8 versus sample 2. Looking at the library sizes, we can indeed see that the library size for sample 2 is about \(11×10^6\) while it is only about \(7×10^6\) for sample 8! This is a clear library size effect that we should take into account.

We can solve these issues by introducing offsets in our model.

\[ \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. \]

6.1 TMM method (default of edgeR)

Robinson and Oshlack (2010). Genome Biology

knitr::include_graphics("./figs/edgeRNormIntro.png")

  • On the plot we see a clear effect on all genes
  • Correcting for library size tends to over correct.
  • Some DE genes are highly abundant and determine the library size to a large extend

The trimmed mean of M-values (TMM) method introduced by Robinson & Oshlack (2010) is a normalization procedure that calculates a single normalization factor for each sample. As the name suggests, it is based on a trimmed mean of fold-changes (\(M\)-values) as the scaling factor. A trimmed mean is an average after removing a set of ``extreme’’ values. Specifically, TMM calculates a normalization factor \(F_i^{(r)}\) across genes \(g\) for each sample \(i\) as compared to a reference sample \(r\), \[ \log_2(F_i^{(r)}) = \frac{\sum_{g \in {\cal G}^*} w_{gi}^r M_{gi}^r}{\sum_{g \in {\cal G}^*} w_{gi}^r}, \] where \(M_{gi}^r\) represents the \(\log_2\)-fold-change of the gene expression fraction as compared to a reference sample \(r\), i.e., \[ M_{gi}^r = \log_2\left( \frac{Y_{gi} / N_i}{ Y_{gr} / N_r} \right), \] and \(w_{gi}^r\) represents a precision weight calculated as \[ w_{gi}^r = \frac{N_i - Y_{gi}}{N_i Y_{gi}} + \frac{N_r - Y_{gr}}{N_r Y_{gr}}, \] and \({\cal G}^*\) represents the set of genes after trimming those with the most extreme average expression. The weights serve to account for the fact that fold-changes for genes with lower read counts are more variable.

The procedure only takes genes into account where both \(Y_{gi}>0\) and \(Y_{gr}>0\). By default, TMM trims genes with the \(30\%\) most extreme \(M\)-values and \(5\%\) most extreme average gene expression, and chooses as reference \(r\) the sample whose upper-quartile is closest to the across-sample average upper-quartile. The normalized counts are then given by \(\tilde{Y}_{gi} = Y_{gi} / N_i^s\), where \[N_i^s = \frac{N_i F_i^{(r)}}{\sum_{i=1}^n N_i F_i^{(r)}/n}.\]

TMM normalization may be performed from the calcNormFactors function implemented in edgeR:

dge <- edgeR::calcNormFactors(se)
dge$samples #normalization factors added to colData
group lib.size norm.factors run experiment patient treatment time submission study sample
Sample1 1 9102683 0.9782830 SRR479052 SRX140503 1 Control 24h SRA051611 SRP012167 SRS308865
Sample2 1 10827109 0.9728700 SRR479053 SRX140504 1 Control 48h SRA051611 SRP012167 SRS308866
Sample3 1 5217761 0.9898593 SRR479054 SRX140505 1 DPN 24h SRA051611 SRP012167 SRS308867
Sample4 1 9706035 0.9930169 SRR479055 SRX140506 1 DPN 48h SRA051611 SRP012167 SRS308868
Sample5 1 5700022 0.9850867 SRR479056 SRX140507 1 OHT 24h SRA051611 SRP012167 SRS308869
Sample6 1 7854568 0.9897270 SRR479057 SRX140508 1 OHT 48h SRA051611 SRP012167 SRS308870
Sample7 1 8610014 0.9266581 SRR479058 SRX140509 2 Control 24h SRA051611 SRP012167 SRS308871
Sample8 1 6844144 0.9544240 SRR479059 SRX140510 2 Control 48h SRA051611 SRP012167 SRS308872
Sample9 1 24584280 0.9188545 SRR479060 SRX140511 2 DPN 24h SRA051611 SRP012167 SRS308873
Sample10 1 8267977 0.9398000 SRR479062 SRX140512 2 DPN 48h SRA051611 SRP012167 SRS308874
Sample11 1 23590411 0.9096695 SRR479063 SRX140513 2 OHT 24h SRA051611 SRP012167 SRS308875
Sample12 1 8247122 0.9369050 SRR479065 SRX140514 2 OHT 48h SRA051611 SRP012167 SRS308876
Sample13 1 7341000 1.0668032 SRR479066 SRX140515 3 Control 24h SRA051611 SRP012167 SRS308877
Sample14 1 8064268 1.0552688 SRR479067 SRX140516 3 Control 48h SRA051611 SRP012167 SRS308878
Sample15 1 12481958 1.0461698 SRR479068 SRX140517 3 DPN 24h SRA051611 SRP012167 SRS308879
Sample16 1 16310090 1.0260056 SRR479069 SRX140518 3 DPN 48h SRA051611 SRP012167 SRS308880
Sample17 1 23697329 1.0268459 SRR479070 SRX140519 3 OHT 24h SRA051611 SRP012167 SRS308881
Sample18 1 7642648 1.0409451 SRR479071 SRX140520 3 OHT 48h SRA051611 SRP012167 SRS308882
Sample19 1 7701432 1.0559132 SRR479072 SRX140521 4 Control 48h SRA051611 SRP012167 SRS308883
Sample20 1 7135899 1.0675040 SRR479073 SRX140522 4 DPN 24h SRA051611 SRP012167 SRS308884
Sample21 1 13818393 1.0327004 SRR479074 SRX140523 4 DPN 48h SRA051611 SRP012167 SRS308885
Sample22 1 6099942 1.0890994 SRR479076 SRX140524 4 OHT 24h SRA051611 SRP012167 SRS308886
Sample23 1 15825211 1.0286470 SRR479077 SRX140525 4 OHT 48h SRA051611 SRP012167 SRS308887

Let’s check how our MD-plots look like after normalization. Note that, we can rewrite the GLM as \[ \log\left( \frac{\mu_{gi}}{N_i^s} \right) = \mathbf{X}_i^T \beta_g \] and so \(\frac{\mu_{gi}}{N_i^s}\) can be considered as an ‘offset-corrected count’.

We see that all MD-plots are now nicely centered around a log-fold-change of zero!

## normalize
effLibSize <- dge$samples$lib.size * dge$samples$norm.factors
normCountTMM <- sweep(assays(se)$counts, 2, FUN="/", effLibSize)

plotsNorm <- apply(pairComb,2,function(x) ma2Samp(normCountTMM[,x]) + ggtitle(paste("samples",x[2],"vs", x[1])))
do.call("grid.arrange",c(plots,ncol=3))

6.2 Median-of-Ratios Method (default of DESeq2)

The median-of-ratios method is used in DESeq2 as described in Love et al. (2014). It assumes that the expected value \(\mu_{gi} = E(Y_{gi})\) is proportional to the true expression of the gene, \(q_{gi}\), scaled by a normalization factor \(s_{i}\) for each sample, \[ \mu_{gi} = s_{i}q_{gi}. \]

The normalization factor \(s_{i}\) is then estimated using the median-of-ratios method compared to a synthetic reference sample \(r\) defined based on geometric means of counts across samples \[ s_i = \text{median}_{\{{g:Y^{*}_{gr} \ne 0}\}} \frac{Y_{gi}}{Y^{*}_{gr}}, \] with \[ Y^{*}_{gr} = \left( \prod_{i=1}^n Y_{gi} \right)^{1/n}. \]

We can then use the size factors \(s_i\) as offsets to the GLM.

Median-of-ratios normalization is implemented in the DESeq2 package:

dds <- DESeq2::DESeqDataSetFromMatrix(countData = assays(se)$counts,
                                      colData = colData(se),
                                      design = ~ 1) #just add intercept to showcase normalization
## converting counts to integer mode
dds <- DESeq2::estimateSizeFactors(dds)
sizeFactors(dds)
##  [1] 0.9187624 1.0644717 0.5232069 0.9826698 0.5676212 0.7807662 0.8284089
##  [8] 0.6600848 2.3955069 0.7906917 2.2784591 0.7860386 0.7807842 0.8445577
## [15] 1.3345483 1.7011973 2.5001652 0.7889061 0.8138720 0.7632306 1.4518320
## [22] 0.6553677 1.6696064

You may also want to check out the StatQuest video on DESeq2 normalization.

6.2.1 Comparing TMM with DESeq2 Normalization

We can compare the size factors for both normalizations to verify if they agree on the normalization factors. Note we need to scale the effective library sizes from edgeR to enforce a similar scale as the size factors from DESeq2. While below we are using an arithmetic mean, a geometric mean may be used as well, which will be more robust to outlying effective library sizes.

plot(effLibSize / mean(effLibSize), sizeFactors(dds),
     xlab = "edgeR size factor",
     ylab = "DESeq2 size factor")

7 Aliasing

Suppose we are working with the following experimental design on colon cancer. Studying the effect of a drug on gene expression, researchers gather RNA-seq data from four colon cancer patients and four healthy individuals. For each individual, they obtain RNA-seq data from a blood sample before as well as two weeks after taking a daily dose of the drug. The research question relates to differential expression after vs. before taking the drug, in particular whether this is different for the diseased versus healthy group (i.e., the interaction between time (before/after taking the drug) and disease status (healthy/colon cancer)).

In terms of the model matrix, we could imagine a design such as ~ patient + disease*time, where

  • disease is a binary indicator referring to colon cancer versus control sample.
  • time defines if the sample is taken before or after taking the drug.
  • patient defines the individual donor the sample comes from.

The research question could then amount to testing the disease * time interaction.

Let’s try this, by simulating random data for one gene.

set.seed(2)
# 2 samples per patient for 8 patients
patient <- factor(rep(letters[1:8], each=2)) 
# first four are healthy, next four are diseased
disease <- factor(c(rep("healthy",8), rep("cancer",8)), levels=c("healthy", "cancer")) 
# one before and one after sample for each
time <- factor(rep(c("before", "after"), 8), levels=c("before", "after")) 

table(patient, disease, time)
patient disease time Freq
a cancer after 0
before 0
healthy after 1
before 1
b cancer after 0
before 0
healthy after 1
before 1
c cancer after 0
before 0
healthy after 1
before 1
d cancer after 0
before 0
healthy after 1
before 1
e cancer after 1
before 1
healthy after 0
before 0
f cancer after 1
before 1
healthy after 0
before 0
g cancer after 1
before 1
healthy after 0
before 0
h cancer after 1
before 1
healthy after 0
before 0
## simulate data for one gene
n <- 16
y <- rpois(n = n, lambda = 50)

## fit a Poisson model
m <- glm(y ~ patient + disease*time,
         family = "poisson")
summary(m)
## 
## Call:
## glm(formula = y ~ patient + disease * time, family = "poisson")
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -1.52772  -0.43544   0.00013   0.44162   1.34650  
## 
## Coefficients: (1 not defined because of singularities)
##                         Estimate Std. Error z value Pr(>|z|)    
## (Intercept)              3.76900    0.11916  31.631   <2e-16 ***
## patientb                 0.06744    0.14999   0.450   0.6530    
## patientc                 0.06744    0.14999   0.450   0.6530    
## patientd                 0.27304    0.14310   1.908   0.0564 .  
## patiente                 0.16449    0.16224   1.014   0.3107    
## patientf                 0.02565    0.16644   0.154   0.8775    
## patientg                -0.01784    0.16785  -0.106   0.9154    
## patienth                 0.05706    0.16544   0.345   0.7302    
## diseasecancer                 NA         NA      NA       NA    
## timeafter               -0.01567    0.10220  -0.153   0.8782    
## diseasecancer:timeafter  0.12374    0.14407   0.859   0.3904    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 16.1200  on 15  degrees of freedom
## Residual deviance:  8.8417  on  6  degrees of freedom
## AIC: 120.16
## 
## Number of Fisher Scoring iterations: 4

We find that one of the coefficients is NA! This is obviously not because we’re dealing with NA values in the data as we’ve just simulated the response variable ourselves. What’s going on?

One of the parameters, in this case the parameter distinguishing cancer from healthy patients cannot be estimated as it is a linear combination of other parameters. In our case, estimating the diseased effect would use information that is already used to estimate the patient-level intercepts. In other words, once you know the patient, you immediately also know the disease status, so estimating the diseased vs healthy effect on top of the patient effect provides no additional information if we have already estimated the patient-level effects. This concept is called aliasing, and is a common technical issue in ’omics experiments with complex experimental designs.


While to understand the origin of the aliasing it is crucial to understand the relationship between the variables in the experimental design, we can also investigate it in detail using the alias function, to give us an idea.

alias(m)
## Model :
## y ~ patient + disease * time
## 
## Complete :
##               (Intercept) patientb patientc patientd patiente patientf patientg
## diseasecancer 0           0        0        0        1        1        1       
##               patienth timeafter diseasecancer:timeafter
## diseasecancer 1        0         0

We see that the effect diseasecancer is a linear combination of the patient-specific effects of the cancer patients. This makes sense!


For clarity, let’s reproduce this using our design matrix.

X <- model.matrix(~ patient + disease*time) # this is the design used in glm()

## these are indeed identical.
X[,"diseasecancer"]
##  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 
##  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1
X[,"patiente"] + X[,"patientf"] + X[,"patientg"] + X[,"patienth"]
##  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 
##  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1

Since one of our parameters is a linear combination of other parameters, it cannot be estimated simultaneously with the other parameters. In this case, we can actually drop the disease main effect from the model, since we know that it is already included in the patient effect.


We will have to carefully construct our design matrix in order to account for all important sources of variation while still allowing us to answer the research question of interest. The aliasing exploration above has made it clear we may drop the disease main effect, so let’s start by constructing this design matrix.

X <- model.matrix(~ patient + time + disease:time)

m2 <- glm(y ~ -1 + X,
         family = "poisson")
summary(m2)
## 
## Call:
## glm(formula = y ~ -1 + X, family = "poisson")
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -1.52772  -0.43544   0.00013   0.44162   1.34650  
## 
## Coefficients: (1 not defined because of singularities)
##                           Estimate Std. Error z value Pr(>|z|)    
## X(Intercept)               3.76900    0.11916  31.631   <2e-16 ***
## Xpatientb                  0.06744    0.14999   0.450   0.6530    
## Xpatientc                  0.06744    0.14999   0.450   0.6530    
## Xpatientd                  0.27304    0.14310   1.908   0.0564 .  
## Xpatiente                  0.28823    0.16077   1.793   0.0730 .  
## Xpatientf                  0.14939    0.16500   0.905   0.3653    
## Xpatientg                  0.10590    0.16643   0.636   0.5246    
## Xpatienth                  0.18081    0.16400   1.102   0.2703    
## Xtimeafter                -0.01567    0.10220  -0.153   0.8782    
## Xtimebefore:diseasecancer -0.12374    0.14407  -0.859   0.3904    
## Xtimeafter:diseasecancer        NA         NA      NA       NA    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 4489.2752  on 16  degrees of freedom
## Residual deviance:    8.8417  on  6  degrees of freedom
## AIC: 120.16
## 
## Number of Fisher Scoring iterations: 4
alias(m2)
## Model :
## y ~ -1 + X
## 
## Complete :
##                          X(Intercept) Xpatientb Xpatientc Xpatientd Xpatiente
## Xtimeafter:diseasecancer  0            0         0         0         1       
##                          Xpatientf Xpatientg Xpatienth Xtimeafter
## Xtimeafter:diseasecancer  1         1         1         0        
##                          Xtimebefore:diseasecancer
## Xtimeafter:diseasecancer -1

We are still confronted with aliasing as the model matrix contains an interaction effect timebefore:diseasecancer as well as timeafter:diseasecancer, while only the latter is relevant. Indeed, we know that we can derive the timebefore:diseasecancer effect by averaging the patient effects of the cancer patients.


X <- X[,!colnames(X) %in% "timebefore:diseasecancer"]


## fit a Poisson model
m2 <- glm(y ~ -1 + X,
         family = "poisson")
summary(m2)
## 
## Call:
## glm(formula = y ~ -1 + X, family = "poisson")
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -1.52772  -0.43544   0.00013   0.44162   1.34650  
## 
## Coefficients:
##                          Estimate Std. Error z value Pr(>|z|)    
## X(Intercept)              3.76900    0.11916  31.631   <2e-16 ***
## Xpatientb                 0.06744    0.14999   0.450   0.6530    
## Xpatientc                 0.06744    0.14999   0.450   0.6530    
## Xpatientd                 0.27304    0.14310   1.908   0.0564 .  
## Xpatiente                 0.16449    0.16224   1.014   0.3107    
## Xpatientf                 0.02565    0.16644   0.154   0.8775    
## Xpatientg                -0.01784    0.16785  -0.106   0.9154    
## Xpatienth                 0.05706    0.16544   0.345   0.7302    
## Xtimeafter               -0.01567    0.10220  -0.153   0.8782    
## Xtimeafter:diseasecancer  0.12374    0.14407   0.859   0.3904    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 4489.2752  on 16  degrees of freedom
## Residual deviance:    8.8417  on  6  degrees of freedom
## AIC: 120.16
## 
## Number of Fisher Scoring iterations: 4

We see that all coefficients can now be estimated. The timeafter effect may be interpreted as the time effect for healthy patients, while the timeafter:diseasecancer effect may be interpreted as the difference in the time effect for cancer patients as compared to healthy patients, i.e., it is the relevant interaction effect we are interested in.

LS0tCnRpdGxlOiAnU2VxdWVuY2luZzogU2VsZWN0ZWQgdGVjaG5pY2FsIHRvcGljcycKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQgJiBLb2VuIFZhbiBkZW4gQmVyZ2UiCmRhdGU6ICJMYXN0IGVkaXRlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CmFsd2F5c19hbGxvd19odG1sOiB0cnVlCmxpbmtjb2xvcjogYmx1ZQp1cmxjb2xvcjogYmx1ZSAKY2l0ZWNvbG9yOiBibHVlCmxpbmstY2l0YXRpb25zOiB5ZXMKCi0tLQoKYGBge3IgZnVuY3Rpb25zLCBpbmNsdWRlPUZBTFNFfQojIEEgZnVuY3Rpb24gZm9yIGNhcHRpb25pbmcgYW5kIHJlZmVyZW5jaW5nIGltYWdlcwpmaWcgPC0gbG9jYWwoewogICAgaSA8LSAwCiAgICByZWYgPC0gbGlzdCgpCiAgICBsaXN0KAogICAgICAgIGNhcD1mdW5jdGlvbihyZWZOYW1lLCB0ZXh0KSB7CiAgICAgICAgICAgIGkgPDwtIGkgKyAxCiAgICAgICAgICAgIHJlZltbcmVmTmFtZV1dIDw8LSBpCiAgICAgICAgICAgIHBhc3RlKCJGaWd1cmUgIiwgaSwgIjogIiwgdGV4dCwgc2VwPSIiKQogICAgICAgIH0sCiAgICAgICAgcmVmPWZ1bmN0aW9uKHJlZk5hbWUpIHsKICAgICAgICAgICAgcmVmW1tyZWZOYW1lXV0KICAgICAgICB9KQp9KQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBldmFsPVRSVUV9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShrbml0cikKICBsaWJyYXJ5KHJtYXJrZG93bikKICBsaWJyYXJ5KGdncGxvdDIpCiAgbGlicmFyeShwcmludHIpCiAgbGlicmFyeShncmlkRXh0cmEpCiAgbGlicmFyeSh0aWR5dmVyc2UpCiAgbGlicmFyeShwbG90bHkpCn0pCmBgYAoKIyBQYXJhbWV0ZXIgRXN0aW1hdGlvbiBhbmQgSW5mZXJlbmNlIGluIEdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMgCgojIyBTaW11bGF0ZSBQb2lzc29uIERhdGEKCi0gV2Ugc2ltdWxhdGUgZGF0YSBmb3IgMTAwIG9ic2VydmF0aW9ucy4KLSBDb3ZhcmlhdGVzIHggYXJlIHNpbXVsYXRlZCBmcm9tIG5vcm1hbCBkaXN0cmlidXRpb24KLSBUaGUgJFxiZXRhJCBhcmUgY2hvc2VuIGF0ICRcYmV0YV8wPTIkLCAkXGJldGFfMT0wLjgkLCAkXGJldGFfMj0xLjIkCgpgYGB7cn0Kc2V0LnNlZWQoMzAwKQp4aGxwPC1jYmluZCgxLHJub3JtKDEwMCkscm5vcm0oMTAwKSkKYmV0YXNUcnVlPC1jKDIsMC44LDEuMikKZXRhVHJ1ZTwteGhscCUqJWJldGFzVHJ1ZQp5PC1ycG9pcygxMDAsZXhwKGV0YVRydWUpKQpkYXRhLmZyYW1lKGNvZWYgPTA6MixiZXRhc1RydWU9YmV0YXNUcnVlKSAlPiUgCiAgZ2dwbG90KGFlcyh4PWNvZWYseT1iZXRhc1RydWUpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIHlsYWIoInBhcmFtZXRlciB2YWx1ZSIpICsKICB4bGFiKCJiZXRhIikgKwogIHlsaW0oMCw0KSArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDAsMSwyKSkKCgpgYGAKCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CngxIDwtIHhobHBbLDJdCngyIDwtIHhobHBbLDNdCnAxIDwtIHBsb3RfbHkoCiAgICB4ID0gfngxLAogICAgeSA9IH54MiwKICAgIHo9IH55LAogICAgbW9kZT0ibWFya2VycyIsCiAgICBzaXplPS41KSAlPiUKICBhZGRfbWFya2Vycyh0eXBlPSJzY2F0dGVyM2QiKSAlPiUKICBsYXlvdXQoCiAgICBzY2VuZSA9IGxpc3QoCiAgICAgIGFzcGVjdG1vZGU9ImN1YmUiLAogICAgICB4YXhpcyA9IGxpc3QocmFuZ2U9cmFuZ2UoeGhscFssMl0pKSksIHlheGlzID0gbGlzdChyYW5nZT1yYW5nZSh4aGxwWywzXSkpLCB6YXhpcyA9IGxpc3QocmFuZ2U9cmFuZ2UoeSkpCiAgICAgICkKcDEKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQp4MSA8LSB4aGxwWywyXQp4MiA8LSB4aGxwWywzXQpwMSA8LSBwbG90X2x5KAogICAgeCA9IH54MSwKICAgIHkgPSB+eDIsCiAgICB6PSB+IGxvZzIoeSsuNSksCiAgICBtb2RlPSJtYXJrZXJzIiwKICAgIHNpemU9LjUpICU+JQogIGFkZF9tYXJrZXJzKHR5cGU9InNjYXR0ZXIzZCIpICU+JQogIGxheW91dCgKICAgIHNjZW5lID0gbGlzdCgKICAgICAgYXNwZWN0bW9kZT0iY3ViZSIsCiAgICAgIHhheGlzID0gbGlzdChyYW5nZT1yYW5nZSh4aGxwWywyXSkpKSwgeWF4aXMgPSBsaXN0KHJhbmdlPXJhbmdlKHhobHBbLDNdKSksIHpheGlzID0gbGlzdChyYW5nZT1yYW5nZSh5KSkKICAgICAgKQpwMQpgYGAKCgojIyBFeHBvbmVudGlhbCBGYW1pbHkKCiQkCmYoeV9pXHZlcnQgXHRoZXRhX2ksXHBoaSk9XGV4cFxsZWZ0XHsgXGZyYWN7eV9pXHRoZXRhX2ktIGIoXHRoZXRhX2kpfXthKFxwaGkpfStjKHlfaSxccGhpKVxyaWdodFx9CiQkCgp3aXRoCgotICRcdGhldGFfaSQ6IGNhbm9uaWNhbCBwYXJhbWV0ZXJzCi0gJFxwaGkkOiBkaXNwZXJzaW9uIHBhcmFtZXRlcgotICRhKC4pJCwgJGIoLikkLCAkYyguKSQ6IHNwZWNpZmljIGZ1bmN0aW9ucyB0aGF0IGRlcGVuZCBvbiB0aGUgZGlzdHJpYnV0aW9uLCAKCmUuZy4gZm9yIG5vcm1hbCBkaXN0cmlidXRpb24gCgotICRccGhpPVxzaWdtYV4yJCwgCi0gJFx0aGV0YT1cbXUkLCAKLSAkYShccGhpKT1ccGhpPVxzaWdtYV4yJCwgCi0gJGIoXHRoZXRhX2kpPVx0aGV0YV9pXjIvMiQsIAotICRjKHlfaSxccGhpKT0tXGZyYWN7MX17Mn1beV4yL1xwaGkrXGxvZygyXHBpXHBoaSldJAoKIyMjIFBvaXNzb24gRGlzdHJpYnV0aW9uCgokJAp5X2kgXHNpbSBcZnJhY3tcbXVfaV57eV9pfWVeey1cbXVfaX19e3lfaSF9CiQkCgpJbiBmb3JtYXQgb2YgZXhwb25lbnRpYWwgZmFtaWx5OiAKCiQkCnlfaSBcc2ltIFxleHBcbGVmdFx7eV9pXGxvZyhcbXVfaSkgLSBcbXVfaSAtIFxsb2coeV9pISlccmlnaHRcfQokJAoKIyMgQ29tcG9uZW50cyBvZiBHZW5lcmFsaXplZCBMaW5lYXIgTW9kZWwKCiQkClxsZWZ0XHtcYmVnaW57YXJyYXl9e2NjY30KeV9pXHZlcnQgeF9pJlxzaW0mZih5X2lcdmVydHtcdGhldGF9X2ksXHBoaSlcXFxcClx0ZXh0e0V9XGxlZnRbIHlfaVx2ZXJ0IFxtYXRoYmZ7eH1faVxyaWdodF0mPSZcbXVfaVxcXFwKZyhcbXVfaSkmPSZcZXRhKFxtYXRoYmZ7eH1faSlcXFxcClxldGEoXG1hdGhiZnt4fV9pKSY9JlxtYXRoYmZ7eH1faV5UXGJvbGRzeW1ib2x7XGJldGF9ClxlbmR7YXJyYXl9XHJpZ2h0LiwKJCQKd2l0aCAkZyguKSQgdGhlIGxpbmsgZnVuY3Rpb24sIGUuZy4gCgotICRnKC4pPS4kIDogaWRlbnRpdHkgbGluayBmb3IgTm9ybWFsIGRpc3RyaWJ1dGlvbgotICRnKC4pPVxsb2coLikkIDogY2Fub25pY2FsIGxpbmsgZm9yIFBvaXNzb24gZGlzdHJpYnV0aW9uCi0gJGcoLik9XHRleHR7bG9naXR9KC4pPVxsb2dcbGVmdFtcZnJhY3soLil9eygxLS4pfVxyaWdodF0kIDogY2Fub25pY2FsIGxpbmsgZm9yIEJlcm5vdWlsbGkgZGlzdHJpYnV0aW9uLgoKIyMjIFBvaXNzb24gR0xNCgokJFxsZWZ0XHtcYmVnaW57YXJyYXl9e2xjcn0KeV9pICZcc2ltJiBQb2lzc29uKFxtdV9pKVxcCkVbeV9pXSY9JlxtdV9pXFwKXGxvZyhcbXVfaSkmPSZcZXRhX2lcXApcZXRhX2kmPSZcbWF0aGJme3h9X2lcYm9sZHN5bWJvbHtcYmV0YX0KXGVuZHthcnJheX1ccmlnaHQuJCQKCiMjIExpa2VsaWhvb2QKCldlIHN0YXJ0IGZyb20gYSBzYW1wbGUsIGFuZCBjb25zaWRlciBpdCBhcyBmaXhlZCBhbmQga25vd24uIAoKLSBJbiBwYXJ0aWN1bGFyIHdlIGRvIE5PVCBjb25zaWRlciB0aGUgc2FtcGxlIG9ic2VydmF0aW9ucyBhcyByYW5kb20gdmFyaWFibGVzLiAKCi0gVGhlcmVmb3JlIHdlIHdyaXRlIHRoZSBvYnNlcnZlZCBzYW1wbGUgYXMgJHlfaSwgLiAuIC4gLCB5X24kCgotIFRoZSB0aGVvcnkgaXMgYmFzZWQgb24gdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24sIHdoaWNoICBjYW4gYmUgaW50ZXJwcmV0ZWQgYXMgYSBtZWFzdXJlIGZvciB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgZ2l2ZW4gc2FtcGxlIGlzIG9ic2VydmVkIGFzIGEgcmVhbGlzYXRpb24gb2YgYSBzZXF1ZW5jZSBvZiByYW5kb20gdmFyaWFibGVzICRZXzEsIFxsZG90cyBZX24kIAoKLSBUaGUgcmFuZG9tIHZhcmlhYmxlcyAkWV9pJCBhcmUgY2hhcmFjdGVyaXplZCBieSBhIGRpc3RyaWJ1dGlvbiBvciBkZW5zaXR5IGZ1bmN0aW9uIHdoaWNoIGhhcyB0eXBpY2FsbHkgdW5rbm93biBwYXJhbWV0ZXJzLCBlLmcuIGEgUG9pc3NvbiBkaXN0cmlidXRpb24gJGYoWV9pKVxzaW0gXHRleHR7UG9pc3Nvbn0oXHRoZXRhX2kpJC4KCi0gV2hlbiB0aGUgc3ViamVjdHMgYXJlIG11dHVhbGx5IGluZGVwZW5kZW50IHRoZSBqb2ludCBsaWtlbGlob29kIHRvIG9ic2VydmUgJHlfMSwgXGxkb3RzLCB5X24kIGVxdWFscyAKJCRccHJvZFxsaW1pdHNfe2k9MX1ebiBmKHlfaSxcdGhldGFfaSxccGhpKSQkCi0gVGhlIGRlbnNpdGllcyBhcmUgYWN0dWFsbHkgYWxzbyBhIGZ1bmN0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzICRcdGhldGFfaSwgXHBoaSQuIFRvIHN0cmVzcyB0aGlzLCB3ZSBpbmRpY2F0ZWQgdGhhdCBpbiB0aGUgZGVuc2l0eSBmb3JtdWxhdGlvbi4KCi0gVGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gaXMgYSBmdW5jdGlvbiBvZiBhbGwgcGFyYW1ldGVycyAKCiQkCkwoXGJvbGRzeW1ib2x7XHRoZXRhfSxccGhpXHZlcnQgXGJvbGRzeW1ib2x7eX0pPVxwcm9kXGxpbWl0c197aT0xfV5uIGYoeV9pLFx0aGV0YV9pLFxwaGkpCiQkCgotIFRoZSBsb2ctbGlrZWxpaG9vZCBmdW5jdGlvbiBpcyBvZnRlbiB1c2VkLCB3aGljaCBpcyBkZWZpbmVkIGFzCiQkCmwoXGJvbGRzeW1ib2x7XHRoZXRhfSxccGhpXHZlcnQgXGJvbGRzeW1ib2x7eX0pID0gXGxvZyBMKFxib2xkc3ltYm9se1x0aGV0YX0sXHBoaVx2ZXJ0IFxib2xkc3ltYm9se3l9KSA9ICBcc3VtXGxpbWl0c197aT0xfV5uIFxsb2cgIGYoeV9pLFx0aGV0YV9pLFxwaGkpCiQkCiAKIyMjIFBvaXNzb24gRXhhbXBsZSAKClRoZSBsb2ctbGlrZWxpaG9vZCBmb3Igb3VyIHNpbXVsYXRlZCBkYXRhc2V0IGdpdmVuIHRoZSByZWFsIG1vZGVsIHBhcmFtZXRlcnMgaXM6IAoKRm9yIG9uZSBvYnNlcnZhdGlvbjoKJCRsKFxtdV9pIFx2ZXJ0IHlfaSkgPSB5X2kgXGxvZyBcbXVfaSAtIFxtdV9pIC0gXGxvZyB5X2khJCQKCi0gVmVyaWZ5IGluIFIuCgpgYGB7cn0KbXVUcnVlIDwtIGV4cChldGFUcnVlKQpsb2dsaWtQb2lzIDwtIGRwb2lzKHksbXVUcnVlLGxvZyA9IFRSVUUpCmxvZ0xpa1NlbGYgPC0geSpsb2cobXVUcnVlKSAtIG11VHJ1ZSAtIGxmYWN0b3JpYWwoeSkKcXBsb3QobG9nbGlrUG9pcyxsb2dMaWtTZWxmKSArIHRoZW1lX2J3KCkgKyBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZT0xKQpgYGAKClRoZSBsb2ctbGlrZWxpaG9vZCBjYW4gYWxzbyBiZSB3cml0dGVuIGluIHRlcm1zIG9mIHRoZSBjYW5vbmljYWwgbW9kZWwgcGFyYW1ldGVycyAkXHRoZXRhJAoKJCRsKFxtdV9pIFx2ZXJ0IHlfaSkgPSB5X2kgXHRoZXRhX2kgLSBlXntcdGhldGFfaX0gLSBcbG9nIHlfaSEkJAoKLSBOb3RlIHRoYXQgJFx0aGV0YV9pID0gXGV0YV9pJC4gVGhlIGNhbm9uaWNhbCBwYXJhbWV0ZXIgZm9yIHRoZSBwb2lzc29uIGVxdWFscyB0aGUgbGluZWFyIHByZWRpY3RvciEKICAkJFx0aGV0YV9pPVxldGFfaT1cbWF0aGJme3h9X2ledFxib2xkc3ltYm9se1xiZXRhfSQkCgpMb2ctbGlrZWxpaG9vZCBmb3IgYWxsIG9ic2VydmF0aW9ucywgZ2l2ZW4gdGhhdCB0aGV5IGFyZSBpbmRlcGVuZGVudDoKJCRsKFxib2xkc3ltYm9se1xtdX0gXHZlcnQgXG1hdGhiZnt5fSkgPSBcc3VtXGxpbWl0c197aT0xfV5uIFxsZWZ0XHsgeV9pIFx0aGV0YV9pIC0gZV57XHRoZXRhX2l9IC0gXGxvZyB5X2khXHJpZ2h0XH0kJAoKLSBDYWxjdWxhdGUgaW4gUjogCgpgYGB7cn0KZHBvaXMoeSxsYW1iZGEgPSBtdVRydWUsIGxvZyA9IFRSVUUpICU+JSAKICBzdW0oKQpgYGAKCiMjIyBQcm9wZXJ0aWVzIG9mIHRoZSBMb2ctTGlrZWxpaG9vZAoKJCQKbChcdGhldGFfaSxccGhpXHZlcnQgeV9pKT1cbGVmdFx7IFxmcmFje3lfaVx0aGV0YV9pLSBiKFx0aGV0YV9pKX17YShccGhpKX0rYyh5X2ksXHBoaSlccmlnaHRcfQokJAoKLSAkRVt5X2ldPVxtdV9pPWJeXHByaW1lKFx0aGV0YV9pKSQKLSAkXHRleHR7dmFyfVt5X2ldPWJee1xwcmltZVxwcmltZX0oXHRoZXRhX2kpIGEoXHBoaSkkCgpOb3RlIHRoYXQsIAoKLSBWYXJpYW5jZSAkXHRleHR7dmFyfVt5X2ldJCBkZXBlbmRzIG9uIG1lYW4hIAotIE9mdGVuIHRoZXJlIGlzIG5vIGRpc3BlcnNpb24gcGFyYW1ldGVyIGUuZy4gQmVybm91aWxsaTogJFx0ZXh0e3Zhcn1beV9pXT1cbXVfaSgxLVxtdV9pKSQsIAoKIyMjIFBvaXNzb24gRXhhbXBsZSAKCi0gQ2Fub25pY2FsIG1vZGVsIHBhcmFtZXRlciAkXHRoZXRhX2k9XGxvZ3tcbXVfaX0kLgotICRiKFx0aGV0YV9pKSA9IFxleHAoXHRoZXRhX2kpJAotICRjKHlfaSxccGhpKSA9IC0gXGxvZyh5X2khKSQKLSAkXHBoaSA9IDEkCi0gJGEoXHBoaSk9IDEkCi0gJFxtdV9pID1cZnJhY3tccGFydGlhbCBiKFx0aGV0YV9pKX17XHBhcnRpYWwgXHRoZXRhX2l9PSAgXGZyYWN7XHBhcnRpYWwgXGV4cChcdGhldGFfaSl9e1xwYXJ0aWFsIFx0aGV0YV9pfT1cZXhwKFx0aGV0YV9pKSQKLSAkXHRleHR7VmFyfVxsZWZ0W3lfaSBccmlnaHRdPSBhKFxwaGkpXGZyYWN7XHBhcnRpYWxeMiBiKFx0aGV0YV9pKX17KFxwYXJ0aWFsIFx0aGV0YV9pKV4yfT0gIFxmcmFje1xwYXJ0aWFsXjIgXGV4cChcdGhldGFfaSl9e1xwYXJ0aWFsIFx0aGV0YV9pXjJ9PVxleHAoXHRoZXRhX2kpJC4KLSBNZWFuIGlzIGVxdWFsIHRvIHZhcmlhbmNlIGZvciBQb2lzc29uIQoKIyMgUGFyYW1ldGVyIEVzdGltYXRpb246IE1heGltdW0gTGlrZWxpaG9vZAoKQ2hvb3NlIHRoZSBwYXJhbWV0ZXJzICRcYm9sZHN5bWJvbHtcYmV0YX0kIHNvIHRoYXQgdGhlIGxpa2VsaWhvb2QgdG8gb2JzZXJ2ZSB0aGUgc2FtcGxlIHVuZGVyIHRoZSBzdGF0aXN0aWNhbCBtb2RlbCBpcyBtYXhpbWl6ZWQuCgpJdCBpcyBlYXNpZXIgYW5kIGVxdWl2YWxlbnQgdG8gbWF4aW1pemUgdGhlIGxvZy1saWtpaG9vZAoKJCRcdGV4dHthcmdtYXh9X3tcYm9sZHN5bWJvbHtcYmV0YX19IGwoXGJvbGRzeW1ib2x7XG11fSBcdmVydCBcbWF0aGJme3l9KSQkCgokJApcZnJhY3tccGFydGlhbCAgbChcYm9sZHN5bWJvbHtcbXV9IFx2ZXJ0IFxtYXRoYmZ7eX0pfXsKXHBhcnRpYWx7XGJvbGRzeW1ib2x7XGJldGF9fX09MAokJAoKJFxmcmFje1xwYXJ0aWFsICBsKFxib2xkc3ltYm9se1xtdX0gXHZlcnQgXG1hdGhiZnt5fSl9ewpccGFydGlhbHtcYm9sZHN5bWJvbHtcYmV0YX19fSQgaXMgYWxzbyByZWZlcnJlZCB0byBhcyB0aGUgc2NvcmUgZnVuY3Rpb24uCgojIyMgU2NvcmUgZnVuY3Rpb24KCiQkClNfaShcdGhldGFfaSk9IFxmcmFje1xwYXJ0aWFsIGwoXHRoZXRhX2ksXHBoaVx2ZXJ0IHlfaSl9e1xwYXJ0aWFsIFx0aGV0YV9pfQokJAoKJCQKU19pKFx0aGV0YV9pKT0gXGZyYWN7XHBhcnRpYWwgbChcdGhldGFfaSxccGhpXHZlcnQgeV9pKX17XHBhcnRpYWwgXHRoZXRhX2l9PVxmcmFje3lfaSAtIFxtdV9pfXthKFxwaGkpfQokJAoKd2hlbiBjYW5vbmljYWwgbGluayBmdW5jdGlvbiBpcyB1c2VkOgoKLSAkXG11X2k9Yl5ccHJpbWUoXHRoZXRhX2kpJCAKCgpSZWdyZXNzaW9uIChjaGFpbiBydWxlIGFuZCAkaT0xLFxsZG90cyxuJCBpLmkuZCBvYnNlcnZhdGlvbnMpCgpcYmVnaW57ZXFuYXJyYXkqfQpTKFxib2xkc3ltYm9se1xiZXRhfSkmPSZcZnJhY3tccGFydGlhbCAgXHN1bVxsaW1pdHNfe2k9MX1ebiBcbGVmdFx7IFxmcmFje3lfaSBcdGhldGFfaSAtIGIoXHRoZXRhX2kpfXthKFxwaGkpfStjKHlfaSxccGhpKVxyaWdodFx9fXtccGFydGlhbCBcYmV0YX1cXAomPSZcc3VtXGxpbWl0c197aT0xfV5uXGZyYWN7XHBhcnRpYWwgIFxsZWZ0XHsgXGZyYWN7eV9pIFx0aGV0YV9pIC0gYihcdGhldGFfaSl9e2EoXHBoaSl9K2MoeV9pLFxwaGkpXHJpZ2h0XH19e1xwYXJ0aWFsIFx0aGV0YX1cZnJhY3tccGFydGlhbCBcdGhldGF9e1xwYXJ0aWFsXG11fVxmcmFje1xwYXJ0aWFsXG11fXtccGFydGlhbFxldGF9XGZyYWN7XHBhcnRpYWxcZXRhfXtccGFydGlhbFxiZXRhfVxcCiY9JlxzdW1cbGltaXRzX3tpPTF9Xm5cZnJhY3t5X2ktXG11X2l9e2EoXHBoaSl9XGZyYWN7MX17Yl57XHByaW1lXHByaW1lfShcdGhldGEpfVxmcmFje1xwYXJ0aWFsXG11fXtccGFydGlhbFxldGF9XG1hdGhiZnt4fV50XFwKJj0mXG1hdGhiZntYfV5UXG1hdGhiZntBfVxsZWZ0KFxtYXRoYmZ7eX0tXGJvbGRzeW1ib2x7XG11fVxyaWdodCkKXGVuZHtlcW5hcnJheSp9CgoKLSAkXGJvbGRzeW1ib2x7QX0kIGlzIGEgZGlhZ29uYWwgbWF0cml4OiAkYV97aWl9PVxsZWZ0KFx0ZXh0e3Zhcn1beV9pXVxmcmFje1xwYXJ0aWFsIFxldGFfaX17XHBhcnRpYWwgXG11X2l9XHJpZ2h0KV57LTF9JCwgJFxib2xkc3ltYm9se3l9PVt5XzEsXGxkb3RzLCB5X25dXlQkLCAkXGJvbGRzeW1ib2x7XG11fT1bXG11XzEsXGxkb3RzLFxtdV9uXV5UJCAKCiMjIyMgUG9pc3NvbiBFeGFtcGxlIAoKCkZvciBwb2lzc29uIGRhdGE6ICRhKFxwaGkpYl57XHByaW1lXHByaW1lfShcdGhldGEpPVxtdSQgYW5kICRcZnJhY3tccGFydGlhbCBcbXV9e1xwYXJ0aWFsIFxldGF9PVxtdSQuIFNvICRcbWF0aGJme0F9PVxtYXRoYmZ7SX0kIGFuZAoKJCQgUyhcYmV0YSk9XG1hdGhiZntYfV5UIFxsZWZ0XHtcbWF0aGJme1l9LVxib2xkc3ltYm9se1xtdX1ccmlnaHRcfSQkCgojIyMgU29sdmUgU2NvcmUgRXF1YXRpb25zCgpGaW5kICBwYXJhbWV0ZXIgZXN0aW1hdG9yICAkXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19JCBzbyB0aGF0CiQkIFMoXGJvbGRzeW1ib2x7XGJldGF9KSA9IFxtYXRoYmZ7MH0kJAokJFxtYXRoYmZ7WH1eVFxtYXRoYmZ7QX1cbGVmdChcbWF0aGJme3l9LVxib2xkc3ltYm9se1xtdX1ccmlnaHQpID1cbWF0aGJmezB9JCQKUHJvYmxlbSEgTm9uIGxpbmVhciBpbiAkXGJldGEkIGR1ZSB0byAKCi0gbGluayBmdW5jdGlvbjogJFxib2xkc3ltYm9se1xtdX09aF57LTF9KFxldGEpJCAKLSAkYV97aWl9PVxsZWZ0KFx0ZXh0e3Zhcn1beV9pXVxmcmFje1xwYXJ0aWFsIFxldGFfaX17XHBhcnRpYWwgXG11X2l9XHJpZ2h0KV57LTF9JAoKJFxyaWdodGFycm93JCBGaW5kIHJvb3RzIG9mIHNjb3JlIGVxdWF0aW9uIGJ5IHVzaW5nIE5ld3Rvbi1SYXBoc29uIG1ldGhvZC4KCiMjIyBOZXd0b24tUmFwaHNvbgoKYGBge3IgZWNobz1GQUxTRSwgZmlnLmNhcD0gIk5ld3Rvbi1SYXBoc29uIGFsZ29yaXRobTogVGhlIGFsZ29yaXRobSBzdGFydHMgYXQgYW4gaW5pdGlhbCBndWVzcyBiayBmb3IgdGhlIHJvb3Qgb2YgdGhlIHNjb3JlIGZ1bmN0aW9uIFMuIEl0IHdhbGtzIGFsb25nIHRoZSB0YW5nZW50IGxpbmUgb2YgdGhlIHNjb3JlIGZ1bmN0aW9uIGluIFMoYmspICB0byBtb3ZlIGludG8gdGhlIGRpcmVjdGlvbiB3aGVyZSB0aGUgc2NvcmUgZnVuY3Rpb24gYmVjb21lcyB6ZXJvIChhbmQgd2hlcmUgdGhlIGxvZy1saWtlbGlob29kIGZ1bmN0aW9uIGlzIG1heGltYWwpLiBUaGUgbmV3IGVzdGltYXRlIG9mIHRoZSByb290IGlzIHRha2VuIGF0IGJrKzEgd2hlcmUgdGhlIHRhbmdlbnQgbGluZSBiZWNvbWVzIHplcm8uIEl0IHRoZW4gY2FsY3VsYXRlcyB0aGUgc2NvcmUgZm9yIHRoZSB1cGRhdGVkIHBhcmFtZXRlciBlc3RpbWF0b3IgYW5kIHRoZSB3aG9sZSBwcm9jZXNzIGlzIHJlcGVhdGVkIHVudGlsIHRoZSByb290IGlzIGZvdW5kLiJ9ClMgPC0gZnVuY3Rpb24oYmV0YSkgKGJldGEgLSAzKV4yIApkUyA8LSBmdW5jdGlvbihiZXRhKSAyKihiZXRhLTMpCmRTbGluZSA8LSBmdW5jdGlvbihiZXRhLCBiZXRhaykgZFMoYmV0YWspICpiZXRhICsgUyhiZXRhaykgLSBkUyhiZXRhaykgKiBiZXRhawoKYmV0YSA8LSBzZXEoLTIxLDE1LGxlbmd0aD0xMDApCmJldGFrIDwtIC0yMApiZXRhazEgPC0gYmV0YWsgLSAxL2RTKGJldGFrKSpTKGJldGFrKQpiZXRhU2VxRGsgPC0gc2VxKGJldGFrLGJldGFrMSxsZW5ndGg9MTAwKQpwTlIgPC0gcXBsb3QoYmV0YSwgUyhiZXRhKSwgZ2VvbT0ibGluZSIpICsKICBhbm5vdGF0ZSgibGluZSIseD1iZXRhU2VxRGsseT1kU2xpbmUoYmV0YVNlcURrLGJldGFrKSxsaW5ldHlwZSA9ICJkYXNoZWQiLGNvbD0icmVkIikgKwogICAgYW5ub3RhdGUoImxpbmUiLHg9cmVwKGJldGFrMSwyKSx5PWMoMCxTKGJldGFrMSkpLGxpbmV0eXBlID0gImRhc2hlZCIsY29sPSJyZWQiKSArCiAgICBhbm5vdGF0ZSgicG9pbnQiLHg9YmV0YWsseT1TKGJldGFrKSxjb2w9InJlZCIpICsKICB0aGVtZV9idygpCmJldGFOcyA8LSBiZXRhazEKZm9yIChpIGluIDE6NSkKewpiZXRhayA8LSBiZXRhazEKYmV0YWsxIDwtIGJldGFrIC0gMS9kUyhiZXRhaykqUyhiZXRhaykKYmV0YU5zIDwtIGMoYmV0YU5zLGJldGFrMSkKYmV0YVNlcURrIDwtIHNlcShiZXRhayxiZXRhazEsbGVuZ3RoPTEwMCkKcE5SIDwtIHBOUiArCiAgICBhbm5vdGF0ZSgibGluZSIseD1iZXRhU2VxRGsseT1kU2xpbmUoYmV0YVNlcURrLGJldGFrKSxsaW5ldHlwZSA9ICJkYXNoZWQiLGNvbD0icmVkIikgKwogICAgYW5ub3RhdGUoImxpbmUiLHg9cmVwKGJldGFrMSwyKSx5PWMoMCxTKGJldGFrMSkpLGxpbmV0eXBlID0gImRhc2hlZCIsY29sPSJyZWQiKSAKfQpwTlIgKyAKICBhbm5vdGF0ZSgidGV4dCIseD0tMjAseT1TKC0yMCkrNDAsbGFiZWw9cGFzdGUwKCJTKGJldGFba10pIiksY29sPSJyZWQiLHBhcnNlPVRSVUUpICsKICBhbm5vdGF0ZSgidGV4dCIseD1iZXRhTnNbMToyXSx5PVMoYmV0YU5zWzE6Ml0pKzQwLGxhYmVsPXBhc3RlMCgiUyhiZXRhW2srIiwxOjIsIl0pIiksY29sPSJyZWQiLHBhcnNlPVRSVUUpICAgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWJldGFOc1sxOjNdLHk9cmVwKC00MCwzKSxsYWJlbD1wYXN0ZTAoImJldGFbaysiLDE6MywiXSIpLGNvbD0icmVkIixwYXJzZT1UUlVFKSArCiAgYW5ub3RhdGUoInBvaW50Iix4PWJldGFOcyx5PVMoYmV0YU5zKSxjb2w9InJlZCIpICsKICBhbm5vdGF0ZSgidGV4dCIseD0tMjAseT00MDAsIGxhYmVsPSJmcmFjKHBhcnRpYWxkaWZmKlMsIHBhcnRpYWxkaWZmKmJldGEpKicgfCcqYmV0YVtrXSIsIGNvbD0icmVkIixwYXJzZT1UUlVFKSArCiAgeGxhYihleHByZXNzaW9uKGJldGEpKSArCiAgeWxhYihleHByZXNzaW9uKFMoYmV0YSkpKQpgYGAKCk5ld3RvbiBSYXBoc29uIGFsZ29yaXRobSB0byBmaW5kIHRoZSByb290IG9mIHRoZSBzY29yZSBmdW5jdGlvbi4gCgoxLiBDaG9vc2UgaW5pdGlhbCBwYXJhbWV0ZXIgZXN0aW1hdGUgJFxib2xkc3ltYm9se1xiZXRhfV5rPVxib2xkc3ltYm9se1xiZXRhfV4wJAoyLiBDYWxjdWxhdGUgc2NvcmUgJFMoXGJvbGRzeW1ib2x7XGJldGF9KVx2ZXJ0X3tcYm9sZHN5bWJvbHtcYmV0YX09XGJvbGRzeW1ib2x7XGJldGF9Xmt9JAozLiBDYWxjdWxhdGUgZGVyaXZhdGl2ZSBvZiB0aGUgZnVuY3Rpb24gZm9yIHdoaWNoIHlvdSB3YW50IHRvIGNhbGN1bGF0ZSB0aGUgcm9vdHMKNC4gV2FsayBhbG9uZyBmaXJzdCBkZXJpdmF0aXZlIHVudGlsIGxpbmUgKHBsYW5lKSBvZiB0aGUgZGVyaXZhdGl2ZSBjcm9zc2VzIHplcm8KNS4gVXBkYXRlIHRoZSBiZXRhcyAkXGJvbGRzeW1ib2x7XGJldGF9XntrKzF9JAo2LiBJdGVyYXRlIGZyb20gc3RlcCAyIC0gNSB1bnRpbCBjb252ZXJnZW5jZS4KCgojIyMjIERlcml2YXRpdmUgb2YgU2NvcmUgCgpXZSBoYXZlIHRvIGltcGxlbWVudCBhbiBpbnRlcmF0aXZlIGFsZ29yaXRobSBmb3Igb3B0aW1pc2F0aW9uLiBUbyBtYWtlIHRoaW5ncyB0cmFjdGFibGUgd2Ugd2lsbCBhY3QgYXMgaWYgJFxtYXRoYmZ7QX0kIGlzIGtub3duIGFuZCBmaXggaXQgdXNpbmcgdGhlIGN1cnJlbnQgdmFsdWVzIG9mICRcYm9sZHN5bWJvbHtcYmV0YX1eayQuIE5vdGUsIHRoYXQgZm9yIFBvaXNzb24gcmVncmVzc2lvbiAkXG1hdGhiZntBfT1cbWF0aGJme0l9JC4KClxiZWdpbntlcW5hcnJheSp9ClxmcmFje1xwYXJ0aWFsIFMoXGJvbGRzeW1ib2x7XGJldGF9KX17XHBhcnRpYWwgXGJvbGRzeW1ib2x7XGJldGF9fSAmPSYgXGZyYWN7IFxtYXRoYmZ7WH1eVFxtYXRoYmZ7QX0gXGxlZnRce1xtYXRoYmZ7WX0tXGJvbGRzeW1ib2x7XG11fVxyaWdodFx9fXtccGFydGlhbCBcYm9sZHN5bWJvbHtcYmV0YX19XFwKJj0mIC0gXG1hdGhiZntYfV5UIFxtYXRoYmZ7QX1cbGVmdFsKXGJlZ2lue2FycmF5fXtjY2NjfSBcZnJhY3tccGFydGlhbCBcbXVfMX17XHBhcnRpYWwgXGV0YV8xfSAmMCZcbGRvdHMmMFxcCiAwJlxmcmFje1xwYXJ0aWFsIFxtdV8yfXtccGFydGlhbCBcZXRhXzJ9ICZcbGRvdHMmMFxcClx2ZG90cyZcdmRvdHMmXHZkb3RzJlx2ZG90c1xcCjAmMCZcbGRvdHMmIFxmcmFje1xwYXJ0aWFsIFxtdV9ufXtccGFydGlhbCBcZXRhX259XFwKXGVuZHthcnJheX1ccmlnaHRdIFxmcmFje1xwYXJ0aWFsIFxib2xkc3ltYm9se1xldGF9fXtccGFydGlhbCBcYm9sZHN5bWJvbHtcYmV0YX19XFwKJj0mLVxtYXRoYmZ7WH1eVFxtYXRoYmZ7V1h9ClxlbmR7ZXFuYXJyYXkqfQoKIyMjIyBEZWZpbmUgZXF1YXRpb24gb2YgVGFuZ2VudCBMaW5lIChQbGFuZSkKCi0gV2Uga25vdyB0d28gcG9pbnRzIG9mIHRoZSB0YW5nZW50IHBsYW5lICQoXGJvbGRzeW1ib2x7XGJldGF9XmssUyhcYm9sZHN5bWJvbHtcYmV0YX1eaykpJCBhbmQgJChcYm9sZHN5bWJvbHtcYmV0YX1ee2srMX0sMCkkCi0gV2Uga25vdyB0aGUgZGlyZWN0aW9uIG9mIHRoZSBwbGFuZSAkU15ccHJpbWUoXGJvbGRzeW1ib2x7XGJldGF9KT1cZnJhY3tccGFydGlhbCBTKFxib2xkc3ltYm9se1xiZXRhfSl9e1xwYXJ0aWFsIFxib2xkc3ltYm9se1xiZXRhfX0kCi0gRXF1YXRpb24gb2YgUGxhbmU6CiQkUyhcYm9sZHN5bWJvbHtcYmV0YX0pPXtcYWxwaGF9XzArU15ccHJpbWVcdmVydF97XGJvbGRzeW1ib2x7XGJldGF9Xmt9IFxib2xkc3ltYm9se1xiZXRhfSQkCgotIEdldCAkXGJvbGRzeW1ib2x7XGJldGF9X3trKzF9JApcYmVnaW57ZXFuYXJyYXkqfQpcbWF0aGJmezB9Jj0me1xhbHBoYX1fMCtTXlxwcmltZVx2ZXJ0X3tcYm9sZHN5bWJvbHtcYmV0YX1ee2t9fSBcYm9sZHN5bWJvbHtcYmV0YX1ee2srMX1cXApcYm9sZHN5bWJvbHtcYmV0YX1ee2srMX0mPSYtXGxlZnQoU157XHByaW1lfVx2ZXJ0X3tcYm9sZHN5bWJvbHtcYmV0YX1ee2t9fVxyaWdodCleey0xfXtcYWxwaGF9XzBcXApcZW5ke2VxbmFycmF5Kn0KCi0gR2V0ICR7XGFscGhhfV8wJApcYmVnaW57ZXFuYXJyYXkqfQpTKFxib2xkc3ltYm9se1xiZXRhfV5rKSY9Jlxib2xkc3ltYm9se1xhbHBoYX1fMCtTXlxwcmltZVx2ZXJ0X3tcYm9sZHN5bWJvbHtcYmV0YX1ea30gXGJvbGRzeW1ib2x7XGJldGF9XmtcXAp7XGFscGhhfV8wJj0mLVNeXHByaW1lXHZlcnRfe1xib2xkc3ltYm9se1xiZXRhfV5rfSBcYm9sZHN5bWJvbHtcYmV0YX1eayArIFMoXGJvbGRzeW1ib2x7XGJldGF9XmspXFwKXGVuZHtlcW5hcnJheSp9CgotIEdldCAkXGJvbGRzeW1ib2x7XGJldGF9X3trKzF9JAoKXGJlZ2lue2VxbmFycmF5Kn0KXGJvbGRzeW1ib2x7XGJldGF9XntrKzF9Jj0mXGJvbGRzeW1ib2x7XGJldGF9XmstXGxlZnQoU157XHByaW1lfVx2ZXJ0X3tcYm9sZHN5bWJvbHtcYmV0YX1ee2t9fVxyaWdodCleey0xfVMoXGJvbGRzeW1ib2x7XGJldGF9XmspXFwKXGJvbGRzeW1ib2x7XGJldGF9XntrKzF9Jj0mXGJvbGRzeW1ib2x7XGJldGF9XmsrIFxsZWZ0KFxtYXRoYmZ7WH1eVFxtYXRoYmZ7V1h9XHJpZ2h0KV57LTF9IFMoXGJvbGRzeW1ib2x7XGJldGF9XmspClxlbmR7ZXFuYXJyYXkqfQoKV2l0aCAkSihcYm9sZHN5bWJvbHtcYmV0YX0pPUkoXGJvbGRzeW1ib2x7XGJldGF9KT1cbWF0aGJme1h9XlRcbWF0aGJme1dYfSQgIHRoZSBGaXNoZXIgaW5mb3JtYXRpb24gbWF0cml4LgoKIyMjIEZpc2hlciBTY29yaW5nCgpCZWNhdXNlIHdlIHVzZSB0aGUgY2Fub25pY2FsIG1vZGVsIHBhcmFtZXRlcnMgdGhlIG9ic2VydmVkICBGaXNoZXIgaW5mb3JtYXRpb24gbWF0cml4IGVxdWFscyB0aGUgZXhwZWN0ZWQgIEZpc2hlciBpbmZvcm1hdGlvbiBtYXRyaXggJEooXGJvbGRzeW1ib2x7XGJldGF9KT1JKFxib2xkc3ltYm9se1xiZXRhfSkkLgpJbmRlZWQsIHRoZSBvYnNlcnZlZCBGaXNoZXIgaW5mb3JtYXRpb24gbWF0cml4IGlzIG5vdCBkZXBlbmRpbmcgb24gdGhlIG9ic2VydmF0aW9ucywgYnV0IG9ubHkgb24gdGhlIGRlc2lnbiBhbmQgdGhlIHZhcmlhbmNlIG9mIHRoZSBkYXRhICh2aWEgdGhlIHdlaWdodHMpLgoKSGVuY2UsIE5ld3Rvbi1SYXBoc29uIGlzIGVxdWl2YWxlbnQgdG8gRmlzaGVyIHNjb3Jpbmcgd2hlbiB0aGUgY2Fub25pY2FsIGxpbmsgZnVuY3Rpb24gaXMgdXNlZC4KCk5vdGUsIHRoYXQgdGhlIEZpc2hlciBtYXRyaXgsIG1pbnVzIHNlY29uZCBkZXJpdmF0aXZlIChvciBoZXNzaWFuKSBvZiB0aGUgbGlrZWxpaG9vZCB0byB0aGUgbW9kZWwgcGFyYW1ldGVycywgaXMgYWxzbyB0aGUgaW52ZXJzZSBvZiB0aGUgdmFyaWFuY2UgY292YXJpYW5jZSBtYXRyaXggb2YgdGhlIG1vZGVsIHBhcmFtZXRlcnMuIEl0IGlzIHRodXMgcmVsYXRlZCB0byB0aGUgcHJlY2lzaW9uLgoKCiMjIyBJdGVyYXRpdmVseSBSZXdlaWdodGVkIExlYXN0IFNxdWFyZXMgKElSTFMpLgoKV2UgY2FuIHJld3JpdGUgTmV3dG9uIFJhcGhzb24gb3IgRmlzaGVyIHNjb3JpbmcgYXMgSVJMUy4KClxiZWdpbntlcW5hcnJheSp9Clxib2xkc3ltYm9se1xiZXRhfV57aysxfSY9Jlxib2xkc3ltYm9se1xiZXRhfV5rKyBcbGVmdChcbWF0aGJme1h9XlRcbWF0aGJme1dYfVxyaWdodCleey0xfSBTKFxib2xkc3ltYm9se1xiZXRhfV5rKVxcClxib2xkc3ltYm9se1xiZXRhfV57aysxfSY9Jlxib2xkc3ltYm9se1xiZXRhfV5rKyBcbGVmdChcbWF0aGJme1h9XlRcbWF0aGJme1dYfVxyaWdodCleey0xfSBcbWF0aGJme1h9XlRcbWF0aGJme0F9IFxsZWZ0KFxtYXRoYmZ7WX0tXGJvbGRzeW1ib2x7XG11fVxyaWdodClcXApcYm9sZHN5bWJvbHtcYmV0YX1ee2srMX0mPSYgXGxlZnQoXG1hdGhiZntYfV5UXG1hdGhiZntXWH1ccmlnaHQpXnstMX1cbWF0aGJme1h9XlRcbWF0aGJme1dYfVxib2xkc3ltYm9se1xiZXRhfV5rKyBcbGVmdChcYm9sZHN5bWJvbHtYfV5UXG1hdGhiZntXWH1ccmlnaHQpXnstMX0gXG1hdGhiZntYfV5UIFxtYXRoYmZ7V31cZnJhY3tccGFydGlhbCBcZXRhfXtccGFydGlhbCBcbXV9ICBcbGVmdChcbWF0aGJme1l9LVxib2xkc3ltYm9se1xtdX1ccmlnaHQpXFwKXGJvbGRzeW1ib2x7XGJldGF9XntrKzF9Jj0mIFxsZWZ0KFxtYXRoYmZ7WH1eVFxtYXRoYmZ7V1h9XHJpZ2h0KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntXfSBcbGVmdFtcbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9XmsgKyBcZnJhY3tccGFydGlhbCBcZXRhfXtccGFydGlhbCBcbXV9ICBcbGVmdChcbWF0aGJme1l9LVxib2xkc3ltYm9se1xtdX1ccmlnaHQpClxyaWdodF1cXApcYm9sZHN5bWJvbHtcYmV0YX1ee2srMX0mPSYgXGxlZnQoXG1hdGhiZntYfV5UXG1hdGhiZntXWH1ccmlnaHQpXnstMX1cbWF0aGJme1h9XlRcbWF0aGJme1d6fQpcZW5ke2VxbmFycmF5Kn0KCndpdGggJFxtYXRoYmZ7en09XGxlZnRbXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfV5rICsgXGZyYWN7XHBhcnRpYWwgXGV0YX17XHBhcnRpYWwgXG11fSAgXGxlZnQoXG1hdGhiZntZfS1cYm9sZHN5bWJvbHtcbXV9XHJpZ2h0KVxyaWdodF0kCgpTbyB3ZSBjYW4gZml0IHRoZSBtb2RlbCBieSBwZXJmb3JtaW5nIGl0ZXJhdGl2ZSByZWdyZXNzaW9ucyBvZiB0aGUgcHNldWRvIGRhdGEgJFxtYXRoYmZ7en0kIG9uICRcbWF0aGJme1h9JC4KSW4gZWFjaCBpdGVyYXRpb24gd2Ugd2lsbCB1cGRhdGUgJFxtYXRoYmZ7en0kLCB0aGUgd2VpZ2h0cyAkXG1hdGhiZntXfSQgYW5kIHRoZSBtb2RlbCBwYXJhbWV0ZXJzLgoKRm9yIFBvaXNzb24gZGF0YSAKCi0gJFxmcmFje1xwYXJ0aWFsIFxldGF9e1xwYXJ0aWFsIFxtdX09XGZyYWN7XHBhcnRpYWxcbG9nIFxtdX17XHBhcnRpYWxcbXV9PVxmcmFjezF9e1xtdX09XGV4cCgtXGV0YSkkICAKLSAkXG1hdGhiZntXfT1cbWF0aGJme0F9XGZyYWN7XHBhcnRpYWx7XG11fX17e1xwYXJ0aWFsIFxldGF9fSQgaXMgYSBkaWFnb25hbCBtYXRyaXggd2l0aCAkW1xmcmFje1xwYXJ0aWFse1xtdV9pfX17e1xwYXJ0aWFsIFxldGFfaX19XV97aWl9PVtcbXVfaV1fe2lpfT1bXGV4cChcZXRhX2kpXV97aWl9JCBvbiBpdHMgZGlhZ29uYWwgZWxlbWVudHMuCgoKIyMjIFZhcmlhbmNlLUNvdmFyaWFuY2UgTWF0cml4IG9mIE1lYW4gTW9kZWwgUGFyYW1ldGVycz8KCkluIHRoZSBJUldMUyBhbGdvcml0aG0sIHRoZSBkYXRhIGlzIHdlaWdodGVkIGFjY29yZGluZyB0byB0aGUgdmFyaWFuY2Ugb2YgJFxtYXRoYmZ7WX0kLiBXZSBjb3JyZWN0IGZvciB0aGUgZmFjdCB0aGF0IHRoZSBkYXRhIGFyZSBoZXRlcm9zY2VkYXN0aWMuCgpDb3VudCBkYXRhIGhhdmUgYSBtZWFuIHZhcmlhbmNlIHJlbGF0aW9uIChlLmcuIGluIFBvaXNzb24gY2FzZSAkXHRleHR7RX1cbGVmdFtZIFxyaWdodF09XHRleHR7dmFyfVxsZWZ0W1kgXHJpZ2h0XT1cbXUkKS4KVGhlIElSV0xTIGFsc28gY29ycmVjdHMgZm9yIHRoZSBzY2FsZSBwYXJhbWV0ZXIgJFxwaGkkIGluICRcbWF0aGJme1d9JC4gKE5vdGUgdGhhdCB0aGUgc2NhbGUgcGFyYW1ldGVyIGZvciBQb2lzc29uIGlzICRccGhpPTEkKS4KClNvIElSV0xTIHRoZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCBmb3IgdGhlIG1vZGVsIHBhcmFtZXRlciBlcXVhbHMKJCRcbWF0aGJme1xTaWdtYX1fe1xoYXRcYmV0YX09XGxlZnQoXG1hdGhiZntYfV5UXG1hdGhiZntXWH1ccmlnaHQpXnstMX0uJCQKCk5vdGUsIHRoYXQgdGhlIEZpc2hlciBJbmZvcm1hdGlvbiBNYXRyaXggZXF1YWxzIHRoZSBpbnZlcnNlIG9mIHRoZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCBvZiB0aGUgZXhwZXJpbWVudC4KVGhlIGxhcmdlciB0aGUgRmlzaGVyIEluZm9ybWF0aW9uIE1hdHJpeCB0aGUgbW9yZSBpbmZvcm1hdGlvbiB3ZSBoYXZlIG9uIHRoZSBleHBlcmltZW50IHRvIGVzdGltYXRlIHRoZSBtb2RlbCBwYXJhbWV0ZXJzLgpGSU0gJFx1cGFycm93JCwgcHJlY2lzaW9uICRcdXBhcnJvdyQsICRcdGV4dHtTRX1cZG93bmFycm93JAoKIyMgUG9pc3NvbiBFeGFtcGxlIAoKCiMjIyBJbml0aWFsIEVzdGltYXRlCgpUaGlzIGlzIGEgdmVyeSBwb29yIGluaXRpYWwgZXN0aW1hdGUgdXNlZCB0byBpbGx1c3RyYXRlIHRoZSBhbGdvcml0aG0uCk90aGVyd2lzZSBjb252ZXJnZW5jZSBmb3IgdGhpcyBzaW1wbGUgZXhhbXBsZSBpcyB3YXkgdG9vIHF1aWNrCgpgYGB7cn0KaXRlcmF0aW9uPTAKYmV0YXM8LWMobG9nKG1lYW4oeSkpLDAsMCkKcGxvdChiZXRhc1RydWUsCiAgICAgeWxhYj1leHByZXNzaW9uKGJldGEpLAogICAgIHlsaW09YygwLDQpLAogICAgIHBjaD0xOSwKICAgICB0eXBlPSJiIiwgCiAgICAgbWFpbj1wYXN0ZTAoImxpa2VsaWhvb2QgcmVhbCBiZXRhPSIsCiAgICAgICAgICAgICAgICAgcm91bmQoc3VtKGRwb2lzKHksZXhwKGV0YVRydWUpLGxvZz1UUlVFKSksMSksIlxubGlrZWxpaG9vZCBmaXQ9Iiwgcm91bmQoc3VtKGRwb2lzKHksZXhwKHhobHAlKiViZXRhcyksbG9nPVRSVUUpKSwxKSkKKQpsaW5lcyhiZXRhcyx0eXBlPSJiIixsdHk9MiApCmBgYAoKIyMjIEl0ZXJhdGl2ZWx5IFJld2VpZ2h0ZWQgTGVhc3QgU3F1YXJlcwoKIyMjIyBQc2V1ZG8gRGF0YQoKJCR6X2k9IFxldGFfaSArIFxmcmFje1xwYXJ0aWFsIFxldGFfaX17XHBhcnRpYWwgXG11X2l9KHlfaSAtXG11X2kpJCQKJCR6X2k9IFxldGFfaSArIGVeey1cZXRhX2l9IHlfaSAtMSQkCgojIyMjIFdlaWdodCBNYXRyaXg/CgokJFt3X3tpaX1dPSB2YXJfe3lfaX1eey0xfSBcbGVmdChcZnJhY3tccGFydGlhbCBcbXV9e1xwYXJ0aWFsIFxldGF9XHJpZ2h0KV4yJCQKJCRbd197aWl9XT0gZV57XGV0YV9pfSQkCgojIyMjIFJ1biBVcGRhdGUgU3RlcCBNdWx0aXBsZSBUaW1lcwoKRmlyc3QgMyB0aW1lcyAoY29sb3JzIGFyZSBibGFjayAwLCByZWQgaXRlcmF0aW9uIDEsIGdyZWVuIGl0ZXJhdGlvbiAyLCBibHVlIGl0ZXJhdGlvbiAzKQoKYGBge3J9CnBsb3QoYmV0YXNUcnVlLHlsYWI9ZXhwcmVzc2lvbihiZXRhKSx5bGltPWMoMCw0KSxwY2g9MTksdHlwZT0iYiIpCmxpbmVzKGJldGFzLHR5cGU9ImIiLGx0eT0yKQoKY2F0KCJcbmxpa2VsaWhvb2QgVFJVRT0iLCByb3VuZChzdW0oZHBvaXMoeSxleHAoeGhscCUqJWJldGFzVHJ1ZSksbG9nPVRSVUUpKSwxKSkKY2F0KCJcbmxpa2VsaWhvb2QgaW5pdGlhbCBmaXQ9Iiwgcm91bmQoc3VtKGRwb2lzKHksZXhwKHhobHAlKiViZXRhcyksbG9nPVRSVUUpKSwxKSkKCiNDYWxjdWxhdGUgY3VycmVudCBldGEKZXRhPC14aGxwJSolYmV0YXMKCml0ZXJhdGlvbj0wCmZvciAoaSBpbiAxOjMpCnsKI3N0YXJ0IElSTFMgVVBEQVRFIFNURVAKaXRlcmF0aW9uPWl0ZXJhdGlvbisxCiNjYWxjdWxhdGUgcHNldWRvIGRhdGEgYmFzZWQgb24gY3VycmVudCBiZXRhcwp6PWV0YStleHAoLWV0YSkqKHktZXhwKGV0YSkpCiNjYWxjdWxhdGUgbmV3IHdlaWdodHM6IGRpYWdvbmFsIGVsZW1lbnRzCnc8LWMoZXhwKGV0YSkpCgojdXBkYXRlIGJldGFzCmxtVXBkYXRlPC1sbSh6fi0xK3hobHAsd2VpZ2h0PXcpCiNldGE8LXhobHAlKiViZXRhcwpldGE8LWxtVXBkYXRlJGZpdHRlZApiZXRhczwtbG1VcGRhdGUkY29lZgpsaW5lcyhiZXRhcyx0eXBlPSJiIixjb2w9aXRlcmF0aW9uKzEscGNoPWl0ZXJhdGlvbixsdHk9MikKY2F0KCJcbmxpa2VsaWhvb2QgY3VycmVudCBmaXQ9Iiwgcm91bmQoc3VtKGRwb2lzKHksZXhwKHhobHAlKiViZXRhcyksbG9nPVRSVUUpKSwxKSkKfQpgYGAKCgojIyBDb21wYXJpc29uIHdpdGggR0xNIEZ1bmN0aW9uCgojIyMgU21hcnRlciBJbml0aWFsaXNhdGlvbgpgYGB7cn0KejwtbG9nKHkrLjUpCmJldGFzPC1sbSh6fi0xK3hobHApJGNvZWYKcGxvdChiZXRhc1RydWUseWxhYj1leHByZXNzaW9uKGJldGEpLHlsaW09YygwLDQpLHBjaD0xOSx0eXBlPSJiIikKbGluZXMoYmV0YXMsY29sPTIsdHlwZT0iYiIsbHR5PTIpCiNjYWxjdWxhdGUgY3VycmVudCBldGEKZXRhPC14aGxwJSolYmV0YXMKCmNhdCgiXG5saWtlbGlob29kIFRSVUU9Iiwgcm91bmQoc3VtKGRwb2lzKHksZXhwKHhobHAlKiViZXRhc1RydWUpLGxvZz1UUlVFKSksMSkpCmNhdCgiXG5saWtlbGlob29kIGluaXRpYWwgZml0PSIsIHJvdW5kKHN1bShkcG9pcyh5LGV4cCh4aGxwJSolYmV0YXMpLGxvZz1UUlVFKSksMSkpCmBgYAoKIyMjIEV2YWx1YXRpb24gU3RvcHBpbmcgQ3JpdGVyaW9uCgotIFJlc2lkdWFsIGRldmlhbmNlOiBJcyAyIGxvZyBvZiBMUiBiZXR3ZWVuIGJlc3QgcG9zc2libGUgZml0IGFuZCBjdXJyZW50IGZpdAokJExSPVxmcmFje0xfXHRleHR7YmVzdH19e0xfXHRleHR7Y3VycmVudH19JCQKJCREPTIgKFxsb2cgTF9cdGV4dHtiZXN0fS0gXGxvZyBMX1x0ZXh0e2N1cnJlbnR9KSQkCiQkRD0yIChsX1x0ZXh0e2Jlc3R9LWxfXHRleHR7Y3VycmVudH0pJCQKLSBCZXN0IGZpdDogJFxtdT15JAotIE9wdGltYWwgcG9pc3NvbjoKJCQgbF9cdGV4dHtiZXN0fT1cc3VtXGxlZnRbeV9pIFxsb2coeV9pKSAtIHlfaSAtIFxsb2dcbGVmdCh5X2khXHJpZ2h0KVxyaWdodF0kJAotIEN1cnJlbnQgZml0CiQkIGxfXHRleHR7Y3VycmVudH09XHN1bSBcbGVmdFt5X2kgXGV0YV9pIC1lXntcZXRhX2l9IC0gbG9nXGxlZnQoeV9pIVxyaWdodClccmlnaHRdJCQKLSBEZXZpYW5jZSBEOgokJEQgPSAyIFxzdW0gXGxlZnRbIHlfaSBsb2coeV9pKSAtIHlfaSBcZXRhX2kgLSAoeV9pIC1lXntcZXRhX2l9KVxyaWdodF0kJAotIFByb2JsZW0gdG8gY2FsY3VsYXRlIGl0IGlmIHk9MCBidXQgYnkgYXBwbHkgbCdIb3BpdGFsJ3MgcnVsZSB3ZSBrbm93CiQkXGxpbV97eV9pIFx0byAwfSB5X2kgXGxvZyh5X2kpID0wJCQKCmBgYHtyfQp5bG9neTwtZnVuY3Rpb24oeSkKewpyZXR1cm4oaWZlbHNlKHk9PTAscmVwKDAsbGVuZ3RoKHkpKSx5KmxvZyh5KSkpCn0KCmRldmlhbmNlPC0yKnN1bSh5bG9neSh5KS15KmV0YS0oeS1leHAoZXRhKSkpCgpkZXZpYW5jZU9sZDwtMWUzMApgYGAKCgojIyMgUnVuIFVwZGF0ZSBTdGVwIHVudGlsIENvbnZlcmdlbmNlCgpgYGB7cn0KcGxvdChiZXRhc1RydWUseWxhYj1leHByZXNzaW9uKGJldGEpLHlsaW09YygwLDQpLHBjaD0xOSx0eXBlPSJiIikKbGluZXMoYmV0YXMsdHlwZT0iYiIsbHR5PTIpCgp0b2w8LTFlLTYKaXRlcmF0aW9uPTAKd2hpbGUoKChkZXZpYW5jZU9sZC1kZXZpYW5jZSkvZGV2aWFuY2VPbGQpPnRvbCkKewojc3RhcnQgSVJMUyBVUERBVEUgU1RFUAppdGVyYXRpb249aXRlcmF0aW9uKzEKI2NhbGN1bGF0ZSBwc2V1ZG8gZGF0YSBiYXNlZCBvbiBjdXJyZW50IGJldGFzCno9ZXRhK2V4cCgtZXRhKSooeS1leHAoZXRhKSkKI2NhbGN1bGF0ZSBuZXcgd2VpZ2h0czogZGlhZ29uYWwgZWxlbWVudHMKdzwtYyhleHAoZXRhKSkKCiN1cGRhdGUgYmV0YXMKbG1VcGRhdGU8LWxtKHp+LTEreGhscCx3ZWlnaHQ9dykKI2V0YTwteGhscCUqJWJldGFzCmV0YTwtbG1VcGRhdGUkZml0dGVkCmJldGFzPC1sbVVwZGF0ZSRjb2VmCmxpbmVzKGJldGFzLHR5cGU9ImIiLGNvbD1pdGVyYXRpb24rMSxwY2g9aXRlcmF0aW9uLGx0eT0yKQoKI2NyaXRlcmlvbiBmb3IgY29udmVyZ2VuY2UKZGV2aWFuY2VPbGQ8LWRldmlhbmNlCmRldmlhbmNlPC0yKnN1bSh5bG9neSh5KS15KmV0YS0oeS1leHAoZXRhKSkpCmNhdCgiaXRlcmF0aW9uIixpdGVyYXRpb24sIkRldmlhbmNlIE9sZCIsZGV2aWFuY2VPbGQsIkRldmlhbmNlIiwgZGV2aWFuY2UsIlxuIikKfQpgYGAKCiMjIyBWYXJpYW5jZSAkXGJldGEkPwoKJCRcU2lnbWFfe1xiZXRhfT1cbGVmdChcbWF0aGJme1h9XlRcbWF0aGJme1d9IFxtYXRoYmZ7WH1ccmlnaHQpXnstMX0kJAoKYGBge3J9CnZhckJldGE9c29sdmUodCh4aGxwKSUqJWRpYWcodyklKiV4aGxwKQpgYGAKCiMjIyBDb21wYXJpc29uIHdpdGggR0xNIGZpdAoKVXNlIC0xIGJlY2F1c2UgaW50ZXJjZXB0IGlzIGFscmVhZHkgaW4geGhscApgYGB7cn0KZ2xtZml0PWdsbSh5fi0xK3hobHAsZmFtaWx5PXBvaXNzb24pCmNvbXA9ZGF0YS5mcmFtZShnbG1maXQ9YyhnbG1maXQkZGV2aWFuY2UsZ2xtZml0JGNvZWYsc3VtbWFyeShnbG1maXQpJGNvZWZbLDJdKSxvdXJGaXQ9YyhkZXZpYW5jZSxiZXRhcyxzcXJ0KGRpYWcodmFyQmV0YSkpKSkKcm93Lm5hbWVzKGNvbXApPWMoImRldmlhbmNlIixwYXN0ZSgiYmV0YSIsMTozLHNlcD0iIikscGFzdGUoInNlIiwxOjMsc2VwPSIiKSkKY29tcApgYGAKCgojIyBIeXBvdGhlc2lzIHRlc3Rpbmc6IExhcmdlIHNhbXBsZSB0aGVvcnkKCiMjIyBXYWxkIHRlc3QKCi0gRm9sbG93cyBpbW1lZGlhdGVseSBmcm9tIHRoZSBpbmZvcm1hdGlvbiBtYXRyaXggZm9yIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMKIFxbSShcYm9sZHN5bWJvbHtcYmV0YX0pID0gXG1hdGhiZntYfV5UXG1hdGhiZntXWH1cXSAKIAogc28gbGFyZ2Ugc2FtcGxlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRvciAkXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19JCBpcyBtdWx0aXZhcmlhdGUgbm9ybWFsClxbClxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSBcc2ltIE1WTlxsZWZ0W1xib2xkc3ltYm9se1xiZXRhfSxcbGVmdChcbWF0aGJme1h9XlRcbWF0aGJme1dYfVxyaWdodCleey0xfVxyaWdodF0KXF0KCldlIGNhbiBwZXJmb3JtIGEgV2FsZCBUZXN0IGZvciBhIHNpbmdsZSBtb2RlbCBwYXJhbWV0ZXIgCgokJApXID0gXGZyYWN7XGhhdFxiZXRhX219e1xoYXR7XHRleHR7c2V9fV97XGhhdFxiZXRhX219fSBcYXBwcm94IE4oMCwxKVx2ZXJ0IEhfMAokJAp0byB0ZXN0IGZvciAKCiQkCkhfMDogXGJldGFfcD0wIFxsZWZ0cmlnaHRhcnJvdyBIXzE6XGJldGFfcFxuZXEwCiQkCgoKQWdhaW4sIHdlIGNhbiBhbHNvIGFzc2VzcyBjb250cmFzdHMhIEluZGVlZCwgbGluZWFyIGNvbWJpbmF0aW9ucyBvZiBtb2RlbCBwYXJhbWV0ZXIgZXN0aW1hdG9ycyBhbHNvIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIAoKJCQKXG1hdGhiZntMfV5UXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19IFxzaW0gTlxsZWZ0W1xtYXRoYmZ7TH1eVFxib2xkc3ltYm9se1xiZXRhfSxcbWF0aGJme0x9XlRcaGF0e1xib2xkc3ltYm9se1xTaWdtYX19X3tcaGF0e1xib2xkc3ltYm9se1xiZXRhfX19XG1hdGhiZntMfVxyaWdodF0KJCQKCldpdGggJFxtYXRoYmZ7TH0kIGEgdmVjdG9yIGZvciBhIHNpbmdsZSBjb250cmFzdC4gCgokJApXID0gXGZyYWN7XG1hdGhiZntMfV5UXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19fXtcaGF0e1x0ZXh0e3NlfX1fe1xtYXRoYmZ7TH1eVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fX19IFxhcHByb3ggTigwLDEpXHZlcnQgSF8wCiQkCnRlc3RpbmcgZm9yIAokJApIXzA6IFxtYXRoYmZ7TH1eVFxib2xkc3ltYm9se1xiZXRhfT0wIFxsZWZ0cmlnaHRhcnJvdyBIXzE6XG1hdGhiZntMfV5UXGJvbGRzeW1ib2x7XGJldGF9XG5lcTAKJCQKCldlIGNhbiBhbHNvIHRlc3QgZm9yIG11bHRpcGxlIGNvbnRyYXN0cyBzaW11bHRhbmVvdXNseSwgZS5nLiBieSBhc3N1bWluZyB0aGF0IG11bHRpcGxlIG1vZGVsIHBhcmFtZXRlcnMgYXJlIHplcm8uIFN1cHBvc2UgdGhhdCAkXG1hdGhiZntMfSQgaXMgdGhlIGNvbnRyYXN0IG1hdHJpeCB0aGF0IGNvcnJlc3BvbmRzIHRlc3RpbmcgZm9yICRjJCBtb2RlbCBwYXJhbWV0ZXJzLCBzaW11bHRhbmVvdXNseS4gIFRoZW4gCgokJApcbWF0aGJme0x9XlRcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gXHNpbSBNVk5cbGVmdFtcbWF0aGJme0x9XlRcYm9sZHN5bWJvbHtcYmV0YX0sXG1hdGhiZntMfV5UXGhhdHtcYm9sZHN5bWJvbHtcU2lnbWF9fV97XGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19fVxtYXRoYmZ7TH1ccmlnaHRdCiQkCgphbmQgCgokJCAKVyA9IFxtYXRoYmZ7TH1eVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fVxsZWZ0KFxtYXRoYmZ7TH1eVFxoYXR7XGJvbGRzeW1ib2x7XFNpZ21hfX1fe1xoYXR7XGJvbGRzeW1ib2x7XGJldGF9fX1cbWF0aGJme0x9XHJpZ2h0KV57LTF9XGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19XG1hdGhiZntMfSBcc2ltIFxjaGleMl9jXHZlcnQgSF8wCiQkCnRvIHRlc3QgZm9yIAoKJCQKSF8wOiBcbWF0aGJme0x9XlRcYm9sZHN5bWJvbHtcYmV0YX09XG1hdGhiZnswfSBcbGVmdHJpZ2h0YXJyb3cgSF8xOlxtYXRoYmZ7TH1eVFxib2xkc3ltYm9se1xiZXRhfT1cbWF0aGJmezB9XG5lcTAKJCQKCkluIGdlbmVyYWwsIHdoZW4gd2UgdGVzdCBmb3IgJGNcZ2VxMSQgY29udHJhc3RzLCB0aGVuIHRoZSB0ZXN0IHN0YXRpc3RpYyAkV+KIvFxjaGleMl9yfEhfMCQsIHdpdGggJHIkIHRoZSByYW5rIG9mIHRoZSBjb250cmFzdCBtYXRyaXguCgoKIyMjICBMaWtlbGlob29kIHJhdGlvIHRlc3QKClRoZSBsaWtlbGlob29kIHJhdGlvIHRlc3QgKExSVCkgbWVhc3VyZXMgdGhlIGRpc2NyZXBhbmN5IGluIGxvZy1saWtlbGlob29kIGJldHdlZW4gb3VyIGN1cnJlbnQgbW9kZWwgKHNvbWV0aW1lcyBhbHNvIHJlZmVycmVkIHRvIGFzIGZ1bGwgbW9kZWwpIGFuZCBhIHJlZHVjZWQgbW9kZWwgKHNvbWV0aW1lcyBhbHNvIHJlZmVycmVkIHRvIGFzIG51bGwgb3IgYWx0ZXJuYXRpdmUgbW9kZWwpLiAKClRoZSByZWR1Y2VkIG1vZGVsIG11c3QgYmUgbmVzdGVkIGluIChhbmQgdGhlcmVmb3JlIG9mIGxvd2VyIGRpbWVuc2lvbiBhcyBjb21wYXJlZCB0bykgdGhlIGZ1bGwgbW9kZWwuIAoKV2hpbGUgYWRkaW5nIG1vcmUgY292YXJpYXRlcyB3aWxsIGFsd2F5cyBleHBsYWluIG1vcmUgdmFyaWFiaWxpdHkgaW4gb3VyIHJlc3BvbnNlIHZhcmlhYmxlLCB0aGUgTFJUIHRlc3RzIHdoZXRoZXIgdGhpcyBpcyBhY3R1YWxseSBzaWduaWZpY2FudC4gCgpGb3IgZXhhbXBsZSwgaW4gdGhlIGV4YW1wbGUgb2YgZ2VuZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiZXR3ZWVuIGhlYWx0aHkgdmVyc3VzIHR1bW9yYWwgdGlzc3VlLCB0aGUgZnVsbCBtb2RlbCBjb3VsZCBiZSBhIEdMTSB3aGVyZSB0aGUgbWVhbiBpcyBtb2RlbGVkIGFjY29yZGluZyB0byBhbiBpbnRlcmNlcHQgYW5kIGEgdGlzc3VlIGluZGljYXRvciB2YXJpYWJsZSAoaGVhbHRoeSAvIHR1bW9yKSwgd2hpbGUgdGhlIGFsdGVybmF0aXZlIG1vZGVsIGNvdWxkIGJlIGEgR0xNIHdpdGgganVzdCBhbiBpbnRlcmNlcHQuIEluZGVlZCwgaWYgdGhlIGdlbmUgaXMgc2ltaWxhcmx5IGV4cHJlc3NlZCBiZXR3ZWVuIGhlYWx0aHkgYW5kIHR1bW9yIHRpc3N1ZSwgdGhlIGxvZy1saWtlbGlob29kIG9mIHRoZSBhbHRlcm5hdGl2ZSBtb2RlbCB3aWxsIGRlY3JlYXNlIG9ubHkgYSBsaXR0bGUgYXMgY29tcGFyZWQgdG8gdGhlIGZ1bGwgbW9kZWwuIAoKQXMgdGhlIG5hbWUgc3VnZ2VzdHMsIHRoZSBsaWtlbGlob29kIHJhdGlvIHRlc3QgYXNzZXNzZXMgd2hldGhlciB0aGUgcmF0aW8gb2YgdGhlIGxvZy1saWtlbGlob29kcyBwcm92aWRlcyBzdWZmaWNpZW50IGV2aWRlbmNlIGZvciBhIHdvcnNlIGZpdCBvZiB0aGUgYWx0ZXJuYXRpdmUgdmVyc3VzIGZ1bGwgbW9kZWwKCiAkJAogXGxhbWJkYT0yXGxlZnRbbChcaGF0e1xib2xkc3ltYm9se1xiZXRhfX1fXHRleHR7ZnVsbH0pLTJsKFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fV9cdGV4dHswfSlccmlnaHRdCiAkJCAKIAogICAgQXN5bXB0b3RpY2FsbHksIHVuZGVyIHRoZSBudWxsIGh5cG90aGVzaXMgaXQgY2FuIGJlIHNob3duIHRoYXQKICAgJCQgTCBcc2ltIFxjaGlfY14yIHwgSF8wLCAkJAogICB3aXRoICRjJCB0aGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgZHJvcHBlZCBpbiB0aGUgYWx0ZXJuYXRpdmUgbW9kZWwgdmVyc3VzIHRoZSBmdWxsIG1vZGVsLiAKICAgCkxldCAkXG1hdGhiZntDfSQgZGVub3RlIHRoZSAkYyBcdGltZXMgcCQgY29udHJhc3QgbWF0cml4IGRlbm90aW5nIHRoZSBjb250cmFzdCBmb3IgdGhlIHBhcmFtZXRlcnMgYmVpbmcgZHJvcHBlZCwgdGhlIG51bGwgYW5kIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgYXJlIGFzIGluIHRoZSBXYWxkIHRlc3Qgc2V0dGluZzoKICAgJCQgSF8wOiBcbWF0aGJme0N9IFxiZXRhID0gMCQkCiAgICQkIEhfMTogXG1hdGhiZntDfSBcYmV0YSBcbmUgMCQkCiAgIAoKIC0gSXQgaXMgaW1wb3J0YW50IHRvIGtlZXAgaW4gbWluZCB0aGF0IHN0YW5kYXJkIHN0YXRpc3RpY2FsIGluZmVyZW5jZSB0aGVvcnkgaW4gR0xNcyB3b3JrcyAqKmFzeW1wdG90aWNhbGx5IGluIHRlcm1zIG9mIHRoZSBzYW1wbGUgc2l6ZSoqLgoKVGh1cyB3ZSBuZWVkIG1hbnkgZGF0YSBwb2ludHMgaW4gb3JkZXIgZm9yIHRoZSB0aGVvcnkgdG8gaG9sZCBpbiBwcmFjdGljZS4gSW4gb3JkZXIgZm9yIHRoZSAkcCQtdmFsdWVzIHRvIGJlIGNvcnJlY3QsIG91ciBwYXJhbWV0cmljIChkaXN0cmlidXRpb25hbCkgYXNzdW1wdGlvbnMgYXMgd2VsbCBhcyB0aGUgaW5kZXBlbmRlbmNlIGFzc3VtcHRpb24sIG11c3QgYWxzbyBob2xkLgoKIC0gSW4gYnVsayBSTkEtc2VxLCB3ZSBhcmUgb2Z0ZW4gd29ya2luZyB3aXRoIGEgbGltaXRlZCBudW1iZXIgb2Ygc2FtcGxlcyBhbmQgc28gd2UgdHlwaWNhbGx5IGRvIG5vdCBleHBlY3QgYXN5bXB0b3RpYyB0aGVvcnkgdG8gaG9sZCB5ZXQuIEluIHNpbmdsZS1jZWxsIFJOQS1zZXEsIHdlIG9mdGVuIHBlcmZvcm0gc2V2ZXJhbCBwcmVwcm9jZXNzaW5nIHN0ZXBzIGJlZm9yZSBjYWxjdWxhdGluZyAkcCQtdmFsdWVzIGZvciBlYWNoIGdlbmUgYW5kIHNvIHdlIG1heSBiZSAndXNpbmcgdGhlIGRhdGEgbXVsdGlwbGUgdGltZXMnLiBSYXRoZXIgdGhhbiBhdHRhY2hpbmcgc3Ryb25nIHByb2JhYmlsaXN0aWMgaW50ZXJwcmV0YXRpb25zIHRvIHRoZSAkcCQtdmFsdWVzLCB3ZSB0aGVyZWZvcmUgYWR2aWNlIHRvIHZpZXcgdGhlICRwJC12YWx1ZXMgc2ltcGx5IGFzIHVzZWZ1bCBudW1lcmljYWwgc3VtbWFyaWVzIGZvciByYW5raW5nIHRoZSBnZW5lcyBmb3IgZnVydGhlciBpbnNwZWN0aW9uIGluIGdlbm9taWNzIGFwcGxpY2F0aW9ucy4KIApgYGB7ciBlY2hvPUZBTFNFLCBmaWcud2lkdGg9MC41LCBmaWcuY2FwdGlvbj0iRGlmZmVyZW5jZSBiZXR3ZWVuIFdhbGQgYW5kIExSVCB0ZXN0IixmaWcuYWxpZ249J2NlbnRlcid9CmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvbGlrVGVzdHMucG5nIikKYGBgCiAKCiMgRWRnZVIKCltNY0NhcnRoeSBhbmQgU215dGgsIDIwMTJdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzMzNzg4ODIvKQoKYGBge3IgZmlnLndpZHRoPS45OSwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vZmlncy9NY0NhcnRoeVNteXRoMjAxMi5wbmciKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9Ljk5LCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9maWdzL01jQ2FydGh5U215dGgyMDEyX3Rlc3RpbmcucG5nIikKYGBgCgojIEVkZ2VSIC0gUXVhc2ktTGlrZWxpaG9vZAoKW0x1bmQgZXQgYWwuIDIwMTJdKGh0dHBzOi8vcHVibGljYXRpb25zLndlaGkuZWR1LmF1L2RvY3VtZW50YXNwZGYvMjU2LnBkZikKCkZvciBxdWFzaS1saWtlbGlob29kIHdlIGRvIG5vdCBzcGVjaWZ5IHRoZSBmdWxsIGRpc3RyaWJ1dGlvbiwgb25seSB0aGUgZmlyc3QgdHdvIG1vbWVudHM6IHRoZSBtZWFuIGFuZCB2YXJpYW5jZS4KCiQkClxsZWZ0XHsKXGJlZ2lue2FycmF5fXtsY2x9CkVbeV97aWd9XHZlcnQgXG1hdGhiZnt4fV97aWd9XSY9JlxtdV97aWd9XFwKbG9nKFxtdV97aWd9KSY9JlxldGFfe2lnfVxcClxldGFfe2lnfSY9JiBcbWF0aGJme3h9X3tpfV5UXGJvbGRzeW1ib2x7XGJldGF9KyBcbG9nIE5faVxcClx0ZXh0e1Zhcn1beV97aWd9XHZlcnQgXG1hdGhiZnt4fV97aWd9XSY9JlxzaWdtYV4yX2dcbGVmdChcbXVfe2lnfStccGhpXG11X3tpZ31eMlxyaWdodCkKXGVuZHthcnJheX1ccmlnaHQuCiQkCgpXZSB3aWxsIGxvb2stdXAgdGhlIGRldGFpbHMgaW4gdGhlIHBhcGVyLiAKCgoKCiMgTGltbWEgLSBWb29tCgpbTGF3IGV0IGFsLiAoMjAxMykuIEdlbm9tZSBCaW9sb2d5XShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9nYi0yMDE0LTE1LTItcjI5KQoKLSBDb3VudCBtb2RlbHMgdnMgdHJhbnNmb3JtYXRpb246IFBvaXNzb24gY291bnRzLCAkXHNxcnQoeSkkICBzdGFiaWxpc2VzIHRoZSB2YXJpYW5jZSwgaW5zdWZmaWNpZW50IGZvciBuZWdhdGl2ZSBiaW5vbWlhbC4gCkxvZyB0cmFuc2Zvcm1hdGlvbjogdGhlIHRyYW5zZm9ybWVkIGRhdGEgYXJlIHN0aWxsIGhldGVyb3NjZWRhc3RpYy4kXHJpZ2h0YXJyb3ckIGxpbW1hLXZvb20KLSBVc2Ugbm9ybWFsaXplZCBsb2ctY3BtIExpbW1hIHBpcGVsaW5lIGZvciBzZXF1ZW5jaW5nCgpQcm9ibGVtOiBjb3VudHMgaGF2ZSBhIG1lYW4gdmFyaWFuY2UgcmVsYXRpb25zaGlwOiBoZXRlcm9zY2VkYXN0aWMKCkhvdyBkbyB3ZSBkZWFsIHdpdGggaGV0ZXJvc2NlZGFzdGljaXR5IGluIHRyYWRpdGlvbmFsIGxpbmVhciBtb2RlbHM/CgpUd28gc3RhZ2UgYXBwcm9hY2g6CgoxLiBTdGFnZSBJCgogIC0gT0xTCiAgLSBFc3RpbWF0ZSB2YXJpYW5jZXMgYXQgZWFjaCBkYXRhIHBvaW50CiAgLSBVc2UgdmFyaWFuY2VzIGFzIHdlaWdodHM6ICRXPVx0ZXh0e2RpYWd9WzEvXGhhdFxzaWdtYV9pXjJdJAoKMi4gU3RhZ2UgSUkgV0xTICRcdGV4dHthcmdtaW59X3tcYm9sZHN5bWJvbHtcYmV0YX19IFx7IChcbWF0aGJme3l9LVxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0pXlRcbWF0aGJme1d9IChcbWF0aGJme3l9LVxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0pXH0kCgoKUG9ydCB0aGlzIGlkZWEgdG8gUk5BLXNlcSBwaXBlbGluZQoKYGBge3IgZmlnLndpZHRoPS45OSwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vZmlncy9saW1tYVZvb21QYXBlckNodW5rMS5wbmciKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0uOTksIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL2ZpZ3MvbGltbWFWb29tUGFwZXJDaHVuazIucG5nIikKYGBgCgpgYGB7ciBmaWcud2lkdGg9Ljk5LCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9maWdzL2xpbW1hVm9vbVBhcGVyRmlnMi5wbmciKQpgYGAKCgoKIyBJbmRlcGVuZGVudCBGaWx0ZXJpbmcKCkluZGVwZW5kZW50IGZpbHRlcmluZyBpcyBhIHN0cmF0ZWd5IHRvIHJlbW92ZSBmZWF0dXJlcyAoaW4gdGhpcyBjYXNlLCBnZW5lcykgcHJpb3IgdG8gdGhlIGFuYWx5c2lzLiBSZW1vdmFsIG9mIHRoZXNlIGZlYXR1cmVzIG1heSBsb3dlciB0aGUgbXVsdGlwbGUgdGVzdGluZyBjb3JyZWN0aW9uIGZvciBvdGhlciBnZW5lcyB0aGF0IHBhc3MgdGhlIGZpbHRlci4gV2UgdHJ5IHRvIHJlbW92ZSBnZW5lcyB0aGF0IGhhdmUgYSBsb3cgcG93ZXIgdG8gYmUgZm91bmQgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCwgYW5kL29yIHRoYXQgYXJlIGJpb2xvZ2ljYWxseSBsZXNzIHJlbGV2YW50LiAKQSBjb21tb24gZmlsdGVyaW5nIHN0cmF0ZWd5IGlzIHRvIHJlbW92ZSBnZW5lcyB3aXRoIGEgZ2VuZXJhbGx5IGxvdyBleHByZXNzaW9uLCBhcyBsb3cgY291bnRzIGhhdmUgbG93ZXIgcmVsYXRpdmUgdW5jZXJ0YWludHkgKGhlbmNlIGxvd2VyIHN0YXRpc3RpY2FsIHBvd2VyKSwgYW5kIG1heSBiZSBjb25zaWRlcmVkIGJpb2xvZ2ljYWxseSBsZXNzIHJlbGV2YW50LgoKSW1wbGVtZW50YXRpb24gaW4gZWRnZVIuIAoKYGBge3J9Cj9maWx0ZXJCeUV4cHIKYGBgCgpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KGxpbW1hKQogIGxpYnJhcnkoZWRnZVIpCiAgbGlicmFyeShERVNlcTIpCn0pCgpkZHMgPC0gbWFrZUV4YW1wbGVERVNlcURhdGFTZXQoKQpzaW1Db3VudHMgPC1jb3VudHMoZGRzKQpncm91cCA8LSBkZHMkY29uZGl0aW9uCmRnZSA8LSBlZGdlUjo6REdFTGlzdChzaW1Db3VudHMpCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofmdyb3VwKQprZWVwIDwtIGZpbHRlckJ5RXhwcihkZ2UsIGRlc2lnbikKdGFibGUoa2VlcCkKYGBgCgpgYGB7cn0KbGliLnNpemUgPC0gZGdlJHNhbXBsZXMkbGliLnNpemUgKiBkZ2Ukc2FtcGxlcyRub3JtLmZhY3RvcnMKY3BtTWluQ291bnQgPC0gMTAvbWVkaWFuKGxpYi5zaXplKSoxZTYKc3VtbWFyeShncm91cCkKbWluU2FtcFNpemUgPC0gbWluKHN1bW1hcnkoZ3JvdXApKQptaW5TYW1wU2l6ZQprZWVwIDwtIHJvd1N1bXMoY3BtKGRnZSkgPiBjcG1NaW5Db3VudCkgPj0gbWluU2FtcFNpemUKdGFibGUoa2VlcCkKYGBgCgpgYGB7cn0KbGV2ZXJhZ2UgPC0gZGVzaWduJSolIHNvbHZlKHQoZGVzaWduKSUqJWRlc2lnbiklKiV0KGRlc2lnbikgJT4lZGlhZygpCjEvbGV2ZXJhZ2UKbWluKDEvbGV2ZXJhZ2UpCmBgYAoKSW5kZXBlbmRlbnQgZmlsdGVyaW5nIGhhcyBiZWVuIGZvcm1hbGl6ZWQgYnkgW0JvdXJnb24gKmV0IGFsLiogKDIwMTApXShodHRwczovL3d3dy5wbmFzLm9yZy9jb250ZW50LzEwNy8yMS85NTQ2KS4KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPXBhc3RlKCJGaWd1cmUgMSBmcm9tIEJvdXJnb24gKmV0IGFsLiogKDIwMTApLiIpLCBmaWcud2lkdGg9MC45OX0KIyBBbGwgZGVmYXVsdHMKaW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9pbmRlcGVuZGVudEZpbHRlcmluZy5wbmciKQpgYGAKCiAtLS0KClRoZSBjb25jZXB0IG9mIGluZGVwZW5kZW50IGZpbHRlcmluZyBjYW4gYmUgc3VtbWFyaXplZCBhcyBmb2xsb3dzOgoKIC0gRm9yIGVhY2ggZmVhdHVyZSB3ZSBjYWxjdWxhdGUgdHdvIHN0YXRpc3RpY3MsICRTX0YkIGFuZCAkU19UJCwgcmVzcGVjdGl2ZWx5IHVzZWQgZm9yIHR3byBzdGFnZXM6IGZpbHRlcmluZyBhbmQgdGVzdGluZyAoZS5nLiwgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24pLgogLSBJbiBvcmRlciBmb3IgYSBmZWF0dXJlIHRvIGJlIGRlZW1lZCBzaWduaWZpY2FudCwgYm90aCBvZiBpdHMgc3RhdGlzdGljcyBtdXN0IGJlIGdyZWF0ZXIgdGhhbiBzb21lIGN1dC1vZmYuCiAtIFdlIHdhbnQgdG8gY29udHJvbCB0aGUgdHlwZSBJIGVycm9yIHJhdGUgb2YgdGhlIHNlY29uZCBzdGFnZSAodGVzdGluZykuIEJ1dCBub3RlIHRoYXQgKip0aGUgc2Vjb25kIHN0YWdlIGlzIGNvbmRpdGlvbmFsIG9uIHRoZSBmaXJzdCBzdGFnZSoqLCBhcyB3ZSBvbmx5IHRlc3QgZmVhdHVyZXMgcGFzc2luZyB0aGUgZmlsdGVyLCBhbmQgYmFzaWNhbGx5IGlnbm9yZSB0aGUgZmFjdCB0aGF0IGZpbHRlcmluZyB3YXMgcGVyZm9ybWVkLiBJbmRlZWQsIG9uZSBjcml0aWNpc20gaXMgdGhhdCBjb21wdXRpbmcgYW5kIGNvcnJlY3RpbmcgdGhlICRwJC12YWx1ZXMgYXMgaWYgZmlsdGVyaW5nIGhhZCBub3QgYmVlbiBwZXJmb3JtZWQgbWF5IGxlYWQgdG8gb3Zlcm9wdGltaXN0aWMgYWRqdXN0ZWQgJHAkLXZhbHVlcy4KIC0gW0JvdXJnb24gKmV0IGFsLiogKDIwMTApXShodHRwczovL3d3dy5wbmFzLm9yZy9jb250ZW50LzEwNy8yMS85NTQ2KSBzaG93IHRoYXQgZmlsdGVyaW5nIGlzIG9ubHkgYXBwcm9wcmlhdGUgKGkuZS4sIGRvZXMgbm90IGluZmxhdGUgdHlwZSBJIGVycm9yIHJhdGUpIGlmIHRoZSBjb25kaXRpb25hbCBudWxsIGRpc3RyaWJ1dGlvbiBvZiB0ZXN0IHN0YXRpc3RpY3MgZm9yIGZlYXR1cmVzIHBhc3NpbmcgdGhlIGZpbHRlciBpcyB0aGUgc2FtZSBhcyB0aGUgdW5jb25kaXRpb25hbCBudWxsIGRpc3RyaWJ1dGlvbi4gVGhlcmVmb3JlLCAqKmZpbHRlcmluZyBpcyBhcHByb3ByaWF0ZSBpZiB0aGUgc3RhdGlzdGljIHVzZWQgZm9yIGZpbHRlcmluZyBpcyBpbmRlcGVuZGVudCBvZiB0aGUgc3RhdGlzdGljIHVzZWQgZm9yIHRlc3RpbmcgdW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcyoqLgogCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPXBhc3RlKCJGaWd1cmUgMiBmcm9tIEJvdXJnb24gKmV0IGFsLiogKDIwMTApLiIpLCBmaWcud2lkdGg9MC45OSxmaWcuYWxpZ249J2NlbnRlcid9CiMgQWxsIGRlZmF1bHRzCmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvaW5kZXBlbmRlbnRGaWx0ZXJpbmcyLnBuZyIpCmBgYAogIC0tLQogIApMZXQncyB0cnkgYSBjb3VwbGUgb2YgZXhhbXBsZXMgdG8gZ2V0IHNvbWUgaW50dWl0aW9uIHVzaW5nIHNpbXVsYXRlZCBkYXRhLgoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KERFU2VxMikpCnNldC5zZWVkKDI0KQpkZHMgPC0gREVTZXEyOjptYWtlRXhhbXBsZURFU2VxRGF0YVNldCgpCnNpbUNvdW50cyA8LSBjb3VudHMoZGRzKQpncm91cCA8LSBkZHMkY29uZGl0aW9uCmBgYAoKIyMgQSBEZXBlbmRlbnQgVGVzdCBTdGF0aXN0aWMKCmBgYHtyfQpmaWx0ZXJTdGF0RWZmZWN0U2l6ZSA8LSBhYnMocm93TWVhbnMoc2ltQ291bnRzWyxncm91cCA9PSAiQSJdKSAtIHJvd01lYW5zKHNpbUNvdW50c1ssZ3JvdXAgPT0gIkIiXSkpCnRlc3RTdGF0IDwtIGdlbmVmaWx0ZXI6OnJvd3R0ZXN0cyhzaW1Db3VudHMsIGdyb3VwKQoKIyMgdW5jb25kaXRpb25hbCBkaXN0cmlidXRpb24KcGxvdChkZW5zaXR5KHRlc3RTdGF0JHN0YXRpc3RpYywgbmEucm09VFJVRSksCiAgICAgeGxhYiA9ICJUZXN0IHN0YXRpc3RpYyIsCiAgICAgbWFpbiA9ICJVbmNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiIpCgojIyBjb25kaXRpb25hbCBkaXN0cmlidXRpb246IHZlcnkgZGlmZmVyZW50IQptZWFuKGZpbHRlclN0YXRFZmZlY3RTaXplID4gMSkKaGlzdChmaWx0ZXJTdGF0RWZmZWN0U2l6ZSwgYnJlYWtzPTQwKQphYmxpbmUodj0xLCBjb2w9InJlZCIpCmtlZXBFZmZlY3RTaXplIDwtIGZpbHRlclN0YXRFZmZlY3RTaXplID4gMQpwbG90KGRlbnNpdHkodGVzdFN0YXQkc3RhdGlzdGljW2tlZXBFZmZlY3RTaXplXSwgbmEucm09VFJVRSksCiAgICAgeGxhYiA9ICJUZXN0IHN0YXRpc3RpYyIsCiAgICAgbWFpbiA9ICJDb25kaXRpb25hbCBkaXN0cmlidXRpb24iKQpgYGAKCgojIyBBbiBJbmRlcGVuZGVudCBUZXN0IFN0YXRpc3RpYwoKYGBge3J9CmZpbHRlclN0YXRHbG9iYWxNZWFuIDwtIHJvd01lYW5zKHNpbUNvdW50cykKbWVhbihmaWx0ZXJTdGF0R2xvYmFsTWVhbiA+IDUpICMgd2UgcmVtb3ZlIGEgc2ltaWxhciBmcmFjdGlvbgprZWVwR2xvYmFsTWVhbiA8LSBmaWx0ZXJTdGF0R2xvYmFsTWVhbiA+IDUKCiMjIHVuY29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uCnBsb3QoZGVuc2l0eSh0ZXN0U3RhdCRzdGF0aXN0aWMsIG5hLnJtPVRSVUUpLAogICAgIHhsYWIgPSAiVGVzdCBzdGF0aXN0aWMiLAogICAgIG1haW4gPSAiVW5jb25kaXRpb25hbCBkaXN0cmlidXRpb24iKQoKIyMgY29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uOiB0aGUgc2FtZS4KcGxvdChkZW5zaXR5KHRlc3RTdGF0JHN0YXRpc3RpY1trZWVwR2xvYmFsTWVhbl0sIG5hLnJtPVRSVUUpLAogICAgIHhsYWIgPSAiVGVzdCBzdGF0aXN0aWMiLAogICAgIG1haW4gPSAiQ29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uIikKYGBgCgoKIyBOb3JtYWxpemF0aW9uCgpOb3JtYWxpemF0aW9uIGlzIG5lY2Vzc2FyeSB0byBjb3JyZWN0IGZvciBzZXZlcmFsIHNvdXJjZXMgb2YgdGVjaG5pY2FsIHZhcmlhdGlvbjoKCiAtICoqRGlmZmVyZW5jZXMgaW4gc2VxdWVuY2luZyBkZXB0aCoqIGJldHdlZW4gc2FtcGxlcy4gU29tZSBzYW1wbGVzIGdldCBzZXF1ZW5jZWQgZGVlcGVyIGluIHRoZSBzZW5zZSB0aGF0IHRoZXkgY29uc2lzdCBvZiBtb3JlIChtYXBwZWQpIHJlYWRzIGFuZCB0aGVyZWZvcmUgY2FuIGJlIGNvbnNpZGVyZWQgdG8gY29udGFpbiBhIGhpZ2hlciBhbW91bnQgb2YgaW5mb3JtYXRpb24sIHdoaWNoIHdlIHNob3VsZCBiZSB0YWtpbmcgaW50byBhY2NvdW50LiBJbiBhZGRpdGlvbiwgaWYgYSBzYW1wbGUgaXMgc2VxdWVuY2VkIGRlZXBlciwgaXQgaXMgbmF0dXJhbCB0aGF0IHRoZSBjb3VudHMgZm9yIGVhY2ggZ2VuZSB3aWxsIGJlIGhpZ2hlciwgamVvcGFyZGl6aW5nIGEgZGlyZWN0IGNvbXBhcmlzb24gb2YgdGhlIGV4cHJlc3Npb24gY291bnRzLgogLSAqKkRpZmZlcmVuY2VzIGluIFJOQSBwb3B1bGF0aW9uIGNvbXBvc2l0aW9uKiogYmV0d2VlbiBzYW1wbGVzLiBBcyBhbiBleHRyZW1lIGV4YW1wbGUsIHN1cHBvc2UgdGhhdCB0d28gc2FtcGxlcyBoYXZlIGJlZW4gc2VxdWVuY2VkIHRvIHRoZSBleGFjdCBzYW1lIGRlcHRoLiBPbmUgc2FtcGxlIGlzIGNvbnRhbWluYXRlZCBhbmQgaGFzIGEgdmVyeSBoaWdoIGNvbmNlbnRyYXRpb24gb2YgdGhlIGNvbnRhbWluYW50IGNETkEgYmVpbmcgc2VxdWVuY2VkLCBidXQgb3RoZXJ3aXNlIHRoZSB0d28gc2FtcGxlcyBhcmUgaWRlbnRpY2FsLiBTaW5jZSB0aGUgY29udGFtaW5hbnQgd2lsbCBiZSB0YWtpbmcgdXAgYSBzaWduaWZpY2FudCBwcm9wb3J0aW9uIG9mIHRoZSByZWFkcyBiZWluZyBzZXF1ZW5jZWQsIHRoZSBjb3VudHMgd2lsbCBub3QgYmUgZGlyZWN0bHkgY29tcGFyYWJsZSBiZXR3ZWVuIHRoZSBzYW1wbGVzLiBIZW5jZSwgd2UgbWF5IGFsc28gd2FudCB0byBjb3JyZWN0IGZvciBkaWZmZXJlbmNlcyBpbiB0aGUgY29tcG9zaXRpb24gb2YgdGhlIFJOQSBwb3B1bGF0aW9uIG9mIHRoZSBzYW1wbGVzLgogLSAqKk90aGVyIHRlY2huaWNhbCB2YXJpYXRpb24qKiBzdWNoIGFzIHNhbXBsZS1zcGVjaWZpYyBHQy1jb250ZW50IG9yIHRyYW5zY3JpcHQgbGVuZ3RoIGVmZmVjdHMgbWF5IGFsc28gYmUgYWNjb3VudGVkIGZvci4KIAogCmBgYHtyfQpkYXRhKCJwYXJhdGh5cm9pZEdlbmVzU0UiLCBwYWNrYWdlPSJwYXJhdGh5cm9pZFNFIikKc2UxIDwtIHBhcmF0aHlyb2lkR2VuZXNTRQpybShwYXJhdGh5cm9pZEdlbmVzU0UpCgpjb2xEYXRhKHNlMSkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgZmlsdGVyKGR1cGxpY2F0ZWQoZXhwZXJpbWVudCkpIApgYGAKClRoZXJlIGFyZSB0ZWNobmljYWwgcmVwZWF0cyBpbiB0aGUgZGF0YS4gCgpXZSBtZW50aW9uZWQgcHJldmlvdXMgbGVjdHVyZXMgdGhhdCB3ZSBjYW4gc3VtIG92ZXIgdGVjaG5pY2FsIHJlcGVhdHMsIGJlY2F1c2UgdGVjaGljYWwgcmVwZWF0cyBhcmUgUG9pc3NvbiBhbmQgdGhlIHN1bSBvZiB0d28gUG9pc3NvbiB2YXJpYWJsZXMgaXMgYWdhaW4gUG9pc3Nvbi4gCgpgYGB7cn0KZHVwRXhwcyA8LSBjb2xEYXRhKHNlMSkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgZmlsdGVyKGR1cGxpY2F0ZWQoZXhwZXJpbWVudCkpICAlPiUgCiAgcHVsbChleHBlcmltZW50KQoKY291bnRzIDwtIGFzc2F5cyhzZTEpJGNvdW50cwpuZXdDb3VudHMgPC0gY291bnRzCmNkIDwtIGNvbERhdGEoc2UxKQpmb3Ioc3MgaW4gMTpsZW5ndGgoZHVwRXhwcykpewogICMgY2hlY2sgd2hpY2ggc2FtcGxlcyBhcmUgZHVwbGljYXRlcwogIHJlbGV2YW50SWQgPC0gd2hpY2goY29sRGF0YShzZTEpJGV4cGVyaW1lbnQgPT0gZHVwRXhwc1tzc10pCiAgIyBzdW0gY291bnRzCiAgbmV3Q291bnRzWyxyZWxldmFudElkWzFdXSA8LSByb3dTdW1zKGNvdW50c1sscmVsZXZhbnRJZF0pCiAgIyBrZWVwIHdoaWNoIGNvbHVtbnMgLyByb3dzIHRvIHJlbW92ZS4KICBpZihzcyA9PSAxKXsKICAgIHRvUmVtb3ZlIDwtIHJlbGV2YW50SWRbMl0KICB9IGVsc2UgewogICAgdG9SZW1vdmUgPC0gYyh0b1JlbW92ZSwgcmVsZXZhbnRJZFsyXSkKICB9Cn0KCiMgcmVtb3ZlIGFmdGVyIHN1bW1pbmcgY291bnRzIChvdGhlcndpc2UgSURzIGdldCBtaXhlZCB1cCkKbmV3Q291bnRzIDwtIG5ld0NvdW50c1ssLXRvUmVtb3ZlXQpuZXdDRCA8LSBjZFstdG9SZW1vdmUsXQoKIyBDcmVhdGUgbmV3IFN1bW1hcml6ZWRFeHBlcmltZW50CnNlIDwtIFN1bW1hcml6ZWRFeHBlcmltZW50KGFzc2F5cyA9IGxpc3QoImNvdW50cyIgPSBuZXdDb3VudHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IG5ld0NELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEgPSBtZXRhZGF0YShzZTEpKQoKdHJlYXRtZW50IDwtIGNvbERhdGEoc2UpJHRyZWF0bWVudAp0YWJsZSh0cmVhdG1lbnQpCmBgYAoKYGBge3J9CnFwbG90KGNvbFN1bXMoYXNzYXlzKHNlKSRjb3VudHMpLzFlNiwgZ2VvbT0iaGlzdG9ncmFtIiwgYmlucz0xMCxjb2w9ImJsYWNrIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHhsYWIoImxpYnNpemUgKG1pbGxpb24gcmVhZHMpIikKCnFwbG90KAogIGNvbERhdGEoc2UpJHRyZWF0bWVudDpjb2xEYXRhKHNlKSR0aW1lLAogIGNvbFN1bXMoYXNzYXlzKHNlKSRjb3VudHMpLzFlNixnZW9tPSJib3hwbG90IgogICkgKwogIHhsYWIoInRyZWF0bWVudCIpKwogIHlsYWIoImxpYnNpemUgKG1pbGxpb24gcmVhZHMpIikKCnFwbG90KAogIGNvbERhdGEoc2UpJHBhdGllbnQsCiAgY29sU3Vtcyhhc3NheXMoc2UpJGNvdW50cykvMWU2LGdlb209ImJveHBsb3QiCiAgKSArCiAgeGxhYigiUGF0aWVudCIpKwogIHlsYWIoImxpYnNpemUgKG1pbGxpb24gcmVhZHMpIikKYGBgCmBgYHtyfQptYTJTYW1wIDwtIGZ1bmN0aW9uKGNvdW50TXgpIHsKc3RvcGlmbm90KCJgY291bnRNeGAgaXMgbm90IGEgbWF0cml4IHdpdGggdHdvIGNvbHVtbnMiID0gbmNvbChjb3VudE14KSA9PSAyKQpBIDwtIGNvdW50TXggJT4lIGxvZzIgJT4lIHJvd01lYW5zCk0gPC0gY291bnRNeCAlPiUgbG9nMiAlPiUgYXBwbHkoLiwxLGRpZmYpCncgPC0gY291bnRNeFssMV09PW1pbihjb3VudE14WywxXSkgfCBjb3VudE14WywyXT09bWluKGNvdW50TXhbLDJdKQppZiAoYW55KHcpKSB7CiAgICAgICAgICAgIEFbd10gPC0gcnVuaWYoc3VtKHcpLCBtaW4gPSAtMSwgbWF4ID0gLjEpCiAgICAgICAgICAgIE1bd10gPC0gbG9nMihjb3VudE14W3csMl0gKyAxKSAtIGxvZzIoY291bnRNeFt3LDFdICsgMSkKfQpNQXBsb3QgPC0gcXBsb3QoQSwgTSwgY29sPXcpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwib3JhbmdlIikpICsKICB4bGFiKCJBOiBsb2cyIEF2ZXJhZ2UiKSArCiAgeWxhYigiTTogbG9nMiBGb2xkIENoYW5nZSIpCgpNQXBsb3QgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdD0wLHNsb3BlPTAsY29sPSJyZWQiKQp9CmBgYAoKTGV04oCZcyB0YWtlIGEgbG9vayBhdCBob3cgY29tcGFyYWJsZSBkaWZmZXJlbnQgcmVwbGljYXRlcyBhcmUgaW4gdGhlIENvbnRyb2wgY29uZGl0aW9uIGF0IDQ4aCBpbiBvdXIgZGF0YXNldC4gV2Ugd2lsbCBpbnZlc3RpZ2F0ZSB0aGlzIHVzaW5nIE1ELXBsb3RzIChtZWFuLWRpZmZlcmVuY2UgcGxvdHMgYXMgaW50cm9kdWNlZCBieSBEdWRvaXQgZXQgYWwuICgyMDAyKSksIGFsc28gc29tZXRpbWVzIHJlZmVycmVkIHRvIGFzIE1BLXBsb3RzLgoKYGBge3J9CmlkcyA8LSB3aGljaChjb2xEYXRhKHNlKSR0cmVhdG1lbnQgPT0iQ29udHJvbCIgJiBjb2xEYXRhKHNlKSR0aW1lID09ICI0OGgiKQppZHMKY29sU3Vtcyhhc3NheXMoc2UpJGNvdW50c1ssaWRzXSkgLyAxZTYKYGBgCgpgYGB7cn0KcGFpckNvbWIgPC0gY29tYm4oCiAgaWRzLCAKICBtPTIpCnBsb3RzIDwtIGFwcGx5KHBhaXJDb21iLDIsZnVuY3Rpb24oeCkgbWEyU2FtcChhc3NheShzZSlbLHhdKSArIGdndGl0bGUocGFzdGUoInNhbXBsZXMiLHhbMl0sInZzIiwgeFsxXSkpKQpkby5jYWxsKCJncmlkLmFycmFuZ2UiLGMocGxvdHMsbmNvbD0zKSkKYGBgCgpXZSBzZWUgY2xlYXIgYmlhcyBmb3Igc29tZSBwYWlyd2lzZSBjb21wYXJpc29ucy4gRm9yIGV4YW1wbGUsIGluIHRoZSBmaXJzdCBwbG90IGNvbXBhcmluZyBzYW1wbGUgOCB2ZXJzdXMgc2FtcGxlIDIsIHRoZSBsb2cgZm9sZC1jaGFuZ2VzIGFyZSBiaWFzZWQgZG93bndhcmRzLiBUaGlzIG1lYW5zIHRoYXQsIG9uIGF2ZXJhZ2UsIGEgZ2VuZSBpcyBsb3dlciBleHByZXNzZWQgaW4gc2FtcGxlIDggdmVyc3VzIHNhbXBsZSAyLiBMb29raW5nIGF0IHRoZSBsaWJyYXJ5IHNpemVzLCB3ZSBjYW4gaW5kZWVkIHNlZSB0aGF0IHRoZSBsaWJyYXJ5IHNpemUgZm9yIHNhbXBsZSAyIGlzIGFib3V0ICQxMcOXMTBeNiQgd2hpbGUgaXQgaXMgb25seSBhYm91dCAkN8OXMTBeNiQgZm9yIHNhbXBsZSA4ISBUaGlzIGlzIGEgY2xlYXIgbGlicmFyeSBzaXplIGVmZmVjdCB0aGF0IHdlIHNob3VsZCB0YWtlIGludG8gYWNjb3VudC4KCgpXZSBjYW4gc29sdmUgdGhlc2UgaXNzdWVzIGJ5IGludHJvZHVjaW5nIG9mZnNldHMgaW4gb3VyIG1vZGVsLgoKJCQKICBcbGVmdFx7CiAgXGJlZ2lue2FycmF5fXtjY2N9CiAgWV97Z2l9ICYgXHNpbSAmIFBvaShcbXVfe2dpfSkgXFwKICBcbG9nIFxtdV97Z2l9ICYgPSAmIFxldGFfe2dpfSBcXAogIFxldGFfe2dpfSAmID0gJiBcbWF0aGJme1h9XlRfaSBcYmV0YV9nICsgbG9nKE9fe2dpfSkgXFwKICBcZW5ke2FycmF5fQogIFxyaWdodC4KICAkJAogIAojIyBUTU0gbWV0aG9kIChkZWZhdWx0IG9mIGBlZGdlUmApCgpbUm9iaW5zb24gYW5kIE9zaGxhY2sgKDIwMTApLiBHZW5vbWUgQmlvbG9neV0oaHR0cHM6Ly9nZW5vbWViaW9sb2d5LmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvZ2ItMjAxMC0xMS0zLXIyNSkKCmBgYHtyIGZpZy53aWR0aD0wLjUsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLmNhcD09IiBSb2JpbnNvbiBhbmQgT3NobGFjayAoMjAxMCkuIEdlbm9tZSBCaW9sb2d5LiJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL2ZpZ3MvZWRnZVJOb3JtSW50cm8ucG5nIikKYGBgCgotIE9uIHRoZSBwbG90IHdlIHNlZSBhIGNsZWFyIGVmZmVjdCBvbiBhbGwgZ2VuZXMKLSBDb3JyZWN0aW5nIGZvciBsaWJyYXJ5IHNpemUgdGVuZHMgdG8gb3ZlciBjb3JyZWN0LiAKLSBTb21lIERFIGdlbmVzIGFyZSBoaWdobHkgYWJ1bmRhbnQgYW5kIGRldGVybWluZSB0aGUgbGlicmFyeSBzaXplIHRvIGEgbGFyZ2UgZXh0ZW5kCgpUaGUgdHJpbW1lZCBtZWFuIG9mIE0tdmFsdWVzIChUTU0pIG1ldGhvZCBpbnRyb2R1Y2VkIGJ5IFtSb2JpbnNvbiAmIE9zaGxhY2sgKDIwMTApXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9nYi0yMDEwLTExLTMtcjI1KSBpcyBhIG5vcm1hbGl6YXRpb24gcHJvY2VkdXJlIHRoYXQgY2FsY3VsYXRlcyBhIHNpbmdsZSBub3JtYWxpemF0aW9uIGZhY3RvciBmb3IgZWFjaCBzYW1wbGUuIEFzIHRoZSBuYW1lIHN1Z2dlc3RzLCBpdCBpcyBiYXNlZCBvbiBhIHRyaW1tZWQgbWVhbiBvZiBmb2xkLWNoYW5nZXMgKCRNJC12YWx1ZXMpIGFzIHRoZSBzY2FsaW5nIGZhY3Rvci4gQSB0cmltbWVkIG1lYW4gaXMgYW4gYXZlcmFnZSBhZnRlciByZW1vdmluZyBhIHNldCBvZiBgYGV4dHJlbWUnJyB2YWx1ZXMuIApTcGVjaWZpY2FsbHksIFRNTSBjYWxjdWxhdGVzIGEgbm9ybWFsaXphdGlvbiBmYWN0b3IgJEZfaV57KHIpfSQgYWNyb3NzIGdlbmVzICRnJCBmb3IgZWFjaCBzYW1wbGUgJGkkIGFzIGNvbXBhcmVkIHRvIGEgcmVmZXJlbmNlIHNhbXBsZSAkciQsCiQkClxsb2dfMihGX2leeyhyKX0pID0gXGZyYWN7XHN1bV97ZyBcaW4ge1xjYWwgR31eKn0gd197Z2l9XnIgTV97Z2l9XnJ9e1xzdW1fe2cgXGluIHtcY2FsIEd9Xip9IHdfe2dpfV5yfSwKJCQKd2hlcmUgJE1fe2dpfV5yJCByZXByZXNlbnRzIHRoZSAkXGxvZ18yJC1mb2xkLWNoYW5nZSBvZiB0aGUgZ2VuZSBleHByZXNzaW9uIGZyYWN0aW9uIGFzIGNvbXBhcmVkIHRvIGEgcmVmZXJlbmNlIHNhbXBsZSAkciQsIGkuZS4sCiQkIE1fe2dpfV5yID0gXGxvZ18yXGxlZnQoIFxmcmFje1lfe2dpfSAvIE5faX17IFlfe2dyfSAvIE5fcn0gXHJpZ2h0KSwgJCQKYW5kICR3X3tnaX1eciQgcmVwcmVzZW50cyBhIHByZWNpc2lvbiB3ZWlnaHQgY2FsY3VsYXRlZCBhcwokJAogd197Z2l9XnIgPSBcZnJhY3tOX2kgLSBZX3tnaX19e05faSBZX3tnaX19ICsgXGZyYWN7Tl9yIC0gWV97Z3J9fXtOX3IgWV97Z3J9fSwKJCQKYW5kICR7XGNhbCBHfV4qJCByZXByZXNlbnRzIHRoZSBzZXQgb2YgZ2VuZXMgYWZ0ZXIgdHJpbW1pbmcgdGhvc2Ugd2l0aCB0aGUgbW9zdCBleHRyZW1lIGF2ZXJhZ2UgZXhwcmVzc2lvbi4gVGhlIHdlaWdodHMgc2VydmUgdG8gYWNjb3VudCBmb3IgdGhlIGZhY3QgdGhhdCBmb2xkLWNoYW5nZXMgZm9yIGdlbmVzIHdpdGggbG93ZXIgcmVhZCBjb3VudHMgYXJlIG1vcmUgdmFyaWFibGUuCgpUaGUgcHJvY2VkdXJlIG9ubHkgdGFrZXMgZ2VuZXMgaW50byBhY2NvdW50IHdoZXJlIGJvdGggJFlfe2dpfT4wJCBhbmQgJFlfe2dyfT4wJC4gQnkgZGVmYXVsdCwgVE1NIHRyaW1zIGdlbmVzIHdpdGggdGhlICQzMFwlJCBtb3N0IGV4dHJlbWUgJE0kLXZhbHVlcyBhbmQgJDVcJSQgbW9zdCBleHRyZW1lIGF2ZXJhZ2UgZ2VuZSBleHByZXNzaW9uLCBhbmQgY2hvb3NlcyBhcyByZWZlcmVuY2UgJHIkIHRoZSBzYW1wbGUgd2hvc2UgdXBwZXItcXVhcnRpbGUgaXMgY2xvc2VzdCB0byB0aGUgYWNyb3NzLXNhbXBsZSBhdmVyYWdlIHVwcGVyLXF1YXJ0aWxlLiBUaGUgbm9ybWFsaXplZCBjb3VudHMgYXJlIHRoZW4gZ2l2ZW4gYnkgJFx0aWxkZXtZfV97Z2l9ID0gWV97Z2l9IC8gTl9pXnMkLCB3aGVyZSAKJCROX2lecyA9IFxmcmFje05faSBGX2leeyhyKX19e1xzdW1fe2k9MX1ebiBOX2kgRl9pXnsocil9L259LiQkCgpUTU0gbm9ybWFsaXphdGlvbiBtYXkgYmUgcGVyZm9ybWVkIGZyb20gdGhlIGBjYWxjTm9ybUZhY3RvcnNgIGZ1bmN0aW9uIGltcGxlbWVudGVkIGluIGBlZGdlUmA6CgpgYGB7cn0KZGdlIDwtIGVkZ2VSOjpjYWxjTm9ybUZhY3RvcnMoc2UpCmRnZSRzYW1wbGVzICNub3JtYWxpemF0aW9uIGZhY3RvcnMgYWRkZWQgdG8gY29sRGF0YQpgYGAKCkxldCdzIGNoZWNrIGhvdyBvdXIgTUQtcGxvdHMgbG9vayBsaWtlIGFmdGVyIG5vcm1hbGl6YXRpb24uIE5vdGUgdGhhdCwgd2UgY2FuIHJld3JpdGUgdGhlIEdMTSBhcwokJCBcbG9nXGxlZnQoIFxmcmFje1xtdV97Z2l9fXtOX2lec30gXHJpZ2h0KSA9IFxtYXRoYmZ7WH1faV5UIFxiZXRhX2cgJCQKYW5kIHNvICRcZnJhY3tcbXVfe2dpfX17Tl9pXnN9JCBjYW4gYmUgY29uc2lkZXJlZCBhcyBhbiAnb2Zmc2V0LWNvcnJlY3RlZCBjb3VudCcuCgpXZSBzZWUgdGhhdCBhbGwgTUQtcGxvdHMgYXJlIG5vdyBuaWNlbHkgY2VudGVyZWQgYXJvdW5kIGEgbG9nLWZvbGQtY2hhbmdlIG9mIHplcm8hCgpgYGB7cn0KIyMgbm9ybWFsaXplCmVmZkxpYlNpemUgPC0gZGdlJHNhbXBsZXMkbGliLnNpemUgKiBkZ2Ukc2FtcGxlcyRub3JtLmZhY3RvcnMKbm9ybUNvdW50VE1NIDwtIHN3ZWVwKGFzc2F5cyhzZSkkY291bnRzLCAyLCBGVU49Ii8iLCBlZmZMaWJTaXplKQoKcGxvdHNOb3JtIDwtIGFwcGx5KHBhaXJDb21iLDIsZnVuY3Rpb24oeCkgbWEyU2FtcChub3JtQ291bnRUTU1bLHhdKSArIGdndGl0bGUocGFzdGUoInNhbXBsZXMiLHhbMl0sInZzIiwgeFsxXSkpKQpkby5jYWxsKCJncmlkLmFycmFuZ2UiLGMocGxvdHMsbmNvbD0zKSkKYGBgCgoKIyMgTWVkaWFuLW9mLVJhdGlvcyBNZXRob2QgKGRlZmF1bHQgb2YgYERFU2VxMmApCgpUaGUgbWVkaWFuLW9mLXJhdGlvcyBtZXRob2QgaXMgdXNlZCBpbiBgREVTZXEyYCBhcyBkZXNjcmliZWQgaW4gW0xvdmUgKmV0IGFsLiogKDIwMTQpXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE0LTA1NTAtOCkuCkl0IGFzc3VtZXMgdGhhdCB0aGUgZXhwZWN0ZWQgdmFsdWUgJFxtdV97Z2l9ID0gRShZX3tnaX0pJCBpcyBwcm9wb3J0aW9uYWwgdG8gdGhlIHRydWUgZXhwcmVzc2lvbiBvZiB0aGUgZ2VuZSwgJHFfe2dpfSQsIHNjYWxlZCBieSBhIG5vcm1hbGl6YXRpb24gZmFjdG9yICRzX3tpfSQgZm9yIGVhY2ggc2FtcGxlLAokJCBcbXVfe2dpfSA9IHNfe2l9cV97Z2l9LiAkJAoKVGhlIG5vcm1hbGl6YXRpb24gZmFjdG9yICRzX3tpfSQgaXMgdGhlbiBlc3RpbWF0ZWQgdXNpbmcgdGhlIG1lZGlhbi1vZi1yYXRpb3MgbWV0aG9kIGNvbXBhcmVkIHRvIGEgc3ludGhldGljIHJlZmVyZW5jZSBzYW1wbGUgJHIkIGRlZmluZWQgYmFzZWQgb24gZ2VvbWV0cmljIG1lYW5zIG9mIGNvdW50cyBhY3Jvc3Mgc2FtcGxlcwokJApzX2kgPSBcdGV4dHttZWRpYW59X3tce3tnOlleeyp9X3tncn0gXG5lIDB9XH19IFxmcmFje1lfe2dpfX17WV57Kn1fe2dyfX0sCiQkCndpdGggCiQkIFleeyp9X3tncn0gPSBcbGVmdCggXHByb2Rfe2k9MX1ebiBZX3tnaX0gXHJpZ2h0KV57MS9ufS4gJCQKCldlIGNhbiB0aGVuIHVzZSB0aGUgc2l6ZSBmYWN0b3JzICRzX2kkIGFzIG9mZnNldHMgdG8gdGhlIEdMTS4KCk1lZGlhbi1vZi1yYXRpb3Mgbm9ybWFsaXphdGlvbiBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgYERFU2VxMmAgcGFja2FnZToKCmBgYHtyfQpkZHMgPC0gREVTZXEyOjpERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGFzc2F5cyhzZSkkY291bnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBjb2xEYXRhKHNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IDEpICNqdXN0IGFkZCBpbnRlcmNlcHQgdG8gc2hvd2Nhc2Ugbm9ybWFsaXphdGlvbgpkZHMgPC0gREVTZXEyOjplc3RpbWF0ZVNpemVGYWN0b3JzKGRkcykKc2l6ZUZhY3RvcnMoZGRzKQpgYGAKCllvdSBtYXkgYWxzbyB3YW50IHRvIGNoZWNrIG91dCB0aGUgW1N0YXRRdWVzdCB2aWRlbyBvbiBERVNlcTIgbm9ybWFsaXphdGlvbl0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1VRkI5OTN4dWZVVSkuCgojIyMgQ29tcGFyaW5nIFRNTSB3aXRoIERFU2VxMiBOb3JtYWxpemF0aW9uCgpXZSBjYW4gY29tcGFyZSB0aGUgc2l6ZSBmYWN0b3JzIGZvciBib3RoIG5vcm1hbGl6YXRpb25zIHRvIHZlcmlmeSBpZiB0aGV5IGFncmVlIG9uIHRoZSBub3JtYWxpemF0aW9uIGZhY3RvcnMuIE5vdGUgd2UgbmVlZCB0byBzY2FsZSB0aGUgZWZmZWN0aXZlIGxpYnJhcnkgc2l6ZXMgZnJvbSBgZWRnZVJgIHRvIGVuZm9yY2UgYSBzaW1pbGFyIHNjYWxlIGFzIHRoZSBzaXplIGZhY3RvcnMgZnJvbSBgREVTZXEyYC4gV2hpbGUgYmVsb3cgd2UgYXJlIHVzaW5nIGFuIGFyaXRobWV0aWMgbWVhbiwgYSBnZW9tZXRyaWMgbWVhbiBtYXkgYmUgdXNlZCBhcyB3ZWxsLCB3aGljaCB3aWxsIGJlIG1vcmUgcm9idXN0IHRvIG91dGx5aW5nIGVmZmVjdGl2ZSBsaWJyYXJ5IHNpemVzLgoKYGBge3J9CnBsb3QoZWZmTGliU2l6ZSAvIG1lYW4oZWZmTGliU2l6ZSksIHNpemVGYWN0b3JzKGRkcyksCiAgICAgeGxhYiA9ICJlZGdlUiBzaXplIGZhY3RvciIsCiAgICAgeWxhYiA9ICJERVNlcTIgc2l6ZSBmYWN0b3IiKQpgYGAKCiMgQWxpYXNpbmcKClN1cHBvc2Ugd2UgYXJlIHdvcmtpbmcgd2l0aCB0aGUgZm9sbG93aW5nIGV4cGVyaW1lbnRhbCBkZXNpZ24gb24gY29sb24gY2FuY2VyLiBTdHVkeWluZyB0aGUgZWZmZWN0IG9mIGEgZHJ1ZyBvbiBnZW5lIGV4cHJlc3Npb24sIHJlc2VhcmNoZXJzIGdhdGhlciBSTkEtc2VxIGRhdGEgZnJvbSBmb3VyIGNvbG9uIGNhbmNlciBwYXRpZW50cyBhbmQgZm91ciBoZWFsdGh5IGluZGl2aWR1YWxzLiBGb3IgZWFjaCBpbmRpdmlkdWFsLCB0aGV5IG9idGFpbiBSTkEtc2VxIGRhdGEgZnJvbSBhIGJsb29kIHNhbXBsZSBiZWZvcmUgYXMgd2VsbCBhcyB0d28gd2Vla3MgYWZ0ZXIgdGFraW5nIGEgZGFpbHkgZG9zZSBvZiB0aGUgZHJ1Zy4gVGhlIHJlc2VhcmNoIHF1ZXN0aW9uIHJlbGF0ZXMgdG8gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYWZ0ZXIgdnMuIGJlZm9yZSB0YWtpbmcgdGhlIGRydWcsIGluIHBhcnRpY3VsYXIgd2hldGhlciB0aGlzIGlzIGRpZmZlcmVudCBmb3IgdGhlIGRpc2Vhc2VkIHZlcnN1cyBoZWFsdGh5IGdyb3VwIChpLmUuLCB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiB0aW1lIChiZWZvcmUvYWZ0ZXIgdGFraW5nIHRoZSBkcnVnKSBhbmQgZGlzZWFzZSBzdGF0dXMgKGhlYWx0aHkvY29sb24gY2FuY2VyKSkuCgpJbiB0ZXJtcyBvZiB0aGUgbW9kZWwgbWF0cml4LCB3ZSBjb3VsZCBpbWFnaW5lIGEgZGVzaWduIHN1Y2ggYXMgYCB+IHBhdGllbnQgKyBkaXNlYXNlKnRpbWVgLCB3aGVyZSAKCiAtIGBkaXNlYXNlYCBpcyBhIGJpbmFyeSBpbmRpY2F0b3IgcmVmZXJyaW5nIHRvIGNvbG9uIGNhbmNlciB2ZXJzdXMgY29udHJvbCBzYW1wbGUuCiAtIGB0aW1lYCBkZWZpbmVzIGlmIHRoZSBzYW1wbGUgaXMgdGFrZW4gYmVmb3JlIG9yIGFmdGVyIHRha2luZyB0aGUgZHJ1Zy4KIC0gYHBhdGllbnRgIGRlZmluZXMgdGhlIGluZGl2aWR1YWwgZG9ub3IgdGhlIHNhbXBsZSBjb21lcyBmcm9tLgoKVGhlIHJlc2VhcmNoIHF1ZXN0aW9uIGNvdWxkIHRoZW4gYW1vdW50IHRvIHRlc3RpbmcgdGhlIGBkaXNlYXNlICogdGltZWAgaW50ZXJhY3Rpb24uCgpMZXQncyB0cnkgdGhpcywgYnkgc2ltdWxhdGluZyByYW5kb20gZGF0YSBmb3Igb25lIGdlbmUuCgpgYGB7cn0Kc2V0LnNlZWQoMikKIyAyIHNhbXBsZXMgcGVyIHBhdGllbnQgZm9yIDggcGF0aWVudHMKcGF0aWVudCA8LSBmYWN0b3IocmVwKGxldHRlcnNbMTo4XSwgZWFjaD0yKSkgCiMgZmlyc3QgZm91ciBhcmUgaGVhbHRoeSwgbmV4dCBmb3VyIGFyZSBkaXNlYXNlZApkaXNlYXNlIDwtIGZhY3RvcihjKHJlcCgiaGVhbHRoeSIsOCksIHJlcCgiY2FuY2VyIiw4KSksIGxldmVscz1jKCJoZWFsdGh5IiwgImNhbmNlciIpKSAKIyBvbmUgYmVmb3JlIGFuZCBvbmUgYWZ0ZXIgc2FtcGxlIGZvciBlYWNoCnRpbWUgPC0gZmFjdG9yKHJlcChjKCJiZWZvcmUiLCAiYWZ0ZXIiKSwgOCksIGxldmVscz1jKCJiZWZvcmUiLCAiYWZ0ZXIiKSkgCgp0YWJsZShwYXRpZW50LCBkaXNlYXNlLCB0aW1lKQoKIyMgc2ltdWxhdGUgZGF0YSBmb3Igb25lIGdlbmUKbiA8LSAxNgp5IDwtIHJwb2lzKG4gPSBuLCBsYW1iZGEgPSA1MCkKCiMjIGZpdCBhIFBvaXNzb24gbW9kZWwKbSA8LSBnbG0oeSB+IHBhdGllbnQgKyBkaXNlYXNlKnRpbWUsCiAgICAgICAgIGZhbWlseSA9ICJwb2lzc29uIikKc3VtbWFyeShtKQpgYGAKCiAtLS0KIApXZSBmaW5kIHRoYXQgb25lIG9mIHRoZSBjb2VmZmljaWVudHMgaXMgYE5BYCEgVGhpcyBpcyBvYnZpb3VzbHkgbm90IGJlY2F1c2Ugd2UncmUgZGVhbGluZyB3aXRoIGBOQWAgdmFsdWVzIGluIHRoZSBkYXRhIGFzIHdlJ3ZlIGp1c3Qgc2ltdWxhdGVkIHRoZSByZXNwb25zZSB2YXJpYWJsZSBvdXJzZWx2ZXMuIFdoYXQncyBnb2luZyBvbj8KCk9uZSBvZiB0aGUgcGFyYW1ldGVycywgaW4gdGhpcyBjYXNlIHRoZSBwYXJhbWV0ZXIgZGlzdGluZ3Vpc2hpbmcgY2FuY2VyIGZyb20gaGVhbHRoeSBwYXRpZW50cyAqKmNhbm5vdCBiZSBlc3RpbWF0ZWQgYXMgaXQgaXMgYSBsaW5lYXIgY29tYmluYXRpb24gb2Ygb3RoZXIgcGFyYW1ldGVycyoqLiBJbiBvdXIgY2FzZSwgZXN0aW1hdGluZyB0aGUgZGlzZWFzZWQgZWZmZWN0IHdvdWxkIHVzZSBpbmZvcm1hdGlvbiB0aGF0IGlzIGFscmVhZHkgdXNlZCB0byBlc3RpbWF0ZSB0aGUgcGF0aWVudC1sZXZlbCBpbnRlcmNlcHRzLiBJbiBvdGhlciB3b3JkcywgKipvbmNlIHlvdSBrbm93IHRoZSBwYXRpZW50LCB5b3UgaW1tZWRpYXRlbHkgYWxzbyBrbm93IHRoZSBkaXNlYXNlIHN0YXR1cyoqLCBzbyBlc3RpbWF0aW5nIHRoZSBkaXNlYXNlZCB2cyBoZWFsdGh5IGVmZmVjdCBvbiB0b3Agb2YgdGhlIHBhdGllbnQgZWZmZWN0IHByb3ZpZGVzIG5vIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gaWYgd2UgaGF2ZSBhbHJlYWR5IGVzdGltYXRlZCB0aGUgcGF0aWVudC1sZXZlbCBlZmZlY3RzLiBUaGlzIGNvbmNlcHQgaXMgY2FsbGVkIGFsaWFzaW5nLCBhbmQgaXMgYSBjb21tb24gdGVjaG5pY2FsIGlzc3VlIGluICdvbWljcyBleHBlcmltZW50cyB3aXRoIGNvbXBsZXggZXhwZXJpbWVudGFsIGRlc2lnbnMuIAoKIC0tLQoKV2hpbGUgdG8gdW5kZXJzdGFuZCB0aGUgb3JpZ2luIG9mIHRoZSBhbGlhc2luZyBpdCBpcyBjcnVjaWFsIHRvIHVuZGVyc3RhbmQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGV4cGVyaW1lbnRhbCBkZXNpZ24sIHdlIGNhbiBhbHNvIGludmVzdGlnYXRlIGl0IGluIGRldGFpbCB1c2luZyB0aGUgYGFsaWFzYCBmdW5jdGlvbiwgdG8gZ2l2ZSB1cyBhbiBpZGVhLgoKYGBge3J9CmFsaWFzKG0pCmBgYAoKV2Ugc2VlIHRoYXQgdGhlIGVmZmVjdCBgZGlzZWFzZWNhbmNlcmAgaXMgYSBsaW5lYXIgY29tYmluYXRpb24gb2YgdGhlIHBhdGllbnQtc3BlY2lmaWMgZWZmZWN0cyBvZiB0aGUgY2FuY2VyIHBhdGllbnRzLiBUaGlzIG1ha2VzIHNlbnNlIQoKIC0tLSAKCkZvciBjbGFyaXR5LCBsZXQncyByZXByb2R1Y2UgdGhpcyB1c2luZyBvdXIgZGVzaWduIG1hdHJpeC4KCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeCh+IHBhdGllbnQgKyBkaXNlYXNlKnRpbWUpICMgdGhpcyBpcyB0aGUgZGVzaWduIHVzZWQgaW4gZ2xtKCkKCiMjIHRoZXNlIGFyZSBpbmRlZWQgaWRlbnRpY2FsLgpYWywiZGlzZWFzZWNhbmNlciJdClhbLCJwYXRpZW50ZSJdICsgWFssInBhdGllbnRmIl0gKyBYWywicGF0aWVudGciXSArIFhbLCJwYXRpZW50aCJdCmBgYAoKU2luY2Ugb25lIG9mIG91ciBwYXJhbWV0ZXJzIGlzIGEgbGluZWFyIGNvbWJpbmF0aW9uIG9mIG90aGVyIHBhcmFtZXRlcnMsIGl0IGNhbm5vdCBiZSBlc3RpbWF0ZWQgc2ltdWx0YW5lb3VzbHkgd2l0aCB0aGUgb3RoZXIgcGFyYW1ldGVycy4gSW4gdGhpcyBjYXNlLCB3ZSBjYW4gYWN0dWFsbHkgZHJvcCB0aGUgYGRpc2Vhc2VgIG1haW4gZWZmZWN0IGZyb20gdGhlIG1vZGVsLCBzaW5jZSB3ZSBrbm93IHRoYXQgaXQgaXMgYWxyZWFkeSBpbmNsdWRlZCBpbiB0aGUgYHBhdGllbnRgIGVmZmVjdC4KCiAtLS0KCldlIHdpbGwgaGF2ZSB0byBjYXJlZnVsbHkgY29uc3RydWN0IG91ciBkZXNpZ24gbWF0cml4IGluIG9yZGVyIHRvIGFjY291bnQgZm9yIGFsbCBpbXBvcnRhbnQgc291cmNlcyBvZiB2YXJpYXRpb24gd2hpbGUgc3RpbGwgYWxsb3dpbmcgdXMgdG8gYW5zd2VyIHRoZSByZXNlYXJjaCBxdWVzdGlvbiBvZiBpbnRlcmVzdC4gVGhlIGFsaWFzaW5nIGV4cGxvcmF0aW9uIGFib3ZlIGhhcyBtYWRlIGl0IGNsZWFyIHdlIG1heSBkcm9wIHRoZSBgZGlzZWFzZWAgbWFpbiBlZmZlY3QsIHNvIGxldCdzIHN0YXJ0IGJ5IGNvbnN0cnVjdGluZyB0aGlzIGRlc2lnbiBtYXRyaXguCgpgYGB7cn0KWCA8LSBtb2RlbC5tYXRyaXgofiBwYXRpZW50ICsgdGltZSArIGRpc2Vhc2U6dGltZSkKCm0yIDwtIGdsbSh5IH4gLTEgKyBYLAogICAgICAgICBmYW1pbHkgPSAicG9pc3NvbiIpCnN1bW1hcnkobTIpCmFsaWFzKG0yKQpgYGAKCldlIGFyZSBzdGlsbCBjb25mcm9udGVkIHdpdGggYWxpYXNpbmcgYXMgdGhlIG1vZGVsIG1hdHJpeCBjb250YWlucyBhbiBpbnRlcmFjdGlvbiBlZmZlY3QgYHRpbWViZWZvcmU6ZGlzZWFzZWNhbmNlcmAgYXMgd2VsbCBhcyBgdGltZWFmdGVyOmRpc2Vhc2VjYW5jZXJgLCB3aGlsZSBvbmx5IHRoZSBsYXR0ZXIgaXMgcmVsZXZhbnQuIEluZGVlZCwgd2Uga25vdyB0aGF0IHdlIGNhbiBkZXJpdmUgdGhlIGB0aW1lYmVmb3JlOmRpc2Vhc2VjYW5jZXJgIGVmZmVjdCBieSBhdmVyYWdpbmcgdGhlIHBhdGllbnQgZWZmZWN0cyBvZiB0aGUgY2FuY2VyIHBhdGllbnRzLgoKIC0tLQoKYGBge3J9ClggPC0gWFssIWNvbG5hbWVzKFgpICVpbiUgInRpbWViZWZvcmU6ZGlzZWFzZWNhbmNlciJdCgoKIyMgZml0IGEgUG9pc3NvbiBtb2RlbAptMiA8LSBnbG0oeSB+IC0xICsgWCwKICAgICAgICAgZmFtaWx5ID0gInBvaXNzb24iKQpzdW1tYXJ5KG0yKQpgYGAKCldlIHNlZSB0aGF0IGFsbCBjb2VmZmljaWVudHMgY2FuIG5vdyBiZSBlc3RpbWF0ZWQuIFRoZSBgdGltZWFmdGVyYCBlZmZlY3QgbWF5IGJlIGludGVycHJldGVkIGFzIHRoZSB0aW1lIGVmZmVjdCBmb3IgaGVhbHRoeSBwYXRpZW50cywgd2hpbGUgdGhlIGB0aW1lYWZ0ZXI6ZGlzZWFzZWNhbmNlcmAgZWZmZWN0IG1heSBiZSBpbnRlcnByZXRlZCBhcyB0aGUgZGlmZmVyZW5jZSBpbiB0aGUgdGltZSBlZmZlY3QgZm9yIGNhbmNlciBwYXRpZW50cyBhcyBjb21wYXJlZCB0byBoZWFsdGh5IHBhdGllbnRzLCBpLmUuLCBpdCBpcyB0aGUgcmVsZXZhbnQgaW50ZXJhY3Rpb24gZWZmZWN0IHdlIGFyZSBpbnRlcmVzdGVkIGluLiAKCg==