Creative Commons License

library(tidyverse)
## ── Attaching packages ───────────────────────────── tidyverse 1.3.1 ──
## ✔ ggplot2 3.3.5     ✔ purrr   0.3.4
## ✔ tibble  3.1.6     ✔ dplyr   1.0.8
## ✔ tidyr   1.2.0     ✔ stringr 1.4.0
## ✔ readr   2.1.2     ✔ forcats 0.5.1
## ── Conflicts ──────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()

1 Concepts

  • Experimental units representative for population: Randomisation
  • Replication: technical vs biological, sample size - power
  • Sources of variation: technical, biological, within subject, between subject

2 Replication

Two pager on Replication in nature methods: [PDF]

2.1 Example

Consider a single cell RNA-seq experiment where researchers want to assess the effect of drug treatment on gene expression

Potential research questions

  • Is there a difference in average gene expression between treated and non treated samples
  • Is there a difference in variability of gene expression between treated and non treated samples

2.2 Sources of variation

TABLE 1 NATURE METHODS | VOL.11 NO.9 | SEPTEMBER 2014 | 879 - 880

Replicate type Replicate category\(^\text{a}\)
Colonies B
Strains B
Animal study subjects Cohoused groups B
Gender B
Individuals B
Organs from sacrificed animals B
Methods for dissociating cells from tissue T
Sample preparation Dissociation runs from given tissue sample T
Individual cells B
RNA-seq library construction T
Runs from the library of a given cell T
Sequencing Reads from different transcript molecules V\(^\text{b}\)
Reads with unique molecular identifier (UMI) from a given transcript molecule T
  1. Replicates are categorized as biological (B), technical (T) or of variable type (V).
  2. Sequence reads serve diverse purposes depending on the application and how reads are used in analysis.

2.3 At which level should we replicate?

  • \(\text{var}(X)=\sigma^2_A+\sigma^2_C+\sigma^2_M=\sigma^2_{TOT}\)
  • \(\text{var}(\bar{X})=\frac{\sigma^2_A}{n_A}+\frac{\sigma^2_C}{n_A n_C} + \frac{\sigma^2_M}{n_A n_C n_M}\)

Figure 1 NATURE METHODS | VOL.11 NO.9 | SEPTEMBER 2014 | 879 - 880

  1. Three levels of replication (two biological, one technical) with animal, cell and measurement replicates normally distributed with a mean across animals of 10 and ratio of variances 1:2:0.5. Solid green (biological) and blue (technical) dots show how a measurement of the expression (X = 12) samples from all three sources of variation. Distribution s.d. is shown as horizontal lines.
  2. Expression variance, Var(X), and variance of expression mean, Var(\(\bar X\)), computed across 10,000 simulations of nAnCnM = 48 measurements for unique combinations of the number of animals (nA = 1 to 48), cells per animal (nC = 1 to 48) and technical replicate measurements per cell (nM = 1 and 3). The ratio of Var(X) and Var(\(\bar X\)) is the effective sample size, n, which corresponds to the equivalent number of statistically independent measurements. Horizontal dashed lines correspond to biological and total variation. Error bars on Var(X) show s.d. from the 10,000 simulated samples (nM = 1).

  • Cost benefit trade-off: cost difference between biological and technical replicates
  • Typically, biological variability is substantially larger than technical variability

\(\rightarrow\) commit resources to sampling biologically relevant variables

\(\rightarrow\) unless measures of technical variability are of interest then increasing number of technical measurements is valuable.

  • Good experimental design practice includes planning for replication.

    1. Identify the research questions to be answered with experiments.
    2. Determine proportion of variability induced by each step.
    3. Be aware for pseudoreplication and aim for statistically independent replicates.

3 Power, sample size and other design aspects

Reading materials: Nature Methods (2013), 10(12), 1139–1140

3.1 Intermezzo linear regression in matrix form

  • Linear regression is a very important statistical tool to study the association between variables and to build prediction models.

3.1.1 Scalar form

  • Consider a vector of predictors \(\mathbf{x}=(x_1,\ldots,x_p)\) and
  • a real-valued response \(Y\)
  • then the linear regression model can be written as \[ Y=f(\mathbf{x}) +\epsilon=\beta_0+\sum\limits_{j=1}^p x_j\beta_j + \epsilon \] with i.i.d. \(\epsilon\sim N(0,\sigma^2)\)

3.1.2 Vector/Matrix form

  • \(n\) observations \((\mathbf{x}_1,y_1) \ldots (\mathbf{x}_n,y_n)\) with \(\mathbf{x}_1^T=[1 x_1 \ldots x_p]\)
  • Regression in matrix notation \[\mathbf{Y}=\mathbf{X\beta} + \mathbf{\epsilon}\] with \(\mathbf{Y}=\left[\begin{array}{c}y_1\\ \vdots\\y_n\end{array}\right]\), \(\mathbf{X}=\left[\begin{array}{cccc} 1&x_{11}&\ldots&x_{1p}\\ \vdots&\vdots&&\vdots\\ 1&x_{n1}&\ldots&x_{np} \end{array}\right]\) or \(\mathbf{X}=\left[\begin{array}{c} \mathbf{x}_1^T\\\vdots\\\mathbf{x}_n^T\end{array}\right]\), \(\boldsymbol{\beta}=\left[\begin{array}{c}\beta_0\\ \vdots\\ \beta_p\end{array}\right]\) and \(\mathbf{\epsilon}=\left[\begin{array}{c} \epsilon_1 \\ \vdots \\ \epsilon_n\end{array}\right]\)

3.2 Least Squares (LS)

  • Minimize the residual sum of squares \[\begin{eqnarray*} RSS(\boldsymbol{\beta})&=&\sum\limits_{i=1}^n e^2_i\\ &=&\sum\limits_{i=1}^n \left(y_i-\beta_0-\sum\limits_{j=1}^p x_{ij}\beta_j\right)^2 \end{eqnarray*}\]

  • or in matrix notation

\[ \text{RSS}(\boldsymbol{\beta})=(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X\beta}) \]

\[\rightarrow \hat{\boldsymbol{\beta}}=\text{argmin}_\beta \text{ RSS}(\boldsymbol{\beta})\]


3.2.1 Minimize RSS

\[ \begin{array}{ccc} \frac{\partial RSS}{\partial \boldsymbol{\beta}}&=&\mathbf{0}\\\\ \frac{(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}&=&\mathbf{0}\\\\ -2\mathbf{X}^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta})&=&\mathbf{0}\\\\ \mathbf{X}^T\mathbf{X\beta}&=&\mathbf{X}^T\mathbf{Y}\\\\ \hat{\boldsymbol{\beta}}&=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y} \end{array} \]


3.3 Variance Estimator?

\[ \begin{array}{ccl} \hat{\boldsymbol{\Sigma}}_{\hat{\boldsymbol{\beta}}} &=&\text{var}\left[(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y}\right]\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\text{var}\left[\mathbf{Y}\right]\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T(\mathbf{I}\sigma^2)\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1} \\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{I}\quad\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\\\\ %\hat{\boldmath{\Sigma}}_{\hat{\boldsymbol{\beta}}}&=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T \text{var}\left[\mathbf{Y}\right](\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2 \end{array} \]

The uncertainty on the model parameters thus depends on the residual variability and the design!

  • The larger \(\mathbf{X}^T\mathbf{X}\) the more information the experiment contains on the model parameters and the smaller their variances and standard errors will be!
  • Factorial designs?
  • Designs with continuous predictors?

The effect size of interest is typically a linear combinations of the model parameters, i.e. \[ l_0 \times \beta_0 + l_1 \times \beta_1 + ... + l_{p-1} \times \beta_{p-1} = \mathbf{L}^T\boldsymbol{\beta} \]

The null hypothesis of our test statistics is than formulated as

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

vs the alternative hypothesis

\[ H_0: \mathbf{L}^T\boldsymbol{\beta} \neq 0 \] which can be assessed using a t-test statistic:

\[ t=\frac{\mathbf{L}^T\hat{\boldsymbol{\beta}} - 0}{\text{se}_{\mathbf{L}^T\hat{\boldsymbol{\beta}}}} \] which follows a t-distribution with n-p degrees of freedom under the null hypothesis when all assumptions for the linear model are valid.

So the power

\[P(p < 0.05 | H_1)\]

will typically depends on

  • the real effect size in the population \(\mathbf{L}^T\boldsymbol{\beta}\).
  • Number of observations: SE and df of t-test.
  • Choice of the design points.
  • Choice of significance level.

Similar to the introductory example, we can use simulations to assess the power.

3.4 Mouse example

  • In 2021 Choa et al. published that the cytokine Thymic stromal lymphopoietin (TSLP) induced fat loss through sebum secretion (talg). [html] [PDF]

  • Suppose that you would like to set up a similar study to test if cytokine interleukin 25 (IL) also has beneficial effect.

  • You plan to setup a study with a control group of high fat diet (HFD) fed mice and a treatment group that recieves the HFD and IL.

  • What sample size do you need to pick up the effect of the treatment.

3.4.1 How will we analyse the data of this experiment?


  • \(H_0\): The average weight difference is equal to zero

  • \(H_1\): The average weight difference is different from zero

  • Two sample t-test or a t-test on the slope of a linear model with one dummy variable.

\[ Y_i = \beta_0 + \beta_1 X_\text{iIL} + \epsilon_i\]

\[ \text{with } X_\text{iIL}=\begin{cases}X_{iIL}=0 & \text{HFD}\\X_{iIL}=1 & \text{HFD + IL}\end{cases}. \]

  • Estimated effect size \[\hat\delta = \bar X_{IL} - \bar X_{c} = \hat \beta_1\]

  • Test statistic \[ T = \frac{\bar{X}_{IL}-\bar{X}_{c}}{SD_\text{pooled} \times \sqrt{\frac{1}{n_1} + \frac{1}{n_2}}} = \frac{\hat\beta_1}{\text{SE}_{\hat\beta_1}} \]

  • \(\hat \beta_1\) is an unbiased estimator of the real weight difference (\(\delta\)) that would occur in the population of rats fed with HFD and rats fed with HFD + IL.

