To laboratorium na temat Regresji grzbietowej (Ridge Regression - RR) i Lasso w R pochodzi ze stron 251-255 książki “Introduction to Statistical Learning with Applications in R” autorstwa Garetha Jamesa, Danieli Witten, Trevora Hastie i Roberta Tibshirani. Zostało ono ponownie zaimplementowane jesienią 2016 roku w formacie tidyverse przez Amelię McNamarę i R. Jordana Crousera w Smith College.

W tym tygodniu omówimy dwie alternatywne formy regresji liniowej zwane regresją grzbietową i regresją LASSO. Te dwie metody są przykładami metod regularyzacji lub zmniejszania, w których zachęca się do tego, aby parametry modelu były małe.

Regresja Grzbietowa i Lasso

Wykorzystamy pakiet glmnet w celu przeprowadzenia regresji ridge i lasso. Główną funkcją w tym pakiecie jest glmnet(), która może być użyta do dopasowania modeli regresji grzbietowej, modeli lasso i innych.

Funkcja ta ma nieco inną składnię niż inne funkcje dopasowujące modele, z którymi zetknęliśmy się do tej pory. W szczególności, musimy przekazać macierz \(x\) jak również wektor \(y\) i nie używamy składni \(y \sim x\).

Zanim przejdziemy dalej, upewnijmy się najpierw, że brakujące wartości zostały zostały usunięte z danych, jak opisano w poprzednim laboratorium.

Hitters = na.omit(Hitters)

W raporcie tym przeprowadzimy regresję grzbietową i lasso, aby przewidzieć Salary na danych Hitters.

Skonfigurujmy nasze dane:

x = model.matrix(Salary~., Hitters)[,-1] # przycinam pierwszą kolumnę
                                         # zostawiam predyktory
y = Hitters %>%
  select(Salary) %>%
  unlist() %>%
  as.numeric()

Funkcja model.matrix() jest szczególnie przydatna do tworzenia \(x\); nie tylko nie tylko tworzy macierz odpowiadającą 19 predyktorom, ale również automatycznie przekształca wszelkie zmienne jakościowe w zmienne dummy.

Ta ostatnia właściwość jest ważna, ponieważ glmnet() może przyjmować tylko numeryczne, ilościowe dane wejściowe.

Bias vs Variance

Wybór modelu w problemach uczenia nadzorowanego wiąże się z realizacją dwóch sprzecznych celów:

1.) Model powienien być dobrze dopasowany do danych uczących, aby uchwycić zależność pomiędzy danymi.

2.) Model powinien dobrze przybliżać nieznane dane (zapewniać mały błąd generalizacji).

Modele złożone dobdrze dopasowują się do danych wyjściowych, ale charakteryzują się dużą zmiennością wartości wyjściowych. Ryzykiem jest nadmierne dopasowanie = overfitting!

Modele prostsze są obciążone dużym błędem systematyczny (bias) i ich zastosowanie niesie ryzyko niewystarczającego dopasowania (underfitting)!

Składnikiem błędów generalizacji jest nieredukowalny błąd związany ze zmiennością danych.

Regularyzacja

Duża liczna zmiennych objaśniających (predyktorów): Metoda OLS nie daje jednoznacznego rozwiązania, gdy macierz XTX nie jest odwracalna (tzn. gdy zmienne objaśniające są liniowo zależne).

Taka sytuacja może mieć miejsce gdy zmiennych objaśniających jest tyle samo lub więcej niż obserwacji.

Duża wartość θi oznacza dużą wrażliwość funkcji regresji na drobne fluktuacje cechy!

Lepszym rozwiązaniem jest gorsze dopasowanie do danych uczących przy równoczesnym ograniczeniu parametrów świadczących o potencjalnie dużym błędzie generalizacji.

Regresja Grzbietowa

Wprowadzenie

Regresja grzbietowa (ang. Ridge regression) to technika regresji liniowej, która wprowadza regularyzację \(L_2\) do estymacji współczynników modelu. Regularyzacja \(L_2\) polega na dodaniu do funkcji celu kary proporcjonalnej do kwadratu wartości współczynników regresji.

Podstawową ideą regresji grzbietowej jest minimalizacja funkcji celu, która składa się z dwóch składników: błędu dopasowania (sumy kwadratów różnic pomiędzy rzeczywistymi wartościami odpowiedzi a przewidywanymi wartościami modelu) i kary regularyzacyjnej \(L_2\).

Wzór funkcji celu dla regresji grzbietowej można przedstawić jako: Minimize: RSS + \(\lambda \|\beta\|_2^2\), gdzie:

  • RSS to suma kwadratów różnic pomiędzy rzeczywistymi wartościami odpowiedzi a przewidywanymi wartościami modelu (błąd dopasowania),

  • \(\lambda\) (lambda) to parametr regularyzacji, który kontroluje siłę regularyzacji,

  • \(\|\beta\|_2^2\) to norma \(L_2\) współczynników regresji podniesiona do kwadratu.

Dodanie kary regularyzacyjnej \(L_2\) powoduje, że współczynniki regresji są skupione wokół zera, ale nie dokładnie równe zeru (chyba że \(\lambda\)=0).

Regresja grzbietowa zmniejsza wartości współczynników, ale nie powoduje, że stają się one równe zero. Im większa wartość \(\lambda\), tym bardziej są “sciskane” współczynniki regresji.

Regresja grzbietowa jest szczególnie przydatna, gdy mamy do czynienia z modelem, w którym występuje nadmierna wielowymiarowość lub wysokie korelacje między zmiennymi niezależnymi.

Poprzez zmniejszanie wartości współczynników, regresja grzbietowa może pomóc w redukcji wpływu mało istotnych cech, poprawić stabilność modelu i zmniejszyć ryzyko przeuczenia (overfitting).

Jednym ze sposobów kontroli złożoności modelu jest penalizacja jego wielkości. Na przykład, w problemie regresji liniowej:

\[ \min_{\beta \in \mathbb{R}^p} \sum_{i=1}^n (y_i - x_i^\top \beta)^2, \]

możemy kontrolować wielkość współczynników \(\beta\). Oczywiście wielkość \(\beta\) można zdefiniować na różne sposoby, np. norma-2: \(\|\beta\|_2\), norma-1: \(\|\beta\|_1\) czy norma-nieskończoność: \(\|\beta\|_{\infty}\). Regresja grzbietowa wiąże się z karą dwóch norm:

\[ \min_{\beta \in \mathbb{R}^p} \sum_{i=1}^n (y_i - x_i^\top \beta)^2 + \lambda \|\beta\|_2^2 \]

gdzie \(\lambda\) jest parametrem kontrolującym poziom regularyzacji. Zauważ, że \(X\) to macierz \(n\) na \(p\) wymiarów z wierszami: \(x_i^\top\), oraz \(Y\) to \(n\) na 1 wektor \(y_i\). Załóżmy, że \(X^\top X + \lambda I\) jest odwracalna, mamy dokładne rozwiązanie problemu regresji grzbietowej:

\[ \hat \beta_{ridge} = (X^\top X + \lambda I)^{-1}X^\top Y. \]

Przypomnijmy, że rozwiązaniem zwykłej regresji najmniejszych kwadratów jest (zakładając odwracalność macierzy \(X^\top X\)):

\[ \hat \beta_{ols} = (X^\top X)^{-1}X^\top Y. \]

Dwa fakty: kiedy \(\lambda \to 0\), \(\hat \beta_{ridge} \to \hat \beta_{ols}\); kiedy \(\lambda \to \infty\), \(\hat \beta_{ridge} \to 0\).

W szczególnych przypadkach \(X\) jest ortogonalna (tzn. kolumny \(X\) są ortogonalne), mamy:

\[ \hat \beta_{ridge} = \frac{\hat \beta_{ols}}{1 + \lambda}. \]

Widzimy więc, że estymator grzbietowy ma dodatkowo \(1/(1 + \lambda)\) tzw. “shrinkage factor”. W związku z tym na estymatorze grzbietowym występuje obciążliwość (bias).

Przykład

Funkcja glmnet() posiada argument alfa, który określa, jaki typ modelu jest dopasowywany.

Jeśli alfa = 0 to dopasowywany jest model regresji grzbietowej, a jeśli alfa = 1 to dopasowywany jest model lasso.

Najpierw dopasowujemy model regresji grzbietowej:

grid = 10^seq(10, -2, length = 100)
ridge_mod = glmnet(x, y, alpha = 0, lambda = grid)

Domyślnie funkcja glmnet() wykonuje regresję grzbietową dla automatycznie wybranego wybranego zakresu wartości \(\lambda\). Jednakże, tutaj wybraliśmy implementację funkcję w zakresie wartości od \(\lambda = 10^{10}\) do \(\lambda = 10^{-2}\), zasadniczo pokrywając pełen zakres scenariuszy od modelu zerowego zawierającego tylko przechwyt, do dopasowania najmniejszego kwadratu.

Jak widać, możemy również obliczyć dopasowanie modelu dla konkretnej wartości \(\lambda\), która nie jest jedną z oryginalnych wartości siatki.

Zauważ, że domyślnie funkcja glmnet() standaryzuje zmienne tak, by były w tej samej skali. Aby wyłączyć to domyślne ustawienie, użyj argumentu standardize = FALSE.

Z każdą wartością \(\lambda\) związany jest wektor współczynników regresji grzbietowej, przechowywany w macierzy, do której można uzyskać dostęp przez coef(). W tym przypadku jest to macierz \(20 \times 100\), z 20 wierszami (po jednym dla każdego predyktora, plus intercept) i 100 kolumnami (po jednej dla każdej wartości \(\lambda\)).

## [1]  20 100

Spodziewamy się, że oszacowania współczynników będą znacznie mniejsze, w sensie normy \(l_2\), gdy używana jest duża wartość \(\lambda\), w porównaniu z małą wartością \(\lambda\).

Oto współczynniki, gdy \(\lambda = 11498\), wraz z ich normą \(l_2\):

## [1] 11497.57
##   (Intercept)         AtBat          Hits         HmRun          Runs 
## 407.356050200   0.036957182   0.138180344   0.524629976   0.230701523 
##           RBI         Walks         Years        CAtBat         CHits 
##   0.239841459   0.289618741   1.107702929   0.003131815   0.011653637 
##        CHmRun         CRuns          CRBI        CWalks       LeagueN 
##   0.087545670   0.023379882   0.024138320   0.025015421   0.085028114 
##     DivisionW       PutOuts       Assists        Errors    NewLeagueN 
##  -6.215440973   0.016482577   0.002612988  -0.020502690   0.301433531
## [1] 6.360612

Dla kontrastu, oto współczynniki, gdy \(\lambda = 705\), wraz z ich \(l_2\) normą. Zwróć uwagę na znacznie większą normę \(l_2\) współczynników związanych z tą mniejszą wartością \(\lambda\).

ridge_mod$lambda[60] # Wyświetl 60-tą wartość lambdy
## [1] 705.4802
coef(ridge_mod)[,60] # Wyświetl współczynniki powiązane z 60-tą wartość lambdy
##  (Intercept)        AtBat         Hits        HmRun         Runs          RBI 
##  54.32519950   0.11211115   0.65622409   1.17980910   0.93769713   0.84718546 
##        Walks        Years       CAtBat        CHits       CHmRun        CRuns 
##   1.31987948   2.59640425   0.01083413   0.04674557   0.33777318   0.09355528 
##         CRBI       CWalks      LeagueN    DivisionW      PutOuts      Assists 
##   0.09780402   0.07189612  13.68370191 -54.65877750   0.11852289   0.01606037 
##       Errors   NewLeagueN 
##  -0.70358655   8.61181213
sqrt(sum(coef(ridge_mod)[-1,60]^2)) # Oblicz normę l2
## [1] 57.11001

Funkcję predict() możemy wykorzystać do wielu celów. Na przykład, możemy uzyskać współczynniki regresji grzbietowej dla nowej wartości \(\lambda\), powiedzmy 50:

##   (Intercept)         AtBat          Hits         HmRun          Runs 
##  4.876610e+01 -3.580999e-01  1.969359e+00 -1.278248e+00  1.145892e+00 
##           RBI         Walks         Years        CAtBat         CHits 
##  8.038292e-01  2.716186e+00 -6.218319e+00  5.447837e-03  1.064895e-01 
##        CHmRun         CRuns          CRBI        CWalks       LeagueN 
##  6.244860e-01  2.214985e-01  2.186914e-01 -1.500245e-01  4.592589e+01 
##     DivisionW       PutOuts       Assists        Errors    NewLeagueN 
## -1.182011e+02  2.502322e-01  1.215665e-01 -3.278600e+00 -9.496680e+00

Podzielimy teraz próbki na zbiór treningowy i testowy w celu oszacować błąd testu regresji grzbietowej i lasso.

Następnie dopasowujemy model regresji grzbietowej na zbiorze treningowym i oceniamy jego MSE na zbiorze testowym, używając \(\lambda = 4\). Zwróć uwagę na użycie funkcji predict(). Ponownie: tym razem otrzymujemy przewidywania dla zbioru testowego, zastępując type="coefficients" argumentem newx.

## [1] 139858.6

Testowe MSE wynosi 139858. Zauważ, że gdybyśmy zamiast tego dopasowali po prostu model tylko z wyrazem wolnym, przewidywalibyśmy każdą obserwację testową używając średniej z obserwacji zbioru treningowego. W takim przypadku moglibyśmy obliczyć MSE zestawu testowego w ten sposób:

## [1] 224692.1

Moglibyśmy również uzyskać ten sam wynik, dopasowując model regresji grzbietowej z bardzo dużą wartością \(\lambda\). Zauważ, że 1e10 oznacza \(10^{10}\).

## [1] 224692.1

