
## ── 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()
Concepts
- Experimental units representative for population: Randomisation
- Replication: technical vs biological, sample size - power
- Sources of variation: technical, biological, within subject, between subject
Replication
Two pager on Replication in nature methods: [PDF]
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
Sources of variation
TABLE 1 NATURE METHODS | VOL.11 NO.9 | SEPTEMBER 2014 | 879 - 880
|
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 |
Vb |
|
Reads with unique molecular identifier (UMI) from a given transcript molecule |
T |
- Replicates are categorized as biological (B), technical (T) or of variable type (V).
- Sequence reads serve diverse purposes depending on the application and how reads are used in analysis.
At which level should we replicate?
- var(X)=σ2A+σ2C+σ2M=σ2TOT
- var(ˉX)=σ2AnA+σ2CnAnC+σ2MnAnCnM
Figure 1 NATURE METHODS | VOL.11 NO.9 | SEPTEMBER 2014 | 879 - 880
- 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.
- Expression variance, Var(X), and variance of expression mean, Var(ˉ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(ˉ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
→ commit resources to sampling biologically relevant variables
→ unless measures of technical variability are of interest then increasing number of technical measurements is valuable.
Power, sample size and other design aspects
Reading materials: Nature Methods (2013), 10(12), 1139–1140
Least Squares (LS)
\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})
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.
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.
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.
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
- The assumptions of the model are met: weights are normally distributed with same variance
and we know
- the standard deviation of the weight measurements around their average mean for HFD-fed mice
- the real effect size in the population
- n_1 and n_2
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
| | | | |
---|
chow | 23.81333 | 3.022541 | 12 | 0.8725323 |
hf | 26.83417 | 4.097606 | 12 | 1.1828769 |
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:
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.
Simulation of repeated experiments
## 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.
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
| | | | |
---|
3 | 0.114 | | | |
5 | 0.158 | | | |
10 | 0.304 | | | |
25 | 0.648 | | | |
50 | 0.890 | | | |
75 | 0.982 | | | |
100 | 0.998 | | | |
power %>%
ggplot(aes(x = n, y = power)) +
geom_line()

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.
LS0tCnRpdGxlOiAiOC40IEV4cGVyaW1lbnRhbCBEZXNpZ24gSUlJOiBSZXBsaWNhdGlvbiBhbmQgUG93ZXIiCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgotLS0KCgo8YSByZWw9ImxpY2Vuc2UiIGhyZWY9Imh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1zYS80LjAiPjxpbWcgYWx0PSJDcmVhdGl2ZSBDb21tb25zIExpY2Vuc2UiIHN0eWxlPSJib3JkZXItd2lkdGg6MCIgc3JjPSJodHRwczovL2kuY3JlYXRpdmVjb21tb25zLm9yZy9sL2J5LW5jLXNhLzQuMC84OHgzMS5wbmciIC8+PC9hPgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMgQ29uY2VwdHMKCi0gRXhwZXJpbWVudGFsIHVuaXRzIHJlcHJlc2VudGF0aXZlIGZvciBwb3B1bGF0aW9uOiBSYW5kb21pc2F0aW9uCi0gUmVwbGljYXRpb246IHRlY2huaWNhbCB2cyBiaW9sb2dpY2FsLCBzYW1wbGUgc2l6ZSAtIHBvd2VyCi0gU291cmNlcyBvZiB2YXJpYXRpb246IHRlY2huaWNhbCwgYmlvbG9naWNhbCwgd2l0aGluIHN1YmplY3QsIGJldHdlZW4gc3ViamVjdAoKIyBSZXBsaWNhdGlvbgoKVHdvIHBhZ2VyIG9uIFJlcGxpY2F0aW9uIGluIG5hdHVyZSBtZXRob2RzOiBbW1BERl0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9ubWV0aC4zMDkxLnBkZildCiFbXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25tZXRoLjMwOTEucGRmKXsgd2lkdGg9MTAwJSB9CgojIyBFeGFtcGxlCgpDb25zaWRlciBhIHNpbmdsZSBjZWxsIFJOQS1zZXEgZXhwZXJpbWVudCB3aGVyZSByZXNlYXJjaGVycyB3YW50IHRvIGFzc2VzcyB0aGUgZWZmZWN0IG9mIGRydWcgdHJlYXRtZW50IG9uIGdlbmUgZXhwcmVzc2lvbgoKUG90ZW50aWFsIHJlc2VhcmNoIHF1ZXN0aW9ucwoKLSBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgaW4gYXZlcmFnZSBnZW5lIGV4cHJlc3Npb24gYmV0d2VlbiB0cmVhdGVkIGFuZCBub24gdHJlYXRlZCBzYW1wbGVzCi0gSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGluIHZhcmlhYmlsaXR5IG9mIGdlbmUgZXhwcmVzc2lvbiBiZXR3ZWVuIHRyZWF0ZWQgYW5kIG5vbiB0cmVhdGVkIHNhbXBsZXMKCiMjIFNvdXJjZXMgb2YgdmFyaWF0aW9uCgpUQUJMRSAxIE5BVFVSRSBNRVRIT0RTIHwgVk9MLjExIE5PLjkgfCBTRVBURU1CRVIgMjAxNCB8IDg3OSAtIDg4MAoKfCAgIHwgUmVwbGljYXRlIHR5cGUgfCBSZXBsaWNhdGUgY2F0ZWdvcnkkXlx0ZXh0e2F9JCB8Cnw6LS0tOnw6LS0tfDotLS06fAp8IHwgQ29sb25pZXMgfAlCIHwKfCB8IFN0cmFpbnMgfAlCIHwKfCBBbmltYWwgc3R1ZHkgc3ViamVjdHMgfCBDb2hvdXNlZCBncm91cHMgfAlCIHwKfCB8IEdlbmRlciB8IEIgfAp8IHwgSW5kaXZpZHVhbHMJfCBCIHwKfCB8IHwgfAp8IHwgT3JnYW5zIGZyb20gc2FjcmlmaWNlZCBhbmltYWxzIHwgQiB8CnwgfCBNZXRob2RzIGZvciBkaXNzb2NpYXRpbmcgY2VsbHMgZnJvbSB0aXNzdWUgfCBUIHwKfCBTYW1wbGUgcHJlcGFyYXRpb24gfCBEaXNzb2NpYXRpb24gcnVucyBmcm9tIGdpdmVuIHRpc3N1ZSBzYW1wbGUgfCBUIHwKfCB8IEluZGl2aWR1YWwgY2VsbHMgfCBCIHwKfCB8IHwgfAp8IHwgUk5BLXNlcSBsaWJyYXJ5IGNvbnN0cnVjdGlvbiB8IFQgfAp8IHwgUnVucyBmcm9tIHRoZSBsaWJyYXJ5IG9mIGEgZ2l2ZW4gY2VsbCB8CVQgfAp8IFNlcXVlbmNpbmcgfCBSZWFkcyBmcm9tIGRpZmZlcmVudCB0cmFuc2NyaXB0IG1vbGVjdWxlcyB8IFYkXlx0ZXh0e2J9JCB8CnwgfCBSZWFkcyB3aXRoIHVuaXF1ZSBtb2xlY3VsYXIgaWRlbnRpZmllciAoVU1JKSBmcm9tIGEgZ2l2ZW4gdHJhbnNjcmlwdCBtb2xlY3VsZSB8CVQgfAoKKGEpIFJlcGxpY2F0ZXMgYXJlIGNhdGVnb3JpemVkIGFzIGJpb2xvZ2ljYWwgKEIpLCB0ZWNobmljYWwgKFQpIG9yIG9mIHZhcmlhYmxlIHR5cGUgKFYpLgooYikgU2VxdWVuY2UgcmVhZHMgc2VydmUgZGl2ZXJzZSBwdXJwb3NlcyBkZXBlbmRpbmcgb24gdGhlIGFwcGxpY2F0aW9uIGFuZCBob3cgcmVhZHMgYXJlIHVzZWQgaW4gYW5hbHlzaXMuCgotLS0KCiMjIEF0IHdoaWNoIGxldmVsIHNob3VsZCB3ZSByZXBsaWNhdGU/CgotICRcdGV4dHt2YXJ9KFgpPVxzaWdtYV4yX0ErXHNpZ21hXjJfQytcc2lnbWFeMl9NPVxzaWdtYV4yX3tUT1R9JAotICRcdGV4dHt2YXJ9KFxiYXJ7WH0pPVxmcmFje1xzaWdtYV4yX0F9e25fQX0rXGZyYWN7XHNpZ21hXjJfQ317bl9BIG5fQ30gKyBcZnJhY3tcc2lnbWFeMl9NfXtuX0Egbl9DIG5fTX0kCgohW10oaHR0cHM6Ly9tZWRpYS5zcHJpbmdlcm5hdHVyZS5jb20vZnVsbC9zcHJpbmdlci1zdGF0aWMvaW1hZ2UvYXJ0JTNBMTAuMTAzOCUyRm5tZXRoLjMwOTEvTWVkaWFPYmplY3RzLzQxNTkyXzIwMTRfQXJ0aWNsZV9CRm5tZXRoMzA5MV9GaWcxX0hUTUwuanBnKQpGaWd1cmUgMSBOQVRVUkUgTUVUSE9EUyB8IFZPTC4xMSBOTy45IHwgU0VQVEVNQkVSIDIwMTQgfCA4NzkgLSA4ODAKCihhKSBUaHJlZSBsZXZlbHMgb2YgcmVwbGljYXRpb24gKHR3byBiaW9sb2dpY2FsLCBvbmUgdGVjaG5pY2FsKSB3aXRoIGFuaW1hbCwgY2VsbCBhbmQgbWVhc3VyZW1lbnQgcmVwbGljYXRlcyBub3JtYWxseSBkaXN0cmlidXRlZCB3aXRoIGEgbWVhbiBhY3Jvc3MgYW5pbWFscyBvZiAxMCBhbmQgcmF0aW8gb2YgdmFyaWFuY2VzIDE6MjowLjUuIFNvbGlkIGdyZWVuIChiaW9sb2dpY2FsKSBhbmQgYmx1ZSAodGVjaG5pY2FsKSBkb3RzIHNob3cgaG93IGEgbWVhc3VyZW1lbnQgb2YgdGhlIGV4cHJlc3Npb24gKFggPSAxMikgc2FtcGxlcyBmcm9tIGFsbCB0aHJlZSBzb3VyY2VzIG9mIHZhcmlhdGlvbi4gRGlzdHJpYnV0aW9uIHMuZC4gaXMgc2hvd24gYXMgaG9yaXpvbnRhbCBsaW5lcy4KKGIpIEV4cHJlc3Npb24gdmFyaWFuY2UsIFZhcihYKSwgYW5kIHZhcmlhbmNlIG9mIGV4cHJlc3Npb24gbWVhbiwgVmFyKCRcYmFyIFgkKSwgY29tcHV0ZWQgYWNyb3NzIDEwLDAwMCBzaW11bGF0aW9ucyBvZiBuQW5Dbk0gPSA0OCBtZWFzdXJlbWVudHMgZm9yIHVuaXF1ZSBjb21iaW5hdGlvbnMgb2YgdGhlIG51bWJlciBvZiBhbmltYWxzIChuQSA9IDEgdG8gNDgpLCBjZWxscyBwZXIgYW5pbWFsIChuQyA9IDEgdG8gNDgpIGFuZCB0ZWNobmljYWwgcmVwbGljYXRlIG1lYXN1cmVtZW50cyBwZXIgY2VsbCAobk0gPSAxIGFuZCAzKS4gVGhlIHJhdGlvIG9mIFZhcihYKSBhbmQgVmFyKCRcYmFyIFgkKSBpcyB0aGUgZWZmZWN0aXZlIHNhbXBsZSBzaXplLCBuLCB3aGljaCBjb3JyZXNwb25kcyB0byB0aGUgZXF1aXZhbGVudCBudW1iZXIgb2Ygc3RhdGlzdGljYWxseSBpbmRlcGVuZGVudCBtZWFzdXJlbWVudHMuIEhvcml6b250YWwgZGFzaGVkIGxpbmVzIGNvcnJlc3BvbmQgdG8gYmlvbG9naWNhbCBhbmQgdG90YWwgdmFyaWF0aW9uLiBFcnJvciBiYXJzIG9uIFZhcihYKSBzaG93IHMuZC4gZnJvbSB0aGUgMTAsMDAwIHNpbXVsYXRlZCBzYW1wbGVzIChuTSA9IDEpLgoKLS0tCgotIENvc3QgYmVuZWZpdCB0cmFkZS1vZmY6IGNvc3QgZGlmZmVyZW5jZSBiZXR3ZWVuIGJpb2xvZ2ljYWwgYW5kIHRlY2huaWNhbCByZXBsaWNhdGVzCi0gVHlwaWNhbGx5LCBiaW9sb2dpY2FsIHZhcmlhYmlsaXR5IGlzIHN1YnN0YW50aWFsbHkgbGFyZ2VyIHRoYW4gdGVjaG5pY2FsIHZhcmlhYmlsaXR5CgokXHJpZ2h0YXJyb3ckIGNvbW1pdCByZXNvdXJjZXMgdG8gc2FtcGxpbmcgYmlvbG9naWNhbGx5IHJlbGV2YW50IHZhcmlhYmxlcwoKJFxyaWdodGFycm93JCB1bmxlc3MgbWVhc3VyZXMgb2YgdGVjaG5pY2FsIHZhcmlhYmlsaXR5IGFyZSBvZiBpbnRlcmVzdCB0aGVuIGluY3JlYXNpbmcgbnVtYmVyIG9mIHRlY2huaWNhbCBtZWFzdXJlbWVudHMgaXMgdmFsdWFibGUuCgotIEdvb2QgZXhwZXJpbWVudGFsIGRlc2lnbiBwcmFjdGljZSBpbmNsdWRlcyBwbGFubmluZyBmb3IgcmVwbGljYXRpb24uCgogIDEuIElkZW50aWZ5IHRoZSByZXNlYXJjaCBxdWVzdGlvbnMgdG8gYmUgYW5zd2VyZWQgd2l0aCBleHBlcmltZW50cy4KICAyLiBEZXRlcm1pbmUgcHJvcG9ydGlvbiBvZiB2YXJpYWJpbGl0eSBpbmR1Y2VkIGJ5IGVhY2ggc3RlcC4KICAzLiBCZSBhd2FyZSBmb3IgcHNldWRvcmVwbGljYXRpb24gYW5kIGFpbSBmb3Igc3RhdGlzdGljYWxseSBpbmRlcGVuZGVudCByZXBsaWNhdGVzLgoKLS0tCgojIFBvd2VyLCBzYW1wbGUgc2l6ZSBhbmQgb3RoZXIgZGVzaWduIGFzcGVjdHMKClJlYWRpbmcgbWF0ZXJpYWxzOgpbTmF0dXJlIE1ldGhvZHMgKDIwMTMpLCAxMCgxMiksIDExMznigJMxMTQwXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25tZXRoLjI3MzgucGRmKQoKIyMgSW50ZXJtZXp6byBsaW5lYXIgcmVncmVzc2lvbiBpbiBtYXRyaXggZm9ybQoKLSBMaW5lYXIgcmVncmVzc2lvbiBpcyBhIHZlcnkgaW1wb3J0YW50IHN0YXRpc3RpY2FsIHRvb2wgdG8gc3R1ZHkgdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gdmFyaWFibGVzIGFuZCB0byBidWlsZCBwcmVkaWN0aW9uIG1vZGVscy4KCiMjIyBTY2FsYXIgZm9ybQoKLSBDb25zaWRlciBhIHZlY3RvciBvZiBwcmVkaWN0b3JzICRcbWF0aGJme3h9PSh4XzEsXGxkb3RzLHhfcCkkIGFuZAotIGEgcmVhbC12YWx1ZWQgcmVzcG9uc2UgJFkkCi0gdGhlbiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXMKXFsKWT1mKFxtYXRoYmZ7eH0pICtcZXBzaWxvbj1cYmV0YV8wK1xzdW1cbGltaXRzX3tqPTF9XnAgeF9qXGJldGFfaiArIFxlcHNpbG9uClxdCndpdGggaS5pLmQuICRcZXBzaWxvblxzaW0gTigwLFxzaWdtYV4yKSQKCgoKIyMjIFZlY3Rvci9NYXRyaXggZm9ybQoKLSAkbiQgb2JzZXJ2YXRpb25zICQoXG1hdGhiZnt4fV8xLHlfMSkgXGxkb3RzIChcbWF0aGJme3h9X24seV9uKSQgd2l0aCAkXG1hdGhiZnt4fV8xXlQ9WzEgeF8xIFxsZG90cyB4X3BdJAotIFJlZ3Jlc3Npb24gaW4gbWF0cml4IG5vdGF0aW9uClxbXG1hdGhiZntZfT1cbWF0aGJme1hcYmV0YX0gKyBcbWF0aGJme1xlcHNpbG9ufVxdCndpdGggJFxtYXRoYmZ7WX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfXlfMVxcIFx2ZG90c1xceV9uXGVuZHthcnJheX1ccmlnaHRdJCwKJFxtYXRoYmZ7WH09XGxlZnRbXGJlZ2lue2FycmF5fXtjY2NjfSAxJnhfezExfSZcbGRvdHMmeF97MXB9XFwKXHZkb3RzJlx2ZG90cyYmXHZkb3RzXFwKMSZ4X3tuMX0mXGxkb3RzJnhfe25wfQpcZW5ke2FycmF5fVxyaWdodF0kIG9yICRcbWF0aGJme1h9PVxsZWZ0W1xiZWdpbnthcnJheX17Y30gXG1hdGhiZnt4fV8xXlRcXFx2ZG90c1xcXG1hdGhiZnt4fV9uXlRcZW5ke2FycmF5fVxyaWdodF0kLAokXGJvbGRzeW1ib2x7XGJldGF9PVxsZWZ0W1xiZWdpbnthcnJheX17Y31cYmV0YV8wXFwgXHZkb3RzXFwgXGJldGFfcFxlbmR7YXJyYXl9XHJpZ2h0XSQgYW5kCiRcbWF0aGJme1xlcHNpbG9ufT1cbGVmdFtcYmVnaW57YXJyYXl9e2N9IFxlcHNpbG9uXzEgXFwgXHZkb3RzIFxcIFxlcHNpbG9uX25cZW5ke2FycmF5fVxyaWdodF0kCgotLS0KCiMjIExlYXN0IFNxdWFyZXMgKExTKQoKLSBNaW5pbWl6ZSB0aGUgcmVzaWR1YWwgc3VtIG9mIHNxdWFyZXMKXGJlZ2lue2VxbmFycmF5Kn0KUlNTKFxib2xkc3ltYm9se1xiZXRhfSkmPSZcc3VtXGxpbWl0c197aT0xfV5uIGVeMl9pXFwKJj0mXHN1bVxsaW1pdHNfe2k9MX1ebiBcbGVmdCh5X2ktXGJldGFfMC1cc3VtXGxpbWl0c197aj0xfV5wIHhfe2lqfVxiZXRhX2pccmlnaHQpXjIKXGVuZHtlcW5hcnJheSp9CgotIG9yIGluIG1hdHJpeCBub3RhdGlvbgoKJCQKXHRleHR7UlNTfShcYm9sZHN5bWJvbHtcYmV0YX0pPShcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSleVChcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSkKJCQKCiQkXHJpZ2h0YXJyb3cgXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19PVx0ZXh0e2FyZ21pbn1fXGJldGEgXHRleHR7IFJTU30oXGJvbGRzeW1ib2x7XGJldGF9KSQkCgotLS0KCiMjIyBNaW5pbWl6ZSBSU1MKXFsKXGJlZ2lue2FycmF5fXtjY2N9ClxmcmFje1xwYXJ0aWFsIFJTU317XHBhcnRpYWwgXGJvbGRzeW1ib2x7XGJldGF9fSY9JlxtYXRoYmZ7MH1cXFxcClxmcmFjeyhcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSleVChcbWF0aGJme1l9LVxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0pfXtccGFydGlhbCBcYm9sZHN5bWJvbHtcYmV0YX19Jj0mXG1hdGhiZnswfVxcXFwKLTJcbWF0aGJme1h9XlQoXG1hdGhiZntZfS1cbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9KSY9JlxtYXRoYmZ7MH1cXFxcClxtYXRoYmZ7WH1eVFxtYXRoYmZ7WFxiZXRhfSY9JlxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1cXFxcClxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntZfQpcZW5ke2FycmF5fQpcXQoKLS0tCgojIyBWYXJpYW5jZSBFc3RpbWF0b3I/ClxbClxiZWdpbnthcnJheX17Y2NsfQpcaGF0e1xib2xkc3ltYm9se1xTaWdtYX19X3tcaGF0e1xib2xkc3ltYm9se1xiZXRhfX19CiY9Jlx0ZXh0e3Zhcn1cbGVmdFsoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1ccmlnaHRdXFxcXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XVxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxcXFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlQoXG1hdGhiZntJfVxzaWdtYV4yKVxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfQpcXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntJfVxxdWFkXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjJcXFxcCiVcaGF0e1xib2xkbWF0aHtcU2lnbWF9fV97XGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19fSY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UIFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfVxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjJcXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjIKXGVuZHthcnJheX0KXF0KClRoZSB1bmNlcnRhaW50eSBvbiB0aGUgbW9kZWwgcGFyYW1ldGVycyB0aHVzIGRlcGVuZHMgb24gdGhlIHJlc2lkdWFsIHZhcmlhYmlsaXR5IGFuZCB0aGUgZGVzaWduIQoKLSBUaGUgbGFyZ2VyICRcbWF0aGJme1h9XlRcbWF0aGJme1h9JCB0aGUgbW9yZSBpbmZvcm1hdGlvbiB0aGUgZXhwZXJpbWVudCBjb250YWlucyBvbiB0aGUgbW9kZWwgcGFyYW1ldGVycyBhbmQgdGhlIHNtYWxsZXIgdGhlaXIgdmFyaWFuY2VzIGFuZCBzdGFuZGFyZCBlcnJvcnMgd2lsbCBiZSEKLSBGYWN0b3JpYWwgZGVzaWducz8KLSBEZXNpZ25zIHdpdGggY29udGludW91cyBwcmVkaWN0b3JzPwoKLS0tCgpUaGUgZWZmZWN0IHNpemUgb2YgaW50ZXJlc3QgaXMgdHlwaWNhbGx5IGEgbGluZWFyIGNvbWJpbmF0aW9ucyBvZiB0aGUgbW9kZWwgcGFyYW1ldGVycywgaS5lLgokJApsXzAgXHRpbWVzIFxiZXRhXzAgKyBsXzEgXHRpbWVzIFxiZXRhXzEgKyAuLi4gKyBsX3twLTF9IFx0aW1lcyBcYmV0YV97cC0xfSA9IFxtYXRoYmZ7TH1eVFxib2xkc3ltYm9se1xiZXRhfQokJAoKVGhlIG51bGwgaHlwb3RoZXNpcyBvZiBvdXIgdGVzdCBzdGF0aXN0aWNzIGlzIHRoYW4gZm9ybXVsYXRlZCBhcwoKCiQkCkhfMDogXG1hdGhiZntMfV5UXGJvbGRzeW1ib2x7XGJldGF9ID0gMAokJAoKdnMgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMKCiQkCkhfMDogXG1hdGhiZntMfV5UXGJvbGRzeW1ib2x7XGJldGF9IFxuZXEgMAokJAp3aGljaCBjYW4gYmUgYXNzZXNzZWQgdXNpbmcgYSB0LXRlc3Qgc3RhdGlzdGljOgoKJCQKdD1cZnJhY3tcbWF0aGJme0x9XlRcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gLSAwfXtcdGV4dHtzZX1fe1xtYXRoYmZ7TH1eVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fX19CiQkCndoaWNoIGZvbGxvd3MgYSB0LWRpc3RyaWJ1dGlvbiB3aXRoIG4tcCBkZWdyZWVzIG9mIGZyZWVkb20gdW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcyB3aGVuIGFsbCBhc3N1bXB0aW9ucyBmb3IgdGhlIGxpbmVhciBtb2RlbCBhcmUgdmFsaWQuCgpTbyB0aGUgcG93ZXIKCiQkUChwIDwgMC4wNSB8IEhfMSkkJAoKd2lsbCB0eXBpY2FsbHkgZGVwZW5kcyBvbgoKLSB0aGUgcmVhbCBlZmZlY3Qgc2l6ZSBpbiB0aGUgcG9wdWxhdGlvbiAgJFxtYXRoYmZ7TH1eVFxib2xkc3ltYm9se1xiZXRhfSQuCi0gTnVtYmVyIG9mIG9ic2VydmF0aW9uczogU0UgYW5kIGRmIG9mIHQtdGVzdC4KLSBDaG9pY2Ugb2YgdGhlIGRlc2lnbiBwb2ludHMuCi0gQ2hvaWNlIG9mIHNpZ25pZmljYW5jZSBsZXZlbC4KClNpbWlsYXIgdG8gdGhlIGludHJvZHVjdG9yeSBleGFtcGxlLCB3ZSBjYW4gdXNlIHNpbXVsYXRpb25zIHRvIGFzc2VzcyB0aGUgcG93ZXIuCgojIyBNb3VzZSBleGFtcGxlCgotIEluIDIwMjEgQ2hvYSBldCBhbC4gcHVibGlzaGVkIHRoYXQgdGhlIGN5dG9raW5lIFRoeW1pYyBzdHJvbWFsIGx5bXBob3BvaWV0aW4KKFRTTFApIGluZHVjZWQgZmF0IGxvc3MgdGhyb3VnaCBzZWJ1bSBzZWNyZXRpb24gKHRhbGcpLiBbW2h0bWxdKGh0dHBzOi8vd3d3LnNjaWVuY2Uub3JnL2RvaS9mdWxsLzEwLjExMjYvc2NpZW5jZS5hYmQyODkzKV0gW1tQREZdKGh0dHBzOi8vd3d3LnNjaWVuY2Uub3JnL2RvaS9wZGYvMTAuMTEyNi9zY2llbmNlLmFiZDI4OTMpXQoKLSBTdXBwb3NlIHRoYXQgeW91IHdvdWxkIGxpa2UgdG8gc2V0IHVwIGEgc2ltaWxhciBzdHVkeSB0byB0ZXN0IGlmIGN5dG9raW5lIGludGVybGV1a2luIDI1IChJTCkgYWxzbyBoYXMgYmVuZWZpY2lhbCBlZmZlY3QuCgotIFlvdSBwbGFuIHRvIHNldHVwIGEgc3R1ZHkgd2l0aCBhIGNvbnRyb2wgZ3JvdXAgb2YgaGlnaCBmYXQgZGlldCAoSEZEKSBmZWQgbWljZSBhbmQgYSB0cmVhdG1lbnQgZ3JvdXAgdGhhdCByZWNpZXZlcyB0aGUgSEZEIGFuZCBJTC4KCi0gV2hhdCBzYW1wbGUgc2l6ZSBkbyB5b3UgbmVlZCB0byBwaWNrIHVwIHRoZSBlZmZlY3Qgb2YgdGhlIHRyZWF0bWVudC4KCgojIyMgSG93IHdpbGwgd2UgYW5hbHlzZSB0aGUgZGF0YSBvZiB0aGlzIGV4cGVyaW1lbnQ/CgoKLS0tCgoKCi0gJEhfMCQ6IFRoZSBhdmVyYWdlIHdlaWdodCBkaWZmZXJlbmNlIGlzIGVxdWFsIHRvIHplcm8KLSAkSF8xJDogVGhlIGF2ZXJhZ2Ugd2VpZ2h0IGRpZmZlcmVuY2UgaXMgZGlmZmVyZW50IGZyb20gemVybwoKLSBUd28gc2FtcGxlIHQtdGVzdCBvciBhIHQtdGVzdCBvbiB0aGUgc2xvcGUgb2YgYSBsaW5lYXIgbW9kZWwgd2l0aCBvbmUgZHVtbXkgdmFyaWFibGUuCgokJCBZX2kgPSBcYmV0YV8wICsgXGJldGFfMSBYX1x0ZXh0e2lJTH0gKyBcZXBzaWxvbl9pJCQKCiQkClx0ZXh0e3dpdGggfQpYX1x0ZXh0e2lJTH09XGJlZ2lue2Nhc2VzfVhfe2lJTH09MCAmIFx0ZXh0e0hGRH1cXFhfe2lJTH09MSAmIFx0ZXh0e0hGRCArIElMfVxlbmR7Y2FzZXN9LgokJAoKLSBFc3RpbWF0ZWQgZWZmZWN0IHNpemUKJCRcaGF0XGRlbHRhID0gXGJhciBYX3tJTH0gLSBcYmFyIFhfe2N9ID0gXGhhdCBcYmV0YV8xJCQKCi0gVGVzdCBzdGF0aXN0aWMKJCQKVCA9IFxmcmFje1xiYXJ7WH1fe0lMfS1cYmFye1h9X3tjfX17U0RfXHRleHR7cG9vbGVkfSBcdGltZXMgXHNxcnR7XGZyYWN7MX17bl8xfSArIFxmcmFjezF9e25fMn19fSA9IFxmcmFje1xoYXRcYmV0YV8xfXtcdGV4dHtTRX1fe1xoYXRcYmV0YV8xfX0KJCQKLSAkXGhhdCBcYmV0YV8xJCBpcyBhbiB1bmJpYXNlZCBlc3RpbWF0b3Igb2YgdGhlIHJlYWwgd2VpZ2h0IGRpZmZlcmVuY2UgKCRcZGVsdGEkKSB0aGF0IHdvdWxkIG9jY3VyIGluIHRoZSBwb3B1bGF0aW9uIG9mIHJhdHMgZmVkIHdpdGggSEZEIGFuZCByYXRzIGZlZCB3aXRoIEhGRCArIElMLgoKIyMjIFBvd2VyPwoKJCQKUFtwIDwgXGFscGhhIFx2ZXJ0IFxiZXRhXzEgXG5lcSAwXQokJAoKCi0gSGVuY2UsIHRoZSBwb3dlciB3aWxsIGRlcGVuZCBvbiB0aGUgcmVhbCB3ZWlnaHQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBncm91cCBtZWFucyAkXGJldGFfMSQgaW4gdGhlIHBvcHVsYXRpb24uCi0gVGhlIHZhcmlhYmlsaXR5IG9mIHRoZSB3ZWlnaHQgbWVhc3VyZW1lbnRzCi0gVGhlIHNpZ25pZmljYW5jZSBsZXZlbCAkXGFscGhhJAotIFNhbXBsZSBzaXplICRuX3tJTH0kIGFuZCAkbl9jJCBpbiBib3RoIGdyb3VwcwoKCldlIGNhbiBlc3RpbWF0ZSB0aGUgcG93ZXIgaWYKCjEuIFRoZSBhc3N1bXB0aW9ucyBvZiB0aGUgbW9kZWwgYXJlIG1ldDogd2VpZ2h0cyBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aCBzYW1lIHZhcmlhbmNlCgphbmQgd2Uga25vdwoKMi4gdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgd2VpZ2h0IG1lYXN1cmVtZW50cyBhcm91bmQgdGhlaXIgYXZlcmFnZSBtZWFuIGZvciBIRkQtZmVkIG1pY2UKMy4gdGhlIHJlYWwgZWZmZWN0IHNpemUgaW4gdGhlIHBvcHVsYXRpb24KNC4gJG5fMSQgYW5kICRuXzIkCgojIyMgVXNlIGRhdGEgZnJvbSBhIHByZXZpb3VzIGV4cGVyaW1lbnQgdG8gZ2V0IGluc2lnaHQgaW4gbWljZSBkYXRhCgpTdXBwb3NlIHRoYXQgd2UgaGF2ZSBhY2Nlc3MgdG8gdGhlIGRhdGEgb2YgYSBwcmVsaW1pbmFyeSBleHBlcmltZW50IChlLmcuIHByb3ZpZGVkIGJ5IEthcmVuIFN2ZW5zb24gdmlhIEdhcnkgQ2h1cmNoaWxsIGFuZCBEYW4gR2F0dGkgYW5kIHBhcnRpYWxseSBmdW5kZWQgYnkgUDUwIEdNMDcwNjgzIG9uIFtQSDUyNXhdKGh0dHA6Ly9nZW5vbWljc2NsYXNzLmdpdGh1Yi5pby9ib29rL3BhZ2VzL3JhbmRvbV92YXJpYWJsZXMuaHRtbCkpCgpgYGB7cn0KbWljZSA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dlbm9taWNzY2xhc3MvZGFnZGF0YS9tYXN0ZXIvaW5zdC9leHRkYXRhL2ZlbWFsZU1pY2VXZWlnaHRzLmNzdiIpCgptaWNlICU+JQogIGdncGxvdChhZXMoeCA9IERpZXQsIHkgPSBCb2R5d2VpZ2h0KSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gRkFMU0UpICsKICBnZW9tX2ppdHRlcigpCgptaWNlICU+JQogIGdncGxvdChhZXMoc2FtcGxlID0gQm9keXdlaWdodCkpICsKICBnZW9tX3FxKCkgKwogIGdlb21fcXFfbGluZSgpICsKICBmYWNldF93cmFwKH5EaWV0KQpgYGAKCmBgYHtyfQptaWNlIDwtIG1pY2UgJT4lIG11dGF0ZShEaWV0ID0gYXMuZmFjdG9yKERpZXQpKQptaWNlU3VtIDwtIG1pY2UgJT4lCiAgZ3JvdXBfYnkoRGlldCkgJT4lCiAgc3VtbWFyaXplKAogICAgbWVhbiA9IG1lYW4oQm9keXdlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgIHNkID0gc2QoQm9keXdlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgIG4gPSBuKCkKICApICU+JQogIG11dGF0ZShzZSA9IHNkIC8gc3FydChuKSkKbWljZVN1bQpgYGAKSW4gdGhlIGV4cGVyaW1lbnQgd2UgaGF2ZSBkYXRhIGZyb20gdHdvIGRpZXRzOgoKLSByZWd1bGFyIGRpZXQgb2YgY2VyaWFsIGFuZCBncmFpbiBiYXNlZCBkaWV0IChDaG93KQotIEhpZ2ggRmF0IChoZikKCldlIGNhbiB1c2UgdGhlIGhmIG1pY2UgYXMgaW5wdXQgZm9yIG91ciBwb3dlciBhbmFseXNpcy4KCi0gVGhlIGRhdGEgb2YgdGhlIGhmIG1pY2Ugc2VlbSB0byBiZSBub3JtYWxseSBkaXN0cmlidXRlZAotIFRoZSBtZWFuIHdlaWdodCBpcyBgciBtaWNlU3VtICU+JSBmaWx0ZXIoRGlldCA9PSAiaGYiKSAlPiUgcHVsbCgibWVhbiIpICU+JSByb3VuZCguLDEpYGcKLSBUaGUgU0Qgb2YgdGhlIHdlaWdodCBpcyBgciBtaWNlU3VtICU+JSBmaWx0ZXIoRGlldCA9PSAiaGYiKSAlPiUgcHVsbCgic2QiKSAlPiUgcm91bmQoLiwxKWBnCgpFZmZlY3Qgc2l6ZT8KCi0gVGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgaXMgY29tcGxleC4KLSBJdCBpbmNsdWRlcyBhbGwgcG9zc2libGUgZWZmZWN0cyEKLSBJbiBvcmRlciB0byBkbyB0aGUgcG93ZXIgYW5hbHlzaXMgd2Ugd2lsbCBoYXZlIHRvIGNob29zZSBhIG1pbmltdW0gZWZmZWN0IHNpemUgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIGRldGVjdC4KCi0gU3VwcG9zZSB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gZGV0ZWN0IGlmIHRoZSB3ZWlnaHQgb2YgdGhlIHJhdHMgZGlmZmVyZW5jZSAkXGRlbHRhJCBvZiBhdCBsZWFzdCAxMCUuCgpgYGB7cn0KZGVsdGEgPC0gLXJvdW5kKG1pY2VTdW0kbWVhblsyXSAqIC4xLCAxKQpkZWx0YQpgYGAKCk5vdGUsIHRoYXQgdGhlIGF2ZXJhZ2Ugd2VpZ2h0IHRoZW4gd291bGQgZ2V0IGNsb3NlIHRvIHRoYXQgb2YgdGhlIHJhdHMgaW4gb3VyIHBpbG90IGV4cGVyaW1lbnQgdGhhdCB3ZXJlIGZlZCBvbiB0aGUgY2hvdyBkaWV0LgoKV2UgY2FuIHNldCB1cCBhIHNpbXVsYXRpb24gc3R1ZHkgdG8gYXNzZXNzIHRoZSBwb3dlciBvZiBhbiBleHBlcmltZW50IHdpdGggOCBtaWNlIGluIGVhY2ggZ3JvdXA6CgojIyMjIE9uZSBzaW11bGF0aW9uCgpgYGB7cn0Kc2V0LnNlZWQoMTQyMykKbjEgPC0gbjIgPC0gOApiMCA8LSByb3VuZChtaWNlU3VtJG1lYW5bMl0sIDEpCmIxIDwtIC1kZWx0YQpzZCA8LSByb3VuZChtaWNlU3VtJHNkWzJdLCAxKQphbHBoYSA8LSAwLjA1Cgp4IDwtIHJlcCgwOjEsIGMobjEsIG4yKSkKeSA8LSBiMCArIGIxICogeCArIHJub3JtKG4xICsgbjIsIDAsIHNkID0gc2QpCmZpdCA8LSBsbSh5IH4geCkKYmhhdCA8LSBmaXQkY29lZgpzdGF0IDwtIHN1bW1hcnkoZml0KQpzdW1tYXJ5KGZpdCkkY29lZlsyLCBdCmBgYAoKRm9yIHRoaXMgc2ltdWxhdGVkIGV4cGVyaW1lbnQgd2Ugd291bGQgYHIgaWZlbHNlKHN1bW1hcnkoZml0KSRjb2VmWzIsIlByKD58dHwpIl0gPCBhbHBoYSwiIiwibm90IilgIGhhdmUgYmVlbiBhYmxlIHRvIHBpY2sgdXAgdGhlIGVmZmVjdCBvZiB0aGUgdHJlYXRtZW50IGF0IHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgJFxhbHBoYSQ9YHIgYWxwaGFgIChwID0gYHIgcm91bmQoc3VtbWFyeShmaXQpJGNvZWZbMiwiUHIoPnx0fCkiXSwyKWApIQoKLSBXZSBoYXZlIHRvIHJlcGVhdCB0aGUgZXhwZXJpbWVudCBtYW55IHRpbWVzIHRvIGVzdGltYXRlIHRoZSBwb3dlci4KLSBXZSB3aWxsIHRoZXJlZm9yZSBwdXQgb3VyIHNpbXVsYXRpb24gaW4gYSBnZW5lcmFsIGZ1bmN0aW9uLgoKIyMjIyBTaW11bGF0aW9uIG9mIHJlcGVhdGVkIGV4cGVyaW1lbnRzCgpgYGB7cn0KbGlicmFyeShtdWx0Y29tcCkKbjEgPC0gbjIgPC0gOAoKYjAgPC0gcm91bmQobWljZVN1bSRtZWFuWzJdLCAxKQpiMSA8LSAtZGVsdGEKCnNkIDwtIHJvdW5kKG1pY2VTdW0kc2RbMl0sIDEpCnByZWRpY3RvckRhdGEgPC0gZGF0YS5mcmFtZShEaWV0ID0gcmVwKGMoImMiLCAiaGYiKSwgYyhuMSwgbjIpKSAlPiUgYXMuZmFjdG9yKCkpCgphbHBoYSA8LSAwLjA1CgpzaW1MbSA8LSBmdW5jdGlvbihmb3JtLCBkYXRhLCBiZXRhcywgc2QsIGNvbnRyYXN0cywgc2ltSW5kZXggPSBOQSkgewogIGRhdGFTaW0gPC0gZGF0YQogIFggPC0gbW9kZWwubWF0cml4KGZvcm0sIGRhdGFTaW0pCiAgZGF0YVNpbSR5U2ltIDwtIFggJSolIGJldGFzICsgcm5vcm0obnJvdyhkYXRhU2ltKSwgMCwgc2QpCiAgZm9ybSA8LSBmb3JtdWxhKHBhc3RlKCJ5U2ltIH4iLCBmb3JtW1syXV0pKQogIGZpdFNpbSA8LSBsbShmb3JtLCBkYXRhU2ltKQogIG1jcCA8LSBnbGh0KGZpdFNpbSwgbGluZmN0ID0gY29udHJhc3RzKQogIHJldHVybihzdW1tYXJ5KG1jcCkkdGVzdFtjKCJjb2VmZmljaWVudHMiLCAic2lnbWEiLCAidHN0YXQiLCAicHZhbHVlcyIpXSAlPiUgdW5saXN0KCkpCn0KCnNpbUxtKAogIGZvcm0gPSB+RGlldCwKICBkYXRhID0gcHJlZGljdG9yRGF0YSwKICBiZXRhcyA9IGMoYjAsIGIxKSwKICBzZCA9IHNkLAogIGNvbnRyYXN0cyA9ICJEaWV0aGYgPSAwIgopCmBgYAoKV2Ugbm93IGhhdmUgYSBnZW5lcmljIGZ1bmN0aW9uIHRoYXQgY2FuIHNpbXVsYXRlIGRhdGEgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24gZm9yIGV2ZXJ5IGRlc2lnbi4KVGhlIGZ1bmN0aW9uIGhhcyBhcmd1bWVudHM6CgotIGBmb3JtYDogT25lIHNpZGVkIGZvcm11bGEgaW5jbHVkaW5nIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIHByZWRpY3RvcnMgaW4gdGhlIG1vZGVsCi0gYGRhdGFgOiBEYXRhIGZyYW1lIHdpdGggdGhlIHByZWRpY3RvciB2YWx1ZXMgZm9yIHRoZSBkZXNpZ24KLSBgYmV0YXNgOiBBIHZlY3RvciB3aXRoIHZhbHVlcyBmb3IgYWxsIG1lYW4gbW9kZWwgcGFyYW1ldGVycwotIGBzZGA6IFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGVycm9yCi0gYGNvbnRyYXN0c2A6IGEgc2NhbGFyIG9yIGEgdmVjdG9yIHdpdGggdGhlIG51bGwgaHlwb3RoZXNlcyB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gYXNzZXNzLgotIGBzaW1JbmRleGA6IGFuIGFyYml0cmFyeSBhcmd1bWVudCB0aGF0IGlzIG5vdCB1c2VkIGJ5IHRoZSAgZnVuY3Rpb24gYnV0IHRoYXQgd2lsbCBhbGxvdyBpdCB0byBiZSB1c2VkIGluIGFuIHNhcHBseSBsb29wIHRoYXQgcnVucyBmcm9tIDEgdXAgdG8gdGhlIG51bWJlciBvZiBzaW11bGF0aW9ucy4KClNpbXVsYXRlIG5TaW0gPSAxMDAwIHJlcGVhdGVkIGV4cGVyaW1lbnRzOgoKYGBge3J9CnNldC5zZWVkKDE0MjUpCm5TaW0gPC0gMTAwMAoKc2ltUmVzdWx0cyA8LSB0KHNhcHBseSgxOm5TaW0sIHNpbUxtLCBmb3JtID0gfkRpZXQsIGRhdGEgPSBwcmVkaWN0b3JEYXRhLCBiZXRhcyA9IGMoYjAsIGIxKSwgc2QgPSBzZCwgY29udHJhc3RzID0gIkRpZXRoZiA9IDAiKSkKcG93ZXIgPC0gbWVhbihzaW1SZXN1bHRzWywgNF0gPCBhbHBoYSkKcG93ZXIKYGBgCgpXZSBoYXZlIGEgcG93ZXIgb2YgYHIgcG93ZXIqMTAwYCUgdG8gcGljayB1cCB0aGUgdHJlYXRtZW50IGVmZmVjdCB3aGVuIHVzaW5nIDggYmlvcmVwcyBpbiBlYWNoIHRyZWF0bWVudCBncm91cC4KCiMjIyMgUG93ZXIgZm9yIG11bHRpcGxlIHNhbXBsZSBzaXplcwoKV2UgY2FuIG5vdyBjYWxjdWxhdGUgdGhlIHBvd2VyIGZvciBtdWx0aXBsZSBzYW1wbGUgc2l6ZXMuCgpgYGB7cn0KcG93ZXIgPC0gZGF0YS5mcmFtZShuID0gYygzLCA1LCAxMCwgMjUsIDUwLCA3NSwgMTAwKSwgcG93ZXIgPSBOQSkKCmZvciAoaSBpbiAxOm5yb3cocG93ZXIpKQp7CiAgbjEgPC0gbjIgPC0gcG93ZXIkbltpXQogIHByZWRpY3RvckRhdGEgPC0gZGF0YS5mcmFtZShEaWV0ID0gcmVwKGMoImMiLCAiaGYiKSwgYyhuMSwgbjIpKSAlPiUgYXMuZmFjdG9yKCkpCiAgc2ltUmVzdWx0cyA8LSB0KHNhcHBseSgxOm5TaW0sIHNpbUxtLCBmb3JtID0gfkRpZXQsIGRhdGEgPSBwcmVkaWN0b3JEYXRhLCBiZXRhcyA9IGMoYjAsIGIxKSwgc2QgPSBzZCwgY29udHJhc3RzID0gIkRpZXRoZiA9IDAiKSkKICBwb3dlciRwb3dlcltpXSA8LSBtZWFuKHNpbVJlc3VsdHNbLCAicHZhbHVlcyJdIDwgYWxwaGEpCn0KcG93ZXIKYGBgCgpgYGB7cn0KcG93ZXIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbiwgeSA9IHBvd2VyKSkgKwogIGdlb21fbGluZSgpCmBgYAoKIyMjIyBBc3Nlc3MgcG93ZXIgaW4gZnVuY3Rpb24gb2Ygc2FtcGxlIHNpemUgZm9yIGRpZmZlcmVudCBlZmZlY3Qgc2l6ZXMKCk5vdGUsIHRoYXQKLSB0aGUgc2lnbiBvZiB0aGUgZGVsdGEgaXMgYXJiaXRyYXJ5IGJlY2F1c2Ugd2UgdGVzdCB0d28tc2lkZWQuCi0gdGhlIGludGVyY2VwdCBpcyBhcmJpdHJhcnkgYmVjYXVzZSB3ZSBvbmx5IGFzc2VzICRcYmV0YV8wJAotIFdlIHRoZXJlZm9yZSB0eXBpY2FsbHkgc2V0ICRcYmV0YV8wID0gMCQKCmBgYHtyfQpuU2ltIDwtIDEwMDAKYjAgPC0gMApkZWx0YXMgPC0gYygxLCAyLCAzLCA1LCAxMCkKc2QgPC0gcm91bmQobWljZVN1bSRzZFsyXSwgMSkKbnMgPC0gYygzLCA1LCAxMCwgMjAsIDI1LCA1MCwgNzUsIDEwMCkKCnBvd2VyIDwtIGRhdGEuZnJhbWUoCiAgYjEgPSByZXAoZGVsdGFzLCBlYWNoID0gbGVuZ3RoKG5zKSksCiAgbiA9IHJlcChucywgbGVuZ3RoKGRlbHRhcykpLAogIHBvd2VyID0gTkEKKQoKZm9yIChpIGluIDE6bnJvdyhwb3dlcikpCnsKICBiMSA8LSBwb3dlciRiMVtpXQogIG4xIDwtIG4yIDwtIHBvd2VyJG5baV0KICBwcmVkaWN0b3JEYXRhIDwtIGRhdGEuZnJhbWUoRGlldCA9IHJlcChjKCJjIiwgImhmIiksIGMobjEsIG4yKSkgJT4lIGFzLmZhY3RvcigpKQogIHNpbVJlc3VsdHMgPC0gdChzYXBwbHkoMTpuU2ltLCBzaW1MbSwgZm9ybSA9IH5EaWV0LCBkYXRhID0gcHJlZGljdG9yRGF0YSwgYmV0YXMgPSBjKGIwLCBiMSksIHNkID0gc2QsIGNvbnRyYXN0cyA9ICJEaWV0aGYgPSAwIikpCiAgcG93ZXIkcG93ZXJbaV0gPC0gbWVhbihzaW1SZXN1bHRzWywgInB2YWx1ZXMiXSA8IGFscGhhKQp9CmBgYAoKYGBge3J9CnBvd2VyICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSBwb3dlciwgY29sID0gYjEgJT4lIGFzLmZhY3RvcigpKSkgKwogIGdlb21fbGluZSgpCmBgYAoKTm90ZSwgdGhhdCB0aGUgcG93ZXIgY3VydmVzIGFyZSBzdGlsbCBhIGJpdCBjaG9wcHkgYmVjYXVzZSB3ZSBzZWxlY3RlZCBhIGxpbWl0ZWQgbnVtYmVyIG9mIHNhbXBsZSBzaXplcyBhbmQgYmVjYXVzZSBgblNpbWAgc2ltdWxhdGlvbnMgaXMgbm90IGVub3VnaCB0byBnZXQgYSBnb29kIHBvd2VyIGVzdGltYXRlIHdoZW4gdGhlIHBvd2VyIGlzIGxvdy4KCiMgRmluYWwgcmVtYXJrcwoKLSBUaGUgY29kZSBjYW4gYmUgZWFzaWx5IGV4dGVuZGVkIHRvd2FyZHMgb3RoZXIgZGVzaWducyBieSBhbHRlcmluZwoKICAgIC0gcHJlZGljdG9yIGRhdGEKICAgIC0gZm9ybXVsYQogICAgLSBjb250cmFzdAoKLSBUaGUgc2ltdWxhdGlvbnMgc3RhcnQgdG8gdGFrZSBsb25nIHdoZW4geW91IGV2YWx1YXRlIG1hbnkgc2NlbmFyaW8ncy4KCiAgICAtIE1vcmUgZWZmaWNpZW50IGNvZGUgdXNpbmcgbWF0cmljZXMKICAgIC0gRm9yIGEgdHdvIGdyb3VwIGNvbXBhcmlzb24gY2xvc2VkIGZvcm0gc29sdXRpb25zIGV4aXN0CgoKCgojIyBNb3JlIGVmZmljaWVudCBjb2RlIGJhc2VkIG9uIG1hdHJpeCBhbGdlYnJhCgpDb2RlIHJ1bnMgbXVjaCBmYXN0ZXIuIFdlIG5vdyBzaW11bGF0ZSAyMCB0aW1lcyBtb3JlIGV4cGVyaW1lbnRzIGluIGEgbXVjaCBzaG9ydGVyIHRpbWUhCgpgYGB7cn0Kc2ltRmFzdCA8LSBmdW5jdGlvbihmb3JtLCBkYXRhLCBiZXRhcywgc2QsIGNvbnRyYXN0cywgYWxwaGEgPSAuMDUsIG5TaW0gPSAxMDAwMCkgewogIHlTaW0gPC0gcm5vcm0obnJvdyhkYXRhKSAqIG5TaW0sIHNkID0gc2QpCiAgZGltKHlTaW0pIDwtIGMobnJvdyhkYXRhKSwgblNpbSkKICBkZXNpZ24gPC0gbW9kZWwubWF0cml4KGZvcm0sIGRhdGEpCiAgeVNpbSA8LSB5U2ltICsgYyhkZXNpZ24gJSolIGJldGFzKQogIHlTaW0gPC0gdCh5U2ltKQoKICAjIyMgRml0dGluZwogIGZpdEFsbCA8LSBsaW1tYTo6bG1GaXQoeVNpbSwgZGVzaWduKQoKICAjIyMgSW5mZXJlbmNlCiAgdmFyVW5zY2FsZWQgPC0gYyh0KGNvbnRyYXN0cykgJSolIGZpdEFsbCRjb3YuY29lZmZpY2llbnRzICUqJSBjb250cmFzdHMpCiAgY29udHJhc3RzIDwtIGZpdEFsbCRjb2VmZmljaWVudHMgJSolIGNvbnRyYXN0cwogIHNlQ29udHJhc3RzIDwtIHZhclVuc2NhbGVkXi41ICogZml0QWxsJHNpZ21hCiAgdHN0YXRzIDwtIGNvbnRyYXN0cyAvIHNlQ29udHJhc3RzCiAgcHZhbHMgPC0gcHQoYWJzKHRzdGF0cyksIGZpdEFsbCRkZi5yZXNpZHVhbCwgbG93ZXIudGFpbCA9IEZBTFNFKSAqIDIKICByZXR1cm4obWVhbihwdmFscyA8IGFscGhhKSkKfQpgYGAKCmBgYHtyfQpuU2ltIDwtIDIwMDAwCmIwIDwtIDAKc2QgPC0gcm91bmQobWljZVN1bSRzZFsyXSwgMSkKbnMgPC0gYygzLCA1LCAxMCwgMjAsIDI1LCA1MCwgNzUsIDEwMCkKZGVsdGFzIDwtIGMoMSwgMiwgMywgNSwgMTApCgpjb250cmFzdCA8LSBsaW1tYTo6bWFrZUNvbnRyYXN0cygiRGlldGhmIiwgbGV2ZWxzID0gYygiKEludGVyY2VwdCkiLCAiRGlldGhmIikpCgpwb3dlckZhc3QgPC0gbWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKG5zKSAqIGxlbmd0aChkZWx0YXMpLCBuY29sID0gMykgJT4lIGFzLmRhdGEuZnJhbWUoKQpuYW1lcyhwb3dlckZhc3QpIDwtIGMoImIxIiwgIm4iLCAicG93ZXIiKQpmb3JtIDwtIH5EaWV0CgppIDwtIDAKCmZvciAobiBpbiBucykKewogIG4xIDwtIG4yIDwtIG4KCiAgIyMjIFNpbXVsYXRpb24KICBwcmVkaWN0b3JEYXRhIDwtIGRhdGEuZnJhbWUoRGlldCA9IHJlcChjKCJjIiwgImhmIiksIGMobjEsIG4yKSkgJT4lIGFzLmZhY3RvcigpKQoKICBmb3IgKGIxIGluIGRlbHRhcykKICB7CiAgICBpIDwtIGkgKyAxCiAgICBiZXRhcyA8LSBjKGIwLCBiMSkKICAgIHBvd2VyRmFzdFtpLCBdIDwtIGMoYjEsIG4sIHNpbUZhc3QoZm9ybSwgcHJlZGljdG9yRGF0YSwgYmV0YXMsIHNkLCBjb250cmFzdHMgPSBjb250cmFzdCwgYWxwaGEgPSBhbHBoYSwgblNpbSA9IG5TaW0pKQogIH0KfQpgYGAKCmBgYHtyfQpwb3dlckZhc3QgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbiwgeSA9IHBvd2VyLCBjb2wgPSBiMSAlPiUgYXMuZmFjdG9yKCkpKSArCiAgZ2VvbV9saW5lKCkKYGBgCgojIyBNb3JlIGVmZmljaWVudCBjb2RlIGJhc2VkIG9uIGNsb3NlZCBmb3JtIHNvbHV0aW9uIHRoYXQgZXhpc3RzIGZvciB0d28gZ3JvdXAgY29tcGFyaXNvbgoKRm9yIHRoZSB0d28gc2FtcGxlIHQtdGVzdCBhIGNsb3NlZCBmb3JtIGVzdGltYXRlIGV4aXN0cyBmb3IgdGhlIHBvd2VyLgpJbiB0aGlzIGNvbnRleHQgdGhlIENvaGVuJ3MgZWZmZWN0IHNpemUgaXMgdHlwaWNhbGx5IHVzZWQ6CgokRCA9IFxmcmFje1xkZWx0YX17U0R9JAoKCmBgYHtyfQpwb3dlci50LnRlc3QobiA9IDgsIGRlbHRhID0gZGVsdGEsIHNkID0gc2QsIHR5cGUgPSAidHdvLnNhbXBsZSIpCmBgYAoKTm90ZSwgdGhhdCB0aGlzIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgcG93ZXIgdGhhdCB3ZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBzaW11bGF0aW9ucyEKCmBgYHtyfQpiMCA8LSAwCnNkIDwtIHJvdW5kKG1pY2VTdW0kc2RbMl0sIDEpCm5zIDwtIGMoMywgNSwgMTAsIDIwLCAyNSwgNTAsIDc1LCAxMDApCmRlbHRhcyA8LSBjKDEsIDIsIDMsIDUsIDEwKQoKCnBvd2VyVGhlbyA8LSBkYXRhLmZyYW1lKAogIGRlbHRhcyA9IHJlcChkZWx0YXMsIGVhY2ggPSBsZW5ndGgobnMpKSwKICBuID0gcmVwKG5zLCBsZW5ndGgoZGVsdGFzKSksCiAgcG93ZXIgPSBOQQopCgpwb3dlclRoZW8kcG93ZXIgPC0gYXBwbHkocG93ZXJUaGVvWywgMToyXSwgMSwgZnVuY3Rpb24oeCkgcG93ZXIudC50ZXN0KGRlbHRhID0geFsxXSwgbiA9IHhbMl0sIHNkID0gc2QsIHR5cGUgPSAidHdvLnNhbXBsZSIpJHBvd2VyKQpgYGAKCgpgYGB7cn0KcG93ZXJUaGVvICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSBwb3dlciwgY29sID0gZGVsdGFzICU+JSBhcy5mYWN0b3IoKSkpICsKICBnZW9tX2xpbmUoKQpgYGAK