Data

In our example this week, we are going to use the fake data - about real estates in Wroclaw - prices by districts, size of apartments and many more.

Preprocessing

As you can see, not all formats of our variables are adapted. We need to prepare appropriate formats of our variables according to their measurement scale and future application.

apartments$district<-as.factor(apartments$district)
apartments$building_type<-as.factor(apartments$building_type)
apartments$rooms<-factor(apartments$rooms,ordered=TRUE)
attach(apartments)
apartments$price_PLN<-as.numeric(apartments$price_PLN)
apartments$price_EUR<-as.numeric(apartments$price_EUR)

Frequency Tables

In the first step of our analysis, we will group our data into a simple frequency table.

First, let’s look at the distribution of housing prices in our sample and verify tabular validity using the TAI measure:

Ok, it looks quite ugly, so let’s wrap it up using the ‘kable’ package:

Apartments in Wroclaw - prices in kPLN
x label Freq Percent Valid Percent Cumulative Percent
Valid 350-450 kPLN 9 4.5 4.5 4.5
450-550 kPLN 21 10.5 10.5 15.0
550-650 kPLN 33 16.5 16.5 31.5
650-750 kPLN 36 18.0 18.0 49.5
750-850 kPLN 31 15.5 15.5 65.0
850-950 kPLN 36 18.0 18.0 83.0
950-1050 kPLN 21 10.5 10.5 93.5
1050-1150 kPLN 10 5.0 5.0 98.5
1150-1250 kPLN 2 1.0 1.0 99.5
1250-1350 kPLN 1 0.5 0.5 100.0
Total 200 100.0 100.0
Missing <blank> 0 0.0
<NA> 0 0.0
Total 200 100.0
##        # classes  Goodness of fit Tabular accuracy 
##       10.0000000        0.9780872        0.8508467

As we can see - the TAI index is quite high. 0.85 means that we can accept the proposed construction of the frequency table.

Basic plots

In this section, we should represent our data using basic (pre-installed in R) graphics. Select the most appropriate graphs depending on the scale of the selected variables. Explore the heterogeneity of the distribution by presenting the data by group (e.g., by neighborhood, building type, etc.). Don’t forget about main titles, labels and legends. Read more about graphical parameters here.

Note that the echo = FALSE parameter has been added to the code snippet to prevent printing the R code that generated the graph.

ggplot2 plots

Now, let’s use the ggplot2 and ggpubr libraries to plot.

Ggplot2 allows you to show the average value for each group using the stat_summary() function. You no longer need to calculate average values before creating a graph!

RainCloud Plot

Faceting

Faceting generates small multiples, each showing a different subset of the data. They are a powerful tool for exploratory data analysis: you can quickly compare patterns in different parts of the data and see if they are the same or different. Read more here.

Univariate Statistics

Before automatically reporting the full summary table of descriptive statistics, this time your goal is to measure the central tendency of the price distribution. Compare the mean, median, and mode along with positional measures - quantiles - by district and building type or number of rooms in the apartment.

    mean(price_PLN)
## [1] 760035
    median(price_PLN)
## [1] 755719.5
    sd(price_PLN) #standard deviation
## [1] 186099.8
    var(price_PLN) #variance
## [1] 34633125960
    coeff_var<-sd(price_PLN)/mean(price_PLN) #coefficient of variability %
    coeff_var
## [1] 0.2448568
    IQR(price_PLN)# difference between quartiles =Q3-Q1 
##      75% 
## 282686.5
    sx<-IQR(price_PLN)/2  #interquartile deviation
    coeff_varx<-sx/median(price_PLN) #IQR coefficient of variability %
    coeff_varx
##       75% 
## 0.1870314
    min(price_PLN)
## [1] 359769
    max(price_PLN)
## [1] 1277691
    quantile(price_PLN,probs=c(0,0.1,0.25,0.5,0.75,0.95,1),na.rm=TRUE)
##        0%       10%       25%       50%       75%       95%      100% 
##  359769.0  518806.8  619073.8  755719.5  901760.2 1054250.8 1277691.0

Ok, we have calculated all of the basic summary statistics above. Let’s wrap them up together now.

rooms boxplot histogram line1 line2 points1
1
2
3
4

Summary tables

Ok, now we will finally summarize the basic measures of central tendency for prices by district/building type using the ‘kable’ package. Feel free to customize your final report. See some hints here.

##  'qwraps2_summary_table' chr [1:14, 1:5] "\\bf{Price in PLN}" "~~ Min" ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:5] "" "1 (N = 44)" "2 (N = 50)" "3 (N = 58)" ...
##  - attr(*, "qable_args")=List of 6
##   ..$ rtitle    : chr ""
##   ..$ rgroup    : Named int 13
##   .. ..- attr(*, "names")= chr "Price in PLN"
##   ..$ rnames    : chr [1:13] "Min" "Max" "Q1" "Median" ...
##   ..$ cnames    : chr [1:5] "" "1 (N = 44)" "2 (N = 50)" "3 (N = 58)" ...
##   ..$ markup    : chr "latex"
##   ..$ kable_args: list()

gtsummary

We can calculate easily descriptive statistics also using gtsummary package:

apartments %>%
  select(price_PLN,rooms) %>%
  tbl_summary(label= price_PLN ~ "Price",digits=c(price_PLN)~2,by=rooms,type = all_continuous() ~ "continuous2", statistic = all_continuous() ~ c("{N_nonmiss}", "{median} ({p25}, {p75})", "{min}, {max}"),missing = "no")
Characteristic 1, N = 44 2, N = 50 3, N = 58 4, N = 48
Price



    N 44.00 50.00 58.00 48.00
    Median (IQR) 520,507.00 (479,684.75, 555,024.75) 677,260.00 (634,757.25, 717,728.50) 846,303.50 (769,683.75, 901,078.75) 964,338.50 (909,371.50, 1,050,976.75)
    Range 359,769.00, 657,146.00 590,286.00, 888,634.00 632,770.00, 965,829.00 736,669.00, 1,277,691.00

dfSummary

dfSummary() creates a summary table with statistics, frequencies and graphs for all variables in a data frame. The information displayed is type-specific (character, factor, numeric, date) and also varies according to the number of distinct values.

When using dfSummary() in R Markdown documents, it is generally a good idea to exclude a column or two to avoid margin overflow. Since the Valid and Missing columns are redundant, we can drop either one of them.

dfSummary(apartments,
          plain.ascii  = FALSE, 
          style        = "grid", 
          graph.magnif = 0.75, 
          valid.col    = FALSE,
          tmp.img.dir  = "/tmp")
## temporary images written to 'C:\tmp'

Data Frame Summary

apartments

Dimensions: 200 x 6
Duplicates: 0

No Variable Stats / Values Freqs (% of Valid) Graph Missing
1 price_PLN
[numeric]
Mean (sd) : 760035 (186099.8)
min < med < max:
359769 < 755719.5 < 1277691
IQR (CV) : 282686.5 (0.2)
200 distinct values 0
(0.0%)
2 price_EUR
[numeric]
Mean (sd) : 175934 (43078.6)
min < med < max:
83280 < 174935 < 295762
IQR (CV) : 65436.2 (0.2)
200 distinct values 0
(0.0%)
3 rooms
[ordered, factor]
1. 1
2. 2
3. 3
4. 4
44 (22.0%)
50 (25.0%)
58 (29.0%)
48 (24.0%)
0
(0.0%)
4 size
[numeric]
Mean (sd) : 46.2 (20.1)
min < med < max:
17 < 43.7 < 87.7
IQR (CV) : 30.2 (0.4)
162 distinct values 0
(0.0%)
5 district
[factor]
1. Biskupin
2. Krzyki
3. Srodmiescie
65 (32.5%)
79 (39.5%)
56 (28.0%)
0
(0.0%)
6 building_type
[factor]
1. kamienica
2. niski blok
3. wiezowiec
61 (30.5%)
63 (31.5%)
76 (38.0%)
0
(0.0%)

To produce optimal results, summarytools has its own version of the base by() function. It’s called stby(), and we use it exactly as we would by():

(stats_by_rooms <- stby(data      = apartments, INDICES   = apartments$rooms, FUN       = descr, stats     = "common", transpose = TRUE))
## Non-numerical variable(s) ignored: rooms, district, building_type

Descriptive Statistics
apartments
Group: rooms = 1
N: 44

Mean Std.Dev Min Median Max N.Valid Pct.Valid
price_EUR 119332.95 15497.90 83280.00 120488.00 152117.00 44.00 100.00
price_PLN 515518.05 66951.03 359769.00 520507.00 657146.00 44.00 100.00
size 19.28 1.46 17.00 19.10 21.90 44.00 100.00

Group: rooms = 2
N: 50

Mean Std.Dev Min Median Max N.Valid Pct.Valid
price_EUR 158233.22 15063.13 136640.00 156773.00 205702.00 50.00 100.00
price_PLN 683567.70 65072.66 590286.00 677260.00 888634.00 50.00 100.00
size 36.80 4.46 29.60 35.95 43.70 50.00 100.00

Group: rooms = 3
N: 58

Mean Std.Dev Min Median Max N.Valid Pct.Valid
price_EUR 192987.55 20125.88 146475.00 195904.00 223572.00 58.00 100.00
price_PLN 833706.02 86943.90 632770.00 846303.50 965829.00 58.00 100.00
size 53.33 7.21 41.20 53.45 65.20 58.00 100.00

Group: rooms = 4
N: 48

Mean Std.Dev Min Median Max N.Valid Pct.Valid
price_EUR 225650.42 26347.03 170525.00 223226.50 295762.00 48.00 100.00
price_PLN 974809.96 113819.21 736669.00 964338.50 1277691.00 48.00 100.00
size 72.05 10.18 53.30 70.85 87.70 48.00 100.00

Tidy Tables

When generating freq() or descr() tables, it is possible to turn the results into “tidy” tables with the use of the tb() function (think of tb as a diminutive for tibble). For example:

apartments %>%
  descr(stats = "common") %>%
  tb()
## # A tibble: 3 × 8
##   variable      mean       sd    min      med       max n.valid pct.valid
##   <chr>        <dbl>    <dbl>  <dbl>    <dbl>     <dbl>   <dbl>     <dbl>
## 1 price_EUR 175934.   43079.   83280 174935    295762       200       100
## 2 price_PLN 760035.  186100.  359769 755720.  1277691       200       100
## 3 size          46.2     20.1     17     43.7      87.7     200       100

Here are some examples showing how lists created using stby() or group_by() can be transformed into tidy tibbles.

grouped_descr <- stby(data    = apartments,INDICES = apartments$rooms, FUN     = descr, stats   = "common")

grouped_descr %>% tb()
## # A tibble: 12 × 9
##    rooms variable      mean        sd      min      med    max n.valid pct.valid
##    <fct> <chr>        <dbl>     <dbl>    <dbl>    <dbl>  <dbl>   <dbl>     <dbl>
##  1 1     price_EUR 119333.   15498.    83280   120488   1.52e5      44       100
##  2 1     price_PLN 515518.   66951.   359769   520507   6.57e5      44       100
##  3 1     size          19.3      1.46     17       19.1 2.19e1      44       100
##  4 2     price_EUR 158233.   15063.   136640   156773   2.06e5      50       100
##  5 2     price_PLN 683568.   65073.   590286   677260   8.89e5      50       100
##  6 2     size          36.8      4.46     29.6     36.0 4.37e1      50       100
##  7 3     price_EUR 192988.   20126.   146475   195904   2.24e5      58       100
##  8 3     price_PLN 833706.   86944.   632770   846304.  9.66e5      58       100
##  9 3     size          53.3      7.21     41.2     53.4 6.52e1      58       100
## 10 4     price_EUR 225650.   26347.   170525   223226.  2.96e5      48       100
## 11 4     price_PLN 974810.  113819.   736669   964338.  1.28e6      48       100
## 12 4     size          72.0     10.2      53.3     70.8 8.77e1      48       100

A Bridge to Other Packages

stby(data    = apartments, 
     INDICES = apartments$rooms, 
     FUN     = descr, 
     stats   = "fivenum") %>%
  tb(order = 3) %>%
  kable(format = "html", digits = 2) %>%
  collapse_rows(columns = 1, valign = "top")
variable rooms min q1 med q3 max
price_EUR 1 83280.0 110881.0 120488.00 128568.00 152117.0
2 136640.0 146754.0 156773.00 166259.00 205702.0
3 146475.0 177478.0 195904.00 208599.00 223572.0
4 170525.0 209827.5 223226.50 243300.00 295762.0
price_PLN 1 359769.0 479005.5 520507.00 555411.50 657146.0
2 590286.0 633978.0 677260.00 718237.00 888634.0
3 632770.0 766707.0 846303.50 901149.00 965829.0
4 736669.0 906455.0 964338.50 1051055.50 1277691.0
size 1 17.0 18.1 19.10 20.60 21.9
2 29.6 32.9 35.95 40.50 43.7
3 41.2 47.9 53.45 59.70 65.2
4 53.3 64.2 70.85 82.15 87.7

Your turn!

Your task this week is to: prepare your own descriptive analysis for the “CreditCard” dataset (AER package). It is a cross-sectional dataframe on the credit history for a sample of applicants for a type of credit card.

