Parameter Estimation
and Inference in Generalized linear models
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))

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)]\)
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\}
\]
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.
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.\]
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)
\]
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!\]
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)
## Warning: `qplot()` was deprecated in ggplot2 3.4.0.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

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\}\]
dpois(y,lambda = muTrue, log = TRUE) %>%
sum()
## [1] -260.485
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)\),
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!
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.
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\)
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\}\]
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.
Newton-Raphson
Newton Raphson algorithm to find the root of the score function.
- Choose initial parameter estimate \(\boldsymbol{\beta}^k=\boldsymbol{\beta}^0\)
- Calculate score \(S(\boldsymbol{\beta})\vert_{\boldsymbol{\beta}=\boldsymbol{\beta}^k}\)
- Calculate derivative of the function for which you want to calculate
the roots
- Walk along first derivative until line (plane) of the derivative
crosses zero
- Update the betas \(\boldsymbol{\beta}^{k+1}\)
- Iterate from step 2 - 5 until convergence.
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*}\]
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.
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.
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.
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\)
Poisson Example
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 )

Iteratively
Reweighted Least Squares
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\]
Weight
Matrix?
\[[w_{ii}]= var_{y_i}^{-1}
\left(\frac{\partial \mu}{\partial \eta}\right)^2\] \[[w_{ii}]= e^{\eta_i}\]
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
Comparison with GLM
Function
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
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
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
Variance \(\beta\)?
\[\Sigma_{\beta}=\left(\mathbf{X}^T\mathbf{W}
\mathbf{X}\right)^{-1}\]
varBeta=solve(t(xhlp)%*%diag(w)%*%xhlp)
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
| 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 |
Hypothesis testing:
Large sample theory
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.
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.

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.
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:
- Stage I
- OLS
- Estimate variances at each data point
- Use variances as weights: \(W=\text{diag}[1/\hat\sigma_i^2]\)
- 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



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.
## No documentation for 'filterByExpr' in specified packages and libraries
suppressPackageStartupMessages({
library(limma)
library(edgeR)
library(DESeq2)
})
## Warning: multiple methods tables found for 'union'
## Warning: multiple methods tables found for 'intersect'
## Warning: multiple methods tables found for 'setdiff'
## Warning: multiple methods tables found for 'setequal'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'IRanges'
## Warning: multiple methods tables found for 'union'
## Warning: multiple methods tables found for 'intersect'
## Warning: multiple methods tables found for 'setdiff'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'GenomeInfoDb'
## Warning: multiple methods tables found for 'intersect'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'GenomicRanges'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'XVector'
## Warning: multiple methods tables found for 'union'
## Warning: multiple methods tables found for 'intersect'
## Warning: multiple methods tables found for 'setdiff'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'SummarizedExperiment'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'S4Arrays'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'DelayedArray'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'SparseArray'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'DESeq2'
dds <- makeExampleDESeqDataSet()
simCounts <-counts(dds)
group <- dds$condition
dge <- edgeR::DGEList(simCounts)
design <- model.matrix(~group)
keep <- filterByExpr(dge, design)
table(keep)
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)
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
## [1] 6
Independent filtering has been formalized by 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.
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
A Dependent Test
Statistic
filterStatEffectSize <- abs(rowMeans(simCounts[,group == "A"]) - rowMeans(simCounts[,group == "B"]))
testStat <- genefilter::rowttests(simCounts, group)
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'AnnotationDbi'
## Warning: replacing previous import 'BiocGenerics::setequal' by
## 'S4Vectors::setequal' when loading 'Biostrings'
## Warning: multiple methods tables found for 'union'
## Warning: multiple methods tables found for 'intersect'
## Warning: multiple methods tables found for 'setdiff'
## Warning: multiple methods tables found for 'setequal'
## 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")

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")

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))
| 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)
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,libSize=NULL) {
stopifnot("`countMx` is not a matrix with two columns" = ncol(countMx) == 2)
A <- countMx %>% log2 %>% rowMeans
if(is.null(libSize))
M <- countMx %>% log2 %>% apply(.,1,diff)
else
M <- countMx %>% log2 %>% apply(.,1,diff) - libSize %>% log2 %>% 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="blue") +
geom_abline(intercept=median(M[!w],na.rm=TRUE),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.
\]
libSize <- colSums(assay(se))
plots2 <- apply(pairComb,2,function(x) ma2Samp(assay(se)[,x],libSize = libSize[x]) + ggtitle(paste("samples",x[2],"vs", x[1])))
do.call("grid.arrange",c(plots2,ncol=3))

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
| 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(assays(se)$counts[,x], effLibSize[x]) + ggtitle(paste("samples",x[2],"vs", x[1])))
do.call("grid.arrange",c(plotsNorm,ncol=3))

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)
| 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")
##
## 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.
## 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")
##
## 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
## 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")
##
## 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+IDUKCiMjIHVuY29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uCnBsb3QoZGVuc2l0eSh0ZXN0U3RhdCRzdGF0aXN0aWMsIG5hLnJtPVRSVUUpLAogICAgIHhsYWIgPSAiVGVzdCBzdGF0aXN0aWMiLAogICAgIG1haW4gPSAiVW5jb25kaXRpb25hbCBkaXN0cmlidXRpb24iKQoKIyMgY29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uOiB0aGUgc2FtZS4KcGxvdChkZW5zaXR5KHRlc3RTdGF0JHN0YXRpc3RpY1trZWVwR2xvYmFsTWVhbl0sIG5hLnJtPVRSVUUpLAogICAgIHhsYWIgPSAiVGVzdCBzdGF0aXN0aWMiLAogICAgIG1haW4gPSAiQ29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uIikKYGBgCgoKIyBOb3JtYWxpemF0aW9uCgpOb3JtYWxpemF0aW9uIGlzIG5lY2Vzc2FyeSB0byBjb3JyZWN0IGZvciBzZXZlcmFsIHNvdXJjZXMgb2YgdGVjaG5pY2FsIHZhcmlhdGlvbjoKCiAtICoqRGlmZmVyZW5jZXMgaW4gc2VxdWVuY2luZyBkZXB0aCoqIGJldHdlZW4gc2FtcGxlcy4gU29tZSBzYW1wbGVzIGdldCBzZXF1ZW5jZWQgZGVlcGVyIGluIHRoZSBzZW5zZSB0aGF0IHRoZXkgY29uc2lzdCBvZiBtb3JlIChtYXBwZWQpIHJlYWRzIGFuZCB0aGVyZWZvcmUgY2FuIGJlIGNvbnNpZGVyZWQgdG8gY29udGFpbiBhIGhpZ2hlciBhbW91bnQgb2YgaW5mb3JtYXRpb24sIHdoaWNoIHdlIHNob3VsZCBiZSB0YWtpbmcgaW50byBhY2NvdW50LiBJbiBhZGRpdGlvbiwgaWYgYSBzYW1wbGUgaXMgc2VxdWVuY2VkIGRlZXBlciwgaXQgaXMgbmF0dXJhbCB0aGF0IHRoZSBjb3VudHMgZm9yIGVhY2ggZ2VuZSB3aWxsIGJlIGhpZ2hlciwgamVvcGFyZGl6aW5nIGEgZGlyZWN0IGNvbXBhcmlzb24gb2YgdGhlIGV4cHJlc3Npb24gY291bnRzLgogLSAqKkRpZmZlcmVuY2VzIGluIFJOQSBwb3B1bGF0aW9uIGNvbXBvc2l0aW9uKiogYmV0d2VlbiBzYW1wbGVzLiBBcyBhbiBleHRyZW1lIGV4YW1wbGUsIHN1cHBvc2UgdGhhdCB0d28gc2FtcGxlcyBoYXZlIGJlZW4gc2VxdWVuY2VkIHRvIHRoZSBleGFjdCBzYW1lIGRlcHRoLiBPbmUgc2FtcGxlIGlzIGNvbnRhbWluYXRlZCBhbmQgaGFzIGEgdmVyeSBoaWdoIGNvbmNlbnRyYXRpb24gb2YgdGhlIGNvbnRhbWluYW50IGNETkEgYmVpbmcgc2VxdWVuY2VkLCBidXQgb3RoZXJ3aXNlIHRoZSB0d28gc2FtcGxlcyBhcmUgaWRlbnRpY2FsLiBTaW5jZSB0aGUgY29udGFtaW5hbnQgd2lsbCBiZSB0YWtpbmcgdXAgYSBzaWduaWZpY2FudCBwcm9wb3J0aW9uIG9mIHRoZSByZWFkcyBiZWluZyBzZXF1ZW5jZWQsIHRoZSBjb3VudHMgd2lsbCBub3QgYmUgZGlyZWN0bHkgY29tcGFyYWJsZSBiZXR3ZWVuIHRoZSBzYW1wbGVzLiBIZW5jZSwgd2UgbWF5IGFsc28gd2FudCB0byBjb3JyZWN0IGZvciBkaWZmZXJlbmNlcyBpbiB0aGUgY29tcG9zaXRpb24gb2YgdGhlIFJOQSBwb3B1bGF0aW9uIG9mIHRoZSBzYW1wbGVzLgogLSAqKk90aGVyIHRlY2huaWNhbCB2YXJpYXRpb24qKiBzdWNoIGFzIHNhbXBsZS1zcGVjaWZpYyBHQy1jb250ZW50IG9yIHRyYW5zY3JpcHQgbGVuZ3RoIGVmZmVjdHMgbWF5IGFsc28gYmUgYWNjb3VudGVkIGZvci4KIAogCmBgYHtyfQpkYXRhKCJwYXJhdGh5cm9pZEdlbmVzU0UiLCBwYWNrYWdlPSJwYXJhdGh5cm9pZFNFIikKc2UxIDwtIHBhcmF0aHlyb2lkR2VuZXNTRQpybShwYXJhdGh5cm9pZEdlbmVzU0UpCgpjb2xEYXRhKHNlMSkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgZmlsdGVyKGR1cGxpY2F0ZWQoZXhwZXJpbWVudCkpIApgYGAKClRoZXJlIGFyZSB0ZWNobmljYWwgcmVwZWF0cyBpbiB0aGUgZGF0YS4gCgpXZSBtZW50aW9uZWQgcHJldmlvdXMgbGVjdHVyZXMgdGhhdCB3ZSBjYW4gc3VtIG92ZXIgdGVjaG5pY2FsIHJlcGVhdHMsIGJlY2F1c2UgdGVjaGljYWwgcmVwZWF0cyBhcmUgUG9pc3NvbiBhbmQgdGhlIHN1bSBvZiB0d28gUG9pc3NvbiB2YXJpYWJsZXMgaXMgYWdhaW4gUG9pc3Nvbi4gCgpgYGB7cn0KZHVwRXhwcyA8LSBjb2xEYXRhKHNlMSkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgZmlsdGVyKGR1cGxpY2F0ZWQoZXhwZXJpbWVudCkpICAlPiUgCiAgcHVsbChleHBlcmltZW50KQoKY291bnRzIDwtIGFzc2F5cyhzZTEpJGNvdW50cwpuZXdDb3VudHMgPC0gY291bnRzCmNkIDwtIGNvbERhdGEoc2UxKQpmb3Ioc3MgaW4gMTpsZW5ndGgoZHVwRXhwcykpewogICMgY2hlY2sgd2hpY2ggc2FtcGxlcyBhcmUgZHVwbGljYXRlcwogIHJlbGV2YW50SWQgPC0gd2hpY2goY29sRGF0YShzZTEpJGV4cGVyaW1lbnQgPT0gZHVwRXhwc1tzc10pCiAgIyBzdW0gY291bnRzCiAgbmV3Q291bnRzWyxyZWxldmFudElkWzFdXSA8LSByb3dTdW1zKGNvdW50c1sscmVsZXZhbnRJZF0pCiAgIyBrZWVwIHdoaWNoIGNvbHVtbnMgLyByb3dzIHRvIHJlbW92ZS4KICBpZihzcyA9PSAxKXsKICAgIHRvUmVtb3ZlIDwtIHJlbGV2YW50SWRbMl0KICB9IGVsc2UgewogICAgdG9SZW1vdmUgPC0gYyh0b1JlbW92ZSwgcmVsZXZhbnRJZFsyXSkKICB9Cn0KCiMgcmVtb3ZlIGFmdGVyIHN1bW1pbmcgY291bnRzIChvdGhlcndpc2UgSURzIGdldCBtaXhlZCB1cCkKbmV3Q291bnRzIDwtIG5ld0NvdW50c1ssLXRvUmVtb3ZlXQpuZXdDRCA8LSBjZFstdG9SZW1vdmUsXQoKIyBDcmVhdGUgbmV3IFN1bW1hcml6ZWRFeHBlcmltZW50CnNlIDwtIFN1bW1hcml6ZWRFeHBlcmltZW50KGFzc2F5cyA9IGxpc3QoImNvdW50cyIgPSBuZXdDb3VudHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IG5ld0NELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEgPSBtZXRhZGF0YShzZTEpKQoKdHJlYXRtZW50IDwtIGNvbERhdGEoc2UpJHRyZWF0bWVudAp0YWJsZSh0cmVhdG1lbnQpCmBgYAoKYGBge3J9CnFwbG90KGNvbFN1bXMoYXNzYXlzKHNlKSRjb3VudHMpLzFlNiwgZ2VvbT0iaGlzdG9ncmFtIiwgYmlucz0xMCxjb2w9ImJsYWNrIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHhsYWIoImxpYnNpemUgKG1pbGxpb24gcmVhZHMpIikKCnFwbG90KAogIGNvbERhdGEoc2UpJHRyZWF0bWVudDpjb2xEYXRhKHNlKSR0aW1lLAogIGNvbFN1bXMoYXNzYXlzKHNlKSRjb3VudHMpLzFlNixnZW9tPSJib3hwbG90IgogICkgKwogIHhsYWIoInRyZWF0bWVudCIpKwogIHlsYWIoImxpYnNpemUgKG1pbGxpb24gcmVhZHMpIikKCnFwbG90KAogIGNvbERhdGEoc2UpJHBhdGllbnQsCiAgY29sU3Vtcyhhc3NheXMoc2UpJGNvdW50cykvMWU2LGdlb209ImJveHBsb3QiCiAgKSArCiAgeGxhYigiUGF0aWVudCIpKwogIHlsYWIoImxpYnNpemUgKG1pbGxpb24gcmVhZHMpIikKYGBgCgpgYGB7cn0KbWEyU2FtcCA8LSBmdW5jdGlvbihjb3VudE14LGxpYlNpemU9TlVMTCkgewpzdG9waWZub3QoImBjb3VudE14YCBpcyBub3QgYSBtYXRyaXggd2l0aCB0d28gY29sdW1ucyIgPSBuY29sKGNvdW50TXgpID09IDIpCkEgPC0gY291bnRNeCAlPiUgbG9nMiAlPiUgcm93TWVhbnMKaWYoaXMubnVsbChsaWJTaXplKSkgCiAgTSA8LSBjb3VudE14ICU+JSBsb2cyICU+JSBhcHBseSguLDEsZGlmZikgCmVsc2UgCiAgTSA8LSBjb3VudE14ICU+JSBsb2cyICU+JSBhcHBseSguLDEsZGlmZikgLSBsaWJTaXplICU+JSBsb2cyICU+JSBkaWZmCncgPC0gY291bnRNeFssMV09PW1pbihjb3VudE14WywxXSkgfCBjb3VudE14WywyXT09bWluKGNvdW50TXhbLDJdKQppZiAoYW55KHcpKSB7CiAgICAgICAgICAgIEFbd10gPC0gcnVuaWYoc3VtKHcpLCBtaW4gPSAtMSwgbWF4ID0gLjEpCiAgICAgICAgICAgIE1bd10gPC0gbG9nMihjb3VudE14W3csMl0gKyAxKSAtIGxvZzIoY291bnRNeFt3LDFdICsgMSkKfQpNQXBsb3QgPC0gcXBsb3QoQSwgTSwgY29sPXcpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwib3JhbmdlIikpICsKICB4bGFiKCJBOiBsb2cyIEF2ZXJhZ2UiKSArCiAgeWxhYigiTTogbG9nMiBGb2xkIENoYW5nZSIpCgpNQXBsb3QgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdD0wLHNsb3BlPTAsY29sPSJibHVlIikgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdD1tZWRpYW4oTVshd10sbmEucm09VFJVRSksc2xvcGU9MCxjb2w9InJlZCIpCn0KYGBgCgpMZXTigJlzIHRha2UgYSBsb29rIGF0IGhvdyBjb21wYXJhYmxlIGRpZmZlcmVudCByZXBsaWNhdGVzIGFyZSBpbiB0aGUgQ29udHJvbCBjb25kaXRpb24gYXQgNDhoIGluIG91ciBkYXRhc2V0LiBXZSB3aWxsIGludmVzdGlnYXRlIHRoaXMgdXNpbmcgTUQtcGxvdHMgKG1lYW4tZGlmZmVyZW5jZSBwbG90cyBhcyBpbnRyb2R1Y2VkIGJ5IER1ZG9pdCBldCBhbC4gKDIwMDIpKSwgYWxzbyBzb21ldGltZXMgcmVmZXJyZWQgdG8gYXMgTUEtcGxvdHMuCgpgYGB7cn0KaWRzIDwtIHdoaWNoKGNvbERhdGEoc2UpJHRyZWF0bWVudCA9PSJDb250cm9sIiAmIGNvbERhdGEoc2UpJHRpbWUgPT0gIjQ4aCIpCmlkcwpjb2xTdW1zKGFzc2F5cyhzZSkkY291bnRzWyxpZHNdKSAvIDFlNgpgYGAKCmBgYHtyfQpwYWlyQ29tYiA8LSBjb21ibigKICBpZHMsIAogIG09MikKcGxvdHMgPC0gYXBwbHkocGFpckNvbWIsMixmdW5jdGlvbih4KSBtYTJTYW1wKGFzc2F5KHNlKVsseF0pICsgZ2d0aXRsZShwYXN0ZSgic2FtcGxlcyIseFsyXSwidnMiLCB4WzFdKSkpCmRvLmNhbGwoImdyaWQuYXJyYW5nZSIsYyhwbG90cyxuY29sPTMpKQpgYGAKCldlIHNlZSBjbGVhciBiaWFzIGZvciBzb21lIHBhaXJ3aXNlIGNvbXBhcmlzb25zLiBGb3IgZXhhbXBsZSwgaW4gdGhlIGZpcnN0IHBsb3QgY29tcGFyaW5nIHNhbXBsZSA4IHZlcnN1cyBzYW1wbGUgMiwgdGhlIGxvZyBmb2xkLWNoYW5nZXMgYXJlIGJpYXNlZCBkb3dud2FyZHMuIFRoaXMgbWVhbnMgdGhhdCwgb24gYXZlcmFnZSwgYSBnZW5lIGlzIGxvd2VyIGV4cHJlc3NlZCBpbiBzYW1wbGUgOCB2ZXJzdXMgc2FtcGxlIDIuIExvb2tpbmcgYXQgdGhlIGxpYnJhcnkgc2l6ZXMsIHdlIGNhbiBpbmRlZWQgc2VlIHRoYXQgdGhlIGxpYnJhcnkgc2l6ZSBmb3Igc2FtcGxlIDIgaXMgYWJvdXQgJDExw5cxMF42JCB3aGlsZSBpdCBpcyBvbmx5IGFib3V0ICQ3w5cxMF42JCBmb3Igc2FtcGxlIDghIFRoaXMgaXMgYSBjbGVhciBsaWJyYXJ5IHNpemUgZWZmZWN0IHRoYXQgd2Ugc2hvdWxkIHRha2UgaW50byBhY2NvdW50LgoKCldlIGNhbiBzb2x2ZSB0aGVzZSBpc3N1ZXMgYnkgaW50cm9kdWNpbmcgb2Zmc2V0cyBpbiBvdXIgbW9kZWwuCgokJAogIFxsZWZ0XHsKICBcYmVnaW57YXJyYXl9e2NjY30KICBZX3tnaX0gJiBcc2ltICYgUG9pKFxtdV97Z2l9KSBcXAogIFxsb2cgXG11X3tnaX0gJiA9ICYgXGV0YV97Z2l9IFxcCiAgXGV0YV97Z2l9ICYgPSAmIFxtYXRoYmZ7WH1eVF9pIFxiZXRhX2cgKyBsb2coT197Z2l9KSBcXAogIFxlbmR7YXJyYXl9CiAgXHJpZ2h0LgogICQkCiAgCgpgYGB7cn0KbGliU2l6ZSA8LSBjb2xTdW1zKGFzc2F5KHNlKSkKcGxvdHMyIDwtIGFwcGx5KHBhaXJDb21iLDIsZnVuY3Rpb24oeCkgbWEyU2FtcChhc3NheShzZSlbLHhdLGxpYlNpemUgPSBsaWJTaXplW3hdKSArIGdndGl0bGUocGFzdGUoInNhbXBsZXMiLHhbMl0sInZzIiwgeFsxXSkpKQpkby5jYWxsKCJncmlkLmFycmFuZ2UiLGMocGxvdHMyLG5jb2w9MykpCmBgYAoKIyMgVE1NIG1ldGhvZCAoZGVmYXVsdCBvZiBgZWRnZVJgKQoKW1JvYmluc29uIGFuZCBPc2hsYWNrICgyMDEwKS4gR2Vub21lIEJpb2xvZ3ldKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L2diLTIwMTAtMTEtMy1yMjUpCgpgYGB7ciBmaWcud2lkdGg9MC41LCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5jYXA9PSIgUm9iaW5zb24gYW5kIE9zaGxhY2sgKDIwMTApLiBHZW5vbWUgQmlvbG9neS4ifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9maWdzL2VkZ2VSTm9ybUludHJvLnBuZyIpCmBgYAoKLSBPbiB0aGUgcGxvdCB3ZSBzZWUgYSBjbGVhciBlZmZlY3Qgb24gYWxsIGdlbmVzCi0gQ29ycmVjdGluZyBmb3IgbGlicmFyeSBzaXplIHRlbmRzIHRvIG92ZXIgY29ycmVjdC4gCi0gU29tZSBERSBnZW5lcyBhcmUgaGlnaGx5IGFidW5kYW50IGFuZCBkZXRlcm1pbmUgdGhlIGxpYnJhcnkgc2l6ZSB0byBhIGxhcmdlIGV4dGVuZAoKVGhlIHRyaW1tZWQgbWVhbiBvZiBNLXZhbHVlcyAoVE1NKSBtZXRob2QgaW50cm9kdWNlZCBieSBbUm9iaW5zb24gJiBPc2hsYWNrICgyMDEwKV0oaHR0cHM6Ly9nZW5vbWViaW9sb2d5LmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvZ2ItMjAxMC0xMS0zLXIyNSkgaXMgYSBub3JtYWxpemF0aW9uIHByb2NlZHVyZSB0aGF0IGNhbGN1bGF0ZXMgYSBzaW5nbGUgbm9ybWFsaXphdGlvbiBmYWN0b3IgZm9yIGVhY2ggc2FtcGxlLiBBcyB0aGUgbmFtZSBzdWdnZXN0cywgaXQgaXMgYmFzZWQgb24gYSB0cmltbWVkIG1lYW4gb2YgZm9sZC1jaGFuZ2VzICgkTSQtdmFsdWVzKSBhcyB0aGUgc2NhbGluZyBmYWN0b3IuIEEgdHJpbW1lZCBtZWFuIGlzIGFuIGF2ZXJhZ2UgYWZ0ZXIgcmVtb3ZpbmcgYSBzZXQgb2YgYGBleHRyZW1lJycgdmFsdWVzLiAKU3BlY2lmaWNhbGx5LCBUTU0gY2FsY3VsYXRlcyBhIG5vcm1hbGl6YXRpb24gZmFjdG9yICRGX2leeyhyKX0kIGFjcm9zcyBnZW5lcyAkZyQgZm9yIGVhY2ggc2FtcGxlICRpJCBhcyBjb21wYXJlZCB0byBhIHJlZmVyZW5jZSBzYW1wbGUgJHIkLAokJApcbG9nXzIoRl9pXnsocil9KSA9IFxmcmFje1xzdW1fe2cgXGluIHtcY2FsIEd9Xip9IHdfe2dpfV5yIE1fe2dpfV5yfXtcc3VtX3tnIFxpbiB7XGNhbCBHfV4qfSB3X3tnaX1ecn0sCiQkCndoZXJlICRNX3tnaX1eciQgcmVwcmVzZW50cyB0aGUgJFxsb2dfMiQtZm9sZC1jaGFuZ2Ugb2YgdGhlIGdlbmUgZXhwcmVzc2lvbiBmcmFjdGlvbiBhcyBjb21wYXJlZCB0byBhIHJlZmVyZW5jZSBzYW1wbGUgJHIkLCBpLmUuLAokJCBNX3tnaX1eciA9IFxsb2dfMlxsZWZ0KCBcZnJhY3tZX3tnaX0gLyBOX2l9eyBZX3tncn0gLyBOX3J9IFxyaWdodCksICQkCmFuZCAkd197Z2l9XnIkIHJlcHJlc2VudHMgYSBwcmVjaXNpb24gd2VpZ2h0IGNhbGN1bGF0ZWQgYXMKJCQKIHdfe2dpfV5yID0gXGZyYWN7Tl9pIC0gWV97Z2l9fXtOX2kgWV97Z2l9fSArIFxmcmFje05fciAtIFlfe2dyfX17Tl9yIFlfe2dyfX0sCiQkCmFuZCAke1xjYWwgR31eKiQgcmVwcmVzZW50cyB0aGUgc2V0IG9mIGdlbmVzIGFmdGVyIHRyaW1taW5nIHRob3NlIHdpdGggdGhlIG1vc3QgZXh0cmVtZSBhdmVyYWdlIGV4cHJlc3Npb24uIFRoZSB3ZWlnaHRzIHNlcnZlIHRvIGFjY291bnQgZm9yIHRoZSBmYWN0IHRoYXQgZm9sZC1jaGFuZ2VzIGZvciBnZW5lcyB3aXRoIGxvd2VyIHJlYWQgY291bnRzIGFyZSBtb3JlIHZhcmlhYmxlLgoKVGhlIHByb2NlZHVyZSBvbmx5IHRha2VzIGdlbmVzIGludG8gYWNjb3VudCB3aGVyZSBib3RoICRZX3tnaX0+MCQgYW5kICRZX3tncn0+MCQuIEJ5IGRlZmF1bHQsIFRNTSB0cmltcyBnZW5lcyB3aXRoIHRoZSAkMzBcJSQgbW9zdCBleHRyZW1lICRNJC12YWx1ZXMgYW5kICQ1XCUkIG1vc3QgZXh0cmVtZSBhdmVyYWdlIGdlbmUgZXhwcmVzc2lvbiwgYW5kIGNob29zZXMgYXMgcmVmZXJlbmNlICRyJCB0aGUgc2FtcGxlIHdob3NlIHVwcGVyLXF1YXJ0aWxlIGlzIGNsb3Nlc3QgdG8gdGhlIGFjcm9zcy1zYW1wbGUgYXZlcmFnZSB1cHBlci1xdWFydGlsZS4gVGhlIG5vcm1hbGl6ZWQgY291bnRzIGFyZSB0aGVuIGdpdmVuIGJ5ICRcdGlsZGV7WX1fe2dpfSA9IFlfe2dpfSAvIE5faV5zJCwgd2hlcmUgCiQkTl9pXnMgPSBcZnJhY3tOX2kgRl9pXnsocil9fXtcc3VtX3tpPTF9Xm4gTl9pIEZfaV57KHIpfS9ufS4kJAoKVE1NIG5vcm1hbGl6YXRpb24gbWF5IGJlIHBlcmZvcm1lZCBmcm9tIHRoZSBgY2FsY05vcm1GYWN0b3JzYCBmdW5jdGlvbiBpbXBsZW1lbnRlZCBpbiBgZWRnZVJgOgoKYGBge3J9CmRnZSA8LSBlZGdlUjo6Y2FsY05vcm1GYWN0b3JzKHNlKQpkZ2Ukc2FtcGxlcyAjbm9ybWFsaXphdGlvbiBmYWN0b3JzIGFkZGVkIHRvIGNvbERhdGEKYGBgCgpMZXQncyBjaGVjayBob3cgb3VyIE1ELXBsb3RzIGxvb2sgbGlrZSBhZnRlciBub3JtYWxpemF0aW9uLiBOb3RlIHRoYXQsIHdlIGNhbiByZXdyaXRlIHRoZSBHTE0gYXMKJCQgXGxvZ1xsZWZ0KCBcZnJhY3tcbXVfe2dpfX17Tl9pXnN9IFxyaWdodCkgPSBcbWF0aGJme1h9X2leVCBcYmV0YV9nICQkCmFuZCBzbyAkXGZyYWN7XG11X3tnaX19e05faV5zfSQgY2FuIGJlIGNvbnNpZGVyZWQgYXMgYW4gJ29mZnNldC1jb3JyZWN0ZWQgY291bnQnLgoKV2Ugc2VlIHRoYXQgYWxsIE1ELXBsb3RzIGFyZSBub3cgbmljZWx5IGNlbnRlcmVkIGFyb3VuZCBhIGxvZy1mb2xkLWNoYW5nZSBvZiB6ZXJvIQoKYGBge3J9CiMjIG5vcm1hbGl6ZQplZmZMaWJTaXplIDwtIGRnZSRzYW1wbGVzJGxpYi5zaXplICogZGdlJHNhbXBsZXMkbm9ybS5mYWN0b3JzCiNub3JtQ291bnRUTU0gPC0gc3dlZXAoYXNzYXlzKHNlKSRjb3VudHMsIDIsIEZVTj0iLyIsIGVmZkxpYlNpemUpCgpwbG90c05vcm0gPC0gYXBwbHkocGFpckNvbWIsMixmdW5jdGlvbih4KSAKICBtYTJTYW1wKGFzc2F5cyhzZSkkY291bnRzWyx4XSwgZWZmTGliU2l6ZVt4XSkgKyBnZ3RpdGxlKHBhc3RlKCJzYW1wbGVzIix4WzJdLCJ2cyIsIHhbMV0pKSkKZG8uY2FsbCgiZ3JpZC5hcnJhbmdlIixjKHBsb3RzTm9ybSxuY29sPTMpKQpgYGAKCgojIyBNZWRpYW4tb2YtUmF0aW9zIE1ldGhvZCAoZGVmYXVsdCBvZiBgREVTZXEyYCkKClRoZSBtZWRpYW4tb2YtcmF0aW9zIG1ldGhvZCBpcyB1c2VkIGluIGBERVNlcTJgIGFzIGRlc2NyaWJlZCBpbiBbTG92ZSAqZXQgYWwuKiAoMjAxNCldKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMzA1OS0wMTQtMDU1MC04KS4KSXQgYXNzdW1lcyB0aGF0IHRoZSBleHBlY3RlZCB2YWx1ZSAkXG11X3tnaX0gPSBFKFlfe2dpfSkkIGlzIHByb3BvcnRpb25hbCB0byB0aGUgdHJ1ZSBleHByZXNzaW9uIG9mIHRoZSBnZW5lLCAkcV97Z2l9JCwgc2NhbGVkIGJ5IGEgbm9ybWFsaXphdGlvbiBmYWN0b3IgJHNfe2l9JCBmb3IgZWFjaCBzYW1wbGUsCiQkIFxtdV97Z2l9ID0gc197aX1xX3tnaX0uICQkCgpUaGUgbm9ybWFsaXphdGlvbiBmYWN0b3IgJHNfe2l9JCBpcyB0aGVuIGVzdGltYXRlZCB1c2luZyB0aGUgbWVkaWFuLW9mLXJhdGlvcyBtZXRob2QgY29tcGFyZWQgdG8gYSBzeW50aGV0aWMgcmVmZXJlbmNlIHNhbXBsZSAkciQgZGVmaW5lZCBiYXNlZCBvbiBnZW9tZXRyaWMgbWVhbnMgb2YgY291bnRzIGFjcm9zcyBzYW1wbGVzCiQkCnNfaSA9IFx0ZXh0e21lZGlhbn1fe1x7e2c6WV57Kn1fe2dyfSBcbmUgMH1cfX0gXGZyYWN7WV97Z2l9fXtZXnsqfV97Z3J9fSwKJCQKd2l0aCAKJCQgWV57Kn1fe2dyfSA9IFxsZWZ0KCBccHJvZF97aT0xfV5uIFlfe2dpfSBccmlnaHQpXnsxL259LiAkJAoKV2UgY2FuIHRoZW4gdXNlIHRoZSBzaXplIGZhY3RvcnMgJHNfaSQgYXMgb2Zmc2V0cyB0byB0aGUgR0xNLgoKTWVkaWFuLW9mLXJhdGlvcyBub3JtYWxpemF0aW9uIGlzIGltcGxlbWVudGVkIGluIHRoZSBgREVTZXEyYCBwYWNrYWdlOgoKYGBge3J9CmRkcyA8LSBERVNlcTI6OkRFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gYXNzYXlzKHNlKSRjb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbERhdGEoc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gMSkgI2p1c3QgYWRkIGludGVyY2VwdCB0byBzaG93Y2FzZSBub3JtYWxpemF0aW9uCmRkcyA8LSBERVNlcTI6OmVzdGltYXRlU2l6ZUZhY3RvcnMoZGRzKQpzaXplRmFjdG9ycyhkZHMpCmBgYAoKWW91IG1heSBhbHNvIHdhbnQgdG8gY2hlY2sgb3V0IHRoZSBbU3RhdFF1ZXN0IHZpZGVvIG9uIERFU2VxMiBub3JtYWxpemF0aW9uXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PVVGQjk5M3h1ZlVVKS4KCiMjIyBDb21wYXJpbmcgVE1NIHdpdGggREVTZXEyIE5vcm1hbGl6YXRpb24KCldlIGNhbiBjb21wYXJlIHRoZSBzaXplIGZhY3RvcnMgZm9yIGJvdGggbm9ybWFsaXphdGlvbnMgdG8gdmVyaWZ5IGlmIHRoZXkgYWdyZWUgb24gdGhlIG5vcm1hbGl6YXRpb24gZmFjdG9ycy4gTm90ZSB3ZSBuZWVkIHRvIHNjYWxlIHRoZSBlZmZlY3RpdmUgbGlicmFyeSBzaXplcyBmcm9tIGBlZGdlUmAgdG8gZW5mb3JjZSBhIHNpbWlsYXIgc2NhbGUgYXMgdGhlIHNpemUgZmFjdG9ycyBmcm9tIGBERVNlcTJgLiBXaGlsZSBiZWxvdyB3ZSBhcmUgdXNpbmcgYW4gYXJpdGhtZXRpYyBtZWFuLCBhIGdlb21ldHJpYyBtZWFuIG1heSBiZSB1c2VkIGFzIHdlbGwsIHdoaWNoIHdpbGwgYmUgbW9yZSByb2J1c3QgdG8gb3V0bHlpbmcgZWZmZWN0aXZlIGxpYnJhcnkgc2l6ZXMuCgpgYGB7cn0KcGxvdChlZmZMaWJTaXplIC8gbWVhbihlZmZMaWJTaXplKSwgc2l6ZUZhY3RvcnMoZGRzKSwKICAgICB4bGFiID0gImVkZ2VSIHNpemUgZmFjdG9yIiwKICAgICB5bGFiID0gIkRFU2VxMiBzaXplIGZhY3RvciIpCmBgYAoKIyBBbGlhc2luZwoKU3VwcG9zZSB3ZSBhcmUgd29ya2luZyB3aXRoIHRoZSBmb2xsb3dpbmcgZXhwZXJpbWVudGFsIGRlc2lnbiBvbiBjb2xvbiBjYW5jZXIuIFN0dWR5aW5nIHRoZSBlZmZlY3Qgb2YgYSBkcnVnIG9uIGdlbmUgZXhwcmVzc2lvbiwgcmVzZWFyY2hlcnMgZ2F0aGVyIFJOQS1zZXEgZGF0YSBmcm9tIGZvdXIgY29sb24gY2FuY2VyIHBhdGllbnRzIGFuZCBmb3VyIGhlYWx0aHkgaW5kaXZpZHVhbHMuIEZvciBlYWNoIGluZGl2aWR1YWwsIHRoZXkgb2J0YWluIFJOQS1zZXEgZGF0YSBmcm9tIGEgYmxvb2Qgc2FtcGxlIGJlZm9yZSBhcyB3ZWxsIGFzIHR3byB3ZWVrcyBhZnRlciB0YWtpbmcgYSBkYWlseSBkb3NlIG9mIHRoZSBkcnVnLiBUaGUgcmVzZWFyY2ggcXVlc3Rpb24gcmVsYXRlcyB0byBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhZnRlciB2cy4gYmVmb3JlIHRha2luZyB0aGUgZHJ1ZywgaW4gcGFydGljdWxhciB3aGV0aGVyIHRoaXMgaXMgZGlmZmVyZW50IGZvciB0aGUgZGlzZWFzZWQgdmVyc3VzIGhlYWx0aHkgZ3JvdXAgKGkuZS4sIHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRpbWUgKGJlZm9yZS9hZnRlciB0YWtpbmcgdGhlIGRydWcpIGFuZCBkaXNlYXNlIHN0YXR1cyAoaGVhbHRoeS9jb2xvbiBjYW5jZXIpKS4KCkluIHRlcm1zIG9mIHRoZSBtb2RlbCBtYXRyaXgsIHdlIGNvdWxkIGltYWdpbmUgYSBkZXNpZ24gc3VjaCBhcyBgIH4gcGF0aWVudCArIGRpc2Vhc2UqdGltZWAsIHdoZXJlIAoKIC0gYGRpc2Vhc2VgIGlzIGEgYmluYXJ5IGluZGljYXRvciByZWZlcnJpbmcgdG8gY29sb24gY2FuY2VyIHZlcnN1cyBjb250cm9sIHNhbXBsZS4KIC0gYHRpbWVgIGRlZmluZXMgaWYgdGhlIHNhbXBsZSBpcyB0YWtlbiBiZWZvcmUgb3IgYWZ0ZXIgdGFraW5nIHRoZSBkcnVnLgogLSBgcGF0aWVudGAgZGVmaW5lcyB0aGUgaW5kaXZpZHVhbCBkb25vciB0aGUgc2FtcGxlIGNvbWVzIGZyb20uCgpUaGUgcmVzZWFyY2ggcXVlc3Rpb24gY291bGQgdGhlbiBhbW91bnQgdG8gdGVzdGluZyB0aGUgYGRpc2Vhc2UgKiB0aW1lYCBpbnRlcmFjdGlvbi4KCkxldCdzIHRyeSB0aGlzLCBieSBzaW11bGF0aW5nIHJhbmRvbSBkYXRhIGZvciBvbmUgZ2VuZS4KCmBgYHtyfQpzZXQuc2VlZCgyKQojIDIgc2FtcGxlcyBwZXIgcGF0aWVudCBmb3IgOCBwYXRpZW50cwpwYXRpZW50IDwtIGZhY3RvcihyZXAobGV0dGVyc1sxOjhdLCBlYWNoPTIpKSAKIyBmaXJzdCBmb3VyIGFyZSBoZWFsdGh5LCBuZXh0IGZvdXIgYXJlIGRpc2Vhc2VkCmRpc2Vhc2UgPC0gZmFjdG9yKGMocmVwKCJoZWFsdGh5Iiw4KSwgcmVwKCJjYW5jZXIiLDgpKSwgbGV2ZWxzPWMoImhlYWx0aHkiLCAiY2FuY2VyIikpIAojIG9uZSBiZWZvcmUgYW5kIG9uZSBhZnRlciBzYW1wbGUgZm9yIGVhY2gKdGltZSA8LSBmYWN0b3IocmVwKGMoImJlZm9yZSIsICJhZnRlciIpLCA4KSwgbGV2ZWxzPWMoImJlZm9yZSIsICJhZnRlciIpKSAKCnRhYmxlKHBhdGllbnQsIGRpc2Vhc2UsIHRpbWUpCgojIyBzaW11bGF0ZSBkYXRhIGZvciBvbmUgZ2VuZQpuIDwtIDE2CnkgPC0gcnBvaXMobiA9IG4sIGxhbWJkYSA9IDUwKQoKIyMgZml0IGEgUG9pc3NvbiBtb2RlbAptIDwtIGdsbSh5IH4gcGF0aWVudCArIGRpc2Vhc2UqdGltZSwKICAgICAgICAgZmFtaWx5ID0gInBvaXNzb24iKQpzdW1tYXJ5KG0pCmBgYAoKIC0tLQogCldlIGZpbmQgdGhhdCBvbmUgb2YgdGhlIGNvZWZmaWNpZW50cyBpcyBgTkFgISBUaGlzIGlzIG9idmlvdXNseSBub3QgYmVjYXVzZSB3ZSdyZSBkZWFsaW5nIHdpdGggYE5BYCB2YWx1ZXMgaW4gdGhlIGRhdGEgYXMgd2UndmUganVzdCBzaW11bGF0ZWQgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIG91cnNlbHZlcy4gV2hhdCdzIGdvaW5nIG9uPwoKT25lIG9mIHRoZSBwYXJhbWV0ZXJzLCBpbiB0aGlzIGNhc2UgdGhlIHBhcmFtZXRlciBkaXN0aW5ndWlzaGluZyBjYW5jZXIgZnJvbSBoZWFsdGh5IHBhdGllbnRzICoqY2Fubm90IGJlIGVzdGltYXRlZCBhcyBpdCBpcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiBvdGhlciBwYXJhbWV0ZXJzKiouIEluIG91ciBjYXNlLCBlc3RpbWF0aW5nIHRoZSBkaXNlYXNlZCBlZmZlY3Qgd291bGQgdXNlIGluZm9ybWF0aW9uIHRoYXQgaXMgYWxyZWFkeSB1c2VkIHRvIGVzdGltYXRlIHRoZSBwYXRpZW50LWxldmVsIGludGVyY2VwdHMuIEluIG90aGVyIHdvcmRzLCAqKm9uY2UgeW91IGtub3cgdGhlIHBhdGllbnQsIHlvdSBpbW1lZGlhdGVseSBhbHNvIGtub3cgdGhlIGRpc2Vhc2Ugc3RhdHVzKiosIHNvIGVzdGltYXRpbmcgdGhlIGRpc2Vhc2VkIHZzIGhlYWx0aHkgZWZmZWN0IG9uIHRvcCBvZiB0aGUgcGF0aWVudCBlZmZlY3QgcHJvdmlkZXMgbm8gYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBpZiB3ZSBoYXZlIGFscmVhZHkgZXN0aW1hdGVkIHRoZSBwYXRpZW50LWxldmVsIGVmZmVjdHMuIFRoaXMgY29uY2VwdCBpcyBjYWxsZWQgYWxpYXNpbmcsIGFuZCBpcyBhIGNvbW1vbiB0ZWNobmljYWwgaXNzdWUgaW4gJ29taWNzIGV4cGVyaW1lbnRzIHdpdGggY29tcGxleCBleHBlcmltZW50YWwgZGVzaWducy4gCgogLS0tCgpXaGlsZSB0byB1bmRlcnN0YW5kIHRoZSBvcmlnaW4gb2YgdGhlIGFsaWFzaW5nIGl0IGlzIGNydWNpYWwgdG8gdW5kZXJzdGFuZCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHZhcmlhYmxlcyBpbiB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbiwgd2UgY2FuIGFsc28gaW52ZXN0aWdhdGUgaXQgaW4gZGV0YWlsIHVzaW5nIHRoZSBgYWxpYXNgIGZ1bmN0aW9uLCB0byBnaXZlIHVzIGFuIGlkZWEuCgpgYGB7cn0KYWxpYXMobSkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgZWZmZWN0IGBkaXNlYXNlY2FuY2VyYCBpcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0aGUgcGF0aWVudC1zcGVjaWZpYyBlZmZlY3RzIG9mIHRoZSBjYW5jZXIgcGF0aWVudHMuIFRoaXMgbWFrZXMgc2Vuc2UhCgogLS0tIAoKRm9yIGNsYXJpdHksIGxldCdzIHJlcHJvZHVjZSB0aGlzIHVzaW5nIG91ciBkZXNpZ24gbWF0cml4LgoKYGBge3J9ClggPC0gbW9kZWwubWF0cml4KH4gcGF0aWVudCArIGRpc2Vhc2UqdGltZSkgIyB0aGlzIGlzIHRoZSBkZXNpZ24gdXNlZCBpbiBnbG0oKQoKIyMgdGhlc2UgYXJlIGluZGVlZCBpZGVudGljYWwuClhbLCJkaXNlYXNlY2FuY2VyIl0KWFssInBhdGllbnRlIl0gKyBYWywicGF0aWVudGYiXSArIFhbLCJwYXRpZW50ZyJdICsgWFssInBhdGllbnRoIl0KYGBgCgpTaW5jZSBvbmUgb2Ygb3VyIHBhcmFtZXRlcnMgaXMgYSBsaW5lYXIgY29tYmluYXRpb24gb2Ygb3RoZXIgcGFyYW1ldGVycywgaXQgY2Fubm90IGJlIGVzdGltYXRlZCBzaW11bHRhbmVvdXNseSB3aXRoIHRoZSBvdGhlciBwYXJhbWV0ZXJzLiBJbiB0aGlzIGNhc2UsIHdlIGNhbiBhY3R1YWxseSBkcm9wIHRoZSBgZGlzZWFzZWAgbWFpbiBlZmZlY3QgZnJvbSB0aGUgbW9kZWwsIHNpbmNlIHdlIGtub3cgdGhhdCBpdCBpcyBhbHJlYWR5IGluY2x1ZGVkIGluIHRoZSBgcGF0aWVudGAgZWZmZWN0LgoKIC0tLQoKV2Ugd2lsbCBoYXZlIHRvIGNhcmVmdWxseSBjb25zdHJ1Y3Qgb3VyIGRlc2lnbiBtYXRyaXggaW4gb3JkZXIgdG8gYWNjb3VudCBmb3IgYWxsIGltcG9ydGFudCBzb3VyY2VzIG9mIHZhcmlhdGlvbiB3aGlsZSBzdGlsbCBhbGxvd2luZyB1cyB0byBhbnN3ZXIgdGhlIHJlc2VhcmNoIHF1ZXN0aW9uIG9mIGludGVyZXN0LiBUaGUgYWxpYXNpbmcgZXhwbG9yYXRpb24gYWJvdmUgaGFzIG1hZGUgaXQgY2xlYXIgd2UgbWF5IGRyb3AgdGhlIGBkaXNlYXNlYCBtYWluIGVmZmVjdCwgc28gbGV0J3Mgc3RhcnQgYnkgY29uc3RydWN0aW5nIHRoaXMgZGVzaWduIG1hdHJpeC4KCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeCh+IHBhdGllbnQgKyB0aW1lICsgZGlzZWFzZTp0aW1lKQoKbTIgPC0gZ2xtKHkgfiAtMSArIFgsCiAgICAgICAgIGZhbWlseSA9ICJwb2lzc29uIikKc3VtbWFyeShtMikKYWxpYXMobTIpCmBgYAoKV2UgYXJlIHN0aWxsIGNvbmZyb250ZWQgd2l0aCBhbGlhc2luZyBhcyB0aGUgbW9kZWwgbWF0cml4IGNvbnRhaW5zIGFuIGludGVyYWN0aW9uIGVmZmVjdCBgdGltZWJlZm9yZTpkaXNlYXNlY2FuY2VyYCBhcyB3ZWxsIGFzIGB0aW1lYWZ0ZXI6ZGlzZWFzZWNhbmNlcmAsIHdoaWxlIG9ubHkgdGhlIGxhdHRlciBpcyByZWxldmFudC4gSW5kZWVkLCB3ZSBrbm93IHRoYXQgd2UgY2FuIGRlcml2ZSB0aGUgYHRpbWViZWZvcmU6ZGlzZWFzZWNhbmNlcmAgZWZmZWN0IGJ5IGF2ZXJhZ2luZyB0aGUgcGF0aWVudCBlZmZlY3RzIG9mIHRoZSBjYW5jZXIgcGF0aWVudHMuCgogLS0tCgpgYGB7cn0KWCA8LSBYWywhY29sbmFtZXMoWCkgJWluJSAidGltZWJlZm9yZTpkaXNlYXNlY2FuY2VyIl0KCgojIyBmaXQgYSBQb2lzc29uIG1vZGVsCm0yIDwtIGdsbSh5IH4gLTEgKyBYLAogICAgICAgICBmYW1pbHkgPSAicG9pc3NvbiIpCnN1bW1hcnkobTIpCmBgYAoKV2Ugc2VlIHRoYXQgYWxsIGNvZWZmaWNpZW50cyBjYW4gbm93IGJlIGVzdGltYXRlZC4gVGhlIGB0aW1lYWZ0ZXJgIGVmZmVjdCBtYXkgYmUgaW50ZXJwcmV0ZWQgYXMgdGhlIHRpbWUgZWZmZWN0IGZvciBoZWFsdGh5IHBhdGllbnRzLCB3aGlsZSB0aGUgYHRpbWVhZnRlcjpkaXNlYXNlY2FuY2VyYCBlZmZlY3QgbWF5IGJlIGludGVycHJldGVkIGFzIHRoZSBkaWZmZXJlbmNlIGluIHRoZSB0aW1lIGVmZmVjdCBmb3IgY2FuY2VyIHBhdGllbnRzIGFzIGNvbXBhcmVkIHRvIGhlYWx0aHkgcGF0aWVudHMsIGkuZS4sIGl0IGlzIHRoZSByZWxldmFudCBpbnRlcmFjdGlvbiBlZmZlY3Qgd2UgYXJlIGludGVyZXN0ZWQgaW4uIAoK