In this lecture we will introduce the main principles of working with count data, and how to model these using generalized linear models (GLMs). We focus on introducing the concept of generalized linear models, and how to interpret its results. We touch briefly upon statistical inference, providing the main results rather than the theory behind it, such that they can be applied to genomics data analysis.

1 The Poisson distribution

  • The Poisson distribution is a typical count distribution that is generally popular and fairly easy to work with. It is defined by a single parameter: its mean \(\mu\). For a Poisson distributed random variable \(Y_i\) with observations \(i \in \{1, \ldots, n\}\), its variance is equal to its mean. That is, if \(Y_i \sim Poi(\mu)\), then \(E(Y_i) = Var(Y_i) = \mu\).
  • This immediately shows an important feature of count data: the mean-variance relationship. Indeed, in count data, the variance will always be a function of the mean.
  • This is quite intuitive. Consider the following example. You have two bird cages, where in one bird cage there are \(10\) birds, while in the other there are \(100\) birds. You let a sample of people count the number of birds in either one of the cages. It seems unlikely that a person in front of the 10-bird cage would come up with an estimate of \(5\), while it seems quite likely that someone in front of the 100-bird cage would come up with an estimate of \(95\). Even though the difference from the true value is the same, the exact value has an impact on the plausible deviation around it.
set.seed(11)
y1 <- rpois(n=500, lambda=10)
y2 <- rpois(n=500, lambda=100)

par(mfrow = c(1,2))
hist(y1, main="Poisson(10)", breaks=40)
hist(y2, main="Poisson(100)", breaks=40)

1.1 The Poisson distribution in RNA-seq

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

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

1.2 Relative uncertainty for Poisson distributed random variables

Take a minute to consider the following question:

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

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

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

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


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

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

2 Modeling count data: Generalized linear models

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

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

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

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

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

  • The variance-covariance matrix of \(\mathbf{Y}\) is assumed a diagonal matrix with \(\sigma^2\) on the diagonal elements and zero everywhere else. This means that the data points are uncorrelated, and that every observation has the same variance \(\sigma^2\), also referred to as homoscedasticity.
  • The latter doesn’t hold for count data, due to the mean-variance relationship. This makes linear models, in its basic form, unsuitable to model count data.
  • In addition, count data are non-negative, while there are no such constraints in the standard linear model to make sure that our estimates will be non-negative. Indeed, \(\hat{Y}_i = \mathbf{X}^T_i \hat{\beta} \in ] -\infty, \infty[\).

2.2 Generalized linear models

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

2.2.1 A Poisson GLM

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

2.2.2 Parameter estimation using maximum likelihood

  • In maximum likelihood, we attempt to maximize the likelihood function of the data, under the posited assumptions. The likelihood function is typically parametrized by a limited number of parameters, hence we can find the values of the parameters that maximize the likelihood function.
  • We do this by finding the point on the likelihood function where its first derivative equals zero, as this must be a maximum of the function. For non-convex likelihood functions, this may be a local maximum, but for GLMs the likelihood function is convex and therefore the obtained maximum must be the global maximum.

2.2.2.1 Maximum likelihood for a linear model

For linear models, we can derive an equivalent estimator for \(\beta\) using maximum likelihood estimation as we had derived in our recap lecture using least squares estimation. We can define a linear model as

\[ Y_i \sim N(\mu_i, \sigma^2\mathbf{I}) \\ \mu_i = \mathbf{X}_i \mathbf{\beta} \]

The likelihood function of the data is the product of the likelihoods of each datum. Since we are assuming a Gaussian distribution, we use the Gaussian probability density function:

\[ L(\mathbf{Y}; \beta, \sigma) = \prod_{i=1}^n \frac{1}{\sqrt{2 \pi \sigma^2}} \exp \left\{ - \frac{(Y_i - \mathbf{X}_i \beta)^2}{2 \sigma^2} \right\} \]

Log-likelihood function

\[ \ell(\mathbf{Y}; \beta, \sigma) = \sum_{i=1}^n \left\{ -\frac{1}{2} \log(2\pi \sigma^2) - \frac{1}{2\sigma^2} (Y_i - \mathbf{X}_i \beta)^2 \right\} \]

Score function is the derivative of the log-likelihood

\[ S(\mathbf{\beta}) = \frac{\partial \ell(\mathbf{Y}; \beta, \sigma)}{\partial \beta} = \sum_{i=1}^n \frac{1}{\sigma^2} \mathbf{X}_i (Y_i - \mathbf{X}_i \beta) \]

Set to zero and solve

\[ \mathbf{X}^T\mathbf{Y} - \mathbf{X}^T \mathbf{X} \mathbf{\beta} = \mathbf{0} \\ \rightarrow \widehat{\mathbf{\beta}} = (\mathbf{X}^T \mathbf{X})^{-1} \mathbf{X}^T\mathbf{Y} \]

which gives us exactly the same estimator as we had derived using least squares!

2.2.2.2 Maximum likelihood for a generalized linear model

Now that we know how to use maximum likelihood for parameter estimation, we can also apply it to estimate the parameters of a generalized linear model. Let’s try it for the Poisson GLM we have just introduced:

