Creative Commons License

1 Longvolume en rokerstatus

  • Onderzoekers willen de impact van roken onderzoeken op longcapaciteit bij schoolgaande kinderen en adolescenten.
  • Ze meten hierbij de longcapaciteit (in l) bij rokende en niet-rokende leerlingen (forced expiratory volume).

1.1 Importeer data

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
fev <- read_tsv("https://raw.githubusercontent.com/GTPB/PSLS20/master/data/fev.txt")
## Rows: 606 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (1): gender
## dbl (4): age, fev, height, smoking
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(fev)

1.2 Data manipulatie

  1. gender and smoking kunnen beter worden omgezet naar factor variabelen
  2. De height variabele is in inches dus deze zetten we om in cm.
fev <- fev %>%
  mutate(gender = as.factor(gender)) %>%
  mutate(smoking = as.factor(smoking)) %>%
  mutate(height_cm = height*2.54)
head(fev)

1.3 Enkele Concepten

  • In een experimentele studie gaat een onderzoeker de behandeling at random toewijzen aan de experimentele eenheden en observeert hij/zij het effect van de behandeling bij de experimentele eenheden door het meten van één of meerdere reponse variabelen.

  • Een experimentele eenheid (experimental unit) is de eenheid (subject, plant, pot, proefdier) dat at random aan de behandeling wordt toegewezen. Experimentele eenheden in fev example?

  • Een response variabele is a karakteristiek van de experimentele eenheid die wordt gemeten en geanalyseerd om het effect van de behandeling na te gaan. Response variable in fev example?

  • Een observationele eenheid is de eenheid waarop de response variabele wordt gemeten. Veelal is er een één-op-één overeenkomst tussen experimentele en observationele eenheden. Maar dat is niet altijd zo: b.v. pseudoreplicatie zoals wanneer men technische herhalingen heeft voor elke gemeten karakteristiek bij een subject. Observationele eenheid in fev voorbeeld?

  • Een factor is een verklarende variabele die twee of meer niveaus aan kan nemen. Voorbeelden in de fev studie?


  • Populatie?

1.4 Data Exploratie

1.4.1 Summary statistics

fevSum <- fev %>%
  group_by(smoking) %>%
  summarize_at("fev", 
               list(mean = mean,
                    sd = sd,
                    n = length)
                ) %>%
  mutate(se = sd/sqrt(n))
fevSum
  • Let op dat deze code niet werkt als er ontbrekende data zijn.
  • In dat geval gebruik je onderstaande code
fevSum <- fev %>%
  group_by(smoking) %>%
    summarize_at("fev",list(mean=~mean(.,na.rm=TRUE),
                    sd=~sd(.,na.rm=TRUE),
                    n=function(x) x%>%is.na%>%`!`%>%sum)) %>%
  mutate(se=sd/sqrt(n))
fevSum

1.5 Visualisatie

fev %>%
  ggplot(aes(x = smoking, y = fev)) +
  geom_boxplot(outlier.shape = NA) +
  geom_jitter(alpha = .2)

  • Effect grootte?

We kunnen de plots ook als objecten opslaan voor later gebruik!

p1 <- fev %>%
  ggplot(aes(x=smoking,y=fev)) +
  geom_boxplot(outlier.shape=NA) +
  geom_jitter(alpha=.2)

p2 <- fev %>%
  ggplot(aes(sample=fev)) +
  geom_qq() +
  geom_qq_line() +
  facet_wrap(~smoking)

p1

p2

1.6 Onderzoeksvraag

De onderzoekers wensen het effect te kennen van roken op de long capaciteit van jongeren.

1.7 Hoe modelleren we de data?

  • \(x_i\): dummy variabele voor rokerstatus voor subject \(i\): \(x_{i,1}=\begin{cases} 0& \text{niet-roker}\\ 1& \text{roker} \end{cases}\).

\[Y_i\vert x_i\sim N(\mu_i,\sigma^2)\]

via lineair model?

\[Y_i = \beta_0 + \beta_1 x_i + \epsilon_i \text{ met } \epsilon_i\sim N(0,\sigma^2)\]

Vertaal de onderzoeksvraag naar de model parameters?

library(ExploreModelMatrix)

lm1 <- lm(fev~smoking, fev)
lm1 
## 
## Call:
## lm(formula = fev ~ smoking, data = fev)
## 
## Coefficients:
## (Intercept)     smoking1  
##      2.6346       0.6054
explMx <- VisualizeDesign(fev,designFormula = ~smoking)
explMx$plotlist
## [[1]]

  • Vertaal de onderzoeksvraag naar een parameter in het model.

  • Effectgrootte:

\[\mu_1-\mu_0 = \beta_0+\beta_1 - \beta_0 = \beta_1\]

1.7.1 Schatten van de effectgrootte en de standard error

summary(lm1)
## 
## Call:
## lm(formula = fev ~ smoking, data = fev)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.5460 -0.5754 -0.1036  0.4474  3.1584 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  2.63463    0.03469  75.943  < 2e-16 ***
## smoking1     0.60541    0.10935   5.537  4.6e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8099 on 604 degrees of freedom
## Multiple R-squared:  0.0483, Adjusted R-squared:  0.04672 
## F-statistic: 30.65 on 1 and 604 DF,  p-value: 4.603e-08

1.8 Statistische Inferentie?

1.8.1 Nul en alternatieve hypothese?

We wensen de alternatieve hypothese \(H_1\) aan te tonen: er is een effect van roken op de longcapaciteit van jongeren. We vertalen dit naar ons model:

  • \(H_1\): Gemiddeld is de longcapaciteit van niet-rokende en rokende jongeren verschillend \[\beta \neq 0\]

We kunnen op basis van data echter een hypothese niet bewijzen. Daarom zullen we het omgekeerde falsifiëren:

  • \(H_0\): Gemiddeld is de longcapaciteit van niet-rokende en rokende jongeren gelijk \[\beta = 0\]

  • Hoe falsifieren we \(H_0\)?


summary(lm1)
## 
## Call:
## lm(formula = fev ~ smoking, data = fev)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.5460 -0.5754 -0.1036  0.4474  3.1584 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  2.63463    0.03469  75.943  < 2e-16 ***
## smoking1     0.60541    0.10935   5.537  4.6e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8099 on 604 degrees of freedom
## Multiple R-squared:  0.0483, Adjusted R-squared:  0.04672 
## F-statistic: 30.65 on 1 and 604 DF,  p-value: 4.603e-08
  • Hoe waarschijnlijk is het om een effectgrootte (test-statistiek) te vinden die meer extreem is dan wat we observeerden in een random steekproef wanneer de nulhypothese waar is.

  • Als we aannames kunnen doen over de verdelingen van de gegevens, kennen we de verdeling van de test statistiek en kunnen we die kans berekenen: p-waarde.

  • Als de p-waarde lager is dan het nominale significantie-niveau \(\alpha\) verwerpen we de null hypothese.

