1 Getting started

To copy the code, click the button in the upper right corner of the code-chunks.

1.1 clean up

rm(list = ls())
gc()


1.2 custom functions

We defined a number custom functions, at Download custom_functions.R.

source("./custom_functions.R")


1.3 necessary packages

  • tidyverse: data wrangling
  • igraph: generate and visualize graphs
  • parallel: parallel computing to speed up simulation
  • foreach: looping in parallel
  • doParallel: parallel backend for foreach
  • ggplot2: data visualization
  • ggh4x: hacks for ggplot2
  • ggpubr: make visualizations publication-ready
packages = c("tidyverse", "igraph", "ggplot2", "parallel", "doParallel", "foreach", "ggh4x", "ggpubr",
    "plotly", "RColorBrewer", "grid", "gridExtra", "patchwork", "ggplotify", "ggraph", "gganimate", "RColorBrewer",
    "ggtext", "magick", "jsonlite")

invisible(fpackage.check(packages))
rm(packages)

2 Experimental conditions

# pick one configuration that likely leads to an unpopular norm, and explore multiple 'seeds':
run_one_seed <- function(
  i,
  base_seed = 1253281123,
  params = list(s = 15, e = 10, w = 40, z = 50, lambda1 = 5, lambda2 = 1.8),
  
  # tweak network
  k_min = 2, 
  k_max = 9,
  alpha = 2.1,
  rho = 0.4,
  r = -0.1,
  
  # retrieve network
  return_network = FALSE
  
) {
  # derived seed for this run
  seed_i <- base_seed + i
  set.seed(seed_i)

  # --- network creation ---
  degseq <- fdegseq(
    n      = 10,
    alpha  = alpha,
    k_min  = k_min,
    k_max  = k_max,
    dist   = "log-normal", #use log-normal 
    seed   = seed_i
  )

  network <- sample_degseq(degseq, method = "vl")
  
  V(network)$role <- sample(
    c(rep("trendsetter", 2), rep("conformist", 8))
  )
  

  rewired_network <- frewire_r(network, r, verbose = FALSE, max_iter = 1e5)
  final_network   <- fswap_rho(rewired_network, rho, verbose = FALSE, max_iter = 1e4)
  
  # --- stats ---
  stats <- list(
    run           = i,
    seed          = seed_i,
    num_nodes     = vcount(final_network),
    num_edges     = ecount(final_network),
    avg_degree    = mean(degree(final_network)),
    sd_degree     = sd(degree(final_network)),
    net_density   = edge_density(final_network),
    net_diameter  = diameter(final_network, directed = FALSE, unconnected = TRUE),
    avg_path_len  = average.path.length(final_network, directed = FALSE),
    clust_coeff   = transitivity(final_network, type = "global"),
    assort_deg    = assortativity_degree(final_network),
    deg_trait_cor = fdegtraitcor(final_network)$cor,
    components    = components(final_network)$no
  )
  
  fplot_graph(final_network, layout = layout_with_fr(final_network)) 
  
  
  # --- initial actions ---
  V(final_network)$action <- ifelse(V(final_network)$role == "trendsetter", 1, 0)
  
  # --- deterministic simulation ---
  sim_det <- fabm(
    network      = final_network,
    params       = params,
    max_rounds   = 50,
    mi_threshold = 0.49,
    choice_rule  = "deterministic",
    plot         = TRUE,
    histories    = TRUE
  )
  
  # generate the gif for the current network
  gif_filename <- paste0("./figures/animation_network_", seed_i, ".gif")
  gif_path <- fnetworkgif(final_network, sim_det$decision_history, rounds = sim_det$equilibrium$round, output_dir = "./figures")
  # rename the gif to match the naming pattern
  file.rename(gif_path, gif_filename)

  if (!is.null(sim_det$plot)) {
    print(sim_det$plot)
  }
  
  # --- probabilistic simulation ---
  sim_prob <- fabm(
    network                = final_network,
    params                 = params,
    max_rounds             = 100,
    mi_threshold           = 0.49,
    choice_rule            = "probabilistic",
    stable_window          = 8,   # the length of the window of adoption values
    required_stable_rounds = 20, # number of windows needed to declare equilibrium
    plot                   = TRUE
  )
  if (!is.null(sim_prob$plot)) {
    print(sim_prob$plot)
  }
  
   result <- list(
    segregation_det  = sim_det$equilibrium$segregation,
    segregation_prob = sim_prob$equilibrium$segregation,
    stats            = stats
  )
  
  if (return_network) {
    result$network <- final_network
  }
  
  result
}

####

