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==