Are the yearly incomes (in USD 10,000), credit card expenditures, age, ratio of monthly credit card expenditure to yearly income - significantly different for applicants for customers with different credit risk (“card” variable - factor)?

Prepare a professional data visualizations, descriptive statistics’ tables and interpret them.

# your code here
CreditCard$age<-as.numeric(CreditCard$age)
CreditCard$reports<-as.numeric(CreditCard$reports)
CreditCard$income<-as.numeric(CreditCard$income)
CreditCard$share<-as.numeric(CreditCard$share)
CreditCard$expenditure<-as.numeric(CreditCard$expenditure)
CreditCard$card<-as.factor(CreditCard$card)
credit_card_yes <- subset(CreditCard, card == 'yes')
credit_card_no <- subset(CreditCard, card == 'no')
credit_card_yes <- subset(CreditCard, card == 'yes')
credit_card_no <- subset(CreditCard, card == 'no')

age_yes <- credit_card_yes$age
age_no <- credit_card_no$age

summary(age_yes)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.1667 25.3333 31.0833 33.2160 39.7083 83.5000
summary(age_no)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.75   25.65   31.83   33.20   38.06   80.17
ggplot() +
  geom_density(data = credit_card_yes, aes(x = age, fill = "Card Owner"), color = "coral4", alpha = 0.7) +
  geom_density(data = credit_card_no, aes(x = age, fill = "Non-Owner"), color = "pink", alpha = 0.7) +
  labs(x = "Age", y = "Density", title = "Density Plot of Age by Card Ownership") +
  scale_fill_manual(values = c("Card Owner" = "coral4", "Non-Owner" = "pink"),
                    name = "Card Ownership",
                    labels = c("Owner", "Non-Owner")) +
  theme_minimal()

ggplot() +
  geom_boxplot(data = credit_card_yes, aes(x = "Card Owner", y = age, fill = "Card Owner"), color = "coral4", alpha = 0.7) +
  geom_boxplot(data = credit_card_no, aes(x = "Non-Owner", y = age, fill = "Non-Owner"), color = "pink", alpha = 0.7) +
  labs(x = "Card Ownership", y = "Age", title = "Boxplot of Age by Card Ownership") +
  scale_fill_manual(values = c("Card Owner" = "coral4", "Non-Owner" = "pink"),
                    name = "Card Ownership",
                    labels = c("Owner", "Non-Owner")) +
  theme_minimal()

summary_df_yes <- data.frame(
  Statistic = c("Mean", "Median", "Standard Deviation", "Variance", "Coefficient of Variability (%)", 
                "Interquartile Range (IQR)", "Interquartile Deviation", "IQR Coefficient of Variability (%)",
                "Minimum", "Maximum"),
  Card_Owners = c(
    mean(age_yes), median(age_yes), sd(age_yes), var(age_yes),
    sd(age_yes) / mean(age_yes) * 100, IQR(age_yes), IQR(age_yes) / 2,
    (IQR(age_yes) / 2) / median(age_yes) * 100, min(age_yes), max(age_yes)
  )
)

summary_df_no <- data.frame(
  Statistic = c("Mean", "Median", "Standard Deviation", "Variance", "Coefficient of Variability (%)", 
                "Interquartile Range (IQR)", "Interquartile Deviation", "IQR Coefficient of Variability (%)",
                "Minimum", "Maximum"),
  Non_Card_Owners = c(
    mean(age_no), median(age_no), sd(age_no), var(age_no),
    sd(age_no) / mean(age_no) * 100, IQR(age_no), IQR(age_no) / 2,
    (IQR(age_no) / 2) / median(age_no) * 100, min(age_no), max(age_no)
  )
)

summary_combined <- merge(summary_df_yes, summary_df_no, by = "Statistic", all = TRUE)

knitr::kable(summary_combined, format = "markdown", caption = "Summary Statistics for Age by Card Ownership") %>%
  kable_styling(full_width = FALSE, bootstrap_options = "striped", position = "center")
Summary Statistics for Age by Card Ownership
Statistic Card_Owners Non_Card_Owners
Coefficient of Variability (%) 30.7404349 29.880709
Interquartile Deviation 7.1875025 6.208333
Interquartile Range (IQR) 14.3750050 12.416665
IQR Coefficient of Variability (%) 23.1233349 19.502614
Maximum 83.5000000 80.166660
Mean 33.2160312 33.202984
Median 31.0833300 31.833335
Minimum 0.1666667 0.750000
Standard Deviation 10.2107524 9.921287
Variance 104.2594649 98.431938

Summing up the data showing from the table and two plots of age of people using credit cards and age of people who don’t we can present some interesting information:

  • maximum age is bigger in the group using credit cards, which most people would think is oppositely,

  • there is more people around age 25 who are owners of credit card then people around same age not owing credit cards,

  • people owining cedit cards are mostly young people, we can see big decline around age 27 to 35,

  • in the group owning credit card age is more diverse,

  • in both groups we have some data errors because we can see some outliner around 0, which is impossible in real life,

income_yes <- credit_card_yes$income
income_no <- credit_card_no$income

summary(income_yes)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.210   2.350   3.000   3.451   4.000  13.500
summary(income_no)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.490   2.000   2.590   3.069   3.625  11.000
ggplot() +
  geom_density(data = credit_card_yes, aes(x = income, fill = "Card Owner"), color = "violetred2", alpha = 0.7) +
  geom_density(data = credit_card_no, aes(x = income, fill = "Non-Owner"), color = "violet", alpha = 0.7) +
  labs(x = "Income", y = "Density", title = "Density Plot of Income (in USD 10,000) by Card Owners") +
  scale_fill_manual(values = c("Card Owner" = "violetred2", "Non-Owner" = "violet"),
                    name = "Card Ownership",
                    labels = c("Owner", "Non-owner")) +
  theme_minimal()

ggplot() +
  geom_boxplot(data = credit_card_yes, aes(x = "Card Owner", y = income, fill = "Card Owner"), color = "violetred2", alpha = 0.7) +
  geom_boxplot(data = credit_card_no, aes(x = "Non-Owner", y = income, fill = "Non-Owner"), color = "violet", alpha = 0.7) +
  labs(x = "Card Ownership", y = "Income (in USD 10,000)", title = "Boxplot of Income (in USD 10,000) by Card Ownership") +
  scale_fill_manual(values = c("Card Owner" = "violetred2", "Non-Owner" = "violet"),
                    name = "Card Ownership",
                    labels = c("Owner", "Non-Owner")) +
  theme_minimal()

summary_df_yes <- data.frame(
  Statistic = c("Mean", "Median", "Standard Deviation", "Variance", "Coefficient of Variability (%)", 
                "Interquartile Range (IQR)", "Interquartile Deviation", "IQR Coefficient of Variability (%)",
                "Minimum", "Maximum"),
  Card_Owners = c(
    mean(income_yes), median(income_yes), sd(income_yes), var(income_yes),
    sd(income_yes) / mean(income_yes) * 100, IQR(income_yes), IQR(income_yes) / 2,
    (IQR(income_yes) / 2) / median(income_yes) * 100, min(income_yes), max(income_yes)
  )
)

summary_df_no <- data.frame(
  Statistic = c("Mean", "Median", "Standard Deviation", "Variance", "Coefficient of Variability (%)", 
                "Interquartile Range (IQR)", "Interquartile Deviation", "IQR Coefficient of Variability (%)",
                "Minimum", "Maximum"),
  Non_Card_Owners = c(
    mean(income_no), median(income_no), sd(income_no), var(income_no),
    sd(income_no) / mean(income_no) * 100, IQR(income_no), IQR(income_no) / 2,
    (IQR(income_no) / 2) / median(income_no) * 100, min(income_no), max(income_no)
  )
)

summary_combined <- merge(summary_df_yes, summary_df_no, by = "Statistic", all = TRUE)

knitr::kable(summary_combined, format = "markdown", caption = "Summary Statistics for Income by Card Ownership") %>%
  kable_styling(full_width = FALSE, bootstrap_options = "striped", position = "center")
Summary Statistics for Income by Card Ownership
Statistic Card_Owners Non_Card_Owners
Coefficient of Variability (%) 49.463385 52.642387
Interquartile Deviation 0.825000 0.812500
Interquartile Range (IQR) 1.650000 1.625000
IQR Coefficient of Variability (%) 27.500000 31.370656
Maximum 13.500000 11.000000
Mean 3.451273 3.068509
Median 3.000000 2.590000
Minimum 0.210000 0.490000
Standard Deviation 1.707116 1.615336
Variance 2.914247 2.609312

Analizing data from the plots and table we can conclude:

  • there is bigger diversity in the income of card owners,

  • if we group card owners by hey income we can tell that the most numerous group is the one of income 25000 USD,

  • if we do the same with non card owners then the biggest group is the one of income around 20000 USD,

  • people with credit cards have bigger average income then people without them,

  • maximum income of credit card owners is bigger then in non owners.

income_age_yes <- credit_card_yes[, c("income", "age")]
summary(income_age_yes)
##      income            age         
##  Min.   : 0.210   Min.   : 0.1667  
##  1st Qu.: 2.350   1st Qu.:25.3333  
##  Median : 3.000   Median :31.0833  
##  Mean   : 3.451   Mean   :33.2160  
##  3rd Qu.: 4.000   3rd Qu.:39.7083  
##  Max.   :13.500   Max.   :83.5000
income_age_no <- credit_card_no[, c("income", "age")]
summary(income_age_no)
##      income            age       
##  Min.   : 0.490   Min.   : 0.75  
##  1st Qu.: 2.000   1st Qu.:25.65  
##  Median : 2.590   Median :31.83  
##  Mean   : 3.069   Mean   :33.20  
##  3rd Qu.: 3.625   3rd Qu.:38.06  
##  Max.   :11.000   Max.   :80.17
ggplot(income_age_yes, aes(x = age, y = income)) +
  geom_bar(stat = "identity", fill = "skyblue", color = "skyblue") +
  labs(x = "Age", y = "Income (in USD 10,000)", title = "Income by Age (For People with Cards)") +
  theme_grey()

ggplot(income_age_no, aes(x = age, y = income)) +
  geom_bar(stat = "identity", fill = "pink", color = "pink") +
  labs(x = "Age", y = "Income (in USD 10,000)", title = "Income by Age (For People Without Cards)") +
  theme_grey()

Now lets compare income by age in both of he groups: - with credit cards biggest income have people around age of 42, while for non - owners this age is a little bit smaller, because it’s around 35.

  • we can see bigger differences in income of different age groups for card owners comparing to non-owners,

  • there is more age groups lacking in the non-owners, which is only assuring us that there is bigger age diversity in the card owners group,

  • again we can see some errors in data set, because here is group of people around age 0 which has some income and it’s rather impossible in real life, so based on this we can assume that those are errors.

ggplot() +
  geom_boxplot(data = credit_card_yes, aes(x = "Card Owner", y = share, fill = "Card Owner"), color = "turquoise2", alpha = 0.7) +
  geom_boxplot(data = credit_card_no, aes(x = "Non-Owner", y = share, fill = "Non-Owner"), color = "purple", alpha = 0.7) +
  labs(x = "Card Ownership", y = "Share", title = "Boxplot of Share by Card Ownership") +
  scale_fill_manual(values = c("Card Owner" = "turquoise2", "Non-Owner" = "purple"),
                    name = "Card Ownership",
                    labels = c("Owner", "Non-Owner")) +
  theme_minimal()

ggplot() +
  geom_density(data = credit_card_yes, aes(x = share, fill = "Card Owner"), color = "turquoise2", alpha = 0.7) +
  labs(x = "Share", y = "Density", title = "Density Plot of Share by Card Owners") +
  scale_fill_manual(values = c("Card Owner" = "turquoise2"),
                    name = "Card Ownership",
                    labels = c("Owner")) +
  theme_minimal()

share_yes <- credit_card_yes$share
share_no <- credit_card_no$share

summary_df_yes <- data.frame(
  Statistic = c("Mean", "Median", "Standard Deviation", "Variance", "Coefficient of Variability (%)", 
                "Interquartile Range (IQR)", "Interquartile Deviation", "IQR Coefficient of Variability (%)",
                "Minimum", "Maximum"),
  Card_Owners = c(
    mean(share_yes), median(share_yes), sd(share_yes), var(share_yes),
    sd(share_yes) / mean(share_yes) * 100, IQR(share_yes), IQR(share_yes) / 2,
    (IQR(share_yes) / 2) / median(share_yes) * 100, min(share_yes), max(share_yes)
  )
)

summary_df_no <- data.frame(
  Statistic = c("Mean", "Median", "Standard Deviation", "Variance", "Coefficient of Variability (%)", 
                "Interquartile Range (IQR)", "Interquartile Deviation", "IQR Coefficient of Variability (%)",
                "Minimum", "Maximum"),
  Non_Card_Owners = c(
    mean(share_no), median(share_no), sd(share_no), var(share_no),
    sd(share_no) / mean(share_no) * 100, IQR(share_no), IQR(share_no) / 2,
    (IQR(share_no) / 2) / median(share_no) * 100, min(share_no), max(share_no)
  )
)

summary_combined <- merge(summary_df_yes, summary_df_no, by = "Statistic", all = TRUE)

