Breast cancer dataset
brca <- read_csv("https://raw.githubusercontent.com/statOmics/sbc20/master/data/breastcancer.csv")
brca
# A tibble: 32 x 10
sample_name filename treatment er grade node size age ESR1 S100A8
<chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 OXFT_209 gsm65344.ceā¦ tamoxifen 1 3 1 2.5 66 1939. 207.
2 OXFT_1769 gsm65345.ceā¦ tamoxifen 1 1 1 3.5 86 2752. 37.0
3 OXFT_2093 gsm65347.ceā¦ tamoxifen 1 1 1 2.2 74 379. 2364.
4 OXFT_1770 gsm65348.ceā¦ tamoxifen 1 1 1 1.7 69 2532. 23.6
5 OXFT_1342 gsm65350.ceā¦ tamoxifen 1 3 0 2.5 62 141. 3219.
6 OXFT_2338 gsm65352.ceā¦ tamoxifen 1 3 1 1.4 63 1495. 108.
7 OXFT_2341 gsm65353.ceā¦ tamoxifen 1 1 1 3.3 76 3406. 14.0
8 OXFT_1902 gsm65354.ceā¦ tamoxifen 1 3 0 2.4 61 2813. 68.4
9 OXFT_1982 gsm65355.ceā¦ tamoxifen 1 1 0 1.7 62 950. 74.2
10 OXFT_5210 gsm65356.ceā¦ tamoxifen 1 3 0 3.5 65 1053. 182.
# ā¦ with 22 more rows
- Om didactische redenen verwijderen we eerst 3 outliers in de S100A8 expressie data.
- In deze studie kan dit echter niet worden verantwoord
- Later in de lessen laten we zien hoe je goed met alle data omgaat.
brca %>%
ggplot(aes(x="", y=S100A8)) +
geom_boxplot() +
xlab("") +
ylab("S100A8 expressie")
library(GGally)
brcaSubset <- brca %>%
filter(S100A8<2000)
brcaSubset[,-(1:4)] %>% ggpairs()
Associatie tussen ESR1 en S100A8 expressie
ESR1 in \(\pm\) 75% van borstkankertumoren.
- Expressie van ER-gen positief voor behandeling: tumor reageert op hormoontherapie
- Tamoxifen interageert met ER en moduleert genexpressie.
Eiwitten van de S100-familie zijn vaak gedisreguleerd bij kanker
S100A8 expressie onderdrukt immuunsysteem in tumor en creƫert inflamatoir milieu die kankergroei promoot.
Interesse in associatie tussen ESR1 en S100A8 expressie.
- pipe dataset naar ggplot
- selecteer data
ggplot(aes(x=ESR1,y=S100A8))
- voeg punten toe met
geom_point()
- voeg een āsmooth lineā toe
geom_smooth()
brcaSubset %>%
ggplot(aes(x=ESR1,y=S100A8)) +
geom_point() +
geom_smooth()
Lineaire Regressie
Statistische methode om relatie tussen 2 reeksen observaties \((X_i, Y_i)\), bekomen voor onafhankelijke subjecten \(i = 1, ..., n\), te beschrijven.
Gen expressie voorbeeld
- Response Y : S100A8 expressie
- Predictor X: ESR1 expressie
- pipe dataset naar ggplot
- selecteer data
ggplot(aes(x=ESR1,y=S100A8))
- voeg punten toe met
geom_point()
- voeg een āsmooth lineā toe
geom_smooth()
- voeg een rechte toe
geom_smooth()
met method = "lm"
(linear model). (We zetten se = FALSE
om geen puntgewijze betrouwbaarheidsintervallen weer te geven)
brcaSubset %>%
ggplot(aes(x = ESR1,y = S100A8)) +
geom_point() +
geom_smooth(se = FALSE, col = "grey") +
geom_smooth(method = "lm", se = FALSE)
Model
- Voor vaste \(X\), heeft \(Y\) niet noodzakelijke dezelfde waarde
\[\text{observation = signal + noise}\]
\[Y_i=g(X_i)+\epsilon_i\]
- We definiƫren \(g(x)\) als het verwachte resultaat voor subjects met \(X_i=x\)
\[E[Y_i|X_i=x]=g(x)\]
Daarom is \(\epsilon_i\) gemiddeld 0 voor subjects met dezelfde \(X_i\): \[E[\epsilon_i|X_i]=0\]
Lineaire regressie
- Om accurate en interpreteerbare resultaten te bekomen, kiest men \(g(x)\) vaak als een lineaire functie met ongekende parameters.
\[E(Y|X=x)=\beta_0 + \beta_1 x\]
onbekend intercept \(\beta_0\) en helling \(\beta_1\).
Lineair model legt een assumptie op de verdeling van \(X\) en \(Y\), die incorrect kan zijn.
Efficiƫnte data-analyse: benut alle observaties om iets te leren over verwachte uitkomst bij \(X=x\).
Gebruik
Predictie: wanneer \(Y\) ongekend is, maar \(X\) wel, kunnen we \(Y\) voorspellen op basis van \(X\) \[E(Y|X=x)=\beta_0 + \beta_1 x\]
Associatie: biologische relatie tussen variabele \(X\) en continue meting \(Y\) beschrijven.
Intercept: \(E(Y|X=0)=\beta_0\)
Slope: \[\begin{eqnarray*}
E(Y|X=x+\delta)-E(Y|X=x)&=&\beta_0 + \beta_1 (x+\delta) -\beta_0-\beta_1 x\\
&=& \beta_1\delta
\end{eqnarray*}\]
\(\beta_1:\) verschil in gemiddelde uitkomst voor subjecten die verschillen in Ć©Ć©n eenheid van de predictor \(X\).
Parameterschatting
- Kleinste kwadraten techniek (Least squares)
brcaSubset %>%
ggplot(aes(x = ESR1, y = S100A8)) +
geom_point() +
geom_smooth(se = FALSE, col = "grey") +
geom_smooth(method = "lm", se = FALSE)
\[SSE=\sum_{i=1}^n (y_i-\beta_0-\beta_1 x_i)^2=\sum_{i=1}^n e_i^2\]
- Met \(e_i\) de residuen: de verticale afstanden van de observaties tot de gefitte regressierechte
Schatters die SSE minimaliseren
\[\hat{\beta_1}= \frac{\sum\limits_{i=1}^n (y_i-\bar y)(x_i-\bar x)}{\sum\limits_{i=1}^n (x_i-\bar x_i)^2}=\frac{\mbox{cor}(x,y)s_y}{s_x} \]
\[\hat{\beta_0}=\bar y - \hat{\beta}_1 \bar x \]
- Merk op dat de helling van de kleinste kwadratenlijn evenredig is met de correlatie tussen de uitkomst en de verklarende variabele.
Geschatte lineaire regressiemodel laat toe om:
verwachte uitkomst te voorspellen voor subjecten met een gegeven waarde \(x\) voor de predictor: \[\text{E} [ Y | X = x]=\hat{\beta}_0+\hat{\beta}_1x\]
na te gaan hoeveel uitkomst gemiddeld verschilt tussen 2 groepen subjecten met een verschil van \(\delta\) eenheden in de verklarende variabele:
\[\text{E}\left[Y|X=x+\delta\right]-\text{E}\left[Y|X=x\right]= \hat{\beta}_1\delta\]
Borstkanker voorbeeld
lm1 <- lm(S100A8 ~ ESR1, brcaSubset)
summary(lm1)
Call:
lm(formula = S100A8 ~ ESR1, data = brcaSubset)
Residuals:
Min 1Q Median 3Q Max
-95.43 -34.81 -6.79 34.23 145.21
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 208.47145 28.57207 7.296 7.56e-08 ***
ESR1 -0.05926 0.01212 -4.891 4.08e-05 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 59.91 on 27 degrees of freedom
Multiple R-squared: 0.4698, Adjusted R-squared: 0.4502
F-statistic: 23.93 on 1 and 27 DF, p-value: 4.078e-05
\[E(Y|X=x)=208.47-0.059 x\]
De verwachte S100A8-expressie is gemiddeld 59 eenheden lager voor patiƫnten met een ESR1-expressieniveau die 1000 eenheden hoger ligt.
Verwachte S100A8 expressieniveau voor patiƫnten met een ESR1 expressieniveau van 2000:
\[208.47-0.059\times 2000=89.94\]
Verwachte S100A8 expressieniveau voor patiƫnten met een ESR1 expressieniveau van 4000:
\[208.47-0.059\times 4000=-28.58\]
Let op voor extrapolatie! (Veronderstelling van lineariteit kan men enkel nagaan binnen het bereik van de data).
Statistische besluitvorming (statistische inferentie)
Om besluiten te kunnen trekken over lineaire regressiemodel \[E(Y|X)=\beta_0+\beta_1 X\]
moeten we weten:
Hoe de least squares parameter schatters variƫren van steekproef tot steekproef, en
En hoe is dit onder de nulhypothese dat er geen associatie is tussen predictor en response
Noodzaak aan statistisch model!
Modelleer de verdeling van \(Y\) gegeven \(X\) expliciet: \(f_{Y|X}(y)\)
Modelleer verdeling van Y?
- Naast lineariteit hebben we nog aannames nodig!
- Onafhankelijkheid: waarnemingen \((X_1,Y_1), ..., (X_n,Y_n)\) zijn gemaakt voor n onafhankelijke subjecten (Is vereist om de variantie te schatten)
- Homoscedasticiteit of gelijke varianties: waarnemingen variƫren met gelijk gemiddelde rond de regressielijn.
- Residuen \(\epsilon_i\) hebben gelijke variantie voor elke \(X_i=x\)
- \(\text{var}(Y\vert X=x) = \sigma^2\) voor elke \(X=x\)
- \(\sigma\) wordt de residuele standaarddeviatie genoemd.
- Normaliteit: de residuen \(\epsilon_i\) zijn normaal verdeeld
Uit 2, 3 en 4 volgt dat \[\epsilon_i \text{ i.i.d.} N(0,\sigma^2).\]
Als we ook steunen op eerste veronderstelling van lineariteit: \[Y_i\vert X_i\sim N(\beta_0+\beta_1 X_i,\sigma^2),\]
Verder kan men aantonen dat onder deze aannames \[\sigma^2_{\hat{\beta}_0}=\frac{\sum\limits_{i=1}^n X^2_i}{\sum\limits_{i=1}^n (X_i-\bar X)^2} \times\frac{\sigma^2}{n} \text{ en } \sigma^2_{\hat{\beta}_1}=\frac{\sigma^2}{\sum\limits_{i=1}^n (X_i-\bar X)^2}\]
en dat de parameterschatters eveneens normaal verdeeld zijn \[\hat\beta_0 \sim N\left(\beta_0,\sigma^2_{\hat \beta_0}\right) \text{ en } \hat\beta_1 \sim N\left(\beta_1,\sigma^2_{\hat \beta_1}\right)\]
Hoge spreiding op \(X\) verbetert de precisie
\[\sigma^2_{\hat{\beta}_1}=\frac{\sigma^2}{\sum\limits_{i=1}^n (X_i-\bar X)^2}\]
- Conditionele variantie (\(\sigma^2\)) is niet gekend
- Schatten d.m.v. gemiddelde van die kwadratische afwijkingen rond de regressierechte
- mean squared error (MSE)
\[\hat\sigma^2=MSE=\frac{\sum\limits_{i=1}^n \left(y_i-\hat\beta_0-\hat\beta_1\times x_i\right)^2}{n-2}=\frac{\sum\limits_{i=1}^n e^2_i}{n-2}.\] - Voor het bekomen van deze schatter steunen we op onafhankelijkheid (aanname 2) en homoscedasticiteit (aanname 3). - deel door \(n-2\)
Na schatting van \(\sigma^2\) bekomen we volgende standaard errors:
\[\text{SE}_{\hat{\beta}_0}=\hat\sigma_{\hat{\beta}_0}=\sqrt{\frac{\sum\limits_{i=1}^n X^2_i}{\sum\limits_{i=1}^n (X_i-\bar X)^2} \times\frac{\text{MSE}}{n}} \text{ en } \text{SE}_{\hat{\beta}_1}=\hat\sigma_{\hat{\beta}_1}=\sqrt{\frac{\text{MSE}}{\sum\limits_{i=1}^n (X_i-\bar X)^2}}\]
Opnieuw toetsen en betrouwbaarheidsintervallen o.b.v. \[T=\frac{\hat{\beta}_k-\beta_k}{SE(\hat{\beta}_k)} \text{ with } k=1,2.\]
Als aan alle aannames is voldaan volgt \(T\) een t-verdeling met n-2 vrijheidsgraden.
Als geen normaliteit maar wel onafhankelijk, lineariteit en homoscedasticiteit en grote dataset \[\rightarrow \text{Centrale Limietstelling}\]
Borstkanker voorbeeld
Negatieve associatie tussen S100A8 en ESR1 gen expressie.
Veralgemeen effect in steekproef naar populatie met behulp van het betrouwbaarheidsinterval op de helling: \[[\hat\beta_1 - t_{n-2,\alpha/2} \text{SE}_{\hat\beta_1},\hat\beta_1 + t_{n-2,\alpha/2} \text{SE}_{\hat\beta_1}]\].
2.5 % 97.5 %
(Intercept) 149.84639096 267.09649989
ESR1 -0.08412397 -0.03440378
- Negatief verband is significant op het 5% significantieniveau.
Hypothese test
Vertaal de onderzoeksvraag āIs er een associatie tussen de S100A8- en ESR1-genexpressie?ā naar de parameters in het model.
Onder nulhypothese geen associatie tussen expressie van beide genen: \[H_0: \beta_1=0\]
Onder alternatieve hypothese is er een associatie tussen beide genen: \[H_1: \beta_1\neq0\]
Test statistiek \[T=\frac{\hat{\beta}_1-0}{SE(\hat{\beta}_k)}\]
Onder \(H_0\) volgt de statistiek een t-verdeling met n-2 vrijheidsgraden.
Brca dataset
Call:
lm(formula = S100A8 ~ ESR1, data = brcaSubset)
Residuals:
Min 1Q Median 3Q Max
-95.43 -34.81 -6.79 34.23 145.21
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 208.47145 28.57207 7.296 7.56e-08 ***
ESR1 -0.05926 0.01212 -4.891 4.08e-05 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 59.91 on 27 degrees of freedom
Multiple R-squared: 0.4698, Adjusted R-squared: 0.4502
F-statistic: 23.93 on 1 and 27 DF, p-value: 4.078e-05
- Associatie tussen de S100A8 en ESR1 genexpressie extreem significant is (p<<0.001).
- Maar eerst moeten we alle assumpties controleren!
- Anders kunnen de conclusies o.b.v. de statistische test en het BI onjuist zijn.
Nagaan van modelveronderstellingen
- Onafhankelijkheid: design
- Lineariteit: besluitvorming geen zin als model niet lineair is
- Homoscedasticiteit: besluitvorming/p-waarde is niet betrouwbaar als de data niet homoscedastisch zijn
- Normaliteit: besluitvorming/p-waarde is niet betrouwbaar als de data niet normaal verdeeld zijn in kleine steekproeven
Lineariteit
brcaSubset %>%
ggplot(aes(x = ESR1, y = S100A8)) +
geom_point() +
geom_smooth(se = FALSE, col = "grey") +
geom_smooth(method = "lm", se = FALSE)
Residuplots
Afwijkingen van lineariteit echter makkelijker opgespoord d.m.v. een residuplot. (Zeker als er later meer variabelen zijn in het lineaire model)
verklarende variabele of predicties \(\hat\beta_0+\hat\beta_1 x\) op de \(X\)-as
de residuen op de \(Y\)-as \[e_i=y_i-\hat{g}(x_i)=y_i-\hat\beta_0-\hat\beta_1\times x_i,\]
Homoscedasticiteit (gelijkheid van variantie)
Residuen en kwadratische residuās dragen informatie over residuele variabiliteit.
Associatie met de verklarende variabelen \(\rightarrow\) indicatie van heteroscedasticiteit.
Scatterplot van of \(e_i\) versus \(x_i\) of predicties \(\hat \beta_0+ \hat \beta_1 x_i\).
Scatterplot van gestandardiseerd residu versus \(x_i\) of predicties.
Normaliteit
Indien voldoende gegevens, schatters normaal verdeeld zelfs wanneer observaties niet Normaal verdeeld zijn: centrale limiet stelling
Wat āvoldoende observatiesā zijn, hangt af van hoe goed de verdeling op de Normale lijkt.
Aanname is dat uitkomsten Normaal verdeeld zijn bij vaste waarden van de verklarende variabelen. \[Y_i\vert X_i\sim N(\beta_0+\beta_1X_i,\sigma^2)\]
QQ-plot van response Y is heel misleidend.
QQ-plot van de residuen \(e_i\)
Afwijkingen van Modelveronderstellingen
Borstkanker voorbeeld
Problemen met
heteroscedasticiteit
mogelijkse afwijking van normaliteit (scheefheid naar rechts)
negatieve concentratievoorspellingen die theoretisch niet mogelijk zijn
niet-lineairiteit
treedt veelal op bij concentratie en intensiteitsmetingen
Deze zijn vaak log-normaal verdeeld (normale verdeling na log-transformatie)
In Figuur 6.3 eveneens een soort exponentiƫle trend
In de genexpressie literatuur wordt veelal gebruik gemaak van \(\log_2\) transformatie
gen-expressie op log-schaal proportionele verschillen op de originele schaal
brca %>%
ggplot(aes(x = ESR1, y = S100A8)) +
geom_point() +
geom_smooth()
brca %>%
ggplot(aes(
x = ESR1 %>% log2,
y = S100A8 %>% log2)
) +
geom_point() +
geom_smooth()
lm2<-lm(S100A8 %>% log2 ~ ESR1 %>% log2, brca)
plot(lm2)
Call:
lm(formula = S100A8 %>% log2 ~ ESR1 %>% log2, data = brca)
Residuals:
Min 1Q Median 3Q Max
-1.94279 -0.66537 0.08124 0.68468 1.92714
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 23.401 1.603 14.60 3.57e-15 ***
ESR1 %>% log2 -1.615 0.150 -10.76 8.07e-12 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.026 on 30 degrees of freedom
Multiple R-squared: 0.7942, Adjusted R-squared: 0.7874
F-statistic: 115.8 on 1 and 30 DF, p-value: 8.07e-12
2.5 % 97.5 %
(Intercept) 20.128645 26.674023
ESR1 %>% log2 -1.921047 -1.308185
Interpretatie 1
Een patiƫnt met een ESR1 expressie die 1 eenheid op de \(\log_2\) schaal hoger ligt dan dat van een andere patiƫnt heeft gemiddeld gezien een expressie-niveau van het S100A8 gen dat 1.61 eenheden lager ligt (95% BI [-1.92,-1.31]).
Crossectionele studie: enkel uitspraken over verschillen tussen patiƫnten!
\[\log_2 \hat\mu_1=23.401 -1.615 \times \text{logESR}_1,\text{ } \log_2 \hat\mu_2=23.401 -1.615 \times \text{logESR}_2 \] \[\log_2 \hat\mu_2-\log_2 \hat\mu_1= -1.615 (\log_2 \text{ESR}_2-\log_2 \text{ESR}_1) = -1.615 \times 1 = -1.615\]
Interpretatie 2
Model op log-schaal: bij terugtransformatie verkrijgen we het geometrische gemiddelde
\[\begin{eqnarray*}
\sum\limits_{i=1}^n \frac{\log x_i}{n}&=&\frac{\log x_1 + \ldots + \log x_n}{n}\\\\
&\stackrel{(1)}{=}&\frac{\log(x_1 \times \ldots \times x_n)}{n}=\frac{\log\left(\prod\limits_{i=1}^n x_i\right)}{n}\\\\
&\stackrel{(2)}{=}&\log \left(\sqrt[\leftroot{-1}\uproot{2}\scriptstyle n]{\prod\limits_{i=1}^n x_i}\right)
\end{eqnarray*}\]
- Populatiegemiddelden \(\mu\) dus geschat a.d.h.v. geometrisch gemiddelden.
- Logaritmische transformatie is een monotoon: we kunnen betrouwbaarheidsintervallen berekend op log-schaal terugtransformeren!
ESR1 %>% log2
0.3265519
ESR1 %>% log2
3.0623
2.5 % 97.5 %
3.786977 2.476298
Een patiƫnt met een ESR1 expressie die 2 keer zo hoog is als die van een andere patiƫnt, zal gemiddeld een S100A8-expressie hebben die 3.06 keer lager is (95% BI [2.48,3.79]).
\[\log_2 \hat\mu_1=23.401 -1.615 \times \text{logESR}_1,\text{ } \log_2 \hat\mu_2=23.401 -1.615 \times \text{logESR}_2 \] \[\log_2 \hat\mu_2-\log_2 \hat\mu_1= -1.615 (\log_2 \text{ESR}_2-\log_2 \text{ESR}_1) \] \[\log_2 \left[\frac{\hat\mu_2}{\hat\mu_1}\right]= -1.615 \log_2\left[\frac{ \text{ESR}_2}{\text{ESR}_1}\right] \] \[\frac{\hat\mu_2}{\hat\mu_1}=\left[\frac{ \text{ESR}_2}{\text{ESR}_1}\right]^{-1.615}=2^ {-1.615} =0.326\] or \[\frac{\hat\mu_1}{\hat\mu_2}=2^{1.615} =3.06\]
Interpratie 3
Een patiƫnt met een ESR1 expressie die 1% hoger is dan die van een andere patiƫnt zal gemiddeld een expressieniveau voor het S100A8 gen hebben dat ongeveer -1.61% lager is (95% BI [-1.92,-1.31])%.
\[\log_2 \hat\mu_1=23.401 -1.615 \times \text{logESR}_1,\text{ } \log_2 \hat\mu_2=23.401 -1.615 \times \text{logESR}_2 \] \[\log_2 \hat\mu_2-\hat\log_2 \mu_1= -1.615 (\log_2 \text{ESR}_2-\log_2 \text{ESR}_1) \] \[\log_2 \left[\frac{\hat\mu_2}{\hat\mu_1}\right]= -1.615 \log_2\left[\frac{ \text{ESR}_2}{\text{ESR}_1}\right] \] \[\frac{\hat\mu_2}{\hat\mu_1}=\left[\frac{ \text{ESR}_2}{\text{ESR}_1}\right]^{-1.615}=1.01^ {-1.615} =0.984 \approx -1.6\%\]
Dit geldt voor lage tot matige waarden van \(\beta_1\): \[-10<\beta_1<10 \rightarrow 1.01^{\beta_1} -1 \approx \frac{\beta_1}{100}.\]
Besluitvorming over gemiddelde uitkomst
Regressie model kan ook worden gebruikt voor predictie
Besluitvorming te doen over de gemiddelde uitkomst bij een gegeven waarde \(x\), m.a.w. \[\hat{g}(x)= \hat{\beta}_0 + \hat{\beta}_1 x\]
\(\hat{g}(x)\) een schatter van het conditionele gemiddelde \(E[Y\vert X=x]\)
Parameterschatters Normale verdeeld en onvertekend \(\rightarrow\) schatter \(\hat{g}(x)\) ook Normaal verdeeld en onvertekend.
\[\text{SE}_{\hat{g}(x)}=\sqrt{MSE\left\{\frac{1}{n}+\frac{(x-\bar X)^2}{\sum\limits_{i=1}^n (X_i-\bar X)^2}\right\}}.\]
\[T=\frac{\hat{g}(x)-g(x)}{SE_{\hat{g}(x)}}\sim t_{n-2}\]
- Gemiddelde uitkomst en betrouwbaarheidsintervallen op de gemiddelde uitkomst in R via de
predict(.)
functie.
newdata
argument: predictorwaarden (x-waarden) voor het berekenen van gemiddelde uitkomsten
interval="confidence"
argument om betrouwbaarheidsintervallen te bekomen.
- Zonder
newdata
argument wordt de gemiddelde uitkomsten berekend voor alle predictorwaarden van de dataset.
grid <- 140:4000
g <- predict(
lm2,
newdata = data.frame(ESR1 = grid),
interval = "confidence")
head(g)
fit lwr upr
1 11.89028 10.76082 13.01974
2 11.87370 10.74721 13.00019
3 11.85724 10.73370 12.98078
4 11.84089 10.72028 12.96151
5 11.82466 10.70696 12.94237
6 11.80854 10.69372 12.92336
Merk op dat we de nieuwe data die we gespecificeerd hebben voor de ESR1 expressie niet moeten transformeren omdat we het model fitten met de lm
functie en de transformatie hebben gespecificeerd binnen die functie met behulp van het pipe commando!
brca %>% ggplot(
aes(
x = ESR1 %>% log2,
y = S100A8 %>% log2)
) +
geom_point() +
geom_smooth(method = "lm")
Predictie-intervallen
-We kunnen ook een voorspelling doen voor de locatie van een nieuwe waarneming die zou worden verzameld in een nieuw experiment voor een patiƫnt met een bepaalde waarde voor hun ESR1-expressie
Het is belangrijk op te merken dat dit experiment nog moet worden uitgevoerd. We willen dus de niet-waargenomen individuele expressiewaarde voor een nieuwe patiƫnt voorspellen.
Voor een nieuwe onafhankelijke waarneming \(Y^*\) \[
Y^* = g(x) + \epsilon^*
\] met \(\epsilon^*\sim N(0,\sigma^2)\) en \(\epsilon^*\) onafhankelijk van de waarnemingen in de steekproef \(Y_1,\ldots, Y_n\).
We voorspellen een nieuwe log-S100A8 voor een patiƫnt met een gekend log2-ESR1-expressieniveau x \[
\hat{y}(x)=\hat{\beta}_0+\hat{\beta}_1 \times x
\]
De geschatte gemiddelde uitkomst en voorspelling voor een nieuwe waarneming zijn gelijk.
Maar hun steekproef verdelingen zijn anders!
- Onzekerheid over de geschatte gemiddelde uitkomst \(\leftarrow\) onzekerheid over de geschatte modelparameters \(\hat\beta_0\) en \(\hat\beta_1\).
- Onzekerheid over nieuwe waarneming \(\leftarrow\) onzekerheid over geschat gemiddelde en extra onzekerheid omdat de nieuwe waarneming zal afwijken rond het gemiddelde!
\[\text{SE}_{\hat{Y}(x)}=\sqrt{\hat\sigma^2+\hat\sigma^2_{\hat{g}(x)}}=\sqrt{MSE\left\{1+\frac{1}{n}+\frac{(x-\bar X)^2}{\sum\limits_{i=1}^n (X_i-\bar X)^2}\right\}}.\]
\[\frac{\hat{Y}(x)-Y}{\text{SE}_{\hat{Y}(x)}}\sim t_{n-2}\]
- Merk op dat een predictie-interval (PI) een verbeterde versie is van een referentie-interval wanneer de modelparameters onbekend zijn: onzekerheid over modelparameters + t-verdeling.
p <- predict(
lm2,
newdata = data.frame(ESR1 = grid),
interval="prediction")
head(p)
fit lwr upr
1 11.89028 9.510524 14.27004
2 11.87370 9.495354 14.25205
3 11.85724 9.480288 14.23419
4 11.84089 9.465324 14.21646
5 11.82466 9.450461 14.19886
6 11.80854 9.435698 14.18138
preddata<-data.frame(
grid = grid%>%log2,
p)
brca %>% ggplot(aes(x=ESR1%>%log2,y=S100A8%>%log2)) +
geom_point() +
geom_smooth(method="lm") +
geom_line(aes(x=grid,y=lwr),preddata,color="blue") +
geom_line(aes(x=grid,y=upr),preddata,color="blue")
preddata<-data.frame(cbind(grid,2^p))
brca %>% ggplot(aes(x = ESR1, y = S100A8)) +
geom_point() +
geom_line(
aes(x = grid,y = fit),
newdata) +
geom_line(
aes(x = grid, y = lwr),
newdata,
color = "grey") +
geom_line(
aes(x = grid, y = upr),
newdata,
color = "grey") +
geom_line(
aes(x = grid, y = lwr),
preddata,
color = "blue") +
geom_line(
aes(x = grid,y = upr),
preddata,
color = "blue")
NHANES voorbeeld
library(NHANES)
fem <- NHANES %>%
filter(Gender=="female"&!is.na(DirectChol))
2^(
fem %>%
pull(DirectChol) %>%
log2 %>%
mean +
c(-1,1) *
qnorm(0.975) *
(fem %>%
pull(DirectChol) %>%
log2 %>%
sd)
)
[1] 0.8361311 2.4397130
lmChol <- lm(DirectChol %>% log2 ~ 1, data=fem)
predInt <- predict(
lmChol,
interval="prediction",
newdata=data.frame(noPred=1)
)
round(2^predInt,2)
fit lwr upr
1 1.43 0.84 2.44
Merk op dat het voorspellingsinterval bijna gelijk is aan het referentie-interval voor de grote steekproef. We konden de parameters inderdaad heel precies schatten.
We zullen hetzelfde doen voor een kleine steekproef van 10 patiƫnten.
set.seed(1)
fem10 <- NHANES %>%
filter(Gender=="female"&!is.na(DirectChol)) %>%
sample_n(size=10)
2^(
fem10 %>%
pull(DirectChol) %>%
log2 %>%
mean +
c(-1,1) *
qnorm(0.975) *
(fem10 %>%
pull(DirectChol) %>%
log2 %>%
sd)
)
[1] 0.8976012 2.2571645
Het referentie-interval is veel smaller dan in de grote steekproef.
lmChol10 <- lm(DirectChol %>% log2 ~ 1, data = fem10)
predInt10 <- predict(
lmChol10,
interval = "prediction",
newdata = data.frame(noPred=1)
)
round(2^predInt10, 2)
fit lwr upr
1 1.42 0.81 2.49
Merk op dat het PI nu onzekerheid meeneemt in parameterschatters (gemiddelde en standaard error). En dat het interval veel breder wordt! Dit is hier vooral belangrijk voor de bovengrens omdat we de gegevens terug hebben getransformeerd!
Het interval is bijna net zo breed als dat gebaseerd op de grote steekproef.
Bij kleine steekproeven is het erg belangrijk om met deze extra onzekerheid rekening te houden.
Kwadratensommen en Anova-tabel
Totale kwadratensom
\[\text{SSTot} = \sum_{i=1}^n (Y_i-\bar{Y})^2.\]
SStot kan worden gebruikt om de variantie van de marginale verdeling van de respons te schatten.
In dit hoofdstuk hebben we ons gefocused op de conditionele verdeling \(f(Y\vert X=x)\).
We weten dat MSE een goede schatting is van de variantie van de conditionele verdeling van \(Y\vert X=x\).
Kwadratensom van de regressie SSR
\[\text{SSR} = \sum_{i=1}^n (\hat{Y}_i - \bar{Y})^2 = \sum_{i=1}^n (\hat{g}(x_i) - \bar{Y})^2.\]
Maat voor de afwijking tussen de predicties op de geschatte regressierechte en het steekproefgemiddelde van de uitkomsten.
Een andere interpretatie: verschil tussen twee modellen
- Geschatte model \(\hat{g}(x)=\hat\beta_0+\hat\beta_1x\)
- Geschatte model zonder predictor (enkel intercept): \(g(x)=\beta_0\) \(\rightarrow\) \(\beta_0\) zal gelijk zijn aan \(\bar{Y}\).
SSR meet de grootte van het effect van de predictor
Kwadratensom van de fouten
\[ \text{SSE} = \sum_{i=1}^n (Y_i-\hat{Y}_i )^2 = \sum_{i=1}^n \left\{Y_i-\hat{g}\left(x_i\right)\right\}^2.\]
We kunnen aantonen dat SST kan worden ontbonden in \[\begin{eqnarray*}
\text{SSTot}
&=& \sum_{i=1}^n (Y_i-\bar{Y})^2 \\
&=& \sum_{i=1}^n (Y_i-\hat{Y}_i+\hat{Y}_i-\bar{Y})^2 \\
&=& \sum_{i=1}^n (Y_i-\hat{Y}_i)^2+\sum_{i=1}^n(\hat{Y}_i-\bar{Y})^2 \\
&=& \text{SSE }+\text{SSR}
\end{eqnarray*}\]
- De totale variabiliteit in de gegevens (SSTot) wordt gedeeltelijk verklaard door het regressieverband (SSR).
- Variabiliteit die we niet kunnen verklaren met het regressiemodel is de residuele variabiliteit (SSE).
Determinatie-coƫfficiƫnt
\[ R^2 = 1-\frac{\text{SSE}}{\text{SSTot}}=\frac{\text{SSR}}{\text{SSTot}}.\]
Fractie van de totale variabiliteit in de steekproef-uitkomsten die verklaard wordt door het geschatte regressieverband.
Grote \(R^2\) indicatie dat model potentieel tot goede predicties kan leiden (kleine SSE)
Slechts in beperkte mate indicatief voor de p-waarde van de test \(H_0:\beta_1=0\) vs \(H_1:\beta_1\neq0\).
- p-waarde sterk beĆÆnvloed door SSE en steekproefgrootte \(n\), maar niet door SSTot
- De determinatiecoƫfficiƫnt \(R^2\) wordt door SSE en SSTot bepaald, maar niet door de steekproefgrootte n.
Model met lage \(R^2\) blijft wel nuttig om associatie te bestuderen, zolang het de associatie correct modelleert!
Borstkanker voorbeeld
Call:
lm(formula = S100A8 %>% log2 ~ ESR1 %>% log2, data = brca)
Residuals:
Min 1Q Median 3Q Max
-1.94279 -0.66537 0.08124 0.68468 1.92714
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 23.401 1.603 14.60 3.57e-15 ***
ESR1 %>% log2 -1.615 0.150 -10.76 8.07e-12 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.026 on 30 degrees of freedom
Multiple R-squared: 0.7942, Adjusted R-squared: 0.7874
F-statistic: 115.8 on 1 and 30 DF, p-value: 8.07e-12
F-Testen in het enkelvoudig lineair regressiemodel
- Kwadratensommen zijn basis voor \(F\)-tests \[ F = \frac{\text{MSR}}{\text{MSE}}\]
met \(\text{MSR} = \frac{\text{SSR}}{1} \text{ and } \text{MSE} = \frac{\text{SSE}}{n-2}.\)
MSR wordt de gemiddelde kwadratensom van de regressie genoemd.
noemers 1 en \(n-2\) zijn de vrijheidsgraden van SSR en SSE.
onder \(H_0: \beta_1=0\) volgt de teststatistiek \[H_0:F = \frac{\text{MSR}}{\text{MSE}} \sim F_{1,n-2},\]
F-test is altijd twee-zijdig! \(H_1:\beta_1\neq 0\) \[ p = P_0\left[F\geq f\right]=1-F_F(f;1,n-2)\]
Call:
lm(formula = S100A8 %>% log2 ~ ESR1 %>% log2, data = brca)
Residuals:
Min 1Q Median 3Q Max
-1.94279 -0.66537 0.08124 0.68468 1.92714
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 23.401 1.603 14.60 3.57e-15 ***
ESR1 %>% log2 -1.615 0.150 -10.76 8.07e-12 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.026 on 30 degrees of freedom
Multiple R-squared: 0.7942, Adjusted R-squared: 0.7874
F-statistic: 115.8 on 1 and 30 DF, p-value: 8.07e-12
Anova Tabel
Regressie |
vrijheidsgraden SSR |
SSR |
MSR |
f-statistiek |
p-waarde |
Error |
vrijheidsgraden SSE |
SSE |
MSE |
|
|
Analysis of Variance Table
Response: S100A8 %>% log2
Df Sum Sq Mean Sq F value Pr(>F)
ESR1 %>% log2 1 121.814 121.814 115.8 8.07e-12 ***
Residuals 30 31.559 1.052
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Dummy variabelen
Lineaire regressiemodel voor het vergelijken van twee gemiddelden.
Borstkanker: verschil is in gemiddelde leeftijd van de patiƫnten met onaangetaste lymfeknopen en patiƫnten waarvan lymfeknopen werden verwijderd.
Hiervoor definiƫren we eerst een \(dummy\) variabele \[x_i = \left\{ \begin{array}{ll}
1 & \text{aangetaste lymfeknopen} \\
0 & \text{onaangetaste lymfeknopen} \end{array}\right.\]
groep met \(x_i=0\) wordt de referentiegroep genoemd.
Het regressiemodel blijft ongewijzigd, \[Y_i = \beta_0 + \beta_1 x_i +\epsilon_i\] met \(\epsilon_i \text{ iid } N(0,\sigma^2)\)
Gezien \(x_i\) slechts twee waarden kan aannemen, is het eenvoudig om het regressiemodel voor beide waarden van \(x_i\) afzonderlijk te bekijken: \[ \begin{array}{lcll}
Y_i &=& \beta_0 +\epsilon_i &\text{onaangetaste lymfeknopen} (x_i=0) \\
Y_i &=& \beta_0 + \beta_1 +\epsilon_i &\text{ aangetaste lymfeknopen} (x_i=1) .
\end{array}\]
Dus \[\begin{eqnarray*}
E\left[Y_i\mid x_i=0\right] &=& \beta_0 \\
E\left[Y_i\mid x_i=1\right] &=& \beta_0 + \beta_1,
\end{eqnarray*}\]
waaruit direct de interpretatie van \(\beta_1\) volgt: \[ \beta_1 = E\left[Y_i\mid x_i=1\right]-E\left[Y_i\mid x_i=0\right]\]
\(\beta_1\) is dus het gemiddelde verschil in leeftijd tussen patiƫnten met aangetaste lymfeknopen en patiƫnten met onaangetaste lymfeknopen (referentiegroep).
Met de notatie \(\mu_0= E\left[Y_i\mid x_i=0\right]\) en \(\mu_1= E\left[Y_i\mid x_i=1\right]\) wordt dit \[\beta_1 = \mu_1-\mu_0.\]
Er kan aangetoond worden dat \[\begin{array}{ccll}
\hat\beta_0
&=& \bar{Y}_1&\text{ (steekproefgemiddelde in referentiegroep)} \\
\hat\beta_1
&=& \bar{Y}_2-\bar{Y}_1&\text{(schatter van effectgrootte)} \\
\text{MSE}
&=& S_p^2 .
\end{array}\]
De testen voor \(H_0:\beta_1=0\) vs.Ā \(H_1:\beta_1\neq0\) kunnen gebruikt worden voor het testen van de nulhypothese van de two-sample \(t\)-test, \(H_0:\mu_1=\mu_2\) t.o.v. \(H_1:\mu_1\neq\mu_2\).
brca$node <- as.factor(brca$node)
t.test(age~node,brca,var.equal=TRUE)
Two Sample t-test
data: age by node
t = -2.7988, df = 30, p-value = 0.008879
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-15.791307 -2.467802
sample estimates:
mean in group 0 mean in group 1
59.94737 69.07692
lm3 <- lm(age~node, brca)
summary(lm3)
Call:
lm(formula = age ~ node, data = brca)
Residuals:
Min 1Q Median 3Q Max
-19.9474 -5.3269 0.0526 5.3026 18.0526
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 59.947 2.079 28.834 < 2e-16 ***
node1 9.130 3.262 2.799 0.00888 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 9.063 on 30 degrees of freedom
Multiple R-squared: 0.207, Adjusted R-squared: 0.1806
F-statistic: 7.833 on 1 and 30 DF, p-value: 0.008879
brca %>%
ggplot(
aes(
x = node %>%
as.factor,
y = age)
) +
geom_boxplot()
- Simulate 9 datasets with the same number of observations as the brca dataset from a normal distribution with the same standard deviation as in the original data. Store the data of all simulations in a data frame
- Plot the simulated data using the
ggplot
function
- Add a boxplot layer
- Use facet_wrap to make a separate plot for simulated dataset
- Change label of y-axis
set.seed(354)
sim_df <- data.frame(
node = rep(brca$node, 9),
iid = rnorm(9 * nrow(brca), sd = sigma(lm3)),
sim = rep(1:9, each = 32)
)
sim_df %>%
ggplot(aes(x = node, y=iid)) +
geom_boxplot() +
facet_wrap(.~sim) +
ylab(paste0("iid N(0,",round(sigma(lm3)^2,2),")"))
Observationele study
We kunnen echter niet besluiten dat oudere personen een hoger risico hebben op aantasting van de lymfeknopen ten gevolge van hun leeftijd.
Mogelijks confounding: geen randomisatie \(\rightarrow\) groepen patiƫnten met aangetaste lymfeknopen en niet-aangetaste lymfeknopen kunnen nog in andere karateristieken van elkaar verschillen.
Enkel besluiten dat er een associatie is tussen de lymfeknoop status en de leeftijd.
Het is dus niet noodzakelijkerwijs een causaal verband!
Is ook zo voor lineair model voor de \(\log_2\)-S100A8-expressie.
Aangezien we de ESR1-expressie niet experimenteel vast hebben kunnen leggen, kunnen we niet besluiten dat een hogere ESR1-expressie de S100A8-expressie doet verlagen.
Enkel besluiten dat er een negatieve associatie is.
Om impact van gen te bestuderen op andere genen: knockout mutanten generenen in het labo
LS0tCnRpdGxlOiAiNi4gRW5rZWx2b3VkaWdlIGxpbmVhaXJlIHJlZ3Jlc3NpZSIgICAKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCm91dHB1dDoKICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgICAgCiAgICAgIHRoZW1lOiBjb3NtbwogICAgICB0b2M6IHRydWUKICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgIGhpZ2hsaWdodDogdGFuZ28KICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChpbmNsdWRlID0gVFJVRSwgY29tbWVudCA9IE5BLCBlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQpsaWJyYXJ5KFJtaXNjKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgoKIyBCcmVhc3QgY2FuY2VyIGRhdGFzZXQKCgotIHN1YnNldCB2YW4gc3R1ZGllIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDkzL2puY2kvZGpqMDUyCi0gMzIgYm9yc3RrYW5rZXIgcGF0acOrbnRlbiBtZXQgZWVuIGVzdHJvZ2VuIHJlY2VwdGVyIHBvc2l0aWV2ZSB0dW1vciBkaWUgdGFtb3hpZmVuIGNoZW1vdGhlcmFweSBiZWhhbmRlbGluZyBvbmRlcmdhYW4uIFZhcmlhYmVsZW46CgogICAgLSBncmFkZTogaGlzdG9sb2dpc2NoZSBncmFhZCB2YW4gdHVtb3IgKGdyYWFkIDEgdnMgMyksCiAgICAtIG5vZGU6IHN0YXR1cyB2YW4gZGUgbHltZmUga25vcGVuICgwOiBuaWV0IGFhbmdldGFzdCwgMTogYWFudGFzdGluZyBlbiB2ZXJ3aWpkZXJpbmcgdmFuIGRlIGx5bWZlIGtub3BlbiksCiAgICAtIHNpemU6IGdyb290dGUgdmFuIHR1bW9yIGluIGNtLAogICAgLSBFU1IxIGVuIFMxMDBBOCBnZW4gZXhwcmVzc2llIGluIHR1bW9yIGJpb3BzeSAodmlhIG1pY3JvYXJyYXkgdGVjaG5vbG9naWUpCgoKYGBge3J9CmJyY2EgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3Mvc2JjMjAvbWFzdGVyL2RhdGEvYnJlYXN0Y2FuY2VyLmNzdiIpCmJyY2EKYGBgCgotIE9tIGRpZGFjdGlzY2hlIHJlZGVuZW4gdmVyd2lqZGVyZW4gd2UgZWVyc3QgMyBvdXRsaWVycyBpbiBkZSBTMTAwQTggZXhwcmVzc2llIGRhdGEuCi0gSW4gZGV6ZSBzdHVkaWUga2FuIGRpdCBlY2h0ZXIgbmlldCB3b3JkZW4gdmVyYW50d29vcmQKLSBMYXRlciBpbiBkZSBsZXNzZW4gbGF0ZW4gd2UgemllbiBob2UgamUgZ29lZCBtZXQgYWxsZSBkYXRhIG9tZ2FhdC4KCgpgYGB7ciBvdXQud2lkdGg9JzcwJScsIGZpZy5hbGlnbj0nY2VudGVyJyx3YXJuaW5ncz1GQUxTRX0KYnJjYSAlPiUKICBnZ3Bsb3QoYWVzKHg9IiIsIHk9UzEwMEE4KSkgKwogIGdlb21fYm94cGxvdCgpICsKICB4bGFiKCIiKSArCiAgeWxhYigiUzEwMEE4IGV4cHJlc3NpZSIpCmBgYAoKLS0tCgpgYGB7cn0KbGlicmFyeShHR2FsbHkpCmJyY2FTdWJzZXQgPC0gYnJjYSAlPiUKICBmaWx0ZXIoUzEwMEE4PDIwMDApCmJyY2FTdWJzZXRbLC0oMTo0KV0gJT4lIGdncGFpcnMoKQpgYGAKCiMjIEFzc29jaWF0aWUgdHVzc2VuIEVTUjEgZW4gUzEwMEE4IGV4cHJlc3NpZQoKLSBFU1IxIGluICRccG0kIDc1JSB2YW4gYm9yc3RrYW5rZXJ0dW1vcmVuLgoKICAgIC0gRXhwcmVzc2llIHZhbiBFUi1nZW4gcG9zaXRpZWYgdm9vciBiZWhhbmRlbGluZzogdHVtb3IgcmVhZ2VlcnQgb3AgaG9ybW9vbnRoZXJhcGllCiAgICAtIFRhbW94aWZlbiBpbnRlcmFnZWVydCBtZXQgRVIgZW4gbW9kdWxlZXJ0IGdlbmV4cHJlc3NpZS4KCi0gRWl3aXR0ZW4gdmFuIGRlIFMxMDAtZmFtaWxpZSB6aWpuIHZhYWsgZ2VkaXNyZWd1bGVlcmQgYmlqIGthbmtlcgoKLSBTMTAwQTggZXhwcmVzc2llIG9uZGVyZHJ1a3QgaW1tdXVuc3lzdGVlbSBpbiB0dW1vciBlbiBjcmXDq2VydCBpbmZsYW1hdG9pciBtaWxpZXUgZGllIGthbmtlcmdyb2VpIHByb21vb3QuCgotIEludGVyZXNzZSBpbiBhc3NvY2lhdGllIHR1c3NlbiBFU1IxIGVuIFMxMDBBOCBleHByZXNzaWUuCgoxLiBwaXBlIGRhdGFzZXQgbmFhciBnZ3Bsb3QKMi4gc2VsZWN0ZWVyIGRhdGEgYGdncGxvdChhZXMoeD1FU1IxLHk9UzEwMEE4KSlgCjMuIHZvZWcgcHVudGVuIHRvZSBtZXQgYGdlb21fcG9pbnQoKWAKNC4gdm9lZyBlZW4gInNtb290aCBsaW5lIiB0b2UgYGdlb21fc21vb3RoKClgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9CmJyY2FTdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PUVTUjEseT1TMTAwQTgpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpCmBgYAoKIyBMaW5lYWlyZSBSZWdyZXNzaWUKCi0gU3RhdGlzdGlzY2hlIG1ldGhvZGUgb20gcmVsYXRpZSB0dXNzZW4gMiByZWVrc2VuIG9ic2VydmF0aWVzICQoWF9pLCBZX2kpJCwgYmVrb21lbiB2b29yIG9uYWZoYW5rZWxpamtlIHN1YmplY3RlbiAkaSA9IDEsIC4uLiwgbiQsIHRlIGJlc2NocmlqdmVuLgoKLSBHZW4gZXhwcmVzc2llIHZvb3JiZWVsZAoKICAgIC0gUmVzcG9uc2UgWSA6IFMxMDBBOCBleHByZXNzaWUKICAgIC0gUHJlZGljdG9yIFg6IEVTUjEgZXhwcmVzc2llCgoxLiBwaXBlIGRhdGFzZXQgbmFhciBnZ3Bsb3QKMi4gc2VsZWN0ZWVyIGRhdGEgYGdncGxvdChhZXMoeD1FU1IxLHk9UzEwMEE4KSlgCjMuIHZvZWcgcHVudGVuIHRvZSBtZXQgYGdlb21fcG9pbnQoKWAKNC4gdm9lZyBlZW4gInNtb290aCBsaW5lIiB0b2UgYGdlb21fc21vb3RoKClgCjUuIHZvZWcgZWVuIHJlY2h0ZSB0b2UgYGdlb21fc21vb3RoKClgIG1ldCBgbWV0aG9kID0gImxtImAgKGxpbmVhciBtb2RlbCkuIChXZSB6ZXR0ZW4gYHNlID0gRkFMU0VgIG9tIGdlZW4gcHVudGdld2lqemUgYmV0cm91d2JhYXJoZWlkc2ludGVydmFsbGVuIHdlZXIgdGUgZ2V2ZW4pCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9CmJyY2FTdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4ID0gRVNSMSx5ID0gUzEwMEE4KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgY29sID0gImdyZXkiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkKYGBgCgojIyBNb2RlbAoKLSBWb29yIHZhc3RlICRYJCwgaGVlZnQgJFkkIG5pZXQgbm9vZHpha2VsaWprZSBkZXplbGZkZSB3YWFyZGUKCiQkXHRleHR7b2JzZXJ2YXRpb24gPSBzaWduYWwgKyBub2lzZX0kJAoKJCRZX2k9ZyhYX2kpK1xlcHNpbG9uX2kkJAoKLSBXZSBkZWZpbmnDq3JlbiAkZyh4KSQgYWxzIGhldCB2ZXJ3YWNodGUgcmVzdWx0YWF0IHZvb3Igc3ViamVjdHMgbWV0ICRYX2k9eCQKCiQkRVtZX2l8WF9pPXhdPWcoeCkkJAoKRGFhcm9tIGlzICRcZXBzaWxvbl9pJCBnZW1pZGRlbGQgMCB2b29yIHN1YmplY3RzIG1ldCBkZXplbGZkZSAkWF9pJDoKJCRFW1xlcHNpbG9uX2l8WF9pXT0wJCQKCiMjIExpbmVhaXJlIHJlZ3Jlc3NpZQoKLSBPbSAqKmFjY3VyYXRlKiogZW4gKippbnRlcnByZXRlZXJiYXJlKiogcmVzdWx0YXRlbiB0ZSBiZWtvbWVuLCBraWVzdCBtZW4gJGcoeCkkIHZhYWsgYWxzIGVlbiBsaW5lYWlyZSBmdW5jdGllIG1ldCBvbmdla2VuZGUgcGFyYW1ldGVycy4KCiQkRShZfFg9eCk9XGJldGFfMCArIFxiZXRhXzEgeCQkCgpvbmJla2VuZCAgKippbnRlcmNlcHQqKiAkXGJldGFfMCQgZW4KKipoZWxsaW5nKiogJFxiZXRhXzEkLgoKLSBMaW5lYWlyIG1vZGVsIGxlZ3QgZWVuICphc3N1bXB0aWUqIG9wIGRlIHZlcmRlbGluZyB2YW4gJFgkIGVuICRZJCwgZGllIGluY29ycmVjdCBrYW4gemlqbi4KCi0gKkVmZmljacOrbnRlIGRhdGEtYW5hbHlzZSo6IGJlbnV0IGFsbGUgb2JzZXJ2YXRpZXMgb20gaWV0cyB0ZSBsZXJlbiBvdmVyIHZlcndhY2h0ZSB1aXRrb21zdCBiaWogJFg9eCQuCgoKIyMgR2VicnVpawoKLSAqUHJlZGljdGllKjogd2FubmVlciAkWSQgb25nZWtlbmQgaXMsIG1hYXIgJFgkIHdlbCwga3VubmVuIHdlICRZJCB2b29yc3BlbGxlbiBvcCBiYXNpcyB2YW4gJFgkClxbRShZfFg9eCk9XGJldGFfMCArIFxiZXRhXzEgeFxdCgotICpBc3NvY2lhdGllKjogYmlvbG9naXNjaGUgcmVsYXRpZSB0dXNzZW4gdmFyaWFiZWxlICRYJCBlbiBjb250aW51ZSBtZXRpbmcgJFkkIGJlc2NocmlqdmVuLgotICpJbnRlcmNlcHQ6KiAkRShZfFg9MCk9XGJldGFfMCQKCi0gKlNsb3BlKjoKXGJlZ2lue2VxbmFycmF5Kn0KRShZfFg9eCtcZGVsdGEpLUUoWXxYPXgpJj0mXGJldGFfMCArIFxiZXRhXzEgKHgrXGRlbHRhKSAtXGJldGFfMC1cYmV0YV8xIHhcXAomPSYgXGJldGFfMVxkZWx0YQpcZW5ke2VxbmFycmF5Kn0KCiRcYmV0YV8xOiQgdmVyc2NoaWwgaW4gZ2VtaWRkZWxkZSB1aXRrb21zdCB2b29yIHN1YmplY3RlbiBkaWUgdmVyc2NoaWxsZW4gaW4gw6nDqW4gZWVuaGVpZCB2YW4gZGUgcHJlZGljdG9yICRYJC4KCiMgUGFyYW1ldGVyc2NoYXR0aW5nCgotIC0gS2xlaW5zdGUga3dhZHJhdGVuIHRlY2huaWVrCihMZWFzdCBzcXVhcmVzKQoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInfQpicmNhU3Vic2V0ICU+JQogIGdncGxvdChhZXMoeCA9IEVTUjEsIHkgPSBTMTAwQTgpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFLCBjb2wgPSAiZ3JleSIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQpgYGAKCi0gUGFyYW1ldGVycyAkXGJldGFfMCQgZW4gJFxiZXRhXzEkIHppam4gb25nZWtlbmQKCi0gUGFyYW1ldGVycyBzY2hhdHRlbiBvcCBiYXNpcyB2YW4gYmVwZXJrdGUgc3RlZWtwcm9lZgoKLSBCZXN0IHBhc3NlbmRlIGxpam4KCiAgICAtIFB1bnQgb3AgcmVncmVzc2llbGlqbiB2b29yIGVlbiBnZWdldmVuICR4X2kkOiAkKHhfaSwgXGJldGFfMCArIFxiZXRhXzEgeF9pKSQgIHpvIGRpY2h0IG1vZ2VsaWprIGJpaiAkKHhfaSwgeV9pKSQKICAgIC0gS2llcyAkXGJldGFfMCQgZW4gJFxiZXRhXzEkIHpvZGF0IGRlIHNvbSB0dXNzZW4gdm9vcnNwZWxkZSBlbiB3YWFyZ2Vub21lbiBwdW50ZW4gem8ga2xlaW4gbW9nZWxpamsgd29yZHQuCgokJFNTRT1cc3VtX3tpPTF9Xm4gKHlfaS1cYmV0YV8wLVxiZXRhXzEgeF9pKV4yPVxzdW1fe2k9MX1ebiBlX2leMiQkCgotIE1ldCAkZV9pJCBkZSByZXNpZHVlbjogZGUgdmVydGljYWxlIGFmc3RhbmRlbiB2YW4gZGUgb2JzZXJ2YXRpZXMgdG90IGRlIGdlZml0dGUgcmVncmVzc2llcmVjaHRlCgojIyBTY2hhdHRlcnMgZGllIFNTRSBtaW5pbWFsaXNlcmVuCgokJFxoYXR7XGJldGFfMX09IFxmcmFje1xzdW1cbGltaXRzX3tpPTF9Xm4gKHlfaS1cYmFyIHkpKHhfaS1cYmFyIHgpfXtcc3VtXGxpbWl0c197aT0xfV5uICh4X2ktXGJhciB4X2kpXjJ9PVxmcmFje1xtYm94e2Nvcn0oeCx5KXNfeX17c194fSAkJAoKJCRcaGF0e1xiZXRhXzB9PVxiYXIgeSAtIFxoYXR7XGJldGF9XzEgXGJhciB4ICQkCgotIE1lcmsgb3AgZGF0IGRlIGhlbGxpbmcgdmFuIGRlIGtsZWluc3RlIGt3YWRyYXRlbmxpam4gZXZlbnJlZGlnIGlzIG1ldCBkZSBjb3JyZWxhdGllIHR1c3NlbiBkZSB1aXRrb21zdCBlbiBkZSB2ZXJrbGFyZW5kZSB2YXJpYWJlbGUuCgpHZXNjaGF0dGUgbGluZWFpcmUgcmVncmVzc2llbW9kZWwgbGFhdCB0b2Ugb206CgotIHZlcndhY2h0ZSB1aXRrb21zdCB0ZSB2b29yc3BlbGxlbiB2b29yIHN1YmplY3RlbiBtZXQgZWVuIGdlZ2V2ZW4gd2FhcmRlICR4JCB2b29yIGRlIHByZWRpY3RvcjoKJCRcdGV4dHtFfSBbIFkgfCBYID0geF09XGhhdHtcYmV0YX1fMCtcaGF0e1xiZXRhfV8xeCQkCgotIG5hIHRlIGdhYW4gaG9ldmVlbCB1aXRrb21zdCBnZW1pZGRlbGQgdmVyc2NoaWx0IHR1c3NlbiAyIGdyb2VwZW4gc3ViamVjdGVuIG1ldCBlZW4gdmVyc2NoaWwgdmFuICRcZGVsdGEkIGVlbmhlZGVuIGluIGRlIHZlcmtsYXJlbmRlIHZhcmlhYmVsZToKCiQkXHRleHR7RX1cbGVmdFtZfFg9eCtcZGVsdGFccmlnaHRdLVx0ZXh0e0V9XGxlZnRbWXxYPXhccmlnaHRdPSBcaGF0e1xiZXRhfV8xXGRlbHRhJCQKCiMjIyBCb3JzdGthbmtlciB2b29yYmVlbGQKCmBgYHtyfQpsbTEgPC0gbG0oUzEwMEE4IH4gRVNSMSwgYnJjYVN1YnNldCkKc3VtbWFyeShsbTEpCmBgYAoKXFtFKFl8WD14KT1gciByb3VuZChsbTEkY29lZlsxXSwyKWAtYHIgYWJzKHJvdW5kKGxtMSRjb2VmWzJdLDMpKWAgeFxdCgotIERlIHZlcndhY2h0ZSBTMTAwQTgtZXhwcmVzc2llIGlzIGdlbWlkZGVsZCAgYHIgYWJzKHJvdW5kKGxtMSRjb2VmWzJdLDMpKjEwMDApYCBlZW5oZWRlbiBsYWdlciB2b29yIHBhdGnDq250ZW4gbWV0IGVlbiBFU1IxLWV4cHJlc3NpZW5pdmVhdSBkaWUgMTAwMCBlZW5oZWRlbiBob2dlciBsaWd0LgoKLSBWZXJ3YWNodGUgUzEwMEE4IGV4cHJlc3NpZW5pdmVhdSB2b29yIHBhdGnDq250ZW4gbWV0IGVlbiBFU1IxIGV4cHJlc3NpZW5pdmVhdSB2YW4gMjAwMDogIApcW2ByIHJvdW5kKGxtMSRjb2VmWzFdLDIpYC1gciBhYnMocm91bmQobG0xJGNvZWZbMl0sMykpYFx0aW1lcyAyMDAwPWByIHJvdW5kKGxtMSRjb2VmWzFdK2xtMSRjb2VmWzJdKjIwMDAsMilgXF0KCi0gVmVyd2FjaHRlIFMxMDBBOCBleHByZXNzaWVuaXZlYXUgdm9vciBwYXRpw6tudGVuIG1ldCBlZW4gRVNSMSBleHByZXNzaWVuaXZlYXUgdmFuIDQwMDA6ICAKXFtgciByb3VuZChsbTEkY29lZlsxXSwyKWAtYHIgYWJzKHJvdW5kKGxtMSRjb2VmWzJdLDMpKWBcdGltZXMgNDAwMD1gciByb3VuZChsbTEkY29lZlsxXStsbTEkY29lZlsyXSo0MDAwLDIpYFxdCi0gKipMZXQgb3Agdm9vciBleHRyYXBvbGF0aWUhKiogKFZlcm9uZGVyc3RlbGxpbmcgdmFuIGxpbmVhcml0ZWl0IGthbiBtZW4gZW5rZWwgbmFnYWFuIGJpbm5lbiBoZXQgYmVyZWlrIHZhbiBkZSBkYXRhKS4KCiMgU3RhdGlzdGlzY2hlIGJlc2x1aXR2b3JtaW5nIChzdGF0aXN0aXNjaGUgaW5mZXJlbnRpZSkKCk9tIGJlc2x1aXRlbiB0ZSBrdW5uZW4gdHJla2tlbiBvdmVyIGxpbmVhaXJlIHJlZ3Jlc3NpZW1vZGVsClxbRShZfFgpPVxiZXRhXzArXGJldGFfMSBYXF0KCm1vZXRlbiB3ZSB3ZXRlbjoKCi0gSG9lIGRlIGxlYXN0IHNxdWFyZXMgcGFyYW1ldGVyIHNjaGF0dGVycyB2YXJpw6tyZW4gdmFuIHN0ZWVrcHJvZWYgdG90IHN0ZWVrcHJvZWYsIGVuCi0gRW4gaG9lIGlzIGRpdCBvbmRlciBkZSBudWxoeXBvdGhlc2UgZGF0IGVyIGdlZW4gYXNzb2NpYXRpZSBpcyB0dXNzZW4gcHJlZGljdG9yIGVuIHJlc3BvbnNlCgotIE5vb2R6YWFrIGFhbiBzdGF0aXN0aXNjaCBtb2RlbCEKCi0gTW9kZWxsZWVyIGRlIHZlcmRlbGluZyB2YW4gJFkkIGdlZ2V2ZW4gJFgkIGV4cGxpY2lldDogJGZfe1l8WH0oeSkkCgojIyBNb2RlbGxlZXIgdmVyZGVsaW5nIHZhbiBZPwoKMS4gTmFhc3QgKmxpbmVhcml0ZWl0KiBoZWJiZW4gd2Ugbm9nIGFhbm5hbWVzIG5vZGlnIQoyLiAqT25hZmhhbmtlbGlqa2hlaWQqOiB3YWFybmVtaW5nZW4gJChYXzEsWV8xKSwgLi4uLCAgKFhfbixZX24pJCB6aWpuIGdlbWFha3Qgdm9vciBuIG9uYWZoYW5rZWxpamtlIHN1YmplY3RlbiAoSXMgdmVyZWlzdCBvbSBkZSB2YXJpYW50aWUgdGUgc2NoYXR0ZW4pCjMuICpIb21vc2NlZGFzdGljaXRlaXQgKiBvZiAqZ2VsaWprZSB2YXJpYW50aWVzKjogd2Fhcm5lbWluZ2VuIHZhcmnDq3JlbiBtZXQgZ2VsaWprIGdlbWlkZGVsZGUgcm9uZCBkZSByZWdyZXNzaWVsaWpuLgogICAgLSBSZXNpZHVlbiAkXGVwc2lsb25faSQgaGViYmVuIGdlbGlqa2UgdmFyaWFudGllIHZvb3IgZWxrZSAkWF9pPXgkCiAgICAtICRcdGV4dHt2YXJ9KFlcdmVydCBYPXgpID0gXHNpZ21hXjIkIHZvb3IgZWxrZSAkWD14JAogICAgLSAkXHNpZ21hJCB3b3JkdCBkZSAqcmVzaWR1ZWxlIHN0YW5kYWFyZGRldmlhdGllKiBnZW5vZW1kLgo0LiAqTm9ybWFsaXRlaXQqOiBkZSByZXNpZHVlbiAkXGVwc2lsb25faSQgemlqbiBub3JtYWFsIHZlcmRlZWxkCgohW10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0YXRvbWljcy9zYmMyMC9tYXN0ZXIvZmlndXJlcy9SZWdNb2RlbDMucG5nKXt3aWR0aD0xMDAlfQoKCi0gVWl0IDIsIDMgZW4gNCB2b2xndCBkYXQKJCRcZXBzaWxvbl9pIFx0ZXh0eyBpLmkuZC59IE4oMCxcc2lnbWFeMikuJCQKLSBBbHMgd2Ugb29rIHN0ZXVuZW4gb3AgZWVyc3RlIHZlcm9uZGVyc3RlbGxpbmcgdmFuIGxpbmVhcml0ZWl0OgokJFlfaVx2ZXJ0IFhfaVxzaW0gTihcYmV0YV8wK1xiZXRhXzEgWF9pLFxzaWdtYV4yKSwkJAoKLSBWZXJkZXIga2FuIG1lbiBhYW50b25lbiBkYXQgb25kZXIgZGV6ZSBhYW5uYW1lcwokJFxzaWdtYV4yX3tcaGF0e1xiZXRhfV8wfT1cZnJhY3tcc3VtXGxpbWl0c197aT0xfV5uIFheMl9pfXtcc3VtXGxpbWl0c197aT0xfV5uIChYX2ktXGJhciBYKV4yfSBcdGltZXNcZnJhY3tcc2lnbWFeMn17bn0gXHRleHR7IGVuIH0gXHNpZ21hXjJfe1xoYXR7XGJldGF9XzF9PVxmcmFje1xzaWdtYV4yfXtcc3VtXGxpbWl0c197aT0xfV5uIChYX2ktXGJhciBYKV4yfSQkCi0gZW4gZGF0IGRlIHBhcmFtZXRlcnNjaGF0dGVycyBldmVuZWVucyBub3JtYWFsIHZlcmRlZWxkIHppam4KJCRcaGF0XGJldGFfMCBcc2ltIE5cbGVmdChcYmV0YV8wLFxzaWdtYV4yX3tcaGF0IFxiZXRhXzB9XHJpZ2h0KSBcdGV4dHsgZW4gfSBcaGF0XGJldGFfMSBcc2ltIE5cbGVmdChcYmV0YV8xLFxzaWdtYV4yX3tcaGF0IFxiZXRhXzF9XHJpZ2h0KSQkCgojIyBIb2dlIHNwcmVpZGluZyBvcCAkWCQgdmVyYmV0ZXJ0IGRlIHByZWNpc2llCgokJFxzaWdtYV4yX3tcaGF0e1xiZXRhfV8xfT1cZnJhY3tcc2lnbWFeMn17XHN1bVxsaW1pdHNfe2k9MX1ebiAoWF9pLVxiYXIgWCleMn0kJAoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0b21pY3Mvc2JjMjAvbWFzdGVyL2ZpZ3VyZXMvc3ByZWFkLnBuZyl7IHdpZHRoPTEwMCUgfQoKLSBDb25kaXRpb25lbGUgdmFyaWFudGllICgkXHNpZ21hXjIkKSBpcyBuaWV0IGdla2VuZAotIFNjaGF0dGVuIGQubS52LiBnZW1pZGRlbGRlIHZhbiBkaWUga3dhZHJhdGlzY2hlIGFmd2lqa2luZ2VuIHJvbmQgZGUgcmVncmVzc2llcmVjaHRlCi0gKm1lYW4gc3F1YXJlZCBlcnJvciogKE1TRSkKCiQkXGhhdFxzaWdtYV4yPU1TRT1cZnJhY3tcc3VtXGxpbWl0c197aT0xfV5uIFxsZWZ0KHlfaS1caGF0XGJldGFfMC1caGF0XGJldGFfMVx0aW1lcyB4X2lccmlnaHQpXjJ9e24tMn09XGZyYWN7XHN1bVxsaW1pdHNfe2k9MX1ebiBlXjJfaX17bi0yfS4kJAotIFZvb3IgaGV0IGJla29tZW4gdmFuIGRlemUgc2NoYXR0ZXIgc3RldW5lbiB3ZSBvcCBvbmFmaGFua2VsaWpraGVpZCAoYWFubmFtZSAyKSBlbiBob21vc2NlZGFzdGljaXRlaXQgKGFhbm5hbWUgMykuCi0gZGVlbCBkb29yICRuLTIkCgpOYSBzY2hhdHRpbmcgdmFuICRcc2lnbWFeMiQgYmVrb21lbiB3ZSB2b2xnZW5kZSBzdGFuZGFhcmQgZXJyb3JzOgoKJCRcdGV4dHtTRX1fe1xoYXR7XGJldGF9XzB9PVxoYXRcc2lnbWFfe1xoYXR7XGJldGF9XzB9PVxzcXJ0e1xmcmFje1xzdW1cbGltaXRzX3tpPTF9Xm4gWF4yX2l9e1xzdW1cbGltaXRzX3tpPTF9Xm4gKFhfaS1cYmFyIFgpXjJ9IFx0aW1lc1xmcmFje1x0ZXh0e01TRX19e259fSBcdGV4dHsgZW4gfSBcdGV4dHtTRX1fe1xoYXR7XGJldGF9XzF9PVxoYXRcc2lnbWFfe1xoYXR7XGJldGF9XzF9PVxzcXJ0e1xmcmFje1x0ZXh0e01TRX19e1xzdW1cbGltaXRzX3tpPTF9Xm4gKFhfaS1cYmFyIFgpXjJ9fSQkCgotIE9wbmlldXcgdG9ldHNlbiBlbiBiZXRyb3V3YmFhcmhlaWRzaW50ZXJ2YWxsZW4gby5iLnYuCiQkVD1cZnJhY3tcaGF0e1xiZXRhfV9rLVxiZXRhX2t9e1NFKFxoYXR7XGJldGF9X2spfSBcdGV4dHsgd2l0aCB9IGs9MSwyLiQkCgotIEFscyBhYW4gYWxsZSBhYW5uYW1lcyBpcyB2b2xkYWFuIHZvbGd0ICRUJCBlZW4gdC12ZXJkZWxpbmcgbWV0IG4tMiB2cmlqaGVpZHNncmFkZW4uCgotIEFscyBnZWVuIG5vcm1hbGl0ZWl0IG1hYXIgd2VsIG9uYWZoYW5rZWxpamssIGxpbmVhcml0ZWl0IGVuIGhvbW9zY2VkYXN0aWNpdGVpdCBlbiBncm90ZSBkYXRhc2V0ClxbXHJpZ2h0YXJyb3cgXHRleHR7Q2VudHJhbGUgTGltaWV0c3RlbGxpbmd9XF0KCgojIyMgQm9yc3RrYW5rZXIgdm9vcmJlZWxkCgotIE5lZ2F0aWV2ZSBhc3NvY2lhdGllIHR1c3NlbiBTMTAwQTggZW4gRVNSMSBnZW4gZXhwcmVzc2llLgoKLSBWZXJhbGdlbWVlbiBlZmZlY3QgaW4gc3RlZWtwcm9lZiBuYWFyIHBvcHVsYXRpZSBtZXQgYmVodWxwIHZhbiBoZXQgYmV0cm91d2JhYXJoZWlkc2ludGVydmFsIG9wIGRlIGhlbGxpbmc6CiQkW1xoYXRcYmV0YV8xIC0gdF97bi0yLFxhbHBoYS8yfSBcdGV4dHtTRX1fe1xoYXRcYmV0YV8xfSxcaGF0XGJldGFfMSArIHRfe24tMixcYWxwaGEvMn0gXHRleHR7U0V9X3tcaGF0XGJldGFfMX1dJCQuCgpgYGB7cn0KY29uZmludChsbTEpCmBgYAoKLSBOZWdhdGllZiB2ZXJiYW5kIGlzIHNpZ25pZmljYW50IG9wIGhldCA1JSBzaWduaWZpY2FudGllbml2ZWF1LgoKCiMjIEh5cG90aGVzZSB0ZXN0CgotIFZlcnRhYWwgZGUgb25kZXJ6b2Vrc3ZyYWFnICJJcyBlciBlZW4gYXNzb2NpYXRpZSB0dXNzZW4gZGUgUzEwMEE4LSBlbiBFU1IxLWdlbmV4cHJlc3NpZT8iIG5hYXIgZGUgcGFyYW1ldGVycyBpbiBoZXQgbW9kZWwuCi0gT25kZXIgbnVsaHlwb3RoZXNlIGdlZW4gYXNzb2NpYXRpZSB0dXNzZW4gZXhwcmVzc2llIHZhbiBiZWlkZSBnZW5lbjoKJCRIXzA6IFxiZXRhXzE9MCQkCgotIE9uZGVyIGFsdGVybmF0aWV2ZSBoeXBvdGhlc2UgaXMgZXIgZWVuIGFzc29jaWF0aWUgdHVzc2VuIGJlaWRlIGdlbmVuOgokJEhfMTogXGJldGFfMVxuZXEwJCQKCi0gVGVzdCBzdGF0aXN0aWVrCiQkVD1cZnJhY3tcaGF0e1xiZXRhfV8xLTB9e1NFKFxoYXR7XGJldGF9X2spfSQkCgotIE9uZGVyICRIXzAkIHZvbGd0IGRlIHN0YXRpc3RpZWsgZWVuIHQtdmVyZGVsaW5nIG1ldCBuLTIgdnJpamhlaWRzZ3JhZGVuLgoKIyMjIEJyY2EgZGF0YXNldAoKYGBge3J9CnN1bW1hcnkobG0xKQpgYGAKCgotIEFzc29jaWF0aWUgdHVzc2VuIGRlIFMxMDBBOCBlbiBFU1IxIGdlbmV4cHJlc3NpZSBleHRyZWVtIHNpZ25pZmljYW50IGlzIChwPDwwLjAwMSkuCi0gTWFhciBlZXJzdCBtb2V0ZW4gd2UgYWxsZSBhc3N1bXB0aWVzIGNvbnRyb2xlcmVuIQotIEFuZGVycyBrdW5uZW4gZGUgY29uY2x1c2llcyBvLmIudi4gZGUgc3RhdGlzdGlzY2hlIHRlc3QgZW4gaGV0IEJJIG9uanVpc3Qgemlqbi4gIAoKIyBOYWdhYW4gdmFuIG1vZGVsdmVyb25kZXJzdGVsbGluZ2VuCgotIE9uYWZoYW5rZWxpamtoZWlkOiBkZXNpZ24KLSBMaW5lYXJpdGVpdDogYmVzbHVpdHZvcm1pbmcgZ2VlbiB6aW4gYWxzIG1vZGVsIG5pZXQgbGluZWFpciBpcwotIEhvbW9zY2VkYXN0aWNpdGVpdDogYmVzbHVpdHZvcm1pbmcvcC13YWFyZGUgaXMgbmlldCBiZXRyb3V3YmFhciBhbHMgZGUgZGF0YSBuaWV0IGhvbW9zY2VkYXN0aXNjaCB6aWpuCi0gTm9ybWFsaXRlaXQ6IGJlc2x1aXR2b3JtaW5nL3Atd2FhcmRlIGlzIG5pZXQgYmV0cm91d2JhYXIgYWxzIGRlIGRhdGEgbmlldCBub3JtYWFsIHZlcmRlZWxkIHppam4gaW4ga2xlaW5lIHN0ZWVrcHJvZXZlbgoKCiMjIExpbmVhcml0ZWl0CgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9CmJyY2FTdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4ID0gRVNSMSwgeSA9IFMxMDBBOCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIGNvbCA9ICJncmV5IikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpCmBgYAoKCiMjIFJlc2lkdXBsb3RzCgotIEFmd2lqa2luZ2VuIHZhbiBsaW5lYXJpdGVpdCBlY2h0ZXIgbWFra2VsaWprZXIgb3BnZXNwb29yZCBkLm0udi4gZWVuICpyZXNpZHVwbG90Ki4gKFpla2VyIGFscyBlciBsYXRlciBtZWVyIHZhcmlhYmVsZW4gemlqbiBpbiBoZXQgbGluZWFpcmUgbW9kZWwpCgotIHZlcmtsYXJlbmRlIHZhcmlhYmVsZSBvZiBwcmVkaWN0aWVzICRcaGF0XGJldGFfMCtcaGF0XGJldGFfMSB4JCBvcCBkZSAkWCQtYXMKCi0gZGUgKnJlc2lkdWVuKiBvcCBkZSAkWSQtYXMKJCRlX2k9eV9pLVxoYXR7Z30oeF9pKT15X2ktXGhhdFxiZXRhXzAtXGhhdFxiZXRhXzFcdGltZXMgeF9pLCQkCgpgYGB7cn0KcGxvdChsbTEpCmBgYAoKIyMgSG9tb3NjZWRhc3RpY2l0ZWl0IChnZWxpamtoZWlkIHZhbiB2YXJpYW50aWUpCgotIFJlc2lkdWVuIGVuIGt3YWRyYXRpc2NoZSByZXNpZHXigJlzIGRyYWdlbiBpbmZvcm1hdGllIG92ZXIgcmVzaWR1ZWxlIHZhcmlhYmlsaXRlaXQuCgotIEFzc29jaWF0aWUgbWV0IGRlIHZlcmtsYXJlbmRlIHZhcmlhYmVsZW4gJFxyaWdodGFycm93JCBpbmRpY2F0aWUgdmFuIGhldGVyb3NjZWRhc3RpY2l0ZWl0LgoKLSBTY2F0dGVycGxvdCB2YW4gb2YgJGVfaSQgdmVyc3VzICR4X2kkIG9mIHByZWRpY3RpZXMgJFxoYXQgXGJldGFfMCsgXGhhdCBcYmV0YV8xIHhfaSQuCgotIFNjYXR0ZXJwbG90IHZhbiBnZXN0YW5kYXJkaXNlZXJkIHJlc2lkdSB2ZXJzdXMgJHhfaSQgb2YgcHJlZGljdGllcy4KCgojIyBOb3JtYWxpdGVpdAoKLSBJbmRpZW4gdm9sZG9lbmRlIGdlZ2V2ZW5zLCBzY2hhdHRlcnMgbm9ybWFhbCB2ZXJkZWVsZCB6ZWxmcyB3YW5uZWVyIG9ic2VydmF0aWVzIG5pZXQgTm9ybWFhbCB2ZXJkZWVsZCB6aWpuOiBjZW50cmFsZSBsaW1pZXQgc3RlbGxpbmcKCi0gV2F0IOKAmHZvbGRvZW5kZSBvYnNlcnZhdGllc+KAmSB6aWpuLCBoYW5ndCBhZiB2YW4gaG9lIGdvZWQgZGUgdmVyZGVsaW5nIG9wIGRlIE5vcm1hbGUgbGlqa3QuCgotIEFhbm5hbWUgaXMgZGF0IHVpdGtvbXN0ZW4gTm9ybWFhbCB2ZXJkZWVsZCB6aWpuIGJpaiB2YXN0ZSB3YWFyZGVuIHZhbiBkZSB2ZXJrbGFyZW5kZSB2YXJpYWJlbGVuLgokJFlfaVx2ZXJ0IFhfaVxzaW0gTihcYmV0YV8wK1xiZXRhXzFYX2ksXHNpZ21hXjIpJCQKCi0gUVEtcGxvdCB2YW4gcmVzcG9uc2UgWSBpcyBoZWVsIG1pc2xlaWRlbmQuCgotIFFRLXBsb3QgdmFuIGRlIHJlc2lkdWVuICRlX2kkCgpgYGB7ciBlY2hvPUZBTFNFfQpzZXQuc2VlZCA8LSAyMDAKcGFyKG1mcm93PWMoMSwzKSkKeCA8LSByZXAoMToxMCxlYWNoPTIwKQp5IDwtIHgrcm5vcm0obGVuZ3RoKHgpKQpib3hwbG90KHl+eCkKcXFub3JtKHksIG1haW49Ik9yaWdpbmFsIG9ic2VydmF0aW9ucyIpCnFxbGluZSh5KQpsbUggPC0gbG0oeX54KQpwbG90KGxtSCwgd2hpY2g9MiwgbWFpbj0iUmVzaWR1YWxzIikKYGBgCgoKYGBge3J9CnBsb3QobG0xLCB3aGljaD0yKQpgYGAKCgojIEFmd2lqa2luZ2VuIHZhbiBNb2RlbHZlcm9uZGVyc3RlbGxpbmdlbgoKLSBUcmFuc2Zvcm1hdGllIHZhbiBvbmFmaGFua2VsaWprZSB2ZXJhbmRlcmxpamtlIHdpanppZ3QgZGUgdmVyZGVsaW5nIHZhbiBZIGJpaiBnZWdldmVuIFggbmlldDoKCiAgICAtIGhlbHB0IG5pZXQgb20gbm9ybWFsaXRlaXQgb2YgaG9tb3NjZWRhc3RpY2l0ZWl0IHRlIGJla29tZW4KICAgIC0gaGVscHQgd2VsIG9tIGxpbmVhcml0ZWl0IHRlIGJla29tZW4gd2FubmVlciBlciBub3JtYWxpdGVpdCBlbiBob21vc2NlZGFzdGljaXRlaXQgaXMKICAgIC0gVmFhayBvb2sgaG9nZXJlIG9yZGUgdGVybWVuOiAkWF4yJCwgJFheMyQsIC4uLgogICAgJCRZX2k9XGJldGFfMCtcYmV0YV8xWF9pK1xiZXRhXzJYX2leMisgLi4uICsgXGVwc2lsb25faSQkCgoKLSBUcmFuc2Zvcm1hdGllIHZhbiByZXNwb25zZSBZIGthbiBoZWxwZW4gb20gbm9ybWFsaXRlaXQgZW4gaG9tb3NjZWRhc3RpY2l0ZWl0IHRlIGJla29tZW4uCgogICAgLSAgJFxzcXJ0KFkpJCwgJFxsb2coWSkkLCAxL1ksIC4uLgoKCiMjIEJvcnN0a2Fua2VyIHZvb3JiZWVsZAoKUHJvYmxlbWVuIG1ldAoKLSAgaGV0ZXJvc2NlZGFzdGljaXRlaXQKLSBtb2dlbGlqa3NlIGFmd2lqa2luZyB2YW4gbm9ybWFsaXRlaXQgKHNjaGVlZmhlaWQgbmFhciByZWNodHMpCi0gbmVnYXRpZXZlIGNvbmNlbnRyYXRpZXZvb3JzcGVsbGluZ2VuIGRpZSB0aGVvcmV0aXNjaCBuaWV0IG1vZ2VsaWprIHppam4KLSBuaWV0LWxpbmVhaXJpdGVpdAoKLSB0cmVlZHQgdmVlbGFsIG9wIGJpaiBjb25jZW50cmF0aWUgZW4gaW50ZW5zaXRlaXRzbWV0aW5nZW4KLSBEZXplIHppam4gdmFhayBsb2ctbm9ybWFhbCB2ZXJkZWVsZCAobm9ybWFsZSB2ZXJkZWxpbmcgbmEgbG9nLXRyYW5zZm9ybWF0aWUpCi0gSW4gRmlndXVyIDYuMyBldmVuZWVucyBlZW4gc29vcnQgZXhwb25lbnRpw6tsZSB0cmVuZAotIEluIGRlIGdlbmV4cHJlc3NpZSBsaXRlcmF0dXVyIHdvcmR0IHZlZWxhbCBnZWJydWlrIGdlbWFhayB2YW4gJFxsb2dfMiQgdHJhbnNmb3JtYXRpZQotIGdlbi1leHByZXNzaWUgb3AgbG9nLXNjaGFhbCBwcm9wb3J0aW9uZWxlIHZlcnNjaGlsbGVuIG9wIGRlIG9yaWdpbmVsZSBzY2hhYWwKCgpgYGB7cn0KYnJjYSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBFU1IxLCB5ID0gUzEwMEE4KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQpgYGAKCmBgYHtyfQpicmNhICU+JQogIGdncGxvdChhZXMoCiAgICB4ID0gRVNSMSAlPiUgbG9nMiwKICAgIHkgPSBTMTAwQTggJT4lIGxvZzIpCiAgICApICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKCkKYGBgCgpgYGB7cn0KbG0yPC1sbShTMTAwQTggJT4lIGxvZzIgfiBFU1IxICU+JSBsb2cyLCBicmNhKQpwbG90KGxtMikKc3VtbWFyeShsbTIpCmBgYAoKCmBgYHtyfQpjb25maW50KGxtMikKYGBgCgoKIyMjIEludGVycHJldGF0aWUgMQoKRWVuIHBhdGnDq250IG1ldCBlZW4gRVNSMSBleHByZXNzaWUgZGllIDEgZWVuaGVpZCBvcCBkZSAkXGxvZ18yJCBzY2hhYWwgaG9nZXIgbGlndCBkYW4gZGF0IHZhbiBlZW4gYW5kZXJlIHBhdGnDq250IGhlZWZ0IGdlbWlkZGVsZCBnZXppZW4gZWVuIGV4cHJlc3NpZS1uaXZlYXUgdmFuIGhldCBTMTAwQTggZ2VuICBkYXQgYHIgYWJzKHJvdW5kKGxtMiRjb2VmWzJdLDIpKWAgZWVuaGVkZW4gbGFnZXIgbGlndCAoOTVcJSBCSSBbYHIgcGFzdGUocm91bmQoY29uZmludChsbTIpWzIsXSwyKSxjb2xsYXBzZT0iLCIpYF0pLgoKKipDcm9zc2VjdGlvbmVsZSBzdHVkaWU6IGVua2VsIHVpdHNwcmFrZW4gb3ZlciB2ZXJzY2hpbGxlbiB0dXNzZW4gcGF0acOrbnRlbiEqKgoKJCRcbG9nXzIgXGhhdFxtdV8xPTIzLjQwMSAgLTEuNjE1IFx0aW1lcyBcdGV4dHtsb2dFU1J9XzEsXHRleHR7IH0gXGxvZ18yIFxoYXRcbXVfMj0yMy40MDEgIC0xLjYxNSBcdGltZXMgXHRleHR7bG9nRVNSfV8yICQkCiQkXGxvZ18yIFxoYXRcbXVfMi1cbG9nXzIgXGhhdFxtdV8xPSAgLTEuNjE1IChcbG9nXzIgXHRleHR7RVNSfV8yLVxsb2dfMiBcdGV4dHtFU1J9XzEpID0gLTEuNjE1IFx0aW1lcyAxID0gLTEuNjE1JCQKCiMjIyBJbnRlcnByZXRhdGllIDIKCk1vZGVsIG9wIGxvZy1zY2hhYWw6IGJpaiB0ZXJ1Z3RyYW5zZm9ybWF0aWUgdmVya3JpamdlbiB3ZSBoZXQgZ2VvbWV0cmlzY2hlIGdlbWlkZGVsZGUKClxiZWdpbntlcW5hcnJheSp9ClxzdW1cbGltaXRzX3tpPTF9Xm4gXGZyYWN7XGxvZyB4X2l9e259Jj0mXGZyYWN7XGxvZyB4XzEgKyBcbGRvdHMgKyBcbG9nIHhfbn17bn1cXFxcCiZcc3RhY2tyZWx7KDEpfXs9fSZcZnJhY3tcbG9nKHhfMSBcdGltZXMgXGxkb3RzIFx0aW1lcyB4X24pfXtufT1cZnJhY3tcbG9nXGxlZnQoXHByb2RcbGltaXRzX3tpPTF9Xm4geF9pXHJpZ2h0KX17bn1cXFxcCiZcc3RhY2tyZWx7KDIpfXs9fSZcbG9nIFxsZWZ0KFxzcXJ0W1xsZWZ0cm9vdHstMX1cdXByb290ezJ9XHNjcmlwdHN0eWxlIG5de1xwcm9kXGxpbWl0c197aT0xfV5uIHhfaX1ccmlnaHQpClxlbmR7ZXFuYXJyYXkqfQoKLSBQb3B1bGF0aWVnZW1pZGRlbGRlbiAkXG11JCBkdXMgZ2VzY2hhdCBhLmQuaC52LiBnZW9tZXRyaXNjaCBnZW1pZGRlbGRlbi4KLSBMb2dhcml0bWlzY2hlIHRyYW5zZm9ybWF0aWUgaXMgZWVuIG1vbm90b29uOiB3ZSBrdW5uZW4gYmV0cm91d2JhYXJoZWlkc2ludGVydmFsbGVuIGJlcmVrZW5kIG9wIGxvZy1zY2hhYWwgdGVydWd0cmFuc2Zvcm1lcmVuIQoKYGBge3J9CjJebG0yJGNvZWZbMl0KMl4tbG0yJGNvZWZbMl0KMl4tY29uZmludChsbTIpWzIsXQpgYGAKCkVlbiBwYXRpw6tudCBtZXQgZWVuIEVTUjEgZXhwcmVzc2llIGRpZSAyIGtlZXIgem8gaG9vZyBpcyBhbHMgZGllIHZhbiBlZW4gYW5kZXJlIHBhdGnDq250LCB6YWwgZ2VtaWRkZWxkIGVlbiBTMTAwQTgtZXhwcmVzc2llIGhlYmJlbiBkaWUgYHIgcm91bmQoMl4tbG0yJGNvZWZbMl0KLDIpYCBrZWVyIGxhZ2VyIGlzICg5NVwlIEJJIFtgciBwYXN0ZShzb3J0KHJvdW5kKDJeLWNvbmZpbnQobG0yKVsyLF0sMikpLGNvbGxhcHNlPSIsIilgXSkuCgoKJCRcbG9nXzIgXGhhdFxtdV8xPTIzLjQwMSAgLTEuNjE1IFx0aW1lcyBcdGV4dHtsb2dFU1J9XzEsXHRleHR7IH0gXGxvZ18yIFxoYXRcbXVfMj0yMy40MDEgIC0xLjYxNSBcdGltZXMgXHRleHR7bG9nRVNSfV8yICQkCiQkXGxvZ18yIFxoYXRcbXVfMi1cbG9nXzIgXGhhdFxtdV8xPSAgLTEuNjE1IChcbG9nXzIgXHRleHR7RVNSfV8yLVxsb2dfMiBcdGV4dHtFU1J9XzEpICQkCiQkXGxvZ18yIFxsZWZ0W1xmcmFje1xoYXRcbXVfMn17XGhhdFxtdV8xfVxyaWdodF09ICAtMS42MTUgXGxvZ18yXGxlZnRbXGZyYWN7IFx0ZXh0e0VTUn1fMn17XHRleHR7RVNSfV8xfVxyaWdodF0gJCQKJCRcZnJhY3tcaGF0XG11XzJ9e1xoYXRcbXVfMX09XGxlZnRbXGZyYWN7IFx0ZXh0e0VTUn1fMn17XHRleHR7RVNSfV8xfVxyaWdodF1eey0xLjYxNX09Ml4gey0xLjYxNX0gPTAuMzI2JCQKb3IKJCRcZnJhY3tcaGF0XG11XzF9e1xoYXRcbXVfMn09Ml57MS42MTV9ID0zLjA2JCQKCgojIyMgSW50ZXJwcmF0aWUgMwoKRWVuIHBhdGnDq250IG1ldCBlZW4gRVNSMSBleHByZXNzaWUgZGllIDFcJSBob2dlciBpcyBkYW4gZGllIHZhbiBlZW4gYW5kZXJlIHBhdGnDq250IHphbCBnZW1pZGRlbGQgZWVuIGV4cHJlc3NpZW5pdmVhdSB2b29yIGhldCBTMTAwQTggZ2VuICBoZWJiZW4gZGF0IG9uZ2V2ZWVyIGByIHJvdW5kKGxtMiRjb2VmWzJdLDIpYCUgbGFnZXIgaXMgKDk1XCUgQkkgW2ByIHBhc3RlKHJvdW5kKGNvbmZpbnQobG0yKVsyLF0sMiksY29sbGFwc2U9IiwiKWBdKSUuCgokJFxsb2dfMiBcaGF0XG11XzE9MjMuNDAxICAtMS42MTUgXHRpbWVzIFx0ZXh0e2xvZ0VTUn1fMSxcdGV4dHsgfSBcbG9nXzIgXGhhdFxtdV8yPTIzLjQwMSAgLTEuNjE1IFx0aW1lcyBcdGV4dHtsb2dFU1J9XzIgJCQKJCRcbG9nXzIgXGhhdFxtdV8yLVxoYXRcbG9nXzIgXG11XzE9ICAtMS42MTUgKFxsb2dfMiBcdGV4dHtFU1J9XzItXGxvZ18yIFx0ZXh0e0VTUn1fMSkgJCQKJCRcbG9nXzIgXGxlZnRbXGZyYWN7XGhhdFxtdV8yfXtcaGF0XG11XzF9XHJpZ2h0XT0gIC0xLjYxNSBcbG9nXzJcbGVmdFtcZnJhY3sgXHRleHR7RVNSfV8yfXtcdGV4dHtFU1J9XzF9XHJpZ2h0XSAkJAokJFxmcmFje1xoYXRcbXVfMn17XGhhdFxtdV8xfT1cbGVmdFtcZnJhY3sgXHRleHR7RVNSfV8yfXtcdGV4dHtFU1J9XzF9XHJpZ2h0XV57LTEuNjE1fT0xLjAxXiB7LTEuNjE1fSA9MC45ODQgXGFwcHJveCAtMS42XCUkJAoKRGl0IGdlbGR0IHZvb3IgbGFnZSB0b3QgbWF0aWdlIHdhYXJkZW4gdmFuICRcYmV0YV8xJDoKJCQtMTA8XGJldGFfMTwxMCBccmlnaHRhcnJvdyAxLjAxXntcYmV0YV8xfSAtMSBcYXBwcm94IFxmcmFje1xiZXRhXzF9ezEwMH0uJCQKCiMgQmVzbHVpdHZvcm1pbmcgb3ZlciBnZW1pZGRlbGRlIHVpdGtvbXN0CgotIFJlZ3Jlc3NpZSBtb2RlbCBrYW4gb29rIHdvcmRlbiBnZWJydWlrdCB2b29yIHByZWRpY3RpZQoKLSBCZXNsdWl0dm9ybWluZyB0ZSBkb2VuIG92ZXIgZGUgZ2VtaWRkZWxkZSB1aXRrb21zdCBiaWogZWVuIGdlZ2V2ZW4gd2FhcmRlICR4JCwgbS5hLncuCiQkXGhhdHtnfSh4KT0gXGhhdHtcYmV0YX1fMCArIFxoYXR7XGJldGF9XzEgeCQkCi0gJFxoYXR7Z30oeCkkIGVlbiBzY2hhdHRlciB2YW4gaGV0IGNvbmRpdGlvbmVsZSBnZW1pZGRlbGRlICRFW1lcdmVydCBYPXhdJAoKLSBQYXJhbWV0ZXJzY2hhdHRlcnMgTm9ybWFsZSB2ZXJkZWVsZCBlbiBvbnZlcnRla2VuZCAkXHJpZ2h0YXJyb3ckIHNjaGF0dGVyICRcaGF0e2d9KHgpJCBvb2sgTm9ybWFhbCB2ZXJkZWVsZCBlbiBvbnZlcnRla2VuZC4KCiQkXHRleHR7U0V9X3tcaGF0e2d9KHgpfT1cc3FydHtNU0VcbGVmdFx7XGZyYWN7MX17bn0rXGZyYWN7KHgtXGJhciBYKV4yfXtcc3VtXGxpbWl0c197aT0xfV5uIChYX2ktXGJhciBYKV4yfVxyaWdodFx9fS4kJAoKJCRUPVxmcmFje1xoYXR7Z30oeCktZyh4KX17U0Vfe1xoYXR7Z30oeCl9fVxzaW0gdF97bi0yfSQkCgotIEdlbWlkZGVsZGUgdWl0a29tc3QgZW4gYmV0cm91d2JhYXJoZWlkc2ludGVydmFsbGVuIG9wIGRlIGdlbWlkZGVsZGUgdWl0a29tc3QgaW4gUiB2aWEgZGUgYHByZWRpY3QoLilgIGZ1bmN0aWUuCi0gYG5ld2RhdGFgIGFyZ3VtZW50OiBwcmVkaWN0b3J3YWFyZGVuICh4LXdhYXJkZW4pIHZvb3IgaGV0IGJlcmVrZW5lbiB2YW4gZ2VtaWRkZWxkZSB1aXRrb21zdGVuCi0gYGludGVydmFsPSJjb25maWRlbmNlImAgYXJndW1lbnQgb20gYmV0cm91d2JhYXJoZWlkc2ludGVydmFsbGVuIHRlIGJla29tZW4uCi0gWm9uZGVyIGBuZXdkYXRhYCBhcmd1bWVudCB3b3JkdCBkZSBnZW1pZGRlbGRlIHVpdGtvbXN0ZW4gYmVyZWtlbmQgdm9vciBhbGxlIHByZWRpY3RvcndhYXJkZW4gdmFuIGRlIGRhdGFzZXQuCgoKYGBge3J9CmdyaWQgPC0gMTQwOjQwMDAKZyA8LSBwcmVkaWN0KAogIGxtMiwKICBuZXdkYXRhID0gZGF0YS5mcmFtZShFU1IxID0gZ3JpZCksCiAgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpCmhlYWQoZykKYGBgCgpNZXJrIG9wIGRhdCB3ZSBkZSBuaWV1d2UgZGF0YSBkaWUgd2UgZ2VzcGVjaWZpY2VlcmQgaGViYmVuIHZvb3IgZGUgRVNSMSBleHByZXNzaWUgbmlldCBtb2V0ZW4gdHJhbnNmb3JtZXJlbiBvbWRhdCB3ZSBoZXQgbW9kZWwgZml0dGVuIG1ldCBkZSBgbG1gZnVuY3RpZSBlbiBkZSB0cmFuc2Zvcm1hdGllIGhlYmJlbiBnZXNwZWNpZmljZWVyZCBiaW5uZW4gZGllIGZ1bmN0aWUgbWV0IGJlaHVscCB2YW4gaGV0IHBpcGUgY29tbWFuZG8hCgpgYGB7cn0KYnJjYSAlPiUgZ2dwbG90KAogIGFlcygKICAgIHggPSBFU1IxICU+JSBsb2cyLAogICAgeSA9IFMxMDBBOCAlPiUgbG9nMikKICApICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpCmBgYAoKIyMgVGVydWd0cmFuc2Zvcm1hdGllCmBgYHtyfQpuZXdkYXRhPC1kYXRhLmZyYW1lKGNiaW5kKGdyaWQsMl5nKSkKYnJjYSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBFU1IxLCB5ID0gUzEwMEE4KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKGFlcyh4PWdyaWQseT1maXQpLG5ld2RhdGEpICsKICBnZW9tX2xpbmUoYWVzKHg9Z3JpZCx5PWx3ciksbmV3ZGF0YSxjb2xvcj0iZ3JleSIpICsKICBnZW9tX2xpbmUoYWVzKHg9Z3JpZCx5PXVwciksbmV3ZGF0YSxjb2xvcj0iZ3JleSIpCmBgYAoKIyBQcmVkaWN0aWUtaW50ZXJ2YWxsZW4KCi1XZSBrdW5uZW4gb29rIGVlbiB2b29yc3BlbGxpbmcgZG9lbiB2b29yIGRlIGxvY2F0aWUgdmFuIGVlbiBuaWV1d2Ugd2Fhcm5lbWluZyBkaWUgem91IHdvcmRlbiB2ZXJ6YW1lbGQgaW4gZWVuIG5pZXV3IGV4cGVyaW1lbnQgdm9vciBlZW4gcGF0acOrbnQgbWV0IGVlbiBiZXBhYWxkZSB3YWFyZGUgdm9vciBodW4gRVNSMS1leHByZXNzaWUKCi0gSGV0IGlzIGJlbGFuZ3Jpamsgb3AgdGUgbWVya2VuIGRhdCBkaXQgZXhwZXJpbWVudCBub2cgbW9ldCB3b3JkZW4gdWl0Z2V2b2VyZC4gV2Ugd2lsbGVuIGR1cyBkZSBuaWV0LXdhYXJnZW5vbWVuIGluZGl2aWR1ZWxlIGV4cHJlc3NpZXdhYXJkZSB2b29yIGVlbiBuaWV1d2UgcGF0acOrbnQgdm9vcnNwZWxsZW4uCgotIFZvb3IgZWVuIG5pZXV3ZSBvbmFmaGFua2VsaWprZSB3YWFybmVtaW5nICRZXiokCiQkCiAgWV4qID0gZyh4KSArIFxlcHNpbG9uXioKJCQKbWV0ICRcZXBzaWxvbl4qXHNpbSBOKDAsXHNpZ21hXjIpJCBlbiAkXGVwc2lsb25eKiQgb25hZmhhbmtlbGlqayB2YW4gZGUgd2Fhcm5lbWluZ2VuIGluIGRlIHN0ZWVrcHJvZWYgJFlfMSxcbGRvdHMsIFlfbiQuCgotIFdlIHZvb3JzcGVsbGVuIGVlbiBuaWV1d2UgbG9nLVMxMDBBOCB2b29yIGVlbiBwYXRpw6tudCBtZXQgZWVuIGdla2VuZCBsb2cyLUVTUjEtZXhwcmVzc2llbml2ZWF1IHgKXFsKICBcaGF0e3l9KHgpPVxoYXR7XGJldGF9XzArXGhhdHtcYmV0YX1fMSBcdGltZXMgeApcXQoKLSAgRGUgZ2VzY2hhdHRlIGdlbWlkZGVsZGUgdWl0a29tc3QgZW4gdm9vcnNwZWxsaW5nIHZvb3IgZWVuIG5pZXV3ZSB3YWFybmVtaW5nIHppam4gZ2VsaWprLgoKLSAgTWFhciBodW4gc3RlZWtwcm9lZiB2ZXJkZWxpbmdlbiB6aWpuIGFuZGVycyEKCiAgICAtIE9uemVrZXJoZWlkIG92ZXIgZGUgZ2VzY2hhdHRlIGdlbWlkZGVsZGUgdWl0a29tc3QgICAkXGxlZnRhcnJvdyQgb256ZWtlcmhlaWQgb3ZlciBkZSBnZXNjaGF0dGUgbW9kZWxwYXJhbWV0ZXJzICRcaGF0XGJldGFfMCQgZW4gJFxoYXRcYmV0YV8xJC4KICAgIC0gT256ZWtlcmhlaWQgb3ZlciBuaWV1d2Ugd2Fhcm5lbWluZyAkXGxlZnRhcnJvdyQgKm9uemVrZXJoZWlkIG92ZXIgZ2VzY2hhdCBnZW1pZGRlbGRlKiBlbiAgKmV4dHJhIG9uemVrZXJoZWlkKiAgb21kYXQgZGUgbmlldXdlIHdhYXJuZW1pbmcgemFsIGFmd2lqa2VuIHJvbmQgaGV0IGdlbWlkZGVsZGUhCgoKJCRcdGV4dHtTRX1fe1xoYXR7WX0oeCl9PVxzcXJ0e1xoYXRcc2lnbWFeMitcaGF0XHNpZ21hXjJfe1xoYXR7Z30oeCl9fT1cc3FydHtNU0VcbGVmdFx7MStcZnJhY3sxfXtufStcZnJhY3soeC1cYmFyIFgpXjJ9e1xzdW1cbGltaXRzX3tpPTF9Xm4gKFhfaS1cYmFyIFgpXjJ9XHJpZ2h0XH19LiQkCgokJFxmcmFje1xoYXR7WX0oeCktWX17XHRleHR7U0V9X3tcaGF0e1l9KHgpfX1cc2ltIHRfe24tMn0kJAoKLSBNZXJrIG9wIGRhdCBlZW4gKipwcmVkaWN0aWUtaW50ZXJ2YWwqKiAoUEkpIGVlbiB2ZXJiZXRlcmRlIHZlcnNpZSBpcyB2YW4gZWVuIHJlZmVyZW50aWUtaW50ZXJ2YWwgd2FubmVlciBkZSBtb2RlbHBhcmFtZXRlcnMgb25iZWtlbmQgemlqbjogb256ZWtlcmhlaWQgb3ZlciBtb2RlbHBhcmFtZXRlcnMgKyB0LXZlcmRlbGluZy4KCgpgYGB7cn0KcCA8LSBwcmVkaWN0KAogIGxtMiwKICBuZXdkYXRhID0gZGF0YS5mcmFtZShFU1IxID0gZ3JpZCksCiAgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQoKaGVhZChwKQpgYGAKCmBgYHtyfQpwcmVkZGF0YTwtZGF0YS5mcmFtZSgKICBncmlkID0gZ3JpZCU+JWxvZzIsCiAgcCkKYnJjYSAlPiUgZ2dwbG90KGFlcyh4PUVTUjElPiVsb2cyLHk9UzEwMEE4JT4lbG9nMikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArCiAgICAgZ2VvbV9saW5lKGFlcyh4PWdyaWQseT1sd3IpLHByZWRkYXRhLGNvbG9yPSJibHVlIikgKwogIGdlb21fbGluZShhZXMoeD1ncmlkLHk9dXByKSxwcmVkZGF0YSxjb2xvcj0iYmx1ZSIpCmBgYAoKYGBge3J9CnByZWRkYXRhPC1kYXRhLmZyYW1lKGNiaW5kKGdyaWQsMl5wKSkKYnJjYSAlPiUgZ2dwbG90KGFlcyh4ID0gRVNSMSwgeSA9IFMxMDBBOCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZSgKICAgIGFlcyh4ID0gZ3JpZCx5ID0gZml0KSwKICAgIG5ld2RhdGEpICsKICBnZW9tX2xpbmUoCiAgICBhZXMoeCA9IGdyaWQsIHkgPSBsd3IpLAogICAgbmV3ZGF0YSwKICAgIGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV9saW5lKAogICAgYWVzKHggPSBncmlkLCB5ID0gdXByKSwKICAgIG5ld2RhdGEsCiAgICBjb2xvciA9ICJncmV5IikgKwogIGdlb21fbGluZSgKICAgIGFlcyh4ID0gZ3JpZCwgeSA9IGx3ciksCiAgICBwcmVkZGF0YSwKICAgIGNvbG9yID0gImJsdWUiKSArCiAgZ2VvbV9saW5lKAogICAgYWVzKHggPSBncmlkLHkgPSB1cHIpLAogICAgcHJlZGRhdGEsCiAgICBjb2xvciA9ICJibHVlIikKYGBgCgoKIyMgTkhBTkVTIHZvb3JiZWVsZAoKCi0gVmVyZ2VsaWprIHJlZmVyZW50aWUtaW50ZXJ2YWwgdm9vciBjaG9sZXN0ZXJvbGdlaGFsdGUgbWV0IHByZWRpY3RpZSBpbnRlcnZhbC4KCi0gUmVmZXJlbnRpZS1pbnRlcnZhbAoKYGBge3J9CmxpYnJhcnkoTkhBTkVTKQpmZW0gPC0gTkhBTkVTICU+JQogIGZpbHRlcihHZW5kZXI9PSJmZW1hbGUiJiFpcy5uYShEaXJlY3RDaG9sKSkKCjJeKAogIGZlbSAlPiUKICBwdWxsKERpcmVjdENob2wpICU+JQogIGxvZzIgJT4lCiAgbWVhbiArCiAgICBjKC0xLDEpICoKICAgIHFub3JtKDAuOTc1KSAqCiAgICAoZmVtICU+JQogICAgICBwdWxsKERpcmVjdENob2wpICU+JQogICAgICBsb2cyICU+JQogICAgICBzZCkKICApCmBgYAoKLSBQcmVkaWN0aWUgaW50ZXJ2YWwKCmBgYHtyfQpsbUNob2wgPC0gbG0oRGlyZWN0Q2hvbCAlPiUgbG9nMiB+IDEsIGRhdGE9ZmVtKQpwcmVkSW50IDwtIHByZWRpY3QoCiAgbG1DaG9sLAogIGludGVydmFsPSJwcmVkaWN0aW9uIiwKICBuZXdkYXRhPWRhdGEuZnJhbWUobm9QcmVkPTEpCiAgKQpyb3VuZCgyXnByZWRJbnQsMikKYGBgCgpNZXJrIG9wIGRhdCBoZXQgdm9vcnNwZWxsaW5nc2ludGVydmFsIGJpam5hIGdlbGlqayBpcyBhYW4gaGV0IHJlZmVyZW50aWUtaW50ZXJ2YWwgdm9vciBkZSBncm90ZSBzdGVla3Byb2VmLiBXZSBrb25kZW4gZGUgcGFyYW1ldGVycyBpbmRlcmRhYWQgaGVlbCBwcmVjaWVzIHNjaGF0dGVuLgoKV2UgenVsbGVuIGhldHplbGZkZSBkb2VuIHZvb3IgZWVuIGtsZWluZSBzdGVla3Byb2VmIHZhbiAxMCBwYXRpw6tudGVuLgoKLSBSZWZlcmVudGllIGludGVydmFsCgpgYGB7cn0Kc2V0LnNlZWQoMSkKZmVtMTAgPC0gTkhBTkVTICU+JQogIGZpbHRlcihHZW5kZXI9PSJmZW1hbGUiJiFpcy5uYShEaXJlY3RDaG9sKSkgJT4lCiAgc2FtcGxlX24oc2l6ZT0xMCkKCjJeKAogIGZlbTEwICU+JQogICAgcHVsbChEaXJlY3RDaG9sKSAlPiUKICAgIGxvZzIgJT4lCiAgICBtZWFuICsKICAgICAgYygtMSwxKSAqCiAgICAgIHFub3JtKDAuOTc1KSAqCiAgICAgIChmZW0xMCAlPiUKICAgICAgICBwdWxsKERpcmVjdENob2wpICU+JQogICAgICAgIGxvZzIgJT4lCiAgICAgICAgc2QpCiAgKQpgYGAKCkhldCByZWZlcmVudGllLWludGVydmFsIGlzIHZlZWwgc21hbGxlciBkYW4gaW4gZGUgZ3JvdGUgc3RlZWtwcm9lZi4KCi0gUHJlZGljdGllIGludGVydmFsCgpgYGB7cn0KbG1DaG9sMTAgPC0gbG0oRGlyZWN0Q2hvbCAlPiUgbG9nMiB+IDEsIGRhdGEgPSBmZW0xMCkKcHJlZEludDEwIDwtIHByZWRpY3QoCiAgbG1DaG9sMTAsCiAgaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIsCiAgbmV3ZGF0YSA9IGRhdGEuZnJhbWUobm9QcmVkPTEpCiAgKQpyb3VuZCgyXnByZWRJbnQxMCwgMikKYGBgCgotIE1lcmsgb3AgZGF0IGhldCBQSSBudSBvbnpla2VyaGVpZCBtZWVuZWVtdCBpbiBwYXJhbWV0ZXJzY2hhdHRlcnMgKGdlbWlkZGVsZGUgZW4gc3RhbmRhYXJkIGVycm9yKS4KRW4gZGF0IGhldCBpbnRlcnZhbCB2ZWVsIGJyZWRlciB3b3JkdCEgRGl0IGlzIGhpZXIgdm9vcmFsIGJlbGFuZ3Jpamsgdm9vciBkZSBib3ZlbmdyZW5zIG9tZGF0IHdlIGRlIGdlZ2V2ZW5zIHRlcnVnIGhlYmJlbiBnZXRyYW5zZm9ybWVlcmQhCgotIEhldCBpbnRlcnZhbCBpcyBiaWpuYSBuZXQgem8gYnJlZWQgYWxzIGRhdCBnZWJhc2VlcmQgb3AgZGUgZ3JvdGUgc3RlZWtwcm9lZi4KCi0gQmlqIGtsZWluZSBzdGVla3Byb2V2ZW4gaXMgaGV0IGVyZyBiZWxhbmdyaWprIG9tIG1ldCBkZXplIGV4dHJhIG9uemVrZXJoZWlkIHJla2VuaW5nIHRlIGhvdWRlbi4KCgojIEt3YWRyYXRlbnNvbW1lbiBlbiBBbm92YS10YWJlbAoKIyMgVG90YWxlIGt3YWRyYXRlbnNvbQoKJCRcdGV4dHtTU1RvdH0gPSBcc3VtX3tpPTF9Xm4gKFlfaS1cYmFye1l9KV4yLiQkCgotIFNTdG90IGthbiB3b3JkZW4gZ2VicnVpa3Qgb20gZGUgdmFyaWFudGllIHZhbiBkZSAqKm1hcmdpbmFsZSB2ZXJkZWxpbmcqKiB2YW4gZGUgcmVzcG9ucyB0ZSBzY2hhdHRlbi4KCi0gSW4gZGl0IGhvb2Zkc3R1ayBoZWJiZW4gd2Ugb25zIGdlZm9jdXNlZCBvcCBkZSAqKmNvbmRpdGlvbmVsZSB2ZXJkZWxpbmcqKiAkZihZXHZlcnQgWD14KSQuCgotIFdlIHdldGVuIGRhdCBNU0UgZWVuIGdvZWRlIHNjaGF0dGluZyBpcyB2YW4gZGUgdmFyaWFudGllIHZhbiBkZSBjb25kaXRpb25lbGUgdmVyZGVsaW5nIHZhbiAgJFlcdmVydCBYPXgkLgoKCmBgYHtyIG91dC53aWR0aD0nMTAwJScsIGZpZy5hc3A9LjgsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1GQUxTRX0KYnJjYSRsb2cyRVNSMSA8LSBsb2cyKGJyY2EkRVNSMSkKYnJjYSRsb2cyUzEwMEE4IDwtIGxvZzIoYnJjYSRTMTAwQTgpCnBsb3QobG9nMlMxMDBBOCB+IGxvZzJFU1IxLAogIGRhdGEgPSBicmNhLAogIHhsYWIgPSAiRVNSMSBleHByZXNzaWUgKGxvZzIpIiwKICB5bGFiID0gIlMxMDBBOCBleHByZXNzaWUgKGxvZzIpIiwKICBjZXguYXhpcz0xLjUsCiAgY2V4Lm1haW49MS41LAogIGNleC5sYWI9MS41LGNvbD00KQphYmxpbmUoaCA9IG1lYW4oYnJjYSRsb2cyUzEwMEE4KSkKZm9yIChpIGluIDE6bGVuZ3RoKGJyY2EkbG9nMlMxMDBBOCkpIGxpbmVzKHJlcChicmNhJGxvZzJFU1IxW2ldLDIpLGMobWVhbihicmNhJGxvZzJTMTAwQTgpLGJyY2EkbG9nMlMxMDBBOFtpXSksbHR5PTIsY29sPTQpCmBgYAoKIyMgS3dhZHJhdGVuc29tIHZhbiBkZSByZWdyZXNzaWUgU1NSCgokJFx0ZXh0e1NTUn0gPSBcc3VtX3tpPTF9Xm4gKFxoYXR7WX1faSAtIFxiYXJ7WX0pXjIgPSBcc3VtX3tpPTF9Xm4gKFxoYXR7Z30oeF9pKSAtIFxiYXJ7WX0pXjIuJCQKCi0gTWFhdCB2b29yIGRlIGFmd2lqa2luZyB0dXNzZW4gZGUgcHJlZGljdGllcyBvcCBkZSBnZXNjaGF0dGUgcmVncmVzc2llcmVjaHRlIGVuIGhldCBzdGVla3Byb2VmZ2VtaWRkZWxkZSB2YW4gZGUgdWl0a29tc3Rlbi4KCi0gRWVuIGFuZGVyZSBpbnRlcnByZXRhdGllOiB2ZXJzY2hpbCB0dXNzZW4gdHdlZSBtb2RlbGxlbgoKICAgIC0gR2VzY2hhdHRlIG1vZGVsICRcaGF0e2d9KHgpPVxoYXRcYmV0YV8wK1xoYXRcYmV0YV8xeCQKICAgIC0gR2VzY2hhdHRlIG1vZGVsIHpvbmRlciBwcmVkaWN0b3IgKGVua2VsIGludGVyY2VwdCk6ICRnKHgpPVxiZXRhXzAkICRccmlnaHRhcnJvdyQgJFxiZXRhXzAkIHphbCBnZWxpamsgemlqbiBhYW4gJFxiYXJ7WX0kLgoKLSBTU1IgbWVldCBkZSBncm9vdHRlIHZhbiBoZXQgZWZmZWN0IHZhbiBkZSBwcmVkaWN0b3IKCmBgYHtyIG91dC53aWR0aD0nMTAwJScsIGZpZy5hc3A9LjgsIGZpZy5hbGlnbj0nY2VudGVyJyxlY2hvPUZBTFNFfQpwbG90KGxvZzJTMTAwQTh+bG9nMkVTUjEsYnJjYSx4bGFiPSJFU1IxIGV4cHJlc3NpZSAobG9nMikiLHlsYWI9IlMxMDBBOCBleHByZXNzaWUgKGxvZzIpIixjZXguYXhpcz0xLjUsY2V4Lm1haW49MS41LGNleC5sYWI9MS41KQphYmxpbmUoaD1tZWFuKGJyY2EkbG9nMlMxMDBBOCkpCmFibGluZShsbTIsY29sPTIpCnBvaW50cyhicmNhJGxvZzJFU1IxLGxtMiRmaXR0ZWQscGNoPTIsY29sPTIpCmZvciAoaSBpbiAxOmxlbmd0aChicmNhJGxvZzJTMTAwQTgpKSBsaW5lcyhyZXAoYnJjYSRsb2cyRVNSMVtpXSwyKSxjKG1lYW4oYnJjYSRsb2cyUzEwMEE4KSxsbTIkZml0dGVkW2ldKSxsdHk9Mixjb2w9MikKYGBgCgoKIyMgS3dhZHJhdGVuc29tIHZhbiBkZSBmb3V0ZW4KCiQkIFx0ZXh0e1NTRX0gPSBcc3VtX3tpPTF9Xm4gKFlfaS1caGF0e1l9X2kgKV4yID0gXHN1bV97aT0xfV5uIFxsZWZ0XHtZX2ktXGhhdHtnfVxsZWZ0KHhfaVxyaWdodClccmlnaHRcfV4yLiQkCgotIEhvZSBrbGVpbmVyIGRlIFNTRSwgaG9lIGJldGVyIGhldCBtb2RlbCBmaXQuCgoKLSBLbGVpbnN0ZSBrd2FkcmF0ZW4gdGVjaG5pZWshCgotLS0KCmBgYHtyIG91dC53aWR0aD0nMTAwJScsIGZpZy5hc3A9LjgsIGZpZy5hbGlnbj0nY2VudGVyJyxlY2hvPUZBTFNFfQpwbG90KGxvZzJTMTAwQTh+bG9nMkVTUjEsYnJjYSx4bGFiPSJFU1IxIGV4cHJlc3NpZSAobG9nMikiLHlsYWI9IlMxMDBBOCBleHByZXNzaWUgKGxvZzIpIixjZXguYXhpcz0xLjUsY2V4Lm1haW49MS41LGNleC5sYWI9MS41KQphYmxpbmUobG0yLGNvbD0yKQpwb2ludHMoYnJjYSRsb2cyRVNSMSxsbTIkZml0dGVkLHBjaD0yLGNvbD0yKQpmb3IgKGkgaW4gMTpsZW5ndGgoYnJjYSRsb2cyUzEwMEE4KSkgbGluZXMocmVwKGJyY2EkbG9nMkVTUjFbaV0sMiksYyhicmNhJGxvZzJTMTAwQThbaV0sbG0yJGZpdHRlZFtpXSksbHR5PTIpCmBgYAoKV2Uga3VubmVuIGFhbnRvbmVuIGRhdCBTU1Qga2FuIHdvcmRlbiBvbnRib25kZW4gaW4KXGJlZ2lue2VxbmFycmF5Kn0KICBcdGV4dHtTU1RvdH0KICAgICY9JiAgXHN1bV97aT0xfV5uIChZX2ktXGJhcntZfSleMiBcXAogICAgJj0mICBcc3VtX3tpPTF9Xm4gKFlfaS1caGF0e1l9X2krXGhhdHtZfV9pLVxiYXJ7WX0pXjIgXFwKICAgICY9JiAgXHN1bV97aT0xfV5uIChZX2ktXGhhdHtZfV9pKV4yK1xzdW1fe2k9MX1ebihcaGF0e1l9X2ktXGJhcntZfSleMiBcXAogICAgJj0mICBcdGV4dHtTU0UgfStcdGV4dHtTU1J9ICAKICBcZW5ke2VxbmFycmF5Kn0KCi0gIERlIHRvdGFsZSB2YXJpYWJpbGl0ZWl0IGluIGRlIGdlZ2V2ZW5zIChTU1RvdCkgd29yZHQgZ2VkZWVsdGVsaWprIHZlcmtsYWFyZCBkb29yIGhldCByZWdyZXNzaWV2ZXJiYW5kIChTU1IpLgotIFZhcmlhYmlsaXRlaXQgZGllIHdlIG5pZXQga3VubmVuIHZlcmtsYXJlbiBtZXQgaGV0IHJlZ3Jlc3NpZW1vZGVsIGlzIGRlIHJlc2lkdWVsZSB2YXJpYWJpbGl0ZWl0IChTU0UpLgoKCiMjICBEZXRlcm1pbmF0aWUtY2/Dq2ZmaWNpw6tudAoKJCQgUl4yID0gMS1cZnJhY3tcdGV4dHtTU0V9fXtcdGV4dHtTU1RvdH19PVxmcmFje1x0ZXh0e1NTUn19e1x0ZXh0e1NTVG90fX0uJCQKCi0gKkZyYWN0aWUgdmFuIGRlIHRvdGFsZSB2YXJpYWJpbGl0ZWl0IGluIGRlIHN0ZWVrcHJvZWYtdWl0a29tc3RlbiBkaWUgdmVya2xhYXJkIHdvcmR0IGRvb3IgaGV0IGdlc2NoYXR0ZSByZWdyZXNzaWV2ZXJiYW5kKi4KCi0gR3JvdGUgJFJeMiQgaW5kaWNhdGllIGRhdCBtb2RlbCBwb3RlbnRpZWVsIHRvdCBnb2VkZSBwcmVkaWN0aWVzIGthbiBsZWlkZW4gKGtsZWluZSBTU0UpCgotIFNsZWNodHMgaW4gYmVwZXJrdGUgbWF0ZSBpbmRpY2F0aWVmIHZvb3IgZGUgcC13YWFyZGUgdmFuIGRlIHRlc3QgJEhfMDpcYmV0YV8xPTAkIHZzICRIXzE6XGJldGFfMVxuZXEwJC4KCiAgICAtIHAtd2FhcmRlIHN0ZXJrIGJlw69udmxvZWQgZG9vciBTU0UgZW4gc3RlZWtwcm9lZmdyb290dGUgJG4kLCBtYWFyIG5pZXQgZG9vciBTU1RvdAogICAgLSBEZSBkZXRlcm1pbmF0aWVjb8OrZmZpY2nDq250ICRSXjIkIHdvcmR0IGRvb3IgU1NFIGVuIFNTVG90IGJlcGFhbGQsIG1hYXIgbmlldCBkb29yIGRlIHN0ZWVrcHJvZWZncm9vdHRlIG4uCgotIE1vZGVsIG1ldCBsYWdlICRSXjIkIGJsaWpmdCB3ZWwgbnV0dGlnIG9tIGFzc29jaWF0aWUgdGUgYmVzdHVkZXJlbiwgem9sYW5nIGhldCBkZSBhc3NvY2lhdGllIGNvcnJlY3QgbW9kZWxsZWVydCEKCiMjIyBCb3JzdGthbmtlciB2b29yYmVlbGQKCmBgYHtyfQpzdW1tYXJ5KGxtMikKYGBgCgojIyBGLVRlc3RlbiBpbiBoZXQgZW5rZWx2b3VkaWcgbGluZWFpciByZWdyZXNzaWVtb2RlbAoKLSBLd2FkcmF0ZW5zb21tZW4gIHppam4gYmFzaXMgdm9vciAkRiQtdGVzdHMKJCQgIEYgID0gXGZyYWN7XHRleHR7TVNSfX17XHRleHR7TVNFfX0kJAoKbWV0ICAkXHRleHR7TVNSfSA9IFxmcmFje1x0ZXh0e1NTUn19ezF9IFx0ZXh0eyBhbmQgfSBcdGV4dHtNU0V9ID0gXGZyYWN7XHRleHR7U1NFfX17bi0yfS4kCgotIE1TUiB3b3JkdCBkZSBnZW1pZGRlbGRlIGt3YWRyYXRlbnNvbSB2YW4gZGUgcmVncmVzc2llIGdlbm9lbWQuCgotIG5vZW1lcnMgMSBlbiAkbi0yJCB6aWpuIGRlIHZyaWpoZWlkc2dyYWRlbiB2YW4gU1NSIGVuIFNTRS4KCi0gb25kZXIgJEhfMDogXGJldGFfMT0wJCB2b2xndCBkZSB0ZXN0c3RhdGlzdGllawokJEhfMDpGID0gXGZyYWN7XHRleHR7TVNSfX17XHRleHR7TVNFfX0gXHNpbSBGX3sxLG4tMn0sJCQKLSBGLXRlc3QgaXMgYWx0aWpkIHR3ZWUtemlqZGlnISAkSF8xOlxiZXRhXzFcbmVxIDAkCiQkICBwID0gUF8wXGxlZnRbRlxnZXEgZlxyaWdodF09MS1GX0YoZjsxLG4tMikkJAoKCmBgYHtyfQpzdW1tYXJ5KGxtMikKYGBgCgoKYGBge3IsIGVjaG89RkFMU0V9CmdyaWQ8LXNlcSgwLDEwLC4xKQpwbG90KGdyaWQsZGYoZ3JpZCwxLDMwKSx0eXBlPSJsIix4bGFiPSJGIix5bGFiPSJEZW5zaXR5IixtYWluPSJGLWRpc3RyaWJ1dGllIDEgZGYgaW4gdGVsbGVuLCAzMCBpbiBub2VtZXIiLGNleC5tYWluPTEuNSxjZXguYXhpcz0xLjUsY2V4LmxhYj0xLjUpCmBgYAoKCiMjIEFub3ZhIFRhYmVsCgoKfCB8RGZ8U3VtIFNxfE1lYW4gU3F8RiB2YWx1ZXxQcig+Ril8CnwtLS18LS0tfC0tLXwtLS18LS0tfC0tLXwKfFJlZ3Jlc3NpZXx2cmlqaGVpZHNncmFkZW4gU1NSfFNTUnxNU1J8Zi1zdGF0aXN0aWVrfHAtd2FhcmRlfAp8RXJyb3J8dnJpamhlaWRzZ3JhZGVuIFNTRXxTU0V8TVNFfCB8IHwKCmBgYHtyfQphbm92YShsbTIpCmBgYAoKCiMgRHVtbXkgdmFyaWFiZWxlbgoKLSBMaW5lYWlyZSByZWdyZXNzaWVtb2RlbCB2b29yIGhldCB2ZXJnZWxpamtlbiB2YW4gdHdlZSBnZW1pZGRlbGRlbi4KLSBCb3JzdGthbmtlcjogdmVyc2NoaWwgaXMgaW4gZ2VtaWRkZWxkZSBsZWVmdGlqZCB2YW4gZGUgcGF0acOrbnRlbiBtZXQgb25hYW5nZXRhc3RlIGx5bWZla25vcGVuIGVuIHBhdGnDq250ZW4gd2FhcnZhbiBseW1mZWtub3BlbiB3ZXJkZW4gdmVyd2lqZGVyZC4KCi0gSGllcnZvb3IgZGVmaW5pw6tyZW4gd2UgZWVyc3QgZWVuICRkdW1teSQgdmFyaWFiZWxlCiQkeF9pID0gXGxlZnRceyBcYmVnaW57YXJyYXl9e2xsfQoxICYgXHRleHR7YWFuZ2V0YXN0ZSBseW1mZWtub3Blbn0gXFwKMCAmIFx0ZXh0e29uYWFuZ2V0YXN0ZSBseW1mZWtub3Blbn0gXGVuZHthcnJheX1ccmlnaHQuJCQKCi0gZ3JvZXAgbWV0ICR4X2k9MCQgd29yZHQgZGUgKipyZWZlcmVudGllZ3JvZXAqKiBnZW5vZW1kLgoKLSBIZXQgcmVncmVzc2llbW9kZWwgYmxpamZ0IG9uZ2V3aWp6aWdkLAokJFlfaSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfaSArXGVwc2lsb25faSQkCm1ldCAkXGVwc2lsb25faSBcdGV4dHsgaWlkIH0gTigwLFxzaWdtYV4yKSQKCgpHZXppZW4gJHhfaSQgc2xlY2h0cyB0d2VlIHdhYXJkZW4ga2FuIGFhbm5lbWVuLCBpcyBoZXQgZWVudm91ZGlnIG9tIGhldCByZWdyZXNzaWVtb2RlbCB2b29yIGJlaWRlIHdhYXJkZW4gdmFuICR4X2kkIGFmem9uZGVybGlqayB0ZSBiZWtpamtlbjoKJCQgXGJlZ2lue2FycmF5fXtsY2xsfQogICBZX2kgJj0mIFxiZXRhXzAgK1xlcHNpbG9uX2kgJlx0ZXh0e29uYWFuZ2V0YXN0ZSBseW1mZWtub3Blbn0gKHhfaT0wKSBcXAogICBZX2kgJj0mIFxiZXRhXzAgKyBcYmV0YV8xICtcZXBzaWxvbl9pICZcdGV4dHsgYWFuZ2V0YXN0ZSBseW1mZWtub3Blbn0gKHhfaT0xKSAuCiBcZW5ke2FycmF5fSQkCgpEdXMKIFxiZWdpbntlcW5hcnJheSp9CiAgIEVcbGVmdFtZX2lcbWlkIHhfaT0wXHJpZ2h0XSAmPSYgXGJldGFfMCBcXAogICBFXGxlZnRbWV9pXG1pZCB4X2k9MVxyaWdodF0gJj0mIFxiZXRhXzAgKyBcYmV0YV8xLApcZW5ke2VxbmFycmF5Kn0KCiB3YWFydWl0IGRpcmVjdCBkZSBpbnRlcnByZXRhdGllIHZhbiAkXGJldGFfMSQgdm9sZ3Q6CiQkICAgXGJldGFfMSA9IEVcbGVmdFtZX2lcbWlkIHhfaT0xXHJpZ2h0XS1FXGxlZnRbWV9pXG1pZCB4X2k9MFxyaWdodF0kJAoKJFxiZXRhXzEkIGlzIGR1cyBoZXQgZ2VtaWRkZWxkZSB2ZXJzY2hpbCBpbiBsZWVmdGlqZCB0dXNzZW4gcGF0acOrbnRlbiBtZXQgYWFuZ2V0YXN0ZSBseW1mZWtub3BlbiBlbiBwYXRpw6tudGVuIG1ldCBvbmFhbmdldGFzdGUgbHltZmVrbm9wZW4gKHJlZmVyZW50aWVncm9lcCkuCgpNZXQgZGUgbm90YXRpZSAkXG11XzA9IEVcbGVmdFtZX2lcbWlkIHhfaT0wXHJpZ2h0XSQgZW4gJFxtdV8xPSBFXGxlZnRbWV9pXG1pZCB4X2k9MVxyaWdodF0kIHdvcmR0IGRpdAokJFxiZXRhXzEgPSBcbXVfMS1cbXVfMC4kJAoKCkVyIGthbiBhYW5nZXRvb25kIHdvcmRlbiBkYXQKJCRcYmVnaW57YXJyYXl9e2NjbGx9CiBcaGF0XGJldGFfMAogICAmPSYgXGJhcntZfV8xJlx0ZXh0eyAoc3RlZWtwcm9lZmdlbWlkZGVsZGUgaW4gcmVmZXJlbnRpZWdyb2VwKX0gXFwKIFxoYXRcYmV0YV8xCiAgICY9JiBcYmFye1l9XzItXGJhcntZfV8xJlx0ZXh0eyhzY2hhdHRlciB2YW4gZWZmZWN0Z3Jvb3R0ZSl9IFxcCiBcdGV4dHtNU0V9CiAgICY9JiBTX3BeMiAuClxlbmR7YXJyYXl9JCQKCkRlIHRlc3RlbiB2b29yICRIXzA6XGJldGFfMT0wJCB2cy4gICRIXzE6XGJldGFfMVxuZXEwJCBrdW5uZW4gZ2VicnVpa3Qgd29yZGVuIHZvb3IgaGV0IHRlc3RlbiB2YW4gZGUgbnVsaHlwb3RoZXNlIHZhbiBkZSB0d28tc2FtcGxlICR0JC10ZXN0LCAkSF8wOlxtdV8xPVxtdV8yJCB0Lm8udi4gJEhfMTpcbXVfMVxuZXFcbXVfMiQuCgpgYGB7cn0KYnJjYSRub2RlIDwtIGFzLmZhY3RvcihicmNhJG5vZGUpCnQudGVzdChhZ2V+bm9kZSxicmNhLHZhci5lcXVhbD1UUlVFKQpgYGAKCmBgYHtyfQpsbTMgPC0gbG0oYWdlfm5vZGUsIGJyY2EpCnN1bW1hcnkobG0zKQpgYGAKCmBgYHtyfQpwbG90KGxtMykKYGBgCgoKCmBgYHtyfQpicmNhICU+JQogIGdncGxvdCgKICAgIGFlcygKICAgICAgeCA9IG5vZGUgJT4lCiAgICAgICAgYXMuZmFjdG9yLAogICAgICB5ID0gYWdlKQogICAgICApICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCgoxLiBTaW11bGF0ZSA5IGRhdGFzZXRzIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBhcyB0aGUgYnJjYSBkYXRhc2V0IGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggdGhlIHNhbWUgc3RhbmRhcmQgZGV2aWF0aW9uIGFzIGluIHRoZSBvcmlnaW5hbCBkYXRhLiBTdG9yZSB0aGUgZGF0YSBvZiBhbGwgc2ltdWxhdGlvbnMgaW4gYSBkYXRhIGZyYW1lCjIuIFBsb3QgdGhlIHNpbXVsYXRlZCBkYXRhIHVzaW5nIHRoZSBgZ2dwbG90YCBmdW5jdGlvbgozLiBBZGQgYSBib3hwbG90IGxheWVyCjQuIFVzZSBmYWNldF93cmFwIHRvIG1ha2UgYSBzZXBhcmF0ZSBwbG90IGZvciBzaW11bGF0ZWQgZGF0YXNldAo1LiBDaGFuZ2UgbGFiZWwgb2YgeS1heGlzCgpgYGB7ciBvdXQud2lkdGg9JzEwMCUnLCBmaWcuYXNwPS44LCBmaWcuYWxpZ249J2NlbnRlcid9CnNldC5zZWVkKDM1NCkKc2ltX2RmIDwtIGRhdGEuZnJhbWUoCiAgbm9kZSA9IHJlcChicmNhJG5vZGUsIDkpLAogIGlpZCA9IHJub3JtKDkgKiBucm93KGJyY2EpLCBzZCA9IHNpZ21hKGxtMykpLAogIHNpbSA9IHJlcCgxOjksIGVhY2ggPSAzMikKICApCnNpbV9kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBub2RlLCB5PWlpZCkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZmFjZXRfd3JhcCgufnNpbSkgKwogIHlsYWIocGFzdGUwKCJpaWQgTigwLCIscm91bmQoc2lnbWEobG0zKV4yLDIpLCIpIikpCmBgYAoKCiMjIE9ic2VydmF0aW9uZWxlIHN0dWR5CgotIFdlIGt1bm5lbiBlY2h0ZXIgbmlldCBiZXNsdWl0ZW4gZGF0IG91ZGVyZSBwZXJzb25lbiBlZW4gaG9nZXIgcmlzaWNvIGhlYmJlbiBvcCBhYW50YXN0aW5nIHZhbiBkZSBseW1mZWtub3BlbiB0ZW4gZ2V2b2xnZSB2YW4gaHVuIGxlZWZ0aWpkLgoKLSBNb2dlbGlqa3MgKipjb25mb3VuZGluZyoqOiBnZWVuIHJhbmRvbWlzYXRpZSAkXHJpZ2h0YXJyb3ckIGdyb2VwZW4gcGF0acOrbnRlbiBtZXQgYWFuZ2V0YXN0ZSBseW1mZWtub3BlbiBlbiBuaWV0LWFhbmdldGFzdGUgbHltZmVrbm9wZW4ga3VubmVuIG5vZyBpbiBhbmRlcmUga2FyYXRlcmlzdGlla2VuIHZhbiBlbGthYXIgdmVyc2NoaWxsZW4uCgotIEVua2VsIGJlc2x1aXRlbiBkYXQgZXIgZWVuIGFzc29jaWF0aWUgaXMgdHVzc2VuIGRlIGx5bWZla25vb3Agc3RhdHVzIGVuIGRlIGxlZWZ0aWpkLgoKLSBIZXQgaXMgZHVzIG5pZXQgbm9vZHpha2VsaWprZXJ3aWpzIGVlbiBjYXVzYWFsIHZlcmJhbmQhCgotLS0KCi0gSXMgb29rIHpvIHZvb3IgbGluZWFpciBtb2RlbCB2b29yIGRlICRcbG9nXzIkLVMxMDBBOC1leHByZXNzaWUuCgotIEFhbmdlemllbiB3ZSBkZSBFU1IxLWV4cHJlc3NpZSBuaWV0IGV4cGVyaW1lbnRlZWwgdmFzdCBoZWJiZW4ga3VubmVuIGxlZ2dlbiwga3VubmVuIHdlIG5pZXQgYmVzbHVpdGVuIGRhdCBlZW4gaG9nZXJlIEVTUjEtZXhwcmVzc2llIGRlIFMxMDBBOC1leHByZXNzaWUgZG9ldCB2ZXJsYWdlbi4KCi0gRW5rZWwgYmVzbHVpdGVuIGRhdCBlciBlZW4gbmVnYXRpZXZlIGFzc29jaWF0aWUgaXMuCgotIE9tIGltcGFjdCB2YW4gZ2VuIHRlIGJlc3R1ZGVyZW4gb3AgYW5kZXJlIGdlbmVuOiBrbm9ja291dCBtdXRhbnRlbiBnZW5lcmVuZW4gaW4gaGV0IGxhYm8KCgotLS0KCiMgW0hvbWVdKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pby9zYmMyMC8pIHstfQo=