Prostacycline dataset
Effect van arachidonzuur op het prostacycline niveau in het bloedplasma.
3 verschillende concentraties van arachidonzuur:
- laag (L, 10 eenheden)
- gemiddeld (M, 25 eenheden)
- hoge dosis (H, 50 eenheden)
Prostacycline concentratie in bloed plasma via gecalibreerde elisa fluorescentie meting
12 ratten worden at random toegekend aan elke behandelingsgroep.
Factoriële proef, volledige gerandomiseerde proefopzet, “completely randomized design” CRD.
prostacyclin <- read_tsv("https://raw.githubusercontent.com/GTPB/PSLS20/master/data/prostacyclin.txt")
prostacyclin <- prostacyclin %>%
mutate(dose = as.factor(prostacyclin$dose))
head(prostacyclin)
# A tibble: 6 x 2
prostac dose
<dbl> <fct>
1 19.2 10
2 10.8 10
3 33.6 10
4 11.9 10
5 15.9 10
6 33.3 10
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)
Data in de drie groepen lijkt normaal verdeeld en de variantie is ongeveer gelijk: \[Y_i \vert \text{groep j} \sim N(\mu_j,\sigma^2),\] met \(j= \text{1, 2, 3}\)
Onderzoeksvraag
Vraagstelling kan vertaald worden in volgende hypotheses
\(H_0\): De arachidonzuurconcentratie heeft geen effect op het gemiddelde prostacycline niveau bij ratten \[
H_0:\mu_1=\mu_2 = \mu_3
\]
\(H_1\): De arachidonzuurconcentratie heeft een effect op het gemiddelde prostacycline niveau bij ratten. Wat betekent dat minstens twee gemiddelden verschillend zijn: \[H_1: \exists\ j,k \in \{1,\ldots,g\} : \mu_j\neq\mu_k\]
naïeve benadering: nulhypothese op splitsen in partiële hypotheses \[
H_{0jk}: \mu_j=\mu_k \text{ versus } H_{1jk}: \mu_j \neq \mu_k
\]
Elk van deze partiële hypotheses testen met two-sample \(t\)-testen
\(\rightarrow\) Probleem van meervoudig toetsen + verlies van power.
\(\rightarrow\) \(H_0:\mu_1=\mu_2=\mu_3\) testen met 1 enkele test.
Analyse van variantie
Correcte oplossing voor het testprobleem: variantie-analyse, afgekort door ANOVA (ANalysis Of VAriance)
We leiden de methode af voor de meest eenvoudige uitbreiding met 3 groepen (prostacycline voorbeeld)
Data modelleren a.d.h.v een lineair model door gebruik te maken van dummy variabelen.
1 dummy variable minder nodig hebben dan het aantal groepen. Hier dus 2 dummy variabelen.
De veralgemening naar g groepen \(g>3\) is triviaal (extra dummy variabelen)
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 uitkomst voor observatie \(i\) (\(i=1,\ldots, n\))
- \(\epsilon_i\text{i.i.d.} N(0,\sigma^2)\)
- en dummyvariabelen \[x_{i1} = \left\{ \begin{array}{ll}
1 & \text{ als observatie $i$ tot de middelste dosisgroep behoort (M)} \\
0 & \text{ als observatie $i$ behoort tot een andere dosisgroep} \end{array}\right.\] \[x_{i2} = \left\{ \begin{array}{ll}
1 & \text{ als observatie $i$ behoort tot de groep met hoge doses (H)} \\
0 & \text{als observatie $i$ behoort tot een andere dosisgroep} \end{array}\right. .\]
- Lage dosis groep (L) with \(x_{i1}=x_{i2}=0\) is referentie groep
Regressiemodel kan worden herschreven als een model voor elke groep: \[\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*}\] met \(\epsilon_i \sim N(0,\sigma^2)\)
Interpretatie van model parameters: \[\begin{eqnarray*}
\beta_0 &=& \text{E}\left[Y_i \mid \text{Behandeling met lage dosis groep L}\right] \\
\beta_1 &=& (\beta_0+\beta_1)-\beta_0 = \text{E}\left[Y_i \mid \text{behandeling M}\right] - \text{E}\left[Y_i \mid \text{behandeling L}\right] \\
\beta_2 &=& (\beta_0+\beta_2)-\beta_0 = \text{E}\left[Y_i \mid \text{behandeling H}\right]-\text{E}\left[Y_i \mid \text{behandeling L}\right].
\end{eqnarray*}\]
\(\beta_0\) is gemiddelde uitkomst in groep L
\(\beta_1\) is effect (verschil in gemiddelde concentratie) van groep M t.o.v. groep L
\(\beta_2\) is effect van groep H t.o.v. groep L
We herformuleren het model door \(\mu\)-notaties te gebruiken: \[\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*}\] met \(\epsilon_i \sim N(0,\sigma^2)\) en \[ \mu_j = \text{E}\left[Y_i \mid \text{treatment group } j\right].\]
De oorspronkelijk nulhypothese \(H_0:\mu_1=\mu_2=\mu_3\) kan equivalent geformuleerd worden als
\[H_0: \beta_1=\beta_2=0\]
Model laat toe om methoden van lineaire regressie te gebruiken voor meervoudig vergelijken van gemiddelden.
Parameterschatting van parameters, varianties en standard errors uit theorie van lineaire regressie
Inferentie: Betrouwbaarheidsintervallen, hypothesetests
Test \(H_0: \beta_1=\beta_2=0\) met \(F\)-test.
Prostacyclin voorbeeld
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
Kwadratensommen en Anova
Zoals bij enkelvoudige regressie kwadratensom van regressie gebruiken bij het opstellen van de 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*}\] met \(n_1\), \(n_2\) en \(n_3\) het aantal waarnemingen in elke groep (hier \(n-1=n_2=n_3=12\)).
\[\begin{eqnarray*}
\text{SSR}&=&\sum\limits_{i=1}^n (\hat Y_i -\bar Y)^2
\end{eqnarray*}\]
Kwadratensom opnieuw equivalent aan vergelijken van model (1) en een gereduceerd model met enkel een intercept.
Voor gereduceerd model zal intercept worden geschat door steekproefgemiddelde.
Deze kwadratensom heeft dus g-1=2 vrijheidsgraden:
- g=3 model parameters - 1 parameter voor steekproefgemiddelde of
- g=3 par. van complexe model - 1 par. van gereduceerde model.
Ontbinding van de Totale Kwadratensom
De conventie in een Anova setting is om de kwadratensom te noteren als SST, de kwadratensom van de behandeling (treatment) of als SSBetween.
De kwadratensom van de regressie geeft voor model (1) inderdaad de variabiliteit weer tussen de groepen.
De overeenkomstige gemiddelde kwadratensom wordt dan \(\text{MST}=\text{SST}/2\).
De decompositie van SSTot wordt dan geschreven als \[
\text{SSTot} = \text{SST} + \text{SSE}
\]
##SSTot
##SST
SSE
Anova test
Test \(H_0: \beta_1=\beta_2=0\) met \(F\)-test. \[
F = \frac{\text{MST}}{\text{MSE}}
\]
met
- \(\text{MST}=\text{SST}/(g-1)\)
- \(\text{MSE}=\text{SSE}/(n-p)\)
- Teststatistiek vergelijkt de variabiliteit verklaard door model (MST) met de residuele variabiliteit (MSE)
of
- Variabiliteit tussen groepen (MST) tot variabiliteit binnen groepen (MSE)
- onder \(H_0\): \(F \sim F_{g-1,n-g}\), met g=3.
Anova Tabel
Treatment |
d.f. SST |
SST |
MST |
F-statistiek |
p-waarde |
Error |
d.f. SSE |
SSE |
MSE |
|
|
Analysis of Variance Table
Response: prostac
Df Sum Sq Mean Sq F value Pr(>F)
dose 2 12658 6329.0 13.944 4.081e-05 ***
Residuals 33 14979 453.9
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
F-verdeling met kritieke waarde (\(\alpha\)=5%) en geobserveerde F-statistiek voor prostacycline voorbeeld
F-verdeling met verschillend aantal vrijheidsgraden in de noemer en teller
Prostacyclin voorbeeld: welke groepen zijn verschillend?
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
Met output van model kunnen we ook testen of de gemiddelde prostacycline concentratie verschillend is tussen de matige en lage dosis groep (\(\beta_1\): dose25) en tussen de hoge en lage dosis groep (\(\beta_2\): dose50).
De p-waarden houden geen rekening met het feit dat we meervoudig toetsen.
Post hoc analysis: Meerdere vergelijkingen van gemiddelden
Naïeve methode
In eerste deel van dit hoofdstuk hebben we \(F\)-test besproken voor het testen van
\[ 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\]
Een eerste, maar naïeve benadering: \(H_0\) opsplitsen in partiële hypotheses \[H_{0jk}: \mu_j=\mu_k \text{ versus } H_{1jk}: \mu_j \neq \mu_k\]
partiële hypotheses testen met two-sample \(t\)-testen
Voor vergelijken van groep \(j\) met groep \(k\) wordt klassieke two-sample \(t\)-test onder gelijkheid van variantie gegeven door: \[T_{jk} = \frac{\bar{Y}_j-\bar{Y}_k}{S_p\sqrt{\frac{1}{n_j}+\frac{1}{n_k}}} \sim t_{n-2}\]
Met
\(S_p^2\) de gepoolde variantieschatter is, \[S_p^2 = \frac{(n_j-1)S_j^2 + (n_k-1)S_k^2}{n_j+n_k-2}\]
met \(S_j^2\) en \(S_k^2\) de steekproefvarianties van respectievelijk de uitkomsten uit groep \(j\) en \(k\).
In ANOVA context veronderstellen we dat variantie in alle \(g\) groepen dezelfde is: de residuele variantie \(\sigma^2\).
Gebruik van \(S_p^2\) is niet efficiënt omdat die niet van alle data gebruik maakt
Aan efficiëntie winnen door door MSE te gebruiken \[\text{MSE}= \sum_{j=1}^g \frac{(n_j-1)S_j^2}{n-g}\]
De \(t\)-testen worden dus best gebaseerd op \[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
Het werken met \(m\)-testen op het \(\alpha\) significantieniveau is echter een foute aanpak die de kans op een type I fout niet onder controle kan houden.
We tonen aan dat naïve methode niet werkt via simulatie
- We simuleren uit een ANOVA model met \(g=3\) groepen.
- De gemiddelden in het ANOVA model zijn gelijk aan elkaar, zodat de nulhypothese \[H_0: \mu_1=\mu_2=\mu_3\] opgaat.
- Voor iedere gesimuleerde dataset zijn er \(m=3\) paarsgewijze two-sample \(t\)-testen
- Zodra minstens één van de \(p\)-waarden kleiner is dan het significantieniveau \(\alpha=5\%\), wordt de nulhypothese \(H_0: \mu_1=\mu_2=\mu_3\) verworpen omdat er minstens twee gemiddelden verschillend zijn volgens de \(t\)-testen.
- We rapporteren de relatieve frequentie van het verwerpen van de globale nulhypothese, meer bepaald de kans op een type I fout van de test voor \(H_0: \mu_1=\mu_2=\mu_3\).
g <- 3 # aantal behandelingen (g=3)
ni <- 12 # aantal herhalingen in iedere groep
n <- g*ni # totaal aantal observaties
alpha <- 0.05 # significantieniveau van een individuele test
N <- 10000 #aantal simulaties
set.seed(302) #seed zodat resultaten exact geproduceerd kunnen worden
trt <- factor(rep(1:g, ni)) #factor
cnt <- 0 #teller voor aantal foutieve verwerpingen
for(i in 1:N) {
if (i %% 1000 == 0) cat(i, "/", N, "\n")
y <- rnorm(n)
tests <- pairwise.t.test(y, trt, "none")
verwerp <- min(tests$p.value, na.rm = T) < alpha
if(verwerp) cnt <- cnt+1
}
1000 / 10000
2000 / 10000
3000 / 10000
4000 / 10000
5000 / 10000
6000 / 10000
7000 / 10000
8000 / 10000
9000 / 10000
10000 / 10000
[1] 0.1209
Kans op een type I fout gelijk is aan 12.1%
Is meer dan dubbel zo groot is dan vooropgestelde \(\alpha=5\)%.
Als we simulatiestudie herhalen met g = 5 groepen (i.e. 10 paarsgewijze t-testen) dan vinden we 28.0% in plaats van de gewenste 5%.
Deze simulaties illustreren het probleem van multipliciteit (Engels: multiplicity)
- klassieke \(p\)-waarden mogen enkel met het significantieniveau \(\alpha\) vergeleken worden, indien het besluit op exact één \(p\)-waarde gebaseerd is.
- Finale besluit (al dan niet verwerpen van \(H_0: \mu_1=\cdots =\mu_g\)) gebaseerd op \(m=g\times(g-1)/2\) \(p\)-waarden.
We bespreken eerst een uitbreiding van het begrip van type I fout en vervolgens introduceren we enkele oplossingen.
Family-wise error rate
- Wanneer \(m>1\) toetsen worden aangewend om 1 beslissing te vormen, is het noodzakelijk te corrigeren voor het risico op vals positieve resultaten (type I fout).
- Meeste procedures voor meervoudig toetsen gaan ervan uit dat alle \(m\) nulhypotheses waar zijn.
- Er wordt dan geprobeerd om het risico op minstens 1 vals positief resultaat te controleren op experimentgewijs significantieniveau \(\alpha_E\), typisch \(\alpha_E=0.05\).
- In de Engelstalige literatuur wordt het experimentgewijs significantieniveau family-wise error rate (FWER) genoemd.
Bonferroni correction
Bij het uitvoeren van \(m\) onafhankelijke toetsen met elk significantieniveau \(\alpha\), is \[\begin{eqnarray*}
\alpha_E&=&\text{P}[\text{minstens 1 Type I fout}]\\
&=&1-(1-\alpha)^m \leq m\alpha
\end{eqnarray*}\]
- Als we 3 toetsen uitvoeren op het 5% significantieniveau is FWER \(\approx 15\%\).
- Door ze op het 1% significantieniveau uit te voeren, bekomen we FWER \(\approx 5\%\).
- De Bonferroni correctie houdt de FWER begrensd op \(\alpha_E\) door \[\alpha=\alpha_E/m\] te kiezen voor het uitvoeren van de \(m\) paarsgewijze vergelijkingen.
Als alternatieve methode kunnen we ook
aangepaste p-waarden rapporteren zodat we deze met het experimentgewijze \(\alpha_E\) niveau kunnen vergelijken: \[\tilde{p}=min(m\times p,1)\]
\((1-\alpha_E/m)100\%\) betrouwbaarheidsintervallen rapporteren.
Prostacyclin voorbeeld
with(
prostacyclin,
pairwise.t.test(
prostac,
dose,
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
- Conclusies blijven gelijk behalve dat FWER nu gecontroleerd is \(\alpha_E=5\%\) en \(\tilde{p}\)-waarden zijn factor 3 groter
Zelfde analyse kan via multcomp
R package ontwikkeld voor multipliciteit in lineaire modellen.
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)
Om Bonferroni aangepaste betrouwbaarheidsintervallen te verkrijgen moeten we eerst zelf functie definiëren in R om bonferroni kritische waarde te bepalen.
Bonferonni-betrouwbaarheidsintervallen worden niet geïmplementeerd omdat er betere methoden bestaan voor meervoudige tests.
De onderstaande functie is hier alleen voor de volledigheid toegevoegd, maar we zullen over het algemeen de standaardmethode gebruiken voor meervoudige tests 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
Simulatie om Bonferroni methode te evalueren
g <- 3 # aantal behandelingen (g=3)
ni <- 12 # aantal herhalingen in iedere groep
n <- g*ni # totaal aantal observaties
alpha <- 0.05 # significantieniveau van een individuele test
N <- 10000 #aantal simulaties
set.seed(302) #seed zodat resultaten exact geproduceerd kunnen worden
trt <- factor(rep(1:g, ni)) #factor
cnt <- 0 #teller voor aantal foutieve verwerpingen
for(i in 1:N) {
if (i %% 1000 == 0) cat(i, "/", N, "\n")
y <- rnorm(n)
tests <- pairwise.t.test(y, trt, "bonferroni")
verwerp <- min(tests$p.value, na.rm = T) < alpha
if(verwerp) cnt <- cnt+1
}
1000 / 10000
2000 / 10000
3000 / 10000
4000 / 10000
5000 / 10000
6000 / 10000
7000 / 10000
8000 / 10000
9000 / 10000
10000 / 10000
[1] 0.0457
We vinden een FWER van 4.6% (een beetje conservatief)
Bij simulaties voor \(g=5\) groepen, vinden we een FWER van \(4.1\%\) (conservatiever).
Door Bonferroni correctie is kans op minstens één vals positief resultaat \(< \alpha_E\).
Power verlies aangezien werkelijke niveau lager is dan het vooropgestelde 5% experimentsgewijs significantieniveau.
Tukey Methode
Minder conservatieve methode
Implementatie benadert de nuldistributie van de posthoc test d.m.v. simulaties.
Resultaten kunnen lichtjes verschillen wanneer je posthoc analyse opnieuw uitvoert.
Details van de methode vallen buiten het bestek van deze cursus.
Implementatie in multcomp package:
Captopril voorbeeld
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)
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
model1.mcp %>%
confint %>%
plot
Evalueer Tukey methode in een simulatiestudie
g <- 3 # aantal behandelingen (g=3)
ni <- 12 # aantal herhalingen in iedere groep
n <- g*ni # totaal aantal observaties
alpha <- 0.05 # significantieniveau van een individuele test
N <- 10000 #aantal simulaties
set.seed(302) #seed zodat resultaten exact geproduceerd kunnen worden
trt <- factor(rep(1:g, ni)) #factor
cnt <- 0 #teller voor aantal foutieve verwerpingen
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
verwerp <- min(
as.numeric(tests$pvalues),
na.rm=T) < alpha
if(verwerp) cnt <- cnt+1
}
1000 / 10000
2000 / 10000
3000 / 10000
4000 / 10000
5000 / 10000
6000 / 10000
7000 / 10000
8000 / 10000
9000 / 10000
10000 / 10000
[1] 0.0503
De methode wordt mooi op het 5% experimentsgewijs significantieniveau gecontroleerd.
Conclusies: Prostacycline voorbeeld
Volledige analyse voor voorbeeld prostacycline
Anova vóór posthoc-tests: F-test heeft een hogere power dan paarsgewijze t-test
- F-test gebruikt alle gegevens
- Voor F-test hoeven we niet te corrigeren voor meervoudige testen: er wordt één test uitgevoerd voor de algemene omnibushypothese
model1 <- lm(prostac ~ dose, data = prostacyclin)
anova(model1)
Analysis of Variance Table
Response: prostac
Df Sum Sq Mean Sq F value Pr(>F)
dose 2 12658 6329.0 13.944 4.081e-05 ***
Residuals 33 14979 453.9
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
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)
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
- Er is een extreem significant effect van arachidonzuur op de gemiddelde bloedconcentratie van prostacycline bij ratten (\(p<0.001\)). De gemiddelde prostacyclineconcentratie is hoger in de hoge dosisgroep dan in de lage en matige dosisgroep (beide p-waardes zijn kleiner dan \(p<0.001\)).
- De gemiddelde concentratie in de hoge dosisgroep is 43.3ng/ml (95% CI [21.9,64.6]ng/ml) en 35ng/ml (95% BI [13.6,56.4]ng/ml) hoger dan in de lage en matige dosisgroep, respectievelijk.
- Het verschil in gemiddelde prostacyclineconcentratie tussen de matige en lage dosisgroep is niet significant (p=0.61). (Alle p-waardes en betrouwbaarheidsintervallen voor post-hoc-tests worden gecorrigeerd voor meervoudige tests met behulp van de Tukey-methode).
Merk op dat het belangrijk is om ook de niet significante resultaten te vermelden!
LS0tCnRpdGxlOiAiNy4gQW5hbHlzaXMgb2YgVmFyaWFuY2UiCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgpvdXRwdXQ6CiAgICBodG1sX2RvY3VtZW50OgogICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICAgIAogICAgICB0aGVtZTogY29zbW8KICAgICAgdG9jOiB0cnVlCiAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoaW5jbHVkZSA9IFRSVUUsIGNvbW1lbnQgPSBOQSwgZWNobyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKbGlicmFyeShSbWlzYykKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKCiMgUHJvc3RhY3ljbGluZSBkYXRhc2V0CgotIEVmZmVjdCB2YW4gYXJhY2hpZG9uenV1ciBvcCBoZXQgcHJvc3RhY3ljbGluZSBuaXZlYXUgaW4gaGV0IGJsb2VkcGxhc21hLgotIDMgdmVyc2NoaWxsZW5kZSBjb25jZW50cmF0aWVzIHZhbiBhcmFjaGlkb256dXVyOgoKICAtIGxhYWcgKEwsIDEwIGVlbmhlZGVuKQogIC0gZ2VtaWRkZWxkIChNLCAyNSBlZW5oZWRlbikKICAtIGhvZ2UgZG9zaXMgKEgsIDUwIGVlbmhlZGVuKQoKLSBQcm9zdGFjeWNsaW5lIGNvbmNlbnRyYXRpZSBpbiBibG9lZCBwbGFzbWEgdmlhICBnZWNhbGlicmVlcmRlIGVsaXNhIGZsdW9yZXNjZW50aWUgbWV0aW5nCi0gMTIgcmF0dGVuIHdvcmRlbiBhdCByYW5kb20gdG9lZ2VrZW5kIGFhbiBlbGtlIGJlaGFuZGVsaW5nc2dyb2VwLgotIEZhY3RvcmnDq2xlIHByb2VmLCAqdm9sbGVkaWdlIGdlcmFuZG9taXNlZXJkZSBwcm9lZm9wemV0KiwgKiJjb21wbGV0ZWx5IHJhbmRvbWl6ZWQgZGVzaWduIiBDUkQqLgoKCgoKYGBge3J9CnByb3N0YWN5Y2xpbiA8LSByZWFkX3RzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0dUUEIvUFNMUzIwL21hc3Rlci9kYXRhL3Byb3N0YWN5Y2xpbi50eHQiKQoKcHJvc3RhY3ljbGluIDwtIHByb3N0YWN5Y2xpbiAlPiUKICBtdXRhdGUoZG9zZSA9IGFzLmZhY3Rvcihwcm9zdGFjeWNsaW4kZG9zZSkpCgpoZWFkKHByb3N0YWN5Y2xpbikKYGBgCgotLS0KCiMjIERhdGEgZXhwbG9yYXRpb24KCmBgYHtyfQpwcm9zdGFjeWNsaW4gJT4lCiAgZ2dwbG90KGFlcyh4ID0gZG9zZSwgeSA9IHByb3N0YWMsIGZpbGwgPSBkb3NlKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIpICsKICB5bGFiKCJwcm9zdGFjeWNsaW4gKG5nL21sKSIpCgpwcm9zdGFjeWNsaW4gJT4lCiAgZ2dwbG90KGFlcyhzYW1wbGUgPSBwcm9zdGFjKSkgKwogIGdlb21fcXEoKSArCiAgZ2VvbV9xcV9saW5lKCkgKwogIGZhY2V0X2dyaWQofiBkb3NlKQpgYGAKCgoKRGF0YSBpbiBkZSBkcmllIGdyb2VwZW4gbGlqa3Qgbm9ybWFhbCB2ZXJkZWVsZCBlbiBkZSB2YXJpYW50aWUgaXMgb25nZXZlZXIgZ2VsaWprOgpcW1lfaSBcdmVydCBcdGV4dHtncm9lcCBqfSBcc2ltIE4oXG11X2osXHNpZ21hXjIpLFxdCm1ldCAkaj0gXHRleHR7MSwgMiwgM30kCgoKIyMgT25kZXJ6b2Vrc3ZyYWFnCgpWcmFhZ3N0ZWxsaW5nIGthbiB2ZXJ0YWFsZCB3b3JkZW4gaW4gdm9sZ2VuZGUgaHlwb3RoZXNlcwoKLSAkSF8wJDogRGUgYXJhY2hpZG9uenV1cmNvbmNlbnRyYXRpZSBoZWVmdCBnZWVuIGVmZmVjdCBvcCBoZXQgZ2VtaWRkZWxkZSBwcm9zdGFjeWNsaW5lIG5pdmVhdSBiaWogcmF0dGVuClxbCiAgSF8wOlxtdV8xPVxtdV8yID0gXG11XzMKXF0KCi0gJEhfMSQ6IERlIGFyYWNoaWRvbnp1dXJjb25jZW50cmF0aWUgaGVlZnQgZWVuIGVmZmVjdCBvcCBoZXQgZ2VtaWRkZWxkZSBwcm9zdGFjeWNsaW5lIG5pdmVhdSBiaWogcmF0dGVuLiBXYXQgYmV0ZWtlbnQgZGF0IG1pbnN0ZW5zIHR3ZWUgZ2VtaWRkZWxkZW4gdmVyc2NoaWxsZW5kIHppam46ClxbSF8xOiBcZXhpc3RzXCBqLGsgXGluIFx7MSxcbGRvdHMsZ1x9IDogXG11X2pcbmVxXG11X2tcXQoKCgoqKm5hw69ldmUgYmVuYWRlcmluZyoqOiBudWxoeXBvdGhlc2Ugb3Agc3BsaXRzZW4gaW4gKipwYXJ0acOrbGUgaHlwb3RoZXNlcyoqClxbCiAgSF97MGprfTogXG11X2o9XG11X2sgXHRleHR7IHZlcnN1cyB9IEhfezFqa306IFxtdV9qIFxuZXEgXG11X2sKXF0KCkVsayB2YW4gZGV6ZSBwYXJ0acOrbGUgaHlwb3RoZXNlcyB0ZXN0ZW4gbWV0IHR3by1zYW1wbGUgJHQkLXRlc3RlbgoKJFxyaWdodGFycm93JCBQcm9ibGVlbSB2YW4gbWVlcnZvdWRpZyB0b2V0c2VuICsgdmVybGllcyB2YW4gcG93ZXIuCgokXHJpZ2h0YXJyb3ckICAkSF8wOlxtdV8xPVxtdV8yPVxtdV8zJCB0ZXN0ZW4gbWV0ICoqMSBlbmtlbGUgdGVzdCoqLgoKCgoKIyBBbmFseXNlIHZhbiB2YXJpYW50aWUKCi0gQ29ycmVjdGUgb3Bsb3NzaW5nIHZvb3IgaGV0IHRlc3Rwcm9ibGVlbTogKip2YXJpYW50aWUtYW5hbHlzZSoqLCBhZmdla29ydCBkb29yIEFOT1ZBIChBTmFseXNpcyBPZiBWQXJpYW5jZSkKCi0gV2UgbGVpZGVuIGRlIG1ldGhvZGUgYWYgdm9vciBkZSBtZWVzdCBlZW52b3VkaWdlIHVpdGJyZWlkaW5nIG1ldCAzIGdyb2VwZW4gKHByb3N0YWN5Y2xpbmUgdm9vcmJlZWxkKQoKLSBEYXRhIG1vZGVsbGVyZW4gYS5kLmgudiBlZW4gbGluZWFpciBtb2RlbCBkb29yIGdlYnJ1aWsgdGUgbWFrZW4gdmFuIGR1bW15IHZhcmlhYmVsZW4uCgotIDEgZHVtbXkgdmFyaWFibGUgbWluZGVyIG5vZGlnIGhlYmJlbiBkYW4gaGV0IGFhbnRhbCBncm9lcGVuLiBIaWVyIGR1cyAyIGR1bW15IHZhcmlhYmVsZW4uCi0gRGUgdmVyYWxnZW1lbmluZyBuYWFyIGcgZ3JvZXBlbiAkZz4zJCBpcyB0cml2aWFhbCAoZXh0cmEgZHVtbXkgdmFyaWFiZWxlbikKCiMjIE1vZGVsCgpcYmVnaW57ZXFuYXJyYXl9CiAgWV9pICY9JiBnKHhfe2kxfSx4X3tpMn0pICsgXGVwc2lsb25faVxcCiAgWV9pICY9JiBcYmV0YV8wK1xiZXRhXzEgeF97aTF9ICtcYmV0YV8yIHhfe2kyfSArXGVwc2lsb25faQpcZW5ke2VxbmFycmF5fQoKLSAkWV9pJCBkZSB1aXRrb21zdCB2b29yIG9ic2VydmF0aWUgJGkkICgkaT0xLFxsZG90cywgbiQpClx2c3BhY2V7N3B0fQotICRcZXBzaWxvbl9pXHRleHR7aS5pLmQufSBOKDAsXHNpZ21hXjIpJApcdnNwYWNlezdwdH0KLSBlbiBkdW1teXZhcmlhYmVsZW4KJCR4X3tpMX0gPSBcbGVmdFx7IFxiZWdpbnthcnJheX17bGx9CjEgJiBcdGV4dHsgYWxzIG9ic2VydmF0aWUgJGkkIHRvdCBkZSBtaWRkZWxzdGUgZG9zaXNncm9lcCBiZWhvb3J0IChNKX0gXFwKMCAmIFx0ZXh0eyBhbHMgb2JzZXJ2YXRpZSAkaSQgYmVob29ydCB0b3QgZWVuIGFuZGVyZSBkb3Npc2dyb2VwfSBcZW5ke2FycmF5fVxyaWdodC4kJAokJHhfe2kyfSA9IFxsZWZ0XHsgXGJlZ2lue2FycmF5fXtsbH0KMSAmIFx0ZXh0eyBhbHMgb2JzZXJ2YXRpZSAkaSQgYmVob29ydCB0b3QgZGUgZ3JvZXAgbWV0IGhvZ2UgZG9zZXMgKEgpfSBcXAowICYgXHRleHR7YWxzIG9ic2VydmF0aWUgJGkkIGJlaG9vcnQgdG90IGVlbiBhbmRlcmUgZG9zaXNncm9lcH0gXGVuZHthcnJheX1ccmlnaHQuIC4kJApcdnNwYWNlezdwdH0KIC0gTGFnZSBkb3NpcyBncm9lcCAoTCkgd2l0aCAkeF97aTF9PXhfe2kyfT0wJCBpcyAgKnJlZmVyZW50aWUgZ3JvZXAqCgpSZWdyZXNzaWVtb2RlbCBrYW4gd29yZGVuIGhlcnNjaHJldmVuIGFscyBlZW4gbW9kZWwgdm9vciBlbGtlIGdyb2VwOgpcdnNwYWNley0yMHB0fQpcYmVnaW57ZXFuYXJyYXkqfQogWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9TH19ICY9JiBcYmV0YV8wK1xlcHNpbG9uX2kgXFwKIFlfe2lcdmVydCBcdGV4dHtkb3NlPU19fSAmPSYgXGJldGFfMCtcYmV0YV8xKyBcZXBzaWxvbl9pICBcXAogWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9SH19ICY9JiBcYmV0YV8wK1xiZXRhXzIgKyBcZXBzaWxvbl9pClxlbmR7ZXFuYXJyYXkqfQptZXQgJFxlcHNpbG9uX2kgXHNpbSBOKDAsXHNpZ21hXjIpJApcdnNwYWNlezEwcHR9CgpJbnRlcnByZXRhdGllIHZhbiBtb2RlbCBwYXJhbWV0ZXJzOgpcdnNwYWNley0yMHB0fQogXGJlZ2lue2VxbmFycmF5Kn0KICAgXGJldGFfMCAmPSYgIFx0ZXh0e0V9XGxlZnRbWV9pIFxtaWQgXHRleHR7QmVoYW5kZWxpbmcgbWV0IGxhZ2UgZG9zaXMgZ3JvZXAgTH1ccmlnaHRdIFxcCiAgIFxiZXRhXzEgJj0mICAoXGJldGFfMCtcYmV0YV8xKS1cYmV0YV8wID0gXHRleHR7RX1cbGVmdFtZX2kgXG1pZCBcdGV4dHtiZWhhbmRlbGluZyBNfVxyaWdodF0gLSBcdGV4dHtFfVxsZWZ0W1lfaSBcbWlkIFx0ZXh0e2JlaGFuZGVsaW5nIEx9XHJpZ2h0XSBcXAogICBcYmV0YV8yICY9JiAgKFxiZXRhXzArXGJldGFfMiktXGJldGFfMCA9IFx0ZXh0e0V9XGxlZnRbWV9pIFxtaWQgXHRleHR7YmVoYW5kZWxpbmcgSH1ccmlnaHRdLVx0ZXh0e0V9XGxlZnRbWV9pIFxtaWQgXHRleHR7YmVoYW5kZWxpbmcgTH1ccmlnaHRdLgogXGVuZHtlcW5hcnJheSp9CgogIDEuICAkXGJldGFfMCQgaXMgZ2VtaWRkZWxkZSB1aXRrb21zdCBpbiBncm9lcCBMCgogIDIuICAkXGJldGFfMSQgaXMgZWZmZWN0ICh2ZXJzY2hpbCBpbiBnZW1pZGRlbGRlIGNvbmNlbnRyYXRpZSkgdmFuIGdyb2VwIE0gdC5vLnYuIGdyb2VwIEwKCiAgMy4gICRcYmV0YV8yJCBpcyBlZmZlY3QgdmFuIGdyb2VwIEggdC5vLnYuIGdyb2VwIEwKCgpXZSBoZXJmb3JtdWxlcmVuIGhldCBtb2RlbCBkb29yICRcbXUkLW5vdGF0aWVzIHRlIGdlYnJ1aWtlbjoKIFx2c3BhY2V7LTdwdH0KIFxiZWdpbntlcW5hcnJheSp9CiAgWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9TH19ICY9JiBcYmV0YV8wK1xlcHNpbG9uX2kgPSBcbXVfMStcZXBzaWxvbl9pIFxcCiAgWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9TX19ICY9JiBcYmV0YV8wK1xiZXRhXzErIFxlcHNpbG9uX2kgPSBcbXVfMitcZXBzaWxvbl9pIFxcCiAgWV97aVx2ZXJ0IFx0ZXh0e2Rvc2U9SH19ICY9JiBcYmV0YV8wK1xiZXRhXzIgKyBcZXBzaWxvbl9pID0gXG11XzMrXGVwc2lsb25faSAuCiBcZW5ke2VxbmFycmF5Kn0KIG1ldCAkXGVwc2lsb25faSBcc2ltIE4oMCxcc2lnbWFeMikkIGVuCiAkJCAgXG11X2ogPSBcdGV4dHtFfVxsZWZ0W1lfaSBcbWlkIFx0ZXh0e3RyZWF0bWVudCBncm91cCB9IGpccmlnaHRdLiQkCgogRGUgb29yc3Byb25rZWxpamsgbnVsaHlwb3RoZXNlICRIXzA6XG11XzE9XG11XzI9XG11XzMkIGthbiBlcXVpdmFsZW50IGdlZm9ybXVsZWVyZCB3b3JkZW4gYWxzCgpcW0hfMDogXGJldGFfMT1cYmV0YV8yPTBcXQoKIE1vZGVsIGxhYXQgdG9lIG9tIG1ldGhvZGVuIHZhbiBsaW5lYWlyZSByZWdyZXNzaWUgdGUgZ2VicnVpa2VuIHZvb3IgbWVlcnZvdWRpZyB2ZXJnZWxpamtlbiB2YW4gZ2VtaWRkZWxkZW4uCgogLSBQYXJhbWV0ZXJzY2hhdHRpbmcgdmFuIHBhcmFtZXRlcnMsIHZhcmlhbnRpZXMgZW4gc3RhbmRhcmQgZXJyb3JzIHVpdCB0aGVvcmllIHZhbiBsaW5lYWlyZSByZWdyZXNzaWUKCi0gSW5mZXJlbnRpZTogQmV0cm91d2JhYXJoZWlkc2ludGVydmFsbGVuLCBoeXBvdGhlc2V0ZXN0cwoKLSBUZXN0ICRIXzA6IFxiZXRhXzE9XGJldGFfMj0wJCBtZXQgJEYkLXRlc3QuCgojIyBQcm9zdGFjeWNsaW4gdm9vcmJlZWxkCgpgYGB7cn0KbW9kZWwxIDwtIGxtKHByb3N0YWMgfiBkb3NlLCBkYXRhID0gcHJvc3RhY3ljbGluKQpzdW1tYXJ5KG1vZGVsMSkKYGBgCgoKIyBLd2FkcmF0ZW5zb21tZW4gZW4gQW5vdmEKClpvYWxzIGJpaiBlbmtlbHZvdWRpZ2UgcmVncmVzc2llIGt3YWRyYXRlbnNvbSB2YW4gcmVncmVzc2llIGdlYnJ1aWtlbiBiaWogaGV0IG9wc3RlbGxlbiB2YW4gZGUgRi10ZXN0LgpcdnNwYWNley0xMHB0fQpcYmVnaW57ZXFuYXJyYXkqfQpcdGV4dHtTU1J9Jj0mXHN1bVxsaW1pdHNfe2k9MX1ebiAoXGhhdCBZX2kgLVxiYXIgWSleMlxcCiY9JiBcc3VtXGxpbWl0c197aT0xfV5uIChcaGF0e2d9ICh4X3tpMX0seF97aTJ9KSAtIFxiYXIgWSleMlxcCiY9JiBcc3VtXGxpbWl0c197aT0xfV5uIChcaGF0XGJldGFfMCtcaGF0XGJldGFfMXhfe2kxfStcaGF0XGJldGFfMnhfe2kyfSkgLSBcYmFyIFkpXjJcXAomPSYgXHN1bVxsaW1pdHNfe2k9MX1ee25fMX0gKFxoYXRcYmV0YV8wIC0gXGJhciBZKV4yICtcc3VtXGxpbWl0c197aT0xfV57bl8yfSAoXGhhdFxiZXRhXzAgKyBcaGF0XGJldGFfMSAtIFxiYXIgWSleMitcc3VtXGxpbWl0c197aT0xfV57bl8zfSAoXGhhdFxiZXRhXzAgKyBcaGF0XGJldGFfMiAtIFxiYXIgWSleMlxcCiY9JiBcc3VtXGxpbWl0c197aT0xfV57bl8xfSAoXGJhciBZXzEtIFxiYXIgWSleMiArXHN1bVxsaW1pdHNfe2k9MX1ee25fMn0gKFxiYXIgWV8yLSBcYmFyIFkpXjIrXHN1bVxsaW1pdHNfe2k9MX1ee25fM30gKFxiYXIgWV8zIC0gXGJhciBZKV4yXFwKXGVuZHtlcW5hcnJheSp9Cm1ldCAkbl8xJCwgJG5fMiQgZW4gJG5fMyQgaGV0IGFhbnRhbCB3YWFybmVtaW5nZW4gaW4gZWxrZSBncm9lcCAoaGllciAkbi0xPW5fMj1uXzM9MTIkKS4KCgpcYmVnaW57ZXFuYXJyYXkqfQpcdGV4dHtTU1J9Jj0mXHN1bVxsaW1pdHNfe2k9MX1ebiAoXGhhdCBZX2kgLVxiYXIgWSleMgpcZW5ke2VxbmFycmF5Kn0KCi0gS3dhZHJhdGVuc29tIG9wbmlldXcgZXF1aXZhbGVudCBhYW4gdmVyZ2VsaWprZW4gdmFuIG1vZGVsICgxKSBlbiBlZW4gZ2VyZWR1Y2VlcmQgbW9kZWwgbWV0IGVua2VsIGVlbiBpbnRlcmNlcHQuCi0gVm9vciBnZXJlZHVjZWVyZCBtb2RlbCB6YWwgaW50ZXJjZXB0IHdvcmRlbiBnZXNjaGF0IGRvb3Igc3RlZWtwcm9lZmdlbWlkZGVsZGUuCi0gRGV6ZSBrd2FkcmF0ZW5zb20gaGVlZnQgZHVzIGctMT0yIHZyaWpoZWlkc2dyYWRlbjogIAoKICAtIGc9MyBtb2RlbCBwYXJhbWV0ZXJzIC0gMSBwYXJhbWV0ZXIgdm9vciBzdGVla3Byb2VmZ2VtaWRkZWxkZSBvZgogIC0gZz0zIHBhci4gdmFuIGNvbXBsZXhlIG1vZGVsIC0gMSBwYXIuIHZhbiBnZXJlZHVjZWVyZGUgbW9kZWwuCgoKIyMgT250YmluZGluZyB2YW4gZGUgVG90YWxlIEt3YWRyYXRlbnNvbQoKLSBEZSBjb252ZW50aWUgaW4gZWVuIEFub3ZhIHNldHRpbmcgaXMgb20gZGUga3dhZHJhdGVuc29tIHRlIG5vdGVyZW4gYWxzIFNTVCwgZGUgKiprd2FkcmF0ZW5zb20gdmFuIGRlIGJlaGFuZGVsaW5nICh0cmVhdG1lbnQpKiogb2YgYWxzIFNTQmV0d2Vlbi4KCgotIERlIGt3YWRyYXRlbnNvbSB2YW4gZGUgcmVncmVzc2llIGdlZWZ0IHZvb3IgbW9kZWwgKDEpIGluZGVyZGFhZCBkZSB2YXJpYWJpbGl0ZWl0IHdlZXIgdHVzc2VuIGRlIGdyb2VwZW4uCgotIERlIG92ZXJlZW5rb21zdGlnZSBnZW1pZGRlbGRlIGt3YWRyYXRlbnNvbSB3b3JkdCBkYW4gJFx0ZXh0e01TVH09XHRleHR7U1NUfS8yJC4KCkRlIGRlY29tcG9zaXRpZSB2YW4gU1NUb3Qgd29yZHQgZGFuIGdlc2NocmV2ZW4gYWxzCiAgXFsKICAgIFx0ZXh0e1NTVG90fSA9IFx0ZXh0e1NTVH0gKyBcdGV4dHtTU0V9CiAgXF0KCiMjU1NUb3QKCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBhcihtZnJvdz1jKDEsMikpCmppdElrPXJ1bmlmKDM2LC0uMiwuMikrcmVwKDE6MyxlYWNoPTEyKQpwbG90KHByb3N0YWN+ZG9zZSxkYXRhPXByb3N0YWN5Y2xpbix4bGFiPSJBcmFjaGlkb25pYyBhY2lkIGRvc2UgIix5bGFiPSJQcm9zdGFjeWNsaW4gKG5nL21sKSIsY2V4LmF4aXM9MS41LGNleC5sYWI9MS41LGNleC5tYWluPTEuNSkKcG9pbnRzKGppdElrLHByb3N0YWN5Y2xpbiRwcm9zdGFjLGNvbD1jb2wscGNoPTE5KQpwb2ludHMoaml0SWsscHJvc3RhY3ljbGluJHByb3N0YWMsY29sPTQpCnBvaW50cygxOjMscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKSxwY2g9MTcsY29sPWMoImJpc3F1ZSIsImNvcmFsIiwiZGFya2N5YW4iKSxjZXg9MS41KQpwb2ludHMoMTozLHByZWRpY3QobW9kZWwxLGRhdGEuZnJhbWUoZG9zZT1mYWN0b3IoYygxMCwyNSw1MCkpKSkscGNoPTIsY29sPTEsY2V4PTEuNSkKYWJsaW5lKGg9bWVhbihwcm9zdGFjeWNsaW4kcHJvc3RhYyksbHR5PTEpCmZvciAoaSBpbiAxOjM2KSBsaW5lcyhyZXAoaml0SWtbaV0sMiksYyhtZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxwcm9zdGFjeWNsaW4kcHJvc3RhY1tpXSksY29sPTQsbHR5PTIpCmppdElrPXJ1bmlmKDM2LC0uMiwuMikrcmVwKDE6MyxlYWNoPTEyKQoKcGxvdChyZXAoMSwzNikscHJvc3RhY3ljbGluJHByb3N0YWMtbWVhbihwcm9zdGFjeWNsaW4kcHJvc3RhYykseGF4dD0ibm9uZSIseWxhYj0iRGV2aWF0aW9ucyIsY2V4LmxhYj0xLjUsY2V4Lm1haW49MS41LGNleC5heGlzPTEuNSxjb2w9YXMuY2hhcmFjdGVyKHByb3N0YWN5Y2xpbiRjb2wpLHhsaW09YygxLDMpLHBjaD0xOSx4bGFiPSIiKQpwb2ludHMocmVwKDEsMzYpLHByb3N0YWN5Y2xpbiRwcm9zdGFjLW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHBjaD0xLGNvbD00KQpheGlzKGF0PTE6MyxsYWJlbHM9YyhleHByZXNzaW9uKHBhc3RlKHlbaV0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZShiYXIoeSlbal0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZSh5W2ldLCIgLSAiLGJhcih5KVtqXSkpKSxzaWRlPTEsY2V4LmF4aXM9MS41KQpgYGAKCgojI1NTVAoKYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdChwcm9zdGFjfmRvc2UsZGF0YT1wcm9zdGFjeWNsaW4seGxhYj0iQXJhY2hpZG9uaWMgYWNpZCBkb3NlICIseWxhYj0iUHJvc3RhY3ljbGluIChuZy9tbCkiLGNleC5heGlzPTEuNSxjZXgubGFiPTEuNSxjZXgubWFpbj0xLjUpCnBvaW50cyhqaXRJayxwcm9zdGFjeWNsaW4kcHJvc3RhYyxjb2w9Y29sLHBjaD0xOSkKcG9pbnRzKGppdElrLHByb3N0YWN5Y2xpbiRwcm9zdGFjLGNvbD00KQpwb2ludHMoMTozLHByZWRpY3QobW9kZWwxLGRhdGEuZnJhbWUoZG9zZT1mYWN0b3IoYygxMCwyNSw1MCkpKSkscGNoPTE3LGNvbD1jKCJiaXNxdWUiLCJjb3JhbCIsImRhcmtjeWFuIiksY2V4PTEuNSkKcG9pbnRzKDE6MyxwcmVkaWN0KG1vZGVsMSxkYXRhLmZyYW1lKGRvc2U9ZmFjdG9yKGMoMTAsMjUsNTApKSkpLHBjaD0yLGNvbD0yLGNleD0xLjUpCmFibGluZShoPW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLGx0eT0xKQpmb3IgKGkgaW4gMTozKSBsaW5lcyhyZXAoaSwyKSxjKG1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHByZWRpY3QobW9kZWwxLGRhdGEuZnJhbWUoZG9zZT1sZXZlbHMocHJvc3RhY3ljbGluJGRvc2UpW2ldKSkpLGNvbD0yLGx0eT0yKQoKcGxvdChyZXAoMSwzNikscHJvc3RhY3ljbGluJHByb3N0YWMtbWVhbihwcm9zdGFjeWNsaW4kcHJvc3RhYykseGF4dD0ibm9uZSIseWxhYj0iRGV2aWF0aW9ucyIsY2V4LmxhYj0xLjUsY2V4Lm1haW49MS41LGNleC5heGlzPTEuNSxjb2w9YXMuY2hhcmFjdGVyKHByb3N0YWN5Y2xpbiRjb2wpLHhsaW09YygxLDMpLHBjaD0xOSx4bGFiPSIiKQpwb2ludHMocmVwKDEsMzYpLHByb3N0YWN5Y2xpbiRwcm9zdGFjLW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHBjaD0xLGNvbD00KQpwb2ludHMocmVwKDIsMykscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKS1tZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxwY2g9MTcsY29sPXVuaXF1ZShwcm9zdGFjeWNsaW4kY29sKSxjZXg9MS41KQpwb2ludHMocmVwKDIsMykscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKS1tZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxwY2g9MixjZXg9MS41LGNvbD0yKQpheGlzKGF0PTE6MyxsYWJlbHM9YyhleHByZXNzaW9uKHBhc3RlKHlbaV0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZShiYXIoeSlbal0sIiAtICIsYmFyKHkpKSksZXhwcmVzc2lvbihwYXN0ZSh5W2ldLCIgLSAiLGJhcih5KVtqXSkpKSxzaWRlPTEsY2V4LmF4aXM9MS41KQpgYGAKCiMjIFNTRQoKYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdChwcm9zdGFjfmRvc2UsZGF0YT1wcm9zdGFjeWNsaW4seGxhYj0iQXJhY2hpZG9uaWMgYWNpZCBkb3NlICIseWxhYj0iUHJvc3RhY3ljbGluIChuZy9tbCkiLGNleC5heGlzPTEuNSxjZXgubGFiPTEuNSxjZXgubWFpbj0xLjUpCnBvaW50cyhqaXRJayxwcm9zdGFjeWNsaW4kcHJvc3RhYyxjb2w9Y29sLHBjaD0xOSkKcG9pbnRzKGppdElrLHByb3N0YWN5Y2xpbiRwcm9zdGFjLGNvbD0xKQpwb2ludHMoMTozLHByZWRpY3QobW9kZWwxLGRhdGEuZnJhbWUoZG9zZT1mYWN0b3IoYygxMCwyNSw1MCkpKSkscGNoPTE3LGNvbD1jKCJiaXNxdWUiLCJjb3JhbCIsImRhcmtjeWFuIiksY2V4PTEuNSkKcG9pbnRzKDE6MyxwcmVkaWN0KG1vZGVsMSxkYXRhLmZyYW1lKGRvc2U9ZmFjdG9yKGMoMTAsMjUsNTApKSkpLHBjaD0yLGNvbD0yLGNleD0xLjUpCmZvciAoaSBpbiAxOjMpIGxpbmVzKGMoaS0uMixpKy4yKSxyZXAocHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWxldmVscyhwcm9zdGFjeWNsaW4kZG9zZSlbaV0pKSwyKSxjb2w9YygiYmlzcXVlIiwiY29yYWwiLCJkYXJrY3lhbiIpW2ldKQphYmxpbmUoaD1tZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxsdHk9MSkKZm9yIChpIGluIDE6MzYpIGxpbmVzKHJlcChqaXRJa1tpXSwyKSxjKHByb3N0YWN5Y2xpbiRwcm9zdGFjW2ldLG1vZGVsMSRmaXR0ZWRbaV0pLGNvbD0xLGx0eT0yKQoKcGxvdChyZXAoMSwzNikscHJvc3RhY3ljbGluJHByb3N0YWMtbWVhbihwcm9zdGFjeWNsaW4kcHJvc3RhYykseGF4dD0ibm9uZSIseWxhYj0iRGV2aWF0aW9ucyIsY2V4LmxhYj0xLjUsY2V4Lm1haW49MS41LGNleC5heGlzPTEuNSxjb2w9YXMuY2hhcmFjdGVyKHByb3N0YWN5Y2xpbiRjb2wpLHhsaW09YygxLDMpLHBjaD0xOSx4bGFiPSIiKQpwb2ludHMocmVwKDEsMzYpLHByb3N0YWN5Y2xpbiRwcm9zdGFjLW1lYW4ocHJvc3RhY3ljbGluJHByb3N0YWMpLHBjaD0xLGNvbD00KQpwb2ludHMocmVwKDIsMykscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKS1tZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxwY2g9MTcsY29sPXVuaXF1ZShwcm9zdGFjeWNsaW4kY29sKSxjZXg9MS41KQpwb2ludHMocmVwKDIsMykscHJlZGljdChtb2RlbDEsZGF0YS5mcmFtZShkb3NlPWZhY3RvcihjKDEwLDI1LDUwKSkpKS1tZWFuKHByb3N0YWN5Y2xpbiRwcm9zdGFjKSxwY2g9Mixjb2w9MixjZXg9MS41KQpwb2ludHMocmVwKDMsMzYpLG1vZGVsMSRyZXMscGNoPTE5LGNvbD1hcy5jaGFyYWN0ZXIocHJvc3RhY3ljbGluJGNvbCkpCnBvaW50cyhyZXAoMywzNiksbW9kZWwxJHJlcyxwY2g9MSkKYXhpcyhhdD0xOjMsbGFiZWxzPWMoZXhwcmVzc2lvbihwYXN0ZSh5W2ldLCIgLSAiLGJhcih5KSkpLGV4cHJlc3Npb24ocGFzdGUoYmFyKHkpW2pdLCIgLSAiLGJhcih5KSkpLGV4cHJlc3Npb24ocGFzdGUoeVtpXSwiIC0gIixiYXIoeSlbal0pKSksc2lkZT0xLGNleC5heGlzPTEuNSkKYGBgCgoKIyMgQW5vdmEgdGVzdAoKVGVzdCAkSF8wOiBcYmV0YV8xPVxiZXRhXzI9MCQgbWV0ICRGJC10ZXN0LgpcWwogIEYgPSBcZnJhY3tcdGV4dHtNU1R9fXtcdGV4dHtNU0V9fQpcXQoKbWV0CgotICRcdGV4dHtNU1R9PVx0ZXh0e1NTVH0vKGctMSkkICAKXHZzcGFjZXsxMHB0fQotICRcdGV4dHtNU0V9PVx0ZXh0e1NTRX0vKG4tcCkkClx2c3BhY2V7MTBwdH0KLSBUZXN0c3RhdGlzdGllayB2ZXJnZWxpamt0IGRlIHZhcmlhYmlsaXRlaXQgdmVya2xhYXJkIGRvb3IgbW9kZWwgKE1TVCkgbWV0IGRlIHJlc2lkdWVsZSB2YXJpYWJpbGl0ZWl0IChNU0UpCgpvZgoKLSBWYXJpYWJpbGl0ZWl0IHR1c3NlbiBncm9lcGVuIChNU1QpIHRvdCB2YXJpYWJpbGl0ZWl0IGJpbm5lbiBncm9lcGVuIChNU0UpClx2c3BhY2V7MTBwdH0KLSBvbmRlciAkSF8wJDogJEYgXHNpbSBGX3tnLTEsbi1nfSQsIG1ldCBnPTMuCgoKIyMgQW5vdmEgVGFiZWwKCnwgfERmfFN1bSBTcXxNZWFuIFNxfEYgdmFsdWV8UHIoPkYpfAp8LS0tfC0tLXwtLS18LS0tfC0tLXwtLS18CnxUcmVhdG1lbnR8ZC5mLiBTU1R8U1NUfE1TVHxGLXN0YXRpc3RpZWt8cC13YWFyZGV8CnxFcnJvcnxkLmYuIFNTRXxTU0V8TVNFfCB8IHwKCmBgYHtyfQphbm92YShtb2RlbDEpCmBgYAoKCiMjIyBGLXZlcmRlbGluZyBtZXQga3JpdGlla2Ugd2FhcmRlICAoJFxhbHBoYSQ9NSUpIGVuIGdlb2JzZXJ2ZWVyZGUgRi1zdGF0aXN0aWVrIHZvb3IgcHJvc3RhY3ljbGluZSB2b29yYmVlbGQKYGBge3IgcHJvc3RhY0YsIG91dC53aWR0aD0nMTAwJScsIGZpZy5hc3A9LjgsIGZpZy5hbGlnbj0nY2VudGVyJyxlY2hvPUZBTFNFfQpncmlkIDwtIHNlcSgwLDE3LC4wMSkKZGYxPWFub3ZhKG1vZGVsMSlbMSwxXQpkZjI9YW5vdmEobW9kZWwxKVsyLDFdCmZ2YWw9YW5vdmEobW9kZWwxKVsxLDRdCmNyaXQ9cWYoMC45NSxkZjEsZGYyKQpyZWplY3Q9Yyhjcml0LGdyaWRbd2hpY2goZ3JpZD5jcml0KV0pCmFjY2VwdD1jKGdyaWRbd2hpY2goZ3JpZDxjcml0KV0sY3JpdCkKcGxvdChncmlkLGRmKGdyaWQsZGYxLGRmMiksdHlwZT0ibCIseWxhYj0iRGVuc2l0eSIseGxhYj0iRi1zdGF0aXN0aWMiLGNleC5heGlzPTEuNSxjZXgubGFiPTEuNSkKcG9seWdvbihjKDAsYWNjZXB0LGNyaXQsMCksYygwLGRmKGFjY2VwdCxkZjEsZGYyKSwwLDApLGNvbD0iYmx1ZSIsYm9yZGVyPSJibHVlIikKdGV4dChjcml0LzIsLjk3LGxhYmVscz0iYWNjZXB0XG45NSUiLGNvbD0iYmx1ZSIsY2V4PTEuNSkKcG9seWdvbihjKGNyaXQscmVqZWN0LDE1LGNyaXQpLGMoMCxkZihyZWplY3QsZGYxLGRmMiksMCwwKSxjb2w9InJlZCIsYm9yZGVyPSJyZWQiKQphYmxpbmUodj1jcml0LGNvbD0icmVkIixsd2Q9MikKdGV4dChjcml0KyhmdmFsLWNyaXQpLzIsLjk3LGxhYmVscz0icmVqZWN0XG41JSIsY29sPSJyZWQiLGNleD0xLjUpCnRleHQocG9zPTQsY3JpdCxkZihjcml0LDIsMzMpLGxhYmVscz1wYXN0ZTAoIkYoMC4wNSwiLGRmMSwiLCIsZGYyLCIpIiksY29sPSJyZWQiLGNleD0xLjUpCnRleHQocG9zPTQsZnZhbCxkZihjcml0LGRmMSxkZjIpLGxhYmVscz1wYXN0ZTAoImY9Iixyb3VuZChmdmFsLDEpKSxjb2w9ImRhcmtvcmFuZ2UiLGNleD0xLjUpCmFibGluZSh2PWZ2YWwsY29sPSJkYXJrb3JhbmdlIixsd2Q9MixsdHk9MikKdGV4dCgxNS41LC45NyxsYWJlbHM9cGFzdGUwKCJwLXZhbHVlXG4iLGZvcm1hdChhbm92YShtb2RlbDEpWzEsNV0sZGlnaXRzPTIpKSxjb2w9ImRhcmtvcmFuZ2UiLGNleD0xLjUpCmFycm93cyh4MD0xNy41LHgxPWZ2YWwseTA9LjkseTE9LjksY29sPSJkYXJrb3JhbmdlIikKYGBgCgoKIyMjIEYtdmVyZGVsaW5nIG1ldCB2ZXJzY2hpbGxlbmQgYWFudGFsIHZyaWpoZWlkc2dyYWRlbiBpbiBkZSBub2VtZXIgZW4gdGVsbGVyCmBgYHtyIGZ0aGVvLCBvdXQud2lkdGg9JzEwMCUnLCBmaWcuYXNwPS44LCBmaWcuYWxpZ249J2NlbnRlcicsZWNobz1GQUxTRX0KcGxvdChncmlkLGRmKGdyaWQsMSw1KSx0eXBlPSJsIix5bGFiPSJEZW5zaXR5Iix4bGFiPSJGLXN0YXRpc3RpYyIseGxpbT1jKDAsNSkseWxpbT1jKDAsMS41KSxsd2Q9MixjZXguYXhpcz0xLjUsY2V4LmxhYj0xLjUpCmxpbmVzKGdyaWQsZGYoZ3JpZCw1LDUpLHR5cGU9ImwiLGNvbD0yLGx3ZD0yKQpsaW5lcyhncmlkLGRmKGdyaWQsMTAsMzApLHR5cGU9ImwiLGNvbD0zLGx3ZD0yKQpsaW5lcyhncmlkLGRmKGdyaWQsMjAsMzApLHR5cGU9ImwiLGNvbD00LGx3ZD0yKQpsaW5lcyhncmlkLGRmKGdyaWQsNTAsNTApLHR5cGU9ImwiLGNvbD01LGx3ZD0yKQpsZWdlbmQoInRvcHJpZ2h0IixsdHk9MSxjb2w9YygxLDIsMyw0LDUpLGxlZ2VuZD1jKCJGKDEsNSkiLCJGKDUsNSkiLCJGKDEwLDMwKSIsIkYoMjAsMzApIiwiRig1MCw1MCkiKSxsd2Q9MixjZXg9MS41KQpgYGAKCiMjIyBQcm9zdGFjeWNsaW4gdm9vcmJlZWxkOiB3ZWxrZSBncm9lcGVuIHppam4gdmVyc2NoaWxsZW5kPwoKYGBge3J9CnN1bW1hcnkobW9kZWwxKQpgYGAKCgpNZXQgb3V0cHV0IHZhbiBtb2RlbCBrdW5uZW4gd2Ugb29rIHRlc3RlbiBvZiBkZSBnZW1pZGRlbGRlIHByb3N0YWN5Y2xpbmUgY29uY2VudHJhdGllIHZlcnNjaGlsbGVuZCBpcyB0dXNzZW4gZGUgbWF0aWdlIGVuIGxhZ2UgZG9zaXMgZ3JvZXAgKCRcYmV0YV8xJDogZG9zZTI1KSBlbiB0dXNzZW4gZGUgaG9nZSBlbiBsYWdlIGRvc2lzIGdyb2VwICgkXGJldGFfMiQ6IGRvc2U1MCkuCgpEZSBwLXdhYXJkZW4gaG91ZGVuIGdlZW4gcmVrZW5pbmcgbWV0IGhldCBmZWl0IGRhdCB3ZSBtZWVydm91ZGlnIHRvZXRzZW4uCgojIFBvc3QgaG9jIGFuYWx5c2lzOiBNZWVyZGVyZSB2ZXJnZWxpamtpbmdlbiB2YW4gZ2VtaWRkZWxkZW4KCiMjIE5hw69ldmUgbWV0aG9kZQoKCgpJbiBlZXJzdGUgZGVlbCB2YW4gZGl0IGhvb2Zkc3R1ayBoZWJiZW4gd2UgJEYkLXRlc3QgYmVzcHJva2VuIHZvb3IgaGV0IHRlc3RlbiB2YW4KCiQkICBIXzA6IFxtdV8xPVxjZG90cyA9IFxtdV9nIFx0ZXh0eyB2ZXJzdXMgfSBIXzE6IEhfMTogXGV4aXN0c1wgaixrIFxpbiBcezEsXGxkb3RzLGdcfSA6IFxtdV9qXG5lcVxtdV9rJCQKCi0gQWxzIHdlICRIXzAkIHZlcndlcnBlbiBiZXNsdWl0ZW4gd2UgZGF0IGVyIG1pbnN0ZW5zIHR3ZWUgZ2VtaWRkZWxkZW4gdmVyc2NoaWxsZW4gdmFuIGVsa2Fhci4KCi0gRGUgbWV0aG9kZSBuaWV0IGluIHN0YWF0IG9tIHRlIGlkZW50aWZpY2VyZW4gd2Vsa2UgZ2VtaWRkZWxkZW4gdmFuIGVsa2FhciB2ZXJzY2hpbGxlbi4KCgpFZW4gZWVyc3RlLCBtYWFyIG5hw69ldmUgYmVuYWRlcmluZzogICRIXzAkIG9wc3BsaXRzZW4gaW4gcGFydGnDq2xlIGh5cG90aGVzZXMKJCRIX3swamt9OiBcbXVfaj1cbXVfayBcdGV4dHsgdmVyc3VzIH0gSF97MWprfTogXG11X2ogXG5lcSBcbXVfayQkCgotIHBhcnRpw6tsZSBoeXBvdGhlc2VzIHRlc3RlbiBtZXQgdHdvLXNhbXBsZSAkdCQtdGVzdGVuCgotIFZvb3IgdmVyZ2VsaWprZW4gdmFuIGdyb2VwICRqJCBtZXQgZ3JvZXAgJGskIHdvcmR0ICBrbGFzc2lla2UgdHdvLXNhbXBsZSAkdCQtdGVzdCBvbmRlciBnZWxpamtoZWlkIHZhbiB2YXJpYW50aWUgZ2VnZXZlbiBkb29yOgokJFRfe2prfSA9IFxmcmFje1xiYXJ7WX1fai1cYmFye1l9X2t9e1NfcFxzcXJ0e1xmcmFjezF9e25fan0rXGZyYWN7MX17bl9rfX19IFxzaW0gdF97bi0yfSQkCgpNZXQKCi0gJFNfcF4yJCBkZSBnZXBvb2xkZSB2YXJpYW50aWVzY2hhdHRlciBpcywKJCRTX3BeMiA9IFxmcmFjeyhuX2otMSlTX2peMiArIChuX2stMSlTX2teMn17bl9qK25fay0yfSQkCgotIG1ldCAkU19qXjIkIGVuICRTX2teMiQgZGUgc3RlZWtwcm9lZnZhcmlhbnRpZXMgdmFuIHJlc3BlY3RpZXZlbGlqayBkZSB1aXRrb21zdGVuIHVpdCBncm9lcCAkaiQgZW4gJGskLgoKLS0tCgoKSW4gQU5PVkEgY29udGV4dCB2ZXJvbmRlcnN0ZWxsZW4gd2UgZGF0IHZhcmlhbnRpZSBpbiAgKiphbGxlKiogJGckIGdyb2VwZW4gZGV6ZWxmZGUgaXM6IGRlIHJlc2lkdWVsZSB2YXJpYW50aWUgJFxzaWdtYV4yJC4KCi0gR2VicnVpayB2YW4gJFNfcF4yJCBpcyBuaWV0IGVmZmljacOrbnQgb21kYXQgZGllIG5pZXQgdmFuIGFsbGUgZGF0YSBnZWJydWlrIG1hYWt0CgotIEFhbiBlZmZpY2nDq250aWUgd2lubmVuIGRvb3IgZG9vciBNU0UgdGUgZ2VicnVpa2VuCiQkXHRleHR7TVNFfT0gXHN1bV97aj0xfV5nIFxmcmFjeyhuX2otMSlTX2peMn17bi1nfSQkCgotIERlICR0JC10ZXN0ZW4gd29yZGVuIGR1cyBiZXN0IGdlYmFzZWVyZCBvcAokJFRfe2prfSA9IFxmcmFje1xiYXJ7WX1fai1cYmFye1l9X2t9e1x0ZXh0e01TRX1cc3FydHtcZnJhY3sxfXtuX2p9K1xmcmFjezF9e25fa319fSBcc2ltIHRfe24tZ30uJCQKCi0tLQoKYGBge3J9CndpdGgoCiAgcHJvc3RhY3ljbGluLAogIHBhaXJ3aXNlLnQudGVzdChwcm9zdGFjLCBkb3NlLCAibm9uZSIpCiAgKQpgYGAKCkhldCB3ZXJrZW4gbWV0ICRtJC10ZXN0ZW4gb3AgaGV0ICRcYWxwaGEkIHNpZ25pZmljYW50aWVuaXZlYXUgaXMgZWNodGVyIGVlbiBmb3V0ZSBhYW5wYWsgZGllIGRlIGthbnMgb3AgZWVuIHR5cGUgSSBmb3V0IG5pZXQgb25kZXIgY29udHJvbGUga2FuIGhvdWRlbi4KCiMjIFdlIHRvbmVuIGFhbiBkYXQgbmHDr3ZlIG1ldGhvZGUgbmlldCB3ZXJrdCB2aWEgc2ltdWxhdGllCgoxLiBXZSBzaW11bGVyZW4gdWl0IGVlbiBBTk9WQSBtb2RlbCBtZXQgJGc9MyQgZ3JvZXBlbi4KMi4gRGUgZ2VtaWRkZWxkZW4gaW4gaGV0IEFOT1ZBIG1vZGVsIHppam4gZ2VsaWprIGFhbiBlbGthYXIsIHpvZGF0IGRlIG51bGh5cG90aGVzZSAkJEhfMDogXG11XzE9XG11XzI9XG11XzMkJCBvcGdhYXQuCjMuIFZvb3IgaWVkZXJlIGdlc2ltdWxlZXJkZSBkYXRhc2V0IHppam4gZXIgJG09MyQgcGFhcnNnZXdpanplIHR3by1zYW1wbGUgJHQkLXRlc3Rlbgo0LiBab2RyYSBtaW5zdGVucyDDqcOpbiB2YW4gZGUgJHAkLXdhYXJkZW4ga2xlaW5lciBpcyBkYW4gaGV0IHNpZ25pZmljYW50aWVuaXZlYXUgJFxhbHBoYT01XCUkLCB3b3JkdCBkZSBudWxoeXBvdGhlc2UgJEhfMDogXG11XzE9XG11XzI9XG11XzMkIHZlcndvcnBlbiBvbWRhdCBlciBtaW5zdGVucyB0d2VlIGdlbWlkZGVsZGVuIHZlcnNjaGlsbGVuZCB6aWpuIHZvbGdlbnMgZGUgJHQkLXRlc3Rlbi4KNS4gV2UgcmFwcG9ydGVyZW4gZGUgcmVsYXRpZXZlIGZyZXF1ZW50aWUgdmFuIGhldCB2ZXJ3ZXJwZW4gdmFuIGRlIGdsb2JhbGUgbnVsaHlwb3RoZXNlLCBtZWVyIGJlcGFhbGQgZGUga2FucyBvcCBlZW4gdHlwZSBJIGZvdXQgdmFuIGRlIHRlc3Qgdm9vciAgJEhfMDogXG11XzE9XG11XzI9XG11XzMkLgoKCmBgYHtyfQpnIDwtIDMgIyBhYW50YWwgYmVoYW5kZWxpbmdlbiAoZz0zKQpuaSA8LSAxMiAjIGFhbnRhbCBoZXJoYWxpbmdlbiBpbiBpZWRlcmUgZ3JvZXAKbiA8LSBnKm5pICMgdG90YWFsIGFhbnRhbCBvYnNlcnZhdGllcwphbHBoYSA8LSAwLjA1ICMgc2lnbmlmaWNhbnRpZW5pdmVhdSB2YW4gZWVuIGluZGl2aWR1ZWxlIHRlc3QKTiA8LSAxMDAwMCAjYWFudGFsIHNpbXVsYXRpZXMKc2V0LnNlZWQoMzAyKSAjc2VlZCB6b2RhdCByZXN1bHRhdGVuIGV4YWN0IGdlcHJvZHVjZWVyZCBrdW5uZW4gd29yZGVuCnRydCA8LSBmYWN0b3IocmVwKDE6ZywgbmkpKSAjZmFjdG9yCmNudCA8LSAwICN0ZWxsZXIgdm9vciBhYW50YWwgZm91dGlldmUgdmVyd2VycGluZ2VuCgpmb3IoaSBpbiAxOk4pIHsKaWYgKGkgJSUgMTAwMCA9PSAwKSBjYXQoaSwgIi8iLCBOLCAiXG4iKQp5IDwtIHJub3JtKG4pCnRlc3RzIDwtIHBhaXJ3aXNlLnQudGVzdCh5LCB0cnQsICJub25lIikKdmVyd2VycCA8LSBtaW4odGVzdHMkcC52YWx1ZSwgbmEucm0gPSBUKSA8IGFscGhhCmlmKHZlcndlcnApIGNudCA8LSBjbnQrMQp9CmNudC9OCmBgYAoKLS0tCgotIEthbnMgb3AgZWVuIHR5cGUgSSBmb3V0IGdlbGlqayBpcyBhYW4gYHIgcm91bmQoY250L04sMykqMTAwYCUKCi0gSXMgbWVlciBkYW4gZHViYmVsIHpvIGdyb290IGlzIGRhbiB2b29yb3BnZXN0ZWxkZSAkXGFscGhhPTUkJS4KCi0gQWxzIHdlIHNpbXVsYXRpZXN0dWRpZSBoZXJoYWxlbiBtZXQgZyA9IDUgZ3JvZXBlbiAoaS5lLiAxMCBwYWFyc2dld2lqemUgdC10ZXN0ZW4pIGRhbiB2aW5kZW4gd2UgMjguMCUgaW4gcGxhYXRzIHZhbiBkZSBnZXdlbnN0ZSA1JS4KCi0gRGV6ZSBzaW11bGF0aWVzIGlsbHVzdHJlcmVuIGhldCBwcm9ibGVlbSB2YW4gKiptdWx0aXBsaWNpdGVpdCoqIChFbmdlbHM6ICptdWx0aXBsaWNpdHkqKQoKICAtIGtsYXNzaWVrZSAkcCQtd2FhcmRlbiAgbW9nZW4gZW5rZWwgbWV0IGhldCBzaWduaWZpY2FudGllbml2ZWF1ICRcYWxwaGEkIHZlcmdlbGVrZW4gd29yZGVuLCBpbmRpZW4gaGV0IGJlc2x1aXQgb3AgZXhhY3Qgw6nDqW4gJHAkLXdhYXJkZSBnZWJhc2VlcmQgaXMuCiAgLSBGaW5hbGUgYmVzbHVpdCAoYWwgZGFuIG5pZXQgdmVyd2VycGVuIHZhbiAkSF8wOiBcbXVfMT1cY2RvdHMgPVxtdV9nJCkgZ2ViYXNlZXJkIG9wICRtPWdcdGltZXMoZy0xKS8yJCAkcCQtd2FhcmRlbi4KCi0gV2UgYmVzcHJla2VuIGVlcnN0IGVlbiB1aXRicmVpZGluZyB2YW4gaGV0IGJlZ3JpcCB2YW4gdHlwZSBJIGZvdXQgZW4gdmVydm9sZ2VucyBpbnRyb2R1Y2VyZW4gd2UgZW5rZWxlIG9wbG9zc2luZ2VuLgoKIyMgRmFtaWx5LXdpc2UgZXJyb3IgcmF0ZQoKLSBXYW5uZWVyICRtPjEkIHRvZXRzZW4gd29yZGVuIGFhbmdld2VuZCBvbSAxIGJlc2xpc3NpbmcgdGUgdm9ybWVuLCBpcyBoZXQgbm9vZHpha2VsaWprIHRlIGNvcnJpZ2VyZW4gdm9vciBoZXQgcmlzaWNvIG9wIHZhbHMgcG9zaXRpZXZlIHJlc3VsdGF0ZW4gKHR5cGUgSSBmb3V0KS4KXHZzcGFjZXsxMHB0fQotIE1lZXN0ZSBwcm9jZWR1cmVzIHZvb3IgbWVlcnZvdWRpZyB0b2V0c2VuIGdhYW4gZXJ2YW4gdWl0IGRhdCAqYWxsZSAkbSQgbnVsaHlwb3RoZXNlcyB3YWFyKiB6aWpuLgpcdnNwYWNlezEwcHR9Ci0gRXIgd29yZHQgZGFuIGdlcHJvYmVlcmQgb20gaGV0ICpyaXNpY28gb3AgbWluc3RlbnMgMSB2YWxzIHBvc2l0aWVmIHJlc3VsdGFhdCogdGUgY29udHJvbGVyZW4gb3AgKipleHBlcmltZW50Z2V3aWpzIHNpZ25pZmljYW50aWVuaXZlYXUgJFxhbHBoYV9FJCoqLCB0eXBpc2NoICRcYWxwaGFfRT0wLjA1JC4KXHZzcGFjZXsxMHB0fQotIEluIGRlIEVuZ2Vsc3RhbGlnZSBsaXRlcmF0dXVyIHdvcmR0IGhldCBleHBlcmltZW50Z2V3aWpzIHNpZ25pZmljYW50aWVuaXZlYXUgKmZhbWlseS13aXNlIGVycm9yIHJhdGUgKEZXRVIpKiBnZW5vZW1kLgoKCiMjIEJvbmZlcnJvbmkgY29ycmVjdGlvbgoKQmlqIGhldCB1aXR2b2VyZW4gdmFuICRtJCBvbmFmaGFua2VsaWprZSB0b2V0c2VuIG1ldCBlbGsgc2lnbmlmaWNhbnRpZW5pdmVhdSAkXGFscGhhJCwgaXMKXGJlZ2lue2VxbmFycmF5Kn0KXGFscGhhX0UmPSZcdGV4dHtQfVtcdGV4dHttaW5zdGVucyAxIFR5cGUgSSBmb3V0fV1cXAomPSYxLSgxLVxhbHBoYSlebSBcbGVxIG1cYWxwaGEKXGVuZHtlcW5hcnJheSp9CgotIEFscyB3ZSAzIHRvZXRzZW4gdWl0dm9lcmVuIG9wIGhldCA1XCUgc2lnbmlmaWNhbnRpZW5pdmVhdSBpcyBGV0VSICRcYXBwcm94IDE1XCUkLgpcdnNwYWNlezEwcHR9Ci0gRG9vciB6ZSBvcCBoZXQgMVwlIHNpZ25pZmljYW50aWVuaXZlYXUgdWl0IHRlIHZvZXJlbiwgYmVrb21lbiB3ZSBGV0VSICRcYXBwcm94IDVcJSQuClx2c3BhY2V7MTBwdH0KLSBEZSBCb25mZXJyb25pIGNvcnJlY3RpZSBob3VkdCBkZSBGV0VSIGJlZ3JlbnNkIG9wICRcYWxwaGFfRSQgZG9vciAkJFxhbHBoYT1cYWxwaGFfRS9tJCQgdGUga2llemVuIHZvb3IgaGV0IHVpdHZvZXJlbiB2YW4gZGUgJG0kIHBhYXJzZ2V3aWp6ZSB2ZXJnZWxpamtpbmdlbi4KCi0tLQoKQWxzIGFsdGVybmF0aWV2ZSBtZXRob2RlIGt1bm5lbiB3ZSBvb2sKCiAgMS4gKmFhbmdlcGFzdGUgcC13YWFyZGVuKiByYXBwb3J0ZXJlbiB6b2RhdCB3ZSBkZXplIG1ldCBoZXQgZXhwZXJpbWVudGdld2lqemUgJFxhbHBoYV9FJCBuaXZlYXUga3VubmVuIHZlcmdlbGlqa2VuOiAkJFx0aWxkZXtwfT1taW4obVx0aW1lcyBwLDEpJCQKCiAgMi4gJCgxLVxhbHBoYV9FL20pMTAwXCUkIGJldHJvdXdiYWFyaGVpZHNpbnRlcnZhbGxlbiByYXBwb3J0ZXJlbi4KCiMjIyBQcm9zdGFjeWNsaW4gdm9vcmJlZWxkCgpgYGB7cn0Kd2l0aCgKICBwcm9zdGFjeWNsaW4sCiAgcGFpcndpc2UudC50ZXN0KAogICAgcHJvc3RhYywKICAgIGRvc2UsCiAgICBwLmFkanVzdC5tZXRob2Q9ImJvbmZlcnJvbmkiKQogICkKYGBgCgotIENvbmNsdXNpZXMgYmxpanZlbiBnZWxpamsgYmVoYWx2ZSBkYXQgRldFUiBudSBnZWNvbnRyb2xlZXJkIGlzICRcYWxwaGFfRT01XCUkIGVuICRcdGlsZGV7cH0kLXdhYXJkZW4gemlqbiBmYWN0b3IgMyBncm90ZXIKCi0tLQoKWmVsZmRlIGFuYWx5c2Uga2FuIHZpYSBgbXVsdGNvbXBgIFIgcGFja2FnZSBvbnR3aWtrZWxkIHZvb3IgbXVsdGlwbGljaXRlaXQgaW4gbGluZWFpcmUgbW9kZWxsZW4uCgpgYGB7cn0KbGlicmFyeShtdWx0Y29tcCkKYGBgCgpgYGB7cn0KbGlicmFyeShtdWx0Y29tcCkKbW9kZWwxLm1jcCA8LSBnbGh0KG1vZGVsMSwgbGluZmN0ID0gbWNwKGRvc2UgPSAiVHVrZXkiKSkKc3VtbWFyeShtb2RlbDEubWNwLCB0ZXN0ID0gYWRqdXN0ZWQoImJvbmZlcnJvbmkiKSkKYGBgCgotLS0KCk9tIEJvbmZlcnJvbmkgYWFuZ2VwYXN0ZSBiZXRyb3V3YmFhcmhlaWRzaW50ZXJ2YWxsZW4gdGUgdmVya3JpamdlbiBtb2V0ZW4gd2UgZWVyc3QgemVsZiBmdW5jdGllIGRlZmluacOrcmVuIGluIFIgb20gYm9uZmVycm9uaSBrcml0aXNjaGUgd2FhcmRlIHRlIGJlcGFsZW4uCgotIEJvbmZlcm9ubmktYmV0cm91d2JhYXJoZWlkc2ludGVydmFsbGVuIHdvcmRlbiBuaWV0IGdlw69tcGxlbWVudGVlcmQgb21kYXQgZXIgYmV0ZXJlIG1ldGhvZGVuIGJlc3RhYW4gdm9vciBtZWVydm91ZGlnZSB0ZXN0cy4KCi0gRGUgb25kZXJzdGFhbmRlIGZ1bmN0aWUgaXMgaGllciBhbGxlZW4gdm9vciBkZSB2b2xsZWRpZ2hlaWQgdG9lZ2V2b2VnZCwgbWFhciB3ZSB6dWxsZW4gb3ZlciBoZXQgYWxnZW1lZW4gZGUgc3RhbmRhYXJkbWV0aG9kZSBnZWJydWlrZW4gdm9vciBtZWVydm91ZGlnZSB0ZXN0cyBpbiBtdWx0Y29tcC4KCmBgYHtyfQpjYWxwaGFfYm9uX3QgPC0gZnVuY3Rpb24ob2JqZWN0LCBsZXZlbCkKewogIGFicygKICAgIHF0KAogICAgICAoMS1sZXZlbCkvMi9ucm93KG9iamVjdCRsaW5mY3QpLAogICAgICBvYmplY3QkZGYpCiAgICApCn0KYGBgCgpgYGB7cn0KY29uZmludChtb2RlbDEubWNwLCBjYWxwaGEgPSBjYWxwaGFfYm9uX3QpCmBgYAoKIyMjIFNpbXVsYXRpZSBvbSBCb25mZXJyb25pIG1ldGhvZGUgdGUgZXZhbHVlcmVuCgpgYGB7cn0KZyA8LSAzICMgYWFudGFsIGJlaGFuZGVsaW5nZW4gKGc9MykKbmkgPC0gMTIgIyBhYW50YWwgaGVyaGFsaW5nZW4gaW4gaWVkZXJlIGdyb2VwCm4gPC0gZypuaSAjIHRvdGFhbCBhYW50YWwgb2JzZXJ2YXRpZXMKYWxwaGEgPC0gMC4wNSAjIHNpZ25pZmljYW50aWVuaXZlYXUgdmFuIGVlbiBpbmRpdmlkdWVsZSB0ZXN0Ck4gPC0gMTAwMDAgI2FhbnRhbCBzaW11bGF0aWVzCnNldC5zZWVkKDMwMikgI3NlZWQgem9kYXQgcmVzdWx0YXRlbiBleGFjdCBnZXByb2R1Y2VlcmQga3VubmVuIHdvcmRlbgp0cnQgPC0gZmFjdG9yKHJlcCgxOmcsIG5pKSkgI2ZhY3RvcgpjbnQgPC0gMCAjdGVsbGVyIHZvb3IgYWFudGFsIGZvdXRpZXZlIHZlcndlcnBpbmdlbgoKZm9yKGkgaW4gMTpOKSB7CmlmIChpICUlIDEwMDAgPT0gMCkgY2F0KGksICIvIiwgTiwgIlxuIikKeSA8LSBybm9ybShuKQp0ZXN0cyA8LSBwYWlyd2lzZS50LnRlc3QoeSwgdHJ0LCAiYm9uZmVycm9uaSIpCnZlcndlcnAgPC0gbWluKHRlc3RzJHAudmFsdWUsIG5hLnJtID0gVCkgPCBhbHBoYQppZih2ZXJ3ZXJwKSBjbnQgPC0gY250KzEKfQpjbnQvTgpgYGAKCgoKLSBXZSB2aW5kZW4gZWVuIEZXRVIgdmFuIGByIHJvdW5kKGNudC9OKjEwMCwxKWAlIChlZW4gYmVldGplIGNvbnNlcnZhdGllZikKCi0gQmlqIHNpbXVsYXRpZXMgdm9vciAkZz01JCBncm9lcGVuLCB2aW5kZW4gd2UgZWVuIEZXRVIgdmFuICQ0LjFcJSQgKGNvbnNlcnZhdGlldmVyKS4KCi0gRG9vciBCb25mZXJyb25pIGNvcnJlY3RpZSBpcyBrYW5zIG9wIG1pbnN0ZW5zIMOpw6luIHZhbHMgcG9zaXRpZWYgcmVzdWx0YWF0ICQ8IFxhbHBoYV9FJC4KCi0gUG93ZXIgdmVybGllcyBhYW5nZXppZW4gd2Vya2VsaWprZSBuaXZlYXUgbGFnZXIgaXMgZGFuIGhldCB2b29yb3BnZXN0ZWxkZSA1JSBleHBlcmltZW50c2dld2lqcyBzaWduaWZpY2FudGllbml2ZWF1LgoKIyMgVHVrZXkgTWV0aG9kZQoKCi0gTWluZGVyIGNvbnNlcnZhdGlldmUgbWV0aG9kZQoKLSBJbXBsZW1lbnRhdGllIGJlbmFkZXJ0IGRlIG51bGRpc3RyaWJ1dGllIHZhbiBkZSBwb3N0aG9jIHRlc3QgZC5tLnYuIHNpbXVsYXRpZXMuCgotIFJlc3VsdGF0ZW4ga3VubmVuIGxpY2h0amVzIHZlcnNjaGlsbGVuIHdhbm5lZXIgamUgcG9zdGhvYyBhbmFseXNlIG9wbmlldXcgdWl0dm9lcnQuCgotIERldGFpbHMgdmFuIGRlIG1ldGhvZGUgdmFsbGVuIGJ1aXRlbiBoZXQgYmVzdGVrIHZhbiBkZXplIGN1cnN1cy4KCi0gSW1wbGVtZW50YXRpZSBpbiBtdWx0Y29tcCBwYWNrYWdlOgoKICAgIC0gYWFuZ2VwYXN0ZSBwLXdhYXJkZW4KCiAgICAtIGFhbmdlcGFzdGUgYmV0cm91d2JhYXJoZWlkc2ludGVydmFsbGVuCgogICAgLSBOaWV0IG5vZGlnIG9tIGZ1bmN0aWVzIHRlIGRlZmluacOrcmVuIHZvb3IgVHVrZXkgZ2Vjb3JyaWdlZXJkZSBCSXMKCgojIyMgQ2FwdG9wcmlsIHZvb3JiZWVsZAoKYGBge3J9Cm1vZGVsMS5tY3AgPC0gZ2xodChtb2RlbDEsIGxpbmZjdCA9IG1jcChkb3NlID0gIlR1a2V5IikpCnN1bW1hcnkobW9kZWwxLm1jcCkKYGBgCgoKYGBge3J9CmNvbmZpbnQobW9kZWwxLm1jcCkKYGBgCgoKYGBge3Igb3V0LndpZHRoPScxMDAlJywgZmlnLmFzcD0uOCwgZmlnLmFsaWduPSdjZW50ZXInfQptb2RlbDEubWNwICU+JQogIGNvbmZpbnQgJT4lCiAgcGxvdApgYGAKCiMjIyBFdmFsdWVlciBUdWtleSBtZXRob2RlIGluIGVlbiBzaW11bGF0aWVzdHVkaWUKCmBgYHtyfQpnIDwtIDMgIyBhYW50YWwgYmVoYW5kZWxpbmdlbiAoZz0zKQpuaSA8LSAxMiAjIGFhbnRhbCBoZXJoYWxpbmdlbiBpbiBpZWRlcmUgZ3JvZXAKbiA8LSBnKm5pICMgdG90YWFsIGFhbnRhbCBvYnNlcnZhdGllcwphbHBoYSA8LSAwLjA1ICMgc2lnbmlmaWNhbnRpZW5pdmVhdSB2YW4gZWVuIGluZGl2aWR1ZWxlIHRlc3QKTiA8LSAxMDAwMCAjYWFudGFsIHNpbXVsYXRpZXMKc2V0LnNlZWQoMzAyKSAjc2VlZCB6b2RhdCByZXN1bHRhdGVuIGV4YWN0IGdlcHJvZHVjZWVyZCBrdW5uZW4gd29yZGVuCnRydCA8LSBmYWN0b3IocmVwKDE6ZywgbmkpKSAjZmFjdG9yCmNudCA8LSAwICN0ZWxsZXIgdm9vciBhYW50YWwgZm91dGlldmUgdmVyd2VycGluZ2VuCgpmb3IoaSBpbiAxOk4pIHsKaWYgKGkgJSUgMTAwMCA9PSAwKSBjYXQoaSwgIi8iLCBOLCAiXG4iKQp5IDwtIHJub3JtKG4pCm0gPC0gbG0oeSB+IHRydCkKbS5tY3AgPC0gZ2xodChtLCBsaW5mY3QgPSBtY3AodHJ0ID0gIlR1a2V5IikpCnRlc3RzIDwtIHN1bW1hcnkobS5tY3ApJHRlc3QKdmVyd2VycCA8LSBtaW4oCiAgYXMubnVtZXJpYyh0ZXN0cyRwdmFsdWVzKSwKICBuYS5ybT1UKSA8IGFscGhhCmlmKHZlcndlcnApIGNudCA8LSBjbnQrMQp9CmNudC9OCmBgYAoKRGUgbWV0aG9kZSB3b3JkdCBtb29pIG9wIGhldCA1JSBleHBlcmltZW50c2dld2lqcyBzaWduaWZpY2FudGllbml2ZWF1IGdlY29udHJvbGVlcmQuCgojIENvbmNsdXNpZXM6IFByb3N0YWN5Y2xpbmUgdm9vcmJlZWxkCgpWb2xsZWRpZ2UgYW5hbHlzZSB2b29yIHZvb3JiZWVsZCBwcm9zdGFjeWNsaW5lCgoxLiBBbm92YSB2w7PDs3IgcG9zdGhvYy10ZXN0czogRi10ZXN0IGhlZWZ0IGVlbiBob2dlcmUgcG93ZXIgZGFuIHBhYXJzZ2V3aWp6ZSB0LXRlc3QKCiAgICAtIEYtdGVzdCBnZWJydWlrdCBhbGxlIGdlZ2V2ZW5zCiAgICAtIFZvb3IgRi10ZXN0IGhvZXZlbiB3ZSBuaWV0IHRlIGNvcnJpZ2VyZW4gdm9vciBtZWVydm91ZGlnZSB0ZXN0ZW46IGVyIHdvcmR0IMOpw6luIHRlc3QgdWl0Z2V2b2VyZCB2b29yIGRlIGFsZ2VtZW5lIG9tbmlidXNoeXBvdGhlc2UKCmBgYHtyfQptb2RlbDEgPC0gbG0ocHJvc3RhYyB+IGRvc2UsIGRhdGEgPSBwcm9zdGFjeWNsaW4pCmFub3ZhKG1vZGVsMSkKYGBgCgpgYGB7cn0KbW9kZWwxLm1jcCA8LSBnbGh0KG1vZGVsMSwgbGluZmN0ID0gbWNwKGRvc2UgPSAiVHVrZXkiKSkKc3VtbWFyeShtb2RlbDEubWNwKQpgYGAKCmBgYHtyfQpjb25maW50KG1vZGVsMS5tY3ApCmBgYAoKCi0gRXIgaXMgZWVuIGV4dHJlZW0gc2lnbmlmaWNhbnQgZWZmZWN0IHZhbiBhcmFjaGlkb256dXVyIG9wIGRlIGdlbWlkZGVsZGUgYmxvZWRjb25jZW50cmF0aWUgdmFuIHByb3N0YWN5Y2xpbmUgYmlqIHJhdHRlbiAoJHA8MC4wMDEkKS4KRGUgZ2VtaWRkZWxkZSBwcm9zdGFjeWNsaW5lY29uY2VudHJhdGllIGlzIGhvZ2VyIGluIGRlIGhvZ2UgZG9zaXNncm9lcCBkYW4gaW4gZGUgbGFnZSBlbiBtYXRpZ2UgZG9zaXNncm9lcCAoYmVpZGUgcC13YWFyZGVzIHppam4ga2xlaW5lciBkYW4gJHA8MC4wMDEkKS4KLSBEZSBnZW1pZGRlbGRlIGNvbmNlbnRyYXRpZSBpbiBkZSBob2dlIGRvc2lzZ3JvZXAgaXMgYHIgcm91bmQoY29uZmludChtb2RlbDEubWNwKSRjb25maW50WzIsMV0sMSlgbmcvbWwgKDk1JSBDSSBbYHIgcGFzdGUocm91bmQoY29uZmludChtb2RlbDEubWNwKSRjb25maW50WzIsMjozXSwxKSxjb2xsYXBzZT0iLCIpYF1uZy9tbCkgZW4gYHIgcm91bmQoY29uZmludChtb2RlbDEubWNwKSRjb25maW50WzMsMV0sMSlgbmcvbWwgKDk1JSBCSSBbYHIgcGFzdGUocm91bmQoY29uZmludChtb2RlbDEubWNwKSRjb25maW50WzMsMjozXSwxKSxjb2xsYXBzZT0iLCIpYF1uZy9tbCkgaG9nZXIgZGFuIGluIGRlIGxhZ2UgZW4gbWF0aWdlIGRvc2lzZ3JvZXAsIHJlc3BlY3RpZXZlbGlqay4KLSBIZXQgdmVyc2NoaWwgaW4gZ2VtaWRkZWxkZSBwcm9zdGFjeWNsaW5lY29uY2VudHJhdGllIHR1c3NlbiBkZSBtYXRpZ2UgZW4gbGFnZSBkb3Npc2dyb2VwIGlzIG5pZXQgc2lnbmlmaWNhbnQgIChwPWByIHJvdW5kKHN1bW1hcnkobW9kZWwxLm1jcCkkdGVzdCRwdmFsdWVzWzFdLDIpYCkuCihBbGxlIHAtd2FhcmRlcyBlbiBiZXRyb3V3YmFhcmhlaWRzaW50ZXJ2YWxsZW4gdm9vciBwb3N0LWhvYy10ZXN0cyB3b3JkZW4gZ2Vjb3JyaWdlZXJkIHZvb3IgbWVlcnZvdWRpZ2UgdGVzdHMgbWV0IGJlaHVscCB2YW4gZGUgVHVrZXktbWV0aG9kZSkuCgpNZXJrIG9wIGRhdCBoZXQgYmVsYW5ncmlqayBpcyBvbSBvb2sgZGUgbmlldCBzaWduaWZpY2FudGUgcmVzdWx0YXRlbiB0ZSB2ZXJtZWxkZW4hCgotLS0KCiMgW0hvbWVdKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pby9zYmMyMC8pIHstfQo=