Tak więc dopasowanie modelu regresji grzbietowej z \(\lambda = 4\) prowadzi do znacznie niższego testu MSE niż dopasowanie modelu z samym przechwytem.

Sprawdzimy teraz, czy jest jakaś korzyść z wykonania regresji grzbietowej z \(\lambda = 4\) zamiast po prostu wykonać regresję najmniejszych kwadratów.

Przypomnijmy, że najmniejsza kwadratura to po prostu regresja grzbietowa z \(\lambda = 0\).

* Uwaga: Aby glmnet() dawał dokładne (exact) współczynniki najmniejszego kwadratu, gdy \(\lambda = 0\), używamy argumentu exact=T przy wywołaniu funkcji predict(). W przeciwnym razie, funkcja predict() będzie interpolować nad siatką wartości \(\lambda\) użytą w dopasowaniu modelu glmnet(), dając przybliżone wyniki. Nawet gdy użyjemy exact = T, pozostaje niewielka rozbieżność na trzecim miejscu po przecinku między wynikami glmnet(), gdy \(\lambda = 0\) i wyjściem z lm(); jest to spowodowane numerycznym przybliżeniem ze strony glmnet().

## [1] 174060
## 
## Call:
## lm(formula = Salary ~ ., data = train)
## 
## Coefficients:
## (Intercept)        AtBat         Hits        HmRun         Runs          RBI  
##   2.398e+02   -1.639e-03   -2.179e+00    6.337e+00    7.139e-01    8.735e-01  
##       Walks        Years       CAtBat        CHits       CHmRun        CRuns  
##   3.594e+00   -1.309e+01   -7.136e-01    3.316e+00    3.407e+00   -5.671e-01  
##        CRBI       CWalks      LeagueN    DivisionW      PutOuts      Assists  
##  -7.525e-01    2.347e-01    1.322e+02   -1.346e+02    2.099e-01    6.229e-01  
##      Errors   NewLeagueN  
##  -4.616e+00   -8.330e+01
##   (Intercept)         AtBat          Hits         HmRun          Runs 
##  239.89368111   -0.01946204   -2.07305757    6.44254692    0.64610179 
##           RBI         Walks         Years        CAtBat         CHits 
##    0.82179888    3.62448842  -13.28142313   -0.70314292    3.26064805 
##        CHmRun         CRuns          CRBI        CWalks       LeagueN 
##    3.33170237   -0.54000590   -0.72015101    0.22582579  131.41324242 
##     DivisionW       PutOuts       Assists        Errors    NewLeagueN 
## -134.76073238    0.20949301    0.61942855   -4.58545824  -82.35090554

Wygląda na to, że rzeczywiście poprawiamy się w stosunku do zwykłego najmniejszego kwadratu!

Uwaga: ogólnie, jeśli chcemy dopasować (niespenalizowany) model najmniejszych kwadratów, to powinniśmy użyć funkcji lm(), ponieważ ta funkcja dostarcza bardziej użytecznych wyjścia, takie jak błędy standardowe i wartości \(p\) dla współczynników.

Zamiast arbitralnie wybierać \(\lambda = 4\), lepiej byłoby użyć walidacji krzyżowej do wyboru parametru dostrojenia \(\lambda\). Możemy to zrobić używając wbudowanej funkcji walidacji krzyżowej, cv.glmnet(). Domyślnie funkcja ta wykonuje 10-krotną walidację krzyżową, choć można to zmienić używając argumentu argumentu folds. Zauważ, że najpierw ustawiamy losowe ziarno, aby nasze wyniki były powtarzalne, ponieważ wybór krotności walidacji krzyżowej jest losowy.

## [1] 326.1406

Widzimy zatem, że wartość \(\lambda\), która powoduje najmniejszy błąd walidacji krzyżowej to 326. Możemy również wykreślić MSE jako funkcję \(\lambda\):

Jaki jest testowy MSE związany z tą wartością \(\lambda\)?

ridge_pred = predict(ridge_mod, s = bestlam, newx = x_test) # Użyj najlepszej lambdy do przewidywania danych testowych
mean((ridge_pred - y_test)^2) # Oblicz testowe MSE
## [1] 140056.2

Stanowi to dalszą poprawę w stosunku do testowego MSE, które uzyskaliśmy używając \(\lambda = 4\). Ostatecznie, ponownie wyznaczamy nasz model regresji grzbietowej na pełnym zestawie danych, używając wartości \(\lambda\) wybranej w walidacji krzyżowej, i sprawdzamy oszacowania współczynników.

##  (Intercept)        AtBat         Hits        HmRun         Runs          RBI 
##  15.44834992   0.07716945   0.85906253   0.60120338   1.06366687   0.87936073 
##        Walks        Years       CAtBat        CHits       CHmRun        CRuns 
##   1.62437580   1.35296285   0.01134998   0.05746377   0.40678422   0.11455696 
##         CRBI       CWalks      LeagueN    DivisionW      PutOuts      Assists 
##   0.12115916   0.05299953  22.08942756 -79.03490992   0.16618830   0.02941513 
##       Errors   NewLeagueN 
##  -1.36075645   9.12528397

Zgodnie z oczekiwaniami, żaden ze współczynników nie jest dokładnie zerowy - regresja grzbietowa nie dokonuje selekcji zmiennych!

Regresja Lasso

Wprowadzenie

Zamiast regularyzacji \(L_2\), LASSO używa penalizacji \(L_1\), to znaczy:

\[ \min_{\beta \in \mathbb{R}^p} \sum_{i=1}^n (y_i - x_i^\top \beta)^2 + \lambda \|\beta\|_1. \]

Ze względu na charakter normy \(L_1\), LASSO ma tendencję do dawania bardziej rzadkich rozwiązań niż regresja grzbietowa. Jest to typowo użyteczne w ustawieniach wielowymiarowych, gdy prawdziwy model jest w rzeczywistości niskowymiarowym osadzeniem.

Model regresji lasso został pierwotnie opracowany w 1989 roku. Jest to alternatywa dla klasycznego oszacowania metodą najmniejszych kwadratów, która unika wielu problemów z nadmiernym dopasowaniem (overfittingiem), gdy mamy dużą liczbę niezależnych zmiennych.

Regresja Lasso (Least Absolute Shrinkage and Selection Operator) to technika regresji liniowej stosowana do oszacowania współczynników modelu, która wprowadza regularyzację \(L_1\). Regularyzacja L1 polega na dodaniu do funkcji celu kary proporcjonalnej do wartości bezwzględnej współczynników regresji.

Regresja Lasso ma zdolność do jednoczesnego wykonania selekcji cech i regularyzacji, co oznacza, że może pomóc w identyfikacji najbardziej istotnych cech modelu, a także zmniejszyć wpływ mniej istotnych cech.

Podstawowym celem regresji Lasso jest minimalizacja funkcji celu, która składa się z dwóch składników: błędu dopasowania (sumy kwadratów różnic pomiędzy rzeczywistymi wartościami odpowiedzi a przewidywanymi wartościami modelu) i kary regularyzacyjnej \(L_1\).

Wzór funkcji celu dla regresji Lasso może być przedstawiony jako: Minimize: RSS + \(\lambda \|\beta\|_1\), gdzie:

  • RSS to suma kwadratów różnic pomiędzy rzeczywistymi wartościami odpowiedzi a przewidywanymi wartościami modelu (błąd dopasowania),

  • \(\lambda\) (lambda) to parametr regularyzacji, który kontroluje siłę regularyzacji, a \(\|\beta\|_1\) to norma \(L_1\) współczynników regresji.

Dodanie kary regularyzacyjnej \(L_1\) powoduje, że niektóre współczynniki regresji stają się równe zero, co prowadzi do selekcji cech. Im większa wartość \(\lambda\), tym większa jest tendencja do redukcji współczynników do zera, prowadząc do bardziej rzadkiego modelu z mniejszą liczbą cech.

Regresja Lasso jest przydatna w przypadkach, gdy mamy do czynienia z wieloma cechami, z których niektóre mogą być nieistotne. Może pomóc w identyfikacji istotnych cech, redukcji nadmiaru danych i zwiększeniu interpretowalności modelu.

Przykład

Zobaczyliśmy, że regresja grzbietowa z mądrym wyborem \(\lambda\) może przewyższać metodę najmniejszych kwadratów, jak również model zerowy na zbiorze danych Hitters.

Teraz zobaczmy, czy lasso może dać albo dokładniejszy, albo bardziej interpretowalny model niż regresja grzbietowa.

W celu dopasowania modelu lasso, po raz kolejny używamy funkcji glmnet(), jednak tym razem używamy argumentu alpha=1. Poza tą zmianą postępujemy tak samo jak w przypadku dopasowywania modelu regresji grzbietowej:

Zauważmy, że na wykresie współczynników, w zależności od wyboru dostrojenia parametru, niektóre ze współczynników są dokładnie równe zeru. Teraz przeprowadzimy walidację krzyżową i obliczymy związany z nią błąd testu:

## [1] 143273

Jest to znacznie niższe MSE zbioru testowego niż modelu zerowego i modelu najmniejszych kwadratów, i bardzo podobny do MSE testu regresji grzbietowej z \(\lambda\) wybranej przez walidację krzyżową.

Jednakże lasso ma istotną przewagę nad regresją grzbietową w tym, że wynikowe oszacowania współczynników są rzadkie. Tutaj widzimy, że 12 z 19 oszacowań współczynników jest dokładnie zerowych:

Wybierając tylko predyktory o niezerowych współczynnikach widzimy, że model lasso z \(\lambda\) wybranym przez walidację krzyżową zawiera tylko siedem zmiennych:

Twoja kolej!

Który zbiór danych wybrałeś?

“auto.csv” z An Introduction to Statistical Learning

Jaka była Twoja zmienna zależna (tzn. co próbowałeś modelować)?

“mpg” - miles per galon of fuel (continuous variable)

Specyfikacja modeli

Zmienną zależną wybrano mpg (miles per gallon). Zmiennymi niezależnymi były: cylinders, displacement, horsepower, weight oraz acceleration. Modele regresji grzbietowej (Ridge) i LASSO zostały skonstruowane, aby zredukować potencjalne problemy z multikolinearnością i zbadać wpływ regularizacji na dopasowanie modelu.

Czy oczekiwałeś, że regresja grzbietowa będzie lepsza od lasso, czy odwrotnie? Jak wypada w stosunku do OLS? Pokaż odpowiednie raporty, miary dopasowania i krótko je omów (porównaj).

Na podstawie wyników regresji:

• OLS MSE wynosi 17.23, co jest zbliżone do wyników Ridge (MSE = 17.15) i LASSO (MSE = 17.04).

• Wyniki sugerują, że OLS jest porównywalny pod względem błędu do Ridge i LASSO, jednak regularizacja (Ridge i LASSO) ma potencjał do lepszej generalizacji w przypadku większej zmienności danych.

• Oczekiwałem, że LASSO będzie nieco lepsze od Ridge w uproszczeniu modelu i eliminacji mniej istotnych zmiennych, co potwierdzają wyniki.

Które predyktory okazały się ważne w ostatecznym modelu (modelach)?

1. OLS:

• weight (p-value < 0.001) i horsepower (p-value < 0.05) to najważniejsze predyktory. Współczynniki są ujemne, co oznacza, że większa waga i moc silnika zmniejszają wydajność paliwa (mpg). • Pozostałe zmienne (cylinders, displacement, acceleration) okazały się statystycznie nieistotne.

2. LASSO:

• weight i horsepower również okazały się kluczowe. LASSO wyeliminowało mniej istotne zmienne (cylinders, displacement, acceleration), przypisując im współczynniki równe zero.

3. Ridge:

• Uwzględnił wszystkie zmienne, ale największe znaczenie przypisano weight i horsepower.

##   mpg cylinders displacement horsepower weight acceleration year origin
## 1  18         8          307        130   3504         12.0   70      1
## 2  15         8          350        165   3693         11.5   70      1
## 3  18         8          318        150   3436         11.0   70      1
## 4  16         8          304        150   3433         12.0   70      1
## 5  17         8          302        140   3449         10.5   70      1
## 6  15         8          429        198   4341         10.0   70      1
##                        name
## 1 chevrolet chevelle malibu
## 2         buick skylark 320
## 3        plymouth satellite
## 4             amc rebel sst
## 5               ford torino
## 6          ford galaxie 500
##   mpg_Min cylinders_Min displacement_Min horsepower_Min weight_Min
## 1       9             3               68             46       1613
##   acceleration_Min year_Min origin_Min mpg_Q1 cylinders_Q1 displacement_Q1
## 1                8       70          1     17            4             105
##   horsepower_Q1 weight_Q1 acceleration_Q1 year_Q1 origin_Q1 mpg_Median
## 1            75   2225.25          13.775      73         1      22.75
##   cylinders_Median displacement_Median horsepower_Median weight_Median
## 1                4                 151              93.5        2803.5
##   acceleration_Median year_Median origin_Median mpg_Mean cylinders_Mean
## 1                15.5          76             1 23.44592       5.471939
##   displacement_Mean horsepower_Mean weight_Mean acceleration_Mean year_Mean
## 1           194.412        104.4694    2977.584          15.54133  75.97959
##   origin_Mean mpg_Q3 cylinders_Q3 displacement_Q3 horsepower_Q3 weight_Q3
## 1    1.576531     29            8          275.75           126   3614.75
##   acceleration_Q3 year_Q3 origin_Q3 mpg_Max cylinders_Max displacement_Max
## 1          17.025      79         2    46.6             8              455
##   horsepower_Max weight_Max acceleration_Max year_Max origin_Max   mpg_SD
## 1            230       5140             24.8       82          3 7.805007
##   cylinders_SD displacement_SD horsepower_SD weight_SD acceleration_SD  year_SD
## 1     1.705783         104.644      38.49116  849.4026        2.758864 3.683737
##   origin_SD
## 1 0.8055182
## Ridge Regression:
## Best Lambda: 0.6628973
## Mean Squared Error: 17.14577
## LASSO Regression:
## Best Lambda: 0.07621708
## Mean Squared Error: 17.03969
## 
## Call:
## lm(formula = mpg ~ cylinders + displacement + horsepower + weight + 
##     acceleration, data = train_data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -11.7969  -2.8373  -0.5557   2.3910  16.1053 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  46.1705125  2.9764623  15.512  < 2e-16 ***
## cylinders    -0.6109538  0.4511213  -1.354   0.1766    
## displacement  0.0036826  0.0099461   0.370   0.7114    
## horsepower   -0.0411272  0.0187688  -2.191   0.0292 *  
## weight       -0.0054348  0.0009086  -5.981 6.15e-09 ***
## acceleration  0.0330668  0.1397960   0.237   0.8132    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 4.279 on 307 degrees of freedom
## Multiple R-squared:  0.7144, Adjusted R-squared:  0.7097 
## F-statistic: 153.6 on 5 and 307 DF,  p-value: < 2.2e-16
## OLS Mean Squared Error: 17.23451

