Helper functions used throughout

documentation on the functions is interspersed through code comments

set some options

dont show messages when loading libraries

library = function(...) suppressMessages(base::library(...))

never set strings as factors automatically (google for reason why)

options(stringsAsFactors = FALSE)

show four significant digits tops

options(digits = 4)

tend not to show scientific notation, because we’re just psychologists

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) # if you use your significant other's birthday make sure you stay together for the sake of reproducibility

Load packages

generate the site

library(rmarkdown)

set options for chunks

library(knitr)

my 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)

grammar of graphics plots

library(ggplot2)

svg graphs

# library(svglite);
library(ggthemes)
library(codebook)

tidyverse: has a lot of naming conflicts, so always load last

library(tidyverse)

some packages may be needed without being loaded

fool_renv = function() {
  # needed to install formr package
  library(devtools)
  library(rmarkdown)
  library(Cairo)
  # needed to actually run rmarkdown in RStudio, but for some reason not in its dependencies
  library(formatR)
}

Output options

use pander to pretty-print objects (if possible)

opts_chunk$set(
  dev = "png"
)

don’t split tables, scroll horizontally

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

Set plot defaults

theme_set(theme_tufte(base_size = 20, base_family='Helvetica Neue'))

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)) {
      temporary_Rmds[i] <- Rmd_file
      if(file.exists(all_Rs[i])) {
      temp_knit = spin(all_Rs[i], knit = FALSE, envir = new.env(), format = "Rmd")
      prepended_yaml = paste0(c(paste0("---
title: ",all_Rs[i],"
output:
  html_document:
    code_folding: 'show'
"), readLines(temp_knit)), collapse = "\n")
      unlink(temp_knit)
      cat(prepended_yaml, file = Rmd_file)
      }
    }
  }
  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)
}
we use this function to automatically get nice tables
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 # obj is knit_asis already, don't touch it
    # (useful if e.g. pander is called with options in the doc)
  } else if (anyS3method(x)) {
    pander(x, row.names = F, ...) # if method available, pander
  } else if (isS4(x)) {
    show(x)
  } else {
    print(x)
  }
}





##### counting excluded participants

n_excluded = function(x) {
  excluded_new = sum(is.na(x) | x == FALSE,na.rm = T)
  if (is.null(excluded_old)) {
    excluded = excluded_new
  } else {
    excluded = excluded_new - excluded_old
  }
  cat(excluded, "excluded\n")
  excluded_old <<- excluded_new
  excluded
}

### counting excluded days

n_excluded_days = function(x) {
  excluded_new_days = sum(is.na(x) | x == FALSE,na.rm = T)
  if (is.null(excluded_old_days)) {
    excluded = excluded_new_days
  } else {
    excluded = excluded_new_days - excluded_old_days
  }
  cat(excluded, "excluded\n")
  excluded_old_days <<- excluded_new_days
  excluded
}

### counting excluded days in social diary

n_excluded_social = function(x) {
  excluded_new_social = sum(is.na(x) | x == FALSE,na.rm = T)
  if (is.null(excluded_old_social)) {
    excluded = excluded_new_social
  } else {
    excluded = excluded_new_social - excluded_old_social
  }
  cat(excluded, "excluded\n")
  excluded_old_social <<- excluded_new_social
  excluded
}

### counting excluded participants in social network

n_excluded_network = function(x) {
  excluded_new_network = sum(is.na(x) | x == FALSE,na.rm = T)
  if (is.null(excluded_old_network)) {
    excluded = excluded_new_network
  } else {
    excluded = excluded_new_network - excluded_old_network
  }
  cat(excluded, "excluded\n")
  excluded_old_network <<- excluded_new_network
  excluded
}

###### strict exclusion
n_excluded_network_strict = function(x) {
  excluded_new_network_strict = sum(is.na(x) | x == FALSE,na.rm = T)
  if (is.null(excluded_old_network_strict)) {
    excluded = excluded_new_network_strict
  } else {
    excluded = excluded_new_network_strict - excluded_old_network_strict
  }
  cat(excluded, "excluded\n")
  excluded_old_network_strict <<- excluded_new_network_strict
  excluded
}

###### bar plot

