Helper functions used throughout

Documentation on the functions is interspersed through code comments

Set some options

do net set strings as factors

options(stringsAsFactors = FALSE)

show four significant digits

options(digits = 4)

tend not to show scientific notation

options(scipen = 7)

make output a bit wider

options(width = 110)

set a seed to make analyses depending on random number generation reproducible

set.seed(1710)

Load packages

generate the site

library(rmarkdown)

set options for chunks

library(knitr)

formr utility package to generate e.g. the bibliography

library(formr)

pretty-printed output

library(pander)

tidyverse date times

library(lubridate)

tidyverse strings

library(stringr)

extractor functions for models

library(broom)

marginal effects plots for regressions

library(effects)

grammar of graphics plots

library(ggplot2)

enhanced version of data.frame

library(data.table)

linear mixed effects models

library(lme4)

interpret linear mixed effects models output

library(lmerTest)

effect displays for all sort of models

library(effects)

simple, consistent wrappers for common string operations

library(stringr)

tools for working with categorical variables

library(forcats)

helps with fancier graphs

library(cowplot)

provides a funtion to format R source corde

library(formatR)

loads specific r tools

library(devtools)

imports IFLS data

library(haven)

package to perform factor analysis

library(GPArotation)

another package to perform factor analysis

library(psych)

yet another package to perform factor analysis

library(lavaan)

many usefull functions for data analysis

library(Hmisc)

all colors are from here

library(RColorBrewer)

tidyverse: transform data wide to long

library(tidyr)

tidyverse-style data wrangling. has a lot of naming conflicts, so always load last

library(dplyr)

Spin R files

R scripts can be documented in markdown using Roxygen comments, as demonstrated here This function turns all R files (that don’t have an Rmd file of the same name and that don’t start with an underscore _) into HTML pages

