knitr::opts_chunk$set(
    echo = TRUE,
    message = FALSE,
    warning = FALSE
)

Spracované a inšpirované Notebookom Jason Locklin: Introduction to R for Education Data Analysis and Visualization

Práca s údajmi

Tradičná práca s databázou

Pre prácu s údajmi (databázou) používame najčastejšie dátový typ .data.frame.. Je to tabuľka, ktorá pozostáva zo stĺpcov rozličných typov. Jeden riadok pritom predstavuje jeden záznam databázy.

Príklad

Majme údaje o žiakoch, ktoré predstavujú tri premenné - Meno, Vek a Body:

# Working with data frames

  Zviera = c("Medveď", "Tiger", "Aligátor")
  Výška = c(250, 270, 350)
  Váha = c(250, 200, 380)

Tieto tri premenné nie sú zatiaľ nijako prepojené, predstavujú izolované stĺpce tabuľky. Do tabuľky ich spojíme nasledovne

udaje <- data.frame(Zviera,Výška,Váha)
print(udaje)

Vysvetlenie: DataFrame má tri stĺpce: Zviera, Výšku a Váhu. Niektoré operácie s údajmi organizovanými v .data.frame. sú uvedené nasledovne

print(udaje$Výška)                 # takto adresujeme jednotlivé premenné v data.frame
[1] 250 270 350
print(mean(udaje$Výška))           # priemernu výšku
[1] 290
print(udaje[Zviera=="Tiger",])     # adresovanie celého riadku
print(udaje[3,])                 # ina moznost adresovania celeho riadku
print(udaje[,2:3])               # vypisanie druheho a tretieho stlpca tabulky
print(udaje[1,1])                # vypisanie jednej bunky tabulky
[1] "Medveď"
summary(udaje)                   # zakladna deskriptivna statistika celej tabulky
    Zviera              Výška          Váha      
 Length:3           Min.   :250   Min.   :200.0  
 Class :character   1st Qu.:260   1st Qu.:225.0  
 Mode  :character   Median :270   Median :250.0  
                    Mean   :290   Mean   :276.7  
                    3rd Qu.:310   3rd Qu.:315.0  
                    Max.   :350   Max.   :380.0  

Ak chceme pridať k tabuľke dodatočný stĺpec, potom to robíme nasledovne

Mäsožravec <- c(TRUE,TRUE,TRUE)
udaje <- cbind(udaje,Mäsožravec)
print(udaje)

Ak chceme pridať riadok, potom

# New record (must match column order/types)
novy.riadok <- data.frame(Zviera = "Delfín", Výška = 300, Váha = 220, Mäsožravec = TRUE)

novy.riadok <- data.frame(Zviera = "Žirafa", Výška = 500, Váha = 1200, Mäsožravec = FALSE)

novy.riadok <- data.frame(Zviera = "Krava", Výška = 150, Váha = 700, Mäsožravec = FALSE)

novy.riadok <- data.frame(Zviera = "Slon", Výška = 300, Váha = 6000, Mäsožravec = FALSE)


# Append
udaje <- rbind(udaje, novy.riadok)
print(udaje)

Tabuľky v prostredí kableextra

library(knitr)
library(kableExtra)
kable(
  udaje,
#  format,
digits = 2,
#  row.names = NA,
#  col.names = NA,
  align=c("l","c","l","r"),
  caption = "ZOO"
#  label = NULL,
#  format.args = list(),
#  escape = TRUE,
 # ...
) %>%
      kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center")
ZOO
Zviera Výška Váha Mäsožravec
Medveď 250 250 TRUE
Tiger 270 200 TRUE
Aligátor 350 380 TRUE
Delfín 300 220 TRUE
Žirafa 500 1200 FALSE
Krava 150 700 FALSE
Slon 300 6000 FALSE
NA
NA
NA
NA
NA

Tidyverse - moderná práca s údajmi

Tidyverse je súbor knižníc, ktoré majú zjednodušiť prácu s údajmi. Majú jednotný komunikačný štandard, vzájomne sa doplňujú.

# Load tidyverse
library(tidyverse)

dplyr - pre manipuláciu s údajmi

.dplyr. poskytuje základné možnosti manipulácie s údajmi, ako napr.:

  1. filter(): vyberá riadky

  2. select(): vyberá stĺpce

  3. mutate(): vytvára nové stĺpce tabuľky

  4. arrange(): triedi riadky

  5. summarise(): sumarizuje

V nasledovnej ukážke využijeme tzv. .pipes. %>% alebo %<% umožňuje posielať výsledky z jednej funkcie priamo do volanie nasledovnej funkcie. To umožňuje ľahšiu čitateľnosť kódov, konvencia sa ujala a má široké použitie.

Výber a triedenie

# výber a následné triedenie
udaje %>%
  filter(Váha > 180) %>%     # vybera zaznamy s váhou väčšou ako 180 kg
  arrange(desc(Váha)) %>%     # vysledny subor triedi zostupne podla premennej Váha
kable %>%
    kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Zviera Výška Váha Mäsožravec
Slon 300 6000 FALSE
Žirafa 500 1200 FALSE
Krava 150 700 FALSE
Aligátor 350 380 TRUE
Medveď 250 250 TRUE
Delfín 300 220 TRUE
Tiger 270 200 TRUE

Zoskupenie a sumarizácia

# Zoskupí and sumarizuje
udaje %>%
  group_by(Mäsožravec) %>%      # zoskupi zaznamy podla toho či je mäsožravec a vypocita za kazdu skupinu jej priemer Váhy
  summarise(                # a taktiez spocita pocetnosti oboch skupin
    Priem.Váha = mean(Váha),
    count = n()
  ) %>%
 kable(
    caption = "Počet Zvierat v ZOO, ktoré sú Mäsožravé a ich priemerná váha ",
    col.names = c("Mäsožravec", "Váha", "Počet"),
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Počet Zvierat v ZOO, ktoré sú Mäsožravé a ich priemerná váha
Mäsožravec Váha Počet
FALSE 2633.333 3
TRUE 262.500 4

Vytváranie novej premennej

# Vytváranie novej premennej
udaje %>%
  mutate(
    Inteligencia = case_when(     # vytvara novu premennu Inteligencia podla nasledovnej relacnej schemy, aby sme zapísali, ktoré zviera je inteligentnejšie od toho druhého 
     Zviera == "Delfín" ~ "A",       # veľmi inteligentné
      Zviera == "Slon" ~ "A",         # veľmi inteligentné
      Zviera == "Medveď" ~ "B",       # nadpriemerná inteligencia
      Zviera == "Tiger" ~ "B",        # nadpriemerná inteligencia
      Zviera == "Žirafa" ~ "C",       # priemerná inteligencia
      Zviera == "Krava" ~ "C",        # priemerná inteligencia
      Zviera == "Aligátor" ~ "D",     # nízka inteligencia
      TRUE ~ "Neznáme"
    ),
  
  ) %>% 
  kable %>%
   kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  ) 
Zviera Výška Váha Mäsožravec Inteligencia
Medveď 250 250 TRUE B
Tiger 270 200 TRUE B
Aligátor 350 380 TRUE D
Delfín 300 220 TRUE A
Žirafa 500 1200 FALSE C
Krava 150 700 FALSE C
Slon 300 6000 FALSE A

