1 Zakład Bioinformatyki, Instytut Informatyki, Uniwersytet w Białymstoku

Correspondence: Jarosław Kotowicz <>

1 Przygotowanie do pracy środowiska pracy

1.1 Instalacja oprogramowania

  1. R strona domowa www.r-project.org
  • wybieramy download
  • wybieramy mirror np. serwer niemiecki
  • ściągamy plik instalacyjny
  1. Rstudio (GUI) dla R RStudio
  • należy pobrać system opensource
  1. Przeglądarka plików pdf natywna dla RStudio (pod Windows) sumatra (opensource)
  1. Instalujemy w kolejności R, sumatra, RStudio

1.2 Przygotowanie do pracy

  1. W katalagu dokumenty tworzymy folder (nazwa zgodnie z zasadą Inf_xxxxx, gdzie xxxxx to numer albumu).
  2. Uruchamiamy RSudio
  3. Zachowujemy porządek. Tworzymy nowy projekt
  • Files -> New Project… -> Existing Directory -> Browse… i wybieramy folder, który utworzyliśmy oraz zaznaczamy Open in new session.
    Uwaga: Zawsze na koniec pracy/zajęć należy zamknąć projekt. Files -> Close Project
    Uwaga: Zawsze na początku pracy/zajęć należy otworzyć projekt. Files -> Recent Projects i wybrać swój (będzie miał on taką nazwę, jak nasz katalog).
  1. Dostosowujem RSudio do następujących wymagań:
  • Tools -> Project Options ->
    1. Code Editing -> Text encoding wybór UTF-8
    2. Sweave -> PDF Generaion wybór knitr
      Uwaga: Jeżeli chcemy korzystać z TeX (LaTeX), to musimy mieć zaistalowny system/program np. MikTeX.
  • Tools -> Global Options ->
    1. Appearance wybór wyglądu RSudion np. ambiance (ciemny motyw).

  1. W domu instalujemy konieczne biblioteki R nazywane package
  • dolne prawe okienko - okno Packages -> Install wpisujemy tidyverse
  1. Otwieramy pliki
  • R Script: Files -> New File -> R Script
  • R Notebook: Files -> New File -> R Notebook
  • R Markdown: Files -> New File -> R Markdown i wybieramy rodzaj dokumentu np. Document lub Presentation.

  1. Będziemy zawsze pracować albo w pliku skryptowym R albo w R Notebook.
  2. Informacje dla pliku skryptowego
  • wywołanie aktualnej linii w konsoli CTRL+ENTER
  • odwołanie się do funkcji z danej biblioteki package::function np. stats::filter
  • podczytanie biblioteki library(nazwa bibilioteki)
  • znak komentarza hashtag tzn. #

  1. Import danych
  • korzystamy z danych dostępnych na stronie Jareda P. Landera
  • plik csv (textowe) import: File -> Import Dataset -> From Text (readr) (w polu File/URL kopiujemy adres https://jaredlander.com/data/ i dopisujemy nazwę pliku np. acs_ny.csv) -> Update
  • wykorzystujemy opcje Import Options aby wybrać np. znak rozdzielający dane średnik to semicolon itd.
  • możemy zmienić też typy kolumn wybierając dla kolumny z menu rozwijalnego wartość Uwaga: W przypadku typu factor (zmienna czynnikowa) należy wskazać wszystkie poziomy (wartości).
  • kopiujemy z okienka Code Preview do pliku skryptowego polecenie poczytując wcześniej bibliotekę readr, ja tutaj podczytuję tidyverse.
  • braki danych NA (skrót od not available), nieliczby NaN (not a number)
  • typ numeric w R identyczny jak double w C++ i co więcej używane też jest równoważnie numeric i double
library(tidyverse)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages --------------------------------------- tidyverse 1.3.0 --
<U+221A> ggplot2 3.3.0     <U+221A> purrr   0.3.4
<U+221A> tibble  3.0.1     <U+221A> dplyr   0.8.5
<U+221A> tidyr   1.0.2     <U+221A> stringr 1.4.0
<U+221A> readr   1.3.1     <U+221A> forcats 0.5.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

Przykład (skopiowany kod i następnie uporządkowany)

acsNY <- read_csv("https://jaredlander.com/data/acs_ny.csv", 
                  col_types = cols(ElectricBill = col_integer(), 
                                   FamilyIncome = col_integer(), 
                                   FamilyType = col_factor(levels = c("Married", "Female Head", "Male Head")), 
                                   HouseCosts = col_integer(), 
                                   Insurance = col_integer(), 
                                   NumBedrooms = col_integer(), 
                                   NumChildren = col_integer(), 
                                   NumPeople = col_integer(), 
                                   NumRooms = col_integer(), 
                                   NumVehicles = col_integer(), 
                                   NumWorkers = col_integer()))

  1. Przetwarzanie potokowe tzw. pipe (biblioteka magrittr) Uwaga: Użycie biblioteki dplyr powoduje możliwość korzystania z przetwarzania potokowego. Używając biblioteki tidyverse podczytuję również dplyr.
    co wysyłam %>% gdzie wysyłam
    Zasada przetwarzania potkowego f(a) jest to a %>% f

f(a,b) jest to a %>% f(b)

f(a,b) jest to b %>% f(a,.) f(a,b) Uwaga: Kropka wskazuje miejsca wstawienia zmiennej.

g(f(a)) jest to a %>% f %>% g


  1. Reguła wcięć poprawny zapis zgodny z regułą wcięć to
acsNY %>% 
  summary()
    Acres            FamilyIncome           FamilyType     NumBedrooms     NumChildren     
 Length:22745       Min.   :     50   Married    :18326   Min.   :0.000   Min.   : 0.0000  
 Class :character   1st Qu.:  52540   Female Head: 3266   1st Qu.:3.000   1st Qu.: 0.0000  
 Mode  :character   Median :  87000   Male Head  : 1153   Median :3.000   Median : 0.0000  
                    Mean   : 110281                       Mean   :3.385   Mean   : 0.9012  
                    3rd Qu.: 133800                       3rd Qu.:4.000   3rd Qu.: 2.0000  
                    Max.   :1605000                       Max.   :8.000   Max.   :12.0000  
   NumPeople        NumRooms        NumUnits          NumVehicles      NumWorkers   
 Min.   : 2.00   Min.   : 1.000   Length:22745       Min.   :0.000   Min.   :0.000  
 1st Qu.: 2.00   1st Qu.: 6.000   Class :character   1st Qu.:2.000   1st Qu.:1.000  
 Median : 3.00   Median : 7.000   Mode  :character   Median :2.000   Median :2.000  
 Mean   : 3.39   Mean   : 7.175                      Mean   :2.113   Mean   :1.745  
 3rd Qu.: 4.00   3rd Qu.: 8.000                      3rd Qu.:3.000   3rd Qu.:2.000  
 Max.   :18.00   Max.   :21.000                      Max.   :6.000   Max.   :3.000  
   OwnRent           YearBuilt           HouseCosts    ElectricBill  FoodStamp        
 Length:22745       Length:22745       Min.   :   4   Min.   :  1   Length:22745      
 Class :character   Class :character   1st Qu.: 650   1st Qu.:100   Class :character  
 Mode  :character   Mode  :character   Median :1200   Median :150   Mode  :character  
                                       Mean   :1480   Mean   :175                     
                                       3rd Qu.:2000   3rd Qu.:220                     
                                       Max.   :7090   Max.   :580                     
 HeatingFuel          Insurance        Language        
 Length:22745       Min.   :   0.0   Length:22745      
 Class :character   1st Qu.: 400.0   Class :character  
 Mode  :character   Median : 720.0   Mode  :character  
                    Mean   : 960.9                     
                    3rd Qu.:1200.0                     
                    Max.   :6600.0                     

2 Funkcje wywołujące rozkłady w R

Budowa funkcji prefix NazwaRozkładu

Rodzaje prefiksów

  1. d - funkcja gęstości w przypadku rozkładu ciągłego lub funkcja prawdopodobieństwa w przypadku rozkłądu dyskretnego
  2. p - dystrybuanta rozkładu
  3. q - kwantyle rozkładu
  4. r - liczba (liczby) pseudolosowe z rozkładu

Nazwy rozkładów

  1. norm - noramlny,

  2. gamma

  3. beta

  4. chisq

  5. itd

  6. Wyznaczamy wartość gęstości rozkładu normalnego standardowego dla argumentu 0.

dnorm(0)
[1] 0.3989423
  1. Wyznaczamy wartość gęstości rozkładu N(2,5) dla argumentu 0.
dnorm(0, mean = 2, sd = 5)
[1] 0.07365403
  1. Wyznaczamy wartość dystrybuanty rozkładu normalnego standardowego dla argumentu od 0 do 1 z krokiem .2.
pnorm(seq(0, 1, by = .2))
[1] 0.5000000 0.5792597 0.6554217 0.7257469 0.7881446 0.8413447

2.1 Liczby pseudolosowe z zadanego rozkładu prawdopodobieństwa

set.seed(20200512)
x.norm32 <- rnorm(1000, mean = 3, sd = 2)
set.seed(20200512)
x.norm01 <- rnorm(1000, mean = 0, sd = 1)

2.2 Dystrybuanta

pnorm(0)
[1] 0.5

2.3 Gęstość

dnorm(0)
[1] 0.3989423

2.4 Kwantyle

qnorm(c(.25, .5, .75))
[1] -0.6744898  0.0000000  0.6744898

3 Statystyka opisowa

3.1 Szereg rozdzielczy przedziałowy (prosty, skumulowany)

3.1.1 Zmienna jednowymiarowa

Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2
Registered S3 method overwritten by 'gdata':
  method         from     
  reorder.factor DescTools
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
table(cut(x.norm01, seq(min(x.norm01), max(x.norm01)+.2, by = 1), include.lowest = TRUE))

 [-3.17,-2.17]  (-2.17,-1.17] (-1.17,-0.169] (-0.169,0.831]   (0.831,1.83]    (1.83,2.83] 
            15            109            293            381            175             24 
   (2.83,3.83] 
             3 
sjmisc::frq(sample(c("x", "y", "z"), size = 1000, replace = TRUE, prob = c(.3, .5, .2)))

x <character>
# total N=1000  valid N=1000  mean=1.88  sd=0.68

Value |   N | Raw % | Valid % | Cum. %
--------------------------------------
x     | 297 | 29.70 |   29.70 |  29.70
y     | 525 | 52.50 |   52.50 |  82.20
z     | 178 | 17.80 |   17.80 | 100.00
<NA>  |   0 |  0.00 |    <NA> |   <NA>

3.1.2 Zmienna wielowymiarowa (ramka danych data.frame)

df01 <- data.frame(zm1 = sample(c("a", "b"), 100, replace = TRUE),
                   zm2 = sample(c("c", "d"), 100, replace = TRUE))
sjmisc::frq(df01)

zm1 <character>
# total N=100  valid N=100  mean=1.38  sd=0.49

Value |  N | Raw % | Valid % | Cum. %
-------------------------------------
a     | 62 |    62 |      62 |     62
b     | 38 |    38 |      38 |    100
<NA>  |  0 |     0 |    <NA> |   <NA>


zm2 <character>
# total N=100  valid N=100  mean=1.55  sd=0.50

Value |  N | Raw % | Valid % | Cum. %
-------------------------------------
c     | 45 | 45.00 |   45.00 |     45
d     | 55 | 55.00 |   55.00 |    100
<NA>  |  0 |  0.00 |    <NA> |   <NA>

3.2 Tablica kontyngencji

(tabela <- table(df01$zm1, df01$zm2))
   
     c  d
  a 24 38
  b 21 17

3.3 Średnia

mean(x.norm01)
[1] -0.009051554
mean(x.norm32)
[1] 2.981897

3.4 Wariancja

var(x.norm01)
[1] 0.9785055

3.5 Odchylenie standradowe

sd(x.norm32)
[1] 1.978389

3.6 Tzw. fivenum (minimum, kwartyle, maksimum)

fivenum(x.norm01)
[1] -3.1686257 -0.6939557  0.0476431  0.6494921  3.6518932

3.7 Kwantyle

quantile(x.norm01, seq(0,1, by = .1))
        0%        10%        20%        30%        40%        50%        60%        70% 
-3.1686257 -1.2769306 -0.8797193 -0.5415714 -0.2266632  0.0476431  0.2510401  0.4988340 
       80%        90%       100% 
 0.8378308  1.2253506  3.6518932 

3.8 Zakres zmienności

range(x.norm32)
[1] -3.337251 10.303786

3.9 Mediana

median(x.norm32)
[1] 3.095286

3.10 Podsumowanie i summary vs. fivenum

summary(x.norm32)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -3.337   1.612   3.095   2.982   4.293  10.304 
fivenum(x.norm32)
[1] -3.337251  1.612089  3.095286  4.298984 10.303786

3.11 Podstawowe miary statystyki opisowej

psych::describe(x.norm32, quant = c(.25, .75), IQR = TRUE)
Hmisc::describe(x.norm32)
x.norm32 
       n  missing distinct     Info     Mean      Gmd      .05      .10      .25      .50 
    1000        0     1000        1    2.982    2.229  -0.3510   0.4461   1.6121   3.0953 
     .75      .90      .95 
  4.2929   5.4507   6.0952 

lowest : -3.337251 -3.304396 -2.868932 -2.750761 -1.974559
highest:  8.327035  8.470047  8.957606  9.994734 10.303786

3.12 Rozstęp międzyćwiartkowy (czyli podwojone odchylenie ćwiartkowe)

IQR(x.norm32)
[1] 2.68075

3.13 Skośność (wywołanie na dwa sposoby i trzech bibliotek)

e1071::skewness(x.norm01)
[1] -0.03115604
library(e1071)
skewness(x.norm32)
[1] -0.03115604
detach(package:e1071)
agricolae::skewness(x.norm01)
[1] -0.03124973
psych::skew(x.norm01)
[1] -0.03115604

3.14 Moda

RVAideMemoire::mod(x.norm32)
[1] 3.371455

3.15 Kurtoza

e1071::kurtosis(x.norm01)
[1] 0.1129802
agricolae::kurtosis(x.norm01)
[1] 0.1258378
psych::kurtosi(x.norm01)
[1] 0.1129802

3.16 Momenty

3.16.1 Z domyślnymi parametrami

e1071::moment(x.norm32)
[1] 2.981897

3.16.2 Odpowiedniego rzędu

e1071::moment(x.norm32, order = 2)
[1] 12.80182

3.16.3 Centralne

e1071::moment(x.norm32, order = 2, center = TRUE)
[1] 3.910108

3.16.4 Moment centralny rzędu 2 vs. wariancja (var)

e1071::moment(x.norm32, order = 2, center = TRUE)
[1] 3.910108
var(x.norm32)
[1] 3.914022
var(x.norm32)/length(x.norm32)*(length(x.norm32)-1)
[1] 3.910108

3.17 Korelacja i kowariancja (macierze korelacji i kowariancji)

df <- data.frame(norm01 = x.norm01, norm32 = x.norm32, ga = rgamma(1000, 2, 1)) %>% as_tibble()

3.17.1 Kowariancja

cov(x.norm01, df$ga)
[1] -0.02882743
cov(df)
            norm01      norm32          ga
norm01  0.97850552  1.95701104 -0.02882743
norm32  1.95701104  3.91402208 -0.05765486
ga     -0.02882743 -0.05765486  2.19773905

3.17.2 Korelacja

cor(df)
            norm01      norm32          ga
norm01  1.00000000  1.00000000 -0.01965786
norm32  1.00000000  1.00000000 -0.01965786
ga     -0.01965786 -0.01965786  1.00000000
Hmisc::rcorr(as.matrix(df), type = "pearson")
       norm01 norm32    ga
norm01   1.00   1.00 -0.02
norm32   1.00   1.00 -0.02
ga      -0.02  -0.02  1.00

n= 1000 


P
       norm01 norm32 ga    
norm01        0.0000 0.5347
norm32 0.0000        0.5347
ga     0.5347 0.5347       
Hmisc::rcorr(as.matrix(df), type = "spearman")
       norm01 norm32    ga
norm01   1.00   1.00 -0.01
norm32   1.00   1.00 -0.01
ga      -0.01  -0.01  1.00

n= 1000 


P
       norm01 norm32 ga    
norm01        0.0000 0.8288
norm32 0.0000        0.8288
ga     0.8288 0.8288       

4 Statystyki graficzne z ggplot2

4.1 Dystrybuanta empiryczna

df %>%
ggplot(aes(x = norm01)) +
  stat_ecdf()

4.2 Gęstość

df %>%
  ggplot(aes(x.norm01)) +
  geom_density(kernel = "gaussian")

4.3 Histogram

df %>%
  ggplot(aes(x.norm01)) +
  geom_histogram()

df %>%
  ggplot(aes(x.norm01)) +
  geom_freqpoly()

4.4 Wykres kwantyli

df %>%
  ggplot() +
  geom_qq(aes(sample= x.norm01))

GGally::ggpairs(df)


5 Estymacja punktowa metodą największej wiarogodnosci (biblioteka MASS)

MASS::fitdistr(x.norm01, "normal")
       mean            sd     
  -0.009051554    0.988699659 
 ( 0.031265428) ( 0.022107996)
MASS::fitdistr(df$ga, "gamma")
     shape         rate   
  1.81814044   0.93017351 
 (0.07504241) (0.04415700)

6 Estymacja przedziałowa

6.1 Różne funkcje wyznaczające przedział ufności dla średniej

Rmisc::CI(x.norm01)
       upper         mean        lower 
 0.052332592 -0.009051554 -0.070435701 
DescTools::MeanCI(x.norm01)
        mean       lwr.ci       upr.ci 
-0.009051554 -0.070435701  0.052332592 
t.test(x.norm01)

    One Sample t-test

data:  x.norm01
t = -0.28936, df = 999, p-value = 0.7724
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 -0.07043570  0.05233259
sample estimates:
   mean of x 
-0.009051554 
gmodels::ci(x.norm01)
No class or unkown class.  Using default calcuation.
    Estimate     CI lower     CI upper   Std. Error 
-0.009051554 -0.070435701  0.052332592  0.031281073 

6.2 Przedział ufności dla frakcji

set.seed(20200512)
x.bin <-rbinom(1000, size = 1, prob = .3)
gmodels::ci.binom(x.bin)
     Estimate  CI lower  CI upper Std. Error
[1,]    0.294 0.2659037 0.3233147 0.01440708
prop.test(sum(x.bin), length(x.bin), .3)

    1-sample proportions test with continuity correction

data:  sum(x.bin) out of length(x.bin), null probability 0.3
X-squared = 0.14405, df = 1, p-value = 0.7043
alternative hypothesis: true p is not equal to 0.3
95 percent confidence interval:
 0.2661099 0.3234946
sample estimates:
    p 
0.294 

6.3 Budowa własnej funkcji wyznaczającej przedział ufności dla wariancji

CI_var_N <- function(dane, alpha = .05) {
  konieclewy <- (length(dane) -1) * var(dane) / qchisq(1- alpha/2, df = length(dane) - 1)
  koniecprawy <- (length(dane) -1) * var(dane) / qchisq(alpha/2, df = length(dane) - 1)
  cat(paste("Przedział ufności dla wariancji wynosi:\n [", konieclewy, ",", koniecprawy, "]\n"))
}

CI_var_N(x.norm01)
Przedział ufności dla wariancji wynosi:
 [ 0.898060293361267 , 1.07032294630049 ]

7 Testowanie hipotez istotoności

7.1 Testy parametryczne

7.1.1 Test dla średniej

t.test(x.norm01)

    One Sample t-test

data:  x.norm01
t = -0.28936, df = 999, p-value = 0.7724
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 -0.07043570  0.05233259
sample estimates:
   mean of x 
-0.009051554 

Interpretacja wyniku!

t.test(x.norm32)

    One Sample t-test

data:  x.norm32
t = 47.663, df = 999, p-value < 2.2e-16
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 2.859129 3.104665
sample estimates:
mean of x 
 2.981897 

Interpretacja wyniku!

t.test(x.norm32, mu = 3)

    One Sample t-test

data:  x.norm32
t = -0.28936, df = 999, p-value = 0.7724
alternative hypothesis: true mean is not equal to 3
95 percent confidence interval:
 2.859129 3.104665
sample estimates:
mean of x 
 2.981897 

Interpretacja wyniku!

7.1.1.1 Różne alternatywy

t.test(x.norm32, mu = 3, alternative = "less")

    One Sample t-test

data:  x.norm32
t = -0.28936, df = 999, p-value = 0.3862
alternative hypothesis: true mean is less than 3
95 percent confidence interval:
     -Inf 3.084898
sample estimates:
mean of x 
 2.981897 

Interpretacja wyniku!

t.test(x.norm32, mu = 3, alternative = "greater")

    One Sample t-test

data:  x.norm32
t = -0.28936, df = 999, p-value = 0.6138
alternative hypothesis: true mean is greater than 3
95 percent confidence interval:
 2.878896      Inf
sample estimates:
mean of x 
 2.981897 

Interpretacja wyniku!

7.1.2 Testy dwóch średnich

t.test(x.norm32, x.norm01)

    Welch Two Sample t-test

data:  x.norm32 and x.norm01
t = 42.76, df = 1469.1, p-value < 2.2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 2.853743 3.128154
sample estimates:
   mean of x    mean of y 
 2.981896891 -0.009051554 

Interpretacja wyniku!

t.test(x.norm32[1:500], x.norm32[501:1000])

    Welch Two Sample t-test

data:  x.norm32[1:500] and x.norm32[501:1000]
t = 0.46174, df = 997.23, p-value = 0.6444
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -0.1878366  0.3034311
sample estimates:
mean of x mean of y 
 3.010796  2.952998 

Interpretacja wyniku!

7.1.3 Testy dla dwóch wariancji

var.test(x.norm01, x.norm32)

    F test to compare two variances

data:  x.norm01 and x.norm32
F = 0.25, num df = 999, denom df = 999, p-value < 2.2e-16
alternative hypothesis: true ratio of variances is not equal to 1
95 percent confidence interval:
 0.2208247 0.2830300
sample estimates:
ratio of variances 
              0.25 

Interpretacja wyniku!

var.test(x.norm01[1:100], x.norm01[901:100])

    F test to compare two variances

data:  x.norm01[1:100] and x.norm01[901:100]
F = 0.97939, num df = 99, denom df = 801, p-value = 0.9215
alternative hypothesis: true ratio of variances is not equal to 1
95 percent confidence interval:
 0.7406962 1.3406822
sample estimates:
ratio of variances 
          0.979391 

Interpretacja wyniku!

var.test(x.norm01, x.norm32)

    F test to compare two variances

data:  x.norm01 and x.norm32
F = 0.25, num df = 999, denom df = 999, p-value < 2.2e-16
alternative hypothesis: true ratio of variances is not equal to 1
95 percent confidence interval:
 0.2208247 0.2830300
sample estimates:
ratio of variances 
              0.25 

Interpretacja wyniku!

mood.test(x.norm01, x.norm32)

    Mood two-sample test of scale

data:  x.norm01 and x.norm32
Z = -5.3831, p-value = 7.323e-08
alternative hypothesis: two.sided

Interpretacja wyniku!

ansari.test(x.norm01, x.norm32)

    Ansari-Bradley test

data:  x.norm01 and x.norm32
AB = 541388, p-value = 2.408e-10
alternative hypothesis: true ratio of scales is not equal to 1

Interpretacja wyniku!

7.1.4 Test równości frakcji

prop.test(470, 1000, .5)

    1-sample proportions test with continuity correction

data:  470 out of 1000, null probability 0.5
X-squared = 3.481, df = 1, p-value = 0.06208
alternative hypothesis: true p is not equal to 0.5
95 percent confidence interval:
 0.4387437 0.5014896
sample estimates:
   p 
0.47 

Interpretacja wyniku!

7.2 Testy niemaprametryczne

7.2.1 Testy zgodności z rozkładem normalnym z biblioteki nortest

library(nortest)
cvm.test(x.norm01)

    Cramer-von Mises normality test

data:  x.norm01
W = 0.10747, p-value = 0.08869

Interpretacja wyniku!

ad.test(x.norm01)

    Anderson-Darling normality test

data:  x.norm01
A = 0.52378, p-value = 0.1819

Interpretacja wyniku!

shapiro.test(x.norm01)

    Shapiro-Wilk normality test

data:  x.norm01
W = 0.99829, p-value = 0.428

Interpretacja wyniku!

lillie.test(x.norm01)

    Lilliefors (Kolmogorov-Smirnov) normality test

data:  x.norm01
D = 0.028205, p-value = 0.05865

Interpretacja wyniku!

pearson.test(x.norm01)

    Pearson chi-square normality test

data:  x.norm01
P = 31.04, p-value = 0.3635

Interpretacja wyniku!

detach(package:nortest)

7.2.2 Inne testy zgosności z rozkładem normalnym

fBasics::dagoTest(x.norm01)

Title:
 D'Agostino Normality Test

Test Results:
  STATISTIC:
    Chi2 | Omnibus: 0.8855
    Z3  | Skewness: -0.4057
    Z4  | Kurtosis: 0.8491
  P VALUE:
    Omnibus  Test: 0.6423 
    Skewness Test: 0.685 
    Kurtosis Test: 0.3959 

Description:
 Thu May 14 22:08:00 2020 by user: user

7.2.3 Zodności z rozkładem jednostajnym (test chi-kwdrat)

set.seed(20200512)
x.unif <- runif(1000, 1, 3)
chisq.test(x.unif)
Aproksymacja chi-kwadrat mo戼㹦e by攼㸶 niepoprawna

    Chi-squared test for given probabilities

data:  x.unif
X-squared = 166.57, df = 999, p-value = 1

Interpretacja wyniku!

chisq.test(x.norm32 - min(x.norm32))

    Chi-squared test for given probabilities

data:  x.norm32 - min(x.norm32)
X-squared = 618.77, df = 999, p-value = 1

Interpretacja wyniku!

7.2.4 Zgodności z rozkładem wzorcowym (Kołmogorowa-Smirnowa)

ks.test(x.unif, "punif")

    One-sample Kolmogorov-Smirnov test

data:  x.unif
D = 1, p-value < 2.2e-16
alternative hypothesis: two-sided

Interpretacja wyniku!

ks.test(x.unif, "punif", 1, 3)

    One-sample Kolmogorov-Smirnov test

data:  x.unif
D = 0.022248, p-value = 0.7054
alternative hypothesis: two-sided

Interpretacja wyniku!

ks.test(x.norm32, "pnorm")

    One-sample Kolmogorov-Smirnov test

data:  x.norm32
D = 0.70315, p-value < 2.2e-16
alternative hypothesis: two-sided

Interpretacja wyniku!

ks.test(x.norm32, "pnorm", 3, 2)

    One-sample Kolmogorov-Smirnov test

data:  x.norm32
D = 0.024589, p-value = 0.581
alternative hypothesis: two-sided

Interpretacja wyniku!

7.2.5 Zgodności dwóch rozkładów (Kołmogorowa-Smirnowa)

ks.test(x.norm01, x.norm32)

    Two-sample Kolmogorov-Smirnov test

data:  x.norm01 and x.norm32
D = 0.714, p-value < 2.2e-16
alternative hypothesis: two-sided

Interpretacja wyniku!

ks.test(x.norm01, x.norm32, alternative = "greater")

    Two-sample Kolmogorov-Smirnov test

data:  x.norm01 and x.norm32
D^+ = 0.714, p-value < 2.2e-16
alternative hypothesis: the CDF of x lies above that of y

Interpretacja wyniku!

ks.test(x.norm01, x.norm32, alternative = "less")

    Two-sample Kolmogorov-Smirnov test

data:  x.norm01 and x.norm32
D^- = 0.002, p-value = 0.996
alternative hypothesis: the CDF of x lies below that of y

Interpretacja wyniku!

7.2.6 Testy niezależności

cor.test(x.norm01, x.norm32)

    Pearson's product-moment correlation

data:  x.norm01 and x.norm32
t = Inf, df = 998, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 1 1
sample estimates:
cor 
  1 

Interpretacja wyniku!

cor.test(x.norm01, df$ga)

    Pearson's product-moment correlation

data:  x.norm01 and df$ga
t = -0.62113, df = 998, p-value = 0.5347
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.08155156  0.04238688
sample estimates:
        cor 
-0.01965786 

Interpretacja wyniku!

cor.test(x.norm01, df$ga, method = "spearman")

    Spearman's rank correlation rho

data:  x.norm01 and df$ga
S = 167807514, p-value = 0.8288
alternative hypothesis: true rho is not equal to 0
sample estimates:
         rho 
-0.006846091 

Interpretacja wyniku!

chisq.test(tabela)

    Pearson's Chi-squared test with Yates' continuity correction

data:  tabela
X-squared = 1.9825, df = 1, p-value = 0.1591

Interpretacja wyniku!

7.2.7 Miary zależności oparte na statystyce chi kwadrat

7.2.7.1 V Cramera

lsr::cramersV(tabela)
[1] 0.1408003
DescTools::CramerV(tabela)
[1] 0.1615063

7.2.7.2 Czuprowa

DescTools::TschuprowT(tabela)
[1] 0.1615063

7.2.7.3 Phi

DescTools::Phi(tabela)
[1] 0.1615063

7.2.7.4 Kontyngencji C Pearsona

DescTools::ContCoef(tabela)
[1] 0.1594402

7.2.7.5 Yule’a

DescTools::YuleQ(tabela)
[1] -0.3233831
DescTools::YuleY(tabela)
[1] -0.1661555

8 Analiza regresji i ANOVA

daneMieszkania <- read_delim("http://www.biecek.pl/R/dane/daneMieszkania.csv", 
                             ";", escape_double = FALSE, trim_ws = TRUE)
Parsed with column specification:
cols(
  cena = col_double(),
  pokoi = col_double(),
  powierzchnia = col_double(),
  dzielnica = col_character(),
  `typ budynku` = col_character()
)
daneMieszkania %>% DT::datatable()

8.1 Zmiana zmiennych dzielnica i typ budynku na zmienne czynnikowe

daneMieszkania <- daneMieszkania %>%
  mutate_if(is.character, list(factor))

8.2 Podsumowanie obserwacji

daneMieszkania %>% summary
      cena            pokoi       powierzchnia         dzielnica      typ budynku
 Min.   : 83280   Min.   :1.00   Min.   :17.00   Biskupin   :65   kamienica :61  
 1st Qu.:143304   1st Qu.:2.00   1st Qu.:31.15   Krzyki     :79   niski blok:63  
 Median :174935   Median :3.00   Median :43.70   Srodmiescie:56   wiezowiec :76  
 Mean   :175934   Mean   :2.55   Mean   :46.20                                   
 3rd Qu.:208741   3rd Qu.:3.00   3rd Qu.:61.40                                   
 Max.   :295762   Max.   :4.00   Max.   :87.70                                   

8.3 Budowa modelu liniowego (regresja)

model <- lm(cena~dzielnica, data = daneMieszkania)
model %>% summary

Call:
lm(formula = cena ~ dzielnica, data = daneMieszkania)

Residuals:
   Min     1Q Median     3Q    Max 
-84893 -31892   -880  29611 106268 

Coefficients:
                     Estimate Std. Error t value Pr(>|t|)    
(Intercept)            189494       5238  36.178  < 2e-16 ***
dzielnicaKrzyki        -21321       7072  -3.015  0.00291 ** 
dzielnicaSrodmiescie   -18351       7699  -2.383  0.01810 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 42230 on 197 degrees of freedom
Multiple R-squared:  0.04873,   Adjusted R-squared:  0.03907 
F-statistic: 5.046 on 2 and 197 DF,  p-value: 0.007294

8.4 Sprawdzanie równości średnich w podgrupach

anova(model)
Analysis of Variance Table

Response: cena
           Df     Sum Sq    Mean Sq F value   Pr(>F)   
dzielnica   2 1.7995e+10 8997691613  5.0456 0.007294 **
Residuals 197 3.5130e+11 1783263361                    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Interpretacja wyniku!

8.5 Wykres

daneMieszkania %>%
  ggplot(aes(powierzchnia, cena)) +
  geom_point(aes(color = dzielnica)) +
  geom_smooth()

8.6 Sprawdzanie założeń regresji

8.6.1 Zależności liniowa

lmtest::harvtest(model)

    Harvey-Collier test

data:  model
HC = 1.3246, df = 196, p-value = 0.1868

Interpretacja wyniku!

lmtest::raintest(model)

    Rainbow test

data:  model
Rain = 0.98189, df1 = 100, df2 = 97, p-value = 0.5364

Interpretacja wyniku!

8.6.2 Normalność reszt

normtest::jb.norm.test(model$residuals)

    Jarque-Bera test for normality

data:  model$residuals
JB = 5.2583, p-value = 0.0555

Interpretacja wyniku!

8.6.3 Brak autokorelacji

lmtest::dwtest(model)

    Durbin-Watson test

data:  model
DW = 2.1565, p-value = 0.8655
alternative hypothesis: true autocorrelation is greater than 0

Interpretacja wyniku!

8.6.4 Homoskedatyczność (równość wariancji)

lmtest::gqtest(model)

    Goldfeld-Quandt test

data:  model
GQ = 1.0691, df1 = 97, df2 = 97, p-value = 0.3713
alternative hypothesis: variance increases from segment 1 to 2

Interpretacja wyniku!

lmtest::bptest(model)

    studentized Breusch-Pagan test

data:  model
BP = 2.2201, df = 2, p-value = 0.3295

Interpretacja wyniku!

lmtest::hmctest(model)

    Harrison-McCabe test

data:  model
HMC = 0.48313, p-value = 0.362

Interpretacja wyniku!

bartlett.test(cena~dzielnica, data = daneMieszkania)

    Bartlett test of homogeneity of variances

data:  cena by dzielnica
Bartlett's K-squared = 1.3075, df = 2, p-value = 0.5201

Interpretacja wyniku!

fligner.test(cena~dzielnica, data = daneMieszkania)

    Fligner-Killeen test of homogeneity of variances

data:  cena by dzielnica
Fligner-Killeen:med chi-squared = 1.7358, df = 2, p-value = 0.4198

Interpretacja wyniku!

car::leveneTest(residuals(model) ~ dzielnica, data = daneMieszkania)
Levene's Test for Homogeneity of Variance (center = median)
       Df F value Pr(>F)
group   2  0.6934 0.5011
      197               
lawstat::levene.test(residuals(model), daneMieszkania$dzielnica)

    Modified robust Brown-Forsythe Levene-type test based on the absolute deviations from
    the median

data:  residuals(model)
Test Statistic = 0.6934, p-value = 0.5011
lawstat::levene.test(residuals(model), daneMieszkania$dzielnica, location = "mean")

    Classical Levene's test based on the absolute deviations from the mean ( none not
    applied because the location is not set to median )

