library(tidyverse, warn.conflicts = F)
library(tidymodels, warn.conflicts = F)
library(ggthemes)
theme_set(theme_clean())
source("../scripts/prune_race_variables.R")

Data Import and Joining

hh <- read_csv("../data/hh.csv", show_col_types = FALSE) %>%
    mutate(leaid = as.integer(leaid)) %>% 
    filter(
        if_any(ends_with("MOE"), 
               function(x) {x < 50}), 
        children >= 100
    ) %>%
    select(-ends_with("MOE"))

grad <- read_csv("../data/grad.csv", show_col_types = FALSE) %>%
    mutate(leaid = as.integer(leaid))

race <- read_csv("../data/race.csv", show_col_types = FALSE) %>% 
    prune_and_predom() %>%
    mutate(leaid = as.integer(leaid), 
           predom_race = as.character(predom_race))

assess <- read_csv("../data/assess.csv", show_col_types = FALSE) %>%
    mutate(leaid = as.integer(leaid))

finance <- read_csv("../data/finance.csv", show_col_types = FALSE) %>%
    mutate(leaid = as.integer(leaid))

data <- hh %>%
    left_join(grad,    by = c("leaid" = "leaid")) %>%
    left_join(race,    by = c("leaid" = "leaid")) %>%
    left_join(assess,  by = c("leaid" = "leaid")) %>%
    left_join(finance, by = c("leaid" = "leaid")) %>%
    select(
        -state, -leaid
    ) %>%
    relocate(region, predom_race, .after = dist)

rm(hh, grad, race, assess, finance)

nrow(data)
## [1] 12532
data <- data %>%
    na.omit()

nrow(data)
## [1] 10110
data %>% skimr::skim()
Data summary
Name Piped data
Number of rows 10110
Number of columns 24
_______________________
Column type frequency:
character 3
numeric 21
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
dist 0 1 4 68 0 9652 0
region 0 1 4 13 0 4 0
predom_race 0 1 5 15 0 5 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
children 0 1 5099.68 15995.22 101.00 724.00 1703.50 4136.75 724446.00 ▇▁▁▁▁
pct_pov 0 1 16.28 9.01 0.00 9.57 14.89 21.38 65.39 ▇▇▂▁▁
pct_SP 0 1 30.80 12.88 0.00 21.82 29.51 38.17 96.45 ▂▇▃▁▁
pct_HHVJ 0 1 26.26 5.11 0.00 22.98 26.25 29.39 56.76 ▁▂▇▁▁
pct_CC 0 1 2.47 2.96 0.00 0.90 1.72 2.98 55.85 ▇▁▁▁▁
pct_NCI 0 1 14.15 10.81 0.00 6.16 11.69 19.34 87.83 ▇▃▁▁▁
pct_CD 0 1 5.96 3.53 0.00 3.55 5.36 7.73 32.07 ▇▅▁▁▁
pct_CLI 0 1 2.04 4.17 0.00 0.00 0.00 2.22 66.10 ▇▁▁▁▁
grad_rate_midpt 0 1 86.15 7.75 3.23 82.35 87.91 91.71 97.93 ▁▁▁▂▇
pct_hisp_latino 0 1 13.94 18.92 0.00 2.50 6.60 16.20 100.00 ▇▁▁▁▁
pct_white 0 1 70.82 25.73 0.00 56.50 80.25 90.90 100.00 ▁▁▂▃▇
pct_black 0 1 7.05 14.92 0.00 0.00 1.20 5.60 99.10 ▇▁▁▁▁
pct_native 0 1 2.09 9.24 0.00 0.00 0.00 0.40 100.00 ▇▁▁▁▁
pct_asian 0 1 1.87 4.54 0.00 0.00 0.20 1.80 65.20 ▇▁▁▁▁
pct_PI 0 1 0.06 0.38 0.00 0.00 0.00 0.00 13.10 ▇▁▁▁▁
read_score 0 1 58.57 19.87 2.50 44.50 58.25 73.00 99.50 ▁▅▇▇▃
math_score 0 1 47.19 23.74 1.10 28.00 43.27 65.00 99.50 ▃▇▆▅▃
total_score 0 1 105.76 41.94 5.00 74.21 101.66 135.10 198.70 ▂▆▇▅▃
fed_per_child 0 1 973.74 1039.47 11.51 516.29 784.34 1153.09 25064.94 ▇▁▁▁▁
state_per_child 0 1 6550.46 4558.79 275.17 4298.02 5949.87 7852.63 265535.71 ▇▁▁▁▁
local_per_child 0 1 6180.88 4962.31 152.86 3162.79 4839.12 7622.83 123709.28 ▇▁▁▁▁

Introduction

So far with this data set, we have constructed several models, including linear models, regularized linear models, splines, trees, KNNs, and ensemble models such as random forests and gradient boosted trees. We’ve observed from our prior analyses that with these models, we can achieve \(R^2\) values between 0.35 and 0.45. The purpose of this analysis is twofold: first, to dive into some of the more fine details of the random forest and XGBoost models we constructed in our prior analysis, and second, to compare the model accuracy when using dummy variables for a certain categorical variable, versus conducting separate analyses for all values of a given categorical variable. In particular, we would like to see if our Region variables (which takes the values “South”, “West”, “Northeast”, and “North Central”) has a better effect on our model if we split it into four dummy variables or if we perform four separate analyses, one for each region.

Our first step, however, comes from the end of our previous analysis. We noted when looking at the variable importance plot for our Random Forest model, that we had some unexpected candidates for the most important variables in the construction of that model. We will first take a look at these relationships, with the potential of informing our construction of interaction variables during the preprocessing stage of modeling.

Next, once we have done a more exploratory analysis on the most important variables from the previous analysis, we will dive into constructing four different models for the four different regions, as well as one aggregate model for all the regions, in order to compare the \(R^2\) values for each of the models. We might preliminarily expect that, if there is some natural clustering within regions of the U.S., then splitting up the regions into separate analyses might provide some benefit. However, we would like to compare this against simply constructing a dummy variable for the region to see if it actually does improve the modeling accuracy at all.

Exploring Relationships

There were a few distinct terms which stuck out in the variable importance plot from the previous analysis: - pct_pov_x_fed_per_child - pct_SP_x_pct_HHVJ - pct_pov - pct_CC_x_pct_native - children - children_x_pct_native