bar_count = function(data, variable, na.rm = FALSE) {
  varname = deparse(substitute(variable))
  var = data %>% select_(varname) %>% .[[1]]
  if (na.rm == T) {
    var = var %>% na.omit()
  }
  var = factor(var, exclude = NULL)
  data$var = var

  ggplot(data, aes(x = var)) +
    geom_bar() +
    stat_count(aes(label = paste(..count.., "\n", scales::percent(round(..count../sum(count),2)))), hjust = -0.1, geom = "text", position = "identity", na.rm = T) +
    scale_y_continuous(expand = c(0.1, 0)) +
    xlab(varname) +
    coord_flip()
}



multi_rel = function(diary, lme = T, lmer = T) {
  mrel = diary %>%
    group_by(short) %>%
    filter(!is.na(risk_taking)) %>%
    filter(day_number <= 70) %>%
    gather(variable, value, -short, -day_number) %>%
    psych::multilevel.reliability(., "short", "day_number", lme = lme, lmer = lmer, items = "variable", values = "value", long = T, aov = F)
  mrel
}


robust_rowmeans <- function(x) {
  y <- rowMeans(x, na.rm = TRUE)
  y[is.nan(y)] <- NA_real_
  y
}


cortest_stretch <- function(d) {
  var_pairs <- t(combn(names(d), 2)) %>%
    as_data_frame() %>%
    setNames(c("x", "y"))

  p_values <- var_pairs %>%
    dplyr::mutate(r.test = purrr::map2(x, y, ~ stats::cor.test(d[[.x]], d[[.y]])),
                  r.test = purrr::map(r.test, broom::tidy)) %>%
    tidyr::unnest(r.test)
  p_values
}