We controleren de kans op het maken van een vals positieve conclusie op het \(\alpha\)-niveau (type I fout)

  • De p-value is enkel correct als de onderliggende aannames geldig zijn.
p1

p2

  • Biologische metingen zijn dikwijls niet normaal verdeeld.
  • Dikwijls zijn de gegevens na log-transformatie meer normaal verdeeld.
  • Verschillen op log schaal:

\[ \log_2(S) - \log_2(NS) = \log_2 \frac{S}{NS} = \log_2 FC_{\frac{S}{NS}} \]


1.9 Log transformation

p1 <- fev %>%
  ggplot(aes(x=smoking,y=log2(fev))) +
  geom_boxplot(outlier.shape=NA) +
  geom_jitter(alpha=.2)

p2 <- fev %>%
  ggplot(aes(sample=log2(fev))) +
  geom_qq() +
  geom_qq_line() +
  facet_wrap(~smoking)
p1

p2

lm1 <- lm(log2(fev)~smoking,fev)
summary(lm1)
## 
## Call:
## lm(formula = log2(fev) ~ smoking, data = fev)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.11108 -0.28248  0.00693  0.28172  1.20290 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.33141    0.01833  72.649  < 2e-16 ***
## smoking1     0.32592    0.05776   5.642 2.58e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4278 on 604 degrees of freedom
## Multiple R-squared:  0.05007,    Adjusted R-squared:  0.0485 
## F-statistic: 31.84 on 1 and 604 DF,  p-value: 2.582e-08
2^(lm1$coefficients)
## (Intercept)    smoking1 
##    2.516487    1.253463
2^(confint(lm1))
##                2.5 %   97.5 %
## (Intercept) 2.454484 2.580058
## smoking1    1.158676 1.356004
## verschil in variantie --> Welch t-test

fevttest <- t.test(log2(fev)~smoking,fev)
2^(fevttest$estimate[2]-fevttest$estimate[1])
## mean in group 1 
##        1.253463
2^-fevttest$conf.int %>% sort
## [1] 1.174285 1.337979

