rm(list = ls())
library(tidyverse)
Warning: package ‘tidyverse’ was built under R version 4.1.3Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
-- Attaching packages ----------------------------------------------------------------------------------------- tidyverse 1.3.2 --v ggplot2 3.4.0 v purrr 0.3.4
v tibble 3.1.8 v dplyr 1.0.10
v tidyr 1.2.1 v stringr 1.4.0
v readr 2.1.3 v forcats 0.5.1 Warning: package ‘ggplot2’ was built under R version 4.1.3Warning: package ‘tibble’ was built under R version 4.1.3Warning: package ‘tidyr’ was built under R version 4.1.3Warning: package ‘readr’ was built under R version 4.1.3Warning: package ‘dplyr’ was built under R version 4.1.3-- Conflicts -------------------------------------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
library(ggplot2)
districts = read_csv("./data/districts.csv")
Rows: 146 Columns: 27-- Column specification ----------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (2): system_name, region
dbl (25): system, alg_1, alg_2, bio, chem, ela, eng_1, eng_2, eng_3, math, science, enrollment, black, hispanic, native, el, s...
i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
districts
There are 146 rows and 27 columns in districts.
Q2. Notice that the first row corresponds to the whole State of
Tennessee. Remove this row and save the result back to districts.
districts = districts[-1, ]
head(districts)
Q3. How many districts have a proficiency rate of at least 80% for
both alg_1 and eng_1?
districts%>%filter(alg_1 >= 80 & eng_1 >= 80)
count(districts%>%filter(alg_1 >= 80 & eng_1 >= 80))
There are 13 districts.
Q4. How many districts have a proficiency rate less than 50% for
either alg_1 or eng_1?
districts%>%filter(alg_1 < 50 | eng_1 < 50)
count(districts%>%filter(alg_1 < 50 | eng_1 < 50))
There are 8 districts.
Q5.Which district has the lowest graduation rate?
districts%>%filter(grad == min(districts$grad, na.rm = TRUE))%>%
select(system_name, grad)
Tennessee School for Blind has the lowest graduation rate.
#oa
districts%>%
arrange(grad) %>%
head(1)
Q6. Within the Mid Cumberland region, which district has the highest
ACT composite?
districts%>%filter(region == 'Mid Cumberland')%>%
filter(act_composite == max(act_composite, na.rm = TRUE))%>%
select(system_name, act_composite)
Q7. Create a histogram showing the distribution of graduation rates.
What can you say about this distribution?
ggplot(districts, aes(grad)) +
geom_histogram(binwidth = 5, fill = 'blue', color = 'black')+
scale_x_continuous(breaks=seq(0, 100, 10))

NA
districts %>%
ggplot(aes(x=grad, fill = region)) +
geom_histogram(bins = 50, na.rm = TRUE)

Most graduation rates fall between 80-100%. There is one outlier with
a graduation rate 11%.
Q8. Create a scatter plot to compare alg_1 proficiency rates to
alg_2 rates. What do you notice?
Facet this plot by region. Does anything stand out when you facet
the plots?
ggplot(districts) +
geom_point(aes(alg_1, alg_2)) +
scale_x_continuous(breaks=seq(0, 100, 10))

The scatter plot shows there is a strong correlation between alg_1
and alg_2 proficiency rates.
ggplot(districts) +
geom_point(aes(alg_1, alg_2, color = factor(region))) +
facet_wrap((.~region), nrow = 3, ncol = 3)+
xlim(0,100)+
ylim(0,100)

The scatter plots show Southwest/Memphis shows the strongest positive
correlation.Southeast shows the weakest correlation. Upper Cumberland
shows a non-linear trend.
Q9. Create a bar chart showing the total enrollment by region.
Which region has the highest total enrollment? Which has the
smallest?
options(scipen = 999)
p = ggplot(districts, aes(x=region, y=enrollment, fill = region)) +
geom_bar(stat = "identity") +
scale_fill_brewer(palette = "Dark2")
p + coord_flip()

NA
NA
Reordering by enrollment
library(scales)
Warning: package ‘scales’ was built under R version 4.1.3
Attaching package: ‘scales’
The following object is masked from ‘package:purrr’:
discard
The following object is masked from ‘package:readr’:
col_factor
options(scipen = 999)
p = ggplot(districts, aes(x = reorder(region, +enrollment), y=enrollment, fill = region)) +
geom_bar(stat = "identity", na.rm = T) +
scale_fill_brewer(palette = "Dark2")+
scale_y_continuous(labels = comma)
p + coord_flip()

upper cumberland and southwest memphis do not get in order. Mid
Cumberland has the highest enrollment, Northwest has the lowest
enrollment.
Q10. When creating this bar chart you may have noticed that some
districts have missing enrollment values.
For how many districts is this the case?
sum(is.na(districts$enrollment))
[1] 4
districts%>%filter(is.na(enrollment))%>%group_by(region, system_name)%>%tally()
Q11. What is the mean graduation rate across all districts?
What might be wrong with using just the regular mean to assess
average graduation rates?
mean(districts$grad, na.rm = TRUE)
[1] 90.06562
Q12. Redo the previous question but use a weighted average
(weighted.mean) graduation across all districts, weighing by
enrollment.
How much does this change your answer?
Can you explain using the data the reason for the big change from
using the mean?
weighted.mean(districts$grad, dplyr::coalesce(districts$enrollment,0), na.rm=TRUE)
[1] 87.33285
weighted.mean(districts$grad, coalesce(districts$enrollment,0), na.rm=TRUE)
[1] 87.33285
districts%>%
summarize(grad_weighted_mean = weighted.mean(grad, coalesce(enrollment,0), na.rm=TRUE))
df <- districts %>% filter((!is.na(enrollment))&(!is.na(grad)))
weighted.mean(df$grad, df$enrollment)
[1] 87.33285
The unweighted graduation rate is 90%. The enrollment weighted
graduation rate is 87%. Enrollment weighted – schools with larger
enrollments influence the average more. The weighted average
decreased–schools with larger enrollments have smaller graduation rates.
Weighted-higher enrollments contribute more to the avg.
Q13. Create a boxplot showing graduation rates per region.
Does anything stand out?
ggplot(districts)+
geom_boxplot(aes(x=region, y=grad, fill = region))+
ggtitle('Graduation Rate by Region')+
scale_y_continuous(breaks = seq(0,100,5))+
coord_flip()

The boxplot shows outliers for Southwest/Memphis at ~50% and
MidCumberland at ~10%
Q14. Find the weighted average of graduation rates by region using
enrollment as weights.
Compare the results you get for the weighted average to what you see
from the boxplots.
options(digits = 3)
districts %>%
group_by(region)%>%
summarise(enr_wm = weighted.mean(grad, coalesce(enrollment,0), na.rm=T),
mean = mean(grad, na.rm =T))
NA
NA
NA
NA
Southwest/Memphis region has a lower graduation rate of 79.5, which
is consistent with its boxplot showing a small outlier and skewed left
distribution. Midcumberland’s graduation rate is not that much lower
than other regions even though its boxplot shows a small outlier around
~12%.
Q15. For many districts, values for alg_2 are lower than for
alg_1.
districts <- districts %>% mutate(alg_diff = alg_1 - alg_2)
Create a histogram showing the distribution of differences (alg_1 -
alg_2).
ggplot() +
geom_histogram(aes(x=alg_diff, fill="a"), alpha=.7, data=subset(districts, alg_diff <=0), color = "blue", binwidth = 5) +
geom_histogram(aes(x=alg_diff, fill="z"), alpha=.5, data=subset(districts, alg_diff >0), color="black", binwidth = 5) +
scale_fill_manual(name="", values=c("a" = "skyblue", "z" ="red"), labels=c("a"="Alg1 < Alg2", "z" ="Alg1 > Alg2"))+
scale_x_continuous(breaks=seq(-20, 60, 10))

NA
NA
The histogram shows about 20 districts had a drop in algebra by about
15 points.
Which school had the largest drop from alg_1 to alg_2?
districts%>%filter(alg_diff == max(alg_diff, na.rm=TRUE))%>%select(system_name, alg_1, alg_2, alg_diff)
Pickett County has the largest drop from alg_1 to alg_2.
For what percentage of schools is it true that alg_2 is larger than
alg_1?
districts%>%summarise(pct_sch = sum(alg_diff<0, na.rm = TRUE)/nrow(districts))
districts%>%summarise(pct_sch = sum(alg_diff<0, na.rm = TRUE)/ sum(!is.na(alg_diff)))
districts%>%summarise(pct_sch = 100*sum(na.omit(alg_diff<0))/145)
15.4% of schools have alg_2 greater than alg_1.
Is there a similar drop off for eng_2 and eng_3?
districts <- districts %>% mutate(eng23_diff = eng_2 - eng_3)
ggplot() +
geom_histogram(aes(x=eng23_diff, fill="a"), alpha=.9, data=subset(districts, eng23_diff <0), color = "blue", binwidth = 5) +
geom_histogram(aes(x=eng23_diff, fill="z"), alpha=.5, data=subset(districts, eng23_diff >0), color="black", binwidth = 5) +
scale_fill_manual(name="", values=c("a" = "skyblue", "z" ="red"), labels=c("a"="Eng2 < Eng3", "z" ="Eng2 > Eng3"))