Import údajov z otv. databáz

  1. Mendeley Data Tuto sa dostaneme z Mendeley Data, kde si údaje viete voľne stiahnúť. Údaje sa vzťahujú k už publikovaným článkom vo vydavateľstve Elsevier. Výber sa dá urobiť jednoducho zadaním kľúčových slov.
  2. Kaggle Data Tuto sa dostaneme z Kaggle Datasets, kde si údaje viete voľne stiahnúť. Údaje sa vzťahujú k projektom podporovaným Kaggle. Výber sa dá urobiť jednoducho zadaním kľúčových slov.
  3. Databázy knižníc R - .library(datasets). alebo .library(wooldridge). ale aj iné - stačí si dať príkaz data()

Import údajov z .csv alebo .xls

Ja som si zvolil údaje z [Abosede Tiamiyu: Environmental, Social, and Governance Reporting Evidencing Firm Performance in Emerging Economy]{https://data.mendeley.com/datasets/7k8pjhsrwb/1}. Na stránke sa nachádza súbor .Dataset ESG and Firm Performance.xlsx., ktorý som si stiahol a exportoval do formátu csv. Ako oddeľovač položiek som si zvolil bodkočiarku (semicolon ;), vyžívam desatinnú bodku a nie čiarku a tiež textové premenné uvádzam apostrofmi “. V prvom riadku sa nachádzajú názvy stĺpcov, ktoré neskôr budú vystupovať ako premenné. Tie obsahujú medzery, čo je v zázve premennej neprípustné a nahradil som ich podtrhovátkom”.”.

Náhľad na xls databázu otvorenú v tabuľkovom procesore
Náhľad na xls databázu otvorenú v tabuľkovom procesore
Náhľad na csv databázu otvorenú v textovom procesore
Náhľad na csv databázu otvorenú v textovom procesore

Potom už stačí importovať údaje do .data.frame., a to nasledovne

library(readr)
udaje <- read_delim("test.csv", delim = NULL)
head(udaje)
                                    # nazvy premennych

Grafy

library(dplyr)

udaje.road_type <- udaje %>%
  filter(road_type == "Highway") %>%
  select(traffic_density, avg_speed, weather_condition, num_lanes, road_surface, lighting)

ggplot2 - knižnica pre grafy

Výber a následné triedenie Knižnica .ggplot2. je v súčasnosti najčastejšie používaná grafická knižnica, pričom predpripravené kódy k jednotlivým obrázkom si viete nájsť v R Graph Gallery. Tu si uvedieme jednoduchšie z nich.

Scatter plot

# Basic scatter plot
library(ggplot2)

ggplot(udaje.road_type, aes(x = traffic_density, y = avg_speed)) +
  geom_point(alpha = 0.4, color = "steelblue") +   # priehľadné modré body
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # pridá červenú trendovú čiaru
  theme_minimal() +
  labs(
    title = "Vzťah medzi hustotou premávky a priemernou rýchlosťou",
    x = "Hustota premávky (vozidlá/km)",
    y = "Priemerná rýchlosť (km/h)"
  )

Boxplot

# Bar plot with grouping
library(ggplot2)

library(ggplot2)

ggplot(udaje.road_type, aes(x = weather_condition, y = avg_speed)) +
  geom_boxplot(fill = "lightblue", color = "red") +
  labs(
    title = "Rýchlosť podľa počasia",
    x = "Počasie",
    y = "Priemerná rýchlosť (km/h)"
  ) +
  theme_minimal()

Základné štatistiky.

knitr - tabuľka

library(dplyr)
library(knitr)
density.stats <- udaje.road_type %>%
  group_by(weather_condition) %>%               # segment = počasie
  summarise(
     pozorovania = n(),                            # počet pozorovaní
    priemer  = mean(traffic_density, na.rm = TRUE),  # priemer
    minimum   = min(traffic_density, na.rm = TRUE),   # minimum
    maximum   = max(traffic_density, na.rm = TRUE),   # maximum
    .groups = "drop"
  )

kable(density.stats, digits = 2, caption = "Štatistiky hustoty premávky podľa počasia")
Štatistiky hustoty premávky podľa počasia
weather_condition pozorovania priemer minimum maximum
Clear 327 275.32 52.19 496.30
Foggy 331 280.55 50.32 497.28
Rainy 345 263.70 53.13 499.51

alebo krajšie tabuľky s pomocou .kableExtra.:

library(dplyr)
library(knitr)
library(kableExtra)

# Summarise basic statistics for traffic data
traffic.stats <- udaje.road_type %>%
  group_by(weather_condition) %>%   # ← môžeš zmeniť napr. na road_type alebo lighting
  summarise(
    n     = n(),                                  # počet pozorovaní
    mean  = mean(traffic_density, na.rm = TRUE),  # priemer
    min   = min(traffic_density, na.rm = TRUE),   # minimum
    max   = max(traffic_density, na.rm = TRUE),   # maximum
    .groups = "drop"
  )

# Create styled table
traffic.stats %>%
  kable(
    digits = 2,
    caption = "Základné štatistiky hustoty premávky podľa počasia"
  ) %>%
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover", "condensed")
  ) %>%
  column_spec(1, bold = TRUE) %>%                             # zvýrazni názvy kategórií
  row_spec(0, bold = TRUE, background = "#f2f2f2") %>%        # štýl hlavičky
  add_header_above(c(" " = 1, "Štatistiky hustoty premávky" = 4))  # nadpis nad tabuľkou
Základné štatistiky hustoty premávky podľa počasia
Štatistiky hustoty premávky
weather_condition n mean min max
Clear 327 275.32 52.19 496.30
Foggy 331 280.55 50.32 497.28
Rainy 345 263.70 53.13 499.51

t-test: Porovnanie hustoty premávky medzi dňom a nocou

t.test.result <- t.test(
  udaje.road_type$traffic_density[udaje.road_type$lighting == "Daylight"],
  udaje.road_type$traffic_density[udaje.road_type$lighting == "Night"]
)

print(t.test.result)

    Welch Two Sample t-test

data:  udaje.road_type$traffic_density[udaje.road_type$lighting == "Daylight"] and udaje.road_type$traffic_density[udaje.road_type$lighting == "Night"]
t = -0.72539, df = 1000.1, p-value = 0.4684
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -21.73942  10.00490
sample estimates:
mean of x mean of y 
 270.0460  275.9133 

ANOVA: Kontrola či sa líši hustotu premávky podľa počasia

anova.result <- aov(traffic_density ~ weather_condition, data = udaje.road_type)
summary(anova.result)
                    Df   Sum Sq Mean Sq F value Pr(>F)
weather_condition    2    50483   25241   1.541  0.215
Residuals         1000 16382534   16383               

Linear Regression: Predikcia rýchlosti áut

model <- lm(avg_speed ~ traffic_density + num_lanes + weather_condition, data = udaje.road_type)
summary(model)

Call:
lm(formula = avg_speed ~ traffic_density + num_lanes + weather_condition, 
    data = udaje.road_type)