3.4.2 Power?

\[ P[p < \alpha \vert \beta_1 \neq 0] \]

  • Hence, the power will depend on the real weight difference between the group means \(\beta_1\) in the population.
  • The variability of the weight measurements
  • The significance level \(\alpha\)
  • Sample size \(n_{IL}\) and \(n_c\) in both groups

We can estimate the power if

  1. The assumptions of the model are met: weights are normally distributed with same variance

and we know

  1. the standard deviation of the weight measurements around their average mean for HFD-fed mice
  2. the real effect size in the population
  3. \(n_1\) and \(n_2\)

3.4.3 Use data from a previous experiment to get insight in mice data

Suppose that we have access to the data of a preliminary experiment (e.g. provided by Karen Svenson via Gary Churchill and Dan Gatti and partially funded by P50 GM070683 on PH525x)

mice <- read.csv("https://raw.githubusercontent.com/genomicsclass/dagdata/master/inst/extdata/femaleMiceWeights.csv")

mice %>%
  ggplot(aes(x = Diet, y = Bodyweight)) +
  geom_boxplot(outlier.shape = FALSE) +
  geom_jitter()

mice %>%
  ggplot(aes(sample = Bodyweight)) +
  geom_qq() +
  geom_qq_line() +
  facet_wrap(~Diet)

mice <- mice %>% mutate(Diet = as.factor(Diet))
miceSum <- mice %>%
  group_by(Diet) %>%
  summarize(
    mean = mean(Bodyweight, na.rm = TRUE),
    sd = sd(Bodyweight, na.rm = TRUE),
    n = n()
  ) %>%
  mutate(se = sd / sqrt(n))
miceSum

In the experiment we have data from two diets:

  • regular diet of cerial and grain based diet (Chow)
  • High Fat (hf)

We can use the hf mice as input for our power analysis.

  • The data of the hf mice seem to be normally distributed
  • The mean weight is 26.8g
  • The SD of the weight is 4.1g

Effect size?

  • The alternative hypothesis is complex.

  • It includes all possible effects!

  • In order to do the power analysis we will have to choose a minimum effect size that we would like to detect.

  • Suppose that we would like to detect if the weight of the rats difference \(\delta\) of at least 10%.

delta <- -round(miceSum$mean[2] * .1, 1)
delta
## [1] -2.7

Note, that the average weight then would get close to that of the rats in our pilot experiment that were fed on the chow diet.

We can set up a simulation study to assess the power of an experiment with 8 mice in each group:

3.4.3.1 One simulation

set.seed(1423)
n1 <- n2 <- 8
b0 <- round(miceSum$mean[2], 1)
b1 <- -delta
sd <- round(miceSum$sd[2], 1)
alpha <- 0.05

x <- rep(0:1, c(n1, n2))
y <- b0 + b1 * x + rnorm(n1 + n2, 0, sd = sd)
fit <- lm(y ~ x)
bhat <- fit$coef
stat <- summary(fit)
summary(fit)$coef[2, ]
##   Estimate Std. Error    t value   Pr(>|t|) 
##  2.5972480  1.7919658  1.4493848  0.1692595

For this simulated experiment we would not have been able to pick up the effect of the treatment at the significance level \(\alpha\)=0.05 (p = 0.17)!

  • We have to repeat the experiment many times to estimate the power.
  • We will therefore put our simulation in a general function.

3.4.3.2 Simulation of repeated experiments

library(multcomp)
## Loading required package: mvtnorm
## Loading required package: survival
## Loading required package: TH.data
## Loading required package: MASS
## 
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
## 
##     select
## 
## Attaching package: 'TH.data'
## The following object is masked from 'package:MASS':
## 
##     geyser
n1 <- n2 <- 8

b0 <- round(miceSum$mean[2], 1)
b1 <- -delta

sd <- round(miceSum$sd[2], 1)
predictorData <- data.frame(Diet = rep(c("c", "hf"), c(n1, n2)) %>% as.factor())

alpha <- 0.05

simLm <- function(form, data, betas, sd, contrasts, simIndex = NA) {
  dataSim <- data
  X <- model.matrix(form, dataSim)
  dataSim$ySim <- X %*% betas + rnorm(nrow(dataSim), 0, sd)
  form <- formula(paste("ySim ~", form[[2]]))
  fitSim <- lm(form, dataSim)
  mcp <- glht(fitSim, linfct = contrasts)
  return(summary(mcp)$test[c("coefficients", "sigma", "tstat", "pvalues")] %>% unlist())
}

simLm(
  form = ~Diet,
  data = predictorData,
  betas = c(b0, b1),
  sd = sd,
  contrasts = "Diethf = 0"
)
## coefficients.Diethf        sigma.Diethf        tstat.Diethf 
##           1.9444623           2.0931302           0.9289734 
##             pvalues 
##           0.3686439

We now have a generic function that can simulate data from a normal distribution for every design. The function has arguments:

  • form: One sided formula including the structure of the predictors in the model
  • data: Data frame with the predictor values for the design
  • betas: A vector with values for all mean model parameters
  • sd: The standard deviation of the error
  • contrasts: a scalar or a vector with the null hypotheses that we would like to assess.
  • simIndex: an arbitrary argument that is not used by the function but that will allow it to be used in an sapply loop that runs from 1 up to the number of simulations.

Simulate nSim = 1000 repeated experiments:

set.seed(1425)
nSim <- 1000

simResults <- t(sapply(1:nSim, simLm, form = ~Diet, data = predictorData, betas = c(b0, b1), sd = sd, contrasts = "Diethf = 0"))
power <- mean(simResults[, 4] < alpha)
power
## [1] 0.221

We have a power of 22.1% to pick up the treatment effect when using 8 bioreps in each treatment group.

3.4.3.3 Power for multiple sample sizes

We can now calculate the power for multiple sample sizes.

power <- data.frame(n = c(3, 5, 10, 25, 50, 75, 100), power = NA)

for (i in 1:nrow(power))
{
  n1 <- n2 <- power$n[i]
  predictorData <- data.frame(Diet = rep(c("c", "hf"), c(n1, n2)) %>% as.factor())
  simResults <- t(sapply(1:nSim, simLm, form = ~Diet, data = predictorData, betas = c(b0, b1), sd = sd, contrasts = "Diethf = 0"))
  power$power[i] <- mean(simResults[, "pvalues"] < alpha)
}
power
power %>%
  ggplot(aes(x = n, y = power)) +
  geom_line()

3.4.3.4 Assess power in function of sample size for different effect sizes

Note, that - the sign of the delta is arbitrary because we test two-sided. - the intercept is arbitrary because we only asses \(\beta_0\) - We therefore typically set \(\beta_0 = 0\)

nSim <- 1000
b0 <- 0
deltas <- c(1, 2, 3, 5, 10)
sd <- round(miceSum$sd[2], 1)
ns <- c(3, 5, 10, 20, 25, 50, 75, 100)

power <- data.frame(
  b1 = rep(deltas, each = length(ns)),
  n = rep(ns, length(deltas)),
  power = NA
)

for (i in 1:nrow(power))
{
  b1 <- power$b1[i]
  n1 <- n2 <- power$n[i]
  predictorData <- data.frame(Diet = rep(c("c", "hf"), c(n1, n2)) %>% as.factor())
  simResults <- t(sapply(1:nSim, simLm, form = ~Diet, data = predictorData, betas = c(b0, b1), sd = sd, contrasts = "Diethf = 0"))
  power$power[i] <- mean(simResults[, "pvalues"] < alpha)
}
power %>%
  ggplot(aes(x = n, y = power, col = b1 %>% as.factor())) +
  geom_line()

Note, that the power curves are still a bit choppy because we selected a limited number of sample sizes and because nSim simulations is not enough to get a good power estimate when the power is low.

4 Final remarks

  • The code can be easily extended towards other designs by altering

    • predictor data
    • formula
    • contrast
  • The simulations start to take long when you evaluate many scenario’s.

    • More efficient code using matrices
    • For a two group comparison closed form solutions exist

4.1 More efficient code based on matrix algebra

Code runs much faster. We now simulate 20 times more experiments in a much shorter time!

simFast <- function(form, data, betas, sd, contrasts, alpha = .05, nSim = 10000) {
  ySim <- rnorm(nrow(data) * nSim, sd = sd)
  dim(ySim) <- c(nrow(data), nSim)
  design <- model.matrix(form, data)
  ySim <- ySim + c(design %*% betas)
  ySim <- t(ySim)

  ### Fitting
  fitAll <- limma::lmFit(ySim, design)

  ### Inference
  varUnscaled <- c(t(contrasts) %*% fitAll$cov.coefficients %*% contrasts)
  contrasts <- fitAll$coefficients %*% contrasts
  seContrasts <- varUnscaled^.5 * fitAll$sigma
  tstats <- contrasts / seContrasts
  pvals <- pt(abs(tstats), fitAll$df.residual, lower.tail = FALSE) * 2
  return(mean(pvals < alpha))
}
nSim <- 20000
b0 <- 0
sd <- round(miceSum$sd[2], 1)
ns <- c(3, 5, 10, 20, 25, 50, 75, 100)
deltas <- c(1, 2, 3, 5, 10)

contrast <- limma::makeContrasts("Diethf", levels = c("(Intercept)", "Diethf"))
## Warning in limma::makeContrasts("Diethf", levels = c("(Intercept)",
## "Diethf")): Renaming (Intercept) to Intercept
powerFast <- matrix(NA, nrow = length(ns) * length(deltas), ncol = 3) %>% as.data.frame()
names(powerFast) <- c("b1", "n", "power")
form <- ~Diet