#scale_x_continuous(breaks=seq(-20, 60, 10))
The histogram shows almost all schools had drops in scores from Eng2
to Eng3.
districts%>%summarise(pct_sch = 100*sum(na.omit(eng23_diff>0))/145)
86% of schools showed decrease in score from eng2 to eng3.
16. You may have noticed that a lot of rows are missing values.
Which district has the largest number of missing values?
What do you notice about schools that have a lot of missing
values?
districts %>% mutate(na_ct = rowSums(is.na(.)))%>%filter(na_ct == max(na_ct))
West Tenn School for Deaf has the most missing values
districts %>% mutate(na_ct = rowSums(is.na(.)))%>%arrange(desc(na_ct))
The missing values seem to come from the subject proficiency rate and
act_composite columns. Maybe the schools did not participate in the
state tests. They also have missing values in grad and dropout
columns
###Q17. Find the correlation between graduation rate and all other
variables. ### Create a horizontal bar chart showing these correlations.
### Make sure that your plot is ordered by correlation values. ### What
do you notice from these correlations?
districts %>%
select(where(is.numeric)) %>%
cor(. , districts$grad, use = 'complete.obs')
[,1]
system -0.0781
alg_1 0.3312
alg_2 0.3620
bio 0.3740
chem 0.2348
ela 0.2710
eng_1 0.3052
eng_2 0.3042
eng_3 0.4108
math 0.2990
science 0.3961
enrollment -0.4164
black -0.4199
hispanic -0.2958
native -0.0534
el -0.3423
swd 0.1714
ed -0.2130
expenditures -0.2167
act_composite 0.2004
chronic_abs -0.2434
suspended -0.5017
expelled -0.3954
grad 1.0000
dropout -0.9218
alg_diff -0.1934
eng23_diff -0.2223
cor_dt <- districts %>%
select(where(is.numeric)) %>%
cor(. , districts$grad, use = 'complete.obs') %>%
as_tibble(cor_dt, rownames = "variable") %>%
rename(correlation = V1)%>%
filter(variable != "grad")
Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if `.name_repair` is omitted as of tibble 2.0.0.
Using compatibility `.name_repair`.
cor_dt
ggplot(data = cor_dt, aes(y=reorder(variable, -correlation), x=correlation)) +
geom_bar(stat = 'identity')+
theme_bw()

NA
NA
NA
ggplot(data = cor_dt, aes(x=correlation, y=reorder(variable, -correlation), fill=correlation)) +
geom_bar(stat = 'identity')+
scale_fill_gradient(low='firebrick', high='skyblue', space='Lab')

NA
NA
NA
NA
Dropout and suspension rates are the most negatively correlated with
graduation rate. Scores in English, Algebra and Sciences are the most
positively correlated with graduation rates.
###Q18. Create a scatterplot for grad vs. suspended. ### Does what
you see make sense given your answer from the previous part?
ggplot(districts) +
geom_point(aes(suspended, grad), na.rm = TRUE)+
xlim(0,100)+
ylim(0,100)

NA
The scatter plot shows that lower suspension rates are associated
with higher graduation rates. But for lower suspension rates (<5%),
there does not seem to be a strong variability in graduation rate. The
negative correlation may be as strong as it is because of a few
outliers.
###Q19. Create a linear regression model using lm with target
variable grad and predictor variable suspended. ### What R^2 value does
this model have? What is the interpretation of this number?
simple_regression_model <-lm(grad ~ suspended, data = districts)
summary(simple_regression_model)
Call:
lm(formula = grad ~ suspended, data = districts)
Residuals:
Min 1Q Median 3Q Max
-81.41 -2.27 1.19 3.93 12.21
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 92.507 1.092 84.7 <0.0000000000000002 ***
suspended -0.786 0.238 -3.3 0.0013 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 9.09 on 126 degrees of freedom
(17 observations deleted due to missingness)
Multiple R-squared: 0.0795, Adjusted R-squared: 0.0722
F-statistic: 10.9 on 1 and 126 DF, p-value: 0.00126
The R^2 of the model is 0.0795 or 7.95%. 7.95% of the variability in
the graduation rate can be explained by suspension rate.
grad_lm <- districts %>%
filter(grad > 25) %>%
lm(grad ~ suspended, data = .)
summary(grad_lm)
Call:
lm(formula = grad ~ suspended, data = .)
Residuals:
Min 1Q Median 3Q Max
-24.815 -3.011 0.796 3.116 13.306
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 93.700 0.658 142.49 < 0.0000000000000002 ***
suspended -0.962 0.143 -6.74 0.00000000053 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 5.43 on 125 degrees of freedom
Multiple R-squared: 0.266, Adjusted R-squared: 0.26
F-statistic: 45.4 on 1 and 125 DF, p-value: 0.000000000532
Q20. Add the regression line to your scatterplot using geom_smooth
with method=‘lm’.
How do you feel about the regression line after seeing it plotted on
the scatterplot?
ggplot(districts, aes(x=suspended, y=grad)) +
geom_point()+
geom_smooth(method = "lm")

NA
NA
The regression line does not capture most points well, there is a lot
of scatter for suspended rate below 5%. This is consistent with the low
R^2 value for the model.
Continued Exploration and Practice
Q21. Read in the school-level testing data for 2014, available
here.
You might find the readxl library useful for this task. If you use
this library, be sure to look at the na argument for the read_excel
function.
library(readxl)
library(tidyverse)
testing <- read_excel("./data/data_2014_school_base.xlsx", na = c("*", "**"))
Q22. How many schools have at least 20 percent of students below bsc
for Algebra I?
colnames(testing)
[1] "year" "system" "system_name" "school" "school_name" "subject"
[7] "grade" "subgroup" "valid_tests" "n_below_bsc" "n_bsc" "n_prof"
[13] "n_adv" "pct_below_bsc" "pct_bsc" "pct_prof" "pct_adv" "pct_bsc_and_below"
[19] "pct_prof_adv"
count(testing %>% filter((subject == "Algebra I")&(pct_below_bsc >= 20)))
NA
Which districts do these schools belong to?
testing %>%
filter((subject == "Algebra I")&(pct_below_bsc >= 20)) %>%
distinct(system_name)
There are 102 districts that have at least 20% below bsc for Algebra
I.
count(testing %>% distinct(system_name))
There are 141 total districts in the dataset.
####Q23. How many schools have at least 20 percent of students below
bsc for both Algebra I and English I?
fil_tst <- testing %>%
filter(pct_below_bsc >= 20) %>%
filter(subject == "Algebra I" | subject == "English I") %>%
select(school_name, subject, pct_below_bsc)
fil_tst
fil_tst %>%
group_by(school_name) %>%
summarise(count = n_distinct(subject)) %>%
filter(count > 1)
There are 51 schools that have at least 20% below bsc in both English
I and Algebra I.
###Q24. Which grade has the highest pct_adv for Algebra I?
testing %>%
filter(subject == "Algebra I") %>%
filter(pct_adv == max(pct_adv, na.rm = T)) %>%
group_by(grade)
8th grade has the highest Algebra I grade.
Plot the average pct_adv per grade level as a bar chart.
testing %>%
group_by(grade) %>%
summarise(avg_pct_adv = mean(pct_adv, na.rm = T)) %>%
arrange(as.numeric(grade))
Warning: NAs introduced by coercion
NA
testing %>%
group_by(grade) %>%
summarise(avg_pct_adv = mean(pct_adv, na.rm = T)) %>%
ggplot(aes(x = factor(as.numeric(grade)), y=avg_pct_adv))+
geom_bar(stat = "identity", fill = 'skyblue3', color = 'black')+
xlab("Grade")+
ylab("Advanced Percentage")+
theme_minimal()

NA
NA
#Pct_adv only for Algebra I
testing %>%
filter(subject=="Algebra I") %>%
group_by(grade) %>%
summarise(avg_pct_adv = mean(pct_adv, na.rm = T)) %>%
ggplot(aes(x = factor(as.numeric(grade)), y=avg_pct_adv))+
geom_bar(stat = "identity", fill = 'steelblue', color = 'black')+
xlab("Grade")+
ylab("Advanced Percentage for Algebra I")

NA
NA
###Q25. Find the correlation between pct_adv for Algebra I and
pct_adv for Algebra II by school. #### Create a scatterplot showing
Algebra II scores vs. Algebra I scores by school.
#Filter dataset for AlgI/AlgII
algebra <- testing %>%
filter(subject == "Algebra I" | subject == "Algebra II") %>%
select(school_name, subject, pct_adv)
#Pivot algebra
alg_pvt <- algebra %>%
pivot_wider(names_from = subject, values_from = pct_adv, values_fn = function(x) mean(x, na.rm = TRUE))
alg_pvt <- alg_pvt %>% rename('Algebra_I' = 'Algebra I',
'Algebra_II' = 'Algebra II')
alg_pvt
ggplot(alg_pvt)+
geom_point(aes(Algebra_I, Algebra_II))+
xlim(0,100)+
ylim(0,100)

