library(tidyverse)
library(targets)
library(broom)
library(broom.mixed)
library(tidybayes)
library(glue)
library(brms)
library(scales)
library(kableExtra)
library(modelsummary)
library(lubridate)
library(here)

# Generated via random.org
set.seed(9936)

# Load models
withr::with_dir(here::here(), {
  source(tar_read(plot_funs))
  
  # Load big list of models
  tar_load(model_df)
  
  # Load actual model objects
  # This works, but doesn't put the models in the dependency network for some reason
  # tar_load(model_df$model)
  
  # This also works but again doesn't register correctly as dependencies
  # tar_load(starts_with("m_pts"))
  
  # So we do it this manual way
  tar_load(c(m_pts_baseline, m_pts_total, m_pts_total_new, 
             m_pts_advocacy, m_pts_entry, m_pts_funding, 
             m_pts_v2csreprss, 
             m_lhr_baseline, m_lhr_total, m_lhr_total_new, 
             m_lhr_advocacy, m_lhr_entry, m_lhr_funding, 
             m_lhr_v2csreprss, 
             m_pts_baseline_train, m_pts_total_train, 
             m_pts_advocacy_train, m_pts_entry_train, m_pts_funding_train, 
             m_pts_v2csreprss_train,
             m_lhr_baseline_train, m_lhr_total_train, m_lhr_total_new_train, 
             m_lhr_advocacy_train, m_lhr_entry_train, m_lhr_funding_train, 
             m_lhr_v2csreprss_train))
})
computer <- "Personal"

if (computer == "Work") {
  computer_details <- "2019 MacBook Pro with 16 cores and 32 GB of RAM, using Stan through brms through cmdstanr"
} else {
  computer_details <- "2021 MacBook Pro M1 Max with 10 cores and 32 GB of RAM, using Stan through brms through cmdstanr"
}

We ran these models on a 2021 MacBook Pro M1 Max with 10 cores and 32 GB of RAM, using Stan through brms through cmdstanr.

 

Model run times

Models for E1 (de jure civil society laws)

Full data

models_e1 <- model_df %>% 
  filter(!str_detect(model, "v2csreprss")) %>% 
  mutate(actual_model = model %>% map(~eval(rlang::sym(.)))) %>% 
  mutate(across(c(outcome_var, explan_var, family), ~fct_inorder(., ordered = TRUE)))

model_time_e1 <- models_e1 %>% 
  filter(training == "Full data") %>% 
  mutate(duration = map(actual_model, ~rstan::get_elapsed_time(.$fit)),
         duration = map(duration, ~rownames_to_column(as_tibble(.)))) %>% 
  select(-actual_model) %>% 
  unnest(duration) %>% 
  mutate(model = glue("<code>{model}</code>")) %>% 
  group_by(Model = model, Outcome = outcome_var, 
           `Main predictor` = explan_var, `Family` = family) %>% 
  summarize(`Total time (i.e. longest chain)` = as.duration(max(warmup + sample))) %>%
  ungroup() %>% 
  arrange(Outcome, `Main predictor`)

total_row_e1 <- tibble(Outcome = "Total", 
                       `Total time (i.e. longest chain)` = 
                         as.duration(sum(model_time_e1$`Total time (i.e. longest chain)`)))

model_time_e1 <- model_time_e1 %>% 
  bind_rows(total_row_e1)

model_time_e1 %>% 
  select(-Outcome) %>% 
  kbl(escape = FALSE) %>% 
  pack_rows(index = table(fct_inorder(model_time_e1$Outcome))) %>% 
  kable_styling()
Model Main predictor Family Total time (i.e. longest chain)
Political terror
m_pts_baseline Baseline Ordered logit 80.659s (~1.34 minutes)
m_pts_total Total legal barriers Ordered logit 81.274s (~1.35 minutes)
m_pts_total_new New legal barriers Ordered logit 72.786s (~1.21 minutes)
m_pts_advocacy Barriers to advocacy Ordered logit 80.014s (~1.33 minutes)
m_pts_entry Barriers to entry Ordered logit 76.606s (~1.28 minutes)
m_pts_funding Barriers to funding Ordered logit 77.1s (~1.29 minutes)
Latent human rights
m_lhr_baseline Baseline OLS 13.227s
m_lhr_total Total legal barriers OLS 13.997s
m_lhr_total_new New legal barriers OLS 13.137s
m_lhr_advocacy Barriers to advocacy OLS 15.639s
m_lhr_entry Barriers to entry OLS 13.714s
m_lhr_funding Barriers to funding OLS 14.51s
Total
552.663s (~9.21 minutes)

Training data

model_time_e1 <- models_e1 %>% 
  filter(training == "Training") %>% 
  mutate(duration = map(actual_model, ~rstan::get_elapsed_time(.$fit)),
         duration = map(duration, ~rownames_to_column(as_tibble(.)))) %>% 
  select(-actual_model) %>% 
  unnest(duration) %>% 
  mutate(model = glue("<code>{model}</code>")) %>% 
  group_by(Model = model, Outcome = outcome_var, 
           `Main predictor` = explan_var, `Family` = family) %>% 
  summarize(`Total time (i.e. longest chain)` = as.duration(max(warmup + sample))) %>%
  ungroup() %>% 
  arrange(Outcome, `Main predictor`)

