Processing math: 100%
  • 1 Breast cancer dataset
    • 1.1 Associatie tussen ESR1 en S100A8 expressie
  • 2 Lineaire Regressie
    • 2.1 Model
    • 2.2 Lineaire regressie
    • 2.3 Gebruik
  • 3 Parameterschatting
    • 3.1 Schatters die SSE minimaliseren
      • 3.1.1 Borstkanker voorbeeld
  • 4 Statistische besluitvorming (statistische inferentie)
    • 4.1 Modelleer verdeling van Y?
    • 4.2 Hoge spreiding op X verbetert de precisie
      • 4.2.1 Borstkanker voorbeeld
    • 4.3 Hypothese test
      • 4.3.1 Brca dataset
  • 5 Nagaan van modelveronderstellingen
    • 5.1 Lineariteit
    • 5.2 Residuplots
    • 5.3 Homoscedasticiteit (gelijkheid van variantie)
    • 5.4 Normaliteit
  • 6 Afwijkingen van Modelveronderstellingen
    • 6.1 Borstkanker voorbeeld
      • 6.1.1 Interpretatie 1
      • 6.1.2 Interpretatie 2
      • 6.1.3 Interpratie 3
  • 7 Besluitvorming over gemiddelde uitkomst
    • 7.1 Terugtransformatie
  • 8 Predictie-intervallen
    • 8.1 NHANES voorbeeld
  • 9 Kwadratensommen en Anova-tabel
    • 9.1 Totale kwadratensom
    • 9.2 Kwadratensom van de regressie SSR
    • 9.3 Kwadratensom van de fouten
    • 9.4 Determinatie-coëfficiënt
      • 9.4.1 Borstkanker voorbeeld
    • 9.5 F-Testen in het enkelvoudig lineair regressiemodel
    • 9.6 Anova Tabel
  • 10 Dummy variabelen
    • 10.1 Observationele study

1 Breast cancer dataset

  • subset van studie https://doi.org/10.1093/jnci/djj052

  • 32 borstkanker patiënten met een estrogen recepter positieve tumor die tamoxifen chemotherapy behandeling ondergaan. Variabelen:

    • grade: histologische graad van tumor (graad 1 vs 3),
    • node: status van de lymfe knopen (0: niet aangetast, 1: aantasting en verwijdering van de lymfe knopen),
    • size: grootte van tumor in cm,
    • ESR1 en S100A8 gen expressie in tumor biopsy (via microarray technologie)
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()

1.1 Associatie tussen ESR1 en S100A8 expressie

  • ESR1 in ± 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.

  1. pipe dataset naar ggplot
  2. selecteer data ggplot(aes(x=ESR1,y=S100A8))
  3. voeg punten toe met geom_point()
  4. voeg een “smooth line” toe geom_smooth()
brcaSubset %>%
  ggplot(aes(x=ESR1,y=S100A8)) +
  geom_point() +
  geom_smooth()

2 Lineaire Regressie

  • Statistische methode om relatie tussen 2 reeksen observaties (Xi,Yi), bekomen voor onafhankelijke subjecten i=1,...,n, te beschrijven.

  • Gen expressie voorbeeld

    • Response Y : S100A8 expressie
    • Predictor X: ESR1 expressie
  1. pipe dataset naar ggplot
  2. selecteer data ggplot(aes(x=ESR1,y=S100A8))
  3. voeg punten toe met geom_point()
  4. voeg een “smooth line” toe geom_smooth()
  5. 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)

2.1 Model

  • Voor vaste X, heeft Y niet noodzakelijke dezelfde waarde

observation = signal + noise

Yi=g(Xi)+ϵi

  • We definiëren g(x) als het verwachte resultaat voor subjects met Xi=x

E[Yi|Xi=x]=g(x)

Daarom is ϵi gemiddeld 0 voor subjects met dezelfde Xi: E[ϵi|Xi]=0

2.2 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)=β0+β1x

