Čistenie údajov

Predpokladajme, že v názvoch stĺpcov v pôvodnom súbore máme medzery, teda Náhľad na xls databázu otvorenú v tabuľkovom procesore

Tu sa v názvoch stĺpcov vyskytujú medzery. Názvy stĺpcov sa v prostredí R stávajú názvami premenných a tie nesmú byť súčasťou názvu premennej. Neprípustné znaky v názvoch premenných vo všeobecnosti môžeme nahradiť s pomocou knižnice janitor.

Import údajov

Skontrolujeme si, či sa doplňujú na miesta chýbajúcich údajov doplňujú NA hodnoty (NA - Not Available).

# Import the CSV file into a data frame
# - header = TRUE: the first row contains variable names
# - sep = ";": variables are separated by semicolons
# - dec = ".": decimal numbers use a dot
# - na.strings = c("", "NA"): empty cells and text "NA" are treated as missing values
# - stringsAsFactors = FALSE: text variables remain text, not factors

udaje1 <- read.csv2(
  "udaje/ChybnaDatabaza.csv",
  header = TRUE,
  sep = ";",
  dec = ".",
  na.strings = c("", "NA"),
  stringsAsFactors = FALSE
)

# Show the first rows of the dataset
head(udaje1)
NA
NA

Upravovanie názvov premenných

# Load the dplyr package
# dplyr provides convenient tools for working with data frames
library(dplyr)

# -----------------------------
# 1. Save the original column names
# -----------------------------
old_names <- names(udaje1)

# -----------------------------
# 2. Shorten (abbreviate) column names
# -----------------------------
# rename_with() applies a function to all column names
# abbreviate() automatically shortens long names
# strict = FALSE allows a more flexible abbreviation
udaje1 <- udaje1 %>%
  rename_with(~ abbreviate(.x, strict = FALSE))

# -----------------------------
# 3. Ensure that column names are unique
# -----------------------------
# Sometimes abbreviation may create identical names
# make.unique() automatically adds suffixes (.1, .2, ...) if necessary
names(udaje1) <- make.unique(names(udaje1))

# -----------------------------
# 4. Show comparison: old vs. new names
# -----------------------------
comparison <- data.frame(
  Original_Name = old_names,
  Shortened_Name = names(udaje1)
)

print(comparison)
NA
NA

Počiatočné čistenie obsahu databázy, imputácia chýbahúcich údajov

Odporúčam tu použiť knižnice VIM, Amelia, mice a iné. Pokiaľ máme databázu dostatočne nekonzistentnú a nevieme ju upraviť vynechaním niekoľkých riadkov / stĺpcov, potom odporúčame blog M. Fatih Tüzen: Handling Missing Data in R: A Comprehensive Guide, R bloggers.

E3te raz si pozrime našu pôvodnú databázu s chýbajúcimi údajmi:

Databáza s chýbajúcimi údajmi
Databáza s chýbajúcimi údajmi
library(mice)
library(VIM)

# Count missing values in each column
print("pocet chybajucich udajov za jednotlive premenne")
[1] "pocet chybajucich udajov za jednotlive premenne"
colSums(is.na(udaje1))
YEAR COMP EXCH PRIM TOBI MARK RETU DEBT FIRM SOCI ENVI GOVE ESG. 
   1    0    0    1    0    1    0    1    1    1    1    1    0 

Štatistika vyššie nám hovorí, koľko NA má ktorý stĺpec databázy. Ďalšie riadky nám hovoria o štruktúre záznamov, kde sa nachádzajú chýbajúce hodnoty. Posledný riadok hovorí o počte chýbajúcich údajov za jednotlivé premenné a za celú databázu. Máme 760 záznamov , z kotých 754 je úplných a mámo 8 chýbajúcich hodnôt. Podbnú informáciu nám dáva nasledovný graf.

# pattern of missingness
md.pattern(udaje1)
    COMP EXCH TOBI RETU ESG. YEAR PRIM MARK DEBT FIRM SOCI ENVI GOVE  