run_one_sw <- function(
  i,
  base_seed = 1253281123,
  model = "watts-strogatz",
  beta = 0,
  nei = 3,
  clique_size = 5,
  pmin = 0.1,
  
  params = list(s = 15, e = 10, w = 40, z = 50, lambda1 = 5, lambda2 = 1.8),
  
  # retrieve network
  return_network = FALSE
  
) {
  # derived seed for this run
  seed_i <- base_seed + i
  set.seed(seed_i)

  if (model == "watts-strogatz") {
    network <- sample_smallworld(dim = 1, size = 10, nei = 3, p = beta) }
  else if (model == "caveman") {
    network <- simulate_caveman(n = 10, clique_size = clique_size) 
  }

  V(network)$role <- sample(
    c(rep("trendsetter", 2), rep("conformist", 8))
  )
  
  
  final_network <- network

  
  # --- stats ---
  stats <- list(
    run           = i,
    seed          = seed_i,
    num_nodes     = vcount(final_network),
    num_edges     = ecount(final_network),
    avg_degree    = mean(degree(final_network)),
    sd_degree     = sd(degree(final_network)),
    net_density   = edge_density(final_network),
    net_diameter  = diameter(final_network, directed = FALSE, unconnected = TRUE),
    avg_path_len  = average.path.length(final_network, directed = FALSE),
    clust_coeff   = transitivity(final_network, type = "global"),
    assort_deg    = assortativity_degree(final_network),
    deg_trait_cor = fdegtraitcor(final_network)$cor,
    components    = components(final_network)$no
  )
  
  stats_df <- data.frame(
    Metric = names(stats),
    Value  = unlist(stats),
    row.names = NULL
  )
  
  #print(stats_df)
  fplot_graph(final_network, layout = layout.kamada.kawai(final_network))
  

  # --- initial actions ---
  V(final_network)$action <- ifelse(V(final_network)$role == "trendsetter", 1, 0)
  
  # --- deterministic simulation ---
  sim_det <- fabm(
    network      = final_network,
    params       = params,
    max_rounds   = 50,
    mi_threshold = 0.49,
    choice_rule  = "deterministic",
    plot         = TRUE,
    histories    = TRUE
  )

  # generate the gif for the current network
  gif_filename <- paste0("./figures/animation_network_", seed_i, ".gif")
  gif_path <- fnetworkgif(final_network, sim_det$decision_history, rounds = sim_det$equilibrium$round, output_dir = "./figures")
  # rename the gif to match the naming pattern
  file.rename(gif_path, gif_filename)
  
  
  if (!is.null(sim_det$plot)) {
    print(sim_det$plot)
  }
  
  # --- probabilistic simulation ---
  sim_prob <- fabm(
    network                = final_network,
    params                 = params,
    max_rounds             = 100,
    mi_threshold           = 0.49,
    choice_rule            = "probabilistic",
    stable_window          = 8,   # the length of the window of adoption values
    required_stable_rounds = 20, # number of windows needed to declare equilibrium
    plot                   = TRUE
  )
  if (!is.null(sim_prob$plot)) {
    print(sim_prob$plot)
  }
  
  
  result <- list(
    segregation_det  = sim_det$equilibrium$segregation,
    segregation_prob = sim_prob$equilibrium$segregation,
    stats            = stats
  )
  
  if (return_network) {
    result$network <- final_network
  }
  
  result
}
test <- run_one_seed(1, k_min = 2, k_max = 9, alpha = 2.1, rho = 0.7, r = -0.1, return_network = TRUE)

base = 1253281123
seed = base + 1
# use this as the network structure for an otree session:
# cbind(degree(test$network),V(test$network)$role)

# convert to adjacency matrix
adj_matrix <- as.matrix(as_adjacency_matrix(test$network))

# get roles
role_vector <- ifelse(V(test$network)$role == "trendsetter", 1, 0)
# create a list to store the network data
net <- list(adj_matrix = adj_matrix, role_vector = role_vector)
# save the list as a JSON file
write_json(net, "network_test_n10.json")
# random
test <- run_one_sw(i = 15, beta = 1, pmin = 0.15, return_network = TRUE)

# fplot_graph(test$network)
# use this as the network structure for an otree session:
# cbind(degree(test$network),V(test$network)$role)

# convert to adjacency matrix
adj_matrix <- as.matrix(as_adjacency_matrix(test$network))

