Creative Commons License

1 Prostacyclin Example

Researchers study the effect of arachidonic acid on prostacyclin level in blood plasma. They use 3 different concentrations of arachidonic acid:

  • low,
  • medium and
  • high dose

Each treatment is adopted to 12 rats. They measure the prostacyclin levels in blood plasma using an elisa fluorescence measurement.

prostacyclin <- read_tsv("https://raw.githubusercontent.com/GTPB/PSLS20/master/data/prostacyclin.txt")
prostacyclin$dose <- as.factor(prostacyclin$dose)
head(prostacyclin)

##Data exploration

prostacyclin %>% 
  ggplot(aes(x=dose,y=prostac,fill=dose)) +
  geom_boxplot() +
  geom_point(position="jitter") +
  ylab("prostacyclin (ng/ml)")

prostacyclin %>% 
  ggplot(aes(sample=prostac)) +
  geom_qq() +
  geom_qq_line() + 
  facet_grid(~dose)

The data in the three groups is approximately Normally distributed with equal variance: \[Y_i \vert \text{group j} \sim N(\mu_j,\sigma^2),\] with \(j= \text{1, 2, 3}\)

1.1 Research Question

Research question can translated in the following hypotheses

  • \(H_0\): the arachidonic acid concentration has no effect on the mean prostacyclin level in blood plasma in rats \[ H_0:\mu_1=\mu_2 = \mu_3 \]

  • \(H_1\): the arachidonic acid concentration has an effect on the mean prostacyclin level in blood plasma in rats, which implies that the at least two means are different.

In terms of the model parameters this becomes

\[ H_0:\mu_1=\mu_2 = \mu_3 \] and \[H_1: \exists\ j,k \in \{1,\ldots,g\} : \mu_j\neq\mu_k\]

Alternative approach: split null hypothesis in partial hypotheses: \[ H_{0jk}: \mu_j=\mu_k \text{ versus } H_{1jk}: \mu_j \neq \mu_k \]

Each hypothesis can be tested via two-sample t-tests \(\rightarrow\) multiple testing problem + loss of power.

\(\rightarrow\) assess \(H_0:\mu_1=\mu_2=\mu_3\) with a single test.

2 Analyse of Variance

Correct solution for testing problem: ANalysis Of VAriance (ANOVA)

  • We develop the method for 3 groups (prostacyclin example)
  • Model data with linear model by using dummy variables.
  • 1 dummy variable less than the number of groups. Here we need 2 dummy variables.
  • Generalizing to g groups \(g>3\) is trivial (extra dummy variables)

2.0.1 Model

\[\begin{eqnarray} Y_i &=& g(x_{i1},x_{i2}) + \epsilon_i\\ Y_i &=& \beta_0+\beta_1 x_{i1} +\beta_2 x_{i2} +\epsilon_i \end{eqnarray}\]

  • \(Y_i\) de outcome for observation \(i\) (\(i=1,\ldots, n\))
  • \(\epsilon_i\text{i.i.d.} N(0,\sigma^2)\)
  • and dummy variables \[x_{i1} = \left\{ \begin{array}{ll} 1 & \text{ if observation $i$ belongs to middle dose group (M)} \\ 0 & \text{ if observation $i$ belongs to other dose group} \end{array}\right.\] \[x_{i2} = \left\{ \begin{array}{ll} 1 & \text{ if observation $i$ belongs to high dose group (H)} \\ 0 & \text{if observation $i$ belongs to other dose group} \end{array}\right. .\]
  • Low dose group (L) with \(x_{i1}=x_{i2}=0\) is reference group

Regression-model can be rewritten as a model for each group : \[\begin{eqnarray*} Y_{i\vert \text{dose=L}} &=& \beta_0+\epsilon_i \\ Y_{i\vert \text{dose=M}} &=& \beta_0+\beta_1+ \epsilon_i \\ Y_{i\vert \text{dose=H}} &=& \beta_0+\beta_2 + \epsilon_i \end{eqnarray*}\] with \(\epsilon_i \sim N(0,\sigma^2)\)

Interpretation of model parameters: \[\begin{eqnarray*} \beta_0 &=& \text{E}\left[Y_i \mid \text{treatment with low dose group L}\right] \\ \beta_1 &=& (\beta_0+\beta_1)-\beta_0 = \text{E}\left[Y_i \mid \text{treatment M}\right] - \text{E}\left[Y_i \mid \text{treatment L}\right] \\ \beta_2 &=& (\beta_0+\beta_2)-\beta_0 = \text{E}\left[Y_i \mid \text{treatment H}\right]-\text{E}\left[Y_i \mid \text{treatment L}\right]. \end{eqnarray*}\]

  1. \(\beta_0\) is the mean outcome for group L
  2. \(\beta_1\) is effect (difference in mean concentration) of group M vs group L
  3. \(\beta_2\) is effect of group H vs group L

We reformulate the model by using \(\mu\)-notations: \[\begin{eqnarray*} Y_{i\vert \text{dose=L}} &=& \beta_0+\epsilon_i = \mu_1+\epsilon_i \\ Y_{i\vert \text{dose=M}} &=& \beta_0+\beta_1+ \epsilon_i = \mu_2+\epsilon_i \\ Y_{i\vert \text{dose=H}} &=& \beta_0+\beta_2 + \epsilon_i = \mu_3+\epsilon_i . \end{eqnarray*}\] with \(\epsilon_i \sim N(0,\sigma^2)\) and \[ \mu_j = \text{E}\left[Y_i \mid \text{treatment group } j\right].\]

Original null hypothese \[H_0:\mu_1=\mu_2=\mu_3\] can be formulated as \[H_0: \beta_1=\beta_2=0.\]

Model allows us to use all methods from linear regression.

  • Parameter estimators for means, variances and standard errors
  • Inference: Confidence intervals, hypothesis tests
    • Test \(H_0: \beta_1=\beta_2=0\) with \(F\)-test.

2.1 Prostacyclin example

model1 <- lm(prostac~dose,data=prostacyclin)
summary(model1)

Call:
lm(formula = prostac ~ dose, data = prostacyclin)

Residuals:
    Min      1Q  Median      3Q     Max 
-35.167 -17.117  -4.958  17.927  41.133 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   40.108      6.150   6.521 2.10e-07 ***
dose25         8.258      8.698   0.949    0.349    
dose50        43.258      8.698   4.974 1.99e-05 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 21.3 on 33 degrees of freedom
Multiple R-squared:  0.458, Adjusted R-squared:  0.4252 
F-statistic: 13.94 on 2 and 33 DF,  p-value: 4.081e-05

3 Sum of squares and Anova

Similar to simple linear regression we will use sum of squares to derive the F-test. \[\begin{eqnarray*} \text{SSR}&=&\sum\limits_{i=1}^n (\hat Y_i -\bar Y)^2\\ &=& \sum\limits_{i=1}^n (\hat{g} (x_{i1},x_{i2}) - \bar Y)^2\\ &=& \sum\limits_{i=1}^n (\hat\beta_0+\hat\beta_1x_{i1}+\hat\beta_2x_{i2}) - \bar Y)^2\\ &=& \sum\limits_{i=1}^{n_1} (\hat\beta_0 - \bar Y)^2 +\sum\limits_{i=1}^{n_2} (\hat\beta_0 + \hat\beta_1 - \bar Y)^2+\sum\limits_{i=1}^{n_3} (\hat\beta_0 + \hat\beta_2 - \bar Y)^2\\ &=& \sum\limits_{i=1}^{n_1} (\bar Y_1- \bar Y)^2 +\sum\limits_{i=1}^{n_2} (\bar Y_2- \bar Y)^2+\sum\limits_{i=1}^{n_3} (\bar Y_3 - \bar Y)^2\\ \end{eqnarray*}\] with \(n_1\), \(n_2\) en \(n_3\) the number of observations in each group (here \(n-1=n_2=n_3=12\)).

\[\begin{eqnarray*} \text{SSR}&=&\sum\limits_{i=1}^n (\hat Y_i -\bar Y)^2 \end{eqnarray*}\]

  • Sum of squares is again equivalent with comparison of model (1) and reduced model with an intercept, only.

  • For reduced model the intercept is estimated by the sample mean.

  • This sum of squares has g-1=2 degrees of freedom:

    • g=3 model parameters - 1 parameter to estimate overall sample mean or
    • g=3 par. in complex model - 1 par. in reduced model.

3.1 Decomposition of Total Sum of Squares

  • The convention in the Anova setting is to denote the sum of squares as SST, the Sum of Squares of the Treatment (treatment) or as SSBetween.
  • The sum of squares of the regression indeed reflects the variability between the groups.
  • The corresponding mean sum of squares becomes \(\text{MST}=\text{SST}/2\).

The decomposition of SSTot can be written as \[ \text{SSTot} = \text{SST} + \text{SSE} \]

##SSTot

##SST

##SSE

3.2 Anova test

Test \(H_0: \beta_1=\beta_2=0\) with \(F\)-test. \[ F = \frac{\text{MST}}{\text{MSE}} \]

with

  • \(\text{MST}=\text{SST}/(g-1)\)
  • \(\text{MSE}=\text{SSE}/(n-p)\)
  • Test statistic compares the variability explained by model (MST) with the residual variability (MSE)

or

  • Variability between groups (MST) to variability within groups (MSE)
  • Under \(H_0\): \(F \sim F_{g-1,n-g}\), with g=3.

3.3 Anova Table

Df Sum Sq Mean Sq F value Pr(>F)
Treatment d.f. SST SST MST F-statistiek p-waarde
Error d.f. SSE SSE MSE
anova(model1)

3.3.1 F-distribution with critical value (\(\alpha\)=5%) and observed F-statistic for prostacyclin example

3.3.2 F-distributions with different number of degrees of freedom in the nominator and denominator

###Prostacyclin example: which groups are different?

summary(model1)

Call:
lm(formula = prostac ~ dose, data = prostacyclin)

Residuals:
    Min      1Q  Median      3Q     Max 
-35.167 -17.117  -4.958  17.927  41.133 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   40.108      6.150   6.521 2.10e-07 ***
dose25         8.258      8.698   0.949    0.349    
dose50        43.258      8.698   4.974 1.99e-05 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 21.3 on 33 degrees of freedom
Multiple R-squared:  0.458, Adjusted R-squared:  0.4252 
F-statistic: 13.94 on 2 and 33 DF,  p-value: 4.081e-05

With model output we can assess if the mean prostacyclin concentration differs between middle and low dose group (\(\beta_1\): dose25), and, between high and low dose group (\(\beta_2\): dose50).

The p-values do not account for multiple testing.

4 Post hoc analysis: Multiple comparisons of means

4.1 Naive method

In the first part we developed the \(F\)-test to assess

\[ H_0: \mu_1=\cdots = \mu_g \text{ versus } H_1: H_1: \exists\ j,k \in \{1,\ldots,g\} : \mu_j\neq\mu_k\]

  • If we reject \(H_0\) we conclude that at least two means are different
  • The method does not allow to identify which means are different.

A first naive method is to split \(H_0\) in partial hypotheses \[H_{0jk}: \mu_j=\mu_k \text{ versus } H_{1jk}: \mu_j \neq \mu_k\]

  • Falsify partial null hypotheses with two-sample \(t\)-testen

  • Comparison of group \(j\) with group \(k\) with two-sample \(t\)-test under the equality of means: \[T_{jk} = \frac{\bar{Y}_j-\bar{Y}_k}{S_p\sqrt{\frac{1}{n_j}+\frac{1}{n_k}}} \sim t_{n-2}\]

With

  • \(S_p^2\) the pooled variance estimator, \[S_p^2 = \frac{(n_j-1)S_j^2 + (n_k-1)S_k^2}{n_j+n_k-2}\]

  • with \(S_j^2\) and \(S_k^2\) the sample variances of group \(j\) en \(k\), respectively.

In ANOVA context we assume that the variance of all \(g\) groups is equal, i.e. the residual variance \(\sigma^2\).

  • Use of \(S_p^2\) is not efficient because it does not make use of all data

  • We can gain efficiency by using MSE \[\text{MSE}= \sum_{j=1}^g \frac{(n_j-1)S_j^2}{n-g}\]

  • The \(t\)-tests are thus best based on \[T_{jk} = \frac{\bar{Y}_j-\bar{Y}_k}{\text{MSE}\sqrt{\frac{1}{n_j}+\frac{1}{n_k}}} \sim t_{n-g}.\]


with(prostacyclin,pairwise.t.test(prostac,dose,"none"))

    Pairwise comparisons using t tests with pooled SD 

data:  prostac and dose 

   10      25     
25 0.34927 -      
50 2e-05   0.00031

P value adjustment method: none 

When we perform \(m\)-tests on the \(\alpha\) significance level we cannot correctly control the type I error.

4.2 We show with simulation that naive method does not work

  1. We simulate from an ANOVA model with \(g=3\) groups.
  2. The means in the ANOVA model are equal to each other, so that \[H_0: \mu_1=\mu_2=\mu_3\].
  3. For each simulated dataset we conduct \(m=3\) pairwise two-sample \(t\)-test
  4. As soon as one of the \(p\)-values is below significance level \(\alpha=5\%\), we reject \(H_0: \mu_1=\mu_2=\mu_3\) because two means are different according to the \(t\)-tests.
  5. We rapport the relative frequency of rejection of the global null hypothesis, i.e. the probability on a type I error \(H_0: \mu_1=\mu_2=\mu_3\).
g<-3 # number of treatments (g=3)
ni<-12 # number of observations in each group
n<-g*ni # total number of observation
alpha<-0.05 # significance level of individual test 
N=10000 # number of simulations
set.seed(302) #seed to reproduce results exactly 
trt=factor(rep(1:g,ni)) #factor
cnt<-0 #counter for erroneous rejections
for(i in 1:N) {
#if (i%%1000==0) cat(i,"/",N,"\n")
y <- rnorm(n)
tests<-pairwise.t.test(y,trt,"none")
reject<-min(tests$p.value,na.rm=T)<alpha
if(reject) cnt<-cnt+1
}
cnt/N
[1] 0.1209

  • Probability on the type I error equals 12.1%

  • It is more then twice \(\alpha=5\)%.

  • If we repeat the simulation with g = 5 groups (i.e. 10 pairwise t-tests) we find a type I error of 28.0% instead of the desired 5%.

  • The simulation study illustrates the multiplicity problem

    • Classical p-values can only be compared with the significance level \(\alpha\), if the conclusion is based on a single p-value.
    • Here the final decision is based on \(m=g\times(g-1)/2\) \(p\)-values.
  • We first discuss on the extension of the concept of type I errors and then introduce some solutions

4.3 Family-wise error rate

  • When \(m>1\) tests are used to make one decision it is necessary to correct for the risk on false positive results (type I errors).

  • Most procedures for multiple testing assume that all \(m\) null hypotheses are true.

  • So one tries to control the risk on at least 1 false positive on the family wise error rate (FWER) \(\alpha_F\), typical \(\alpha_F=0.05\).

4.4 Bonferroni correction

When we conduct \(m\) independent test each on the significance level \(\alpha\), then \[\begin{eqnarray*} \alpha_F&=&\text{P}[\text{at least 1 Type I fout}]\\ &=&1-(1-\alpha)^m \leq m\alpha \end{eqnarray*}\]

  • If we assess 5 tests on the 5% significance level then the FWER \(\approx 25\%\). {10pt}

  • By conducting them at the 1% significance level the FWER \(\approx 5\%\).

  • The Bonferroni correction controls the FWER on \(\alpha_F\) by setting \[\alpha=\alpha_F/m\] for each of the \(m\) pairwise comparisons

An alternative approach is to report

  1. adjusted p-values that can be compared to the FWER \(\alpha_F\) level: \[\tilde{p}=min(m\times p,1)\]
  2. and \((1-\alpha_F/m)100\%\) confidence intervals.

4.4.1 prostacyclin example

with(prostacyclin,pairwise.t.test(prostac,dose, data = prostacyclin, p.adjust.method="bonferroni"))

    Pairwise comparisons using t tests with pooled SD 

data:  prostac and dose 

   10      25     
25 1.00000 -      
50 6e-05   0.00094

P value adjustment method: bonferroni 
  • The conclusions remain similar, except that the FWER is now controlled at \(\alpha_F=5\%\) and that the \(\tilde{p}\)-values are larger with a factor 3.

The same analysis can be conducted in the multcomp R package that is developed for multiple testing in linear models.

library(multcomp)
model1.mcp<-glht(model1,linfct=mcp(dose="Tukey"))
summary(model1.mcp,test=adjusted("bonferroni"))

     Simultaneous Tests for General Linear Hypotheses

Multiple Comparisons of Means: Tukey Contrasts


Fit: lm(formula = prostac ~ dose, data = prostacyclin)

Linear Hypotheses:
             Estimate Std. Error t value Pr(>|t|)    
25 - 10 == 0    8.258      8.698   0.949 1.000000    
50 - 10 == 0   43.258      8.698   4.974 5.98e-05 ***
50 - 25 == 0   35.000      8.698   4.024 0.000943 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Adjusted p values reported -- bonferroni method)

