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

Import údajov z .csv alebo .xls

udaje <- read.csv2("data.csv",sep = ",", header = TRUE)
colnames(udaje)
 [1] "Customer.ID"           "Purchase.Date"         "Product.Category"     
 [4] "Product.Price"         "Quantity"              "Total.Purchase.Amount"
 [7] "Payment.Method"        "Customer.Age"          "Returns"              
[10] "Customer.Name"         "Age"                   "Gender"               
[13] "Churn"                

Grafy

ggplot2 - knižnica pre grafy

Výber a následné triedenie

library(dplyr)

udaje.2020 <- udaje %>%
  filter(Purchase.Date == 2020) %>%
  select(Product.Category,Product.Price,Quantity,Total.Purchase.Amount,Customer.Age,Gender,Purchase.Date)

Scatter plot

library(ggplot2)
ggplot(udaje.2020, aes(x = Customer.Age, y = Total.Purchase.Amount, color = Gender)) +
  geom_point(alpha = 0.6, size = 2) +  
  labs(
    title = "Vzťah veku zákazníka a celkovej sumy nákupu v roku 2020",
    x = "Vek zákazníka",
    y = "Celková suma nákupu",
    color = "Pohlavie"
  ) +
  theme_minimal()      

Zákazníci pokrývajú široké vekové rozpätie- približne od 18 do 70 rokov. Počet nákupov je pomerne rovnomerne rozložený vo všetkých vekových kategóriách — žiadna skupina výrazne nedominuje. Väčšina nákupov sa pohybuje v dolnej polovici grafu (do približne 4000), čo naznačuje, že menšie nákupy sú oveľa častejšie. Červené (Female) a tyrkysové (Male) body sú rozmiestnené veľmi podobne. Muži aj ženy nakupujú v podobných objemoch naprieč vekovými kategóriami.

Boxplot

library(ggplot2)

library(ggplot2)