total_row_e1 <- tibble(Outcome = "Total", 
                       `Total time (i.e. longest chain)` = 
                         as.duration(sum(model_time_e1$`Total time (i.e. longest chain)`)))

model_time_e1 <- model_time_e1 %>% 
  bind_rows(total_row_e1)

model_time_e1 %>% 
  select(-Outcome) %>% 
  kbl(escape = FALSE) %>% 
  pack_rows(index = table(fct_inorder(model_time_e1$Outcome))) %>% 
  kable_styling()
Model Main predictor Family Total time (i.e. longest chain)
Political terror
m_pts_baseline_train Baseline Ordered logit 69.16s (~1.15 minutes)
m_pts_total_train Total legal barriers Ordered logit 66.243s (~1.1 minutes)
m_pts_advocacy_train Barriers to advocacy Ordered logit 71.552s (~1.19 minutes)
m_pts_entry_train Barriers to entry Ordered logit 66.969s (~1.12 minutes)
m_pts_funding_train Barriers to funding Ordered logit 67.204s (~1.12 minutes)
Latent human rights
m_lhr_baseline_train Baseline OLS 11.832s
m_lhr_total_train Total legal barriers OLS 12.77s
m_lhr_total_new_train New legal barriers OLS 12.13s
m_lhr_advocacy_train Barriers to advocacy OLS 15.035s
m_lhr_entry_train Barriers to entry OLS 14.105s
m_lhr_funding_train Barriers to funding OLS 13.531s
Total
420.531s (~7.01 minutes)

Models for E2 (de facto civil society environment)

Full data

models_e2 <- model_df %>% 
  filter(str_detect(model, "baseline") | str_detect(model, "v2csreprss")) %>% 
  mutate(actual_model = model %>% map(~eval(rlang::sym(.)))) %>% 
  mutate(across(c(outcome_var, explan_var, family), ~fct_inorder(., ordered = TRUE)))

model_time_e2 <- models_e2 %>% 
  filter(training == "Full data") %>% 
  mutate(duration = map(actual_model, ~rstan::get_elapsed_time(.$fit)),
         duration = map(duration, ~rownames_to_column(as_tibble(.)))) %>% 
  select(-actual_model) %>% 
  unnest(duration) %>% 
  mutate(model = glue("<code>{model}</code>")) %>% 
  group_by(Model = model, Outcome = outcome_var, 
           `Main predictor` = explan_var, `Family` = family) %>% 
  summarize(`Total time (i.e. longest chain)` = as.duration(max(warmup + sample))) %>%
  ungroup() %>% 
  arrange(Outcome, `Main predictor`)

total_row_e2 <- tibble(Outcome = "Total", 
                       `Total time (i.e. longest chain)` = 
                         as.duration(sum(model_time_e2$`Total time (i.e. longest chain)`)))

model_time_e2 <- model_time_e2 %>% 
  bind_rows(total_row_e2)

model_time_e2 %>% 
  select(-Outcome) %>% 
  kbl(escape = FALSE) %>% 
  pack_rows(index = table(fct_inorder(model_time_e2$Outcome))) %>% 
  kable_styling()
Model Main predictor Family Total time (i.e. longest chain)
Political terror
m_pts_baseline Baseline Ordered logit 80.659s (~1.34 minutes)
m_pts_v2csreprss Civil society repression Ordered logit 85.602s (~1.43 minutes)
Latent human rights
m_lhr_baseline Baseline OLS 13.227s
m_lhr_v2csreprss Civil society repression OLS 14.233s
Total
193.721s (~3.23 minutes)

Training data

model_time_e2 <- models_e2 %>% 
  filter(training == "Training") %>% 
  mutate(duration = map(actual_model, ~rstan::get_elapsed_time(.$fit)),
         duration = map(duration, ~rownames_to_column(as_tibble(.)))) %>% 
  select(-actual_model) %>% 
  unnest(duration) %>% 
  mutate(model = glue("<code>{model}</code>")) %>% 
  group_by(Model = model, Outcome = outcome_var, 
           `Main predictor` = explan_var, `Family` = family) %>% 
  summarize(`Total time (i.e. longest chain)` = as.duration(max(warmup + sample))) %>%
  ungroup() %>% 
  arrange(Outcome, `Main predictor`)

total_row_e2 <- tibble(Outcome = "Total", 
                       `Total time (i.e. longest chain)` = 
                         as.duration(sum(model_time_e2$`Total time (i.e. longest chain)`)))

model_time_e2 <- model_time_e2 %>% 
  bind_rows(total_row_e2)

model_time_e2 %>% 
  select(-Outcome) %>% 
  kbl(escape = FALSE) %>% 
  pack_rows(index = table(fct_inorder(model_time_e2$Outcome))) %>% 
  kable_styling()
Model Main predictor Family Total time (i.e. longest chain)
Political terror
m_pts_baseline_train Baseline Ordered logit 69.16s (~1.15 minutes)
m_pts_v2csreprss_train Civil society repression Ordered logit 74.994s (~1.25 minutes)
Latent human rights
m_lhr_baseline_train Baseline OLS 11.832s
m_lhr_v2csreprss_train Civil society repression OLS 16.914s
Total
172.9s (~2.88 minutes)

 

Actual code

All the models are run with a targets pipeline with dataset-specific functions that live in these files:

  • R/models_pts.R (for PTS-based models)
  • R/models_lhr.R (for latent human rights-based models)

To keep the analysis relatively self contained here in the analysis notebook, and to make it so there’s no need to hunt through the GitHub repository to find the actual code, here’s that code:

