Data wrangling
For a clearer interpretation of the model parameters later on, we will make the untreated state enzymes the reference category.
Puromycin <- Puromycin %>%
mutate(state = fct_relevel(state, c("untreated", "treated")))
Data Exploration
First, we visualize the association between the concentration and the enzyme rate, for both of the enzyme states.
Puromycin %>%
ggplot(aes(conc, rate,col=state)) +
geom_point() +
stat_smooth(method = "lm") +
labs(
x = "Substrate concentration (ppm)",
y = "Reaction rate (counts/min/min)"
) +
ggtitle("Reaction rates for puromycin-treated enzymes")
## `geom_smooth()` using formula 'y ~ x'
The plot shows that there is a relation between the velocity and the concentration, however, the relation does not seem to be linear.
We will assess the impact of log-transforming the concentration. Because the concentration is measured in ppm we will log\(_{10}\) transform the data.
Puromycin %>%
ggplot(aes(x = log10(conc), y = rate,col=state)) +
geom_point() +
stat_smooth(method = "lm") +
labs(
x = "Substrate concentration (log10(ppm))",
y = "Reaction rate (counts/min/min)"
) +
ggtitle("Reaction rates for puromycin-treated enzymes")
## `geom_smooth()` using formula 'y ~ x'
The relation between the velocity and the log\(_{10}\) transformed concentration seems to be linear.
Puromycin <- Puromycin %>%
mutate(log10conc = log10(conc))
Linear regression
We will fit the following model to the data
\(Y_i = \beta_0 + \beta_c x_c+ \beta_s x_s +\beta_{c:s}x_{c}x_{s} + \epsilon_i\)
with
\(Y_i\) the reaction rate,
\(\beta_0\) the intercept,
\(\beta_{c}\) the main effect for log10 concentration,
\(x_c\) the log10 concentration,
\(\beta_{p}\) the main effect for treatment ,
\(x_s\) a dummy variable for “state” that is 0 if the enzymes that are untreated and 1 if the enzymes are treated with Puromycin,
\(\beta_{c:s}\) the interaction effect between concentration and treatment state,
\(\epsilon_i\) i.i.d. normally distributed with mean 0 and variance \(\sigma^2\).
Note, that we write the substrate concentration with a small letter because the predictor is not random. The researchers have chosen the substrate concentrations in the design phase and it is thus no random variable.
The model implies two different regression lines
- no treatment (\(x_s = 0\)) \[
Y_i = \beta_0 + \beta_c x_c + \epsilon
\]
- treatment (x_s = 1)
\[
Y_i = (\beta_0 + \beta_s) + (\beta_c+\beta_{c:s}) x_c + \epsilon
\]
So the main effect for treatment has the interpretation as the change in intercept between treated and untreated samples.
The interaction term has the interpretation as the change in slope between treated and untreated samples.
Intermezzo: Graphical interpretation of the parameters
Simple linear model: 1 slope
This is the same model that we fit in the Chapter 6 exercise:
\[ Y_i = \beta_0 + \beta_{\text{rate}} X_{i, \text{rate}} + \epsilon_i \]
mod_simple <- lm(rate ~ log10conc, data = Puromycin)
mod_simple
##
## Call:
## lm(formula = rate ~ log10conc, data = Puromycin)
##
## Coefficients:
## (Intercept) log10conc
## 190.09 76.45
ggplot(Puromycin, aes(log10conc, rate)) +
geom_point(aes(color = state)) +
geom_abline(
intercept = coef(mod_simple)[1],
slope = coef(mod_simple)[2]
) +
scale_color_manual(values = c("darkorchid", "forestgreen")) +
ggtitle("The simple linear model")
Additive model: 2 parallel slopes
We add an additional term for the state of the reactin (treated or untreated). Note that this variable is categorical.
\[
Y_i = \beta_0 + \beta_{\text{rate}} X_{i, \text{rate}} +
\beta_{\text{state}} X_{i, \text{state}} + \epsilon_i
\]
mod_add <- lm(rate ~ log10conc + state, data = Puromycin)
mod_add
##
## Call:
## lm(formula = rate ~ log10conc + state, data = Puromycin)
##
## Coefficients:
## (Intercept) log10conc statetreated
## 175.73 74.98 25.18
ggplot(Puromycin, aes(log10conc, rate, col = state)) +
geom_point() +
## Line for the untreated group
geom_abline(
intercept = coef(mod_add)[1],
slope = coef(mod_add)[2],
col = "darkorchid"
) +
## Line for the treated group
geom_abline(
intercept = coef(mod_add)[1] + coef(mod_add)[3],
slope = coef(mod_add)[2],
col = "forestgreen"
) +
scale_color_manual(values = c("darkorchid", "forestgreen")) +
ggtitle("The additive linear model with parallel slopes")
The interaction model: 2 non-parallel slopes
\[
Y_i = \beta_0 + \beta_{\text{rate}} X_{i, \text{rate}} +
\beta_{\text{state}} X_{i, \text{state}} +
\beta_{\text{rate:state}} X_{i, \text{rate}} X_{i, \text{state}} +
\epsilon_i
\]
mod_int <- lm(rate ~ log10conc * state, data = Puromycin)
mod_int
##
## Call:
## lm(formula = rate ~ log10conc * state, data = Puromycin)
##
## Coefficients:
## (Intercept) log10conc
## 164.59 62.13
## statetreated log10conc:statetreated
## 44.61 23.32
ggplot(Puromycin, aes(log10conc, rate, col = state)) +
geom_point() +
## Line for the untreated group
geom_abline(
intercept = coef(mod_int)[1],
slope = coef(mod_int)[2],
col = "darkorchid"
) +
## Line for the treated group
geom_abline(
intercept = coef(mod_int)[1] + coef(mod_int)[3],
slope = coef(mod_int)[2] + coef(mod_int)[4],
col = "forestgreen"
) +
scale_color_manual(values = c("darkorchid", "forestgreen")) +
ggtitle("The linear model with interaction: non-parallel slopes")
mod1 <- lm(rate ~ log10conc * state, Puromycin)
summary(mod1)
##
## Call:
## lm(formula = rate ~ log10conc * state, data = Puromycin)
##
## Residuals:
## Min 1Q Median 3Q Max
## -17.018 -6.381 -1.005 7.616 13.323
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 164.588 5.153 31.938 < 2e-16 ***
## log10conc 62.129 5.020 12.375 1.54e-10 ***
## statetreated 44.606 6.811 6.549 2.85e-06 ***
## log10conc:statetreated 23.321 6.763 3.448 0.00269 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 9.151 on 19 degrees of freedom
## Multiple R-squared: 0.968, Adjusted R-squared: 0.9629
## F-statistic: 191.3 on 3 and 19 DF, p-value: 2.267e-14
Before we perform inference we will first assess the assumptions
Assumptions
- Linearity and representative sample
\[E[\hat \beta_j]=\beta_j\]
- Normal distribution of the residuals
\[Y_{i} \sim N(\mu_i,\sigma^2)\] \[Y_i \sim N(\beta_0 + \beta_c x_{ci}+ \beta_s x_{si} +\beta_{c:s}x_{ci}x_{si},\sigma^2) \longrightarrow \hat \beta_j \sim N(\beta_j, \sigma^2_{\hat \beta_j})\]
linear combinations of model parameter estimators are also normally distributed, e.g. \[
\longrightarrow \hat \beta_c + \hat \beta_{c:s} \sim N(\beta_c+\beta_{cs}, \sigma^2_{\hat\beta_c+\hat\beta_{c:s}})
\] \[
\longrightarrow L^T\hat \beta \sim N(L^T\beta, \sigma^2_{L^T\hat{\boldsymbol{\beta}}})
\]
- Independence and Homoscedasticity
We assume that the experiment was well designed and that the different reactions that were use in the experiment are independent.
\[\sigma^2_{L^T\hat{\boldsymbol{\beta}}} = c_L \sigma^2\]
\[\hat \sigma^2 = MSE = \sum\limits_{i=1} ^ n \frac{(Y_i - \hat Y_i)^2}{n-p}\]
\[
SE_{L^T\hat{\boldsymbol{\beta}}} = c_L \hat \sigma^2
\]
\[
T = \frac{L\hat{\boldsymbol{\beta}} - L\boldsymbol{\beta}}{\text{SE}_{L\hat{\boldsymbol{\beta}}}}
\sim t_{n-p}
\]
CI and T-test \(H_0: L\boldsymbol{\beta} = 0\) vs \(H_1: L\boldsymbol{\beta} \neq 0\)
F statistic follows F-distribution under \(H_0\) \[ F = \frac{MSR_2 - MSR_1}{MSE} \sim F_{p_2 - p_1,n-p_2}\]
Linearity
We assess linearity in a residual analysis
The assumption of linearity is met.
Normality
The QQ-plot does not show large deviations from normality.
Homoscedasticity: equality of the variance
We can again use the residual plot for assessing this assumption or the plot were we plot the square root of the standardized residuals in function of the fit.
We see that the spread of the majority of the residuals is more or less similar. As such, we may assume homoscedasticity of the data.
Inference
We first do an omnibus test to assess is there is an effect of the log10 concentration on the velocity.
mod0 <- lm(rate ~ state, data = Puromycin)
anova(mod0, mod1)
Next, we assess the interaction.
We cannot remove the interaction of the model.
Hence, we cannot study the effect of the concentration without accounting for the treatment and have to assess following research questions.
- the association between velocity and the concentration is significant in the untreated group
\[
H_0: \beta_c = 0 \text{ vs } H_1: \beta_c \neq 0
\]
- the association between velocity and the concentration is significant in the treated group
\[
H_0: \beta_c + \beta_{c:s}= 0 \text{ vs } H_1: \beta_c + \beta_{c:s}\neq 0
\]
- the association between velocity and the concentration is different between treated and untreated group
\[
H_0: \beta_{c:s}= 0\text{ vs }H_1: \beta_{c:s}\neq 0
\]
We can assess all these hypotheses using multcomp while correcting for multiple testing.
## 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
mcp1 <- glht(mod1,
linfct = c(
"log10conc = 0",
"log10conc + log10conc:statetreated = 0",
"log10conc:statetreated = 0"
)
)
summary(mcp1)
##
## Simultaneous Tests for General Linear Hypotheses
##
## Fit: lm(formula = rate ~ log10conc * state, data = Puromycin)
##
## Linear Hypotheses:
## Estimate Std. Error t value
## log10conc == 0 62.129 5.020 12.375
## log10conc + log10conc:statetreated == 0 85.450 4.531 18.858
## log10conc:statetreated == 0 23.321 6.763 3.448
## Pr(>|t|)
## log10conc == 0 < 0.001 ***
## log10conc + log10conc:statetreated == 0 < 0.001 ***
## log10conc:statetreated == 0 0.00698 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## (Adjusted p values reported -- single-step method)
##
## Simultaneous Confidence Intervals
##
## Fit: lm(formula = rate ~ log10conc * state, data = Puromycin)
##
## Quantile = 2.5089
## 95% family-wise confidence level
##
##
## Linear Hypotheses:
## Estimate lwr upr
## log10conc == 0 62.1285 49.5330 74.7241
## log10conc + log10conc:statetreated == 0 85.4499 74.0819 96.8180
## log10conc:statetreated == 0 23.3214 6.3544 40.2885
Conclusion
There is an extremely significant effect of the substrate concentration on the reaction rate (p<<0.001). The effect of the substrate concentration on the reaction rate is extremely significant for reactions catalysed with untreated enzymes. A reaction at a substrate concentration that is 10 times higher will have a reaction speed that is on average 62.1 counts/min higher (95% CI [49.5, 74.7] counts/min) (p << 0.001). The effect of the substrate concentration on the reaction rate is extremely significant for reactions catalysed puromycin treated enzymes (p << 0.001). A reaction at a substrate concentration that is 10 times higher will have a reaction speed that is on average 85.4 counts/min higher (95% CI [74.1, 96.8] counts/min). The effect of the substrate concentration on the reaction rate is very significantly higher for reactions catalysed with Puromycin treated enzymes than when catalysed with non-treated enzymes (p = 0.007). A reaction at a substrate concentration that is 10 times higher will have a reaction speed that is on average 23.3 counts/min higher for reactions that are catalysed with Puromycin treated enzymes than with untreated enzymes (95% CI [6.4, 40.3] counts/min).
Additional concepts
Interaction between two continuous predictors?
\[
Y_i = \beta_0 + \beta_v x_{iv} + \beta_w x_{iw} +\beta_s x_{is} + \beta_{vw} x_{iv}x_{iw} +\epsilon_i
\]
Patient with a 1 unit difference in \(X_v\) have an average difference in lpsa of
\[
\begin{array}{l}
E(Y | X_v=x_v +1, X_w=x_w, X_s=x_s) - E(Y | X_v=x_v, X_w=x_w, X_s=x_s) \\
\quad = \left[\beta_0 + \beta_v (x_{v}+1) + \beta_w x_w +\beta_s x_{s} + \beta_{vw} (x_{v}+1) x_w \right] - \left[\beta_0 + \beta_v x_{v} + \beta_w x_w + \beta_s x_{s} + \beta_{vw} (x_{v}) x_w \right]\\
\quad = \beta_v + \beta_{vw} x_w
\end{array}
\]
lmVWS_IntVW <- lm(
lpsa ~ lcavol +
lweight +
svi +
lcavol:lweight,
prostate)
summary(lmVWS_IntVW)
##
## Call:
## lm(formula = lpsa ~ lcavol + lweight + svi + lcavol:lweight,
## data = prostate)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.65886 -0.44673 0.02082 0.50244 1.57457
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.6430 0.7030 -0.915 0.36278
## lcavol 1.0046 0.5427 1.851 0.06734 .
## lweight 0.6146 0.1961 3.134 0.00232 **
## sviinvasion 0.6859 0.2114 3.244 0.00164 **
## lcavol:lweight -0.1246 0.1478 -0.843 0.40156
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.7179 on 92 degrees of freedom
## Multiple R-squared: 0.6293, Adjusted R-squared: 0.6132
## F-statistic: 39.05 on 4 and 92 DF, p-value: < 2.2e-16
## Warning: no DISPLAY variable so Tk is not available
Interaction between two factors?
kpna2 <- read_tsv("https://raw.githubusercontent.com/statOmics/SGA21/master/data/kpna2.txt")
## Rows: 24 Columns: 3
## ── Column specification ──────────────────────────────────────────────
## Delimiter: "\t"
## dbl (3): grade, node, gene
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
kpna2 <- kpna2 %>%
mutate(
grade = as.factor(grade),
node = as.factor(node),
node = fct_recode(node, Unaffected = "0", Removed = "1"),
log2gene = log2(gene)
)
ggplot(kpna2, aes(x = grade, y = gene, fill = node)) +
geom_boxplot(outlier.shape = NA) +
geom_point(position = position_jitterdodge(), shape = 21, size = 2) +
labs(
x = "Histologic grade", y = "KPNA2 expression",
fill = "Lymph node status"
) +
ggtitle("KPNA2 gene expression in breast cancer patients")
ggplot(kpna2, aes(x = grade, y = log2gene, fill = node)) +
geom_boxplot(outlier.shape = NA) +
geom_point(position = position_jitterdodge(), shape = 21, size = 2) +
labs(
x = "Histologic grade", y = "KPNA2 log2 expression",
fill = "Lymph node status"
) +
ggtitle("KPNA2 gene expression in breast cancer patients")
fit <- lm(log2(gene) ~ grade * node, data = kpna2)
ExploreModelMatrix::VisualizeDesign(kpna2, ~ grade * node)$plotlist[[1]]
LS0tCnRpdGxlOiAiV3JhcHVwIE11bHRpcGxlIHJlZ3Jlc3Npb24iCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50LCBKZXJvZW4gR2lsaXMgYW5kIE1pbGFuIE1hbGZhaXQiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCi0tLQoKPGEgcmVsPSJsaWNlbnNlIiBocmVmPSJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtc2EvNC4wIj48aW1nIGFsdD0iQ3JlYXRpdmUgQ29tbW9ucyBMaWNlbnNlIiBzdHlsZT0iYm9yZGVyLXdpZHRoOjAiIHNyYz0iaHR0cHM6Ly9pLmNyZWF0aXZlY29tbW9ucy5vcmcvbC9ieS1uYy1zYS80LjAvODh4MzEucG5nIiAvPjwvYT4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCmBgYAoKIyBQdXJvbXljaW4gZGF0YQoKRGF0YSBvbiB0aGUgdmVsb2NpdHkgb2YgYW4gZW56eW1hdGljIHJlYWN0aW9uIHdlcmUgb2J0YWluZWQgYnkgVHJlbG9hciAoMTk3NCkuClRoZSBudW1iZXIgb2YgY291bnRzIHBlciBtaW51dGUgb2YgcmFkaW9hY3RpdmUgcHJvZHVjdCBmcm9tIHRoZSByZWFjdGlvbiB3YXMKbWVhc3VyZWQgYXMgYSBmdW5jdGlvbiBvZiBzdWJzdHJhdGUgY29uY2VudHJhdGlvbiBpbiBwYXJ0cyBwZXIgbWlsbGlvbiAocHBtKSBhbmQKZnJvbSB0aGVzZSBjb3VudHMgdGhlIGluaXRpYWwgcmF0ZSAob3IgdmVsb2NpdHkpIG9mIHRoZSByZWFjdGlvbiB3YXMgY2FsY3VsYXRlZCAoY291bnRzL21pbi9taW4pLiBUaGUgZXhwZXJpbWVudCB3YXMgY29uZHVjdGVkIG9uY2Ugd2l0aCB0aGUgZW56eW1lIHRyZWF0ZWQKd2l0aCBQdXJvbXljaW4sIGFuZCBvbmNlIHdpdGggdGhlIGVuenltZSB1bnRyZWF0ZWQuCgpBc3Nlc3MgaWYgdGhlcmUgaXMgYW4gYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgc3Vic3RyYXRlIGNvbmNlbnRyYXRpb24gYW5kIHJhdGUKKipmb3IgYm90aCB0aGUgdHJlYXRlZCBhbmQgdW50cmVhdGVkIGVuenltZXMuKioKCiMgSW1wb3J0IGRhdGEKCmBgYHtyfQpkYXRhKFB1cm9teWNpbikKYGBgCgojIERhdGEgd3JhbmdsaW5nCgpGb3IgYSBjbGVhcmVyIGludGVycHJldGF0aW9uIG9mIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIGxhdGVyIG9uLCB3ZSB3aWxsIG1ha2UKdGhlIHVudHJlYXRlZCBzdGF0ZSBlbnp5bWVzIHRoZSByZWZlcmVuY2UgY2F0ZWdvcnkuCgpgYGB7cn0KUHVyb215Y2luIDwtIFB1cm9teWNpbiAlPiUKICBtdXRhdGUoc3RhdGUgPSBmY3RfcmVsZXZlbChzdGF0ZSwgYygidW50cmVhdGVkIiwgInRyZWF0ZWQiKSkpCmBgYAoKIyMgRGF0YSBFeHBsb3JhdGlvbgoKRmlyc3QsIHdlIHZpc3VhbGl6ZSB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgY29uY2VudHJhdGlvbiBhbmQgdGhlIGVuenltZQpyYXRlLCBmb3IgYm90aCBvZiB0aGUgZW56eW1lIHN0YXRlcy4KCmBgYHtyfQpQdXJvbXljaW4gJT4lCiAgZ2dwbG90KGFlcyhjb25jLCByYXRlLGNvbD1zdGF0ZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICBsYWJzKAogICAgeCA9ICJTdWJzdHJhdGUgY29uY2VudHJhdGlvbiAocHBtKSIsCiAgICB5ID0gIlJlYWN0aW9uIHJhdGUgKGNvdW50cy9taW4vbWluKSIKICApICsKICBnZ3RpdGxlKCJSZWFjdGlvbiByYXRlcyBmb3IgcHVyb215Y2luLXRyZWF0ZWQgZW56eW1lcyIpCmBgYAoKVGhlIHBsb3Qgc2hvd3MgdGhhdCB0aGVyZSBpcyBhIHJlbGF0aW9uIGJldHdlZW4gdGhlIHZlbG9jaXR5IGFuZCB0aGUKY29uY2VudHJhdGlvbiwgaG93ZXZlciwgdGhlIHJlbGF0aW9uIGRvZXMgbm90IHNlZW0gdG8gYmUgbGluZWFyLgoKV2Ugd2lsbCBhc3Nlc3MgdGhlIGltcGFjdCBvZiBsb2ctdHJhbnNmb3JtaW5nIHRoZSBjb25jZW50cmF0aW9uLiBCZWNhdXNlIHRoZQpjb25jZW50cmF0aW9uIGlzIG1lYXN1cmVkIGluIHBwbSB3ZSB3aWxsIGxvZyRfezEwfSQgdHJhbnNmb3JtIHRoZSBkYXRhLgoKYGBge3J9ClB1cm9teWNpbiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBsb2cxMChjb25jKSwgeSA9IHJhdGUsY29sPXN0YXRlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIikgKwogICAgbGFicygKICAgICAgeCA9ICJTdWJzdHJhdGUgY29uY2VudHJhdGlvbiAobG9nMTAocHBtKSkiLAogICAgICB5ID0gIlJlYWN0aW9uIHJhdGUgKGNvdW50cy9taW4vbWluKSIKICAgICkgKwogICAgZ2d0aXRsZSgiUmVhY3Rpb24gcmF0ZXMgZm9yIHB1cm9teWNpbi10cmVhdGVkIGVuenltZXMiKQpgYGAKClRoZSByZWxhdGlvbiBiZXR3ZWVuIHRoZSB2ZWxvY2l0eSBhbmQgdGhlIGxvZyRfezEwfSQgdHJhbnNmb3JtZWQgY29uY2VudHJhdGlvbgpzZWVtcyB0byBiZSBsaW5lYXIuCgpgYGB7cn0KUHVyb215Y2luIDwtIFB1cm9teWNpbiAlPiUKICBtdXRhdGUobG9nMTBjb25jID0gbG9nMTAoY29uYykpCmBgYAoKIyBMaW5lYXIgcmVncmVzc2lvbgoKV2Ugd2lsbCBmaXQgdGhlIGZvbGxvd2luZyBtb2RlbCB0byB0aGUgZGF0YQoKJFlfaSA9IFxiZXRhXzAgKyBcYmV0YV9jIHhfYysgXGJldGFfcyB4X3MgK1xiZXRhX3tjOnN9eF97Y314X3tzfSArIFxlcHNpbG9uX2kkCgp3aXRoCgotICRZX2kkIHRoZSByZWFjdGlvbiByYXRlLAoKLSAkXGJldGFfMCQgdGhlIGludGVyY2VwdCwKCi0gJFxiZXRhX3tjfSQgdGhlIG1haW4gZWZmZWN0IGZvciBsb2cxMCBjb25jZW50cmF0aW9uLAoKLSAkeF9jJCB0aGUgbG9nMTAgY29uY2VudHJhdGlvbiwKCi0gJFxiZXRhX3twfSQgdGhlIG1haW4gZWZmZWN0IGZvciB0cmVhdG1lbnQgLAoKLSAkeF9zJCBhIGR1bW15IHZhcmlhYmxlIGZvciAic3RhdGUiIHRoYXQgaXMgMCBpZiB0aGUgZW56eW1lcyB0aGF0IGFyZSB1bnRyZWF0ZWQgYW5kIDEgaWYKdGhlIGVuenltZXMgYXJlIHRyZWF0ZWQgd2l0aCBQdXJvbXljaW4sCgotICRcYmV0YV97YzpzfSQgdGhlIGludGVyYWN0aW9uIGVmZmVjdCBiZXR3ZWVuIGNvbmNlbnRyYXRpb24gYW5kIHRyZWF0bWVudCBzdGF0ZSwKCi0gJFxlcHNpbG9uX2kkIGkuaS5kLiBub3JtYWxseSBkaXN0cmlidXRlZCB3aXRoIG1lYW4gMCBhbmQgdmFyaWFuY2UgJFxzaWdtYV4yJC4KCk5vdGUsIHRoYXQgd2Ugd3JpdGUgdGhlIHN1YnN0cmF0ZSBjb25jZW50cmF0aW9uIHdpdGggYSBzbWFsbCBsZXR0ZXIgYmVjYXVzZQp0aGUgcHJlZGljdG9yIGlzIG5vdCByYW5kb20uIFRoZSByZXNlYXJjaGVycyBoYXZlIGNob3NlbiB0aGUgc3Vic3RyYXRlCmNvbmNlbnRyYXRpb25zIGluIHRoZSBkZXNpZ24gcGhhc2UgYW5kIGl0IGlzIHRodXMgbm8gcmFuZG9tIHZhcmlhYmxlLgoKVGhlIG1vZGVsIGltcGxpZXMgdHdvIGRpZmZlcmVudCByZWdyZXNzaW9uIGxpbmVzCgotIG5vIHRyZWF0bWVudCAoJHhfcyA9IDAkKQokJApZX2kgPSBcYmV0YV8wICsgXGJldGFfYyB4X2MgKyBcZXBzaWxvbgokJAotIHRyZWF0bWVudCAoeF9zID0gMSkKCiQkCllfaSA9IChcYmV0YV8wICsgXGJldGFfcykgICsgKFxiZXRhX2MrXGJldGFfe2M6c30pIHhfYyArIFxlcHNpbG9uCiQkCgpTbyB0aGUgbWFpbiBlZmZlY3QgZm9yIHRyZWF0bWVudCBoYXMgdGhlIGludGVycHJldGF0aW9uIGFzIHRoZSBjaGFuZ2UgaW4gaW50ZXJjZXB0IGJldHdlZW4gdHJlYXRlZCBhbmQgdW50cmVhdGVkIHNhbXBsZXMuCgpUaGUgaW50ZXJhY3Rpb24gdGVybSBoYXMgdGhlIGludGVycHJldGF0aW9uIGFzIHRoZSBjaGFuZ2UgaW4gc2xvcGUgYmV0d2VlbiB0cmVhdGVkIGFuZCB1bnRyZWF0ZWQgc2FtcGxlcy4KCgojIyBJbnRlcm1lenpvOiBHcmFwaGljYWwgaW50ZXJwcmV0YXRpb24gb2YgdGhlIHBhcmFtZXRlcnMKCiMjIyBTaW1wbGUgbGluZWFyIG1vZGVsOiAxIHNsb3BlIHstfQoKVGhpcyBpcyB0aGUgc2FtZSBtb2RlbCB0aGF0IHdlIGZpdCBpbiB0aGUKW0NoYXB0ZXIgNiBleGVyY2lzZV0oLi8wNl8xX3B1cm9teWNpbi5odG1sKToKCiQkIFlfaSA9IFxiZXRhXzAgKyBcYmV0YV97XHRleHR7cmF0ZX19IFhfe2ksIFx0ZXh0e3JhdGV9fSArIFxlcHNpbG9uX2kgJCQKCmBgYHtyfQptb2Rfc2ltcGxlIDwtIGxtKHJhdGUgfiBsb2cxMGNvbmMsIGRhdGEgPSBQdXJvbXljaW4pCm1vZF9zaW1wbGUKCmdncGxvdChQdXJvbXljaW4sIGFlcyhsb2cxMGNvbmMsIHJhdGUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBzdGF0ZSkpICsKICBnZW9tX2FibGluZSgKICAgICBpbnRlcmNlcHQgPSBjb2VmKG1vZF9zaW1wbGUpWzFdLAogICAgIHNsb3BlID0gY29lZihtb2Rfc2ltcGxlKVsyXQogICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJkYXJrb3JjaGlkIiwgImZvcmVzdGdyZWVuIikpICsKICBnZ3RpdGxlKCJUaGUgc2ltcGxlIGxpbmVhciBtb2RlbCIpCmBgYAoKCiMjIyBBZGRpdGl2ZSBtb2RlbDogMiBwYXJhbGxlbCBzbG9wZXMgey19CgpXZSBhZGQgYW4gYWRkaXRpb25hbCB0ZXJtIGZvciB0aGUgc3RhdGUgb2YgdGhlIHJlYWN0aW4gKHRyZWF0ZWQgb3IgdW50cmVhdGVkKS4KTm90ZSB0aGF0IHRoaXMgdmFyaWFibGUgaXMgKipjYXRlZ29yaWNhbCoqLgoKJCQKWV9pID0gXGJldGFfMCArIFxiZXRhX3tcdGV4dHtyYXRlfX0gWF97aSwgXHRleHR7cmF0ZX19ICsKICBcYmV0YV97XHRleHR7c3RhdGV9fSBYX3tpLCBcdGV4dHtzdGF0ZX19ICsgXGVwc2lsb25faQokJAoKYGBge3J9Cm1vZF9hZGQgPC0gbG0ocmF0ZSB+IGxvZzEwY29uYyArIHN0YXRlLCBkYXRhID0gUHVyb215Y2luKQptb2RfYWRkCgpnZ3Bsb3QoUHVyb215Y2luLCBhZXMobG9nMTBjb25jLCByYXRlLCBjb2wgPSBzdGF0ZSkpICsKICBnZW9tX3BvaW50KCkgKwogICMjIExpbmUgZm9yIHRoZSB1bnRyZWF0ZWQgZ3JvdXAKICBnZW9tX2FibGluZSgKICAgICBpbnRlcmNlcHQgPSBjb2VmKG1vZF9hZGQpWzFdLAogICAgIHNsb3BlID0gY29lZihtb2RfYWRkKVsyXSwKICAgICBjb2wgPSAiZGFya29yY2hpZCIKICApICsKICAjIyBMaW5lIGZvciB0aGUgdHJlYXRlZCBncm91cAogIGdlb21fYWJsaW5lKAogICAgIGludGVyY2VwdCA9IGNvZWYobW9kX2FkZClbMV0gKyBjb2VmKG1vZF9hZGQpWzNdLAogICAgIHNsb3BlID0gY29lZihtb2RfYWRkKVsyXSwKICAgICBjb2wgPSAiZm9yZXN0Z3JlZW4iCiAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtvcmNoaWQiLCAiZm9yZXN0Z3JlZW4iKSkgKwogIGdndGl0bGUoIlRoZSBhZGRpdGl2ZSBsaW5lYXIgbW9kZWwgd2l0aCBwYXJhbGxlbCBzbG9wZXMiKQpgYGAKCgojIyMgVGhlIGludGVyYWN0aW9uIG1vZGVsOiAyIG5vbi1wYXJhbGxlbCBzbG9wZXMgey19CgokJApZX2kgPSBcYmV0YV8wICsgXGJldGFfe1x0ZXh0e3JhdGV9fSBYX3tpLCBcdGV4dHtyYXRlfX0gKwogIFxiZXRhX3tcdGV4dHtzdGF0ZX19IFhfe2ksIFx0ZXh0e3N0YXRlfX0gKwogIFxiZXRhX3tcdGV4dHtyYXRlOnN0YXRlfX0gWF97aSwgXHRleHR7cmF0ZX19IFhfe2ksIFx0ZXh0e3N0YXRlfX0gKwogIFxlcHNpbG9uX2kKJCQKCmBgYHtyfQptb2RfaW50IDwtIGxtKHJhdGUgfiBsb2cxMGNvbmMgKiBzdGF0ZSwgZGF0YSA9IFB1cm9teWNpbikKbW9kX2ludAoKZ2dwbG90KFB1cm9teWNpbiwgYWVzKGxvZzEwY29uYywgcmF0ZSwgY29sID0gc3RhdGUpKSArCiAgZ2VvbV9wb2ludCgpICsKICAjIyBMaW5lIGZvciB0aGUgdW50cmVhdGVkIGdyb3VwCiAgZ2VvbV9hYmxpbmUoCiAgICAgaW50ZXJjZXB0ID0gY29lZihtb2RfaW50KVsxXSwKICAgICBzbG9wZSA9IGNvZWYobW9kX2ludClbMl0sCiAgICAgY29sID0gImRhcmtvcmNoaWQiCiAgKSArCiAgIyMgTGluZSBmb3IgdGhlIHRyZWF0ZWQgZ3JvdXAKICBnZW9tX2FibGluZSgKICAgICBpbnRlcmNlcHQgPSBjb2VmKG1vZF9pbnQpWzFdICsgY29lZihtb2RfaW50KVszXSwKICAgICBzbG9wZSA9IGNvZWYobW9kX2ludClbMl0gKyBjb2VmKG1vZF9pbnQpWzRdLAogICAgIGNvbCA9ICJmb3Jlc3RncmVlbiIKICApICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZGFya29yY2hpZCIsICJmb3Jlc3RncmVlbiIpKSArCiAgZ2d0aXRsZSgiVGhlIGxpbmVhciBtb2RlbCB3aXRoIGludGVyYWN0aW9uOiBub24tcGFyYWxsZWwgc2xvcGVzIikKYGBgCgpgYGB7cn0KbW9kMSA8LSBsbShyYXRlIH4gbG9nMTBjb25jICogc3RhdGUsIFB1cm9teWNpbikKc3VtbWFyeShtb2QxKQpgYGAKCkJlZm9yZSB3ZSBwZXJmb3JtIGluZmVyZW5jZSB3ZSB3aWxsIGZpcnN0IGFzc2VzcyB0aGUgYXNzdW1wdGlvbnMKCiMjIEFzc3VtcHRpb25zCgoKMS4gTGluZWFyaXR5IGFuZCByZXByZXNlbnRhdGl2ZSBzYW1wbGUKClxbRVtcaGF0IFxiZXRhX2pdPVxiZXRhX2pcXQoKMi4gTm9ybWFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVzaWR1YWxzCgpcW1lfe2l9IFxzaW0gTihcbXVfaSxcc2lnbWFeMilcXQpcW1lfaSBcc2ltIE4oXGJldGFfMCArIFxiZXRhX2MgeF97Y2l9KyBcYmV0YV9zIHhfe3NpfSArXGJldGFfe2M6c314X3tjaX14X3tzaX0sXHNpZ21hXjIpICBcbG9uZ3JpZ2h0YXJyb3cgXGhhdCBcYmV0YV9qIFxzaW0gTihcYmV0YV9qLCBcc2lnbWFeMl97XGhhdCBcYmV0YV9qfSlcXQoKbGluZWFyIGNvbWJpbmF0aW9ucyBvZiBtb2RlbCBwYXJhbWV0ZXIgZXN0aW1hdG9ycyBhcmUgYWxzbyBub3JtYWxseSBkaXN0cmlidXRlZCwgZS5nLgokJApcbG9uZ3JpZ2h0YXJyb3cgXGhhdCBcYmV0YV9jICsgXGhhdCBcYmV0YV97YzpzfSBcc2ltIE4oXGJldGFfYytcYmV0YV97Y3N9LCBcc2lnbWFeMl97XGhhdFxiZXRhX2MrXGhhdFxiZXRhX3tjOnN9fSkKJCQKJCQKXGxvbmdyaWdodGFycm93IExeVFxoYXQgXGJldGEgXHNpbSBOKExeVFxiZXRhLCBcc2lnbWFeMl97TF5UXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19fSkKJCQKCgoKNC4gSW5kZXBlbmRlbmNlIGFuZCBIb21vc2NlZGFzdGljaXR5CgpXZSBhc3N1bWUgdGhhdCB0aGUgZXhwZXJpbWVudCB3YXMgd2VsbCBkZXNpZ25lZCBhbmQgdGhhdCB0aGUgZGlmZmVyZW50IHJlYWN0aW9ucyB0aGF0IHdlcmUgdXNlIGluIHRoZSBleHBlcmltZW50IGFyZSBpbmRlcGVuZGVudC4KCiQkXHNpZ21hXjJfe0xeVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fX0gPSBjX0wgXHNpZ21hXjIkJAoKCi0gJFxzaWdtYV4yPyQKCiQkXGhhdCBcc2lnbWFeMiA9IE1TRSA9IFxzdW1cbGltaXRzX3tpPTF9IF4gbiBcZnJhY3soWV9pIC0gXGhhdCBZX2kpXjJ9e24tcH0kJAoKJCQKU0Vfe0xeVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fX0gPSBjX0wgXGhhdCBcc2lnbWFeMgokJAoKLSB0LXN0YXRpc3RpYwoKJCQKVCA9IFxmcmFje0xcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gLSBMXGJvbGRzeW1ib2x7XGJldGF9fXtcdGV4dHtTRX1fe0xcaGF0e1xib2xkc3ltYm9se1xiZXRhfX19fQogXHNpbSB0X3tuLXB9CiQkCgotIENJIGFuZCBULXRlc3QgJEhfMDogTFxib2xkc3ltYm9se1xiZXRhfSA9IDAkIHZzICAkSF8xOiBMXGJvbGRzeW1ib2x7XGJldGF9IFxuZXEgMCQKCi0gRiBzdGF0aXN0aWMgZm9sbG93cyBGLWRpc3RyaWJ1dGlvbiB1bmRlciAkSF8wJApcWyBGID0gXGZyYWN7TVNSXzIgLSBNU1JfMX17TVNFfSBcc2ltIEZfe3BfMiAtIHBfMSxuLXBfMn1cXQoKCiMjIyBMaW5lYXJpdHkKCldlIGFzc2VzcyBsaW5lYXJpdHkgaW4gYSByZXNpZHVhbCBhbmFseXNpcwoKYGBge3J9CnBsb3QobW9kMSwgd2hpY2ggPSAxKQpgYGAKClRoZSBhc3N1bXB0aW9uIG9mIGxpbmVhcml0eSBpcyBtZXQuCgojIyMgTm9ybWFsaXR5CgpgYGB7cn0KcGxvdChtb2QxLCB3aGljaCA9IDIpCmBgYAoKVGhlIFFRLXBsb3QgZG9lcyBub3Qgc2hvdyBsYXJnZSBkZXZpYXRpb25zIGZyb20gbm9ybWFsaXR5LgoKIyMjIEhvbW9zY2VkYXN0aWNpdHk6IGVxdWFsaXR5IG9mIHRoZSB2YXJpYW5jZQoKV2UgY2FuIGFnYWluIHVzZSB0aGUgcmVzaWR1YWwgcGxvdCBmb3IgYXNzZXNzaW5nIHRoaXMgYXNzdW1wdGlvbiBvciB0aGUgcGxvdAp3ZXJlIHdlIHBsb3QgdGhlIHNxdWFyZSByb290IG9mIHRoZSBzdGFuZGFyZGl6ZWQgcmVzaWR1YWxzIGluIGZ1bmN0aW9uIG9mCnRoZSBmaXQuCgpgYGB7cn0KcGxvdChtb2QxLCB3aGljaCA9IDEpCnBsb3QobW9kMSwgd2hpY2ggPSAzKQpgYGAKCldlIHNlZSB0aGF0IHRoZSBzcHJlYWQgb2YgdGhlIG1ham9yaXR5IG9mIHRoZSByZXNpZHVhbHMgaXMgbW9yZSBvciBsZXNzIHNpbWlsYXIuCkFzIHN1Y2gsIHdlIG1heSBhc3N1bWUgaG9tb3NjZWRhc3RpY2l0eSBvZiB0aGUgZGF0YS4KCgoKIyMgSW5mZXJlbmNlCgpXZSBmaXJzdCBkbyBhbiBvbW5pYnVzIHRlc3QgdG8gYXNzZXNzIGlzIHRoZXJlIGlzIGFuIGVmZmVjdCBvZiB0aGUgbG9nMTAgY29uY2VudHJhdGlvbiBvbiB0aGUgdmVsb2NpdHkuCgpgYGB7cn0KbW9kMCA8LSBsbShyYXRlIH4gc3RhdGUsIGRhdGEgPSBQdXJvbXljaW4pCmFub3ZhKG1vZDAsIG1vZDEpCmBgYAoKTmV4dCwgd2UgYXNzZXNzIHRoZSBpbnRlcmFjdGlvbi4KCmBgYHtyfQphbm92YShtb2QxKQpgYGAKCldlIGNhbm5vdCByZW1vdmUgdGhlIGludGVyYWN0aW9uIG9mIHRoZSBtb2RlbC4KCkhlbmNlLCB3ZSBjYW5ub3Qgc3R1ZHkgdGhlIGVmZmVjdCBvZiB0aGUgY29uY2VudHJhdGlvbiB3aXRob3V0IGFjY291bnRpbmcgZm9yIHRoZSB0cmVhdG1lbnQgYW5kIGhhdmUgdG8gYXNzZXNzIGZvbGxvd2luZyByZXNlYXJjaCBxdWVzdGlvbnMuCgoxLiB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB2ZWxvY2l0eSBhbmQgdGhlIGNvbmNlbnRyYXRpb24gaXMgc2lnbmlmaWNhbnQgaW4gdGhlIHVudHJlYXRlZCBncm91cAoKJCQKSF8wOiBcYmV0YV9jID0gMCBcdGV4dHsgdnMgfSBIXzE6IFxiZXRhX2MgXG5lcSAwCiQkCgoyLiB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB2ZWxvY2l0eSBhbmQgdGhlIGNvbmNlbnRyYXRpb24gaXMgc2lnbmlmaWNhbnQgaW4gdGhlIHRyZWF0ZWQgZ3JvdXAKCiQkCkhfMDogXGJldGFfYyArIFxiZXRhX3tjOnN9PSAwIFx0ZXh0eyB2cyB9IEhfMTogXGJldGFfYyArIFxiZXRhX3tjOnN9XG5lcSAwCiQkCgozLiB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB2ZWxvY2l0eSBhbmQgdGhlIGNvbmNlbnRyYXRpb24gaXMgZGlmZmVyZW50IGJldHdlZW4gdHJlYXRlZCBhbmQgdW50cmVhdGVkIGdyb3VwCgokJApIXzA6IFxiZXRhX3tjOnN9PSAwXHRleHR7IHZzIH1IXzE6IFxiZXRhX3tjOnN9XG5lcSAwCiQkCgpXZSBjYW4gYXNzZXNzIGFsbCB0aGVzZSBoeXBvdGhlc2VzIHVzaW5nIG11bHRjb21wIHdoaWxlIGNvcnJlY3RpbmcgZm9yIG11bHRpcGxlIHRlc3RpbmcuCgpgYGB7cn0KbGlicmFyeShtdWx0Y29tcCkKbWNwMSA8LSBnbGh0KG1vZDEsCiAgbGluZmN0ID0gYygKICAgICJsb2cxMGNvbmMgPSAwIiwKICAgICJsb2cxMGNvbmMgKyBsb2cxMGNvbmM6c3RhdGV0cmVhdGVkID0gMCIsCiAgICAibG9nMTBjb25jOnN0YXRldHJlYXRlZCA9IDAiCiAgKQopCnN1bW1hcnkobWNwMSkKY29uZmludChtY3AxKQpgYGAKCgojIyBDb25jbHVzaW9uCgpUaGVyZSBpcyBhbiBleHRyZW1lbHkgc2lnbmlmaWNhbnQgZWZmZWN0IG9mIHRoZSBzdWJzdHJhdGUgY29uY2VudHJhdGlvbiBvbiB0aGUgcmVhY3Rpb24gcmF0ZSAocDw8MC4wMDEpLgpUaGUgZWZmZWN0IG9mIHRoZSBzdWJzdHJhdGUgY29uY2VudHJhdGlvbiBvbiB0aGUgcmVhY3Rpb24gcmF0ZSBpcyBleHRyZW1lbHkgc2lnbmlmaWNhbnQgZm9yIHJlYWN0aW9ucyBjYXRhbHlzZWQgd2l0aCB1bnRyZWF0ZWQgZW56eW1lcy4gQSByZWFjdGlvbiBhdCBhIHN1YnN0cmF0ZSBjb25jZW50cmF0aW9uIHRoYXQgaXMgMTAgdGltZXMgaGlnaGVyIHdpbGwgaGF2ZSBhIHJlYWN0aW9uIHNwZWVkIHRoYXQgaXMgb24gYXZlcmFnZSAgYHIgcm91bmQoY29uZmludChtY3AxKSRjb25maW50WzEsMV0sMSlgIGNvdW50cy9taW4gaGlnaGVyICg5NSUgQ0kgW2ByIHJvdW5kKGNvbmZpbnQobWNwMSkkY29uZmludFsxLC0xXSwxKWBdIGNvdW50cy9taW4pIChwIDw8IDAuMDAxKS4KVGhlIGVmZmVjdCBvZiB0aGUgc3Vic3RyYXRlIGNvbmNlbnRyYXRpb24gb24gdGhlIHJlYWN0aW9uIHJhdGUgaXMgZXh0cmVtZWx5IHNpZ25pZmljYW50IGZvciByZWFjdGlvbnMgY2F0YWx5c2VkIHB1cm9teWNpbiB0cmVhdGVkIGVuenltZXMgIChwIDw8IDAuMDAxKS4gQSByZWFjdGlvbiBhdCBhIHN1YnN0cmF0ZSBjb25jZW50cmF0aW9uIHRoYXQgaXMgMTAgdGltZXMgaGlnaGVyIHdpbGwgaGF2ZSBhIHJlYWN0aW9uIHNwZWVkIHRoYXQgaXMgb24gYXZlcmFnZSAgYHIgcm91bmQoY29uZmludChtY3AxKSRjb25maW50WzIsMV0sMSlgIGNvdW50cy9taW4gaGlnaGVyICg5NSUgQ0kgW2ByIHJvdW5kKGNvbmZpbnQobWNwMSkkY29uZmludFsyLC0xXSwxKWBdIGNvdW50cy9taW4pLgpUaGUgZWZmZWN0IG9mIHRoZSBzdWJzdHJhdGUgY29uY2VudHJhdGlvbiBvbiB0aGUgcmVhY3Rpb24gcmF0ZSBpcyB2ZXJ5IHNpZ25pZmljYW50bHkgaGlnaGVyIGZvciByZWFjdGlvbnMgY2F0YWx5c2VkIHdpdGggUHVyb215Y2luIHRyZWF0ZWQgZW56eW1lcyB0aGFuIHdoZW4gY2F0YWx5c2VkIHdpdGggbm9uLXRyZWF0ZWQgZW56eW1lcyAgKHAgPSBgciByb3VuZChzdW1tYXJ5KG1jcDEpJHRlc3QkcHZhbHVlWzNdLDMpYCkuIEEgcmVhY3Rpb24gYXQgYSBzdWJzdHJhdGUgY29uY2VudHJhdGlvbiB0aGF0IGlzIDEwIHRpbWVzIGhpZ2hlciB3aWxsIGhhdmUgYSByZWFjdGlvbiBzcGVlZCB0aGF0IGlzIG9uIGF2ZXJhZ2UgYHIgcm91bmQoY29uZmludChtY3AxKSRjb25maW50WzMsMV0sMSlgIGNvdW50cy9taW4gaGlnaGVyIGZvciByZWFjdGlvbnMgdGhhdCBhcmUgY2F0YWx5c2VkIHdpdGggUHVyb215Y2luIHRyZWF0ZWQgZW56eW1lcyB0aGFuIHdpdGggdW50cmVhdGVkIGVuenltZXMgKDk1JSBDSSBbYHIgcm91bmQoY29uZmludChtY3AxKSRjb25maW50WzMsLTFdLDEpYF0gY291bnRzL21pbikuCgoKIyBBZGRpdGlvbmFsIGNvbmNlcHRzCgojIyBJbnRlcmFjdGlvbiBiZXR3ZWVuIHR3byBjb250aW51b3VzIHByZWRpY3RvcnM/CgpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShtdWx0Y29tcCkKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkoY2FyKQpwcm9zdGF0ZSA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0YXRvbWljcy9zYmMyMC9tYXN0ZXIvZGF0YS9wcm9zdGF0ZS5jc3YiKQpwcm9zdGF0ZSA8LSBwcm9zdGF0ZSAlPiUKICBtdXRhdGUoc3ZpID0gYXMuZmFjdG9yKHN2aSkpCgpsaWJyYXJ5KEdHYWxseSkKcHJvc3RhdGUgJT4lCiAgZHBseXI6OnNlbGVjdCgtcGdnNDUpICAlPiUKICBnZ3BhaXJzKCkKbG1WV1MgPC0gbG0obHBzYX5sY2F2b2wgKyBsd2VpZ2h0ICsgc3ZpLCBwcm9zdGF0ZSkKYGBgCgokJApZX2kgPSBcYmV0YV8wICsgXGJldGFfdiB4X3tpdn0gKyBcYmV0YV93IHhfe2l3fSArXGJldGFfcyB4X3tpc30gKyBcYmV0YV97dnd9IHhfe2l2fXhfe2l3fSArXGVwc2lsb25faQokJAoKLS0tCgpQYXRpZW50IHdpdGggYSAxIHVuaXQgZGlmZmVyZW5jZSBpbiAkWF92JCBoYXZlIGFuIGF2ZXJhZ2UgZGlmZmVyZW5jZSBpbiBscHNhIG9mCgokJApcYmVnaW57YXJyYXl9e2x9CkUoWSB8IFhfdj14X3YgKzEsIFhfdz14X3csIFhfcz14X3MpIC0gRShZIHwgWF92PXhfdiwgWF93PXhfdywgWF9zPXhfcykgXFwKXHF1YWQgPSBcbGVmdFtcYmV0YV8wICsgXGJldGFfdiAoeF97dn0rMSkgKyBcYmV0YV93IHhfdyArXGJldGFfcyB4X3tzfSArIFxiZXRhX3t2d30gKHhfe3Z9KzEpIHhfdyBccmlnaHRdIC0gXGxlZnRbXGJldGFfMCArIFxiZXRhX3YgeF97dn0gKyBcYmV0YV93IHhfdyAgKyBcYmV0YV9zIHhfe3N9ICsgXGJldGFfe3Z3fSAoeF97dn0pIHhfdyBccmlnaHRdXFwKXHF1YWQgPSBcYmV0YV92ICsgIFxiZXRhX3t2d30geF93CiBcZW5ke2FycmF5fQogJCQKCi0gU2xvcGUgZm9yIGxjYXZvbCBkZXBlbmRzIG9uIGxvZyB3ZWlnaHQgb2YgcHJvc3RhdGUhCgotIFdlIGNhbiBkbyB0aGUgc2FtZSBmb3IgdGhlIGx3ZWlnaHQuIFNsb3BlIGZvciBsd2VpZ2h0IGRlcGVuZHMgb24gbGNhdm9sIG9mIHByb3N0YXRlIQoKLS0tCgpgYGB7cn0KbG1WV1NfSW50VlcgPC0gbG0oCiAgbHBzYSB+IGxjYXZvbCArCiAgICBsd2VpZ2h0ICsKICAgIHN2aSArCiAgICBsY2F2b2w6bHdlaWdodCwKICBwcm9zdGF0ZSkKCnN1bW1hcnkobG1WV1NfSW50VlcpCmBgYAoKYGBge3Igb3V0LndpZHRoPScxMDAlJywgZmlnLmFzcD0uOCwgZmlnLmFsaWduPSdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLGVjaG89RkFMU0V9CnBhcihtZnJvdz1jKDEsMikpCmxpYnJhcnkocGxvdDNEKQpncmlkLmxpbmVzID0gMTAKeDwtcHJvc3RhdGUkbGNhdm9sCnk8LXByb3N0YXRlJGx3ZWlnaHQKejwtcHJvc3RhdGUkbHBzYQpmaXQ8LWxtKHp+eCt5K3N2aSxkYXRhPXByb3N0YXRlKQp4LnByZWQgPC0gc2VxKG1pbih4KSwgbWF4KHgpLCBsZW5ndGgub3V0ID0gZ3JpZC5saW5lcykKeS5wcmVkIDwtIHNlcShtaW4oeSksIG1heCh5KSwgbGVuZ3RoLm91dCA9IGdyaWQubGluZXMpCgojIGZpdHRlZCBwb2ludHMgZm9yIGRyb3BsaW5lcyB0byBzdXJmYWNlCnRoPS0yNQpwaD01CnNjYXR0ZXIzRCh4LCB5LCB6LCBwY2ggPSAxNixjb2w9YygiZGFya2JsdWUiLCJyZWQiKVthcy5kb3VibGUocHJvc3RhdGUkc3ZpKV0sIGNleCA9IC43NSwKICAgIHRoZXRhID0gdGgsIHBoaSA9IHBoLCB0aWNrdHlwZSA9ICJkZXRhaWxlZCIsCiAgICB4bGFiID0gImxjYXZvbCIsIHlsYWIgPSAibHdlaWdodCIsIHpsYWIgPSAibHBzYSIsCiAgIGNvbHZhcj1GQUxTRSxidHkgPSAiZyIsbWFpbj0iQWRkaXRpdmUgbW9kZWwiKQoKZm9yIChpIGluIHdoaWNoKHByb3N0YXRlJHN2aT09ImhlYWx0aHkiKSkKbGluZXMzRCh4PXJlcChwcm9zdGF0ZSRsY2F2b2xbaV0sMikseT1yZXAocHJvc3RhdGUkbHdlaWdodFtpXSwyKSx6PWMocHJvc3RhdGUkbHBzYVtpXSxsbVZXUyRmaXRbaV0pLGNvbD1jKCJkYXJrYmx1ZSIsInJlZCIpW2FzLmRvdWJsZShwcm9zdGF0ZSRzdmkpW2ldXSxhZGQ9VFJVRSxsdHk9MikKCnoucHJlZDNEIDwtIG91dGVyKHgucHJlZCwgeS5wcmVkLCBmdW5jdGlvbih4LHkpIHtsbVZXUyRjb2VmWzFdK2xtVldTJGNvZWZbMl0qeCtsbVZXUyRjb2VmWzNdKnl9KQp4LnByZWQzRCA8LSBvdXRlcih4LnByZWQseS5wcmVkLGZ1bmN0aW9uKHgseSkgeCkKeS5wcmVkM0QgPC0gb3V0ZXIoeC5wcmVkLHkucHJlZCxmdW5jdGlvbih4LHkpIHkpCnN1cmYzRCh4LnByZWQzRCx5LnByZWQzRCx6LnByZWQzRCxjb2w9ImJsdWUiLGZhY2V0cz1OQSxhZGQ9VFJVRSkKCgpzY2F0dGVyM0QoeCwgeSwgeiwgcGNoID0gMTYsY29sPWMoImRhcmtibHVlIiwicmVkIilbYXMuZG91YmxlKHByb3N0YXRlJHN2aSldLCBjZXggPSAuNzUsCiAgICB0aGV0YSA9IHRoLCBwaGkgPSBwaCwgdGlja3R5cGUgPSAiZGV0YWlsZWQiLAogICAgeGxhYiA9ICJsY2F2b2wiLCB5bGFiID0gImx3ZWlnaHQiLCB6bGFiID0gImxwc2EiLAogICBjb2x2YXI9RkFMU0UsYnR5ID0gImciLG1haW49Ik1vZGVsIG1ldCBsY2F2b2w6bHdlaWdodCBpbnRlcmFjdGllIikKCmZvciAoaSBpbiB3aGljaChwcm9zdGF0ZSRzdmk9PSJoZWFsdGh5IikpCmxpbmVzM0QoeD1yZXAocHJvc3RhdGUkbGNhdm9sW2ldLDIpLHk9cmVwKHByb3N0YXRlJGx3ZWlnaHRbaV0sMiksej1jKHByb3N0YXRlJGxwc2FbaV0sbG1WV1NfSW50VlckZml0W2ldKSxjb2w9YygiZGFya2JsdWUiLCJyZWQiKVthcy5kb3VibGUocHJvc3RhdGUkc3ZpKVtpXV0sYWRkPVRSVUUsbHR5PTIpCgp6LnByZWQzRCA8LSBvdXRlcih4LnByZWQsIHkucHJlZCwgZnVuY3Rpb24oeCx5KSB7bG1WV1NfSW50VlckY29lZlsxXStsbVZXU19JbnRWVyRjb2VmWzJdKngrbG1WV1NfSW50VlckY29lZlszXSp5K2xtVldTX0ludFZXJGNvZWZbNV0qeCp5fSkKeC5wcmVkM0QgPC0gb3V0ZXIoeC5wcmVkLHkucHJlZCxmdW5jdGlvbih4LHkpIHgpCnkucHJlZDNEIDwtIG91dGVyKHgucHJlZCx5LnByZWQsZnVuY3Rpb24oeCx5KSB5KQpzdXJmM0QoeC5wcmVkM0QseS5wcmVkM0Qsei5wcmVkM0QsY29sPSJibHVlIixmYWNldHM9TkEsYWRkPVRSVUUpCmBgYAoKIyMgSW50ZXJhY3Rpb24gYmV0d2VlbiB0d28gZmFjdG9ycz8KCmBgYHtyfQprcG5hMiA8LSByZWFkX3RzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0YXRPbWljcy9TR0EyMS9tYXN0ZXIvZGF0YS9rcG5hMi50eHQiKQprcG5hMiA8LSBrcG5hMiAlPiUKICBtdXRhdGUoCiAgICBncmFkZSA9IGFzLmZhY3RvcihncmFkZSksCiAgICBub2RlID0gYXMuZmFjdG9yKG5vZGUpLAogICAgbm9kZSA9IGZjdF9yZWNvZGUobm9kZSwgVW5hZmZlY3RlZCA9ICIwIiwgUmVtb3ZlZCA9ICIxIiksCiAgICBsb2cyZ2VuZSA9IGxvZzIoZ2VuZSkKICApCmBgYAoKYGBge3J9CmdncGxvdChrcG5hMiwgYWVzKHggPSBncmFkZSwgeSA9IGdlbmUsIGZpbGwgPSBub2RlKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2UoKSwgc2hhcGUgPSAyMSwgc2l6ZSA9IDIpICsKICBsYWJzKAogICAgeCA9ICJIaXN0b2xvZ2ljIGdyYWRlIiwgeSA9ICJLUE5BMiBleHByZXNzaW9uIiwKICAgIGZpbGwgPSAiTHltcGggbm9kZSBzdGF0dXMiCiAgKSArCiAgZ2d0aXRsZSgiS1BOQTIgZ2VuZSBleHByZXNzaW9uIGluIGJyZWFzdCBjYW5jZXIgcGF0aWVudHMiKQoKZ2dwbG90KGtwbmEyLCBhZXMoeCA9IGdyYWRlLCB5ID0gbG9nMmdlbmUsIGZpbGwgPSBub2RlKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2UoKSwgc2hhcGUgPSAyMSwgc2l6ZSA9IDIpICsKICBsYWJzKAogICAgeCA9ICJIaXN0b2xvZ2ljIGdyYWRlIiwgeSA9ICJLUE5BMiBsb2cyIGV4cHJlc3Npb24iLAogICAgZmlsbCA9ICJMeW1waCBub2RlIHN0YXR1cyIKICApICsKICBnZ3RpdGxlKCJLUE5BMiBnZW5lIGV4cHJlc3Npb24gaW4gYnJlYXN0IGNhbmNlciBwYXRpZW50cyIpCmBgYAoKYGBge3J9CmZpdCA8LSBsbShsb2cyKGdlbmUpIH4gZ3JhZGUgKiBub2RlLCBkYXRhID0ga3BuYTIpCkV4cGxvcmVNb2RlbE1hdHJpeDo6VmlzdWFsaXplRGVzaWduKGtwbmEyLCB+IGdyYWRlICogbm9kZSkkcGxvdGxpc3RbWzFdXQpgYGAK