Note, that the user has to define custum functions to obtain Bonferonni adjusted confidence intervals.

  • Bonferonni confidence intervals are not implemented because better methods exist for multiple testing.

  • The function below is added here merely for completeness, but we will generally use the default method for multiple testing in multcomp.

calpha_bon_t<-function(object,level)
 abs(
   qt(
     (1-level)/2/nrow(object$linfct),
     object$df
     )
    )
confint(model1.mcp,calpha=calpha_bon_t)

     Simultaneous Confidence Intervals

Multiple Comparisons of Means: Tukey Contrasts


Fit: lm(formula = prostac ~ dose, data = prostacyclin)

Quantile = 2.5222
95% confidence level
 

Linear Hypotheses:
             Estimate lwr      upr     
25 - 10 == 0   8.2583 -13.6790  30.1957
50 - 10 == 0  43.2583  21.3210  65.1957
50 - 25 == 0  35.0000  13.0626  56.9374

4.4.2 Evaluate Bonferroni method via simulation

g<-3 # number of treatments (g=3)
ni<-12 # number of observations in each group
n<-g*ni # totaal number observation
alpha<-0.05 # significance level of individual test 
N=10000 # number of simulaties
set.seed(302) #seed to reproduce results exactly 
trt=factor(rep(1:g,ni)) #factor
cnt<-0 #counter for erroneous rejections
for(i in 1:N) {
#if (i%%1000==0) cat(i,"/",N,"\n")
y <- rnorm(n)
tests<-pairwise.t.test(y,trt,"bonferroni")
reject<-min(tests$p.value,na.rm=T)<alpha
if(reject) cnt<-cnt+1
}
cnt/N
[1] 0.0457
  • We find an FWER of 4.6%, which is slightly conservative.

  • For simulations of \(g=5\) group the FWER is \(4.1\%\) (more conservative).

  • By using Bonferroni the probability on at least one false positive result is lower than \(< \alpha_F\).

  • Power loss because the real FWER is smaller than 5%

4.5 Tukey Method

  • Less conservatieve

  • Implementation approximates the null distribution of posthoc tests via simulations

  • Results can change slightly if the posthoc analysis is repeated

  • Details on the method falls outside the scope of the short course

  • Is the default method in the multcomp package:

    • adjusted p-values
    • adjusted confidence intervals

4.5.1 Captopril example

model1.mcp<-glht(model1,linfct=mcp(dose="Tukey"))
summary(model1.mcp)

     Simultaneous Tests for General Linear Hypotheses

Multiple Comparisons of Means: Tukey Contrasts


Fit: lm(formula = prostac ~ dose, data = prostacyclin)

Linear Hypotheses:
             Estimate Std. Error t value Pr(>|t|)    
25 - 10 == 0    8.258      8.698   0.949 0.613390    
50 - 10 == 0   43.258      8.698   4.974  < 1e-04 ***
50 - 25 == 0   35.000      8.698   4.024 0.000835 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Adjusted p values reported -- single-step method)
confint(model1.mcp)

     Simultaneous Confidence Intervals

Multiple Comparisons of Means: Tukey Contrasts


Fit: lm(formula = prostac ~ dose, data = prostacyclin)

Quantile = 2.4539
95% family-wise confidence level
 

Linear Hypotheses:
             Estimate lwr      upr     
25 - 10 == 0   8.2583 -13.0849  29.6016
50 - 10 == 0  43.2583  21.9151  64.6016
50 - 25 == 0  35.0000  13.6567  56.3433
plot(confint(model1.mcp))

###Evaluate Tukey method

g<-3 # number of treatments (g=3)
ni<-12 # number of observations in each group
n<-g*ni # totaal number observation
alpha<-0.05 # significance level of individual test 
N <- 10000 # number of simulations
set.seed(302) #seed to reproduce results exactly 
trt <- factor(rep(1:g,ni)) #factor
cnt<-0 #counter for erroneous rejections
for(i in 1:N) {
#if (i%%1000==0) cat(i,"/",N,"\n")
y <- rnorm(n)
m<-lm(y~trt)
m.mcp<-glht(m,linfct=mcp(trt="Tukey"))
tests<-summary(m.mcp)$test
reject<-min(as.numeric(tests$pvalues),na.rm=T)<alpha
if(reject) cnt<-cnt+1
}
cnt/N
[1] 0.0503

5 Conclusions: Prostacyclin example

Entire analysis for prostacyclin example

  1. Anova before posthoc tests: F-test has a higher power than pairwise t-test

    • F-test uses all data
    • For F-test we do not need to correct for multiple testing: one test is conducted for the general omnibus hypothesis
model1 <- lm(prostac~dose,data=prostacyclin)
anova(model1)
model1.mcp<-glht(model1,linfct=mcp(dose="Tukey"))
summary(model1.mcp)

     Simultaneous Tests for General Linear Hypotheses

Multiple Comparisons of Means: Tukey Contrasts


Fit: lm(formula = prostac ~ dose, data = prostacyclin)

Linear Hypotheses:
             Estimate Std. Error t value Pr(>|t|)    
25 - 10 == 0    8.258      8.698   0.949 0.613433    
50 - 10 == 0   43.258      8.698   4.974  < 1e-04 ***
50 - 25 == 0   35.000      8.698   4.024 0.000922 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Adjusted p values reported -- single-step method)
confint(model1.mcp)

     Simultaneous Confidence Intervals

Multiple Comparisons of Means: Tukey Contrasts


Fit: lm(formula = prostac ~ dose, data = prostacyclin)

Quantile = 2.4526
95% family-wise confidence level
 

Linear Hypotheses:
             Estimate lwr      upr     
