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 .
Table 1. Apartments in Wroclaw - prices in PLN by number of rooms.
1 room
2 rooms
3 rooms
4 rooms
Min
359769.00
590286.00
632770.00
736669.00
Max
657146.00
888634.00
965829.00
1277691.00
Q1
479684.75
634757.25
769683.75
909371.50
Median
520507.00
677260.00
846303.50
964338.50
Q3
555024.75
717728.50
901078.75
1050976.75
Mean
515518.05
683567.70
833706.02
974809.96
Sd
66951.03
65072.66
86943.90
113819.21
IQR
75340.00
82971.25
131395.00
141605.25
Sx
37670.00
41485.62
65697.50
70802.62
Var %
0.13
0.10
0.10
0.12
IQR Var %
0.14
0.12
0.16
0.15
Skewness
-0.20
0.80
-0.42
0.33
Kurtosis
-0.38
0.48
-0.83
0.05
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
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 = "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
price_EUR
2
136640.0
146754.0
156773.00
166259.00
205702.0
price_EUR
3
146475.0
177478.0
195904.00
208599.00
223572.0
price_EUR
4
170525.0
209827.5
223226.50
243300.00
295762.0
price_PLN
1
359769.0
479005.5
520507.00
555411.50
657146.0
price_PLN
2
590286.0
633978.0
677260.00
718237.00
888634.0
price_PLN
3
632770.0
766707.0
846303.50
901149.00
965829.0
price_PLN
4
736669.0
906455.0
964338.50
1051055.50
1277691.0
size
1
17.0
18.1
19.10
20.60
21.9
size
2
29.6
32.9
35.95
40.50
43.7
size
3
41.2
47.9
53.45
59.70
65.2
size
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
LS0tDQp0aXRsZTogJ0Rlc2NyaXB0aXZlIFN0YXRpc3RpY3MnDQpzdWJ0aXRsZTogJ1VuaXZhcmlhdGUgU3RhdGlzdGljcycNCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCmF1dGhvcjogIllvdXIgTmFtZSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIGZvbnRzaXplOiA4cHQNCiAgICB0b2M6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICBkZl9wcmludDogZGVmYXVsdA0KICAgIHRvY19kZXB0aDogNQ0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cDEsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpvcHRpb25zKHF3cmFwczJfbWFya3VwID0gIm1hcmtkb3duIikNCmxpYnJhcnkocXdyYXBzMikNCmxpYnJhcnkoYXJzZW5hbCkNCmxpYnJhcnkoZTEwNzEpDQpsaWJyYXJ5KGhhdmVuKQ0KbGlicmFyeShwYXBlUikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkoc3VtbWFyeXRvb2xzKQ0KbGlicmFyeShjbGFzc0ludCkNCmxpYnJhcnkocGFzdGVjcykNCmxpYnJhcnkocmVwb3J0dG9vbHMpDQpsaWJyYXJ5KGRlc2N0YWJsZSkNCmxpYnJhcnkocHN5Y2gpDQpsaWJyYXJ5KGZyZXF1ZW5jeSkNCmxpYnJhcnkoZ2dwdWJyKQ0KbGlicmFyeShnZ2ZvcmNlKQ0KbGlicmFyeShnZ2Rpc3QpDQpsaWJyYXJ5KGdnaGFsdmVzKQ0KbGlicmFyeShndHN1bW1hcnkpDQpsaWJyYXJ5KEFFUikNCmRvd25sb2FkLmZpbGUoImh0dHBzOi8vZ2l0aHViLmNvbS9rZmxpc2lrb3dza2kvZHMvYmxvYi9tYXN0ZXIvZGF0YV9hcGFydG1lbnRzLmNzdj9yYXc9dHJ1ZSIsIGRlc3RmaWxlID0ibWllc3prYW5pYS5jc3YiLG1vZGU9IndiIikNCmFwYXJ0bWVudHMgPC0gcmVhZC5jc3YoIm1pZXN6a2FuaWEuY3N2IixzZXA9IjsiLGRlYz0iLCIpDQpgYGANCg0KIyMgRGF0YQ0KDQpJbiBvdXIgZXhhbXBsZSB0aGlzIHdlZWssIHdlIGFyZSBnb2luZyB0byB1c2UgdGhlIGZha2UgZGF0YSAtIGFib3V0IHJlYWwNCmVzdGF0ZXMgaW4gV3JvY2xhdyAtIHByaWNlcyBieSBkaXN0cmljdHMsIHNpemUgb2YgYXBhcnRtZW50cyBhbmQgbWFueQ0KbW9yZS4NCg0KIyMjIFByZXByb2Nlc3NpbmcNCg0KQXMgeW91IGNhbiBzZWUsIG5vdCBhbGwgZm9ybWF0cyBvZiBvdXIgdmFyaWFibGVzIGFyZSBhZGFwdGVkLiBXZSBuZWVkIHRvDQpwcmVwYXJlIGFwcHJvcHJpYXRlIGZvcm1hdHMgb2Ygb3VyIHZhcmlhYmxlcyBhY2NvcmRpbmcgdG8gdGhlaXINCm1lYXN1cmVtZW50IHNjYWxlIGFuZCBmdXR1cmUgYXBwbGljYXRpb24uDQoNCmBgYHtyIHdyYW5nbGluZywgaW5jbHVkZT1UUlVFfQ0KYXBhcnRtZW50cyRkaXN0cmljdDwtYXMuZmFjdG9yKGFwYXJ0bWVudHMkZGlzdHJpY3QpDQphcGFydG1lbnRzJGJ1aWxkaW5nX3R5cGU8LWFzLmZhY3RvcihhcGFydG1lbnRzJGJ1aWxkaW5nX3R5cGUpDQphcGFydG1lbnRzJHJvb21zPC1mYWN0b3IoYXBhcnRtZW50cyRyb29tcyxvcmRlcmVkPVRSVUUpDQphdHRhY2goYXBhcnRtZW50cykNCmFwYXJ0bWVudHMkcHJpY2VfUExOPC1hcy5udW1lcmljKGFwYXJ0bWVudHMkcHJpY2VfUExOKQ0KYXBhcnRtZW50cyRwcmljZV9FVVI8LWFzLm51bWVyaWMoYXBhcnRtZW50cyRwcmljZV9FVVIpDQpgYGANCg0KIyMgRnJlcXVlbmN5IFRhYmxlcw0KDQpJbiB0aGUgZmlyc3Qgc3RlcCBvZiBvdXIgYW5hbHlzaXMsIHdlIHdpbGwgZ3JvdXAgb3VyIGRhdGEgaW50byBhIHNpbXBsZQ0KZnJlcXVlbmN5IHRhYmxlLg0KDQpGaXJzdCwgbGV0J3MgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGhvdXNpbmcgcHJpY2VzIGluIG91ciBzYW1wbGUNCmFuZCB2ZXJpZnkgdGFidWxhciB2YWxpZGl0eSB1c2luZyB0aGUgVEFJIG1lYXN1cmU6DQoNCmBgYHtyIHRhYmxlLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCmV0eWtpZXR5PC1jKCIzNTAtNDUwIGtQTE4iLCI0NTAtNTUwIGtQTE4iLCI1NTAtNjUwIGtQTE4iLCI2NTAtNzUwIGtQTE4iLCI3NTAtODUwIGtQTE4iLCI4NTAtOTUwIGtQTE4iLCI5NTAtMTA1MCBrUExOIiwiMTA1MC0xMTUwIGtQTE4iLCIxMTUwLTEyNTAga1BMTiIsIjEyNTAtMTM1MCBrUExOIikNCmxpbWl0czwtY3V0KGFwYXJ0bWVudHMkcHJpY2VfUExOLHNlcSgzNTAwMDAsMTM1MDAwMCxieT0xMDAwMDApLGxhYmVscz1ldHlraWV0eSkNCnRhYmVsYTE8LWZyZXEobGltaXRzLHR5cGU9Imh0bWwiKQ0KYGBgDQoNCk9rLCBpdCBsb29rcyBxdWl0ZSB1Z2x5LCBzbyBsZXQncyB3cmFwIGl0IHVwIHVzaW5nIHRoZSAna2FibGUnIHBhY2thZ2U6DQoNCmBgYHtyIHRhaSwgZWNobz1GQUxTRX0NCmtibCh0YWJlbGExLGNhcHRpb24gPSAiQXBhcnRtZW50cyBpbiBXcm9jbGF3IC0gcHJpY2VzIGluIGtQTE4iKSAlPiUNCiAgICBrYWJsZV9tYXRlcmlhbChjKCJzdHJpcGVkIiwgImhvdmVyIikpDQp0YWIxPC1jbGFzc0ludGVydmFscyhhcGFydG1lbnRzJHByaWNlX1BMTixuPTEwLHN0eWxlPSJmaXhlZCIsZml4ZWRCcmVha3M9c2VxKDM1MDAwMCwxMzUwMDAwLGJ5PTEwMDAwMCkpDQpqZW5rcy50ZXN0cyh0YWIxKQ0KYGBgDQoNCkFzIHdlIGNhbiBzZWUgLSB0aGUgVEFJIGluZGV4IGlzIHF1aXRlIGhpZ2guIDAuODUgbWVhbnMgdGhhdCB3ZSBjYW4NCmFjY2VwdCB0aGUgcHJvcG9zZWQgY29uc3RydWN0aW9uIG9mIHRoZSBmcmVxdWVuY3kgdGFibGUuDQoNCiMjIEJhc2ljIHBsb3RzDQoNCkluIHRoaXMgc2VjdGlvbiwgd2Ugc2hvdWxkIHJlcHJlc2VudCBvdXIgZGF0YSB1c2luZyBiYXNpYyAocHJlLWluc3RhbGxlZA0KaW4gUikgZ3JhcGhpY3MuIFNlbGVjdCB0aGUgbW9zdCBhcHByb3ByaWF0ZSBncmFwaHMgZGVwZW5kaW5nIG9uIHRoZQ0Kc2NhbGUgb2YgdGhlIHNlbGVjdGVkIHZhcmlhYmxlcy4gRXhwbG9yZSB0aGUgaGV0ZXJvZ2VuZWl0eSBvZiB0aGUNCmRpc3RyaWJ1dGlvbiBieSBwcmVzZW50aW5nIHRoZSBkYXRhIGJ5IGdyb3VwIChlLmcuLCBieSBuZWlnaGJvcmhvb2QsDQpidWlsZGluZyB0eXBlLCBldGMuKS4gRG9uJ3QgZm9yZ2V0IGFib3V0IG1haW4gdGl0bGVzLCBsYWJlbHMgYW5kDQpsZWdlbmRzLiBSZWFkIG1vcmUgYWJvdXQgZ3JhcGhpY2FsIHBhcmFtZXRlcnMNCltoZXJlXShodHRwOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL3dpa2kvZ3JhcGhpY2FsLXBhcmFtZXRlcnMpLg0KDQpgYGB7ciBoaXN0b2dyYW0sIGVjaG89RkFMU0V9DQpoaXN0KHByaWNlX1BMTiwgYnJlYWtzPSJGRCIsIGNvbD0iZ3JlZW4iLCBwcm9iYWJpbGl0eSA9IFRSVUUsDQogICAgIG1haW49IlByaWNlcyBpbiBQTE4gLSBXcm9jbGF3IikNCmxpbmVzKGRlbnNpdHkocHJpY2VfUExOW2Rpc3RyaWN0PT0iS3J6eWtpIl0pLGNvbD0yKQ0KbGluZXMoZGVuc2l0eShwcmljZV9QTE5bZGlzdHJpY3Q9PSJCaXNrdXBpbiJdKSxjb2w9MykNCmxpbmVzKGRlbnNpdHkocHJpY2VfUExOW2Rpc3RyaWN0PT0iU3JvZG1pZXNjaWUiXSksY29sPTQpDQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kPWMoIktyenlraSIsICJCaXNrdXBpbiIsICJTcm9kbWllc2NpZSIpLA0KICAgICAgIGNvbD1jKDIsMyw0KSwgbHR5PTE6MiwgaG9yaXo9RkFMU0UsIGJveC5sdHk9MCwgY2V4PTAuOCkNCg0KYGBgDQoNCk5vdGUgdGhhdCB0aGUgYGVjaG8gPSBGQUxTRWAgcGFyYW1ldGVyIGhhcyBiZWVuIGFkZGVkIHRvIHRoZSBjb2RlDQpzbmlwcGV0IHRvIHByZXZlbnQgcHJpbnRpbmcgdGhlIFIgY29kZSB0aGF0IGdlbmVyYXRlZCB0aGUgZ3JhcGguDQoNCmBgYHtyIGJveHBsb3QsIGVjaG89RkFMU0V9DQpib3hwbG90KHByaWNlX1BMTn5kaXN0cmljdCkNCmBgYA0KDQojIyBnZ3Bsb3QyIHBsb3RzDQoNCk5vdywgbGV0J3MgdXNlIHRoZSAqKipnZ3Bsb3QyKioqIGFuZCAqKipnZ3B1YnIqKiogbGlicmFyaWVzIHRvIHBsb3QuDQoNCmBgYHtyIGhpc3RvZ3JhbTIsIGVjaG89RkFMU0V9DQojIERlbnNpdHkgcGxvdCBvZiAicHJpY2VfUExOIg0KIzo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6DQpkZW5zaXR5LnAgPC0gZ2dkZW5zaXR5KGFwYXJ0bWVudHMsIHggPSAicHJpY2VfUExOIiwgDQogICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiZGlzdHJpY3QiLCBwYWxldHRlID0gImpjbyIpKw0KICBzdGF0X292ZXJsYXlfbm9ybWFsX2RlbnNpdHkoY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikNCg0KIyBEcmF3IHRoZSBzdW1tYXJ5IHRhYmxlIG9mIHByaWNlX1BMTg0KIzo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6DQojIENvbXB1dGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBieSBncm91cHMNCnN0YWJsZSA8LSBkZXNjX3N0YXRieShhcGFydG1lbnRzLCBtZWFzdXJlLnZhciA9ICJwcmljZV9QTE4iLA0KICAgICAgICAgICAgICAgICAgICAgIGdycHMgPSAiZGlzdHJpY3QiKQ0Kc3RhYmxlIDwtIHN0YWJsZVssIGMoImRpc3RyaWN0IiwgImxlbmd0aCIsICJtZWFuIiwgInNkIildDQojIFN1bW1hcnkgdGFibGUgcGxvdCwgbWVkaXVtIG9yYW5nZSB0aGVtZQ0Kc3RhYmxlLnAgPC0gZ2d0ZXh0dGFibGUoc3RhYmxlLCByb3dzID0gTlVMTCwgDQogICAgICAgICAgICAgICAgICAgICAgICB0aGVtZSA9IHR0aGVtZSgibU9yYW5nZSIpKQ0KIyBEcmF3IHRleHQNCiM6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Og0KdGV4dCA8LSBwYXN0ZSgiUHJpY2UgcGVyIGFwYXJ0bWVudCBieSAzIGRpc3RyaWN0cyAtIFdyb2NsYXcuIiwNCiAgICAgICAgICAgICAgIlJhbmRvbSBzYW1wbGUgb2YgMjAwIGFwYXJ0bWVudHMuIiwNCiAgICAgICAgICAgICAgIHNlcCA9ICIgIikNCnRleHQucCA8LSBnZ3BhcmFncmFwaCh0ZXh0ID0gdGV4dCwgZmFjZSA9ICJpdGFsaWMiLCBzaXplID0gMTEsIGNvbG9yID0gImJsYWNrIikNCiMgQXJyYW5nZSB0aGUgcGxvdHMgb24gdGhlIHNhbWUgcGFnZQ0KZ2dhcnJhbmdlKGRlbnNpdHkucCwgc3RhYmxlLnAsIHRleHQucCwgDQogICAgICAgICAgbmNvbCA9IDEsIG5yb3cgPSAzLA0KICAgICAgICAgIGhlaWdodHMgPSBjKDEsIDAuNSwgMC4zKSkNCmBgYA0KDQpHZ3Bsb3QyIGFsbG93cyB5b3UgdG8gc2hvdyB0aGUgYXZlcmFnZSB2YWx1ZSBmb3IgZWFjaCBncm91cCB1c2luZyB0aGUNCioqc3RhdF9zdW1tYXJ5KCkqKiBmdW5jdGlvbi4gWW91IG5vIGxvbmdlciBuZWVkIHRvIGNhbGN1bGF0ZSBhdmVyYWdlDQp2YWx1ZXMgYmVmb3JlIGNyZWF0aW5nIGEgZ3JhcGghDQoNCmBgYHtyIGJveHBsb3QyLCBlY2hvPUZBTFNFfQ0KZ2dwbG90KGFwYXJ0bWVudHMsIGFlcyh4PWRpc3RyaWN0LCB5PXByaWNlX1BMTikpICsNCiAgICBnZW9tX2JveHBsb3QoYWxwaGE9MC43KSArDQogICAgc3RhdF9zdW1tYXJ5KGZ1bj0ibWVhbiIsIGdlb209InBvaW50Iiwgc2hhcGU9MjAsIHNpemU9NSwgY29sb3I9InJlZCIsIGZpbGw9InJlZCIpICsNCiBnZW9tX2ppdHRlcigpICsNCiAgICBmYWNldF9ncmlkKH5idWlsZGluZ190eXBlKSArDQogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpDQoNCmBgYA0KDQojIyMgUmFpbkNsb3VkIFBsb3QNCg0KYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmFwYXJ0bWVudHMgJT4lIA0KICBmaWx0ZXIocm9vbXMgJWluJSBjKDEsIDIsIDMsIDQpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZhY3Rvcihyb29tcyksIHkgPSBwcmljZV9QTE4sIGZpbGwgPSBmYWN0b3Iocm9vbXMpKSkgKw0KICANCiAgIyBhZGQgaGFsZi12aW9saW4gZnJvbSB7Z2dkaXN0fSBwYWNrYWdlDQogIHN0YXRfaGFsZmV5ZSgNCiAgICAjIGFkanVzdCBiYW5kd2lkdGgNCiAgICBhZGp1c3QgPSAwLjUsDQogICAgIyBtb3ZlIHRvIHRoZSByaWdodA0KICAgIGp1c3RpZmljYXRpb24gPSAtMC4yLA0KICAgICMgcmVtb3ZlIHRoZSBzbHViIGludGVydmFsDQogICAgLndpZHRoID0gMCwNCiAgICBwb2ludF9jb2xvdXIgPSBOQQ0KICApICsNCiAgZ2VvbV9ib3hwbG90KA0KICAgIHdpZHRoID0gMC4xMiwNCiAgICAjIHJlbW92aW5nIG91dGxpZXJzDQogICAgb3V0bGllci5jb2xvciA9IE5BLA0KICAgIGFscGhhID0gMC41DQogICkgKw0KICBzdGF0X2RvdHMoDQogICAgIyBwbG90aW5nIG9uIGxlZnQgc2lkZQ0KICAgIHNpZGUgPSAibGVmdCIsDQogICAgIyBhZGp1c3RpbmcgcG9zaXRpb24NCiAgICBqdXN0aWZpY2F0aW9uID0gMS4xLA0KICAgICMgYWRqdXN0IGdyb3VwaW5nIChiaW5uaW5nKSBvZiBvYnNlcnZhdGlvbnMNCiAgICBiaW53aWR0aCA9IDAuMjUNCiAgKSArDQojIFRoZW1lcyBhbmQgTGFiZWxzDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUmFpbkNsb3VkIFBsb3QiLA0KICAgIHggPSAiTm8uIG9mIHJvb21zIiwNCiAgICB5ID0gIlByaWNlcyBpbiBQTE4iLA0KICAgIGZpbGwgPSAicm9vbXMiDQogICkgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQojIyMgRmFjZXRpbmcNCg0KRmFjZXRpbmcgZ2VuZXJhdGVzIHNtYWxsIG11bHRpcGxlcywgZWFjaCBzaG93aW5nIGEgZGlmZmVyZW50IHN1YnNldCBvZg0KdGhlIGRhdGEuIFRoZXkgYXJlIGEgcG93ZXJmdWwgdG9vbCBmb3IgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpczogeW91DQpjYW4gcXVpY2tseSBjb21wYXJlIHBhdHRlcm5zIGluIGRpZmZlcmVudCBwYXJ0cyBvZiB0aGUgZGF0YSBhbmQgc2VlIGlmDQp0aGV5IGFyZSB0aGUgc2FtZSBvciBkaWZmZXJlbnQuIFJlYWQgbW9yZQ0KW2hlcmVdKGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9mYWNldC5odG1sKS4NCg0KYGBge3IgZmFjZXQxLCBlY2hvPUZBTFNFfQ0KcGxvdDEgPC0gZ2dwbG90KGFwYXJ0bWVudHMsIGFlcyhwcmljZV9QTE4sIHJvb21zKSkgKyANCiAgZ2VvbV9hYmxpbmUoKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4xLCBoZWlnaHQgPSAwLjEpIA0KcGxvdDEgKyBmYWNldF93cmFwKH5kaXN0cmljdCkNCmBgYA0KDQojIyBVbml2YXJpYXRlIFN0YXRpc3RpY3MNCg0KQmVmb3JlIGF1dG9tYXRpY2FsbHkgcmVwb3J0aW5nIHRoZSBmdWxsIHN1bW1hcnkgdGFibGUgb2YgZGVzY3JpcHRpdmUNCnN0YXRpc3RpY3MsIHRoaXMgdGltZSB5b3VyIGdvYWwgaXMgdG8gbWVhc3VyZSB0aGUgY2VudHJhbCB0ZW5kZW5jeSBvZg0KdGhlIHByaWNlIGRpc3RyaWJ1dGlvbi4gQ29tcGFyZSB0aGUgbWVhbiwgbWVkaWFuLCBhbmQgbW9kZSBhbG9uZyB3aXRoDQpwb3NpdGlvbmFsIG1lYXN1cmVzIC0gcXVhbnRpbGVzIC0gYnkgZGlzdHJpY3QgYW5kIGJ1aWxkaW5nIHR5cGUgb3INCm51bWJlciBvZiByb29tcyBpbiB0aGUgYXBhcnRtZW50Lg0KDQpgYGB7cn0NCiAgICBtZWFuKHByaWNlX1BMTikNCiAgICBtZWRpYW4ocHJpY2VfUExOKQ0KICAgIHNkKHByaWNlX1BMTikgI3N0YW5kYXJkIGRldmlhdGlvbg0KICAgIHZhcihwcmljZV9QTE4pICN2YXJpYW5jZQ0KICAgIGNvZWZmX3Zhcjwtc2QocHJpY2VfUExOKS9tZWFuKHByaWNlX1BMTikgI2NvZWZmaWNpZW50IG9mIHZhcmlhYmlsaXR5ICUNCiAgICBjb2VmZl92YXINCiAgICBJUVIocHJpY2VfUExOKSMgZGlmZmVyZW5jZSBiZXR3ZWVuIHF1YXJ0aWxlcyA9UTMtUTEgDQogICAgc3g8LUlRUihwcmljZV9QTE4pLzIgICNpbnRlcnF1YXJ0aWxlIGRldmlhdGlvbg0KICAgIGNvZWZmX3Zhcng8LXN4L21lZGlhbihwcmljZV9QTE4pICNJUVIgY29lZmZpY2llbnQgb2YgdmFyaWFiaWxpdHkgJQ0KICAgIGNvZWZmX3ZhcngNCiAgICBtaW4ocHJpY2VfUExOKQ0KICAgIG1heChwcmljZV9QTE4pDQogICAgcXVhbnRpbGUocHJpY2VfUExOLHByb2JzPWMoMCwwLjEsMC4yNSwwLjUsMC43NSwwLjk1LDEpLG5hLnJtPVRSVUUpDQpgYGANCg0KT2ssIHdlIGhhdmUgY2FsY3VsYXRlZCBhbGwgb2YgdGhlIGJhc2ljIHN1bW1hcnkgc3RhdGlzdGljcyBhYm92ZS4gTGV0J3MNCndyYXAgdGhlbSB1cCB0b2dldGhlciBub3cuDQoNCmBgYHtyIGthYmxlX3JlcG9ydCwgZWNobz1GQUxTRX0NCmFwYXJ0bWVudHNfbGlzdCA8LSBzcGxpdChhcGFydG1lbnRzJHByaWNlX1BMTiwgYXBhcnRtZW50cyRyb29tcykNCmlubGluZV9wbG90IDwtIGRhdGEuZnJhbWUocm9vbXMgPSBjKDEsIDIsIDMsIDQpLCBib3hwbG90ID0gIiIsIGhpc3RvZ3JhbSA9ICIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lMSA9ICIiLCBsaW5lMiA9ICIiLCBwb2ludHMxID0gIiIpDQppbmxpbmVfcGxvdCAlPiUNCiAga2JsKGJvb2t0YWJzID0gVFJVRSkgJT4lDQogIGthYmxlX3BhcGVyKGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lDQogIGNvbHVtbl9zcGVjKDIsIGltYWdlID0gc3BlY19ib3hwbG90KGFwYXJ0bWVudHNfbGlzdCkpICU+JQ0KICBjb2x1bW5fc3BlYygzLCBpbWFnZSA9IHNwZWNfaGlzdChhcGFydG1lbnRzX2xpc3QpKSAlPiUNCiAgY29sdW1uX3NwZWMoNCwgaW1hZ2UgPSBzcGVjX3Bsb3QoYXBhcnRtZW50c19saXN0LCBzYW1lX2xpbSA9IFRSVUUpKSAlPiUNCiAgY29sdW1uX3NwZWMoNSwgaW1hZ2UgPSBzcGVjX3Bsb3QoYXBhcnRtZW50c19saXN0LCBzYW1lX2xpbSA9IEZBTFNFKSkgJT4lDQogIGNvbHVtbl9zcGVjKDYsIGltYWdlID0gc3BlY19wbG90KGFwYXJ0bWVudHNfbGlzdCwgdHlwZSA9ICJwIikpDQoNCmBgYA0KDQojIyMgU3VtbWFyeSB0YWJsZXMNCg0KT2ssIG5vdyB3ZSB3aWxsIGZpbmFsbHkgc3VtbWFyaXplIHRoZSBiYXNpYyBtZWFzdXJlcyBvZiBjZW50cmFsIHRlbmRlbmN5DQpmb3IgcHJpY2VzIGJ5IGRpc3RyaWN0L2J1aWxkaW5nIHR5cGUgdXNpbmcgdGhlICcqKiprYWJsZSoqKicgcGFja2FnZS4NCkZlZWwgZnJlZSB0byBjdXN0b21pemUgeW91ciBmaW5hbCByZXBvcnQuIFNlZSBzb21lIGhpbnRzDQpbaGVyZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3F3cmFwczIvdmlnbmV0dGVzL3N1bW1hcnktc3RhdGlzdGljcy5odG1sKS4NCg0KYGBge3Iga2FibGVfcmVwb3J0MiwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnJhcG9ydCA8LQ0KICBsaXN0KCJQcmljZSBpbiBQTE4iID0NCiAgICAgICBsaXN0KCJNaW4iICAgICAgID0gfiBtaW4ocHJpY2VfUExOKSwNCiAgICAgICAgICAgICJNYXgiICAgICAgID0gfiBtYXgocHJpY2VfUExOKSwNCiAgICAgICAgICAgICJRMSIgICAgICAgID0gfiBxdWFudGlsZShwcmljZV9QTE4sMC4yNSksDQogICAgICAgICAgICAiTWVkaWFuIiA9IH4gcm91bmQobWVkaWFuKHByaWNlX1BMTiksMiksDQogICAgICAgICAgICAiUTMiICAgICAgICA9IH4gcXVhbnRpbGUocHJpY2VfUExOLDAuNzUpLA0KICAgICAgICAgICAgIk1lYW4iID0gfiByb3VuZChtZWFuKHByaWNlX1BMTiksMiksDQogICAgICAgICAgICAiU2QiID0gfiByb3VuZChzZChwcmljZV9QTE4pLDIpLA0KICAgICAgICAgICAgICJJUVIiID0gfiByb3VuZChpcXIocHJpY2VfUExOKSwyKSwNCiAgICAgICAgICAgICJTeCIgPSB+IHJvdW5kKGlxcihwcmljZV9QTE4pLzIsMiksDQogICAgICAgICAgICAiVmFyICUiID0gfiByb3VuZCgoc2QocHJpY2VfUExOKS9tZWFuKHByaWNlX1BMTikpLDIpLA0KICAgICAgICAgICAgIklRUiBWYXIgJSIgPSB+IHJvdW5kKChpcXIocHJpY2VfUExOKS9tZWRpYW4ocHJpY2VfUExOKSksMiksDQogICAgICAgICAgICAiU2tld25lc3MiID0gfiAgcm91bmQoc2tldyhwcmljZV9QTE4pLDIpLA0KICAgICAgICAgICAgICJLdXJ0b3NpcyIgPSB+ICByb3VuZChrdXJ0b3NpKHByaWNlX1BMTiksMikNCiAgICAgICAgICAgICkpDQp0YWJlbGE8LXN1bW1hcnlfdGFibGUoYXBhcnRtZW50cywgc3VtbWFyaWVzID0gcmFwb3J0LCBieSA9IGMoInJvb21zIikpDQoNCmtibCh0YWJlbGEsICBkaWdpdHMgPSAyLA0KICBjYXB0aW9uPSJUYWJsZSAxLiBBcGFydG1lbnRzIGluIFdyb2NsYXcgLSBwcmljZXMgaW4gUExOIGJ5IG51bWJlciBvZiByb29tcy4iLCAgY29sLm5hbWVzID0gYygnMSByb29tJywgJzIgcm9vbXMnLCAnMyByb29tcycsICc0IHJvb21zJykpICU+JSBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGLCBodG1sX2ZvbnQgPSAiQ2FtYnJpYSIpJT4lIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KIyMjIGd0c3VtbWFyeQ0KDQpXZSBjYW4gY2FsY3VsYXRlIGVhc2lseSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGFsc28gdXNpbmcgZ3RzdW1tYXJ5DQpwYWNrYWdlOg0KDQpgYGB7cn0NCmFwYXJ0bWVudHMgJT4lDQogIHNlbGVjdChwcmljZV9QTE4scm9vbXMpICU+JQ0KICB0Ymxfc3VtbWFyeShsYWJlbD0gcHJpY2VfUExOIH4gIlByaWNlIixkaWdpdHM9YyhwcmljZV9QTE4pfjIsYnk9cm9vbXMsdHlwZSA9IGFsbF9jb250aW51b3VzKCkgfiAiY29udGludW91czIiLCBzdGF0aXN0aWMgPSBhbGxfY29udGludW91cygpIH4gYygie05fbm9ubWlzc30iLCAie21lZGlhbn0gKHtwMjV9LCB7cDc1fSkiLCAie21pbn0sIHttYXh9IiksbWlzc2luZyA9ICJubyIpDQpgYGANCg0KIyMjIGRmU3VtbWFyeQ0KDQpkZlN1bW1hcnkoKSBjcmVhdGVzIGEgc3VtbWFyeSB0YWJsZSB3aXRoIHN0YXRpc3RpY3MsIGZyZXF1ZW5jaWVzIGFuZA0KZ3JhcGhzIGZvciBhbGwgdmFyaWFibGVzIGluIGEgZGF0YSBmcmFtZS4gVGhlIGluZm9ybWF0aW9uIGRpc3BsYXllZCBpcw0KdHlwZS1zcGVjaWZpYyAoY2hhcmFjdGVyLCBmYWN0b3IsIG51bWVyaWMsIGRhdGUpIGFuZCBhbHNvIHZhcmllcw0KYWNjb3JkaW5nIHRvIHRoZSBudW1iZXIgb2YgZGlzdGluY3QgdmFsdWVzLg0KDQpXaGVuIHVzaW5nIGRmU3VtbWFyeSgpIGluIFIgTWFya2Rvd24gZG9jdW1lbnRzLCBpdCBpcyBnZW5lcmFsbHkgYSBnb29kDQppZGVhIHRvIGV4Y2x1ZGUgYSBjb2x1bW4gb3IgdHdvIHRvIGF2b2lkIG1hcmdpbiBvdmVyZmxvdy4gU2luY2UgdGhlDQpWYWxpZCBhbmQgTWlzc2luZyBjb2x1bW5zIGFyZSByZWR1bmRhbnQsIHdlIGNhbiBkcm9wIGVpdGhlciBvbmUgb2YgdGhlbS4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgcmVzdWx0cz0iYXNpcyJ9DQpkZlN1bW1hcnkoYXBhcnRtZW50cywNCiAgICAgICAgICBwbGFpbi5hc2NpaSAgPSBGQUxTRSwgDQogICAgICAgICAgc3R5bGUgICAgICAgID0gImdyaWQiLCANCiAgICAgICAgICBncmFwaC5tYWduaWYgPSAwLjc1LCANCiAgICAgICAgICB2YWxpZC5jb2wgICAgPSBGQUxTRSwNCiAgICAgICAgICB0bXAuaW1nLmRpciAgPSAiL3RtcCIpDQpgYGANCg0KVG8gcHJvZHVjZSBvcHRpbWFsIHJlc3VsdHMsIHN1bW1hcnl0b29scyBoYXMgaXRzIG93biB2ZXJzaW9uIG9mIHRoZSBiYXNlDQpieSgpIGZ1bmN0aW9uLiBJdCdzIGNhbGxlZCBzdGJ5KCksIGFuZCB3ZSB1c2UgaXQgZXhhY3RseSBhcyB3ZSB3b3VsZA0KYnkoKToNCg0KYGBge3IgcmVzdWx0cz0iYXNpcyIsIHdhcm5pbmc9RkFMU0V9DQooc3RhdHNfYnlfcm9vbXMgPC0gc3RieShkYXRhICAgICAgPSBhcGFydG1lbnRzLCBJTkRJQ0VTICAgPSBhcGFydG1lbnRzJHJvb21zLCBGVU4gICAgICAgPSBkZXNjciwgc3RhdHMgICAgID0gImNvbW1vbiIsIHRyYW5zcG9zZSA9IFRSVUUpKQ0KYGBgDQoNCiMjIyBUaWR5IFRhYmxlcw0KDQpXaGVuIGdlbmVyYXRpbmcgZnJlcSgpIG9yIGRlc2NyKCkgdGFibGVzLCBpdCBpcyBwb3NzaWJsZSB0byB0dXJuIHRoZQ0KcmVzdWx0cyBpbnRvICJ0aWR5IiB0YWJsZXMgd2l0aCB0aGUgdXNlIG9mIHRoZSB0YigpIGZ1bmN0aW9uICh0aGluayBvZg0KdGIgYXMgYSBkaW1pbnV0aXZlIGZvciB0aWJibGUpLiBGb3IgZXhhbXBsZToNCg0KYGBge3J9DQphcGFydG1lbnRzICU+JQ0KICBkZXNjcihzdGF0cyA9ICJjb21tb24iKSAlPiUNCiAgdGIoKQ0KYGBgDQoNCkhlcmUgYXJlIHNvbWUgZXhhbXBsZXMgc2hvd2luZyBob3cgbGlzdHMgY3JlYXRlZCB1c2luZyBzdGJ5KCkgb3INCmdyb3VwX2J5KCkgY2FuIGJlIHRyYW5zZm9ybWVkIGludG8gdGlkeSB0aWJibGVzLg0KDQpgYGB7cn0NCmdyb3VwZWRfZGVzY3IgPC0gc3RieShkYXRhICAgID0gYXBhcnRtZW50cyxJTkRJQ0VTID0gYXBhcnRtZW50cyRyb29tcywgRlVOICAgICA9IGRlc2NyLCBzdGF0cyAgID0gImNvbW1vbiIpDQoNCmdyb3VwZWRfZGVzY3IgJT4lIHRiKCkNCmBgYA0KDQojIyMgQSBCcmlkZ2UgdG8gT3RoZXIgUGFja2FnZXMNCg0KYGBge3J9DQpzdGJ5KGRhdGEgICAgPSBhcGFydG1lbnRzLCANCiAgICAgSU5ESUNFUyA9IGFwYXJ0bWVudHMkcm9vbXMsIA0KICAgICBGVU4gICAgID0gZGVzY3IsIA0KICAgICBzdGF0cyAgID0gImZpdmVudW0iKSAlPiUNCiAgdGIob3JkZXIgPSAzKSAlPiUNCiAga2FibGUoZm9ybWF0ID0gImh0bWwiLCBkaWdpdHMgPSAyKSAlPiUNCiAgY29sbGFwc2Vfcm93cyhjb2x1bW5zID0gMSwgdmFsaWduID0gInRvcCIpDQpgYGANCg0KIyMgWW91ciB0dXJuIQ0KDQpZb3VyIHRhc2sgdGhpcyB3ZWVrIGlzIHRvOiBwcmVwYXJlIHlvdXIgb3duIGRlc2NyaXB0aXZlIGFuYWx5c2lzIGZvciB0aGUNCiJDcmVkaXRDYXJkIiBkYXRhc2V0IChBRVIgcGFja2FnZSkuIEl0IGlzIGEgY3Jvc3Mtc2VjdGlvbmFsIGRhdGFmcmFtZSBvbg0KdGhlIGNyZWRpdCBoaXN0b3J5IGZvciBhIHNhbXBsZSBvZiBhcHBsaWNhbnRzIGZvciBhIHR5cGUgb2YgY3JlZGl0IGNhcmQuDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpkYXRhKENyZWRpdENhcmQpDQojP0NyZWRpdENhcmQgIHJlYWQgZGVzY3JpcHRpb24gZmlyc3QNCmBgYA0KDQpBcmUgdGhlIHllYXJseSBpbmNvbWVzIChpbiBVU0QgMTAsMDAwKSwgY3JlZGl0IGNhcmQgZXhwZW5kaXR1cmVzLCBhZ2UsDQpyYXRpbyBvZiBtb250aGx5IGNyZWRpdCBjYXJkIGV4cGVuZGl0dXJlIHRvIHllYXJseSBpbmNvbWUgLQ0Kc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZm9yIGFwcGxpY2FudHMgZm9yIGN1c3RvbWVycyB3aXRoIGRpZmZlcmVudA0KY3JlZGl0IHJpc2sgKCJjYXJkIiB2YXJpYWJsZSAtIGZhY3Rvcik/DQoNClByZXBhcmUgYSBwcm9mZXNzaW9uYWwgZGF0YSB2aXN1YWxpemF0aW9ucywgZGVzY3JpcHRpdmUgc3RhdGlzdGljcycNCnRhYmxlcyBhbmQgaW50ZXJwcmV0IHRoZW0uDQoNCmBgYHtyIG15X3N1bW1hcnlfdGFibGV9DQojIHlvdXIgY29kZSBoZXJlDQpgYGANCg==