i <- 0

for (n in ns)
{
  n1 <- n2 <- n

  ### Simulation
  predictorData <- data.frame(Diet = rep(c("c", "hf"), c(n1, n2)) %>% as.factor())

  for (b1 in deltas)
  {
    i <- i + 1
    betas <- c(b0, b1)
    powerFast[i, ] <- c(b1, n, simFast(form, predictorData, betas, sd, contrasts = contrast, alpha = alpha, nSim = nSim))
  }
}
powerFast %>%
  ggplot(aes(x = n, y = power, col = b1 %>% as.factor())) +
  geom_line()

4.2 More efficient code based on closed form solution that exists for two group comparison

For the two sample t-test a closed form estimate exists for the power. In this context the Cohen’s effect size is typically used:

\(D = \frac{\delta}{SD}\)

power.t.test(n = 8, delta = delta, sd = sd, type = "two.sample")
## 
##      Two-sample t test power calculation 
## 
##               n = 8
##           delta = 2.7
##              sd = 4.1
##       sig.level = 0.05
##           power = 0.2320512
##     alternative = two.sided
## 
## NOTE: n is number in *each* group

Note, that this is very similar to the power that we calculated using the simulations!

b0 <- 0
sd <- round(miceSum$sd[2], 1)
ns <- c(3, 5, 10, 20, 25, 50, 75, 100)
deltas <- c(1, 2, 3, 5, 10)


powerTheo <- data.frame(
  deltas = rep(deltas, each = length(ns)),
  n = rep(ns, length(deltas)),
  power = NA
)

powerTheo$power <- apply(powerTheo[, 1:2], 1, function(x) power.t.test(delta = x[1], n = x[2], sd = sd, type = "two.sample")$power)
powerTheo %>%
  ggplot(aes(x = n, y = power, col = deltas %>% as.factor())) +
  geom_line()

