Note: For now, this is all based on the BHL version of the simulation with landscape_fitness_linked as the outcome variable. That can all be changed, though.


library(tidyverse)
library(randomForest)
library(ggridges)
library(ggstance)
library(pander)
library(here)

# Load details about constraints
constraints <- read_rds(here("data", "derived_data", "constraints.rds"))

constraint_levels <- constraints %>%
  mutate(constraint_clean = fct_inorder(constraint_clean)) %>%
  mutate(levels = map(levels_clean, names)) %>%
  unnest(levels) %>%
  mutate(levels = fct_inorder(levels))

# Load simulation data
# TODO: Switch back to full BLS.rds and BHL.rds
BHL <- read_rds(here("data", "derived_data", "BHL_small.rds"))

sim_results <- BHL %>%
  # Make new columns with "_constraint" suffix that show if constraint is T/F
  # instead of using the label. e.g. "Selection" becomes TRUE, "No selection"
  # becomes FALSE
  mutate_at(vars(one_of(constraints$constraint)),
            list(constraint = ~as.logical(-as.integer(.) + 2L))) %>%
  # Count how many of the constraints are TRUE in each row
  mutate(n_constraints = reduce(select(., ends_with("_constraint")), `+`)) %>%
  # Make a factor version of the constraint count for plotting
  mutate(n_constraints_f = as.factor(n_constraints))

Average fitness as number of constraints increase

Points

Thanos apparently snapped this plot.

ggplot(sim_results, 
       aes(x = n_constraints_f, y = landscape_fitness_linked)) +
  geom_point(size = 0.5, alpha = 0.3, position = position_jitter(seed = 1234)) +
  scale_y_reverse() +
  labs(x = "Number of constraints",
       y = "Landscape fitness, linked") +
  theme_bw()

Ridges

ggplot(sim_results, 
       aes(x = landscape_fitness_linked, y = fct_rev(n_constraints_f))) +
  geom_density_ridges(rel_min_height = 0.005) +
  scale_x_reverse() +
  labs(x = "Landscape fitness, linked",
       y = "Number of constraints") +
  theme_bw()

Violins

ggplot(sim_results, aes(x = n_constraints_f, y = landscape_fitness_linked)) +
  geom_violin(size = 0.25, fill = "grey70") +
  stat_summary(geom = "point", fun.y = "mean",
               size = 2, pch = 21, fill = "black", color = "white") +
  scale_y_reverse() +
  labs(x = "Number of constraints",
       y = "Landscape fitness, linked",
       caption = "Point = mean value") +
  theme_bw()
## Warning: `fun.y` is deprecated. Use `fun` instead.

Boxplots

ggplot(sim_results, aes(x = n_constraints_f, y = landscape_fitness_linked)) +
  geom_boxplot() +
  stat_summary(geom = "point", fun.y = "mean",
               size = 2, pch = 21, fill = "black", color = "white") +
  scale_y_reverse() +
  labs(x = "Number of constraints",
       y = "Landscape fitness, linked",
       caption = "Point = mean value") +
  theme_bw()
## Warning: `fun.y` is deprecated. Use `fun` instead.

Constraint importance

Here we use a random forest to determine variable importance. We don’t need to include all the interactions (e.g. create_network + select + create_network * select, etc.) because random forests inherently pick those up.

# This takes a couple minutes to run
forest_model <- 
  randomForest(landscape_fitness_linked ~
                 create_network + select + disperse + 
                 compete + selectfor_d + catastrophe,
               data = sim_results)

According to Liz Dinsdale:

The mean decrease in Gini coefficient is a measure of how each variable contributes to the homogeneity of the nodes and leaves in the resulting random forest. Each time a particular variable is used to split a node, the Gini coefficient for the child nodes are calculated and compared to that of the original node. The Gini coefficient is a measure of homogeneity from 0 (homogeneous) to 1 (heterogeneous). The changes in Gini are summed for each variable and normalized at the end of the calculation. Variables that result in nodes with higher purity have a higher decrease in Gini coefficient.

Basically, the higher the decrease in impurity, the more important the variable in explaining the outcome.

constraint_importance <- importance(forest_model) %>% 
  data.frame() %>% 
  rownames_to_column(var = "constraint") %>% 
  arrange(desc(IncNodePurity)) %>% 
  mutate(constraint = fct_inorder(constraint))