spin_R_files_to_site_html = function() {
  library(knitr)
  all_Rs = c(list.files(pattern = "^[^_].+\\.R$"), ".Rprofile")
  component_Rmds = list.files(pattern = "^_.+\\.Rmd$")
  temporary_Rmds = c()
  for (i in seq_along(all_Rs)) {
    if(all_Rs[i] == ".Rprofile") {
      Rmd_file = ".Rprofile.Rmd"
    } else {
      Rmd_file = paste0(all_Rs[i], "md")
    }
    if (!file.exists(Rmd_file)) {
      next_document = length(temporary_Rmds) + 1
      temporary_Rmds[next_document] = spin(all_Rs[i], knit = FALSE, envir = new.env(), format = "Rmd")
      prepended_yaml = paste0(c("---
output:
  html_document:
    code_folding: 'show'
---

", readLines(temporary_Rmds[next_document])), collapse = "\n")
      cat(prepended_yaml, file = temporary_Rmds[next_document])
    }
  }
  components_and_scripts = c(temporary_Rmds, component_Rmds)
  for (i in seq_along(components_and_scripts)) {
    opts_chunk$set(eval = FALSE, cache = FALSE)
    # if we call render_site on the .R file directly it adds a header I don't like
    rmarkdown::render_site(components_and_scripts[i], quiet = TRUE)
  }
  opts_chunk$set(eval = TRUE, cache = TRUE)
  unlink(temporary_Rmds)
}

Output options

use pander to pretty-print objects (if possible)

pander_handler = function(x, ...) {
  anyS3method = function(x) {
    classes = class(x)
    any(sapply(classes, FUN = function(classes) { !is.null(getS3method('pander',classes,TRUE)) } ))
  }
  if ("knit_asis" %in% class(x)) {
    x
  } else if (is.data.table(x)) {
    ""
    # don't ever print stupid data tables
  } else if (anyS3method(x)) {
    pander(x, row.names = F, ...)
  } else if (isS4(x)) {
    show(x)
  } else {
    print(x)
  }
}

opts_chunk$set(
  render = pander_handler
)

don’t split tables, scroll horizontally

panderOptions("table.split.table", Inf)

Functions used

set apa-conform theme for all graphs

apatheme=theme_bw()+
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.border=element_blank(),
        axis.line=element_line())

function used to do all birth order effect plots

plot_birthorder = function(model, ylabel = NULL, title = "", bo_var = "birth_order", separate = TRUE) {
  if(inherits(model, "merMod")) {
    varnames = names(model@frame)
  } else {
    varnames = names(model$model)
  }
  outcome = varnames[1]
  if(is.null(ylabel)) ylabel = outcome
  library(effects)
  library(tidyr)
  emm = allEffects(model)
  bo_var = names(emm)[names(emm) %contains% bo_var]
  cemm = as.data.frame(emm[[bo_var]])
  if (separate != TRUE) {
    cemm = cemm %>% rename_("Birth order" = bo_var) %>% mutate(Sibship = "across")
  } else {
    cemm = cemm %>%
      separate_(bo_var, into = c("Birth order", "Sibship"), sep = "/")
    number = spread(as.data.frame(table(model.frame(model)[`bo_var`])), Var1, Freq)
    n2 = paste0("2 (", sum(number$`1/2`, number$`2/2`), ")", seperate="")
    n3 = paste0("3 (", sum(number$`1/3`, number$`2/3`, number$`3/3`), ")",
                seperate="")
    n4 = paste0("4 (", sum(number$`1/4`, number$`2/4`, number$`3/4`,
                           number$`4/4`), ")", seperate="")
    n5 = paste0("5 (", sum(number$`1/5`, number$`2/5`, number$`3/5`,
                           number$`4/5`, number$`5/5`), ")", seperate="")
    n5more = paste0("5+ (", sum(number$`1/5+`, number$`2/5+`, number$`3/5+`,
                                number$`4/5+`, number$`5/5+`, number$`5+/5+`), ")",
                    seperate="")
    cemm = cemm %>%
      mutate(Sibship = recode_factor(Sibship, "2" = `n2`, "3" = `n3`, "4" = `n4`,
                                     "5" = `n5`, "5+" = `n5more`))
  }
  plotx = ggplot(cemm, aes(`Birth order`, y = fit, ymax = upper, ymin = lower,
                           colour = `Sibship`, group = `Sibship`)) +
    geom_pointrange(stat = "identity", position = position_dodge(width = 0.5)) +
    geom_line(position = position_dodge(width = 0.5)) +
    scale_y_continuous(name=ylabel, limits = c(-0.6, 0.5), breaks = c(-0.4, -0.2, 0,
                                                                      0.2, 0.4)) +
    labs(title= title) +
    apatheme +
    theme(legend.background = element_rect(fill="gray90", size=.5, linetype="dotted"),
          plot.title = element_text(hjust = 0)) +
    guides(colour=guide_legend(title = "Sibship")) +
    scale_fill_brewer(palette = "Set2") +
    scale_colour_brewer(palette = "Set2")
  print(plotx)
  assign(paste0("plot_", outcome, seperate=""),plotx,.GlobalEnv)
}

functions used to compare models

compare_models_markdown = function(m1_covariates_only) {
  formr::asis_knit_child('_test_outcome.Rmd')
  }

pad_month = function(x) { str_pad(x, width = 2, side = "left", pad = "0")}

Function to calculate the birthdate out of all available informations for one individual

all_available_info_birth_date = function(byear, bmonth, bday = NULL) {
  if(!is.null(bday)) {
    bday = paste0("-", pad_month(bday))
  } else {
    bday = ""
  }
  ifelse(is.na(byear), NA,
         paste0(byear, "-", pad_month(bmonth), pad_month(bday)))
  # can yield 2016-NA-NA
  #           2016-01-NA
  #           2016-01-01
  #           2016-01
}

Function to calculate the birthorder based on the siblings still alive at the time of birth

older_sibs_alive_and_dependent = function(byear, dyear) {
  sibs = length(byear)
  older_sibs_alive_and_dependent = integer(length=sibs) + 1
  for(i in 1:sibs) {
    older_sibs = byear <= byear[i] # not using < because of twins
    older_sibs[i] = F # minus self
    my_sibs = sum(older_sibs,na.rm = T) # minus self
    if(my_sibs > 0) {
      sib_births = byear[ which(older_sibs) ]
      sib_deaths = dyear[ which(older_sibs) ]
      my_sibs = my_sibs -
        sum(
          # sib_births < (byear[i] - 5) | # others born more than 5y earlier than me  # 10 seconds of 17
          (sib_deaths <= byear[i]) # died before my birth
          ,na.rm=T)
      older_sibs_alive_and_dependent[i] = my_sibs
    }
  }
  older_sibs_alive_and_dependent
}
LS0tDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9mb2xkaW5nOiAnc2hvdycNCi0tLQ0KDQoNCiMgSGVscGVyIGZ1bmN0aW9ucyB1c2VkIHRocm91Z2hvdXQgey50YWJzZXQgLnRhYnNldC1zdGlja3l9DQpEb2N1bWVudGF0aW9uIG9uIHRoZSBmdW5jdGlvbnMgaXMgaW50ZXJzcGVyc2VkIHRocm91Z2ggY29kZSBjb21tZW50cw0KDQojIyBTZXQgc29tZSBvcHRpb25zDQpkbyBuZXQgc2V0IHN0cmluZ3MgYXMgZmFjdG9ycw0KDQpgYGB7ciB9DQpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmBgYA0KDQpzaG93IGZvdXIgc2lnbmlmaWNhbnQgZGlnaXRzDQoNCmBgYHtyIH0NCm9wdGlvbnMoZGlnaXRzID0gNCkNCmBgYA0KDQp0ZW5kIG5vdCB0byBzaG93IHNjaWVudGlmaWMgbm90YXRpb24NCg0KYGBge3IgfQ0Kb3B0aW9ucyhzY2lwZW4gPSA3KQ0KYGBgDQoNCm1ha2Ugb3V0cHV0IGEgYml0IHdpZGVyDQoNCmBgYHtyIH0NCm9wdGlvbnMod2lkdGggPSAxMTApDQpgYGANCg0Kc2V0IGEgc2VlZCB0byBtYWtlIGFuYWx5c2VzIGRlcGVuZGluZyBvbiByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gcmVwcm9kdWNpYmxlDQoNCmBgYHtyIH0NCnNldC5zZWVkKDE3MTApDQpgYGANCg0KIyMgTG9hZCBwYWNrYWdlcw0KZ2VuZXJhdGUgdGhlIHNpdGUNCg0KYGBge3IgfQ0KbGlicmFyeShybWFya2Rvd24pDQpgYGANCg0Kc2V0IG9wdGlvbnMgZm9yIGNodW5rcw0KDQpgYGB7ciB9DQpsaWJyYXJ5KGtuaXRyKQ0KYGBgDQoNCmZvcm1yIHV0aWxpdHkgcGFja2FnZSB0byBnZW5lcmF0ZSBlLmcuIHRoZSBiaWJsaW9ncmFwaHkNCg0KYGBge3IgfQ0KbGlicmFyeShmb3JtcikNCmBgYA0KDQpwcmV0dHktcHJpbnRlZCBvdXRwdXQNCg0KYGBge3IgfQ0KbGlicmFyeShwYW5kZXIpDQpgYGANCg0KdGlkeXZlcnNlIGRhdGUgdGltZXMNCg0KYGBge3IgfQ0KbGlicmFyeShsdWJyaWRhdGUpDQpgYGANCg0KdGlkeXZlcnNlIHN0cmluZ3MNCg0KYGBge3IgfQ0KbGlicmFyeShzdHJpbmdyKQ0KYGBgDQoNCmV4dHJhY3RvciBmdW5jdGlvbnMgZm9yIG1vZGVscw0KDQpgYGB7ciB9DQpsaWJyYXJ5KGJyb29tKQ0KYGBgDQoNCm1hcmdpbmFsIGVmZmVjdHMgcGxvdHMgZm9yIHJlZ3Jlc3Npb25zDQoNCmBgYHtyIH0NCmxpYnJhcnkoZWZmZWN0cykNCmBgYA0KDQpncmFtbWFyIG9mIGdyYXBoaWNzIHBsb3RzDQoNCmBgYHtyIH0NCmxpYnJhcnkoZ2dwbG90MikNCmBgYA0KDQplbmhhbmNlZCB2ZXJzaW9uIG9mIGRhdGEuZnJhbWUNCg0KYGBge3IgfQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KYGBgDQoNCmxpbmVhciBtaXhlZCBlZmZlY3RzIG1vZGVscw0KDQpgYGB7ciB9DQpsaWJyYXJ5KGxtZTQpDQpgYGANCg0KaW50ZXJwcmV0IGxpbmVhciBtaXhlZCBlZmZlY3RzIG1vZGVscyBvdXRwdXQNCg0KYGBge3IgfQ0KbGlicmFyeShsbWVyVGVzdCkNCmBgYA0KDQplZmZlY3QgZGlzcGxheXMgZm9yIGFsbCBzb3J0IG9mIG1vZGVscw0KDQpgYGB7ciB9DQpsaWJyYXJ5KGVmZmVjdHMpDQpgYGANCg0Kc2ltcGxlLCBjb25zaXN0ZW50IHdyYXBwZXJzIGZvciBjb21tb24gc3RyaW5nIG9wZXJhdGlvbnMNCg0KYGBge3IgfQ0KbGlicmFyeShzdHJpbmdyKQ0KYGBgDQoNCnRvb2xzIGZvciB3b3JraW5nIHdpdGggY2F0ZWdvcmljYWwgdmFyaWFibGVzDQoNCmBgYHtyIH0NCmxpYnJhcnkoZm9yY2F0cykNCmBgYA0KDQpoZWxwcyB3aXRoIGZhbmNpZXIgZ3JhcGhzDQoNCmBgYHtyIH0NCmxpYnJhcnkoY293cGxvdCkNCmBgYA0KDQpwcm92aWRlcyBhIGZ1bnRpb24gdG8gZm9ybWF0IFIgc291cmNlIGNvcmRlDQoNCmBgYHtyIH0NCmxpYnJhcnkoZm9ybWF0UikNCmBgYA0KDQpsb2FkcyBzcGVjaWZpYyByIHRvb2xzDQoNCmBgYHtyIH0NCmxpYnJhcnkoZGV2dG9vbHMpDQpgYGANCg0KaW1wb3J0cyBJRkxTIGRhdGENCg0KYGBge3IgfQ0KbGlicmFyeShoYXZlbikNCmBgYA0KDQpwYWNrYWdlIHRvIHBlcmZvcm0gZmFjdG9yIGFuYWx5c2lzDQoNCmBgYHtyIH0NCmxpYnJhcnkoR1BBcm90YXRpb24pDQpgYGANCg0KYW5vdGhlciBwYWNrYWdlIHRvIHBlcmZvcm0gZmFjdG9yIGFuYWx5c2lzDQoNCmBgYHtyIH0NCmxpYnJhcnkocHN5Y2gpDQpgYGANCg0KeWV0IGFub3RoZXIgcGFja2FnZSB0byBwZXJmb3JtIGZhY3RvciBhbmFseXNpcw0KDQpgYGB7ciB9DQpsaWJyYXJ5KGxhdmFhbikNCmBgYA0KDQptYW55IHVzZWZ1bGwgZnVuY3Rpb25zIGZvciBkYXRhIGFuYWx5c2lzDQoNCmBgYHtyIH0NCmxpYnJhcnkoSG1pc2MpDQpgYGANCg0KYWxsIGNvbG9ycyBhcmUgZnJvbSBoZXJlDQoNCmBgYHtyIH0NCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KYGBgDQoNCnRpZHl2ZXJzZTogdHJhbnNmb3JtIGRhdGEgd2lkZSB0byBsb25nDQoNCmBgYHtyIH0NCmxpYnJhcnkodGlkeXIpDQpgYGANCg0KdGlkeXZlcnNlLXN0eWxlIGRhdGEgd3JhbmdsaW5nLiBoYXMgYSBsb3Qgb2YgbmFtaW5nIGNvbmZsaWN0cywgc28gYWx3YXlzIGxvYWQgbGFzdA0KDQpgYGB7ciB9DQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCiMjIFNwaW4gUiBmaWxlcw0KUiBzY3JpcHRzIGNhbiBiZSBkb2N1bWVudGVkIGluIG1hcmtkb3duIHVzaW5nIFJveHlnZW4gY29tbWVudHMsIGFzIGRlbW9uc3RyYXRlZCBoZXJlDQpUaGlzIGZ1bmN0aW9uIHR1cm5zIGFsbCBSIGZpbGVzICh0aGF0IGRvbid0IGhhdmUgYW4gUm1kIGZpbGUgb2YgdGhlIHNhbWUgbmFtZSBhbmQgdGhhdCBkb24ndCBzdGFydCB3aXRoIGFuIHVuZGVyc2NvcmUgXykgaW50byBIVE1MIHBhZ2VzDQoNCmBgYHtyIH0NCnNwaW5fUl9maWxlc190b19zaXRlX2h0bWwgPSBmdW5jdGlvbigpIHsNCiAgbGlicmFyeShrbml0cikNCiAgYWxsX1JzID0gYyhsaXN0LmZpbGVzKHBhdHRlcm4gPSAiXlteX10uK1xcLlIkIiksICIuUnByb2ZpbGUiKQ0KICBjb21wb25lbnRfUm1kcyA9IGxpc3QuZmlsZXMocGF0dGVybiA9ICJeXy4rXFwuUm1kJCIpDQogIHRlbXBvcmFyeV9SbWRzID0gYygpDQogIGZvciAoaSBpbiBzZXFfYWxvbmcoYWxsX1JzKSkgew0KICAgIGlmKGFsbF9Sc1tpXSA9PSAiLlJwcm9maWxlIikgew0KICAgICAgUm1kX2ZpbGUgPSAiLlJwcm9maWxlLlJtZCINCiAgICB9IGVsc2Ugew0KICAgICAgUm1kX2ZpbGUgPSBwYXN0ZTAoYWxsX1JzW2ldLCAibWQiKQ0KICAgIH0NCiAgICBpZiAoIWZpbGUuZXhpc3RzKFJtZF9maWxlKSkgew0KICAgICAgbmV4dF9kb2N1bWVudCA9IGxlbmd0aCh0ZW1wb3JhcnlfUm1kcykgKyAxDQogICAgICB0ZW1wb3JhcnlfUm1kc1tuZXh0X2RvY3VtZW50XSA9IHNwaW4oYWxsX1JzW2ldLCBrbml0ID0gRkFMU0UsIGVudmlyID0gbmV3LmVudigpLCBmb3JtYXQgPSAiUm1kIikNCiAgICAgIHByZXBlbmRlZF95YW1sID0gcGFzdGUwKGMoIi0tLQ0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogJ3Nob3cnDQotLS0NCg0KIiwgcmVhZExpbmVzKHRlbXBvcmFyeV9SbWRzW25leHRfZG9jdW1lbnRdKSksIGNvbGxhcHNlID0gIlxuIikNCiAgICAgIGNhdChwcmVwZW5kZWRfeWFtbCwgZmlsZSA9IHRlbXBvcmFyeV9SbWRzW25leHRfZG9jdW1lbnRdKQ0KICAgIH0NCiAgfQ0KICBjb21wb25lbnRzX2FuZF9zY3JpcHRzID0gYyh0ZW1wb3JhcnlfUm1kcywgY29tcG9uZW50X1JtZHMpDQogIGZvciAoaSBpbiBzZXFfYWxvbmcoY29tcG9uZW50c19hbmRfc2NyaXB0cykpIHsNCiAgICBvcHRzX2NodW5rJHNldChldmFsID0gRkFMU0UsIGNhY2hlID0gRkFMU0UpDQogICAgIyBpZiB3ZSBjYWxsIHJlbmRlcl9zaXRlIG9uIHRoZSAuUiBmaWxlIGRpcmVjdGx5IGl0IGFkZHMgYSBoZWFkZXIgSSBkb24ndCBsaWtlDQogICAgcm1hcmtkb3duOjpyZW5kZXJfc2l0ZShjb21wb25lbnRzX2FuZF9zY3JpcHRzW2ldLCBxdWlldCA9IFRSVUUpDQogIH0NCiAgb3B0c19jaHVuayRzZXQoZXZhbCA9IFRSVUUsIGNhY2hlID0gVFJVRSkNCiAgdW5saW5rKHRlbXBvcmFyeV9SbWRzKQ0KfQ0KYGBgDQoNCiMjIE91dHB1dCBvcHRpb25zDQp1c2UgcGFuZGVyIHRvIHByZXR0eS1wcmludCBvYmplY3RzIChpZiBwb3NzaWJsZSkNCg0KYGBge3IgfQ0KcGFuZGVyX2hhbmRsZXIgPSBmdW5jdGlvbih4LCAuLi4pIHsNCiAgYW55UzNtZXRob2QgPSBmdW5jdGlvbih4KSB7DQogICAgY2xhc3NlcyA9IGNsYXNzKHgpDQogICAgYW55KHNhcHBseShjbGFzc2VzLCBGVU4gPSBmdW5jdGlvbihjbGFzc2VzKSB7ICFpcy5udWxsKGdldFMzbWV0aG9kKCdwYW5kZXInLGNsYXNzZXMsVFJVRSkpIH0gKSkNCiAgfQ0KICBpZiAoImtuaXRfYXNpcyIgJWluJSBjbGFzcyh4KSkgew0KICAgIHgNCiAgfSBlbHNlIGlmIChpcy5kYXRhLnRhYmxlKHgpKSB7DQogICAgIiINCiAgICAjIGRvbid0IGV2ZXIgcHJpbnQgc3R1cGlkIGRhdGEgdGFibGVzDQogIH0gZWxzZSBpZiAoYW55UzNtZXRob2QoeCkpIHsNCiAgICBwYW5kZXIoeCwgcm93Lm5hbWVzID0gRiwgLi4uKQ0KICB9IGVsc2UgaWYgKGlzUzQoeCkpIHsNCiAgICBzaG93KHgpDQogIH0gZWxzZSB7DQogICAgcHJpbnQoeCkNCiAgfQ0KfQ0KDQpvcHRzX2NodW5rJHNldCgNCiAgcmVuZGVyID0gcGFuZGVyX2hhbmRsZXINCikNCmBgYA0KDQpkb24ndCBzcGxpdCB0YWJsZXMsIHNjcm9sbCBob3Jpem9udGFsbHkNCg0KYGBge3IgfQ0KcGFuZGVyT3B0aW9ucygidGFibGUuc3BsaXQudGFibGUiLCBJbmYpDQpgYGANCg0KIyMgRnVuY3Rpb25zIHVzZWQNCnNldCBhcGEtY29uZm9ybSB0aGVtZSBmb3IgYWxsIGdyYXBocw0KDQpgYGB7ciB9DQphcGF0aGVtZT10aGVtZV9idygpKw0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMubGluZT1lbGVtZW50X2xpbmUoKSkNCmBgYA0KDQpmdW5jdGlvbiB1c2VkIHRvIGRvIGFsbCBiaXJ0aCBvcmRlciBlZmZlY3QgcGxvdHMNCg0KYGBge3IgfQ0KcGxvdF9iaXJ0aG9yZGVyID0gZnVuY3Rpb24obW9kZWwsIHlsYWJlbCA9IE5VTEwsIHRpdGxlID0gIiIsIGJvX3ZhciA9ICJiaXJ0aF9vcmRlciIsIHNlcGFyYXRlID0gVFJVRSkgew0KICBpZihpbmhlcml0cyhtb2RlbCwgIm1lck1vZCIpKSB7DQogICAgdmFybmFtZXMgPSBuYW1lcyhtb2RlbEBmcmFtZSkNCiAgfSBlbHNlIHsNCiAgICB2YXJuYW1lcyA9IG5hbWVzKG1vZGVsJG1vZGVsKQ0KICB9DQogIG91dGNvbWUgPSB2YXJuYW1lc1sxXQ0KICBpZihpcy5udWxsKHlsYWJlbCkpIHlsYWJlbCA9IG91dGNvbWUNCiAgbGlicmFyeShlZmZlY3RzKQ0KICBsaWJyYXJ5KHRpZHlyKQ0KICBlbW0gPSBhbGxFZmZlY3RzKG1vZGVsKQ0KICBib192YXIgPSBuYW1lcyhlbW0pW25hbWVzKGVtbSkgJWNvbnRhaW5zJSBib192YXJdDQogIGNlbW0gPSBhcy5kYXRhLmZyYW1lKGVtbVtbYm9fdmFyXV0pDQogIGlmIChzZXBhcmF0ZSAhPSBUUlVFKSB7DQogICAgY2VtbSA9IGNlbW0gJT4lIHJlbmFtZV8oIkJpcnRoIG9yZGVyIiA9IGJvX3ZhcikgJT4lIG11dGF0ZShTaWJzaGlwID0gImFjcm9zcyIpDQogIH0gZWxzZSB7DQogICAgY2VtbSA9IGNlbW0gJT4lDQogICAgICBzZXBhcmF0ZV8oYm9fdmFyLCBpbnRvID0gYygiQmlydGggb3JkZXIiLCAiU2lic2hpcCIpLCBzZXAgPSAiLyIpDQogICAgbnVtYmVyID0gc3ByZWFkKGFzLmRhdGEuZnJhbWUodGFibGUobW9kZWwuZnJhbWUobW9kZWwpW2Bib192YXJgXSkpLCBWYXIxLCBGcmVxKQ0KICAgIG4yID0gcGFzdGUwKCIyICgiLCBzdW0obnVtYmVyJGAxLzJgLCBudW1iZXIkYDIvMmApLCAiKSIsIHNlcGVyYXRlPSIiKQ0KICAgIG4zID0gcGFzdGUwKCIzICgiLCBzdW0obnVtYmVyJGAxLzNgLCBudW1iZXIkYDIvM2AsIG51bWJlciRgMy8zYCksICIpIiwNCiAgICAgICAgICAgICAgICBzZXBlcmF0ZT0iIikNCiAgICBuNCA9IHBhc3RlMCgiNCAoIiwgc3VtKG51bWJlciRgMS80YCwgbnVtYmVyJGAyLzRgLCBudW1iZXIkYDMvNGAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIkYDQvNGApLCAiKSIsIHNlcGVyYXRlPSIiKQ0KICAgIG41ID0gcGFzdGUwKCI1ICgiLCBzdW0obnVtYmVyJGAxLzVgLCBudW1iZXIkYDIvNWAsIG51bWJlciRgMy81YCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciRgNC81YCwgbnVtYmVyJGA1LzVgKSwgIikiLCBzZXBlcmF0ZT0iIikNCiAgICBuNW1vcmUgPSBwYXN0ZTAoIjUrICgiLCBzdW0obnVtYmVyJGAxLzUrYCwgbnVtYmVyJGAyLzUrYCwgbnVtYmVyJGAzLzUrYCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyJGA0LzUrYCwgbnVtYmVyJGA1LzUrYCwgbnVtYmVyJGA1Ky81K2ApLCAiKSIsDQogICAgICAgICAgICAgICAgICAgIHNlcGVyYXRlPSIiKQ0KICAgIGNlbW0gPSBjZW1tICU+JQ0KICAgICAgbXV0YXRlKFNpYnNoaXAgPSByZWNvZGVfZmFjdG9yKFNpYnNoaXAsICIyIiA9IGBuMmAsICIzIiA9IGBuM2AsICI0IiA9IGBuNGAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUiID0gYG41YCwgIjUrIiA9IGBuNW1vcmVgKSkNCiAgfQ0KICBwbG90eCA9IGdncGxvdChjZW1tLCBhZXMoYEJpcnRoIG9yZGVyYCwgeSA9IGZpdCwgeW1heCA9IHVwcGVyLCB5bWluID0gbG93ZXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSBgU2lic2hpcGAsIGdyb3VwID0gYFNpYnNoaXBgKSkgKw0KICAgIGdlb21fcG9pbnRyYW5nZShzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSkpICsNCiAgICBnZW9tX2xpbmUocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZT15bGFiZWwsIGxpbWl0cyA9IGMoLTAuNiwgMC41KSwgYnJlYWtzID0gYygtMC40LCAtMC4yLCAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDAuMiwgMC40KSkgKw0KICAgIGxhYnModGl0bGU9IHRpdGxlKSArDQogICAgYXBhdGhlbWUgKw0KICAgIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9ImdyYXk5MCIsIHNpemU9LjUsIGxpbmV0eXBlPSJkb3R0ZWQiKSwNCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCkpICsNCiAgICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJTaWJzaGlwIikpICsNCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArDQogICAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDIiKQ0KICBwcmludChwbG90eCkNCiAgYXNzaWduKHBhc3RlMCgicGxvdF8iLCBvdXRjb21lLCBzZXBlcmF0ZT0iIikscGxvdHgsLkdsb2JhbEVudikNCn0NCmBgYA0KDQpmdW5jdGlvbnMgdXNlZCB0byBjb21wYXJlIG1vZGVscw0KDQpgYGB7ciB9DQpjb21wYXJlX21vZGVsc19tYXJrZG93biA9IGZ1bmN0aW9uKG0xX2NvdmFyaWF0ZXNfb25seSkgew0KICBmb3Jtcjo6YXNpc19rbml0X2NoaWxkKCdfdGVzdF9vdXRjb21lLlJtZCcpDQogIH0NCg0KcGFkX21vbnRoID0gZnVuY3Rpb24oeCkgeyBzdHJfcGFkKHgsIHdpZHRoID0gMiwgc2lkZSA9ICJsZWZ0IiwgcGFkID0gIjAiKX0NCmBgYA0KDQpGdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGJpcnRoZGF0ZSBvdXQgb2YgYWxsIGF2YWlsYWJsZSBpbmZvcm1hdGlvbnMgZm9yIG9uZSBpbmRpdmlkdWFsDQoNCmBgYHtyIH0NCmFsbF9hdmFpbGFibGVfaW5mb19iaXJ0aF9kYXRlID0gZnVuY3Rpb24oYnllYXIsIGJtb250aCwgYmRheSA9IE5VTEwpIHsNCiAgaWYoIWlzLm51bGwoYmRheSkpIHsNCiAgICBiZGF5ID0gcGFzdGUwKCItIiwgcGFkX21vbnRoKGJkYXkpKQ0KICB9IGVsc2Ugew0KICAgIGJkYXkgPSAiIg0KICB9DQogIGlmZWxzZShpcy5uYShieWVhciksIE5BLA0KICAgICAgICAgcGFzdGUwKGJ5ZWFyLCAiLSIsIHBhZF9tb250aChibW9udGgpLCBwYWRfbW9udGgoYmRheSkpKQ0KICAjIGNhbiB5aWVsZCAyMDE2LU5BLU5BDQogICMgICAgICAgICAgIDIwMTYtMDEtTkENCiAgIyAgICAgICAgICAgMjAxNi0wMS0wMQ0KICAjICAgICAgICAgICAyMDE2LTAxDQp9DQpgYGANCg0KRnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBiaXJ0aG9yZGVyIGJhc2VkIG9uIHRoZSBzaWJsaW5ncyBzdGlsbCBhbGl2ZSBhdCB0aGUgdGltZSBvZiBiaXJ0aA0KDQpgYGB7ciB9DQpvbGRlcl9zaWJzX2FsaXZlX2FuZF9kZXBlbmRlbnQgPSBmdW5jdGlvbihieWVhciwgZHllYXIpIHsNCiAgc2licyA9IGxlbmd0aChieWVhcikNCiAgb2xkZXJfc2lic19hbGl2ZV9hbmRfZGVwZW5kZW50ID0gaW50ZWdlcihsZW5ndGg9c2licykgKyAxDQogIGZvcihpIGluIDE6c2licykgew0KICAgIG9sZGVyX3NpYnMgPSBieWVhciA8PSBieWVhcltpXSAjIG5vdCB1c2luZyA8IGJlY2F1c2Ugb2YgdHdpbnMNCiAgICBvbGRlcl9zaWJzW2ldID0gRiAjIG1pbnVzIHNlbGYNCiAgICBteV9zaWJzID0gc3VtKG9sZGVyX3NpYnMsbmEucm0gPSBUKSAjIG1pbnVzIHNlbGYNCiAgICBpZihteV9zaWJzID4gMCkgew0KICAgICAgc2liX2JpcnRocyA9IGJ5ZWFyWyB3aGljaChvbGRlcl9zaWJzKSBdDQogICAgICBzaWJfZGVhdGhzID0gZHllYXJbIHdoaWNoKG9sZGVyX3NpYnMpIF0NCiAgICAgIG15X3NpYnMgPSBteV9zaWJzIC0NCiAgICAgICAgc3VtKA0KICAgICAgICAgICMgc2liX2JpcnRocyA8IChieWVhcltpXSAtIDUpIHwgIyBvdGhlcnMgYm9ybiBtb3JlIHRoYW4gNXkgZWFybGllciB0aGFuIG1lICAjIDEwIHNlY29uZHMgb2YgMTcNCiAgICAgICAgICAoc2liX2RlYXRocyA8PSBieWVhcltpXSkgIyBkaWVkIGJlZm9yZSBteSBiaXJ0aA0KICAgICAgICAgICxuYS5ybT1UKQ0KICAgICAgb2xkZXJfc2lic19hbGl2ZV9hbmRfZGVwZW5kZW50W2ldID0gbXlfc2licw0KICAgIH0NCiAgfQ0KICBvbGRlcl9zaWJzX2FsaXZlX2FuZF9kZXBlbmRlbnQNCn0NCmBgYA0K