alg_pvt <- alg_pvt %>%transform(Algebra_I = as.numeric(Algebra_I), Algebra_II = as.numeric(Algebra_II))
cor(alg_pvt$Algebra_I, alg_pvt$Algebra_II, use = 'complete.obs')
[1] 0.462
Q19. Find all schools in Rutherford County that have “High School”
in their name.
For these schools, create a chart (your choice) showing the
differences in
pct_below_bsc, pct_bsc, pct_prof, and pct_adv for Algebra I when
looking across all subgroups and grades.
testing %>%
filter(system_name == 'Rutherford County') %>%
filter(str_detect(school_name, "High School"))%>%
filter(subject == 'Algebra I') %>%
select(school_name)%>%
distinct(school_name)
rcs <- testing %>%
filter(system_name == 'Rutherford County') %>%
filter(str_detect(school_name, "High School"))%>%
filter(subject == 'Algebra I') %>%
select(school_name, grade, subgroup, pct_below_bsc, pct_bsc, pct_prof, pct_adv, pct_bsc_and_below, pct_prof_adv)
pct_piv <- rcs %>% pivot_longer(cols = starts_with("pct"),
names_to = "themeasure",
values_to = "thenumber")
pct_piv %>%
filter(!themeasure %in% c("pct_bsc_and_below", "pct_prof_adv")) %>%
group_by(school_name, themeasure) %>%
summarize(pctg = mean(thenumber, na.rm = TRUE))
`summarise()` has grouped output by 'school_name'. You can override using the `.groups` argument.
rcs_pctgs <- pct_piv %>%
filter(!themeasure %in% c("pct_bsc_and_below", "pct_prof_adv")) %>%
group_by(school_name, themeasure) %>%
summarize(pctg = mean(thenumber, na.rm = TRUE))
`summarise()` has grouped output by 'school_name'. You can override using the `.groups` argument.
rcs_pctgs %>% ggplot(aes(x=themeasure, y=pctg, fill = themeasure))+
geom_bar(position = "dodge", stat = "identity")+
facet_wrap(~school_name)