Let’s explore the relationships between these variables and graduation rate.

data %>%
    mutate(pct_pov_x_fed_per_child = pct_pov * fed_per_child, 
           pct_SP_x_pct_HHVJ = pct_SP * pct_HHVJ,
           pct_CC_x_pct_native = pct_CC * pct_native, 
           children_x_pct_native = children * pct_native) %>%
    select(grad_rate_midpt, children, pct_pov, 
           pct_pov_x_fed_per_child, 
           pct_SP_x_pct_HHVJ, 
           pct_CC_x_pct_native, 
           children_x_pct_native) %>%
    pivot_longer(cols = children:children_x_pct_native, 
                 names_to = "value_type", 
                 values_to = "value") %>%
    ggplot(aes(y = grad_rate_midpt, x = value)) +
    geom_point(alpha = 0.05) + 
    geom_smooth() +
    facet_wrap(~ value_type, scales = "free")
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

It’s pretty obvious to see that many of these distributions are very right-skewed. Let’s see what we can do to transform these data to make them more symmetric:

Children

data %>% 
    select(grad_rate_midpt, children) %>%
    mutate(log_children = log(children)) %>%
    pivot_longer(children:log_children, 
                 names_to = "type", 
                 values_to = "value") %>%
ggplot(aes(x = value, y = grad_rate_midpt)) + 
    geom_point(alpha = 0.1) + 
    geom_smooth() + 
    facet_wrap(~ type, scales = "free")
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

It immediately appears as though the log-transformation on children makes the distribution more symmetric, thus it would make sense to log-transform the children variable for modeling purposes.

We can also notice, however, that there is a strange line which appears around the 75% mark of graduation rates; let’s explore this a little bit further:

ggplot(data, aes(x = grad_rate_midpt)) + 
    geom_histogram() + 
    lims(x = c(70, 80))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 8650 rows containing non-finite values (stat_bin).
## Warning: Removed 2 rows containing missing values (geom_bar).

data %>%
    filter(grad_rate_midpt == 75) %>%
    pivot_longer(pct_pov:last_col(), 
                 names_to = "type", 
                 values_to = "value") %>%
    ggplot(aes(x = children, y = value)) + 
    geom_point() + 
    facet_grid(rows = vars(type), 
               #cols = vars(region), 
               scales = "free")

For the school districts which have exactly a 75% graduation rate, there don’t appear to be any really distinct patterns in the data, other than the fact that all but one of these school districts has fewer than 1000 children in it.

Children * Percent Native

data %>% 
    select(predom_race, grad_rate_midpt, children, pct_native) %>%
    ggplot(aes(x = log(children), y = log(1 + pct_native))) + 
    geom_point(alpha = 0.3)

We can see that a log transform helps us out a lot here due to the right-skewed nature of both of these predictors. Because pct_native has a lot of zero values, we compute \(\log(1+\texttt{pct_native})\) to avoid losing a ton of data.

Percent Poverty * Fed per Child

ggplot(data, aes(x = pct_pov, y = fed_per_child)) + 
    geom_point()

cor(data$fed_per_child, data$pct_pov)
## [1] 0.4519707

We can notice that there is, in fact, a very distinct correlation between these two variables. Due to, once again, the right-skewed nature of the federal funding per child, let’s log-transform this to see how that affects things.

data %>% 
    select(fed_per_child, pct_pov) %>%
    mutate(log_fed_per_child = log(fed_per_child), .after = fed_per_child) %>%
    mutate(log_pct_pov = log(1 + pct_pov), .after = pct_pov) %>%
    cor() %>%
    as.data.frame() %>%
    select(3:4) %>%
    filter(row_number() < 3)
##                     pct_pov log_pct_pov
## fed_per_child     0.4519707   0.4139854
## log_fed_per_child 0.6616490   0.6791239
ggplot(data, aes(x = log(100*pct_pov), y = log(fed_per_child))) + 
    geom_point()

While this isn’t super interesting, it’s good to note that as the poverty rate of a school district increases, the amount of federal funding per child increases. Additionally, by log-transforming the predictors, we increase the correlation between them significantly.

Percent Single Parent * Percent HH in Vulnerable Job

ggplot(data, aes(x = pct_SP, y = pct_HHVJ)) + 
    geom_point(alpha = 0.2)

cor(data$pct_SP, data$pct_HHVJ)
## [1] 0.2585147

There doesn’t appear to be any distinct skew of these variables—let’s see if log-transforming them has any impact on their correlation.

data %>% 
    select(pct_SP, pct_HHVJ) %>%
    mutate(log_pct_SP = log(1+100*pct_SP), .after = pct_SP) %>%
    mutate(log_pct_HHVJ = log(1 + 100*pct_HHVJ), .after = pct_HHVJ) %>%
    cor() %>%
    as.data.frame() %>%
    select(3:4) %>%
    filter(row_number() < 3)
##             pct_HHVJ log_pct_HHVJ
## pct_SP     0.2585147    0.2308271
## log_pct_SP 0.2774935    0.3360805
ggplot(data, aes(x = log(100*pct_HHVJ), y = log(100*pct_SP))) + 
    geom_point()

It doesn’t appear that a log-transformation has an impact on their correlation, so we can leave these two untransformed before creating the interaction variables.

Percent Poverty

ggplot(data, aes(x = pct_pov, y = grad_rate_midpt)) + 
    geom_point(alpha = 0.1)

Percent Crowded Conditions * Percent Native

ggplot(data, aes(x = log(1 + pct_CC), y = log(1 + pct_native))) + 
    geom_point(aes(color = predom_race), alpha = 0.1) + 
    geom_smooth(aes(color = predom_race))
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

All Predictor Distributions

We can see that for many of these variables which are heavily right-skewed, performing a \(\ln(1 + X)\) transformation can be beneficial.

Let’s take a look at the distribution of every predictor to see which ones might benefit from a log transformation. Let’s plot a histogram of every predictor along with its log transformation in order to see which ones benefit.

Percentage Distributions