Residuals:
    Min      1Q  Median      3Q     Max 
-53.564 -27.164  -0.229  26.633  53.085 

Coefficients:
                        Estimate Std. Error t value Pr(>|t|)    
(Intercept)            75.698975   3.289408  23.013   <2e-16 ***
traffic_density        -0.001863   0.007351  -0.254   0.7999    
num_lanes              -1.397612   0.659876  -2.118   0.0344 *  
weather_conditionFoggy -1.297266   2.320887  -0.559   0.5763    
weather_conditionRainy -1.834966   2.298663  -0.798   0.4249    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 29.75 on 998 degrees of freedom
Multiple R-squared:  0.005145,  Adjusted R-squared:  0.001157 
F-statistic:  1.29 on 4 and 998 DF,  p-value: 0.272
# install.packages(c("broom", "kableExtra", "dplyr", "stringr"))
library(broom)
library(dplyr)
library(kableExtra)
library(stringr)

# Your model (already fitted)
# model <- lm(ESG.INDEX ~ RETURN.ON.ASSETS + FIRM.SIZE + DEBT.TO.ASSET, data = udaje.2013)

coef.tbl <- tidy(model, conf.int = TRUE) %>%
  mutate(
    term = recode(term,
      "(Intercept)" = "Intercept",
      "traffic_density" = "Traffic Density",
      "num_lanes" = "Number of Lanes",
      "weather_conditionRain" = "Weather: Rain",
      "weather_conditionFoggy" = "Weather: Foggy"
    ),
    stars = case_when(
      p.value < 0.001 ~ "***",
      p.value < 0.01  ~ "**",
      p.value < 0.05  ~ "*",
      p.value < 0.1   ~ "·",
      TRUE            ~ ""
    )
  ) %>%
  transmute(
    Term = term,
    Estimate = estimate,
    `Std. Error` = std.error,
    `t value` = statistic,
    `p value` = p.value,
    `95% CI` = str_c("[", round(conf.low, 3), ", ", round(conf.high, 3), "]"),
    Sig = stars
  )

coef.tbl %>%
  kable(
    digits = 3,
    caption = "OLS Regression Coefficients (avg_speed ~ traffic_density + num_lanes + weather_condition)"
  ) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed")) %>%
  column_spec(1, bold = TRUE) %>%
  row_spec(0, bold = TRUE, background = "#f2f2f2") %>%
  footnote(
    general = "Signif. codes: *** p<0.001, ** p<0.01, * p<0.05, · p<0.1.",
    threeparttable = TRUE
  )
OLS Regression Coefficients (avg_speed ~ traffic_density + num_lanes + weather_condition)
Term Estimate Std. Error t value p value 95% CI Sig
Intercept 75.699 3.289 23.013 0.000 [69.244, 82.154] ***
Traffic Density -0.002 0.007 -0.254 0.800 [-0.016, 0.013]
Number of Lanes -1.398 0.660 -2.118 0.034 [-2.693, -0.103] *
Weather: Foggy -1.297 2.321 -0.559 0.576 [-5.852, 3.257]
weather_conditionRainy -1.835 2.299 -0.798 0.425 [-6.346, 2.676]
Note:
Signif. codes: *** p<0.001, ** p<0.01, * p<0.05, · p<0.1.
fit.tbl <- glance(model) %>%
  transmute(
    `R-squared`      = r.squared,
    `Adj. R-squared` = adj.r.squared,
    `F-statistic`    = statistic,
    `F p-value`      = p.value,
    `AIC`            = AIC,
    `BIC`            = BIC,
    `Num. obs.`      = nobs
  )

# 4️⃣ Vykresli tabuľku s popisom
fit.tbl %>%
  kable(
    digits = 3,
    caption = "Model Fit Statistics (avg_speed ~ traffic_density + num_lanes + weather_condition)"
  ) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("condensed"))
Model Fit Statistics (avg_speed ~ traffic_density + num_lanes + weather_condition)
R-squared Adj. R-squared F-statistic F p-value AIC BIC Num. obs.
0.005 0.001 1.29 0.272 9659.519 9688.983 1003
NA

Info zdroje pre ďalšie štúdium

R Project

Posit

Community Resources