ggplot(udaje.2020, aes(x = `Product.Category`, y = `Total.Purchase.Amount`, fill = `Product.Category`)) +
  geom_boxplot() +
  labs(
    title = "Distribúcia celkovej sumy nákupu podľa kategórie produktu",
    x = "Kategória produktu",
    y = "Celková suma nákupu"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Distribúcia celkovej sumy nákupu je veľmi podobná naprieč všetkými kategóriami produktov (Books, Clothing, Electronics, Home). Neexistujú veľké rozdiely v mediáne ani v rozsahu nákupov, čo znamená, že žiadna kategória výrazne nevyniká v tom, koľko ľudia utrácajú.

Základné štatistiky.

knitr - tabuľka

library(dplyr)
library(knitr)


purchase.stats <- udaje %>%
  filter(Purchase.Date %in% 2020:2023) %>%   
  group_by(Purchase.Date) %>%
  summarise(
    n       = n(),
    mean    = mean(Total.Purchase.Amount, na.rm = TRUE),
    sd      = sd(Total.Purchase.Amount, na.rm = TRUE),
    min     = min(Total.Purchase.Amount, na.rm = TRUE),
    q25     = quantile(Total.Purchase.Amount, 0.25, na.rm = TRUE),
    median  = median(Total.Purchase.Amount, na.rm = TRUE),
    q75     = quantile(Total.Purchase.Amount, 0.75, na.rm = TRUE),
    max     = max(Total.Purchase.Amount, na.rm = TRUE),
    .groups = "drop"
  )

kable(purchase.stats, digits = 2, caption = "Základné štatistiky Total Purchase Amount podľa roku")
Základné štatistiky Total Purchase Amount podľa roku
Purchase.Date n mean sd min q25 median q75 max
2020 66473 2714.75 1445.93 108 1467 2707 3967.00 5345
2021 66295 2732.49 1440.76 100 1482 2737 3976.00 5350
2022 69732 2730.42 1442.80 101 1481 2731 3978.25 5350
2023 47500 2722.90 1441.92 100 1475 2717 3972.00 5350

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

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

 purchase.stats <- udaje %>%
  filter(Purchase.Date %in% 2020:2023) %>%   
  group_by(Purchase.Date) %>%
  summarise(
    n       = n(),
    mean    = mean(Total.Purchase.Amount, na.rm = TRUE),
    sd      = sd(Total.Purchase.Amount, na.rm = TRUE),
    min     = min(Total.Purchase.Amount, na.rm = TRUE),
    q25     = quantile(Total.Purchase.Amount, 0.25, na.rm = TRUE),
    median  = median(Total.Purchase.Amount, na.rm = TRUE),
    q75     = quantile(Total.Purchase.Amount, 0.75, na.rm = TRUE),
    max     = max(Total.Purchase.Amount, na.rm = TRUE),
    .groups = "drop"
  )


purchase.stats %>%
  kable(digits = 2, caption = "Základné štatistiky Total Purchase Amount podľa roku") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed")) %>%
  column_spec(1, bold = TRUE) %>%                 
  row_spec(0, bold = TRUE, background = "#f2f2f2") %>%  
  add_header_above(c(" " = 2, "Total Purchase Amount Statistics" = 7))
Základné štatistiky Total Purchase Amount podľa roku
Total Purchase Amount Statistics
Purchase.Date n mean sd min q25 median q75 max
2020 66473 2714.75 1445.93 108 1467 2707 3967.00 5345
2021 66295 2732.49 1440.76 100 1482 2737 3976.00 5350
2022 69732 2730.42 1442.80 101 1481 2731 3978.25 5350
2023 47500 2722.90 1441.92 100 1475 2717 3972.00 5350

Testovanie hypotéz

t.test.result <- t.test(
  udaje$Total.Purchase.Amount[udaje$Purchase.Date == 2020],
  udaje$Total.Purchase.Amount[udaje$Purchase.Date == 2021]
)

print(t.test.result)

    Welch Two Sample t-test

data:  udaje$Total.Purchase.Amount[udaje$Purchase.Date == 2020] and udaje$Total.Purchase.Amount[udaje$Purchase.Date == 2021]
t = -2.2399, df = 132766, p-value = 0.0251
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -33.272512  -2.217213
sample estimates:
mean of x mean of y 
 2714.745  2732.490 

ANOVA: Comparing Reading Scores Across Programs

anova.result <- aov(Total.Purchase.Amount ~ factor(Purchase.Date), data = udaje)
summary(anova.result)
                          Df    Sum Sq Mean Sq F value Pr(>F)
factor(Purchase.Date)      3 1.293e+07 4310257    2.07  0.102
Residuals             249996 5.205e+11 2082031               

Linear Regression: Predicting Math Scores

model <- lm(Total.Purchase.Amount ~ Product.Price + Customer.Age + Quantity, data = udaje)

summary(model)

Call:
lm(formula = Total.Purchase.Amount ~ Product.Price + Customer.Age + 
    Quantity, data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-2503.19 -1245.94     0.78  1248.80  2502.01 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)   2517.10048   11.87068 212.044   <2e-16 ***
Product.Price   -0.02177    0.02036  -1.069    0.285    
Customer.Age     4.87284    0.18775  25.954   <2e-16 ***
Quantity        -0.10037    2.03719  -0.049    0.961    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1441 on 249996 degrees of freedom
Multiple R-squared:  0.002693,  Adjusted R-squared:  0.002681 
F-statistic:   225 on 3 and 249996 DF,  p-value: < 2.2e-16
library(broom)
library(dplyr)
library(kableExtra)
library(stringr)

model <- lm(Total.Purchase.Amount ~ Product.Price + Customer.Age + Quantity, data = udaje)

coef.tbl <- tidy(model, conf.int = TRUE) %>%
  mutate(
    term = recode(term,
      "(Intercept)" = "Intercept",
      "Product.Price" = "Product Price",
      "Customer.Age" = "Customer Age",
      "Quantity" = "Quantity"
    ),
    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 (Total Purchase Amount ~ Product Price + Customer Age + Quantity)"
  ) %>%
  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 (Total Purchase Amount ~ Product Price + Customer Age + Quantity)
Term Estimate Std. Error t value p value 95% CI Sig
Intercept 2517.100 11.871 212.044 0.000 [2493.834, 2540.367] ***
Product Price -0.022 0.020 -1.069 0.285 [-0.062, 0.018]
Customer Age 4.873 0.188 25.954 0.000 [4.505, 5.241] ***
Quantity -0.100 2.037 -0.049 0.961 [-4.093, 3.892]
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
  )

fit.tbl %>%
  kable(digits = 3, caption = "Model Fit Statistics") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("condensed"))
