1 Introduction

This tutorial uses rstanarm version 2.21.1 and requires the following R packages.

knitr::opts_chunk$set(echo = TRUE, comment = "", cache = T)
# Required Packages
# install.packages("skimr") # Or install Skimr package
library(bookdown)
library(skimr)
library(mlmRev)
library(lme4)
library(lmtest)
library(rstanarm)
library(ggplot2)
library(dplyr)
library(kableExtra)

1.1 Data example

We will be analyzing the guImmun dataset (Pebley et al., 1996) from the mlmRev package in R. The data include 2159 children (1-4 years of age) who had received some immunization. These 2159 children came from 1595 families living in 161 communities, with an average of 1.4 children per family and 13.4 families per community. The guImmun dataset consists of the following 13 variables:

  • kid: a factor identifying the child.
  • mom: a factor identifying the family.
  • comm: a factor identifying the community.
  • immun: a factor indicating whether the child had received a complete set of immunizations. All children in this data frame had received at least one immunization.
  • kid2p: a factor indicating whether the child was 2 years of age or older at the time of the survey.
  • mom25p: a factor indicating whether the mother was 25 years of age or older.
  • ord: a factor indicating the child’s birth’s order within the family (01 - first child, 23 - second or third child, 46 - fourth to sixth child and 7p - seventh or later child).
  • ethn: a factor indicating the mother’s ethnicity. (L - Latino, N - indigenous not speaking Spanish and S - indigenous speaking Spanish).
  • momEd: a factor describing the mother’s level of education. (N - not finished primary, P - finished primary and S - finished secondary).
  • husEd: a factor describing the husband’s level of education. (Levels are the same as those for momEd plus U - unknown).
  • momWork: a factor indicating whether the mother had ever worked outside the home.
  • rural: a factor indicating whether the community’s location is considered rural or urban.
  • pcInd81: the percentage of indigenous population in the community according to the 1981 census.
# Use example dataset from mlmRev package: Immunization in Guatemala
data(guImmun, package = "mlmRev")
str(guImmun)
'data.frame':   2159 obs. of  13 variables:
 $ kid    : Factor w/ 2159 levels "2","269","272",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ mom    : Factor w/ 1595 levels "2","185","186",..: 1 2 3 4 5 5 6 7 7 8 ...
 $ comm   : Factor w/ 161 levels "1","36","38",..: 1 2 2 2 2 2 2 2 2 2 ...
 $ immun  : Factor w/ 2 levels "N","Y": 2 1 1 1 1 2 2 2 2 2 ...
 $ kid2p  : Factor w/ 2 levels "N","Y": 2 2 2 2 2 2 2 1 2 2 ...
 $ mom25p : Factor w/ 2 levels "N","Y": 1 1 1 1 2 1 1 2 2 2 ...
 $ ord    : Factor w/ 4 levels "01","23","46",..: 1 2 1 2 3 2 2 4 3 3 ...
 $ ethn   : Factor w/ 3 levels "L","N","S": 1 1 1 1 1 1 1 1 1 1 ...
 $ momEd  : Factor w/ 3 levels "N","P","S": 3 2 2 2 2 2 3 2 2 2 ...
 $ husEd  : Factor w/ 4 levels "N","P","S","U": 3 2 3 2 4 4 2 2 2 3 ...
 $ momWork: Factor w/ 2 levels "N","Y": 1 2 2 2 2 2 2 2 2 2 ...
 $ rural  : Factor w/ 2 levels "N","Y": 1 1 1 1 1 1 1 1 1 1 ...
 $ pcInd81: num  0.1075 0.0437 0.0437 0.0437 0.0437 ...

In this example, we are going to model the probability that these 2159 children had received a full set of immunizations immun as a function of individual characteristics such as kid2p, mom25p, and ord, family characteristics such as ethn, momEd, husEd, and momWork, and community characteristics such as rural and pcInd81.

# Count unique communities, families, and kids
K <- length(unique(guImmun$comm))
J <- length(unique(guImmun$mom))
I <- length(unique(guImmun$kid))
kids moms communities
2159 1595 161

The content of this tutorial is as follows.

  1. In section 2 we introduce Model 1, a random-intercept logistic regression with level-1 (child-level) predictors.

    We use the glmer() function from the lme4 package to estimate the model parameters by maximum likelihood (ML) using adaptive quadrature. Then we use the ranef() function from the lme4 package to assign values to the random intercepts for individual families by employing empirical Bayes (EB). Specifically, the predictions are the modes of the conditional posterior densities of the random intercepts, conditional on the parameters being equal to the MLEs.

  2. In section 3, we employ a fully Bayesian (FB) approach to obtain estimates (posterior means and medians) and associated measures of uncertainty for all parameters, including the random intercepts, for Model 1. We compare ML estimates of model parameters and EB predictions of random intercepts with their FB counterparts.

  3. In section 4, we extend Model 1 to include not just level-1 covariates but also level-2 covariates, as well as cross-level interactions (Model 2), We fit Model 2 using both the rstanarm package and the lme4 package. In addition, we show how to access a library of plotting functions for estimates from the rstanarm package.

  4. In section 5, we fit a three-level random-intercept model (Model 3) with level-1, level-2, and level-3 predictors using both packages.

  5. In section 6, we fit our last model (Model 4), which is a three-level random-coefficient model, using both packages.

To address potential confusion, we clarify how to use the terms appropriately for the different approaches.

  • Remark 1: Estimation vs. Prediction. Although both frequentists and Bayesians use the term “estimation” when they estimate model parameters, frequentists do not say “estimation” when assigning values to random effects because they do not view random effects as model parameters. They instead use the term “prediction”.

  • Remark 2: Whether model parameters include random intercepts. Bayesians treat \(\zeta_j\) as random parameters, whereas frequentists treat \(\zeta_j\) as random variables, since frenquentists do not accept “random” parameters. Therefore, frequentists call them latent variables or random effects. For Bayesians, both regression coefficients \(\beta\)s and random intercepts \(\zeta_j\) are parameters, but an important distinction between them is that the intercepts \(\zeta_{j}\) vary between clusters and are therefore sometimes referred to as varying intercepts.


2 Likelihood inference using glmer()

In this section, we start with a two-level random-intercept model with level-1 predictors (Model 1).

2.1 Model 1: Two-level random intercept model with level-1 predictors

2.1.1 Model specification

In the first model, we include a family-specific random intercept \(\zeta_j\) in the linear predictor to obtain a random-intercept logistic regression model. The random effects represent unobserved heterogeneity between families (level-2) and induce dependence between children within families (level-1). The model can be written as: \[\textrm{logit}\{Pr(immun_{ij}=1|\boldsymbol{x}_{ij}, \zeta_j)\} = \beta_1 + \beta_2 kid2p_{ij} + \beta_3 mom25p_{ij} +\beta_4 ord23_{ij} +\beta_5 ord46_{ij} +\beta_6 ord7p_{ij}+ \zeta_j \] where the random intercepts \(\zeta_j|\boldsymbol{x}_{ij} \sim N(0, \psi)\) are assumed to be independent of each other, identically distributed across families \(j\), and independent of the covariates \(\boldsymbol{x}_{ij}\).

This is an example of a generalized linear mixed model (GLMM) because it is a generalized linear model with mixed (fixed and random) effects, i.e., fixed effects \(\beta_1\) to \(\beta_6\) and a random effect \(\zeta_j\).

Using the latent-response formulation, the model can equivalently be written as \[ y^*_{ij} = \beta_1 + \beta_2 kid2p_{ij} + \beta_3 mom25p_{ij} +\beta_4 ord23_{ij} +\beta_5 ord46_{ij} +\beta_6 ord7p_{ij}+ \zeta_j +\epsilon_{ij}\] where \(\zeta_j \sim N(0, \psi)\) and the \(\epsilon_{ij}\) follow standard logistic distributions. The binary responses \(y_{ij}\) are determined by the latent continuous responses via the threshold model \[ y_{ij} = \left\{ \begin{array}\\ 1 & \mbox{if } \ y^*_{ij} > 0 \\ 0 & \mbox{otherwise} \\ \end{array} \right. \]

In both formulations of the model (via a logit link or in terms of a latent response), it is assumed that the random intercepts are independent across families and independent of the covariates of children \(i\). It is also assumed that the covariates of other children do not affect the response probability of a given child, conditional on the child’s own covariates and the random intercepts (known as the strict exogeneity conditional on the random intercepts). For the latent response formulation, the \(\epsilon_{ij}\) are assumed to be independent across both children and families and independent of both \(\zeta_j\) and \(x_{ij}\).

Below we use the glmer() function to estimate a random-intercept logistic regression model with kid2p (Yes or No), mom25p (Yes or No), the three ord dummy variables (with level ‘01’ as the reference group) as child-level categorical covariates, and a random intercept for mom, a factor identifying the family.

The syntax for glmer() is similar to that of lmer() for linear models. That is, the glmer() function with family = gaussian(link = "identity") is equivalent to lmer(). Our random-intercept logistic regression models can be fitted using glmer() by specifying a logit link and a binomial distribution with the family = binomial("logit") option. The parameters are estimated by approximate maximum likelihood, based on an adaptive Gaussian Hermite quadrature approximation of the likelihood. We specify the number of integration points with the nAGQ=n option. By choosing 30 integration points, we obtain an more accurate approximation of the log-likelihood than the default Laplace approximation, which is equivalent to nAGQ=1. The default Laplace approximation is one of the fastest computational methods, but its accuracy is low. We can solve the problem by increasing the number of quadrature points (i.e., the adaptive Gaussian Hermite quadrature approximation). From the next R chunk, we can see the difference in point estimates of model parameters between the model estimated using the adaptive Gaussian Hermite quadrature approximation and the model estimated using the Laplace approximation is large. However, for models with more than one random effect, the lme4 package supports only the Laplace approximation, which may perform poorly.

To solve this limitation, the GLMMadaptive package can be employed to fit models with multiple random effects (e.g., random intercepts, random slopes, and random quadratic slopes). However, this package can only fit generalized linear mixed models for a single grouping factor. In other words, although this package implements MLE with adaptive Gaussian quadrature, it can only do so for two-level models. In this tutorial, we fit a three-level, random-coefficient logistic regression model in section 6 using the rstanarm package, which solves this problem.

# Adaptive Gauss-Hermite Quadrature, nAGQ = 30
M1 <- glmer(immun ~ kid2p + mom25p + ord + (1 | mom),  
            family = binomial("logit"), 
            data = guImmun, control = glmerControl(optimizer = "bobyqa"),
            nAGQ = 30)

# Laplace approximation
M1_La <- glmer(immun ~ kid2p + mom25p + ord + (1 | mom),  
            family = binomial("logit"), 
            data = guImmun, control = glmerControl(optimizer = "bobyqa"),
            nAGQ = 1)
# Comparing parameter estimates between the model estimated by using adaptive Gaussian Hermite quadrature approximation and the model estimated by Laplacian approximation of the likelihood.
summary(M1_La, corr = FALSE)
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: immun ~ kid2p + mom25p + ord + (1 | mom)
   Data: guImmun
Control: glmerControl(optimizer = "bobyqa")

     AIC      BIC   logLik deviance df.resid 
  2855.4   2895.2  -1420.7   2841.4     2152 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.3462 -0.7192 -0.3725  0.7657  2.0377 

Random effects:
 Groups Name        Variance Std.Dev.
 mom    (Intercept) 1.597    1.264   
Number of obs: 2159, groups:  mom, 1595

Fixed effects:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept) -1.11563    0.18853  -5.917 3.27e-09 ***
kid2pY       1.21701    0.15426   7.889 3.03e-15 ***
mom25pY     -0.05152    0.15967  -0.323    0.747    
ord23       -0.16469    0.16986  -0.970    0.332    
ord46       -0.10636    0.20440  -0.520    0.603    
ord7p       -0.11084    0.25284  -0.438    0.661    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(M1, corr = FALSE)
Generalized linear mixed model fit by maximum likelihood (Adaptive
  Gauss-Hermite Quadrature, nAGQ = 30) [glmerMod]
 Family: binomial  ( logit )
Formula: immun ~ kid2p + mom25p + ord + (1 | mom)
   Data: guImmun
Control: glmerControl(optimizer = "bobyqa")

     AIC      BIC   logLik deviance df.resid 
  2777.3   2817.0  -1381.6   2763.3     2152 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.7427 -0.4876 -0.2043  0.5166  2.5810 

Random effects:
 Groups Name        Variance Std.Dev.
 mom    (Intercept) 6.836    2.615   
Number of obs: 2159, groups:  mom, 1595

Fixed effects:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -1.4937     0.2708  -5.517 3.45e-08 ***
kid2pY        1.6845     0.2168   7.770 7.86e-15 ***
mom25pY      -0.1298     0.2329  -0.557    0.577    
ord23        -0.3140     0.2354  -1.334    0.182    
ord46        -0.1798     0.2935  -0.612    0.540    
ord7p        -0.1017     0.3664  -0.278    0.781    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

The summary() function displays the estimated fixed effects and associated standard errors. We apply this function with models M1_La and M1 to compare estimates and associated standard errors from the Laplace approximation and the adaptive Gaussian Hermite quadrature approximation. Comparisons show that the fixed effect estimates from M1 are closer to one (with smaller standard errors) than M1_La.

We then use the print() function, which gives a more concise summary of the parameters than the summary() function.

print(M1, corr = FALSE)
Generalized linear mixed model fit by maximum likelihood (Adaptive
  Gauss-Hermite Quadrature, nAGQ = 30) [glmerMod]
 Family: binomial  ( logit )
Formula: immun ~ kid2p + mom25p + ord + (1 | mom)
   Data: guImmun
      AIC       BIC    logLik  deviance  df.resid 
 2777.284  2817.026 -1381.642  2763.284      2152 
Random effects:
 Groups Name        Std.Dev.
 mom    (Intercept) 2.615   
Number of obs: 2159, groups:  mom, 1595
Fixed Effects:
(Intercept)       kid2pY      mom25pY        ord23        ord46        ord7p  
    -1.4937       1.6845      -0.1298      -0.3140      -0.1798      -0.1017  

From the output:

  • Under the Fixed Effects part of the M1 output, we obtain the estimated regression coefficients in the usual format. To interpret the point estimates of the fixed part, we can say that the log odds of receiving the full set of immunizations for first-born children who were less than 2 years old with a mother who was less than 25 years old is estimated as \(\hat{\beta_1}=-1.494\). \(\hat{\beta_2}=1.68\) indicates that the estimated conditional log odds for a given family are higher if the children were 2 years of age or older, after controlling for all other covariates. Instead of considering changes in log odds, we can obtain the exponentiated regression coefficients to make the results more interpretable:
# display odds ratios
exp(fixef(M1))
(Intercept)      kid2pY     mom25pY       ord23       ord46       ord7p 
  0.2245292   5.3900043   0.8782654   0.7305449   0.8354437   0.9033166 
  • For the Random Effects of the model output, the estimated standard deviation of the random intercept \(\sqrt{\hat{\psi}}\) is 2.615.

2.1.2 Assigning values to random effects

In this section, we want to assign values to the random intercepts \(\zeta_j\) for individual families \(j\). By using the ranef() function, we can obtain the empirical Bayes modal (MAP) predictions of the random intercepts for families. We name the MAP predictions EB_PMode and the standard errors MAP_SE in the following R chunk. The MAP predictions are based on the conditional posterior distribution of the random intercepts, conditional on all model parameters being equal to their ML estimates, \(P(\zeta_j|y_{1j}, ..., y_{n_{j}j}, \boldsymbol{X}_{j};\boldsymbol{\hat\beta}^{ML}_j,\hat\psi^{ML})\). The standard errors MAP_SE are the standard deviations of a normal density whose logarithm has the same curvature at the mode as the conditional posterior distribution.

# display random intercepts assigned for the corresponding moms
head(ranef(M1)$mom)
knitr::opts_chunk$set(message=FALSE, 
                       warning=FALSE,
                       comment=NA,
                       cache = T) 
# display the MAP predictions
EB_PMode <- ranef(M1, condVar = TRUE)
# display the standard errors
MAP_SE <- sqrt(attr(EB_PMode[[1]], "postVar")[1, , ])
head(MAP_SE)
[1] 1.830832 1.821254 1.779558 1.821254 1.244001 1.788154

It is also useful to compare the conditional modes visually, which we do with the caterpillar plot below. The blue dots are the conditional modes with 95% confidence intervals (using \(\pm 1.96\) standard error). We display the random effects in order from the smallest to the largest. As there are 1959 families, we suppress their IDs by using the scales = list(y = list(alternating = 0)) argument. Therefore, the y-axis of mom’s ID is not displayed in the caterpillar plot.

# display the caterpillar plot
lattice::dotplot(ranef(M1, which = "mom", condVar = TRUE), scales = list(y = list(alternating = 0)))
$mom


3 Bayesian inference using rstanarm

In the previous section, we fitted the random-intercept logistic regression by using glmer(), which estimates the regression coefficients and standard deviation of random effects by approximate maximum likelihood. We specified 30 integration points so that the adaptive quadrature approximation is sufficiently accurate. However, when our models have more than a single random effect, glmer() could work poorly as it only supports the Laplace approximation. Joe (2008) showed that these Laplace estimates work especially poorly for models with binary responses and small cluster sizes, which is the case here. We also found large discrepancies between estimates based on Laplace and adaptive quadrature for Model 1.

A fully Bayesian (FB) approach overcomes these problems and produces estimates for all model parameters, including the random intercepts together with their associated uncertainties.

In this section, we show how to fit and evaluate Model 1 by using the rstanarm package. To prevent the results from being subjective (overly influenced by the priors), the default priors in rstanarm are designed to be weakly informative (see Prior Distributions for rstanarm Models for more details). Specifying weakly informative priors can be used for regularizing parameter estimates, avoiding for instance separation in logistic regression (Gelman et al, 2008).

3.1 Using the rstanarm package

The rstanarm package is a wrapper for the rstan package that enables the most commonly used (multilevel) regression models to be specified and fit in rstan using customary R modeling syntax. Educational researchers can use the Bayesian approach for multilevel models with only minimal changes to their existing glmer() syntax. We use the chains=6 argument to set the number of Markov chains to 6 and the iter=4000 argument to set the number of iterations for each chain to 4,000. The first half of these iteration, named warmup, will be discarded. Since we start each chain with some arbitrary starting values for the parameters, we need a long warm-up phase to reach the stationary distribution. By using the print() function, we can display a quick summary of the FB estimates of the model parameters, excluding the random intercepts.

M1_stanglmer <- stan_glmer(immun ~ kid2p + mom25p + ord + (1 | mom), 
                           family = binomial("logit"), 
                           data = guImmun,
                           iter = 4000,
                           chains = 6,
                           seed = 349)
# display the median and the median absolute deviation (MAD) of the posterior draws
print(M1_stanglmer, digits = 2)
stan_glmer
 family:       binomial [logit]
 formula:      immun ~ kid2p + mom25p + ord + (1 | mom)
 observations: 2159
------
            Median MAD_SD
(Intercept) -1.51   0.27 
kid2pY       1.71   0.22 
mom25pY     -0.14   0.23 
ord23       -0.33   0.24 
ord46       -0.19   0.30 
ord7p       -0.10   0.36 

Error terms:
 Groups Name        Std.Dev.
 mom    (Intercept) 2.7     
Num. levels: mom 1595 

------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg

The print() function displays the median and the median absolute deviation (MAD) of the posterior draws. One great advantage of Bayesian estimation is that we can choose any summaries of the posterior distribution. For example, we can use the summary() function to obtain the mean and standard deviation of the marginal posterior distribution for each parameter as shown below. In addition, users can specify which parameters to display by using the pars argument in the summary() function. Moreover, 95% credible intervals, which have a 95% probability of containing the true value of the parameter given the data, can be obtained by considering the \(2.5^{th}\) to \(97.5^{th}\) percentiles of the posterior draws. In addition, by using the probs argument, we can specify which quantiles of the posterior distribution to display.

# display the mean and standard deviation of the marginal posterior distribution of each parameter
summary(M1_stanglmer, 
        pars = c("(Intercept)", "kid2pY", "mom25pY", "ord46", "ord7p",  "b[(Intercept) mom:2]", "b[(Intercept) mom:185]", "b[(Intercept) mom:186]"),
        probs = c(0.025, 0.975),
        digits = 2)

Model Info:
 function:     stan_glmer
 family:       binomial [logit]
 formula:      immun ~ kid2p + mom25p + ord + (1 | mom)
 algorithm:    sampling
 sample:       12000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 2159
 groups:       mom (1595)

Estimates:
                         mean   sd    2.5%   97.5%
(Intercept)            -1.51   0.27 -2.06  -0.99  
kid2pY                  1.71   0.22  1.30   2.16  
mom25pY                -0.14   0.23 -0.60   0.32  
ord46                  -0.19   0.30 -0.79   0.40  
ord7p                  -0.10   0.37 -0.83   0.62  
b[(Intercept) mom:2]    1.74   2.04 -1.93   6.14  
b[(Intercept) mom:185] -1.73   2.04 -6.07   1.90  
b[(Intercept) mom:186] -1.87   1.96 -6.05   1.72  

MCMC diagnostics
                       mcse Rhat n_eff
(Intercept)            0.00 1.00  5836
kid2pY                 0.00 1.00  4984
mom25pY                0.00 1.00  5485
ord46                  0.00 1.00  4475
ord7p                  0.01 1.00  4925
b[(Intercept) mom:2]   0.02 1.00 13612
b[(Intercept) mom:185] 0.02 1.00 13807
b[(Intercept) mom:186] 0.02 1.00 13810

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).

MCMC diagnostics
Under MCMC diagnostics from the output, values under mcse represent estimates of the Monte Carlo standard errors (MCSEs) which represent the uncertainty due to estimating posterior expectations by sample means of Monte Carlo draws from the posterior. That is, the MCSEs represent the standard deviation of estimates obtained for a given dataset by repeatedly using the same MCMC approach to estimate the parameters (but with different starting values and random number sequences). Additionally, the MCMC diagnostics include Rhat and the effective sample size, n_eff.

Rhat: potential scale reduction statistic
Rhat measures the ratio of the average variance of the MCMC iterations within each chain to the variance of the pooled iterations across all chains (after discarding the warmup iterations). If all chains are at equilibrium (have reached the same stationary distribution), these variances will be the same so Rhat will equal one. The desired value of Rhat is smaller than 1.1. After fitting a Bayesian model, we can also use the bayesplot package to visualize MCMC diagnostics (see General MCMC diagnostics and vignette for more details). In the next R chunk, we use a generic rhat function to extract \(\hat{R}\) for estimates for all model parameters and random effects from M1_stanglmer and display the first 10 extracted \(\hat{R}\) values. Then, We visualize the \(\hat{R}\) values for all model parameters and random effects by using the mcmc_rhat function:

library("bayesplot")
rhats <- rhat(M1_stanglmer)
structure(rhats[1:10], my_attribute = "This is a vector")
           (Intercept)                 kid2pY                mom25pY 
             1.0002910              1.0003843              1.0007301 
                 ord23                  ord46                  ord7p 
             1.0007943              1.0006602              1.0006004 
  b[(Intercept) mom:2] b[(Intercept) mom:185] b[(Intercept) mom:186] 
             1.0000814              0.9999651              1.0000840 
b[(Intercept) mom:187] 
             1.0000155 
attr(,"my_attribute")
[1] "This is a vector"
color_scheme_set("brightblue") # see help("color_scheme_set")
mcmc_rhat(rhats)

In the plot, the points representing the \(\hat{R}\) values are colored based on their values by cutoff points 1.1 and 1.05.

Another advantage of rstanarm is that, in addition to allowing a range of plotting options with the bayesplot package, it allows the use of the shinystan package to obtain interactive diagnostics and posterior analysis for the Bayesian model (see shinystan). ShinyStan provides interactive plots and tables that are helpful for analyzing a posterior sample and can be used to identify potential problems by assessing the performance of the MCMC algorithm or the specification of the model.

Effective sample size

n_eff is an estimate of the effective number of independent draws from the posterior distribution for the parameter of interest. Because the draws within a chain are not independent if there is autocorrelation, the effective sample size, \(n_{eff}\), will be smaller than the total sample size based on the number of iterations, \(N\) (i.e., \(N=6,000 = 6\textrm{ chains} \times (2,000\textrm{ iterations} -1,000 \textrm{ iterations for warmup})\)in this case).

Therefore, the larger the ratio of \(n_{eff}\) to N the better. In the next R chunk, we use the neff_ratio function to extract the \(n_{eff}/ N\) values by fitting the the M1_stanglmer model. Then, we visualize the ratios by using the mcmc_neff function. Since the axis y-axis text is off by default for the above plot because the M1_stanglmer model has too many parameters. To make the labels clear, we can use the yaxis_text function to display names of the parameters with the concerning ratios, \(n_{eff}/ N\). The next R chunk displays a subset of parameters focusing on regression coefficients \(\beta\)s and three of the random intercepts \(\zeta_j\).

ratios <- neff_ratio(M1_stanglmer )
ratios_spe <- neff_ratio(M1_stanglmer, pars = c("(Intercept)", "kid2pY", "mom25pY", "ord46", "ord7p",  "b[(Intercept) mom:2]", "b[(Intercept) mom:185]", "b[(Intercept) mom:186]"))
mcmc_neff(ratios_spe) + yaxis_text(hjust = 1)

In the above plot, the points representing the ratios, \(n_{eff}/N\), are colored based on cutoff points 0.1 and 0.5.

3.2 Comparisons between estimates from stan_glmer and glmer

3.2.1 Comparisons of point estimates

In this section, we compare the point estimates from the glmer() and stan_glmer() functions that we name ML (maximum likelihood) and FB (fully Bayesian), respectively.

Comparison

  • For the regression coefficients, the FB estimate (median of posterior draws) of the population intercept \(\hat{\beta_1}\) is -1.51, which is close to the ML estimate (-1.49). The FB estimates of the coefficients \(\hat{\beta_2}\) to \(\hat{\beta_6}\) for the covariates \(kid2p_{ij}\), \(mom25p_{ij}\), \(ord23_{ij}\), \(ord46_{ij}\), \(ord7p_{ij}\) are 1.71, -0.13, -0.32, -0.19, -0.11 respectively, which are close to the corresponding ML estimates (1.68, 0.13, -0.31, -0.18, -0.10).

  • The FB estimate \(\sqrt{\hat{\psi}}\) of the random-intercept standard deviation is 2.72, which is larger than the ML estimate (2.61).

In section 2.1.3, we assigned values to the random intercepts by using the posterior modes (MAP) of the conditional posterior distributions, conditional on the parameters being equal to their ML estimates. Corresponding standard errors were based on the curvature of the conditional log-posterior. In this section, by using the summary() function to display the FB estimates, we are able to get the means of the draws of parameters and random effects from their joint posterior distribution. Moreover, users can directly access the posterior draws (as shown in section 3.2.3). Next, we compare the marginal (with respect to the other parameters) mean of those posterior draws of the random intercepts with the predictions generated by ranef().

3.2.2 Access to posterior draws directly

# Obtain family-level varying intercept zeta_j
## draws for 1595 family-level errors zeta_j
u_sims <- as.matrix(M1_stanglmer, 
                     regex_pars = "b\\[\\(Intercept\\) mom\\:")

# Compute mean, SD, median, and 95% credible interval of varying intercepts
## Posterior mean and SD of each alpha
FB_PMean <- apply(X = u_sims,     # posterior mean
                MARGIN = 2,
                FUN = mean)
FB_Psd <- apply(X = u_sims,       # posterior SD
              MARGIN = 2,
              FUN = sd)

## Posterior median and 95% credible interval
P_quant <- apply(X = u_sims, 
                 MARGIN = 2, 
                 FUN = quantile, 
                 probs = c(0.025, 0.50, 0.975))

FB_Pquant <- data.frame(t(P_quant))
names(FB_Pquant) <- c("Q2.5", "Q50", "Q97.5")

## Combine summary statistics of posterior simulation draws
FB<- data.frame(FB_PMean, FB_Psd , FB_Pquant)
round(head(FB), 2)

The FB table above shows the means of the posterior draws of the random intercepts for the first four families in the FB_PMean column, the standard deviations in the FB_Psd column, and the medians and 95% credible intervals in the Q2.5, Q50, Q97.5 columns, respectively.

# Obtain empirical Bayes modal prediction extracted from the ranef function in the section 2.1.2 (sometimes called modal a posterior (MAP) prediction)
MAP <- EB_PMode[1]
# Obtain the standard errors for the random-effect prediction
MAP_se <- sqrt(attr(EB_PMode[[1]], "postVar")[1, , ])


immun_N<-guImmun %>%
     filter(immun == "N", 
            ) %>%
     group_by(mom) %>%
     count(immun == "N", name = "num_N", .drop = FALSE)

immun_Y<-guImmun %>%
     filter(immun == "Y", 
            ) %>%
     group_by(mom) %>%
     count(immun == "Y", name = "num_Y", .drop = FALSE) 
    
immun_diff<-immun_Y[3]-immun_N[3]
a_df <- data.frame(immun_Y[3], immun_N[3], immun_diff, FB_PMean, FB_Psd, MAP, MAP_se)


options(knitr.table.format = "html") 
knitr::kable(head(a_df), col.names = c('num_Y', '  num_N  ',' Y-N ', ' FB_PMean ', ' FB_PSD ', ' MAP ', ' MAP_se '), caption = "1. Comparison table",linesep = " ", align = "c", digit=2)%>%
      kable_styling(latex_options="scale_down") %>%
      column_spec(1,width = "3in") 
  1. Comparison table
num_Y num_N Y-N FB_PMean FB_PSD MAP MAP_se
b[(Intercept) mom:2] 1 0 1 1.74 2.04 1.28 1.83
b[(Intercept) mom:185] 0 1 -1 -1.73 2.04 -1.31 1.82
b[(Intercept) mom:186] 0 1 -1 -1.87 1.96 -1.48 1.78
b[(Intercept) mom:187] 0 1 -1 -1.72 2.01 -1.31 1.82
b[(Intercept) mom:188] 1 1 0 0.08 1.44 0.09 1.24
b[(Intercept) mom:189] 1 0 1 1.88 2.00 1.44 1.79

 

In Table 1, the FB_PMean and FB_Psd columns show the FB posterior mean and standard deviation for each family and the MAP and MAP_se columns display the conditional (empirical Bayesian) posterior modes and corresponding standard errors. We will discuss these in more detail and visualize the differences in section 3.2.4.

Next, we make a similar table for some extreme families in which all responses are “Y” or “N”.

knitr::kable(head(a_df[order(a_df$num_Y, -a_df$num_N),]), col.names = c('num_Y', '  num_N  ',' Y-N ', ' FB_PMean ', ' FB_PSD ', ' MAP ', ' MAP_se '), caption = "2. Comparison table of families with extreme number of No", digit=2)%>%
      kable_styling(latex_options="scale_down") %>%
      column_spec(1,width = "3in") 
  1. Comparison table of families with extreme number of No
