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 .
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 'E:\tmp'
Data Frame Summary
apartments
Dimensions: 200 x 6
Duplicates: 0
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
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
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
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
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 = "all") %>%
tb()
## # A tibble: 3 Ă 16
## variable mean sd min q1 med q3 max mad iqr cv
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 price_EUR 1.76e5 4.31e4 83280 1.43e5 1.75e5 2.09e5 2.96e5 4.99e4 6.54e4 0.245
## 2 price_PLN 7.60e5 1.86e5 359769 6.18e5 7.56e5 9.02e5 1.28e6 2.15e5 2.83e5 0.245
## 3 size 4.62e1 2.01e1 17 3.11e1 4.37e1 6.14e1 8.77e1 2.48e1 3.03e1 0.435
## # âč 5 more variables: skewness <dbl>, se.skewness <dbl>, kurtosis <dbl>,
## # n.valid <dbl>, pct.valid <dbl>
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 %>%
group_by(card) %>%
summarise(
mean_income = mean(income),
sd_income = sd(income),
mean_expenditure = mean(expenditure),
sd_expenditure = sd(expenditure),
mean_age = mean(age),
sd_age = sd(age),
mean_share = mean(share),
sd_share = sd(share)
) %>%
kable(format = "html") %>%
kable_styling(full_width = FALSE, bootstrap_options = "striped", font_size = 14) %>%
column_spec(1, bold = TRUE) %>%
collapse_rows(columns = 1, valign = "middle")
card
mean_income
sd_income
mean_expenditure
sd_expenditure
mean_age
sd_age
mean_share
sd_share
no
3.068509
1.615336
0.0000
0.0000
33.20298
9.921287
0.0004768
0.0002131
yes
3.451273
1.707116
238.6024
287.7098
33.21603
10.210752
0.0884815
0.0990702
#CreditCard %>%
#group_by(card) %>%
#descr(stats = "common") %>%
#tb()
ggplot(CreditCard, aes(x = card, y = income)) +
geom_boxplot(fill = "blue") +
labs(title = "Boxplot of yearly incomes by credit risk",
x = "credit risk",
y = "yearly income (in USD 10,000)")
ggplot(CreditCard, aes(x = card, y = expenditure)) +
geom_boxplot(fill = "purple") +
labs(title = "Boxplot of credit card expenditures by credit risk",
x = "credit risk",
y = "credit card expenditure")
ggplot(CreditCard, aes(x = age, y = share, color = card)) +
geom_point() +
labs(title = "Scatterplot of age and ratio of monthly expenditure to yearly income by credit risk",
x = "age",
y = "ratio of monthly expenditure to yearly income",
color = "credit risk")
# Based on this data visualization we can clearly see that in some aspects there are huge differences between credit cards accepted and not accepted.
#First of all for not accepted cards monthly expenditure doesn't exist.
#There is a huge difference in ratio as for example for accepted mean is equal to 0.088 and for not accepted it is 0.0005
#But we should also mention things that dont change or differ that much with this factor like: age, income.
LS0tDQp0aXRsZTogJ0Rlc2NyaXB0aXZlIFN0YXRpc3RpY3MnDQpzdWJ0aXRsZTogJ1VuaXZhcmlhdGUgU3RhdGlzdGljcycNCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCmF1dGhvcjogIk1hcmNpbiBUeXN6a2EsIEtyenlzenRvZiDFmndpcnlkY3p1aywgSmFrdWIgVGFybmF3c2tpIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgZm9udHNpemU6IDhwdA0KICAgIHRvYzogeWVzDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KICAgIGRmX3ByaW50OiBkZWZhdWx0DQogICAgdG9jX2RlcHRoOiA1DQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCmBgYHtyIHNldHVwMSwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCm9wdGlvbnMocXdyYXBzMl9tYXJrdXAgPSAibWFya2Rvd24iKQ0KbGlicmFyeShxd3JhcHMyKQ0KbGlicmFyeShhcnNlbmFsKQ0KbGlicmFyeShlMTA3MSkNCmxpYnJhcnkoaGF2ZW4pDQpsaWJyYXJ5KHBhcGVSKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShzdW1tYXJ5dG9vbHMpDQpsaWJyYXJ5KGNsYXNzSW50KQ0KbGlicmFyeShwYXN0ZWNzKQ0KbGlicmFyeShyZXBvcnR0b29scykNCmxpYnJhcnkoZGVzY3RhYmxlKQ0KbGlicmFyeShwc3ljaCkNCmxpYnJhcnkoZnJlcXVlbmN5KQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KGdnZm9yY2UpDQpsaWJyYXJ5KGdnZGlzdCkNCmxpYnJhcnkoZ2doYWx2ZXMpDQpsaWJyYXJ5KGd0c3VtbWFyeSkNCmxpYnJhcnkoQUVSKQ0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9naXRodWIuY29tL2tmbGlzaWtvd3NraS9kcy9ibG9iL21hc3Rlci9kYXRhX2FwYXJ0bWVudHMuY3N2P3Jhdz10cnVlIiwgZGVzdGZpbGUgPSJtaWVzemthbmlhLmNzdiIsbW9kZT0id2IiKQ0KYXBhcnRtZW50cyA8LSByZWFkLmNzdigibWllc3prYW5pYS5jc3YiLHNlcD0iOyIsZGVjPSIsIikNCmBgYA0KDQojIyBEYXRhDQoNCkluIG91ciBleGFtcGxlIHRoaXMgd2Vlaywgd2UgYXJlIGdvaW5nIHRvIHVzZSB0aGUgZmFrZSBkYXRhIC0gYWJvdXQgcmVhbA0KZXN0YXRlcyBpbiBXcm9jbGF3IC0gcHJpY2VzIGJ5IGRpc3RyaWN0cywgc2l6ZSBvZiBhcGFydG1lbnRzIGFuZCBtYW55DQptb3JlLg0KDQojIyMgUHJlcHJvY2Vzc2luZw0KDQpBcyB5b3UgY2FuIHNlZSwgbm90IGFsbCBmb3JtYXRzIG9mIG91ciB2YXJpYWJsZXMgYXJlIGFkYXB0ZWQuIFdlIG5lZWQgdG8NCnByZXBhcmUgYXBwcm9wcmlhdGUgZm9ybWF0cyBvZiBvdXIgdmFyaWFibGVzIGFjY29yZGluZyB0byB0aGVpcg0KbWVhc3VyZW1lbnQgc2NhbGUgYW5kIGZ1dHVyZSBhcHBsaWNhdGlvbi4NCg0KYGBge3Igd3JhbmdsaW5nLCBpbmNsdWRlPVRSVUV9DQphcGFydG1lbnRzJGRpc3RyaWN0PC1hcy5mYWN0b3IoYXBhcnRtZW50cyRkaXN0cmljdCkNCmFwYXJ0bWVudHMkYnVpbGRpbmdfdHlwZTwtYXMuZmFjdG9yKGFwYXJ0bWVudHMkYnVpbGRpbmdfdHlwZSkNCmFwYXJ0bWVudHMkcm9vbXM8LWZhY3RvcihhcGFydG1lbnRzJHJvb21zLG9yZGVyZWQ9VFJVRSkNCmF0dGFjaChhcGFydG1lbnRzKQ0KYXBhcnRtZW50cyRwcmljZV9QTE48LWFzLm51bWVyaWMoYXBhcnRtZW50cyRwcmljZV9QTE4pDQphcGFydG1lbnRzJHByaWNlX0VVUjwtYXMubnVtZXJpYyhhcGFydG1lbnRzJHByaWNlX0VVUikNCmBgYA0KDQojIyBGcmVxdWVuY3kgVGFibGVzDQoNCkluIHRoZSBmaXJzdCBzdGVwIG9mIG91ciBhbmFseXNpcywgd2Ugd2lsbCBncm91cCBvdXIgZGF0YSBpbnRvIGEgc2ltcGxlDQpmcmVxdWVuY3kgdGFibGUuDQoNCkZpcnN0LCBsZXQncyBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgaG91c2luZyBwcmljZXMgaW4gb3VyIHNhbXBsZQ0KYW5kIHZlcmlmeSB0YWJ1bGFyIHZhbGlkaXR5IHVzaW5nIHRoZSBUQUkgbWVhc3VyZToNCg0KYGBge3IgdGFibGUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KZXR5a2lldHk8LWMoIjM1MC00NTAga1BMTiIsIjQ1MC01NTAga1BMTiIsIjU1MC02NTAga1BMTiIsIjY1MC03NTAga1BMTiIsIjc1MC04NTAga1BMTiIsIjg1MC05NTAga1BMTiIsIjk1MC0xMDUwIGtQTE4iLCIxMDUwLTExNTAga1BMTiIsIjExNTAtMTI1MCBrUExOIiwiMTI1MC0xMzUwIGtQTE4iKQ0KbGltaXRzPC1jdXQoYXBhcnRtZW50cyRwcmljZV9QTE4sc2VxKDM1MDAwMCwxMzUwMDAwLGJ5PTEwMDAwMCksbGFiZWxzPWV0eWtpZXR5KQ0KdGFiZWxhMTwtZnJlcShsaW1pdHMsdHlwZT0iaHRtbCIpDQpgYGANCg0KT2ssIGl0IGxvb2tzIHF1aXRlIHVnbHksIHNvIGxldCdzIHdyYXAgaXQgdXAgdXNpbmcgdGhlICdrYWJsZScgcGFja2FnZToNCg0KYGBge3IgdGFpLCBlY2hvPUZBTFNFfQ0Ka2JsKHRhYmVsYTEsY2FwdGlvbiA9ICJBcGFydG1lbnRzIGluIFdyb2NsYXcgLSBwcmljZXMgaW4ga1BMTiIpICU+JQ0KICAgIGthYmxlX21hdGVyaWFsKGMoInN0cmlwZWQiLCAiaG92ZXIiKSkNCnRhYjE8LWNsYXNzSW50ZXJ2YWxzKGFwYXJ0bWVudHMkcHJpY2VfUExOLG49MTAsc3R5bGU9ImZpeGVkIixmaXhlZEJyZWFrcz1zZXEoMzUwMDAwLDEzNTAwMDAsYnk9MTAwMDAwKSkNCmplbmtzLnRlc3RzKHRhYjEpDQpgYGANCg0KQXMgd2UgY2FuIHNlZSAtIHRoZSBUQUkgaW5kZXggaXMgcXVpdGUgaGlnaC4gMC44NSBtZWFucyB0aGF0IHdlIGNhbg0KYWNjZXB0IHRoZSBwcm9wb3NlZCBjb25zdHJ1Y3Rpb24gb2YgdGhlIGZyZXF1ZW5jeSB0YWJsZS4NCg0KIyMgQmFzaWMgcGxvdHMNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBzaG91bGQgcmVwcmVzZW50IG91ciBkYXRhIHVzaW5nIGJhc2ljIChwcmUtaW5zdGFsbGVkDQppbiBSKSBncmFwaGljcy4gU2VsZWN0IHRoZSBtb3N0IGFwcHJvcHJpYXRlIGdyYXBocyBkZXBlbmRpbmcgb24gdGhlDQpzY2FsZSBvZiB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzLiBFeHBsb3JlIHRoZSBoZXRlcm9nZW5laXR5IG9mIHRoZQ0KZGlzdHJpYnV0aW9uIGJ5IHByZXNlbnRpbmcgdGhlIGRhdGEgYnkgZ3JvdXAgKGUuZy4sIGJ5IG5laWdoYm9yaG9vZCwNCmJ1aWxkaW5nIHR5cGUsIGV0Yy4pLiBEb24ndCBmb3JnZXQgYWJvdXQgbWFpbiB0aXRsZXMsIGxhYmVscyBhbmQNCmxlZ2VuZHMuIFJlYWQgbW9yZSBhYm91dCBncmFwaGljYWwgcGFyYW1ldGVycw0KW2hlcmVdKGh0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvd2lraS9ncmFwaGljYWwtcGFyYW1ldGVycykuDQoNCmBgYHtyIGhpc3RvZ3JhbSwgZWNobz1GQUxTRX0NCmhpc3QocHJpY2VfUExOLCBicmVha3M9IkZEIiwgY29sPSJncmVlbiIsIHByb2JhYmlsaXR5ID0gVFJVRSwNCiAgICAgbWFpbj0iUHJpY2VzIGluIFBMTiAtIFdyb2NsYXciKQ0KbGluZXMoZGVuc2l0eShwcmljZV9QTE5bZGlzdHJpY3Q9PSJLcnp5a2kiXSksY29sPTIpDQpsaW5lcyhkZW5zaXR5KHByaWNlX1BMTltkaXN0cmljdD09IkJpc2t1cGluIl0pLGNvbD0zKQ0KbGluZXMoZGVuc2l0eShwcmljZV9QTE5bZGlzdHJpY3Q9PSJTcm9kbWllc2NpZSJdKSxjb2w9NCkNCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQ9YygiS3J6eWtpIiwgIkJpc2t1cGluIiwgIlNyb2RtaWVzY2llIiksDQogICAgICAgY29sPWMoMiwzLDQpLCBsdHk9MToyLCBob3Jpej1GQUxTRSwgYm94Lmx0eT0wLCBjZXg9MC44KQ0KDQpgYGANCg0KTm90ZSB0aGF0IHRoZSBgZWNobyA9IEZBTFNFYCBwYXJhbWV0ZXIgaGFzIGJlZW4gYWRkZWQgdG8gdGhlIGNvZGUNCnNuaXBwZXQgdG8gcHJldmVudCBwcmludGluZyB0aGUgUiBjb2RlIHRoYXQgZ2VuZXJhdGVkIHRoZSBncmFwaC4NCg0KYGBge3IgYm94cGxvdCwgZWNobz1GQUxTRX0NCmJveHBsb3QocHJpY2VfUExOfmRpc3RyaWN0KQ0KYGBgDQoNCiMjIGdncGxvdDIgcGxvdHMNCg0KTm93LCBsZXQncyB1c2UgdGhlICoqKmdncGxvdDIqKiogYW5kICoqKmdncHVicioqKiBsaWJyYXJpZXMgdG8gcGxvdC4NCg0KYGBge3IgaGlzdG9ncmFtMiwgZWNobz1GQUxTRX0NCiMgRGVuc2l0eSBwbG90IG9mICJwcmljZV9QTE4iDQojOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoNCmRlbnNpdHkucCA8LSBnZ2RlbnNpdHkoYXBhcnRtZW50cywgeCA9ICJwcmljZV9QTE4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJkaXN0cmljdCIsIHBhbGV0dGUgPSAiamNvIikrDQogIHN0YXRfb3ZlcmxheV9ub3JtYWxfZGVuc2l0eShjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKQ0KDQojIERyYXcgdGhlIHN1bW1hcnkgdGFibGUgb2YgcHJpY2VfUExODQojOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoNCiMgQ29tcHV0ZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGJ5IGdyb3Vwcw0Kc3RhYmxlIDwtIGRlc2Nfc3RhdGJ5KGFwYXJ0bWVudHMsIG1lYXN1cmUudmFyID0gInByaWNlX1BMTiIsDQogICAgICAgICAgICAgICAgICAgICAgZ3JwcyA9ICJkaXN0cmljdCIpDQpzdGFibGUgPC0gc3RhYmxlWywgYygiZGlzdHJpY3QiLCAibGVuZ3RoIiwgIm1lYW4iLCAic2QiKV0NCiMgU3VtbWFyeSB0YWJsZSBwbG90LCBtZWRpdW0gb3JhbmdlIHRoZW1lDQpzdGFibGUucCA8LSBnZ3RleHR0YWJsZShzdGFibGUsIHJvd3MgPSBOVUxMLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lID0gdHRoZW1lKCJtT3JhbmdlIikpDQojIERyYXcgdGV4dA0KIzo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6DQp0ZXh0IDwtIHBhc3RlKCJQcmljZSBwZXIgYXBhcnRtZW50IGJ5IDMgZGlzdHJpY3RzIC0gV3JvY2xhdy4iLA0KICAgICAgICAgICAgICAiUmFuZG9tIHNhbXBsZSBvZiAyMDAgYXBhcnRtZW50cy4iLA0KICAgICAgICAgICAgICAgc2VwID0gIiAiKQ0KdGV4dC5wIDwtIGdncGFyYWdyYXBoKHRleHQgPSB0ZXh0LCBmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxMSwgY29sb3IgPSAiYmxhY2siKQ0KIyBBcnJhbmdlIHRoZSBwbG90cyBvbiB0aGUgc2FtZSBwYWdlDQpnZ2FycmFuZ2UoZGVuc2l0eS5wLCBzdGFibGUucCwgdGV4dC5wLCANCiAgICAgICAgICBuY29sID0gMSwgbnJvdyA9IDMsDQogICAgICAgICAgaGVpZ2h0cyA9IGMoMSwgMC41LCAwLjMpKQ0KYGBgDQoNCkdncGxvdDIgYWxsb3dzIHlvdSB0byBzaG93IHRoZSBhdmVyYWdlIHZhbHVlIGZvciBlYWNoIGdyb3VwIHVzaW5nIHRoZQ0KKipzdGF0X3N1bW1hcnkoKSoqIGZ1bmN0aW9uLiBZb3Ugbm8gbG9uZ2VyIG5lZWQgdG8gY2FsY3VsYXRlIGF2ZXJhZ2UNCnZhbHVlcyBiZWZvcmUgY3JlYXRpbmcgYSBncmFwaCENCg0KYGBge3IgYm94cGxvdDIsIGVjaG89RkFMU0V9DQpnZ3Bsb3QoYXBhcnRtZW50cywgYWVzKHg9ZGlzdHJpY3QsIHk9cHJpY2VfUExOKSkgKw0KICAgIGdlb21fYm94cGxvdChhbHBoYT0wLjcpICsNCiAgICBzdGF0X3N1bW1hcnkoZnVuPSJtZWFuIiwgZ2VvbT0icG9pbnQiLCBzaGFwZT0yMCwgc2l6ZT01LCBjb2xvcj0icmVkIiwgZmlsbD0icmVkIikgKw0KIGdlb21faml0dGVyKCkgKw0KICAgIGZhY2V0X2dyaWQofmJ1aWxkaW5nX3R5cGUpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJTZXQxIikNCg0KYGBgDQoNCiMjIyBSYWluQ2xvdWQgUGxvdA0KDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYXBhcnRtZW50cyAlPiUgDQogIGZpbHRlcihyb29tcyAlaW4lIGMoMSwgMiwgMywgNCkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKHJvb21zKSwgeSA9IHByaWNlX1BMTiwgZmlsbCA9IGZhY3Rvcihyb29tcykpKSArDQogIA0KICAjIGFkZCBoYWxmLXZpb2xpbiBmcm9tIHtnZ2Rpc3R9IHBhY2thZ2UNCiAgc3RhdF9oYWxmZXllKA0KICAgICMgYWRqdXN0IGJhbmR3aWR0aA0KICAgIGFkanVzdCA9IDAuNSwNCiAgICAjIG1vdmUgdG8gdGhlIHJpZ2h0DQogICAganVzdGlmaWNhdGlvbiA9IC0wLjIsDQogICAgIyByZW1vdmUgdGhlIHNsdWIgaW50ZXJ2YWwNCiAgICAud2lkdGggPSAwLA0KICAgIHBvaW50X2NvbG91ciA9IE5BDQogICkgKw0KICBnZW9tX2JveHBsb3QoDQogICAgd2lkdGggPSAwLjEyLA0KICAgICMgcmVtb3Zpbmcgb3V0bGllcnMNCiAgICBvdXRsaWVyLmNvbG9yID0gTkEsDQogICAgYWxwaGEgPSAwLjUNCiAgKSArDQogIHN0YXRfZG90cygNCiAgICAjIHBsb3Rpbmcgb24gbGVmdCBzaWRlDQogICAgc2lkZSA9ICJsZWZ0IiwNCiAgICAjIGFkanVzdGluZyBwb3NpdGlvbg0KICAgIGp1c3RpZmljYXRpb24gPSAxLjEsDQogICAgIyBhZGp1c3QgZ3JvdXBpbmcgKGJpbm5pbmcpIG9mIG9ic2VydmF0aW9ucw0KICAgIGJpbndpZHRoID0gMC4yNQ0KICApICsNCiMgVGhlbWVzIGFuZCBMYWJlbHMNCiAgbGFicygNCiAgICB0aXRsZSA9ICJSYWluQ2xvdWQgUGxvdCIsDQogICAgeCA9ICJOby4gb2Ygcm9vbXMiLA0KICAgIHkgPSAiUHJpY2VzIGluIFBMTiIsDQogICAgZmlsbCA9ICJyb29tcyINCiAgKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCiMjIyBGYWNldGluZw0KDQpGYWNldGluZyBnZW5lcmF0ZXMgc21hbGwgbXVsdGlwbGVzLCBlYWNoIHNob3dpbmcgYSBkaWZmZXJlbnQgc3Vic2V0IG9mDQp0aGUgZGF0YS4gVGhleSBhcmUgYSBwb3dlcmZ1bCB0b29sIGZvciBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzOiB5b3UNCmNhbiBxdWlja2x5IGNvbXBhcmUgcGF0dGVybnMgaW4gZGlmZmVyZW50IHBhcnRzIG9mIHRoZSBkYXRhIGFuZCBzZWUgaWYNCnRoZXkgYXJlIHRoZSBzYW1lIG9yIGRpZmZlcmVudC4gUmVhZCBtb3JlDQpbaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnL2ZhY2V0Lmh0bWwpLg0KDQpgYGB7ciBmYWNldDEsIGVjaG89RkFMU0V9DQpwbG90MSA8LSBnZ3Bsb3QoYXBhcnRtZW50cywgYWVzKHByaWNlX1BMTiwgcm9vbXMpKSArIA0KICBnZW9tX2FibGluZSgpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjEsIGhlaWdodCA9IDAuMSkgDQpwbG90MSArIGZhY2V0X3dyYXAofmRpc3RyaWN0KQ0KYGBgDQoNCiMjIFVuaXZhcmlhdGUgU3RhdGlzdGljcw0KDQpCZWZvcmUgYXV0b21hdGljYWxseSByZXBvcnRpbmcgdGhlIGZ1bGwgc3VtbWFyeSB0YWJsZSBvZiBkZXNjcmlwdGl2ZQ0Kc3RhdGlzdGljcywgdGhpcyB0aW1lIHlvdXIgZ29hbCBpcyB0byBtZWFzdXJlIHRoZSBjZW50cmFsIHRlbmRlbmN5IG9mDQp0aGUgcHJpY2UgZGlzdHJpYnV0aW9uLiBDb21wYXJlIHRoZSBtZWFuLCBtZWRpYW4sIGFuZCBtb2RlIGFsb25nIHdpdGgNCnBvc2l0aW9uYWwgbWVhc3VyZXMgLSBxdWFudGlsZXMgLSBieSBkaXN0cmljdCBhbmQgYnVpbGRpbmcgdHlwZSBvcg0KbnVtYmVyIG9mIHJvb21zIGluIHRoZSBhcGFydG1lbnQuDQoNCmBgYHtyfQ0KICAgIG1lYW4ocHJpY2VfUExOKQ0KICAgIG1lZGlhbihwcmljZV9QTE4pDQogICAgc2QocHJpY2VfUExOKSAjc3RhbmRhcmQgZGV2aWF0aW9uDQogICAgdmFyKHByaWNlX1BMTikgI3ZhcmlhbmNlDQogICAgY29lZmZfdmFyPC1zZChwcmljZV9QTE4pL21lYW4ocHJpY2VfUExOKSAjY29lZmZpY2llbnQgb2YgdmFyaWFiaWxpdHkgJQ0KICAgIGNvZWZmX3Zhcg0KICAgIElRUihwcmljZV9QTE4pIyBkaWZmZXJlbmNlIGJldHdlZW4gcXVhcnRpbGVzID1RMy1RMSANCiAgICBzeDwtSVFSKHByaWNlX1BMTikvMiAgI2ludGVycXVhcnRpbGUgZGV2aWF0aW9uDQogICAgY29lZmZfdmFyeDwtc3gvbWVkaWFuKHByaWNlX1BMTikgI0lRUiBjb2VmZmljaWVudCBvZiB2YXJpYWJpbGl0eSAlDQogICAgY29lZmZfdmFyeA0KICAgIG1pbihwcmljZV9QTE4pDQogICAgbWF4KHByaWNlX1BMTikNCiAgICBxdWFudGlsZShwcmljZV9QTE4scHJvYnM9YygwLDAuMSwwLjI1LDAuNSwwLjc1LDAuOTUsMSksbmEucm09VFJVRSkNCmBgYA0KDQpPaywgd2UgaGF2ZSBjYWxjdWxhdGVkIGFsbCBvZiB0aGUgYmFzaWMgc3VtbWFyeSBzdGF0aXN0aWNzIGFib3ZlLiBMZXQncw0Kd3JhcCB0aGVtIHVwIHRvZ2V0aGVyIG5vdy4NCg0KYGBge3Iga2FibGVfcmVwb3J0LCBlY2hvPUZBTFNFfQ0KYXBhcnRtZW50c19saXN0IDwtIHNwbGl0KGFwYXJ0bWVudHMkcHJpY2VfUExOLCBhcGFydG1lbnRzJHJvb21zKQ0KaW5saW5lX3Bsb3QgPC0gZGF0YS5mcmFtZShyb29tcyA9IGMoMSwgMiwgMywgNCksIGJveHBsb3QgPSAiIiwgaGlzdG9ncmFtID0gIiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUxID0gIiIsIGxpbmUyID0gIiIsIHBvaW50czEgPSAiIikNCmlubGluZV9wbG90ICU+JQ0KICBrYmwoYm9va3RhYnMgPSBUUlVFKSAlPiUNCiAga2FibGVfcGFwZXIoZnVsbF93aWR0aCA9IEZBTFNFKSAlPiUNCiAgY29sdW1uX3NwZWMoMiwgaW1hZ2UgPSBzcGVjX2JveHBsb3QoYXBhcnRtZW50c19saXN0KSkgJT4lDQogIGNvbHVtbl9zcGVjKDMsIGltYWdlID0gc3BlY19oaXN0KGFwYXJ0bWVudHNfbGlzdCkpICU+JQ0KICBjb2x1bW5fc3BlYyg0LCBpbWFnZSA9IHNwZWNfcGxvdChhcGFydG1lbnRzX2xpc3QsIHNhbWVfbGltID0gVFJVRSkpICU+JQ0KICBjb2x1bW5fc3BlYyg1LCBpbWFnZSA9IHNwZWNfcGxvdChhcGFydG1lbnRzX2xpc3QsIHNhbWVfbGltID0gRkFMU0UpKSAlPiUNCiAgY29sdW1uX3NwZWMoNiwgaW1hZ2UgPSBzcGVjX3Bsb3QoYXBhcnRtZW50c19saXN0LCB0eXBlID0gInAiKSkNCg0KYGBgDQoNCiMjIyBTdW1tYXJ5IHRhYmxlcw0KDQpPaywgbm93IHdlIHdpbGwgZmluYWxseSBzdW1tYXJpemUgdGhlIGJhc2ljIG1lYXN1cmVzIG9mIGNlbnRyYWwgdGVuZGVuY3kNCmZvciBwcmljZXMgYnkgZGlzdHJpY3QvYnVpbGRpbmcgdHlwZSB1c2luZyB0aGUgJyoqKmthYmxlKioqJyBwYWNrYWdlLg0KRmVlbCBmcmVlIHRvIGN1c3RvbWl6ZSB5b3VyIGZpbmFsIHJlcG9ydC4gU2VlIHNvbWUgaGludHMNCltoZXJlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcXdyYXBzMi92aWduZXR0ZXMvc3VtbWFyeS1zdGF0aXN0aWNzLmh0bWwpLg0KDQpgYGB7fQ0KYGBgDQoNCiMjIyBndHN1bW1hcnkNCg0KV2UgY2FuIGNhbGN1bGF0ZSBlYXNpbHkgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBhbHNvIHVzaW5nIGd0c3VtbWFyeQ0KcGFja2FnZToNCg0KYGBge3J9DQphcGFydG1lbnRzICU+JQ0KICBzZWxlY3QocHJpY2VfUExOLHJvb21zKSAlPiUNCiAgdGJsX3N1bW1hcnkobGFiZWw9IHByaWNlX1BMTiB+ICJQcmljZSIsZGlnaXRzPWMocHJpY2VfUExOKX4yLGJ5PXJvb21zLHR5cGUgPSBhbGxfY29udGludW91cygpIH4gImNvbnRpbnVvdXMyIiwgc3RhdGlzdGljID0gYWxsX2NvbnRpbnVvdXMoKSB+IGMoIntOX25vbm1pc3N9IiwgInttZWRpYW59ICh7cDI1fSwge3A3NX0pIiwgInttaW59LCB7bWF4fSIpLG1pc3NpbmcgPSAibm8iKQ0KYGBgDQoNCiMjIyBkZlN1bW1hcnkNCg0KZGZTdW1tYXJ5KCkgY3JlYXRlcyBhIHN1bW1hcnkgdGFibGUgd2l0aCBzdGF0aXN0aWNzLCBmcmVxdWVuY2llcyBhbmQNCmdyYXBocyBmb3IgYWxsIHZhcmlhYmxlcyBpbiBhIGRhdGEgZnJhbWUuIFRoZSBpbmZvcm1hdGlvbiBkaXNwbGF5ZWQgaXMNCnR5cGUtc3BlY2lmaWMgKGNoYXJhY3RlciwgZmFjdG9yLCBudW1lcmljLCBkYXRlKSBhbmQgYWxzbyB2YXJpZXMNCmFjY29yZGluZyB0byB0aGUgbnVtYmVyIG9mIGRpc3RpbmN0IHZhbHVlcy4NCg0KV2hlbiB1c2luZyBkZlN1bW1hcnkoKSBpbiBSIE1hcmtkb3duIGRvY3VtZW50cywgaXQgaXMgZ2VuZXJhbGx5IGEgZ29vZA0KaWRlYSB0byBleGNsdWRlIGEgY29sdW1uIG9yIHR3byB0byBhdm9pZCBtYXJnaW4gb3ZlcmZsb3cuIFNpbmNlIHRoZQ0KVmFsaWQgYW5kIE1pc3NpbmcgY29sdW1ucyBhcmUgcmVkdW5kYW50LCB3ZSBjYW4gZHJvcCBlaXRoZXIgb25lIG9mIHRoZW0uDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9ImFzaXMifQ0KZGZTdW1tYXJ5KGFwYXJ0bWVudHMsDQogICAgICAgICAgcGxhaW4uYXNjaWkgID0gRkFMU0UsIA0KICAgICAgICAgIHN0eWxlICAgICAgICA9ICJncmlkIiwgDQogICAgICAgICAgZ3JhcGgubWFnbmlmID0gMC43NSwgDQogICAgICAgICAgdmFsaWQuY29sICAgID0gRkFMU0UsDQogICAgICAgICAgdG1wLmltZy5kaXIgID0gIi90bXAiKQ0KYGBgDQoNClRvIHByb2R1Y2Ugb3B0aW1hbCByZXN1bHRzLCBzdW1tYXJ5dG9vbHMgaGFzIGl0cyBvd24gdmVyc2lvbiBvZiB0aGUgYmFzZQ0KYnkoKSBmdW5jdGlvbi4gSXQncyBjYWxsZWQgc3RieSgpLCBhbmQgd2UgdXNlIGl0IGV4YWN0bHkgYXMgd2Ugd291bGQNCmJ5KCk6DQoNCmBgYHtyIHJlc3VsdHM9ImFzaXMiLCB3YXJuaW5nPUZBTFNFfQ0KKHN0YXRzX2J5X3Jvb21zIDwtIHN0YnkoZGF0YSAgICAgID0gYXBhcnRtZW50cywgSU5ESUNFUyAgID0gYXBhcnRtZW50cyRyb29tcywgRlVOICAgICAgID0gZGVzY3IsIHN0YXRzICAgICA9ICJjb21tb24iLCB0cmFuc3Bvc2UgPSBUUlVFKSkNCmBgYA0KDQojIyMgVGlkeSBUYWJsZXMNCg0KV2hlbiBnZW5lcmF0aW5nIGZyZXEoKSBvciBkZXNjcigpIHRhYmxlcywgaXQgaXMgcG9zc2libGUgdG8gdHVybiB0aGUNCnJlc3VsdHMgaW50byAidGlkeSIgdGFibGVzIHdpdGggdGhlIHVzZSBvZiB0aGUgdGIoKSBmdW5jdGlvbiAodGhpbmsgb2YNCnRiIGFzIGEgZGltaW51dGl2ZSBmb3IgdGliYmxlKS4gRm9yIGV4YW1wbGU6DQoNCmBgYHtyfQ0KYXBhcnRtZW50cyAlPiUNCiAgZGVzY3Ioc3RhdHMgPSAiYWxsIikgJT4lDQogIHRiKCkNCmBgYA0KDQpIZXJlIGFyZSBzb21lIGV4YW1wbGVzIHNob3dpbmcgaG93IGxpc3RzIGNyZWF0ZWQgdXNpbmcgc3RieSgpIG9yDQpncm91cF9ieSgpIGNhbiBiZSB0cmFuc2Zvcm1lZCBpbnRvIHRpZHkgdGliYmxlcy4NCg0KYGBge3J9DQpncm91cGVkX2Rlc2NyIDwtIHN0YnkoZGF0YSAgICA9IGFwYXJ0bWVudHMsSU5ESUNFUyA9IGFwYXJ0bWVudHMkcm9vbXMsIEZVTiAgICAgPSBkZXNjciwgc3RhdHMgICA9ICJjb21tb24iKQ0KDQpncm91cGVkX2Rlc2NyICU+JSB0YigpDQpgYGANCg0KIyMjIEEgQnJpZGdlIHRvIE90aGVyIFBhY2thZ2VzDQoNCmBgYHtyfQ0Kc3RieShkYXRhICAgID0gYXBhcnRtZW50cywgDQogICAgIElORElDRVMgPSBhcGFydG1lbnRzJHJvb21zLCANCiAgICAgRlVOICAgICA9IGRlc2NyLCANCiAgICAgc3RhdHMgICA9ICJmaXZlbnVtIikgJT4lDQogIHRiKG9yZGVyID0gMykgJT4lDQogIGthYmxlKGZvcm1hdCA9ICJodG1sIiwgZGlnaXRzID0gMikgJT4lDQogIGNvbGxhcHNlX3Jvd3MoY29sdW1ucyA9IDEsIHZhbGlnbiA9ICJ0b3AiKQ0KYGBgDQoNCiMjIFlvdXIgdHVybiENCg0KWW91ciB0YXNrIHRoaXMgd2VlayBpcyB0bzogcHJlcGFyZSB5b3VyIG93biBkZXNjcmlwdGl2ZSBhbmFseXNpcyBmb3IgdGhlDQoiQ3JlZGl0Q2FyZCIgZGF0YXNldCAoQUVSIHBhY2thZ2UpLiBJdCBpcyBhIGNyb3NzLXNlY3Rpb25hbCBkYXRhZnJhbWUgb24NCnRoZSBjcmVkaXQgaGlzdG9yeSBmb3IgYSBzYW1wbGUgb2YgYXBwbGljYW50cyBmb3IgYSB0eXBlIG9mIGNyZWRpdCBjYXJkLg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KZGF0YShDcmVkaXRDYXJkKQ0KQ3JlZGl0Q2FyZA0KIz9DcmVkaXRDYXJkICByZWFkIGRlc2NyaXB0aW9uIGZpcnN0DQpgYGANCg0KQXJlIHRoZSB5ZWFybHkgaW5jb21lcyAoaW4gVVNEIDEwLDAwMCksIGNyZWRpdCBjYXJkIGV4cGVuZGl0dXJlcywgYWdlLA0KcmF0aW8gb2YgbW9udGhseSBjcmVkaXQgY2FyZCBleHBlbmRpdHVyZSB0byB5ZWFybHkgaW5jb21lIC0NCnNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZvciBhcHBsaWNhbnRzIGZvciBjdXN0b21lcnMgd2l0aCBkaWZmZXJlbnQNCmNyZWRpdCByaXNrICgiY2FyZCIgdmFyaWFibGUgLSBmYWN0b3IpPw0KDQpQcmVwYXJlIGEgcHJvZmVzc2lvbmFsIGRhdGEgdmlzdWFsaXphdGlvbnMsIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MnDQp0YWJsZXMgYW5kIGludGVycHJldCB0aGVtLg0KDQpgYGB7ciBteV9zdW1tYXJ5X3RhYmxlfQ0KIyB5b3VyIGNvZGUgaGVyZQ0KQ3JlZGl0Q2FyZCAlPiUNCiAgZ3JvdXBfYnkoY2FyZCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBtZWFuX2luY29tZSA9IG1lYW4oaW5jb21lKSwNCiAgICBzZF9pbmNvbWUgPSBzZChpbmNvbWUpLA0KICAgIG1lYW5fZXhwZW5kaXR1cmUgPSBtZWFuKGV4cGVuZGl0dXJlKSwNCiAgICBzZF9leHBlbmRpdHVyZSA9IHNkKGV4cGVuZGl0dXJlKSwNCiAgICBtZWFuX2FnZSA9IG1lYW4oYWdlKSwNCiAgICBzZF9hZ2UgPSBzZChhZ2UpLA0KICAgIG1lYW5fc2hhcmUgPSBtZWFuKHNoYXJlKSwNCiAgICBzZF9zaGFyZSA9IHNkKHNoYXJlKQ0KICApICU+JQ0KICBrYWJsZShmb3JtYXQgPSAiaHRtbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIGZvbnRfc2l6ZSA9IDE0KSAlPiUNCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JQ0KICBjb2xsYXBzZV9yb3dzKGNvbHVtbnMgPSAxLCB2YWxpZ24gPSAibWlkZGxlIikNCg0KI0NyZWRpdENhcmQgJT4lDQogICNncm91cF9ieShjYXJkKSAlPiUNCiAgI2Rlc2NyKHN0YXRzID0gImNvbW1vbiIpICU+JQ0KICAjdGIoKQ0KDQpnZ3Bsb3QoQ3JlZGl0Q2FyZCwgYWVzKHggPSBjYXJkLCB5ID0gaW5jb21lKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIkJveHBsb3Qgb2YgeWVhcmx5IGluY29tZXMgYnkgY3JlZGl0IHJpc2siLA0KICAgICAgIHggPSAiY3JlZGl0IHJpc2siLA0KICAgICAgIHkgPSAieWVhcmx5IGluY29tZSAoaW4gVVNEIDEwLDAwMCkiKQ0KDQpnZ3Bsb3QoQ3JlZGl0Q2FyZCwgYWVzKHggPSBjYXJkLCB5ID0gZXhwZW5kaXR1cmUpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInB1cnBsZSIpICsNCiAgbGFicyh0aXRsZSA9ICJCb3hwbG90IG9mIGNyZWRpdCBjYXJkIGV4cGVuZGl0dXJlcyBieSBjcmVkaXQgcmlzayIsDQogICAgICAgeCA9ICJjcmVkaXQgcmlzayIsDQogICAgICAgeSA9ICJjcmVkaXQgY2FyZCBleHBlbmRpdHVyZSIpDQoNCmdncGxvdChDcmVkaXRDYXJkLCBhZXMoeCA9IGFnZSwgeSA9IHNoYXJlLCBjb2xvciA9IGNhcmQpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGxhYnModGl0bGUgPSAiU2NhdHRlcnBsb3Qgb2YgYWdlIGFuZCByYXRpbyBvZiBtb250aGx5IGV4cGVuZGl0dXJlIHRvIHllYXJseSBpbmNvbWUgYnkgY3JlZGl0IHJpc2siLA0KICAgICAgIHggPSAiYWdlIiwNCiAgICAgICB5ID0gInJhdGlvIG9mIG1vbnRobHkgZXhwZW5kaXR1cmUgdG8geWVhcmx5IGluY29tZSIsDQogICAgICAgY29sb3IgPSAiY3JlZGl0IHJpc2siKQ0KDQojIEJhc2VkIG9uIHRoaXMgZGF0YSB2aXN1YWxpemF0aW9uIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGF0IGluIHNvbWUgYXNwZWN0cyB0aGVyZSBhcmUgaHVnZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGNyZWRpdCBjYXJkcyBhY2NlcHRlZCBhbmQgbm90IGFjY2VwdGVkLiANCg0KI0ZpcnN0IG9mIGFsbCBmb3Igbm90IGFjY2VwdGVkIGNhcmRzIG1vbnRobHkgZXhwZW5kaXR1cmUgZG9lc24ndCBleGlzdC4NCg0KI1RoZXJlIGlzIGEgaHVnZSBkaWZmZXJlbmNlIGluIHJhdGlvIGFzIGZvciBleGFtcGxlIGZvciBhY2NlcHRlZCBtZWFuIGlzIGVxdWFsIHRvIDAuMDg4IGFuZCBmb3Igbm90IGFjY2VwdGVkIGl0IGlzIDAuMDAwNQ0KDQojQnV0IHdlIHNob3VsZCBhbHNvIG1lbnRpb24gdGhpbmdzIHRoYXQgZG9udCBjaGFuZ2Ugb3IgZGlmZmVyIHRoYXQgbXVjaCB3aXRoIHRoaXMgZmFjdG9yIGxpa2U6IGFnZSwgaW5jb21lLg0KYGBgDQo=