data:  residuals(model)
Test Statistic = 0.72784, p-value = 0.4842
mood.test(cena~dzielnica, data = daneMieszkania)
Błąd w poleceniu 'mood.test.formula(cena ~ dzielnica, data = daneMieszkania)':
  grupujący czynnik musi mieć dokładnie 2 poziomy

Interpretacja wyniku!

8.7 Wykresy diagnostyczne

plot(model)

8.8 Wykres pudełko-wąsy

daneMieszkania %>%
  ggplot(aes(x = dzielnica, y = cena)) +
  geom_boxplot()

8.9 Test post-hoc

TukeyHSD(aov(model))
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = model)

$dzielnica
                           diff       lwr        upr     p adj
Krzyki-Biskupin      -21321.019 -38021.10 -4620.9333 0.0081457
Srodmiescie-Biskupin -18350.541 -36532.88  -168.2053 0.0473579
Srodmiescie-Krzyki     2970.478 -14450.28 20391.2340 0.9145465
agricolae::scheffe.test(aov(model), "dzielnica", group=TRUE, console=TRUE)

Study: aov(model) ~ "dzielnica"

Scheffe Test for cena 

Mean Square Error  : 1783263361 

dzielnica,  means

Alpha: 0.05 ; DF Error: 197 
Critical Value of F: 3.041753 

