Full repo: https://github.com/jrfarrer/stats701_hw3/

Published file: https://jrfarrer.github.io/stats701_hw3/

# Set the seed for reproducibility
set.seed(44)
# Set the locale of the session so languages other than English can be used
invisible(Sys.setlocale("LC_ALL", "en_US.UTF-8"))
# Prevent printing in scientific notation
options(digits = 4, width = 220)
# Create a logger function
logger <- function(msg, level = "info", file = log_file) {
    cat(paste0("[", format(Sys.time(), "%Y-%m-%d %H:%M:%S.%OS"), "][", level, "] ", msg, "\n"), file = stdout())
}
# Set the project directory
base_dir <- ''
data_dir <- paste0(base_dir, "data/")
code_dir <- paste0(base_dir, "code/")
viz_dir <- paste0(base_dir, "viz/")
dir.create(data_dir, showWarnings = FALSE)
dir.create(code_dir, showWarnings = FALSE)
dir.create(viz_dir, showWarnings = FALSE)
# To the font second font, run the following two lines of code and add name of user to vector
# system(paste0("cp -r ",viz_dir,"fonts/. ~/Library/Fonts/")) # instantaneous
# font_import() # takes approximately 5-10 min
users_v <- c("Jordan")
# Create a color palette
pal538 <- ggthemes_data$fivethirtyeight
# Create a theme to use throughout the analysis
theme_jrf <- function(base_size = 8, base_family = ifelse(Sys.info()[['user']] %in% users_v, "DecimaMonoPro", "Helvetica")) {
    theme(
        plot.background = element_rect(fill = "#F0F0F0", colour = "#606063"), 
        panel.background = element_rect(fill = "#F0F0F0", colour = NA), 
        panel.border = element_blank(),
        panel.grid.major =   element_line(colour = "#D7D7D8"),
        panel.grid.minor =   element_line(colour = "#D7D7D8", size = 0.25),
        panel.margin =       unit(0.25, "lines"),
        panel.margin.x =     NULL,
        panel.margin.y =     NULL,
        axis.ticks.x = element_blank(), 
        axis.ticks.y = element_blank(),
        axis.title = element_text(colour = "#A0A0A3"),
        axis.text.x = element_text(vjust = 1, colour = '#3C3C3C',
                                   family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica")),
        axis.text.y = element_text(hjust = 1, colour = '#3C3C3C',
                                    family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica")),
        legend.background = element_blank(),
        legend.key = element_blank(), 
        plot.title = element_text(face = 'bold', colour = '#3C3C3C', hjust = 0),
        text = element_text(size = 9, family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica")),
        title = element_text(family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica"))
        
    )
}

1 ISLR Problem

1.1 A

We create 100 \(X\) and \(\epsilon\) variables using rnorm.

X = rnorm(100)
eps = rnorm(100)

1.2 B

We select \(\beta_0 = 1\) \(\beta_1 = 2\), \(\beta_2 = -1.4\), \(\beta_3 = 0.6\).

beta0 = 1
beta1 = 2.5
beta2 = -3.4
beta3 = 0.9
Y = beta0 + beta1 * X + beta2 * X^2 + beta3 * X^3 + eps

1.3 C

We use regsubsets with exhaustive search.

data_df = data.frame(y = Y, x = X)
fn_regsubsets_plots <- function(fit_obj, elbow = NULL) {
    regsubsets_summary <- summary(fit_obj)
    
    g <- 
    data_frame(
          predictors = 1:length(regsubsets_summary$cp)
        , cp = regsubsets_summary$cp
        , bic = regsubsets_summary$bic
        , adjr2 = regsubsets_summary$adjr2
    ) %>%
        gather(metric, value, -predictors) %>%
        mutate(metric = factor(metric, levels = c("cp","bic","adjr2"))) %>%
        ggplot(aes(x = predictors, y = value, colour = metric)) +
        facet_grid(metric ~ ., scale = "free_y", switch = "y", 
                   labeller = ggplot2::labeller(metric = c(cp = "Cp", bic = "BIC", adjr2 = "Adjusted R^2"))) +
        geom_line() + geom_point() +
        geom_label(data = data_frame(
            predictors = c(which.min(regsubsets_summary$cp), which.min(regsubsets_summary$bic),
                           which.max(regsubsets_summary$adjr2))
            , metric = factor(c("cp","bic","adjr2"), levels = c("cp","bic","adjr2"))
            , value = c(min(regsubsets_summary$cp), min(regsubsets_summary$bic), max(regsubsets_summary$adjr2))
            , label = paste0("Optimal\nd=", c(which.min(regsubsets_summary$cp), which.min(regsubsets_summary$bic),
                                              which.max(regsubsets_summary$adjr2)))
            , vjust = c(-.5, -.5, 1.25)
        ), aes(x = predictors, y = value, label = label, vjust = vjust), family = "DecimaMonoPro") +
        theme_jrf() + 
        labs(title = paste0(stringr::str_to_title(regsubsets_summary$obj$method), " Search"), 
             x = "# of Predictors", y = NULL) +
        scale_colour_manual(guide = FALSE, values = c(pal538['red'][[1]], pal538['green'][[1]], pal538['blue'][[1]]))
    
    if (!is.null(elbow)) {
        g <- g + geom_vline(xintercept = elbow, alpha = 0.5) + 
            geom_label(data = data_frame(x = elbow, y = 300, metric = factor(c("cp"), levels = c("cp","bic","adjr2")), 
                label = "Elbow with\n3 predictors"), aes(x=x,y=y,label=label), colour = "black", hjust = -.1,
               family = "DecimaMonoPro")
    }
    
    print(g)
}
        
fit1 = regsubsets(y ~ poly(x, 10, raw = TRUE), data = data_df, nvmax = 10, method = 'exhaustive')
fit1_summary = summary(fit1)
# Find the model size for best cp, BIC and adjr2
data_frame(
    Cp = which.min(fit1_summary$cp)
    , BIC = which.min(fit1_summary$bic)
    , `Adj R^2` = which.max(fit1_summary$adjr2)
) %>%
    pander(caption = "Exhaustive Search: Optimal Information Criterion")
Exhaustive Search: Optimal Information Criterion
Cp BIC Adj R^2
3 3 3
fn_regsubsets_plots(fit1)

Based on the table and plots above for Exhaustive Search, the best model is

\[Y = \beta_0 + \beta_{1}X^1 + \beta_{2}X^2 + \beta_{3}X^3\]

1.4 D

fit2 = regsubsets(y ~ poly(x, 10, raw = TRUE), data = data_df, nvmax = 10, method = 'backward')
fit2_summary = summary(fit2)
# Find the model size for best cp, BIC and adjr2
data_frame(
    Cp = which.min(fit2_summary$cp)
    , BIC = which.min(fit2_summary$bic)
    , `Adj R^2` = which.max(fit2_summary$adjr2)
) %>%
    pander(caption = "Backwards Search: Optimal Information Criterion")
Backwards Search: Optimal Information Criterion
Cp BIC Adj R^2
3 3 3
fn_regsubsets_plots(fit2)

Based on the table and plots above for Backwards Search, the best model is

\[Y = \beta_0 + \beta_{1}X^1 + \beta_{2}X^2 + \beta_{3}X^3\]

fit3 = regsubsets(y ~ poly(x, 10, raw = TRUE), data = data_df, nvmax = 10, method = 'forward')
fit3_summary = summary(fit3)
# Find the model size for best cp, BIC and adjr2
data_frame(
    Cp = which.min(fit3_summary$cp)
    , BIC = which.min(fit3_summary$bic)
    , `Adj R^2` = which.max(fit3_summary$adjr2)
) %>%
    pander(caption = "Forward Search: Optimal Information Criterion")
Forward Search: Optimal Information Criterion
Cp BIC Adj R^2
3 3 3
fn_regsubsets_plots(fit3)

Based on the table and plots above for Forwards Search, the best model is

\[Y = \beta_0 + \beta_{1}X^1 + \beta_{2}X^2 + \beta_{3}X^3\]

We see that for the \(\beta\)s chosen, backwards and forwards search produces the same optimal model.

1.5 E

xmat = model.matrix(y ~ poly(x, 10, raw = T), data = data_df)[, -1]
mod.lasso = cv.glmnet(xmat, Y, alpha = 1)
best.lambda = mod.lasso$lambda.min

The optimal value of \(\lambda\) is 0.0681.

fn_plot_cv_glmnet <- function(cv_glmnet, main) {
    data <- 
        tidy(cv_glmnet) %>% as_tibble() %>%
        mutate(log_lambda = log(lambda)) 
    
    data2 <-
        data %>%
        filter(row_number() %% 4 == 0)
    
    data3 <-
        data_frame(
            log_lambda = c(log(cv_glmnet$lambda.min), log(cv_glmnet$lambda.1se))
            , name = c("Min", "1se")
        )
    
    ggplot() +
        geom_errorbar(data = data, aes(x = log_lambda, ymin = conf.low, ymax = conf.high), 
                      colour = pal538['dkgray'][[1]], alpha = 0.6) +
        geom_point(data = data, aes(x = log_lambda, y = estimate), colour = pal538['red'][[1]]) +
        geom_vline(xintercept = log(cv_glmnet$lambda.min), colour = pal538['dkgray'][[1]], alpha = 0.6) +
        geom_vline(xintercept = log(cv_glmnet$lambda.1se), colour = pal538['dkgray'][[1]], alpha = 0.6) + 
        theme_jrf() +
        labs(title = main, x = expression(log(lambda)), y = cv_glmnet$name) +
        geom_text(data = data2, aes(x = log_lambda, y = Inf, label = nzero), vjust = 1, colour = '#3C3C3C',
                  family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica"),
                  size = 2.25) +
        geom_label(data = data3, aes(x = log_lambda, y = Inf, label = name), vjust = 5, colour = '#3C3C3C',
                   family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica"))
}
fn_plot_cv_glmnet(mod.lasso, "Lasso Model on Simulated Data")

best.model <- glmnet(xmat, Y, alpha = 1)
coeffiecients <- predict(best.model, s = best.lambda, type = "coefficients")
coeffiecients_df <-
    data_frame(
    coeffiecient = names(coeffiecients[, 1])
    , estimate = coeffiecients[, 1]
    ) %>%
    mutate(estimate = ifelse(estimate == 0, NA, estimate)) %>%
    mutate(coeffiecient = paste0("$\\beta_{", row_number() - 1, "}$"))
betas_above_3 <- 
    coeffiecients_df %>% 
    filter(!is.na(estimate) & row_number() > 4) %>% 
    select(coeffiecient) %>% 
    unlist() %>% 
    stringr::str_extract("(\\d+)")
    
coeffiecients_df %>%
    pander(missing = "")
coeffiecient estimate
\(\beta_{0}\) 9.993e-01
\(\beta_{1}\) 2.329e+00
\(\beta_{2}\) -3.458e+00
\(\beta_{3}\) 9.076e-01
\(\beta_{4}\) -8.271e-04
\(\beta_{5}\) 6.297e-06
\(\beta_{6}\)
\(\beta_{7}\) 6.832e-05
\(\beta_{8}\)
\(\beta_{9}\) 5.934e-06
\(\beta_{10}\)

Lasso picks \(X_{4}\),\(X_{5}\),\(X_{7}\),\(X_{9}\) over \(X_3\).

1.6 F

We select \(\beta_7 = 7\) and regsubsets with Exhaustive Search.

beta7 = 7
Y2 = beta0 + beta7 * X^7 + eps
data_df2 <- data_frame(y = Y2, x = X)
fit7 = regsubsets(y ~ poly(x, 10, raw = TRUE), data = data_df2, nvmax = 10, method = 'exhaustive')
fn_regsubsets_plots(fit7)

For a model with one predictor, the estimates is very close to the actual coefficients:

\[Y = 0.9428 + 7.0002X^7\]

Now using Lasso,

xmat2 = model.matrix(y ~ poly(x, 10, raw = T), data = data_df2)[, -1]
mod.lasso2 = cv.glmnet(xmat2, Y2, alpha = 1)
best.lambda2 = mod.lasso2$lambda.min
fn_plot_cv_glmnet(mod.lasso2, "Lasso Model on Simulated Data")

best.model2 = glmnet(xmat2, Y, alpha = 1)
coeffiecients2 = predict(best.model2, s = best.lambda2, type = "coefficients")
coeffiecients_df2 <-
    data_frame(
    coeffiecient = names(coeffiecients2[, 1])
    , estimate = coeffiecients2[, 1]
    ) %>%
    mutate(estimate = ifelse(estimate == 0, NA, estimate)) %>%
    mutate(coeffiecient = paste0("$\\beta_{", row_number() - 1, "}$"))
betas_above_3 <- coeffiecients_df2 %>% filter(is.na(estimate) & row_number() > 3) %>% select(coeffiecient) %>% unlist() %>% stringr::str_extract("(\\d+)")
    
coeffiecients_df2 %>%
    pander(missing = "")
coeffiecient estimate
\(\beta_{0}\) -4.307
\(\beta_{1}\)
\(\beta_{2}\)
\(\beta_{3}\)
\(\beta_{4}\)
\(\beta_{5}\)
\(\beta_{6}\)
\(\beta_{7}\)
\(\beta_{8}\)
\(\beta_{9}\)
\(\beta_{10}\)

Lasso only selects the intercept term and not \(X^7\) and the intercept is quite off.

2 Crime Data

2.1 Part 1

The map below shows the mean of the pct.unemployed variable in CrimeData.csv. This metric does not make much sense because it the average of a percent of a subset of communities in the US. This map is only to demonstrate the functionality.

crime_data <- readr::read_csv(paste0(data_dir, "CrimeData.csv"), na = c("","?"))
data("state")
data("usgeojson")
df <- 
    data_frame(State = rownames(state.x77), abb = state.abb) %>% 
    inner_join(
        crime_data %>%
            group_by(state) %>%
            summarise(mean_pct.unemployed = mean(pct.unemployed))
        , by = c("abb" = "state")
    )
highchart() %>%
  hc_title(text = "Mean of Percentage of Unemployed") %>%
  hc_subtitle(text = "Source: CrimeData.csv") %>%
  hc_add_series_map(usgeojson, df, name = "Per Capita Income (1974)",
                    value = "mean_pct.unemployed", joinBy = c("woename", "State"),
                    dataLabels = list(enabled = TRUE,
                                      format = '{point.properties.postalcode}')) %>%
  hc_colorAxis(min = min(df$Income)) %>%
  hc_legend(valueDecimals = 0, valueSuffix = "%") %>%
  hc_tooltip(valuePrefix = "%", valueDecimals = 1) %>%
  hc_mapNavigation(enabled = TRUE) %>%
  hc_credits(enabled = TRUE, text = "Inspired by http://jkunst.com/highcharter/highmaps.html", 
             href = "http://jkunst.com/highcharter/highmaps.html")

The map below shows the population by county from the CrimeData.csv dataset. There are many communities in the dataset that are missing county codes and many counties are missing all the communities. Again, this map is only to demonstrate the functionality.

data("uscountygeojson")
crime_data2 <- 
    crime_data %>%
    filter(county != "?") %>%
    group_by(state, county) %>%
    summarise(population = sum(population)) %>%
    mutate(code = paste0('us-',tolower(state),'-', stringr::str_pad(county, 3, side = "left", pad = "0"))) %>%
    select(state, county, code, population)
n <- 16
dstops <- data.frame(q = 0:n/n, c = rev(substring(viridis(n + 1, option = "B"), 0, 7)))
dstops <- list_parse2(dstops)
highchart() %>% 
  hc_title(text = "Population by County") %>%
  hc_subtitle(text = "Source: CrimeData.csv") %>%
  hc_add_series_map(map = uscountygeojson, df = crime_data2,
                    value = "population", joinBy = c("code", "code"),
                    name = "Population", borderWidth = 0.1) %>% 
  hc_colorAxis(stops = dstops, min = min(crime_data2$population)) %>% 
  hc_legend(layout = "vertical", reversed = TRUE,
            floating = TRUE, align = "right") %>% 
  hc_mapNavigation(enabled = TRUE, align = "right") %>% 
  hc_tooltip(valueDecimals = 0) %>%
  hc_credits(enabled = TRUE, text = "Inspired by http://jkunst.com/highcharter/highmaps.html", 
             href = "http://jkunst.com/highcharter/highmaps.html")

2.2 Part 2

var_names_out <- c("num.urban","other.percap","num.underpov","num.vacant.house","num.murders","num.rapes",
                   "num.robberies","num.assaults","num.burglaries","num.larcenies","num.autothefts","num.arsons")
names_other_crimes <- c("murder.perpop", "rapes.perpop","robberies.perpop","assaults.perpop","nonviolentcrimes.perpop",
                        "burglaries.perpop","larcenies.perpop","autothefts.perpop","arsons.perpop")
data_cafl <- 
    crime_data %>% 
    select(c(2,6:103,121,122,123, 130:147)) %>%
    select(-one_of(c(var_names_out, names_other_crimes))) %>%
    filter(state %in% c("FL","CA")) %>%
    filter(complete.cases(.))

From the CrimeData.csv dataset we extract the data for only the states CA and FL. In addition, we select only variables from following the process used in Lecture 6 and remove missing values. As a result we have 368 observations and 99 variables.

2.2.1 (A)

We perform 10-fold cross-validation for glmnet with \(\alpha\) = 0.99.

x_matrix_flca <- model.matrix(violentcrimes.perpop ~ ., data = data_cafl)[, -1]
y_violent_crimes <- 
    data_cafl %>% 
    select(violentcrimes.perpop) %>% 
    unlist()
fit_cafl_1 <- cv.glmnet(x_matrix_flca, y_violent_crimes, alpha = .99, nfolds = 10)
fn_plot_cv_glmnet(fit_cafl_1, expression("FL & CA Crime Data: ElasticNet "~ alpha ~"= 0.99"))

We see that the mean cross-validated error is minimized when \(\lambda = e^{4.2907} = 73.0182\) and there are 5 non-zero estimates of \(\beta_i\). We will use this with glmnet to create new fit.

fit_cafl_2 <- glmnet(x_matrix_flca, y_violent_crimes, alpha = .99, lambda = fit_cafl_1$lambda.min)
relevent_vars <- 
    tidy(fit_cafl_2) %>% 
    filter(estimate != 0 & term != "(Intercept)")
regsubsets_cafl1 <- 
    regsubsets(as.formula(paste0("violentcrimes.perpop ~ ", paste0(relevent_vars$term, collapse = " + "))),
                                          nvmax = 15, method = "exhaustive", data = data_cafl)
summary(regsubsets_cafl1)$outmat %>% 
    as_tibble() %>% 
    pander(missing = "", split.table = Inf)
race.pctblack pct.kids2parents pct.kids.nvrmarried pct.house.vacant num.in.shelters
*
* *
* * *
* * * *
* * * * *

The table above shows which of the five varaibles would be present in the optimal model at each # of predictors. For example, note that pct.house.vacant would not be present if only 4 variables were used. We create a plot to show the optimal # of predictors based on Cp, BIC, and Adjusted \(R^2\).

fn_regsubsets_plots(regsubsets_cafl1)

The optimal model based ont the BIC criteria uses 4 predictors, but based on Cp the optimal model would include 5 predictors. We will only create a model if the \(p\)-values are less than 0.05. We’ll create this model and note that all variables are significant at the 0.05 level. However, the variable pct.house.vacant is just barely significant.

fit_cafl_3 <- lm(as.formula(paste0("violentcrimes.perpop ~ ", 
                    paste0(summary(regsubsets_cafl1)$obj$xnames[-1], collapse = " + "))), data = data_cafl)
tidy(fit_cafl_3) %>% arrange(p.value) %>% pander()
term estimate std.error statistic p.value
(Intercept) 1991 261.6 7.609 2.411e-13
pct.kids2parents -22.52 3.312 -6.8 4.331e-11
pct.kids.nvrmarried 81.55 12.68 6.43 4.023e-10
race.pctblack 13.5 2.718 4.967 1.05e-06
num.in.shelters 0.1587 0.05364 2.958 0.003299
pct.house.vacant 24.77 10.97 2.259 0.02449

We remove pct.house.vacant, fit another linear model, and see all the variables are signifcant at a 0.01 confidence level.

cafl_terms <- 
    tidy(fit_cafl_3) %>%
    filter(term != "(Intercept)") %>%
    arrange(p.value) %>%
    select(term) %>%
    unlist()
fit_cafl_4 <- lm(as.formula(paste0("violentcrimes.perpop ~ ", paste0(cafl_terms[-5], collapse = " + "))), data = data_cafl)
tidy_cafl_1 <- tidy(fit_cafl_4) %>% arrange(p.value)
tidy_cafl_1 %>% pander()
term estimate std.error statistic p.value
(Intercept) 2002 263.1 7.611 2.366e-13
pct.kids.nvrmarried 89.63 12.24 7.325 1.555e-12
pct.kids2parents -22.5 3.33 -6.756 5.647e-11
race.pctblack 14.27 2.711 5.265 2.41e-07
num.in.shelters 0.1698 0.05371 3.162 0.001702

Though the requirements of the problem state to include variables with \(p\)-values less than 0.05, in order to create a parsimonious linear model we will exclude the variable pct.house.vacant as this does not provide much more explanatory power. The BIC criteria, which has a much harsher complexity penalty than Cp, indicates 4 variables is optimal and we will follow this heuristic. Moreover, we believe in the maxim that simpler models are always better.

The final model is

\[violentcrimes.perpop = 2002.3118 + 89.633pct.kids.nvrmarried + -22.5007pct.kids2parents + 14.2725race.pctblack + \\ 0.1698num.in.shelters\]

Holding all other variables constant in a community,

  • An increase in 1% of families with kids with unmarried parents, increases the number of violent crimes per person by 89.633.
  • An increase in 1% of families with kids and 2 parents, decreases the number of violent crimes per person by -22.5007.
  • An increase in 1% of black residents, increases the number of violent crimes per person by 14.2725.
  • An increase in 1% of the number of shelters, increases the number of violent crimes per person by 0.1698.

2.2.2 (B)

In order to cross-validate lambdas and alphas, we use the caret package which generalizes model training and testing. Two important inputs to the train function are a trainControl and tuneGrid. In the trainControl we specify 10-fold cross-validation, repeated 5 times. As both lambdas and alphas are statistics it is important to use repeated cross-validation in this scenario. In the tuneGrid we specify which alphas and lambdas to attempt. For alphas, we use a sequence between 0 and 1 and for lambdas we use the original lambdas found in part (A).

We then plot the alpha and lambda combinations against the mean-squared error (MSE) and highlight the combination that minimizes the MSE.

glmnetTrControl <- 
    trainControl(
          method = "repeatedCV"
        , number = 10
        , repeats = 5
    )
glmnetGrid <- 
    expand.grid(
          alpha = seq(0, 1, 0.1)
        , lambda = fit_cafl_1$lambda
    )
set.seed(42)
model <- 
    train(
          violentcrimes.perpop ~ .
        , data = data_cafl
        , method = 'glmnet'
        , tuneGrid = glmnetGrid
        , trControl = glmnetTrControl
    )
model_results <- 
    model$results %>% 
        as_tibble() %>%
        mutate(
            log_lambda = log(lambda)
            , alpha = round(alpha, 1)
            , MSE = RMSE^2
        )
minimized <- model_results %>% arrange(RMSE) %>% filter(row_number() == 1)
data_cafl2 <-
    data_cafl %>%
    select(one_of(c("violentcrimes.perpop"), predictors(model)))
x_matrix_flca2 <- model.matrix(violentcrimes.perpop ~ ., data = data_cafl2)[, -1]
glmnet_fit1 <- glmnet(x_matrix_flca2, y_violent_crimes, alpha = model$bestTune$alpha, lambda = model$bestTune$lambda)
tidy_cafl_2 <- tidy(glmnet_fit1)
ggplot() +
theme_jrf() + 
geom_line(data = model_results, aes(x = log_lambda, y = MSE, 
                                    group = as.factor(alpha), colour = as.factor(alpha))) +
geom_point(aes(x = minimized$log_lambda, y = minimized$MSE), colour = pal538['dkgray'][[1]]) +
labs(title = "Cross Validation of Alpha and Lambda", x = expression(log(lambda)), 
     y = "Mean-Squared Error (Repeated Cross-Validation)") +
scale_colour_discrete(guide = guide_legend(title = expression(alpha))) +
geom_segment(aes(x = minimized$log_lambda, xend = minimized$log_lambda, y = 500^2, yend = 390^2), 
             arrow = arrow(length = unit(0.03, "npc")), colour = pal538['dkgray'][[1]], alpha = 0.5) +
geom_label(data = data_frame(x = minimized$log_lambda, y = 500^2, label = "Alpha and Lambda\nthat Minimize MSE"), 
    aes(x = x, y = y, label = label), colour = pal538['dkgray'][[1]], family = "DecimaMonoPro")

This model uses 15 predictors: race.pctblack, pct.farmself.inc, pct.inv.inc, asian.percap, male.pct.divorce, pct.kids2parents, pct.workmom, num.kids.nvrmarried, pct.kids.nvrmarried, pct.english.only, pct.house.occup, pct.house.vacant, med.yr.house.built, pct.house.nophone and num.in.shelters. The optimal parameters are

\[\alpha = 1\] \[\lambda = 26.2414\]

and the equation is

\[violentcrimes.perpop = 4958.2725 + 16.836race.pctblack + -15.8618pct.farmself.inc + -0.8978pct.inv.inc + \\ 0.0026asian.percap + 23.5108male.pct.divorce + -16.8671pct.kids2parents + \\ -4.8738pct.workmom + 8.9613\times 10^{-4}num.kids.nvrmarried + 58.8977pct.kids.nvrmarried + \\ -3.0611pct.english.only + -1.7794pct.house.occup + 11.4984pct.house.vacant + \\ -1.4255med.yr.house.built + 7.1541pct.house.nophone + 0.0849num.in.shelters\]

The prediction error of this model is 149,055.

We then use these predictors in OLS and find the prediction error is 152,690.

fit_cafl_6 <- lm(as.formula(paste0("violentcrimes.perpop ~ ", paste0(predictors(model), collapse = " + "))), 
                 data = data_cafl)
tidy_cafl_3 <- 
    tidy(fit_cafl_6) %>% 
    mutate(isIntercept = ifelse(term == "(Intercept)", 0, 1)) %>% 
    arrange(isIntercept, p.value) %>%
    select(-isIntercept)
tidy_cafl_3 %>% pander()
term estimate std.error statistic p.value
(Intercept) 11813 6352 1.86 0.06376
race.pctblack 22.29 3.428 6.501 2.732e-10
male.pct.divorce 38.96 11.93 3.264 0.001204
asian.percap 0.007932 0.00253 3.136 0.001858
pct.kids2parents -13.03 4.769 -2.733 0.006598
pct.english.only -5.699 2.158 -2.641 0.008647
pct.workmom -8.398 3.696 -2.272 0.02367
pct.house.occup -7.865 3.979 -1.977 0.04886
pct.kids.nvrmarried 33.74 17.98 1.876 0.06146
num.in.shelters 0.1339 0.08088 1.655 0.09878
pct.inv.inc -4.781 3.16 -1.513 0.1312
med.yr.house.built -4.546 3.187 -1.427 0.1546
pct.farmself.inc -60.13 43.85 -1.371 0.1712
pct.house.vacant 12.38 10.94 1.131 0.2588
pct.house.nophone 11.41 10.88 1.049 0.2949
num.kids.nvrmarried 0.001592 0.002584 0.6161 0.5383

We see there are predictors that are not significant at the 0.05 significance level.

\[violentcrimes.perpop = 1.1813\times 10^{4} + 22.2876race.pctblack + 38.961male.pct.divorce + 0.0079asian.percap + \\ -13.0321pct.kids2parents + -5.6991pct.english.only + -8.398pct.workmom + \\ -7.8647pct.house.occup + 33.7405pct.kids.nvrmarried + 0.1339num.in.shelters + \\ -4.7811pct.inv.inc + -4.5459med.yr.house.built + -60.1253pct.farmself.inc + \\ 12.3785pct.house.vacant + 11.4147pct.house.nophone + 0.0016num.kids.nvrmarried\]

For example, holding all other variables constant in a community,

  • An increase in 1% of black residents, increases the number of violent crimes per person by 22.2876.
  • An increase in 1% of males that that are divorced, increases the number of violent crimes per person by 38.961.
  • An increase in 1% of Asian residences, increases the number of violent crimes per person by 38.961.
  • An increase in 1% of families with kids and 2 parents, decreases the number of violent crimes per person by 0.0079.
  • An increase in 1% of English only speakers, decreases the number of violent crimes per person by -13.0321.
  • An increase in 1% of working mothers, decreases the number of violent crimes per person by -5.6991.
  • An increase in 1% of occupied houses, decreases the number of violent crimes per person by -8.398.

This is only the first seven variables, but such statements could be applied to all predictors.

The equation from ElasticNet and OLS are similar in the respect that they contain the same predictors (this is by design), but the coefficient estimates are slightly different.

LS0tCnRpdGxlOiAiU1RBVFM3MDEgSG9tZXdvcmsgMyIKYXV0aG9yOiAiSm9yZGFuIEZhcnJlciIKZGF0ZTogJzIwMTYtMTAtMzAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjc3M6IHN0eWxlLmNzcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCgpGdWxsIHJlcG86IFtodHRwczovL2dpdGh1Yi5jb20vanJmYXJyZXIvc3RhdHM3MDFfaHczL10oaHR0cHM6Ly9naXRodWIuY29tL2pyZmFycmVyL3N0YXRzNzAxX2h3My8pCgpQdWJsaXNoZWQgZmlsZTogW2h0dHBzOi8vanJmYXJyZXIuZ2l0aHViLmlvL3N0YXRzNzAxX2h3My9dKGh0dHBzOi8vanJmYXJyZXIuZ2l0aHViLmlvL3N0YXRzNzAxX2h3My8pCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgU2V0IG9wdGlvbnMgZm9yIHRoZSBybWFya2Rvd24gZmlsZQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgd2lkdGggPSAxMDApCmBgYAoKYGBge3Igc2V0dXAyfQojIFNldCB0aGUgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDQ0KQojIFNldCB0aGUgbG9jYWxlIG9mIHRoZSBzZXNzaW9uIHNvIGxhbmd1YWdlcyBvdGhlciB0aGFuIEVuZ2xpc2ggY2FuIGJlIHVzZWQKaW52aXNpYmxlKFN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsICJlbl9VUy5VVEYtOCIpKQojIFByZXZlbnQgcHJpbnRpbmcgaW4gc2NpZW50aWZpYyBub3RhdGlvbgpvcHRpb25zKGRpZ2l0cyA9IDQsIHdpZHRoID0gMjIwKQoKIyBDcmVhdGUgYSBsb2dnZXIgZnVuY3Rpb24KbG9nZ2VyIDwtIGZ1bmN0aW9uKG1zZywgbGV2ZWwgPSAiaW5mbyIsIGZpbGUgPSBsb2dfZmlsZSkgewogICAgY2F0KHBhc3RlMCgiWyIsIGZvcm1hdChTeXMudGltZSgpLCAiJVktJW0tJWQgJUg6JU06JVMuJU9TIiksICJdWyIsIGxldmVsLCAiXSAiLCBtc2csICJcbiIpLCBmaWxlID0gc3Rkb3V0KCkpCn0KCiMgU2V0IHRoZSBwcm9qZWN0IGRpcmVjdG9yeQpiYXNlX2RpciA8LSAnJwpkYXRhX2RpciA8LSBwYXN0ZTAoYmFzZV9kaXIsICJkYXRhLyIpCmNvZGVfZGlyIDwtIHBhc3RlMChiYXNlX2RpciwgImNvZGUvIikKdml6X2RpciA8LSBwYXN0ZTAoYmFzZV9kaXIsICJ2aXovIikKCmRpci5jcmVhdGUoZGF0YV9kaXIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpkaXIuY3JlYXRlKGNvZGVfZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKZGlyLmNyZWF0ZSh2aXpfZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKYGBgCgpgYGB7ciBMb2FkIFBhY2thZ2VzLCBpbmNsdWRlID0gRkFMU0V9CiMgQ3JlYXRlIGEgZnVuY3Rpb24gdGhhdCB3aWxsIGJlIHVzZWQgdG8gbG9hZC9pbnN0YWxsIHBhY2thZ2VzCmZuX2xvYWRfcGFja2FnZXMgPC0gZnVuY3Rpb24ocCkgewogIGlmICghaXMuZWxlbWVudChwLCBpbnN0YWxsZWQucGFja2FnZXMoKVssMV0pIHx8IChwID09IkRUIiAmJiAhKHBhY2thZ2VWZXJzaW9uKHApID4gIjAuMSIpKSkgewogICAgaWYgKHAgPT0gIkRUIikgewogICAgICBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ3JzdHVkaW8vRFQnKQogICAgfSBlbHNlIHsKICAgICAgaW5zdGFsbC5wYWNrYWdlcyhwLCBkZXAgPSBUUlVFLCByZXBvcyA9ICdodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnJykKICAgIH0KICB9CiAgYSA8LSBzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMocmVxdWlyZShwLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKQogIGlmIChhKSB7CiAgICBsb2dnZXIocGFzdGUwKCJMb2FkZWQgcGFja2FnZSAiLCBwLCAiIHZlcnNpb24gIiwgcGFja2FnZVZlcnNpb24ocCkpKQogIH0gZWxzZSB7CiAgICBsb2dnZXIocGFzdGUwKCJVbmFibGUgdG8gbG9hZCBwYWNrYWdlcyAiLCBwKSkKICB9Cn0KIyBDcmVhdGUgYSB2ZWN0b3Igb2YgcGFja2FnZXMKcGFja2FnZXMgPC0gYygndGlkeXZlcnNlJywnZ2d0aGVtZXMnLCdrbml0cicsJ2V4dHJhZm9udCcsJ2Jyb29tJywnYW9kJywnbGVhcHMnLCdiZXN0Z2xtJywnZ2xtbmV0JywKICAgICAgICAgICAgICAnR0dhbGx5JywncGFuZGVyJywnZGVzY3InLCdwbG90Uk9DJywgJ1JPQ1InLCdwUk9DJywgJ2NvcnJwbG90JywnY2FyZXQnLAogICAgICAgICAgICAgICdoaWdoY2hhcnRlcicsJ3ZpcmlkaXNMaXRlJywnc3RyaW5ncicsJ3JlYWRyJykKIyBVc2UgZnVuY3Rpb24gdG8gbG9hZCB0aGUgcmVxdWlyZWQgcGFja2FnZXMKaW52aXNpYmxlKGxhcHBseShwYWNrYWdlcywgZm5fbG9hZF9wYWNrYWdlcykpCmBgYAoKYGBge3IgSW1wb3J0IEZvbnRzfQojIFRvIHRoZSBmb250IHNlY29uZCBmb250LCBydW4gdGhlIGZvbGxvd2luZyB0d28gbGluZXMgb2YgY29kZSBhbmQgYWRkIG5hbWUgb2YgdXNlciB0byB2ZWN0b3IKIyBzeXN0ZW0ocGFzdGUwKCJjcCAtciAiLHZpel9kaXIsImZvbnRzLy4gfi9MaWJyYXJ5L0ZvbnRzLyIpKSAjIGluc3RhbnRhbmVvdXMKIyBmb250X2ltcG9ydCgpICMgdGFrZXMgYXBwcm94aW1hdGVseSA1LTEwIG1pbgp1c2Vyc192IDwtIGMoIkpvcmRhbiIpCmBgYAoKYGBge3IgQ3JlYXRlIHBhbGV0dGUgYW5kIHRoZW1lfQojIENyZWF0ZSBhIGNvbG9yIHBhbGV0dGUKcGFsNTM4IDwtIGdndGhlbWVzX2RhdGEkZml2ZXRoaXJ0eWVpZ2h0CgojIENyZWF0ZSBhIHRoZW1lIHRvIHVzZSB0aHJvdWdob3V0IHRoZSBhbmFseXNpcwp0aGVtZV9qcmYgPC0gZnVuY3Rpb24oYmFzZV9zaXplID0gOCwgYmFzZV9mYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCAiRGVjaW1hTW9ub1BybyIsICJIZWx2ZXRpY2EiKSkgewogICAgdGhlbWUoCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0YwRjBGMCIsIGNvbG91ciA9ICIjNjA2MDYzIiksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRjBGMEYwIiwgY29sb3VyID0gTkEpLCAKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9ICAgZWxlbWVudF9saW5lKGNvbG91ciA9ICIjRDdEN0Q4IiksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9ICAgZWxlbWVudF9saW5lKGNvbG91ciA9ICIjRDdEN0Q4Iiwgc2l6ZSA9IDAuMjUpLAogICAgICAgIHBhbmVsLm1hcmdpbiA9ICAgICAgIHVuaXQoMC4yNSwgImxpbmVzIiksCiAgICAgICAgcGFuZWwubWFyZ2luLnggPSAgICAgTlVMTCwKICAgICAgICBwYW5lbC5tYXJnaW4ueSA9ICAgICBOVUxMLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIiNBMEEwQTMiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCh2anVzdCA9IDEsIGNvbG91ciA9ICcjM0MzQzNDJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIGNvbG91ciA9ICcjM0MzQzNDJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gaWZlbHNlKFN5cy5pbmZvKClbWyd1c2VyJ11dICVpbiUgdXNlcnNfdiwiRGVjaW1hTW9ub1BybyIsICJIZWx2ZXRpY2EiKSksCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gJ2JvbGQnLCBjb2xvdXIgPSAnIzNDM0MzQycsIGhqdXN0ID0gMCksCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgZmFtaWx5ID0gaWZlbHNlKFN5cy5pbmZvKClbWyd1c2VyJ11dICVpbiUgdXNlcnNfdiwiRGVjaW1hTW9ub1BybyIsICJIZWx2ZXRpY2EiKSksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gaWZlbHNlKFN5cy5pbmZvKClbWyd1c2VyJ11dICVpbiUgdXNlcnNfdiwiRGVjaW1hTW9ub1BybyIsICJIZWx2ZXRpY2EiKSkKICAgICAgICAKICAgICkKfQpgYGAKCiMgSVNMUiBQcm9ibGVtCgojIyBBCgpXZSBjcmVhdGUgMTAwICRYJCBhbmQgJFxlcHNpbG9uJCB2YXJpYWJsZXMgdXNpbmcgYHJub3JtYC4KCmBgYHtyfQpYID0gcm5vcm0oMTAwKQplcHMgPSBybm9ybSgxMDApCmBgYAoKIyMgQgoKV2Ugc2VsZWN0ICRcYmV0YV8wID0gMSQgJFxiZXRhXzEgPSAyJCwgJFxiZXRhXzIgPSAtMS40JCwgJFxiZXRhXzMgPSAwLjYkLgoKYGBge3J9CmJldGEwID0gMQpiZXRhMSA9IDIuNQpiZXRhMiA9IC0zLjQKYmV0YTMgPSAwLjkKWSA9IGJldGEwICsgYmV0YTEgKiBYICsgYmV0YTIgKiBYXjIgKyBiZXRhMyAqIFheMyArIGVwcwpgYGAKCgojIyBDCgpXZSB1c2UgYHJlZ3N1YnNldHNgIHdpdGggZXhoYXVzdGl2ZSBzZWFyY2guCgpgYGB7cn0KZGF0YV9kZiA9IGRhdGEuZnJhbWUoeSA9IFksIHggPSBYKQpgYGAKCmBgYHtyfQpmbl9yZWdzdWJzZXRzX3Bsb3RzIDwtIGZ1bmN0aW9uKGZpdF9vYmosIGVsYm93ID0gTlVMTCkgewoKICAgIHJlZ3N1YnNldHNfc3VtbWFyeSA8LSBzdW1tYXJ5KGZpdF9vYmopCiAgICAKICAgIGcgPC0gCiAgICBkYXRhX2ZyYW1lKAogICAgICAgICAgcHJlZGljdG9ycyA9IDE6bGVuZ3RoKHJlZ3N1YnNldHNfc3VtbWFyeSRjcCkKICAgICAgICAsIGNwID0gcmVnc3Vic2V0c19zdW1tYXJ5JGNwCiAgICAgICAgLCBiaWMgPSByZWdzdWJzZXRzX3N1bW1hcnkkYmljCiAgICAgICAgLCBhZGpyMiA9IHJlZ3N1YnNldHNfc3VtbWFyeSRhZGpyMgogICAgKSAlPiUKICAgICAgICBnYXRoZXIobWV0cmljLCB2YWx1ZSwgLXByZWRpY3RvcnMpICU+JQogICAgICAgIG11dGF0ZShtZXRyaWMgPSBmYWN0b3IobWV0cmljLCBsZXZlbHMgPSBjKCJjcCIsImJpYyIsImFkanIyIikpKSAlPiUKICAgICAgICBnZ3Bsb3QoYWVzKHggPSBwcmVkaWN0b3JzLCB5ID0gdmFsdWUsIGNvbG91ciA9IG1ldHJpYykpICsKICAgICAgICBmYWNldF9ncmlkKG1ldHJpYyB+IC4sIHNjYWxlID0gImZyZWVfeSIsIHN3aXRjaCA9ICJ5IiwgCiAgICAgICAgICAgICAgICAgICBsYWJlbGxlciA9IGdncGxvdDI6OmxhYmVsbGVyKG1ldHJpYyA9IGMoY3AgPSAiQ3AiLCBiaWMgPSAiQklDIiwgYWRqcjIgPSAiQWRqdXN0ZWQgUl4yIikpKSArCiAgICAgICAgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkgKwogICAgICAgIGdlb21fbGFiZWwoZGF0YSA9IGRhdGFfZnJhbWUoCiAgICAgICAgICAgIHByZWRpY3RvcnMgPSBjKHdoaWNoLm1pbihyZWdzdWJzZXRzX3N1bW1hcnkkY3ApLCB3aGljaC5taW4ocmVnc3Vic2V0c19zdW1tYXJ5JGJpYyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoLm1heChyZWdzdWJzZXRzX3N1bW1hcnkkYWRqcjIpKQogICAgICAgICAgICAsIG1ldHJpYyA9IGZhY3RvcihjKCJjcCIsImJpYyIsImFkanIyIiksIGxldmVscyA9IGMoImNwIiwiYmljIiwiYWRqcjIiKSkKICAgICAgICAgICAgLCB2YWx1ZSA9IGMobWluKHJlZ3N1YnNldHNfc3VtbWFyeSRjcCksIG1pbihyZWdzdWJzZXRzX3N1bW1hcnkkYmljKSwgbWF4KHJlZ3N1YnNldHNfc3VtbWFyeSRhZGpyMikpCiAgICAgICAgICAgICwgbGFiZWwgPSBwYXN0ZTAoIk9wdGltYWxcbmQ9IiwgYyh3aGljaC5taW4ocmVnc3Vic2V0c19zdW1tYXJ5JGNwKSwgd2hpY2gubWluKHJlZ3N1YnNldHNfc3VtbWFyeSRiaWMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2gubWF4KHJlZ3N1YnNldHNfc3VtbWFyeSRhZGpyMikpKQogICAgICAgICAgICAsIHZqdXN0ID0gYygtLjUsIC0uNSwgMS4yNSkKICAgICAgICApLCBhZXMoeCA9IHByZWRpY3RvcnMsIHkgPSB2YWx1ZSwgbGFiZWwgPSBsYWJlbCwgdmp1c3QgPSB2anVzdCksIGZhbWlseSA9ICJEZWNpbWFNb25vUHJvIikgKwogICAgICAgIHRoZW1lX2pyZigpICsgCiAgICAgICAgbGFicyh0aXRsZSA9IHBhc3RlMChzdHJpbmdyOjpzdHJfdG9fdGl0bGUocmVnc3Vic2V0c19zdW1tYXJ5JG9iaiRtZXRob2QpLCAiIFNlYXJjaCIpLCAKICAgICAgICAgICAgIHggPSAiIyBvZiBQcmVkaWN0b3JzIiwgeSA9IE5VTEwpICsKICAgICAgICBzY2FsZV9jb2xvdXJfbWFudWFsKGd1aWRlID0gRkFMU0UsIHZhbHVlcyA9IGMocGFsNTM4WydyZWQnXVtbMV1dLCBwYWw1MzhbJ2dyZWVuJ11bWzFdXSwgcGFsNTM4WydibHVlJ11bWzFdXSkpCiAgICAKICAgIGlmICghaXMubnVsbChlbGJvdykpIHsKICAgICAgICBnIDwtIGcgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBlbGJvdywgYWxwaGEgPSAwLjUpICsgCiAgICAgICAgICAgIGdlb21fbGFiZWwoZGF0YSA9IGRhdGFfZnJhbWUoeCA9IGVsYm93LCB5ID0gMzAwLCBtZXRyaWMgPSBmYWN0b3IoYygiY3AiKSwgbGV2ZWxzID0gYygiY3AiLCJiaWMiLCJhZGpyMiIpKSwgCiAgICAgICAgICAgICAgICBsYWJlbCA9ICJFbGJvdyB3aXRoXG4zIHByZWRpY3RvcnMiKSwgYWVzKHg9eCx5PXksbGFiZWw9bGFiZWwpLCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IC0uMSwKICAgICAgICAgICAgICAgZmFtaWx5ID0gIkRlY2ltYU1vbm9Qcm8iKQogICAgfQogICAgCiAgICBwcmludChnKQp9CiAgICAgICAgCmBgYAoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KZml0MSA9IHJlZ3N1YnNldHMoeSB+IHBvbHkoeCwgMTAsIHJhdyA9IFRSVUUpLCBkYXRhID0gZGF0YV9kZiwgbnZtYXggPSAxMCwgbWV0aG9kID0gJ2V4aGF1c3RpdmUnKQpmaXQxX3N1bW1hcnkgPSBzdW1tYXJ5KGZpdDEpCgojIEZpbmQgdGhlIG1vZGVsIHNpemUgZm9yIGJlc3QgY3AsIEJJQyBhbmQgYWRqcjIKZGF0YV9mcmFtZSgKICAgIENwID0gd2hpY2gubWluKGZpdDFfc3VtbWFyeSRjcCkKICAgICwgQklDID0gd2hpY2gubWluKGZpdDFfc3VtbWFyeSRiaWMpCiAgICAsIGBBZGogUl4yYCA9IHdoaWNoLm1heChmaXQxX3N1bW1hcnkkYWRqcjIpCikgJT4lCiAgICBwYW5kZXIoY2FwdGlvbiA9ICJFeGhhdXN0aXZlIFNlYXJjaDogT3B0aW1hbCBJbmZvcm1hdGlvbiBDcml0ZXJpb24iKQpgYGAKCmBgYHtyfQpmbl9yZWdzdWJzZXRzX3Bsb3RzKGZpdDEpCmBgYAoKQmFzZWQgb24gdGhlIHRhYmxlIGFuZCBwbG90cyBhYm92ZSBmb3IgRXhoYXVzdGl2ZSBTZWFyY2gsIHRoZSBiZXN0IG1vZGVsIGlzCgokJGByIHBhc3RlMCgiWSA9ICIscGFzdGUwKGMoIlxcYmV0YV8wIiwgcGFzdGUwKCJcXGJldGFfeyIsMToxMCwifVheIiwxOjEwKSlbZml0MV9zdW1tYXJ5JHdoaWNoW3doaWNoLm1pbihmaXQxX3N1bW1hcnkkY3ApLCBdXSwgY29sbGFwc2UgPSAiICsgIikpYCQkCgoKIyMgRAoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KZml0MiA9IHJlZ3N1YnNldHMoeSB+IHBvbHkoeCwgMTAsIHJhdyA9IFRSVUUpLCBkYXRhID0gZGF0YV9kZiwgbnZtYXggPSAxMCwgbWV0aG9kID0gJ2JhY2t3YXJkJykKZml0Ml9zdW1tYXJ5ID0gc3VtbWFyeShmaXQyKQoKIyBGaW5kIHRoZSBtb2RlbCBzaXplIGZvciBiZXN0IGNwLCBCSUMgYW5kIGFkanIyCmRhdGFfZnJhbWUoCiAgICBDcCA9IHdoaWNoLm1pbihmaXQyX3N1bW1hcnkkY3ApCiAgICAsIEJJQyA9IHdoaWNoLm1pbihmaXQyX3N1bW1hcnkkYmljKQogICAgLCBgQWRqIFJeMmAgPSB3aGljaC5tYXgoZml0Ml9zdW1tYXJ5JGFkanIyKQopICU+JQogICAgcGFuZGVyKGNhcHRpb24gPSAiQmFja3dhcmRzIFNlYXJjaDogT3B0aW1hbCBJbmZvcm1hdGlvbiBDcml0ZXJpb24iKQpgYGAKCmBgYHtyfQpmbl9yZWdzdWJzZXRzX3Bsb3RzKGZpdDIpCmBgYAoKQmFzZWQgb24gdGhlIHRhYmxlIGFuZCBwbG90cyBhYm92ZSBmb3IgQmFja3dhcmRzIFNlYXJjaCwgdGhlIGJlc3QgbW9kZWwgaXMKCiQkYHIgcGFzdGUwKCJZID0gIixwYXN0ZTAoYygiXFxiZXRhXzAiLCBwYXN0ZTAoIlxcYmV0YV97IiwxOjEwLCJ9WF4iLDE6MTApKVtmaXQyX3N1bW1hcnkkd2hpY2hbd2hpY2gubWluKGZpdDJfc3VtbWFyeSRjcCksIF1dLCBjb2xsYXBzZSA9ICIgKyAiKSlgJCQKCmBgYHtyIHJlc3VsdHMgPSAnYXNpcyd9CmZpdDMgPSByZWdzdWJzZXRzKHkgfiBwb2x5KHgsIDEwLCByYXcgPSBUUlVFKSwgZGF0YSA9IGRhdGFfZGYsIG52bWF4ID0gMTAsIG1ldGhvZCA9ICdmb3J3YXJkJykKZml0M19zdW1tYXJ5ID0gc3VtbWFyeShmaXQzKQoKIyBGaW5kIHRoZSBtb2RlbCBzaXplIGZvciBiZXN0IGNwLCBCSUMgYW5kIGFkanIyCmRhdGFfZnJhbWUoCiAgICBDcCA9IHdoaWNoLm1pbihmaXQzX3N1bW1hcnkkY3ApCiAgICAsIEJJQyA9IHdoaWNoLm1pbihmaXQzX3N1bW1hcnkkYmljKQogICAgLCBgQWRqIFJeMmAgPSB3aGljaC5tYXgoZml0M19zdW1tYXJ5JGFkanIyKQopICU+JQogICAgcGFuZGVyKGNhcHRpb24gPSAiRm9yd2FyZCBTZWFyY2g6IE9wdGltYWwgSW5mb3JtYXRpb24gQ3JpdGVyaW9uIikKYGBgCgpgYGB7cn0KZm5fcmVnc3Vic2V0c19wbG90cyhmaXQzKQpgYGAKCkJhc2VkIG9uIHRoZSB0YWJsZSBhbmQgcGxvdHMgYWJvdmUgZm9yIEZvcndhcmRzIFNlYXJjaCwgdGhlIGJlc3QgbW9kZWwgaXMKCiQkYHIgcGFzdGUwKCJZID0gIixwYXN0ZTAoYygiXFxiZXRhXzAiLCBwYXN0ZTAoIlxcYmV0YV97IiwxOjEwLCJ9WF4iLDE6MTApKVtmaXQzX3N1bW1hcnkkd2hpY2hbd2hpY2gubWluKGZpdDNfc3VtbWFyeSRjcCksIF1dLCBjb2xsYXBzZSA9ICIgKyAiKSlgJCQKCgpXZSBzZWUgdGhhdCBmb3IgdGhlICRcYmV0YSRzIGNob3NlbiwgYmFja3dhcmRzIGFuZCBmb3J3YXJkcyBzZWFyY2ggcHJvZHVjZXMgdGhlIHNhbWUgb3B0aW1hbCBtb2RlbC4KCiMjIEUKYGBge3J9CnhtYXQgPSBtb2RlbC5tYXRyaXgoeSB+IHBvbHkoeCwgMTAsIHJhdyA9IFQpLCBkYXRhID0gZGF0YV9kZilbLCAtMV0KbW9kLmxhc3NvID0gY3YuZ2xtbmV0KHhtYXQsIFksIGFscGhhID0gMSkKYmVzdC5sYW1iZGEgPSBtb2QubGFzc28kbGFtYmRhLm1pbgoKYGBgCgpUaGUgb3B0aW1hbCB2YWx1ZSBvZiAkXGxhbWJkYSQgaXMgYHIgYmVzdC5sYW1iZGFgLgoKYGBge3J9CmZuX3Bsb3RfY3ZfZ2xtbmV0IDwtIGZ1bmN0aW9uKGN2X2dsbW5ldCwgbWFpbikgewoKICAgIGRhdGEgPC0gCiAgICAgICAgdGlkeShjdl9nbG1uZXQpICU+JSBhc190aWJibGUoKSAlPiUKICAgICAgICBtdXRhdGUobG9nX2xhbWJkYSA9IGxvZyhsYW1iZGEpKSAKICAgIAogICAgZGF0YTIgPC0KICAgICAgICBkYXRhICU+JQogICAgICAgIGZpbHRlcihyb3dfbnVtYmVyKCkgJSUgNCA9PSAwKQogICAgCiAgICBkYXRhMyA8LQogICAgICAgIGRhdGFfZnJhbWUoCiAgICAgICAgICAgIGxvZ19sYW1iZGEgPSBjKGxvZyhjdl9nbG1uZXQkbGFtYmRhLm1pbiksIGxvZyhjdl9nbG1uZXQkbGFtYmRhLjFzZSkpCiAgICAgICAgICAgICwgbmFtZSA9IGMoIk1pbiIsICIxc2UiKQogICAgICAgICkKICAgIAogICAgZ2dwbG90KCkgKwogICAgICAgIGdlb21fZXJyb3JiYXIoZGF0YSA9IGRhdGEsIGFlcyh4ID0gbG9nX2xhbWJkYSwgeW1pbiA9IGNvbmYubG93LCB5bWF4ID0gY29uZi5oaWdoKSwgCiAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSBwYWw1MzhbJ2RrZ3JheSddW1sxXV0sIGFscGhhID0gMC42KSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gZGF0YSwgYWVzKHggPSBsb2dfbGFtYmRhLCB5ID0gZXN0aW1hdGUpLCBjb2xvdXIgPSBwYWw1MzhbJ3JlZCddW1sxXV0pICsKICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsb2coY3ZfZ2xtbmV0JGxhbWJkYS5taW4pLCBjb2xvdXIgPSBwYWw1MzhbJ2RrZ3JheSddW1sxXV0sIGFscGhhID0gMC42KSArCiAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbG9nKGN2X2dsbW5ldCRsYW1iZGEuMXNlKSwgY29sb3VyID0gcGFsNTM4Wydka2dyYXknXVtbMV1dLCBhbHBoYSA9IDAuNikgKyAKICAgICAgICB0aGVtZV9qcmYoKSArCiAgICAgICAgbGFicyh0aXRsZSA9IG1haW4sIHggPSBleHByZXNzaW9uKGxvZyhsYW1iZGEpKSwgeSA9IGN2X2dsbW5ldCRuYW1lKSArCiAgICAgICAgZ2VvbV90ZXh0KGRhdGEgPSBkYXRhMiwgYWVzKHggPSBsb2dfbGFtYmRhLCB5ID0gSW5mLCBsYWJlbCA9IG56ZXJvKSwgdmp1c3QgPSAxLCBjb2xvdXIgPSAnIzNDM0MzQycsCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGlmZWxzZShTeXMuaW5mbygpW1sndXNlciddXSAlaW4lIHVzZXJzX3YsIkRlY2ltYU1vbm9Qcm8iLCAiSGVsdmV0aWNhIiksCiAgICAgICAgICAgICAgICAgIHNpemUgPSAyLjI1KSArCiAgICAgICAgZ2VvbV9sYWJlbChkYXRhID0gZGF0YTMsIGFlcyh4ID0gbG9nX2xhbWJkYSwgeSA9IEluZiwgbGFiZWwgPSBuYW1lKSwgdmp1c3QgPSA1LCBjb2xvdXIgPSAnIzNDM0MzQycsCiAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpKQp9CmBgYAoKYGBge3J9CmZuX3Bsb3RfY3ZfZ2xtbmV0KG1vZC5sYXNzbywgIkxhc3NvIE1vZGVsIG9uIFNpbXVsYXRlZCBEYXRhIikKYGBgCgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KYmVzdC5tb2RlbCA8LSBnbG1uZXQoeG1hdCwgWSwgYWxwaGEgPSAxKQpjb2VmZmllY2llbnRzIDwtIHByZWRpY3QoYmVzdC5tb2RlbCwgcyA9IGJlc3QubGFtYmRhLCB0eXBlID0gImNvZWZmaWNpZW50cyIpCmNvZWZmaWVjaWVudHNfZGYgPC0KICAgIGRhdGFfZnJhbWUoCiAgICBjb2VmZmllY2llbnQgPSBuYW1lcyhjb2VmZmllY2llbnRzWywgMV0pCiAgICAsIGVzdGltYXRlID0gY29lZmZpZWNpZW50c1ssIDFdCiAgICApICU+JQogICAgbXV0YXRlKGVzdGltYXRlID0gaWZlbHNlKGVzdGltYXRlID09IDAsIE5BLCBlc3RpbWF0ZSkpICU+JQogICAgbXV0YXRlKGNvZWZmaWVjaWVudCA9IHBhc3RlMCgiJFxcYmV0YV97Iiwgcm93X251bWJlcigpIC0gMSwgIn0kIikpCgpiZXRhc19hYm92ZV8zIDwtIAogICAgY29lZmZpZWNpZW50c19kZiAlPiUgCiAgICBmaWx0ZXIoIWlzLm5hKGVzdGltYXRlKSAmIHJvd19udW1iZXIoKSA+IDQpICU+JSAKICAgIHNlbGVjdChjb2VmZmllY2llbnQpICU+JSAKICAgIHVubGlzdCgpICU+JSAKICAgIHN0cmluZ3I6OnN0cl9leHRyYWN0KCIoXFxkKykiKQogICAgCmNvZWZmaWVjaWVudHNfZGYgJT4lCiAgICBwYW5kZXIobWlzc2luZyA9ICIiKQpgYGAKCkxhc3NvIHBpY2tzIGByIHBhc3RlMChwYXN0ZTAoIiRYX3siLGJldGFzX2Fib3ZlXzMsIn0kIiksIGNvbGxhcHNlID0gIiwiKWAgb3ZlciAkWF8zJC4KCiMjIEYKCldlIHNlbGVjdCAkXGJldGFfNyA9IDckIGFuZCByZWdzdWJzZXRzIHdpdGggRXhoYXVzdGl2ZSBTZWFyY2guCgpgYGB7cn0KYmV0YTcgPSA3CgpZMiA9IGJldGEwICsgYmV0YTcgKiBYXjcgKyBlcHMKYGBgCgoKYGBge3J9CmRhdGFfZGYyIDwtIGRhdGFfZnJhbWUoeSA9IFkyLCB4ID0gWCkKZml0NyA9IHJlZ3N1YnNldHMoeSB+IHBvbHkoeCwgMTAsIHJhdyA9IFRSVUUpLCBkYXRhID0gZGF0YV9kZjIsIG52bWF4ID0gMTAsIG1ldGhvZCA9ICdleGhhdXN0aXZlJykKCmZuX3JlZ3N1YnNldHNfcGxvdHMoZml0NykKYGBgCgpGb3IgYSBtb2RlbCB3aXRoIG9uZSBwcmVkaWN0b3IsIHRoZSBlc3RpbWF0ZXMgaXMgdmVyeSBjbG9zZSB0byB0aGUgYWN0dWFsIGNvZWZmaWNpZW50czoKCiQkWSA9IGByIGNvZWYoZml0NywgMSlbWzFdXWAgKyBgciBjb2VmKGZpdDcsIDEpW1syXV1gWF43JCQKCk5vdyB1c2luZyBMYXNzbywKCmBgYHtyfQp4bWF0MiA9IG1vZGVsLm1hdHJpeCh5IH4gcG9seSh4LCAxMCwgcmF3ID0gVCksIGRhdGEgPSBkYXRhX2RmMilbLCAtMV0KbW9kLmxhc3NvMiA9IGN2LmdsbW5ldCh4bWF0MiwgWTIsIGFscGhhID0gMSkKYmVzdC5sYW1iZGEyID0gbW9kLmxhc3NvMiRsYW1iZGEubWluCmZuX3Bsb3RfY3ZfZ2xtbmV0KG1vZC5sYXNzbzIsICJMYXNzbyBNb2RlbCBvbiBTaW11bGF0ZWQgRGF0YSIpCmBgYAoKCmBgYHtyIHJlc3VsdHM9ICdhc2lzJ30KYmVzdC5tb2RlbDIgPSBnbG1uZXQoeG1hdDIsIFksIGFscGhhID0gMSkKY29lZmZpZWNpZW50czIgPSBwcmVkaWN0KGJlc3QubW9kZWwyLCBzID0gYmVzdC5sYW1iZGEyLCB0eXBlID0gImNvZWZmaWNpZW50cyIpCmNvZWZmaWVjaWVudHNfZGYyIDwtCiAgICBkYXRhX2ZyYW1lKAogICAgY29lZmZpZWNpZW50ID0gbmFtZXMoY29lZmZpZWNpZW50czJbLCAxXSkKICAgICwgZXN0aW1hdGUgPSBjb2VmZmllY2llbnRzMlssIDFdCiAgICApICU+JQogICAgbXV0YXRlKGVzdGltYXRlID0gaWZlbHNlKGVzdGltYXRlID09IDAsIE5BLCBlc3RpbWF0ZSkpICU+JQogICAgbXV0YXRlKGNvZWZmaWVjaWVudCA9IHBhc3RlMCgiJFxcYmV0YV97Iiwgcm93X251bWJlcigpIC0gMSwgIn0kIikpCgpiZXRhc19hYm92ZV8zIDwtIGNvZWZmaWVjaWVudHNfZGYyICU+JSBmaWx0ZXIoaXMubmEoZXN0aW1hdGUpICYgcm93X251bWJlcigpID4gMykgJT4lIHNlbGVjdChjb2VmZmllY2llbnQpICU+JSB1bmxpc3QoKSAlPiUgc3RyaW5ncjo6c3RyX2V4dHJhY3QoIihcXGQrKSIpCiAgICAKY29lZmZpZWNpZW50c19kZjIgJT4lCiAgICBwYW5kZXIobWlzc2luZyA9ICIiKQpgYGAKCkxhc3NvIG9ubHkgc2VsZWN0cyB0aGUgaW50ZXJjZXB0IHRlcm0gYW5kIG5vdCAkWF43JCBhbmQgdGhlIGludGVyY2VwdCBpcyBxdWl0ZSBvZmYuCgojIENyaW1lIERhdGEKCiMjIFBhcnQgMQoKVGhlIG1hcCBiZWxvdyBzaG93cyB0aGUgbWVhbiBvZiB0aGUgYHBjdC51bmVtcGxveWVkYCB2YXJpYWJsZSBpbiBgQ3JpbWVEYXRhLmNzdmAuIFRoaXMgbWV0cmljIGRvZXMgbm90IG1ha2UgbXVjaCBzZW5zZSBiZWNhdXNlIGl0IHRoZSBhdmVyYWdlIG9mIGEgcGVyY2VudCBvZiBhIHN1YnNldCBvZiBjb21tdW5pdGllcyBpbiB0aGUgVVMuIFRoaXMgbWFwIGlzIG9ubHkgdG8gZGVtb25zdHJhdGUgdGhlIGZ1bmN0aW9uYWxpdHkuCgpgYGB7cn0KY3JpbWVfZGF0YSA8LSByZWFkcjo6cmVhZF9jc3YocGFzdGUwKGRhdGFfZGlyLCAiQ3JpbWVEYXRhLmNzdiIpLCBuYSA9IGMoIiIsIj8iKSkKCgoKZGF0YSgic3RhdGUiKQpkYXRhKCJ1c2dlb2pzb24iKQoKZGYgPC0gCiAgICBkYXRhX2ZyYW1lKFN0YXRlID0gcm93bmFtZXMoc3RhdGUueDc3KSwgYWJiID0gc3RhdGUuYWJiKSAlPiUgCiAgICBpbm5lcl9qb2luKAogICAgICAgIGNyaW1lX2RhdGEgJT4lCiAgICAgICAgICAgIGdyb3VwX2J5KHN0YXRlKSAlPiUKICAgICAgICAgICAgc3VtbWFyaXNlKG1lYW5fcGN0LnVuZW1wbG95ZWQgPSBtZWFuKHBjdC51bmVtcGxveWVkKSkKICAgICAgICAsIGJ5ID0gYygiYWJiIiA9ICJzdGF0ZSIpCiAgICApCgoKaGlnaGNoYXJ0KCkgJT4lCiAgaGNfdGl0bGUodGV4dCA9ICJNZWFuIG9mIFBlcmNlbnRhZ2Ugb2YgVW5lbXBsb3llZCIpICU+JQogIGhjX3N1YnRpdGxlKHRleHQgPSAiU291cmNlOiBDcmltZURhdGEuY3N2IikgJT4lCiAgaGNfYWRkX3Nlcmllc19tYXAodXNnZW9qc29uLCBkZiwgbmFtZSA9ICJQZXIgQ2FwaXRhIEluY29tZSAoMTk3NCkiLAogICAgICAgICAgICAgICAgICAgIHZhbHVlID0gIm1lYW5fcGN0LnVuZW1wbG95ZWQiLCBqb2luQnkgPSBjKCJ3b2VuYW1lIiwgIlN0YXRlIiksCiAgICAgICAgICAgICAgICAgICAgZGF0YUxhYmVscyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gJ3twb2ludC5wcm9wZXJ0aWVzLnBvc3RhbGNvZGV9JykpICU+JQogIGhjX2NvbG9yQXhpcyhtaW4gPSBtaW4oZGYkSW5jb21lKSkgJT4lCiAgaGNfbGVnZW5kKHZhbHVlRGVjaW1hbHMgPSAwLCB2YWx1ZVN1ZmZpeCA9ICIlIikgJT4lCiAgaGNfdG9vbHRpcCh2YWx1ZVByZWZpeCA9ICIlIiwgdmFsdWVEZWNpbWFscyA9IDEpICU+JQogIGhjX21hcE5hdmlnYXRpb24oZW5hYmxlZCA9IFRSVUUpICU+JQogIGhjX2NyZWRpdHMoZW5hYmxlZCA9IFRSVUUsIHRleHQgPSAiSW5zcGlyZWQgYnkgaHR0cDovL2prdW5zdC5jb20vaGlnaGNoYXJ0ZXIvaGlnaG1hcHMuaHRtbCIsIAogICAgICAgICAgICAgaHJlZiA9ICJodHRwOi8vamt1bnN0LmNvbS9oaWdoY2hhcnRlci9oaWdobWFwcy5odG1sIikKYGBgCgoKVGhlIG1hcCBiZWxvdyBzaG93cyB0aGUgcG9wdWxhdGlvbiBieSBjb3VudHkgZnJvbSB0aGUgYENyaW1lRGF0YS5jc3ZgIGRhdGFzZXQuIFRoZXJlIGFyZSBtYW55IGNvbW11bml0aWVzIGluIHRoZSBkYXRhc2V0IHRoYXQgYXJlIG1pc3NpbmcgY291bnR5IGNvZGVzIGFuZCBtYW55IGNvdW50aWVzIGFyZSBtaXNzaW5nIGFsbCB0aGUgY29tbXVuaXRpZXMuIEFnYWluLCB0aGlzIG1hcCBpcyBvbmx5IHRvIGRlbW9uc3RyYXRlIHRoZSBmdW5jdGlvbmFsaXR5LgoKYGBge3J9CmRhdGEoInVzY291bnR5Z2VvanNvbiIpCgpjcmltZV9kYXRhMiA8LSAKICAgIGNyaW1lX2RhdGEgJT4lCiAgICBmaWx0ZXIoY291bnR5ICE9ICI/IikgJT4lCiAgICBncm91cF9ieShzdGF0ZSwgY291bnR5KSAlPiUKICAgIHN1bW1hcmlzZShwb3B1bGF0aW9uID0gc3VtKHBvcHVsYXRpb24pKSAlPiUKICAgIG11dGF0ZShjb2RlID0gcGFzdGUwKCd1cy0nLHRvbG93ZXIoc3RhdGUpLCctJywgc3RyaW5ncjo6c3RyX3BhZChjb3VudHksIDMsIHNpZGUgPSAibGVmdCIsIHBhZCA9ICIwIikpKSAlPiUKICAgIHNlbGVjdChzdGF0ZSwgY291bnR5LCBjb2RlLCBwb3B1bGF0aW9uKQoKbiA8LSAxNgpkc3RvcHMgPC0gZGF0YS5mcmFtZShxID0gMDpuL24sIGMgPSByZXYoc3Vic3RyaW5nKHZpcmlkaXMobiArIDEsIG9wdGlvbiA9ICJCIiksIDAsIDcpKSkKZHN0b3BzIDwtIGxpc3RfcGFyc2UyKGRzdG9wcykKCmhpZ2hjaGFydCgpICU+JSAKICBoY190aXRsZSh0ZXh0ID0gIlBvcHVsYXRpb24gYnkgQ291bnR5IikgJT4lCiAgaGNfc3VidGl0bGUodGV4dCA9ICJTb3VyY2U6IENyaW1lRGF0YS5jc3YiKSAlPiUKICBoY19hZGRfc2VyaWVzX21hcChtYXAgPSB1c2NvdW50eWdlb2pzb24sIGRmID0gY3JpbWVfZGF0YTIsCiAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAicG9wdWxhdGlvbiIsIGpvaW5CeSA9IGMoImNvZGUiLCAiY29kZSIpLAogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiUG9wdWxhdGlvbiIsIGJvcmRlcldpZHRoID0gMC4xKSAlPiUgCiAgaGNfY29sb3JBeGlzKHN0b3BzID0gZHN0b3BzLCBtaW4gPSBtaW4oY3JpbWVfZGF0YTIkcG9wdWxhdGlvbikpICU+JSAKICBoY19sZWdlbmQobGF5b3V0ID0gInZlcnRpY2FsIiwgcmV2ZXJzZWQgPSBUUlVFLAogICAgICAgICAgICBmbG9hdGluZyA9IFRSVUUsIGFsaWduID0gInJpZ2h0IikgJT4lIAogIGhjX21hcE5hdmlnYXRpb24oZW5hYmxlZCA9IFRSVUUsIGFsaWduID0gInJpZ2h0IikgJT4lIAogIGhjX3Rvb2x0aXAodmFsdWVEZWNpbWFscyA9IDApICU+JQogIGhjX2NyZWRpdHMoZW5hYmxlZCA9IFRSVUUsIHRleHQgPSAiSW5zcGlyZWQgYnkgaHR0cDovL2prdW5zdC5jb20vaGlnaGNoYXJ0ZXIvaGlnaG1hcHMuaHRtbCIsIAogICAgICAgICAgICAgaHJlZiA9ICJodHRwOi8vamt1bnN0LmNvbS9oaWdoY2hhcnRlci9oaWdobWFwcy5odG1sIikKYGBgCgojIyBQYXJ0IDIKCmBgYHtyfQp2YXJfbmFtZXNfb3V0IDwtIGMoIm51bS51cmJhbiIsIm90aGVyLnBlcmNhcCIsIm51bS51bmRlcnBvdiIsIm51bS52YWNhbnQuaG91c2UiLCJudW0ubXVyZGVycyIsIm51bS5yYXBlcyIsCiAgICAgICAgICAgICAgICAgICAibnVtLnJvYmJlcmllcyIsIm51bS5hc3NhdWx0cyIsIm51bS5idXJnbGFyaWVzIiwibnVtLmxhcmNlbmllcyIsIm51bS5hdXRvdGhlZnRzIiwibnVtLmFyc29ucyIpCgpuYW1lc19vdGhlcl9jcmltZXMgPC0gYygibXVyZGVyLnBlcnBvcCIsICJyYXBlcy5wZXJwb3AiLCJyb2JiZXJpZXMucGVycG9wIiwiYXNzYXVsdHMucGVycG9wIiwibm9udmlvbGVudGNyaW1lcy5wZXJwb3AiLAogICAgICAgICAgICAgICAgICAgICAgICAiYnVyZ2xhcmllcy5wZXJwb3AiLCJsYXJjZW5pZXMucGVycG9wIiwiYXV0b3RoZWZ0cy5wZXJwb3AiLCJhcnNvbnMucGVycG9wIikKCmRhdGFfY2FmbCA8LSAKICAgIGNyaW1lX2RhdGEgJT4lIAogICAgc2VsZWN0KGMoMiw2OjEwMywxMjEsMTIyLDEyMywgMTMwOjE0NykpICU+JQogICAgc2VsZWN0KC1vbmVfb2YoYyh2YXJfbmFtZXNfb3V0LCBuYW1lc19vdGhlcl9jcmltZXMpKSkgJT4lCiAgICBmaWx0ZXIoc3RhdGUgJWluJSBjKCJGTCIsIkNBIikpICU+JQogICAgZmlsdGVyKGNvbXBsZXRlLmNhc2VzKC4pKQpgYGAKCkZyb20gdGhlIGBDcmltZURhdGEuY3N2YCBkYXRhc2V0IHdlIGV4dHJhY3QgdGhlIGRhdGEgZm9yIG9ubHkgdGhlIHN0YXRlcyBDQSBhbmQgRkwuIEluIGFkZGl0aW9uLCB3ZSBzZWxlY3Qgb25seSB2YXJpYWJsZXMgZnJvbSBmb2xsb3dpbmcgdGhlIHByb2Nlc3MgdXNlZCBpbiBMZWN0dXJlIDYgYW5kIHJlbW92ZSBtaXNzaW5nIHZhbHVlcy4gQXMgYSByZXN1bHQgd2UgaGF2ZSBgciBucm93KGRhdGFfY2FmbClgIG9ic2VydmF0aW9ucyBhbmQgYHIgbmNvbChkYXRhX2NhZmwpYCB2YXJpYWJsZXMuCgojIyMgKEEpCgpXZSBwZXJmb3JtIDEwLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBmb3IgZ2xtbmV0IHdpdGggJFxhbHBoYSQgPSAwLjk5LiAKCmBgYHtyfQp4X21hdHJpeF9mbGNhIDwtIG1vZGVsLm1hdHJpeCh2aW9sZW50Y3JpbWVzLnBlcnBvcCB+IC4sIGRhdGEgPSBkYXRhX2NhZmwpWywgLTFdCgp5X3Zpb2xlbnRfY3JpbWVzIDwtIAogICAgZGF0YV9jYWZsICU+JSAKICAgIHNlbGVjdCh2aW9sZW50Y3JpbWVzLnBlcnBvcCkgJT4lIAogICAgdW5saXN0KCkKCmZpdF9jYWZsXzEgPC0gY3YuZ2xtbmV0KHhfbWF0cml4X2ZsY2EsIHlfdmlvbGVudF9jcmltZXMsIGFscGhhID0gLjk5LCBuZm9sZHMgPSAxMCkKCmZuX3Bsb3RfY3ZfZ2xtbmV0KGZpdF9jYWZsXzEsIGV4cHJlc3Npb24oIkZMICYgQ0EgQ3JpbWUgRGF0YTogRWxhc3RpY05ldCAifiBhbHBoYSB+Ij0gMC45OSIpKQpgYGAKCldlIHNlZSB0aGF0IHRoZSBtZWFuIGNyb3NzLXZhbGlkYXRlZCBlcnJvciBpcyBtaW5pbWl6ZWQgd2hlbiAkXGxhbWJkYSA9IGVee2ByIGxvZyhmaXRfY2FmbF8xJGxhbWJkYS5taW4pYH0gPSBgciBmaXRfY2FmbF8xJGxhbWJkYS5taW5gJCBhbmQgdGhlcmUgYXJlIGByIHRpZHkoZml0X2NhZmxfMikgJT4lIGZpbHRlcihlc3RpbWF0ZSAhPSAwICYgdGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUgbnJvdygpYCBub24temVybyBlc3RpbWF0ZXMgb2YgJFxiZXRhX2kkLiBXZSB3aWxsIHVzZSB0aGlzIHdpdGggZ2xtbmV0IHRvIGNyZWF0ZSBuZXcgZml0LgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KZml0X2NhZmxfMiA8LSBnbG1uZXQoeF9tYXRyaXhfZmxjYSwgeV92aW9sZW50X2NyaW1lcywgYWxwaGEgPSAuOTksIGxhbWJkYSA9IGZpdF9jYWZsXzEkbGFtYmRhLm1pbikKCnJlbGV2ZW50X3ZhcnMgPC0gCiAgICB0aWR5KGZpdF9jYWZsXzIpICU+JSAKICAgIGZpbHRlcihlc3RpbWF0ZSAhPSAwICYgdGVybSAhPSAiKEludGVyY2VwdCkiKQoKCnJlZ3N1YnNldHNfY2FmbDEgPC0gCiAgICByZWdzdWJzZXRzKGFzLmZvcm11bGEocGFzdGUwKCJ2aW9sZW50Y3JpbWVzLnBlcnBvcCB+ICIsIHBhc3RlMChyZWxldmVudF92YXJzJHRlcm0sIGNvbGxhcHNlID0gIiArICIpKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG52bWF4ID0gMTUsIG1ldGhvZCA9ICJleGhhdXN0aXZlIiwgZGF0YSA9IGRhdGFfY2FmbCkKCnN1bW1hcnkocmVnc3Vic2V0c19jYWZsMSkkb3V0bWF0ICU+JSAKICAgIGFzX3RpYmJsZSgpICU+JSAKICAgIHBhbmRlcihtaXNzaW5nID0gIiIsIHNwbGl0LnRhYmxlID0gSW5mKQpgYGAgICAgCgpUaGUgdGFibGUgYWJvdmUgc2hvd3Mgd2hpY2ggb2YgdGhlIGZpdmUgdmFyYWlibGVzIHdvdWxkIGJlIHByZXNlbnQgaW4gdGhlIG9wdGltYWwgbW9kZWwgYXQgZWFjaCAjIG9mIHByZWRpY3RvcnMuIEZvciBleGFtcGxlLCBub3RlIHRoYXQgYHBjdC5ob3VzZS52YWNhbnRgIHdvdWxkIG5vdCBiZSBwcmVzZW50IGlmIG9ubHkgNCB2YXJpYWJsZXMgd2VyZSB1c2VkLiBXZSBjcmVhdGUgYSBwbG90IHRvIHNob3cgdGhlIG9wdGltYWwgIyBvZiBwcmVkaWN0b3JzIGJhc2VkIG9uIENwLCBCSUMsIGFuZCBBZGp1c3RlZCAkUl4yJC4KCgpgYGB7cn0KZm5fcmVnc3Vic2V0c19wbG90cyhyZWdzdWJzZXRzX2NhZmwxKQpgYGAKClRoZSBvcHRpbWFsIG1vZGVsIGJhc2VkIG9udCB0aGUgQklDIGNyaXRlcmlhIHVzZXMgNCBwcmVkaWN0b3JzLCBidXQgYmFzZWQgb24gQ3AgdGhlIG9wdGltYWwgbW9kZWwgd291bGQgaW5jbHVkZSA1IHByZWRpY3RvcnMuIFdlIHdpbGwgb25seSBjcmVhdGUgYSBtb2RlbCBpZiB0aGUgJHAkLXZhbHVlcyBhcmUgbGVzcyB0aGFuIDAuMDUuIFdlJ2xsIGNyZWF0ZSB0aGlzIG1vZGVsIGFuZCBub3RlIHRoYXQgKiphbGwgdmFyaWFibGVzIGFyZSBzaWduaWZpY2FudCBhdCB0aGUgMC4wNSBsZXZlbCoqLiBIb3dldmVyLCB0aGUgdmFyaWFibGUgYHBjdC5ob3VzZS52YWNhbnRgIGlzIGp1c3QgYmFyZWx5IHNpZ25pZmljYW50LgoKCmBgYHtyIHJlc3VsdHMgPSAnYXNpcyd9CmZpdF9jYWZsXzMgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZTAoInZpb2xlbnRjcmltZXMucGVycG9wIH4gIiwgCiAgICAgICAgICAgICAgICAgICAgcGFzdGUwKHN1bW1hcnkocmVnc3Vic2V0c19jYWZsMSkkb2JqJHhuYW1lc1stMV0sIGNvbGxhcHNlID0gIiArICIpKSksIGRhdGEgPSBkYXRhX2NhZmwpCnRpZHkoZml0X2NhZmxfMykgJT4lIGFycmFuZ2UocC52YWx1ZSkgJT4lIHBhbmRlcigpCmBgYAoKV2UgcmVtb3ZlIGBwY3QuaG91c2UudmFjYW50YCwgZml0IGFub3RoZXIgbGluZWFyIG1vZGVsLCBhbmQgc2VlIGFsbCB0aGUgdmFyaWFibGVzIGFyZSBzaWduaWZjYW50IGF0IGEgMC4wMSBjb25maWRlbmNlIGxldmVsLiAKCmBgYHtyIHJlc3VsdHMgPSAnYXNpcyd9CmNhZmxfdGVybXMgPC0gCiAgICB0aWR5KGZpdF9jYWZsXzMpICU+JQogICAgZmlsdGVyKHRlcm0gIT0gIihJbnRlcmNlcHQpIikgJT4lCiAgICBhcnJhbmdlKHAudmFsdWUpICU+JQogICAgc2VsZWN0KHRlcm0pICU+JQogICAgdW5saXN0KCkKCmZpdF9jYWZsXzQgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZTAoInZpb2xlbnRjcmltZXMucGVycG9wIH4gIiwgcGFzdGUwKGNhZmxfdGVybXNbLTVdLCBjb2xsYXBzZSA9ICIgKyAiKSkpLCBkYXRhID0gZGF0YV9jYWZsKQoKdGlkeV9jYWZsXzEgPC0gdGlkeShmaXRfY2FmbF80KSAlPiUgYXJyYW5nZShwLnZhbHVlKQp0aWR5X2NhZmxfMSAlPiUgcGFuZGVyKCkKYGBgCgpUaG91Z2ggdGhlIHJlcXVpcmVtZW50cyBvZiB0aGUgcHJvYmxlbSBzdGF0ZSB0byBpbmNsdWRlIHZhcmlhYmxlcyB3aXRoICRwJC12YWx1ZXMgbGVzcyB0aGFuIDAuMDUsIGluIG9yZGVyIHRvIGNyZWF0ZSBhIHBhcnNpbW9uaW91cyBsaW5lYXIgbW9kZWwgd2Ugd2lsbCBleGNsdWRlIHRoZSB2YXJpYWJsZSBgcGN0LmhvdXNlLnZhY2FudGAgYXMgdGhpcyBkb2VzIG5vdCBwcm92aWRlIG11Y2ggbW9yZSBleHBsYW5hdG9yeSBwb3dlci4gVGhlIEJJQyBjcml0ZXJpYSwgd2hpY2ggaGFzIGEgbXVjaCBoYXJzaGVyIGNvbXBsZXhpdHkgcGVuYWx0eSB0aGFuIENwLCBpbmRpY2F0ZXMgNCB2YXJpYWJsZXMgaXMgb3B0aW1hbCBhbmQgd2Ugd2lsbCBmb2xsb3cgdGhpcyBoZXVyaXN0aWMuIE1vcmVvdmVyLCB3ZSBiZWxpZXZlIGluIHRoZSBtYXhpbSB0aGF0IHNpbXBsZXIgbW9kZWxzIGFyZSBhbHdheXMgYmV0dGVyLgoKVGhlIGZpbmFsIG1vZGVsIGlzIAoKJCR2aW9sZW50Y3JpbWVzLnBlcnBvcCA9IGByIHRpZHlfY2FmbF8xWzEsIDJdYCArIApgciB0aWR5X2NhZmxfMVsyLCAyXWBgciB0aWR5X2NhZmxfMVsyLCAxXWAgKwpgciB0aWR5X2NhZmxfMVszLCAyXWBgciB0aWR5X2NhZmxfMVszLCAxXWAgKwpgciB0aWR5X2NhZmxfMVs0LCAyXWBgciB0aWR5X2NhZmxfMVs0LCAxXWAgKyBcXApgciB0aWR5X2NhZmxfMVs1LCAyXWBgciB0aWR5X2NhZmxfMVs1LCAxXWAkJAoKCkhvbGRpbmcgYWxsIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudCBpbiBhIGNvbW11bml0eSwKCisgQW4gaW5jcmVhc2UgaW4gMSUgb2YgKipmYW1pbGllcyB3aXRoIGtpZHMgd2l0aCB1bm1hcnJpZWQgcGFyZW50cyoqLCBpbmNyZWFzZXMgdGhlIG51bWJlciBvZiB2aW9sZW50IGNyaW1lcyBwZXIgcGVyc29uIGJ5ICoqYHIgdGlkeV9jYWZsXzFbMiwgMl1gKiouCisgQW4gaW5jcmVhc2UgaW4gMSUgb2YgKipmYW1pbGllcyB3aXRoIGtpZHMgYW5kIDIgcGFyZW50cyoqLCBkZWNyZWFzZXMgdGhlIG51bWJlciBvZiB2aW9sZW50IGNyaW1lcyBwZXIgcGVyc29uIGJ5ICoqYHIgdGlkeV9jYWZsXzFbMywgMl1gKiouCisgQW4gaW5jcmVhc2UgaW4gMSUgb2YgKipibGFjayByZXNpZGVudHMqKiwgaW5jcmVhc2VzIHRoZSBudW1iZXIgb2YgdmlvbGVudCBjcmltZXMgcGVyIHBlcnNvbiBieSAqKmByIHRpZHlfY2FmbF8xWzQsIDJdYCoqLgorIEFuIGluY3JlYXNlIGluIDElIG9mICoqdGhlIG51bWJlciBvZiBzaGVsdGVycyoqLCBpbmNyZWFzZXMgdGhlIG51bWJlciBvZiB2aW9sZW50IGNyaW1lcyBwZXIgcGVyc29uIGJ5ICoqYHIgdGlkeV9jYWZsXzFbNSwgMl1gKiouCgojIyMgKEIpCgpJbiBvcmRlciB0byBjcm9zcy12YWxpZGF0ZSBsYW1iZGFzIGFuZCBhbHBoYXMsIHdlIHVzZSB0aGUgW2NhcmV0XShodHRwOi8vdG9wZXBvLmdpdGh1Yi5pby9jYXJldC9pbmRleC5odG1sKSBwYWNrYWdlIHdoaWNoIGdlbmVyYWxpemVzIG1vZGVsIHRyYWluaW5nIGFuZCB0ZXN0aW5nLiBUd28gaW1wb3J0YW50IGlucHV0cyB0byB0aGUgYHRyYWluYCBmdW5jdGlvbiBhcmUgYSBgdHJhaW5Db250cm9sYCBhbmQgYHR1bmVHcmlkYC4gSW4gdGhlIGB0cmFpbkNvbnRyb2xgIHdlIHNwZWNpZnkgMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uLCByZXBlYXRlZCA1IHRpbWVzLiBBcyBib3RoIGxhbWJkYXMgYW5kIGFscGhhcyBhcmUgc3RhdGlzdGljcyBpdCBpcyBpbXBvcnRhbnQgdG8gdXNlIHJlcGVhdGVkIGNyb3NzLXZhbGlkYXRpb24gaW4gdGhpcyBzY2VuYXJpby4gSW4gdGhlIGB0dW5lR3JpZGAgd2Ugc3BlY2lmeSB3aGljaCBhbHBoYXMgYW5kIGxhbWJkYXMgdG8gYXR0ZW1wdC4gRm9yIGFscGhhcywgd2UgdXNlIGEgc2VxdWVuY2UgYmV0d2VlbiAwIGFuZCAxIGFuZCBmb3IgbGFtYmRhcyB3ZSB1c2UgdGhlIG9yaWdpbmFsIGxhbWJkYXMgZm91bmQgaW4gcGFydCAoQSkuCgpXZSB0aGVuIHBsb3QgdGhlIGFscGhhIGFuZCBsYW1iZGEgY29tYmluYXRpb25zIGFnYWluc3QgdGhlIG1lYW4tc3F1YXJlZCBlcnJvciAoTVNFKSBhbmQgaGlnaGxpZ2h0IHRoZSBjb21iaW5hdGlvbiB0aGF0IG1pbmltaXplcyB0aGUgTVNFLgoKCmBgYHtyfQpnbG1uZXRUckNvbnRyb2wgPC0gCiAgICB0cmFpbkNvbnRyb2woCgkJICBtZXRob2QgPSAicmVwZWF0ZWRDViIKCQksIG51bWJlciA9IDEwCgkJLCByZXBlYXRzID0gNQoJKQoKZ2xtbmV0R3JpZCA8LSAKICAgIGV4cGFuZC5ncmlkKAogICAgICAgICAgYWxwaGEgPSBzZXEoMCwgMSwgMC4xKQogICAgICAgICwgbGFtYmRhID0gZml0X2NhZmxfMSRsYW1iZGEKICAgICkKCnNldC5zZWVkKDQyKQptb2RlbCA8LSAKICAgIHRyYWluKAogICAgICAgICAgdmlvbGVudGNyaW1lcy5wZXJwb3AgfiAuCiAgICAgICAgLCBkYXRhID0gZGF0YV9jYWZsCiAgICAgICAgLCBtZXRob2QgPSAnZ2xtbmV0JwogICAgCSwgdHVuZUdyaWQgPSBnbG1uZXRHcmlkCiAgICAJLCB0ckNvbnRyb2wgPSBnbG1uZXRUckNvbnRyb2wKICAgICkKCm1vZGVsX3Jlc3VsdHMgPC0gCiAgICBtb2RlbCRyZXN1bHRzICU+JSAKICAgICAgICBhc190aWJibGUoKSAlPiUKICAgICAgICBtdXRhdGUoCiAgICAgICAgICAgIGxvZ19sYW1iZGEgPSBsb2cobGFtYmRhKQogICAgICAgICAgICAsIGFscGhhID0gcm91bmQoYWxwaGEsIDEpCiAgICAgICAgICAgICwgTVNFID0gUk1TRV4yCiAgICAgICAgKQoKbWluaW1pemVkIDwtIG1vZGVsX3Jlc3VsdHMgJT4lIGFycmFuZ2UoUk1TRSkgJT4lIGZpbHRlcihyb3dfbnVtYmVyKCkgPT0gMSkKCmRhdGFfY2FmbDIgPC0KICAgIGRhdGFfY2FmbCAlPiUKICAgIHNlbGVjdChvbmVfb2YoYygidmlvbGVudGNyaW1lcy5wZXJwb3AiKSwgcHJlZGljdG9ycyhtb2RlbCkpKQoKeF9tYXRyaXhfZmxjYTIgPC0gbW9kZWwubWF0cml4KHZpb2xlbnRjcmltZXMucGVycG9wIH4gLiwgZGF0YSA9IGRhdGFfY2FmbDIpWywgLTFdCgpnbG1uZXRfZml0MSA8LSBnbG1uZXQoeF9tYXRyaXhfZmxjYTIsIHlfdmlvbGVudF9jcmltZXMsIGFscGhhID0gbW9kZWwkYmVzdFR1bmUkYWxwaGEsIGxhbWJkYSA9IG1vZGVsJGJlc3RUdW5lJGxhbWJkYSkKCnRpZHlfY2FmbF8yIDwtIHRpZHkoZ2xtbmV0X2ZpdDEpCgpnZ3Bsb3QoKSArCnRoZW1lX2pyZigpICsgCmdlb21fbGluZShkYXRhID0gbW9kZWxfcmVzdWx0cywgYWVzKHggPSBsb2dfbGFtYmRhLCB5ID0gTVNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBhcy5mYWN0b3IoYWxwaGEpLCBjb2xvdXIgPSBhcy5mYWN0b3IoYWxwaGEpKSkgKwpnZW9tX3BvaW50KGFlcyh4ID0gbWluaW1pemVkJGxvZ19sYW1iZGEsIHkgPSBtaW5pbWl6ZWQkTVNFKSwgY29sb3VyID0gcGFsNTM4Wydka2dyYXknXVtbMV1dKSArCmxhYnModGl0bGUgPSAiQ3Jvc3MgVmFsaWRhdGlvbiBvZiBBbHBoYSBhbmQgTGFtYmRhIiwgeCA9IGV4cHJlc3Npb24obG9nKGxhbWJkYSkpLCAKICAgICB5ID0gIk1lYW4tU3F1YXJlZCBFcnJvciAoUmVwZWF0ZWQgQ3Jvc3MtVmFsaWRhdGlvbikiKSArCnNjYWxlX2NvbG91cl9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9IGV4cHJlc3Npb24oYWxwaGEpKSkgKwpnZW9tX3NlZ21lbnQoYWVzKHggPSBtaW5pbWl6ZWQkbG9nX2xhbWJkYSwgeGVuZCA9IG1pbmltaXplZCRsb2dfbGFtYmRhLCB5ID0gNTAwXjIsIHllbmQgPSAzOTBeMiksIAogICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMDMsICJucGMiKSksIGNvbG91ciA9IHBhbDUzOFsnZGtncmF5J11bWzFdXSwgYWxwaGEgPSAwLjUpICsKZ2VvbV9sYWJlbChkYXRhID0gZGF0YV9mcmFtZSh4ID0gbWluaW1pemVkJGxvZ19sYW1iZGEsIHkgPSA1MDBeMiwgbGFiZWwgPSAiQWxwaGEgYW5kIExhbWJkYVxudGhhdCBNaW5pbWl6ZSBNU0UiKSwgCiAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsKSwgY29sb3VyID0gcGFsNTM4Wydka2dyYXknXVtbMV1dLCBmYW1pbHkgPSAiRGVjaW1hTW9ub1BybyIpCmBgYAoKVGhpcyBtb2RlbCB1c2VzIGByIGxlbmd0aChwcmVkaWN0b3JzKG1vZGVsKSlgIHByZWRpY3RvcnM6IGByIHByZWRpY3RvcnMobW9kZWwpICU+JSBwYW5kZXIoKWAuIFRoZSBvcHRpbWFsIHBhcmFtZXRlcnMgYXJlCgokJFxhbHBoYSA9IGByIG1vZGVsJGJlc3RUdW5lJGFscGhhYCQkCiQkXGxhbWJkYSA9IGByIG1vZGVsJGJlc3RUdW5lJGxhbWJkYWAkJAoKYW5kIHRoZSBlcXVhdGlvbiBpcyAKCiQkdmlvbGVudGNyaW1lcy5wZXJwb3AgPSBgciB0aWR5X2NhZmxfMlsxLCAzXWAgKyAKYHIgdGlkeV9jYWZsXzJbMiwgM11gYHIgdGlkeV9jYWZsXzJbMiwgMV1gICsKYHIgdGlkeV9jYWZsXzJbMywgM11gYHIgdGlkeV9jYWZsXzJbMywgMV1gICsKYHIgdGlkeV9jYWZsXzJbNCwgM11gYHIgdGlkeV9jYWZsXzJbNCwgMV1gICsgXFwKYHIgdGlkeV9jYWZsXzJbNSwgM11gYHIgdGlkeV9jYWZsXzJbNSwgMV1gICsKYHIgdGlkeV9jYWZsXzJbNiwgM11gYHIgdGlkeV9jYWZsXzJbNiwgMV1gICsKYHIgdGlkeV9jYWZsXzJbNywgM11gYHIgdGlkeV9jYWZsXzJbNywgMV1gICsgXFwKYHIgdGlkeV9jYWZsXzJbOCwgM11gYHIgdGlkeV9jYWZsXzJbOCwgMV1gICsKYHIgdGlkeV9jYWZsXzJbOSwgM11gYHIgdGlkeV9jYWZsXzJbOSwgMV1gICsKYHIgdGlkeV9jYWZsXzJbMTAsIDNdYGByIHRpZHlfY2FmbF8yWzEwLCAxXWAgKyBcXApgciB0aWR5X2NhZmxfMlsxMSwgM11gYHIgdGlkeV9jYWZsXzJbMTEsIDFdYCArCmByIHRpZHlfY2FmbF8yWzEyLCAzXWBgciB0aWR5X2NhZmxfMlsxMiwgMV1gICsKYHIgdGlkeV9jYWZsXzJbMTMsIDNdYGByIHRpZHlfY2FmbF8yWzEzLCAxXWAgKyBcXApgciB0aWR5X2NhZmxfMlsxNCwgM11gYHIgdGlkeV9jYWZsXzJbMTQsIDFdYCArCmByIHRpZHlfY2FmbF8yWzE1LCAzXWBgciB0aWR5X2NhZmxfMlsxNSwgMV1gICsKYHIgdGlkeV9jYWZsXzJbMTYsIDNdYGByIHRpZHlfY2FmbF8yWzE2LCAxXWAkJAoKVGhlIHByZWRpY3Rpb24gZXJyb3Igb2YgdGhpcyBtb2RlbCBpcyAqKmByIHByZXR0eU51bShtaW5pbWl6ZWQkTVNFLCBiaWcubWFyayA9ICcsJylgKiouIAoKV2UgdGhlbiB1c2UgdGhlc2UgcHJlZGljdG9ycyBpbiBPTFMgYW5kIGZpbmQgdGhlIHByZWRpY3Rpb24gZXJyb3IgaXMgKipgciBzZXQuc2VlZCg0Mik7IHByZXR0eU51bShjdi5nbG1uZXQoeF9tYXRyaXhfZmxjYTIsIHlfdmlvbGVudF9jcmltZXMsIGxhbWJkYSA9IGMoMCwgMSkpJGN2bVsxXSwgYmlnLm1hcmsgPSAnLCcpYCoqLgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KZml0X2NhZmxfNiA8LSBsbShhcy5mb3JtdWxhKHBhc3RlMCgidmlvbGVudGNyaW1lcy5wZXJwb3AgfiAiLCBwYXN0ZTAocHJlZGljdG9ycyhtb2RlbCksIGNvbGxhcHNlID0gIiArICIpKSksIAogICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX2NhZmwpCgp0aWR5X2NhZmxfMyA8LSAKICAgIHRpZHkoZml0X2NhZmxfNikgJT4lIAogICAgbXV0YXRlKGlzSW50ZXJjZXB0ID0gaWZlbHNlKHRlcm0gPT0gIihJbnRlcmNlcHQpIiwgMCwgMSkpICU+JSAKICAgIGFycmFuZ2UoaXNJbnRlcmNlcHQsIHAudmFsdWUpICU+JQogICAgc2VsZWN0KC1pc0ludGVyY2VwdCkKCnRpZHlfY2FmbF8zICU+JSBwYW5kZXIoKQpgYGAKCldlIHNlZSB0aGVyZSBhcmUgcHJlZGljdG9ycyB0aGF0IGFyZSBub3Qgc2lnbmlmaWNhbnQgYXQgdGhlIDAuMDUgc2lnbmlmaWNhbmNlIGxldmVsLgoKJCR2aW9sZW50Y3JpbWVzLnBlcnBvcCA9IGByIHRpZHlfY2FmbF8zWzEsIDJdYCArIApgciB0aWR5X2NhZmxfM1syLCAyXWBgciB0aWR5X2NhZmxfM1syLCAxXWAgKwpgciB0aWR5X2NhZmxfM1szLCAyXWBgciB0aWR5X2NhZmxfM1szLCAxXWAgKwpgciB0aWR5X2NhZmxfM1s0LCAyXWBgciB0aWR5X2NhZmxfM1s0LCAxXWAgKyBcXApgciB0aWR5X2NhZmxfM1s1LCAyXWBgciB0aWR5X2NhZmxfM1s1LCAxXWAgKwpgciB0aWR5X2NhZmxfM1s2LCAyXWBgciB0aWR5X2NhZmxfM1s2LCAxXWAgKwpgciB0aWR5X2NhZmxfM1s3LCAyXWBgciB0aWR5X2NhZmxfM1s3LCAxXWAgKyBcXApgciB0aWR5X2NhZmxfM1s4LCAyXWBgciB0aWR5X2NhZmxfM1s4LCAxXWAgKwpgciB0aWR5X2NhZmxfM1s5LCAyXWBgciB0aWR5X2NhZmxfM1s5LCAxXWAgKwpgciB0aWR5X2NhZmxfM1sxMCwgMl1gYHIgdGlkeV9jYWZsXzNbMTAsIDFdYCArIFxcCmByIHRpZHlfY2FmbF8zWzExLCAyXWBgciB0aWR5X2NhZmxfM1sxMSwgMV1gICsKYHIgdGlkeV9jYWZsXzNbMTIsIDJdYGByIHRpZHlfY2FmbF8zWzEyLCAxXWAgKwpgciB0aWR5X2NhZmxfM1sxMywgMl1gYHIgdGlkeV9jYWZsXzNbMTMsIDFdYCArIFxcCmByIHRpZHlfY2FmbF8zWzE0LCAyXWBgciB0aWR5X2NhZmxfM1sxNCwgMV1gICsKYHIgdGlkeV9jYWZsXzNbMTUsIDJdYGByIHRpZHlfY2FmbF8zWzE1LCAxXWAgKwpgciB0aWR5X2NhZmxfM1sxNiwgMl1gYHIgdGlkeV9jYWZsXzNbMTYsIDFdYCQkCgoKRm9yIGV4YW1wbGUsIGhvbGRpbmcgYWxsIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudCBpbiBhIGNvbW11bml0eSwKCisgQW4gaW5jcmVhc2UgaW4gMSUgb2YgKipibGFjayByZXNpZGVudHMqKiwgaW5jcmVhc2VzIHRoZSBudW1iZXIgb2YgdmlvbGVudCBjcmltZXMgcGVyIHBlcnNvbiBieSAqKmByIHRpZHlfY2FmbF8zWzIsIDJdYCoqLgorIEFuIGluY3JlYXNlIGluIDElIG9mICoqbWFsZXMgdGhhdCB0aGF0IGFyZSBkaXZvcmNlZCoqLCBpbmNyZWFzZXMgdGhlIG51bWJlciBvZiB2aW9sZW50IGNyaW1lcyBwZXIgcGVyc29uIGJ5ICoqYHIgdGlkeV9jYWZsXzNbMywgMl1gKiouCisgQW4gaW5jcmVhc2UgaW4gMSUgb2YgKipBc2lhbiByZXNpZGVuY2VzKiosIGluY3JlYXNlcyB0aGUgbnVtYmVyIG9mIHZpb2xlbnQgY3JpbWVzIHBlciBwZXJzb24gYnkgKipgciB0aWR5X2NhZmxfM1szLCAyXWAqKi4KKyBBbiBpbmNyZWFzZSBpbiAxJSBvZiAqKmZhbWlsaWVzIHdpdGgga2lkcyBhbmQgMiBwYXJlbnRzKiosIGRlY3JlYXNlcyB0aGUgbnVtYmVyIG9mIHZpb2xlbnQgY3JpbWVzIHBlciBwZXJzb24gYnkgKipgciB0aWR5X2NhZmxfM1s0LCAyXWAqKi4KKyBBbiBpbmNyZWFzZSBpbiAxJSBvZiAqKkVuZ2xpc2ggb25seSBzcGVha2VycyoqLCBkZWNyZWFzZXMgdGhlIG51bWJlciBvZiB2aW9sZW50IGNyaW1lcyBwZXIgcGVyc29uIGJ5ICoqYHIgdGlkeV9jYWZsXzNbNSwgMl1gKiouCisgQW4gaW5jcmVhc2UgaW4gMSUgb2YgKip3b3JraW5nIG1vdGhlcnMqKiwgZGVjcmVhc2VzIHRoZSBudW1iZXIgb2YgdmlvbGVudCBjcmltZXMgcGVyIHBlcnNvbiBieSAqKmByIHRpZHlfY2FmbF8zWzYsIDJdYCoqLgorIEFuIGluY3JlYXNlIGluIDElIG9mICoqb2NjdXBpZWQgaG91c2VzKiosIGRlY3JlYXNlcyB0aGUgbnVtYmVyIG9mIHZpb2xlbnQgY3JpbWVzIHBlciBwZXJzb24gYnkgKipgciB0aWR5X2NhZmxfM1s3LCAyXWAqKi4KClRoaXMgaXMgb25seSB0aGUgZmlyc3Qgc2V2ZW4gdmFyaWFibGVzLCBidXQgc3VjaCBzdGF0ZW1lbnRzIGNvdWxkIGJlIGFwcGxpZWQgdG8gYWxsIHByZWRpY3RvcnMuCgpUaGUgZXF1YXRpb24gZnJvbSBFbGFzdGljTmV0IGFuZCBPTFMgYXJlIHNpbWlsYXIgaW4gdGhlIHJlc3BlY3QgdGhhdCB0aGV5IGNvbnRhaW4gdGhlIHNhbWUgcHJlZGljdG9ycyAodGhpcyBpcyBieSBkZXNpZ24pLCBidXQgdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlcyBhcmUgc2xpZ2h0bHkgZGlmZmVyZW50LiAKCgoKCg==