trans <- data %>%
    select(-grad_rate_midpt) %>%
    pivot_longer(children:last_col(), 
                 names_to = "type", 
                 values_to = "untransformed") %>%
    mutate(
        transformed = log10(1 + untransformed)
    ) %>%
    pivot_longer(c(untransformed, transformed), 
                 names_to = "transformation_status", 
                 values_to = "value") %>%
    mutate(transformation_status = factor(transformation_status, 
                                          levels = c(
                                              "untransformed", 
                                              "transformed"
                                          )))
trans %>%
    filter(type %>% str_detect('pct')) %>%
    ggplot(aes(x = value)) + 
    geom_histogram() + 
    facet_grid(cols = vars(transformation_status), 
               rows = vars(type), scales = "free")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

For all of our percentage predictors other than pct_white, a log-transformation appeared to make the distributions more symmetric.

pct_white

Let’s try a different transformation for pct_white, such as an exponential transformation. Let’s try quadratic, arcsine, and exponential transformations to see how symmetric we can get our pct_white variable to be.

data %>%
    select("untransformed" = pct_white) %>%
    mutate(
        "quadratic" = untransformed^2, 
        "arcsin" = asin(untransformed/100), 
        "exponential" = exp(untransformed)
    ) %>%
    pivot_longer(everything(), 
                 names_to = "transformation_type", 
                 values_to = "value") %>%
    mutate(transformation_type = factor(transformation_type, 
                                          levels = c(
                                              "untransformed", 
                                              "quadratic", 
                                              "arcsin", 
                                              "exponential"
                                          ))) %>%
    ggplot(aes(x = value)) + 
    geom_histogram() + 
    facet_wrap(~ transformation_type, scales = "free")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

It appears as though the arcsine transformation of the Percent White variable yields the most symmetric results. We will take this into account during the feature engineering stage of creating our final model.

Children, Assessments, and Funding

Let’s now take a look at our other, non-percentage variables.

trans %>%
    filter(type == "children") %>%
    ggplot(aes(x = value)) + 
    geom_histogram() + 
    facet_grid(cols = vars(transformation_status), 
               rows = vars(type), scales = "free") + 
    labs(x = NULL, y = NULL, 
         title = "Original vs. Log-transformed Children", 
         subtitle = "(Log transformations are base-10)")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

trans %>%
    filter(type %>% str_detect("score")) %>%
    ggplot(aes(x = value)) + 
    geom_histogram() + 
    facet_grid(cols = vars(transformation_status), 
               rows = vars(type), scales = "free")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

trans %>%
    filter(type %>% str_detect("per_child")) %>%
    ggplot(aes(x = value)) + 
    geom_histogram() + 
    facet_grid(cols = vars(transformation_status), 
               rows = vars(type), scales = "free")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Log-transforming children makes sense, as it gets much more symmetric upon log transformation.

Assessment scores are already mostly symmetric, and log transformations tend to skew them to the left, so we will leave these un-transformed.

Funding (federal, local, and state) per child benefits heavily from log transformations, so we will log-transform these in the preprocessing stage.

