Dlaczego kwantylowa?

Dlaczego potrzebujemy regresji kwantylowej (QR)?

W szczególności, QR:

  • jest odporna na punkty odstające i wpływowe

  • nie zakłada stałej wariancji (znanej jako homoskedastyczność) dla zmiennej odpowiedzi lub reszt

  • nie zakłada normalności ale główną zaletą QR w porównaniu z regresją liniową (LR) jest to, że QR bada różne wartości zmiennej odpowiedzi, a nie tylko średnią, i dostarcza w związku z tym pełniejszego obrazu związków między zmiennymi!

Wprowadzenie

Regresja kwantylowa (ang. quantile regression) została zaproponowana przez Koenkera i Bassetta (1978). Szczególny przypadek regresji kwantylowej dla kwantyla rzędu 0,5 (czyli mediany) jest równoważny estymatorowi LAD (ang. Least Absolute Deviation) – minimalizuje sumę bezwzględnych błędów.
Wprowadzenie różnych kwantyli regresji daje pełniejszy opis rozkładów warunkowych zwłaszcza w przypadku rozkładów asymetrycznych lub uciętych.

Regresja kwantylowa jest kolejną wariacją na temat najmniejszych kwadratów . Stratą jest współczynnik \(l_1\) funkcji:

\[ \phi(u) = \tau\max(u,0) - (1-\tau)\max(-u,0) = \frac{1}{2}|u| + \left(\tau - \frac{1}{2}\right)u, \]

gdzie \(\tau \in (0,1)\) oznacza konkretny kwantyl. Problemem jak poprzednio jest minimalizacja całkowitej straty resztowej. Model ten jest powszechnie stosowany w ekologii, ochronie zdrowia i innych dziedzinach, gdzie sama średnia nie wystarcza do uchwycenia złożonych zależności między zmiennymi.

Wymagania

Wymagana jest jedna liczbowa zmienna zależna. Zmienna przewidywana musi być zmienną ilościową. Predyktory mogą być zmiennymi ilościowymi lub sztucznymi zmiennymi w przypadku predyktorów jakościowych. Aby można było uruchomić analizę, wymagany jest wyraz wolny lub co najmniej jeden predyktor.

Regresja kwantylowa nie czyni założeń dotyczących rozkładu zmiennej przewidywanej i jest odporna na wpływ obserwacji odstających.

Analiza kwantylowa jest pokrewna regresji metodą najmniejszych kwadratów.

Przykład 1.

Wykorzystamy przykład z pakietu quantreg.

Jaki jest związek między całkowitym dochodem gospodarstwa domowego a odsetkiem dochodów wydatkowanych na żywność? Prawo Engela w ekonomii głosi, że w miarę wzrostu dochodów, część dochodów wydatkowanych na żywność spada, nawet jeśli wydatki na żywność bezwzględnie rosną. Stosując regresję kwantylową do tych danych, można określić, jakie wydatki na żywność ponosi 90% rodzin (dla 100 rodzin z danym dochodem), gdy nie interesują nas średnie wydatki na żywność.

Dane, które wykorzystamy - to zbiór “engel” - dane dotyczące wydatków na żywność. Jest to zbiór danych regresyjnych składający się z 235 obserwacji dotyczących dochodów i wydatków na żywność dla belgijskich gospodarstw domowych klasy robotniczej.

Powyższy wykres przedstawia dopasowanie regresji kwantylowej dla \(\tau = (0.1, 0.25, 0.5, 0.75, 0.90, 0.95)\). Dopasowanie KMNK to gruba czarna linia.

Poniżej znajduje się tabela z oszacowanymi współczynnikami.

knitr::kable(fits, format = "html", caption = "Oszacowania z KMNK oraz `quantreg`") %>%
    kable_styling("striped") %>%
    column_spec(1:8, background = "#ececec")
Oszacowania z KMNK oraz quantreg
OLS \(\tau_{0.10}\) \(\tau_{0.25}\) \(\tau_{0.50}\) \(\tau_{0.75}\) \(\tau_{0.90}\) \(\tau_{0.95}\)
(Intercept) 147.4753885 110.1415742 95.4835396 81.4822474 62.3965855 67.3508721 64.1039632
income 0.4851784 0.4017658 0.4741032 0.5601806 0.6440141 0.6862995 0.7090685

Ok, możemy to zrobić bardziej przejrzyście i sformatować w ładnej tabeli wyników:

## 
## Wyniki regresji kwantylowych
## ==========================================
##                   Dependent variable:     
##              -----------------------------
##                         foodexp           
##                 (1)       (2)       (3)   
## ------------------------------------------
## income       0.474***  0.560***  0.644*** 
##               (0.029)   (0.028)   (0.023) 
##                                           
## Constant     95.484*** 81.482*** 62.397***
##              (21.392)  (19.251)  (16.305) 
##                                           
## ------------------------------------------
## Observations    235       235       235   
## ==========================================
## Note:          *p<0.1; **p<0.05; ***p<0.01

Finalnie, zaprezentujmy wyłącznie te 3 modele na wykresie:

Przykład 2.

Tutaj przeprowadzimy testy użycia pakietu quantreg, wykorzystując wbudowany zbiór danych “mtcars”. Zmienna “mpg” oznacza spalanie samochodów (mile/galon).

Zamodulejmy zależność regresyjną dla tej zmiennej od kilku predyktorów.

Najpierw oszacujmy regresję KMNK:

kmnk <- lm(mpg ~ disp + hp + factor(am) + factor(vs), data = mtcars)
summary(kmnk)
## 
## Call:
## lm(formula = mpg ~ disp + hp + factor(am) + factor(vs), data = mtcars)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -4.7981 -1.9532  0.0111  1.5665  5.6321 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 24.832119   2.890418   8.591 3.32e-09 ***
## disp        -0.008304   0.010087  -0.823  0.41757    
## hp          -0.037623   0.013846  -2.717  0.01135 *  
## factor(am)1  4.419257   1.493243   2.960  0.00634 ** 
## factor(vs)1  2.052472   1.627096   1.261  0.21794    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.812 on 27 degrees of freedom
## Multiple R-squared:  0.8104, Adjusted R-squared:  0.7823 
## F-statistic: 28.85 on 4 and 27 DF,  p-value: 2.13e-09

Teraz oszacujmy warunkowe regresje kwantylowe na różnych kwantylach, błąd standardowy uzyskany przez bootstrap.

Zauważ, że istnieje gradient we współczynnikach kwantylowych hp, jak również disp. Znak disp odwraca się, również współczynnik na czynniku am jest różny w zależności od kwantyli:

kwantyle <- c(0.25, 0.50, 0.75)
reg_kwantylowa <- rq(mpg ~ disp + hp + factor(am),tau = kwantyle,data = mtcars)
summary(reg_kwantylowa, se = "boot")
## 
## Call: rq(formula = mpg ~ disp + hp + factor(am), tau = kwantyle, data = mtcars)
## 
## tau: [1] 0.25
## 
## Coefficients:
##             Value    Std. Error t value  Pr(>|t|)
## (Intercept) 25.34665  1.51562   16.72360  0.00000
## disp        -0.02441  0.00853   -2.86281  0.00786
## hp          -0.01672  0.01542   -1.08383  0.28768
## factor(am)1  1.39719  1.38280    1.01041  0.32095
## 
## Call: rq(formula = mpg ~ disp + hp + factor(am), tau = kwantyle, data = mtcars)
## 
## tau: [1] 0.5
## 
## Coefficients:
##             Value    Std. Error t value  Pr(>|t|)
## (Intercept) 27.49722  1.65388   16.62588  0.00000
## disp        -0.02253  0.01620   -1.39028  0.17540
## hp          -0.02713  0.02352   -1.15343  0.25849
## factor(am)1  3.37328  1.97624    1.70692  0.09891
## 
## Call: rq(formula = mpg ~ disp + hp + factor(am), tau = kwantyle, data = mtcars)
## 
## tau: [1] 0.75
## 
## Coefficients:
##             Value    Std. Error t value  Pr(>|t|)
## (Intercept) 28.06384  1.94463   14.43146  0.00000
## disp         0.00445  0.01575    0.28265  0.77953
## hp          -0.06662  0.02054   -3.24387  0.00305
## factor(am)1  7.91402  2.47636    3.19582  0.00344

Testy współczynników

Użyjemy funkcji rq.anova z pakietu regresji kwantylowej, aby przeprowadzić test WALDA. Pamiętaj, że test WALDA mówi, że biorąc pod uwagę nieograniczone oszacowania modelu, przetestujemy hipotezę zerową mówiącą, że współczynniki spełniają pewne liniowe ograniczenia.

Aby ją przetestować, użyjemy obiektu zwróconego z uruchomienia rq z różnymi liczbami kwantyli i ustawimy opcję joint na true lub false. Gdy joint jest true: “równość współczynników kierunkowych powinna być wykonana jako wspólne testy na wszystkich parametrach nachylenia”, gdy joint jest false: “należy zgłaszać oddzielne testy na każdym z parametrów nachylenia”.

Zauważ, że testy kwantylowe są testami “linii równoległej”. Oznacza to, że powinniśmy wyjąć różne x-wyrazy_wolne dla każdego kwantyla, ponieważ reprezentują one poziomy rozkładów warunkowych. Jeśli jednak współczynniki kwantyli dla współczynnikow są takie same, to nie ma efektów specyficznych dla kwantyli, wystarczą efekty średnie.

Badanie statystycznej różnicy między 25. i 50. kwantylem warunkowym:

Biorąc pod uwagę powyższe oszacowania kwantyli, różnica między kwantylami 0,25 i 0,50 istnieje, ale czy są one wystarczająco duże, aby być statystycznie różne? Jaka jest wartość p? Przeglądając poniższe wyniki, nie są one statystycznie różne!

Po pierwsze, joint = TRUE. To nie jest testowanie, czy współczynnik na disp jest taki sam jak współczynnik na hp. To jest wspólne testowanie, czy współczynniki dla różnych kwantyli disp i różnych kwantyli hp są takie same dla każdej zmiennej.

kwantyle <- c(0.25, 0.50)
reg_kwantylowa <- rq(mpg ~ disp + hp + factor(am),tau = kwantyle, data = mtcars)
anova(reg_kwantylowa, test = "Wald", joint=TRUE)
## Quantile Regression Analysis of Deviance Table
## 
## Model: mpg ~ disp + hp + factor(am)
## Joint Test of Equality of Slopes: tau in {  0.25 0.5  }
## 
##   Df Resid Df F value Pr(>F)
## 1  3       61  0.8421 0.4761

Po drugie, joint = False:

anova(reg_kwantylowa, test = "Wald", joint=FALSE)
## Quantile Regression Analysis of Deviance Table
## 
## Model: mpg ~ disp + hp + factor(am)
## Tests of Equality of Distinct Slopes: tau in {  0.25 0.5  }
## 
##             Df Resid Df F value Pr(>F)
## disp         1       63  0.0305 0.8619
## hp           1       63  0.5461 0.4627
## factor(am)1  1       63  1.3500 0.2497

Badanie statystycznej różnicy między 25, 50 i 75 kwantylem warunkowym:

Pierwszy kwartyl i mediana nie wydają się być statystycznie różne, teraz dołączymy trzeci kwartyl. Jak widać wcześniej, kwartyle wspólnie wykazują gradient. Teraz możemy zobaczyć, że disp, hp i am są oddzielnie statystycznie różne.

Po pierwsze, joint = TRUE:

kwantyle <- c(0.25, 0.50, 0.75)

reg_kwantylowa <- rq(mpg ~ disp + hp + factor(am),tau = kwantyle, data = mtcars)

anova(reg_kwantylowa, test = "Wald", joint=TRUE)
## Quantile Regression Analysis of Deviance Table
## 
## Model: mpg ~ disp + hp + factor(am)
## Joint Test of Equality of Slopes: tau in {  0.25 0.5 0.75  }
## 
##   Df Resid Df F value   Pr(>F)   
## 1  6       90  3.3173 0.005367 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Po drugie, joint = False:

anova(reg_kwantylowa, test = "Wald", joint=FALSE)
## Quantile Regression Analysis of Deviance Table
## 
## Model: mpg ~ disp + hp + factor(am)
## Tests of Equality of Distinct Slopes: tau in {  0.25 0.5 0.75  }
## 
##             Df Resid Df F value   Pr(>F)   
## disp         2       94  5.4903 0.005558 **
## hp           2       94  6.7221 0.001868 **
## factor(am)1  2       94  7.2758 0.001154 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Dobroć dopasowania

Możemy obliczyć współczynniki dobroci dopasowania regresji kwantylowej z wykorzystaniem reszt i reszt bezwarunkowych:

# goodfit(resid, resid_nl, tau)

Miara dobroci dopasowania dla regresji kwantylowej jest szacowana jako 1 minus stosunek sumy odchyleń bezwzględnych w modelach w pełni sparametryzowanych do sumy odchyleń bezwzględnych w zerowym (bezwarunkowym) modelu kwantylowym.

Wartości te są przydatne do porównań między modelami kwantylowymi, ale nie są porównywalne ze standardowymi współczynnikami determinacji. Te ostatnie oparte są na wariancji odchyleń kwadratowych, natomiast wartości dobroci dopasowania dla regresji kwantylowej oparte są na odchyleniach bezwzględnych. Wartości dobroci dopasowania zawsze będą mniejsze niż wartości R2.

## model kwantylowy
model1 <- rq(mpg ~ disp + hp + factor(am),tau = 0.5, data = mtcars)
reszty1 <- resid(model1)