Groups according to probability of means differences and alpha level( 0.05 )

Means with the same letter are not significantly different.
agricolae::LSD.test(aov(model), "dzielnica", p.adj="none", group=FALSE, console = TRUE)

Study: aov(model) ~ "dzielnica"

LSD t Test for cena 

Mean Square Error:  1783263361 

dzielnica,  means and individual ( 95 %) CI

Alpha: 0.05 ; DF Error: 197
Critical Value of t: 1.972079 

Comparison between treatments means
agricolae::LSD.test(aov(model), "dzielnica", p.adj="bonferroni", group=FALSE, console = TRUE) 

Study: aov(model) ~ "dzielnica"

LSD t Test for cena 
P value adjustment method: bonferroni 

Mean Square Error:  1783263361 

dzielnica,  means and individual ( 95 %) CI

Alpha: 0.05 ; DF Error: 197
Critical Value of t: 2.414597 

Comparison between treatments means
agricolae::duncan.test(aov(model), "dzielnica", group=TRUE, console=TRUE) 

Study: aov(model) ~ "dzielnica"

Duncan's new multiple range test
for cena 

Mean Square Error:  1783263361 

dzielnica,  means

Groups according to probability of means differences and alpha level( 0.05 )

Means with the same letter are not significantly different.
LS0tDQp0aXRsZTogIld5a8WCYWQgMTIgLSBTdGF0eXN0eWthIG1hdGVtYXR5Y3puYSAoa2llcnVuZWsgbWF0ZW1hdHlrYSBzcGVjamFsbm/Fm8SHIG1hdGVtYXR5a2EgZmluYW5zb3dhKSINCnN1YnRpdGxlOiAiUHJhY2EgdyDFm3JvZG93aXNrdSBSIg0KYXV0aG9yOg0KLSBKYXJvc8WCYXcgS290b3dpY3o6DQogICAgY29ycmVzcG9uZGVuY2U6IG5vDQogICAgZW1haWw6IGoua290b3dpY3pAdXdiLmVkdS5wbA0KICAgIGluc3RpdHV0ZTogSUlVd0INCmRhdGU6ICIxMyBtYWphIDIwMjAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGhpZ2hsaWdodDogaGFkZG9jaw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgcGFuZG9jX2FyZ3M6DQogICAgLSAtLWx1YS1maWx0ZXI9c2Nob2xhcmx5LW1ldGFkYXRhLmx1YQ0KICAgIC0gLS1sdWEtZmlsdGVyPWF1dGhvci1pbmZvLWJsb2Nrcy5sdWENCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICB0b2M6IHllcw0KaW5zdGl0dXRlOg0KLSBJSVV3QjogWmFrxYJhZCBCaW9pbmZvcm1hdHlraSwgSW5zdHl0dXQgSW5mb3JtYXR5a2ksIFVuaXdlcnN5dGV0IHcgQmlhxYJ5bXN0b2t1DQpjc2w6IGJpZy1kYXRhLWFuZC1pbmZvcm1hdGlvbi1hbmFseXRpY3MuY3NsDQphbHdheXNfYWxsb3dfaHRtbDogeWVzDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyBQcnp5Z290b3dhbmllIGRvIHByYWN5IMWbcm9kb3dpc2thIHByYWN5DQoNCiMjIEluc3RhbGFjamEgb3Byb2dyYW1vd2FuaWENCjEuIFIgIFtzdHJvbmEgZG9tb3dhIHd3dy5yLXByb2plY3Qub3JnXShodHRwczovL3d3dy5yLXByb2plY3Qub3JnKQ0KICAtIHd5YmllcmFteSBkb3dubG9hZA0KICAtIHd5YmllcmFteSBtaXJyb3IgbnAuIHNlcndlciBuaWVtaWVja2kNCiAgLSDFm2NpxIVnYW15IHBsaWsgaW5zdGFsYWN5am55DQoyLiBSc3R1ZGlvIChHVUkpIGRsYSBSIFtSU3R1ZGlvXShodHRwczovL3d3dy5yLnN0dWRpby5jb20vKQ0KICAtIG5hbGXFvHkgcG9icmHEhyBzeXN0ZW0gb3BlbnNvdXJjZQ0KMy4gUHJ6ZWdsxIVkYXJrYSBwbGlrw7N3ICoqcGRmKiogbmF0eXduYSBkbGEgUlN0dWRpbyAocG9kIFdpbmRvd3MpICoqc3VtYXRyYSoqIChvcGVuc291cmNlKQ0KICAtIHBvYmllcmFteSB6ZSBzdHJvbnkgW1N1bWF0cmFdKGh0dHBzOi8vd3d3LnN1bWF0cmFwZGZyZWFkZXIub3JnL2ZyZWUtcGRmLXJlYWRlci5odG1sKQ0KNC4gSW5zdGFsdWplbXkgdyBrb2xlam5vxZtjaSBSLCBzdW1hdHJhLCBSU3R1ZGlvICANCiAgDQojIyBQcnp5Z290b3dhbmllIGRvIHByYWN5DQoxLiBXIGthdGFsYWd1IGRva3VtZW50eSB0d29yenlteSBmb2xkZXIgKG5hendhIHpnb2RuaWUgeiB6YXNhZMSFICoqSW5mX3h4eHh4KiosIGdkemllICp4eHh4eCogdG8gbnVtZXIgYWxidW11KS4NCjIuIFVydWNoYW1pYW15ICpSU3VkaW8qDQozLiBaYWNob3d1amVteSBwb3J6xIVkZWsuIFR3b3J6eW15IG5vd3kgcHJvamVrdA0KICAtICpGaWxlcyogLT4gKk5ldyBQcm9qZWN0Li4uKiAtPiAqRXhpc3RpbmcgRGlyZWN0b3J5KiAtPiAqQnJvd3NlLi4uKiBpIHd5YmllcmFteSBmb2xkZXIsIGt0w7NyeSB1dHdvcnp5bGnFm215IG9yYXogemF6bmFjemFteSAqT3BlbiBpbiBuZXcgc2Vzc2lvbiouIDxCUj4NCjxzcGFuIHN0eWxlID0gImNvbG9yOiAgcmVkIj4gVXdhZ2E6IFphd3N6ZSBuYSBrb25pZWMgcHJhY3kvemFqxJnEhyBuYWxlxbx5IHphbWtuxIXEhyBwcm9qZWt0Ljwvc3Bhbj4gDQogICpGaWxlcyogLT4gKkNsb3NlIFByb2plY3QqICA8QlI+DQo8c3BhbiBzdHlsZSA9ICJjb2xvcjogIHJlZCI+IFV3YWdhOiBaYXdzemUgbmEgcG9jesSFdGt1IHByYWN5L3phasSZxIcgbmFsZcW8eSBvdHdvcnp5xIcgcHJvamVrdC48L3NwYW4+IA0KICAqRmlsZXMqIC0+ICpSZWNlbnQgUHJvamVjdHMqICBpIHd5YnJhxIcgc3fDs2ogKCpixJlkemllIG1pYcWCIG9uIHRha8SFIG5henfEmSwgamFrIG5hc3oga2F0YWxvZyopLg0KNC4gRG9zdG9zb3d1amVtICpSU3VkaW8qIGRvIG5hc3TEmXB1asSFY3ljaCB3eW1hZ2HFhDoNCiAgLSAgKlRvb2xzKiAtPiAgKlByb2plY3QgT3B0aW9ucyogLT4gDQogIDxvbD4NCiAgICA8bGk+ICpDb2RlIEVkaXRpbmcqIC0+ICAqVGV4dCBlbmNvZGluZyogd3liw7NyICoqVVRGLTgqKiA8L2xpPg0KICAgIDxsaT4gKlN3ZWF2ZSogLT4gICpQREYgR2VuZXJhaW9uKiAgd3liw7NyICoqa25pdHIqKjxCUj4NCjxzcGFuIHN0eWxlID0gImNvbG9yOiAgcmVkIj4gVXdhZ2E6IEplxbxlbGkgY2hjZW15IGtvcnp5c3RhxIcgeiBUZVggKExhVGVYKSwgdG8gbXVzaW15IG1pZcSHIHphaXN0YWxvd255IHN5c3RlbS9wcm9ncmFtIG5wLiBNaWtUZVguPC9zcGFuPiA8L2xpPg0KICA8L29sPg0KICAtICAqVG9vbHMqIC0+ICAqR2xvYmFsIE9wdGlvbnMqIC0+DQogIDxvbD4NCiAgICA8bGk+ICpBcHBlYXJhbmNlKiB3eWLDs3Igd3lnbMSFZHUgKlJTdWRpb24qIG5wLiAqKmFtYmlhbmNlKiogKCpjaWVtbnkgbW90eXcqKS4gPC9saT4NCiAgPC9vbD4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KNS4gVyBkb211IGluc3RhbHVqZW15IGtvbmllY3puZSBiaWJsaW90ZWtpICoqUioqIG5henl3YW5lICoqcGFja2FnZSoqDQogIC0gZG9sbmUgcHJhd2Ugb2tpZW5rbyAtIG9rbm8gKlBhY2thZ2VzKiAtPiAqSW5zdGFsbCogIHdwaXN1amVteSAqKnRpZHl2ZXJzZSoqDQo2LiBPdHdpZXJhbXkgcGxpa2kgDQogIC0gKipSIFNjcmlwdCoqOiAqRmlsZXMqIC0+ICpOZXcgRmlsZSogLT4gKlIgU2NyaXB0Kg0KICAtICoqUiBOb3RlYm9vayoqOiAgKkZpbGVzKiAtPiAqTmV3IEZpbGUqIC0+ICpSIE5vdGVib29rKg0KICAtICoqUiBNYXJrZG93bioqOiAqRmlsZXMqIC0+ICpOZXcgRmlsZSogLT4gKlIgTWFya2Rvd24qIGkgd3liaWVyYW15IHJvZHphaiBkb2t1bWVudHUgbnAuICpEb2N1bWVudCogbHViICpQcmVzZW50YXRpb24qLiAgIDxCUj4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KNy4gQsSZZHppZW15IHphd3N6ZSBwcmFjb3dhxIcgYWxibyB3IHBsaWt1IHNrcnlwdG93eW0gKlIqIGFsYm8gdyAqUiBOb3RlYm9vayouDQo4LiBJbmZvcm1hY2plIGRsYSBwbGlrdSBza3J5cHRvd2VnbyANCiAgLSB3eXdvxYJhbmllIGFrdHVhbG5laiBsaW5paSB3IGtvbnNvbGkgKkNUUkwrRU5URVIqDQogIC0gb2R3b8WCYW5pZSBzacSZIGRvIGZ1bmtjamkgeiBkYW5laiBiaWJsaW90ZWtpICpwYWNrYWdlOjpmdW5jdGlvbiogbnAuICpzdGF0czo6ZmlsdGVyKg0KICAtIHBvZGN6eXRhbmllIGJpYmxpb3Rla2kgKmxpYnJhcnkobmF6d2EgYmliaWxpb3Rla2kpKg0KICAtIHpuYWsga29tZW50YXJ6YSBoYXNodGFnIHR6bi4gIw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQo5LiBJbXBvcnQgZGFueWNoIA0KICAtIGtvcnp5c3RhbXkgeiBkYW55Y2ggZG9zdMSZcG55Y2ggbmEgc3Ryb25pZSBbSmFyZWRhIFAuIExhbmRlcmFdKGh0dHBzOi8vamFyZWRsYW5kZXIuY29tL2RhdGEvKQ0KICAtIHBsaWsgKmNzdiogKHRleHRvd2UpIGltcG9ydDogKkZpbGUqIC0+ICpJbXBvcnQgRGF0YXNldCogLT4gKkZyb20gVGV4dCAocmVhZHIpKiAgKHcgcG9sdSAqRmlsZS9VUkwqDQogIGtvcGl1amVteSBhZHJlcyAqaHR0cHM6Ly9qYXJlZGxhbmRlci5jb20vZGF0YS8qIGkgZG9waXN1amVteSBuYXp3xJkgcGxpa3UgbnAuICphY3NfbnkuY3N2KikgLT4gKlVwZGF0ZSoNCiAgLSB3eWtvcnp5c3R1amVteSBvcGNqZSAqSW1wb3J0IE9wdGlvbnMqCWFieSB3eWJyYcSHIG5wLiB6bmFrIHJvemR6aWVsYWrEhWN5IGRhbmUgxZtyZWRuaWsgdG8gKnNlbWljb2xvbiogaXRkLg0KICAtIG1vxbxlbXkgem1pZW5pxIcgdGXFvCB0eXB5IGtvbHVtbiB3eWJpZXJhasSFYyBkbGEga29sdW1ueSB6IG1lbnUgcm96d2lqYWxuZWdvIHdhcnRvxZvEhyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogIHJlZCI+IFV3YWdhOiBXIHByenlwYWRrdSB0eXB1ICpmYWN0b3IqICh6bWllbm5hIGN6eW5uaWtvd2EpIG5hbGXFvHkgd3NrYXphxIcgd3N6eXN0a2llIHBvemlvbXkgKHdhcnRvxZtjaSkuPC9zcGFuPiANCiAgLSBrb3BpdWplbXkgeiBva2llbmthICpDb2RlIFByZXZpZXcqIGRvIHBsaWt1IHNrcnlwdG93ZWdvIHBvbGVjZW5pZSBwb2N6eXR1asSFYyB3Y3plxZtuaWVqIGJpYmxpb3Rla8SZICpyZWFkciosIGphIHR1dGFqIHBvZGN6eXR1asSZICp0aWR5dmVyc2UqLg0KICAtIGJyYWtpIGRhbnljaCAqKk5BKiogIChza3LDs3Qgb2QgKm5vdCBhdmFpbGFibGUqKSwgbmllbGljemJ5ICoqTmFOKiogKCpub3QgYSBudW1iZXIqKQ0KICAtIHR5cCAqbnVtZXJpYyogdyBSIGlkZW50eWN6bnkgamFrICpkb3VibGUqIHcgQysrIGkgY28gd2nEmWNlaiB1xbx5d2FuZSB0ZcW8IGplc3QgcsOzd25vd2HFvG5pZSAqbnVtZXJpYyogaSAqZG91YmxlKg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoJDQpQcnp5a8WCYWQgKHNrb3Bpb3dhbnkga29kIGkgbmFzdMSZcG5pZSB1cG9yesSFZGtvd2FueSkNCmBgYHtyfQ0KYWNzTlkgPC0gcmVhZF9jc3YoImh0dHBzOi8vamFyZWRsYW5kZXIuY29tL2RhdGEvYWNzX255LmNzdiIsIA0KICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scyhFbGVjdHJpY0JpbGwgPSBjb2xfaW50ZWdlcigpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmFtaWx5SW5jb21lID0gY29sX2ludGVnZXIoKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZhbWlseVR5cGUgPSBjb2xfZmFjdG9yKGxldmVscyA9IGMoIk1hcnJpZWQiLCAiRmVtYWxlIEhlYWQiLCAiTWFsZSBIZWFkIikpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSG91c2VDb3N0cyA9IGNvbF9pbnRlZ2VyKCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbnN1cmFuY2UgPSBjb2xfaW50ZWdlcigpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtQmVkcm9vbXMgPSBjb2xfaW50ZWdlcigpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtQ2hpbGRyZW4gPSBjb2xfaW50ZWdlcigpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtUGVvcGxlID0gY29sX2ludGVnZXIoKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bVJvb21zID0gY29sX2ludGVnZXIoKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bVZlaGljbGVzID0gY29sX2ludGVnZXIoKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bVdvcmtlcnMgPSBjb2xfaW50ZWdlcigpKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoxMC4gUHJ6ZXR3YXJ6YW5pZSBwb3Rva293ZSB0encuICpwaXBlKiAoYmlibGlvdGVrYSAqbWFncml0dHIqKSA8c3BhbiBzdHlsZSA9ICJjb2xvcjogIHJlZCI+IFV3YWdhOiBVxbx5Y2llIGJpYmxpb3Rla2kgKmRwbHlyKiBwb3dvZHVqZSBtb8W8bGl3b8WbxIcga29yenlzdGFuaWEgeiBwcnpldHdhcnphbmlhIHBvdG9rb3dlZ28uIFXFvHl3YWrEhWMgYmlibGlvdGVraSAqdGlkeXZlcnNlKiBwb2Rjenl0dWrEmSByw7N3bmllxbwgKmRwbHlyKi48L3NwYW4+DQo8Y2VudGVyPg0KKipjbyB3eXN5xYJhbSAlPiUgZ2R6aWUgd3lzecWCYW0qKg0KPC9jZW50ZXI+DQpaYXNhZGEgIHByemV0d2FyemFuaWEgcG90a293ZWdvDQoqKmYoYSkqKiBqZXN0IHRvICAqKmEgJT4lIGYqKg0KDQoqKmYoYSxiKSoqIGplc3QgdG8gKiphICU+JSBmKGIpKioNCg0KKipmKGEsYikqKiBqZXN0IHRvICoqYiAlPiUgZihhLC4pKiogKipmKGEsYikqKiA8c3BhbiBzdHlsZSA9ICJjb2xvcjogIHJlZCI+IFV3YWdhOiAqKktyb3BrYSoqIHdza2F6dWplIG1pZWpzY2Egd3N0YXdpZW5pYSB6bWllbm5lai48L3NwYW4+DQoNCioqZyhmKGEpKSoqIGplc3QgdG8gKiphICU+JSBmICU+JSBnKioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KMTEuIFJlZ3XFgmEgd2NpxJnEhyBwb3ByYXdueSB6YXBpcyB6Z29kbnkgeiByZWd1xYLEhSB3Y2nEmcSHIHRvDQoNCmBgYHtyfQ0KYWNzTlkgJT4lIA0KICBzdW1tYXJ5KCkNCmBgYA0KDQpgYGB7ciBpbmNsdWRlID0gRkFMU0V9DQojIGRldGFjaChwYWNrYWdlOnRpZHl2ZXJzZSkNCiMgZGV0YWNoKHBhY2thZ2U6Z2dwbG90MikNCiMgZGV0YWNoKHBhY2thZ2U6dGliYmxlKQ0KIyBkZXRhY2gocGFja2FnZTp0aWR5cikNCiMgZGV0YWNoKHBhY2thZ2U6cmVhZHIpDQojIGRldGFjaChwYWNrYWdlOnB1cnJyKQ0KIyBkZXRhY2gocGFja2FnZTpkcGx5cikNCiMgZGV0YWNoKHBhY2thZ2U6c3RyaW5ncikNCiMgZGV0YWNoKHBhY2thZ2U6Zm9yY2F0cykNCg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgRnVua2NqZSB3eXdvxYJ1asSFY2Ugcm96a8WCYWR5IHcgKipSKioNCg0KQnVkb3dhIGZ1bmtjamkgKnByZWZpeCogKipOYXp3YVJvemvFgmFkdSoqDQoNClJvZHphamUgKnByZWZpa3PDs3cqDQoNCjEuICoqZCoqIC0gZnVua2NqYSBnxJlzdG/Fm2NpIHcgcHJ6eXBhZGt1IHJvemvFgmFkdSBjacSFZ8WCZWdvIGx1YiBmdW5rY2phIHByYXdkb3BvZG9iaWXFhHN0d2EgdyBwcnp5cGFka3Ugcm96a8WCxIVkdSBkeXNrcmV0bmVnbw0KMi4gKipwKiogLSBkeXN0cnlidWFudGEgcm96a8WCYWR1DQozLiAqKnEqKiAtIGt3YW50eWxlIHJvemvFgmFkdQ0KNC4gKipyKiogLSBsaWN6YmEgKGxpY3pieSkgcHNldWRvbG9zb3dlIHogcm96a8WCYWR1DQoNCk5hend5IHJvemvFgmFkw7N3DQoNCjEuICoqbm9ybSoqIC0gbm9yYW1sbnksDQoyLiAqKmdhbW1hKioNCjMuICoqYmV0YSoqDQo0LiAqKmNoaXNxKioNCjUuIGl0ZA0KDQoxLiBXeXpuYWN6YW15IHdhcnRvxZvEhyBnxJlzdG/Fm2NpIHJvemvFgmFkdSBub3JtYWxuZWdvIHN0YW5kYXJkb3dlZ28gZGxhIGFyZ3VtZW50dSAwLg0KYGBge3IgZWNobz1UUlVFfQ0KZG5vcm0oMCkNCmBgYA0KDQoyLiBXeXpuYWN6YW15IHdhcnRvxZvEhyBnxJlzdG/Fm2NpIHJvemvFgmFkdSAqTigyLDUpKiBkbGEgYXJndW1lbnR1IDAuDQpgYGB7cn0NCmRub3JtKDAsIG1lYW4gPSAyLCBzZCA9IDUpDQpgYGANCjMuIFd5em5hY3phbXkgd2FydG/Fm8SHIGR5c3RyeWJ1YW50eSByb3prxYJhZHUgbm9ybWFsbmVnbyBzdGFuZGFyZG93ZWdvIGRsYSBhcmd1bWVudHUgb2QgMCBkbyAxIHoga3Jva2llbSAuMi4NCmBgYHtyfQ0KcG5vcm0oc2VxKDAsIDEsIGJ5ID0gLjIpKQ0KYGBgDQoNCiMjIExpY3pieSBwc2V1ZG9sb3Nvd2UgeiB6YWRhbmVnbyByb3prxYJhZHUgcHJhd2RvcG9kb2JpZcWEc3R3YQ0KYGBge3J9DQpzZXQuc2VlZCgyMDIwMDUxMikNCngubm9ybTMyIDwtIHJub3JtKDEwMDAsIG1lYW4gPSAzLCBzZCA9IDIpDQpzZXQuc2VlZCgyMDIwMDUxMikNCngubm9ybTAxIDwtIHJub3JtKDEwMDAsIG1lYW4gPSAwLCBzZCA9IDEpDQpgYGANCg0KIyMgRHlzdHJ5YnVhbnRhDQpgYGB7cn0NCnBub3JtKDApDQpgYGANCiMjIEfEmXN0b8WbxIcNCmBgYHtyfQ0KZG5vcm0oMCkNCmBgYA0KDQojIyBLd2FudHlsZQ0KYGBge3J9DQpxbm9ybShjKC4yNSwgLjUsIC43NSkpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBTdGF0eXN0eWthIG9waXNvd2ENCg0KIyMgU3plcmVnIHJvemR6aWVsY3p5IHByemVkemlhxYJvd3kgKHByb3N0eSwgc2t1bXVsb3dhbnkpDQoNCiMjIyBabWllbm5hIGplZG5vd3ltaWFyb3dhDQpgYGB7cn0NCnRhYmxlKGN1dCh4Lm5vcm0wMSwgc2VxKG1pbih4Lm5vcm0wMSksIG1heCh4Lm5vcm0wMSkrLjIsIGJ5ID0gMSksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpDQpgYGANCg0KYGBge3J9DQpzam1pc2M6OmZycShzYW1wbGUoYygieCIsICJ5IiwgInoiKSwgc2l6ZSA9IDEwMDAsIHJlcGxhY2UgPSBUUlVFLCBwcm9iID0gYyguMywgLjUsIC4yKSkpDQpgYGANCg0KIyMjIFptaWVubmEgd2llbG93eW1pYXJvd2EgKHJhbWthIGRhbnljaCAqKmRhdGEuZnJhbWUqKikNCmBgYHtyfQ0KZGYwMSA8LSBkYXRhLmZyYW1lKHptMSA9IHNhbXBsZShjKCJhIiwgImIiKSwgMTAwLCByZXBsYWNlID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgem0yID0gc2FtcGxlKGMoImMiLCAiZCIpLCAxMDAsIHJlcGxhY2UgPSBUUlVFKSkNCmBgYA0KDQpgYGB7cn0NCnNqbWlzYzo6ZnJxKGRmMDEpDQpgYGANCg0KIyMgVGFibGljYSBrb250eW5nZW5jamkNCg0KYGBge3J9DQoodGFiZWxhIDwtIHRhYmxlKGRmMDEkem0xLCBkZjAxJHptMikpDQpgYGANCg0KIyMgxZpyZWRuaWENCmBgYHtyfQ0KbWVhbih4Lm5vcm0wMSkNCm1lYW4oeC5ub3JtMzIpDQpgYGANCg0KIyMgV2FyaWFuY2phDQpgYGB7cn0NCnZhcih4Lm5vcm0wMSkNCmBgYA0KDQojIyBPZGNoeWxlbmllIHN0YW5kcmFkb3dlDQpgYGB7cn0NCnNkKHgubm9ybTMyKQ0KYGBgDQoNCiMjIFR6dy4gKmZpdmVudW0qIChtaW5pbXVtLCBrd2FydHlsZSwgbWFrc2ltdW0pDQpgYGB7cn0NCmZpdmVudW0oeC5ub3JtMDEpDQpgYGANCg0KIyMgS3dhbnR5bGUNCmBgYHtyfQ0KcXVhbnRpbGUoeC5ub3JtMDEsIHNlcSgwLDEsIGJ5ID0gLjEpKQ0KYGBgDQoNCiMjIFpha3JlcyB6bWllbm5vxZtjaQ0KYGBge3J9DQpyYW5nZSh4Lm5vcm0zMikNCmBgYA0KDQojIyBNZWRpYW5hDQpgYGB7cn0NCm1lZGlhbih4Lm5vcm0zMikNCmBgYA0KDQojIyBQb2RzdW1vd2FuaWUgaSAqc3VtbWFyeSogdnMuICpmaXZlbnVtKg0KYGBge3J9DQpzdW1tYXJ5KHgubm9ybTMyKQ0KYGBgDQoNCmBgYHtyfQ0KZml2ZW51bSh4Lm5vcm0zMikNCmBgYA0KIyMgUG9kc3Rhd293ZSBtaWFyeSBzdGF0eXN0eWtpIG9waXNvd2VqDQoNCmBgYHtyfQ0KcHN5Y2g6OmRlc2NyaWJlKHgubm9ybTMyLCBxdWFudCA9IGMoLjI1LCAuNzUpLCBJUVIgPSBUUlVFKQ0KYGBgDQpgYGB7cn0NCkhtaXNjOjpkZXNjcmliZSh4Lm5vcm0zMikNCmBgYA0KDQoNCiMjIFJvenN0xJlwIG1pxJlkennEh3dpYXJ0a293eSAoY3p5bGkgcG9kd29qb25lIG9kY2h5bGVuaWUgxId3aWFydGtvd2UpDQpgYGB7cn0NCklRUih4Lm5vcm0zMikNCmBgYA0KDQojIyBTa2/Fm25vxZvEhyAod3l3b8WCYW5pZSBuYSBkd2Egc3Bvc29ieSBpIHRyemVjaCBiaWJsaW90ZWspDQpgYGB7cn0NCmUxMDcxOjpza2V3bmVzcyh4Lm5vcm0wMSkNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZTEwNzEpDQpza2V3bmVzcyh4Lm5vcm0zMikNCmRldGFjaChwYWNrYWdlOmUxMDcxKQ0KYGBgDQoNCmBgYHtyfQ0KYWdyaWNvbGFlOjpza2V3bmVzcyh4Lm5vcm0wMSkNCmBgYA0KDQpgYGB7cn0NCnBzeWNoOjpza2V3KHgubm9ybTAxKQ0KYGBgDQoNCiMjIE1vZGENCmBgYHtyfQ0KUlZBaWRlTWVtb2lyZTo6bW9kKHgubm9ybTMyKQ0KYGBgDQoNCiMjIEt1cnRvemENCmBgYHtyfQ0KZTEwNzE6Omt1cnRvc2lzKHgubm9ybTAxKQ0KYGBgDQoNCmBgYHtyfQ0KYWdyaWNvbGFlOjprdXJ0b3Npcyh4Lm5vcm0wMSkNCmBgYA0KDQpgYGB7cn0NCnBzeWNoOjprdXJ0b3NpKHgubm9ybTAxKQ0KYGBgDQoNCiMjIE1vbWVudHkNCg0KIyMjIFogZG9tecWbbG55bWkgcGFyYW1ldHJhbWkNCmBgYHtyfQ0KZTEwNzE6Om1vbWVudCh4Lm5vcm0zMikNCmBgYA0KDQojIyMgT2Rwb3dpZWRuaWVnbyByesSZZHUNCmBgYHtyfQ0KZTEwNzE6Om1vbWVudCh4Lm5vcm0zMiwgb3JkZXIgPSAyKQ0KYGBgDQoNCiMjIyBDZW50cmFsbmUNCmBgYHtyfQ0KZTEwNzE6Om1vbWVudCh4Lm5vcm0zMiwgb3JkZXIgPSAyLCBjZW50ZXIgPSBUUlVFKQ0KYGBgDQoNCiMjIyBNb21lbnQgY2VudHJhbG55IHJ6xJlkdSAyIHZzLiB3YXJpYW5jamEgKCp2YXIqKQ0KYGBge3J9DQplMTA3MTo6bW9tZW50KHgubm9ybTMyLCBvcmRlciA9IDIsIGNlbnRlciA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQp2YXIoeC5ub3JtMzIpDQp2YXIoeC5ub3JtMzIpL2xlbmd0aCh4Lm5vcm0zMikqKGxlbmd0aCh4Lm5vcm0zMiktMSkNCmBgYA0KDQojIyBLb3JlbGFjamEgaSBrb3dhcmlhbmNqYSAobWFjaWVyemUga29yZWxhY2ppIGkga293YXJpYW5jamkpDQpgYGB7cn0NCmRmIDwtIGRhdGEuZnJhbWUobm9ybTAxID0geC5ub3JtMDEsIG5vcm0zMiA9IHgubm9ybTMyLCBnYSA9IHJnYW1tYSgxMDAwLCAyLCAxKSkgJT4lIGFzX3RpYmJsZSgpDQpgYGANCg0KIyMjIEtvd2FyaWFuY2phDQoNCmBgYHtyfQ0KY292KHgubm9ybTAxLCBkZiRnYSkNCmBgYA0KDQpgYGB7cn0NCmNvdihkZikNCmBgYA0KDQojIyMgS29yZWxhY2phDQpgYGB7cn0NCmNvcihkZikNCmBgYA0KDQpgYGB7cn0NCkhtaXNjOjpyY29ycihhcy5tYXRyaXgoZGYpLCB0eXBlID0gInBlYXJzb24iKQ0KYGBgDQoNCmBgYHtyfQ0KSG1pc2M6OnJjb3JyKGFzLm1hdHJpeChkZiksIHR5cGUgPSAic3BlYXJtYW4iKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgU3RhdHlzdHlraSBncmFmaWN6bmUgeiBnZ3Bsb3QyDQoNCiMjIER5c3RyeWJ1YW50YSBlbXBpcnljem5hDQpgYGB7cn0NCmRmICU+JQ0KZ2dwbG90KGFlcyh4ID0gbm9ybTAxKSkgKw0KICBzdGF0X2VjZGYoKQ0KYGBgDQoNCiMjIEfEmXN0b8WbxIcNCmBgYHtyfQ0KZGYgJT4lDQogIGdncGxvdChhZXMoeC5ub3JtMDEpKSArDQogIGdlb21fZGVuc2l0eShrZXJuZWwgPSAiZ2F1c3NpYW4iKQ0KYGBgDQoNCiMjIEhpc3RvZ3JhbQ0KYGBge3J9DQpkZiAlPiUNCiAgZ2dwbG90KGFlcyh4Lm5vcm0wMSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBgDQoNCmBgYHtyfQ0KZGYgJT4lDQogIGdncGxvdChhZXMoeC5ub3JtMDEpKSArDQogIGdlb21fZnJlcXBvbHkoKQ0KYGBgDQoNCiMjIFd5a3JlcyBrd2FudHlsaQ0KYGBge3J9DQpkZiAlPiUNCiAgZ2dwbG90KCkgKw0KICBnZW9tX3FxKGFlcyhzYW1wbGU9IHgubm9ybTAxKSkNCmBgYA0KDQpgYGB7cn0NCkdHYWxseTo6Z2dwYWlycyhkZikNCg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgRXN0eW1hY2phIHB1bmt0b3dhIG1ldG9kxIUgbmFqd2nEmWtzemVqIHdpYXJvZ29kbm9zY2kgKGJpYmxpb3Rla2EgKipNQVNTKiopDQpgYGB7cn0NCk1BU1M6OmZpdGRpc3RyKHgubm9ybTAxLCAibm9ybWFsIikNCmBgYA0KDQpgYGB7cn0NCk1BU1M6OmZpdGRpc3RyKGRmJGdhLCAiZ2FtbWEiKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgRXN0eW1hY2phIHByemVkemlhxYJvd2ENCg0KIyMgUsOzxbxuZSBmdW5rY2plIHd5em5hY3phasSFY2UgcHJ6ZWR6aWHFgiB1Zm5vxZtjaSBkbGEgxZtyZWRuaWVqDQpgYGB7cn0NClJtaXNjOjpDSSh4Lm5vcm0wMSkNCmBgYA0KDQpgYGB7cn0NCkRlc2NUb29sczo6TWVhbkNJKHgubm9ybTAxKQ0KYGBgDQoNCmBgYHtyfQ0KdC50ZXN0KHgubm9ybTAxKQ0KYGBgDQoNCmBgYHtyfQ0KZ21vZGVsczo6Y2koeC5ub3JtMDEpDQpgYGANCg0KIyMgUHJ6ZWR6aWHFgiB1Zm5vxZtjaSBkbGEgZnJha2NqaQ0KYGBge3J9DQpzZXQuc2VlZCgyMDIwMDUxMikNCnguYmluIDwtcmJpbm9tKDEwMDAsIHNpemUgPSAxLCBwcm9iID0gLjMpDQpnbW9kZWxzOjpjaS5iaW5vbSh4LmJpbikNCmBgYA0KDQpgYGB7cn0NCnByb3AudGVzdChzdW0oeC5iaW4pLCBsZW5ndGgoeC5iaW4pLCAuMykNCmBgYA0KDQojIyBCdWRvd2Egd8WCYXNuZWogZnVua2NqaSB3eXpuYWN6YWrEhWNlaiBwcnplZHppYcWCIHVmbm/Fm2NpIGRsYSB3YXJpYW5jamkNCmBgYHtyfQ0KQ0lfdmFyX04gPC0gZnVuY3Rpb24oZGFuZSwgYWxwaGEgPSAuMDUpIHsNCiAga29uaWVjbGV3eSA8LSAobGVuZ3RoKGRhbmUpIC0xKSAqIHZhcihkYW5lKSAvIHFjaGlzcSgxLSBhbHBoYS8yLCBkZiA9IGxlbmd0aChkYW5lKSAtIDEpDQogIGtvbmllY3ByYXd5IDwtIChsZW5ndGgoZGFuZSkgLTEpICogdmFyKGRhbmUpIC8gcWNoaXNxKGFscGhhLzIsIGRmID0gbGVuZ3RoKGRhbmUpIC0gMSkNCiAgY2F0KHBhc3RlKCJQcnplZHppYcWCIHVmbm/Fm2NpIGRsYSB3YXJpYW5jamkgd3lub3NpOlxuIFsiLCBrb25pZWNsZXd5LCAiLCIsIGtvbmllY3ByYXd5LCAiXVxuIikpDQp9DQoNCkNJX3Zhcl9OKHgubm9ybTAxKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgVGVzdG93YW5pZSBoaXBvdGV6IGlzdG90b25vxZtjaQ0KDQojIyBUZXN0eSBwYXJhbWV0cnljem5lIA0KDQojIyMgVGVzdCBkbGEgxZtyZWRuaWVqDQpgYGB7cn0NCnQudGVzdCh4Lm5vcm0wMSkNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KYGBge3J9DQp0LnRlc3QoeC5ub3JtMzIpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0KdC50ZXN0KHgubm9ybTMyLCBtdSA9IDMpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCiMjIyMgUsOzxbxuZSBhbHRlcm5hdHl3eQ0KYGBge3J9DQp0LnRlc3QoeC5ub3JtMzIsIG11ID0gMywgYWx0ZXJuYXRpdmUgPSAibGVzcyIpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0KdC50ZXN0KHgubm9ybTMyLCBtdSA9IDMsIGFsdGVybmF0aXZlID0gImdyZWF0ZXIiKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQojIyMgVGVzdHkgZHfDs2NoIMWbcmVkbmljaA0KYGBge3J9DQp0LnRlc3QoeC5ub3JtMzIsIHgubm9ybTAxKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCnQudGVzdCh4Lm5vcm0zMlsxOjUwMF0sIHgubm9ybTMyWzUwMToxMDAwXSkNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KIyMjIFRlc3R5IGRsYSBkd8OzY2ggd2FyaWFuY2ppDQoNCmBgYHtyfQ0KdmFyLnRlc3QoeC5ub3JtMDEsIHgubm9ybTMyKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCnZhci50ZXN0KHgubm9ybTAxWzE6MTAwXSwgeC5ub3JtMDFbOTAxOjEwMF0pDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0KdmFyLnRlc3QoeC5ub3JtMDEsIHgubm9ybTMyKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCm1vb2QudGVzdCh4Lm5vcm0wMSwgeC5ub3JtMzIpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0KYW5zYXJpLnRlc3QoeC5ub3JtMDEsIHgubm9ybTMyKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQojIyMgVGVzdCByw7N3bm/Fm2NpIGZyYWtjamkNCmBgYHtyfQ0KcHJvcC50ZXN0KDQ3MCwgMTAwMCwgLjUpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCiMjIFRlc3R5IG5pZW1hcHJhbWV0cnljem5lDQoNCiMjIyBUZXN0eSB6Z29kbm/Fm2NpIHogcm96a8WCYWRlbSBub3JtYWxueW0geiBiaWJsaW90ZWtpICoqbm9ydGVzdCoqDQpgYGB7cn0NCmxpYnJhcnkobm9ydGVzdCkNCmBgYA0KDQpgYGB7cn0NCmN2bS50ZXN0KHgubm9ybTAxKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmFkLnRlc3QoeC5ub3JtMDEpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0Kc2hhcGlyby50ZXN0KHgubm9ybTAxKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmxpbGxpZS50ZXN0KHgubm9ybTAxKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCnBlYXJzb24udGVzdCh4Lm5vcm0wMSkNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KYGBge3J9DQpkZXRhY2gocGFja2FnZTpub3J0ZXN0KQ0KYGBgDQoNCiMjIyBJbm5lIHRlc3R5IHpnb3Nub8WbY2kgeiByb3prxYJhZGVtIG5vcm1hbG55bQ0KYGBge3J9DQpmQmFzaWNzOjpkYWdvVGVzdCh4Lm5vcm0wMSkNCmBgYA0KDQojIyMgWm9kbm/Fm2NpIHogcm96a8WCYWRlbSBqZWRub3N0YWpueW0gKHRlc3QgY2hpLWt3ZHJhdCkNCmBgYHtyfQ0Kc2V0LnNlZWQoMjAyMDA1MTIpDQp4LnVuaWYgPC0gcnVuaWYoMTAwMCwgMSwgMykNCmBgYA0KDQpgYGB7cn0NCmNoaXNxLnRlc3QoeC51bmlmKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmNoaXNxLnRlc3QoeC5ub3JtMzIgLSBtaW4oeC5ub3JtMzIpKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQojIyMgWmdvZG5vxZtjaSB6IHJvemvFgmFkZW0gd3pvcmNvd3ltIChLb8WCbW9nb3Jvd2EtU21pcm5vd2EpDQpgYGB7cn0NCmtzLnRlc3QoeC51bmlmLCAicHVuaWYiKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmtzLnRlc3QoeC51bmlmLCAicHVuaWYiLCAxLCAzKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmtzLnRlc3QoeC5ub3JtMzIsICJwbm9ybSIpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0Ka3MudGVzdCh4Lm5vcm0zMiwgInBub3JtIiwgMywgMikNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KIyMjIFpnb2Rub8WbY2kgZHfDs2NoIHJvemvFgmFkw7N3IChLb8WCbW9nb3Jvd2EtU21pcm5vd2EpDQpgYGB7cn0NCmtzLnRlc3QoeC5ub3JtMDEsIHgubm9ybTMyKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmtzLnRlc3QoeC5ub3JtMDEsIHgubm9ybTMyLCBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyIikNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KYGBge3J9DQprcy50ZXN0KHgubm9ybTAxLCB4Lm5vcm0zMiwgYWx0ZXJuYXRpdmUgPSAibGVzcyIpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCiMjIyBUZXN0eSBuaWV6YWxlxbxub8WbY2kNCg0KYGBge3J9DQpjb3IudGVzdCh4Lm5vcm0wMSwgeC5ub3JtMzIpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0KY29yLnRlc3QoeC5ub3JtMDEsIGRmJGdhKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmNvci50ZXN0KHgubm9ybTAxLCBkZiRnYSwgbWV0aG9kID0gInNwZWFybWFuIikNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KYGBge3J9DQpjaGlzcS50ZXN0KHRhYmVsYSkNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KIyMjIE1pYXJ5IHphbGXFvG5vxZtjaSBvcGFydGUgbmEgc3RhdHlzdHljZSAqY2hpIGt3YWRyYXQqDQoNCiMjIyMgViBDcmFtZXJhDQpgYGB7cn0NCmxzcjo6Y3JhbWVyc1YodGFiZWxhKQ0KYGBgDQoNCmBgYHtyfQ0KRGVzY1Rvb2xzOjpDcmFtZXJWKHRhYmVsYSkNCmBgYA0KDQojIyMjIEN6dXByb3dhDQpgYGB7cn0NCkRlc2NUb29sczo6VHNjaHVwcm93VCh0YWJlbGEpDQpgYGANCg0KIyMjIyBQaGkNCmBgYHtyfQ0KRGVzY1Rvb2xzOjpQaGkodGFiZWxhKQ0KYGBgDQoNCiMjIyMgS29udHluZ2VuY2ppIEMgUGVhcnNvbmENCmBgYHtyfQ0KRGVzY1Rvb2xzOjpDb250Q29lZih0YWJlbGEpDQpgYGANCg0KIyMjIyBZdWxlJ2ENCmBgYHtyfQ0KRGVzY1Rvb2xzOjpZdWxlUSh0YWJlbGEpDQpgYGANCg0KYGBge3J9DQpEZXNjVG9vbHM6Oll1bGVZKHRhYmVsYSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIEFuYWxpemEgcmVncmVzamkgaSBBTk9WQQ0KDQpgYGB7cn0NCmRhbmVNaWVzemthbmlhIDwtIHJlYWRfZGVsaW0oImh0dHA6Ly93d3cuYmllY2VrLnBsL1IvZGFuZS9kYW5lTWllc3prYW5pYS5jc3YiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjsiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIHRyaW1fd3MgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KZGFuZU1pZXN6a2FuaWEgJT4lIERUOjpkYXRhdGFibGUoKQ0KYGBgDQojIyBabWlhbmEgem1pZW5ueWNoICpkemllbG5pY2EqIGkgKnR5cCBidWR5bmt1KiBuYSB6bWllbm5lIGN6eW5uaWtvd2UNCmBgYHtyfQ0KZGFuZU1pZXN6a2FuaWEgPC0gZGFuZU1pZXN6a2FuaWEgJT4lDQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGxpc3QoZmFjdG9yKSkNCmBgYA0KDQojIyBQb2RzdW1vd2FuaWUgb2JzZXJ3YWNqaQ0KYGBge3J9DQpkYW5lTWllc3prYW5pYSAlPiUgc3VtbWFyeQ0KYGBgDQoNCiMjIEJ1ZG93YSBtb2RlbHUgbGluaW93ZWdvIChyZWdyZXNqYSkgDQpgYGB7cn0NCm1vZGVsIDwtIGxtKGNlbmF+ZHppZWxuaWNhLCBkYXRhID0gZGFuZU1pZXN6a2FuaWEpDQpgYGANCg0KYGBge3J9DQptb2RlbCAlPiUgc3VtbWFyeQ0KYGBgDQoNCiMjIFNwcmF3ZHphbmllIHLDs3dub8WbY2kgxZtyZWRuaWNoIHcgcG9kZ3J1cGFjaA0KYGBge3J9DQphbm92YShtb2RlbCkNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KIyMgV3lrcmVzDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0NCmRhbmVNaWVzemthbmlhICU+JQ0KICBnZ3Bsb3QoYWVzKHBvd2llcnpjaG5pYSwgY2VuYSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkemllbG5pY2EpKSArDQogIGdlb21fc21vb3RoKCkNCmBgYA0KIyMgU3ByYXdkemFuaWUgemHFgm/FvGXFhCByZWdyZXNqaQ0KDQojIyMgWmFsZcW8bm/Fm2NpIGxpbmlvd2ENCmBgYHtyfQ0KbG10ZXN0OjpoYXJ2dGVzdChtb2RlbCkNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KYGBge3J9DQpsbXRlc3Q6OnJhaW50ZXN0KG1vZGVsKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQojIyMgTm9ybWFsbm/Fm8SHIHJlc3p0DQpgYGB7cn0NCm5vcm10ZXN0OjpqYi5ub3JtLnRlc3QobW9kZWwkcmVzaWR1YWxzKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQojIyMgQnJhayBhdXRva29yZWxhY2ppDQpgYGB7cn0NCmxtdGVzdDo6ZHd0ZXN0KG1vZGVsKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQojIyMgSG9tb3NrZWRhdHljem5vxZvEhyAocsOzd25vxZvEhyB3YXJpYW5jamkpDQpgYGB7cn0NCmxtdGVzdDo6Z3F0ZXN0KG1vZGVsKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmxtdGVzdDo6YnB0ZXN0KG1vZGVsKQ0KYGBgDQo8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+SW50ZXJwcmV0YWNqYSB3eW5pa3UhPC9zcGFuPg0KDQpgYGB7cn0NCmxtdGVzdDo6aG1jdGVzdChtb2RlbCkNCmBgYA0KPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkludGVycHJldGFjamEgd3luaWt1ITwvc3Bhbj4NCg0KYGBge3J9DQpiYXJ0bGV0dC50ZXN0KGNlbmF+ZHppZWxuaWNhLCBkYXRhID0gZGFuZU1pZXN6a2FuaWEpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0KZmxpZ25lci50ZXN0KGNlbmF+ZHppZWxuaWNhLCBkYXRhID0gZGFuZU1pZXN6a2FuaWEpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCmBgYHtyfQ0KY2FyOjpsZXZlbmVUZXN0KHJlc2lkdWFscyhtb2RlbCkgfiBkemllbG5pY2EsIGRhdGEgPSBkYW5lTWllc3prYW5pYSkNCmBgYA0KDQpgYGB7cn0NCmxhd3N0YXQ6OmxldmVuZS50ZXN0KHJlc2lkdWFscyhtb2RlbCksIGRhbmVNaWVzemthbmlhJGR6aWVsbmljYSkNCmBgYA0KYGBge3J9DQpsYXdzdGF0OjpsZXZlbmUudGVzdChyZXNpZHVhbHMobW9kZWwpLCBkYW5lTWllc3prYW5pYSRkemllbG5pY2EsIGxvY2F0aW9uID0gIm1lYW4iKQ0KYGBgDQoNCmBgYHtyfQ0KbW9vZC50ZXN0KGNlbmF+ZHppZWxuaWNhLCBkYXRhID0gZGFuZU1pZXN6a2FuaWEpDQpgYGANCjxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnRlcnByZXRhY2phIHd5bmlrdSE8L3NwYW4+DQoNCiMjIFd5a3Jlc3kgZGlhZ25vc3R5Y3puZQ0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9N30NCnBsb3QobW9kZWwpDQpgYGANCg0KIyMgV3lrcmVzIHB1ZGXFgmtvLXfEhXN5IA0KYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQ0KZGFuZU1pZXN6a2FuaWEgJT4lDQogIGdncGxvdChhZXMoeCA9IGR6aWVsbmljYSwgeSA9IGNlbmEpKSArDQogIGdlb21fYm94cGxvdCgpDQpgYGANCg0KIyMgVGVzdCBwb3N0LWhvYw0KDQpgYGB7cn0NClR1a2V5SFNEKGFvdihtb2RlbCkpDQpgYGANCg0KYGBge3J9DQphZ3JpY29sYWU6OnNjaGVmZmUudGVzdChhb3YobW9kZWwpLCAiZHppZWxuaWNhIiwgZ3JvdXA9VFJVRSwgY29uc29sZT1UUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KYWdyaWNvbGFlOjpMU0QudGVzdChhb3YobW9kZWwpLCAiZHppZWxuaWNhIiwgcC5hZGo9Im5vbmUiLCBncm91cD1GQUxTRSwgY29uc29sZSA9IFRSVUUpDQpgYGANCg0KDQpgYGB7cn0NCmFncmljb2xhZTo6TFNELnRlc3QoYW92KG1vZGVsKSwgImR6aWVsbmljYSIsIHAuYWRqPSJib25mZXJyb25pIiwgZ3JvdXA9RkFMU0UsIGNvbnNvbGUgPSBUUlVFKSANCmBgYA0KDQpgYGB7cn0NCmFncmljb2xhZTo6ZHVuY2FuLnRlc3QoYW92KG1vZGVsKSwgImR6aWVsbmljYSIsIGdyb3VwPVRSVUUsIGNvbnNvbGU9VFJVRSkgDQoNCmBgYA0KDQpgYGB7ciBpbmNsdWRlID0gRkFMU0V9DQojIGRldGFjaChwYWNrYWdlOnRpZHl2ZXJzZSkNCiMgZGV0YWNoKHBhY2thZ2U6Z2dwbG90MikNCiMgZGV0YWNoKHBhY2thZ2U6dGliYmxlKQ0KIyBkZXRhY2gocGFja2FnZTp0aWR5cikNCiMgZGV0YWNoKHBhY2thZ2U6cmVhZHIpDQojIGRldGFjaChwYWNrYWdlOnB1cnJyKQ0KIyBkZXRhY2gocGFja2FnZTpkcGx5cikNCiMgZGV0YWNoKHBhY2thZ2U6c3RyaW5ncikNCiMgZGV0YWNoKHBhY2thZ2U6Zm9yY2F0cykNCmBgYA0KDQo=