LS0tCnRpdGxlOiAiOC40IEV4cGVyaW1lbnRhbCBEZXNpZ24gSUlJOiBSZXBsaWNhdGlvbiBhbmQgUG93ZXIiCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgotLS0KCgo8YSByZWw9ImxpY2Vuc2UiIGhyZWY9Imh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1zYS80LjAiPjxpbWcgYWx0PSJDcmVhdGl2ZSBDb21tb25zIExpY2Vuc2UiIHN0eWxlPSJib3JkZXItd2lkdGg6MCIgc3JjPSJodHRwczovL2kuY3JlYXRpdmVjb21tb25zLm9yZy9sL2J5LW5jLXNhLzQuMC84OHgzMS5wbmciIC8+PC9hPgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMgQ29uY2VwdHMKCi0gRXhwZXJpbWVudGFsIHVuaXRzIHJlcHJlc2VudGF0aXZlIGZvciBwb3B1bGF0aW9uOiBSYW5kb21pc2F0aW9uCi0gUmVwbGljYXRpb246IHRlY2huaWNhbCB2cyBiaW9sb2dpY2FsLCBzYW1wbGUgc2l6ZSAtIHBvd2VyCi0gU291cmNlcyBvZiB2YXJpYXRpb246IHRlY2huaWNhbCwgYmlvbG9naWNhbCwgd2l0aGluIHN1YmplY3QsIGJldHdlZW4gc3ViamVjdAoKIyBSZXBsaWNhdGlvbgoKVHdvIHBhZ2VyIG9uIFJlcGxpY2F0aW9uIGluIG5hdHVyZSBtZXRob2RzOiBbW1BERl0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9ubWV0aC4zMDkxLnBkZildCiFbXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25tZXRoLjMwOTEucGRmKXsgd2lkdGg9MTAwJSB9CgojIyBFeGFtcGxlCgpDb25zaWRlciBhIHNpbmdsZSBjZWxsIFJOQS1zZXEgZXhwZXJpbWVudCB3aGVyZSByZXNlYXJjaGVycyB3YW50IHRvIGFzc2VzcyB0aGUgZWZmZWN0IG9mIGRydWcgdHJlYXRtZW50IG9uIGdlbmUgZXhwcmVzc2lvbgoKUG90ZW50aWFsIHJlc2VhcmNoIHF1ZXN0aW9ucwoKLSBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgaW4gYXZlcmFnZSBnZW5lIGV4cHJlc3Npb24gYmV0d2VlbiB0cmVhdGVkIGFuZCBub24gdHJlYXRlZCBzYW1wbGVzCi0gSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGluIHZhcmlhYmlsaXR5IG9mIGdlbmUgZXhwcmVzc2lvbiBiZXR3ZWVuIHRyZWF0ZWQgYW5kIG5vbiB0cmVhdGVkIHNhbXBsZXMKCiMjIFNvdXJjZXMgb2YgdmFyaWF0aW9uCgpUQUJMRSAxIE5BVFVSRSBNRVRIT0RTIHwgVk9MLjExIE5PLjkgfCBTRVBURU1CRVIgMjAxNCB8IDg3OSAtIDg4MAoKfCAgIHwgUmVwbGljYXRlIHR5cGUgfCBSZXBsaWNhdGUgY2F0ZWdvcnkkXlx0ZXh0e2F9JCB8Cnw6LS0tOnw6LS0tfDotLS06fAp8IHwgQ29sb25pZXMgfAlCIHwKfCB8IFN0cmFpbnMgfAlCIHwKfCBBbmltYWwgc3R1ZHkgc3ViamVjdHMgfCBDb2hvdXNlZCBncm91cHMgfAlCIHwKfCB8IEdlbmRlciB8IEIgfAp8IHwgSW5kaXZpZHVhbHMJfCBCIHwKfCB8IHwgfAp8IHwgT3JnYW5zIGZyb20gc2FjcmlmaWNlZCBhbmltYWxzIHwgQiB8CnwgfCBNZXRob2RzIGZvciBkaXNzb2NpYXRpbmcgY2VsbHMgZnJvbSB0aXNzdWUgfCBUIHwKfCBTYW1wbGUgcHJlcGFyYXRpb24gfCBEaXNzb2NpYXRpb24gcnVucyBmcm9tIGdpdmVuIHRpc3N1ZSBzYW1wbGUgfCBUIHwKfCB8IEluZGl2aWR1YWwgY2VsbHMgfCBCIHwKfCB8IHwgfAp8IHwgUk5BLXNlcSBsaWJyYXJ5IGNvbnN0cnVjdGlvbiB8IFQgfAp8IHwgUnVucyBmcm9tIHRoZSBsaWJyYXJ5IG9mIGEgZ2l2ZW4gY2VsbCB8CVQgfAp8IFNlcXVlbmNpbmcgfCBSZWFkcyBmcm9tIGRpZmZlcmVudCB0cmFuc2NyaXB0IG1vbGVjdWxlcyB8IFYkXlx0ZXh0e2J9JCB8CnwgfCBSZWFkcyB3aXRoIHVuaXF1ZSBtb2xlY3VsYXIgaWRlbnRpZmllciAoVU1JKSBmcm9tIGEgZ2l2ZW4gdHJhbnNjcmlwdCBtb2xlY3VsZSB8CVQgfAoKKGEpIFJlcGxpY2F0ZXMgYXJlIGNhdGVnb3JpemVkIGFzIGJpb2xvZ2ljYWwgKEIpLCB0ZWNobmljYWwgKFQpIG9yIG9mIHZhcmlhYmxlIHR5cGUgKFYpLgooYikgU2VxdWVuY2UgcmVhZHMgc2VydmUgZGl2ZXJzZSBwdXJwb3NlcyBkZXBlbmRpbmcgb24gdGhlIGFwcGxpY2F0aW9uIGFuZCBob3cgcmVhZHMgYXJlIHVzZWQgaW4gYW5hbHlzaXMuCgotLS0KCiMjIEF0IHdoaWNoIGxldmVsIHNob3VsZCB3ZSByZXBsaWNhdGU/CgotICRcdGV4dHt2YXJ9KFgpPVxzaWdtYV4yX0ErXHNpZ21hXjJfQytcc2lnbWFeMl9NPVxzaWdtYV4yX3tUT1R9JAotICRcdGV4dHt2YXJ9KFxiYXJ7WH0pPVxmcmFje1xzaWdtYV4yX0F9e25fQX0rXGZyYWN7XHNpZ21hXjJfQ317bl9BIG5fQ30gKyBcZnJhY3tcc2lnbWFeMl9NfXtuX0Egbl9DIG5fTX0kCgohW10oaHR0cHM6Ly9tZWRpYS5zcHJpbmdlcm5hdHVyZS5jb20vZnVsbC9zcHJpbmdlci1zdGF0aWMvaW1hZ2UvYXJ0JTNBMTAuMTAzOCUyRm5tZXRoLjMwOTEvTWVkaWFPYmplY3RzLzQxNTkyXzIwMTRfQXJ0aWNsZV9CRm5tZXRoMzA5MV9GaWcxX0hUTUwuanBnKQpGaWd1cmUgMSBOQVRVUkUgTUVUSE9EUyB8IFZPTC4xMSBOTy45IHwgU0VQVEVNQkVSIDIwMTQgfCA4NzkgLSA4ODAKCihhKSBUaHJlZSBsZXZlbHMgb2YgcmVwbGljYXRpb24gKHR3byBiaW9sb2dpY2FsLCBvbmUgdGVjaG5pY2FsKSB3aXRoIGFuaW1hbCwgY2VsbCBhbmQgbWVhc3VyZW1lbnQgcmVwbGljYXRlcyBub3JtYWxseSBkaXN0cmlidXRlZCB3aXRoIGEgbWVhbiBhY3Jvc3MgYW5pbWFscyBvZiAxMCBhbmQgcmF0aW8gb2YgdmFyaWFuY2VzIDE6MjowLjUuIFNvbGlkIGdyZWVuIChiaW9sb2dpY2FsKSBhbmQgYmx1ZSAodGVjaG5pY2FsKSBkb3RzIHNob3cgaG93IGEgbWVhc3VyZW1lbnQgb2YgdGhlIGV4cHJlc3Npb24gKFggPSAxMikgc2FtcGxlcyBmcm9tIGFsbCB0aHJlZSBzb3VyY2VzIG9mIHZhcmlhdGlvbi4gRGlzdHJpYnV0aW9uIHMuZC4gaXMgc2hvd24gYXMgaG9yaXpvbnRhbCBsaW5lcy4KKGIpIEV4cHJlc3Npb24gdmFyaWFuY2UsIFZhcihYKSwgYW5kIHZhcmlhbmNlIG9mIGV4cHJlc3Npb24gbWVhbiwgVmFyKCRcYmFyIFgkKSwgY29tcHV0ZWQgYWNyb3NzIDEwLDAwMCBzaW11bGF0aW9ucyBvZiBuQW5Dbk0gPSA0OCBtZWFzdXJlbWVudHMgZm9yIHVuaXF1ZSBjb21iaW5hdGlvbnMgb2YgdGhlIG51bWJlciBvZiBhbmltYWxzIChuQSA9IDEgdG8gNDgpLCBjZWxscyBwZXIgYW5pbWFsIChuQyA9IDEgdG8gNDgpIGFuZCB0ZWNobmljYWwgcmVwbGljYXRlIG1lYXN1cmVtZW50cyBwZXIgY2VsbCAobk0gPSAxIGFuZCAzKS4gVGhlIHJhdGlvIG9mIFZhcihYKSBhbmQgVmFyKCRcYmFyIFgkKSBpcyB0aGUgZWZmZWN0aXZlIHNhbXBsZSBzaXplLCBuLCB3aGljaCBjb3JyZXNwb25kcyB0byB0aGUgZXF1aXZhbGVudCBudW1iZXIgb2Ygc3RhdGlzdGljYWxseSBpbmRlcGVuZGVudCBtZWFzdXJlbWVudHMuIEhvcml6b250YWwgZGFzaGVkIGxpbmVzIGNvcnJlc3BvbmQgdG8gYmlvbG9naWNhbCBhbmQgdG90YWwgdmFyaWF0aW9uLiBFcnJvciBiYXJzIG9uIFZhcihYKSBzaG93IHMuZC4gZnJvbSB0aGUgMTAsMDAwIHNpbXVsYXRlZCBzYW1wbGVzIChuTSA9IDEpLgoKLS0tCgotIENvc3QgYmVuZWZpdCB0cmFkZS1vZmY6IGNvc3QgZGlmZmVyZW5jZSBiZXR3ZWVuIGJpb2xvZ2ljYWwgYW5kIHRlY2huaWNhbCByZXBsaWNhdGVzCi0gVHlwaWNhbGx5LCBiaW9sb2dpY2FsIHZhcmlhYmlsaXR5IGlzIHN1YnN0YW50aWFsbHkgbGFyZ2VyIHRoYW4gdGVjaG5pY2FsIHZhcmlhYmlsaXR5CgokXHJpZ2h0YXJyb3ckIGNvbW1pdCByZXNvdXJjZXMgdG8gc2FtcGxpbmcgYmlvbG9naWNhbGx5IHJlbGV2YW50IHZhcmlhYmxlcwoKJFxyaWdodGFycm93JCB1bmxlc3MgbWVhc3VyZXMgb2YgdGVjaG5pY2FsIHZhcmlhYmlsaXR5IGFyZSBvZiBpbnRlcmVzdCB0aGVuIGluY3JlYXNpbmcgbnVtYmVyIG9mIHRlY2huaWNhbCBtZWFzdXJlbWVudHMgaXMgdmFsdWFibGUuCgotIEdvb2QgZXhwZXJpbWVudGFsIGRlc2lnbiBwcmFjdGljZSBpbmNsdWRlcyBwbGFubmluZyBmb3IgcmVwbGljYXRpb24uCgogIDEuIElkZW50aWZ5IHRoZSByZXNlYXJjaCBxdWVzdGlvbnMgdG8gYmUgYW5zd2VyZWQgd2l0aCBleHBlcmltZW50cy4KICAyLiBEZXRlcm1pbmUgcHJvcG9ydGlvbiBvZiB2YXJpYWJpbGl0eSBpbmR1Y2VkIGJ5IGVhY2ggc3RlcC4KICAzLiBCZSBhd2FyZSBmb3IgcHNldWRvcmVwbGljYXRpb24gYW5kIGFpbSBmb3Igc3RhdGlzdGljYWxseSBpbmRlcGVuZGVudCByZXBsaWNhdGVzLgoKLS0tCgojIFBvd2VyLCBzYW1wbGUgc2l6ZSBhbmQgb3RoZXIgZGVzaWduIGFzcGVjdHMKClJlYWRpbmcgbWF0ZXJpYWxzOgpbTmF0dXJlIE1ldGhvZHMgKDIwMTMpLCAxMCgxMiksIDExMznigJMxMTQwXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25tZXRoLjI3MzgucGRmKQoKIyMgSW50ZXJtZXp6byBsaW5lYXIgcmVncmVzc2lvbiBpbiBtYXRyaXggZm9ybQoKLSBMaW5lYXIgcmVncmVzc2lvbiBpcyBhIHZlcnkgaW1wb3J0YW50IHN0YXRpc3RpY2FsIHRvb2wgdG8gc3R1ZHkgdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gdmFyaWFibGVzIGFuZCB0byBidWlsZCBwcmVkaWN0aW9uIG1vZGVscy4KCiMjIyBTY2FsYXIgZm9ybQoKLSBDb25zaWRlciBhIHZlY3RvciBvZiBwcmVkaWN0b3JzICRcbWF0aGJme3h9PSh4XzEsXGxkb3RzLHhfcCkkIGFuZAotIGEgcmVhbC12YWx1ZWQgcmVzcG9uc2UgJFkkCi0gdGhlbiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXMKXFsKWT1mKFxtYXRoYmZ7eH0pICtcZXBzaWxvbj1cYmV0YV8wK1xzdW1cbGltaXRzX3tqPTF9XnAgeF9qXGJldGFfaiArIFxlcHNpbG9uClxdCndpdGggaS5pLmQuICRcZXBzaWxvblxzaW0gTigwLFxzaWdtYV4yKSQKCgoKIyMjIFZlY3Rvci9NYXRyaXggZm9ybQoKLSAkbiQgb2JzZXJ2YXRpb25zICQoXG1hdGhiZnt4fV8xLHlfMSkgXGxkb3RzIChcbWF0aGJme3h9X24seV9uKSQgd2l0aCAkXG1hdGhiZnt4fV8xXlQ9WzEgeF8xIFxsZG90cyB4X3BdJAotIFJlZ3Jlc3Npb24gaW4gbWF0cml4IG5vdGF0aW9uClxbXG1hdGhiZntZfT1cbWF0aGJme1hcYmV0YX0gKyBcbWF0aGJme1xlcHNpbG9ufVxdCndpdGggJFxtYXRoYmZ7WX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfXlfMVxcIFx2ZG90c1xceV9uXGVuZHthcnJheX1ccmlnaHRdJCwKJFxtYXRoYmZ7WH09XGxlZnRbXGJlZ2lue2FycmF5fXtjY2NjfSAxJnhfezExfSZcbGRvdHMmeF97MXB9XFwKXHZkb3RzJlx2ZG90cyYmXHZkb3RzXFwKMSZ4X3tuMX0mXGxkb3RzJnhfe25wfQpcZW5ke2FycmF5fVxyaWdodF0kIG9yICRcbWF0aGJme1h9PVxsZWZ0W1xiZWdpbnthcnJheX17Y30gXG1hdGhiZnt4fV8xXlRcXFx2ZG90c1xcXG1hdGhiZnt4fV9uXlRcZW5ke2FycmF5fVxyaWdodF0kLAokXGJvbGRzeW1ib2x7XGJldGF9PVxsZWZ0W1xiZWdpbnthcnJheX17Y31cYmV0YV8wXFwgXHZkb3RzXFwgXGJldGFfcFxlbmR7YXJyYXl9XHJpZ2h0XSQgYW5kCiRcbWF0aGJme1xlcHNpbG9ufT1cbGVmdFtcYmVnaW57YXJyYXl9e2N9IFxlcHNpbG9uXzEgXFwgXHZkb3RzIFxcIFxlcHNpbG9uX25cZW5ke2FycmF5fVxyaWdodF0kCgotLS0KCiMjIExlYXN0IFNxdWFyZXMgKExTKQoKLSBNaW5pbWl6ZSB0aGUgcmVzaWR1YWwgc3VtIG9mIHNxdWFyZXMKXGJlZ2lue2VxbmFycmF5Kn0KUlNTKFxib2xkc3ltYm9se1xiZXRhfSkmPSZcc3VtXGxpbWl0c197aT0xfV5uIGVeMl9pXFwKJj0mXHN1bVxsaW1pdHNfe2k9MX1ebiBcbGVmdCh5X2ktXGJldGFfMC1cc3VtXGxpbWl0c197aj0xfV5wIHhfe2lqfVxiZXRhX2pccmlnaHQpXjIKXGVuZHtlcW5hcnJheSp9CgotIG9yIGluIG1hdHJpeCBub3RhdGlvbgoKJCQKXHRleHR7UlNTfShcYm9sZHN5bWJvbHtcYmV0YX0pPShcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSleVChcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSkKJCQKCiQkXHJpZ2h0YXJyb3cgXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19PVx0ZXh0e2FyZ21pbn1fXGJldGEgXHRleHR7IFJTU30oXGJvbGRzeW1ib2x7XGJldGF9KSQkCgotLS0KCiMjIyBNaW5pbWl6ZSBSU1MKXFsKXGJlZ2lue2FycmF5fXtjY2N9ClxmcmFje1xwYXJ0aWFsIFJTU317XHBhcnRpYWwgXGJvbGRzeW1ib2x7XGJldGF9fSY9JlxtYXRoYmZ7MH1cXFxcClxmcmFjeyhcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSleVChcbWF0aGJme1l9LVxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0pfXtccGFydGlhbCBcYm9sZHN5bWJvbHtcYmV0YX19Jj0mXG1hdGhiZnswfVxcXFwKLTJcbWF0aGJme1h9XlQoXG1hdGhiZntZfS1cbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9KSY9JlxtYXRoYmZ7MH1cXFxcClxtYXRoYmZ7WH1eVFxtYXRoYmZ7WFxiZXRhfSY9JlxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1cXFxcClxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntZfQpcZW5ke2FycmF5fQpcXQoKLS0tCgojIyBWYXJpYW5jZSBFc3RpbWF0b3I/ClxbClxiZWdpbnthcnJheX17Y2NsfQpcaGF0e1xib2xkc3ltYm9se1xTaWdtYX19X3tcaGF0e1xib2xkc3ltYm9se1xiZXRhfX19CiY9Jlx0ZXh0e3Zhcn1cbGVmdFsoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1ccmlnaHRdXFxcXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XVxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxcXFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlQoXG1hdGhiZntJfVxzaWdtYV4yKVxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfQpcXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntJfVxxdWFkXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjJcXFxcCiVcaGF0e1xib2xkbWF0aHtcU2lnbWF9fV97XGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19fSY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UIFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfVxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjJcXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjIKXGVuZHthcnJheX0KXF0KClRoZSB1bmNlcnRhaW50eSBvbiB0aGUgbW9kZWwgcGFyYW1ldGVycyB0aHVzIGRlcGVuZHMgb24gdGhlIHJlc2lkdWFsIHZhcmlhYmlsaXR5IGFuZCB0aGUgZGVzaWduIQoKLSBUaGUgbGFyZ2VyICRcbWF0aGJme1h9XlRcbWF0aGJme1h9JCB0aGUgbW9yZSBpbmZvcm1hdGlvbiB0aGUgZXhwZXJpbWVudCBjb250YWlucyBvbiB0aGUgbW9kZWwgcGFyYW1ldGVycyBhbmQgdGhlIHNtYWxsZXIgdGhlaXIgdmFyaWFuY2VzIGFuZCBzdGFuZGFyZCBlcnJvcnMgd2lsbCBiZSEKLSBGYWN0b3JpYWwgZGVzaWducz8KLSBEZXNpZ25zIHdpdGggY29udGludW91cyBwcmVkaWN0b3JzPwoKLS0tCgpUaGUgZWZmZWN0IHNpemUgb2YgaW50ZXJlc3QgaXMgdHlwaWNhbGx5IGEgbGluZWFyIGNvbWJpbmF0aW9ucyBvZiB0aGUgbW9kZWwgcGFyYW1ldGVycywgaS5lLgokJApsXzAgXHRpbWVzIFxiZXRhXzAgKyBsXzEgXHRpbWVzIFxiZXRhXzEgKyAuLi4gKyBsX3twLTF9IFx0aW1lcyBcYmV0YV97cC0xfSA9IFxtYXRoYmZ7TH1eVFxib2xkc3ltYm9se1xiZXRhfQokJAoKVGhlIG51bGwgaHlwb3RoZXNpcyBvZiBvdXIgdGVzdCBzdGF0aXN0aWNzIGlzIHRoYW4gZm9ybXVsYXRlZCBhcwoKCiQkCkhfMDogXG1hdGhiZntMfV5UXGJvbGRzeW1ib2x7XGJldGF9ID0gMAokJAoKdnMgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMKCiQkCkhfMDogXG1hdGhiZntMfV5UXGJvbGRzeW1ib2x7XGJldGF9IFxuZXEgMAokJAp3aGljaCBjYW4gYmUgYXNzZXNzZWQgdXNpbmcgYSB0LXRlc3Qgc3RhdGlzdGljOgoKJCQKdD1cZnJhY3tcbWF0aGJme0x9XlRcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gLSAwfXtcdGV4dHtzZX1fe1xtYXRoYmZ7TH1eVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fX19CiQkCndoaWNoIGZvbGxvd3MgYSB0LWRpc3RyaWJ1dGlvbiB3aXRoIG4tcCBkZWdyZWVzIG9mIGZyZWVkb20gdW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcyB3aGVuIGFsbCBhc3N1bXB0aW9ucyBmb3IgdGhlIGxpbmVhciBtb2RlbCBhcmUgdmFsaWQuCgpTbyB0aGUgcG93ZXIKCiQkUChwIDwgMC4wNSB8IEhfMSkkJAoKd2lsbCB0eXBpY2FsbHkgZGVwZW5kcyBvbgoKLSB0aGUgcmVhbCBlZmZlY3Qgc2l6ZSBpbiB0aGUgcG9wdWxhdGlvbiAgJFxtYXRoYmZ7TH1eVFxib2xkc3ltYm9se1xiZXRhfSQuCi0gTnVtYmVyIG9mIG9ic2VydmF0aW9uczogU0UgYW5kIGRmIG9mIHQtdGVzdC4KLSBDaG9pY2Ugb2YgdGhlIGRlc2lnbiBwb2ludHMuCi0gQ2hvaWNlIG9mIHNpZ25pZmljYW5jZSBsZXZlbC4KClNpbWlsYXIgdG8gdGhlIGludHJvZHVjdG9yeSBleGFtcGxlLCB3ZSBjYW4gdXNlIHNpbXVsYXRpb25zIHRvIGFzc2VzcyB0aGUgcG93ZXIuCgojIyBNb3VzZSBleGFtcGxlCgotIEluIDIwMjEgQ2hvYSBldCBhbC4gcHVibGlzaGVkIHRoYXQgdGhlIGN5dG9raW5lIFRoeW1pYyBzdHJvbWFsIGx5bXBob3BvaWV0aW4KKFRTTFApIGluZHVjZWQgZmF0IGxvc3MgdGhyb3VnaCBzZWJ1bSBzZWNyZXRpb24gKHRhbGcpLiBbW2h0bWxdKGh0dHBzOi8vd3d3LnNjaWVuY2Uub3JnL2RvaS9mdWxsLzEwLjExMjYvc2NpZW5jZS5hYmQyODkzKV0gW1tQREZdKGh0dHBzOi8vd3d3LnNjaWVuY2Uub3JnL2RvaS9wZGYvMTAuMTEyNi9zY2llbmNlLmFiZDI4OTMpXQoKLSBTdXBwb3NlIHRoYXQgeW91IHdvdWxkIGxpa2UgdG8gc2V0IHVwIGEgc2ltaWxhciBzdHVkeSB0byB0ZXN0IGlmIGN5dG9raW5lIGludGVybGV1a2luIDI1IChJTCkgYWxzbyBoYXMgYmVuZWZpY2lhbCBlZmZlY3QuCgotIFlvdSBwbGFuIHRvIHNldHVwIGEgc3R1ZHkgd2l0aCBhIGNvbnRyb2wgZ3JvdXAgb2YgaGlnaCBmYXQgZGlldCAoSEZEKSBmZWQgbWljZSBhbmQgYSB0cmVhdG1lbnQgZ3JvdXAgdGhhdCByZWNpZXZlcyB0aGUgSEZEIGFuZCBJTC4KCi0gV2hhdCBzYW1wbGUgc2l6ZSBkbyB5b3UgbmVlZCB0byBwaWNrIHVwIHRoZSBlZmZlY3Qgb2YgdGhlIHRyZWF0bWVudC4KCgojIyMgSG93IHdpbGwgd2UgYW5hbHlzZSB0aGUgZGF0YSBvZiB0aGlzIGV4cGVyaW1lbnQ/CgoKLS0tCgoKCi0gJEhfMCQ6IFRoZSBhdmVyYWdlIHdlaWdodCBkaWZmZXJlbmNlIGlzIGVxdWFsIHRvIHplcm8KLSAkSF8xJDogVGhlIGF2ZXJhZ2Ugd2VpZ2h0IGRpZmZlcmVuY2UgaXMgZGlmZmVyZW50IGZyb20gemVybwoKLSBUd28gc2FtcGxlIHQtdGVzdCBvciBhIHQtdGVzdCBvbiB0aGUgc2xvcGUgb2YgYSBsaW5lYXIgbW9kZWwgd2l0aCBvbmUgZHVtbXkgdmFyaWFibGUuCgokJCBZX2kgPSBcYmV0YV8wICsgXGJldGFfMSBYX1x0ZXh0e2lJTH0gKyBcZXBzaWxvbl9pJCQKCiQkClx0ZXh0e3dpdGggfQpYX1x0ZXh0e2lJTH09XGJlZ2lue2Nhc2VzfVhfe2lJTH09MCAmIFx0ZXh0e0hGRH1cXFhfe2lJTH09MSAmIFx0ZXh0e0hGRCArIElMfVxlbmR7Y2FzZXN9LgokJAoKLSBFc3RpbWF0ZWQgZWZmZWN0IHNpemUKJCRcaGF0XGRlbHRhID0gXGJhciBYX3tJTH0gLSBcYmFyIFhfe2N9ID0gXGhhdCBcYmV0YV8xJCQKCi0gVGVzdCBzdGF0aXN0aWMKJCQKVCA9IFxmcmFje1xiYXJ7WH1fe0lMfS1cYmFye1h9X3tjfX17U0RfXHRleHR7cG9vbGVkfSBcdGltZXMgXHNxcnR7XGZyYWN7MX17bl8xfSArIFxmcmFjezF9e25fMn19fSA9IFxmcmFje1xoYXRcYmV0YV8xfXtcdGV4dHtTRX1fe1xoYXRcYmV0YV8xfX0KJCQKLSAkXGhhdCBcYmV0YV8xJCBpcyBhbiB1bmJpYXNlZCBlc3RpbWF0b3Igb2YgdGhlIHJlYWwgd2VpZ2h0IGRpZmZlcmVuY2UgKCRcZGVsdGEkKSB0aGF0IHdvdWxkIG9jY3VyIGluIHRoZSBwb3B1bGF0aW9uIG9mIHJhdHMgZmVkIHdpdGggSEZEIGFuZCByYXRzIGZlZCB3aXRoIEhGRCArIElMLgoKIyMjIFBvd2VyPwoKJCQKUFtwIDwgXGFscGhhIFx2ZXJ0IFxiZXRhXzEgXG5lcSAwXQokJAoKCi0gSGVuY2UsIHRoZSBwb3dlciB3aWxsIGRlcGVuZCBvbiB0aGUgcmVhbCB3ZWlnaHQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBncm91cCBtZWFucyAkXGJldGFfMSQgaW4gdGhlIHBvcHVsYXRpb24uCi0gVGhlIHZhcmlhYmlsaXR5IG9mIHRoZSB3ZWlnaHQgbWVhc3VyZW1lbnRzCi0gVGhlIHNpZ25pZmljYW5jZSBsZXZlbCAkXGFscGhhJAotIFNhbXBsZSBzaXplICRuX3tJTH0kIGFuZCAkbl9jJCBpbiBib3RoIGdyb3VwcwoKCldlIGNhbiBlc3RpbWF0ZSB0aGUgcG93ZXIgaWYKCjEuIFRoZSBhc3N1bXB0aW9ucyBvZiB0aGUgbW9kZWwgYXJlIG1ldDogd2VpZ2h0cyBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aCBzYW1lIHZhcmlhbmNlCgphbmQgd2Uga25vdwoKMi4gdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgd2VpZ2h0IG1lYXN1cmVtZW50cyBhcm91bmQgdGhlaXIgYXZlcmFnZSBtZWFuIGZvciBIRkQtZmVkIG1pY2UKMy4gdGhlIHJlYWwgZWZmZWN0IHNpemUgaW4gdGhlIHBvcHVsYXRpb24KNC4gJG5fMSQgYW5kICRuXzIkCgojIyMgVXNlIGRhdGEgZnJvbSBhIHByZXZpb3VzIGV4cGVyaW1lbnQgdG8gZ2V0IGluc2lnaHQgaW4gbWljZSBkYXRhCgpTdXBwb3NlIHRoYXQgd2UgaGF2ZSBhY2Nlc3MgdG8gdGhlIGRhdGEgb2YgYSBwcmVsaW1pbmFyeSBleHBlcmltZW50IChlLmcuIHByb3ZpZGVkIGJ5IEthcmVuIFN2ZW5zb24gdmlhIEdhcnkgQ2h1cmNoaWxsIGFuZCBEYW4gR2F0dGkgYW5kIHBhcnRpYWxseSBmdW5kZWQgYnkgUDUwIEdNMDcwNjgzIG9uIFtQSDUyNXhdKGh0dHA6Ly9nZW5vbWljc2NsYXNzLmdpdGh1Yi5pby9ib29rL3BhZ2VzL3JhbmRvbV92YXJpYWJsZXMuaHRtbCkpCgpgYGB7cn0KbWljZSA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dlbm9taWNzY2xhc3MvZGFnZGF0YS9tYXN0ZXIvaW5zdC9leHRkYXRhL2ZlbWFsZU1pY2VXZWlnaHRzLmNzdiIpCgptaWNlICU+JQogIGdncGxvdChhZXMoeCA9IERpZXQsIHkgPSBCb2R5d2VpZ2h0KSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gRkFMU0UpICsKICBnZW9tX2ppdHRlcigpCgptaWNlICU+JQogIGdncGxvdChhZXMoc2FtcGxlID0gQm9keXdlaWdodCkpICsKICBnZW9tX3FxKCkgKwogIGdlb21fcXFfbGluZSgpICsKICBmYWNldF93cmFwKH5EaWV0KQpgYGAKCmBgYHtyfQptaWNlIDwtIG1pY2UgJT4lIG11dGF0ZShEaWV0ID0gYXMuZmFjdG9yKERpZXQpKQptaWNlU3VtIDwtIG1pY2UgJT4lCiAgZ3JvdXBfYnkoRGlldCkgJT4lCiAgc3VtbWFyaXplKAogICAgbWVhbiA9IG1lYW4oQm9keXdlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgIHNkID0gc2QoQm9keXdlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgIG4gPSBuKCkKICApICU+JQogIG11dGF0ZShzZSA9IHNkIC8gc3FydChuKSkKbWljZVN1bQpgYGAKSW4gdGhlIGV4cGVyaW1lbnQgd2UgaGF2ZSBkYXRhIGZyb20gdHdvIGRpZXRzOgoKLSByZWd1bGFyIGRpZXQgb2YgY2VyaWFsIGFuZCBncmFpbiBiYXNlZCBkaWV0IChDaG93KQotIEhpZ2ggRmF0IChoZikKCldlIGNhbiB1c2UgdGhlIGhmIG1pY2UgYXMgaW5wdXQgZm9yIG91ciBwb3dlciBhbmFseXNpcy4KCi0gVGhlIGRhdGEgb2YgdGhlIGhmIG1pY2Ugc2VlbSB0byBiZSBub3JtYWxseSBkaXN0cmlidXRlZAotIFRoZSBtZWFuIHdlaWdodCBpcyBgciBtaWNlU3VtICU+JSBmaWx0ZXIoRGlldCA9PSAiaGYiKSAlPiUgcHVsbCgibWVhbiIpICU+JSByb3VuZCguLDEpYGcKLSBUaGUgU0Qgb2YgdGhlIHdlaWdodCBpcyBgciBtaWNlU3VtICU+JSBmaWx0ZXIoRGlldCA9PSAiaGYiKSAlPiUgcHVsbCgic2QiKSAlPiUgcm91bmQoLiwxKWBnCgpFZmZlY3Qgc2l6ZT8KCi0gVGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgaXMgY29tcGxleC4KLSBJdCBpbmNsdWRlcyBhbGwgcG9zc2libGUgZWZmZWN0cyEKLSBJbiBvcmRlciB0byBkbyB0aGUgcG93ZXIgYW5hbHlzaXMgd2Ugd2lsbCBoYXZlIHRvIGNob29zZSBhIG1pbmltdW0gZWZmZWN0IHNpemUgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIGRldGVjdC4KCi0gU3VwcG9zZSB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gZGV0ZWN0IGlmIHRoZSB3ZWlnaHQgb2YgdGhlIHJhdHMgZGlmZmVyZW5jZSAkXGRlbHRhJCBvZiBhdCBsZWFzdCAxMCUuCgpgYGB7cn0KZGVsdGEgPC0gLXJvdW5kKG1pY2VTdW0kbWVhblsyXSAqIC4xLCAxKQpkZWx0YQpgYGAKCk5vdGUsIHRoYXQgdGhlIGF2ZXJhZ2Ugd2VpZ2h0IHRoZW4gd291bGQgZ2V0IGNsb3NlIHRvIHRoYXQgb2YgdGhlIHJhdHMgaW4gb3VyIHBpbG90IGV4cGVyaW1lbnQgdGhhdCB3ZXJlIGZlZCBvbiB0aGUgY2hvdyBkaWV0LgoKV2UgY2FuIHNldCB1cCBhIHNpbXVsYXRpb24gc3R1ZHkgdG8gYXNzZXNzIHRoZSBwb3dlciBvZiBhbiBleHBlcmltZW50IHdpdGggOCBtaWNlIGluIGVhY2ggZ3JvdXA6CgojIyMjIE9uZSBzaW11bGF0aW9uCgpgYGB7cn0Kc2V0LnNlZWQoMTQyMykKbjEgPC0gbjIgPC0gOApiMCA8LSByb3VuZChtaWNlU3VtJG1lYW5bMl0sIDEpCmIxIDwtIC1kZWx0YQpzZCA8LSByb3VuZChtaWNlU3VtJHNkWzJdLCAxKQphbHBoYSA8LSAwLjA1Cgp4IDwtIHJlcCgwOjEsIGMobjEsIG4yKSkKeSA8LSBiMCArIGIxICogeCArIHJub3JtKG4xICsgbjIsIDAsIHNkID0gc2QpCmZpdCA8LSBsbSh5IH4geCkKYmhhdCA8LSBmaXQkY29lZgpzdGF0IDwtIHN1bW1hcnkoZml0KQpzdW1tYXJ5KGZpdCkkY29lZlsyLCBdCmBgYAoKRm9yIHRoaXMgc2ltdWxhdGVkIGV4cGVyaW1lbnQgd2Ugd291bGQgYHIgaWZlbHNlKHN1bW1hcnkoZml0KSRjb2VmWzIsIlByKD58dHwpIl0gPCBhbHBoYSwiIiwibm90IilgIGhhdmUgYmVlbiBhYmxlIHRvIHBpY2sgdXAgdGhlIGVmZmVjdCBvZiB0aGUgdHJlYXRtZW50IGF0IHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgJFxhbHBoYSQ9YHIgYWxwaGFgIChwID0gYHIgcm91bmQoc3VtbWFyeShmaXQpJGNvZWZbMiwiUHIoPnx0fCkiXSwyKWApIQoKLSBXZSBoYXZlIHRvIHJlcGVhdCB0aGUgZXhwZXJpbWVudCBtYW55IHRpbWVzIHRvIGVzdGltYXRlIHRoZSBwb3dlci4KLSBXZSB3aWxsIHRoZXJlZm9yZSBwdXQgb3VyIHNpbXVsYXRpb24gaW4gYSBnZW5lcmFsIGZ1bmN0aW9uLgoKIyMjIyBTaW11bGF0aW9uIG9mIHJlcGVhdGVkIGV4cGVyaW1lbnRzCgpgYGB7cn0KbGlicmFyeShtdWx0Y29tcCkKbjEgPC0gbjIgPC0gOAoKYjAgPC0gcm91bmQobWljZVN1bSRtZWFuWzJdLCAxKQpiMSA8LSAtZGVsdGEKCnNkIDwtIHJvdW5kKG1pY2VTdW0kc2RbMl0sIDEpCnByZWRpY3RvckRhdGEgPC0gZGF0YS5mcmFtZShEaWV0ID0gcmVwKGMoImMiLCAiaGYiKSwgYyhuMSwgbjIpKSAlPiUgYXMuZmFjdG9yKCkpCgphbHBoYSA8LSAwLjA1CgpzaW1MbSA8LSBmdW5jdGlvbihmb3JtLCBkYXRhLCBiZXRhcywgc2QsIGNvbnRyYXN0cywgc2ltSW5kZXggPSBOQSkgewogIGRhdGFTaW0gPC0gZGF0YQogIFggPC0gbW9kZWwubWF0cml4KGZvcm0sIGRhdGFTaW0pCiAgZGF0YVNpbSR5U2ltIDwtIFggJSolIGJldGFzICsgcm5vcm0obnJvdyhkYXRhU2ltKSwgMCwgc2QpCiAgZm9ybSA8LSBmb3JtdWxhKHBhc3RlKCJ5U2ltIH4iLCBmb3JtW1syXV0pKQogIGZpdFNpbSA8LSBsbShmb3JtLCBkYXRhU2ltKQogIG1jcCA8LSBnbGh0KGZpdFNpbSwgbGluZmN0ID0gY29udHJhc3RzKQogIHJldHVybihzdW1tYXJ5KG1jcCkkdGVzdFtjKCJjb2VmZmljaWVudHMiLCAic2lnbWEiLCAidHN0YXQiLCAicHZhbHVlcyIpXSAlPiUgdW5saXN0KCkpCn0KCnNpbUxtKAogIGZvcm0gPSB+RGlldCwKICBkYXRhID0gcHJlZGljdG9yRGF0YSwKICBiZXRhcyA9IGMoYjAsIGIxKSwKICBzZCA9IHNkLAogIGNvbnRyYXN0cyA9ICJEaWV0aGYgPSAwIgopCmBgYAoKV2Ugbm93IGhhdmUgYSBnZW5lcmljIGZ1bmN0aW9uIHRoYXQgY2FuIHNpbXVsYXRlIGRhdGEgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24gZm9yIGV2ZXJ5IGRlc2lnbi4KVGhlIGZ1bmN0aW9uIGhhcyBhcmd1bWVudHM6CgotIGBmb3JtYDogT25lIHNpZGVkIGZvcm11bGEgaW5jbHVkaW5nIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIHByZWRpY3RvcnMgaW4gdGhlIG1vZGVsCi0gYGRhdGFgOiBEYXRhIGZyYW1lIHdpdGggdGhlIHByZWRpY3RvciB2YWx1ZXMgZm9yIHRoZSBkZXNpZ24KLSBgYmV0YXNgOiBBIHZlY3RvciB3aXRoIHZhbHVlcyBmb3IgYWxsIG1lYW4gbW9kZWwgcGFyYW1ldGVycwotIGBzZGA6IFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGVycm9yCi0gYGNvbnRyYXN0c2A6IGEgc2NhbGFyIG9yIGEgdmVjdG9yIHdpdGggdGhlIG51bGwgaHlwb3RoZXNlcyB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gYXNzZXNzLgotIGBzaW1JbmRleGA6IGFuIGFyYml0cmFyeSBhcmd1bWVudCB0aGF0IGlzIG5vdCB1c2VkIGJ5IHRoZSAgZnVuY3Rpb24gYnV0IHRoYXQgd2lsbCBhbGxvdyBpdCB0byBiZSB1c2VkIGluIGFuIHNhcHBseSBsb29wIHRoYXQgcnVucyBmcm9tIDEgdXAgdG8gdGhlIG51bWJlciBvZiBzaW11bGF0aW9ucy4KClNpbXVsYXRlIG5TaW0gPSAxMDAwIHJlcGVhdGVkIGV4cGVyaW1lbnRzOgoKYGBge3J9CnNldC5zZWVkKDE0MjUpCm5TaW0gPC0gMTAwMAoKc2ltUmVzdWx0cyA8LSB0KHNhcHBseSgxOm5TaW0sIHNpbUxtLCBmb3JtID0gfkRpZXQsIGRhdGEgPSBwcmVkaWN0b3JEYXRhLCBiZXRhcyA9IGMoYjAsIGIxKSwgc2QgPSBzZCwgY29udHJhc3RzID0gIkRpZXRoZiA9IDAiKSkKcG93ZXIgPC0gbWVhbihzaW1SZXN1bHRzWywgNF0gPCBhbHBoYSkKcG93ZXIKYGBgCgpXZSBoYXZlIGEgcG93ZXIgb2YgYHIgcG93ZXIqMTAwYCUgdG8gcGljayB1cCB0aGUgdHJlYXRtZW50IGVmZmVjdCB3aGVuIHVzaW5nIDggYmlvcmVwcyBpbiBlYWNoIHRyZWF0bWVudCBncm91cC4KCiMjIyMgUG93ZXIgZm9yIG11bHRpcGxlIHNhbXBsZSBzaXplcwoKV2UgY2FuIG5vdyBjYWxjdWxhdGUgdGhlIHBvd2VyIGZvciBtdWx0aXBsZSBzYW1wbGUgc2l6ZXMuCgpgYGB7cn0KcG93ZXIgPC0gZGF0YS5mcmFtZShuID0gYygzLCA1LCAxMCwgMjUsIDUwLCA3NSwgMTAwKSwgcG93ZXIgPSBOQSkKCmZvciAoaSBpbiAxOm5yb3cocG93ZXIpKQp7CiAgbjEgPC0gbjIgPC0gcG93ZXIkbltpXQogIHByZWRpY3RvckRhdGEgPC0gZGF0YS5mcmFtZShEaWV0ID0gcmVwKGMoImMiLCAiaGYiKSwgYyhuMSwgbjIpKSAlPiUgYXMuZmFjdG9yKCkpCiAgc2ltUmVzdWx0cyA8LSB0KHNhcHBseSgxOm5TaW0sIHNpbUxtLCBmb3JtID0gfkRpZXQsIGRhdGEgPSBwcmVkaWN0b3JEYXRhLCBiZXRhcyA9IGMoYjAsIGIxKSwgc2QgPSBzZCwgY29udHJhc3RzID0gIkRpZXRoZiA9IDAiKSkKICBwb3dlciRwb3dlcltpXSA8LSBtZWFuKHNpbVJlc3VsdHNbLCAicHZhbHVlcyJdIDwgYWxwaGEpCn0KcG93ZXIKYGBgCgpgYGB7cn0KcG93ZXIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbiwgeSA9IHBvd2VyKSkgKwogIGdlb21fbGluZSgpCmBgYAoKIyMjIyBBc3Nlc3MgcG93ZXIgaW4gZnVuY3Rpb24gb2Ygc2FtcGxlIHNpemUgZm9yIGRpZmZlcmVudCBlZmZlY3Qgc2l6ZXMKCk5vdGUsIHRoYXQKLSB0aGUgc2lnbiBvZiB0aGUgZGVsdGEgaXMgYXJiaXRyYXJ5IGJlY2F1c2Ugd2UgdGVzdCB0d28tc2lkZWQuCi0gdGhlIGludGVyY2VwdCBpcyBhcmJpdHJhcnkgYmVjYXVzZSB3ZSBvbmx5IGFzc2VzICRcYmV0YV8wJAotIFdlIHRoZXJlZm9yZSB0eXBpY2FsbHkgc2V0ICRcYmV0YV8wID0gMCQKCmBgYHtyfQpuU2ltIDwtIDEwMDAKYjAgPC0gMApkZWx0YXMgPC0gYygxLCAyLCAzLCA1LCAxMCkKc2QgPC0gcm91bmQobWljZVN1bSRzZFsyXSwgMSkKbnMgPC0gYygzLCA1LCAxMCwgMjAsIDI1LCA1MCwgNzUsIDEwMCkKCnBvd2VyIDwtIGRhdGEuZnJhbWUoCiAgYjEgPSByZXAoZGVsdGFzLCBlYWNoID0gbGVuZ3RoKG5zKSksCiAgbiA9IHJlcChucywgbGVuZ3RoKGRlbHRhcykpLAogIHBvd2VyID0gTkEKKQoKZm9yIChpIGluIDE6bnJvdyhwb3dlcikpCnsKICBiMSA8LSBwb3dlciRiMVtpXQogIG4xIDwtIG4yIDwtIHBvd2VyJG5baV0KICBwcmVkaWN0b3JEYXRhIDwtIGRhdGEuZnJhbWUoRGlldCA9IHJlcChjKCJjIiwgImhmIiksIGMobjEsIG4yKSkgJT4lIGFzLmZhY3RvcigpKQogIHNpbVJlc3VsdHMgPC0gdChzYXBwbHkoMTpuU2ltLCBzaW1MbSwgZm9ybSA9IH5EaWV0LCBkYXRhID0gcHJlZGljdG9yRGF0YSwgYmV0YXMgPSBjKGIwLCBiMSksIHNkID0gc2QsIGNvbnRyYXN0cyA9ICJEaWV0aGYgPSAwIikpCiAgcG93ZXIkcG93ZXJbaV0gPC0gbWVhbihzaW1SZXN1bHRzWywgInB2YWx1ZXMiXSA8IGFscGhhKQp9CmBgYAoKYGBge3J9CnBvd2VyICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSBwb3dlciwgY29sID0gYjEgJT4lIGFzLmZhY3RvcigpKSkgKwogIGdlb21fbGluZSgpCmBgYAoKTm90ZSwgdGhhdCB0aGUgcG93ZXIgY3VydmVzIGFyZSBzdGlsbCBhIGJpdCBjaG9wcHkgYmVjYXVzZSB3ZSBzZWxlY3RlZCBhIGxpbWl0ZWQgbnVtYmVyIG9mIHNhbXBsZSBzaXplcyBhbmQgYmVjYXVzZSBgblNpbWAgc2ltdWxhdGlvbnMgaXMgbm90IGVub3VnaCB0byBnZXQgYSBnb29kIHBvd2VyIGVzdGltYXRlIHdoZW4gdGhlIHBvd2VyIGlzIGxvdy4KCiMgRmluYWwgcmVtYXJrcwoKLSBUaGUgY29kZSBjYW4gYmUgZWFzaWx5IGV4dGVuZGVkIHRvd2FyZHMgb3RoZXIgZGVzaWducyBieSBhbHRlcmluZwoKICAgIC0gcHJlZGljdG9yIGRhdGEKICAgIC0gZm9ybXVsYQogICAgLSBjb250cmFzdAoKLSBUaGUgc2ltdWxhdGlvbnMgc3RhcnQgdG8gdGFrZSBsb25nIHdoZW4geW91IGV2YWx1YXRlIG1hbnkgc2NlbmFyaW8ncy4KCiAgICAtIE1vcmUgZWZmaWNpZW50IGNvZGUgdXNpbmcgbWF0cmljZXMKICAgIC0gRm9yIGEgdHdvIGdyb3VwIGNvbXBhcmlzb24gY2xvc2VkIGZvcm0gc29sdXRpb25zIGV4aXN0CgoKCgojIyBNb3JlIGVmZmljaWVudCBjb2RlIGJhc2VkIG9uIG1hdHJpeCBhbGdlYnJhCgpDb2RlIHJ1bnMgbXVjaCBmYXN0ZXIuIFdlIG5vdyBzaW11bGF0ZSAyMCB0aW1lcyBtb3JlIGV4cGVyaW1lbnRzIGluIGEgbXVjaCBzaG9ydGVyIHRpbWUhCgpgYGB7cn0Kc2ltRmFzdCA8LSBmdW5jdGlvbihmb3JtLCBkYXRhLCBiZXRhcywgc2QsIGNvbnRyYXN0cywgYWxwaGEgPSAuMDUsIG5TaW0gPSAxMDAwMCkgewogIHlTaW0gPC0gcm5vcm0obnJvdyhkYXRhKSAqIG5TaW0sIHNkID0gc2QpCiAgZGltKHlTaW0pIDwtIGMobnJvdyhkYXRhKSwgblNpbSkKICBkZXNpZ24gPC0gbW9kZWwubWF0cml4KGZvcm0sIGRhdGEpCiAgeVNpbSA8LSB5U2ltICsgYyhkZXNpZ24gJSolIGJldGFzKQogIHlTaW0gPC0gdCh5U2ltKQoKICAjIyMgRml0dGluZwogIGZpdEFsbCA8LSBsaW1tYTo6bG1GaXQoeVNpbSwgZGVzaWduKQoKICAjIyMgSW5mZXJlbmNlCiAgdmFyVW5zY2FsZWQgPC0gYyh0KGNvbnRyYXN0cykgJSolIGZpdEFsbCRjb3YuY29lZmZpY2llbnRzICUqJSBjb250cmFzdHMpCiAgY29udHJhc3RzIDwtIGZpdEFsbCRjb2VmZmljaWVudHMgJSolIGNvbnRyYXN0cwogIHNlQ29udHJhc3RzIDwtIHZhclVuc2NhbGVkXi41ICogZml0QWxsJHNpZ21hCiAgdHN0YXRzIDwtIGNvbnRyYXN0cyAvIHNlQ29udHJhc3RzCiAgcHZhbHMgPC0gcHQoYWJzKHRzdGF0cyksIGZpdEFsbCRkZi5yZXNpZHVhbCwgbG93ZXIudGFpbCA9IEZBTFNFKSAqIDIKICByZXR1cm4obWVhbihwdmFscyA8IGFscGhhKSkKfQpgYGAKCmBgYHtyfQpuU2ltIDwtIDIwMDAwCmIwIDwtIDAKc2QgPC0gcm91bmQobWljZVN1bSRzZFsyXSwgMSkKbnMgPC0gYygzLCA1LCAxMCwgMjAsIDI1LCA1MCwgNzUsIDEwMCkKZGVsdGFzIDwtIGMoMSwgMiwgMywgNSwgMTApCgpjb250cmFzdCA8LSBsaW1tYTo6bWFrZUNvbnRyYXN0cygiRGlldGhmIiwgbGV2ZWxzID0gYygiKEludGVyY2VwdCkiLCAiRGlldGhmIikpCgpwb3dlckZhc3QgPC0gbWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKG5zKSAqIGxlbmd0aChkZWx0YXMpLCBuY29sID0gMykgJT4lIGFzLmRhdGEuZnJhbWUoKQpuYW1lcyhwb3dlckZhc3QpIDwtIGMoImIxIiwgIm4iLCAicG93ZXIiKQpmb3JtIDwtIH5EaWV0CgppIDwtIDAKCmZvciAobiBpbiBucykKewogIG4xIDwtIG4yIDwtIG4KCiAgIyMjIFNpbXVsYXRpb24KICBwcmVkaWN0b3JEYXRhIDwtIGRhdGEuZnJhbWUoRGlldCA9IHJlcChjKCJjIiwgImhmIiksIGMobjEsIG4yKSkgJT4lIGFzLmZhY3RvcigpKQoKICBmb3IgKGIxIGluIGRlbHRhcykKICB7CiAgICBpIDwtIGkgKyAxCiAgICBiZXRhcyA8LSBjKGIwLCBiMSkKICAgIHBvd2VyRmFzdFtpLCBdIDwtIGMoYjEsIG4sIHNpbUZhc3QoZm9ybSwgcHJlZGljdG9yRGF0YSwgYmV0YXMsIHNkLCBjb250cmFzdHMgPSBjb250cmFzdCwgYWxwaGEgPSBhbHBoYSwgblNpbSA9IG5TaW0pKQogIH0KfQpgYGAKCmBgYHtyfQpwb3dlckZhc3QgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbiwgeSA9IHBvd2VyLCBjb2wgPSBiMSAlPiUgYXMuZmFjdG9yKCkpKSArCiAgZ2VvbV9saW5lKCkKYGBgCgojIyBNb3JlIGVmZmljaWVudCBjb2RlIGJhc2VkIG9uIGNsb3NlZCBmb3JtIHNvbHV0aW9uIHRoYXQgZXhpc3RzIGZvciB0d28gZ3JvdXAgY29tcGFyaXNvbgoKRm9yIHRoZSB0d28gc2FtcGxlIHQtdGVzdCBhIGNsb3NlZCBmb3JtIGVzdGltYXRlIGV4aXN0cyBmb3IgdGhlIHBvd2VyLgpJbiB0aGlzIGNvbnRleHQgdGhlIENvaGVuJ3MgZWZmZWN0IHNpemUgaXMgdHlwaWNhbGx5IHVzZWQ6CgokRCA9IFxmcmFje1xkZWx0YX17U0R9JAoKCmBgYHtyfQpwb3dlci50LnRlc3QobiA9IDgsIGRlbHRhID0gZGVsdGEsIHNkID0gc2QsIHR5cGUgPSAidHdvLnNhbXBsZSIpCmBgYAoKTm90ZSwgdGhhdCB0aGlzIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgcG93ZXIgdGhhdCB3ZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBzaW11bGF0aW9ucyEKCmBgYHtyfQpiMCA8LSAwCnNkIDwtIHJvdW5kKG1pY2VTdW0kc2RbMl0sIDEpCm5zIDwtIGMoMywgNSwgMTAsIDIwLCAyNSwgNTAsIDc1LCAxMDApCmRlbHRhcyA8LSBjKDEsIDIsIDMsIDUsIDEwKQoKCnBvd2VyVGhlbyA8LSBkYXRhLmZyYW1lKAogIGRlbHRhcyA9IHJlcChkZWx0YXMsIGVhY2ggPSBsZW5ndGgobnMpKSwKICBuID0gcmVwKG5zLCBsZW5ndGgoZGVsdGFzKSksCiAgcG93ZXIgPSBOQQopCgpwb3dlclRoZW8kcG93ZXIgPC0gYXBwbHkocG93ZXJUaGVvWywgMToyXSwgMSwgZnVuY3Rpb24oeCkgcG93ZXIudC50ZXN0KGRlbHRhID0geFsxXSwgbiA9IHhbMl0sIHNkID0gc2QsIHR5cGUgPSAidHdvLnNhbXBsZSIpJHBvd2VyKQpgYGAKCgpgYGB7cn0KcG93ZXJUaGVvICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSBwb3dlciwgY29sID0gZGVsdGFzICU+JSBhcy5mYWN0b3IoKSkpICsKICBnZW9tX2xpbmUoKQpgYGAK