num_Y num_N Y-N FB_PMean FB_PSD MAP MAP_se
b[(Intercept) mom:730] 0 3 -3 -2.50 1.78 -1.94 1.59
b[(Intercept) mom:1079] 0 3 -3 -2.51 1.77 -1.94 1.59
b[(Intercept) mom:1485] 0 3 -3 -2.52 1.75 -1.94 1.59
b[(Intercept) mom:1652] 0 3 -3 -2.72 1.76 -2.12 1.54
b[(Intercept) mom:2176] 0 3 -3 -2.52 1.80 -1.94 1.59
b[(Intercept) mom:2282] 0 3 -3 -2.44 1.78 -1.89 1.60
knitr::kable(head(a_df[order(-a_df$num_Y, a_df$num_N),]), col.names = c('num_Y', '  num_N  ',' Y-N ', ' FB_PMean ', ' FB_PSD ',  ' MAP ', ' MAP_se '), caption = "3. Comparison table of families with extreme number of Yes", digit=2)%>%
      kable_styling(latex_options="scale_down") %>%
      column_spec(1,width = "3in") 
  1. Comparison table of families with extreme number of Yes
num_Y num_N Y-N FB_PMean FB_PSD MAP MAP_se
b[(Intercept) mom:384] 3 0 3 2.87 1.68 2.29 1.50
b[(Intercept) mom:456] 3 0 3 3.27 1.71 2.74 1.46
b[(Intercept) mom:699] 3 0 3 3.30 1.64 2.79 1.46
b[(Intercept) mom:827] 3 0 3 3.24 1.66 2.72 1.46
b[(Intercept) mom:829] 3 0 3 3.23 1.71 2.71 1.47
b[(Intercept) mom:879] 3 0 3 3.23 1.69 2.74 1.47

 

Tables 2 and 3 show that the FB posterior means (FB_PMean) are more different from zero than the conditional posterior mode (MAP) predictions. As will be shown in section 3.2.4, the difference between FB posterior means and conditional posterior mean (EAP) predictions is in the same direction but much less pronounced, suggesting that the differences are mostly due to using different summaries (mean versus mode) of skewed posterior distributions.

In addition, in these extreme families, the FB posterior standard deviations (FB_Psd) are larger than the corresponding standard errors for the conditional MAP predictions. One reason for these discrepancies is that the FB approach takes the parameter uncertainty for the other parameters into account by using the marginal posteriors (marginal over the other parameters), whereas the conditional MAP standard errors are based on the conditional posteriors (conditional on the other parameters being equal to the ML estimates).

For comparison, the following table displays some less extreme families in which the numbers of “Y” and “N” responses are almost the same.

knitr::kable(head(a_df[(immun_diff==0),]), col.names = c('num_Y', '  num_N  ',' Y-N ', ' FB_PMean ', ' FB_PSD ', ' MAP ', ' MAP_se '), caption = "4. Comparison table of families with same number of Yes and No responses ", digit=2) %>%
      kable_styling(latex_options="scale_down") %>%
      column_spec(1,width = "3in") 
  1. Comparison table of families with same number of Yes and No responses
num_Y num_N Y-N FB_PMean FB_PSD MAP MAP_se
b[(Intercept) mom:188] 1 1 0 0.08 1.44 0.09 1.24
b[(Intercept) mom:192] 1 1 0 0.58 1.49 0.59 1.36
b[(Intercept) mom:250] 1 1 0 0.03 1.41 0.04 1.24
b[(Intercept) mom:254] 1 1 0 0.68 1.50 0.71 1.33
b[(Intercept) mom:268] 1 1 0 0.08 1.45 0.10 1.24
b[(Intercept) mom:304] 1 1 0 0.09 1.41 0.09 1.24

 

As shown in table 4, when the numbers of “Y” and “N” responses become closer, the discrepancies of predictions and standard errors between the two approaches become smaller. That could be, in part, because the posteriors are more symmetric.

So far, we have shown that the conditional MAP predictions of the random intercepts for families are different from FB estimates. To summarize, for conditional MAP, we obtain the conditional posterior distributions of \(\zeta_j\) by plugging in the ML estimates of the regression coefficients and the random-intercept variance obtained from glmer(). Then we use the modes of the conditional posterior distributions, conditional on the parameters being equal to their ML estimates. In contrast, in FB we obtain draws from the joint posterior distributions of the random intercepts and all other parameters. The means and standard deviations of the draws of the random intercepts therefore represent marginal posterior means and standard deviations, marginal over all other parameters.

3.2.3 Plotting functions for Stan estimates

In this section, we use the package bayesplot, which provides a library of plotting functions for Stan estimates, to visualize the posterior draws. The plots created by bayesplot are ggplot objects and can be customized using various functions from the ggplot2 package. Furthermore, baysplot offers visual MCMC diagnostics, graphical posterior predictive checking, and more. Since we are not digging more into these visualizations in this tutorial, see bayesplot for more information.

library("bayesplot")
library("ggplot2")
library("rstanarm")  
library(rstan)
#posterior <- as.array(M1_stanglmer)
#posterior<- as.matrix(M1_stanglmer)
posterior<- as.data.frame(M1_stanglmer)
dim(posterior)
[1] 12000  1602

In the next R chunk, we use the mcmc_dens() function to display kernel density plots of MCMC draws of a parameter (combining all chains). By using the pars option, we specify the parameter and display the distribution of the posterior draws for a specific family, mom with id #2, as an example in this tutorial.

color_scheme_set("purple")
p2<-mcmc_dens(posterior, pars = c("b[(Intercept) mom:2]"))
p2

3.2.4 Visualized comparison between empirical Bayes predictions and fully Bayesian estimates

We now compare the FB marginal posterior density of the random intercept for the family whose identifier in variable mom was \(2\), aka mom 2 (as an example) with the corresponding conditional posterior density, with ML estimates plugged in for the regression coefficients and the random-intercept variances.

First, for our FB estimates, we use the as.data.frame() function to retrieve the posterior draws of the random effects from M1_stanglmer. This method enables us to access the draws directly and thus makes it easier to manipulate the plot. For instance, we plot the posterior distribution of the random intercept for a specific family, mom 2. We add a black-solid line to show the mean of posterior draws for mom 2. The 95% credible interval is represented by a black error bar which, unlike frequentist confidence intervals, can be interpreted as having a 95% probability of containing the random intercept.

posterior<- as.data.frame(M1_stanglmer) # retrieve the posterior draws 
#print(dimnames(posterior))
FB_posterior_mom2<-posterior$`b[(Intercept) mom:2]`
df <- data.frame(col1 = (1:12000),
                   col2=FB_posterior_mom2)
p <- ggplot(df) 
p + geom_density(aes(FB_posterior_mom2), fill = "lightgray") +
  geom_vline(aes(xintercept = mean(FB_posterior_mom2)), linetype = "solid", lwd = 1.5)+
  geom_errorbarh(aes(xmin=mean(FB_posterior_mom2)-2*sd(FB_posterior_mom2), xmax =mean(FB_posterior_mom2)+2*sd(FB_posterior_mom2), y=0.02, height = .005), lwd = 1.5)+
  annotate(geom="text", x=3, y=0.03, label="FB_mean",color="black")

Secondly, we derive the conditional posterior density of the random intercept for mom 2 with ML estimates plugged in for the regression coefficients and random-intercept variance. This conditional posterior density can be written as \[\textrm{Posterior}(\zeta_2|immun_{22}=1, kid2p_{22}, mom25p_{22}, ord23_{22}, ord46_{22}, ord7p_{22}; \boldsymbol{\hat\beta}^{ML}_j,\hat\psi^{ML})=\\ \frac{\textrm{Prior}(\zeta_2)\times \textrm{Likelihood}(immun_{22}=1|kid2p_{22}, mom25p_{22}, ord23_{22}, ord46_{22}, ord7p_{22},\zeta_2)}{C}\]

The conditional prior is simply a normal density with variance set equal to its ML estimate, and we can evaluate this prior density at a set of points as follows:

# evaluate the normal density function at different values
zeta <- seq(-5, 10, by = .01)
# specify a normal density with variance set equal to its ML estimate
sqrt_psi<-sqrt(6.836)
prior <- dnorm(zeta, 
                mean = 0, 
                sd = sqrt_psi)  

The likelihood function is logit\(^{-1}[\)linpred\(_j+\zeta_j]\), where linpred\(_j\) represents the “fixed” part of the linear predictor for this specific family, mom 2 (who had one child with \(y_{i2}=1\)). The “fixed” part of the linear predictor can be written as \[linpred_j=\hat\beta_1 + \hat\beta_2 kid2p_{ij} + \hat\beta_3 mom25p_{ij} +\hat\beta_4 ord23_{ij} +\hat\beta_5 ord46_{ij} +\hat\beta_6 ord7p_{ij}\]

linpred=-1.4937 + 1.6845*1 +-0.1298*0+-0.3140*0+-0.1798*0+-0.1017*0            
likelihood <- invlogit(linpred+zeta) 

It follows from Bayes theorem that the posterior distribution is proportional to the product of the likelihood and the prior. The normalizing constant is the marginal probability of the response (marginal over the random intercept), which can be written as \[C =\int \hat Pr(y_{ij}=1|mom25p_{ij}, ord23_{ij}, ord46_{ij}, ord7p_{ij}, \zeta_j)\phi(\zeta_j; 0, \hat\psi^{ML})d\zeta_j\] where \(\phi(\zeta_j; 0, \hat\psi^{ML})\) is the normal density of \(\zeta_j\) with a mean of 0 and a variance of \(\hat\psi^{ML}\). This can be obtained by Monte Carlo integration, sampling \(\zeta_j\) from its conditional prior distribution:

y <- rnorm(100000, sd = sqrt_psi)
normalizing.constant<- mean((invlogit(linpred+ y)))

Lastly, we calculate the product of the prior of the random intercept and the likelihood of the responses for the cluster (for mom 2’s child) given the random intercept and divide this product by the normalizing constant to obtain the posterior density:

EB_posterior <-prior*likelihood/normalizing.constant

Below, this conditional posterior is added to the plot. This conditional posterior density is not a normal density as in linear mixed models, and hence its mode does not equal its mean. Recall that the random intercept predictions obtained by using the ranef() function are the modes (MAP) of this conditional posterior.

We can calculate the conditional mean (EAP) and the corresponding standard deviation by using the integrate() function, as shown in the next R chunk.

# EB mean for mom 2: y = 1
x <- rnorm(10000000, sd = sqrt_psi)
normalizing.constant<- mean((invlogit((linpred+x))))

func <- function(x){
  x*dnorm(x, sd = sqrt_psi)*(invlogit((linpred+x)))/normalizing.constant
}
integrate(func, lower = -Inf, upper = Inf)
1.653317 with absolute error < 4.1e-08
EX <- integrate(func, lower = -Inf, upper = Inf)[[1]]

func_sd <- function(x){
  x^2*dnorm(x, sd = sqrt_psi)*(invlogit((linpred+x)) )/normalizing.constant
}
EX2 <- integrate(func_sd, lower = -Inf, upper = Inf)[[1]]
# conditional sd
EAP_sd <- sqrt(EX2 - EX^2)

To compare the different estimates, the code below adds the conditional EAP to the density plot as a dark blue-dotted vertical line and the corresponding conditional MAP as a lighter, blue-dashed vertical line. The figure shows that the black vertical line (FB_Mean) is further away from zero than the dark blue-dotted vertical line (conditional EAP) and the lighter, blue-dashed vertical line (conditional MAP). However, the distance between the black vertical line (FB_Mean) and the dark blue-dotted vertical line (conditional EAP) is in the same direction but much shorter than the distance between the black vertical line (FB_Mean) and the lighter, blue-dashed vertical line (conditional MAP). This suggests that the main reason for the discrepancy between FB posterior means and conditional MAP predictions is that the posteriors (and conditional posteriors) are asymmetric - skewed to the left for positive FB posterior means and to the right for negative ones. As mentioned, because the FB posterior standard deviation takes parameter uncertainty for the other parameters into account, whereas the conditional EAP and MAP standard errors are based on the conditional posteriors, the 95% credible interval for FB is broader than those for the conditional EAP and MAP, which is why in the figure the black error bar is wider than the blue one and the lighter blue one. Additionally, the conditional MAP standard error is smaller than the conditional EAP standard error because the former is based on the curvature of the conditional log posterior at the mode and is therefore not affected by the thicker tail on the right.

df2 <- data.frame(col1 =zeta, col2= EB_posterior)
p + geom_density(aes(FB_posterior_mom2), fill = "lightgray") +
  geom_vline(aes(xintercept = mean(FB_posterior_mom2)), linetype = "solid", lwd = 1.5)+
  geom_errorbarh(aes(xmin=mean(FB_posterior_mom2)-2*sd(FB_posterior_mom2), xmax =mean(FB_posterior_mom2)+2*sd(FB_posterior_mom2), y=0.03, colour=FB_Mean),colour="black", height = .005, lwd = 1.5)+
  annotate(geom="errorbarh", xmin=6, xmax=6.5, y=0.2,height = .005,colour="black", size=1.5)+
  annotate(geom="text", x=9, y=0.2, label="FB_mean", size=4)+
  geom_line(data=df2, aes(zeta, EB_posterior), colour="blue")+
  geom_vline(aes(xintercept = EX), linetype = "dotted", colour="blue", lwd = 1.5)+ # adds the EAP to the density plot as a dark blue-dotted vertical line
  geom_errorbarh(aes(xmin=EX-2*EAP_sd, xmax =EX+2*EAP_sd, y=0.02, colour=EB_Mean), height = .005, colour="blue", lwd = 1.5)+
  annotate(geom="errorbarh", xmin=6, xmax=6.5, y=0.19,height = .005,colour="blue", size=1.5)+
  annotate(geom="text", x=9, y=0.19, label="EB_mean", size=4)+
  geom_vline(aes(xintercept = a_df$X.Intercept.[1]), linetype = "dashed", colour="lightblue3", lwd = 1.5)+
  geom_errorbarh(aes(xmin=a_df$X.Intercept.[1]-2*a_df$MAP_se[1], xmax=a_df$X.Intercept.[1]+2*a_df$MAP_se[1], y=0.01, colour=EB_Mode), height = .005, colour="lightblue3", lwd = 1.5)+
  annotate(geom="errorbarh", xmin=6, xmax=6.5, y=0.18,height = .005,colour="lightblue3", size=1.5)+
  annotate(geom="text", x=9, y=0.18, label="EB_mode", size=4)

4. Model 2: Two-level random intercept model with level-1, level-2, and cross-level interactions

4.1 Likelihood inference for Model 2 using glmer()

In addition to the child-level covariates that have been added to the first model, we continue to add a series of family-level covariates (namely \(ethnN_{j}\), \(ethnS_{j}\), \(momEdP_{j}\), \(momEdS_{j}\), \(husEdP_{j}\), \(husEdS_{j}\), \(husEdU_{j}\), and \(momWorkY_{j}\)) and cross-level interactions between the level-1 variable indicating children’s age and the level-2 variable indicating mothers’ education levels (\(kid2pY_{ij}\times momEdP_{j}\) and \(kid2pY_{ij}\times momEdS_{j}\)). Model 2 can be written as \[\textrm{logit}\{Pr(immun_{ij}=1|x_{ij}, \zeta_j)\} = \beta_1 + \beta_2 kid2p_{ij} + \beta_3 mom25p_{ij} +\beta_4 ord23_{ij} +\beta_5 ord46_{ij} +\beta_6 ord7p_{ij}+\\ \beta_7 ethnN_{j}+\beta_8 ethnS_{j}+\beta_9 momEdP_{j}+ \beta_{10}momEdS_{j}+ \beta_{11} husEdP_{j} +\beta_{12} husEdS_{j}+\beta_{13} husEdU_{j}+ \beta_{14} momWorkYes_{j}+\\ \beta_{15} kid2pYes_{ij}\times momEdP_{j}+ \beta_{16} kid2pYes_{ij}\times momEdS_{j} +\zeta_j\]

The random intercepts \(\zeta_j \sim N(0, \psi)\) are assumed to be independent of each other and identically distributed across families \(j\) and independent of the covariates in the model.

M2 <- glmer(immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork +kid2p * momEd +  (1 | mom),  
            family = binomial("logit"), 
            data = guImmun, control = glmerControl(optimizer = "bobyqa"),
            nAGQ = 10)
summary(M2)
Generalized linear mixed model fit by maximum likelihood (Adaptive
  Gauss-Hermite Quadrature, nAGQ = 10) [glmerMod]
 Family: binomial  ( logit )
Formula: immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork +  
    kid2p * momEd + (1 | mom)
   Data: guImmun
Control: glmerControl(optimizer = "bobyqa")

     AIC      BIC   logLik deviance df.resid 
  2731.9   2828.4  -1348.9   2697.9     2142 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.6535 -0.4250 -0.2222  0.4636  2.6119 

Random effects:
 Groups Name        Variance Std.Dev.
 mom    (Intercept) 6.795    2.607   
Number of obs: 2159, groups:  mom, 1595

Fixed effects:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept)   -2.23632    0.42995  -5.201 1.98e-07 ***
kid2pY         1.45443    0.29729   4.892 9.97e-07 ***
mom25pY       -0.18574    0.23791  -0.781  0.43497    
ord23         -0.23704    0.23850  -0.994  0.32028    
ord46          0.10312    0.30281   0.341  0.73344    
ord7p          0.35860    0.38198   0.939  0.34784    
ethnN         -0.44176    0.30501  -1.448  0.14752    
ethnS         -0.36978    0.24345  -1.519  0.12879    
momEdP         0.33879    0.38706   0.875  0.38141    
momEdS        -0.74728    0.80424  -0.929  0.35280    
husEdP         0.56626    0.23493   2.410  0.01594 *  
husEdS         0.69970    0.41438   1.689  0.09131 .  
husEdU        -0.05678    0.36983  -0.154  0.87798    
momWorkY       0.55080    0.20448   2.694  0.00707 ** 
kid2pY:momEdP  0.32512    0.39488   0.823  0.41032    
kid2pY:momEdS  2.05458    0.83985   2.446  0.01443 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation matrix not shown by default, as p = 16 > 12.
Use print(x, correlation=TRUE)  or
    vcov(x)        if you need it

From the output:

  • The Fixed Effects section displays the point estimates of regression coefficients with estimated standard errors and Wald tests. We can learn from the output which child-level and family-level covariates are statistically significantly associated with the child-level response variable, controlling for the other variables.

  • In the Random Effects section, the ML estimates provide the estimated standard deviation (\(\sqrt{\hat{\psi}}\approx2.61\)) of the random intercepts. We can learn from the random effects output about the extent to which between-family variability in the response variable is left unexplained by employing Model 2. We can also compare this estimate with the corresponding estimate obtained for Model 1 to quantify the extent to which between-family variability has been explained by the family-level covariates and the cross-level interaction added to the second model.

Then, we create the caterpillar plot below to show the distribution of the conditional modes.

lattice::dotplot(ranef(M2, which = "mom", condVar = TRUE), scales = list(y = list(alternating = 0)))
$mom

4.2 Bayesian inference for Model 2 using rstanarm

In this section, we use stan_glmer() to estimate Model 2. Similar to Model 1, we use weakly informative prior distributions for the hyper-parameter \(\psi\).

M2_stanglmer <- stan_glmer(immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork + kid2p * momEd+(1 | mom), 
                           family = binomial("logit"), 
                           data = guImmun,
                           seed = 349)

5. Model 3: Three-level random intercept model with level-1, level-2, and level-3 predictors

5.1 Likelihood inference for Model 3 glmer()

