1 Wczytanie i przygotowanie danych

1.1 Wczytanie zbioru danych z pliku płaskiego

autodane<-read.table("/usr/miswdm/autodane.csv", header = TRUE, sep = ",", dec = ".")

1.2 Zmienne pomocnicze

p<-2                # liczba predyktorów
no<-nrow(autodane)
indices<-seq(no)
df<-no-p-1          # liczba stopni swobody

1.3 Zmienne egzogeniczne

wiek<-autodane$Wiek
przebieg<-autodane$Przebieg

1.4 Zmienna endogeniczna

cena<-autodane$Cena

1.5 Nowa obserwacja

Do prognozowania ceny.

new.car<-data.frame(wiek=4, przebieg=145)

2 Obliczanie kowariancji

Kowariancja – miara wielkości zależności liniowej pomiędzy zmiennymi losowymi: \(\begin{aligned} Cov(X, Y)=\mathbb{E}[(X-\mathbb{E}[X])\cdot(Y-\mathbb{E}[Y])]=\mathbb{E}[XY]-\mathbb{E}[X]\cdot\mathbb{E}[Y] \end{aligned}\)

2.1 Wariancja wieku

\(\begin{aligned} Var(X)=\mathbb{E}[(X-\mathbb{E}[X])^2]=\mathbb{E}[X^2]-{\mathbb{E}[X]}^2 \end{aligned}\)

Zgodny, nieobciążony estymator z próbki:
\(\begin{aligned} Var(X)=\frac{1}{n-1}\sum_{i=1}^n(x_i-\bar{x})^2 \end{aligned}\)

wiek.mean<-sum(wiek)/no
wiek.diff<-wiek-wiek.mean
wiek.diff.power<-wiek.diff^2
var.wiek<-sum(wiek.diff.power)/(no-1)
[1] 7.066667

2.2 Wariancja przebiegu

Zgodny, nieobciążony estymator z próbki.

przebieg.mean<-sum(przebieg)/no
przebieg.diff<-przebieg-przebieg.mean
przebieg.diff.power<-przebieg.diff^2
var.przebieg<-sum(przebieg.diff.power)/(no-1)
[1] 2540

2.3 Kowariancja zmiennych egzogenicznych

Zgodny, nieobciążony estymator z próbki:
\(\begin{aligned} Cov(X, Y)=\frac{1}{n-1}\sum_{i=1}^n(x_i-\bar{x})\cdot(y_i-\bar{y}) \end{aligned}\)

x.mult<-wiek.diff*przebieg.diff
cov.x<-sum(x.mult)/(no-1)
[1] 119.8889

Obliczenia pomocnicze.

cena.mean<-sum(cena)/no
cena.diff<-cena-cena.mean
wiek.cena.mult<-wiek.diff*cena.diff
przebieg.cena.mult<-przebieg.diff*cena.diff

Kowariancja wieku i ceny.

cov.wiek.cena<-sum(wiek.cena.mult)/(no-1)
[1] -24.66667

Kowariancja przebiegu i ceny.

cov.przebieg.cena<-sum(przebieg.cena.mult)/(no-1)
[1] -432.7778

3 Wyznaczanie wartości współczynników modelu

3.1 Macierz kowariancji zmiennych niezależnych

A<-matrix(c(var.wiek, cov.x, cov.x, var.przebieg), nrow=p)
dimnames(A)<-list(c('wiek', 'przebieg'), c('wiek', 'przebieg'))

Alternatywnie.

A<-cov(cbind(wiek, przebieg))
               wiek  przebieg
wiek       7.066667  119.8889
przebieg 119.888889 2540.0000

3.2 Wektor kowariancji wartości zmiennych niezależnych i zmiennych zależnych (obserwacji)

B<-c(cov.wiek.cena, cov.przebieg.cena)

Alternatywnie.

B<-cov(cbind(wiek, przebieg), cena)
[1]  -24.66667 -432.77778

3.3 Układ równań liniowych

7.0666667 * B1 + 119.8888889 * B2 = -24.6666667
119.8888889 * B1 + 2540 * B2 = -432.7777778

Rozwiązanie układu.

coef<-solve(A, B)
B1<-coef[1]; B2<-coef[2]
[1] "-3.01122024477396; -0.0282543025323229"

3.4 Wartość wyrazu wolnego B0

Obliczenia pomocnicze.

aux<-B1*wiek+B2*przebieg
aux.numerator<-cena-aux

Wartość wyrazu wolnego B0.

B0<-sum(aux.numerator)/no

Alternatywnie

B0<-mean(cena-as.matrix(cbind(wiek, przebieg)) %*% coef)
[1] 46.9232

4 Model regresji liniowej

4.1 Funkcja prognozy wartości zmiennej zależnej

est.fnc<-function(a, b) B0+B1*a+B2*b

Cena = 46.9232017 - 3.0112202 * Wiek - 0.0282543 * Przebieg

4.2 Prognozowana cena nowej obserwacji

result<-est.fnc(new.car$wiek, new.car$przebieg)[[1]]
[1] 30.78145

4.3 Wariancja resztowa

Obliczenia pomocnicze.

cena.est<-est.fnc(wiek, przebieg)
cena.est.diff<-cena-cena.est
cena.est.diff.power<-cena.est.diff^2

Suma kwadratów resztowych (suma kwadratów błędów).

RSS<-sum(cena.est.diff.power)
[1] 93.4586

Nieobciążony estymator wariancji błędu obserwacji \(\sigma^2\):
\(\begin{aligned} S^2=\frac{\sum_{i=1}^n(y_i-\hat{y_i})^2}{df}=\frac{\sum_{i=1}^n(y_i-\hat{y_i})^2}{n-k}=\frac{RSS}{n-p-1}={RSE}^2 \end{aligned}\)
\(df\) - liczba stopni swobody
\(n\) - liczba obserwacji
\(k\) - liczba estymowanych parametrów; \(k=p+1\)

S2<-RSS/df
[1] 13.35123

5 Dopasowanie modelu

5.1 Współczynnik R2

Całkowita suma kwadratów.

cena.diff.power<-cena.diff^2
TSS<-sum(cena.diff.power)
[1] 872

\(\begin{aligned} R^2=\frac{Regresyjna\ suma\ kwadratów}{Całkowita\ suma\ kwadratów} \end{aligned}\)

R2<-(TSS-RSS)/TSS
[1] 0.8928227

5.2 Współczynnik R2 dopasowany

R2.adj<-1-(1-R2)*(no-1)/df
[1] 0.8622006

5.3 Statystyka F

F<-df*(TSS-RSS)/(p*RSS)
F.pValue<-pf(F, p, df, lower.tail=FALSE)
[1] 0.0004030507

5.4 Test t-Studenta

5.4.1 Macierz zmiennych niezależnych

Pierwszą kolumną jest wartość 1 dla wyznaczenia wartości błędu parametru wyrazu wolnego.

X<-as.matrix(cbind(rep(1, no), wiek, przebieg))

5.4.2 Estymator macierzy kowariancji estymatora metody najmniejszych kwadratów

Macierz kowariancji parametrów modelu.

Sigma<-S2*solve(t(X)%*%X)  #to samo co Sigma<-S2*solve(t(X)%*%X, diag(2))
colnames(Sigma)[1]<-rownames(Sigma)[1]<-'Intercept'

5.4.3 Błędy współczynników

Błąd współczynnika B0.

B0.err<-sqrt(diag(Sigma)['Intercept'])  # to samo co B0.err<-sqrt(Sigma[1,1])
Intercept 
 2.962759 

Błąd współczynnika B1.

B1.err<-sqrt(diag(Sigma)['wiek'])       # to samo co B1.err<-sqrt(Sigma[2,2])
    wiek 
1.026498 

Błąd współczynnika B2.

B2.err<-sqrt(diag(Sigma)['przebieg'])   # to samo co B2.err<-sqrt(Sigma[3,3])
  przebieg 
0.05414379 

5.4.4 Wartości statystyki t

Dla współczynników B0, B1, B2.