onbekend intercept β0 en helling β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.

2.3 Gebruik

  • Predictie: wanneer Y ongekend is, maar X wel, kunnen we Y voorspellen op basis van X E(Y|X=x)=β0+β1x

  • Associatie: biologische relatie tussen variabele X en continue meting Y beschrijven.

  • Intercept: E(Y|X=0)=β0

  • Slope: E(Y|X=x+δ)E(Y|X=x)=β0+β1(x+δ)β0β1x=β1δ

β1: verschil in gemiddelde uitkomst voor subjecten die verschillen in één eenheid van de predictor X.

3 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)

  • Parameters β0 en β1 zijn ongekend

  • Parameters schatten op basis van beperkte steekproef

  • Best passende lijn

    • Punt op regressielijn voor een gegeven xi: (xi,β0+β1xi) zo dicht mogelijk bij (xi,yi)
    • Kies β0 en β1 zodat de som tussen voorspelde en waargenomen punten zo klein mogelijk wordt.

SSE=ni=1(yiβ0β1xi)2=ni=1e2i

  • Met ei de residuen: de verticale afstanden van de observaties tot de gefitte regressierechte

3.1 Schatters die SSE minimaliseren

^β1=ni=1(yiˉy)(xiˉx)ni=1(xiˉxi)2=cor(x,y)sysx

^β0=ˉyˆβ1ˉ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: E[Y|X=x]=ˆβ0+ˆβ1x

  • na te gaan hoeveel uitkomst gemiddeld verschilt tussen 2 groepen subjecten met een verschil van δ eenheden in de verklarende variabele:

E[Y|X=x+δ]E[Y|X=x]=ˆβ1δ

3.1.1 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.470.059x

  • 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.470.059×2000=89.94

  • Verwachte S100A8 expressieniveau voor patiënten met een ESR1 expressieniveau van 4000:
    208.470.059×4000=28.58

  • Let op voor extrapolatie! (Veronderstelling van lineariteit kan men enkel nagaan binnen het bereik van de data).

4 Statistische besluitvorming (statistische inferentie)

Om besluiten te kunnen trekken over lineaire regressiemodel E(Y|X)=β0+β1X

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: fY|X(y)

4.1 Modelleer verdeling van Y?

  1. Naast lineariteit hebben we nog aannames nodig!
  2. Onafhankelijkheid: waarnemingen (X1,Y1),...,(Xn,Yn) zijn gemaakt voor n onafhankelijke subjecten (Is vereist om de variantie te schatten)
  3. Homoscedasticiteit of gelijke varianties: waarnemingen variëren met gelijk gemiddelde rond de regressielijn.
    • Residuen ϵi hebben gelijke variantie voor elke Xi=x
    • var(Y|X=x)=σ2 voor elke X=x
    • σ wordt de residuele standaarddeviatie genoemd.
  4. Normaliteit: de residuen ϵi zijn normaal verdeeld

  • Uit 2, 3 en 4 volgt dat ϵi i.i.d.N(0,σ2).

  • Als we ook steunen op eerste veronderstelling van lineariteit: Yi|XiN(β0+β1Xi,σ2),

  • Verder kan men aantonen dat onder deze aannames σ2ˆβ0=ni=1X2ini=1(XiˉX)2×σ2n en σ2ˆβ1=σ2ni=1(XiˉX)2

  • en dat de parameterschatters eveneens normaal verdeeld zijn ˆβ0N(β0,σ2ˆβ0) en ˆβ1N(β1,σ2ˆβ1)

4.2 Hoge spreiding op X verbetert de precisie

σ2ˆβ1=σ2ni=1(XiˉX)2

  • Conditionele variantie (σ2) is niet gekend
  • Schatten d.m.v. gemiddelde van die kwadratische afwijkingen rond de regressierechte
  • mean squared error (MSE)

