set.seed(1) # Set the seed for reproducibility
invisible(Sys.setlocale("LC_ALL", "en_US.UTF-8"))
options(digits = 4, width = 220) # Prevent printing in scientific notation
# 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)
pacman::p_load(tidyverse, ggthemes, stringr, glmnet, forcats, randomForest, zipcode,
              caret, broom, leaps, networkD3, scales, leaflet, rvest, gridExtra, descr,
              pander, knitr, car, rattle, plotROC, rpart, ROCR, caTools, pROC, highcharter)
# 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","rstudio")
# 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.spacing =       unit(0.25, "lines"),
        panel.spacing.x =     NULL,
        panel.spacing.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"))
        
    )
}
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_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)
}
fn_spline_chart <- function(df, numeric_column, groups, yaxis, title, subtitle) {
  
  df2 <- 
    df %>%
        mutate_(.dots = setNames(list(paste0("mean(",numeric_column,")")), c("mean"))) %>%
        group_by_(.dots = lapply(c(groups, "mean"), as.symbol)) %>%
        summarise_(.dots = setNames(list(paste0("mean(",numeric_column,")")), c("group_mean"))) %>%
        ungroup() %>%
        mutate(
            diff = group_mean - mean
            , sign = if_else(diff >= 0, 'pos', 'neg')
            , label = paste0(if_else(sign == "pos", "+", "-"), round(abs(diff), 2))
        ) %>%
        rename_(.dots = setNames(groups, "group")) %>%
        arrange(diff)
        
  overall_mean <- round(unique(df2$mean), 2)
  ymid <-
    df2 %>%
      group_by(sign) %>%
      summarise(ymid = mean(range(diff)))
  ggplot() +
  geom_bar(data = df2, aes(x = fct_inorder(group), y = diff, fill = sign), stat = 'identity') +
  coord_flip() +
  theme_jrf() +
  guides(fill = FALSE) +
  geom_hline(data = data_frame(x = 0), mapping = aes(yintercept=x), color = "black", size = 1.5) +
  scale_fill_manual(values = c('pos' = pal538[['green']], 'neg' = pal538[['red']])) +
  scale_y_continuous(labels = function(x){return(x + overall_mean)}) +
  scale_x_discrete(labels = function(x) str_wrap(x, width = 12)) +
  labs(x = NULL, y = yaxis, title = title, subtitle = subtitle) +
  geom_text(data = df2, aes(x = fct_inorder(group), y = diff, label=label, 
                    hjust = if_else(sign == "pos", if_else(diff < filter(ymid, sign == "pos")$ymid, -0.1, 1.1),
                                if_else(diff > filter(ymid, sign == "neg")$ymid, 1.1, -0.1))), 
              colour="black", angle = 0,
              family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica"))
}
fn_spline_chart_correct <- function(df, groups, yaxis, title, subtitle) {
  
  df2 <- 
    df %>%
        mutate(rate = sum(correct == "Yes") / n()) %>%
        group_by_(.dots = lapply(c(groups, "rate"), as.symbol)) %>%
        summarise_(.dots = setNames(list('sum(correct == "Yes")/n()'), c("group_rate"))) %>%
        ungroup() %>%
        mutate(
            diff = group_rate - rate
            , sign = if_else(diff >= 0, 'pos', 'neg')
            , label = paste0(if_else(sign == "pos", "+", "-"), round(abs(diff), 2))
        ) %>%
        rename_(.dots = setNames(groups, "group")) %>%
        arrange(diff)
        
  overall_rate <- round(unique(df2$rate), 2)
  ymid <-
    df2 %>%
      group_by(sign) %>%
      summarise(ymid = mean(range(diff)))
  ggplot() +
  geom_bar(data = df2, aes(x = fct_inorder(group), y = diff, fill = sign), stat = 'identity') +
  coord_flip() +
  theme_jrf() +
  guides(fill = FALSE) +
  geom_hline(data = data_frame(x = 0), mapping = aes(yintercept=x), color = "black", size = 1.5) +
  scale_fill_manual(values = c('pos' = pal538[['green']], 'neg' = pal538[['red']])) +
  scale_y_continuous(labels = function(x){return(paste0(round(100 * (x + overall_rate), 0), "%"))}) +
  scale_x_discrete(labels = function(x) str_wrap(x, width = 12)) +
  labs(x = NULL, y = yaxis, title = title, subtitle = subtitle) +
  geom_text(data = df2, aes(x = fct_inorder(group), y = diff, label=label, 
                    hjust = if_else(sign == "pos", if_else(diff < filter(ymid, sign == "pos")$ymid, -0.1, 1.1),
                                if_else(diff > filter(ymid, sign == "neg")$ymid, 1.1, -0.1))), 
              colour="black", angle = 0,
              family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica"))
}
fn_auc_pr <- function(predictions, labels) {
    pred <- prediction(predictions, labels)
    perf <- performance(pred, "prec", "rec")
    f <-
        data_frame(
              recall = unlist(perf@x.values) 
            , precision = unlist(perf@y.values)
        ) %>%
        filter(complete.cases(.))
         
    auc <- caTools::trapz(f$recall, f$precision)
    auc
}
fn_plot_df <- function(data, data_type, model_list = list()) {
    if (data_type == "Training") {
        tmp_df <- 
        data_frame(
                `Log. Regression`         = model_list[[1]]$fitted.values 
                , rpart                   = predict(model_list[[2]], type = "prob")[, 2]  
                , `Random Forest`         = predict(model_list[[3]], type = "prob")[, 2] 
                )
    } else {
        tmp_df <- 
        data_frame(
                `Log. Regression`         = predict(model_list[[1]], data, type = "response")
                , `rpart`                 = predict(model_list[[2]], data, type = "prob")[, 2]
                , `Random Forest`         = predict(model_list[[3]], data, type = "prob")[, 2]
                )
    }
    
    data %>% 
        select(correct) %>%
        bind_cols(tmp_df) %>%
        gather(model, predictions, -correct) %>%
        mutate(model = factor(model, 
                              levels = c("Log. Regression","rpart","Random Forest"))) %>%
        mutate(correct = as.integer(correct) - 1)
}
fn_summary <- function(data) {
    data %>%
        mutate(
            fail = ifelse(predictions != correct, 1, 0)
            , response = ifelse(predictions >= .5, 1, 0)
            , fp = ifelse(correct == 0 & response == 1, 1, 0)
            , fn = ifelse(correct == 1 & response == 0, 1, 0)
        ) %>%
        group_by(model) %>%
        summarise(
               MCE = paste0(round(100 * (sum(fp) + sum(fn)) / n(), 2), "%")
            , `AUC ROC` = auc(roc(correct, predictions))[1]
            , `AUC PR` = fn_auc_pr(predictions, correct)
            ) %>%
        mutate(
             `MCE Rank` = dense_rank(`MCE`)
            , `AUC ROC Rank` = dense_rank(desc(`AUC ROC`))
            , `AUC PR Rank` = dense_rank(desc(`AUC PR`))
        ) %>%
        select(model,MCE,`MCE Rank`,`AUC ROC`,`AUC ROC Rank`,`AUC PR`,`AUC PR Rank`)
}
fn_roc_plot <- function(data, data_type) {
    data %>%
    ggplot(aes(d = correct, m = predictions, colour = model, linetype = model)) + 
    geom_roc() + style_roc() + 
    theme_jrf() +
    labs(title = paste0("ROC - ",data_type," Data")) +
    scale_colour_manual("Model", values = c('Log. Regression' = pal538['blue'][[1]], 
                                            'Random Forest' = pal538['red'][[1]],
                                            'rpart' = pal538['green'][[1]]
                                            )) +
    scale_linetype_manual(values = c(rep("solid", 4), rep("dashed", 1))) +
    guides(linetype = FALSE)
}
fn_pr <- function(x, y, data) {
    predictions <- data %>% select_(x)
    labels <- data %>% select_(y)
    pred <- prediction(predictions, labels)
    perf <- performance(pred, "prec", "rec")
    f <-
        data_frame(
              recall = unlist(perf@x.values) 
            , precision = unlist(perf@y.values)
        ) %>%
        filter(complete.cases(.))
    f
}
# Create the ROC PR Plot
fn_pr_plot <- function(data, data_type) {
    data %>%
    group_by(model) %>%
    nest() %>%
    mutate(pr = purrr::map(data, ~ fn_pr('predictions', 'correct', data = .))) %>%
    select(model, pr) %>%
    unnest() %>%
    ggplot(aes(x = recall, y = precision, colour = model, linetype = model)) + 
    geom_line() + 
    theme_jrf() +
    labs(title = paste0("Precision vs Recall - ",data_type," Data"), x = "Recall", y = "Precision") +
    scale_colour_manual("Model", values = c('Log. Regression' = pal538['blue'][[1]], 
                                            'Random Forest' = pal538['red'][[1]],
                                            'rpart' = pal538['green'][[1]]
                                            )) +
    scale_linetype_manual(values = c(rep("solid", 4), rep("dashed", 1))) +
    guides(linetype = FALSE)
}
fn_backwards_elimination <- function(lr_model) {
  
  df <- Anova(lr_model) %>% tidy()
  lr_formula1 <- lr_model$formula
  
  
  while (max(df$p.value) > 0.01) {
    to_remove <- df %>% arrange(desc(p.value)) %>% head(1) %>% select(term) %>% unlist()
    
    lr_formula1 <- update(lr_formula1,  as.formula(paste0("~ . -",to_remove)))
    lr_model <- glm(lr_formula1, data = lr_model$data, family = "binomial")
    df <- Anova(lr_model) %>% tidy()
     
  }
  return(lr_model)
}

1 Executive Summary

During the last US election cycle, there was significant concern about the impact of fake news stories. After the election, Buzzfeed commissioned Ipsos Public Affairs to conduct a large-scale (3,015 respondents) survey to understand how frequently Americans fall victim to fake news stories. In this analysis we use the raw survey data published by Buzzfeed on GitHub to identify if there are demographic or lifestyle characteristics that correlate with the inability to differentiate inaccurate stories from accurate ones. We find that age and the candidate that someone voted for in the 2016 presidential election are best best predictors of whether someone can correctly distinguish between accuracte and inaccurate news headlines.

In the web-app below, the reader seen the headlines used and see how they might have done on such a survey:

2 Survey Background

Each respondent was shown 6 of the headlines list in the table below (3 were fake stories and 3 were real stories). All headlines appear on the internet over the past year.

articles <- read_delim(paste0(data_dir, "articles.txt"), delim = "|")
articles %>%
  mutate(Article = paste0("[", article, "](", url, ")")) %>%
  select(Num = id, Article, Type = type) %>%
  pander(spit.cell = 65, split.table = Inf, caption = "Actual Headlines shown to Respondents",
         justify = c("center", "left","center"))
Actual Headlines shown to Respondents
Num Article Type
1 Pope Francis Shocks World, Endorses Donald Trump for President, Releases Statement Fake
2 Donald Trump Sent His Own Plane to Transport 200 Stranded Marines Fake
3 FBI Agent Suspected in Hillary Email Leaks Found Dead in Apparent Murder-Suicide Fake
4 Donald Trump Protester Speaks Out: “I Was Paid $3,500 to Protest Trump’s Rally” Fake
5 FBI Director Comey Just Put a Trump Sign on His Front Lawn Fake
6 Melania Trump’s Girl-on-Girl Photos From Racy Shoot Revealed Real
7 Barbara Bush: “I Don’t Know How Women Can Vote” for Trump Real
8 Donald Trump Says He’d “Absolutely” Require Muslims to Register Real
9 Trump: “I Will Protect Our LGBTQ Citizens” Real
10 I Ran the CIA. Now I’m Endorsing Hillary Clinton Real
11 Donald Trump on Refusing Presidential Salary: “I’m Not Taking It” Real

For each headline, respondents were asked if they recalled having seen or heard about the headline (possible answers were yes, no, and not sure). When a respondent responded with yes to the recall question, they were asked if they believed the headline to be accurate (possible answers where very accurate, somewhat accurate, not very accurate, and not at all accurate).

Prior to questions regarding the headlines, respondents were asked about their political party affiliation and who they voted for during the 2016 presidential election. After responding to questions about the headlines, respondents were asked about the social media website/apps they used and where they consumed news. In addition to their behavioral survey data, for each respondent the survey includes demographic information such as age, race, sex, income, education. Ipsos used this information to determine a representative sample.

All the data used for this analysis is published in Buzzfeed’s GitHub repository. Specifically, we used

  1. Raw Data
  2. Documenation of Survey Questions
  3. Demographic Distributions

The demographic distributions were used to match the coded data (i.e. 1, 2) to factor data (i.e. Male, Female). This process was quite manual, but the distributions for each demographic characteristic matched the distributions we saw in the raw data so we are confident in our process.

3 Data Cleaning

fn_down_raw_data <- function(df) {
    download.file(df$url, destfile = paste0(data_dir, df$data, ".", df$file_type), mode="wb", quiet = TRUE)
    return(df)
}
data_locations <- 
    data_frame(
          data = c("raw_data","survey_questions","demographic_distributions")
        , file_type = c("csv", "docx","xlsx")
        , url = c("https://github.com/BuzzFeedNews/2016-12-fake-news-survey/blob/master/data/raw-data.csv?raw=true",
                  "https://github.com/BuzzFeedNews/2016-12-fake-news-survey/raw/master/docs/BF%20News%20Accuracy%20Questionnaire%20Data%20Dictionary.1262016.docx",
                  "https://github.com/BuzzFeedNews/2016-12-fake-news-survey/raw/master/docs/ipsos-tables.xlsx")
    ) %>%
    do(fn_down_raw_data(.))
raw_data <- read_csv(paste0(data_dir, "raw_data",".","csv"))
articles <- read_delim(paste0(data_dir, "articles.txt"), delim = "|", progress = FALSE)

The raw dataset contains 3,015 rows - each representing a survey respondent and 418 columns - each representing either demographic data, response data, or internal Ipsos coding. Below is a sample:

raw_data %>%
    head()

We proceed to recode our columns of interest. We find no unexpected missing values. There are missing values in the raw dataset because many of the questions were condition (i.e. if you respond to question 5 that you did not vote you were not asked question 6 regarding who you voted for).

3.1 Demographic

We extract the following cleaned demographic variables:

ipos_labels <- 
  list(
      household_income = c('Under $15K','$15K to less than $20K','$20K to less than $25K',
                           '$25K to less than $30K','$30K to less than $40K','$40K to less than $50K',
                           '$50K to less than $75K','$75K to less than $100K','$100K to less than $150K',
                           '$150K or more')                                                              # Tab 151
    , marital_status   = c("Single","Domestic Partnership","Married","Widowed","Divorced or separated")  # Tab 169
    , education        = c("Grade School","Some High School","Graduated High School","Some College",
                           "Associate's degree (AA, AS, etc.)","Bachelor's degree (BA, BS, etc.)",
                           "Post Graduate Degree")                                                       # Tab 163
    , employment       = c('Employed - full-time','Employed - part-time','Self-Employed','Retired',
                           'Student/Pupil','Military','Homemaker','Currently Unemployed','(Dk/Ns)')      # Tab 172
    , race             = c('White','Black','Asian','Other')                                              # Tab 175
    , hispanic         = c('Yes','No','(Dk/Ns)')                                                         # Tab 178
    , gender           = c("Male","Female")                                                              # Tab 154
    , children         = c('Under 6 only','6-12 Only','13-17 Only','Under 6 and 6-12',
                           'Under 6 and 13-17','6-12 and 13-17','All 3','None Under 18')                 # Tab 166
  )
demographics <- 
  raw_data %>%
    mutate(
        household_income = factor(DP_USHHI2_der, labels = ipos_labels[['household_income']])
      , marital_status = factor(usmar2_der, labels = ipos_labels[['marital_status']])
      , education = factor(usedu3_der, labels = ipos_labels[['education']])
      , employment = factor(EMP01_der, labels = ipos_labels[['employment']])
      , race = factor(USRACE4_der, labels = ipos_labels[['race']])
      , hispanic = factor(USRETH3_der, labels = ipos_labels[['hispanic']])
      , gender = factor(resp_gender, labels = ipos_labels[['gender']])
    ) %>%
    mutate(
        id = ID
      , age = resp_age
      , market_size = factor(HCAL_STDMKTSIZE_Label_US, levels = c("Non metro", "<1M", "1M-4.9M", "5M+"))
      , region = as.factor(HCAL_STDREGION_4CODES_Label_US)
      , state = as.factor(HCAL_REGION1_Label_US)
      , city = HCAL_Region3_Label_US
      , zip_code = HADD_ZipCode_US
    ) %>%
    mutate(
      city = stringr::str_to_title(city)
    ) %>%
    select(
        id
      , gender
      , age
      , race
      , hispanic
      , marital_status
      , employment
      , education
      , household_income
      , region
      , market_size
      , city
      , state
      , zip_code
    )
fn_summary_table <- function(df) {
    
    fn_factor_summary <- function(df, x) {
        
        col_vector <- df %>% select_(x) %>% unlist()
        
        if(is.factor(col_vector)) {
            col_levels <- levels(col_vector) %>% head(5)
            Description <- paste0("*", paste0(col_levels, collapse = "*, *"),"*")
        }  else {
            Description <- ""
        }
        return(Description)
    }
    
    summary_table <-data_frame(Variable = names(df))
    
    summary_table %>%
        rowwise() %>%
        mutate(Description = fn_factor_summary(df, Variable))
}
fn_summary_table(demographics) %>% pander(split.cells = 80, split.table = Inf, justify = c('left', 'left'))
Variable Description
id
gender Male, Female
age
race White, Black, Asian, Other
hispanic Yes, No, (Dk/Ns)
marital_status Single, Domestic Partnership, Married, Widowed, Divorced or separated
employment Employed - full-time, Employed - part-time, Self-Employed, Retired, Student/Pupil
education Grade School, Some High School, Graduated High School, Some College, Associate’s degree (AA, AS, etc.)
household_income Under $15K, $15K to less than $20K, $20K to less than $25K, $25K to less than $30K, $30K to less than $40K
region Midwest, Northeast, South, West
market_size Non metro, <1M, 1M-4.9M, 5M+
city
state Alabama, Alaska, Arizona, Arkansas, California
zip_code

3.2 General Responses

We also clean the responses to general political affiliation questions:

party_responses <-
  raw_data %>%
  mutate(
      party = if_else(DWD1 == 4, 3L, DWD1)
    , party = factor(party, labels = c("Democrat","Republican","Independent/Other"))
    , party_strength = factor(coalesce(DWD2, DWD3), labels = c("Strong","Weak"))
    , sub_party = paste0(if_else(is.na(party_strength), "", paste0(as.character(party_strength), " ")), party)
    , independent_type = factor(DWD4, labels = c("Democrat","Republican","Neither"))
    , sub_party = if_else(!is.na(independent_type), paste0(sub_party, ", Lean ", independent_type), sub_party)
    , sub_party = factor(sub_party, levels = c('Strong Democrat', 'Weak Democrat', 
                                               'Independent/Other, Lean Democrat',
                                               'Independent/Other, Lean Neither', 
                                               'Independent/Other, Lean Republican',
                                               'Weak Republican', 'Strong Republican'))
    , voted = factor(if_else(DWD5 != 4, "No", "Yes"), levels = c("No", "Yes"))
    , candidate = fct_explicit_na(fct_recode(as.factor(DWD6), 
            "Clinton" = "1", "Trump" = "2", "Johnson" = "3", "Stein" = "4", "Other" = "5"), "Did Not Vote")
  ) %>%
  select(id = ID, party, sub_party, voted, candidate)
fn_summary_table(party_responses) %>% pander(split.cells = 80, split.table = Inf, justify = c('center', 'left'))
Variable Description
id
party Democrat, Republican, Independent/Other
sub_party Strong Democrat, Weak Democrat, Independent/Other, Lean Democrat, Independent/Other, Lean Neither, Independent/Other, Lean Republican
voted No, Yes
candidate Clinton, Trump, Johnson, Stein, Other

3.3 Headline Responses

Each time a respondent was shown a question, we have 3 data points:

  1. The order they saw this question (1-6)
  2. Whether they recalled the news story
  3. Whether they thought the news story was accurate

Here we identify if the respondent was correct in their selection of accuracy. We define the each response to (3) as correct if respondents said a true story was Very accurate or Somewhat accurate and a fake story was Not very accurate or Not at all accurate. We define incorrect as the converse.

headline_order_cols <- c('MRK_ORD_LOOPDWD7_DWD8_A_ORD_LOOPDWD7_DWD8','MRK_ORD_LOOPDWD7_DWD8_B_ORD_LOOPDWD7_DWD8',
                         'MRK_ORD_LOOPDWD7_DWD8_C_ORD_LOOPDWD7_DWD8','MRK_ORD_LOOPDWD7_DWD8_D_ORD_LOOPDWD7_DWD8',
                         'MRK_ORD_LOOPDWD7_DWD8_E_ORD_LOOPDWD7_DWD8','MRK_ORD_LOOPDWD7_DWD8_F_ORD_LOOPDWD7_DWD8',
                         'MRK_ORD_LOOPDWD7_DWD8_G_ORD_LOOPDWD7_DWD8','MRK_ORD_LOOPDWD7_DWD8_H_ORD_LOOPDWD7_DWD8',
                         'MRK_ORD_LOOPDWD7_DWD8_I_ORD_LOOPDWD7_DWD8','MRK_ORD_LOOPDWD7_DWD8_J_ORD_LOOPDWD7_DWD8',
                         'MRK_ORD_LOOPDWD7_DWD8_K_ORD_LOOPDWD7_DWD8')
headline_recall_cols <- c('LOOPDWD7_DWD8_A_DWD7','LOOPDWD7_DWD8_B_DWD7','LOOPDWD7_DWD8_C_DWD7','LOOPDWD7_DWD8_D_DWD7',
                          'LOOPDWD7_DWD8_E_DWD7','LOOPDWD7_DWD8_F_DWD7','LOOPDWD7_DWD8_G_DWD7','LOOPDWD7_DWD8_H_DWD7',
                          'LOOPDWD7_DWD8_I_DWD7','LOOPDWD7_DWD8_J_DWD7','LOOPDWD7_DWD8_K_DWD7')
headline_accuracy_cols <- c('LOOPDWD7_DWD8_A_DWD8','LOOPDWD7_DWD8_B_DWD8','LOOPDWD7_DWD8_C_DWD8','LOOPDWD7_DWD8_D_DWD8',
                            'LOOPDWD7_DWD8_E_DWD8','LOOPDWD7_DWD8_F_DWD8','LOOPDWD7_DWD8_G_DWD8','LOOPDWD7_DWD8_H_DWD8',
                            'LOOPDWD7_DWD8_I_DWD8','LOOPDWD7_DWD8_J_DWD8','LOOPDWD7_DWD8_K_DWD8')
fn_rename_question_cols <- function(x, suffix) {
  paste0(str_replace_all(str_extract(x, "_[A-Za-z]_"), "_", ""), "_", suffix)
}
headline_responses <- 
  raw_data %>%
    select(one_of(c("ID", headline_order_cols, headline_recall_cols, headline_accuracy_cols))) %>%
    rename(id = ID) %>%
    rename_(.dots = setNames(headline_order_cols, fn_rename_question_cols(headline_order_cols, "order"))) %>%
    rename_(.dots = setNames(headline_recall_cols, fn_rename_question_cols(headline_recall_cols, "recall"))) %>%
    rename_(.dots = setNames(headline_accuracy_cols, fn_rename_question_cols(headline_accuracy_cols, "accuracy"))) %>%
    gather(question, response, -id, na.rm = TRUE) %>%
    separate(col = question, into = c("question","variable"), sep = "_") %>%
    spread(variable, response) %>%
    mutate(
        recall = factor(recall, levels = c(1,2,3), labels = c("Yes","No","Unsure"))
      , accuracy = factor(accuracy, levels = c(1,2,3, 4), labels = c("Very accurate","Somewhat accurate","Not very accurate","Not at all accurate"))
      , q_id = match(question, LETTERS)
      , response = if_else(is.na(accuracy), NA_character_, if_else(accuracy %in% c("Very accurate","Somewhat accurate"), "Real", "Fake"))
    ) %>%
    left_join(articles %>% select(-url), by = c("q_id" = "id")) %>%
    mutate(correct = factor(type == response, labels = c("No" ,"Yes"))) %>%
    select(id, question, q_id, order, recall, accuracy, article, type, response, correct)
headline_responses %>% head()

3.4 Social Media and News Sources

Respondents were asked how often they used social media sites / apps such as Facebook, Instagram, Pinterest, and Snapchat. They were also given a list of news outlets such as Huffington Post, New York Times, and Buzzfeed and asked whether they used this outlet as a source of news.

fn_news_media <- function(x) {
  mapping <- c("Multiple times a day","Once a day","A few times a week","Once a week","A few times a month",
               "Once a month","Less than once a month","I don’t use this social media platform")
  factor(x, labels = mapping)
}
fn_share <- function(x) {
  mapping <- c("Multiple times a day","Once a day","A few times a week","Once a week","A few times a month",
               "Once a month","Less than once a month","Rarely / never")
  factor(x, labels = mapping)
}
fn_news_source <- function(x) {
  mapping <- c("Is a major source of news for me","Is a minor source of news for me",
               "Is rarely a source of news for me","Is never a source of news for me",
               "I’m not familiar with this news source")
  factor(x, labels = mapping)
}
news_source_responses <- 
  raw_data %>%
    rename(
        Facebook           = GRID_DWD9_1_DWD9
      , Instagram          = GRID_DWD9_2_DWD9
      , Pinterest          = GRID_DWD9_3_DWD9
      , Snapchat           = GRID_DWD9_4_DWD9
      , Twitter            = GRID_DWD9_5_DWD9
      , YouTube            = GRID_DWD9_6_DWD9
      , Share              = DWD10
      , BuzzFeed           = GRID_DWD11_1_DWD11
      , Huffington.Post    = GRID_DWD11_2_DWD11
      , New.York.Times     = GRID_DWD11_3_DWD11
      , Facebook.News      = GRID_DWD11_4_DWD11
      , Twitter.News       = GRID_DWD11_5_DWD11
      , Snapchat.News      = GRID_DWD11_6_DWD11
      , VICE               = GRID_DWD11_7_DWD11
      , CNN                = GRID_DWD11_8_DWD11
      , Vox                = GRID_DWD11_9_DWD11
      , Business.Insider   = GRID_DWD11_10_DWD11
      , Washington.Post    = GRID_DWD11_11_DWD11
      , Google.News        = GRID_DWD11_12_DWD11
      , Yahoo.News         = GRID_DWD11_13_DWD11
      , Drudge.Report      = GRID_DWD11_14_DWD11
      , Fox.News           = GRID_DWD11_15_DWD11
    ) %>%
    select(id = ID, Facebook, Instagram, Pinterest, Snapchat, Twitter, YouTube, Share, BuzzFeed, 
           Huffington.Post, New.York.Times, Facebook.News, Twitter.News, Snapchat.News, VICE, CNN, Vox, 
           Business.Insider, Washington.Post, Google.News, Yahoo.News, Drudge.Report, Fox.News) %>%
    mutate_at(.cols = c("Facebook", "Instagram", "Pinterest", "Snapchat", "Twitter", "YouTube"), fn_news_media) %>%
    mutate_at(.cols = c('Share'), fn_share) %>%
    mutate_at(.cols = c('BuzzFeed','Huffington.Post','New.York.Times','Facebook.News','Twitter.News',
                        'Snapchat.News','VICE','CNN','Vox','Business.Insider','Washington.Post',
                        'Google.News','Yahoo.News','Drudge.Report','Fox.News'), fn_news_source)

3.5 Final Dataset

For a final dataset, we unpivoted the questions so that each record represents a respondent and a headline they were shown. As there are 3,015 respondents, there are 3,015 \(\times\) 6 = 18,090 records in our final dataset. Our final dataset includes 49 columns, many engineered (i.e. correct as in correct identification of a news story as being accurate/inaccurate) from other variables and number are directly from the original dataset.

master_table <-
  headline_responses %>%
  left_join(demographics, by = c("id")) %>%
  left_join(party_responses, by = c("id")) %>%
  left_join(news_source_responses, by = c("id"))
master_table %>%
    head()

4 Exploratory Analysis

4.1 Overview

Before we start learning about the survey respondents, let’s understand the process they went through in news headline component of the survey. The Sankey diagram below helps chart out this path. The green bar at the left represents the 3,015 respondents. The middle section represents the recall. Each person could recall 0-6 of the headlines. The top bar shows that 1,106 people recalled no headlines and thus were not asked to respond to their accuracy. Any person from a blue boxes in the middle was asked about the accuracy of at least one headline. The boxes at the right indicate how many of the headlines the respondent incorrectly responded to (i.e. # wrong). The blue links to the red boxes are the people who answered at least one question incorrectly. Later on in the analysis the people in the blue boxes will be our population of interest in answering the question what factors unlie one’s ability to distinguish an accurate news story from an inaccurate one.

sankey_tmp <- 
  master_table %>%
    select(id, recall, correct) %>%
    group_by(id) %>%
    summarise(
      num_recall = sum(recall == "Yes", na.rm = TRUE)
      , num_wrong = sum(correct == "No", na.rm = TRUE)
    ) %>%
    ungroup() %>%
    group_by(num_recall, num_wrong) %>%
    count() %>% 
    ungroup()
sankey_tmp2 <- 
  union_all(
    sankey_tmp %>%
      group_by(num_recall) %>%
      summarise(n = sum(n)) %>%
      mutate(
        SourceName = "All Respondents"
        , TargetName = paste0("Recalled ", num_recall)
        , LinkGroup = "Good"
      ) %>%
      select(SourceName, TargetName, Value = n, LinkGroup)
    , 
    sankey_tmp %>%
      group_by(num_recall, num_wrong) %>%
      summarise(n = sum(n)) %>%
      ungroup() %>%
      mutate(
        SourceName = paste0("Recalled ", num_recall)
        , TargetName = if_else(num_recall == 0 & num_wrong == 0, 'No Response', paste0(as.character(num_wrong), " wrong"))
        , LinkGroup = if_else(num_wrong > 0, "Bad", "Good")
      ) %>%
      select(SourceName, TargetName, Value = n, LinkGroup)
  )
sankey_nodes <-
  bind_rows(
    select(sankey_tmp2, NodeName = SourceName)
    , select(sankey_tmp2, NodeName = TargetName)
    ) %>% 
  distinct() %>%
  mutate(NodeID = row_number() - 1) %>%
  select(NodeID, NodeName)
sankey_links <-
  sankey_tmp2 %>%
  left_join(
    sankey_nodes, by = c('SourceName' = "NodeName")
  ) %>% 
  rename(Source = NodeID) %>%
  left_join(
    sankey_nodes, by = c('TargetName' = "NodeName")
  ) %>% 
  rename(Target = NodeID) %>%
  select(Source, Target, Value, LinkGroup)
  
  
sankey_nodes <-
  sankey_nodes %>%
  mutate(NodeGroup = 
           ifelse(NodeName %in% c("Recalled 0", "No Response","0 wrong"), "Other", 
             ifelse(str_detect(NodeName, "Recall"), "Recall", 
                  ifelse(str_detect(NodeName, "wrong"), "Wrong", "All"
                      )))
  )
ColourScale <- 'd3.scale.ordinal()
            .domain(["Other", "Recall","Wrong","All"])
           .range(["#D2D2D2", "#008FD5","#FF2700","#77AB43"]);'
sankeyNetwork(Links = sankey_links, Nodes = sankey_nodes, Source = "Source", Target = "Target", Value = "Value", 
              NodeID = "NodeName", LinkGroup = "LinkGroup", units = "people", NodeGroup = "NodeGroup", colourScale = networkD3::JS(ColourScale), width = 700, height = 450,
              fontFamily = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica"), nodeWidth = 35, fontSize = 12)

4.2 Demographics

4.2.1 Age

The only continous numeric variable in our dataset is age. Below is a histogram:

master_table %>%
    select(id, age) %>%
    distinct() %>%
    ggplot(aes(x = age)) + 
    geom_histogram(binwidth = 1, fill = pal538[['blue']]) +
    theme_jrf() +
    labs(x = "Age", y = "# Respondents", title = "Age")

NA

4.2.2 Factor Variables

As the majority of the remaining demographic information are factor variables, we will use treemaps to show what percent of the whole 3,015 respondent pool are in each category.

fn_treemap <- function(df, group, title) {
    
    df2 <- 
        df %>%
        select(one_of(c("id", group))) %>%
        distinct() %>%
        group_by_(.dots = lapply(c(group), as.symbol)) %>%
        count() %>%
        ungroup() %>%
        mutate(percent = round(100 * n / sum(n), 2)) %>%
        rename_(.dots = setNames(group, "group"))
    hchart(df2, "treemap", x = group, y = percent, value = n, color = percent) %>%
        hc_tooltip(pointFormat = "<b>{point.name}</b>: {point.value}</b> (<b>{point.y}%</b>)<br/>") %>%
        hc_title(text = title) %>%
        hc_add_theme(hc_theme_538())
    
}
fn_treemap(master_table, "gender", "Gender")

fn_treemap(master_table, "race", "Race")

fn_treemap(master_table, "hispanic", "Hispanic")

fn_treemap(master_table, "education", "Education")

fn_treemap(master_table, "marital_status", "Marital Status")

fn_treemap(master_table, "employment", "Employment")

fn_treemap(master_table, "household_income", "Household Income")

fn_treemap(master_table, "region", "Region")

fn_treemap(master_table, "market_size", "Market Size")

4.3 Politics Responses

Similar to demographic information, we will look at the composition of the party affiliation and who respondents voted for as treemaps.

df3 <- 
    party_responses %>%
        group_by(party, sub_party) %>%
        count() %>%
        ungroup() %>%
        mutate(y = round(100 * n / sum(n), 2)) %>%
        mutate(color = ifelse(party == "Democrat", pal538[['blue']], 
                              ifelse(party == "Republican", pal538[['red']], pal538[['medgray']]))) %>%
        rename(x = sub_party, value = n)
hchart(df3, "treemap", x = x) %>%
    hc_tooltip(pointFormat = "<b>{point.name}</b>: {point.value}</b> (<b>{point.y}%</b>)<br/>") %>%
    hc_title(text = "Party Affiliation") %>%
    hc_add_theme(hc_theme_538())
df4 <- 
    party_responses %>%
        group_by(candidate) %>%
        count() %>%
        ungroup() %>%
        mutate(y = round(100 * n / sum(n), 2)) %>%
        mutate(color = ifelse(candidate == "Trump", pal538[['red']], 
                              ifelse(candidate == "Clinton", pal538[['blue']], 
                              ifelse(candidate == "Johnson", pal538[['green']], 
                              ifelse(candidate == "Stein", "#551A8B",
                              ifelse(candidate == "Other", pal538[['medgray']],
                              pal538[['dkgray']])))))
                                            
        ) %>%
        rename(x = candidate, value = n)
hchart(df4, "treemap", x = x) %>%
    hc_tooltip(pointFormat = "<b>{point.name}</b>: {point.value}</b> (<b>{point.y}%</b>)<br/>") %>%
    hc_title(text = "Candidate") %>%
    hc_add_theme(hc_theme_538())

4.4 News Sources

Repondents were asked about the different social media services they used and how often. Note how ubiquitous Facebook is - 47% of respondents use it multiple times a day!

master_table %>%
  select(id, candidate, Facebook, Instagram, Pinterest, Snapchat, Twitter, YouTube) %>%
  distinct() %>%
  gather(social_media_outlet, frequency, -id, -candidate) %>%
  group_by(social_media_outlet, frequency) %>%
  count() %>%
  mutate(percent = n / sum(n)) %>%
  ungroup() %>%
  mutate(
    social_media_outlet = factor(social_media_outlet, 
                                      levels = c("Facebook", "Instagram", "Pinterest", "Snapchat", "Twitter", "YouTube"))
    , frequency = factor(frequency, levels = c("Multiple times a day","Once a day","A few times a week","Once a week","A few times a month",
               "Once a month","Less than once a month","I don’t use this social media platform"))
  ) %>%  
  mutate(frequency = fct_recode(frequency, Never = "I don’t use this social media platform")) %>%
  ggplot(aes(x = frequency, y = fct_rev(social_media_outlet))) +
  geom_point(aes(size = percent, colour = percent), stat = "identity", position = "identity", shape = 15) +
  guides(size = FALSE, colour = FALSE) +
  scale_size_continuous(range = c(6,15)) + 
  scale_color_gradient(low = pal538[['medgray']], high = pal538[['red']]) +
  scale_x_discrete(position = "top") +
  theme(axis.text.x = element_text(angle = 25)) +
  geom_text(aes(label = percent(percent)), size = 3, family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica")) + 
  theme_jrf() + 
  labs(x = NULL, y = NULL, title = "How frequently do you visit each of the\nfollowing social media websites / apps?",
       caption = "3,015 respondents") +
  theme(title = element_text(size = 10),
    axis.text = element_text(size = 10), axis.text.x = element_text(hjust = -.05))

4.5 News Sources

Respondents were also asked where they consumed news. The matrix below shows the diversity but also how traditional cable networks still play a big role: 25% of respondents consider Fox News and CNN to be major new sources for them. This is also representative of the age distribution in the sample. Many people say they have never consumed news from Vox, Snapchat, or Twitter - this would not be the case if the population skewed younger.

master_table %>%
  select(id, candidate, BuzzFeed, 
           Huffington.Post,New.York.Times, Facebook.News, Twitter.News, Snapchat.News, VICE, CNN, Vox, 
           Business.Insider, Washington.Post, Google.News, Yahoo.News, Drudge.Report, Fox.News) %>%
  distinct() %>%
  gather(news_source, frequency, -id, -candidate) %>%
  group_by(news_source, frequency) %>%
  count() %>%
  mutate(percent = n / sum(n)) %>%
  ungroup() %>%
  mutate(
    news_source = factor(news_source, 
             levels = c('BuzzFeed','Huffington.Post','New.York.Times','Facebook.News','Twitter.News',
                        'Snapchat.News','VICE','CNN','Vox','Business.Insider','Washington.Post',
                        'Google.News','Yahoo.News','Drudge.Report','Fox.News'),
             labels = c('BuzzFeed','Huffington Post','New York Times','Facebook','Twitter','Snapchat',
                        'VICE','CNN','Vox','Business Insider','Washington Post','Google News','Yahoo News',
                        'Drudge Report','Fox News'))
    , frequency = factor(frequency, levels = c("Is a major source of news for me","Is a minor source of news for me",
               "Is rarely a source of news for me","Is never a source of news for me",
               "I’m not familiar with this news source"), 
               labels = c("Major", "Minor","Rarely","Never","Not Familiar"))
    
  ) %>%  
  #mutate(frequency = fct_recode(frequency, `Rarely / Never` = "I don’t use this social media platform")) %>%
  ggplot(aes(x = frequency, y = fct_rev(news_source))) +
  geom_point(aes(size = percent, colour = percent), stat = "identity", position = "identity", shape = 15) +
  guides(size = FALSE, colour = FALSE) +
  scale_size_continuous(range = c(6,17)) + 
  scale_color_gradient(low = pal538[['medgray']], high = pal538[['red']]) +
  scale_x_discrete(position = "top") +
  #theme(axis.text.x = element_text(angle = 25)) +
  geom_text(aes(label = percent(percent)), size = 4, family = ifelse(Sys.info()[['user']] %in% users_v,"DecimaMonoPro", "Helvetica")) + 
  theme_jrf() + 
  labs(x = NULL, y = NULL, title = "How major or minor of a source it is for you when reading news and current events online?",
       caption = "3,015 respondents") +
  theme(title = element_text(size = 10),
    axis.text = element_text(size = 10))

4.6 Geography

The dataset included zipcode so we were able to map these to show where the 3,015 respondents come from.

data(zipcode)
map_zipcodes <- 
  master_table %>%
    select(id, zip_code, age, race, gender) %>%
    mutate(fill = paste0("Age: ", age, "<br>Race: ", race, "<br>Gender: ", gender)) %>%
    distinct() %>%
    left_join(
          zipcode %>%
                as_tibble() %>%
                select(zip, latitude, longitude)
      , by = c("zip_code" = "zip")
    ) 
leaflet(data = map_zipcodes, width = 700, height= 450) %>% 
  addProviderTiles("CartoDB.Positron") %>%
  addTiles() %>%
  addCircleMarkers(~longitude, ~latitude, popup = ~fill, radius = 3, stroke = FALSE, fillOpacity = 0.8, fillColor = pal538[['green']]) %>%
  setView(lng = -97.11, lat = 37.69, zoom = 3)

Futhermore, we used the new tilegram program created by Google to show each US state in proportion to the number of respondents in the survey. As expected, the most populations states (CA, FL, NY, TX) are most represented here.

state_fips <-
  read_html("http://www.columbia.edu/~sue/state-fips.html") %>% 
  html_node("table") %>% 
  html_table(header = TRUE) %>% 
  rename(fips = `FIPS Code`, state = `State or District`) %>%
  mutate(state = if_else(state == "Geogia", "Georgia", state)) %>%
    inner_join(
        master_table %>%
        select(id, state) %>%
        distinct() %>%
        group_by(state) %>%
        count()
    ) %>%
    select(fips, n) %>%
    mutate(fips = str_pad(fips, 2, "left", "0"))
write_csv(state_fips, path = paste0(data_dir,"state_fips.csv"))
knitr::include_graphics(paste0(viz_dir, "tilegram_respondents.png"))

5 Model Building

Due to how the survey was conducted, if we want to show that there are factors associated with being able to correctly distinguish real and fake news headlines, we need to show that these factors are not the same as those that predict whether one can recall a headline. For example, if being married was associated with recalling headlines and associated with being able to correctly identify real and fake news headlines, we would not be able to say that being married is factor causing proper identification because recall and identification are linked.

What we show in the analysis below is that news sources and social media usage are the determinants underpinning the number of headlines recalled (regardless of real, fake, and all). However, we show that there are a number of other factors associated with proper identification of real and fake headlines, most noteably age and candidate voted for in the 2016 presidential election.

5.1 Recalling Headlines

recall_df <-
    master_table %>%
    group_by(id) %>%
    mutate(
        num_recall_all = sum(recall == "Yes")
        , num_recall_true = sum(recall == "Yes" & type == "Real")
        , num_recall_false = sum(recall == "Yes" & type == "Fake")
    ) %>%
    ungroup() %>%
    select(-question, -q_id, -order, -type, -response, -correct, -article, -recall, -type, -accuracy) %>%
    distinct() %>%
    select(-id, -zip_code, -city)

We will first attempt to build a model to predict whether or not Americans can recall one of the news headlines. We will split this process into:

  1. Identifying any headline
  2. Identifying an inaccurate (false) headline
  3. Identying an accurate (true) headline

We will not be using this for prediction, but rather to understand the factors that correlate with someone’s ability to recall these particular 11 headlines. For that reason, we are not using training and test datasets here. We will be building three models to see if the factors that influence one’s ability to recall a real news headline are the same as the ones that influence the ability to recall a fake news headline. In general, we see that respondents recall 1.37 headlines, but are more likely to recall true headlines than fake headlines (thank goodness).

recall_df %>% 
  summarise(
    All = mean(num_recall_all)
    , Real = mean(num_recall_true)
    , Fake = mean(num_recall_false)
  ) %>%
  gather(Type, `Average Number of Recalls Per Person`) %>%
  pander()
Type Average Number of Recalls Per Person
All 1.3715
Real 0.8687
Fake 0.5028

5.1.1 Recall of Any Headline

We first use a LASSO model for feature selection.

x_matrix_num_recall_all <- model.matrix(num_recall_all ~ . -num_recall_true -num_recall_false, 
                                        data = recall_df)[, -1]
y_num_recall_all <- recall_df %>% select(num_recall_all) %>% unlist()
cv_glmnet_recall_all <- cv.glmnet(x_matrix_num_recall_all, y_num_recall_all, alpha = .99, nfolds = 10)
fn_plot_cv_glmnet(cv_glmnet_recall_all, "Recall Any LASS")

Then we use the 30 variables selected by lambda.1se in regsubsets.

beta_lasso_recall_all <- 
    coef(cv_glmnet_recall_all, s = "lambda.1se") %>% tidy() %>%
    dplyr::rename(term = row) %>%
    filter(term != "(Intercept)") %>%
    mutate(var_name = str_extract(term, paste(names(recall_df), collapse = '|'))) %>%
    mutate(level = str_replace(term, var_name, ""))
regsubsets_fit1 <- regsubsets(as.formula(paste0("num_recall_all ~ ", 
                                                  paste0(unique(beta_lasso_recall_all$var_name), 
                                                             collapse = " + "))), data = recall_df,
                                                              method = "forward", nvmax = 15)
fn_regsubsets_plots(regsubsets_fit1)

There looks to be somewhat of an elbow with 10 variables in the Cp and BIC charts so we will use that to create a linear model to predict the number of headlines recalled.

regsubsets_vars_recall_all <- 
    summary(regsubsets_fit1)$outmat[10, ] %>% 
        as.data.frame() %>% 
        tibble::rownames_to_column(var = "term") %>% 
        filter(`.` == "*") %>%
        filter(term != "(Intercept)") %>%
        mutate(var_name = str_extract(term, paste(names(recall_df), collapse = '|'))) %>%
        mutate(level = str_replace(term, var_name, ""))
fit_recall_all <- lm(as.formula(paste0("num_recall_all ~ ", paste0(unique(regsubsets_vars_recall_all$var_name), 
                                                             collapse = " + "))), data = recall_df)

Using an Anova table, we see that all of the final predictors are statistically significant in predicting the number of headlines a respondent will recall.

Anova(fit_recall_all) %>%
  tidy() %>%
  pander(missing = "", caption = "Anova Table: Linear Model Predicting Number of Headlines Recalled")
Anova Table: Linear Model Predicting Number of Headlines Recalled
term sumsq df statistic p.value
sub_party 171 6 17.45 5.989e-20
Snapchat 24.95 7 2.182 0.03296
Share 155.2 7 13.57 2.259e-17
New.York.Times 98.35 4 15.05 3.473e-12
Vox 127 4 19.43 8.42e-16
Drudge.Report 108.2 4 16.56 1.967e-13
Residuals 4871 2982

Finally, we will use a spine chart to show the effect of each of these variables. On average, respondents recall 1.37 headlines but certain groups of people can identify more or less on average.

f1 <- fn_spline_chart(recall_df, numeric_column = "num_recall_all", groups = "sub_party", 
                yaxis = "Number of Questions Recalled (all)", subtitle = "Number of Headlines Recalled", 
                title = "Party Affiliation")
f2 <-  fn_spline_chart(recall_df, numeric_column = "num_recall_all", groups = "Share", 
                yaxis = "Number of Questions Recalled (all)", subtitle = "Number of Headlines Recalled", 
                title = "Sharing News on Social Media")
f3 <- fn_spline_chart(recall_df, numeric_column = "num_recall_all", groups = "Vox", 
                yaxis = "Number of Questions Recalled (all)", subtitle = "Number of Headlines Recalled", 
                title = "Vox")
f4 <- fn_spline_chart(recall_df, numeric_column = "num_recall_all", groups = "Washington.Post", 
                yaxis = "Number of Questions Recalled (all)", subtitle = "Number of Headlines Recalled", 
                title = "Washington Post")
f5 <- fn_spline_chart(recall_df, numeric_column = "num_recall_all", groups = "Drudge.Report", 
                yaxis = "Number of Questions Recalled (all)", subtitle = "Number of Headlines Recalled", 
                title = "Drudge Report")
f6 <- fn_spline_chart(recall_df, numeric_column = "num_recall_all", groups = "Fox.News", 
                yaxis = "Number of Questions Recalled (all)", subtitle = "Number of Headlines Recalled", 
                title = "Fox News")
gridExtra::grid.arrange(f1, f2, f3, f4, f5, f6, ncol=2)

5.1.2 Recall of a Fake Headline

Now we are predicting the number of fake headlines identified. Again, we start out with a LASSO model for feature selection.

x_matrix_num_recall_false <- model.matrix(num_recall_false ~ . -num_recall_all -num_recall_true, 
                                        data = recall_df)[, -1]
y_num_recall_false <- recall_df %>% select(num_recall_false) %>% unlist()
cv_glmnet_recall_false <- cv.glmnet(x_matrix_num_recall_false, y_num_recall_false, alpha = .99, nfolds = 10)
fn_plot_cv_glmnet(cv_glmnet_recall_false, "Recall Fake Stories LASSO")

Then we use the 28 variables selected by lambda.1se in regsubsets.

beta_lasso_recall_false <- 
    coef(cv_glmnet_recall_false, s = "lambda.1se") %>% tidy() %>%
    dplyr::rename(term = row) %>%
    filter(term != "(Intercept)") %>%
    mutate(var_name = str_extract(term, paste(names(recall_df), collapse = '|'))) %>%
    mutate(level = str_replace(term, var_name, ""))
regsubsets_fit2 <- regsubsets(as.formula(paste0("num_recall_false ~ ", 
                                                  paste0(unique(beta_lasso_recall_false$var_name), 
                                                             collapse = " + "))), data = recall_df,
                                                              method = "forward", nvmax = 15)

Again we use the best 10 variable model.

fn_regsubsets_plots(regsubsets_fit2)

regsubsets_vars_recall_false <- 
    summary(regsubsets_fit2)$outmat[10, ] %>% 
        as.data.frame() %>% 
        tibble::rownames_to_column(var = "term") %>% 
        filter(`.` == "*") %>%
        filter(term != "(Intercept)") %>%
        mutate(var_name = str_extract(term, paste(names(recall_df), collapse = '|'))) %>%
        mutate(level = str_replace(term, var_name, ""))
fit_recall_false <- lm(as.formula(paste0("num_recall_false ~ ",
                paste0(unique(regsubsets_vars_recall_false$var_name), collapse = " + "))), data = recall_df)

Using an Anova table, we see that the predictors are similar to those for all headlines. However, there is now Vice News and no longer the New York Times. Education does not appear to be statistically significant at the 0.05 level.

Anova(fit_recall_false) %>%
  tidy() %>%
  pander(missing = "", caption = "Anova Table: Linear Model Predicting Number of Headlines Recalled (Fake Only)")
Anova Table: Linear Model Predicting Number of Headlines Recalled (Fake Only)
term sumsq df statistic p.value
education 5.048 6 1.669 0.1245
sub_party 37.61 6 12.44 6.967e-14
Snapchat 7.6 7 2.154 0.03534
Share 30.8 7 8.729 1.183e-10
VICE 7.51 4 3.725 0.004987
Vox 25.49 4 12.64 3.354e-10
Washington.Post 11.39 4 5.647 0.0001587
Drudge.Report 23.63 4 11.72 1.908e-09
Fox.News 10.01 4 4.963 0.0005485
Residuals 1496 2968

5.1.3 Recall of a Real Headline

Now we are predicting the number of real headlines identified. Again, we start out with a LASSO model for feature selection.

x_matrix_num_recall_true <- model.matrix(num_recall_true ~ . -num_recall_all -num_recall_false, 
                                        data = recall_df)[, -1]
y_num_recall_true <- recall_df %>% select(num_recall_true) %>% unlist()
cv_glmnet_recall_true <- cv.glmnet(x_matrix_num_recall_true, y_num_recall_true, alpha = .99, nfolds = 10)
fn_plot_cv_glmnet(cv_glmnet_recall_true, "Recall True LASS")

Then we use the 24 variables selected by lambda.1se in regsubsets.

beta_lasso_recall_true <- 
    coef(cv_glmnet_recall_true, s = "lambda.1se") %>% tidy() %>%
    dplyr::rename(term = row) %>%
    filter(term != "(Intercept)") %>%
    mutate(var_name = str_extract(term, paste(names(recall_df), collapse = '|'))) %>%
    mutate(level = str_replace(term, var_name, ""))
regsubsets_fit3 <- regsubsets(as.formula(paste0("num_recall_true ~ ", 
                                                  paste0(unique(beta_lasso_recall_true$var_name), 
                                                             collapse = " + "))), data = recall_df,
                                                              method = "forward", nvmax = 15)

Again we use the best 10 variable model.

fn_regsubsets_plots(regsubsets_fit3)

regsubsets_vars_recall_true <- 
    summary(regsubsets_fit3)$outmat[10, ] %>% 
        as.data.frame() %>% 
        tibble::rownames_to_column(var = "term") %>% 
        filter(`.` == "*") %>%
        filter(term != "(Intercept)") %>%
        mutate(var_name = str_extract(term, paste(names(recall_df), collapse = '|'))) %>%
        mutate(level = str_replace(term, var_name, ""))
fit_recall_true <- lm(as.formula(paste0("num_recall_true ~ ", paste0(unique(regsubsets_vars_recall_true$var_name), 
                                                             collapse = " + "))), data = recall_df)

Using an Anova table, we see that the predictors are nearly the same as the those for all headlines. However, now Twitter has replaced Snapchat, but it is not statistically significant at the 0.01 level anyway.

Anova(fit_recall_false) %>%
  tidy() %>%
  pander(missing = "", caption = "Anova Table: Linear Model Predicting Number of Headlines Recalled (Real Only)")
Anova Table: Linear Model Predicting Number of Headlines Recalled (Real Only)
term sumsq df statistic p.value
education 5.048 6 1.669 0.1245
sub_party 37.61 6 12.44 6.967e-14
Snapchat 7.6 7 2.154 0.03534
Share 30.8 7 8.729 1.183e-10
VICE 7.51 4 3.725 0.004987
Vox 25.49 4 12.64 3.354e-10
Washington.Post 11.39 4 5.647 0.0001587
Drudge.Report 23.63 4 11.72 1.908e-09
Fox.News 10.01 4 4.963 0.0005485
Residuals 1496 2968

We have found (unsurprisingly) that whether your can recall a news headline correlates well with news readership. We note that candidate and age have not appear in any of these 3 model building exercises.

5.2 Identifying Accuracy

5.2.1 Background

Our objective is to create a model to predict whether or not an individual will be able to correctly identify a news headline as accurate or inaccurate (classification). Like the Recall section above, we will break this process into 3 parts

  1. Identification of any headline
  2. Identification of a real headline
  3. Identification of a fake headline

We see from the table below that not all of the questions are created equal. Some are more challenging for Americans to classify as accuracy or inaccurate.

master_table %>% 
  filter(!is.na(correct)) %>% 
  group_by(article, q_id, type, correct) %>% 
  count() %>% 
  ungroup() %>%
  spread(correct, n) %>% 
  mutate(Total = No + Yes) %>% 
  mutate(
    No = paste0(round(100 * No/Total, 2), "%")
    , Yes = paste0(round(100 * Yes/Total, 2), "%")
  ) %>% 
  arrange(q_id) %>%
  select(Num = q_id, Article = article, Type = type, Correct = Yes, Incorrect = No, Total) %>%
  pander(split.cells = 90, split.table = Inf)
Num Article Type Correct Incorrect Total
1 Pope Francis Shocks World, Endorses Donald Trump for President, Releases Statement Fake 37.58% 62.42% 330
2 Donald Trump Sent His Own Plane to Transport 200 Stranded Marines Fake 15.59% 84.41% 263
3 FBI Agent Suspected in Hillary Email Leaks Found Dead in Apparent Murder-Suicide Fake 30.85% 69.15% 389
4 Donald Trump Protester Speaks Out: “I Was Paid $3,500 to Protest Trump’s Rally” Fake 23.56% 76.44% 348
5 FBI Director Comey Just Put a Trump Sign on His Front Lawn Fake 19.35% 80.65% 186
6 Melania Trump’s Girl-on-Girl Photos From Racy Shoot Revealed Real 80% 20% 335
7 Barbara Bush: “I Don’t Know How Women Can Vote” for Trump Real 80.73% 19.27% 358
8 Donald Trump Says He’d “Absolutely” Require Muslims to Register Real 80.08% 19.92% 507
9 Trump: “I Will Protect Our LGBTQ Citizens” Real 72.14% 27.86% 402
10 I Ran the CIA. Now I’m Endorsing Hillary Clinton Real 89.81% 10.19% 157
11 Donald Trump on Refusing Presidential Salary: “I’m Not Taking It” Real 90.23% 9.77% 860

5.2.2 Identification of Any Headline

5.2.2.1 Feature Selection

To create this prediction model, we will treat each response to the question “Is this headline accuracy?” as an observation. We know this violates the assumption of independence because some of these respondents are the same individual. However, we have reason to believe that because not each question is the same, that the answering of each question can be treated as a separate event.

We create a dataset of 36 columns and 35 predictors of the response correct. We then break this dataset into a training (80%) and test (20%) dataset.

accuracy_df <-
    master_table %>%
    filter(!is.na(correct)) %>%
    select(-question, -q_id, -order, -response, -article, -recall, -voted,
           -type, -accuracy, -id, -zip_code, -city, -state)
accuracy_df_train <- sample_frac(accuracy_df, 0.8)
accuracy_df_test <- dplyr::setdiff(accuracy_df, accuracy_df_train)

We first begin by using glment to identify the most important features.

x_matrix_accuracy_all <- model.matrix(correct ~ ., 
                                        data = accuracy_df_train)[, -1]
y_num_accuracy_all <- accuracy_df_train %>% select(correct) %>% unlist()
cv_glmnet_accuracy_all <- cv.glmnet(x_matrix_accuracy_all, y_num_accuracy_all, 
                                  family = "binomial", alpha = .99, nfolds = 10)
fn_plot_cv_glmnet(cv_glmnet_accuracy_all, "Accuracy Any LASSO")

beta_lasso_accuracy_all <- 
    coef(cv_glmnet_accuracy_all, s = "lambda.min") %>% tidy() %>%
    dplyr::rename(term = row) %>%
    filter(term != "(Intercept)") %>%
    mutate(var_name = str_extract(term, paste(names(accuracy_df), collapse = '|'))) %>%
    mutate(level = str_replace(term, var_name, ""))

5.2.2.2 Logistic Regression

We use the 10 variables selected by lambda.min in the chart above and fit a logistic regression model. We then use Backwards Elimination so that only predictors that are signicant at the 0.01 signifance level are left to create final logistic regression model.

lr_formula <- as.formula(paste0("correct ~ ", paste(unique(beta_lasso_accuracy_all$var_name), collapse = " + ")))
lr_fit1 <- glm(lr_formula, data = accuracy_df_train, family = "binomial")
lr_fit2 <- fn_backwards_elimination(lr_fit1)
y_actual <- as.numeric(accuracy_df_train$correct == "Yes")
y_predicted <- ifelse(predict(lr_fit2, accuracy_df_train, type = "response") > .5, 1, 0) 
mce_lr_fit2 <- mean(y_predicted != y_actual)

We see that this model’s misclassification rate (in-sample) is 0.3764 which is nearly the same as the trivial model’s misclassification rate of 0.4072. The trivial model is predicting correct every time, or that no respondent gets it wrong.

5.2.2.3 Classification Tree

We produce a classification tree to see the which variables create the initial splits. Our process is to build a try with cp = -1 and use the first value of cp that produces a non-root tree to grow a tree.

rpart_fomrula <- as.formula(paste0("correct ~ ", 
                                    paste(unique(beta_lasso_accuracy_all$var_name), collapse = " + ")))
inf_cp_tree <- rpart(rpart_fomrula, data = accuracy_df_train, 
                            control = rpart.control(cp = -1), method="class")
tree_fit <- rpart(rpart_fomrula, data = accuracy_df_train, 
              control = rpart.control(cp = inf_cp_tree$cptable[2, 1]), method="class")
fancyRpartPlot(tree_fit, main = "Classification Tree", sub = "")

5.2.2.4 Random Forest

We try creating 3 random forest models:

  1. Only the variables selected from LASSO
  2. All variables in accuracy_df_train
  3. Only age and candidate - the only significant variables in logistic regression
rf_formula <- rpart_fomrula
rf_fit_selected_vars <- randomForest(rf_formula, data = accuracy_df_train, ntree = 500)
rf_fit_all_vars <- randomForest(correct ~ ., data = accuracy_df_train, ntree = 500)
rf_fit_two_vars <- randomForest(correct ~ age + candidate, data = accuracy_df_train, ntree = 500)

We find that option 3 (only using age and candidate) produces a model with the best OOB-error rate. Unfortunately, that error rate is still comparable tothe trivial model’s error rate.

rf_fit_two_vars$err.rate %>%
    as_tibble() %>%
    mutate(id = 1:n()) %>%
    gather(Error, value, -id) %>%
    mutate(Error = factor(Error, levels = c("OOB","Yes","No"))) %>%
    ggplot(aes(x = id, y = value, colour = Error)) +
    geom_line() +
    theme_jrf() +
    labs(title = "Random Forest Error Rate", x = "Trees", y = "Error Rate") +
    scale_colour_manual(values = c(pal538[4:6] %>% unname()))

5.2.2.5 Model Comparison

5.2.2.5.1 Training Data

We compare the models we have built by looking at the in-sample misclassification error rate (MSE) and the AUC for the ROC Curve. Unfortunately, none of these models is much better than the trival model (always predict the respondent correctly identified the headline as accurate or inaccurate).

plot_df_train <- fn_plot_df(accuracy_df_train, "Training", list(lr_fit2, tree_fit, rf_fit_two_vars))
train_summary <- fn_summary(plot_df_train)
train_summary %>%
    pander(caption = "Model Comparison - Training Data")
Model Comparison - Training Data
model MCE MCE Rank AUC ROC AUC ROC Rank AUC PR AUC PR Rank
Log. Regression 37.64% 2 0.6127 1 0.7132 1
rpart 36% 1 0.5909 2 0.7082 2
Random Forest 37.64% 2 0.5194 3 0.5499 3

We explore the ROC curve for the three models.

fn_roc_plot(plot_df_train, "Training")

Below is the Precision-Recall curves for each of the three models.

fn_pr_plot(plot_df_train, "Training")

5.2.2.5.2 Testing Data

We select the logistic regression model as our final model because of its AUC PR. We imagine that this metric will make the model more robust to new data. We find this to be the case. The logistic regression model has the best MCE and by far the best in AUC PR.

plot_df_test <- fn_plot_df(accuracy_df_test, "Testing", list(lr_fit2, tree_fit, rf_fit_two_vars))
test_summary <- fn_summary(plot_df_test)
test_summary %>%
    pander(caption = "Model Comparison - Testing Data")
Model Comparison - Testing Data
model MCE MCE Rank AUC ROC AUC ROC Rank AUC PR AUC PR Rank
Log. Regression 39.82% 1 0.5553 2 0.6161 1
rpart 43.11% 3 0.5230 3 0.3320 3
Random Forest 40.72% 2 0.5579 1 0.5339 2

We find that the logistic regression models perform quite well with this dataset.

fn_roc_plot(plot_df_test, "Testing")

fn_pr_plot(plot_df_test, "Testing")

5.2.2.6 Summary

lr_fit3 <- glm(correct ~ age + candidate, 
               data = accuracy_df_train %>% mutate(candidate = fct_relevel(candidate, 'Did Not Vote')), 
                                                   family = "binomial")
tidy_lr_fit3 <- tidy(lr_fit3)
lr_fit3_summary <-
  tidy_lr_fit3 %>%
  filter(term != "(Intercept)") %>%
  mutate(
        odds = exp(estimate)
      , prob = odds / (1 + odds)
      , percentage = paste0(round(prob * 100, 2), "%")
  ) %>%
  select(-std.error, -statistic)
y_predicted <- ifelse(predict(lr_fit3, accuracy_df_test, type = "response") > .5, 1, 0) 
y_actual <- as.numeric(accuracy_df_test$correct == "Yes")
confusion_lr<- table(y_actual, y_predicted)

The final model for all headlines is

\[{Correct} = 0.094 + 0.0081age + 0.3861candidateClinton + -0.2328candidateTrump + \\ 0.3062candidateJohnson + 0.2357candidateStein + 0.148candidateOther\]

pander(descr::CrossTable(y_actual, y_predicted, prop.chisq=FALSE, prop.r = F, prop.c = F, prop.t = TRUE, chisq = TRUE, dnn = c("Correct","Prediction")), split.table = Inf, big.mark = ',')
 
y_actual
y_predicted
1
 
Total
0
N
Total(%)
 
136
40.72%
 
136
1
N
Total(%)
 
198
59.28%
 
198
Total 334 334
lr_fit3_summary %>%
  select(-percentage) %>%
  pander()
term estimate p.value odds prob
age 0.008104 0.000718 1.008 0.502
candidateClinton 0.3861 0.0001544 1.471 0.5953
candidateTrump -0.2328 0.02102 0.7923 0.4421
candidateJohnson 0.3062 0.2226 1.358 0.576
candidateStein 0.2357 0.4722 1.266 0.5587
candidateOther 0.148 0.5394 1.159 0.5369

Holding all other variables constant,

  • An increase in age by one year, we expect to see about 50.2% increase in the probability of correctly predicting the accuracy of a news headline.
  • The difference of voting for Clinton vs not voting, we expect to see about 59.53% increase in the probability of correctly predicting the accuracy of a news headline.
  • The difference of voting for Trump vs not voting, we expect to see about 44.21% decrease in the probability of correctly predicting the accuracy of a news headline.
  • The difference of voting for Johnson vs not voting, we expect to see about 57.6% increase in the probability of correctly predicting the accuracy of a news headline.
  • The difference of voting for Stein vs not voting, we expect to see about 55.87% decrease in the probability of correctly predicting the accuracy of a news headline.
  • The difference of voting for another 3rd party candidate vs not voting, we expect to see about 53.69% increase in the probability of correctly predicting the accuracy of a news headline.

In the plot below, we show the log odds of correctly identifying a news headline as accurate/inaccurate vs Age and by candidate voted for. First, we see that categorically our model predicts that older Americans are better at distinguishing real from fake new stories holding candidate they supported constant. Second, we see that those that voted for Hilary Clinton and Jilly Stein have the highest log odds (and thus probability) of correctly identifying a news headline, while Donald Trump supports have the lowest log odds (and thus probability).

summary_accuracy_all <- glm(correct ~ age + candidate, data = accuracy_df, family = "binomial")
summary_accuracy_all2 <-
  accuracy_df %>%
    select(age, candidate, correct) %>%
    cbind(estimate = predict(summary_accuracy_all, accuracy_df, type = "response")) %>%
    mutate(
      odds = exp(estimate)
      , prob = odds / (1 + odds)
      , correct = as.numeric(correct == "Yes")
    )
ggplot(summary_accuracy_all2, aes(x = age, y = estimate, colour = candidate)) +
  geom_point(position = 'jitter') +
  stat_smooth(aes(y = correct, colour = candidate), method="glm", method.args = list(family = "binomial"), se=F) +
  theme_jrf() +
  labs(x = "Age", y = "Log Odds", title = "Model for Both Real and Fake News Stories", 
       subtitle = "Log Odds of Correctly Identifying Headline as Accurate/Inaccurate") +
  scale_colour_discrete(name = "Candidate")

5.2.3 Identification of a Fake Headline

Now we will limit our model building to only the 5 fake headlines to see if there are a different set of predictors that determine one’s ability to determine its accuracy. In other words, we are taking a two-sided problem and making it one-side. Now, we are only looking at the situation where the story is fake and trying to build a model just for these correct and incorrect responses.

accuracy_df_fake <-
    master_table %>%
    filter(!is.na(correct)) %>%
    filter(type == "Fake") %>%
    select(-question, -q_id, -order, -response, -article, -recall, -voted,
           -type, -accuracy, -id, -zip_code, -city, -state)
accuracy_df_train_fake <- sample_frac(accuracy_df_fake, 0.8)
accuracy_df_test_fake <- dplyr::setdiff(accuracy_df_fake, accuracy_df_train_fake)

We create a dataset of 36 variables and 35 predictors of the response correct. We then break this dataset into a training (80%) and test (20%) dataset as we did before.

x_matrix_accuracy_all_fake <- model.matrix(correct ~ ., 
                                        data = accuracy_df_train_fake)[, -1]
y_num_accuracy_all_fake <- accuracy_df_train_fake %>% select(correct) %>% unlist()
cv_glmnet_accuracy_all_fake <- cv.glmnet(x_matrix_accuracy_all_fake, y_num_accuracy_all_fake, 
                                  family = "binomial", alpha = .99, nfolds = 10)
fn_plot_cv_glmnet(cv_glmnet_accuracy_all_fake, "Accuracy - Fake Only - LASSO")

beta_lasso_accuracy_all_fake <- 
    coef(cv_glmnet_accuracy_all_fake, s = "lambda.min") %>% tidy() %>%
    dplyr::rename(term = row) %>%
    filter(term != "(Intercept)") %>%
    mutate(var_name = str_extract(term, paste(names(accuracy_df), collapse = '|'))) %>%
    mutate(level = str_replace(term, var_name, ""))

5.2.3.1 Logistic Regression

We use the 10 variables selected by lambda.min in the chart above and fit a logistic regression model. We then use Backwards Elimination so that only predictors that are signicant at the 0.01 signifance level are left to create final logistic regression model.

lr_formula_fake <- as.formula(paste0("correct ~ ", paste(unique(beta_lasso_accuracy_all_fake$var_name), collapse = " + ")))
lr_fit1_fake <- glm(lr_formula_fake, data = accuracy_df_train_fake, family = "binomial")
lr_fit2_fake <- fn_backwards_elimination(lr_fit1_fake)
y_actual_fake <- as.numeric(accuracy_df_train_fake$correct == "Yes")
y_predicted_fake <- ifelse(predict(lr_fit2_fake, accuracy_df_train_fake, type = "response") > .5, 1, 0) 
mce_lr_fit2_fake <- mean(y_predicted_fake != y_actual_fake)

We see that this model’s misclassification rate (in-sample) is 0.1781 which is lower than the trivial model’s misclassification rate of 0.2737. The trivial model is predicting incorrect every time, or that no respondent gets it right.

5.2.3.2 Classification Tree

We produce a classification tree to see the which variables create the initial splits. Our process is to build a try with cp = -1 and use the first value of cp that produces a non-root tree to grow a tree.

rpart_fomrula_fake <- as.formula(paste0("correct ~ ", 
                                    paste(unique(beta_lasso_accuracy_all_fake$var_name), collapse = " + ")))
inf_cp_tree_fake <- rpart(rpart_fomrula_fake, data = accuracy_df_train_fake, 
                            control = rpart.control(cp = -1), method="class")
tree_fit_fake <- rpart(rpart_fomrula_fake, data = accuracy_df_train_fake, 
              control = rpart.control(cp = inf_cp_tree_fake$cptable[2, 1]), method="class")
fancyRpartPlot(tree_fit_fake, main = "Classification Tree", sub = "")

5.2.3.3 Random Forest

We try creating 3 random forest models:

  1. Only the variables selected from LASSO
  2. All variables in accuracy_df_train_fake
  3. Only age and candidate - the only significant variables in logistic regression
rf_formula_fake <- rpart_fomrula_fake
rf_fit_selected_vars_fake <- randomForest(rf_formula_fake, data = accuracy_df_train_fake, ntree = 500)
rf_fit_all_vars_fake <- randomForest(correct ~ ., data = accuracy_df_train_fake, ntree = 500)
rf_fit_two_vars_fake <- randomForest(correct ~ age + candidate, data = accuracy_df_train_fake, ntree = 500)

We find that option 1 (only only the variables selected in LASSO) produces model with the best OOB-error rate. Luckily, that error rate is lower than the trivial model’s error rate.

rf_fit_selected_vars_fake$err.rate %>%
    as_tibble() %>%
    mutate(id = 1:n()) %>%
    gather(Error, value, -id) %>%
    mutate(Error = factor(Error, levels = c("OOB","Yes","No"))) %>%
    ggplot(aes(x = id, y = value, colour = Error)) +
    geom_line() +
    theme_jrf() +
    labs(title = "Random Forest Error Rate", x = "Trees", y = "Error Rate") +
    scale_colour_manual(values = c(pal538[4:6] %>% unname()))

5.2.3.4 Model Comparison

5.2.3.4.1 Training Data

In comparing the three models we see that the rpart model likely overfits the data.

plot_df_train_fake <- fn_plot_df(accuracy_df_train_fake, "Training", list(lr_fit2_fake, tree_fit_fake, rf_fit_selected_vars_fake))
train_summary_fake <- fn_summary(plot_df_train_fake)
train_summary_fake %>%
    pander(caption = "Model Comparison - Training Data")
Model Comparison - Training Data
model MCE MCE Rank AUC ROC AUC ROC Rank AUC PR AUC PR Rank
Log. Regression 17.81% 1 0.8297 1 0.6874 1
rpart 20.45% 2 0.6888 3 0.2714 3
Random Forest 21.27% 3 0.7851 2 0.6088 2

We explore the ROC curve for the three models.

fn_roc_plot(plot_df_train_fake, "Training")

Below is the Precision-Recall curves for each of the three models.

fn_pr_plot(plot_df_train_fake, "Training")

5.2.3.4.2 Testing Data

We select the random forest model our final model, but check all three against the testing data. We find that the random forest does ok with respect to MCE, but not AUC.

plot_df_test_fake <- fn_plot_df(accuracy_df_test_fake, "Testing", list(lr_fit2_fake, tree_fit_fake, rf_fit_selected_vars_fake))
test_summary_fake <- fn_summary(plot_df_test_fake)
test_summary_fake %>%
    pander(caption = "Model Comparison - Testing Data")

---------------------------------------------------------------------------------
     model       MCE    MCE Rank   AUC ROC   AUC ROC Rank   AUC PR   AUC PR Rank 
--------------- ------ ---------- --------- -------------- -------- -------------
Log. Regression 31.88%     2       0.6167         2         0.4592        1      

     rpart      31.25%     1       0.6262         1         0.3169        3      

 Random Forest  35.62%     3       0.5020         3         0.3314        2      
---------------------------------------------------------------------------------

Table: Model Comparison - Testing Data

We find that the logistic regression models perform quite well with this dataset.

fn_roc_plot(plot_df_test_fake, "Testing")

fn_pr_plot(plot_df_test_fake, "Testing")

5.2.3.5 Summary

y_predicted_fake_test <- ifelse(predict(rf_fit_selected_vars_fake, 
                                        accuracy_df_test_fake, type = "prob")[, 2] >= .5, 1, 0) 
y_actual_fake_test <- as.numeric(accuracy_df_test_fake$correct == "Yes")
confusion_lr_fake_test <- table(y_actual_fake_test, y_predicted_fake_test)
mce_rf_final <- mean(y_predicted_fake_test != y_actual_fake_test)

Our final model is a random forest model with a testing misclassification rate of 0.3562, but is worse that the trivial model’s misclassification rate of 0.2737. Thus, this model would not be useful for prediction. Below is the confusion matrix:

pander(descr::CrossTable(y_actual_fake_test, y_predicted_fake_test, prop.chisq=FALSE, prop.r = F, prop.c = F, prop.t = TRUE, chisq = TRUE, dnn = c("Correct","Prediction")), split.table = Inf, big.mark = ',')
 
Correct
Prediction
0
 
1
 
Total
0
N
Total(%)
 
89
55.62%
 
20
12.50%
 
109
1
N
Total(%)
 
37
23.12%
 
14
8.75%
 
51
Total 126 34 160

Below is the feature importance of the variables:

varImp(rf_fit_selected_vars_fake) %>%
  rownames_to_column("Variable") %>%
  as_tibble() %>%
  rename(Importance = Overall) %>%
  mutate(Importance = Importance / max(Importance)) %>%
  mutate(Variable = fct_reorder(Variable, Importance)) %>%
  arrange(desc(Importance)) %>%
  ggplot() + 
  geom_segment(aes(x = Variable, xend=Variable, y = Importance, yend=0), color = pal538[['blue']]) +
  geom_point(aes(x = Variable, y = Importance), color = pal538[['blue']]) +
  coord_flip() +
  labs(x = NULL, y = "Relative Importance", title = "Feature Importance in Random Forest",
       subtitle = "Model for Fake News Stories Only") +
  theme_jrf()

5.2.4 Identification of a Real Headline

Now we will limit our model building to only the 6 real headlines to see if there are a different set of predictors that determine one’s ability to determine its accuracy.

accuracy_df_real <-
    master_table %>%
    filter(!is.na(correct)) %>%
    filter(type == "Real") %>%
    select(-question, -q_id, -order, -response, -article, -recall, -voted,
           -type, -accuracy, -id, -zip_code, -city, -state)
accuracy_df_train_real <- sample_frac(accuracy_df_real, 0.8)
accuracy_df_test_real <- dplyr::setdiff(accuracy_df_real, accuracy_df_train_real)

We create a dataset of 36 variables and 35 predictors of the response correct. We then break this dataset into a training (80%) and test (20%) dataset, as we did in the two previous instances. We use LASSO again for variable selection.

x_matrix_accuracy_all_real <- model.matrix(correct ~ ., 
                                        data = accuracy_df_train_real)[, -1]
y_num_accuracy_all_real <- accuracy_df_train_real %>% select(correct) %>% unlist()
cv_glmnet_accuracy_all_real <- cv.glmnet(x_matrix_accuracy_all_real, y_num_accuracy_all_real, 
                                  family = "binomial", alpha = .99, nfolds = 10)
fn_plot_cv_glmnet(cv_glmnet_accuracy_all_real, "Accuracy - Real LASSO")

beta_lasso_accuracy_all_real <- 
    coef(cv_glmnet_accuracy_all_real, s = "lambda.min") %>% tidy() %>%
    dplyr::rename(term = row) %>%
    filter(term != "(Intercept)") %>%
    mutate(var_name = str_extract(term, paste(names(accuracy_df), collapse = '|'))) %>%
    mutate(level = str_replace(term, var_name, ""))

5.2.4.1 Logistic Regression

We use the 12 variables selected by lambda.min in the chart above and fit a logistic regression model. We then use Backwards Elimination so that only predictors that are signicant at the 0.01 signifance level are left to create final logistic regression model. However, there are no variables that significant in predicting whether a respondent with be correct when the headlines are true. So, we will not being using a logistic regression model.

lr_formula_real <- as.formula(paste0("correct ~ ", paste(unique(beta_lasso_accuracy_all_real$var_name), collapse = " + ")))
lr_fit1_real <- glm(lr_formula_real, data = accuracy_df_train_real, family = "binomial")
lr_fit2_real <- try(fn_backwards_elimination(lr_fit1_real), silent = TRUE)

5.2.4.2 Classification Tree

We produce a classification tree to see the which variables create the initial splits. Our process is to build a try with cp = -1 and use the first value of cp that produces a non-root tree to grow a tree.

rpart_fomrula_real <- as.formula(paste0("correct ~ ", 
                                    paste(unique(beta_lasso_accuracy_all_real$var_name), collapse = " + ")))
inf_cp_tree_real <- rpart(rpart_fomrula_real, data = accuracy_df_train_real, 
                            control = rpart.control(cp = -1), method="class")
tree_fit_real <- rpart(rpart_fomrula_real, data = accuracy_df_train_real, 
              control = rpart.control(cp = inf_cp_tree_real$cptable[2, 1]), method="class")
fancyRpartPlot(tree_fit_real, main = "Classification Tree", sub = "")

5.2.4.3 Random Forest

We try creating 3 random forest models:

  1. Only the variables selected from LASSO
  2. All variables in accuracy_df_train_real
  3. Only age and candidate - the only significant variables in logistic regression
rf_formula_real <- rpart_fomrula_real
rf_fit_selected_vars_real <- randomForest(rf_formula_real, data = accuracy_df_train_real, ntree = 500)
rf_fit_all_vars_real <- randomForest(correct ~ ., data = accuracy_df_train_real, ntree = 500)

We find that option 3 (only using age and candidate) produces model with the best OOB-error rate. Unfortunately, that error rate is still higher than the trivial model’s error rate.

rf_fit_selected_vars_real$err.rate %>%
    as_tibble() %>%
    mutate(id = 1:n()) %>%
    gather(Error, value, -id) %>%
    mutate(Error = factor(Error, levels = c("OOB","Yes","No"))) %>%
    ggplot(aes(x = id, y = value, colour = Error)) +
    geom_line() +
    theme_jrf() +
    labs(title = "Random Forest Error Rate", x = "Trees", y = "Error Rate") +
    scale_colour_manual(values = c(pal538[4:6] %>% unname()))

5.2.4.4 Feature Importance

As no predictors were significant in the logistic regression model we will not do model selection. Rather, we will show which predictors were important in the random forest model.

varImp(rf_fit_selected_vars_real) %>%
  rownames_to_column("Variable") %>%
  as_tibble() %>%
  rename(Importance = Overall) %>%
  mutate(Importance = Importance / max(Importance)) %>%
  mutate(Variable = fct_reorder(Variable, Importance)) %>%
  arrange(desc(Importance)) %>%
  ggplot() + 
  geom_segment(aes(x = Variable, xend=Variable, y = Importance, yend=0), color = pal538[['blue']]) +
  geom_point(aes(x = Variable, y = Importance), color = pal538[['blue']]) +
  coord_flip() +
  labs(x = NULL, y = "Relative Importance", title = "Feature Importance in Random Forest",
       subtitle = "Model for Real News Stories Only") +
  theme_jrf()

6 Conclusion

6.1 Final Results

In this analysis we attempted to build models to predict

  1. The number of headlines respondents would recall
  2. Whether or not respondents could distinguish real and fake news

None of our models were better than trivial models on the test datasets. However, we been able to identify from the model-building process some assoications:

  1. For correctly distinguishing any type of news we found that the two most important factors were age and candidate voted for in the 2016 presidential election
accuracy_df_tmp <- 
    accuracy_df %>%
        mutate(
              age_buckets = cut_number(age, 6)
            , age_buckets = as.character(age_buckets)
            , age_buckets = gsub("," , " - ", gsub("]", "", gsub("\\(", "", age_buckets)))
        )
   
r1 <- fn_spline_chart_correct(accuracy_df_tmp, group = "age_buckets", yaxis = "% Correct",
                              title = "Age","% Diff of All News Stories Identified Correctly") 
r2 <- fn_spline_chart_correct(accuracy_df_tmp, group = "candidate", yaxis = "% Correct",
                              title = "Age","% Diff of All News Stories Identified Correctly") 
gridExtra::grid.arrange(r1, r2, ncol=2)

  1. For correctly identifying fake news, we identified an number of factors using a random forest model. Below are summaries the 8 most important. In generally, for those that recalled a fake news story, respondents correctly identified it as inaccurate 26.58% of the time. However, certain factors impact this result, as shown below.
g1 <- fn_spline_chart_correct(accuracy_df_fake, group = "candidate", yaxis = "% Correct",
                              title = "Candidate","% Diff of Fake News Stories Identified Correctly")
g2 <- fn_spline_chart_correct(accuracy_df_fake, group = "Fox.News", yaxis = "% Correct",
                              title = "Fox News","% Diff of Fake News Stories Identified Correctly")
g3 <- fn_spline_chart_correct(accuracy_df_fake, group = "sub_party", yaxis = "% Correct",
                              title = "Party Affiliation","% Diff of Fake News Stories Identified Correctly")
g4 <- fn_spline_chart_correct(accuracy_df_fake, group = "household_income", yaxis = "% Correct",
                              title = "Household Income","% Diff of Fake News Stories Identified Correctly")
g5 <- fn_spline_chart_correct(accuracy_df_fake, group = "Share", yaxis = "% Correct",
                              title = "Share News","% Diff of Fake News Stories Identified Correctly")
g6 <- fn_spline_chart_correct(accuracy_df_fake, group = "Pinterest", yaxis = "% Correct",
                              title = "Pinterest","% Diff of Fake News Stories Identified Correctly")
g7 <- fn_spline_chart_correct(accuracy_df_fake, group = "YouTube", yaxis = "% Correct",
                              title = "YouTube","% Diff of Fake News Stories Identified Correctly")
g8 <- fn_spline_chart_correct(accuracy_df_fake, group = "Snapchat", yaxis = "% Correct",
                              title = "Snapchat","% Diff of Fake News Stories Identified Correctly")
gridExtra::grid.arrange(g1, g2, g3, g4, g5, g6, g7, g8, ncol=2)

6.2 Limitations & Next Steps

  1. It is unclear whether this is truly a representative sample. Ipsos used weighting for demographic groups in their distributions, which we did not do in our analysis.
  2. We used a dataset designed for a particular task for another purpose. This potentially creates issues. Noteably, there is bias in who can and cannot recall certain news headlines. Though we tried to show that there was not a one-to-one correlation with recall and identification, there still is certainly a significant relationship.
  3. We used a frequentist model-based approach as this aligned with STATS701; however, a Bayesnian approach would have been much more appropriate for this problem. We could have tried to make statements such as, “given the respondent recalled 4 headlines (2 real and 2 fake) and voted for Trump, the probability of correctly identify all 4 correctly was X”. We could have created priors from the data for the general population of people you recalled 4 headlines.
LS0tCnRpdGxlOiAiTmV3cyBBY2N1cmFjeSIKc3VidGl0bGU6ICdXaGVuIHRoZSBsZWdlbmQgYmVjb21lcyBmYWN0LCBwcmludCB0aGUgbGVnZW5kJwphdXRob3I6ICJKb3JkYW4gRmFycmVyIgpkYXRlOiAnMjAxNi0xMi0yMycKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNzczogc3R5bGUuY3NzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgU2V0IG9wdGlvbnMgZm9yIHRoZSBybWFya2Rvd24gZmlsZQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgd2lkdGggPSAxMDApCmBgYAoKYGBge3Igc2V0dXAyfQpzZXQuc2VlZCgxKSAjIFNldCB0aGUgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CmludmlzaWJsZShTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCAiZW5fVVMuVVRGLTgiKSkKb3B0aW9ucyhkaWdpdHMgPSA0LCB3aWR0aCA9IDIyMCkgIyBQcmV2ZW50IHByaW50aW5nIGluIHNjaWVudGlmaWMgbm90YXRpb24KIyBDcmVhdGUgYSBsb2dnZXIgZnVuY3Rpb24KbG9nZ2VyIDwtIGZ1bmN0aW9uKG1zZywgbGV2ZWwgPSAiaW5mbyIsIGZpbGUgPSBsb2dfZmlsZSkgewogICAgY2F0KHBhc3RlMCgiWyIsIGZvcm1hdChTeXMudGltZSgpLCAiJVktJW0tJWQgJUg6JU06JVMuJU9TIiksICJdWyIsIGxldmVsLCAiXSAiLCBtc2csICJcbiIpLCBmaWxlID0gc3Rkb3V0KCkpCn0KIyBTZXQgdGhlIHByb2plY3QgZGlyZWN0b3J5CmJhc2VfZGlyIDwtICcnCmRhdGFfZGlyIDwtIHBhc3RlMChiYXNlX2RpciwgImRhdGEvIikKY29kZV9kaXIgPC0gcGFzdGUwKGJhc2VfZGlyLCAiY29kZS8iKQp2aXpfZGlyIDwtIHBhc3RlMChiYXNlX2RpciwgInZpei8iKQoKZGlyLmNyZWF0ZShkYXRhX2Rpciwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCmRpci5jcmVhdGUoY29kZV9kaXIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpkaXIuY3JlYXRlKHZpel9kaXIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpgYGAKCmBgYHtyIExvYWQgUGFja2FnZXN9CnBhY21hbjo6cF9sb2FkKHRpZHl2ZXJzZSwgZ2d0aGVtZXMsIHN0cmluZ3IsIGdsbW5ldCwgZm9yY2F0cywgcmFuZG9tRm9yZXN0LCB6aXBjb2RlLAogICAgICAgICAgICAgIGNhcmV0LCBicm9vbSwgbGVhcHMsIG5ldHdvcmtEMywgc2NhbGVzLCBsZWFmbGV0LCBydmVzdCwgZ3JpZEV4dHJhLCBkZXNjciwKICAgICAgICAgICAgICBwYW5kZXIsIGtuaXRyLCBjYXIsIHJhdHRsZSwgcGxvdFJPQywgcnBhcnQsIFJPQ1IsIGNhVG9vbHMsIHBST0MsIGhpZ2hjaGFydGVyKQpgYGAKCmBgYHtyIEltcG9ydCBGb250c30KIyBUbyB0aGUgZm9udCBzZWNvbmQgZm9udCwgcnVuIHRoZSBmb2xsb3dpbmcgdHdvIGxpbmVzIG9mIGNvZGUgYW5kIGFkZCBuYW1lIG9mIHVzZXIgdG8gdmVjdG9yCiMgc3lzdGVtKHBhc3RlMCgiY3AgLXIgIix2aXpfZGlyLCJmb250cy8uIH4vTGlicmFyeS9Gb250cy8iKSkgIyBpbnN0YW50YW5lb3VzCiMgZm9udF9pbXBvcnQoKSAjIHRha2VzIGFwcHJveGltYXRlbHkgNS0xMCBtaW4KdXNlcnNfdiA8LSBjKCJKb3JkYW4iLCJyc3R1ZGlvIikKYGBgCgpgYGB7ciBDcmVhdGUgcGFsZXR0ZSBhbmQgdGhlbWV9CiMgQ3JlYXRlIGEgY29sb3IgcGFsZXR0ZQpwYWw1MzggPC0gZ2d0aGVtZXNfZGF0YSRmaXZldGhpcnR5ZWlnaHQKCiMgQ3JlYXRlIGEgdGhlbWUgdG8gdXNlIHRocm91Z2hvdXQgdGhlIGFuYWx5c2lzCnRoZW1lX2pyZiA8LSBmdW5jdGlvbihiYXNlX3NpemUgPSA4LCBiYXNlX2ZhbWlseSA9IGlmZWxzZShTeXMuaW5mbygpW1sndXNlciddXSAlaW4lIHVzZXJzX3YsICJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpKSB7CiAgICB0aGVtZSgKICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRjBGMEYwIiwgY29sb3VyID0gIiM2MDYwNjMiKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNGMEYwRjAiLCBjb2xvdXIgPSBOQSksIAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gICBlbGVtZW50X2xpbmUoY29sb3VyID0gIiNEN0Q3RDgiKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gICBlbGVtZW50X2xpbmUoY29sb3VyID0gIiNEN0Q3RDgiLCBzaXplID0gMC4yNSksCiAgICAgICAgcGFuZWwuc3BhY2luZyA9ICAgICAgIHVuaXQoMC4yNSwgImxpbmVzIiksCiAgICAgICAgcGFuZWwuc3BhY2luZy54ID0gICAgIE5VTEwsCiAgICAgICAgcGFuZWwuc3BhY2luZy55ID0gICAgIE5VTEwsCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiI0EwQTBBMyIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHZqdXN0ID0gMSwgY29sb3VyID0gJyMzQzNDM0MnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGlmZWxzZShTeXMuaW5mbygpW1sndXNlciddXSAlaW4lIHVzZXJzX3YsIkRlY2ltYU1vbm9Qcm8iLCAiSGVsdmV0aWNhIikpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSwgY29sb3VyID0gJyMzQzNDM0MnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpKSwKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAnYm9sZCcsIGNvbG91ciA9ICcjM0MzQzNDJywgaGp1c3QgPSAwKSwKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LCBmYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpKSwKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpKQogICAgICAgIAogICAgKQp9CmBgYAoKYGBge3IgZm5fcGxvdF9jdl9nbG1uZXR9CmZuX3Bsb3RfY3ZfZ2xtbmV0IDwtIGZ1bmN0aW9uKGN2X2dsbW5ldCwgbWFpbikgewoKICAgIGRhdGEgPC0gCiAgICAgICAgdGlkeShjdl9nbG1uZXQpICU+JSBhc190aWJibGUoKSAlPiUKICAgICAgICBtdXRhdGUobG9nX2xhbWJkYSA9IGxvZyhsYW1iZGEpKSAKICAgIAogICAgZGF0YTIgPC0KICAgICAgICBkYXRhICU+JQogICAgICAgIGZpbHRlcihyb3dfbnVtYmVyKCkgJSUgNCA9PSAwKQogICAgCiAgICBkYXRhMyA8LQogICAgICAgIGRhdGFfZnJhbWUoCiAgICAgICAgICAgIGxvZ19sYW1iZGEgPSBjKGxvZyhjdl9nbG1uZXQkbGFtYmRhLm1pbiksIGxvZyhjdl9nbG1uZXQkbGFtYmRhLjFzZSkpCiAgICAgICAgICAgICwgbmFtZSA9IGMoIk1pbiIsICIxc2UiKQogICAgICAgICkKICAgIAogICAgZ2dwbG90KCkgKwogICAgICAgIGdlb21fZXJyb3JiYXIoZGF0YSA9IGRhdGEsIGFlcyh4ID0gbG9nX2xhbWJkYSwgeW1pbiA9IGNvbmYubG93LCB5bWF4ID0gY29uZi5oaWdoKSwgCiAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSBwYWw1MzhbJ2RrZ3JheSddW1sxXV0sIGFscGhhID0gMC42KSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gZGF0YSwgYWVzKHggPSBsb2dfbGFtYmRhLCB5ID0gZXN0aW1hdGUpLCBjb2xvdXIgPSBwYWw1MzhbJ3JlZCddW1sxXV0pICsKICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsb2coY3ZfZ2xtbmV0JGxhbWJkYS5taW4pLCBjb2xvdXIgPSBwYWw1MzhbJ2RrZ3JheSddW1sxXV0sIGFscGhhID0gMC42KSArCiAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbG9nKGN2X2dsbW5ldCRsYW1iZGEuMXNlKSwgY29sb3VyID0gcGFsNTM4Wydka2dyYXknXVtbMV1dLCBhbHBoYSA9IDAuNikgKyAKICAgICAgICB0aGVtZV9qcmYoKSArCiAgICAgICAgbGFicyh0aXRsZSA9IG1haW4sIHggPSBleHByZXNzaW9uKGxvZyhsYW1iZGEpKSwgeSA9IGN2X2dsbW5ldCRuYW1lKSArCiAgICAgICAgZ2VvbV90ZXh0KGRhdGEgPSBkYXRhMiwgYWVzKHggPSBsb2dfbGFtYmRhLCB5ID0gSW5mLCBsYWJlbCA9IG56ZXJvKSwgdmp1c3QgPSAxLCBjb2xvdXIgPSAnIzNDM0MzQycsCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGlmZWxzZShTeXMuaW5mbygpW1sndXNlciddXSAlaW4lIHVzZXJzX3YsIkRlY2ltYU1vbm9Qcm8iLCAiSGVsdmV0aWNhIiksCiAgICAgICAgICAgICAgICAgIHNpemUgPSAyLjI1KSArCiAgICAgICAgZ2VvbV9sYWJlbChkYXRhID0gZGF0YTMsIGFlcyh4ID0gbG9nX2xhbWJkYSwgeSA9IEluZiwgbGFiZWwgPSBuYW1lKSwgdmp1c3QgPSA1LCBjb2xvdXIgPSAnIzNDM0MzQycsCiAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpKQp9ICAgCmBgYAoKYGBge3J9CmZuX3JlZ3N1YnNldHNfcGxvdHMgPC0gZnVuY3Rpb24oZml0X29iaiwgZWxib3cgPSBOVUxMKSB7CiAgICByZWdzdWJzZXRzX3N1bW1hcnkgPC0gc3VtbWFyeShmaXRfb2JqKQogICAgCiAgICBnIDwtIAogICAgZGF0YV9mcmFtZSgKICAgICAgICAgIHByZWRpY3RvcnMgPSAxOmxlbmd0aChyZWdzdWJzZXRzX3N1bW1hcnkkY3ApCiAgICAgICAgLCBjcCA9IHJlZ3N1YnNldHNfc3VtbWFyeSRjcAogICAgICAgICwgYmljID0gcmVnc3Vic2V0c19zdW1tYXJ5JGJpYwogICAgICAgICwgYWRqcjIgPSByZWdzdWJzZXRzX3N1bW1hcnkkYWRqcjIKICAgICkgJT4lCiAgICAgICAgZ2F0aGVyKG1ldHJpYywgdmFsdWUsIC1wcmVkaWN0b3JzKSAlPiUKICAgICAgICBtdXRhdGUobWV0cmljID0gZmFjdG9yKG1ldHJpYywgbGV2ZWxzID0gYygiY3AiLCJiaWMiLCJhZGpyMiIpKSkgJT4lCiAgICAgICAgZ2dwbG90KGFlcyh4ID0gcHJlZGljdG9ycywgeSA9IHZhbHVlLCBjb2xvdXIgPSBtZXRyaWMpKSArCiAgICAgICAgZmFjZXRfZ3JpZChtZXRyaWMgfiAuLCBzY2FsZSA9ICJmcmVlX3kiLCBzd2l0Y2ggPSAieSIsIAogICAgICAgICAgICAgICAgICAgbGFiZWxsZXIgPSBnZ3Bsb3QyOjpsYWJlbGxlcihtZXRyaWMgPSBjKGNwID0gIkNwIiwgYmljID0gIkJJQyIsIGFkanIyID0gIkFkanVzdGVkIFJeMiIpKSkgKwogICAgICAgIGdlb21fbGluZSgpICsgZ2VvbV9wb2ludCgpICsKICAgICAgICBnZW9tX2xhYmVsKGRhdGEgPSBkYXRhX2ZyYW1lKAogICAgICAgICAgICBwcmVkaWN0b3JzID0gYyh3aGljaC5taW4ocmVnc3Vic2V0c19zdW1tYXJ5JGNwKSwgd2hpY2gubWluKHJlZ3N1YnNldHNfc3VtbWFyeSRiaWMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaC5tYXgocmVnc3Vic2V0c19zdW1tYXJ5JGFkanIyKSkKICAgICAgICAgICAgLCBtZXRyaWMgPSBmYWN0b3IoYygiY3AiLCJiaWMiLCJhZGpyMiIpLCBsZXZlbHMgPSBjKCJjcCIsImJpYyIsImFkanIyIikpCiAgICAgICAgICAgICwgdmFsdWUgPSBjKG1pbihyZWdzdWJzZXRzX3N1bW1hcnkkY3ApLCBtaW4ocmVnc3Vic2V0c19zdW1tYXJ5JGJpYyksIG1heChyZWdzdWJzZXRzX3N1bW1hcnkkYWRqcjIpKQogICAgICAgICAgICAsIGxhYmVsID0gcGFzdGUwKCJPcHRpbWFsXG5kPSIsIGMod2hpY2gubWluKHJlZ3N1YnNldHNfc3VtbWFyeSRjcCksIHdoaWNoLm1pbihyZWdzdWJzZXRzX3N1bW1hcnkkYmljKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoLm1heChyZWdzdWJzZXRzX3N1bW1hcnkkYWRqcjIpKSkKICAgICAgICAgICAgLCB2anVzdCA9IGMoLS41LCAtLjUsIDEuMjUpCiAgICAgICAgKSwgYWVzKHggPSBwcmVkaWN0b3JzLCB5ID0gdmFsdWUsIGxhYmVsID0gbGFiZWwsIHZqdXN0ID0gdmp1c3QpLCBmYW1pbHkgPSAiRGVjaW1hTW9ub1BybyIpICsKICAgICAgICB0aGVtZV9qcmYoKSArIAogICAgICAgIGxhYnModGl0bGUgPSBwYXN0ZTAoc3RyaW5ncjo6c3RyX3RvX3RpdGxlKHJlZ3N1YnNldHNfc3VtbWFyeSRvYmokbWV0aG9kKSwgIiBTZWFyY2giKSwgCiAgICAgICAgICAgICB4ID0gIiMgb2YgUHJlZGljdG9ycyIsIHkgPSBOVUxMKSArCiAgICAgICAgc2NhbGVfY29sb3VyX21hbnVhbChndWlkZSA9IEZBTFNFLCB2YWx1ZXMgPSBjKHBhbDUzOFsncmVkJ11bWzFdXSwgcGFsNTM4WydncmVlbiddW1sxXV0sIHBhbDUzOFsnYmx1ZSddW1sxXV0pKQogICAgCiAgICBpZiAoIWlzLm51bGwoZWxib3cpKSB7CiAgICAgICAgZyA8LSBnICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZWxib3csIGFscGhhID0gMC41KSArIAogICAgICAgICAgICBnZW9tX2xhYmVsKGRhdGEgPSBkYXRhX2ZyYW1lKHggPSBlbGJvdywgeSA9IDMwMCwgbWV0cmljID0gZmFjdG9yKGMoImNwIiksIGxldmVscyA9IGMoImNwIiwiYmljIiwiYWRqcjIiKSksIAogICAgICAgICAgICAgICAgbGFiZWwgPSAiRWxib3cgd2l0aFxuMyBwcmVkaWN0b3JzIiksIGFlcyh4PXgseT15LGxhYmVsPWxhYmVsKSwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAtLjEsCiAgICAgICAgICAgICAgIGZhbWlseSA9ICJEZWNpbWFNb25vUHJvIikKICAgIH0KICAgIAogICAgcHJpbnQoZykKfQpgYGAKCgpgYGB7cn0KZm5fc3BsaW5lX2NoYXJ0IDwtIGZ1bmN0aW9uKGRmLCBudW1lcmljX2NvbHVtbiwgZ3JvdXBzLCB5YXhpcywgdGl0bGUsIHN1YnRpdGxlKSB7CiAgCiAgZGYyIDwtIAogICAgZGYgJT4lCiAgICAgICAgbXV0YXRlXyguZG90cyA9IHNldE5hbWVzKGxpc3QocGFzdGUwKCJtZWFuKCIsbnVtZXJpY19jb2x1bW4sIikiKSksIGMoIm1lYW4iKSkpICU+JQogICAgICAgIGdyb3VwX2J5XyguZG90cyA9IGxhcHBseShjKGdyb3VwcywgIm1lYW4iKSwgYXMuc3ltYm9sKSkgJT4lCiAgICAgICAgc3VtbWFyaXNlXyguZG90cyA9IHNldE5hbWVzKGxpc3QocGFzdGUwKCJtZWFuKCIsbnVtZXJpY19jb2x1bW4sIikiKSksIGMoImdyb3VwX21lYW4iKSkpICU+JQogICAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgICBtdXRhdGUoCiAgICAgICAgICAgIGRpZmYgPSBncm91cF9tZWFuIC0gbWVhbgogICAgICAgICAgICAsIHNpZ24gPSBpZl9lbHNlKGRpZmYgPj0gMCwgJ3BvcycsICduZWcnKQogICAgICAgICAgICAsIGxhYmVsID0gcGFzdGUwKGlmX2Vsc2Uoc2lnbiA9PSAicG9zIiwgIisiLCAiLSIpLCByb3VuZChhYnMoZGlmZiksIDIpKQogICAgICAgICkgJT4lCiAgICAgICAgcmVuYW1lXyguZG90cyA9IHNldE5hbWVzKGdyb3VwcywgImdyb3VwIikpICU+JQogICAgICAgIGFycmFuZ2UoZGlmZikKICAgICAgICAKICBvdmVyYWxsX21lYW4gPC0gcm91bmQodW5pcXVlKGRmMiRtZWFuKSwgMikKICB5bWlkIDwtCiAgICBkZjIgJT4lCiAgICAgIGdyb3VwX2J5KHNpZ24pICU+JQogICAgICBzdW1tYXJpc2UoeW1pZCA9IG1lYW4ocmFuZ2UoZGlmZikpKQoKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoZGF0YSA9IGRmMiwgYWVzKHggPSBmY3RfaW5vcmRlcihncm91cCksIHkgPSBkaWZmLCBmaWxsID0gc2lnbiksIHN0YXQgPSAnaWRlbnRpdHknKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9qcmYoKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIGdlb21faGxpbmUoZGF0YSA9IGRhdGFfZnJhbWUoeCA9IDApLCBtYXBwaW5nID0gYWVzKHlpbnRlcmNlcHQ9eCksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoJ3BvcycgPSBwYWw1MzhbWydncmVlbiddXSwgJ25lZycgPSBwYWw1MzhbWydyZWQnXV0pKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGZ1bmN0aW9uKHgpe3JldHVybih4ICsgb3ZlcmFsbF9tZWFuKX0pICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTIpKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9IHlheGlzLCB0aXRsZSA9IHRpdGxlLCBzdWJ0aXRsZSA9IHN1YnRpdGxlKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBkZjIsIGFlcyh4ID0gZmN0X2lub3JkZXIoZ3JvdXApLCB5ID0gZGlmZiwgbGFiZWw9bGFiZWwsIAogICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gaWZfZWxzZShzaWduID09ICJwb3MiLCBpZl9lbHNlKGRpZmYgPCBmaWx0ZXIoeW1pZCwgc2lnbiA9PSAicG9zIikkeW1pZCwgLTAuMSwgMS4xKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKGRpZmYgPiBmaWx0ZXIoeW1pZCwgc2lnbiA9PSAibmVnIikkeW1pZCwgMS4xLCAtMC4xKSkpLCAKICAgICAgICAgICAgICBjb2xvdXI9ImJsYWNrIiwgYW5nbGUgPSAwLAogICAgICAgICAgICAgIGZhbWlseSA9IGlmZWxzZShTeXMuaW5mbygpW1sndXNlciddXSAlaW4lIHVzZXJzX3YsIkRlY2ltYU1vbm9Qcm8iLCAiSGVsdmV0aWNhIikpCn0KYGBgCgpgYGB7cn0KZm5fc3BsaW5lX2NoYXJ0X2NvcnJlY3QgPC0gZnVuY3Rpb24oZGYsIGdyb3VwcywgeWF4aXMsIHRpdGxlLCBzdWJ0aXRsZSkgewogIAogIGRmMiA8LSAKICAgIGRmICU+JQogICAgICAgIG11dGF0ZShyYXRlID0gc3VtKGNvcnJlY3QgPT0gIlllcyIpIC8gbigpKSAlPiUKICAgICAgICBncm91cF9ieV8oLmRvdHMgPSBsYXBwbHkoYyhncm91cHMsICJyYXRlIiksIGFzLnN5bWJvbCkpICU+JQogICAgICAgIHN1bW1hcmlzZV8oLmRvdHMgPSBzZXROYW1lcyhsaXN0KCdzdW0oY29ycmVjdCA9PSAiWWVzIikvbigpJyksIGMoImdyb3VwX3JhdGUiKSkpICU+JQogICAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgICBtdXRhdGUoCiAgICAgICAgICAgIGRpZmYgPSBncm91cF9yYXRlIC0gcmF0ZQogICAgICAgICAgICAsIHNpZ24gPSBpZl9lbHNlKGRpZmYgPj0gMCwgJ3BvcycsICduZWcnKQogICAgICAgICAgICAsIGxhYmVsID0gcGFzdGUwKGlmX2Vsc2Uoc2lnbiA9PSAicG9zIiwgIisiLCAiLSIpLCByb3VuZChhYnMoZGlmZiksIDIpKQogICAgICAgICkgJT4lCiAgICAgICAgcmVuYW1lXyguZG90cyA9IHNldE5hbWVzKGdyb3VwcywgImdyb3VwIikpICU+JQogICAgICAgIGFycmFuZ2UoZGlmZikKICAgICAgICAKICBvdmVyYWxsX3JhdGUgPC0gcm91bmQodW5pcXVlKGRmMiRyYXRlKSwgMikKICB5bWlkIDwtCiAgICBkZjIgJT4lCiAgICAgIGdyb3VwX2J5KHNpZ24pICU+JQogICAgICBzdW1tYXJpc2UoeW1pZCA9IG1lYW4ocmFuZ2UoZGlmZikpKQoKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIoZGF0YSA9IGRmMiwgYWVzKHggPSBmY3RfaW5vcmRlcihncm91cCksIHkgPSBkaWZmLCBmaWxsID0gc2lnbiksIHN0YXQgPSAnaWRlbnRpdHknKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9qcmYoKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIGdlb21faGxpbmUoZGF0YSA9IGRhdGFfZnJhbWUoeCA9IDApLCBtYXBwaW5nID0gYWVzKHlpbnRlcmNlcHQ9eCksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoJ3BvcycgPSBwYWw1MzhbWydncmVlbiddXSwgJ25lZycgPSBwYWw1MzhbWydyZWQnXV0pKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGZ1bmN0aW9uKHgpe3JldHVybihwYXN0ZTAocm91bmQoMTAwICogKHggKyBvdmVyYWxsX3JhdGUpLCAwKSwgIiUiKSl9KSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDEyKSkgKwogIGxhYnMoeCA9IE5VTEwsIHkgPSB5YXhpcywgdGl0bGUgPSB0aXRsZSwgc3VidGl0bGUgPSBzdWJ0aXRsZSkgKwogIGdlb21fdGV4dChkYXRhID0gZGYyLCBhZXMoeCA9IGZjdF9pbm9yZGVyKGdyb3VwKSwgeSA9IGRpZmYsIGxhYmVsPWxhYmVsLCAKICAgICAgICAgICAgICAgICAgICBoanVzdCA9IGlmX2Vsc2Uoc2lnbiA9PSAicG9zIiwgaWZfZWxzZShkaWZmIDwgZmlsdGVyKHltaWQsIHNpZ24gPT0gInBvcyIpJHltaWQsIC0wLjEsIDEuMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShkaWZmID4gZmlsdGVyKHltaWQsIHNpZ24gPT0gIm5lZyIpJHltaWQsIDEuMSwgLTAuMSkpKSwgCiAgICAgICAgICAgICAgY29sb3VyPSJibGFjayIsIGFuZ2xlID0gMCwKICAgICAgICAgICAgICBmYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpKQp9CmBgYAoKCmBgYHtyfQpmbl9hdWNfcHIgPC0gZnVuY3Rpb24ocHJlZGljdGlvbnMsIGxhYmVscykgewogICAgcHJlZCA8LSBwcmVkaWN0aW9uKHByZWRpY3Rpb25zLCBsYWJlbHMpCiAgICBwZXJmIDwtIHBlcmZvcm1hbmNlKHByZWQsICJwcmVjIiwgInJlYyIpCiAgICBmIDwtCiAgICAgICAgZGF0YV9mcmFtZSgKICAgICAgICAgICAgICByZWNhbGwgPSB1bmxpc3QocGVyZkB4LnZhbHVlcykgCiAgICAgICAgICAgICwgcHJlY2lzaW9uID0gdW5saXN0KHBlcmZAeS52YWx1ZXMpCiAgICAgICAgKSAlPiUKICAgICAgICBmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpCiAgICAgICAgIAogICAgYXVjIDwtIGNhVG9vbHM6OnRyYXB6KGYkcmVjYWxsLCBmJHByZWNpc2lvbikKICAgIGF1Ywp9CmZuX3Bsb3RfZGYgPC0gZnVuY3Rpb24oZGF0YSwgZGF0YV90eXBlLCBtb2RlbF9saXN0ID0gbGlzdCgpKSB7CiAgICBpZiAoZGF0YV90eXBlID09ICJUcmFpbmluZyIpIHsKICAgICAgICB0bXBfZGYgPC0gCiAgICAgICAgZGF0YV9mcmFtZSgKICAgICAgICAgICAgICAgIGBMb2cuIFJlZ3Jlc3Npb25gICAgICAgICAgPSBtb2RlbF9saXN0W1sxXV0kZml0dGVkLnZhbHVlcyAKICAgICAgICAgICAgICAgICwgcnBhcnQgICAgICAgICAgICAgICAgICAgPSBwcmVkaWN0KG1vZGVsX2xpc3RbWzJdXSwgdHlwZSA9ICJwcm9iIilbLCAyXSAgCiAgICAgICAgICAgICAgICAsIGBSYW5kb20gRm9yZXN0YCAgICAgICAgID0gcHJlZGljdChtb2RlbF9saXN0W1szXV0sIHR5cGUgPSAicHJvYiIpWywgMl0gCiAgICAgICAgICAgICAgICApCiAgICB9IGVsc2UgewogICAgICAgIHRtcF9kZiA8LSAKICAgICAgICBkYXRhX2ZyYW1lKAogICAgICAgICAgICAgICAgYExvZy4gUmVncmVzc2lvbmAgICAgICAgICA9IHByZWRpY3QobW9kZWxfbGlzdFtbMV1dLCBkYXRhLCB0eXBlID0gInJlc3BvbnNlIikKICAgICAgICAgICAgICAgICwgYHJwYXJ0YCAgICAgICAgICAgICAgICAgPSBwcmVkaWN0KG1vZGVsX2xpc3RbWzJdXSwgZGF0YSwgdHlwZSA9ICJwcm9iIilbLCAyXQogICAgICAgICAgICAgICAgLCBgUmFuZG9tIEZvcmVzdGAgICAgICAgICA9IHByZWRpY3QobW9kZWxfbGlzdFtbM11dLCBkYXRhLCB0eXBlID0gInByb2IiKVssIDJdCiAgICAgICAgICAgICAgICApCiAgICB9CiAgICAKICAgIGRhdGEgJT4lIAogICAgICAgIHNlbGVjdChjb3JyZWN0KSAlPiUKICAgICAgICBiaW5kX2NvbHModG1wX2RmKSAlPiUKICAgICAgICBnYXRoZXIobW9kZWwsIHByZWRpY3Rpb25zLCAtY29ycmVjdCkgJT4lCiAgICAgICAgbXV0YXRlKG1vZGVsID0gZmFjdG9yKG1vZGVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTG9nLiBSZWdyZXNzaW9uIiwicnBhcnQiLCJSYW5kb20gRm9yZXN0IikpKSAlPiUKICAgICAgICBtdXRhdGUoY29ycmVjdCA9IGFzLmludGVnZXIoY29ycmVjdCkgLSAxKQp9CmZuX3N1bW1hcnkgPC0gZnVuY3Rpb24oZGF0YSkgewogICAgZGF0YSAlPiUKICAgICAgICBtdXRhdGUoCiAgICAgICAgICAgIGZhaWwgPSBpZmVsc2UocHJlZGljdGlvbnMgIT0gY29ycmVjdCwgMSwgMCkKICAgICAgICAgICAgLCByZXNwb25zZSA9IGlmZWxzZShwcmVkaWN0aW9ucyA+PSAuNSwgMSwgMCkKICAgICAgICAgICAgLCBmcCA9IGlmZWxzZShjb3JyZWN0ID09IDAgJiByZXNwb25zZSA9PSAxLCAxLCAwKQogICAgICAgICAgICAsIGZuID0gaWZlbHNlKGNvcnJlY3QgPT0gMSAmIHJlc3BvbnNlID09IDAsIDEsIDApCiAgICAgICAgKSAlPiUKICAgICAgICBncm91cF9ieShtb2RlbCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKAogICAgICAgICAgICAgICBNQ0UgPSBwYXN0ZTAocm91bmQoMTAwICogKHN1bShmcCkgKyBzdW0oZm4pKSAvIG4oKSwgMiksICIlIikKICAgICAgICAgICAgLCBgQVVDIFJPQ2AgPSBhdWMocm9jKGNvcnJlY3QsIHByZWRpY3Rpb25zKSlbMV0KICAgICAgICAgICAgLCBgQVVDIFBSYCA9IGZuX2F1Y19wcihwcmVkaWN0aW9ucywgY29ycmVjdCkKICAgICAgICAgICAgKSAlPiUKICAgICAgICBtdXRhdGUoCiAgICAgICAgICAgICBgTUNFIFJhbmtgID0gZGVuc2VfcmFuayhgTUNFYCkKICAgICAgICAgICAgLCBgQVVDIFJPQyBSYW5rYCA9IGRlbnNlX3JhbmsoZGVzYyhgQVVDIFJPQ2ApKQogICAgICAgICAgICAsIGBBVUMgUFIgUmFua2AgPSBkZW5zZV9yYW5rKGRlc2MoYEFVQyBQUmApKQogICAgICAgICkgJT4lCiAgICAgICAgc2VsZWN0KG1vZGVsLE1DRSxgTUNFIFJhbmtgLGBBVUMgUk9DYCxgQVVDIFJPQyBSYW5rYCxgQVVDIFBSYCxgQVVDIFBSIFJhbmtgKQp9CmZuX3JvY19wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIGRhdGFfdHlwZSkgewogICAgZGF0YSAlPiUKICAgIGdncGxvdChhZXMoZCA9IGNvcnJlY3QsIG0gPSBwcmVkaWN0aW9ucywgY29sb3VyID0gbW9kZWwsIGxpbmV0eXBlID0gbW9kZWwpKSArIAogICAgZ2VvbV9yb2MoKSArIHN0eWxlX3JvYygpICsgCiAgICB0aGVtZV9qcmYoKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKCJST0MgLSAiLGRhdGFfdHlwZSwiIERhdGEiKSkgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCgiTW9kZWwiLCB2YWx1ZXMgPSBjKCdMb2cuIFJlZ3Jlc3Npb24nID0gcGFsNTM4WydibHVlJ11bWzFdXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1JhbmRvbSBGb3Jlc3QnID0gcGFsNTM4WydyZWQnXVtbMV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdycGFydCcgPSBwYWw1MzhbJ2dyZWVuJ11bWzFdXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICsKICAgIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKHJlcCgic29saWQiLCA0KSwgcmVwKCJkYXNoZWQiLCAxKSkpICsKICAgIGd1aWRlcyhsaW5ldHlwZSA9IEZBTFNFKQp9CmZuX3ByIDwtIGZ1bmN0aW9uKHgsIHksIGRhdGEpIHsKICAgIHByZWRpY3Rpb25zIDwtIGRhdGEgJT4lIHNlbGVjdF8oeCkKICAgIGxhYmVscyA8LSBkYXRhICU+JSBzZWxlY3RfKHkpCiAgICBwcmVkIDwtIHByZWRpY3Rpb24ocHJlZGljdGlvbnMsIGxhYmVscykKICAgIHBlcmYgPC0gcGVyZm9ybWFuY2UocHJlZCwgInByZWMiLCAicmVjIikKICAgIGYgPC0KICAgICAgICBkYXRhX2ZyYW1lKAogICAgICAgICAgICAgIHJlY2FsbCA9IHVubGlzdChwZXJmQHgudmFsdWVzKSAKICAgICAgICAgICAgLCBwcmVjaXNpb24gPSB1bmxpc3QocGVyZkB5LnZhbHVlcykKICAgICAgICApICU+JQogICAgICAgIGZpbHRlcihjb21wbGV0ZS5jYXNlcyguKSkKICAgIGYKfQojIENyZWF0ZSB0aGUgUk9DIFBSIFBsb3QKZm5fcHJfcGxvdCA8LSBmdW5jdGlvbihkYXRhLCBkYXRhX3R5cGUpIHsKICAgIGRhdGEgJT4lCiAgICBncm91cF9ieShtb2RlbCkgJT4lCiAgICBuZXN0KCkgJT4lCiAgICBtdXRhdGUocHIgPSBwdXJycjo6bWFwKGRhdGEsIH4gZm5fcHIoJ3ByZWRpY3Rpb25zJywgJ2NvcnJlY3QnLCBkYXRhID0gLikpKSAlPiUKICAgIHNlbGVjdChtb2RlbCwgcHIpICU+JQogICAgdW5uZXN0KCkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSByZWNhbGwsIHkgPSBwcmVjaXNpb24sIGNvbG91ciA9IG1vZGVsLCBsaW5ldHlwZSA9IG1vZGVsKSkgKyAKICAgIGdlb21fbGluZSgpICsgCiAgICB0aGVtZV9qcmYoKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKCJQcmVjaXNpb24gdnMgUmVjYWxsIC0gIixkYXRhX3R5cGUsIiBEYXRhIiksIHggPSAiUmVjYWxsIiwgeSA9ICJQcmVjaXNpb24iKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKCJNb2RlbCIsIHZhbHVlcyA9IGMoJ0xvZy4gUmVncmVzc2lvbicgPSBwYWw1MzhbJ2JsdWUnXVtbMV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmFuZG9tIEZvcmVzdCcgPSBwYWw1MzhbJ3JlZCddW1sxXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JwYXJ0JyA9IHBhbDUzOFsnZ3JlZW4nXVtbMV1dCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkgKwogICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMocmVwKCJzb2xpZCIsIDQpLCByZXAoImRhc2hlZCIsIDEpKSkgKwogICAgZ3VpZGVzKGxpbmV0eXBlID0gRkFMU0UpCn0KYGBgCgpgYGB7cn0KZm5fYmFja3dhcmRzX2VsaW1pbmF0aW9uIDwtIGZ1bmN0aW9uKGxyX21vZGVsKSB7CiAgCiAgZGYgPC0gQW5vdmEobHJfbW9kZWwpICU+JSB0aWR5KCkKICBscl9mb3JtdWxhMSA8LSBscl9tb2RlbCRmb3JtdWxhCiAgCiAgCiAgd2hpbGUgKG1heChkZiRwLnZhbHVlKSA+IDAuMDEpIHsKICAgIHRvX3JlbW92ZSA8LSBkZiAlPiUgYXJyYW5nZShkZXNjKHAudmFsdWUpKSAlPiUgaGVhZCgxKSAlPiUgc2VsZWN0KHRlcm0pICU+JSB1bmxpc3QoKQogICAgCiAgICBscl9mb3JtdWxhMSA8LSB1cGRhdGUobHJfZm9ybXVsYTEsICBhcy5mb3JtdWxhKHBhc3RlMCgifiAuIC0iLHRvX3JlbW92ZSkpKQogICAgbHJfbW9kZWwgPC0gZ2xtKGxyX2Zvcm11bGExLCBkYXRhID0gbHJfbW9kZWwkZGF0YSwgZmFtaWx5ID0gImJpbm9taWFsIikKCiAgICBkZiA8LSBBbm92YShscl9tb2RlbCkgJT4lIHRpZHkoKQogICAgIAogIH0KICByZXR1cm4obHJfbW9kZWwpCn0KYGBgCgojIEV4ZWN1dGl2ZSBTdW1tYXJ5CgpEdXJpbmcgdGhlIGxhc3QgVVMgZWxlY3Rpb24gY3ljbGUsIHRoZXJlIHdhcyBzaWduaWZpY2FudCBjb25jZXJuIGFib3V0IHRoZSBpbXBhY3Qgb2YgZmFrZSBuZXdzIHN0b3JpZXMuIEFmdGVyIHRoZSBlbGVjdGlvbiwgW0J1enpmZWVkXSh3d3cuYnV6emZlZWQuY29tKSBjb21taXNzaW9uZWQgW0lwc29zIFB1YmxpYyBBZmZhaXJzXShpcHNvcy1uYS5jb20pIHRvIGNvbmR1Y3QgYSBsYXJnZS1zY2FsZSAoMywwMTUgcmVzcG9uZGVudHMpIHN1cnZleSB0byB1bmRlcnN0YW5kIGhvdyBmcmVxdWVudGx5IEFtZXJpY2FucyBmYWxsIHZpY3RpbSB0byBmYWtlIG5ld3Mgc3Rvcmllcy4gSW4gdGhpcyBhbmFseXNpcyB3ZSB1c2UgdGhlIHJhdyBzdXJ2ZXkgZGF0YSBwdWJsaXNoZWQgYnkgQnV6emZlZWQgb24gW0dpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL0J1enpGZWVkTmV3cy8yMDE2LTEyLWZha2UtbmV3cy1zdXJ2ZXkpIHRvIGlkZW50aWZ5IGlmIHRoZXJlIGFyZSBkZW1vZ3JhcGhpYyBvciBsaWZlc3R5bGUgY2hhcmFjdGVyaXN0aWNzIHRoYXQgY29ycmVsYXRlIHdpdGggdGhlIGluYWJpbGl0eSB0byBkaWZmZXJlbnRpYXRlIGluYWNjdXJhdGUgc3RvcmllcyBmcm9tIGFjY3VyYXRlIG9uZXMuIFdlIGZpbmQgdGhhdCAqKmFnZSBhbmQgdGhlIGNhbmRpZGF0ZSB0aGF0IHNvbWVvbmUgdm90ZWQgZm9yIGluIHRoZSAyMDE2IHByZXNpZGVudGlhbCBlbGVjdGlvbioqIGFyZSBiZXN0IGJlc3QgcHJlZGljdG9ycyBvZiB3aGV0aGVyIHNvbWVvbmUgY2FuIGNvcnJlY3RseSBkaXN0aW5ndWlzaCBiZXR3ZWVuIGFjY3VyYWN0ZSBhbmQgaW5hY2N1cmF0ZSBuZXdzIGhlYWRsaW5lcy4KCkluIHRoZSB3ZWItYXBwIGJlbG93LCB0aGUgcmVhZGVyIHNlZW4gdGhlIGhlYWRsaW5lcyB1c2VkIGFuZCBzZWUgaG93IHRoZXkgbWlnaHQgaGF2ZSBkb25lIG9uIHN1Y2ggYSBzdXJ2ZXk6IAoKPGlmcmFtZSBpZCA9ICJhcHBfaWZyYW1lIiBhbGlnbiA9ICJjZW50ZXIiIHdpZHRoID0gIjQ1MHB4IiwgaGVpZ2h0ID0gIjYyNXB4InNyYz0iaHR0cHM6Ly9qb3JkYW5mYXJyZXIuc2hpbnlhcHBzLmlvL2Zha2VfbmV3cy8iPjwvaWZyYW1lPgoKIyBTdXJ2ZXkgQmFja2dyb3VuZAoKRWFjaCByZXNwb25kZW50IHdhcyBzaG93biA2IG9mIHRoZSBoZWFkbGluZXMgbGlzdCBpbiB0aGUgdGFibGUgYmVsb3cgKDMgd2VyZSBmYWtlIHN0b3JpZXMgYW5kIDMgd2VyZSByZWFsIHN0b3JpZXMpLiBBbGwgaGVhZGxpbmVzIGFwcGVhciBvbiB0aGUgaW50ZXJuZXQgb3ZlciB0aGUgcGFzdCB5ZWFyLiAKCmBgYHtyIHJlc3VsdHMgPSAnYXNpcyd9CmFydGljbGVzIDwtIHJlYWRfZGVsaW0ocGFzdGUwKGRhdGFfZGlyLCAiYXJ0aWNsZXMudHh0IiksIGRlbGltID0gInwiKQoKYXJ0aWNsZXMgJT4lCiAgbXV0YXRlKEFydGljbGUgPSBwYXN0ZTAoIlsiLCBhcnRpY2xlLCAiXSgiLCB1cmwsICIpIikpICU+JQogIHNlbGVjdChOdW0gPSBpZCwgQXJ0aWNsZSwgVHlwZSA9IHR5cGUpICU+JQogIHBhbmRlcihzcGl0LmNlbGwgPSA2NSwgc3BsaXQudGFibGUgPSBJbmYsIGNhcHRpb24gPSAiQWN0dWFsIEhlYWRsaW5lcyBzaG93biB0byBSZXNwb25kZW50cyIsCiAgICAgICAgIGp1c3RpZnkgPSBjKCJjZW50ZXIiLCAibGVmdCIsImNlbnRlciIpKQpgYGAKCgpGb3IgZWFjaCBoZWFkbGluZSwgcmVzcG9uZGVudHMgd2VyZSBhc2tlZCBpZiB0aGV5IHJlY2FsbGVkIGhhdmluZyBzZWVuIG9yIGhlYXJkIGFib3V0IHRoZSBoZWFkbGluZSAocG9zc2libGUgYW5zd2VycyB3ZXJlIGB5ZXNgLCBgbm9gLCBhbmQgYG5vdCBzdXJlYCkuIFdoZW4gYSByZXNwb25kZW50IHJlc3BvbmRlZCB3aXRoIHllcyB0byB0aGUgcmVjYWxsIHF1ZXN0aW9uLCB0aGV5IHdlcmUgYXNrZWQgaWYgdGhleSBiZWxpZXZlZCB0aGUgaGVhZGxpbmUgdG8gYmUgYWNjdXJhdGUgKHBvc3NpYmxlIGFuc3dlcnMgd2hlcmUgYHZlcnkgYWNjdXJhdGVgLCBgc29tZXdoYXQgYWNjdXJhdGVgLCBgbm90IHZlcnkgYWNjdXJhdGVgLCBhbmQgYG5vdCBhdCBhbGwgYWNjdXJhdGVgKS4KClByaW9yIHRvIHF1ZXN0aW9ucyByZWdhcmRpbmcgdGhlIGhlYWRsaW5lcywgcmVzcG9uZGVudHMgd2VyZSBhc2tlZCBhYm91dCB0aGVpciBwb2xpdGljYWwgcGFydHkgYWZmaWxpYXRpb24gYW5kIHdobyB0aGV5IHZvdGVkIGZvciBkdXJpbmcgdGhlIDIwMTYgcHJlc2lkZW50aWFsIGVsZWN0aW9uLiBBZnRlciByZXNwb25kaW5nIHRvIHF1ZXN0aW9ucyBhYm91dCB0aGUgaGVhZGxpbmVzLCByZXNwb25kZW50cyB3ZXJlIGFza2VkIGFib3V0IHRoZSBzb2NpYWwgbWVkaWEgd2Vic2l0ZS9hcHBzIHRoZXkgdXNlZCBhbmQgd2hlcmUgdGhleSBjb25zdW1lZCBuZXdzLiBJbiBhZGRpdGlvbiB0byB0aGVpciBiZWhhdmlvcmFsIHN1cnZleSBkYXRhLCBmb3IgZWFjaCByZXNwb25kZW50IHRoZSBzdXJ2ZXkgaW5jbHVkZXMgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24gc3VjaCBhcyBhZ2UsIHJhY2UsIHNleCwgaW5jb21lLCBlZHVjYXRpb24uIElwc29zIHVzZWQgdGhpcyBpbmZvcm1hdGlvbiB0byBkZXRlcm1pbmUgYSByZXByZXNlbnRhdGl2ZSBzYW1wbGUuCgpBbGwgdGhlIGRhdGEgdXNlZCBmb3IgdGhpcyBhbmFseXNpcyBpcyBwdWJsaXNoZWQgaW4gW0J1enpmZWVkJ3MgR2l0SHViIHJlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS9CdXp6RmVlZE5ld3MvMjAxNi0xMi1mYWtlLW5ld3Mtc3VydmV5KS4gU3BlY2lmaWNhbGx5LCB3ZSB1c2VkIAoKMS4gW1JhdyBEYXRhXShodHRwczovL2dpdGh1Yi5jb20vQnV6ekZlZWROZXdzLzIwMTYtMTItZmFrZS1uZXdzLXN1cnZleS9ibG9iL21hc3Rlci9kYXRhL3Jhdy1kYXRhLmNzdikKMi4gW0RvY3VtZW5hdGlvbiBvZiBTdXJ2ZXkgUXVlc3Rpb25zXShodHRwczovL2dpdGh1Yi5jb20vQnV6ekZlZWROZXdzLzIwMTYtMTItZmFrZS1uZXdzLXN1cnZleS9ibG9iL21hc3Rlci9kb2NzL0JGJTIwTmV3cyUyMEFjY3VyYWN5JTIwUXVlc3Rpb25uYWlyZSUyMERhdGElMjBEaWN0aW9uYXJ5LjEyNjIwMTYuZG9jeCkKMy4gW0RlbW9ncmFwaGljIERpc3RyaWJ1dGlvbnNdKGh0dHBzOi8vZ2l0aHViLmNvbS9CdXp6RmVlZE5ld3MvMjAxNi0xMi1mYWtlLW5ld3Mtc3VydmV5L2Jsb2IvbWFzdGVyL2RvY3MvaXBzb3MtdGFibGVzLnhsc3gpCgpUaGUgZGVtb2dyYXBoaWMgZGlzdHJpYnV0aW9ucyB3ZXJlIHVzZWQgdG8gbWF0Y2ggdGhlIGNvZGVkIGRhdGEgKGkuZS4gYDEsIDJgKSB0byBmYWN0b3IgZGF0YSAoaS5lLiBgTWFsZSwgRmVtYWxlYCkuIFRoaXMgcHJvY2VzcyB3YXMgcXVpdGUgbWFudWFsLCBidXQgdGhlIGRpc3RyaWJ1dGlvbnMgZm9yIGVhY2ggZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWMgbWF0Y2hlZCB0aGUgZGlzdHJpYnV0aW9ucyB3ZSBzYXcgaW4gdGhlIHJhdyBkYXRhIHNvIHdlIGFyZSBjb25maWRlbnQgaW4gb3VyIHByb2Nlc3MuCgojIERhdGEgQ2xlYW5pbmcgCgpgYGB7ciBEb3dubG9hZCBSYXcgRGF0YSBmcm9tIGdpdGh1Yn0KZm5fZG93bl9yYXdfZGF0YSA8LSBmdW5jdGlvbihkZikgewogICAgZG93bmxvYWQuZmlsZShkZiR1cmwsIGRlc3RmaWxlID0gcGFzdGUwKGRhdGFfZGlyLCBkZiRkYXRhLCAiLiIsIGRmJGZpbGVfdHlwZSksIG1vZGU9IndiIiwgcXVpZXQgPSBUUlVFKQogICAgcmV0dXJuKGRmKQp9CgpkYXRhX2xvY2F0aW9ucyA8LSAKICAgIGRhdGFfZnJhbWUoCiAgICAgICAgICBkYXRhID0gYygicmF3X2RhdGEiLCJzdXJ2ZXlfcXVlc3Rpb25zIiwiZGVtb2dyYXBoaWNfZGlzdHJpYnV0aW9ucyIpCiAgICAgICAgLCBmaWxlX3R5cGUgPSBjKCJjc3YiLCAiZG9jeCIsInhsc3giKQogICAgICAgICwgdXJsID0gYygiaHR0cHM6Ly9naXRodWIuY29tL0J1enpGZWVkTmV3cy8yMDE2LTEyLWZha2UtbmV3cy1zdXJ2ZXkvYmxvYi9tYXN0ZXIvZGF0YS9yYXctZGF0YS5jc3Y/cmF3PXRydWUiLAogICAgICAgICAgICAgICAgICAiaHR0cHM6Ly9naXRodWIuY29tL0J1enpGZWVkTmV3cy8yMDE2LTEyLWZha2UtbmV3cy1zdXJ2ZXkvcmF3L21hc3Rlci9kb2NzL0JGJTIwTmV3cyUyMEFjY3VyYWN5JTIwUXVlc3Rpb25uYWlyZSUyMERhdGElMjBEaWN0aW9uYXJ5LjEyNjIwMTYuZG9jeCIsCiAgICAgICAgICAgICAgICAgICJodHRwczovL2dpdGh1Yi5jb20vQnV6ekZlZWROZXdzLzIwMTYtMTItZmFrZS1uZXdzLXN1cnZleS9yYXcvbWFzdGVyL2RvY3MvaXBzb3MtdGFibGVzLnhsc3giKQogICAgKSAlPiUKICAgIGRvKGZuX2Rvd25fcmF3X2RhdGEoLikpCgpyYXdfZGF0YSA8LSByZWFkX2NzdihwYXN0ZTAoZGF0YV9kaXIsICJyYXdfZGF0YSIsIi4iLCJjc3YiKSkKCmFydGljbGVzIDwtIHJlYWRfZGVsaW0ocGFzdGUwKGRhdGFfZGlyLCAiYXJ0aWNsZXMudHh0IiksIGRlbGltID0gInwiLCBwcm9ncmVzcyA9IEZBTFNFKQpgYGAKClRoZSByYXcgZGF0YXNldCBjb250YWlucyAqKmByIHByZXR0eU51bShucm93KHJhd19kYXRhKSwgYmlnLm1hcmsgPSAiLCIpYCoqIHJvd3MgLSBlYWNoIHJlcHJlc2VudGluZyBhIHN1cnZleSByZXNwb25kZW50IGFuZCAqKmByIHByZXR0eU51bShuY29sKHJhd19kYXRhKSwgYmlnLm1hcmsgPSAiLCIpYCoqIGNvbHVtbnMgLSBlYWNoIHJlcHJlc2VudGluZyBlaXRoZXIgZGVtb2dyYXBoaWMgZGF0YSwgcmVzcG9uc2UgZGF0YSwgb3IgaW50ZXJuYWwgSXBzb3MgY29kaW5nLiBCZWxvdyBpcyBhIHNhbXBsZToKCmBgYHtyfQpyYXdfZGF0YSAlPiUKICAgIGhlYWQoKQpgYGAKCldlIHByb2NlZWQgdG8gcmVjb2RlIG91ciBjb2x1bW5zIG9mIGludGVyZXN0LiBXZSBmaW5kIG5vIHVuZXhwZWN0ZWQgbWlzc2luZyB2YWx1ZXMuIFRoZXJlIGFyZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgcmF3IGRhdGFzZXQgYmVjYXVzZSBtYW55IG9mIHRoZSBxdWVzdGlvbnMgd2VyZSBjb25kaXRpb24gKGkuZS4gaWYgeW91IHJlc3BvbmQgdG8gcXVlc3Rpb24gNSB0aGF0IHlvdSBkaWQgbm90IHZvdGUgeW91IHdlcmUgbm90IGFza2VkIHF1ZXN0aW9uIDYgcmVnYXJkaW5nIHdobyB5b3Ugdm90ZWQgZm9yKS4KCiMjIERlbW9ncmFwaGljCgpXZSBleHRyYWN0IHRoZSBmb2xsb3dpbmcgY2xlYW5lZCBkZW1vZ3JhcGhpYyB2YXJpYWJsZXM6CgpgYGB7cn0KaXBvc19sYWJlbHMgPC0gCiAgbGlzdCgKICAgICAgaG91c2Vob2xkX2luY29tZSA9IGMoJ1VuZGVyICQxNUsnLCckMTVLIHRvIGxlc3MgdGhhbiAkMjBLJywnJDIwSyB0byBsZXNzIHRoYW4gJDI1SycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICckMjVLIHRvIGxlc3MgdGhhbiAkMzBLJywnJDMwSyB0byBsZXNzIHRoYW4gJDQwSycsJyQ0MEsgdG8gbGVzcyB0aGFuICQ1MEsnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAnJDUwSyB0byBsZXNzIHRoYW4gJDc1SycsJyQ3NUsgdG8gbGVzcyB0aGFuICQxMDBLJywnJDEwMEsgdG8gbGVzcyB0aGFuICQxNTBLJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgJyQxNTBLIG9yIG1vcmUnKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUYWIgMTUxCiAgICAsIG1hcml0YWxfc3RhdHVzICAgPSBjKCJTaW5nbGUiLCJEb21lc3RpYyBQYXJ0bmVyc2hpcCIsIk1hcnJpZWQiLCJXaWRvd2VkIiwiRGl2b3JjZWQgb3Igc2VwYXJhdGVkIikgICMgVGFiIDE2OQogICAgLCBlZHVjYXRpb24gICAgICAgID0gYygiR3JhZGUgU2Nob29sIiwiU29tZSBIaWdoIFNjaG9vbCIsIkdyYWR1YXRlZCBIaWdoIFNjaG9vbCIsIlNvbWUgQ29sbGVnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJBc3NvY2lhdGUncyBkZWdyZWUgKEFBLCBBUywgZXRjLikiLCJCYWNoZWxvcidzIGRlZ3JlZSAoQkEsIEJTLCBldGMuKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJQb3N0IEdyYWR1YXRlIERlZ3JlZSIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVGFiIDE2MwogICAgLCBlbXBsb3ltZW50ICAgICAgID0gYygnRW1wbG95ZWQgLSBmdWxsLXRpbWUnLCdFbXBsb3llZCAtIHBhcnQtdGltZScsJ1NlbGYtRW1wbG95ZWQnLCdSZXRpcmVkJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1N0dWRlbnQvUHVwaWwnLCdNaWxpdGFyeScsJ0hvbWVtYWtlcicsJ0N1cnJlbnRseSBVbmVtcGxveWVkJywnKERrL05zKScpICAgICAgIyBUYWIgMTcyCiAgICAsIHJhY2UgICAgICAgICAgICAgPSBjKCdXaGl0ZScsJ0JsYWNrJywnQXNpYW4nLCdPdGhlcicpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVGFiIDE3NQogICAgLCBoaXNwYW5pYyAgICAgICAgID0gYygnWWVzJywnTm8nLCcoRGsvTnMpJykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRhYiAxNzgKICAgICwgZ2VuZGVyICAgICAgICAgICA9IGMoIk1hbGUiLCJGZW1hbGUiKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUYWIgMTU0CiAgICAsIGNoaWxkcmVuICAgICAgICAgPSBjKCdVbmRlciA2IG9ubHknLCc2LTEyIE9ubHknLCcxMy0xNyBPbmx5JywnVW5kZXIgNiBhbmQgNi0xMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICdVbmRlciA2IGFuZCAxMy0xNycsJzYtMTIgYW5kIDEzLTE3JywnQWxsIDMnLCdOb25lIFVuZGVyIDE4JykgICAgICAgICAgICAgICAgICMgVGFiIDE2NgogICkKYGBgCgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KZGVtb2dyYXBoaWNzIDwtIAogIHJhd19kYXRhICU+JQogICAgbXV0YXRlKAogICAgICAgIGhvdXNlaG9sZF9pbmNvbWUgPSBmYWN0b3IoRFBfVVNISEkyX2RlciwgbGFiZWxzID0gaXBvc19sYWJlbHNbWydob3VzZWhvbGRfaW5jb21lJ11dKQogICAgICAsIG1hcml0YWxfc3RhdHVzID0gZmFjdG9yKHVzbWFyMl9kZXIsIGxhYmVscyA9IGlwb3NfbGFiZWxzW1snbWFyaXRhbF9zdGF0dXMnXV0pCiAgICAgICwgZWR1Y2F0aW9uID0gZmFjdG9yKHVzZWR1M19kZXIsIGxhYmVscyA9IGlwb3NfbGFiZWxzW1snZWR1Y2F0aW9uJ11dKQogICAgICAsIGVtcGxveW1lbnQgPSBmYWN0b3IoRU1QMDFfZGVyLCBsYWJlbHMgPSBpcG9zX2xhYmVsc1tbJ2VtcGxveW1lbnQnXV0pCiAgICAgICwgcmFjZSA9IGZhY3RvcihVU1JBQ0U0X2RlciwgbGFiZWxzID0gaXBvc19sYWJlbHNbWydyYWNlJ11dKQogICAgICAsIGhpc3BhbmljID0gZmFjdG9yKFVTUkVUSDNfZGVyLCBsYWJlbHMgPSBpcG9zX2xhYmVsc1tbJ2hpc3BhbmljJ11dKQogICAgICAsIGdlbmRlciA9IGZhY3RvcihyZXNwX2dlbmRlciwgbGFiZWxzID0gaXBvc19sYWJlbHNbWydnZW5kZXInXV0pCiAgICApICU+JQogICAgbXV0YXRlKAogICAgICAgIGlkID0gSUQKICAgICAgLCBhZ2UgPSByZXNwX2FnZQogICAgICAsIG1hcmtldF9zaXplID0gZmFjdG9yKEhDQUxfU1RETUtUU0laRV9MYWJlbF9VUywgbGV2ZWxzID0gYygiTm9uIG1ldHJvIiwgIjwxTSIsICIxTS00LjlNIiwgIjVNKyIpKQogICAgICAsIHJlZ2lvbiA9IGFzLmZhY3RvcihIQ0FMX1NURFJFR0lPTl80Q09ERVNfTGFiZWxfVVMpCiAgICAgICwgc3RhdGUgPSBhcy5mYWN0b3IoSENBTF9SRUdJT04xX0xhYmVsX1VTKQogICAgICAsIGNpdHkgPSBIQ0FMX1JlZ2lvbjNfTGFiZWxfVVMKICAgICAgLCB6aXBfY29kZSA9IEhBRERfWmlwQ29kZV9VUwogICAgKSAlPiUKICAgIG11dGF0ZSgKICAgICAgY2l0eSA9IHN0cmluZ3I6OnN0cl90b190aXRsZShjaXR5KQogICAgKSAlPiUKICAgIHNlbGVjdCgKICAgICAgICBpZAogICAgICAsIGdlbmRlcgogICAgICAsIGFnZQogICAgICAsIHJhY2UKICAgICAgLCBoaXNwYW5pYwogICAgICAsIG1hcml0YWxfc3RhdHVzCiAgICAgICwgZW1wbG95bWVudAogICAgICAsIGVkdWNhdGlvbgogICAgICAsIGhvdXNlaG9sZF9pbmNvbWUKICAgICAgLCByZWdpb24KICAgICAgLCBtYXJrZXRfc2l6ZQogICAgICAsIGNpdHkKICAgICAgLCBzdGF0ZQogICAgICAsIHppcF9jb2RlCiAgICApCgpmbl9zdW1tYXJ5X3RhYmxlIDwtIGZ1bmN0aW9uKGRmKSB7CiAgICAKICAgIGZuX2ZhY3Rvcl9zdW1tYXJ5IDwtIGZ1bmN0aW9uKGRmLCB4KSB7CiAgICAgICAgCiAgICAgICAgY29sX3ZlY3RvciA8LSBkZiAlPiUgc2VsZWN0Xyh4KSAlPiUgdW5saXN0KCkKICAgICAgICAKICAgICAgICBpZihpcy5mYWN0b3IoY29sX3ZlY3RvcikpIHsKICAgICAgICAgICAgY29sX2xldmVscyA8LSBsZXZlbHMoY29sX3ZlY3RvcikgJT4lIGhlYWQoNSkKICAgICAgICAgICAgRGVzY3JpcHRpb24gPC0gcGFzdGUwKCIqIiwgcGFzdGUwKGNvbF9sZXZlbHMsIGNvbGxhcHNlID0gIiosICoiKSwiKiIpCiAgICAgICAgfSAgZWxzZSB7CiAgICAgICAgICAgIERlc2NyaXB0aW9uIDwtICIiCiAgICAgICAgfQogICAgICAgIHJldHVybihEZXNjcmlwdGlvbikKICAgIH0KICAgIAogICAgc3VtbWFyeV90YWJsZSA8LWRhdGFfZnJhbWUoVmFyaWFibGUgPSBuYW1lcyhkZikpCiAgICAKICAgIHN1bW1hcnlfdGFibGUgJT4lCiAgICAgICAgcm93d2lzZSgpICU+JQogICAgICAgIG11dGF0ZShEZXNjcmlwdGlvbiA9IGZuX2ZhY3Rvcl9zdW1tYXJ5KGRmLCBWYXJpYWJsZSkpCn0KCmZuX3N1bW1hcnlfdGFibGUoZGVtb2dyYXBoaWNzKSAlPiUgcGFuZGVyKHNwbGl0LmNlbGxzID0gODAsIHNwbGl0LnRhYmxlID0gSW5mLCBqdXN0aWZ5ID0gYygnbGVmdCcsICdsZWZ0JykpCmBgYAoKCiMjIEdlbmVyYWwgUmVzcG9uc2VzCgpXZSBhbHNvIGNsZWFuIHRoZSByZXNwb25zZXMgdG8gZ2VuZXJhbCBwb2xpdGljYWwgYWZmaWxpYXRpb24gcXVlc3Rpb25zOgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KcGFydHlfcmVzcG9uc2VzIDwtCiAgcmF3X2RhdGEgJT4lCiAgbXV0YXRlKAogICAgICBwYXJ0eSA9IGlmX2Vsc2UoRFdEMSA9PSA0LCAzTCwgRFdEMSkKICAgICwgcGFydHkgPSBmYWN0b3IocGFydHksIGxhYmVscyA9IGMoIkRlbW9jcmF0IiwiUmVwdWJsaWNhbiIsIkluZGVwZW5kZW50L090aGVyIikpCiAgICAsIHBhcnR5X3N0cmVuZ3RoID0gZmFjdG9yKGNvYWxlc2NlKERXRDIsIERXRDMpLCBsYWJlbHMgPSBjKCJTdHJvbmciLCJXZWFrIikpCiAgICAsIHN1Yl9wYXJ0eSA9IHBhc3RlMChpZl9lbHNlKGlzLm5hKHBhcnR5X3N0cmVuZ3RoKSwgIiIsIHBhc3RlMChhcy5jaGFyYWN0ZXIocGFydHlfc3RyZW5ndGgpLCAiICIpKSwgcGFydHkpCiAgICAsIGluZGVwZW5kZW50X3R5cGUgPSBmYWN0b3IoRFdENCwgbGFiZWxzID0gYygiRGVtb2NyYXQiLCJSZXB1YmxpY2FuIiwiTmVpdGhlciIpKQogICAgLCBzdWJfcGFydHkgPSBpZl9lbHNlKCFpcy5uYShpbmRlcGVuZGVudF90eXBlKSwgcGFzdGUwKHN1Yl9wYXJ0eSwgIiwgTGVhbiAiLCBpbmRlcGVuZGVudF90eXBlKSwgc3ViX3BhcnR5KQogICAgLCBzdWJfcGFydHkgPSBmYWN0b3Ioc3ViX3BhcnR5LCBsZXZlbHMgPSBjKCdTdHJvbmcgRGVtb2NyYXQnLCAnV2VhayBEZW1vY3JhdCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdJbmRlcGVuZGVudC9PdGhlciwgTGVhbiBEZW1vY3JhdCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0luZGVwZW5kZW50L090aGVyLCBMZWFuIE5laXRoZXInLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSW5kZXBlbmRlbnQvT3RoZXIsIExlYW4gUmVwdWJsaWNhbicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1dlYWsgUmVwdWJsaWNhbicsICdTdHJvbmcgUmVwdWJsaWNhbicpKQogICAgLCB2b3RlZCA9IGZhY3RvcihpZl9lbHNlKERXRDUgIT0gNCwgIk5vIiwgIlllcyIpLCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSkKICAgICwgY2FuZGlkYXRlID0gZmN0X2V4cGxpY2l0X25hKGZjdF9yZWNvZGUoYXMuZmFjdG9yKERXRDYpLCAKICAgICAgICAgICAgIkNsaW50b24iID0gIjEiLCAiVHJ1bXAiID0gIjIiLCAiSm9obnNvbiIgPSAiMyIsICJTdGVpbiIgPSAiNCIsICJPdGhlciIgPSAiNSIpLCAiRGlkIE5vdCBWb3RlIikKICApICU+JQogIHNlbGVjdChpZCA9IElELCBwYXJ0eSwgc3ViX3BhcnR5LCB2b3RlZCwgY2FuZGlkYXRlKQoKZm5fc3VtbWFyeV90YWJsZShwYXJ0eV9yZXNwb25zZXMpICU+JSBwYW5kZXIoc3BsaXQuY2VsbHMgPSA4MCwgc3BsaXQudGFibGUgPSBJbmYsIGp1c3RpZnkgPSBjKCdjZW50ZXInLCAnbGVmdCcpKQpgYGAKCiMjIEhlYWRsaW5lIFJlc3BvbnNlcwoKRWFjaCB0aW1lIGEgcmVzcG9uZGVudCB3YXMgc2hvd24gYSBxdWVzdGlvbiwgd2UgaGF2ZSAzIGRhdGEgcG9pbnRzOgoKMS4gVGhlIG9yZGVyIHRoZXkgc2F3IHRoaXMgcXVlc3Rpb24gKDEtNikKMi4gV2hldGhlciB0aGV5IHJlY2FsbGVkIHRoZSBuZXdzIHN0b3J5CjMuIFdoZXRoZXIgdGhleSB0aG91Z2h0IHRoZSBuZXdzIHN0b3J5IHdhcyBhY2N1cmF0ZQoKSGVyZSB3ZSBpZGVudGlmeSBpZiB0aGUgcmVzcG9uZGVudCB3YXMgY29ycmVjdCBpbiB0aGVpciBzZWxlY3Rpb24gb2YgYWNjdXJhY3kuIFdlIGRlZmluZSB0aGUgZWFjaCByZXNwb25zZSB0byAoMykgYXMgY29ycmVjdCBpZiByZXNwb25kZW50cyBzYWlkIGEgdHJ1ZSBzdG9yeSB3YXMgYFZlcnkgYWNjdXJhdGVgIG9yIGBTb21ld2hhdCBhY2N1cmF0ZWAgYW5kIGEgZmFrZSBzdG9yeSB3YXMgYE5vdCB2ZXJ5IGFjY3VyYXRlYCBvciBgTm90IGF0IGFsbCBhY2N1cmF0ZWAuIFdlIGRlZmluZSBpbmNvcnJlY3QgYXMgdGhlIGNvbnZlcnNlLgoKYGBge3J9CmhlYWRsaW5lX29yZGVyX2NvbHMgPC0gYygnTVJLX09SRF9MT09QRFdEN19EV0Q4X0FfT1JEX0xPT1BEV0Q3X0RXRDgnLCdNUktfT1JEX0xPT1BEV0Q3X0RXRDhfQl9PUkRfTE9PUERXRDdfRFdEOCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAnTVJLX09SRF9MT09QRFdEN19EV0Q4X0NfT1JEX0xPT1BEV0Q3X0RXRDgnLCdNUktfT1JEX0xPT1BEV0Q3X0RXRDhfRF9PUkRfTE9PUERXRDdfRFdEOCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAnTVJLX09SRF9MT09QRFdEN19EV0Q4X0VfT1JEX0xPT1BEV0Q3X0RXRDgnLCdNUktfT1JEX0xPT1BEV0Q3X0RXRDhfRl9PUkRfTE9PUERXRDdfRFdEOCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAnTVJLX09SRF9MT09QRFdEN19EV0Q4X0dfT1JEX0xPT1BEV0Q3X0RXRDgnLCdNUktfT1JEX0xPT1BEV0Q3X0RXRDhfSF9PUkRfTE9PUERXRDdfRFdEOCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAnTVJLX09SRF9MT09QRFdEN19EV0Q4X0lfT1JEX0xPT1BEV0Q3X0RXRDgnLCdNUktfT1JEX0xPT1BEV0Q3X0RXRDhfSl9PUkRfTE9PUERXRDdfRFdEOCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAnTVJLX09SRF9MT09QRFdEN19EV0Q4X0tfT1JEX0xPT1BEV0Q3X0RXRDgnKQoKaGVhZGxpbmVfcmVjYWxsX2NvbHMgPC0gYygnTE9PUERXRDdfRFdEOF9BX0RXRDcnLCdMT09QRFdEN19EV0Q4X0JfRFdENycsJ0xPT1BEV0Q3X0RXRDhfQ19EV0Q3JywnTE9PUERXRDdfRFdEOF9EX0RXRDcnLAogICAgICAgICAgICAgICAgICAgICAgICAgICdMT09QRFdEN19EV0Q4X0VfRFdENycsJ0xPT1BEV0Q3X0RXRDhfRl9EV0Q3JywnTE9PUERXRDdfRFdEOF9HX0RXRDcnLCdMT09QRFdEN19EV0Q4X0hfRFdENycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ0xPT1BEV0Q3X0RXRDhfSV9EV0Q3JywnTE9PUERXRDdfRFdEOF9KX0RXRDcnLCdMT09QRFdEN19EV0Q4X0tfRFdENycpCgpoZWFkbGluZV9hY2N1cmFjeV9jb2xzIDwtIGMoJ0xPT1BEV0Q3X0RXRDhfQV9EV0Q4JywnTE9PUERXRDdfRFdEOF9CX0RXRDgnLCdMT09QRFdEN19EV0Q4X0NfRFdEOCcsJ0xPT1BEV0Q3X0RXRDhfRF9EV0Q4JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdMT09QRFdEN19EV0Q4X0VfRFdEOCcsJ0xPT1BEV0Q3X0RXRDhfRl9EV0Q4JywnTE9PUERXRDdfRFdEOF9HX0RXRDgnLCdMT09QRFdEN19EV0Q4X0hfRFdEOCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnTE9PUERXRDdfRFdEOF9JX0RXRDgnLCdMT09QRFdEN19EV0Q4X0pfRFdEOCcsJ0xPT1BEV0Q3X0RXRDhfS19EV0Q4JykKCmZuX3JlbmFtZV9xdWVzdGlvbl9jb2xzIDwtIGZ1bmN0aW9uKHgsIHN1ZmZpeCkgewogIHBhc3RlMChzdHJfcmVwbGFjZV9hbGwoc3RyX2V4dHJhY3QoeCwgIl9bQS1aYS16XV8iKSwgIl8iLCAiIiksICJfIiwgc3VmZml4KQp9CgpoZWFkbGluZV9yZXNwb25zZXMgPC0gCiAgcmF3X2RhdGEgJT4lCiAgICBzZWxlY3Qob25lX29mKGMoIklEIiwgaGVhZGxpbmVfb3JkZXJfY29scywgaGVhZGxpbmVfcmVjYWxsX2NvbHMsIGhlYWRsaW5lX2FjY3VyYWN5X2NvbHMpKSkgJT4lCiAgICByZW5hbWUoaWQgPSBJRCkgJT4lCiAgICByZW5hbWVfKC5kb3RzID0gc2V0TmFtZXMoaGVhZGxpbmVfb3JkZXJfY29scywgZm5fcmVuYW1lX3F1ZXN0aW9uX2NvbHMoaGVhZGxpbmVfb3JkZXJfY29scywgIm9yZGVyIikpKSAlPiUKICAgIHJlbmFtZV8oLmRvdHMgPSBzZXROYW1lcyhoZWFkbGluZV9yZWNhbGxfY29scywgZm5fcmVuYW1lX3F1ZXN0aW9uX2NvbHMoaGVhZGxpbmVfcmVjYWxsX2NvbHMsICJyZWNhbGwiKSkpICU+JQogICAgcmVuYW1lXyguZG90cyA9IHNldE5hbWVzKGhlYWRsaW5lX2FjY3VyYWN5X2NvbHMsIGZuX3JlbmFtZV9xdWVzdGlvbl9jb2xzKGhlYWRsaW5lX2FjY3VyYWN5X2NvbHMsICJhY2N1cmFjeSIpKSkgJT4lCiAgICBnYXRoZXIocXVlc3Rpb24sIHJlc3BvbnNlLCAtaWQsIG5hLnJtID0gVFJVRSkgJT4lCiAgICBzZXBhcmF0ZShjb2wgPSBxdWVzdGlvbiwgaW50byA9IGMoInF1ZXN0aW9uIiwidmFyaWFibGUiKSwgc2VwID0gIl8iKSAlPiUKICAgIHNwcmVhZCh2YXJpYWJsZSwgcmVzcG9uc2UpICU+JQogICAgbXV0YXRlKAogICAgICAgIHJlY2FsbCA9IGZhY3RvcihyZWNhbGwsIGxldmVscyA9IGMoMSwyLDMpLCBsYWJlbHMgPSBjKCJZZXMiLCJObyIsIlVuc3VyZSIpKQogICAgICAsIGFjY3VyYWN5ID0gZmFjdG9yKGFjY3VyYWN5LCBsZXZlbHMgPSBjKDEsMiwzLCA0KSwgbGFiZWxzID0gYygiVmVyeSBhY2N1cmF0ZSIsIlNvbWV3aGF0IGFjY3VyYXRlIiwiTm90IHZlcnkgYWNjdXJhdGUiLCJOb3QgYXQgYWxsIGFjY3VyYXRlIikpCiAgICAgICwgcV9pZCA9IG1hdGNoKHF1ZXN0aW9uLCBMRVRURVJTKQogICAgICAsIHJlc3BvbnNlID0gaWZfZWxzZShpcy5uYShhY2N1cmFjeSksIE5BX2NoYXJhY3Rlcl8sIGlmX2Vsc2UoYWNjdXJhY3kgJWluJSBjKCJWZXJ5IGFjY3VyYXRlIiwiU29tZXdoYXQgYWNjdXJhdGUiKSwgIlJlYWwiLCAiRmFrZSIpKQogICAgKSAlPiUKICAgIGxlZnRfam9pbihhcnRpY2xlcyAlPiUgc2VsZWN0KC11cmwpLCBieSA9IGMoInFfaWQiID0gImlkIikpICU+JQogICAgbXV0YXRlKGNvcnJlY3QgPSBmYWN0b3IodHlwZSA9PSByZXNwb25zZSwgbGFiZWxzID0gYygiTm8iICwiWWVzIikpKSAlPiUKICAgIHNlbGVjdChpZCwgcXVlc3Rpb24sIHFfaWQsIG9yZGVyLCByZWNhbGwsIGFjY3VyYWN5LCBhcnRpY2xlLCB0eXBlLCByZXNwb25zZSwgY29ycmVjdCkKCmhlYWRsaW5lX3Jlc3BvbnNlcyAlPiUgaGVhZCgpCmBgYAoKIyMgU29jaWFsIE1lZGlhIGFuZCBOZXdzIFNvdXJjZXMKClJlc3BvbmRlbnRzIHdlcmUgYXNrZWQgaG93IG9mdGVuIHRoZXkgdXNlZCBzb2NpYWwgbWVkaWEgc2l0ZXMgLyBhcHBzIHN1Y2ggYXMgRmFjZWJvb2ssIEluc3RhZ3JhbSwgUGludGVyZXN0LCBhbmQgU25hcGNoYXQuIFRoZXkgd2VyZSBhbHNvIGdpdmVuIGEgbGlzdCBvZiBuZXdzIG91dGxldHMgc3VjaCBhcyBIdWZmaW5ndG9uIFBvc3QsIE5ldyBZb3JrIFRpbWVzLCBhbmQgQnV6emZlZWQgYW5kIGFza2VkIHdoZXRoZXIgdGhleSB1c2VkIHRoaXMgb3V0bGV0IGFzIGEgc291cmNlIG9mIG5ld3MuCgpgYGB7cn0KZm5fbmV3c19tZWRpYSA8LSBmdW5jdGlvbih4KSB7CiAgbWFwcGluZyA8LSBjKCJNdWx0aXBsZSB0aW1lcyBhIGRheSIsIk9uY2UgYSBkYXkiLCJBIGZldyB0aW1lcyBhIHdlZWsiLCJPbmNlIGEgd2VlayIsIkEgZmV3IHRpbWVzIGEgbW9udGgiLAogICAgICAgICAgICAgICAiT25jZSBhIG1vbnRoIiwiTGVzcyB0aGFuIG9uY2UgYSBtb250aCIsIkkgZG9u4oCZdCB1c2UgdGhpcyBzb2NpYWwgbWVkaWEgcGxhdGZvcm0iKQogIGZhY3Rvcih4LCBsYWJlbHMgPSBtYXBwaW5nKQp9Cgpmbl9zaGFyZSA8LSBmdW5jdGlvbih4KSB7CiAgbWFwcGluZyA8LSBjKCJNdWx0aXBsZSB0aW1lcyBhIGRheSIsIk9uY2UgYSBkYXkiLCJBIGZldyB0aW1lcyBhIHdlZWsiLCJPbmNlIGEgd2VlayIsIkEgZmV3IHRpbWVzIGEgbW9udGgiLAogICAgICAgICAgICAgICAiT25jZSBhIG1vbnRoIiwiTGVzcyB0aGFuIG9uY2UgYSBtb250aCIsIlJhcmVseSAvIG5ldmVyIikKICBmYWN0b3IoeCwgbGFiZWxzID0gbWFwcGluZykKfQoKCmZuX25ld3Nfc291cmNlIDwtIGZ1bmN0aW9uKHgpIHsKICBtYXBwaW5nIDwtIGMoIklzIGEgbWFqb3Igc291cmNlIG9mIG5ld3MgZm9yIG1lIiwiSXMgYSBtaW5vciBzb3VyY2Ugb2YgbmV3cyBmb3IgbWUiLAogICAgICAgICAgICAgICAiSXMgcmFyZWx5IGEgc291cmNlIG9mIG5ld3MgZm9yIG1lIiwiSXMgbmV2ZXIgYSBzb3VyY2Ugb2YgbmV3cyBmb3IgbWUiLAogICAgICAgICAgICAgICAiSeKAmW0gbm90IGZhbWlsaWFyIHdpdGggdGhpcyBuZXdzIHNvdXJjZSIpCiAgZmFjdG9yKHgsIGxhYmVscyA9IG1hcHBpbmcpCn0KCm5ld3Nfc291cmNlX3Jlc3BvbnNlcyA8LSAKICByYXdfZGF0YSAlPiUKICAgIHJlbmFtZSgKICAgICAgICBGYWNlYm9vayAgICAgICAgICAgPSBHUklEX0RXRDlfMV9EV0Q5CiAgICAgICwgSW5zdGFncmFtICAgICAgICAgID0gR1JJRF9EV0Q5XzJfRFdEOQogICAgICAsIFBpbnRlcmVzdCAgICAgICAgICA9IEdSSURfRFdEOV8zX0RXRDkKICAgICAgLCBTbmFwY2hhdCAgICAgICAgICAgPSBHUklEX0RXRDlfNF9EV0Q5CiAgICAgICwgVHdpdHRlciAgICAgICAgICAgID0gR1JJRF9EV0Q5XzVfRFdEOQogICAgICAsIFlvdVR1YmUgICAgICAgICAgICA9IEdSSURfRFdEOV82X0RXRDkKICAgICAgLCBTaGFyZSAgICAgICAgICAgICAgPSBEV0QxMAogICAgICAsIEJ1enpGZWVkICAgICAgICAgICA9IEdSSURfRFdEMTFfMV9EV0QxMQogICAgICAsIEh1ZmZpbmd0b24uUG9zdCAgICA9IEdSSURfRFdEMTFfMl9EV0QxMQogICAgICAsIE5ldy5Zb3JrLlRpbWVzICAgICA9IEdSSURfRFdEMTFfM19EV0QxMQogICAgICAsIEZhY2Vib29rLk5ld3MgICAgICA9IEdSSURfRFdEMTFfNF9EV0QxMQogICAgICAsIFR3aXR0ZXIuTmV3cyAgICAgICA9IEdSSURfRFdEMTFfNV9EV0QxMQogICAgICAsIFNuYXBjaGF0Lk5ld3MgICAgICA9IEdSSURfRFdEMTFfNl9EV0QxMQogICAgICAsIFZJQ0UgICAgICAgICAgICAgICA9IEdSSURfRFdEMTFfN19EV0QxMQogICAgICAsIENOTiAgICAgICAgICAgICAgICA9IEdSSURfRFdEMTFfOF9EV0QxMQogICAgICAsIFZveCAgICAgICAgICAgICAgICA9IEdSSURfRFdEMTFfOV9EV0QxMQogICAgICAsIEJ1c2luZXNzLkluc2lkZXIgICA9IEdSSURfRFdEMTFfMTBfRFdEMTEKICAgICAgLCBXYXNoaW5ndG9uLlBvc3QgICAgPSBHUklEX0RXRDExXzExX0RXRDExCiAgICAgICwgR29vZ2xlLk5ld3MgICAgICAgID0gR1JJRF9EV0QxMV8xMl9EV0QxMQogICAgICAsIFlhaG9vLk5ld3MgICAgICAgICA9IEdSSURfRFdEMTFfMTNfRFdEMTEKICAgICAgLCBEcnVkZ2UuUmVwb3J0ICAgICAgPSBHUklEX0RXRDExXzE0X0RXRDExCiAgICAgICwgRm94Lk5ld3MgICAgICAgICAgID0gR1JJRF9EV0QxMV8xNV9EV0QxMQogICAgKSAlPiUKICAgIHNlbGVjdChpZCA9IElELCBGYWNlYm9vaywgSW5zdGFncmFtLCBQaW50ZXJlc3QsIFNuYXBjaGF0LCBUd2l0dGVyLCBZb3VUdWJlLCBTaGFyZSwgQnV6ekZlZWQsIAogICAgICAgICAgIEh1ZmZpbmd0b24uUG9zdCwgTmV3LllvcmsuVGltZXMsIEZhY2Vib29rLk5ld3MsIFR3aXR0ZXIuTmV3cywgU25hcGNoYXQuTmV3cywgVklDRSwgQ05OLCBWb3gsIAogICAgICAgICAgIEJ1c2luZXNzLkluc2lkZXIsIFdhc2hpbmd0b24uUG9zdCwgR29vZ2xlLk5ld3MsIFlhaG9vLk5ld3MsIERydWRnZS5SZXBvcnQsIEZveC5OZXdzKSAlPiUKICAgIG11dGF0ZV9hdCguY29scyA9IGMoIkZhY2Vib29rIiwgIkluc3RhZ3JhbSIsICJQaW50ZXJlc3QiLCAiU25hcGNoYXQiLCAiVHdpdHRlciIsICJZb3VUdWJlIiksIGZuX25ld3NfbWVkaWEpICU+JQogICAgbXV0YXRlX2F0KC5jb2xzID0gYygnU2hhcmUnKSwgZm5fc2hhcmUpICU+JQogICAgbXV0YXRlX2F0KC5jb2xzID0gYygnQnV6ekZlZWQnLCdIdWZmaW5ndG9uLlBvc3QnLCdOZXcuWW9yay5UaW1lcycsJ0ZhY2Vib29rLk5ld3MnLCdUd2l0dGVyLk5ld3MnLAogICAgICAgICAgICAgICAgICAgICAgICAnU25hcGNoYXQuTmV3cycsJ1ZJQ0UnLCdDTk4nLCdWb3gnLCdCdXNpbmVzcy5JbnNpZGVyJywnV2FzaGluZ3Rvbi5Qb3N0JywKICAgICAgICAgICAgICAgICAgICAgICAgJ0dvb2dsZS5OZXdzJywnWWFob28uTmV3cycsJ0RydWRnZS5SZXBvcnQnLCdGb3guTmV3cycpLCBmbl9uZXdzX3NvdXJjZSkKYGBgCgojIyBGaW5hbCBEYXRhc2V0CgpGb3IgYSBmaW5hbCBkYXRhc2V0LCB3ZSB1bnBpdm90ZWQgdGhlIHF1ZXN0aW9ucyBzbyB0aGF0IGVhY2ggcmVjb3JkIHJlcHJlc2VudHMgYSByZXNwb25kZW50IGFuZCBhIGhlYWRsaW5lIHRoZXkgd2VyZSBzaG93bi4gQXMgdGhlcmUgYXJlICoqYHIgcHJldHR5TnVtKGxlbmd0aCh1bmlxdWUocmF3X2RhdGEkSUQpKSwgYmlnLm1hcmsgPSAnLCcpYCoqIHJlc3BvbmRlbnRzLCB0aGVyZSBhcmUgKipgciBwcmV0dHlOdW0obGVuZ3RoKHVuaXF1ZShyYXdfZGF0YSRJRCkpLCBiaWcubWFyayA9ICcsJylgKiogJFx0aW1lcyQgNiA9ICoqYHIgcHJldHR5TnVtKG5yb3cobWFzdGVyX3RhYmxlKSwgYmlnLm1hcmsgPSAnLCcpYCoqIHJlY29yZHMgaW4gb3VyIGZpbmFsIGRhdGFzZXQuIE91ciBmaW5hbCBkYXRhc2V0IGluY2x1ZGVzICoqYHIgcHJldHR5TnVtKG5jb2wobWFzdGVyX3RhYmxlKSwgYmlnLm1hcmsgPSAnLCcpYCoqIGNvbHVtbnMsIG1hbnkgZW5naW5lZXJlZCAoaS5lLiBgY29ycmVjdGAgYXMgaW4gY29ycmVjdCBpZGVudGlmaWNhdGlvbiBvZiBhIG5ld3Mgc3RvcnkgYXMgYmVpbmcgYWNjdXJhdGUvaW5hY2N1cmF0ZSkgZnJvbSBvdGhlciB2YXJpYWJsZXMgYW5kIG51bWJlciBhcmUgZGlyZWN0bHkgZnJvbSB0aGUgb3JpZ2luYWwgZGF0YXNldC4KCmBgYHtyfQptYXN0ZXJfdGFibGUgPC0KICBoZWFkbGluZV9yZXNwb25zZXMgJT4lCiAgbGVmdF9qb2luKGRlbW9ncmFwaGljcywgYnkgPSBjKCJpZCIpKSAlPiUKICBsZWZ0X2pvaW4ocGFydHlfcmVzcG9uc2VzLCBieSA9IGMoImlkIikpICU+JQogIGxlZnRfam9pbihuZXdzX3NvdXJjZV9yZXNwb25zZXMsIGJ5ID0gYygiaWQiKSkKCm1hc3Rlcl90YWJsZSAlPiUKICAgIGhlYWQoKQpgYGAKCiMgRXhwbG9yYXRvcnkgQW5hbHlzaXMKCiMjIE92ZXJ2aWV3CgpCZWZvcmUgd2Ugc3RhcnQgbGVhcm5pbmcgYWJvdXQgdGhlIHN1cnZleSByZXNwb25kZW50cywgbGV0J3MgdW5kZXJzdGFuZCB0aGUgcHJvY2VzcyB0aGV5IHdlbnQgdGhyb3VnaCBpbiBuZXdzIGhlYWRsaW5lIGNvbXBvbmVudCBvZiB0aGUgc3VydmV5LiBUaGUgU2Fua2V5IGRpYWdyYW0gYmVsb3cgaGVscHMgY2hhcnQgb3V0IHRoaXMgcGF0aC4gVGhlIGdyZWVuIGJhciBhdCB0aGUgbGVmdCByZXByZXNlbnRzIHRoZSAzLDAxNSByZXNwb25kZW50cy4gVGhlIG1pZGRsZSBzZWN0aW9uIHJlcHJlc2VudHMgdGhlIHJlY2FsbC4gRWFjaCBwZXJzb24gY291bGQgcmVjYWxsIDAtNiBvZiB0aGUgaGVhZGxpbmVzLiBUaGUgdG9wIGJhciBzaG93cyB0aGF0IDEsMTA2IHBlb3BsZSByZWNhbGxlZCBubyBoZWFkbGluZXMgYW5kIHRodXMgd2VyZSBub3QgYXNrZWQgdG8gcmVzcG9uZCB0byB0aGVpciBhY2N1cmFjeS4gQW55IHBlcnNvbiBmcm9tIGEgYmx1ZSBib3hlcyBpbiB0aGUgbWlkZGxlIHdhcyBhc2tlZCBhYm91dCB0aGUgYWNjdXJhY3kgb2YgYXQgbGVhc3Qgb25lIGhlYWRsaW5lLiBUaGUgYm94ZXMgYXQgdGhlIHJpZ2h0IGluZGljYXRlIGhvdyBtYW55IG9mIHRoZSBoZWFkbGluZXMgdGhlIHJlc3BvbmRlbnQgaW5jb3JyZWN0bHkgcmVzcG9uZGVkIHRvIChpLmUuIGAjIHdyb25nYCkuIFRoZSBibHVlIGxpbmtzIHRvIHRoZSByZWQgYm94ZXMgYXJlIHRoZSBwZW9wbGUgd2hvIGFuc3dlcmVkIGF0IGxlYXN0IG9uZSBxdWVzdGlvbiBpbmNvcnJlY3RseS4gTGF0ZXIgb24gaW4gdGhlIGFuYWx5c2lzIHRoZSBwZW9wbGUgaW4gdGhlIGJsdWUgYm94ZXMgd2lsbCBiZSBvdXIgcG9wdWxhdGlvbiBvZiBpbnRlcmVzdCBpbiBhbnN3ZXJpbmcgdGhlIHF1ZXN0aW9uIHdoYXQgZmFjdG9ycyB1bmxpZSBvbmUncyBhYmlsaXR5IHRvIGRpc3Rpbmd1aXNoIGFuIGFjY3VyYXRlIG5ld3Mgc3RvcnkgZnJvbSBhbiBpbmFjY3VyYXRlIG9uZS4KCmBgYHtyfQpzYW5rZXlfdG1wIDwtIAogIG1hc3Rlcl90YWJsZSAlPiUKICAgIHNlbGVjdChpZCwgcmVjYWxsLCBjb3JyZWN0KSAlPiUKICAgIGdyb3VwX2J5KGlkKSAlPiUKICAgIHN1bW1hcmlzZSgKICAgICAgbnVtX3JlY2FsbCA9IHN1bShyZWNhbGwgPT0gIlllcyIsIG5hLnJtID0gVFJVRSkKICAgICAgLCBudW1fd3JvbmcgPSBzdW0oY29ycmVjdCA9PSAiTm8iLCBuYS5ybSA9IFRSVUUpCiAgICApICU+JQogICAgdW5ncm91cCgpICU+JQogICAgZ3JvdXBfYnkobnVtX3JlY2FsbCwgbnVtX3dyb25nKSAlPiUKICAgIGNvdW50KCkgJT4lIAogICAgdW5ncm91cCgpCgpzYW5rZXlfdG1wMiA8LSAKICB1bmlvbl9hbGwoCiAgICBzYW5rZXlfdG1wICU+JQogICAgICBncm91cF9ieShudW1fcmVjYWxsKSAlPiUKICAgICAgc3VtbWFyaXNlKG4gPSBzdW0obikpICU+JQogICAgICBtdXRhdGUoCiAgICAgICAgU291cmNlTmFtZSA9ICJBbGwgUmVzcG9uZGVudHMiCiAgICAgICAgLCBUYXJnZXROYW1lID0gcGFzdGUwKCJSZWNhbGxlZCAiLCBudW1fcmVjYWxsKQogICAgICAgICwgTGlua0dyb3VwID0gIkdvb2QiCiAgICAgICkgJT4lCiAgICAgIHNlbGVjdChTb3VyY2VOYW1lLCBUYXJnZXROYW1lLCBWYWx1ZSA9IG4sIExpbmtHcm91cCkKICAgICwgCiAgICBzYW5rZXlfdG1wICU+JQogICAgICBncm91cF9ieShudW1fcmVjYWxsLCBudW1fd3JvbmcpICU+JQogICAgICBzdW1tYXJpc2UobiA9IHN1bShuKSkgJT4lCiAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgbXV0YXRlKAogICAgICAgIFNvdXJjZU5hbWUgPSBwYXN0ZTAoIlJlY2FsbGVkICIsIG51bV9yZWNhbGwpCiAgICAgICAgLCBUYXJnZXROYW1lID0gaWZfZWxzZShudW1fcmVjYWxsID09IDAgJiBudW1fd3JvbmcgPT0gMCwgJ05vIFJlc3BvbnNlJywgcGFzdGUwKGFzLmNoYXJhY3RlcihudW1fd3JvbmcpLCAiIHdyb25nIikpCiAgICAgICAgLCBMaW5rR3JvdXAgPSBpZl9lbHNlKG51bV93cm9uZyA+IDAsICJCYWQiLCAiR29vZCIpCiAgICAgICkgJT4lCiAgICAgIHNlbGVjdChTb3VyY2VOYW1lLCBUYXJnZXROYW1lLCBWYWx1ZSA9IG4sIExpbmtHcm91cCkKICApCgpzYW5rZXlfbm9kZXMgPC0KICBiaW5kX3Jvd3MoCiAgICBzZWxlY3Qoc2Fua2V5X3RtcDIsIE5vZGVOYW1lID0gU291cmNlTmFtZSkKICAgICwgc2VsZWN0KHNhbmtleV90bXAyLCBOb2RlTmFtZSA9IFRhcmdldE5hbWUpCiAgICApICU+JSAKICBkaXN0aW5jdCgpICU+JQogIG11dGF0ZShOb2RlSUQgPSByb3dfbnVtYmVyKCkgLSAxKSAlPiUKICBzZWxlY3QoTm9kZUlELCBOb2RlTmFtZSkKCgpzYW5rZXlfbGlua3MgPC0KICBzYW5rZXlfdG1wMiAlPiUKICBsZWZ0X2pvaW4oCiAgICBzYW5rZXlfbm9kZXMsIGJ5ID0gYygnU291cmNlTmFtZScgPSAiTm9kZU5hbWUiKQogICkgJT4lIAogIHJlbmFtZShTb3VyY2UgPSBOb2RlSUQpICU+JQogIGxlZnRfam9pbigKICAgIHNhbmtleV9ub2RlcywgYnkgPSBjKCdUYXJnZXROYW1lJyA9ICJOb2RlTmFtZSIpCiAgKSAlPiUgCiAgcmVuYW1lKFRhcmdldCA9IE5vZGVJRCkgJT4lCiAgc2VsZWN0KFNvdXJjZSwgVGFyZ2V0LCBWYWx1ZSwgTGlua0dyb3VwKQogIAogIApzYW5rZXlfbm9kZXMgPC0KICBzYW5rZXlfbm9kZXMgJT4lCiAgbXV0YXRlKE5vZGVHcm91cCA9IAogICAgICAgICAgIGlmZWxzZShOb2RlTmFtZSAlaW4lIGMoIlJlY2FsbGVkIDAiLCAiTm8gUmVzcG9uc2UiLCIwIHdyb25nIiksICJPdGhlciIsIAogICAgICAgICAgICAgaWZlbHNlKHN0cl9kZXRlY3QoTm9kZU5hbWUsICJSZWNhbGwiKSwgIlJlY2FsbCIsIAogICAgICAgICAgICAgICAgICBpZmVsc2Uoc3RyX2RldGVjdChOb2RlTmFtZSwgIndyb25nIiksICJXcm9uZyIsICJBbGwiCiAgICAgICAgICAgICAgICAgICAgICApKSkKICApCgoKQ29sb3VyU2NhbGUgPC0gJ2QzLnNjYWxlLm9yZGluYWwoKQogICAgICAgICAgICAuZG9tYWluKFsiT3RoZXIiLCAiUmVjYWxsIiwiV3JvbmciLCJBbGwiXSkKICAgICAgICAgICAucmFuZ2UoWyIjRDJEMkQyIiwgIiMwMDhGRDUiLCIjRkYyNzAwIiwiIzc3QUI0MyJdKTsnCmBgYAoKYGBge3J9CnNhbmtleU5ldHdvcmsoTGlua3MgPSBzYW5rZXlfbGlua3MsIE5vZGVzID0gc2Fua2V5X25vZGVzLCBTb3VyY2UgPSAiU291cmNlIiwgVGFyZ2V0ID0gIlRhcmdldCIsIFZhbHVlID0gIlZhbHVlIiwgCiAgICAgICAgICAgICAgTm9kZUlEID0gIk5vZGVOYW1lIiwgTGlua0dyb3VwID0gIkxpbmtHcm91cCIsIHVuaXRzID0gInBlb3BsZSIsIE5vZGVHcm91cCA9ICJOb2RlR3JvdXAiLCBjb2xvdXJTY2FsZSA9IG5ldHdvcmtEMzo6SlMoQ29sb3VyU2NhbGUpLCB3aWR0aCA9IDcwMCwgaGVpZ2h0ID0gNDUwLAogICAgICAgICAgICAgIGZvbnRGYW1pbHkgPSBpZmVsc2UoU3lzLmluZm8oKVtbJ3VzZXInXV0gJWluJSB1c2Vyc192LCJEZWNpbWFNb25vUHJvIiwgIkhlbHZldGljYSIpLCBub2RlV2lkdGggPSAzNSwgZm9udFNpemUgPSAxMikKYGBgCgoKIyMgRGVtb2dyYXBoaWNzCgojIyMgQWdlCgpUaGUgb25seSBjb250aW5vdXMgbnVtZXJpYyB2YXJpYWJsZSBpbiBvdXIgZGF0YXNldCBpcyBhZ2UuIEJlbG93IGlzIGEgaGlzdG9ncmFtOgoKYGBge3J9Cm1hc3Rlcl90YWJsZSAlPiUKICAgIHNlbGVjdChpZCwgYWdlKSAlPiUKICAgIGRpc3RpbmN0KCkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBhZ2UpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBmaWxsID0gcGFsNTM4W1snYmx1ZSddXSkgKwogICAgdGhlbWVfanJmKCkgKwogICAgbGFicyh4ID0gIkFnZSIsIHkgPSAiIyBSZXNwb25kZW50cyIsIHRpdGxlID0gIkFnZSIpCiAgICAKYGBgCgojIyMgRmFjdG9yIFZhcmlhYmxlcwoKQXMgdGhlIG1ham9yaXR5IG9mIHRoZSByZW1haW5pbmcgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24gYXJlIGZhY3RvciB2YXJpYWJsZXMsIHdlIHdpbGwgdXNlIHRyZWVtYXBzIHRvIHNob3cgd2hhdCBwZXJjZW50IG9mIHRoZSB3aG9sZSAzLDAxNSByZXNwb25kZW50IHBvb2wgYXJlIGluIGVhY2ggY2F0ZWdvcnkuCgpgYGB7cn0KZm5fdHJlZW1hcCA8LSBmdW5jdGlvbihkZiwgZ3JvdXAsIHRpdGxlKSB7CiAgICAKICAgIGRmMiA8LSAKICAgICAgICBkZiAlPiUKICAgICAgICBzZWxlY3Qob25lX29mKGMoImlkIiwgZ3JvdXApKSkgJT4lCiAgICAgICAgZGlzdGluY3QoKSAlPiUKICAgICAgICBncm91cF9ieV8oLmRvdHMgPSBsYXBwbHkoYyhncm91cCksIGFzLnN5bWJvbCkpICU+JQogICAgICAgIGNvdW50KCkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIG11dGF0ZShwZXJjZW50ID0gcm91bmQoMTAwICogbiAvIHN1bShuKSwgMikpICU+JQogICAgICAgIHJlbmFtZV8oLmRvdHMgPSBzZXROYW1lcyhncm91cCwgImdyb3VwIikpCgogICAgaGNoYXJ0KGRmMiwgInRyZWVtYXAiLCB4ID0gZ3JvdXAsIHkgPSBwZXJjZW50LCB2YWx1ZSA9IG4sIGNvbG9yID0gcGVyY2VudCkgJT4lCiAgICAgICAgaGNfdG9vbHRpcChwb2ludEZvcm1hdCA9ICI8Yj57cG9pbnQubmFtZX08L2I+OiB7cG9pbnQudmFsdWV9PC9iPiAoPGI+e3BvaW50Lnl9JTwvYj4pPGJyLz4iKSAlPiUKICAgICAgICBoY190aXRsZSh0ZXh0ID0gdGl0bGUpICU+JQogICAgICAgIGhjX2FkZF90aGVtZShoY190aGVtZV81MzgoKSkKICAgIAp9Cgpmbl90cmVlbWFwKG1hc3Rlcl90YWJsZSwgImdlbmRlciIsICJHZW5kZXIiKQpmbl90cmVlbWFwKG1hc3Rlcl90YWJsZSwgInJhY2UiLCAiUmFjZSIpCmZuX3RyZWVtYXAobWFzdGVyX3RhYmxlLCAiaGlzcGFuaWMiLCAiSGlzcGFuaWMiKQpmbl90cmVlbWFwKG1hc3Rlcl90YWJsZSwgImVkdWNhdGlvbiIsICJFZHVjYXRpb24iKQpmbl90cmVlbWFwKG1hc3Rlcl90YWJsZSwgIm1hcml0YWxfc3RhdHVzIiwgIk1hcml0YWwgU3RhdHVzIikKZm5fdHJlZW1hcChtYXN0ZXJfdGFibGUsICJlbXBsb3ltZW50IiwgIkVtcGxveW1lbnQiKQpmbl90cmVlbWFwKG1hc3Rlcl90YWJsZSwgImhvdXNlaG9sZF9pbmNvbWUiLCAiSG91c2Vob2xkIEluY29tZSIpCmZuX3RyZWVtYXAobWFzdGVyX3RhYmxlLCAicmVnaW9uIiwgIlJlZ2lvbiIpCmZuX3RyZWVtYXAobWFzdGVyX3RhYmxlLCAibWFya2V0X3NpemUiLCAiTWFya2V0IFNpemUiKQpgYGAKCiMjIFBvbGl0aWNzIFJlc3BvbnNlcwoKU2ltaWxhciB0byBkZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiwgd2Ugd2lsbCBsb29rIGF0IHRoZSBjb21wb3NpdGlvbiBvZiB0aGUgcGFydHkgYWZmaWxpYXRpb24gYW5kIHdobyByZXNwb25kZW50cyB2b3RlZCBmb3IgYXMgdHJlZW1hcHMuCgpgYGB7cn0KZGYzIDwtIAogICAgcGFydHlfcmVzcG9uc2VzICU+JQogICAgICAgIGdyb3VwX2J5KHBhcnR5LCBzdWJfcGFydHkpICU+JQogICAgICAgIGNvdW50KCkgJT4lCiAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgIG11dGF0ZSh5ID0gcm91bmQoMTAwICogbiAvIHN1bShuKSwgMikpICU+JQogICAgICAgIG11dGF0ZShjb2xvciA9IGlmZWxzZShwYXJ0eSA9PSAiRGVtb2NyYXQiLCBwYWw1MzhbWydibHVlJ11dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHBhcnR5ID09ICJSZXB1YmxpY2FuIiwgcGFsNTM4W1sncmVkJ11dLCBwYWw1MzhbWydtZWRncmF5J11dKSkpICU+JQogICAgICAgIHJlbmFtZSh4ID0gc3ViX3BhcnR5LCB2YWx1ZSA9IG4pCgoKaGNoYXJ0KGRmMywgInRyZWVtYXAiLCB4ID0geCkgJT4lCiAgICBoY190b29sdGlwKHBvaW50Rm9ybWF0ID0gIjxiPntwb2ludC5uYW1lfTwvYj46IHtwb2ludC52YWx1ZX08L2I+ICg8Yj57cG9pbnQueX0lPC9iPik8YnIvPiIpICU+JQogICAgaGNfdGl0bGUodGV4dCA9ICJQYXJ0eSBBZmZpbGlhdGlvbiIpICU+JQogICAgaGNfYWRkX3RoZW1lKGhjX3RoZW1lXzUzOCgpKQpgYGAKCmBgYHtyfQpkZjQgPC0gCiAgICBwYXJ0eV9yZXNwb25zZXMgJT4lCiAgICAgICAgZ3JvdXBfYnkoY2FuZGlkYXRlKSAlPiUKICAgICAgICBjb3VudCgpICU+JQogICAgICAgIHVuZ3JvdXAoKSAlPiUKICAgICAgICBtdXRhdGUoeSA9IHJvdW5kKDEwMCAqIG4gLyBzdW0obiksIDIpKSAlPiUKICAgICAgICBtdXRhdGUoY29sb3IgPSBpZmVsc2UoY2FuZGlkYXRlID09ICJUcnVtcCIsIHBhbDUzOFtbJ3JlZCddXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShjYW5kaWRhdGUgPT0gIkNsaW50b24iLCBwYWw1MzhbWydibHVlJ11dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGNhbmRpZGF0ZSA9PSAiSm9obnNvbiIsIHBhbDUzOFtbJ2dyZWVuJ11dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGNhbmRpZGF0ZSA9PSAiU3RlaW4iLCAiIzU1MUE4QiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShjYW5kaWRhdGUgPT0gIk90aGVyIiwgcGFsNTM4W1snbWVkZ3JheSddXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFsNTM4W1snZGtncmF5J11dKSkpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICApICU+JQogICAgICAgIHJlbmFtZSh4ID0gY2FuZGlkYXRlLCB2YWx1ZSA9IG4pCgoKaGNoYXJ0KGRmNCwgInRyZWVtYXAiLCB4ID0geCkgJT4lCiAgICBoY190b29sdGlwKHBvaW50Rm9ybWF0ID0gIjxiPntwb2ludC5uYW1lfTwvYj46IHtwb2ludC52YWx1ZX08L2I+ICg8Yj57cG9pbnQueX0lPC9iPik8YnIvPiIpICU+JQogICAgaGNfdGl0bGUodGV4dCA9ICJDYW5kaWRhdGUiKSAlPiUKICAgIGhjX2FkZF90aGVtZShoY190aGVtZV81MzgoKSkKYGBgCgojIyBOZXdzIFNvdXJjZXMKClJlcG9uZGVudHMgd2VyZSBhc2tlZCBhYm91dCB0aGUgZGlmZmVyZW50IHNvY2lhbCBtZWRpYSBzZXJ2aWNlcyB0aGV5IHVzZWQgYW5kIGhvdyBvZnRlbi4gTm90ZSBob3cgdWJpcXVpdG91cyBGYWNlYm9vayBpcyAtIDQ3JSBvZiByZXNwb25kZW50cyB1c2UgaXQgKm11bHRpcGxlKiB0aW1lcyBhIGRheSEgCgpgYGB7ciBTb2NpYWwgTWVkaWF9Cm1hc3Rlcl90YWJsZSAlPiUKICBzZWxlY3QoaWQsIGNhbmRpZGF0ZSwgRmFjZWJvb2ssIEluc3RhZ3JhbSwgUGludGVyZXN0LCBTbmFwY2hhdCwgVHdpdHRlciwgWW91VHViZSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBnYXRoZXIoc29jaWFsX21lZGlhX291dGxldCwgZnJlcXVlbmN5LCAtaWQsIC1jYW5kaWRhdGUpICU+JQogIGdyb3VwX2J5KHNvY2lhbF9tZWRpYV9vdXRsZXQsIGZyZXF1ZW5jeSkgJT4lCiAgY291bnQoKSAlPiUKICBtdXRhdGUocGVyY2VudCA9IG4gLyBzdW0obikpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoCiAgICBzb2NpYWxfbWVkaWFfb3V0bGV0ID0gZmFjdG9yKHNvY2lhbF9tZWRpYV9vdXRsZXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkZhY2Vib29rIiwgIkluc3RhZ3JhbSIsICJQaW50ZXJlc3QiLCAiU25hcGNoYXQiLCAiVHdpdHRlciIsICJZb3VUdWJlIikpCiAgICAsIGZyZXF1ZW5jeSA9IGZhY3RvcihmcmVxdWVuY3ksIGxldmVscyA9IGMoIk11bHRpcGxlIHRpbWVzIGEgZGF5IiwiT25jZSBhIGRheSIsIkEgZmV3IHRpbWVzIGEgd2VlayIsIk9uY2UgYSB3ZWVrIiwiQSBmZXcgdGltZXMgYSBtb250aCIsCiAgICAgICAgICAgICAgICJPbmNlIGEgbW9udGgiLCJMZXNzIHRoYW4gb25jZSBhIG1vbnRoIiwiSSBkb27igJl0IHVzZSB0aGlzIHNvY2lhbCBtZWRpYSBwbGF0Zm9ybSIpKQogICkgJT4lICAKICBtdXRhdGUoZnJlcXVlbmN5ID0gZmN0X3JlY29kZShmcmVxdWVuY3ksIE5ldmVyID0gIkkgZG9u4oCZdCB1c2UgdGhpcyBzb2NpYWwgbWVkaWEgcGxhdGZvcm0iKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZnJlcXVlbmN5LCB5ID0gZmN0X3Jldihzb2NpYWxfbWVkaWFfb3V0bGV0KSkpICsKICBnZW9tX3BvaW50KGFlcyhzaXplID0gcGVyY2VudCwgY29sb3VyID0gcGVyY2VudCksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIHNoYXBlID0gMTUpICsKICBndWlkZXMoc2l6ZSA9IEZBTFNFLCBjb2xvdXIgPSBGQUxTRSkgKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoNiwxNSkpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gcGFsNTM4W1snbWVkZ3JheSddXSwgaGlnaCA9IHBhbDUzOFtbJ3JlZCddXSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMjUpKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBlcmNlbnQocGVyY2VudCkpLCBzaXplID0gMywgZmFtaWx5ID0gaWZlbHNlKFN5cy5pbmZvKClbWyd1c2VyJ11dICVpbiUgdXNlcnNfdiwiRGVjaW1hTW9ub1BybyIsICJIZWx2ZXRpY2EiKSkgKyAKICB0aGVtZV9qcmYoKSArIAogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMLCB0aXRsZSA9ICJIb3cgZnJlcXVlbnRseSBkbyB5b3UgdmlzaXQgZWFjaCBvZiB0aGVcbmZvbGxvd2luZyBzb2NpYWwgbWVkaWEgd2Vic2l0ZXMgLyBhcHBzPyIsCiAgICAgICBjYXB0aW9uID0gIjMsMDE1IHJlc3BvbmRlbnRzIikgKwogIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAtLjA1KSkKYGBgCgojIyBOZXdzIFNvdXJjZXMKClJlc3BvbmRlbnRzIHdlcmUgYWxzbyBhc2tlZCB3aGVyZSB0aGV5IGNvbnN1bWVkIG5ld3MuIFRoZSBtYXRyaXggYmVsb3cgc2hvd3MgdGhlIGRpdmVyc2l0eSBidXQgYWxzbyBob3cgdHJhZGl0aW9uYWwgY2FibGUgbmV0d29ya3Mgc3RpbGwgcGxheSBhIGJpZyByb2xlOiAyNSUgb2YgcmVzcG9uZGVudHMgY29uc2lkZXIgRm94IE5ld3MgYW5kIENOTiB0byBiZSBtYWpvciBuZXcgc291cmNlcyBmb3IgdGhlbS4gVGhpcyBpcyBhbHNvIHJlcHJlc2VudGF0aXZlIG9mIHRoZSBhZ2UgZGlzdHJpYnV0aW9uIGluIHRoZSBzYW1wbGUuIE1hbnkgcGVvcGxlIHNheSB0aGV5IGhhdmUgbmV2ZXIgY29uc3VtZWQgbmV3cyBmcm9tIFZveCwgU25hcGNoYXQsIG9yIFR3aXR0ZXIgLSB0aGlzIHdvdWxkIG5vdCBiZSB0aGUgY2FzZSBpZiB0aGUgcG9wdWxhdGlvbiBza2V3ZWQgeW91bmdlci4KCmBgYHtyIE5ldyBTb3VyY2VzLCBmaWcuaGVpZ2h0ID0gMTB9Cm1hc3Rlcl90YWJsZSAlPiUKICBzZWxlY3QoaWQsIGNhbmRpZGF0ZSwgQnV6ekZlZWQsIAogICAgICAgICAgIEh1ZmZpbmd0b24uUG9zdCxOZXcuWW9yay5UaW1lcywgRmFjZWJvb2suTmV3cywgVHdpdHRlci5OZXdzLCBTbmFwY2hhdC5OZXdzLCBWSUNFLCBDTk4sIFZveCwgCiAgICAgICAgICAgQnVzaW5lc3MuSW5zaWRlciwgV2FzaGluZ3Rvbi5Qb3N0LCBHb29nbGUuTmV3cywgWWFob28uTmV3cywgRHJ1ZGdlLlJlcG9ydCwgRm94Lk5ld3MpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgZ2F0aGVyKG5ld3Nfc291cmNlLCBmcmVxdWVuY3ksIC1pZCwgLWNhbmRpZGF0ZSkgJT4lCiAgZ3JvdXBfYnkobmV3c19zb3VyY2UsIGZyZXF1ZW5jeSkgJT4lCiAgY291bnQoKSAlPiUKICBtdXRhdGUocGVyY2VudCA9IG4gLyBzdW0obikpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoCiAgICBuZXdzX3NvdXJjZSA9IGZhY3RvcihuZXdzX3NvdXJjZSwgCiAgICAgICAgICAgICBsZXZlbHMgPSBjKCdCdXp6RmVlZCcsJ0h1ZmZpbmd0b24uUG9zdCcsJ05ldy5Zb3JrLlRpbWVzJywnRmFjZWJvb2suTmV3cycsJ1R3aXR0ZXIuTmV3cycsCiAgICAgICAgICAgICAgICAgICAgICAgICdTbmFwY2hhdC5OZXdzJywnVklDRScsJ0NOTicsJ1ZveCcsJ0J1c2luZXNzLkluc2lkZXInLCdXYXNoaW5ndG9uLlBvc3QnLAogICAgICAgICAgICAgICAgICAgICAgICAnR29vZ2xlLk5ld3MnLCdZYWhvby5OZXdzJywnRHJ1ZGdlLlJlcG9ydCcsJ0ZveC5OZXdzJyksCiAgICAgICAgICAgICBsYWJlbHMgPSBjKCdCdXp6RmVlZCcsJ0h1ZmZpbmd0b24gUG9zdCcsJ05ldyBZb3JrIFRpbWVzJywnRmFjZWJvb2snLCdUd2l0dGVyJywnU25hcGNoYXQnLAogICAgICAgICAgICAgICAgICAgICAgICAnVklDRScsJ0NOTicsJ1ZveCcsJ0J1c2luZXNzIEluc2lkZXInLCdXYXNoaW5ndG9uIFBvc3QnLCdHb29nbGUgTmV3cycsJ1lhaG9vIE5ld3MnLAogICAgICAgICAgICAgICAgICAgICAgICAnRHJ1ZGdlIFJlcG9ydCcsJ0ZveCBOZXdzJykpCiAgICAsIGZyZXF1ZW5jeSA9IGZhY3RvcihmcmVxdWVuY3ksIGxldmVscyA9IGMoIklzIGEgbWFqb3Igc291cmNlIG9mIG5ld3MgZm9yIG1lIiwiSXMgYSBtaW5vciBzb3VyY2Ugb2YgbmV3cyBmb3IgbWUiLAogICAgICAgICAgICAgICAiSXMgcmFyZWx5IGEgc291cmNlIG9mIG5ld3MgZm9yIG1lIiwiSXMgbmV2ZXIgYSBzb3VyY2Ugb2YgbmV3cyBmb3IgbWUiLAogICAgICAgICAgICAgICAiSeKAmW0gbm90IGZhbWlsaWFyIHdpdGggdGhpcyBuZXdzIHNvdXJjZSIpLCAKICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTWFqb3IiLCAiTWlub3IiLCJSYXJlbHkiLCJOZXZlciIsIk5vdCBGYW1pbGlhciIpKQogICAgCiAgKSAlPiUgIAogICNtdXRhdGUoZnJlcXVlbmN5ID0gZmN0X3JlY29kZShmcmVxdWVuY3ksIGBSYXJlbHkgLyBOZXZlcmAgPSAiSSBkb27igJl0IHVzZSB0aGlzIHNvY2lhbCBtZWRpYSBwbGF0Zm9ybSIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBmcmVxdWVuY3ksIHkgPSBmY3RfcmV2KG5ld3Nfc291cmNlKSkpICsKICBnZW9tX3BvaW50KGFlcyhzaXplID0gcGVyY2VudCwgY29sb3VyID0gcGVyY2VudCksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIHNoYXBlID0gMTUpICsKICBndWlkZXMoc2l6ZSA9IEZBTFNFLCBjb2xvdXIgPSBGQUxTRSkgKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoNiwxNykpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gcGFsNTM4W1snbWVkZ3JheSddXSwgaGlnaCA9IHBhbDUzOFtbJ3JlZCddXSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogICN0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI1KSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwZXJjZW50KHBlcmNlbnQpKSwgc2l6ZSA9IDQsIGZhbWlseSA9IGlmZWxzZShTeXMuaW5mbygpW1sndXNlciddXSAlaW4lIHVzZXJzX3YsIkRlY2ltYU1vbm9Qcm8iLCAiSGVsdmV0aWNhIikpICsgCiAgdGhlbWVfanJmKCkgKyAKICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgdGl0bGUgPSAiSG93IG1ham9yIG9yIG1pbm9yIG9mIGEgc291cmNlIGl0IGlzIGZvciB5b3Ugd2hlbiByZWFkaW5nIG5ld3MgYW5kIGN1cnJlbnQgZXZlbnRzIG9ubGluZT8iLAogICAgICAgY2FwdGlvbiA9ICIzLDAxNSByZXNwb25kZW50cyIpICsKICB0aGVtZSh0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpCmBgYAoKIyMgR2VvZ3JhcGh5CgpUaGUgZGF0YXNldCBpbmNsdWRlZCB6aXBjb2RlIHNvIHdlIHdlcmUgYWJsZSB0byBtYXAgdGhlc2UgdG8gc2hvdyB3aGVyZSB0aGUgMywwMTUgcmVzcG9uZGVudHMgY29tZSBmcm9tLgoKYGBge3J9CmRhdGEoemlwY29kZSkKCm1hcF96aXBjb2RlcyA8LSAKICBtYXN0ZXJfdGFibGUgJT4lCiAgICBzZWxlY3QoaWQsIHppcF9jb2RlLCBhZ2UsIHJhY2UsIGdlbmRlcikgJT4lCiAgICBtdXRhdGUoZmlsbCA9IHBhc3RlMCgiQWdlOiAiLCBhZ2UsICI8YnI+UmFjZTogIiwgcmFjZSwgIjxicj5HZW5kZXI6ICIsIGdlbmRlcikpICU+JQogICAgZGlzdGluY3QoKSAlPiUKICAgIGxlZnRfam9pbigKICAgICAgICAgIHppcGNvZGUgJT4lCiAgICAgICAgICAgICAgICBhc190aWJibGUoKSAlPiUKICAgICAgICAgICAgICAgIHNlbGVjdCh6aXAsIGxhdGl0dWRlLCBsb25naXR1ZGUpCiAgICAgICwgYnkgPSBjKCJ6aXBfY29kZSIgPSAiemlwIikKICAgICkgCgpsZWFmbGV0KGRhdGEgPSBtYXBfemlwY29kZXMsIHdpZHRoID0gNzAwLCBoZWlnaHQ9IDQ1MCkgJT4lIAogIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIuUG9zaXRyb24iKSAlPiUKICBhZGRUaWxlcygpICU+JQogIGFkZENpcmNsZU1hcmtlcnMofmxvbmdpdHVkZSwgfmxhdGl0dWRlLCBwb3B1cCA9IH5maWxsLCByYWRpdXMgPSAzLCBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjgsIGZpbGxDb2xvciA9IHBhbDUzOFtbJ2dyZWVuJ11dKSAlPiUKICBzZXRWaWV3KGxuZyA9IC05Ny4xMSwgbGF0ID0gMzcuNjksIHpvb20gPSAzKQpgYGAKCkZ1dGhlcm1vcmUsIHdlIHVzZWQgdGhlIG5ldyBbdGlsZWdyYW0gcHJvZ3JhbV0oaHR0cHM6Ly9waXRjaGludGVyYWN0aXZlaW5jLmdpdGh1Yi5pby90aWxlZ3JhbXMvKSBjcmVhdGVkIGJ5IEdvb2dsZSB0byBzaG93IGVhY2ggVVMgc3RhdGUgaW4gcHJvcG9ydGlvbiB0byB0aGUgbnVtYmVyIG9mIHJlc3BvbmRlbnRzIGluIHRoZSBzdXJ2ZXkuIEFzIGV4cGVjdGVkLCB0aGUgbW9zdCBwb3B1bGF0aW9ucyBzdGF0ZXMgKENBLCBGTCwgTlksIFRYKSBhcmUgbW9zdCByZXByZXNlbnRlZCBoZXJlLiAKCmBgYHtyfQpzdGF0ZV9maXBzIDwtCiAgcmVhZF9odG1sKCJodHRwOi8vd3d3LmNvbHVtYmlhLmVkdS9+c3VlL3N0YXRlLWZpcHMuaHRtbCIpICU+JSAKICBodG1sX25vZGUoInRhYmxlIikgJT4lIAogIGh0bWxfdGFibGUoaGVhZGVyID0gVFJVRSkgJT4lIAogIHJlbmFtZShmaXBzID0gYEZJUFMgQ29kZWAsIHN0YXRlID0gYFN0YXRlIG9yIERpc3RyaWN0YCkgJT4lCiAgbXV0YXRlKHN0YXRlID0gaWZfZWxzZShzdGF0ZSA9PSAiR2VvZ2lhIiwgIkdlb3JnaWEiLCBzdGF0ZSkpICU+JQogICAgaW5uZXJfam9pbigKICAgICAgICBtYXN0ZXJfdGFibGUgJT4lCiAgICAgICAgc2VsZWN0KGlkLCBzdGF0ZSkgJT4lCiAgICAgICAgZGlzdGluY3QoKSAlPiUKICAgICAgICBncm91cF9ieShzdGF0ZSkgJT4lCiAgICAgICAgY291bnQoKQogICAgKSAlPiUKICAgIHNlbGVjdChmaXBzLCBuKSAlPiUKICAgIG11dGF0ZShmaXBzID0gc3RyX3BhZChmaXBzLCAyLCAibGVmdCIsICIwIikpCgp3cml0ZV9jc3Yoc3RhdGVfZmlwcywgcGF0aCA9IHBhc3RlMChkYXRhX2Rpciwic3RhdGVfZmlwcy5jc3YiKSkKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKHZpel9kaXIsICJ0aWxlZ3JhbV9yZXNwb25kZW50cy5wbmciKSkKYGBgCgoKIyBNb2RlbCBCdWlsZGluZwoKRHVlIHRvIGhvdyB0aGUgc3VydmV5IHdhcyBjb25kdWN0ZWQsIGlmIHdlIHdhbnQgdG8gc2hvdyB0aGF0IHRoZXJlIGFyZSBmYWN0b3JzIGFzc29jaWF0ZWQgd2l0aCBiZWluZyBhYmxlIHRvIGNvcnJlY3RseSBkaXN0aW5ndWlzaCByZWFsIGFuZCBmYWtlIG5ld3MgaGVhZGxpbmVzLCB3ZSBuZWVkIHRvIHNob3cgdGhhdCB0aGVzZSBmYWN0b3JzIGFyZSBub3QgdGhlIHNhbWUgYXMgdGhvc2UgdGhhdCBwcmVkaWN0IHdoZXRoZXIgb25lIGNhbiByZWNhbGwgYSBoZWFkbGluZS4gRm9yIGV4YW1wbGUsIGlmIGJlaW5nIG1hcnJpZWQgd2FzIGFzc29jaWF0ZWQgd2l0aCByZWNhbGxpbmcgaGVhZGxpbmVzIGFuZCBhc3NvY2lhdGVkIHdpdGggYmVpbmcgYWJsZSB0byBjb3JyZWN0bHkgaWRlbnRpZnkgcmVhbCBhbmQgZmFrZSBuZXdzIGhlYWRsaW5lcywgd2Ugd291bGQgbm90IGJlIGFibGUgdG8gc2F5IHRoYXQgYmVpbmcgbWFycmllZCBpcyBmYWN0b3IgY2F1c2luZyBwcm9wZXIgaWRlbnRpZmljYXRpb24gYmVjYXVzZSByZWNhbGwgYW5kIGlkZW50aWZpY2F0aW9uIGFyZSBsaW5rZWQuCgpXaGF0IHdlIHNob3cgaW4gdGhlIGFuYWx5c2lzIGJlbG93IGlzIHRoYXQgbmV3cyBzb3VyY2VzIGFuZCBzb2NpYWwgbWVkaWEgdXNhZ2UgYXJlIHRoZSBkZXRlcm1pbmFudHMgdW5kZXJwaW5uaW5nIHRoZSBudW1iZXIgb2YgaGVhZGxpbmVzIHJlY2FsbGVkIChyZWdhcmRsZXNzIG9mIHJlYWwsIGZha2UsIGFuZCBhbGwpLiBIb3dldmVyLCB3ZSBzaG93IHRoYXQgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIG90aGVyIGZhY3RvcnMgYXNzb2NpYXRlZCB3aXRoIHByb3BlciBpZGVudGlmaWNhdGlvbiBvZiByZWFsIGFuZCBmYWtlIGhlYWRsaW5lcywgbW9zdCBub3RlYWJseSBhZ2UgYW5kIGNhbmRpZGF0ZSB2b3RlZCBmb3IgaW4gdGhlIDIwMTYgcHJlc2lkZW50aWFsIGVsZWN0aW9uLgoKIyMgUmVjYWxsaW5nIEhlYWRsaW5lcwoKYGBge3J9CnJlY2FsbF9kZiA8LQogICAgbWFzdGVyX3RhYmxlICU+JQogICAgZ3JvdXBfYnkoaWQpICU+JQogICAgbXV0YXRlKAogICAgICAgIG51bV9yZWNhbGxfYWxsID0gc3VtKHJlY2FsbCA9PSAiWWVzIikKICAgICAgICAsIG51bV9yZWNhbGxfdHJ1ZSA9IHN1bShyZWNhbGwgPT0gIlllcyIgJiB0eXBlID09ICJSZWFsIikKICAgICAgICAsIG51bV9yZWNhbGxfZmFsc2UgPSBzdW0ocmVjYWxsID09ICJZZXMiICYgdHlwZSA9PSAiRmFrZSIpCiAgICApICU+JQogICAgdW5ncm91cCgpICU+JQogICAgc2VsZWN0KC1xdWVzdGlvbiwgLXFfaWQsIC1vcmRlciwgLXR5cGUsIC1yZXNwb25zZSwgLWNvcnJlY3QsIC1hcnRpY2xlLCAtcmVjYWxsLCAtdHlwZSwgLWFjY3VyYWN5KSAlPiUKICAgIGRpc3RpbmN0KCkgJT4lCiAgICBzZWxlY3QoLWlkLCAtemlwX2NvZGUsIC1jaXR5KQpgYGAKCgpXZSB3aWxsIGZpcnN0IGF0dGVtcHQgdG8gYnVpbGQgYSBtb2RlbCB0byBwcmVkaWN0IHdoZXRoZXIgb3Igbm90IEFtZXJpY2FucyBjYW4gcmVjYWxsIG9uZSBvZiB0aGUgbmV3cyBoZWFkbGluZXMuIFdlIHdpbGwgc3BsaXQgdGhpcyBwcm9jZXNzIGludG86CgoxLiBJZGVudGlmeWluZyBhbnkgaGVhZGxpbmUKMi4gSWRlbnRpZnlpbmcgYW4gaW5hY2N1cmF0ZSAoZmFsc2UpIGhlYWRsaW5lCjMuIElkZW50eWluZyBhbiBhY2N1cmF0ZSAodHJ1ZSkgaGVhZGxpbmUKCgpXZSB3aWxsIG5vdCBiZSB1c2luZyB0aGlzIGZvciBwcmVkaWN0aW9uLCBidXQgcmF0aGVyIHRvIHVuZGVyc3RhbmQgdGhlIGZhY3RvcnMgdGhhdCBjb3JyZWxhdGUgd2l0aCBzb21lb25lJ3MgYWJpbGl0eSB0byByZWNhbGwgdGhlc2UgcGFydGljdWxhciAxMSBoZWFkbGluZXMuIEZvciB0aGF0IHJlYXNvbiwgd2UgYXJlIG5vdCB1c2luZyB0cmFpbmluZyBhbmQgdGVzdCBkYXRhc2V0cyBoZXJlLiBXZSB3aWxsIGJlIGJ1aWxkaW5nIHRocmVlIG1vZGVscyB0byBzZWUgaWYgdGhlIGZhY3RvcnMgdGhhdCBpbmZsdWVuY2Ugb25lJ3MgYWJpbGl0eSB0byByZWNhbGwgYSByZWFsIG5ld3MgaGVhZGxpbmUgYXJlIHRoZSBzYW1lIGFzIHRoZSBvbmVzIHRoYXQgaW5mbHVlbmNlIHRoZSBhYmlsaXR5IHRvIHJlY2FsbCBhIGZha2UgbmV3cyBoZWFkbGluZS4gSW4gZ2VuZXJhbCwgd2Ugc2VlIHRoYXQgcmVzcG9uZGVudHMgcmVjYWxsICoqYHIgcm91bmQobWVhbihyZWNhbGxfZGYkbnVtX3JlY2FsbF9hbGwpLCAyKWAqKiBoZWFkbGluZXMsIGJ1dCBhcmUgbW9yZSBsaWtlbHkgdG8gcmVjYWxsIHRydWUgaGVhZGxpbmVzIHRoYW4gZmFrZSBoZWFkbGluZXMgKHRoYW5rIGdvb2RuZXNzKS4KCmBgYHtyIHJlc3VsdHMgPSAnYXNpcyd9CnJlY2FsbF9kZiAlPiUgCiAgc3VtbWFyaXNlKAogICAgQWxsID0gbWVhbihudW1fcmVjYWxsX2FsbCkKICAgICwgUmVhbCA9IG1lYW4obnVtX3JlY2FsbF90cnVlKQogICAgLCBGYWtlID0gbWVhbihudW1fcmVjYWxsX2ZhbHNlKQogICkgJT4lCiAgZ2F0aGVyKFR5cGUsIGBBdmVyYWdlIE51bWJlciBvZiBSZWNhbGxzIFBlciBQZXJzb25gKSAlPiUKICBwYW5kZXIoKQpgYGAKCgojIyMgUmVjYWxsIG9mIEFueSBIZWFkbGluZQoKV2UgZmlyc3QgdXNlIGEgTEFTU08gbW9kZWwgZm9yIGZlYXR1cmUgc2VsZWN0aW9uLgoKYGBge3J9CnhfbWF0cml4X251bV9yZWNhbGxfYWxsIDwtIG1vZGVsLm1hdHJpeChudW1fcmVjYWxsX2FsbCB+IC4gLW51bV9yZWNhbGxfdHJ1ZSAtbnVtX3JlY2FsbF9mYWxzZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcmVjYWxsX2RmKVssIC0xXQp5X251bV9yZWNhbGxfYWxsIDwtIHJlY2FsbF9kZiAlPiUgc2VsZWN0KG51bV9yZWNhbGxfYWxsKSAlPiUgdW5saXN0KCkKY3ZfZ2xtbmV0X3JlY2FsbF9hbGwgPC0gY3YuZ2xtbmV0KHhfbWF0cml4X251bV9yZWNhbGxfYWxsLCB5X251bV9yZWNhbGxfYWxsLCBhbHBoYSA9IC45OSwgbmZvbGRzID0gMTApCmZuX3Bsb3RfY3ZfZ2xtbmV0KGN2X2dsbW5ldF9yZWNhbGxfYWxsLCAiUmVjYWxsIEFueSBMQVNTIikKYGBgCgpUaGVuIHdlIHVzZSB0aGUgKipgciBsZW5ndGgodW5pcXVlKGJldGFfbGFzc29fcmVjYWxsX2FsbCR2YXJfbmFtZSkpYCoqIHZhcmlhYmxlcyBzZWxlY3RlZCBieSBgbGFtYmRhLjFzZWAgaW4gYHJlZ3N1YnNldHNgLgoKYGBge3IgcmVzdWx0cyA9ICdoaWRlJ30KYmV0YV9sYXNzb19yZWNhbGxfYWxsIDwtIAogICAgY29lZihjdl9nbG1uZXRfcmVjYWxsX2FsbCwgcyA9ICJsYW1iZGEuMXNlIikgJT4lIHRpZHkoKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodGVybSA9IHJvdykgJT4lCiAgICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUKICAgIG11dGF0ZSh2YXJfbmFtZSA9IHN0cl9leHRyYWN0KHRlcm0sIHBhc3RlKG5hbWVzKHJlY2FsbF9kZiksIGNvbGxhcHNlID0gJ3wnKSkpICU+JQogICAgbXV0YXRlKGxldmVsID0gc3RyX3JlcGxhY2UodGVybSwgdmFyX25hbWUsICIiKSkKCgpyZWdzdWJzZXRzX2ZpdDEgPC0gcmVnc3Vic2V0cyhhcy5mb3JtdWxhKHBhc3RlMCgibnVtX3JlY2FsbF9hbGwgfiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAodW5pcXVlKGJldGFfbGFzc29fcmVjYWxsX2FsbCR2YXJfbmFtZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiICsgIikpKSwgZGF0YSA9IHJlY2FsbF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZm9yd2FyZCIsIG52bWF4ID0gMTUpCgpgYGAKCmBgYHtyfQpmbl9yZWdzdWJzZXRzX3Bsb3RzKHJlZ3N1YnNldHNfZml0MSkKYGBgCgpUaGVyZSBsb29rcyB0byBiZSBzb21ld2hhdCBvZiBhbiBlbGJvdyB3aXRoIDEwIHZhcmlhYmxlcyBpbiB0aGUgQ3AgYW5kIEJJQyBjaGFydHMgc28gd2Ugd2lsbCB1c2UgdGhhdCB0byBjcmVhdGUgYSBsaW5lYXIgbW9kZWwgdG8gcHJlZGljdCB0aGUgbnVtYmVyIG9mIGhlYWRsaW5lcyByZWNhbGxlZC4KCmBgYHtyfQpyZWdzdWJzZXRzX3ZhcnNfcmVjYWxsX2FsbCA8LSAKICAgIHN1bW1hcnkocmVnc3Vic2V0c19maXQxKSRvdXRtYXRbMTAsIF0gJT4lIAogICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICAgICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyID0gInRlcm0iKSAlPiUgCiAgICAgICAgZmlsdGVyKGAuYCA9PSAiKiIpICU+JQogICAgICAgIGZpbHRlcih0ZXJtICE9ICIoSW50ZXJjZXB0KSIpICU+JQogICAgICAgIG11dGF0ZSh2YXJfbmFtZSA9IHN0cl9leHRyYWN0KHRlcm0sIHBhc3RlKG5hbWVzKHJlY2FsbF9kZiksIGNvbGxhcHNlID0gJ3wnKSkpICU+JQogICAgICAgIG11dGF0ZShsZXZlbCA9IHN0cl9yZXBsYWNlKHRlcm0sIHZhcl9uYW1lLCAiIikpCgpmaXRfcmVjYWxsX2FsbCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlMCgibnVtX3JlY2FsbF9hbGwgfiAiLCBwYXN0ZTAodW5pcXVlKHJlZ3N1YnNldHNfdmFyc19yZWNhbGxfYWxsJHZhcl9uYW1lKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgKyAiKSkpLCBkYXRhID0gcmVjYWxsX2RmKQpgYGAKClVzaW5nIGFuIEFub3ZhIHRhYmxlLCB3ZSBzZWUgdGhhdCBhbGwgb2YgdGhlIGZpbmFsIHByZWRpY3RvcnMgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW4gcHJlZGljdGluZyB0aGUgbnVtYmVyIG9mIGhlYWRsaW5lcyBhIHJlc3BvbmRlbnQgd2lsbCByZWNhbGwuIAoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KQW5vdmEoZml0X3JlY2FsbF9hbGwpICU+JQogIHRpZHkoKSAlPiUKICBwYW5kZXIobWlzc2luZyA9ICIiLCBjYXB0aW9uID0gIkFub3ZhIFRhYmxlOiBMaW5lYXIgTW9kZWwgUHJlZGljdGluZyBOdW1iZXIgb2YgSGVhZGxpbmVzIFJlY2FsbGVkIikKYGBgCgpGaW5hbGx5LCB3ZSB3aWxsIHVzZSBhIHNwaW5lIGNoYXJ0IHRvIHNob3cgdGhlIGVmZmVjdCBvZiBlYWNoIG9mIHRoZXNlIHZhcmlhYmxlcy4gT24gYXZlcmFnZSwgcmVzcG9uZGVudHMgcmVjYWxsICoqYHIgcm91bmQobWVhbihyZWNhbGxfZGYkbnVtX3JlY2FsbF9hbGwpLDIpYCoqIGhlYWRsaW5lcyBidXQgY2VydGFpbiBncm91cHMgb2YgcGVvcGxlIGNhbiBpZGVudGlmeSBtb3JlIG9yIGxlc3Mgb24gYXZlcmFnZS4KCmBgYHtyIGZpZy5oZWlnaHQ9IDEyLCBmaWcud2lkdGg9OX0KZjEgPC0gZm5fc3BsaW5lX2NoYXJ0KHJlY2FsbF9kZiwgbnVtZXJpY19jb2x1bW4gPSAibnVtX3JlY2FsbF9hbGwiLCBncm91cHMgPSAic3ViX3BhcnR5IiwgCiAgICAgICAgICAgICAgICB5YXhpcyA9ICJOdW1iZXIgb2YgUXVlc3Rpb25zIFJlY2FsbGVkIChhbGwpIiwgc3VidGl0bGUgPSAiTnVtYmVyIG9mIEhlYWRsaW5lcyBSZWNhbGxlZCIsIAogICAgICAgICAgICAgICAgdGl0bGUgPSAiUGFydHkgQWZmaWxpYXRpb24iKQoKZjIgPC0gIGZuX3NwbGluZV9jaGFydChyZWNhbGxfZGYsIG51bWVyaWNfY29sdW1uID0gIm51bV9yZWNhbGxfYWxsIiwgZ3JvdXBzID0gIlNoYXJlIiwgCiAgICAgICAgICAgICAgICB5YXhpcyA9ICJOdW1iZXIgb2YgUXVlc3Rpb25zIFJlY2FsbGVkIChhbGwpIiwgc3VidGl0bGUgPSAiTnVtYmVyIG9mIEhlYWRsaW5lcyBSZWNhbGxlZCIsIAogICAgICAgICAgICAgICAgdGl0bGUgPSAiU2hhcmluZyBOZXdzIG9uIFNvY2lhbCBNZWRpYSIpCgpmMyA8LSBmbl9zcGxpbmVfY2hhcnQocmVjYWxsX2RmLCBudW1lcmljX2NvbHVtbiA9ICJudW1fcmVjYWxsX2FsbCIsIGdyb3VwcyA9ICJWb3giLCAKICAgICAgICAgICAgICAgIHlheGlzID0gIk51bWJlciBvZiBRdWVzdGlvbnMgUmVjYWxsZWQgKGFsbCkiLCBzdWJ0aXRsZSA9ICJOdW1iZXIgb2YgSGVhZGxpbmVzIFJlY2FsbGVkIiwgCiAgICAgICAgICAgICAgICB0aXRsZSA9ICJWb3giKQoKZjQgPC0gZm5fc3BsaW5lX2NoYXJ0KHJlY2FsbF9kZiwgbnVtZXJpY19jb2x1bW4gPSAibnVtX3JlY2FsbF9hbGwiLCBncm91cHMgPSAiV2FzaGluZ3Rvbi5Qb3N0IiwgCiAgICAgICAgICAgICAgICB5YXhpcyA9ICJOdW1iZXIgb2YgUXVlc3Rpb25zIFJlY2FsbGVkIChhbGwpIiwgc3VidGl0bGUgPSAiTnVtYmVyIG9mIEhlYWRsaW5lcyBSZWNhbGxlZCIsIAogICAgICAgICAgICAgICAgdGl0bGUgPSAiV2FzaGluZ3RvbiBQb3N0IikKCmY1IDwtIGZuX3NwbGluZV9jaGFydChyZWNhbGxfZGYsIG51bWVyaWNfY29sdW1uID0gIm51bV9yZWNhbGxfYWxsIiwgZ3JvdXBzID0gIkRydWRnZS5SZXBvcnQiLCAKICAgICAgICAgICAgICAgIHlheGlzID0gIk51bWJlciBvZiBRdWVzdGlvbnMgUmVjYWxsZWQgKGFsbCkiLCBzdWJ0aXRsZSA9ICJOdW1iZXIgb2YgSGVhZGxpbmVzIFJlY2FsbGVkIiwgCiAgICAgICAgICAgICAgICB0aXRsZSA9ICJEcnVkZ2UgUmVwb3J0IikKCmY2IDwtIGZuX3NwbGluZV9jaGFydChyZWNhbGxfZGYsIG51bWVyaWNfY29sdW1uID0gIm51bV9yZWNhbGxfYWxsIiwgZ3JvdXBzID0gIkZveC5OZXdzIiwgCiAgICAgICAgICAgICAgICB5YXhpcyA9ICJOdW1iZXIgb2YgUXVlc3Rpb25zIFJlY2FsbGVkIChhbGwpIiwgc3VidGl0bGUgPSAiTnVtYmVyIG9mIEhlYWRsaW5lcyBSZWNhbGxlZCIsIAogICAgICAgICAgICAgICAgdGl0bGUgPSAiRm94IE5ld3MiKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZjEsIGYyLCBmMywgZjQsIGY1LCBmNiwgbmNvbD0yKQpgYGAKCgojIyMgUmVjYWxsIG9mIGEgRmFrZSBIZWFkbGluZQoKTm93IHdlIGFyZSBwcmVkaWN0aW5nIHRoZSBudW1iZXIgb2YgKmZha2UqIGhlYWRsaW5lcyBpZGVudGlmaWVkLiBBZ2Fpbiwgd2Ugc3RhcnQgb3V0IHdpdGggYSBMQVNTTyBtb2RlbCBmb3IgZmVhdHVyZSBzZWxlY3Rpb24uCgpgYGB7cn0KeF9tYXRyaXhfbnVtX3JlY2FsbF9mYWxzZSA8LSBtb2RlbC5tYXRyaXgobnVtX3JlY2FsbF9mYWxzZSB+IC4gLW51bV9yZWNhbGxfYWxsIC1udW1fcmVjYWxsX3RydWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlY2FsbF9kZilbLCAtMV0KeV9udW1fcmVjYWxsX2ZhbHNlIDwtIHJlY2FsbF9kZiAlPiUgc2VsZWN0KG51bV9yZWNhbGxfZmFsc2UpICU+JSB1bmxpc3QoKQpjdl9nbG1uZXRfcmVjYWxsX2ZhbHNlIDwtIGN2LmdsbW5ldCh4X21hdHJpeF9udW1fcmVjYWxsX2ZhbHNlLCB5X251bV9yZWNhbGxfZmFsc2UsIGFscGhhID0gLjk5LCBuZm9sZHMgPSAxMCkKZm5fcGxvdF9jdl9nbG1uZXQoY3ZfZ2xtbmV0X3JlY2FsbF9mYWxzZSwgIlJlY2FsbCBGYWtlIFN0b3JpZXMgTEFTU08iKQpgYGAKClRoZW4gd2UgdXNlIHRoZSAqKmByIGxlbmd0aCh1bmlxdWUoYmV0YV9sYXNzb19yZWNhbGxfZmFsc2UkdmFyX25hbWUpKWAqKiB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgYGxhbWJkYS4xc2VgIGluIGByZWdzdWJzZXRzYC4KCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9CmJldGFfbGFzc29fcmVjYWxsX2ZhbHNlIDwtIAogICAgY29lZihjdl9nbG1uZXRfcmVjYWxsX2ZhbHNlLCBzID0gImxhbWJkYS4xc2UiKSAlPiUgdGlkeSgpICU+JQogICAgZHBseXI6OnJlbmFtZSh0ZXJtID0gcm93KSAlPiUKICAgIGZpbHRlcih0ZXJtICE9ICIoSW50ZXJjZXB0KSIpICU+JQogICAgbXV0YXRlKHZhcl9uYW1lID0gc3RyX2V4dHJhY3QodGVybSwgcGFzdGUobmFtZXMocmVjYWxsX2RmKSwgY29sbGFwc2UgPSAnfCcpKSkgJT4lCiAgICBtdXRhdGUobGV2ZWwgPSBzdHJfcmVwbGFjZSh0ZXJtLCB2YXJfbmFtZSwgIiIpKQoKCnJlZ3N1YnNldHNfZml0MiA8LSByZWdzdWJzZXRzKGFzLmZvcm11bGEocGFzdGUwKCJudW1fcmVjYWxsX2ZhbHNlIH4gIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKHVuaXF1ZShiZXRhX2xhc3NvX3JlY2FsbF9mYWxzZSR2YXJfbmFtZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiICsgIikpKSwgZGF0YSA9IHJlY2FsbF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZm9yd2FyZCIsIG52bWF4ID0gMTUpCgpgYGAKCkFnYWluIHdlIHVzZSB0aGUgYmVzdCAxMCB2YXJpYWJsZSBtb2RlbC4KCmBgYHtyfQpmbl9yZWdzdWJzZXRzX3Bsb3RzKHJlZ3N1YnNldHNfZml0MikKYGBgCgpgYGB7cn0KcmVnc3Vic2V0c192YXJzX3JlY2FsbF9mYWxzZSA8LSAKICAgIHN1bW1hcnkocmVnc3Vic2V0c19maXQyKSRvdXRtYXRbMTAsIF0gJT4lIAogICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICAgICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyID0gInRlcm0iKSAlPiUgCiAgICAgICAgZmlsdGVyKGAuYCA9PSAiKiIpICU+JQogICAgICAgIGZpbHRlcih0ZXJtICE9ICIoSW50ZXJjZXB0KSIpICU+JQogICAgICAgIG11dGF0ZSh2YXJfbmFtZSA9IHN0cl9leHRyYWN0KHRlcm0sIHBhc3RlKG5hbWVzKHJlY2FsbF9kZiksIGNvbGxhcHNlID0gJ3wnKSkpICU+JQogICAgICAgIG11dGF0ZShsZXZlbCA9IHN0cl9yZXBsYWNlKHRlcm0sIHZhcl9uYW1lLCAiIikpCgpmaXRfcmVjYWxsX2ZhbHNlIDwtIGxtKGFzLmZvcm11bGEocGFzdGUwKCJudW1fcmVjYWxsX2ZhbHNlIH4gIiwKICAgICAgICAgICAgICAgIHBhc3RlMCh1bmlxdWUocmVnc3Vic2V0c192YXJzX3JlY2FsbF9mYWxzZSR2YXJfbmFtZSksIGNvbGxhcHNlID0gIiArICIpKSksIGRhdGEgPSByZWNhbGxfZGYpCmBgYAoKVXNpbmcgYW4gQW5vdmEgdGFibGUsIHdlIHNlZSB0aGF0IHRoZSBwcmVkaWN0b3JzIGFyZSBzaW1pbGFyIHRvIHRob3NlIGZvciBhbGwgaGVhZGxpbmVzLiBIb3dldmVyLCB0aGVyZSBpcyBub3cgVmljZSBOZXdzIGFuZCBubyBsb25nZXIgdGhlIE5ldyBZb3JrIFRpbWVzLiBFZHVjYXRpb24gZG9lcyBub3QgYXBwZWFyIHRvIGJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYXQgdGhlIDAuMDUgbGV2ZWwuCgpgYGB7ciByZXN1bHRzID0gJ2FzaXMnfQpBbm92YShmaXRfcmVjYWxsX2ZhbHNlKSAlPiUKICB0aWR5KCkgJT4lCiAgcGFuZGVyKG1pc3NpbmcgPSAiIiwgY2FwdGlvbiA9ICJBbm92YSBUYWJsZTogTGluZWFyIE1vZGVsIFByZWRpY3RpbmcgTnVtYmVyIG9mIEhlYWRsaW5lcyBSZWNhbGxlZCAoRmFrZSBPbmx5KSIpCmBgYAoKCiMjIyBSZWNhbGwgb2YgYSBSZWFsIEhlYWRsaW5lCgpOb3cgd2UgYXJlIHByZWRpY3RpbmcgdGhlIG51bWJlciBvZiAqcmVhbCogaGVhZGxpbmVzIGlkZW50aWZpZWQuIEFnYWluLCB3ZSBzdGFydCBvdXQgd2l0aCBhIExBU1NPIG1vZGVsIGZvciBmZWF0dXJlIHNlbGVjdGlvbi4KCmBgYHtyfQp4X21hdHJpeF9udW1fcmVjYWxsX3RydWUgPC0gbW9kZWwubWF0cml4KG51bV9yZWNhbGxfdHJ1ZSB+IC4gLW51bV9yZWNhbGxfYWxsIC1udW1fcmVjYWxsX2ZhbHNlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSByZWNhbGxfZGYpWywgLTFdCnlfbnVtX3JlY2FsbF90cnVlIDwtIHJlY2FsbF9kZiAlPiUgc2VsZWN0KG51bV9yZWNhbGxfdHJ1ZSkgJT4lIHVubGlzdCgpCmN2X2dsbW5ldF9yZWNhbGxfdHJ1ZSA8LSBjdi5nbG1uZXQoeF9tYXRyaXhfbnVtX3JlY2FsbF90cnVlLCB5X251bV9yZWNhbGxfdHJ1ZSwgYWxwaGEgPSAuOTksIG5mb2xkcyA9IDEwKQpmbl9wbG90X2N2X2dsbW5ldChjdl9nbG1uZXRfcmVjYWxsX3RydWUsICJSZWNhbGwgVHJ1ZSBMQVNTIikKYGBgCgpUaGVuIHdlIHVzZSB0aGUgKipgciBsZW5ndGgodW5pcXVlKGJldGFfbGFzc29fcmVjYWxsX3RydWUkdmFyX25hbWUpKWAqKiB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgYGxhbWJkYS4xc2VgIGluIGByZWdzdWJzZXRzYC4KCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9CmJldGFfbGFzc29fcmVjYWxsX3RydWUgPC0gCiAgICBjb2VmKGN2X2dsbW5ldF9yZWNhbGxfdHJ1ZSwgcyA9ICJsYW1iZGEuMXNlIikgJT4lIHRpZHkoKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodGVybSA9IHJvdykgJT4lCiAgICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUKICAgIG11dGF0ZSh2YXJfbmFtZSA9IHN0cl9leHRyYWN0KHRlcm0sIHBhc3RlKG5hbWVzKHJlY2FsbF9kZiksIGNvbGxhcHNlID0gJ3wnKSkpICU+JQogICAgbXV0YXRlKGxldmVsID0gc3RyX3JlcGxhY2UodGVybSwgdmFyX25hbWUsICIiKSkKCgpyZWdzdWJzZXRzX2ZpdDMgPC0gcmVnc3Vic2V0cyhhcy5mb3JtdWxhKHBhc3RlMCgibnVtX3JlY2FsbF90cnVlIH4gIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKHVuaXF1ZShiZXRhX2xhc3NvX3JlY2FsbF90cnVlJHZhcl9uYW1lKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgKyAiKSkpLCBkYXRhID0gcmVjYWxsX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJmb3J3YXJkIiwgbnZtYXggPSAxNSkKCmBgYAoKQWdhaW4gd2UgdXNlIHRoZSBiZXN0IDEwIHZhcmlhYmxlIG1vZGVsLgoKYGBge3J9CmZuX3JlZ3N1YnNldHNfcGxvdHMocmVnc3Vic2V0c19maXQzKQpgYGAKCmBgYHtyfQpyZWdzdWJzZXRzX3ZhcnNfcmVjYWxsX3RydWUgPC0gCiAgICBzdW1tYXJ5KHJlZ3N1YnNldHNfZml0Mykkb3V0bWF0WzEwLCBdICU+JSAKICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgICAgIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJ0ZXJtIikgJT4lIAogICAgICAgIGZpbHRlcihgLmAgPT0gIioiKSAlPiUKICAgICAgICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUKICAgICAgICBtdXRhdGUodmFyX25hbWUgPSBzdHJfZXh0cmFjdCh0ZXJtLCBwYXN0ZShuYW1lcyhyZWNhbGxfZGYpLCBjb2xsYXBzZSA9ICd8JykpKSAlPiUKICAgICAgICBtdXRhdGUobGV2ZWwgPSBzdHJfcmVwbGFjZSh0ZXJtLCB2YXJfbmFtZSwgIiIpKQoKZml0X3JlY2FsbF90cnVlIDwtIGxtKGFzLmZvcm11bGEocGFzdGUwKCJudW1fcmVjYWxsX3RydWUgfiAiLCBwYXN0ZTAodW5pcXVlKHJlZ3N1YnNldHNfdmFyc19yZWNhbGxfdHJ1ZSR2YXJfbmFtZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiICsgIikpKSwgZGF0YSA9IHJlY2FsbF9kZikKYGBgCgpVc2luZyBhbiBBbm92YSB0YWJsZSwgd2Ugc2VlIHRoYXQgdGhlIHByZWRpY3RvcnMgYXJlIG5lYXJseSB0aGUgc2FtZSBhcyB0aGUgdGhvc2UgZm9yIGFsbCBoZWFkbGluZXMuIEhvd2V2ZXIsIG5vdyBUd2l0dGVyIGhhcyByZXBsYWNlZCBTbmFwY2hhdCwgYnV0IGl0IGlzIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGF0IHRoZSAwLjAxIGxldmVsIGFueXdheS4KCmBgYHtyIHJlc3VsdHMgPSAnYXNpcyd9CkFub3ZhKGZpdF9yZWNhbGxfZmFsc2UpICU+JQogIHRpZHkoKSAlPiUKICBwYW5kZXIobWlzc2luZyA9ICIiLCBjYXB0aW9uID0gIkFub3ZhIFRhYmxlOiBMaW5lYXIgTW9kZWwgUHJlZGljdGluZyBOdW1iZXIgb2YgSGVhZGxpbmVzIFJlY2FsbGVkIChSZWFsIE9ubHkpIikKYGBgCgpXZSBoYXZlIGZvdW5kICh1bnN1cnByaXNpbmdseSkgdGhhdCB3aGV0aGVyIHlvdXIgY2FuIHJlY2FsbCBhIG5ld3MgaGVhZGxpbmUgY29ycmVsYXRlcyB3ZWxsIHdpdGggbmV3cyByZWFkZXJzaGlwLiBXZSBub3RlIHRoYXQgY2FuZGlkYXRlIGFuZCBhZ2UgaGF2ZSBub3QgYXBwZWFyIGluIGFueSBvZiB0aGVzZSAzIG1vZGVsIGJ1aWxkaW5nIGV4ZXJjaXNlcy4KCiMjIElkZW50aWZ5aW5nIEFjY3VyYWN5CgojIyMgQmFja2dyb3VuZAoKT3VyIG9iamVjdGl2ZSBpcyB0byBjcmVhdGUgYSBtb2RlbCB0byBwcmVkaWN0IHdoZXRoZXIgb3Igbm90IGFuIGluZGl2aWR1YWwgd2lsbCBiZSBhYmxlIHRvIGNvcnJlY3RseSBpZGVudGlmeSBhIG5ld3MgaGVhZGxpbmUgYXMgYWNjdXJhdGUgb3IgaW5hY2N1cmF0ZSAoY2xhc3NpZmljYXRpb24pLiBMaWtlIHRoZSBSZWNhbGwgc2VjdGlvbiBhYm92ZSwgd2Ugd2lsbCBicmVhayB0aGlzIHByb2Nlc3MgaW50byAzIHBhcnRzCgoxLiBJZGVudGlmaWNhdGlvbiBvZiAqYW55KiBoZWFkbGluZQoyLiBJZGVudGlmaWNhdGlvbiBvZiBhICpyZWFsKiBoZWFkbGluZQozLiBJZGVudGlmaWNhdGlvbiBvZiBhICpmYWtlKiBoZWFkbGluZQoKV2Ugc2VlIGZyb20gdGhlIHRhYmxlIGJlbG93IHRoYXQgbm90IGFsbCBvZiB0aGUgcXVlc3Rpb25zIGFyZSBjcmVhdGVkIGVxdWFsLiBTb21lIGFyZSBtb3JlIGNoYWxsZW5naW5nIGZvciBBbWVyaWNhbnMgdG8gY2xhc3NpZnkgYXMgYWNjdXJhY3kgb3IgaW5hY2N1cmF0ZS4KCmBgYHtyIHJlc3VsdHMgPSAnYXNpcyd9Cm1hc3Rlcl90YWJsZSAlPiUgCiAgZmlsdGVyKCFpcy5uYShjb3JyZWN0KSkgJT4lIAogIGdyb3VwX2J5KGFydGljbGUsIHFfaWQsIHR5cGUsIGNvcnJlY3QpICU+JSAKICBjb3VudCgpICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgc3ByZWFkKGNvcnJlY3QsIG4pICU+JSAKICBtdXRhdGUoVG90YWwgPSBObyArIFllcykgJT4lIAogIG11dGF0ZSgKICAgIE5vID0gcGFzdGUwKHJvdW5kKDEwMCAqIE5vL1RvdGFsLCAyKSwgIiUiKQogICAgLCBZZXMgPSBwYXN0ZTAocm91bmQoMTAwICogWWVzL1RvdGFsLCAyKSwgIiUiKQogICkgJT4lIAogIGFycmFuZ2UocV9pZCkgJT4lCiAgc2VsZWN0KE51bSA9IHFfaWQsIEFydGljbGUgPSBhcnRpY2xlLCBUeXBlID0gdHlwZSwgQ29ycmVjdCA9IFllcywgSW5jb3JyZWN0ID0gTm8sIFRvdGFsKSAlPiUKICBwYW5kZXIoc3BsaXQuY2VsbHMgPSA5MCwgc3BsaXQudGFibGUgPSBJbmYpCmBgYAoKIyMjIElkZW50aWZpY2F0aW9uIG9mIEFueSBIZWFkbGluZQoKIyMjIyBGZWF0dXJlIFNlbGVjdGlvbgoKVG8gY3JlYXRlIHRoaXMgcHJlZGljdGlvbiBtb2RlbCwgd2Ugd2lsbCB0cmVhdCBlYWNoIHJlc3BvbnNlIHRvIHRoZSBxdWVzdGlvbiAiSXMgdGhpcyBoZWFkbGluZSBhY2N1cmFjeT8iIGFzIGFuIG9ic2VydmF0aW9uLiBXZSBrbm93IHRoaXMgdmlvbGF0ZXMgdGhlIGFzc3VtcHRpb24gb2YgaW5kZXBlbmRlbmNlIGJlY2F1c2Ugc29tZSBvZiB0aGVzZSByZXNwb25kZW50cyBhcmUgdGhlIHNhbWUgaW5kaXZpZHVhbC4gSG93ZXZlciwgd2UgaGF2ZSByZWFzb24gdG8gYmVsaWV2ZSB0aGF0IGJlY2F1c2Ugbm90IGVhY2ggcXVlc3Rpb24gaXMgdGhlIHNhbWUsIHRoYXQgdGhlIGFuc3dlcmluZyBvZiBlYWNoIHF1ZXN0aW9uIGNhbiBiZSB0cmVhdGVkIGFzIGEgc2VwYXJhdGUgZXZlbnQuCgpXZSBjcmVhdGUgYSBkYXRhc2V0IG9mICoqYHIgcHJldHR5TnVtKG5jb2woYWNjdXJhY3lfZGYpLCBiaWcubWFyayA9ICcsJylgKiogY29sdW1ucyBhbmQgKipgciBwcmV0dHlOdW0obmNvbChhY2N1cmFjeV9kZikgLTEsIGJpZy5tYXJrID0gJywnKWAqKiBwcmVkaWN0b3JzIG9mIHRoZSByZXNwb25zZSBgY29ycmVjdGAuIFdlIHRoZW4gYnJlYWsgdGhpcyBkYXRhc2V0IGludG8gYSB0cmFpbmluZyAoODAlKSBhbmQgdGVzdCAoMjAlKSBkYXRhc2V0LgoKYGBge3J9CmFjY3VyYWN5X2RmIDwtCiAgICBtYXN0ZXJfdGFibGUgJT4lCiAgICBmaWx0ZXIoIWlzLm5hKGNvcnJlY3QpKSAlPiUKICAgIHNlbGVjdCgtcXVlc3Rpb24sIC1xX2lkLCAtb3JkZXIsIC1yZXNwb25zZSwgLWFydGljbGUsIC1yZWNhbGwsIC12b3RlZCwKICAgICAgICAgICAtdHlwZSwgLWFjY3VyYWN5LCAtaWQsIC16aXBfY29kZSwgLWNpdHksIC1zdGF0ZSkKCgphY2N1cmFjeV9kZl90cmFpbiA8LSBzYW1wbGVfZnJhYyhhY2N1cmFjeV9kZiwgMC44KQphY2N1cmFjeV9kZl90ZXN0IDwtIGRwbHlyOjpzZXRkaWZmKGFjY3VyYWN5X2RmLCBhY2N1cmFjeV9kZl90cmFpbikKYGBgCgpXZSBmaXJzdCBiZWdpbiBieSB1c2luZyBgZ2xtZW50YCB0byBpZGVudGlmeSB0aGUgbW9zdCBpbXBvcnRhbnQgZmVhdHVyZXMuIAoKYGBge3J9CnhfbWF0cml4X2FjY3VyYWN5X2FsbCA8LSBtb2RlbC5tYXRyaXgoY29ycmVjdCB+IC4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluKVssIC0xXQp5X251bV9hY2N1cmFjeV9hbGwgPC0gYWNjdXJhY3lfZGZfdHJhaW4gJT4lIHNlbGVjdChjb3JyZWN0KSAlPiUgdW5saXN0KCkKY3ZfZ2xtbmV0X2FjY3VyYWN5X2FsbCA8LSBjdi5nbG1uZXQoeF9tYXRyaXhfYWNjdXJhY3lfYWxsLCB5X251bV9hY2N1cmFjeV9hbGwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAuOTksIG5mb2xkcyA9IDEwKQpmbl9wbG90X2N2X2dsbW5ldChjdl9nbG1uZXRfYWNjdXJhY3lfYWxsLCAiQWNjdXJhY3kgQW55IExBU1NPIikKCgpiZXRhX2xhc3NvX2FjY3VyYWN5X2FsbCA8LSAKICAgIGNvZWYoY3ZfZ2xtbmV0X2FjY3VyYWN5X2FsbCwgcyA9ICJsYW1iZGEubWluIikgJT4lIHRpZHkoKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodGVybSA9IHJvdykgJT4lCiAgICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUKICAgIG11dGF0ZSh2YXJfbmFtZSA9IHN0cl9leHRyYWN0KHRlcm0sIHBhc3RlKG5hbWVzKGFjY3VyYWN5X2RmKSwgY29sbGFwc2UgPSAnfCcpKSkgJT4lCiAgICBtdXRhdGUobGV2ZWwgPSBzdHJfcmVwbGFjZSh0ZXJtLCB2YXJfbmFtZSwgIiIpKQpgYGAKCiMjIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKV2UgdXNlIHRoZSAqKmByIGxlbmd0aCh1bmlxdWUoYmV0YV9sYXNzb19hY2N1cmFjeV9hbGwkdmFyX25hbWUpKWAqKiB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgYGxhbWJkYS5taW5gIGluIHRoZSBjaGFydCBhYm92ZSBhbmQgZml0IGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4gV2UgdGhlbiB1c2UgQmFja3dhcmRzIEVsaW1pbmF0aW9uIHNvIHRoYXQgb25seSBwcmVkaWN0b3JzIHRoYXQgYXJlIHNpZ25pY2FudCBhdCB0aGUgMC4wMSBzaWduaWZhbmNlIGxldmVsIGFyZSBsZWZ0IHRvIGNyZWF0ZSBmaW5hbCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLgoKYGBge3J9CmxyX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoImNvcnJlY3QgfiAiLCBwYXN0ZSh1bmlxdWUoYmV0YV9sYXNzb19hY2N1cmFjeV9hbGwkdmFyX25hbWUpLCBjb2xsYXBzZSA9ICIgKyAiKSkpCmxyX2ZpdDEgPC0gZ2xtKGxyX2Zvcm11bGEsIGRhdGEgPSBhY2N1cmFjeV9kZl90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIikKCmxyX2ZpdDIgPC0gZm5fYmFja3dhcmRzX2VsaW1pbmF0aW9uKGxyX2ZpdDEpCgp5X2FjdHVhbCA8LSBhcy5udW1lcmljKGFjY3VyYWN5X2RmX3RyYWluJGNvcnJlY3QgPT0gIlllcyIpCnlfcHJlZGljdGVkIDwtIGlmZWxzZShwcmVkaWN0KGxyX2ZpdDIsIGFjY3VyYWN5X2RmX3RyYWluLCB0eXBlID0gInJlc3BvbnNlIikgPiAuNSwgMSwgMCkgCm1jZV9scl9maXQyIDwtIG1lYW4oeV9wcmVkaWN0ZWQgIT0geV9hY3R1YWwpCmBgYAoKV2Ugc2VlIHRoYXQgdGhpcyBtb2RlbCdzIG1pc2NsYXNzaWZpY2F0aW9uIHJhdGUgKGluLXNhbXBsZSkgaXMgKipgciBtY2VfbHJfZml0MmAqKiB3aGljaCBpcyBuZWFybHkgdGhlIHNhbWUgYXMgdGhlIHRyaXZpYWwgbW9kZWwncyBtaXNjbGFzc2lmaWNhdGlvbiByYXRlIG9mICoqYHIgbWVhbihyZXAoMSwgbGVuZ3RoKHlfcHJlZGljdGVkKSkgIT0geV9hY3R1YWwpYCoqLiBUaGUgdHJpdmlhbCBtb2RlbCBpcyBwcmVkaWN0aW5nIGNvcnJlY3QgZXZlcnkgdGltZSwgb3IgdGhhdCBubyByZXNwb25kZW50IGdldHMgaXQgd3JvbmcuCgojIyMjIENsYXNzaWZpY2F0aW9uIFRyZWUKCldlIHByb2R1Y2UgYSBjbGFzc2lmaWNhdGlvbiB0cmVlIHRvIHNlZSB0aGUgd2hpY2ggdmFyaWFibGVzIGNyZWF0ZSB0aGUgaW5pdGlhbCBzcGxpdHMuIE91ciBwcm9jZXNzIGlzIHRvIGJ1aWxkIGEgdHJ5IHdpdGggYGNwID0gLTFgIGFuZCB1c2UgdGhlIGZpcnN0IHZhbHVlIG9mIGNwIHRoYXQgcHJvZHVjZXMgYSBub24tcm9vdCB0cmVlIHRvIGdyb3cgYSB0cmVlLgoKYGBge3J9CnJwYXJ0X2ZvbXJ1bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoImNvcnJlY3QgfiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUodW5pcXVlKGJldGFfbGFzc29fYWNjdXJhY3lfYWxsJHZhcl9uYW1lKSwgY29sbGFwc2UgPSAiICsgIikpKQoKaW5mX2NwX3RyZWUgPC0gcnBhcnQocnBhcnRfZm9tcnVsYSwgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gLTEpLCBtZXRob2Q9ImNsYXNzIikKdHJlZV9maXQgPC0gcnBhcnQocnBhcnRfZm9tcnVsYSwgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluLCAKICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChjcCA9IGluZl9jcF90cmVlJGNwdGFibGVbMiwgMV0pLCBtZXRob2Q9ImNsYXNzIikKZmFuY3lScGFydFBsb3QodHJlZV9maXQsIG1haW4gPSAiQ2xhc3NpZmljYXRpb24gVHJlZSIsIHN1YiA9ICIiKQpgYGAKCgojIyMjIFJhbmRvbSBGb3Jlc3QKCldlIHRyeSBjcmVhdGluZyAzIHJhbmRvbSBmb3Jlc3QgbW9kZWxzOgoKMS4gT25seSB0aGUgdmFyaWFibGVzIHNlbGVjdGVkIGZyb20gTEFTU08KMi4gQWxsIHZhcmlhYmxlcyBpbiBgYWNjdXJhY3lfZGZfdHJhaW5gCjMuIE9ubHkgYGFnZWAgYW5kIGBjYW5kaWRhdGVgIC0gdGhlIG9ubHkgc2lnbmlmaWNhbnQgdmFyaWFibGVzIGluIGxvZ2lzdGljIHJlZ3Jlc3Npb24KCmBgYHtyfQpyZl9mb3JtdWxhIDwtIHJwYXJ0X2ZvbXJ1bGEKcmZfZml0X3NlbGVjdGVkX3ZhcnMgPC0gcmFuZG9tRm9yZXN0KHJmX2Zvcm11bGEsIGRhdGEgPSBhY2N1cmFjeV9kZl90cmFpbiwgbnRyZWUgPSA1MDApCnJmX2ZpdF9hbGxfdmFycyA8LSByYW5kb21Gb3Jlc3QoY29ycmVjdCB+IC4sIGRhdGEgPSBhY2N1cmFjeV9kZl90cmFpbiwgbnRyZWUgPSA1MDApCnJmX2ZpdF90d29fdmFycyA8LSByYW5kb21Gb3Jlc3QoY29ycmVjdCB+IGFnZSArIGNhbmRpZGF0ZSwgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluLCBudHJlZSA9IDUwMCkKYGBgCgpXZSBmaW5kIHRoYXQgb3B0aW9uIDMgKG9ubHkgdXNpbmcgYGFnZWAgYW5kIGBjYW5kaWRhdGVgKSBwcm9kdWNlcyBhIG1vZGVsIHdpdGggdGhlIGJlc3QgT09CLWVycm9yIHJhdGUuIFVuZm9ydHVuYXRlbHksIHRoYXQgZXJyb3IgcmF0ZSBpcyBzdGlsbCBjb21wYXJhYmxlIHRvdGhlIHRyaXZpYWwgbW9kZWwncyBlcnJvciByYXRlLgoKYGBge3J9CnJmX2ZpdF90d29fdmFycyRlcnIucmF0ZSAlPiUKICAgIGFzX3RpYmJsZSgpICU+JQogICAgbXV0YXRlKGlkID0gMTpuKCkpICU+JQogICAgZ2F0aGVyKEVycm9yLCB2YWx1ZSwgLWlkKSAlPiUKICAgIG11dGF0ZShFcnJvciA9IGZhY3RvcihFcnJvciwgbGV2ZWxzID0gYygiT09CIiwiWWVzIiwiTm8iKSkpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gaWQsIHkgPSB2YWx1ZSwgY29sb3VyID0gRXJyb3IpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICB0aGVtZV9qcmYoKSArCiAgICBsYWJzKHRpdGxlID0gIlJhbmRvbSBGb3Jlc3QgRXJyb3IgUmF0ZSIsIHggPSAiVHJlZXMiLCB5ID0gIkVycm9yIFJhdGUiKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMocGFsNTM4WzQ6Nl0gJT4lIHVubmFtZSgpKSkKYGBgCgojIyMjIE1vZGVsIENvbXBhcmlzb24KCiMjIyMjIFRyYWluaW5nIERhdGEKCldlIGNvbXBhcmUgdGhlIG1vZGVscyB3ZSBoYXZlIGJ1aWx0IGJ5IGxvb2tpbmcgYXQgdGhlIGluLXNhbXBsZSBtaXNjbGFzc2lmaWNhdGlvbiBlcnJvciByYXRlIChNU0UpIGFuZCB0aGUgQVVDIGZvciB0aGUgUk9DIEN1cnZlLiBVbmZvcnR1bmF0ZWx5LCBub25lIG9mIHRoZXNlIG1vZGVscyBpcyBtdWNoIGJldHRlciB0aGFuIHRoZSB0cml2YWwgbW9kZWwgKGFsd2F5cyBwcmVkaWN0IHRoZSByZXNwb25kZW50IGNvcnJlY3RseSBpZGVudGlmaWVkIHRoZSBoZWFkbGluZSBhcyBhY2N1cmF0ZSBvciBpbmFjY3VyYXRlKS4KCmBgYHtyIHJlc3VsdHMgPSAnYXNpcyd9CnBsb3RfZGZfdHJhaW4gPC0gZm5fcGxvdF9kZihhY2N1cmFjeV9kZl90cmFpbiwgIlRyYWluaW5nIiwgbGlzdChscl9maXQyLCB0cmVlX2ZpdCwgcmZfZml0X3R3b192YXJzKSkKdHJhaW5fc3VtbWFyeSA8LSBmbl9zdW1tYXJ5KHBsb3RfZGZfdHJhaW4pCnRyYWluX3N1bW1hcnkgJT4lCiAgICBwYW5kZXIoY2FwdGlvbiA9ICJNb2RlbCBDb21wYXJpc29uIC0gVHJhaW5pbmcgRGF0YSIpCmBgYAoKV2UgZXhwbG9yZSB0aGUgUk9DIGN1cnZlIGZvciB0aGUgdGhyZWUgbW9kZWxzLgoKYGBge3J9CmZuX3JvY19wbG90KHBsb3RfZGZfdHJhaW4sICJUcmFpbmluZyIpCmBgYAoKQmVsb3cgaXMgdGhlIFByZWNpc2lvbi1SZWNhbGwgY3VydmVzIGZvciBlYWNoIG9mIHRoZSB0aHJlZSBtb2RlbHMuCgpgYGB7cn0KZm5fcHJfcGxvdChwbG90X2RmX3RyYWluLCAiVHJhaW5pbmciKQpgYGAKCiMjIyMjIFRlc3RpbmcgRGF0YQoKV2Ugc2VsZWN0IHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGFzIG91ciBmaW5hbCBtb2RlbCBiZWNhdXNlIG9mIGl0cyBBVUMgUFIuIFdlIGltYWdpbmUgdGhhdCB0aGlzIG1ldHJpYyB3aWxsIG1ha2UgdGhlIG1vZGVsIG1vcmUgcm9idXN0IHRvIG5ldyBkYXRhLiBXZSBmaW5kIHRoaXMgdG8gYmUgdGhlIGNhc2UuIFRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGhhcyB0aGUgYmVzdCBNQ0UgYW5kIGJ5IGZhciB0aGUgYmVzdCBpbiBBVUMgUFIuCgpgYGB7ciByZXN1bHRzID0gJ2FzaXMnfQpwbG90X2RmX3Rlc3QgPC0gZm5fcGxvdF9kZihhY2N1cmFjeV9kZl90ZXN0LCAiVGVzdGluZyIsIGxpc3QobHJfZml0MiwgdHJlZV9maXQsIHJmX2ZpdF90d29fdmFycykpCnRlc3Rfc3VtbWFyeSA8LSBmbl9zdW1tYXJ5KHBsb3RfZGZfdGVzdCkKdGVzdF9zdW1tYXJ5ICU+JQogICAgcGFuZGVyKGNhcHRpb24gPSAiTW9kZWwgQ29tcGFyaXNvbiAtIFRlc3RpbmcgRGF0YSIpCmBgYAoKV2UgZmluZCB0aGF0IHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBwZXJmb3JtIHF1aXRlIHdlbGwgd2l0aCB0aGlzIGRhdGFzZXQuCgpgYGB7cn0KZm5fcm9jX3Bsb3QocGxvdF9kZl90ZXN0LCAiVGVzdGluZyIpCmBgYAoKCmBgYHtyfQpmbl9wcl9wbG90KHBsb3RfZGZfdGVzdCwgIlRlc3RpbmciKQpgYGAKCiMjIyMgU3VtbWFyeQoKYGBge3J9CmxyX2ZpdDMgPC0gZ2xtKGNvcnJlY3QgfiBhZ2UgKyBjYW5kaWRhdGUsIAogICAgICAgICAgICAgICBkYXRhID0gYWNjdXJhY3lfZGZfdHJhaW4gJT4lIG11dGF0ZShjYW5kaWRhdGUgPSBmY3RfcmVsZXZlbChjYW5kaWRhdGUsICdEaWQgTm90IFZvdGUnKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiYmlub21pYWwiKQoKdGlkeV9scl9maXQzIDwtIHRpZHkobHJfZml0MykKCmxyX2ZpdDNfc3VtbWFyeSA8LQogIHRpZHlfbHJfZml0MyAlPiUKICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUKICBtdXRhdGUoCiAgICAgICAgb2RkcyA9IGV4cChlc3RpbWF0ZSkKICAgICAgLCBwcm9iID0gb2RkcyAvICgxICsgb2RkcykKICAgICAgLCBwZXJjZW50YWdlID0gcGFzdGUwKHJvdW5kKHByb2IgKiAxMDAsIDIpLCAiJSIpCiAgKSAlPiUKICBzZWxlY3QoLXN0ZC5lcnJvciwgLXN0YXRpc3RpYykKCnlfcHJlZGljdGVkIDwtIGlmZWxzZShwcmVkaWN0KGxyX2ZpdDMsIGFjY3VyYWN5X2RmX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKSA+IC41LCAxLCAwKSAKeV9hY3R1YWwgPC0gYXMubnVtZXJpYyhhY2N1cmFjeV9kZl90ZXN0JGNvcnJlY3QgPT0gIlllcyIpCmNvbmZ1c2lvbl9scjwtIHRhYmxlKHlfYWN0dWFsLCB5X3ByZWRpY3RlZCkKYGBgCgpUaGUgZmluYWwgbW9kZWwgZm9yIGFsbCBoZWFkbGluZXMgaXMgCgokJHtDb3JyZWN0fSA9IGByIHRpZHlfbHJfZml0M1sxLCAyXWAgKyAKYHIgdGlkeV9scl9maXQzWzIsIDJdYGByIHRpZHlfbHJfZml0M1syLCAxXWAgKwpgciB0aWR5X2xyX2ZpdDNbMywgMl1gYHIgdGlkeV9scl9maXQzWzMsIDFdYCArCmByIHRpZHlfbHJfZml0M1s0LCAyXWBgciB0aWR5X2xyX2ZpdDNbNCwgMV1gICsgXFwKYHIgdGlkeV9scl9maXQzWzUsIDJdYGByIHRpZHlfbHJfZml0M1s1LCAxXWAgKwpgciB0aWR5X2xyX2ZpdDNbNiwgMl1gYHIgdGlkeV9scl9maXQzWzYsIDFdYCArCmByIHRpZHlfbHJfZml0M1s3LCAyXWBgciB0aWR5X2xyX2ZpdDNbNywgMV1gJCQKCgpgYGB7ciByZXN1bHRzID0gJ2FzaXMnfQpwYW5kZXIoZGVzY3I6OkNyb3NzVGFibGUoeV9hY3R1YWwsIHlfcHJlZGljdGVkLCBwcm9wLmNoaXNxPUZBTFNFLCBwcm9wLnIgPSBGLCBwcm9wLmMgPSBGLCBwcm9wLnQgPSBUUlVFLCBjaGlzcSA9IFRSVUUsIGRubiA9IGMoIkNvcnJlY3QiLCJQcmVkaWN0aW9uIikpLCBzcGxpdC50YWJsZSA9IEluZiwgYmlnLm1hcmsgPSAnLCcpCmBgYAoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KbHJfZml0M19zdW1tYXJ5ICU+JQogIHNlbGVjdCgtcGVyY2VudGFnZSkgJT4lCiAgcGFuZGVyKCkKYGBgCgpIb2xkaW5nIGFsbCBvdGhlciB2YXJpYWJsZXMgY29uc3RhbnQsCgorIEFuIGluY3JlYXNlIGluICoqYWdlIGJ5IG9uZSB5ZWFyKiosIHdlIGV4cGVjdCB0byBzZWUgYWJvdXQgKipgciBscl9maXQzX3N1bW1hcnkgJT4lIGZpbHRlcih0ZXJtID09ICJhZ2UiKSAlPiUgc2VsZWN0KHBlcmNlbnRhZ2UpICU+JSB1bmxpc3QoKWAqKiAqaW5jcmVhc2UqIGluIHRoZSBwcm9iYWJpbGl0eSBvZiBjb3JyZWN0bHkgcHJlZGljdGluZyB0aGUgYWNjdXJhY3kgb2YgYSBuZXdzIGhlYWRsaW5lLgorIFRoZSBkaWZmZXJlbmNlIG9mIHZvdGluZyBmb3IgKipDbGludG9uIHZzIG5vdCB2b3RpbmcqKiwgd2UgZXhwZWN0IHRvIHNlZSBhYm91dCAqKmByIGxyX2ZpdDNfc3VtbWFyeSAlPiUgZmlsdGVyKHRlcm0gPT0gImNhbmRpZGF0ZUNsaW50b24iKSAlPiUgc2VsZWN0KHBlcmNlbnRhZ2UpICU+JSB1bmxpc3QoKWAqKiBpbmNyZWFzZSBpbiB0aGUgcHJvYmFiaWxpdHkgb2YgY29ycmVjdGx5IHByZWRpY3RpbmcgdGhlIGFjY3VyYWN5IG9mIGEgbmV3cyBoZWFkbGluZS4KKyBUaGUgZGlmZmVyZW5jZSBvZiB2b3RpbmcgZm9yICoqVHJ1bXAgdnMgbm90IHZvdGluZyoqLCB3ZSBleHBlY3QgdG8gc2VlIGFib3V0ICoqYHIgbHJfZml0M19zdW1tYXJ5ICU+JSBmaWx0ZXIodGVybSA9PSAiY2FuZGlkYXRlVHJ1bXAiKSAlPiUgc2VsZWN0KHBlcmNlbnRhZ2UpICU+JSB1bmxpc3QoKWAqKiAqZGVjcmVhc2UqIGluIHRoZSBwcm9iYWJpbGl0eSBvZiBjb3JyZWN0bHkgcHJlZGljdGluZyB0aGUgYWNjdXJhY3kgb2YgYSBuZXdzIGhlYWRsaW5lLgorIFRoZSBkaWZmZXJlbmNlIG9mIHZvdGluZyBmb3IgKipKb2huc29uIHZzIG5vdCB2b3RpbmcqKiwgd2UgZXhwZWN0IHRvIHNlZSBhYm91dCAqKmByIGxyX2ZpdDNfc3VtbWFyeSAlPiUgZmlsdGVyKHRlcm0gPT0gImNhbmRpZGF0ZUpvaG5zb24iKSAlPiUgc2VsZWN0KHBlcmNlbnRhZ2UpICU+JSB1bmxpc3QoKWAqKiAqaW5jcmVhc2UqIGluIHRoZSBwcm9iYWJpbGl0eSBvZiBjb3JyZWN0bHkgcHJlZGljdGluZyB0aGUgYWNjdXJhY3kgb2YgYSBuZXdzIGhlYWRsaW5lLgorIFRoZSBkaWZmZXJlbmNlIG9mIHZvdGluZyBmb3IgKipTdGVpbiB2cyBub3Qgdm90aW5nKiosIHdlIGV4cGVjdCB0byBzZWUgYWJvdXQgKipgciBscl9maXQzX3N1bW1hcnkgJT4lIGZpbHRlcih0ZXJtID09ICJjYW5kaWRhdGVTdGVpbiIpICU+JSBzZWxlY3QocGVyY2VudGFnZSkgJT4lIHVubGlzdCgpYCoqICpkZWNyZWFzZSogaW4gdGhlIHByb2JhYmlsaXR5IG9mIGNvcnJlY3RseSBwcmVkaWN0aW5nIHRoZSBhY2N1cmFjeSBvZiBhIG5ld3MgaGVhZGxpbmUuCisgVGhlIGRpZmZlcmVuY2Ugb2Ygdm90aW5nIGZvciAqKmFub3RoZXIgM3JkIHBhcnR5IGNhbmRpZGF0ZSB2cyBub3Qgdm90aW5nKiosIHdlIGV4cGVjdCB0byBzZWUgYWJvdXQgKipgciBscl9maXQzX3N1bW1hcnkgJT4lIGZpbHRlcih0ZXJtID09ICJjYW5kaWRhdGVPdGhlciIpICU+JSBzZWxlY3QocGVyY2VudGFnZSkgJT4lIHVubGlzdCgpYCoqICppbmNyZWFzZSogaW4gdGhlIHByb2JhYmlsaXR5IG9mIGNvcnJlY3RseSBwcmVkaWN0aW5nIHRoZSBhY2N1cmFjeSBvZiBhIG5ld3MgaGVhZGxpbmUuCgpJbiB0aGUgcGxvdCBiZWxvdywgd2Ugc2hvdyB0aGUgbG9nIG9kZHMgb2YgY29ycmVjdGx5IGlkZW50aWZ5aW5nIGEgbmV3cyBoZWFkbGluZSBhcyBhY2N1cmF0ZS9pbmFjY3VyYXRlIHZzIEFnZSBhbmQgYnkgY2FuZGlkYXRlIHZvdGVkIGZvci4gRmlyc3QsIHdlIHNlZSB0aGF0IGNhdGVnb3JpY2FsbHkgb3VyIG1vZGVsIHByZWRpY3RzIHRoYXQgb2xkZXIgQW1lcmljYW5zIGFyZSBiZXR0ZXIgYXQgZGlzdGluZ3Vpc2hpbmcgcmVhbCBmcm9tIGZha2UgbmV3IHN0b3JpZXMgaG9sZGluZyBjYW5kaWRhdGUgdGhleSBzdXBwb3J0ZWQgY29uc3RhbnQuIFNlY29uZCwgd2Ugc2VlIHRoYXQgdGhvc2UgdGhhdCB2b3RlZCBmb3IgSGlsYXJ5IENsaW50b24gYW5kIEppbGx5IFN0ZWluIGhhdmUgdGhlIGhpZ2hlc3QgbG9nIG9kZHMgKGFuZCB0aHVzIHByb2JhYmlsaXR5KSBvZiBjb3JyZWN0bHkgaWRlbnRpZnlpbmcgYSBuZXdzIGhlYWRsaW5lLCB3aGlsZSBEb25hbGQgVHJ1bXAgc3VwcG9ydHMgaGF2ZSB0aGUgbG93ZXN0IGxvZyBvZGRzIChhbmQgdGh1cyBwcm9iYWJpbGl0eSkuCgpgYGB7cn0Kc3VtbWFyeV9hY2N1cmFjeV9hbGwgPC0gZ2xtKGNvcnJlY3QgfiBhZ2UgKyBjYW5kaWRhdGUsIGRhdGEgPSBhY2N1cmFjeV9kZiwgZmFtaWx5ID0gImJpbm9taWFsIikKCnN1bW1hcnlfYWNjdXJhY3lfYWxsMiA8LQogIGFjY3VyYWN5X2RmICU+JQogICAgc2VsZWN0KGFnZSwgY2FuZGlkYXRlLCBjb3JyZWN0KSAlPiUKICAgIGNiaW5kKGVzdGltYXRlID0gcHJlZGljdChzdW1tYXJ5X2FjY3VyYWN5X2FsbCwgYWNjdXJhY3lfZGYsIHR5cGUgPSAicmVzcG9uc2UiKSkgJT4lCiAgICBtdXRhdGUoCiAgICAgIG9kZHMgPSBleHAoZXN0aW1hdGUpCiAgICAgICwgcHJvYiA9IG9kZHMgLyAoMSArIG9kZHMpCiAgICAgICwgY29ycmVjdCA9IGFzLm51bWVyaWMoY29ycmVjdCA9PSAiWWVzIikKICAgICkKCmdncGxvdChzdW1tYXJ5X2FjY3VyYWN5X2FsbDIsIGFlcyh4ID0gYWdlLCB5ID0gZXN0aW1hdGUsIGNvbG91ciA9IGNhbmRpZGF0ZSkpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gJ2ppdHRlcicpICsKICBzdGF0X3Ntb290aChhZXMoeSA9IGNvcnJlY3QsIGNvbG91ciA9IGNhbmRpZGF0ZSksIG1ldGhvZD0iZ2xtIiwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCBzZT1GKSArCiAgdGhlbWVfanJmKCkgKwogIGxhYnMoeCA9ICJBZ2UiLCB5ID0gIkxvZyBPZGRzIiwgdGl0bGUgPSAiTW9kZWwgZm9yIEJvdGggUmVhbCBhbmQgRmFrZSBOZXdzIFN0b3JpZXMiLCAKICAgICAgIHN1YnRpdGxlID0gIkxvZyBPZGRzIG9mIENvcnJlY3RseSBJZGVudGlmeWluZyBIZWFkbGluZSBhcyBBY2N1cmF0ZS9JbmFjY3VyYXRlIikgKwogIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShuYW1lID0gIkNhbmRpZGF0ZSIpCmBgYAoKIyMjIElkZW50aWZpY2F0aW9uIG9mIGEgRmFrZSBIZWFkbGluZQoKTm93IHdlIHdpbGwgbGltaXQgb3VyIG1vZGVsIGJ1aWxkaW5nIHRvIG9ubHkgdGhlIDUgZmFrZSBoZWFkbGluZXMgdG8gc2VlIGlmIHRoZXJlIGFyZSBhIGRpZmZlcmVudCBzZXQgb2YgcHJlZGljdG9ycyB0aGF0IGRldGVybWluZSBvbmUncyBhYmlsaXR5IHRvIGRldGVybWluZSBpdHMgYWNjdXJhY3kuIEluIG90aGVyIHdvcmRzLCB3ZSBhcmUgdGFraW5nIGEgdHdvLXNpZGVkIHByb2JsZW0gYW5kIG1ha2luZyBpdCBvbmUtc2lkZS4gTm93LCB3ZSBhcmUgb25seSBsb29raW5nIGF0IHRoZSBzaXR1YXRpb24gd2hlcmUgdGhlIHN0b3J5IGlzIGZha2UgYW5kIHRyeWluZyB0byBidWlsZCBhIG1vZGVsIGp1c3QgZm9yIHRoZXNlIGNvcnJlY3QgYW5kIGluY29ycmVjdCByZXNwb25zZXMuCgpgYGB7cn0KYWNjdXJhY3lfZGZfZmFrZSA8LQogICAgbWFzdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKCFpcy5uYShjb3JyZWN0KSkgJT4lCiAgICBmaWx0ZXIodHlwZSA9PSAiRmFrZSIpICU+JQogICAgc2VsZWN0KC1xdWVzdGlvbiwgLXFfaWQsIC1vcmRlciwgLXJlc3BvbnNlLCAtYXJ0aWNsZSwgLXJlY2FsbCwgLXZvdGVkLAogICAgICAgICAgIC10eXBlLCAtYWNjdXJhY3ksIC1pZCwgLXppcF9jb2RlLCAtY2l0eSwgLXN0YXRlKQoKCmFjY3VyYWN5X2RmX3RyYWluX2Zha2UgPC0gc2FtcGxlX2ZyYWMoYWNjdXJhY3lfZGZfZmFrZSwgMC44KQphY2N1cmFjeV9kZl90ZXN0X2Zha2UgPC0gZHBseXI6OnNldGRpZmYoYWNjdXJhY3lfZGZfZmFrZSwgYWNjdXJhY3lfZGZfdHJhaW5fZmFrZSkKYGBgCgpXZSBjcmVhdGUgYSBkYXRhc2V0IG9mICoqYHIgcHJldHR5TnVtKG5jb2woYWNjdXJhY3lfZGYpLCBiaWcubWFyayA9ICcsJylgKiogdmFyaWFibGVzIGFuZCAqKmByIHByZXR0eU51bShuY29sKGFjY3VyYWN5X2RmKSAtMSwgYmlnLm1hcmsgPSAnLCcpYCoqIHByZWRpY3RvcnMgb2YgdGhlIHJlc3BvbnNlIGBjb3JyZWN0YC4gV2UgdGhlbiBicmVhayB0aGlzIGRhdGFzZXQgaW50byBhIHRyYWluaW5nICg4MCUpIGFuZCB0ZXN0ICgyMCUpIGRhdGFzZXQgYXMgd2UgZGlkIGJlZm9yZS4KCmBgYHtyfQp4X21hdHJpeF9hY2N1cmFjeV9hbGxfZmFrZSA8LSBtb2RlbC5tYXRyaXgoY29ycmVjdCB+IC4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluX2Zha2UpWywgLTFdCnlfbnVtX2FjY3VyYWN5X2FsbF9mYWtlIDwtIGFjY3VyYWN5X2RmX3RyYWluX2Zha2UgJT4lIHNlbGVjdChjb3JyZWN0KSAlPiUgdW5saXN0KCkKY3ZfZ2xtbmV0X2FjY3VyYWN5X2FsbF9mYWtlIDwtIGN2LmdsbW5ldCh4X21hdHJpeF9hY2N1cmFjeV9hbGxfZmFrZSwgeV9udW1fYWNjdXJhY3lfYWxsX2Zha2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAuOTksIG5mb2xkcyA9IDEwKQpmbl9wbG90X2N2X2dsbW5ldChjdl9nbG1uZXRfYWNjdXJhY3lfYWxsX2Zha2UsICJBY2N1cmFjeSAtIEZha2UgT25seSAtIExBU1NPIikKYGBgCgoKYGBge3J9CmJldGFfbGFzc29fYWNjdXJhY3lfYWxsX2Zha2UgPC0gCiAgICBjb2VmKGN2X2dsbW5ldF9hY2N1cmFjeV9hbGxfZmFrZSwgcyA9ICJsYW1iZGEubWluIikgJT4lIHRpZHkoKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodGVybSA9IHJvdykgJT4lCiAgICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUKICAgIG11dGF0ZSh2YXJfbmFtZSA9IHN0cl9leHRyYWN0KHRlcm0sIHBhc3RlKG5hbWVzKGFjY3VyYWN5X2RmKSwgY29sbGFwc2UgPSAnfCcpKSkgJT4lCiAgICBtdXRhdGUobGV2ZWwgPSBzdHJfcmVwbGFjZSh0ZXJtLCB2YXJfbmFtZSwgIiIpKQpgYGAKCiMjIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKV2UgdXNlIHRoZSAqKmByIGxlbmd0aCh1bmlxdWUoYmV0YV9sYXNzb19hY2N1cmFjeV9hbGwkdmFyX25hbWUpKWAqKiB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgYGxhbWJkYS5taW5gIGluIHRoZSBjaGFydCBhYm92ZSBhbmQgZml0IGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4gV2UgdGhlbiB1c2UgQmFja3dhcmRzIEVsaW1pbmF0aW9uIHNvIHRoYXQgb25seSBwcmVkaWN0b3JzIHRoYXQgYXJlIHNpZ25pY2FudCBhdCB0aGUgMC4wMSBzaWduaWZhbmNlIGxldmVsIGFyZSBsZWZ0IHRvIGNyZWF0ZSBmaW5hbCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLgoKYGBge3J9CmxyX2Zvcm11bGFfZmFrZSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgiY29ycmVjdCB+ICIsIHBhc3RlKHVuaXF1ZShiZXRhX2xhc3NvX2FjY3VyYWN5X2FsbF9mYWtlJHZhcl9uYW1lKSwgY29sbGFwc2UgPSAiICsgIikpKQpscl9maXQxX2Zha2UgPC0gZ2xtKGxyX2Zvcm11bGFfZmFrZSwgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluX2Zha2UsIGZhbWlseSA9ICJiaW5vbWlhbCIpCgpscl9maXQyX2Zha2UgPC0gZm5fYmFja3dhcmRzX2VsaW1pbmF0aW9uKGxyX2ZpdDFfZmFrZSkKCnlfYWN0dWFsX2Zha2UgPC0gYXMubnVtZXJpYyhhY2N1cmFjeV9kZl90cmFpbl9mYWtlJGNvcnJlY3QgPT0gIlllcyIpCnlfcHJlZGljdGVkX2Zha2UgPC0gaWZlbHNlKHByZWRpY3QobHJfZml0Ml9mYWtlLCBhY2N1cmFjeV9kZl90cmFpbl9mYWtlLCB0eXBlID0gInJlc3BvbnNlIikgPiAuNSwgMSwgMCkgCm1jZV9scl9maXQyX2Zha2UgPC0gbWVhbih5X3ByZWRpY3RlZF9mYWtlICE9IHlfYWN0dWFsX2Zha2UpCmBgYAoKV2Ugc2VlIHRoYXQgdGhpcyBtb2RlbCdzIG1pc2NsYXNzaWZpY2F0aW9uIHJhdGUgKGluLXNhbXBsZSkgaXMgKipgciBtY2VfbHJfZml0Ml9mYWtlYCoqIHdoaWNoIGlzIGxvd2VyIHRoYW4gdGhlIHRyaXZpYWwgbW9kZWwncyBtaXNjbGFzc2lmaWNhdGlvbiByYXRlIG9mICoqYHIgbWVhbihyZXAoMCwgbGVuZ3RoKHlfcHJlZGljdGVkX2Zha2UpKSAhPSB5X2FjdHVhbF9mYWtlKWAqKi4gVGhlIHRyaXZpYWwgbW9kZWwgaXMgcHJlZGljdGluZyBpbmNvcnJlY3QgZXZlcnkgdGltZSwgb3IgdGhhdCBubyByZXNwb25kZW50IGdldHMgaXQgcmlnaHQuCgojIyMjIENsYXNzaWZpY2F0aW9uIFRyZWUKCldlIHByb2R1Y2UgYSBjbGFzc2lmaWNhdGlvbiB0cmVlIHRvIHNlZSB0aGUgd2hpY2ggdmFyaWFibGVzIGNyZWF0ZSB0aGUgaW5pdGlhbCBzcGxpdHMuIE91ciBwcm9jZXNzIGlzIHRvIGJ1aWxkIGEgdHJ5IHdpdGggYGNwID0gLTFgIGFuZCB1c2UgdGhlIGZpcnN0IHZhbHVlIG9mIGNwIHRoYXQgcHJvZHVjZXMgYSBub24tcm9vdCB0cmVlIHRvIGdyb3cgYSB0cmVlLgoKYGBge3J9CnJwYXJ0X2ZvbXJ1bGFfZmFrZSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgiY29ycmVjdCB+ICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSh1bmlxdWUoYmV0YV9sYXNzb19hY2N1cmFjeV9hbGxfZmFrZSR2YXJfbmFtZSksIGNvbGxhcHNlID0gIiArICIpKSkKCmluZl9jcF90cmVlX2Zha2UgPC0gcnBhcnQocnBhcnRfZm9tcnVsYV9mYWtlLCBkYXRhID0gYWNjdXJhY3lfZGZfdHJhaW5fZmFrZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChjcCA9IC0xKSwgbWV0aG9kPSJjbGFzcyIpCnRyZWVfZml0X2Zha2UgPC0gcnBhcnQocnBhcnRfZm9tcnVsYV9mYWtlLCBkYXRhID0gYWNjdXJhY3lfZGZfdHJhaW5fZmFrZSwgCiAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSBpbmZfY3BfdHJlZV9mYWtlJGNwdGFibGVbMiwgMV0pLCBtZXRob2Q9ImNsYXNzIikKZmFuY3lScGFydFBsb3QodHJlZV9maXRfZmFrZSwgbWFpbiA9ICJDbGFzc2lmaWNhdGlvbiBUcmVlIiwgc3ViID0gIiIpCmBgYAoKIyMjIyBSYW5kb20gRm9yZXN0CgpXZSB0cnkgY3JlYXRpbmcgMyByYW5kb20gZm9yZXN0IG1vZGVsczoKCjEuIE9ubHkgdGhlIHZhcmlhYmxlcyBzZWxlY3RlZCBmcm9tIExBU1NPCjIuIEFsbCB2YXJpYWJsZXMgaW4gYGFjY3VyYWN5X2RmX3RyYWluX2Zha2VgCjMuIE9ubHkgYGFnZWAgYW5kIGBjYW5kaWRhdGVgIC0gdGhlIG9ubHkgc2lnbmlmaWNhbnQgdmFyaWFibGVzIGluIGxvZ2lzdGljIHJlZ3Jlc3Npb24KCmBgYHtyfQpyZl9mb3JtdWxhX2Zha2UgPC0gcnBhcnRfZm9tcnVsYV9mYWtlCnJmX2ZpdF9zZWxlY3RlZF92YXJzX2Zha2UgPC0gcmFuZG9tRm9yZXN0KHJmX2Zvcm11bGFfZmFrZSwgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluX2Zha2UsIG50cmVlID0gNTAwKQpyZl9maXRfYWxsX3ZhcnNfZmFrZSA8LSByYW5kb21Gb3Jlc3QoY29ycmVjdCB+IC4sIGRhdGEgPSBhY2N1cmFjeV9kZl90cmFpbl9mYWtlLCBudHJlZSA9IDUwMCkKcmZfZml0X3R3b192YXJzX2Zha2UgPC0gcmFuZG9tRm9yZXN0KGNvcnJlY3QgfiBhZ2UgKyBjYW5kaWRhdGUsIGRhdGEgPSBhY2N1cmFjeV9kZl90cmFpbl9mYWtlLCBudHJlZSA9IDUwMCkKYGBgCgpXZSBmaW5kIHRoYXQgb3B0aW9uIDEgKG9ubHkgb25seSB0aGUgdmFyaWFibGVzIHNlbGVjdGVkIGluIExBU1NPKSBwcm9kdWNlcyBtb2RlbCB3aXRoIHRoZSBiZXN0IE9PQi1lcnJvciByYXRlLiBMdWNraWx5LCB0aGF0IGVycm9yIHJhdGUgaXMgbG93ZXIgdGhhbiB0aGUgdHJpdmlhbCBtb2RlbCdzIGVycm9yIHJhdGUuCgpgYGB7cn0KcmZfZml0X3NlbGVjdGVkX3ZhcnNfZmFrZSRlcnIucmF0ZSAlPiUKICAgIGFzX3RpYmJsZSgpICU+JQogICAgbXV0YXRlKGlkID0gMTpuKCkpICU+JQogICAgZ2F0aGVyKEVycm9yLCB2YWx1ZSwgLWlkKSAlPiUKICAgIG11dGF0ZShFcnJvciA9IGZhY3RvcihFcnJvciwgbGV2ZWxzID0gYygiT09CIiwiWWVzIiwiTm8iKSkpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gaWQsIHkgPSB2YWx1ZSwgY29sb3VyID0gRXJyb3IpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICB0aGVtZV9qcmYoKSArCiAgICBsYWJzKHRpdGxlID0gIlJhbmRvbSBGb3Jlc3QgRXJyb3IgUmF0ZSIsIHggPSAiVHJlZXMiLCB5ID0gIkVycm9yIFJhdGUiKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMocGFsNTM4WzQ6Nl0gJT4lIHVubmFtZSgpKSkKYGBgCgoKIyMjIyBNb2RlbCBDb21wYXJpc29uCgojIyMjIyBUcmFpbmluZyBEYXRhCgpJbiBjb21wYXJpbmcgdGhlIHRocmVlIG1vZGVscyB3ZSBzZWUgdGhhdCB0aGUgcnBhcnQgbW9kZWwgbGlrZWx5IG92ZXJmaXRzIHRoZSBkYXRhLgoKYGBge3IgcmVzdWx0cyA9ICdhc2lzJ30KcGxvdF9kZl90cmFpbl9mYWtlIDwtIGZuX3Bsb3RfZGYoYWNjdXJhY3lfZGZfdHJhaW5fZmFrZSwgIlRyYWluaW5nIiwgbGlzdChscl9maXQyX2Zha2UsIHRyZWVfZml0X2Zha2UsIHJmX2ZpdF9zZWxlY3RlZF92YXJzX2Zha2UpKQp0cmFpbl9zdW1tYXJ5X2Zha2UgPC0gZm5fc3VtbWFyeShwbG90X2RmX3RyYWluX2Zha2UpCnRyYWluX3N1bW1hcnlfZmFrZSAlPiUKICAgIHBhbmRlcihjYXB0aW9uID0gIk1vZGVsIENvbXBhcmlzb24gLSBUcmFpbmluZyBEYXRhIikKYGBgCgpXZSBleHBsb3JlIHRoZSBST0MgY3VydmUgZm9yIHRoZSB0aHJlZSBtb2RlbHMuCgpgYGB7cn0KZm5fcm9jX3Bsb3QocGxvdF9kZl90cmFpbl9mYWtlLCAiVHJhaW5pbmciKQpgYGAKCkJlbG93IGlzIHRoZSBQcmVjaXNpb24tUmVjYWxsIGN1cnZlcyBmb3IgZWFjaCBvZiB0aGUgdGhyZWUgbW9kZWxzLgoKYGBge3J9CmZuX3ByX3Bsb3QocGxvdF9kZl90cmFpbl9mYWtlLCAiVHJhaW5pbmciKQpgYGAKCiMjIyMjIFRlc3RpbmcgRGF0YQoKV2Ugc2VsZWN0IHRoZSByYW5kb20gZm9yZXN0IG1vZGVsIG91ciBmaW5hbCBtb2RlbCwgYnV0IGNoZWNrIGFsbCB0aHJlZSBhZ2FpbnN0IHRoZSB0ZXN0aW5nIGRhdGEuIFdlIGZpbmQgdGhhdCB0aGUgcmFuZG9tIGZvcmVzdCBkb2VzIG9rIHdpdGggcmVzcGVjdCB0byBNQ0UsIGJ1dCBub3QgQVVDLgogCmBgYHtyfQpwbG90X2RmX3Rlc3RfZmFrZSA8LSBmbl9wbG90X2RmKGFjY3VyYWN5X2RmX3Rlc3RfZmFrZSwgIlRlc3RpbmciLCBsaXN0KGxyX2ZpdDJfZmFrZSwgdHJlZV9maXRfZmFrZSwgcmZfZml0X3NlbGVjdGVkX3ZhcnNfZmFrZSkpCnRlc3Rfc3VtbWFyeV9mYWtlIDwtIGZuX3N1bW1hcnkocGxvdF9kZl90ZXN0X2Zha2UpCnRlc3Rfc3VtbWFyeV9mYWtlICU+JQogICAgcGFuZGVyKGNhcHRpb24gPSAiTW9kZWwgQ29tcGFyaXNvbiAtIFRlc3RpbmcgRGF0YSIpCmBgYAoKV2UgZmluZCB0aGF0IHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBwZXJmb3JtIHF1aXRlIHdlbGwgd2l0aCB0aGlzIGRhdGFzZXQuCgpgYGB7cn0KZm5fcm9jX3Bsb3QocGxvdF9kZl90ZXN0X2Zha2UsICJUZXN0aW5nIikKYGBgCgoKYGBge3J9CmZuX3ByX3Bsb3QocGxvdF9kZl90ZXN0X2Zha2UsICJUZXN0aW5nIikKYGBgCgojIyMjIFN1bW1hcnkKCmBgYHtyfQp5X3ByZWRpY3RlZF9mYWtlX3Rlc3QgPC0gaWZlbHNlKHByZWRpY3QocmZfZml0X3NlbGVjdGVkX3ZhcnNfZmFrZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2N1cmFjeV9kZl90ZXN0X2Zha2UsIHR5cGUgPSAicHJvYiIpWywgMl0gPj0gLjUsIDEsIDApIAp5X2FjdHVhbF9mYWtlX3Rlc3QgPC0gYXMubnVtZXJpYyhhY2N1cmFjeV9kZl90ZXN0X2Zha2UkY29ycmVjdCA9PSAiWWVzIikKY29uZnVzaW9uX2xyX2Zha2VfdGVzdCA8LSB0YWJsZSh5X2FjdHVhbF9mYWtlX3Rlc3QsIHlfcHJlZGljdGVkX2Zha2VfdGVzdCkKbWNlX3JmX2ZpbmFsIDwtIG1lYW4oeV9wcmVkaWN0ZWRfZmFrZV90ZXN0ICE9IHlfYWN0dWFsX2Zha2VfdGVzdCkKYGBgCgpPdXIgZmluYWwgbW9kZWwgaXMgYSByYW5kb20gZm9yZXN0IG1vZGVsIHdpdGggYSB0ZXN0aW5nIG1pc2NsYXNzaWZpY2F0aW9uIHJhdGUgb2YgKipgciBtY2VfcmZfZmluYWxgKiosIGJ1dCBpcyB3b3JzZSB0aGF0IHRoZSB0cml2aWFsIG1vZGVsJ3MgbWlzY2xhc3NpZmljYXRpb24gcmF0ZSBvZiAqKmByIG1lYW4ocmVwKDAsIGxlbmd0aCh5X3ByZWRpY3RlZF9mYWtlKSkgIT0geV9hY3R1YWxfZmFrZSlgKiouIFRodXMsIHRoaXMgbW9kZWwgd291bGQgbm90IGJlIHVzZWZ1bCBmb3IgcHJlZGljdGlvbi4gQmVsb3cgaXMgdGhlIGNvbmZ1c2lvbiBtYXRyaXg6CgpgYGB7ciByZXN1bHRzID0gJ2FzaXMnfQpwYW5kZXIoZGVzY3I6OkNyb3NzVGFibGUoeV9hY3R1YWxfZmFrZV90ZXN0LCB5X3ByZWRpY3RlZF9mYWtlX3Rlc3QsIHByb3AuY2hpc3E9RkFMU0UsIHByb3AuciA9IEYsIHByb3AuYyA9IEYsIHByb3AudCA9IFRSVUUsIGNoaXNxID0gVFJVRSwgZG5uID0gYygiQ29ycmVjdCIsIlByZWRpY3Rpb24iKSksIHNwbGl0LnRhYmxlID0gSW5mLCBiaWcubWFyayA9ICcsJykKYGBgCgpCZWxvdyBpcyB0aGUgZmVhdHVyZSBpbXBvcnRhbmNlIG9mIHRoZSB2YXJpYWJsZXM6CgpgYGB7cn0KdmFySW1wKHJmX2ZpdF9zZWxlY3RlZF92YXJzX2Zha2UpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiVmFyaWFibGUiKSAlPiUKICBhc190aWJibGUoKSAlPiUKICByZW5hbWUoSW1wb3J0YW5jZSA9IE92ZXJhbGwpICU+JQogIG11dGF0ZShJbXBvcnRhbmNlID0gSW1wb3J0YW5jZSAvIG1heChJbXBvcnRhbmNlKSkgJT4lCiAgbXV0YXRlKFZhcmlhYmxlID0gZmN0X3Jlb3JkZXIoVmFyaWFibGUsIEltcG9ydGFuY2UpKSAlPiUKICBhcnJhbmdlKGRlc2MoSW1wb3J0YW5jZSkpICU+JQogIGdncGxvdCgpICsgCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gVmFyaWFibGUsIHhlbmQ9VmFyaWFibGUsIHkgPSBJbXBvcnRhbmNlLCB5ZW5kPTApLCBjb2xvciA9IHBhbDUzOFtbJ2JsdWUnXV0pICsKICBnZW9tX3BvaW50KGFlcyh4ID0gVmFyaWFibGUsIHkgPSBJbXBvcnRhbmNlKSwgY29sb3IgPSBwYWw1MzhbWydibHVlJ11dKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHggPSBOVUxMLCB5ID0gIlJlbGF0aXZlIEltcG9ydGFuY2UiLCB0aXRsZSA9ICJGZWF0dXJlIEltcG9ydGFuY2UgaW4gUmFuZG9tIEZvcmVzdCIsCiAgICAgICBzdWJ0aXRsZSA9ICJNb2RlbCBmb3IgRmFrZSBOZXdzIFN0b3JpZXMgT25seSIpICsKICB0aGVtZV9qcmYoKQpgYGAKCiMjIyBJZGVudGlmaWNhdGlvbiBvZiBhIFJlYWwgSGVhZGxpbmUKCk5vdyB3ZSB3aWxsIGxpbWl0IG91ciBtb2RlbCBidWlsZGluZyB0byBvbmx5IHRoZSA2IHJlYWwgaGVhZGxpbmVzIHRvIHNlZSBpZiB0aGVyZSBhcmUgYSBkaWZmZXJlbnQgc2V0IG9mIHByZWRpY3RvcnMgdGhhdCBkZXRlcm1pbmUgb25lJ3MgYWJpbGl0eSB0byBkZXRlcm1pbmUgaXRzIGFjY3VyYWN5LgoKYGBge3J9CmFjY3VyYWN5X2RmX3JlYWwgPC0KICAgIG1hc3Rlcl90YWJsZSAlPiUKICAgIGZpbHRlcighaXMubmEoY29ycmVjdCkpICU+JQogICAgZmlsdGVyKHR5cGUgPT0gIlJlYWwiKSAlPiUKICAgIHNlbGVjdCgtcXVlc3Rpb24sIC1xX2lkLCAtb3JkZXIsIC1yZXNwb25zZSwgLWFydGljbGUsIC1yZWNhbGwsIC12b3RlZCwKICAgICAgICAgICAtdHlwZSwgLWFjY3VyYWN5LCAtaWQsIC16aXBfY29kZSwgLWNpdHksIC1zdGF0ZSkKCgphY2N1cmFjeV9kZl90cmFpbl9yZWFsIDwtIHNhbXBsZV9mcmFjKGFjY3VyYWN5X2RmX3JlYWwsIDAuOCkKYWNjdXJhY3lfZGZfdGVzdF9yZWFsIDwtIGRwbHlyOjpzZXRkaWZmKGFjY3VyYWN5X2RmX3JlYWwsIGFjY3VyYWN5X2RmX3RyYWluX3JlYWwpCmBgYAoKV2UgY3JlYXRlIGEgZGF0YXNldCBvZiAqKmByIHByZXR0eU51bShuY29sKGFjY3VyYWN5X2RmX3JlYWwpLCBiaWcubWFyayA9ICcsJylgKiogdmFyaWFibGVzIGFuZCAqKmByIHByZXR0eU51bShuY29sKGFjY3VyYWN5X2RmX3JlYWwpIC0xLCBiaWcubWFyayA9ICcsJylgKiogcHJlZGljdG9ycyBvZiB0aGUgcmVzcG9uc2UgYGNvcnJlY3RgLiBXZSB0aGVuIGJyZWFrIHRoaXMgZGF0YXNldCBpbnRvIGEgdHJhaW5pbmcgKDgwJSkgYW5kIHRlc3QgKDIwJSkgZGF0YXNldCwgYXMgd2UgZGlkIGluIHRoZSB0d28gcHJldmlvdXMgaW5zdGFuY2VzLiBXZSB1c2UgTEFTU08gYWdhaW4gZm9yIHZhcmlhYmxlIHNlbGVjdGlvbi4KCmBgYHtyfQp4X21hdHJpeF9hY2N1cmFjeV9hbGxfcmVhbCA8LSBtb2RlbC5tYXRyaXgoY29ycmVjdCB+IC4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluX3JlYWwpWywgLTFdCnlfbnVtX2FjY3VyYWN5X2FsbF9yZWFsIDwtIGFjY3VyYWN5X2RmX3RyYWluX3JlYWwgJT4lIHNlbGVjdChjb3JyZWN0KSAlPiUgdW5saXN0KCkKY3ZfZ2xtbmV0X2FjY3VyYWN5X2FsbF9yZWFsIDwtIGN2LmdsbW5ldCh4X21hdHJpeF9hY2N1cmFjeV9hbGxfcmVhbCwgeV9udW1fYWNjdXJhY3lfYWxsX3JlYWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAuOTksIG5mb2xkcyA9IDEwKQpmbl9wbG90X2N2X2dsbW5ldChjdl9nbG1uZXRfYWNjdXJhY3lfYWxsX3JlYWwsICJBY2N1cmFjeSAtIFJlYWwgTEFTU08iKQoKCmJldGFfbGFzc29fYWNjdXJhY3lfYWxsX3JlYWwgPC0gCiAgICBjb2VmKGN2X2dsbW5ldF9hY2N1cmFjeV9hbGxfcmVhbCwgcyA9ICJsYW1iZGEubWluIikgJT4lIHRpZHkoKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodGVybSA9IHJvdykgJT4lCiAgICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUKICAgIG11dGF0ZSh2YXJfbmFtZSA9IHN0cl9leHRyYWN0KHRlcm0sIHBhc3RlKG5hbWVzKGFjY3VyYWN5X2RmKSwgY29sbGFwc2UgPSAnfCcpKSkgJT4lCiAgICBtdXRhdGUobGV2ZWwgPSBzdHJfcmVwbGFjZSh0ZXJtLCB2YXJfbmFtZSwgIiIpKQpgYGAKCiMjIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKV2UgdXNlIHRoZSAqKmByIGxlbmd0aCh1bmlxdWUoYmV0YV9sYXNzb19hY2N1cmFjeV9hbGxfcmVhbCR2YXJfbmFtZSkpYCoqIHZhcmlhYmxlcyBzZWxlY3RlZCBieSBgbGFtYmRhLm1pbmAgaW4gdGhlIGNoYXJ0IGFib3ZlIGFuZCBmaXQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLiBXZSB0aGVuIHVzZSBCYWNrd2FyZHMgRWxpbWluYXRpb24gc28gdGhhdCBvbmx5IHByZWRpY3RvcnMgdGhhdCBhcmUgc2lnbmljYW50IGF0IHRoZSAwLjAxIHNpZ25pZmFuY2UgbGV2ZWwgYXJlIGxlZnQgdG8gY3JlYXRlIGZpbmFsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuIEhvd2V2ZXIsIHRoZXJlIGFyZSBubyB2YXJpYWJsZXMgdGhhdCBzaWduaWZpY2FudCBpbiBwcmVkaWN0aW5nIHdoZXRoZXIgYSByZXNwb25kZW50IHdpdGggYmUgY29ycmVjdCB3aGVuIHRoZSBoZWFkbGluZXMgYXJlIHRydWUuIFNvLCB3ZSB3aWxsIG5vdCBiZWluZyB1c2luZyBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuCgpgYGB7cn0KbHJfZm9ybXVsYV9yZWFsIDwtIGFzLmZvcm11bGEocGFzdGUwKCJjb3JyZWN0IH4gIiwgcGFzdGUodW5pcXVlKGJldGFfbGFzc29fYWNjdXJhY3lfYWxsX3JlYWwkdmFyX25hbWUpLCBjb2xsYXBzZSA9ICIgKyAiKSkpCmxyX2ZpdDFfcmVhbCA8LSBnbG0obHJfZm9ybXVsYV9yZWFsLCBkYXRhID0gYWNjdXJhY3lfZGZfdHJhaW5fcmVhbCwgZmFtaWx5ID0gImJpbm9taWFsIikKCmxyX2ZpdDJfcmVhbCA8LSB0cnkoZm5fYmFja3dhcmRzX2VsaW1pbmF0aW9uKGxyX2ZpdDFfcmVhbCksIHNpbGVudCA9IFRSVUUpCmBgYAoKIyMjIyBDbGFzc2lmaWNhdGlvbiBUcmVlCgpXZSBwcm9kdWNlIGEgY2xhc3NpZmljYXRpb24gdHJlZSB0byBzZWUgdGhlIHdoaWNoIHZhcmlhYmxlcyBjcmVhdGUgdGhlIGluaXRpYWwgc3BsaXRzLiBPdXIgcHJvY2VzcyBpcyB0byBidWlsZCBhIHRyeSB3aXRoIGBjcCA9IC0xYCBhbmQgdXNlIHRoZSBmaXJzdCB2YWx1ZSBvZiBjcCB0aGF0IHByb2R1Y2VzIGEgbm9uLXJvb3QgdHJlZSB0byBncm93IGEgdHJlZS4KCmBgYHtyfQpycGFydF9mb21ydWxhX3JlYWwgPC0gYXMuZm9ybXVsYShwYXN0ZTAoImNvcnJlY3QgfiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUodW5pcXVlKGJldGFfbGFzc29fYWNjdXJhY3lfYWxsX3JlYWwkdmFyX25hbWUpLCBjb2xsYXBzZSA9ICIgKyAiKSkpCgppbmZfY3BfdHJlZV9yZWFsIDwtIHJwYXJ0KHJwYXJ0X2ZvbXJ1bGFfcmVhbCwgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluX3JlYWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSAtMSksIG1ldGhvZD0iY2xhc3MiKQp0cmVlX2ZpdF9yZWFsIDwtIHJwYXJ0KHJwYXJ0X2ZvbXJ1bGFfcmVhbCwgZGF0YSA9IGFjY3VyYWN5X2RmX3RyYWluX3JlYWwsIAogICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gaW5mX2NwX3RyZWVfcmVhbCRjcHRhYmxlWzIsIDFdKSwgbWV0aG9kPSJjbGFzcyIpCmZhbmN5UnBhcnRQbG90KHRyZWVfZml0X3JlYWwsIG1haW4gPSAiQ2xhc3NpZmljYXRpb24gVHJlZSIsIHN1YiA9ICIiKQpgYGAKCiMjIyMgUmFuZG9tIEZvcmVzdAoKV2UgdHJ5IGNyZWF0aW5nIDMgcmFuZG9tIGZvcmVzdCBtb2RlbHM6CgoxLiBPbmx5IHRoZSB2YXJpYWJsZXMgc2VsZWN0ZWQgZnJvbSBMQVNTTwoyLiBBbGwgdmFyaWFibGVzIGluIGBhY2N1cmFjeV9kZl90cmFpbl9yZWFsYAozLiBPbmx5IGBhZ2VgIGFuZCBgY2FuZGlkYXRlYCAtIHRoZSBvbmx5IHNpZ25pZmljYW50IHZhcmlhYmxlcyBpbiBsb2dpc3RpYyByZWdyZXNzaW9uCgpgYGB7cn0KcmZfZm9ybXVsYV9yZWFsIDwtIHJwYXJ0X2ZvbXJ1bGFfcmVhbApyZl9maXRfc2VsZWN0ZWRfdmFyc19yZWFsIDwtIHJhbmRvbUZvcmVzdChyZl9mb3JtdWxhX3JlYWwsIGRhdGEgPSBhY2N1cmFjeV9kZl90cmFpbl9yZWFsLCBudHJlZSA9IDUwMCkKcmZfZml0X2FsbF92YXJzX3JlYWwgPC0gcmFuZG9tRm9yZXN0KGNvcnJlY3QgfiAuLCBkYXRhID0gYWNjdXJhY3lfZGZfdHJhaW5fcmVhbCwgbnRyZWUgPSA1MDApCmBgYAoKV2UgZmluZCB0aGF0IG9wdGlvbiAzIChvbmx5IHVzaW5nIGBhZ2VgIGFuZCBgY2FuZGlkYXRlYCkgcHJvZHVjZXMgbW9kZWwgd2l0aCB0aGUgYmVzdCBPT0ItZXJyb3IgcmF0ZS4gVW5mb3J0dW5hdGVseSwgdGhhdCBlcnJvciByYXRlIGlzIHN0aWxsIGhpZ2hlciB0aGFuIHRoZSB0cml2aWFsIG1vZGVsJ3MgZXJyb3IgcmF0ZS4KCmBgYHtyfQpyZl9maXRfc2VsZWN0ZWRfdmFyc19yZWFsJGVyci5yYXRlICU+JQogICAgYXNfdGliYmxlKCkgJT4lCiAgICBtdXRhdGUoaWQgPSAxOm4oKSkgJT4lCiAgICBnYXRoZXIoRXJyb3IsIHZhbHVlLCAtaWQpICU+JQogICAgbXV0YXRlKEVycm9yID0gZmFjdG9yKEVycm9yLCBsZXZlbHMgPSBjKCJPT0IiLCJZZXMiLCJObyIpKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBpZCwgeSA9IHZhbHVlLCBjb2xvdXIgPSBFcnJvcikpICsKICAgIGdlb21fbGluZSgpICsKICAgIHRoZW1lX2pyZigpICsKICAgIGxhYnModGl0bGUgPSAiUmFuZG9tIEZvcmVzdCBFcnJvciBSYXRlIiwgeCA9ICJUcmVlcyIsIHkgPSAiRXJyb3IgUmF0ZSIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhwYWw1MzhbNDo2XSAlPiUgdW5uYW1lKCkpKQpgYGAKCiMjIyMgRmVhdHVyZSBJbXBvcnRhbmNlCgpBcyBubyBwcmVkaWN0b3JzIHdlcmUgc2lnbmlmaWNhbnQgaW4gdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgd2Ugd2lsbCBub3QgZG8gbW9kZWwgc2VsZWN0aW9uLiBSYXRoZXIsIHdlIHdpbGwgc2hvdyB3aGljaCBwcmVkaWN0b3JzIHdlcmUgaW1wb3J0YW50IGluIHRoZSByYW5kb20gZm9yZXN0IG1vZGVsLgoKYGBge3J9CnZhckltcChyZl9maXRfc2VsZWN0ZWRfdmFyc19yZWFsKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oIlZhcmlhYmxlIikgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgcmVuYW1lKEltcG9ydGFuY2UgPSBPdmVyYWxsKSAlPiUKICBtdXRhdGUoSW1wb3J0YW5jZSA9IEltcG9ydGFuY2UgLyBtYXgoSW1wb3J0YW5jZSkpICU+JQogIG11dGF0ZShWYXJpYWJsZSA9IGZjdF9yZW9yZGVyKFZhcmlhYmxlLCBJbXBvcnRhbmNlKSkgJT4lCiAgYXJyYW5nZShkZXNjKEltcG9ydGFuY2UpKSAlPiUKICBnZ3Bsb3QoKSArIAogIGdlb21fc2VnbWVudChhZXMoeCA9IFZhcmlhYmxlLCB4ZW5kPVZhcmlhYmxlLCB5ID0gSW1wb3J0YW5jZSwgeWVuZD0wKSwgY29sb3IgPSBwYWw1MzhbWydibHVlJ11dKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IFZhcmlhYmxlLCB5ID0gSW1wb3J0YW5jZSksIGNvbG9yID0gcGFsNTM4W1snYmx1ZSddXSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJSZWxhdGl2ZSBJbXBvcnRhbmNlIiwgdGl0bGUgPSAiRmVhdHVyZSBJbXBvcnRhbmNlIGluIFJhbmRvbSBGb3Jlc3QiLAogICAgICAgc3VidGl0bGUgPSAiTW9kZWwgZm9yIFJlYWwgTmV3cyBTdG9yaWVzIE9ubHkiKSArCiAgdGhlbWVfanJmKCkKYGBgCgoKIyBDb25jbHVzaW9uCgojIyBGaW5hbCBSZXN1bHRzCgpJbiB0aGlzIGFuYWx5c2lzIHdlIGF0dGVtcHRlZCB0byBidWlsZCBtb2RlbHMgdG8gcHJlZGljdAoKMS4gVGhlIG51bWJlciBvZiBoZWFkbGluZXMgcmVzcG9uZGVudHMgd291bGQgcmVjYWxsCjIuIFdoZXRoZXIgb3Igbm90IHJlc3BvbmRlbnRzIGNvdWxkIGRpc3Rpbmd1aXNoIHJlYWwgYW5kIGZha2UgbmV3cwoKTm9uZSBvZiBvdXIgbW9kZWxzIHdlcmUgYmV0dGVyIHRoYW4gdHJpdmlhbCBtb2RlbHMgb24gdGhlIHRlc3QgZGF0YXNldHMuIEhvd2V2ZXIsIHdlIGJlZW4gYWJsZSB0byBpZGVudGlmeSBmcm9tIHRoZSBtb2RlbC1idWlsZGluZyBwcm9jZXNzIHNvbWUgYXNzb2ljYXRpb25zOgoKMS4gRm9yIGNvcnJlY3RseSBkaXN0aW5ndWlzaGluZyBhbnkgdHlwZSBvZiBuZXdzIHdlIGZvdW5kIHRoYXQgdGhlIHR3byBtb3N0IGltcG9ydGFudCBmYWN0b3JzIHdlcmUgKiphZ2UqKiBhbmQgKipjYW5kaWRhdGUgdm90ZWQgZm9yIGluIHRoZSAyMDE2IHByZXNpZGVudGlhbCBlbGVjdGlvbioqCgpgYGB7cn0KYWNjdXJhY3lfZGZfdG1wIDwtIAogICAgYWNjdXJhY3lfZGYgJT4lCiAgICAgICAgbXV0YXRlKAogICAgICAgICAgICAgIGFnZV9idWNrZXRzID0gY3V0X251bWJlcihhZ2UsIDYpCiAgICAgICAgICAgICwgYWdlX2J1Y2tldHMgPSBhcy5jaGFyYWN0ZXIoYWdlX2J1Y2tldHMpCiAgICAgICAgICAgICwgYWdlX2J1Y2tldHMgPSBnc3ViKCIsIiAsICIgLSAiLCBnc3ViKCJdIiwgIiIsIGdzdWIoIlxcKCIsICIiLCBhZ2VfYnVja2V0cykpKQogICAgICAgICkKICAgCnIxIDwtIGZuX3NwbGluZV9jaGFydF9jb3JyZWN0KGFjY3VyYWN5X2RmX3RtcCwgZ3JvdXAgPSAiYWdlX2J1Y2tldHMiLCB5YXhpcyA9ICIlIENvcnJlY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBZ2UiLCIlIERpZmYgb2YgQWxsIE5ld3MgU3RvcmllcyBJZGVudGlmaWVkIENvcnJlY3RseSIpIAoKcjIgPC0gZm5fc3BsaW5lX2NoYXJ0X2NvcnJlY3QoYWNjdXJhY3lfZGZfdG1wLCBncm91cCA9ICJjYW5kaWRhdGUiLCB5YXhpcyA9ICIlIENvcnJlY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBZ2UiLCIlIERpZmYgb2YgQWxsIE5ld3MgU3RvcmllcyBJZGVudGlmaWVkIENvcnJlY3RseSIpIAoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocjEsIHIyLCBuY29sPTIpCmBgYAoKCjIuIEZvciBjb3JyZWN0bHkgaWRlbnRpZnlpbmcgZmFrZSBuZXdzLCB3ZSBpZGVudGlmaWVkIGFuIG51bWJlciBvZiBmYWN0b3JzIHVzaW5nIGEgcmFuZG9tIGZvcmVzdCBtb2RlbC4gQmVsb3cgYXJlIHN1bW1hcmllcyB0aGUgOCBtb3N0IGltcG9ydGFudC4gSW4gZ2VuZXJhbGx5LCBmb3IgdGhvc2UgdGhhdCByZWNhbGxlZCBhIGZha2UgbmV3cyBzdG9yeSwgcmVzcG9uZGVudHMgY29ycmVjdGx5IGlkZW50aWZpZWQgaXQgYXMgaW5hY2N1cmF0ZSAqKmByIHBhc3RlMChyb3VuZCgxMDAgKiBhY2N1cmFjeV9kZl9mYWtlICU+JSBzdW1tYXJpc2Uoc3VtKGNvcnJlY3QgPT0gIlllcyIpIC8gbigpKSAlPiUgdW5saXN0KCksIDIpLCIlIilgKiogb2YgdGhlIHRpbWUuIEhvd2V2ZXIsIGNlcnRhaW4gZmFjdG9ycyBpbXBhY3QgdGhpcyByZXN1bHQsIGFzIHNob3duIGJlbG93LgoKYGBge3IgZmlnLmhlaWdodD0gMTQsIGZpZy53aWR0aD05fQpnMSA8LSBmbl9zcGxpbmVfY2hhcnRfY29ycmVjdChhY2N1cmFjeV9kZl9mYWtlLCBncm91cCA9ICJjYW5kaWRhdGUiLCB5YXhpcyA9ICIlIENvcnJlY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJDYW5kaWRhdGUiLCIlIERpZmYgb2YgRmFrZSBOZXdzIFN0b3JpZXMgSWRlbnRpZmllZCBDb3JyZWN0bHkiKQpnMiA8LSBmbl9zcGxpbmVfY2hhcnRfY29ycmVjdChhY2N1cmFjeV9kZl9mYWtlLCBncm91cCA9ICJGb3guTmV3cyIsIHlheGlzID0gIiUgQ29ycmVjdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkZveCBOZXdzIiwiJSBEaWZmIG9mIEZha2UgTmV3cyBTdG9yaWVzIElkZW50aWZpZWQgQ29ycmVjdGx5IikKZzMgPC0gZm5fc3BsaW5lX2NoYXJ0X2NvcnJlY3QoYWNjdXJhY3lfZGZfZmFrZSwgZ3JvdXAgPSAic3ViX3BhcnR5IiwgeWF4aXMgPSAiJSBDb3JyZWN0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiUGFydHkgQWZmaWxpYXRpb24iLCIlIERpZmYgb2YgRmFrZSBOZXdzIFN0b3JpZXMgSWRlbnRpZmllZCBDb3JyZWN0bHkiKQpnNCA8LSBmbl9zcGxpbmVfY2hhcnRfY29ycmVjdChhY2N1cmFjeV9kZl9mYWtlLCBncm91cCA9ICJob3VzZWhvbGRfaW5jb21lIiwgeWF4aXMgPSAiJSBDb3JyZWN0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiSG91c2Vob2xkIEluY29tZSIsIiUgRGlmZiBvZiBGYWtlIE5ld3MgU3RvcmllcyBJZGVudGlmaWVkIENvcnJlY3RseSIpCmc1IDwtIGZuX3NwbGluZV9jaGFydF9jb3JyZWN0KGFjY3VyYWN5X2RmX2Zha2UsIGdyb3VwID0gIlNoYXJlIiwgeWF4aXMgPSAiJSBDb3JyZWN0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiU2hhcmUgTmV3cyIsIiUgRGlmZiBvZiBGYWtlIE5ld3MgU3RvcmllcyBJZGVudGlmaWVkIENvcnJlY3RseSIpCmc2IDwtIGZuX3NwbGluZV9jaGFydF9jb3JyZWN0KGFjY3VyYWN5X2RmX2Zha2UsIGdyb3VwID0gIlBpbnRlcmVzdCIsIHlheGlzID0gIiUgQ29ycmVjdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIlBpbnRlcmVzdCIsIiUgRGlmZiBvZiBGYWtlIE5ld3MgU3RvcmllcyBJZGVudGlmaWVkIENvcnJlY3RseSIpCmc3IDwtIGZuX3NwbGluZV9jaGFydF9jb3JyZWN0KGFjY3VyYWN5X2RmX2Zha2UsIGdyb3VwID0gIllvdVR1YmUiLCB5YXhpcyA9ICIlIENvcnJlY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJZb3VUdWJlIiwiJSBEaWZmIG9mIEZha2UgTmV3cyBTdG9yaWVzIElkZW50aWZpZWQgQ29ycmVjdGx5IikKZzggPC0gZm5fc3BsaW5lX2NoYXJ0X2NvcnJlY3QoYWNjdXJhY3lfZGZfZmFrZSwgZ3JvdXAgPSAiU25hcGNoYXQiLCB5YXhpcyA9ICIlIENvcnJlY3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJTbmFwY2hhdCIsIiUgRGlmZiBvZiBGYWtlIE5ld3MgU3RvcmllcyBJZGVudGlmaWVkIENvcnJlY3RseSIpCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShnMSwgZzIsIGczLCBnNCwgZzUsIGc2LCBnNywgZzgsIG5jb2w9MikKYGBgCgojIyBMaW1pdGF0aW9ucyAmIE5leHQgU3RlcHMKCjEuIEl0IGlzIHVuY2xlYXIgd2hldGhlciB0aGlzIGlzIHRydWx5IGEgcmVwcmVzZW50YXRpdmUgc2FtcGxlLiBJcHNvcyB1c2VkIHdlaWdodGluZyBmb3IgZGVtb2dyYXBoaWMgZ3JvdXBzIGluIHRoZWlyIGRpc3RyaWJ1dGlvbnMsIHdoaWNoIHdlIGRpZCBub3QgZG8gaW4gb3VyIGFuYWx5c2lzLgoyLiBXZSB1c2VkIGEgZGF0YXNldCBkZXNpZ25lZCBmb3IgYSBwYXJ0aWN1bGFyIHRhc2sgZm9yIGFub3RoZXIgcHVycG9zZS4gVGhpcyBwb3RlbnRpYWxseSBjcmVhdGVzIGlzc3Vlcy4gTm90ZWFibHksIHRoZXJlIGlzIGJpYXMgaW4gd2hvIGNhbiBhbmQgY2Fubm90IHJlY2FsbCBjZXJ0YWluIG5ld3MgaGVhZGxpbmVzLiBUaG91Z2ggd2UgdHJpZWQgdG8gc2hvdyB0aGF0IHRoZXJlIHdhcyBub3QgYSBvbmUtdG8tb25lIGNvcnJlbGF0aW9uIHdpdGggcmVjYWxsIGFuZCBpZGVudGlmaWNhdGlvbiwgdGhlcmUgc3RpbGwgaXMgY2VydGFpbmx5IGEgc2lnbmlmaWNhbnQgcmVsYXRpb25zaGlwLgozLiBXZSB1c2VkIGEgZnJlcXVlbnRpc3QgbW9kZWwtYmFzZWQgYXBwcm9hY2ggYXMgdGhpcyBhbGlnbmVkIHdpdGggU1RBVFM3MDE7IGhvd2V2ZXIsIGEgQmF5ZXNuaWFuIGFwcHJvYWNoIHdvdWxkIGhhdmUgYmVlbiBtdWNoIG1vcmUgYXBwcm9wcmlhdGUgZm9yIHRoaXMgcHJvYmxlbS4gV2UgY291bGQgaGF2ZSB0cmllZCB0byBtYWtlIHN0YXRlbWVudHMgc3VjaCBhcywgIipnaXZlbiB0aGUgcmVzcG9uZGVudCByZWNhbGxlZCA0IGhlYWRsaW5lcyAoMiByZWFsIGFuZCAyIGZha2UpIGFuZCB2b3RlZCBmb3IgVHJ1bXAsIHRoZSBwcm9iYWJpbGl0eSBvZiBjb3JyZWN0bHkgaWRlbnRpZnkgYWxsIDQgY29ycmVjdGx5IHdhcyBYKiIuIFdlIGNvdWxkIGhhdmUgY3JlYXRlZCBwcmlvcnMgZnJvbSB0aGUgZGF0YSBmb3IgdGhlIGdlbmVyYWwgcG9wdWxhdGlvbiBvZiBwZW9wbGUgeW91IHJlY2FsbGVkIDQgaGVhZGxpbmVzLgoKCgo=