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 = 12532812,
  params = list(s = 15, e = 10, w = 40, z = 50, lambda1 = 5, lambda2 = 1.8),
  
  # tweak network
  k_min = 3, 
  k_max = 15,
  alpha = 2.4,
  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 = 20,
    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", 3), rep("conformist", 17))
  )
  
  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   = 35,
    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             = 50,
    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
}

#and one "random" network
run_one_sw <- function(
  i,
  base_seed = 12532812,
  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 = 20, nei = 3, p = beta) }
  else if (model == "caveman") {
    network <- simulate_caveman(n = 20, clique_size = clique_size) 
  }

  V(network)$role <- sample(
    c(rep("trendsetter", 3), rep("conformist", 17))
  )
  
  
  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   = 35,
    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             = 50,
    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
}

2.1 SRDA computer-lab demonstation

2.1.1 WG1: heterogenous degree distribution; centralized “fanatics”:

test <- run_one_seed(38, k_min = 4, k_max = 16, alpha = 2.3, rho = 1, r = -0.2, return_network = TRUE)

table(degree(test$network))
#> 
#>  4  5  6  8  9 16 
#> 12  1  1  1  3  2
cbind(degree(test$network), V(test$network)$role)
#>       [,1] [,2]         
#>  [1,] "4"  "conformist" 
#>  [2,] "16" "trendsetter"
#>  [3,] "4"  "conformist" 
#>  [4,] "4"  "conformist" 
#>  [5,] "4"  "conformist" 
#>  [6,] "4"  "conformist" 
#>  [7,] "4"  "conformist" 
#>  [8,] "5"  "conformist" 
#>  [9,] "16" "trendsetter"
#> [10,] "9"  "conformist" 
#> [11,] "9"  "trendsetter"
#> [12,] "8"  "conformist" 
#> [13,] "4"  "conformist" 
#> [14,] "4"  "conformist" 
#> [15,] "4"  "conformist" 
#> [16,] "6"  "conformist" 
#> [17,] "4"  "conformist" 
#> [18,] "9"  "conformist" 
#> [19,] "4"  "conformist" 
#> [20,] "4"  "conformist"
base = 12532812
seed = base + 38
knitr::include_graphics(paste0("./figures/animation_network_", seed, ".gif"))

# 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, "SRDA_n20_heterogenous.json")

2.1.2 WG2: homogenous degree distribution–“random network”

test <- run_one_sw(i = 513, beta = 1, pmin = 0.1, return_network = TRUE)

table(degree(test$network))
#> 
#> 2 3 4 5 6 7 8 9 
#> 1 1 2 4 2 6 3 1
cbind(degree(test$network), V(test$network)$role)
#>       [,1] [,2]         
#>  [1,] "4"  "conformist" 
#>  [2,] "8"  "conformist" 
#>  [3,] "7"  "conformist" 
#>  [4,] "7"  "conformist" 
#>  [5,] "8"  "conformist" 
#>  [6,] "7"  "conformist" 
#>  [7,] "7"  "conformist" 
#>  [8,] "3"  "trendsetter"
#>  [9,] "4"  "conformist" 
#> [10,] "5"  "conformist" 
#> [11,] "8"  "conformist" 
#> [12,] "6"  "conformist" 
#> [13,] "6"  "conformist" 
#> [14,] "5"  "conformist" 
#> [15,] "2"  "trendsetter"
#> [16,] "5"  "conformist" 
#> [17,] "7"  "conformist" 
#> [18,] "5"  "conformist" 
#> [19,] "9"  "conformist" 
#> [20,] "7"  "trendsetter"
base_seed = 12532812
seed = base_seed + 513

knitr::include_graphics(paste0("./figures/animation_network_", seed, ".gif"))