cut_common_stem <- function(x) {
  i = 1
  while (i <= max(stringr::str_length(x)) & dplyr::n_distinct(stringr::str_sub(x, 1, i)) == 1) {
    i = i + 1
  }
  stringr::str_sub(x, i)
}
LS0tCnRpdGxlOiAwX2hlbHBlcnMuUgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogJ3Nob3cnCi0tLQoKCiMgSGVscGVyIGZ1bmN0aW9ucyB1c2VkIHRocm91Z2hvdXQgey50YWJzZXQgLnRhYnNldC1zdGlja3l9CmRvY3VtZW50YXRpb24gb24gdGhlIGZ1bmN0aW9ucyBpcyBpbnRlcnNwZXJzZWQgdGhyb3VnaCBjb2RlIGNvbW1lbnRzCgojIyBzZXQgc29tZSBvcHRpb25zCmRvbnQgc2hvdyBtZXNzYWdlcyB3aGVuIGxvYWRpbmcgbGlicmFyaWVzCgpgYGB7ciB9CmxpYnJhcnkgPSBmdW5jdGlvbiguLi4pIHN1cHByZXNzTWVzc2FnZXMoYmFzZTo6bGlicmFyeSguLi4pKQpgYGAKCm5ldmVyIHNldCBzdHJpbmdzIGFzIGZhY3RvcnMgYXV0b21hdGljYWxseSAoZ29vZ2xlIGZvciByZWFzb24gd2h5KQoKYGBge3IgfQpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYGBgCgpzaG93IGZvdXIgc2lnbmlmaWNhbnQgZGlnaXRzIHRvcHMKCmBgYHtyIH0Kb3B0aW9ucyhkaWdpdHMgPSA0KQpgYGAKCnRlbmQgbm90IHRvIHNob3cgc2NpZW50aWZpYyBub3RhdGlvbiwgYmVjYXVzZSB3ZSdyZSBqdXN0IHBzeWNob2xvZ2lzdHMKCmBgYHtyIH0Kb3B0aW9ucyhzY2lwZW4gPSA3KQpgYGAKCm1ha2Ugb3V0cHV0IGEgYml0IHdpZGVyCgpgYGB7ciB9Cm9wdGlvbnMod2lkdGggPSAxMTApCmBgYAoKc2V0IGEgc2VlZCB0byBtYWtlIGFuYWx5c2VzIGRlcGVuZGluZyBvbiByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gcmVwcm9kdWNpYmxlCgpgYGB7ciB9CnNldC5zZWVkKDE3MTApICMgaWYgeW91IHVzZSB5b3VyIHNpZ25pZmljYW50IG90aGVyJ3MgYmlydGhkYXkgbWFrZSBzdXJlIHlvdSBzdGF5IHRvZ2V0aGVyIGZvciB0aGUgc2FrZSBvZiByZXByb2R1Y2liaWxpdHkKYGBgCgojIyBMb2FkIHBhY2thZ2VzCmdlbmVyYXRlIHRoZSBzaXRlCgpgYGB7ciB9CmxpYnJhcnkocm1hcmtkb3duKQpgYGAKCnNldCBvcHRpb25zIGZvciBjaHVua3MKCmBgYHtyIH0KbGlicmFyeShrbml0cikKYGBgCgpteSBmb3JtciB1dGlsaXR5IHBhY2thZ2UgdG8gZ2VuZXJhdGUgZS5nLiB0aGUgYmlibGlvZ3JhcGh5CgpgYGB7ciB9CmxpYnJhcnkoZm9ybXIpCmBgYAoKcHJldHR5LXByaW50ZWQgb3V0cHV0CgpgYGB7ciB9CmxpYnJhcnkocGFuZGVyKQpgYGAKCnRpZHl2ZXJzZSBkYXRlIHRpbWVzCgpgYGB7ciB9CmxpYnJhcnkobHVicmlkYXRlKQpgYGAKCnRpZHl2ZXJzZSBzdHJpbmdzCgpgYGB7ciB9CmxpYnJhcnkoc3RyaW5ncikKYGBgCgpleHRyYWN0b3IgZnVuY3Rpb25zIGZvciBtb2RlbHMKCmBgYHtyIH0KbGlicmFyeShicm9vbSkKYGBgCgpncmFtbWFyIG9mIGdyYXBoaWNzIHBsb3RzCgpgYGB7ciB9CmxpYnJhcnkoZ2dwbG90MikKYGBgCgpzdmcgZ3JhcGhzCgpgYGB7ciB9CiMgbGlicmFyeShzdmdsaXRlKTsKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShjb2RlYm9vaykKYGBgCgp0aWR5dmVyc2U6IGhhcyBhIGxvdCBvZiBuYW1pbmcgY29uZmxpY3RzLCBzbyBhbHdheXMgbG9hZCBsYXN0CgpgYGB7ciB9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCnNvbWUgcGFja2FnZXMgbWF5IGJlIG5lZWRlZCB3aXRob3V0IGJlaW5nIGxvYWRlZAoKYGBge3IgfQpmb29sX3JlbnYgPSBmdW5jdGlvbigpIHsKICAjIG5lZWRlZCB0byBpbnN0YWxsIGZvcm1yIHBhY2thZ2UKICBsaWJyYXJ5KGRldnRvb2xzKQogIGxpYnJhcnkocm1hcmtkb3duKQogIGxpYnJhcnkoQ2Fpcm8pCiAgIyBuZWVkZWQgdG8gYWN0dWFsbHkgcnVuIHJtYXJrZG93biBpbiBSU3R1ZGlvLCBidXQgZm9yIHNvbWUgcmVhc29uIG5vdCBpbiBpdHMgZGVwZW5kZW5jaWVzCiAgbGlicmFyeShmb3JtYXRSKQp9CmBgYAoKIyMgT3V0cHV0IG9wdGlvbnMKdXNlIHBhbmRlciB0byBwcmV0dHktcHJpbnQgb2JqZWN0cyAoaWYgcG9zc2libGUpCgpgYGB7ciB9Cm9wdHNfY2h1bmskc2V0KAogIGRldiA9ICJwbmciCikKYGBgCgpkb24ndCBzcGxpdCB0YWJsZXMsIHNjcm9sbCBob3Jpem9udGFsbHkKCmBgYHtyIH0KcGFuZGVyT3B0aW9ucygidGFibGUuc3BsaXQudGFibGUiLCBJbmYpCmBgYAoKU2V0IHBsb3QgZGVmYXVsdHMKCmBgYHtyIH0KdGhlbWVfc2V0KHRoZW1lX3R1ZnRlKGJhc2Vfc2l6ZSA9IDIwLCBiYXNlX2ZhbWlseT0nSGVsdmV0aWNhIE5ldWUnKSkKYGBgCgojIyBTcGluIFIgZmlsZXMKUiBzY3JpcHRzIGNhbiBiZSBkb2N1bWVudGVkIGluIG1hcmtkb3duIHVzaW5nIFJveHlnZW4gY29tbWVudHMsIGFzIGRlbW9uc3RyYXRlZCBoZXJlClRoaXMgZnVuY3Rpb24gdHVybnMgYWxsIFIgZmlsZXMgKHRoYXQgZG9uJ3QgaGF2ZSBhbiBSbWQgZmlsZSBvZiB0aGUgc2FtZSBuYW1lIGFuZCB0aGF0IGRvbid0IHN0YXJ0IHdpdGggYW4gdW5kZXJzY29yZSBfKSBpbnRvIEhUTUwgcGFnZXMKCmBgYHtyIH0Kc3Bpbl9SX2ZpbGVzX3RvX3NpdGVfaHRtbCA9IGZ1bmN0aW9uKCkgewogIGxpYnJhcnkoa25pdHIpCiAgYWxsX1JzID0gYyhsaXN0LmZpbGVzKHBhdHRlcm4gPSAiXlteX10uK1xcLlIkIiksICIuUnByb2ZpbGUiKQogIGNvbXBvbmVudF9SbWRzID0gbGlzdC5maWxlcyhwYXR0ZXJuID0gIl5fLitcXC5SbWQkIikKICB0ZW1wb3JhcnlfUm1kcyA9IGMoKQogIGZvciAoaSBpbiBzZXFfYWxvbmcoYWxsX1JzKSkgewogICAgaWYoYWxsX1JzW2ldID09ICIuUnByb2ZpbGUiKSB7CiAgICAgIFJtZF9maWxlID0gIlJwcm9maWxlLlJtZCIKICAgIH0gZWxzZSB7CiAgICAgIFJtZF9maWxlID0gcGFzdGUwKGFsbF9Sc1tpXSwgIm1kIikKICAgIH0KICAgIGlmICghZmlsZS5leGlzdHMoUm1kX2ZpbGUpKSB7CiAgICAgIHRlbXBvcmFyeV9SbWRzW2ldIDwtIFJtZF9maWxlCiAgICAgIGlmKGZpbGUuZXhpc3RzKGFsbF9Sc1tpXSkpIHsKICAgICAgdGVtcF9rbml0ID0gc3BpbihhbGxfUnNbaV0sIGtuaXQgPSBGQUxTRSwgZW52aXIgPSBuZXcuZW52KCksIGZvcm1hdCA9ICJSbWQiKQogICAgICBwcmVwZW5kZWRfeWFtbCA9IHBhc3RlMChjKHBhc3RlMCgiLS0tCnRpdGxlOiAiLGFsbF9Sc1tpXSwiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiAnc2hvdycKYGBgCmBgYHtyIH0KCiIpLCByZWFkTGluZXModGVtcF9rbml0KSksIGNvbGxhcHNlID0gIlxuIikKICAgICAgdW5saW5rKHRlbXBfa25pdCkKICAgICAgY2F0KHByZXBlbmRlZF95YW1sLCBmaWxlID0gUm1kX2ZpbGUpCiAgICAgIH0KICAgIH0KICB9CiAgY29tcG9uZW50c19hbmRfc2NyaXB0cyA9IGModGVtcG9yYXJ5X1JtZHMsIGNvbXBvbmVudF9SbWRzKQogIGZvciAoaSBpbiBzZXFfYWxvbmcoY29tcG9uZW50c19hbmRfc2NyaXB0cykpIHsKICAgIG9wdHNfY2h1bmskc2V0KGV2YWwgPSBGQUxTRSwgY2FjaGUgPSBGQUxTRSkKICAgICMgaWYgd2UgY2FsbCByZW5kZXJfc2l0ZSBvbiB0aGUgLlIgZmlsZSBkaXJlY3RseSBpdCBhZGRzIGEgaGVhZGVyIEkgZG9uJ3QgbGlrZQogICAgcm1hcmtkb3duOjpyZW5kZXJfc2l0ZShjb21wb25lbnRzX2FuZF9zY3JpcHRzW2ldLCBxdWlldCA9IFRSVUUpCiAgfQogIG9wdHNfY2h1bmskc2V0KGV2YWwgPSBUUlVFLCBjYWNoZSA9IFRSVUUpCiAgdW5saW5rKHRlbXBvcmFyeV9SbWRzKQp9CmBgYAoKIyMjIyMgd2UgdXNlIHRoaXMgZnVuY3Rpb24gdG8gYXV0b21hdGljYWxseSBnZXQgbmljZSB0YWJsZXMKCmBgYHtyIH0KcGFuZGVyX2hhbmRsZXIgPSBmdW5jdGlvbih4LCAuLi4pIHsKICBhbnlTM21ldGhvZCA9IGZ1bmN0aW9uKHgpIHsKICAgIGNsYXNzZXMgPSBjbGFzcyh4KQogICAgYW55KHNhcHBseShjbGFzc2VzLCBGVU4gPSBmdW5jdGlvbihjbGFzc2VzKSB7ICFpcy5udWxsKGdldFMzbWV0aG9kKCdwYW5kZXInLGNsYXNzZXMsVFJVRSkpIH0gKSkKICB9CiAgaWYgKCJrbml0X2FzaXMiICVpbiUgY2xhc3MoeCkpIHsKICAgIHggIyBvYmogaXMga25pdF9hc2lzIGFscmVhZHksIGRvbid0IHRvdWNoIGl0CiAgICAjICh1c2VmdWwgaWYgZS5nLiBwYW5kZXIgaXMgY2FsbGVkIHdpdGggb3B0aW9ucyBpbiB0aGUgZG9jKQogIH0gZWxzZSBpZiAoYW55UzNtZXRob2QoeCkpIHsKICAgIHBhbmRlcih4LCByb3cubmFtZXMgPSBGLCAuLi4pICMgaWYgbWV0aG9kIGF2YWlsYWJsZSwgcGFuZGVyCiAgfSBlbHNlIGlmIChpc1M0KHgpKSB7CiAgICBzaG93KHgpCiAgfSBlbHNlIHsKICAgIHByaW50KHgpCiAgfQp9CgoKCgoKIyMjIyMgY291bnRpbmcgZXhjbHVkZWQgcGFydGljaXBhbnRzCgpuX2V4Y2x1ZGVkID0gZnVuY3Rpb24oeCkgewogIGV4Y2x1ZGVkX25ldyA9IHN1bShpcy5uYSh4KSB8IHggPT0gRkFMU0UsbmEucm0gPSBUKQogIGlmIChpcy5udWxsKGV4Y2x1ZGVkX29sZCkpIHsKICAgIGV4Y2x1ZGVkID0gZXhjbHVkZWRfbmV3CiAgfSBlbHNlIHsKICAgIGV4Y2x1ZGVkID0gZXhjbHVkZWRfbmV3IC0gZXhjbHVkZWRfb2xkCiAgfQogIGNhdChleGNsdWRlZCwgImV4Y2x1ZGVkXG4iKQogIGV4Y2x1ZGVkX29sZCA8PC0gZXhjbHVkZWRfbmV3CiAgZXhjbHVkZWQKfQoKIyMjIGNvdW50aW5nIGV4Y2x1ZGVkIGRheXMKCm5fZXhjbHVkZWRfZGF5cyA9IGZ1bmN0aW9uKHgpIHsKICBleGNsdWRlZF9uZXdfZGF5cyA9IHN1bShpcy5uYSh4KSB8IHggPT0gRkFMU0UsbmEucm0gPSBUKQogIGlmIChpcy5udWxsKGV4Y2x1ZGVkX29sZF9kYXlzKSkgewogICAgZXhjbHVkZWQgPSBleGNsdWRlZF9uZXdfZGF5cwogIH0gZWxzZSB7CiAgICBleGNsdWRlZCA9IGV4Y2x1ZGVkX25ld19kYXlzIC0gZXhjbHVkZWRfb2xkX2RheXMKICB9CiAgY2F0KGV4Y2x1ZGVkLCAiZXhjbHVkZWRcbiIpCiAgZXhjbHVkZWRfb2xkX2RheXMgPDwtIGV4Y2x1ZGVkX25ld19kYXlzCiAgZXhjbHVkZWQKfQoKIyMjIGNvdW50aW5nIGV4Y2x1ZGVkIGRheXMgaW4gc29jaWFsIGRpYXJ5CgpuX2V4Y2x1ZGVkX3NvY2lhbCA9IGZ1bmN0aW9uKHgpIHsKICBleGNsdWRlZF9uZXdfc29jaWFsID0gc3VtKGlzLm5hKHgpIHwgeCA9PSBGQUxTRSxuYS5ybSA9IFQpCiAgaWYgKGlzLm51bGwoZXhjbHVkZWRfb2xkX3NvY2lhbCkpIHsKICAgIGV4Y2x1ZGVkID0gZXhjbHVkZWRfbmV3X3NvY2lhbAogIH0gZWxzZSB7CiAgICBleGNsdWRlZCA9IGV4Y2x1ZGVkX25ld19zb2NpYWwgLSBleGNsdWRlZF9vbGRfc29jaWFsCiAgfQogIGNhdChleGNsdWRlZCwgImV4Y2x1ZGVkXG4iKQogIGV4Y2x1ZGVkX29sZF9zb2NpYWwgPDwtIGV4Y2x1ZGVkX25ld19zb2NpYWwKICBleGNsdWRlZAp9CgojIyMgY291bnRpbmcgZXhjbHVkZWQgcGFydGljaXBhbnRzIGluIHNvY2lhbCBuZXR3b3JrCgpuX2V4Y2x1ZGVkX25ldHdvcmsgPSBmdW5jdGlvbih4KSB7CiAgZXhjbHVkZWRfbmV3X25ldHdvcmsgPSBzdW0oaXMubmEoeCkgfCB4ID09IEZBTFNFLG5hLnJtID0gVCkKICBpZiAoaXMubnVsbChleGNsdWRlZF9vbGRfbmV0d29yaykpIHsKICAgIGV4Y2x1ZGVkID0gZXhjbHVkZWRfbmV3X25ldHdvcmsKICB9IGVsc2UgewogICAgZXhjbHVkZWQgPSBleGNsdWRlZF9uZXdfbmV0d29yayAtIGV4Y2x1ZGVkX29sZF9uZXR3b3JrCiAgfQogIGNhdChleGNsdWRlZCwgImV4Y2x1ZGVkXG4iKQogIGV4Y2x1ZGVkX29sZF9uZXR3b3JrIDw8LSBleGNsdWRlZF9uZXdfbmV0d29yawogIGV4Y2x1ZGVkCn0KCiMjIyMjIyBzdHJpY3QgZXhjbHVzaW9uCm5fZXhjbHVkZWRfbmV0d29ya19zdHJpY3QgPSBmdW5jdGlvbih4KSB7CiAgZXhjbHVkZWRfbmV3X25ldHdvcmtfc3RyaWN0ID0gc3VtKGlzLm5hKHgpIHwgeCA9PSBGQUxTRSxuYS5ybSA9IFQpCiAgaWYgKGlzLm51bGwoZXhjbHVkZWRfb2xkX25ldHdvcmtfc3RyaWN0KSkgewogICAgZXhjbHVkZWQgPSBleGNsdWRlZF9uZXdfbmV0d29ya19zdHJpY3QKICB9IGVsc2UgewogICAgZXhjbHVkZWQgPSBleGNsdWRlZF9uZXdfbmV0d29ya19zdHJpY3QgLSBleGNsdWRlZF9vbGRfbmV0d29ya19zdHJpY3QKICB9CiAgY2F0KGV4Y2x1ZGVkLCAiZXhjbHVkZWRcbiIpCiAgZXhjbHVkZWRfb2xkX25ldHdvcmtfc3RyaWN0IDw8LSBleGNsdWRlZF9uZXdfbmV0d29ya19zdHJpY3QKICBleGNsdWRlZAp9CgojIyMjIyMgYmFyIHBsb3QKCmJhcl9jb3VudCA9IGZ1bmN0aW9uKGRhdGEsIHZhcmlhYmxlLCBuYS5ybSA9IEZBTFNFKSB7CiAgdmFybmFtZSA9IGRlcGFyc2Uoc3Vic3RpdHV0ZSh2YXJpYWJsZSkpCiAgdmFyID0gZGF0YSAlPiUgc2VsZWN0Xyh2YXJuYW1lKSAlPiUgLltbMV1dCiAgaWYgKG5hLnJtID09IFQpIHsKICAgIHZhciA9IHZhciAlPiUgbmEub21pdCgpCiAgfQogIHZhciA9IGZhY3Rvcih2YXIsIGV4Y2x1ZGUgPSBOVUxMKQogIGRhdGEkdmFyID0gdmFyCgogIGdncGxvdChkYXRhLCBhZXMoeCA9IHZhcikpICsKICAgIGdlb21fYmFyKCkgKwogICAgc3RhdF9jb3VudChhZXMobGFiZWwgPSBwYXN0ZSguLmNvdW50Li4sICJcbiIsIHNjYWxlczo6cGVyY2VudChyb3VuZCguLmNvdW50Li4vc3VtKGNvdW50KSwyKSkpKSwgaGp1c3QgPSAtMC4xLCBnZW9tID0gInRleHQiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIG5hLnJtID0gVCkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMC4xLCAwKSkgKwogICAgeGxhYih2YXJuYW1lKSArCiAgICBjb29yZF9mbGlwKCkKfQoKCgptdWx0aV9yZWwgPSBmdW5jdGlvbihkaWFyeSwgbG1lID0gVCwgbG1lciA9IFQpIHsKICBtcmVsID0gZGlhcnkgJT4lCiAgICBncm91cF9ieShzaG9ydCkgJT4lCiAgICBmaWx0ZXIoIWlzLm5hKHJpc2tfdGFraW5nKSkgJT4lCiAgICBmaWx0ZXIoZGF5X251bWJlciA8PSA3MCkgJT4lCiAgICBnYXRoZXIodmFyaWFibGUsIHZhbHVlLCAtc2hvcnQsIC1kYXlfbnVtYmVyKSAlPiUKICAgIHBzeWNoOjptdWx0aWxldmVsLnJlbGlhYmlsaXR5KC4sICJzaG9ydCIsICJkYXlfbnVtYmVyIiwgbG1lID0gbG1lLCBsbWVyID0gbG1lciwgaXRlbXMgPSAidmFyaWFibGUiLCB2YWx1ZXMgPSAidmFsdWUiLCBsb25nID0gVCwgYW92ID0gRikKICBtcmVsCn0KCgpyb2J1c3Rfcm93bWVhbnMgPC0gZnVuY3Rpb24oeCkgewogIHkgPC0gcm93TWVhbnMoeCwgbmEucm0gPSBUUlVFKQogIHlbaXMubmFuKHkpXSA8LSBOQV9yZWFsXwogIHkKfQoKCmNvcnRlc3Rfc3RyZXRjaCA8LSBmdW5jdGlvbihkKSB7CiAgdmFyX3BhaXJzIDwtIHQoY29tYm4obmFtZXMoZCksIDIpKSAlPiUKICAgIGFzX2RhdGFfZnJhbWUoKSAlPiUKICAgIHNldE5hbWVzKGMoIngiLCAieSIpKQoKICBwX3ZhbHVlcyA8LSB2YXJfcGFpcnMgJT4lCiAgICBkcGx5cjo6bXV0YXRlKHIudGVzdCA9IHB1cnJyOjptYXAyKHgsIHksIH4gc3RhdHM6OmNvci50ZXN0KGRbWy54XV0sIGRbWy55XV0pKSwKICAgICAgICAgICAgICAgICAgci50ZXN0ID0gcHVycnI6Om1hcChyLnRlc3QsIGJyb29tOjp0aWR5KSkgJT4lCiAgICB0aWR5cjo6dW5uZXN0KHIudGVzdCkKICBwX3ZhbHVlcwp9CgoKY3V0X2NvbW1vbl9zdGVtIDwtIGZ1bmN0aW9uKHgpIHsKICBpID0gMQogIHdoaWxlIChpIDw9IG1heChzdHJpbmdyOjpzdHJfbGVuZ3RoKHgpKSAmIGRwbHlyOjpuX2Rpc3RpbmN0KHN0cmluZ3I6OnN0cl9zdWIoeCwgMSwgaSkpID09IDEpIHsKICAgIGkgPSBpICsgMQogIH0KICBzdHJpbmdyOjpzdHJfc3ViKHgsIGkpCn0KYGBgCg==