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")
  Vyska = c(250, 270, 350)
  Vaha = 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,Vyska,Vaha)
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$Vyska)                 # takto adresujeme jednotlivé premenné v data.frame
[1] 250 270 350
print(mean(udaje$Vyska))           # 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              Vyska          Vaha      
 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

Masozravec <- c(TRUE,TRUE,TRUE)
udaje <- cbind(udaje,Masozravec)
print(udaje)

Ak chceme pridať riadok, potom

# New record (must match column order/types)
novy.riadok <- data.frame(Zviera = "Delfín", Vyska = 300, Vaha = 220, Masozravec = TRUE)

novy.riadok <- data.frame(Zviera = "Žirafa", Vyska = 500, Vaha = 1200, Masozravec = FALSE)

novy.riadok <- data.frame(Zviera = "Krava", Vyska = 150, Vaha = 700, Masozravec = FALSE)

novy.riadok <- data.frame(Zviera = "Slon", Vyska = 300, Vaha = 6000, Masozravec = 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 Vyska Vaha Masozravec
Medveď 250 250 TRUE
Tiger 270 200 TRUE
Aligátor 350 380 TRUE
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(Vaha > 180) %>%     # vybera zaznamy s váhou väčšou ako 180 kg
  arrange(desc(Vaha)) %>%     # vysledny subor triedi zostupne podla premennej Vaha
kable %>%
    kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Zviera Vyska Vaha Masozravec
Slon 300 6000 FALSE
Aligátor 350 380 TRUE
Medveď 250 250 TRUE
Tiger 270 200 TRUE

Zoskupenie a sumarizácia

# Zoskupí and sumarizuje
udaje %>%
  group_by(Masozravec) %>%      # zoskupi zaznamy podla toho či je Masozravec a vypocita za kazdu skupinu jej priemer Váhy
  summarise(                # a taktiez spocita pocetnosti oboch skupin
    Priem.Vaha = mean(Vaha),
    count = n()
  ) %>%
 kable(
    caption = "Počet Zvierat v ZOO, ktoré sú Mäsožravé a ich priemerná Vaha ",
    col.names = c("Masozravec", "Vaha", "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á Vaha
Masozravec Vaha Počet
FALSE 6000.0000 1
TRUE 276.6667 3

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 Vyska Vaha Masozravec Inteligencia
Medveď 250 250 TRUE B
Tiger 270 200 TRUE B
Aligátor 350 380 TRUE D
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)
library(kableExtra)

density.stats <- udaje.road_type %>%
  group_by(weather_condition) %>%
  summarise(
    Pozorovania = n(),
    Priemer = mean(traffic_density, na.rm = TRUE),
    Minimum = min(traffic_density, na.rm = TRUE),
    Maximum = max(traffic_density, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    Priemer = round(Priemer, 2),
    Minimum = round(Minimum, 2),
    Maximum = round(Maximum, 2)
  ) %>%
  arrange(weather_condition)

density.stats %>%
  kable(caption = "Štatistiky hustoty premávky podľa počasia") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Š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
NA

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

library(broom)
library(knitr)
library(kableExtra)

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"]
)

t.test.table <- tidy(t.test.result)

t.test.table %>%
  kable(
    digits = 4,
    caption = "t-test – Porovnanie hustoty premávky: Deň vs. Noc"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
t-test – Porovnanie hustoty premávky: Deň vs. Noc
estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high method alternative
-5.8673 270.046 275.9133 -0.7254 0.4684 1000.103 -21.7394 10.0049 Welch Two Sample t-test two.sided
NA

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

library(broom)
library(knitr)
library(kableExtra)

anova.result <- aov(traffic_density ~ weather_condition, data = udaje.road_type)

anova.table <- tidy(anova.result)

anova.table %>%
  kable(
    digits = 4,
    caption = "ANOVA – Vplyv počasia na hustotu premávky"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
ANOVA – Vplyv počasia na hustotu premávky
term df sumsq meansq statistic p.value
weather_condition 2 50482.86 25241.43 1.5408 0.2147
Residuals 1000 16382533.96 16382.53 NA NA
NA
NA
NA

Linear Regression: Predikcia rýchlosti áut

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

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

model.table <- tidy(model) %>%
  mutate(
    estimate  = round(estimate, 3),
    std.error = round(std.error, 3),
    statistic = round(statistic, 2),
    p.value   = round(p.value, 4)
  )

model.table %>%
  kable(
    caption = "Regresný model – faktory ovplyvňujúce priemernú rýchlosť"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Regresný model – faktory ovplyvňujúce priemernú rýchlosť
term estimate std.error statistic p.value
(Intercept) 75.699 3.289 23.01 0.0000
traffic_density -0.002 0.007 -0.25 0.7999
num_lanes -1.398 0.660 -2.12 0.0344
weather_conditionFoggy -1.297 2.321 -0.56 0.5763
weather_conditionRainy -1.835 2.299 -0.80 0.4249
NA
# 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+aWFrb2NoLCBrdG9yw6kgcHJlZHN0YXZ1asO6IHRyaSBwcmVtZW5uw6kgLSBNZW5vLCBWZWsgYSBCb2R5OgoKYGBge3J9CiMgV29ya2luZyB3aXRoIGRhdGEgZnJhbWVzCgogIFp2aWVyYSA9IGMoIk1lZHZlxI8iLCAiVGlnZXIiLCAiQWxpZ8OhdG9yIikKICBWeXNrYSA9IGMoMjUwLCAyNzAsIDM1MCkKICBWYWhhID0gYygyNTAsIDIwMCwgMzgwKQpgYGAKClRpZXRvIHRyaSBwcmVtZW5uw6kgbmllIHPDuiB6YXRpYcS+IG5pamFrbyBwcmVwb2plbsOpLCBwcmVkc3RhdnVqw7ogaXpvbG92YW7DqSBzdMS6cGNlIHRhYnXEvmt5LiBEbyB0YWJ1xL5reSBpY2ggc3BvasOtbWUgbmFzbGVkb3ZuZQoKYGBge3J9CnVkYWplIDwtIGRhdGEuZnJhbWUoWnZpZXJhLFZ5c2thLFZhaGEpCnByaW50KHVkYWplKQpgYGAKCgpWeXN2ZXRsZW5pZTogRGF0YUZyYW1lIG3DoSB0cmkgc3TEunBjZTogWnZpZXJhLCBWw73FoWt1IGEgVsOhaHUuIE5pZWt0b3LDqSBvcGVyw6FjaWUgcyDDumRham1pIG9yZ2FuaXpvdmFuw71taSB2IC5kYXRhLmZyYW1lLiBzw7ogdXZlZGVuw6kgbmFzbGVkb3ZuZQoKYGBge3J9CnByaW50KHVkYWplJFZ5c2thKSAgICAgICAgICAgICAgICAgIyB0YWt0byBhZHJlc3VqZW1lIGplZG5vdGxpdsOpIHByZW1lbm7DqSB2IGRhdGEuZnJhbWUKcHJpbnQobWVhbih1ZGFqZSRWeXNrYSkpICAgICAgICAgICAjIHByaWVtZXJudSB2w73FoWt1CnByaW50KHVkYWplW1p2aWVyYT09IlRpZ2VyIixdKSAgICAgIyBhZHJlc292YW5pZSBjZWzDqWhvIHJpYWRrdQpwcmludCh1ZGFqZVszLF0pICAgICAgICAgICAgICAgICAjIGluYSBtb3pub3N0IGFkcmVzb3ZhbmlhIGNlbGVobyByaWFka3UKcHJpbnQodWRhamVbLDI6M10pICAgICAgICAgICAgICAgIyB2eXBpc2FuaWUgZHJ1aGVobyBhIHRyZXRpZWhvIHN0bHBjYSB0YWJ1bGt5CnByaW50KHVkYWplWzEsMV0pICAgICAgICAgICAgICAgICMgdnlwaXNhbmllIGplZG5laiBidW5reSB0YWJ1bGt5CnN1bW1hcnkodWRhamUpICAgICAgICAgICAgICAgICAgICMgemFrbGFkbmEgZGVza3JpcHRpdm5hIHN0YXRpc3Rpa2EgY2VsZWogdGFidWxreQpgYGAKCkFrIGNoY2VtZSBwcmlkYcWlIGsgdGFidcS+a2UgZG9kYXRvxI1uw70gc3TEunBlYywgcG90b20gdG8gcm9iw61tZSBuYXNsZWRvdm5lCgpgYGB7cn0KTWFzb3pyYXZlYyA8LSBjKFRSVUUsVFJVRSxUUlVFKQp1ZGFqZSA8LSBjYmluZCh1ZGFqZSxNYXNvenJhdmVjKQpwcmludCh1ZGFqZSkKYGBgCgpBayBjaGNlbWUgcHJpZGHFpSByaWFkb2ssIHBvdG9tCgpgYGB7cn0KIyBOZXcgcmVjb3JkIChtdXN0IG1hdGNoIGNvbHVtbiBvcmRlci90eXBlcykKbm92eS5yaWFkb2sgPC0gZGF0YS5mcmFtZShadmllcmEgPSAiRGVsZsOtbiIsIFZ5c2thID0gMzAwLCBWYWhhID0gMjIwLCBNYXNvenJhdmVjID0gVFJVRSkKCm5vdnkucmlhZG9rIDwtIGRhdGEuZnJhbWUoWnZpZXJhID0gIsW9aXJhZmEiLCBWeXNrYSA9IDUwMCwgVmFoYSA9IDEyMDAsIE1hc296cmF2ZWMgPSBGQUxTRSkKCm5vdnkucmlhZG9rIDwtIGRhdGEuZnJhbWUoWnZpZXJhID0gIktyYXZhIiwgVnlza2EgPSAxNTAsIFZhaGEgPSA3MDAsIE1hc296cmF2ZWMgPSBGQUxTRSkKCm5vdnkucmlhZG9rIDwtIGRhdGEuZnJhbWUoWnZpZXJhID0gIlNsb24iLCBWeXNrYSA9IDMwMCwgVmFoYSA9IDYwMDAsIE1hc296cmF2ZWMgPSBGQUxTRSkKCgojIEFwcGVuZAp1ZGFqZSA8LSByYmluZCh1ZGFqZSwgbm92eS5yaWFkb2spCnByaW50KHVkYWplKQpgYGAKCiMjIyBUYWJ1xL5reSB2IHByb3N0cmVkw60ga2FibGVleHRyYQoKCmBgYHtyfQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmthYmxlKAogIHVkYWplLAojICBmb3JtYXQsCmRpZ2l0cyA9IDIsCiMgIHJvdy5uYW1lcyA9IE5BLAojICBjb2wubmFtZXMgPSBOQSwKICBhbGlnbj1jKCJsIiwiYyIsImwiLCJyIiksCiAgY2FwdGlvbiA9ICJaT08iCiMgIGxhYmVsID0gTlVMTCwKIyAgZm9ybWF0LmFyZ3MgPSBsaXN0KCksCiMgIGVzY2FwZSA9IFRSVUUsCiAjIC4uLgopICU+JQogICAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIikKCgoKCgpgYGAKCgoKIyMgVGlkeXZlcnNlIC0gbW9kZXJuw6EgcHLDoWNhIHMgw7pkYWptaQoKVGlkeXZlcnNlIGplIHPDumJvciBrbmnFvm7DrWMsIGt0b3LDqSBtYWrDuiB6amVkbm9kdcWhacWlIHByw6FjdSBzIMO6ZGFqbWkuIE1hasO6IGplZG5vdG7DvSBrb211bmlrYcSNbsO9IMWhdGFuZGFyZCwgdnrDoWpvbW5lIHNhIGRvcGzFiHVqw7ouCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIExvYWQgdGlkeXZlcnNlCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyAgZHBseXIgLSBwcmUgbWFuaXB1bMOhY2l1IHMgw7pkYWptaQoKLmRwbHlyLiBwb3NreXR1amUgesOha2xhZG7DqSBtb8W+bm9zdGkgbWFuaXB1bMOhY2llIHMgw7pkYWptaSwgYWtvIG5hcHIuOiAKCjEuIGZpbHRlcigpOiB2eWJlcsOhIHJpYWRreSAKCjEuIHNlbGVjdCgpOiB2eWJlcsOhIHN0xLpwY2UgCgoxLiBtdXRhdGUoKTogdnl0dsOhcmEgbm92w6kgc3TEunBjZSB0YWJ1xL5reSAKCjEuIGFycmFuZ2UoKTogdHJpZWRpIHJpYWRreSAKCjEuIHN1bW1hcmlzZSgpOiBzdW1hcml6dWplCgpWIG5hc2xlZG92bmVqIHVrw6HFvmtlIHZ5dcW+aWplbWUgdHp2LiAucGlwZXMuICU+JSBhbGVibyAlPCUgdW1vxb7FiHVqZSBwb3NpZWxhxaUgdsO9c2xlZGt5IHogamVkbmVqIGZ1bmtjaWUgcHJpYW1vIGRvIHZvbGFuaWUgbmFzbGVkb3ZuZWogZnVua2NpZS4gVG8gdW1vxb7FiHVqZSDEvmFoxaFpdSDEjWl0YXRlxL5ub3PFpSBrw7Nkb3YsIGtvbnZlbmNpYSBzYSB1amFsYSBhIG3DoSDFoWlyb2vDqSBwb3XFvml0aWUuCgojIyMjIFbDvWJlciBhIHRyaWVkZW5pZQoKYGBge3J9CiMgdsO9YmVyIGEgbsOhc2xlZG7DqSB0cmllZGVuaWUKdWRhamUgJT4lCiAgZmlsdGVyKFZhaGEgPiAxODApICU+JSAgICAgIyB2eWJlcmEgemF6bmFteSBzIHbDoWhvdSB2w6TEjcWhb3UgYWtvIDE4MCBrZwogIGFycmFuZ2UoZGVzYyhWYWhhKSkgJT4lICAgICAjIHZ5c2xlZG55IHN1Ym9yIHRyaWVkaSB6b3N0dXBuZSBwb2RsYSBwcmVtZW5uZWogVmFoYQprYWJsZSAlPiUKICAgIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMjIyBab3NrdXBlbmllIGEgc3VtYXJpesOhY2lhCgpgYGB7cn0KIyBab3NrdXDDrSBhbmQgc3VtYXJpenVqZQp1ZGFqZSAlPiUKICBncm91cF9ieShNYXNvenJhdmVjKSAlPiUgICAgICAjIHpvc2t1cGkgemF6bmFteSBwb2RsYSB0b2hvIMSNaSBqZSBNYXNvenJhdmVjIGEgdnlwb2NpdGEgemEga2F6ZHUgc2t1cGludSBqZWogcHJpZW1lciBWw6FoeQogIHN1bW1hcmlzZSggICAgICAgICAgICAgICAgIyBhIHRha3RpZXogc3BvY2l0YSBwb2NldG5vc3RpIG9ib2NoIHNrdXBpbgogICAgUHJpZW0uVmFoYSA9IG1lYW4oVmFoYSksCiAgICBjb3VudCA9IG4oKQogICkgJT4lCiBrYWJsZSgKICAgIGNhcHRpb24gPSAiUG/EjWV0IFp2aWVyYXQgdiBaT08sIGt0b3LDqSBzw7ogTcOkc2/FvnJhdsOpIGEgaWNoIHByaWVtZXJuw6EgVmFoYSAiLAogICAgY29sLm5hbWVzID0gYygiTWFzb3pyYXZlYyIsICJWYWhhIiwgIlBvxI1ldCIpLAogICAgYWxpZ24gPSAiYyIKICApICU+JQogIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMjIyBWeXR2w6FyYW5pZSBub3ZlaiBwcmVtZW5uZWoKCmBgYHtyfQojIFZ5dHbDoXJhbmllIG5vdmVqIHByZW1lbm5lagp1ZGFqZSAlPiUKICBtdXRhdGUoCiAgICBJbnRlbGlnZW5jaWEgPSBjYXNlX3doZW4oICAgICAjIHZ5dHZhcmEgbm92dSBwcmVtZW5udSBJbnRlbGlnZW5jaWEgcG9kbGEgbmFzbGVkb3ZuZWogcmVsYWNuZWogc2NoZW15LCBhYnkgc21lIHphcMOtc2FsaSwga3RvcsOpIHp2aWVyYSBqZSBpbnRlbGlnZW50bmVqxaFpZSBvZCB0b2hvIGRydWjDqWhvIAogICAgIFp2aWVyYSA9PSAiRGVsZsOtbiIgfiAiQSIsICAgICAgICMgdmXEvm1pIGludGVsaWdlbnRuw6kKICAgICAgWnZpZXJhID09ICJTbG9uIiB+ICJBIiwgICAgICAgICAjIHZlxL5taSBpbnRlbGlnZW50bsOpCiAgICAgIFp2aWVyYSA9PSAiTWVkdmXEjyIgfiAiQiIsICAgICAgICMgbmFkcHJpZW1lcm7DoSBpbnRlbGlnZW5jaWEKICAgICAgWnZpZXJhID09ICJUaWdlciIgfiAiQiIsICAgICAgICAjIG5hZHByaWVtZXJuw6EgaW50ZWxpZ2VuY2lhCiAgICAgIFp2aWVyYSA9PSAixb1pcmFmYSIgfiAiQyIsICAgICAgICMgcHJpZW1lcm7DoSBpbnRlbGlnZW5jaWEKICAgICAgWnZpZXJhID09ICJLcmF2YSIgfiAiQyIsICAgICAgICAjIHByaWVtZXJuw6EgaW50ZWxpZ2VuY2lhCiAgICAgIFp2aWVyYSA9PSAiQWxpZ8OhdG9yIiB+ICJEIiwgICAgICMgbsOtemthIGludGVsaWdlbmNpYQogICAgICBUUlVFIH4gIk5lem7DoW1lIgogICAgKSwKICAKICApICU+JSAKICBrYWJsZSAlPiUKICAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkgCmBgYAoKCiMjIEltcG9ydCDDumRham92IHogb3R2LiBkYXRhYsOhegoKCjEuICpNZW5kZWxleSBEYXRhKiBUdXRvIHNhIGRvc3RhbmVtZSB6IFtNZW5kZWxleSBEYXRhXShodHRwczovL2RhdGEubWVuZGVsZXkuY29tLyl7dGFyZ2V0PSIuYmxhbmsiIHJlbD0ibm9vcGVuZXIifSwga2RlIHNpIMO6ZGFqZSB2aWV0ZSB2b8S+bmUgc3RpYWhuw7rFpS4gw5pkYWplIHNhIHZ6xaVhaHVqw7ogayB1xb4gcHVibGlrb3ZhbsO9bSDEjWzDoW5rb20gdm8gdnlkYXZhdGXEvnN0dmUgRWxzZXZpZXIuIFbDvWJlciBzYSBkw6EgdXJvYmnFpSBqZWRub2R1Y2hvIHphZGFuw61tIGvEvsO6xI1vdsO9Y2ggc2xvdi4KMi4gKkthZ2dsZSBEYXRhKiAgIFR1dG8gc2EgZG9zdGFuZW1lIHogW0thZ2dsZSBEYXRhc2V0c10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cyl7dGFyZ2V0PSIuYmxhbmsiIHJlbD0ibm9vcGVuZXIifSwga2RlIHNpIMO6ZGFqZSB2aWV0ZSB2b8S+bmUgc3RpYWhuw7rFpS4gw5pkYWplIHNhIHZ6xaVhaHVqw7ogayBwcm9qZWt0b20gcG9kcG9yb3ZhbsO9bSBLYWdnbGUuIFbDvWJlciBzYSBkw6EgdXJvYmnFpSBqZWRub2R1Y2hvIHphZGFuw61tIGvEvsO6xI1vdsO9Y2ggc2xvdi4KMy4gRGF0YWLDoXp5IGtuacW+bsOtYyBSIC0gLmxpYnJhcnkoZGF0YXNldHMpLiBhbGVibyAubGlicmFyeSh3b29sZHJpZGdlKS4gYWxlIGFqIGluw6kgLSBzdGHEjcOtIHNpIGRhxaUgcHLDrWtheiBkYXRhKCkKCgojIyMgSW1wb3J0IMO6ZGFqb3YgeiAuY3N2IGFsZWJvIC54bHMKCkphIHNvbSBzaSB6dm9saWwgw7pkYWplIHogW0Fib3NlZGUgVGlhbWl5dTogRW52aXJvbm1lbnRhbCwgU29jaWFsLCBhbmQgR292ZXJuYW5jZSBSZXBvcnRpbmcgRXZpZGVuY2luZyBGaXJtIFBlcmZvcm1hbmNlIGluIEVtZXJnaW5nIEVjb25vbXlde2h0dHBzOi8vZGF0YS5tZW5kZWxleS5jb20vZGF0YXNldHMvN2s4cGpoc3J3Yi8xfS4gTmEgc3Ryw6Fua2Ugc2EgbmFjaMOhZHphIHPDumJvciAuRGF0YXNldCBFU0cgYW5kIEZpcm0gUGVyZm9ybWFuY2UueGxzeC4sIGt0b3LDvSBzb20gc2kgc3RpYWhvbCBhIGV4cG9ydG92YWwgZG8gZm9ybcOhdHUgY3N2LiBBa28gb2RkZcS+b3ZhxI0gcG9sb8W+aWVrIHNvbSBzaSB6dm9saWwgYm9ka2/EjWlhcmt1IChzZW1pY29sb24gOyksIHZ5xb7DrXZhbSBkZXNhdGlubsO6IGJvZGt1IGEgbmllIMSNaWFya3UgYSB0aWXFviB0ZXh0b3bDqSBwcmVtZW5uw6kgdXbDoWR6YW0gYXBvc3Ryb2ZtaSAiLiBWIHBydm9tIHJpYWRrdSBzYSBuYWNow6FkemFqw7ogbsOhenZ5IHN0xLpwY292LCBrdG9yw6kgbmVza8O0ciBidWTDuiB2eXN0dXBvdmHFpSBha28gcHJlbWVubsOpLiBUaWUgb2JzYWh1asO6IG1lZHplcnksIMSNbyBqZSB2IHrDoXp2ZSBwcmVtZW5uZWogbmVwcsOtcHVzdG7DqSBhIG5haHJhZGlsIHNvbSBpY2ggcG9kdHJob3bDoXRrb20gIi4iLiAgCgohW07DoWjEvmFkIG5hIHhscyBkYXRhYsOhenUgb3R2b3JlbsO6IHYgdGFidcS+a292b20gcHJvY2Vzb3JlXShvYnJhemt5L3VkYWpleGxzLmpwZyl7d2lkdGg9MTAwJX0KCiFbTsOhaMS+YWQgbmEgY3N2IGRhdGFiw6F6dSBvdHZvcmVuw7ogdiB0ZXh0b3ZvbSBwcm9jZXNvcmVdKG9icmF6a3kvdWRhamVjc3YuanBnKXt3aWR0aD0xMDAlfQoKUG90b20gdcW+IHN0YcSNw60gaW1wb3J0b3ZhxaUgw7pkYWplIGRvIC5kYXRhLmZyYW1lLiwgYSB0byBuYXNsZWRvdm5lCgpgYGB7cn0KbGlicmFyeShyZWFkcikKdWRhamUgPC0gcmVhZF9kZWxpbSgidGVzdC5jc3YiLCBkZWxpbSA9IE5VTEwpCmhlYWQodWRhamUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbmF6dnkgcHJlbWVubnljaApgYGAKCiMjIEdyYWZ5CmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQoKdWRhamUucm9hZF90eXBlIDwtIHVkYWplICU+JQogIGZpbHRlcihyb2FkX3R5cGUgPT0gIkhpZ2h3YXkiKSAlPiUKICBzZWxlY3QodHJhZmZpY19kZW5zaXR5LCBhdmdfc3BlZWQsIHdlYXRoZXJfY29uZGl0aW9uLCBudW1fbGFuZXMsIHJvYWRfc3VyZmFjZSwgbGlnaHRpbmcpCmBgYAoKIyMjIGdncGxvdDIgLSBrbmnFvm5pY2EgcHJlIGdyYWZ5CgpWw71iZXIgYSBuw6FzbGVkbsOpIHRyaWVkZW5pZQpLbmnFvm5pY2EgLmdncGxvdDIuIGplIHYgc8O6xI1hc25vc3RpIG5hasSNYXN0ZWrFoWllIHBvdcW+w612YW7DoSBncmFmaWNrw6Ega25pxb5uaWNhLCBwcmnEjW9tIHByZWRwcmlwcmF2ZW7DqSBrw7NkeSBrIGplZG5vdGxpdsO9bSBvYnLDoXprb20gc2kgdmlldGUgbsOhanPFpSB2IFtSIEdyYXBoIEdhbGxlcnldKGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8pLiBUdSBzaSB1dmVkaWVtZSBqZWRub2R1Y2jFoWllIHogbmljaC4KCiMjIyMgU2NhdHRlciBwbG90CmBgYHtyfQojIEJhc2ljIHNjYXR0ZXIgcGxvdApsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QodWRhamUucm9hZF90eXBlLCBhZXMoeCA9IHRyYWZmaWNfZGVuc2l0eSwgeSA9IGF2Z19zcGVlZCkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC40LCBjb2xvciA9ICJzdGVlbGJsdWUiKSArICAgIyBwcmllaMS+YWRuw6kgbW9kcsOpIGJvZHkKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiLCBzZSA9IEZBTFNFKSArICAjIHByaWTDoSDEjWVydmVuw7ogdHJlbmRvdsO6IMSNaWFydQogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicygKICAgIHRpdGxlID0gIlZ6xaVhaCBtZWR6aSBodXN0b3RvdSBwcmVtw6F2a3kgYSBwcmllbWVybm91IHLDvWNobG9zxaVvdSIsCiAgICB4ID0gIkh1c3RvdGEgcHJlbcOhdmt5ICh2b3ppZGzDoS9rbSkiLAogICAgeSA9ICJQcmllbWVybsOhIHLDvWNobG9zxaUgKGttL2gpIgogICkKYGBgCgoKCiMjIyMgQm94cGxvdAoKYGBge3J9CiMgQmFyIHBsb3Qgd2l0aCBncm91cGluZwpsaWJyYXJ5KGdncGxvdDIpCgpsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QodWRhamUucm9hZF90eXBlLCBhZXMoeCA9IHdlYXRoZXJfY29uZGl0aW9uLCB5ID0gYXZnX3NwZWVkKSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiUsO9Y2hsb3PFpSBwb2TEvmEgcG/EjWFzaWEiLAogICAgeCA9ICJQb8SNYXNpZSIsCiAgICB5ID0gIlByaWVtZXJuw6EgcsO9Y2hsb3PFpSAoa20vaCkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyBaw6FrbGFkbsOpIMWhdGF0aXN0aWt5LiAKCgojIyBrbml0ciAtIHRhYnXEvmthCgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQoKZGVuc2l0eS5zdGF0cyA8LSB1ZGFqZS5yb2FkX3R5cGUgJT4lCiAgZ3JvdXBfYnkod2VhdGhlcl9jb25kaXRpb24pICU+JQogIHN1bW1hcmlzZSgKICAgIFBvem9yb3ZhbmlhID0gbigpLAogICAgUHJpZW1lciA9IG1lYW4odHJhZmZpY19kZW5zaXR5LCBuYS5ybSA9IFRSVUUpLAogICAgTWluaW11bSA9IG1pbih0cmFmZmljX2RlbnNpdHksIG5hLnJtID0gVFJVRSksCiAgICBNYXhpbXVtID0gbWF4KHRyYWZmaWNfZGVuc2l0eSwgbmEucm0gPSBUUlVFKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIG11dGF0ZSgKICAgIFByaWVtZXIgPSByb3VuZChQcmllbWVyLCAyKSwKICAgIE1pbmltdW0gPSByb3VuZChNaW5pbXVtLCAyKSwKICAgIE1heGltdW0gPSByb3VuZChNYXhpbXVtLCAyKQogICkgJT4lCiAgYXJyYW5nZSh3ZWF0aGVyX2NvbmRpdGlvbikKCmRlbnNpdHkuc3RhdHMgJT4lCiAga2FibGUoY2FwdGlvbiA9ICLFoHRhdGlzdGlreSBodXN0b3R5IHByZW3DoXZreSBwb2TEvmEgcG/EjWFzaWEiKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKCmBgYAoKIyBhbGVibyBrcmFqxaFpZSB0YWJ1xL5reSBzIHBvbW9jb3UgLmthYmxlRXh0cmEuOgoKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKCiMgU3VtbWFyaXNlIGJhc2ljIHN0YXRpc3RpY3MgZm9yIHRyYWZmaWMgZGF0YQp0cmFmZmljLnN0YXRzIDwtIHVkYWplLnJvYWRfdHlwZSAlPiUKICBncm91cF9ieSh3ZWF0aGVyX2NvbmRpdGlvbikgJT4lICAgIyDihpAgbcO0xb5lxaEgem1lbmnFpSBuYXByLiBuYSByb2FkX3R5cGUgYWxlYm8gbGlnaHRpbmcKICBzdW1tYXJpc2UoCiAgICBuICAgICA9IG4oKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBwb8SNZXQgcG96b3JvdmFuw60KICAgIG1lYW4gID0gbWVhbih0cmFmZmljX2RlbnNpdHksIG5hLnJtID0gVFJVRSksICAjIHByaWVtZXIKICAgIG1pbiAgID0gbWluKHRyYWZmaWNfZGVuc2l0eSwgbmEucm0gPSBUUlVFKSwgICAjIG1pbmltdW0KICAgIG1heCAgID0gbWF4KHRyYWZmaWNfZGVuc2l0eSwgbmEucm0gPSBUUlVFKSwgICAjIG1heGltdW0KICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgojIENyZWF0ZSBzdHlsZWQgdGFibGUKdHJhZmZpYy5zdGF0cyAlPiUKICBrYWJsZSgKICAgIGRpZ2l0cyA9IDIsCiAgICBjYXB0aW9uID0gIlrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kgaHVzdG90eSBwcmVtw6F2a3kgcG9kxL5hIHBvxI1hc2lhIgogICkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKQogICkgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB6dsO9cmF6bmkgbsOhenZ5IGthdGVnw7NyacOtCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGJhY2tncm91bmQgPSAiI2YyZjJmMiIpICU+JSAgICAgICAgIyDFoXTDvWwgaGxhdmnEjWt5CiAgYWRkX2hlYWRlcl9hYm92ZShjKCIgIiA9IDEsICLFoHRhdGlzdGlreSBodXN0b3R5IHByZW3DoXZreSIgPSA0KSkgICMgbmFkcGlzIG5hZCB0YWJ1xL5rb3UKYGBgCgoKCiMjIyMgdC10ZXN0OiBQb3Jvdm5hbmllIGh1c3RvdHkgcHJlbcOhdmt5IG1lZHppIGTFiG9tIGEgbm9jb3UKCmBgYHtyfQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgp0LnRlc3QucmVzdWx0IDwtIHQudGVzdCgKICB1ZGFqZS5yb2FkX3R5cGUkdHJhZmZpY19kZW5zaXR5W3VkYWplLnJvYWRfdHlwZSRsaWdodGluZyA9PSAiRGF5bGlnaHQiXSwKICB1ZGFqZS5yb2FkX3R5cGUkdHJhZmZpY19kZW5zaXR5W3VkYWplLnJvYWRfdHlwZSRsaWdodGluZyA9PSAiTmlnaHQiXQopCgp0LnRlc3QudGFibGUgPC0gdGlkeSh0LnRlc3QucmVzdWx0KQoKdC50ZXN0LnRhYmxlICU+JQogIGthYmxlKAogICAgZGlnaXRzID0gNCwKICAgIGNhcHRpb24gPSAidC10ZXN0IOKAkyBQb3Jvdm5hbmllIGh1c3RvdHkgcHJlbcOhdmt5OiBEZcWIIHZzLiBOb2MiCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKCmBgYAoKCiMjIyMgQU5PVkE6IEtvbnRyb2xhIMSNaSBzYSBsw63FoWkgaHVzdG90dSBwcmVtw6F2a3kgcG9kxL5hIHBvxI1hc2lhCgpgYGB7cn0KbGlicmFyeShicm9vbSkKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQoKYW5vdmEucmVzdWx0IDwtIGFvdih0cmFmZmljX2RlbnNpdHkgfiB3ZWF0aGVyX2NvbmRpdGlvbiwgZGF0YSA9IHVkYWplLnJvYWRfdHlwZSkKCmFub3ZhLnRhYmxlIDwtIHRpZHkoYW5vdmEucmVzdWx0KQoKYW5vdmEudGFibGUgJT4lCiAga2FibGUoCiAgICBkaWdpdHMgPSA0LAogICAgY2FwdGlvbiA9ICJBTk9WQSDigJMgVnBseXYgcG/EjWFzaWEgbmEgaHVzdG90dSBwcmVtw6F2a3kiCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKCgoKYGBgCgojIyMjIExpbmVhciBSZWdyZXNzaW9uOiBQcmVkaWtjaWEgcsO9Y2hsb3N0aSDDoXV0CgpgYGB7cn0KbGlicmFyeShicm9vbSkKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGRwbHlyKQoKbW9kZWwgPC0gbG0oYXZnX3NwZWVkIH4gdHJhZmZpY19kZW5zaXR5ICsgbnVtX2xhbmVzICsgd2VhdGhlcl9jb25kaXRpb24sCiAgICAgICAgICAgIGRhdGEgPSB1ZGFqZS5yb2FkX3R5cGUpCgptb2RlbC50YWJsZSA8LSB0aWR5KG1vZGVsKSAlPiUKICBtdXRhdGUoCiAgICBlc3RpbWF0ZSAgPSByb3VuZChlc3RpbWF0ZSwgMyksCiAgICBzdGQuZXJyb3IgPSByb3VuZChzdGQuZXJyb3IsIDMpLAogICAgc3RhdGlzdGljID0gcm91bmQoc3RhdGlzdGljLCAyKSwKICAgIHAudmFsdWUgICA9IHJvdW5kKHAudmFsdWUsIDQpCiAgKQoKbW9kZWwudGFibGUgJT4lCiAga2FibGUoCiAgICBjYXB0aW9uID0gIlJlZ3Jlc27DvSBtb2RlbCDigJMgZmFrdG9yeSBvdnBseXbFiHVqw7pjZSBwcmllbWVybsO6IHLDvWNobG9zxaUiCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKCmBgYAoKCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKGMoImJyb29tIiwgImthYmxlRXh0cmEiLCAiZHBseXIiLCAic3RyaW5nciIpKQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoc3RyaW5ncikKCiMgWW91ciBtb2RlbCAoYWxyZWFkeSBmaXR0ZWQpCiMgbW9kZWwgPC0gbG0oRVNHLklOREVYIH4gUkVUVVJOLk9OLkFTU0VUUyArIEZJUk0uU0laRSArIERFQlQuVE8uQVNTRVQsIGRhdGEgPSB1ZGFqZS4yMDEzKQoKY29lZi50YmwgPC0gdGlkeShtb2RlbCwgY29uZi5pbnQgPSBUUlVFKSAlPiUKICBtdXRhdGUoCiAgICB0ZXJtID0gcmVjb2RlKHRlcm0sCiAgICAgICIoSW50ZXJjZXB0KSIgPSAiSW50ZXJjZXB0IiwKICAgICAgInRyYWZmaWNfZGVuc2l0eSIgPSAiVHJhZmZpYyBEZW5zaXR5IiwKICAgICAgIm51bV9sYW5lcyIgPSAiTnVtYmVyIG9mIExhbmVzIiwKICAgICAgIndlYXRoZXJfY29uZGl0aW9uUmFpbiIgPSAiV2VhdGhlcjogUmFpbiIsCiAgICAgICJ3ZWF0aGVyX2NvbmRpdGlvbkZvZ2d5IiA9ICJXZWF0aGVyOiBGb2dneSIKICAgICksCiAgICBzdGFycyA9IGNhc2Vfd2hlbigKICAgICAgcC52YWx1ZSA8IDAuMDAxIH4gIioqKiIsCiAgICAgIHAudmFsdWUgPCAwLjAxICB+ICIqKiIsCiAgICAgIHAudmFsdWUgPCAwLjA1ICB+ICIqIiwKICAgICAgcC52YWx1ZSA8IDAuMSAgIH4gIsK3IiwKICAgICAgVFJVRSAgICAgICAgICAgIH4gIiIKICAgICkKICApICU+JQogIHRyYW5zbXV0ZSgKICAgIFRlcm0gPSB0ZXJtLAogICAgRXN0aW1hdGUgPSBlc3RpbWF0ZSwKICAgIGBTdGQuIEVycm9yYCA9IHN0ZC5lcnJvciwKICAgIGB0IHZhbHVlYCA9IHN0YXRpc3RpYywKICAgIGBwIHZhbHVlYCA9IHAudmFsdWUsCiAgICBgOTUlIENJYCA9IHN0cl9jKCJbIiwgcm91bmQoY29uZi5sb3csIDMpLCAiLCAiLCByb3VuZChjb25mLmhpZ2gsIDMpLCAiXSIpLAogICAgU2lnID0gc3RhcnMKICApCgpjb2VmLnRibCAlPiUKICBrYWJsZSgKICAgIGRpZ2l0cyA9IDMsCiAgICBjYXB0aW9uID0gIk9MUyBSZWdyZXNzaW9uIENvZWZmaWNpZW50cyAoYXZnX3NwZWVkIH4gdHJhZmZpY19kZW5zaXR5ICsgbnVtX2xhbmVzICsgd2VhdGhlcl9jb25kaXRpb24pIgogICkgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JQogIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFLCBiYWNrZ3JvdW5kID0gIiNmMmYyZjIiKSAlPiUKICBmb290bm90ZSgKICAgIGdlbmVyYWwgPSAiU2lnbmlmLiBjb2RlczogKioqIHA8MC4wMDEsICoqIHA8MC4wMSwgKiBwPDAuMDUsIMK3IHA8MC4xLiIsCiAgICB0aHJlZXBhcnR0YWJsZSA9IFRSVUUKICApCmBgYAoKYGBge3J9CmZpdC50YmwgPC0gZ2xhbmNlKG1vZGVsKSAlPiUKICB0cmFuc211dGUoCiAgICBgUi1zcXVhcmVkYCAgICAgID0gci5zcXVhcmVkLAogICAgYEFkai4gUi1zcXVhcmVkYCA9IGFkai5yLnNxdWFyZWQsCiAgICBgRi1zdGF0aXN0aWNgICAgID0gc3RhdGlzdGljLAogICAgYEYgcC12YWx1ZWAgICAgICA9IHAudmFsdWUsCiAgICBgQUlDYCAgICAgICAgICAgID0gQUlDLAogICAgYEJJQ2AgICAgICAgICAgICA9IEJJQywKICAgIGBOdW0uIG9icy5gICAgICAgPSBub2JzCiAgKQoKIyA077iP4oOjIFZ5a3Jlc2xpIHRhYnXEvmt1IHMgcG9waXNvbQpmaXQudGJsICU+JQogIGthYmxlKAogICAgZGlnaXRzID0gMywKICAgIGNhcHRpb24gPSAiTW9kZWwgRml0IFN0YXRpc3RpY3MgKGF2Z19zcGVlZCB+IHRyYWZmaWNfZGVuc2l0eSArIG51bV9sYW5lcyArIHdlYXRoZXJfY29uZGl0aW9uKSIKICApICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBib290c3RyYXBfb3B0aW9ucyA9IGMoImNvbmRlbnNlZCIpKQogICAgCmBgYAoKCgoKCgojIEluZm8gemRyb2plIHByZSDEj2FsxaFpZSDFoXTDumRpdW0KCiMjIyMgUiBQcm9qZWN0CgotICAgW1IgUHJvamVjdCBIb21lcGFnZV0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pIC0gQmFzZSBSIGRvd25sb2FkcywKICAgIG5ld3MsIGFuZCBsZWFybmluZyByZXNvdXJjZXMKCiMjIyMgUG9zaXQKCi0gICBbUiBTdHVkaW8gRGVza3RvcF0oaHR0cHM6Ly9wb3NpdC5jby9kb3dubG9hZC9yc3R1ZGlvLWRlc2t0b3AvKSAtCiAgICBGZWF0dXJlIHJpY2ggZW52aXJvbm1lbnQgZm9yIHdvcmtpbmcgd2l0aCBkYXRhIGluIFIKLSAgIFtSIGZvciBEYXRhIFNjaWVuY2VdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKSAtIENvbXByZWhlbnNpdmUgb25saW5lCiAgICBib29rCi0gICBbUlN0dWRpbwogICAgQ2hlYXRzaGVldHNdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pIC0gUXVpY2sKICAgIHJlZmVyZW5jZSBndWlkZXMKCiMjIyMgQ29tbXVuaXR5IFJlc291cmNlcwoKLSAgIFtSLWJsb2dnZXJzXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8pIC0gQmxvZyBhZ2dyZWdhdG9yIGZvciBSCiAgICBuZXdzIGFuZCB0dXRvcmlhbHMKLSAgIFtTdGFjayBPdmVyZmxvdyAtIFIKICAgIHRhZ10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL3IpIC0gUSZBIGNvbW11bml0eQotICAgW3JkcnIuaW8gU25pcHBldHNdKGh0dHBzOi8vcmRyci5pby9zbmlwcGV0cy8pIC0gVGVzdCBSIGNvZGUgc25pcHBldHMKICAgIG9ubGluZQotICAgW0NvdXJzZXJhIC0gUgogICAgUHJvZ3JhbW1pbmddKGh0dHBzOi8vd3d3LmNvdXJzZXJhLm9yZy9sZWFybi9yLXByb2dyYW1taW5nKSAtIE9ubGluZQogICAgY291cnNlCi0gICBbRGF0YUNhbXAgLSBJbnRyb2R1Y3Rpb24gdG8KICAgIFJdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2ZyZWUtaW50cm9kdWN0aW9uLXRvLXIpIC0KICAgIEludGVyYWN0aXZlIGxlYXJuaW5nIHBsYXRmb3JtCgo=