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.

Preprocessing

income<-as.numeric(CreditCard$income)
expenditure<-as.numeric(CreditCard$expenditure)
age<-as.integer(CreditCard$age)
card<-as.factor(CreditCard$card)

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 income 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:

Incomes in kPLN
x label Freq Percent Valid Percent Cumulative Percent
Valid 0-2 kPLN 236 17.9 17.9 17.9
2-4 kPLN 783 59.4 59.4 77.3
4-6 kPLN 205 15.5 15.5 92.8
6-8 kPLN 63 4.8 4.8 97.6
8-10 kPLN 23 1.7 1.7 99.3
10-12 kPLN 7 0.5 0.5 99.8
12-14 kPLN 2 0.2 0.2 100.0
Total 1319 100.0 100.0
Missing <blank> 0 0.0
<NA> 0 0.0
Total 1319 100.0
##        # classes  Goodness of fit Tabular accuracy 
##        7.0000000        0.9085328        0.6568085

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

Income

Based on these graphs we can observe a few things. First of all, in the first two graphs we can see that the income line of those who do not have cards is slightly shifted to the left, which means that their income is smaller compared to those with cards. We confirmed this observation by generating the mean of income for those two groups of people, where we can clearly see the difference. Moreover, the boxplot graphs show that the more extreme income values (above 10) occur mostly in the card-holders group with quite an advantage. Based on all that information there seems to be little corelation between income and card factor, however the difference is more significant when it comes to the range of income than its average

Expenditure

How about the relation between expenditure and card factor?

By this single plot we can observe that expenditure is highly dependent from card factor. As shown above, expenditure of card non-holders in all cases is close if not equal to zero, whereas for the other group this variable is in range from 0 to so much as 3000.

    mean(expenditure)
## [1] 185.0571
    median(expenditure)
## [1] 101.2983
    sd(expenditure) #standard deviation
## [1] 272.2189
    var(expenditure) #variance
## [1] 74103.14
    coeff_var<-sd(expenditure)/mean(expenditure) #coefficient of variability %
    coeff_var
## [1] 1.471
    IQR(expenditure)# difference between quartiles =Q3-Q1 
##      75% 
## 244.4525
    sx<-IQR(expenditure)/2  #interquartile deviation
    coeff_varx<-sx/median(expenditure) #IQR coefficient of variability %
    coeff_varx
##      75% 
## 1.206597
    min(expenditure)
## [1] 0
    max(expenditure)
## [1] 3099.505
    quantile(expenditure,probs=c(0,0.1,0.25,0.5,0.75,0.95,1),na.rm=TRUE)
##          0%         10%         25%         50%         75%         95% 
##    0.000000    0.000000    4.583333  101.298300  249.035800  640.730130 
##        100% 
## 3099.505000

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

card boxplot histogram line1 line2 points1
no
yes

##Age

ggplot(CreditCard, aes(x=card, y=age)) + 
  geom_boxplot() +
  labs(title="Age distribution box plot", x="Credit card holdings", y="Age")

With age the correlation is similar to income. The mean and average is similar for both groups, but more extreme values are present in card holders group. Only this time mean seems to be higher for non-holders than for holders.

Ratio of monthly expenditure to income

## [1] 0.51205875 0.11976382 0.05747126 2.17116850 0.87252231 0.68145681

Since expenditure corelation with card factor is very high, ratio corelation will also be high.

## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_point()`).

Here the corelation can be clearly seen.

Summary tables

grouped_stats <- CreditCard %>%
  dplyr::group_by(card) %>%
  dplyr::summarise(
    Mean_Income = mean(income, na.rm = TRUE),
    SD_Income = sd(income, na.rm = TRUE),
    Median_Income = median(income, na.rm = TRUE),
    Mean_Expenditure = mean(expenditure, na.rm = TRUE),
    SD_Expenditure = sd(expenditure, na.rm = TRUE),
    Median_Expenditure = median(expenditure, na.rm = TRUE),
    Median_Age = median(age, na.rm = TRUE),
    Mean_Age = mean(age, na.rm = TRUE),
    SD_Age = sd(age, na.rm = TRUE),
   
  )

 print(grouped_stats)
## # A tibble: 2 × 10
##   card  Mean_Income SD_Income Median_Income Mean_Expenditure SD_Expenditure
##   <fct>       <dbl>     <dbl>         <dbl>            <dbl>          <dbl>
## 1 no           3.07      1.62          2.59               0              0 
## 2 yes          3.45      1.71          3                239.           288.
## # ℹ 4 more variables: Median_Expenditure <dbl>, Median_Age <dbl>,
## #   Mean_Age <dbl>, SD_Age <dbl>

gtsummary

We can calculate easily descriptive statistics also using gtsummary package:

gt_summary_table <- CreditCard %>%
  select(income, expenditure, age, card) %>%
  tbl_summary(
    by = card,
    statistic = list(all_continuous() ~ "{mean} ({sd})")

  )