LS0tCnRpdGxlOiAiUHLDoWNhIHMgZGF0YWLDoXpvdSIKYXV0aG9yOiAiQmFyYm9yYSBLb3ByZG92YSAgPGJyPgoocyB2eXXFvml0w61tIHZlcmVqbmUgZG9zdHVwbsO9Y2gga8OzZG92KSIKZGF0ZTogIk5vdmVtYmVyIDIwMjUiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCmBgYHtyfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgICBlY2hvID0gVFJVRSwKICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgIHdhcm5pbmcgPSBGQUxTRQopCmBgYAoKU3ByYWNvdmFuw6kgYSBpbsWhcGlyb3ZhbsOpIE5vdGVib29rb20KW0phc29uIExvY2tsaW46IEludHJvZHVjdGlvbiB0byBSIGZvciBFZHVjYXRpb24gRGF0YSBBbmFseXNpcyBhbmQgVmlzdWFsaXphdGlvbl0oaHR0cDovL3JwdWJzLmNvbS9qYXNvbkwvTGFuZEwpe3RhcmdldD0iX2JsYW5rIiByZWw9Im5vb3BlbmVyIn0KCgojIFByw6FjYSBzIMO6ZGFqbWkKCiMjIFRyYWRpxI1uw6EgcHLDoWNhIHMgZGF0YWLDoXpvdQoKUHJlIHByw6FjdSBzIMO6ZGFqbWkgKGRhdGFiw6F6b3UpIHBvdcW+w612YW1lIG5hasSNYXN0ZWrFoWllIGTDoXRvdsO9IHR5cCAuZGF0YS5mcmFtZS4uIEplIHRvIHRhYnXEvmthLCBrdG9yw6EgcG96b3N0w6F2YSB6byBzdMS6cGNvdiByb3psacSNbsO9Y2ggdHlwb3YuIEplZGVuIHJpYWRvayBwcml0b20gcHJlZHN0YXZ1amUgamVkZW4gesOhem5hbSBkYXRhYsOhenkuCgojIyMgUHLDrWtsYWQKCk1ham1lIMO6ZGFqZSBvIMW+aWFrb2NoLCBrdG9yw6kgcHJlZHN0YXZ1asO6IHRyaSBwcmVtZW5uw6kgLSBNZW5vLCBWZWsgYSBCb2R5OgoKYGBge3J9CiMgV29ya2luZyB3aXRoIGRhdGEgZnJhbWVzCgogIFp2aWVyYSA9IGMoIk1lZHZlxI8iLCAiVGlnZXIiLCAiQWxpZ8OhdG9yIikKICBWw73FoWthID0gYygyNTAsIDI3MCwgMzUwKQogIFbDoWhhID0gYygyNTAsIDIwMCwgMzgwKQpgYGAKClRpZXRvIHRyaSBwcmVtZW5uw6kgbmllIHPDuiB6YXRpYcS+IG5pamFrbyBwcmVwb2plbsOpLCBwcmVkc3RhdnVqw7ogaXpvbG92YW7DqSBzdMS6cGNlIHRhYnXEvmt5LiBEbyB0YWJ1xL5reSBpY2ggc3BvasOtbWUgbmFzbGVkb3ZuZQoKYGBge3J9CnVkYWplIDwtIGRhdGEuZnJhbWUoWnZpZXJhLFbDvcWha2EsVsOhaGEpCnByaW50KHVkYWplKQpgYGAKCgpWeXN2ZXRsZW5pZTogRGF0YUZyYW1lIG3DoSB0cmkgc3TEunBjZTogWnZpZXJhLCBWw73FoWt1IGEgVsOhaHUuIE5pZWt0b3LDqSBvcGVyw6FjaWUgcyDDumRham1pIG9yZ2FuaXpvdmFuw71taSB2IC5kYXRhLmZyYW1lLiBzw7ogdXZlZGVuw6kgbmFzbGVkb3ZuZQoKYGBge3J9CnByaW50KHVkYWplJFbDvcWha2EpICAgICAgICAgICAgICAgICAjIHRha3RvIGFkcmVzdWplbWUgamVkbm90bGl2w6kgcHJlbWVubsOpIHYgZGF0YS5mcmFtZQpwcmludChtZWFuKHVkYWplJFbDvcWha2EpKSAgICAgICAgICAgIyBwcmllbWVybnUgdsO9xaFrdQpwcmludCh1ZGFqZVtadmllcmE9PSJUaWdlciIsXSkgICAgICMgYWRyZXNvdmFuaWUgY2Vsw6lobyByaWFka3UKcHJpbnQodWRhamVbMyxdKSAgICAgICAgICAgICAgICAgIyBpbmEgbW96bm9zdCBhZHJlc292YW5pYSBjZWxlaG8gcmlhZGt1CnByaW50KHVkYWplWywyOjNdKSAgICAgICAgICAgICAgICMgdnlwaXNhbmllIGRydWhlaG8gYSB0cmV0aWVobyBzdGxwY2EgdGFidWxreQpwcmludCh1ZGFqZVsxLDFdKSAgICAgICAgICAgICAgICAjIHZ5cGlzYW5pZSBqZWRuZWogYnVua3kgdGFidWxreQpzdW1tYXJ5KHVkYWplKSAgICAgICAgICAgICAgICAgICAjIHpha2xhZG5hIGRlc2tyaXB0aXZuYSBzdGF0aXN0aWthIGNlbGVqIHRhYnVsa3kKYGBgCgpBayBjaGNlbWUgcHJpZGHFpSBrIHRhYnXEvmtlIGRvZGF0b8SNbsO9IHN0xLpwZWMsIHBvdG9tIHRvIHJvYsOtbWUgbmFzbGVkb3ZuZQoKYGBge3J9Ck3DpHNvxb5yYXZlYyA8LSBjKFRSVUUsVFJVRSxUUlVFKQp1ZGFqZSA8LSBjYmluZCh1ZGFqZSxNw6Rzb8W+cmF2ZWMpCnByaW50KHVkYWplKQpgYGAKCkFrIGNoY2VtZSBwcmlkYcWlIHJpYWRvaywgcG90b20KCmBgYHtyfQojIE5ldyByZWNvcmQgKG11c3QgbWF0Y2ggY29sdW1uIG9yZGVyL3R5cGVzKQpub3Z5LnJpYWRvayA8LSBkYXRhLmZyYW1lKFp2aWVyYSA9ICJEZWxmw61uIiwgVsO9xaFrYSA9IDMwMCwgVsOhaGEgPSAyMjAsIE3DpHNvxb5yYXZlYyA9IFRSVUUpCgpub3Z5LnJpYWRvayA8LSBkYXRhLmZyYW1lKFp2aWVyYSA9ICLFvWlyYWZhIiwgVsO9xaFrYSA9IDUwMCwgVsOhaGEgPSAxMjAwLCBNw6Rzb8W+cmF2ZWMgPSBGQUxTRSkKCm5vdnkucmlhZG9rIDwtIGRhdGEuZnJhbWUoWnZpZXJhID0gIktyYXZhIiwgVsO9xaFrYSA9IDE1MCwgVsOhaGEgPSA3MDAsIE3DpHNvxb5yYXZlYyA9IEZBTFNFKQoKbm92eS5yaWFkb2sgPC0gZGF0YS5mcmFtZShadmllcmEgPSAiU2xvbiIsIFbDvcWha2EgPSAzMDAsIFbDoWhhID0gNjAwMCwgTcOkc2/FvnJhdmVjID0gRkFMU0UpCgoKIyBBcHBlbmQKdWRhamUgPC0gcmJpbmQodWRhamUsIG5vdnkucmlhZG9rKQpwcmludCh1ZGFqZSkKYGBgCgojIyMgVGFidcS+a3kgdiBwcm9zdHJlZMOtIGthYmxlZXh0cmEKCgpgYGB7cn0KbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQprYWJsZSgKICB1ZGFqZSwKIyAgZm9ybWF0LApkaWdpdHMgPSAyLAojICByb3cubmFtZXMgPSBOQSwKIyAgY29sLm5hbWVzID0gTkEsCiAgYWxpZ249YygibCIsImMiLCJsIiwiciIpLAogIGNhcHRpb24gPSAiWk9PIgojICBsYWJlbCA9IE5VTEwsCiMgIGZvcm1hdC5hcmdzID0gbGlzdCgpLAojICBlc2NhcGUgPSBUUlVFLAogIyAuLi4KKSAlPiUKICAgICAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIpCgoKCgoKYGBgCgoKCiMjIFRpZHl2ZXJzZSAtIG1vZGVybsOhIHByw6FjYSBzIMO6ZGFqbWkKClRpZHl2ZXJzZSBqZSBzw7pib3Iga25pxb5uw61jLCBrdG9yw6kgbWFqw7ogemplZG5vZHXFoWnFpSBwcsOhY3UgcyDDumRham1pLiBNYWrDuiBqZWRub3Ruw70ga29tdW5pa2HEjW7DvSDFoXRhbmRhcmQsIHZ6w6Fqb21uZSBzYSBkb3BsxYh1asO6LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBMb2FkIHRpZHl2ZXJzZQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyMgIGRwbHlyIC0gcHJlIG1hbmlwdWzDoWNpdSBzIMO6ZGFqbWkKCi5kcGx5ci4gcG9za3l0dWplIHrDoWtsYWRuw6kgbW/Fvm5vc3RpIG1hbmlwdWzDoWNpZSBzIMO6ZGFqbWksIGFrbyBuYXByLjogCgoxLiBmaWx0ZXIoKTogdnliZXLDoSByaWFka3kgCgoxLiBzZWxlY3QoKTogdnliZXLDoSBzdMS6cGNlIAoKMS4gbXV0YXRlKCk6IHZ5dHbDoXJhIG5vdsOpIHN0xLpwY2UgdGFidcS+a3kgCgoxLiBhcnJhbmdlKCk6IHRyaWVkaSByaWFka3kgCgoxLiBzdW1tYXJpc2UoKTogc3VtYXJpenVqZQoKViBuYXNsZWRvdm5laiB1a8Ohxb5rZSB2eXXFvmlqZW1lIHR6di4gLnBpcGVzLiAlPiUgYWxlYm8gJTwlIHVtb8W+xYh1amUgcG9zaWVsYcWlIHbDvXNsZWRreSB6IGplZG5laiBmdW5rY2llIHByaWFtbyBkbyB2b2xhbmllIG5hc2xlZG92bmVqIGZ1bmtjaWUuIFRvIHVtb8W+xYh1amUgxL5haMWhaXUgxI1pdGF0ZcS+bm9zxaUga8OzZG92LCBrb252ZW5jaWEgc2EgdWphbGEgYSBtw6EgxaFpcm9rw6kgcG91xb5pdGllLgoKIyMjIyBWw71iZXIgYSB0cmllZGVuaWUKCmBgYHtyfQojIHbDvWJlciBhIG7DoXNsZWRuw6kgdHJpZWRlbmllCnVkYWplICU+JQogIGZpbHRlcihWw6FoYSA+IDE4MCkgJT4lICAgICAjIHZ5YmVyYSB6YXpuYW15IHMgdsOhaG91IHbDpMSNxaFvdSBha28gMTgwIGtnCiAgYXJyYW5nZShkZXNjKFbDoWhhKSkgJT4lICAgICAjIHZ5c2xlZG55IHN1Ym9yIHRyaWVkaSB6b3N0dXBuZSBwb2RsYSBwcmVtZW5uZWogVsOhaGEKa2FibGUgJT4lCiAgICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKQpgYGAKCiMjIyMgWm9za3VwZW5pZSBhIHN1bWFyaXrDoWNpYQoKYGBge3J9CiMgWm9za3Vww60gYW5kIHN1bWFyaXp1amUKdWRhamUgJT4lCiAgZ3JvdXBfYnkoTcOkc2/FvnJhdmVjKSAlPiUgICAgICAjIHpvc2t1cGkgemF6bmFteSBwb2RsYSB0b2hvIMSNaSBqZSBtw6Rzb8W+cmF2ZWMgYSB2eXBvY2l0YSB6YSBrYXpkdSBza3VwaW51IGplaiBwcmllbWVyIFbDoWh5CiAgc3VtbWFyaXNlKCAgICAgICAgICAgICAgICAjIGEgdGFrdGlleiBzcG9jaXRhIHBvY2V0bm9zdGkgb2JvY2ggc2t1cGluCiAgICBQcmllbS5Ww6FoYSA9IG1lYW4oVsOhaGEpLAogICAgY291bnQgPSBuKCkKICApICU+JQoga2FibGUoCiAgICBjYXB0aW9uID0gIlBvxI1ldCBadmllcmF0IHYgWk9PLCBrdG9yw6kgc8O6IE3DpHNvxb5yYXbDqSBhIGljaCBwcmllbWVybsOhIHbDoWhhICIsCiAgICBjb2wubmFtZXMgPSBjKCJNw6Rzb8W+cmF2ZWMiLCAiVsOhaGEiLCAiUG/EjWV0IiksCiAgICBhbGlnbiA9ICJjIgogICkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKYGBgCgojIyMjIFZ5dHbDoXJhbmllIG5vdmVqIHByZW1lbm5lagoKYGBge3J9CiMgVnl0dsOhcmFuaWUgbm92ZWogcHJlbWVubmVqCnVkYWplICU+JQogIG11dGF0ZSgKICAgIEludGVsaWdlbmNpYSA9IGNhc2Vfd2hlbiggICAgICMgdnl0dmFyYSBub3Z1IHByZW1lbm51IEludGVsaWdlbmNpYSBwb2RsYSBuYXNsZWRvdm5laiByZWxhY25laiBzY2hlbXksIGFieSBzbWUgemFww61zYWxpLCBrdG9yw6kgenZpZXJhIGplIGludGVsaWdlbnRuZWrFoWllIG9kIHRvaG8gZHJ1aMOpaG8gCiAgICAgWnZpZXJhID09ICJEZWxmw61uIiB+ICJBIiwgICAgICAgIyB2ZcS+bWkgaW50ZWxpZ2VudG7DqQogICAgICBadmllcmEgPT0gIlNsb24iIH4gIkEiLCAgICAgICAgICMgdmXEvm1pIGludGVsaWdlbnRuw6kKICAgICAgWnZpZXJhID09ICJNZWR2ZcSPIiB+ICJCIiwgICAgICAgIyBuYWRwcmllbWVybsOhIGludGVsaWdlbmNpYQogICAgICBadmllcmEgPT0gIlRpZ2VyIiB+ICJCIiwgICAgICAgICMgbmFkcHJpZW1lcm7DoSBpbnRlbGlnZW5jaWEKICAgICAgWnZpZXJhID09ICLFvWlyYWZhIiB+ICJDIiwgICAgICAgIyBwcmllbWVybsOhIGludGVsaWdlbmNpYQogICAgICBadmllcmEgPT0gIktyYXZhIiB+ICJDIiwgICAgICAgICMgcHJpZW1lcm7DoSBpbnRlbGlnZW5jaWEKICAgICAgWnZpZXJhID09ICJBbGlnw6F0b3IiIH4gIkQiLCAgICAgIyBuw616a2EgaW50ZWxpZ2VuY2lhCiAgICAgIFRSVUUgfiAiTmV6bsOhbWUiCiAgICApLAogIAogICkgJT4lIAogIGthYmxlICU+JQogICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKSAKYGBgCgoKIyMgSW1wb3J0IMO6ZGFqb3YgeiBvdHYuIGRhdGFiw6F6CgoKMS4gKk1lbmRlbGV5IERhdGEqIFR1dG8gc2EgZG9zdGFuZW1lIHogW01lbmRlbGV5IERhdGFdKGh0dHBzOi8vZGF0YS5tZW5kZWxleS5jb20vKXt0YXJnZXQ9Ii5ibGFuayIgcmVsPSJub29wZW5lciJ9LCBrZGUgc2kgw7pkYWplIHZpZXRlIHZvxL5uZSBzdGlhaG7DusWlLiDDmmRhamUgc2EgdnrFpWFodWrDuiBrIHXFviBwdWJsaWtvdmFuw71tIMSNbMOhbmtvbSB2byB2eWRhdmF0ZcS+c3R2ZSBFbHNldmllci4gVsO9YmVyIHNhIGTDoSB1cm9iacWlIGplZG5vZHVjaG8gemFkYW7DrW0ga8S+w7rEjW92w71jaCBzbG92LgoyLiAqS2FnZ2xlIERhdGEqICAgVHV0byBzYSBkb3N0YW5lbWUgeiBbS2FnZ2xlIERhdGFzZXRzXShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzKXt0YXJnZXQ9Ii5ibGFuayIgcmVsPSJub29wZW5lciJ9LCBrZGUgc2kgw7pkYWplIHZpZXRlIHZvxL5uZSBzdGlhaG7DusWlLiDDmmRhamUgc2EgdnrFpWFodWrDuiBrIHByb2pla3RvbSBwb2Rwb3JvdmFuw71tIEthZ2dsZS4gVsO9YmVyIHNhIGTDoSB1cm9iacWlIGplZG5vZHVjaG8gemFkYW7DrW0ga8S+w7rEjW92w71jaCBzbG92LgozLiBEYXRhYsOhenkga25pxb5uw61jIFIgLSAubGlicmFyeShkYXRhc2V0cykuIGFsZWJvIC5saWJyYXJ5KHdvb2xkcmlkZ2UpLiBhbGUgYWogaW7DqSAtIHN0YcSNw60gc2kgZGHFpSBwcsOta2F6IGRhdGEoKQoKCiMjIyBJbXBvcnQgw7pkYWpvdiB6IC5jc3YgYWxlYm8gLnhscwoKSmEgc29tIHNpIHp2b2xpbCDDumRhamUgeiBbQWJvc2VkZSBUaWFtaXl1OiBFbnZpcm9ubWVudGFsLCBTb2NpYWwsIGFuZCBHb3Zlcm5hbmNlIFJlcG9ydGluZyBFdmlkZW5jaW5nIEZpcm0gUGVyZm9ybWFuY2UgaW4gRW1lcmdpbmcgRWNvbm9teV17aHR0cHM6Ly9kYXRhLm1lbmRlbGV5LmNvbS9kYXRhc2V0cy83azhwamhzcndiLzF9LiBOYSBzdHLDoW5rZSBzYSBuYWNow6FkemEgc8O6Ym9yIC5EYXRhc2V0IEVTRyBhbmQgRmlybSBQZXJmb3JtYW5jZS54bHN4Liwga3RvcsO9IHNvbSBzaSBzdGlhaG9sIGEgZXhwb3J0b3ZhbCBkbyBmb3Jtw6F0dSBjc3YuIEFrbyBvZGRlxL5vdmHEjSBwb2xvxb5pZWsgc29tIHNpIHp2b2xpbCBib2Rrb8SNaWFya3UgKHNlbWljb2xvbiA7KSwgdnnFvsOtdmFtIGRlc2F0aW5uw7ogYm9ka3UgYSBuaWUgxI1pYXJrdSBhIHRpZcW+IHRleHRvdsOpIHByZW1lbm7DqSB1dsOhZHphbSBhcG9zdHJvZm1pICIuIFYgcHJ2b20gcmlhZGt1IHNhIG5hY2jDoWR6YWrDuiBuw6F6dnkgc3TEunBjb3YsIGt0b3LDqSBuZXNrw7RyIGJ1ZMO6IHZ5c3R1cG92YcWlIGFrbyBwcmVtZW5uw6kuIFRpZSBvYnNhaHVqw7ogbWVkemVyeSwgxI1vIGplIHYgesOhenZlIHByZW1lbm5laiBuZXByw61wdXN0bsOpIGEgbmFocmFkaWwgc29tIGljaCBwb2R0cmhvdsOhdGtvbSAiLiIuICAKCiFbTsOhaMS+YWQgbmEgeGxzIGRhdGFiw6F6dSBvdHZvcmVuw7ogdiB0YWJ1xL5rb3ZvbSBwcm9jZXNvcmVdKG9icmF6a3kvdWRhamV4bHMuanBnKXt3aWR0aD0xMDAlfQoKIVtOw6FoxL5hZCBuYSBjc3YgZGF0YWLDoXp1IG90dm9yZW7DuiB2IHRleHRvdm9tIHByb2Nlc29yZV0ob2JyYXpreS91ZGFqZWNzdi5qcGcpe3dpZHRoPTEwMCV9CgpQb3RvbSB1xb4gc3RhxI3DrSBpbXBvcnRvdmHFpSDDumRhamUgZG8gLmRhdGEuZnJhbWUuLCBhIHRvIG5hc2xlZG92bmUKCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQp1ZGFqZSA8LSByZWFkX2RlbGltKCJ0ZXN0LmNzdiIsIGRlbGltID0gTlVMTCkKaGVhZCh1ZGFqZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBuYXp2eSBwcmVtZW5ueWNoCmBgYAoKIyMgR3JhZnkKYGBge3J9CmxpYnJhcnkoZHBseXIpCgp1ZGFqZS5yb2FkX3R5cGUgPC0gdWRhamUgJT4lCiAgZmlsdGVyKHJvYWRfdHlwZSA9PSAiSGlnaHdheSIpICU+JQogIHNlbGVjdCh0cmFmZmljX2RlbnNpdHksIGF2Z19zcGVlZCwgd2VhdGhlcl9jb25kaXRpb24sIG51bV9sYW5lcywgcm9hZF9zdXJmYWNlLCBsaWdodGluZykKYGBgCgojIyMgZ2dwbG90MiAtIGtuacW+bmljYSBwcmUgZ3JhZnkKClbDvWJlciBhIG7DoXNsZWRuw6kgdHJpZWRlbmllCktuacW+bmljYSAuZ2dwbG90Mi4gamUgdiBzw7rEjWFzbm9zdGkgbmFqxI1hc3RlasWhaWUgcG91xb7DrXZhbsOhIGdyYWZpY2vDoSBrbmnFvm5pY2EsIHByacSNb20gcHJlZHByaXByYXZlbsOpIGvDs2R5IGsgamVkbm90bGl2w71tIG9icsOhemtvbSBzaSB2aWV0ZSBuw6Fqc8WlIHYgW1IgR3JhcGggR2FsbGVyeV0oaHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tLykuIFR1IHNpIHV2ZWRpZW1lIGplZG5vZHVjaMWhaWUgeiBuaWNoLgoKIyMjIyBTY2F0dGVyIHBsb3QKYGBge3J9CiMgQmFzaWMgc2NhdHRlciBwbG90CmxpYnJhcnkoZ2dwbG90MikKCmdncGxvdCh1ZGFqZS5yb2FkX3R5cGUsIGFlcyh4ID0gdHJhZmZpY19kZW5zaXR5LCB5ID0gYXZnX3NwZWVkKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjQsIGNvbG9yID0gInN0ZWVsYmx1ZSIpICsgICAjIHByaWVoxL5hZG7DqSBtb2Ryw6kgYm9keQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpICsgICMgcHJpZMOhIMSNZXJ2ZW7DuiB0cmVuZG92w7ogxI1pYXJ1CiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiVnrFpWFoIG1lZHppIGh1c3RvdG91IHByZW3DoXZreSBhIHByaWVtZXJub3UgcsO9Y2hsb3PFpW91IiwKICAgIHggPSAiSHVzdG90YSBwcmVtw6F2a3kgKHZvemlkbMOhL2ttKSIsCiAgICB5ID0gIlByaWVtZXJuw6EgcsO9Y2hsb3PFpSAoa20vaCkiCiAgKQpgYGAKCgoKIyMjIyBCb3hwbG90CgpgYGB7cn0KIyBCYXIgcGxvdCB3aXRoIGdyb3VwaW5nCmxpYnJhcnkoZ2dwbG90MikKCmxpYnJhcnkoZ2dwbG90MikKCmdncGxvdCh1ZGFqZS5yb2FkX3R5cGUsIGFlcyh4ID0gd2VhdGhlcl9jb25kaXRpb24sIHkgPSBhdmdfc3BlZWQpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAicmVkIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJSw71jaGxvc8WlIHBvZMS+YSBwb8SNYXNpYSIsCiAgICB4ID0gIlBvxI1hc2llIiwKICAgIHkgPSAiUHJpZW1lcm7DoSByw71jaGxvc8WlIChrbS9oKSIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIFrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kuIAoKCiMjIGtuaXRyIC0gdGFidcS+a2EKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGtuaXRyKQpkZW5zaXR5LnN0YXRzIDwtIHVkYWplLnJvYWRfdHlwZSAlPiUKICBncm91cF9ieSh3ZWF0aGVyX2NvbmRpdGlvbikgJT4lICAgICAgICAgICAgICAgIyBzZWdtZW50ID0gcG/EjWFzaWUKICBzdW1tYXJpc2UoCiAgICAgcG96b3JvdmFuaWEgPSBuKCksICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcG/EjWV0IHBvem9yb3ZhbsOtCiAgICBwcmllbWVyICA9IG1lYW4odHJhZmZpY19kZW5zaXR5LCBuYS5ybSA9IFRSVUUpLCAgIyBwcmllbWVyCiAgICBtaW5pbXVtICAgPSBtaW4odHJhZmZpY19kZW5zaXR5LCBuYS5ybSA9IFRSVUUpLCAgICMgbWluaW11bQogICAgbWF4aW11bSAgID0gbWF4KHRyYWZmaWNfZGVuc2l0eSwgbmEucm0gPSBUUlVFKSwgICAjIG1heGltdW0KICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgprYWJsZShkZW5zaXR5LnN0YXRzLCBkaWdpdHMgPSAyLCBjYXB0aW9uID0gIsWgdGF0aXN0aWt5IGh1c3RvdHkgcHJlbcOhdmt5IHBvZMS+YSBwb8SNYXNpYSIpCmBgYAoKIyBhbGVibyBrcmFqxaFpZSB0YWJ1xL5reSBzIHBvbW9jb3UgLmthYmxlRXh0cmEuOgoKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKCiMgU3VtbWFyaXNlIGJhc2ljIHN0YXRpc3RpY3MgZm9yIHRyYWZmaWMgZGF0YQp0cmFmZmljLnN0YXRzIDwtIHVkYWplLnJvYWRfdHlwZSAlPiUKICBncm91cF9ieSh3ZWF0aGVyX2NvbmRpdGlvbikgJT4lICAgIyDihpAgbcO0xb5lxaEgem1lbmnFpSBuYXByLiBuYSByb2FkX3R5cGUgYWxlYm8gbGlnaHRpbmcKICBzdW1tYXJpc2UoCiAgICBuICAgICA9IG4oKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBwb8SNZXQgcG96b3JvdmFuw60KICAgIG1lYW4gID0gbWVhbih0cmFmZmljX2RlbnNpdHksIG5hLnJtID0gVFJVRSksICAjIHByaWVtZXIKICAgIG1pbiAgID0gbWluKHRyYWZmaWNfZGVuc2l0eSwgbmEucm0gPSBUUlVFKSwgICAjIG1pbmltdW0KICAgIG1heCAgID0gbWF4KHRyYWZmaWNfZGVuc2l0eSwgbmEucm0gPSBUUlVFKSwgICAjIG1heGltdW0KICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgojIENyZWF0ZSBzdHlsZWQgdGFibGUKdHJhZmZpYy5zdGF0cyAlPiUKICBrYWJsZSgKICAgIGRpZ2l0cyA9IDIsCiAgICBjYXB0aW9uID0gIlrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kgaHVzdG90eSBwcmVtw6F2a3kgcG9kxL5hIHBvxI1hc2lhIgogICkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKQogICkgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB6dsO9cmF6bmkgbsOhenZ5IGthdGVnw7NyacOtCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGJhY2tncm91bmQgPSAiI2YyZjJmMiIpICU+JSAgICAgICAgIyDFoXTDvWwgaGxhdmnEjWt5CiAgYWRkX2hlYWRlcl9hYm92ZShjKCIgIiA9IDEsICLFoHRhdGlzdGlreSBodXN0b3R5IHByZW3DoXZreSIgPSA0KSkgICMgbmFkcGlzIG5hZCB0YWJ1xL5rb3UKYGBgCgoKCiMjIyMgdC10ZXN0OiBQb3Jvdm5hbmllIGh1c3RvdHkgcHJlbcOhdmt5IG1lZHppIGTFiG9tIGEgbm9jb3UKCmBgYHtyfQp0LnRlc3QucmVzdWx0IDwtIHQudGVzdCgKICB1ZGFqZS5yb2FkX3R5cGUkdHJhZmZpY19kZW5zaXR5W3VkYWplLnJvYWRfdHlwZSRsaWdodGluZyA9PSAiRGF5bGlnaHQiXSwKICB1ZGFqZS5yb2FkX3R5cGUkdHJhZmZpY19kZW5zaXR5W3VkYWplLnJvYWRfdHlwZSRsaWdodGluZyA9PSAiTmlnaHQiXQopCgpwcmludCh0LnRlc3QucmVzdWx0KQpgYGAKCgojIyMjIEFOT1ZBOiBLb250cm9sYSDEjWkgc2EgbMOtxaFpIGh1c3RvdHUgcHJlbcOhdmt5IHBvZMS+YSBwb8SNYXNpYQoKYGBge3J9CmFub3ZhLnJlc3VsdCA8LSBhb3YodHJhZmZpY19kZW5zaXR5IH4gd2VhdGhlcl9jb25kaXRpb24sIGRhdGEgPSB1ZGFqZS5yb2FkX3R5cGUpCnN1bW1hcnkoYW5vdmEucmVzdWx0KQpgYGAKCiMjIyMgTGluZWFyIFJlZ3Jlc3Npb246IFByZWRpa2NpYSByw71jaGxvc3RpIMOhdXQKCmBgYHtyfQptb2RlbCA8LSBsbShhdmdfc3BlZWQgfiB0cmFmZmljX2RlbnNpdHkgKyBudW1fbGFuZXMgKyB3ZWF0aGVyX2NvbmRpdGlvbiwgZGF0YSA9IHVkYWplLnJvYWRfdHlwZSkKc3VtbWFyeShtb2RlbCkKYGBgCgoKCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoYygiYnJvb20iLCAia2FibGVFeHRyYSIsICJkcGx5ciIsICJzdHJpbmdyIikpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShzdHJpbmdyKQoKIyBZb3VyIG1vZGVsIChhbHJlYWR5IGZpdHRlZCkKIyBtb2RlbCA8LSBsbShFU0cuSU5ERVggfiBSRVRVUk4uT04uQVNTRVRTICsgRklSTS5TSVpFICsgREVCVC5UTy5BU1NFVCwgZGF0YSA9IHVkYWplLjIwMTMpCgpjb2VmLnRibCA8LSB0aWR5KG1vZGVsLCBjb25mLmludCA9IFRSVUUpICU+JQogIG11dGF0ZSgKICAgIHRlcm0gPSByZWNvZGUodGVybSwKICAgICAgIihJbnRlcmNlcHQpIiA9ICJJbnRlcmNlcHQiLAogICAgICAidHJhZmZpY19kZW5zaXR5IiA9ICJUcmFmZmljIERlbnNpdHkiLAogICAgICAibnVtX2xhbmVzIiA9ICJOdW1iZXIgb2YgTGFuZXMiLAogICAgICAid2VhdGhlcl9jb25kaXRpb25SYWluIiA9ICJXZWF0aGVyOiBSYWluIiwKICAgICAgIndlYXRoZXJfY29uZGl0aW9uRm9nZ3kiID0gIldlYXRoZXI6IEZvZ2d5IgogICAgKSwKICAgIHN0YXJzID0gY2FzZV93aGVuKAogICAgICBwLnZhbHVlIDwgMC4wMDEgfiAiKioqIiwKICAgICAgcC52YWx1ZSA8IDAuMDEgIH4gIioqIiwKICAgICAgcC52YWx1ZSA8IDAuMDUgIH4gIioiLAogICAgICBwLnZhbHVlIDwgMC4xICAgfiAiwrciLAogICAgICBUUlVFICAgICAgICAgICAgfiAiIgogICAgKQogICkgJT4lCiAgdHJhbnNtdXRlKAogICAgVGVybSA9IHRlcm0sCiAgICBFc3RpbWF0ZSA9IGVzdGltYXRlLAogICAgYFN0ZC4gRXJyb3JgID0gc3RkLmVycm9yLAogICAgYHQgdmFsdWVgID0gc3RhdGlzdGljLAogICAgYHAgdmFsdWVgID0gcC52YWx1ZSwKICAgIGA5NSUgQ0lgID0gc3RyX2MoIlsiLCByb3VuZChjb25mLmxvdywgMyksICIsICIsIHJvdW5kKGNvbmYuaGlnaCwgMyksICJdIiksCiAgICBTaWcgPSBzdGFycwogICkKCmNvZWYudGJsICU+JQogIGthYmxlKAogICAgZGlnaXRzID0gMywKICAgIGNhcHRpb24gPSAiT0xTIFJlZ3Jlc3Npb24gQ29lZmZpY2llbnRzIChhdmdfc3BlZWQgfiB0cmFmZmljX2RlbnNpdHkgKyBudW1fbGFuZXMgKyB3ZWF0aGVyX2NvbmRpdGlvbikiCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKSAlPiUKICBjb2x1bW5fc3BlYygxLCBib2xkID0gVFJVRSkgJT4lCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGJhY2tncm91bmQgPSAiI2YyZjJmMiIpICU+JQogIGZvb3Rub3RlKAogICAgZ2VuZXJhbCA9ICJTaWduaWYuIGNvZGVzOiAqKiogcDwwLjAwMSwgKiogcDwwLjAxLCAqIHA8MC4wNSwgwrcgcDwwLjEuIiwKICAgIHRocmVlcGFydHRhYmxlID0gVFJVRQogICkKYGBgCgpgYGB7cn0KZml0LnRibCA8LSBnbGFuY2UobW9kZWwpICU+JQogIHRyYW5zbXV0ZSgKICAgIGBSLXNxdWFyZWRgICAgICAgPSByLnNxdWFyZWQsCiAgICBgQWRqLiBSLXNxdWFyZWRgID0gYWRqLnIuc3F1YXJlZCwKICAgIGBGLXN0YXRpc3RpY2AgICAgPSBzdGF0aXN0aWMsCiAgICBgRiBwLXZhbHVlYCAgICAgID0gcC52YWx1ZSwKICAgIGBBSUNgICAgICAgICAgICAgPSBBSUMsCiAgICBgQklDYCAgICAgICAgICAgID0gQklDLAogICAgYE51bS4gb2JzLmAgICAgICA9IG5vYnMKICApCgojIDTvuI/ig6MgVnlrcmVzbGkgdGFidcS+a3UgcyBwb3Bpc29tCmZpdC50YmwgJT4lCiAga2FibGUoCiAgICBkaWdpdHMgPSAzLAogICAgY2FwdGlvbiA9ICJNb2RlbCBGaXQgU3RhdGlzdGljcyAoYXZnX3NwZWVkIH4gdHJhZmZpY19kZW5zaXR5ICsgbnVtX2xhbmVzICsgd2VhdGhlcl9jb25kaXRpb24pIgogICkgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygiY29uZGVuc2VkIikpCiAgICAKYGBgCgoKCgoKCiMgSW5mbyB6ZHJvamUgcHJlIMSPYWzFoWllIMWhdMO6ZGl1bQoKIyMjIyBSIFByb2plY3QKCi0gICBbUiBQcm9qZWN0IEhvbWVwYWdlXShodHRwczovL3d3dy5yLXByb2plY3Qub3JnLykgLSBCYXNlIFIgZG93bmxvYWRzLAogICAgbmV3cywgYW5kIGxlYXJuaW5nIHJlc291cmNlcwoKIyMjIyBQb3NpdAoKLSAgIFtSIFN0dWRpbyBEZXNrdG9wXShodHRwczovL3Bvc2l0LmNvL2Rvd25sb2FkL3JzdHVkaW8tZGVza3RvcC8pIC0KICAgIEZlYXR1cmUgcmljaCBlbnZpcm9ubWVudCBmb3Igd29ya2luZyB3aXRoIGRhdGEgaW4gUgotICAgW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pIC0gQ29tcHJlaGVuc2l2ZSBvbmxpbmUKICAgIGJvb2sKLSAgIFtSU3R1ZGlvCiAgICBDaGVhdHNoZWV0c10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykgLSBRdWljawogICAgcmVmZXJlbmNlIGd1aWRlcwoKIyMjIyBDb21tdW5pdHkgUmVzb3VyY2VzCgotICAgW1ItYmxvZ2dlcnNdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLykgLSBCbG9nIGFnZ3JlZ2F0b3IgZm9yIFIKICAgIG5ld3MgYW5kIHR1dG9yaWFscwotICAgW1N0YWNrIE92ZXJmbG93IC0gUgogICAgdGFnXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy90YWdnZWQvcikgLSBRJkEgY29tbXVuaXR5Ci0gICBbcmRyci5pbyBTbmlwcGV0c10oaHR0cHM6Ly9yZHJyLmlvL3NuaXBwZXRzLykgLSBUZXN0IFIgY29kZSBzbmlwcGV0cwogICAgb25saW5lCi0gICBbQ291cnNlcmEgLSBSCiAgICBQcm9ncmFtbWluZ10oaHR0cHM6Ly93d3cuY291cnNlcmEub3JnL2xlYXJuL3ItcHJvZ3JhbW1pbmcpIC0gT25saW5lCiAgICBjb3Vyc2UKLSAgIFtEYXRhQ2FtcCAtIEludHJvZHVjdGlvbiB0bwogICAgUl0oaHR0cHM6Ly93d3cuZGF0YWNhbXAuY29tL2NvdXJzZXMvZnJlZS1pbnRyb2R1Y3Rpb24tdG8tcikgLQogICAgSW50ZXJhY3RpdmUgbGVhcm5pbmcgcGxhdGZvcm0KCg==