Here, we apply the pre-trained model and our fine-tuned model to data not used for training, a holdout. The holdout sample was collected by Bainbridge et al. 2022.

knitr::opts_chunk$set(echo = TRUE, error = T, message = F, warning = F)

# Libraries and Settings

# Libs ---------------------------
library(knitr)
library(tidyverse)
library(arrow)
library(glue)
library(psych)
library(lavaan)
library(ggplot2)
library(plotly)
library(gridExtra)
library(broom)
library(broom.mixed)
library(brms)
library(tidybayes)
library(cmdstanr)
library(cowplot)

options(mc.cores = parallel::detectCores(), 
        brms.backend = "cmdstanr", 
        brms.file_refit = "on_change")


model_name = "ItemSimilarityTraining-20240502-trial12"
#model_name = "item-similarity-20231018-122504"
pretrained_model_name = "all-mpnet-base-v2"

data_path = glue("./")
pretrained_data_path = glue("./")

set.seed(42)


holdout <- arrow::read_feather(file = file.path(data_path, glue("data/intermediate/{model_name}.raw.osf-bainbridge-2021-s2-0.item_correlations.feather")))

pt_holdout <- arrow::read_feather(file = file.path(data_path, glue("data/intermediate/{pretrained_model_name}.raw.osf-bainbridge-2021-s2-0.item_correlations.feather")))

holdout_mapping_data = arrow::read_feather(
  file = file.path(data_path, glue("{model_name}.raw.osf-bainbridge-2021-s2-0.mapping2.feather"))
) %>%
  rename(scale_0 = scale0,
         scale_1 = scale1)

holdout_human_data = arrow::read_feather(
  file = file.path(data_path, glue("{model_name}.raw.osf-bainbridge-2021-s2-0.human.feather"))
)

holdout_scales <- arrow::read_feather(file.path(data_path, glue("{model_name}.raw.osf-bainbridge-2021-s2-0.scales.feather"))
)

N <- holdout_human_data %>% summarise_all(~ sum(!is.na(.))) %>% min()
total_N <- nrow(holdout_human_data)

The Bainbridge data was collected on N=493 respondents. The item with the most missing values still had n=480.

Synthetic inter-item correlations

holdout_llm <- holdout %>%
  left_join(holdout_mapping_data %>% select(variable_1 = variable, InstrumentA = instrument, ScaleA = scale_0, SubscaleA = scale_1)) %>%
  left_join(holdout_mapping_data %>% select(variable_2 = variable, InstrumentB = instrument, ScaleB = scale_0, SubscaleB = scale_1))

pt_holdout_llm <- pt_holdout %>%
  left_join(holdout_mapping_data %>% select(variable_1 = variable, InstrumentA = instrument, ScaleA = scale_0, SubscaleA = scale_1)) %>%
  left_join(holdout_mapping_data %>% select(variable_2 = variable, InstrumentB = instrument, ScaleB = scale_0, SubscaleB = scale_1))

Accuracy

se2 <- mean(holdout_llm$empirical_r_se^2)

r <- broom::tidy(cor.test(holdout_llm$empirical_r, holdout_llm$synthetic_r))
pt_r <- broom::tidy(cor.test(pt_holdout_llm$empirical_r, pt_holdout_llm$synthetic_r))

