The following precision simulations all follow the same structure.

  • We randomly redraw from our holdout data to get realistic distributions of empirical estimates. SEs are estimated based on our planned N of 400.
  • We repeat this random drawing process many times.
  • We adjust for sampling error/the standard error of the empirical estimate to get the semi-latent accuracy.
  • The target quantity is the average standard error of the semi-latent accuracy with which the synthetic estimates predicts the empirical estimates. We also report the maximal standard error across simulations.

We begin with item correlations, then reliabilities, then scale correlations.

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

# Libraries and Settings

# Libs ---------------------------
library(tidyverse)
library(arrow)
library(glue)
library(psych)
library(lavaan)
library(ggplot2)
library(plotly)
library(gridExtra)
library(semTools)
library(semPlot)

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)

number_of_items <- 246
number_of_scales <- 76
number_of_scales_with_more_than_3_items <- 56
combinations_items <- choose(number_of_items, 2)
combinations_scales <- choose(number_of_scales_with_more_than_3_items, 2)
scale_subscale_pairs <- 38
combinations_scales <- combinations_scales - scale_subscale_pairs # after eliminating scale-subscale pairs
planned_N <- 400

Precision simulation for synthetic inter-item correlations

holdout <- arrow::read_feather(file = file.path(data_path, glue("data/intermediate/{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)

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

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, InstrumentA = instrument, ScaleA = scale_0, SubscaleA = scale_1))

sim_results <- tibble()
library(lavaan)

for(i in 1:500) {
  items <- holdout %>% select(variable_1) %>% distinct() %>% sample_n(number_of_items) %>% pull(variable_1)

  subset <- holdout %>% filter(variable_1 %in% items, variable_2 %in% items)

  N <- planned_N
  subset <- subset %>% mutate(se = (1 - empirical_r^2)/sqrt(N - 3))
  se2 <- mean(subset$se^2)

  r <- broom::tidy(cor.test(subset$empirical_r, subset$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 = subset)

  sim_results <- bind_rows(sim_results,
    standardizedsolution(fit) %>% filter(lhs == "PearsonLatent", rhs ==  "synthetic_r")
  )
}

sim_results %>% summarise(semi_latent_r = mean(est.std), mean_se = sqrt(mean(se^2)), max_se = max(se)) %>% 
  knitr::kable()
semi_latent_r mean_se max_se
0.707866 0.00316 0.0035834

Precision simulation for synthetic reliabilities

source("global_functions.R")
holdout_human_data = arrow::read_feather(
  file = file.path(data_path, glue("{model_name}.raw.osf-bainbridge-2021-s2-0.human.feather"))
)
holdout_human_data <- holdout_human_data %>% haven::zap_labels()

cors_llm <- holdout_llm %>%
  select(x = variable_1, y = variable_2, r = synthetic_r) %>%
  as.data.frame() |>
  igraph::graph_from_data_frame(directed = FALSE) |>
  igraph::as_adjacency_matrix(attr = "r", sparse = FALSE)
diag(cors_llm) <- 1

cors_real <- holdout_llm %>%
  select(x = variable_1, y = variable_2, r = empirical_r) %>%
  as.data.frame() |>
  igraph::graph_from_data_frame(directed = FALSE) |>
  igraph::as_adjacency_matrix(attr = "r", sparse = FALSE)
diag(cors_real) <- 1

mapping_data <- holdout_mapping_data
items_by_scale <- bind_rows(
  scales %>% filter(scale_1 == "") %>% left_join(mapping_data %>% select(-scale_1), by = c("instrument", "scale_0")),
  scales %>% filter(scale_1 != "") %>% left_join(mapping_data, by = c("instrument", "scale_0", "scale_1"))
)

real_scales <- items_by_scale %>%
  group_by(scale) %>%
  summarise(
    items = list(variable),
    number_of_items = n_distinct(variable),
    keyed = first(keyed),
    lvn = paste(first(scale), " =~ ", paste(variable, collapse = " + "))) %>%
  group_by(scale) %>%
  mutate(reverse_items = list(find_reverse_items_by_first_item(cors_real[unlist(items), unlist(items)], keyed))) %>% 
  drop_na() %>% 
  ungroup()

random_scales <- list()
for(i in 1:1000) {
  n_items <- rpois(1, mean(real_scales$number_of_items,na.rm = T))
  n_items <- if_else(n_items < 3, 3, n_items, 3)
  random_scales[[i]] <- holdout_mapping_data %>%
    sample_n(n_items) %>%
    mutate(scale = paste0("random", i)) %>%
    group_by(scale) %>%
    summarise(
      items = list(variable),
      number_of_items = n_distinct(variable),
      lvn = paste(first(scale), " =~ ", paste(variable, collapse = " + "))) %>%
    drop_na() %>% 
    mutate(keyed = 1)
}

random_scales <- bind_rows(random_scales) %>% 
  distinct(items, .keep_all = TRUE) %>% 
  rowwise() %>% 
  mutate(
    reverse_items = list(randomly_choose_items_for_reversion(items))
    ) %>% 
  ungroup()

scales <- bind_rows(real = real_scales, random = random_scales, .id = "type")
n_distinct(scales$scale)
## [1] 1113
scales <- scales %>% filter(number_of_items >= 3)
scales <- scales %>%
  rowwise() %>%
  mutate(r_llm = list(cors_llm[items, items]),
         r_real = list(cors_real[items, items]),
         N_real = holdout %>% 
           filter(variable_1 %in% items, variable_2 %in% items) %>% 
           summarise(min_n=min(pairwise_n)) %>% pull(min_n)) %>%
  mutate(
    rel_real = list(psych::alpha(holdout_human_data[, items], keys = reverse_items, n.iter = 1000)),
    rel_llm = list(psych::alpha(r_llm, keys = reverse_items, n.obs = N_real)$feldt)) %>%
  mutate(empirical_alpha = rel_real$feldt$alpha$raw_alpha,
         synthetic_alpha = rel_llm$alpha$raw_alpha) %>%
  mutate(
    alpha_se = mean(diff(unlist(psychometric::alpha.CI(empirical_alpha, k = number_of_items, N = planned_N, level = 0.95)))/1.96)
  )

realistic_scales <- scales %>% ungroup()

sim_results <- tibble()
for(i in 1:500) {
  picked_scales <- realistic_scales %>% filter(!str_detect(scale, "random")) %>% sample_n(number_of_scales_with_more_than_3_items)
  subset <-
    bind_rows(picked_scales,
              realistic_scales %>% filter(str_detect(scale, "random")) %>% sample_n(200)
  )

  se2 <- mean(subset$alpha_se^2)

  r <- broom::tidy(cor.test(subset$empirical_alpha, subset$synthetic_alpha))
  (r$conf.high - r$conf.low)/2

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

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

    # Relationship between latent variables
    PearsonLatent ~~ synthetic_alpha
  ')

  fit <- sem(model, data = subset)

  sim_results <- bind_rows(sim_results,
                           standardizedsolution(fit) %>% filter(lhs == "PearsonLatent", rhs ==  "synthetic_alpha")
  )
}
sim_results %>% summarise(semi_latent_r = mean(est.std), mean_se = sqrt(mean(se^2)), max_se = max(se)) %>% 
  knitr::kable()
semi_latent_r mean_se max_se
0.8798027 0.0153378 0.0191323

Precision simulation for 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")))


sim_results <- tibble()
library(lavaan)

for(i in 1:500) {
  scales <- manifest_scores %>% select(scale_a) %>% distinct() %>% pull(scale_a)

  subset <- manifest_scores %>% filter(scale_a %in% scales, scale_b %in% scales) %>% 
    sample_n(combinations_scales)

  N <- planned_N
  subset <- subset %>% mutate(se = (1 - empirical_r^2)/sqrt(N - 3))
  se2 <- mean(subset$se^2)

  r <- broom::tidy(cor.test(subset$empirical_r, subset$synthetic_r))
  (r$conf.high - r$conf.low)/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 = subset)

  sim_results <- bind_rows(sim_results,
                           standardizedsolution(fit) %>% filter(lhs == "PearsonLatent", rhs ==  "synthetic_r")
  )
}

sim_results %>% summarise(semi_latent_r = mean(est.std), mean_se = sqrt(mean(se^2)), max_se = max(se)) %>% 
  knitr::kable()
semi_latent_r mean_se max_se
0.8802321 0.006223 0.0070053
LS0tCnRpdGxlOiAiUHJlY2lzaW9uIHNpbXVsYXRpb25zIgphdXRob3I6ICJSdWJlbiBBcnNsYW4iCmRhdGU6ICIyMDIzLTExLTA3IgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCgpUaGUgZm9sbG93aW5nIHByZWNpc2lvbiBzaW11bGF0aW9ucyBhbGwgZm9sbG93IHRoZSBzYW1lIHN0cnVjdHVyZS4KCi0gV2UgcmFuZG9tbHkgcmVkcmF3IGZyb20gb3VyIGhvbGRvdXQgZGF0YSB0byBnZXQgcmVhbGlzdGljIGRpc3RyaWJ1dGlvbnMKICBvZiBlbXBpcmljYWwgZXN0aW1hdGVzLiBTRXMgYXJlIGVzdGltYXRlZCBiYXNlZCBvbiBvdXIgcGxhbm5lZCBOIG9mIDQwMC4KLSBXZSByZXBlYXQgdGhpcyByYW5kb20gZHJhd2luZyBwcm9jZXNzIG1hbnkgdGltZXMuCi0gV2UgYWRqdXN0IGZvciBzYW1wbGluZyBlcnJvci90aGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIGVtcGlyaWNhbCBlc3RpbWF0ZSB0byBnZXQgdGhlIHNlbWktbGF0ZW50IGFjY3VyYWN5LgotIFRoZSB0YXJnZXQgcXVhbnRpdHkgaXMgdGhlIGF2ZXJhZ2Ugc3RhbmRhcmQgZXJyb3Igb2YgdGhlIHNlbWktbGF0ZW50IGFjY3VyYWN5IHdpdGggd2hpY2ggdGhlIHN5bnRoZXRpYyBlc3RpbWF0ZXMgcHJlZGljdHMgdGhlIGVtcGlyaWNhbCBlc3RpbWF0ZXMuIFdlIGFsc28gcmVwb3J0IHRoZSBtYXhpbWFsIHN0YW5kYXJkIGVycm9yIGFjcm9zcyBzaW11bGF0aW9ucy4KCldlIGJlZ2luIHdpdGggaXRlbSBjb3JyZWxhdGlvbnMsIHRoZW4gcmVsaWFiaWxpdGllcywgdGhlbiBzY2FsZSBjb3JyZWxhdGlvbnMuCgpgYGB7ciB3YXJuaW5nPUYsbWVzc2FnZT1GfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGVycm9yID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGKQoKIyBMaWJyYXJpZXMgYW5kIFNldHRpbmdzCgojIExpYnMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGFycm93KQpsaWJyYXJ5KGdsdWUpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkobGF2YWFuKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShzZW1Ub29scykKbGlicmFyeShzZW1QbG90KQoKbW9kZWxfbmFtZSA9ICJJdGVtU2ltaWxhcml0eVRyYWluaW5nLTIwMjQwNTAyLXRyaWFsMTIiCiNtb2RlbF9uYW1lID0gIml0ZW0tc2ltaWxhcml0eS0yMDIzMTAxOC0xMjI1MDQiCnByZXRyYWluZWRfbW9kZWxfbmFtZSA9ICJhbGwtbXBuZXQtYmFzZS12MiIKCmRhdGFfcGF0aCA9IGdsdWUoIi4vIikKcHJldHJhaW5lZF9kYXRhX3BhdGggPSBnbHVlKCIuLyIpCgpzZXQuc2VlZCg0MikKCm51bWJlcl9vZl9pdGVtcyA8LSAyNDYKbnVtYmVyX29mX3NjYWxlcyA8LSA3NgpudW1iZXJfb2Zfc2NhbGVzX3dpdGhfbW9yZV90aGFuXzNfaXRlbXMgPC0gNTYKY29tYmluYXRpb25zX2l0ZW1zIDwtIGNob29zZShudW1iZXJfb2ZfaXRlbXMsIDIpCmNvbWJpbmF0aW9uc19zY2FsZXMgPC0gY2hvb3NlKG51bWJlcl9vZl9zY2FsZXNfd2l0aF9tb3JlX3RoYW5fM19pdGVtcywgMikKc2NhbGVfc3Vic2NhbGVfcGFpcnMgPC0gMzgKY29tYmluYXRpb25zX3NjYWxlcyA8LSBjb21iaW5hdGlvbnNfc2NhbGVzIC0gc2NhbGVfc3Vic2NhbGVfcGFpcnMgIyBhZnRlciBlbGltaW5hdGluZyBzY2FsZS1zdWJzY2FsZSBwYWlycwpwbGFubmVkX04gPC0gNDAwCmBgYAoKCiMjIFByZWNpc2lvbiBzaW11bGF0aW9uIGZvciBzeW50aGV0aWMgaW50ZXItaXRlbSBjb3JyZWxhdGlvbnMKYGBge3J9CmhvbGRvdXQgPC0gYXJyb3c6OnJlYWRfZmVhdGhlcihmaWxlID0gZmlsZS5wYXRoKGRhdGFfcGF0aCwgZ2x1ZSgiaWdub3JlLnttb2RlbF9uYW1lfS5yYXcub3NmLWJhaW5icmlkZ2UtMjAyMS1zMi0wLml0ZW1fY29ycmVsYXRpb25zLmZlYXRoZXIiKSkpCgpob2xkb3V0X21hcHBpbmdfZGF0YSA9IGFycm93OjpyZWFkX2ZlYXRoZXIoCiAgZmlsZSA9IGZpbGUucGF0aChkYXRhX3BhdGgsIGdsdWUoInttb2RlbF9uYW1lfS5yYXcub3NmLWJhaW5icmlkZ2UtMjAyMS1zMi0wLm1hcHBpbmcyLmZlYXRoZXIiKSkKKSAlPiUKICByZW5hbWUoc2NhbGVfMCA9IHNjYWxlMCwKICAgICAgICAgc2NhbGVfMSA9IHNjYWxlMSkKCnNjYWxlcyA8LSBhcnJvdzo6cmVhZF9mZWF0aGVyKGZpbGUucGF0aChkYXRhX3BhdGgsIGdsdWUoInttb2RlbF9uYW1lfS5yYXcub3NmLWJhaW5icmlkZ2UtMjAyMS1zMi0wLnNjYWxlcy5mZWF0aGVyIikpCikKCmhvbGRvdXRfbGxtIDwtIGhvbGRvdXQgJT4lCiAgbGVmdF9qb2luKGhvbGRvdXRfbWFwcGluZ19kYXRhICU+JSBzZWxlY3QodmFyaWFibGVfMSA9IHZhcmlhYmxlLCBJbnN0cnVtZW50QSA9IGluc3RydW1lbnQsIFNjYWxlQSA9IHNjYWxlXzAsIFN1YnNjYWxlQSA9IHNjYWxlXzEpKSAlPiUKICBsZWZ0X2pvaW4oaG9sZG91dF9tYXBwaW5nX2RhdGEgJT4lIHNlbGVjdCh2YXJpYWJsZV8yID0gdmFyaWFibGUsIEluc3RydW1lbnRBID0gaW5zdHJ1bWVudCwgU2NhbGVBID0gc2NhbGVfMCwgU3Vic2NhbGVBID0gc2NhbGVfMSkpCgpzaW1fcmVzdWx0cyA8LSB0aWJibGUoKQpsaWJyYXJ5KGxhdmFhbikKCmZvcihpIGluIDE6NTAwKSB7CiAgaXRlbXMgPC0gaG9sZG91dCAlPiUgc2VsZWN0KHZhcmlhYmxlXzEpICU+JSBkaXN0aW5jdCgpICU+JSBzYW1wbGVfbihudW1iZXJfb2ZfaXRlbXMpICU+JSBwdWxsKHZhcmlhYmxlXzEpCgogIHN1YnNldCA8LSBob2xkb3V0ICU+JSBmaWx0ZXIodmFyaWFibGVfMSAlaW4lIGl0ZW1zLCB2YXJpYWJsZV8yICVpbiUgaXRlbXMpCgogIE4gPC0gcGxhbm5lZF9OCiAgc3Vic2V0IDwtIHN1YnNldCAlPiUgbXV0YXRlKHNlID0gKDEgLSBlbXBpcmljYWxfcl4yKS9zcXJ0KE4gLSAzKSkKICBzZTIgPC0gbWVhbihzdWJzZXQkc2VeMikKCiAgciA8LSBicm9vbTo6dGlkeShjb3IudGVzdChzdWJzZXQkZW1waXJpY2FsX3IsIHN1YnNldCRzeW50aGV0aWNfcikpCgogIG1vZGVsIDwtIHBhc3RlMCgnCiAgICAjIExhdGVudCB2YXJpYWJsZXMKICAgIFBlYXJzb25MYXRlbnQgPX4gMSplbXBpcmljYWxfcgoKICAgICMgRml4aW5nIGVycm9yIHZhcmlhbmNlcyBiYXNlZCBvbiBrbm93biBzdGFuZGFyZCBlcnJvcnMKICAgIGVtcGlyaWNhbF9yIH5+ICcsc2UyLCcqZW1waXJpY2FsX3IKCiAgICAjIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIGxhdGVudCB2YXJpYWJsZXMKICAgIFBlYXJzb25MYXRlbnQgfn4gc3ludGhldGljX3IKICAnKQoKICBmaXQgPC0gc2VtKG1vZGVsLCBkYXRhID0gc3Vic2V0KQoKICBzaW1fcmVzdWx0cyA8LSBiaW5kX3Jvd3Moc2ltX3Jlc3VsdHMsCiAgICBzdGFuZGFyZGl6ZWRzb2x1dGlvbihmaXQpICU+JSBmaWx0ZXIobGhzID09ICJQZWFyc29uTGF0ZW50IiwgcmhzID09ICAic3ludGhldGljX3IiKQogICkKfQoKc2ltX3Jlc3VsdHMgJT4lIHN1bW1hcmlzZShzZW1pX2xhdGVudF9yID0gbWVhbihlc3Quc3RkKSwgbWVhbl9zZSA9IHNxcnQobWVhbihzZV4yKSksIG1heF9zZSA9IG1heChzZSkpICU+JSAKICBrbml0cjo6a2FibGUoKQpgYGAKCgojIyBQcmVjaXNpb24gc2ltdWxhdGlvbiBmb3Igc3ludGhldGljIHJlbGlhYmlsaXRpZXMKYGBge3J9CnNvdXJjZSgiZ2xvYmFsX2Z1bmN0aW9ucy5SIikKaG9sZG91dF9odW1hbl9kYXRhID0gYXJyb3c6OnJlYWRfZmVhdGhlcigKICBmaWxlID0gZmlsZS5wYXRoKGRhdGFfcGF0aCwgZ2x1ZSgie21vZGVsX25hbWV9LnJhdy5vc2YtYmFpbmJyaWRnZS0yMDIxLXMyLTAuaHVtYW4uZmVhdGhlciIpKQopCmhvbGRvdXRfaHVtYW5fZGF0YSA8LSBob2xkb3V0X2h1bWFuX2RhdGEgJT4lIGhhdmVuOjp6YXBfbGFiZWxzKCkKCmNvcnNfbGxtIDwtIGhvbGRvdXRfbGxtICU+JQogIHNlbGVjdCh4ID0gdmFyaWFibGVfMSwgeSA9IHZhcmlhYmxlXzIsIHIgPSBzeW50aGV0aWNfcikgJT4lCiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgaWdyYXBoOjpncmFwaF9mcm9tX2RhdGFfZnJhbWUoZGlyZWN0ZWQgPSBGQUxTRSkgfD4KICBpZ3JhcGg6OmFzX2FkamFjZW5jeV9tYXRyaXgoYXR0ciA9ICJyIiwgc3BhcnNlID0gRkFMU0UpCmRpYWcoY29yc19sbG0pIDwtIDEKCmNvcnNfcmVhbCA8LSBob2xkb3V0X2xsbSAlPiUKICBzZWxlY3QoeCA9IHZhcmlhYmxlXzEsIHkgPSB2YXJpYWJsZV8yLCByID0gZW1waXJpY2FsX3IpICU+JQogIGFzLmRhdGEuZnJhbWUoKSB8PgogIGlncmFwaDo6Z3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGRpcmVjdGVkID0gRkFMU0UpIHw+CiAgaWdyYXBoOjphc19hZGphY2VuY3lfbWF0cml4KGF0dHIgPSAiciIsIHNwYXJzZSA9IEZBTFNFKQpkaWFnKGNvcnNfcmVhbCkgPC0gMQoKbWFwcGluZ19kYXRhIDwtIGhvbGRvdXRfbWFwcGluZ19kYXRhCml0ZW1zX2J5X3NjYWxlIDwtIGJpbmRfcm93cygKICBzY2FsZXMgJT4lIGZpbHRlcihzY2FsZV8xID09ICIiKSAlPiUgbGVmdF9qb2luKG1hcHBpbmdfZGF0YSAlPiUgc2VsZWN0KC1zY2FsZV8xKSwgYnkgPSBjKCJpbnN0cnVtZW50IiwgInNjYWxlXzAiKSksCiAgc2NhbGVzICU+JSBmaWx0ZXIoc2NhbGVfMSAhPSAiIikgJT4lIGxlZnRfam9pbihtYXBwaW5nX2RhdGEsIGJ5ID0gYygiaW5zdHJ1bWVudCIsICJzY2FsZV8wIiwgInNjYWxlXzEiKSkKKQoKcmVhbF9zY2FsZXMgPC0gaXRlbXNfYnlfc2NhbGUgJT4lCiAgZ3JvdXBfYnkoc2NhbGUpICU+JQogIHN1bW1hcmlzZSgKICAgIGl0ZW1zID0gbGlzdCh2YXJpYWJsZSksCiAgICBudW1iZXJfb2ZfaXRlbXMgPSBuX2Rpc3RpbmN0KHZhcmlhYmxlKSwKICAgIGtleWVkID0gZmlyc3Qoa2V5ZWQpLAogICAgbHZuID0gcGFzdGUoZmlyc3Qoc2NhbGUpLCAiID1+ICIsIHBhc3RlKHZhcmlhYmxlLCBjb2xsYXBzZSA9ICIgKyAiKSkpICU+JQogIGdyb3VwX2J5KHNjYWxlKSAlPiUKICBtdXRhdGUocmV2ZXJzZV9pdGVtcyA9IGxpc3QoZmluZF9yZXZlcnNlX2l0ZW1zX2J5X2ZpcnN0X2l0ZW0oY29yc19yZWFsW3VubGlzdChpdGVtcyksIHVubGlzdChpdGVtcyldLCBrZXllZCkpKSAlPiUgCiAgZHJvcF9uYSgpICU+JSAKICB1bmdyb3VwKCkKCnJhbmRvbV9zY2FsZXMgPC0gbGlzdCgpCmZvcihpIGluIDE6MTAwMCkgewogIG5faXRlbXMgPC0gcnBvaXMoMSwgbWVhbihyZWFsX3NjYWxlcyRudW1iZXJfb2ZfaXRlbXMsbmEucm0gPSBUKSkKICBuX2l0ZW1zIDwtIGlmX2Vsc2Uobl9pdGVtcyA8IDMsIDMsIG5faXRlbXMsIDMpCiAgcmFuZG9tX3NjYWxlc1tbaV1dIDwtIGhvbGRvdXRfbWFwcGluZ19kYXRhICU+JQogICAgc2FtcGxlX24obl9pdGVtcykgJT4lCiAgICBtdXRhdGUoc2NhbGUgPSBwYXN0ZTAoInJhbmRvbSIsIGkpKSAlPiUKICAgIGdyb3VwX2J5KHNjYWxlKSAlPiUKICAgIHN1bW1hcmlzZSgKICAgICAgaXRlbXMgPSBsaXN0KHZhcmlhYmxlKSwKICAgICAgbnVtYmVyX29mX2l0ZW1zID0gbl9kaXN0aW5jdCh2YXJpYWJsZSksCiAgICAgIGx2biA9IHBhc3RlKGZpcnN0KHNjYWxlKSwgIiA9fiAiLCBwYXN0ZSh2YXJpYWJsZSwgY29sbGFwc2UgPSAiICsgIikpKSAlPiUKICAgIGRyb3BfbmEoKSAlPiUgCiAgICBtdXRhdGUoa2V5ZWQgPSAxKQp9CgpyYW5kb21fc2NhbGVzIDwtIGJpbmRfcm93cyhyYW5kb21fc2NhbGVzKSAlPiUgCiAgZGlzdGluY3QoaXRlbXMsIC5rZWVwX2FsbCA9IFRSVUUpICU+JSAKICByb3d3aXNlKCkgJT4lIAogIG11dGF0ZSgKICAgIHJldmVyc2VfaXRlbXMgPSBsaXN0KHJhbmRvbWx5X2Nob29zZV9pdGVtc19mb3JfcmV2ZXJzaW9uKGl0ZW1zKSkKICAgICkgJT4lIAogIHVuZ3JvdXAoKQoKc2NhbGVzIDwtIGJpbmRfcm93cyhyZWFsID0gcmVhbF9zY2FsZXMsIHJhbmRvbSA9IHJhbmRvbV9zY2FsZXMsIC5pZCA9ICJ0eXBlIikKbl9kaXN0aW5jdChzY2FsZXMkc2NhbGUpCgpzY2FsZXMgPC0gc2NhbGVzICU+JSBmaWx0ZXIobnVtYmVyX29mX2l0ZW1zID49IDMpCnNjYWxlcyA8LSBzY2FsZXMgJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShyX2xsbSA9IGxpc3QoY29yc19sbG1baXRlbXMsIGl0ZW1zXSksCiAgICAgICAgIHJfcmVhbCA9IGxpc3QoY29yc19yZWFsW2l0ZW1zLCBpdGVtc10pLAogICAgICAgICBOX3JlYWwgPSBob2xkb3V0ICU+JSAKICAgICAgICAgICBmaWx0ZXIodmFyaWFibGVfMSAlaW4lIGl0ZW1zLCB2YXJpYWJsZV8yICVpbiUgaXRlbXMpICU+JSAKICAgICAgICAgICBzdW1tYXJpc2UobWluX249bWluKHBhaXJ3aXNlX24pKSAlPiUgcHVsbChtaW5fbikpICU+JQogIG11dGF0ZSgKICAgIHJlbF9yZWFsID0gbGlzdChwc3ljaDo6YWxwaGEoaG9sZG91dF9odW1hbl9kYXRhWywgaXRlbXNdLCBrZXlzID0gcmV2ZXJzZV9pdGVtcywgbi5pdGVyID0gMTAwMCkpLAogICAgcmVsX2xsbSA9IGxpc3QocHN5Y2g6OmFscGhhKHJfbGxtLCBrZXlzID0gcmV2ZXJzZV9pdGVtcywgbi5vYnMgPSBOX3JlYWwpJGZlbGR0KSkgJT4lCiAgbXV0YXRlKGVtcGlyaWNhbF9hbHBoYSA9IHJlbF9yZWFsJGZlbGR0JGFscGhhJHJhd19hbHBoYSwKICAgICAgICAgc3ludGhldGljX2FscGhhID0gcmVsX2xsbSRhbHBoYSRyYXdfYWxwaGEpICU+JQogIG11dGF0ZSgKICAgIGFscGhhX3NlID0gbWVhbihkaWZmKHVubGlzdChwc3ljaG9tZXRyaWM6OmFscGhhLkNJKGVtcGlyaWNhbF9hbHBoYSwgayA9IG51bWJlcl9vZl9pdGVtcywgTiA9IHBsYW5uZWRfTiwgbGV2ZWwgPSAwLjk1KSkpLzEuOTYpCiAgKQoKcmVhbGlzdGljX3NjYWxlcyA8LSBzY2FsZXMgJT4lIHVuZ3JvdXAoKQoKc2ltX3Jlc3VsdHMgPC0gdGliYmxlKCkKZm9yKGkgaW4gMTo1MDApIHsKICBwaWNrZWRfc2NhbGVzIDwtIHJlYWxpc3RpY19zY2FsZXMgJT4lIGZpbHRlcighc3RyX2RldGVjdChzY2FsZSwgInJhbmRvbSIpKSAlPiUgc2FtcGxlX24obnVtYmVyX29mX3NjYWxlc193aXRoX21vcmVfdGhhbl8zX2l0ZW1zKQogIHN1YnNldCA8LQogICAgYmluZF9yb3dzKHBpY2tlZF9zY2FsZXMsCiAgICAgICAgICAgICAgcmVhbGlzdGljX3NjYWxlcyAlPiUgZmlsdGVyKHN0cl9kZXRlY3Qoc2NhbGUsICJyYW5kb20iKSkgJT4lIHNhbXBsZV9uKDIwMCkKICApCgogIHNlMiA8LSBtZWFuKHN1YnNldCRhbHBoYV9zZV4yKQoKICByIDwtIGJyb29tOjp0aWR5KGNvci50ZXN0KHN1YnNldCRlbXBpcmljYWxfYWxwaGEsIHN1YnNldCRzeW50aGV0aWNfYWxwaGEpKQogIChyJGNvbmYuaGlnaCAtIHIkY29uZi5sb3cpLzIKCiAgbW9kZWwgPC0gcGFzdGUwKCcKICAgICMgTGF0ZW50IHZhcmlhYmxlcwogICAgUGVhcnNvbkxhdGVudCA9fiAxKmVtcGlyaWNhbF9hbHBoYQoKICAgICMgRml4aW5nIGVycm9yIHZhcmlhbmNlcyBiYXNlZCBvbiBrbm93biBzdGFuZGFyZCBlcnJvcnMKICAgIGVtcGlyaWNhbF9hbHBoYSB+fiAnLHNlMiwnKmVtcGlyaWNhbF9hbHBoYQoKICAgICMgUmVsYXRpb25zaGlwIGJldHdlZW4gbGF0ZW50IHZhcmlhYmxlcwogICAgUGVhcnNvbkxhdGVudCB+fiBzeW50aGV0aWNfYWxwaGEKICAnKQoKICBmaXQgPC0gc2VtKG1vZGVsLCBkYXRhID0gc3Vic2V0KQoKICBzaW1fcmVzdWx0cyA8LSBiaW5kX3Jvd3Moc2ltX3Jlc3VsdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplZHNvbHV0aW9uKGZpdCkgJT4lIGZpbHRlcihsaHMgPT0gIlBlYXJzb25MYXRlbnQiLCByaHMgPT0gICJzeW50aGV0aWNfYWxwaGEiKQogICkKfQpzaW1fcmVzdWx0cyAlPiUgc3VtbWFyaXNlKHNlbWlfbGF0ZW50X3IgPSBtZWFuKGVzdC5zdGQpLCBtZWFuX3NlID0gc3FydChtZWFuKHNlXjIpKSwgbWF4X3NlID0gbWF4KHNlKSkgJT4lIAogIGtuaXRyOjprYWJsZSgpCmBgYAoKCiMjIFByZWNpc2lvbiBzaW11bGF0aW9uIGZvciBzeW50aGV0aWMgc2NhbGUgY29ycmVsYXRpb25zCmBgYHtyfQptYW5pZmVzdF9zY29yZXMgPSBhcnJvdzo6cmVhZF9mZWF0aGVyKGZpbGUgPSBmaWxlLnBhdGgoZGF0YV9wYXRoLCBnbHVlKCJpZ25vcmUue21vZGVsX25hbWV9LnJhdy5vc2YtYmFpbmJyaWRnZS0yMDIxLXMyLTAuc2NhbGVfY29ycmVsYXRpb25zLmZlYXRoZXIiKSkpCgoKc2ltX3Jlc3VsdHMgPC0gdGliYmxlKCkKbGlicmFyeShsYXZhYW4pCgpmb3IoaSBpbiAxOjUwMCkgewogIHNjYWxlcyA8LSBtYW5pZmVzdF9zY29yZXMgJT4lIHNlbGVjdChzY2FsZV9hKSAlPiUgZGlzdGluY3QoKSAlPiUgcHVsbChzY2FsZV9hKQoKICBzdWJzZXQgPC0gbWFuaWZlc3Rfc2NvcmVzICU+JSBmaWx0ZXIoc2NhbGVfYSAlaW4lIHNjYWxlcywgc2NhbGVfYiAlaW4lIHNjYWxlcykgJT4lIAogICAgc2FtcGxlX24oY29tYmluYXRpb25zX3NjYWxlcykKCiAgTiA8LSBwbGFubmVkX04KICBzdWJzZXQgPC0gc3Vic2V0ICU+JSBtdXRhdGUoc2UgPSAoMSAtIGVtcGlyaWNhbF9yXjIpL3NxcnQoTiAtIDMpKQogIHNlMiA8LSBtZWFuKHN1YnNldCRzZV4yKQoKICByIDwtIGJyb29tOjp0aWR5KGNvci50ZXN0KHN1YnNldCRlbXBpcmljYWxfciwgc3Vic2V0JHN5bnRoZXRpY19yKSkKICAociRjb25mLmhpZ2ggLSByJGNvbmYubG93KS8yCgogIG1vZGVsIDwtIHBhc3RlMCgnCiAgICAjIExhdGVudCB2YXJpYWJsZXMKICAgIFBlYXJzb25MYXRlbnQgPX4gMSplbXBpcmljYWxfcgoKICAgICMgRml4aW5nIGVycm9yIHZhcmlhbmNlcyBiYXNlZCBvbiBrbm93biBzdGFuZGFyZCBlcnJvcnMKICAgIGVtcGlyaWNhbF9yIH5+ICcsc2UyLCcqZW1waXJpY2FsX3IKCiAgICAjIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIGxhdGVudCB2YXJpYWJsZXMKICAgIFBlYXJzb25MYXRlbnQgfn4gc3ludGhldGljX3IKICAnKQoKICBmaXQgPC0gc2VtKG1vZGVsLCBkYXRhID0gc3Vic2V0KQoKICBzaW1fcmVzdWx0cyA8LSBiaW5kX3Jvd3Moc2ltX3Jlc3VsdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplZHNvbHV0aW9uKGZpdCkgJT4lIGZpbHRlcihsaHMgPT0gIlBlYXJzb25MYXRlbnQiLCByaHMgPT0gICJzeW50aGV0aWNfciIpCiAgKQp9CgpzaW1fcmVzdWx0cyAlPiUgc3VtbWFyaXNlKHNlbWlfbGF0ZW50X3IgPSBtZWFuKGVzdC5zdGQpLCBtZWFuX3NlID0gc3FydChtZWFuKHNlXjIpKSwgbWF4X3NlID0gbWF4KHNlKSkgJT4lIAogIGtuaXRyOjprYWJsZSgpCmBgYAo=