ˆσ2=MSE=ni=1(yiˆβ0ˆβ1×xi)2n2=ni=1e2in2. - Voor het bekomen van deze schatter steunen we op onafhankelijkheid (aanname 2) en homoscedasticiteit (aanname 3). - deel door n2

Na schatting van σ2 bekomen we volgende standaard errors:

SEˆβ0=ˆσˆβ0=ni=1X2ini=1(XiˉX)2×MSEn en SEˆβ1=ˆσˆβ1=MSEni=1(XiˉX)2

  • Opnieuw toetsen en betrouwbaarheidsintervallen o.b.v. T=ˆβkβkSE(ˆβk) 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 Centrale Limietstelling

4.2.1 Borstkanker voorbeeld

  • Negatieve associatie tussen S100A8 en ESR1 gen expressie.

  • Veralgemeen effect in steekproef naar populatie met behulp van het betrouwbaarheidsinterval op de helling: [ˆβ1tn2,α/2SEˆβ1,ˆβ1+tn2,α/2SEˆβ1].

confint(lm1)
                   2.5 %       97.5 %
(Intercept) 149.84639096 267.09649989
ESR1         -0.08412397  -0.03440378
  • Negatief verband is significant op het 5% significantieniveau.

4.3 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: H0:β1=0

  • Onder alternatieve hypothese is er een associatie tussen beide genen: H1:β10

  • Test statistiek T=ˆβ10SE(ˆβk)

  • Onder H0 volgt de statistiek een t-verdeling met n-2 vrijheidsgraden.

4.3.1 Brca dataset

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
  • 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.

5 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

5.1 Lineariteit

brcaSubset %>%
  ggplot(aes(x = ESR1, y = S100A8)) +
  geom_point() +
  geom_smooth(se = FALSE, col = "grey") +
  geom_smooth(method = "lm", se = FALSE)

5.2 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 ˆβ0+ˆβ1x op de X-as

  • de residuen op de Y-as ei=yiˆg(xi)=yiˆβ0ˆβ1×xi,

plot(lm1)

5.3 Homoscedasticiteit (gelijkheid van variantie)

  • Residuen en kwadratische residu’s dragen informatie over residuele variabiliteit.

  • Associatie met de verklarende variabelen indicatie van heteroscedasticiteit.

  • Scatterplot van of ei versus xi of predicties ˆβ0+ˆβ1xi.

  • Scatterplot van gestandardiseerd residu versus xi of predicties.

5.4 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. Yi|XiN(β0+β1Xi,σ2)

  • QQ-plot van response Y is heel misleidend.

  • QQ-plot van de residuen ei

plot(lm1, which=2)

6 Afwijkingen van Modelveronderstellingen

  • Transformatie van onafhankelijke veranderlijke wijzigt de verdeling van Y bij gegeven X niet:

    • helpt niet om normaliteit of homoscedasticiteit te bekomen
    • helpt wel om lineariteit te bekomen wanneer er normaliteit en homoscedasticiteit is
    • Vaak ook hogere orde termen: X2, X3, … Yi=β0+β1Xi+β2X2i+...+ϵi
  • Transformatie van response Y kan helpen om normaliteit en homoscedasticiteit te bekomen.

    • (Y), log(Y), 1/Y, …

6.1 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 log2 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)

summary(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
confint(lm2)
                  2.5 %    97.5 %
(Intercept)   20.128645 26.674023
ESR1 %>% log2 -1.921047 -1.308185

6.1.1 Interpretatie 1

Een patiënt met een ESR1 expressie die 1 eenheid op de log2 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!

log2ˆμ1=23.4011.615×logESR1, log2ˆμ2=23.4011.615×logESR2 log2ˆμ2log2ˆμ1=1.615(log2ESR2log2ESR1)=1.615×1=1.615

6.1.2 Interpretatie 2

Model op log-schaal: bij terugtransformatie verkrijgen we het geometrische gemiddelde

ni=1logxin=logx1++logxnn(1)=log(x1××xn)n=log(ni=1xi)n(2)=log(nni=1xi)

  • Populatiegemiddelden μ dus geschat a.d.h.v. geometrisch gemiddelden.
  • Logaritmische transformatie is een monotoon: we kunnen betrouwbaarheidsintervallen berekend op log-schaal terugtransformeren!
2^lm2$coef[2]
ESR1 %>% log2 
    0.3265519 
2^-lm2$coef[2]
ESR1 %>% log2 
       3.0623 
2^-confint(lm2)[2,]
   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]).