754    1    1    1    1    1    1    1    1    1    1    1    1    1 0
1      1    1    1    1    1    1    1    1    1    1    1    0    1 1
1      1    1    1    1    1    1    1    1    1    0    1    1    1 1
1      1    1    1    1    1    1    1    1    0    1    0    1    0 3
1      1    1    1    1    1    1    1    0    1    1    1    1    1 1
1      1    1    1    1    1    1    0    1    1    1    1    1    1 1
1      1    1    1    1    1    0    1    1    1    1    1    1    1 1
       0    0    0    0    0    1    1    1    1    1    1    1    1 8

# visualize missing data
aggr(udaje1, bars=FALSE,col=c('navyblue','red'), numbers=TRUE, sortVars=TRUE)  # cervena farba signalizuje chybahuce polozky

 Variables sorted by number of missings: 

# multiple imputation - v pripade, ak vam chyba mensi rozsah udajov
imp <- mice(udaje1, seed=123)   # konkretne parametre imputacie vieme nastavovat - pozri help

 iter imp variable
  1   1  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  1   2  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  1   3  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  1   4  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  1   5  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  2   1  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  2   2  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  2   3  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  2   4  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  2   5  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  3   1  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  3   2  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  3   3  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  3   4  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  3   5  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  4   1  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  4   2  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  4   3  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  4   4  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  4   5  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  5   1  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  5   2  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  5   3  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  5   4  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
  5   5  YEAR  MARK  DEBT  FIRM  SOCI  ENVI  GOVE
udaje_imputovane <- complete(imp, 1)
udaje1 <- udaje_imputovane
head(udaje1)
rm(imp)
rm(udaje_imputovane)
print("pocet chybajucich udajov za jednotlive premenne")
[1] "pocet chybajucich udajov za jednotlive premenne"
colSums(is.na(udaje1))
YEAR COMP EXCH PRIM TOBI MARK RETU DEBT FIRM SOCI ENVI GOVE ESG. 
   0    0    0    1    0    0    0    0    0    0    0    0    0 

Celkove nám teda ostala nevyplnená jedna premenná - textová - ktorá označuje Primary Business referencovanej firmy

Tabuľky, grafy, jednoduché štatistiky

Grafy

ggplot2 - knižnica pre grafy

Výber a následné triedenie

library(dplyr)

udaje.2013 <- udaje1 %>%
  filter(YEAR == 2013) %>%
  dplyr::select(RETU, ESG., DEBT, FIRM)

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.2013, aes(x = FIRM, y = ESG.)) +            # specifikacia osi
  geom_point() +                                                   # typ grafu - scatterplot
  theme_minimal() +
  labs(title = "ESG index", x = "Veľkosť firmy", y = "Score")      # oznacenie osi

Boxplot

# Bar plot with grouping
library(ggplot2)

library(ggplot2)

ggplot(udaje1, aes(x = factor(YEAR), y = ESG.)) +        # specifikacia osi
  geom_boxplot(fill = "lightblue", color = "darkblue") +      # typ grafu - boxplot
  labs(                                                       # oznacenie osi, nazov grafu
    title = "ESG Index by YEAR",
    x = "Year",
    y = "ESG Index"
  ) +
  theme_minimal()

Základné štatistiky.

knitr - tabuľka

library(dplyr)
library(knitr)

# Summarise basic statistics
esg.stats <- udaje1 %>%
  filter(YEAR %in% 2013:2016) %>%
  group_by(YEAR) %>%
  summarise(
    n     = n(),
    mean  = mean(ESG., na.rm = TRUE),
    sd    = sd(ESG., na.rm = TRUE),
    min   = min(ESG., na.rm = TRUE),
    q25   = quantile(ESG., 0.25, na.rm = TRUE),
    median= median(ESG., na.rm = TRUE),
    q75   = quantile(ESG., 0.75, na.rm = TRUE),
    max   = max(ESG., na.rm = TRUE),
    .groups = "drop"
  )

