• Libraries
  • Data fetching
  • Encoding the data with correct types
  • Partial data sets and aggregates
  • Model expansion utility function

Libraries

# Data manipulation
library(plyr)
library(dplyr)
library(tidyr)
library(reshape2)
library(tibble)

# Pretty printing
library(scales)

# Fetching data
library(curl)

# Computing hashes, used for efficient model caching
library(hashr)

# Easier plotting
library(ggplot2); theme_set(theme_minimal())
library(likert)
library(bayesplot)
library(ggpubr)
library(HDInterval)
library(ggridges)

# Baysian modeling
library(brms)

Data fetching

We begin by importing the csv data from the data repository.

d.orig <- read.csv(curl("https://raw.githubusercontent.com/BrokenWindowsInvestigation/Data/master/data.csv"))
d.orig
ABCDEFGHIJ0123456789
session
<chr>
group
<chr>
education_level
<chr>
6033c6fc5af2c702367b3a93studentsBachelor degree
6033c6fc5af2c702367b3a93studentsBachelor degree
6033c7315af2c702367b3a94studentsSome master studies
6033c7315af2c702367b3a94studentsSome master studies
6033d69a5af2c702367b3a95studentsSome master studies
6033d69a5af2c702367b3a95studentsSome master studies
6033d90a5af2c702367b3a96studentsSome bachelor studies
6033d90a5af2c702367b3a96studentsSome bachelor studies
6033ea8a5af2c702367b3a97studentsSome bachelor studies
6034fc165af2c702367b3a98studentsSome master studies

Encoding the data with correct types

### Utility functions for encoding ###
encode.categorical <- function(column, categories) {
  factor(column, level = categories)
}

encode.bool <- function(column) {
  encode.categorical(column, c("true", "false"))
}
encode.logic <- function(column) {
  encode.categorical(column, c(TRUE, FALSE))
}

encode.orderedcategorical <- function(column, categories) {
  as.ordered(encode.categorical(column, categories))
}

encode.likert <- function(column) {
  encode.orderedcategorical(column, c(-3, -2, -1, 0, 1, 2, 3))
}

### Encode the original data ###
d <- data.frame(
  session = factor(d.orig$session), 
  
  time = d.orig$time,
  
  reused_logic_constructor = encode.bool(d.orig$reused_logic_constructor),
  reused_logic_validation  = encode.bool(d.orig$reused_logic_validation),
  
  equals.state   = encode.orderedcategorical(
    d.orig$equals_state, 
    c("Not implemented", "Duplicated", "Good")
  ),
  hashcode.state = encode.orderedcategorical(
    d.orig$hashcode_state, 
    c("Not implemented", "Duplicated", "Good")
  ),
  
  documentation = factor(d.orig$documentation),
  
  var_names_copied_all        = d.orig$var_names_copied_all,
  var_names_copied_good       = d.orig$var_names_copied_good,
  var_names_copied_good.ratio = d.orig$var_names_copied_good / d.orig$var_names_copied_all,
  var_names_new_all           = d.orig$var_names_new_all,
  var_names_new_good          = d.orig$var_names_new_good,
  var_names_new_good.ratio    = d.orig$var_names_new_good / d.orig$var_names_new_all,
  var_names_edited_all        = d.orig$var_names_edited_all,
  var_names_edited_good       = d.orig$var_names_edited_good,
  var_names_edited_good.ratio = d.orig$var_names_edited_good / d.orig$var_names_edited_all,
  
  sonarqube_issues          = 
    d.orig$sonarqube_issues_major + 
    d.orig$sonarqube_issues_minor + 
    d.orig$sonarqube_issues_info + 
    d.orig$sonarqube_issues_critical,
  sonarqube_issues.major    = d.orig$sonarqube_issues_major,
  sonarqube_issues.minor    = d.orig$sonarqube_issues_minor,
  sonarqube_issues.info     = d.orig$sonarqube_issues_info,
  sonarqube_issues.critical = d.orig$sonarqube_issues_critical,
  
  group = factor(d.orig$group),
  
  education_level = encode.orderedcategorical(d.orig$education_level, c(
    "None", 
    "Some bachelor studies", 
    "Bachelor degree", 
    "Some master studies", 
    "Master degree", 
    "Some Ph.D. studies", 
    "Ph. D."
  )),
  education_field = factor(d.orig$education_field),
  
  work_domain                 = factor(d.orig$work_domain),
  work_experience_programming = d.orig$work_experience_programming,
  work_experience_java        = d.orig$work_experience_java,
  
  workplace_pair_programming = encode.bool(d.orig$workplace_pair_programming),
  workplace_peer_review      = encode.bool(d.orig$workplace_peer_review),
  workplace_td_tracking      = encode.bool(d.orig$workplace_td_tracking),
  workplace_coding_standards = encode.bool(d.orig$workplace_coding_standards),
  
  task_completion = encode.orderedcategorical(d.orig$task_completion, c(
    "Not submitted", 
    "Does not compile", 
    "Invalid solution", 
    "Completed"
  )),
  
  quality_pre_task  = encode.likert(d.orig$quality_pre_task),
  quality_post_task = encode.likert(d.orig$quality_post_task),
  
  high_debt_version = encode.bool(d.orig$high_debt_version),
  scenario          = encode.categorical(d.orig$scenario, c("booking", "tickets")),
  order             = encode.orderedcategorical(d.orig$order, c(0, 1)),
  
  modified_lines         = d.orig$modified_lines,
  large_structure_change = encode.bool(d.orig$large_structure_change)
)

d$equals.exists <- encode.logic(d$equals.state != "Not implemented")
d$hashcode.exists <-encode.logic(d$hashcode.state != "Not implemented")