Model Fit Statistics
R-squared Adj. R-squared F-statistic F p-value AIC BIC Num. obs.
0.003 0.003 224.989 0 4346021 4346073 250000
LS0tCnRpdGxlOiAiUHLDoWNhIHMgZGF0YWLDoXpvdSAtIGltcG9ydCDDumRham92LCBncmFmeSwgxaF0YXRpc3Rpa3kiCmF1dGhvcjogIk1pcmlhbWEgxaBLdWxjb3bDoSAgPGJyPgoocyB2eXXFvml0w61tIHZlcmVqbmUgZG9zdHVwbsO9Y2gga8OzZG92IGEgQ2hhdEdQVCkiCmRhdGU6ICJTZXB0ZW1iZXIgMjAyNSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIGhpZ2hsaWdodDogdGFuZ28KZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKYGBge3J9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICAgIGVjaG8gPSBUUlVFLAogICAgbWVzc2FnZSA9IEZBTFNFLAogICAgd2FybmluZyA9IEZBTFNFCikKYGBgCgojIyBJbXBvcnQgw7pkYWpvdiB6IC5jc3YgYWxlYm8gLnhscwoKYGBge3J9CnVkYWplIDwtIHJlYWQuY3N2MigiZGF0YS5jc3YiLHNlcCA9ICIsIiwgaGVhZGVyID0gVFJVRSkKY29sbmFtZXModWRhamUpCmBgYAoKIyBHcmFmeQoKCiMjIyBnZ3Bsb3QyIC0ga25pxb5uaWNhIHByZSBncmFmeQoKVsO9YmVyIGEgbsOhc2xlZG7DqSB0cmllZGVuaWUKYGBge3J9CmxpYnJhcnkoZHBseXIpCgp1ZGFqZS4yMDIwIDwtIHVkYWplICU+JQogIGZpbHRlcihQdXJjaGFzZS5EYXRlID09IDIwMjApICU+JQogIHNlbGVjdChQcm9kdWN0LkNhdGVnb3J5LFByb2R1Y3QuUHJpY2UsUXVhbnRpdHksVG90YWwuUHVyY2hhc2UuQW1vdW50LEN1c3RvbWVyLkFnZSxHZW5kZXIsUHVyY2hhc2UuRGF0ZSkKYGBgCgojIyMjIFNjYXR0ZXIgcGxvdAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KHVkYWplLjIwMjAsIGFlcyh4ID0gQ3VzdG9tZXIuQWdlLCB5ID0gVG90YWwuUHVyY2hhc2UuQW1vdW50LCBjb2xvciA9IEdlbmRlcikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC42LCBzaXplID0gMikgKyAgCiAgbGFicygKICAgIHRpdGxlID0gIlZ6xaVhaCB2ZWt1IHrDoWthem7DrWthIGEgY2Vsa292ZWogc3VteSBuw6FrdXB1IHYgcm9rdSAyMDIwIiwKICAgIHggPSAiVmVrIHrDoWthem7DrWthIiwKICAgIHkgPSAiQ2Vsa292w6Egc3VtYSBuw6FrdXB1IiwKICAgIGNvbG9yID0gIlBvaGxhdmllIgogICkgKwogIHRoZW1lX21pbmltYWwoKSAgICAgIApgYGAKWsOha2F6bsOtY2kgcG9rcsO9dmFqw7ogxaFpcm9rw6kgdmVrb3bDqSByb3pww6R0aWUtIHByaWJsacW+bmUgb2QgMTggZG8gNzAgcm9rb3YuIFBvxI1ldCBuw6FrdXBvdiBqZSBwb21lcm5lIHJvdm5vbWVybmUgcm96bG/FvmVuw70gdm8gdsWhZXRrw71jaCB2ZWtvdsO9Y2gga2F0ZWfDs3Jpw6FjaCDigJQgxb5pYWRuYSBza3VwaW5hIHbDvXJhem5lIG5lZG9taW51amUuIFbDpMSNxaFpbmEgbsOha3Vwb3Ygc2EgcG9oeWJ1amUgdiBkb2xuZWogcG9sb3ZpY2kgZ3JhZnUgKGRvIHByaWJsacW+bmUgNDAwMCksIMSNbyBuYXpuYcSNdWplLCDFvmUgbWVuxaFpZSBuw6FrdXB5IHPDuiBvdmXEvmEgxI1hc3RlasWhaWUuCsSMZXJ2ZW7DqSAoRmVtYWxlKSBhIHR5cmt5c292w6kgKE1hbGUpIGJvZHkgc8O6IHJvem1pZXN0bmVuw6kgdmXEvm1pIHBvZG9ibmUuCk11xb5pIGFqIMW+ZW55IG5ha3VwdWrDuiB2IHBvZG9ibsO9Y2ggb2JqZW1vY2ggbmFwcmllxI0gdmVrb3bDvW1pIGthdGVnw7NyaWFtaS4KCiMjIyMgQm94cGxvdAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKCmxpYnJhcnkoZ2dwbG90MikKCmdncGxvdCh1ZGFqZS4yMDIwLCBhZXMoeCA9IGBQcm9kdWN0LkNhdGVnb3J5YCwgeSA9IGBUb3RhbC5QdXJjaGFzZS5BbW91bnRgLCBmaWxsID0gYFByb2R1Y3QuQ2F0ZWdvcnlgKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYsO6Y2lhIGNlbGtvdmVqIHN1bXkgbsOha3VwdSBwb2TEvmEga2F0ZWfDs3JpZSBwcm9kdWt0dSIsCiAgICB4ID0gIkthdGVnw7NyaWEgcHJvZHVrdHUiLAogICAgeSA9ICJDZWxrb3bDoSBzdW1hIG7DoWt1cHUiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQpgYGAKRGlzdHJpYsO6Y2lhIGNlbGtvdmVqIHN1bXkgbsOha3VwdSBqZSB2ZcS+bWkgcG9kb2Juw6EgbmFwcmllxI0gdsWhZXRrw71taSBrYXRlZ8OzcmlhbWkgcHJvZHVrdG92IChCb29rcywgQ2xvdGhpbmcsIEVsZWN0cm9uaWNzLCBIb21lKS4gTmVleGlzdHVqw7ogdmXEvmvDqSByb3pkaWVseSB2IG1lZGnDoW5lIGFuaSB2IHJvenNhaHUgbsOha3Vwb3YsIMSNbyB6bmFtZW7DoSwgxb5lIMW+aWFkbmEga2F0ZWfDs3JpYSB2w71yYXpuZSBuZXZ5bmlrw6EgdiB0b20sIGtvxL5rbyDEvnVkaWEgdXRyw6FjYWrDui4KCiMgWsOha2xhZG7DqSDFoXRhdGlzdGlreS4gCgoKIyMga25pdHIgLSB0YWJ1xL5rYQpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShrbml0cikKCgpwdXJjaGFzZS5zdGF0cyA8LSB1ZGFqZSAlPiUKICBmaWx0ZXIoUHVyY2hhc2UuRGF0ZSAlaW4lIDIwMjA6MjAyMykgJT4lICAgCiAgZ3JvdXBfYnkoUHVyY2hhc2UuRGF0ZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgbiAgICAgICA9IG4oKSwKICAgIG1lYW4gICAgPSBtZWFuKFRvdGFsLlB1cmNoYXNlLkFtb3VudCwgbmEucm0gPSBUUlVFKSwKICAgIHNkICAgICAgPSBzZChUb3RhbC5QdXJjaGFzZS5BbW91bnQsIG5hLnJtID0gVFJVRSksCiAgICBtaW4gICAgID0gbWluKFRvdGFsLlB1cmNoYXNlLkFtb3VudCwgbmEucm0gPSBUUlVFKSwKICAgIHEyNSAgICAgPSBxdWFudGlsZShUb3RhbC5QdXJjaGFzZS5BbW91bnQsIDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICBtZWRpYW4gID0gbWVkaWFuKFRvdGFsLlB1cmNoYXNlLkFtb3VudCwgbmEucm0gPSBUUlVFKSwKICAgIHE3NSAgICAgPSBxdWFudGlsZShUb3RhbC5QdXJjaGFzZS5BbW91bnQsIDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICBtYXggICAgID0gbWF4KFRvdGFsLlB1cmNoYXNlLkFtb3VudCwgbmEucm0gPSBUUlVFKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgprYWJsZShwdXJjaGFzZS5zdGF0cywgZGlnaXRzID0gMiwgY2FwdGlvbiA9ICJaw6FrbGFkbsOpIMWhdGF0aXN0aWt5IFRvdGFsIFB1cmNoYXNlIEFtb3VudCBwb2TEvmEgcm9rdSIpCmBgYAoKYWxlYm8ga3JhasWhaWUgdGFidcS+a3kgcyBwb21vY291IC5rYWJsZUV4dHJhLjoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgogcHVyY2hhc2Uuc3RhdHMgPC0gdWRhamUgJT4lCiAgZmlsdGVyKFB1cmNoYXNlLkRhdGUgJWluJSAyMDIwOjIwMjMpICU+JSAgIAogIGdyb3VwX2J5KFB1cmNoYXNlLkRhdGUpICU+JQogIHN1bW1hcmlzZSgKICAgIG4gICAgICAgPSBuKCksCiAgICBtZWFuICAgID0gbWVhbihUb3RhbC5QdXJjaGFzZS5BbW91bnQsIG5hLnJtID0gVFJVRSksCiAgICBzZCAgICAgID0gc2QoVG90YWwuUHVyY2hhc2UuQW1vdW50LCBuYS5ybSA9IFRSVUUpLAogICAgbWluICAgICA9IG1pbihUb3RhbC5QdXJjaGFzZS5BbW91bnQsIG5hLnJtID0gVFJVRSksCiAgICBxMjUgICAgID0gcXVhbnRpbGUoVG90YWwuUHVyY2hhc2UuQW1vdW50LCAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgbWVkaWFuICA9IG1lZGlhbihUb3RhbC5QdXJjaGFzZS5BbW91bnQsIG5hLnJtID0gVFJVRSksCiAgICBxNzUgICAgID0gcXVhbnRpbGUoVG90YWwuUHVyY2hhc2UuQW1vdW50LCAwLjc1LCBuYS5ybSA9IFRSVUUpLAogICAgbWF4ICAgICA9IG1heChUb3RhbC5QdXJjaGFzZS5BbW91bnQsIG5hLnJtID0gVFJVRSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKCnB1cmNoYXNlLnN0YXRzICU+JQogIGthYmxlKGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiWsOha2xhZG7DqSDFoXRhdGlzdGlreSBUb3RhbCBQdXJjaGFzZSBBbW91bnQgcG9kxL5hIHJva3UiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKSAlPiUKICBjb2x1bW5fc3BlYygxLCBib2xkID0gVFJVRSkgJT4lICAgICAgICAgICAgICAgICAKICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgYmFja2dyb3VuZCA9ICIjZjJmMmYyIikgJT4lICAKICBhZGRfaGVhZGVyX2Fib3ZlKGMoIiAiID0gMiwgIlRvdGFsIFB1cmNoYXNlIEFtb3VudCBTdGF0aXN0aWNzIiA9IDcpKQpgYGAKCgojIFRlc3RvdmFuaWUgaHlwb3TDqXoKCgpgYGB7cn0KdC50ZXN0LnJlc3VsdCA8LSB0LnRlc3QoCiAgdWRhamUkVG90YWwuUHVyY2hhc2UuQW1vdW50W3VkYWplJFB1cmNoYXNlLkRhdGUgPT0gMjAyMF0sCiAgdWRhamUkVG90YWwuUHVyY2hhc2UuQW1vdW50W3VkYWplJFB1cmNoYXNlLkRhdGUgPT0gMjAyMV0KKQoKcHJpbnQodC50ZXN0LnJlc3VsdCkKYGBgCgoKIyMjIyBBTk9WQTogQ29tcGFyaW5nIFJlYWRpbmcgU2NvcmVzIEFjcm9zcyBQcm9ncmFtcwoKYGBge3J9CmFub3ZhLnJlc3VsdCA8LSBhb3YoVG90YWwuUHVyY2hhc2UuQW1vdW50IH4gZmFjdG9yKFB1cmNoYXNlLkRhdGUpLCBkYXRhID0gdWRhamUpCnN1bW1hcnkoYW5vdmEucmVzdWx0KQpgYGAKCiMjIyMgTGluZWFyIFJlZ3Jlc3Npb246IFByZWRpY3RpbmcgTWF0aCBTY29yZXMKCmBgYHtyfQptb2RlbCA8LSBsbShUb3RhbC5QdXJjaGFzZS5BbW91bnQgfiBQcm9kdWN0LlByaWNlICsgQ3VzdG9tZXIuQWdlICsgUXVhbnRpdHksIGRhdGEgPSB1ZGFqZSkKCnN1bW1hcnkobW9kZWwpCmBgYAoKCgpgYGB7cn0KbGlicmFyeShicm9vbSkKbGlicmFyeShkcGx5cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHN0cmluZ3IpCgptb2RlbCA8LSBsbShUb3RhbC5QdXJjaGFzZS5BbW91bnQgfiBQcm9kdWN0LlByaWNlICsgQ3VzdG9tZXIuQWdlICsgUXVhbnRpdHksIGRhdGEgPSB1ZGFqZSkKCmNvZWYudGJsIDwtIHRpZHkobW9kZWwsIGNvbmYuaW50ID0gVFJVRSkgJT4lCiAgbXV0YXRlKAogICAgdGVybSA9IHJlY29kZSh0ZXJtLAogICAgICAiKEludGVyY2VwdCkiID0gIkludGVyY2VwdCIsCiAgICAgICJQcm9kdWN0LlByaWNlIiA9ICJQcm9kdWN0IFByaWNlIiwKICAgICAgIkN1c3RvbWVyLkFnZSIgPSAiQ3VzdG9tZXIgQWdlIiwKICAgICAgIlF1YW50aXR5IiA9ICJRdWFudGl0eSIKICAgICksCiAgICBzdGFycyA9IGNhc2Vfd2hlbigKICAgICAgcC52YWx1ZSA8IDAuMDAxIH4gIioqKiIsCiAgICAgIHAudmFsdWUgPCAwLjAxICB+ICIqKiIsCiAgICAgIHAudmFsdWUgPCAwLjA1ICB+ICIqIiwKICAgICAgcC52YWx1ZSA8IDAuMSAgIH4gIsK3IiwKICAgICAgVFJVRSAgICAgICAgICAgIH4gIiIKICAgICkKICApICU+JQogIHRyYW5zbXV0ZSgKICAgIFRlcm0gPSB0ZXJtLAogICAgRXN0aW1hdGUgPSBlc3RpbWF0ZSwKICAgIGBTdGQuIEVycm9yYCA9IHN0ZC5lcnJvciwKICAgIGB0IHZhbHVlYCA9IHN0YXRpc3RpYywKICAgIGBwIHZhbHVlYCA9IHAudmFsdWUsCiAgICBgOTUlIENJYCA9IHN0cl9jKCJbIiwgcm91bmQoY29uZi5sb3csIDMpLCAiLCAiLCByb3VuZChjb25mLmhpZ2gsIDMpLCAiXSIpLAogICAgU2lnID0gc3RhcnMKICApCgpjb2VmLnRibCAlPiUKICBrYWJsZSgKICAgIGRpZ2l0cyA9IDMsCiAgICBjYXB0aW9uID0gIk9MUyBSZWdyZXNzaW9uIENvZWZmaWNpZW50cyAoVG90YWwgUHVyY2hhc2UgQW1vdW50IH4gUHJvZHVjdCBQcmljZSArIEN1c3RvbWVyIEFnZSArIFF1YW50aXR5KSIKICApICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpICU+JQogIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBUUlVFKSAlPiUKICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgYmFja2dyb3VuZCA9ICIjZjJmMmYyIikgJT4lCiAgZm9vdG5vdGUoCiAgICBnZW5lcmFsID0gIlNpZ25pZi4gY29kZXM6ICoqKiBwPDAuMDAxLCAqKiBwPDAuMDEsICogcDwwLjA1LCDCtyBwPDAuMS4iLAogICAgdGhyZWVwYXJ0dGFibGUgPSBUUlVFCiAgKQpgYGAKCmBgYHtyfQpmaXQudGJsIDwtIGdsYW5jZShtb2RlbCkgJT4lCiAgdHJhbnNtdXRlKAogICAgYFItc3F1YXJlZGAgPSByLnNxdWFyZWQsCiAgICBgQWRqLiBSLXNxdWFyZWRgID0gYWRqLnIuc3F1YXJlZCwKICAgIGBGLXN0YXRpc3RpY2AgPSBzdGF0aXN0aWMsCiAgICBgRiBwLXZhbHVlYCA9IHAudmFsdWUsCiAgICBgQUlDYCA9IEFJQywKICAgIGBCSUNgID0gQklDLAogICAgYE51bS4gb2JzLmAgPSBub2JzCiAgKQoKZml0LnRibCAlPiUKICBrYWJsZShkaWdpdHMgPSAzLCBjYXB0aW9uID0gIk1vZGVsIEZpdCBTdGF0aXN0aWNzIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygiY29uZGVuc2VkIikpCmBgYAoKCgoKCg==