We have looked at the two-level logistic model with a random intercept in depth. In this section, we are going to briefly look at how to estimate a three-level logistic model with level-2 random intercepts, \(\zeta^{(2)}_{jk} \sim N(0, \psi^{(2)})\), for families \(j\) and level-3 random intercepts, \(\zeta^{(3)}_{k} \sim N(0, \psi^{(3)})\), for communities \(k\). In Model 3, children are nested within families, and families are nested in communities, so the levels are nested. The model can be written as \[\textrm{logit}\{Pr(immun_{ijk}=1| x, \zeta^{(2)}_{jk}, \zeta^{(3)}_{k}\} = \beta_1 + \beta_2 kid2p_{ijk} + ... +\beta_6 ord7p_{ijk}+\beta_7 ethnN_{jk}+...\\ + \beta_{14} momWorkYes_{jk}+\beta_{15} rural_{k}+\beta_{16} pcInd81_{k} +\zeta^{(2)}_{jk}+\zeta^{(3)}_{k}\]

In glmer, we do not need to specify the data structure (nested or crossed) because glmer() figures it out automatically based on the data. We use the same general syntax (1|ID) to specify intercepts for the cluster identifier ID, regardless of what other levels there are in the model. In addition to the child-level and family-level covariates, we go further to add two community-level covariates (\(rural_k\) and \(pcInd81_k\)). As mentioned, for a model with more than a single scalar random effect, glmer only supports a single integration point, which can be specified by nAGQ = 1 and is equivalent to the Laplace approximation which may not be accurate. We can overcome this problem by fitting the model in rstanarm (as shown in section 5.2).

M3 <- glmer(immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork + rural+ pcInd81 + (1 | mom) + (1 | comm),  
            family = binomial("logit"), 
            data = guImmun, control = glmerControl(optimizer = "bobyqa"),
            nAGQ = 1)
# print the model results without correlations among fixed effects
print(M3, digits = 2)
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork +  
    rural + pcInd81 + (1 | mom) + (1 | comm)
   Data: guImmun
      AIC       BIC    logLik  deviance  df.resid 
 2747.402  2849.595 -1355.701  2711.402      2141 
Random effects:
 Groups Name        Std.Dev.
 mom    (Intercept) 1.13    
 comm   (Intercept) 0.72    
Number of obs: 2159, groups:  mom, 1595; comm, 161
Fixed Effects:
(Intercept)       kid2pY      mom25pY        ord23        ord46        ord7p  
     -0.947        1.282       -0.128       -0.139        0.174        0.289  
      ethnN        ethnS       momEdP       momEdS       husEdP       husEdS  
     -0.113       -0.035        0.295        0.302        0.395        0.369  
     husEdU     momWorkY       ruralY      pcInd81  
      0.015        0.270       -0.649       -0.857  

Similar to the glmer output for Model 1 and Model 2, the output tells us about the family (which is binomial as our response variable is binary) and the link (which is logit). However, while the output for Models 1 and 2 showed only the estimated standard deviation of the random intercepts at the family level (\(\sqrt{\hat{\psi}}\)), the output of Model 3 shows both the between-family within-community standard deviation estimate (\(\sqrt{\hat{\psi}^{(2)}}= 1.13\)) and the between-community standard deviation estimate (\(\sqrt{\hat{\psi}^{(3)}}=0.72\)).

Now we can plot the conditional posterior modes of the random intercepts for either families or communities. By using the which= option, we can choose between families (mom) and communities (comm).

# for families
lattice::dotplot(ranef(M3, which = "mom", condVar = TRUE), scales = list(y = list(alternating = 0)))
$mom

# for communities
lattice::dotplot(ranef(M3, which = "comm", condVar = TRUE), scales = list(y = list(alternating = 0)))
$comm

5.2 Bayesian inference for Model 3 using rstanarm

We use stan_glmer() to fit Model 3 using the code below. We use the default priors and display the median and the median absolute deviation (MAD) of the posterior draws of the model parameters by using the print() function.

M3_stanglmer <- stan_glmer(immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork + rural + pcInd81 + (1 | mom) + (1 | comm), 
                           family = binomial("logit"), 
                           data = guImmun,
                           seed = 349)
prior_summary(object = M3_stanglmer)
Priors for model 'M3_stanglmer' 
------
Intercept (after predictors centered)
 ~ normal(location = 0, scale = 2.5)

Coefficients
  Specified prior:
    ~ normal(location = [0,0,0,...], scale = [2.5,2.5,2.5,...])
  Adjusted prior:
    ~ normal(location = [0,0,0,...], scale = [5.95,5.00,5.27,...])

Covariance
 ~ decov(reg. = 1, conc. = 1, shape = 1, scale = 1)
------
See help('prior_summary.stanreg') for more details
print(M3_stanglmer, digits = 2)
stan_glmer
 family:       binomial [logit]
 formula:      immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork + 
       rural + pcInd81 + (1 | mom) + (1 | comm)
 observations: 2159
------
            Median MAD_SD
(Intercept) -1.31   0.51 
kid2pY       1.83   0.22 
mom25pY     -0.24   0.24 
ord23       -0.29   0.24 
ord46        0.18   0.31 
ord7p        0.47   0.40 
ethnN       -0.22   0.53 
ethnS       -0.11   0.40 
momEdP       0.47   0.23 
momEdS       0.45   0.52 
husEdP       0.57   0.25 
husEdS       0.55   0.45 
husEdU      -0.02   0.38 
momWorkY     0.42   0.22 
ruralY      -0.95   0.33 
pcInd81     -1.21   0.54 

Error terms:
 Groups Name        Std.Dev.
 mom    (Intercept) 2.61    
 comm   (Intercept) 1.15    
Num. levels: mom 1595, comm 161 

------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg

6. Model 4: Three-level random-coefficient logistic regression model

6.1 Likelihood inference for Model 4 using glmer()

Finally, we show an example of a three-level random-coefficient model. Compared to random-intercept models, in which family-specific probability curves are shifted horizontally depending on the values of the random effects, the curves in random-coefficient models are no longer just shifted, but their slope varies depending on the value of the random coefficients. For example, the effect of whether a child was 2 years of age or older at the time of the survey (which means that they were eligible for the vaccine at the time of the intervention) on whether they had received a complete set of immunizations may vary across communities. Such variability can be modeled by adding the term \(\zeta^{(3)}_{2k}kid2pYes_{ijk}\) to Model 3 and renaming the community-level random intercepts \(\zeta^{(3)}_{k}\) to \(\zeta^{(3)}_{1k}\),which leads to the following random-coefficient model \[\textrm{logit}\{Pr(immun_{ijk}=1| x, \zeta^{(2)}_{jk}, \zeta^{(3)}_{k}\} = \beta_1 + \beta_2 kid2pYes_{ijk} + ... +\beta_6 ord7p_{ijk}+\beta_7 ethnN_{jk}+...\\ + \beta_{14} momWorkYes_{jk}+\beta_{15} rural_{k}+\beta_{16} pcInd81_{k} +\zeta^{(2)}_{jk}+\zeta^{(3)}_{2k}kid2pYes_{ijk}+\zeta^{(3)}_{1k}\]

Given the covariates Xs at all three levels, the random intercept at the family level has a normal distribution with mean zero and variance \(\psi^{(2)}\), \(\zeta^{(2)}_{jk} \sim N(0, \psi^{(2)})\). The random intercept and the random slope at the community-level, which are written as \(\zeta^{(3)}_{1k}\) and \(\zeta^{(3)}_{2k}\), respectively, are bivariate normal with zero means and an unstructured covariance matrix \[\boldsymbol\Psi^{(3)}= \begin{bmatrix} \psi^{3}_{11} &\psi^{3}_{12} \\ \psi^{3}_{21} &\psi^{3}_{22} \end{bmatrix} \equiv \\ \begin{bmatrix} Var(\zeta^{(3)}_{1k}|X_k) & Cov(\zeta^{(3)}_{1k}, \zeta^{(3)}_{2k}|X_k) \\ Cov(\zeta^{(3)}_{2k}, \zeta^{(3)}_{1k}|X_k) & Var(\zeta^{(3)}_{2k}|X_k) \\ \end{bmatrix}\] where \(\psi^{(3)}_{21}=\psi^{(3)}_{12}\).

In glmer(), we use the + operator to “add” this random slope at the community level. The correlation matrix is not shown by default, but we can obtain it by specifying correlation=TRUE in the print() function.

M4 <- glmer(immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork + rural + pcInd81 + (1 | mom) + (1 + kid2p| comm),  
          family = binomial("logit"), 
        data = guImmun, control = glmerControl(optimizer = "bobyqa"),
         nAGQ = 1)
summary(M4)
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork +  
    rural + pcInd81 + (1 | mom) + (1 + kid2p | comm)
   Data: guImmun
Control: glmerControl(optimizer = "bobyqa")

     AIC      BIC   logLik deviance df.resid 
  2747.6   2861.1  -1353.8   2707.6     2139 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.7054 -0.6276 -0.3360  0.6823  2.3462 

Random effects:
 Groups Name        Variance Std.Dev. Corr 
 mom    (Intercept) 1.2835   1.1329        
 comm   (Intercept) 0.9654   0.9825        
        kid2pY      0.5909   0.7687   -0.68
Number of obs: 2159, groups:  mom, 1595; comm, 161

Fixed effects:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept) -1.08578    0.36296  -2.991  0.00278 ** 
kid2pY       1.38297    0.19544   7.076 1.48e-12 ***
mom25pY     -0.11687    0.16758  -0.697  0.48556    
ord23       -0.12778    0.17609  -0.726  0.46805    
ord46        0.18667    0.21642   0.863  0.38841    
ord7p        0.29848    0.27034   1.104  0.26956    
ethnN       -0.08608    0.34048  -0.253  0.80040    
ethnS       -0.03566    0.25374  -0.141  0.88823    
momEdP       0.30963    0.15429   2.007  0.04477 *  
momEdS       0.33060    0.33921   0.975  0.32975    
husEdP       0.40964    0.16128   2.540  0.01109 *  
husEdS       0.37926    0.28800   1.317  0.18788    
husEdU       0.02114    0.24728   0.085  0.93187    
momWorkY     0.27872    0.14060   1.982  0.04744 *  
ruralY      -0.64758    0.21204  -3.054  0.00226 ** 
pcInd81     -0.86155    0.35048  -2.458  0.01396 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation matrix not shown by default, as p = 16 > 12.
Use print(x, correlation=TRUE)  or
    vcov(x)        if you need it
# print the mod results with correlation among fixed effects
print(M4, correlation=TRUE)
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork +  
    rural + pcInd81 + (1 | mom) + (1 + kid2p | comm)
   Data: guImmun
      AIC       BIC    logLik  deviance  df.resid 
 2747.588  2861.136 -1353.794  2707.588      2139 
Random effects:
 Groups Name        Std.Dev. Corr 
 mom    (Intercept) 1.1329        
 comm   (Intercept) 0.9825        
        kid2pY      0.7687   -0.68
Number of obs: 2159, groups:  mom, 1595; comm, 161
Fixed Effects:
(Intercept)       kid2pY      mom25pY        ord23        ord46        ord7p  
   -1.08578      1.38297     -0.11687     -0.12778      0.18667      0.29848  
      ethnN        ethnS       momEdP       momEdS       husEdP       husEdS  
   -0.08608     -0.03566      0.30963      0.33060      0.40964      0.37926  
     husEdU     momWorkY       ruralY      pcInd81  
    0.02114      0.27872     -0.64758     -0.86155  

In the next R chunk, we produce a caterpillar plot of the conditional modes of the random intercepts for families and the random intercepts and slopes for communities.

# for families
lattice::dotplot(ranef(M4, which = "mom", condVar = TRUE), scales = list(y = list(alternating = 0)))
$mom

# for communities
lattice::dotplot(ranef(M4, which = "comm", condVar = TRUE), scales = list(y = list(alternating = 0)))
$comm

6.2 Bayesian inference for Model 4 using rstanarm

In this section, we use stan_glmer() to fit Model 4. We mentioned default (weakly informative) priors in rstanarm, which are designed to provide moderate regularization and help stabilize computation. More information on priors is available in the vignette Prior Distribution for rstanarm Models. We use the default prior distribution for parameters in M4_stanglmer by not specifying any prior options in the stan_glmer() function:

M4_stanglmer <- stan_glmer(immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork + rural + pcInd81 + (1 | mom) + (1 + kid2p| comm),  
                          family = binomial("logit"), 
                         data = guImmun,
                          seed = 349)

The way rstanarm attempts to achieve weakly informative priors by default is to internally adjust the scales of the priors. We explain how this works below:

First, we can use the prior_summary() function to show how the specification of prior distribution works in the rstanarm package.

prior_summary(object = M4_stanglmer)
Priors for model 'M4_stanglmer' 
------
Intercept (after predictors centered)
 ~ normal(location = 0, scale = 2.5)

Coefficients
  Specified prior:
    ~ normal(location = [0,0,0,...], scale = [2.5,2.5,2.5,...])
  Adjusted prior:
    ~ normal(location = [0,0,0,...], scale = [5.95,5.00,5.27,...])

Covariance
 ~ decov(reg. = 1, conc. = 1, shape = 1, scale = 1)
------
See help('prior_summary.stanreg') for more details

Starting from the bottom part of the output:

  • The prior_covariance applies to covariance matrices in multilevel models with varying slopes and intercepts. In other words, it specifies a prior distribution for the unknown covariance matrices of the group-specific coefficients. Specifically, the covariance matrix is decomposed into correlation matrix and variance. The variance is in turn decomposed into the product of a simplex vector and the trace of the matrix. Finally, the trace is the product of the order of the matrix and the square of a scale parameter. The following diagram shows the decomposed components.
library(DiagrammeR)

DiagrammeR::grViz("digraph {
  
graph[layout = dot, rankdir = LR]

'Covariance matrix'
'Correlation matrix'
'Variance'
'Simplex vector'
'The trace of the matrix'
'The order of the matrix'
'The square of a scale parameter'

'Covariance matrix' -> 'Correlation matrix'
'Covariance matrix' -> 'Variance' 
'Variance' -> 'Simplex vector' 
'Variance' -> 'The trace of the matrix'
'The trace of the matrix' -> 'The order of the matrix'
'The trace of the matrix' ->'The square of a scale parameter'
}")

Overall, this prior on a covariance matrix is represented by the decov() function.

The prior for a correlation matrix is called LKJ, which is proportional to the determinant of the correlation matrix raised to the power of a positive regularization parameter minus one. The default reg. =1 indicates this prior is jointly uniform over all correlation matrices of that size.

For the simplex vector, a symmetric Dirchlet prior is used. The default conc. = 1 represents a single (positive) concentration parameter and the prior is jointly uniform over the space of simplex vectors of that size.

The trace of a covariance matrix is equal to the sum of variances. We set the trace equal to the product of the order of the covariance matrix and the square of a positive scale parameter. The particular variances are set equal to the product of a simplex vector (which is non-negative and sums to 1) and the scalar trace. In other words, each element of the simplex vector represents the proportion of the trace attributable to the corresponding variable.

For the positive scale parameter, we use a scale-invariant prior distribution, which is a Gamma distribution in this case. The default shape = 1, scale = 1 implies a unit-exponential distribution.

For more details on prior distributions for the covariance matrix \(\boldsymbol\Psi^{(3)}\) in random-coefficient models, see the bottom part of the tutorial Intro to multilevel modeling using rstanarm and also the function explanation Prior distributions and options.

  • The default prior on a regression coefficient \(\beta_k\) is a normal distribution with zero mean. The default prior scale depends on the family of the prior being used, a normal distribution in this case, which is 2.5 by default when explanatory variables have been scaled to have a standard deviation of 0.5 (Gelman et al., 2008) (see Prior Distribution for rstanarm Models). In order for the default to be weakly informative, rstanarm adjusts the scales of the priors according to the standard deviations of the corresponding explanatory variables. As a result, the prior scales used were [5.95, 5.00, 5.27, …] respectively for the regression coefficients.

  • For the intercepts, the default prior is a normal distribution with zero mean and a standard deviation of 2.5. The note in parentheses informs us that the prior applies to the intercept after all predictors have been centered. In this case, we view the intercept as the value of the fixed part of the linear predictor when all covariates are equal to their means instead of zero, which makes the intercept more interpretable and meaningful.

To disable automatic rescaling, simply specify a prior other than the default, we can explicitly set the autoscale = FALSE argument. In addition, we can specify flat (non-informative) priors by set prior=NULL in the stan_glmer() function. Users can also specify priors that reflect their prior information (see Prior distributions and options for more information).

print(M4_stanglmer, digits = 2)
stan_glmer
 family:       binomial [logit]
 formula:      immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork + 
       rural + pcInd81 + (1 | mom) + (1 + kid2p | comm)
 observations: 2159
------
            Median MAD_SD
(Intercept) -1.54   0.61 
kid2pY       2.04   0.31 
mom25pY     -0.21   0.26 
ord23       -0.30   0.26 
ord46        0.19   0.32 
ord7p        0.49   0.42 
ethnN       -0.17   0.55 
ethnS       -0.11   0.40 
momEdP       0.51   0.26 
momEdS       0.51   0.57 
husEdP       0.64   0.28 
husEdS       0.62   0.48 
husEdU       0.00   0.40 
momWorkY     0.45   0.23 
ruralY      -1.00   0.35 
pcInd81     -1.30   0.57 

Error terms:
 Groups Name        Std.Dev. Corr 
 mom    (Intercept) 2.78          
 comm   (Intercept) 1.63          
        kid2pY      1.36     -0.65
Num. levels: mom 1595, comm 161 

------
* For help interpreting the printed output see ?print.stanreg
* For info on the priors used see ?prior_summary.stanreg
print(M4, digits = 2)
Generalized linear mixed model fit by maximum likelihood (Laplace
  Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: immun ~ kid2p + mom25p + ord + ethn + momEd + husEd + momWork +  
    rural + pcInd81 + (1 | mom) + (1 + kid2p | comm)
   Data: guImmun
      AIC       BIC    logLik  deviance  df.resid 
 2747.588  2861.136 -1353.794  2707.588      2139 
Random effects:
 Groups Name        Std.Dev. Corr 
 mom    (Intercept) 1.13          
 comm   (Intercept) 0.98          
        kid2pY      0.77     -0.68
Number of obs: 2159, groups:  mom, 1595; comm, 161
Fixed Effects:
(Intercept)       kid2pY      mom25pY        ord23        ord46        ord7p  
     -1.086        1.383       -0.117       -0.128        0.187        0.298  
      ethnN        ethnS       momEdP       momEdS       husEdP       husEdS  
     -0.086       -0.036        0.310        0.331        0.410        0.379  
     husEdU     momWorkY       ruralY      pcInd81  
      0.021        0.279       -0.648       -0.862  

Comparing the FB estimates and ML estimates for Model 4, we notice that the FB estimates are further from zero than the ML estimates.

Lastly, in the next R chunk, we use Model 4 as an example to show how to display HTML tables for models estimated from glmer() and stan_glmer.

# Summary of mixed models as HTML table
library(sjPlot)

tab_model(M4)
  immun
Predictors Odds Ratios CI p
(Intercept) 0.34 0.17 – 0.69 0.003
kid2p [Y] 3.99 2.72 – 5.85 <0.001
mom25p [Y] 0.89 0.64 – 1.24 0.486
ord [23] 0.88 0.62 – 1.24 0.468
ord [46] 1.21 0.79 – 1.84 0.388
ord [7p] 1.35 0.79 – 2.29 0.270
ethn [N] 0.92 0.47 – 1.79 0.800
ethn [S] 0.96 0.59 – 1.59 0.888
momEd [P] 1.36 1.01 – 1.84 0.045
momEd [S] 1.39 0.72 – 2.71 0.330
husEd [P] 1.51 1.10 – 2.07 0.011
husEd [S] 1.46 0.83 – 2.57 0.188
husEd [U] 1.02 0.63 – 1.66 0.932
momWork [Y] 1.32 1.00 – 1.74 0.047
rural [Y] 0.52 0.35 – 0.79 0.002
pcInd81 0.42 0.21 – 0.84 0.014
Random Effects
σ2 3.29
τ00 mom 1.28
τ00 comm 0.97
τ11 comm.kid2pY 0.59
ρ01 comm -0.68
ICC 0.37
N mom 1595
N comm 161
Observations 2159
Marginal R2 / Conditional R2 0.129 / 0.450
# Tidying methods for an rstanarm model
library(broom.mixed)
 # non-varying ("population") parameters
  tidy(M4_stanglmer, conf.int = TRUE, prob = 0.5)
  tidy(M4_stanglmer, conf.int = TRUE, conf.method = "HPDinterval", prob = 0.5)
  # hierarchical sd & correlation parameters
  tidy(M4_stanglmer, effects = "ran_pars")
  # group-specific deviations from "population" parameters
  tidy(M4_stanglmer, effects = "ran_vals")
  # glance method
glance(M4_stanglmer)

7. Summary

In this tutorial, we illustrated how to fit multilevel logistic regression models within a fully Bayesian framework in rstanarm. Rather than performing ML estimation using the glmer() function in lme4, we showed how to perform FB estimation (via the HMC approach) using the stan_glmer() function. stan_glmer()is easy to use as the syntax is similar to that of glmer(). Several advantages of estimating these models using rstanarm rather than the lme4 package have been demonstrated in this tutorial. Here is a summary of some major advantages:

  1. rstanarm provides better uncertainty estimates, than does the lme4 package by performing fully Bayesian inference.

  2. For models with more than one random effect, the lme4 package can only use the Laplace approximation, which may perform poorly, especially for binary responses with small cluster sizes.

  3. rstanarm provides posterior draws of the random effects from the joint posterior, whereas the ranef() function, after ML estimation with glmer(), provides only the conditional posterior modes. Having posterior draws allows us to get

    • the means and the standard deviations of the posterior draws of the random effects, marginal over all other parameters by using summary();
    • the median and MAD of the posterior draws or the random effects by using print();
    • any other summary of the posterior draws, such as differences or rankings of random effects of different clusters, with associated measures of uncertainty.
  4. There are a variety of data visualization strategies available for the rstanarm package:

    • the bayesplot package provides a library of plotting functions to visualize the posterior draws;
    • we can manipulate the plots with the posterior draws retrieved from a full Bayes;
    • the shinystan package provides interactive diagnostics and posterior analyses based on the posterior draws.
  5. We can incorporate prior information by using rstanarm. We showed how weakly informative priors are specified by prior automatic scale adjustments.

In sum, although fitting models by approximate ML in the lme4 package tends to be much faster than fitting the models in rstanarm, the benefits of employing fully Bayesian inference via MCMC in rstanarm outweigh the cost. We hope that this tutorial has provided you with enough information to test this out yourself and enough support to apply what you’ve learned in your own research.

REFERENCE

library(knitcitations)
library(bibtex)
read.bibtex("MLR.bib")
[1] D. M. Blei, A. Kucukelbir, and J. D. McAuliffe. "Variational
Inference: A Review for Statisticians". In: _Journal of the American
Statistical Association_ 112.518 (Apr. 2017), pp. 859-877. ISSN:
1537-274X. DOI: 10.1080/01621459.2017.1285773. <URL:
http://dx.doi.org/10.1080/01621459.2017.1285773>.

[2] A. Gelman, A. Jakulin, M. G. Pittau, et al. "A Weakly Informative
Default Prior Distribution for Logistic and Other Regression Models".
In: _The Annals of Applied Statistics_ 2.4 (2008), pp. 1360-1383. ISSN:
19326157. <URL: http://www.jstor.org/stable/30245139>.

[3] H. Joe. "Accuracy of Laplace approximation for discrete response
mixed models". In: _Computational Statistics &amp; Data Analysis_ 52.12
(2008), pp. 5066-5074. <URL:
https://EconPapers.repec.org/RePEc:eee:csdana:v:52:y:2008:i:12:p:5066-5074>.

  1. ↩︎

LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBNdWx0aWxldmVsIExvZ2lzdGljIFJlZ3Jlc3Npb24gVXNpbmcgKipyc3RhbmFybSoqIg0KYXV0aG9yOg0KICAtIFhpbmd5YW8gWGlhb15beGlhb3hnQGJlcmtlbGV5LmVkdV0NCiAgLSBGZW5nIEppDQogIC0gU29waGlhIFJhYmUtSGVza2V0aA0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19kZXB0aDogMw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUNCiAgICBkZl9wcmludDogcGFnZWQNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgd29yZF9kb2N1bWVudDogZGVmYXVsdA0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQogIGJpYmxpb2dyYXBoeTogTUxSLmJpYg0KYWx3YXlzX2FsbG93X2h0bWw6IHllcw0KLS0tDQoNCiMgKioxIEludHJvZHVjdGlvbioqDQoNClRoaXMgdHV0b3JpYWwgdXNlcyAqKnJzdGFuYXJtKiogdmVyc2lvbiAyLjIxLjEgYW5kIHJlcXVpcmVzIHRoZSBmb2xsb3dpbmcgKipSKiogcGFja2FnZXMuDQoNCmBgYHtyIHBhY2thZ2Ugc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGNvbW1lbnQgPSAiIiwgY2FjaGUgPSBUKQ0KIyBSZXF1aXJlZCBQYWNrYWdlcw0KIyBpbnN0YWxsLnBhY2thZ2VzKCJza2ltciIpICMgT3IgaW5zdGFsbCBTa2ltciBwYWNrYWdlDQpsaWJyYXJ5KGJvb2tkb3duKQ0KbGlicmFyeShza2ltcikNCmxpYnJhcnkobWxtUmV2KQ0KbGlicmFyeShsbWU0KQ0KbGlicmFyeShsbXRlc3QpDQpsaWJyYXJ5KHJzdGFuYXJtKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmBgYA0KDQojIyAqKjEuMSBEYXRhIGV4YW1wbGUqKg0KDQpXZSB3aWxsIGJlIGFuYWx5emluZyB0aGUgZ3VJbW11biBkYXRhc2V0IChQZWJsZXkgZXQgYWwuLCAxOTk2KSBmcm9tIHRoZSAqKm1sbVJldioqIHBhY2thZ2UgaW4gKipSKiouIFRoZSBkYXRhIGluY2x1ZGUgMjE1OSBjaGlsZHJlbiAoMS00IHllYXJzIG9mIGFnZSkgd2hvIGhhZCByZWNlaXZlZCBzb21lIGltbXVuaXphdGlvbi4gVGhlc2UgMjE1OSBjaGlsZHJlbiBjYW1lIGZyb20gMTU5NSBmYW1pbGllcyBsaXZpbmcgaW4gMTYxIGNvbW11bml0aWVzLCB3aXRoIGFuIGF2ZXJhZ2Ugb2YgMS40IGNoaWxkcmVuIHBlciBmYW1pbHkgYW5kIDEzLjQgZmFtaWxpZXMgcGVyIGNvbW11bml0eS4gVGhlICpndUltbXVuKiBkYXRhc2V0IGNvbnNpc3RzIG9mIHRoZSBmb2xsb3dpbmcgMTMgdmFyaWFibGVzOg0KDQotICAgKmtpZCo6IGEgZmFjdG9yIGlkZW50aWZ5aW5nIHRoZSBjaGlsZC4NCi0gICAqbW9tKjogYSBmYWN0b3IgaWRlbnRpZnlpbmcgdGhlIGZhbWlseS4NCi0gICAqY29tbSo6IGEgZmFjdG9yIGlkZW50aWZ5aW5nIHRoZSBjb21tdW5pdHkuDQotICAgKmltbXVuKjogYSBmYWN0b3IgaW5kaWNhdGluZyB3aGV0aGVyIHRoZSBjaGlsZCBoYWQgcmVjZWl2ZWQgYSBjb21wbGV0ZSBzZXQgb2YgaW1tdW5pemF0aW9ucy4gQWxsIGNoaWxkcmVuIGluIHRoaXMgZGF0YSBmcmFtZSBoYWQgcmVjZWl2ZWQgYXQgbGVhc3Qgb25lIGltbXVuaXphdGlvbi4NCi0gICAqa2lkMnAqOiBhIGZhY3RvciBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGNoaWxkIHdhcyAyIHllYXJzIG9mIGFnZSBvciBvbGRlciBhdCB0aGUgdGltZSBvZiB0aGUgc3VydmV5Lg0KLSAgICptb20yNXAqOiBhIGZhY3RvciBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIG1vdGhlciB3YXMgMjUgeWVhcnMgb2YgYWdlIG9yIG9sZGVyLg0KLSAgICpvcmQqOiBhIGZhY3RvciBpbmRpY2F0aW5nIHRoZSBjaGlsZCdzIGJpcnRoJ3Mgb3JkZXIgd2l0aGluIHRoZSBmYW1pbHkgKDAxIC0gZmlyc3QgY2hpbGQsIDIzIC0gc2Vjb25kIG9yIHRoaXJkIGNoaWxkLCA0NiAtIGZvdXJ0aCB0byBzaXh0aCBjaGlsZCBhbmQgN3AgLSBzZXZlbnRoIG9yIGxhdGVyIGNoaWxkKS4NCi0gICAqZXRobio6IGEgZmFjdG9yIGluZGljYXRpbmcgdGhlIG1vdGhlcidzIGV0aG5pY2l0eS4gKEwgLSBMYXRpbm8sIE4gLSBpbmRpZ2Vub3VzIG5vdCBzcGVha2luZyBTcGFuaXNoIGFuZCBTIC0gaW5kaWdlbm91cyBzcGVha2luZyBTcGFuaXNoKS4NCi0gICAqbW9tRWQqOiBhIGZhY3RvciBkZXNjcmliaW5nIHRoZSBtb3RoZXIncyBsZXZlbCBvZiBlZHVjYXRpb24uIChOIC0gbm90IGZpbmlzaGVkIHByaW1hcnksIFAgLSBmaW5pc2hlZCBwcmltYXJ5IGFuZCBTIC0gZmluaXNoZWQgc2Vjb25kYXJ5KS4NCi0gICAqaHVzRWQqOiBhIGZhY3RvciBkZXNjcmliaW5nIHRoZSBodXNiYW5kJ3MgbGV2ZWwgb2YgZWR1Y2F0aW9uLiAoTGV2ZWxzIGFyZSB0aGUgc2FtZSBhcyB0aG9zZSBmb3IgbW9tRWQgcGx1cyBVIC0gdW5rbm93bikuDQotICAgKm1vbVdvcmsqOiBhIGZhY3RvciBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIG1vdGhlciBoYWQgZXZlciB3b3JrZWQgb3V0c2lkZSB0aGUgaG9tZS4NCi0gICAqcnVyYWwqOiBhIGZhY3RvciBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGNvbW11bml0eSdzIGxvY2F0aW9uIGlzIGNvbnNpZGVyZWQgcnVyYWwgb3IgdXJiYW4uDQotICAgKnBjSW5kODEqOiB0aGUgcGVyY2VudGFnZSBvZiBpbmRpZ2Vub3VzIHBvcHVsYXRpb24gaW4gdGhlIGNvbW11bml0eSBhY2NvcmRpbmcgdG8gdGhlIDE5ODEgY2Vuc3VzLg0KDQpgYGB7ciBkYXRhc2V0IGV4YW1wbGV9DQojIFVzZSBleGFtcGxlIGRhdGFzZXQgZnJvbSBtbG1SZXYgcGFja2FnZTogSW1tdW5pemF0aW9uIGluIEd1YXRlbWFsYQ0KZGF0YShndUltbXVuLCBwYWNrYWdlID0gIm1sbVJldiIpDQpzdHIoZ3VJbW11bikNCmBgYA0KDQo8cD4NCg0KSW4gdGhpcyBleGFtcGxlLCB3ZSBhcmUgZ29pbmcgdG8gbW9kZWwgdGhlIHByb2JhYmlsaXR5IHRoYXQgdGhlc2UgMjE1OSBjaGlsZHJlbiBoYWQgcmVjZWl2ZWQgYSBmdWxsIHNldCBvZiBpbW11bml6YXRpb25zICoqaW1tdW4qKiBhcyBhIGZ1bmN0aW9uIG9mIGluZGl2aWR1YWwgY2hhcmFjdGVyaXN0aWNzIHN1Y2ggYXMgKipraWQycCoqLCAqKm1vbTI1cCoqLCBhbmQgKipvcmQqKiwgZmFtaWx5IGNoYXJhY3RlcmlzdGljcyBzdWNoIGFzICoqZXRobioqLCAqKm1vbUVkKiosICoqaHVzRWQqKiwgYW5kICoqbW9tV29yayoqLCBhbmQgY29tbXVuaXR5IGNoYXJhY3RlcmlzdGljcyBzdWNoIGFzICoqcnVyYWwqKiBhbmQgKipwY0luZDgxKiouDQoNCmBgYHtyIHNhbXBsZSBzaXplfQ0KIyBDb3VudCB1bmlxdWUgY29tbXVuaXRpZXMsIGZhbWlsaWVzLCBhbmQga2lkcw0KSyA8LSBsZW5ndGgodW5pcXVlKGd1SW1tdW4kY29tbSkpDQpKIDwtIGxlbmd0aCh1bmlxdWUoZ3VJbW11biRtb20pKQ0KSSA8LSBsZW5ndGgodW5pcXVlKGd1SW1tdW4ka2lkKSkNCmBgYA0KDQp8IGtpZHMgfCBtb21zIHwgY29tbXVuaXRpZXMgfA0KfC0tLS0tOnwtLS0tLTp8LS0tLS0tLS0tLS0tOnwNCnwgMjE1OSB8IDE1OTUgfCAgICAgICAgIDE2MSB8DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpUaGUgY29udGVudCBvZiB0aGlzIHR1dG9yaWFsIGlzIGFzIGZvbGxvd3MuIDwhLS0gc3BhY2VzIC0tPg0KDQoxLiAgSW4gKipzZWN0aW9uIDIqKiB3ZSBpbnRyb2R1Y2UgTW9kZWwgMSwgYSByYW5kb20taW50ZXJjZXB0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gd2l0aCBsZXZlbC0xIChjaGlsZC1sZXZlbCkgcHJlZGljdG9ycy48IS0tIHNwYWNlcyAtLT4NCg0KICAgIFdlIHVzZSB0aGUgYGdsbWVyKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBsbWU0YCBwYWNrYWdlIHRvIGVzdGltYXRlIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIGJ5IG1heGltdW0gbGlrZWxpaG9vZCAoTUwpIHVzaW5nIGFkYXB0aXZlIHF1YWRyYXR1cmUuIFRoZW4gd2UgdXNlIHRoZSBgcmFuZWYoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGxtZTRgIHBhY2thZ2UgdG8gYXNzaWduIHZhbHVlcyB0byB0aGUgcmFuZG9tIGludGVyY2VwdHMgZm9yIGluZGl2aWR1YWwgZmFtaWxpZXMgYnkgZW1wbG95aW5nICoqZW1waXJpY2FsIEJheWVzIChFQikqKi4gU3BlY2lmaWNhbGx5LCB0aGUgcHJlZGljdGlvbnMgYXJlIHRoZSBtb2RlcyBvZiB0aGUgY29uZGl0aW9uYWwgcG9zdGVyaW9yIGRlbnNpdGllcyBvZiB0aGUgcmFuZG9tIGludGVyY2VwdHMsIGNvbmRpdGlvbmFsIG9uIHRoZSBwYXJhbWV0ZXJzIGJlaW5nIGVxdWFsIHRvIHRoZSBNTEVzLiA8IS0tIHNwYWNlcyAtLT4NCg0KMi4gIEluICoqc2VjdGlvbiAzKiosIHdlIGVtcGxveSBhICoqZnVsbHkgQmF5ZXNpYW4gKEZCKSoqIGFwcHJvYWNoIHRvIG9idGFpbiBlc3RpbWF0ZXMgKHBvc3RlcmlvciBtZWFucyBhbmQgbWVkaWFucykgYW5kIGFzc29jaWF0ZWQgbWVhc3VyZXMgb2YgdW5jZXJ0YWludHkgZm9yIGFsbCBwYXJhbWV0ZXJzLCBpbmNsdWRpbmcgdGhlIHJhbmRvbSBpbnRlcmNlcHRzLCBmb3IgTW9kZWwgMS4gV2UgY29tcGFyZSBNTCBlc3RpbWF0ZXMgb2YgbW9kZWwgcGFyYW1ldGVycyBhbmQgRUIgcHJlZGljdGlvbnMgb2YgcmFuZG9tIGludGVyY2VwdHMgd2l0aCB0aGVpciBGQiBjb3VudGVycGFydHMuIDwhLS0gc3BhY2VzIC0tPg0KDQozLiAgSW4gKipzZWN0aW9uIDQqKiwgd2UgZXh0ZW5kIE1vZGVsIDEgdG8gaW5jbHVkZSBub3QganVzdCBsZXZlbC0xIGNvdmFyaWF0ZXMgYnV0IGFsc28gbGV2ZWwtMiBjb3ZhcmlhdGVzLCBhcyB3ZWxsIGFzIGNyb3NzLWxldmVsIGludGVyYWN0aW9ucyAoTW9kZWwgMiksIFdlIGZpdCBNb2RlbCAyIHVzaW5nIGJvdGggdGhlICoqcnN0YW5hcm0qKiBwYWNrYWdlIGFuZCB0aGUgKipsbWU0KiogcGFja2FnZS4gSW4gYWRkaXRpb24sIHdlIHNob3cgaG93IHRvIGFjY2VzcyBhIGxpYnJhcnkgb2YgcGxvdHRpbmcgZnVuY3Rpb25zIGZvciBlc3RpbWF0ZXMgZnJvbSB0aGUgKipyc3RhbmFybSoqIHBhY2thZ2UuPCEtLSBzcGFjZXMgLS0+DQoNCjQuICBJbiAqKnNlY3Rpb24gNSoqLCB3ZSBmaXQgYSB0aHJlZS1sZXZlbCByYW5kb20taW50ZXJjZXB0IG1vZGVsIChNb2RlbCAzKSB3aXRoIGxldmVsLTEsIGxldmVsLTIsIGFuZCBsZXZlbC0zIHByZWRpY3RvcnMgdXNpbmcgYm90aCBwYWNrYWdlcy48IS0tIHNwYWNlcyAtLT4NCg0KNS4gIEluICoqc2VjdGlvbiA2KiosIHdlIGZpdCBvdXIgbGFzdCBtb2RlbCAoTW9kZWwgNCksIHdoaWNoIGlzIGEgdGhyZWUtbGV2ZWwgcmFuZG9tLWNvZWZmaWNpZW50IG1vZGVsLCB1c2luZyBib3RoIHBhY2thZ2VzLg0KDQpUbyBhZGRyZXNzIHBvdGVudGlhbCBjb25mdXNpb24sIHdlIGNsYXJpZnkgaG93IHRvIHVzZSB0aGUgdGVybXMgYXBwcm9wcmlhdGVseSBmb3IgdGhlIGRpZmZlcmVudCBhcHByb2FjaGVzLg0KDQotICAgKlJlbWFyayAxKjogKipFc3RpbWF0aW9uKiogdnMuICoqUHJlZGljdGlvbioqLiBBbHRob3VnaCBib3RoIGZyZXF1ZW50aXN0cyBhbmQgQmF5ZXNpYW5zIHVzZSB0aGUgdGVybSAiZXN0aW1hdGlvbiIgd2hlbiB0aGV5IGVzdGltYXRlIG1vZGVsIHBhcmFtZXRlcnMsIGZyZXF1ZW50aXN0cyBkbyBub3Qgc2F5ICJlc3RpbWF0aW9uIiB3aGVuIGFzc2lnbmluZyB2YWx1ZXMgdG8gcmFuZG9tIGVmZmVjdHMgYmVjYXVzZSB0aGV5IGRvIG5vdCB2aWV3IHJhbmRvbSBlZmZlY3RzIGFzIG1vZGVsIHBhcmFtZXRlcnMuIFRoZXkgaW5zdGVhZCB1c2UgdGhlIHRlcm0gInByZWRpY3Rpb24iLg0KDQotICAgKlJlbWFyayAyKjogV2hldGhlciAqKm1vZGVsIHBhcmFtZXRlcnMqKiBpbmNsdWRlICoqcmFuZG9tIGludGVyY2VwdHMqKi4gQmF5ZXNpYW5zIHRyZWF0ICRcemV0YV9qJCBhcyByYW5kb20gcGFyYW1ldGVycywgd2hlcmVhcyBmcmVxdWVudGlzdHMgdHJlYXQgJFx6ZXRhX2okIGFzIHJhbmRvbSB2YXJpYWJsZXMsIHNpbmNlIGZyZW5xdWVudGlzdHMgZG8gbm90IGFjY2VwdCAicmFuZG9tIiBwYXJhbWV0ZXJzLiBUaGVyZWZvcmUsIGZyZXF1ZW50aXN0cyBjYWxsIHRoZW0gbGF0ZW50IHZhcmlhYmxlcyBvciByYW5kb20gZWZmZWN0cy4gRm9yIEJheWVzaWFucywgYm90aCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyAkXGJldGEkcyBhbmQgcmFuZG9tIGludGVyY2VwdHMgJFx6ZXRhX2okIGFyZSBwYXJhbWV0ZXJzLCBidXQgYW4gaW1wb3J0YW50IGRpc3RpbmN0aW9uIGJldHdlZW4gdGhlbSBpcyB0aGF0IHRoZSBpbnRlcmNlcHRzICRcemV0YV97an0kIHZhcnkgYmV0d2VlbiBjbHVzdGVycyBhbmQgYXJlIHRoZXJlZm9yZSBzb21ldGltZXMgcmVmZXJyZWQgdG8gYXMgdmFyeWluZyBpbnRlcmNlcHRzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAqKjIgTGlrZWxpaG9vZCBpbmZlcmVuY2UgdXNpbmcgYGdsbWVyKClgKioNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBzdGFydCB3aXRoIGEgdHdvLWxldmVsIHJhbmRvbS1pbnRlcmNlcHQgbW9kZWwgd2l0aCBsZXZlbC0xIHByZWRpY3RvcnMgKE1vZGVsIDEpLg0KDQojIyAqKjIuMSBNb2RlbCAxOiBUd28tbGV2ZWwgcmFuZG9tIGludGVyY2VwdCBtb2RlbCB3aXRoIGxldmVsLTEgcHJlZGljdG9ycyoqDQoNCiMjIyAqKjIuMS4xIE1vZGVsIHNwZWNpZmljYXRpb24qKg0KDQpJbiB0aGUgZmlyc3QgbW9kZWwsIHdlIGluY2x1ZGUgYSBmYW1pbHktc3BlY2lmaWMgcmFuZG9tIGludGVyY2VwdCAkXHpldGFfaiQgaW4gdGhlIGxpbmVhciBwcmVkaWN0b3IgdG8gb2J0YWluIGEgcmFuZG9tLWludGVyY2VwdCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLiBUaGUgcmFuZG9tIGVmZmVjdHMgcmVwcmVzZW50IHVub2JzZXJ2ZWQgaGV0ZXJvZ2VuZWl0eSBiZXR3ZWVuIGZhbWlsaWVzIChsZXZlbC0yKSBhbmQgaW5kdWNlIGRlcGVuZGVuY2UgYmV0d2VlbiBjaGlsZHJlbiB3aXRoaW4gZmFtaWxpZXMgKGxldmVsLTEpLiBUaGUgbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXM6ICQkXHRleHRybXtsb2dpdH1ce1ByKGltbXVuX3tpan09MXxcYm9sZHN5bWJvbHt4fV97aWp9LCBcemV0YV9qKVx9ID0gXGJldGFfMSArIFxiZXRhXzIga2lkMnBfe2lqfSArIFxiZXRhXzMgbW9tMjVwX3tpan0gK1xiZXRhXzQgb3JkMjNfe2lqfSArXGJldGFfNSBvcmQ0Nl97aWp9ICtcYmV0YV82IG9yZDdwX3tpan0rIFx6ZXRhX2ogJCQgd2hlcmUgdGhlIHJhbmRvbSBpbnRlcmNlcHRzICRcemV0YV9qfFxib2xkc3ltYm9se3h9X3tpan0gXHNpbSBOKDAsIFxwc2kpJCBhcmUgYXNzdW1lZCB0byBiZSBpbmRlcGVuZGVudCBvZiBlYWNoIG90aGVyLCBpZGVudGljYWxseSBkaXN0cmlidXRlZCBhY3Jvc3MgZmFtaWxpZXMgJGokLCBhbmQgaW5kZXBlbmRlbnQgb2YgdGhlIGNvdmFyaWF0ZXMgJFxib2xkc3ltYm9se3h9X3tpan0kLg0KDQpUaGlzIGlzIGFuIGV4YW1wbGUgb2YgYSAqZ2VuZXJhbGl6ZWQgbGluZWFyIG1peGVkIG1vZGVsKiAoR0xNTSkgYmVjYXVzZSBpdCBpcyBhIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCB3aXRoIG1peGVkIChmaXhlZCBhbmQgcmFuZG9tKSBlZmZlY3RzLCBpLmUuLCBmaXhlZCBlZmZlY3RzICRcYmV0YV8xJCB0byAkXGJldGFfNiQgYW5kIGEgcmFuZG9tIGVmZmVjdCAkXHpldGFfaiQuDQoNClVzaW5nIHRoZSBsYXRlbnQtcmVzcG9uc2UgZm9ybXVsYXRpb24sIHRoZSBtb2RlbCBjYW4gZXF1aXZhbGVudGx5IGJlIHdyaXR0ZW4gYXMgJCQgeV4qX3tpan0gPSBcYmV0YV8xICsgXGJldGFfMiBraWQycF97aWp9ICsgXGJldGFfMyBtb20yNXBfe2lqfSArXGJldGFfNCBvcmQyM197aWp9ICtcYmV0YV81IG9yZDQ2X3tpan0gK1xiZXRhXzYgb3JkN3Bfe2lqfSsgXHpldGFfaiArXGVwc2lsb25fe2lqfSQkIHdoZXJlICRcemV0YV9qIFxzaW0gTigwLCBccHNpKSQgYW5kIHRoZSAkXGVwc2lsb25fe2lqfSQgZm9sbG93IHN0YW5kYXJkIGxvZ2lzdGljIGRpc3RyaWJ1dGlvbnMuIFRoZSBiaW5hcnkgcmVzcG9uc2VzICR5X3tpan0kIGFyZSBkZXRlcm1pbmVkIGJ5IHRoZSBsYXRlbnQgY29udGludW91cyByZXNwb25zZXMgdmlhIHRoZSB0aHJlc2hvbGQgbW9kZWwgJCQNCnlfe2lqfSA9IFxsZWZ0XHsNCiAgXGJlZ2lue2FycmF5fVxcDQogICAgMSAmIFxtYm94e2lmIH0gXCAgeV4qX3tpan0gPiAwICBcXA0KICAgICAgICAwICYgXG1ib3h7b3RoZXJ3aXNlfSBcXA0KICBcZW5ke2FycmF5fQ0KXHJpZ2h0Lg0KJCQNCg0KSW4gYm90aCBmb3JtdWxhdGlvbnMgb2YgdGhlIG1vZGVsICh2aWEgYSBsb2dpdCBsaW5rIG9yIGluIHRlcm1zIG9mIGEgbGF0ZW50IHJlc3BvbnNlKSwgaXQgaXMgYXNzdW1lZCB0aGF0IHRoZSByYW5kb20gaW50ZXJjZXB0cyBhcmUgaW5kZXBlbmRlbnQgYWNyb3NzIGZhbWlsaWVzIGFuZCBpbmRlcGVuZGVudCBvZiB0aGUgY292YXJpYXRlcyBvZiBjaGlsZHJlbiAkaSQuIEl0IGlzIGFsc28gYXNzdW1lZCB0aGF0IHRoZSBjb3ZhcmlhdGVzIG9mIG90aGVyIGNoaWxkcmVuIGRvIG5vdCBhZmZlY3QgdGhlIHJlc3BvbnNlIHByb2JhYmlsaXR5IG9mIGEgZ2l2ZW4gY2hpbGQsIGNvbmRpdGlvbmFsIG9uIHRoZSBjaGlsZCdzIG93biBjb3ZhcmlhdGVzIGFuZCB0aGUgcmFuZG9tIGludGVyY2VwdHMgKGtub3duIGFzIHRoZSBzdHJpY3QgZXhvZ2VuZWl0eSBjb25kaXRpb25hbCBvbiB0aGUgcmFuZG9tIGludGVyY2VwdHMpLiBGb3IgdGhlIGxhdGVudCByZXNwb25zZSBmb3JtdWxhdGlvbiwgdGhlICRcZXBzaWxvbl97aWp9JCBhcmUgYXNzdW1lZCB0byBiZSBpbmRlcGVuZGVudCBhY3Jvc3MgYm90aCBjaGlsZHJlbiBhbmQgZmFtaWxpZXMgYW5kIGluZGVwZW5kZW50IG9mIGJvdGggJFx6ZXRhX2okIGFuZCAkeF97aWp9JC4NCg0KQmVsb3cgd2UgdXNlIHRoZSBgZ2xtZXIoKWAgZnVuY3Rpb24gdG8gZXN0aW1hdGUgYSByYW5kb20taW50ZXJjZXB0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCAqa2lkMnAqIChZZXMgb3IgTm8pLCAqbW9tMjVwKiAoWWVzIG9yIE5vKSwgdGhlIHRocmVlICpvcmQqIGR1bW15IHZhcmlhYmxlcyAod2l0aCBsZXZlbCAnMDEnIGFzIHRoZSByZWZlcmVuY2UgZ3JvdXApIGFzIGNoaWxkLWxldmVsIGNhdGVnb3JpY2FsIGNvdmFyaWF0ZXMsIGFuZCBhIHJhbmRvbSBpbnRlcmNlcHQgZm9yICptb20qLCBhIGZhY3RvciBpZGVudGlmeWluZyB0aGUgZmFtaWx5Lg0KDQpUaGUgc3ludGF4IGZvciBgZ2xtZXIoKWAgaXMgc2ltaWxhciB0byB0aGF0IG9mIGBsbWVyKClgIGZvciBsaW5lYXIgbW9kZWxzLiBUaGF0IGlzLCB0aGUgYGdsbWVyKClgIGZ1bmN0aW9uIHdpdGggYGZhbWlseSA9IGdhdXNzaWFuKGxpbmsgPSAiaWRlbnRpdHkiKWAgaXMgZXF1aXZhbGVudCB0byBgbG1lcigpYC4gT3VyIHJhbmRvbS1pbnRlcmNlcHQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgY2FuIGJlIGZpdHRlZCB1c2luZyBgZ2xtZXIoKWAgYnkgc3BlY2lmeWluZyBhIGxvZ2l0IGxpbmsgYW5kIGEgYmlub21pYWwgZGlzdHJpYnV0aW9uIHdpdGggdGhlIGBmYW1pbHkgPSBiaW5vbWlhbCgibG9naXQiKWAgb3B0aW9uLiBUaGUgcGFyYW1ldGVycyBhcmUgZXN0aW1hdGVkIGJ5IGFwcHJveGltYXRlIG1heGltdW0gbGlrZWxpaG9vZCwgYmFzZWQgb24gYW4gYWRhcHRpdmUgR2F1c3NpYW4gSGVybWl0ZSBxdWFkcmF0dXJlIGFwcHJveGltYXRpb24gb2YgdGhlIGxpa2VsaWhvb2QuIFdlIHNwZWNpZnkgdGhlIG51bWJlciBvZiBpbnRlZ3JhdGlvbiBwb2ludHMgd2l0aCB0aGUgYG5BR1E9bmAgb3B0aW9uLiBCeSBjaG9vc2luZyAzMCBpbnRlZ3JhdGlvbiBwb2ludHMsIHdlIG9idGFpbiBhbiBtb3JlIGFjY3VyYXRlIGFwcHJveGltYXRpb24gb2YgdGhlIGxvZy1saWtlbGlob29kIHRoYW4gdGhlIGRlZmF1bHQgTGFwbGFjZSBhcHByb3hpbWF0aW9uLCB3aGljaCBpcyBlcXVpdmFsZW50IHRvIGBuQUdRPTFgLiBUaGUgZGVmYXVsdCBMYXBsYWNlIGFwcHJveGltYXRpb24gaXMgb25lIG9mIHRoZSBmYXN0ZXN0IGNvbXB1dGF0aW9uYWwgbWV0aG9kcywgYnV0IGl0cyBhY2N1cmFjeSBpcyBsb3cuIFdlIGNhbiBzb2x2ZSB0aGUgcHJvYmxlbSBieSBpbmNyZWFzaW5nIHRoZSBudW1iZXIgb2YgcXVhZHJhdHVyZSBwb2ludHMgKGkuZS4sIHRoZSBhZGFwdGl2ZSBHYXVzc2lhbiBIZXJtaXRlIHF1YWRyYXR1cmUgYXBwcm94aW1hdGlvbikuIEZyb20gdGhlIG5leHQgUiBjaHVuaywgd2UgY2FuIHNlZSB0aGUgZGlmZmVyZW5jZSBpbiBwb2ludCBlc3RpbWF0ZXMgb2YgbW9kZWwgcGFyYW1ldGVycyBiZXR3ZWVuIHRoZSBtb2RlbCBlc3RpbWF0ZWQgdXNpbmcgdGhlIGFkYXB0aXZlIEdhdXNzaWFuIEhlcm1pdGUgcXVhZHJhdHVyZSBhcHByb3hpbWF0aW9uIGFuZCB0aGUgbW9kZWwgZXN0aW1hdGVkIHVzaW5nIHRoZSBMYXBsYWNlIGFwcHJveGltYXRpb24gaXMgbGFyZ2UuIEhvd2V2ZXIsIGZvciBtb2RlbHMgd2l0aCBtb3JlIHRoYW4gb25lIHJhbmRvbSBlZmZlY3QsIHRoZSBgbG1lNGAgcGFja2FnZSBzdXBwb3J0cyBvbmx5IHRoZSBMYXBsYWNlIGFwcHJveGltYXRpb24sIHdoaWNoIG1heSBwZXJmb3JtIHBvb3JseS4NCg0KVG8gc29sdmUgdGhpcyBsaW1pdGF0aW9uLCB0aGUgYEdMTU1hZGFwdGl2ZWAgcGFja2FnZSBjYW4gYmUgZW1wbG95ZWQgdG8gZml0IG1vZGVscyB3aXRoIG11bHRpcGxlIHJhbmRvbSBlZmZlY3RzIChlLmcuLCByYW5kb20gaW50ZXJjZXB0cywgcmFuZG9tIHNsb3BlcywgYW5kIHJhbmRvbSBxdWFkcmF0aWMgc2xvcGVzKS4gSG93ZXZlciwgdGhpcyBwYWNrYWdlIGNhbiBvbmx5IGZpdCBnZW5lcmFsaXplZCBsaW5lYXIgbWl4ZWQgbW9kZWxzIGZvciBhIHNpbmdsZSBncm91cGluZyBmYWN0b3IuIEluIG90aGVyIHdvcmRzLCBhbHRob3VnaCB0aGlzIHBhY2thZ2UgaW1wbGVtZW50cyBNTEUgd2l0aCBhZGFwdGl2ZSBHYXVzc2lhbiBxdWFkcmF0dXJlLCBpdCBjYW4gb25seSBkbyBzbyBmb3IgdHdvLWxldmVsIG1vZGVscy4gSW4gdGhpcyB0dXRvcmlhbCwgd2UgZml0IGEgdGhyZWUtbGV2ZWwsIHJhbmRvbS1jb2VmZmljaWVudCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGluICoqc2VjdGlvbiA2KiogdXNpbmcgdGhlIGByc3RhbmFybWAgcGFja2FnZSwgd2hpY2ggc29sdmVzIHRoaXMgcHJvYmxlbS4NCg0KYGBge3J9DQojIEFkYXB0aXZlIEdhdXNzLUhlcm1pdGUgUXVhZHJhdHVyZSwgbkFHUSA9IDMwDQpNMSA8LSBnbG1lcihpbW11biB+IGtpZDJwICsgbW9tMjVwICsgb3JkICsgKDEgfCBtb20pLCAgDQogICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCgibG9naXQiKSwgDQogICAgICAgICAgICBkYXRhID0gZ3VJbW11biwgY29udHJvbCA9IGdsbWVyQ29udHJvbChvcHRpbWl6ZXIgPSAiYm9ieXFhIiksDQogICAgICAgICAgICBuQUdRID0gMzApDQoNCiMgTGFwbGFjZSBhcHByb3hpbWF0aW9uDQpNMV9MYSA8LSBnbG1lcihpbW11biB+IGtpZDJwICsgbW9tMjVwICsgb3JkICsgKDEgfCBtb20pLCAgDQogICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCgibG9naXQiKSwgDQogICAgICAgICAgICBkYXRhID0gZ3VJbW11biwgY29udHJvbCA9IGdsbWVyQ29udHJvbChvcHRpbWl6ZXIgPSAiYm9ieXFhIiksDQogICAgICAgICAgICBuQUdRID0gMSkNCiMgQ29tcGFyaW5nIHBhcmFtZXRlciBlc3RpbWF0ZXMgYmV0d2VlbiB0aGUgbW9kZWwgZXN0aW1hdGVkIGJ5IHVzaW5nIGFkYXB0aXZlIEdhdXNzaWFuIEhlcm1pdGUgcXVhZHJhdHVyZSBhcHByb3hpbWF0aW9uIGFuZCB0aGUgbW9kZWwgZXN0aW1hdGVkIGJ5IExhcGxhY2lhbiBhcHByb3hpbWF0aW9uIG9mIHRoZSBsaWtlbGlob29kLg0Kc3VtbWFyeShNMV9MYSwgY29yciA9IEZBTFNFKQ0Kc3VtbWFyeShNMSwgY29yciA9IEZBTFNFKQ0KYGBgDQoNClRoZSBgc3VtbWFyeSgpYCBmdW5jdGlvbiBkaXNwbGF5cyB0aGUgZXN0aW1hdGVkIGZpeGVkIGVmZmVjdHMgYW5kIGFzc29jaWF0ZWQgc3RhbmRhcmQgZXJyb3JzLiBXZSBhcHBseSB0aGlzIGZ1bmN0aW9uIHdpdGggbW9kZWxzIGBNMV9MYWAgYW5kIGBNMWAgdG8gY29tcGFyZSBlc3RpbWF0ZXMgYW5kIGFzc29jaWF0ZWQgc3RhbmRhcmQgZXJyb3JzIGZyb20gdGhlIExhcGxhY2UgYXBwcm94aW1hdGlvbiBhbmQgdGhlIGFkYXB0aXZlIEdhdXNzaWFuIEhlcm1pdGUgcXVhZHJhdHVyZSBhcHByb3hpbWF0aW9uLiBDb21wYXJpc29ucyBzaG93IHRoYXQgdGhlIGZpeGVkIGVmZmVjdCBlc3RpbWF0ZXMgZnJvbSBgTTFgIGFyZSBjbG9zZXIgdG8gb25lICh3aXRoIHNtYWxsZXIgc3RhbmRhcmQgZXJyb3JzKSB0aGFuIGBNMV9MYWAuDQoNCldlIHRoZW4gdXNlIHRoZSBgcHJpbnQoKWAgZnVuY3Rpb24sIHdoaWNoIGdpdmVzIGEgbW9yZSBjb25jaXNlIHN1bW1hcnkgb2YgdGhlIHBhcmFtZXRlcnMgdGhhbiB0aGUgYHN1bW1hcnkoKWAgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KcHJpbnQoTTEsIGNvcnIgPSBGQUxTRSkNCmBgYA0KDQpGcm9tIHRoZSBvdXRwdXQ6DQoNCi0gICBVbmRlciB0aGUgKipGaXhlZCBFZmZlY3RzKiogcGFydCBvZiB0aGUgYE0xYCBvdXRwdXQsIHdlIG9idGFpbiB0aGUgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGluIHRoZSB1c3VhbCBmb3JtYXQuIFRvIGludGVycHJldCB0aGUgcG9pbnQgZXN0aW1hdGVzIG9mIHRoZSBmaXhlZCBwYXJ0LCB3ZSBjYW4gc2F5IHRoYXQgdGhlIGxvZyBvZGRzIG9mIHJlY2VpdmluZyB0aGUgZnVsbCBzZXQgb2YgaW1tdW5pemF0aW9ucyBmb3IgZmlyc3QtYm9ybiBjaGlsZHJlbiB3aG8gd2VyZSBsZXNzIHRoYW4gMiB5ZWFycyBvbGQgd2l0aCBhIG1vdGhlciB3aG8gd2FzIGxlc3MgdGhhbiAyNSB5ZWFycyBvbGQgaXMgZXN0aW1hdGVkIGFzICRcaGF0e1xiZXRhXzF9PS0xLjQ5NCQuICRcaGF0e1xiZXRhXzJ9PTEuNjgkIGluZGljYXRlcyB0aGF0IHRoZSBlc3RpbWF0ZWQgY29uZGl0aW9uYWwgbG9nIG9kZHMgZm9yIGEgZ2l2ZW4gZmFtaWx5IGFyZSBoaWdoZXIgaWYgdGhlIGNoaWxkcmVuIHdlcmUgMiB5ZWFycyBvZiBhZ2Ugb3Igb2xkZXIsIGFmdGVyIGNvbnRyb2xsaW5nIGZvciBhbGwgb3RoZXIgY292YXJpYXRlcy4gSW5zdGVhZCBvZiBjb25zaWRlcmluZyBjaGFuZ2VzIGluIGxvZyBvZGRzLCB3ZSBjYW4gb2J0YWluIHRoZSBleHBvbmVudGlhdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIHRvIG1ha2UgdGhlIHJlc3VsdHMgbW9yZSBpbnRlcnByZXRhYmxlOg0KDQpgYGB7cn0NCiMgZGlzcGxheSBvZGRzIHJhdGlvcw0KZXhwKGZpeGVmKE0xKSkNCmBgYA0KDQotICAgRm9yIHRoZSAqKlJhbmRvbSBFZmZlY3RzKiogb2YgdGhlIG1vZGVsIG91dHB1dCwgdGhlIGVzdGltYXRlZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHQgJFxzcXJ0e1xoYXR7XHBzaX19JCBpcyAyLjYxNS4NCg0KIyMjICoqMi4xLjIgQXNzaWduaW5nIHZhbHVlcyB0byByYW5kb20gZWZmZWN0cyoqDQoNCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2FudCB0byBhc3NpZ24gdmFsdWVzIHRvIHRoZSByYW5kb20gaW50ZXJjZXB0cyAkXHpldGFfaiQgZm9yIGluZGl2aWR1YWwgZmFtaWxpZXMgJGokLiBCeSB1c2luZyB0aGUgYHJhbmVmKClgIGZ1bmN0aW9uLCB3ZSBjYW4gb2J0YWluIHRoZSBlbXBpcmljYWwgQmF5ZXMgbW9kYWwgKE1BUCkgcHJlZGljdGlvbnMgb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHRzIGZvciBmYW1pbGllcy4gV2UgbmFtZSB0aGUgTUFQIHByZWRpY3Rpb25zIGBFQl9QTW9kZWAgYW5kIHRoZSBzdGFuZGFyZCBlcnJvcnMgYE1BUF9TRWAgaW4gdGhlIGZvbGxvd2luZyBSIGNodW5rLiBUaGUgTUFQIHByZWRpY3Rpb25zIGFyZSBiYXNlZCBvbiB0aGUgY29uZGl0aW9uYWwgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmFuZG9tIGludGVyY2VwdHMsIGNvbmRpdGlvbmFsIG9uIGFsbCBtb2RlbCBwYXJhbWV0ZXJzIGJlaW5nIGVxdWFsIHRvIHRoZWlyIE1MIGVzdGltYXRlcywgJFAoXHpldGFfanx5X3sxan0sIC4uLiwgeV97bl97an1qfSwgXGJvbGRzeW1ib2x7WH1fe2p9O1xib2xkc3ltYm9se1xoYXRcYmV0YX1ee01MfV9qLFxoYXRccHNpXntNTH0pJC4gVGhlIHN0YW5kYXJkIGVycm9ycyBgTUFQX1NFYCBhcmUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbnMgb2YgYSBub3JtYWwgZGVuc2l0eSB3aG9zZSBsb2dhcml0aG0gaGFzIHRoZSBzYW1lIGN1cnZhdHVyZSBhdCB0aGUgbW9kZSBhcyB0aGUgY29uZGl0aW9uYWwgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbi4NCg0KYGBge3J9DQojIGRpc3BsYXkgcmFuZG9tIGludGVyY2VwdHMgYXNzaWduZWQgZm9yIHRoZSBjb3JyZXNwb25kaW5nIG1vbXMNCmhlYWQocmFuZWYoTTEpJG1vbSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlPUZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgICAgd2FybmluZz1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgY29tbWVudD1OQSwNCiAgICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBUKSANCiMgZGlzcGxheSB0aGUgTUFQIHByZWRpY3Rpb25zDQpFQl9QTW9kZSA8LSByYW5lZihNMSwgY29uZFZhciA9IFRSVUUpDQojIGRpc3BsYXkgdGhlIHN0YW5kYXJkIGVycm9ycw0KTUFQX1NFIDwtIHNxcnQoYXR0cihFQl9QTW9kZVtbMV1dLCAicG9zdFZhciIpWzEsICwgXSkNCmhlYWQoTUFQX1NFKQ0KYGBgDQoNCkl0IGlzIGFsc28gdXNlZnVsIHRvIGNvbXBhcmUgdGhlIGNvbmRpdGlvbmFsIG1vZGVzIHZpc3VhbGx5LCB3aGljaCB3ZSBkbyB3aXRoIHRoZSBjYXRlcnBpbGxhciBwbG90IGJlbG93LiBUaGUgYmx1ZSBkb3RzIGFyZSB0aGUgY29uZGl0aW9uYWwgbW9kZXMgd2l0aCA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbHMgKHVzaW5nICRccG0gMS45NiQgc3RhbmRhcmQgZXJyb3IpLiBXZSBkaXNwbGF5IHRoZSByYW5kb20gZWZmZWN0cyBpbiBvcmRlciBmcm9tIHRoZSBzbWFsbGVzdCB0byB0aGUgbGFyZ2VzdC4gQXMgdGhlcmUgYXJlIDE5NTkgZmFtaWxpZXMsIHdlIHN1cHByZXNzIHRoZWlyIElEcyBieSB1c2luZyB0aGUgYHNjYWxlcyA9IGxpc3QoeSA9IGxpc3QoYWx0ZXJuYXRpbmcgPSAwKSlgIGFyZ3VtZW50LiBUaGVyZWZvcmUsIHRoZSB5LWF4aXMgb2YgbW9tJ3MgSUQgaXMgbm90IGRpc3BsYXllZCBpbiB0aGUgY2F0ZXJwaWxsYXIgcGxvdC4NCg0KYGBge3J9DQojIGRpc3BsYXkgdGhlIGNhdGVycGlsbGFyIHBsb3QNCmxhdHRpY2U6OmRvdHBsb3QocmFuZWYoTTEsIHdoaWNoID0gIm1vbSIsIGNvbmRWYXIgPSBUUlVFKSwgc2NhbGVzID0gbGlzdCh5ID0gbGlzdChhbHRlcm5hdGluZyA9IDApKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAqKjMgQmF5ZXNpYW4gaW5mZXJlbmNlIHVzaW5nIGByc3RhbmFybWAqKg0KDQpJbiB0aGUgcHJldmlvdXMgc2VjdGlvbiwgd2UgZml0dGVkIHRoZSByYW5kb20taW50ZXJjZXB0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gYnkgdXNpbmcgYGdsbWVyKClgLCB3aGljaCBlc3RpbWF0ZXMgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgcmFuZG9tIGVmZmVjdHMgYnkgYXBwcm94aW1hdGUgbWF4aW11bSBsaWtlbGlob29kLiBXZSBzcGVjaWZpZWQgMzAgaW50ZWdyYXRpb24gcG9pbnRzIHNvIHRoYXQgdGhlIGFkYXB0aXZlIHF1YWRyYXR1cmUgYXBwcm94aW1hdGlvbiBpcyBzdWZmaWNpZW50bHkgYWNjdXJhdGUuIEhvd2V2ZXIsIHdoZW4gb3VyIG1vZGVscyBoYXZlIG1vcmUgdGhhbiBhIHNpbmdsZSByYW5kb20gZWZmZWN0LCBgZ2xtZXIoKWAgY291bGQgd29yayBwb29ybHkgYXMgaXQgb25seSBzdXBwb3J0cyB0aGUgTGFwbGFjZSBhcHByb3hpbWF0aW9uLiBKb2UgKDIwMDgpIHNob3dlZCB0aGF0IHRoZXNlIExhcGxhY2UgZXN0aW1hdGVzIHdvcmsgZXNwZWNpYWxseSBwb29ybHkgZm9yIG1vZGVscyB3aXRoIGJpbmFyeSByZXNwb25zZXMgYW5kIHNtYWxsIGNsdXN0ZXIgc2l6ZXMsIHdoaWNoIGlzIHRoZSBjYXNlIGhlcmUuIFdlIGFsc28gZm91bmQgbGFyZ2UgZGlzY3JlcGFuY2llcyBiZXR3ZWVuIGVzdGltYXRlcyBiYXNlZCBvbiBMYXBsYWNlIGFuZCBhZGFwdGl2ZSBxdWFkcmF0dXJlIGZvciBNb2RlbCAxLg0KDQpBIGZ1bGx5IEJheWVzaWFuIChGQikgYXBwcm9hY2ggb3ZlcmNvbWVzIHRoZXNlIHByb2JsZW1zIGFuZCBwcm9kdWNlcyBlc3RpbWF0ZXMgZm9yIGFsbCBtb2RlbCBwYXJhbWV0ZXJzLCBpbmNsdWRpbmcgdGhlIHJhbmRvbSBpbnRlcmNlcHRzIHRvZ2V0aGVyIHdpdGggdGhlaXIgYXNzb2NpYXRlZCB1bmNlcnRhaW50aWVzLg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHNob3cgaG93IHRvIGZpdCBhbmQgZXZhbHVhdGUgTW9kZWwgMSBieSB1c2luZyB0aGUgYHJzdGFuYXJtYCBwYWNrYWdlLiBUbyBwcmV2ZW50IHRoZSByZXN1bHRzIGZyb20gYmVpbmcgc3ViamVjdGl2ZSAob3Zlcmx5IGluZmx1ZW5jZWQgYnkgdGhlIHByaW9ycyksIHRoZSBkZWZhdWx0IHByaW9ycyBpbiBgcnN0YW5hcm1gIGFyZSBkZXNpZ25lZCB0byBiZSB3ZWFrbHkgaW5mb3JtYXRpdmUgKHNlZSBbUHJpb3IgRGlzdHJpYnV0aW9ucyBmb3IgcnN0YW5hcm0gTW9kZWxzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcnN0YW5hcm0vdmlnbmV0dGVzL3ByaW9ycy5odG1sKSBmb3IgbW9yZSBkZXRhaWxzKS4gU3BlY2lmeWluZyB3ZWFrbHkgaW5mb3JtYXRpdmUgcHJpb3JzIGNhbiBiZSB1c2VkIGZvciByZWd1bGFyaXppbmcgcGFyYW1ldGVyIGVzdGltYXRlcywgYXZvaWRpbmcgZm9yIGluc3RhbmNlIHNlcGFyYXRpb24gaW4gbG9naXN0aWMgcmVncmVzc2lvbiAoR2VsbWFuIGV0IGFsLCAyMDA4KS4NCg0KIyMgKiozLjEgVXNpbmcgdGhlIGByc3RhbmFybWAgcGFja2FnZSoqDQoNClRoZSBgcnN0YW5hcm1gIHBhY2thZ2UgaXMgYSB3cmFwcGVyIGZvciB0aGUgYHJzdGFuYCBwYWNrYWdlIHRoYXQgZW5hYmxlcyB0aGUgbW9zdCBjb21tb25seSB1c2VkIChtdWx0aWxldmVsKSByZWdyZXNzaW9uIG1vZGVscyB0byBiZSBzcGVjaWZpZWQgYW5kIGZpdCBpbiBgcnN0YW5gIHVzaW5nIGN1c3RvbWFyeSBSIG1vZGVsaW5nIHN5bnRheC4gRWR1Y2F0aW9uYWwgcmVzZWFyY2hlcnMgY2FuIHVzZSB0aGUgQmF5ZXNpYW4gYXBwcm9hY2ggZm9yIG11bHRpbGV2ZWwgbW9kZWxzIHdpdGggb25seSBtaW5pbWFsIGNoYW5nZXMgdG8gdGhlaXIgZXhpc3RpbmcgYGdsbWVyKClgIHN5bnRheC4gV2UgdXNlIHRoZSBgY2hhaW5zPTZgIGFyZ3VtZW50IHRvIHNldCB0aGUgbnVtYmVyIG9mIE1hcmtvdiBjaGFpbnMgdG8gNiBhbmQgdGhlIGBpdGVyPTQwMDBgIGFyZ3VtZW50IHRvIHNldCB0aGUgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgZm9yIGVhY2ggY2hhaW4gdG8gNCwwMDAuIFRoZSBmaXJzdCBoYWxmIG9mIHRoZXNlIGl0ZXJhdGlvbiwgbmFtZWQgd2FybXVwLCB3aWxsIGJlIGRpc2NhcmRlZC4gU2luY2Ugd2Ugc3RhcnQgZWFjaCBjaGFpbiB3aXRoIHNvbWUgYXJiaXRyYXJ5IHN0YXJ0aW5nIHZhbHVlcyBmb3IgdGhlIHBhcmFtZXRlcnMsIHdlIG5lZWQgYSBsb25nIHdhcm0tdXAgcGhhc2UgdG8gcmVhY2ggdGhlIHN0YXRpb25hcnkgZGlzdHJpYnV0aW9uLiBCeSB1c2luZyB0aGUgYHByaW50KClgIGZ1bmN0aW9uLCB3ZSBjYW4gZGlzcGxheSBhIHF1aWNrIHN1bW1hcnkgb2YgdGhlIEZCIGVzdGltYXRlcyBvZiB0aGUgbW9kZWwgcGFyYW1ldGVycywgZXhjbHVkaW5nIHRoZSByYW5kb20gaW50ZXJjZXB0cy4NCg0KYGBge3IgZWNobz1ULCByZXN1bHRzPSdoaWRlJ30NCk0xX3N0YW5nbG1lciA8LSBzdGFuX2dsbWVyKGltbXVuIH4ga2lkMnAgKyBtb20yNXAgKyBvcmQgKyAoMSB8IG1vbSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwoImxvZ2l0IiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGd1SW1tdW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBpdGVyID0gNDAwMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoYWlucyA9IDYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gMzQ5KQ0KYGBgDQoNCmBgYHtyfQ0KIyBkaXNwbGF5IHRoZSBtZWRpYW4gYW5kIHRoZSBtZWRpYW4gYWJzb2x1dGUgZGV2aWF0aW9uIChNQUQpIG9mIHRoZSBwb3N0ZXJpb3IgZHJhd3MNCnByaW50KE0xX3N0YW5nbG1lciwgZGlnaXRzID0gMikNCmBgYA0KDQpUaGUgYHByaW50KClgIGZ1bmN0aW9uIGRpc3BsYXlzIHRoZSAqbWVkaWFuKiBhbmQgdGhlICptZWRpYW4gYWJzb2x1dGUgZGV2aWF0aW9uIChNQUQpKiBvZiB0aGUgcG9zdGVyaW9yIGRyYXdzLiBPbmUgZ3JlYXQgYWR2YW50YWdlIG9mIEJheWVzaWFuIGVzdGltYXRpb24gaXMgdGhhdCB3ZSBjYW4gY2hvb3NlIGFueSBzdW1tYXJpZXMgb2YgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24uIEZvciBleGFtcGxlLCB3ZSBjYW4gdXNlIHRoZSBgc3VtbWFyeSgpYCBmdW5jdGlvbiB0byBvYnRhaW4gdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgbWFyZ2luYWwgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBmb3IgZWFjaCBwYXJhbWV0ZXIgYXMgc2hvd24gYmVsb3cuIEluIGFkZGl0aW9uLCB1c2VycyBjYW4gc3BlY2lmeSB3aGljaCBwYXJhbWV0ZXJzIHRvIGRpc3BsYXkgYnkgdXNpbmcgdGhlIGBwYXJzYCBhcmd1bWVudCBpbiB0aGUgYHN1bW1hcnkoKWAgZnVuY3Rpb24uIE1vcmVvdmVyLCA5NSUgY3JlZGlibGUgaW50ZXJ2YWxzLCB3aGljaCBoYXZlIGEgOTUlIHByb2JhYmlsaXR5IG9mIGNvbnRhaW5pbmcgdGhlIHRydWUgdmFsdWUgb2YgdGhlIHBhcmFtZXRlciBnaXZlbiB0aGUgZGF0YSwgY2FuIGJlIG9idGFpbmVkIGJ5IGNvbnNpZGVyaW5nIHRoZSAkMi41Xnt0aH0kIHRvICQ5Ny41Xnt0aH0kIHBlcmNlbnRpbGVzIG9mIHRoZSBwb3N0ZXJpb3IgZHJhd3MuIEluIGFkZGl0aW9uLCBieSB1c2luZyB0aGUgYHByb2JzYCBhcmd1bWVudCwgd2UgY2FuIHNwZWNpZnkgd2hpY2ggcXVhbnRpbGVzIG9mIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIHRvIGRpc3BsYXkuDQoNCmBgYHtyfQ0KIyBkaXNwbGF5IHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIG1hcmdpbmFsIHBvc3RlcmlvciBkaXN0cmlidXRpb24gb2YgZWFjaCBwYXJhbWV0ZXINCnN1bW1hcnkoTTFfc3RhbmdsbWVyLCANCiAgICAgICAgcGFycyA9IGMoIihJbnRlcmNlcHQpIiwgImtpZDJwWSIsICJtb20yNXBZIiwgIm9yZDQ2IiwgIm9yZDdwIiwgICJiWyhJbnRlcmNlcHQpIG1vbToyXSIsICJiWyhJbnRlcmNlcHQpIG1vbToxODVdIiwgImJbKEludGVyY2VwdCkgbW9tOjE4Nl0iKSwNCiAgICAgICAgcHJvYnMgPSBjKDAuMDI1LCAwLjk3NSksDQogICAgICAgIGRpZ2l0cyA9IDIpDQpgYGANCg0KKipNQ01DIGRpYWdub3N0aWNzKipcDQpVbmRlciBgTUNNQyBkaWFnbm9zdGljc2AgZnJvbSB0aGUgb3V0cHV0LCB2YWx1ZXMgdW5kZXIgYG1jc2VgIHJlcHJlc2VudCBlc3RpbWF0ZXMgb2YgdGhlIE1vbnRlIENhcmxvIHN0YW5kYXJkIGVycm9ycyAoTUNTRXMpIHdoaWNoIHJlcHJlc2VudCB0aGUgdW5jZXJ0YWludHkgZHVlIHRvIGVzdGltYXRpbmcgcG9zdGVyaW9yIGV4cGVjdGF0aW9ucyBieSBzYW1wbGUgbWVhbnMgb2YgTW9udGUgQ2FybG8gZHJhd3MgZnJvbSB0aGUgcG9zdGVyaW9yLiBUaGF0IGlzLCB0aGUgTUNTRXMgcmVwcmVzZW50IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZXN0aW1hdGVzIG9idGFpbmVkIGZvciBhIGdpdmVuIGRhdGFzZXQgYnkgcmVwZWF0ZWRseSB1c2luZyB0aGUgc2FtZSBNQ01DIGFwcHJvYWNoIHRvIGVzdGltYXRlIHRoZSBwYXJhbWV0ZXJzIChidXQgd2l0aCBkaWZmZXJlbnQgc3RhcnRpbmcgdmFsdWVzIGFuZCByYW5kb20gbnVtYmVyIHNlcXVlbmNlcykuIEFkZGl0aW9uYWxseSwgdGhlIE1DTUMgZGlhZ25vc3RpY3MgaW5jbHVkZSBgUmhhdGAgYW5kIHRoZSBlZmZlY3RpdmUgc2FtcGxlIHNpemUsIGBuX2VmZmAuDQoNCioqUmhhdDogcG90ZW50aWFsIHNjYWxlIHJlZHVjdGlvbiBzdGF0aXN0aWMqKlwNCmBSaGF0YCBtZWFzdXJlcyB0aGUgcmF0aW8gb2YgdGhlIGF2ZXJhZ2UgdmFyaWFuY2Ugb2YgdGhlIE1DTUMgaXRlcmF0aW9ucyB3aXRoaW4gZWFjaCBjaGFpbiB0byB0aGUgdmFyaWFuY2Ugb2YgdGhlIHBvb2xlZCBpdGVyYXRpb25zIGFjcm9zcyBhbGwgY2hhaW5zIChhZnRlciBkaXNjYXJkaW5nIHRoZSB3YXJtdXAgaXRlcmF0aW9ucykuIElmIGFsbCBjaGFpbnMgYXJlIGF0IGVxdWlsaWJyaXVtIChoYXZlIHJlYWNoZWQgdGhlIHNhbWUgc3RhdGlvbmFyeSBkaXN0cmlidXRpb24pLCB0aGVzZSB2YXJpYW5jZXMgd2lsbCBiZSB0aGUgc2FtZSBzbyBgUmhhdGAgd2lsbCBlcXVhbCBvbmUuIFRoZSBkZXNpcmVkIHZhbHVlIG9mIFJoYXQgaXMgc21hbGxlciB0aGFuIDEuMS4gQWZ0ZXIgZml0dGluZyBhIEJheWVzaWFuIG1vZGVsLCB3ZSBjYW4gYWxzbyB1c2UgdGhlIGBiYXllc3Bsb3RgIHBhY2thZ2UgdG8gdmlzdWFsaXplIE1DTUMgZGlhZ25vc3RpY3MgKHNlZSBbR2VuZXJhbCBNQ01DIGRpYWdub3N0aWNzXShodHRwczovL21jLXN0YW4ub3JnL2JheWVzcGxvdC9yZWZlcmVuY2UvTUNNQy1kaWFnbm9zdGljcy5odG1sKSBhbmQgW3ZpZ25ldHRlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvYmF5ZXNwbG90L3ZpZ25ldHRlcy92aXN1YWwtbWNtYy1kaWFnbm9zdGljcy5odG1sKSBmb3IgbW9yZSBkZXRhaWxzKS4gSW4gdGhlIG5leHQgUiBjaHVuaywgd2UgdXNlIGEgZ2VuZXJpYyBgcmhhdGAgZnVuY3Rpb24gdG8gZXh0cmFjdCAkXGhhdHtSfSQgZm9yIGVzdGltYXRlcyBmb3IgYWxsIG1vZGVsIHBhcmFtZXRlcnMgYW5kIHJhbmRvbSBlZmZlY3RzIGZyb20gYE0xX3N0YW5nbG1lcmAgYW5kIGRpc3BsYXkgdGhlIGZpcnN0IDEwIGV4dHJhY3RlZCAkXGhhdHtSfSQgdmFsdWVzLiBUaGVuLCBXZSB2aXN1YWxpemUgdGhlICRcaGF0e1J9JCB2YWx1ZXMgZm9yIGFsbCBtb2RlbCBwYXJhbWV0ZXJzIGFuZCByYW5kb20gZWZmZWN0cyBieSB1c2luZyB0aGUgYG1jbWNfcmhhdGAgZnVuY3Rpb246DQoNCmBgYHtyfQ0KbGlicmFyeSgiYmF5ZXNwbG90IikNCnJoYXRzIDwtIHJoYXQoTTFfc3RhbmdsbWVyKQ0Kc3RydWN0dXJlKHJoYXRzWzE6MTBdLCBteV9hdHRyaWJ1dGUgPSAiVGhpcyBpcyBhIHZlY3RvciIpDQpjb2xvcl9zY2hlbWVfc2V0KCJicmlnaHRibHVlIikgIyBzZWUgaGVscCgiY29sb3Jfc2NoZW1lX3NldCIpDQptY21jX3JoYXQocmhhdHMpDQpgYGANCg0KSW4gdGhlIHBsb3QsIHRoZSBwb2ludHMgcmVwcmVzZW50aW5nIHRoZSAkXGhhdHtSfSQgdmFsdWVzIGFyZSBjb2xvcmVkIGJhc2VkIG9uIHRoZWlyIHZhbHVlcyBieSBjdXRvZmYgcG9pbnRzIDEuMSBhbmQgMS4wNS4NCg0KQW5vdGhlciBhZHZhbnRhZ2Ugb2YgYHJzdGFuYXJtYCBpcyB0aGF0LCBpbiBhZGRpdGlvbiB0byBhbGxvd2luZyBhIHJhbmdlIG9mIHBsb3R0aW5nIG9wdGlvbnMgd2l0aCB0aGUgYGJheWVzcGxvdGAgcGFja2FnZSwgaXQgYWxsb3dzIHRoZSB1c2Ugb2YgdGhlIGBzaGlueXN0YW5gIHBhY2thZ2UgdG8gb2J0YWluIGludGVyYWN0aXZlIGRpYWdub3N0aWNzIGFuZCBwb3N0ZXJpb3IgYW5hbHlzaXMgZm9yIHRoZSBCYXllc2lhbiBtb2RlbCAoc2VlIFtzaGlueXN0YW5dKGh0dHA6Ly9tYy1zdGFuLm9yZy9zaGlueXN0YW4vaW5kZXguaHRtbCkpLiBgU2hpbnlTdGFuYCBwcm92aWRlcyBpbnRlcmFjdGl2ZSBwbG90cyBhbmQgdGFibGVzIHRoYXQgYXJlIGhlbHBmdWwgZm9yIGFuYWx5emluZyBhIHBvc3RlcmlvciBzYW1wbGUgYW5kIGNhbiBiZSB1c2VkIHRvIGlkZW50aWZ5IHBvdGVudGlhbCBwcm9ibGVtcyBieSBhc3Nlc3NpbmcgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBNQ01DIGFsZ29yaXRobSBvciB0aGUgc3BlY2lmaWNhdGlvbiBvZiB0aGUgbW9kZWwuDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KHNoaW55c3RhbikNCmxhdW5jaF9zaGlueXN0YW4oTTFfc3RhbmdsbWVyKQ0KYGBgDQoNCioqRWZmZWN0aXZlIHNhbXBsZSBzaXplKioNCg0KYG5fZWZmYCBpcyBhbiBlc3RpbWF0ZSBvZiB0aGUgZWZmZWN0aXZlIG51bWJlciBvZiBpbmRlcGVuZGVudCBkcmF3cyBmcm9tIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIGZvciB0aGUgcGFyYW1ldGVyIG9mIGludGVyZXN0LiBCZWNhdXNlIHRoZSBkcmF3cyB3aXRoaW4gYSBjaGFpbiBhcmUgbm90IGluZGVwZW5kZW50IGlmIHRoZXJlIGlzIGF1dG9jb3JyZWxhdGlvbiwgdGhlIGVmZmVjdGl2ZSBzYW1wbGUgc2l6ZSwgJG5fe2VmZn0kLCB3aWxsIGJlIHNtYWxsZXIgdGhhbiB0aGUgdG90YWwgc2FtcGxlIHNpemUgYmFzZWQgb24gdGhlIG51bWJlciBvZiBpdGVyYXRpb25zLCAkTiQgKGkuZS4sICROPTYsMDAwID0gNlx0ZXh0cm17IGNoYWluc30gXHRpbWVzICgyLDAwMFx0ZXh0cm17IGl0ZXJhdGlvbnN9IC0xLDAwMCBcdGV4dHJteyBpdGVyYXRpb25zIGZvciB3YXJtdXB9KSRpbiB0aGlzIGNhc2UpLg0KDQpUaGVyZWZvcmUsIHRoZSBsYXJnZXIgdGhlIHJhdGlvIG9mICRuX3tlZmZ9JCB0byBOIHRoZSBiZXR0ZXIuIEluIHRoZSBuZXh0IFIgY2h1bmssIHdlIHVzZSB0aGUgYG5lZmZfcmF0aW9gIGZ1bmN0aW9uIHRvIGV4dHJhY3QgdGhlICRuX3tlZmZ9LyBOJCB2YWx1ZXMgYnkgZml0dGluZyB0aGUgdGhlIGBNMV9zdGFuZ2xtZXJgIG1vZGVsLiBUaGVuLCB3ZSB2aXN1YWxpemUgdGhlIHJhdGlvcyBieSB1c2luZyB0aGUgYG1jbWNfbmVmZmAgZnVuY3Rpb24uIFNpbmNlIHRoZSBheGlzIHktYXhpcyB0ZXh0IGlzIG9mZiBieSBkZWZhdWx0IGZvciB0aGUgYWJvdmUgcGxvdCBiZWNhdXNlIHRoZSBgTTFfc3RhbmdsbWVyYCBtb2RlbCBoYXMgdG9vIG1hbnkgcGFyYW1ldGVycy4gVG8gbWFrZSB0aGUgbGFiZWxzIGNsZWFyLCB3ZSBjYW4gdXNlIHRoZSBgeWF4aXNfdGV4dGAgZnVuY3Rpb24gdG8gZGlzcGxheSBuYW1lcyBvZiB0aGUgcGFyYW1ldGVycyB3aXRoIHRoZSBjb25jZXJuaW5nIHJhdGlvcywgJG5fe2VmZn0vIE4kLiBUaGUgbmV4dCBSIGNodW5rIGRpc3BsYXlzIGEgc3Vic2V0IG9mIHBhcmFtZXRlcnMgZm9jdXNpbmcgb24gcmVncmVzc2lvbiBjb2VmZmljaWVudHMgJFxiZXRhJHMgYW5kIHRocmVlIG9mIHRoZSByYW5kb20gaW50ZXJjZXB0cyAkXHpldGFfaiQuDQoNCmBgYHtyfQ0KcmF0aW9zIDwtIG5lZmZfcmF0aW8oTTFfc3RhbmdsbWVyICkNCnJhdGlvc19zcGUgPC0gbmVmZl9yYXRpbyhNMV9zdGFuZ2xtZXIsIHBhcnMgPSBjKCIoSW50ZXJjZXB0KSIsICJraWQycFkiLCAibW9tMjVwWSIsICJvcmQ0NiIsICJvcmQ3cCIsICAiYlsoSW50ZXJjZXB0KSBtb206Ml0iLCAiYlsoSW50ZXJjZXB0KSBtb206MTg1XSIsICJiWyhJbnRlcmNlcHQpIG1vbToxODZdIikpDQptY21jX25lZmYocmF0aW9zX3NwZSkgKyB5YXhpc190ZXh0KGhqdXN0ID0gMSkNCmBgYA0KDQpJbiB0aGUgYWJvdmUgcGxvdCwgdGhlIHBvaW50cyByZXByZXNlbnRpbmcgdGhlIHJhdGlvcywgJG5fe2VmZn0vTiQsIGFyZSBjb2xvcmVkIGJhc2VkIG9uIGN1dG9mZiBwb2ludHMgMC4xIGFuZCAwLjUuDQoNCiMjICoqMy4yIENvbXBhcmlzb25zIGJldHdlZW4gZXN0aW1hdGVzIGZyb20gYHN0YW5fZ2xtZXJgIGFuZCBgZ2xtZXJgKioNCg0KIyMjICoqMy4yLjEgQ29tcGFyaXNvbnMgb2YgcG9pbnQgZXN0aW1hdGVzKioNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBjb21wYXJlIHRoZSBwb2ludCBlc3RpbWF0ZXMgZnJvbSB0aGUgYGdsbWVyKClgIGFuZCBgc3Rhbl9nbG1lcigpYCBmdW5jdGlvbnMgdGhhdCB3ZSBuYW1lIE1MIChtYXhpbXVtIGxpa2VsaWhvb2QpIGFuZCBGQiAoZnVsbHkgQmF5ZXNpYW4pLCByZXNwZWN0aXZlbHkuDQoNCkNvbXBhcmlzb24NCg0KLSAgIEZvciB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMsIHRoZSBGQiBlc3RpbWF0ZSAobWVkaWFuIG9mIHBvc3RlcmlvciBkcmF3cykgb2YgdGhlIHBvcHVsYXRpb24gaW50ZXJjZXB0ICRcaGF0e1xiZXRhXzF9JCBpcyAqKi0xLjUxKiosIHdoaWNoIGlzIGNsb3NlIHRvIHRoZSBNTCBlc3RpbWF0ZSAoKiotMS40OSoqKS4gVGhlIEZCIGVzdGltYXRlcyBvZiB0aGUgY29lZmZpY2llbnRzICRcaGF0e1xiZXRhXzJ9JCB0byAkXGhhdHtcYmV0YV82fSQgZm9yIHRoZSBjb3ZhcmlhdGVzICRraWQycF97aWp9JCwgJG1vbTI1cF97aWp9JCwgJG9yZDIzX3tpan0kLCAkb3JkNDZfe2lqfSQsICRvcmQ3cF97aWp9JCBhcmUgKioxLjcxLCAtMC4xMywgLTAuMzIsIC0wLjE5LCAtMC4xMSoqIHJlc3BlY3RpdmVseSwgd2hpY2ggYXJlIGNsb3NlIHRvIHRoZSBjb3JyZXNwb25kaW5nIE1MIGVzdGltYXRlcyAoKioxLjY4LCAwLjEzLCAtMC4zMSwgLTAuMTgsIC0wLjEwKiopLg0KDQotICAgVGhlIEZCIGVzdGltYXRlICRcc3FydHtcaGF0e1xwc2l9fSQgb2YgdGhlIHJhbmRvbS1pbnRlcmNlcHQgc3RhbmRhcmQgZGV2aWF0aW9uIGlzICoqMi43MioqLCB3aGljaCBpcyBsYXJnZXIgdGhhbiB0aGUgTUwgZXN0aW1hdGUgKCoqMi42MSoqKS4NCg0KSW4gKipzZWN0aW9uIDIuMS4zKiosIHdlIGFzc2lnbmVkIHZhbHVlcyB0byB0aGUgcmFuZG9tIGludGVyY2VwdHMgYnkgdXNpbmcgdGhlIHBvc3RlcmlvciBtb2RlcyAoTUFQKSBvZiB0aGUgKmNvbmRpdGlvbmFsKiBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9ucywgY29uZGl0aW9uYWwgb24gdGhlIHBhcmFtZXRlcnMgYmVpbmcgZXF1YWwgdG8gdGhlaXIgTUwgZXN0aW1hdGVzLiBDb3JyZXNwb25kaW5nIHN0YW5kYXJkIGVycm9ycyB3ZXJlIGJhc2VkIG9uIHRoZSBjdXJ2YXR1cmUgb2YgdGhlIGNvbmRpdGlvbmFsIGxvZy1wb3N0ZXJpb3IuIEluIHRoaXMgc2VjdGlvbiwgYnkgdXNpbmcgdGhlIGBzdW1tYXJ5KClgIGZ1bmN0aW9uIHRvIGRpc3BsYXkgdGhlIEZCIGVzdGltYXRlcywgd2UgYXJlIGFibGUgdG8gZ2V0IHRoZSBtZWFucyBvZiB0aGUgZHJhd3Mgb2YgcGFyYW1ldGVycyBhbmQgcmFuZG9tIGVmZmVjdHMgZnJvbSB0aGVpciAqam9pbnQqIHBvc3RlcmlvciBkaXN0cmlidXRpb24uIE1vcmVvdmVyLCB1c2VycyBjYW4gZGlyZWN0bHkgYWNjZXNzIHRoZSBwb3N0ZXJpb3IgZHJhd3MgKGFzIHNob3duIGluICoqc2VjdGlvbiAzLjIuMyoqKS4gTmV4dCwgd2UgY29tcGFyZSB0aGUgbWFyZ2luYWwgKHdpdGggcmVzcGVjdCB0byB0aGUgb3RoZXIgcGFyYW1ldGVycykgbWVhbiBvZiB0aG9zZSBwb3N0ZXJpb3IgZHJhd3Mgb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHRzIHdpdGggdGhlIHByZWRpY3Rpb25zIGdlbmVyYXRlZCBieSBgcmFuZWYoKWAuDQoNCiMjIyAqKjMuMi4yIEFjY2VzcyB0byBwb3N0ZXJpb3IgZHJhd3MgZGlyZWN0bHkqKg0KDQpgYGB7cn0NCiMgT2J0YWluIGZhbWlseS1sZXZlbCB2YXJ5aW5nIGludGVyY2VwdCB6ZXRhX2oNCiMjIGRyYXdzIGZvciAxNTk1IGZhbWlseS1sZXZlbCBlcnJvcnMgemV0YV9qDQp1X3NpbXMgPC0gYXMubWF0cml4KE0xX3N0YW5nbG1lciwgDQogICAgICAgICAgICAgICAgICAgICByZWdleF9wYXJzID0gImJcXFtcXChJbnRlcmNlcHRcXCkgbW9tXFw6IikNCg0KIyBDb21wdXRlIG1lYW4sIFNELCBtZWRpYW4sIGFuZCA5NSUgY3JlZGlibGUgaW50ZXJ2YWwgb2YgdmFyeWluZyBpbnRlcmNlcHRzDQojIyBQb3N0ZXJpb3IgbWVhbiBhbmQgU0Qgb2YgZWFjaCBhbHBoYQ0KRkJfUE1lYW4gPC0gYXBwbHkoWCA9IHVfc2ltcywgICAgICMgcG9zdGVyaW9yIG1lYW4NCiAgICAgICAgICAgICAgICBNQVJHSU4gPSAyLA0KICAgICAgICAgICAgICAgIEZVTiA9IG1lYW4pDQpGQl9Qc2QgPC0gYXBwbHkoWCA9IHVfc2ltcywgICAgICAgIyBwb3N0ZXJpb3IgU0QNCiAgICAgICAgICAgICAgTUFSR0lOID0gMiwNCiAgICAgICAgICAgICAgRlVOID0gc2QpDQoNCiMjIFBvc3RlcmlvciBtZWRpYW4gYW5kIDk1JSBjcmVkaWJsZSBpbnRlcnZhbA0KUF9xdWFudCA8LSBhcHBseShYID0gdV9zaW1zLCANCiAgICAgICAgICAgICAgICAgTUFSR0lOID0gMiwgDQogICAgICAgICAgICAgICAgIEZVTiA9IHF1YW50aWxlLCANCiAgICAgICAgICAgICAgICAgcHJvYnMgPSBjKDAuMDI1LCAwLjUwLCAwLjk3NSkpDQoNCkZCX1BxdWFudCA8LSBkYXRhLmZyYW1lKHQoUF9xdWFudCkpDQpuYW1lcyhGQl9QcXVhbnQpIDwtIGMoIlEyLjUiLCAiUTUwIiwgIlE5Ny41IikNCg0KIyMgQ29tYmluZSBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgcG9zdGVyaW9yIHNpbXVsYXRpb24gZHJhd3MNCkZCPC0gZGF0YS5mcmFtZShGQl9QTWVhbiwgRkJfUHNkICwgRkJfUHF1YW50KQ0Kcm91bmQoaGVhZChGQiksIDIpDQoNCmBgYA0KDQpUaGUgRkIgdGFibGUgYWJvdmUgc2hvd3MgdGhlIG1lYW5zIG9mIHRoZSBwb3N0ZXJpb3IgZHJhd3Mgb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHRzIGZvciB0aGUgZmlyc3QgZm91ciBmYW1pbGllcyBpbiB0aGUgYEZCX1BNZWFuYCBjb2x1bW4sIHRoZSBzdGFuZGFyZCBkZXZpYXRpb25zIGluIHRoZSBgRkJfUHNkYCBjb2x1bW4sIGFuZCB0aGUgbWVkaWFucyBhbmQgOTUlIGNyZWRpYmxlIGludGVydmFscyBpbiB0aGUgYFEyLjVgLCBgUTUwYCwgYFE5Ny41YCBjb2x1bW5zLCByZXNwZWN0aXZlbHkuDQoNCmBgYHtyfQ0KDQojIE9idGFpbiBlbXBpcmljYWwgQmF5ZXMgbW9kYWwgcHJlZGljdGlvbiBleHRyYWN0ZWQgZnJvbSB0aGUgcmFuZWYgZnVuY3Rpb24gaW4gdGhlIHNlY3Rpb24gMi4xLjIgKHNvbWV0aW1lcyBjYWxsZWQgbW9kYWwgYSBwb3N0ZXJpb3IgKE1BUCkgcHJlZGljdGlvbikNCk1BUCA8LSBFQl9QTW9kZVsxXQ0KIyBPYnRhaW4gdGhlIHN0YW5kYXJkIGVycm9ycyBmb3IgdGhlIHJhbmRvbS1lZmZlY3QgcHJlZGljdGlvbg0KTUFQX3NlIDwtIHNxcnQoYXR0cihFQl9QTW9kZVtbMV1dLCAicG9zdFZhciIpWzEsICwgXSkNCg0KDQppbW11bl9OPC1ndUltbXVuICU+JQ0KICAgICBmaWx0ZXIoaW1tdW4gPT0gIk4iLCANCiAgICAgICAgICAgICkgJT4lDQogICAgIGdyb3VwX2J5KG1vbSkgJT4lDQogICAgIGNvdW50KGltbXVuID09ICJOIiwgbmFtZSA9ICJudW1fTiIsIC5kcm9wID0gRkFMU0UpDQoNCmltbXVuX1k8LWd1SW1tdW4gJT4lDQogICAgIGZpbHRlcihpbW11biA9PSAiWSIsIA0KICAgICAgICAgICAgKSAlPiUNCiAgICAgZ3JvdXBfYnkobW9tKSAlPiUNCiAgICAgY291bnQoaW1tdW4gPT0gIlkiLCBuYW1lID0gIm51bV9ZIiwgLmRyb3AgPSBGQUxTRSkgDQogICAgDQppbW11bl9kaWZmPC1pbW11bl9ZWzNdLWltbXVuX05bM10NCmFfZGYgPC0gZGF0YS5mcmFtZShpbW11bl9ZWzNdLCBpbW11bl9OWzNdLCBpbW11bl9kaWZmLCBGQl9QTWVhbiwgRkJfUHNkLCBNQVAsIE1BUF9zZSkNCg0KDQpvcHRpb25zKGtuaXRyLnRhYmxlLmZvcm1hdCA9ICJodG1sIikgDQprbml0cjo6a2FibGUoaGVhZChhX2RmKSwgY29sLm5hbWVzID0gYygnbnVtX1knLCAnICBudW1fTiAgJywnIFktTiAnLCAnIEZCX1BNZWFuICcsICcgRkJfUFNEICcsICcgTUFQICcsICcgTUFQX3NlICcpLCBjYXB0aW9uID0gIjEuIENvbXBhcmlzb24gdGFibGUiLGxpbmVzZXAgPSAiICIsIGFsaWduID0gImMiLCBkaWdpdD0yKSU+JQ0KICAgICAga2FibGVfc3R5bGluZyhsYXRleF9vcHRpb25zPSJzY2FsZV9kb3duIikgJT4lDQogICAgICBjb2x1bW5fc3BlYygxLHdpZHRoID0gIjNpbiIpIA0KDQpgYGANCg0KwqANCg0KSW4gVGFibGUgMSwgdGhlIGBGQl9QTWVhbmAgYW5kIGBGQl9Qc2RgIGNvbHVtbnMgc2hvdyB0aGUgRkIgcG9zdGVyaW9yIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgZWFjaCBmYW1pbHkgYW5kIHRoZSBgTUFQYCBhbmQgYE1BUF9zZWAgY29sdW1ucyBkaXNwbGF5IHRoZSBjb25kaXRpb25hbCAoZW1waXJpY2FsIEJheWVzaWFuKSBwb3N0ZXJpb3IgbW9kZXMgYW5kIGNvcnJlc3BvbmRpbmcgc3RhbmRhcmQgZXJyb3JzLiBXZSB3aWxsIGRpc2N1c3MgdGhlc2UgaW4gbW9yZSBkZXRhaWwgYW5kIHZpc3VhbGl6ZSB0aGUgZGlmZmVyZW5jZXMgaW4gKipzZWN0aW9uIDMuMi40KiouDQoNCk5leHQsIHdlIG1ha2UgYSBzaW1pbGFyIHRhYmxlIGZvciBzb21lIGV4dHJlbWUgZmFtaWxpZXMgaW4gd2hpY2ggYWxsIHJlc3BvbnNlcyBhcmUgIlkiIG9yICJOIi4NCg0KYGBge3J9DQprbml0cjo6a2FibGUoaGVhZChhX2RmW29yZGVyKGFfZGYkbnVtX1ksIC1hX2RmJG51bV9OKSxdKSwgY29sLm5hbWVzID0gYygnbnVtX1knLCAnICBudW1fTiAgJywnIFktTiAnLCAnIEZCX1BNZWFuICcsICcgRkJfUFNEICcsICcgTUFQICcsICcgTUFQX3NlICcpLCBjYXB0aW9uID0gIjIuIENvbXBhcmlzb24gdGFibGUgb2YgZmFtaWxpZXMgd2l0aCBleHRyZW1lIG51bWJlciBvZiBObyIsIGRpZ2l0PTIpJT4lDQogICAgICBrYWJsZV9zdHlsaW5nKGxhdGV4X29wdGlvbnM9InNjYWxlX2Rvd24iKSAlPiUNCiAgICAgIGNvbHVtbl9zcGVjKDEsd2lkdGggPSAiM2luIikgDQpgYGANCg0KYGBge3J9DQprbml0cjo6a2FibGUoaGVhZChhX2RmW29yZGVyKC1hX2RmJG51bV9ZLCBhX2RmJG51bV9OKSxdKSwgY29sLm5hbWVzID0gYygnbnVtX1knLCAnICBudW1fTiAgJywnIFktTiAnLCAnIEZCX1BNZWFuICcsICcgRkJfUFNEICcsICAnIE1BUCAnLCAnIE1BUF9zZSAnKSwgY2FwdGlvbiA9ICIzLiBDb21wYXJpc29uIHRhYmxlIG9mIGZhbWlsaWVzIHdpdGggZXh0cmVtZSBudW1iZXIgb2YgWWVzIiwgZGlnaXQ9MiklPiUNCiAgICAgIGthYmxlX3N0eWxpbmcobGF0ZXhfb3B0aW9ucz0ic2NhbGVfZG93biIpICU+JQ0KICAgICAgY29sdW1uX3NwZWMoMSx3aWR0aCA9ICIzaW4iKSANCg0KYGBgDQoNCsKgDQoNClRhYmxlcyAyIGFuZCAzIHNob3cgdGhhdCB0aGUgRkIgcG9zdGVyaW9yIG1lYW5zIChgRkJfUE1lYW5gKSBhcmUgbW9yZSBkaWZmZXJlbnQgZnJvbSB6ZXJvIHRoYW4gdGhlIGNvbmRpdGlvbmFsIHBvc3RlcmlvciBtb2RlIChgTUFQYCkgcHJlZGljdGlvbnMuIEFzIHdpbGwgYmUgc2hvd24gaW4gKipzZWN0aW9uIDMuMi40KiosIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gRkIgcG9zdGVyaW9yIG1lYW5zIGFuZCBjb25kaXRpb25hbCBwb3N0ZXJpb3IgbWVhbiAoYEVBUGApIHByZWRpY3Rpb25zIGlzIGluIHRoZSBzYW1lIGRpcmVjdGlvbiBidXQgbXVjaCBsZXNzIHByb25vdW5jZWQsIHN1Z2dlc3RpbmcgdGhhdCB0aGUgZGlmZmVyZW5jZXMgYXJlIG1vc3RseSBkdWUgdG8gdXNpbmcgZGlmZmVyZW50IHN1bW1hcmllcyAobWVhbiB2ZXJzdXMgbW9kZSkgb2Ygc2tld2VkIHBvc3RlcmlvciBkaXN0cmlidXRpb25zLg0KDQpJbiBhZGRpdGlvbiwgaW4gdGhlc2UgZXh0cmVtZSBmYW1pbGllcywgdGhlIEZCIHBvc3RlcmlvciBzdGFuZGFyZCBkZXZpYXRpb25zIChgRkJfUHNkYCkgYXJlIGxhcmdlciB0aGFuIHRoZSBjb3JyZXNwb25kaW5nIHN0YW5kYXJkIGVycm9ycyBmb3IgdGhlIGNvbmRpdGlvbmFsIE1BUCBwcmVkaWN0aW9ucy4gT25lIHJlYXNvbiBmb3IgdGhlc2UgZGlzY3JlcGFuY2llcyBpcyB0aGF0IHRoZSBGQiBhcHByb2FjaCB0YWtlcyB0aGUgcGFyYW1ldGVyIHVuY2VydGFpbnR5IGZvciB0aGUgb3RoZXIgcGFyYW1ldGVycyBpbnRvIGFjY291bnQgYnkgdXNpbmcgdGhlIG1hcmdpbmFsIHBvc3RlcmlvcnMgKG1hcmdpbmFsIG92ZXIgdGhlIG90aGVyIHBhcmFtZXRlcnMpLCB3aGVyZWFzIHRoZSBjb25kaXRpb25hbCBNQVAgc3RhbmRhcmQgZXJyb3JzIGFyZSBiYXNlZCBvbiB0aGUgY29uZGl0aW9uYWwgcG9zdGVyaW9ycyAoY29uZGl0aW9uYWwgb24gdGhlIG90aGVyIHBhcmFtZXRlcnMgYmVpbmcgZXF1YWwgdG8gdGhlIE1MIGVzdGltYXRlcykuDQoNCkZvciBjb21wYXJpc29uLCB0aGUgZm9sbG93aW5nIHRhYmxlIGRpc3BsYXlzIHNvbWUgbGVzcyBleHRyZW1lIGZhbWlsaWVzIGluIHdoaWNoIHRoZSBudW1iZXJzIG9mICJZIiBhbmQgIk4iIHJlc3BvbnNlcyBhcmUgYWxtb3N0IHRoZSBzYW1lLg0KDQpgYGB7cn0NCmtuaXRyOjprYWJsZShoZWFkKGFfZGZbKGltbXVuX2RpZmY9PTApLF0pLCBjb2wubmFtZXMgPSBjKCdudW1fWScsICcgIG51bV9OICAnLCcgWS1OICcsICcgRkJfUE1lYW4gJywgJyBGQl9QU0QgJywgJyBNQVAgJywgJyBNQVBfc2UgJyksIGNhcHRpb24gPSAiNC4gQ29tcGFyaXNvbiB0YWJsZSBvZiBmYW1pbGllcyB3aXRoIHNhbWUgbnVtYmVyIG9mIFllcyBhbmQgTm8gcmVzcG9uc2VzICIsIGRpZ2l0PTIpICU+JQ0KICAgICAga2FibGVfc3R5bGluZyhsYXRleF9vcHRpb25zPSJzY2FsZV9kb3duIikgJT4lDQogICAgICBjb2x1bW5fc3BlYygxLHdpZHRoID0gIjNpbiIpIA0KDQpgYGANCg0KwqANCg0KQXMgc2hvd24gaW4gdGFibGUgNCwgd2hlbiB0aGUgbnVtYmVycyBvZiAiWSIgYW5kICJOIiByZXNwb25zZXMgYmVjb21lIGNsb3NlciwgdGhlIGRpc2NyZXBhbmNpZXMgb2YgcHJlZGljdGlvbnMgYW5kIHN0YW5kYXJkIGVycm9ycyBiZXR3ZWVuIHRoZSB0d28gYXBwcm9hY2hlcyBiZWNvbWUgc21hbGxlci4gVGhhdCBjb3VsZCBiZSwgaW4gcGFydCwgYmVjYXVzZSB0aGUgcG9zdGVyaW9ycyBhcmUgbW9yZSBzeW1tZXRyaWMuDQoNClNvIGZhciwgd2UgaGF2ZSBzaG93biB0aGF0IHRoZSBjb25kaXRpb25hbCBNQVAgcHJlZGljdGlvbnMgb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHRzIGZvciBmYW1pbGllcyBhcmUgZGlmZmVyZW50IGZyb20gRkIgZXN0aW1hdGVzLiBUbyBzdW1tYXJpemUsIGZvciBjb25kaXRpb25hbCBNQVAsIHdlIG9idGFpbiB0aGUgKmNvbmRpdGlvbmFsKiBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9ucyBvZiAkXHpldGFfaiQgYnkgcGx1Z2dpbmcgaW4gdGhlIE1MIGVzdGltYXRlcyBvZiB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYW5kIHRoZSByYW5kb20taW50ZXJjZXB0IHZhcmlhbmNlIG9idGFpbmVkIGZyb20gYGdsbWVyKClgLiBUaGVuIHdlIHVzZSB0aGUgKm1vZGVzKiBvZiB0aGUgKipjb25kaXRpb25hbCoqIHBvc3RlcmlvciBkaXN0cmlidXRpb25zLCBjb25kaXRpb25hbCBvbiB0aGUgcGFyYW1ldGVycyBiZWluZyBlcXVhbCB0byB0aGVpciBNTCBlc3RpbWF0ZXMuIEluIGNvbnRyYXN0LCBpbiBGQiB3ZSBvYnRhaW4gZHJhd3MgZnJvbSB0aGUgam9pbnQgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHRzIGFuZCBhbGwgb3RoZXIgcGFyYW1ldGVycy4gVGhlIG1lYW5zIGFuZCBzdGFuZGFyZCBkZXZpYXRpb25zIG9mIHRoZSBkcmF3cyBvZiB0aGUgcmFuZG9tIGludGVyY2VwdHMgdGhlcmVmb3JlIHJlcHJlc2VudCAqKm1hcmdpbmFsKiogcG9zdGVyaW9yIG1lYW5zIGFuZCBzdGFuZGFyZCBkZXZpYXRpb25zLCBtYXJnaW5hbCBvdmVyIGFsbCBvdGhlciBwYXJhbWV0ZXJzLg0KDQojIyMgKiozLjIuMyBQbG90dGluZyBmdW5jdGlvbnMgZm9yIFN0YW4gZXN0aW1hdGVzKioNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSB1c2UgdGhlIHBhY2thZ2UgYGJheWVzcGxvdGAsIHdoaWNoIHByb3ZpZGVzIGEgbGlicmFyeSBvZiBwbG90dGluZyBmdW5jdGlvbnMgZm9yICpTdGFuKiBlc3RpbWF0ZXMsIHRvIHZpc3VhbGl6ZSB0aGUgcG9zdGVyaW9yIGRyYXdzLiBUaGUgcGxvdHMgY3JlYXRlZCBieSBgYmF5ZXNwbG90YCBhcmUgYGdncGxvdGAgb2JqZWN0cyBhbmQgY2FuIGJlIGN1c3RvbWl6ZWQgdXNpbmcgdmFyaW91cyBmdW5jdGlvbnMgZnJvbSB0aGUgYGdncGxvdDJgIHBhY2thZ2UuIEZ1cnRoZXJtb3JlLCBgYmF5c3Bsb3RgIG9mZmVycyB2aXN1YWwgTUNNQyBkaWFnbm9zdGljcywgZ3JhcGhpY2FsIHBvc3RlcmlvciBwcmVkaWN0aXZlIGNoZWNraW5nLCBhbmQgbW9yZS4gU2luY2Ugd2UgYXJlIG5vdCBkaWdnaW5nIG1vcmUgaW50byB0aGVzZSB2aXN1YWxpemF0aW9ucyBpbiB0aGlzIHR1dG9yaWFsLCBzZWUgW2JheWVzcGxvdF0oaHR0cHM6Ly9tYy1zdGFuLm9yZy9iYXllc3Bsb3QvcmVmZXJlbmNlL2luZGV4Lmh0bWwpIGZvciBtb3JlIGluZm9ybWF0aW9uLg0KDQpgYGB7cn0NCmxpYnJhcnkoImJheWVzcGxvdCIpDQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmxpYnJhcnkoInJzdGFuYXJtIikgIA0KbGlicmFyeShyc3RhbikNCiNwb3N0ZXJpb3IgPC0gYXMuYXJyYXkoTTFfc3RhbmdsbWVyKQ0KI3Bvc3RlcmlvcjwtIGFzLm1hdHJpeChNMV9zdGFuZ2xtZXIpDQpwb3N0ZXJpb3I8LSBhcy5kYXRhLmZyYW1lKE0xX3N0YW5nbG1lcikNCmRpbShwb3N0ZXJpb3IpDQpgYGANCg0KSW4gdGhlIG5leHQgUiBjaHVuaywgd2UgdXNlIHRoZSBgbWNtY19kZW5zKClgIGZ1bmN0aW9uIHRvIGRpc3BsYXkga2VybmVsIGRlbnNpdHkgcGxvdHMgb2YgTUNNQyBkcmF3cyBvZiBhIHBhcmFtZXRlciAoY29tYmluaW5nIGFsbCBjaGFpbnMpLiBCeSB1c2luZyB0aGUgYHBhcnNgIG9wdGlvbiwgd2Ugc3BlY2lmeSB0aGUgcGFyYW1ldGVyIGFuZCBkaXNwbGF5IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHBvc3RlcmlvciBkcmF3cyBmb3IgYSBzcGVjaWZpYyBmYW1pbHksIG1vbSB3aXRoIGlkIFwjMiwgYXMgYW4gZXhhbXBsZSBpbiB0aGlzIHR1dG9yaWFsLg0KDQpgYGB7cn0NCmNvbG9yX3NjaGVtZV9zZXQoInB1cnBsZSIpDQpwMjwtbWNtY19kZW5zKHBvc3RlcmlvciwgcGFycyA9IGMoImJbKEludGVyY2VwdCkgbW9tOjJdIikpDQpwMg0KYGBgDQoNCiMjIyAqKjMuMi40IFZpc3VhbGl6ZWQgY29tcGFyaXNvbiBiZXR3ZWVuIGVtcGlyaWNhbCBCYXllcyBwcmVkaWN0aW9ucyBhbmQgZnVsbHkgQmF5ZXNpYW4gZXN0aW1hdGVzKioNCg0KV2Ugbm93IGNvbXBhcmUgdGhlIEZCIG1hcmdpbmFsIHBvc3RlcmlvciBkZW5zaXR5IG9mIHRoZSByYW5kb20gaW50ZXJjZXB0IGZvciB0aGUgZmFtaWx5IHdob3NlIGlkZW50aWZpZXIgaW4gdmFyaWFibGUgYG1vbWAgd2FzICQyJCwgYWthIG1vbSAyIChhcyBhbiBleGFtcGxlKSB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIGNvbmRpdGlvbmFsIHBvc3RlcmlvciBkZW5zaXR5LCB3aXRoIE1MIGVzdGltYXRlcyBwbHVnZ2VkIGluIGZvciB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYW5kIHRoZSByYW5kb20taW50ZXJjZXB0IHZhcmlhbmNlcy4NCg0KRmlyc3QsIGZvciBvdXIgRkIgZXN0aW1hdGVzLCB3ZSB1c2UgdGhlIGBhcy5kYXRhLmZyYW1lKClgIGZ1bmN0aW9uIHRvIHJldHJpZXZlIHRoZSBwb3N0ZXJpb3IgZHJhd3Mgb2YgdGhlIHJhbmRvbSBlZmZlY3RzIGZyb20gYE0xX3N0YW5nbG1lcmAuIFRoaXMgbWV0aG9kIGVuYWJsZXMgdXMgdG8gYWNjZXNzIHRoZSBkcmF3cyBkaXJlY3RseSBhbmQgdGh1cyBtYWtlcyBpdCBlYXNpZXIgdG8gbWFuaXB1bGF0ZSB0aGUgcGxvdC4gRm9yIGluc3RhbmNlLCB3ZSBwbG90IHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIHRoZSByYW5kb20gaW50ZXJjZXB0IGZvciBhIHNwZWNpZmljIGZhbWlseSwgbW9tIDIuIFdlIGFkZCBhIGJsYWNrLXNvbGlkIGxpbmUgdG8gc2hvdyB0aGUgbWVhbiBvZiBwb3N0ZXJpb3IgZHJhd3MgZm9yIG1vbSAyLiBUaGUgOTUlIGNyZWRpYmxlIGludGVydmFsIGlzIHJlcHJlc2VudGVkIGJ5IGEgYmxhY2sgZXJyb3IgYmFyIHdoaWNoLCB1bmxpa2UgZnJlcXVlbnRpc3QgY29uZmlkZW5jZSBpbnRlcnZhbHMsIGNhbiBiZSBpbnRlcnByZXRlZCBhcyBoYXZpbmcgYSA5NSUgcHJvYmFiaWxpdHkgb2YgY29udGFpbmluZyB0aGUgcmFuZG9tIGludGVyY2VwdC4NCg0KYGBge3J9DQpwb3N0ZXJpb3I8LSBhcy5kYXRhLmZyYW1lKE0xX3N0YW5nbG1lcikgIyByZXRyaWV2ZSB0aGUgcG9zdGVyaW9yIGRyYXdzIA0KI3ByaW50KGRpbW5hbWVzKHBvc3RlcmlvcikpDQpGQl9wb3N0ZXJpb3JfbW9tMjwtcG9zdGVyaW9yJGBiWyhJbnRlcmNlcHQpIG1vbToyXWANCmRmIDwtIGRhdGEuZnJhbWUoY29sMSA9ICgxOjEyMDAwKSwNCiAgICAgICAgICAgICAgICAgICBjb2wyPUZCX3Bvc3Rlcmlvcl9tb20yKQ0KcCA8LSBnZ3Bsb3QoZGYpIA0KcCArIGdlb21fZGVuc2l0eShhZXMoRkJfcG9zdGVyaW9yX21vbTIpLCBmaWxsID0gImxpZ2h0Z3JheSIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oRkJfcG9zdGVyaW9yX21vbTIpKSwgbGluZXR5cGUgPSAic29saWQiLCBsd2QgPSAxLjUpKw0KICBnZW9tX2Vycm9yYmFyaChhZXMoeG1pbj1tZWFuKEZCX3Bvc3Rlcmlvcl9tb20yKS0yKnNkKEZCX3Bvc3Rlcmlvcl9tb20yKSwgeG1heCA9bWVhbihGQl9wb3N0ZXJpb3JfbW9tMikrMipzZChGQl9wb3N0ZXJpb3JfbW9tMiksIHk9MC4wMiwgaGVpZ2h0ID0gLjAwNSksIGx3ZCA9IDEuNSkrDQogIGFubm90YXRlKGdlb209InRleHQiLCB4PTMsIHk9MC4wMywgbGFiZWw9IkZCX21lYW4iLGNvbG9yPSJibGFjayIpDQpgYGANCg0KU2Vjb25kbHksIHdlIGRlcml2ZSB0aGUgY29uZGl0aW9uYWwgcG9zdGVyaW9yIGRlbnNpdHkgb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHQgZm9yIG1vbSAyIHdpdGggTUwgZXN0aW1hdGVzIHBsdWdnZWQgaW4gZm9yIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhbmQgcmFuZG9tLWludGVyY2VwdCB2YXJpYW5jZS4gVGhpcyBjb25kaXRpb25hbCBwb3N0ZXJpb3IgZGVuc2l0eSBjYW4gYmUgd3JpdHRlbiBhcyAkJFx0ZXh0cm17UG9zdGVyaW9yfShcemV0YV8yfGltbXVuX3syMn09MSwga2lkMnBfezIyfSwgbW9tMjVwX3syMn0sIG9yZDIzX3syMn0sIG9yZDQ2X3syMn0sIG9yZDdwX3syMn07IFxib2xkc3ltYm9se1xoYXRcYmV0YX1ee01MfV9qLFxoYXRccHNpXntNTH0pPVxcDQpcZnJhY3tcdGV4dHJte1ByaW9yfShcemV0YV8yKVx0aW1lcyBcdGV4dHJte0xpa2VsaWhvb2R9KGltbXVuX3syMn09MXxraWQycF97MjJ9LCBtb20yNXBfezIyfSwgb3JkMjNfezIyfSwgb3JkNDZfezIyfSwgb3JkN3BfezIyfSxcemV0YV8yKX17Q30kJA0KDQpUaGUgY29uZGl0aW9uYWwgcHJpb3IgaXMgc2ltcGx5IGEgbm9ybWFsIGRlbnNpdHkgd2l0aCB2YXJpYW5jZSBzZXQgZXF1YWwgdG8gaXRzIE1MIGVzdGltYXRlLCBhbmQgd2UgY2FuIGV2YWx1YXRlIHRoaXMgcHJpb3IgZGVuc2l0eSBhdCBhIHNldCBvZiBwb2ludHMgYXMgZm9sbG93czoNCg0KYGBge3J9DQojIGV2YWx1YXRlIHRoZSBub3JtYWwgZGVuc2l0eSBmdW5jdGlvbiBhdCBkaWZmZXJlbnQgdmFsdWVzDQp6ZXRhIDwtIHNlcSgtNSwgMTAsIGJ5ID0gLjAxKQ0KIyBzcGVjaWZ5IGEgbm9ybWFsIGRlbnNpdHkgd2l0aCB2YXJpYW5jZSBzZXQgZXF1YWwgdG8gaXRzIE1MIGVzdGltYXRlDQpzcXJ0X3BzaTwtc3FydCg2LjgzNikNCnByaW9yIDwtIGRub3JtKHpldGEsIA0KICAgICAgICAgICAgICAgIG1lYW4gPSAwLCANCiAgICAgICAgICAgICAgICBzZCA9IHNxcnRfcHNpKSAgDQpgYGANCg0KVGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gaXMgbG9naXQkXnstMX1bJGxpbnByZWQkX2orXHpldGFfal0kLCB3aGVyZSBsaW5wcmVkJF9qJCByZXByZXNlbnRzIHRoZSAiZml4ZWQiIHBhcnQgb2YgdGhlIGxpbmVhciBwcmVkaWN0b3IgZm9yIHRoaXMgc3BlY2lmaWMgZmFtaWx5LCBtb20gMiAod2hvIGhhZCBvbmUgY2hpbGQgd2l0aCAkeV97aTJ9PTEkKS4gVGhlICJmaXhlZCIgcGFydCBvZiB0aGUgbGluZWFyIHByZWRpY3RvciBjYW4gYmUgd3JpdHRlbiBhcyAkJGxpbnByZWRfaj1caGF0XGJldGFfMSArIFxoYXRcYmV0YV8yIGtpZDJwX3tpan0gKyBcaGF0XGJldGFfMyBtb20yNXBfe2lqfSArXGhhdFxiZXRhXzQgb3JkMjNfe2lqfSArXGhhdFxiZXRhXzUgb3JkNDZfe2lqfSArXGhhdFxiZXRhXzYgb3JkN3Bfe2lqfSQkDQoNCmBgYHtyfQ0KbGlucHJlZD0tMS40OTM3ICsgMS42ODQ1KjEgKy0wLjEyOTgqMCstMC4zMTQwKjArLTAuMTc5OCowKy0wLjEwMTcqMCAgICAgICAgICAgIA0KbGlrZWxpaG9vZCA8LSBpbnZsb2dpdChsaW5wcmVkK3pldGEpIA0KYGBgDQoNCkl0IGZvbGxvd3MgZnJvbSBCYXllcyB0aGVvcmVtIHRoYXQgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gaXMgcHJvcG9ydGlvbmFsIHRvIHRoZSBwcm9kdWN0IG9mIHRoZSBsaWtlbGlob29kIGFuZCB0aGUgcHJpb3IuIFRoZSAqbm9ybWFsaXppbmcgY29uc3RhbnQqIGlzIHRoZSBtYXJnaW5hbCBwcm9iYWJpbGl0eSBvZiB0aGUgcmVzcG9uc2UgKG1hcmdpbmFsIG92ZXIgdGhlIHJhbmRvbSBpbnRlcmNlcHQpLCB3aGljaCBjYW4gYmUgd3JpdHRlbiBhcyAkJEMgPVxpbnQgXGhhdCBQcih5X3tpan09MXxtb20yNXBfe2lqfSwgb3JkMjNfe2lqfSwgb3JkNDZfe2lqfSwgb3JkN3Bfe2lqfSwgXHpldGFfailccGhpKFx6ZXRhX2o7IDAsIFxoYXRccHNpXntNTH0pZFx6ZXRhX2okJCB3aGVyZSAkXHBoaShcemV0YV9qOyAwLCBcaGF0XHBzaV57TUx9KSQgaXMgdGhlIG5vcm1hbCBkZW5zaXR5IG9mICRcemV0YV9qJCB3aXRoIGEgbWVhbiBvZiAwIGFuZCBhIHZhcmlhbmNlIG9mICRcaGF0XHBzaV57TUx9JC4gVGhpcyBjYW4gYmUgb2J0YWluZWQgYnkgTW9udGUgQ2FybG8gaW50ZWdyYXRpb24sIHNhbXBsaW5nICRcemV0YV9qJCBmcm9tIGl0cyBjb25kaXRpb25hbCBwcmlvciBkaXN0cmlidXRpb246DQoNCmBgYHtyfQ0KeSA8LSBybm9ybSgxMDAwMDAsIHNkID0gc3FydF9wc2kpDQpub3JtYWxpemluZy5jb25zdGFudDwtIG1lYW4oKGludmxvZ2l0KGxpbnByZWQrIHkpKSkNCmBgYA0KDQpMYXN0bHksIHdlIGNhbGN1bGF0ZSB0aGUgcHJvZHVjdCBvZiB0aGUgcHJpb3Igb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHQgYW5kIHRoZSBsaWtlbGlob29kIG9mIHRoZSByZXNwb25zZXMgZm9yIHRoZSBjbHVzdGVyIChmb3IgbW9tIDIncyBjaGlsZCkgZ2l2ZW4gdGhlIHJhbmRvbSBpbnRlcmNlcHQgYW5kIGRpdmlkZSB0aGlzIHByb2R1Y3QgYnkgdGhlIG5vcm1hbGl6aW5nIGNvbnN0YW50IHRvIG9idGFpbiB0aGUgcG9zdGVyaW9yIGRlbnNpdHk6DQoNCmBgYHtyfQ0KRUJfcG9zdGVyaW9yIDwtcHJpb3IqbGlrZWxpaG9vZC9ub3JtYWxpemluZy5jb25zdGFudA0KYGBgDQoNCkJlbG93LCB0aGlzIGNvbmRpdGlvbmFsIHBvc3RlcmlvciBpcyBhZGRlZCB0byB0aGUgcGxvdC4gVGhpcyBjb25kaXRpb25hbCBwb3N0ZXJpb3IgZGVuc2l0eSBpcyBub3QgYSBub3JtYWwgZGVuc2l0eSBhcyBpbiBsaW5lYXIgbWl4ZWQgbW9kZWxzLCBhbmQgaGVuY2UgaXRzIG1vZGUgZG9lcyBub3QgZXF1YWwgaXRzIG1lYW4uIFJlY2FsbCB0aGF0IHRoZSByYW5kb20gaW50ZXJjZXB0IHByZWRpY3Rpb25zIG9idGFpbmVkIGJ5IHVzaW5nIHRoZSBgcmFuZWYoKWAgZnVuY3Rpb24gYXJlIHRoZSBtb2RlcyAoTUFQKSBvZiB0aGlzIGNvbmRpdGlvbmFsIHBvc3Rlcmlvci4NCg0KV2UgY2FuIGNhbGN1bGF0ZSB0aGUgY29uZGl0aW9uYWwgbWVhbiAoRUFQKSBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgc3RhbmRhcmQgZGV2aWF0aW9uIGJ5IHVzaW5nIHRoZSBgaW50ZWdyYXRlKClgIGZ1bmN0aW9uLCBhcyBzaG93biBpbiB0aGUgbmV4dCBSIGNodW5rLg0KDQpgYGB7cn0NCiMgRUIgbWVhbiBmb3IgbW9tIDI6IHkgPSAxDQp4IDwtIHJub3JtKDEwMDAwMDAwLCBzZCA9IHNxcnRfcHNpKQ0Kbm9ybWFsaXppbmcuY29uc3RhbnQ8LSBtZWFuKChpbnZsb2dpdCgobGlucHJlZCt4KSkpKQ0KDQpmdW5jIDwtIGZ1bmN0aW9uKHgpew0KICB4KmRub3JtKHgsIHNkID0gc3FydF9wc2kpKihpbnZsb2dpdCgobGlucHJlZCt4KSkpL25vcm1hbGl6aW5nLmNvbnN0YW50DQp9DQppbnRlZ3JhdGUoZnVuYywgbG93ZXIgPSAtSW5mLCB1cHBlciA9IEluZikNCg0KRVggPC0gaW50ZWdyYXRlKGZ1bmMsIGxvd2VyID0gLUluZiwgdXBwZXIgPSBJbmYpW1sxXV0NCg0KZnVuY19zZCA8LSBmdW5jdGlvbih4KXsNCiAgeF4yKmRub3JtKHgsIHNkID0gc3FydF9wc2kpKihpbnZsb2dpdCgobGlucHJlZCt4KSkgKS9ub3JtYWxpemluZy5jb25zdGFudA0KfQ0KRVgyIDwtIGludGVncmF0ZShmdW5jX3NkLCBsb3dlciA9IC1JbmYsIHVwcGVyID0gSW5mKVtbMV1dDQojIGNvbmRpdGlvbmFsIHNkDQpFQVBfc2QgPC0gc3FydChFWDIgLSBFWF4yKQ0KDQpgYGANCg0KVG8gY29tcGFyZSB0aGUgZGlmZmVyZW50IGVzdGltYXRlcywgdGhlIGNvZGUgYmVsb3cgYWRkcyB0aGUgY29uZGl0aW9uYWwgRUFQIHRvIHRoZSBkZW5zaXR5IHBsb3QgYXMgYSBkYXJrIGJsdWUtZG90dGVkIHZlcnRpY2FsIGxpbmUgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGNvbmRpdGlvbmFsIE1BUCBhcyBhIGxpZ2h0ZXIsIGJsdWUtZGFzaGVkIHZlcnRpY2FsIGxpbmUuIFRoZSBmaWd1cmUgc2hvd3MgdGhhdCB0aGUgYmxhY2sgdmVydGljYWwgbGluZSAoRkJfTWVhbikgaXMgZnVydGhlciBhd2F5IGZyb20gemVybyB0aGFuIHRoZSBkYXJrIGJsdWUtZG90dGVkIHZlcnRpY2FsIGxpbmUgKGNvbmRpdGlvbmFsIEVBUCkgYW5kIHRoZSBsaWdodGVyLCBibHVlLWRhc2hlZCB2ZXJ0aWNhbCBsaW5lIChjb25kaXRpb25hbCBNQVApLiBIb3dldmVyLCB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0aGUgYmxhY2sgdmVydGljYWwgbGluZSAoRkJfTWVhbikgYW5kIHRoZSBkYXJrIGJsdWUtZG90dGVkIHZlcnRpY2FsIGxpbmUgKGNvbmRpdGlvbmFsIEVBUCkgaXMgaW4gdGhlIHNhbWUgZGlyZWN0aW9uIGJ1dCBtdWNoIHNob3J0ZXIgdGhhbiB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0aGUgYmxhY2sgdmVydGljYWwgbGluZSAoRkJfTWVhbikgYW5kIHRoZSBsaWdodGVyLCBibHVlLWRhc2hlZCB2ZXJ0aWNhbCBsaW5lIChjb25kaXRpb25hbCBNQVApLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIG1haW4gcmVhc29uIGZvciB0aGUgZGlzY3JlcGFuY3kgYmV0d2VlbiBGQiBwb3N0ZXJpb3IgbWVhbnMgYW5kIGNvbmRpdGlvbmFsIE1BUCBwcmVkaWN0aW9ucyBpcyB0aGF0IHRoZSBwb3N0ZXJpb3JzIChhbmQgY29uZGl0aW9uYWwgcG9zdGVyaW9ycykgYXJlIGFzeW1tZXRyaWMgLSBza2V3ZWQgdG8gdGhlIGxlZnQgZm9yIHBvc2l0aXZlIEZCIHBvc3RlcmlvciBtZWFucyBhbmQgdG8gdGhlIHJpZ2h0IGZvciBuZWdhdGl2ZSBvbmVzLiBBcyBtZW50aW9uZWQsIGJlY2F1c2UgdGhlIEZCIHBvc3RlcmlvciBzdGFuZGFyZCBkZXZpYXRpb24gdGFrZXMgcGFyYW1ldGVyIHVuY2VydGFpbnR5IGZvciB0aGUgb3RoZXIgcGFyYW1ldGVycyBpbnRvIGFjY291bnQsIHdoZXJlYXMgdGhlIGNvbmRpdGlvbmFsIEVBUCBhbmQgTUFQIHN0YW5kYXJkIGVycm9ycyBhcmUgYmFzZWQgb24gdGhlIGNvbmRpdGlvbmFsIHBvc3RlcmlvcnMsIHRoZSA5NSUgY3JlZGlibGUgaW50ZXJ2YWwgZm9yIEZCIGlzIGJyb2FkZXIgdGhhbiB0aG9zZSBmb3IgdGhlIGNvbmRpdGlvbmFsIEVBUCBhbmQgTUFQLCB3aGljaCBpcyB3aHkgaW4gdGhlIGZpZ3VyZSB0aGUgYmxhY2sgZXJyb3IgYmFyIGlzIHdpZGVyIHRoYW4gdGhlIGJsdWUgb25lIGFuZCB0aGUgbGlnaHRlciBibHVlIG9uZS4gQWRkaXRpb25hbGx5LCB0aGUgY29uZGl0aW9uYWwgTUFQIHN0YW5kYXJkIGVycm9yIGlzIHNtYWxsZXIgdGhhbiB0aGUgY29uZGl0aW9uYWwgRUFQIHN0YW5kYXJkIGVycm9yIGJlY2F1c2UgdGhlIGZvcm1lciBpcyBiYXNlZCBvbiB0aGUgY3VydmF0dXJlIG9mIHRoZSBjb25kaXRpb25hbCBsb2cgcG9zdGVyaW9yIGF0IHRoZSBtb2RlIGFuZCBpcyB0aGVyZWZvcmUgbm90IGFmZmVjdGVkIGJ5IHRoZSB0aGlja2VyIHRhaWwgb24gdGhlIHJpZ2h0Lg0KDQpgYGB7cn0NCg0KDQpkZjIgPC0gZGF0YS5mcmFtZShjb2wxID16ZXRhLCBjb2wyPSBFQl9wb3N0ZXJpb3IpDQpwICsgZ2VvbV9kZW5zaXR5KGFlcyhGQl9wb3N0ZXJpb3JfbW9tMiksIGZpbGwgPSAibGlnaHRncmF5IikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihGQl9wb3N0ZXJpb3JfbW9tMikpLCBsaW5ldHlwZSA9ICJzb2xpZCIsIGx3ZCA9IDEuNSkrDQogIGdlb21fZXJyb3JiYXJoKGFlcyh4bWluPW1lYW4oRkJfcG9zdGVyaW9yX21vbTIpLTIqc2QoRkJfcG9zdGVyaW9yX21vbTIpLCB4bWF4ID1tZWFuKEZCX3Bvc3Rlcmlvcl9tb20yKSsyKnNkKEZCX3Bvc3Rlcmlvcl9tb20yKSwgeT0wLjAzLCBjb2xvdXI9RkJfTWVhbiksY29sb3VyPSJibGFjayIsIGhlaWdodCA9IC4wMDUsIGx3ZCA9IDEuNSkrDQogIGFubm90YXRlKGdlb209ImVycm9yYmFyaCIsIHhtaW49NiwgeG1heD02LjUsIHk9MC4yLGhlaWdodCA9IC4wMDUsY29sb3VyPSJibGFjayIsIHNpemU9MS41KSsNCiAgYW5ub3RhdGUoZ2VvbT0idGV4dCIsIHg9OSwgeT0wLjIsIGxhYmVsPSJGQl9tZWFuIiwgc2l6ZT00KSsNCiAgZ2VvbV9saW5lKGRhdGE9ZGYyLCBhZXMoemV0YSwgRUJfcG9zdGVyaW9yKSwgY29sb3VyPSJibHVlIikrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBFWCksIGxpbmV0eXBlID0gImRvdHRlZCIsIGNvbG91cj0iYmx1ZSIsIGx3ZCA9IDEuNSkrICMgYWRkcyB0aGUgRUFQIHRvIHRoZSBkZW5zaXR5IHBsb3QgYXMgYSBkYXJrIGJsdWUtZG90dGVkIHZlcnRpY2FsIGxpbmUNCiAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtaW49RVgtMipFQVBfc2QsIHhtYXggPUVYKzIqRUFQX3NkLCB5PTAuMDIsIGNvbG91cj1FQl9NZWFuKSwgaGVpZ2h0ID0gLjAwNSwgY29sb3VyPSJibHVlIiwgbHdkID0gMS41KSsNCiAgYW5ub3RhdGUoZ2VvbT0iZXJyb3JiYXJoIiwgeG1pbj02LCB4bWF4PTYuNSwgeT0wLjE5LGhlaWdodCA9IC4wMDUsY29sb3VyPSJibHVlIiwgc2l6ZT0xLjUpKw0KICBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgeD05LCB5PTAuMTksIGxhYmVsPSJFQl9tZWFuIiwgc2l6ZT00KSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IGFfZGYkWC5JbnRlcmNlcHQuWzFdKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3VyPSJsaWdodGJsdWUzIiwgbHdkID0gMS41KSsNCiAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtaW49YV9kZiRYLkludGVyY2VwdC5bMV0tMiphX2RmJE1BUF9zZVsxXSwgeG1heD1hX2RmJFguSW50ZXJjZXB0LlsxXSsyKmFfZGYkTUFQX3NlWzFdLCB5PTAuMDEsIGNvbG91cj1FQl9Nb2RlKSwgaGVpZ2h0ID0gLjAwNSwgY29sb3VyPSJsaWdodGJsdWUzIiwgbHdkID0gMS41KSsNCiAgYW5ub3RhdGUoZ2VvbT0iZXJyb3JiYXJoIiwgeG1pbj02LCB4bWF4PTYuNSwgeT0wLjE4LGhlaWdodCA9IC4wMDUsY29sb3VyPSJsaWdodGJsdWUzIiwgc2l6ZT0xLjUpKw0KICBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgeD05LCB5PTAuMTgsIGxhYmVsPSJFQl9tb2RlIiwgc2l6ZT00KQ0KYGBgDQoNCiMgKio0LiBNb2RlbCAyOiBUd28tbGV2ZWwgcmFuZG9tIGludGVyY2VwdCBtb2RlbCB3aXRoIGxldmVsLTEsIGxldmVsLTIsIGFuZCBjcm9zcy1sZXZlbCBpbnRlcmFjdGlvbnMqKg0KDQojIyAqKjQuMSBMaWtlbGlob29kIGluZmVyZW5jZSBmb3IgTW9kZWwgMiB1c2luZyBgZ2xtZXIoKWAqKg0KDQpJbiBhZGRpdGlvbiB0byB0aGUgY2hpbGQtbGV2ZWwgY292YXJpYXRlcyB0aGF0IGhhdmUgYmVlbiBhZGRlZCB0byB0aGUgZmlyc3QgbW9kZWwsIHdlIGNvbnRpbnVlIHRvIGFkZCBhIHNlcmllcyBvZiBmYW1pbHktbGV2ZWwgY292YXJpYXRlcyAobmFtZWx5ICRldGhuTl97an0kLCAkZXRoblNfe2p9JCwgJG1vbUVkUF97an0kLCAkbW9tRWRTX3tqfSQsICRodXNFZFBfe2p9JCwgJGh1c0VkU197an0kLCAkaHVzRWRVX3tqfSQsIGFuZCAkbW9tV29ya1lfe2p9JCkgYW5kIGNyb3NzLWxldmVsIGludGVyYWN0aW9ucyBiZXR3ZWVuIHRoZSBsZXZlbC0xIHZhcmlhYmxlIGluZGljYXRpbmcgY2hpbGRyZW4ncyBhZ2UgYW5kIHRoZSBsZXZlbC0yIHZhcmlhYmxlIGluZGljYXRpbmcgbW90aGVycycgZWR1Y2F0aW9uIGxldmVscyAoJGtpZDJwWV97aWp9XHRpbWVzIG1vbUVkUF97an0kIGFuZCAka2lkMnBZX3tpan1cdGltZXMgbW9tRWRTX3tqfSQpLiBNb2RlbCAyIGNhbiBiZSB3cml0dGVuIGFzICQkXHRleHRybXtsb2dpdH1ce1ByKGltbXVuX3tpan09MXx4X3tpan0sIFx6ZXRhX2opXH0gPSBcYmV0YV8xICsgXGJldGFfMiBraWQycF97aWp9ICsgXGJldGFfMyBtb20yNXBfe2lqfSArXGJldGFfNCBvcmQyM197aWp9ICtcYmV0YV81IG9yZDQ2X3tpan0gK1xiZXRhXzYgb3JkN3Bfe2lqfStcXA0KXGJldGFfNyBldGhuTl97an0rXGJldGFfOCBldGhuU197an0rXGJldGFfOSBtb21FZFBfe2p9KyBcYmV0YV97MTB9bW9tRWRTX3tqfSsgXGJldGFfezExfSBodXNFZFBfe2p9ICtcYmV0YV97MTJ9IGh1c0VkU197an0rXGJldGFfezEzfSBodXNFZFVfe2p9KyBcYmV0YV97MTR9IG1vbVdvcmtZZXNfe2p9K1xcDQpcYmV0YV97MTV9IGtpZDJwWWVzX3tpan1cdGltZXMgbW9tRWRQX3tqfSsgXGJldGFfezE2fSBraWQycFllc197aWp9XHRpbWVzIG1vbUVkU197an0gK1x6ZXRhX2okJA0KDQpUaGUgcmFuZG9tIGludGVyY2VwdHMgJFx6ZXRhX2ogXHNpbSBOKDAsIFxwc2kpJCBhcmUgYXNzdW1lZCB0byBiZSBpbmRlcGVuZGVudCBvZiBlYWNoIG90aGVyIGFuZCBpZGVudGljYWxseSBkaXN0cmlidXRlZCBhY3Jvc3MgZmFtaWxpZXMgJGokIGFuZCBpbmRlcGVuZGVudCBvZiB0aGUgY292YXJpYXRlcyBpbiB0aGUgbW9kZWwuDQoNCmBgYHtyfQ0KTTIgPC0gZ2xtZXIoaW1tdW4gfiBraWQycCArIG1vbTI1cCArIG9yZCArIGV0aG4gKyBtb21FZCArIGh1c0VkICsgbW9tV29yayAra2lkMnAgKiBtb21FZCArICAoMSB8IG1vbSksICANCiAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKCJsb2dpdCIpLCANCiAgICAgICAgICAgIGRhdGEgPSBndUltbXVuLCBjb250cm9sID0gZ2xtZXJDb250cm9sKG9wdGltaXplciA9ICJib2J5cWEiKSwNCiAgICAgICAgICAgIG5BR1EgPSAxMCkNCnN1bW1hcnkoTTIpDQoNCmBgYA0KDQpGcm9tIHRoZSBvdXRwdXQ6DQoNCi0gICBUaGUgKipGaXhlZCBFZmZlY3RzKiogc2VjdGlvbiBkaXNwbGF5cyB0aGUgcG9pbnQgZXN0aW1hdGVzIG9mIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIHdpdGggZXN0aW1hdGVkIHN0YW5kYXJkIGVycm9ycyBhbmQgV2FsZCB0ZXN0cy4gV2UgY2FuIGxlYXJuIGZyb20gdGhlIG91dHB1dCB3aGljaCBjaGlsZC1sZXZlbCBhbmQgZmFtaWx5LWxldmVsIGNvdmFyaWF0ZXMgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnRseSBhc3NvY2lhdGVkIHdpdGggdGhlIGNoaWxkLWxldmVsIHJlc3BvbnNlIHZhcmlhYmxlLCBjb250cm9sbGluZyBmb3IgdGhlIG90aGVyIHZhcmlhYmxlcy4NCg0KLSAgIEluIHRoZSAqKlJhbmRvbSBFZmZlY3RzKiogc2VjdGlvbiwgdGhlIE1MIGVzdGltYXRlcyBwcm92aWRlIHRoZSBlc3RpbWF0ZWQgc3RhbmRhcmQgZGV2aWF0aW9uICgkXHNxcnR7XGhhdHtccHNpfX1cYXBwcm94Mi42MSQpIG9mIHRoZSByYW5kb20gaW50ZXJjZXB0cy4gV2UgY2FuIGxlYXJuIGZyb20gdGhlIHJhbmRvbSBlZmZlY3RzIG91dHB1dCBhYm91dCB0aGUgZXh0ZW50IHRvIHdoaWNoIGJldHdlZW4tZmFtaWx5IHZhcmlhYmlsaXR5IGluIHRoZSByZXNwb25zZSB2YXJpYWJsZSBpcyBsZWZ0IHVuZXhwbGFpbmVkIGJ5IGVtcGxveWluZyBNb2RlbCAyLiBXZSBjYW4gYWxzbyBjb21wYXJlIHRoaXMgZXN0aW1hdGUgd2l0aCB0aGUgY29ycmVzcG9uZGluZyBlc3RpbWF0ZSBvYnRhaW5lZCBmb3IgTW9kZWwgMSB0byBxdWFudGlmeSB0aGUgZXh0ZW50IHRvIHdoaWNoIGJldHdlZW4tZmFtaWx5IHZhcmlhYmlsaXR5IGhhcyBiZWVuIGV4cGxhaW5lZCBieSB0aGUgZmFtaWx5LWxldmVsIGNvdmFyaWF0ZXMgYW5kIHRoZSBjcm9zcy1sZXZlbCBpbnRlcmFjdGlvbiBhZGRlZCB0byB0aGUgc2Vjb25kIG1vZGVsLg0KDQpUaGVuLCB3ZSBjcmVhdGUgdGhlIGNhdGVycGlsbGFyIHBsb3QgYmVsb3cgdG8gc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBjb25kaXRpb25hbCBtb2Rlcy4NCg0KYGBge3J9DQpsYXR0aWNlOjpkb3RwbG90KHJhbmVmKE0yLCB3aGljaCA9ICJtb20iLCBjb25kVmFyID0gVFJVRSksIHNjYWxlcyA9IGxpc3QoeSA9IGxpc3QoYWx0ZXJuYXRpbmcgPSAwKSkpDQpgYGANCg0KIyMgKio0LjIgQmF5ZXNpYW4gaW5mZXJlbmNlIGZvciBNb2RlbCAyIHVzaW5nIGByc3RhbmFybWAqKg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHVzZSBgc3Rhbl9nbG1lcigpYCB0byBlc3RpbWF0ZSBNb2RlbCAyLiBTaW1pbGFyIHRvIE1vZGVsIDEsIHdlIHVzZSB3ZWFrbHkgaW5mb3JtYXRpdmUgcHJpb3IgZGlzdHJpYnV0aW9ucyBmb3IgdGhlIGh5cGVyLXBhcmFtZXRlciAkXHBzaSQuDQoNCmBgYHtyIGVjaG89VCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQpNMl9zdGFuZ2xtZXIgPC0gc3Rhbl9nbG1lcihpbW11biB+IGtpZDJwICsgbW9tMjVwICsgb3JkICsgZXRobiArIG1vbUVkICsgaHVzRWQgKyBtb21Xb3JrICsga2lkMnAgKiBtb21FZCsoMSB8IG1vbSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwoImxvZ2l0IiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGd1SW1tdW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gMzQ5KQ0KYGBgDQoNCiMgKio1LiBNb2RlbCAzOiBUaHJlZS1sZXZlbCByYW5kb20gaW50ZXJjZXB0IG1vZGVsIHdpdGggbGV2ZWwtMSwgbGV2ZWwtMiwgYW5kIGxldmVsLTMgcHJlZGljdG9ycyoqDQoNCiMjICoqNS4xIExpa2VsaWhvb2QgaW5mZXJlbmNlIGZvciBNb2RlbCAzIGBnbG1lcigpYCoqDQoNCldlIGhhdmUgbG9va2VkIGF0IHRoZSB0d28tbGV2ZWwgbG9naXN0aWMgbW9kZWwgd2l0aCBhIHJhbmRvbSBpbnRlcmNlcHQgaW4gZGVwdGguIEluIHRoaXMgc2VjdGlvbiwgd2UgYXJlIGdvaW5nIHRvIGJyaWVmbHkgbG9vayBhdCBob3cgdG8gZXN0aW1hdGUgYSB0aHJlZS1sZXZlbCBsb2dpc3RpYyBtb2RlbCB3aXRoIGxldmVsLTIgcmFuZG9tIGludGVyY2VwdHMsICRcemV0YV57KDIpfV97amt9IFxzaW0gTigwLCBccHNpXnsoMil9KSQsIGZvciBmYW1pbGllcyAkaiQgYW5kIGxldmVsLTMgcmFuZG9tIGludGVyY2VwdHMsICRcemV0YV57KDMpfV97a30gXHNpbSBOKDAsIFxwc2leeygzKX0pJCwgZm9yIGNvbW11bml0aWVzICRrJC4gSW4gTW9kZWwgMywgY2hpbGRyZW4gYXJlIG5lc3RlZCB3aXRoaW4gZmFtaWxpZXMsIGFuZCBmYW1pbGllcyBhcmUgbmVzdGVkIGluIGNvbW11bml0aWVzLCBzbyB0aGUgbGV2ZWxzIGFyZSBuZXN0ZWQuIFRoZSBtb2RlbCBjYW4gYmUgd3JpdHRlbiBhcyAkJFx0ZXh0cm17bG9naXR9XHtQcihpbW11bl97aWprfT0xfCB4LCBcemV0YV57KDIpfV97amt9LCBcemV0YV57KDMpfV97a31cfSA9IFxiZXRhXzEgKyBcYmV0YV8yIGtpZDJwX3tpamt9ICsgLi4uICtcYmV0YV82IG9yZDdwX3tpamt9K1xiZXRhXzcgZXRobk5fe2prfSsuLi5cXA0KKyBcYmV0YV97MTR9IG1vbVdvcmtZZXNfe2prfStcYmV0YV97MTV9IHJ1cmFsX3trfStcYmV0YV97MTZ9IHBjSW5kODFfe2t9ICtcemV0YV57KDIpfV97amt9K1x6ZXRhXnsoMyl9X3trfSQkDQoNCkluIGBnbG1lcmAsIHdlIGRvIG5vdCBuZWVkIHRvIHNwZWNpZnkgdGhlIGRhdGEgc3RydWN0dXJlIChuZXN0ZWQgb3IgY3Jvc3NlZCkgYmVjYXVzZSBgZ2xtZXIoKWAgZmlndXJlcyBpdCBvdXQgYXV0b21hdGljYWxseSBiYXNlZCBvbiB0aGUgZGF0YS4gV2UgdXNlIHRoZSBzYW1lIGdlbmVyYWwgc3ludGF4IGAoMXxJRClgIHRvIHNwZWNpZnkgaW50ZXJjZXB0cyBmb3IgdGhlIGNsdXN0ZXIgaWRlbnRpZmllciBgSURgLCByZWdhcmRsZXNzIG9mIHdoYXQgb3RoZXIgbGV2ZWxzIHRoZXJlIGFyZSBpbiB0aGUgbW9kZWwuIEluIGFkZGl0aW9uIHRvIHRoZSBjaGlsZC1sZXZlbCBhbmQgZmFtaWx5LWxldmVsIGNvdmFyaWF0ZXMsIHdlIGdvIGZ1cnRoZXIgdG8gYWRkIHR3byBjb21tdW5pdHktbGV2ZWwgY292YXJpYXRlcyAoJHJ1cmFsX2skIGFuZCAkcGNJbmQ4MV9rJCkuIEFzIG1lbnRpb25lZCwgZm9yIGEgbW9kZWwgd2l0aCBtb3JlIHRoYW4gYSBzaW5nbGUgc2NhbGFyIHJhbmRvbSBlZmZlY3QsIGBnbG1lcmAgb25seSBzdXBwb3J0cyBhIHNpbmdsZSBpbnRlZ3JhdGlvbiBwb2ludCwgd2hpY2ggY2FuIGJlIHNwZWNpZmllZCBieSBgbkFHUSA9IDFgIGFuZCBpcyBlcXVpdmFsZW50IHRvIHRoZSBMYXBsYWNlIGFwcHJveGltYXRpb24gd2hpY2ggbWF5IG5vdCBiZSBhY2N1cmF0ZS4gV2UgY2FuIG92ZXJjb21lIHRoaXMgcHJvYmxlbSBieSBmaXR0aW5nIHRoZSBtb2RlbCBpbiBgcnN0YW5hcm1gIChhcyBzaG93biBpbiAqKnNlY3Rpb24gNS4yKiopLg0KDQpgYGB7cn0NCk0zIDwtIGdsbWVyKGltbXVuIH4ga2lkMnAgKyBtb20yNXAgKyBvcmQgKyBldGhuICsgbW9tRWQgKyBodXNFZCArIG1vbVdvcmsgKyBydXJhbCsgcGNJbmQ4MSArICgxIHwgbW9tKSArICgxIHwgY29tbSksICANCiAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKCJsb2dpdCIpLCANCiAgICAgICAgICAgIGRhdGEgPSBndUltbXVuLCBjb250cm9sID0gZ2xtZXJDb250cm9sKG9wdGltaXplciA9ICJib2J5cWEiKSwNCiAgICAgICAgICAgIG5BR1EgPSAxKQ0KIyBwcmludCB0aGUgbW9kZWwgcmVzdWx0cyB3aXRob3V0IGNvcnJlbGF0aW9ucyBhbW9uZyBmaXhlZCBlZmZlY3RzDQpwcmludChNMywgZGlnaXRzID0gMikNCmBgYA0KDQpTaW1pbGFyIHRvIHRoZSBgZ2xtZXJgIG91dHB1dCBmb3IgTW9kZWwgMSBhbmQgTW9kZWwgMiwgdGhlIG91dHB1dCB0ZWxscyB1cyBhYm91dCB0aGUgZmFtaWx5ICh3aGljaCBpcyBiaW5vbWlhbCBhcyBvdXIgcmVzcG9uc2UgdmFyaWFibGUgaXMgYmluYXJ5KSBhbmQgdGhlIGxpbmsgKHdoaWNoIGlzIGxvZ2l0KS4gSG93ZXZlciwgd2hpbGUgdGhlIG91dHB1dCBmb3IgTW9kZWxzIDEgYW5kIDIgc2hvd2VkIG9ubHkgdGhlIGVzdGltYXRlZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIHJhbmRvbSBpbnRlcmNlcHRzIGF0IHRoZSBmYW1pbHkgbGV2ZWwgKCRcc3FydHtcaGF0e1xwc2l9fSQpLCB0aGUgb3V0cHV0IG9mIE1vZGVsIDMgc2hvd3MgYm90aCB0aGUgYmV0d2Vlbi1mYW1pbHkgd2l0aGluLWNvbW11bml0eSBzdGFuZGFyZCBkZXZpYXRpb24gZXN0aW1hdGUgKCRcc3FydHtcaGF0e1xwc2l9XnsoMil9fT0gMS4xMyQpIGFuZCB0aGUgYmV0d2Vlbi1jb21tdW5pdHkgc3RhbmRhcmQgZGV2aWF0aW9uIGVzdGltYXRlICgkXHNxcnR7XGhhdHtccHNpfV57KDMpfX09MC43MiQpLg0KDQpOb3cgd2UgY2FuIHBsb3QgdGhlIGNvbmRpdGlvbmFsIHBvc3RlcmlvciBtb2RlcyBvZiB0aGUgcmFuZG9tIGludGVyY2VwdHMgZm9yIGVpdGhlciBmYW1pbGllcyBvciBjb21tdW5pdGllcy4gQnkgdXNpbmcgdGhlIGB3aGljaD1gIG9wdGlvbiwgd2UgY2FuIGNob29zZSBiZXR3ZWVuIGZhbWlsaWVzIChgbW9tYCkgYW5kIGNvbW11bml0aWVzIChgY29tbWApLg0KDQpgYGB7cn0NCiMgZm9yIGZhbWlsaWVzDQpsYXR0aWNlOjpkb3RwbG90KHJhbmVmKE0zLCB3aGljaCA9ICJtb20iLCBjb25kVmFyID0gVFJVRSksIHNjYWxlcyA9IGxpc3QoeSA9IGxpc3QoYWx0ZXJuYXRpbmcgPSAwKSkpDQojIGZvciBjb21tdW5pdGllcw0KbGF0dGljZTo6ZG90cGxvdChyYW5lZihNMywgd2hpY2ggPSAiY29tbSIsIGNvbmRWYXIgPSBUUlVFKSwgc2NhbGVzID0gbGlzdCh5ID0gbGlzdChhbHRlcm5hdGluZyA9IDApKSkNCmBgYA0KDQojIyAqKjUuMiBCYXllc2lhbiBpbmZlcmVuY2UgZm9yIE1vZGVsIDMgdXNpbmcgYHJzdGFuYXJtYCoqDQoNCldlIHVzZSBgc3Rhbl9nbG1lcigpYCB0byBmaXQgTW9kZWwgMyB1c2luZyB0aGUgY29kZSBiZWxvdy4gV2UgdXNlIHRoZSBkZWZhdWx0IHByaW9ycyBhbmQgZGlzcGxheSB0aGUgKm1lZGlhbiogYW5kIHRoZSAqbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAoTUFEKSogb2YgdGhlIHBvc3RlcmlvciBkcmF3cyBvZiB0aGUgbW9kZWwgcGFyYW1ldGVycyBieSB1c2luZyB0aGUgYHByaW50KClgIGZ1bmN0aW9uLg0KDQpgYGB7ciBlY2hvPVQsIHJlc3VsdHM9J2hpZGUnfQ0KTTNfc3RhbmdsbWVyIDwtIHN0YW5fZ2xtZXIoaW1tdW4gfiBraWQycCArIG1vbTI1cCArIG9yZCArIGV0aG4gKyBtb21FZCArIGh1c0VkICsgbW9tV29yayArIHJ1cmFsICsgcGNJbmQ4MSArICgxIHwgbW9tKSArICgxIHwgY29tbSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwoImxvZ2l0IiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGd1SW1tdW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gMzQ5KQ0KYGBgDQoNCmBgYHtyfQ0KcHJpb3Jfc3VtbWFyeShvYmplY3QgPSBNM19zdGFuZ2xtZXIpDQpwcmludChNM19zdGFuZ2xtZXIsIGRpZ2l0cyA9IDIpDQpgYGANCg0KIyAqKjYuIE1vZGVsIDQ6IFRocmVlLWxldmVsIHJhbmRvbS1jb2VmZmljaWVudCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsKioNCg0KIyMgKio2LjEgTGlrZWxpaG9vZCBpbmZlcmVuY2UgZm9yIE1vZGVsIDQgdXNpbmcgYGdsbWVyKClgKioNCg0KRmluYWxseSwgd2Ugc2hvdyBhbiBleGFtcGxlIG9mIGEgdGhyZWUtbGV2ZWwgcmFuZG9tLWNvZWZmaWNpZW50IG1vZGVsLiBDb21wYXJlZCB0byByYW5kb20taW50ZXJjZXB0IG1vZGVscywgaW4gd2hpY2ggZmFtaWx5LXNwZWNpZmljIHByb2JhYmlsaXR5IGN1cnZlcyBhcmUgc2hpZnRlZCBob3Jpem9udGFsbHkgZGVwZW5kaW5nIG9uIHRoZSB2YWx1ZXMgb2YgdGhlIHJhbmRvbSBlZmZlY3RzLCB0aGUgY3VydmVzIGluIHJhbmRvbS1jb2VmZmljaWVudCBtb2RlbHMgYXJlIG5vIGxvbmdlciBqdXN0IHNoaWZ0ZWQsIGJ1dCB0aGVpciBzbG9wZSB2YXJpZXMgZGVwZW5kaW5nIG9uIHRoZSB2YWx1ZSBvZiB0aGUgcmFuZG9tIGNvZWZmaWNpZW50cy4gRm9yIGV4YW1wbGUsIHRoZSBlZmZlY3Qgb2Ygd2hldGhlciBhIGNoaWxkIHdhcyAyIHllYXJzIG9mIGFnZSBvciBvbGRlciBhdCB0aGUgdGltZSBvZiB0aGUgc3VydmV5ICh3aGljaCBtZWFucyB0aGF0IHRoZXkgd2VyZSBlbGlnaWJsZSBmb3IgdGhlIHZhY2NpbmUgYXQgdGhlIHRpbWUgb2YgdGhlIGludGVydmVudGlvbikgb24gd2hldGhlciB0aGV5IGhhZCByZWNlaXZlZCBhIGNvbXBsZXRlIHNldCBvZiBpbW11bml6YXRpb25zIG1heSB2YXJ5IGFjcm9zcyBjb21tdW5pdGllcy4gU3VjaCB2YXJpYWJpbGl0eSBjYW4gYmUgbW9kZWxlZCBieSBhZGRpbmcgdGhlIHRlcm0gJFx6ZXRhXnsoMyl9X3sya31raWQycFllc197aWprfSQgdG8gTW9kZWwgMyBhbmQgcmVuYW1pbmcgdGhlIGNvbW11bml0eS1sZXZlbCByYW5kb20gaW50ZXJjZXB0cyAkXHpldGFeeygzKX1fe2t9JCB0byAkXHpldGFeeygzKX1fezFrfSQsd2hpY2ggbGVhZHMgdG8gdGhlIGZvbGxvd2luZyByYW5kb20tY29lZmZpY2llbnQgbW9kZWwgJCRcdGV4dHJte2xvZ2l0fVx7UHIoaW1tdW5fe2lqa309MXwgeCwgXHpldGFeeygyKX1fe2prfSwgXHpldGFeeygzKX1fe2t9XH0gPSBcYmV0YV8xICsgXGJldGFfMiBraWQycFllc197aWprfSArIC4uLiArXGJldGFfNiBvcmQ3cF97aWprfStcYmV0YV83IGV0aG5OX3tqa30rLi4uXFwNCisgXGJldGFfezE0fSBtb21Xb3JrWWVzX3tqa30rXGJldGFfezE1fSBydXJhbF97a30rXGJldGFfezE2fSBwY0luZDgxX3trfSArXHpldGFeeygyKX1fe2prfStcemV0YV57KDMpfV97Mmt9a2lkMnBZZXNfe2lqa30rXHpldGFeeygzKX1fezFrfSQkDQoNCkdpdmVuIHRoZSBjb3ZhcmlhdGVzICoqWCoqcyBhdCBhbGwgdGhyZWUgbGV2ZWxzLCB0aGUgcmFuZG9tIGludGVyY2VwdCBhdCB0aGUgZmFtaWx5IGxldmVsIGhhcyBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuIHplcm8gYW5kIHZhcmlhbmNlICRccHNpXnsoMil9JCwgJFx6ZXRhXnsoMil9X3tqa30gXHNpbSBOKDAsIFxwc2leeygyKX0pJC4gVGhlIHJhbmRvbSBpbnRlcmNlcHQgYW5kIHRoZSByYW5kb20gc2xvcGUgYXQgdGhlIGNvbW11bml0eS1sZXZlbCwgd2hpY2ggYXJlIHdyaXR0ZW4gYXMgJFx6ZXRhXnsoMyl9X3sxa30kIGFuZCAkXHpldGFeeygzKX1fezJrfSQsIHJlc3BlY3RpdmVseSwgYXJlIGJpdmFyaWF0ZSBub3JtYWwgd2l0aCB6ZXJvIG1lYW5zIGFuZCBhbiB1bnN0cnVjdHVyZWQgY292YXJpYW5jZSBtYXRyaXggJCRcYm9sZHN5bWJvbFxQc2leeygzKX09IFxiZWdpbntibWF0cml4fSBccHNpXnszfV97MTF9ICZccHNpXnszfV97MTJ9IFxcDQpccHNpXnszfV97MjF9ICZccHNpXnszfV97MjJ9DQpcZW5ke2JtYXRyaXh9IFxlcXVpdiBcXA0KXGJlZ2lue2JtYXRyaXh9IFZhcihcemV0YV57KDMpfV97MWt9fFhfaykgJiBDb3YoXHpldGFeeygzKX1fezFrfSwgXHpldGFeeygzKX1fezJrfXxYX2spIFxcDQpDb3YoXHpldGFeeygzKX1fezJrfSwgXHpldGFeeygzKX1fezFrfXxYX2spICYgIFZhcihcemV0YV57KDMpfV97Mmt9fFhfaykgXFwNClxlbmR7Ym1hdHJpeH0kJCB3aGVyZSAkXHBzaV57KDMpfV97MjF9PVxwc2leeygzKX1fezEyfSQuDQoNCkluIGBnbG1lcigpYCwgd2UgdXNlIHRoZSAqKisqKiBvcGVyYXRvciB0byAiYWRkIiB0aGlzIHJhbmRvbSBzbG9wZSBhdCB0aGUgY29tbXVuaXR5IGxldmVsLiBUaGUgY29ycmVsYXRpb24gbWF0cml4IGlzIG5vdCBzaG93biBieSBkZWZhdWx0LCBidXQgd2UgY2FuIG9idGFpbiBpdCBieSBzcGVjaWZ5aW5nIGBjb3JyZWxhdGlvbj1UUlVFYCBpbiB0aGUgYHByaW50KClgIGZ1bmN0aW9uLg0KDQpgYGB7ciA2LjF9DQpNNCA8LSBnbG1lcihpbW11biB+IGtpZDJwICsgbW9tMjVwICsgb3JkICsgZXRobiArIG1vbUVkICsgaHVzRWQgKyBtb21Xb3JrICsgcnVyYWwgKyBwY0luZDgxICsgKDEgfCBtb20pICsgKDEgKyBraWQycHwgY29tbSksICANCiAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCgibG9naXQiKSwgDQogICAgICAgIGRhdGEgPSBndUltbXVuLCBjb250cm9sID0gZ2xtZXJDb250cm9sKG9wdGltaXplciA9ICJib2J5cWEiKSwNCiAgICAgICAgIG5BR1EgPSAxKQ0Kc3VtbWFyeShNNCkNCiMgcHJpbnQgdGhlIG1vZCByZXN1bHRzIHdpdGggY29ycmVsYXRpb24gYW1vbmcgZml4ZWQgZWZmZWN0cw0KcHJpbnQoTTQsIGNvcnJlbGF0aW9uPVRSVUUpDQpgYGANCg0KSW4gdGhlIG5leHQgUiBjaHVuaywgd2UgcHJvZHVjZSBhIGNhdGVycGlsbGFyIHBsb3Qgb2YgdGhlIGNvbmRpdGlvbmFsIG1vZGVzIG9mIHRoZSByYW5kb20gaW50ZXJjZXB0cyBmb3IgZmFtaWxpZXMgYW5kIHRoZSByYW5kb20gaW50ZXJjZXB0cyBhbmQgc2xvcGVzIGZvciBjb21tdW5pdGllcy4NCg0KYGBge3J9DQojIGZvciBmYW1pbGllcw0KbGF0dGljZTo6ZG90cGxvdChyYW5lZihNNCwgd2hpY2ggPSAibW9tIiwgY29uZFZhciA9IFRSVUUpLCBzY2FsZXMgPSBsaXN0KHkgPSBsaXN0KGFsdGVybmF0aW5nID0gMCkpKQ0KIyBmb3IgY29tbXVuaXRpZXMNCmxhdHRpY2U6OmRvdHBsb3QocmFuZWYoTTQsIHdoaWNoID0gImNvbW0iLCBjb25kVmFyID0gVFJVRSksIHNjYWxlcyA9IGxpc3QoeSA9IGxpc3QoYWx0ZXJuYXRpbmcgPSAwKSkpDQpgYGANCg0KIyMgKio2LjIgQmF5ZXNpYW4gaW5mZXJlbmNlIGZvciBNb2RlbCA0IHVzaW5nIGByc3RhbmFybWAqKg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHVzZSBgc3Rhbl9nbG1lcigpYCB0byBmaXQgTW9kZWwgNC4gV2UgbWVudGlvbmVkIGRlZmF1bHQgKHdlYWtseSBpbmZvcm1hdGl2ZSkgcHJpb3JzIGluIGByc3RhbmFybWAsIHdoaWNoIGFyZSBkZXNpZ25lZCB0byBwcm92aWRlIG1vZGVyYXRlIHJlZ3VsYXJpemF0aW9uIGFuZCBoZWxwIHN0YWJpbGl6ZSBjb21wdXRhdGlvbi4gTW9yZSBpbmZvcm1hdGlvbiBvbiBwcmlvcnMgaXMgYXZhaWxhYmxlIGluIHRoZSB2aWduZXR0ZSBbUHJpb3IgRGlzdHJpYnV0aW9uIGZvciByc3RhbmFybSBNb2RlbHNdKGh0dHA6Ly9tYy1zdGFuLm9yZy9yc3RhbmFybS9hcnRpY2xlcy9wcmlvcnMuaHRtbCkuIFdlIHVzZSB0aGUgZGVmYXVsdCBwcmlvciBkaXN0cmlidXRpb24gZm9yIHBhcmFtZXRlcnMgaW4gYE00X3N0YW5nbG1lcmAgYnkgbm90IHNwZWNpZnlpbmcgYW55IHByaW9yIG9wdGlvbnMgaW4gdGhlIGBzdGFuX2dsbWVyKClgIGZ1bmN0aW9uOg0KDQpgYGB7ciBlY2hvPVQsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KTTRfc3RhbmdsbWVyIDwtIHN0YW5fZ2xtZXIoaW1tdW4gfiBraWQycCArIG1vbTI1cCArIG9yZCArIGV0aG4gKyBtb21FZCArIGh1c0VkICsgbW9tV29yayArIHJ1cmFsICsgcGNJbmQ4MSArICgxIHwgbW9tKSArICgxICsga2lkMnB8IGNvbW0pLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKCJsb2dpdCIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZ3VJbW11biwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDM0OSkNCmBgYA0KDQpUaGUgd2F5IGByc3RhbmFybWAgYXR0ZW1wdHMgdG8gYWNoaWV2ZSB3ZWFrbHkgaW5mb3JtYXRpdmUgcHJpb3JzIGJ5IGRlZmF1bHQgaXMgdG8gaW50ZXJuYWxseSBhZGp1c3QgdGhlIHNjYWxlcyBvZiB0aGUgcHJpb3JzLiBXZSBleHBsYWluIGhvdyB0aGlzIHdvcmtzIGJlbG93Og0KDQpGaXJzdCwgd2UgY2FuIHVzZSB0aGUgcHJpb3Jfc3VtbWFyeSgpIGZ1bmN0aW9uIHRvIHNob3cgaG93IHRoZSBzcGVjaWZpY2F0aW9uIG9mIHByaW9yIGRpc3RyaWJ1dGlvbiB3b3JrcyBpbiB0aGUgcnN0YW5hcm0gcGFja2FnZS4NCg0KYGBge3J9DQpwcmlvcl9zdW1tYXJ5KG9iamVjdCA9IE00X3N0YW5nbG1lcikNCmBgYA0KDQpTdGFydGluZyBmcm9tIHRoZSBib3R0b20gcGFydCBvZiB0aGUgb3V0cHV0Og0KDQotICAgVGhlIGBwcmlvcl9jb3ZhcmlhbmNlYCBhcHBsaWVzIHRvIGNvdmFyaWFuY2UgbWF0cmljZXMgaW4gbXVsdGlsZXZlbCBtb2RlbHMgd2l0aCB2YXJ5aW5nIHNsb3BlcyBhbmQgaW50ZXJjZXB0cy4gSW4gb3RoZXIgd29yZHMsIGl0IHNwZWNpZmllcyBhIHByaW9yIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIHVua25vd24gY292YXJpYW5jZSBtYXRyaWNlcyBvZiB0aGUgZ3JvdXAtc3BlY2lmaWMgY29lZmZpY2llbnRzLiBTcGVjaWZpY2FsbHksIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBpcyBkZWNvbXBvc2VkIGludG8gKmNvcnJlbGF0aW9uIG1hdHJpeCogYW5kICp2YXJpYW5jZSouIFRoZSAqdmFyaWFuY2UqIGlzIGluIHR1cm4gZGVjb21wb3NlZCBpbnRvIHRoZSBwcm9kdWN0IG9mIGEgKnNpbXBsZXggdmVjdG9yKiBhbmQgKnRoZSB0cmFjZSBvZiB0aGUgbWF0cml4Ki4gRmluYWxseSwgdGhlICp0cmFjZSogaXMgdGhlIHByb2R1Y3Qgb2YgKnRoZSBvcmRlciBvZiB0aGUgbWF0cml4KiBhbmQgKnRoZSBzcXVhcmUgb2YgYSBzY2FsZSBwYXJhbWV0ZXIqLiBUaGUgZm9sbG93aW5nIGRpYWdyYW0gc2hvd3MgdGhlIGRlY29tcG9zZWQgY29tcG9uZW50cy4NCg0KYGBge3J9DQpsaWJyYXJ5KERpYWdyYW1tZVIpDQoNCkRpYWdyYW1tZVI6OmdyVml6KCJkaWdyYXBoIHsNCiAgDQpncmFwaFtsYXlvdXQgPSBkb3QsIHJhbmtkaXIgPSBMUl0NCg0KJ0NvdmFyaWFuY2UgbWF0cml4Jw0KJ0NvcnJlbGF0aW9uIG1hdHJpeCcNCidWYXJpYW5jZScNCidTaW1wbGV4IHZlY3RvcicNCidUaGUgdHJhY2Ugb2YgdGhlIG1hdHJpeCcNCidUaGUgb3JkZXIgb2YgdGhlIG1hdHJpeCcNCidUaGUgc3F1YXJlIG9mIGEgc2NhbGUgcGFyYW1ldGVyJw0KDQonQ292YXJpYW5jZSBtYXRyaXgnIC0+ICdDb3JyZWxhdGlvbiBtYXRyaXgnDQonQ292YXJpYW5jZSBtYXRyaXgnIC0+ICdWYXJpYW5jZScgDQonVmFyaWFuY2UnIC0+ICdTaW1wbGV4IHZlY3RvcicgDQonVmFyaWFuY2UnIC0+ICdUaGUgdHJhY2Ugb2YgdGhlIG1hdHJpeCcNCidUaGUgdHJhY2Ugb2YgdGhlIG1hdHJpeCcgLT4gJ1RoZSBvcmRlciBvZiB0aGUgbWF0cml4Jw0KJ1RoZSB0cmFjZSBvZiB0aGUgbWF0cml4JyAtPidUaGUgc3F1YXJlIG9mIGEgc2NhbGUgcGFyYW1ldGVyJw0KfSIpDQoNCmBgYA0KDQpPdmVyYWxsLCB0aGlzIHByaW9yIG9uIGEgKmNvdmFyaWFuY2UgbWF0cml4KiBpcyByZXByZXNlbnRlZCBieSB0aGUgYGRlY292KClgIGZ1bmN0aW9uLg0KDQpUaGUgcHJpb3IgZm9yIGEgKmNvcnJlbGF0aW9uIG1hdHJpeCogaXMgY2FsbGVkIExLSiwgd2hpY2ggaXMgcHJvcG9ydGlvbmFsIHRvIHRoZSBkZXRlcm1pbmFudCBvZiB0aGUgY29ycmVsYXRpb24gbWF0cml4IHJhaXNlZCB0byB0aGUgcG93ZXIgb2YgYSBwb3NpdGl2ZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIgbWludXMgb25lLiBUaGUgZGVmYXVsdCBgcmVnLiA9MWAgaW5kaWNhdGVzIHRoaXMgcHJpb3IgaXMgam9pbnRseSB1bmlmb3JtIG92ZXIgYWxsIGNvcnJlbGF0aW9uIG1hdHJpY2VzIG9mIHRoYXQgc2l6ZS4NCg0KRm9yIHRoZSAqc2ltcGxleCB2ZWN0b3IqLCBhIHN5bW1ldHJpYyBEaXJjaGxldCBwcmlvciBpcyB1c2VkLiBUaGUgZGVmYXVsdCBgY29uYy4gPSAxYCByZXByZXNlbnRzIGEgc2luZ2xlIChwb3NpdGl2ZSkgYGNvbmNlbnRyYXRpb25gIHBhcmFtZXRlciBhbmQgdGhlIHByaW9yIGlzIGpvaW50bHkgdW5pZm9ybSBvdmVyIHRoZSBzcGFjZSBvZiBzaW1wbGV4IHZlY3RvcnMgb2YgdGhhdCBzaXplLg0KDQoqVGhlIHRyYWNlIG9mIGEgY292YXJpYW5jZSBtYXRyaXgqIGlzIGVxdWFsIHRvIHRoZSBzdW0gb2YgdmFyaWFuY2VzLiBXZSBzZXQgdGhlIHRyYWNlIGVxdWFsIHRvIHRoZSBwcm9kdWN0IG9mIHRoZSBvcmRlciBvZiB0aGUgY292YXJpYW5jZSBtYXRyaXggYW5kIHRoZSBzcXVhcmUgb2YgYSBwb3NpdGl2ZSBzY2FsZSBwYXJhbWV0ZXIuIFRoZSBwYXJ0aWN1bGFyIHZhcmlhbmNlcyBhcmUgc2V0IGVxdWFsIHRvIHRoZSBwcm9kdWN0IG9mIGEgc2ltcGxleCB2ZWN0b3IgKHdoaWNoIGlzIG5vbi1uZWdhdGl2ZSBhbmQgc3VtcyB0byAxKSBhbmQgdGhlIHNjYWxhciB0cmFjZS4gSW4gb3RoZXIgd29yZHMsIGVhY2ggZWxlbWVudCBvZiB0aGUgc2ltcGxleCB2ZWN0b3IgcmVwcmVzZW50cyB0aGUgcHJvcG9ydGlvbiBvZiB0aGUgdHJhY2UgYXR0cmlidXRhYmxlIHRvIHRoZSBjb3JyZXNwb25kaW5nIHZhcmlhYmxlLg0KDQpGb3IgdGhlIHBvc2l0aXZlICpzY2FsZSBwYXJhbWV0ZXIqLCB3ZSB1c2UgYSBzY2FsZS1pbnZhcmlhbnQgcHJpb3IgZGlzdHJpYnV0aW9uLCB3aGljaCBpcyBhIEdhbW1hIGRpc3RyaWJ1dGlvbiBpbiB0aGlzIGNhc2UuIFRoZSBkZWZhdWx0IGBzaGFwZSA9IDEsIHNjYWxlID0gMWAgaW1wbGllcyBhIHVuaXQtZXhwb25lbnRpYWwgZGlzdHJpYnV0aW9uLg0KDQpGb3IgbW9yZSBkZXRhaWxzIG9uIHByaW9yIGRpc3RyaWJ1dGlvbnMgZm9yIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCAkXGJvbGRzeW1ib2xcUHNpXnsoMyl9JCBpbiByYW5kb20tY29lZmZpY2llbnQgbW9kZWxzLCBzZWUgdGhlIGJvdHRvbSBwYXJ0IG9mIHRoZSB0dXRvcmlhbCBbSW50cm8gdG8gbXVsdGlsZXZlbCBtb2RlbGluZyB1c2luZyAqKnJzdGFuYXJtKipdKGh0dHBzOi8vbWMtc3Rhbi5vcmcvdXNlcnMvZG9jdW1lbnRhdGlvbi9jYXNlLXN0dWRpZXMvdHV0b3JpYWxfcnN0YW5hcm0uaHRtbCNmbnJlZjEpIGFuZCBhbHNvIHRoZSBmdW5jdGlvbiBleHBsYW5hdGlvbiBbUHJpb3IgZGlzdHJpYnV0aW9ucyBhbmQgb3B0aW9uc10oaHR0cHM6Ly9tYy1zdGFuLm9yZy9yc3RhbmFybS9yZWZlcmVuY2UvcHJpb3JzLmh0bWwpLg0KDQotICAgVGhlIGRlZmF1bHQgcHJpb3Igb24gYSAqKnJlZ3Jlc3Npb24gY29lZmZpY2llbnQqKiAkXGJldGFfayQgaXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggemVybyBtZWFuLiBUaGUgZGVmYXVsdCBwcmlvciBzY2FsZSBkZXBlbmRzIG9uIHRoZSBmYW1pbHkgb2YgdGhlIHByaW9yIGJlaW5nIHVzZWQsIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpbiB0aGlzIGNhc2UsIHdoaWNoIGlzIDIuNSBieSBkZWZhdWx0IHdoZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzIGhhdmUgYmVlbiBzY2FsZWQgdG8gaGF2ZSBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiAwLjUgKEdlbG1hbiBldCBhbC4sIDIwMDgpIChzZWUgW1ByaW9yIERpc3RyaWJ1dGlvbiBmb3IgcnN0YW5hcm0gTW9kZWxzXShodHRwOi8vbWMtc3Rhbi5vcmcvcnN0YW5hcm0vYXJ0aWNsZXMvcHJpb3JzLmh0bWwpKS4gSW4gb3JkZXIgZm9yIHRoZSBkZWZhdWx0IHRvIGJlIHdlYWtseSBpbmZvcm1hdGl2ZSwgKipyc3RhbmFybSoqIGFkanVzdHMgdGhlIHNjYWxlcyBvZiB0aGUgcHJpb3JzIGFjY29yZGluZyB0byB0aGUgc3RhbmRhcmQgZGV2aWF0aW9ucyBvZiB0aGUgY29ycmVzcG9uZGluZyBleHBsYW5hdG9yeSB2YXJpYWJsZXMuIEFzIGEgcmVzdWx0LCB0aGUgcHJpb3Igc2NhbGVzIHVzZWQgd2VyZSBbNS45NSwgNS4wMCwgNS4yNywgLi4uXSByZXNwZWN0aXZlbHkgZm9yIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4NCg0KLSAgIEZvciB0aGUgKippbnRlcmNlcHRzKiosIHRoZSBkZWZhdWx0IHByaW9yIGlzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aXRoIHplcm8gbWVhbiBhbmQgYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMi41LiBUaGUgbm90ZSBpbiBwYXJlbnRoZXNlcyBpbmZvcm1zIHVzIHRoYXQgdGhlIHByaW9yIGFwcGxpZXMgdG8gdGhlIGludGVyY2VwdCBhZnRlciBhbGwgcHJlZGljdG9ycyBoYXZlIGJlZW4gY2VudGVyZWQuIEluIHRoaXMgY2FzZSwgd2UgdmlldyB0aGUgaW50ZXJjZXB0IGFzIHRoZSB2YWx1ZSBvZiB0aGUgZml4ZWQgcGFydCBvZiB0aGUgbGluZWFyIHByZWRpY3RvciB3aGVuIGFsbCBjb3ZhcmlhdGVzIGFyZSBlcXVhbCB0byB0aGVpciBtZWFucyBpbnN0ZWFkIG9mIHplcm8sIHdoaWNoIG1ha2VzIHRoZSBpbnRlcmNlcHQgbW9yZSBpbnRlcnByZXRhYmxlIGFuZCBtZWFuaW5nZnVsLg0KDQpUbyBkaXNhYmxlIGF1dG9tYXRpYyByZXNjYWxpbmcsIHNpbXBseSBzcGVjaWZ5IGEgcHJpb3Igb3RoZXIgdGhhbiB0aGUgZGVmYXVsdCwgd2UgY2FuIGV4cGxpY2l0bHkgc2V0IHRoZSBgYXV0b3NjYWxlID0gRkFMU0VgIGFyZ3VtZW50LiBJbiBhZGRpdGlvbiwgd2UgY2FuIHNwZWNpZnkgZmxhdCAobm9uLWluZm9ybWF0aXZlKSBwcmlvcnMgYnkgc2V0IGBwcmlvcj1OVUxMYCBpbiB0aGUgYHN0YW5fZ2xtZXIoKWAgZnVuY3Rpb24uIFVzZXJzIGNhbiBhbHNvIHNwZWNpZnkgcHJpb3JzIHRoYXQgcmVmbGVjdCB0aGVpciBwcmlvciBpbmZvcm1hdGlvbiAoc2VlIFtQcmlvciBkaXN0cmlidXRpb25zIGFuZCBvcHRpb25zXShodHRwczovL21jLXN0YW4ub3JnL3JzdGFuYXJtL3JlZmVyZW5jZS9wcmlvcnMuaHRtbCNkZXRhaWxzKSBmb3IgbW9yZSBpbmZvcm1hdGlvbikuDQoNCmBgYHtyIDYuNCwgd2FybmluZz1GQUxTRX0NCnByaW50KE00X3N0YW5nbG1lciwgZGlnaXRzID0gMikNCnByaW50KE00LCBkaWdpdHMgPSAyKQ0KYGBgDQoNCkNvbXBhcmluZyB0aGUgRkIgZXN0aW1hdGVzIGFuZCBNTCBlc3RpbWF0ZXMgZm9yIE1vZGVsIDQsIHdlIG5vdGljZSB0aGF0IHRoZSBGQiBlc3RpbWF0ZXMgYXJlIGZ1cnRoZXIgZnJvbSB6ZXJvIHRoYW4gdGhlIE1MIGVzdGltYXRlcy4NCg0KTGFzdGx5LCBpbiB0aGUgbmV4dCBSIGNodW5rLCB3ZSB1c2UgTW9kZWwgNCBhcyBhbiBleGFtcGxlIHRvIHNob3cgaG93IHRvIGRpc3BsYXkgSFRNTCB0YWJsZXMgZm9yIG1vZGVscyBlc3RpbWF0ZWQgZnJvbSBgZ2xtZXIoKWAgYW5kIGBzdGFuX2dsbWVyYC4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgU3VtbWFyeSBvZiBtaXhlZCBtb2RlbHMgYXMgSFRNTCB0YWJsZQ0KbGlicmFyeShzalBsb3QpDQoNCnRhYl9tb2RlbChNNCkNCg0KIyBUaWR5aW5nIG1ldGhvZHMgZm9yIGFuIHJzdGFuYXJtIG1vZGVsDQpsaWJyYXJ5KGJyb29tLm1peGVkKQ0KICMgbm9uLXZhcnlpbmcgKCJwb3B1bGF0aW9uIikgcGFyYW1ldGVycw0KICB0aWR5KE00X3N0YW5nbG1lciwgY29uZi5pbnQgPSBUUlVFLCBwcm9iID0gMC41KQ0KICB0aWR5KE00X3N0YW5nbG1lciwgY29uZi5pbnQgPSBUUlVFLCBjb25mLm1ldGhvZCA9ICJIUERpbnRlcnZhbCIsIHByb2IgPSAwLjUpDQoNCiAgIyBoaWVyYXJjaGljYWwgc2QgJiBjb3JyZWxhdGlvbiBwYXJhbWV0ZXJzDQogIHRpZHkoTTRfc3RhbmdsbWVyLCBlZmZlY3RzID0gInJhbl9wYXJzIikNCg0KICAjIGdyb3VwLXNwZWNpZmljIGRldmlhdGlvbnMgZnJvbSAicG9wdWxhdGlvbiIgcGFyYW1ldGVycw0KICB0aWR5KE00X3N0YW5nbG1lciwgZWZmZWN0cyA9ICJyYW5fdmFscyIpDQoNCiAgIyBnbGFuY2UgbWV0aG9kDQpnbGFuY2UoTTRfc3RhbmdsbWVyKQ0KYGBgDQoNCiMgKio3LiBTdW1tYXJ5KioNCg0KSW4gdGhpcyB0dXRvcmlhbCwgd2UgaWxsdXN0cmF0ZWQgaG93IHRvIGZpdCBtdWx0aWxldmVsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIHdpdGhpbiBhIGZ1bGx5IEJheWVzaWFuIGZyYW1ld29yayBpbiBgcnN0YW5hcm1gLiBSYXRoZXIgdGhhbiBwZXJmb3JtaW5nIE1MIGVzdGltYXRpb24gdXNpbmcgdGhlIGBnbG1lcigpYCBmdW5jdGlvbiBpbiBgbG1lNGAsIHdlIHNob3dlZCBob3cgdG8gcGVyZm9ybSBGQiBlc3RpbWF0aW9uICh2aWEgdGhlIEhNQyBhcHByb2FjaCkgdXNpbmcgdGhlIGBzdGFuX2dsbWVyKClgIGZ1bmN0aW9uLiBgc3Rhbl9nbG1lcigpYGlzIGVhc3kgdG8gdXNlIGFzIHRoZSBzeW50YXggaXMgc2ltaWxhciB0byB0aGF0IG9mIGBnbG1lcigpYC4gU2V2ZXJhbCBhZHZhbnRhZ2VzIG9mIGVzdGltYXRpbmcgdGhlc2UgbW9kZWxzIHVzaW5nIGByc3RhbmFybWAgcmF0aGVyIHRoYW4gdGhlIGBsbWU0YCBwYWNrYWdlIGhhdmUgYmVlbiBkZW1vbnN0cmF0ZWQgaW4gdGhpcyB0dXRvcmlhbC4gSGVyZSBpcyBhIHN1bW1hcnkgb2Ygc29tZSBtYWpvciBhZHZhbnRhZ2VzOg0KDQoxLiAgYHJzdGFuYXJtYCBwcm92aWRlcyBiZXR0ZXIgdW5jZXJ0YWludHkgZXN0aW1hdGVzLCB0aGFuIGRvZXMgdGhlIGBsbWU0YCBwYWNrYWdlIGJ5IHBlcmZvcm1pbmcgZnVsbHkgQmF5ZXNpYW4gaW5mZXJlbmNlLg0KDQoyLiAgRm9yIG1vZGVscyB3aXRoIG1vcmUgdGhhbiBvbmUgcmFuZG9tIGVmZmVjdCwgdGhlIGBsbWU0YCBwYWNrYWdlIGNhbiBvbmx5IHVzZSB0aGUgTGFwbGFjZSBhcHByb3hpbWF0aW9uLCB3aGljaCBtYXkgcGVyZm9ybSBwb29ybHksIGVzcGVjaWFsbHkgZm9yIGJpbmFyeSByZXNwb25zZXMgd2l0aCBzbWFsbCBjbHVzdGVyIHNpemVzLg0KDQozLiAgYHJzdGFuYXJtYCBwcm92aWRlcyBwb3N0ZXJpb3IgZHJhd3Mgb2YgdGhlIHJhbmRvbSBlZmZlY3RzIGZyb20gdGhlIGpvaW50IHBvc3Rlcmlvciwgd2hlcmVhcyB0aGUgYHJhbmVmKClgIGZ1bmN0aW9uLCBhZnRlciBNTCBlc3RpbWF0aW9uIHdpdGggYGdsbWVyKClgLCBwcm92aWRlcyBvbmx5IHRoZSBjb25kaXRpb25hbCBwb3N0ZXJpb3IgbW9kZXMuIEhhdmluZyBwb3N0ZXJpb3IgZHJhd3MgYWxsb3dzIHVzIHRvIGdldA0KDQogICAgLSAgIHRoZSBtZWFucyBhbmQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbnMgb2YgdGhlIHBvc3RlcmlvciBkcmF3cyBvZiB0aGUgcmFuZG9tIGVmZmVjdHMsIG1hcmdpbmFsIG92ZXIgYWxsIG90aGVyIHBhcmFtZXRlcnMgYnkgdXNpbmcgYHN1bW1hcnkoKWA7DQogICAgLSAgIHRoZSBtZWRpYW4gYW5kIE1BRCBvZiB0aGUgcG9zdGVyaW9yIGRyYXdzIG9yIHRoZSByYW5kb20gZWZmZWN0cyBieSB1c2luZyBgcHJpbnQoKWA7DQogICAgLSAgIGFueSBvdGhlciBzdW1tYXJ5IG9mIHRoZSBwb3N0ZXJpb3IgZHJhd3MsIHN1Y2ggYXMgZGlmZmVyZW5jZXMgb3IgcmFua2luZ3Mgb2YgcmFuZG9tIGVmZmVjdHMgb2YgZGlmZmVyZW50IGNsdXN0ZXJzLCB3aXRoIGFzc29jaWF0ZWQgbWVhc3VyZXMgb2YgdW5jZXJ0YWludHkuDQoNCjQuICBUaGVyZSBhcmUgYSB2YXJpZXR5IG9mIGRhdGEgdmlzdWFsaXphdGlvbiBzdHJhdGVnaWVzIGF2YWlsYWJsZSBmb3IgdGhlIGByc3RhbmFybWAgcGFja2FnZToNCg0KICAgIC0gICB0aGUgYGJheWVzcGxvdGAgcGFja2FnZSBwcm92aWRlcyBhIGxpYnJhcnkgb2YgcGxvdHRpbmcgZnVuY3Rpb25zIHRvIHZpc3VhbGl6ZSB0aGUgcG9zdGVyaW9yIGRyYXdzOw0KICAgIC0gICB3ZSBjYW4gbWFuaXB1bGF0ZSB0aGUgcGxvdHMgd2l0aCB0aGUgcG9zdGVyaW9yIGRyYXdzIHJldHJpZXZlZCBmcm9tIGEgZnVsbCBCYXllczsNCiAgICAtICAgdGhlIGBzaGlueXN0YW5gIHBhY2thZ2UgcHJvdmlkZXMgaW50ZXJhY3RpdmUgZGlhZ25vc3RpY3MgYW5kIHBvc3RlcmlvciBhbmFseXNlcyBiYXNlZCBvbiB0aGUgcG9zdGVyaW9yIGRyYXdzLg0KDQo1LiAgV2UgY2FuIGluY29ycG9yYXRlIHByaW9yIGluZm9ybWF0aW9uIGJ5IHVzaW5nIGByc3RhbmFybWAuIFdlIHNob3dlZCBob3cgd2Vha2x5IGluZm9ybWF0aXZlIHByaW9ycyBhcmUgc3BlY2lmaWVkIGJ5IHByaW9yIGF1dG9tYXRpYyBzY2FsZSBhZGp1c3RtZW50cy4NCg0KSW4gc3VtLCBhbHRob3VnaCBmaXR0aW5nIG1vZGVscyBieSBhcHByb3hpbWF0ZSBNTCBpbiB0aGUgYGxtZTRgIHBhY2thZ2UgdGVuZHMgdG8gYmUgbXVjaCBmYXN0ZXIgdGhhbiBmaXR0aW5nIHRoZSBtb2RlbHMgaW4gYHJzdGFuYXJtYCwgdGhlIGJlbmVmaXRzIG9mIGVtcGxveWluZyBmdWxseSBCYXllc2lhbiBpbmZlcmVuY2UgdmlhIE1DTUMgaW4gYHJzdGFuYXJtYCBvdXR3ZWlnaCB0aGUgY29zdC4gV2UgaG9wZSB0aGF0IHRoaXMgdHV0b3JpYWwgaGFzIHByb3ZpZGVkIHlvdSB3aXRoIGVub3VnaCBpbmZvcm1hdGlvbiB0byB0ZXN0IHRoaXMgb3V0IHlvdXJzZWxmIGFuZCBlbm91Z2ggc3VwcG9ydCB0byBhcHBseSB3aGF0IHlvdSd2ZSBsZWFybmVkIGluIHlvdXIgb3duIHJlc2VhcmNoLg0KDQojIFJFRkVSRU5DRQ0KDQpgYGB7cn0NCmxpYnJhcnkoa25pdGNpdGF0aW9ucykNCmxpYnJhcnkoYmlidGV4KQ0KcmVhZC5iaWJ0ZXgoIk1MUi5iaWIiKQ0KYGBgDQo=