## bezwarunkowy (pusty) model kwantylowy
model2 <- rq(mpg ~ 1, tau = 0.5,data=mtcars)
reszty2 <- resid(model2)

goodfit(reszty1, reszty2, 0.5)
## [1] 0.5403311
## r2 modelu KMNK dla porównania
model_lm <- lm(mpg ~ disp + hp + factor(am), data = mtcars)

summary(model_lm)$r.squared
## [1] 0.7992061

Zadanie

Teraz Wasza kolej ;-)

Waszym zadaniem dzisiaj jest zamodelowanie - porównanie KMNK oraz regresji kwantylowej (różno-poziomowej) dla zmiennej “earnings” - wynagrodzenia.

Dobierz i przetestuj predyktory, kwantyle dla modeli. Wykonaj testy różnic współczynnikow dla finalnych modeli.

W przypadku problemów - obejrzyj video tutorial (włącz polskie napisy) oraz wejdź na jego stronę ze źródłami. Możesz również wykorzystać w/w przykłady.

data("CPSSW9298")
# ?CPSSW9298 

Dane 1992

dane<-CPSSW9298
dane_1992<-dane %>% filter(year==1992)
dane_1992 %>%
  ggplot(aes(age,earnings))+
  geom_point()

data(dane_1992) #dane 
## Warning in data(dane_1992): zbiór danych 'dane_1992' nie został znaleziony
p <- ggplot(data = dane_1992) +
    geom_point(mapping = aes(x = age, y = earnings), color = "blue")
taus <- c(0.20, 0.40, 0.60, 0.80, 0.95, 0.999)
fits <- data.frame(
    coef(lm(earnings ~ age, data = dane_1992)),
    sapply(taus, function(x) coef(rq(formula = earnings ~ age, data = dane_1992, tau = x))))
names(fits) <- c("OLS", sprintf("$\\tau_{%0.2f}$", taus))
nf <- ncol(fits)
colors <- colorRampPalette(colors = c("black", "red"))(nf)
p <- p + geom_abline(intercept = fits[1, 1], slope = fits[2, 1], color = colors[1], linewidth = 1.5)
for (i in seq_len(nf)[-1]) {
    p <- p + geom_abline(intercept = fits[1, i], slope = fits[2, i], color = colors[i])
}
p

data(dane_1992) #dane 
## Warning in data(dane_1992): zbiór danych 'dane_1992' nie został znaleziony
p <- ggplot(data = dane_1992) +
    geom_point(mapping = aes(x = gender, y = earnings), color = "blue")
taus <- c(0.20, 0.40, 0.60, 0.80, 0.95, 0.999)
fits <- data.frame(
    coef(lm(earnings ~ gender, data = dane_1992)),
    sapply(taus, function(x) coef(rq(formula = earnings ~ gender, data = dane_1992, tau = x))))
names(fits) <- c("OLS", sprintf("$\\tau_{%0.2f}$", taus))
nf <- ncol(fits)
colors <- colorRampPalette(colors = c("black", "red"))(nf)
p <- p + geom_abline(intercept = fits[1, 1], slope = fits[2, 1], color = colors[1], linewidth = 1.5)
for (i in seq_len(nf)[-1]) {
    p <- p + geom_abline(intercept = fits[1, i], slope = fits[2, i], color = colors[i])
}
p

q20 <- rq(earnings ~ age+degree+gender, data = dane_1992, tau = 0.20)
q40 <- rq(earnings ~ age+degree+gender, data = dane_1992, tau = 0.40)
q60 <- rq(earnings ~ age+degree+gender, data = dane_1992, tau = 0.60)
q80 <- rq(earnings ~ age+degree+gender, data = dane_1992, tau = 0.80)
q95 <- rq(earnings ~ age+degree+gender, data = dane_1992, tau = 0.95)
q999 <- rq(earnings ~ age+degree+gender, data = dane_1992, tau = 0.999)
# Tabela z porównaniem wyników trzech modeli: 

stargazer(q20, q40, q60, q80, q95, q999, title = "Wyniki regresji kwantylowych", type = "text")
## 
## Wyniki regresji kwantylowych
## =========================================================================
##                                   Dependent variable:                    
##                ----------------------------------------------------------
##                                         earnings                         
##                   (1)       (2)       (3)       (4)       (5)      (6)   
## -------------------------------------------------------------------------
## age            0.151***  0.256***  0.374***  0.451***  0.593***   0.901  
##                 (0.020)   (0.022)   (0.024)   (0.028)   (0.059)  (0.628) 
##                                                                          
## degreebachelor 3.136***  4.008***  4.690***  5.502***  6.506***   4.687  
##                 (0.135)   (0.144)   (0.154)   (0.189)   (0.362)  (3.598) 
##                                                                          
## genderfemale   -0.962*** -1.621*** -2.233*** -2.794*** -4.071***  -2.284 
##                 (0.112)   (0.124)   (0.138)   (0.158)   (0.335)  (3.555) 
##                                                                          
## Constant       2.225***   1.383**    0.214     0.918     1.923    7.812  
##                 (0.597)   (0.650)   (0.718)   (0.822)   (1.777)  (18.586)
##                                                                          
## -------------------------------------------------------------------------
## Observations     7,590     7,590     7,590     7,590     7,590    7,590  
## =========================================================================
## Note:                                         *p<0.1; **p<0.05; ***p<0.01

Interpretacja:

-Kobiety zarabiają średnio o 0,962 dolara za godzinę mniej niż mężczyźni (na podstawie pierwszego kwartylu).

-W wyższych dochodach płeć oraz wykształcenie mają jeszcze silniejszy wpływ na wysokość wynagrodzenia.

-Wyraz wolny w górnych kwartylach jest statystycznie nieistotny, co sugeruje, że warto rozważyć transformację dochodów, na przykład przez ich logarytmowanie, aby lepiej uchwycić zależności.

q20 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.20)
q40 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.40)
q60 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.60)
q80 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.80)
q95 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.95)
q999 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.999)
# Tabela z porównaniem wyników trzech modeli: 

stargazer(q20, q40, q60, q80, q95, q999, title = "Wyniki regresji kwantylowych", type = "text")
## 
## Wyniki regresji kwantylowych
## =========================================================================
##                                   Dependent variable:                    
##                ----------------------------------------------------------
##                                      log(earnings)                       
##                   (1)       (2)       (3)       (4)       (5)      (6)   
## -------------------------------------------------------------------------
## age            0.020***  0.027***  0.031***  0.032***  0.028***   0.024  
##                 (0.003)   (0.002)   (0.002)   (0.002)   (0.003)  (0.017) 
##                                                                          
## degreebachelor 0.402***  0.403***  0.376***  0.364***  0.323***   0.135  
##                 (0.017)   (0.013)   (0.012)   (0.012)   (0.017)  (0.098) 
##                                                                          
## genderfemale   -0.141*** -0.174*** -0.187*** -0.201*** -0.197***  -0.061 
##                 (0.016)   (0.013)   (0.012)   (0.012)   (0.017)  (0.094) 
##                                                                          
## Constant       1.300***  1.392***  1.484***  1.696***  2.121***  2.815***
##                 (0.086)   (0.070)   (0.062)   (0.065)   (0.093)  (0.504) 
##                                                                          
## -------------------------------------------------------------------------
## Observations     7,590     7,590     7,590     7,590     7,590    7,590  
## =========================================================================
## Note:                                         *p<0.1; **p<0.05; ***p<0.01

Wyraz wolny jest teraz istotny. Kwartyl na poziomie 0,999 okazał się nieistotny, dlatego decydujemy się na jego wykluczenie.

q20 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.20)
q40 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.40)
q60 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.60)
q80 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.80)
q95 <- rq(log(earnings) ~ age+degree+gender, data = dane_1992, tau = 0.95)

# Tabela z porównaniem wyników trzech modeli: 

stargazer(q20, q40, q60, q80, q95, title = "Wyniki regresji kwantylowych", type = "text")
## 
## Wyniki regresji kwantylowych
## ================================================================
##                               Dependent variable:               
##                -------------------------------------------------
##                                  log(earnings)                  
##                   (1)       (2)       (3)       (4)       (5)   
## ----------------------------------------------------------------
## age            0.020***  0.027***  0.031***  0.032***  0.028*** 
##                 (0.003)   (0.002)   (0.002)   (0.002)   (0.003) 
##                                                                 
## degreebachelor 0.402***  0.403***  0.376***  0.364***  0.323*** 
##                 (0.017)   (0.013)   (0.012)   (0.012)   (0.017) 
##                                                                 
## genderfemale   -0.141*** -0.174*** -0.187*** -0.201*** -0.197***
##                 (0.016)   (0.013)   (0.012)   (0.012)   (0.017) 
##                                                                 
## Constant       1.300***  1.392***  1.484***  1.696***  2.121*** 
##                 (0.086)   (0.070)   (0.062)   (0.065)   (0.093) 
##                                                                 
## ----------------------------------------------------------------
## Observations     7,590     7,590     7,590     7,590     7,590  
## ================================================================
## Note:                                *p<0.1; **p<0.05; ***p<0.01

Model KMNK

kmnk <- lm(log(earnings) ~ age+degree+gender, data = dane_1992)
summary(kmnk)
## 
## Call:
## lm(formula = log(earnings) ~ age + degree + gender, data = dane_1992)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.80573 -0.26371  0.02737  0.29516  1.53710 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)     1.486349   0.053186   27.95   <2e-16 ***
## age             0.026398   0.001764   14.97   <2e-16 ***
## degreebachelor  0.375044   0.010162   36.91   <2e-16 ***
## genderfemale   -0.167263   0.010009  -16.71   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4305 on 7586 degrees of freedom
## Multiple R-squared:  0.1872, Adjusted R-squared:  0.1869 
## F-statistic: 582.3 on 3 and 7586 DF,  p-value: < 2.2e-16

Model wykazuje niski współczynnik determinacji R^2, co wskazuje na słabe dopasowanie modelu do danych.

kwantyle <- c(0.20, 0.40, 0.60, 0.80, 0.95)
reg_kwantylowa <- rq(log(earnings) ~ age+degree+gender,tau = kwantyle, data = dane_1992)
summary(reg_kwantylowa, se = "boot")
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1992)
## 
## tau: [1] 0.2
## 
## Coefficients:
##                Value    Std. Error t value  Pr(>|t|)
## (Intercept)     1.30031  0.07573   17.16936  0.00000
## age             0.02018  0.00248    8.14165  0.00000
## degreebachelor  0.40238  0.01670   24.09849  0.00000
## genderfemale   -0.14129  0.01436   -9.83596  0.00000
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1992)
## 
## tau: [1] 0.4
## 
## Coefficients:
##                Value     Std. Error t value   Pr(>|t|) 
## (Intercept)      1.39213   0.06268   22.21106   0.00000
## age              0.02659   0.00202   13.17524   0.00000
## degreebachelor   0.40268   0.01080   37.27034   0.00000
## genderfemale    -0.17407   0.01149  -15.14400   0.00000
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1992)
## 
## tau: [1] 0.6
## 
## Coefficients:
##                Value     Std. Error t value   Pr(>|t|) 
## (Intercept)      1.48369   0.06263   23.68889   0.00000
## age              0.03119   0.00207   15.04158   0.00000
## degreebachelor   0.37644   0.01242   30.29884   0.00000
## genderfemale    -0.18712   0.01233  -15.17826   0.00000
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1992)
## 
## tau: [1] 0.8
## 
## Coefficients:
##                Value     Std. Error t value   Pr(>|t|) 
## (Intercept)      1.69618   0.06778   25.02535   0.00000
## age              0.03201   0.00223   14.36281   0.00000
## degreebachelor   0.36352   0.01081   33.62071   0.00000
## genderfemale    -0.20070   0.01203  -16.68646   0.00000
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1992)
## 
## tau: [1] 0.95
## 
## Coefficients:
##                Value     Std. Error t value   Pr(>|t|) 
## (Intercept)      2.12112   0.09912   21.39921   0.00000
## age              0.02807   0.00323    8.67976   0.00000
## degreebachelor   0.32269   0.02067   15.61489   0.00000
## genderfemale    -0.19652   0.01916  -10.25558   0.00000
kwantyle <- c(0.20, 0.40, 0.60, 0.80, 0.95)
reg_kwantylowa <- rq(log(earnings) ~ age+degree+gender,tau = kwantyle, data = dane_1992)
anova(reg_kwantylowa, test = "Wald", joint=TRUE)
## Quantile Regression Analysis of Deviance Table
## 
## Model: log(earnings) ~ age + degree + gender
## Joint Test of Equality of Slopes: tau in {  0.2 0.4 0.6 0.8 0.95  }
## 
##   Df Resid Df F value    Pr(>F)    
## 1 12    37938  4.1132 1.829e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Wartość p (1.829e-06) jest bardzo mała, co wskazuje na istotność statystyczną. Oznacza to, że istnieje statystycznie istotna różnica w współczynnikach nachylenia dla różnych kwantyli, tzn. współczynniki te nie są równe na poziomach kwantyli 0.2, 0.4, 0.6, 0.8 i 0.95.

Model kwantylowy

## model kwantylowy
model1 <- rq(log(earnings) ~ age+degree+gender, tau = 0.80, data = dane_1992)
reszty1 <- resid(model1)

## bezwarunkowy (pusty) model kwantylowy
model2 <- rq(log(earnings) ~ 1, tau = 0.80, data = dane_1992)
reszty2 <- resid(model2)