str(d)
## 'data.frame':    73 obs. of  41 variables:
##  $ session                    : Factor w/ 43 levels "6033c6fc5af2c702367b3a93",..: 1 1 2 2 3 3 4 4 5 6 ...
##  $ time                       : int  1055 2263 3249 12 1382 1197 3855 1984 0 5309 ...
##  $ reused_logic_constructor   : Factor w/ 2 levels "true","false": 1 2 2 2 1 1 1 1 2 2 ...
##  $ reused_logic_validation    : Factor w/ 2 levels "true","false": 1 2 2 2 1 1 1 1 2 1 ...
##  $ equals.state               : Ord.factor w/ 3 levels "Not implemented"<..: 2 2 1 1 1 3 1 3 1 3 ...
##  $ hashcode.state             : Ord.factor w/ 3 levels "Not implemented"<..: 2 2 1 1 1 3 1 3 1 3 ...
##  $ documentation              : Factor w/ 4 levels "Correct","Incorrect",..: 3 2 2 3 1 3 3 2 4 3 ...
##  $ var_names_copied_all       : int  6 12 15 0 8 7 5 7 0 9 ...
##  $ var_names_copied_good      : int  6 1 3 0 8 5 3 7 0 1 ...
##  $ var_names_copied_good.ratio: num  1 0.0833 0.2 NaN 1 ...
##  $ var_names_new_all          : int  4 3 4 0 4 2 4 2 0 22 ...
##  $ var_names_new_good         : int  4 1 0 0 4 2 4 2 0 11 ...
##  $ var_names_new_good.ratio   : num  1 0.333 0 NaN 1 ...
##  $ var_names_edited_all       : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ var_names_edited_good      : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ var_names_edited_good.ratio: num  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ...
##  $ sonarqube_issues           : int  0 4 5 0 0 3 1 0 0 12 ...
##  $ sonarqube_issues.major     : int  0 2 5 0 0 3 1 0 0 8 ...
##  $ sonarqube_issues.minor     : int  0 2 0 0 0 0 0 0 0 4 ...
##  $ sonarqube_issues.info      : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ sonarqube_issues.critical  : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ group                      : Factor w/ 7 levels "code-interested",..: 7 7 7 7 7 7 7 7 7 7 ...
##  $ education_level            : Ord.factor w/ 6 levels "None"<"Some bachelor studies"<..: 3 3 4 4 4 4 2 2 2 4 ...
##  $ education_field            : Factor w/ 7 levels "Civil Engineering",..: 2 2 2 2 2 2 7 7 7 7 ...
##  $ work_domain                : Factor w/ 16 levels "Adtech","App",..: 12 12 3 3 7 7 3 3 12 16 ...
##  $ work_experience_programming: num  0 0 0.2 0.2 1 1 0 0 0 2 ...
##  $ work_experience_java       : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ workplace_pair_programming : Factor w/ 2 levels "true","false": 1 1 2 2 2 2 2 2 2 2 ...
##  $ workplace_peer_review      : Factor w/ 2 levels "true","false": 2 2 1 1 2 2 2 2 2 2 ...
##  $ workplace_td_tracking      : Factor w/ 2 levels "true","false": 2 2 2 2 2 2 2 2 2 2 ...
##  $ workplace_coding_standards : Factor w/ 2 levels "true","false": 2 2 1 1 2 2 1 1 2 2 ...
##  $ task_completion            : Ord.factor w/ 4 levels "Not submitted"<..: 2 4 3 1 4 4 4 4 1 4 ...
##  $ quality_pre_task           : Ord.factor w/ 7 levels "-3"<"-2"<"-1"<..: 4 3 6 6 4 2 2 6 4 3 ...
##  $ quality_post_task          : Ord.factor w/ 7 levels "-3"<"-2"<"-1"<..: 1 1 3 4 4 4 4 4 4 6 ...
##  $ high_debt_version          : Factor w/ 2 levels "true","false": 2 1 1 2 2 1 1 2 2 1 ...
##  $ scenario                   : Factor w/ 2 levels "booking","tickets": 1 2 1 2 1 2 1 2 1 1 ...
##  $ order                      : Ord.factor w/ 2 levels "0"<"1": 2 1 1 2 1 2 2 1 1 2 ...
##  $ modified_lines             : int  75 71 78 0 41 22 18 32 0 246 ...
##  $ large_structure_change     : Factor w/ 2 levels "true","false": 2 2 2 2 2 2 2 2 2 1 ...
##  $ equals.exists              : Factor w/ 2 levels "TRUE","FALSE": 1 1 2 2 2 1 2 1 2 1 ...
##  $ hashcode.exists            : Factor w/ 2 levels "TRUE","FALSE": 1 1 2 2 2 1 2 1 2 1 ...

Partial data sets and aggregates

For some models partial data sets and aggregates are needed.

d.sessions <- d %>% group_by(session) %>% dplyr::summarise(
  across(task_completion, min),
  across(c(
    education_level, 
    education_field, 
    work_domain, 
    group,
    work_experience_java, 
    work_experience_programming, 
    workplace_coding_standards, 
    workplace_pair_programming, 
    workplace_peer_review, 
    workplace_td_tracking
  ), first)
  ) 

d$work_experience_programming.s = scale(d$work_experience_programming)
d$work_experience_java.s = scale(d$work_experience_java)

d.sessions
ABCDEFGHIJ0123456789
session
<fct>
task_completion
<ord>
education_level
<ord>
6033c6fc5af2c702367b3a93Does not compileBachelor degree
6033c7315af2c702367b3a94Not submittedSome master studies
6033d69a5af2c702367b3a95CompletedSome master studies
6033d90a5af2c702367b3a96CompletedSome bachelor studies
6033ea8a5af2c702367b3a97Not submittedSome bachelor studies
6034fc165af2c702367b3a98CompletedSome master studies
603500725af2c702367b3a99CompletedSome bachelor studies
603f84f15af2c702367b3a9bInvalid solutionSome master studies
603f97625af2c702367b3a9dCompletedSome master studies
603fd5d95af2c702367b3a9eCompletedSome bachelor studies
d.sessions.completed <- d.sessions %>% filter(task_completion == "Completed")

d.sessions.completed
ABCDEFGHIJ0123456789
session
<fct>
task_completion
<ord>
education_level
<ord>
education_field
<fct>
work_domain
<fct>
6033d69a5af2c702367b3a95CompletedSome master studiesComputer ScienceFinance
6033d90a5af2c702367b3a96CompletedSome bachelor studiesSoftware EngineeringAutomotive
6034fc165af2c702367b3a98CompletedSome master studiesSoftware EngineeringWeb
603500725af2c702367b3a99CompletedSome bachelor studiesSoftware EngineeringNone
603f97625af2c702367b3a9dCompletedSome master studiesComputer ScienceWeb
603fd5d95af2c702367b3a9eCompletedSome bachelor studiesSoftware EngineeringAutomotive
60409b7b5af2c702367b3a9fCompletedSome master studiesSoftware EngineeringNone
604b82b5a7718fbed181b336CompletedMaster degreeSoftware EngineeringWeb
6050c1bf856f36729d2e5218CompletedSome master studiesSoftware EngineeringDevops
6050e1e7856f36729d2e5219CompletedSome bachelor studiesSoftware EngineeringMixed
d.completed <- d %>% filter(task_completion == "Completed")