model <- paste0('
  # Latent variables
  PearsonLatent =~ 1*empirical_r

  # Fixing error variances based on known standard errors
  empirical_r ~~ ',se2,'*empirical_r

  # Relationship between latent variables
  PearsonLatent ~~ synthetic_r
')

fit <- sem(model, data = holdout_llm)
pt_fit <- sem(model, data = pt_holdout_llm)

m_synth_r_items <- brm(
  bf(empirical_r | mi(empirical_r_se) ~ synthetic_r + (1|mm(variable_1, variable_2)),
     sigma ~ s(synthetic_r)), data = holdout_llm, 
  file = "ignore/m_synth_r_items_lm")

sd_synth <- sd(m_synth_r_items$data$synthetic_r)

newdata <- m_synth_r_items$data %>% select(empirical_r, synthetic_r, empirical_r_se)
epreds <- epred_draws(newdata = newdata, obj = m_synth_r_items, re_formula = NA, ndraws = 200)
preds <- predicted_draws(newdata = newdata, obj = m_synth_r_items, re_formula = NA, ndraws = 200)
epred_preds <- epreds %>% left_join(preds)
by_draw <- epred_preds %>% group_by(.draw) %>% 
  summarise(mae = mean(abs(.epred - .prediction)),
            .epred = var(.epred),
            .prediction = var(.prediction),
            sigma = sqrt(.prediction - .epred),
            latent_r = sqrt(.epred/.prediction))
rm(epred_preds)

accuracy_bayes_items <- by_draw %>% mean_hdci(latent_r)

bind_rows(
  pt_r %>% 
    mutate(model = "pre-trained", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  standardizedsolution(pt_fit) %>% 
    filter(lhs == "PearsonLatent", rhs ==  "synthetic_r") %>% 
    mutate(model = "pre-trained", kind = "latent outcome (SEM)") %>% 
    select(model, kind, accuracy = est.std, 
           conf.low = ci.lower, conf.high = ci.upper),
  r %>% 
    mutate(model = "fine-tuned", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  standardizedsolution(fit) %>% 
    filter(lhs == "PearsonLatent", rhs ==  "synthetic_r") %>% 
    mutate(model = "fine-tuned", kind = "latent outcome (SEM)") %>% 
    select(model, kind, accuracy = est.std, 
           conf.low = ci.lower, conf.high = ci.upper),
  accuracy_bayes_items %>% 
    mutate(model = "fine-tuned", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper)
) %>% 
  knitr::kable(digits = 2)
model kind accuracy conf.low conf.high
pre-trained manifest 0.19 0.18 0.19
pre-trained latent outcome (SEM) 0.19 0.19 0.20
fine-tuned manifest 0.67 0.67 0.68
fine-tuned latent outcome (SEM) 0.70 0.70 0.70
fine-tuned latent outcome (Bayesian EIV) 0.71 0.70 0.72

Prediction error plot according to synthetic estimate

m_synth_r_items
##  Family: gaussian 
##   Links: mu = identity; sigma = log 
## Formula: empirical_r | mi(empirical_r_se) ~ synthetic_r + (1 | mm(variable_1, variable_2)) 
##          sigma ~ s(synthetic_r)
##    Data: holdout_llm (Number of observations: 87153) 
##   Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
##          total post-warmup draws = 4000
## 
## Smoothing Spline Hyperparameters:
##                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
## sds(sigma_ssynthetic_r_1)     0.64      0.24     0.30     1.25 1.00     1402
##                           Tail_ESS
## sds(sigma_ssynthetic_r_1)     2008
## 
## Multilevel Hyperparameters:
## ~mmvariable_1variable_2 (Number of levels: 418) 
##               Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## sd(Intercept)     0.03      0.00     0.03     0.03 1.00      889     1498
## 
## Regression Coefficients:
##                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
## Intercept               -0.01      0.00    -0.01    -0.00 1.01      636
## sigma_Intercept         -2.20      0.00    -2.21    -2.20 1.00     5202
## synthetic_r              0.79      0.00     0.78     0.80 1.00     5610
## sigma_ssynthetic_r_1    -1.50      0.82    -3.19    -0.03 1.00     2219
##                      Tail_ESS
## Intercept                1307
## sigma_Intercept          3543
## synthetic_r              3282
## sigma_ssynthetic_r_1     2824
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).
pred <- conditional_effects(m_synth_r_items, method = "predict")
kable(rmse_items <- by_draw %>% mean_hdci(sigma), caption = "Average prediction error (RMSE)", digits = 2)
Average prediction error (RMSE)
sigma .lower .upper .width .point .interval
0.11 0.11 0.11 0.95 mean hdci
kable(mae_items <- by_draw %>% mean_hdci(mae), caption = "Average prediction error (MAE)", digits = 2)
Average prediction error (MAE)
mae .lower .upper .width .point .interval
0.09 0.09 0.09 0.95 mean hdci
plot_prediction_error_items <- plot(conditional_effects(m_synth_r_items, dpar = "sigma"), plot = F)[[1]] + 
  theme_bw() + 
  xlab("Synthetic inter-item correlation") + 
  ylab("Prediction error (sigma)") +
  geom_smooth(stat = "identity", color = "#a48500", fill = "#EDC951")

plot_prediction_error_items

Scatter plot

ggplot(holdout_llm, aes(synthetic_r, empirical_r, 
              ymin = empirical_r - empirical_r_se,
              ymax = empirical_r + empirical_r_se)) + 
  geom_abline(linetype = "dashed") +
  geom_point(color = "#00A0B0", alpha = 0.1, size = 1) +
  geom_smooth(aes(
    x = synthetic_r,
    y = estimate__,
    ymin = lower__,
    ymax = upper__,
  ), stat = "identity", 
  color = "#a48500",
  fill = "#EDC951",
  data = as.data.frame(pred$synthetic_r)) +
  xlab("Synthetic inter-item correlation") + 
  ylab("Empirical inter-item correlation") +
  theme_bw() +
  coord_fixed(xlim = c(-1,1), ylim = c(-1,1)) -> plot_items
plot_items

Interactive plot

This plot shows only 2000 randomly selected item pairs to conserve memory. A full interactive plot exists, but may react slowly.

item_pair_table <- holdout_llm %>% 
   left_join(holdout_mapping_data %>% select(variable_1 = variable,
                                             item_text_1 = item_text)) %>% 
   left_join(holdout_mapping_data %>% select(variable_2 = variable,
                                             item_text_2 = item_text))

# item_pair_table %>% filter(str_length(item_text_1) < 30, str_length(item_text_2) < 30) %>% 
#   left_join(pt_holdout_llm %>% rename(synthetic_r_pt = synthetic_r)) %>% 
#   select(item_text_1, item_text_2, empirical_r, synthetic_r, synthetic_r_pt) %>% View()
rio::export(item_pair_table, "ignore/item_pair_table.feather")

(item_pair_table %>% 
  mutate(synthetic_r = round(synthetic_r, 2),
         empirical_r = round(empirical_r, 2),
         items = str_replace_all(str_c(item_text_1, "\n", item_text_2),
                                  "_+", " ")) %>% 
    sample_n(2000) %>%
ggplot(., aes(synthetic_r, empirical_r, 
              # ymin = empirical_r - empirical_r_se, 
              # ymax = empirical_r + empirical_r_se, 
              label = items)) + 
  geom_abline(linetype = "dashed") +
  geom_point(color = "#00A0B0", alpha = 0.3, size = 1) +
  xlab("Synthetic inter-item correlation") + 
  ylab("Empirical inter-item correlation") +
  theme_bw() +
  coord_fixed(xlim = c(-1,1), ylim = c(-1,1))) %>% 
  ggplotly()
item_pair_table <- item_pair_table %>% 
  mutate(empirical_r = sprintf("%.2f±%.3f", empirical_r,
                                 empirical_r_se),
           synthetic_r = sprintf("%.2f", synthetic_r)) %>% 
  select(item_text_1, item_text_2, empirical_r, synthetic_r)
rio::export(item_pair_table, "item_pair_table.xlsx")

Robustness checks

Comparing spline and polynomial models for heteroscedasticity

m_synth_r_items_poly <- brm(
  bf(empirical_r | mi(empirical_r_se) ~ synthetic_r + (1|mm(variable_1, variable_2)),
     sigma ~ poly(synthetic_r, degree = 3)), data = holdout_llm, 
  file = "ignore/m_synth_r_items_lm_poly")

newdata <- m_synth_r_items_poly$data %>% select(empirical_r, synthetic_r, empirical_r_se)
epreds <- epred_draws(newdata = newdata, obj = m_synth_r_items_poly, re_formula = NA, ndraws = 200)
preds <- predicted_draws(newdata = newdata, obj = m_synth_r_items_poly, re_formula = NA, ndraws = 200)
epred_preds <- epreds %>% left_join(preds)
by_draw <- epred_preds %>% group_by(.draw) %>% 
  summarise(.epred = var(.epred),
            .prediction = var(.prediction),
            sigma = sqrt(.prediction - .epred),
            latent_r = sqrt(.epred/.prediction))

accuracy_bayes_items_poly <- by_draw %>% mean_hdci(latent_r)

bind_rows(
  accuracy_bayes_items %>% 
    mutate(model = "spline", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper),
  accuracy_bayes_items_poly %>% 
    mutate(model = "polynomial", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper)
) %>% 
  knitr::kable(digits = 2, caption = "Comparing spline and polynomial models for item correlations")
Comparing spline and polynomial models for item correlations
model kind accuracy conf.low conf.high
spline latent outcome (Bayesian EIV) 0.71 0.7 0.72
polynomial latent outcome (Bayesian EIV) 0.71 0.7 0.72
plot_prediction_error_items_poly <- plot(conditional_effects(m_synth_r_items_poly, dpar = "sigma"), plot = F)[[1]] + 
  theme_bw() + 
  xlab("Synthetic inter-item correlation") + 
  ylab("Prediction error (sigma)") +
  geom_smooth(stat = "identity", color = "#a48500", fill = "#EDC951")

plot_prediction_error_items_poly

Is the accuracy lower within/across scales and instruments?

holdout_llm %>% 
  mutate(same_instrument = if_else(InstrumentA == InstrumentB, 1, 0,0),
         same_scale = if_else(ScaleA == ScaleB, 1,0,0),
         same_subscale = if_else(same_scale & SubscaleA == SubscaleB, 1,0,0)) %>% 
  group_by(same_scale, same_instrument, same_subscale) %>% 
  summarise(broom::tidy(cor.test(synthetic_r, empirical_r)), sd_emp_r = sd(empirical_r), n = n()) %>% 
  select(same_instrument, same_scale, same_subscale, r = estimate, conf.low, conf.high, n, sd_emp_r) %>% 
  arrange(same_instrument, same_scale, same_subscale) %>% 
  kable()
same_instrument same_scale same_subscale r conf.low conf.high n sd_emp_r
0 0 0 0.6525977 0.6484796 0.6566776 75364 0.1537790
0 1 0 0.8269917 0.8095247 0.8429959 1376 0.2801202
0 1 1 0.8269181 0.7293619 0.8915144 64 0.4934840
1 0 0 0.6005392 0.5855629 0.6151056 7200 0.1365890
1 1 0 0.7571886 0.7404990 0.7729447 2662 0.2582187
1 1 1 0.8372433 0.8085383 0.8619731 487 0.3847884

Is the accuracy lower outside classic Big Five?

holdout_llm %>% 
  mutate(big_five = case_when(
    str_detect(InstrumentA, "(Personality|Big Five)") & str_detect(InstrumentB, "(Personality|Big Five)") ~ "both",
    str_detect(InstrumentA, "(Personality|Big Five)") | str_detect(InstrumentB, "(Personality|Big Five)") ~ "either",
    TRUE ~ "none"
         )) %>% 
  group_by(big_five) %>% 
  summarise(broom::tidy(cor.test(synthetic_r, empirical_r)), sd_emp_r = sd(empirical_r), n = n()) %>% 
  select(big_five, r = estimate, conf.low, conf.high, n, sd_emp_r) %>% 
  arrange(big_five) %>% 
  kable()
big_five r conf.low conf.high n sd_emp_r
both 0.7105135 0.7027822 0.7180770 16110 0.1754473
either 0.6618969 0.6565425 0.6671846 42840 0.1525947
none 0.6653591 0.6588041 0.6718132 28203 0.1697589

Is the accuracy lower for items that have low variance?

item_variances <- holdout_human_data %>% summarise_all(~ sd(., na.rm = T)) %>% 
  pivot_longer(everything(), names_to = "variable", values_to = "item_sd")

by_max_cov <- holdout_llm %>% 
  left_join(item_variances, by = c("variable_1" = "variable")) %>% 
  left_join(item_variances, by = c("variable_2" = "variable"), suffix = c("_1", "_2")) %>% 
  mutate(max_covariance = ceiling((item_sd_1 * item_sd_2)*10)/10)

rs_by_max_cov <- by_max_cov %>% 
  group_by(max_covariance) %>% 
  filter(n() > 3) %>% 
  summarise(broom::tidy(cor.test(synthetic_r, empirical_r)), sd_emp_r = sd(empirical_r), n = n()) %>% 
  select(max_covariance, r = estimate, conf.low, conf.high, n, sd_emp_r) %>% 
  arrange(max_covariance)

rs_by_max_cov%>% 
  kable()
max_covariance r conf.low conf.high n sd_emp_r
0.5 0.8845860 0.6925673 0.9595392 16 0.0841287
0.6 0.5394056 0.4566420 0.6129128 319 0.1712504
0.7 0.5773367 0.5485306 0.6047739 2162 0.1591129
0.8 0.6331256 0.6192313 0.6466235 7355 0.1524384
0.9 0.6439129 0.6346448 0.6529958 15639 0.1496275
1.0 0.6333785 0.6255374 0.6410916 22779 0.1536605
1.1 0.6791602 0.6718518 0.6863364 21261 0.1657358
1.2 0.7290679 0.7206660 0.7372557 12257 0.1799820
1.3 0.7435976 0.7298785 0.7567179 4268 0.1774902
1.4 0.7194644 0.6869514 0.7491027 930 0.1587180
1.5 0.7077049 0.6188080 0.7787043 154 0.1447413
1.6 0.8896940 0.6452603 0.9688857 12 0.1835193
rs_by_max_cov %>% ggplot(aes(max_covariance, r, ymin = conf.low, ymax = conf.high)) +
  geom_pointrange()

by_max_cov%>% 
  filter(max_covariance > .7) %>% 
  summarise(broom::tidy(cor.test(synthetic_r, empirical_r)), sd_emp_r = sd(empirical_r), n = n()) %>% 
  kable()
estimate statistic p.value parameter conf.low conf.high method alternative sd_emp_r n
0.6758603 266.806 0 84654 0.6721843 0.6795029 Pearson’s product-moment correlation two.sided 0.1624048 84656
holdout_llm %>% 
  left_join(item_variances, by = c("variable_1" = "variable")) %>% 
  left_join(item_variances, by = c("variable_2" = "variable"), suffix = c("_1", "_2")) %>% 
  mutate(max_covariance = ceiling((item_sd_1 * item_sd_2)*10)/10) %>% 
  filter(max_covariance > 1) %>% 
  summarise(broom::tidy(cor.test(synthetic_r, empirical_r)), sd_emp_r = sd(empirical_r), n = n()) %>% 
  select(r = estimate, conf.low, conf.high, n, sd_emp_r) %>% 
  knitr::kable()
r conf.low conf.high n sd_emp_r
0.7085118 0.7035267 0.7134273 38883 0.1726419

Averages

holdout_llm %>% summarise(
  mean(synthetic_r),
  mean(empirical_r),
  mean(abs(synthetic_r)),
  mean(abs(empirical_r)),
  prop_negative = sum(empirical_r < 0)/n(),
  prop_pos = sum(empirical_r > 0)/n(),
  `prop_below_-.10` = sum(empirical_r < -0.1)/n(),
  `prop_above_.10` = sum(empirical_r > 0.1)/n(),
) %>% kable(digits = 2, caption = "Average correlations")
Average correlations
mean(synthetic_r) mean(empirical_r) mean(abs(synthetic_r)) mean(abs(empirical_r)) prop_negative prop_pos prop_below_-.10 prop_above_.10
0.05 0.03 0.11 0.13 0.44 0.56 0.21 0.33

Is the accuracy lower for the pre-trained model?

ggplot(pt_holdout_llm, aes(synthetic_r, empirical_r, 
              ymin = empirical_r - empirical_r_se,
              ymax = empirical_r + empirical_r_se)) + 
  geom_abline(linetype = "dashed") +
  geom_point(color = "#00A0B0", alpha = 0.1, size = 1) +
  xlab("Synthetic inter-item correlation") + 
  ylab("Empirical inter-item correlation") +
  theme_bw() +
  coord_fixed(xlim = c(-1,1), ylim = c(-1,1)) -> pt_plot_items
pt_plot_items

Full table of synthetic and empirical item pair correlations

Synthetic Reliabilities

scales <- read_rds(file = file.path(data_path, glue("data/intermediate/scales_with_alpha_se.rds")))


source("global_functions.R")

scales <- scales %>% filter(number_of_items >= 3)

Accuracy

se2 <- mean(scales$empirical_alpha_se^2)
r <- broom::tidy(cor.test(scales$empirical_alpha, scales$synthetic_alpha))
pt_r <- broom::tidy(cor.test(scales$empirical_alpha, scales$pt_synthetic_alpha))

model <- paste0('
  # Latent variables
  latent_real_rel =~ 1*empirical_alpha

  # Fixing error variances based on known standard errors
  empirical_alpha ~~ ',se2,'*empirical_alpha

  # Relationship between latent variables
  latent_real_rel ~~ synthetic_alpha
')

fit <- sem(model, data = scales)
pt_fit <- sem(model, data = scales %>% 
                select(empirical_alpha, synthetic_alpha = pt_synthetic_alpha))

m_lmsynth_rel_scales <- brm(
  bf(empirical_alpha | mi(empirical_alpha_se) ~ synthetic_alpha,
     sigma ~ s(synthetic_alpha)), data = scales, 
  file = "ignore/m_synth_rel_lm")

newdata <- m_lmsynth_rel_scales$data %>% select(empirical_alpha, synthetic_alpha, empirical_alpha_se)
epreds <- epred_draws(newdata = newdata, obj = m_lmsynth_rel_scales, re_formula = NA)
preds <- predicted_draws(newdata = newdata, obj = m_lmsynth_rel_scales, re_formula = NA)
epred_preds <- epreds %>% left_join(preds)
by_draw <- epred_preds %>% group_by(.draw) %>% 
  summarise(.epred = var(.epred),
            .prediction = var(.prediction),
            sigma = sqrt(.prediction - .epred),
            latent_r = sqrt(.epred/.prediction))

accuracy_bayes_rels <- by_draw %>% mean_hdci(latent_r)

bind_rows(
  pt_r %>% 
    mutate(model = "pre-trained", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  standardizedsolution(pt_fit) %>% 
    filter(lhs == "latent_real_rel", rhs ==  "synthetic_alpha") %>% 
    mutate(model = "pre-trained", kind = "latent outcome (SEM)") %>% 
    select(model, kind, accuracy = est.std, 
           conf.low = ci.lower, conf.high = ci.upper),
  r %>% 
    mutate(model = "fine-tuned", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  standardizedsolution(fit) %>% 
    filter(lhs == "latent_real_rel", rhs ==  "synthetic_alpha") %>% 
    mutate(model = "fine-tuned", kind = "latent outcome (SEM)") %>% 
    select(model, kind, accuracy = est.std, 
           conf.low = ci.lower, conf.high = ci.upper),
  accuracy_bayes_rels %>% 
    mutate(model = "fine-tuned", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper)
  ) %>% 
  knitr::kable(digits = 2)
model kind accuracy conf.low conf.high
pre-trained manifest 0.38 0.28 0.48
pre-trained latent outcome (SEM) 0.39 0.29 0.49
fine-tuned manifest 0.89 0.86 0.91
fine-tuned latent outcome (SEM) 0.90 0.87 0.92
fine-tuned latent outcome (Bayesian EIV) 0.89 0.84 0.94

Prediction error plot according to synthetic estimate

m_lmsynth_rel_scales
##  Family: gaussian 
##   Links: mu = identity; sigma = log 
## Formula: empirical_alpha | mi(empirical_alpha_se) ~ synthetic_alpha 
##          sigma ~ s(synthetic_alpha)
##    Data: scales (Number of observations: 307) 
##   Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
##          total post-warmup draws = 4000
## 
## Smoothing Spline Hyperparameters:
##                               Estimate Est.Error l-95% CI u-95% CI Rhat
## sds(sigma_ssynthetic_alpha_1)     3.35      1.43     1.32     6.94 1.00
##                               Bulk_ESS Tail_ESS
## sds(sigma_ssynthetic_alpha_1)      951      823
## 
## Regression Coefficients:
##                          Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
## Intercept                    0.05      0.01     0.02     0.08 1.00     3077
## sigma_Intercept             -1.83      0.05    -1.92    -1.74 1.00     3731
## synthetic_alpha              0.96      0.02     0.92     1.00 1.00     3159
## sigma_ssynthetic_alpha_1    -1.56      5.57   -10.99    11.23 1.00     1181
##                          Tail_ESS
## Intercept                    3048
## sigma_Intercept              2992
## synthetic_alpha              3150
## sigma_ssynthetic_alpha_1      934
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).
pred <- conditional_effects(m_lmsynth_rel_scales, method = "predict")
kable(rmse_alpha <- by_draw %>% mean_hdci(sigma), caption = "Average prediction error (RMSE)", digits = 2)
Average prediction error (RMSE)
sigma .lower .upper .width .point .interval
0.2 0.13 0.25 0.95 mean hdci
plot_prediction_error_alpha <- plot(conditional_effects(m_lmsynth_rel_scales, dpar = "sigma"), plot = F)[[1]] + 
  theme_bw() + 
  geom_smooth(stat = "identity", color = "#a48500", fill = "#EDC951") + 
  xlab("Synthetic Cronbach's alpha") + 
  ylab("Prediction error (sigma)") +
  coord_cartesian(xlim = c(-1, 1), ylim = c(0, 0.35))
plot_prediction_error_alpha

Scatter plot

ggplot(scales, aes(synthetic_alpha, empirical_alpha, 
                   color = str_detect(scale, "^random"), 
              ymin = empirical_alpha - empirical_alpha_se,
              ymax = empirical_alpha + empirical_alpha_se)) + 
  geom_abline(linetype = "dashed") +
  geom_point(alpha = 0.6, size = 1) +
  geom_smooth(aes(
    x = synthetic_alpha,
    y = estimate__,
    ymin = lower__,
    ymax = upper__,
  ), stat = "identity", 
  color = "#a48500",
  fill = "#EDC951",
  data = as.data.frame(pred$synthetic_alpha)) +
  scale_color_manual(values = c("#00A0B0", "#6A4A3C"),
                     guide = "none") +
  xlab("Synthetic Cronbach's alpha") + 
  ylab("Empirical Cronbach's alpha") +
  theme_bw() +
  coord_fixed(xlim = c(-1,1), ylim = c(-1,1)) -> plot_rels
plot_rels

Interactive plot

(scales %>% 
  filter(type == "real") %>% 
  mutate(synthetic_alpha = round(synthetic_alpha, 2),
         empirical_alpha = round(empirical_alpha, 2),
         scale = str_replace_all(scale, "_+", " ")) %>% 
ggplot(., aes(synthetic_alpha, empirical_alpha, 
              # ymin = empirical_r - empirical_r_se, 
              # ymax = empirical_r + empirical_r_se, 
              label = scale)) + 
  geom_abline(linetype = "dashed") +
  geom_point(alpha = 0.3, size = 1, color = "#00A0B0") +
  xlab("Synthetic Cronbach's alpha") + 
  ylab("Empirical Cronbach's alpha") +
  theme_bw() +
  theme(legend.position='none') + 
  coord_fixed(xlim = c(NA,1), ylim = c(NA,1))) %>% 
  ggplotly()

Table

scales %>% 
  filter(type != "random") %>% 
  mutate(empirical_alpha = sprintf("%.2f±%.3f", empirical_alpha,
                               empirical_alpha_se),
         synthetic_alpha = sprintf("%.2f", synthetic_alpha),
         scale = str_replace_all(scale, "_+", " ")
         ) %>% 
  select(scale, empirical_alpha, synthetic_alpha, number_of_items) %>% 
  DT::datatable(rownames = FALSE,
                filter = "top")
scales %>% ungroup() %>% 
  summarise(mean(empirical_alpha), sd(empirical_alpha))
## # A tibble: 1 × 2
##   `mean(empirical_alpha)` `sd(empirical_alpha)`
##                     <dbl>                 <dbl>
## 1                   0.206                 0.449
scales %>% group_by(type) %>% 
  summarise(mean(empirical_alpha), sd(empirical_alpha),
            cor = broom::tidy(cor.test(synthetic_alpha, empirical_alpha)), n())
## # A tibble: 2 × 5
##   type   `mean(empirical_alpha)` `sd(empirical_alpha)` cor$estimate `n()`
##   <chr>                    <dbl>                 <dbl>        <dbl> <int>
## 1 random                 -0.0851                 0.246        0.587   200
## 2 real                    0.750                  0.101        0.628   107
## # ℹ 7 more variables: cor$statistic <dbl>, $p.value <dbl>, $parameter <int>,
## #   $conf.low <dbl>, $conf.high <dbl>, $method <chr>, $alternative <chr>
psychometric::cRRr(0.632, 0.0992, 0.235)
## # A tibble: 1 × 1
##   unrestricted
##          <dbl>
## 1        0.888

Robustness checks

Accuracy by whether scales were real or random

The SurveyBot3000 does not “know” whether scales were published in the literature or formed at random. Knowing what we do about the research literature in psychology, we can infer that published scales will usually exceed the Nunnally threshold of .70. Hence, we know that the synthetic alphas for published scales should rarely be below .70. If we regress synthetic alphas on empirical alphas separately for the scales taken from the literature, we see this as a bias (a positive regression intercept of .68 and a slope ≠ 1, .26). Still, the synthetic alpha estimates are predictive of empirical alphas with an accuracy of .65.

There is no clear bias for the random scales. When both are analyzed jointly, the clear selection bias for the published scales is mostly averaged out but is reflected in the slope exceeding 1.

scales %>% 
  group_by(type) %>% 
  summarise(broom::tidy(cor.test(synthetic_alpha, empirical_alpha)), sd_alpha = sd(empirical_alpha), n = n()) %>% 
  knitr::kable(digits = 2, caption = "Accuracy shown separately for randomly formed and real scales")
Accuracy shown separately for randomly formed and real scales
type estimate statistic p.value parameter conf.low conf.high method alternative sd_alpha n
random 0.59 10.20 0 198 0.49 0.67 Pearson’s product-moment correlation two.sided 0.25 200
real 0.63 8.27 0 105 0.50 0.73 Pearson’s product-moment correlation two.sided 0.10 107
scales %>% 
  group_by(type) %>% 
  summarise(broom::tidy(lm(empirical_alpha ~ synthetic_alpha)), n = n()) %>% 
  knitr::kable(digits = 2, caption = "Regression intercepts and slopes for randomly formed and real scales")
Regression intercepts and slopes for randomly formed and real scales
type term estimate std.error statistic p.value n
random (Intercept) -0.02 0.02 -1.33 0.19 200
random synthetic_alpha 0.71 0.07 10.20 0.00 200
real (Intercept) 0.58 0.02 26.09 0.00 107
real synthetic_alpha 0.27 0.03 8.27 0.00 107

As in our Stage 1 submission

Here are the results if we calculate the accuracy and prediction error as in the Stage 1 submission. We now think this approach, by conditioning on random variation in the empirical correlations, gave a misleading picture of the accuracy and bias of the synthetic Cronbach’s alphas. Here we report the results if we conduct the analysis as in Stage 1 (but with the corrected SE of empirical alphas).

s1_scales <- scales %>%
  filter(number_of_items > 2) %>% 
  rowwise() %>%
  mutate(reverse_items = if_else(type == "random", list(reverse_items_by_1st), list(reverse_items)),
         r_real_rev = list(reverse_items(r_real, reverse_items)),
         pt_r_llm_rev = list(reverse_items(pt_r_llm, reverse_items)),
         r_llm_rev = list(reverse_items(r_llm, reverse_items))) %>%
  mutate(
    rel_real = list(psych::alpha(r_real_rev, keys = F, n.obs = N)$feldt),
    rel_llm = list(psych::alpha(r_llm_rev, keys = F, n.obs = N)$feldt),
    rel_pt_llm = list(psych::alpha(pt_r_llm_rev, keys = F, n.obs = N)$feldt)) %>%
  mutate(empirical_alpha = rel_real$alpha$raw_alpha,
         synthetic_alpha = rel_llm$alpha$raw_alpha,
         pt_synthetic_alpha = rel_pt_llm$alpha$raw_alpha) %>%
  mutate(
    empirical_alpha_se = mean(diff(unlist(psychometric::alpha.CI(empirical_alpha, k = number_of_items, N = N, level = 0.95))))/1.96) %>% 
      filter(empirical_alpha > 0)

s1_r <- broom::tidy(cor.test(s1_scales$empirical_alpha, s1_scales$synthetic_alpha))
s1_pt_r <- broom::tidy(cor.test(s1_scales$empirical_alpha, s1_scales$pt_synthetic_alpha))
                             
bind_rows(
  s1_pt_r %>% 
    mutate(model = "pre-trained", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  s1_r %>% 
    mutate(model = "fine-tuned", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  ) %>% 
  knitr::kable(digits = 2, caption = "Accuracy of synthetic alphas when empirical alphas are biased upward through adaptive item reversion and selection on positive alphas")
Accuracy of synthetic alphas when empirical alphas are biased upward through adaptive item reversion and selection on positive alphas
model kind accuracy conf.low conf.high
pre-trained manifest 0.08 -0.03 0.19
fine-tuned manifest 0.82 0.77 0.85
m_lmsynth_rel_scales_s1 <- brm(
  bf(empirical_alpha | mi(empirical_alpha_se) ~ synthetic_alpha,
     sigma ~ poly(synthetic_alpha, degree = 3)), data = s1_scales, 
  iter = 6000, control = list(adapt_delta = 0.9),
  file = "ignore/m_synth_rel_lm_as_stage_1")

pred <- conditional_effects(m_lmsynth_rel_scales_s1, method = "predict")
ggplot(s1_scales, aes(synthetic_alpha, empirical_alpha, 
                   color = str_detect(scale, "^random"), 
              ymin = empirical_alpha - empirical_alpha_se,
              ymax = empirical_alpha + empirical_alpha_se)) + 
  geom_abline(linetype = "dashed") +
  geom_point(alpha = 0.6, size = 1) +
  geom_smooth(aes(
    x = synthetic_alpha,
    y = estimate__,
    ymin = lower__,
    ymax = upper__,
  ), stat = "identity", 
  color = "#a48500",
  fill = "#EDC951",
  data = as.data.frame(pred$synthetic_alpha)) +  scale_color_manual(values = c("#00A0B0", "#6A4A3C"),
                     guide = "none") +
  xlab("Synthetic Cronbach's alpha") + 
  ylab("Empirical Cronbach's alpha") +
  theme_bw() +
  coord_fixed(xlim = c(-1, 1), ylim = c(-1,1))  -> s1_plot_rels
s1_plot_rels

newdata <- m_lmsynth_rel_scales_s1$data %>% select(empirical_alpha, synthetic_alpha, empirical_alpha_se)
epreds <- epred_draws(newdata = newdata, obj = m_lmsynth_rel_scales_s1, re_formula = NA)
preds <- predicted_draws(newdata = newdata, obj = m_lmsynth_rel_scales_s1, re_formula = NA)
epred_preds <- epreds %>% left_join(preds)
by_draw <- epred_preds %>% group_by(.draw) %>% 
  summarise(
             mae = mean(abs(.epred - .prediction)),
            .epred = var(.epred),
            .prediction = var(.prediction),
            sigma = sqrt(.prediction - .epred),
            latent_r = sqrt(.epred/.prediction))

accuracy_bayes_rels_poly <- by_draw %>% mean_hdci(latent_r)

s1_plot_rels

bind_rows(
  r %>% 
    mutate(model = "fine-tuned", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  s1_r %>% 
    mutate(model = "fine-tuned, conditioned on empirical r", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  accuracy_bayes_rels %>% 
    mutate(model = "fine-tuned", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper),
  accuracy_bayes_rels_poly %>% 
    mutate(model = "fine-tuned, conditioned on empirical r", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper),
  ) %>% 
  knitr::kable(digits = 2, caption = "Accuracy of synthetic alphas when empirical alphas are biased upward through adaptive item reversion and selection on positive alphas")
Accuracy of synthetic alphas when empirical alphas are biased upward through adaptive item reversion and selection on positive alphas
model kind accuracy conf.low conf.high
fine-tuned manifest 0.89 0.86 0.91
fine-tuned, conditioned on empirical r manifest 0.82 0.77 0.85
fine-tuned latent outcome (Bayesian EIV) 0.89 0.84 0.94
fine-tuned, conditioned on empirical r latent outcome (Bayesian EIV) 0.85 0.77 0.92

Number of items as a trivial predictor

Although the number of items alone can of course predict Cronbach’s alpha, the synthetic alphas explain much more variance in empirical alphas.

scales %>% 
  ungroup() %>% 
  summarise(broom::tidy(cor.test(number_of_items, empirical_alpha)), sd_alpha = sd(empirical_alpha), n = n()) %>% 
  knitr::kable()
estimate statistic p.value parameter conf.low conf.high method alternative sd_alpha n
0.136016 2.397701 0.0170994 305 0.0244477 0.2442379 Pearson’s product-moment correlation two.sided 0.4490774 307
summary(lm(empirical_alpha ~ number_of_items, scales))
## 
## Call:
## lm(formula = empirical_alpha ~ number_of_items, data = scales)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.0458 -0.3356 -0.1278  0.4982  0.7622 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)  
## (Intercept)     0.098934   0.051336   1.927   0.0549 .
## number_of_items 0.016603   0.006925   2.398   0.0171 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4456 on 305 degrees of freedom
## Multiple R-squared:  0.0185, Adjusted R-squared:  0.01528 
## F-statistic: 5.749 on 1 and 305 DF,  p-value: 0.0171
summary(lm(empirical_alpha ~ number_of_items + synthetic_alpha, scales))
## 
## Call:
## lm(formula = empirical_alpha ~ number_of_items + synthetic_alpha, 
##     data = scales)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.58321 -0.09758  0.00643  0.10477  1.09299 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      0.051451   0.024017   2.142    0.033 *  
## number_of_items -0.000593   0.003275  -0.181    0.856    
## synthetic_alpha  0.984484   0.029758  33.083   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2081 on 304 degrees of freedom
## Multiple R-squared:  0.7866, Adjusted R-squared:  0.7852 
## F-statistic: 560.4 on 2 and 304 DF,  p-value: < 2.2e-16

Is the accuracy lower for the pre-trained model?

ggplot(scales, aes(pt_synthetic_alpha, empirical_alpha, 
                   color = str_detect(scale, "^random"), 
              ymin = empirical_alpha - empirical_alpha_se,
              ymax = empirical_alpha + empirical_alpha_se)) + 
  geom_abline(linetype = "dashed") +
  geom_point(alpha = 0.6, size = 1) +
  scale_color_manual(values = c("#00A0B0", "#6A4A3C"),
                     guide = "none") +
  xlab("Synthetic Cronbach's alpha") + 
  ylab("Empirical Cronbach's alpha") +
  theme_bw() +
  coord_fixed(xlim = c(-1,1), ylim = c(-1,1)) -> pt_plot_rels
pt_plot_rels

Synthetic Scale Correlations

manifest_scores = arrow::read_feather(file = file.path(data_path, glue("data/intermediate/{model_name}.raw.osf-bainbridge-2021-s2-0.scale_correlations.feather")))
pt_manifest_scores = arrow::read_feather(file = file.path(data_path, glue("data/intermediate/{pretrained_model_name}.raw.osf-bainbridge-2021-s2-0.scale_correlations.feather")))

n_distinct(manifest_scores$scale_a)
## [1] 112
manifest_scores <- manifest_scores %>%
 left_join(scales, by = c("scale_a" = "scale")) %>%
 left_join(scales, by = c("scale_b" = "scale"))

Accuracy

r <- broom::tidy(cor.test(manifest_scores$empirical_r, manifest_scores$synthetic_r))
pt_r <- broom::tidy(cor.test(pt_manifest_scores$empirical_r, pt_manifest_scores$synthetic_r))

se2 <- mean(manifest_scores$empirical_r_se^2)
model <- paste0('
    # Latent variables
    PearsonLatent =~ 1*empirical_r

    # Fixing error variances based on known standard errors
    empirical_r ~~ ',se2,'*empirical_r

    # Relationship between latent variables
    PearsonLatent ~~ synthetic_r
  ')

fit <- sem(model, data = manifest_scores)
pt_fit <- sem(model, data = pt_manifest_scores)


m_lmsynth_r_scales <- brm(
  bf(empirical_r | mi(empirical_r_se) ~ synthetic_r + (1|mm(scale_a, scale_b)),
     sigma ~ s(synthetic_r)), data = manifest_scores, 
  file = "ignore/m_synth_r_scales_lm8")

sd_synth <- sd(m_lmsynth_r_scales$data$synthetic_r)


newdata <- m_lmsynth_r_scales$data %>% select(empirical_r, synthetic_r, empirical_r_se)
epreds <- epred_draws(newdata = newdata, obj = m_lmsynth_r_scales, re_formula = NA)
preds <- predicted_draws(newdata = newdata, obj = m_lmsynth_r_scales, re_formula = NA)
epred_preds <- epreds %>% left_join(preds)
by_draw <- epred_preds %>% group_by(.draw) %>% 
  summarise(.epred = var(.epred),
            .prediction = var(.prediction),
            sigma = sqrt(.prediction - .epred),
            latent_r = sqrt(.epred/.prediction))

accuracy_bayes_scales <- by_draw %>% mean_hdci(latent_r)

bind_rows(
  pt_r %>% 
    mutate(model = "pre-trained", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  standardizedsolution(pt_fit) %>% 
    filter(lhs == "PearsonLatent", rhs ==  "synthetic_r") %>% 
    mutate(model = "pre-trained", kind = "latent outcome (SEM)") %>% 
    select(model, kind, accuracy = est.std, 
           conf.low = ci.lower, conf.high = ci.upper),
  r %>% 
    mutate(model = "fine-tuned", kind = "manifest") %>% 
    select(model, kind, accuracy = estimate, conf.low, conf.high),
  standardizedsolution(fit) %>% 
    filter(lhs == "PearsonLatent", rhs ==  "synthetic_r") %>% 
    mutate(model = "fine-tuned", kind = "latent outcome (SEM)") %>% 
    select(model, kind, accuracy = est.std, 
           conf.low = ci.lower, conf.high = ci.upper),
  accuracy_bayes_scales %>% 
    mutate(model = "fine-tuned", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper)
  ) %>% 
  knitr::kable(digits = 2)
model kind accuracy conf.low conf.high
pre-trained manifest 0.33 0.30 0.35
pre-trained latent outcome (SEM) 0.33 0.31 0.35
fine-tuned manifest 0.87 0.86 0.87
fine-tuned latent outcome (SEM) 0.88 0.87 0.89
fine-tuned latent outcome (Bayesian EIV) 0.89 0.88 0.90

Prediction error plot according to synthetic estimate

m_lmsynth_r_scales
##  Family: gaussian 
##   Links: mu = identity; sigma = log 
## Formula: empirical_r | mi(empirical_r_se) ~ synthetic_r + (1 | mm(scale_a, scale_b)) 
##          sigma ~ s(synthetic_r)
##    Data: manifest_scores (Number of observations: 6245) 
##   Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
##          total post-warmup draws = 4000
## 
## Smoothing Spline Hyperparameters:
##                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
## sds(sigma_ssynthetic_r_1)     0.81      0.41     0.28     1.90 1.00     1061
##                           Tail_ESS
## sds(sigma_ssynthetic_r_1)     1472
## 
## Multilevel Hyperparameters:
## ~mmscale_ascale_b (Number of levels: 113) 
##               Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## sd(Intercept)     0.07      0.01     0.06     0.08 1.01      743     1279
## 
## Regression Coefficients:
##                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
## Intercept                0.01      0.01    -0.00     0.03 1.01      398
## sigma_Intercept         -2.15      0.01    -2.17    -2.13 1.00     3867
## synthetic_r              0.95      0.01     0.94     0.97 1.00     3232
## sigma_ssynthetic_r_1     0.02      1.20    -2.29     2.57 1.00     1202
##                      Tail_ESS
## Intercept                 770
## sigma_Intercept          2975
## synthetic_r              2772
## sigma_ssynthetic_r_1     1000
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).
kable(rmse_scales <- by_draw %>% mean_hdci(sigma), caption = "Average prediction error (RMSE)", digits = 2)
Average prediction error (RMSE)
sigma .lower .upper .width .point .interval
0.12 0.11 0.12 0.95 mean hdci
pred <- conditional_effects(m_lmsynth_r_scales, method = "predict")
plot_prediction_error_scales <- plot(conditional_effects(m_lmsynth_r_scales, dpar = "sigma"), plot = F)[[1]] + 
  theme_bw() + 
  geom_smooth(stat = "identity", color = "#a48500", fill = "#EDC951") + 
  xlab("Synthetic inter-scale correlation") + 
  ylab("Prediction error (sigma)")
plot_prediction_error_scales

Scatter plot

ggplot(manifest_scores, aes(synthetic_r, empirical_r, 
              ymin = empirical_r - empirical_r_se,
              ymax = empirical_r + empirical_r_se)) + 
  geom_abline(linetype = "dashed") +
  geom_point(color = "#00A0B0", alpha = 0.3, size = 1) +
  geom_smooth(aes(
    x = synthetic_r,
    y = estimate__,
    ymin = lower__,
    ymax = upper__,
  ), stat = "identity", 
  color = "#a48500",
  fill = "#EDC951",
  data = as.data.frame(pred$synthetic_r)) +
  xlab("Synthetic inter-scale correlation") + 
  ylab("Empirical inter-scale correlation") +
  theme_bw() +
  coord_fixed(xlim = c(-1,1), ylim = c(-1,1)) -> plot_scales
plot_scales

Interactive plot

(manifest_scores %>% 
  mutate(synthetic_r = round(synthetic_r, 2),
         empirical_r = round(empirical_r, 2),
         scales = str_replace_all(str_c(scale_a, "\n", scale_b),
                                  "_+", " ")) %>% 
ggplot(., aes(synthetic_r, empirical_r, 
              # ymin = empirical_r - empirical_r_se, 
              # ymax = empirical_r + empirical_r_se, 
              label = scales)) + 
  geom_abline(linetype = "dashed") +
  geom_point(color = "#00A0B0", alpha = 0.3, size = 1) +
  xlab("Synthetic inter-scale correlation") + 
  ylab("Empirical inter-scale correlation") +
  theme_bw() +
  coord_fixed(xlim = c(-1,1), ylim = c(-1,1))) %>% 
  ggplotly()
Table
manifest_scores %>% 
                mutate(empirical_r = sprintf("%.2f±%.3f", empirical_r,
                                             empirical_r_se),
                       synthetic_r = sprintf("%.2f", synthetic_r),
                       scale_a = str_replace_all(scale_a, "_+", " "),
                       scale_b = str_replace_all(scale_b, "_+", " ")
                       ) %>% 
                select(scale_a, scale_b, empirical_r, synthetic_r) %>% 
  DT::datatable(rownames = FALSE,
                filter = "top")

Robustness checks

Comparing spline and polynomial models for heteroscedasticity

m_lmsynth_r_scales_poly <- brm(
  bf(empirical_r | mi(empirical_r_se) ~ synthetic_r + (1|mm(scale_a, scale_b)),
     sigma ~ poly(synthetic_r, degree = 3)), data = manifest_scores, 
  file = "ignore/m_synth_r_scales_lm_poly")

newdata <- m_lmsynth_r_scales_poly$data %>% select(empirical_r, synthetic_r, empirical_r_se)
epreds <- epred_draws(newdata = newdata, obj = m_lmsynth_r_scales_poly, re_formula = NA)
preds <- predicted_draws(newdata = newdata, obj = m_lmsynth_r_scales_poly, re_formula = NA)
epred_preds <- epreds %>% left_join(preds)
by_draw <- epred_preds %>% group_by(.draw) %>% 
  summarise(.epred = var(.epred),
            .prediction = var(.prediction),
            sigma = sqrt(.prediction - .epred),
            latent_r = sqrt(.epred/.prediction))

accuracy_bayes_scales_poly <- by_draw %>% mean_hdci(latent_r)

bind_rows(
  accuracy_bayes_scales %>% 
    mutate(model = "spline", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper),
  accuracy_bayes_scales_poly %>% 
    mutate(model = "polynomial", kind = "latent outcome (Bayesian EIV)") %>% 
    select(model, kind, accuracy = latent_r, conf.low = .lower, conf.high = .upper)
) %>% 
  knitr::kable(digits = 2, caption = "Comparing spline and polynomial models for scale correlations")
Comparing spline and polynomial models for scale correlations
model kind accuracy conf.low conf.high
spline latent outcome (Bayesian EIV) 0.89 0.88 0.9
polynomial latent outcome (Bayesian EIV) 0.89 0.88 0.9
plot_prediction_error_scales_poly <- plot(conditional_effects(m_lmsynth_r_scales_poly, dpar = "sigma"), plot = F)[[1]] + 
  theme_bw() + 
  geom_smooth(stat = "identity", color = "#a48500", fill = "#EDC951") + 
  xlab("Synthetic inter-scale correlation") + 
  ylab("Prediction error (sigma)")
plot_prediction_error_scales_poly

How does number of items across the two scales relate to accuracy?

by_item_number <- manifest_scores %>%
  mutate(items = number_of_items.x + number_of_items.y) %>%
  group_by(items) %>%
  summarise(broom::tidy(cor.test(empirical_r, synthetic_r)), pairwise_n = n()) 

by_item_number %>% 
  ggplot(aes(items, estimate, ymin = conf.low, ymax = conf.high)) + 
  geom_pointrange() +
  scale_y_continuous("Manifest accuracy (with 95% confidence interval)") +
  xlab("Number of items summed across scales")

lm(estimate ~ items, by_item_number, weights = 1/(by_item_number$conf.high-by_item_number$conf.low))
## 
## Call:
## lm(formula = estimate ~ items, data = by_item_number, weights = 1/(by_item_number$conf.high - 
##     by_item_number$conf.low))
## 
## Coefficients:
## (Intercept)        items  
##    0.845728     0.002668
manifest_scores %>%
  filter(number_of_items.x >= 10, number_of_items.y >= 10) %>%
  summarise(cor = cor(empirical_r, synthetic_r), n())
## # A tibble: 1 × 2
##     cor `n()`
##   <dbl> <int>
## 1 0.926   300

Averages

scales %>% filter(type == "real") %>% ungroup() %>% 
  filter(number_of_items >= 3) %>% 
  summarise(median(number_of_items),
            mean(number_of_items))
## # A tibble: 1 × 2
##   `median(number_of_items)` `mean(number_of_items)`
##                       <int>                   <dbl>
## 1                         4                    6.79

Is the accuracy lower for the pre-trained model?

ggplot(pt_manifest_scores, aes(synthetic_r, empirical_r, 
              ymin = empirical_r - empirical_r_se,
              ymax = empirical_r + empirical_r_se)) + 
  geom_abline(linetype = "dashed") +
  geom_point(color = "#00A0B0", alpha = 0.3, size = 1) +
  xlab("Synthetic inter-scale correlation") + 
  ylab("Empirical inter-scale correlation") +
  theme_bw() +
  coord_fixed(xlim = c(-1,1), ylim = c(-1,1)) -> pt_plot_scales
pt_plot_scales

Combined plot

get_scale_point <- function(data, synthetic_approx, empirical_approx) {
  data %>%
    ungroup() %>% 
    # Find closest point to target coordinates
    mutate(dist = sqrt((synthetic_alpha - synthetic_approx)^2 + 
                      (empirical_alpha - empirical_approx)^2)) %>%
    arrange(dist) %>%
    slice(1) %>%
    select(synthetic_alpha, empirical_alpha)
}
get_scale_point_by_name <- function(data, name) {
  data %>%
    filter(scale == name) %>%
    slice(1) %>%
    select(synthetic_alpha, empirical_alpha, pt_synthetic_alpha)
}
ipip_e <- get_scale_point_by_name(scales, "International_Personality_Item_Pool_120_extraversion")
lot_o <- get_scale_point_by_name(scales, "Life_Orientation_Test_Optimism")
# get_scale_point_by_name <- function(data, var_1, var_2) {
#   data %>%
#     filter(variable_1 == scale_name) %>% 
#     select(synthetic_r, empirical_r)
# }

library(patchwork)
pt_plot_items2 <- pt_plot_items +
    annotate("text", size = 2.5, x = 0.5, y = -0.8, vjust = 1, hjust = 1, label = "r(I fear for the worst,\nI never worry about anything)", color = "#00A0B0") +
  annotate("segment", x = 0, y = -0.78, xend = 0.2761906, yend = -0.3459711, color = "#00A0B0", alpha = 0.7) +
  annotate("text", size = 2.5, x = -.1, y = 0.5, hjust = 1, label = "r(I get angry easily,\nI lose my temper)", color = "#00A0B0") + 
  annotate("segment", x = -.1, y = 0.5, xend = 0.6935711, yend = 0.7140546, color = "#00A0B0", alpha = 0.7)
  

plot_items2 <- plot_items +
  annotate("text", size = 3, x = -1, y = 0.98, vjust = 0, hjust = 0, label = with(accuracy_bayes_items, { sprintf("accuracy = %.2f [%.2f;%.2f]", latent_r, .lower, .upper) })) +
  annotate("text", size = 2.5, x = 0.5, y = -0.8, vjust = 1, hjust = 1, label = "r(I fear for the worst,\nI never worry about anything)", color = "#00A0B0") +
  annotate("segment", x = 0, y = -0.78, xend = -0.1104686, yend = -0.3459711, color = "#00A0B0", alpha = 0.7) +
  annotate("text", size = 2.5, x = -.1, y = 0.5, hjust = 1, label = "r(I get angry easily,\nI lose my temper)", color = "#00A0B0") + 
  annotate("segment", x = -.1, y = 0.5, xend = 0.8031979, yend = 0.7140546, color = "#00A0B0", alpha = 0.7)
  # annotate("text", size = 2.5, x = -0.5, y = -0.8, hjust = 0, label = "r(I love life,\nI avoid crowds)", color = "#00A0B0") +
  # annotate("segment", x = -.3, y = -0.7, xend = -0.23, yend = -0.28, color = "#00A0B0", alpha = 0.7) +
  # annotate("text", size = 2.5, x = -.3, y = 0.5, hjust = 1, label = "r(I work hard,\nI am diligent)", color = "#00A0B0") +
  # annotate("segment", x = -.3, y = .5, xend = .52, yend = .58, color = "#00A0B0", alpha = 0.7)


pt_plot_rels2 <- pt_plot_rels + 
  annotate("text", size = 2.5, x = 0.21, y = -0.2, hjust = 0, label = "IPIP Extraversion", color = "#00A0B0") +
  annotate("segment", x = 0.75, y = -0.2, xend = 0.87, yend = 0.90, color = "#00A0B0", alpha = 0.7) +
  annotate("text", size = 2.5, x = -0.02, y = 0.95, hjust = 0, label = "LOT Optimism", color = "#00A0B0") +
  annotate("segment", x = 0.11, y = 0.93, xend = -1.477226, yend = 0.71, color = "#00A0B0", alpha = 0.7)

p1 <- get_scale_point(scales, 0.30, 0.22)
p2 <- get_scale_point(scales, 0.12, -0.36) 
p3 <- get_scale_point(scales, -0.4, -0.90)


plot_rels2 <- plot_rels + 
  annotate("text", size = 3, x = -1, y = 0.98, vjust = 0, hjust = 0, 
           label = with(accuracy_bayes_rels, { sprintf("accuracy = %.2f [%.2f;%.2f]", latent_r, .lower, .upper) })) +
  annotate("text", size = 2.5, x = -0.2, y = -0.9, hjust = 0, label = "randomly formed scales", color = "#6A4A3C") +
  annotate("segment", x = 0.4, y = -0.85, xend = p1$synthetic_alpha, yend = p1$empirical_alpha, color = "#6A4A3C", alpha = 0.7) +
  annotate("segment", x = 0.4, y = -0.85, xend = p2$synthetic_alpha, yend = p2$empirical_alpha, color = "#6A4A3C", alpha = 0.7) +
  annotate("segment", x = 0.4, y = -0.85, xend = p3$synthetic_alpha, yend = p3$empirical_alpha, color = "#6A4A3C", alpha = 0.7) +

  annotate("text", size = 2.5, x = 0.21, y = -0.2, hjust = 0, label = "IPIP Extraversion", color = "#00A0B0") +
  annotate("segment", x = 0.75, y = -0.2, xend = 0.87, yend = 0.90, color = "#00A0B0", alpha = 0.7) +
  annotate("text", size = 2.5, x = -0.32, y = 0.86, hjust = 0, label = "LOT Optimism", color = "#00A0B0") +
  annotate("segment", x = 0, y = 0.83, xend = 0.49, yend = 0.71, color = "#00A0B0", alpha = 0.7)

pt_plot_scales2 <- pt_plot_scales +
  annotate("text", size = 2.5, x = -0.2, y = 0.8, hjust = 1, label = "r(BFI Neuroticism,\nIPIP Neuroticism)", color = "#00A0B0") +
  annotate("segment", x = -.2, y = 0.8, xend = 0.22, yend = .84, color = "#00A0B0", alpha = 0.7) +
  annotate("text", size = 2.5, x = -.1, y = -0.9, hjust = 0, label = "r(BFI Depression facet,\nLOT Optimism)", color = "#00A0B0") +
  annotate("segment", x = -.1, y = -.9, xend = -0.17, yend = -.63, color = "#00A0B0", alpha = 0.7)


plot_scales2 <- plot_scales +
  annotate("text", size = 3, x = -1, y = 0.98, vjust = 0, hjust = 0, label = with(accuracy_bayes_scales, { sprintf("accuracy = %.2f [%.2f;%.2f]", latent_r, .lower, .upper) })) +
  annotate("text", size = 2.5, x = -0.1, y = 0.5, hjust = 1, label = "r(BFI Neuroticism,\nIPIP Neuroticism)", color = "#00A0B0") +
  annotate("segment", x = -.1, y = 0.5, xend = .84, yend = .84, color = "#00A0B0", alpha = 0.7) +
  annotate("text", size = 2.5, x = -.15, y = -0.7, hjust = 0, label = "r(BFI Depression facet,\nLOT Optimism)", color = "#00A0B0") +
  annotate("segment", x = -.15, y = -.7, xend = -.34, yend = -.63, color = "#00A0B0", alpha = 0.7)


(pt_plot_items2  + ggtitle("Pre-trained model before domain adaptation and fine-tuning")+
    pt_plot_rels2 +
    pt_plot_scales2) /


(plot_items2 + ggtitle("SurveyBot 3000") +
    plot_rels2  +
    plot_scales2)

ggsave("Figure_pilot.pdf", width = 8.3, height = 6, device = grDevices::cairo_pdf)
ggsave("Figure_pilot.png", width = 8.3, height = 6)



# ggsave("ignore/Figure_pilot.svg", width = 8.3, height = 5.5, device = svglite::svglite)

Prediction error plots

library(patchwork)

(plot_prediction_error_items + 
    coord_cartesian(xlim = c(-1, 1), ylim = c(0, 0.4)) +
    annotate("text", size = 3, x = -1, y = 0.4, vjust = 0, hjust = 0, label = with(rmse_items, { sprintf("RMSE = %.2f [%.2f;%.2f]", sigma, .lower, .upper) })) +
    
    plot_prediction_error_alpha + 
    coord_cartesian(xlim = c(-1, 1), ylim = c(0, 0.4))  +
    annotate("text", size = 3, x = -1, y = 0.4, vjust = 0, hjust = 0, label = with(rmse_alpha, { sprintf("RMSE = %.2f [%.2f;%.2f]", sigma, .lower, .upper) })) +
    
    plot_prediction_error_scales + 
    coord_cartesian(xlim = c(-1, 1), ylim = c(0, 0.4)) +
    annotate("text", size = 3, x = -1, y = 0.4, vjust = 0, hjust = 0, label = with(rmse_scales, { sprintf("RMSE = %.2f [%.2f;%.2f]", sigma, .lower, .upper) }))
)

ggsave("ignore/Figure_prediction_error_pilot.pdf", width = 8.3, height = 3, device = grDevices::cairo_pdf)
ggsave("ignore/Figure_prediction_error_pilot.png", width = 8.3, height = 3)
LS0tCnRpdGxlOiAiTGFuZ3VhZ2UgbW9kZWxzIGFjY3VyYXRlbHkgaW5mZXIgY29ycmVsYXRpb25zIGJldHdlZW4gcHN5Y2hvbG9naWNhbCBpdGVtcyBhbmQgc2NhbGVzIGZyb20gdGV4dCBhbG9uZSIKZGF0ZTogIjIwMjMtMTEtMDciCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjc3M6IHN0eWxlLmNzcyAKLS0tCgpIZXJlLCB3ZSBhcHBseSB0aGUgcHJlLXRyYWluZWQgbW9kZWwgYW5kIG91ciBmaW5lLXR1bmVkIG1vZGVsIHRvIGRhdGEgbm90IHVzZWQgZm9yIHRyYWluaW5nLCBhIGhvbGRvdXQuIFRoZSBob2xkb3V0IHNhbXBsZSB3YXMgY29sbGVjdGVkIGJ5IEJhaW5icmlkZ2UgZXQgYWwuIDIwMjIuCgpgYGB7ciB3YXJuaW5nPUYsbWVzc2FnZT1GfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGVycm9yID0gVCwgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGKQoKIyBMaWJyYXJpZXMgYW5kIFNldHRpbmdzCgojIExpYnMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGFycm93KQpsaWJyYXJ5KGdsdWUpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkobGF2YWFuKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShicm9vbSkKbGlicmFyeShicm9vbS5taXhlZCkKbGlicmFyeShicm1zKQpsaWJyYXJ5KHRpZHliYXllcykKbGlicmFyeShjbWRzdGFucikKbGlicmFyeShjb3dwbG90KQoKb3B0aW9ucyhtYy5jb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLCAKICAgICAgICBicm1zLmJhY2tlbmQgPSAiY21kc3RhbnIiLCAKICAgICAgICBicm1zLmZpbGVfcmVmaXQgPSAib25fY2hhbmdlIikKCgptb2RlbF9uYW1lID0gIkl0ZW1TaW1pbGFyaXR5VHJhaW5pbmctMjAyNDA1MDItdHJpYWwxMiIKI21vZGVsX25hbWUgPSAiaXRlbS1zaW1pbGFyaXR5LTIwMjMxMDE4LTEyMjUwNCIKcHJldHJhaW5lZF9tb2RlbF9uYW1lID0gImFsbC1tcG5ldC1iYXNlLXYyIgoKZGF0YV9wYXRoID0gZ2x1ZSgiLi8iKQpwcmV0cmFpbmVkX2RhdGFfcGF0aCA9IGdsdWUoIi4vIikKCnNldC5zZWVkKDQyKQoKCmhvbGRvdXQgPC0gYXJyb3c6OnJlYWRfZmVhdGhlcihmaWxlID0gZmlsZS5wYXRoKGRhdGFfcGF0aCwgZ2x1ZSgiaWdub3JlLnttb2RlbF9uYW1lfS5yYXcub3NmLWJhaW5icmlkZ2UtMjAyMS1zMi0wLml0ZW1fY29ycmVsYXRpb25zLmZlYXRoZXIiKSkpCgpwdF9ob2xkb3V0IDwtIGFycm93OjpyZWFkX2ZlYXRoZXIoZmlsZSA9IGZpbGUucGF0aChkYXRhX3BhdGgsIGdsdWUoImlnbm9yZS57cHJldHJhaW5lZF9tb2RlbF9uYW1lfS5yYXcub3NmLWJhaW5icmlkZ2UtMjAyMS1zMi0wLml0ZW1fY29ycmVsYXRpb25zLmZlYXRoZXIiKSkpCgpob2xkb3V0X21hcHBpbmdfZGF0YSA9IGFycm93OjpyZWFkX2ZlYXRoZXIoCiAgZmlsZSA9IGZpbGUucGF0aChkYXRhX3BhdGgsIGdsdWUoInttb2RlbF9uYW1lfS5yYXcub3NmLWJhaW5icmlkZ2UtMjAyMS1zMi0wLm1hcHBpbmcyLmZlYXRoZXIiKSkKKSAlPiUKICByZW5hbWUoc2NhbGVfMCA9IHNjYWxlMCwKICAgICAgICAgc2NhbGVfMSA9IHNjYWxlMSkKCmhvbGRvdXRfaHVtYW5fZGF0YSA9IGFycm93OjpyZWFkX2ZlYXRoZXIoCiAgZmlsZSA9IGZpbGUucGF0aChkYXRhX3BhdGgsIGdsdWUoInttb2RlbF9uYW1lfS5yYXcub3NmLWJhaW5icmlkZ2UtMjAyMS1zMi0wLmh1bWFuLmZlYXRoZXIiKSkKKQoKaG9sZG91dF9zY2FsZXMgPC0gYXJyb3c6OnJlYWRfZmVhdGhlcihmaWxlLnBhdGgoZGF0YV9wYXRoLCBnbHVlKCJ7bW9kZWxfbmFtZX0ucmF3Lm9zZi1iYWluYnJpZGdlLTIwMjEtczItMC5zY2FsZXMuZmVhdGhlciIpKQopCgpOIDwtIGhvbGRvdXRfaHVtYW5fZGF0YSAlPiUgc3VtbWFyaXNlX2FsbCh+IHN1bSghaXMubmEoLikpKSAlPiUgbWluKCkKdG90YWxfTiA8LSBucm93KGhvbGRvdXRfaHVtYW5fZGF0YSkKYGBgCgpUaGUgQmFpbmJyaWRnZSBkYXRhIHdhcyBjb2xsZWN0ZWQgb24gTj1gciB0b3RhbF9OYCByZXNwb25kZW50cy4gVGhlIGl0ZW0gd2l0aCB0aGUgbW9zdCBtaXNzaW5nIHZhbHVlcyBzdGlsbCBoYWQgbj1gciBOYC4KCgojIyBTeW50aGV0aWMgaW50ZXItaXRlbSBjb3JyZWxhdGlvbnMKYGBge3J9CmhvbGRvdXRfbGxtIDwtIGhvbGRvdXQgJT4lCiAgbGVmdF9qb2luKGhvbGRvdXRfbWFwcGluZ19kYXRhICU+JSBzZWxlY3QodmFyaWFibGVfMSA9IHZhcmlhYmxlLCBJbnN0cnVtZW50QSA9IGluc3RydW1lbnQsIFNjYWxlQSA9IHNjYWxlXzAsIFN1YnNjYWxlQSA9IHNjYWxlXzEpKSAlPiUKICBsZWZ0X2pvaW4oaG9sZG91dF9tYXBwaW5nX2RhdGEgJT4lIHNlbGVjdCh2YXJpYWJsZV8yID0gdmFyaWFibGUsIEluc3RydW1lbnRCID0gaW5zdHJ1bWVudCwgU2NhbGVCID0gc2NhbGVfMCwgU3Vic2NhbGVCID0gc2NhbGVfMSkpCgpwdF9ob2xkb3V0X2xsbSA8LSBwdF9ob2xkb3V0ICU+JQogIGxlZnRfam9pbihob2xkb3V0X21hcHBpbmdfZGF0YSAlPiUgc2VsZWN0KHZhcmlhYmxlXzEgPSB2YXJpYWJsZSwgSW5zdHJ1bWVudEEgPSBpbnN0cnVtZW50LCBTY2FsZUEgPSBzY2FsZV8wLCBTdWJzY2FsZUEgPSBzY2FsZV8xKSkgJT4lCiAgbGVmdF9qb2luKGhvbGRvdXRfbWFwcGluZ19kYXRhICU+JSBzZWxlY3QodmFyaWFibGVfMiA9IHZhcmlhYmxlLCBJbnN0cnVtZW50QiA9IGluc3RydW1lbnQsIFNjYWxlQiA9IHNjYWxlXzAsIFN1YnNjYWxlQiA9IHNjYWxlXzEpKQpgYGAKCgojIyMgQWNjdXJhY3kKYGBge3J9CnNlMiA8LSBtZWFuKGhvbGRvdXRfbGxtJGVtcGlyaWNhbF9yX3NlXjIpCgpyIDwtIGJyb29tOjp0aWR5KGNvci50ZXN0KGhvbGRvdXRfbGxtJGVtcGlyaWNhbF9yLCBob2xkb3V0X2xsbSRzeW50aGV0aWNfcikpCnB0X3IgPC0gYnJvb206OnRpZHkoY29yLnRlc3QocHRfaG9sZG91dF9sbG0kZW1waXJpY2FsX3IsIHB0X2hvbGRvdXRfbGxtJHN5bnRoZXRpY19yKSkKCm1vZGVsIDwtIHBhc3RlMCgnCiAgIyBMYXRlbnQgdmFyaWFibGVzCiAgUGVhcnNvbkxhdGVudCA9fiAxKmVtcGlyaWNhbF9yCgogICMgRml4aW5nIGVycm9yIHZhcmlhbmNlcyBiYXNlZCBvbiBrbm93biBzdGFuZGFyZCBlcnJvcnMKICBlbXBpcmljYWxfciB+fiAnLHNlMiwnKmVtcGlyaWNhbF9yCgogICMgUmVsYXRpb25zaGlwIGJldHdlZW4gbGF0ZW50IHZhcmlhYmxlcwogIFBlYXJzb25MYXRlbnQgfn4gc3ludGhldGljX3IKJykKCmZpdCA8LSBzZW0obW9kZWwsIGRhdGEgPSBob2xkb3V0X2xsbSkKcHRfZml0IDwtIHNlbShtb2RlbCwgZGF0YSA9IHB0X2hvbGRvdXRfbGxtKQoKbV9zeW50aF9yX2l0ZW1zIDwtIGJybSgKICBiZihlbXBpcmljYWxfciB8IG1pKGVtcGlyaWNhbF9yX3NlKSB+IHN5bnRoZXRpY19yICsgKDF8bW0odmFyaWFibGVfMSwgdmFyaWFibGVfMikpLAogICAgIHNpZ21hIH4gcyhzeW50aGV0aWNfcikpLCBkYXRhID0gaG9sZG91dF9sbG0sIAogIGZpbGUgPSAiaWdub3JlL21fc3ludGhfcl9pdGVtc19sbSIpCgpzZF9zeW50aCA8LSBzZChtX3N5bnRoX3JfaXRlbXMkZGF0YSRzeW50aGV0aWNfcikKCm5ld2RhdGEgPC0gbV9zeW50aF9yX2l0ZW1zJGRhdGEgJT4lIHNlbGVjdChlbXBpcmljYWxfciwgc3ludGhldGljX3IsIGVtcGlyaWNhbF9yX3NlKQplcHJlZHMgPC0gZXByZWRfZHJhd3MobmV3ZGF0YSA9IG5ld2RhdGEsIG9iaiA9IG1fc3ludGhfcl9pdGVtcywgcmVfZm9ybXVsYSA9IE5BLCBuZHJhd3MgPSAyMDApCnByZWRzIDwtIHByZWRpY3RlZF9kcmF3cyhuZXdkYXRhID0gbmV3ZGF0YSwgb2JqID0gbV9zeW50aF9yX2l0ZW1zLCByZV9mb3JtdWxhID0gTkEsIG5kcmF3cyA9IDIwMCkKZXByZWRfcHJlZHMgPC0gZXByZWRzICU+JSBsZWZ0X2pvaW4ocHJlZHMpCmJ5X2RyYXcgPC0gZXByZWRfcHJlZHMgJT4lIGdyb3VwX2J5KC5kcmF3KSAlPiUgCiAgc3VtbWFyaXNlKG1hZSA9IG1lYW4oYWJzKC5lcHJlZCAtIC5wcmVkaWN0aW9uKSksCiAgICAgICAgICAgIC5lcHJlZCA9IHZhciguZXByZWQpLAogICAgICAgICAgICAucHJlZGljdGlvbiA9IHZhcigucHJlZGljdGlvbiksCiAgICAgICAgICAgIHNpZ21hID0gc3FydCgucHJlZGljdGlvbiAtIC5lcHJlZCksCiAgICAgICAgICAgIGxhdGVudF9yID0gc3FydCguZXByZWQvLnByZWRpY3Rpb24pKQpybShlcHJlZF9wcmVkcykKCmFjY3VyYWN5X2JheWVzX2l0ZW1zIDwtIGJ5X2RyYXcgJT4lIG1lYW5faGRjaShsYXRlbnRfcikKCmJpbmRfcm93cygKICBwdF9yICU+JSAKICAgIG11dGF0ZShtb2RlbCA9ICJwcmUtdHJhaW5lZCIsIGtpbmQgPSAibWFuaWZlc3QiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gpLAogIHN0YW5kYXJkaXplZHNvbHV0aW9uKHB0X2ZpdCkgJT4lIAogICAgZmlsdGVyKGxocyA9PSAiUGVhcnNvbkxhdGVudCIsIHJocyA9PSAgInN5bnRoZXRpY19yIikgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gInByZS10cmFpbmVkIiwga2luZCA9ICJsYXRlbnQgb3V0Y29tZSAoU0VNKSIpICU+JSAKICAgIHNlbGVjdChtb2RlbCwga2luZCwgYWNjdXJhY3kgPSBlc3Quc3RkLCAKICAgICAgICAgICBjb25mLmxvdyA9IGNpLmxvd2VyLCBjb25mLmhpZ2ggPSBjaS51cHBlciksCiAgciAlPiUgCiAgICBtdXRhdGUobW9kZWwgPSAiZmluZS10dW5lZCIsIGtpbmQgPSAibWFuaWZlc3QiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gpLAogIHN0YW5kYXJkaXplZHNvbHV0aW9uKGZpdCkgJT4lIAogICAgZmlsdGVyKGxocyA9PSAiUGVhcnNvbkxhdGVudCIsIHJocyA9PSAgInN5bnRoZXRpY19yIikgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gImZpbmUtdHVuZWQiLCBraW5kID0gImxhdGVudCBvdXRjb21lIChTRU0pIikgJT4lIAogICAgc2VsZWN0KG1vZGVsLCBraW5kLCBhY2N1cmFjeSA9IGVzdC5zdGQsIAogICAgICAgICAgIGNvbmYubG93ID0gY2kubG93ZXIsIGNvbmYuaGlnaCA9IGNpLnVwcGVyKSwKICBhY2N1cmFjeV9iYXllc19pdGVtcyAlPiUgCiAgICBtdXRhdGUobW9kZWwgPSAiZmluZS10dW5lZCIsIGtpbmQgPSAibGF0ZW50IG91dGNvbWUgKEJheWVzaWFuIEVJVikiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gbGF0ZW50X3IsIGNvbmYubG93ID0gLmxvd2VyLCBjb25mLmhpZ2ggPSAudXBwZXIpCikgJT4lIAogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKQpgYGAKCgo8ZGV0YWlscz48c3VtbWFyeT48aDQ+UHJlZGljdGlvbiBlcnJvciBwbG90IGFjY29yZGluZyB0byBzeW50aGV0aWMgZXN0aW1hdGU8L2g0Pjwvc3VtbWFyeT4KCmBgYHtyfQptX3N5bnRoX3JfaXRlbXMKCnByZWQgPC0gY29uZGl0aW9uYWxfZWZmZWN0cyhtX3N5bnRoX3JfaXRlbXMsIG1ldGhvZCA9ICJwcmVkaWN0IikKa2FibGUocm1zZV9pdGVtcyA8LSBieV9kcmF3ICU+JSBtZWFuX2hkY2koc2lnbWEpLCBjYXB0aW9uID0gIkF2ZXJhZ2UgcHJlZGljdGlvbiBlcnJvciAoUk1TRSkiLCBkaWdpdHMgPSAyKQprYWJsZShtYWVfaXRlbXMgPC0gYnlfZHJhdyAlPiUgbWVhbl9oZGNpKG1hZSksIGNhcHRpb24gPSAiQXZlcmFnZSBwcmVkaWN0aW9uIGVycm9yIChNQUUpIiwgZGlnaXRzID0gMikKCnBsb3RfcHJlZGljdGlvbl9lcnJvcl9pdGVtcyA8LSBwbG90KGNvbmRpdGlvbmFsX2VmZmVjdHMobV9zeW50aF9yX2l0ZW1zLCBkcGFyID0gInNpZ21hIiksIHBsb3QgPSBGKVtbMV1dICsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIlN5bnRoZXRpYyBpbnRlci1pdGVtIGNvcnJlbGF0aW9uIikgKyAKICB5bGFiKCJQcmVkaWN0aW9uIGVycm9yIChzaWdtYSkiKSArCiAgZ2VvbV9zbW9vdGgoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gIiNhNDg1MDAiLCBmaWxsID0gIiNFREM5NTEiKQoKcGxvdF9wcmVkaWN0aW9uX2Vycm9yX2l0ZW1zCmBgYAoKPC9kZXRhaWxzPgoKCgojIyMgU2NhdHRlciBwbG90CmBgYHtyfQpnZ3Bsb3QoaG9sZG91dF9sbG0sIGFlcyhzeW50aGV0aWNfciwgZW1waXJpY2FsX3IsIAogICAgICAgICAgICAgIHltaW4gPSBlbXBpcmljYWxfciAtIGVtcGlyaWNhbF9yX3NlLAogICAgICAgICAgICAgIHltYXggPSBlbXBpcmljYWxfciArIGVtcGlyaWNhbF9yX3NlKSkgKyAKICBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjMDBBMEIwIiwgYWxwaGEgPSAwLjEsIHNpemUgPSAxKSArCiAgZ2VvbV9zbW9vdGgoYWVzKAogICAgeCA9IHN5bnRoZXRpY19yLAogICAgeSA9IGVzdGltYXRlX18sCiAgICB5bWluID0gbG93ZXJfXywKICAgIHltYXggPSB1cHBlcl9fLAogICksIHN0YXQgPSAiaWRlbnRpdHkiLCAKICBjb2xvciA9ICIjYTQ4NTAwIiwKICBmaWxsID0gIiNFREM5NTEiLAogIGRhdGEgPSBhcy5kYXRhLmZyYW1lKHByZWQkc3ludGhldGljX3IpKSArCiAgeGxhYigiU3ludGhldGljIGludGVyLWl0ZW0gY29ycmVsYXRpb24iKSArIAogIHlsYWIoIkVtcGlyaWNhbCBpbnRlci1pdGVtIGNvcnJlbGF0aW9uIikgKwogIHRoZW1lX2J3KCkgKwogIGNvb3JkX2ZpeGVkKHhsaW0gPSBjKC0xLDEpLCB5bGltID0gYygtMSwxKSkgLT4gcGxvdF9pdGVtcwpwbG90X2l0ZW1zCmBgYAoKIyMjIEludGVyYWN0aXZlIHBsb3QKVGhpcyBwbG90IHNob3dzIG9ubHkgMjAwMCByYW5kb21seSBzZWxlY3RlZCBpdGVtIHBhaXJzIHRvIGNvbnNlcnZlIG1lbW9yeS4gQSBbZnVsbCBpbnRlcmFjdGl2ZSBwbG90XSgyX2ludGVyYWN0aXZlX2l0ZW1fcGxvdC5odG1sKSBleGlzdHMsIGJ1dCBtYXkgcmVhY3Qgc2xvd2x5LgoKYGBge3J9Cml0ZW1fcGFpcl90YWJsZSA8LSBob2xkb3V0X2xsbSAlPiUgCiAgIGxlZnRfam9pbihob2xkb3V0X21hcHBpbmdfZGF0YSAlPiUgc2VsZWN0KHZhcmlhYmxlXzEgPSB2YXJpYWJsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlbV90ZXh0XzEgPSBpdGVtX3RleHQpKSAlPiUgCiAgIGxlZnRfam9pbihob2xkb3V0X21hcHBpbmdfZGF0YSAlPiUgc2VsZWN0KHZhcmlhYmxlXzIgPSB2YXJpYWJsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlbV90ZXh0XzIgPSBpdGVtX3RleHQpKQoKIyBpdGVtX3BhaXJfdGFibGUgJT4lIGZpbHRlcihzdHJfbGVuZ3RoKGl0ZW1fdGV4dF8xKSA8IDMwLCBzdHJfbGVuZ3RoKGl0ZW1fdGV4dF8yKSA8IDMwKSAlPiUgCiMgICBsZWZ0X2pvaW4ocHRfaG9sZG91dF9sbG0gJT4lIHJlbmFtZShzeW50aGV0aWNfcl9wdCA9IHN5bnRoZXRpY19yKSkgJT4lIAojICAgc2VsZWN0KGl0ZW1fdGV4dF8xLCBpdGVtX3RleHRfMiwgZW1waXJpY2FsX3IsIHN5bnRoZXRpY19yLCBzeW50aGV0aWNfcl9wdCkgJT4lIFZpZXcoKQpyaW86OmV4cG9ydChpdGVtX3BhaXJfdGFibGUsICJpZ25vcmUvaXRlbV9wYWlyX3RhYmxlLmZlYXRoZXIiKQoKKGl0ZW1fcGFpcl90YWJsZSAlPiUgCiAgbXV0YXRlKHN5bnRoZXRpY19yID0gcm91bmQoc3ludGhldGljX3IsIDIpLAogICAgICAgICBlbXBpcmljYWxfciA9IHJvdW5kKGVtcGlyaWNhbF9yLCAyKSwKICAgICAgICAgaXRlbXMgPSBzdHJfcmVwbGFjZV9hbGwoc3RyX2MoaXRlbV90ZXh0XzEsICJcbiIsIGl0ZW1fdGV4dF8yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJfKyIsICIgIikpICU+JSAKICAgIHNhbXBsZV9uKDIwMDApICU+JQpnZ3Bsb3QoLiwgYWVzKHN5bnRoZXRpY19yLCBlbXBpcmljYWxfciwgCiAgICAgICAgICAgICAgIyB5bWluID0gZW1waXJpY2FsX3IgLSBlbXBpcmljYWxfcl9zZSwgCiAgICAgICAgICAgICAgIyB5bWF4ID0gZW1waXJpY2FsX3IgKyBlbXBpcmljYWxfcl9zZSwgCiAgICAgICAgICAgICAgbGFiZWwgPSBpdGVtcykpICsgCiAgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiIzAwQTBCMCIsIGFscGhhID0gMC4zLCBzaXplID0gMSkgKwogIHhsYWIoIlN5bnRoZXRpYyBpbnRlci1pdGVtIGNvcnJlbGF0aW9uIikgKyAKICB5bGFiKCJFbXBpcmljYWwgaW50ZXItaXRlbSBjb3JyZWxhdGlvbiIpICsKICB0aGVtZV9idygpICsKICBjb29yZF9maXhlZCh4bGltID0gYygtMSwxKSwgeWxpbSA9IGMoLTEsMSkpKSAlPiUgCiAgZ2dwbG90bHkoKQoKaXRlbV9wYWlyX3RhYmxlIDwtIGl0ZW1fcGFpcl90YWJsZSAlPiUgCiAgbXV0YXRlKGVtcGlyaWNhbF9yID0gc3ByaW50ZigiJS4yZsKxJS4zZiIsIGVtcGlyaWNhbF9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbXBpcmljYWxfcl9zZSksCiAgICAgICAgICAgc3ludGhldGljX3IgPSBzcHJpbnRmKCIlLjJmIiwgc3ludGhldGljX3IpKSAlPiUgCiAgc2VsZWN0KGl0ZW1fdGV4dF8xLCBpdGVtX3RleHRfMiwgZW1waXJpY2FsX3IsIHN5bnRoZXRpY19yKQpyaW86OmV4cG9ydChpdGVtX3BhaXJfdGFibGUsICJpdGVtX3BhaXJfdGFibGUueGxzeCIpCmBgYAoKPGRldGFpbHM+PHN1bW1hcnk+PGgzPlJvYnVzdG5lc3MgY2hlY2tzPC9oMz48L3N1bW1hcnk+CgoKIyMjIyBDb21wYXJpbmcgc3BsaW5lIGFuZCBwb2x5bm9taWFsIG1vZGVscyBmb3IgaGV0ZXJvc2NlZGFzdGljaXR5CmBgYHtyfQptX3N5bnRoX3JfaXRlbXNfcG9seSA8LSBicm0oCiAgYmYoZW1waXJpY2FsX3IgfCBtaShlbXBpcmljYWxfcl9zZSkgfiBzeW50aGV0aWNfciArICgxfG1tKHZhcmlhYmxlXzEsIHZhcmlhYmxlXzIpKSwKICAgICBzaWdtYSB+IHBvbHkoc3ludGhldGljX3IsIGRlZ3JlZSA9IDMpKSwgZGF0YSA9IGhvbGRvdXRfbGxtLCAKICBmaWxlID0gImlnbm9yZS9tX3N5bnRoX3JfaXRlbXNfbG1fcG9seSIpCgpuZXdkYXRhIDwtIG1fc3ludGhfcl9pdGVtc19wb2x5JGRhdGEgJT4lIHNlbGVjdChlbXBpcmljYWxfciwgc3ludGhldGljX3IsIGVtcGlyaWNhbF9yX3NlKQplcHJlZHMgPC0gZXByZWRfZHJhd3MobmV3ZGF0YSA9IG5ld2RhdGEsIG9iaiA9IG1fc3ludGhfcl9pdGVtc19wb2x5LCByZV9mb3JtdWxhID0gTkEsIG5kcmF3cyA9IDIwMCkKcHJlZHMgPC0gcHJlZGljdGVkX2RyYXdzKG5ld2RhdGEgPSBuZXdkYXRhLCBvYmogPSBtX3N5bnRoX3JfaXRlbXNfcG9seSwgcmVfZm9ybXVsYSA9IE5BLCBuZHJhd3MgPSAyMDApCmVwcmVkX3ByZWRzIDwtIGVwcmVkcyAlPiUgbGVmdF9qb2luKHByZWRzKQpieV9kcmF3IDwtIGVwcmVkX3ByZWRzICU+JSBncm91cF9ieSguZHJhdykgJT4lIAogIHN1bW1hcmlzZSguZXByZWQgPSB2YXIoLmVwcmVkKSwKICAgICAgICAgICAgLnByZWRpY3Rpb24gPSB2YXIoLnByZWRpY3Rpb24pLAogICAgICAgICAgICBzaWdtYSA9IHNxcnQoLnByZWRpY3Rpb24gLSAuZXByZWQpLAogICAgICAgICAgICBsYXRlbnRfciA9IHNxcnQoLmVwcmVkLy5wcmVkaWN0aW9uKSkKCmFjY3VyYWN5X2JheWVzX2l0ZW1zX3BvbHkgPC0gYnlfZHJhdyAlPiUgbWVhbl9oZGNpKGxhdGVudF9yKQoKYmluZF9yb3dzKAogIGFjY3VyYWN5X2JheWVzX2l0ZW1zICU+JSAKICAgIG11dGF0ZShtb2RlbCA9ICJzcGxpbmUiLCBraW5kID0gImxhdGVudCBvdXRjb21lIChCYXllc2lhbiBFSVYpIikgJT4lIAogICAgc2VsZWN0KG1vZGVsLCBraW5kLCBhY2N1cmFjeSA9IGxhdGVudF9yLCBjb25mLmxvdyA9IC5sb3dlciwgY29uZi5oaWdoID0gLnVwcGVyKSwKICBhY2N1cmFjeV9iYXllc19pdGVtc19wb2x5ICU+JSAKICAgIG11dGF0ZShtb2RlbCA9ICJwb2x5bm9taWFsIiwga2luZCA9ICJsYXRlbnQgb3V0Y29tZSAoQmF5ZXNpYW4gRUlWKSIpICU+JSAKICAgIHNlbGVjdChtb2RlbCwga2luZCwgYWNjdXJhY3kgPSBsYXRlbnRfciwgY29uZi5sb3cgPSAubG93ZXIsIGNvbmYuaGlnaCA9IC51cHBlcikKKSAlPiUgCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiQ29tcGFyaW5nIHNwbGluZSBhbmQgcG9seW5vbWlhbCBtb2RlbHMgZm9yIGl0ZW0gY29ycmVsYXRpb25zIikKYGBgCgpgYGB7cn0KcGxvdF9wcmVkaWN0aW9uX2Vycm9yX2l0ZW1zX3BvbHkgPC0gcGxvdChjb25kaXRpb25hbF9lZmZlY3RzKG1fc3ludGhfcl9pdGVtc19wb2x5LCBkcGFyID0gInNpZ21hIiksIHBsb3QgPSBGKVtbMV1dICsgCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIlN5bnRoZXRpYyBpbnRlci1pdGVtIGNvcnJlbGF0aW9uIikgKyAKICB5bGFiKCJQcmVkaWN0aW9uIGVycm9yIChzaWdtYSkiKSArCiAgZ2VvbV9zbW9vdGgoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gIiNhNDg1MDAiLCBmaWxsID0gIiNFREM5NTEiKQoKcGxvdF9wcmVkaWN0aW9uX2Vycm9yX2l0ZW1zX3BvbHkKYGBgCgoKSXMgdGhlIGFjY3VyYWN5IGxvd2VyIHdpdGhpbi9hY3Jvc3Mgc2NhbGVzIGFuZCBpbnN0cnVtZW50cz8KCmBgYHtyfQpob2xkb3V0X2xsbSAlPiUgCiAgbXV0YXRlKHNhbWVfaW5zdHJ1bWVudCA9IGlmX2Vsc2UoSW5zdHJ1bWVudEEgPT0gSW5zdHJ1bWVudEIsIDEsIDAsMCksCiAgICAgICAgIHNhbWVfc2NhbGUgPSBpZl9lbHNlKFNjYWxlQSA9PSBTY2FsZUIsIDEsMCwwKSwKICAgICAgICAgc2FtZV9zdWJzY2FsZSA9IGlmX2Vsc2Uoc2FtZV9zY2FsZSAmIFN1YnNjYWxlQSA9PSBTdWJzY2FsZUIsIDEsMCwwKSkgJT4lIAogIGdyb3VwX2J5KHNhbWVfc2NhbGUsIHNhbWVfaW5zdHJ1bWVudCwgc2FtZV9zdWJzY2FsZSkgJT4lIAogIHN1bW1hcmlzZShicm9vbTo6dGlkeShjb3IudGVzdChzeW50aGV0aWNfciwgZW1waXJpY2FsX3IpKSwgc2RfZW1wX3IgPSBzZChlbXBpcmljYWxfciksIG4gPSBuKCkpICU+JSAKICBzZWxlY3Qoc2FtZV9pbnN0cnVtZW50LCBzYW1lX3NjYWxlLCBzYW1lX3N1YnNjYWxlLCByID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gsIG4sIHNkX2VtcF9yKSAlPiUgCiAgYXJyYW5nZShzYW1lX2luc3RydW1lbnQsIHNhbWVfc2NhbGUsIHNhbWVfc3Vic2NhbGUpICU+JSAKICBrYWJsZSgpCmBgYAoKSXMgdGhlIGFjY3VyYWN5IGxvd2VyIG91dHNpZGUgY2xhc3NpYyBCaWcgRml2ZT8KCmBgYHtyfQpob2xkb3V0X2xsbSAlPiUgCiAgbXV0YXRlKGJpZ19maXZlID0gY2FzZV93aGVuKAogICAgc3RyX2RldGVjdChJbnN0cnVtZW50QSwgIihQZXJzb25hbGl0eXxCaWcgRml2ZSkiKSAmIHN0cl9kZXRlY3QoSW5zdHJ1bWVudEIsICIoUGVyc29uYWxpdHl8QmlnIEZpdmUpIikgfiAiYm90aCIsCiAgICBzdHJfZGV0ZWN0KEluc3RydW1lbnRBLCAiKFBlcnNvbmFsaXR5fEJpZyBGaXZlKSIpIHwgc3RyX2RldGVjdChJbnN0cnVtZW50QiwgIihQZXJzb25hbGl0eXxCaWcgRml2ZSkiKSB+ICJlaXRoZXIiLAogICAgVFJVRSB+ICJub25lIgogICAgICAgICApKSAlPiUgCiAgZ3JvdXBfYnkoYmlnX2ZpdmUpICU+JSAKICBzdW1tYXJpc2UoYnJvb206OnRpZHkoY29yLnRlc3Qoc3ludGhldGljX3IsIGVtcGlyaWNhbF9yKSksIHNkX2VtcF9yID0gc2QoZW1waXJpY2FsX3IpLCBuID0gbigpKSAlPiUgCiAgc2VsZWN0KGJpZ19maXZlLCByID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gsIG4sIHNkX2VtcF9yKSAlPiUgCiAgYXJyYW5nZShiaWdfZml2ZSkgJT4lIAogIGthYmxlKCkKYGBgCgpJcyB0aGUgYWNjdXJhY3kgbG93ZXIgZm9yIGl0ZW1zIHRoYXQgaGF2ZSBsb3cgdmFyaWFuY2U/CgpgYGB7cn0KaXRlbV92YXJpYW5jZXMgPC0gaG9sZG91dF9odW1hbl9kYXRhICU+JSBzdW1tYXJpc2VfYWxsKH4gc2QoLiwgbmEucm0gPSBUKSkgJT4lIAogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gIml0ZW1fc2QiKQoKYnlfbWF4X2NvdiA8LSBob2xkb3V0X2xsbSAlPiUgCiAgbGVmdF9qb2luKGl0ZW1fdmFyaWFuY2VzLCBieSA9IGMoInZhcmlhYmxlXzEiID0gInZhcmlhYmxlIikpICU+JSAKICBsZWZ0X2pvaW4oaXRlbV92YXJpYW5jZXMsIGJ5ID0gYygidmFyaWFibGVfMiIgPSAidmFyaWFibGUiKSwgc3VmZml4ID0gYygiXzEiLCAiXzIiKSkgJT4lIAogIG11dGF0ZShtYXhfY292YXJpYW5jZSA9IGNlaWxpbmcoKGl0ZW1fc2RfMSAqIGl0ZW1fc2RfMikqMTApLzEwKQoKcnNfYnlfbWF4X2NvdiA8LSBieV9tYXhfY292ICU+JSAKICBncm91cF9ieShtYXhfY292YXJpYW5jZSkgJT4lIAogIGZpbHRlcihuKCkgPiAzKSAlPiUgCiAgc3VtbWFyaXNlKGJyb29tOjp0aWR5KGNvci50ZXN0KHN5bnRoZXRpY19yLCBlbXBpcmljYWxfcikpLCBzZF9lbXBfciA9IHNkKGVtcGlyaWNhbF9yKSwgbiA9IG4oKSkgJT4lIAogIHNlbGVjdChtYXhfY292YXJpYW5jZSwgciA9IGVzdGltYXRlLCBjb25mLmxvdywgY29uZi5oaWdoLCBuLCBzZF9lbXBfcikgJT4lIAogIGFycmFuZ2UobWF4X2NvdmFyaWFuY2UpCgpyc19ieV9tYXhfY292JT4lIAogIGthYmxlKCkKcnNfYnlfbWF4X2NvdiAlPiUgZ2dwbG90KGFlcyhtYXhfY292YXJpYW5jZSwgciwgeW1pbiA9IGNvbmYubG93LCB5bWF4ID0gY29uZi5oaWdoKSkgKwogIGdlb21fcG9pbnRyYW5nZSgpCgpieV9tYXhfY292JT4lIAogIGZpbHRlcihtYXhfY292YXJpYW5jZSA+IC43KSAlPiUgCiAgc3VtbWFyaXNlKGJyb29tOjp0aWR5KGNvci50ZXN0KHN5bnRoZXRpY19yLCBlbXBpcmljYWxfcikpLCBzZF9lbXBfciA9IHNkKGVtcGlyaWNhbF9yKSwgbiA9IG4oKSkgJT4lIAogIGthYmxlKCkKCmhvbGRvdXRfbGxtICU+JSAKICBsZWZ0X2pvaW4oaXRlbV92YXJpYW5jZXMsIGJ5ID0gYygidmFyaWFibGVfMSIgPSAidmFyaWFibGUiKSkgJT4lIAogIGxlZnRfam9pbihpdGVtX3ZhcmlhbmNlcywgYnkgPSBjKCJ2YXJpYWJsZV8yIiA9ICJ2YXJpYWJsZSIpLCBzdWZmaXggPSBjKCJfMSIsICJfMiIpKSAlPiUgCiAgbXV0YXRlKG1heF9jb3ZhcmlhbmNlID0gY2VpbGluZygoaXRlbV9zZF8xICogaXRlbV9zZF8yKSoxMCkvMTApICU+JSAKICBmaWx0ZXIobWF4X2NvdmFyaWFuY2UgPiAxKSAlPiUgCiAgc3VtbWFyaXNlKGJyb29tOjp0aWR5KGNvci50ZXN0KHN5bnRoZXRpY19yLCBlbXBpcmljYWxfcikpLCBzZF9lbXBfciA9IHNkKGVtcGlyaWNhbF9yKSwgbiA9IG4oKSkgJT4lIAogIHNlbGVjdChyID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gsIG4sIHNkX2VtcF9yKSAlPiUgCiAga25pdHI6OmthYmxlKCkKYGBgCgojIyMjIEF2ZXJhZ2VzCmBgYHtyfQpob2xkb3V0X2xsbSAlPiUgc3VtbWFyaXNlKAogIG1lYW4oc3ludGhldGljX3IpLAogIG1lYW4oZW1waXJpY2FsX3IpLAogIG1lYW4oYWJzKHN5bnRoZXRpY19yKSksCiAgbWVhbihhYnMoZW1waXJpY2FsX3IpKSwKICBwcm9wX25lZ2F0aXZlID0gc3VtKGVtcGlyaWNhbF9yIDwgMCkvbigpLAogIHByb3BfcG9zID0gc3VtKGVtcGlyaWNhbF9yID4gMCkvbigpLAogIGBwcm9wX2JlbG93Xy0uMTBgID0gc3VtKGVtcGlyaWNhbF9yIDwgLTAuMSkvbigpLAogIGBwcm9wX2Fib3ZlXy4xMGAgPSBzdW0oZW1waXJpY2FsX3IgPiAwLjEpL24oKSwKKSAlPiUga2FibGUoZGlnaXRzID0gMiwgY2FwdGlvbiA9ICJBdmVyYWdlIGNvcnJlbGF0aW9ucyIpCmBgYAoKSXMgdGhlIGFjY3VyYWN5IGxvd2VyIGZvciB0aGUgcHJlLXRyYWluZWQgbW9kZWw/CgpgYGB7cn0KZ2dwbG90KHB0X2hvbGRvdXRfbGxtLCBhZXMoc3ludGhldGljX3IsIGVtcGlyaWNhbF9yLCAKICAgICAgICAgICAgICB5bWluID0gZW1waXJpY2FsX3IgLSBlbXBpcmljYWxfcl9zZSwKICAgICAgICAgICAgICB5bWF4ID0gZW1waXJpY2FsX3IgKyBlbXBpcmljYWxfcl9zZSkpICsgCiAgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiIzAwQTBCMCIsIGFscGhhID0gMC4xLCBzaXplID0gMSkgKwogIHhsYWIoIlN5bnRoZXRpYyBpbnRlci1pdGVtIGNvcnJlbGF0aW9uIikgKyAKICB5bGFiKCJFbXBpcmljYWwgaW50ZXItaXRlbSBjb3JyZWxhdGlvbiIpICsKICB0aGVtZV9idygpICsKICBjb29yZF9maXhlZCh4bGltID0gYygtMSwxKSwgeWxpbSA9IGMoLTEsMSkpIC0+IHB0X3Bsb3RfaXRlbXMKcHRfcGxvdF9pdGVtcwpgYGAKCgo8L2RldGFpbHM+CgoKCltGdWxsIHRhYmxlIG9mIHN5bnRoZXRpYyBhbmQgZW1waXJpY2FsIGl0ZW0gcGFpciBjb3JyZWxhdGlvbnNdKGl0ZW1fcGFpcl90YWJsZS54bHN4KQoKCiMjIFN5bnRoZXRpYyBSZWxpYWJpbGl0aWVzCmBgYHtyfQoKc2NhbGVzIDwtIHJlYWRfcmRzKGZpbGUgPSBmaWxlLnBhdGgoZGF0YV9wYXRoLCBnbHVlKCJpZ25vcmUuc2NhbGVzX3dpdGhfYWxwaGFfc2UucmRzIikpKQoKCnNvdXJjZSgiZ2xvYmFsX2Z1bmN0aW9ucy5SIikKCnNjYWxlcyA8LSBzY2FsZXMgJT4lIGZpbHRlcihudW1iZXJfb2ZfaXRlbXMgPj0gMykKYGBgCgoKIyMjIEFjY3VyYWN5CmBgYHtyfQpzZTIgPC0gbWVhbihzY2FsZXMkZW1waXJpY2FsX2FscGhhX3NlXjIpCnIgPC0gYnJvb206OnRpZHkoY29yLnRlc3Qoc2NhbGVzJGVtcGlyaWNhbF9hbHBoYSwgc2NhbGVzJHN5bnRoZXRpY19hbHBoYSkpCnB0X3IgPC0gYnJvb206OnRpZHkoY29yLnRlc3Qoc2NhbGVzJGVtcGlyaWNhbF9hbHBoYSwgc2NhbGVzJHB0X3N5bnRoZXRpY19hbHBoYSkpCgptb2RlbCA8LSBwYXN0ZTAoJwogICMgTGF0ZW50IHZhcmlhYmxlcwogIGxhdGVudF9yZWFsX3JlbCA9fiAxKmVtcGlyaWNhbF9hbHBoYQoKICAjIEZpeGluZyBlcnJvciB2YXJpYW5jZXMgYmFzZWQgb24ga25vd24gc3RhbmRhcmQgZXJyb3JzCiAgZW1waXJpY2FsX2FscGhhIH5+ICcsc2UyLCcqZW1waXJpY2FsX2FscGhhCgogICMgUmVsYXRpb25zaGlwIGJldHdlZW4gbGF0ZW50IHZhcmlhYmxlcwogIGxhdGVudF9yZWFsX3JlbCB+fiBzeW50aGV0aWNfYWxwaGEKJykKCmZpdCA8LSBzZW0obW9kZWwsIGRhdGEgPSBzY2FsZXMpCnB0X2ZpdCA8LSBzZW0obW9kZWwsIGRhdGEgPSBzY2FsZXMgJT4lIAogICAgICAgICAgICAgICAgc2VsZWN0KGVtcGlyaWNhbF9hbHBoYSwgc3ludGhldGljX2FscGhhID0gcHRfc3ludGhldGljX2FscGhhKSkKCm1fbG1zeW50aF9yZWxfc2NhbGVzIDwtIGJybSgKICBiZihlbXBpcmljYWxfYWxwaGEgfCBtaShlbXBpcmljYWxfYWxwaGFfc2UpIH4gc3ludGhldGljX2FscGhhLAogICAgIHNpZ21hIH4gcyhzeW50aGV0aWNfYWxwaGEpKSwgZGF0YSA9IHNjYWxlcywgCiAgZmlsZSA9ICJpZ25vcmUvbV9zeW50aF9yZWxfbG0iKQoKbmV3ZGF0YSA8LSBtX2xtc3ludGhfcmVsX3NjYWxlcyRkYXRhICU+JSBzZWxlY3QoZW1waXJpY2FsX2FscGhhLCBzeW50aGV0aWNfYWxwaGEsIGVtcGlyaWNhbF9hbHBoYV9zZSkKZXByZWRzIDwtIGVwcmVkX2RyYXdzKG5ld2RhdGEgPSBuZXdkYXRhLCBvYmogPSBtX2xtc3ludGhfcmVsX3NjYWxlcywgcmVfZm9ybXVsYSA9IE5BKQpwcmVkcyA8LSBwcmVkaWN0ZWRfZHJhd3MobmV3ZGF0YSA9IG5ld2RhdGEsIG9iaiA9IG1fbG1zeW50aF9yZWxfc2NhbGVzLCByZV9mb3JtdWxhID0gTkEpCmVwcmVkX3ByZWRzIDwtIGVwcmVkcyAlPiUgbGVmdF9qb2luKHByZWRzKQpieV9kcmF3IDwtIGVwcmVkX3ByZWRzICU+JSBncm91cF9ieSguZHJhdykgJT4lIAogIHN1bW1hcmlzZSguZXByZWQgPSB2YXIoLmVwcmVkKSwKICAgICAgICAgICAgLnByZWRpY3Rpb24gPSB2YXIoLnByZWRpY3Rpb24pLAogICAgICAgICAgICBzaWdtYSA9IHNxcnQoLnByZWRpY3Rpb24gLSAuZXByZWQpLAogICAgICAgICAgICBsYXRlbnRfciA9IHNxcnQoLmVwcmVkLy5wcmVkaWN0aW9uKSkKCmFjY3VyYWN5X2JheWVzX3JlbHMgPC0gYnlfZHJhdyAlPiUgbWVhbl9oZGNpKGxhdGVudF9yKQoKYmluZF9yb3dzKAogIHB0X3IgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gInByZS10cmFpbmVkIiwga2luZCA9ICJtYW5pZmVzdCIpICU+JSAKICAgIHNlbGVjdChtb2RlbCwga2luZCwgYWNjdXJhY3kgPSBlc3RpbWF0ZSwgY29uZi5sb3csIGNvbmYuaGlnaCksCiAgc3RhbmRhcmRpemVkc29sdXRpb24ocHRfZml0KSAlPiUgCiAgICBmaWx0ZXIobGhzID09ICJsYXRlbnRfcmVhbF9yZWwiLCByaHMgPT0gICJzeW50aGV0aWNfYWxwaGEiKSAlPiUgCiAgICBtdXRhdGUobW9kZWwgPSAicHJlLXRyYWluZWQiLCBraW5kID0gImxhdGVudCBvdXRjb21lIChTRU0pIikgJT4lIAogICAgc2VsZWN0KG1vZGVsLCBraW5kLCBhY2N1cmFjeSA9IGVzdC5zdGQsIAogICAgICAgICAgIGNvbmYubG93ID0gY2kubG93ZXIsIGNvbmYuaGlnaCA9IGNpLnVwcGVyKSwKICByICU+JSAKICAgIG11dGF0ZShtb2RlbCA9ICJmaW5lLXR1bmVkIiwga2luZCA9ICJtYW5pZmVzdCIpICU+JSAKICAgIHNlbGVjdChtb2RlbCwga2luZCwgYWNjdXJhY3kgPSBlc3RpbWF0ZSwgY29uZi5sb3csIGNvbmYuaGlnaCksCiAgc3RhbmRhcmRpemVkc29sdXRpb24oZml0KSAlPiUgCiAgICBmaWx0ZXIobGhzID09ICJsYXRlbnRfcmVhbF9yZWwiLCByaHMgPT0gICJzeW50aGV0aWNfYWxwaGEiKSAlPiUgCiAgICBtdXRhdGUobW9kZWwgPSAiZmluZS10dW5lZCIsIGtpbmQgPSAibGF0ZW50IG91dGNvbWUgKFNFTSkiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gZXN0LnN0ZCwgCiAgICAgICAgICAgY29uZi5sb3cgPSBjaS5sb3dlciwgY29uZi5oaWdoID0gY2kudXBwZXIpLAogIGFjY3VyYWN5X2JheWVzX3JlbHMgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gImZpbmUtdHVuZWQiLCBraW5kID0gImxhdGVudCBvdXRjb21lIChCYXllc2lhbiBFSVYpIikgJT4lIAogICAgc2VsZWN0KG1vZGVsLCBraW5kLCBhY2N1cmFjeSA9IGxhdGVudF9yLCBjb25mLmxvdyA9IC5sb3dlciwgY29uZi5oaWdoID0gLnVwcGVyKQogICkgJT4lIAogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKQpgYGAKCgo8ZGV0YWlscz48c3VtbWFyeT48aDQ+UHJlZGljdGlvbiBlcnJvciBwbG90IGFjY29yZGluZyB0byBzeW50aGV0aWMgZXN0aW1hdGU8L2g0Pjwvc3VtbWFyeT4KCmBgYHtyfQptX2xtc3ludGhfcmVsX3NjYWxlcwoKcHJlZCA8LSBjb25kaXRpb25hbF9lZmZlY3RzKG1fbG1zeW50aF9yZWxfc2NhbGVzLCBtZXRob2QgPSAicHJlZGljdCIpCmthYmxlKHJtc2VfYWxwaGEgPC0gYnlfZHJhdyAlPiUgbWVhbl9oZGNpKHNpZ21hKSwgY2FwdGlvbiA9ICJBdmVyYWdlIHByZWRpY3Rpb24gZXJyb3IgKFJNU0UpIiwgZGlnaXRzID0gMikKCnBsb3RfcHJlZGljdGlvbl9lcnJvcl9hbHBoYSA8LSBwbG90KGNvbmRpdGlvbmFsX2VmZmVjdHMobV9sbXN5bnRoX3JlbF9zY2FsZXMsIGRwYXIgPSAic2lnbWEiKSwgcGxvdCA9IEYpW1sxXV0gKyAKICB0aGVtZV9idygpICsgCiAgZ2VvbV9zbW9vdGgoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gIiNhNDg1MDAiLCBmaWxsID0gIiNFREM5NTEiKSArIAogIHhsYWIoIlN5bnRoZXRpYyBDcm9uYmFjaCdzIGFscGhhIikgKyAKICB5bGFiKCJQcmVkaWN0aW9uIGVycm9yIChzaWdtYSkiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0xLCAxKSwgeWxpbSA9IGMoMCwgMC4zNSkpCnBsb3RfcHJlZGljdGlvbl9lcnJvcl9hbHBoYQpgYGAKCjwvZGV0YWlscz4KCgoKCiMjIyBTY2F0dGVyIHBsb3QKYGBge3J9CmdncGxvdChzY2FsZXMsIGFlcyhzeW50aGV0aWNfYWxwaGEsIGVtcGlyaWNhbF9hbHBoYSwgCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHN0cl9kZXRlY3Qoc2NhbGUsICJecmFuZG9tIiksIAogICAgICAgICAgICAgIHltaW4gPSBlbXBpcmljYWxfYWxwaGEgLSBlbXBpcmljYWxfYWxwaGFfc2UsCiAgICAgICAgICAgICAgeW1heCA9IGVtcGlyaWNhbF9hbHBoYSArIGVtcGlyaWNhbF9hbHBoYV9zZSkpICsgCiAgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYsIHNpemUgPSAxKSArCiAgZ2VvbV9zbW9vdGgoYWVzKAogICAgeCA9IHN5bnRoZXRpY19hbHBoYSwKICAgIHkgPSBlc3RpbWF0ZV9fLAogICAgeW1pbiA9IGxvd2VyX18sCiAgICB5bWF4ID0gdXBwZXJfXywKICApLCBzdGF0ID0gImlkZW50aXR5IiwgCiAgY29sb3IgPSAiI2E0ODUwMCIsCiAgZmlsbCA9ICIjRURDOTUxIiwKICBkYXRhID0gYXMuZGF0YS5mcmFtZShwcmVkJHN5bnRoZXRpY19hbHBoYSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzAwQTBCMCIsICIjNkE0QTNDIiksCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gIm5vbmUiKSArCiAgeGxhYigiU3ludGhldGljIENyb25iYWNoJ3MgYWxwaGEiKSArIAogIHlsYWIoIkVtcGlyaWNhbCBDcm9uYmFjaCdzIGFscGhhIikgKwogIHRoZW1lX2J3KCkgKwogIGNvb3JkX2ZpeGVkKHhsaW0gPSBjKC0xLDEpLCB5bGltID0gYygtMSwxKSkgLT4gcGxvdF9yZWxzCnBsb3RfcmVscwpgYGAKCiMjIyBJbnRlcmFjdGl2ZSBwbG90CmBgYHtyfQooc2NhbGVzICU+JSAKICBmaWx0ZXIodHlwZSA9PSAicmVhbCIpICU+JSAKICBtdXRhdGUoc3ludGhldGljX2FscGhhID0gcm91bmQoc3ludGhldGljX2FscGhhLCAyKSwKICAgICAgICAgZW1waXJpY2FsX2FscGhhID0gcm91bmQoZW1waXJpY2FsX2FscGhhLCAyKSwKICAgICAgICAgc2NhbGUgPSBzdHJfcmVwbGFjZV9hbGwoc2NhbGUsICJfKyIsICIgIikpICU+JSAKZ2dwbG90KC4sIGFlcyhzeW50aGV0aWNfYWxwaGEsIGVtcGlyaWNhbF9hbHBoYSwgCiAgICAgICAgICAgICAgIyB5bWluID0gZW1waXJpY2FsX3IgLSBlbXBpcmljYWxfcl9zZSwgCiAgICAgICAgICAgICAgIyB5bWF4ID0gZW1waXJpY2FsX3IgKyBlbXBpcmljYWxfcl9zZSwgCiAgICAgICAgICAgICAgbGFiZWwgPSBzY2FsZSkpICsgCiAgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMsIHNpemUgPSAxLCBjb2xvciA9ICIjMDBBMEIwIikgKwogIHhsYWIoIlN5bnRoZXRpYyBDcm9uYmFjaCdzIGFscGhhIikgKyAKICB5bGFiKCJFbXBpcmljYWwgQ3JvbmJhY2gncyBhbHBoYSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J25vbmUnKSArIAogIGNvb3JkX2ZpeGVkKHhsaW0gPSBjKE5BLDEpLCB5bGltID0gYyhOQSwxKSkpICU+JSAKICBnZ3Bsb3RseSgpCmBgYAoKPGRldGFpbHM+PHN1bW1hcnk+PGg0PlRhYmxlPC9oND48L3N1bW1hcnk+CgpgYGB7cn0Kc2NhbGVzICU+JSAKICBmaWx0ZXIodHlwZSAhPSAicmFuZG9tIikgJT4lIAogIG11dGF0ZShlbXBpcmljYWxfYWxwaGEgPSBzcHJpbnRmKCIlLjJmwrElLjNmIiwgZW1waXJpY2FsX2FscGhhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW1waXJpY2FsX2FscGhhX3NlKSwKICAgICAgICAgc3ludGhldGljX2FscGhhID0gc3ByaW50ZigiJS4yZiIsIHN5bnRoZXRpY19hbHBoYSksCiAgICAgICAgIHNjYWxlID0gc3RyX3JlcGxhY2VfYWxsKHNjYWxlLCAiXysiLCAiICIpCiAgICAgICAgICkgJT4lIAogIHNlbGVjdChzY2FsZSwgZW1waXJpY2FsX2FscGhhLCBzeW50aGV0aWNfYWxwaGEsIG51bWJlcl9vZl9pdGVtcykgJT4lIAogIERUOjpkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGZpbHRlciA9ICJ0b3AiKQoKc2NhbGVzICU+JSB1bmdyb3VwKCkgJT4lIAogIHN1bW1hcmlzZShtZWFuKGVtcGlyaWNhbF9hbHBoYSksIHNkKGVtcGlyaWNhbF9hbHBoYSkpCnNjYWxlcyAlPiUgZ3JvdXBfYnkodHlwZSkgJT4lIAogIHN1bW1hcmlzZShtZWFuKGVtcGlyaWNhbF9hbHBoYSksIHNkKGVtcGlyaWNhbF9hbHBoYSksCiAgICAgICAgICAgIGNvciA9IGJyb29tOjp0aWR5KGNvci50ZXN0KHN5bnRoZXRpY19hbHBoYSwgZW1waXJpY2FsX2FscGhhKSksIG4oKSkKcHN5Y2hvbWV0cmljOjpjUlJyKDAuNjMyLCAwLjA5OTIsIDAuMjM1KQpgYGAKCjwvZGV0YWlscz4KCgoKPGRldGFpbHM+PHN1bW1hcnk+PGgzPlJvYnVzdG5lc3MgY2hlY2tzPC9oMz48L3N1bW1hcnk+CgojIyMjIEFjY3VyYWN5IGJ5IHdoZXRoZXIgc2NhbGVzIHdlcmUgcmVhbCBvciByYW5kb20KVGhlIFN1cnZleUJvdDMwMDAgZG9lcyBub3QgImtub3ciIHdoZXRoZXIgc2NhbGVzIHdlcmUgcHVibGlzaGVkIGluIHRoZSBsaXRlcmF0dXJlIG9yIGZvcm1lZCBhdCByYW5kb20uIEtub3dpbmcgd2hhdCB3ZSBkbyBhYm91dCB0aGUgcmVzZWFyY2ggbGl0ZXJhdHVyZSBpbiBwc3ljaG9sb2d5LCB3ZSBjYW4gaW5mZXIgdGhhdCBwdWJsaXNoZWQgc2NhbGVzIHdpbGwgdXN1YWxseSBleGNlZWQgdGhlIE51bm5hbGx5IHRocmVzaG9sZCBvZiAuNzAuIEhlbmNlLCB3ZSBrbm93IHRoYXQgdGhlIHN5bnRoZXRpYyBhbHBoYXMgZm9yIHB1Ymxpc2hlZCBzY2FsZXMgc2hvdWxkIHJhcmVseSBiZSBiZWxvdyAuNzAuIElmIHdlIHJlZ3Jlc3Mgc3ludGhldGljIGFscGhhcyBvbiBlbXBpcmljYWwgYWxwaGFzIHNlcGFyYXRlbHkgZm9yIHRoZSBzY2FsZXMgdGFrZW4gZnJvbSB0aGUgbGl0ZXJhdHVyZSwgd2Ugc2VlIHRoaXMgYXMgYSBiaWFzIChhIHBvc2l0aXZlIHJlZ3Jlc3Npb24gaW50ZXJjZXB0IG9mIC42OCBhbmQgYSBzbG9wZSDiiaAgMSwgLjI2KS4gU3RpbGwsIHRoZSBzeW50aGV0aWMgYWxwaGEgZXN0aW1hdGVzIGFyZSBwcmVkaWN0aXZlIG9mIGVtcGlyaWNhbCBhbHBoYXMgd2l0aCBhbiBhY2N1cmFjeSBvZiAuNjUuIAoKVGhlcmUgaXMgbm8gY2xlYXIgYmlhcyBmb3IgdGhlIHJhbmRvbSBzY2FsZXMuIFdoZW4gYm90aCBhcmUgYW5hbHl6ZWQgam9pbnRseSwgdGhlIGNsZWFyIHNlbGVjdGlvbiBiaWFzIGZvciB0aGUgcHVibGlzaGVkIHNjYWxlcyBpcyBtb3N0bHkgYXZlcmFnZWQgb3V0IGJ1dCBpcyByZWZsZWN0ZWQgaW4gdGhlIHNsb3BlIGV4Y2VlZGluZyAxLgoKYGBge3J9CnNjYWxlcyAlPiUgCiAgZ3JvdXBfYnkodHlwZSkgJT4lIAogIHN1bW1hcmlzZShicm9vbTo6dGlkeShjb3IudGVzdChzeW50aGV0aWNfYWxwaGEsIGVtcGlyaWNhbF9hbHBoYSkpLCBzZF9hbHBoYSA9IHNkKGVtcGlyaWNhbF9hbHBoYSksIG4gPSBuKCkpICU+JSAKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMiwgY2FwdGlvbiA9ICJBY2N1cmFjeSBzaG93biBzZXBhcmF0ZWx5IGZvciByYW5kb21seSBmb3JtZWQgYW5kIHJlYWwgc2NhbGVzIikKYGBgCgpgYGB7cn0Kc2NhbGVzICU+JSAKICBncm91cF9ieSh0eXBlKSAlPiUgCiAgc3VtbWFyaXNlKGJyb29tOjp0aWR5KGxtKGVtcGlyaWNhbF9hbHBoYSB+IHN5bnRoZXRpY19hbHBoYSkpLCBuID0gbigpKSAlPiUgCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiUmVncmVzc2lvbiBpbnRlcmNlcHRzIGFuZCBzbG9wZXMgZm9yIHJhbmRvbWx5IGZvcm1lZCBhbmQgcmVhbCBzY2FsZXMiKQpgYGAKCiMjIyMgQXMgaW4gb3VyIFN0YWdlIDEgc3VibWlzc2lvbgpIZXJlIGFyZSB0aGUgcmVzdWx0cyBpZiB3ZSBjYWxjdWxhdGUgdGhlIGFjY3VyYWN5IGFuZCBwcmVkaWN0aW9uIGVycm9yIGFzIGluIHRoZSBTdGFnZSAxIHN1Ym1pc3Npb24uIFdlIG5vdyB0aGluayB0aGlzIGFwcHJvYWNoLCBieSBjb25kaXRpb25pbmcgb24gcmFuZG9tIHZhcmlhdGlvbiBpbiB0aGUgZW1waXJpY2FsIGNvcnJlbGF0aW9ucywgZ2F2ZSBhIG1pc2xlYWRpbmcgcGljdHVyZSBvZiB0aGUgYWNjdXJhY3kgYW5kIGJpYXMgb2YgdGhlIHN5bnRoZXRpYyBDcm9uYmFjaCdzIGFscGhhcy4gSGVyZSB3ZSByZXBvcnQgdGhlIHJlc3VsdHMgaWYgd2UgY29uZHVjdCB0aGUgYW5hbHlzaXMgYXMgaW4gU3RhZ2UgMSAoYnV0IHdpdGggdGhlIGNvcnJlY3RlZCBTRSBvZiBlbXBpcmljYWwgYWxwaGFzKS4KCmBgYHtyfQpzMV9zY2FsZXMgPC0gc2NhbGVzICU+JQogIGZpbHRlcihudW1iZXJfb2ZfaXRlbXMgPiAyKSAlPiUgCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShyZXZlcnNlX2l0ZW1zID0gaWZfZWxzZSh0eXBlID09ICJyYW5kb20iLCBsaXN0KHJldmVyc2VfaXRlbXNfYnlfMXN0KSwgbGlzdChyZXZlcnNlX2l0ZW1zKSksCiAgICAgICAgIHJfcmVhbF9yZXYgPSBsaXN0KHJldmVyc2VfaXRlbXMocl9yZWFsLCByZXZlcnNlX2l0ZW1zKSksCiAgICAgICAgIHB0X3JfbGxtX3JldiA9IGxpc3QocmV2ZXJzZV9pdGVtcyhwdF9yX2xsbSwgcmV2ZXJzZV9pdGVtcykpLAogICAgICAgICByX2xsbV9yZXYgPSBsaXN0KHJldmVyc2VfaXRlbXMocl9sbG0sIHJldmVyc2VfaXRlbXMpKSkgJT4lCiAgbXV0YXRlKAogICAgcmVsX3JlYWwgPSBsaXN0KHBzeWNoOjphbHBoYShyX3JlYWxfcmV2LCBrZXlzID0gRiwgbi5vYnMgPSBOKSRmZWxkdCksCiAgICByZWxfbGxtID0gbGlzdChwc3ljaDo6YWxwaGEocl9sbG1fcmV2LCBrZXlzID0gRiwgbi5vYnMgPSBOKSRmZWxkdCksCiAgICByZWxfcHRfbGxtID0gbGlzdChwc3ljaDo6YWxwaGEocHRfcl9sbG1fcmV2LCBrZXlzID0gRiwgbi5vYnMgPSBOKSRmZWxkdCkpICU+JQogIG11dGF0ZShlbXBpcmljYWxfYWxwaGEgPSByZWxfcmVhbCRhbHBoYSRyYXdfYWxwaGEsCiAgICAgICAgIHN5bnRoZXRpY19hbHBoYSA9IHJlbF9sbG0kYWxwaGEkcmF3X2FscGhhLAogICAgICAgICBwdF9zeW50aGV0aWNfYWxwaGEgPSByZWxfcHRfbGxtJGFscGhhJHJhd19hbHBoYSkgJT4lCiAgbXV0YXRlKAogICAgZW1waXJpY2FsX2FscGhhX3NlID0gbWVhbihkaWZmKHVubGlzdChwc3ljaG9tZXRyaWM6OmFscGhhLkNJKGVtcGlyaWNhbF9hbHBoYSwgayA9IG51bWJlcl9vZl9pdGVtcywgTiA9IE4sIGxldmVsID0gMC45NSkpKSkvMS45NikgJT4lIAogICAgICBmaWx0ZXIoZW1waXJpY2FsX2FscGhhID4gMCkKCnMxX3IgPC0gYnJvb206OnRpZHkoY29yLnRlc3QoczFfc2NhbGVzJGVtcGlyaWNhbF9hbHBoYSwgczFfc2NhbGVzJHN5bnRoZXRpY19hbHBoYSkpCnMxX3B0X3IgPC0gYnJvb206OnRpZHkoY29yLnRlc3QoczFfc2NhbGVzJGVtcGlyaWNhbF9hbHBoYSwgczFfc2NhbGVzJHB0X3N5bnRoZXRpY19hbHBoYSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCmJpbmRfcm93cygKICBzMV9wdF9yICU+JSAKICAgIG11dGF0ZShtb2RlbCA9ICJwcmUtdHJhaW5lZCIsIGtpbmQgPSAibWFuaWZlc3QiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gpLAogIHMxX3IgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gImZpbmUtdHVuZWQiLCBraW5kID0gIm1hbmlmZXN0IikgJT4lIAogICAgc2VsZWN0KG1vZGVsLCBraW5kLCBhY2N1cmFjeSA9IGVzdGltYXRlLCBjb25mLmxvdywgY29uZi5oaWdoKSwKICApICU+JSAKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMiwgY2FwdGlvbiA9ICJBY2N1cmFjeSBvZiBzeW50aGV0aWMgYWxwaGFzIHdoZW4gZW1waXJpY2FsIGFscGhhcyBhcmUgYmlhc2VkIHVwd2FyZCB0aHJvdWdoIGFkYXB0aXZlIGl0ZW0gcmV2ZXJzaW9uIGFuZCBzZWxlY3Rpb24gb24gcG9zaXRpdmUgYWxwaGFzIikKYGBgCgoKYGBge3J9Cm1fbG1zeW50aF9yZWxfc2NhbGVzX3MxIDwtIGJybSgKICBiZihlbXBpcmljYWxfYWxwaGEgfCBtaShlbXBpcmljYWxfYWxwaGFfc2UpIH4gc3ludGhldGljX2FscGhhLAogICAgIHNpZ21hIH4gcG9seShzeW50aGV0aWNfYWxwaGEsIGRlZ3JlZSA9IDMpKSwgZGF0YSA9IHMxX3NjYWxlcywgCiAgaXRlciA9IDYwMDAsIGNvbnRyb2wgPSBsaXN0KGFkYXB0X2RlbHRhID0gMC45KSwKICBmaWxlID0gImlnbm9yZS9tX3N5bnRoX3JlbF9sbV9hc19zdGFnZV8xIikKCnByZWQgPC0gY29uZGl0aW9uYWxfZWZmZWN0cyhtX2xtc3ludGhfcmVsX3NjYWxlc19zMSwgbWV0aG9kID0gInByZWRpY3QiKQpnZ3Bsb3QoczFfc2NhbGVzLCBhZXMoc3ludGhldGljX2FscGhhLCBlbXBpcmljYWxfYWxwaGEsIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBzdHJfZGV0ZWN0KHNjYWxlLCAiXnJhbmRvbSIpLCAKICAgICAgICAgICAgICB5bWluID0gZW1waXJpY2FsX2FscGhhIC0gZW1waXJpY2FsX2FscGhhX3NlLAogICAgICAgICAgICAgIHltYXggPSBlbXBpcmljYWxfYWxwaGEgKyBlbXBpcmljYWxfYWxwaGFfc2UpKSArIAogIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC42LCBzaXplID0gMSkgKwogIGdlb21fc21vb3RoKGFlcygKICAgIHggPSBzeW50aGV0aWNfYWxwaGEsCiAgICB5ID0gZXN0aW1hdGVfXywKICAgIHltaW4gPSBsb3dlcl9fLAogICAgeW1heCA9IHVwcGVyX18sCiAgKSwgc3RhdCA9ICJpZGVudGl0eSIsIAogIGNvbG9yID0gIiNhNDg1MDAiLAogIGZpbGwgPSAiI0VEQzk1MSIsCiAgZGF0YSA9IGFzLmRhdGEuZnJhbWUocHJlZCRzeW50aGV0aWNfYWxwaGEpKSArICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzAwQTBCMCIsICIjNkE0QTNDIiksCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gIm5vbmUiKSArCiAgeGxhYigiU3ludGhldGljIENyb25iYWNoJ3MgYWxwaGEiKSArIAogIHlsYWIoIkVtcGlyaWNhbCBDcm9uYmFjaCdzIGFscGhhIikgKwogIHRoZW1lX2J3KCkgKwogIGNvb3JkX2ZpeGVkKHhsaW0gPSBjKC0xLCAxKSwgeWxpbSA9IGMoLTEsMSkpICAtPiBzMV9wbG90X3JlbHMKczFfcGxvdF9yZWxzCgoKbmV3ZGF0YSA8LSBtX2xtc3ludGhfcmVsX3NjYWxlc19zMSRkYXRhICU+JSBzZWxlY3QoZW1waXJpY2FsX2FscGhhLCBzeW50aGV0aWNfYWxwaGEsIGVtcGlyaWNhbF9hbHBoYV9zZSkKZXByZWRzIDwtIGVwcmVkX2RyYXdzKG5ld2RhdGEgPSBuZXdkYXRhLCBvYmogPSBtX2xtc3ludGhfcmVsX3NjYWxlc19zMSwgcmVfZm9ybXVsYSA9IE5BKQpwcmVkcyA8LSBwcmVkaWN0ZWRfZHJhd3MobmV3ZGF0YSA9IG5ld2RhdGEsIG9iaiA9IG1fbG1zeW50aF9yZWxfc2NhbGVzX3MxLCByZV9mb3JtdWxhID0gTkEpCmVwcmVkX3ByZWRzIDwtIGVwcmVkcyAlPiUgbGVmdF9qb2luKHByZWRzKQpieV9kcmF3IDwtIGVwcmVkX3ByZWRzICU+JSBncm91cF9ieSguZHJhdykgJT4lIAogIHN1bW1hcmlzZSgKICAgICAgICAgICAgIG1hZSA9IG1lYW4oYWJzKC5lcHJlZCAtIC5wcmVkaWN0aW9uKSksCiAgICAgICAgICAgIC5lcHJlZCA9IHZhciguZXByZWQpLAogICAgICAgICAgICAucHJlZGljdGlvbiA9IHZhcigucHJlZGljdGlvbiksCiAgICAgICAgICAgIHNpZ21hID0gc3FydCgucHJlZGljdGlvbiAtIC5lcHJlZCksCiAgICAgICAgICAgIGxhdGVudF9yID0gc3FydCguZXByZWQvLnByZWRpY3Rpb24pKQoKYWNjdXJhY3lfYmF5ZXNfcmVsc19wb2x5IDwtIGJ5X2RyYXcgJT4lIG1lYW5faGRjaShsYXRlbnRfcikKCnMxX3Bsb3RfcmVscwpgYGAKCmBgYHtyfQpiaW5kX3Jvd3MoCiAgciAlPiUgCiAgICBtdXRhdGUobW9kZWwgPSAiZmluZS10dW5lZCIsIGtpbmQgPSAibWFuaWZlc3QiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gpLAogIHMxX3IgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gImZpbmUtdHVuZWQsIGNvbmRpdGlvbmVkIG9uIGVtcGlyaWNhbCByIiwga2luZCA9ICJtYW5pZmVzdCIpICU+JSAKICAgIHNlbGVjdChtb2RlbCwga2luZCwgYWNjdXJhY3kgPSBlc3RpbWF0ZSwgY29uZi5sb3csIGNvbmYuaGlnaCksCiAgYWNjdXJhY3lfYmF5ZXNfcmVscyAlPiUgCiAgICBtdXRhdGUobW9kZWwgPSAiZmluZS10dW5lZCIsIGtpbmQgPSAibGF0ZW50IG91dGNvbWUgKEJheWVzaWFuIEVJVikiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gbGF0ZW50X3IsIGNvbmYubG93ID0gLmxvd2VyLCBjb25mLmhpZ2ggPSAudXBwZXIpLAogIGFjY3VyYWN5X2JheWVzX3JlbHNfcG9seSAlPiUgCiAgICBtdXRhdGUobW9kZWwgPSAiZmluZS10dW5lZCwgY29uZGl0aW9uZWQgb24gZW1waXJpY2FsIHIiLCBraW5kID0gImxhdGVudCBvdXRjb21lIChCYXllc2lhbiBFSVYpIikgJT4lIAogICAgc2VsZWN0KG1vZGVsLCBraW5kLCBhY2N1cmFjeSA9IGxhdGVudF9yLCBjb25mLmxvdyA9IC5sb3dlciwgY29uZi5oaWdoID0gLnVwcGVyKSwKICApICU+JSAKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMiwgY2FwdGlvbiA9ICJBY2N1cmFjeSBvZiBzeW50aGV0aWMgYWxwaGFzIHdoZW4gZW1waXJpY2FsIGFscGhhcyBhcmUgYmlhc2VkIHVwd2FyZCB0aHJvdWdoIGFkYXB0aXZlIGl0ZW0gcmV2ZXJzaW9uIGFuZCBzZWxlY3Rpb24gb24gcG9zaXRpdmUgYWxwaGFzIikKYGBgCgoKCiMjIyMgTnVtYmVyIG9mIGl0ZW1zIGFzIGEgdHJpdmlhbCBwcmVkaWN0b3IKCkFsdGhvdWdoIHRoZSBudW1iZXIgb2YgaXRlbXMgYWxvbmUgY2FuIG9mIGNvdXJzZSBwcmVkaWN0IENyb25iYWNoJ3MgYWxwaGEsIHRoZSBzeW50aGV0aWMgYWxwaGFzIGV4cGxhaW4gbXVjaCBtb3JlIHZhcmlhbmNlIGluIGVtcGlyaWNhbCBhbHBoYXMuCgpgYGB7cn0Kc2NhbGVzICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHN1bW1hcmlzZShicm9vbTo6dGlkeShjb3IudGVzdChudW1iZXJfb2ZfaXRlbXMsIGVtcGlyaWNhbF9hbHBoYSkpLCBzZF9hbHBoYSA9IHNkKGVtcGlyaWNhbF9hbHBoYSksIG4gPSBuKCkpICU+JSAKICBrbml0cjo6a2FibGUoKQoKCnN1bW1hcnkobG0oZW1waXJpY2FsX2FscGhhIH4gbnVtYmVyX29mX2l0ZW1zLCBzY2FsZXMpKQpzdW1tYXJ5KGxtKGVtcGlyaWNhbF9hbHBoYSB+IG51bWJlcl9vZl9pdGVtcyArIHN5bnRoZXRpY19hbHBoYSwgc2NhbGVzKSkKYGBgCgoKCklzIHRoZSBhY2N1cmFjeSBsb3dlciBmb3IgdGhlIHByZS10cmFpbmVkIG1vZGVsPwoKYGBge3J9CmdncGxvdChzY2FsZXMsIGFlcyhwdF9zeW50aGV0aWNfYWxwaGEsIGVtcGlyaWNhbF9hbHBoYSwgCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHN0cl9kZXRlY3Qoc2NhbGUsICJecmFuZG9tIiksIAogICAgICAgICAgICAgIHltaW4gPSBlbXBpcmljYWxfYWxwaGEgLSBlbXBpcmljYWxfYWxwaGFfc2UsCiAgICAgICAgICAgICAgeW1heCA9IGVtcGlyaWNhbF9hbHBoYSArIGVtcGlyaWNhbF9hbHBoYV9zZSkpICsgCiAgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYsIHNpemUgPSAxKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMwMEEwQjAiLCAiIzZBNEEzQyIpLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICJub25lIikgKwogIHhsYWIoIlN5bnRoZXRpYyBDcm9uYmFjaCdzIGFscGhhIikgKyAKICB5bGFiKCJFbXBpcmljYWwgQ3JvbmJhY2gncyBhbHBoYSIpICsKICB0aGVtZV9idygpICsKICBjb29yZF9maXhlZCh4bGltID0gYygtMSwxKSwgeWxpbSA9IGMoLTEsMSkpIC0+IHB0X3Bsb3RfcmVscwpwdF9wbG90X3JlbHMKYGBgCgoKPC9kZXRhaWxzPgoKCgoKIyMgU3ludGhldGljIFNjYWxlIENvcnJlbGF0aW9ucwpgYGB7cn0KbWFuaWZlc3Rfc2NvcmVzID0gYXJyb3c6OnJlYWRfZmVhdGhlcihmaWxlID0gZmlsZS5wYXRoKGRhdGFfcGF0aCwgZ2x1ZSgiaWdub3JlLnttb2RlbF9uYW1lfS5yYXcub3NmLWJhaW5icmlkZ2UtMjAyMS1zMi0wLnNjYWxlX2NvcnJlbGF0aW9ucy5mZWF0aGVyIikpKQpwdF9tYW5pZmVzdF9zY29yZXMgPSBhcnJvdzo6cmVhZF9mZWF0aGVyKGZpbGUgPSBmaWxlLnBhdGgoZGF0YV9wYXRoLCBnbHVlKCJpZ25vcmUue3ByZXRyYWluZWRfbW9kZWxfbmFtZX0ucmF3Lm9zZi1iYWluYnJpZGdlLTIwMjEtczItMC5zY2FsZV9jb3JyZWxhdGlvbnMuZmVhdGhlciIpKSkKCm5fZGlzdGluY3QobWFuaWZlc3Rfc2NvcmVzJHNjYWxlX2EpCgptYW5pZmVzdF9zY29yZXMgPC0gbWFuaWZlc3Rfc2NvcmVzICU+JQogbGVmdF9qb2luKHNjYWxlcywgYnkgPSBjKCJzY2FsZV9hIiA9ICJzY2FsZSIpKSAlPiUKIGxlZnRfam9pbihzY2FsZXMsIGJ5ID0gYygic2NhbGVfYiIgPSAic2NhbGUiKSkKYGBgCgojIyMgQWNjdXJhY3kKYGBge3J9CnIgPC0gYnJvb206OnRpZHkoY29yLnRlc3QobWFuaWZlc3Rfc2NvcmVzJGVtcGlyaWNhbF9yLCBtYW5pZmVzdF9zY29yZXMkc3ludGhldGljX3IpKQpwdF9yIDwtIGJyb29tOjp0aWR5KGNvci50ZXN0KHB0X21hbmlmZXN0X3Njb3JlcyRlbXBpcmljYWxfciwgcHRfbWFuaWZlc3Rfc2NvcmVzJHN5bnRoZXRpY19yKSkKCnNlMiA8LSBtZWFuKG1hbmlmZXN0X3Njb3JlcyRlbXBpcmljYWxfcl9zZV4yKQptb2RlbCA8LSBwYXN0ZTAoJwogICAgIyBMYXRlbnQgdmFyaWFibGVzCiAgICBQZWFyc29uTGF0ZW50ID1+IDEqZW1waXJpY2FsX3IKCiAgICAjIEZpeGluZyBlcnJvciB2YXJpYW5jZXMgYmFzZWQgb24ga25vd24gc3RhbmRhcmQgZXJyb3JzCiAgICBlbXBpcmljYWxfciB+fiAnLHNlMiwnKmVtcGlyaWNhbF9yCgogICAgIyBSZWxhdGlvbnNoaXAgYmV0d2VlbiBsYXRlbnQgdmFyaWFibGVzCiAgICBQZWFyc29uTGF0ZW50IH5+IHN5bnRoZXRpY19yCiAgJykKCmZpdCA8LSBzZW0obW9kZWwsIGRhdGEgPSBtYW5pZmVzdF9zY29yZXMpCnB0X2ZpdCA8LSBzZW0obW9kZWwsIGRhdGEgPSBwdF9tYW5pZmVzdF9zY29yZXMpCgoKbV9sbXN5bnRoX3Jfc2NhbGVzIDwtIGJybSgKICBiZihlbXBpcmljYWxfciB8IG1pKGVtcGlyaWNhbF9yX3NlKSB+IHN5bnRoZXRpY19yICsgKDF8bW0oc2NhbGVfYSwgc2NhbGVfYikpLAogICAgIHNpZ21hIH4gcyhzeW50aGV0aWNfcikpLCBkYXRhID0gbWFuaWZlc3Rfc2NvcmVzLCAKICBmaWxlID0gImlnbm9yZS9tX3N5bnRoX3Jfc2NhbGVzX2xtOCIpCgpzZF9zeW50aCA8LSBzZChtX2xtc3ludGhfcl9zY2FsZXMkZGF0YSRzeW50aGV0aWNfcikKCgpuZXdkYXRhIDwtIG1fbG1zeW50aF9yX3NjYWxlcyRkYXRhICU+JSBzZWxlY3QoZW1waXJpY2FsX3IsIHN5bnRoZXRpY19yLCBlbXBpcmljYWxfcl9zZSkKZXByZWRzIDwtIGVwcmVkX2RyYXdzKG5ld2RhdGEgPSBuZXdkYXRhLCBvYmogPSBtX2xtc3ludGhfcl9zY2FsZXMsIHJlX2Zvcm11bGEgPSBOQSkKcHJlZHMgPC0gcHJlZGljdGVkX2RyYXdzKG5ld2RhdGEgPSBuZXdkYXRhLCBvYmogPSBtX2xtc3ludGhfcl9zY2FsZXMsIHJlX2Zvcm11bGEgPSBOQSkKZXByZWRfcHJlZHMgPC0gZXByZWRzICU+JSBsZWZ0X2pvaW4ocHJlZHMpCmJ5X2RyYXcgPC0gZXByZWRfcHJlZHMgJT4lIGdyb3VwX2J5KC5kcmF3KSAlPiUgCiAgc3VtbWFyaXNlKC5lcHJlZCA9IHZhciguZXByZWQpLAogICAgICAgICAgICAucHJlZGljdGlvbiA9IHZhcigucHJlZGljdGlvbiksCiAgICAgICAgICAgIHNpZ21hID0gc3FydCgucHJlZGljdGlvbiAtIC5lcHJlZCksCiAgICAgICAgICAgIGxhdGVudF9yID0gc3FydCguZXByZWQvLnByZWRpY3Rpb24pKQoKYWNjdXJhY3lfYmF5ZXNfc2NhbGVzIDwtIGJ5X2RyYXcgJT4lIG1lYW5faGRjaShsYXRlbnRfcikKCmJpbmRfcm93cygKICBwdF9yICU+JSAKICAgIG11dGF0ZShtb2RlbCA9ICJwcmUtdHJhaW5lZCIsIGtpbmQgPSAibWFuaWZlc3QiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gpLAogIHN0YW5kYXJkaXplZHNvbHV0aW9uKHB0X2ZpdCkgJT4lIAogICAgZmlsdGVyKGxocyA9PSAiUGVhcnNvbkxhdGVudCIsIHJocyA9PSAgInN5bnRoZXRpY19yIikgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gInByZS10cmFpbmVkIiwga2luZCA9ICJsYXRlbnQgb3V0Y29tZSAoU0VNKSIpICU+JSAKICAgIHNlbGVjdChtb2RlbCwga2luZCwgYWNjdXJhY3kgPSBlc3Quc3RkLCAKICAgICAgICAgICBjb25mLmxvdyA9IGNpLmxvd2VyLCBjb25mLmhpZ2ggPSBjaS51cHBlciksCiAgciAlPiUgCiAgICBtdXRhdGUobW9kZWwgPSAiZmluZS10dW5lZCIsIGtpbmQgPSAibWFuaWZlc3QiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gZXN0aW1hdGUsIGNvbmYubG93LCBjb25mLmhpZ2gpLAogIHN0YW5kYXJkaXplZHNvbHV0aW9uKGZpdCkgJT4lIAogICAgZmlsdGVyKGxocyA9PSAiUGVhcnNvbkxhdGVudCIsIHJocyA9PSAgInN5bnRoZXRpY19yIikgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gImZpbmUtdHVuZWQiLCBraW5kID0gImxhdGVudCBvdXRjb21lIChTRU0pIikgJT4lIAogICAgc2VsZWN0KG1vZGVsLCBraW5kLCBhY2N1cmFjeSA9IGVzdC5zdGQsIAogICAgICAgICAgIGNvbmYubG93ID0gY2kubG93ZXIsIGNvbmYuaGlnaCA9IGNpLnVwcGVyKSwKICBhY2N1cmFjeV9iYXllc19zY2FsZXMgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gImZpbmUtdHVuZWQiLCBraW5kID0gImxhdGVudCBvdXRjb21lIChCYXllc2lhbiBFSVYpIikgJT4lIAogICAgc2VsZWN0KG1vZGVsLCBraW5kLCBhY2N1cmFjeSA9IGxhdGVudF9yLCBjb25mLmxvdyA9IC5sb3dlciwgY29uZi5oaWdoID0gLnVwcGVyKQogICkgJT4lIAogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKQpgYGAKCgo8ZGV0YWlscz48c3VtbWFyeT48aDQ+UHJlZGljdGlvbiBlcnJvciBwbG90IGFjY29yZGluZyB0byBzeW50aGV0aWMgZXN0aW1hdGU8L2g0Pjwvc3VtbWFyeT4KCmBgYHtyfQptX2xtc3ludGhfcl9zY2FsZXMKa2FibGUocm1zZV9zY2FsZXMgPC0gYnlfZHJhdyAlPiUgbWVhbl9oZGNpKHNpZ21hKSwgY2FwdGlvbiA9ICJBdmVyYWdlIHByZWRpY3Rpb24gZXJyb3IgKFJNU0UpIiwgZGlnaXRzID0gMikKCgpwcmVkIDwtIGNvbmRpdGlvbmFsX2VmZmVjdHMobV9sbXN5bnRoX3Jfc2NhbGVzLCBtZXRob2QgPSAicHJlZGljdCIpCnBsb3RfcHJlZGljdGlvbl9lcnJvcl9zY2FsZXMgPC0gcGxvdChjb25kaXRpb25hbF9lZmZlY3RzKG1fbG1zeW50aF9yX3NjYWxlcywgZHBhciA9ICJzaWdtYSIpLCBwbG90ID0gRilbWzFdXSArIAogIHRoZW1lX2J3KCkgKyAKICBnZW9tX3Ntb290aChzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiI2E0ODUwMCIsIGZpbGwgPSAiI0VEQzk1MSIpICsgCiAgeGxhYigiU3ludGhldGljIGludGVyLXNjYWxlIGNvcnJlbGF0aW9uIikgKyAKICB5bGFiKCJQcmVkaWN0aW9uIGVycm9yIChzaWdtYSkiKQpwbG90X3ByZWRpY3Rpb25fZXJyb3Jfc2NhbGVzCmBgYAoKCjwvZGV0YWlscz4KCgoKIyMjIFNjYXR0ZXIgcGxvdApgYGB7cn0KZ2dwbG90KG1hbmlmZXN0X3Njb3JlcywgYWVzKHN5bnRoZXRpY19yLCBlbXBpcmljYWxfciwgCiAgICAgICAgICAgICAgeW1pbiA9IGVtcGlyaWNhbF9yIC0gZW1waXJpY2FsX3Jfc2UsCiAgICAgICAgICAgICAgeW1heCA9IGVtcGlyaWNhbF9yICsgZW1waXJpY2FsX3Jfc2UpKSArIAogIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3BvaW50KGNvbG9yID0gIiMwMEEwQjAiLCBhbHBoYSA9IDAuMywgc2l6ZSA9IDEpICsKICBnZW9tX3Ntb290aChhZXMoCiAgICB4ID0gc3ludGhldGljX3IsCiAgICB5ID0gZXN0aW1hdGVfXywKICAgIHltaW4gPSBsb3dlcl9fLAogICAgeW1heCA9IHVwcGVyX18sCiAgKSwgc3RhdCA9ICJpZGVudGl0eSIsIAogIGNvbG9yID0gIiNhNDg1MDAiLAogIGZpbGwgPSAiI0VEQzk1MSIsCiAgZGF0YSA9IGFzLmRhdGEuZnJhbWUocHJlZCRzeW50aGV0aWNfcikpICsKICB4bGFiKCJTeW50aGV0aWMgaW50ZXItc2NhbGUgY29ycmVsYXRpb24iKSArIAogIHlsYWIoIkVtcGlyaWNhbCBpbnRlci1zY2FsZSBjb3JyZWxhdGlvbiIpICsKICB0aGVtZV9idygpICsKICBjb29yZF9maXhlZCh4bGltID0gYygtMSwxKSwgeWxpbSA9IGMoLTEsMSkpIC0+IHBsb3Rfc2NhbGVzCnBsb3Rfc2NhbGVzCmBgYAoKIyMjIEludGVyYWN0aXZlIHBsb3QKYGBge3J9CihtYW5pZmVzdF9zY29yZXMgJT4lIAogIG11dGF0ZShzeW50aGV0aWNfciA9IHJvdW5kKHN5bnRoZXRpY19yLCAyKSwKICAgICAgICAgZW1waXJpY2FsX3IgPSByb3VuZChlbXBpcmljYWxfciwgMiksCiAgICAgICAgIHNjYWxlcyA9IHN0cl9yZXBsYWNlX2FsbChzdHJfYyhzY2FsZV9hLCAiXG4iLCBzY2FsZV9iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJfKyIsICIgIikpICU+JSAKZ2dwbG90KC4sIGFlcyhzeW50aGV0aWNfciwgZW1waXJpY2FsX3IsIAogICAgICAgICAgICAgICMgeW1pbiA9IGVtcGlyaWNhbF9yIC0gZW1waXJpY2FsX3Jfc2UsIAogICAgICAgICAgICAgICMgeW1heCA9IGVtcGlyaWNhbF9yICsgZW1waXJpY2FsX3Jfc2UsIAogICAgICAgICAgICAgIGxhYmVsID0gc2NhbGVzKSkgKyAKICBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjMDBBMEIwIiwgYWxwaGEgPSAwLjMsIHNpemUgPSAxKSArCiAgeGxhYigiU3ludGhldGljIGludGVyLXNjYWxlIGNvcnJlbGF0aW9uIikgKyAKICB5bGFiKCJFbXBpcmljYWwgaW50ZXItc2NhbGUgY29ycmVsYXRpb24iKSArCiAgdGhlbWVfYncoKSArCiAgY29vcmRfZml4ZWQoeGxpbSA9IGMoLTEsMSksIHlsaW0gPSBjKC0xLDEpKSkgJT4lIAogIGdncGxvdGx5KCkKYGBgCgo8ZGV0YWlscz48c3VtbWFyeT5UYWJsZTwvc3VtbWFyeT4KCmBgYHtyfQptYW5pZmVzdF9zY29yZXMgJT4lIAogICAgICAgICAgICAgICAgbXV0YXRlKGVtcGlyaWNhbF9yID0gc3ByaW50ZigiJS4yZsKxJS4zZiIsIGVtcGlyaWNhbF9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbXBpcmljYWxfcl9zZSksCiAgICAgICAgICAgICAgICAgICAgICAgc3ludGhldGljX3IgPSBzcHJpbnRmKCIlLjJmIiwgc3ludGhldGljX3IpLAogICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX2EgPSBzdHJfcmVwbGFjZV9hbGwoc2NhbGVfYSwgIl8rIiwgIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV9iID0gc3RyX3JlcGxhY2VfYWxsKHNjYWxlX2IsICJfKyIsICIgIikKICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgIHNlbGVjdChzY2FsZV9hLCBzY2FsZV9iLCBlbXBpcmljYWxfciwgc3ludGhldGljX3IpICU+JSAKICBEVDo6ZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBmaWx0ZXIgPSAidG9wIikKYGBgCgo8L2RldGFpbHM+CgoKCgo8ZGV0YWlscz48c3VtbWFyeT48aDM+Um9idXN0bmVzcyBjaGVja3M8L2gzPjwvc3VtbWFyeT4KCgojIyMjIENvbXBhcmluZyBzcGxpbmUgYW5kIHBvbHlub21pYWwgbW9kZWxzIGZvciBoZXRlcm9zY2VkYXN0aWNpdHkKYGBge3J9Cm1fbG1zeW50aF9yX3NjYWxlc19wb2x5IDwtIGJybSgKICBiZihlbXBpcmljYWxfciB8IG1pKGVtcGlyaWNhbF9yX3NlKSB+IHN5bnRoZXRpY19yICsgKDF8bW0oc2NhbGVfYSwgc2NhbGVfYikpLAogICAgIHNpZ21hIH4gcG9seShzeW50aGV0aWNfciwgZGVncmVlID0gMykpLCBkYXRhID0gbWFuaWZlc3Rfc2NvcmVzLCAKICBmaWxlID0gImlnbm9yZS9tX3N5bnRoX3Jfc2NhbGVzX2xtX3BvbHkiKQoKbmV3ZGF0YSA8LSBtX2xtc3ludGhfcl9zY2FsZXNfcG9seSRkYXRhICU+JSBzZWxlY3QoZW1waXJpY2FsX3IsIHN5bnRoZXRpY19yLCBlbXBpcmljYWxfcl9zZSkKZXByZWRzIDwtIGVwcmVkX2RyYXdzKG5ld2RhdGEgPSBuZXdkYXRhLCBvYmogPSBtX2xtc3ludGhfcl9zY2FsZXNfcG9seSwgcmVfZm9ybXVsYSA9IE5BKQpwcmVkcyA8LSBwcmVkaWN0ZWRfZHJhd3MobmV3ZGF0YSA9IG5ld2RhdGEsIG9iaiA9IG1fbG1zeW50aF9yX3NjYWxlc19wb2x5LCByZV9mb3JtdWxhID0gTkEpCmVwcmVkX3ByZWRzIDwtIGVwcmVkcyAlPiUgbGVmdF9qb2luKHByZWRzKQpieV9kcmF3IDwtIGVwcmVkX3ByZWRzICU+JSBncm91cF9ieSguZHJhdykgJT4lIAogIHN1bW1hcmlzZSguZXByZWQgPSB2YXIoLmVwcmVkKSwKICAgICAgICAgICAgLnByZWRpY3Rpb24gPSB2YXIoLnByZWRpY3Rpb24pLAogICAgICAgICAgICBzaWdtYSA9IHNxcnQoLnByZWRpY3Rpb24gLSAuZXByZWQpLAogICAgICAgICAgICBsYXRlbnRfciA9IHNxcnQoLmVwcmVkLy5wcmVkaWN0aW9uKSkKCmFjY3VyYWN5X2JheWVzX3NjYWxlc19wb2x5IDwtIGJ5X2RyYXcgJT4lIG1lYW5faGRjaShsYXRlbnRfcikKCmJpbmRfcm93cygKICBhY2N1cmFjeV9iYXllc19zY2FsZXMgJT4lIAogICAgbXV0YXRlKG1vZGVsID0gInNwbGluZSIsIGtpbmQgPSAibGF0ZW50IG91dGNvbWUgKEJheWVzaWFuIEVJVikiKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGtpbmQsIGFjY3VyYWN5ID0gbGF0ZW50X3IsIGNvbmYubG93ID0gLmxvd2VyLCBjb25mLmhpZ2ggPSAudXBwZXIpLAogIGFjY3VyYWN5X2JheWVzX3NjYWxlc19wb2x5ICU+JSAKICAgIG11dGF0ZShtb2RlbCA9ICJwb2x5bm9taWFsIiwga2luZCA9ICJsYXRlbnQgb3V0Y29tZSAoQmF5ZXNpYW4gRUlWKSIpICU+JSAKICAgIHNlbGVjdChtb2RlbCwga2luZCwgYWNjdXJhY3kgPSBsYXRlbnRfciwgY29uZi5sb3cgPSAubG93ZXIsIGNvbmYuaGlnaCA9IC51cHBlcikKKSAlPiUgCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiQ29tcGFyaW5nIHNwbGluZSBhbmQgcG9seW5vbWlhbCBtb2RlbHMgZm9yIHNjYWxlIGNvcnJlbGF0aW9ucyIpCmBgYAoKCmBgYHtyfQpwbG90X3ByZWRpY3Rpb25fZXJyb3Jfc2NhbGVzX3BvbHkgPC0gcGxvdChjb25kaXRpb25hbF9lZmZlY3RzKG1fbG1zeW50aF9yX3NjYWxlc19wb2x5LCBkcGFyID0gInNpZ21hIiksIHBsb3QgPSBGKVtbMV1dICsgCiAgdGhlbWVfYncoKSArIAogIGdlb21fc21vb3RoKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICIjYTQ4NTAwIiwgZmlsbCA9ICIjRURDOTUxIikgKyAKICB4bGFiKCJTeW50aGV0aWMgaW50ZXItc2NhbGUgY29ycmVsYXRpb24iKSArIAogIHlsYWIoIlByZWRpY3Rpb24gZXJyb3IgKHNpZ21hKSIpCnBsb3RfcHJlZGljdGlvbl9lcnJvcl9zY2FsZXNfcG9seQpgYGAKCgojIyMjIEhvdyBkb2VzIG51bWJlciBvZiBpdGVtcyBhY3Jvc3MgdGhlIHR3byBzY2FsZXMgcmVsYXRlIHRvIGFjY3VyYWN5PwoKYGBge3J9CmJ5X2l0ZW1fbnVtYmVyIDwtIG1hbmlmZXN0X3Njb3JlcyAlPiUKICBtdXRhdGUoaXRlbXMgPSBudW1iZXJfb2ZfaXRlbXMueCArIG51bWJlcl9vZl9pdGVtcy55KSAlPiUKICBncm91cF9ieShpdGVtcykgJT4lCiAgc3VtbWFyaXNlKGJyb29tOjp0aWR5KGNvci50ZXN0KGVtcGlyaWNhbF9yLCBzeW50aGV0aWNfcikpLCBwYWlyd2lzZV9uID0gbigpKSAKCmJ5X2l0ZW1fbnVtYmVyICU+JSAKICBnZ3Bsb3QoYWVzKGl0ZW1zLCBlc3RpbWF0ZSwgeW1pbiA9IGNvbmYubG93LCB5bWF4ID0gY29uZi5oaWdoKSkgKyAKICBnZW9tX3BvaW50cmFuZ2UoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKCJNYW5pZmVzdCBhY2N1cmFjeSAod2l0aCA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCkiKSArCiAgeGxhYigiTnVtYmVyIG9mIGl0ZW1zIHN1bW1lZCBhY3Jvc3Mgc2NhbGVzIikKCmxtKGVzdGltYXRlIH4gaXRlbXMsIGJ5X2l0ZW1fbnVtYmVyLCB3ZWlnaHRzID0gMS8oYnlfaXRlbV9udW1iZXIkY29uZi5oaWdoLWJ5X2l0ZW1fbnVtYmVyJGNvbmYubG93KSkKCm1hbmlmZXN0X3Njb3JlcyAlPiUKICBmaWx0ZXIobnVtYmVyX29mX2l0ZW1zLnggPj0gMTAsIG51bWJlcl9vZl9pdGVtcy55ID49IDEwKSAlPiUKICBzdW1tYXJpc2UoY29yID0gY29yKGVtcGlyaWNhbF9yLCBzeW50aGV0aWNfciksIG4oKSkKYGBgCgoKIyMjIyBBdmVyYWdlcwpgYGB7cn0Kc2NhbGVzICU+JSBmaWx0ZXIodHlwZSA9PSAicmVhbCIpICU+JSB1bmdyb3VwKCkgJT4lIAogIGZpbHRlcihudW1iZXJfb2ZfaXRlbXMgPj0gMykgJT4lIAogIHN1bW1hcmlzZShtZWRpYW4obnVtYmVyX29mX2l0ZW1zKSwKICAgICAgICAgICAgbWVhbihudW1iZXJfb2ZfaXRlbXMpKQpgYGAKCgoKIyMjIyBJcyB0aGUgYWNjdXJhY3kgbG93ZXIgZm9yIHRoZSBwcmUtdHJhaW5lZCBtb2RlbD8KCmBgYHtyfQpnZ3Bsb3QocHRfbWFuaWZlc3Rfc2NvcmVzLCBhZXMoc3ludGhldGljX3IsIGVtcGlyaWNhbF9yLCAKICAgICAgICAgICAgICB5bWluID0gZW1waXJpY2FsX3IgLSBlbXBpcmljYWxfcl9zZSwKICAgICAgICAgICAgICB5bWF4ID0gZW1waXJpY2FsX3IgKyBlbXBpcmljYWxfcl9zZSkpICsgCiAgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiIzAwQTBCMCIsIGFscGhhID0gMC4zLCBzaXplID0gMSkgKwogIHhsYWIoIlN5bnRoZXRpYyBpbnRlci1zY2FsZSBjb3JyZWxhdGlvbiIpICsgCiAgeWxhYigiRW1waXJpY2FsIGludGVyLXNjYWxlIGNvcnJlbGF0aW9uIikgKwogIHRoZW1lX2J3KCkgKwogIGNvb3JkX2ZpeGVkKHhsaW0gPSBjKC0xLDEpLCB5bGltID0gYygtMSwxKSkgLT4gcHRfcGxvdF9zY2FsZXMKcHRfcGxvdF9zY2FsZXMKYGBgCgoKPC9kZXRhaWxzPgoKCgoKIyMgQ29tYmluZWQgcGxvdAoKYGBge3IgZmlnLndpZHRoID0gOC4zLCBmaWcuaGVpZ2h0ID0gNn0KCgpnZXRfc2NhbGVfcG9pbnQgPC0gZnVuY3Rpb24oZGF0YSwgc3ludGhldGljX2FwcHJveCwgZW1waXJpY2FsX2FwcHJveCkgewogIGRhdGEgJT4lCiAgICB1bmdyb3VwKCkgJT4lIAogICAgIyBGaW5kIGNsb3Nlc3QgcG9pbnQgdG8gdGFyZ2V0IGNvb3JkaW5hdGVzCiAgICBtdXRhdGUoZGlzdCA9IHNxcnQoKHN5bnRoZXRpY19hbHBoYSAtIHN5bnRoZXRpY19hcHByb3gpXjIgKyAKICAgICAgICAgICAgICAgICAgICAgIChlbXBpcmljYWxfYWxwaGEgLSBlbXBpcmljYWxfYXBwcm94KV4yKSkgJT4lCiAgICBhcnJhbmdlKGRpc3QpICU+JQogICAgc2xpY2UoMSkgJT4lCiAgICBzZWxlY3Qoc3ludGhldGljX2FscGhhLCBlbXBpcmljYWxfYWxwaGEpCn0KZ2V0X3NjYWxlX3BvaW50X2J5X25hbWUgPC0gZnVuY3Rpb24oZGF0YSwgbmFtZSkgewogIGRhdGEgJT4lCiAgICBmaWx0ZXIoc2NhbGUgPT0gbmFtZSkgJT4lCiAgICBzbGljZSgxKSAlPiUKICAgIHNlbGVjdChzeW50aGV0aWNfYWxwaGEsIGVtcGlyaWNhbF9hbHBoYSwgcHRfc3ludGhldGljX2FscGhhKQp9CmlwaXBfZSA8LSBnZXRfc2NhbGVfcG9pbnRfYnlfbmFtZShzY2FsZXMsICJJbnRlcm5hdGlvbmFsX1BlcnNvbmFsaXR5X0l0ZW1fUG9vbF8xMjBfZXh0cmF2ZXJzaW9uIikKbG90X28gPC0gZ2V0X3NjYWxlX3BvaW50X2J5X25hbWUoc2NhbGVzLCAiTGlmZV9PcmllbnRhdGlvbl9UZXN0X09wdGltaXNtIikKIyBnZXRfc2NhbGVfcG9pbnRfYnlfbmFtZSA8LSBmdW5jdGlvbihkYXRhLCB2YXJfMSwgdmFyXzIpIHsKIyAgIGRhdGEgJT4lCiMgICAgIGZpbHRlcih2YXJpYWJsZV8xID09IHNjYWxlX25hbWUpICU+JSAKIyAgICAgc2VsZWN0KHN5bnRoZXRpY19yLCBlbXBpcmljYWxfcikKIyB9CgpsaWJyYXJ5KHBhdGNod29yaykKcHRfcGxvdF9pdGVtczIgPC0gcHRfcGxvdF9pdGVtcyArCiAgICBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAyLjUsIHggPSAwLjUsIHkgPSAtMC44LCB2anVzdCA9IDEsIGhqdXN0ID0gMSwgbGFiZWwgPSAicihJIGZlYXIgZm9yIHRoZSB3b3JzdCxcbkkgbmV2ZXIgd29ycnkgYWJvdXQgYW55dGhpbmcpIiwgY29sb3IgPSAiIzAwQTBCMCIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAwLCB5ID0gLTAuNzgsIHhlbmQgPSAwLjI3NjE5MDYsIHllbmQgPSAtMC4zNDU5NzExLCBjb2xvciA9ICIjMDBBMEIwIiwgYWxwaGEgPSAwLjcpICsKICBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAyLjUsIHggPSAtLjEsIHkgPSAwLjUsIGhqdXN0ID0gMSwgbGFiZWwgPSAicihJIGdldCBhbmdyeSBlYXNpbHksXG5JIGxvc2UgbXkgdGVtcGVyKSIsIGNvbG9yID0gIiMwMEEwQjAiKSArIAogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IC0uMSwgeSA9IDAuNSwgeGVuZCA9IDAuNjkzNTcxMSwgeWVuZCA9IDAuNzE0MDU0NiwgY29sb3IgPSAiIzAwQTBCMCIsIGFscGhhID0gMC43KQogIAoKcGxvdF9pdGVtczIgPC0gcGxvdF9pdGVtcyArCiAgYW5ub3RhdGUoInRleHQiLCBzaXplID0gMywgeCA9IC0xLCB5ID0gMC45OCwgdmp1c3QgPSAwLCBoanVzdCA9IDAsIGxhYmVsID0gd2l0aChhY2N1cmFjeV9iYXllc19pdGVtcywgeyBzcHJpbnRmKCJhY2N1cmFjeSA9ICUuMmYgWyUuMmY7JS4yZl0iLCBsYXRlbnRfciwgLmxvd2VyLCAudXBwZXIpIH0pKSArCiAgYW5ub3RhdGUoInRleHQiLCBzaXplID0gMi41LCB4ID0gMC41LCB5ID0gLTAuOCwgdmp1c3QgPSAxLCBoanVzdCA9IDEsIGxhYmVsID0gInIoSSBmZWFyIGZvciB0aGUgd29yc3QsXG5JIG5ldmVyIHdvcnJ5IGFib3V0IGFueXRoaW5nKSIsIGNvbG9yID0gIiMwMEEwQjAiKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMCwgeSA9IC0wLjc4LCB4ZW5kID0gLTAuMTEwNDY4NiwgeWVuZCA9IC0wLjM0NTk3MTEsIGNvbG9yID0gIiMwMEEwQjAiLCBhbHBoYSA9IDAuNykgKwogIGFubm90YXRlKCJ0ZXh0Iiwgc2l6ZSA9IDIuNSwgeCA9IC0uMSwgeSA9IDAuNSwgaGp1c3QgPSAxLCBsYWJlbCA9ICJyKEkgZ2V0IGFuZ3J5IGVhc2lseSxcbkkgbG9zZSBteSB0ZW1wZXIpIiwgY29sb3IgPSAiIzAwQTBCMCIpICsgCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gLS4xLCB5ID0gMC41LCB4ZW5kID0gMC44MDMxOTc5LCB5ZW5kID0gMC43MTQwNTQ2LCBjb2xvciA9ICIjMDBBMEIwIiwgYWxwaGEgPSAwLjcpCiAgIyBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAyLjUsIHggPSAtMC41LCB5ID0gLTAuOCwgaGp1c3QgPSAwLCBsYWJlbCA9ICJyKEkgbG92ZSBsaWZlLFxuSSBhdm9pZCBjcm93ZHMpIiwgY29sb3IgPSAiIzAwQTBCMCIpICsKICAjIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IC0uMywgeSA9IC0wLjcsIHhlbmQgPSAtMC4yMywgeWVuZCA9IC0wLjI4LCBjb2xvciA9ICIjMDBBMEIwIiwgYWxwaGEgPSAwLjcpICsKICAjIGFubm90YXRlKCJ0ZXh0Iiwgc2l6ZSA9IDIuNSwgeCA9IC0uMywgeSA9IDAuNSwgaGp1c3QgPSAxLCBsYWJlbCA9ICJyKEkgd29yayBoYXJkLFxuSSBhbSBkaWxpZ2VudCkiLCBjb2xvciA9ICIjMDBBMEIwIikgKwogICMgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gLS4zLCB5ID0gLjUsIHhlbmQgPSAuNTIsIHllbmQgPSAuNTgsIGNvbG9yID0gIiMwMEEwQjAiLCBhbHBoYSA9IDAuNykKCgpwdF9wbG90X3JlbHMyIDwtIHB0X3Bsb3RfcmVscyArIAogIGFubm90YXRlKCJ0ZXh0Iiwgc2l6ZSA9IDIuNSwgeCA9IDAuMjEsIHkgPSAtMC4yLCBoanVzdCA9IDAsIGxhYmVsID0gIklQSVAgRXh0cmF2ZXJzaW9uIiwgY29sb3IgPSAiIzAwQTBCMCIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAwLjc1LCB5ID0gLTAuMiwgeGVuZCA9IDAuODcsIHllbmQgPSAwLjkwLCBjb2xvciA9ICIjMDBBMEIwIiwgYWxwaGEgPSAwLjcpICsKICBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAyLjUsIHggPSAtMC4wMiwgeSA9IDAuOTUsIGhqdXN0ID0gMCwgbGFiZWwgPSAiTE9UIE9wdGltaXNtIiwgY29sb3IgPSAiIzAwQTBCMCIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAwLjExLCB5ID0gMC45MywgeGVuZCA9IC0xLjQ3NzIyNiwgeWVuZCA9IDAuNzEsIGNvbG9yID0gIiMwMEEwQjAiLCBhbHBoYSA9IDAuNykKCnAxIDwtIGdldF9zY2FsZV9wb2ludChzY2FsZXMsIDAuMzAsIDAuMjIpCnAyIDwtIGdldF9zY2FsZV9wb2ludChzY2FsZXMsIDAuMTIsIC0wLjM2KSAKcDMgPC0gZ2V0X3NjYWxlX3BvaW50KHNjYWxlcywgLTAuNCwgLTAuOTApCgoKcGxvdF9yZWxzMiA8LSBwbG90X3JlbHMgKyAKICBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAzLCB4ID0gLTEsIHkgPSAwLjk4LCB2anVzdCA9IDAsIGhqdXN0ID0gMCwgCiAgICAgICAgICAgbGFiZWwgPSB3aXRoKGFjY3VyYWN5X2JheWVzX3JlbHMsIHsgc3ByaW50ZigiYWNjdXJhY3kgPSAlLjJmIFslLjJmOyUuMmZdIiwgbGF0ZW50X3IsIC5sb3dlciwgLnVwcGVyKSB9KSkgKwogIGFubm90YXRlKCJ0ZXh0Iiwgc2l6ZSA9IDIuNSwgeCA9IC0wLjIsIHkgPSAtMC45LCBoanVzdCA9IDAsIGxhYmVsID0gInJhbmRvbWx5IGZvcm1lZCBzY2FsZXMiLCBjb2xvciA9ICIjNkE0QTNDIikgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IDAuNCwgeSA9IC0wLjg1LCB4ZW5kID0gcDEkc3ludGhldGljX2FscGhhLCB5ZW5kID0gcDEkZW1waXJpY2FsX2FscGhhLCBjb2xvciA9ICIjNkE0QTNDIiwgYWxwaGEgPSAwLjcpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAwLjQsIHkgPSAtMC44NSwgeGVuZCA9IHAyJHN5bnRoZXRpY19hbHBoYSwgeWVuZCA9IHAyJGVtcGlyaWNhbF9hbHBoYSwgY29sb3IgPSAiIzZBNEEzQyIsIGFscGhhID0gMC43KSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMC40LCB5ID0gLTAuODUsIHhlbmQgPSBwMyRzeW50aGV0aWNfYWxwaGEsIHllbmQgPSBwMyRlbXBpcmljYWxfYWxwaGEsIGNvbG9yID0gIiM2QTRBM0MiLCBhbHBoYSA9IDAuNykgKwoKICBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAyLjUsIHggPSAwLjIxLCB5ID0gLTAuMiwgaGp1c3QgPSAwLCBsYWJlbCA9ICJJUElQIEV4dHJhdmVyc2lvbiIsIGNvbG9yID0gIiMwMEEwQjAiKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMC43NSwgeSA9IC0wLjIsIHhlbmQgPSAwLjg3LCB5ZW5kID0gMC45MCwgY29sb3IgPSAiIzAwQTBCMCIsIGFscGhhID0gMC43KSArCiAgYW5ub3RhdGUoInRleHQiLCBzaXplID0gMi41LCB4ID0gLTAuMzIsIHkgPSAwLjg2LCBoanVzdCA9IDAsIGxhYmVsID0gIkxPVCBPcHRpbWlzbSIsIGNvbG9yID0gIiMwMEEwQjAiKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMCwgeSA9IDAuODMsIHhlbmQgPSAwLjQ5LCB5ZW5kID0gMC43MSwgY29sb3IgPSAiIzAwQTBCMCIsIGFscGhhID0gMC43KQoKcHRfcGxvdF9zY2FsZXMyIDwtIHB0X3Bsb3Rfc2NhbGVzICsKICBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAyLjUsIHggPSAtMC4yLCB5ID0gMC44LCBoanVzdCA9IDEsIGxhYmVsID0gInIoQkZJIE5ldXJvdGljaXNtLFxuSVBJUCBOZXVyb3RpY2lzbSkiLCBjb2xvciA9ICIjMDBBMEIwIikgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IC0uMiwgeSA9IDAuOCwgeGVuZCA9IDAuMjIsIHllbmQgPSAuODQsIGNvbG9yID0gIiMwMEEwQjAiLCBhbHBoYSA9IDAuNykgKwogIGFubm90YXRlKCJ0ZXh0Iiwgc2l6ZSA9IDIuNSwgeCA9IC0uMSwgeSA9IC0wLjksIGhqdXN0ID0gMCwgbGFiZWwgPSAicihCRkkgRGVwcmVzc2lvbiBmYWNldCxcbkxPVCBPcHRpbWlzbSkiLCBjb2xvciA9ICIjMDBBMEIwIikgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IC0uMSwgeSA9IC0uOSwgeGVuZCA9IC0wLjE3LCB5ZW5kID0gLS42MywgY29sb3IgPSAiIzAwQTBCMCIsIGFscGhhID0gMC43KQoKCnBsb3Rfc2NhbGVzMiA8LSBwbG90X3NjYWxlcyArCiAgYW5ub3RhdGUoInRleHQiLCBzaXplID0gMywgeCA9IC0xLCB5ID0gMC45OCwgdmp1c3QgPSAwLCBoanVzdCA9IDAsIGxhYmVsID0gd2l0aChhY2N1cmFjeV9iYXllc19zY2FsZXMsIHsgc3ByaW50ZigiYWNjdXJhY3kgPSAlLjJmIFslLjJmOyUuMmZdIiwgbGF0ZW50X3IsIC5sb3dlciwgLnVwcGVyKSB9KSkgKwogIGFubm90YXRlKCJ0ZXh0Iiwgc2l6ZSA9IDIuNSwgeCA9IC0wLjEsIHkgPSAwLjUsIGhqdXN0ID0gMSwgbGFiZWwgPSAicihCRkkgTmV1cm90aWNpc20sXG5JUElQIE5ldXJvdGljaXNtKSIsIGNvbG9yID0gIiMwMEEwQjAiKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gLS4xLCB5ID0gMC41LCB4ZW5kID0gLjg0LCB5ZW5kID0gLjg0LCBjb2xvciA9ICIjMDBBMEIwIiwgYWxwaGEgPSAwLjcpICsKICBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAyLjUsIHggPSAtLjE1LCB5ID0gLTAuNywgaGp1c3QgPSAwLCBsYWJlbCA9ICJyKEJGSSBEZXByZXNzaW9uIGZhY2V0LFxuTE9UIE9wdGltaXNtKSIsIGNvbG9yID0gIiMwMEEwQjAiKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gLS4xNSwgeSA9IC0uNywgeGVuZCA9IC0uMzQsIHllbmQgPSAtLjYzLCBjb2xvciA9ICIjMDBBMEIwIiwgYWxwaGEgPSAwLjcpCgoKKHB0X3Bsb3RfaXRlbXMyICArIGdndGl0bGUoIlByZS10cmFpbmVkIG1vZGVsIGJlZm9yZSBkb21haW4gYWRhcHRhdGlvbiBhbmQgZmluZS10dW5pbmciKSsKICAgIHB0X3Bsb3RfcmVsczIgKwogICAgcHRfcGxvdF9zY2FsZXMyKSAvCgoKKHBsb3RfaXRlbXMyICsgZ2d0aXRsZSgiU3VydmV5Qm90IDMwMDAiKSArCiAgICBwbG90X3JlbHMyICArCiAgICBwbG90X3NjYWxlczIpCgpnZ3NhdmUoIkZpZ3VyZV9waWxvdC5wZGYiLCB3aWR0aCA9IDguMywgaGVpZ2h0ID0gNiwgZGV2aWNlID0gZ3JEZXZpY2VzOjpjYWlyb19wZGYpCmdnc2F2ZSgiRmlndXJlX3BpbG90LnBuZyIsIHdpZHRoID0gOC4zLCBoZWlnaHQgPSA2KQoKCgojIGdnc2F2ZSgiaWdub3JlL0ZpZ3VyZV9waWxvdC5zdmciLCB3aWR0aCA9IDguMywgaGVpZ2h0ID0gNS41LCBkZXZpY2UgPSBzdmdsaXRlOjpzdmdsaXRlKQpgYGAKCiMjIyBQcmVkaWN0aW9uIGVycm9yIHBsb3RzCmBgYHtyIGZpZy53aWR0aCA9IDguMywgZmlnLmhlaWdodCA9IDN9CmxpYnJhcnkocGF0Y2h3b3JrKQoKKHBsb3RfcHJlZGljdGlvbl9lcnJvcl9pdGVtcyArIAogICAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0xLCAxKSwgeWxpbSA9IGMoMCwgMC40KSkgKwogICAgYW5ub3RhdGUoInRleHQiLCBzaXplID0gMywgeCA9IC0xLCB5ID0gMC40LCB2anVzdCA9IDAsIGhqdXN0ID0gMCwgbGFiZWwgPSB3aXRoKHJtc2VfaXRlbXMsIHsgc3ByaW50ZigiUk1TRSA9ICUuMmYgWyUuMmY7JS4yZl0iLCBzaWdtYSwgLmxvd2VyLCAudXBwZXIpIH0pKSArCiAgICAKICAgIHBsb3RfcHJlZGljdGlvbl9lcnJvcl9hbHBoYSArIAogICAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0xLCAxKSwgeWxpbSA9IGMoMCwgMC40KSkgICsKICAgIGFubm90YXRlKCJ0ZXh0Iiwgc2l6ZSA9IDMsIHggPSAtMSwgeSA9IDAuNCwgdmp1c3QgPSAwLCBoanVzdCA9IDAsIGxhYmVsID0gd2l0aChybXNlX2FscGhhLCB7IHNwcmludGYoIlJNU0UgPSAlLjJmIFslLjJmOyUuMmZdIiwgc2lnbWEsIC5sb3dlciwgLnVwcGVyKSB9KSkgKwogICAgCiAgICBwbG90X3ByZWRpY3Rpb25fZXJyb3Jfc2NhbGVzICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTEsIDEpLCB5bGltID0gYygwLCAwLjQpKSArCiAgICBhbm5vdGF0ZSgidGV4dCIsIHNpemUgPSAzLCB4ID0gLTEsIHkgPSAwLjQsIHZqdXN0ID0gMCwgaGp1c3QgPSAwLCBsYWJlbCA9IHdpdGgocm1zZV9zY2FsZXMsIHsgc3ByaW50ZigiUk1TRSA9ICUuMmYgWyUuMmY7JS4yZl0iLCBzaWdtYSwgLmxvd2VyLCAudXBwZXIpIH0pKQopCgpnZ3NhdmUoImlnbm9yZS9GaWd1cmVfcHJlZGljdGlvbl9lcnJvcl9waWxvdC5wZGYiLCB3aWR0aCA9IDguMywgaGVpZ2h0ID0gMywgZGV2aWNlID0gZ3JEZXZpY2VzOjpjYWlyb19wZGYpCmdnc2F2ZSgiaWdub3JlL0ZpZ3VyZV9wcmVkaWN0aW9uX2Vycm9yX3BpbG90LnBuZyIsIHdpZHRoID0gOC4zLCBoZWlnaHQgPSAzKQpgYGAKCgo=