Wykresy poprawności dopasowania danych dla regresji OLS

LS0tCnRpdGxlOiAnTmlla2xhc3ljem5lIG1ldG9keSBzdGF0eXN0eWtpJwpzdWJ0aXRsZTogJ1JlZ3VsYXJ5emFjamEnCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKYXV0aG9yOiAiTWFyY2luIFdpbGsiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgZm9udHNpemU6IDEwcHQKICAgIHRvYzogeWVzCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBubwogICAgZGZfcHJpbnQ6IGRlZmF1bHQKICAgIHRvY19kZXB0aDogNQplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1GQUxTRX0KbGlicmFyeShJU0xSKQpsaWJyYXJ5KGdsbW5ldCkKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKYGBgCgpUbyBsYWJvcmF0b3JpdW0gbmEgdGVtYXQgUmVncmVzamkgZ3J6YmlldG93ZWogKFJpZGdlIFJlZ3Jlc3Npb24gLSBSUikgaQpMYXNzbyB3IFIgcG9jaG9kemkgemUgc3Ryb24gMjUxLTI1NSBrc2nEhcW8a2kgIkludHJvZHVjdGlvbiB0byBTdGF0aXN0aWNhbApMZWFybmluZyB3aXRoIEFwcGxpY2F0aW9ucyBpbiBSIiBhdXRvcnN0d2EgR2FyZXRoYSBKYW1lc2EsIERhbmllbGkKV2l0dGVuLCBUcmV2b3JhIEhhc3RpZSBpIFJvYmVydGEgVGlic2hpcmFuaS4gWm9zdGHFgm8gb25vIHBvbm93bmllCnphaW1wbGVtZW50b3dhbmUgamVzaWVuacSFIDIwMTYgcm9rdSB3IGZvcm1hY2llIGB0aWR5dmVyc2VgIHByemV6IEFtZWxpxJkKTWNOYW1hcsSZIGkgUi4gSm9yZGFuYSBDcm91c2VyYSB3IFNtaXRoIENvbGxlZ2UuCgpXIHR5bSB0eWdvZG5pdSBvbcOzd2lteSBkd2llIGFsdGVybmF0eXduZSBmb3JteSByZWdyZXNqaSBsaW5pb3dlaiB6d2FuZQoqKnJlZ3Jlc2rEhSBncnpiaWV0b3fEhSoqIGkgKipyZWdyZXNqxIUgTEFTU08qKi4gVGUgZHdpZSBtZXRvZHkgc8SFCnByenlrxYJhZGFtaSBtZXRvZCAqKnJlZ3VsYXJ5emFjamkqKiBsdWIgKip6bW5pZWpzemFuaWEqKiwgdyBrdMOzcnljaAp6YWNoxJljYSBzacSZIGRvIHRlZ28sIGFieSBwYXJhbWV0cnkgbW9kZWx1IGJ5xYJ5IG1hxYJlLgoKIyBSZWdyZXNqYSBHcnpiaWV0b3dhIGkgTGFzc28KCld5a29yenlzdGFteSBwYWtpZXQgYGdsbW5ldGAgdyBjZWx1IHByemVwcm93YWR6ZW5pYSByZWdyZXNqaSByaWRnZSBpCmxhc3NvLiBHxYLDs3duxIUgZnVua2NqxIUgdyB0eW0gcGFraWVjaWUgamVzdCBgZ2xtbmV0KClgLCBrdMOzcmEgbW/FvGUgYnnEhwp1xbx5dGEgZG8gZG9wYXNvd2FuaWEgbW9kZWxpIHJlZ3Jlc2ppIGdyemJpZXRvd2VqLCBtb2RlbGkgbGFzc28gaSBpbm55Y2guCgpGdW5rY2phIHRhIG1hIG5pZWNvIGlubsSFIHNrxYJhZG5pxJkgbmnFvCBpbm5lIGZ1bmtjamUgZG9wYXNvd3VqxIVjZSBtb2RlbGUsCnoga3TDs3J5bWkgemV0a27EmWxpxZtteSBzacSZIGRvIHRlaiBwb3J5LiBXIHN6Y3plZ8OzbG5vxZtjaSwgbXVzaW15IHByemVrYXphxIcKbWFjaWVyeiAkeCQgamFrIHLDs3duaWXFvCB3ZWt0b3IgJHkkIGkgbmllIHXFvHl3YW15IHNrxYJhZG5pICR5IFxzaW0geCQuCgpaYW5pbSBwcnplamR6aWVteSBkYWxlaiwgdXBld25pam15IHNpxJkgbmFqcGllcncsIMW8ZSBicmFrdWrEhWNlIHdhcnRvxZtjaQp6b3N0YcWCeSB6b3N0YcWCeSB1c3VuacSZdGUgeiBkYW55Y2gsIGphayBvcGlzYW5vIHcgcG9wcnplZG5pbQpsYWJvcmF0b3JpdW0uCgpgYGB7cn0KSGl0dGVycyA9IG5hLm9taXQoSGl0dGVycykKYGBgCgpXIHJhcG9yY2llIHR5bSBwcnplcHJvd2FkemlteSByZWdyZXNqxJkgZ3J6YmlldG93xIUgaSBsYXNzbywgYWJ5CnByemV3aWR6aWXEhyBgU2FsYXJ5YCBuYSBkYW55Y2ggYEhpdHRlcnNgLgoKU2tvbmZpZ3VydWpteSBuYXN6ZSBkYW5lOgoKYGBge3J9CnggPSBtb2RlbC5tYXRyaXgoU2FsYXJ5fi4sIEhpdHRlcnMpWywtMV0gIyBwcnp5Y2luYW0gcGllcndzesSFIGtvbHVtbsSZCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB6b3N0YXdpYW0gcHJlZHlrdG9yeQp5ID0gSGl0dGVycyAlPiUKICBzZWxlY3QoU2FsYXJ5KSAlPiUKICB1bmxpc3QoKSAlPiUKICBhcy5udW1lcmljKCkKYGBgCgpGdW5rY2phIGBtb2RlbC5tYXRyaXgoKWAgamVzdCBzemN6ZWfDs2xuaWUgcHJ6eWRhdG5hIGRvIHR3b3J6ZW5pYSAkeCQ7Cm5pZSB0eWxrbyBuaWUgdHlsa28gdHdvcnp5IG1hY2llcnogb2Rwb3dpYWRhasSFY8SFIDE5IHByZWR5a3Rvcm9tLCBhbGUKcsOzd25pZcW8IGF1dG9tYXR5Y3puaWUgcHJ6ZWtzenRhxYJjYSB3c3plbGtpZSB6bWllbm5lIGpha2/Fm2Npb3dlIHcgem1pZW5uZQpkdW1teS4KClRhIG9zdGF0bmlhIHfFgmHFm2Npd2/Fm8SHIGplc3Qgd2HFvG5hLCBwb25pZXdhxbwgYGdsbW5ldCgpYCBtb8W8ZSBwcnp5am1vd2HEhwp0eWxrbyBudW1lcnljem5lLCBpbG/Fm2Npb3dlIGRhbmUgd2VqxZtjaW93ZS4KCiMjIEJpYXMgdnMgVmFyaWFuY2UKCld5YsOzciBtb2RlbHUgdyBwcm9ibGVtYWNoIHVjemVuaWEgbmFkem9yb3dhbmVnbyB3acSFxbxlIHNpxJkgeiByZWFsaXphY2rEhQpkd8OzY2ggc3ByemVjem55Y2ggY2Vsw7N3OgoKMS4pIE1vZGVsIHBvd2llbmllbiBiecSHIGRvYnJ6ZSBkb3Bhc293YW55IGRvIGRhbnljaCB1Y3rEhWN5Y2gsIGFieQp1Y2h3eWNpxIcgemFsZcW8bm/Fm8SHIHBvbWnEmWR6eSBkYW55bWkuCgoyLikgTW9kZWwgcG93aW5pZW4gZG9icnplIHByenlibGnFvGHEhyBuaWV6bmFuZSBkYW5lICh6YXBld25pYcSHIG1hxYJ5IGLFgsSFZApnZW5lcmFsaXphY2ppKS4KCk1vZGVsZSB6xYJvxbxvbmUgZG9iZHJ6ZSBkb3Bhc293dWrEhSBzacSZIGRvIGRhbnljaCB3eWrFm2Npb3d5Y2gsIGFsZQpjaGFyYWt0ZXJ5enVqxIUgc2nEmSBkdcW8xIUgem1pZW5ub8WbY2nEhSB3YXJ0b8WbY2kgd3lqxZtjaW93eWNoLiBSeXp5a2llbSBqZXN0Cm5hZG1pZXJuZSBkb3Bhc293YW5pZSA9IG92ZXJmaXR0aW5nIQoKTW9kZWxlIHByb3N0c3plIHPEhSBvYmNpxIXFvG9uZSBkdcW8eW0gYsWCxJlkZW0gc3lzdGVtYXR5Y3pueSAoYmlhcykgaSBpY2gKemFzdG9zb3dhbmllIG5pZXNpZSByeXp5a28gbmlld3lzdGFyY3phasSFY2VnbyBkb3Bhc293YW5pYQoodW5kZXJmaXR0aW5nKSEKClNrxYJhZG5pa2llbSBixYLEmWTDs3cgZ2VuZXJhbGl6YWNqaSBqZXN0IG5pZXJlZHVrb3dhbG55IGLFgsSFZCB6d2nEhXphbnkgemUKem1pZW5ub8WbY2nEhSBkYW55Y2guCgojIyBSZWd1bGFyeXphY2phCgpEdcW8YSBsaWN6bmEgem1pZW5ueWNoIG9iamHFm25pYWrEhWN5Y2ggKHByZWR5a3RvcsOzdyk6IE1ldG9kYSBPTFMgbmllIGRhamUKamVkbm96bmFjem5lZ28gcm96d2nEhXphbmlhLCBnZHkgbWFjaWVyeiBYVFggbmllIGplc3Qgb2R3cmFjYWxuYSAodHpuLgpnZHkgem1pZW5uZSBvYmphxZtuaWFqxIVjZSBzxIUgbGluaW93byB6YWxlxbxuZSkuCgpUYWthIHN5dHVhY2phIG1vxbxlIG1pZcSHIG1pZWpzY2UgZ2R5IHptaWVubnljaCBvYmphxZtuaWFqxIVjeWNoIGplc3QgdHlsZQpzYW1vIGx1YiB3acSZY2VqIG5pxbwgb2JzZXJ3YWNqaS4KCkR1xbxhIHdhcnRvxZvEhyDOuGkgb3puYWN6YSBkdcW8xIUgd3JhxbxsaXdvxZvEhyBmdW5rY2ppIHJlZ3Jlc2ppIG5hIGRyb2JuZQpmbHVrdHVhY2plIGNlY2h5IQoKTGVwc3p5bSByb3p3acSFemFuaWVtIGplc3QgZ29yc3plIGRvcGFzb3dhbmllIGRvIGRhbnljaCB1Y3rEhWN5Y2ggcHJ6eQpyw7N3bm9jemVzbnltIG9ncmFuaWN6ZW5pdSBwYXJhbWV0csOzdyDFm3dpYWRjesSFY3ljaCBvIHBvdGVuY2phbG5pZSBkdcW8eW0KYsWCxJlkemllIGdlbmVyYWxpemFjamkuCgojIyBSZWdyZXNqYSBHcnpiaWV0b3dhCgojIyMgV3Byb3dhZHplbmllCgpSZWdyZXNqYSBncnpiaWV0b3dhIChhbmcuIFJpZGdlIHJlZ3Jlc3Npb24pIHRvIHRlY2huaWthIHJlZ3Jlc2ppCmxpbmlvd2VqLCBrdMOzcmEgd3Byb3dhZHphIHJlZ3VsYXJ5emFjasSZICRMXzIkIGRvIGVzdHltYWNqaQp3c3DDs8WCY3p5bm5pa8OzdyBtb2RlbHUuIFJlZ3VsYXJ5emFjamEgJExfMiQgcG9sZWdhIG5hIGRvZGFuaXUgZG8gZnVua2NqaQpjZWx1IGthcnkgcHJvcG9yY2pvbmFsbmVqIGRvIGt3YWRyYXR1IHdhcnRvxZtjaSB3c3DDs8WCY3p5bm5pa8OzdyByZWdyZXNqaS4KClBvZHN0YXdvd8SFIGlkZcSFIHJlZ3Jlc2ppIGdyemJpZXRvd2VqIGplc3QgbWluaW1hbGl6YWNqYSBmdW5rY2ppIGNlbHUsCmt0w7NyYSBza8WCYWRhIHNpxJkgeiBkd8OzY2ggc2vFgmFkbmlrw7N3OiBixYLEmWR1IGRvcGFzb3dhbmlhIChzdW15IGt3YWRyYXTDs3cKcsOzxbxuaWMgcG9tacSZZHp5IHJ6ZWN6eXdpc3R5bWkgd2FydG/Fm2NpYW1pIG9kcG93aWVkemkgYSBwcnpld2lkeXdhbnltaQp3YXJ0b8WbY2lhbWkgbW9kZWx1KSBpIGthcnkgcmVndWxhcnl6YWN5am5laiAkTF8yJC4KCld6w7NyIGZ1bmtjamkgY2VsdSBkbGEgcmVncmVzamkgZ3J6YmlldG93ZWogbW/FvG5hIHByemVkc3Rhd2nEhyBqYWtvOgpNaW5pbWl6ZTogUlNTICsgJFxsYW1iZGEgXHxcYmV0YVx8XzJeMiQsIGdkemllOgoKLSAgIFJTUyB0byBzdW1hIGt3YWRyYXTDs3cgcsOzxbxuaWMgcG9tacSZZHp5IHJ6ZWN6eXdpc3R5bWkgd2FydG/Fm2NpYW1pCiAgICBvZHBvd2llZHppIGEgcHJ6ZXdpZHl3YW55bWkgd2FydG/Fm2NpYW1pIG1vZGVsdSAoYsWCxIVkIGRvcGFzb3dhbmlhKSwKCi0gICAkXGxhbWJkYSQgKGxhbWJkYSkgdG8gcGFyYW1ldHIgcmVndWxhcnl6YWNqaSwga3TDs3J5IGtvbnRyb2x1amUgc2nFgsSZCiAgICByZWd1bGFyeXphY2ppLAoKLSAgICRcfFxiZXRhXHxfMl4yJCB0byBub3JtYSAkTF8yJCB3c3DDs8WCY3p5bm5pa8OzdyByZWdyZXNqaSBwb2RuaWVzaW9uYQogICAgZG8ga3dhZHJhdHUuCgpEb2RhbmllIGthcnkgcmVndWxhcnl6YWN5am5laiAkTF8yJCBwb3dvZHVqZSwgxbxlIHdzcMOzxYJjenlubmlraSByZWdyZXNqaQpzxIUgc2t1cGlvbmUgd29rw7PFgiB6ZXJhLCBhbGUgbmllIGRva8WCYWRuaWUgcsOzd25lIHplcnUgKGNoeWJhIMW8ZQokXGxhbWJkYSQ9MCkuCgpSZWdyZXNqYSBncnpiaWV0b3dhIHptbmllanN6YSB3YXJ0b8WbY2kgd3Nww7PFgmN6eW5uaWvDs3csIGFsZSBuaWUgcG93b2R1amUsCsW8ZSBzdGFqxIUgc2nEmSBvbmUgcsOzd25lIHplcm8uIEltIHdpxJlrc3phIHdhcnRvxZvEhyAkXGxhbWJkYSQsIHR5bSBiYXJkemllagpzxIUgInNjaXNrYW5lIiB3c3DDs8WCY3p5bm5pa2kgcmVncmVzamkuCgpSZWdyZXNqYSBncnpiaWV0b3dhIGplc3Qgc3pjemVnw7NsbmllIHByenlkYXRuYSwgZ2R5IG1hbXkgZG8gY3p5bmllbmlhIHoKbW9kZWxlbSwgdyBrdMOzcnltIHd5c3TEmXB1amUgbmFkbWllcm5hIHdpZWxvd3ltaWFyb3dvxZvEhyBsdWIgd3lzb2tpZQprb3JlbGFjamUgbWnEmWR6eSB6bWllbm55bWkgbmllemFsZcW8bnltaS4KClBvcHJ6ZXogem1uaWVqc3phbmllIHdhcnRvxZtjaSB3c3DDs8WCY3p5bm5pa8OzdywgcmVncmVzamEgZ3J6YmlldG93YSBtb8W8ZQpwb23Ds2MgdyByZWR1a2NqaSB3cMWCeXd1IG1hxYJvIGlzdG90bnljaCBjZWNoLCBwb3ByYXdpxIcgc3RhYmlsbm/Fm8SHIG1vZGVsdQppIHptbmllanN6ecSHIHJ5enlrbyBwcnpldWN6ZW5pYSAoKipvdmVyZml0dGluZyoqKS4KCkplZG55bSB6ZSBzcG9zb2LDs3cga29udHJvbGkgesWCb8W8b25vxZtjaSBtb2RlbHUgamVzdCBwZW5hbGl6YWNqYSBqZWdvCndpZWxrb8WbY2kuIE5hIHByenlrxYJhZCwgdyBwcm9ibGVtaWUgcmVncmVzamkgbGluaW93ZWo6CgokJApcbWluX3tcYmV0YSBcaW4gXG1hdGhiYntSfV5wfSBcc3VtX3tpPTF9Xm4gKHlfaSAtIHhfaV5cdG9wIFxiZXRhKV4yLAokJAoKbW/FvGVteSBrb250cm9sb3dhxIcgd2llbGtvxZvEhyB3c3DDs8WCY3p5bm5pa8OzdyAkXGJldGEkLiBPY3p5d2nFm2NpZSB3aWVsa2/Fm8SHCiRcYmV0YSQgbW/FvG5hIHpkZWZpbmlvd2HEhyBuYSByw7PFvG5lIHNwb3NvYnksIG5wLiBub3JtYS0yOiAkXHxcYmV0YVx8XzIkLApub3JtYS0xOiAkXHxcYmV0YVx8XzEkIGN6eSBub3JtYS1uaWVza2/FhGN6b25vxZvEhzogJFx8XGJldGFcfF97XGluZnR5fSQuClJlZ3Jlc2phIGdyemJpZXRvd2Egd2nEhcW8ZSBzacSZIHoga2FyxIUgZHfDs2NoIG5vcm06CgokJApcbWluX3tcYmV0YSBcaW4gXG1hdGhiYntSfV5wfSBcc3VtX3tpPTF9Xm4gKHlfaSAtIHhfaV5cdG9wIFxiZXRhKV4yICsgXGxhbWJkYSBcfFxiZXRhXHxfMl4yCiQkCgpnZHppZSAkXGxhbWJkYSQgamVzdCBwYXJhbWV0cmVtIGtvbnRyb2x1asSFY3ltIHBvemlvbSByZWd1bGFyeXphY2ppLgpaYXV3YcW8LCDFvGUgJFgkIHRvIG1hY2llcnogJG4kIG5hICRwJCB3eW1pYXLDs3cgeiB3aWVyc3phbWk6ICR4X2leXHRvcCQsCm9yYXogJFkkIHRvICRuJCBuYSAxIHdla3RvciAkeV9pJC4gWmHFgsOzxbxteSwgxbxlICRYXlx0b3AgWCArIFxsYW1iZGEgSSQKamVzdCBvZHdyYWNhbG5hLCBtYW15IGRva8WCYWRuZSByb3p3acSFemFuaWUgcHJvYmxlbXUgcmVncmVzamkKZ3J6YmlldG93ZWo6CgokJApcaGF0IFxiZXRhX3tyaWRnZX0gPSAoWF5cdG9wIFggKyBcbGFtYmRhIEkpXnstMX1YXlx0b3AgWS4KJCQKClByenlwb21uaWpteSwgxbxlIHJvendpxIV6YW5pZW0gend5a8WCZWogcmVncmVzamkgbmFqbW5pZWpzenljaCBrd2FkcmF0w7N3Cmplc3QgKHpha8WCYWRhasSFYyBvZHdyYWNhbG5vxZvEhyBtYWNpZXJ6eSAkWF5cdG9wIFgkKToKCiQkClxoYXQgXGJldGFfe29sc30gPSAoWF5cdG9wIFgpXnstMX1YXlx0b3AgWS4KJCQKCkR3YSBmYWt0eToga2llZHkgJFxsYW1iZGEgXHRvIDAkLAokXGhhdCBcYmV0YV97cmlkZ2V9IFx0byBcaGF0IFxiZXRhX3tvbHN9JDsga2llZHkgJFxsYW1iZGEgXHRvIFxpbmZ0eSQsCiRcaGF0IFxiZXRhX3tyaWRnZX0gXHRvIDAkLgoKVyBzemN6ZWfDs2xueWNoIHByenlwYWRrYWNoICRYJCBqZXN0IG9ydG9nb25hbG5hICh0em4uIGtvbHVtbnkgJFgkIHPEhQpvcnRvZ29uYWxuZSksIG1hbXk6CgokJApcaGF0IFxiZXRhX3tyaWRnZX0gPSBcZnJhY3tcaGF0IFxiZXRhX3tvbHN9fXsxICsgXGxhbWJkYX0uCiQkCgpXaWR6aW15IHdpxJljLCDFvGUgZXN0eW1hdG9yIGdyemJpZXRvd3kgbWEgZG9kYXRrb3dvICQxLygxICsgXGxhbWJkYSkkCnR6dy4gInNocmlua2FnZSBmYWN0b3IiLiBXIHp3acSFemt1IHogdHltIG5hIGVzdHltYXRvcnplIGdyemJpZXRvd3ltCnd5c3TEmXB1amUgb2JjacSFxbxsaXdvxZvEhyAoYmlhcykuCgojIyMgUHJ6eWvFgmFkCgpGdW5rY2phIGBnbG1uZXQoKWAgcG9zaWFkYSBhcmd1bWVudCBhbGZhLCBrdMOzcnkgb2tyZcWbbGEsIGpha2kgdHlwIG1vZGVsdQpqZXN0IGRvcGFzb3d5d2FueS4KCkplxZtsaSBgYWxmYSA9IDBgIHRvIGRvcGFzb3d5d2FueSBqZXN0IG1vZGVsIHJlZ3Jlc2ppIGdyemJpZXRvd2VqLCBhCmplxZtsaSBgYWxmYSA9IDFgIHRvIGRvcGFzb3d5d2FueSBqZXN0IG1vZGVsIGxhc3NvLgoKTmFqcGllcncgZG9wYXNvd3VqZW15IG1vZGVsIHJlZ3Jlc2ppIGdyemJpZXRvd2VqOgoKYGBge3J9CmdyaWQgPSAxMF5zZXEoMTAsIC0yLCBsZW5ndGggPSAxMDApCnJpZGdlX21vZCA9IGdsbW5ldCh4LCB5LCBhbHBoYSA9IDAsIGxhbWJkYSA9IGdyaWQpCmBgYAoKRG9tecWbbG5pZSBmdW5rY2phIGBnbG1uZXQoKWAgd3lrb251amUgcmVncmVzasSZIGdyemJpZXRvd8SFIGRsYQphdXRvbWF0eWN6bmllIHd5YnJhbmVnbyB3eWJyYW5lZ28gemFrcmVzdSB3YXJ0b8WbY2kgJFxsYW1iZGEkLiBKZWRuYWvFvGUsCnR1dGFqIHd5YnJhbGnFm215IGltcGxlbWVudGFjasSZIGZ1bmtjasSZIHcgemFrcmVzaWUgd2FydG/Fm2NpIG9kCiRcbGFtYmRhID0gMTBeezEwfSQgZG8gJFxsYW1iZGEgPSAxMF57LTJ9JCwgemFzYWRuaWN6byBwb2tyeXdhasSFYyBwZcWCZW4KemFrcmVzIHNjZW5hcml1c3p5IG9kIG1vZGVsdSB6ZXJvd2VnbyB6YXdpZXJhasSFY2VnbyB0eWxrbyBwcnplY2h3eXQsIGRvCmRvcGFzb3dhbmlhIG5ham1uaWVqc3plZ28ga3dhZHJhdHUuCgpKYWsgd2lkYcSHLCBtb8W8ZW15IHLDs3duaWXFvCBvYmxpY3p5xIcgZG9wYXNvd2FuaWUgbW9kZWx1IGRsYSBrb25rcmV0bmVqCndhcnRvxZtjaSAkXGxhbWJkYSQsIGt0w7NyYSBuaWUgamVzdCBqZWRuxIUgeiBvcnlnaW5hbG55Y2ggd2FydG/Fm2NpIHNpYXRraS4KClphdXdhxbwsIMW8ZSBkb215xZtsbmllIGZ1bmtjamEgYGdsbW5ldCgpYCBzdGFuZGFyeXp1amUgem1pZW5uZSB0YWssIGJ5CmJ5xYJ5IHcgdGVqIHNhbWVqIHNrYWxpLiBBYnkgd3nFgsSFY3p5xIcgdG8gZG9tecWbbG5lIHVzdGF3aWVuaWUsIHXFvHlqCmFyZ3VtZW50dSBgc3RhbmRhcmRpemUgPSBGQUxTRWAuCgpaIGthxbxkxIUgd2FydG/Fm2NpxIUgJFxsYW1iZGEkIHp3acSFemFueSBqZXN0IHdla3RvciB3c3DDs8WCY3p5bm5pa8OzdyByZWdyZXNqaQpncnpiaWV0b3dlaiwgcHJ6ZWNob3d5d2FueSB3IG1hY2llcnp5LCBkbyBrdMOzcmVqIG1vxbxuYSB1enlza2HEhyBkb3N0xJlwCnByemV6IGBjb2VmKClgLiBXIHR5bSBwcnp5cGFka3UgamVzdCB0byBtYWNpZXJ6ICQyMCBcdGltZXMgMTAwJCwgeiAyMAp3aWVyc3phbWkgKHBvIGplZG55bSBkbGEga2HFvGRlZ28gcHJlZHlrdG9yYSwgcGx1cyBpbnRlcmNlcHQpIGkgMTAwCmtvbHVtbmFtaSAocG8gamVkbmVqIGRsYSBrYcW8ZGVqIHdhcnRvxZtjaSAkXGxhbWJkYSQpLgoKYGBge3IgZWNobz1GQUxTRX0KZGltKGNvZWYocmlkZ2VfbW9kKSkKcGxvdChyaWRnZV9tb2QpICAgICMgd3lrcmVzIHdzcMOzxYJjenlubmlrw7N3CmBgYAoKU3BvZHppZXdhbXkgc2nEmSwgxbxlIG9zemFjb3dhbmlhIHdzcMOzxYJjenlubmlrw7N3IGLEmWTEhSB6bmFjem5pZSBtbmllanN6ZSwgdwpzZW5zaWUgbm9ybXkgJGxfMiQsIGdkeSB1xbx5d2FuYSBqZXN0IGR1xbxhIHdhcnRvxZvEhyAkXGxhbWJkYSQsIHcKcG9yw7N3bmFuaXUgeiBtYcWCxIUgd2FydG/Fm2NpxIUgJFxsYW1iZGEkLgoKT3RvIHdzcMOzxYJjenlubmlraSwgZ2R5ICRcbGFtYmRhID0gMTE0OTgkLCB3cmF6IHogaWNoIG5vcm3EhSAkbF8yJDoKCmBgYHtyIGVjaG89RkFMU0V9CnJpZGdlX21vZCRsYW1iZGFbNTBdICMgV3nFm3dpZXRsIDUwLXTEhSB3YXJ0b8WbxIcgbGFtYmR5CmNvZWYocmlkZ2VfbW9kKVssNTBdICMgV3nFm3dpZXRsIHdzcMOzxYJjenlubmlraSB6d2nEhXphbmUgeiA1MC10xIUgd2FydG/Fm2NpxIUgbGFtYmR5CnNxcnQoc3VtKGNvZWYocmlkZ2VfbW9kKVstMSw1MF1eMikpICMgT2JsaWN6IG5vcm3EmSBsMgpgYGAKCkRsYSBrb250cmFzdHUsIG90byB3c3DDs8WCY3p5bm5pa2ksIGdkeSAkXGxhbWJkYSA9IDcwNSQsIHdyYXogeiBpY2ggJGxfMiQKbm9ybcSFLiBad3LDs8SHIHV3YWfEmSBuYSB6bmFjem5pZSB3acSZa3N6xIUgbm9ybcSZICRsXzIkIHdzcMOzxYJjenlubmlrw7N3Cnp3acSFemFueWNoIHogdMSFIG1uaWVqc3rEhSB3YXJ0b8WbY2nEhSAkXGxhbWJkYSQuCgpgYGB7cn0KcmlkZ2VfbW9kJGxhbWJkYVs2MF0gIyBXecWbd2lldGwgNjAtdMSFIHdhcnRvxZvEhyBsYW1iZHkKY29lZihyaWRnZV9tb2QpWyw2MF0gIyBXecWbd2lldGwgd3Nww7PFgmN6eW5uaWtpIHBvd2nEhXphbmUgeiA2MC10xIUgd2FydG/Fm8SHIGxhbWJkeQpzcXJ0KHN1bShjb2VmKHJpZGdlX21vZClbLTEsNjBdXjIpKSAjIE9ibGljeiBub3JtxJkgbDIKYGBgCgpGdW5rY2rEmSBgcHJlZGljdCgpYCBtb8W8ZW15IHd5a29yenlzdGHEhyBkbyB3aWVsdSBjZWzDs3cuIE5hIHByenlrxYJhZCwKbW/FvGVteSB1enlza2HEhyB3c3DDs8WCY3p5bm5pa2kgcmVncmVzamkgZ3J6YmlldG93ZWogZGxhIG5vd2VqIHdhcnRvxZtjaQokXGxhbWJkYSQsIHBvd2llZHpteSA1MDoKCmBgYHtyIGVjaG89RkFMU0V9CnByZWRpY3QocmlkZ2VfbW9kLCBzID0gNTAsIHR5cGUgPSAiY29lZmZpY2llbnRzIilbMToyMCxdCmBgYAoKUG9kemllbGlteSB0ZXJheiBwcsOzYmtpIG5hIHpiacOzciB0cmVuaW5nb3d5IGkgdGVzdG93eSB3IGNlbHUgb3N6YWNvd2HEhwpixYLEhWQgdGVzdHUgcmVncmVzamkgZ3J6YmlldG93ZWogaSBsYXNzby4KCmBgYHtyIGVjaG89RkFMU0V9CnNldC5zZWVkKDEpCgp0cmFpbiA9IEhpdHRlcnMgJT4lCiAgc2FtcGxlX2ZyYWMoMC41KSAjendpxJlrc3p5YyBkbyAwLjcvMC44Cgp0ZXN0ID0gSGl0dGVycyAlPiUKICBzZXRkaWZmKHRyYWluKQoKeF90cmFpbiA9IG1vZGVsLm1hdHJpeChTYWxhcnl+LiwgdHJhaW4pWywtMV0KeF90ZXN0ID0gbW9kZWwubWF0cml4KFNhbGFyeX4uLCB0ZXN0KVssLTFdCgp5X3RyYWluID0gdHJhaW4gJT4lCiAgc2VsZWN0KFNhbGFyeSkgJT4lCiAgdW5saXN0KCkgJT4lCiAgYXMubnVtZXJpYygpCgp5X3Rlc3QgPSB0ZXN0ICU+JQogIHNlbGVjdChTYWxhcnkpICU+JQogIHVubGlzdCgpICU+JQogIGFzLm51bWVyaWMoKQpgYGAKCk5hc3TEmXBuaWUgZG9wYXNvd3VqZW15IG1vZGVsIHJlZ3Jlc2ppIGdyemJpZXRvd2VqIG5hIHpiaW9yemUgdHJlbmluZ293eW0KaSBvY2VuaWFteSBqZWdvIE1TRSBuYSB6YmlvcnplIHRlc3Rvd3ltLCB1xbx5d2FqxIVjICRcbGFtYmRhID0gNCQuIFp3csOzxIcKdXdhZ8SZIG5hIHXFvHljaWUgZnVua2NqaSBgcHJlZGljdCgpYC4gUG9ub3duaWU6IHR5bSByYXplbSBvdHJ6eW11amVteQpwcnpld2lkeXdhbmlhIGRsYSB6YmlvcnUgdGVzdG93ZWdvLCB6YXN0xJlwdWrEhWMgYHR5cGU9ImNvZWZmaWNpZW50cyJgCmFyZ3VtZW50ZW0gYG5ld3hgLgoKYGBge3IgZWNobz1GQUxTRX0KcmlkZ2VfbW9kID0gZ2xtbmV0KHhfdHJhaW4sIHlfdHJhaW4sIGFscGhhPTAsIGxhbWJkYSA9IGdyaWQsIHRocmVzaCA9IDFlLTEyKQpyaWRnZV9wcmVkID0gcHJlZGljdChyaWRnZV9tb2QsIHMgPSA0LCBuZXd4ID0geF90ZXN0KQptZWFuKChyaWRnZV9wcmVkIC0geV90ZXN0KV4yKQpgYGAKClRlc3Rvd2UgTVNFIHd5bm9zaSAxMzk4NTguIFphdXdhxbwsIMW8ZSBnZHliecWbbXkgemFtaWFzdCB0ZWdvIGRvcGFzb3dhbGkKcG8gcHJvc3R1IG1vZGVsIHR5bGtvIHogd3lyYXplbSB3b2xueW0sIHByemV3aWR5d2FsaWJ5xZtteSBrYcW8ZMSFCm9ic2Vyd2FjasSZIHRlc3Rvd8SFIHXFvHl3YWrEhWMgxZtyZWRuaWVqIHogb2JzZXJ3YWNqaSB6YmlvcnUgdHJlbmluZ293ZWdvLiBXCnRha2ltIHByenlwYWRrdSBtb2dsaWJ5xZtteSBvYmxpY3p5xIcgTVNFIHplc3Rhd3UgdGVzdG93ZWdvIHcgdGVuIHNwb3PDs2I6CgpgYGB7ciBlY2hvPUZBTFNFfQptZWFuKChtZWFuKHlfdHJhaW4pIC0geV90ZXN0KV4yKQpgYGAKCk1vZ2xpYnnFm215IHLDs3duaWXFvCB1enlza2HEhyB0ZW4gc2FtIHd5bmlrLCBkb3Bhc293dWrEhWMgbW9kZWwgcmVncmVzamkKZ3J6YmlldG93ZWogeiBiYXJkem8gZHXFvMSFIHdhcnRvxZtjacSFICRcbGFtYmRhJC4gWmF1d2HFvCwgxbxlIGAxZTEwYCBvem5hY3phCiQxMF57MTB9JC4KCmBgYHtyIGVjaG89RkFMU0V9CnJpZGdlX3ByZWQgPSBwcmVkaWN0KHJpZGdlX21vZCwgcyA9IDFlMTAsIG5ld3ggPSB4X3Rlc3QpCm1lYW4oKHJpZGdlX3ByZWQgLSB5X3Rlc3QpXjIpCmBgYAoKVGFrIHdpxJljIGRvcGFzb3dhbmllIG1vZGVsdSByZWdyZXNqaSBncnpiaWV0b3dlaiB6ICRcbGFtYmRhID0gNCQKcHJvd2FkemkgZG8gem5hY3puaWUgbmnFvHN6ZWdvIHRlc3R1IE1TRSBuacW8IGRvcGFzb3dhbmllIG1vZGVsdSB6IHNhbXltCnByemVjaHd5dGVtLgoKU3ByYXdkemlteSB0ZXJheiwgY3p5IGplc3QgamFrYcWbIGtvcnp5xZvEhyB6IHd5a29uYW5pYSByZWdyZXNqaQpncnpiaWV0b3dlaiB6ICRcbGFtYmRhID0gNCQgemFtaWFzdCBwbyBwcm9zdHUgd3lrb25hxIcgcmVncmVzasSZCm5ham1uaWVqc3p5Y2gga3dhZHJhdMOzdy4KClByenlwb21uaWpteSwgxbxlIG5ham1uaWVqc3phIGt3YWRyYXR1cmEgdG8gcG8gcHJvc3R1IHJlZ3Jlc2phIGdyemJpZXRvd2EKeiAkXGxhbWJkYSA9IDAkLgoKXCogVXdhZ2E6IEFieSBgZ2xtbmV0KClgIGRhd2HFgiAqKmRva8WCYWRuZSAoZXhhY3QpKiogd3Nww7PFgmN6eW5uaWtpCm5ham1uaWVqc3plZ28ga3dhZHJhdHUsIGdkeSAkXGxhbWJkYSA9IDAkLCB1xbx5d2FteSBhcmd1bWVudHUgYGV4YWN0PVRgCnByenkgd3l3b8WCYW5pdSBmdW5rY2ppIGBwcmVkaWN0KClgLiBXIHByemVjaXdueW0gcmF6aWUsIGZ1bmtjamEKYHByZWRpY3QoKWAgYsSZZHppZSBpbnRlcnBvbG93YcSHIG5hZCBzaWF0a8SFIHdhcnRvxZtjaSAkXGxhbWJkYSQgdcW8eXTEhSB3CmRvcGFzb3dhbml1IG1vZGVsdSBgZ2xtbmV0KClgLCBkYWrEhWMgcHJ6eWJsacW8b25lIHd5bmlraS4gTmF3ZXQgZ2R5CnXFvHlqZW15IGBleGFjdCA9IFRgLCBwb3pvc3RhamUgbmlld2llbGthIHJvemJpZcW8bm/Fm8SHIG5hIHRyemVjaW0gbWllanNjdQpwbyBwcnplY2lua3UgbWnEmWR6eSB3eW5pa2FtaSBgZ2xtbmV0KClgLCBnZHkgJFxsYW1iZGEgPSAwJCBpIHd5asWbY2llbSB6CmBsbSgpYDsgamVzdCB0byBzcG93b2Rvd2FuZSBudW1lcnljem55bSBwcnp5YmxpxbxlbmllbSB6ZSBzdHJvbnkKYGdsbW5ldCgpYC4KCmBgYHtyIGVjaG89RkFMU0V9CnJpZGdlX3ByZWQgPSBwcmVkaWN0KHJpZGdlX21vZCwgcyA9IDAsIG5ld3ggPSB4X3Rlc3QpCm1lYW4oKHJpZGdlX3ByZWQgLSB5X3Rlc3QpXjIpCgpsbShTYWxhcnl+LiwgZGF0YSA9IHRyYWluKQpwcmVkaWN0KHJpZGdlX21vZCwgcyA9IDAsIHR5cGU9ImNvZWZmaWNpZW50cyIpWzE6MjAsXQpgYGAKCld5Z2zEhWRhIG5hIHRvLCDFvGUgcnplY3p5d2nFm2NpZSBwb3ByYXdpYW15IHNpxJkgdyBzdG9zdW5rdSBkbyB6d3lrxYJlZ28KbmFqbW5pZWpzemVnbyBrd2FkcmF0dSEKClV3YWdhOiBvZ8OzbG5pZSwgamXFm2xpIGNoY2VteSBkb3Bhc293YcSHIChuaWVzcGVuYWxpem93YW55KSBtb2RlbApuYWptbmllanN6eWNoIGt3YWRyYXTDs3csIHRvIHBvd2lubmnFm215IHXFvHnEhyBmdW5rY2ppIGBsbSgpYCwgcG9uaWV3YcW8IHRhCmZ1bmtjamEgZG9zdGFyY3phIGJhcmR6aWVqIHXFvHl0ZWN6bnljaCB3eWrFm2NpYSwgdGFraWUgamFrIGLFgsSZZHkKc3RhbmRhcmRvd2UgaSB3YXJ0b8WbY2kgJHAkIGRsYSB3c3DDs8WCY3p5bm5pa8Ozdy4KClphbWlhc3QgYXJiaXRyYWxuaWUgd3liaWVyYcSHICRcbGFtYmRhID0gNCQsIGxlcGllaiBiecWCb2J5IHXFvHnEhyB3YWxpZGFjamkKa3J6ecW8b3dlaiBkbyB3eWJvcnUgcGFyYW1ldHJ1IGRvc3Ryb2plbmlhICRcbGFtYmRhJC4gTW/FvGVteSB0byB6cm9iacSHCnXFvHl3YWrEhWMgd2J1ZG93YW5laiBmdW5rY2ppIHdhbGlkYWNqaSBrcnp5xbxvd2VqLCBgY3YuZ2xtbmV0KClgLgpEb215xZtsbmllIGZ1bmtjamEgdGEgd3lrb251amUgMTAta3JvdG7EhSB3YWxpZGFjasSZIGtyennFvG93xIUsIGNob8SHIG1vxbxuYQp0byB6bWllbmnEhyB1xbx5d2FqxIVjIGFyZ3VtZW50dSBhcmd1bWVudHUgYGZvbGRzYC4gWmF1d2HFvCwgxbxlIG5hanBpZXJ3CnVzdGF3aWFteSBsb3Nvd2Ugemlhcm5vLCBhYnkgbmFzemUgd3luaWtpIGJ5xYJ5IHBvd3RhcnphbG5lLCBwb25pZXdhxbwKd3liw7NyIGtyb3Rub8WbY2kgd2FsaWRhY2ppIGtyennFvG93ZWogamVzdCBsb3Nvd3kuCgpgYGB7ciBlY2hvPUZBTFNFfQpzZXQuc2VlZCgxKQpjdi5vdXQgPSBjdi5nbG1uZXQoeF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwKSAjIERvcGFzdWogbW9kZWwgcmVncmVzamkgZ3J6YmlldG93ZWogbmEgZGFueWNoIHRyZW5pbmdvd3ljaApiZXN0bGFtID0gY3Yub3V0JGxhbWJkYS5taW4gICMgV3liaWVyeiBsYW1kxJksIGt0w7NyYSBtaW5pbWFsaXp1amUgdHJlbmluZ293eSBNU0UgCmJlc3RsYW0KYGBgCgpXaWR6aW15IHphdGVtLCDFvGUgd2FydG/Fm8SHICRcbGFtYmRhJCwga3TDs3JhIHBvd29kdWplIG5ham1uaWVqc3p5IGLFgsSFZAp3YWxpZGFjamkga3J6ecW8b3dlaiB0byAzMjYuIE1vxbxlbXkgcsOzd25pZcW8IHd5a3JlxZtsacSHIE1TRSBqYWtvIGZ1bmtjasSZCiRcbGFtYmRhJDoKCmBgYHtyIGVjaG89RkFMU0V9CnBsb3QoY3Yub3V0KSAjIE5hcnlzdWogd3lrcmVzIHRyZW5pbmdvd2VnbyBNU0UgamFrbyBmdW5rY2rEmSBsYW1iZGEKYGBgCgpKYWtpIGplc3QgdGVzdG93eSBNU0UgendpxIV6YW55IHogdMSFIHdhcnRvxZtjacSFICRcbGFtYmRhJD8KCmBgYHtyfQpyaWRnZV9wcmVkID0gcHJlZGljdChyaWRnZV9tb2QsIHMgPSBiZXN0bGFtLCBuZXd4ID0geF90ZXN0KSAjIFXFvHlqIG5hamxlcHN6ZWogbGFtYmR5IGRvIHByemV3aWR5d2FuaWEgZGFueWNoIHRlc3Rvd3ljaAptZWFuKChyaWRnZV9wcmVkIC0geV90ZXN0KV4yKSAjIE9ibGljeiB0ZXN0b3dlIE1TRQpgYGAKClN0YW5vd2kgdG8gZGFsc3rEhSBwb3ByYXfEmSB3IHN0b3N1bmt1IGRvIHRlc3Rvd2VnbyBNU0UsIGt0w7NyZSB1enlza2FsacWbbXkKdcW8eXdhasSFYyAkXGxhbWJkYSA9IDQkLiBPc3RhdGVjem5pZSwgcG9ub3duaWUgd3l6bmFjemFteSBuYXN6IG1vZGVsCnJlZ3Jlc2ppIGdyemJpZXRvd2VqIG5hIHBlxYJueW0gemVzdGF3aWUgZGFueWNoLCB1xbx5d2FqxIVjIHdhcnRvxZtjaQokXGxhbWJkYSQgd3licmFuZWogdyB3YWxpZGFjamkga3J6ecW8b3dlaiwgaSBzcHJhd2R6YW15IG9zemFjb3dhbmlhCndzcMOzxYJjenlubmlrw7N3LgoKYGBge3IgZWNobz1GQUxTRX0Kb3V0ID0gZ2xtbmV0KHgsIHksIGFscGhhID0gMCkgIyBEb3Bhc3VqIG1vZGVsIHJlZ3Jlc2ppIGdyemJpZXRvd2VqIGRvIHBlxYJuZWdvIHpiaW9ydSBkYW55Y2gKcHJlZGljdChvdXQsIHR5cGUgPSAiY29lZmZpY2llbnRzIiwgcyA9IGJlc3RsYW0pWzE6MjAsXSAjIFd5xZt3aWV0bGFuaWUgd3Nww7PFgmN6eW5uaWvDs3cgcHJ6eSB1xbx5Y2l1IGxhbWJkYSB3eWJyYW5lZ28gcHJ6ZXogQ1YKYGBgCgpaZ29kbmllIHogb2N6ZWtpd2FuaWFtaSwgxbxhZGVuIHplIHdzcMOzxYJjenlubmlrw7N3IG5pZSBqZXN0IGRva8WCYWRuaWUKemVyb3d5IC0gcmVncmVzamEgZ3J6YmlldG93YSBuaWUgZG9rb251amUgc2VsZWtjamkgem1pZW5ueWNoIQoKIyMgUmVncmVzamEgTGFzc28KCiMjIyBXcHJvd2FkemVuaWUKClphbWlhc3QgcmVndWxhcnl6YWNqaSAkTF8yJCwgTEFTU08gdcW8eXdhIHBlbmFsaXphY2ppICRMXzEkLCB0byB6bmFjenk6CgokJApcbWluX3tcYmV0YSBcaW4gXG1hdGhiYntSfV5wfSBcc3VtX3tpPTF9Xm4gKHlfaSAtIHhfaV5cdG9wIFxiZXRhKV4yICsgXGxhbWJkYSBcfFxiZXRhXHxfMS4gCiQkCgpaZSB3emdsxJlkdSBuYSBjaGFyYWt0ZXIgbm9ybXkgJExfMSQsIExBU1NPIG1hIHRlbmRlbmNqxJkgZG8gZGF3YW5pYQpiYXJkemllaiByemFka2ljaCByb3p3acSFemHFhCBuacW8IHJlZ3Jlc2phIGdyemJpZXRvd2EuIEplc3QgdG8gdHlwb3dvCnXFvHl0ZWN6bmUgdyB1c3Rhd2llbmlhY2ggd2llbG93eW1pYXJvd3ljaCwgZ2R5IHByYXdkeml3eSBtb2RlbCBqZXN0IHcKcnplY3p5d2lzdG/Fm2NpIG5pc2tvd3ltaWFyb3d5bSBvc2FkemVuaWVtLgoKTW9kZWwgcmVncmVzamkgbGFzc28gem9zdGHFgiBwaWVyd290bmllIG9wcmFjb3dhbnkgdyAxOTg5IHJva3UuIEplc3QgdG8KYWx0ZXJuYXR5d2EgZGxhIGtsYXN5Y3puZWdvIG9zemFjb3dhbmlhIG1ldG9kxIUgbmFqbW5pZWpzenljaCBrd2FkcmF0w7N3LAprdMOzcmEgdW5pa2Egd2llbHUgcHJvYmxlbcOzdyB6IG5hZG1pZXJueW0gZG9wYXNvd2FuaWVtCigqKm92ZXJmaXR0aW5naWVtKiopLCBnZHkgbWFteSBkdcW8xIUgbGljemLEmSBuaWV6YWxlxbxueWNoIHptaWVubnljaC4KClJlZ3Jlc2phIExhc3NvIChMZWFzdCBBYnNvbHV0ZSBTaHJpbmthZ2UgYW5kIFNlbGVjdGlvbiBPcGVyYXRvcikgdG8KdGVjaG5pa2EgcmVncmVzamkgbGluaW93ZWogc3Rvc293YW5hIGRvIG9zemFjb3dhbmlhIHdzcMOzxYJjenlubmlrw7N3Cm1vZGVsdSwga3TDs3JhIHdwcm93YWR6YSByZWd1bGFyeXphY2rEmSAkTF8xJC4gUmVndWxhcnl6YWNqYSBMMSBwb2xlZ2EgbmEKZG9kYW5pdSBkbyBmdW5rY2ppIGNlbHUga2FyeSBwcm9wb3Jjam9uYWxuZWogZG8gd2FydG/Fm2NpIGJlend6Z2zEmWRuZWoKd3Nww7PFgmN6eW5uaWvDs3cgcmVncmVzamkuCgpSZWdyZXNqYSBMYXNzbyBtYSB6ZG9sbm/Fm8SHIGRvIGplZG5vY3plc25lZ28gd3lrb25hbmlhIHNlbGVrY2ppIGNlY2ggaQpyZWd1bGFyeXphY2ppLCBjbyBvem5hY3phLCDFvGUgbW/FvGUgcG9tw7NjIHcgaWRlbnR5ZmlrYWNqaSBuYWpiYXJkemllagppc3RvdG55Y2ggY2VjaCBtb2RlbHUsIGEgdGFrxbxlIHptbmllanN6ecSHIHdwxYJ5dyBtbmllaiBpc3RvdG55Y2ggY2VjaC4KClBvZHN0YXdvd3ltIGNlbGVtIHJlZ3Jlc2ppIExhc3NvIGplc3QgbWluaW1hbGl6YWNqYSBmdW5rY2ppIGNlbHUsIGt0w7NyYQpza8WCYWRhIHNpxJkgeiBkd8OzY2ggc2vFgmFkbmlrw7N3OiBixYLEmWR1IGRvcGFzb3dhbmlhIChzdW15IGt3YWRyYXTDs3cgcsOzxbxuaWMKcG9tacSZZHp5IHJ6ZWN6eXdpc3R5bWkgd2FydG/Fm2NpYW1pIG9kcG93aWVkemkgYSBwcnpld2lkeXdhbnltaQp3YXJ0b8WbY2lhbWkgbW9kZWx1KSBpIGthcnkgcmVndWxhcnl6YWN5am5laiAkTF8xJC4KCld6w7NyIGZ1bmtjamkgY2VsdSBkbGEgcmVncmVzamkgTGFzc28gbW/FvGUgYnnEhyBwcnplZHN0YXdpb255IGpha286Ck1pbmltaXplOiBSU1MgKyAkXGxhbWJkYSBcfFxiZXRhXHxfMSQsIGdkemllOgoKLSAgIFJTUyB0byBzdW1hIGt3YWRyYXTDs3cgcsOzxbxuaWMgcG9tacSZZHp5IHJ6ZWN6eXdpc3R5bWkgd2FydG/Fm2NpYW1pCiAgICBvZHBvd2llZHppIGEgcHJ6ZXdpZHl3YW55bWkgd2FydG/Fm2NpYW1pIG1vZGVsdSAoYsWCxIVkIGRvcGFzb3dhbmlhKSwKCi0gICAkXGxhbWJkYSQgKGxhbWJkYSkgdG8gcGFyYW1ldHIgcmVndWxhcnl6YWNqaSwga3TDs3J5IGtvbnRyb2x1amUgc2nFgsSZCiAgICByZWd1bGFyeXphY2ppLCBhICRcfFxiZXRhXHxfMSQgdG8gbm9ybWEgJExfMSQgd3Nww7PFgmN6eW5uaWvDs3cKICAgIHJlZ3Jlc2ppLgoKRG9kYW5pZSBrYXJ5IHJlZ3VsYXJ5emFjeWpuZWogJExfMSQgcG93b2R1amUsIMW8ZSBuaWVrdMOzcmUgd3Nww7PFgmN6eW5uaWtpCnJlZ3Jlc2ppIHN0YWrEhSBzacSZIHLDs3duZSB6ZXJvLCBjbyBwcm93YWR6aSBkbyBzZWxla2NqaSBjZWNoLiBJbSB3acSZa3N6YQp3YXJ0b8WbxIcgJFxsYW1iZGEkLCB0eW0gd2nEmWtzemEgamVzdCB0ZW5kZW5jamEgZG8gcmVkdWtjamkgd3Nww7PFgmN6eW5uaWvDs3cKZG8gemVyYSwgcHJvd2FkesSFYyBkbyBiYXJkemllaiByemFka2llZ28gbW9kZWx1IHogbW5pZWpzesSFIGxpY3pixIUgY2VjaC4KClJlZ3Jlc2phIExhc3NvIGplc3QgcHJ6eWRhdG5hIHcgcHJ6eXBhZGthY2gsIGdkeSBtYW15IGRvIGN6eW5pZW5pYSB6CndpZWxvbWEgY2VjaGFtaSwgeiBrdMOzcnljaCBuaWVrdMOzcmUgbW9nxIUgYnnEhyBuaWVpc3RvdG5lLiBNb8W8ZSBwb23Ds2MgdwppZGVudHlmaWthY2ppIGlzdG90bnljaCBjZWNoLCByZWR1a2NqaSBuYWRtaWFydSBkYW55Y2ggaSB6d2nEmWtzemVuaXUKaW50ZXJwcmV0b3dhbG5vxZtjaSBtb2RlbHUuCgojIyMgUHJ6eWvFgmFkCgpab2JhY3p5bGnFm215LCDFvGUgcmVncmVzamEgZ3J6YmlldG93YSB6IG3EhWRyeW0gd3lib3JlbSAkXGxhbWJkYSQgbW/FvGUKcHJ6ZXd5xbxzemHEhyBtZXRvZMSZIG5ham1uaWVqc3p5Y2gga3dhZHJhdMOzdywgamFrIHLDs3duaWXFvCBtb2RlbCB6ZXJvd3kgbmEKemJpb3J6ZSBkYW55Y2ggSGl0dGVycy4KClRlcmF6IHpvYmFjem15LCBjenkgbGFzc28gbW/FvGUgZGHEhyBhbGJvIGRva8WCYWRuaWVqc3p5LCBhbGJvIGJhcmR6aWVqCmludGVycHJldG93YWxueSBtb2RlbCBuacW8IHJlZ3Jlc2phIGdyemJpZXRvd2EuCgpXIGNlbHUgZG9wYXNvd2FuaWEgbW9kZWx1IGxhc3NvLCBwbyByYXoga29sZWpueSB1xbx5d2FteSBmdW5rY2ppCmBnbG1uZXQoKWAsIGplZG5hayB0eW0gcmF6ZW0gdcW8eXdhbXkgYXJndW1lbnR1IGBhbHBoYT0xYC4gUG96YSB0xIUgem1pYW7EhQpwb3N0xJlwdWplbXkgdGFrIHNhbW8gamFrIHcgcHJ6eXBhZGt1IGRvcGFzb3d5d2FuaWEgbW9kZWx1IHJlZ3Jlc2ppCmdyemJpZXRvd2VqOgoKYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGFzc29fbW9kID0gZ2xtbmV0KHhfdHJhaW4sIAogICAgICAgICAgICAgICAgICAgeV90cmFpbiwgCiAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEsIAogICAgICAgICAgICAgICAgICAgbGFtYmRhID0gZ3JpZCkgIyBEb3Bhc3VqIG1vZGVsIGxhc3NvIGRvIGRhbnljaCB0cmVuaW5nb3d5Y2gKCnBsb3QobGFzc29fbW9kKSAgICAjIFd5a3JlxZtsIHdzcMOzxYJjenlubmlraQpgYGAKClphdXdhxbxteSwgxbxlIG5hIHd5a3Jlc2llIHdzcMOzxYJjenlubmlrw7N3LCB3IHphbGXFvG5vxZtjaSBvZCB3eWJvcnUKZG9zdHJvamVuaWEgcGFyYW1ldHJ1LCBuaWVrdMOzcmUgemUgd3Nww7PFgmN6eW5uaWvDs3cgc8SFIGRva8WCYWRuaWUgcsOzd25lCnplcnUuIFRlcmF6IHByemVwcm93YWR6aW15IHdhbGlkYWNqxJkga3J6ecW8b3fEhSBpIG9ibGljenlteSB6d2nEhXphbnkgeiBuacSFCmLFgsSFZCB0ZXN0dToKCmBgYHtyIGVjaG89RkFMU0V9CnNldC5zZWVkKDEpCmN2Lm91dCA9IGN2LmdsbW5ldCh4X3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDEpICMgRG9wYXN1aiBtb2RlbCBsYXNzbyBkbyBkYW55Y2ggdHJlbmluZ293eWNoCnBsb3QoY3Yub3V0KSAjIE5hcnlzdWogd3lrcmVzIE1TRSBkbGEgcHLDs2J5IHVjesSFY2VqIGpha28gZnVua2NqxJkgbGFtYmRhCmJlc3RsYW0gPSBjdi5vdXQkbGFtYmRhLm1pbiAjIFd5YmllcnogbGFtZMSZLCBrdMOzcmEgbWluaW1hbGl6dWplIE1TRSB3IHByw7NiaWUgdWN6xIVjZWoKbGFzc29fcHJlZCA9IHByZWRpY3QobGFzc29fbW9kLCBzID0gYmVzdGxhbSwgbmV3eCA9IHhfdGVzdCkgIyBVxbx5aiBuYWpsZXBzemVqIGxhbWJkeSBkbyBwcnpld2lkeXdhbmlhIGRhbnljaCB0ZXN0b3d5Y2gKbWVhbigobGFzc29fcHJlZCAtIHlfdGVzdCleMikgIyBPYmxpY3ogTVNFIHcgcHLDs2JpZSB0ZXN0b3dlagpgYGAKCkplc3QgdG8gem5hY3puaWUgbmnFvHN6ZSBNU0UgemJpb3J1IHRlc3Rvd2VnbyBuacW8IG1vZGVsdSB6ZXJvd2VnbyBpCm1vZGVsdSBuYWptbmllanN6eWNoIGt3YWRyYXTDs3csIGkgYmFyZHpvIHBvZG9ibnkgZG8gTVNFIHRlc3R1IHJlZ3Jlc2ppCmdyemJpZXRvd2VqIHogJFxsYW1iZGEkIHd5YnJhbmVqIHByemV6IHdhbGlkYWNqxJkga3J6ecW8b3fEhS4KCkplZG5ha8W8ZSBsYXNzbyBtYSBpc3RvdG7EhSBwcnpld2FnxJkgbmFkIHJlZ3Jlc2rEhSBncnpiaWV0b3fEhSB3IHR5bSwgxbxlCnd5bmlrb3dlIG9zemFjb3dhbmlhIHdzcMOzxYJjenlubmlrw7N3IHPEhSByemFka2llLiBUdXRhaiB3aWR6aW15LCDFvGUgMTIgegoxOSBvc3phY293YcWEIHdzcMOzxYJjenlubmlrw7N3IGplc3QgZG9rxYJhZG5pZSB6ZXJvd3ljaDoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9Cm91dCA9IGdsbW5ldCh4LCB5LCBhbHBoYSA9IDEsIGxhbWJkYSA9IGdyaWQpICMgRG9wYXN1aiBtb2RlbCBsYXNzbyBkbyBwZcWCbmVnbyB6YmlvcnUgZGFueWNoCmxhc3NvX2NvZWYgPSBwcmVkaWN0KG91dCwgdHlwZSA9ICJjb2VmZmljaWVudHMiLCBzID0gYmVzdGxhbSlbMToyMCxdICMgV3nFm3dpZXRsYW5pZSB3c3DDs8WCY3p5bm5pa8OzdyBwcnp5IHXFvHljaXUgbGFtYmRhIHd5YnJhbmVnbyBwcnpleiBDVgpsYXNzb19jb2VmCmBgYAoKV3liaWVyYWrEhWMgdHlsa28gcHJlZHlrdG9yeSBvIG5pZXplcm93eWNoIHdzcMOzxYJjenlubmlrYWNoIHdpZHppbXksIMW8ZQptb2RlbCBsYXNzbyB6ICRcbGFtYmRhJCB3eWJyYW55bSBwcnpleiB3YWxpZGFjasSZIGtyennFvG93xIUgemF3aWVyYSB0eWxrbwpzaWVkZW0gem1pZW5ueWNoOgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KbGFzc29fY29lZltsYXNzb19jb2VmICE9IDBdICMgV3nFm3dpZXRsYW5pZSB0eWxrbyBuaWV6ZXJvd3ljaCB3c3DDs8WCY3p5bm5pa8OzdwpgYGAKCiMgVHdvamEga29sZWohCgojIyMgS3TDs3J5IHpiacOzciBkYW55Y2ggd3licmHFgmXFmz8KCiJhdXRvLmNzdiIgeiAqQW4gSW50cm9kdWN0aW9uIHRvIFN0YXRpc3RpY2FsIExlYXJuaW5nKgoKIyMjIEpha2EgYnnFgmEgVHdvamEgem1pZW5uYSB6YWxlxbxuYSAodHpuLiBjbyBwcsOzYm93YcWCZcWbIG1vZGVsb3dhxIcpPwoKIm1wZyIgLSAqKiptaWxlcyBwZXIgZ2Fsb24gb2YgZnVlbCoqIChjb250aW51b3VzIHZhcmlhYmxlKSoKCiMjIyMgU3BlY3lmaWthY2phIG1vZGVsaQoKWm1pZW5uxIUgemFsZcW8bsSFIHd5YnJhbm8gbXBnIChtaWxlcyBwZXIgZ2FsbG9uKS4gWm1pZW5ueW1pIG5pZXphbGXFvG55bWkKYnnFgnk6IGN5bGluZGVycywgZGlzcGxhY2VtZW50LCBob3JzZXBvd2VyLCB3ZWlnaHQgb3JheiBhY2NlbGVyYXRpb24uCk1vZGVsZSByZWdyZXNqaSBncnpiaWV0b3dlaiAoUmlkZ2UpIGkgTEFTU08gem9zdGHFgnkgc2tvbnN0cnVvd2FuZSwgYWJ5CnpyZWR1a293YcSHIHBvdGVuY2phbG5lIHByb2JsZW15IHogbXVsdGlrb2xpbmVhcm5vxZtjacSFIGkgemJhZGHEhyB3cMWCeXcKcmVndWxhcml6YWNqaSBuYSBkb3Bhc293YW5pZSBtb2RlbHUuCgojIyMgQ3p5IG9jemVraXdhxYJlxZssIMW8ZSByZWdyZXNqYSBncnpiaWV0b3dhIGLEmWR6aWUgbGVwc3phIG9kIGxhc3NvLCBjenkgb2R3cm90bmllPyBKYWsgd3lwYWRhIHcgc3Rvc3Vua3UgZG8gT0xTPyBQb2thxbwgb2Rwb3dpZWRuaWUgcmFwb3J0eSwgbWlhcnkgZG9wYXNvd2FuaWEgaSBrcsOzdGtvIGplIG9tw7N3IChwb3LDs3duYWopLgoKTmEgcG9kc3Rhd2llIHd5bmlrw7N3IHJlZ3Jlc2ppOgoK4oCiIE9MUyBNU0Ugd3lub3NpIDE3LjIzLCBjbyBqZXN0IHpibGnFvG9uZSBkbyB3eW5pa8OzdyBSaWRnZSAoTVNFID0gMTcuMTUpCmkgTEFTU08gKE1TRSA9IDE3LjA0KS4KCuKAoiBXeW5pa2kgc3VnZXJ1asSFLCDFvGUgT0xTIGplc3QgcG9yw7N3bnl3YWxueSBwb2Qgd3pnbMSZZGVtIGLFgsSZZHUgZG8gUmlkZ2UKaSBMQVNTTywgamVkbmFrIHJlZ3VsYXJpemFjamEgKFJpZGdlIGkgTEFTU08pIG1hIHBvdGVuY2phxYIgZG8gbGVwc3plagpnZW5lcmFsaXphY2ppIHcgcHJ6eXBhZGt1IHdpxJlrc3plaiB6bWllbm5vxZtjaSBkYW55Y2guCgrigKIgT2N6ZWtpd2HFgmVtLCDFvGUgTEFTU08gYsSZZHppZSBuaWVjbyBsZXBzemUgb2QgUmlkZ2UgdyB1cHJvc3pjemVuaXUKbW9kZWx1IGkgZWxpbWluYWNqaSBtbmllaiBpc3RvdG55Y2ggem1pZW5ueWNoLCBjbyBwb3R3aWVyZHphasSFIHd5bmlraS4KCiMjIyBLdMOzcmUgcHJlZHlrdG9yeSBva2F6YcWCeSBzacSZIHdhxbxuZSB3IG9zdGF0ZWN6bnltIG1vZGVsdSAobW9kZWxhY2gpPwoKMVwuICoqT0xTKio6CgrigKIgd2VpZ2h0IChwLXZhbHVlIFw8IDAuMDAxKSBpIGhvcnNlcG93ZXIgKHAtdmFsdWUgXDwgMC4wNSkgdG8KbmFqd2HFvG5pZWpzemUgcHJlZHlrdG9yeS4gV3Nww7PFgmN6eW5uaWtpIHPEhSB1amVtbmUsIGNvIG96bmFjemEsIMW8ZQp3acSZa3N6YSB3YWdhIGkgbW9jIHNpbG5pa2Egem1uaWVqc3phasSFIHd5ZGFqbm/Fm8SHIHBhbGl3YSAobXBnKS4K4oCiIFBvem9zdGHFgmUgem1pZW5uZSAoY3lsaW5kZXJzLCBkaXNwbGFjZW1lbnQsIGFjY2VsZXJhdGlvbikgb2themHFgnkgc2nEmQpzdGF0eXN0eWN6bmllIG5pZWlzdG90bmUuCgoyXC4gKipMQVNTTyoqOgoK4oCiIHdlaWdodCBpIGhvcnNlcG93ZXIgcsOzd25pZcW8IG9rYXphxYJ5IHNpxJkga2x1Y3pvd2UuIExBU1NPIHd5ZWxpbWlub3dhxYJvCm1uaWVqIGlzdG90bmUgem1pZW5uZSAoY3lsaW5kZXJzLCBkaXNwbGFjZW1lbnQsIGFjY2VsZXJhdGlvbiksCnByenlwaXN1asSFYyBpbSB3c3DDs8WCY3p5bm5pa2kgcsOzd25lIHplcm8uCgozXC4gKipSaWRnZSoqOgoK4oCiIFV3emdsxJlkbmnFgiB3c3p5c3RraWUgem1pZW5uZSwgYWxlIG5handpxJlrc3plIHpuYWN6ZW5pZSBwcnp5cGlzYW5vCndlaWdodCBpIGhvcnNlcG93ZXIuCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpoZWFkKEF1dG8pCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBXY3p5dGFqIGRhbmUgQXV0bwphdXRvX2RhdGEgPC0gSVNMUjo6QXV0byAgIyBaYWvFgmFkYW0sIMW8ZSBtYXN6IGRvc3TEmXAgZG8gcGFraWV0dSBJU0xSCgojIFd5xZt3aWV0bCBwaWVyd3N6ZSBraWxrYSB3aWVyc3p5CmhlYWQoYXV0b19kYXRhKQoKIyBPYmxpY3ogc3RhdHlzdHlraSBvcGlzb3dlIGRsYSBkYW55Y2ggbGljemJvd3ljaApzdW1tYXJ5X3N0YXRzIDwtIGF1dG9fZGF0YSAlPiUKICBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lCiAgc3VtbWFyaXNlX2FsbChsaXN0KAogICAgTWluID0gbWluLAogICAgUTEgPSB+IHF1YW50aWxlKC4sIDAuMjUpLAogICAgTWVkaWFuID0gbWVkaWFuLAogICAgTWVhbiA9IG1lYW4sCiAgICBRMyA9IH4gcXVhbnRpbGUoLiwgMC43NSksCiAgICBNYXggPSBtYXgsCiAgICBTRCA9IHNkCiAgKSkKCiMgV3nFm3dpZXRsIHN0YXR5c3R5a2kKcHJpbnQoc3VtbWFyeV9zdGF0cykKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQoKIyBXY3p5dGFqIHpiacOzciBkYW55Y2ggQXV0bwpkYXRhKEF1dG8pCmF1dG9fZGF0YSA8LSBuYS5vbWl0KEF1dG8pCgojIFd5Ymllcnogem1pZW5uxIUgemFsZcW8bsSFIGkgNSB6bWllbm55Y2ggbmllemFsZcW8bnljaAp5IDwtIGF1dG9fZGF0YSRtcGcKWCA8LSBhcy5tYXRyaXgoYXV0b19kYXRhWywgYygiY3lsaW5kZXJzIiwgImRpc3BsYWNlbWVudCIsICJob3JzZXBvd2VyIiwgIndlaWdodCIsICJhY2NlbGVyYXRpb24iKV0pCgojIFBvZHppZWwgZGFuZSBuYSB6YmnDs3IgdHJlbmluZ293eSBpIHRlc3Rvd3kKc2V0LnNlZWQoMTIzKSAgIyBVc3RhbCBsb3Nvd2/Fm8SHIGRsYSBwb3d0YXJ6YWxub8WbY2kKdHJhaW5faW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KFgpLCBzaXplID0gMC44ICogbnJvdyhYKSkKWF90cmFpbiA8LSBYW3RyYWluX2luZGljZXMsIF0KeV90cmFpbiA8LSB5W3RyYWluX2luZGljZXNdClhfdGVzdCA8LSBYWy10cmFpbl9pbmRpY2VzLCBdCnlfdGVzdCA8LSB5Wy10cmFpbl9pbmRpY2VzXQoKIyBTdGFuZGFyZHl6YWNqYSBkYW55Y2gKWF90cmFpbl9zY2FsZWQgPC0gc2NhbGUoWF90cmFpbikKWF90ZXN0X3NjYWxlZCA8LSBzY2FsZShYX3Rlc3QsIGNlbnRlciA9IGF0dHIoWF90cmFpbl9zY2FsZWQsICJzY2FsZWQ6Y2VudGVyIiksIHNjYWxlID0gYXR0cihYX3RyYWluX3NjYWxlZCwgInNjYWxlZDpzY2FsZSIpKQoKIyBSZWdyZXNqYSBncnpiaWV0b3dhIChyaWRnZSkKcmlkZ2VfbW9kZWwgPC0gZ2xtbmV0KFhfdHJhaW5fc2NhbGVkLCB5X3RyYWluLCBhbHBoYSA9IDApICAjIGFscGhhID0gMCBvem5hY3phIHJpZGdlCnJpZGdlX2N2IDwtIGN2LmdsbW5ldChYX3RyYWluX3NjYWxlZCwgeV90cmFpbiwgYWxwaGEgPSAwKQpyaWRnZV9iZXN0X2xhbWJkYSA8LSByaWRnZV9jdiRsYW1iZGEubWluCgojIFJlZ3Jlc2phIExBU1NPCmxhc3NvX21vZGVsIDwtIGdsbW5ldChYX3RyYWluX3NjYWxlZCwgeV90cmFpbiwgYWxwaGEgPSAxKSAgIyBhbHBoYSA9IDEgb3puYWN6YSBMQVNTTwpsYXNzb19jdiA8LSBjdi5nbG1uZXQoWF90cmFpbl9zY2FsZWQsIHlfdHJhaW4sIGFscGhhID0gMSkKbGFzc29fYmVzdF9sYW1iZGEgPC0gbGFzc29fY3YkbGFtYmRhLm1pbgoKIyBQcmVkeWtjamEgbmEgemJpb3J6ZSB0ZXN0b3d5bQpyaWRnZV9wcmVkIDwtIHByZWRpY3QocmlkZ2VfbW9kZWwsIHMgPSByaWRnZV9iZXN0X2xhbWJkYSwgbmV3eCA9IFhfdGVzdF9zY2FsZWQpCmxhc3NvX3ByZWQgPC0gcHJlZGljdChsYXNzb19tb2RlbCwgcyA9IGxhc3NvX2Jlc3RfbGFtYmRhLCBuZXd4ID0gWF90ZXN0X3NjYWxlZCkKCiMgT2NlbmEgbW9kZWxpCnJpZGdlX21zZSA8LSBtZWFuKCh5X3Rlc3QgLSByaWRnZV9wcmVkKV4yKQpsYXNzb19tc2UgPC0gbWVhbigoeV90ZXN0IC0gbGFzc29fcHJlZCleMikKCiMgV3nFm3dpZXRsIHd5bmlraQpjYXQoIlJpZGdlIFJlZ3Jlc3Npb246XG4iKQpjYXQoIkJlc3QgTGFtYmRhOiIsIHJpZGdlX2Jlc3RfbGFtYmRhLCAiXG4iKQpjYXQoIk1lYW4gU3F1YXJlZCBFcnJvcjoiLCByaWRnZV9tc2UsICJcblxuIikKCmNhdCgiTEFTU08gUmVncmVzc2lvbjpcbiIpCmNhdCgiQmVzdCBMYW1iZGE6IiwgbGFzc29fYmVzdF9sYW1iZGEsICJcbiIpCmNhdCgiTWVhbiBTcXVhcmVkIEVycm9yOiIsIGxhc3NvX21zZSwgIlxuIikKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFJlZ3Jlc2phIE9MUwoKIyBQb2R6aWVsIGRhbmUgbmEgemJpw7NyIHRyZW5pbmdvd3kgaSB0ZXN0b3d5CnNldC5zZWVkKDEyMykgICMgVXN0YWwgbG9zb3dvxZvEhyBkbGEgcG93dGFyemFsbm/Fm2NpCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhhdXRvX2RhdGEpLCBzaXplID0gMC44ICogbnJvdyhhdXRvX2RhdGEpKQp0cmFpbl9kYXRhIDwtIGF1dG9fZGF0YVt0cmFpbl9pbmRpY2VzLCBdCnRlc3RfZGF0YSA8LSBhdXRvX2RhdGFbLXRyYWluX2luZGljZXMsIF0KCgpvbHNfbW9kZWwgPC0gbG0obXBnIH4gY3lsaW5kZXJzICsgZGlzcGxhY2VtZW50ICsgaG9yc2Vwb3dlciArIHdlaWdodCArIGFjY2VsZXJhdGlvbiwgZGF0YSA9IHRyYWluX2RhdGEpCgojIFd5xZt3aWV0bCBwb2RzdW1vd2FuaWUgbW9kZWx1CnN1bW1hcnkob2xzX21vZGVsKQoKIyBQcmVkeWtjamEgbmEgZGFueWNoIHRlc3Rvd3ljaApvbHNfcHJlZCA8LSBwcmVkaWN0KG9sc19tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKCiMgT2JsaWN6ZW5pZSBixYLEmWR1IMWbcmVkbmlva3dhZHJhdG93ZWdvIChNU0UpIGRsYSBPTFMKb2xzX21zZSA8LSBtZWFuKCh0ZXN0X2RhdGEkbXBnIC0gb2xzX3ByZWQpXjIpCmNhdCgiT0xTIE1lYW4gU3F1YXJlZCBFcnJvcjoiLCBvbHNfbXNlLCAiXG4iKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgV2N6eXRhaiB6YmnDs3IgZGFueWNoIEF1dG8KZGF0YShBdXRvKQphdXRvX2RhdGEgPC0gbmEub21pdChBdXRvKQoKIyBXeWJpZXJ6IHptaWVubsSFIHphbGXFvG7EhSBpIDUgem1pZW5ueWNoIG5pZXphbGXFvG55Y2gKeSA8LSBhdXRvX2RhdGEkbXBnClggPC0gYXMubWF0cml4KGF1dG9fZGF0YVssIGMoImN5bGluZGVycyIsICJkaXNwbGFjZW1lbnQiLCAiaG9yc2Vwb3dlciIsICJ3ZWlnaHQiLCAiYWNjZWxlcmF0aW9uIildKQoKIyBQb2R6aWVsIGRhbmUgbmEgemJpw7NyIHRyZW5pbmdvd3kgaSB0ZXN0b3d5CnNldC5zZWVkKDEyMykgICMgVXN0YWwgbG9zb3dvxZvEhyBkbGEgcG93dGFyemFsbm/Fm2NpCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhYKSwgc2l6ZSA9IDAuOCAqIG5yb3coWCkpClhfdHJhaW4gPC0gWFt0cmFpbl9pbmRpY2VzLCBdCnlfdHJhaW4gPC0geVt0cmFpbl9pbmRpY2VzXQpYX3Rlc3QgPC0gWFstdHJhaW5faW5kaWNlcywgXQp5X3Rlc3QgPC0geVstdHJhaW5faW5kaWNlc10KCiMgU3RhbmRhcmR5emFjamEgZGFueWNoClhfdHJhaW5fc2NhbGVkIDwtIHNjYWxlKFhfdHJhaW4pClhfdGVzdF9zY2FsZWQgPC0gc2NhbGUoWF90ZXN0LCBjZW50ZXIgPSBhdHRyKFhfdHJhaW5fc2NhbGVkLCAic2NhbGVkOmNlbnRlciIpLCBzY2FsZSA9IGF0dHIoWF90cmFpbl9zY2FsZWQsICJzY2FsZWQ6c2NhbGUiKSkKCiMgUmVncmVzamEgZ3J6YmlldG93YSAoUmlkZ2UpCnJpZGdlX21vZGVsIDwtIGdsbW5ldChYX3RyYWluX3NjYWxlZCwgeV90cmFpbiwgYWxwaGEgPSAwKSAgIyBhbHBoYSA9IDAgb3puYWN6YSBSaWRnZQpyaWRnZV9jdiA8LSBjdi5nbG1uZXQoWF90cmFpbl9zY2FsZWQsIHlfdHJhaW4sIGFscGhhID0gMCkKcmlkZ2VfYmVzdF9sYW1iZGEgPC0gcmlkZ2VfY3YkbGFtYmRhLm1pbgoKIyBSZWdyZXNqYSBMQVNTTwpsYXNzb19tb2RlbCA8LSBnbG1uZXQoWF90cmFpbl9zY2FsZWQsIHlfdHJhaW4sIGFscGhhID0gMSkgICMgYWxwaGEgPSAxIG96bmFjemEgTEFTU08KbGFzc29fY3YgPC0gY3YuZ2xtbmV0KFhfdHJhaW5fc2NhbGVkLCB5X3RyYWluLCBhbHBoYSA9IDEpCmxhc3NvX2Jlc3RfbGFtYmRhIDwtIGxhc3NvX2N2JGxhbWJkYS5taW4KCiMgUG9wcmF3aW9uZSB3eWtyZXN5CgojIFd5a3JlcyBkbGEgcmVncmVzamkgTEFTU08KcGxvdChsYXNzb19jdiwKICAgICBtYWluID0gIkxBU1NPIFJlZ3Jlc3Npb246IE1TRSB2cyBMYW1iZGEiLAogICAgIHhsYWIgPSAibG9nKExhbWJkYSkiLAogICAgIHlsYWIgPSAiTWVhbiBTcXVhcmVkIEVycm9yIiwKICAgICBjZXgubGFiID0gMS4yLCAgIyBQb3dpxJlrc3plbmllIGV0eWtpZXQgb3NpCiAgICAgY2V4LmF4aXMgPSAxLjEpICMgUG93acSZa3N6ZW5pZSB3YXJ0b8WbY2kgb3NpCgojIFd5a3JlcyBkbGEgcmVncmVzamkgUmlkZ2UKcGxvdChyaWRnZV9jdiwKICAgICBtYWluID0gIlJpZGdlIFJlZ3Jlc3Npb246IE1TRSB2cyBMYW1iZGEiLAogICAgIHhsYWIgPSAibG9nKExhbWJkYSkiLAogICAgIHlsYWIgPSAiTWVhbiBTcXVhcmVkIEVycm9yIiwKICAgICBjZXgubGFiID0gMS4yLAogICAgIGNleC5heGlzID0gMS4xKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgV3lrcmVzIGRpYWdub3N0eWN6bnkgZGxhIHJlZ3Jlc2ppIE9MUwpwYXIobWZyb3cgPSBjKDIsIDIpKSAgIyBVc3Rhd2llbmllIHNpYXRraSAyeDIgbmEgd3lrcmVzeQpwbG90KG9sc19tb2RlbCkKYGBgCgpXeWtyZXN5IHBvcHJhd25vxZtjaSBkb3Bhc293YW5pYSBkYW55Y2ggZGxhIHJlZ3Jlc2ppIE9MUwo=