B0.t<-B0/B0.err
B1.t<-B1/B1.err
B2.t<-B2/B2.err
[1] "Intercept: 15.8376706620683; wiek: -2.93348798741743; przebieg: -0.521838285707935"

H0: nie ma zależności pomiędzy predyktorem a zmienną objaśnianą; Bi = 0.
Przyjęty poziom istotności: 5%.

alpha<-1-.05/2                          # test obustronny
tValue<-qt(alpha, df, lower.tail=TRUE)  # kwatyl rozkładu t-Studenta

Moduł wartości brzegowej obszaru krytycznego wynosi zatem:

[1] 2.364624

p-wartości

Współczynnik W obszarze krytycznym 5%? p-wartość
B0 Intercept TRUE Pr(>|15.84|)=9.7e-07
B1 wiek TRUE Pr(>|-2.933|)=0.0219
B2 cena FALSE Pr(>|-0.5218|)=0.618

5.5 Przedziały ufności współczynników modelu

Poziom ufności ciągle 95%.

tValues<-qt(c(1-alpha, alpha), df)
B0.conf<-B0+B0.err*tValues
B1.conf<-B1+B1.err*tValues
B2.conf<-B2+B2.err*tValues
Intercept: [39.917389890249; 53.929013589753]
wiek: [-5.43850286812919; -0.583937621418716]
przebieg: [-0.156284021335215; 0.099775416270569]

5.6 Przedział ufności wyestymowanych wartości modelu

Poziom istotności 5%

cena.est.conf<-data.frame(cena.est)
for (i in indices) {
  C<-c(1, wiek[i], przebieg[i])
  delta<-sqrt(drop(t(C)%*%Sigma%*%C))
  interval<-cena.est[i]+tValues*delta
  cena.est.conf$lwr[i]<-interval[1]
  cena.est.conf$upr[i]<-interval[2]
}

6 Prognoza punktowa i błąd ex-ante

6.1 Wariancja prognozy

\(\begin{aligned} S^2_\tau=S^2+S^2\cdot\left(\frac{1}{n}+\frac{(x_\tau-\bar{x})^2}{\sum_{i=1}^n(x_i-\bar{x})^2}\right)= \end{aligned}\\ \begin{aligned} =S^2\cdot\left(1+\frac{1}{n}+\frac{(x_\tau-\bar{x})^2}{\sum_{i=1}^n(x_i-\bar{x})^2}\right)= \end{aligned}\\ \begin{aligned} =S^2\cdot\left(1+\frac{\sum_{i=1}^nx_i^2+nx_\tau^2-2x_\tau\sum_{i=1}^nx_i}{n\sum_{i=1}^nx_i^2-(\sum_{i=1}^nx_i)^2}\right) \end{aligned}\)
\(\hat{\Sigma}\) - estymator macierzy kowariancji estymatora metody najmniejszych kwadratów
\(C\) - wektor wartości nowej obserwacji (do kombinacji liniowej z modelem regresji)
Standardowy błąd obserwacji:
\(\begin{aligned} \delta_\tau=S\sqrt{\frac{1}{n}+\frac{(x_\tau-\bar{x})^2}{\sum_{i=1}^n(x_i-\bar{x})^2}}=\sqrt{C^T\hat{\Sigma}C}=\sqrt{\delta_\tau^2} \end{aligned}\)

C<-c(1, new.car$wiek, new.car$przebieg)
est.var<-drop(t(C)%*%Sigma%*%C)          # to samo co est.var<-drop(C%*%Sigma%*%C)
[1] 24.9955

6.2 Błąd ex-ante

Błąd prognozy punktowej:
\(\begin{aligned} S_\tau=\sqrt{S^2+\delta_\tau^2}=\sqrt{S^2+C^T\hat{\Sigma}C} \end{aligned}\)

exAnte<-sqrt(S2+est.var)
[1] 6.192473

6.3 Względny błąd ex-ante

Względny błąd prognozy punktowej:
\(\begin{aligned} \eta_\tau=\frac{S_\tau}{|\hat{y_\tau}|}\cdot100\% \end{aligned}\)

exAnte.rel<-exAnte/abs(est.fnc(C[2], C[3]))
     wiek 
0.2011755 

6.4 Przedział ufności prognozy

result.conf<-result+exAnte*tValues
[1] 16.13857 45.42432

7 Wykorzystanie pakietu stats

Funkcja lm z pakietu stats (będąca przeciążeniem funkcji glm z tego samego pakietu) pozwala na stworzenie modelu regresji liniowej z zastosowaniem formuł.

est<-lm(cena~wiek+przebieg)

Współczynniki modelu.

est$coefficients  #to samo co coefficients(est)
(Intercept)        wiek    przebieg 
 46.9232017  -3.0112202  -0.0282543 

Podsumowanie modelu.

summary(est)

Call:
lm(formula = cena ~ wiek + przebieg)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.0194 -0.9033 -0.1010  1.4341  6.1219 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 46.92320    2.96276  15.838  9.7e-07 ***
wiek        -3.01122    1.02650  -2.933   0.0219 *  
przebieg    -0.02825    0.05414  -0.522   0.6179    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.654 on 7 degrees of freedom
Multiple R-squared:  0.8928,    Adjusted R-squared:  0.8622 
F-statistic: 29.16 on 2 and 7 DF,  p-value: 0.0004031

Przedziały ufności współczynników modelu (ang. confidence intervals).

confint(est)
                2.5 %      97.5 %
(Intercept) 39.917390 53.92901359
wiek        -5.438503 -0.58393762
przebieg    -0.156284  0.09977542

Przedziały ufności wyestymowanych wartości modelu (ang. narrow intervals).

predict(est, interval="confidence", level=.95)
        fit       lwr      upr
1  11.72522  5.731752 17.71870
2  27.01935 22.767096 31.27161
3  19.44292 15.775111 23.11074
4  33.46561 30.008315 36.92290
5  18.59529 13.701139 23.48945
6  23.86686 18.207126 29.52659
7  29.04167 24.593028 33.49031
8  26.87808 23.092696 30.66346
9  36.47683 32.178454 40.77520
10 43.48817 37.490825 49.48551

Predykcja z zadanym poziomem tolerancji (ang. wide intervals).

pred<-predict(est, newdata=new.car, se.fit=TRUE, level=.95, interval="prediction")  # błąd.obserwacji=TRUE
$fit
       fit      lwr      upr
1 30.78145 16.13857 45.42432

$se.fit
[1] 4.99955

$df
[1] 7

$residual.scale
[1] 3.653933

Błąd ex-ante (wariancja modelu + wariancja obserwacji).

signif(sqrt(pred$residual.scale^2+pred$se.fit**2), 4)
[1] 6.192

8 Zmienne skorelowane

summary(lm(cena~wiek*przebieg))

Alternatywnie.

summary(lm(cena~wiek+przebieg+wiek:przebieg))

Call:
lm(formula = cena ~ wiek * przebieg)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.0207 -0.9115 -0.0935  1.4333  6.1194 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)   46.9001050  4.9745134   9.428  8.1e-05 ***
wiek          -3.0095093  1.1440731  -2.631    0.039 *  
przebieg      -0.0276481  0.1158114  -0.239    0.819    
wiek:przebieg -0.0000646  0.0106521  -0.006    0.995    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.947 on 6 degrees of freedom
Multiple R-squared:  0.8928,    Adjusted R-squared:  0.8392 
F-statistic: 16.66 on 3 and 6 DF,  p-value: 0.002582

9 Wizualizacja