# Create knitr table
kable(esg.stats, digits = 2, caption = "Basic statistics of ESG Index (2013–2016)")
Basic statistics of ESG Index (2013–2016)
YEAR n mean sd min q25 median q75 max
2013 76 0.23 0.10 0 0.15 0.22 0.29 0.58
2014 76 0.24 0.11 0 0.16 0.24 0.31 0.57
2015 76 0.25 0.12 0 0.17 0.25 0.31 0.65
2016 75 0.26 0.12 0 0.17 0.27 0.32 0.64

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

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

# Summarise basic statistics
esg.stats <- udaje1 %>%
  filter(YEAR %in% 2013:2016) %>%
  group_by(YEAR) %>%
  summarise(
    n      = n(),
    mean   = mean(ESG., na.rm = TRUE),
    sd     = sd(ESG., na.rm = TRUE),
    min    = min(ESG., na.rm = TRUE),
    q25    = quantile(ESG., 0.25, na.rm = TRUE),
    median = median(ESG., na.rm = TRUE),
    q75    = quantile(ESG., 0.75, na.rm = TRUE),
    max    = max(ESG., na.rm = TRUE),
    .groups = "drop"
  )

# Create styled kableExtra table
esg.stats %>%
  kable(digits = 2, caption = "Basic statistics of ESG Index (2013–2016)") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed")) %>%
  column_spec(1, bold = TRUE) %>%          # make YEAR bold
  row_spec(0, bold = TRUE, background = "#f2f2f2") %>%  # style header row
  add_header_above(c(" " = 2, "ESG Index Statistics" = 7))
Basic statistics of ESG Index (2013–2016)
ESG Index Statistics
YEAR n mean sd min q25 median q75 max
2013 76 0.23 0.10 0 0.15 0.22 0.29 0.58
2014 76 0.24 0.11 0 0.16 0.24 0.31 0.57
2015 76 0.25 0.12 0 0.17 0.25 0.31 0.65
2016 75 0.26 0.12 0 0.17 0.27 0.32 0.64

Testovanie hypotéz

t-test: Porovnanie priemeru ESG indexu v rokoch 2013 a 2015

t.test.result <- t.test(
  udaje1$ESG.[udaje1$YEAR == 2013],
  udaje1$ESG.[udaje1$YEAR == 2015]
)

print(t.test.result)

    Welch Two Sample t-test

data:  udaje1$ESG.[udaje1$YEAR == 2013] and udaje1$ESG.[udaje1$YEAR == 2015]
t = -1.2984, df = 147.86, p-value = 0.1962
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -0.05906743  0.01222533
sample estimates:
mean of x mean of y 
0.2276316 0.2510526 

ANOVA: Comparing Reading Scores Across Programs

anova.result <- aov(ESG. ~ YEAR, data = udaje1)
summary(anova.result)
             Df Sum Sq Mean Sq F value   Pr(>F)    
YEAR          1  0.617  0.6169   29.88 6.25e-08 ***
Residuals   758 15.651  0.0206                     
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Linear Regression: Predicting Math Scores

model <- lm(ESG. ~ RETU + FIRM + DEBT, data = udaje.2013)
summary(model)