d.completed$work_experience_programming.s = scale(d.completed$work_experience_programming)
d.completed$work_experience_java.s = scale(d.completed$work_experience_java)
d.completed$time.s = scale(d.completed$time)
d.completed$sonarqube_issues.s = scale(d.completed$sonarqube_issues)

d.completed
ABCDEFGHIJ0123456789
session
<fct>
time
<int>
reused_logic_constructor
<fct>
reused_logic_validation
<fct>
equals.state
<ord>
6033c6fc5af2c702367b3a932263falsefalseDuplicated
6033d69a5af2c702367b3a951382truetrueNot implemented
6033d69a5af2c702367b3a951197truetrueGood
6033d90a5af2c702367b3a963855truetrueNot implemented
6033d90a5af2c702367b3a961984truetrueGood
6034fc165af2c702367b3a985309falsetrueGood
6034fc165af2c702367b3a98868truetrueNot implemented
603500725af2c702367b3a99622falsefalseDuplicated
603500725af2c702367b3a99474falsefalseDuplicated
603f84f15af2c702367b3a9b448truetrueNot implemented
d.both_completed <- d %>% semi_join(d.sessions.completed, by = "session")

d.both_completed$work_experience_programming.s = scale(d.both_completed$work_experience_programming)
d.both_completed$work_experience_java.s = scale(d.both_completed$work_experience_java)
d.both_completed$time.s = scale(d.both_completed$time)
d.both_completed$sonarqube_issues.s = scale(d.both_completed$sonarqube_issues)

d.both_completed
ABCDEFGHIJ0123456789
session
<fct>
time
<int>
reused_logic_constructor
<fct>
reused_logic_validation
<fct>
equals.state
<ord>
6033d69a5af2c702367b3a951382truetrueNot implemented
6033d69a5af2c702367b3a951197truetrueGood
6033d90a5af2c702367b3a963855truetrueNot implemented
6033d90a5af2c702367b3a961984truetrueGood
6034fc165af2c702367b3a985309falsetrueGood
6034fc165af2c702367b3a98868truetrueNot implemented
603500725af2c702367b3a99622falsefalseDuplicated
603500725af2c702367b3a99474falsefalseDuplicated
603f97625af2c702367b3a9d1084falsefalseDuplicated
603f97625af2c702367b3a9d1170falsefalseDuplicated

Model expansion utility function

The function extendable_model takes some basic arguments for creating brms models and returns a function that can be called with additioanl parameters to combine with those passed to extendable_model. The extendable_model takes the following arguments:

  • base_name is a name that is used to identify this extendable model while caching.
  • base_formula the formula that will be extended and passed to brms::brm, represented as string.
  • data the data frame to be passed to brms::brm.
  • base_priors (NULL) is a vector of priors to be passed to brms::brm.
  • base_control (NULL) is a vector of control options to be passed to brms::brm.

The returned function takes the following arguments:

  • additional_variables (NULL) a vector of aditional variables (predictors) to pass to pass to brms::brm in adition to base_formula.
  • additional_priors (NULL) a vector of additioanl priors to pass to brms::brm in adition to base_priors.
  • only_priors (FALSE) indicates if the model should be epty and not compiled, usefull to extract default priors of a model.
  • sample_prior ("no") is passed to the sample_prior of brms::brm.
  • control_override (NULL) takes a vector of control arguments for brms::brm that will override base_control.
extendable_model <- function(
  base_name, 
  base_formula, 
  family, 
  data, 
  base_priors = NULL, 
  base_control = NULL
) {
  function(
    additional_variables = NULL, 
    additional_priors = NULL, 
    only_priors = FALSE, 
    sample_prior = "no", 
    control_override = NULL
  ) {
    # Sort variable names for consistent caching and naming
    additional_variables.sorted <- sort(additional_variables)
    
    # Build priors
    priors <- base_priors
    if (!is.null(additional_priors)) {
      priors <- c(base_priors, additional_priors)
    }
    if (only_priors) {
      priors <- NULL
    }
    
    # Build formula
    additional_variables.formula <- paste(additional_variables.sorted, collapse = " + ")
    formula <- base_formula
    if (!is.null(additional_variables)) {
      formula <- paste(base_formula, additional_variables.formula, sep = " + ")
    }
    
    # Build cache file name
    additional_variables.name <- paste(additional_variables.sorted, collapse = ".")
    name <- base_name
    if (!is.null(additional_variables)) {
      name <- paste(base_name, hash(additional_variables.name), sep = ".")
    }
    name <- paste(name, paste("sample_priors-", sample_prior, sep = ""), sep = ".")
    name <- paste(name, paste("priors_hash-", hash(priors), sep = ""), sep = ".")
    name <- paste(name, paste("formula_hash-", hash(formula), sep = ""), sep = ".")
    
    # Get control options
    control <- base_control
    if (!is.null(control_override)) {
      control <- control_override
    }
    
    # Create and return the brms model
    brm(
      formula = as.formula(formula),
      family = family,
      data = as.data.frame(data),
      prior = priors,
      empty = only_priors,
      sample_prior = sample_prior,
      file = paste("fits", name, sep = "/"),
      file_refit = "on_change",
      seed = 20210421,
      control = control
    )
  }
}

Example usage:

## Not run
m.with <- extendable_model(
  base_name = "m", 
  base_formula = "time ~ 1",
  family = negbinomial(), 
  data = d.both_completed, 
  base_priors = c(
    prior(normal(0, 1), class = "Intercept")
  )
)

prior_summary(m.with(only_priors = TRUE))

pp_check(m.with(sample_prior = "only"), nsamples = 200)

summary(m.with())

pp_check(m.with(), nsamples = 200)