goodfit(reszty1, reszty2, 0.80)
## [1] 0.1256738
## r2 modelu KMNK dla porównania
model_lm <- lm(log(earnings) ~ age+degree+gender, data = dane_1992)

summary(model_lm)$r.squared
## [1] 0.1871756

Wartość 0.1256738 modelu kwantylowego jest relatywnie niska, co sugeruje, że dopasowanie modelu kwantylowego nie jest idealne, ale nadal może być użyteczne. Niższa wartość wskazuje na to, że model kwantylowy nie poprawił się znacząco w porównaniu do modelu pustego. Warto jednak zaznaczyć, że model kwantylowy lepiej radzi sobie z nierówną zmiennością w różnych częściach danych, a model liniowy zapewnia tylko jedno ogólne dopasowanie.

Dane 1998

dane_1998<-dane %>% filter(year==1998)
dane_1998 %>%
  ggplot(aes(age,earnings))+
  geom_point()

data(dane_1998) #dane 
## Warning in data(dane_1998): zbiór danych 'dane_1998' nie został znaleziony
p <- ggplot(data = dane_1998) +
    geom_point(mapping = aes(x = age, y = earnings), color = "blue")
taus <- c(0.20, 0.40, 0.60, 0.80, 0.90, 0.95, 0.99)
fits <- data.frame(
    coef(lm(earnings ~ age, data = dane_1992)),
    sapply(taus, function(x) coef(rq(formula = earnings ~ age, data = dane_1998, tau = x))))
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
names(fits) <- c("OLS", sprintf("$\\tau_{%0.2f}$", taus))
nf <- ncol(fits)
colors <- colorRampPalette(colors = c("black", "red"))(nf)
p <- p + geom_abline(intercept = fits[1, 1], slope = fits[2, 1], color = colors[1], linewidth = 1.5)
for (i in seq_len(nf)[-1]) {
    p <- p + geom_abline(intercept = fits[1, i], slope = fits[2, i], color = colors[i])
}
p

Tutaj zdecydowaliśmy się na dodanie kwantyla 0.99, aby uchwycić więcej informacji z danych i lepiej zrozumieć zależności w najwyższych poziomach rozkładu dochodów.

q20_98 <- rq(earnings ~ age+degree+gender, data = dane_1998, tau = 0.20)
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
q40_98 <- rq(earnings ~ age+degree+gender, data = dane_1998, tau = 0.40)
q60_98 <- rq(earnings ~ age+degree+gender, data = dane_1998, tau = 0.60)
q80_98 <- rq(earnings ~ age+degree+gender, data = dane_1998, tau = 0.80)
q90_98 <- rq(earnings ~ age+degree+gender, data = dane_1998, tau = 0.90)
q95_98 <- rq(earnings ~ age+degree+gender, data = dane_1998, tau = 0.95)
q99_98 <- rq(earnings ~ age+degree+gender, data = dane_1998, tau = 0.99)
# Tabela z porównaniem wyników trzech modeli: 

stargazer(q20_98, q40_98, q60_98, q80_98, q90_98, q95_98,q99_98, title = "Wyniki regresji kwantylowych", type = "text")
## 
## Wyniki regresji kwantylowych
## ====================================================================================
##                                         Dependent variable:                         
##                ---------------------------------------------------------------------
##                                              earnings                               
##                   (1)       (2)       (3)       (4)       (5)       (6)       (7)   
## ------------------------------------------------------------------------------------
## age            0.160***  0.253***  0.361***  0.454***  0.481***  0.466***    0.310  
##                 (0.024)   (0.029)   (0.033)   (0.046)   (0.069)   (0.087)   (0.208) 
##                                                                                     
## degreebachelor 3.526***  4.432***  5.311***  6.867***  8.761***  10.096*** 12.392***
##                 (0.142)   (0.171)   (0.192)   (0.281)   (0.419)   (0.527)   (1.104) 
##                                                                                     
## genderfemale   -1.442*** -1.976*** -2.671*** -3.633*** -4.434*** -4.677*** -5.706***
##                 (0.133)   (0.162)   (0.183)   (0.258)   (0.388)   (0.485)   (1.105) 
##                                                                                     
## Constant       3.205***  2.972***   2.408**   3.351**  5.769***  9.101***  20.755***
##                 (0.693)   (0.846)   (0.958)   (1.366)   (2.032)   (2.665)   (6.516) 
##                                                                                     
## ------------------------------------------------------------------------------------
## Observations     5,911     5,911     5,911     5,911     5,911     5,911     5,911  
## ====================================================================================
## Note:                                                    *p<0.1; **p<0.05; ***p<0.01

Wiek (age):

Wpływ wieku na wynagrodzenie rośnie w wyższych kwantylach dochodów. Na przykład, dla 20. percentyla wzrost wieku o jedną jednostkę zwiększa dochód średnio o 0.160, podczas gdy dla 80. percentyla ten wzrost wynosi już 0.454. W górnych kwantylach (np. 95. percentyl) efekt wieku pozostaje istotny statystycznie, ale nie zmienia się znacząco w stosunku do 90. percentyla. Jednak dla 99. percentyla efekt jest słabszy i staje się nieistotny.

Wykształcenie (degreebachelor):

Efekt posiadania stopnia licencjata jest wyraźny i rośnie w miarę wzrostu kwantyla. Na przykład, dla 20. percentyla wykształcenie zwiększa dochód średnio o 3.526 jednostek, podczas gdy dla 95. percentyla efekt wynosi już 10.096 jednostek, a dla 99. percentyla aż 12.392 jednostek. Wskazuje to, że w wyższych dochodach wykształcenie odgrywa coraz większą rolę, co sugeruje, że wykształcenie jest kluczowym czynnikiem w uzyskaniu wysokich dochodów.

Płeć (genderfemale):

Efekt płci (bycia kobietą) jest negatywny i wzrasta wraz z kwantylem, co oznacza, że różnica w wynagrodzeniu między kobietami a mężczyznami staje się większa w wyższych dochodach. Dla 20. percentyla kobiety zarabiają średnio o 1.442 jednostki mniej niż mężczyźni, ale w 95. percentylu różnica ta wynosi już 4.677 jednostek, a w 99. percentylu (model 7) aż 5.706 jednostek. Sugeruje to, że luka płacowa między kobietami a mężczyznami jest szczególnie wyraźna w najwyższych poziomach dochodów. Co istotne, luka płciowa (różnice w dochodach między kobietami a mężczyznami) jest bardziej widoczna w danych z 1998 roku niż w danych z 1992 roku, co może wskazywać na nasilające się nierówności w wynagrodzeniach w tym okresie.

Model KMNK

kmnk_1998 <- lm(log(earnings) ~ age+degree+gender, data = dane_1998)
summary(kmnk_1998)
## 
## Call:
## lm(formula = log(earnings) ~ age + degree + gender, data = dane_1998)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.96583 -0.27644  0.02536  0.30209  1.50215 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)     1.78845    0.06230   28.71   <2e-16 ***
## age             0.02142    0.00207   10.35   <2e-16 ***
## degreebachelor  0.38277    0.01173   32.64   <2e-16 ***
## genderfemale   -0.18003    0.01182  -15.23   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4457 on 5907 degrees of freedom
## Multiple R-squared:  0.1828, Adjusted R-squared:  0.1824 
## F-statistic: 440.5 on 3 and 5907 DF,  p-value: < 2.2e-16

Powyższy model, podobnie jak w przypadku modelu z danymi z 1992 roku, wykazuje niski poziom R^2, zatem model słabo dopasowuje się do danych.

kwantyle_1998 <- c(0.20, 0.40, 0.60, 0.80, 0.90, 0.95, 0.99)
reg_kwantylowa_1998 <- rq(log(earnings) ~ age+degree+gender,tau = kwantyle, data = dane_1998)
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
summary(reg_kwantylowa_1998, se = "boot")
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1998)
## 
## tau: [1] 0.2
## 
## Coefficients:
##                Value     Std. Error t value   Pr(>|t|) 
## (Intercept)      1.50442   0.08728   17.23633   0.00000
## age              0.01843   0.00301    6.12589   0.00000
## degreebachelor   0.41255   0.01453   28.38607   0.00000
## genderfemale    -0.16917   0.01648  -10.26764   0.00000
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1998)
## 
## tau: [1] 0.4
## 
## Coefficients:
##                Value     Std. Error t value   Pr(>|t|) 
## (Intercept)      1.66252   0.07218   23.03171   0.00000
## age              0.02255   0.00241    9.35761   0.00000
## degreebachelor   0.39379   0.01338   29.42057   0.00000
## genderfemale    -0.17863   0.01224  -14.59643   0.00000
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1998)
## 
## tau: [1] 0.6
## 
## Coefficients:
##                Value     Std. Error t value   Pr(>|t|) 
## (Intercept)      1.78327   0.06705   26.59740   0.00000
## age              0.02605   0.00225   11.59740   0.00000
## degreebachelor   0.37942   0.01340   28.31883   0.00000
## genderfemale    -0.19710   0.01360  -14.48759   0.00000
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1998)
## 
## tau: [1] 0.8
## 
## Coefficients:
##                Value     Std. Error t value   Pr(>|t|) 
## (Intercept)      2.04254   0.07741   26.38640   0.00000
## age              0.02569   0.00253   10.16456   0.00000
## degreebachelor   0.37692   0.01461   25.80463   0.00000
## genderfemale    -0.21061   0.01590  -13.24672   0.00000
## 
## Call: rq(formula = log(earnings) ~ age + degree + gender, tau = kwantyle, 
##     data = dane_1998)
## 
## tau: [1] 0.95
## 
## Coefficients:
##                Value    Std. Error t value  Pr(>|t|)
## (Intercept)     2.55322  0.14726   17.33780  0.00000
## age             0.01898  0.00472    4.02407  0.00006
## degreebachelor  0.40561  0.02137   18.98149  0.00000
## genderfemale   -0.19263  0.02062   -9.34298  0.00000
reg_kwantylowa_1998 <- rq(log(earnings) ~ age+degree+gender,tau = kwantyle_1998, data = dane_1998)
## Warning in rq.fit.br(x, y, tau = tau, ...): Solution may be nonunique
anova(reg_kwantylowa_1998, test = "Wald", joint=TRUE)
## Quantile Regression Analysis of Deviance Table
## 
## Model: log(earnings) ~ age + degree + gender
## Joint Test of Equality of Slopes: tau in {  0.2 0.4 0.6 0.8 0.9 0.95 0.99  }
## 
##   Df Resid Df F value   Pr(>F)   
## 1 18    41359  2.0464 0.005521 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Wartość p jest mniejsza niż 0.01, co wskazuje na istotność statystyczną na poziomie 1%. Oznacza to, że istnieje statystycznie istotna różnica w nachyleniach (współczynnikach) dla różnych kwantyli w zakresie {0.2,0.4,0.6,0.8,0.9,0.95,0.99}.

Model kwantylowy

## model kwantylowy
model1_1998 <- rq(log(earnings) ~ age+degree+gender, tau = 0.70, data = dane_1998)
reszty1_1998 <- resid(model1_1998)

## bezwarunkowy (pusty) model kwantylowy
model2_1998 <- rq(log(earnings) ~ 1, tau = 0.70, data = dane_1998)
reszty2_1998 <- resid(model2_1998)

goodfit(reszty1_1998, reszty2_1998, 0.70)
## [1] 0.1116213
## r2 modelu KMNK dla porównania
model_lm_1998 <- lm(log(earnings) ~ age+degree+gender, data = dane_1998)

summary(model_lm_1998)$r.squared
## [1] 0.1828058

Wartość 0.1116213 dla miary goodfit w modelu kwantylowym (tau = 0.70) jest relatywnie niska. Oznacza to, że dopasowanie modelu kwantylowego w porównaniu do modelu pustego (bez zmiennych objaśniających) nie jest idealne, ale nadal może być uznane za użyteczne.

Niższa wartość goodfit sugeruje, że model kwantylowy nie wprowadza dużego wyjaśnienia zmienności w danych w porównaniu do modelu pustego. Jednak istotą modelu kwantylowego jest to, że koncentruje się on na specyficznej części rozkładu, co czyni go bardziej odpowiednim do analizy rozkładu dochodów niż klasyczny model liniowy.

Wartość R^2 wynosi 0.1828058, co oznacza, że model liniowy wyjaśnia około 18.28% zmienności logarytmów dochodów w danych. Mimo że R^2 jest wyższe niż wartość goodfit w modelu kwantylowym, model liniowy zakłada jednolitą zależność między zmiennymi objaśniającymi a zmienną zależną w całym rozkładzie danych, co może nie odzwierciedlać różnic w różnych częściach rozkładu.

Wnioski:

Model kwantylowy może być bardziej użyteczny, gdy interesuje nas analiza specyficznej części rozkładu, ponieważ pokazuje, jak zmienne objaśniające wpływają na dochody w tym konkretnym kwantylu. Model liniowy dostarcza bardziej ogólnej analizy, wyjaśniając globalną zmienność w danych, ale może nie oddawać precyzyjnie różnic w zależnościach między zmiennymi w różnych częściach rozkładu dochodów.