Call:
lm(formula = ESG. ~ RETU + FIRM + DEBT, data = udaje.2013)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.189450 -0.054050 -0.001079  0.049273  0.237250 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -3.080e-01  8.210e-02  -3.751 0.000354 ***
RETU         1.668e-04  9.115e-05   1.830 0.071345 .  
FIRM         8.080e-02  1.136e-02   7.115 6.78e-10 ***
DEBT        -6.201e-04  4.205e-04  -1.475 0.144688    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.08095 on 72 degrees of freedom
Multiple R-squared:  0.4217,    Adjusted R-squared:  0.3976 
F-statistic:  17.5 on 3 and 72 DF,  p-value: 1.24e-08
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIMSMaXN0ZW5pZSDDumRham92CgpQcmVkcG9rbGFkYWptZSwgxb5lIHYgbsOhenZvY2ggc3TEunBjb3YgdiBww7R2b2Rub20gc8O6Ym9yZSBtw6FtZSBtZWR6ZXJ5LCB0ZWRhCiFbTsOhaMS+YWQgbmEgeGxzIGRhdGFiw6F6dSBvdHZvcmVuw7ogdiB0YWJ1xL5rb3ZvbSBwcm9jZXNvcmVdKG9icmF6a3kvQ2h5Ym5hX3hscy5qcGcpe3dpZHRoPTEwMCV9CgoKClR1IHNhIHYgbsOhenZvY2ggc3TEunBjb3Ygdnlza3l0dWrDuiBtZWR6ZXJ5LiBOw6F6dnkgc3TEunBjb3Ygc2EgdiBwcm9zdHJlZMOtIFIgc3TDoXZhasO6IG7DoXp2YW1pIHByZW1lbm7DvWNoIGEgdGllIG5lc23DuiBiecWlIHPDusSNYXPFpW91IG7DoXp2dSBwcmVtZW5uZWouIE5lcHLDrXB1c3Ruw6kgem5ha3kgdiBuw6F6dm9jaCBwcmVtZW5uw71jaCB2byB2xaFlb2JlY25vc3RpIG3DtMW+ZW1lIG5haHJhZGnFpSBzIHBvbW9jb3Uga25pxb5uaWNlIF9qYW5pdG9yXy4KCiMjIyBJbXBvcnQgw7pkYWpvdgoKU2tvbnRyb2x1amVtZSBzaSwgxI1pIHNhIGRvcGzFiHVqw7ogbmEgbWllc3RhIGNow71iYWrDumNpY2ggw7pkYWpvdiBkb3BsxYh1asO6ICBOQSBob2Rub3R5IChOQSAtIE5vdCBBdmFpbGFibGUpLgoKYGBge3J9CiMgSW1wb3J0IHRoZSBDU1YgZmlsZSBpbnRvIGEgZGF0YSBmcmFtZQojIC0gaGVhZGVyID0gVFJVRTogdGhlIGZpcnN0IHJvdyBjb250YWlucyB2YXJpYWJsZSBuYW1lcwojIC0gc2VwID0gIjsiOiB2YXJpYWJsZXMgYXJlIHNlcGFyYXRlZCBieSBzZW1pY29sb25zCiMgLSBkZWMgPSAiLiI6IGRlY2ltYWwgbnVtYmVycyB1c2UgYSBkb3QKIyAtIG5hLnN0cmluZ3MgPSBjKCIiLCAiTkEiKTogZW1wdHkgY2VsbHMgYW5kIHRleHQgIk5BIiBhcmUgdHJlYXRlZCBhcyBtaXNzaW5nIHZhbHVlcwojIC0gc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFOiB0ZXh0IHZhcmlhYmxlcyByZW1haW4gdGV4dCwgbm90IGZhY3RvcnMKCnVkYWplMSA8LSByZWFkLmNzdjIoCiAgInVkYWplL0NoeWJuYURhdGFiYXphLmNzdiIsCiAgaGVhZGVyID0gVFJVRSwKICBzZXAgPSAiOyIsCiAgZGVjID0gIi4iLAogIG5hLnN0cmluZ3MgPSBjKCIiLCAiTkEiKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBTaG93IHRoZSBmaXJzdCByb3dzIG9mIHRoZSBkYXRhc2V0CmhlYWQodWRhamUxKQoKCmBgYAoKIyMjIFVwcmF2b3ZhbmllIG7DoXp2b3YgcHJlbWVubsO9Y2gKCmBgYHtyfQojIExvYWQgdGhlIGRwbHlyIHBhY2thZ2UKIyBkcGx5ciBwcm92aWRlcyBjb252ZW5pZW50IHRvb2xzIGZvciB3b3JraW5nIHdpdGggZGF0YSBmcmFtZXMKbGlicmFyeShkcGx5cikKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxLiBTYXZlIHRoZSBvcmlnaW5hbCBjb2x1bW4gbmFtZXMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpvbGRfbmFtZXMgPC0gbmFtZXModWRhamUxKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDIuIFNob3J0ZW4gKGFiYnJldmlhdGUpIGNvbHVtbiBuYW1lcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgcmVuYW1lX3dpdGgoKSBhcHBsaWVzIGEgZnVuY3Rpb24gdG8gYWxsIGNvbHVtbiBuYW1lcwojIGFiYnJldmlhdGUoKSBhdXRvbWF0aWNhbGx5IHNob3J0ZW5zIGxvbmcgbmFtZXMKIyBzdHJpY3QgPSBGQUxTRSBhbGxvd3MgYSBtb3JlIGZsZXhpYmxlIGFiYnJldmlhdGlvbgp1ZGFqZTEgPC0gdWRhamUxICU+JQogIHJlbmFtZV93aXRoKH4gYWJicmV2aWF0ZSgueCwgc3RyaWN0ID0gRkFMU0UpKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDMuIEVuc3VyZSB0aGF0IGNvbHVtbiBuYW1lcyBhcmUgdW5pcXVlCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTb21ldGltZXMgYWJicmV2aWF0aW9uIG1heSBjcmVhdGUgaWRlbnRpY2FsIG5hbWVzCiMgbWFrZS51bmlxdWUoKSBhdXRvbWF0aWNhbGx5IGFkZHMgc3VmZml4ZXMgKC4xLCAuMiwgLi4uKSBpZiBuZWNlc3NhcnkKbmFtZXModWRhamUxKSA8LSBtYWtlLnVuaXF1ZShuYW1lcyh1ZGFqZTEpKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDQuIFNob3cgY29tcGFyaXNvbjogb2xkIHZzLiBuZXcgbmFtZXMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpjb21wYXJpc29uIDwtIGRhdGEuZnJhbWUoCiAgT3JpZ2luYWxfTmFtZSA9IG9sZF9uYW1lcywKICBTaG9ydGVuZWRfTmFtZSA9IG5hbWVzKHVkYWplMSkKKQoKcHJpbnQoY29tcGFyaXNvbikKCgpgYGAKCgoKIyMjIyBQb8SNaWF0b8SNbsOpIMSNaXN0ZW5pZSBvYnNhaHUgZGF0YWLDoXp5LCBpbXB1dMOhY2lhIGNow71iYWjDumNpY2ggw7pkYWpvdiAKCk9kcG9yw7rEjWFtIHR1IHBvdcW+acWlIGtuacW+bmljZSBWSU0sIEFtZWxpYSwgbWljZSBhIGluw6kuIFBva2lhxL4gbcOhbWUgZGF0YWLDoXp1IGRvc3RhdG/EjW5lIG5la29uemlzdGVudG7DuiBhIG5ldmllbWUganUgdXByYXZpxaUgdnluZWNoYW7DrW0gbmlla2/EvmvDvWNoIHJpYWRrb3YgLyBzdMS6cGNvdiwgcG90b20gb2Rwb3LDusSNYW1lIGJsb2cgW00uIEZhdGloIFTDvHplbjogSGFuZGxpbmcgTWlzc2luZyBEYXRhIGluIFI6IEEgQ29tcHJlaGVuc2l2ZSBHdWlkZSwgUiBibG9nZ2Vyc10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vMjAyNS8wOC9oYW5kbGluZy1taXNzaW5nLWRhdGEtaW4tci1hLWNvbXByZWhlbnNpdmUtZ3VpZGUvKS4KCkUzdGUgcmF6IHNpIHBvenJpbWUgbmHFoXUgcMO0dm9kbsO6IGRhdGFiw6F6dSBzIGNow71iYWrDumNpbWkgw7pkYWptaToKCiFbRGF0YWLDoXphIHMgY2jDvWJhasO6Y2ltaSDDumRham1pXShvYnJhemt5L0NoeWJuYV94bHMuanBnKXt3aWR0aD02MCV9CgoKYGBge3J9CmxpYnJhcnkobWljZSkKbGlicmFyeShWSU0pCgojIENvdW50IG1pc3NpbmcgdmFsdWVzIGluIGVhY2ggY29sdW1uCnByaW50KCJwb2NldCBjaHliYWp1Y2ljaCB1ZGFqb3YgemEgamVkbm90bGl2ZSBwcmVtZW5uZSIpCmNvbFN1bXMoaXMubmEodWRhamUxKSkKYGBgCgrFoHRhdGlzdGlrYSB2ecWhxaFpZSBuw6FtIGhvdm9yw60sIGtvxL5rbyBOQSBtw6Ega3RvcsO9IHN0xLpwZWMgZGF0YWLDoXp5LiDEjmFsxaFpZSByaWFka3kgbsOhbSBob3ZvcmlhIG8gxaF0cnVrdMO6cmUgesOhem5hbW92LCBrZGUgc2EgbmFjaMOhZHphasO6IGNow71iYWrDumNlIGhvZG5vdHkuIFBvc2xlZG7DvSByaWFkb2sgaG92b3LDrSBvIHBvxI10ZSBjaMO9YmFqw7pjaWNoIMO6ZGFqb3YgemEgamVkbm90bGl2w6kgcHJlbWVubsOpIGEgemEgY2Vsw7ogZGF0YWLDoXp1LiBNw6FtZSA3NjAgesOhem5hbW92ICwgeiBrb3TDvWNoIDc1NCBqZSDDunBsbsO9Y2ggYSBtw6FtbyA4IGNow71iYWrDumNpY2ggaG9kbsO0dC4gUG9kYm7DuiBpbmZvcm3DoWNpdSBuw6FtIGTDoXZhIG5hc2xlZG92bsO9IGdyYWYuCgpgYGB7cn0KIyBwYXR0ZXJuIG9mIG1pc3NpbmduZXNzCm1kLnBhdHRlcm4odWRhamUxKQoKIyB2aXN1YWxpemUgbWlzc2luZyBkYXRhCmFnZ3IodWRhamUxLCBiYXJzPUZBTFNFLGNvbD1jKCduYXZ5Ymx1ZScsJ3JlZCcpLCBudW1iZXJzPVRSVUUsIHNvcnRWYXJzPVRSVUUpICAjIGNlcnZlbmEgZmFyYmEgc2lnbmFsaXp1amUgY2h5YmFodWNlIHBvbG96a3kKCiMgbXVsdGlwbGUgaW1wdXRhdGlvbiAtIHYgcHJpcGFkZSwgYWsgdmFtIGNoeWJhIG1lbnNpIHJvenNhaCB1ZGFqb3YKaW1wIDwtIG1pY2UodWRhamUxLCBzZWVkPTEyMykgICAjIGtvbmtyZXRuZSBwYXJhbWV0cmUgaW1wdXRhY2llIHZpZW1lIG5hc3Rhdm92YXQgLSBwb3pyaSBoZWxwCnVkYWplX2ltcHV0b3ZhbmUgPC0gY29tcGxldGUoaW1wLCAxKQp1ZGFqZTEgPC0gdWRhamVfaW1wdXRvdmFuZQpoZWFkKHVkYWplMSkKcm0oaW1wKQpybSh1ZGFqZV9pbXB1dG92YW5lKQpwcmludCgicG9jZXQgY2h5YmFqdWNpY2ggdWRham92IHphIGplZG5vdGxpdmUgcHJlbWVubmUiKQpjb2xTdW1zKGlzLm5hKHVkYWplMSkpCgpgYGAKQ2Vsa292ZSBuw6FtIHRlZGEgb3N0YWxhIG5ldnlwbG5lbsOhIGplZG5hIHByZW1lbm7DoSAtIHRleHRvdsOhIC0ga3RvcsOhIG96bmHEjXVqZSBQcmltYXJ5IEJ1c2luZXNzIHJlZmVyZW5jb3ZhbmVqIGZpcm15CgojICAgVGFidcS+a3ksIGdyYWZ5LCBqZWRub2R1Y2jDqSDFoXRhdGlzdGlreQoKIyMgR3JhZnkKCgojIyMgZ2dwbG90MiAtIGtuacW+bmljYSBwcmUgZ3JhZnkKClbDvWJlciBhIG7DoXNsZWRuw6kgdHJpZWRlbmllCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQoKdWRhamUuMjAxMyA8LSB1ZGFqZTEgJT4lCiAgZmlsdGVyKFlFQVIgPT0gMjAxMykgJT4lCiAgZHBseXI6OnNlbGVjdChSRVRVLCBFU0cuLCBERUJULCBGSVJNKQpgYGAKCktuacW+bmljYSAuZ2dwbG90Mi4gamUgdiBzw7rEjWFzbm9zdGkgbmFqxI1hc3RlasWhaWUgcG91xb7DrXZhbsOhIGdyYWZpY2vDoSBrbmnFvm5pY2EsIHByacSNb20gcHJlZHByaXByYXZlbsOpIGvDs2R5IGsgamVkbm90bGl2w71tIG9icsOhemtvbSBzaSB2aWV0ZSBuw6Fqc8WlIHYgW1IgR3JhcGggR2FsbGVyeV0oaHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tLykuIFR1IHNpIHV2ZWRpZW1lIGplZG5vZHVjaMWhaWUgeiBuaWNoLgoKIyMjIyBTY2F0dGVyIHBsb3QKCmBgYHtyfQojIEJhc2ljIHNjYXR0ZXIgcGxvdApsaWJyYXJ5KGdncGxvdDIpCmdncGxvdCh1ZGFqZS4yMDEzLCBhZXMoeCA9IEZJUk0sIHkgPSBFU0cuKSkgKyAgICAgICAgICAgICMgc3BlY2lmaWthY2lhIG9zaQogIGdlb21fcG9pbnQoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0eXAgZ3JhZnUgLSBzY2F0dGVycGxvdAogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJFU0cgaW5kZXgiLCB4ID0gIlZlxL5rb3PFpSBmaXJteSIsIHkgPSAiU2NvcmUiKSAgICAgICMgb3puYWNlbmllIG9zaQpgYGAKCiMjIyMgQm94cGxvdAoKYGBge3J9CiMgQmFyIHBsb3Qgd2l0aCBncm91cGluZwpsaWJyYXJ5KGdncGxvdDIpCgpsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QodWRhamUxLCBhZXMoeCA9IGZhY3RvcihZRUFSKSwgeSA9IEVTRy4pKSArICAgICAgICAjIHNwZWNpZmlrYWNpYSBvc2kKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsgICAgICAjIHR5cCBncmFmdSAtIGJveHBsb3QKICBsYWJzKCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG96bmFjZW5pZSBvc2ksIG5hem92IGdyYWZ1CiAgICB0aXRsZSA9ICJFU0cgSW5kZXggYnkgWUVBUiIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJFU0cgSW5kZXgiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyBaw6FrbGFkbsOpIMWhdGF0aXN0aWt5LiAKCgojIyBrbml0ciAtIHRhYnXEvmthCgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShrbml0cikKCiMgU3VtbWFyaXNlIGJhc2ljIHN0YXRpc3RpY3MKZXNnLnN0YXRzIDwtIHVkYWplMSAlPiUKICBmaWx0ZXIoWUVBUiAlaW4lIDIwMTM6MjAxNikgJT4lCiAgZ3JvdXBfYnkoWUVBUikgJT4lCiAgc3VtbWFyaXNlKAogICAgbiAgICAgPSBuKCksCiAgICBtZWFuICA9IG1lYW4oRVNHLiwgbmEucm0gPSBUUlVFKSwKICAgIHNkICAgID0gc2QoRVNHLiwgbmEucm0gPSBUUlVFKSwKICAgIG1pbiAgID0gbWluKEVTRy4sIG5hLnJtID0gVFJVRSksCiAgICBxMjUgICA9IHF1YW50aWxlKEVTRy4sIDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICBtZWRpYW49IG1lZGlhbihFU0cuLCBuYS5ybSA9IFRSVUUpLAogICAgcTc1ICAgPSBxdWFudGlsZShFU0cuLCAwLjc1LCBuYS5ybSA9IFRSVUUpLAogICAgbWF4ICAgPSBtYXgoRVNHLiwgbmEucm0gPSBUUlVFKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgojIENyZWF0ZSBrbml0ciB0YWJsZQprYWJsZShlc2cuc3RhdHMsIGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiQmFzaWMgc3RhdGlzdGljcyBvZiBFU0cgSW5kZXggKDIwMTPigJMyMDE2KSIpCmBgYAoKYWxlYm8ga3JhasWhaWUgdGFidcS+a3kgcyBwb21vY291IC5rYWJsZUV4dHJhLjoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgojIFN1bW1hcmlzZSBiYXNpYyBzdGF0aXN0aWNzCmVzZy5zdGF0cyA8LSB1ZGFqZTEgJT4lCiAgZmlsdGVyKFlFQVIgJWluJSAyMDEzOjIwMTYpICU+JQogIGdyb3VwX2J5KFlFQVIpICU+JQogIHN1bW1hcmlzZSgKICAgIG4gICAgICA9IG4oKSwKICAgIG1lYW4gICA9IG1lYW4oRVNHLiwgbmEucm0gPSBUUlVFKSwKICAgIHNkICAgICA9IHNkKEVTRy4sIG5hLnJtID0gVFJVRSksCiAgICBtaW4gICAgPSBtaW4oRVNHLiwgbmEucm0gPSBUUlVFKSwKICAgIHEyNSAgICA9IHF1YW50aWxlKEVTRy4sIDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICBtZWRpYW4gPSBtZWRpYW4oRVNHLiwgbmEucm0gPSBUUlVFKSwKICAgIHE3NSAgICA9IHF1YW50aWxlKEVTRy4sIDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICBtYXggICAgPSBtYXgoRVNHLiwgbmEucm0gPSBUUlVFKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgojIENyZWF0ZSBzdHlsZWQga2FibGVFeHRyYSB0YWJsZQplc2cuc3RhdHMgJT4lCiAga2FibGUoZGlnaXRzID0gMiwgY2FwdGlvbiA9ICJCYXNpYyBzdGF0aXN0aWNzIG9mIEVTRyBJbmRleCAoMjAxM+KAkzIwMTYpIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JSAgICAgICAgICAjIG1ha2UgWUVBUiBib2xkCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGJhY2tncm91bmQgPSAiI2YyZjJmMiIpICU+JSAgIyBzdHlsZSBoZWFkZXIgcm93CiAgYWRkX2hlYWRlcl9hYm92ZShjKCIgIiA9IDIsICJFU0cgSW5kZXggU3RhdGlzdGljcyIgPSA3KSkKYGBgCgoKIyBUZXN0b3ZhbmllIGh5cG90w6l6CgojIyMjIHQtdGVzdDogUG9yb3ZuYW5pZSBwcmllbWVydSBFU0cgaW5kZXh1IHYgcm9rb2NoIDIwMTMgYSAyMDE1CgpgYGB7cn0KdC50ZXN0LnJlc3VsdCA8LSB0LnRlc3QoCiAgdWRhamUxJEVTRy5bdWRhamUxJFlFQVIgPT0gMjAxM10sCiAgdWRhamUxJEVTRy5bdWRhamUxJFlFQVIgPT0gMjAxNV0KKQoKcHJpbnQodC50ZXN0LnJlc3VsdCkKYGBgCgoKIyMjIyBBTk9WQTogQ29tcGFyaW5nIFJlYWRpbmcgU2NvcmVzIEFjcm9zcyBQcm9ncmFtcwoKYGBge3J9CmFub3ZhLnJlc3VsdCA8LSBhb3YoRVNHLiB+IFlFQVIsIGRhdGEgPSB1ZGFqZTEpCnN1bW1hcnkoYW5vdmEucmVzdWx0KQpgYGAKCiMjIyMgTGluZWFyIFJlZ3Jlc3Npb246IFByZWRpY3RpbmcgTWF0aCBTY29yZXMKCmBgYHtyfQptb2RlbCA8LSBsbShFU0cuIH4gUkVUVSArIEZJUk0gKyBERUJULCBkYXRhID0gdWRhamUuMjAxMykKc3VtbWFyeShtb2RlbCkKYGBgCgo=