loo(
  m.with(),
  m.with("high_debt_version"),
  m.with(c("high_debt_version", "scenario"))
)
## End(Not run)
LS0tCnRpdGxlOiAiU2V0dXAgJiBEYXRhIFByZXBhcmF0aW9uIgphdXRob3I6IEhhbXB1cyBCcm9tYW4gJiBXaWxsaWFtIExldsOpbgpkYXRlOiAyMDIxLTA1Cm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICBwYW5kb2NfYXJnczogWyAiLW8iLCAiZG9jcy9zZXR1cC5odG1sIiBdCi0tLQojIyBMaWJyYXJpZXMKCmBgYHtyIGxvYWQtbGlicmFyaWVzLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93JywgbWVzc2FnZT1GQUxTRX0KIyBEYXRhIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KHBseXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkodGliYmxlKQoKIyBQcmV0dHkgcHJpbnRpbmcKbGlicmFyeShzY2FsZXMpCgojIEZldGNoaW5nIGRhdGEKbGlicmFyeShjdXJsKQoKIyBDb21wdXRpbmcgaGFzaGVzLCB1c2VkIGZvciBlZmZpY2llbnQgbW9kZWwgY2FjaGluZwpsaWJyYXJ5KGhhc2hyKQoKIyBFYXNpZXIgcGxvdHRpbmcKbGlicmFyeShnZ3Bsb3QyKTsgdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkKbGlicmFyeShsaWtlcnQpCmxpYnJhcnkoYmF5ZXNwbG90KQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShIREludGVydmFsKQpsaWJyYXJ5KGdncmlkZ2VzKQoKIyBCYXlzaWFuIG1vZGVsaW5nCmxpYnJhcnkoYnJtcykKYGBgCgojIyBEYXRhIGZldGNoaW5nCgpXZSBiZWdpbiBieSBpbXBvcnRpbmcgdGhlIGNzdiBkYXRhIGZyb20gdGhlIFtkYXRhIHJlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS9Ccm9rZW5XaW5kb3dzSW52ZXN0aWdhdGlvbi9EYXRhKS4KCmBgYHtyIGZldGNoLWRhdGF9CmQub3JpZyA8LSByZWFkLmNzdihjdXJsKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQnJva2VuV2luZG93c0ludmVzdGlnYXRpb24vRGF0YS9tYXN0ZXIvZGF0YS5jc3YiKSkKZC5vcmlnCmBgYAoKIyMgRW5jb2RpbmcgdGhlIGRhdGEgd2l0aCBjb3JyZWN0IHR5cGVzCgpgYGB7ciBkYXRhLWVuY29kaW5nfQojIyMgVXRpbGl0eSBmdW5jdGlvbnMgZm9yIGVuY29kaW5nICMjIwplbmNvZGUuY2F0ZWdvcmljYWwgPC0gZnVuY3Rpb24oY29sdW1uLCBjYXRlZ29yaWVzKSB7CiAgZmFjdG9yKGNvbHVtbiwgbGV2ZWwgPSBjYXRlZ29yaWVzKQp9CgplbmNvZGUuYm9vbCA8LSBmdW5jdGlvbihjb2x1bW4pIHsKICBlbmNvZGUuY2F0ZWdvcmljYWwoY29sdW1uLCBjKCJ0cnVlIiwgImZhbHNlIikpCn0KZW5jb2RlLmxvZ2ljIDwtIGZ1bmN0aW9uKGNvbHVtbikgewogIGVuY29kZS5jYXRlZ29yaWNhbChjb2x1bW4sIGMoVFJVRSwgRkFMU0UpKQp9CgplbmNvZGUub3JkZXJlZGNhdGVnb3JpY2FsIDwtIGZ1bmN0aW9uKGNvbHVtbiwgY2F0ZWdvcmllcykgewogIGFzLm9yZGVyZWQoZW5jb2RlLmNhdGVnb3JpY2FsKGNvbHVtbiwgY2F0ZWdvcmllcykpCn0KCmVuY29kZS5saWtlcnQgPC0gZnVuY3Rpb24oY29sdW1uKSB7CiAgZW5jb2RlLm9yZGVyZWRjYXRlZ29yaWNhbChjb2x1bW4sIGMoLTMsIC0yLCAtMSwgMCwgMSwgMiwgMykpCn0KCiMjIyBFbmNvZGUgdGhlIG9yaWdpbmFsIGRhdGEgIyMjCmQgPC0gZGF0YS5mcmFtZSgKICBzZXNzaW9uID0gZmFjdG9yKGQub3JpZyRzZXNzaW9uKSwgCiAgCiAgdGltZSA9IGQub3JpZyR0aW1lLAogIAogIHJldXNlZF9sb2dpY19jb25zdHJ1Y3RvciA9IGVuY29kZS5ib29sKGQub3JpZyRyZXVzZWRfbG9naWNfY29uc3RydWN0b3IpLAogIHJldXNlZF9sb2dpY192YWxpZGF0aW9uICA9IGVuY29kZS5ib29sKGQub3JpZyRyZXVzZWRfbG9naWNfdmFsaWRhdGlvbiksCiAgCiAgZXF1YWxzLnN0YXRlICAgPSBlbmNvZGUub3JkZXJlZGNhdGVnb3JpY2FsKAogICAgZC5vcmlnJGVxdWFsc19zdGF0ZSwgCiAgICBjKCJOb3QgaW1wbGVtZW50ZWQiLCAiRHVwbGljYXRlZCIsICJHb29kIikKICApLAogIGhhc2hjb2RlLnN0YXRlID0gZW5jb2RlLm9yZGVyZWRjYXRlZ29yaWNhbCgKICAgIGQub3JpZyRoYXNoY29kZV9zdGF0ZSwgCiAgICBjKCJOb3QgaW1wbGVtZW50ZWQiLCAiRHVwbGljYXRlZCIsICJHb29kIikKICApLAogIAogIGRvY3VtZW50YXRpb24gPSBmYWN0b3IoZC5vcmlnJGRvY3VtZW50YXRpb24pLAogIAogIHZhcl9uYW1lc19jb3BpZWRfYWxsICAgICAgICA9IGQub3JpZyR2YXJfbmFtZXNfY29waWVkX2FsbCwKICB2YXJfbmFtZXNfY29waWVkX2dvb2QgICAgICAgPSBkLm9yaWckdmFyX25hbWVzX2NvcGllZF9nb29kLAogIHZhcl9uYW1lc19jb3BpZWRfZ29vZC5yYXRpbyA9IGQub3JpZyR2YXJfbmFtZXNfY29waWVkX2dvb2QgLyBkLm9yaWckdmFyX25hbWVzX2NvcGllZF9hbGwsCiAgdmFyX25hbWVzX25ld19hbGwgICAgICAgICAgID0gZC5vcmlnJHZhcl9uYW1lc19uZXdfYWxsLAogIHZhcl9uYW1lc19uZXdfZ29vZCAgICAgICAgICA9IGQub3JpZyR2YXJfbmFtZXNfbmV3X2dvb2QsCiAgdmFyX25hbWVzX25ld19nb29kLnJhdGlvICAgID0gZC5vcmlnJHZhcl9uYW1lc19uZXdfZ29vZCAvIGQub3JpZyR2YXJfbmFtZXNfbmV3X2FsbCwKICB2YXJfbmFtZXNfZWRpdGVkX2FsbCAgICAgICAgPSBkLm9yaWckdmFyX25hbWVzX2VkaXRlZF9hbGwsCiAgdmFyX25hbWVzX2VkaXRlZF9nb29kICAgICAgID0gZC5vcmlnJHZhcl9uYW1lc19lZGl0ZWRfZ29vZCwKICB2YXJfbmFtZXNfZWRpdGVkX2dvb2QucmF0aW8gPSBkLm9yaWckdmFyX25hbWVzX2VkaXRlZF9nb29kIC8gZC5vcmlnJHZhcl9uYW1lc19lZGl0ZWRfYWxsLAogIAogIHNvbmFycXViZV9pc3N1ZXMgICAgICAgICAgPSAKICAgIGQub3JpZyRzb25hcnF1YmVfaXNzdWVzX21ham9yICsgCiAgICBkLm9yaWckc29uYXJxdWJlX2lzc3Vlc19taW5vciArIAogICAgZC5vcmlnJHNvbmFycXViZV9pc3N1ZXNfaW5mbyArIAogICAgZC5vcmlnJHNvbmFycXViZV9pc3N1ZXNfY3JpdGljYWwsCiAgc29uYXJxdWJlX2lzc3Vlcy5tYWpvciAgICA9IGQub3JpZyRzb25hcnF1YmVfaXNzdWVzX21ham9yLAogIHNvbmFycXViZV9pc3N1ZXMubWlub3IgICAgPSBkLm9yaWckc29uYXJxdWJlX2lzc3Vlc19taW5vciwKICBzb25hcnF1YmVfaXNzdWVzLmluZm8gICAgID0gZC5vcmlnJHNvbmFycXViZV9pc3N1ZXNfaW5mbywKICBzb25hcnF1YmVfaXNzdWVzLmNyaXRpY2FsID0gZC5vcmlnJHNvbmFycXViZV9pc3N1ZXNfY3JpdGljYWwsCiAgCiAgZ3JvdXAgPSBmYWN0b3IoZC5vcmlnJGdyb3VwKSwKICAKICBlZHVjYXRpb25fbGV2ZWwgPSBlbmNvZGUub3JkZXJlZGNhdGVnb3JpY2FsKGQub3JpZyRlZHVjYXRpb25fbGV2ZWwsIGMoCiAgICAiTm9uZSIsIAogICAgIlNvbWUgYmFjaGVsb3Igc3R1ZGllcyIsIAogICAgIkJhY2hlbG9yIGRlZ3JlZSIsIAogICAgIlNvbWUgbWFzdGVyIHN0dWRpZXMiLCAKICAgICJNYXN0ZXIgZGVncmVlIiwgCiAgICAiU29tZSBQaC5ELiBzdHVkaWVzIiwgCiAgICAiUGguIEQuIgogICkpLAogIGVkdWNhdGlvbl9maWVsZCA9IGZhY3RvcihkLm9yaWckZWR1Y2F0aW9uX2ZpZWxkKSwKICAKICB3b3JrX2RvbWFpbiAgICAgICAgICAgICAgICAgPSBmYWN0b3IoZC5vcmlnJHdvcmtfZG9tYWluKSwKICB3b3JrX2V4cGVyaWVuY2VfcHJvZ3JhbW1pbmcgPSBkLm9yaWckd29ya19leHBlcmllbmNlX3Byb2dyYW1taW5nLAogIHdvcmtfZXhwZXJpZW5jZV9qYXZhICAgICAgICA9IGQub3JpZyR3b3JrX2V4cGVyaWVuY2VfamF2YSwKICAKICB3b3JrcGxhY2VfcGFpcl9wcm9ncmFtbWluZyA9IGVuY29kZS5ib29sKGQub3JpZyR3b3JrcGxhY2VfcGFpcl9wcm9ncmFtbWluZyksCiAgd29ya3BsYWNlX3BlZXJfcmV2aWV3ICAgICAgPSBlbmNvZGUuYm9vbChkLm9yaWckd29ya3BsYWNlX3BlZXJfcmV2aWV3KSwKICB3b3JrcGxhY2VfdGRfdHJhY2tpbmcgICAgICA9IGVuY29kZS5ib29sKGQub3JpZyR3b3JrcGxhY2VfdGRfdHJhY2tpbmcpLAogIHdvcmtwbGFjZV9jb2Rpbmdfc3RhbmRhcmRzID0gZW5jb2RlLmJvb2woZC5vcmlnJHdvcmtwbGFjZV9jb2Rpbmdfc3RhbmRhcmRzKSwKICAKICB0YXNrX2NvbXBsZXRpb24gPSBlbmNvZGUub3JkZXJlZGNhdGVnb3JpY2FsKGQub3JpZyR0YXNrX2NvbXBsZXRpb24sIGMoCiAgICAiTm90IHN1Ym1pdHRlZCIsIAogICAgIkRvZXMgbm90IGNvbXBpbGUiLCAKICAgICJJbnZhbGlkIHNvbHV0aW9uIiwgCiAgICAiQ29tcGxldGVkIgogICkpLAogIAogIHF1YWxpdHlfcHJlX3Rhc2sgID0gZW5jb2RlLmxpa2VydChkLm9yaWckcXVhbGl0eV9wcmVfdGFzayksCiAgcXVhbGl0eV9wb3N0X3Rhc2sgPSBlbmNvZGUubGlrZXJ0KGQub3JpZyRxdWFsaXR5X3Bvc3RfdGFzayksCiAgCiAgaGlnaF9kZWJ0X3ZlcnNpb24gPSBlbmNvZGUuYm9vbChkLm9yaWckaGlnaF9kZWJ0X3ZlcnNpb24pLAogIHNjZW5hcmlvICAgICAgICAgID0gZW5jb2RlLmNhdGVnb3JpY2FsKGQub3JpZyRzY2VuYXJpbywgYygiYm9va2luZyIsICJ0aWNrZXRzIikpLAogIG9yZGVyICAgICAgICAgICAgID0gZW5jb2RlLm9yZGVyZWRjYXRlZ29yaWNhbChkLm9yaWckb3JkZXIsIGMoMCwgMSkpLAogIAogIG1vZGlmaWVkX2xpbmVzICAgICAgICAgPSBkLm9yaWckbW9kaWZpZWRfbGluZXMsCiAgbGFyZ2Vfc3RydWN0dXJlX2NoYW5nZSA9IGVuY29kZS5ib29sKGQub3JpZyRsYXJnZV9zdHJ1Y3R1cmVfY2hhbmdlKQopCgpkJGVxdWFscy5leGlzdHMgPC0gZW5jb2RlLmxvZ2ljKGQkZXF1YWxzLnN0YXRlICE9ICJOb3QgaW1wbGVtZW50ZWQiKQpkJGhhc2hjb2RlLmV4aXN0cyA8LWVuY29kZS5sb2dpYyhkJGhhc2hjb2RlLnN0YXRlICE9ICJOb3QgaW1wbGVtZW50ZWQiKQoKc3RyKGQpCmBgYAoKIyMgUGFydGlhbCBkYXRhIHNldHMgYW5kIGFnZ3JlZ2F0ZXMgey50YWJzZXR9CgpGb3Igc29tZSBtb2RlbHMgcGFydGlhbCBkYXRhIHNldHMgYW5kIGFnZ3JlZ2F0ZXMgYXJlIG5lZWRlZC4KCiMjIyBTZXNzaW9ucyBhcyByb3dzCgpgYGB7ciBwYXJ0aWFsLXNlc3Npb25zfQpkLnNlc3Npb25zIDwtIGQgJT4lIGdyb3VwX2J5KHNlc3Npb24pICU+JSBkcGx5cjo6c3VtbWFyaXNlKAogIGFjcm9zcyh0YXNrX2NvbXBsZXRpb24sIG1pbiksCiAgYWNyb3NzKGMoCiAgICBlZHVjYXRpb25fbGV2ZWwsIAogICAgZWR1Y2F0aW9uX2ZpZWxkLCAKICAgIHdvcmtfZG9tYWluLCAKICAgIGdyb3VwLAogICAgd29ya19leHBlcmllbmNlX2phdmEsIAogICAgd29ya19leHBlcmllbmNlX3Byb2dyYW1taW5nLCAKICAgIHdvcmtwbGFjZV9jb2Rpbmdfc3RhbmRhcmRzLCAKICAgIHdvcmtwbGFjZV9wYWlyX3Byb2dyYW1taW5nLCAKICAgIHdvcmtwbGFjZV9wZWVyX3JldmlldywgCiAgICB3b3JrcGxhY2VfdGRfdHJhY2tpbmcKICApLCBmaXJzdCkKICApIAoKZCR3b3JrX2V4cGVyaWVuY2VfcHJvZ3JhbW1pbmcucyA9IHNjYWxlKGQkd29ya19leHBlcmllbmNlX3Byb2dyYW1taW5nKQpkJHdvcmtfZXhwZXJpZW5jZV9qYXZhLnMgPSBzY2FsZShkJHdvcmtfZXhwZXJpZW5jZV9qYXZhKQoKZC5zZXNzaW9ucwpgYGAKCiMjIyBTZXNzaW9ucyBhcyByb3dzIChvbmx5IGNvbXBsZXRlZCkKCmBgYHtyIHBhcnRpYWwtc2Vzc2lvbnMtY29tcGxldGVkfQpkLnNlc3Npb25zLmNvbXBsZXRlZCA8LSBkLnNlc3Npb25zICU+JSBmaWx0ZXIodGFza19jb21wbGV0aW9uID09ICJDb21wbGV0ZWQiKQoKZC5zZXNzaW9ucy5jb21wbGV0ZWQKYGBgCgojIyMgT25seSBjb21wbGV0ZWQKCmBgYHtyIHBhcnRpYWwtc3VibWl0dGVkfQpkLmNvbXBsZXRlZCA8LSBkICU+JSBmaWx0ZXIodGFza19jb21wbGV0aW9uID09ICJDb21wbGV0ZWQiKQoKZC5jb21wbGV0ZWQkd29ya19leHBlcmllbmNlX3Byb2dyYW1taW5nLnMgPSBzY2FsZShkLmNvbXBsZXRlZCR3b3JrX2V4cGVyaWVuY2VfcHJvZ3JhbW1pbmcpCmQuY29tcGxldGVkJHdvcmtfZXhwZXJpZW5jZV9qYXZhLnMgPSBzY2FsZShkLmNvbXBsZXRlZCR3b3JrX2V4cGVyaWVuY2VfamF2YSkKZC5jb21wbGV0ZWQkdGltZS5zID0gc2NhbGUoZC5jb21wbGV0ZWQkdGltZSkKZC5jb21wbGV0ZWQkc29uYXJxdWJlX2lzc3Vlcy5zID0gc2NhbGUoZC5jb21wbGV0ZWQkc29uYXJxdWJlX2lzc3VlcykKCmQuY29tcGxldGVkCmBgYAoKIyMjIE9ubHkgYm90aCBjb21wbGV0ZWQKYGBge3IgcGFydGlhbC1zdWJtaXR0ZWQtYm90aH0KZC5ib3RoX2NvbXBsZXRlZCA8LSBkICU+JSBzZW1pX2pvaW4oZC5zZXNzaW9ucy5jb21wbGV0ZWQsIGJ5ID0gInNlc3Npb24iKQoKZC5ib3RoX2NvbXBsZXRlZCR3b3JrX2V4cGVyaWVuY2VfcHJvZ3JhbW1pbmcucyA9IHNjYWxlKGQuYm90aF9jb21wbGV0ZWQkd29ya19leHBlcmllbmNlX3Byb2dyYW1taW5nKQpkLmJvdGhfY29tcGxldGVkJHdvcmtfZXhwZXJpZW5jZV9qYXZhLnMgPSBzY2FsZShkLmJvdGhfY29tcGxldGVkJHdvcmtfZXhwZXJpZW5jZV9qYXZhKQpkLmJvdGhfY29tcGxldGVkJHRpbWUucyA9IHNjYWxlKGQuYm90aF9jb21wbGV0ZWQkdGltZSkKZC5ib3RoX2NvbXBsZXRlZCRzb25hcnF1YmVfaXNzdWVzLnMgPSBzY2FsZShkLmJvdGhfY29tcGxldGVkJHNvbmFycXViZV9pc3N1ZXMpCgpkLmJvdGhfY29tcGxldGVkCmBgYAoKIyMgTW9kZWwgZXhwYW5zaW9uIHV0aWxpdHkgZnVuY3Rpb24KClRoZSBmdW5jdGlvbiBgZXh0ZW5kYWJsZV9tb2RlbGAgdGFrZXMgc29tZSBiYXNpYyBhcmd1bWVudHMgZm9yIGNyZWF0aW5nIGJybXMgbW9kZWxzIGFuZCByZXR1cm5zIGEgZnVuY3Rpb24gdGhhdCBjYW4gYmUgY2FsbGVkIHdpdGggYWRkaXRpb2FubCBwYXJhbWV0ZXJzIHRvIGNvbWJpbmUgd2l0aCB0aG9zZSBwYXNzZWQgdG8gYGV4dGVuZGFibGVfbW9kZWxgLiBUaGUgYGV4dGVuZGFibGVfbW9kZWxgIHRha2VzIHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzOgoKKiBgYmFzZV9uYW1lYCBpcyBhIG5hbWUgdGhhdCBpcyB1c2VkIHRvIGlkZW50aWZ5IHRoaXMgZXh0ZW5kYWJsZSBtb2RlbCB3aGlsZSBjYWNoaW5nLgoqIGBiYXNlX2Zvcm11bGFgIHRoZSBmb3JtdWxhIHRoYXQgd2lsbCBiZSBleHRlbmRlZCBhbmQgcGFzc2VkIHRvIGBicm1zOjpicm1gLCByZXByZXNlbnRlZCBhcyBzdHJpbmcuCiogYGRhdGFgIHRoZSBkYXRhIGZyYW1lIHRvIGJlIHBhc3NlZCB0byBgYnJtczo6YnJtYC4KKiBgYmFzZV9wcmlvcnNgIChgTlVMTGApIGlzIGEgdmVjdG9yIG9mIHByaW9ycyB0byBiZSBwYXNzZWQgdG8gYGJybXM6OmJybWAuCiogYGJhc2VfY29udHJvbGAgKGBOVUxMYCkgaXMgYSB2ZWN0b3Igb2YgY29udHJvbCBvcHRpb25zIHRvIGJlIHBhc3NlZCB0byBgYnJtczo6YnJtYC4KClRoZSByZXR1cm5lZCBmdW5jdGlvbiB0YWtlcyB0aGUgZm9sbG93aW5nIGFyZ3VtZW50czoKCiogYGFkZGl0aW9uYWxfdmFyaWFibGVzYCAoYE5VTExgKSBhIHZlY3RvciBvZiBhZGl0aW9uYWwgdmFyaWFibGVzIChwcmVkaWN0b3JzKSB0byBwYXNzIHRvIHBhc3MgdG8gYGJybXM6OmJybWAgaW4gYWRpdGlvbiB0byBgYmFzZV9mb3JtdWxhYC4KKiBgYWRkaXRpb25hbF9wcmlvcnNgIChgTlVMTGApIGEgdmVjdG9yIG9mIGFkZGl0aW9hbmwgcHJpb3JzIHRvIHBhc3MgdG8gYGJybXM6OmJybWAgaW4gYWRpdGlvbiB0byBgYmFzZV9wcmlvcnNgLgoqIGBvbmx5X3ByaW9yc2AgKGBGQUxTRWApIGluZGljYXRlcyBpZiB0aGUgbW9kZWwgc2hvdWxkIGJlIGVwdHkgYW5kIG5vdCBjb21waWxlZCwgdXNlZnVsbCB0byBleHRyYWN0IGRlZmF1bHQgcHJpb3JzIG9mIGEgbW9kZWwuCiogYHNhbXBsZV9wcmlvcmAgKGAibm8iYCkgaXMgcGFzc2VkIHRvIHRoZSBgc2FtcGxlX3ByaW9yYCBvZiBgYnJtczo6YnJtYC4KKiBgY29udHJvbF9vdmVycmlkZWAgKGBOVUxMYCkgdGFrZXMgYSB2ZWN0b3Igb2YgYGNvbnRyb2xgIGFyZ3VtZW50cyBmb3IgYGJybXM6OmJybWAgdGhhdCB3aWxsIG92ZXJyaWRlIGBiYXNlX2NvbnRyb2xgLgoKYGBge3IgZXh0ZW5kYWJsZS1tb2RlbH0KZXh0ZW5kYWJsZV9tb2RlbCA8LSBmdW5jdGlvbigKICBiYXNlX25hbWUsIAogIGJhc2VfZm9ybXVsYSwgCiAgZmFtaWx5LCAKICBkYXRhLCAKICBiYXNlX3ByaW9ycyA9IE5VTEwsIAogIGJhc2VfY29udHJvbCA9IE5VTEwKKSB7CiAgZnVuY3Rpb24oCiAgICBhZGRpdGlvbmFsX3ZhcmlhYmxlcyA9IE5VTEwsIAogICAgYWRkaXRpb25hbF9wcmlvcnMgPSBOVUxMLCAKICAgIG9ubHlfcHJpb3JzID0gRkFMU0UsIAogICAgc2FtcGxlX3ByaW9yID0gIm5vIiwgCiAgICBjb250cm9sX292ZXJyaWRlID0gTlVMTAogICkgewogICAgIyBTb3J0IHZhcmlhYmxlIG5hbWVzIGZvciBjb25zaXN0ZW50IGNhY2hpbmcgYW5kIG5hbWluZwogICAgYWRkaXRpb25hbF92YXJpYWJsZXMuc29ydGVkIDwtIHNvcnQoYWRkaXRpb25hbF92YXJpYWJsZXMpCiAgICAKICAgICMgQnVpbGQgcHJpb3JzCiAgICBwcmlvcnMgPC0gYmFzZV9wcmlvcnMKICAgIGlmICghaXMubnVsbChhZGRpdGlvbmFsX3ByaW9ycykpIHsKICAgICAgcHJpb3JzIDwtIGMoYmFzZV9wcmlvcnMsIGFkZGl0aW9uYWxfcHJpb3JzKQogICAgfQogICAgaWYgKG9ubHlfcHJpb3JzKSB7CiAgICAgIHByaW9ycyA8LSBOVUxMCiAgICB9CiAgICAKICAgICMgQnVpbGQgZm9ybXVsYQogICAgYWRkaXRpb25hbF92YXJpYWJsZXMuZm9ybXVsYSA8LSBwYXN0ZShhZGRpdGlvbmFsX3ZhcmlhYmxlcy5zb3J0ZWQsIGNvbGxhcHNlID0gIiArICIpCiAgICBmb3JtdWxhIDwtIGJhc2VfZm9ybXVsYQogICAgaWYgKCFpcy5udWxsKGFkZGl0aW9uYWxfdmFyaWFibGVzKSkgewogICAgICBmb3JtdWxhIDwtIHBhc3RlKGJhc2VfZm9ybXVsYSwgYWRkaXRpb25hbF92YXJpYWJsZXMuZm9ybXVsYSwgc2VwID0gIiArICIpCiAgICB9CiAgICAKICAgICMgQnVpbGQgY2FjaGUgZmlsZSBuYW1lCiAgICBhZGRpdGlvbmFsX3ZhcmlhYmxlcy5uYW1lIDwtIHBhc3RlKGFkZGl0aW9uYWxfdmFyaWFibGVzLnNvcnRlZCwgY29sbGFwc2UgPSAiLiIpCiAgICBuYW1lIDwtIGJhc2VfbmFtZQogICAgaWYgKCFpcy5udWxsKGFkZGl0aW9uYWxfdmFyaWFibGVzKSkgewogICAgICBuYW1lIDwtIHBhc3RlKGJhc2VfbmFtZSwgaGFzaChhZGRpdGlvbmFsX3ZhcmlhYmxlcy5uYW1lKSwgc2VwID0gIi4iKQogICAgfQogICAgbmFtZSA8LSBwYXN0ZShuYW1lLCBwYXN0ZSgic2FtcGxlX3ByaW9ycy0iLCBzYW1wbGVfcHJpb3IsIHNlcCA9ICIiKSwgc2VwID0gIi4iKQogICAgbmFtZSA8LSBwYXN0ZShuYW1lLCBwYXN0ZSgicHJpb3JzX2hhc2gtIiwgaGFzaChwcmlvcnMpLCBzZXAgPSAiIiksIHNlcCA9ICIuIikKICAgIG5hbWUgPC0gcGFzdGUobmFtZSwgcGFzdGUoImZvcm11bGFfaGFzaC0iLCBoYXNoKGZvcm11bGEpLCBzZXAgPSAiIiksIHNlcCA9ICIuIikKICAgIAogICAgIyBHZXQgY29udHJvbCBvcHRpb25zCiAgICBjb250cm9sIDwtIGJhc2VfY29udHJvbAogICAgaWYgKCFpcy5udWxsKGNvbnRyb2xfb3ZlcnJpZGUpKSB7CiAgICAgIGNvbnRyb2wgPC0gY29udHJvbF9vdmVycmlkZQogICAgfQogICAgCiAgICAjIENyZWF0ZSBhbmQgcmV0dXJuIHRoZSBicm1zIG1vZGVsCiAgICBicm0oCiAgICAgIGZvcm11bGEgPSBhcy5mb3JtdWxhKGZvcm11bGEpLAogICAgICBmYW1pbHkgPSBmYW1pbHksCiAgICAgIGRhdGEgPSBhcy5kYXRhLmZyYW1lKGRhdGEpLAogICAgICBwcmlvciA9IHByaW9ycywKICAgICAgZW1wdHkgPSBvbmx5X3ByaW9ycywKICAgICAgc2FtcGxlX3ByaW9yID0gc2FtcGxlX3ByaW9yLAogICAgICBmaWxlID0gcGFzdGUoImZpdHMiLCBuYW1lLCBzZXAgPSAiLyIpLAogICAgICBmaWxlX3JlZml0ID0gIm9uX2NoYW5nZSIsCiAgICAgIHNlZWQgPSAyMDIxMDQyMSwKICAgICAgY29udHJvbCA9IGNvbnRyb2wKICAgICkKICB9Cn0KYGBgCgpFeGFtcGxlIHVzYWdlOgoKYGBge3IsIGV2YWw9RkFMU0UsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIyBOb3QgcnVuCm0ud2l0aCA8LSBleHRlbmRhYmxlX21vZGVsKAogIGJhc2VfbmFtZSA9ICJtIiwgCiAgYmFzZV9mb3JtdWxhID0gInRpbWUgfiAxIiwKICBmYW1pbHkgPSBuZWdiaW5vbWlhbCgpLCAKICBkYXRhID0gZC5ib3RoX2NvbXBsZXRlZCwgCiAgYmFzZV9wcmlvcnMgPSBjKAogICAgcHJpb3Iobm9ybWFsKDAsIDEpLCBjbGFzcyA9ICJJbnRlcmNlcHQiKQogICkKKQoKcHJpb3Jfc3VtbWFyeShtLndpdGgob25seV9wcmlvcnMgPSBUUlVFKSkKCnBwX2NoZWNrKG0ud2l0aChzYW1wbGVfcHJpb3IgPSAib25seSIpLCBuc2FtcGxlcyA9IDIwMCkKCnN1bW1hcnkobS53aXRoKCkpCgpwcF9jaGVjayhtLndpdGgoKSwgbnNhbXBsZXMgPSAyMDApCgpsb28oCiAgbS53aXRoKCksCiAgbS53aXRoKCJoaWdoX2RlYnRfdmVyc2lvbiIpLAogIG0ud2l0aChjKCJoaWdoX2RlYnRfdmVyc2lvbiIsICJzY2VuYXJpbyIpKQopCiMjIEVuZChOb3QgcnVuKQpgYGAK