knitr::kable(summary_combined, format = "markdown", caption = "Summary Statistics for Age by Card Ownership") %>%
  kable_styling(full_width = FALSE, bootstrap_options = "striped", position = "center")
Summary Statistics for Age by Card Ownership
Statistic Card_Owners Non_Card_Owners
Coefficient of Variability (%) 111.9670714 44.6937484
Interquartile Deviation 0.0436119 0.0001345
Interquartile Range (IQR) 0.0872238 0.0002689
IQR Coefficient of Variability (%) 72.4336454 29.0204022
Maximum 0.9063205 0.0024490
Mean 0.0884815 0.0004768
Median 0.0602094 0.0004633
Minimum 0.0001860 0.0001091
Standard Deviation 0.0990702 0.0002131
Variance 0.0098149 0.0000000

Those plots and table shows us share (Ratio of monthly credit card expenditure to yearly income) of card owners and non-owners, and it shows us something very interesting. We could assume that share for non-owners should be 0, because how someone without credit card can have a ratio of monthly credit card expenditure to yearly income, but from the table it’s says that those are extremely small numbers, which we can assume are just incorrect values.

So we will be only focusing on the group of card owners while analising the data.

  • diversit of the share is quite big,

  • when we gropu people by their share we can say, that most numerous gruop is of people having share around 0.12,

  • maximum share is 0.9 which is almost 1 so it would mean that thoes people spend all their salary by credit card,

expenditures_age<-credit_card_yes[, c("expenditure", "age")]

summary(expenditures_age)
##   expenditure           age         
##  Min.   :   0.00   Min.   : 0.1667  
##  1st Qu.:  66.52   1st Qu.:25.3333  
##  Median : 150.18   Median :31.0833  
##  Mean   : 238.60   Mean   :33.2160  
##  3rd Qu.: 313.96   3rd Qu.:39.7083  
##  Max.   :3099.51   Max.   :83.5000
ggplot(expenditures_age, aes(x = age, y = expenditure)) +
  geom_bar(stat = "identity", fill = "maroon", color = "maroon") +
  labs(x = "Age", y = "Expenditure", title = "Expenditure by Age") +
  theme_minimal()

Now just quick conclusion from this bar plot:

  • mostly young people are paying with credit card,

  • biggest expenditure have people around 35,

  • expedtiture of people after 50 is decreasing,

  • again we see incorrect data around age 0.

share_age<-credit_card_yes[, c("share", "age")]

summary(share_age)
##      share               age         
##  Min.   :0.000186   Min.   : 0.1667  
##  1st Qu.:0.026635   1st Qu.:25.3333  
##  Median :0.060209   Median :31.0833  
##  Mean   :0.088482   Mean   :33.2160  
##  3rd Qu.:0.113858   3rd Qu.:39.7083  
##  Max.   :0.906320   Max.   :83.5000
ggplot(share_age, aes(x = age, y = share)) +
  geom_bar(stat = "identity", fill = "slateblue2" ,color = "slateblue2") +
  labs(x = "Age", y = "Share", title = "Ratio of monthly credit card expenditure to yearly income by Age") +
  theme_minimal()

And quick assumptions from this one:

  • biggest share have people around age of 25,

  • we can see that after age of 40 the share is way smaller compering to younger people,

  • again we see some incorrect data values around age 0.

Summing up all the data analysis we have few most important conclusions:

  • more people use credit card then not,

  • biggest group age of credit card users are young people, around age 20-30,

  • people who have bigger income they tend to have credit cards,

  • young people use credit card more frequently then people after 40s.