\[ \left\{ \begin{array}{ccc} Y_i & \sim & Poi(\mu_i) \\ \log \mu_i & = & \eta_i \\ \eta_i & = & \mathbf{X}^T_i \beta \\ \end{array} \right. \]

Likelihood function of the Poisson distribution

\[ L(Y_i ; \mu) = \prod_{i=1}^n \frac{e^{-\mu} \mu^{Y_i}}{Y_i!} \]

Log-likelihood function

\[ \ell(Y_i ; \mu) = \sum_{i=1}^n - \mu + Y_i \log(\mu) - \log (Y_i!) \]

Note that the score function is the derivative of the log-likelihood with respect to our parameter of interest, \(\beta\). So let’s first rewrite our log-likelihood as a function of our parameter of interest. We know from the model that \(\mu_i = \exp(\mathbf{X}_i \mathbf{\beta})\).

\[ \ell(Y_i ; \beta) = \sum_{i=1}^n - \exp(\mathbf{X}_i \mathbf{\beta}) + Y_i (\mathbf{X}_i \mathbf{\beta}) - \log (Y_i!) \]

The score function then equals

\[ S(\mathbf{\beta}) = \frac{\partial \ell(\mathbf{Y}; \beta)}{\partial \beta} = \sum_{i=1}^n -\mathbf{X}_i^T \exp(\mathbf{X}_i \mathbf{\beta}) + \mathbf{X}_i^TY_i = -\mathbf{X}^T \exp(\mathbf{X} \mathbf{\beta}) + \mathbf{X}^T \mathbf{Y} \]

Set to zero and solve

\[ \mathbf{X}^T \mathbf{Y} = \mathbf{X}^T \exp(\mathbf{X} \mathbf{\beta}) \]

However, since this is a non-linear equation in \(\beta\), we cannot find a closed-form solution! You may see this more clearly when writing out in non-matrix form

\[ \sum_i \sum_p x_{ip} \exp(x_{ip} \beta_p) = \sum_i \sum_p x_{ip} Y_i. \]


  • The above derivations show that estimating the parameters of a GLM is much harder as compared to a linear model.
  • The iterative reweighted least squares (IRLS) algorithm is usually adopted for fitting GLMs using maximum likelihood. As the name suggests, it is an iterative algorithm, where each data point is reweighted in each iteration according to the assumed mean-variance relationship, which is a function of its estimated mean of the previous iteration. Indeed, observations with high variance will be downweighted and vice versa. IRLS uses the derivative of the score function (i.e., the second derivative of the log-likelihood function) to move into the direction where the first derivative is zero.

Figure: Finding the root of the score function using Newton-Raphson optimization. The Figure shows estimation of a single \(\beta\) parameter. The black solid line is the Score function evaluated at \(\beta\). An initial estimate of \(\beta\) is 2.25, which is represented by the dotted line. The value of the score function of this initial value is \(S(\beta^k)\). The first derivative of the score function at that point, evaluated at \(\beta = 2.25\), is represented by the solid blue line and is given by \(\frac{\partial S(\beta)}{\partial \beta}\). The value of \(\beta\) where the solid blue line crosses zero is the new estimate for \(\beta\), namely \(\beta^{k+1}\) which has a value of 1.4. The difference between \(\beta^{k+1}\) and \(\beta^{k}\) is given by \(\left \{ \frac{ \partial S(\beta)}{ \partial \beta} \right \}^{-1} S(\beta^k)\). This procedure is iterated until a convergence in the \(\beta\) estimate is met.

2.2.3 Generalized linear models in R

  • In order to get familiar with GLMs, we will fit a Poisson GLM in R, using the Bikeshare dataset as part of the ISLR2 package. This dataset records how many bikes were being used from a bike-sharing service, every hour of the day over a full year (365 days).

  • Full information of the dataset is provided here. Variables of interest for us are:

    • bikers: Discrete count variable; the number of bikes being used that hour.

    • hum: Continuous variable ranging between 0 and 1; normalized humidity.

    • hr: Categorical variable between 0 and 23; the hour of the day. One could also consider this variable to be numeric and model it as such, but the data exploration will show that’s not appropriate.

    • weathersit: Categorical variable; the weather condition of that hour, with

      1. Clear, Few clouds, Partly cloudy.
      2. Mist + Cloudy, Mist + Broken clouds, Mist + Few clouds, Mist.
      3. Light Snow, Light Rain + Thunderstorm + Scattered clouds, Light Rain + Scattered clouds.
      4. Heavy Rain + Ice Pallets + Thunderstorm + Mist, Snow + Fog.

# if ISLR2 isn't installed, install it:
if(!"ISLR2" %in% installed.packages()[,1]){
  install.packages("ISLR2")
}
## Installing package into '/Users/runner/work/_temp/Library'
## (as 'lib' is unspecified)
## 
## The downloaded binary packages are in
##  /var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T//RtmpmpbBq0/downloaded_packages
# load and preview the dataset:
data("Bikeshare", package="ISLR2")
head(Bikeshare)
# association with weather on count and log scale
barplot(table(Bikeshare$weathersit))

boxplot(bikers ~ weathersit, data=Bikeshare,
        xlab = "Weather", ylab = "Bikers")

boxplot(log1p(bikers) ~ weathersit, data=Bikeshare,
        xlab = "Weather", ylab = "Log (bikers +1)")

# association with humidity on count and log scale
hist(Bikeshare$hum, breaks=40)

plot(bikers ~ hum, data=Bikeshare, pch=16, cex=1/2,
        xlab = "Humidity", ylab = "Bikers")
loHum <- loess(bikers ~ hum, data=Bikeshare)
xGrid <- seq(0, 1, length=50)
yhat <- predict(loHum, data.frame(hum = xGrid))
lines(x=xGrid, y=yhat, col="red", lwd=3)

plot(log1p(bikers) ~ hum, data=Bikeshare, pch=16, cex=1/2,
        xlab = "Humidity", ylab = "Log (bikers +1)")
loHum <- loess(log1p(bikers) ~ hum, data=Bikeshare)
xGrid <- seq(0, 1, length=50)
yhat <- predict(loHum, data.frame(hum = xGrid))
lines(x=xGrid, y=yhat, col="red", lwd=3)

# association with hour on count and log scale
barplot(table(Bikeshare$hr), xlab="Hour of day", ylab="Number of observations")

plot(bikers ~ hr, data=Bikeshare, pch=16, cex=1/2,
        xlab = "Hour of day", ylab = "Bikers")

plot(log1p(bikers) ~ hr, data=Bikeshare, pch=16, cex=1/2,
        xlab = "Hour of day", ylab = "Log (bikers +1)")

The data exploration shows that

  • More bikes are being used in better weather.
  • There seems to be a non-linear association between bicycle rentals and humidity, where in both low and high humidity conditions relatively few bikes are used, possibly reflecting very hot and very wet days respectively, while most bikes are being used at moderate humidity.
  • Bicycle rental is associated with the hour of the day, however, in a non-linear way, with clear peaks in usage at typical commute hours (6h-8h and 17h-19h). Here, we will add hr as a categorical variable to the model, estimating one parameter for each hour. Note that alternative strategies are possible that may be more efficient, such as incorporating hr as a numerical variable and modeling the non-linearity using a lower number of parameters.
  • Disclaimer: Note that there are likely interactions between the variables, which here we will not evaluate as our goal is to introduce a Poisson GLM rather than a full analysis of the Bikeshare dataset. For example, it seems likely that more people commute by bike in good weather, while fewer people will commute by bike in terrible weather. This would motivate an interaction between the variables weathersit and hr.

  • Below, we fit a Poisson GLM using the glm function. The number of bikers is used as a response variable, which is modeled as a function of weathersit, hum and hr.
  • Note that there seems to be a non-linear, though fairly simple, association between our response variable and the humidity. We will therefore add a quadratic and cubic term for humidity to the model. In order to avoid multicollinearity between the linear, quadratic and cubic humidity effects, we will first center the humidity variable and store this in a new variable called humc. This means that when humc=0, this corresponds to the average humidity in the dataset.
  • The argument family = "poisson" specifies the Poisson distribution for the response variable and by default the canonical link function, which is the log link, will be used.
Bikeshare$humc <- Bikeshare$hum - mean(Bikeshare$hum)
m <- glm(bikers ~ weathersit + humc + I(humc^2) + I(humc^3) + hr,
         data = Bikeshare,
         family = "poisson")
summary(m)
## 
## Call:
## glm(formula = bikers ~ weathersit + humc + I(humc^2) + I(humc^3) + 
##     hr, family = "poisson", data = Bikeshare)
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -23.3408   -4.6201   -0.9922    3.4605   27.4153  
## 
## Coefficients:
##                            Estimate Std. Error  z value Pr(>|z|)    
## (Intercept)                3.893651   0.008083  481.708   <2e-16 ***
## weathersitcloudy/misty    -0.146618   0.002277  -64.401   <2e-16 ***
## weathersitlight rain/snow -0.556153   0.004585 -121.292   <2e-16 ***
## weathersitheavy rain/snow -1.855194   0.166742  -11.126   <2e-16 ***
## humc                       0.091751   0.009233    9.938   <2e-16 ***
## I(humc^2)                 -2.233919   0.029421  -75.929   <2e-16 ***
## I(humc^3)                 -1.823066   0.091428  -19.940   <2e-16 ***
## hr1                       -0.476470   0.012999  -36.654   <2e-16 ***
## hr2                       -0.806959   0.014646  -55.099   <2e-16 ***
## hr3                       -1.433648   0.018842  -76.090   <2e-16 ***
## hr4                       -2.058714   0.024796  -83.027   <2e-16 ***
## hr5                       -1.061695   0.016074  -66.051   <2e-16 ***
## hr6                        0.315691   0.010607   29.761   <2e-16 ***
## hr7                        1.317856   0.009052  145.586   <2e-16 ***
## hr8                        1.830026   0.008653  211.480   <2e-16 ***
## hr9                        1.352135   0.009022  149.871   <2e-16 ***
## hr10                       1.129497   0.009271  121.831   <2e-16 ***
## hr11                       1.308554   0.009102  143.766   <2e-16 ***
## hr12                       1.522234   0.008947  170.131   <2e-16 ***
## hr13                       1.536827   0.008959  171.542   <2e-16 ***
## hr14                       1.499506   0.008999  166.633   <2e-16 ***
## hr15                       1.535043   0.008974  171.062   <2e-16 ***
## hr16                       1.745128   0.008800  198.318   <2e-16 ***
## hr17                       2.140488   0.008565  249.925   <2e-16 ***
## hr18                       2.037740   0.008588  237.279   <2e-16 ***
## hr19                       1.711545   0.008747  195.667   <2e-16 ***
## hr20                       1.393901   0.008976  155.284   <2e-16 ***
## hr21                       1.132543   0.009216  122.895   <2e-16 ***
## hr22                       0.882534   0.009537   92.539   <2e-16 ***
## hr23                       0.481354   0.010207   47.157   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 1052921  on 8644  degrees of freedom
## Residual deviance:  375265  on 8615  degrees of freedom
## AIC: 428362
## 
## Number of Fisher Scoring iterations: 5

2.2.3.1 Interpretation of estimated model parameters

  • Remember that the Poisson GLM can be defined as \[ \left\{ \begin{array}{ccc} Y_i & \sim & Poi(\mu_i) \\ \log \mu_i & = & \eta_i \\ \eta_i & = & \mathbf{X}^T_i \beta \\ \end{array} \right. \]

Interpretation of the intercept.

  • We will first interpret the intercept, in terms of the average number of bikes being used. Note that the intercept corresponds to hour 0, at good weather (weathersit level 1), and average humidity (humc=0). We will denote the intercept as \(\beta_0\) and its estimate as \(\hat{\beta}_0\). All other coefficients will thus denote a relative change with respect to that reference level.
  • The model definition shows that \(\log \mu_i = \mathbf{X}^T_i \mathbf{\beta}\), with \(\mu\) the average number of bikes being used. Since we’re only working with the intercept here, we may write \(\log \mu_i = \beta_0\), and thus \(\mu_i = \exp \beta_0\).
  • Plugging in the estimated intercept \(\hat{\beta}_0\), we have \(\exp \hat{\beta}_0 =\) 49.09. In other words, in clear weather with few clouds, at average humidity and at hour 0, an average of 49.09 bikes are being used.

Interpretation of weathersitcloudy/misty.

  • We will denote this coefficient as \(\beta_1\) and its estimate as \(\hat{\beta}_1\).
  • Note that this coefficient defines the difference in linear predictor between weathersit=2 and weathersit=1, all other variables being equal (say, at their reference level). Indeed, define \(\eta_{w2}\) and \(\eta_{w1}\) to denote the linear predictor at weathersit=2, and weathersit=1, respectively. Then, \(\eta_{w2} - \eta_{w1} = (\beta_0 + \beta_1) - \beta_0 = \beta_1\).
  • This also means that \(\beta_1 = \log \mu_{w2} - \log \mu_{w1} = \log \frac{\mu_{w2}}{\mu_{21}}\), and thus \(\exp \beta_1 = \frac{\mu_{w2}}{\mu_{21}}\).
  • In our case, \(\exp \hat{\beta}_1 =\) 0.86. In words: All other variables being equal, the average number of bikes being used in cloudy/misty weather is \(0.85\) times (or, also, \(85\%\) of) the number of bikes being used in good weather.
  • This exercise has shown us that, due to the \(\log\) link function, the parameters in a Poisson GLM cannot be interpreted in terms of absolute differences in averages of the response variable but instead must be interpreted in terms of multiplicative differences!
  • If you’re in a meeting and you need a quick way to interpret these parameters, remember that \(exp(1) = 2.72 \approx 3\) and thus a difference of \(1\) (\(-1\)) means the average of the response variable is about three times higher (lower).

Interpretation of the humidity effect.

  • The humidity effect is a bit more involved to interpret. Due to the quadratic and cubic terms, it is not straight forward to interpret the linear term separately (and the same applies to the quadratic or cubic term); we must interpret both the linear, quadratic and cubic term simultaneously.

  • Also due to the higher-order terms, the rate of change in average bikers will not be constant across the range of humidity. We can therefore not interpret the humidity effect using a single number as we’ve done previously.

  • We can, however, provide some examples for specific humidity values, along with a visualization of its global effect.

  • For example, let’s derive the change in average bikes being used at a humidity that is \(0.2\) above average, versus average humidity.

    For average humidity \(+0.2\) the linear predictor \(\eta_{0.2} = \beta_0 + \beta_4 x_{hum} + \beta_5 x_{hum}^2 + \beta_6 x_{hum}^3 = \beta_0 + \beta_4 0.2 + \beta_5 0.2^2 + \beta_6 0.2^3\).

    For average humidity, the linear predictor \(\eta_{0} = \beta_0 + \beta_4 x_{hum} + \beta_5 x_{hum}^2 + \beta_6 x_{hum}^3 = \beta_0 + \beta_4 0 + \beta_5 0^2 + \beta_6 0^3 = \beta_0\).

    We thus have \(\log \frac{\mu_{0.2}}{\mu_0} = \beta_4 0.2 + \beta_5 0.2^2 + \beta_6 0.2^3\). In our case, \(\log \frac{\hat{\mu}_{0.2}}{\hat{\mu}_0} = 0.091751*0.2 - 2.233919 * 0.2^2 - 1.823066 * 0.2^3 = -0.0856\) and thus \(\frac{\hat{\mu}_{0.2}}{\hat{\mu}_0} = 0.92\). Therefore, at humidity that is \(0.2\) above average, the average number of bikes being used are \(0.92\) times the average number of bikes used at average humidity.

  • Just like with linear models, the predict function is extremely helpful when trying to visualize and understand a fitted GLM. In GLMs, the type argument becomes essential when using the predict function. Indeed, by default, estimates are provided on the linear predictor scale: in our case, on the \(\log\) scale. If we’d like predictions on the scale of the response variable, we need to set type="response". You can find more information in the help file using ?predict.glm.

  • The visualization shows that the highest number of bikes are being used at around average humidity, with a decreased usage at higher and lower humidities.

humidityGrid <- seq(min(Bikeshare$humc), max(Bikeshare$humc), 
                    length.out = 50)
newDf <- data.frame(weathersit = factor("clear"),
                    hr = factor(8),
                    humc = humidityGrid,
                    "I(humc^2)" = humidityGrid^2,
                    "I(humc^3)" = humidityGrid^3)
yhat <- predict(m, 
                newdata = newDf,
                type = "response")

plot(x = humidityGrid,
     y = yhat,
     type = 'l', lwd=2,
     xlab = "Centered humidity",
     ylab = "Average number of bikers")


Setting up a contrast.

  • Suppose we’re interested in whether there are more bikers at (A) maximum humidity (centered humidity value of \(0.357\)), hour 17, in the light rain/snow weather category, versus (B) average humidity, hour 8, in the clear weather category. This requires us to set up a contrast in terms of a linear combination of the model parameters.
  • Manually by hand: \[ \log \mu_A = \beta_0 + \beta_2 x_{rainSnow} + \beta_4 x_{hum} + \beta_5 x_{hum}^2 + \beta_6 x_{hum}^3 + \beta_{23} x_{hr17} \\= 3.894 - 0.556 + 0.092 * 0.357 - 2.234 * 0.357^2 - 1.823 * 0.357^3 + 2.140 = 5.143.\\ \log \mu_B = \beta_0 + \beta_{14} x_{hr8} = 3.894 + 1.830 = 5.724.\\ \frac{\mu_A}{\mu_B} = \exp(5.143 - 5.724) = 0.559. \] Thus, at maximum humidity, hour 17, in the light rain/snow weather category the average number of bikers is 56% times the average number of bikers in the average humidity, hour 8, in the clear weather category.
  • Manually in R: We can also use matrix multiplication to derive the estimates. We know from our manual calculations above, that the contrast of interest is \((\beta_0 + \beta_2 x_{rainSnow} + \beta_4 x_{hum} + \beta_5 x_{hum}^+ \beta_6 x_{hum}^3 + \beta_{23} x_{hr17}) - (\beta_0 + \beta_{14} x_{hr8})\). We can store this in a contrast matrix, and then multiply it with the coefficients of our model:
L <- matrix(0, 
            nrow = length(coef(m)),
            ncol = 1)
rownames(L) <- names(coef(m))
L["weathersitlight rain/snow",1] <- 1
L["humc",1] <- 0.357
L["I(humc^2)", 1] <- 0.357^2
L["I(humc^3)", 1] <- 0.357^3
L["hr17", 1] <- 1
L["hr8",1] <- -1
L
##                                  [,1]
## (Intercept)                0.00000000
## weathersitcloudy/misty     0.00000000
## weathersitlight rain/snow  1.00000000
## weathersitheavy rain/snow  0.00000000
## humc                       0.35700000
## I(humc^2)                  0.12744900
## I(humc^3)                  0.04549929
## hr1                        0.00000000
## hr2                        0.00000000
## hr3                        0.00000000
## hr4                        0.00000000
## hr5                        0.00000000
## hr6                        0.00000000
## hr7                        0.00000000
## hr8                       -1.00000000
## hr9                        0.00000000
## hr10                       0.00000000
## hr11                       0.00000000
## hr12                       0.00000000
## hr13                       0.00000000
## hr14                       0.00000000
## hr15                       0.00000000
## hr16                       0.00000000
## hr17                       1.00000000
## hr18                       0.00000000
## hr19                       0.00000000
## hr20                       0.00000000
## hr21                       0.00000000
## hr22                       0.00000000
## hr23                       0.00000000
beta <- matrix(coef(m), ncol=1)
exp(t(L) %*% beta) # equals our manual calculation.
##           [,1]
## [1,] 0.5595652
  • Using predict in R:
# set up data frames with relevant predictor variables' values.
dfA <- data.frame(weathersit = factor("light rain/snow"),
                    hr = factor(17),
                    humc = 0.357)
dfB <- data.frame(weathersit = factor("clear"),
                    hr = factor(8),
                    humc = 0)

# calculate estimated average number of bikers
yhatA <- predict(m, 
                newdata = dfA,
                type = "response")
yhatB <- predict(m, 
                newdata = dfB,
                type = "response")

yhatA / yhatB # also equal to above.
##         1 
## 0.5595652

Exercise: try to derive the change in average number of bikers between (a) humidity of 0.1 above average, clear weather (weathersit=1), at hour 10 and (b) humidity of 0.1 below average, cloudy weather (weathersit=2), at hour 20, using all three methods.

2.3 Statistical inference in GLMs

2.3.1 Wald test and likelihood ratio test

  • In our interpretation above we have focussed on deriving changes in the average number of bikers between groups of interest. However, we have not yet tested whether these changes are statistically significant.
  • In genomics applications, statistical inference in GLMs is often adopted to test for differential expression between conditions for each gene (e.g., is gene A differently expressed in healthy versus tumoral tissue?), which amounts to testing the null hypothesis of whether a (linear combination of) coefficient(s) equals zero.
  • In this course, we will mainly work with two types of statistical tests for GLMs:
    • Wald test: The Wald test may be viewed as being anaologous to the \(t\)-test we are using in linear models. The Wald test relies on the following asymptotic result \[\hat{\beta} | \beta \sim N (\beta, Var(\hat{\beta}))\]. The Wald test statistic for testing a single parameter \(\hat{\beta}\) \[W = \frac{\hat{\beta}}{\hat{SE}(\hat{\beta})} \sim N(0,1) | H_0\] or, equivalently, letting \(\mathbf{C}\) denote the \(1 \times p\) contrast matrix denoting the contrast for the single parameter \(\beta\) we would like to test, and \(\hat{\Sigma}_{\hat{\beta}}\) the \(p \times p\) variance-covariance matrix of the parameters, \[W = \mathbf{C}\hat{\beta} (\mathbf{C} \hat{\Sigma}_{\hat{\beta}} \mathbf{C}^T)^{-1} \hat{\beta}^T \mathbf{C}^T \sim \chi^2_1 | H_0.\] The null and alternative hypothesis can therefore in general be written as \[ H_0: \mathbf{C} \beta = 0\] \[ H_1: \mathbf{C} \beta \ne 0\] If \(c \ge 1\) contrasts are tested, then the test statistic \(W \sim \chi^2_c | H_0\), provided that the \(c\) contrasts are linearly independent (i.e., the contrast matrix is full rank).
    • 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 / tumoral), while the alternative model could be a GLM with just an intercept. Indeed, if the gene is similarly expressed between healthy and tumoral 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 \[L = 2 \left\{ \ell(\hat{\beta}_{full}) - \ell(\hat{\beta}_{alternative}) \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. If we again 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\] Finally, note that while, in this explanation, I have focussed on reducing a more complex model, but of course the LRT can also be adopted to check whether adding a covariate significantly improves the fit.
  • 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.

2.3.2 Wald test and likelihood ratio test in R

Let’s use a Wald test and a likelihood ratio test to test whether the average number of bikers differs between a working day or a weekend day, using a simple GLM with only that variable as a covariate. This amounts to testing

\[H_0: \beta_{workingday} = 0\] \[H_1: \beta_{workingday} \ne 0\]

mSimple <- glm(bikers ~ workingday, 
               family = "poisson", 
               data = Bikeshare)
summSimple <- summary(mSimple)
summSimple$coefficients["workingday",]
##     Estimate   Std. Error      z value     Pr(>|z|) 
## 2.352087e-02 1.937241e-03 1.214143e+01 6.370326e-34
# Wald test manually
W <- summSimple$coefficients["workingday", "Estimate"] / summSimple$coefficients["workingday", "Std. Error"]
pval <- 2*(1 - pnorm(W))
W
## [1] 12.14143
pval
## [1] 0
# Wald test through a contrast
C <- matrix(0, nrow=1, ncol=length(coef(mSimple)))
colnames(C) <- names(coef(mSimple))
C[, "workingday"] <- 1
C
##      (Intercept) workingday
## [1,]           0          1
beta <- matrix(coef(mSimple), ncol=1)
Sigma <- vcov(mSimple)

W2 <- C %*% beta %*% solve(C %*% Sigma %*% t(C)) %*% t(beta) %*% t(C)
W2
##          [,1]
## [1,] 147.4143
# note this being equal to
W^2
## [1] 147.4143
pval <- 1-pchisq(W2, df=1)
pval
##      [,1]
## [1,]    0
# finally, we can also read the Wald test result from the summary of the model
summSimple
## 
## Call:
## glm(formula = bikers ~ workingday, family = "poisson", data = Bikeshare)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -16.666  -11.361   -3.026    5.214   30.729  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) 4.952243   0.001608 3080.12   <2e-16 ***
## workingday  0.023521   0.001937   12.14   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 1052921  on 8644  degrees of freedom
## Residual deviance: 1052773  on 8643  degrees of freedom
## AIC: 1105815
## 
## Number of Fisher Scoring iterations: 5
mFull <- glm(bikers ~ workingday, 
               family = "poisson", 
               data = Bikeshare)

mReduced <- glm(bikers ~ 1, 
               family = "poisson", 
               data = Bikeshare)

# manual LRT
llFull <- logLik(mFull)
llReduced <- logLik(mReduced)

lrt <- as.numeric(2 * (llFull - llReduced))
lrt
## [1] 147.8481
pval <- 1 - pchisq(lrt, df=1)
pval
## [1] 0
# using anova function
anova(mReduced, mFull, test = "Chisq") # note test statistic is identical

2.3.3 Linearly dependent contrasts

Below, we show how one may be able to derive independent contrasts from their contrast matrix for statistical inference. We also show that attempting to use linearly dependent contrasts results in an issue, since the variance-covariance matrix of our contrasts will be singular and therefore non-invertible.

C <- matrix(0, nrow=3, ncol=length(coef(mSimple)))
colnames(C) <- names(coef(mSimple))
C[1, "(Intercept)"] <- 1
C[2, "workingday"] <- 1
C[3,c("(Intercept)", "workingday")] <- c(1,1)
C
##      (Intercept) workingday
## [1,]           1          0
## [2,]           0          1
## [3,]           1          1
# doesn't work because we have a singular matrix
W2 <- try(t(C %*% beta) %*% solve(C %*% Sigma %*% t(C)) %*% C %*% beta)
## Error in solve.default(C %*% Sigma %*% t(C)) : 
##   system is computationally singular: reciprocal condition number = 1.88084e-17
W2 # errrors
## [1] "Error in solve.default(C %*% Sigma %*% t(C)) : \n  system is computationally singular: reciprocal condition number = 1.88084e-17\n"
## attr(,"class")
## [1] "try-error"
## attr(,"condition")
## <simpleError in solve.default(C %*% Sigma %*% t(C)): system is computationally singular: reciprocal condition number = 1.88084e-17>
# identify the linearly independent contrasts
Ct <- t(C)
CtIndep <- Ct[,qr(Ct)$pivot[1:qr(Ct)$rank]]
CIndep <- t(CtIndep)

# try again
W2 <- t(CIndep %*% beta) %*% solve(CIndep %*% Sigma %*% t(CIndep)) %*% CIndep %*% beta
W2
##          [,1]
## [1,] 30686825

2.4 Model deviance, residuals and goodness-of-fit

  • In linear models, we often use residuals \(e_i = y_i - \hat{\mu}_i\) to check model assumptions (linearity, homoscedasticity). However, in a GLM setting, we know that the variance of our residuals will depend on the mean, i.e., \(Var(\epsilon_i) = f(\mu_i)\). Using ordinary residuals such as \(e_i\) therefore is no longer appropriate.
  • We have seen that the objective function that is used to fit a GLM is the log-likelihood of the data under the posited model. For example, the log likelihood of a Poisson GLM with response variable \(\mathbf{Y}\), with elements \(Y_i, i \in \{1, \ldots, n\}\) and model matrix \(\mathbf{X}\) is \[ \ell(\mathbf{Y}; \beta) = \log \prod_{i=1}^n \left( \frac{\exp (\mathbf{X}_i^T \beta)^{Y_i} \exp( - \exp(\mathbf{X}_i^T \beta))}{Y_i!} \right) = \sum_{i=1}^n \log \left( \frac{\exp(\mathbf{X}_i^T \beta)^{Y_i} \exp( - \exp(\mathbf{X}_i^T \beta))}{Y_i!} \right) \\ = \sum_{i=1}^n Y_i (\mathbf{X}_i^T \beta) - \exp(\mathbf{X}_i^T \beta) - \log Y_i!. \] The estimates \(\hat{\beta}\) are then found by maximizing \(\ell(\beta | \mathbf{Y}, \mathbf{X} )\) with respect to \(\beta\). This is analogous to maximizing a Gaussian likelihood in the linear model setting.
  • A goodness-of-fit measure used in the GLM setting is the residual deviance \(D\) (sometimes referred to simply as ‘deviance’), that is twice the difference in log-likelihood between a ‘saturated model’ and the current model. Here, a saturated model, is a model where we fit one parameter per data point and therefore fit the data perfectly, in other words \(\hat{\mu}_i = y_i\). This is, \[ D = 2 * \left\{ \ell(\mathbf{Y}; \beta | \hat{\mu}_i = y_i) - \ell(\mathbf{Y}; \beta | \hat{\mu}_i = \exp(\mathbf{X}_i^T \beta)) \right\}.\]
  • From the equation above it becomes clear that the residual deviance is actually a ratio in log-likelihoods and therefore a likelihood ratio test statistic!
  • A low residual deviance can thus be interpreted as a model that is fitting the data well, since your current model will be close in log-likelihood to the saturated model. The deviance is a very useful statistic that is also important in statistical inference and model selection, e.g., for testing if a smaller model fits significantly worse than a larger model.
  • Finally, a deviance residual \(D_i\) can then be defined as the square root of the contribution of the \(i\)th datum to the residual deviance \[ D_i = sign(Y_i - \exp(\mathbf{X}_i^T \beta)) \sqrt{ 2* \left\{ \ell(Y_i; \beta | \hat{\mu}_i = y_i) - \ell(Y_i; \beta | \hat{\mu}_i = \exp(\mathbf{X}_i^T \beta)) \right\} } \]

  • Another type of residuals commonly used in a GLM setting are Pearson residuals. A Pearson residual is defined as \[ e_i = \frac{y_i - E(y_i)}{\sqrt{Var(y_i)}},\] and we can see that it has the form of a regular residual such as used in liner models (numerator), but normalized according to the variance of the observed response (denominator), to correct for the mean-variance relationship.

  • Goodness-of-fit (GOF) analyses serve to assess how well the model actually fits the observed data. One may view the fitting of a GLM as replacing a set of observed data points \(\mathbf{y}\) by a set of fitted values \(\hat{\mathbf{\mu}}\) derived from a model. In general \(\hat{\mathbf{\mu}} \ne \mathbf{y}\) and the question arises as to how well \(\hat{\mathbf{\mu}}\) approximates \(\mathbf{y}\). This naturally raises the question of how much of a discrepancy we believe to be tolerable. Two important discrepancy measures are often used in a GLM setting.
  • Note that the residual deviance was a likelihood ratio test statistic between a saturated and our current model. This saturated model actually provides us with a baseline as to how well a model can fit the observed data (even if we know that the saturated model is uninformative for summarizing the data). This motivates a statistical test with
    \(H_0\): The current model provides a similar fit as the saturated model.
    \(H_1\): The current model fits significantly worse than a saturated model.
  • The residual deviance immediately tests this hypothesis using a likelihood ratio test and is therefore a useful goodness-of-fit measure.
  • Another measure of discrepancy is the generalized Pearson \(\chi^2\) statistic \[ X^2 = \sum_{i=1}^n \frac{(y_i - \hat{\mu}_i)^2}{Var(y_i)} = \sum_{i=1}^n e_i^2,\] with \(e_i\) the Pearson residual of observation \(i\).
  • Asymptotic theory shows that both the residual deviance \(D \sim \chi^2_{n-p} | H_0\) and \(X^2 \sim \chi^2_{n-p} | H_0\), with \(n\) the number of observations in our dataset (and, hence, the number of parameters fitted in our saturated model), and \(p\) the number of parameters fitted in our current model.

Exercise:

  • Verify the residual deviance that is reported in the summary of our model above.
  • Also check if you can recover the correct deviance and Pearson residuals by calculating them yourself. You can get the correct deviance residuals in R by resid(m, type="deviance") and resid(m, type="pearson").
  • Does your model fit significantly worse than a saturated model?

2.5 Overdispersion

  • Above we have always assumed that the Poisson distribution is valid for the dataset we have been using. However, we never checked for this.
  • As a matter of fact, it often happens that the variance=mean assumption is too stringent for count data. If the variance is larger than the mean, this is referred to as overdispersion. Though much less common, underdispersion happens when the variance is smaller than the mean.
  • We can use Pearson residuals to measure overdispersion using the following argument. The Poisson GLM implies that \[ Y_i | X, \hat{\beta} \sim Poi(\hat{\mu}_i),\] with \(\hat{\mu}_i = \exp (\mathbf{X}_i^T \hat{\beta})\). This implies \[ Var(Y_i | X, \hat{\beta}) = \hat{\mu}_i.\] Since the variance is unaffected by addition we may also write \[ Var(Y_i - \hat{\mu}_i | X, \hat{\beta}) = \hat{\mu}_i.\] Which is also equal to \[ Var\left(\frac{Y_i - \hat{\mu}_i}{\sqrt{Var(Y_i)}} | X, \hat{\beta} \right) = \frac{\hat{\mu}_i}{Var(Y_i)}.\] Since we know from the Poisson distribution that \(Var(Y_i) = \hat{\mu}_i\), we have that \[ Var \left(\frac{Y_i - \hat{\mu}_i}{\sqrt{\hat{\mu}_i}} | X, \hat{\beta} \right) = \frac{\hat{\mu}_i}{\hat{\mu}_i}.\] Note that the formulation within the variance at left-hand side of the equation is our definition of Pearson residuals \(E_i\). Thus, if the Poisson assumption holds, we can write \[ Var(E_i | X, \hat{\beta}) = 1,\] which is something we can empirically test using our fitted model. Indeed, if the variance of our Pearson residuals is much larger than \(1\), we are dealing with overdispersion. As a rough rule, I consider overdispersion to be present if this value is larger than \(\sim 1.3\), but this is arbitrary and may depend on the situation (and statistician).

  • Below, we apply this to the Bikeshare dataset. We will notice that the overdispersion is huge! The p-values and standard errors provided by the model can therefore not be trusted!
ePearson <- resid(m, type="pearson")
n <- nrow(Bikeshare)
p <- length(coef(m))
varPearson <- sum((ePearson^2)) / (n - p)
varPearson # HUGE!
## [1] 42.44815

2.5.1 Remedies to overdispersion

  • The presence of overdispersion tells us that the distributional assumption we have been making does not hold. Overdispersion is a common problem, and luckily we have a few available remedies, as in alternative distributions, although choosing between them may not always be trival.
    • The negative binomial (NB) distribution is a popular choice for modeling data that are overdispersed with respect to the Poisson distribution. The NB can be considered as a member of the exponential family and therefore fitted using standard GLM fitting engines. Just like the Poisson distribution, it is a distribution only appropriate for modeling count data. If \[ Y_i \sim NB(\mu_i, \phi), \] then \(E(Y_i) = \mu_i\) and \(Var(Y_i) = \mu_i + \phi \mu_i^2\), with \(\phi \ge 0\) the dispersion parameter. Since \(\phi \ge 0\) the variance of the negative binomial is always larger than that of the Poisson distribution, and in fact is now a quadratic (rather than linear) function of the mean. When \(\phi = 0\), the NB reduces to the Poisson distribution.
    • The quasi-Poisson model is an alternative choice derived using the quasi-likelihood framework developed by Wedderburn (1974). However, only the first two moments (mean and variance) are specified, and all other moments are left unspecified. In particular if model \(Y_i\) using a quasi-Poisson model, then \(E(Y_i) = \mu_i\) and \(Var(Y_i) = \phi \mu_i\), with \(\phi \ge 0\) the (quasi-)dispersion parameter. Again, since \(\phi \ge 0\) the variance of the quasi-Poisson is always larger than that of the Poisson distribution, however, the dispersion parameter here is on the linear scale, and so the mean-variance relationship is still linear as opposed to quadratic in the NB.

Below, we fit a negative binomial and quasi-Poisson model in R.

## negative binomial
library(MASS)
mNB <- glm.nb(bikers ~ weathersit + humc + I(humc^2) + I(humc^3) + hr,
         data = Bikeshare)
summary(mNB)
## 
## Call:
## glm.nb(formula = bikers ~ weathersit + humc + I(humc^2) + I(humc^3) + 
##     hr, data = Bikeshare, init.theta = 2.420957542, link = log)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -4.0170  -0.9246  -0.1729   0.5008   5.4746  
## 
## Coefficients:
##                           Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                3.86978    0.03560 108.701  < 2e-16 ***
## weathersitcloudy/misty    -0.15215    0.01753  -8.681  < 2e-16 ***
## weathersitlight rain/snow -0.62708    0.02956 -21.212  < 2e-16 ***
## weathersitheavy rain/snow -1.95785    0.66526  -2.943  0.00325 ** 
## humc                       0.22567    0.06968   3.239  0.00120 ** 
## I(humc^2)                 -1.95867    0.20007  -9.790  < 2e-16 ***
## I(humc^3)                 -0.75361    0.59317  -1.270  0.20391    
## hr1                       -0.47370    0.04968  -9.535  < 2e-16 ***
## hr2                       -0.79592    0.05041 -15.789  < 2e-16 ***
## hr3                       -1.42636    0.05216 -27.346  < 2e-16 ***
## hr4                       -2.04660    0.05478 -37.360  < 2e-16 ***
## hr5                       -1.05264    0.05085 -20.701  < 2e-16 ***
## hr6                        0.32952    0.04909   6.712 1.92e-11 ***
## hr7                        1.33266    0.04868  27.375  < 2e-16 ***
## hr8                        1.86123    0.04862  38.283  < 2e-16 ***
## hr9                        1.38427    0.04877  28.382  < 2e-16 ***
## hr10                       1.14603    0.04900  23.390  < 2e-16 ***
## hr11                       1.32835    0.04913  27.035  < 2e-16 ***
## hr12                       1.54916    0.04934  31.401  < 2e-16 ***
## hr13                       1.56716    0.04951  31.655  < 2e-16 ***
## hr14                       1.53797    0.04960  31.010  < 2e-16 ***
## hr15                       1.57197    0.04961  31.689  < 2e-16 ***
## hr16                       1.78550    0.04947  36.092  < 2e-16 ***
## hr17                       2.19094    0.04924  44.491  < 2e-16 ***
## hr18                       2.08597    0.04911  42.473  < 2e-16 ***
## hr19                       1.75147    0.04890  35.816  < 2e-16 ***
## hr20                       1.41815    0.04883  29.044  < 2e-16 ***
## hr21                       1.14329    0.04875  23.450  < 2e-16 ***
## hr22                       0.89344    0.04879  18.313  < 2e-16 ***
## hr23                       0.49606    0.04891  10.142  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(2.421) family taken to be 1)
## 
##     Null deviance: 26632.4  on 8644  degrees of freedom
## Residual deviance:  9314.9  on 8615  degrees of freedom
## AIC: 93224
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  2.4210 
##           Std. Err.:  0.0374 
## 
##  2 x log-likelihood:  -93161.6150
mean(resid(mNB, type = "pearson")^2) # no more overdispersion!
## [1] 0.9833573
## quasi-Poisson
mQP <- glm(bikers ~ weathersit + humc + I(humc^2) + I(humc^3) + hr,
         data = Bikeshare,
         family="quasipoisson")
# note the dispersion parameter being estimated is equal to our overdispersion diagnostic measure. 
# Indeed, this is the way the dispersion parameter is estimated for the QP!!
summary(mQP) 
## 
## Call:
## glm(formula = bikers ~ weathersit + humc + I(humc^2) + I(humc^3) + 
##     hr, family = "quasipoisson", data = Bikeshare)
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -23.3408   -4.6201   -0.9922    3.4605   27.4153  
## 
## Coefficients:
##                           Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                3.89365    0.05266  73.936  < 2e-16 ***
## weathersitcloudy/misty    -0.14662    0.01483  -9.885  < 2e-16 ***
## weathersitlight rain/snow -0.55615    0.02987 -18.617  < 2e-16 ***
## weathersitheavy rain/snow -1.85519    1.08636  -1.708  0.08773 .  
## humc                       0.09175    0.06015   1.525  0.12723    
## I(humc^2)                 -2.23392    0.19169 -11.654  < 2e-16 ***
## I(humc^3)                 -1.82307    0.59568  -3.060  0.00222 ** 
## hr1                       -0.47647    0.08469  -5.626 1.90e-08 ***
## hr2                       -0.80696    0.09542  -8.457  < 2e-16 ***
## hr3                       -1.43365    0.12276 -11.679  < 2e-16 ***
## hr4                       -2.05871    0.16155 -12.744  < 2e-16 ***
## hr5                       -1.06169    0.10473 -10.138  < 2e-16 ***
## hr6                        0.31569    0.06911   4.568 4.99e-06 ***
## hr7                        1.31786    0.05898  22.346  < 2e-16 ***
## hr8                        1.83003    0.05638  32.459  < 2e-16 ***
## hr9                        1.35214    0.05878  23.003  < 2e-16 ***
## hr10                       1.12950    0.06040  18.699  < 2e-16 ***
## hr11                       1.30855    0.05930  22.066  < 2e-16 ***
## hr12                       1.52223    0.05829  26.113  < 2e-16 ***
## hr13                       1.53683    0.05837  26.329  < 2e-16 ***
## hr14                       1.49951    0.05863  25.576  < 2e-16 ***
## hr15                       1.53504    0.05846  26.256  < 2e-16 ***
## hr16                       1.74513    0.05733  30.439  < 2e-16 ***
## hr17                       2.14049    0.05580  38.360  < 2e-16 ***
## hr18                       2.03774    0.05595  36.419  < 2e-16 ***
## hr19                       1.71154    0.05699  30.032  < 2e-16 ***
## hr20                       1.39390    0.05848  23.834  < 2e-16 ***
## hr21                       1.13254    0.06004  18.863  < 2e-16 ***
## hr22                       0.88253    0.06214  14.203  < 2e-16 ***
## hr23                       0.48135    0.06650   7.238 4.94e-13 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for quasipoisson family taken to be 42.44818)
## 
##     Null deviance: 1052921  on 8644  degrees of freedom
## Residual deviance:  375265  on 8615  degrees of freedom
## AIC: NA
## 
## Number of Fisher Scoring iterations: 5
  • ver Hoef et al. (2007) provide a useful diagnostic to empirically check whether your data is more amenable to a quasi-Poisson versus a negative binomial distribution.

3 A final note

In this lecture, we have introduced GLMs to model data that can be assumed to follow a distribution belonging to the exponential family. We focussed on estimation, interpretation, statistical inference and some model goodness-of-fit diagnostics. We did not consider important topics like model selection, or even whether a GLM is appropriate for your dataset! These are other important topics which, unfortunately, we do not have the bandwidth for to include in this course. Therefore, a final note through an XKCD comic, after finishing this long chapter!

4 References

  • Marioni et al. (2008) describe the distribution of gene expression counts across technical replicates, and in addition discuss lane effects in RNA-seq, as well as a comparison between RNA-seq and array-based platforms.
  • Wedderburn (1974) introduced the (extended) quasi-likelihood framework.
  • The count data chapter of Modern Statistics for Modern Biology by Wolfgang Huber and Susan Holmes handles similar topics also in the context of RNA-seq: https://www.huber.embl.de/msmb/Chap-CountData.html
LS0tCnRpdGxlOiAnU2VxdWVuY2luZzogV29ya2luZyB3aXRoIGNvdW50IGRhdGEnCmF1dGhvcjogIktvZW4gVmFuIGRlbiBCZXJnZSIKZGF0ZTogIkxhc3QgY29tcGlsZWQgb24gYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGxhdGV4X2VuZ2luZTogeGVsYXRleAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2RlcHRoOiA0Ci0tLQoKYGBge3IgZnVuY3Rpb25zLCBpbmNsdWRlPUZBTFNFfQojIEEgZnVuY3Rpb24gZm9yIGNhcHRpb25pbmcgYW5kIHJlZmVyZW5jaW5nIGltYWdlcwpmaWcgPC0gbG9jYWwoewogICAgaSA8LSAwCiAgICByZWYgPC0gbGlzdCgpCiAgICBsaXN0KAogICAgICAgIGNhcD1mdW5jdGlvbihyZWZOYW1lLCB0ZXh0KSB7CiAgICAgICAgICAgIGkgPDwtIGkgKyAxCiAgICAgICAgICAgIHJlZltbcmVmTmFtZV1dIDw8LSBpCiAgICAgICAgICAgIHBhc3RlKCJGaWd1cmUgIiwgaSwgIjogIiwgdGV4dCwgc2VwPSIiKQogICAgICAgIH0sCiAgICAgICAgcmVmPWZ1bmN0aW9uKHJlZk5hbWUpIHsKICAgICAgICAgICAgcmVmW1tyZWZOYW1lXV0KICAgICAgICB9KQp9KQpgYGAgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoa25pdHIpCiAgbGlicmFyeShybWFya2Rvd24pCiAgbGlicmFyeShnZ3Bsb3QyKQp9KQpgYGAKCkluIHRoaXMgbGVjdHVyZSB3ZSB3aWxsIGludHJvZHVjZSB0aGUgbWFpbiBwcmluY2lwbGVzIG9mIHdvcmtpbmcgd2l0aCBjb3VudCBkYXRhLCBhbmQgaG93IHRvIG1vZGVsIHRoZXNlIHVzaW5nIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMgKEdMTXMpLiBXZSBmb2N1cyBvbiBpbnRyb2R1Y2luZyB0aGUgY29uY2VwdCBvZiBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWxzLCBhbmQgaG93IHRvIGludGVycHJldCBpdHMgcmVzdWx0cy4gV2UgdG91Y2ggYnJpZWZseSB1cG9uIHN0YXRpc3RpY2FsIGluZmVyZW5jZSwgcHJvdmlkaW5nIHRoZSBtYWluIHJlc3VsdHMgcmF0aGVyIHRoYW4gdGhlIHRoZW9yeSBiZWhpbmQgaXQsIHN1Y2ggdGhhdCB0aGV5IGNhbiBiZSBhcHBsaWVkIHRvIGdlbm9taWNzIGRhdGEgYW5hbHlzaXMuCgojIFRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbgoKIC0gVGhlIFBvaXNzb24gZGlzdHJpYnV0aW9uIGlzIGEgdHlwaWNhbCBjb3VudCBkaXN0cmlidXRpb24gdGhhdCBpcyBnZW5lcmFsbHkgcG9wdWxhciBhbmQgZmFpcmx5IGVhc3kgdG8gd29yayB3aXRoLiBJdCBpcyBkZWZpbmVkIGJ5IGEgc2luZ2xlIHBhcmFtZXRlcjogaXRzIG1lYW4gJFxtdSQuIEZvciBhIFBvaXNzb24gZGlzdHJpYnV0ZWQgcmFuZG9tIHZhcmlhYmxlICRZX2kkIHdpdGggb2JzZXJ2YXRpb25zICRpIFxpbiBcezEsIFxsZG90cywgblx9JCwgaXRzIHZhcmlhbmNlIGlzIGVxdWFsIHRvIGl0cyBtZWFuLiBUaGF0IGlzLCBpZiAkWV9pIFxzaW0gUG9pKFxtdSkkLCB0aGVuICRFKFlfaSkgPSBWYXIoWV9pKSA9IFxtdSQuCiAtIFRoaXMgaW1tZWRpYXRlbHkgc2hvd3MgYW4gaW1wb3J0YW50IGZlYXR1cmUgb2YgY291bnQgZGF0YTogdGhlICoqbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAqKi4gSW5kZWVkLCBpbiBjb3VudCBkYXRhLCB0aGUgdmFyaWFuY2Ugd2lsbCBhbHdheXMgYmUgYSBmdW5jdGlvbiBvZiB0aGUgbWVhbi4KIC0gVGhpcyBpcyBxdWl0ZSBpbnR1aXRpdmUuIENvbnNpZGVyIHRoZSBmb2xsb3dpbmcgZXhhbXBsZS4gWW91IGhhdmUgdHdvIGJpcmQgY2FnZXMsIHdoZXJlIGluIG9uZSBiaXJkIGNhZ2UgdGhlcmUgYXJlICQxMCQgYmlyZHMsIHdoaWxlIGluIHRoZSBvdGhlciB0aGVyZSBhcmUgJDEwMCQgYmlyZHMuIFlvdSBsZXQgYSBzYW1wbGUgb2YgcGVvcGxlIGNvdW50IHRoZSBudW1iZXIgb2YgYmlyZHMgaW4gZWl0aGVyIG9uZSBvZiB0aGUgY2FnZXMuIEl0IHNlZW1zIHVubGlrZWx5IHRoYXQgYSBwZXJzb24gaW4gZnJvbnQgb2YgdGhlIDEwLWJpcmQgY2FnZSB3b3VsZCBjb21lIHVwIHdpdGggYW4gZXN0aW1hdGUgb2YgJDUkLCB3aGlsZSBpdCBzZWVtcyBxdWl0ZSBsaWtlbHkgdGhhdCBzb21lb25lIGluIGZyb250IG9mIHRoZSAxMDAtYmlyZCBjYWdlIHdvdWxkIGNvbWUgdXAgd2l0aCBhbiBlc3RpbWF0ZSBvZiAkOTUkLiBFdmVuIHRob3VnaCB0aGUgZGlmZmVyZW5jZSBmcm9tIHRoZSB0cnVlIHZhbHVlIGlzIHRoZSBzYW1lLCB0aGUgZXhhY3QgdmFsdWUgaGFzIGFuIGltcGFjdCBvbiB0aGUgcGxhdXNpYmxlIGRldmlhdGlvbiBhcm91bmQgaXQuCiAKYGBge3J9CnNldC5zZWVkKDExKQp5MSA8LSBycG9pcyhuPTUwMCwgbGFtYmRhPTEwKQp5MiA8LSBycG9pcyhuPTUwMCwgbGFtYmRhPTEwMCkKCnBhcihtZnJvdyA9IGMoMSwyKSkKaGlzdCh5MSwgbWFpbj0iUG9pc3NvbigxMCkiLCBicmVha3M9NDApCmhpc3QoeTIsIG1haW49IlBvaXNzb24oMTAwKSIsIGJyZWFrcz00MCkKYGBgCgojIyBUaGUgUG9pc3NvbiBkaXN0cmlidXRpb24gaW4gUk5BLXNlcQoKIC0gSW4gUk5BLXNlcSwgdGVjaG5pY2FsIHJlcGxpY2F0ZXMgcmVwcmVzZW50IGRpZmZlcmVudCBhbGlxdW90cyBvZiB0aGUgc2FtZSBzYW1wbGUgYmVpbmcgc2VxdWVuY2VkIHJlcGVhdGVkbHkuIFRoZSB1bmRlcmx5aW5nIHRydWUgZXhwcmVzc2lvbiBvZiBhIGdlbmUgY2FuIGhlbmNlIHNhZmVseSBiZSBhc3N1bWVkIHRvIGJlIGVxdWFsIGFjcm9zcyB0aGVzZSB0ZWNobmljYWwgcmVwbGljYXRlcy4KIC0gW01hcmlvbmkgKmV0IGFsLiogKDIwMDgpXShodHRwczovL2dlbm9tZS5jc2hscC5vcmcvY29udGVudC8xOC85LzE1MDkpIGhhdmUgc2hvd24gdGhhdCwgZm9yIG1vc3QgZ2VuZXMsIHRoZSBkaXN0cmlidXRpb24gb2Ygb2JzZXJ2ZWQgZ2VuZSBleHByZXNzaW9uIGNvdW50cyBhY3Jvc3MgdGVjaG5pY2FsIHJlcGxpY2F0ZXMgZm9sbG93IGEgUG9pc3NvbiBkaXN0cmlidXRpb24uIEEgc21hbGwgcHJvcG9ydGlvbiBvZiBnZW5lcyAoJFxzaW0gMC41XCUkKSBkbyBub3QgZm9sbG93IHRoaXMgUG9pc3NvbiBtb2RlbCwgaG93ZXZlciwgYW5kIGFjdHVhbGx5IHNob3cgZXZpZGVuY2UgZm9yIConZXh0cmEtUG9pc3NvbiB2YXJpYXRpb24nKi4KIApgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmNhcD1wYXN0ZSgiRmlndXJlOiBUZWNobmljYWwgcmVwbGljYXRpb24gaW4gUk5BLXNlcS4gRmlndXJlcyBmcm9tIE1hcmlvbmkgZXQgYWwuICgyMDA4KS4iKX0KIyBBbGwgZGVmYXVsdHMKaW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9tYXJpb25pRmlnc19jcm9wcGVkLnBuZyIpCmBgYAoKIyMgUmVsYXRpdmUgdW5jZXJ0YWludHkgZm9yIFBvaXNzb24gZGlzdHJpYnV0ZWQgcmFuZG9tIHZhcmlhYmxlcwoKVGFrZSBhIG1pbnV0ZSB0byBjb25zaWRlciB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uOgoKIC0gU3VwcG9zZSB0aGF0IHdlIGhhdmUgYSBzb2xpZCB0dW1vciBzYW1wbGUgZnJvbSBhIGNhbmNlciBwYXRpZW50LCBhcyB3ZWxsIGFzIGEgc2FtcGxlIG9mIHN1cnJvdW5kaW5nIGhlYWx0aHkgdGlzc3VlLiBGb3IgZWFjaCBzYW1wbGUsIHdlIGhhdmUgdGhyZWUgdGVjaG5pY2FsIHJlcGxpY2F0ZXMgYXQgb3VyIGRpc3Bvc2FsLiBMZXQgJFlfe2dydH0kIGRlbm90ZSB0aGUgb2JzZXJ2ZWQgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBvZiBnZW5lICRnJCBpbiByZXBsaWNhdGUgJHIgXGluIFx7MSwyLDNcfSQgZnJvbSB0aXNzdWUgJHQgXGluIFx7MCwxXH0kLCB3aGVyZSAkdD0wJCBkZW5vdGVzIGhlYWx0aHkgdGlzc3VlIGFuZCAkdD0xJCBkZW5vdGVzIHR1bW9yYWwgdGlzc3VlLiAKIC0gV2UgdGhlbiBrbm93IHRoYXQgdGhlIHJhbmRvbSB2YXJpYWJsZXMgJFlfe2dyMH0kIGFuZCAkWV97Z3IxfSQgZm9sbG93IGEgUG9pc3NvbiBkaXN0cmlidXRpb24sIGFuZCB3ZSB3b3VsZCBlc3RpbWF0ZSBpdHMgbWVhbiBhcyAkXGJhcntZfV97ZzB9ID0gXGZyYWN7MX17M30gXHN1bV97cj0xfV4zIFlfe2dyMH0kIGFuZCAkXGJhcntZfV97ZzF9ID0gXGZyYWN7MX17M30gXHN1bV97cj0xfV4zIFlfe2dyMX0kLCByZXNwZWN0aXZlbHkuIAogLSBTaW1pbGFyLCBmb3IgYW5vdGhlciBnZW5lICRrJCwgd2Ugb2JzZXJ2ZSAkWV97a3J0fSQsIGFuZCBlc3RpbWF0ZSAkXGJhcntZfV97azB9JCBhbmQgJFxiYXJ7WX1fe2sxfSQgY29ycmVzcG9uZGluZ2x5LgogLSBOb3cgc3VwcG9zZSB0aGF0ICRcYmV0YV97a30gPSBcYmFye1l9X3trMX0gLyBcYmFye1l9X3trMH0gPSA1JCwgYnV0IGFsc28gJFxiZXRhX2cgPSBcYmFye1l9X3tnMX0gLyBcYmFye1l9X3tnMH0gPSA1JCwgaS5lLiwgdGhlIHR3byBnZW5lcyBoYXZlIHRoZSBzYW1lIGF2ZXJhZ2UgZXhwcmVzc2lvbiByYXRpbyAoYWxzbyBvZnRlbiBjYWxsZWQgYSBmb2xkLWNoYW5nZSkgYWNyb3NzIHNhbXBsZXMuIEhvd2V2ZXIsIHRoZXkgYXJlIGRpZmZlcmVudGx5IGV4cHJlc3NlZCBhcyAkXGJhcntZfV97azF9ID0gMTAwJCwgYW5kICRcYmFye1l9X3tnMX0gPSAxMCQgKG1ha2luZyAkXGJhcntZfV97azB9ID0gMjAkLCBhbmQgJFxiYXJ7WX1fe2cwfSA9IDIkKS4KIC0gRm9yIHdoaWNoIG9mIHRoZSB0d28gZ2VuZXMgaXMgdGhlIHVuY2VydGFpbnR5IG9uIHRoZSBleHByZXNzaW9uIHJhdGlvIHRoZSBoaWdoZXN0PyBJbiBvdGhlciB3b3JkcywgZG8gd2UgdHJ1c3QgJFxiZXRhX2skIG1vcmUgb3IgZG8gd2UgdHJ1c3QgJFxiZXRhX2ckIG1vcmU/CiAKIC0tLQoKTGV0J3MgYXBwcm94aW1hdGUgdGhlIHVuY2VydGFpbnR5IGluICRcYmV0YV9nJCBhbmQgJFxiZXRhX2skIHVzaW5nIHNpbXVsYXRpb246CiAKYGBge3J9Ck4gPC0gMWUzCmJldGFfZyA8LSBiZXRhX2sgPC0gdmVjdG9yKGxlbmd0aD1OKQpmb3IoaWkgaW4gMTpOKXsKICB5Z3IxIDwtIHJwb2lzKG49MywgbGFtYmRhPTEwKQogIHlncjAgPC0gcnBvaXMobj0zLCBsYW1iZGE9MikKICB5a3IxIDwtIHJwb2lzKG49MywgbGFtYmRhPTEwMCkKICB5a3IwIDwtIHJwb2lzKG49MywgbGFtYmRhPTIwKQogIGJldGFfZ1tpaV0gPC0gbWVhbih5Z3IxKSAvIG1lYW4oeWdyMCkKICBiZXRhX2tbaWldIDwtIG1lYW4oeWtyMSkgLyBtZWFuKHlrcjApCn0KCnBhcihtZnJvdz1jKDEsMiksIG1hcj1jKDQsMiwzLDEpKQpoaXN0KGJldGFfZywgYnJlYWtzPXNlcSgwLDUwLGJ5PTEpLCB4bGltPWMoMCw1MCkpCmhpc3QoYmV0YV9rLCBicmVha3M9c2VxKDAsNTAsYnk9MSksIHhsaW09YygwLDUwKSkKYGBgCiAKIC0tLSAKIAogV2UgY2xlYXJseSBzZWUgdGhhdCB0aGUgdW5jZXJ0YWludHkgb24gJFxiZXRhX2skIGlzIG11Y2ggbG93ZXIgdGhhbiBvbiAkXGJldGFfZyQuIEV2ZW4gdGhvdWdoIHRoZSB2YXJpYW5jZSBvbiB0aGUgY291bnRzIG9mIGdlbmUgJGskIGlzIGhpZ2hlciwgc2luY2UgaXRzIG1lYW4gaXMgaGlnaGVyIGFuZCBpdCBpcyBkaXN0cmlidXRlZCBhcyBhIFBvaXNzb24gdmFyaWFibGUuIEhvdyBkbyB3ZSBleHBsYWluIHRoaXM/CiAKIC0gV2UgbWF5IGV4cGxhaW4gdGhpcyBieSBjb25zaWRlcmluZyB0aGUgcmVsYXRpdmUgdW5jZXJ0YWludHkgb24gdGhlIG1lYW4uIFJlbGF0aXZlIHVuY2VydGFpbnR5IG1heSBiZSBkZWZpbmVkIGFzIHRoZSBjb2VmZmljaWVudCBvZiB2YXJpYXRpb24gJENWID0gXGZyYWN7XHNpZ21hfXtcbXV9JCAodGhpcyBpcywgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBkaXZpZGVkIGJ5IHRoZSBtZWFuKS4gSW5kZWVkLCB0aGUgQ1YgZGVzY3JpYmVzIHRoZSByZWxhdGl2ZSBkZXZpYXRpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbiByZWxhdGl2ZSB0byBpdHMgbWVhbiwgd2hlcmUgYSBsb3cgQ1YgaW5kaWNhdGVzIGxvdyBkaXNwZXJzaW9uIHdpdGggcmVzcGVjdCB0byB0aGUgbWVhbi4KIC0gQ2FsY3VsYXRpbmcgdGhlIENWIHNob3dzIHRoYXQgKip0aGUgcmVsYXRpdmUgdW5jZXJ0YWludHkgZm9yIGdlbmUgJGskIHRoYW4gZm9yIGdlbmUgJGckLCBldmVuIHRob3VnaCB0aGUgdmFyaWFuY2Ugb24gdGhlIHJhdyBjb3VudHMgaXMgaGlnaGVyIGZvciBnZW5lICRrJCB0aGFuIGZvciBnZW5lICRnJCoqLiAKIC0gVGhpcyBsb3dlciByZWxhdGl2ZSB1bmNlcnRhaW50eSBvbiB0aGUgbWVhbiB0aGVuIHByb3BhZ2F0ZXMgZnVydGhlciB0byBhIGxvd2VyIHVuY2VydGFpbnR5IG9uIHRoZSBmb2xkLWNoYW5nZS4gVGhpcyBiYXNpYyByZXN1bHQgd2lsbCBiZSBlc3NlbnRpYWwgZm9yIHVuZGVyc3RhbmRpbmcgdGhlIHJlc3VsdHMgb2YgYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyEKIApgYGB7cn0Kc3FydCgxMDApLzEwMCAjQ1YgZm9yIGdlbmUgawoKc3FydCgxMCkvMTAgI0NWIGZvciBnZW5lIGcKYGBgCiAKIyBNb2RlbGluZyBjb3VudCBkYXRhOiBHZW5lcmFsaXplZCBsaW5lYXIgbW9kZWxzCgpKdXN0IGxpa2Ugd2UgaGF2ZSBtb2RlbGVkIHByb3RlaW4gYWJ1bmRhbmNlcyBpbiB0aGUgcHJvdGVvbWljcyBtb2R1bGUgb2YgdGhpcyBjb3Vyc2UgaW4gb3JkZXIgdG8gYXNzZXNzIGRpZmZlcmVudGlhbCBwcm90ZWluIGFidW5kYW5jZSwgd2UgY2FuIG1vZGVsIGdlbmUgZXhwcmVzc2lvbiBjb3VudHMgdG8gaWRlbnRpZnkgZ2VuZXMgd2l0aCBkaWZmZXJlbmNlcyBpbiBhdmVyYWdlIGV4cHJlc3Npb24gYmV0d2VlbiBncm91cHMgb2Ygc2FtcGxlcy4KCiMjIFdoeSB3ZSBjYW4oJ3QpIHVzZSBsaW5lYXIgbW9kZWxzIHRvIG1vZGVsIGNvdW50IGRhdGEKCiAtIElmIHdlJ3JlIHVzaW5nIGEgbGluZWFyIG1vZGVsIHRvIG1vZGVsIGEgcmVzcG9uc2UgJFlfaSQsIHdpdGggJGkgXGluIFx7MSwgXGxkb3RzLCBuXH0kIGluIGZ1bmN0aW9uIG9mIGEgc2luZ2xlIGNvdmFyaWF0ZSAkWF9pJCwgdGhlIGxpbmVhciBtb2RlbCBjYW4gYmUgZGVmaW5lZCBhcyBmb2xsb3dzOgoKXFsKXGxlZnRcewpcYmVnaW57YXJyYXl9e2NjY30KWV9pICYgPSAmIFxiZXRhXzAgKyBcYmV0YV8xIFhfaSArIFxlcHNpbG9uX2kgXFwKWV9pIHwgWF9pICYgXHNpbSAmIE4oXGJldGFfMCArIFxiZXRhXzEgWF9pLCBcc2lnbWFeMiBcbWF0aGJme0l9KS4KXGVuZHthcnJheX0KXHJpZ2h0LgpcXQoKIC0gT3IsIGVxdWl2YWxlbnRseSwgd2UndmUgc2VlbiB3ZSBjYW4gd3JpdGUgaXQgaW4gbWF0cml4IGZvcm0gYXMKICAkJAogIFxsZWZ0XHsKICBcYmVnaW57YXJyYXl9e2NjY30KICBZX2kgJiA9ICYgXG1hdGhiZntYfV5UX2kgXGJldGEgKyBcZXBzaWxvbl9pIFxcCiAgWV9pIHwgXG1hdGhiZntYfV9pICYgXHNpbSAmIE4oXG1hdGhiZntYfV5UX2kgXGJldGEsIFxzaWdtYV4yIFxtYXRoYmZ7SX0pLAogIFxlbmR7YXJyYXl9CiAgXHJpZ2h0LgogICQkCndoZXJlICRcbWF0aGJme1h9JCBub3cgcmVwcmVzZW50cyBvdXIgJG4gXHRpbWVzIHAkIGRlc2lnbiBtYXRyaXgsIHdpdGggcm93ICRpJCBjb3JyZXNwb25kaW5nIHRvIG9ic2VydmF0aW9uICRpJC4KCiAtLS0KCiAtIFRoZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCBvZiAkXG1hdGhiZntZfSQgaXMgYXNzdW1lZCBhIGRpYWdvbmFsIG1hdHJpeCB3aXRoICRcc2lnbWFeMiQgb24gdGhlIGRpYWdvbmFsIGVsZW1lbnRzIGFuZCB6ZXJvIGV2ZXJ5d2hlcmUgZWxzZS4gVGhpcyBtZWFucyB0aGF0IHRoZSBkYXRhIHBvaW50cyBhcmUgdW5jb3JyZWxhdGVkLCBhbmQgdGhhdCBldmVyeSBvYnNlcnZhdGlvbiBoYXMgdGhlIHNhbWUgdmFyaWFuY2UgJFxzaWdtYV4yJCwgYWxzbyByZWZlcnJlZCB0byBhcyBob21vc2NlZGFzdGljaXR5LiAKIC0gVGhlIGxhdHRlciBkb2Vzbid0IGhvbGQgZm9yIGNvdW50IGRhdGEsIGR1ZSB0byB0aGUgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAuIFRoaXMgbWFrZXMgbGluZWFyIG1vZGVscywgaW4gaXRzIGJhc2ljIGZvcm0sIHVuc3VpdGFibGUgdG8gbW9kZWwgY291bnQgZGF0YS4KIC0gSW4gYWRkaXRpb24sIGNvdW50IGRhdGEgYXJlIG5vbi1uZWdhdGl2ZSwgd2hpbGUgdGhlcmUgYXJlIG5vIHN1Y2ggY29uc3RyYWludHMgaW4gdGhlIHN0YW5kYXJkIGxpbmVhciBtb2RlbCB0byBtYWtlIHN1cmUgdGhhdCBvdXIgZXN0aW1hdGVzIHdpbGwgYmUgbm9uLW5lZ2F0aXZlLiBJbmRlZWQsICRcaGF0e1l9X2kgPSBcbWF0aGJme1h9XlRfaSBcaGF0e1xiZXRhfSBcaW4gXSAtXGluZnR5LCBcaW5mdHlbJC4KCiMjIEdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMKCiAtIEFzIHRoZSBuYW1lIHN1Z2dlc3RzLCBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWxzIChHTE1zKSBleHRlbmQgbGluZWFyIG1vZGVscy4gSW4gR0xNcywgd2UgZXh0ZW5kIHR3byB0aGluZ3Mgd2l0aCByZXNwZWN0IHRvIHRoZSBsaW5lYXIgbW9kZWw6CiAgICAtIFRoZSAqKmNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgJFlfaSB8IFhfaSQqKiBjYW4gYmUgYXNzdW1lZCB0byBmb2xsb3cgYW55IGRpc3RyaWJ1dGlvbiB0aGF0IGJlbG9uZ3MgdG8gdGhlICoqZXhwb25lbnRpYWwgZmFtaWx5Kiogb2YgZGlzdHJpYnV0aW9ucywgd2hpY2ggaW5jbHVkZXMgdGhlIEdhdXNzaWFuIGJ1dCBhbHNvIG90aGVyIGNvbW1vbmx5IGtub3duIGRpc3RyaWJ1dGlvbnMsIHN1Y2ggYXMgdGhlIEJpbm9taWFsLCBHYW1tYSBhbmQgUG9pc3NvbiBkaXN0cmlidXRpb24uCiAgICAtIFRoZSBsaW5lYXIgbW9kZWwgYXNzdW1lZCBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiAkWV9pJCBhbmQgJFhfaSQsIHNpbmNlIHdlIGFzc3VtZWQgdGhhdCAkRShZX2kgfCBYX2kpID0gXG1hdGhiZntYfV5UX2kgXGJldGEkLiBJbiBHTE1zLCB3ZSB3aWxsIGFsbG93IGEgKipsaW5rIGZ1bmN0aW9uKiogJGcoKSQgdGhhdCBsaW5rcyB0aGUgY29uZGl0aW9uYWwgbWVhbiB0byB0aGUgY292YXJpYXRlcy4gSGVuY2UsIGluIEdMTXMgd2UgaGF2ZSB0aGF0ICRnKEUoWV9pIHwgWF9pKSkgPSBcbWF0aGJme1h9XlRfaSBcYmV0YSQuIE5vdGUgdGhhdCBlYWNoIGZhbWlseSBoYXMgZ290IGEgY2Fub25pY2FsIGxpbmsgZnVuY3Rpb24sIHdoaWNoIGlzIHRoZSBpZGVudGl0eSBsaW5rIGZ1bmN0aW9uICRnKFxtdSkgPSBcbXUkIGZvciBHYXVzc2lhbiwgdGhlIGxvZyBsaW5rIGZ1bmN0aW9uICRnKFxtdSkgPSBcbG9nIFxtdSQgZm9yIFBvaXNzb24sIG9yIHRoZSBsb2dpdCBsaW5rIGZ1bmN0aW9uICRnKFxtdSkgPSBcbG9nKFxmcmFje1xtdX17MS1cbXV9KSQgZm9yIEJpbm9taWFsLgogICAgCiMjIyBBIFBvaXNzb24gR0xNCgogLSBXZSBjYW4gZGVmaW5lIGEgUG9pc3NvbiBHTE0gYXMgZm9sbG93cwogICQkCiAgXGxlZnRcewogIFxiZWdpbnthcnJheX17Y2NjfQogIFlfaSAmIFxzaW0gJiBQb2koXG11X2kpIFxcCiAgXGxvZyBcbXVfaSAmID0gJiBcZXRhX2kgXFwKICBcZXRhX2kgJiA9ICYgXG1hdGhiZntYfV5UX2kgXGJldGEgXFwKICBcZW5ke2FycmF5fQogIFxyaWdodC4KICAkJAp3aGVyZSAkWV9pJCBpcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUsIHdpdGggbWVhbiAkXG11X2kkLCAkXGV0YV9pJCBpcyB0aGUgbGluZWFyIHByZWRpY3RvciwgJFxtYXRoYmZ7WH0kIGlzIHRoZSAkbiBcdGltZXMgcCQgbW9kZWwgbWF0cml4IGFuZCAkXGJldGEkIGlzIHRoZSAkcCBcdGltZXMgMSQgbWF0cml4IG9mIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLCB3aGVyZSAkbiQgaXMgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyBhbmQgJHAkIHRoZSB0b3RhbCBudW1iZXIgb2YgcGFyYW1ldGVycyB0byBiZSBlc3RpbWF0ZWQuCiAgLSBJdCBpcyBpbnNpZ2h0ZnVsIHRvIGNvbXBhcmUgdGhpcyBtb2RlbCB0byBhIGxpbmVhciBtb2RlbCB3aGVyZSAkWV9pJCBpcyBsb2ctdHJhbnNmb3JtZWQuIEluZGVlZCwgaW4gdGhlIGxpbmVhciBtb2RlbCBjYXNlLCB3ZSB3b3VsZCBiZSBtb2RlbGluZyAkRShcbG9nIFlfaSApJCwgd2hpbGUgaW4gdGhlIEdMTSB3ZSBhcmUgbW9kZWxpbmcgJFxsb2cgRShZX2kpJC4KICAtIFRoaXMgc2hvd3MgdGhhdCBpbiB0aGUgR0xNIHNldHRpbmcgd2UgYXJlIG1vZGVsaW5nIGEgdHJhbnNmb3JtZWQgdmVyc2lvbiBvZiB0aGUgZXhwZWN0ZWQgdmFsdWUsIGFuZCBhZnRlciByZXRyYW5zZm9ybWluZyB3ZSBjYW4gaW50ZXJwcmV0IHRoZSBmaXQgaW4gdGVybXMgb2YgdGhlIG1lYW4gb2Ygb3VyIHJlc3BvbnNlIHZhcmlhYmxlLiBJbiB0aGUgdHJhbnNmb3JtZWQgbGluZWFyIG1vZGVsLCBob3dldmVyLCB3ZSBhcmUgd29ya2luZyB3aXRoIHRoZSBleHBlY3RlZCB2YWx1ZSBvZiBhIHRyYW5zZm9ybWVkIHZlcnNpb24gb2Ygb3VyIHJlc3BvbnNlIHZhcmlhYmxlLCBhbmQgd2Ugd2lsbCBub3QgYmUgYWJsZSB0byBpbnRlcnByZXQgdGhlIGZpdCBpbiB0ZXJtcyBvZiB0aGUgbWVhbiwgYmVjYXVzZSAkRSggXGxvZyBZX2kpIFxuZSBcbG9nIEUoWV9pKSQuIEluIHRoaXMgc3BlY2lmaWMgY2FzZSwgd2Ugd291bGQgaGF2ZSB0byByZXNvcnQgdG8gaW50ZXJwcmV0aW5nIGNoYW5nZXMgaW4gdGVybXMgb2YgYSBnZW9tZXRyaWMgbWVhbi4KICAtIEFsc28gbm90ZSB0aGF0ICRcbWF0aGJme1h9XlRfaSBcYmV0YSBcaW4gXS1caW5mdHksIFxpbmZ0eVskLCB3aGlsZSAkWV9pJCBtdXN0IGJlIG5vbi1uZWdhdGl2ZSAkWzAsIFxpbmZ0eVskLiBUaGUgbGluayBmdW5jdGlvbiBoZWxwcyB3aXRoIHRoaXMsIHNpbmNlIHRoZSBleHBvbmVudGlhbCBmdW5jdGlvbiB0cmFuc2Zvcm1zIGFueSByZWFsIG51bWJlciB0byBhIG5vbi1uZWdhdGl2ZSBudW1iZXIsIGkuZS4sICRcZXhwKFxtYXRoYmZ7WH1eVF9pIFxiZXRhKSBcaW4gWzAsIFxpbmZ0eVskLiAKICAKCiMjIyBQYXJhbWV0ZXIgZXN0aW1hdGlvbiB1c2luZyBtYXhpbXVtIGxpa2VsaWhvb2QKCiAtIEluIG1heGltdW0gbGlrZWxpaG9vZCwgd2UgYXR0ZW1wdCB0byAqKm1heGltaXplIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIG9mIHRoZSBkYXRhKiosIHVuZGVyIHRoZSBwb3NpdGVkIGFzc3VtcHRpb25zLiBUaGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBpcyB0eXBpY2FsbHkgcGFyYW1ldHJpemVkIGJ5IGEgbGltaXRlZCBudW1iZXIgb2YgcGFyYW1ldGVycywgaGVuY2Ugd2UgY2FuIGZpbmQgdGhlIHZhbHVlcyBvZiB0aGUgcGFyYW1ldGVycyB0aGF0IG1heGltaXplIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uLgogLSBXZSBkbyB0aGlzIGJ5IGZpbmRpbmcgdGhlIHBvaW50IG9uIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIHdoZXJlIGl0cyBmaXJzdCBkZXJpdmF0aXZlIGVxdWFscyB6ZXJvLCBhcyB0aGlzIG11c3QgYmUgYSBtYXhpbXVtIG9mIHRoZSBmdW5jdGlvbi4gRm9yIG5vbi1jb252ZXggbGlrZWxpaG9vZCBmdW5jdGlvbnMsIHRoaXMgbWF5IGJlIGEgbG9jYWwgbWF4aW11bSwgYnV0IGZvciBHTE1zIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIGlzIGNvbnZleCBhbmQgdGhlcmVmb3JlIHRoZSBvYnRhaW5lZCBtYXhpbXVtIG11c3QgYmUgdGhlIGdsb2JhbCBtYXhpbXVtLgogCiMjIyMgTWF4aW11bSBsaWtlbGlob29kIGZvciBhIGxpbmVhciBtb2RlbAoKRm9yIGxpbmVhciBtb2RlbHMsIHdlIGNhbiBkZXJpdmUgYW4gZXF1aXZhbGVudCBlc3RpbWF0b3IgZm9yICRcYmV0YSQgdXNpbmcgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24gYXMgd2UgaGFkIGRlcml2ZWQgaW4gb3VyIHJlY2FwIGxlY3R1cmUgdXNpbmcgbGVhc3Qgc3F1YXJlcyBlc3RpbWF0aW9uLiBXZSBjYW4gZGVmaW5lIGEgbGluZWFyIG1vZGVsIGFzCgokJApZX2kgXHNpbSBOKFxtdV9pLCBcc2lnbWFeMlxtYXRoYmZ7SX0pIFxcClxtdV9pID0gXG1hdGhiZntYfV9pIFxtYXRoYmZ7XGJldGF9CiQkCiAKVGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gb2YgdGhlIGRhdGEgaXMgdGhlIHByb2R1Y3Qgb2YgdGhlIGxpa2VsaWhvb2RzIG9mIGVhY2ggZGF0dW0uIFNpbmNlIHdlIGFyZSBhc3N1bWluZyBhIEdhdXNzaWFuIGRpc3RyaWJ1dGlvbiwgd2UgdXNlIHRoZSBHYXVzc2lhbiBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uOgoKJCQKTChcbWF0aGJme1l9OyBcYmV0YSwgXHNpZ21hKSA9IFxwcm9kX3tpPTF9Xm4gXGZyYWN7MX17XHNxcnR7MiBccGkgXHNpZ21hXjJ9fSBcZXhwIFxsZWZ0XHsgLSBcZnJhY3soWV9pIC0gXG1hdGhiZntYfV9pIFxiZXRhKV4yfXsyIFxzaWdtYV4yfSBccmlnaHRcfQokJAogCkxvZy1saWtlbGlob29kIGZ1bmN0aW9uCgokJApcZWxsKFxtYXRoYmZ7WX07IFxiZXRhLCBcc2lnbWEpID0gXHN1bV97aT0xfV5uIFxsZWZ0XHsgLVxmcmFjezF9ezJ9IFxsb2coMlxwaSBcc2lnbWFeMikgLSBcZnJhY3sxfXsyXHNpZ21hXjJ9IChZX2kgLSBcbWF0aGJme1h9X2kgXGJldGEpXjIgXHJpZ2h0XH0KJCQKIApTY29yZSBmdW5jdGlvbiBpcyB0aGUgZGVyaXZhdGl2ZSBvZiB0aGUgbG9nLWxpa2VsaWhvb2QKCiQkClMoXG1hdGhiZntcYmV0YX0pID0gXGZyYWN7XHBhcnRpYWwgXGVsbChcbWF0aGJme1l9OyBcYmV0YSwgXHNpZ21hKX17XHBhcnRpYWwgXGJldGF9ID0gXHN1bV97aT0xfV5uIFxmcmFjezF9e1xzaWdtYV4yfSBcbWF0aGJme1h9X2kgKFlfaSAtIFxtYXRoYmZ7WH1faSBcYmV0YSkKJCQKIAogU2V0IHRvIHplcm8gYW5kIHNvbHZlCiAKJCQKXG1hdGhiZntYfV5UXG1hdGhiZntZfSAtIFxtYXRoYmZ7WH1eVCBcbWF0aGJme1h9IFxtYXRoYmZ7XGJldGF9ID0gXG1hdGhiZnswfSBcXApccmlnaHRhcnJvdyBcd2lkZWhhdHtcbWF0aGJme1xiZXRhfX0gPSAoXG1hdGhiZntYfV5UIFxtYXRoYmZ7WH0pXnstMX0gXG1hdGhiZntYfV5UXG1hdGhiZntZfQokJAogCndoaWNoIGdpdmVzIHVzIGV4YWN0bHkgdGhlIHNhbWUgZXN0aW1hdG9yIGFzIHdlIGhhZCBkZXJpdmVkIHVzaW5nIGxlYXN0IHNxdWFyZXMhCgojIyMjIE1heGltdW0gbGlrZWxpaG9vZCBmb3IgYSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwKCk5vdyB0aGF0IHdlIGtub3cgaG93IHRvIHVzZSBtYXhpbXVtIGxpa2VsaWhvb2QgZm9yIHBhcmFtZXRlciBlc3RpbWF0aW9uLCB3ZSBjYW4gYWxzbyBhcHBseSBpdCB0byBlc3RpbWF0ZSB0aGUgcGFyYW1ldGVycyBvZiBhIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbC4gTGV0J3MgdHJ5IGl0IGZvciB0aGUgUG9pc3NvbiBHTE0gd2UgaGF2ZSBqdXN0IGludHJvZHVjZWQ6CgokJApcbGVmdFx7ClxiZWdpbnthcnJheX17Y2NjfQpZX2kgJiBcc2ltICYgUG9pKFxtdV9pKSBcXApcbG9nIFxtdV9pICYgPSAmIFxldGFfaSBcXApcZXRhX2kgJiA9ICYgXG1hdGhiZntYfV5UX2kgXGJldGEgXFwKXGVuZHthcnJheX0KXHJpZ2h0LgokJAoKCkxpa2VsaWhvb2QgZnVuY3Rpb24gb2YgdGhlIFBvaXNzb24gZGlzdHJpYnV0aW9uCgokJApMKFlfaSA7IFxtdSkgPSBccHJvZF97aT0xfV5uIFxmcmFje2Veey1cbXV9IFxtdV57WV9pfX17WV9pIX0KJCQKCkxvZy1saWtlbGlob29kIGZ1bmN0aW9uCgokJApcZWxsKFlfaSA7IFxtdSkgPSBcc3VtX3tpPTF9Xm4gLSBcbXUgKyBZX2kgXGxvZyhcbXUpIC0gXGxvZyAoWV9pISkKJCQKCk5vdGUgdGhhdCB0aGUgc2NvcmUgZnVuY3Rpb24gaXMgdGhlIGRlcml2YXRpdmUgb2YgdGhlIGxvZy1saWtlbGlob29kIHdpdGggcmVzcGVjdCB0byBvdXIgcGFyYW1ldGVyIG9mIGludGVyZXN0LCAkXGJldGEkLiBTbyBsZXQncyBmaXJzdCByZXdyaXRlIG91ciBsb2ctbGlrZWxpaG9vZCBhcyBhIGZ1bmN0aW9uIG9mIG91ciBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QuIFdlIGtub3cgZnJvbSB0aGUgbW9kZWwgdGhhdCAkXG11X2kgPSBcZXhwKFxtYXRoYmZ7WH1faSBcbWF0aGJme1xiZXRhfSkkLgoKJCQKXGVsbChZX2kgOyBcYmV0YSkgPSBcc3VtX3tpPTF9Xm4gLSBcZXhwKFxtYXRoYmZ7WH1faSBcbWF0aGJme1xiZXRhfSkgKyBZX2kgKFxtYXRoYmZ7WH1faSBcbWF0aGJme1xiZXRhfSkgLSBcbG9nIChZX2khKQokJAoKVGhlIHNjb3JlIGZ1bmN0aW9uIHRoZW4gZXF1YWxzCgokJApTKFxtYXRoYmZ7XGJldGF9KSA9IFxmcmFje1xwYXJ0aWFsIFxlbGwoXG1hdGhiZntZfTsgXGJldGEpfXtccGFydGlhbCBcYmV0YX0gPSBcc3VtX3tpPTF9Xm4gLVxtYXRoYmZ7WH1faV5UIFxleHAoXG1hdGhiZntYfV9pIFxtYXRoYmZ7XGJldGF9KSArIFxtYXRoYmZ7WH1faV5UWV9pID0gLVxtYXRoYmZ7WH1eVCBcZXhwKFxtYXRoYmZ7WH0gXG1hdGhiZntcYmV0YX0pICsgXG1hdGhiZntYfV5UIFxtYXRoYmZ7WX0KJCQKClNldCB0byB6ZXJvIGFuZCBzb2x2ZQoKJCQKXG1hdGhiZntYfV5UIFxtYXRoYmZ7WX0gPSBcbWF0aGJme1h9XlQgXGV4cChcbWF0aGJme1h9IFxtYXRoYmZ7XGJldGF9KQokJAoKSG93ZXZlciwgc2luY2UgdGhpcyBpcyBhIG5vbi1saW5lYXIgZXF1YXRpb24gaW4gJFxiZXRhJCwgd2UgY2Fubm90IGZpbmQgYSBjbG9zZWQtZm9ybSBzb2x1dGlvbiEgWW91IG1heSBzZWUgdGhpcyBtb3JlIGNsZWFybHkgd2hlbiB3cml0aW5nIG91dCBpbiBub24tbWF0cml4IGZvcm0KCiQkClxzdW1faSBcc3VtX3AgeF97aXB9IFxleHAoeF97aXB9IFxiZXRhX3ApID0gXHN1bV9pIFxzdW1fcCB4X3tpcH0gWV9pLgokJAoKIC0tLQoKIC0gVGhlIGFib3ZlIGRlcml2YXRpb25zIHNob3cgdGhhdCBlc3RpbWF0aW5nIHRoZSBwYXJhbWV0ZXJzIG9mIGEgR0xNIGlzIG11Y2ggaGFyZGVyIGFzIGNvbXBhcmVkIHRvIGEgbGluZWFyIG1vZGVsLgogLSBUaGUgKippdGVyYXRpdmUgcmV3ZWlnaHRlZCBsZWFzdCBzcXVhcmVzIChJUkxTKSoqIGFsZ29yaXRobSBpcyB1c3VhbGx5IGFkb3B0ZWQgZm9yIGZpdHRpbmcgR0xNcyB1c2luZyBtYXhpbXVtIGxpa2VsaWhvb2QuIEFzIHRoZSBuYW1lIHN1Z2dlc3RzLCBpdCBpcyBhbiBpdGVyYXRpdmUgYWxnb3JpdGhtLCB3aGVyZSBlYWNoIGRhdGEgcG9pbnQgaXMgcmV3ZWlnaHRlZCBpbiBlYWNoIGl0ZXJhdGlvbiBhY2NvcmRpbmcgdG8gdGhlIGFzc3VtZWQgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAsIHdoaWNoIGlzIGEgZnVuY3Rpb24gb2YgaXRzIGVzdGltYXRlZCBtZWFuIG9mIHRoZSBwcmV2aW91cyBpdGVyYXRpb24uIEluZGVlZCwgb2JzZXJ2YXRpb25zIHdpdGggaGlnaCB2YXJpYW5jZSB3aWxsIGJlIGRvd253ZWlnaHRlZCBhbmQgdmljZSB2ZXJzYS4gSVJMUyB1c2VzIHRoZSBkZXJpdmF0aXZlIG9mIHRoZSBzY29yZSBmdW5jdGlvbiAoaS5lLiwgdGhlIHNlY29uZCBkZXJpdmF0aXZlIG9mIHRoZSBsb2ctbGlrZWxpaG9vZCBmdW5jdGlvbikgdG8gbW92ZSBpbnRvIHRoZSBkaXJlY3Rpb24gd2hlcmUgdGhlIGZpcnN0IGRlcml2YXRpdmUgaXMgemVyby4KIApgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmNhcD1wYXN0ZSgiIil9CiMgQWxsIGRlZmF1bHRzCmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvaXJsc1NjaGVtZS5wbmciKQpgYGAKRmlndXJlOiBGaW5kaW5nIHRoZSByb290IG9mIHRoZSBzY29yZSBmdW5jdGlvbiB1c2luZyBOZXd0b24tUmFwaHNvbiBvcHRpbWl6YXRpb24uIFRoZSBGaWd1cmUgc2hvd3MgZXN0aW1hdGlvbiBvZiBhIHNpbmdsZSAkXGJldGEkIHBhcmFtZXRlci4gVGhlIGJsYWNrIHNvbGlkIGxpbmUgaXMgdGhlIFNjb3JlIGZ1bmN0aW9uIGV2YWx1YXRlZCBhdCAkXGJldGEkLiBBbiBpbml0aWFsIGVzdGltYXRlIG9mICRcYmV0YSQgaXMgMi4yNSwgd2hpY2ggaXMgcmVwcmVzZW50ZWQgYnkgdGhlIGRvdHRlZCBsaW5lLiBUaGUgdmFsdWUgb2YgdGhlIHNjb3JlIGZ1bmN0aW9uIG9mIHRoaXMgaW5pdGlhbCB2YWx1ZSBpcyAkUyhcYmV0YV5rKSQuIFRoZSBmaXJzdCBkZXJpdmF0aXZlIG9mIHRoZSBzY29yZSBmdW5jdGlvbiBhdCB0aGF0IHBvaW50LCBldmFsdWF0ZWQgYXQgJFxiZXRhID0gMi4yNSQsIGlzIHJlcHJlc2VudGVkIGJ5IHRoZSBzb2xpZCBibHVlIGxpbmUgYW5kIGlzIGdpdmVuIGJ5ICRcZnJhY3tccGFydGlhbCBTKFxiZXRhKX17XHBhcnRpYWwgXGJldGF9JC4gVGhlIHZhbHVlIG9mICRcYmV0YSQgd2hlcmUgdGhlIHNvbGlkIGJsdWUgbGluZSBjcm9zc2VzIHplcm8gaXMgdGhlIG5ldyBlc3RpbWF0ZSBmb3IgJFxiZXRhJCwgbmFtZWx5ICRcYmV0YV57aysxfSQgd2hpY2ggaGFzIGEgdmFsdWUgb2YgMS40LiBUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuICRcYmV0YV57aysxfSQgYW5kICRcYmV0YV57a30kIGlzIGdpdmVuIGJ5ICRcbGVmdCBceyBcZnJhY3sgXHBhcnRpYWwgUyhcYmV0YSl9eyBccGFydGlhbCBcYmV0YX0gICBccmlnaHQgXH1eey0xfSBTKFxiZXRhXmspJC4gVGhpcyBwcm9jZWR1cmUgaXMgaXRlcmF0ZWQgdW50aWwgYSBjb252ZXJnZW5jZSBpbiB0aGUgJFxiZXRhJCBlc3RpbWF0ZSBpcyBtZXQuCgojIyMgR2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVscyBpbiBgUmAKCiAtIEluIG9yZGVyIHRvIGdldCBmYW1pbGlhciB3aXRoIEdMTXMsIHdlIHdpbGwgZml0IGEgUG9pc3NvbiBHTE0gaW4gYFJgLCB1c2luZyB0aGUgYEJpa2VzaGFyZWAgZGF0YXNldCBhcyBwYXJ0IG9mIHRoZSBgSVNMUjJgIHBhY2thZ2UuIFRoaXMgZGF0YXNldCByZWNvcmRzIGhvdyBtYW55IGJpa2VzIHdlcmUgYmVpbmcgdXNlZCBmcm9tIGEgYmlrZS1zaGFyaW5nIHNlcnZpY2UsIGV2ZXJ5IGhvdXIgb2YgdGhlIGRheSBvdmVyIGEgZnVsbCB5ZWFyICgzNjUgZGF5cykuCiAtIEZ1bGwgaW5mb3JtYXRpb24gb2YgdGhlIGRhdGFzZXQgaXMgcHJvdmlkZWQgW2hlcmVdKGh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9tbC9kYXRhc2V0cy9iaWtlK3NoYXJpbmcrZGF0YXNldCkuIFZhcmlhYmxlcyBvZiBpbnRlcmVzdCBmb3IgdXMgYXJlOgogCiAgICAtIGBiaWtlcnNgOiBEaXNjcmV0ZSBjb3VudCB2YXJpYWJsZTsgdGhlIG51bWJlciBvZiBiaWtlcyBiZWluZyB1c2VkIHRoYXQgaG91ci4KICAgIC0gYGh1bWA6IENvbnRpbnVvdXMgdmFyaWFibGUgcmFuZ2luZyBiZXR3ZWVuIDAgYW5kIDE7IG5vcm1hbGl6ZWQgaHVtaWRpdHkuCiAgICAtIGBocmA6IENhdGVnb3JpY2FsIHZhcmlhYmxlIGJldHdlZW4gMCBhbmQgMjM7IHRoZSBob3VyIG9mIHRoZSBkYXkuIE9uZSBjb3VsZCBhbHNvIGNvbnNpZGVyIHRoaXMgdmFyaWFibGUgdG8gYmUgbnVtZXJpYyBhbmQgbW9kZWwgaXQgYXMgc3VjaCwgYnV0IHRoZSBkYXRhIGV4cGxvcmF0aW9uIHdpbGwgc2hvdyB0aGF0J3Mgbm90IGFwcHJvcHJpYXRlLgogICAgLSBgd2VhdGhlcnNpdGA6IENhdGVnb3JpY2FsIHZhcmlhYmxlOyB0aGUgd2VhdGhlciBjb25kaXRpb24gb2YgdGhhdCBob3VyLCB3aXRoCiAgICAKICAgICAgMS4gQ2xlYXIsIEZldyBjbG91ZHMsIFBhcnRseSBjbG91ZHkuCiAgICAgIDIuIE1pc3QgKyBDbG91ZHksIE1pc3QgKyBCcm9rZW4gY2xvdWRzLCBNaXN0ICsgRmV3IGNsb3VkcywgTWlzdC4KICAgICAgMy4gTGlnaHQgU25vdywgTGlnaHQgUmFpbiArIFRodW5kZXJzdG9ybSArIFNjYXR0ZXJlZCBjbG91ZHMsIExpZ2h0IFJhaW4gKyBTY2F0dGVyZWQgY2xvdWRzLgogICAgICA0LiBIZWF2eSBSYWluICsgSWNlIFBhbGxldHMgKyBUaHVuZGVyc3Rvcm0gKyBNaXN0LCBTbm93ICsgRm9nLgogICAgCiAtLS0gIAoKYGBge3J9CiMgaWYgSVNMUjIgaXNuJ3QgaW5zdGFsbGVkLCBpbnN0YWxsIGl0OgppZighIklTTFIyIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywxXSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiSVNMUjIiKQp9CiMgbG9hZCBhbmQgcHJldmlldyB0aGUgZGF0YXNldDoKZGF0YSgiQmlrZXNoYXJlIiwgcGFja2FnZT0iSVNMUjIiKQpoZWFkKEJpa2VzaGFyZSkKCiMgYXNzb2NpYXRpb24gd2l0aCB3ZWF0aGVyIG9uIGNvdW50IGFuZCBsb2cgc2NhbGUKYmFycGxvdCh0YWJsZShCaWtlc2hhcmUkd2VhdGhlcnNpdCkpCmJveHBsb3QoYmlrZXJzIH4gd2VhdGhlcnNpdCwgZGF0YT1CaWtlc2hhcmUsCiAgICAgICAgeGxhYiA9ICJXZWF0aGVyIiwgeWxhYiA9ICJCaWtlcnMiKQpib3hwbG90KGxvZzFwKGJpa2VycykgfiB3ZWF0aGVyc2l0LCBkYXRhPUJpa2VzaGFyZSwKICAgICAgICB4bGFiID0gIldlYXRoZXIiLCB5bGFiID0gIkxvZyAoYmlrZXJzICsxKSIpCgojIGFzc29jaWF0aW9uIHdpdGggaHVtaWRpdHkgb24gY291bnQgYW5kIGxvZyBzY2FsZQpoaXN0KEJpa2VzaGFyZSRodW0sIGJyZWFrcz00MCkKcGxvdChiaWtlcnMgfiBodW0sIGRhdGE9QmlrZXNoYXJlLCBwY2g9MTYsIGNleD0xLzIsCiAgICAgICAgeGxhYiA9ICJIdW1pZGl0eSIsIHlsYWIgPSAiQmlrZXJzIikKbG9IdW0gPC0gbG9lc3MoYmlrZXJzIH4gaHVtLCBkYXRhPUJpa2VzaGFyZSkKeEdyaWQgPC0gc2VxKDAsIDEsIGxlbmd0aD01MCkKeWhhdCA8LSBwcmVkaWN0KGxvSHVtLCBkYXRhLmZyYW1lKGh1bSA9IHhHcmlkKSkKbGluZXMoeD14R3JpZCwgeT15aGF0LCBjb2w9InJlZCIsIGx3ZD0zKQoKcGxvdChsb2cxcChiaWtlcnMpIH4gaHVtLCBkYXRhPUJpa2VzaGFyZSwgcGNoPTE2LCBjZXg9MS8yLAogICAgICAgIHhsYWIgPSAiSHVtaWRpdHkiLCB5bGFiID0gIkxvZyAoYmlrZXJzICsxKSIpCmxvSHVtIDwtIGxvZXNzKGxvZzFwKGJpa2VycykgfiBodW0sIGRhdGE9QmlrZXNoYXJlKQp4R3JpZCA8LSBzZXEoMCwgMSwgbGVuZ3RoPTUwKQp5aGF0IDwtIHByZWRpY3QobG9IdW0sIGRhdGEuZnJhbWUoaHVtID0geEdyaWQpKQpsaW5lcyh4PXhHcmlkLCB5PXloYXQsIGNvbD0icmVkIiwgbHdkPTMpCgojIGFzc29jaWF0aW9uIHdpdGggaG91ciBvbiBjb3VudCBhbmQgbG9nIHNjYWxlCmJhcnBsb3QodGFibGUoQmlrZXNoYXJlJGhyKSwgeGxhYj0iSG91ciBvZiBkYXkiLCB5bGFiPSJOdW1iZXIgb2Ygb2JzZXJ2YXRpb25zIikKcGxvdChiaWtlcnMgfiBociwgZGF0YT1CaWtlc2hhcmUsIHBjaD0xNiwgY2V4PTEvMiwKICAgICAgICB4bGFiID0gIkhvdXIgb2YgZGF5IiwgeWxhYiA9ICJCaWtlcnMiKQpwbG90KGxvZzFwKGJpa2VycykgfiBociwgZGF0YT1CaWtlc2hhcmUsIHBjaD0xNiwgY2V4PTEvMiwKICAgICAgICB4bGFiID0gIkhvdXIgb2YgZGF5IiwgeWxhYiA9ICJMb2cgKGJpa2VycyArMSkiKQpgYGAKClRoZSBkYXRhIGV4cGxvcmF0aW9uIHNob3dzIHRoYXQKCiAtIE1vcmUgYmlrZXMgYXJlIGJlaW5nIHVzZWQgaW4gYmV0dGVyIHdlYXRoZXIuCiAtIFRoZXJlIHNlZW1zIHRvIGJlIGEgbm9uLWxpbmVhciBhc3NvY2lhdGlvbiBiZXR3ZWVuIGJpY3ljbGUgcmVudGFscyBhbmQgaHVtaWRpdHksIHdoZXJlIGluIGJvdGggbG93IGFuZCBoaWdoIGh1bWlkaXR5IGNvbmRpdGlvbnMgcmVsYXRpdmVseSBmZXcgYmlrZXMgYXJlIHVzZWQsIHBvc3NpYmx5IHJlZmxlY3RpbmcgdmVyeSBob3QgYW5kIHZlcnkgd2V0IGRheXMgcmVzcGVjdGl2ZWx5LCB3aGlsZSBtb3N0IGJpa2VzIGFyZSBiZWluZyB1c2VkIGF0IG1vZGVyYXRlIGh1bWlkaXR5LgogLSBCaWN5Y2xlIHJlbnRhbCBpcyBhc3NvY2lhdGVkIHdpdGggdGhlIGhvdXIgb2YgdGhlIGRheSwgaG93ZXZlciwgaW4gYSBub24tbGluZWFyIHdheSwgd2l0aCBjbGVhciBwZWFrcyBpbiB1c2FnZSBhdCB0eXBpY2FsIGNvbW11dGUgaG91cnMgKDZoLThoIGFuZCAxN2gtMTloKS4gSGVyZSwgd2Ugd2lsbCBhZGQgYGhyYCBhcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHRvIHRoZSBtb2RlbCwgZXN0aW1hdGluZyBvbmUgcGFyYW1ldGVyIGZvciBlYWNoIGhvdXIuIE5vdGUgdGhhdCBhbHRlcm5hdGl2ZSBzdHJhdGVnaWVzIGFyZSBwb3NzaWJsZSB0aGF0IG1heSBiZSBtb3JlIGVmZmljaWVudCwgc3VjaCBhcyBpbmNvcnBvcmF0aW5nIGBocmAgYXMgYSBudW1lcmljYWwgdmFyaWFibGUgYW5kIG1vZGVsaW5nIHRoZSBub24tbGluZWFyaXR5IHVzaW5nIGEgbG93ZXIgbnVtYmVyIG9mIHBhcmFtZXRlcnMuCiAtICpEaXNjbGFpbWVyKjogTm90ZSB0aGF0IHRoZXJlIGFyZSBsaWtlbHkgaW50ZXJhY3Rpb25zIGJldHdlZW4gdGhlIHZhcmlhYmxlcywgd2hpY2ggaGVyZSB3ZSB3aWxsIG5vdCBldmFsdWF0ZSBhcyBvdXIgZ29hbCBpcyB0byBpbnRyb2R1Y2UgYSBQb2lzc29uIEdMTSByYXRoZXIgdGhhbiBhIGZ1bGwgYW5hbHlzaXMgb2YgdGhlIGBCaWtlc2hhcmVgIGRhdGFzZXQuIEZvciBleGFtcGxlLCBpdCBzZWVtcyBsaWtlbHkgdGhhdCBtb3JlIHBlb3BsZSBjb21tdXRlIGJ5IGJpa2UgaW4gZ29vZCB3ZWF0aGVyLCB3aGlsZSBmZXdlciBwZW9wbGUgd2lsbCBjb21tdXRlIGJ5IGJpa2UgaW4gdGVycmlibGUgd2VhdGhlci4gVGhpcyB3b3VsZCBtb3RpdmF0ZSBhbiBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMgYHdlYXRoZXJzaXRgIGFuZCBgaHJgLgogCiAtLS0KIAogLSBCZWxvdywgd2UgZml0IGEgUG9pc3NvbiBHTE0gdXNpbmcgdGhlIGBnbG1gIGZ1bmN0aW9uLiBUaGUgbnVtYmVyIG9mIGJpa2VycyBpcyB1c2VkIGFzIGEgcmVzcG9uc2UgdmFyaWFibGUsIHdoaWNoIGlzIG1vZGVsZWQgYXMgYSBmdW5jdGlvbiBvZiBgd2VhdGhlcnNpdGAsIGBodW1gIGFuZCBgaHJgLiAKIC0gTm90ZSB0aGF0IHRoZXJlIHNlZW1zIHRvIGJlIGEgbm9uLWxpbmVhciwgdGhvdWdoIGZhaXJseSBzaW1wbGUsIGFzc29jaWF0aW9uIGJldHdlZW4gb3VyIHJlc3BvbnNlIHZhcmlhYmxlIGFuZCB0aGUgaHVtaWRpdHkuIFdlIHdpbGwgdGhlcmVmb3JlIGFkZCBhIHF1YWRyYXRpYyBhbmQgY3ViaWMgdGVybSBmb3IgaHVtaWRpdHkgdG8gdGhlIG1vZGVsLiBJbiBvcmRlciB0byBhdm9pZCBtdWx0aWNvbGxpbmVhcml0eSBiZXR3ZWVuIHRoZSBsaW5lYXIsIHF1YWRyYXRpYyBhbmQgY3ViaWMgaHVtaWRpdHkgZWZmZWN0cywgd2Ugd2lsbCBmaXJzdCBjZW50ZXIgdGhlIGh1bWlkaXR5IHZhcmlhYmxlIGFuZCBzdG9yZSB0aGlzIGluIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCBgaHVtY2AuIFRoaXMgbWVhbnMgdGhhdCB3aGVuIGBodW1jPTBgLCB0aGlzIGNvcnJlc3BvbmRzIHRvIHRoZSBhdmVyYWdlIGh1bWlkaXR5IGluIHRoZSBkYXRhc2V0LgogLSBUaGUgYXJndW1lbnQgYGZhbWlseSA9ICJwb2lzc29uImAgc3BlY2lmaWVzIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGFuZCBieSBkZWZhdWx0IHRoZSBjYW5vbmljYWwgbGluayBmdW5jdGlvbiwgd2hpY2ggaXMgdGhlIGxvZyBsaW5rLCB3aWxsIGJlIHVzZWQuCiAKYGBge3J9CkJpa2VzaGFyZSRodW1jIDwtIEJpa2VzaGFyZSRodW0gLSBtZWFuKEJpa2VzaGFyZSRodW0pCm0gPC0gZ2xtKGJpa2VycyB+IHdlYXRoZXJzaXQgKyBodW1jICsgSShodW1jXjIpICsgSShodW1jXjMpICsgaHIsCiAgICAgICAgIGRhdGEgPSBCaWtlc2hhcmUsCiAgICAgICAgIGZhbWlseSA9ICJwb2lzc29uIikKc3VtbWFyeShtKQpgYGAKCiMjIyMgSW50ZXJwcmV0YXRpb24gb2YgZXN0aW1hdGVkIG1vZGVsIHBhcmFtZXRlcnMgCgogLSBSZW1lbWJlciB0aGF0IHRoZSBQb2lzc29uIEdMTSBjYW4gYmUgZGVmaW5lZCBhcwogJCQKICBcbGVmdFx7CiAgXGJlZ2lue2FycmF5fXtjY2N9CiAgWV9pICYgXHNpbSAmIFBvaShcbXVfaSkgXFwKICBcbG9nIFxtdV9pICYgPSAmIFxldGFfaSBcXAogIFxldGFfaSAmID0gJiBcbWF0aGJme1h9XlRfaSBcYmV0YSBcXAogIFxlbmR7YXJyYXl9CiAgXHJpZ2h0LgogICQkCiAgCiAqSW50ZXJwcmV0YXRpb24gb2YgdGhlIGludGVyY2VwdC4qCiAgCiAtIFdlIHdpbGwgZmlyc3QgaW50ZXJwcmV0IHRoZSBpbnRlcmNlcHQsIGluIHRlcm1zIG9mIHRoZSBhdmVyYWdlIG51bWJlciBvZiBiaWtlcyBiZWluZyB1c2VkLiBOb3RlIHRoYXQgdGhlIGludGVyY2VwdCBjb3JyZXNwb25kcyB0byBob3VyIDAsIGF0IGdvb2Qgd2VhdGhlciAoYHdlYXRoZXJzaXRgIGxldmVsIDEpLCBhbmQgYXZlcmFnZSBodW1pZGl0eSAoYGh1bWNgPTApLiBXZSB3aWxsIGRlbm90ZSB0aGUgaW50ZXJjZXB0IGFzICRcYmV0YV8wJCBhbmQgaXRzIGVzdGltYXRlIGFzICRcaGF0e1xiZXRhfV8wJC4gQWxsIG90aGVyIGNvZWZmaWNpZW50cyB3aWxsIHRodXMgZGVub3RlIGEgcmVsYXRpdmUgY2hhbmdlIHdpdGggcmVzcGVjdCB0byB0aGF0IHJlZmVyZW5jZSBsZXZlbC4KIC0gVGhlIG1vZGVsIGRlZmluaXRpb24gc2hvd3MgdGhhdCAkXGxvZyBcbXVfaSA9IFxtYXRoYmZ7WH1eVF9pIFxtYXRoYmZ7XGJldGF9JCwgd2l0aCAkXG11JCB0aGUgYXZlcmFnZSBudW1iZXIgb2YgYmlrZXMgYmVpbmcgdXNlZC4gU2luY2Ugd2UncmUgb25seSB3b3JraW5nIHdpdGggdGhlIGludGVyY2VwdCBoZXJlLCB3ZSBtYXkgd3JpdGUgJFxsb2cgXG11X2kgPSBcYmV0YV8wJCwgYW5kIHRodXMgJFxtdV9pID0gXGV4cCBcYmV0YV8wJC4KIC0gUGx1Z2dpbmcgaW4gdGhlIGVzdGltYXRlZCBpbnRlcmNlcHQgJFxoYXR7XGJldGF9XzAkLCB3ZSBoYXZlICRcZXhwIFxoYXR7XGJldGF9XzAgPSQgYHIgcm91bmQoZXhwKGNvZWYobSlbMV0pLCAyKWAuIEluIG90aGVyIHdvcmRzLCBpbiBjbGVhciB3ZWF0aGVyIHdpdGggZmV3IGNsb3VkcywgYXQgYXZlcmFnZSBodW1pZGl0eSBhbmQgYXQgaG91ciAwLCBhbiBhdmVyYWdlIG9mIGByIHJvdW5kKGV4cChjb2VmKG0pWzFdKSwgMilgIGJpa2VzIGFyZSBiZWluZyB1c2VkLgogCiAtLS0KIAogKkludGVycHJldGF0aW9uIG9mIGB3ZWF0aGVyc2l0Y2xvdWR5L21pc3R5YC4qCiAKIC0gV2Ugd2lsbCBkZW5vdGUgdGhpcyBjb2VmZmljaWVudCBhcyAkXGJldGFfMSQgYW5kIGl0cyBlc3RpbWF0ZSBhcyAkXGhhdHtcYmV0YX1fMSQuIAogLSBOb3RlIHRoYXQgdGhpcyBjb2VmZmljaWVudCBkZWZpbmVzIHRoZSBkaWZmZXJlbmNlIGluIGxpbmVhciBwcmVkaWN0b3IgYmV0d2VlbiBgd2VhdGhlcnNpdD0yYCBhbmQgYHdlYXRoZXJzaXQ9MWAsIGFsbCBvdGhlciB2YXJpYWJsZXMgYmVpbmcgZXF1YWwgKHNheSwgYXQgdGhlaXIgcmVmZXJlbmNlIGxldmVsKS4gSW5kZWVkLCBkZWZpbmUgJFxldGFfe3cyfSQgYW5kICRcZXRhX3t3MX0kIHRvIGRlbm90ZSB0aGUgbGluZWFyIHByZWRpY3RvciBhdCBgd2VhdGhlcnNpdD0yYCwgYW5kIGB3ZWF0aGVyc2l0PTFgLCByZXNwZWN0aXZlbHkuIFRoZW4sICRcZXRhX3t3Mn0gLSBcZXRhX3t3MX0gPSAoXGJldGFfMCArIFxiZXRhXzEpIC0gXGJldGFfMCA9IFxiZXRhXzEkLgogLSBUaGlzIGFsc28gbWVhbnMgdGhhdCAkXGJldGFfMSA9IFxsb2cgXG11X3t3Mn0gLSBcbG9nIFxtdV97dzF9ID0gXGxvZyBcZnJhY3tcbXVfe3cyfX17XG11X3syMX19JCwgYW5kIHRodXMgJFxleHAgXGJldGFfMSA9IFxmcmFje1xtdV97dzJ9fXtcbXVfezIxfX0kLgogLSBJbiBvdXIgY2FzZSwgJFxleHAgXGhhdHtcYmV0YX1fMSA9JCBgciByb3VuZChleHAoY29lZihtKVsyXSksIDIpYC4gSW4gd29yZHM6IEFsbCBvdGhlciB2YXJpYWJsZXMgYmVpbmcgZXF1YWwsIHRoZSBhdmVyYWdlIG51bWJlciBvZiBiaWtlcyBiZWluZyB1c2VkIGluIGNsb3VkeS9taXN0eSB3ZWF0aGVyIGlzICQwLjg1JCB0aW1lcyAob3IsIGFsc28sICQ4NVwlJCBvZikgdGhlIG51bWJlciBvZiBiaWtlcyBiZWluZyB1c2VkIGluIGdvb2Qgd2VhdGhlci4KIC0gVGhpcyBleGVyY2lzZSBoYXMgc2hvd24gdXMgdGhhdCwgKipkdWUgdG8gdGhlICRcbG9nJCBsaW5rIGZ1bmN0aW9uKiosIHRoZSBwYXJhbWV0ZXJzIGluIGEgUG9pc3NvbiBHTE0gY2Fubm90IGJlIGludGVycHJldGVkIGluIHRlcm1zIG9mIGFic29sdXRlIGRpZmZlcmVuY2VzIGluIGF2ZXJhZ2VzIG9mIHRoZSByZXNwb25zZSB2YXJpYWJsZSBidXQgaW5zdGVhZCBtdXN0IGJlIGludGVycHJldGVkIGluIHRlcm1zIG9mICoqbXVsdGlwbGljYXRpdmUgZGlmZmVyZW5jZXMqKiEKIC0gSWYgeW91J3JlIGluIGEgbWVldGluZyBhbmQgeW91IG5lZWQgYSBxdWljayB3YXkgdG8gaW50ZXJwcmV0IHRoZXNlIHBhcmFtZXRlcnMsIHJlbWVtYmVyIHRoYXQgJGV4cCgxKSA9IDIuNzIgXGFwcHJveCAzJCBhbmQgdGh1cyBhIGRpZmZlcmVuY2Ugb2YgJDEkICgkLTEkKSBtZWFucyB0aGUgYXZlcmFnZSBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgaXMgYWJvdXQgdGhyZWUgdGltZXMgaGlnaGVyIChsb3dlcikuCiAKIC0tLQogCiAqSW50ZXJwcmV0YXRpb24gb2YgdGhlIGh1bWlkaXR5IGVmZmVjdC4qCiAKIC0gVGhlIGh1bWlkaXR5IGVmZmVjdCBpcyBhIGJpdCBtb3JlIGludm9sdmVkIHRvIGludGVycHJldC4gRHVlIHRvIHRoZSBxdWFkcmF0aWMgYW5kIGN1YmljIHRlcm1zLCBpdCBpcyBub3Qgc3RyYWlnaHQgZm9yd2FyZCB0byBpbnRlcnByZXQgdGhlIGxpbmVhciB0ZXJtIHNlcGFyYXRlbHkgKGFuZCB0aGUgc2FtZSBhcHBsaWVzIHRvIHRoZSBxdWFkcmF0aWMgb3IgY3ViaWMgdGVybSk7IHdlIG11c3QgaW50ZXJwcmV0IGJvdGggdGhlIGxpbmVhciwgcXVhZHJhdGljIGFuZCBjdWJpYyB0ZXJtIHNpbXVsdGFuZW91c2x5LgogLSBBbHNvIGR1ZSB0byB0aGUgaGlnaGVyLW9yZGVyIHRlcm1zLCB0aGUgcmF0ZSBvZiBjaGFuZ2UgaW4gYXZlcmFnZSBiaWtlcnMgd2lsbCBub3QgYmUgY29uc3RhbnQgYWNyb3NzIHRoZSByYW5nZSBvZiBodW1pZGl0eS4gV2UgY2FuIHRoZXJlZm9yZSBub3QgaW50ZXJwcmV0IHRoZSBodW1pZGl0eSBlZmZlY3QgdXNpbmcgYSBzaW5nbGUgbnVtYmVyIGFzIHdlJ3ZlIGRvbmUgcHJldmlvdXNseS4KIC0gV2UgY2FuLCBob3dldmVyLCBwcm92aWRlIHNvbWUgZXhhbXBsZXMgZm9yIHNwZWNpZmljIGh1bWlkaXR5IHZhbHVlcywgYWxvbmcgd2l0aCBhIHZpc3VhbGl6YXRpb24gb2YgaXRzIGdsb2JhbCBlZmZlY3QuCiAgLSBGb3IgZXhhbXBsZSwgbGV0J3MgZGVyaXZlIHRoZSBjaGFuZ2UgaW4gYXZlcmFnZSBiaWtlcyBiZWluZyB1c2VkIGF0IGEgaHVtaWRpdHkgdGhhdCBpcyAkMC4yJCBhYm92ZSBhdmVyYWdlLCB2ZXJzdXMgYXZlcmFnZSBodW1pZGl0eS4KICAKICAgIEZvciBhdmVyYWdlIGh1bWlkaXR5ICQrMC4yJCB0aGUgbGluZWFyIHByZWRpY3RvciAkXGV0YV97MC4yfSA9IFxiZXRhXzAgKyBcYmV0YV80IHhfe2h1bX0gKyBcYmV0YV81IHhfe2h1bX1eMiArIFxiZXRhXzYgeF97aHVtfV4zID0gXGJldGFfMCArIFxiZXRhXzQgMC4yICsgXGJldGFfNSAwLjJeMiArIFxiZXRhXzYgMC4yXjMkLgogICAgCiAgICBGb3IgYXZlcmFnZSBodW1pZGl0eSwgdGhlIGxpbmVhciBwcmVkaWN0b3IgJFxldGFfezB9ID0gXGJldGFfMCArIFxiZXRhXzQgeF97aHVtfSArIFxiZXRhXzUgeF97aHVtfV4yICsgXGJldGFfNiB4X3todW19XjMgPSBcYmV0YV8wICsgXGJldGFfNCAwICsgXGJldGFfNSAwXjIgKyBcYmV0YV82IDBeMyA9IFxiZXRhXzAkLgogICAgCiAgICBXZSB0aHVzIGhhdmUgJFxsb2cgXGZyYWN7XG11X3swLjJ9fXtcbXVfMH0gPSBcYmV0YV80IDAuMiArIFxiZXRhXzUgMC4yXjIgKyBcYmV0YV82IDAuMl4zJC4gSW4gb3VyIGNhc2UsICRcbG9nIFxmcmFje1xoYXR7XG11fV97MC4yfX17XGhhdHtcbXV9XzB9ID0gMC4wOTE3NTEqMC4yIC0gMi4yMzM5MTkgKiAwLjJeMiAtIDEuODIzMDY2ICogMC4yXjMgPSAtMC4wODU2JCBhbmQgdGh1cyAkXGZyYWN7XGhhdHtcbXV9X3swLjJ9fXtcaGF0e1xtdX1fMH0gPSAwLjkyJC4gVGhlcmVmb3JlLCBhdCBodW1pZGl0eSB0aGF0IGlzICQwLjIkIGFib3ZlIGF2ZXJhZ2UsIHRoZSBhdmVyYWdlIG51bWJlciBvZiBiaWtlcyBiZWluZyB1c2VkIGFyZSAkMC45MiQgdGltZXMgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGJpa2VzIHVzZWQgYXQgYXZlcmFnZSBodW1pZGl0eS4KIC0gSnVzdCBsaWtlIHdpdGggbGluZWFyIG1vZGVscywgdGhlIGBwcmVkaWN0YCBmdW5jdGlvbiBpcyBleHRyZW1lbHkgaGVscGZ1bCB3aGVuIHRyeWluZyB0byB2aXN1YWxpemUgYW5kIHVuZGVyc3RhbmQgYSBmaXR0ZWQgR0xNLiBJbiBHTE1zLCB0aGUgYHR5cGVgIGFyZ3VtZW50IGJlY29tZXMgZXNzZW50aWFsIHdoZW4gdXNpbmcgdGhlIGBwcmVkaWN0YCBmdW5jdGlvbi4gSW5kZWVkLCBieSBkZWZhdWx0LCBlc3RpbWF0ZXMgYXJlIHByb3ZpZGVkIG9uIHRoZSBsaW5lYXIgcHJlZGljdG9yIHNjYWxlOiBpbiBvdXIgY2FzZSwgb24gdGhlICRcbG9nJCBzY2FsZS4gSWYgd2UnZCBsaWtlIHByZWRpY3Rpb25zIG9uIHRoZSBzY2FsZSBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUsIHdlIG5lZWQgdG8gc2V0IGB0eXBlPSJyZXNwb25zZSJgLiBZb3UgY2FuIGZpbmQgbW9yZSBpbmZvcm1hdGlvbiBpbiB0aGUgaGVscCBmaWxlIHVzaW5nIGA/cHJlZGljdC5nbG1gLgogLSBUaGUgdmlzdWFsaXphdGlvbiBzaG93cyB0aGF0IHRoZSBoaWdoZXN0IG51bWJlciBvZiBiaWtlcyBhcmUgYmVpbmcgdXNlZCBhdCBhcm91bmQgYXZlcmFnZSBodW1pZGl0eSwgd2l0aCBhIGRlY3JlYXNlZCB1c2FnZSBhdCBoaWdoZXIgYW5kIGxvd2VyIGh1bWlkaXRpZXMuCiAKYGBge3J9Cmh1bWlkaXR5R3JpZCA8LSBzZXEobWluKEJpa2VzaGFyZSRodW1jKSwgbWF4KEJpa2VzaGFyZSRodW1jKSwgCiAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDUwKQpuZXdEZiA8LSBkYXRhLmZyYW1lKHdlYXRoZXJzaXQgPSBmYWN0b3IoImNsZWFyIiksCiAgICAgICAgICAgICAgICAgICAgaHIgPSBmYWN0b3IoOCksCiAgICAgICAgICAgICAgICAgICAgaHVtYyA9IGh1bWlkaXR5R3JpZCwKICAgICAgICAgICAgICAgICAgICAiSShodW1jXjIpIiA9IGh1bWlkaXR5R3JpZF4yLAogICAgICAgICAgICAgICAgICAgICJJKGh1bWNeMykiID0gaHVtaWRpdHlHcmlkXjMpCnloYXQgPC0gcHJlZGljdChtLCAKICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBuZXdEZiwKICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQoKcGxvdCh4ID0gaHVtaWRpdHlHcmlkLAogICAgIHkgPSB5aGF0LAogICAgIHR5cGUgPSAnbCcsIGx3ZD0yLAogICAgIHhsYWIgPSAiQ2VudGVyZWQgaHVtaWRpdHkiLAogICAgIHlsYWIgPSAiQXZlcmFnZSBudW1iZXIgb2YgYmlrZXJzIikKCmBgYAogCiAtLS0gCiAKICpTZXR0aW5nIHVwIGEgY29udHJhc3QuKgogCiAtIFN1cHBvc2Ugd2UncmUgaW50ZXJlc3RlZCBpbiB3aGV0aGVyIHRoZXJlIGFyZSBtb3JlIGJpa2VycyBhdCAoQSkgbWF4aW11bSBodW1pZGl0eSAoY2VudGVyZWQgaHVtaWRpdHkgdmFsdWUgb2YgJDAuMzU3JCksIGhvdXIgMTcsIGluIHRoZSBgbGlnaHQgcmFpbi9zbm93YCB3ZWF0aGVyIGNhdGVnb3J5LCB2ZXJzdXMgKEIpIGF2ZXJhZ2UgaHVtaWRpdHksIGhvdXIgOCwgaW4gdGhlIGBjbGVhcmAgd2VhdGhlciBjYXRlZ29yeS4gVGhpcyByZXF1aXJlcyB1cyB0byBzZXQgdXAgYSBjb250cmFzdCBpbiB0ZXJtcyBvZiBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0aGUgbW9kZWwgcGFyYW1ldGVycy4KIC0gKipNYW51YWxseSBieSBoYW5kKio6IAogJCQgXGxvZyBcbXVfQSA9IFxiZXRhXzAgKyBcYmV0YV8yIHhfe3JhaW5Tbm93fSArIFxiZXRhXzQgeF97aHVtfSArIFxiZXRhXzUgeF97aHVtfV4yICsgXGJldGFfNiB4X3todW19XjMgKyBcYmV0YV97MjN9IHhfe2hyMTd9IFxcPSAzLjg5NCAtIDAuNTU2ICsgMC4wOTIgKiAwLjM1NyAtIDIuMjM0ICogMC4zNTdeMiAtIDEuODIzICogMC4zNTdeMyArIDIuMTQwID0gNS4xNDMuXFwKICAgXGxvZyBcbXVfQiA9IFxiZXRhXzAgKyBcYmV0YV97MTR9IHhfe2hyOH0gPSAzLjg5NCArIDEuODMwID0gNS43MjQuXFwKICAgXGZyYWN7XG11X0F9e1xtdV9CfSA9IFxleHAoNS4xNDMgLSA1LjcyNCkgPSAwLjU1OS4KICAgJCQKICAgVGh1cywgYXQgbWF4aW11bSBodW1pZGl0eSwgaG91ciAxNywgaW4gdGhlIGBsaWdodCByYWluL3Nub3dgIHdlYXRoZXIgY2F0ZWdvcnkgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGJpa2VycyBpcyA1NiUgdGltZXMgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGJpa2VycyBpbiB0aGUgYXZlcmFnZSBodW1pZGl0eSwgaG91ciA4LCBpbiB0aGUgYGNsZWFyYCB3ZWF0aGVyIGNhdGVnb3J5LgogLSAqKk1hbnVhbGx5IGluIGBSYCoqOiBXZSBjYW4gYWxzbyB1c2UgbWF0cml4IG11bHRpcGxpY2F0aW9uIHRvIGRlcml2ZSB0aGUgZXN0aW1hdGVzLiBXZSBrbm93IGZyb20gb3VyIG1hbnVhbCBjYWxjdWxhdGlvbnMgYWJvdmUsIHRoYXQgdGhlIGNvbnRyYXN0IG9mIGludGVyZXN0IGlzICQoXGJldGFfMCArIFxiZXRhXzIgeF97cmFpblNub3d9ICsgXGJldGFfNCB4X3todW19ICsgXGJldGFfNSB4X3todW19XisgXGJldGFfNiB4X3todW19XjMgKyBcYmV0YV97MjN9IHhfe2hyMTd9KSAtIChcYmV0YV8wICsgXGJldGFfezE0fSB4X3tocjh9KSQuIFdlIGNhbiBzdG9yZSB0aGlzIGluIGEgY29udHJhc3QgbWF0cml4LCBhbmQgdGhlbiBtdWx0aXBseSBpdCB3aXRoIHRoZSBjb2VmZmljaWVudHMgb2Ygb3VyIG1vZGVsOgogCmBgYHtyfQpMIDwtIG1hdHJpeCgwLCAKICAgICAgICAgICAgbnJvdyA9IGxlbmd0aChjb2VmKG0pKSwKICAgICAgICAgICAgbmNvbCA9IDEpCnJvd25hbWVzKEwpIDwtIG5hbWVzKGNvZWYobSkpCkxbIndlYXRoZXJzaXRsaWdodCByYWluL3Nub3ciLDFdIDwtIDEKTFsiaHVtYyIsMV0gPC0gMC4zNTcKTFsiSShodW1jXjIpIiwgMV0gPC0gMC4zNTdeMgpMWyJJKGh1bWNeMykiLCAxXSA8LSAwLjM1N14zCkxbImhyMTciLCAxXSA8LSAxCkxbImhyOCIsMV0gPC0gLTEKTAoKYmV0YSA8LSBtYXRyaXgoY29lZihtKSwgbmNvbD0xKQpleHAodChMKSAlKiUgYmV0YSkgIyBlcXVhbHMgb3VyIG1hbnVhbCBjYWxjdWxhdGlvbi4KYGBgCiAgIAogLSAqKlVzaW5nIGBwcmVkaWN0YCBpbiBgUmAqKjoKCmBgYHtyfQojIHNldCB1cCBkYXRhIGZyYW1lcyB3aXRoIHJlbGV2YW50IHByZWRpY3RvciB2YXJpYWJsZXMnIHZhbHVlcy4KZGZBIDwtIGRhdGEuZnJhbWUod2VhdGhlcnNpdCA9IGZhY3RvcigibGlnaHQgcmFpbi9zbm93IiksCiAgICAgICAgICAgICAgICAgICAgaHIgPSBmYWN0b3IoMTcpLAogICAgICAgICAgICAgICAgICAgIGh1bWMgPSAwLjM1NykKZGZCIDwtIGRhdGEuZnJhbWUod2VhdGhlcnNpdCA9IGZhY3RvcigiY2xlYXIiKSwKICAgICAgICAgICAgICAgICAgICBociA9IGZhY3Rvcig4KSwKICAgICAgICAgICAgICAgICAgICBodW1jID0gMCkKCiMgY2FsY3VsYXRlIGVzdGltYXRlZCBhdmVyYWdlIG51bWJlciBvZiBiaWtlcnMKeWhhdEEgPC0gcHJlZGljdChtLCAKICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkZkEsCiAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikKeWhhdEIgPC0gcHJlZGljdChtLCAKICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkZkIsCiAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikKCnloYXRBIC8geWhhdEIgIyBhbHNvIGVxdWFsIHRvIGFib3ZlLgpgYGAKIAogLS0tCiAKICoqRXhlcmNpc2UqKjogdHJ5IHRvIGRlcml2ZSB0aGUgY2hhbmdlIGluIGF2ZXJhZ2UgbnVtYmVyIG9mIGJpa2VycyBiZXR3ZWVuIChhKSBodW1pZGl0eSBvZiAwLjEgYWJvdmUgYXZlcmFnZSwgY2xlYXIgd2VhdGhlciAoYHdlYXRoZXJzaXQ9MWApLCBhdCBob3VyIDEwIGFuZCAoYikgaHVtaWRpdHkgb2YgMC4xIGJlbG93IGF2ZXJhZ2UsIGNsb3VkeSB3ZWF0aGVyIChgd2VhdGhlcnNpdD0yYCksIGF0IGhvdXIgMjAsIHVzaW5nIGFsbCB0aHJlZSBtZXRob2RzLgogCiMjIFN0YXRpc3RpY2FsIGluZmVyZW5jZSBpbiBHTE1zCgojIyMgV2FsZCB0ZXN0IGFuZCBsaWtlbGlob29kIHJhdGlvIHRlc3QKCiAtIEluIG91ciBpbnRlcnByZXRhdGlvbiBhYm92ZSB3ZSBoYXZlIGZvY3Vzc2VkIG9uIGRlcml2aW5nIGNoYW5nZXMgaW4gdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGJpa2VycyBiZXR3ZWVuIGdyb3VwcyBvZiBpbnRlcmVzdC4gSG93ZXZlciwgd2UgaGF2ZSBub3QgeWV0IHRlc3RlZCB3aGV0aGVyIHRoZXNlIGNoYW5nZXMgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuCiAtIEluIGdlbm9taWNzIGFwcGxpY2F0aW9ucywgc3RhdGlzdGljYWwgaW5mZXJlbmNlIGluIEdMTXMgaXMgb2Z0ZW4gYWRvcHRlZCB0byB0ZXN0IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiZXR3ZWVuIGNvbmRpdGlvbnMgZm9yIGVhY2ggZ2VuZSAoZS5nLiwgKmlzIGdlbmUgQSBkaWZmZXJlbnRseSBleHByZXNzZWQgaW4gaGVhbHRoeSB2ZXJzdXMgdHVtb3JhbCB0aXNzdWU/KiksIHdoaWNoIGFtb3VudHMgdG8gdGVzdGluZyB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIHdoZXRoZXIgYSAobGluZWFyIGNvbWJpbmF0aW9uIG9mKSBjb2VmZmljaWVudChzKSBlcXVhbHMgemVyby4KIC0gSW4gdGhpcyBjb3Vyc2UsIHdlIHdpbGwgbWFpbmx5IHdvcmsgd2l0aCB0d28gdHlwZXMgb2Ygc3RhdGlzdGljYWwgdGVzdHMgZm9yIEdMTXM6CiAgIC0gKipXYWxkIHRlc3QqKjogVGhlIFdhbGQgdGVzdCBtYXkgYmUgdmlld2VkIGFzIGJlaW5nIGFuYW9sb2dvdXMgdG8gdGhlICR0JC10ZXN0IHdlIGFyZSB1c2luZyBpbiBsaW5lYXIgbW9kZWxzLiBUaGUgV2FsZCB0ZXN0IHJlbGllcyBvbiB0aGUgZm9sbG93aW5nIGFzeW1wdG90aWMgcmVzdWx0CiAgICQkXGhhdHtcYmV0YX0gfCBcYmV0YSBcc2ltIE4gKFxiZXRhLCBWYXIoXGhhdHtcYmV0YX0pKSQkLgogICBUaGUgV2FsZCB0ZXN0IHN0YXRpc3RpYyBmb3IgdGVzdGluZyBhIHNpbmdsZSBwYXJhbWV0ZXIgJFxoYXR7XGJldGF9JAogICAkJFcgPSBcZnJhY3tcaGF0e1xiZXRhfX17XGhhdHtTRX0oXGhhdHtcYmV0YX0pfSBcc2ltIE4oMCwxKSB8IEhfMCQkCiAgIG9yLCBlcXVpdmFsZW50bHksIGxldHRpbmcgJFxtYXRoYmZ7Q30kIGRlbm90ZSB0aGUgJDEgXHRpbWVzIHAkIGNvbnRyYXN0IG1hdHJpeCBkZW5vdGluZyB0aGUgY29udHJhc3QgZm9yIHRoZSBzaW5nbGUgcGFyYW1ldGVyICRcYmV0YSQgd2Ugd291bGQgbGlrZSB0byB0ZXN0LCBhbmQgJFxoYXR7XFNpZ21hfV97XGhhdHtcYmV0YX19JCAgdGhlICRwIFx0aW1lcyBwJCB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCBvZiB0aGUgcGFyYW1ldGVycywKICAgJCRXID0gXG1hdGhiZntDfVxoYXR7XGJldGF9IChcbWF0aGJme0N9IFxoYXR7XFNpZ21hfV97XGhhdHtcYmV0YX19IFxtYXRoYmZ7Q31eVCleey0xfSBcaGF0e1xiZXRhfV5UIFxtYXRoYmZ7Q31eVCBcc2ltIFxjaGleMl8xIHwgSF8wLiQkCiAgIFRoZSBudWxsIGFuZCBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIGNhbiB0aGVyZWZvcmUgaW4gZ2VuZXJhbCBiZSB3cml0dGVuIGFzCiAgICQkIEhfMDogXG1hdGhiZntDfSBcYmV0YSA9IDAkJAogICAkJCBIXzE6IFxtYXRoYmZ7Q30gXGJldGEgXG5lIDAkJAogICBJZiAkYyBcZ2UgMSQgY29udHJhc3RzIGFyZSB0ZXN0ZWQsIHRoZW4gdGhlIHRlc3Qgc3RhdGlzdGljICRXIFxzaW0gXGNoaV4yX2MgfCBIXzAkLCBwcm92aWRlZCB0aGF0IHRoZSAkYyQgY29udHJhc3RzIGFyZSBsaW5lYXJseSBpbmRlcGVuZGVudCAoaS5lLiwgdGhlIGNvbnRyYXN0IG1hdHJpeCBpcyBmdWxsIHJhbmspLgogICAtICoqTGlrZWxpaG9vZCByYXRpbyB0ZXN0Kio6IFRoZSBsaWtlbGlob29kIHJhdGlvIHRlc3QgKExSVCkgbWVhc3VyZXMgdGhlIGRpc2NyZXBhbmN5IGluIGxvZy1saWtlbGlob29kIGJldHdlZW4gb3VyIGN1cnJlbnQgbW9kZWwgKHNvbWV0aW1lcyBhbHNvIHJlZmVycmVkIHRvIGFzIGZ1bGwgbW9kZWwpIGFuZCBhIHJlZHVjZWQgbW9kZWwgKHNvbWV0aW1lcyBhbHNvIHJlZmVycmVkIHRvIGFzIG51bGwgb3IgYWx0ZXJuYXRpdmUgbW9kZWwpLiBUaGUgcmVkdWNlZCBtb2RlbCBtdXN0IGJlIG5lc3RlZCBpbiAoYW5kIHRoZXJlZm9yZSBvZiBsb3dlciBkaW1lbnNpb24gYXMgY29tcGFyZWQgdG8pIHRoZSBmdWxsIG1vZGVsLiBXaGlsZSBhZGRpbmcgbW9yZSBjb3ZhcmlhdGVzIHdpbGwgYWx3YXlzIGV4cGxhaW4gbW9yZSB2YXJpYWJpbGl0eSBpbiBvdXIgcmVzcG9uc2UgdmFyaWFibGUsIHRoZSBMUlQgdGVzdHMgd2hldGhlciB0aGlzIGlzIGFjdHVhbGx5IHNpZ25pZmljYW50LgogICBGb3IgZXhhbXBsZSwgaW4gdGhlIGV4YW1wbGUgb2YgZ2VuZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiZXR3ZWVuIGhlYWx0aHkgdmVyc3VzIHR1bW9yYWwgdGlzc3VlLCB0aGUgZnVsbCBtb2RlbCBjb3VsZCBiZSBhIEdMTSB3aGVyZSB0aGUgbWVhbiBpcyBtb2RlbGVkIGFjY29yZGluZyB0byBhbiBpbnRlcmNlcHQgYW5kIGEgdGlzc3VlIGluZGljYXRvciB2YXJpYWJsZSAoaGVhbHRoeSAvIHR1bW9yYWwpLCB3aGlsZSB0aGUgYWx0ZXJuYXRpdmUgbW9kZWwgY291bGQgYmUgYSBHTE0gd2l0aCBqdXN0IGFuIGludGVyY2VwdC4gSW5kZWVkLCBpZiB0aGUgZ2VuZSBpcyBzaW1pbGFybHkgZXhwcmVzc2VkIGJldHdlZW4gaGVhbHRoeSBhbmQgdHVtb3JhbCB0aXNzdWUsIHRoZSBsb2ctbGlrZWxpaG9vZCBvZiB0aGUgYWx0ZXJuYXRpdmUgbW9kZWwgd2lsbCBkZWNyZWFzZSBvbmx5IGEgbGl0dGxlIGFzIGNvbXBhcmVkIHRvIHRoZSBmdWxsIG1vZGVsLgogICBBcyB0aGUgbmFtZSBzdWdnZXN0cywgdGhlIGxpa2VsaWhvb2QgcmF0aW8gdGVzdCBhc3Nlc3NlcyB3aGV0aGVyIHRoZSByYXRpbyBvZiB0aGUgbG9nLWxpa2VsaWhvb2RzIHByb3ZpZGVzIHN1ZmZpY2llbnQgZXZpZGVuY2UgZm9yIGEgd29yc2UgZml0IG9mIHRoZSBhbHRlcm5hdGl2ZSB2ZXJzdXMgZnVsbCBtb2RlbAogICAkJEwgPSAyIFxsZWZ0XHsgXGVsbChcaGF0e1xiZXRhfV97ZnVsbH0pIC0gIFxlbGwoXGhhdHtcYmV0YX1fe2FsdGVybmF0aXZlfSkgXHJpZ2h0XH0uJCQKICAgQXN5bXB0b3RpY2FsbHksIHVuZGVyIHRoZSBudWxsIGh5cG90aGVzaXMgaXQgY2FuIGJlIHNob3duIHRoYXQKICAgJCQgTCBcc2ltIFxjaGlfY14yIHwgSF8wLCAkJAogICB3aXRoICRjJCB0aGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgZHJvcHBlZCBpbiB0aGUgYWx0ZXJuYXRpdmUgbW9kZWwgdmVyc3VzIHRoZSBmdWxsIG1vZGVsLiBJZiB3ZSBhZ2FpbiBsZXQgJFxtYXRoYmZ7Q30kIGRlbm90ZSB0aGUgJGMgXHRpbWVzIHAkIGNvbnRyYXN0IG1hdHJpeCBkZW5vdGluZyB0aGUgY29udHJhc3QgZm9yIHRoZSBwYXJhbWV0ZXJzIGJlaW5nIGRyb3BwZWQsIHRoZSBudWxsIGFuZCBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIGFyZSBhcyBpbiB0aGUgV2FsZCB0ZXN0IHNldHRpbmc6CiAgICQkIEhfMDogXG1hdGhiZntDfSBcYmV0YSA9IDAkJAogICAkJCBIXzE6IFxtYXRoYmZ7Q30gXGJldGEgXG5lIDAkJAogICBGaW5hbGx5LCBub3RlIHRoYXQgd2hpbGUsIGluIHRoaXMgZXhwbGFuYXRpb24sIEkgaGF2ZSBmb2N1c3NlZCBvbiByZWR1Y2luZyBhIG1vcmUgY29tcGxleCBtb2RlbCwgYnV0IG9mIGNvdXJzZSB0aGUgTFJUIGNhbiBhbHNvIGJlIGFkb3B0ZWQgdG8gY2hlY2sgd2hldGhlciBhZGRpbmcgYSBjb3ZhcmlhdGUgc2lnbmlmaWNhbnRseSBpbXByb3ZlcyB0aGUgZml0LgogLSBJdCBpcyBpbXBvcnRhbnQgdG8ga2VlcCBpbiBtaW5kIHRoYXQgc3RhbmRhcmQgc3RhdGlzdGljYWwgaW5mZXJlbmNlIHRoZW9yeSBpbiBHTE1zIHdvcmtzICoqYXN5bXB0b3RpY2FsbHkgaW4gdGVybXMgb2YgdGhlIHNhbXBsZSBzaXplKiouIFRodXMgd2UgbmVlZCBtYW55IGRhdGEgcG9pbnRzIGluIG9yZGVyIGZvciB0aGUgdGhlb3J5IHRvIGhvbGQgaW4gcHJhY3RpY2UuIEluIG9yZGVyIGZvciB0aGUgJHAkLXZhbHVlcyB0byBiZSBjb3JyZWN0LCBvdXIgcGFyYW1ldHJpYyAoZGlzdHJpYnV0aW9uYWwpIGFzc3VtcHRpb25zIGFzIHdlbGwgYXMgdGhlIGluZGVwZW5kZW5jZSBhc3N1bXB0aW9uLCBtdXN0IGFsc28gaG9sZC4KIC0gSW4gYnVsayBSTkEtc2VxLCB3ZSBhcmUgb2Z0ZW4gd29ya2luZyB3aXRoIGEgbGltaXRlZCBudW1iZXIgb2Ygc2FtcGxlcyBhbmQgc28gd2UgdHlwaWNhbGx5IGRvIG5vdCBleHBlY3QgYXN5bXB0b3RpYyB0aGVvcnkgdG8gaG9sZCB5ZXQuIEluIHNpbmdsZS1jZWxsIFJOQS1zZXEsIHdlIG9mdGVuIHBlcmZvcm0gc2V2ZXJhbCBwcmVwcm9jZXNzaW5nIHN0ZXBzIGJlZm9yZSBjYWxjdWxhdGluZyAkcCQtdmFsdWVzIGZvciBlYWNoIGdlbmUgYW5kIHNvIHdlIG1heSBiZSAndXNpbmcgdGhlIGRhdGEgbXVsdGlwbGUgdGltZXMnLiBSYXRoZXIgdGhhbiBhdHRhY2hpbmcgc3Ryb25nIHByb2JhYmlsaXN0aWMgaW50ZXJwcmV0YXRpb25zIHRvIHRoZSAkcCQtdmFsdWVzLCB3ZSB0aGVyZWZvcmUgYWR2aWNlIHRvIHZpZXcgdGhlICRwJC12YWx1ZXMgc2ltcGx5IGFzIHVzZWZ1bCBudW1lcmljYWwgc3VtbWFyaWVzIGZvciByYW5raW5nIHRoZSBnZW5lcyBmb3IgZnVydGhlciBpbnNwZWN0aW9uIGluIGdlbm9taWNzIGFwcGxpY2F0aW9ucy4KIApgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmNhcD1wYXN0ZSgiIil9CmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvbGlrVGVzdHMucG5nIikKYGBgCgojIyMgV2FsZCB0ZXN0IGFuZCBsaWtlbGlob29kIHJhdGlvIHRlc3QgaW4gYFJgCgpMZXQncyB1c2UgYSBXYWxkIHRlc3QgYW5kIGEgbGlrZWxpaG9vZCByYXRpbyB0ZXN0IHRvIHRlc3Qgd2hldGhlciB0aGUgYXZlcmFnZSBudW1iZXIgb2YgYmlrZXJzIGRpZmZlcnMgYmV0d2VlbiBhIHdvcmtpbmcgZGF5IG9yIGEgd2Vla2VuZCBkYXksIHVzaW5nIGEgc2ltcGxlIEdMTSB3aXRoIG9ubHkgdGhhdCB2YXJpYWJsZSBhcyBhIGNvdmFyaWF0ZS4KVGhpcyBhbW91bnRzIHRvIHRlc3RpbmcKCiQkSF8wOiBcYmV0YV97d29ya2luZ2RheX0gPSAwJCQKJCRIXzE6IFxiZXRhX3t3b3JraW5nZGF5fSBcbmUgMCQkCgpgYGB7ciB3YWxkVGVzdH0KbVNpbXBsZSA8LSBnbG0oYmlrZXJzIH4gd29ya2luZ2RheSwgCiAgICAgICAgICAgICAgIGZhbWlseSA9ICJwb2lzc29uIiwgCiAgICAgICAgICAgICAgIGRhdGEgPSBCaWtlc2hhcmUpCnN1bW1TaW1wbGUgPC0gc3VtbWFyeShtU2ltcGxlKQpzdW1tU2ltcGxlJGNvZWZmaWNpZW50c1sid29ya2luZ2RheSIsXQojIFdhbGQgdGVzdCBtYW51YWxseQpXIDwtIHN1bW1TaW1wbGUkY29lZmZpY2llbnRzWyJ3b3JraW5nZGF5IiwgIkVzdGltYXRlIl0gLyBzdW1tU2ltcGxlJGNvZWZmaWNpZW50c1sid29ya2luZ2RheSIsICJTdGQuIEVycm9yIl0KcHZhbCA8LSAyKigxIC0gcG5vcm0oVykpClcKcHZhbAogCiMgV2FsZCB0ZXN0IHRocm91Z2ggYSBjb250cmFzdApDIDwtIG1hdHJpeCgwLCBucm93PTEsIG5jb2w9bGVuZ3RoKGNvZWYobVNpbXBsZSkpKQpjb2xuYW1lcyhDKSA8LSBuYW1lcyhjb2VmKG1TaW1wbGUpKQpDWywgIndvcmtpbmdkYXkiXSA8LSAxCkMKYmV0YSA8LSBtYXRyaXgoY29lZihtU2ltcGxlKSwgbmNvbD0xKQpTaWdtYSA8LSB2Y292KG1TaW1wbGUpCgpXMiA8LSBDICUqJSBiZXRhICUqJSBzb2x2ZShDICUqJSBTaWdtYSAlKiUgdChDKSkgJSolIHQoYmV0YSkgJSolIHQoQykKVzIKIyBub3RlIHRoaXMgYmVpbmcgZXF1YWwgdG8KV14yCnB2YWwgPC0gMS1wY2hpc3EoVzIsIGRmPTEpCnB2YWwKCiMgZmluYWxseSwgd2UgY2FuIGFsc28gcmVhZCB0aGUgV2FsZCB0ZXN0IHJlc3VsdCBmcm9tIHRoZSBzdW1tYXJ5IG9mIHRoZSBtb2RlbApzdW1tU2ltcGxlCmBgYAoKYGBge3IgbHJUZXN0fQptRnVsbCA8LSBnbG0oYmlrZXJzIH4gd29ya2luZ2RheSwgCiAgICAgICAgICAgICAgIGZhbWlseSA9ICJwb2lzc29uIiwgCiAgICAgICAgICAgICAgIGRhdGEgPSBCaWtlc2hhcmUpCgptUmVkdWNlZCA8LSBnbG0oYmlrZXJzIH4gMSwgCiAgICAgICAgICAgICAgIGZhbWlseSA9ICJwb2lzc29uIiwgCiAgICAgICAgICAgICAgIGRhdGEgPSBCaWtlc2hhcmUpCgojIG1hbnVhbCBMUlQKbGxGdWxsIDwtIGxvZ0xpayhtRnVsbCkKbGxSZWR1Y2VkIDwtIGxvZ0xpayhtUmVkdWNlZCkKCmxydCA8LSBhcy5udW1lcmljKDIgKiAobGxGdWxsIC0gbGxSZWR1Y2VkKSkKbHJ0CnB2YWwgPC0gMSAtIHBjaGlzcShscnQsIGRmPTEpCnB2YWwKCiMgdXNpbmcgYW5vdmEgZnVuY3Rpb24KYW5vdmEobVJlZHVjZWQsIG1GdWxsLCB0ZXN0ID0gIkNoaXNxIikgIyBub3RlIHRlc3Qgc3RhdGlzdGljIGlzIGlkZW50aWNhbApgYGAKCiMjIyBMaW5lYXJseSBkZXBlbmRlbnQgY29udHJhc3RzCgpCZWxvdywgd2Ugc2hvdyBob3cgb25lIG1heSBiZSBhYmxlIHRvIGRlcml2ZSBpbmRlcGVuZGVudCBjb250cmFzdHMgZnJvbSB0aGVpciBjb250cmFzdCBtYXRyaXggZm9yIHN0YXRpc3RpY2FsIGluZmVyZW5jZS4KV2UgYWxzbyBzaG93IHRoYXQgYXR0ZW1wdGluZyB0byB1c2UgbGluZWFybHkgZGVwZW5kZW50IGNvbnRyYXN0cyByZXN1bHRzIGluIGFuIGlzc3VlLCBzaW5jZSB0aGUgdmFyaWFuY2UtY292YXJpYW5jZSBtYXRyaXggb2Ygb3VyIGNvbnRyYXN0cyB3aWxsIGJlIHNpbmd1bGFyIGFuZCB0aGVyZWZvcmUgbm9uLWludmVydGlibGUuCgpgYGB7cn0KQyA8LSBtYXRyaXgoMCwgbnJvdz0zLCBuY29sPWxlbmd0aChjb2VmKG1TaW1wbGUpKSkKY29sbmFtZXMoQykgPC0gbmFtZXMoY29lZihtU2ltcGxlKSkKQ1sxLCAiKEludGVyY2VwdCkiXSA8LSAxCkNbMiwgIndvcmtpbmdkYXkiXSA8LSAxCkNbMyxjKCIoSW50ZXJjZXB0KSIsICJ3b3JraW5nZGF5IildIDwtIGMoMSwxKQpDCgojIGRvZXNuJ3Qgd29yayBiZWNhdXNlIHdlIGhhdmUgYSBzaW5ndWxhciBtYXRyaXgKVzIgPC0gdHJ5KHQoQyAlKiUgYmV0YSkgJSolIHNvbHZlKEMgJSolIFNpZ21hICUqJSB0KEMpKSAlKiUgQyAlKiUgYmV0YSkKVzIgIyBlcnJyb3JzCgojIGlkZW50aWZ5IHRoZSBsaW5lYXJseSBpbmRlcGVuZGVudCBjb250cmFzdHMKQ3QgPC0gdChDKQpDdEluZGVwIDwtIEN0WyxxcihDdCkkcGl2b3RbMTpxcihDdCkkcmFua11dCkNJbmRlcCA8LSB0KEN0SW5kZXApCgojIHRyeSBhZ2FpbgpXMiA8LSB0KENJbmRlcCAlKiUgYmV0YSkgJSolIHNvbHZlKENJbmRlcCAlKiUgU2lnbWEgJSolIHQoQ0luZGVwKSkgJSolIENJbmRlcCAlKiUgYmV0YQpXMgpgYGAKCiAKIyMgTW9kZWwgZGV2aWFuY2UsIHJlc2lkdWFscyBhbmQgZ29vZG5lc3Mtb2YtZml0CgogLSBJbiBsaW5lYXIgbW9kZWxzLCB3ZSBvZnRlbiB1c2UgcmVzaWR1YWxzICRlX2kgPSB5X2kgLSBcaGF0e1xtdX1faSQgdG8gY2hlY2sgbW9kZWwgYXNzdW1wdGlvbnMgKGxpbmVhcml0eSwgaG9tb3NjZWRhc3RpY2l0eSkuIEhvd2V2ZXIsIGluIGEgR0xNIHNldHRpbmcsIHdlIGtub3cgdGhhdCB0aGUgdmFyaWFuY2Ugb2Ygb3VyIHJlc2lkdWFscyB3aWxsIGRlcGVuZCBvbiB0aGUgbWVhbiwgaS5lLiwgJFZhcihcZXBzaWxvbl9pKSA9IGYoXG11X2kpJC4gVXNpbmcgb3JkaW5hcnkgcmVzaWR1YWxzIHN1Y2ggYXMgJGVfaSQgdGhlcmVmb3JlIGlzIG5vIGxvbmdlciBhcHByb3ByaWF0ZS4KIC0gV2UgaGF2ZSBzZWVuIHRoYXQgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiB0aGF0IGlzIHVzZWQgdG8gZml0IGEgR0xNIGlzIHRoZSBsb2ctbGlrZWxpaG9vZCBvZiB0aGUgZGF0YSB1bmRlciB0aGUgcG9zaXRlZCBtb2RlbC4gRm9yIGV4YW1wbGUsIHRoZSBsb2cgbGlrZWxpaG9vZCBvZiBhIFBvaXNzb24gR0xNIHdpdGggcmVzcG9uc2UgdmFyaWFibGUgJFxtYXRoYmZ7WX0kLCB3aXRoIGVsZW1lbnRzICRZX2ksIGkgXGluIFx7MSwgXGxkb3RzLCBuXH0kIGFuZCBtb2RlbCBtYXRyaXggJFxtYXRoYmZ7WH0kIGlzCiAkJCBcZWxsKFxtYXRoYmZ7WX07ICBcYmV0YSkgPSBcbG9nIFxwcm9kX3tpPTF9Xm4gXGxlZnQoIFxmcmFje1xleHAgKFxtYXRoYmZ7WH1faV5UIFxiZXRhKV57WV9pfSBcZXhwKCAtIFxleHAoXG1hdGhiZntYfV9pXlQgXGJldGEpKX17WV9pIX0gXHJpZ2h0KSA9IFxzdW1fe2k9MX1ebiBcbG9nIFxsZWZ0KCBcZnJhY3tcZXhwKFxtYXRoYmZ7WH1faV5UIFxiZXRhKV57WV9pfSBcZXhwKCAtIFxleHAoXG1hdGhiZntYfV9pXlQgXGJldGEpKX17WV9pIX0gXHJpZ2h0KSBcXCA9IFxzdW1fe2k9MX1ebiBZX2kgKFxtYXRoYmZ7WH1faV5UIFxiZXRhKSAtIFxleHAoXG1hdGhiZntYfV9pXlQgXGJldGEpICAtIFxsb2cgWV9pIS4gJCQKIFRoZSBlc3RpbWF0ZXMgJFxoYXR7XGJldGF9JCBhcmUgdGhlbiBmb3VuZCBieSBtYXhpbWl6aW5nICRcZWxsKFxiZXRhIHwgXG1hdGhiZntZfSwgXG1hdGhiZntYfSApJCB3aXRoIHJlc3BlY3QgdG8gJFxiZXRhJC4gVGhpcyBpcyBhbmFsb2dvdXMgdG8gbWF4aW1pemluZyBhIEdhdXNzaWFuIGxpa2VsaWhvb2QgaW4gdGhlIGxpbmVhciBtb2RlbCBzZXR0aW5nLgogLSBBIGdvb2RuZXNzLW9mLWZpdCBtZWFzdXJlIHVzZWQgaW4gdGhlIEdMTSBzZXR0aW5nIGlzIHRoZSAqKnJlc2lkdWFsIGRldmlhbmNlKiogJEQkIChzb21ldGltZXMgcmVmZXJyZWQgdG8gc2ltcGx5IGFzICdkZXZpYW5jZScpLCB0aGF0IGlzIHR3aWNlIHRoZSBkaWZmZXJlbmNlIGluIGxvZy1saWtlbGlob29kIGJldHdlZW4gYSAnc2F0dXJhdGVkIG1vZGVsJyBhbmQgdGhlIGN1cnJlbnQgbW9kZWwuIEhlcmUsIGEgc2F0dXJhdGVkIG1vZGVsLCBpcyBhIG1vZGVsIHdoZXJlIHdlIGZpdCBvbmUgcGFyYW1ldGVyIHBlciBkYXRhIHBvaW50IGFuZCB0aGVyZWZvcmUgZml0IHRoZSBkYXRhIHBlcmZlY3RseSwgaW4gb3RoZXIgd29yZHMgJFxoYXR7XG11fV9pID0geV9pJC4gVGhpcyBpcywKICQkIEQgPSAyICogXGxlZnRceyBcZWxsKFxtYXRoYmZ7WX07ICBcYmV0YSB8IFxoYXR7XG11fV9pID0geV9pKSAtIFxlbGwoXG1hdGhiZntZfTsgIFxiZXRhIHwgXGhhdHtcbXV9X2kgPSBcZXhwKFxtYXRoYmZ7WH1faV5UIFxiZXRhKSkgXHJpZ2h0XH0uJCQKIC0gRnJvbSB0aGUgZXF1YXRpb24gYWJvdmUgaXQgYmVjb21lcyBjbGVhciB0aGF0IHRoZSByZXNpZHVhbCBkZXZpYW5jZSBpcyBhY3R1YWxseSBhIHJhdGlvIGluIGxvZy1saWtlbGlob29kcyBhbmQgdGhlcmVmb3JlIGEgbGlrZWxpaG9vZCByYXRpbyB0ZXN0IHN0YXRpc3RpYyEKIC0gQSBsb3cgcmVzaWR1YWwgZGV2aWFuY2UgY2FuIHRodXMgYmUgaW50ZXJwcmV0ZWQgYXMgYSBtb2RlbCB0aGF0IGlzIGZpdHRpbmcgdGhlIGRhdGEgd2VsbCwgc2luY2UgeW91ciBjdXJyZW50IG1vZGVsIHdpbGwgYmUgY2xvc2UgaW4gbG9nLWxpa2VsaWhvb2QgdG8gdGhlIHNhdHVyYXRlZCBtb2RlbC4gVGhlIGRldmlhbmNlIGlzIGEgdmVyeSB1c2VmdWwgc3RhdGlzdGljIHRoYXQgaXMgYWxzbyBpbXBvcnRhbnQgaW4gc3RhdGlzdGljYWwgaW5mZXJlbmNlIGFuZCBtb2RlbCBzZWxlY3Rpb24sIGUuZy4sIGZvciB0ZXN0aW5nIGlmIGEgc21hbGxlciBtb2RlbCBmaXRzIHNpZ25pZmljYW50bHkgd29yc2UgdGhhbiBhIGxhcmdlciBtb2RlbC4KIC0gRmluYWxseSwgYSAqKmRldmlhbmNlIHJlc2lkdWFsKiogJERfaSQgY2FuIHRoZW4gYmUgZGVmaW5lZCBhcyB0aGUgc3F1YXJlIHJvb3Qgb2YgdGhlIGNvbnRyaWJ1dGlvbiBvZiB0aGUgJGkkdGggZGF0dW0gdG8gdGhlIHJlc2lkdWFsIGRldmlhbmNlCiAkJCBEX2kgPSBzaWduKFlfaSAtIFxleHAoXG1hdGhiZntYfV9pXlQgXGJldGEpKSBcc3FydHsgMiogXGxlZnRceyBcZWxsKFlfaTsgIFxiZXRhIHwgXGhhdHtcbXV9X2kgPSB5X2kpIC0gXGVsbChZX2k7ICBcYmV0YSB8IFxoYXR7XG11fV9pID0gXGV4cChcbWF0aGJme1h9X2leVCBcYmV0YSkpIFxyaWdodFx9IH0gJCQKIAogLS0tCiAKIC0gQW5vdGhlciB0eXBlIG9mIHJlc2lkdWFscyBjb21tb25seSB1c2VkIGluIGEgR0xNIHNldHRpbmcgYXJlICoqUGVhcnNvbiByZXNpZHVhbHMqKi4gQSBQZWFyc29uIHJlc2lkdWFsIGlzIGRlZmluZWQgYXMKICQkIGVfaSA9IFxmcmFje3lfaSAtIEUoeV9pKX17XHNxcnR7VmFyKHlfaSl9fSwkJAogYW5kIHdlIGNhbiBzZWUgdGhhdCBpdCBoYXMgdGhlIGZvcm0gb2YgYSByZWd1bGFyIHJlc2lkdWFsIHN1Y2ggYXMgdXNlZCBpbiBsaW5lciBtb2RlbHMgKG51bWVyYXRvciksIGJ1dCBub3JtYWxpemVkIGFjY29yZGluZyB0byB0aGUgdmFyaWFuY2Ugb2YgdGhlIG9ic2VydmVkIHJlc3BvbnNlIChkZW5vbWluYXRvciksIHRvIGNvcnJlY3QgZm9yIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4KIAogLS0tCiAKIC0gKipHb29kbmVzcy1vZi1maXQqKiAoR09GKSBhbmFseXNlcyBzZXJ2ZSB0byBhc3Nlc3MgaG93IHdlbGwgdGhlIG1vZGVsIGFjdHVhbGx5IGZpdHMgdGhlIG9ic2VydmVkIGRhdGEuIE9uZSBtYXkgdmlldyB0aGUgZml0dGluZyBvZiBhIEdMTSBhcyByZXBsYWNpbmcgYSBzZXQgb2Ygb2JzZXJ2ZWQgZGF0YSBwb2ludHMgJFxtYXRoYmZ7eX0kIGJ5IGEgc2V0IG9mIGZpdHRlZCB2YWx1ZXMgJFxoYXR7XG1hdGhiZntcbXV9fSQgZGVyaXZlZCBmcm9tIGEgbW9kZWwuIEluIGdlbmVyYWwgJFxoYXR7XG1hdGhiZntcbXV9fSBcbmUgXG1hdGhiZnt5fSQgYW5kIHRoZSBxdWVzdGlvbiBhcmlzZXMgYXMgdG8gaG93IHdlbGwgJFxoYXR7XG1hdGhiZntcbXV9fSQgYXBwcm94aW1hdGVzICRcbWF0aGJme3l9JC4gVGhpcyBuYXR1cmFsbHkgcmFpc2VzIHRoZSBxdWVzdGlvbiBvZiBob3cgbXVjaCBvZiBhIGRpc2NyZXBhbmN5IHdlIGJlbGlldmUgdG8gYmUgdG9sZXJhYmxlLiBUd28gaW1wb3J0YW50IGRpc2NyZXBhbmN5IG1lYXN1cmVzIGFyZSBvZnRlbiB1c2VkIGluIGEgR0xNIHNldHRpbmcuIAogLSBOb3RlIHRoYXQgdGhlICoqcmVzaWR1YWwgZGV2aWFuY2UqKiB3YXMgYSBsaWtlbGlob29kIHJhdGlvIHRlc3Qgc3RhdGlzdGljIGJldHdlZW4gYSBzYXR1cmF0ZWQgYW5kIG91ciBjdXJyZW50IG1vZGVsLiBUaGlzIHNhdHVyYXRlZCBtb2RlbCBhY3R1YWxseSBwcm92aWRlcyB1cyB3aXRoIGEgYmFzZWxpbmUgYXMgdG8gaG93IHdlbGwgYSBtb2RlbCBjYW4gZml0IHRoZSBvYnNlcnZlZCBkYXRhIChldmVuIGlmIHdlIGtub3cgdGhhdCB0aGUgc2F0dXJhdGVkIG1vZGVsIGlzIHVuaW5mb3JtYXRpdmUgZm9yIHN1bW1hcml6aW5nIHRoZSBkYXRhKS4gVGhpcyBtb3RpdmF0ZXMgYSBzdGF0aXN0aWNhbCB0ZXN0IHdpdGggIAogJEhfMCQ6IFRoZSBjdXJyZW50IG1vZGVsIHByb3ZpZGVzIGEgc2ltaWxhciBmaXQgYXMgdGhlIHNhdHVyYXRlZCBtb2RlbC4gIAogJEhfMSQ6IFRoZSBjdXJyZW50IG1vZGVsIGZpdHMgc2lnbmlmaWNhbnRseSB3b3JzZSB0aGFuIGEgc2F0dXJhdGVkIG1vZGVsLiAgCiAtIFRoZSByZXNpZHVhbCBkZXZpYW5jZSBpbW1lZGlhdGVseSB0ZXN0cyB0aGlzIGh5cG90aGVzaXMgdXNpbmcgYSBsaWtlbGlob29kIHJhdGlvIHRlc3QgYW5kIGlzIHRoZXJlZm9yZSBhIHVzZWZ1bCBnb29kbmVzcy1vZi1maXQgbWVhc3VyZS4KIC0gQW5vdGhlciBtZWFzdXJlIG9mIGRpc2NyZXBhbmN5IGlzIHRoZSBnZW5lcmFsaXplZCBQZWFyc29uICRcY2hpXjIkIHN0YXRpc3RpYwogJCQgWF4yID0gXHN1bV97aT0xfV5uIFxmcmFjeyh5X2kgLSBcaGF0e1xtdX1faSleMn17VmFyKHlfaSl9ID0gXHN1bV97aT0xfV5uIGVfaV4yLCQkCiB3aXRoICRlX2kkIHRoZSBQZWFyc29uIHJlc2lkdWFsIG9mIG9ic2VydmF0aW9uICRpJC4gCiAgLSBBc3ltcHRvdGljIHRoZW9yeSBzaG93cyB0aGF0IGJvdGggdGhlIHJlc2lkdWFsIGRldmlhbmNlICREIFxzaW0gXGNoaV4yX3tuLXB9IHwgSF8wJCBhbmQgJFheMiBcc2ltIFxjaGleMl97bi1wfSB8IEhfMCQsIHdpdGggJG4kIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIG91ciBkYXRhc2V0IChhbmQsIGhlbmNlLCB0aGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgZml0dGVkIGluIG91ciBzYXR1cmF0ZWQgbW9kZWwpLCBhbmQgJHAkIHRoZSBudW1iZXIgb2YgcGFyYW1ldGVycyBmaXR0ZWQgaW4gb3VyIGN1cnJlbnQgbW9kZWwuCiAKIC0tLQoKICoqRXhlcmNpc2UqKjoKIAogLSBWZXJpZnkgdGhlIHJlc2lkdWFsIGRldmlhbmNlIHRoYXQgaXMgcmVwb3J0ZWQgaW4gdGhlIHN1bW1hcnkgb2Ygb3VyIG1vZGVsIGFib3ZlLiAKIC0gQWxzbyBjaGVjayBpZiB5b3UgY2FuIHJlY292ZXIgdGhlIGNvcnJlY3QgZGV2aWFuY2UgYW5kIFBlYXJzb24gcmVzaWR1YWxzIGJ5IGNhbGN1bGF0aW5nIHRoZW0geW91cnNlbGYuIFlvdSBjYW4gZ2V0IHRoZSBjb3JyZWN0IGRldmlhbmNlIHJlc2lkdWFscyBpbiBgUmAgYnkgYHJlc2lkKG0sIHR5cGU9ImRldmlhbmNlIilgIGFuZCBgcmVzaWQobSwgdHlwZT0icGVhcnNvbiIpYC4KIC0gRG9lcyB5b3VyIG1vZGVsIGZpdCBzaWduaWZpY2FudGx5IHdvcnNlIHRoYW4gYSBzYXR1cmF0ZWQgbW9kZWw/CgojIyBPdmVyZGlzcGVyc2lvbgoKIC0gQWJvdmUgd2UgaGF2ZSBhbHdheXMgYXNzdW1lZCB0aGF0IHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiBpcyB2YWxpZCBmb3IgdGhlIGRhdGFzZXQgd2UgaGF2ZSBiZWVuIHVzaW5nLiBIb3dldmVyLCB3ZSBuZXZlciBjaGVja2VkIGZvciB0aGlzLgogLSBBcyBhIG1hdHRlciBvZiBmYWN0LCBpdCBvZnRlbiBoYXBwZW5zIHRoYXQgdGhlIHZhcmlhbmNlPW1lYW4gYXNzdW1wdGlvbiBpcyB0b28gc3RyaW5nZW50IGZvciBjb3VudCBkYXRhLiBJZiB0aGUgdmFyaWFuY2UgaXMgbGFyZ2VyIHRoYW4gdGhlIG1lYW4sIHRoaXMgaXMgcmVmZXJyZWQgdG8gYXMgKipvdmVyZGlzcGVyc2lvbioqLiBUaG91Z2ggbXVjaCBsZXNzIGNvbW1vbiwgdW5kZXJkaXNwZXJzaW9uIGhhcHBlbnMgd2hlbiB0aGUgdmFyaWFuY2UgaXMgc21hbGxlciB0aGFuIHRoZSBtZWFuLgogLSBXZSBjYW4gdXNlIFBlYXJzb24gcmVzaWR1YWxzIHRvIG1lYXN1cmUgb3ZlcmRpc3BlcnNpb24gdXNpbmcgdGhlIGZvbGxvd2luZyBhcmd1bWVudC4gVGhlIFBvaXNzb24gR0xNIGltcGxpZXMgdGhhdAogJCQgWV9pIHwgWCwgXGhhdHtcYmV0YX0gXHNpbSBQb2koXGhhdHtcbXV9X2kpLCQkCiB3aXRoICRcaGF0e1xtdX1faSA9IFxleHAgKFxtYXRoYmZ7WH1faV5UIFxoYXR7XGJldGF9KSQuIFRoaXMgaW1wbGllcwogJCQgVmFyKFlfaSB8IFgsIFxoYXR7XGJldGF9KSA9IFxoYXR7XG11fV9pLiQkCiBTaW5jZSB0aGUgdmFyaWFuY2UgaXMgdW5hZmZlY3RlZCBieSBhZGRpdGlvbiB3ZSBtYXkgYWxzbyB3cml0ZQogJCQgVmFyKFlfaSAtIFxoYXR7XG11fV9pIHwgWCwgXGhhdHtcYmV0YX0pID0gXGhhdHtcbXV9X2kuJCQKIFdoaWNoIGlzIGFsc28gZXF1YWwgdG8KICQkIFZhclxsZWZ0KFxmcmFje1lfaSAtIFxoYXR7XG11fV9pfXtcc3FydHtWYXIoWV9pKX19IHwgWCwgXGhhdHtcYmV0YX0gXHJpZ2h0KSA9IFxmcmFje1xoYXR7XG11fV9pfXtWYXIoWV9pKX0uJCQKIFNpbmNlIHdlIGtub3cgZnJvbSB0aGUgUG9pc3NvbiBkaXN0cmlidXRpb24gdGhhdCAkVmFyKFlfaSkgPSBcaGF0e1xtdX1faSQsIHdlIGhhdmUgdGhhdCAKICQkIFZhciBcbGVmdChcZnJhY3tZX2kgLSBcaGF0e1xtdX1faX17XHNxcnR7XGhhdHtcbXV9X2l9fSB8IFgsIFxoYXR7XGJldGF9IFxyaWdodCkgPSBcZnJhY3tcaGF0e1xtdX1faX17XGhhdHtcbXV9X2l9LiQkCiBOb3RlIHRoYXQgdGhlIGZvcm11bGF0aW9uIHdpdGhpbiB0aGUgdmFyaWFuY2UgYXQgbGVmdC1oYW5kIHNpZGUgb2YgdGhlIGVxdWF0aW9uIGlzIG91ciBkZWZpbml0aW9uIG9mIFBlYXJzb24gcmVzaWR1YWxzICRFX2kkLiBUaHVzLCBpZiB0aGUgUG9pc3NvbiBhc3N1bXB0aW9uIGhvbGRzLCB3ZSBjYW4gd3JpdGUKICQkIFZhcihFX2kgfCBYLCBcaGF0e1xiZXRhfSkgPSAxLCQkCiB3aGljaCBpcyBzb21ldGhpbmcgd2UgY2FuIGVtcGlyaWNhbGx5IHRlc3QgdXNpbmcgb3VyIGZpdHRlZCBtb2RlbC4gSW5kZWVkLCAqKmlmIHRoZSB2YXJpYW5jZSBvZiBvdXIgUGVhcnNvbiByZXNpZHVhbHMgaXMgbXVjaCBsYXJnZXIgdGhhbiAkMSQsIHdlIGFyZSBkZWFsaW5nIHdpdGggb3ZlcmRpc3BlcnNpb24qKi4gQXMgYSByb3VnaCBydWxlLCBJIGNvbnNpZGVyIG92ZXJkaXNwZXJzaW9uIHRvIGJlIHByZXNlbnQgaWYgdGhpcyB2YWx1ZSBpcyBsYXJnZXIgdGhhbiAkXHNpbSAxLjMkLCBidXQgdGhpcyBpcyBhcmJpdHJhcnkgYW5kIG1heSBkZXBlbmQgb24gdGhlIHNpdHVhdGlvbiAoYW5kIHN0YXRpc3RpY2lhbikuIAogCiAtLS0KIAogLSBCZWxvdywgd2UgYXBwbHkgdGhpcyB0byB0aGUgYEJpa2VzaGFyZWAgZGF0YXNldC4gV2Ugd2lsbCBub3RpY2UgdGhhdCB0aGUgb3ZlcmRpc3BlcnNpb24gaXMgaHVnZSEgVGhlIHAtdmFsdWVzIGFuZCBzdGFuZGFyZCBlcnJvcnMgcHJvdmlkZWQgYnkgdGhlIG1vZGVsIGNhbiB0aGVyZWZvcmUgbm90IGJlIHRydXN0ZWQhCgpgYGB7cn0KZVBlYXJzb24gPC0gcmVzaWQobSwgdHlwZT0icGVhcnNvbiIpCm4gPC0gbnJvdyhCaWtlc2hhcmUpCnAgPC0gbGVuZ3RoKGNvZWYobSkpCnZhclBlYXJzb24gPC0gc3VtKChlUGVhcnNvbl4yKSkgLyAobiAtIHApCnZhclBlYXJzb24gIyBIVUdFIQpgYGAKCiMjIyBSZW1lZGllcyB0byBvdmVyZGlzcGVyc2lvbgoKLSBUaGUgcHJlc2VuY2Ugb2Ygb3ZlcmRpc3BlcnNpb24gdGVsbHMgdXMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uYWwgYXNzdW1wdGlvbiB3ZSBoYXZlIGJlZW4gbWFraW5nIGRvZXMgbm90IGhvbGQuIE92ZXJkaXNwZXJzaW9uIGlzIGEgY29tbW9uIHByb2JsZW0sIGFuZCBsdWNraWx5IHdlIGhhdmUgYSBmZXcgYXZhaWxhYmxlIHJlbWVkaWVzLCBhcyBpbiBhbHRlcm5hdGl2ZSBkaXN0cmlidXRpb25zLCBhbHRob3VnaCBjaG9vc2luZyBiZXR3ZWVuIHRoZW0gbWF5IG5vdCBhbHdheXMgYmUgdHJpdmFsLgogIC0gVGhlICoqbmVnYXRpdmUgYmlub21pYWwqKiAoTkIpIGRpc3RyaWJ1dGlvbiBpcyBhIHBvcHVsYXIgY2hvaWNlIGZvciBtb2RlbGluZyBkYXRhIHRoYXQgYXJlIG92ZXJkaXNwZXJzZWQgd2l0aCByZXNwZWN0IHRvIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbi4gVGhlIE5CIGNhbiBiZSBjb25zaWRlcmVkIGFzIGEgbWVtYmVyIG9mIHRoZSBleHBvbmVudGlhbCBmYW1pbHkgYW5kIHRoZXJlZm9yZSBmaXR0ZWQgdXNpbmcgc3RhbmRhcmQgR0xNIGZpdHRpbmcgZW5naW5lcy4gSnVzdCBsaWtlIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiwgaXQgaXMgYSBkaXN0cmlidXRpb24gb25seSBhcHByb3ByaWF0ZSBmb3IgbW9kZWxpbmcgY291bnQgZGF0YS4gSWYKICAkJCBZX2kgXHNpbSBOQihcbXVfaSwgXHBoaSksICQkCiAgdGhlbiAkRShZX2kpID0gXG11X2kkIGFuZCAkVmFyKFlfaSkgPSBcbXVfaSArIFxwaGkgXG11X2leMiQsIHdpdGggJFxwaGkgXGdlIDAkIHRoZSAqKmRpc3BlcnNpb24gcGFyYW1ldGVyKiouIFNpbmNlICRccGhpIFxnZSAwJCB0aGUgdmFyaWFuY2Ugb2YgdGhlIG5lZ2F0aXZlIGJpbm9taWFsIGlzIGFsd2F5cyBsYXJnZXIgdGhhbiB0aGF0IG9mIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiwgYW5kIGluIGZhY3QgaXMgbm93IGEgcXVhZHJhdGljIChyYXRoZXIgdGhhbiBsaW5lYXIpIGZ1bmN0aW9uIG9mIHRoZSBtZWFuLiBXaGVuICRccGhpID0gMCQsIHRoZSBOQiByZWR1Y2VzIHRvIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbi4KICAtIFRoZSAqKnF1YXNpLVBvaXNzb24qKiBtb2RlbCBpcyBhbiBhbHRlcm5hdGl2ZSBjaG9pY2UgZGVyaXZlZCB1c2luZyB0aGUgcXVhc2ktbGlrZWxpaG9vZCBmcmFtZXdvcmsgZGV2ZWxvcGVkIGJ5IFtXZWRkZXJidXJuICgxOTc0KV0oaHR0cHM6Ly93d3cuanN0b3Iub3JnL3N0YWJsZS8yMzM0NzI1P3NlcT0xI21ldGFkYXRhX2luZm9fdGFiX2NvbnRlbnRzKS4gSG93ZXZlciwgb25seSB0aGUgZmlyc3QgdHdvIG1vbWVudHMgKG1lYW4gYW5kIHZhcmlhbmNlKSBhcmUgc3BlY2lmaWVkLCBhbmQgYWxsIG90aGVyIG1vbWVudHMgYXJlIGxlZnQgdW5zcGVjaWZpZWQuIEluIHBhcnRpY3VsYXIgaWYgbW9kZWwgJFlfaSQgdXNpbmcgYSBxdWFzaS1Qb2lzc29uIG1vZGVsLCB0aGVuICRFKFlfaSkgPSBcbXVfaSQgYW5kICRWYXIoWV9pKSA9IFxwaGkgXG11X2kkLCB3aXRoICRccGhpIFxnZSAwJCB0aGUgKioocXVhc2ktKWRpc3BlcnNpb24gcGFyYW1ldGVyKiouIEFnYWluLCBzaW5jZSAkXHBoaSBcZ2UgMCQgdGhlIHZhcmlhbmNlIG9mIHRoZSBxdWFzaS1Qb2lzc29uIGlzIGFsd2F5cyBsYXJnZXIgdGhhbiB0aGF0IG9mIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiwgaG93ZXZlciwgdGhlIGRpc3BlcnNpb24gcGFyYW1ldGVyIGhlcmUgaXMgb24gdGhlIGxpbmVhciBzY2FsZSwgYW5kIHNvIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcCBpcyBzdGlsbCBsaW5lYXIgYXMgb3Bwb3NlZCB0byBxdWFkcmF0aWMgaW4gdGhlIE5CLgogIAogLS0tCiAKIEJlbG93LCB3ZSBmaXQgYSBuZWdhdGl2ZSBiaW5vbWlhbCBhbmQgcXVhc2ktUG9pc3NvbiBtb2RlbCBpbiBgUmAuCgpgYGB7cn0KIyMgbmVnYXRpdmUgYmlub21pYWwKbGlicmFyeShNQVNTKQptTkIgPC0gZ2xtLm5iKGJpa2VycyB+IHdlYXRoZXJzaXQgKyBodW1jICsgSShodW1jXjIpICsgSShodW1jXjMpICsgaHIsCiAgICAgICAgIGRhdGEgPSBCaWtlc2hhcmUpCnN1bW1hcnkobU5CKQptZWFuKHJlc2lkKG1OQiwgdHlwZSA9ICJwZWFyc29uIileMikgIyBubyBtb3JlIG92ZXJkaXNwZXJzaW9uIQoKIyMgcXVhc2ktUG9pc3NvbgptUVAgPC0gZ2xtKGJpa2VycyB+IHdlYXRoZXJzaXQgKyBodW1jICsgSShodW1jXjIpICsgSShodW1jXjMpICsgaHIsCiAgICAgICAgIGRhdGEgPSBCaWtlc2hhcmUsCiAgICAgICAgIGZhbWlseT0icXVhc2lwb2lzc29uIikKIyBub3RlIHRoZSBkaXNwZXJzaW9uIHBhcmFtZXRlciBiZWluZyBlc3RpbWF0ZWQgaXMgZXF1YWwgdG8gb3VyIG92ZXJkaXNwZXJzaW9uIGRpYWdub3N0aWMgbWVhc3VyZS4gCiMgSW5kZWVkLCB0aGlzIGlzIHRoZSB3YXkgdGhlIGRpc3BlcnNpb24gcGFyYW1ldGVyIGlzIGVzdGltYXRlZCBmb3IgdGhlIFFQISEKc3VtbWFyeShtUVApIApgYGAKCgogLSBbdmVyIEhvZWYgKmV0IGFsLiogKDIwMDcpXShodHRwczovL2RpZ2l0YWxjb21tb25zLnVubC5lZHUvY2dpL3ZpZXdjb250ZW50LmNnaT9hcnRpY2xlPTExNDEmY29udGV4dD11c2RlcHRjb21tZXJjZXB1YikgcHJvdmlkZSBhIHVzZWZ1bCBkaWFnbm9zdGljIHRvIGVtcGlyaWNhbGx5IGNoZWNrIHdoZXRoZXIgeW91ciBkYXRhIGlzIG1vcmUgYW1lbmFibGUgdG8gYSBxdWFzaS1Qb2lzc29uIHZlcnN1cyBhIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbi4KCiMgQSBmaW5hbCBub3RlCgpJbiB0aGlzIGxlY3R1cmUsIHdlIGhhdmUgaW50cm9kdWNlZCBHTE1zIHRvIG1vZGVsIGRhdGEgdGhhdCBjYW4gYmUgYXNzdW1lZCB0byBmb2xsb3cgYSBkaXN0cmlidXRpb24gYmVsb25naW5nIHRvIHRoZSBleHBvbmVudGlhbCBmYW1pbHkuCldlIGZvY3Vzc2VkIG9uIGVzdGltYXRpb24sIGludGVycHJldGF0aW9uLCBzdGF0aXN0aWNhbCBpbmZlcmVuY2UgYW5kIHNvbWUgbW9kZWwgZ29vZG5lc3Mtb2YtZml0IGRpYWdub3N0aWNzLiAKV2UgZGlkIG5vdCBjb25zaWRlciBpbXBvcnRhbnQgdG9waWNzIGxpa2UgbW9kZWwgc2VsZWN0aW9uLCBvciBldmVuIHdoZXRoZXIgYSBHTE0gaXMgYXBwcm9wcmlhdGUgZm9yIHlvdXIgZGF0YXNldCEgVGhlc2UgYXJlIG90aGVyIGltcG9ydGFudCB0b3BpY3Mgd2hpY2gsIHVuZm9ydHVuYXRlbHksIHdlIGRvIG5vdCBoYXZlIHRoZSBiYW5kd2lkdGggZm9yIHRvIGluY2x1ZGUgaW4gdGhpcyBjb3Vyc2UuClRoZXJlZm9yZSwgYSBmaW5hbCBub3RlIHRocm91Z2ggYW4gW1hLQ0QgY29taWNdKGh0dHBzOi8veGtjZC5jb20vKSwgYWZ0ZXIgZmluaXNoaW5nIHRoaXMgbG9uZyBjaGFwdGVyIQoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5jYXA9cGFzdGUoIiIpfQojIEFsbCBkZWZhdWx0cwppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL3hrY2QtMTcyNS1saW5lYXJfcmVncmVzc2lvbl8yeC5wbmciKQpgYGAKCgojIFJlZmVyZW5jZXMKCiAtIFtNYXJpb25pICpldCBhbC4qICgyMDA4KV0oaHR0cHM6Ly9nZW5vbWUuY3NobHAub3JnL2NvbnRlbnQvMTgvOS8xNTA5KSBkZXNjcmliZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGdlbmUgZXhwcmVzc2lvbiBjb3VudHMgYWNyb3NzIHRlY2huaWNhbCByZXBsaWNhdGVzLCBhbmQgaW4gYWRkaXRpb24gZGlzY3VzcyBsYW5lIGVmZmVjdHMgaW4gUk5BLXNlcSwgYXMgd2VsbCBhcyBhIGNvbXBhcmlzb24gYmV0d2VlbiBSTkEtc2VxIGFuZCBhcnJheS1iYXNlZCBwbGF0Zm9ybXMuCiAtIFtXZWRkZXJidXJuICgxOTc0KV0oaHR0cHM6Ly93d3cuanN0b3Iub3JnL3N0YWJsZS8yMzM0NzI1P3NlcT0xI21ldGFkYXRhX2luZm9fdGFiX2NvbnRlbnRzKSBpbnRyb2R1Y2VkIHRoZSAoZXh0ZW5kZWQpIHF1YXNpLWxpa2VsaWhvb2QgZnJhbWV3b3JrLgogLSBUaGUgY291bnQgZGF0YSBjaGFwdGVyIG9mIE1vZGVybiBTdGF0aXN0aWNzIGZvciBNb2Rlcm4gQmlvbG9neSBieSBXb2xmZ2FuZyBIdWJlciBhbmQgU3VzYW4gSG9sbWVzIGhhbmRsZXMgc2ltaWxhciB0b3BpY3MgYWxzbyBpbiB0aGUgY29udGV4dCBvZiBSTkEtc2VxOiBodHRwczovL3d3dy5odWJlci5lbWJsLmRlL21zbWIvQ2hhcC1Db3VudERhdGEuaHRtbAo=