# get roles
role_vector <- ifelse(V(test$network)$role == "trendsetter", 1, 0)
# create a list to store the network data
net <- list(adj_matrix = adj_matrix, role_vector = role_vector)
# save the list as a JSON file
write_json(net, "network_test_n10_random.json")
LS0tDQp0aXRsZTogIkV4cGVyaW1lbnQiDQpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliDQpsaW5rLWNpdGF0aW9uczogdHJ1ZQ0KZGF0ZTogIkxhc3QgY29tcGlsZWQgb24gYHIgZm9ybWF0KFN5cy50aW1lKCksICclZC0lbS0lWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICBjc3M6IHR3ZWFrcy5jc3MNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCi0tLQ0KDQpgYGB7ciwgZ2xvYmFsc2V0dGluZ3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QzIikpDQpvcHRpb25zKHdpZHRoID0gMTAwKQ0KcmdsOjpzZXR1cEtuaXRyKCkNCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCmBgYA0KDQpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0NCmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpDQoja2xpcHB5OjprbGlwcHkoY29sb3IgPSAnZGFya3JlZCcpDQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpDQpgYGANCg0KLS0tDQoNCiMgR2V0dGluZyBzdGFydGVkDQoNClRvIGNvcHkgdGhlIGNvZGUsIGNsaWNrIHRoZSBidXR0b24gaW4gdGhlIHVwcGVyIHJpZ2h0IGNvcm5lciBvZiB0aGUgY29kZS1jaHVua3MuDQoNCiMjIGNsZWFuIHVwDQoNCmBgYHtyLCBjbGVhbl91cCwgcmVzdWx0cz0naGlkZSd9DQpybShsaXN0PWxzKCkpDQpnYygpDQpgYGANCg0KPGJyPg0KDQojIyBjdXN0b20gZnVuY3Rpb25zDQoNCldlIGRlZmluZWQgYSBudW1iZXIgY3VzdG9tIGZ1bmN0aW9ucywgYXQgYHIgeGZ1bjo6ZW1iZWRfZmlsZSgiLi9jdXN0b21fZnVuY3Rpb25zLlIiKWAuDQoNCmBgYHtyLCBjdXN0b21fZnVuY3Rpb25zfQ0Kc291cmNlKCIuL2N1c3RvbV9mdW5jdGlvbnMuUiIpDQpgYGANCg0KPGJyPg0KDQojIyBuZWNlc3NhcnkgcGFja2FnZXMNCg0KLSBgdGlkeXZlcnNlYDogZGF0YSB3cmFuZ2xpbmcNCi0gYGlncmFwaGA6IGdlbmVyYXRlIGFuZCB2aXN1YWxpemUgZ3JhcGhzDQotIGBwYXJhbGxlbGA6IHBhcmFsbGVsIGNvbXB1dGluZyB0byBzcGVlZCB1cCBzaW11bGF0aW9uDQotIGBmb3JlYWNoYDogbG9vcGluZyBpbiBwYXJhbGxlbA0KLSBgZG9QYXJhbGxlbGA6IHBhcmFsbGVsIGJhY2tlbmQgZm9yIGBmb3JlYWNoYA0KLSBgZ2dwbG90MmA6IGRhdGEgdmlzdWFsaXphdGlvbg0KLSBgZ2doNHhgOiBoYWNrcyBmb3IgYGdncGxvdDJgDQotIGBnZ3B1YnJgOiBtYWtlIHZpc3VhbGl6YXRpb25zIHB1YmxpY2F0aW9uLXJlYWR5DQoNCmBgYHtyLCBwYWNrYWdlc30NCnBhY2thZ2VzID0gYygidGlkeXZlcnNlIiwgImlncmFwaCIsICJnZ3Bsb3QyIiwgInBhcmFsbGVsIiwgImRvUGFyYWxsZWwiLCAiZm9yZWFjaCIsICJnZ2g0eCIsICJnZ3B1YnIiLCAicGxvdGx5IiwgIlJDb2xvckJyZXdlciIsICJncmlkIiwgImdyaWRFeHRyYSIsICJwYXRjaHdvcmsiLCAiZ2dwbG90aWZ5IiwgImdncmFwaCIsICJnZ2FuaW1hdGUiLCAiUkNvbG9yQnJld2VyIiwNCiAgICAiZ2d0ZXh0IiwgIm1hZ2ljayIsICJqc29ubGl0ZSIpDQoNCmludmlzaWJsZShmcGFja2FnZS5jaGVjayhwYWNrYWdlcykpDQpybShwYWNrYWdlcykNCmBgYA0KDQotLS0NCg0KIyBFeHBlcmltZW50YWwgY29uZGl0aW9ucw0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsIGZpZy5zaG93PSdob2xkJywgZmlnLmtlZXA9J2FsbCcsIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQ9NX0NCiMgcGljayBvbmUgY29uZmlndXJhdGlvbiB0aGF0IGxpa2VseSBsZWFkcyB0byBhbiB1bnBvcHVsYXIgbm9ybSwgYW5kIGV4cGxvcmUgbXVsdGlwbGUgJ3NlZWRzJzoNCnJ1bl9vbmVfc2VlZCA8LSBmdW5jdGlvbigNCiAgaSwNCiAgYmFzZV9zZWVkID0gMTI1MzI4MTEyMywNCiAgcGFyYW1zID0gbGlzdChzID0gMTUsIGUgPSAxMCwgdyA9IDQwLCB6ID0gNTAsIGxhbWJkYTEgPSA1LCBsYW1iZGEyID0gMS44KSwNCiAgDQogICMgdHdlYWsgbmV0d29yaw0KICBrX21pbiA9IDIsIA0KICBrX21heCA9IDksDQogIGFscGhhID0gMi4xLA0KICByaG8gPSAwLjQsDQogIHIgPSAtMC4xLA0KICANCiAgIyByZXRyaWV2ZSBuZXR3b3JrDQogIHJldHVybl9uZXR3b3JrID0gRkFMU0UNCiAgDQopIHsNCiAgIyBkZXJpdmVkIHNlZWQgZm9yIHRoaXMgcnVuDQogIHNlZWRfaSA8LSBiYXNlX3NlZWQgKyBpDQogIHNldC5zZWVkKHNlZWRfaSkNCg0KICAjIC0tLSBuZXR3b3JrIGNyZWF0aW9uIC0tLQ0KICBkZWdzZXEgPC0gZmRlZ3NlcSgNCiAgICBuICAgICAgPSAxMCwNCiAgICBhbHBoYSAgPSBhbHBoYSwNCiAgICBrX21pbiAgPSBrX21pbiwNCiAgICBrX21heCAgPSBrX21heCwNCiAgICBkaXN0ICAgPSAibG9nLW5vcm1hbCIsICN1c2UgbG9nLW5vcm1hbCANCiAgICBzZWVkICAgPSBzZWVkX2kNCiAgKQ0KDQogIG5ldHdvcmsgPC0gc2FtcGxlX2RlZ3NlcShkZWdzZXEsIG1ldGhvZCA9ICJ2bCIpDQogIA0KICBWKG5ldHdvcmspJHJvbGUgPC0gc2FtcGxlKA0KICAgIGMocmVwKCJ0cmVuZHNldHRlciIsIDIpLCByZXAoImNvbmZvcm1pc3QiLCA4KSkNCiAgKQ0KICANCg0KICByZXdpcmVkX25ldHdvcmsgPC0gZnJld2lyZV9yKG5ldHdvcmssIHIsIHZlcmJvc2UgPSBGQUxTRSwgbWF4X2l0ZXIgPSAxZTUpDQogIGZpbmFsX25ldHdvcmsgICA8LSBmc3dhcF9yaG8ocmV3aXJlZF9uZXR3b3JrLCByaG8sIHZlcmJvc2UgPSBGQUxTRSwgbWF4X2l0ZXIgPSAxZTQpDQogIA0KICAjIC0tLSBzdGF0cyAtLS0NCiAgc3RhdHMgPC0gbGlzdCgNCiAgICBydW4gICAgICAgICAgID0gaSwNCiAgICBzZWVkICAgICAgICAgID0gc2VlZF9pLA0KICAgIG51bV9ub2RlcyAgICAgPSB2Y291bnQoZmluYWxfbmV0d29yayksDQogICAgbnVtX2VkZ2VzICAgICA9IGVjb3VudChmaW5hbF9uZXR3b3JrKSwNCiAgICBhdmdfZGVncmVlICAgID0gbWVhbihkZWdyZWUoZmluYWxfbmV0d29yaykpLA0KICAgIHNkX2RlZ3JlZSAgICAgPSBzZChkZWdyZWUoZmluYWxfbmV0d29yaykpLA0KICAgIG5ldF9kZW5zaXR5ICAgPSBlZGdlX2RlbnNpdHkoZmluYWxfbmV0d29yayksDQogICAgbmV0X2RpYW1ldGVyICA9IGRpYW1ldGVyKGZpbmFsX25ldHdvcmssIGRpcmVjdGVkID0gRkFMU0UsIHVuY29ubmVjdGVkID0gVFJVRSksDQogICAgYXZnX3BhdGhfbGVuICA9IGF2ZXJhZ2UucGF0aC5sZW5ndGgoZmluYWxfbmV0d29yaywgZGlyZWN0ZWQgPSBGQUxTRSksDQogICAgY2x1c3RfY29lZmYgICA9IHRyYW5zaXRpdml0eShmaW5hbF9uZXR3b3JrLCB0eXBlID0gImdsb2JhbCIpLA0KICAgIGFzc29ydF9kZWcgICAgPSBhc3NvcnRhdGl2aXR5X2RlZ3JlZShmaW5hbF9uZXR3b3JrKSwNCiAgICBkZWdfdHJhaXRfY29yID0gZmRlZ3RyYWl0Y29yKGZpbmFsX25ldHdvcmspJGNvciwNCiAgICBjb21wb25lbnRzICAgID0gY29tcG9uZW50cyhmaW5hbF9uZXR3b3JrKSRubw0KICApDQogIA0KICBmcGxvdF9ncmFwaChmaW5hbF9uZXR3b3JrLCBsYXlvdXQgPSBsYXlvdXRfd2l0aF9mcihmaW5hbF9uZXR3b3JrKSkgDQogIA0KICANCiAgIyAtLS0gaW5pdGlhbCBhY3Rpb25zIC0tLQ0KICBWKGZpbmFsX25ldHdvcmspJGFjdGlvbiA8LSBpZmVsc2UoVihmaW5hbF9uZXR3b3JrKSRyb2xlID09ICJ0cmVuZHNldHRlciIsIDEsIDApDQogIA0KICAjIC0tLSBkZXRlcm1pbmlzdGljIHNpbXVsYXRpb24gLS0tDQogIHNpbV9kZXQgPC0gZmFibSgNCiAgICBuZXR3b3JrICAgICAgPSBmaW5hbF9uZXR3b3JrLA0KICAgIHBhcmFtcyAgICAgICA9IHBhcmFtcywNCiAgICBtYXhfcm91bmRzICAgPSA1MCwNCiAgICBtaV90aHJlc2hvbGQgPSAwLjQ5LA0KICAgIGNob2ljZV9ydWxlICA9ICJkZXRlcm1pbmlzdGljIiwNCiAgICBwbG90ICAgICAgICAgPSBUUlVFLA0KICAgIGhpc3RvcmllcyAgICA9IFRSVUUNCiAgKQ0KICANCiAgIyBnZW5lcmF0ZSB0aGUgZ2lmIGZvciB0aGUgY3VycmVudCBuZXR3b3JrDQogIGdpZl9maWxlbmFtZSA8LSBwYXN0ZTAoIi4vZmlndXJlcy9hbmltYXRpb25fbmV0d29ya18iLCBzZWVkX2ksICIuZ2lmIikNCiAgZ2lmX3BhdGggPC0gZm5ldHdvcmtnaWYoZmluYWxfbmV0d29yaywgc2ltX2RldCRkZWNpc2lvbl9oaXN0b3J5LCByb3VuZHMgPSBzaW1fZGV0JGVxdWlsaWJyaXVtJHJvdW5kLCBvdXRwdXRfZGlyID0gIi4vZmlndXJlcyIpDQogICMgcmVuYW1lIHRoZSBnaWYgdG8gbWF0Y2ggdGhlIG5hbWluZyBwYXR0ZXJuDQogIGZpbGUucmVuYW1lKGdpZl9wYXRoLCBnaWZfZmlsZW5hbWUpDQoNCiAgaWYgKCFpcy5udWxsKHNpbV9kZXQkcGxvdCkpIHsNCiAgICBwcmludChzaW1fZGV0JHBsb3QpDQogIH0NCiAgDQogICMgLS0tIHByb2JhYmlsaXN0aWMgc2ltdWxhdGlvbiAtLS0NCiAgc2ltX3Byb2IgPC0gZmFibSgNCiAgICBuZXR3b3JrICAgICAgICAgICAgICAgID0gZmluYWxfbmV0d29yaywNCiAgICBwYXJhbXMgICAgICAgICAgICAgICAgID0gcGFyYW1zLA0KICAgIG1heF9yb3VuZHMgICAgICAgICAgICAgPSAxMDAsDQogICAgbWlfdGhyZXNob2xkICAgICAgICAgICA9IDAuNDksDQogICAgY2hvaWNlX3J1bGUgICAgICAgICAgICA9ICJwcm9iYWJpbGlzdGljIiwNCiAgICBzdGFibGVfd2luZG93ICAgICAgICAgID0gOCwgICAjIHRoZSBsZW5ndGggb2YgdGhlIHdpbmRvdyBvZiBhZG9wdGlvbiB2YWx1ZXMNCiAgICByZXF1aXJlZF9zdGFibGVfcm91bmRzID0gMjAsICMgbnVtYmVyIG9mIHdpbmRvd3MgbmVlZGVkIHRvIGRlY2xhcmUgZXF1aWxpYnJpdW0NCiAgICBwbG90ICAgICAgICAgICAgICAgICAgID0gVFJVRQ0KICApDQogIGlmICghaXMubnVsbChzaW1fcHJvYiRwbG90KSkgew0KICAgIHByaW50KHNpbV9wcm9iJHBsb3QpDQogIH0NCiAgDQogICByZXN1bHQgPC0gbGlzdCgNCiAgICBzZWdyZWdhdGlvbl9kZXQgID0gc2ltX2RldCRlcXVpbGlicml1bSRzZWdyZWdhdGlvbiwNCiAgICBzZWdyZWdhdGlvbl9wcm9iID0gc2ltX3Byb2IkZXF1aWxpYnJpdW0kc2VncmVnYXRpb24sDQogICAgc3RhdHMgICAgICAgICAgICA9IHN0YXRzDQogICkNCiAgDQogIGlmIChyZXR1cm5fbmV0d29yaykgew0KICAgIHJlc3VsdCRuZXR3b3JrIDwtIGZpbmFsX25ldHdvcmsNCiAgfQ0KICANCiAgcmVzdWx0DQp9DQoNCiMjIyMNCg0KcnVuX29uZV9zdyA8LSBmdW5jdGlvbigNCiAgaSwNCiAgYmFzZV9zZWVkID0gMTI1MzI4MTEyMywNCiAgbW9kZWwgPSAid2F0dHMtc3Ryb2dhdHoiLA0KICBiZXRhID0gMCwNCiAgbmVpID0gMywNCiAgY2xpcXVlX3NpemUgPSA1LA0KICBwbWluID0gMC4xLA0KICANCiAgcGFyYW1zID0gbGlzdChzID0gMTUsIGUgPSAxMCwgdyA9IDQwLCB6ID0gNTAsIGxhbWJkYTEgPSA1LCBsYW1iZGEyID0gMS44KSwNCiAgDQogICMgcmV0cmlldmUgbmV0d29yaw0KICByZXR1cm5fbmV0d29yayA9IEZBTFNFDQogIA0KKSB7DQogICMgZGVyaXZlZCBzZWVkIGZvciB0aGlzIHJ1bg0KICBzZWVkX2kgPC0gYmFzZV9zZWVkICsgaQ0KICBzZXQuc2VlZChzZWVkX2kpDQoNCiAgaWYgKG1vZGVsID09ICJ3YXR0cy1zdHJvZ2F0eiIpIHsNCiAgICBuZXR3b3JrIDwtIHNhbXBsZV9zbWFsbHdvcmxkKGRpbSA9IDEsIHNpemUgPSAxMCwgbmVpID0gMywgcCA9IGJldGEpIH0NCiAgZWxzZSBpZiAobW9kZWwgPT0gImNhdmVtYW4iKSB7DQogICAgbmV0d29yayA8LSBzaW11bGF0ZV9jYXZlbWFuKG4gPSAxMCwgY2xpcXVlX3NpemUgPSBjbGlxdWVfc2l6ZSkgDQogIH0NCg0KICBWKG5ldHdvcmspJHJvbGUgPC0gc2FtcGxlKA0KICAgIGMocmVwKCJ0cmVuZHNldHRlciIsIDIpLCByZXAoImNvbmZvcm1pc3QiLCA4KSkNCiAgKQ0KICANCiAgDQogIGZpbmFsX25ldHdvcmsgPC0gbmV0d29yaw0KDQogIA0KICAjIC0tLSBzdGF0cyAtLS0NCiAgc3RhdHMgPC0gbGlzdCgNCiAgICBydW4gICAgICAgICAgID0gaSwNCiAgICBzZWVkICAgICAgICAgID0gc2VlZF9pLA0KICAgIG51bV9ub2RlcyAgICAgPSB2Y291bnQoZmluYWxfbmV0d29yayksDQogICAgbnVtX2VkZ2VzICAgICA9IGVjb3VudChmaW5hbF9uZXR3b3JrKSwNCiAgICBhdmdfZGVncmVlICAgID0gbWVhbihkZWdyZWUoZmluYWxfbmV0d29yaykpLA0KICAgIHNkX2RlZ3JlZSAgICAgPSBzZChkZWdyZWUoZmluYWxfbmV0d29yaykpLA0KICAgIG5ldF9kZW5zaXR5ICAgPSBlZGdlX2RlbnNpdHkoZmluYWxfbmV0d29yayksDQogICAgbmV0X2RpYW1ldGVyICA9IGRpYW1ldGVyKGZpbmFsX25ldHdvcmssIGRpcmVjdGVkID0gRkFMU0UsIHVuY29ubmVjdGVkID0gVFJVRSksDQogICAgYXZnX3BhdGhfbGVuICA9IGF2ZXJhZ2UucGF0aC5sZW5ndGgoZmluYWxfbmV0d29yaywgZGlyZWN0ZWQgPSBGQUxTRSksDQogICAgY2x1c3RfY29lZmYgICA9IHRyYW5zaXRpdml0eShmaW5hbF9uZXR3b3JrLCB0eXBlID0gImdsb2JhbCIpLA0KICAgIGFzc29ydF9kZWcgICAgPSBhc3NvcnRhdGl2aXR5X2RlZ3JlZShmaW5hbF9uZXR3b3JrKSwNCiAgICBkZWdfdHJhaXRfY29yID0gZmRlZ3RyYWl0Y29yKGZpbmFsX25ldHdvcmspJGNvciwNCiAgICBjb21wb25lbnRzICAgID0gY29tcG9uZW50cyhmaW5hbF9uZXR3b3JrKSRubw0KICApDQogIA0KICBzdGF0c19kZiA8LSBkYXRhLmZyYW1lKA0KICAgIE1ldHJpYyA9IG5hbWVzKHN0YXRzKSwNCiAgICBWYWx1ZSAgPSB1bmxpc3Qoc3RhdHMpLA0KICAgIHJvdy5uYW1lcyA9IE5VTEwNCiAgKQ0KICANCiAgI3ByaW50KHN0YXRzX2RmKQ0KICBmcGxvdF9ncmFwaChmaW5hbF9uZXR3b3JrLCBsYXlvdXQgPSBsYXlvdXQua2FtYWRhLmthd2FpKGZpbmFsX25ldHdvcmspKQ0KICANCg0KICAjIC0tLSBpbml0aWFsIGFjdGlvbnMgLS0tDQogIFYoZmluYWxfbmV0d29yaykkYWN0aW9uIDwtIGlmZWxzZShWKGZpbmFsX25ldHdvcmspJHJvbGUgPT0gInRyZW5kc2V0dGVyIiwgMSwgMCkNCiAgDQogICMgLS0tIGRldGVybWluaXN0aWMgc2ltdWxhdGlvbiAtLS0NCiAgc2ltX2RldCA8LSBmYWJtKA0KICAgIG5ldHdvcmsgICAgICA9IGZpbmFsX25ldHdvcmssDQogICAgcGFyYW1zICAgICAgID0gcGFyYW1zLA0KICAgIG1heF9yb3VuZHMgICA9IDUwLA0KICAgIG1pX3RocmVzaG9sZCA9IDAuNDksDQogICAgY2hvaWNlX3J1bGUgID0gImRldGVybWluaXN0aWMiLA0KICAgIHBsb3QgICAgICAgICA9IFRSVUUsDQogICAgaGlzdG9yaWVzICAgID0gVFJVRQ0KICApDQoNCiAgIyBnZW5lcmF0ZSB0aGUgZ2lmIGZvciB0aGUgY3VycmVudCBuZXR3b3JrDQogIGdpZl9maWxlbmFtZSA8LSBwYXN0ZTAoIi4vZmlndXJlcy9hbmltYXRpb25fbmV0d29ya18iLCBzZWVkX2ksICIuZ2lmIikNCiAgZ2lmX3BhdGggPC0gZm5ldHdvcmtnaWYoZmluYWxfbmV0d29yaywgc2ltX2RldCRkZWNpc2lvbl9oaXN0b3J5LCByb3VuZHMgPSBzaW1fZGV0JGVxdWlsaWJyaXVtJHJvdW5kLCBvdXRwdXRfZGlyID0gIi4vZmlndXJlcyIpDQogICMgcmVuYW1lIHRoZSBnaWYgdG8gbWF0Y2ggdGhlIG5hbWluZyBwYXR0ZXJuDQogIGZpbGUucmVuYW1lKGdpZl9wYXRoLCBnaWZfZmlsZW5hbWUpDQogIA0KICANCiAgaWYgKCFpcy5udWxsKHNpbV9kZXQkcGxvdCkpIHsNCiAgICBwcmludChzaW1fZGV0JHBsb3QpDQogIH0NCiAgDQogICMgLS0tIHByb2JhYmlsaXN0aWMgc2ltdWxhdGlvbiAtLS0NCiAgc2ltX3Byb2IgPC0gZmFibSgNCiAgICBuZXR3b3JrICAgICAgICAgICAgICAgID0gZmluYWxfbmV0d29yaywNCiAgICBwYXJhbXMgICAgICAgICAgICAgICAgID0gcGFyYW1zLA0KICAgIG1heF9yb3VuZHMgICAgICAgICAgICAgPSAxMDAsDQogICAgbWlfdGhyZXNob2xkICAgICAgICAgICA9IDAuNDksDQogICAgY2hvaWNlX3J1bGUgICAgICAgICAgICA9ICJwcm9iYWJpbGlzdGljIiwNCiAgICBzdGFibGVfd2luZG93ICAgICAgICAgID0gOCwgICAjIHRoZSBsZW5ndGggb2YgdGhlIHdpbmRvdyBvZiBhZG9wdGlvbiB2YWx1ZXMNCiAgICByZXF1aXJlZF9zdGFibGVfcm91bmRzID0gMjAsICMgbnVtYmVyIG9mIHdpbmRvd3MgbmVlZGVkIHRvIGRlY2xhcmUgZXF1aWxpYnJpdW0NCiAgICBwbG90ICAgICAgICAgICAgICAgICAgID0gVFJVRQ0KICApDQogIGlmICghaXMubnVsbChzaW1fcHJvYiRwbG90KSkgew0KICAgIHByaW50KHNpbV9wcm9iJHBsb3QpDQogIH0NCiAgDQogIA0KICByZXN1bHQgPC0gbGlzdCgNCiAgICBzZWdyZWdhdGlvbl9kZXQgID0gc2ltX2RldCRlcXVpbGlicml1bSRzZWdyZWdhdGlvbiwNCiAgICBzZWdyZWdhdGlvbl9wcm9iID0gc2ltX3Byb2IkZXF1aWxpYnJpdW0kc2VncmVnYXRpb24sDQogICAgc3RhdHMgICAgICAgICAgICA9IHN0YXRzDQogICkNCiAgDQogIGlmIChyZXR1cm5fbmV0d29yaykgew0KICAgIHJlc3VsdCRuZXR3b3JrIDwtIGZpbmFsX25ldHdvcmsNCiAgfQ0KICANCiAgcmVzdWx0DQp9DQpgYGANCg0KDQpgYGB7cn0NCnRlc3QgPC0gcnVuX29uZV9zZWVkKDEsIGtfbWluID0gMiwga19tYXggPSA5LCBhbHBoYSA9IDIuMSwgcmhvID0gMC43LCByID0gLTAuMSwgcmV0dXJuX25ldHdvcmsgPSBUUlVFKQ0KDQpiYXNlID0gMTI1MzI4MTEyMw0Kc2VlZCA9IGJhc2UgKyAxDQpgYGAgDQoNCmBgYHtyLCBldmFsID0gRkFMU0V9DQojIHVzZSB0aGlzIGFzIHRoZSBuZXR3b3JrIHN0cnVjdHVyZSBmb3IgYW4gb3RyZWUgc2Vzc2lvbjoNCiNjYmluZChkZWdyZWUodGVzdCRuZXR3b3JrKSxWKHRlc3QkbmV0d29yaykkcm9sZSkNCg0KIyBjb252ZXJ0IHRvIGFkamFjZW5jeSBtYXRyaXgNCmFkal9tYXRyaXggPC0gYXMubWF0cml4KGFzX2FkamFjZW5jeV9tYXRyaXgodGVzdCRuZXR3b3JrKSkNCg0KI2dldCByb2xlcw0Kcm9sZV92ZWN0b3IgPC0gaWZlbHNlKFYodGVzdCRuZXR3b3JrKSRyb2xlID09ICJ0cmVuZHNldHRlciIsMSwwKQ0KIyBjcmVhdGUgYSBsaXN0IHRvIHN0b3JlIHRoZSBuZXR3b3JrIGRhdGENCm5ldCA8LSBsaXN0KGFkal9tYXRyaXggPSBhZGpfbWF0cml4LCByb2xlX3ZlY3RvciA9IHJvbGVfdmVjdG9yKQ0KIyBzYXZlIHRoZSBsaXN0IGFzIGEgSlNPTiBmaWxlDQp3cml0ZV9qc29uKG5ldCwgIm5ldHdvcmtfdGVzdF9uMTAuanNvbiIpDQpgYGANCg0KDQpgYGB7cn0NCiNyYW5kb20NCnRlc3QgPC0gcnVuX29uZV9zdyhpPTE1LCBiZXRhPTEsIHBtaW49LjE1LCByZXR1cm5fbmV0d29yayA9IFRSVUUgKQ0KI2ZwbG90X2dyYXBoKHRlc3QkbmV0d29yaykNCmBgYA0KYGBge3IsZXZhbD1GQUxTRX0NCiMgdXNlIHRoaXMgYXMgdGhlIG5ldHdvcmsgc3RydWN0dXJlIGZvciBhbiBvdHJlZSBzZXNzaW9uOg0KI2NiaW5kKGRlZ3JlZSh0ZXN0JG5ldHdvcmspLFYodGVzdCRuZXR3b3JrKSRyb2xlKQ0KDQojIGNvbnZlcnQgdG8gYWRqYWNlbmN5IG1hdHJpeA0KYWRqX21hdHJpeCA8LSBhcy5tYXRyaXgoYXNfYWRqYWNlbmN5X21hdHJpeCh0ZXN0JG5ldHdvcmspKQ0KDQojZ2V0IHJvbGVzDQpyb2xlX3ZlY3RvciA8LSBpZmVsc2UoVih0ZXN0JG5ldHdvcmspJHJvbGUgPT0gInRyZW5kc2V0dGVyIiwxLDApDQojIGNyZWF0ZSBhIGxpc3QgdG8gc3RvcmUgdGhlIG5ldHdvcmsgZGF0YQ0KbmV0IDwtIGxpc3QoYWRqX21hdHJpeCA9IGFkal9tYXRyaXgsIHJvbGVfdmVjdG9yID0gcm9sZV92ZWN0b3IpDQojIHNhdmUgdGhlIGxpc3QgYXMgYSBKU09OIGZpbGUNCndyaXRlX2pzb24obmV0LCAibmV0d29ya190ZXN0X24xMF9yYW5kb20uanNvbiIpDQpgYGANCg==


Copyright © Rob Franken