LS0tDQp0aXRsZTogJ05pZWtsYXN5Y3puZSBtZXRvZHkgc3RhdHlzdHlraScNCnN1YnRpdGxlOiAnUmVncmVzamEga3dhbnR5bG93YScNCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCmF1dGhvcjogIldlcm9uaWthIE5pZHpnb3Jza2EsIERhd2lkIE5hdXMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBmb250c2l6ZTogMTBwdA0KICAgIHRvYzogeWVzDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KICAgIGRmX3ByaW50OiBkZWZhdWx0DQogICAgdG9jX2RlcHRoOiA1DQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCmBgYHtyIHByZXJlcXMsIG1lc3NhZ2UgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCAgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoQ1ZYUikNCmxpYnJhcnkoQUVSKQ0KbGlicmFyeShzdGFyZ2F6ZXIpDQpsaWJyYXJ5KFdSVERTdGlkYWwpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkocXVhbnRyZWcpDQpsaWJyYXJ5KFBvZ3JvbWN5RGFueWNoKQ0KYGBgDQoNCiMjIERsYWN6ZWdvIGt3YW50eWxvd2E/DQoNCkRsYWN6ZWdvIHBvdHJ6ZWJ1amVteSByZWdyZXNqaSBrd2FudHlsb3dlaiAoUVIpPw0KDQpXIHN6Y3plZ8OzbG5vxZtjaSwgUVI6DQoNCi0gICBqZXN0IG9kcG9ybmEgbmEgcHVua3R5IG9kc3RhasSFY2UgaSB3cMWCeXdvd2UNCg0KLSAgIG5pZSB6YWvFgmFkYSBzdGHFgmVqIHdhcmlhbmNqaSAoem5hbmVqIGpha28gaG9tb3NrZWRhc3R5Y3pub8WbxIcpIGRsYQ0KICAgIHptaWVubmVqIG9kcG93aWVkemkgbHViIHJlc3p0DQoNCi0gICBuaWUgemFrxYJhZGEgbm9ybWFsbm/Fm2NpIGFsZSBnxYLDs3duxIUgemFsZXTEhSBRUiB3IHBvcsOzd25hbml1IHogcmVncmVzasSFDQogICAgbGluaW93xIUgKExSKSBqZXN0IHRvLCDFvGUgUVIgYmFkYSByw7PFvG5lIHdhcnRvxZtjaSB6bWllbm5laiBvZHBvd2llZHppLA0KICAgIGEgbmllIHR5bGtvIMWbcmVkbmnEhSwgaSBkb3N0YXJjemEgdyB6d2nEhXprdSB6IHR5bSBwZcWCbmllanN6ZWdvIG9icmF6dQ0KICAgIHp3acSFemvDs3cgbWnEmWR6eSB6bWllbm55bWkhDQoNCiMjIFdwcm93YWR6ZW5pZQ0KDQpSZWdyZXNqYSBrd2FudHlsb3dhIChhbmcuIHF1YW50aWxlIHJlZ3Jlc3Npb24pIHpvc3RhxYJhIHphcHJvcG9ub3dhbmENCnByemV6IEtvZW5rZXJhIGkgQmFzc2V0dGEgKDE5NzgpLiBTemN6ZWfDs2xueSBwcnp5cGFkZWsgcmVncmVzamkNCmt3YW50eWxvd2VqIGRsYSBrd2FudHlsYSByesSZZHUgMCw1IChjenlsaSBtZWRpYW55KSBqZXN0IHLDs3dub3dhxbxueQ0KZXN0eW1hdG9yb3dpIExBRCAoYW5nLiBMZWFzdCBBYnNvbHV0ZSBEZXZpYXRpb24pIC0tIG1pbmltYWxpenVqZSBzdW3EmQ0KYmV6d3pnbMSZZG55Y2ggYsWCxJlkw7N3LlwNCldwcm93YWR6ZW5pZSByw7PFvG55Y2gga3dhbnR5bGkgcmVncmVzamkgZGFqZSBwZcWCbmllanN6eSBvcGlzIHJvemvFgmFkw7N3DQp3YXJ1bmtvd3ljaCB6d8WCYXN6Y3phIHcgcHJ6eXBhZGt1IHJvemvFgmFkw7N3IGFzeW1ldHJ5Y3pueWNoIGx1YiB1Y2nEmXR5Y2guDQoNClJlZ3Jlc2phIGt3YW50eWxvd2EgamVzdCBrb2xlam7EhSB3YXJpYWNqxIUgbmEgdGVtYXQgbmFqbW5pZWpzenljaA0Ka3dhZHJhdMOzdyBcY2l0ZXB7cXVhbnRpbGV9LiBTdHJhdMSFIGplc3Qgd3Nww7PFgmN6eW5uaWsgJGxfMSQgZnVua2NqaToNCg0KJCQNCiAgICBccGhpKHUpID0gXHRhdVxtYXgodSwwKSAtICgxLVx0YXUpXG1heCgtdSwwKSA9IFxmcmFjezF9ezJ9fHV8ICsgXGxlZnQoXHRhdSAtIFxmcmFjezF9ezJ9XHJpZ2h0KXUsDQokJA0KDQpnZHppZSAkXHRhdSBcaW4gKDAsMSkkIG96bmFjemEga29ua3JldG55IGt3YW50eWwuIFByb2JsZW1lbSBqYWsNCnBvcHJ6ZWRuaW8gamVzdCBtaW5pbWFsaXphY2phIGNhxYJrb3dpdGVqIHN0cmF0eSByZXN6dG93ZWouIE1vZGVsIHRlbg0KamVzdCBwb3dzemVjaG5pZSBzdG9zb3dhbnkgdyBla29sb2dpaSwgb2Nocm9uaWUgemRyb3dpYSBpIGlubnljaA0KZHppZWR6aW5hY2gsIGdkemllIHNhbWEgxZtyZWRuaWEgbmllIHd5c3RhcmN6YSBkbyB1Y2h3eWNlbmlhIHrFgm/FvG9ueWNoDQp6YWxlxbxub8WbY2kgbWnEmWR6eSB6bWllbm55bWkuDQoNCiMjIFd5bWFnYW5pYQ0KDQpXeW1hZ2FuYSBqZXN0IGplZG5hIGxpY3pib3dhIHptaWVubmEgemFsZcW8bmEuIFptaWVubmEgcHJ6ZXdpZHl3YW5hIG11c2kNCmJ5xIcgem1pZW5uxIUgaWxvxZtjaW93xIUuIFByZWR5a3RvcnkgbW9nxIUgYnnEhyB6bWllbm55bWkgaWxvxZtjaW93eW1pIGx1Yg0Kc3p0dWN6bnltaSB6bWllbm55bWkgdyBwcnp5cGFka3UgcHJlZHlrdG9yw7N3IGpha2/Fm2Npb3d5Y2guIEFieSBtb8W8bmENCmJ5xYJvIHVydWNob21pxIcgYW5hbGl6xJksIHd5bWFnYW55IGplc3Qgd3lyYXogd29sbnkgbHViIGNvIG5ham1uaWVqIGplZGVuDQpwcmVkeWt0b3IuDQoNClJlZ3Jlc2phIGt3YW50eWxvd2EgbmllIGN6eW5pIHphxYJvxbxlxYQgZG90eWN6xIVjeWNoIHJvemvFgmFkdSB6bWllbm5lag0KcHJ6ZXdpZHl3YW5laiBpIGplc3Qgb2Rwb3JuYSBuYSB3cMWCeXcgb2JzZXJ3YWNqaSBvZHN0YWrEhWN5Y2guDQoNCkFuYWxpemEga3dhbnR5bG93YSBqZXN0IHBva3Jld25hIHJlZ3Jlc2ppIG1ldG9kxIUgbmFqbW5pZWpzenljaA0Ka3dhZHJhdMOzdy4NCg0KIyMgUHJ6eWvFgmFkIDEuDQoNCld5a29yenlzdGFteSBwcnp5a8WCYWQgeiBwYWtpZXR1IHF1YW50cmVnLg0KDQpKYWtpIGplc3QgendpxIV6ZWsgbWnEmWR6eSBjYcWCa293aXR5bSBkb2Nob2RlbSBnb3Nwb2RhcnN0d2EgZG9tb3dlZ28gYQ0Kb2RzZXRraWVtIGRvY2hvZMOzdyB3eWRhdGtvd2FueWNoIG5hIMW8eXdub8WbxIc/IFByYXdvIEVuZ2VsYSB3IGVrb25vbWlpDQpnxYJvc2ksIMW8ZSB3IG1pYXLEmSB3enJvc3R1IGRvY2hvZMOzdywgY3rEmcWbxIcgZG9jaG9kw7N3IHd5ZGF0a293YW55Y2ggbmENCsW8eXdub8WbxIcgc3BhZGEsIG5hd2V0IGplxZtsaSB3eWRhdGtpIG5hIMW8eXdub8WbxIcgYmV6d3pnbMSZZG5pZSByb3NuxIUuDQpTdG9zdWrEhWMgcmVncmVzasSZIGt3YW50eWxvd8SFIGRvIHR5Y2ggZGFueWNoLCBtb8W8bmEgb2tyZcWbbGnEhywgamFraWUNCnd5ZGF0a2kgbmEgxbx5d25vxZvEhyBwb25vc2kgOTAlIHJvZHppbiAoZGxhIDEwMCByb2R6aW4geiBkYW55bSBkb2Nob2RlbSksDQpnZHkgbmllIGludGVyZXN1asSFIG5hcyDFm3JlZG5pZSB3eWRhdGtpIG5hIMW8eXdub8WbxIcuDQoNCkRhbmUsIGt0w7NyZSB3eWtvcnp5c3RhbXkgLSB0byB6YmnDs3IgImVuZ2VsIiAtIGRhbmUgZG90eWN6xIVjZSB3eWRhdGvDs3cgbmENCsW8eXdub8WbxIcuIEplc3QgdG8gemJpw7NyIGRhbnljaCByZWdyZXN5am55Y2ggc2vFgmFkYWrEhWN5IHNpxJkgeiAyMzUNCm9ic2Vyd2FjamkgZG90eWN6xIVjeWNoIGRvY2hvZMOzdyBpIHd5ZGF0a8OzdyBuYSDFvHl3bm/Fm8SHIGRsYSBiZWxnaWpza2ljaA0KZ29zcG9kYXJzdHcgZG9tb3d5Y2gga2xhc3kgcm9ib3RuaWN6ZWouDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpkYXRhKGVuZ2VsKSAjZGFuZSANCnAgPC0gZ2dwbG90KGRhdGEgPSBlbmdlbCkgKw0KICAgIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gaW5jb21lLCB5ID0gZm9vZGV4cCksIGNvbG9yID0gImJsdWUiKQ0KdGF1cyA8LSBjKDAuMSwgMC4yNSwgMC41LCAwLjc1LCAwLjkwLCAwLjk1KQ0KZml0cyA8LSBkYXRhLmZyYW1lKA0KICAgIGNvZWYobG0oZm9vZGV4cCB+IGluY29tZSwgZGF0YSA9IGVuZ2VsKSksDQogICAgc2FwcGx5KHRhdXMsIGZ1bmN0aW9uKHgpIGNvZWYocnEoZm9ybXVsYSA9IGZvb2RleHAgfiBpbmNvbWUsIGRhdGEgPSBlbmdlbCwgdGF1ID0geCkpKSkNCm5hbWVzKGZpdHMpIDwtIGMoIk9MUyIsIHNwcmludGYoIiRcXHRhdV97JTAuMmZ9JCIsIHRhdXMpKQ0KbmYgPC0gbmNvbChmaXRzKQ0KY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmxhY2siLCAicmVkIikpKG5mKQ0KcCA8LSBwICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gZml0c1sxLCAxXSwgc2xvcGUgPSBmaXRzWzIsIDFdLCBjb2xvciA9IGNvbG9yc1sxXSwgbGluZXdpZHRoID0gMS41KQ0KZm9yIChpIGluIHNlcV9sZW4obmYpWy0xXSkgew0KICAgIHAgPC0gcCArIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IGZpdHNbMSwgaV0sIHNsb3BlID0gZml0c1syLCBpXSwgY29sb3IgPSBjb2xvcnNbaV0pDQp9DQpwDQpgYGANCg0KUG93ecW8c3p5IHd5a3JlcyBwcnplZHN0YXdpYSBkb3Bhc293YW5pZSByZWdyZXNqaSBrd2FudHlsb3dlaiBkbGENCiRcdGF1ID0gKDAuMSwgMC4yNSwgMC41LCAwLjc1LCAwLjkwLCAwLjk1KSQuIERvcGFzb3dhbmllIEtNTksgdG8gZ3J1YmENCmN6YXJuYSBsaW5pYS4NCg0KUG9uacW8ZWogem5hamR1amUgc2nEmSB0YWJlbGEgeiBvc3phY293YW55bWkgd3Nww7PFgmN6eW5uaWthbWkuDQoNCmBgYHtyfQ0Ka25pdHI6OmthYmxlKGZpdHMsIGZvcm1hdCA9ICJodG1sIiwgY2FwdGlvbiA9ICJPc3phY293YW5pYSB6IEtNTksgb3JheiBgcXVhbnRyZWdgIikgJT4lDQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIpICU+JQ0KICAgIGNvbHVtbl9zcGVjKDE6OCwgYmFja2dyb3VuZCA9ICIjZWNlY2VjIikNCmBgYA0KDQpPaywgbW/FvGVteSB0byB6cm9iacSHIGJhcmR6aWVqIHByemVqcnp5xZtjaWUgaSBzZm9ybWF0b3dhxIcgdyDFgmFkbmVqIHRhYmVsaQ0Kd3luaWvDs3c6DQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PVRSVUV9DQpxMjUgPC0gcnEoZm9vZGV4cCB+IGluY29tZSwgZGF0YSA9IGVuZ2VsLCB0YXUgPSAwLjI1KQ0KcTUwIDwtIHJxKGZvb2RleHAgfiBpbmNvbWUsIGRhdGEgPSBlbmdlbCwgdGF1ID0gMC41MCkNCnE3NSA8LSBycShmb29kZXhwIH4gaW5jb21lLCBkYXRhID0gZW5nZWwsIHRhdSA9IDAuNzUpDQoNCiMgVGFiZWxhIHogcG9yw7N3bmFuaWVtIHd5bmlrw7N3IHRyemVjaCBtb2RlbGk6IA0KDQpzdGFyZ2F6ZXIocTI1LCBxNTAsIHE3NSwgdGl0bGUgPSAiV3luaWtpIHJlZ3Jlc2ppIGt3YW50eWxvd3ljaCIsIHR5cGUgPSAidGV4dCIpDQpgYGANCg0KRmluYWxuaWUsIHphcHJlemVudHVqbXkgd3nFgsSFY3puaWUgdGUgMyBtb2RlbGUgbmEgd3lrcmVzaWU6DQoNCmBgYHtyIGVjaG89RkFMU0V9DQpteV9xciA8LSBycShmb29kZXhwIH4gaW5jb21lLCBkYXRhID0gZW5nZWwsIHRhdSA9IHNlcSgwLjI1LCAwLjc1LCAwLjI1KSkNCg0KaW50ZXJjZXB0X3Nsb3BlIDwtIG15X3FyICU+JSANCiAgY29lZigpICU+JSANCiAgdCgpICU+JSANCiAgZGF0YS5mcmFtZSgpICU+JSANCiAgcmVuYW1lKGludGVyY2VwdCA9IFguSW50ZXJjZXB0Liwgc2xvcGUgPSBpbmNvbWUpICU+JSANCiAgbXV0YXRlKHF1YW50aWxlID0gcm93Lm5hbWVzKC4pKQ0KDQpnZ3Bsb3QoKSArIA0KICBnZW9tX3BvaW50KGRhdGEgPSBlbmdlbCwgYWVzKGluY29tZSwgZm9vZGV4cCksIGFscGhhID0gMC41KSArIA0KICBnZW9tX2FibGluZShkYXRhID0gaW50ZXJjZXB0X3Nsb3BlLCBhZXMoaW50ZXJjZXB0ID0gaW50ZXJjZXB0LCBzbG9wZSA9IHNsb3BlLCBjb2xvciA9IHF1YW50aWxlKSkgKyANCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIGxhYnMoeCA9ICJEb2Now7NkIiwgeSA9ICJXeWRhdGtpIG5hIMW8eXdub8WbxIciLCB0aXRsZSA9ICJSZWdyZXNqZSBrd2FudHlsb3dlIHogdGF1ID0gMC4yNSwgMC41MCBvcmF6IDAuNzUiLCANCiAgICAgICBjYXB0aW9uID0gIsW5csOzZMWCbyBkYW55Y2g6IEtvZW5rZXIgYW5kIEJhc3NldHQgKDE5ODIpIikNCmBgYA0KDQojIyBQcnp5a8WCYWQgMi4NCg0KVHV0YWogcHJ6ZXByb3dhZHppbXkgdGVzdHkgdcW8eWNpYSBwYWtpZXR1IHF1YW50cmVnLCB3eWtvcnp5c3R1asSFYw0Kd2J1ZG93YW55IHpiacOzciBkYW55Y2ggIioqbXRjYXJzKioiLiBabWllbm5hICIqKm1wZyoqIiBvem5hY3phIHNwYWxhbmllDQpzYW1vY2hvZMOzdyAoKm1pbGUvZ2Fsb24qKS4NCg0KWmFtb2R1bGVqbXkgemFsZcW8bm/Fm8SHIHJlZ3Jlc3lqbsSFIGRsYSB0ZWogem1pZW5uZWogb2Qga2lsa3UgcHJlZHlrdG9yw7N3Lg0KDQpOYWpwaWVydyBvc3phY3VqbXkgcmVncmVzasSZIEtNTks6DQoNCmBgYHtyfQ0Ka21uayA8LSBsbShtcGcgfiBkaXNwICsgaHAgKyBmYWN0b3IoYW0pICsgZmFjdG9yKHZzKSwgZGF0YSA9IG10Y2FycykNCnN1bW1hcnkoa21uaykNCmBgYA0KDQpUZXJheiBvc3phY3VqbXkgd2FydW5rb3dlIHJlZ3Jlc2plIGt3YW50eWxvd2UgbmEgcsOzxbxueWNoIGt3YW50eWxhY2gsDQpixYLEhWQgc3RhbmRhcmRvd3kgdXp5c2thbnkgcHJ6ZXogKioqYm9vdHN0cmFwKioqLg0KDQpaYXV3YcW8LCDFvGUgaXN0bmllamUgZ3JhZGllbnQgd2Ugd3Nww7PFgmN6eW5uaWthY2gga3dhbnR5bG93eWNoICoqaHAqKiwgamFrDQpyw7N3bmllxbwgKipkaXNwKiouIFpuYWsgKipkaXNwKiogb2R3cmFjYSBzacSZLCByw7N3bmllxbwgd3Nww7PFgmN6eW5uaWsgbmENCmN6eW5uaWt1ICoqYW0qKiBqZXN0IHLDs8W8bnkgdyB6YWxlxbxub8WbY2kgb2Qga3dhbnR5bGk6DQoNCmBgYHtyfQ0Ka3dhbnR5bGUgPC0gYygwLjI1LCAwLjUwLCAwLjc1KQ0KcmVnX2t3YW50eWxvd2EgPC0gcnEobXBnIH4gZGlzcCArIGhwICsgZmFjdG9yKGFtKSx0YXUgPSBrd2FudHlsZSxkYXRhID0gbXRjYXJzKQ0Kc3VtbWFyeShyZWdfa3dhbnR5bG93YSwgc2UgPSAiYm9vdCIpDQpgYGANCg0KIyMjIFRlc3R5IHdzcMOzxYJjenlubmlrw7N3DQoNClXFvHlqZW15IGZ1bmtjamkgcnEuYW5vdmEgeiBwYWtpZXR1IHJlZ3Jlc2ppIGt3YW50eWxvd2VqLCBhYnkNCnByemVwcm93YWR6acSHIHRlc3QgV0FMREEuIFBhbWnEmXRhaiwgxbxlIHRlc3QgV0FMREEgbcOzd2ksIMW8ZSBiaW9yxIVjIHBvZA0KdXdhZ8SZIG5pZW9ncmFuaWN6b25lIG9zemFjb3dhbmlhIG1vZGVsdSwgcHJ6ZXRlc3R1amVteSBoaXBvdGV6xJkgemVyb3fEhQ0KbcOzd2nEhWPEhSwgxbxlIHdzcMOzxYJjenlubmlraSBzcGXFgm5pYWrEhSBwZXduZSBsaW5pb3dlIG9ncmFuaWN6ZW5pYS4NCg0KQWJ5IGrEhSBwcnpldGVzdG93YcSHLCB1xbx5amVteSBvYmlla3R1IHp3csOzY29uZWdvIHogdXJ1Y2hvbWllbmlhICoqKnJxKioqDQp6IHLDs8W8bnltaSBsaWN6YmFtaSBrd2FudHlsaSBpIHVzdGF3aW15IG9wY2rEmSAqKipqb2ludCoqKiBuYSB0cnVlIGx1Yg0KZmFsc2UuIEdkeSAqKipqb2ludCoqKiBqZXN0IHRydWU6ICJyw7N3bm/Fm8SHIHdzcMOzxYJjenlubmlrw7N3IGtpZXJ1bmtvd3ljaA0KcG93aW5uYSBiecSHIHd5a29uYW5hIGpha28gd3Nww7NsbmUgdGVzdHkgbmEgd3N6eXN0a2ljaCBwYXJhbWV0cmFjaA0KbmFjaHlsZW5pYSIsIGdkeSAqKipqb2ludCoqKiBqZXN0IGZhbHNlOiAibmFsZcW8eSB6Z8WCYXN6YcSHIG9kZHppZWxuZQ0KdGVzdHkgbmEga2HFvGR5bSB6IHBhcmFtZXRyw7N3IG5hY2h5bGVuaWEiLg0KDQpaYXV3YcW8LCDFvGUgdGVzdHkga3dhbnR5bG93ZSBzxIUgdGVzdGFtaSAibGluaWkgcsOzd25vbGVnxYJlaiIuIE96bmFjemEgdG8sDQrFvGUgcG93aW5uacWbbXkgd3lqxIXEhyByw7PFvG5lIHgtd3lyYXp5X3dvbG5lIGRsYSBrYcW8ZGVnbyBrd2FudHlsYSwgcG9uaWV3YcW8DQpyZXByZXplbnR1asSFIG9uZSBwb3ppb215IHJvemvFgmFkw7N3IHdhcnVua293eWNoLiBKZcWbbGkgamVkbmFrDQp3c3DDs8WCY3p5bm5pa2kga3dhbnR5bGkgZGxhIHdzcMOzxYJjenlubmlrb3cgc8SFIHRha2llIHNhbWUsIHRvIG5pZSBtYQ0KZWZla3TDs3cgc3BlY3lmaWN6bnljaCBkbGEga3dhbnR5bGksIHd5c3RhcmN6xIUgZWZla3R5IMWbcmVkbmllLg0KDQoqKkJhZGFuaWUgc3RhdHlzdHljem5laiByw7PFvG5pY3kgbWnEmWR6eSAyNS4gaSA1MC4ga3dhbnR5bGVtIHdhcnVua293eW06KioNCg0KQmlvcsSFYyBwb2QgdXdhZ8SZIHBvd3nFvHN6ZSBvc3phY293YW5pYSBrd2FudHlsaSwgcsOzxbxuaWNhIG1pxJlkenkNCmt3YW50eWxhbWkgMCwyNSBpIDAsNTAgaXN0bmllamUsIGFsZSBjenkgc8SFIG9uZSB3eXN0YXJjemFqxIVjbyBkdcW8ZSwgYWJ5DQpiecSHIHN0YXR5c3R5Y3puaWUgcsOzxbxuZT8gSmFrYSBqZXN0IHdhcnRvxZvEhyBwPyBQcnplZ2zEhWRhasSFYyBwb25pxbxzemUNCnd5bmlraSwgbmllIHPEhSBvbmUgc3RhdHlzdHljem5pZSByw7PFvG5lIQ0KDQpQbyBwaWVyd3N6ZSwgam9pbnQgPSBUUlVFLiBUbyBuaWUgamVzdCB0ZXN0b3dhbmllLCBjenkgd3Nww7PFgmN6eW5uaWsgbmENCmRpc3AgamVzdCB0YWtpIHNhbSBqYWsgd3Nww7PFgmN6eW5uaWsgbmEgaHAuIFRvIGplc3Qgd3Nww7NsbmUgdGVzdG93YW5pZSwNCmN6eSB3c3DDs8WCY3p5bm5pa2kgZGxhIHLDs8W8bnljaCBrd2FudHlsaSBkaXNwIGkgcsOzxbxueWNoIGt3YW50eWxpIGhwIHPEhQ0KdGFraWUgc2FtZSBkbGEga2HFvGRlaiB6bWllbm5lai4NCg0KYGBge3J9DQprd2FudHlsZSA8LSBjKDAuMjUsIDAuNTApDQpyZWdfa3dhbnR5bG93YSA8LSBycShtcGcgfiBkaXNwICsgaHAgKyBmYWN0b3IoYW0pLHRhdSA9IGt3YW50eWxlLCBkYXRhID0gbXRjYXJzKQ0KYW5vdmEocmVnX2t3YW50eWxvd2EsIHRlc3QgPSAiV2FsZCIsIGpvaW50PVRSVUUpDQpgYGANCg0KUG8gZHJ1Z2llLCBqb2ludCA9IEZhbHNlOg0KDQpgYGB7cn0NCmFub3ZhKHJlZ19rd2FudHlsb3dhLCB0ZXN0ID0gIldhbGQiLCBqb2ludD1GQUxTRSkNCmBgYA0KDQoqKkJhZGFuaWUgc3RhdHlzdHljem5laiByw7PFvG5pY3kgbWnEmWR6eSAyNSwgNTAgaSA3NSBrd2FudHlsZW0NCndhcnVua293eW06KioNCg0KUGllcndzenkga3dhcnR5bCBpIG1lZGlhbmEgbmllIHd5ZGFqxIUgc2nEmSBiecSHIHN0YXR5c3R5Y3puaWUgcsOzxbxuZSwgdGVyYXoNCmRvxYLEhWN6eW15IHRyemVjaSBrd2FydHlsLiBKYWsgd2lkYcSHIHdjemXFm25pZWosIGt3YXJ0eWxlIHdzcMOzbG5pZQ0Kd3lrYXp1asSFIGdyYWRpZW50LiBUZXJheiBtb8W8ZW15IHpvYmFjennEhywgxbxlICoqZGlzcCoqLCAqKmhwKiogaSAqKmFtKioNCnPEhSBvZGR6aWVsbmllIHN0YXR5c3R5Y3puaWUgcsOzxbxuZS4NCg0KUG8gcGllcndzemUsIGpvaW50ID0gVFJVRToNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmt3YW50eWxlIDwtIGMoMC4yNSwgMC41MCwgMC43NSkNCg0KcmVnX2t3YW50eWxvd2EgPC0gcnEobXBnIH4gZGlzcCArIGhwICsgZmFjdG9yKGFtKSx0YXUgPSBrd2FudHlsZSwgZGF0YSA9IG10Y2FycykNCg0KYW5vdmEocmVnX2t3YW50eWxvd2EsIHRlc3QgPSAiV2FsZCIsIGpvaW50PVRSVUUpDQpgYGANCg0KUG8gZHJ1Z2llLCBqb2ludCA9IEZhbHNlOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYW5vdmEocmVnX2t3YW50eWxvd2EsIHRlc3QgPSAiV2FsZCIsIGpvaW50PUZBTFNFKQ0KYGBgDQoNCiMjIyBEb2Jyb8SHIGRvcGFzb3dhbmlhDQoNCk1vxbxlbXkgb2JsaWN6ecSHIHdzcMOzxYJjenlubmlraSBkb2Jyb2NpIGRvcGFzb3dhbmlhIHJlZ3Jlc2ppIGt3YW50eWxvd2VqIHoNCnd5a29yenlzdGFuaWVtIHJlc3p0IGkgcmVzenQgYmV6d2FydW5rb3d5Y2g6DQoNCmBgYHtyfQ0KIyBnb29kZml0KHJlc2lkLCByZXNpZF9ubCwgdGF1KQ0KYGBgDQoNCk1pYXJhIGRvYnJvY2kgZG9wYXNvd2FuaWEgZGxhIHJlZ3Jlc2ppIGt3YW50eWxvd2VqIGplc3Qgc3phY293YW5hIGpha28gMQ0KbWludXMgc3Rvc3VuZWsgc3VteSBvZGNoeWxlxYQgYmV6d3pnbMSZZG55Y2ggdyBtb2RlbGFjaCB3IHBlxYJuaQ0Kc3BhcmFtZXRyeXpvd2FueWNoIGRvIHN1bXkgb2RjaHlsZcWEIGJlend6Z2zEmWRueWNoIHcgemVyb3d5bQ0KKGJlendhcnVua293eW0pIG1vZGVsdSBrd2FudHlsb3d5bS4NCg0KV2FydG/Fm2NpIHRlIHPEhSBwcnp5ZGF0bmUgZG8gcG9yw7N3bmHFhCBtacSZZHp5IG1vZGVsYW1pIGt3YW50eWxvd3ltaSwgYWxlDQpuaWUgc8SFIHBvcsOzd255d2FsbmUgemUgc3RhbmRhcmRvd3ltaSB3c3DDs8WCY3p5bm5pa2FtaSBkZXRlcm1pbmFjamkuIFRlDQpvc3RhdG5pZSBvcGFydGUgc8SFIG5hIHdhcmlhbmNqaSBvZGNoeWxlxYQga3dhZHJhdG93eWNoLCBuYXRvbWlhc3QNCndhcnRvxZtjaSBkb2Jyb2NpIGRvcGFzb3dhbmlhIGRsYSByZWdyZXNqaSBrd2FudHlsb3dlaiBvcGFydGUgc8SFIG5hDQpvZGNoeWxlbmlhY2ggYmV6d3pnbMSZZG55Y2guIFdhcnRvxZtjaSBkb2Jyb2NpIGRvcGFzb3dhbmlhIHphd3N6ZSBixJlkxIUNCm1uaWVqc3plIG5pxbwgd2FydG/Fm2NpIFJeMl4uDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyBtb2RlbCBrd2FudHlsb3d5DQptb2RlbDEgPC0gcnEobXBnIH4gZGlzcCArIGhwICsgZmFjdG9yKGFtKSx0YXUgPSAwLjUsIGRhdGEgPSBtdGNhcnMpDQpyZXN6dHkxIDwtIHJlc2lkKG1vZGVsMSkNCg0KIyMgYmV6d2FydW5rb3d5IChwdXN0eSkgbW9kZWwga3dhbnR5bG93eQ0KbW9kZWwyIDwtIHJxKG1wZyB+IDEsIHRhdSA9IDAuNSxkYXRhPW10Y2FycykNCnJlc3p0eTIgPC0gcmVzaWQobW9kZWwyKQ0KDQpnb29kZml0KHJlc3p0eTEsIHJlc3p0eTIsIDAuNSkNCg0KIyMgcjIgbW9kZWx1IEtNTksgZGxhIHBvcsOzd25hbmlhDQptb2RlbF9sbSA8LSBsbShtcGcgfiBkaXNwICsgaHAgKyBmYWN0b3IoYW0pLCBkYXRhID0gbXRjYXJzKQ0KDQpzdW1tYXJ5KG1vZGVsX2xtKSRyLnNxdWFyZWQNCmBgYA0KDQojIyBaYWRhbmllDQoNClRlcmF6IFdhc3phIGtvbGVqIDstKQ0KDQpXYXN6eW0gemFkYW5pZW0gZHppc2lhaiBqZXN0IHphbW9kZWxvd2FuaWUgLSBwb3LDs3duYW5pZSBLTU5LIG9yYXoNCnJlZ3Jlc2ppIGt3YW50eWxvd2VqIChyw7PFvG5vLXBvemlvbW93ZWopIGRsYSB6bWllbm5laiAiZWFybmluZ3MiIC0NCnd5bmFncm9kemVuaWEuDQoNCkRvYmllcnogaSBwcnpldGVzdHVqIHByZWR5a3RvcnksIGt3YW50eWxlIGRsYSBtb2RlbGkuIFd5a29uYWogdGVzdHkNCnLDs8W8bmljIHdzcMOzxYJjenlubmlrb3cgZGxhIGZpbmFsbnljaCBtb2RlbGkuDQoNClcgcHJ6eXBhZGt1IHByb2JsZW3Ds3cgLSBvYmVqcnp5aiB2aWRlbyB0dXRvcmlhbCAod8WCxIVjeiBwb2xza2llIG5hcGlzeSkNCm9yYXogd2VqZMW6IG5hIGplZ28gc3Ryb27EmSB6ZSDFunLDs2TFgmFtaS4gTW/FvGVzeiByw7N3bmllxbwgd3lrb3J6eXN0YcSHIHcvdw0KcHJ6eWvFgmFkeS4NCg0KYGBge3J9DQpkYXRhKCJDUFNTVzkyOTgiKQ0KIyA/Q1BTU1c5Mjk4IA0KYGBgDQoNCiMjIyBEYW5lIDE5OTINCg0KYGBge3J9DQpkYW5lPC1DUFNTVzkyOTgNCmRhbmVfMTk5MjwtZGFuZSAlPiUgZmlsdGVyKHllYXI9PTE5OTIpDQpkYW5lXzE5OTIgJT4lDQogIGdncGxvdChhZXMoYWdlLGVhcm5pbmdzKSkrDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YShkYW5lXzE5OTIpICNkYW5lIA0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGRhbmVfMTk5MikgKw0KICAgIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gYWdlLCB5ID0gZWFybmluZ3MpLCBjb2xvciA9ICJibHVlIikNCnRhdXMgPC0gYygwLjIwLCAwLjQwLCAwLjYwLCAwLjgwLCAwLjk1LCAwLjk5OSkNCmZpdHMgPC0gZGF0YS5mcmFtZSgNCiAgICBjb2VmKGxtKGVhcm5pbmdzIH4gYWdlLCBkYXRhID0gZGFuZV8xOTkyKSksDQogICAgc2FwcGx5KHRhdXMsIGZ1bmN0aW9uKHgpIGNvZWYocnEoZm9ybXVsYSA9IGVhcm5pbmdzIH4gYWdlLCBkYXRhID0gZGFuZV8xOTkyLCB0YXUgPSB4KSkpKQ0KbmFtZXMoZml0cykgPC0gYygiT0xTIiwgc3ByaW50ZigiJFxcdGF1X3slMC4yZn0kIiwgdGF1cykpDQpuZiA8LSBuY29sKGZpdHMpDQpjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibGFjayIsICJyZWQiKSkobmYpDQpwIDwtIHAgKyBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBmaXRzWzEsIDFdLCBzbG9wZSA9IGZpdHNbMiwgMV0sIGNvbG9yID0gY29sb3JzWzFdLCBsaW5ld2lkdGggPSAxLjUpDQpmb3IgKGkgaW4gc2VxX2xlbihuZilbLTFdKSB7DQogICAgcCA8LSBwICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gZml0c1sxLCBpXSwgc2xvcGUgPSBmaXRzWzIsIGldLCBjb2xvciA9IGNvbG9yc1tpXSkNCn0NCnANCmBgYA0KYGBge3J9DQpkYXRhKGRhbmVfMTk5MikgI2RhbmUgDQpwIDwtIGdncGxvdChkYXRhID0gZGFuZV8xOTkyKSArDQogICAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBnZW5kZXIsIHkgPSBlYXJuaW5ncyksIGNvbG9yID0gImJsdWUiKQ0KdGF1cyA8LSBjKDAuMjAsIDAuNDAsIDAuNjAsIDAuODAsIDAuOTUsIDAuOTk5KQ0KZml0cyA8LSBkYXRhLmZyYW1lKA0KICAgIGNvZWYobG0oZWFybmluZ3MgfiBnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIpKSwNCiAgICBzYXBwbHkodGF1cywgZnVuY3Rpb24oeCkgY29lZihycShmb3JtdWxhID0gZWFybmluZ3MgfiBnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIsIHRhdSA9IHgpKSkpDQpuYW1lcyhmaXRzKSA8LSBjKCJPTFMiLCBzcHJpbnRmKCIkXFx0YXVfeyUwLjJmfSQiLCB0YXVzKSkNCm5mIDwtIG5jb2woZml0cykNCmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoImJsYWNrIiwgInJlZCIpKShuZikNCnAgPC0gcCArIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IGZpdHNbMSwgMV0sIHNsb3BlID0gZml0c1syLCAxXSwgY29sb3IgPSBjb2xvcnNbMV0sIGxpbmV3aWR0aCA9IDEuNSkNCmZvciAoaSBpbiBzZXFfbGVuKG5mKVstMV0pIHsNCiAgICBwIDwtIHAgKyBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBmaXRzWzEsIGldLCBzbG9wZSA9IGZpdHNbMiwgaV0sIGNvbG9yID0gY29sb3JzW2ldKQ0KfQ0KcA0KYGBgDQpgYGB7cn0NCnEyMCA8LSBycShlYXJuaW5ncyB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTkyLCB0YXUgPSAwLjIwKQ0KcTQwIDwtIHJxKGVhcm5pbmdzIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIsIHRhdSA9IDAuNDApDQpxNjAgPC0gcnEoZWFybmluZ3MgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5MiwgdGF1ID0gMC42MCkNCnE4MCA8LSBycShlYXJuaW5ncyB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTkyLCB0YXUgPSAwLjgwKQ0KcTk1IDwtIHJxKGVhcm5pbmdzIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIsIHRhdSA9IDAuOTUpDQpxOTk5IDwtIHJxKGVhcm5pbmdzIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIsIHRhdSA9IDAuOTk5KQ0KIyBUYWJlbGEgeiBwb3LDs3duYW5pZW0gd3luaWvDs3cgdHJ6ZWNoIG1vZGVsaTogDQoNCnN0YXJnYXplcihxMjAsIHE0MCwgcTYwLCBxODAsIHE5NSwgcTk5OSwgdGl0bGUgPSAiV3luaWtpIHJlZ3Jlc2ppIGt3YW50eWxvd3ljaCIsIHR5cGUgPSAidGV4dCIpDQpgYGANCipJbnRlcnByZXRhY2phOioNCg0KLUtvYmlldHkgemFyYWJpYWrEhSDFm3JlZG5pbyBvIDAsOTYyIGRvbGFyYSB6YSBnb2R6aW7EmSBtbmllaiBuacW8IG3EmcW8Y3p5xbpuaSAobmEgcG9kc3Rhd2llIHBpZXJ3c3plZ28ga3dhcnR5bHUpLg0KDQotVyB3ecW8c3p5Y2ggZG9jaG9kYWNoIHDFgmXEhyBvcmF6IHd5a3N6dGHFgmNlbmllIG1hasSFIGplc3pjemUgc2lsbmllanN6eSB3cMWCeXcgbmEgd3lzb2tvxZvEhyB3eW5hZ3JvZHplbmlhLg0KDQotV3lyYXogd29sbnkgdyBnw7NybnljaCBrd2FydHlsYWNoIGplc3Qgc3RhdHlzdHljem5pZSBuaWVpc3RvdG55LCBjbyBzdWdlcnVqZSwgxbxlIHdhcnRvIHJvendhxbx5xIcgdHJhbnNmb3JtYWNqxJkgZG9jaG9kw7N3LCBuYSBwcnp5a8WCYWQgcHJ6ZXogaWNoIGxvZ2FyeXRtb3dhbmllLCBhYnkgbGVwaWVqIHVjaHd5Y2nEhyB6YWxlxbxub8WbY2kuDQoNCmBgYHtyfQ0KcTIwIDwtIHJxKGxvZyhlYXJuaW5ncykgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5MiwgdGF1ID0gMC4yMCkNCnE0MCA8LSBycShsb2coZWFybmluZ3MpIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIsIHRhdSA9IDAuNDApDQpxNjAgPC0gcnEobG9nKGVhcm5pbmdzKSB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTkyLCB0YXUgPSAwLjYwKQ0KcTgwIDwtIHJxKGxvZyhlYXJuaW5ncykgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5MiwgdGF1ID0gMC44MCkNCnE5NSA8LSBycShsb2coZWFybmluZ3MpIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIsIHRhdSA9IDAuOTUpDQpxOTk5IDwtIHJxKGxvZyhlYXJuaW5ncykgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5MiwgdGF1ID0gMC45OTkpDQojIFRhYmVsYSB6IHBvcsOzd25hbmllbSB3eW5pa8OzdyB0cnplY2ggbW9kZWxpOiANCg0Kc3RhcmdhemVyKHEyMCwgcTQwLCBxNjAsIHE4MCwgcTk1LCBxOTk5LCB0aXRsZSA9ICJXeW5pa2kgcmVncmVzamkga3dhbnR5bG93eWNoIiwgdHlwZSA9ICJ0ZXh0IikNCmBgYA0KDQpXeXJheiB3b2xueSBqZXN0IHRlcmF6IGlzdG90bnkuDQpLd2FydHlsIG5hIHBvemlvbWllIDAsOTk5IG9rYXphxYIgc2nEmSBuaWVpc3RvdG55LCBkbGF0ZWdvIGRlY3lkdWplbXkgc2nEmSBuYSBqZWdvIHd5a2x1Y3plbmllLg0KDQpgYGB7cn0NCnEyMCA8LSBycShsb2coZWFybmluZ3MpIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIsIHRhdSA9IDAuMjApDQpxNDAgPC0gcnEobG9nKGVhcm5pbmdzKSB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTkyLCB0YXUgPSAwLjQwKQ0KcTYwIDwtIHJxKGxvZyhlYXJuaW5ncykgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5MiwgdGF1ID0gMC42MCkNCnE4MCA8LSBycShsb2coZWFybmluZ3MpIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIsIHRhdSA9IDAuODApDQpxOTUgPC0gcnEobG9nKGVhcm5pbmdzKSB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTkyLCB0YXUgPSAwLjk1KQ0KDQojIFRhYmVsYSB6IHBvcsOzd25hbmllbSB3eW5pa8OzdyB0cnplY2ggbW9kZWxpOiANCg0Kc3RhcmdhemVyKHEyMCwgcTQwLCBxNjAsIHE4MCwgcTk1LCB0aXRsZSA9ICJXeW5pa2kgcmVncmVzamkga3dhbnR5bG93eWNoIiwgdHlwZSA9ICJ0ZXh0IikNCmBgYA0KIyMjIyBNb2RlbCBLTU5LDQoNCmBgYHtyfQ0Ka21uayA8LSBsbShsb2coZWFybmluZ3MpIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTIpDQpzdW1tYXJ5KGttbmspDQpgYGANCg0KTW9kZWwgd3lrYXp1amUgbmlza2kgd3Nww7PFgmN6eW5uaWsgZGV0ZXJtaW5hY2ppIFJeMiwgY28gd3NrYXp1amUgbmEgc8WCYWJlIGRvcGFzb3dhbmllIG1vZGVsdSBkbyBkYW55Y2guDQoNCmBgYHtyfQ0Ka3dhbnR5bGUgPC0gYygwLjIwLCAwLjQwLCAwLjYwLCAwLjgwLCAwLjk1KQ0KcmVnX2t3YW50eWxvd2EgPC0gcnEobG9nKGVhcm5pbmdzKSB+IGFnZStkZWdyZWUrZ2VuZGVyLHRhdSA9IGt3YW50eWxlLCBkYXRhID0gZGFuZV8xOTkyKQ0Kc3VtbWFyeShyZWdfa3dhbnR5bG93YSwgc2UgPSAiYm9vdCIpDQpgYGANCg0KYGBge3J9DQprd2FudHlsZSA8LSBjKDAuMjAsIDAuNDAsIDAuNjAsIDAuODAsIDAuOTUpDQpyZWdfa3dhbnR5bG93YSA8LSBycShsb2coZWFybmluZ3MpIH4gYWdlK2RlZ3JlZStnZW5kZXIsdGF1ID0ga3dhbnR5bGUsIGRhdGEgPSBkYW5lXzE5OTIpDQphbm92YShyZWdfa3dhbnR5bG93YSwgdGVzdCA9ICJXYWxkIiwgam9pbnQ9VFJVRSkNCmBgYA0KV2FydG/Fm8SHIHAgKDEuODI5ZS0wNikgamVzdCBiYXJkem8gbWHFgmEsIGNvIHdza2F6dWplIG5hIGlzdG90bm/Fm8SHIHN0YXR5c3R5Y3puxIUuIE96bmFjemEgdG8sIMW8ZSBpc3RuaWVqZSBzdGF0eXN0eWN6bmllIGlzdG90bmEgcsOzxbxuaWNhIHcgd3Nww7PFgmN6eW5uaWthY2ggbmFjaHlsZW5pYSBkbGEgcsOzxbxueWNoIGt3YW50eWxpLCB0em4uIHdzcMOzxYJjenlubmlraSB0ZSBuaWUgc8SFIHLDs3duZSBuYSBwb3ppb21hY2gga3dhbnR5bGkgMC4yLCAwLjQsIDAuNiwgMC44IGkgMC45NS4NCg0KIyMjIyBNb2RlbCBrd2FudHlsb3d5DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyBtb2RlbCBrd2FudHlsb3d5DQptb2RlbDEgPC0gcnEobG9nKGVhcm5pbmdzKSB+IGFnZStkZWdyZWUrZ2VuZGVyLCB0YXUgPSAwLjgwLCBkYXRhID0gZGFuZV8xOTkyKQ0KcmVzenR5MSA8LSByZXNpZChtb2RlbDEpDQoNCiMjIGJlendhcnVua293eSAocHVzdHkpIG1vZGVsIGt3YW50eWxvd3kNCm1vZGVsMiA8LSBycShsb2coZWFybmluZ3MpIH4gMSwgdGF1ID0gMC44MCwgZGF0YSA9IGRhbmVfMTk5MikNCnJlc3p0eTIgPC0gcmVzaWQobW9kZWwyKQ0KDQpnb29kZml0KHJlc3p0eTEsIHJlc3p0eTIsIDAuODApDQoNCiMjIHIyIG1vZGVsdSBLTU5LIGRsYSBwb3LDs3duYW5pYQ0KbW9kZWxfbG0gPC0gbG0obG9nKGVhcm5pbmdzKSB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTkyKQ0KDQpzdW1tYXJ5KG1vZGVsX2xtKSRyLnNxdWFyZWQNCmBgYA0KV2FydG/Fm8SHIDAuMTI1NjczOCBtb2RlbHUga3dhbnR5bG93ZWdvIGplc3QgcmVsYXR5d25pZSBuaXNrYSwgY28gc3VnZXJ1amUsIMW8ZSBkb3Bhc293YW5pZSBtb2RlbHUga3dhbnR5bG93ZWdvIG5pZSBqZXN0IGlkZWFsbmUsIGFsZSBuYWRhbCBtb8W8ZSBiecSHIHXFvHl0ZWN6bmUuIE5pxbxzemEgd2FydG/Fm8SHIHdza2F6dWplIG5hIHRvLCDFvGUgbW9kZWwga3dhbnR5bG93eSBuaWUgcG9wcmF3acWCIHNpxJkgem5hY3rEhWNvIHcgcG9yw7N3bmFuaXUgZG8gbW9kZWx1IHB1c3RlZ28uIFdhcnRvIGplZG5hayB6YXpuYWN6ecSHLCDFvGUgbW9kZWwga3dhbnR5bG93eSBsZXBpZWogcmFkemkgc29iaWUgeiBuaWVyw7N3bsSFIHptaWVubm/Fm2NpxIUgdyByw7PFvG55Y2ggY3rEmcWbY2lhY2ggZGFueWNoLCBhIG1vZGVsIGxpbmlvd3kgemFwZXduaWEgdHlsa28gamVkbm8gb2fDs2xuZSBkb3Bhc293YW5pZS4NCg0KIyMjIERhbmUgMTk5OA0KDQpgYGB7cn0NCmRhbmVfMTk5ODwtZGFuZSAlPiUgZmlsdGVyKHllYXI9PTE5OTgpDQpkYW5lXzE5OTggJT4lDQogIGdncGxvdChhZXMoYWdlLGVhcm5pbmdzKSkrDQogIGdlb21fcG9pbnQoKQ0KYGBgDQpgYGB7cn0NCmRhdGEoZGFuZV8xOTk4KSAjZGFuZSANCnAgPC0gZ2dwbG90KGRhdGEgPSBkYW5lXzE5OTgpICsNCiAgICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGFnZSwgeSA9IGVhcm5pbmdzKSwgY29sb3IgPSAiYmx1ZSIpDQp0YXVzIDwtIGMoMC4yMCwgMC40MCwgMC42MCwgMC44MCwgMC45MCwgMC45NSwgMC45OSkNCmZpdHMgPC0gZGF0YS5mcmFtZSgNCiAgICBjb2VmKGxtKGVhcm5pbmdzIH4gYWdlLCBkYXRhID0gZGFuZV8xOTkyKSksDQogICAgc2FwcGx5KHRhdXMsIGZ1bmN0aW9uKHgpIGNvZWYocnEoZm9ybXVsYSA9IGVhcm5pbmdzIH4gYWdlLCBkYXRhID0gZGFuZV8xOTk4LCB0YXUgPSB4KSkpKQ0KbmFtZXMoZml0cykgPC0gYygiT0xTIiwgc3ByaW50ZigiJFxcdGF1X3slMC4yZn0kIiwgdGF1cykpDQpuZiA8LSBuY29sKGZpdHMpDQpjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibGFjayIsICJyZWQiKSkobmYpDQpwIDwtIHAgKyBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBmaXRzWzEsIDFdLCBzbG9wZSA9IGZpdHNbMiwgMV0sIGNvbG9yID0gY29sb3JzWzFdLCBsaW5ld2lkdGggPSAxLjUpDQpmb3IgKGkgaW4gc2VxX2xlbihuZilbLTFdKSB7DQogICAgcCA8LSBwICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gZml0c1sxLCBpXSwgc2xvcGUgPSBmaXRzWzIsIGldLCBjb2xvciA9IGNvbG9yc1tpXSkNCn0NCnANCmBgYA0KVHV0YWogemRlY3lkb3dhbGnFm215IHNpxJkgbmEgZG9kYW5pZSBrd2FudHlsYSAwLjk5LCBhYnkgdWNod3ljacSHIHdpxJljZWogaW5mb3JtYWNqaSB6IGRhbnljaCBpIGxlcGllaiB6cm96dW1pZcSHIHphbGXFvG5vxZtjaSB3IG5hand5xbxzenljaCBwb3ppb21hY2ggcm96a8WCYWR1IGRvY2hvZMOzdy4NCg0KYGBge3J9DQpxMjBfOTggPC0gcnEoZWFybmluZ3MgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5OCwgdGF1ID0gMC4yMCkNCnE0MF85OCA8LSBycShlYXJuaW5ncyB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTk4LCB0YXUgPSAwLjQwKQ0KcTYwXzk4IDwtIHJxKGVhcm5pbmdzIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTgsIHRhdSA9IDAuNjApDQpxODBfOTggPC0gcnEoZWFybmluZ3MgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5OCwgdGF1ID0gMC44MCkNCnE5MF85OCA8LSBycShlYXJuaW5ncyB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTk4LCB0YXUgPSAwLjkwKQ0KcTk1Xzk4IDwtIHJxKGVhcm5pbmdzIH4gYWdlK2RlZ3JlZStnZW5kZXIsIGRhdGEgPSBkYW5lXzE5OTgsIHRhdSA9IDAuOTUpDQpxOTlfOTggPC0gcnEoZWFybmluZ3MgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5OCwgdGF1ID0gMC45OSkNCiMgVGFiZWxhIHogcG9yw7N3bmFuaWVtIHd5bmlrw7N3IHRyemVjaCBtb2RlbGk6IA0KDQpzdGFyZ2F6ZXIocTIwXzk4LCBxNDBfOTgsIHE2MF85OCwgcTgwXzk4LCBxOTBfOTgsIHE5NV85OCxxOTlfOTgsIHRpdGxlID0gIld5bmlraSByZWdyZXNqaSBrd2FudHlsb3d5Y2giLCB0eXBlID0gInRleHQiKQ0KYGBgDQoqV2llayAoYWdlKToqDQoNCldwxYJ5dyB3aWVrdSBuYSB3eW5hZ3JvZHplbmllIHJvxZtuaWUgdyB3ecW8c3p5Y2gga3dhbnR5bGFjaCBkb2Nob2TDs3cuIE5hIHByenlrxYJhZCwgZGxhIDIwLiBwZXJjZW50eWxhIHd6cm9zdCB3aWVrdSBvIGplZG7EhSBqZWRub3N0a8SZIHp3acSZa3N6YSBkb2Now7NkIMWbcmVkbmlvIG8gMC4xNjAsIHBvZGN6YXMgZ2R5IGRsYSA4MC4gcGVyY2VudHlsYSB0ZW4gd3pyb3N0IHd5bm9zaSBqdcW8IDAuNDU0Lg0KVyBnw7NybnljaCBrd2FudHlsYWNoIChucC4gOTUuIHBlcmNlbnR5bCkgZWZla3Qgd2lla3UgcG96b3N0YWplIGlzdG90bnkgc3RhdHlzdHljem5pZSwgYWxlIG5pZSB6bWllbmlhIHNpxJkgem5hY3rEhWNvIHcgc3Rvc3Vua3UgZG8gOTAuIHBlcmNlbnR5bGEuIEplZG5hayBkbGEgOTkuIHBlcmNlbnR5bGEgZWZla3QgamVzdCBzxYJhYnN6eSBpIHN0YWplIHNpxJkgbmllaXN0b3RueS4NCg0KKld5a3N6dGHFgmNlbmllIChkZWdyZWViYWNoZWxvcik6Kg0KDQpFZmVrdCBwb3NpYWRhbmlhIHN0b3BuaWEgbGljZW5jamF0YSBqZXN0IHd5cmHFum55IGkgcm/Fm25pZSB3IG1pYXLEmSB3enJvc3R1IGt3YW50eWxhLiBOYSBwcnp5a8WCYWQsIGRsYSAyMC4gcGVyY2VudHlsYSB3eWtzenRhxYJjZW5pZSB6d2nEmWtzemEgZG9jaMOzZCDFm3JlZG5pbyBvIDMuNTI2IGplZG5vc3RlaywgcG9kY3phcyBnZHkgZGxhIDk1LiBwZXJjZW50eWxhIGVmZWt0IHd5bm9zaSBqdcW8IDEwLjA5NiBqZWRub3N0ZWssIGEgZGxhIDk5LiBwZXJjZW50eWxhIGHFvCAxMi4zOTIgamVkbm9zdGVrLg0KV3NrYXp1amUgdG8sIMW8ZSB3IHd5xbxzenljaCBkb2Nob2RhY2ggd3lrc3p0YcWCY2VuaWUgb2Rncnl3YSBjb3JheiB3acSZa3N6xIUgcm9sxJksIGNvIHN1Z2VydWplLCDFvGUgd3lrc3p0YcWCY2VuaWUgamVzdCBrbHVjem93eW0gY3p5bm5pa2llbSB3IHV6eXNrYW5pdSB3eXNva2ljaCBkb2Nob2TDs3cuDQoNCipQxYJlxIcgKGdlbmRlcmZlbWFsZSk6Kg0KDQpFZmVrdCBwxYJjaSAoYnljaWEga29iaWV0xIUpIGplc3QgbmVnYXR5d255IGkgd3pyYXN0YSB3cmF6IHoga3dhbnR5bGVtLCBjbyBvem5hY3phLCDFvGUgcsOzxbxuaWNhIHcgd3luYWdyb2R6ZW5pdSBtacSZZHp5IGtvYmlldGFtaSBhIG3EmcW8Y3p5em5hbWkgc3RhamUgc2nEmSB3acSZa3N6YSB3IHd5xbxzenljaCBkb2Nob2RhY2guIERsYSAyMC4gcGVyY2VudHlsYSBrb2JpZXR5IHphcmFiaWFqxIUgxZtyZWRuaW8gbyAxLjQ0MiBqZWRub3N0a2kgbW5pZWogbmnFvCBtxJnFvGN6ecW6bmksIGFsZSB3IDk1LiBwZXJjZW50eWx1IHLDs8W8bmljYSB0YSB3eW5vc2kganXFvCA0LjY3NyBqZWRub3N0ZWssIGEgdyA5OS4gcGVyY2VudHlsdSAobW9kZWwgNykgYcW8IDUuNzA2IGplZG5vc3Rlay4NClN1Z2VydWplIHRvLCDFvGUgbHVrYSBwxYJhY293YSBtacSZZHp5IGtvYmlldGFtaSBhIG3EmcW8Y3p5em5hbWkgamVzdCBzemN6ZWfDs2xuaWUgd3lyYcW6bmEgdyBuYWp3ecW8c3p5Y2ggcG96aW9tYWNoIGRvY2hvZMOzdy4NCkNvIGlzdG90bmUsIGx1a2EgcMWCY2lvd2EgKHLDs8W8bmljZSB3IGRvY2hvZGFjaCBtacSZZHp5IGtvYmlldGFtaSBhIG3EmcW8Y3p5em5hbWkpIGplc3QgYmFyZHppZWogd2lkb2N6bmEgdyBkYW55Y2ggeiAxOTk4IHJva3UgbmnFvCB3IGRhbnljaCB6IDE5OTIgcm9rdSwgY28gbW/FvGUgd3NrYXp5d2HEhyBuYSBuYXNpbGFqxIVjZSBzacSZIG5pZXLDs3dub8WbY2kgdyB3eW5hZ3JvZHplbmlhY2ggdyB0eW0gb2tyZXNpZS4NCg0KIyMjIyBNb2RlbCBLTU5LDQoNCmBgYHtyfQ0Ka21ua18xOTk4IDwtIGxtKGxvZyhlYXJuaW5ncykgfiBhZ2UrZGVncmVlK2dlbmRlciwgZGF0YSA9IGRhbmVfMTk5OCkNCnN1bW1hcnkoa21ua18xOTk4KQ0KYGBgDQpQb3d5xbxzenkgbW9kZWwsIHBvZG9ibmllIGphayB3IHByenlwYWRrdSBtb2RlbHUgeiBkYW55bWkgeiAxOTkyIHJva3UsIHd5a2F6dWplIG5pc2tpIHBvemlvbSBSXjIsIHphdGVtIG1vZGVsIHPFgmFibyBkb3Bhc293dWplIHNpxJkgZG8gZGFueWNoLg0KDQpgYGB7cn0NCmt3YW50eWxlXzE5OTggPC0gYygwLjIwLCAwLjQwLCAwLjYwLCAwLjgwLCAwLjkwLCAwLjk1LCAwLjk5KQ0KcmVnX2t3YW50eWxvd2FfMTk5OCA8LSBycShsb2coZWFybmluZ3MpIH4gYWdlK2RlZ3JlZStnZW5kZXIsdGF1ID0ga3dhbnR5bGUsIGRhdGEgPSBkYW5lXzE5OTgpDQpzdW1tYXJ5KHJlZ19rd2FudHlsb3dhXzE5OTgsIHNlID0gImJvb3QiKQ0KYGBgDQpgYGB7cn0NCnJlZ19rd2FudHlsb3dhXzE5OTggPC0gcnEobG9nKGVhcm5pbmdzKSB+IGFnZStkZWdyZWUrZ2VuZGVyLHRhdSA9IGt3YW50eWxlXzE5OTgsIGRhdGEgPSBkYW5lXzE5OTgpDQphbm92YShyZWdfa3dhbnR5bG93YV8xOTk4LCB0ZXN0ID0gIldhbGQiLCBqb2ludD1UUlVFKQ0KYGBgDQpXYXJ0b8WbxIcgcCBqZXN0IG1uaWVqc3phIG5pxbwgMC4wMSwgY28gd3NrYXp1amUgbmEgaXN0b3Rub8WbxIcgc3RhdHlzdHljem7EhSBuYSBwb3ppb21pZSAxJS4gT3puYWN6YSB0bywgxbxlIGlzdG5pZWplIHN0YXR5c3R5Y3puaWUgaXN0b3RuYSByw7PFvG5pY2EgdyBuYWNoeWxlbmlhY2ggKHdzcMOzxYJjenlubmlrYWNoKSBkbGEgcsOzxbxueWNoIGt3YW50eWxpIHcgemFrcmVzaWUgezAuMiwwLjQsMC42LDAuOCwwLjksMC45NSwwLjk5fS4NCg0KIyMjIyBNb2RlbCBrd2FudHlsb3d5DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyBtb2RlbCBrd2FudHlsb3d5DQptb2RlbDFfMTk5OCA8LSBycShsb2coZWFybmluZ3MpIH4gYWdlK2RlZ3JlZStnZW5kZXIsIHRhdSA9IDAuNzAsIGRhdGEgPSBkYW5lXzE5OTgpDQpyZXN6dHkxXzE5OTggPC0gcmVzaWQobW9kZWwxXzE5OTgpDQoNCiMjIGJlendhcnVua293eSAocHVzdHkpIG1vZGVsIGt3YW50eWxvd3kNCm1vZGVsMl8xOTk4IDwtIHJxKGxvZyhlYXJuaW5ncykgfiAxLCB0YXUgPSAwLjcwLCBkYXRhID0gZGFuZV8xOTk4KQ0KcmVzenR5Ml8xOTk4IDwtIHJlc2lkKG1vZGVsMl8xOTk4KQ0KDQpnb29kZml0KHJlc3p0eTFfMTk5OCwgcmVzenR5Ml8xOTk4LCAwLjcwKQ0KDQojIyByMiBtb2RlbHUgS01OSyBkbGEgcG9yw7N3bmFuaWENCm1vZGVsX2xtXzE5OTggPC0gbG0obG9nKGVhcm5pbmdzKSB+IGFnZStkZWdyZWUrZ2VuZGVyLCBkYXRhID0gZGFuZV8xOTk4KQ0KDQpzdW1tYXJ5KG1vZGVsX2xtXzE5OTgpJHIuc3F1YXJlZA0KYGBgDQpXYXJ0b8WbxIcgMC4xMTE2MjEzIGRsYSBtaWFyeSBnb29kZml0IHcgbW9kZWx1IGt3YW50eWxvd3ltICh0YXUgPSAwLjcwKSBqZXN0IHJlbGF0eXduaWUgbmlza2EuIE96bmFjemEgdG8sIMW8ZSBkb3Bhc293YW5pZSBtb2RlbHUga3dhbnR5bG93ZWdvIHcgcG9yw7N3bmFuaXUgZG8gbW9kZWx1IHB1c3RlZ28gKGJleiB6bWllbm55Y2ggb2JqYcWbbmlhasSFY3ljaCkgbmllIGplc3QgaWRlYWxuZSwgYWxlIG5hZGFsIG1vxbxlIGJ5xIcgdXpuYW5lIHphIHXFvHl0ZWN6bmUuDQoNCk5pxbxzemEgd2FydG/Fm8SHIGdvb2RmaXQgc3VnZXJ1amUsIMW8ZSBtb2RlbCBrd2FudHlsb3d5IG5pZSB3cHJvd2FkemEgZHXFvGVnbyB3eWphxZtuaWVuaWEgem1pZW5ub8WbY2kgdyBkYW55Y2ggdyBwb3LDs3duYW5pdSBkbyBtb2RlbHUgcHVzdGVnby4gSmVkbmFrIGlzdG90xIUgbW9kZWx1IGt3YW50eWxvd2VnbyBqZXN0IHRvLCDFvGUga29uY2VudHJ1amUgc2nEmSBvbiBuYSBzcGVjeWZpY3puZWogY3rEmcWbY2kgcm96a8WCYWR1LCBjbyBjenluaSBnbyBiYXJkemllaiBvZHBvd2llZG5pbSBkbyBhbmFsaXp5IHJvemvFgmFkdSBkb2Nob2TDs3cgbmnFvCBrbGFzeWN6bnkgbW9kZWwgbGluaW93eS4NCg0KV2FydG/Fm8SHIFJeMiB3eW5vc2kgMC4xODI4MDU4LCBjbyBvem5hY3phLCDFvGUgbW9kZWwgbGluaW93eSB3eWphxZtuaWEgb2tvxYJvIDE4LjI4JSB6bWllbm5vxZtjaSBsb2dhcnl0bcOzdyBkb2Nob2TDs3cgdyBkYW55Y2guDQpNaW1vIMW8ZSBSXjIgamVzdCB3ecW8c3plIG5pxbwgd2FydG/Fm8SHIGdvb2RmaXQgdyBtb2RlbHUga3dhbnR5bG93eW0sIG1vZGVsIGxpbmlvd3kgemFrxYJhZGEgamVkbm9saXTEhSB6YWxlxbxub8WbxIcgbWnEmWR6eSB6bWllbm55bWkgb2JqYcWbbmlhasSFY3ltaSBhIHptaWVubsSFIHphbGXFvG7EhSB3IGNhxYJ5bSByb3prxYJhZHppZSBkYW55Y2gsIGNvIG1vxbxlIG5pZSBvZHp3aWVyY2llZGxhxIcgcsOzxbxuaWMgdyByw7PFvG55Y2ggY3rEmcWbY2lhY2ggcm96a8WCYWR1Lg0KDQoqV25pb3NraToqDQoNCk1vZGVsIGt3YW50eWxvd3kgbW/FvGUgYnnEhyBiYXJkemllaiB1xbx5dGVjem55LCBnZHkgaW50ZXJlc3VqZSBuYXMgYW5hbGl6YSBzcGVjeWZpY3puZWogY3rEmcWbY2kgcm96a8WCYWR1LCBwb25pZXdhxbwgcG9rYXp1amUsIGphayB6bWllbm5lIG9iamHFm25pYWrEhWNlIHdwxYJ5d2FqxIUgbmEgZG9jaG9keSB3IHR5bSBrb25rcmV0bnltIGt3YW50eWx1Lg0KTW9kZWwgbGluaW93eSBkb3N0YXJjemEgYmFyZHppZWogb2fDs2xuZWogYW5hbGl6eSwgd3lqYcWbbmlhasSFYyBnbG9iYWxuxIUgem1pZW5ub8WbxIcgdyBkYW55Y2gsIGFsZSBtb8W8ZSBuaWUgb2RkYXdhxIcgcHJlY3l6eWpuaWUgcsOzxbxuaWMgdyB6YWxlxbxub8WbY2lhY2ggbWnEmWR6eSB6bWllbm55bWkgdyByw7PFvG55Y2ggY3rEmcWbY2lhY2ggcm96a8WCYWR1IGRvY2hvZMOzdy4=