1.10 Conclusie

  • Er is een extreem significant verschil in gemiddelde longinhoud tussen rokende en niet-rokende jongeren (\(p << 0.001\)).

  • Gemiddelde is de longinhoud 1.25 keer groter bij rokerende dan bij niet-rokende jongeren (95% BI [1.17, 1.34] .


  • Probleem!
  • Observationele studie
  • Confounding!

  • Leeftijd (L) is heeft een invloed op het rookgedrag (R) en op de longinhoud (V)!
  • Als we leeftijd niet meenemen in de analyse dan kan het zijn dat de associatie tussen rookgedrag en de longinhoud vertekend is omdat rokers en niet-rokers niet alleen in rookgedrag maar ook in leeftijd verschillen!
  • Confounding is een probleem die typisch optreedt in observationele studies!
library(GGally)
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
fev %>% 
  mutate(lfev=log2(fev)) %>% 
  dplyr::select(smoking,gender,age,height_cm,lfev) %>% 
  ggpairs()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Een betere data exploratie:

fev %>%
  ggplot(aes(x=as.factor(age),y=fev,fill=smoking)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point(size = 0.1, position = position_jitterdodge()) +
  theme_bw() +
  scale_fill_manual(values=c("dimgrey","firebrick")) +
  ggtitle("Boxplot of FEV versus smoking, stratified on age and gender") +
  ylab("fev (l)") +
  xlab("age (years)") + 
  facet_grid(rows = vars(gender))


  • Hoe zouden we de associatie tussen leeftijd en fev kunnen modelleren?
  • We doen dit eerst enkel voor niet-rokende meisjes. Want er is ook een associatie tussen fev en geslacht.
lm2 <- fev %>% 
  filter(gender=="f" & smoking == 0) %>% 
  lm(log2(fev)~age,.)

summary(lm2)
## 
## Call:
## lm(formula = log2(fev) ~ age, data = .)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.75023 -0.16864  0.00122  0.18291  0.51611 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.150534   0.069503   2.166   0.0313 *  
## age         0.114369   0.007011  16.313   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.252 on 253 degrees of freedom
## Multiple R-squared:  0.5126, Adjusted R-squared:  0.5107 
## F-statistic: 266.1 on 1 and 253 DF,  p-value: < 2.2e-16
plot(lm2)

fev %>% 
  filter(gender=="f" & smoking == 0) %>% 
  ggplot(aes(x=age,y=log2(fev))) + 
  geom_point() +
  geom_smooth(method = "lm", formula = y ~ x )

  • Residu plot heeft probleem aan!
lm3 <- fev %>% 
  filter(gender=="f" & smoking == 0) %>% 
  lm(log2(fev)~age + I(age ^2),.)

summary(lm3)
## 
## Call:
## lm(formula = log2(fev) ~ age + I(age^2), data = .)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -0.8147 -0.1650  0.0153  0.1645  0.5122 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -1.04905    0.23698  -4.427 1.43e-05 ***
## age          0.36096    0.04725   7.639 4.57e-13 ***
## I(age^2)    -0.01202    0.00228  -5.271 2.91e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2396 on 252 degrees of freedom
## Multiple R-squared:  0.561,  Adjusted R-squared:  0.5576 
## F-statistic:   161 on 2 and 252 DF,  p-value: < 2.2e-16
plot(lm3)

fev %>% 
  ggplot(aes(x=age,y=log2(fev))) + 
  geom_point() +
  geom_smooth(method = "lm", formula = y ~ x + I(x^2)) +
  facet_wrap(gender ~ smoking)

2 Algemeen Linear Model

Hoe kunnen we meerdere factoren en continue predictoren combineren in een lineair model?

\[ y_i= \beta_0 + \beta_S x_{i,S} + \beta_A x_{i,A} +\epsilon_i, \]

emt

  • \(x_{i,S}\) een dummy variabele voor rokerstatus: \(x_{i,1}=\begin{cases} 0& \text{niet-roker}\\ 1& \text{roker} \end{cases}\)
  • \(x_{i,A}\) is de leeftijd.

2.1 Implementatie in R

  • We doen dit nu enkel voor de meisjes omdat er ook effecten van geslacht zijn. Later leren we hoe we daarmee om kunnen gaan.
lmS_age <- fev %>% 
  filter(gender=="f") %>%
  lm(log2(fev)~smoking + age,data=.)
summary(lmS_age)
## 
## Call:
## lm(formula = log2(fev) ~ smoking + age, data = .)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.74590 -0.16639  0.01665  0.19547  0.52963 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  0.271785   0.069410   3.916 0.000113 ***
## smoking1    -0.048477   0.051598  -0.940 0.348257    
## age          0.101810   0.006984  14.577  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2626 on 289 degrees of freedom
## Multiple R-squared:  0.4648, Adjusted R-squared:  0.4611 
## F-statistic: 125.5 on 2 and 289 DF,  p-value: < 2.2e-16

De parameter smoking1 krijgt nu de interpretatie van de gemiddelde log2 fold change in longinhoud tussen rokers en niet rokers na correctie voor de leeftijd:

  • Roker: \[E[y|\text{roker}, X_A=x] = \beta_0 + \beta_s + \beta_A x\]
  • Niet-roker: \[E[y|\text{niet-roker}, X_A=x] = \beta_0 + \beta_A x\]
  • verschil tussen roker en niet-roker van dezelfde leeftijd (\(X_A=x\)): \[E[y|\text{roker}, X_A=x]- E[y|\text{niet-roker}, X_A=x]=\beta_0 + \beta_s + \beta_A x- \beta_0 + \beta_A x = \beta_s\]

We weten uit de data exploratie echter dat dit nog niet het correcte model is:

  • Niet-lineair effect van leeftijd.
  • Associatie tussen FEV en rokersstatus verandert i.f.v. leeftijd, waarschijnlijk door associatie met hoelang al wordt gerookt. We zullen later zien dat dit een interactie is.
  • We wensen het model ook uit te breiden voor Gender.
  • We zien dat het niet evident is om conclusie te trekken op basis van observationele studies omwille van mogelijkse confounding
  • In de volgende lessen gaan we dieper in op het algemene lineair model waarin we meerdere factoren en continue variabelen kunnen combineren als predictoren voor het modelleren van het gemiddelde van de continue response variabele.
LS0tCnRpdGxlOiAiSW50cm9kdWN0aWUiCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGxhdGV4X2VuZ2luZTogeGVsYXRleAotLS0KCgo8YSByZWw9ImxpY2Vuc2UiIGhyZWY9Imh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1zYS80LjAiPjxpbWcgYWx0PSJDcmVhdGl2ZSBDb21tb25zIExpY2Vuc2UiIHN0eWxlPSJib3JkZXItd2lkdGg6MCIgc3JjPSJodHRwczovL2kuY3JlYXRpdmVjb21tb25zLm9yZy9sL2J5LW5jLXNhLzQuMC84OHgzMS5wbmciIC8+PC9hPgoKIyBMb25ndm9sdW1lIGVuIHJva2Vyc3RhdHVzCgotIE9uZGVyem9la2VycyB3aWxsZW4gZGUgaW1wYWN0IHZhbiByb2tlbiBvbmRlcnpvZWtlbiBvcCBsb25nY2FwYWNpdGVpdCBiaWogc2Nob29sZ2FhbmRlIGtpbmRlcmVuIGVuIGFkb2xlc2NlbnRlbi4gCi0gWmUgbWV0ZW4gaGllcmJpaiBkZSBsb25nY2FwYWNpdGVpdCAoaW4gbCkgYmlqIHJva2VuZGUgZW4gbmlldC1yb2tlbmRlIGxlZXJsaW5nZW4gKGZvcmNlZCBleHBpcmF0b3J5IHZvbHVtZSkuCgpgYGB7ciBlY2hvPUZBTFNFfQoja25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvYmlvc3RhdGlzdGljczIxL21hc3Rlci9maWdzL2Zldk1lYXN1cmVtZW50LmpwZyIpCmBgYAoKIVtdKC4vZmlncy9mZXZNZWFzdXJlbWVudC5qcGcpCgojIyBJbXBvcnRlZXIgZGF0YQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpmZXYgPC0gcmVhZF90c3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9HVFBCL1BTTFMyMC9tYXN0ZXIvZGF0YS9mZXYudHh0IikKYGBgCgoKYGBge3J9CmhlYWQoZmV2KQpgYGAKCiMjIERhdGEgbWFuaXB1bGF0aWUKCjEuICBgZ2VuZGVyYCBhbmQgYHNtb2tpbmdgIGt1bm5lbiBiZXRlciB3b3JkZW4gb21nZXpldCBuYWFyIGZhY3RvciB2YXJpYWJlbGVuCjIuIERlIGBoZWlnaHRgIHZhcmlhYmVsZSBpcyBpbiBpbmNoZXMgZHVzIGRlemUgemV0dGVuIHdlIG9tIGluIGNtLiAKCmBgYHtyfQpmZXYgPC0gZmV2ICU+JQogIG11dGF0ZShnZW5kZXIgPSBhcy5mYWN0b3IoZ2VuZGVyKSkgJT4lCiAgbXV0YXRlKHNtb2tpbmcgPSBhcy5mYWN0b3Ioc21va2luZykpICU+JQogIG11dGF0ZShoZWlnaHRfY20gPSBoZWlnaHQqMi41NCkKaGVhZChmZXYpCmBgYAoKIyMgRW5rZWxlIENvbmNlcHRlbgoKLSBJbiBlZW4gKipleHBlcmltZW50ZWxlIHN0dWRpZSoqIGdhYXQgZWVuIG9uZGVyem9la2VyIGRlICoqYmVoYW5kZWxpbmcqKiAqKmF0IHJhbmRvbSoqIHRvZXdpanplbiBhYW4gZGUgKipleHBlcmltZW50ZWxlIGVlbmhlZGVuKiogZW4gb2JzZXJ2ZWVydCBoaWovemlqIGhldCBlZmZlY3QgdmFuIGRlICoqYmVoYW5kZWxpbmcqKiBiaWogZGUgZXhwZXJpbWVudGVsZSBlZW5oZWRlbiBkb29yIGhldCBtZXRlbiB2YW4gw6nDqW4gb2YgbWVlcmRlcmUgKipyZXBvbnNlIHZhcmlhYmVsZW4qKi4gCgotIEVlbiAgKipleHBlcmltZW50ZWxlIGVlbmhlaWQqKiAoZXhwZXJpbWVudGFsIHVuaXQpIGlzIGRlIGVlbmhlaWQgKHN1YmplY3QsIHBsYW50LCBwb3QsIHByb2VmZGllcikgZGF0IGF0IHJhbmRvbSBhYW4gZGUgYmVoYW5kZWxpbmcgd29yZHQgdG9lZ2V3ZXplbi4gCioqRXhwZXJpbWVudGVsZSBlZW5oZWRlbiBpbiBmZXYgZXhhbXBsZT8qKiAKCi0gRWVuICoqcmVzcG9uc2UgdmFyaWFiZWxlKiogaXMgYSBrYXJha3RlcmlzdGllayB2YW4gZGUgZXhwZXJpbWVudGVsZSBlZW5oZWlkIGRpZSB3b3JkdCBnZW1ldGVuIGVuIGdlYW5hbHlzZWVyZCBvbSBoZXQgZWZmZWN0IHZhbiBkZSBiZWhhbmRlbGluZyBuYSB0ZSBnYWFuLiAgKipSZXNwb25zZSB2YXJpYWJsZSBpbiBmZXYgZXhhbXBsZT8qKgoKLSBFZW4gKipvYnNlcnZhdGlvbmVsZSBlZW5oZWlkKiogaXMgZGUgZWVuaGVpZCB3YWFyb3AgZGUgcmVzcG9uc2UgdmFyaWFiZWxlIHdvcmR0IGdlbWV0ZW4uIFZlZWxhbCBpcyBlciBlZW4gw6nDqW4tb3Atw6nDqW4gb3ZlcmVlbmtvbXN0IHR1c3NlbiBleHBlcmltZW50ZWxlIGVuIG9ic2VydmF0aW9uZWxlIGVlbmhlZGVuLiBNYWFyIGRhdCBpcyBuaWV0IGFsdGlqZCB6bzogYi52LiBwc2V1ZG9yZXBsaWNhdGllIHpvYWxzIHdhbm5lZXIgbWVuIHRlY2huaXNjaGUgaGVyaGFsaW5nZW4gaGVlZnQgdm9vciBlbGtlIGdlbWV0ZW4ga2FyYWt0ZXJpc3RpZWsgYmlqIGVlbiBzdWJqZWN0LiAqKk9ic2VydmF0aW9uZWxlIGVlbmhlaWQgaW4gZmV2IHZvb3JiZWVsZD8qKiAKCi0gRWVuICoqZmFjdG9yKiogaXMgZWVuIHZlcmtsYXJlbmRlIHZhcmlhYmVsZSBkaWUgdHdlZSBvZiBtZWVyIG5pdmVhdXMgYWFuIGthbiBuZW1lbi4gVm9vcmJlZWxkZW4gaW4gZGUgZmV2IHN0dWRpZT8gIAoKLS0tCgpgYGB7ciBwb3AyU2FtcDJQb3AsZmlnLmFzcD0uOCwgZmlnLmFsaWduPSdjZW50ZXInLGVjaG89RkFMU0V9CmlmICgicGkiJWluJWxzKCkpIHJtKCJwaSIpCmtvcHZvZXRlcjwtZnVuY3Rpb24oeCx5LGFuZ2xlPTAsbD0uMixjZXguZG90PS41LHBjaD0xOSxjb2w9ImJsYWNrIikKewphbmdsZT1hbmdsZS8xODAqcGkKcG9pbnRzKHgseSxjZXg9Y2V4LmRvdCxwY2g9cGNoLGNvbD1jb2wpCmxpbmVzKGMoeCx4K2wqY29zKC1waS8yK2FuZ2xlKSksYyh5LHkrbCpzaW4oLXBpLzIrYW5nbGUpKSxjb2w9Y29sKQpsaW5lcyhjKHgrbC8yKmNvcygtcGkvMithbmdsZSkseCtsLzIqY29zKC1waS8yK2FuZ2xlKStsLzQqY29zKGFuZ2xlKSksYyh5K2wvMipzaW4oLXBpLzIrYW5nbGUpLHkrbC8yKnNpbigtcGkvMithbmdsZSkrbC80KnNpbihhbmdsZSkpLGNvbD1jb2wpCmxpbmVzKGMoeCtsLzIqY29zKC1waS8yK2FuZ2xlKSx4K2wvMipjb3MoLXBpLzIrYW5nbGUpK2wvNCpjb3MocGkrYW5nbGUpKSxjKHkrbC8yKnNpbigtcGkvMithbmdsZSkseStsLzIqc2luKC1waS8yK2FuZ2xlKStsLzQqc2luKHBpK2FuZ2xlKSksY29sPWNvbCkKbGluZXMoYyh4K2wqY29zKC1waS8yK2FuZ2xlKSx4K2wqY29zKC1waS8yK2FuZ2xlKStsLzIqY29zKC1waS8yK3BpLzQrYW5nbGUpKSxjKHkrbCpzaW4oLXBpLzIrYW5nbGUpLHkrbCpzaW4oLXBpLzIrYW5nbGUpK2wvMipzaW4oLXBpLzIrcGkvNCthbmdsZSkpLGNvbD1jb2wpCmxpbmVzKGMoeCtsKmNvcygtcGkvMithbmdsZSkseCtsKmNvcygtcGkvMithbmdsZSkrbC8yKmNvcygtcGkvMi1waS80K2FuZ2xlKSksYyh5K2wqc2luKC1waS8yK2FuZ2xlKSx5K2wqc2luKC1waS8yK2FuZ2xlKStsLzIqc2luKC1waS8yLXBpLzQrYW5nbGUpKSxjb2w9Y29sKQp9CgpwYXIobWFyPWMoMCwwLDAsMCksbWFpPWMoMCwwLDAsMCkpCnBsb3QoMCwwLHhsYWI9IiIseWxhYj0iIix4bGltPWMoMCwxMCkseWxpbT1jKDAsMTApLGNvbD0wLHhheHQ9Im5vbmUiLHlheHQ9Im5vbmUiLGF4ZXM9RkFMU0UpCnJlY3QoMCw2LDEwLDEwLGJvcmRlcj0icmVkIixsd2Q9MikKdGV4dCguNSw4LCJwb3B1bGF0aWUiLHNydD05MCxjb2w9InJlZCIsY2V4PTIpCnN5bWJvbHMgKDMsIDgsIGNpcmNsZXM9MS41LCBjb2w9InJlZCIsYWRkPVRSVUUsZmc9InJlZCIsaW5jaGVzPUZBTFNFLGx3ZD0yKQpncmlkPXNlcSgwLDEuMywuMDEpCgpmb3IgKGkgaW4gMTo1MCkKewoJYW5nbGUxPXJ1bmlmKG49MSxtaW49MCxtYXg9MzYwKQoJYW5nbGUyPXJ1bmlmKG49MSxtaW49MCxtYXg9MzYwKQoJcmFkaXVzPXNhbXBsZShncmlkLHByb2I9Z3JpZF4yKnBpL3N1bShncmlkXjIqcGkpLHNpemU9MSkKCWtvcHZvZXRlcigzK3JhZGl1cypjb3MoYW5nbGUxLzE4MCpwaSksOCtyYWRpdXMqc2luKGFuZ2xlMS8xODAqcGkpLGFuZ2xlPWFuZ2xlMikKfQp0ZXh0KDcuNSw4LCJFZmZlY3QgdmFuIHJva2VuIGluIHBvcHVsYXRpZSIsY29sPSJyZWQiLGNleD0xLjIpCgpyZWN0KDAsMCwxMCw0LGJvcmRlcj0iYmx1ZSIsbHdkPTIpCnRleHQoLjUsMiwic2FtcGxlIixzcnQ9OTAsY29sPSJibHVlIixjZXg9MikKc3ltYm9scyAoMywgMiwgY2lyY2xlcz0xLjUsIGNvbD0icmVkIixhZGQ9VFJVRSxmZz0iYmx1ZSIsaW5jaGVzPUZBTFNFLGx3ZD0yKQpmb3IgKGkgaW4gMDoyKQoJZm9yIChqIGluIDA6NCkKewoKCWtvcHZvZXRlcigyLjEraiooMy45LTIuMSkvNCwxLjEraSkKfQp0ZXh0KDcuNSwyLCJFZmZlY3QgdmFuIHJva2VuIGluIHN0ZWVrcHJvZWYiLGNvbD0iYmx1ZSIsY2V4PTEuMikKCmFycm93cygzLDUuOSwzLDQuMSxjb2w9ImJsYWNrIixsd2Q9MykKYXJyb3dzKDcsNC4xLDcsNS45LGNvbD0iYmxhY2siLGx3ZD0zKQp0ZXh0KDEuNSw1LCJFWFAuIERFU0lHTiAoMSkiLGNvbD0iYmxhY2siLGNleD0xLjIpCnRleHQoOC41LDUsIlNDSEFUVEVOICZcbklORkVSRU5USUUgKDMpIixjb2w9ImJsYWNrIixjZXg9MS4yKQp0ZXh0KDcuNSwuNSwiREFUQSBFWFBMT1JBVElFICZcbkJFU0NIUklKVkVOREUgU1RBVElTVElFSyAoMikiLGNvbD0iYmxhY2siLGNleD0xLjIpCmBgYAoKLSBQb3B1bGF0aWU/CgoKLS0tCgojIyBEYXRhIEV4cGxvcmF0aWUKCiMjIyBTdW1tYXJ5IHN0YXRpc3RpY3MKCgpgYGB7cn0KZmV2U3VtIDwtIGZldiAlPiUKICBncm91cF9ieShzbW9raW5nKSAlPiUKICBzdW1tYXJpemVfYXQoImZldiIsIAogICAgICAgICAgICAgICBsaXN0KG1lYW4gPSBtZWFuLAogICAgICAgICAgICAgICAgICAgIHNkID0gc2QsCiAgICAgICAgICAgICAgICAgICAgbiA9IGxlbmd0aCkKICAgICAgICAgICAgICAgICkgJT4lCiAgbXV0YXRlKHNlID0gc2Qvc3FydChuKSkKZmV2U3VtCmBgYAoKLSBMZXQgb3AgZGF0IGRlemUgY29kZSBuaWV0IHdlcmt0IGFscyBlciBvbnRicmVrZW5kZSBkYXRhIHppam4uCi0gSW4gZGF0IGdldmFsIGdlYnJ1aWsgamUgb25kZXJzdGFhbmRlIGNvZGUKCgpgYGB7cn0KZmV2U3VtIDwtIGZldiAlPiUKICBncm91cF9ieShzbW9raW5nKSAlPiUKICAgIHN1bW1hcml6ZV9hdCgiZmV2IixsaXN0KG1lYW49fm1lYW4oLixuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgICBzZD1+c2QoLixuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgICBuPWZ1bmN0aW9uKHgpIHglPiVpcy5uYSU+JWAhYCU+JXN1bSkpICU+JQogIG11dGF0ZShzZT1zZC9zcXJ0KG4pKQpmZXZTdW0KYGBgCgojIyBWaXN1YWxpc2F0aWUKCmBgYHtyfQpmZXYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc21va2luZywgeSA9IGZldikpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAuMikKYGBgCgotIEVmZmVjdCBncm9vdHRlPyAKCgpXZSBrdW5uZW4gZGUgcGxvdHMgb29rIGFscyBvYmplY3RlbiBvcHNsYWFuIHZvb3IgbGF0ZXIgZ2VicnVpayEgCgpgYGB7cn0KcDEgPC0gZmV2ICU+JQogIGdncGxvdChhZXMoeD1zbW9raW5nLHk9ZmV2KSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGE9LjIpCgpwMiA8LSBmZXYgJT4lCiAgZ2dwbG90KGFlcyhzYW1wbGU9ZmV2KSkgKwogIGdlb21fcXEoKSArCiAgZ2VvbV9xcV9saW5lKCkgKwogIGZhY2V0X3dyYXAofnNtb2tpbmcpCgpwMQpwMgpgYGAKCgojIyBPbmRlcnpvZWtzdnJhYWcKCkRlIG9uZGVyem9la2VycyB3ZW5zZW4gaGV0IGVmZmVjdCB0ZSBrZW5uZW4gdmFuIHJva2VuIG9wIGRlIGxvbmcgY2FwYWNpdGVpdCB2YW4gam9uZ2VyZW4uIAoKIyMgSG9lIG1vZGVsbGVyZW4gd2UgZGUgZGF0YT8gCgoKLSAkeF9pJDogZHVtbXkgdmFyaWFiZWxlIHZvb3Igcm9rZXJzdGF0dXMgdm9vciBzdWJqZWN0ICRpJDogJHhfe2ksMX09XGJlZ2lue2Nhc2VzfQowJiBcdGV4dHtuaWV0LXJva2VyfVxcCjEmIFx0ZXh0e3Jva2VyfQpcZW5ke2Nhc2VzfSQuIAoKXFtZX2lcdmVydCB4X2lcc2ltIE4oXG11X2ksXHNpZ21hXjIpXF0KCnZpYSBsaW5lYWlyIG1vZGVsPyAKClxbWV9pID0gXGJldGFfMCArIFxiZXRhXzEgeF9pICsgXGVwc2lsb25faSBcdGV4dHsgbWV0IH0gXGVwc2lsb25faVxzaW0gTigwLFxzaWdtYV4yKVxdCgoKVmVydGFhbCBkZSBvbmRlcnpvZWtzdnJhYWcgbmFhciBkZSBtb2RlbCBwYXJhbWV0ZXJzPyAKCmBgYHtyfQpsaWJyYXJ5KEV4cGxvcmVNb2RlbE1hdHJpeCkKCmxtMSA8LSBsbShmZXZ+c21va2luZywgZmV2KQpsbTEgCgpleHBsTXggPC0gVmlzdWFsaXplRGVzaWduKGZldixkZXNpZ25Gb3JtdWxhID0gfnNtb2tpbmcpCmV4cGxNeCRwbG90bGlzdAoKYGBgCgotIFZlcnRhYWwgZGUgb25kZXJ6b2Vrc3ZyYWFnIG5hYXIgZWVuIHBhcmFtZXRlciBpbiBoZXQgbW9kZWwuIAoKLSBFZmZlY3Rncm9vdHRlOiAKClxbXG11XzEtXG11XzAgPSBcYmV0YV8wK1xiZXRhXzEgLSBcYmV0YV8wID0gXGJldGFfMVxdIAoKCiMjIyBTY2hhdHRlbiB2YW4gZGUgZWZmZWN0Z3Jvb3R0ZSBlbiBkZSBzdGFuZGFyZCBlcnJvcgoKYGBge3J9CnN1bW1hcnkobG0xKQpgYGAKCiMjIFN0YXRpc3Rpc2NoZSBJbmZlcmVudGllPwoKIyMjIE51bCBlbiBhbHRlcm5hdGlldmUgaHlwb3RoZXNlPyAKCldlIHdlbnNlbiBkZSAqKmFsdGVybmF0aWV2ZSBoeXBvdGhlc2UqKiAkSF8xJCBhYW4gdGUgdG9uZW46IGVyIGlzIGVlbiBlZmZlY3QgdmFuIHJva2VuIG9wIGRlIGxvbmdjYXBhY2l0ZWl0IHZhbiBqb25nZXJlbi4gV2UgdmVydGFsZW4gZGl0IG5hYXIgb25zIG1vZGVsOgoKLSAkSF8xJDogR2VtaWRkZWxkIGlzIGRlIGxvbmdjYXBhY2l0ZWl0IHZhbiBuaWV0LXJva2VuZGUgZW4gcm9rZW5kZSBqb25nZXJlbiB2ZXJzY2hpbGxlbmQKXFtcYmV0YSBcbmVxIDBcXSAKCldlIGt1bm5lbiBvcCBiYXNpcyB2YW4gZGF0YSBlY2h0ZXIgZWVuIGh5cG90aGVzZSBuaWV0IGJld2lqemVuLiBEYWFyb20genVsbGVuIHdlIGhldCBvbWdla2VlcmRlIGZhbHNpZmnDq3JlbjogCgotICRIXzAkOiBHZW1pZGRlbGQgaXMgZGUgbG9uZ2NhcGFjaXRlaXQgdmFuIG5pZXQtcm9rZW5kZSBlbiByb2tlbmRlIGpvbmdlcmVuIGdlbGlqawpcW1xiZXRhID0gMFxdIAoKLSBIb2UgZmFsc2lmaWVyZW4gd2UgJEhfMCQ/IAoKLS0tCgoKCmBgYHtyfQpzdW1tYXJ5KGxtMSkKYGBgCgotIEhvZSB3YWFyc2NoaWpubGlqayBpcyBoZXQgb20gZWVuIGVmZmVjdGdyb290dGUgKHRlc3Qtc3RhdGlzdGllaykgdGUgdmluZGVuIGRpZSBtZWVyIGV4dHJlZW0gaXMgZGFuIHdhdCB3ZSBvYnNlcnZlZXJkZW4gaW4gZWVuIHJhbmRvbSBzdGVla3Byb2VmIHdhbm5lZXIgZGUgbnVsaHlwb3RoZXNlIHdhYXIgaXMuICAKCi0gQWxzIHdlIGFhbm5hbWVzIGt1bm5lbiBkb2VuIG92ZXIgZGUgdmVyZGVsaW5nZW4gdmFuIGRlIGdlZ2V2ZW5zLCBrZW5uZW4gd2UgZGUgdmVyZGVsaW5nIHZhbiBkZSB0ZXN0IHN0YXRpc3RpZWsgZW4ga3VubmVuIHdlIGRpZSBrYW5zIGJlcmVrZW5lbjogKipwLXdhYXJkZSoqLgotIEFscyBkZSBwLXdhYXJkZSBsYWdlciBpcyBkYW4gaGV0IG5vbWluYWxlIHNpZ25pZmljYW50aWUtbml2ZWF1ICRcYWxwaGEkIHZlcndlcnBlbiB3ZSBkZSBudWxsIGh5cG90aGVzZS4gCgoqV2UgY29udHJvbGVyZW4gZGUga2FucyBvcCBoZXQgbWFrZW4gdmFuIGVlbiB2YWxzIHBvc2l0aWV2ZSBjb25jbHVzaWUgb3AgaGV0ICRcYWxwaGEkLW5pdmVhdSAodHlwZSBJIGZvdXQpKgoKLSBEZSBwLXZhbHVlIGlzIGVua2VsIGNvcnJlY3QgYWxzIGRlIG9uZGVybGlnZ2VuZGUgYWFubmFtZXMgZ2VsZGlnIHppam4uIAoKYGBge3J9CnAxCnAyCmBgYAoKLSBCaW9sb2dpc2NoZSBtZXRpbmdlbiB6aWpuIGRpa3dpamxzIG5pZXQgbm9ybWFhbCB2ZXJkZWVsZC4gCi0gRGlrd2lqbHMgIHppam4gZGUgZ2VnZXZlbnMgbmEgbG9nLXRyYW5zZm9ybWF0aWUgbWVlciBub3JtYWFsIHZlcmRlZWxkLiAgCi0gVmVyc2NoaWxsZW4gb3AgbG9nIHNjaGFhbDoKCiQkClxsb2dfMihTKSAtIFxsb2dfMihOUykgPSBcbG9nXzIgXGZyYWN7U317TlN9ID0gXGxvZ18yIEZDX3tcZnJhY3tTfXtOU319CiQkCgoKLS0tCgojIyBMb2cgdHJhbnNmb3JtYXRpb24KCmBgYHtyfQoKcDEgPC0gZmV2ICU+JQogIGdncGxvdChhZXMoeD1zbW9raW5nLHk9bG9nMihmZXYpKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGE9LjIpCgpwMiA8LSBmZXYgJT4lCiAgZ2dwbG90KGFlcyhzYW1wbGU9bG9nMihmZXYpKSkgKwogIGdlb21fcXEoKSArCiAgZ2VvbV9xcV9saW5lKCkgKwogIGZhY2V0X3dyYXAofnNtb2tpbmcpCnAxCnAyCgpsbTEgPC0gbG0obG9nMihmZXYpfnNtb2tpbmcsZmV2KQpzdW1tYXJ5KGxtMSkKMl4obG0xJGNvZWZmaWNpZW50cykKMl4oY29uZmludChsbTEpKQoKCiMjIHZlcnNjaGlsIGluIHZhcmlhbnRpZSAtLT4gV2VsY2ggdC10ZXN0CgpmZXZ0dGVzdCA8LSB0LnRlc3QobG9nMihmZXYpfnNtb2tpbmcsZmV2KQoyXihmZXZ0dGVzdCRlc3RpbWF0ZVsyXS1mZXZ0dGVzdCRlc3RpbWF0ZVsxXSkKMl4tZmV2dHRlc3QkY29uZi5pbnQgJT4lIHNvcnQKYGBgCgojIyBDb25jbHVzaWUKCi0gRXIgaXMgZWVuIGV4dHJlZW0gc2lnbmlmaWNhbnQgdmVyc2NoaWwgaW4gZ2VtaWRkZWxkZSBsb25naW5ob3VkIHR1c3NlbiByb2tlbmRlIGVuIG5pZXQtcm9rZW5kZSBqb25nZXJlbiAoJHAgPDwgMC4wMDEkKS4gCgotIEdlbWlkZGVsZGUgaXMgZGUgbG9uZ2luaG91ZCBgciAyXihmZXZ0dGVzdCRlc3RpbWF0ZVsyXS1mZXZ0dGVzdCRlc3RpbWF0ZVsxXSkgJT4lIGZvcm1hdChkaWdpdHM9MylgIGtlZXIgZ3JvdGVyIGJpaiByb2tlcmVuZGUgZGFuIGJpaiBuaWV0LXJva2VuZGUgam9uZ2VyZW4gKDk1JSBCSSBbYHIgMl4tZmV2dHRlc3QkY29uZi5pbnQgJT4lIHNvcnQgJT4lIGZvcm1hdChkaWdpdHM9MylgXQouICAKCi0tLQoKLSBQcm9ibGVlbSEKLSBPYnNlcnZhdGlvbmVsZSBzdHVkaWUKLSBDb25mb3VuZGluZyEgCgoKYGBge3IsZWNobz1GQUxTRSwgZmlnLmFsaWduID0gImNlbnRlciIsb3V0LndpZHRoID0gJzUwJSd9CnBsb3QoYygwLDAsMSksYygtMiwyLDApLHBjaD1jKCJMIiwiUiIsIlYiKSx4YXh0PSJub25lIix5YXh0PSJub25lIixheGVzPUZBTFNFLHhsYWI9IiIseWxhYj0iIixjZXg9NCx5bGltPWMoLTIuMiwyLjIpKQphcnJvd3MoeDA9MC4xLHgxPS45LHkwPTEuOCx5MT0wLjEsbHdkPTQpCmFycm93cyh4MD0wLjEseDE9LjkseTA9LTEuOCx5MT0tMC4yLGx3ZD00KQphcnJvd3MoeDA9MCx4MT0wLHkwPS0xLjQseTE9MS40LGx3ZD00KQpgYGAKCi0gTGVlZnRpamQgKEwpIGlzIGhlZWZ0IGVlbiBpbnZsb2VkIG9wIGhldCByb29rZ2VkcmFnIChSKSBlbiBvcCBkZSBsb25naW5ob3VkIChWKSEKLSBBbHMgd2UgbGVlZnRpamQgbmlldCBtZWVuZW1lbiBpbiBkZSBhbmFseXNlIGRhbiBrYW4gaGV0IHppam4gZGF0IGRlIGFzc29jaWF0aWUgdHVzc2VuIHJvb2tnZWRyYWcgZW4gZGUgbG9uZ2luaG91ZCB2ZXJ0ZWtlbmQgaXMgb21kYXQgcm9rZXJzIGVuIG5pZXQtcm9rZXJzIG5pZXQgYWxsZWVuIGluIHJvb2tnZWRyYWcgbWFhciBvb2sgaW4gbGVlZnRpamQgdmVyc2NoaWxsZW4hCi0gQ29uZm91bmRpbmcgaXMgZWVuIHByb2JsZWVtIGRpZSB0eXBpc2NoIG9wdHJlZWR0IGluIG9ic2VydmF0aW9uZWxlIHN0dWRpZXMhCgoKYGBge3J9CmxpYnJhcnkoR0dhbGx5KQoKZmV2ICU+JSAKICBtdXRhdGUobGZldj1sb2cyKGZldikpICU+JSAKICBkcGx5cjo6c2VsZWN0KHNtb2tpbmcsZ2VuZGVyLGFnZSxoZWlnaHRfY20sbGZldikgJT4lIAogIGdncGFpcnMoKQpgYGAKCkVlbiBiZXRlcmUgZGF0YSBleHBsb3JhdGllOiAKYGBge3J9CmZldiAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMuZmFjdG9yKGFnZSkseT1mZXYsZmlsbD1zbW9raW5nKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC4xLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKCkpICsKICB0aGVtZV9idygpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiZGltZ3JleSIsImZpcmVicmljayIpKSArCiAgZ2d0aXRsZSgiQm94cGxvdCBvZiBGRVYgdmVyc3VzIHNtb2tpbmcsIHN0cmF0aWZpZWQgb24gYWdlIGFuZCBnZW5kZXIiKSArCiAgeWxhYigiZmV2IChsKSIpICsKICB4bGFiKCJhZ2UgKHllYXJzKSIpICsgCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhnZW5kZXIpKQpgYGAKCi0tLQoKLSBIb2Ugem91ZGVuIHdlIGRlIGFzc29jaWF0aWUgdHVzc2VuIGxlZWZ0aWpkIGVuIGZldiBrdW5uZW4gbW9kZWxsZXJlbj8gCi0gV2UgZG9lbiBkaXQgZWVyc3QgZW5rZWwgdm9vciBuaWV0LXJva2VuZGUgbWVpc2plcy4gV2FudCBlciBpcyBvb2sgZWVuIGFzc29jaWF0aWUgdHVzc2VuIGZldiBlbiBnZXNsYWNodC4gIAoKYGBge3J9CmxtMiA8LSBmZXYgJT4lIAogIGZpbHRlcihnZW5kZXI9PSJmIiAmIHNtb2tpbmcgPT0gMCkgJT4lIAogIGxtKGxvZzIoZmV2KX5hZ2UsLikKCnN1bW1hcnkobG0yKQpwbG90KGxtMikKCmZldiAlPiUgCiAgZmlsdGVyKGdlbmRlcj09ImYiICYgc21va2luZyA9PSAwKSAlPiUgCiAgZ2dwbG90KGFlcyh4PWFnZSx5PWxvZzIoZmV2KSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHggKQpgYGAKCi0gUmVzaWR1IHBsb3QgaGVlZnQgcHJvYmxlZW0gYWFuIQoKCmBgYHtyfQpsbTMgPC0gZmV2ICU+JSAKICBmaWx0ZXIoZ2VuZGVyPT0iZiIgJiBzbW9raW5nID09IDApICU+JSAKICBsbShsb2cyKGZldil+YWdlICsgSShhZ2UgXjIpLC4pCgpzdW1tYXJ5KGxtMykKcGxvdChsbTMpCmBgYAoKYGBge3J9CmZldiAlPiUgCiAgZ2dwbG90KGFlcyh4PWFnZSx5PWxvZzIoZmV2KSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHggKyBJKHheMikpICsKICBmYWNldF93cmFwKGdlbmRlciB+IHNtb2tpbmcpCmBgYAoKCgojIEFsZ2VtZWVuIExpbmVhciBNb2RlbAoKSG9lIGt1bm5lbiB3ZSBtZWVyZGVyZSBmYWN0b3JlbiBlbiBjb250aW51ZSBwcmVkaWN0b3JlbiBjb21iaW5lcmVuIGluIGVlbiBsaW5lYWlyIG1vZGVsPwoKXFsKeV9pPSBcYmV0YV8wICsgXGJldGFfUyB4X3tpLFN9ICsgXGJldGFfQSB4X3tpLEF9ICArXGVwc2lsb25faSwKXF0KCmVtdAoKLSAkeF97aSxTfSQgZWVuIGR1bW15IHZhcmlhYmVsZSB2b29yIHJva2Vyc3RhdHVzOiAkeF97aSwxfT1cYmVnaW57Y2FzZXN9CjAmIFx0ZXh0e25pZXQtcm9rZXJ9XFwKMSYgXHRleHR7cm9rZXJ9ClxlbmR7Y2FzZXN9JAotICR4X3tpLEF9JCBpcyBkZSBsZWVmdGlqZC4gCgoKLS0tCgojIyBJbXBsZW1lbnRhdGllIGluIFIKCi0gV2UgZG9lbiBkaXQgbnUgZW5rZWwgdm9vciBkZSBtZWlzamVzIG9tZGF0IGVyIG9vayBlZmZlY3RlbiB2YW4gZ2VzbGFjaHQgemlqbi4gTGF0ZXIgbGVyZW4gd2UgaG9lIHdlIGRhYXJtZWUgb20ga3VubmVuIGdhYW4uICAKCmBgYHtyfQpsbVNfYWdlIDwtIGZldiAlPiUgCiAgZmlsdGVyKGdlbmRlcj09ImYiKSAlPiUKICBsbShsb2cyKGZldil+c21va2luZyArIGFnZSxkYXRhPS4pCnN1bW1hcnkobG1TX2FnZSkKYGBgCgpEZSBwYXJhbWV0ZXIgc21va2luZzEga3Jpamd0IG51IGRlIGludGVycHJldGF0aWUgdmFuIGRlIGdlbWlkZGVsZGUgbG9nMiBmb2xkIGNoYW5nZSBpbiBsb25naW5ob3VkIHR1c3NlbiByb2tlcnMgZW4gbmlldCByb2tlcnMgbmEgY29ycmVjdGllIHZvb3IgZGUgbGVlZnRpamQ6IAoKLSBSb2tlcjogXFtFW3l8XHRleHR7cm9rZXJ9LCBYX0E9eF0gPSBcYmV0YV8wICsgXGJldGFfcyArIFxiZXRhX0EgeFxdCi0gTmlldC1yb2tlcjogXFtFW3l8XHRleHR7bmlldC1yb2tlcn0sIFhfQT14XSA9IFxiZXRhXzAgKyBcYmV0YV9BIHhcXQotIHZlcnNjaGlsIHR1c3NlbiByb2tlciBlbiBuaWV0LXJva2VyIHZhbiBkZXplbGZkZSBsZWVmdGlqZCAoJFhfQT14JCk6IApcW0VbeXxcdGV4dHtyb2tlcn0sIFhfQT14XS0gRVt5fFx0ZXh0e25pZXQtcm9rZXJ9LCBYX0E9eF09XGJldGFfMCArIFxiZXRhX3MgKyBcYmV0YV9BIHgtIFxiZXRhXzAgKyBcYmV0YV9BIHggPSBcYmV0YV9zXF0KCldlIHdldGVuIHVpdCBkZSBkYXRhIGV4cGxvcmF0aWUgZWNodGVyIGRhdCBkaXQgbm9nIG5pZXQgaGV0IGNvcnJlY3RlIG1vZGVsIGlzOiAKCi0gTmlldC1saW5lYWlyIGVmZmVjdCB2YW4gbGVlZnRpamQuIAotIEFzc29jaWF0aWUgdHVzc2VuIEZFViBlbiByb2tlcnNzdGF0dXMgdmVyYW5kZXJ0IGkuZi52LiBsZWVmdGlqZCwgd2FhcnNjaGlqbmxpamsgZG9vciBhc3NvY2lhdGllIG1ldCBob2VsYW5nIGFsIHdvcmR0IGdlcm9va3QuIFdlIHp1bGxlbiBsYXRlciB6aWVuIGRhdCBkaXQgZWVuIGludGVyYWN0aWUgaXMuIAotIFdlIHdlbnNlbiBoZXQgbW9kZWwgb29rIHVpdCB0ZSBicmVpZGVuIHZvb3IgR2VuZGVyLgotIFdlIHppZW4gZGF0IGhldCBuaWV0IGV2aWRlbnQgaXMgb20gY29uY2x1c2llIHRlIHRyZWtrZW4gb3AgYmFzaXMgdmFuIG9ic2VydmF0aW9uZWxlIHN0dWRpZXMgb213aWxsZSB2YW4gbW9nZWxpamtzZSBjb25mb3VuZGluZwotIEluIGRlIHZvbGdlbmRlIGxlc3NlbiBnYWFuIHdlIGRpZXBlciBpbiBvcCBoZXQgYWxnZW1lbmUgbGluZWFpciBtb2RlbCB3YWFyaW4gd2UgbWVlcmRlcmUgZmFjdG9yZW4gZW4gY29udGludWUgdmFyaWFiZWxlbiBrdW5uZW4gY29tYmluZXJlbiBhbHMgcHJlZGljdG9yZW4gdm9vciBoZXQgbW9kZWxsZXJlbiB2YW4gaGV0IGdlbWlkZGVsZGUgdmFuIGRlIGNvbnRpbnVlIHJlc3BvbnNlIHZhcmlhYmVsZS4gCgo=