# 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, "SRDA_n20_random.json")
LS0tDQp0aXRsZTogIkV4cGVyaW1lbnQgaW4gU1JEQSBjb21wdXRlci1sYWIiDQpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliDQpsaW5rLWNpdGF0aW9uczogdHJ1ZQ0KZGF0ZTogIkxhc3QgY29tcGlsZWQgb24gYHIgZm9ybWF0KFN5cy50aW1lKCksICclZC0lbS0lWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICBjc3M6IHR3ZWFrcy5jc3MNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCi0tLQ0KDQpgYGB7ciwgZ2xvYmFsc2V0dGluZ3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QzIikpDQpvcHRpb25zKHdpZHRoID0gMTAwKQ0KcmdsOjpzZXR1cEtuaXRyKCkNCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCmBgYA0KDQpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0NCmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpDQoja2xpcHB5OjprbGlwcHkoY29sb3IgPSAnZGFya3JlZCcpDQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpDQpgYGANCg0KLS0tDQoNCiMgR2V0dGluZyBzdGFydGVkDQoNClRvIGNvcHkgdGhlIGNvZGUsIGNsaWNrIHRoZSBidXR0b24gaW4gdGhlIHVwcGVyIHJpZ2h0IGNvcm5lciBvZiB0aGUgY29kZS1jaHVua3MuDQoNCiMjIGNsZWFuIHVwDQoNCmBgYHtyLCBjbGVhbl91cCwgcmVzdWx0cz0naGlkZSd9DQpybShsaXN0PWxzKCkpDQpnYygpDQpgYGANCg0KPGJyPg0KDQojIyBjdXN0b20gZnVuY3Rpb25zDQoNCldlIGRlZmluZWQgYSBudW1iZXIgY3VzdG9tIGZ1bmN0aW9ucywgYXQgYHIgeGZ1bjo6ZW1iZWRfZmlsZSgiLi9jdXN0b21fZnVuY3Rpb25zLlIiKWAuDQoNCmBgYHtyLCBjdXN0b21fZnVuY3Rpb25zfQ0Kc291cmNlKCIuL2N1c3RvbV9mdW5jdGlvbnMuUiIpDQpgYGANCg0KPGJyPg0KDQojIyBuZWNlc3NhcnkgcGFja2FnZXMNCg0KLSBgdGlkeXZlcnNlYDogZGF0YSB3cmFuZ2xpbmcNCi0gYGlncmFwaGA6IGdlbmVyYXRlIGFuZCB2aXN1YWxpemUgZ3JhcGhzDQotIGBwYXJhbGxlbGA6IHBhcmFsbGVsIGNvbXB1dGluZyB0byBzcGVlZCB1cCBzaW11bGF0aW9uDQotIGBmb3JlYWNoYDogbG9vcGluZyBpbiBwYXJhbGxlbA0KLSBgZG9QYXJhbGxlbGA6IHBhcmFsbGVsIGJhY2tlbmQgZm9yIGBmb3JlYWNoYA0KLSBgZ2dwbG90MmA6IGRhdGEgdmlzdWFsaXphdGlvbg0KLSBgZ2doNHhgOiBoYWNrcyBmb3IgYGdncGxvdDJgDQotIGBnZ3B1YnJgOiBtYWtlIHZpc3VhbGl6YXRpb25zIHB1YmxpY2F0aW9uLXJlYWR5DQoNCmBgYHtyLCBwYWNrYWdlc30NCnBhY2thZ2VzID0gYygidGlkeXZlcnNlIiwgImlncmFwaCIsICJnZ3Bsb3QyIiwgInBhcmFsbGVsIiwgImRvUGFyYWxsZWwiLCAiZm9yZWFjaCIsICJnZ2g0eCIsICJnZ3B1YnIiLCAicGxvdGx5IiwgIlJDb2xvckJyZXdlciIsICJncmlkIiwgImdyaWRFeHRyYSIsICJwYXRjaHdvcmsiLCAiZ2dwbG90aWZ5IiwgImdncmFwaCIsICJnZ2FuaW1hdGUiLCAiUkNvbG9yQnJld2VyIiwNCiAgICAiZ2d0ZXh0IiwgIm1hZ2ljayIsICJqc29ubGl0ZSIpDQoNCmludmlzaWJsZShmcGFja2FnZS5jaGVjayhwYWNrYWdlcykpDQpybShwYWNrYWdlcykNCmBgYA0KDQotLS0NCg0KIyBFeHBlcmltZW50YWwgY29uZGl0aW9ucw0KDQpgYGB7ciwgZWNobz1UUlVFLCBmaWcuc2hvdz0naG9sZCcsIGZpZy5rZWVwPSdhbGwnLCBtZXNzYWdlPUZBTFNFLCBmaWcuaGVpZ2h0PTV9DQojIHBpY2sgb25lIGNvbmZpZ3VyYXRpb24gdGhhdCBsaWtlbHkgbGVhZHMgdG8gYW4gdW5wb3B1bGFyIG5vcm0sIGFuZCBleHBsb3JlIG11bHRpcGxlICdzZWVkcyc6DQpydW5fb25lX3NlZWQgPC0gZnVuY3Rpb24oDQogIGksDQogIGJhc2Vfc2VlZCA9IDEyNTMyODEyLA0KICBwYXJhbXMgPSBsaXN0KHMgPSAxNSwgZSA9IDEwLCB3ID0gNDAsIHogPSA1MCwgbGFtYmRhMSA9IDUsIGxhbWJkYTIgPSAxLjgpLA0KICANCiAgIyB0d2VhayBuZXR3b3JrDQogIGtfbWluID0gMywgDQogIGtfbWF4ID0gMTUsDQogIGFscGhhID0gMi40LA0KICByaG8gPSAwLjQsDQogIHIgPSAtMC4xLA0KICANCiAgIyByZXRyaWV2ZSBuZXR3b3JrDQogIHJldHVybl9uZXR3b3JrID0gRkFMU0UNCiAgDQopIHsNCiAgIyBkZXJpdmVkIHNlZWQgZm9yIHRoaXMgcnVuDQogIHNlZWRfaSA8LSBiYXNlX3NlZWQgKyBpDQogIHNldC5zZWVkKHNlZWRfaSkNCg0KICAjIC0tLSBuZXR3b3JrIGNyZWF0aW9uIC0tLQ0KICBkZWdzZXEgPC0gZmRlZ3NlcSgNCiAgICBuID0gMjAsDQogICAgYWxwaGEgID0gYWxwaGEsDQogICAga19taW4gID0ga19taW4sDQogICAga19tYXggID0ga19tYXgsDQogICAgZGlzdCAgID0gImxvZy1ub3JtYWwiLCAjdXNlIGxvZy1ub3JtYWwgDQogICAgc2VlZCAgID0gc2VlZF9pDQogICkNCg0KICBuZXR3b3JrIDwtIHNhbXBsZV9kZWdzZXEoZGVnc2VxLCBtZXRob2QgPSAidmwiKQ0KICANCiAgDQogICBWKG5ldHdvcmspJHJvbGUgPC0gc2FtcGxlKA0KICAgIGMocmVwKCJ0cmVuZHNldHRlciIsIDMpLCByZXAoImNvbmZvcm1pc3QiLCAxNykpDQogICkNCiAgDQogIHJld2lyZWRfbmV0d29yayA8LSBmcmV3aXJlX3IobmV0d29yaywgciwgdmVyYm9zZSA9IEZBTFNFLCBtYXhfaXRlciA9IDFlNSkNCiAgZmluYWxfbmV0d29yayAgIDwtIGZzd2FwX3JobyhyZXdpcmVkX25ldHdvcmssIHJobywgdmVyYm9zZSA9IEZBTFNFLCBtYXhfaXRlciA9IDFlNCkNCiAgDQogICMgLS0tIHN0YXRzIC0tLQ0KICBzdGF0cyA8LSBsaXN0KA0KICAgIHJ1biAgICAgICAgICAgPSBpLA0KICAgIHNlZWQgICAgICAgICAgPSBzZWVkX2ksDQogICAgbnVtX25vZGVzICAgICA9IHZjb3VudChmaW5hbF9uZXR3b3JrKSwNCiAgICBudW1fZWRnZXMgICAgID0gZWNvdW50KGZpbmFsX25ldHdvcmspLA0KICAgIGF2Z19kZWdyZWUgICAgPSBtZWFuKGRlZ3JlZShmaW5hbF9uZXR3b3JrKSksDQogICAgc2RfZGVncmVlICAgICA9IHNkKGRlZ3JlZShmaW5hbF9uZXR3b3JrKSksDQogICAgbmV0X2RlbnNpdHkgICA9IGVkZ2VfZGVuc2l0eShmaW5hbF9uZXR3b3JrKSwNCiAgICBuZXRfZGlhbWV0ZXIgID0gZGlhbWV0ZXIoZmluYWxfbmV0d29yaywgZGlyZWN0ZWQgPSBGQUxTRSwgdW5jb25uZWN0ZWQgPSBUUlVFKSwNCiAgICBhdmdfcGF0aF9sZW4gID0gYXZlcmFnZS5wYXRoLmxlbmd0aChmaW5hbF9uZXR3b3JrLCBkaXJlY3RlZCA9IEZBTFNFKSwNCiAgICBjbHVzdF9jb2VmZiAgID0gdHJhbnNpdGl2aXR5KGZpbmFsX25ldHdvcmssIHR5cGUgPSAiZ2xvYmFsIiksDQogICAgYXNzb3J0X2RlZyAgICA9IGFzc29ydGF0aXZpdHlfZGVncmVlKGZpbmFsX25ldHdvcmspLA0KICAgIGRlZ190cmFpdF9jb3IgPSBmZGVndHJhaXRjb3IoZmluYWxfbmV0d29yaykkY29yLA0KICAgIGNvbXBvbmVudHMgICAgPSBjb21wb25lbnRzKGZpbmFsX25ldHdvcmspJG5vDQogICkNCiAgDQogIGZwbG90X2dyYXBoKGZpbmFsX25ldHdvcmssIGxheW91dCA9IGxheW91dF93aXRoX2ZyKGZpbmFsX25ldHdvcmspKSANCiAgDQogIA0KICAjIC0tLSBpbml0aWFsIGFjdGlvbnMgLS0tDQogIFYoZmluYWxfbmV0d29yaykkYWN0aW9uIDwtIGlmZWxzZShWKGZpbmFsX25ldHdvcmspJHJvbGUgPT0gInRyZW5kc2V0dGVyIiwgMSwgMCkNCiAgDQogICMgLS0tIGRldGVybWluaXN0aWMgc2ltdWxhdGlvbiAtLS0NCiAgc2ltX2RldCA8LSBmYWJtKA0KICAgIG5ldHdvcmsgICAgICA9IGZpbmFsX25ldHdvcmssDQogICAgcGFyYW1zICAgICAgID0gcGFyYW1zLA0KICAgIG1heF9yb3VuZHMgICA9IDM1LA0KICAgIG1pX3RocmVzaG9sZCA9IDAuNDksDQogICAgY2hvaWNlX3J1bGUgID0gImRldGVybWluaXN0aWMiLA0KICAgIHBsb3QgICAgICAgICA9IFRSVUUsDQogICAgaGlzdG9yaWVzICAgID0gVFJVRQ0KICApDQogIA0KICAjIGdlbmVyYXRlIHRoZSBnaWYgZm9yIHRoZSBjdXJyZW50IG5ldHdvcmsNCiAgZ2lmX2ZpbGVuYW1lIDwtIHBhc3RlMCgiLi9maWd1cmVzL2FuaW1hdGlvbl9uZXR3b3JrXyIsIHNlZWRfaSwgIi5naWYiKQ0KICBnaWZfcGF0aCA8LSBmbmV0d29ya2dpZihmaW5hbF9uZXR3b3JrLCBzaW1fZGV0JGRlY2lzaW9uX2hpc3RvcnksIHJvdW5kcyA9IHNpbV9kZXQkZXF1aWxpYnJpdW0kcm91bmQsIG91dHB1dF9kaXIgPSAiLi9maWd1cmVzIikNCiAgIyByZW5hbWUgdGhlIGdpZiB0byBtYXRjaCB0aGUgbmFtaW5nIHBhdHRlcm4NCiAgZmlsZS5yZW5hbWUoZ2lmX3BhdGgsIGdpZl9maWxlbmFtZSkNCg0KICBpZiAoIWlzLm51bGwoc2ltX2RldCRwbG90KSkgew0KICAgIHByaW50KHNpbV9kZXQkcGxvdCkNCiAgfQ0KICANCiAgIyAtLS0gcHJvYmFiaWxpc3RpYyBzaW11bGF0aW9uIC0tLQ0KICBzaW1fcHJvYiA8LSBmYWJtKA0KICAgIG5ldHdvcmsgICAgICAgICAgICAgICAgPSBmaW5hbF9uZXR3b3JrLA0KICAgIHBhcmFtcyAgICAgICAgICAgICAgICAgPSBwYXJhbXMsDQogICAgbWF4X3JvdW5kcyAgICAgICAgICAgICA9IDUwLA0KICAgIG1pX3RocmVzaG9sZCAgICAgICAgICAgPSAwLjQ5LA0KICAgIGNob2ljZV9ydWxlICAgICAgICAgICAgPSAicHJvYmFiaWxpc3RpYyIsDQogICAgc3RhYmxlX3dpbmRvdyAgICAgICAgICA9IDgsICAgIyB0aGUgbGVuZ3RoIG9mIHRoZSB3aW5kb3cgb2YgYWRvcHRpb24gdmFsdWVzDQogICAgcmVxdWlyZWRfc3RhYmxlX3JvdW5kcyA9IDIwLCAjIG51bWJlciBvZiB3aW5kb3dzIG5lZWRlZCB0byBkZWNsYXJlIGVxdWlsaWJyaXVtDQogICAgcGxvdCAgICAgICAgICAgICAgICAgICA9IFRSVUUNCiAgKQ0KICBpZiAoIWlzLm51bGwoc2ltX3Byb2IkcGxvdCkpIHsNCiAgICBwcmludChzaW1fcHJvYiRwbG90KQ0KICB9DQogIA0KICAgcmVzdWx0IDwtIGxpc3QoDQogICAgc2VncmVnYXRpb25fZGV0ICA9IHNpbV9kZXQkZXF1aWxpYnJpdW0kc2VncmVnYXRpb24sDQogICAgc2VncmVnYXRpb25fcHJvYiA9IHNpbV9wcm9iJGVxdWlsaWJyaXVtJHNlZ3JlZ2F0aW9uLA0KICAgIHN0YXRzICAgICAgICAgICAgPSBzdGF0cw0KICApDQogIA0KICBpZiAocmV0dXJuX25ldHdvcmspIHsNCiAgICByZXN1bHQkbmV0d29yayA8LSBmaW5hbF9uZXR3b3JrDQogIH0NCiAgDQogIHJlc3VsdA0KfQ0KDQojYW5kIG9uZSAicmFuZG9tIiBuZXR3b3JrDQpydW5fb25lX3N3IDwtIGZ1bmN0aW9uKA0KICBpLA0KICBiYXNlX3NlZWQgPSAxMjUzMjgxMiwNCiAgbW9kZWwgPSAid2F0dHMtc3Ryb2dhdHoiLA0KICBiZXRhID0gMCwNCiAgbmVpID0gMywNCiAgY2xpcXVlX3NpemUgPSA1LA0KICBwbWluID0gMC4xLA0KICANCiAgcGFyYW1zID0gbGlzdChzID0gMTUsIGUgPSAxMCwgdyA9IDQwLCB6ID0gNTAsIGxhbWJkYTEgPSA1LCBsYW1iZGEyID0gMS44KSwNCiAgDQogICMgcmV0cmlldmUgbmV0d29yaw0KICByZXR1cm5fbmV0d29yayA9IEZBTFNFDQogIA0KKSB7DQogICMgZGVyaXZlZCBzZWVkIGZvciB0aGlzIHJ1bg0KICBzZWVkX2kgPC0gYmFzZV9zZWVkICsgaQ0KICBzZXQuc2VlZChzZWVkX2kpDQoNCiAgaWYgKG1vZGVsID09ICJ3YXR0cy1zdHJvZ2F0eiIpIHsNCiAgICBuZXR3b3JrIDwtIHNhbXBsZV9zbWFsbHdvcmxkKGRpbSA9IDEsIHNpemUgPSAyMCwgbmVpID0gMywgcCA9IGJldGEpIH0NCiAgZWxzZSBpZiAobW9kZWwgPT0gImNhdmVtYW4iKSB7DQogICAgbmV0d29yayA8LSBzaW11bGF0ZV9jYXZlbWFuKG4gPSAyMCwgY2xpcXVlX3NpemUgPSBjbGlxdWVfc2l6ZSkgDQogIH0NCg0KICBWKG5ldHdvcmspJHJvbGUgPC0gc2FtcGxlKA0KICAgIGMocmVwKCJ0cmVuZHNldHRlciIsIDMpLCByZXAoImNvbmZvcm1pc3QiLCAxNykpDQogICkNCiAgDQogIA0KICBmaW5hbF9uZXR3b3JrIDwtIG5ldHdvcmsNCg0KICANCiAgIyAtLS0gc3RhdHMgLS0tDQogIHN0YXRzIDwtIGxpc3QoDQogICAgcnVuICAgICAgICAgICA9IGksDQogICAgc2VlZCAgICAgICAgICA9IHNlZWRfaSwNCiAgICBudW1fbm9kZXMgICAgID0gdmNvdW50KGZpbmFsX25ldHdvcmspLA0KICAgIG51bV9lZGdlcyAgICAgPSBlY291bnQoZmluYWxfbmV0d29yayksDQogICAgYXZnX2RlZ3JlZSAgICA9IG1lYW4oZGVncmVlKGZpbmFsX25ldHdvcmspKSwNCiAgICBzZF9kZWdyZWUgICAgID0gc2QoZGVncmVlKGZpbmFsX25ldHdvcmspKSwNCiAgICBuZXRfZGVuc2l0eSAgID0gZWRnZV9kZW5zaXR5KGZpbmFsX25ldHdvcmspLA0KICAgIG5ldF9kaWFtZXRlciAgPSBkaWFtZXRlcihmaW5hbF9uZXR3b3JrLCBkaXJlY3RlZCA9IEZBTFNFLCB1bmNvbm5lY3RlZCA9IFRSVUUpLA0KICAgIGF2Z19wYXRoX2xlbiAgPSBhdmVyYWdlLnBhdGgubGVuZ3RoKGZpbmFsX25ldHdvcmssIGRpcmVjdGVkID0gRkFMU0UpLA0KICAgIGNsdXN0X2NvZWZmICAgPSB0cmFuc2l0aXZpdHkoZmluYWxfbmV0d29yaywgdHlwZSA9ICJnbG9iYWwiKSwNCiAgICBhc3NvcnRfZGVnICAgID0gYXNzb3J0YXRpdml0eV9kZWdyZWUoZmluYWxfbmV0d29yayksDQogICAgZGVnX3RyYWl0X2NvciA9IGZkZWd0cmFpdGNvcihmaW5hbF9uZXR3b3JrKSRjb3IsDQogICAgY29tcG9uZW50cyAgICA9IGNvbXBvbmVudHMoZmluYWxfbmV0d29yaykkbm8NCiAgKQ0KICANCiAgc3RhdHNfZGYgPC0gZGF0YS5mcmFtZSgNCiAgICBNZXRyaWMgPSBuYW1lcyhzdGF0cyksDQogICAgVmFsdWUgID0gdW5saXN0KHN0YXRzKSwNCiAgICByb3cubmFtZXMgPSBOVUxMDQogICkNCiAgDQogICNwcmludChzdGF0c19kZikNCiAgZnBsb3RfZ3JhcGgoZmluYWxfbmV0d29yaywgbGF5b3V0ID0gbGF5b3V0LmthbWFkYS5rYXdhaShmaW5hbF9uZXR3b3JrKSkNCiAgDQoNCiAgIyAtLS0gaW5pdGlhbCBhY3Rpb25zIC0tLQ0KICBWKGZpbmFsX25ldHdvcmspJGFjdGlvbiA8LSBpZmVsc2UoVihmaW5hbF9uZXR3b3JrKSRyb2xlID09ICJ0cmVuZHNldHRlciIsIDEsIDApDQogIA0KICAjIC0tLSBkZXRlcm1pbmlzdGljIHNpbXVsYXRpb24gLS0tDQogIHNpbV9kZXQgPC0gZmFibSgNCiAgICBuZXR3b3JrICAgICAgPSBmaW5hbF9uZXR3b3JrLA0KICAgIHBhcmFtcyAgICAgICA9IHBhcmFtcywNCiAgICBtYXhfcm91bmRzICAgPSAzNSwNCiAgICBtaV90aHJlc2hvbGQgPSAwLjQ5LA0KICAgIGNob2ljZV9ydWxlICA9ICJkZXRlcm1pbmlzdGljIiwNCiAgICBwbG90ICAgICAgICAgPSBUUlVFLA0KICAgIGhpc3RvcmllcyAgICA9IFRSVUUNCiAgKQ0KDQogICMgZ2VuZXJhdGUgdGhlIGdpZiBmb3IgdGhlIGN1cnJlbnQgbmV0d29yaw0KICBnaWZfZmlsZW5hbWUgPC0gcGFzdGUwKCIuL2ZpZ3VyZXMvYW5pbWF0aW9uX25ldHdvcmtfIiwgc2VlZF9pLCAiLmdpZiIpDQogIGdpZl9wYXRoIDwtIGZuZXR3b3JrZ2lmKGZpbmFsX25ldHdvcmssIHNpbV9kZXQkZGVjaXNpb25faGlzdG9yeSwgcm91bmRzID0gc2ltX2RldCRlcXVpbGlicml1bSRyb3VuZCwgb3V0cHV0X2RpciA9ICIuL2ZpZ3VyZXMiKQ0KICAjIHJlbmFtZSB0aGUgZ2lmIHRvIG1hdGNoIHRoZSBuYW1pbmcgcGF0dGVybg0KICBmaWxlLnJlbmFtZShnaWZfcGF0aCwgZ2lmX2ZpbGVuYW1lKQ0KICANCiAgDQogIGlmICghaXMubnVsbChzaW1fZGV0JHBsb3QpKSB7DQogICAgcHJpbnQoc2ltX2RldCRwbG90KQ0KICB9DQogIA0KICAjIC0tLSBwcm9iYWJpbGlzdGljIHNpbXVsYXRpb24gLS0tDQogIHNpbV9wcm9iIDwtIGZhYm0oDQogICAgbmV0d29yayAgICAgICAgICAgICAgICA9IGZpbmFsX25ldHdvcmssDQogICAgcGFyYW1zICAgICAgICAgICAgICAgICA9IHBhcmFtcywNCiAgICBtYXhfcm91bmRzICAgICAgICAgICAgID0gNTAsDQogICAgbWlfdGhyZXNob2xkICAgICAgICAgICA9IDAuNDksDQogICAgY2hvaWNlX3J1bGUgICAgICAgICAgICA9ICJwcm9iYWJpbGlzdGljIiwNCiAgICBzdGFibGVfd2luZG93ICAgICAgICAgID0gOCwgICAjIHRoZSBsZW5ndGggb2YgdGhlIHdpbmRvdyBvZiBhZG9wdGlvbiB2YWx1ZXMNCiAgICByZXF1aXJlZF9zdGFibGVfcm91bmRzID0gMjAsICMgbnVtYmVyIG9mIHdpbmRvd3MgbmVlZGVkIHRvIGRlY2xhcmUgZXF1aWxpYnJpdW0NCiAgICBwbG90ICAgICAgICAgICAgICAgICAgID0gVFJVRQ0KICApDQogIGlmICghaXMubnVsbChzaW1fcHJvYiRwbG90KSkgew0KICAgIHByaW50KHNpbV9wcm9iJHBsb3QpDQogIH0NCiAgDQogIA0KICByZXN1bHQgPC0gbGlzdCgNCiAgICBzZWdyZWdhdGlvbl9kZXQgID0gc2ltX2RldCRlcXVpbGlicml1bSRzZWdyZWdhdGlvbiwNCiAgICBzZWdyZWdhdGlvbl9wcm9iID0gc2ltX3Byb2IkZXF1aWxpYnJpdW0kc2VncmVnYXRpb24sDQogICAgc3RhdHMgICAgICAgICAgICA9IHN0YXRzDQogICkNCiAgDQogIGlmIChyZXR1cm5fbmV0d29yaykgew0KICAgIHJlc3VsdCRuZXR3b3JrIDwtIGZpbmFsX25ldHdvcmsNCiAgfQ0KICANCiAgcmVzdWx0DQp9DQpgYGANCg0KDQotLS0tLQ0KDQojIyBTUkRBIGNvbXB1dGVyLWxhYiBkZW1vbnN0YXRpb24NCg0KIyMjIFdHMTogaGV0ZXJvZ2Vub3VzIGRlZ3JlZSBkaXN0cmlidXRpb247IGNlbnRyYWxpemVkICJmYW5hdGljcyI6DQoNCmBgYHtyfQ0KdGVzdCA8LSBydW5fb25lX3NlZWQoMzgsIGtfbWluID0gNCwga19tYXggPSAxNiwgYWxwaGEgPSAyLjMsIHJobyA9IDEsIHIgPSAtMC4yLCByZXR1cm5fbmV0d29yayA9IFRSVUUpDQoNCnRhYmxlKGRlZ3JlZSh0ZXN0JG5ldHdvcmspKQ0KY2JpbmQoZGVncmVlKHRlc3QkbmV0d29yayksVih0ZXN0JG5ldHdvcmspJHJvbGUpDQpgYGANCg0KDQpgYGB7ciwgb3V0LndpZHRoPSI2MCUifQ0KYmFzZSA9IDEyNTMyODEyDQpzZWVkID0gYmFzZSArIDM4DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwYXN0ZTAoIi4vZmlndXJlcy9hbmltYXRpb25fbmV0d29ya18iLCBzZWVkICwiLmdpZiIpKQ0KYGBgIA0KDQoNCmBgYHtyLCBldmFsID0gRkFMU0V9DQojIHVzZSB0aGlzIGFzIHRoZSBuZXR3b3JrIHN0cnVjdHVyZSBmb3IgYW4gb3RyZWUgc2Vzc2lvbjoNCiNjYmluZChkZWdyZWUodGVzdCRuZXR3b3JrKSxWKHRlc3QkbmV0d29yaykkcm9sZSkNCg0KIyBjb252ZXJ0IHRvIGFkamFjZW5jeSBtYXRyaXgNCmFkal9tYXRyaXggPC0gYXMubWF0cml4KGFzX2FkamFjZW5jeV9tYXRyaXgodGVzdCRuZXR3b3JrKSkNCg0KI2dldCByb2xlcw0Kcm9sZV92ZWN0b3IgPC0gaWZlbHNlKFYodGVzdCRuZXR3b3JrKSRyb2xlID09ICJ0cmVuZHNldHRlciIsMSwwKQ0KIyBjcmVhdGUgYSBsaXN0IHRvIHN0b3JlIHRoZSBuZXR3b3JrIGRhdGENCm5ldCA8LSBsaXN0KGFkal9tYXRyaXggPSBhZGpfbWF0cml4LCByb2xlX3ZlY3RvciA9IHJvbGVfdmVjdG9yKQ0KIyBzYXZlIHRoZSBsaXN0IGFzIGEgSlNPTiBmaWxlDQp3cml0ZV9qc29uKG5ldCwgIlNSREFfbjIwX2hldGVyb2dlbm91cy5qc29uIikNCmBgYA0KDQoNCiMjIyBXRzI6IGhvbW9nZW5vdXMgZGVncmVlIGRpc3RyaWJ1dGlvbi0tInJhbmRvbSBuZXR3b3JrIg0KDQoNCmBgYHtyfQ0KdGVzdCA8LSBydW5fb25lX3N3KGk9NTEzLCBiZXRhPTEsIHBtaW49LjEsIHJldHVybl9uZXR3b3JrID0gVFJVRSApDQoNCnRhYmxlKGRlZ3JlZSh0ZXN0JG5ldHdvcmspKQ0KY2JpbmQoZGVncmVlKHRlc3QkbmV0d29yayksVih0ZXN0JG5ldHdvcmspJHJvbGUpDQpgYGANCg0KDQpgYGB7ciwgb3V0LndpZHRoPSI2MCUifQ0KYmFzZV9zZWVkID0gMTI1MzI4MTINCnNlZWQgPSBiYXNlX3NlZWQgKyA1MTMNCg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKCIuL2ZpZ3VyZXMvYW5pbWF0aW9uX25ldHdvcmtfIiwgc2VlZCAsIi5naWYiKSkNCmBgYCANCg0KDQoNCmBgYHtyLCBldmFsID0gRkFMU0V9DQojIHVzZSB0aGlzIGFzIHRoZSBuZXR3b3JrIHN0cnVjdHVyZSBmb3IgYW4gb3RyZWUgc2Vzc2lvbjoNCiNjYmluZChkZWdyZWUodGVzdCRuZXR3b3JrKSxWKHRlc3QkbmV0d29yaykkcm9sZSkNCg0KIyBjb252ZXJ0IHRvIGFkamFjZW5jeSBtYXRyaXgNCmFkal9tYXRyaXggPC0gYXMubWF0cml4KGFzX2FkamFjZW5jeV9tYXRyaXgodGVzdCRuZXR3b3JrKSkNCg0KI2dldCByb2xlcw0Kcm9sZV92ZWN0b3IgPC0gaWZlbHNlKFYodGVzdCRuZXR3b3JrKSRyb2xlID09ICJ0cmVuZHNldHRlciIsMSwwKQ0KIyBjcmVhdGUgYSBsaXN0IHRvIHN0b3JlIHRoZSBuZXR3b3JrIGRhdGENCm5ldCA8LSBsaXN0KGFkal9tYXRyaXggPSBhZGpfbWF0cml4LCByb2xlX3ZlY3RvciA9IHJvbGVfdmVjdG9yKQ0KIyBzYXZlIHRoZSBsaXN0IGFzIGEgSlNPTiBmaWxlDQp3cml0ZV9qc29uKG5ldCwgIlNSREFfbjIwX3JhbmRvbS5qc29uIikNCmBgYA0K


Copyright © Rob Franken