library(scatterplot3d)
s3d<-scatterplot3d(x=wiek, y=przebieg, z=cena, angle=55, highlight.3d=T, scale.y=0.8)
s3d$plane3d(est)
residual3d<-function(x, y, z, residual){
  s3d$points3d(x=x, y=y, z=z-residual, type='p', lwd=1, pch=5, col=3)
  s3d$points3d(x=c(x, x), y=c(y, y), z=c(z-residual, z), type='l', lwd=1, col='dark green')
}
for (i in indices) {
  residual3d(wiek[i], przebieg[i], cena[i], est$residuals[i])
}
# Predykcja z zadanym poziomem tolerancji (ang. wide intervals).
s3d$points3d(x=new.car$wiek, y=new.car$przebieg, z=pred$fit[,'fit'], lwd=1, pch=21, bg=8, col=2)
s3d$points3d(x=rep(new.car$wiek, 2), y=rep(new.car$przebieg, 2), z=pred$fit[,2:3], lwd=1, type='l', col=2)

9.1 Przedziały ufności (osobny przykład)

x<-1:7
y<-c(8, 13, 14, 17, 18, 20, 22)
est<-lm(y~x)

Call:
lm(formula = y ~ x)

Residuals:
      1       2       3       4       5       6       7 
-1.5714  1.2857  0.1429  1.0000 -0.1429 -0.2857 -0.4286 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   7.4286     0.8806   8.436 0.000384 ***
x             2.1429     0.1969  10.882 0.000114 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.042 on 5 degrees of freedom
Multiple R-squared:  0.9595,    Adjusted R-squared:  0.9514 
F-statistic: 118.4 on 1 and 5 DF,  p-value: 0.0001138

Przedziały ufności.

confint(est)  # to samo co confint(est, level=.95))
               2.5 %   97.5 %
(Intercept) 5.164838 9.692304
x           1.636671 2.649043

t-statystyka, stopni swobody: 5.

res<-signif(residuals(est), 3)
pre<-predict(est, level=.99, interval="confidence")
plot(x, y)
abline(est)
segments(x, y, x, pre[,"fit"], col="red")
library(MASS)
library(calibrate)
textxy(x, y, res, cex=0.8)
# Przedziały ufności (ang. narrow intervals)
# 99%
lines(x, pre[,2], col=8)
lines(x, pre[,3], col=8)
# 95%
pre<-predict(est, level=0.95, interval="confidence")
lines(est$model[["x"]], pre[,2], col=5)
lines(est$model[["x"]], pre[,3], col=5)
# Predykcja z zadanym poziomem tolerancji (ang. wide intervals)
# 99%
result<-predict(est, newdata=data.frame(x=c(4.5)), level=.99, interval="prediction")
segments(4.5, result[,"lwr"], 4.5, result[,"upr"], col=8, lwd=5)
textxy(4.5, result[,"lwr"], round(result[,"lwr"], 2), cex=0.85, col=8)
textxy(4.5, result[,"upr"], round(result[,"upr"], 2), cex=0.85, col=8)
# 95%
result<-predict(est, newdata=data.frame(x=c(4.5)), level=.95, interval="prediction")
segments(4.5, result[,"lwr"], 4.5, result[,"upr"], col=5, lwd=2)
textxy(4.5, result[,"lwr"], round(result[,"lwr"], 2), cex=0.85, col=5)
textxy(4.5, result[,"upr"], round(result[,"upr"], 2), cex=0.85, col=5)
points(4.5, result[,"fit"], pch=23, col="darkgreen", bg="green")