25 - 10 == 0   8.2583 -13.0736  29.5902
50 - 10 == 0  43.2583  21.9264  64.5902
50 - 25 == 0  35.0000  13.6681  56.3319
  • There is an extreme significant effect of arachidonic acid on the average prostacyclin blood concentration in rats (\(p<0.001\)). The average prostacyclin concentration is higher in the high dose group than in the low and moderate dose group (both p-values are smaller than \(p<0.001\)).
  • The average concentration in the high dose group is 43.3ng/ml (95% CI [21.9,64.6]ng/ml) and 35ng/ml (95% BI [13.6,56.4]ng/ml) higher than in the low and middle dose group, respectively.
  • The difference in average prostacyclin concentration between the moderate and low dose group is not significant (p=0.61). (All p-values and confidence intervals for post-hoc tests are corrected for multiple testing using the Tukey method).
LS0tCnRpdGxlOiAiNy4gQW5hbHlzaXMgb2YgVmFyaWFuY2UiIAphdXRob3I6ICJMaWV2ZW4gQ2xlbWVudCIKZGF0ZTogInN0YXRPbWljcywgR2hlbnQgVW5pdmVyc2l0eSAoaHR0cHM6Ly9zdGF0b21pY3MuZ2l0aHViLmlvKSIKb3V0cHV0OgogICAgaHRtbF9kb2N1bWVudDoKICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZSAgICAKICAgICAgdGhlbWU6IGNvc21vCiAgICAgIHRvYzogdHJ1ZQogICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKLS0tCgo8YSByZWw9ImxpY2Vuc2UiIGhyZWY9Imh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1zYS80LjAiPjxpbWcgYWx0PSJDcmVhdGl2ZSBDb21tb25zIExpY2Vuc2UiIHN0eWxlPSJib3JkZXItd2lkdGg6MCIgc3JjPSJodHRwczovL2kuY3JlYXRpdmVjb21tb25zLm9yZy9sL2J5LW5jLXNhLzQuMC84OHgzMS5wbmciIC8+PC9hPgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChpbmNsdWRlID0gVFJVRSwgY29tbWVudCA9IE5BLCBlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQpsaWJyYXJ5KFJtaXNjKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgoKIyBQcm9zdGFjeWNsaW4gRXhhbXBsZQoKUmVzZWFyY2hlcnMgc3R1ZHkgdGhlIGVmZmVjdCBvZiBhcmFjaGlkb25pYyBhY2lkIG9uIHByb3N0YWN5Y2xpbiBsZXZlbCBpbiBibG9vZCBwbGFzbWEuIFRoZXkgdXNlIDMgZGlmZmVyZW50IGNvbmNlbnRyYXRpb25zIG9mIGFyYWNoaWRvbmljIGFjaWQ6IAoKLSBsb3csIAotIG1lZGl1bSBhbmQgCi0gaGlnaCBkb3NlCgpFYWNoIHRyZWF0bWVudCBpcyBhZG9wdGVkIHRvIDEyIHJhdHMuIFRoZXkgbWVhc3VyZSB0aGUgcHJvc3RhY3ljbGluIGxldmVscyBpbiBibG9vZCBwbGFzbWEgdXNpbmcgYW4gZWxpc2EgZmx1b3Jlc2NlbmNlIG1lYXN1cmVtZW50LgoKCmBgYHtyfQpwcm9zdGFjeWNsaW4gPC0gcmVhZF90c3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9HVFBCL1BTTFMyMC9tYXN0ZXIvZGF0YS9wcm9zdGFjeWNsaW4udHh0IikKcHJvc3RhY3ljbGluJGRvc2UgPC0gYXMuZmFjdG9yKHByb3N0YWN5Y2xpbiRkb3NlKQpoZWFkKHByb3N0YWN5Y2xpbikKYGBgCgotLS0KCiMjRGF0YSBleHBsb3JhdGlvbgoKYGBge3J9CnByb3N0YWN5Y2xpbiAlPiUgCiAgZ2dwbG90KGFlcyh4PWRvc2UseT1wcm9zdGFjLGZpbGw9ZG9zZSkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbj0iaml0dGVyIikgKwogIHlsYWIoInByb3N0YWN5Y2xpbiAobmcvbWwpIikKICAKcHJvc3RhY3ljbGluICU+JSAKICBnZ3Bsb3QoYWVzKHNhbXBsZT1wcm9zdGFjKSkgKwogIGdlb21fcXEoKSArCiAgZ2VvbV9xcV9saW5lKCkgKyAKICBmYWNldF9ncmlkKH5kb3NlKQpgYGAKCgpUaGUgZGF0YSBpbiB0aGUgdGhyZWUgZ3JvdXBzIGlzIGFwcHJveGltYXRlbHkgTm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aCBlcXVhbCB2YXJpYW5jZToKXFtZX2kgXHZlcnQgXHRleHR7Z3JvdXAgan0gXHNpbSBOKFxtdV9qLFxzaWdtYV4yKSxcXQp3aXRoICRqPSBcdGV4dHsxLCAyLCAzfSQKCiMjIFJlc2VhcmNoIFF1ZXN0aW9uCgpSZXNlYXJjaCBxdWVzdGlvbiBjYW4gdHJhbnNsYXRlZCBpbiB0aGUgZm9sbG93aW5nIGh5cG90aGVzZXMgCgotICRIXzAkOiB0aGUgYXJhY2hpZG9uaWMgYWNpZCBjb25jZW50cmF0aW9uIGhhcyBubyBlZmZlY3Qgb24gdGhlIG1lYW4gcHJvc3RhY3ljbGluIGxldmVsIGluIGJsb29kIHBsYXNtYSBpbiByYXRzClxbCiAgSF8wOlxtdV8xPVxtdV8yID0gXG11XzMKXF0KCi0gJEhfMSQ6IHRoZSBhcmFjaGlkb25pYyBhY2lkIGNvbmNlbnRyYXRpb24gaGFzIGFuIGVmZmVjdCBvbiB0aGUgbWVhbiBwcm9zdGFjeWNsaW4gbGV2ZWwgaW4gYmxvb2QgcGxhc21hIGluIHJhdHMsIHdoaWNoIGltcGxpZXMgdGhhdCB0aGUgYXQgbGVhc3QgdHdvIG1lYW5zIGFyZSBkaWZmZXJlbnQuCgpJbiB0ZXJtcyBvZiB0aGUgbW9kZWwgcGFyYW1ldGVycyB0aGlzIGJlY29tZXMKClxbCiAgSF8wOlxtdV8xPVxtdV8yID0gXG11XzMKXF0KYW5kClxbSF8xOiBcZXhpc3RzXCBqLGsgXGluIFx7MSxcbGRvdHMsZ1x9IDogXG11X2pcbmVxXG11X2tcXQoKCkFsdGVybmF0aXZlIGFwcHJvYWNoOiBzcGxpdCBudWxsIGh5cG90aGVzaXMgaW4gcGFydGlhbCBoeXBvdGhlc2VzOiAKXFsKICBIX3swamt9OiBcbXVfaj1cbXVfayBcdGV4dHsgdmVyc3VzIH0gSF97MWprfTogXG11X2ogXG5lcSBcbXVfawpcXQoKRWFjaCBoeXBvdGhlc2lzIGNhbiBiZSB0ZXN0ZWQgdmlhIHR3by1zYW1wbGUgdC10ZXN0cyAkXHJpZ2h0YXJyb3ckIG11bHRpcGxlIHRlc3RpbmcgcHJvYmxlbSArIGxvc3Mgb2YgcG93ZXIuCgokXHJpZ2h0YXJyb3ckIGFzc2VzcyAkSF8wOlxtdV8xPVxtdV8yPVxtdV8zJCB3aXRoIGEgKnNpbmdsZSB0ZXN0Ki4KCgoKIyBBbmFseXNlIG9mIFZhcmlhbmNlCgpDb3JyZWN0IHNvbHV0aW9uIGZvciB0ZXN0aW5nIHByb2JsZW06IEFOYWx5c2lzIE9mIFZBcmlhbmNlIChBTk9WQSkKCi0gV2UgZGV2ZWxvcCB0aGUgbWV0aG9kIGZvciAzIGdyb3VwcyAocHJvc3RhY3ljbGluIGV4YW1wbGUpCi0gTW9kZWwgZGF0YSB3aXRoIGxpbmVhciBtb2RlbCBieSB1c2luZyBkdW1teSB2YXJpYWJsZXMuCi0gMSBkdW1teSB2YXJpYWJsZSBsZXNzIHRoYW4gdGhlIG51bWJlciBvZiBncm91cHMuIEhlcmUgd2UgbmVlZCAyIGR1bW15IHZhcmlhYmxlcy4KLSBHZW5lcmFsaXppbmcgdG8gZyBncm91cHMgJGc+MyQgaXMgdHJpdmlhbCAoZXh0cmEgZHVtbXkgdmFyaWFibGVzKQoKIyMjIE1vZGVsCgpcYmVnaW57ZXFuYXJyYXl9CiAgWV9pICY9JiBnKHhfe2kxfSx4X3tpMn0pICsgXGVwc2lsb25faVxcCiAgWV9pICY9JiBcYmV0YV8wK1xiZXRhXzEgeF97aTF9ICtcYmV0YV8yIHhfe2kyfSArXGVwc2lsb25faQpcZW5ke2VxbmFycmF5fQoKLSAkWV9pJCBkZSBvdXRjb21lIGZvciBvYnNlcnZhdGlvbiAkaSQgKCRpPTEsXGxkb3RzLCBuJCkKXHZzcGFjZXs3cHR9Ci0gJFxlcHNpbG9uX2lcdGV4dHtpLmkuZC59IE4oMCxcc2lnbWFeMikkClx2c3BhY2V7N3B0fQotIGFuZCBkdW1teSB2YXJpYWJsZXMgCiQkeF97aTF9ID0gXGxlZnRceyBcYmVnaW57YXJyYXl9e2xsfQoxICYgXHRleHR7IGlmIG9ic2VydmF0aW9uICRpJCBiZWxvbmdzIHRvIG1pZGRsZSBkb3NlIGdyb3VwIChNKX0gXFwKMCAmIFx0ZXh0eyBpZiBvYnNlcnZhdGlvbiAkaSQgYmVsb25ncyB0byBvdGhlciBkb3NlIGdyb3VwfSBcZW5ke2FycmF5fVxyaWdodC4kJAokJHhfe2kyfSA9IFxsZWZ0XHsgXGJlZ2lue2FycmF5fXtsbH0KMSAmIFx0ZXh0eyBpZiBvYnNlcnZhdGlvbiAkaSQgYmVsb25ncyB0byBoaWdoIGRvc2UgZ3JvdXAgKEgpfSBcXAowICYgXHRleHR7aWYgb2JzZXJ2YXRpb24gJGkkIGJlbG9uZ3MgdG8gb3RoZXIgZG9zZSBncm91cH0gXGVuZHthcnJheX1ccmlnaHQuIC4kJApcdnNwYWNlezdwdH0KIC0gTG93IGRvc2UgZ3JvdXAgKEwpIHdpdGggJHhfe2kxfT14X3tpMn09MCQgaXMgICpyZWZlcmVuY2UgZ3JvdXAqCgpSZWdyZXNzaW9uLW1vZGVsIGNhbiBiZSByZXdyaXR0ZW4gYXMgYSBtb2RlbCBmb3IgZWFjaCBncm91cCA6Clx2c3BhY2V7LTIwcHR9ClxiZWdpbntlcW5hcnJheSp9CiBZX3tpXHZlcnQgXHRleHR7ZG9zZT1MfX0gJj0mIFxiZXRhXzArXGVwc2lsb25faSBcXAogWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9TX19ICY9JiBcYmV0YV8wK1xiZXRhXzErIFxlcHNpbG9uX2kgIFxcCiBZX3tpXHZlcnQgXHRleHR7ZG9zZT1IfX0gJj0mIFxiZXRhXzArXGJldGFfMiArIFxlcHNpbG9uX2kKXGVuZHtlcW5hcnJheSp9CndpdGggJFxlcHNpbG9uX2kgXHNpbSBOKDAsXHNpZ21hXjIpJApcdnNwYWNlezEwcHR9CgpJbnRlcnByZXRhdGlvbiBvZiBtb2RlbCBwYXJhbWV0ZXJzOgpcdnNwYWNley0yMHB0fQogXGJlZ2lue2VxbmFycmF5Kn0KICAgXGJldGFfMCAmPSYgIFx0ZXh0e0V9XGxlZnRbWV9pIFxtaWQgXHRleHR7dHJlYXRtZW50IHdpdGggbG93IGRvc2UgZ3JvdXAgTH1ccmlnaHRdIFxcCiAgIFxiZXRhXzEgJj0mICAoXGJldGFfMCtcYmV0YV8xKS1cYmV0YV8wID0gXHRleHR7RX1cbGVmdFtZX2kgXG1pZCBcdGV4dHt0cmVhdG1lbnQgTX1ccmlnaHRdIC0gXHRleHR7RX1cbGVmdFtZX2kgXG1pZCBcdGV4dHt0cmVhdG1lbnQgTH1ccmlnaHRdIFxcCiAgIFxiZXRhXzIgJj0mICAoXGJldGFfMCtcYmV0YV8yKS1cYmV0YV8wID0gXHRleHR7RX1cbGVmdFtZX2kgXG1pZCBcdGV4dHt0cmVhdG1lbnQgSH1ccmlnaHRdLVx0ZXh0e0V9XGxlZnRbWV9pIFxtaWQgXHRleHR7dHJlYXRtZW50IEx9XHJpZ2h0XS4KIFxlbmR7ZXFuYXJyYXkqfQoKIDEuICAkXGJldGFfMCQgaXMgdGhlIG1lYW4gb3V0Y29tZSBmb3IgZ3JvdXAgTAogXHZzcGFjZXs3cHR9CiAyLiAgJFxiZXRhXzEkIGlzIGVmZmVjdCAoZGlmZmVyZW5jZSBpbiBtZWFuICBjb25jZW50cmF0aW9uKSBvZiBncm91cCBNIHZzIGdyb3VwIEwKIFx2c3BhY2V7N3B0fQogMy4gICRcYmV0YV8yJCBpcyBlZmZlY3Qgb2YgZ3JvdXAgSCB2cyBncm91cCBMCgoKV2UgcmVmb3JtdWxhdGUgdGhlIG1vZGVsIGJ5IHVzaW5nICRcbXUkLW5vdGF0aW9uczoKIFx2c3BhY2V7LTdwdH0KIFxiZWdpbntlcW5hcnJheSp9CiAgWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9TH19ICY9JiBcYmV0YV8wK1xlcHNpbG9uX2kgPSBcbXVfMStcZXBzaWxvbl9pIFxcCiAgWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9TX19ICY9JiBcYmV0YV8wK1xiZXRhXzErIFxlcHNpbG9uX2kgPSBcbXVfMitcZXBzaWxvbl9pIFxcCiAgWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9SH19ICY9JiBcYmV0YV8wK1xiZXRhXzIgKyBcZXBzaWxvbl9pID0gXG11XzMrXGVwc2lsb25faSAuCiBcZW5ke2VxbmFycmF5Kn0KIHdpdGggJFxlcHNpbG9uX2kgXHNpbSBOKDAsXHNpZ21hXjIpJCBhbmQKICQkICBcbXVfaiA9IFx0ZXh0e0V9XGxlZnRbWV9pIFxtaWQgXHRleHR7dHJlYXRtZW50IGdyb3VwIH0galxyaWdodF0uJCQKCiBPcmlnaW5hbCBudWxsIGh5cG90aGVzZSAKICQkSF8wOlxtdV8xPVxtdV8yPVxtdV8zJCQKIGNhbiBiZSBmb3JtdWxhdGVkIGFzCiAkJEhfMDogXGJldGFfMT1cYmV0YV8yPTAuJCQKCk1vZGVsIGFsbG93cyB1cyB0byB1c2UgYWxsIG1ldGhvZHMgZnJvbSBsaW5lYXIgcmVncmVzc2lvbi4KCi0gUGFyYW1ldGVyIGVzdGltYXRvcnMgZm9yIG1lYW5zLCB2YXJpYW5jZXMgYW5kICBzdGFuZGFyZCBlcnJvcnMKXHZzcGFjZXsxMHB0fQotIEluZmVyZW5jZTogQ29uZmlkZW5jZSBpbnRlcnZhbHMsIGh5cG90aGVzaXMgdGVzdHMKXHZzcGFjZXsxMHB0fQogIC0gVGVzdCAkSF8wOiBcYmV0YV8xPVxiZXRhXzI9MCQgd2l0aCAkRiQtdGVzdC4KCiMjIFByb3N0YWN5Y2xpbiBleGFtcGxlCgpgYGB7cn0KbW9kZWwxIDwtIGxtKHByb3N0YWN+ZG9zZSxkYXRhPXByb3N0YWN5Y2xpbikKc3VtbWFyeShtb2RlbDEpCmBgYAoKCiMgU3VtIG9mIHNxdWFyZXMgYW5kIEFub3ZhCgpTaW1pbGFyIHRvIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiB3ZSB3aWxsIHVzZSBzdW0gb2Ygc3F1YXJlcyB0byBkZXJpdmUgdGhlIEYtdGVzdC4KXHZzcGFjZXstMTBwdH0KXGJlZ2lue2VxbmFycmF5Kn0KXHRleHR7U1NSfSY9JlxzdW1cbGltaXRzX3tpPTF9Xm4gKFxoYXQgWV9pIC1cYmFyIFkpXjJcXAomPSYgXHN1bVxsaW1pdHNfe2k9MX1ebiAoXGhhdHtnfSAoeF97aTF9LHhfe2kyfSkgLSBcYmFyIFkpXjJcXAomPSYgXHN1bVxsaW1pdHNfe2k9MX1ebiAoXGhhdFxiZXRhXzArXGhhdFxiZXRhXzF4X3tpMX0rXGhhdFxiZXRhXzJ4X3tpMn0pIC0gXGJhciBZKV4yXFwKJj0mIFxzdW1cbGltaXRzX3tpPTF9XntuXzF9IChcaGF0XGJldGFfMCAtIFxiYXIgWSleMiArXHN1bVxsaW1pdHNfe2k9MX1ee25fMn0gKFxoYXRcYmV0YV8wICsgXGhhdFxiZXRhXzEgLSBcYmFyIFkpXjIrXHN1bVxsaW1pdHNfe2k9MX1ee25fM30gKFxoYXRcYmV0YV8wICsgXGhhdFxiZXRhXzIgLSBcYmFyIFkpXjJcXAomPSYgXHN1bVxsaW1pdHNfe2k9MX1ee25fMX0gKFxiYXIgWV8xLSBcYmFyIFkpXjIgK1xzdW1cbGltaXRzX3tpPTF9XntuXzJ9IChcYmFyIFlfMi0gXGJhciBZKV4yK1xzdW1cbGltaXRzX3tpPTF9XntuXzN9IChcYmFyIFlfMyAtIFxiYXIgWSleMlxcClxlbmR7ZXFuYXJyYXkqfQp3aXRoICRuXzEkLCAkbl8yJCBlbiAkbl8zJCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIGdyb3VwIChoZXJlICRuLTE9bl8yPW5fMz0xMiQpLgoKClxiZWdpbntlcW5hcnJheSp9Clx0ZXh0e1NTUn0mPSZcc3VtXGxpbWl0c197aT0xfV5uIChcaGF0IFlfaSAtXGJhciBZKV4yClxlbmR7ZXFuYXJyYXkqfQoKLSBTdW0gb2Ygc3F1YXJlcyBpcyBhZ2FpbiBlcXVpdmFsZW50IHdpdGggY29tcGFyaXNvbiBvZiBtb2RlbCAoMSkgYW5kIHJlZHVjZWQgbW9kZWwgd2l0aCBhbiBpbnRlcmNlcHQsIG9ubHkuIAotIEZvciByZWR1Y2VkIG1vZGVsIHRoZSBpbnRlcmNlcHQgaXMgZXN0aW1hdGVkIGJ5IHRoZSBzYW1wbGUgbWVhbi4gIAotIFRoaXMgc3VtIG9mIHNxdWFyZXMgaGFzIGctMT0yIGRlZ3JlZXMgb2YgZnJlZWRvbTogIAoKICAtIGc9MyBtb2RlbCBwYXJhbWV0ZXJzIC0gMSBwYXJhbWV0ZXIgdG8gZXN0aW1hdGUgb3ZlcmFsbCBzYW1wbGUgbWVhbiBvcgogIC0gZz0zIHBhci4gaW4gY29tcGxleCBtb2RlbCAtIDEgcGFyLiBpbiByZWR1Y2VkIG1vZGVsLgoKCiMjIERlY29tcG9zaXRpb24gb2YgVG90YWwgU3VtIG9mIFNxdWFyZXMKCiAtIFRoZSBjb252ZW50aW9uIGluIHRoZSBBbm92YSBzZXR0aW5nIGlzIHRvIGRlbm90ZSB0aGUgc3VtIG9mIHNxdWFyZXMgYXMgU1NULCB0aGUgKipTdW0gb2YgU3F1YXJlcyBvZiB0aGUgVHJlYXRtZW50ICh0cmVhdG1lbnQpKiogb3IgYXMgU1NCZXR3ZWVuLgotIFRoZSBzdW0gb2Ygc3F1YXJlcyBvZiB0aGUgcmVncmVzc2lvbiBpbmRlZWQgcmVmbGVjdHMgdGhlIHZhcmlhYmlsaXR5IGJldHdlZW4gdGhlIGdyb3Vwcy4gCi0gVGhlIGNvcnJlc3BvbmRpbmcgbWVhbiBzdW0gb2Ygc3F1YXJlcyBiZWNvbWVzICAkXHRleHR7TVNUfT1cdGV4dHtTU1R9LzIkLgoKVGhlIGRlY29tcG9zaXRpb24gb2YgU1NUb3QgY2FuIGJlIHdyaXR0ZW4gYXMgClxbCiAgXHRleHR7U1NUb3R9ID0gXHRleHR7U1NUfSArIFx0ZXh0e1NTRX0KXF0KCiMjU1NUb3QKClx2c3BhY2V7MTBwdH0KCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBhcihtZnJvdz1jKDEsMikpCmppdElrPXJ1bmlmKDM2LC0uMiwuMikrcmVwKDE6MyxlYWNoPTEyKQpwbG90KHByb3N0YWN+ZG9zZSxkYXRhPXByb3N0YWN5Y2xpbix4bGFiPSJBcmFjaGlkb25pYyBhY2lkIGRvc2UgIix5bGFiPSJQcm9zdGFjeWNsaW4gKG5nL21sKSIsY2V4LmF4aXM9MS41LGNleC5sYWI9MS41LGNleC5tYWluPTEuNSkKcG9pbnRzKGppdElrLHByb3N0YWN5Y2xpbiRwcm9zdGFjLGNvbD1jb2wscGNoPTE5KQpwb2ludHMoaml0SWsscHJvc3RhY3ljbGluJHByb3N0YWMsY29sPTQpCnBvaW50cygxOjMscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKSxwY2g9MTcsY29sPWMoImJpc3F1ZSIsImNvcmFsIiwiZGFya2N5YW4iKSxjZXg9MS41KQpwb2ludHMoMTozLHByZWRpY3QobW9kZWwxLGRhdGEuZnJhbWUoZG9zZT1mYWN0b3IoYygxMCwyNSw1MCkpKSkscGNoPTIsY29sPTEsY2V4PTEuNSkKYWJsaW5lKGg9bWVhbihwcm9zdGFjeWNsaW4kcHJvc3RhYyksbHR5PTEpCmZvciAoaSBpbiAxOjM2KSBsaW5lcyhyZXAoaml0SWtbaV0sMiksYyhtZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxwcm9zdGFjeWNsaW4kcHJvc3RhY1tpXSksY29sPTQsbHR5PTIpCmppdElrPXJ1bmlmKDM2LC0uMiwuMikrcmVwKDE6MyxlYWNoPTEyKQoKcGxvdChyZXAoMSwzNikscHJvc3RhY3ljbGluJHByb3N0YWMtbWVhbihwcm9zdGFjeWNsaW4kcHJvc3RhYykseGF4dD0ibm9uZSIseWxhYj0iRGV2aWF0aW9ucyIsY2V4LmxhYj0xLjUsY2V4Lm1haW49MS41LGNleC5heGlzPTEuNSxjb2w9YXMuY2hhcmFjdGVyKHByb3N0YWN5Y2xpbiRjb2wpLHhsaW09YygxLDMpLHBjaD0xOSx4bGFiPSIiKQpwb2ludHMocmVwKDEsMzYpLHByb3N0YWN5Y2xpbiRwcm9zdGFjLW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHBjaD0xLGNvbD00KQpheGlzKGF0PTE6MyxsYWJlbHM9YyhleHByZXNzaW9uKHBhc3RlKHlbaV0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZShiYXIoeSlbal0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZSh5W2ldLCIgLSAiLGJhcih5KVtqXSkpKSxzaWRlPTEsY2V4LmF4aXM9MS41KQpgYGAKCgojI1NTVAoKXHZzcGFjZXsxMHB0fQoKYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdChwcm9zdGFjfmRvc2UsZGF0YT1wcm9zdGFjeWNsaW4seGxhYj0iQXJhY2hpZG9uaWMgYWNpZCBkb3NlICIseWxhYj0iUHJvc3RhY3ljbGluIChuZy9tbCkiLGNleC5heGlzPTEuNSxjZXgubGFiPTEuNSxjZXgubWFpbj0xLjUpCnBvaW50cyhqaXRJayxwcm9zdGFjeWNsaW4kcHJvc3RhYyxjb2w9Y29sLHBjaD0xOSkKcG9pbnRzKGppdElrLHByb3N0YWN5Y2xpbiRwcm9zdGFjLGNvbD00KQpwb2ludHMoMTozLHByZWRpY3QobW9kZWwxLGRhdGEuZnJhbWUoZG9zZT1mYWN0b3IoYygxMCwyNSw1MCkpKSkscGNoPTE3LGNvbD1jKCJiaXNxdWUiLCJjb3JhbCIsImRhcmtjeWFuIiksY2V4PTEuNSkKcG9pbnRzKDE6MyxwcmVkaWN0KG1vZGVsMSxkYXRhLmZyYW1lKGRvc2U9ZmFjdG9yKGMoMTAsMjUsNTApKSkpLHBjaD0yLGNvbD0yLGNleD0xLjUpCmFibGluZShoPW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLGx0eT0xKQpmb3IgKGkgaW4gMTozKSBsaW5lcyhyZXAoaSwyKSxjKG1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHByZWRpY3QobW9kZWwxLGRhdGEuZnJhbWUoZG9zZT1sZXZlbHMocHJvc3RhY3ljbGluJGRvc2UpW2ldKSkpLGNvbD0yLGx0eT0yKQoKcGxvdChyZXAoMSwzNikscHJvc3RhY3ljbGluJHByb3N0YWMtbWVhbihwcm9zdGFjeWNsaW4kcHJvc3RhYykseGF4dD0ibm9uZSIseWxhYj0iRGV2aWF0aW9ucyIsY2V4LmxhYj0xLjUsY2V4Lm1haW49MS41LGNleC5heGlzPTEuNSxjb2w9YXMuY2hhcmFjdGVyKHByb3N0YWN5Y2xpbiRjb2wpLHhsaW09YygxLDMpLHBjaD0xOSx4bGFiPSIiKQpwb2ludHMocmVwKDEsMzYpLHByb3N0YWN5Y2xpbiRwcm9zdGFjLW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHBjaD0xLGNvbD00KQpwb2ludHMocmVwKDIsMykscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKS1tZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxwY2g9MTcsY29sPXVuaXF1ZShwcm9zdGFjeWNsaW4kY29sKSxjZXg9MS41KQpwb2ludHMocmVwKDIsMykscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKS1tZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxwY2g9MixjZXg9MS41LGNvbD0yKQpheGlzKGF0PTE6MyxsYWJlbHM9YyhleHByZXNzaW9uKHBhc3RlKHlbaV0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZShiYXIoeSlbal0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZSh5W2ldLCIgLSAiLGJhcih5KVtqXSkpKSxzaWRlPTEsY2V4LmF4aXM9MS41KQpgYGAKCiMjU1NFCgpcdnNwYWNlezEwcHR9CgpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwYXIobWZyb3c9YygxLDIpKQpwbG90KHByb3N0YWN+ZG9zZSxkYXRhPXByb3N0YWN5Y2xpbix4bGFiPSJBcmFjaGlkb25pYyBhY2lkIGRvc2UgIix5bGFiPSJQcm9zdGFjeWNsaW4gKG5nL21sKSIsY2V4LmF4aXM9MS41LGNleC5sYWI9MS41LGNleC5tYWluPTEuNSkKcG9pbnRzKGppdElrLHByb3N0YWN5Y2xpbiRwcm9zdGFjLGNvbD1jb2wscGNoPTE5KQpwb2ludHMoaml0SWsscHJvc3RhY3ljbGluJHByb3N0YWMsY29sPTEpCnBvaW50cygxOjMscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKSxwY2g9MTcsY29sPWMoImJpc3F1ZSIsImNvcmFsIiwiZGFya2N5YW4iKSxjZXg9MS41KQpwb2ludHMoMTozLHByZWRpY3QobW9kZWwxLGRhdGEuZnJhbWUoZG9zZT1mYWN0b3IoYygxMCwyNSw1MCkpKSkscGNoPTIsY29sPTIsY2V4PTEuNSkKZm9yIChpIGluIDE6MykgbGluZXMoYyhpLS4yLGkrLjIpLHJlcChwcmVkaWN0KG1vZGVsMSxkYXRhLmZyYW1lKGRvc2U9bGV2ZWxzKHByb3N0YWN5Y2xpbiRkb3NlKVtpXSkpLDIpLGNvbD1jKCJiaXNxdWUiLCJjb3JhbCIsImRhcmtjeWFuIilbaV0pCmFibGluZShoPW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLGx0eT0xKQpmb3IgKGkgaW4gMTozNikgbGluZXMocmVwKGppdElrW2ldLDIpLGMocHJvc3RhY3ljbGluJHByb3N0YWNbaV0sbW9kZWwxJGZpdHRlZFtpXSksY29sPTEsbHR5PTIpCgpwbG90KHJlcCgxLDM2KSxwcm9zdGFjeWNsaW4kcHJvc3RhYy1tZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSx4YXh0PSJub25lIix5bGFiPSJEZXZpYXRpb25zIixjZXgubGFiPTEuNSxjZXgubWFpbj0xLjUsY2V4LmF4aXM9MS41LGNvbD1hcy5jaGFyYWN0ZXIocHJvc3RhY3ljbGluJGNvbCkseGxpbT1jKDEsMykscGNoPTE5LHhsYWI9IiIpCnBvaW50cyhyZXAoMSwzNikscHJvc3RhY3ljbGluJHByb3N0YWMtbWVhbihwcm9zdGFjeWNsaW4kcHJvc3RhYykscGNoPTEsY29sPTQpCnBvaW50cyhyZXAoMiwzKSxwcmVkaWN0KG1vZGVsMSxkYXRhLmZyYW1lKGRvc2U9ZmFjdG9yKGMoMTAsMjUsNTApKSkpLW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHBjaD0xNyxjb2w9dW5pcXVlKHByb3N0YWN5Y2xpbiRjb2wpLGNleD0xLjUpCnBvaW50cyhyZXAoMiwzKSxwcmVkaWN0KG1vZGVsMSxkYXRhLmZyYW1lKGRvc2U9ZmFjdG9yKGMoMTAsMjUsNTApKSkpLW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHBjaD0yLGNvbD0yLGNleD0xLjUpCnBvaW50cyhyZXAoMywzNiksbW9kZWwxJHJlcyxwY2g9MTksY29sPWFzLmNoYXJhY3Rlcihwcm9zdGFjeWNsaW4kY29sKSkKcG9pbnRzKHJlcCgzLDM2KSxtb2RlbDEkcmVzLHBjaD0xKQpheGlzKGF0PTE6MyxsYWJlbHM9YyhleHByZXNzaW9uKHBhc3RlKHlbaV0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZShiYXIoeSlbal0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZSh5W2ldLCIgLSAiLGJhcih5KVtqXSkpKSxzaWRlPTEsY2V4LmF4aXM9MS41KQpgYGAKCgojIyBBbm92YSB0ZXN0CgpUZXN0ICRIXzA6IFxiZXRhXzE9XGJldGFfMj0wJCB3aXRoICRGJC10ZXN0LgpcWwogIEYgPSBcZnJhY3tcdGV4dHtNU1R9fXtcdGV4dHtNU0V9fQpcXQoKd2l0aAoKLSAkXHRleHR7TVNUfT1cdGV4dHtTU1R9LyhnLTEpJCAgClx2c3BhY2V7MTBwdH0KLSAkXHRleHR7TVNFfT1cdGV4dHtTU0V9LyhuLXApJApcdnNwYWNlezEwcHR9Ci0gVGVzdCBzdGF0aXN0aWMgY29tcGFyZXMgdGhlIHZhcmlhYmlsaXR5IGV4cGxhaW5lZCBieSAgbW9kZWwgKE1TVCkgd2l0aCB0aGUgcmVzaWR1YWwgdmFyaWFiaWxpdHkgKE1TRSkKCm9yCgotIFZhcmlhYmlsaXR5IGJldHdlZW4gZ3JvdXBzIChNU1QpIHRvIHZhcmlhYmlsaXR5IHdpdGhpbiBncm91cHMgKE1TRSkKXHZzcGFjZXsxMHB0fQotIFVuZGVyICRIXzAkOiAkRiBcc2ltIEZfe2ctMSxuLWd9JCwgd2l0aCBnPTMuCgoKIyMgQW5vdmEgVGFibGUKCnwgfERmfFN1bSBTcXxNZWFuIFNxfEYgdmFsdWV8UHIoPkYpfAp8LS0tfC0tLXwtLS18LS0tfC0tLXwtLS18CnxUcmVhdG1lbnR8ZC5mLiBTU1R8U1NUfE1TVHxGLXN0YXRpc3RpZWt8cC13YWFyZGV8CnxFcnJvcnxkLmYuIFNTRXxTU0V8TVNFfCB8IHwKCmBgYHtyfQphbm92YShtb2RlbDEpCmBgYAoKCiMjIyBGLWRpc3RyaWJ1dGlvbiB3aXRoIGNyaXRpY2FsIHZhbHVlICAoJFxhbHBoYSQ9NSUpIGFuZCBvYnNlcnZlZCBGLXN0YXRpc3RpYyBmb3IgcHJvc3RhY3ljbGluIGV4YW1wbGUKYGBge3IgcHJvc3RhY0YsIG91dC53aWR0aD0nMTAwJScsIGZpZy5hc3A9LjgsIGZpZy5hbGlnbj0nY2VudGVyJyxlY2hvPUZBTFNFfQpncmlkIDwtIHNlcSgwLDE3LC4wMSkKZGYxPWFub3ZhKG1vZGVsMSlbMSwxXQpkZjI9YW5vdmEobW9kZWwxKVsyLDFdCmZ2YWw9YW5vdmEobW9kZWwxKVsxLDRdCmNyaXQ9cWYoMC45NSxkZjEsZGYyKQpyZWplY3Q9Yyhjcml0LGdyaWRbd2hpY2goZ3JpZD5jcml0KV0pCmFjY2VwdD1jKGdyaWRbd2hpY2goZ3JpZDxjcml0KV0sY3JpdCkKcGxvdChncmlkLGRmKGdyaWQsZGYxLGRmMiksdHlwZT0ibCIseWxhYj0iRGVuc2l0eSIseGxhYj0iRi1zdGF0aXN0aWMiLGNleC5heGlzPTEuNSxjZXgubGFiPTEuNSkKcG9seWdvbihjKDAsYWNjZXB0LGNyaXQsMCksYygwLGRmKGFjY2VwdCxkZjEsZGYyKSwwLDApLGNvbD0iYmx1ZSIsYm9yZGVyPSJibHVlIikKdGV4dChjcml0LzIsLjk3LGxhYmVscz0iYWNjZXB0XG45NSUiLGNvbD0iYmx1ZSIsY2V4PTEuNSkKcG9seWdvbihjKGNyaXQscmVqZWN0LDE1LGNyaXQpLGMoMCxkZihyZWplY3QsZGYxLGRmMiksMCwwKSxjb2w9InJlZCIsYm9yZGVyPSJyZWQiKQphYmxpbmUodj1jcml0LGNvbD0icmVkIixsd2Q9MikKdGV4dChjcml0KyhmdmFsLWNyaXQpLzIsLjk3LGxhYmVscz0icmVqZWN0XG41JSIsY29sPSJyZWQiLGNleD0xLjUpCnRleHQocG9zPTQsY3JpdCxkZihjcml0LDIsMzMpLGxhYmVscz1wYXN0ZTAoIkYoMC4wNSwiLGRmMSwiLCIsZGYyLCIpIiksY29sPSJyZWQiLGNleD0xLjUpCnRleHQocG9zPTQsZnZhbCxkZihjcml0LGRmMSxkZjIpLGxhYmVscz1wYXN0ZTAoImY9Iixyb3VuZChmdmFsLDEpKSxjb2w9ImRhcmtvcmFuZ2UiLGNleD0xLjUpCmFibGluZSh2PWZ2YWwsY29sPSJkYXJrb3JhbmdlIixsd2Q9MixsdHk9MikKdGV4dCgxNS41LC45NyxsYWJlbHM9cGFzdGUwKCJwLXZhbHVlXG4iLGZvcm1hdChhbm92YShtb2RlbDEpWzEsNV0sZGlnaXRzPTIpKSxjb2w9ImRhcmtvcmFuZ2UiLGNleD0xLjUpCmFycm93cyh4MD0xNy41LHgxPWZ2YWwseTA9LjkseTE9LjksY29sPSJkYXJrb3JhbmdlIikKYGBgCgoKIyMjIEYtZGlzdHJpYnV0aW9ucyB3aXRoIGRpZmZlcmVudCBudW1iZXIgb2YgZGVncmVlcyBvZiBmcmVlZG9tIGluIHRoZSBub21pbmF0b3IgYW5kIGRlbm9taW5hdG9yIApgYGB7ciBmdGhlbywgb3V0LndpZHRoPScxMDAlJywgZmlnLmFzcD0uOCwgZmlnLmFsaWduPSdjZW50ZXInLGVjaG89RkFMU0V9CnBsb3QoZ3JpZCxkZihncmlkLDEsNSksdHlwZT0ibCIseWxhYj0iRGVuc2l0eSIseGxhYj0iRi1zdGF0aXN0aWMiLHhsaW09YygwLDUpLHlsaW09YygwLDEuNSksbHdkPTIsY2V4LmF4aXM9MS41LGNleC5sYWI9MS41KQpsaW5lcyhncmlkLGRmKGdyaWQsNSw1KSx0eXBlPSJsIixjb2w9Mixsd2Q9MikKbGluZXMoZ3JpZCxkZihncmlkLDEwLDMwKSx0eXBlPSJsIixjb2w9Myxsd2Q9MikKbGluZXMoZ3JpZCxkZihncmlkLDIwLDMwKSx0eXBlPSJsIixjb2w9NCxsd2Q9MikKbGluZXMoZ3JpZCxkZihncmlkLDUwLDUwKSx0eXBlPSJsIixjb2w9NSxsd2Q9MikKbGVnZW5kKCJ0b3ByaWdodCIsbHR5PTEsY29sPWMoMSwyLDMsNCw1KSxsZWdlbmQ9YygiRigxLDUpIiwiRig1LDUpIiwiRigxMCwzMCkiLCJGKDIwLDMwKSIsIkYoNTAsNTApIiksbHdkPTIsY2V4PTEuNSkKYGBgCgojIyNQcm9zdGFjeWNsaW4gZXhhbXBsZTogd2hpY2ggZ3JvdXBzIGFyZSBkaWZmZXJlbnQ/CgpgYGB7cn0Kc3VtbWFyeShtb2RlbDEpCmBgYAoKV2l0aCBtb2RlbCBvdXRwdXQgd2UgY2FuIGFzc2VzcyBpZiB0aGUgbWVhbiBwcm9zdGFjeWNsaW4gY29uY2VudHJhdGlvbiBkaWZmZXJzIGJldHdlZW4gbWlkZGxlIGFuZCBsb3cgZG9zZSBncm91cCAoJFxiZXRhXzEkOiBkb3NlMjUpLCBhbmQsIGJldHdlZW4gaGlnaCBhbmQgbG93IGRvc2UgZ3JvdXAgKCRcYmV0YV8yJDogZG9zZTUwKS4KClRoZSBwLXZhbHVlcyBkbyBub3QgYWNjb3VudCBmb3IgbXVsdGlwbGUgdGVzdGluZy4gIAoKIyBQb3N0IGhvYyBhbmFseXNpczogTXVsdGlwbGUgY29tcGFyaXNvbnMgb2YgbWVhbnMKCiMjIE5haXZlIG1ldGhvZAoKSW4gdGhlIGZpcnN0IHBhcnQgd2UgZGV2ZWxvcGVkIHRoZSAkRiQtdGVzdCB0byBhc3Nlc3MKCiQkICBIXzA6IFxtdV8xPVxjZG90cyA9IFxtdV9nIFx0ZXh0eyB2ZXJzdXMgfSBIXzE6IEhfMTogXGV4aXN0c1wgaixrIFxpbiBcezEsXGxkb3RzLGdcfSA6IFxtdV9qXG5lcVxtdV9rJCQKCgotIElmIHdlIHJlamVjdCAkSF8wJCB3ZSBjb25jbHVkZSB0aGF0IGF0IGxlYXN0IHR3byBtZWFucyBhcmUgZGlmZmVyZW50Ci0gVGhlIG1ldGhvZCBkb2VzIG5vdCBhbGxvdyB0byBpZGVudGlmeSB3aGljaCBtZWFucyBhcmUgZGlmZmVyZW50LgoKQSBmaXJzdCBuYWl2ZSBtZXRob2QgaXMgdG8gc3BsaXQgJEhfMCQgIGluIHBhcnRpYWwgaHlwb3RoZXNlcwokJEhfezBqa306IFxtdV9qPVxtdV9rIFx0ZXh0eyB2ZXJzdXMgfSBIX3sxamt9OiBcbXVfaiBcbmVxIFxtdV9rJCQKCi0gRmFsc2lmeSBwYXJ0aWFsIG51bGwgaHlwb3RoZXNlcyB3aXRoIHR3by1zYW1wbGUgJHQkLXRlc3RlbgoKLSBDb21wYXJpc29uIG9mIGdyb3VwICRqJCB3aXRoIGdyb3VwICRrJCB3aXRoIHR3by1zYW1wbGUgJHQkLXRlc3QgdW5kZXIgdGhlIGVxdWFsaXR5IG9mIG1lYW5zOgokJFRfe2prfSA9IFxmcmFje1xiYXJ7WX1fai1cYmFye1l9X2t9e1NfcFxzcXJ0e1xmcmFjezF9e25fan0rXGZyYWN7MX17bl9rfX19IFxzaW0gdF97bi0yfSQkCgpXaXRoCgotICRTX3BeMiQgdGhlIHBvb2xlZCB2YXJpYW5jZSBlc3RpbWF0b3IsCiQkU19wXjIgPSBcZnJhY3sobl9qLTEpU19qXjIgKyAobl9rLTEpU19rXjJ9e25faituX2stMn0kJAoKLSB3aXRoICRTX2peMiQgYW5kICRTX2teMiQgdGhlIHNhbXBsZSB2YXJpYW5jZXMgb2YgZ3JvdXAgJGokIGVuICRrJCwgcmVzcGVjdGl2ZWx5LiAKCkluIEFOT1ZBIGNvbnRleHQgd2UgYXNzdW1lIHRoYXQgdGhlIHZhcmlhbmNlIG9mICoqYWxsKiogJGckIGdyb3VwcyBpcyBlcXVhbCwgaS5lLiB0aGUgcmVzaWR1YWwgdmFyaWFuY2UgJFxzaWdtYV4yJC4KCi0gVXNlIG9mICRTX3BeMiQgaXMgbm90IGVmZmljaWVudCBiZWNhdXNlIGl0IGRvZXMgbm90IG1ha2UgdXNlIG9mIGFsbCBkYXRhCgotIFdlIGNhbiBnYWluIGVmZmljaWVuY3kgYnkgdXNpbmcgTVNFCiQkXHRleHR7TVNFfT0gXHN1bV97aj0xfV5nIFxmcmFjeyhuX2otMSlTX2peMn17bi1nfSQkCgotIFRoZSAkdCQtdGVzdHMgYXJlIHRodXMgYmVzdCBiYXNlZCBvbgokJFRfe2prfSA9IFxmcmFje1xiYXJ7WX1fai1cYmFye1l9X2t9e1x0ZXh0e01TRX1cc3FydHtcZnJhY3sxfXtuX2p9K1xmcmFjezF9e25fa319fSBcc2ltIHRfe24tZ30uJCQKCi0tLQoKYGBge3J9CndpdGgocHJvc3RhY3ljbGluLHBhaXJ3aXNlLnQudGVzdChwcm9zdGFjLGRvc2UsIm5vbmUiKSkKYGBgCgpXaGVuIHdlIHBlcmZvcm0gJG0kLXRlc3RzIG9uIHRoZSAkXGFscGhhJCBzaWduaWZpY2FuY2UgbGV2ZWwgd2UgY2Fubm90IGNvcnJlY3RseSBjb250cm9sIHRoZSB0eXBlIEkgZXJyb3IuCgojIyBXZSBzaG93IHdpdGggc2ltdWxhdGlvbiB0aGF0IG5haXZlIG1ldGhvZCBkb2VzIG5vdCB3b3JrCgoxLiBXZSBzaW11bGF0ZSBmcm9tIGFuIEFOT1ZBIG1vZGVsIHdpdGggJGc9MyQgZ3JvdXBzLgoyLiBUaGUgbWVhbnMgaW4gdGhlIEFOT1ZBIG1vZGVsIGFyZSBlcXVhbCB0byBlYWNoIG90aGVyLCBzbyB0aGF0ICQkSF8wOiBcbXVfMT1cbXVfMj1cbXVfMyQkLgozLiBGb3IgZWFjaCBzaW11bGF0ZWQgZGF0YXNldCB3ZSBjb25kdWN0ICRtPTMkIHBhaXJ3aXNlIHR3by1zYW1wbGUgJHQkLXRlc3QKNC4gQXMgc29vbiBhcyBvbmUgb2YgdGhlICRwJC12YWx1ZXMgaXMgYmVsb3cgc2lnbmlmaWNhbmNlIGxldmVsICRcYWxwaGE9NVwlJCwgd2UgcmVqZWN0ICRIXzA6IFxtdV8xPVxtdV8yPVxtdV8zJCBiZWNhdXNlIHR3byBtZWFucyBhcmUgZGlmZmVyZW50IGFjY29yZGluZyB0byB0aGUgICR0JC10ZXN0cy4KNS4gV2UgcmFwcG9ydCB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5IG9mIHJlamVjdGlvbiBvZiB0aGUgZ2xvYmFsIG51bGwgaHlwb3RoZXNpcywgaS5lLiB0aGUgcHJvYmFiaWxpdHkgb24gYSB0eXBlIEkgZXJyb3IgICRIXzA6IFxtdV8xPVxtdV8yPVxtdV8zJC4KCgpgYGB7cn0KZzwtMyAjIG51bWJlciBvZiB0cmVhdG1lbnRzIChnPTMpCm5pPC0xMiAjIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCBncm91cApuPC1nKm5pICMgdG90YWwgbnVtYmVyIG9mIG9ic2VydmF0aW9uCmFscGhhPC0wLjA1ICMgc2lnbmlmaWNhbmNlIGxldmVsIG9mIGluZGl2aWR1YWwgdGVzdCAKTj0xMDAwMCAjIG51bWJlciBvZiBzaW11bGF0aW9ucwpzZXQuc2VlZCgzMDIpICNzZWVkIHRvIHJlcHJvZHVjZSByZXN1bHRzIGV4YWN0bHkgCnRydD1mYWN0b3IocmVwKDE6ZyxuaSkpICNmYWN0b3IKY250PC0wICNjb3VudGVyIGZvciBlcnJvbmVvdXMgcmVqZWN0aW9ucwpmb3IoaSBpbiAxOk4pIHsKI2lmIChpJSUxMDAwPT0wKSBjYXQoaSwiLyIsTiwiXG4iKQp5IDwtIHJub3JtKG4pCnRlc3RzPC1wYWlyd2lzZS50LnRlc3QoeSx0cnQsIm5vbmUiKQpyZWplY3Q8LW1pbih0ZXN0cyRwLnZhbHVlLG5hLnJtPVQpPGFscGhhCmlmKHJlamVjdCkgY250PC1jbnQrMQp9CmNudC9OCmBgYAoKLS0tCgotIFByb2JhYmlsaXR5IG9uIHRoZSB0eXBlIEkgZXJyb3IgZXF1YWxzIGByIHJvdW5kKGNudC9OLDMpKjEwMGAlCi0gSXQgaXMgbW9yZSB0aGVuIHR3aWNlICRcYWxwaGE9NSQlLgotIElmIHdlIHJlcGVhdCB0aGUgc2ltdWxhdGlvbiB3aXRoIGcgPSA1IGdyb3VwcyAoaS5lLiAxMCBwYWlyd2lzZSB0LXRlc3RzKSB3ZSBmaW5kIGEgdHlwZSBJIGVycm9yIG9mIDI4LjAlIGluc3RlYWQgb2YgdGhlIGRlc2lyZWQgNSUuCgotIFRoZSBzaW11bGF0aW9uIHN0dWR5IGlsbHVzdHJhdGVzIHRoZSAgKiptdWx0aXBsaWNpdHkqKiBwcm9ibGVtCgogIC0gQ2xhc3NpY2FsIHAtdmFsdWVzIGNhbiBvbmx5IGJlIGNvbXBhcmVkIHdpdGggdGhlIHNpZ25pZmljYW5jZSBsZXZlbCAkXGFscGhhJCwgaWYgdGhlIGNvbmNsdXNpb24gaXMgYmFzZWQgb24gYSBzaW5nbGUgcC12YWx1ZS4KICAtIEhlcmUgdGhlIGZpbmFsIGRlY2lzaW9uIGlzIGJhc2VkIG9uICRtPWdcdGltZXMoZy0xKS8yJCAkcCQtdmFsdWVzLgoKLSBXZSBmaXJzdCBkaXNjdXNzIG9uIHRoZSBleHRlbnNpb24gb2YgdGhlIGNvbmNlcHQgb2YgdHlwZSBJIGVycm9ycyBhbmQgdGhlbiBpbnRyb2R1Y2Ugc29tZSBzb2x1dGlvbnMKCiMjIEZhbWlseS13aXNlIGVycm9yIHJhdGUKCi0gV2hlbiAkbT4xJCB0ZXN0cyBhcmUgdXNlZCB0byBtYWtlIG9uZSBkZWNpc2lvbiBpdCBpcyBuZWNlc3NhcnkgdG8gY29ycmVjdCBmb3IgdGhlIHJpc2sgb24gZmFsc2UgcG9zaXRpdmUgcmVzdWx0cyAodHlwZSBJIGVycm9ycykuCi0gTW9zdCBwcm9jZWR1cmVzIGZvciBtdWx0aXBsZSB0ZXN0aW5nIGFzc3VtZSB0aGF0ICphbGwgJG0kIG51bGwgaHlwb3RoZXNlcyBhcmUgdHJ1ZSouCgotIFNvIG9uZSB0cmllcyB0byBjb250cm9sIHRoZSAqcmlzayBvbiBhdCBsZWFzdCAxIGZhbHNlIHBvc2l0aXZlKiBvbiB0aGUgKipmYW1pbHkgd2lzZSBlcnJvciByYXRlIChGV0VSKSAkXGFscGhhX0YkKiosIHR5cGljYWwgJFxhbHBoYV9GPTAuMDUkLgoKIyMgQm9uZmVycm9uaSBjb3JyZWN0aW9uCgpXaGVuIHdlIGNvbmR1Y3QgJG0kIGluZGVwZW5kZW50IHRlc3QgZWFjaCBvbiB0aGUgc2lnbmlmaWNhbmNlIGxldmVsICRcYWxwaGEkLCB0aGVuClxiZWdpbntlcW5hcnJheSp9ClxhbHBoYV9GJj0mXHRleHR7UH1bXHRleHR7YXQgbGVhc3QgMSBUeXBlIEkgZm91dH1dXFwKJj0mMS0oMS1cYWxwaGEpXm0gXGxlcSBtXGFscGhhClxlbmR7ZXFuYXJyYXkqfQoKLSBJZiB3ZSBhc3Nlc3MgNSB0ZXN0cyBvbiB0aGUgNSUgc2lnbmlmaWNhbmNlIGxldmVsIHRoZW4gIHRoZSBGV0VSICRcYXBwcm94IDI1XCUkLiB7MTBwdH0KLSBCeSBjb25kdWN0aW5nIHRoZW0gYXQgdGhlIDElIHNpZ25pZmljYW5jZSBsZXZlbCB0aGUgRldFUiAkXGFwcHJveCA1XCUkLgoKLSBUaGUgQm9uZmVycm9uaSBjb3JyZWN0aW9uIGNvbnRyb2xzIHRoZSBGV0VSIG9uICRcYWxwaGFfRiQgYnkgc2V0dGluZyAkJFxhbHBoYT1cYWxwaGFfRi9tJCQgZm9yIGVhY2ggb2YgdGhlICRtJCBwYWlyd2lzZSBjb21wYXJpc29ucwoKQW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggaXMgdG8gcmVwb3J0CgogIDEuICphZGp1c3RlZCBwLXZhbHVlcyogdGhhdCBjYW4gYmUgY29tcGFyZWQgdG8gdGhlIEZXRVIgJFxhbHBoYV9GJCBsZXZlbDogJCRcdGlsZGV7cH09bWluKG1cdGltZXMgcCwxKSQkCiAgMi4gYW5kICQoMS1cYWxwaGFfRi9tKTEwMFwlJCBjb25maWRlbmNlIGludGVydmFscy4KCiMjIyBwcm9zdGFjeWNsaW4gZXhhbXBsZSAKCmBgYHtyfQp3aXRoKHByb3N0YWN5Y2xpbixwYWlyd2lzZS50LnRlc3QocHJvc3RhYyxkb3NlLCBkYXRhID0gcHJvc3RhY3ljbGluLCBwLmFkanVzdC5tZXRob2Q9ImJvbmZlcnJvbmkiKSkKYGBgCgotIFRoZSBjb25jbHVzaW9ucyByZW1haW4gc2ltaWxhciwgZXhjZXB0IHRoYXQgdGhlIEZXRVIgaXMgbm93IGNvbnRyb2xsZWQgYXQgJFxhbHBoYV9GPTVcJSQgYW5kIHRoYXQgdGhlICRcdGlsZGV7cH0kLXZhbHVlcyBhcmUgbGFyZ2VyIHdpdGggYSBmYWN0b3IgMy4gCgpUaGUgc2FtZSBhbmFseXNpcyBjYW4gYmUgY29uZHVjdGVkIGluIHRoZSBgbXVsdGNvbXBgIFIgcGFja2FnZSB0aGF0IGlzIGRldmVsb3BlZCBmb3IgbXVsdGlwbGUgdGVzdGluZyBpbiBsaW5lYXIgbW9kZWxzLgpgYGB7cn0KbGlicmFyeShtdWx0Y29tcCkKYGBgCgpgYGB7cn0KbW9kZWwxLm1jcDwtZ2xodChtb2RlbDEsbGluZmN0PW1jcChkb3NlPSJUdWtleSIpKQpzdW1tYXJ5KG1vZGVsMS5tY3AsdGVzdD1hZGp1c3RlZCgiYm9uZmVycm9uaSIpKQpgYGAKCk5vdGUsIHRoYXQgdGhlIHVzZXIgaGFzIHRvIGRlZmluZSBjdXN0dW0gZnVuY3Rpb25zIHRvIG9idGFpbiBCb25mZXJvbm5pIGFkanVzdGVkIGNvbmZpZGVuY2UgaW50ZXJ2YWxzLiAKCi0gQm9uZmVyb25uaSBjb25maWRlbmNlIGludGVydmFscyBhcmUgbm90IGltcGxlbWVudGVkIGJlY2F1c2UgYmV0dGVyIG1ldGhvZHMgZXhpc3QgZm9yIG11bHRpcGxlIHRlc3RpbmcuIAoKLSBUaGUgZnVuY3Rpb24gYmVsb3cgaXMgYWRkZWQgaGVyZSBtZXJlbHkgZm9yIGNvbXBsZXRlbmVzcywgYnV0IHdlIHdpbGwgZ2VuZXJhbGx5IHVzZSB0aGUgZGVmYXVsdCBtZXRob2QgZm9yIG11bHRpcGxlIHRlc3RpbmcgaW4gbXVsdGNvbXAuCgpgYGB7cn0KY2FscGhhX2Jvbl90PC1mdW5jdGlvbihvYmplY3QsbGV2ZWwpCiBhYnMoCiAgIHF0KAogICAgICgxLWxldmVsKS8yL25yb3cob2JqZWN0JGxpbmZjdCksCiAgICAgb2JqZWN0JGRmCiAgICAgKQogICAgKQpgYGAKCmBgYHtyfQpjb25maW50KG1vZGVsMS5tY3AsY2FscGhhPWNhbHBoYV9ib25fdCkKYGBgCgojIyMgRXZhbHVhdGUgQm9uZmVycm9uaSBtZXRob2QgdmlhIHNpbXVsYXRpb24KCmBgYHtyfQpnPC0zICMgbnVtYmVyIG9mIHRyZWF0bWVudHMgKGc9MykKbmk8LTEyICMgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIGdyb3VwCm48LWcqbmkgIyB0b3RhYWwgbnVtYmVyIG9ic2VydmF0aW9uCmFscGhhPC0wLjA1ICMgc2lnbmlmaWNhbmNlIGxldmVsIG9mIGluZGl2aWR1YWwgdGVzdCAKTj0xMDAwMCAjIG51bWJlciBvZiBzaW11bGF0aWVzCnNldC5zZWVkKDMwMikgI3NlZWQgdG8gcmVwcm9kdWNlIHJlc3VsdHMgZXhhY3RseSAKdHJ0PWZhY3RvcihyZXAoMTpnLG5pKSkgI2ZhY3RvcgpjbnQ8LTAgI2NvdW50ZXIgZm9yIGVycm9uZW91cyByZWplY3Rpb25zCmZvcihpIGluIDE6TikgewojaWYgKGklJTEwMDA9PTApIGNhdChpLCIvIixOLCJcbiIpCnkgPC0gcm5vcm0obikKdGVzdHM8LXBhaXJ3aXNlLnQudGVzdCh5LHRydCwiYm9uZmVycm9uaSIpCnJlamVjdDwtbWluKHRlc3RzJHAudmFsdWUsbmEucm09VCk8YWxwaGEKaWYocmVqZWN0KSBjbnQ8LWNudCsxCn0KY250L04KYGBgCgoKLSBXZSBmaW5kIGFuIEZXRVIgb2YgYHIgcm91bmQoY250L04qMTAwLDEpYCUsIHdoaWNoIGlzIHNsaWdodGx5IGNvbnNlcnZhdGl2ZS4gCi0gRm9yIHNpbXVsYXRpb25zIG9mICRnPTUkIGdyb3VwIHRoZSBGV0VSIGlzICQ0LjFcJSQgKG1vcmUgY29uc2VydmF0aXZlKS4KCi0gQnkgdXNpbmcgQm9uZmVycm9uaSB0aGUgcHJvYmFiaWxpdHkgb24gYXQgbGVhc3Qgb25lIGZhbHNlIHBvc2l0aXZlIHJlc3VsdCBpcyBsb3dlciB0aGFuICQ8IFxhbHBoYV9GJC4KLSBQb3dlciBsb3NzIGJlY2F1c2UgdGhlIHJlYWwgRldFUiBpcyBzbWFsbGVyIHRoYW4gNSUKCiMjIFR1a2V5IE1ldGhvZAoKLSBMZXNzIGNvbnNlcnZhdGlldmUKLSBJbXBsZW1lbnRhdGlvbiBhcHByb3hpbWF0ZXMgdGhlIG51bGwgZGlzdHJpYnV0aW9uIG9mIHBvc3Rob2MgdGVzdHMgdmlhIHNpbXVsYXRpb25zCi0gUmVzdWx0cyBjYW4gY2hhbmdlIHNsaWdodGx5IGlmIHRoZSBwb3N0aG9jIGFuYWx5c2lzIGlzIHJlcGVhdGVkIAotIERldGFpbHMgb24gdGhlIG1ldGhvZCBmYWxscyBvdXRzaWRlIHRoZSBzY29wZSBvZiB0aGUgc2hvcnQgY291cnNlICAKLSBJcyB0aGUgZGVmYXVsdCBtZXRob2QgaW4gdGhlIG11bHRjb21wIHBhY2thZ2U6CgogICAgLSBhZGp1c3RlZCBwLXZhbHVlcwogICAgLSBhZGp1c3RlZCBjb25maWRlbmNlIGludGVydmFscyAKICAgIAojIyMgQ2FwdG9wcmlsIGV4YW1wbGUKCmBgYHtyfQptb2RlbDEubWNwPC1nbGh0KG1vZGVsMSxsaW5mY3Q9bWNwKGRvc2U9IlR1a2V5IikpCnN1bW1hcnkobW9kZWwxLm1jcCkKYGBgCgoKYGBge3J9CmNvbmZpbnQobW9kZWwxLm1jcCkKYGBgCgoKYGBge3Igb3V0LndpZHRoPScxMDAlJywgZmlnLmFzcD0uOCwgZmlnLmFsaWduPSdjZW50ZXInfQpwbG90KGNvbmZpbnQobW9kZWwxLm1jcCkpCmBgYAoKIyMjRXZhbHVhdGUgVHVrZXkgbWV0aG9kCgpgYGB7cn0KZzwtMyAjIG51bWJlciBvZiB0cmVhdG1lbnRzIChnPTMpCm5pPC0xMiAjIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCBncm91cApuPC1nKm5pICMgdG90YWFsIG51bWJlciBvYnNlcnZhdGlvbgphbHBoYTwtMC4wNSAjIHNpZ25pZmljYW5jZSBsZXZlbCBvZiBpbmRpdmlkdWFsIHRlc3QgCk4gPC0gMTAwMDAgIyBudW1iZXIgb2Ygc2ltdWxhdGlvbnMKc2V0LnNlZWQoMzAyKSAjc2VlZCB0byByZXByb2R1Y2UgcmVzdWx0cyBleGFjdGx5IAp0cnQgPC0gZmFjdG9yKHJlcCgxOmcsbmkpKSAjZmFjdG9yCmNudDwtMCAjY291bnRlciBmb3IgZXJyb25lb3VzIHJlamVjdGlvbnMKZm9yKGkgaW4gMTpOKSB7CiNpZiAoaSUlMTAwMD09MCkgY2F0KGksIi8iLE4sIlxuIikKeSA8LSBybm9ybShuKQptPC1sbSh5fnRydCkKbS5tY3A8LWdsaHQobSxsaW5mY3Q9bWNwKHRydD0iVHVrZXkiKSkKdGVzdHM8LXN1bW1hcnkobS5tY3ApJHRlc3QKcmVqZWN0PC1taW4oYXMubnVtZXJpYyh0ZXN0cyRwdmFsdWVzKSxuYS5ybT1UKTxhbHBoYQppZihyZWplY3QpIGNudDwtY250KzEKfQpjbnQvTgpgYGAKCgojIENvbmNsdXNpb25zOiBQcm9zdGFjeWNsaW4gZXhhbXBsZSAKCkVudGlyZSBhbmFseXNpcyBmb3IgcHJvc3RhY3ljbGluIGV4YW1wbGUgCgoxLiBBbm92YSBiZWZvcmUgcG9zdGhvYyB0ZXN0czogRi10ZXN0IGhhcyBhIGhpZ2hlciBwb3dlciB0aGFuICBwYWlyd2lzZSB0LXRlc3QKCiAgICAtIEYtdGVzdCB1c2VzIGFsbCBkYXRhCiAgICAtIEZvciBGLXRlc3Qgd2UgZG8gbm90IG5lZWQgdG8gY29ycmVjdCBmb3IgbXVsdGlwbGUgdGVzdGluZzogb25lIHRlc3QgaXMgY29uZHVjdGVkIGZvciB0aGUgZ2VuZXJhbCBvbW5pYnVzIGh5cG90aGVzaXMKCmBgYHtyfQptb2RlbDEgPC0gbG0ocHJvc3RhY35kb3NlLGRhdGE9cHJvc3RhY3ljbGluKQphbm92YShtb2RlbDEpCmBgYAoKYGBge3J9Cm1vZGVsMS5tY3A8LWdsaHQobW9kZWwxLGxpbmZjdD1tY3AoZG9zZT0iVHVrZXkiKSkKc3VtbWFyeShtb2RlbDEubWNwKQpgYGAKCmBgYHtyfQpjb25maW50KG1vZGVsMS5tY3ApCmBgYAoKCi0gVGhlcmUgaXMgYW4gZXh0cmVtZSBzaWduaWZpY2FudCBlZmZlY3Qgb2YgYXJhY2hpZG9uaWMgYWNpZCBvbiB0aGUgYXZlcmFnZSBwcm9zdGFjeWNsaW4gYmxvb2QgY29uY2VudHJhdGlvbiBpbiByYXRzICgkcDwwLjAwMSQpLgpUaGUgYXZlcmFnZSBwcm9zdGFjeWNsaW4gY29uY2VudHJhdGlvbiBpcyBoaWdoZXIgaW4gdGhlIGhpZ2ggZG9zZSBncm91cCB0aGFuIGluIHRoZSBsb3cgYW5kIG1vZGVyYXRlIGRvc2UgZ3JvdXAgKGJvdGggcC12YWx1ZXMgYXJlIHNtYWxsZXIgdGhhbiAkcDwwLjAwMSQpLgotIFRoZSBhdmVyYWdlIGNvbmNlbnRyYXRpb24gaW4gdGhlIGhpZ2ggZG9zZSBncm91cCBpcyBgciByb3VuZChjb25maW50KG1vZGVsMS5tY3ApJGNvbmZpbnRbMiwxXSwxKWBuZy9tbCAoOTUlIENJIFtgciBwYXN0ZShyb3VuZChjb25maW50KG1vZGVsMS5tY3ApJGNvbmZpbnRbMiwyOjNdLDEpLGNvbGxhcHNlPSIsIilgXW5nL21sKSBhbmQgYHIgcm91bmQoY29uZmludChtb2RlbDEubWNwKSRjb25maW50WzMsMV0sMSlgbmcvbWwgKDk1JSBCSSBbYHIgcGFzdGUocm91bmQoY29uZmludChtb2RlbDEubWNwKSRjb25maW50WzMsMjozXSwxKSxjb2xsYXBzZT0iLCIpYF1uZy9tbCkgaGlnaGVyIHRoYW4gaW4gdGhlIGxvdyBhbmQgbWlkZGxlIGRvc2UgZ3JvdXAsIHJlc3BlY3RpdmVseS4KLSBUaGUgZGlmZmVyZW5jZSBpbiBhdmVyYWdlIHByb3N0YWN5Y2xpbiBjb25jZW50cmF0aW9uIGJldHdlZW4gdGhlIG1vZGVyYXRlIGFuZCBsb3cgZG9zZSBncm91cCBpcyBub3Qgc2lnbmlmaWNhbnQgIChwPWByIHJvdW5kKHN1bW1hcnkobW9kZWwxLm1jcCkkdGVzdCRwdmFsdWVzWzFdLDIpYCkuCihBbGwgcC12YWx1ZXMgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciBwb3N0LWhvYyB0ZXN0cyBhcmUgY29ycmVjdGVkIGZvciBtdWx0aXBsZSB0ZXN0aW5nIHVzaW5nIHRoZSBUdWtleSBtZXRob2QpLgoK