log2ˆμ1=23.4011.615×logESR1, log2ˆμ2=23.4011.615×logESR2 log2ˆμ2log2ˆμ1=1.615(log2ESR2log2ESR1) log2[ˆμ2ˆμ1]=1.615log2[ESR2ESR1] ˆμ2ˆμ1=[ESR2ESR1]1.615=21.615=0.326 or ˆμ1ˆμ2=21.615=3.06

6.1.3 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])%.

log2ˆμ1=23.4011.615×logESR1, log2ˆμ2=23.4011.615×logESR2 log2ˆμ2^log2μ1=1.615(log2ESR2log2ESR1) log2[ˆμ2ˆμ1]=1.615log2[ESR2ESR1] ˆμ2ˆμ1=[ESR2ESR1]1.615=1.011.615=0.9841.6%

Dit geldt voor lage tot matige waarden van β1: 10<β1<101.01β11β1100.

7 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. ˆg(x)=ˆβ0+ˆβ1x

  • ˆg(x) een schatter van het conditionele gemiddelde E[Y|X=x]

  • Parameterschatters Normale verdeeld en onvertekend schatter ˆg(x) ook Normaal verdeeld en onvertekend.

SEˆg(x)=MSE{1n+(xˉX)2ni=1(XiˉX)2}.

T=ˆg(x)g(x)SEˆg(x)tn2

  • 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 lmfunctie 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")

7.1 Terugtransformatie

newdata<-data.frame(cbind(grid,2^g))
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")

8 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)+ϵ met ϵN(0,σ2) en ϵ onafhankelijk van de waarnemingen in de steekproef Y1,,Yn.

  • We voorspellen een nieuwe log-S100A8 voor een patiënt met een gekend log2-ESR1-expressieniveau x ˆy(x)=ˆβ0+ˆβ1×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 onzekerheid over de geschatte modelparameters ˆβ0 en ˆβ1.
    • Onzekerheid over nieuwe waarneming onzekerheid over geschat gemiddelde en extra onzekerheid omdat de nieuwe waarneming zal afwijken rond het gemiddelde!

SEˆY(x)=ˆσ2+ˆσ2ˆg(x)=MSE{1+1n+(xˉX)2ni=1(XiˉX)2}.

ˆY(x)YSEˆY(x)tn2

  • 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")

8.1 NHANES voorbeeld

  • Vergelijk referentie-interval voor cholesterolgehalte met predictie interval.

  • Referentie-interval

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
  • Predictie interval
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.

  • Referentie interval
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.

  • Predictie interval
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.

9 Kwadratensommen en Anova-tabel

9.1 Totale kwadratensom

SSTot=ni=1(Yiˉ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|X=x).

  • We weten dat MSE een goede schatting is van de variantie van de conditionele verdeling van Y|X=x.

9.2 Kwadratensom van de regressie SSR

SSR=ni=1(ˆYiˉY)2=ni=1(ˆg(xi)ˉ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 ˆg(x)=ˆβ0+ˆβ1x
    • Geschatte model zonder predictor (enkel intercept): g(x)=β0 β0 zal gelijk zijn aan ˉY.
  • SSR meet de grootte van het effect van de predictor

9.3 Kwadratensom van de fouten

SSE=ni=1(YiˆYi)2=ni=1{Yiˆg(xi)}2.

  • Hoe kleiner de SSE, hoe beter het model fit.

  • Kleinste kwadraten techniek!


We kunnen aantonen dat SST kan worden ontbonden in SSTot=ni=1(YiˉY)2=ni=1(YiˆYi+ˆYiˉY)2=ni=1(YiˆYi)2+ni=1(ˆYiˉY)2=SSE +SSR

  • 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).