R/models_pts.R

# Settings ----------------------------------------------------------------

# Run this inside each model function instead of outside so that future workers
# use these options internally
pts_setup <- function() {
  options(worker_options)

  # Settings
  CHAINS <- 4
  ITER <- 2000
  WARMUP <- 1000
  BAYES_SEED <- 2009  # From random.org
  threads <- getOption("mc.threads")

  # Priors
  priors_vague <- c(set_prior("normal(0, 3)", class = "Intercept"),
                    set_prior("normal(0, 3)", class = "b"),
                    set_prior("cauchy(0, 1)", class = "sd"))

  return(list(chains = CHAINS, iter = ITER, warmup = WARMUP, seed = BAYES_SEED,
              threads = threads, priors_vague = priors_vague))
}


# Regular models ----------------------------------------------------------

f_pts_baseline <- function(dat) {
  pts_settings <- pts_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(PTS_factor_lead1 ~ PTS_factor +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = cumulative(),
    prior = pts_settings$priors_vague,
    data = dat,
    threads = threading(pts_settings$threads),
    chains = pts_settings$chains, iter = pts_settings$iter,
    warmup = pts_settings$warmup, seed = pts_settings$seed)

  return(model)
}

f_pts_total <- function(dat) {
  pts_settings <- pts_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(PTS_factor_lead1 ~ barriers_total + barriers_total_lag1 +
         PTS_factor +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = cumulative(),
    prior = pts_settings$priors_vague,
    data = dat,
    threads = threading(pts_settings$threads),
    chains = pts_settings$chains, iter = pts_settings$iter,
    warmup = pts_settings$warmup, seed = pts_settings$seed)

  return(model)
}

f_pts_total_new <- function(dat) {
  pts_settings <- pts_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(PTS_factor_lead1 ~ barriers_total_new + barriers_total_new_lag1 +
         PTS_factor +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = cumulative(),
    prior = pts_settings$priors_vague,
    data = dat,
    threads = threading(pts_settings$threads),
    chains = pts_settings$chains, iter = pts_settings$iter,
    warmup = pts_settings$warmup, seed = pts_settings$seed)

  return(model)
}

f_pts_advocacy <- function(dat) {
  pts_settings <- pts_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(PTS_factor_lead1 ~ advocacy + advocacy_lag1 +
         PTS_factor +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = cumulative(),
    prior = pts_settings$priors_vague,
    data = dat,
    threads = threading(pts_settings$threads),
    chains = pts_settings$chains, iter = pts_settings$iter,
    warmup = pts_settings$warmup, seed = pts_settings$seed)

  return(model)
}

f_pts_entry <- function(dat) {
  pts_settings <- pts_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(PTS_factor_lead1 ~ entry + entry_lag1 +
         PTS_factor +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = cumulative(),
    prior = pts_settings$priors_vague,
    data = dat,
    threads = threading(pts_settings$threads),
    chains = pts_settings$chains, iter = pts_settings$iter,
    warmup = pts_settings$warmup, seed = pts_settings$seed)

  return(model)
}

f_pts_funding <- function(dat) {
  pts_settings <- pts_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(PTS_factor_lead1 ~ funding + funding_lag1 +
         PTS_factor +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = cumulative(),
    prior = pts_settings$priors_vague,
    data = dat,
    threads = threading(pts_settings$threads),
    chains = pts_settings$chains, iter = pts_settings$iter,
    warmup = pts_settings$warmup, seed = pts_settings$seed)

  return(model)
}