gt_summary_table
Characteristic no, N = 2961 yes, N = 1,0231
income 3.07 (1.62) 3.45 (1.71)
expenditure 0 (0) 239 (288)
age 33 (10) 33 (10)
1 Mean (SD)

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.

 CreditCard<- CreditCard %>%
  select(income, expenditure, age, card)
dfSummary(
  CreditCard,
  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

CreditCard

Dimensions: 1319 x 4
Duplicates: 1

No Variable Stats / Values Freqs (% of Valid) Graph Missing
1 income
[numeric]
Mean (sd) : 3.4 (1.7)
min < med < max:
0.2 < 2.9 < 13.5
IQR (CV) : 1.8 (0.5)
431 distinct values 0
(0.0%)
2 expenditure
[numeric]
Mean (sd) : 185.1 (272.2)
min < med < max:
0 < 101.3 < 3099.5
IQR (CV) : 244.5 (1.5)
981 distinct values 0
(0.0%)
3 age
[numeric]
Mean (sd) : 33.2 (10.1)
min < med < max:
0.2 < 31.2 < 83.5
IQR (CV) : 14 (0.3)
418 distinct values 0
(0.0%)
4 card
[factor]
1. no
2. yes
296 (22.4%)
1023 (77.6%)
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():

## # A tibble: 3 × 8
##   variable      mean     sd   min   med    max n.valid pct.valid
##   <chr>        <dbl>  <dbl> <dbl> <dbl>  <dbl>   <dbl>     <dbl>
## 1 age          33.2   10.1  0.167  31.2   83.5    1319       100
## 2 expenditure 185.   272.   0     101.  3100.     1319       100
## 3 income        3.37   1.69 0.21    2.9   13.5    1319       100
## # A tibble: 6 × 9
##   card  variable      mean     sd   min    med    max n.valid pct.valid
##   <fct> <chr>        <dbl>  <dbl> <dbl>  <dbl>  <dbl>   <dbl>     <dbl>
## 1 no    age          33.2    9.92 0.75   31.8    80.2     296       100
## 2 no    expenditure   0      0    0       0       0       296       100
## 3 no    income        3.07   1.62 0.49    2.59   11       296       100
## 4 yes   age          33.2   10.2  0.167  31.1    83.5    1023       100
## 5 yes   expenditure 239.   288.   0     150.   3100.     1023       100
## 6 yes   income        3.45   1.71 0.21    3      13.5    1023       100
variable card min q1 med q3 max
age no 0.75 25.62 31.83 38.12 80.17
age yes 0.17 25.33 31.08 39.75 83.50
expenditure no 0.00 0.00 0.00 0.00 0.00
expenditure yes 0.00 66.30 150.18 314.49 3099.50
income no 0.49 2.00 2.59 3.65 11.00
income yes 0.21 2.35 3.00 4.00 13.50
LS0tDQp0aXRsZTogJ0Rlc2NyaXB0aXZlIFN0YXRpc3RpY3MnDQpzdWJ0aXRsZTogJ1VuaXZhcmlhdGUgU3RhdGlzdGljcycNCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCmF1dGhvcjogIk5hdGFsaWEgU2FyYmlld3NrYSwgTWFyY2luIFN1Y2h0bywgWmhhbmcgSGFva2FuZywgTWFjaWVqIEthcndpayINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIGZvbnRzaXplOiA4cHQNCiAgICB0b2M6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICBkZl9wcmludDogZGVmYXVsdA0KICAgIHRvY19kZXB0aDogNQ0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cDEsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpvcHRpb25zKHF3cmFwczJfbWFya3VwID0gIm1hcmtkb3duIikNCmxpYnJhcnkocXdyYXBzMikNCmxpYnJhcnkoYXJzZW5hbCkNCmxpYnJhcnkoZTEwNzEpDQpsaWJyYXJ5KGhhdmVuKQ0KbGlicmFyeShwYXBlUikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkoc3VtbWFyeXRvb2xzKQ0KbGlicmFyeShjbGFzc0ludCkNCmxpYnJhcnkocGFzdGVjcykNCmxpYnJhcnkocmVwb3J0dG9vbHMpDQpsaWJyYXJ5KGRlc2N0YWJsZSkNCmxpYnJhcnkocHN5Y2gpDQpsaWJyYXJ5KGZyZXF1ZW5jeSkNCmxpYnJhcnkoZ2dwdWJyKQ0KbGlicmFyeShnZ2ZvcmNlKQ0KbGlicmFyeShnZ2Rpc3QpDQpsaWJyYXJ5KGdnaGFsdmVzKQ0KbGlicmFyeShndHN1bW1hcnkpDQpsaWJyYXJ5KEFFUikNCg0KYGBgDQojIyBZb3VyIHR1cm4hDQoNCllvdXIgdGFzayB0aGlzIHdlZWsgaXMgdG86IHByZXBhcmUgeW91ciBvd24gZGVzY3JpcHRpdmUgYW5hbHlzaXMgZm9yIHRoZQ0KIkNyZWRpdENhcmQiIGRhdGFzZXQgKEFFUiBwYWNrYWdlKS4gSXQgaXMgYSBjcm9zcy1zZWN0aW9uYWwgZGF0YWZyYW1lIG9uDQp0aGUgY3JlZGl0IGhpc3RvcnkgZm9yIGEgc2FtcGxlIG9mIGFwcGxpY2FudHMgZm9yIGEgdHlwZSBvZiBjcmVkaXQgY2FyZC4NCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmRhdGEoQ3JlZGl0Q2FyZCkNCg0Kc3RyKENyZWRpdENhcmQpDQpzdW1tYXJ5KENyZWRpdENhcmQpDQpgYGANCg0KQXJlIHRoZSB5ZWFybHkgaW5jb21lcyAoaW4gVVNEIDEwLDAwMCksIGNyZWRpdCBjYXJkIGV4cGVuZGl0dXJlcywgYWdlLA0KcmF0aW8gb2YgbW9udGhseSBjcmVkaXQgY2FyZCBleHBlbmRpdHVyZSB0byB5ZWFybHkgaW5jb21lIC0NCnNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZvciBhcHBsaWNhbnRzIGZvciBjdXN0b21lcnMgd2l0aCBkaWZmZXJlbnQNCmNyZWRpdCByaXNrICgiY2FyZCIgdmFyaWFibGUgLSBmYWN0b3IpPw0KDQpQcmVwYXJlIGEgcHJvZmVzc2lvbmFsIGRhdGEgdmlzdWFsaXphdGlvbnMsIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MnDQp0YWJsZXMgYW5kIGludGVycHJldCB0aGVtLg0KDQojIyBQcmVwcm9jZXNzaW5nDQpgYGB7ciB3cmFuZ2xpbmd9DQppbmNvbWU8LWFzLm51bWVyaWMoQ3JlZGl0Q2FyZCRpbmNvbWUpDQpleHBlbmRpdHVyZTwtYXMubnVtZXJpYyhDcmVkaXRDYXJkJGV4cGVuZGl0dXJlKQ0KYWdlPC1hcy5pbnRlZ2VyKENyZWRpdENhcmQkYWdlKQ0KY2FyZDwtYXMuZmFjdG9yKENyZWRpdENhcmQkY2FyZCkNCg0KYGBgDQojIyBGcmVxdWVuY3kgVGFibGVzDQoNCkluIHRoZSBmaXJzdCBzdGVwIG9mIG91ciBhbmFseXNpcywgd2Ugd2lsbCBncm91cCBvdXIgZGF0YSBpbnRvIGEgc2ltcGxlDQpmcmVxdWVuY3kgdGFibGUuDQoNCkZpcnN0LCBsZXQncyBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgaW5jb21lIGluIG91ciBzYW1wbGUNCmFuZCB2ZXJpZnkgdGFidWxhciB2YWxpZGl0eSB1c2luZyB0aGUgVEFJIG1lYXN1cmU6DQoNCmBgYHtyIHRhYmxlLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCmxhYjwtYygiMC0yIGtQTE4iLCIyLTQga1BMTiIsIjQtNiBrUExOIiwiNi04IGtQTE4iLCI4LTEwIGtQTE4iLCIxMC0xMiBrUExOIiwiMTItMTQga1BMTiIpDQpsaW08LWN1dChDcmVkaXRDYXJkJGluY29tZSxzZXEoIDAsIDE0ICxieT0yKSxsYWJlbHM9bGFiKQ0KdGFiPC1mcmVxKGxpbSwgdHlwZT0iaHRtbCIpDQpgYGANCg0KT2ssIGl0IGxvb2tzIHF1aXRlIHVnbHksIHNvIGxldCdzIHdyYXAgaXQgdXAgdXNpbmcgdGhlICdrYWJsZScgcGFja2FnZToNCg0KYGBge3IgdGFpLCBlY2hvPUZBTFNFfQ0Ka2JsKHRhYixjYXB0aW9uID0gIkluY29tZXMgaW4ga1BMTiIpICU+JQ0KICAgIGthYmxlX21hdGVyaWFsKGMoInN0cmlwZWQiLCAiaG92ZXIiKSkNCnRhYjwtY2xhc3NJbnRlcnZhbHMoQ3JlZGl0Q2FyZCRpbmNvbWUsbj0xMCxzdHlsZT0iZml4ZWQiLGZpeGVkQnJlYWtzPXNlcSgwLCAxNCAsYnk9MikpDQpqZW5rcy50ZXN0cyh0YWIpDQpgYGANCkFzIHdlIGNhbiBzZWUgLSB0aGUgVEFJIGluZGV4IGlzIHF1aXRlIGhpZ2guIDAuNjUgbWVhbnMgdGhhdCB3ZSBjYW4NCmFjY2VwdCB0aGUgcHJvcG9zZWQgY29uc3RydWN0aW9uIG9mIHRoZSBmcmVxdWVuY3kgdGFibGUuDQoNCiMjIEluY29tZQ0KYGBge3IgaGlzdG9ncmFtLCBlY2hvPUZBTFNFfQ0KI0ZpcnN0IGEgaGlzdG9ncmFtDQogIGhpc3QoaW5jb21lLCBicmVha3M9IkZEIiwgY29sPSJwaW5rIiwgcHJvYmFiaWxpdHkgPSBUUlVFLA0KICAgICAgbWFpbj0iSW5jb21lIGluIDEwayBvZiBQTE4iKQ0KICBsaW5lcyhkZW5zaXR5KGluY29tZVtjYXJkPT0ibm8iXSksY29sPTQpDQogIGxpbmVzKGRlbnNpdHkoaW5jb21lW2NhcmQ9PSJ5ZXMiXSksY29sPSJwdXJwbGUiKQ0KICBsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kPWMoIm5vIiwgInllcyIpLA0KICAgICAgICBjb2w9Yyg0LCJwdXJwbGUiKSwgbHR5PTE6MiwgaG9yaXo9RkFMU0UsIGJveC5sdHk9MCwgY2V4PTAuOCkNCg0KZGVuc2l0eS5wIDwtIGdnZGVuc2l0eShDcmVkaXRDYXJkLCB4ID0gImluY29tZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gImNhcmQiLCBwYWxldHRlID0gImpjbyIpKw0KICBzdGF0X292ZXJsYXlfbm9ybWFsX2RlbnNpdHkoY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikNCg0KDQojIENvbXB1dGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBieSBncm91cHMNCnN0YWJsZSA8LSBkZXNjX3N0YXRieShDcmVkaXRDYXJkLCBtZWFzdXJlLnZhciA9ICJpbmNvbWUiLA0KICAgICAgICAgICAgICAgICAgICAgIGdycHMgPSAiY2FyZCIpDQpzdGFibGUgPC0gc3RhYmxlWywgYygiY2FyZCIsICJsZW5ndGgiLCAibWVhbiIsICJzZCIpXQ0KIyBTdW1tYXJ5IHRhYmxlIHBsb3QsIG1lZGl1bSBvcmFuZ2UgdGhlbWUNCnN0YWJsZS5wIDwtIGdndGV4dHRhYmxlKHN0YWJsZSwgcm93cyA9IE5VTEwsIA0KICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUgPSB0dGhlbWUoIm1PcmFuZ2UiKSkNCiMgRHJhdyB0ZXh0DQojOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoNCnRleHQgPC0gcGFzdGUoIlllYXJseSBpbmNvbWVzIGluIDEwayBVU0QgaW4gcmVsYXRpb24gdG8gY2FyZCBmYWN0b3IiLA0KICAgICAgICAgICAgICAgc2VwID0gIiAiKQ0KdGV4dC5wIDwtIGdncGFyYWdyYXBoKHRleHQgPSB0ZXh0LCBmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxMSwgY29sb3IgPSAiYmxhY2siKQ0KIyBBcnJhbmdlIHRoZSBwbG90cyBvbiB0aGUgc2FtZSBwYWdlDQpnZ2FycmFuZ2UoZGVuc2l0eS5wLCBzdGFibGUucCwgdGV4dC5wLCANCiAgICAgICAgICBuY29sID0gMSwgbnJvdyA9IDMsDQogICAgICAgICAgaGVpZ2h0cyA9IGMoMSwgMC41LCAwLjMpKQ0KDQojR2dwbG90ICANCmdncGxvdChDcmVkaXRDYXJkLCBhZXMoeD1jYXJkLCB5PWluY29tZSwgZmlsbD1jYXJkKSkgKyANCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZG9kZ2UiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJibHVlIiwgInJlZCIpKSArICAjIOaMh+WumuminOiJsg0KICBsYWJzKHRpdGxlPSJBbm51YWwgaW5jb21lIGRpc3RyaWJ1dGlvbiBvZiBjcmVkaXQgY2FyZCBob2xkZXJzIGFuZCBub24taG9sZGVycyIsIA0KICAgICAgIHg9IkNyZWRpdCBjYXJkIGhvbGRpbmdzIiwgDQogICAgICAgeT0iQW5udWFsIGluY29tZSAoVW5pdDogVVNEIDEwLDAwMCkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3QoQ3JlZGl0Q2FyZCwgYWVzKHg9Y2FyZCwgeT1pbmNvbWUpKSArIA0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnModGl0bGU9IkFubnVhbCBpbmNvbWUgZGlzdHJpYnV0aW9uIGJveCBwbG90IiwgeD0iQ3JlZGl0IGNhcmQgaG9sZGluZ3MiLCB5PSJBbm51YWwgaW5jb21lIChVbml0OiBVU0QgMTAsMDAwKSIpDQpgYGANCkJhc2VkIG9uIHRoZXNlIGdyYXBocyB3ZSBjYW4gb2JzZXJ2ZSBhIGZldyB0aGluZ3MuIEZpcnN0IG9mIGFsbCwgaW4gdGhlIGZpcnN0IHR3byBncmFwaHMgd2UgY2FuIHNlZSB0aGF0IHRoZSBpbmNvbWUgbGluZSBvZiB0aG9zZSB3aG8gZG8gbm90IGhhdmUgY2FyZHMgaXMgc2xpZ2h0bHkgc2hpZnRlZCB0byB0aGUgbGVmdCwgd2hpY2ggbWVhbnMgdGhhdCB0aGVpciBpbmNvbWUgaXMgc21hbGxlciBjb21wYXJlZCB0byB0aG9zZSB3aXRoIGNhcmRzLiBXZSBjb25maXJtZWQgdGhpcyBvYnNlcnZhdGlvbiBieSBnZW5lcmF0aW5nIHRoZSBtZWFuIG9mIGluY29tZSBmb3IgdGhvc2UgdHdvIGdyb3VwcyBvZiBwZW9wbGUsIHdoZXJlIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGUgZGlmZmVyZW5jZS4gTW9yZW92ZXIsIHRoZSBib3hwbG90IGdyYXBocyBzaG93IHRoYXQgdGhlIG1vcmUgZXh0cmVtZSBpbmNvbWUgdmFsdWVzIChhYm92ZSAxMCkgb2NjdXIgbW9zdGx5IGluIHRoZSBjYXJkLWhvbGRlcnMgZ3JvdXAgd2l0aCBxdWl0ZSBhbiBhZHZhbnRhZ2UuIEJhc2VkIG9uIGFsbCB0aGF0IGluZm9ybWF0aW9uIHRoZXJlIHNlZW1zIHRvIGJlIGxpdHRsZSBjb3JlbGF0aW9uIGJldHdlZW4gaW5jb21lIGFuZCBjYXJkIGZhY3RvciwgaG93ZXZlciB0aGUgZGlmZmVyZW5jZSBpcyBtb3JlIHNpZ25pZmljYW50IHdoZW4gaXQgY29tZXMgdG8gdGhlIHJhbmdlIG9mIGluY29tZSB0aGFuIGl0cyBhdmVyYWdlIA0KDQojIyBFeHBlbmRpdHVyZQ0KSG93IGFib3V0IHRoZSByZWxhdGlvbiBiZXR3ZWVuIGV4cGVuZGl0dXJlIGFuZCBjYXJkIGZhY3Rvcj8NCg0KYGBge3IgYm94cGxvdDIsIGVjaG89RkFMU0V9DQpnZ3Bsb3QoQ3JlZGl0Q2FyZCwgYWVzKHg9Y2FyZCwgeT1leHBlbmRpdHVyZSkpICsNCiAgICBnZW9tX2JveHBsb3QoYWxwaGE9MC43KSArDQogICAgc3RhdF9zdW1tYXJ5KGZ1bj0ibWVhbiIsIGdlb209InBvaW50Iiwgc2hhcGU9MjAsIHNpemU9NSwgY29sb3I9InJlZCIsIGZpbGw9InJlZCIpICsNCiBnZW9tX2ppdHRlcigpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJTZXQxIikNCg0KYGBgDQpCeSB0aGlzIHNpbmdsZSBwbG90IHdlIGNhbiBvYnNlcnZlIHRoYXQgZXhwZW5kaXR1cmUgaXMgaGlnaGx5IGRlcGVuZGVudCBmcm9tIGNhcmQgZmFjdG9yLiBBcyBzaG93biBhYm92ZSwgZXhwZW5kaXR1cmUgb2YgY2FyZCBub24taG9sZGVycyBpbiBhbGwgY2FzZXMgaXMgY2xvc2UgaWYgbm90IGVxdWFsIHRvIHplcm8sIHdoZXJlYXMgZm9yIHRoZSBvdGhlciBncm91cCB0aGlzIHZhcmlhYmxlIGlzIGluIHJhbmdlIGZyb20gMCB0byBzbyBtdWNoIGFzIDMwMDAuDQpgYGB7cn0NCiAgICANCiAgICBtZWFuKGV4cGVuZGl0dXJlKQ0KICAgIG1lZGlhbihleHBlbmRpdHVyZSkNCiAgICBzZChleHBlbmRpdHVyZSkgI3N0YW5kYXJkIGRldmlhdGlvbg0KICAgIHZhcihleHBlbmRpdHVyZSkgI3ZhcmlhbmNlDQogICAgY29lZmZfdmFyPC1zZChleHBlbmRpdHVyZSkvbWVhbihleHBlbmRpdHVyZSkgI2NvZWZmaWNpZW50IG9mIHZhcmlhYmlsaXR5ICUNCiAgICBjb2VmZl92YXINCiAgICBJUVIoZXhwZW5kaXR1cmUpIyBkaWZmZXJlbmNlIGJldHdlZW4gcXVhcnRpbGVzID1RMy1RMSANCiAgICBzeDwtSVFSKGV4cGVuZGl0dXJlKS8yICAjaW50ZXJxdWFydGlsZSBkZXZpYXRpb24NCiAgICBjb2VmZl92YXJ4PC1zeC9tZWRpYW4oZXhwZW5kaXR1cmUpICNJUVIgY29lZmZpY2llbnQgb2YgdmFyaWFiaWxpdHkgJQ0KICAgIGNvZWZmX3ZhcngNCiAgICBtaW4oZXhwZW5kaXR1cmUpDQogICAgbWF4KGV4cGVuZGl0dXJlKQ0KICAgIHF1YW50aWxlKGV4cGVuZGl0dXJlLHByb2JzPWMoMCwwLjEsMC4yNSwwLjUsMC43NSwwLjk1LDEpLG5hLnJtPVRSVUUpDQogICAgDQpgYGANCg0KT2ssIHdlIGhhdmUgY2FsY3VsYXRlZCBhbGwgb2YgdGhlIGJhc2ljIHN1bW1hcnkgc3RhdGlzdGljcyBhYm92ZS4gTGV0J3MNCndyYXAgdGhlbSB1cCB0b2dldGhlciBub3cuDQoNCmBgYHtyIGthYmxlX3JlcG9ydCwgZWNobz1GQUxTRX0NCmxpYnJhcnkoa2FibGVFeHRyYSkNCmNhcmRfbGlzdCA8LSBzcGxpdChDcmVkaXRDYXJkJGV4cGVuZGl0dXJlLCBDcmVkaXRDYXJkJGNhcmQpDQppbmxpbmVfcGxvdCA8LSBkYXRhLmZyYW1lKGNhcmQgPSBjKCdubycsICd5ZXMnKSwgYm94cGxvdCA9ICIiLCBoaXN0b2dyYW0gPSAiIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZTEgPSAiIiwgbGluZTIgPSAiIiwgcG9pbnRzMSA9ICIiKQ0KaW5saW5lX3Bsb3QgJT4lDQogIGtibChib29rdGFicyA9IFRSVUUpICU+JQ0KICBrYWJsZV9wYXBlcihmdWxsX3dpZHRoID0gRkFMU0UpICU+JQ0KICBjb2x1bW5fc3BlYygyLCBpbWFnZSA9IHNwZWNfYm94cGxvdChjYXJkX2xpc3QpKSAlPiUNCiAgY29sdW1uX3NwZWMoMywgaW1hZ2UgPSBzcGVjX2hpc3QoY2FyZF9saXN0KSkgJT4lDQogIGNvbHVtbl9zcGVjKDQsIGltYWdlID0gc3BlY19wbG90KGNhcmRfbGlzdCwgc2FtZV9saW0gPSBUUlVFKSkgJT4lDQogIGNvbHVtbl9zcGVjKDUsIGltYWdlID0gc3BlY19wbG90KGNhcmRfbGlzdCwgc2FtZV9saW0gPSBGQUxTRSkpICU+JQ0KICBjb2x1bW5fc3BlYyg2LCBpbWFnZSA9IHNwZWNfcGxvdChjYXJkX2xpc3QsIHR5cGUgPSAicCIpKQ0KDQpgYGANCg0KIyNBZ2UNCmBgYHtyIH0NCg0KZ2dwbG90KENyZWRpdENhcmQsIGFlcyh4PWNhcmQsIHk9YWdlKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHRpdGxlPSJBZ2UgZGlzdHJpYnV0aW9uIGJveCBwbG90IiwgeD0iQ3JlZGl0IGNhcmQgaG9sZGluZ3MiLCB5PSJBZ2UiKQ0KDQpgYGANCldpdGggYWdlIHRoZSBjb3JyZWxhdGlvbiBpcyBzaW1pbGFyIHRvIGluY29tZS4gVGhlIG1lYW4gYW5kIGF2ZXJhZ2UgaXMgc2ltaWxhciBmb3IgYm90aCBncm91cHMsIGJ1dCBtb3JlIGV4dHJlbWUgdmFsdWVzIGFyZSBwcmVzZW50IGluIGNhcmQgaG9sZGVycyBncm91cC4gT25seSB0aGlzIHRpbWUgbWVhbiBzZWVtcyB0byBiZSBoaWdoZXIgZm9yIG5vbi1ob2xkZXJzIHRoYW4gZm9yIGhvbGRlcnMuDQoNCiMjIFJhdGlvIG9mIG1vbnRobHkgZXhwZW5kaXR1cmUgdG8gaW5jb21lDQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCkNyZWRpdENhcmQgPC0gdHJhbnNmb3JtKENyZWRpdENhcmQsIHJhdGlvID0gaWZlbHNlKGluY29tZSAhPSAwLCBleHBlbmRpdHVyZSAvIChpbmNvbWUgKiBtb250aHMpLCBOQSkpDQpoZWFkKENyZWRpdENhcmQkcmF0aW8pDQpDcmVkaXRDYXJkICU+JSANCiAgZmlsdGVyKGNhcmQgJWluJSBjKCJubyIsICJ5ZXMiKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IoY2FyZCksIHkgPSByYXRpbywgZmlsbCA9IGZhY3RvcihjYXJkKSkpICsNCiAgDQogIHN0YXRfaGFsZmV5ZSgNCiAgICBhZGp1c3QgPSAwLjUsDQogICAganVzdGlmaWNhdGlvbiA9IC0wLjIsDQogICAgLndpZHRoID0gMCwNCiAgICBwb2ludF9jb2xvdXIgPSBOQQ0KICApICsNCiAgZ2VvbV9ib3hwbG90KA0KICAgIHdpZHRoID0gMC4xMiwNCiAgICBvdXRsaWVyLmNvbG9yID0gTkEsDQogICAgYWxwaGEgPSAwLjUNCiAgKSAgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlJhaW5DbG91ZCBQbG90IiwNCiAgICB4ID0gIkNhcmQiLA0KICAgIHkgPSAiUmF0aW8gb2YgZXhwZW5kaXR1cmUgdG8gaW5jb21lIg0KICApICsNCiAgY29vcmRfZmxpcCgpKw0KICB5bGltKGMoMCwgNCkpDQpgYGANClNpbmNlIGV4cGVuZGl0dXJlIGNvcmVsYXRpb24gd2l0aCBjYXJkIGZhY3RvciBpcyB2ZXJ5IGhpZ2gsIHJhdGlvIGNvcmVsYXRpb24gd2lsbCBhbHNvIGJlIGhpZ2guIA0KDQpgYGB7ciBmYWNldDEsIGVjaG89RkFMU0V9DQpwbG90MSA8LSBnZ3Bsb3QoQ3JlZGl0Q2FyZCwgYWVzKGluY29tZSwgcmF0aW8pKSArIA0KICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMSwgaGVpZ2h0ID0gMC4xKSANCnBsb3QxICsgZmFjZXRfd3JhcCh+Y2FyZCkNCmBgYA0KSGVyZSB0aGUgY29yZWxhdGlvbiBjYW4gYmUgY2xlYXJseSBzZWVuLg0KDQojIyBTdW1tYXJ5IHRhYmxlcw0KDQpgYGB7cn0NCmdyb3VwZWRfc3RhdHMgPC0gQ3JlZGl0Q2FyZCAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KGNhcmQpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIE1lYW5fSW5jb21lID0gbWVhbihpbmNvbWUsIG5hLnJtID0gVFJVRSksDQogICAgU0RfSW5jb21lID0gc2QoaW5jb21lLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1lZGlhbl9JbmNvbWUgPSBtZWRpYW4oaW5jb21lLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1lYW5fRXhwZW5kaXR1cmUgPSBtZWFuKGV4cGVuZGl0dXJlLCBuYS5ybSA9IFRSVUUpLA0KICAgIFNEX0V4cGVuZGl0dXJlID0gc2QoZXhwZW5kaXR1cmUsIG5hLnJtID0gVFJVRSksDQogICAgTWVkaWFuX0V4cGVuZGl0dXJlID0gbWVkaWFuKGV4cGVuZGl0dXJlLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1lZGlhbl9BZ2UgPSBtZWRpYW4oYWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1lYW5fQWdlID0gbWVhbihhZ2UsIG5hLnJtID0gVFJVRSksDQogICAgU0RfQWdlID0gc2QoYWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgDQogICkNCg0KIHByaW50KGdyb3VwZWRfc3RhdHMpDQoNCmBgYA0KDQoNCiMjIyBndHN1bW1hcnkNCg0KV2UgY2FuIGNhbGN1bGF0ZSBlYXNpbHkgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBhbHNvIHVzaW5nIGd0c3VtbWFyeQ0KcGFja2FnZToNCg0KYGBge3J9DQoNCg0KZ3Rfc3VtbWFyeV90YWJsZSA8LSBDcmVkaXRDYXJkICU+JQ0KICBzZWxlY3QoaW5jb21lLCBleHBlbmRpdHVyZSwgYWdlLCBjYXJkKSAlPiUNCiAgdGJsX3N1bW1hcnkoDQogICAgYnkgPSBjYXJkLA0KICAgIHN0YXRpc3RpYyA9IGxpc3QoYWxsX2NvbnRpbnVvdXMoKSB+ICJ7bWVhbn0gKHtzZH0pIikNCg0KICApDQoNCmd0X3N1bW1hcnlfdGFibGUNCg0KYGBgDQoNCiMjIyBkZlN1bW1hcnkNCg0KZGZTdW1tYXJ5KCkgY3JlYXRlcyBhIHN1bW1hcnkgdGFibGUgd2l0aCBzdGF0aXN0aWNzLCBmcmVxdWVuY2llcyBhbmQNCmdyYXBocyBmb3IgYWxsIHZhcmlhYmxlcyBpbiBhIGRhdGEgZnJhbWUuIFRoZSBpbmZvcm1hdGlvbiBkaXNwbGF5ZWQgaXMNCnR5cGUtc3BlY2lmaWMgKGNoYXJhY3RlciwgZmFjdG9yLCBudW1lcmljLCBkYXRlKSBhbmQgYWxzbyB2YXJpZXMNCmFjY29yZGluZyB0byB0aGUgbnVtYmVyIG9mIGRpc3RpbmN0IHZhbHVlcy4NCg0KV2hlbiB1c2luZyBkZlN1bW1hcnkoKSBpbiBSIE1hcmtkb3duIGRvY3VtZW50cywgaXQgaXMgZ2VuZXJhbGx5IGEgZ29vZA0KaWRlYSB0byBleGNsdWRlIGEgY29sdW1uIG9yIHR3byB0byBhdm9pZCBtYXJnaW4gb3ZlcmZsb3cuIFNpbmNlIHRoZQ0KVmFsaWQgYW5kIE1pc3NpbmcgY29sdW1ucyBhcmUgcmVkdW5kYW50LCB3ZSBjYW4gZHJvcCBlaXRoZXIgb25lIG9mIHRoZW0uDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9ImFzaXMifQ0KIENyZWRpdENhcmQ8LSBDcmVkaXRDYXJkICU+JQ0KICBzZWxlY3QoaW5jb21lLCBleHBlbmRpdHVyZSwgYWdlLCBjYXJkKQ0KZGZTdW1tYXJ5KA0KICBDcmVkaXRDYXJkLA0KICBwbGFpbi5hc2NpaSA9IEZBTFNFLA0KICBzdHlsZSA9ICJncmlkIiwNCiAgZ3JhcGgubWFnbmlmID0gMC43NSwNCiAgdmFsaWQuY29sID0gRkFMU0UsDQogIHRtcC5pbWcuZGlyID0gIi90bXAiDQopDQoNCmBgYA0KDQpUbyBwcm9kdWNlIG9wdGltYWwgcmVzdWx0cywgc3VtbWFyeXRvb2xzIGhhcyBpdHMgb3duIHZlcnNpb24gb2YgdGhlIGJhc2UNCmJ5KCkgZnVuY3Rpb24uIEl0J3MgY2FsbGVkIHN0YnkoKSwgYW5kIHdlIHVzZSBpdCBleGFjdGx5IGFzIHdlIHdvdWxkDQpieSgpOg0KDQoNCmBgYHtyIGVjaG89RkFMU0V9DQogQ3JlZGl0Q2FyZDwtIENyZWRpdENhcmQgJT4lDQogIHNlbGVjdChpbmNvbWUsIGV4cGVuZGl0dXJlLCBhZ2UsIGNhcmQpDQpDcmVkaXRDYXJkICU+JQ0KICBkZXNjcihzdGF0cyA9ICJjb21tb24iKSAlPiUNCiAgdGIoKQ0KDQpncm91cGVkX2Rlc2NyIDwtIHN0YnkoZGF0YSA9IENyZWRpdENhcmQsIElORElDRVMgPSBDcmVkaXRDYXJkJGNhcmQsIEZVTiA9IGRlc2NyLCBzdGF0cyA9ICJjb21tb24iKQ0KDQpncm91cGVkX2Rlc2NyICU+JSB0YigpDQpgYGANCg0KYGBge3IgZWNobz1GQUxTRX0NCiBDcmVkaXRDYXJkPC0gQ3JlZGl0Q2FyZCAlPiUNCiAgc2VsZWN0KGluY29tZSwgZXhwZW5kaXR1cmUsIGFnZSwgY2FyZCkNCnN0YnkoZGF0YSAgICA9IENyZWRpdENhcmQsIA0KICAgICBJTkRJQ0VTID0gQ3JlZGl0Q2FyZCRjYXJkLCANCiAgICAgRlVOICAgICA9IGRlc2NyLCANCiAgICAgc3RhdHMgICA9ICJmaXZlbnVtIikgJT4lDQogIHRiKG9yZGVyID0gMykgJT4lDQogIGthYmxlKGZvcm1hdCA9ICJodG1sIiwgZGlnaXRzID0gMikNCmBgYA0KDQo=