9.4 Determinatie-coëfficiënt

R2=1SSESSTot=SSRSSTot.

  • Fractie van de totale variabiliteit in de steekproef-uitkomsten die verklaard wordt door het geschatte regressieverband.

  • Grote R2 indicatie dat model potentieel tot goede predicties kan leiden (kleine SSE)

  • Slechts in beperkte mate indicatief voor de p-waarde van de test H0:β1=0 vs H1:β10.

    • p-waarde sterk beïnvloed door SSE en steekproefgrootte n, maar niet door SSTot
    • De determinatiecoëfficiënt R2 wordt door SSE en SSTot bepaald, maar niet door de steekproefgrootte n.
  • Model met lage R2 blijft wel nuttig om associatie te bestuderen, zolang het de associatie correct modelleert!

9.4.1 Borstkanker voorbeeld

summary(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

9.5 F-Testen in het enkelvoudig lineair regressiemodel

  • Kwadratensommen zijn basis voor F-tests F=MSRMSE

met MSR=SSR1 and MSE=SSEn2.

  • MSR wordt de gemiddelde kwadratensom van de regressie genoemd.

  • noemers 1 en n2 zijn de vrijheidsgraden van SSR en SSE.

  • onder H0:β1=0 volgt de teststatistiek H0:F=MSRMSEF1,n2,

  • F-test is altijd twee-zijdig! H1:β10 p=P0[Ff]=1FF(f;1,n2)

summary(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

9.6 Anova Tabel

Df Sum Sq Mean Sq F value Pr(>F)
Regressie vrijheidsgraden SSR SSR MSR f-statistiek p-waarde
Error vrijheidsgraden SSE SSE MSE
anova(lm2)
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

10 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 xi={1aangetaste lymfeknopen0onaangetaste lymfeknopen

  • groep met xi=0 wordt de referentiegroep genoemd.

  • Het regressiemodel blijft ongewijzigd, Yi=β0+β1xi+ϵi met ϵi iid N(0,σ2)

Gezien xi slechts twee waarden kan aannemen, is het eenvoudig om het regressiemodel voor beide waarden van xi afzonderlijk te bekijken: Yi=β0+ϵionaangetaste lymfeknopen(xi=0)Yi=β0+β1+ϵi aangetaste lymfeknopen(xi=1).

Dus E[Yixi=0]=β0E[Yixi=1]=β0+β1,

waaruit direct de interpretatie van β1 volgt: β1=E[Yixi=1]E[Yixi=0]

β1 is dus het gemiddelde verschil in leeftijd tussen patiënten met aangetaste lymfeknopen en patiënten met onaangetaste lymfeknopen (referentiegroep).

Met de notatie μ0=E[Yixi=0] en μ1=E[Yixi=1] wordt dit β1=μ1μ0.

Er kan aangetoond worden dat ˆβ0=ˉY1 (steekproefgemiddelde in referentiegroep)ˆβ1=ˉY2ˉY1(schatter van effectgrootte)MSE=S2p.

De testen voor H0:β1=0 vs. H1:β10 kunnen gebruikt worden voor het testen van de nulhypothese van de two-sample t-test, H0:μ1=μ2 t.o.v. H1:μ1μ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
plot(lm3)

brca %>%
  ggplot(
    aes(
      x = node %>%
        as.factor,
      y = age)
      ) +
  geom_boxplot()

  1. 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
  2. Plot the simulated data using the ggplot function
  3. Add a boxplot layer
  4. Use facet_wrap to make a separate plot for simulated dataset
  5. 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),")"))

10.1 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 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 log2-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=