LS0tCnRpdGxlOiAiVmFyaWFibGUgSW50ZXJhY3Rpb25zIEFuYWx5c2lzIgphdXRob3I6ICJKb24gR2VpZ2VyLCBOb2VsIEdvb2R3aW4sIEFiaWdhaWwgSm9wcGEiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiBvcGVuaW50cm86OmxhYl9yZXBvcnQKLS0tCgpgYGB7ciBsb2FkLXBhY2thZ2VzLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KbGlicmFyeSh0aWR5dmVyc2UsIHdhcm4uY29uZmxpY3RzID0gRikKbGlicmFyeSh0aWR5bW9kZWxzLCB3YXJuLmNvbmZsaWN0cyA9IEYpCmxpYnJhcnkoZ2d0aGVtZXMpCnRoZW1lX3NldCh0aGVtZV9jbGVhbigpKQpzb3VyY2UoIi4uL3NjcmlwdHMvcHJ1bmVfcmFjZV92YXJpYWJsZXMuUiIpCmBgYAoKIyBEYXRhIEltcG9ydCBhbmQgSm9pbmluZwoKYGBge3IgaW1wb3J0LWRhdGF9CmhoIDwtIHJlYWRfY3N2KCIuLi9kYXRhL2hoLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JQogICAgbXV0YXRlKGxlYWlkID0gYXMuaW50ZWdlcihsZWFpZCkpICU+JSAKICAgIGZpbHRlcigKICAgICAgICBpZl9hbnkoZW5kc193aXRoKCJNT0UiKSwgCiAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHt4IDwgNTB9KSwgCiAgICAgICAgY2hpbGRyZW4gPj0gMTAwCiAgICApICU+JQogICAgc2VsZWN0KC1lbmRzX3dpdGgoIk1PRSIpKQoKZ3JhZCA8LSByZWFkX2NzdigiLi4vZGF0YS9ncmFkLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JQogICAgbXV0YXRlKGxlYWlkID0gYXMuaW50ZWdlcihsZWFpZCkpCgpyYWNlIDwtIHJlYWRfY3N2KCIuLi9kYXRhL3JhY2UuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lIAogICAgcHJ1bmVfYW5kX3ByZWRvbSgpICU+JQogICAgbXV0YXRlKGxlYWlkID0gYXMuaW50ZWdlcihsZWFpZCksIAogICAgICAgICAgIHByZWRvbV9yYWNlID0gYXMuY2hhcmFjdGVyKHByZWRvbV9yYWNlKSkKCmFzc2VzcyA8LSByZWFkX2NzdigiLi4vZGF0YS9hc3Nlc3MuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lCiAgICBtdXRhdGUobGVhaWQgPSBhcy5pbnRlZ2VyKGxlYWlkKSkKCmZpbmFuY2UgPC0gcmVhZF9jc3YoIi4uL2RhdGEvZmluYW5jZS5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUKICAgIG11dGF0ZShsZWFpZCA9IGFzLmludGVnZXIobGVhaWQpKQoKZGF0YSA8LSBoaCAlPiUKICAgIGxlZnRfam9pbihncmFkLCAgICBieSA9IGMoImxlYWlkIiA9ICJsZWFpZCIpKSAlPiUKICAgIGxlZnRfam9pbihyYWNlLCAgICBieSA9IGMoImxlYWlkIiA9ICJsZWFpZCIpKSAlPiUKICAgIGxlZnRfam9pbihhc3Nlc3MsICBieSA9IGMoImxlYWlkIiA9ICJsZWFpZCIpKSAlPiUKICAgIGxlZnRfam9pbihmaW5hbmNlLCBieSA9IGMoImxlYWlkIiA9ICJsZWFpZCIpKSAlPiUKICAgIHNlbGVjdCgKICAgICAgICAtc3RhdGUsIC1sZWFpZAogICAgKSAlPiUKICAgIHJlbG9jYXRlKHJlZ2lvbiwgcHJlZG9tX3JhY2UsIC5hZnRlciA9IGRpc3QpCgpybShoaCwgZ3JhZCwgcmFjZSwgYXNzZXNzLCBmaW5hbmNlKQoKbnJvdyhkYXRhKQoKZGF0YSA8LSBkYXRhICU+JQogICAgbmEub21pdCgpCgpucm93KGRhdGEpCmBgYAoKYGBge3J9CmRhdGEgJT4lIHNraW1yOjpza2ltKCkKYGBgCgojIEludHJvZHVjdGlvbgoKU28gZmFyIHdpdGggdGhpcyBkYXRhIHNldCwgd2UgaGF2ZSBjb25zdHJ1Y3RlZCBzZXZlcmFsIG1vZGVscywgaW5jbHVkaW5nIGxpbmVhciBtb2RlbHMsIHJlZ3VsYXJpemVkIGxpbmVhciBtb2RlbHMsIHNwbGluZXMsIHRyZWVzLCBLTk5zLCBhbmQgZW5zZW1ibGUgbW9kZWxzIHN1Y2ggYXMgcmFuZG9tIGZvcmVzdHMgYW5kIGdyYWRpZW50IGJvb3N0ZWQgdHJlZXMuIFdlJ3ZlIG9ic2VydmVkIGZyb20gb3VyIHByaW9yIGFuYWx5c2VzIHRoYXQgd2l0aCB0aGVzZSBtb2RlbHMsIHdlIGNhbiBhY2hpZXZlICRSXjIkIHZhbHVlcyBiZXR3ZWVuIDAuMzUgYW5kIDAuNDUuIFRoZSBwdXJwb3NlIG9mIHRoaXMgYW5hbHlzaXMgaXMgdHdvZm9sZDogZmlyc3QsIHRvIGRpdmUgaW50byBzb21lIG9mIHRoZSBtb3JlIGZpbmUgZGV0YWlscyBvZiB0aGUgcmFuZG9tIGZvcmVzdCBhbmQgWEdCb29zdCBtb2RlbHMgd2UgY29uc3RydWN0ZWQgaW4gb3VyIHByaW9yIGFuYWx5c2lzLCBhbmQgc2Vjb25kLCB0byBjb21wYXJlIHRoZSBtb2RlbCBhY2N1cmFjeSB3aGVuIHVzaW5nIGR1bW15IHZhcmlhYmxlcyBmb3IgYSBjZXJ0YWluIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCB2ZXJzdXMgY29uZHVjdGluZyBzZXBhcmF0ZSBhbmFseXNlcyBmb3IgYWxsIHZhbHVlcyBvZiBhIGdpdmVuIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBJbiBwYXJ0aWN1bGFyLCB3ZSB3b3VsZCBsaWtlIHRvIHNlZSBpZiBvdXIgUmVnaW9uIHZhcmlhYmxlcyAod2hpY2ggdGFrZXMgdGhlIHZhbHVlcyAiU291dGgiLCAiV2VzdCIsICJOb3J0aGVhc3QiLCBhbmQgIk5vcnRoIENlbnRyYWwiKSBoYXMgYSBiZXR0ZXIgZWZmZWN0IG9uIG91ciBtb2RlbCBpZiB3ZSBzcGxpdCBpdCBpbnRvIGZvdXIgZHVtbXkgdmFyaWFibGVzIG9yIGlmIHdlIHBlcmZvcm0gZm91ciBzZXBhcmF0ZSBhbmFseXNlcywgb25lIGZvciBlYWNoIHJlZ2lvbi4gIAoKT3VyIGZpcnN0IHN0ZXAsIGhvd2V2ZXIsIGNvbWVzIGZyb20gdGhlIGVuZCBvZiBvdXIgcHJldmlvdXMgYW5hbHlzaXMuIFdlIG5vdGVkIHdoZW4gbG9va2luZyBhdCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSBwbG90IGZvciBvdXIgUmFuZG9tIEZvcmVzdCBtb2RlbCwgdGhhdCB3ZSBoYWQgc29tZSB1bmV4cGVjdGVkIGNhbmRpZGF0ZXMgZm9yIHRoZSBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMgaW4gdGhlIGNvbnN0cnVjdGlvbiBvZiB0aGF0IG1vZGVsLiBXZSB3aWxsIGZpcnN0IHRha2UgYSBsb29rIGF0IHRoZXNlIHJlbGF0aW9uc2hpcHMsIHdpdGggdGhlIHBvdGVudGlhbCBvZiBpbmZvcm1pbmcgb3VyIGNvbnN0cnVjdGlvbiBvZiBpbnRlcmFjdGlvbiB2YXJpYWJsZXMgZHVyaW5nIHRoZSBwcmVwcm9jZXNzaW5nIHN0YWdlIG9mIG1vZGVsaW5nLiAKCk5leHQsIG9uY2Ugd2UgaGF2ZSBkb25lIGEgbW9yZSBleHBsb3JhdG9yeSBhbmFseXNpcyBvbiB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzIGZyb20gdGhlIHByZXZpb3VzIGFuYWx5c2lzLCB3ZSB3aWxsIGRpdmUgaW50byBjb25zdHJ1Y3RpbmcgZm91ciBkaWZmZXJlbnQgbW9kZWxzIGZvciB0aGUgZm91ciBkaWZmZXJlbnQgcmVnaW9ucywgYXMgd2VsbCBhcyBvbmUgYWdncmVnYXRlIG1vZGVsIGZvciBhbGwgdGhlIHJlZ2lvbnMsIGluIG9yZGVyIHRvIGNvbXBhcmUgdGhlICRSXjIkIHZhbHVlcyBmb3IgZWFjaCBvZiB0aGUgbW9kZWxzLiBXZSBtaWdodCBwcmVsaW1pbmFyaWx5IGV4cGVjdCB0aGF0LCBpZiB0aGVyZSBpcyBzb21lIG5hdHVyYWwgY2x1c3RlcmluZyB3aXRoaW4gcmVnaW9ucyBvZiB0aGUgVS5TLiwgdGhlbiBzcGxpdHRpbmcgdXAgdGhlIHJlZ2lvbnMgaW50byBzZXBhcmF0ZSBhbmFseXNlcyBtaWdodCBwcm92aWRlIHNvbWUgYmVuZWZpdC4gSG93ZXZlciwgd2Ugd291bGQgbGlrZSB0byBjb21wYXJlIHRoaXMgYWdhaW5zdCBzaW1wbHkgY29uc3RydWN0aW5nIGEgZHVtbXkgdmFyaWFibGUgZm9yIHRoZSByZWdpb24gdG8gc2VlIGlmIGl0IGFjdHVhbGx5IGRvZXMgaW1wcm92ZSB0aGUgbW9kZWxpbmcgYWNjdXJhY3kgYXQgYWxsLiAKCiMgRXhwbG9yaW5nIFJlbGF0aW9uc2hpcHMKClRoZXJlIHdlcmUgYSBmZXcgZGlzdGluY3QgdGVybXMgd2hpY2ggc3R1Y2sgb3V0IGluIHRoZSB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QgZnJvbSB0aGUgcHJldmlvdXMgYW5hbHlzaXM6IAotIGBwY3RfcG92X3hfZmVkX3Blcl9jaGlsZGAKLSBgcGN0X1NQX3hfcGN0X0hIVkpgCi0gYHBjdF9wb3ZgCi0gYHBjdF9DQ194X3BjdF9uYXRpdmVgCi0gYGNoaWxkcmVuYAotIGBjaGlsZHJlbl94X3BjdF9uYXRpdmVgCgpMZXQncyBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlc2UgdmFyaWFibGVzIGFuZCBncmFkdWF0aW9uIHJhdGUuIAoKYGBge3J9CmRhdGEgJT4lCiAgICBtdXRhdGUocGN0X3Bvdl94X2ZlZF9wZXJfY2hpbGQgPSBwY3RfcG92ICogZmVkX3Blcl9jaGlsZCwgCiAgICAgICAgICAgcGN0X1NQX3hfcGN0X0hIVkogPSBwY3RfU1AgKiBwY3RfSEhWSiwKICAgICAgICAgICBwY3RfQ0NfeF9wY3RfbmF0aXZlID0gcGN0X0NDICogcGN0X25hdGl2ZSwgCiAgICAgICAgICAgY2hpbGRyZW5feF9wY3RfbmF0aXZlID0gY2hpbGRyZW4gKiBwY3RfbmF0aXZlKSAlPiUKICAgIHNlbGVjdChncmFkX3JhdGVfbWlkcHQsIGNoaWxkcmVuLCBwY3RfcG92LCAKICAgICAgICAgICBwY3RfcG92X3hfZmVkX3Blcl9jaGlsZCwgCiAgICAgICAgICAgcGN0X1NQX3hfcGN0X0hIVkosIAogICAgICAgICAgIHBjdF9DQ194X3BjdF9uYXRpdmUsIAogICAgICAgICAgIGNoaWxkcmVuX3hfcGN0X25hdGl2ZSkgJT4lCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGNoaWxkcmVuOmNoaWxkcmVuX3hfcGN0X25hdGl2ZSwgCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFsdWVfdHlwZSIsIAogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQogICAgZ2dwbG90KGFlcyh5ID0gZ3JhZF9yYXRlX21pZHB0LCB4ID0gdmFsdWUpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4wNSkgKyAKICAgIGdlb21fc21vb3RoKCkgKwogICAgZmFjZXRfd3JhcCh+IHZhbHVlX3R5cGUsIHNjYWxlcyA9ICJmcmVlIikKYGBgCgpJdCdzIHByZXR0eSBvYnZpb3VzIHRvIHNlZSB0aGF0IG1hbnkgb2YgdGhlc2UgZGlzdHJpYnV0aW9ucyBhcmUgdmVyeSByaWdodC1za2V3ZWQuIExldCdzIHNlZSB3aGF0IHdlIGNhbiBkbyB0byB0cmFuc2Zvcm0gdGhlc2UgZGF0YSB0byBtYWtlIHRoZW0gbW9yZSBzeW1tZXRyaWM6IAoKIyMgQ2hpbGRyZW4KYGBge3J9CmRhdGEgJT4lIAogICAgc2VsZWN0KGdyYWRfcmF0ZV9taWRwdCwgY2hpbGRyZW4pICU+JQogICAgbXV0YXRlKGxvZ19jaGlsZHJlbiA9IGxvZyhjaGlsZHJlbikpICU+JQogICAgcGl2b3RfbG9uZ2VyKGNoaWxkcmVuOmxvZ19jaGlsZHJlbiwgCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidHlwZSIsIAogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQpnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeSA9IGdyYWRfcmF0ZV9taWRwdCkpICsgCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4xKSArIAogICAgZ2VvbV9zbW9vdGgoKSArIAogICAgZmFjZXRfd3JhcCh+IHR5cGUsIHNjYWxlcyA9ICJmcmVlIikKYGBgCgpJdCBpbW1lZGlhdGVseSBhcHBlYXJzIGFzIHRob3VnaCB0aGUgbG9nLXRyYW5zZm9ybWF0aW9uIG9uIGNoaWxkcmVuIG1ha2VzIHRoZSBkaXN0cmlidXRpb24gbW9yZSBzeW1tZXRyaWMsIHRodXMgaXQgd291bGQgbWFrZSBzZW5zZSB0byBsb2ctdHJhbnNmb3JtIHRoZSBjaGlsZHJlbiB2YXJpYWJsZSBmb3IgbW9kZWxpbmcgcHVycG9zZXMuIAoKV2UgY2FuIGFsc28gbm90aWNlLCBob3dldmVyLCB0aGF0IHRoZXJlIGlzIGEgc3RyYW5nZSBsaW5lIHdoaWNoIGFwcGVhcnMgYXJvdW5kIHRoZSA3NSUgbWFyayBvZiBncmFkdWF0aW9uIHJhdGVzOyBsZXQncyBleHBsb3JlIHRoaXMgYSBsaXR0bGUgYml0IGZ1cnRoZXI6IAoKYGBge3J9CmdncGxvdChkYXRhLCBhZXMoeCA9IGdyYWRfcmF0ZV9taWRwdCkpICsgCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsgCiAgICBsaW1zKHggPSBjKDcwLCA4MCkpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQgPSAyMH0KZGF0YSAlPiUKICAgIGZpbHRlcihncmFkX3JhdGVfbWlkcHQgPT0gNzUpICU+JQogICAgcGl2b3RfbG9uZ2VyKHBjdF9wb3Y6bGFzdF9jb2woKSwgCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidHlwZSIsIAogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gY2hpbGRyZW4sIHkgPSB2YWx1ZSkpICsgCiAgICBnZW9tX3BvaW50KCkgKyAKICAgIGZhY2V0X2dyaWQocm93cyA9IHZhcnModHlwZSksIAogICAgICAgICAgICAgICAjY29scyA9IHZhcnMocmVnaW9uKSwgCiAgICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlIikKYGBgCgpGb3IgdGhlIHNjaG9vbCBkaXN0cmljdHMgd2hpY2ggaGF2ZSBleGFjdGx5IGEgNzUlIGdyYWR1YXRpb24gcmF0ZSwgdGhlcmUgZG9uJ3QgYXBwZWFyIHRvIGJlIGFueSByZWFsbHkgZGlzdGluY3QgcGF0dGVybnMgaW4gdGhlIGRhdGEsIG90aGVyIHRoYW4gdGhlIGZhY3QgdGhhdCBhbGwgYnV0IG9uZSBvZiB0aGVzZSBzY2hvb2wgZGlzdHJpY3RzIGhhcyBmZXdlciB0aGFuIDEwMDAgY2hpbGRyZW4gaW4gaXQuIAoKCiMjIENoaWxkcmVuICogUGVyY2VudCBOYXRpdmUKCmBgYHtyfQpkYXRhICU+JSAKICAgIHNlbGVjdChwcmVkb21fcmFjZSwgZ3JhZF9yYXRlX21pZHB0LCBjaGlsZHJlbiwgcGN0X25hdGl2ZSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBsb2coY2hpbGRyZW4pLCB5ID0gbG9nKDEgKyBwY3RfbmF0aXZlKSkpICsgCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4zKQpgYGAKCldlIGNhbiBzZWUgdGhhdCBhIGxvZyB0cmFuc2Zvcm0gaGVscHMgdXMgb3V0IGEgbG90IGhlcmUgZHVlIHRvIHRoZSByaWdodC1za2V3ZWQgbmF0dXJlIG9mIGJvdGggb2YgdGhlc2UgcHJlZGljdG9ycy4gQmVjYXVzZSBgcGN0X25hdGl2ZWAgaGFzIGEgbG90IG9mIHplcm8gdmFsdWVzLCB3ZSBjb21wdXRlICRcbG9nKDErXHRleHR0dHtwY3RfbmF0aXZlfSkkIHRvIGF2b2lkIGxvc2luZyBhIHRvbiBvZiBkYXRhLiAKCiMjIFBlcmNlbnQgUG92ZXJ0eSAqIEZlZCBwZXIgQ2hpbGQKCmBgYHtyfQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBwY3RfcG92LCB5ID0gZmVkX3Blcl9jaGlsZCkpICsgCiAgICBnZW9tX3BvaW50KCkKY29yKGRhdGEkZmVkX3Blcl9jaGlsZCwgZGF0YSRwY3RfcG92KQpgYGAKCldlIGNhbiBub3RpY2UgdGhhdCB0aGVyZSBpcywgaW4gZmFjdCwgYSB2ZXJ5IGRpc3RpbmN0IGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlc2UgdHdvIHZhcmlhYmxlcy4gRHVlIHRvLCBvbmNlIGFnYWluLCB0aGUgcmlnaHQtc2tld2VkIG5hdHVyZSBvZiB0aGUgZmVkZXJhbCBmdW5kaW5nIHBlciBjaGlsZCwgbGV0J3MgbG9nLXRyYW5zZm9ybSB0aGlzIHRvIHNlZSBob3cgdGhhdCBhZmZlY3RzIHRoaW5ncy4gCgpgYGB7cn0KZGF0YSAlPiUgCiAgICBzZWxlY3QoZmVkX3Blcl9jaGlsZCwgcGN0X3BvdikgJT4lCiAgICBtdXRhdGUobG9nX2ZlZF9wZXJfY2hpbGQgPSBsb2coZmVkX3Blcl9jaGlsZCksIC5hZnRlciA9IGZlZF9wZXJfY2hpbGQpICU+JQogICAgbXV0YXRlKGxvZ19wY3RfcG92ID0gbG9nKDEgKyBwY3RfcG92KSwgLmFmdGVyID0gcGN0X3BvdikgJT4lCiAgICBjb3IoKSAlPiUKICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUKICAgIHNlbGVjdCgzOjQpICU+JQogICAgZmlsdGVyKHJvd19udW1iZXIoKSA8IDMpCmdncGxvdChkYXRhLCBhZXMoeCA9IGxvZygxMDAqcGN0X3BvdiksIHkgPSBsb2coZmVkX3Blcl9jaGlsZCkpKSArIAogICAgZ2VvbV9wb2ludCgpCmBgYAoKV2hpbGUgdGhpcyBpc24ndCBzdXBlciBpbnRlcmVzdGluZywgaXQncyBnb29kIHRvIG5vdGUgdGhhdCBhcyB0aGUgcG92ZXJ0eSByYXRlIG9mIGEgc2Nob29sIGRpc3RyaWN0IGluY3JlYXNlcywgdGhlIGFtb3VudCBvZiBmZWRlcmFsIGZ1bmRpbmcgcGVyIGNoaWxkIGluY3JlYXNlcy4gQWRkaXRpb25hbGx5LCBieSBsb2ctdHJhbnNmb3JtaW5nIHRoZSBwcmVkaWN0b3JzLCB3ZSBpbmNyZWFzZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGVtIHNpZ25pZmljYW50bHkuIAoKIyMgUGVyY2VudCBTaW5nbGUgUGFyZW50ICogUGVyY2VudCBISCBpbiBWdWxuZXJhYmxlIEpvYgoKYGBge3J9CmdncGxvdChkYXRhLCBhZXMoeCA9IHBjdF9TUCwgeSA9IHBjdF9ISFZKKSkgKyAKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpCmNvcihkYXRhJHBjdF9TUCwgZGF0YSRwY3RfSEhWSikKYGBgCgpUaGVyZSBkb2Vzbid0IGFwcGVhciB0byBiZSBhbnkgZGlzdGluY3Qgc2tldyBvZiB0aGVzZSB2YXJpYWJsZXMtLS1sZXQncyBzZWUgaWYgbG9nLXRyYW5zZm9ybWluZyB0aGVtIGhhcyBhbnkgaW1wYWN0IG9uIHRoZWlyIGNvcnJlbGF0aW9uLgoKYGBge3J9CmRhdGEgJT4lIAogICAgc2VsZWN0KHBjdF9TUCwgcGN0X0hIVkopICU+JQogICAgbXV0YXRlKGxvZ19wY3RfU1AgPSBsb2coMSsxMDAqcGN0X1NQKSwgLmFmdGVyID0gcGN0X1NQKSAlPiUKICAgIG11dGF0ZShsb2dfcGN0X0hIVkogPSBsb2coMSArIDEwMCpwY3RfSEhWSiksIC5hZnRlciA9IHBjdF9ISFZKKSAlPiUKICAgIGNvcigpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgc2VsZWN0KDM6NCkgJT4lCiAgICBmaWx0ZXIocm93X251bWJlcigpIDwgMykKZ2dwbG90KGRhdGEsIGFlcyh4ID0gbG9nKDEwMCpwY3RfSEhWSiksIHkgPSBsb2coMTAwKnBjdF9TUCkpKSArIAogICAgZ2VvbV9wb2ludCgpCmBgYAoKSXQgZG9lc24ndCBhcHBlYXIgdGhhdCBhIGxvZy10cmFuc2Zvcm1hdGlvbiBoYXMgYW4gaW1wYWN0IG9uIHRoZWlyIGNvcnJlbGF0aW9uLCBzbyB3ZSBjYW4gbGVhdmUgdGhlc2UgdHdvIHVudHJhbnNmb3JtZWQgYmVmb3JlIGNyZWF0aW5nIHRoZSBpbnRlcmFjdGlvbiB2YXJpYWJsZXMuIAoKIyMgUGVyY2VudCBQb3ZlcnR5CgpgYGB7cn0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gcGN0X3BvdiwgeSA9IGdyYWRfcmF0ZV9taWRwdCkpICsgCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4xKQpgYGAKCiMjIFBlcmNlbnQgQ3Jvd2RlZCBDb25kaXRpb25zICogUGVyY2VudCBOYXRpdmUKCmBgYHtyfQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBsb2coMSArIHBjdF9DQyksIHkgPSBsb2coMSArIHBjdF9uYXRpdmUpKSkgKyAKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gcHJlZG9tX3JhY2UpLCBhbHBoYSA9IDAuMSkgKyAKICAgIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IHByZWRvbV9yYWNlKSkKYGBgCgojIEFsbCBQcmVkaWN0b3IgRGlzdHJpYnV0aW9ucwoKV2UgY2FuIHNlZSB0aGF0IGZvciBtYW55IG9mIHRoZXNlIHZhcmlhYmxlcyB3aGljaCBhcmUgaGVhdmlseSByaWdodC1za2V3ZWQsIHBlcmZvcm1pbmcgYSAkXGxuKDEgKyBYKSQgdHJhbnNmb3JtYXRpb24gY2FuIGJlIGJlbmVmaWNpYWwuICAKCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZXZlcnkgcHJlZGljdG9yIHRvIHNlZSB3aGljaCBvbmVzIG1pZ2h0IGJlbmVmaXQgZnJvbSBhIGxvZyB0cmFuc2Zvcm1hdGlvbi4gTGV0J3MgcGxvdCBhIGhpc3RvZ3JhbSBvZiBldmVyeSBwcmVkaWN0b3IgYWxvbmcgd2l0aCBpdHMgbG9nIHRyYW5zZm9ybWF0aW9uIGluIG9yZGVyIHRvIHNlZSB3aGljaCBvbmVzIGJlbmVmaXQuIAoKIyMgUGVyY2VudGFnZSBEaXN0cmlidXRpb25zCgpgYGB7ciwgZmlnLmhlaWdodD0yMH0KdHJhbnMgPC0gZGF0YSAlPiUKICAgIHNlbGVjdCgtZ3JhZF9yYXRlX21pZHB0KSAlPiUKICAgIHBpdm90X2xvbmdlcihjaGlsZHJlbjpsYXN0X2NvbCgpLCAKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJ0eXBlIiwgCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInVudHJhbnNmb3JtZWQiKSAlPiUKICAgIG11dGF0ZSgKICAgICAgICB0cmFuc2Zvcm1lZCA9IGxvZzEwKDEgKyB1bnRyYW5zZm9ybWVkKQogICAgKSAlPiUKICAgIHBpdm90X2xvbmdlcihjKHVudHJhbnNmb3JtZWQsIHRyYW5zZm9ybWVkKSwgCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidHJhbnNmb3JtYXRpb25fc3RhdHVzIiwgCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lCiAgICBtdXRhdGUodHJhbnNmb3JtYXRpb25fc3RhdHVzID0gZmFjdG9yKHRyYW5zZm9ybWF0aW9uX3N0YXR1cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW50cmFuc2Zvcm1lZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRyYW5zZm9ybWVkIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkKdHJhbnMgJT4lCiAgICBmaWx0ZXIodHlwZSAlPiUgc3RyX2RldGVjdCgncGN0JykpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gdmFsdWUpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oKSArIAogICAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyh0cmFuc2Zvcm1hdGlvbl9zdGF0dXMpLCAKICAgICAgICAgICAgICAgcm93cyA9IHZhcnModHlwZSksIHNjYWxlcyA9ICJmcmVlIikKYGBgCgpGb3IgYWxsIG9mIG91ciBwZXJjZW50YWdlIHByZWRpY3RvcnMgb3RoZXIgdGhhbiBgcGN0X3doaXRlYCwgYSBsb2ctdHJhbnNmb3JtYXRpb24gYXBwZWFyZWQgdG8gbWFrZSB0aGUgZGlzdHJpYnV0aW9ucyBtb3JlIHN5bW1ldHJpYy4gCgojIyBgcGN0X3doaXRlYAoKTGV0J3MgdHJ5IGEgZGlmZmVyZW50IHRyYW5zZm9ybWF0aW9uIGZvciBgcGN0X3doaXRlYCwgc3VjaCBhcyBhbiBleHBvbmVudGlhbCB0cmFuc2Zvcm1hdGlvbi4gTGV0J3MgdHJ5IHF1YWRyYXRpYywgYXJjc2luZSwgYW5kIGV4cG9uZW50aWFsIHRyYW5zZm9ybWF0aW9ucyB0byBzZWUgaG93IHN5bW1ldHJpYyB3ZSBjYW4gZ2V0IG91ciBgcGN0X3doaXRlYCB2YXJpYWJsZSB0byBiZS4gCgpgYGB7cn0KZGF0YSAlPiUKICAgIHNlbGVjdCgidW50cmFuc2Zvcm1lZCIgPSBwY3Rfd2hpdGUpICU+JQogICAgbXV0YXRlKAogICAgICAgICJxdWFkcmF0aWMiID0gdW50cmFuc2Zvcm1lZF4yLCAKICAgICAgICAiYXJjc2luIiA9IGFzaW4odW50cmFuc2Zvcm1lZC8xMDApLCAKICAgICAgICAiZXhwb25lbnRpYWwiID0gZXhwKHVudHJhbnNmb3JtZWQpCiAgICApICU+JQogICAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidHJhbnNmb3JtYXRpb25fdHlwZSIsIAogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQogICAgbXV0YXRlKHRyYW5zZm9ybWF0aW9uX3R5cGUgPSBmYWN0b3IodHJhbnNmb3JtYXRpb25fdHlwZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW50cmFuc2Zvcm1lZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInF1YWRyYXRpYyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFyY3NpbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImV4cG9uZW50aWFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSkpICsgCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsgCiAgICBmYWNldF93cmFwKH4gdHJhbnNmb3JtYXRpb25fdHlwZSwgc2NhbGVzID0gImZyZWUiKQpgYGAKCkl0IGFwcGVhcnMgYXMgdGhvdWdoIHRoZSBhcmNzaW5lIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSBQZXJjZW50IFdoaXRlIHZhcmlhYmxlIHlpZWxkcyB0aGUgbW9zdCBzeW1tZXRyaWMgcmVzdWx0cy4gV2Ugd2lsbCB0YWtlIHRoaXMgaW50byBhY2NvdW50IGR1cmluZyB0aGUgZmVhdHVyZSBlbmdpbmVlcmluZyBzdGFnZSBvZiBjcmVhdGluZyBvdXIgZmluYWwgbW9kZWwuIAoKCiMjIENoaWxkcmVuLCBBc3Nlc3NtZW50cywgYW5kIEZ1bmRpbmcKCkxldCdzIG5vdyB0YWtlIGEgbG9vayBhdCBvdXIgb3RoZXIsIG5vbi1wZXJjZW50YWdlIHZhcmlhYmxlcy4KCmBgYHtyfQp0cmFucyAlPiUKICAgIGZpbHRlcih0eXBlID09ICJjaGlsZHJlbiIpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gdmFsdWUpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oKSArIAogICAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyh0cmFuc2Zvcm1hdGlvbl9zdGF0dXMpLCAKICAgICAgICAgICAgICAgcm93cyA9IHZhcnModHlwZSksIHNjYWxlcyA9ICJmcmVlIikgKyAKICAgIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMLCAKICAgICAgICAgdGl0bGUgPSAiT3JpZ2luYWwgdnMuIExvZy10cmFuc2Zvcm1lZCBDaGlsZHJlbiIsIAogICAgICAgICBzdWJ0aXRsZSA9ICIoTG9nIHRyYW5zZm9ybWF0aW9ucyBhcmUgYmFzZS0xMCkiKQoKdHJhbnMgJT4lCiAgICBmaWx0ZXIodHlwZSAlPiUgc3RyX2RldGVjdCgic2NvcmUiKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSkpICsgCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsgCiAgICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKHRyYW5zZm9ybWF0aW9uX3N0YXR1cyksIAogICAgICAgICAgICAgICByb3dzID0gdmFycyh0eXBlKSwgc2NhbGVzID0gImZyZWUiKQoKdHJhbnMgJT4lCiAgICBmaWx0ZXIodHlwZSAlPiUgc3RyX2RldGVjdCgicGVyX2NoaWxkIikpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gdmFsdWUpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oKSArIAogICAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyh0cmFuc2Zvcm1hdGlvbl9zdGF0dXMpLCAKICAgICAgICAgICAgICAgcm93cyA9IHZhcnModHlwZSksIHNjYWxlcyA9ICJmcmVlIikKYGBgCgpMb2ctdHJhbnNmb3JtaW5nIGNoaWxkcmVuIG1ha2VzIHNlbnNlLCBhcyBpdCBnZXRzIG11Y2ggbW9yZSBzeW1tZXRyaWMgdXBvbiBsb2cgdHJhbnNmb3JtYXRpb24uIAoKQXNzZXNzbWVudCBzY29yZXMgYXJlIGFscmVhZHkgbW9zdGx5IHN5bW1ldHJpYywgYW5kIGxvZyB0cmFuc2Zvcm1hdGlvbnMgdGVuZCB0byBza2V3IHRoZW0gdG8gdGhlIGxlZnQsIHNvIHdlIHdpbGwgbGVhdmUgdGhlc2UgdW4tdHJhbnNmb3JtZWQuIAoKRnVuZGluZyAoZmVkZXJhbCwgbG9jYWwsIGFuZCBzdGF0ZSkgcGVyIGNoaWxkIGJlbmVmaXRzIGhlYXZpbHkgZnJvbSBsb2cgdHJhbnNmb3JtYXRpb25zLCBzbyB3ZSB3aWxsIGxvZy10cmFuc2Zvcm0gdGhlc2UgaW4gdGhlIHByZXByb2Nlc3Npbmcgc3RhZ2UuIA==