rcs_pctgs$themeasure <- factor(rcs_pctgs$themeasure, levels = c("pct_adv", "pct_prof", "pct_bsc","pct_below_bsc"))
ggplot(rcs_pctgs, aes(x=pctg, y= school_name, fill=themeasure, label = pctg))+
geom_bar(position = "stack", stat = "identity")+
xlab("Percentage")+
ylab("School")+
geom_text(size=3, aes(label=paste0(sprintf("%1.0f", pctg),"%")), position = position_stack(vjust = 0.5))+
theme(axis.text.x=element_text(angle = 45, hjust = 1))+
coord_flip()+
scale_fill_manual(values = c("pct_below_bsc" = "#d7191c",
"pct_bsc" = "#fdae61",
"pct_prof" = "#abd9e9",
"pct_adv" = "#2c7bb6"))

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCnJtKGxpc3QgPSBscygpKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQpgYGB7cn0NCmRpc3RyaWN0cyA9IHJlYWRfY3N2KCIuL2RhdGEvZGlzdHJpY3RzLmNzdiIpDQpkaXN0cmljdHMNCmBgYA0KVGhlcmUgYXJlIDE0NiByb3dzIGFuZCAyNyBjb2x1bW5zIGluIGRpc3RyaWN0cy4NCg0KUTIuIE5vdGljZSB0aGF0IHRoZSBmaXJzdCByb3cgY29ycmVzcG9uZHMgdG8gdGhlIHdob2xlIFN0YXRlIG9mIFRlbm5lc3NlZS4gUmVtb3ZlIHRoaXMgcm93IGFuZCBzYXZlIHRoZSByZXN1bHQgYmFjayB0byBkaXN0cmljdHMuDQoNCmBgYHtyfQ0KZGlzdHJpY3RzID0gZGlzdHJpY3RzWy0xLCBdDQpoZWFkKGRpc3RyaWN0cykNCmBgYA0KDQojIyMgUTMuIEhvdyBtYW55IGRpc3RyaWN0cyBoYXZlIGEgcHJvZmljaWVuY3kgcmF0ZSBvZiBhdCBsZWFzdCA4MCUgZm9yIGJvdGggYWxnXzEgYW5kIGVuZ18xPw0KDQpgYGB7cn0NCmRpc3RyaWN0cyU+JWZpbHRlcihhbGdfMSA+PSA4MCAmIGVuZ18xID49IDgwKQ0KYGBgDQoNCmBgYHtyfQ0KY291bnQoZGlzdHJpY3RzJT4lZmlsdGVyKGFsZ18xID49IDgwICYgZW5nXzEgPj0gODApKQ0KYGBgDQpUaGVyZSBhcmUgMTMgZGlzdHJpY3RzLg0KDQoNCiMjIyBRNC4gSG93IG1hbnkgZGlzdHJpY3RzIGhhdmUgYSBwcm9maWNpZW5jeSByYXRlIGxlc3MgdGhhbiA1MCUgZm9yIGVpdGhlciBhbGdfMSBvciBlbmdfMT8NCmBgYHtyfQ0KZGlzdHJpY3RzJT4lZmlsdGVyKGFsZ18xIDwgNTAgIHwgZW5nXzEgPCA1MCkNCmBgYA0KYGBge3J9DQpjb3VudChkaXN0cmljdHMlPiVmaWx0ZXIoYWxnXzEgPCA1MCAgfCBlbmdfMSA8IDUwKSkNCmBgYA0KDQpUaGVyZSBhcmUgOCBkaXN0cmljdHMuIA0KDQoNCiMjIyBRNS5XaGljaCBkaXN0cmljdCBoYXMgdGhlIGxvd2VzdCBncmFkdWF0aW9uIHJhdGU/DQpgYGB7cn0NCmRpc3RyaWN0cyU+JWZpbHRlcihncmFkID09IG1pbihkaXN0cmljdHMkZ3JhZCwgbmEucm0gPSBUUlVFKSklPiUNCiAgc2VsZWN0KHN5c3RlbV9uYW1lLCBncmFkKQ0KYGBgDQpUZW5uZXNzZWUgU2Nob29sIGZvciBCbGluZCBoYXMgdGhlIGxvd2VzdCBncmFkdWF0aW9uIHJhdGUuIA0KDQpgYGB7cn0NCiNvYQ0KZGlzdHJpY3RzJT4lDQogIGFycmFuZ2UoZ3JhZCkgJT4lIA0KICBoZWFkKDEpDQpgYGANCg0KDQoNCg0KDQoNCiMjIyBRNi4gIFdpdGhpbiB0aGUgTWlkIEN1bWJlcmxhbmQgcmVnaW9uLCB3aGljaCBkaXN0cmljdCBoYXMgdGhlIGhpZ2hlc3QgQUNUIGNvbXBvc2l0ZT8NCmBgYHtyfQ0KZGlzdHJpY3RzJT4lZmlsdGVyKHJlZ2lvbiA9PSAnTWlkIEN1bWJlcmxhbmQnKSU+JQ0KICBmaWx0ZXIoYWN0X2NvbXBvc2l0ZSA9PSBtYXgoYWN0X2NvbXBvc2l0ZSwgbmEucm0gPSBUUlVFKSklPiUNCiAgc2VsZWN0KHN5c3RlbV9uYW1lLCBhY3RfY29tcG9zaXRlKQ0KYGBgDQoNCiMjIyBRNy4gQ3JlYXRlIGEgaGlzdG9ncmFtIHNob3dpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBncmFkdWF0aW9uIHJhdGVzLiBXaGF0IGNhbiB5b3Ugc2F5IGFib3V0IHRoaXMgZGlzdHJpYnV0aW9uPw0KYGBge3J9DQpnZ3Bsb3QoZGlzdHJpY3RzLCBhZXMoZ3JhZCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBmaWxsID0gJ2JsdWUnLCBjb2xvciA9ICdibGFjaycpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLCAxMDAsIDEwKSkNCiAgDQpgYGAgDQpgYGB7cn0NCmRpc3RyaWN0cyAlPiUgDQogIGdncGxvdChhZXMoeD1ncmFkLCBmaWxsID0gcmVnaW9uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIG5hLnJtID0gVFJVRSkNCg0KYGBgDQoNCg0KDQoNCk1vc3QgZ3JhZHVhdGlvbiByYXRlcyBmYWxsIGJldHdlZW4gODAtMTAwJS4gVGhlcmUgaXMgb25lIG91dGxpZXIgd2l0aCBhIGdyYWR1YXRpb24gcmF0ZSAxMSUuDQoNCiMjIyBROC4gQ3JlYXRlIGEgc2NhdHRlciBwbG90IHRvIGNvbXBhcmUgYWxnXzEgcHJvZmljaWVuY3kgcmF0ZXMgdG8gYWxnXzIgcmF0ZXMuIFdoYXQgZG8geW91IG5vdGljZT8gDQojIyMgICAgIEZhY2V0IHRoaXMgcGxvdCBieSByZWdpb24uIERvZXMgYW55dGhpbmcgc3RhbmQgb3V0IHdoZW4geW91IGZhY2V0IHRoZSBwbG90cz8NCg0KDQpgYGB7cn0NCmdncGxvdChkaXN0cmljdHMpICsNCiAgZ2VvbV9wb2ludChhZXMoYWxnXzEsIGFsZ18yKSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLCAxMDAsIDEwKSkNCmBgYA0KVGhlIHNjYXR0ZXIgcGxvdCBzaG93cyB0aGVyZSBpcyBhIHN0cm9uZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIGFsZ18xIGFuZCBhbGdfMiBwcm9maWNpZW5jeSByYXRlcy4gDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGlzdHJpY3RzKSArIA0KICBnZW9tX3BvaW50KGFlcyhhbGdfMSwgYWxnXzIsIGNvbG9yID0gZmFjdG9yKHJlZ2lvbikpKSArDQogIGZhY2V0X3dyYXAoKC5+cmVnaW9uKSwgbnJvdyA9IDMsIG5jb2wgPSAzKSsNCiAgeGxpbSgwLDEwMCkrDQogIHlsaW0oMCwxMDApDQpgYGANClRoZSBzY2F0dGVyIHBsb3RzIHNob3cgU291dGh3ZXN0L01lbXBoaXMgc2hvd3MgdGhlIHN0cm9uZ2VzdCBwb3NpdGl2ZSBjb3JyZWxhdGlvbi5Tb3V0aGVhc3Qgc2hvd3MgdGhlIHdlYWtlc3QgY29ycmVsYXRpb24uDQpVcHBlciBDdW1iZXJsYW5kIHNob3dzIGEgbm9uLWxpbmVhciB0cmVuZC4gDQoNCg0KIyMjIFE5LiBDcmVhdGUgYSBiYXIgY2hhcnQgc2hvd2luZyB0aGUgdG90YWwgZW5yb2xsbWVudCBieSByZWdpb24uIA0KIyMjICAgICBXaGljaCByZWdpb24gaGFzIHRoZSBoaWdoZXN0IHRvdGFsIGVucm9sbG1lbnQ/IFdoaWNoIGhhcyB0aGUgc21hbGxlc3Q/DQoNCg0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KcCA9IGdncGxvdChkaXN0cmljdHMsIGFlcyh4PXJlZ2lvbiwgeT1lbnJvbGxtZW50LCBmaWxsID0gcmVnaW9uKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikNCg0KcCArIGNvb3JkX2ZsaXAoKSANCiAgDQogIA0KYGBgDQoNClJlb3JkZXJpbmcgYnkgZW5yb2xsbWVudA0KDQpgYGB7cn0NCmxpYnJhcnkoc2NhbGVzKQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpDQpwID0gZ2dwbG90KGRpc3RyaWN0cywgYWVzKHggPSByZW9yZGVyKHJlZ2lvbiwgK2Vucm9sbG1lbnQpLCB5PWVucm9sbG1lbnQsIGZpbGwgPSByZWdpb24pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBuYS5ybSA9IFQpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpDQoNCnAgKyBjb29yZF9mbGlwKCkgDQoNCmBgYA0KdXBwZXIgY3VtYmVybGFuZCBhbmQgc291dGh3ZXN0IG1lbXBoaXMgZG8gbm90IGdldCBpbiBvcmRlci4gDQpNaWQgQ3VtYmVybGFuZCBoYXMgdGhlIGhpZ2hlc3QgZW5yb2xsbWVudCwgTm9ydGh3ZXN0IGhhcyB0aGUgbG93ZXN0IGVucm9sbG1lbnQuIA0KDQojIyMgUTEwLiBXaGVuIGNyZWF0aW5nIHRoaXMgYmFyIGNoYXJ0IHlvdSBtYXkgaGF2ZSBub3RpY2VkIHRoYXQgc29tZSBkaXN0cmljdHMgaGF2ZSBtaXNzaW5nIGVucm9sbG1lbnQgdmFsdWVzLiANCiMjIyAgICAgIEZvciBob3cgbWFueSBkaXN0cmljdHMgaXMgdGhpcyB0aGUgY2FzZT8NCg0KYGBge3J9DQoNCnN1bShpcy5uYShkaXN0cmljdHMkZW5yb2xsbWVudCkpDQoNCmBgYA0KDQpgYGB7cn0NCmRpc3RyaWN0cyU+JWZpbHRlcihpcy5uYShlbnJvbGxtZW50KSklPiVncm91cF9ieShyZWdpb24sIHN5c3RlbV9uYW1lKSU+JXRhbGx5KCkNCmBgYA0KIyMjIFExMS4gV2hhdCBpcyB0aGUgbWVhbiBncmFkdWF0aW9uIHJhdGUgYWNyb3NzIGFsbCBkaXN0cmljdHM/IA0KIyMjICAgICAgV2hhdCBtaWdodCBiZSB3cm9uZyB3aXRoIHVzaW5nIGp1c3QgdGhlIHJlZ3VsYXIgbWVhbiB0byBhc3Nlc3MgYXZlcmFnZSBncmFkdWF0aW9uIHJhdGVzPw0KDQpgYGB7cn0NCm1lYW4oZGlzdHJpY3RzJGdyYWQsIG5hLnJtID0gVFJVRSkNCg0KYGBgDQoNCg0KIyMjIFExMi4gUmVkbyB0aGUgcHJldmlvdXMgcXVlc3Rpb24gYnV0IHVzZSBhIHdlaWdodGVkIGF2ZXJhZ2UgKHdlaWdodGVkLm1lYW4pIGdyYWR1YXRpb24gYWNyb3NzIGFsbCBkaXN0cmljdHMsIHdlaWdoaW5nIGJ5IGVucm9sbG1lbnQuIA0KIyMjICAgICAgSG93IG11Y2ggZG9lcyB0aGlzIGNoYW5nZSB5b3VyIGFuc3dlcj8gDQojIyMgICAgICBDYW4geW91IGV4cGxhaW4gdXNpbmcgdGhlIGRhdGEgdGhlIHJlYXNvbiBmb3IgdGhlIGJpZyBjaGFuZ2UgZnJvbSB1c2luZyB0aGUgbWVhbj8NCg0KYGBge3J9DQp3ZWlnaHRlZC5tZWFuKGRpc3RyaWN0cyRncmFkLCBkcGx5cjo6Y29hbGVzY2UoZGlzdHJpY3RzJGVucm9sbG1lbnQsMCksIG5hLnJtPVRSVUUpDQpgYGANCg0KYGBge3J9DQp3ZWlnaHRlZC5tZWFuKGRpc3RyaWN0cyRncmFkLCBjb2FsZXNjZShkaXN0cmljdHMkZW5yb2xsbWVudCwwKSwgbmEucm09VFJVRSkNCmBgYA0KDQoNCmBgYHtyfQ0KZGlzdHJpY3RzJT4lDQogIHN1bW1hcml6ZShncmFkX3dlaWdodGVkX21lYW4gPSB3ZWlnaHRlZC5tZWFuKGdyYWQsIGNvYWxlc2NlKGVucm9sbG1lbnQsMCksIG5hLnJtPVRSVUUpKQ0KYGBgDQoNCg0KYGBge3J9DQpkZiA8LSBkaXN0cmljdHMgJT4lIGZpbHRlcigoIWlzLm5hKGVucm9sbG1lbnQpKSYoIWlzLm5hKGdyYWQpKSkNCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0Kd2VpZ2h0ZWQubWVhbihkZiRncmFkLCBkZiRlbnJvbGxtZW50KQ0KYGBgDQoNClRoZSB1bndlaWdodGVkIGdyYWR1YXRpb24gcmF0ZSBpcyA5MCUuIA0KVGhlICBlbnJvbGxtZW50IHdlaWdodGVkIGdyYWR1YXRpb24gcmF0ZSBpcyA4NyUuDQpFbnJvbGxtZW50IHdlaWdodGVkIC0tIHNjaG9vbHMgd2l0aCBsYXJnZXIgZW5yb2xsbWVudHMgaW5mbHVlbmNlDQp0aGUgYXZlcmFnZSBtb3JlLiANClRoZSB3ZWlnaHRlZCBhdmVyYWdlIGRlY3JlYXNlZC0tc2Nob29scyB3aXRoIGxhcmdlciBlbnJvbGxtZW50cyBoYXZlDQpzbWFsbGVyIGdyYWR1YXRpb24gcmF0ZXMuDQpXZWlnaHRlZC1oaWdoZXIgZW5yb2xsbWVudHMgY29udHJpYnV0ZSBtb3JlIHRvIHRoZSBhdmcuDQoNCiMjIyBRMTMuIENyZWF0ZSBhIGJveHBsb3Qgc2hvd2luZyBncmFkdWF0aW9uIHJhdGVzIHBlciByZWdpb24uIA0KIyMjICAgICAgRG9lcyBhbnl0aGluZyBzdGFuZCBvdXQ/DQoNCmBgYHtyfQ0KZ2dwbG90KGRpc3RyaWN0cykrDQogIGdlb21fYm94cGxvdChhZXMoeD1yZWdpb24sIHk9Z3JhZCwgZmlsbCA9IHJlZ2lvbikpKw0KICBnZ3RpdGxlKCdHcmFkdWF0aW9uIFJhdGUgYnkgUmVnaW9uJykrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMDAsNSkpKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KVGhlIGJveHBsb3Qgc2hvd3Mgb3V0bGllcnMgZm9yIFNvdXRod2VzdC9NZW1waGlzIGF0IH41MCUgYW5kIE1pZEN1bWJlcmxhbmQgYXQgfjEwJQ0KDQoNCiMjIyBRMTQuIEZpbmQgdGhlIHdlaWdodGVkIGF2ZXJhZ2Ugb2YgZ3JhZHVhdGlvbiByYXRlcyBieSByZWdpb24gdXNpbmcgZW5yb2xsbWVudCBhcyB3ZWlnaHRzLiANCiMjIyBDb21wYXJlIHRoZSByZXN1bHRzIHlvdSBnZXQgZm9yIHRoZSB3ZWlnaHRlZCBhdmVyYWdlIHRvIHdoYXQgeW91IHNlZSBmcm9tIHRoZSBib3hwbG90cy4NCg0KDQpgYGB7cn0NCm9wdGlvbnMoZGlnaXRzID0gMykNCmRpc3RyaWN0cyAlPiUgDQogIGdyb3VwX2J5KHJlZ2lvbiklPiUNCiAgc3VtbWFyaXNlKGVucl93bSA9IHdlaWdodGVkLm1lYW4oZ3JhZCwgY29hbGVzY2UoZW5yb2xsbWVudCwwKSwgbmEucm09VCksDQogICAgICAgICAgICBtZWFuID0gbWVhbihncmFkLCBuYS5ybSA9VCkpDQogIA0KICANCiAgDQogIA0KYGBgDQoNClNvdXRod2VzdC9NZW1waGlzIHJlZ2lvbiBoYXMgYSBsb3dlciBncmFkdWF0aW9uIHJhdGUgb2YgNzkuNSwgd2hpY2ggaXMgY29uc2lzdGVudCB3aXRoIGl0cyBib3hwbG90DQpzaG93aW5nIGEgc21hbGwgb3V0bGllciBhbmQgc2tld2VkIGxlZnQgZGlzdHJpYnV0aW9uLg0KTWlkY3VtYmVybGFuZCdzIGdyYWR1YXRpb24gcmF0ZSBpcyBub3QgdGhhdCBtdWNoIGxvd2VyIHRoYW4gb3RoZXIgcmVnaW9ucyBldmVuIHRob3VnaCBpdHMgYm94cGxvdA0Kc2hvd3MgYSBzbWFsbCBvdXRsaWVyIGFyb3VuZCB+MTIlLg0KDQoNCiMjIyBRMTUuIEZvciBtYW55IGRpc3RyaWN0cywgdmFsdWVzIGZvciBhbGdfMiBhcmUgbG93ZXIgdGhhbiBmb3IgYWxnXzEuIA0KIA0KYGBge3J9DQpkaXN0cmljdHMgPC0gZGlzdHJpY3RzICU+JSBtdXRhdGUoYWxnX2RpZmYgPSBhbGdfMSAtIGFsZ18yKQ0KDQpgYGANCg0KDQojIyMgQ3JlYXRlIGEgaGlzdG9ncmFtIHNob3dpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkaWZmZXJlbmNlcyAoYWxnXzEgLSBhbGdfMikuDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9YWxnX2RpZmYsIGZpbGw9ImEiKSwgYWxwaGE9LjcsIGRhdGE9c3Vic2V0KGRpc3RyaWN0cywgYWxnX2RpZmYgPD0wKSwgIGNvbG9yID0gImJsdWUiLCBiaW53aWR0aCA9IDUpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9YWxnX2RpZmYsIGZpbGw9InoiKSwgYWxwaGE9LjUsIGRhdGE9c3Vic2V0KGRpc3RyaWN0cywgYWxnX2RpZmYgPjApLCAgY29sb3I9ImJsYWNrIiwgYmlud2lkdGggPSA1KSArDQogIA0KICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLCB2YWx1ZXM9YygiYSIgPSAic2t5Ymx1ZSIsICJ6IiA9InJlZCIpLCBsYWJlbHM9YygiYSI9IkFsZzEgPCBBbGcyIiwgInoiID0iQWxnMSA+IEFsZzIiKSkrDQogIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgtMjAsIDYwLCAxMCkpDQogIA0KDQpgYGANClRoZSBoaXN0b2dyYW0gc2hvd3MgYWJvdXQgMjAgZGlzdHJpY3RzIGhhZCBhIGRyb3AgaW4gYWxnZWJyYSBieSBhYm91dCAxNSBwb2ludHMuIA0KDQoNCiMjIyBXaGljaCBzY2hvb2wgaGFkIHRoZSBsYXJnZXN0IGRyb3AgZnJvbSBhbGdfMSB0byBhbGdfMj8gDQoNCmBgYHtyfQ0KZGlzdHJpY3RzJT4lZmlsdGVyKGFsZ19kaWZmID09IG1heChhbGdfZGlmZiwgbmEucm09VFJVRSkpJT4lc2VsZWN0KHN5c3RlbV9uYW1lLCBhbGdfMSwgYWxnXzIsIGFsZ19kaWZmKQ0KYGBgDQpQaWNrZXR0IENvdW50eSBoYXMgdGhlIGxhcmdlc3QgZHJvcCBmcm9tIGFsZ18xIHRvIGFsZ18yLg0KDQoNCiMjIyBGb3Igd2hhdCBwZXJjZW50YWdlIG9mIHNjaG9vbHMgaXMgaXQgdHJ1ZSB0aGF0IGFsZ18yIGlzIGxhcmdlciB0aGFuIGFsZ18xPyANCmBgYHtyfQ0KZGlzdHJpY3RzJT4lc3VtbWFyaXNlKHBjdF9zY2ggPSBzdW0oYWxnX2RpZmY8MCwgbmEucm0gPSBUUlVFKS9ucm93KGRpc3RyaWN0cykpDQpgYGANCg0KYGBge3J9DQpkaXN0cmljdHMlPiVzdW1tYXJpc2UocGN0X3NjaCA9IHN1bShhbGdfZGlmZjwwLCBuYS5ybSA9IFRSVUUpLyBzdW0oIWlzLm5hKGFsZ19kaWZmKSkpDQpgYGANCg0KDQoNCmBgYHtyfQ0KZGlzdHJpY3RzJT4lc3VtbWFyaXNlKHBjdF9zY2ggPSAxMDAqc3VtKG5hLm9taXQoYWxnX2RpZmY8MCkpLzE0NSkNCmBgYA0KMTUuNCUgb2Ygc2Nob29scyBoYXZlIGFsZ18yIGdyZWF0ZXIgdGhhbiBhbGdfMS4gDQpgYGB7cn0NCmBgYA0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KIyMjIElzIHRoZXJlIGEgc2ltaWxhciBkcm9wIG9mZiBmb3IgZW5nXzIgYW5kIGVuZ18zPw0KDQpgYGB7cn0NCmRpc3RyaWN0cyA8LSBkaXN0cmljdHMgJT4lIG11dGF0ZShlbmcyM19kaWZmID0gZW5nXzIgLSBlbmdfMykNCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9ZW5nMjNfZGlmZiwgZmlsbD0iYSIpLCBhbHBoYT0uOSwgZGF0YT1zdWJzZXQoZGlzdHJpY3RzLCBlbmcyM19kaWZmIDwwKSwgIGNvbG9yID0gImJsdWUiLCBiaW53aWR0aCA9IDUpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9ZW5nMjNfZGlmZiwgZmlsbD0ieiIpLCBhbHBoYT0uNSwgZGF0YT1zdWJzZXQoZGlzdHJpY3RzLCBlbmcyM19kaWZmID4wKSwgIGNvbG9yPSJibGFjayIsIGJpbndpZHRoID0gNSkgKw0KICANCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iIiwgdmFsdWVzPWMoImEiID0gInNreWJsdWUiLCAieiIgPSJyZWQiKSwgbGFiZWxzPWMoImEiPSJFbmcyIDwgRW5nMyIsICJ6IiA9IkVuZzIgPiBFbmczIikpDQogIA0KICAjc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoLTIwLCA2MCwgMTApKQ0KICANCg0KYGBgDQpUaGUgaGlzdG9ncmFtIHNob3dzIGFsbW9zdCBhbGwgc2Nob29scyBoYWQgZHJvcHMgaW4gc2NvcmVzIGZyb20gRW5nMiB0byBFbmczLiANCg0KDQpgYGB7cn0NCmRpc3RyaWN0cyU+JXN1bW1hcmlzZShwY3Rfc2NoID0gMTAwKnN1bShuYS5vbWl0KGVuZzIzX2RpZmY+MCkpLzE0NSkNCmBgYA0KODYlIG9mIHNjaG9vbHMgc2hvd2VkIGRlY3JlYXNlIGluIHNjb3JlIGZyb20gZW5nMiB0byBlbmczLiANCg0KDQojIyMgMTYuIFlvdSBtYXkgaGF2ZSBub3RpY2VkIHRoYXQgYSBsb3Qgb2Ygcm93cyBhcmUgbWlzc2luZyB2YWx1ZXMuIA0KIyMjICAgICBXaGljaCBkaXN0cmljdCBoYXMgdGhlIGxhcmdlc3QgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzPyANCiMjIyAgICAgV2hhdCBkbyB5b3Ugbm90aWNlIGFib3V0IHNjaG9vbHMgdGhhdCBoYXZlIGEgbG90IG9mIG1pc3NpbmcgdmFsdWVzPw0KDQoNCg0KYGBge3J9DQpkaXN0cmljdHMgJT4lIG11dGF0ZShuYV9jdCA9IHJvd1N1bXMoaXMubmEoLikpKSU+JWZpbHRlcihuYV9jdCA9PSBtYXgobmFfY3QpKQ0KYGBgDQpXZXN0IFRlbm4gU2Nob29sIGZvciBEZWFmIGhhcyB0aGUgbW9zdCBtaXNzaW5nIHZhbHVlcw0KDQoNCmBgYHtyfQ0KZGlzdHJpY3RzICU+JSBtdXRhdGUobmFfY3QgPSByb3dTdW1zKGlzLm5hKC4pKSklPiVhcnJhbmdlKGRlc2MobmFfY3QpKQ0KYGBgDQoNClRoZSBtaXNzaW5nIHZhbHVlcyBzZWVtIHRvIGNvbWUgZnJvbSB0aGUgc3ViamVjdCBwcm9maWNpZW5jeSByYXRlIGFuZCBhY3RfY29tcG9zaXRlIGNvbHVtbnMuDQpNYXliZSB0aGUgc2Nob29scyBkaWQgbm90IHBhcnRpY2lwYXRlIGluIHRoZSBzdGF0ZSB0ZXN0cy4NClRoZXkgYWxzbyBoYXZlIG1pc3NpbmcgdmFsdWVzIGluIGdyYWQgYW5kIGRyb3BvdXQgY29sdW1ucw0KDQojIyNRMTcuIEZpbmQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gZ3JhZHVhdGlvbiByYXRlIGFuZCBhbGwgb3RoZXIgdmFyaWFibGVzLg0KIyMjICAgICBDcmVhdGUgYSBob3Jpem9udGFsIGJhciBjaGFydCBzaG93aW5nIHRoZXNlIGNvcnJlbGF0aW9ucy4gDQojIyMgICAgIE1ha2Ugc3VyZSB0aGF0IHlvdXIgcGxvdCBpcyBvcmRlcmVkIGJ5IGNvcnJlbGF0aW9uIHZhbHVlcy4gDQojIyMgICAgIFdoYXQgZG8geW91IG5vdGljZSBmcm9tIHRoZXNlIGNvcnJlbGF0aW9ucz8NCg0KDQoNCmBgYHtyfQ0KZGlzdHJpY3RzICU+JSANCiAgc2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpKSAlPiUgDQogIGNvciguICwgZGlzdHJpY3RzJGdyYWQsIHVzZSA9ICdjb21wbGV0ZS5vYnMnKQ0KYGBgDQoNCg0KYGBge3J9DQpjb3JfZHQgPC0gZGlzdHJpY3RzICU+JSANCiAgc2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpKSAlPiUgDQogIGNvciguICwgZGlzdHJpY3RzJGdyYWQsIHVzZSA9ICdjb21wbGV0ZS5vYnMnKSAlPiUgDQogIGFzX3RpYmJsZShjb3JfZHQsIHJvd25hbWVzID0gInZhcmlhYmxlIikgJT4lIA0KICByZW5hbWUoY29ycmVsYXRpb24gPSBWMSklPiUNCiAgZmlsdGVyKHZhcmlhYmxlICE9ICJncmFkIikNCg0KDQpjb3JfZHQNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gY29yX2R0LCBhZXMoeT1yZW9yZGVyKHZhcmlhYmxlLCAtY29ycmVsYXRpb24pLCB4PWNvcnJlbGF0aW9uKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykrDQogIHRoZW1lX2J3KCkNCg0KDQoNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBjb3JfZHQsIGFlcyh4PWNvcnJlbGF0aW9uLCB5PXJlb3JkZXIodmFyaWFibGUsIC1jb3JyZWxhdGlvbiksIGZpbGw9Y29ycmVsYXRpb24pKSArDQogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9J2ZpcmVicmljaycsIGhpZ2g9J3NreWJsdWUnLCBzcGFjZT0nTGFiJykNCg0KDQoNCg0KYGBgDQoNCkRyb3BvdXQgYW5kIHN1c3BlbnNpb24gcmF0ZXMgYXJlIHRoZSBtb3N0IG5lZ2F0aXZlbHkgY29ycmVsYXRlZCB3aXRoIGdyYWR1YXRpb24gcmF0ZS4NClNjb3JlcyBpbiBFbmdsaXNoLCBBbGdlYnJhIGFuZCBTY2llbmNlcyBhcmUgdGhlIG1vc3QgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggZ3JhZHVhdGlvbiByYXRlcy4NCg0KDQojIyNRMTguIENyZWF0ZSBhIHNjYXR0ZXJwbG90IGZvciBncmFkIHZzLiBzdXNwZW5kZWQuIA0KIyMjICAgICBEb2VzIHdoYXQgeW91IHNlZSBtYWtlIHNlbnNlIGdpdmVuIHlvdXIgYW5zd2VyIGZyb20gdGhlIHByZXZpb3VzIHBhcnQ/DQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGlzdHJpY3RzKSArDQogIGdlb21fcG9pbnQoYWVzKHN1c3BlbmRlZCwgZ3JhZCksICBuYS5ybSA9IFRSVUUpKw0KICB4bGltKDAsMTAwKSsNCiAgeWxpbSgwLDEwMCkNCiAgDQpgYGANClRoZSBzY2F0dGVyIHBsb3Qgc2hvd3MgdGhhdCBsb3dlciBzdXNwZW5zaW9uIHJhdGVzIGFyZSBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIGdyYWR1YXRpb24gcmF0ZXMuIA0KQnV0IGZvciBsb3dlciBzdXNwZW5zaW9uIHJhdGVzICg8NSUpLCB0aGVyZSBkb2VzIG5vdCBzZWVtIHRvIGJlIGEgc3Ryb25nIHZhcmlhYmlsaXR5IGluIGdyYWR1YXRpb24gcmF0ZS4gDQpUaGUgbmVnYXRpdmUgY29ycmVsYXRpb24gbWF5IGJlIGFzIHN0cm9uZyBhcyBpdCBpcyBiZWNhdXNlIG9mIGEgZmV3IG91dGxpZXJzLiANCg0KIyMjUTE5LiBDcmVhdGUgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBsbSB3aXRoIHRhcmdldCB2YXJpYWJsZSBncmFkIGFuZCBwcmVkaWN0b3IgdmFyaWFibGUgc3VzcGVuZGVkLiANCiMjIyAgICAgV2hhdCBSXjIgdmFsdWUgZG9lcyB0aGlzIG1vZGVsIGhhdmU/IFdoYXQgaXMgdGhlIGludGVycHJldGF0aW9uIG9mIHRoaXMgbnVtYmVyPw0KDQpgYGB7cn0NCg0Kc2ltcGxlX3JlZ3Jlc3Npb25fbW9kZWwgPC1sbShncmFkIH4gc3VzcGVuZGVkLCBkYXRhID0gZGlzdHJpY3RzKQ0KDQpzdW1tYXJ5KHNpbXBsZV9yZWdyZXNzaW9uX21vZGVsKQ0KDQpgYGANCg0KVGhlIFJeMiBvZiB0aGUgbW9kZWwgaXMgMC4wNzk1IG9yIDcuOTUlLiANCjcuOTUlIG9mIHRoZSB2YXJpYWJpbGl0eSBpbiB0aGUgZ3JhZHVhdGlvbiByYXRlIGNhbiBiZSBleHBsYWluZWQgYnkgc3VzcGVuc2lvbiByYXRlLiANCg0KDQpgYGB7cn0NCmdyYWRfbG0gPC0gZGlzdHJpY3RzICU+JSANCiAgZmlsdGVyKGdyYWQgPiAyNSkgJT4lIA0KICBsbShncmFkIH4gc3VzcGVuZGVkLCBkYXRhID0gLikNCnN1bW1hcnkoZ3JhZF9sbSkNCg0KYGBgDQoNCg0KDQoNCiMjIyBRMjAuIEFkZCB0aGUgcmVncmVzc2lvbiBsaW5lIHRvIHlvdXIgc2NhdHRlcnBsb3QgdXNpbmcgZ2VvbV9zbW9vdGggd2l0aCBtZXRob2Q9J2xtJy4gDQojIyMgSG93IGRvIHlvdSBmZWVsIGFib3V0IHRoZSByZWdyZXNzaW9uIGxpbmUgYWZ0ZXIgc2VlaW5nIGl0IHBsb3R0ZWQgb24gdGhlIHNjYXR0ZXJwbG90Pw0KDQpgYGB7cn0NCmdncGxvdChkaXN0cmljdHMsIGFlcyh4PXN1c3BlbmRlZCwgeT1ncmFkKSkgKw0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpDQogIA0KICANCmBgYA0KVGhlIHJlZ3Jlc3Npb24gbGluZSBkb2VzIG5vdCBjYXB0dXJlIG1vc3QgcG9pbnRzIHdlbGwsIHRoZXJlIGlzIGEgbG90IG9mIHNjYXR0ZXIgZm9yDQpzdXNwZW5kZWQgcmF0ZSBiZWxvdyA1JS4gDQpUaGlzIGlzIGNvbnNpc3RlbnQgd2l0aCB0aGUgbG93IFJeMiB2YWx1ZSBmb3IgdGhlIG1vZGVsLiANCg0KDQojIyBDb250aW51ZWQgRXhwbG9yYXRpb24gYW5kIFByYWN0aWNlDQoNCiMjIyMgUTIxLiBSZWFkIGluIHRoZSBzY2hvb2wtbGV2ZWwgdGVzdGluZyBkYXRhIGZvciAyMDE0LCBhdmFpbGFibGUgaGVyZS4gDQpZb3UgbWlnaHQgZmluZCB0aGUgcmVhZHhsIGxpYnJhcnkgdXNlZnVsIGZvciB0aGlzIHRhc2suIA0KSWYgeW91IHVzZSB0aGlzIGxpYnJhcnksIGJlIHN1cmUgdG8gbG9vayBhdCB0aGUgbmEgYXJndW1lbnQgZm9yIHRoZSByZWFkX2V4Y2VsIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KYGBge3J9DQp0ZXN0aW5nIDwtIHJlYWRfZXhjZWwoIi4vZGF0YS9kYXRhXzIwMTRfc2Nob29sX2Jhc2UueGxzeCIsIG5hID0gYygiKiIsICIqKiIpKQ0KYGBgDQoNCiMjIyMgUTIyLiBIb3cgbWFueSBzY2hvb2xzIGhhdmUgYXQgbGVhc3QgMjAgcGVyY2VudCBvZiBzdHVkZW50cyBiZWxvdyBic2MgZm9yIEFsZ2VicmEgST8gDQogICAgICAgICAgDQpgYGB7cn0NCmNvbG5hbWVzKHRlc3RpbmcpDQpgYGANCg0KYGBge3J9DQpjb3VudCh0ZXN0aW5nICU+JSBmaWx0ZXIoKHN1YmplY3QgPT0gIkFsZ2VicmEgSSIpJihwY3RfYmVsb3dfYnNjID49IDIwKSkpDQogIA0KYGBgDQogICAgICAgICAgDQpXaGljaCBkaXN0cmljdHMgZG8gdGhlc2Ugc2Nob29scyBiZWxvbmcgdG8/DQpgYGB7cn0NCnRlc3RpbmcgJT4lIA0KICBmaWx0ZXIoKHN1YmplY3QgPT0gIkFsZ2VicmEgSSIpJihwY3RfYmVsb3dfYnNjID49IDIwKSkgJT4lIA0KICBkaXN0aW5jdChzeXN0ZW1fbmFtZSkNCmBgYA0KVGhlcmUgYXJlIDEwMiBkaXN0cmljdHMgdGhhdCBoYXZlIGF0IGxlYXN0IDIwJSBiZWxvdyBic2MgZm9yIEFsZ2VicmEgSS4gDQoNCmBgYHtyfQ0KY291bnQodGVzdGluZyAlPiUgZGlzdGluY3Qoc3lzdGVtX25hbWUpKQ0KYGBgDQpUaGVyZSBhcmUgMTQxIHRvdGFsIGRpc3RyaWN0cyBpbiB0aGUgZGF0YXNldC4gDQoNCiMjIyNRMjMuIEhvdyBtYW55IHNjaG9vbHMgaGF2ZSBhdCBsZWFzdCAyMCBwZXJjZW50IG9mIHN0dWRlbnRzIGJlbG93IGJzYyBmb3IgYm90aCBBbGdlYnJhIEkgYW5kIEVuZ2xpc2ggST8NCg0KYGBge3J9DQpmaWxfdHN0IDwtIHRlc3RpbmcgJT4lDQogIGZpbHRlcihwY3RfYmVsb3dfYnNjID49IDIwKSAlPiUgDQogIGZpbHRlcihzdWJqZWN0ID09ICJBbGdlYnJhIEkiIHwgc3ViamVjdCA9PSAiRW5nbGlzaCBJIikgJT4lDQogIHNlbGVjdChzY2hvb2xfbmFtZSwgc3ViamVjdCwgcGN0X2JlbG93X2JzYykgDQoNCmBgYA0KYGBge3J9DQpmaWxfdHN0DQpgYGANCmBgYHtyfQ0KZmlsX3RzdCAlPiUgDQogIGdyb3VwX2J5KHNjaG9vbF9uYW1lKSAlPiUgDQogIHN1bW1hcmlzZShjb3VudCA9IG5fZGlzdGluY3Qoc3ViamVjdCkpICU+JSANCiAgZmlsdGVyKGNvdW50ID4gMSkNCmBgYA0KDQpUaGVyZSBhcmUgNTEgc2Nob29scyB0aGF0IGhhdmUgYXQgbGVhc3QgMjAlIGJlbG93IGJzYyBpbiBib3RoIEVuZ2xpc2ggSSBhbmQgQWxnZWJyYSBJLiANCg0KDQoNCg0KIyMjUTI0LiBXaGljaCBncmFkZSBoYXMgdGhlIGhpZ2hlc3QgcGN0X2FkdiBmb3IgQWxnZWJyYSBJPyANCiANCg0KIA0KIA0KYGBge3J9DQp0ZXN0aW5nICU+JSAgDQogIGZpbHRlcihzdWJqZWN0ID09ICJBbGdlYnJhIEkiKSAlPiUgDQogIGZpbHRlcihwY3RfYWR2ID09IG1heChwY3RfYWR2LCBuYS5ybSA9IFQpKSAlPiUgDQogIGdyb3VwX2J5KGdyYWRlKQ0KYGBgDQo4dGggZ3JhZGUgaGFzIHRoZSBoaWdoZXN0IEFsZ2VicmEgSSBncmFkZS4NCg0KIyMjIyAgICBQbG90IHRoZSBhdmVyYWdlIHBjdF9hZHYgcGVyIGdyYWRlIGxldmVsIGFzIGEgYmFyIGNoYXJ0Lg0KDQogDQpgYGB7cn0NCnRlc3RpbmcgJT4lIA0KICBncm91cF9ieShncmFkZSkgJT4lDQogIHN1bW1hcmlzZShhdmdfcGN0X2FkdiA9IG1lYW4ocGN0X2FkdiwgbmEucm0gPSBUKSkgJT4lDQogIGFycmFuZ2UoYXMubnVtZXJpYyhncmFkZSkpDQogIA0KYGBgDQpgYGB7cn0NCnRlc3RpbmcgJT4lIA0KICBncm91cF9ieShncmFkZSkgJT4lDQogIHN1bW1hcmlzZShhdmdfcGN0X2FkdiA9IG1lYW4ocGN0X2FkdiwgbmEucm0gPSBUKSkgJT4lIA0KICANCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKGFzLm51bWVyaWMoZ3JhZGUpKSwgeT1hdmdfcGN0X2FkdikpKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICdza3libHVlMycsIGNvbG9yID0gJ2JsYWNrJykrDQogIHhsYWIoIkdyYWRlIikrDQogIHlsYWIoIkFkdmFuY2VkIFBlcmNlbnRhZ2UiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQogDQogIA0KYGBgDQpgYGB7cn0NCiNQY3RfYWR2IG9ubHkgZm9yIEFsZ2VicmEgSQ0KdGVzdGluZyAlPiUNCiAgZmlsdGVyKHN1YmplY3Q9PSJBbGdlYnJhIEkiKSAlPiUgDQogIGdyb3VwX2J5KGdyYWRlKSAlPiUNCiAgc3VtbWFyaXNlKGF2Z19wY3RfYWR2ID0gbWVhbihwY3RfYWR2LCBuYS5ybSA9IFQpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IoYXMubnVtZXJpYyhncmFkZSkpLCB5PWF2Z19wY3RfYWR2KSkrDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gJ3N0ZWVsYmx1ZScsIGNvbG9yID0gJ2JsYWNrJykrDQogIHhsYWIoIkdyYWRlIikrDQogIHlsYWIoIkFkdmFuY2VkIFBlcmNlbnRhZ2UgZm9yIEFsZ2VicmEgSSIpDQogDQogIA0KYGBgDQojIyNRMjUuIEZpbmQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gcGN0X2FkdiBmb3IgQWxnZWJyYSBJIGFuZCBwY3RfYWR2IGZvciBBbGdlYnJhIElJIGJ5IHNjaG9vbC4gDQojIyMjICAgICBDcmVhdGUgYSBzY2F0dGVycGxvdCBzaG93aW5nIEFsZ2VicmEgSUkgc2NvcmVzIHZzLiBBbGdlYnJhIEkgc2NvcmVzIGJ5IHNjaG9vbC4gDQpgYGB7cn0NCg0KI0ZpbHRlciBkYXRhc2V0IGZvciBBbGdJL0FsZ0lJDQphbGdlYnJhIDwtIHRlc3RpbmcgJT4lIA0KICBmaWx0ZXIoc3ViamVjdCA9PSAiQWxnZWJyYSBJIiB8IHN1YmplY3QgPT0gIkFsZ2VicmEgSUkiKSAlPiUgDQogIHNlbGVjdChzY2hvb2xfbmFtZSwgc3ViamVjdCwgcGN0X2FkdikNCg0KYGBgDQoNCmBgYHtyfQ0KI1Bpdm90IGFsZ2VicmENCmFsZ19wdnQgPC0gYWxnZWJyYSAlPiUgIA0KICAgDQogIA0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc3ViamVjdCwgdmFsdWVzX2Zyb20gPSBwY3RfYWR2LCB2YWx1ZXNfZm4gPSBmdW5jdGlvbih4KSBtZWFuKHgsIG5hLnJtID0gVFJVRSkpDQpgYGANCg0KYGBge3J9DQphbGdfcHZ0IDwtIGFsZ19wdnQgJT4lIHJlbmFtZSgnQWxnZWJyYV9JJyA9ICdBbGdlYnJhIEknLA0KICAgICAgICAgICAgICAgICAgICdBbGdlYnJhX0lJJyA9ICdBbGdlYnJhIElJJykNCmBgYA0KDQpgYGB7cn0NCmFsZ19wdnQNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCmdncGxvdChhbGdfcHZ0KSsNCiAgZ2VvbV9wb2ludChhZXMoQWxnZWJyYV9JLCBBbGdlYnJhX0lJKSkrDQogIHhsaW0oMCwxMDApKw0KICB5bGltKDAsMTAwKQ0KYGBgDQpgYGB7cn0NCmFsZ19wdnQgPC0gYWxnX3B2dCAlPiV0cmFuc2Zvcm0oQWxnZWJyYV9JID0gYXMubnVtZXJpYyhBbGdlYnJhX0kpLCBBbGdlYnJhX0lJID0gYXMubnVtZXJpYyhBbGdlYnJhX0lJKSkNCmBgYA0KDQpgYGB7cn0NCmNvcihhbGdfcHZ0JEFsZ2VicmFfSSwgYWxnX3B2dCRBbGdlYnJhX0lJLCB1c2UgPSAnY29tcGxldGUub2JzJykNCmBgYA0KDQojIyMgUTE5LiBGaW5kIGFsbCBzY2hvb2xzIGluIFJ1dGhlcmZvcmQgQ291bnR5IHRoYXQgaGF2ZSAiSGlnaCBTY2hvb2wiIGluIHRoZWlyIG5hbWUuIA0KIyMjIyAgICAgRm9yIHRoZXNlIHNjaG9vbHMsIGNyZWF0ZSBhIGNoYXJ0ICh5b3VyIGNob2ljZSkgc2hvd2luZyB0aGUgZGlmZmVyZW5jZXMgaW4gDQojIyMjICAgICBwY3RfYmVsb3dfYnNjLCBwY3RfYnNjLCBwY3RfcHJvZiwgYW5kIHBjdF9hZHYgZm9yIEFsZ2VicmEgSSB3aGVuIGxvb2tpbmcgYWNyb3NzIGFsbCBzdWJncm91cHMgYW5kIGdyYWRlcy4NCg0KYGBge3J9DQp0ZXN0aW5nICU+JSANCiAgZmlsdGVyKHN5c3RlbV9uYW1lID09ICdSdXRoZXJmb3JkIENvdW50eScpICU+JSANCiAgZmlsdGVyKHN0cl9kZXRlY3Qoc2Nob29sX25hbWUsICJIaWdoIFNjaG9vbCIpKSU+JQ0KICBmaWx0ZXIoc3ViamVjdCA9PSAnQWxnZWJyYSBJJykgJT4lIA0KICANCiAgc2VsZWN0KHNjaG9vbF9uYW1lKSU+JQ0KICBkaXN0aW5jdChzY2hvb2xfbmFtZSkNCmBgYA0KDQoNCmBgYHtyfQ0KcmNzIDwtIHRlc3RpbmcgJT4lIA0KICBmaWx0ZXIoc3lzdGVtX25hbWUgPT0gJ1J1dGhlcmZvcmQgQ291bnR5JykgJT4lIA0KICBmaWx0ZXIoc3RyX2RldGVjdChzY2hvb2xfbmFtZSwgIkhpZ2ggU2Nob29sIikpJT4lDQogIGZpbHRlcihzdWJqZWN0ID09ICdBbGdlYnJhIEknKSAlPiUgDQogIHNlbGVjdChzY2hvb2xfbmFtZSwgZ3JhZGUsIHN1Ymdyb3VwLCBwY3RfYmVsb3dfYnNjLCBwY3RfYnNjLCBwY3RfcHJvZiwgcGN0X2FkdiwgcGN0X2JzY19hbmRfYmVsb3csIHBjdF9wcm9mX2FkdikNCg0KYGBgDQoNCg0KDQoNCg0KDQoNCmBgYHtyfQ0KcGN0X3BpdiA8LSByY3MgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoInBjdCIpLA0KICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidGhlbWVhc3VyZSIsDQogICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidGhlbnVtYmVyIikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCnBjdF9waXYgJT4lIA0KICBmaWx0ZXIoIXRoZW1lYXN1cmUgJWluJSBjKCJwY3RfYnNjX2FuZF9iZWxvdyIsICJwY3RfcHJvZl9hZHYiKSkgJT4lDQogIGdyb3VwX2J5KHNjaG9vbF9uYW1lLCB0aGVtZWFzdXJlKSAlPiUgDQogIA0KICBzdW1tYXJpemUocGN0ZyA9IG1lYW4odGhlbnVtYmVyLCBuYS5ybSA9IFRSVUUpKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCnJjc19wY3RncyA8LSBwY3RfcGl2ICU+JSANCiAgZmlsdGVyKCF0aGVtZWFzdXJlICVpbiUgYygicGN0X2JzY19hbmRfYmVsb3ciLCAicGN0X3Byb2ZfYWR2IikpICU+JQ0KICBncm91cF9ieShzY2hvb2xfbmFtZSwgdGhlbWVhc3VyZSkgJT4lIA0KICANCiAgc3VtbWFyaXplKHBjdGcgPSBtZWFuKHRoZW51bWJlciwgbmEucm0gPSBUUlVFKSkNCmBgYA0KDQoNCg0KYGBge3J9DQpyY3NfcGN0Z3MgJT4lIGdncGxvdChhZXMoeD10aGVtZWFzdXJlLCB5PXBjdGcsIGZpbGwgPSB0aGVtZWFzdXJlKSkrDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpKw0KICANCiAgZmFjZXRfd3JhcCh+c2Nob29sX25hbWUpDQpgYGANCg0KDQoNCmBgYHtyfQ0KcmNzX3BjdGdzJHRoZW1lYXN1cmUgPC0gZmFjdG9yKHJjc19wY3RncyR0aGVtZWFzdXJlLCBsZXZlbHMgPSBjKCJwY3RfYWR2IiwgInBjdF9wcm9mIiwgInBjdF9ic2MiLCJwY3RfYmVsb3dfYnNjIikpDQoNCmdncGxvdChyY3NfcGN0Z3MsIGFlcyh4PXBjdGcsIHk9IHNjaG9vbF9uYW1lLCBmaWxsPXRoZW1lYXN1cmUsIGxhYmVsID0gcGN0ZykpKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQgPSAiaWRlbnRpdHkiKSsNCiAgeGxhYigiUGVyY2VudGFnZSIpKw0KICB5bGFiKCJTY2hvb2wiKSsNCiAgZ2VvbV90ZXh0KHNpemU9MywgYWVzKGxhYmVsPXBhc3RlMChzcHJpbnRmKCIlMS4wZiIsIHBjdGcpLCIlIikpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkrDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSsNCiAgY29vcmRfZmxpcCgpKw0KICANCg0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJwY3RfYmVsb3dfYnNjIiA9ICIjZDcxOTFjIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBjdF9ic2MiID0gIiNmZGFlNjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGN0X3Byb2YiID0gIiNhYmQ5ZTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGN0X2FkdiIgPSAiIzJjN2JiNiIpKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==