f_pts_v2csreprss <- function(dat) {
  pts_settings <- pts_setup()

  model <- brm(
    bf(PTS_factor_lead1 ~ v2csreprss + v2csreprss_lag1 +
         PTS_factor +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = cumulative(),
    prior = pts_settings$priors_vague,
    data = dat,
    threads = threading(pts_settings$threads),
    chains = pts_settings$chains, iter = pts_settings$iter,
    warmup = pts_settings$warmup, seed = pts_settings$seed)

  return(model)
}

R/models_lhr.R

# Settings ----------------------------------------------------------------

lhr_setup <- function() {
  options(worker_options)

  # Settings
  CHAINS <- 4
  ITER <- 2000
  WARMUP <- 1000
  BAYES_SEED <- 4045  # From random.org
  threads <- getOption("mc.threads")

  # Priors
  priors_vague <- c(set_prior("normal(0, 10)", class = "Intercept"),
                    set_prior("normal(0, 3)", class = "b"),
                    set_prior("cauchy(0, 1)", class = "sd"))

  return(list(chains = CHAINS, iter = ITER, warmup = WARMUP, seed = BAYES_SEED,
              threads = threads, priors_vague = priors_vague))
}


# Regular models ----------------------------------------------------------

f_lhr_baseline <- function(dat) {
  lhr_settings <- lhr_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(latent_hr_mean_lead1 ~ latent_hr_mean +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = gaussian(),
    prior = lhr_settings$priors_vague,
    control = list(adapt_delta = 0.9),
    data = dat,
    threads = threading(lhr_settings$threads),
    chains = lhr_settings$chains, iter = lhr_settings$iter,
    warmup = lhr_settings$warmup, seed = lhr_settings$seed)

  return(model)
}

f_lhr_total <- function(dat) {
  lhr_settings <- lhr_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(latent_hr_mean_lead1 ~ barriers_total + barriers_total_lag1 +
         latent_hr_mean +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = gaussian(),
    prior = lhr_settings$priors_vague,
    control = list(adapt_delta = 0.9),
    data = dat,
    threads = threading(lhr_settings$threads),
    chains = lhr_settings$chains, iter = lhr_settings$iter,
    warmup = lhr_settings$warmup, seed = lhr_settings$seed)

  return(model)
}

f_lhr_total_new <- function(dat) {
  lhr_settings <- lhr_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(latent_hr_mean_lead1 ~ barriers_total_new + barriers_total_new_lag1 +
         latent_hr_mean +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = gaussian(),
    prior = lhr_settings$priors_vague,
    control = list(adapt_delta = 0.9),
    data = dat,
    threads = threading(lhr_settings$threads),
    chains = lhr_settings$chains, iter = lhr_settings$iter,
    warmup = lhr_settings$warmup, seed = lhr_settings$seed)

  return(model)
}

f_lhr_advocacy <- function(dat) {
  lhr_settings <- lhr_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(latent_hr_mean_lead1 ~ advocacy + advocacy_lag1 +
         latent_hr_mean +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = gaussian(),
    prior = lhr_settings$priors_vague,
    control = list(adapt_delta = 0.9),
    data = dat,
    threads = threading(lhr_settings$threads),
    chains = lhr_settings$chains, iter = lhr_settings$iter,
    warmup = lhr_settings$warmup, seed = lhr_settings$seed)

  return(model)
}

f_lhr_entry <- function(dat) {
  lhr_settings <- lhr_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(latent_hr_mean_lead1 ~ entry + entry_lag1 +
         latent_hr_mean +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = gaussian(),
    prior = lhr_settings$priors_vague,
    control = list(adapt_delta = 0.9),
    data = dat,
    threads = threading(lhr_settings$threads),
    chains = lhr_settings$chains, iter = lhr_settings$iter,
    warmup = lhr_settings$warmup, seed = lhr_settings$seed)

  return(model)
}

f_lhr_funding <- function(dat) {
  lhr_settings <- lhr_setup()

  dat <- dat %>% filter(laws)

  model <- brm(
    bf(latent_hr_mean_lead1 ~ funding + funding_lag1 +
         latent_hr_mean +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = gaussian(),
    prior = lhr_settings$priors_vague,
    control = list(adapt_delta = 0.9),
    data = dat,
    threads = threading(lhr_settings$threads),
    chains = lhr_settings$chains, iter = lhr_settings$iter,
    warmup = lhr_settings$warmup, seed = lhr_settings$seed)

  return(model)
}

f_lhr_v2csreprss <- function(dat) {
  lhr_settings <- lhr_setup()

  model <- brm(
    bf(latent_hr_mean_lead1 ~ v2csreprss + v2csreprss_lag1 +
         latent_hr_mean +
         v2x_polyarchy +
         gdpcap_log +
         un_trade_pct_gdp +
         armed_conflict +
         (1 | gwcode)
    ),
    family = gaussian(),
    prior = lhr_settings$priors_vague,
    control = list(adapt_delta = 0.9),
    data = dat,
    threads = threading(lhr_settings$threads),
    chains = lhr_settings$chains, iter = lhr_settings$iter,
    warmup = lhr_settings$warmup, seed = lhr_settings$seed)

  return(model)
}
LS0tCnRpdGxlOiAiTW9kZWwgZGV0YWlscyBhbmQgZGlhZ25vc3RpY3MiCmF1dGhvcjogIlN1cGFybmEgQ2hhdWRocnkgYW5kIEFuZHJldyBIZWlzcyIKZGF0ZTogIkxhc3QgcnVuOiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVGJylgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKa25pdF9wcmludC5kYXRhLmZyYW1lIDwtIGZ1bmN0aW9uKHgsIC4uLikgewogIHJlcyA8LSBwYXN0ZShjKCcnLCAnJywga2FibGVfc3R5bGluZyhrYWJsZSh4LCBib29rdGFicyA9IFRSVUUpKSksIGNvbGxhcHNlID0gJ1xuJykKICBhc2lzX291dHB1dChyZXMpCn0KCnJlZ2lzdGVyUzNtZXRob2QoImtuaXRfcHJpbnQiLCAiZGF0YS5mcmFtZSIsIGtuaXRfcHJpbnQuZGF0YS5mcmFtZSkKcmVnaXN0ZXJTM21ldGhvZCgia25pdF9wcmludCIsICJncm91cGVkX2RmIiwga25pdF9wcmludC5kYXRhLmZyYW1lKQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5yZXRpbmEgPSAzLAogICAgICAgICAgICAgICAgICAgICAgdGlkeS5vcHRzID0gbGlzdCh3aWR0aC5jdXRvZmYgPSAxMjApLCAgIyBGb3IgY29kZQogICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucyh3aWR0aCA9IDkwKSwgICMgRm9yIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgZmlnLmFzcCA9IDAuNjE4LCBmaWcud2lkdGggPSA3LCAKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBvdXQud2lkdGggPSAiODUlIikKCm9wdGlvbnMoZHBseXIuc3VtbWFyaXNlLmluZm9ybSA9IEZBTFNFLAogICAgICAgIGtuaXRyLmthYmxlLk5BID0gIiIpCmBgYAoKYGBge3IgbG9hZC1saWJyYXJpZXMtZGF0YSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGFyZ2V0cykKbGlicmFyeShicm9vbSkKbGlicmFyeShicm9vbS5taXhlZCkKbGlicmFyeSh0aWR5YmF5ZXMpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeShicm1zKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KG1vZGVsc3VtbWFyeSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoaGVyZSkKCiMgR2VuZXJhdGVkIHZpYSByYW5kb20ub3JnCnNldC5zZWVkKDk5MzYpCgojIExvYWQgbW9kZWxzCndpdGhyOjp3aXRoX2RpcihoZXJlOjpoZXJlKCksIHsKICBzb3VyY2UodGFyX3JlYWQocGxvdF9mdW5zKSkKICAKICAjIExvYWQgYmlnIGxpc3Qgb2YgbW9kZWxzCiAgdGFyX2xvYWQobW9kZWxfZGYpCiAgCiAgIyBMb2FkIGFjdHVhbCBtb2RlbCBvYmplY3RzCiAgIyBUaGlzIHdvcmtzLCBidXQgZG9lc24ndCBwdXQgdGhlIG1vZGVscyBpbiB0aGUgZGVwZW5kZW5jeSBuZXR3b3JrIGZvciBzb21lIHJlYXNvbgogICMgdGFyX2xvYWQobW9kZWxfZGYkbW9kZWwpCiAgCiAgIyBUaGlzIGFsc28gd29ya3MgYnV0IGFnYWluIGRvZXNuJ3QgcmVnaXN0ZXIgY29ycmVjdGx5IGFzIGRlcGVuZGVuY2llcwogICMgdGFyX2xvYWQoc3RhcnRzX3dpdGgoIm1fcHRzIikpCiAgCiAgIyBTbyB3ZSBkbyBpdCB0aGlzIG1hbnVhbCB3YXkKICB0YXJfbG9hZChjKG1fcHRzX2Jhc2VsaW5lLCBtX3B0c190b3RhbCwgbV9wdHNfdG90YWxfbmV3LCAKICAgICAgICAgICAgIG1fcHRzX2Fkdm9jYWN5LCBtX3B0c19lbnRyeSwgbV9wdHNfZnVuZGluZywgCiAgICAgICAgICAgICBtX3B0c192MmNzcmVwcnNzLCAKICAgICAgICAgICAgIG1fbGhyX2Jhc2VsaW5lLCBtX2xocl90b3RhbCwgbV9saHJfdG90YWxfbmV3LCAKICAgICAgICAgICAgIG1fbGhyX2Fkdm9jYWN5LCBtX2xocl9lbnRyeSwgbV9saHJfZnVuZGluZywgCiAgICAgICAgICAgICBtX2xocl92MmNzcmVwcnNzLCAKICAgICAgICAgICAgIG1fcHRzX2Jhc2VsaW5lX3RyYWluLCBtX3B0c190b3RhbF90cmFpbiwgCiAgICAgICAgICAgICBtX3B0c19hZHZvY2FjeV90cmFpbiwgbV9wdHNfZW50cnlfdHJhaW4sIG1fcHRzX2Z1bmRpbmdfdHJhaW4sIAogICAgICAgICAgICAgbV9wdHNfdjJjc3JlcHJzc190cmFpbiwKICAgICAgICAgICAgIG1fbGhyX2Jhc2VsaW5lX3RyYWluLCBtX2xocl90b3RhbF90cmFpbiwgbV9saHJfdG90YWxfbmV3X3RyYWluLCAKICAgICAgICAgICAgIG1fbGhyX2Fkdm9jYWN5X3RyYWluLCBtX2xocl9lbnRyeV90cmFpbiwgbV9saHJfZnVuZGluZ190cmFpbiwgCiAgICAgICAgICAgICBtX2xocl92MmNzcmVwcnNzX3RyYWluKSkKfSkKYGBgCgpgYGB7ciBzZXQtY29tcHV0ZXItZGV0YWlsc30KY29tcHV0ZXIgPC0gIlBlcnNvbmFsIgoKaWYgKGNvbXB1dGVyID09ICJXb3JrIikgewogIGNvbXB1dGVyX2RldGFpbHMgPC0gIjIwMTkgTWFjQm9vayBQcm8gd2l0aCAxNiBjb3JlcyBhbmQgMzIgR0Igb2YgUkFNLCB1c2luZyBTdGFuIHRocm91Z2ggYnJtcyB0aHJvdWdoIGNtZHN0YW5yIgp9IGVsc2UgewogIGNvbXB1dGVyX2RldGFpbHMgPC0gIjIwMjEgTWFjQm9vayBQcm8gTTEgTWF4IHdpdGggMTAgY29yZXMgYW5kIDMyIEdCIG9mIFJBTSwgdXNpbmcgU3RhbiB0aHJvdWdoIGJybXMgdGhyb3VnaCBjbWRzdGFuciIKfQpgYGAKCldlIHJhbiB0aGVzZSBtb2RlbHMgb24gYSBgciBjb21wdXRlcl9kZXRhaWxzYC4KClwgCgojIE1vZGVsIHJ1biB0aW1lcwoKIyMgTW9kZWxzIGZvciBFfjF+IChkZSBqdXJlIGNpdmlsIHNvY2lldHkgbGF3cykKCiMjIyBGdWxsIGRhdGEKCmBgYHtyIHJ1bm5pbmctdGltZS1lMX0KbW9kZWxzX2UxIDwtIG1vZGVsX2RmICU+JSAKICBmaWx0ZXIoIXN0cl9kZXRlY3QobW9kZWwsICJ2MmNzcmVwcnNzIikpICU+JSAKICBtdXRhdGUoYWN0dWFsX21vZGVsID0gbW9kZWwgJT4lIG1hcCh+ZXZhbChybGFuZzo6c3ltKC4pKSkpICU+JSAKICBtdXRhdGUoYWNyb3NzKGMob3V0Y29tZV92YXIsIGV4cGxhbl92YXIsIGZhbWlseSksIH5mY3RfaW5vcmRlciguLCBvcmRlcmVkID0gVFJVRSkpKQoKbW9kZWxfdGltZV9lMSA8LSBtb2RlbHNfZTEgJT4lIAogIGZpbHRlcih0cmFpbmluZyA9PSAiRnVsbCBkYXRhIikgJT4lIAogIG11dGF0ZShkdXJhdGlvbiA9IG1hcChhY3R1YWxfbW9kZWwsIH5yc3Rhbjo6Z2V0X2VsYXBzZWRfdGltZSguJGZpdCkpLAogICAgICAgICBkdXJhdGlvbiA9IG1hcChkdXJhdGlvbiwgfnJvd25hbWVzX3RvX2NvbHVtbihhc190aWJibGUoLikpKSkgJT4lIAogIHNlbGVjdCgtYWN0dWFsX21vZGVsKSAlPiUgCiAgdW5uZXN0KGR1cmF0aW9uKSAlPiUgCiAgbXV0YXRlKG1vZGVsID0gZ2x1ZSgiPGNvZGU+e21vZGVsfTwvY29kZT4iKSkgJT4lIAogIGdyb3VwX2J5KE1vZGVsID0gbW9kZWwsIE91dGNvbWUgPSBvdXRjb21lX3ZhciwgCiAgICAgICAgICAgYE1haW4gcHJlZGljdG9yYCA9IGV4cGxhbl92YXIsIGBGYW1pbHlgID0gZmFtaWx5KSAlPiUgCiAgc3VtbWFyaXplKGBUb3RhbCB0aW1lIChpLmUuIGxvbmdlc3QgY2hhaW4pYCA9IGFzLmR1cmF0aW9uKG1heCh3YXJtdXAgKyBzYW1wbGUpKSkgJT4lCiAgdW5ncm91cCgpICU+JSAKICBhcnJhbmdlKE91dGNvbWUsIGBNYWluIHByZWRpY3RvcmApCgp0b3RhbF9yb3dfZTEgPC0gdGliYmxlKE91dGNvbWUgPSAiVG90YWwiLCAKICAgICAgICAgICAgICAgICAgICAgICBgVG90YWwgdGltZSAoaS5lLiBsb25nZXN0IGNoYWluKWAgPSAKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmR1cmF0aW9uKHN1bShtb2RlbF90aW1lX2UxJGBUb3RhbCB0aW1lIChpLmUuIGxvbmdlc3QgY2hhaW4pYCkpKQoKbW9kZWxfdGltZV9lMSA8LSBtb2RlbF90aW1lX2UxICU+JSAKICBiaW5kX3Jvd3ModG90YWxfcm93X2UxKQoKbW9kZWxfdGltZV9lMSAlPiUgCiAgc2VsZWN0KC1PdXRjb21lKSAlPiUgCiAga2JsKGVzY2FwZSA9IEZBTFNFKSAlPiUgCiAgcGFja19yb3dzKGluZGV4ID0gdGFibGUoZmN0X2lub3JkZXIobW9kZWxfdGltZV9lMSRPdXRjb21lKSkpICU+JSAKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgojIyMgVHJhaW5pbmcgZGF0YQoKYGBge3IgcnVubmluZy10aW1lLWUxLXRyYWluaW5nfQptb2RlbF90aW1lX2UxIDwtIG1vZGVsc19lMSAlPiUgCiAgZmlsdGVyKHRyYWluaW5nID09ICJUcmFpbmluZyIpICU+JSAKICBtdXRhdGUoZHVyYXRpb24gPSBtYXAoYWN0dWFsX21vZGVsLCB+cnN0YW46OmdldF9lbGFwc2VkX3RpbWUoLiRmaXQpKSwKICAgICAgICAgZHVyYXRpb24gPSBtYXAoZHVyYXRpb24sIH5yb3duYW1lc190b19jb2x1bW4oYXNfdGliYmxlKC4pKSkpICU+JSAKICBzZWxlY3QoLWFjdHVhbF9tb2RlbCkgJT4lIAogIHVubmVzdChkdXJhdGlvbikgJT4lIAogIG11dGF0ZShtb2RlbCA9IGdsdWUoIjxjb2RlPnttb2RlbH08L2NvZGU+IikpICU+JSAKICBncm91cF9ieShNb2RlbCA9IG1vZGVsLCBPdXRjb21lID0gb3V0Y29tZV92YXIsIAogICAgICAgICAgIGBNYWluIHByZWRpY3RvcmAgPSBleHBsYW5fdmFyLCBgRmFtaWx5YCA9IGZhbWlseSkgJT4lIAogIHN1bW1hcml6ZShgVG90YWwgdGltZSAoaS5lLiBsb25nZXN0IGNoYWluKWAgPSBhcy5kdXJhdGlvbihtYXgod2FybXVwICsgc2FtcGxlKSkpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgYXJyYW5nZShPdXRjb21lLCBgTWFpbiBwcmVkaWN0b3JgKQoKdG90YWxfcm93X2UxIDwtIHRpYmJsZShPdXRjb21lID0gIlRvdGFsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgYFRvdGFsIHRpbWUgKGkuZS4gbG9uZ2VzdCBjaGFpbilgID0gCiAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kdXJhdGlvbihzdW0obW9kZWxfdGltZV9lMSRgVG90YWwgdGltZSAoaS5lLiBsb25nZXN0IGNoYWluKWApKSkKCm1vZGVsX3RpbWVfZTEgPC0gbW9kZWxfdGltZV9lMSAlPiUgCiAgYmluZF9yb3dzKHRvdGFsX3Jvd19lMSkKCm1vZGVsX3RpbWVfZTEgJT4lIAogIHNlbGVjdCgtT3V0Y29tZSkgJT4lIAogIGtibChlc2NhcGUgPSBGQUxTRSkgJT4lIAogIHBhY2tfcm93cyhpbmRleCA9IHRhYmxlKGZjdF9pbm9yZGVyKG1vZGVsX3RpbWVfZTEkT3V0Y29tZSkpKSAlPiUgCiAga2FibGVfc3R5bGluZygpCmBgYAoKCiMjIE1vZGVscyBmb3IgRX4yfiAoZGUgZmFjdG8gY2l2aWwgc29jaWV0eSBlbnZpcm9ubWVudCkKCiMjIyBGdWxsIGRhdGEKCmBgYHtyIHJ1bm5pbmctdGltZS1lMn0KbW9kZWxzX2UyIDwtIG1vZGVsX2RmICU+JSAKICBmaWx0ZXIoc3RyX2RldGVjdChtb2RlbCwgImJhc2VsaW5lIikgfCBzdHJfZGV0ZWN0KG1vZGVsLCAidjJjc3JlcHJzcyIpKSAlPiUgCiAgbXV0YXRlKGFjdHVhbF9tb2RlbCA9IG1vZGVsICU+JSBtYXAofmV2YWwocmxhbmc6OnN5bSguKSkpKSAlPiUgCiAgbXV0YXRlKGFjcm9zcyhjKG91dGNvbWVfdmFyLCBleHBsYW5fdmFyLCBmYW1pbHkpLCB+ZmN0X2lub3JkZXIoLiwgb3JkZXJlZCA9IFRSVUUpKSkKCm1vZGVsX3RpbWVfZTIgPC0gbW9kZWxzX2UyICU+JSAKICBmaWx0ZXIodHJhaW5pbmcgPT0gIkZ1bGwgZGF0YSIpICU+JSAKICBtdXRhdGUoZHVyYXRpb24gPSBtYXAoYWN0dWFsX21vZGVsLCB+cnN0YW46OmdldF9lbGFwc2VkX3RpbWUoLiRmaXQpKSwKICAgICAgICAgZHVyYXRpb24gPSBtYXAoZHVyYXRpb24sIH5yb3duYW1lc190b19jb2x1bW4oYXNfdGliYmxlKC4pKSkpICU+JSAKICBzZWxlY3QoLWFjdHVhbF9tb2RlbCkgJT4lIAogIHVubmVzdChkdXJhdGlvbikgJT4lIAogIG11dGF0ZShtb2RlbCA9IGdsdWUoIjxjb2RlPnttb2RlbH08L2NvZGU+IikpICU+JSAKICBncm91cF9ieShNb2RlbCA9IG1vZGVsLCBPdXRjb21lID0gb3V0Y29tZV92YXIsIAogICAgICAgICAgIGBNYWluIHByZWRpY3RvcmAgPSBleHBsYW5fdmFyLCBgRmFtaWx5YCA9IGZhbWlseSkgJT4lIAogIHN1bW1hcml6ZShgVG90YWwgdGltZSAoaS5lLiBsb25nZXN0IGNoYWluKWAgPSBhcy5kdXJhdGlvbihtYXgod2FybXVwICsgc2FtcGxlKSkpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgYXJyYW5nZShPdXRjb21lLCBgTWFpbiBwcmVkaWN0b3JgKQoKdG90YWxfcm93X2UyIDwtIHRpYmJsZShPdXRjb21lID0gIlRvdGFsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgYFRvdGFsIHRpbWUgKGkuZS4gbG9uZ2VzdCBjaGFpbilgID0gCiAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kdXJhdGlvbihzdW0obW9kZWxfdGltZV9lMiRgVG90YWwgdGltZSAoaS5lLiBsb25nZXN0IGNoYWluKWApKSkKCm1vZGVsX3RpbWVfZTIgPC0gbW9kZWxfdGltZV9lMiAlPiUgCiAgYmluZF9yb3dzKHRvdGFsX3Jvd19lMikKCm1vZGVsX3RpbWVfZTIgJT4lIAogIHNlbGVjdCgtT3V0Y29tZSkgJT4lIAogIGtibChlc2NhcGUgPSBGQUxTRSkgJT4lIAogIHBhY2tfcm93cyhpbmRleCA9IHRhYmxlKGZjdF9pbm9yZGVyKG1vZGVsX3RpbWVfZTIkT3V0Y29tZSkpKSAlPiUgCiAga2FibGVfc3R5bGluZygpCmBgYAoKIyMjIFRyYWluaW5nIGRhdGEKCmBgYHtyIHJ1bm5pbmctdGltZS1lMi10cmFpbmluZ30KbW9kZWxfdGltZV9lMiA8LSBtb2RlbHNfZTIgJT4lIAogIGZpbHRlcih0cmFpbmluZyA9PSAiVHJhaW5pbmciKSAlPiUgCiAgbXV0YXRlKGR1cmF0aW9uID0gbWFwKGFjdHVhbF9tb2RlbCwgfnJzdGFuOjpnZXRfZWxhcHNlZF90aW1lKC4kZml0KSksCiAgICAgICAgIGR1cmF0aW9uID0gbWFwKGR1cmF0aW9uLCB+cm93bmFtZXNfdG9fY29sdW1uKGFzX3RpYmJsZSguKSkpKSAlPiUgCiAgc2VsZWN0KC1hY3R1YWxfbW9kZWwpICU+JSAKICB1bm5lc3QoZHVyYXRpb24pICU+JSAKICBtdXRhdGUobW9kZWwgPSBnbHVlKCI8Y29kZT57bW9kZWx9PC9jb2RlPiIpKSAlPiUgCiAgZ3JvdXBfYnkoTW9kZWwgPSBtb2RlbCwgT3V0Y29tZSA9IG91dGNvbWVfdmFyLCAKICAgICAgICAgICBgTWFpbiBwcmVkaWN0b3JgID0gZXhwbGFuX3ZhciwgYEZhbWlseWAgPSBmYW1pbHkpICU+JSAKICBzdW1tYXJpemUoYFRvdGFsIHRpbWUgKGkuZS4gbG9uZ2VzdCBjaGFpbilgID0gYXMuZHVyYXRpb24obWF4KHdhcm11cCArIHNhbXBsZSkpKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGFycmFuZ2UoT3V0Y29tZSwgYE1haW4gcHJlZGljdG9yYCkKCnRvdGFsX3Jvd19lMiA8LSB0aWJibGUoT3V0Y29tZSA9ICJUb3RhbCIsIAogICAgICAgICAgICAgICAgICAgICAgIGBUb3RhbCB0aW1lIChpLmUuIGxvbmdlc3QgY2hhaW4pYCA9IAogICAgICAgICAgICAgICAgICAgICAgICAgYXMuZHVyYXRpb24oc3VtKG1vZGVsX3RpbWVfZTIkYFRvdGFsIHRpbWUgKGkuZS4gbG9uZ2VzdCBjaGFpbilgKSkpCgptb2RlbF90aW1lX2UyIDwtIG1vZGVsX3RpbWVfZTIgJT4lIAogIGJpbmRfcm93cyh0b3RhbF9yb3dfZTIpCgptb2RlbF90aW1lX2UyICU+JSAKICBzZWxlY3QoLU91dGNvbWUpICU+JSAKICBrYmwoZXNjYXBlID0gRkFMU0UpICU+JSAKICBwYWNrX3Jvd3MoaW5kZXggPSB0YWJsZShmY3RfaW5vcmRlcihtb2RlbF90aW1lX2UyJE91dGNvbWUpKSkgJT4lIAogIGthYmxlX3N0eWxpbmcoKQpgYGAKCgpcIAoKIyBBY3R1YWwgY29kZQoKQWxsIHRoZSBtb2RlbHMgYXJlIHJ1biB3aXRoIGEgKipgdGFyZ2V0c2AqKiBwaXBlbGluZSB3aXRoIGRhdGFzZXQtc3BlY2lmaWMgZnVuY3Rpb25zIHRoYXQgbGl2ZSBpbiB0aGVzZSBmaWxlczoKCi0gYFIvbW9kZWxzX3B0cy5SYCAoZm9yIFBUUy1iYXNlZCBtb2RlbHMpCi0gYFIvbW9kZWxzX2xoci5SYCAoZm9yIGxhdGVudCBodW1hbiByaWdodHMtYmFzZWQgbW9kZWxzKQoKVG8ga2VlcCB0aGUgYW5hbHlzaXMgcmVsYXRpdmVseSBzZWxmIGNvbnRhaW5lZCBoZXJlIGluIHRoZSBhbmFseXNpcyBub3RlYm9vaywgYW5kIHRvIG1ha2UgaXQgc28gdGhlcmUncyBubyBuZWVkIHRvIGh1bnQgdGhyb3VnaCB0aGUgR2l0SHViIHJlcG9zaXRvcnkgdG8gZmluZCB0aGUgYWN0dWFsIGNvZGUsIGhlcmUncyB0aGF0IGNvZGU6CgojIyMjIGBSL21vZGVsc19wdHMuUmAKCmBgYHtyLCBjb2RlPXhmdW46OnJlYWRfdXRmOChoZXJlOjpoZXJlKCJSIiwgIm1vZGVsc19wdHMuUiIpKSwgZXZhbD1GQUxTRSwgY2xhc3Muc291cmNlPSJmb2xkLWhpZGUifQpgYGAKCiMjIyMgYFIvbW9kZWxzX2xoci5SYAoKYGBge3IsIGNvZGU9eGZ1bjo6cmVhZF91dGY4KGhlcmU6OmhlcmUoIlIiLCAibW9kZWxzX2xoci5SIikpLCBldmFsPUZBTFNFLCBjbGFzcy5zb3VyY2U9ImZvbGQtaGlkZSJ9CmBgYAo=