constraint_importance %>% 
  mutate(constraint = paste0("`", constraint, "`")) %>% 
  pandoc.table(justify = "lc")
constraint IncNodePurity
select 728.4
compete 455
catastrophe 34.17
create_network 17.89
disperse 3.257
selectfor_d 0.699
ggplot(constraint_importance, 
       aes(x = IncNodePurity, y = fct_rev(constraint))) +
  geom_pointrangeh(aes(xmin = 0, xmax = IncNodePurity)) +
  labs(x = "Mean decrease in node impurity (Gini)",
       y = NULL) +
  theme_bw()

Power of different combinations of constraints

THIS IS MAGICAL.

# Create columns for every combination of constraint in the data (e.g. select &
# disperse, select & disperse & create_network) 
#
# By the inimitable Vincent Arel-Bundock
make_combinations <- function(df, m = 5) {
  com <- colnames(df)[2:ncol(df)] %>%
    combn(m) %>%
    as_tibble()
  out <- com %>%
    map(~ df[.]) %>%
    map(~ rowSums(.) == ncol(.)) %>%
    setNames(map(com, paste, collapse = " + ")) %>%
    as_tibble()
  return(out)
}

# Select just the run number and *_constraint TRUE/FALSE columns
constraint_combinations <- sim_results %>%
  select(runnum, ends_with("_constraint")) %>% 
  # Shrink names by removing "_constraint"
  rename_at(vars(ends_with("constraint")), 
            list(~str_replace_all(., "_constraint", "")))

# Find all combinations of variables (m = number of items in combination; m = 2
# means pairs, m = 3 means triplets, etc.)
all_constraint_combos <- map(2:6, ~make_combinations(constraint_combinations, m = .)) %>% 
  bind_cols(constraint_combinations, .)

# Select the outcome variables we care about (for now just
# landscape_fitness_linked) and join the constraint combinations
constraint_combo_outcomes <- sim_results %>%
  select(runnum, n_constraints, landscape_fitness_linked) %>% 
  right_join(all_constraint_combos, by = "runnum")

# Don't double count rows. If a row has two constraints like select and
# disperse, it'll also have select + disperse set to TRUE. If that's the case,
# we don't want to include it in just select or just disperse
constraint_combo_outcomes_nested <- constraint_combo_outcomes %>% 
  select(-n_constraints) %>% 
  # Make long
  gather(constraint_combo, value, -c(runnum, landscape_fitness_linked)) %>% 
  # Count how many constraints there are within each row based on + signs
  mutate(n = str_count(constraint_combo, "\\+") + 1) %>%
  # Only keep rows where the constraint is turned on
  filter(value == TRUE) %>%
  # Nest all the constraint combinations within each row
  group_by(runnum) %>% 
  nest()

# Only keep the values where n == max(n) for that row
constraint_combo_outcomes_filtered <- constraint_combo_outcomes_nested %>% 
  mutate(filtered = data %>% map(~filter(., n == max(.$n)))) %>% 
  select(-data) %>% 
  unnest(filtered)

# This omitted all the rows where n_constraints == 0, so add those back in
no_constraints <- constraint_combo_outcomes %>% 
  filter(n_constraints == 0) %>% 
  mutate(constraint_combo = "No constraints", n = 0) %>% 
  select(runnum, landscape_fitness_linked, constraint_combo, n)

constraint_combo_outcomes_done <- bind_rows(constraint_combo_outcomes_filtered,
                                            no_constraints) %>% 
  select(-value)
n_constraints_combos <- constraint_combo_outcomes_done %>%
  group_by(constraint_combo, n) %>%
  summarize(avg = mean(landscape_fitness_linked)) %>% 
  arrange(desc(n), desc(avg)) %>% 
  ungroup() %>% 
  mutate(constraint_combo = fct_inorder(constraint_combo))

ggplot(n_constraints_combos,
       aes(x = avg, y = constraint_combo)) +
  geom_pointrangeh(aes(xmin = 0, xmax = avg)) + 
  labs(x = "Average landscape fitness, linked",
       y = NULL) +
  facet_grid(rows = vars(n),
             scales = "free", space = "free") + 
  theme_bw()