LS0tDQp0aXRsZTogJ0Rlc2NyaXB0aXZlIFN0YXRpc3RpY3MnDQpzdWJ0aXRsZTogJ1VuaXZhcmlhdGUgU3RhdGlzdGljcycNCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCmF1dGhvcjogIkp1bGlhIEtvemEsIFRvbWFzeiBCxYJhc3pjemFrLCBNaWNoYcWCIE1hbGlub3dza2kiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBmb250c2l6ZTogOHB0DQogICAgdG9jOiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQogICAgZGZfcHJpbnQ6IGRlZmF1bHQNCiAgICB0b2NfZGVwdGg6IDUNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KYGBge3Igc2V0dXAxLCBpbmNsdWRlPUZBTFNFfQ0KcmVxdWlyZWRfcGFja2FnZXMgPC0gYygicXdyYXBzMiIsICJhcnNlbmFsIiwgImUxMDcxIiwgImhhdmVuIiwgInBhcGVSIiwgDQogICAgICAgICAgICAgICAgICAgICAgICJkcGx5ciIsICJ0aWR5dmVyc2UiLCAia2FibGVFeHRyYSIsICJzdW1tYXJ5dG9vbHMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgImNsYXNzSW50IiwgInBhc3RlY3MiLCAicmVwb3J0dG9vbHMiLCAiZGVzY3RhYmxlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICJwc3ljaCIsICJmcmVxdWVuY3kiLCAiZ2dwdWJyIiwgImdnZm9yY2UiLCAiZ2dkaXN0IiwgDQogICAgICAgICAgICAgICAgICAgICAgICJnZ2hhbHZlcyIsICJndHN1bW1hcnkiLCAiQUVSIikNCmZvciAocGFja2FnZSBpbiByZXF1aXJlZF9wYWNrYWdlcykgew0KICBpZiAoIShwYWNrYWdlICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlKQ0KICB9DQp9DQoNCiMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcw0KbGlicmFyeShxd3JhcHMyKQ0KbGlicmFyeShhcnNlbmFsKQ0KbGlicmFyeShlMTA3MSkNCmxpYnJhcnkoaGF2ZW4pDQpsaWJyYXJ5KHBhcGVSKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShzdW1tYXJ5dG9vbHMpDQpsaWJyYXJ5KGNsYXNzSW50KQ0KbGlicmFyeShwYXN0ZWNzKQ0KbGlicmFyeShyZXBvcnR0b29scykNCmxpYnJhcnkoZGVzY3RhYmxlKQ0KbGlicmFyeShwc3ljaCkNCmxpYnJhcnkoZnJlcXVlbmN5KQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KGdnZm9yY2UpDQpsaWJyYXJ5KGdnZGlzdCkNCmxpYnJhcnkoZ2doYWx2ZXMpDQpsaWJyYXJ5KGd0c3VtbWFyeSkNCmxpYnJhcnkoQUVSKQ0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9naXRodWIuY29tL2tmbGlzaWtvd3NraS9kcy9ibG9iL21hc3Rlci9kYXRhX2FwYXJ0bWVudHMuY3N2P3Jhdz10cnVlIiwgDQogICAgICAgICAgICAgIGRlc3RmaWxlID0ibWllc3prYW5pYS5jc3YiLG1vZGU9IndiIikNCmFwYXJ0bWVudHMgPC0gcmVhZC5jc3YoIm1pZXN6a2FuaWEuY3N2Iiwgc2VwPSI7IiwgZGVjPSIsIikNCmBgYA0KDQojIyBEYXRhDQoNCkluIG91ciBleGFtcGxlIHRoaXMgd2Vlaywgd2UgYXJlIGdvaW5nIHRvIHVzZSB0aGUgZmFrZSBkYXRhIC0gYWJvdXQgcmVhbA0KZXN0YXRlcyBpbiBXcm9jbGF3IC0gcHJpY2VzIGJ5IGRpc3RyaWN0cywgc2l6ZSBvZiBhcGFydG1lbnRzIGFuZCBtYW55DQptb3JlLg0KDQojIyMgUHJlcHJvY2Vzc2luZw0KDQpBcyB5b3UgY2FuIHNlZSwgbm90IGFsbCBmb3JtYXRzIG9mIG91ciB2YXJpYWJsZXMgYXJlIGFkYXB0ZWQuIFdlIG5lZWQgdG8NCnByZXBhcmUgYXBwcm9wcmlhdGUgZm9ybWF0cyBvZiBvdXIgdmFyaWFibGVzIGFjY29yZGluZyB0byB0aGVpcg0KbWVhc3VyZW1lbnQgc2NhbGUgYW5kIGZ1dHVyZSBhcHBsaWNhdGlvbi4NCg0KYGBge3Igd3JhbmdsaW5nLCBpbmNsdWRlPVRSVUV9DQphcGFydG1lbnRzJGRpc3RyaWN0PC1hcy5mYWN0b3IoYXBhcnRtZW50cyRkaXN0cmljdCkNCmFwYXJ0bWVudHMkYnVpbGRpbmdfdHlwZTwtYXMuZmFjdG9yKGFwYXJ0bWVudHMkYnVpbGRpbmdfdHlwZSkNCmFwYXJ0bWVudHMkcm9vbXM8LWZhY3RvcihhcGFydG1lbnRzJHJvb21zLG9yZGVyZWQ9VFJVRSkNCmF0dGFjaChhcGFydG1lbnRzKQ0KYXBhcnRtZW50cyRwcmljZV9QTE48LWFzLm51bWVyaWMoYXBhcnRtZW50cyRwcmljZV9QTE4pDQphcGFydG1lbnRzJHByaWNlX0VVUjwtYXMubnVtZXJpYyhhcGFydG1lbnRzJHByaWNlX0VVUikNCmBgYA0KDQojIyBGcmVxdWVuY3kgVGFibGVzDQoNCkluIHRoZSBmaXJzdCBzdGVwIG9mIG91ciBhbmFseXNpcywgd2Ugd2lsbCBncm91cCBvdXIgZGF0YSBpbnRvIGEgc2ltcGxlDQpmcmVxdWVuY3kgdGFibGUuDQoNCkZpcnN0LCBsZXQncyBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgaG91c2luZyBwcmljZXMgaW4gb3VyIHNhbXBsZQ0KYW5kIHZlcmlmeSB0YWJ1bGFyIHZhbGlkaXR5IHVzaW5nIHRoZSBUQUkgbWVhc3VyZToNCg0KYGBge3IgdGFibGUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KZXR5a2lldHk8LWMoIjM1MC00NTAga1BMTiIsIjQ1MC01NTAga1BMTiIsIjU1MC02NTAga1BMTiIsIjY1MC03NTAga1BMTiIsIjc1MC04NTAga1BMTiIsIjg1MC05NTAga1BMTiIsIjk1MC0xMDUwIGtQTE4iLCIxMDUwLTExNTAga1BMTiIsIjExNTAtMTI1MCBrUExOIiwiMTI1MC0xMzUwIGtQTE4iKQ0KbGltaXRzPC1jdXQoYXBhcnRtZW50cyRwcmljZV9QTE4sc2VxKDM1MDAwMCwxMzUwMDAwLGJ5PTEwMDAwMCksbGFiZWxzPWV0eWtpZXR5KQ0KdGFiZWxhMTwtZnJlcShsaW1pdHMsdHlwZT0iaHRtbCIpDQpgYGANCg0KT2ssIGl0IGxvb2tzIHF1aXRlIHVnbHksIHNvIGxldCdzIHdyYXAgaXQgdXAgdXNpbmcgdGhlICdrYWJsZScgcGFja2FnZToNCg0KYGBge3IgdGFpLCBlY2hvPUZBTFNFfQ0Ka2JsKHRhYmVsYTEsY2FwdGlvbiA9ICJBcGFydG1lbnRzIGluIFdyb2NsYXcgLSBwcmljZXMgaW4ga1BMTiIpICU+JQ0KICAgIGthYmxlX21hdGVyaWFsKGMoInN0cmlwZWQiLCAiaG92ZXIiKSkNCnRhYjE8LWNsYXNzSW50ZXJ2YWxzKGFwYXJ0bWVudHMkcHJpY2VfUExOLG49MTAsc3R5bGU9ImZpeGVkIixmaXhlZEJyZWFrcz1zZXEoMzUwMDAwLDEzNTAwMDAsYnk9MTAwMDAwKSkNCmplbmtzLnRlc3RzKHRhYjEpDQpgYGANCg0KQXMgd2UgY2FuIHNlZSAtIHRoZSBUQUkgaW5kZXggaXMgcXVpdGUgaGlnaC4gMC44NSBtZWFucyB0aGF0IHdlIGNhbg0KYWNjZXB0IHRoZSBwcm9wb3NlZCBjb25zdHJ1Y3Rpb24gb2YgdGhlIGZyZXF1ZW5jeSB0YWJsZS4NCg0KIyMgQmFzaWMgcGxvdHMNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBzaG91bGQgcmVwcmVzZW50IG91ciBkYXRhIHVzaW5nIGJhc2ljIChwcmUtaW5zdGFsbGVkDQppbiBSKSBncmFwaGljcy4gU2VsZWN0IHRoZSBtb3N0IGFwcHJvcHJpYXRlIGdyYXBocyBkZXBlbmRpbmcgb24gdGhlDQpzY2FsZSBvZiB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzLiBFeHBsb3JlIHRoZSBoZXRlcm9nZW5laXR5IG9mIHRoZQ0KZGlzdHJpYnV0aW9uIGJ5IHByZXNlbnRpbmcgdGhlIGRhdGEgYnkgZ3JvdXAgKGUuZy4sIGJ5IG5laWdoYm9yaG9vZCwNCmJ1aWxkaW5nIHR5cGUsIGV0Yy4pLiBEb24ndCBmb3JnZXQgYWJvdXQgbWFpbiB0aXRsZXMsIGxhYmVscyBhbmQNCmxlZ2VuZHMuIFJlYWQgbW9yZSBhYm91dCBncmFwaGljYWwgcGFyYW1ldGVycw0KW2hlcmVdKGh0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvd2lraS9ncmFwaGljYWwtcGFyYW1ldGVycykuDQoNCmBgYHtyIGhpc3RvZ3JhbSwgZWNobz1GQUxTRX0NCmhpc3QocHJpY2VfUExOLCBicmVha3M9IkZEIiwgY29sPSJncmVlbiIsIHByb2JhYmlsaXR5ID0gVFJVRSwNCiAgICAgbWFpbj0iUHJpY2VzIGluIFBMTiAtIFdyb2NsYXciKQ0KbGluZXMoZGVuc2l0eShwcmljZV9QTE5bZGlzdHJpY3Q9PSJLcnp5a2kiXSksY29sPTIpDQpsaW5lcyhkZW5zaXR5KHByaWNlX1BMTltkaXN0cmljdD09IkJpc2t1cGluIl0pLGNvbD0zKQ0KbGluZXMoZGVuc2l0eShwcmljZV9QTE5bZGlzdHJpY3Q9PSJTcm9kbWllc2NpZSJdKSxjb2w9NCkNCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQ9YygiS3J6eWtpIiwgIkJpc2t1cGluIiwgIlNyb2RtaWVzY2llIiksDQogICAgICAgY29sPWMoMiwzLDQpLCBsdHk9MToyLCBob3Jpej1GQUxTRSwgYm94Lmx0eT0wLCBjZXg9MC44KQ0KDQpgYGANCg0KTm90ZSB0aGF0IHRoZSBgZWNobyA9IEZBTFNFYCBwYXJhbWV0ZXIgaGFzIGJlZW4gYWRkZWQgdG8gdGhlIGNvZGUNCnNuaXBwZXQgdG8gcHJldmVudCBwcmludGluZyB0aGUgUiBjb2RlIHRoYXQgZ2VuZXJhdGVkIHRoZSBncmFwaC4NCg0KYGBge3IgYm94cGxvdCwgZWNobz1GQUxTRX0NCmJveHBsb3QocHJpY2VfUExOfmRpc3RyaWN0KQ0KYGBgDQoNCiMjIGdncGxvdDIgcGxvdHMNCg0KTm93LCBsZXQncyB1c2UgdGhlICoqKmdncGxvdDIqKiogYW5kICoqKmdncHVicioqKiBsaWJyYXJpZXMgdG8gcGxvdC4NCg0KYGBge3IgaGlzdG9ncmFtMiwgZWNobz1GQUxTRX0NCiMgRGVuc2l0eSBwbG90IG9mICJwcmljZV9QTE4iDQojOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoNCmRlbnNpdHkucCA8LSBnZ2RlbnNpdHkoYXBhcnRtZW50cywgeCA9ICJwcmljZV9QTE4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJkaXN0cmljdCIsIHBhbGV0dGUgPSAiamNvIikrDQogIHN0YXRfb3ZlcmxheV9ub3JtYWxfZGVuc2l0eShjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKQ0KDQojIERyYXcgdGhlIHN1bW1hcnkgdGFibGUgb2YgcHJpY2VfUExODQojOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoNCiMgQ29tcHV0ZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGJ5IGdyb3Vwcw0Kc3RhYmxlIDwtIGRlc2Nfc3RhdGJ5KGFwYXJ0bWVudHMsIG1lYXN1cmUudmFyID0gInByaWNlX1BMTiIsDQogICAgICAgICAgICAgICAgICAgICAgZ3JwcyA9ICJkaXN0cmljdCIpDQpzdGFibGUgPC0gc3RhYmxlWywgYygiZGlzdHJpY3QiLCAibGVuZ3RoIiwgIm1lYW4iLCAic2QiKV0NCiMgU3VtbWFyeSB0YWJsZSBwbG90LCBtZWRpdW0gb3JhbmdlIHRoZW1lDQpzdGFibGUucCA8LSBnZ3RleHR0YWJsZShzdGFibGUsIHJvd3MgPSBOVUxMLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lID0gdHRoZW1lKCJtT3JhbmdlIikpDQojIERyYXcgdGV4dA0KIzo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6DQp0ZXh0IDwtIHBhc3RlKCJQcmljZSBwZXIgYXBhcnRtZW50IGJ5IDMgZGlzdHJpY3RzIC0gV3JvY2xhdy4iLA0KICAgICAgICAgICAgICAiUmFuZG9tIHNhbXBsZSBvZiAyMDAgYXBhcnRtZW50cy4iLA0KICAgICAgICAgICAgICAgc2VwID0gIiAiKQ0KdGV4dC5wIDwtIGdncGFyYWdyYXBoKHRleHQgPSB0ZXh0LCBmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxMSwgY29sb3IgPSAiYmxhY2siKQ0KIyBBcnJhbmdlIHRoZSBwbG90cyBvbiB0aGUgc2FtZSBwYWdlDQpnZ2FycmFuZ2UoZGVuc2l0eS5wLCBzdGFibGUucCwgdGV4dC5wLCANCiAgICAgICAgICBuY29sID0gMSwgbnJvdyA9IDMsDQogICAgICAgICAgaGVpZ2h0cyA9IGMoMSwgMC41LCAwLjMpKQ0KYGBgDQoNCkdncGxvdDIgYWxsb3dzIHlvdSB0byBzaG93IHRoZSBhdmVyYWdlIHZhbHVlIGZvciBlYWNoIGdyb3VwIHVzaW5nIHRoZQ0KKipzdGF0X3N1bW1hcnkoKSoqIGZ1bmN0aW9uLiBZb3Ugbm8gbG9uZ2VyIG5lZWQgdG8gY2FsY3VsYXRlIGF2ZXJhZ2UNCnZhbHVlcyBiZWZvcmUgY3JlYXRpbmcgYSBncmFwaCENCg0KYGBge3IgYm94cGxvdDIsIGVjaG89RkFMU0V9DQpnZ3Bsb3QoYXBhcnRtZW50cywgYWVzKHg9ZGlzdHJpY3QsIHk9cHJpY2VfUExOKSkgKw0KICAgIGdlb21fYm94cGxvdChhbHBoYT0wLjcpICsNCiAgICBzdGF0X3N1bW1hcnkoZnVuPSJtZWFuIiwgZ2VvbT0icG9pbnQiLCBzaGFwZT0yMCwgc2l6ZT01LCBjb2xvcj0icmVkIiwgZmlsbD0icmVkIikgKw0KIGdlb21faml0dGVyKCkgKw0KICAgIGZhY2V0X2dyaWQofmJ1aWxkaW5nX3R5cGUpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJTZXQxIikNCg0KYGBgDQoNCiMjIyBSYWluQ2xvdWQgUGxvdA0KDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYXBhcnRtZW50cyAlPiUgDQogIGZpbHRlcihyb29tcyAlaW4lIGMoMSwgMiwgMywgNCkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKHJvb21zKSwgeSA9IHByaWNlX1BMTiwgZmlsbCA9IGZhY3Rvcihyb29tcykpKSArDQogIA0KICAjIGFkZCBoYWxmLXZpb2xpbiBmcm9tIHtnZ2Rpc3R9IHBhY2thZ2UNCiAgc3RhdF9oYWxmZXllKA0KICAgICMgYWRqdXN0IGJhbmR3aWR0aA0KICAgIGFkanVzdCA9IDAuNSwNCiAgICAjIG1vdmUgdG8gdGhlIHJpZ2h0DQogICAganVzdGlmaWNhdGlvbiA9IC0wLjIsDQogICAgIyByZW1vdmUgdGhlIHNsdWIgaW50ZXJ2YWwNCiAgICAud2lkdGggPSAwLA0KICAgIHBvaW50X2NvbG91ciA9IE5BDQogICkgKw0KICBnZW9tX2JveHBsb3QoDQogICAgd2lkdGggPSAwLjEyLA0KICAgICMgcmVtb3Zpbmcgb3V0bGllcnMNCiAgICBvdXRsaWVyLmNvbG9yID0gTkEsDQogICAgYWxwaGEgPSAwLjUNCiAgKSArDQogIHN0YXRfZG90cygNCiAgICAjIHBsb3Rpbmcgb24gbGVmdCBzaWRlDQogICAgc2lkZSA9ICJsZWZ0IiwNCiAgICAjIGFkanVzdGluZyBwb3NpdGlvbg0KICAgIGp1c3RpZmljYXRpb24gPSAxLjEsDQogICAgIyBhZGp1c3QgZ3JvdXBpbmcgKGJpbm5pbmcpIG9mIG9ic2VydmF0aW9ucw0KICAgIGJpbndpZHRoID0gMC4yNQ0KICApICsNCiMgVGhlbWVzIGFuZCBMYWJlbHMNCiAgbGFicygNCiAgICB0aXRsZSA9ICJSYWluQ2xvdWQgUGxvdCIsDQogICAgeCA9ICJOby4gb2Ygcm9vbXMiLA0KICAgIHkgPSAiUHJpY2VzIGluIFBMTiIsDQogICAgZmlsbCA9ICJyb29tcyINCiAgKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCiMjIyBGYWNldGluZw0KDQpGYWNldGluZyBnZW5lcmF0ZXMgc21hbGwgbXVsdGlwbGVzLCBlYWNoIHNob3dpbmcgYSBkaWZmZXJlbnQgc3Vic2V0IG9mDQp0aGUgZGF0YS4gVGhleSBhcmUgYSBwb3dlcmZ1bCB0b29sIGZvciBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzOiB5b3UNCmNhbiBxdWlja2x5IGNvbXBhcmUgcGF0dGVybnMgaW4gZGlmZmVyZW50IHBhcnRzIG9mIHRoZSBkYXRhIGFuZCBzZWUgaWYNCnRoZXkgYXJlIHRoZSBzYW1lIG9yIGRpZmZlcmVudC4gUmVhZCBtb3JlDQpbaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnL2ZhY2V0Lmh0bWwpLg0KDQpgYGB7ciBmYWNldDEsIGVjaG89RkFMU0V9DQpwbG90MSA8LSBnZ3Bsb3QoYXBhcnRtZW50cywgYWVzKHByaWNlX1BMTiwgcm9vbXMpKSArIA0KICBnZW9tX2FibGluZSgpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjEsIGhlaWdodCA9IDAuMSkgDQpwbG90MSArIGZhY2V0X3dyYXAofmRpc3RyaWN0KQ0KYGBgDQoNCiMjIFVuaXZhcmlhdGUgU3RhdGlzdGljcw0KDQpCZWZvcmUgYXV0b21hdGljYWxseSByZXBvcnRpbmcgdGhlIGZ1bGwgc3VtbWFyeSB0YWJsZSBvZiBkZXNjcmlwdGl2ZQ0Kc3RhdGlzdGljcywgdGhpcyB0aW1lIHlvdXIgZ29hbCBpcyB0byBtZWFzdXJlIHRoZSBjZW50cmFsIHRlbmRlbmN5IG9mDQp0aGUgcHJpY2UgZGlzdHJpYnV0aW9uLiBDb21wYXJlIHRoZSBtZWFuLCBtZWRpYW4sIGFuZCBtb2RlIGFsb25nIHdpdGgNCnBvc2l0aW9uYWwgbWVhc3VyZXMgLSBxdWFudGlsZXMgLSBieSBkaXN0cmljdCBhbmQgYnVpbGRpbmcgdHlwZSBvcg0KbnVtYmVyIG9mIHJvb21zIGluIHRoZSBhcGFydG1lbnQuDQoNCmBgYHtyfQ0KICAgIG1lYW4ocHJpY2VfUExOKQ0KICAgIG1lZGlhbihwcmljZV9QTE4pDQogICAgc2QocHJpY2VfUExOKSAjc3RhbmRhcmQgZGV2aWF0aW9uDQogICAgdmFyKHByaWNlX1BMTikgI3ZhcmlhbmNlDQogICAgY29lZmZfdmFyPC1zZChwcmljZV9QTE4pL21lYW4ocHJpY2VfUExOKSAjY29lZmZpY2llbnQgb2YgdmFyaWFiaWxpdHkgJQ0KICAgIGNvZWZmX3Zhcg0KICAgIElRUihwcmljZV9QTE4pIyBkaWZmZXJlbmNlIGJldHdlZW4gcXVhcnRpbGVzID1RMy1RMSANCiAgICBzeDwtSVFSKHByaWNlX1BMTikvMiAgI2ludGVycXVhcnRpbGUgZGV2aWF0aW9uDQogICAgY29lZmZfdmFyeDwtc3gvbWVkaWFuKHByaWNlX1BMTikgI0lRUiBjb2VmZmljaWVudCBvZiB2YXJpYWJpbGl0eSAlDQogICAgY29lZmZfdmFyeA0KICAgIG1pbihwcmljZV9QTE4pDQogICAgbWF4KHByaWNlX1BMTikNCiAgICBxdWFudGlsZShwcmljZV9QTE4scHJvYnM9YygwLDAuMSwwLjI1LDAuNSwwLjc1LDAuOTUsMSksbmEucm09VFJVRSkNCmBgYA0KDQpPaywgd2UgaGF2ZSBjYWxjdWxhdGVkIGFsbCBvZiB0aGUgYmFzaWMgc3VtbWFyeSBzdGF0aXN0aWNzIGFib3ZlLiBMZXQncw0Kd3JhcCB0aGVtIHVwIHRvZ2V0aGVyIG5vdy4NCg0KYGBge3Iga2FibGVfcmVwb3J0LCBlY2hvPUZBTFNFfQ0KYXBhcnRtZW50c19saXN0IDwtIHNwbGl0KGFwYXJ0bWVudHMkcHJpY2VfUExOLCBhcGFydG1lbnRzJHJvb21zKQ0KaW5saW5lX3Bsb3QgPC0gZGF0YS5mcmFtZShyb29tcyA9IGMoMSwgMiwgMywgNCksIGJveHBsb3QgPSAiIiwgaGlzdG9ncmFtID0gIiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUxID0gIiIsIGxpbmUyID0gIiIsIHBvaW50czEgPSAiIikNCmlubGluZV9wbG90ICU+JQ0KICBrYmwoYm9va3RhYnMgPSBUUlVFKSAlPiUNCiAga2FibGVfcGFwZXIoZnVsbF93aWR0aCA9IEZBTFNFKSAlPiUNCiAgY29sdW1uX3NwZWMoMiwgaW1hZ2UgPSBzcGVjX2JveHBsb3QoYXBhcnRtZW50c19saXN0KSkgJT4lDQogIGNvbHVtbl9zcGVjKDMsIGltYWdlID0gc3BlY19oaXN0KGFwYXJ0bWVudHNfbGlzdCkpICU+JQ0KICBjb2x1bW5fc3BlYyg0LCBpbWFnZSA9IHNwZWNfcGxvdChhcGFydG1lbnRzX2xpc3QsIHNhbWVfbGltID0gVFJVRSkpICU+JQ0KICBjb2x1bW5fc3BlYyg1LCBpbWFnZSA9IHNwZWNfcGxvdChhcGFydG1lbnRzX2xpc3QsIHNhbWVfbGltID0gRkFMU0UpKSAlPiUNCiAgY29sdW1uX3NwZWMoNiwgaW1hZ2UgPSBzcGVjX3Bsb3QoYXBhcnRtZW50c19saXN0LCB0eXBlID0gInAiKSkNCg0KYGBgDQoNCiMjIyBTdW1tYXJ5IHRhYmxlcw0KDQpPaywgbm93IHdlIHdpbGwgZmluYWxseSBzdW1tYXJpemUgdGhlIGJhc2ljIG1lYXN1cmVzIG9mIGNlbnRyYWwgdGVuZGVuY3kNCmZvciBwcmljZXMgYnkgZGlzdHJpY3QvYnVpbGRpbmcgdHlwZSB1c2luZyB0aGUgJyoqKmthYmxlKioqJyBwYWNrYWdlLg0KRmVlbCBmcmVlIHRvIGN1c3RvbWl6ZSB5b3VyIGZpbmFsIHJlcG9ydC4gU2VlIHNvbWUgaGludHMNCltoZXJlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcXdyYXBzMi92aWduZXR0ZXMvc3VtbWFyeS1zdGF0aXN0aWNzLmh0bWwpLg0KDQpgYGB7ciBrYWJsZV9yZXBvcnQyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcmFwb3J0IDwtIGxpc3QoIlByaWNlIGluIFBMTiIgPQ0KICAgICAgICAgICAgICAgIGxpc3QoIk1pbiIgPSB+IG1pbihwcmljZV9QTE4pLA0KICAgICAgICAgICAgICAgICAgICAgIk1heCIgPSB+IG1heChwcmljZV9QTE4pLA0KICAgICAgICAgICAgICAgICAgICAgIlExIiA9IH4gcXVhbnRpbGUocHJpY2VfUExOLCAwLjI1KSwNCiAgICAgICAgICAgICAgICAgICAgICJNZWRpYW4iID0gfiByb3VuZChtZWRpYW4ocHJpY2VfUExOKSwgMiksDQogICAgICAgICAgICAgICAgICAgICAiUTMiID0gfiBxdWFudGlsZShwcmljZV9QTE4sIDAuNzUpLA0KICAgICAgICAgICAgICAgICAgICAgIk1lYW4iID0gfiByb3VuZChtZWFuKHByaWNlX1BMTiksIDIpLA0KICAgICAgICAgICAgICAgICAgICAgIlNkIiA9IH4gcm91bmQoc2QocHJpY2VfUExOKSwgMiksDQogICAgICAgICAgICAgICAgICAgICAiSVFSIiA9IH4gcm91bmQoSVFSKHByaWNlX1BMTiksIDIpLA0KICAgICAgICAgICAgICAgICAgICAgIlN4IiA9IH4gcm91bmQoSVFSKHByaWNlX1BMTikgLyAyLCAyKSwNCiAgICAgICAgICAgICAgICAgICAgICJWYXIgJSIgPSB+IHJvdW5kKChzZChwcmljZV9QTE4pIC8gbWVhbihwcmljZV9QTE4pKSwgMiksDQogICAgICAgICAgICAgICAgICAgICAiSVFSIFZhciAlIiA9IH4gcm91bmQoKElRUihwcmljZV9QTE4pIC8gbWVkaWFuKHByaWNlX1BMTikpLCAyKSwNCiAgICAgICAgICAgICAgICAgICAgICJTa2V3bmVzcyIgPSB+IHJvdW5kKHNrZXduZXNzKHByaWNlX1BMTiksIDIpLA0KICAgICAgICAgICAgICAgICAgICAgIkt1cnRvc2lzIiA9IH4gcm91bmQoa3VydG9zaXMocHJpY2VfUExOKSwgMikNCiAgICAgICAgICAgICAgICApKQ0KDQojIEdlbmVyYXRlIHRoZSBzdW1tYXJ5IHRhYmxlDQp0YWJlbGEgPC0gc3VtbWFyeV90YWJsZShhcGFydG1lbnRzLCBzdW1tYXJpZXMgPSByYXBvcnQsIGJ5ID0gYygicm9vbXMiKSkNCg0KIyBJbnNwZWN0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIHRhYmVsYSBvYmplY3QNCnN0cih0YWJlbGEpDQoNCiMgRGVmaW5lIGNvbHVtbiBuYW1lcyBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIHJvb21zDQpjb2xfbmFtZXMgPC0gcGFzdGUodW5pcXVlKGFwYXJ0bWVudHMkcm9vbXMpLCAicm9vbXMiKQ0KDQojIFJlbmRlciB0aGUgdGFibGUgdXNpbmcga2JsDQoja2JsKHRhYmVsYSwgY2FwdGlvbiA9ICJUYWJsZSAxLiBBcGFydG1lbnRzIGluIFdyb2NsYXcgLSBwcmljZXMgaW4gUExOIGJ5IG51bWJlciBvZiByb29tcy4iLA0KICMgICBjb2wubmFtZXMgPSBjb2xfbmFtZXMpICU+JQ0KICAja2FibGVfY2xhc3NpYyhmdWxsX3dpZHRoID0gRkFMU0UsIGh0bWxfZm9udCA9ICJDYW1icmlhIikgJT4lDQogICNrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCiMjIyBndHN1bW1hcnkNCg0KV2UgY2FuIGNhbGN1bGF0ZSBlYXNpbHkgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBhbHNvIHVzaW5nIGd0c3VtbWFyeQ0KcGFja2FnZToNCg0KYGBge3J9DQphcGFydG1lbnRzICU+JQ0KICBzZWxlY3QocHJpY2VfUExOLHJvb21zKSAlPiUNCiAgdGJsX3N1bW1hcnkobGFiZWw9IHByaWNlX1BMTiB+ICJQcmljZSIsZGlnaXRzPWMocHJpY2VfUExOKX4yLGJ5PXJvb21zLHR5cGUgPSBhbGxfY29udGludW91cygpIH4gImNvbnRpbnVvdXMyIiwgc3RhdGlzdGljID0gYWxsX2NvbnRpbnVvdXMoKSB+IGMoIntOX25vbm1pc3N9IiwgInttZWRpYW59ICh7cDI1fSwge3A3NX0pIiwgInttaW59LCB7bWF4fSIpLG1pc3NpbmcgPSAibm8iKQ0KYGBgDQoNCiMjIyBkZlN1bW1hcnkNCg0KZGZTdW1tYXJ5KCkgY3JlYXRlcyBhIHN1bW1hcnkgdGFibGUgd2l0aCBzdGF0aXN0aWNzLCBmcmVxdWVuY2llcyBhbmQNCmdyYXBocyBmb3IgYWxsIHZhcmlhYmxlcyBpbiBhIGRhdGEgZnJhbWUuIFRoZSBpbmZvcm1hdGlvbiBkaXNwbGF5ZWQgaXMNCnR5cGUtc3BlY2lmaWMgKGNoYXJhY3RlciwgZmFjdG9yLCBudW1lcmljLCBkYXRlKSBhbmQgYWxzbyB2YXJpZXMNCmFjY29yZGluZyB0byB0aGUgbnVtYmVyIG9mIGRpc3RpbmN0IHZhbHVlcy4NCg0KV2hlbiB1c2luZyBkZlN1bW1hcnkoKSBpbiBSIE1hcmtkb3duIGRvY3VtZW50cywgaXQgaXMgZ2VuZXJhbGx5IGEgZ29vZA0KaWRlYSB0byBleGNsdWRlIGEgY29sdW1uIG9yIHR3byB0byBhdm9pZCBtYXJnaW4gb3ZlcmZsb3cuIFNpbmNlIHRoZQ0KVmFsaWQgYW5kIE1pc3NpbmcgY29sdW1ucyBhcmUgcmVkdW5kYW50LCB3ZSBjYW4gZHJvcCBlaXRoZXIgb25lIG9mIHRoZW0uDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9ImFzaXMifQ0KZGZTdW1tYXJ5KGFwYXJ0bWVudHMsDQogICAgICAgICAgcGxhaW4uYXNjaWkgID0gRkFMU0UsIA0KICAgICAgICAgIHN0eWxlICAgICAgICA9ICJncmlkIiwgDQogICAgICAgICAgZ3JhcGgubWFnbmlmID0gMC43NSwgDQogICAgICAgICAgdmFsaWQuY29sICAgID0gRkFMU0UsDQogICAgICAgICAgdG1wLmltZy5kaXIgID0gIi90bXAiKQ0KYGBgDQoNClRvIHByb2R1Y2Ugb3B0aW1hbCByZXN1bHRzLCBzdW1tYXJ5dG9vbHMgaGFzIGl0cyBvd24gdmVyc2lvbiBvZiB0aGUgYmFzZQ0KYnkoKSBmdW5jdGlvbi4gSXQncyBjYWxsZWQgc3RieSgpLCBhbmQgd2UgdXNlIGl0IGV4YWN0bHkgYXMgd2Ugd291bGQNCmJ5KCk6DQoNCmBgYHtyIHJlc3VsdHM9ImFzaXMiLCB3YXJuaW5nPUZBTFNFfQ0KKHN0YXRzX2J5X3Jvb21zIDwtIHN0YnkoZGF0YSAgICAgID0gYXBhcnRtZW50cywgSU5ESUNFUyAgID0gYXBhcnRtZW50cyRyb29tcywgRlVOICAgICAgID0gZGVzY3IsIHN0YXRzICAgICA9ICJjb21tb24iLCB0cmFuc3Bvc2UgPSBUUlVFKSkNCmBgYA0KDQojIyMgVGlkeSBUYWJsZXMNCg0KV2hlbiBnZW5lcmF0aW5nIGZyZXEoKSBvciBkZXNjcigpIHRhYmxlcywgaXQgaXMgcG9zc2libGUgdG8gdHVybiB0aGUNCnJlc3VsdHMgaW50byAidGlkeSIgdGFibGVzIHdpdGggdGhlIHVzZSBvZiB0aGUgdGIoKSBmdW5jdGlvbiAodGhpbmsgb2YNCnRiIGFzIGEgZGltaW51dGl2ZSBmb3IgdGliYmxlKS4gRm9yIGV4YW1wbGU6DQoNCmBgYHtyfQ0KYXBhcnRtZW50cyAlPiUNCiAgZGVzY3Ioc3RhdHMgPSAiY29tbW9uIikgJT4lDQogIHRiKCkNCmBgYA0KDQpIZXJlIGFyZSBzb21lIGV4YW1wbGVzIHNob3dpbmcgaG93IGxpc3RzIGNyZWF0ZWQgdXNpbmcgc3RieSgpIG9yDQpncm91cF9ieSgpIGNhbiBiZSB0cmFuc2Zvcm1lZCBpbnRvIHRpZHkgdGliYmxlcy4NCg0KYGBge3J9DQpncm91cGVkX2Rlc2NyIDwtIHN0YnkoZGF0YSAgICA9IGFwYXJ0bWVudHMsSU5ESUNFUyA9IGFwYXJ0bWVudHMkcm9vbXMsIEZVTiAgICAgPSBkZXNjciwgc3RhdHMgICA9ICJjb21tb24iKQ0KDQpncm91cGVkX2Rlc2NyICU+JSB0YigpDQpgYGANCg0KIyMjIEEgQnJpZGdlIHRvIE90aGVyIFBhY2thZ2VzDQoNCmBgYHtyfQ0Kc3RieShkYXRhICAgID0gYXBhcnRtZW50cywgDQogICAgIElORElDRVMgPSBhcGFydG1lbnRzJHJvb21zLCANCiAgICAgRlVOICAgICA9IGRlc2NyLCANCiAgICAgc3RhdHMgICA9ICJmaXZlbnVtIikgJT4lDQogIHRiKG9yZGVyID0gMykgJT4lDQogIGthYmxlKGZvcm1hdCA9ICJodG1sIiwgZGlnaXRzID0gMikgJT4lDQogIGNvbGxhcHNlX3Jvd3MoY29sdW1ucyA9IDEsIHZhbGlnbiA9ICJ0b3AiKQ0KYGBgDQoNCiMjIFlvdXIgdHVybiENCg0KWW91ciB0YXNrIHRoaXMgd2VlayBpcyB0bzogcHJlcGFyZSB5b3VyIG93biBkZXNjcmlwdGl2ZSBhbmFseXNpcyBmb3IgdGhlDQoiQ3JlZGl0Q2FyZCIgZGF0YXNldCAoQUVSIHBhY2thZ2UpLiBJdCBpcyBhIGNyb3NzLXNlY3Rpb25hbCBkYXRhZnJhbWUgb24NCnRoZSBjcmVkaXQgaGlzdG9yeSBmb3IgYSBzYW1wbGUgb2YgYXBwbGljYW50cyBmb3IgYSB0eXBlIG9mIGNyZWRpdCBjYXJkLg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KZGF0YShDcmVkaXRDYXJkKQ0KIz9DcmVkaXRDYXJkICByZWFkIGRlc2NyaXB0aW9uIGZpcnN0DQpgYGANCg0KQXJlIHRoZSB5ZWFybHkgaW5jb21lcyAoaW4gVVNEIDEwLDAwMCksIGNyZWRpdCBjYXJkIGV4cGVuZGl0dXJlcywgYWdlLA0KcmF0aW8gb2YgbW9udGhseSBjcmVkaXQgY2FyZCBleHBlbmRpdHVyZSB0byB5ZWFybHkgaW5jb21lIC0NCnNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZvciBhcHBsaWNhbnRzIGZvciBjdXN0b21lcnMgd2l0aCBkaWZmZXJlbnQNCmNyZWRpdCByaXNrICgiY2FyZCIgdmFyaWFibGUgLSBmYWN0b3IpPw0KDQpQcmVwYXJlIGEgcHJvZmVzc2lvbmFsIGRhdGEgdmlzdWFsaXphdGlvbnMsIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MnDQp0YWJsZXMgYW5kIGludGVycHJldCB0aGVtLg0KDQpgYGB7ciBteV9zdW1tYXJ5X3RhYmxlfQ0KIyB5b3VyIGNvZGUgaGVyZQ0KQ3JlZGl0Q2FyZCRhZ2U8LWFzLm51bWVyaWMoQ3JlZGl0Q2FyZCRhZ2UpDQpDcmVkaXRDYXJkJHJlcG9ydHM8LWFzLm51bWVyaWMoQ3JlZGl0Q2FyZCRyZXBvcnRzKQ0KQ3JlZGl0Q2FyZCRpbmNvbWU8LWFzLm51bWVyaWMoQ3JlZGl0Q2FyZCRpbmNvbWUpDQpDcmVkaXRDYXJkJHNoYXJlPC1hcy5udW1lcmljKENyZWRpdENhcmQkc2hhcmUpDQpDcmVkaXRDYXJkJGV4cGVuZGl0dXJlPC1hcy5udW1lcmljKENyZWRpdENhcmQkZXhwZW5kaXR1cmUpDQpDcmVkaXRDYXJkJGNhcmQ8LWFzLmZhY3RvcihDcmVkaXRDYXJkJGNhcmQpDQpgYGANCg0KYGBge3J9DQpjcmVkaXRfY2FyZF95ZXMgPC0gc3Vic2V0KENyZWRpdENhcmQsIGNhcmQgPT0gJ3llcycpDQpjcmVkaXRfY2FyZF9ubyA8LSBzdWJzZXQoQ3JlZGl0Q2FyZCwgY2FyZCA9PSAnbm8nKQ0KYGBgDQoNCmBgYHtyfQ0KY3JlZGl0X2NhcmRfeWVzIDwtIHN1YnNldChDcmVkaXRDYXJkLCBjYXJkID09ICd5ZXMnKQ0KY3JlZGl0X2NhcmRfbm8gPC0gc3Vic2V0KENyZWRpdENhcmQsIGNhcmQgPT0gJ25vJykNCg0KYWdlX3llcyA8LSBjcmVkaXRfY2FyZF95ZXMkYWdlDQphZ2Vfbm8gPC0gY3JlZGl0X2NhcmRfbm8kYWdlDQoNCnN1bW1hcnkoYWdlX3llcykNCnN1bW1hcnkoYWdlX25vKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fZGVuc2l0eShkYXRhID0gY3JlZGl0X2NhcmRfeWVzLCBhZXMoeCA9IGFnZSwgZmlsbCA9ICJDYXJkIE93bmVyIiksIGNvbG9yID0gImNvcmFsNCIsIGFscGhhID0gMC43KSArDQogIGdlb21fZGVuc2l0eShkYXRhID0gY3JlZGl0X2NhcmRfbm8sIGFlcyh4ID0gYWdlLCBmaWxsID0gIk5vbi1Pd25lciIpLCBjb2xvciA9ICJwaW5rIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh4ID0gIkFnZSIsIHkgPSAiRGVuc2l0eSIsIHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBBZ2UgYnkgQ2FyZCBPd25lcnNoaXAiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNhcmQgT3duZXIiID0gImNvcmFsNCIsICJOb24tT3duZXIiID0gInBpbmsiKSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJDYXJkIE93bmVyc2hpcCIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk93bmVyIiwgIk5vbi1Pd25lciIpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICBnZW9tX2JveHBsb3QoZGF0YSA9IGNyZWRpdF9jYXJkX3llcywgYWVzKHggPSAiQ2FyZCBPd25lciIsIHkgPSBhZ2UsIGZpbGwgPSAiQ2FyZCBPd25lciIpLCBjb2xvciA9ICJjb3JhbDQiLCBhbHBoYSA9IDAuNykgKw0KICBnZW9tX2JveHBsb3QoZGF0YSA9IGNyZWRpdF9jYXJkX25vLCBhZXMoeCA9ICJOb24tT3duZXIiLCB5ID0gYWdlLCBmaWxsID0gIk5vbi1Pd25lciIpLCBjb2xvciA9ICJwaW5rIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh4ID0gIkNhcmQgT3duZXJzaGlwIiwgeSA9ICJBZ2UiLCB0aXRsZSA9ICJCb3hwbG90IG9mIEFnZSBieSBDYXJkIE93bmVyc2hpcCIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQ2FyZCBPd25lciIgPSAiY29yYWw0IiwgIk5vbi1Pd25lciIgPSAicGluayIpLA0KICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNhcmQgT3duZXJzaGlwIiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiT3duZXIiLCAiTm9uLU93bmVyIikpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpgYGB7cn0NCnN1bW1hcnlfZGZfeWVzIDwtIGRhdGEuZnJhbWUoDQogIFN0YXRpc3RpYyA9IGMoIk1lYW4iLCAiTWVkaWFuIiwgIlN0YW5kYXJkIERldmlhdGlvbiIsICJWYXJpYW5jZSIsICJDb2VmZmljaWVudCBvZiBWYXJpYWJpbGl0eSAoJSkiLCANCiAgICAgICAgICAgICAgICAiSW50ZXJxdWFydGlsZSBSYW5nZSAoSVFSKSIsICJJbnRlcnF1YXJ0aWxlIERldmlhdGlvbiIsICJJUVIgQ29lZmZpY2llbnQgb2YgVmFyaWFiaWxpdHkgKCUpIiwNCiAgICAgICAgICAgICAgICAiTWluaW11bSIsICJNYXhpbXVtIiksDQogIENhcmRfT3duZXJzID0gYygNCiAgICBtZWFuKGFnZV95ZXMpLCBtZWRpYW4oYWdlX3llcyksIHNkKGFnZV95ZXMpLCB2YXIoYWdlX3llcyksDQogICAgc2QoYWdlX3llcykgLyBtZWFuKGFnZV95ZXMpICogMTAwLCBJUVIoYWdlX3llcyksIElRUihhZ2VfeWVzKSAvIDIsDQogICAgKElRUihhZ2VfeWVzKSAvIDIpIC8gbWVkaWFuKGFnZV95ZXMpICogMTAwLCBtaW4oYWdlX3llcyksIG1heChhZ2VfeWVzKQ0KICApDQopDQoNCnN1bW1hcnlfZGZfbm8gPC0gZGF0YS5mcmFtZSgNCiAgU3RhdGlzdGljID0gYygiTWVhbiIsICJNZWRpYW4iLCAiU3RhbmRhcmQgRGV2aWF0aW9uIiwgIlZhcmlhbmNlIiwgIkNvZWZmaWNpZW50IG9mIFZhcmlhYmlsaXR5ICglKSIsIA0KICAgICAgICAgICAgICAgICJJbnRlcnF1YXJ0aWxlIFJhbmdlIChJUVIpIiwgIkludGVycXVhcnRpbGUgRGV2aWF0aW9uIiwgIklRUiBDb2VmZmljaWVudCBvZiBWYXJpYWJpbGl0eSAoJSkiLA0KICAgICAgICAgICAgICAgICJNaW5pbXVtIiwgIk1heGltdW0iKSwNCiAgTm9uX0NhcmRfT3duZXJzID0gYygNCiAgICBtZWFuKGFnZV9ubyksIG1lZGlhbihhZ2Vfbm8pLCBzZChhZ2Vfbm8pLCB2YXIoYWdlX25vKSwNCiAgICBzZChhZ2Vfbm8pIC8gbWVhbihhZ2Vfbm8pICogMTAwLCBJUVIoYWdlX25vKSwgSVFSKGFnZV9ubykgLyAyLA0KICAgIChJUVIoYWdlX25vKSAvIDIpIC8gbWVkaWFuKGFnZV9ubykgKiAxMDAsIG1pbihhZ2Vfbm8pLCBtYXgoYWdlX25vKQ0KICApDQopDQoNCnN1bW1hcnlfY29tYmluZWQgPC0gbWVyZ2Uoc3VtbWFyeV9kZl95ZXMsIHN1bW1hcnlfZGZfbm8sIGJ5ID0gIlN0YXRpc3RpYyIsIGFsbCA9IFRSVUUpDQoNCmtuaXRyOjprYWJsZShzdW1tYXJ5X2NvbWJpbmVkLCBmb3JtYXQgPSAibWFya2Rvd24iLCBjYXB0aW9uID0gIlN1bW1hcnkgU3RhdGlzdGljcyBmb3IgQWdlIGJ5IENhcmQgT3duZXJzaGlwIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIiwgcG9zaXRpb24gPSAiY2VudGVyIikNCmBgYA0KDQoNClN1bW1pbmcgdXAgdGhlIGRhdGEgc2hvd2luZyBmcm9tIHRoZSB0YWJsZSBhbmQgdHdvIHBsb3RzIG9mIGFnZSBvZiBwZW9wbGUgdXNpbmcgY3JlZGl0IGNhcmRzIGFuZCBhZ2Ugb2YgcGVvcGxlIHdobyBkb24ndCB3ZSBjYW4gcHJlc2VudCBzb21lIGludGVyZXN0aW5nIGluZm9ybWF0aW9uOg0KDQotIG1heGltdW0gYWdlIGlzIGJpZ2dlciBpbiB0aGUgZ3JvdXAgdXNpbmcgY3JlZGl0IGNhcmRzLCB3aGljaCBtb3N0IHBlb3BsZSB3b3VsZCB0aGluayBpcyBvcHBvc2l0ZWx5LA0KDQotIHRoZXJlIGlzIG1vcmUgcGVvcGxlIGFyb3VuZCBhZ2UgMjUgd2hvIGFyZSBvd25lcnMgb2YgY3JlZGl0IGNhcmQgdGhlbiBwZW9wbGUgYXJvdW5kIHNhbWUgYWdlIG5vdCBvd2luZyBjcmVkaXQgY2FyZHMsDQoNCi0gcGVvcGxlIG93aW5pbmcgY2VkaXQgY2FyZHMgYXJlIG1vc3RseSB5b3VuZyBwZW9wbGUsIHdlIGNhbiBzZWUgYmlnIGRlY2xpbmUgYXJvdW5kIGFnZSAyNyB0byAzNSwNCg0KLSBpbiB0aGUgZ3JvdXAgb3duaW5nIGNyZWRpdCBjYXJkIGFnZSBpcyBtb3JlIGRpdmVyc2UsDQoNCi0gaW4gYm90aCBncm91cHMgd2UgaGF2ZSBzb21lIGRhdGEgZXJyb3JzIGJlY2F1c2Ugd2UgY2FuIHNlZSBzb21lIG91dGxpbmVyIGFyb3VuZCAwLCB3aGljaCBpcyBpbXBvc3NpYmxlIGluIHJlYWwgbGlmZSwNCg0KDQpgYGB7cn0NCmluY29tZV95ZXMgPC0gY3JlZGl0X2NhcmRfeWVzJGluY29tZQ0KaW5jb21lX25vIDwtIGNyZWRpdF9jYXJkX25vJGluY29tZQ0KDQpzdW1tYXJ5KGluY29tZV95ZXMpDQpzdW1tYXJ5KGluY29tZV9ubykNCg0KZ2dwbG90KCkgKw0KICBnZW9tX2RlbnNpdHkoZGF0YSA9IGNyZWRpdF9jYXJkX3llcywgYWVzKHggPSBpbmNvbWUsIGZpbGwgPSAiQ2FyZCBPd25lciIpLCBjb2xvciA9ICJ2aW9sZXRyZWQyIiwgYWxwaGEgPSAwLjcpICsNCiAgZ2VvbV9kZW5zaXR5KGRhdGEgPSBjcmVkaXRfY2FyZF9ubywgYWVzKHggPSBpbmNvbWUsIGZpbGwgPSAiTm9uLU93bmVyIiksIGNvbG9yID0gInZpb2xldCIsIGFscGhhID0gMC43KSArDQogIGxhYnMoeCA9ICJJbmNvbWUiLCB5ID0gIkRlbnNpdHkiLCB0aXRsZSA9ICJEZW5zaXR5IFBsb3Qgb2YgSW5jb21lIChpbiBVU0QgMTAsMDAwKSBieSBDYXJkIE93bmVycyIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQ2FyZCBPd25lciIgPSAidmlvbGV0cmVkMiIsICJOb24tT3duZXIiID0gInZpb2xldCIpLA0KICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNhcmQgT3duZXJzaGlwIiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiT3duZXIiLCAiTm9uLW93bmVyIikpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogIGdlb21fYm94cGxvdChkYXRhID0gY3JlZGl0X2NhcmRfeWVzLCBhZXMoeCA9ICJDYXJkIE93bmVyIiwgeSA9IGluY29tZSwgZmlsbCA9ICJDYXJkIE93bmVyIiksIGNvbG9yID0gInZpb2xldHJlZDIiLCBhbHBoYSA9IDAuNykgKw0KICBnZW9tX2JveHBsb3QoZGF0YSA9IGNyZWRpdF9jYXJkX25vLCBhZXMoeCA9ICJOb24tT3duZXIiLCB5ID0gaW5jb21lLCBmaWxsID0gIk5vbi1Pd25lciIpLCBjb2xvciA9ICJ2aW9sZXQiLCBhbHBoYSA9IDAuNykgKw0KICBsYWJzKHggPSAiQ2FyZCBPd25lcnNoaXAiLCB5ID0gIkluY29tZSAoaW4gVVNEIDEwLDAwMCkiLCB0aXRsZSA9ICJCb3hwbG90IG9mIEluY29tZSAoaW4gVVNEIDEwLDAwMCkgYnkgQ2FyZCBPd25lcnNoaXAiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNhcmQgT3duZXIiID0gInZpb2xldHJlZDIiLCAiTm9uLU93bmVyIiA9ICJ2aW9sZXQiKSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJDYXJkIE93bmVyc2hpcCIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk93bmVyIiwgIk5vbi1Pd25lciIpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3J9DQpzdW1tYXJ5X2RmX3llcyA8LSBkYXRhLmZyYW1lKA0KICBTdGF0aXN0aWMgPSBjKCJNZWFuIiwgIk1lZGlhbiIsICJTdGFuZGFyZCBEZXZpYXRpb24iLCAiVmFyaWFuY2UiLCAiQ29lZmZpY2llbnQgb2YgVmFyaWFiaWxpdHkgKCUpIiwgDQogICAgICAgICAgICAgICAgIkludGVycXVhcnRpbGUgUmFuZ2UgKElRUikiLCAiSW50ZXJxdWFydGlsZSBEZXZpYXRpb24iLCAiSVFSIENvZWZmaWNpZW50IG9mIFZhcmlhYmlsaXR5ICglKSIsDQogICAgICAgICAgICAgICAgIk1pbmltdW0iLCAiTWF4aW11bSIpLA0KICBDYXJkX093bmVycyA9IGMoDQogICAgbWVhbihpbmNvbWVfeWVzKSwgbWVkaWFuKGluY29tZV95ZXMpLCBzZChpbmNvbWVfeWVzKSwgdmFyKGluY29tZV95ZXMpLA0KICAgIHNkKGluY29tZV95ZXMpIC8gbWVhbihpbmNvbWVfeWVzKSAqIDEwMCwgSVFSKGluY29tZV95ZXMpLCBJUVIoaW5jb21lX3llcykgLyAyLA0KICAgIChJUVIoaW5jb21lX3llcykgLyAyKSAvIG1lZGlhbihpbmNvbWVfeWVzKSAqIDEwMCwgbWluKGluY29tZV95ZXMpLCBtYXgoaW5jb21lX3llcykNCiAgKQ0KKQ0KDQpzdW1tYXJ5X2RmX25vIDwtIGRhdGEuZnJhbWUoDQogIFN0YXRpc3RpYyA9IGMoIk1lYW4iLCAiTWVkaWFuIiwgIlN0YW5kYXJkIERldmlhdGlvbiIsICJWYXJpYW5jZSIsICJDb2VmZmljaWVudCBvZiBWYXJpYWJpbGl0eSAoJSkiLCANCiAgICAgICAgICAgICAgICAiSW50ZXJxdWFydGlsZSBSYW5nZSAoSVFSKSIsICJJbnRlcnF1YXJ0aWxlIERldmlhdGlvbiIsICJJUVIgQ29lZmZpY2llbnQgb2YgVmFyaWFiaWxpdHkgKCUpIiwNCiAgICAgICAgICAgICAgICAiTWluaW11bSIsICJNYXhpbXVtIiksDQogIE5vbl9DYXJkX093bmVycyA9IGMoDQogICAgbWVhbihpbmNvbWVfbm8pLCBtZWRpYW4oaW5jb21lX25vKSwgc2QoaW5jb21lX25vKSwgdmFyKGluY29tZV9ubyksDQogICAgc2QoaW5jb21lX25vKSAvIG1lYW4oaW5jb21lX25vKSAqIDEwMCwgSVFSKGluY29tZV9ubyksIElRUihpbmNvbWVfbm8pIC8gMiwNCiAgICAoSVFSKGluY29tZV9ubykgLyAyKSAvIG1lZGlhbihpbmNvbWVfbm8pICogMTAwLCBtaW4oaW5jb21lX25vKSwgbWF4KGluY29tZV9ubykNCiAgKQ0KKQ0KDQpzdW1tYXJ5X2NvbWJpbmVkIDwtIG1lcmdlKHN1bW1hcnlfZGZfeWVzLCBzdW1tYXJ5X2RmX25vLCBieSA9ICJTdGF0aXN0aWMiLCBhbGwgPSBUUlVFKQ0KDQprbml0cjo6a2FibGUoc3VtbWFyeV9jb21iaW5lZCwgZm9ybWF0ID0gIm1hcmtkb3duIiwgY2FwdGlvbiA9ICJTdW1tYXJ5IFN0YXRpc3RpY3MgZm9yIEluY29tZSBieSBDYXJkIE93bmVyc2hpcCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIHBvc2l0aW9uID0gImNlbnRlciIpDQoNCmBgYA0KQW5hbGl6aW5nIGRhdGEgZnJvbSB0aGUgcGxvdHMgYW5kIHRhYmxlIHdlIGNhbiBjb25jbHVkZToNCg0KLSB0aGVyZSBpcyBiaWdnZXIgZGl2ZXJzaXR5IGluIHRoZSBpbmNvbWUgb2YgY2FyZCBvd25lcnMsIA0KDQotIGlmIHdlIGdyb3VwIGNhcmQgb3duZXJzIGJ5IGhleSBpbmNvbWUgd2UgY2FuIHRlbGwgdGhhdCB0aGUgbW9zdCBudW1lcm91cyBncm91cCBpcyB0aGUgb25lIG9mIGluY29tZSAyNTAwMCBVU0QsDQoNCi0gaWYgd2UgZG8gdGhlIHNhbWUgd2l0aCBub24gY2FyZCBvd25lcnMgdGhlbiB0aGUgYmlnZ2VzdCBncm91cCBpcyB0aGUgb25lIG9mIGluY29tZSBhcm91bmQgMjAwMDAgVVNELA0KDQotIHBlb3BsZSB3aXRoIGNyZWRpdCBjYXJkcyBoYXZlIGJpZ2dlciBhdmVyYWdlIGluY29tZSB0aGVuIHBlb3BsZSB3aXRob3V0IHRoZW0sDQoNCi0gbWF4aW11bSBpbmNvbWUgb2YgY3JlZGl0IGNhcmQgb3duZXJzIGlzIGJpZ2dlciB0aGVuIGluIG5vbiBvd25lcnMuDQoNCg0KYGBge3J9DQppbmNvbWVfYWdlX3llcyA8LSBjcmVkaXRfY2FyZF95ZXNbLCBjKCJpbmNvbWUiLCAiYWdlIildDQpzdW1tYXJ5KGluY29tZV9hZ2VfeWVzKQ0KaW5jb21lX2FnZV9ubyA8LSBjcmVkaXRfY2FyZF9ub1ssIGMoImluY29tZSIsICJhZ2UiKV0NCnN1bW1hcnkoaW5jb21lX2FnZV9ubykNCg0KZ2dwbG90KGluY29tZV9hZ2VfeWVzLCBhZXMoeCA9IGFnZSwgeSA9IGluY29tZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic2t5Ymx1ZSIsIGNvbG9yID0gInNreWJsdWUiKSArDQogIGxhYnMoeCA9ICJBZ2UiLCB5ID0gIkluY29tZSAoaW4gVVNEIDEwLDAwMCkiLCB0aXRsZSA9ICJJbmNvbWUgYnkgQWdlIChGb3IgUGVvcGxlIHdpdGggQ2FyZHMpIikgKw0KICB0aGVtZV9ncmV5KCkNCg0KZ2dwbG90KGluY29tZV9hZ2Vfbm8sIGFlcyh4ID0gYWdlLCB5ID0gaW5jb21lKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJwaW5rIiwgY29sb3IgPSAicGluayIpICsNCiAgbGFicyh4ID0gIkFnZSIsIHkgPSAiSW5jb21lIChpbiBVU0QgMTAsMDAwKSIsIHRpdGxlID0gIkluY29tZSBieSBBZ2UgKEZvciBQZW9wbGUgV2l0aG91dCBDYXJkcykiKSArDQogIHRoZW1lX2dyZXkoKQ0KYGBgDQpOb3cgbGV0cyBjb21wYXJlIGluY29tZSBieSBhZ2UgaW4gYm90aCBvZiBoZSBncm91cHM6DQotIHdpdGggY3JlZGl0IGNhcmRzIGJpZ2dlc3QgaW5jb21lIGhhdmUgcGVvcGxlIGFyb3VuZCBhZ2Ugb2YgNDIsIHdoaWxlIGZvciBub24gLSBvd25lcnMgdGhpcyBhZ2UgaXMgYSBsaXR0bGUgYml0IHNtYWxsZXIsIGJlY2F1c2UgaXQncyBhcm91bmQgMzUuDQoNCi0gd2UgY2FuIHNlZSBiaWdnZXIgZGlmZmVyZW5jZXMgaW4gaW5jb21lIG9mIGRpZmZlcmVudCBhZ2UgZ3JvdXBzIGZvciBjYXJkIG93bmVycyBjb21wYXJpbmcgdG8gbm9uLW93bmVycywgDQoNCi0gdGhlcmUgaXMgbW9yZSBhZ2UgZ3JvdXBzIGxhY2tpbmcgaW4gdGhlIG5vbi1vd25lcnMsIHdoaWNoIGlzIG9ubHkgYXNzdXJpbmcgdXMgdGhhdCB0aGVyZSBpcyBiaWdnZXIgYWdlIGRpdmVyc2l0eSBpbiB0aGUgY2FyZCBvd25lcnMgZ3JvdXAsDQoNCi0gYWdhaW4gd2UgY2FuIHNlZSBzb21lIGVycm9ycyBpbiBkYXRhIHNldCwgYmVjYXVzZSBoZXJlIGlzIGdyb3VwIG9mIHBlb3BsZSBhcm91bmQgYWdlIDAgd2hpY2ggaGFzIHNvbWUgaW5jb21lIGFuZCBpdCdzIHJhdGhlciBpbXBvc3NpYmxlIGluIHJlYWwgbGlmZSwgc28gYmFzZWQgb24gdGhpcyB3ZSBjYW4gYXNzdW1lIHRoYXQgdGhvc2UgYXJlIGVycm9ycy4NCg0KDQpgYGB7cn0NCmdncGxvdCgpICsNCiAgZ2VvbV9ib3hwbG90KGRhdGEgPSBjcmVkaXRfY2FyZF95ZXMsIGFlcyh4ID0gIkNhcmQgT3duZXIiLCB5ID0gc2hhcmUsIGZpbGwgPSAiQ2FyZCBPd25lciIpLCBjb2xvciA9ICJ0dXJxdW9pc2UyIiwgYWxwaGEgPSAwLjcpICsNCiAgZ2VvbV9ib3hwbG90KGRhdGEgPSBjcmVkaXRfY2FyZF9ubywgYWVzKHggPSAiTm9uLU93bmVyIiwgeSA9IHNoYXJlLCBmaWxsID0gIk5vbi1Pd25lciIpLCBjb2xvciA9ICJwdXJwbGUiLCBhbHBoYSA9IDAuNykgKw0KICBsYWJzKHggPSAiQ2FyZCBPd25lcnNoaXAiLCB5ID0gIlNoYXJlIiwgdGl0bGUgPSAiQm94cGxvdCBvZiBTaGFyZSBieSBDYXJkIE93bmVyc2hpcCIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQ2FyZCBPd25lciIgPSAidHVycXVvaXNlMiIsICJOb24tT3duZXIiID0gInB1cnBsZSIpLA0KICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNhcmQgT3duZXJzaGlwIiwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiT3duZXIiLCAiTm9uLU93bmVyIikpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpgYGB7cn0NCmdncGxvdCgpICsNCiAgZ2VvbV9kZW5zaXR5KGRhdGEgPSBjcmVkaXRfY2FyZF95ZXMsIGFlcyh4ID0gc2hhcmUsIGZpbGwgPSAiQ2FyZCBPd25lciIpLCBjb2xvciA9ICJ0dXJxdW9pc2UyIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh4ID0gIlNoYXJlIiwgeSA9ICJEZW5zaXR5IiwgdGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIFNoYXJlIGJ5IENhcmQgT3duZXJzIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJDYXJkIE93bmVyIiA9ICJ0dXJxdW9pc2UyIiksDQogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2FyZCBPd25lcnNoaXAiLA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJPd25lciIpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KYGBge3J9DQpzaGFyZV95ZXMgPC0gY3JlZGl0X2NhcmRfeWVzJHNoYXJlDQpzaGFyZV9ubyA8LSBjcmVkaXRfY2FyZF9ubyRzaGFyZQ0KDQpzdW1tYXJ5X2RmX3llcyA8LSBkYXRhLmZyYW1lKA0KICBTdGF0aXN0aWMgPSBjKCJNZWFuIiwgIk1lZGlhbiIsICJTdGFuZGFyZCBEZXZpYXRpb24iLCAiVmFyaWFuY2UiLCAiQ29lZmZpY2llbnQgb2YgVmFyaWFiaWxpdHkgKCUpIiwgDQogICAgICAgICAgICAgICAgIkludGVycXVhcnRpbGUgUmFuZ2UgKElRUikiLCAiSW50ZXJxdWFydGlsZSBEZXZpYXRpb24iLCAiSVFSIENvZWZmaWNpZW50IG9mIFZhcmlhYmlsaXR5ICglKSIsDQogICAgICAgICAgICAgICAgIk1pbmltdW0iLCAiTWF4aW11bSIpLA0KICBDYXJkX093bmVycyA9IGMoDQogICAgbWVhbihzaGFyZV95ZXMpLCBtZWRpYW4oc2hhcmVfeWVzKSwgc2Qoc2hhcmVfeWVzKSwgdmFyKHNoYXJlX3llcyksDQogICAgc2Qoc2hhcmVfeWVzKSAvIG1lYW4oc2hhcmVfeWVzKSAqIDEwMCwgSVFSKHNoYXJlX3llcyksIElRUihzaGFyZV95ZXMpIC8gMiwNCiAgICAoSVFSKHNoYXJlX3llcykgLyAyKSAvIG1lZGlhbihzaGFyZV95ZXMpICogMTAwLCBtaW4oc2hhcmVfeWVzKSwgbWF4KHNoYXJlX3llcykNCiAgKQ0KKQ0KDQpzdW1tYXJ5X2RmX25vIDwtIGRhdGEuZnJhbWUoDQogIFN0YXRpc3RpYyA9IGMoIk1lYW4iLCAiTWVkaWFuIiwgIlN0YW5kYXJkIERldmlhdGlvbiIsICJWYXJpYW5jZSIsICJDb2VmZmljaWVudCBvZiBWYXJpYWJpbGl0eSAoJSkiLCANCiAgICAgICAgICAgICAgICAiSW50ZXJxdWFydGlsZSBSYW5nZSAoSVFSKSIsICJJbnRlcnF1YXJ0aWxlIERldmlhdGlvbiIsICJJUVIgQ29lZmZpY2llbnQgb2YgVmFyaWFiaWxpdHkgKCUpIiwNCiAgICAgICAgICAgICAgICAiTWluaW11bSIsICJNYXhpbXVtIiksDQogIE5vbl9DYXJkX093bmVycyA9IGMoDQogICAgbWVhbihzaGFyZV9ubyksIG1lZGlhbihzaGFyZV9ubyksIHNkKHNoYXJlX25vKSwgdmFyKHNoYXJlX25vKSwNCiAgICBzZChzaGFyZV9ubykgLyBtZWFuKHNoYXJlX25vKSAqIDEwMCwgSVFSKHNoYXJlX25vKSwgSVFSKHNoYXJlX25vKSAvIDIsDQogICAgKElRUihzaGFyZV9ubykgLyAyKSAvIG1lZGlhbihzaGFyZV9ubykgKiAxMDAsIG1pbihzaGFyZV9ubyksIG1heChzaGFyZV9ubykNCiAgKQ0KKQ0KDQpzdW1tYXJ5X2NvbWJpbmVkIDwtIG1lcmdlKHN1bW1hcnlfZGZfeWVzLCBzdW1tYXJ5X2RmX25vLCBieSA9ICJTdGF0aXN0aWMiLCBhbGwgPSBUUlVFKQ0KDQprbml0cjo6a2FibGUoc3VtbWFyeV9jb21iaW5lZCwgZm9ybWF0ID0gIm1hcmtkb3duIiwgY2FwdGlvbiA9ICJTdW1tYXJ5IFN0YXRpc3RpY3MgZm9yIEFnZSBieSBDYXJkIE93bmVyc2hpcCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIHBvc2l0aW9uID0gImNlbnRlciIpDQoNCmBgYA0KVGhvc2UgcGxvdHMgYW5kIHRhYmxlIHNob3dzIHVzIHNoYXJlIChSYXRpbyBvZiBtb250aGx5IGNyZWRpdCBjYXJkIGV4cGVuZGl0dXJlIHRvIHllYXJseSBpbmNvbWUpIG9mIGNhcmQgb3duZXJzIGFuZCBub24tb3duZXJzLCBhbmQgaXQgc2hvd3MgdXMgc29tZXRoaW5nIHZlcnkgaW50ZXJlc3RpbmcuIFdlIGNvdWxkIGFzc3VtZSB0aGF0IHNoYXJlIGZvciBub24tb3duZXJzIHNob3VsZCBiZSAwLCBiZWNhdXNlIGhvdyBzb21lb25lIHdpdGhvdXQgY3JlZGl0IGNhcmQgY2FuIGhhdmUgYSByYXRpbyBvZiBtb250aGx5IGNyZWRpdCBjYXJkIGV4cGVuZGl0dXJlIHRvIHllYXJseSBpbmNvbWUsIGJ1dCBmcm9tIHRoZSB0YWJsZSBpdCdzIHNheXMgdGhhdCB0aG9zZSBhcmUgZXh0cmVtZWx5IHNtYWxsIG51bWJlcnMsIHdoaWNoIHdlIGNhbiBhc3N1bWUgYXJlIGp1c3QgaW5jb3JyZWN0IHZhbHVlcy4gDQoNClNvIHdlIHdpbGwgYmUgb25seSBmb2N1c2luZyBvbiB0aGUgZ3JvdXAgb2YgY2FyZCBvd25lcnMgd2hpbGUgYW5hbGlzaW5nIHRoZSBkYXRhLg0KDQotIGRpdmVyc2l0IG9mIHRoZSBzaGFyZSBpcyBxdWl0ZSBiaWcsDQoNCi0gd2hlbiB3ZSBncm9wdSBwZW9wbGUgYnkgdGhlaXIgc2hhcmUgd2UgY2FuIHNheSwgdGhhdCBtb3N0IG51bWVyb3VzIGdydW9wIGlzIG9mIHBlb3BsZSBoYXZpbmcgc2hhcmUgYXJvdW5kIDAuMTIsDQoNCi0gbWF4aW11bSBzaGFyZSBpcyAwLjkgd2hpY2ggaXMgYWxtb3N0IDEgc28gaXQgd291bGQgbWVhbiB0aGF0IHRob2VzIHBlb3BsZSBzcGVuZCBhbGwgdGhlaXIgc2FsYXJ5IGJ5IGNyZWRpdCBjYXJkLA0KDQpgYGB7cn0NCmV4cGVuZGl0dXJlc19hZ2U8LWNyZWRpdF9jYXJkX3llc1ssIGMoImV4cGVuZGl0dXJlIiwgImFnZSIpXQ0KDQpzdW1tYXJ5KGV4cGVuZGl0dXJlc19hZ2UpDQoNCmdncGxvdChleHBlbmRpdHVyZXNfYWdlLCBhZXMoeCA9IGFnZSwgeSA9IGV4cGVuZGl0dXJlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJtYXJvb24iLCBjb2xvciA9ICJtYXJvb24iKSArDQogIGxhYnMoeCA9ICJBZ2UiLCB5ID0gIkV4cGVuZGl0dXJlIiwgdGl0bGUgPSAiRXhwZW5kaXR1cmUgYnkgQWdlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KTm93IGp1c3QgcXVpY2sgY29uY2x1c2lvbiBmcm9tIHRoaXMgYmFyIHBsb3Q6DQoNCi0gbW9zdGx5IHlvdW5nIHBlb3BsZSBhcmUgcGF5aW5nIHdpdGggY3JlZGl0IGNhcmQsDQoNCi0gYmlnZ2VzdCBleHBlbmRpdHVyZSBoYXZlIHBlb3BsZSBhcm91bmQgMzUsDQoNCi0gZXhwZWR0aXR1cmUgb2YgcGVvcGxlIGFmdGVyIDUwIGlzIGRlY3JlYXNpbmcsDQoNCi0gYWdhaW4gd2Ugc2VlIGluY29ycmVjdCBkYXRhIGFyb3VuZCBhZ2UgMC4NCg0KYGBge3J9DQpzaGFyZV9hZ2U8LWNyZWRpdF9jYXJkX3llc1ssIGMoInNoYXJlIiwgImFnZSIpXQ0KDQpzdW1tYXJ5KHNoYXJlX2FnZSkNCg0KZ2dwbG90KHNoYXJlX2FnZSwgYWVzKHggPSBhZ2UsIHkgPSBzaGFyZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic2xhdGVibHVlMiIgLGNvbG9yID0gInNsYXRlYmx1ZTIiKSArDQogIGxhYnMoeCA9ICJBZ2UiLCB5ID0gIlNoYXJlIiwgdGl0bGUgPSAiUmF0aW8gb2YgbW9udGhseSBjcmVkaXQgY2FyZCBleHBlbmRpdHVyZSB0byB5ZWFybHkgaW5jb21lIGJ5IEFnZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCkFuZCBxdWljayBhc3N1bXB0aW9ucyBmcm9tIHRoaXMgb25lOg0KDQotIGJpZ2dlc3Qgc2hhcmUgaGF2ZSBwZW9wbGUgYXJvdW5kIGFnZSBvZiAyNSwgDQoNCi0gd2UgY2FuIHNlZSB0aGF0IGFmdGVyIGFnZSBvZiA0MCB0aGUgc2hhcmUgaXMgd2F5IHNtYWxsZXIgY29tcGVyaW5nIHRvIHlvdW5nZXIgcGVvcGxlLA0KDQotIGFnYWluIHdlIHNlZSBzb21lIGluY29ycmVjdCBkYXRhIHZhbHVlcyBhcm91bmQgYWdlIDAuDQoNCg0KDQoNCg0KDQpTdW1taW5nIHVwIGFsbCB0aGUgZGF0YSBhbmFseXNpcyB3ZSBoYXZlIGZldyBtb3N0IGltcG9ydGFudCBjb25jbHVzaW9uczoNCg0KLSBtb3JlIHBlb3BsZSB1c2UgY3JlZGl0IGNhcmQgdGhlbiBub3QsIA0KDQotIGJpZ2dlc3QgZ3JvdXAgYWdlIG9mIGNyZWRpdCBjYXJkIHVzZXJzIGFyZSB5b3VuZyBwZW9wbGUsIGFyb3VuZCBhZ2UgMjAtMzAsDQoNCi0gcGVvcGxlIHdobyBoYXZlIGJpZ2dlciBpbmNvbWUgdGhleSB0ZW5kIHRvIGhhdmUgY3JlZGl0IGNhcmRzLA0KDQotIHlvdW5nIHBlb3BsZSB1c2UgY3JlZGl0IGNhcmQgbW9yZSBmcmVxdWVudGx5IHRoZW4gcGVvcGxlIGFmdGVyIDQwcy4NCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=