LS0tCnRpdGxlOiAiTGluZWFyIFJlZ3Jlc3Npb24iCmF1dGhvcjogIktyenlzenRvZiBNaWVyemVqZXdza2kiCmRhdGU6ICIwMS0wNC0yMDE4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRmX3ByaW50OiBwYWdlZAogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQotLS0KIyBXY3p5dGFuaWUgaSBwcnp5Z290b3dhbmllIGRhbnljaAojIyBXY3p5dGFuaWUgemJpb3J1IGRhbnljaCB6IHBsaWt1IHDFgmFza2llZ28KYGBge3J9CmF1dG9kYW5lPC1yZWFkLnRhYmxlKCIvdXNyL21pc3dkbS9hdXRvZGFuZS5jc3YiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIsIGRlYyA9ICIuIikKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnByaW50KGF1dG9kYW5lKQpgYGAKIyMgWm1pZW5uZSBwb21vY25pY3plCmBgYHtyfQpwPC0yICAgICAgICAgICAgICAgICMgbGljemJhIHByZWR5a3RvcsOzdwpubzwtbnJvdyhhdXRvZGFuZSkKaW5kaWNlczwtc2VxKG5vKQpkZjwtbm8tcC0xICAgICAgICAgICMgbGljemJhIHN0b3BuaSBzd29ib2R5CmBgYAojIyBabWllbm5lIGVnem9nZW5pY3puZQpgYGB7cn0Kd2llazwtYXV0b2RhbmUkV2llawpwcnplYmllZzwtYXV0b2RhbmUkUHJ6ZWJpZWcKYGBgCiMjIFptaWVubmEgZW5kb2dlbmljem5hCmBgYHtyfQpjZW5hPC1hdXRvZGFuZSRDZW5hCmBgYAojIyBOb3dhIG9ic2Vyd2FjamEKRG8gcHJvZ25vem93YW5pYSBjZW55LgpgYGB7cn0KbmV3LmNhcjwtZGF0YS5mcmFtZSh3aWVrPTQsIHByemViaWVnPTE0NSkKYGBgCiMgT2JsaWN6YW5pZSBrb3dhcmlhbmNqaQpLb3dhcmlhbmNqYSDigJMgbWlhcmEgd2llbGtvxZtjaSB6YWxlxbxub8WbY2kgbGluaW93ZWogcG9tacSZZHp5IHptaWVubnltaSBsb3Nvd3ltaToKJFxiZWdpbnthbGlnbmVkfQpDb3YoWCwgWSk9XG1hdGhiYntFfVsoWC1cbWF0aGJie0V9W1hdKVxjZG90KFktXG1hdGhiYntFfVtZXSldPVxtYXRoYmJ7RX1bWFldLVxtYXRoYmJ7RX1bWF1cY2RvdFxtYXRoYmJ7RX1bWV0KXGVuZHthbGlnbmVkfSQKCiMjIFdhcmlhbmNqYSB3aWVrdQokXGJlZ2lue2FsaWduZWR9ClZhcihYKT1cbWF0aGJie0V9WyhYLVxtYXRoYmJ7RX1bWF0pXjJdPVxtYXRoYmJ7RX1bWF4yXS17XG1hdGhiYntFfVtYXX1eMgpcZW5ke2FsaWduZWR9JAoKWmdvZG55LCBuaWVvYmNpxIXFvG9ueSBlc3R5bWF0b3IgeiBwcsOzYmtpOiAgCiRcYmVnaW57YWxpZ25lZH0KVmFyKFgpPVxmcmFjezF9e24tMX1cc3VtX3tpPTF9Xm4oeF9pLVxiYXJ7eH0pXjIKXGVuZHthbGlnbmVkfSQKYGBge3J9CndpZWsubWVhbjwtc3VtKHdpZWspL25vCndpZWsuZGlmZjwtd2llay13aWVrLm1lYW4Kd2llay5kaWZmLnBvd2VyPC13aWVrLmRpZmZeMgp2YXIud2llazwtc3VtKHdpZWsuZGlmZi5wb3dlcikvKG5vLTEpCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwcmludCh2YXIud2llaykKYGBgCiMjIFdhcmlhbmNqYSBwcnplYmllZ3UKWmdvZG55LCBuaWVvYmNpxIXFvG9ueSBlc3R5bWF0b3IgeiBwcsOzYmtpLgpgYGB7cn0KcHJ6ZWJpZWcubWVhbjwtc3VtKHByemViaWVnKS9ubwpwcnplYmllZy5kaWZmPC1wcnplYmllZy1wcnplYmllZy5tZWFuCnByemViaWVnLmRpZmYucG93ZXI8LXByemViaWVnLmRpZmZeMgp2YXIucHJ6ZWJpZWc8LXN1bShwcnplYmllZy5kaWZmLnBvd2VyKS8obm8tMSkKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnByaW50KHZhci5wcnplYmllZykKYGBgCiMjIEtvd2FyaWFuY2phIHptaWVubnljaCBlZ3pvZ2VuaWN6bnljaApaZ29kbnksIG5pZW9iY2nEhcW8b255IGVzdHltYXRvciB6IHByw7Nia2k6ICAKJFxiZWdpbnthbGlnbmVkfQpDb3YoWCwgWSk9XGZyYWN7MX17bi0xfVxzdW1fe2k9MX1ebih4X2ktXGJhcnt4fSlcY2RvdCh5X2ktXGJhcnt5fSkKXGVuZHthbGlnbmVkfSQKYGBge3J9CngubXVsdDwtd2llay5kaWZmKnByemViaWVnLmRpZmYKY292Lng8LXN1bSh4Lm11bHQpLyhuby0xKQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoY292LngpCmBgYApPYmxpY3plbmlhIHBvbW9jbmljemUuCmBgYHtyfQpjZW5hLm1lYW48LXN1bShjZW5hKS9ubwpjZW5hLmRpZmY8LWNlbmEtY2VuYS5tZWFuCndpZWsuY2VuYS5tdWx0PC13aWVrLmRpZmYqY2VuYS5kaWZmCnByemViaWVnLmNlbmEubXVsdDwtcHJ6ZWJpZWcuZGlmZipjZW5hLmRpZmYKYGBgCktvd2FyaWFuY2phIHdpZWt1IGkgY2VueS4KYGBge3J9CmNvdi53aWVrLmNlbmE8LXN1bSh3aWVrLmNlbmEubXVsdCkvKG5vLTEpCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwcmludChjb3Yud2llay5jZW5hKQpgYGAKS293YXJpYW5jamEgcHJ6ZWJpZWd1IGkgY2VueS4KYGBge3J9CmNvdi5wcnplYmllZy5jZW5hPC1zdW0ocHJ6ZWJpZWcuY2VuYS5tdWx0KS8obm8tMSkKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnByaW50KGNvdi5wcnplYmllZy5jZW5hKQpgYGAKIyBXeXpuYWN6YW5pZSB3YXJ0b8WbY2kgd3Nww7PFgmN6eW5uaWvDs3cgbW9kZWx1CiMjIE1hY2llcnoga293YXJpYW5jamkgem1pZW5ueWNoIG5pZXphbGXFvG55Y2gKYGBge3J9CkE8LW1hdHJpeChjKHZhci53aWVrLCBjb3YueCwgY292LngsIHZhci5wcnplYmllZyksIG5yb3c9cCkKZGltbmFtZXMoQSk8LWxpc3QoYygnd2llaycsICdwcnplYmllZycpLCBjKCd3aWVrJywgJ3ByemViaWVnJykpCmBgYApBbHRlcm5hdHl3bmllLgpgYGB7ciBldmFsPUZBTFNFfQpBPC1jb3YoY2JpbmQod2llaywgcHJ6ZWJpZWcpKQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoQSkKYGBgCiMjIFdla3RvciBrb3dhcmlhbmNqaSB3YXJ0b8WbY2kgem1pZW5ueWNoIG5pZXphbGXFvG55Y2ggaSB6bWllbm55Y2ggemFsZcW8bnljaCAob2JzZXJ3YWNqaSkKYGBge3J9CkI8LWMoY292LndpZWsuY2VuYSwgY292LnByemViaWVnLmNlbmEpCmBgYApBbHRlcm5hdHl3bmllLgpgYGB7ciBldmFsPUZBTFNFfQpCPC1jb3YoY2JpbmQod2llaywgcHJ6ZWJpZWcpLCBjZW5hKQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoQikKYGBgCiMjIFVrxYJhZCByw7N3bmHFhCBsaW5pb3d5Y2gKYHIgdmFyLndpZWtgIFwqIEJ+MX4gKyBgciBjb3YueGAgXCogQn4yfiA9IGByIGNvdi53aWVrLmNlbmFgICAKYHIgY292LnhgIFwqIEJ+MX4gKyBgciB2YXIucHJ6ZWJpZWdgIFwqIEJ+Mn4gPSBgciBjb3YucHJ6ZWJpZWcuY2VuYWAKClJvendpxIV6YW5pZSB1a8WCYWR1LgpgYGB7cn0KY29lZjwtc29sdmUoQSwgQikKQjE8LWNvZWZbMV07IEIyPC1jb2VmWzJdCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwYXN0ZShCMSwgQjIsIHNlcCA9ICc7ICcpCmBgYAojIyBXYXJ0b8WbxIcgd3lyYXp1IHdvbG5lZ28gQn4wfgpPYmxpY3plbmlhIHBvbW9jbmljemUuCmBgYHtyfQphdXg8LUIxKndpZWsrQjIqcHJ6ZWJpZWcKYXV4Lm51bWVyYXRvcjwtY2VuYS1hdXgKYGBgCldhcnRvxZvEhyB3eXJhenUgd29sbmVnbyBCfjB+LgpgYGB7cn0KQjA8LXN1bShhdXgubnVtZXJhdG9yKS9ubwpgYGAKQWx0ZXJuYXR5d25pZQpgYGB7ciBldmFsPUZBTFNFfQpCMDwtbWVhbihjZW5hLWFzLm1hdHJpeChjYmluZCh3aWVrLCBwcnplYmllZykpICUqJSBjb2VmKQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoQjApCmBgYAojIE1vZGVsIHJlZ3Jlc2ppIGxpbmlvd2VqCiMjIEZ1bmtjamEgcHJvZ25venkgd2FydG/Fm2NpIHptaWVubmVqIHphbGXFvG5lagpgYGB7cn0KZXN0LmZuYzwtZnVuY3Rpb24oYSwgYikgQjArQjEqYStCMipiCmBgYApDZW5hID0gYHIgQjBgIGByIGlmZWxzZShCMSA8IDAsICctJywgJysnKWAgYHIgYWJzKEIxKWAgXCogV2llayBgciBpZmVsc2UoQjIgPCAwLCAnLScsICcrJylgIGByIGFicyhCMilgIFwqIFByemViaWVnCgojIyBQcm9nbm96b3dhbmEgY2VuYSBub3dlaiBvYnNlcndhY2ppCmBgYHtyfQpyZXN1bHQ8LWVzdC5mbmMobmV3LmNhciR3aWVrLCBuZXcuY2FyJHByemViaWVnKVtbMV1dCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwcmludChyZXN1bHQpCmBgYAojIyBXYXJpYW5jamEgcmVzenRvd2EKT2JsaWN6ZW5pYSBwb21vY25pY3plLgpgYGB7cn0KY2VuYS5lc3Q8LWVzdC5mbmMod2llaywgcHJ6ZWJpZWcpCmNlbmEuZXN0LmRpZmY8LWNlbmEtY2VuYS5lc3QKY2VuYS5lc3QuZGlmZi5wb3dlcjwtY2VuYS5lc3QuZGlmZl4yCmBgYApTdW1hIGt3YWRyYXTDs3cgcmVzenRvd3ljaCAoc3VtYSBrd2FkcmF0w7N3IGLFgsSZZMOzdykuCmBgYHtyfQpSU1M8LXN1bShjZW5hLmVzdC5kaWZmLnBvd2VyKQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoUlNTKQpgYGAKTmllb2JjacSFxbxvbnkgZXN0eW1hdG9yIHdhcmlhbmNqaSBixYLEmWR1IG9ic2Vyd2FjamkgJFxzaWdtYV4yJDogIAokXGJlZ2lue2FsaWduZWR9ClNeMj1cZnJhY3tcc3VtX3tpPTF9Xm4oeV9pLVxoYXR7eV9pfSleMn17ZGZ9PVxmcmFje1xzdW1fe2k9MX1ebih5X2ktXGhhdHt5X2l9KV4yfXtuLWt9PVxmcmFje1JTU317bi1wLTF9PXtSU0V9XjIKXGVuZHthbGlnbmVkfSQgIAokZGYkIC0gbGljemJhIHN0b3BuaSBzd29ib2R5ICAKJG4kIC0gbGljemJhIG9ic2Vyd2FjamkgIAokayQgLSBsaWN6YmEgZXN0eW1vd2FueWNoIHBhcmFtZXRyw7N3OyAkaz1wKzEkCmBgYHtyfQpTMjwtUlNTL2RmCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwcmludChTMikKYGBgCiMgRG9wYXNvd2FuaWUgbW9kZWx1CiMjIFdzcMOzxYJjenlubmlrIFJeMl4KQ2HFgmtvd2l0YSBzdW1hIGt3YWRyYXTDs3cuCmBgYHtyfQpjZW5hLmRpZmYucG93ZXI8LWNlbmEuZGlmZl4yClRTUzwtc3VtKGNlbmEuZGlmZi5wb3dlcikKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnByaW50KFRTUykKYGBgCiRcYmVnaW57YWxpZ25lZH0KUl4yPVxmcmFje1JlZ3Jlc3lqbmFcIHN1bWFcIGt3YWRyYXTDs3d9e0NhxYJrb3dpdGFcIHN1bWFcIGt3YWRyYXTDs3d9ClxlbmR7YWxpZ25lZH0kCmBgYHtyfQpSMjwtKFRTUy1SU1MpL1RTUwpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoUjIpCmBgYAojIyBXc3DDs8WCY3p5bm5payBSXjJeIGRvcGFzb3dhbnkKYGBge3J9ClIyLmFkajwtMS0oMS1SMikqKG5vLTEpL2RmCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwcmludChSMi5hZGopCmBgYAojIyBTdGF0eXN0eWthIEYKYGBge3J9CkY8LWRmKihUU1MtUlNTKS8ocCpSU1MpCkYucFZhbHVlPC1wZihGLCBwLCBkZiwgbG93ZXIudGFpbD1GQUxTRSkKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnByaW50KEYucFZhbHVlKQpgYGAKIyMgVGVzdCB0LVN0dWRlbnRhCiMjIyBNYWNpZXJ6IHptaWVubnljaCBuaWV6YWxlxbxueWNoClBpZXJ3c3rEhSBrb2x1bW7EhSBqZXN0IHdhcnRvxZvEhyBgMWAgZGxhIHd5em5hY3plbmlhIHdhcnRvxZtjaSBixYLEmWR1IHBhcmFtZXRydSB3eXJhenUgd29sbmVnby4KYGBge3J9Clg8LWFzLm1hdHJpeChjYmluZChyZXAoMSwgbm8pLCB3aWVrLCBwcnplYmllZykpCmBgYAojIyMgRXN0eW1hdG9yIG1hY2llcnp5IGtvd2FyaWFuY2ppIGVzdHltYXRvcmEgbWV0b2R5IG5ham1uaWVqc3p5Y2gga3dhZHJhdMOzdwpNYWNpZXJ6IGtvd2FyaWFuY2ppIHBhcmFtZXRyw7N3IG1vZGVsdS4KYGBge3J9ClNpZ21hPC1TMipzb2x2ZSh0KFgpJSolWCkgICN0byBzYW1vIGNvIFNpZ21hPC1TMipzb2x2ZSh0KFgpJSolWCwgZGlhZygyKSkKY29sbmFtZXMoU2lnbWEpWzFdPC1yb3duYW1lcyhTaWdtYSlbMV08LSdJbnRlcmNlcHQnCmBgYAojIyMgQsWCxJlkeSB3c3DDs8WCY3p5bm5pa8OzdwpCxYLEhWQgd3Nww7PFgmN6eW5uaWthIEJ+MH4uCmBgYHtyfQpCMC5lcnI8LXNxcnQoZGlhZyhTaWdtYSlbJ0ludGVyY2VwdCddKSAgIyB0byBzYW1vIGNvIEIwLmVycjwtc3FydChTaWdtYVsxLDFdKQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoQjAuZXJyKQpgYGAKQsWCxIVkIHdzcMOzxYJjenlubmlrYSBCfjF+LgpgYGB7cn0KQjEuZXJyPC1zcXJ0KGRpYWcoU2lnbWEpWyd3aWVrJ10pICAgICAgICMgdG8gc2FtbyBjbyBCMS5lcnI8LXNxcnQoU2lnbWFbMiwyXSkKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnByaW50KEIxLmVycikKYGBgCkLFgsSFZCB3c3DDs8WCY3p5bm5pa2EgQn4yfi4KYGBge3J9CkIyLmVycjwtc3FydChkaWFnKFNpZ21hKVsncHJ6ZWJpZWcnXSkgICAjIHRvIHNhbW8gY28gQjIuZXJyPC1zcXJ0KFNpZ21hWzMsM10pCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwcmludChCMi5lcnIpCmBgYAojIyMgV2FydG/Fm2NpIHN0YXR5c3R5a2kgKip0KioKRGxhIHdzcMOzxYJjenlubmlrw7N3IEJ+MH4sIEJ+MX4sIEJ+Mn4uIApgYGB7cn0KQjAudDwtQjAvQjAuZXJyCkIxLnQ8LUIxL0IxLmVycgpCMi50PC1CMi9CMi5lcnIKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnBhc3RlKGMoJ0ludGVyY2VwdCcsICd3aWVrJywgJ3ByemViaWVnJyksIGMoQjAudCwgQjEudCwgQjIudCksIHNlcCA9ICc6ICcsIGNvbGxhcHNlID0gJzsgJykKYGBgCioqSH4wfioqOiBfbmllIG1hIHphbGXFvG5vxZtjaSBwb21pxJlkenkgcHJlZHlrdG9yZW0gYSB6bWllbm7EhSBvYmphxZtuaWFuxIU7IEJ+aX4gPSAwXy4gIApQcnp5asSZdHkgcG96aW9tIGlzdG90bm/Fm2NpOiAqKjUlKiouIApgYGB7cn0KYWxwaGE8LTEtLjA1LzIgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGVzdCBvYnVzdHJvbm55CnRWYWx1ZTwtcXQoYWxwaGEsIGRmLCBsb3dlci50YWlsPVRSVUUpICAjIGt3YXR5bCByb3prxYJhZHUgdC1TdHVkZW50YQpgYGAKTW9kdcWCIHdhcnRvxZtjaSBicnplZ293ZWogb2JzemFydSBrcnl0eWN6bmVnbyB3eW5vc2kgemF0ZW06CmBgYHtyIGVjaG89RkFMU0V9CnByaW50KHRWYWx1ZSkKYGBgCioqcC13YXJ0b8WbY2kqKgpgYGB7ciBlY2hvPUZBTFNFfQpCMC50LnByIDwtIGZvcm1hdChzaWduaWYoMipwdChhYnMoQjAudCksIGRmLCBsb3dlci50YWlsPUZBTFNFKSAsMykpCkIxLnQucHIgPC0gZm9ybWF0KHNpZ25pZigyKnB0KGFicyhCMS50KSwgZGYsIGxvd2VyLnRhaWw9RkFMU0UpICwzKSkKQjIudC5wciA8LSBmb3JtYXQoc2lnbmlmKDIqcHQoYWJzKEIyLnQpLCBkZiwgbG93ZXIudGFpbD1GQUxTRSkgLDMpKQpgYGAKfCBXc3DDs8WCY3p5bm5payAgIHwgVyBvYnN6YXJ6ZSBrcnl0eWN6bnltIDUlPyAgfCBwLXdhcnRvxZvEhyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAtLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06fAp8IEJ+MH4gSW50ZXJjZXB0IHwgYHIgYWJzKEIwLnQpPnRWYWx1ZWAgICAgICB8IFByKD5cfGByIHNpZ25pZihCMC50LCA0KWBcfCk9KipgciBCMC50LnByYCoqIHwKfCBCfjF+IHdpZWsgICAgICB8IGByIGFicyhCMS50KT50VmFsdWVgICAgICAgfCBQcig+XHxgciBzaWduaWYoQjEudCwgNClgXHwpPSoqYHIgQjEudC5wcmAqKiB8CnwgQn4yfiBjZW5hICAgICAgfCBgciBhYnMoQjIudCk+dFZhbHVlYCAgICAgIHwgUHIoPlx8YHIgc2lnbmlmKEIyLnQsIDQpYFx8KT0qKmByIEIyLnQucHJgKiogfApgYGB7cn0KYGBgCiMjIFByemVkemlhxYJ5IHVmbm/Fm2NpIHdzcMOzxYJjenlubmlrw7N3IG1vZGVsdQpQb3ppb20gdWZub8WbY2kgY2nEhWdsZSAqKjk1JSoqLgpgYGB7cn0KdFZhbHVlczwtcXQoYygxLWFscGhhLCBhbHBoYSksIGRmKQpCMC5jb25mPC1CMCtCMC5lcnIqdFZhbHVlcwpCMS5jb25mPC1CMStCMS5lcnIqdFZhbHVlcwpCMi5jb25mPC1CMitCMi5lcnIqdFZhbHVlcwpgYGAKYGBge3IgZWNobz1GQUxTRX0KY2F0KHBhc3RlKGMoJ0ludGVyY2VwdCcsICd3aWVrJywgJ3ByemViaWVnJyksIGMocGFzdGUoJ1snLCBwYXN0ZShCMC5jb25mLCBjb2xsYXBzZT0nOyAnKSwgJ10nLCBzZXA9JycpLCBwYXN0ZSgnWycsIHBhc3RlKEIxLmNvbmYsIGNvbGxhcHNlPSc7ICcpLCAnXScsIHNlcD0nJyksIHBhc3RlKCdbJywgcGFzdGUoQjIuY29uZiwgY29sbGFwc2U9JzsgJyksICddJywgc2VwPScnKSksIHNlcCA9ICc6ICcsIGNvbGxhcHNlID0gJ1xuJykpCmBgYAojIyBQcnplZHppYcWCIHVmbm/Fm2NpIHd5ZXN0eW1vd2FueWNoIHdhcnRvxZtjaSBtb2RlbHUKUG96aW9tIGlzdG90bm/Fm2NpICoqNSUqKgpgYGB7cn0KY2VuYS5lc3QuY29uZjwtZGF0YS5mcmFtZShjZW5hLmVzdCkKZm9yIChpIGluIGluZGljZXMpIHsKICBDPC1jKDEsIHdpZWtbaV0sIHByemViaWVnW2ldKQogIGRlbHRhPC1zcXJ0KGRyb3AodChDKSUqJVNpZ21hJSolQykpCiAgaW50ZXJ2YWw8LWNlbmEuZXN0W2ldK3RWYWx1ZXMqZGVsdGEKICBjZW5hLmVzdC5jb25mJGx3cltpXTwtaW50ZXJ2YWxbMV0KICBjZW5hLmVzdC5jb25mJHVwcltpXTwtaW50ZXJ2YWxbMl0KfQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoY2VuYS5lc3QuY29uZikKYGBgCiMgUHJvZ25vemEgcHVua3Rvd2EgaSBixYLEhWQgZXgtYW50ZQojIyBXYXJpYW5jamEgcHJvZ25venkKJFxiZWdpbnthbGlnbmVkfQpTXjJfXHRhdT1TXjIrU14yXGNkb3RcbGVmdChcZnJhY3sxfXtufStcZnJhY3soeF9cdGF1LVxiYXJ7eH0pXjJ9e1xzdW1fe2k9MX1ebih4X2ktXGJhcnt4fSleMn1ccmlnaHQpPQpcZW5ke2FsaWduZWR9XFwKXGJlZ2lue2FsaWduZWR9Cj1TXjJcY2RvdFxsZWZ0KDErXGZyYWN7MX17bn0rXGZyYWN7KHhfXHRhdS1cYmFye3h9KV4yfXtcc3VtX3tpPTF9Xm4oeF9pLVxiYXJ7eH0pXjJ9XHJpZ2h0KT0KXGVuZHthbGlnbmVkfVxcClxiZWdpbnthbGlnbmVkfQo9U14yXGNkb3RcbGVmdCgxK1xmcmFje1xzdW1fe2k9MX1ebnhfaV4yK254X1x0YXVeMi0yeF9cdGF1XHN1bV97aT0xfV5ueF9pfXtuXHN1bV97aT0xfV5ueF9pXjItKFxzdW1fe2k9MX1ebnhfaSleMn1ccmlnaHQpClxlbmR7YWxpZ25lZH0kICAKJFxoYXR7XFNpZ21hfSQgLSBlc3R5bWF0b3IgbWFjaWVyenkga293YXJpYW5jamkgZXN0eW1hdG9yYSBtZXRvZHkgbmFqbW5pZWpzenljaCBrd2FkcmF0w7N3ICAKJEMkIC0gd2VrdG9yIHdhcnRvxZtjaSBub3dlaiBvYnNlcndhY2ppIChkbyBrb21iaW5hY2ppIGxpbmlvd2VqIHogbW9kZWxlbSByZWdyZXNqaSkgIApTdGFuZGFyZG93eSBixYLEhWQgb2JzZXJ3YWNqaTogIAokXGJlZ2lue2FsaWduZWR9ClxkZWx0YV9cdGF1PVNcc3FydHtcZnJhY3sxfXtufStcZnJhY3soeF9cdGF1LVxiYXJ7eH0pXjJ9e1xzdW1fe2k9MX1ebih4X2ktXGJhcnt4fSleMn19PVxzcXJ0e0NeVFxoYXR7XFNpZ21hfUN9PVxzcXJ0e1xkZWx0YV9cdGF1XjJ9ClxlbmR7YWxpZ25lZH0kCmBgYHtyfQpDPC1jKDEsIG5ldy5jYXIkd2llaywgbmV3LmNhciRwcnplYmllZykKZXN0LnZhcjwtZHJvcCh0KEMpJSolU2lnbWElKiVDKSAgICAgICAgICAjIHRvIHNhbW8gY28gZXN0LnZhcjwtZHJvcChDJSolU2lnbWElKiVDKQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQoZXN0LnZhcikKYGBgCiMjIELFgsSFZCBleC1hbnRlCkLFgsSFZCBwcm9nbm96eSBwdW5rdG93ZWo6ICAKJFxiZWdpbnthbGlnbmVkfQpTX1x0YXU9XHNxcnR7U14yK1xkZWx0YV9cdGF1XjJ9PVxzcXJ0e1NeMitDXlRcaGF0e1xTaWdtYX1DfQpcZW5ke2FsaWduZWR9JApgYGB7cn0KZXhBbnRlPC1zcXJ0KFMyK2VzdC52YXIpCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwcmludChleEFudGUpCmBgYAojIyBXemdsxJlkbnkgYsWCxIVkIGV4LWFudGUKV3pnbMSZZG55IGLFgsSFZCBwcm9nbm96eSBwdW5rdG93ZWo6ICAKJFxiZWdpbnthbGlnbmVkfQpcZXRhX1x0YXU9XGZyYWN7U19cdGF1fXt8XGhhdHt5X1x0YXV9fH1cY2RvdDEwMFwlClxlbmR7YWxpZ25lZH0kCmBgYHtyfQpleEFudGUucmVsPC1leEFudGUvYWJzKGVzdC5mbmMoQ1syXSwgQ1szXSkpCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpwcmludChleEFudGUucmVsKQpgYGAKIyMgUHJ6ZWR6aWHFgiB1Zm5vxZtjaSBwcm9nbm96eQpgYGB7cn0KcmVzdWx0LmNvbmY8LXJlc3VsdCtleEFudGUqdFZhbHVlcwpgYGAKYGBge3IgZWNobz1GQUxTRX0KcHJpbnQocmVzdWx0LmNvbmYpCmBgYAojIFd5a29yenlzdGFuaWUgcGFraWV0dSBgc3RhdHNgCkZ1bmtjamEgYGxtYCB6IHBha2lldHUgYHN0YXRzYCAoYsSZZMSFY2EgcHJ6ZWNpxIXFvGVuaWVtIGZ1bmtjamkgYGdsbWAgeiB0ZWdvIHNhbWVnbyBwYWtpZXR1KSBwb3p3YWxhIG5hIHN0d29yemVuaWUgbW9kZWx1IHJlZ3Jlc2ppIGxpbmlvd2VqIHogemFzdG9zb3dhbmllbSBfZm9ybXXFgl8uCmBgYHtyfQplc3Q8LWxtKGNlbmF+d2llaytwcnplYmllZykKYGBgCldzcMOzxYJjenlubmlraSBtb2RlbHUuCmBgYHtyfQplc3QkY29lZmZpY2llbnRzICAjdG8gc2FtbyBjbyBjb2VmZmljaWVudHMoZXN0KQpgYGAKUG9kc3Vtb3dhbmllIG1vZGVsdS4KYGBge3J9CnN1bW1hcnkoZXN0KQpgYGAKUHJ6ZWR6aWHFgnkgdWZub8WbY2kgd3Nww7PFgmN6eW5uaWvDs3cgbW9kZWx1IChhbmcuIF9jb25maWRlbmNlIGludGVydmFsc18pLgpgYGB7cn0KY29uZmludChlc3QpCmBgYApQcnplZHppYcWCeSB1Zm5vxZtjaSB3eWVzdHltb3dhbnljaCB3YXJ0b8WbY2kgbW9kZWx1IChhbmcuIF9uYXJyb3cgaW50ZXJ2YWxzXykuCmBgYHtyfQpwcmVkaWN0KGVzdCwgaW50ZXJ2YWw9ImNvbmZpZGVuY2UiLCBsZXZlbD0uOTUpCmBgYApQcmVkeWtjamEgeiB6YWRhbnltIHBvemlvbWVtIHRvbGVyYW5jamkgKGFuZy4gX3dpZGUgaW50ZXJ2YWxzXykuCmBgYHtyfQpwcmVkPC1wcmVkaWN0KGVzdCwgbmV3ZGF0YT1uZXcuY2FyLCBzZS5maXQ9VFJVRSwgbGV2ZWw9Ljk1LCBpbnRlcnZhbD0icHJlZGljdGlvbiIpICAjIGLFgsSFZC5vYnNlcndhY2ppPVRSVUUKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnByaW50KHByZWQpCmBgYApCxYLEhWQgZXgtYW50ZSAod2FyaWFuY2phIG1vZGVsdSArIHdhcmlhbmNqYSBvYnNlcndhY2ppKS4KYGBge3J9CnNpZ25pZihzcXJ0KHByZWQkcmVzaWR1YWwuc2NhbGVeMitwcmVkJHNlLmZpdCoqMiksIDQpCmBgYAojIFptaWVubmUgc2tvcmVsb3dhbmUKYGBge3IgZXZhbD1GQUxTRX0Kc3VtbWFyeShsbShjZW5hfndpZWsqcHJ6ZWJpZWcpKQpgYGAKQWx0ZXJuYXR5d25pZS4KYGBge3IgZXZhbD1GQUxTRX0Kc3VtbWFyeShsbShjZW5hfndpZWsrcHJ6ZWJpZWcrd2llazpwcnplYmllZykpCmBgYApgYGB7ciBlY2hvPUZBTFNFfQpzdW1tYXJ5KGxtKGNlbmF+d2llaypwcnplYmllZykpCmBgYAojIFdpenVhbGl6YWNqYQpgYGB7ciBldmFsPUZBTFNFfQpsaWJyYXJ5KHNjYXR0ZXJwbG90M2QpCnMzZDwtc2NhdHRlcnBsb3QzZCh4PXdpZWssIHk9cHJ6ZWJpZWcsIHo9Y2VuYSwgYW5nbGU9NTUsIGhpZ2hsaWdodC4zZD1ULCBzY2FsZS55PTAuOCkKczNkJHBsYW5lM2QoZXN0KQpyZXNpZHVhbDNkPC1mdW5jdGlvbih4LCB5LCB6LCByZXNpZHVhbCl7CiAgczNkJHBvaW50czNkKHg9eCwgeT15LCB6PXotcmVzaWR1YWwsIHR5cGU9J3AnLCBsd2Q9MSwgcGNoPTUsIGNvbD0zKQogIHMzZCRwb2ludHMzZCh4PWMoeCwgeCksIHk9Yyh5LCB5KSwgej1jKHotcmVzaWR1YWwsIHopLCB0eXBlPSdsJywgbHdkPTEsIGNvbD0nZGFyayBncmVlbicpCn0KZm9yIChpIGluIGluZGljZXMpIHsKICByZXNpZHVhbDNkKHdpZWtbaV0sIHByemViaWVnW2ldLCBjZW5hW2ldLCBlc3QkcmVzaWR1YWxzW2ldKQp9CiMgUHJlZHlrY2phIHogemFkYW55bSBwb3ppb21lbSB0b2xlcmFuY2ppIChhbmcuIHdpZGUgaW50ZXJ2YWxzKS4KczNkJHBvaW50czNkKHg9bmV3LmNhciR3aWVrLCB5PW5ldy5jYXIkcHJ6ZWJpZWcsIHo9cHJlZCRmaXRbLCdmaXQnXSwgbHdkPTEsIHBjaD0yMSwgYmc9OCwgY29sPTIpCnMzZCRwb2ludHMzZCh4PXJlcChuZXcuY2FyJHdpZWssIDIpLCB5PXJlcChuZXcuY2FyJHByemViaWVnLCAyKSwgej1wcmVkJGZpdFssMjozXSwgbHdkPTEsIHR5cGU9J2wnLCBjb2w9MikKYGBgCmBgYHtyIGVjaG89RkFMU0V9CmxpYnJhcnkoc2NhdHRlcnBsb3QzZCkKczNkPC1zY2F0dGVycGxvdDNkKHg9d2llaywgeT1wcnplYmllZywgej1jZW5hLCBhbmdsZT01NSwgaGlnaGxpZ2h0LjNkPVQsIHNjYWxlLnk9MC44KQpzM2QkcGxhbmUzZChlc3QpCnJlc2lkdWFsM2Q8LWZ1bmN0aW9uKHgsIHksIHosIHJlc2lkdWFsKXsKICBzM2QkcG9pbnRzM2QoeD14LCB5PXksIHo9ei1yZXNpZHVhbCwgdHlwZT0ncCcsIGx3ZD0xLCBwY2g9NSwgY29sPTMpCiAgczNkJHBvaW50czNkKHg9Yyh4LCB4KSwgeT1jKHksIHkpLCB6PWMoei1yZXNpZHVhbCwgeiksIHR5cGU9J2wnLCBsd2Q9MSwgY29sPSdkYXJrIGdyZWVuJykKfQpmb3IgKGkgaW4gaW5kaWNlcykgewogIHJlc2lkdWFsM2Qod2lla1tpXSwgcHJ6ZWJpZWdbaV0sIGNlbmFbaV0sIGVzdCRyZXNpZHVhbHNbaV0pCn0KIyBQcmVkeWtjamEgeiB6YWRhbnltIHBvemlvbWVtIHRvbGVyYW5jamkgKGFuZy4gd2lkZSBpbnRlcnZhbHMpLgpzM2QkcG9pbnRzM2QoeD1uZXcuY2FyJHdpZWssIHk9bmV3LmNhciRwcnplYmllZywgej1wcmVkJGZpdFssJ2ZpdCddLCBsd2Q9MSwgcGNoPTIxLCBiZz04LCBjb2w9MikKczNkJHBvaW50czNkKHg9cmVwKG5ldy5jYXIkd2llaywgMiksIHk9cmVwKG5ldy5jYXIkcHJ6ZWJpZWcsIDIpLCB6PXByZWQkZml0WywyOjNdLCBsd2Q9MSwgdHlwZT0nbCcsIGNvbD0yKQpgYGAKIyMgUHJ6ZWR6aWHFgnkgdWZub8WbY2kgKG9zb2JueSBwcnp5a8WCYWQpCmBgYHtyfQp4PC0xOjcKeTwtYyg4LCAxMywgMTQsIDE3LCAxOCwgMjAsIDIyKQplc3Q8LWxtKHl+eCkKYGBgCmBgYHtyIGVjaG89RkFMU0V9CnByaW50KHN1bW1hcnkoZXN0KSkKYGBgClByemVkemlhxYJ5IHVmbm/Fm2NpLgpgYGB7cn0KY29uZmludChlc3QpICAjIHRvIHNhbW8gY28gY29uZmludChlc3QsIGxldmVsPS45NSkpCmBgYAp0LXN0YXR5c3R5a2EsIHN0b3BuaSBzd29ib2R5OiBgciBkZi5yZXNpZHVhbChlc3QpYC4KYGBge3IgZXZhbD1GQUxTRX0KcmVzPC1zaWduaWYocmVzaWR1YWxzKGVzdCksIDMpCnByZTwtcHJlZGljdChlc3QsIGxldmVsPS45OSwgaW50ZXJ2YWw9ImNvbmZpZGVuY2UiKQpwbG90KHgsIHkpCmFibGluZShlc3QpCnNlZ21lbnRzKHgsIHksIHgsIHByZVssImZpdCJdLCBjb2w9InJlZCIpCmxpYnJhcnkoTUFTUykKbGlicmFyeShjYWxpYnJhdGUpCnRleHR4eSh4LCB5LCByZXMsIGNleD0wLjgpCiMgUHJ6ZWR6aWHFgnkgdWZub8WbY2kgKGFuZy4gbmFycm93IGludGVydmFscykKIyA5OSUKbGluZXMoeCwgcHJlWywyXSwgY29sPTgpCmxpbmVzKHgsIHByZVssM10sIGNvbD04KQojIDk1JQpwcmU8LXByZWRpY3QoZXN0LCBsZXZlbD0wLjk1LCBpbnRlcnZhbD0iY29uZmlkZW5jZSIpCmxpbmVzKGVzdCRtb2RlbFtbIngiXV0sIHByZVssMl0sIGNvbD01KQpsaW5lcyhlc3QkbW9kZWxbWyJ4Il1dLCBwcmVbLDNdLCBjb2w9NSkKIyBQcmVkeWtjamEgeiB6YWRhbnltIHBvemlvbWVtIHRvbGVyYW5jamkgKGFuZy4gd2lkZSBpbnRlcnZhbHMpCiMgOTklCnJlc3VsdDwtcHJlZGljdChlc3QsIG5ld2RhdGE9ZGF0YS5mcmFtZSh4PWMoNC41KSksIGxldmVsPS45OSwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQpzZWdtZW50cyg0LjUsIHJlc3VsdFssImx3ciJdLCA0LjUsIHJlc3VsdFssInVwciJdLCBjb2w9OCwgbHdkPTUpCnRleHR4eSg0LjUsIHJlc3VsdFssImx3ciJdLCByb3VuZChyZXN1bHRbLCJsd3IiXSwgMiksIGNleD0wLjg1LCBjb2w9OCkKdGV4dHh5KDQuNSwgcmVzdWx0WywidXByIl0sIHJvdW5kKHJlc3VsdFssInVwciJdLCAyKSwgY2V4PTAuODUsIGNvbD04KQojIDk1JQpyZXN1bHQ8LXByZWRpY3QoZXN0LCBuZXdkYXRhPWRhdGEuZnJhbWUoeD1jKDQuNSkpLCBsZXZlbD0uOTUsIGludGVydmFsPSJwcmVkaWN0aW9uIikKc2VnbWVudHMoNC41LCByZXN1bHRbLCJsd3IiXSwgNC41LCByZXN1bHRbLCJ1cHIiXSwgY29sPTUsIGx3ZD0yKQp0ZXh0eHkoNC41LCByZXN1bHRbLCJsd3IiXSwgcm91bmQocmVzdWx0WywibHdyIl0sIDIpLCBjZXg9MC44NSwgY29sPTUpCnRleHR4eSg0LjUsIHJlc3VsdFssInVwciJdLCByb3VuZChyZXN1bHRbLCJ1cHIiXSwgMiksIGNleD0wLjg1LCBjb2w9NSkKcG9pbnRzKDQuNSwgcmVzdWx0WywiZml0Il0sIHBjaD0yMywgY29sPSJkYXJrZ3JlZW4iLCBiZz0iZ3JlZW4iKQpgYGAKYGBge3IgZWNobz1GQUxTRX0KcmVzPC1zaWduaWYocmVzaWR1YWxzKGVzdCksIDMpCnByZTwtcHJlZGljdChlc3QsIGxldmVsPS45OSwgaW50ZXJ2YWw9ImNvbmZpZGVuY2UiKQpwbG90KHgsIHkpCmFibGluZShlc3QpCnNlZ21lbnRzKHgsIHksIHgsIHByZVssImZpdCJdLCBjb2w9InJlZCIpCmxpYnJhcnkoTUFTUykKbGlicmFyeShjYWxpYnJhdGUpCnRleHR4eSh4LCB5LCByZXMsIGNleD0wLjgpCiMgUHJ6ZWR6aWHFgnkgdWZub8WbY2kgKGFuZy4gbmFycm93IGludGVydmFscykKIyA5OSUKbGluZXMoeCwgcHJlWywyXSwgY29sPTgpCmxpbmVzKHgsIHByZVssM10sIGNvbD04KQojIDk1JQpwcmU8LXByZWRpY3QoZXN0LCBsZXZlbD0wLjk1LCBpbnRlcnZhbD0iY29uZmlkZW5jZSIpCmxpbmVzKGVzdCRtb2RlbFtbIngiXV0sIHByZVssMl0sIGNvbD01KQpsaW5lcyhlc3QkbW9kZWxbWyJ4Il1dLCBwcmVbLDNdLCBjb2w9NSkKIyBQcmVkeWtjamEgeiB6YWRhbnltIHBvemlvbWVtIHRvbGVyYW5jamkgKGFuZy4gd2lkZSBpbnRlcnZhbHMpCiMgOTklCnJlc3VsdDwtcHJlZGljdChlc3QsIG5ld2RhdGE9ZGF0YS5mcmFtZSh4PWMoNC41KSksIGxldmVsPS45OSwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQpzZWdtZW50cyg0LjUsIHJlc3VsdFssImx3ciJdLCA0LjUsIHJlc3VsdFssInVwciJdLCBjb2w9OCwgbHdkPTUpCnRleHR4eSg0LjUsIHJlc3VsdFssImx3ciJdLCByb3VuZChyZXN1bHRbLCJsd3IiXSwgMiksIGNleD0wLjg1LCBjb2w9OCkKdGV4dHh5KDQuNSwgcmVzdWx0WywidXByIl0sIHJvdW5kKHJlc3VsdFssInVwciJdLCAyKSwgY2V4PTAuODUsIGNvbD04KQojIDk1JQpyZXN1bHQ8LXByZWRpY3QoZXN0LCBuZXdkYXRhPWRhdGEuZnJhbWUoeD1jKDQuNSkpLCBsZXZlbD0uOTUsIGludGVydmFsPSJwcmVkaWN0aW9uIikKc2VnbWVudHMoNC41LCByZXN1bHRbLCJsd3IiXSwgNC41LCByZXN1bHRbLCJ1cHIiXSwgY29sPTUsIGx3ZD0yKQp0ZXh0eHkoNC41LCByZXN1bHRbLCJsd3IiXSwgcm91bmQocmVzdWx0WywibHdyIl0sIDIpLCBjZXg9MC44NSwgY29sPTUpCnRleHR4eSg0LjUsIHJlc3VsdFssInVwciJdLCByb3VuZChyZXN1bHRbLCJ1cHIiXSwgMiksIGNleD0wLjg1LCBjb2w9NSkKcG9pbnRzKDQuNSwgcmVzdWx0WywiZml0Il0sIHBjaD0yMywgY29sPSJkYXJrZ3JlZW4iLCBiZz0iZ3JlZW4iKQpgYGA=