LS0tCnRpdGxlOiAiRmFuY2llciBhbmFseXNpcyIKYXV0aG9yOiAiU3RldmVuIEwuIFBlY2sgYW5kIEFuZHJldyBIZWlzcyIKZGF0ZTogIkxhc3QgcnVuOiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVlLCAlWScpYCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiBoaWRlCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCioqTm90ZToqKiBGb3Igbm93LCB0aGlzIGlzIGFsbCBiYXNlZCBvbiB0aGUgYEJITGAgdmVyc2lvbiBvZiB0aGUgc2ltdWxhdGlvbiB3aXRoIGBsYW5kc2NhcGVfZml0bmVzc19saW5rZWRgIGFzIHRoZSBvdXRjb21lIHZhcmlhYmxlLiBUaGF0IGNhbiBhbGwgYmUgY2hhbmdlZCwgdGhvdWdoLgoKLS0tCgpgYGB7ciBsb2FkLWxpYnJhcmllcy1kYXRhLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZ2dzdGFuY2UpCmxpYnJhcnkocGFuZGVyKQpsaWJyYXJ5KGhlcmUpCgojIExvYWQgZGV0YWlscyBhYm91dCBjb25zdHJhaW50cwpjb25zdHJhaW50cyA8LSByZWFkX3JkcyhoZXJlKCJkYXRhIiwgImRlcml2ZWRfZGF0YSIsICJjb25zdHJhaW50cy5yZHMiKSkKCmNvbnN0cmFpbnRfbGV2ZWxzIDwtIGNvbnN0cmFpbnRzICU+JQogIG11dGF0ZShjb25zdHJhaW50X2NsZWFuID0gZmN0X2lub3JkZXIoY29uc3RyYWludF9jbGVhbikpICU+JQogIG11dGF0ZShsZXZlbHMgPSBtYXAobGV2ZWxzX2NsZWFuLCBuYW1lcykpICU+JQogIHVubmVzdChsZXZlbHMpICU+JQogIG11dGF0ZShsZXZlbHMgPSBmY3RfaW5vcmRlcihsZXZlbHMpKQoKIyBMb2FkIHNpbXVsYXRpb24gZGF0YQojIFRPRE86IFN3aXRjaCBiYWNrIHRvIGZ1bGwgQkxTLnJkcyBhbmQgQkhMLnJkcwpCSEwgPC0gcmVhZF9yZHMoaGVyZSgiZGF0YSIsICJkZXJpdmVkX2RhdGEiLCAiQkhMX3NtYWxsLnJkcyIpKQoKc2ltX3Jlc3VsdHMgPC0gQkhMICU+JQogICMgTWFrZSBuZXcgY29sdW1ucyB3aXRoICJfY29uc3RyYWludCIgc3VmZml4IHRoYXQgc2hvdyBpZiBjb25zdHJhaW50IGlzIFQvRgogICMgaW5zdGVhZCBvZiB1c2luZyB0aGUgbGFiZWwuIGUuZy4gIlNlbGVjdGlvbiIgYmVjb21lcyBUUlVFLCAiTm8gc2VsZWN0aW9uIgogICMgYmVjb21lcyBGQUxTRQogIG11dGF0ZV9hdCh2YXJzKG9uZV9vZihjb25zdHJhaW50cyRjb25zdHJhaW50KSksCiAgICAgICAgICAgIGxpc3QoY29uc3RyYWludCA9IH5hcy5sb2dpY2FsKC1hcy5pbnRlZ2VyKC4pICsgMkwpKSkgJT4lCiAgIyBDb3VudCBob3cgbWFueSBvZiB0aGUgY29uc3RyYWludHMgYXJlIFRSVUUgaW4gZWFjaCByb3cKICBtdXRhdGUobl9jb25zdHJhaW50cyA9IHJlZHVjZShzZWxlY3QoLiwgZW5kc193aXRoKCJfY29uc3RyYWludCIpKSwgYCtgKSkgJT4lCiAgIyBNYWtlIGEgZmFjdG9yIHZlcnNpb24gb2YgdGhlIGNvbnN0cmFpbnQgY291bnQgZm9yIHBsb3R0aW5nCiAgbXV0YXRlKG5fY29uc3RyYWludHNfZiA9IGFzLmZhY3RvcihuX2NvbnN0cmFpbnRzKSkKYGBgCgoKIyBBdmVyYWdlIGZpdG5lc3MgYXMgbnVtYmVyIG9mIGNvbnN0cmFpbnRzIGluY3JlYXNlCgojIyBQb2ludHMKClRoYW5vcyBhcHBhcmVudGx5IHNuYXBwZWQgdGhpcyBwbG90LgoKYGBge3IgcGxvdC1maXRuZXNzLWNvbnN0cmFpbnRzLXBvaW50cywgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NC41fQpnZ3Bsb3Qoc2ltX3Jlc3VsdHMsIAogICAgICAgYWVzKHggPSBuX2NvbnN0cmFpbnRzX2YsIHkgPSBsYW5kc2NhcGVfZml0bmVzc19saW5rZWQpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMywgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIoc2VlZCA9IDEyMzQpKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIGxhYnMoeCA9ICJOdW1iZXIgb2YgY29uc3RyYWludHMiLAogICAgICAgeSA9ICJMYW5kc2NhcGUgZml0bmVzcywgbGlua2VkIikgKwogIHRoZW1lX2J3KCkKYGBgCgojIyBSaWRnZXMKCmBgYHtyIHBsb3QtZml0bmVzcy1jb25zdHJhaW50cy1yaWRnZXMsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTQuNSwgbWVzc2FnZT1GQUxTRX0KZ2dwbG90KHNpbV9yZXN1bHRzLCAKICAgICAgIGFlcyh4ID0gbGFuZHNjYXBlX2ZpdG5lc3NfbGlua2VkLCB5ID0gZmN0X3JldihuX2NvbnN0cmFpbnRzX2YpKSkgKwogIGdlb21fZGVuc2l0eV9yaWRnZXMocmVsX21pbl9oZWlnaHQgPSAwLjAwNSkgKwogIHNjYWxlX3hfcmV2ZXJzZSgpICsKICBsYWJzKHggPSAiTGFuZHNjYXBlIGZpdG5lc3MsIGxpbmtlZCIsCiAgICAgICB5ID0gIk51bWJlciBvZiBjb25zdHJhaW50cyIpICsKICB0aGVtZV9idygpCmBgYAoKIyMgVmlvbGlucwoKYGBge3IgcGxvdC1maXRuZXNzLWNvbnN0cmFpbnRzLXZpb2xpbiwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NC41fQpnZ3Bsb3Qoc2ltX3Jlc3VsdHMsIGFlcyh4ID0gbl9jb25zdHJhaW50c19mLCB5ID0gbGFuZHNjYXBlX2ZpdG5lc3NfbGlua2VkKSkgKwogIGdlb21fdmlvbGluKHNpemUgPSAwLjI1LCBmaWxsID0gImdyZXk3MCIpICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJwb2ludCIsIGZ1bi55ID0gIm1lYW4iLAogICAgICAgICAgICAgICBzaXplID0gMiwgcGNoID0gMjEsIGZpbGwgPSAiYmxhY2siLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X3JldmVyc2UoKSArCiAgbGFicyh4ID0gIk51bWJlciBvZiBjb25zdHJhaW50cyIsCiAgICAgICB5ID0gIkxhbmRzY2FwZSBmaXRuZXNzLCBsaW5rZWQiLAogICAgICAgY2FwdGlvbiA9ICJQb2ludCA9IG1lYW4gdmFsdWUiKSArCiAgdGhlbWVfYncoKQpgYGAKCiMjIEJveHBsb3RzCgpgYGB7ciBwbG90LWZpdG5lc3MtY29uc3RyYWludHMtYm94cGxvdCwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NC41fQpnZ3Bsb3Qoc2ltX3Jlc3VsdHMsIGFlcyh4ID0gbl9jb25zdHJhaW50c19mLCB5ID0gbGFuZHNjYXBlX2ZpdG5lc3NfbGlua2VkKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJwb2ludCIsIGZ1bi55ID0gIm1lYW4iLAogICAgICAgICAgICAgICBzaXplID0gMiwgcGNoID0gMjEsIGZpbGwgPSAiYmxhY2siLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X3JldmVyc2UoKSArCiAgbGFicyh4ID0gIk51bWJlciBvZiBjb25zdHJhaW50cyIsCiAgICAgICB5ID0gIkxhbmRzY2FwZSBmaXRuZXNzLCBsaW5rZWQiLAogICAgICAgY2FwdGlvbiA9ICJQb2ludCA9IG1lYW4gdmFsdWUiKSArCiAgdGhlbWVfYncoKQpgYGAKCgojIENvbnN0cmFpbnQgaW1wb3J0YW5jZQoKSGVyZSB3ZSB1c2UgYSByYW5kb20gZm9yZXN0IHRvIGRldGVybWluZSB2YXJpYWJsZSBpbXBvcnRhbmNlLiBXZSBkb24ndCBuZWVkIHRvIGluY2x1ZGUgYWxsIHRoZSBpbnRlcmFjdGlvbnMgKGUuZy4gYGNyZWF0ZV9uZXR3b3JrICsgc2VsZWN0ICsgY3JlYXRlX25ldHdvcmsgKiBzZWxlY3RgLCBldGMuKSBiZWNhdXNlIFtyYW5kb20gZm9yZXN0cyBpbmhlcmVudGx5IHBpY2sgdGhvc2UgdXBdKGh0dHBzOi8vc3RhdHMuc3RhY2tleGNoYW5nZS5jb20vYS8xNTc2NzQvMzAyNSkuCgpgYGB7ciByZi1ydW4tbW9kZWxzLCBjYWNoZT1UUlVFfQojIFRoaXMgdGFrZXMgYSBjb3VwbGUgbWludXRlcyB0byBydW4KZm9yZXN0X21vZGVsIDwtIAogIHJhbmRvbUZvcmVzdChsYW5kc2NhcGVfZml0bmVzc19saW5rZWQgfgogICAgICAgICAgICAgICAgIGNyZWF0ZV9uZXR3b3JrICsgc2VsZWN0ICsgZGlzcGVyc2UgKyAKICAgICAgICAgICAgICAgICBjb21wZXRlICsgc2VsZWN0Zm9yX2QgKyBjYXRhc3Ryb3BoZSwKICAgICAgICAgICAgICAgZGF0YSA9IHNpbV9yZXN1bHRzKQpgYGAKCkFjY29yZGluZyB0byBbTGl6IERpbnNkYWxlXShodHRwczovL2RpbnNkYWxlbGFiLnNkc3UuZWR1L21ldGFnLnN0YXRzL2NvZGUvcmFuZG9tZm9yZXN0Lmh0bWwpOgoKPiBUaGUgbWVhbiBkZWNyZWFzZSBpbiBHaW5pIGNvZWZmaWNpZW50IGlzIGEgbWVhc3VyZSBvZiBob3cgZWFjaCB2YXJpYWJsZSBjb250cmlidXRlcyB0byB0aGUgaG9tb2dlbmVpdHkgb2YgdGhlIG5vZGVzIGFuZCBsZWF2ZXMgaW4gdGhlIHJlc3VsdGluZyByYW5kb20gZm9yZXN0LiBFYWNoIHRpbWUgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlIGlzIHVzZWQgdG8gc3BsaXQgYSBub2RlLCB0aGUgR2luaSBjb2VmZmljaWVudCBmb3IgdGhlIGNoaWxkIG5vZGVzIGFyZSBjYWxjdWxhdGVkIGFuZCBjb21wYXJlZCB0byB0aGF0IG9mIHRoZSBvcmlnaW5hbCBub2RlLiBUaGUgR2luaSBjb2VmZmljaWVudCBpcyBhIG1lYXN1cmUgb2YgaG9tb2dlbmVpdHkgZnJvbSAwIChob21vZ2VuZW91cykgdG8gMSAoaGV0ZXJvZ2VuZW91cykuIFRoZSBjaGFuZ2VzIGluIEdpbmkgYXJlIHN1bW1lZCBmb3IgZWFjaCB2YXJpYWJsZSBhbmQgbm9ybWFsaXplZCBhdCB0aGUgZW5kIG9mIHRoZSBjYWxjdWxhdGlvbi4gVmFyaWFibGVzIHRoYXQgcmVzdWx0IGluIG5vZGVzIHdpdGggaGlnaGVyIHB1cml0eSBoYXZlIGEgaGlnaGVyIGRlY3JlYXNlIGluIEdpbmkgY29lZmZpY2llbnQuCgpCYXNpY2FsbHksIHRoZSBoaWdoZXIgdGhlIGRlY3JlYXNlIGluIGltcHVyaXR5LCB0aGUgbW9yZSBpbXBvcnRhbnQgdGhlIHZhcmlhYmxlIGluIGV4cGxhaW5pbmcgdGhlIG91dGNvbWUuCgpgYGB7ciBjb25zdHJhaW50LWltcG9ydGFuY2UtdGJsLCByZXN1bHRzPSJhc2lzIn0KY29uc3RyYWludF9pbXBvcnRhbmNlIDwtIGltcG9ydGFuY2UoZm9yZXN0X21vZGVsKSAlPiUgCiAgZGF0YS5mcmFtZSgpICU+JSAKICByb3duYW1lc190b19jb2x1bW4odmFyID0gImNvbnN0cmFpbnQiKSAlPiUgCiAgYXJyYW5nZShkZXNjKEluY05vZGVQdXJpdHkpKSAlPiUgCiAgbXV0YXRlKGNvbnN0cmFpbnQgPSBmY3RfaW5vcmRlcihjb25zdHJhaW50KSkKCmNvbnN0cmFpbnRfaW1wb3J0YW5jZSAlPiUgCiAgbXV0YXRlKGNvbnN0cmFpbnQgPSBwYXN0ZTAoImAiLCBjb25zdHJhaW50LCAiYCIpKSAlPiUgCiAgcGFuZG9jLnRhYmxlKGp1c3RpZnkgPSAibGMiKQpgYGAKCmBgYHtyIGNvbnN0cmFpbnQtaW1wb3J0YW5jZS1wbG90LCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00LjV9CmdncGxvdChjb25zdHJhaW50X2ltcG9ydGFuY2UsIAogICAgICAgYWVzKHggPSBJbmNOb2RlUHVyaXR5LCB5ID0gZmN0X3Jldihjb25zdHJhaW50KSkpICsKICBnZW9tX3BvaW50cmFuZ2VoKGFlcyh4bWluID0gMCwgeG1heCA9IEluY05vZGVQdXJpdHkpKSArCiAgbGFicyh4ID0gIk1lYW4gZGVjcmVhc2UgaW4gbm9kZSBpbXB1cml0eSAoR2luaSkiLAogICAgICAgeSA9IE5VTEwpICsKICB0aGVtZV9idygpCmBgYAoKCiMgUG93ZXIgb2YgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBjb25zdHJhaW50cwoKVEhJUyBJUyBNQUdJQ0FMLgoKYGBge3IgZmluZC12YWx1ZXMtYXQtYWxsLWNvbWJpbmF0aW9ucywgY2FjaGU9VFJVRSwgd2FybmluZz1GQUxTRX0KIyBDcmVhdGUgY29sdW1ucyBmb3IgZXZlcnkgY29tYmluYXRpb24gb2YgY29uc3RyYWludCBpbiB0aGUgZGF0YSAoZS5nLiBzZWxlY3QgJgojIGRpc3BlcnNlLCBzZWxlY3QgJiBkaXNwZXJzZSAmIGNyZWF0ZV9uZXR3b3JrKSAKIwojIEJ5IHRoZSBpbmltaXRhYmxlIFZpbmNlbnQgQXJlbC1CdW5kb2NrCm1ha2VfY29tYmluYXRpb25zIDwtIGZ1bmN0aW9uKGRmLCBtID0gNSkgewogIGNvbSA8LSBjb2xuYW1lcyhkZilbMjpuY29sKGRmKV0gJT4lCiAgICBjb21ibihtKSAlPiUKICAgIGFzX3RpYmJsZSgpCiAgb3V0IDwtIGNvbSAlPiUKICAgIG1hcCh+IGRmWy5dKSAlPiUKICAgIG1hcCh+IHJvd1N1bXMoLikgPT0gbmNvbCguKSkgJT4lCiAgICBzZXROYW1lcyhtYXAoY29tLCBwYXN0ZSwgY29sbGFwc2UgPSAiICsgIikpICU+JQogICAgYXNfdGliYmxlKCkKICByZXR1cm4ob3V0KQp9CgojIFNlbGVjdCBqdXN0IHRoZSBydW4gbnVtYmVyIGFuZCAqX2NvbnN0cmFpbnQgVFJVRS9GQUxTRSBjb2x1bW5zCmNvbnN0cmFpbnRfY29tYmluYXRpb25zIDwtIHNpbV9yZXN1bHRzICU+JQogIHNlbGVjdChydW5udW0sIGVuZHNfd2l0aCgiX2NvbnN0cmFpbnQiKSkgJT4lIAogICMgU2hyaW5rIG5hbWVzIGJ5IHJlbW92aW5nICJfY29uc3RyYWludCIKICByZW5hbWVfYXQodmFycyhlbmRzX3dpdGgoImNvbnN0cmFpbnQiKSksIAogICAgICAgICAgICBsaXN0KH5zdHJfcmVwbGFjZV9hbGwoLiwgIl9jb25zdHJhaW50IiwgIiIpKSkKCiMgRmluZCBhbGwgY29tYmluYXRpb25zIG9mIHZhcmlhYmxlcyAobSA9IG51bWJlciBvZiBpdGVtcyBpbiBjb21iaW5hdGlvbjsgbSA9IDIKIyBtZWFucyBwYWlycywgbSA9IDMgbWVhbnMgdHJpcGxldHMsIGV0Yy4pCmFsbF9jb25zdHJhaW50X2NvbWJvcyA8LSBtYXAoMjo2LCB+bWFrZV9jb21iaW5hdGlvbnMoY29uc3RyYWludF9jb21iaW5hdGlvbnMsIG0gPSAuKSkgJT4lIAogIGJpbmRfY29scyhjb25zdHJhaW50X2NvbWJpbmF0aW9ucywgLikKCiMgU2VsZWN0IHRoZSBvdXRjb21lIHZhcmlhYmxlcyB3ZSBjYXJlIGFib3V0IChmb3Igbm93IGp1c3QKIyBsYW5kc2NhcGVfZml0bmVzc19saW5rZWQpIGFuZCBqb2luIHRoZSBjb25zdHJhaW50IGNvbWJpbmF0aW9ucwpjb25zdHJhaW50X2NvbWJvX291dGNvbWVzIDwtIHNpbV9yZXN1bHRzICU+JQogIHNlbGVjdChydW5udW0sIG5fY29uc3RyYWludHMsIGxhbmRzY2FwZV9maXRuZXNzX2xpbmtlZCkgJT4lIAogIHJpZ2h0X2pvaW4oYWxsX2NvbnN0cmFpbnRfY29tYm9zLCBieSA9ICJydW5udW0iKQoKIyBEb24ndCBkb3VibGUgY291bnQgcm93cy4gSWYgYSByb3cgaGFzIHR3byBjb25zdHJhaW50cyBsaWtlIHNlbGVjdCBhbmQKIyBkaXNwZXJzZSwgaXQnbGwgYWxzbyBoYXZlIHNlbGVjdCArIGRpc3BlcnNlIHNldCB0byBUUlVFLiBJZiB0aGF0J3MgdGhlIGNhc2UsCiMgd2UgZG9uJ3Qgd2FudCB0byBpbmNsdWRlIGl0IGluIGp1c3Qgc2VsZWN0IG9yIGp1c3QgZGlzcGVyc2UKY29uc3RyYWludF9jb21ib19vdXRjb21lc19uZXN0ZWQgPC0gY29uc3RyYWludF9jb21ib19vdXRjb21lcyAlPiUgCiAgc2VsZWN0KC1uX2NvbnN0cmFpbnRzKSAlPiUgCiAgIyBNYWtlIGxvbmcKICBnYXRoZXIoY29uc3RyYWludF9jb21ibywgdmFsdWUsIC1jKHJ1bm51bSwgbGFuZHNjYXBlX2ZpdG5lc3NfbGlua2VkKSkgJT4lIAogICMgQ291bnQgaG93IG1hbnkgY29uc3RyYWludHMgdGhlcmUgYXJlIHdpdGhpbiBlYWNoIHJvdyBiYXNlZCBvbiArIHNpZ25zCiAgbXV0YXRlKG4gPSBzdHJfY291bnQoY29uc3RyYWludF9jb21ibywgIlxcKyIpICsgMSkgJT4lCiAgIyBPbmx5IGtlZXAgcm93cyB3aGVyZSB0aGUgY29uc3RyYWludCBpcyB0dXJuZWQgb24KICBmaWx0ZXIodmFsdWUgPT0gVFJVRSkgJT4lCiAgIyBOZXN0IGFsbCB0aGUgY29uc3RyYWludCBjb21iaW5hdGlvbnMgd2l0aGluIGVhY2ggcm93CiAgZ3JvdXBfYnkocnVubnVtKSAlPiUgCiAgbmVzdCgpCgojIE9ubHkga2VlcCB0aGUgdmFsdWVzIHdoZXJlIG4gPT0gbWF4KG4pIGZvciB0aGF0IHJvdwpjb25zdHJhaW50X2NvbWJvX291dGNvbWVzX2ZpbHRlcmVkIDwtIGNvbnN0cmFpbnRfY29tYm9fb3V0Y29tZXNfbmVzdGVkICU+JSAKICBtdXRhdGUoZmlsdGVyZWQgPSBkYXRhICU+JSBtYXAofmZpbHRlciguLCBuID09IG1heCguJG4pKSkpICU+JSAKICBzZWxlY3QoLWRhdGEpICU+JSAKICB1bm5lc3QoZmlsdGVyZWQpCgojIFRoaXMgb21pdHRlZCBhbGwgdGhlIHJvd3Mgd2hlcmUgbl9jb25zdHJhaW50cyA9PSAwLCBzbyBhZGQgdGhvc2UgYmFjayBpbgpub19jb25zdHJhaW50cyA8LSBjb25zdHJhaW50X2NvbWJvX291dGNvbWVzICU+JSAKICBmaWx0ZXIobl9jb25zdHJhaW50cyA9PSAwKSAlPiUgCiAgbXV0YXRlKGNvbnN0cmFpbnRfY29tYm8gPSAiTm8gY29uc3RyYWludHMiLCBuID0gMCkgJT4lIAogIHNlbGVjdChydW5udW0sIGxhbmRzY2FwZV9maXRuZXNzX2xpbmtlZCwgY29uc3RyYWludF9jb21ibywgbikKCmNvbnN0cmFpbnRfY29tYm9fb3V0Y29tZXNfZG9uZSA8LSBiaW5kX3Jvd3MoY29uc3RyYWludF9jb21ib19vdXRjb21lc19maWx0ZXJlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub19jb25zdHJhaW50cykgJT4lIAogIHNlbGVjdCgtdmFsdWUpCmBgYAoKYGBge3IgcGxvdC1maXRuZXNzLWFsbC1jb21ib3MsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEwfQpuX2NvbnN0cmFpbnRzX2NvbWJvcyA8LSBjb25zdHJhaW50X2NvbWJvX291dGNvbWVzX2RvbmUgJT4lCiAgZ3JvdXBfYnkoY29uc3RyYWludF9jb21ibywgbikgJT4lCiAgc3VtbWFyaXplKGF2ZyA9IG1lYW4obGFuZHNjYXBlX2ZpdG5lc3NfbGlua2VkKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSwgZGVzYyhhdmcpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUoY29uc3RyYWludF9jb21ibyA9IGZjdF9pbm9yZGVyKGNvbnN0cmFpbnRfY29tYm8pKQoKZ2dwbG90KG5fY29uc3RyYWludHNfY29tYm9zLAogICAgICAgYWVzKHggPSBhdmcsIHkgPSBjb25zdHJhaW50X2NvbWJvKSkgKwogIGdlb21fcG9pbnRyYW5nZWgoYWVzKHhtaW4gPSAwLCB4bWF4ID0gYXZnKSkgKyAKICBsYWJzKHggPSAiQXZlcmFnZSBsYW5kc2NhcGUgZml0bmVzcywgbGlua2VkIiwKICAgICAgIHkgPSBOVUxMKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhuKSwKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsgCiAgdGhlbWVfYncoKQpgYGAK