Last updated: 2021-10-06

Checks: 7 0

Knit directory: myTidyTuesday/

This reproducible R Markdown analysis was created with workflowr (version 1.6.2). The Checks tab describes the reproducibility checks that were applied when the results were created. The Past versions tab lists the development history.


Great! Since the R Markdown file has been committed to the Git repository, you know the exact version of the code that produced these results.

Great job! The global environment was empty. Objects defined in the global environment can affect the analysis in your R Markdown file in unknown ways. For reproduciblity it’s best to always run the code in an empty environment.

The command set.seed(20210907) was run prior to running the code in the R Markdown file. Setting a seed ensures that any results that rely on randomness, e.g. subsampling or permutations, are reproducible.

Great job! Recording the operating system, R version, and package versions is critical for reproducibility.

Nice! There were no cached chunks for this analysis, so you can be confident that you successfully produced the results during this run.

Great job! Using relative paths to the files within your workflowr project makes it easier to run your code on other machines.

Great! You are using Git for version control. Tracking code development and connecting the code version to the results is critical for reproducibility.

The results in this page were generated with repository version bc5cd7d. See the Past versions tab to see a history of the changes made to the R Markdown and HTML files.

Note that you need to be careful to ensure that all relevant files for the analysis have been committed to Git prior to generating the results (you can use wflow_publish or wflow_git_commit). workflowr only checks the R Markdown file, but you know if there are other scripts or data files that it depends on. Below is the status of the Git repository when the results were generated:


Ignored files:
    Ignored:    .Rhistory
    Ignored:    .Rproj.user/
    Ignored:    catboost_info/
    Ignored:    data/2021-09-08/
    Ignored:    data/CNHI_Excel_Chart.xlsx
    Ignored:    data/CommunityTreemap.jpeg
    Ignored:    data/Community_Roles.jpeg
    Ignored:    data/YammerDigitalDataScienceMembership.xlsx
    Ignored:    data/acs_poverty.rds
    Ignored:    data/fmhpi.rds
    Ignored:    data/grainstocks.rds
    Ignored:    data/hike_data.rds
    Ignored:    data/nber_rs.rmd
    Ignored:    data/netflixTitles.rmd
    Ignored:    data/us_states.rds
    Ignored:    data/us_states_hexgrid.geojson
    Ignored:    data/weatherstats_toronto_daily.csv

Untracked files:
    Untracked:  analysis/2021_04_20.Rmd
    Untracked:  code/YammerReach.R
    Untracked:  code/work list batch targets.R

Unstaged changes:
    Modified:   code/_common.R

Note that any generated files, e.g. HTML, png, CSS, etc., are not included in this status report because it is ok for generated content to have uncommitted changes.


These are the previous versions of the repository in which changes were made to the R Markdown (analysis/2021_09_28_NBERpapers.Rmd) and HTML (docs/2021_09_28_NBERpapers.html) files. If you’ve configured a remote Git repository (see ?wflow_git_remote), click on the hyperlinks in the table below to view the files as they were in that past version.

File Version Author Date Message
html bc5cd7d opus1993 2021-10-06 Build site.
Rmd 0a83f10 opus1993 2021-10-06 use best_contrast in treemap
html 0c46dc1 opus1993 2021-10-06 Build site.
Rmd 9fb758b opus1993 2021-10-06 better constrasting text
html 0fdc287 opus1993 2021-10-04 Build site.
Rmd 4c00fd7 opus1993 2021-10-04 normalize the preprocessed data to properly lasso penalize the
html 594b352 opus1993 2021-10-04 Build site.
Rmd a80b1b6 opus1993 2021-10-04 improve bar graph geom_text legibility
html 08aaac0 opus1993 2021-10-03 Build site.
Rmd 645c9a6 opus1993 2021-10-03 add tidytuesday tweet
html 322ecf4 opus1993 2021-10-03 Build site.
Rmd 5998caf opus1993 2021-10-03 annotate with nber logo and tidytuesday logo 2nd try
html 66b00e0 opus1993 2021-10-03 Build site.
Rmd 01425d1 opus1993 2021-10-03 annotate with nber logo and tidytuesday logo
html 4370089 opus1993 2021-10-03 Build site.
Rmd d6b0274 opus1993 2021-10-03 attempting alt text in the new knitr engine
html 83a6c48 opus1993 2021-10-03 Build site.
Rmd becd96c opus1993 2021-10-03 drop the plotly chart in favor of static ggplot
html c2084af opus1993 2021-10-02 Build site.
Rmd f64fb40 opus1993 2021-10-02 initial commit

The #TidyTuesday data sets this week come from the National Bureau of Economic Research NBER by way of the nberwp package by Ben Davies.

National Bureau of Economic Research

Ben also has a detailed blogpost looking at this data, and NBER discusses the history of the organization here here.

My plan here is to learn something about the documents in the set and explore some R package functionality. Let’s load up R packages:

source(here::here("code","_common.R"),
       verbose = FALSE,
       local = knitr::knit_global())
Registered S3 method overwritten by 'tune':
  method                   from   
  required_pkgs.model_spec parsnip
suppressPackageStartupMessages({

library(tidyverse) # clean and transform rectangular data
library(treemapify)
library(extrafont)
library(tidytext)
library(tidylo) 
  
library(tidygraph)
library(ggraph)
library(igraph)
library(patchwork)
  
library(tidymodels) # machine learning tools
library(textrecipes)
library(finetune) # racing methods for accelerating hyperparameter tuning

library(themis) # ml prep tools for handling unbalanced datasets
  })

extrafont::loadfonts(quiet = TRUE)

theme_set(theme_jim(base_size = 14))
papers <-
  readr::read_csv(
    "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-09-28/papers.csv"
  )
programs <-
  readr::read_csv(
    "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-09-28/programs.csv"
  )
paper_authors <-
  readr::read_csv(
    "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-09-28/paper_authors.csv"
  )
paper_programs <-
  readr::read_csv(
    "https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-09-28/paper_programs.csv"
  )

Let’s start by joining up these datasets to find the info we need. The papers are grouped into twenty one programs so let’s train a multiclass predictive model to better understand them. I am going to choose to omit the technical working papers.

papers_joined <-
  paper_programs %>%
  left_join(programs) %>%
  left_join(papers) %>%
  filter(!is.na(program_desc)) %>%
  mutate(pub_date = lubridate::ym(paste(year, "-", month))) %>%
  distinct(paper, pub_date, title, program_desc, program_category) %>%
  filter(
    program_desc != "Technical Working Papers",
    !is.na(pub_date),
    !is.na(title),
    !is.na(program_desc)
  ) %>%
  mutate(program_desc = factor(program_desc))

papers_joined %>%
  count(program_desc, sort = TRUE)

The data are not balanced, so we will investigate strategies for finding features that work accordingly.

First, though, let’s explore the data with some visuals. Julia Silge’s approach to text modeling often goes down a path of exploring the log odds uniqueness of words within the document categories. Let’s try her approach, found here. And Emil Hvitfeldt’s prismatic package offers a nice method of building contrasting text on the bars.

title_log_odds <-
  papers_joined %>%
  unnest_tokens(word, title) %>%
  filter(!is.na(program_desc)) %>%
  count(program_desc, word, sort = TRUE) %>%
  bind_log_odds(program_desc, word, n)

p <- title_log_odds %>%
  group_by(program_desc) %>%
  slice_max(log_odds_weighted, n = 5) %>%
  ungroup() %>%
  mutate(word = reorder_within(word, log_odds_weighted, program_desc)) %>%
  ggplot(aes(log_odds_weighted, word, fill = program_desc)) +
  geom_col(show.legend = FALSE) +
  scale_y_reordered() +
  geom_text(aes(
    label = str_extract(word, "[:alnum:]*"),
    color = after_scale(map_chr(fill, best_contrast)), hjust = 0
  ),
  x = 0.2,
  size = rel(4),
  show.legend = FALSE,
  nudge_x = 40
  ) +
  scale_color_manual(values = viridis::viridis_pal(option = "H")(21)[c(1, 8, 15, 2, 9, 16, 3, 10, 17, 4, 11, 18, 5, 12, 19, 6, 13, 20, 7, 14, 21)]) +
  scale_fill_manual(values = viridis::viridis_pal(option = "H")(21)[c(1, 8, 15, 2, 9, 16, 3, 10, 17, 4, 11, 18, 5, 12, 19, 6, 13, 20, 7, 14, 21)]) +
  facet_wrap(vars(program_desc),
    scales = "free",
    labeller = label_wrap_gen()
  ) +
  labs(
    x = "Log odds (weighted)", y = NULL,
    title = "NBER Paper Titles: Most Descriptive Words by Program",
    caption = "Data Source: `nberwp` package by Ben Davies"
  ) +
  theme_jim(base_size = 10) +
  theme(
    panel.grid.major.y = element_blank(),
    axis.title.y = element_blank(),
    axis.text.y = element_blank()
  )

imglogo <- magick::image_read("https://github.com/rfordatascience/tidytuesday/blob/master/data/2021/2021-09-28/nber-logo.png?raw=true")
tidy_logo <- magick::image_read("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/static/plot_logo.png") %>%
  magick::image_resize("300x300")

cowplot::ggdraw() +
  cowplot::draw_plot(p) +
  cowplot::draw_image(imglogo,
    x = 0.01,
    y = -0.48,
    width = 0.2
  ) +
  cowplot::draw_image(tidy_logo,
    x = 0.2,
    y = -0.48,
    width = .1
  )

A faceted bar plot showing the relationships between words found in National Bureau of Economics Research papers titles and the programs that the papers were submitted into, using counts and log odds to find those that are most distinctively associated

With this many categorical variables, lets take a look at counts in a treemap:

papers_joined %>%
  group_by(program_desc, program_category) %>%
  summarise(
    n = n(),
    last_paper = last(pub_date),
    .groups = "drop"
  ) %>%
  slice_max(order_by = n, n = 60) %>%
  ggplot(aes(
    area = n,
    fill = last_paper,
    label = program_desc,
    subgroup = program_category
  )) +
  geom_treemap() +
  geom_treemap_subgroup_border() +
  geom_treemap_text(aes(color = after_scale(map_chr(fill, best_contrast))),
    place = "top",
    reflow = TRUE
  ) +
  geom_treemap_subgroup_text(
    color = "white",
    place = "bottomleft",
    fontface = "italic",
    min.size = 0
  ) +
  scale_fill_viridis_c(option = "H") +
  labs(
    fill = NULL, title = "National Bureau of Economic Research Papers",
    subtitle = "Three primary program categories cover 20 programs. The area corresponds to the\nnumber of papers of each in the dataset.",
    caption = "Data Source: Ben Davies {nberwp}"
  ) +
  theme(legend.position = "none")

We could create a co-authorship network graphic straight from the paper_authors table that makes the link between the papers and their authors.

To speed up the process of plotting every year, let’s build a function:

ggraph_theme <- theme_jim(base_size = 12) +
  theme(
    legend.position = "none",
    axis.title = element_blank(),
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    axis.text = element_blank(),
    axis.text.x = element_blank(),
    axis.text.y = element_blank(),
    panel.grid = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank()
  )

fun_net <- function(yr) {
  paper_authors_year <- paper_authors %>%
    left_join(papers) %>%
    filter(year == yr)

  edges_list <- paper_authors_year %>%
    mutate(author_id = as.integer(as.factor(author)))

  edges_list <- edges_list %>%
    left_join(edges_list, by = "paper") %>%
    count(author_id.x, author_id.y) %>%
    mutate(
      max = pmax(author_id.x, author_id.y),
      min = pmin(author_id.x, author_id.y)
    ) %>%
    unite(check, c(min, max), remove = FALSE) %>%
    distinct(check, .keep_all = TRUE) %>%
    mutate(n = case_when(
      (author_id.x == author_id.y) ~ 0L,
      TRUE ~ n
    )) %>%
    rename(
      from = author_id.x,
      to = author_id.y
    ) %>%
    select(from, to, n)

  network <- as_tbl_graph(edges_list, directed = FALSE)

  return(network)
}
plot80 <- ggraph(
  graph = fun_net(1980),
  layout = "kk"
) +
  geom_node_point() +
  geom_edge_diagonal(color = "dimgrey", alpha = 0.8) +
  ggraph_theme

plot00 <- ggraph(
  graph = fun_net(2000),
  layout = "kk"
) +
  geom_node_point() +
  geom_edge_diagonal(color = "dimgrey", alpha = 0.8) +
  ggraph_theme

layout <- c(
  area(
    t = 1,
    l = 1,
    b = 4,
    r = 2
  ),
  area(
    t = 1,
    l = 3,
    b = 4,
    r = 4
  )
)

plot80 + plot00 +
  plot_layout(design = layout) +
  plot_annotation(
    title = "Evolution of co-authorship networks for NBER papers",
    subtitle = "Algorithm: Kamda-Kawai",
    caption = "Data Source: `nberwp` package by Ben Davies",
    tag_levels = list(c("1980", "2000"))
  )

What can be done to further annotate the visual with metrics? In network analysis, the most common metric for each node is degree, that is the number of connections for each node.

net80 <- fun_net(1980) %>%
  activate(edges) %>%
  mutate(weights = case_when(
    # Solo-authored set to weight=0
    n == 0 ~ 0,
    # Weight = 1 for all others collaborations
    TRUE ~ 1
  )) %>%
  # Now activate nodes
  activate(nodes) %>%
  # Compute degree for each node
  mutate(deg = centrality_degree(weights = weights)) %>%
  # Find author with most collaboration
  # (highest-degree node)
  mutate(max_deg = max(deg)) %>%
  mutate(max_author = case_when(
    deg == max_deg ~ 1,
    TRUE ~ 0
  ))

# Same steps for year 2000
net00 <- fun_net(2000) %>%
  activate(edges) %>%
  mutate(weights = case_when(
    n == 0 ~ 0,
    TRUE ~ 1
  )) %>%
  activate(nodes) %>%
  mutate(deg = centrality_degree(weights = weights)) %>%
  mutate(max_deg = max(deg)) %>%
  mutate(max_author = case_when(
    deg == max_deg ~ 1,
    TRUE ~ 0
  ))

We may now calculate the average degree for each network.

stat_deg_80 <- net80 %>%
  activate(nodes) %>%
  as_tibble() %>%
  summarise(
    year = "1980",
    mean_deg = mean(deg),
    max_deg = mean(max_deg)
  )

stat_deg_00 <- net00 %>%
  activate(nodes) %>%
  as_tibble() %>%
  summarise(
    year = "2000",
    mean_deg = mean(deg),
    max_deg = mean(max_deg)
  )

bind_rows(stat_deg_80, stat_deg_00)

The number of connection is increasing between 1980 and 2000. For papers published in 2000, each author collaborated on average with approximately two other authors.

We may now add these information to the plots.

p80 <- ggraph(net80,
  layout = "kk"
) +
  geom_node_point(aes(col = deg, size = max_author)) +
  geom_edge_diagonal(color = "dimgrey", alpha = 0.8) +
  scale_color_viridis_b(option = "H") +
  guides(size = "none", color = "none") +
  ggraph_theme

p00 <- ggraph(net00,
  layout = "kk"
) +
  geom_node_point(aes(col = deg, size = max_author)) +
  guides(size = "none") +
  labs(color = "Degree") +
  scale_color_viridis_b(option = "H") +
  geom_edge_diagonal(color = "dimgrey", alpha = 0.8) +
  ggraph_theme

p80 + p00 +
  plot_layout(
    design = layout,
    guides = "collect"
  ) +
  plot_annotation(
    title = "Evolution of co-authorship networks for NBER papers",
    subtitle = "Largest points show node with maximum degree. Algorithm: Kamda-Kawai",
    caption = "Data Source: `nberwp` package by Ben Davies",
    tag_levels = list(c("1980", "2000"))
  )

More metrics regarding these networks are described in Ben Davies’ blog post.

Ben Nowak’s superb complete network diagram submission:

tweetrmd::include_tweet("https://twitter.com/BjnNowak/status/1442807060396134408")

For this week #TidyTuesday : evolution of co-authorship networks for @nberpubs publications.#RStats code here: https://t.co/42jqOPbo3q pic.twitter.com/8WDkFYbPDv

— Benjamin Nowak (@BjnNowak) September 28, 2021

The types of relationships, as inferences, between program and title words are what we want to discover more about in a predictive model.

Build and Tune a Model

Let’s start our modeling by setting up our “data budget.” We’ll stratify by our outcome program_desc.

nber_split <- initial_split(papers_joined,
  strata = program_desc
)
nber_train <- training(nber_split)
nber_test <- testing(nber_split)

set.seed(2021)
nber_folds <- vfold_cv(nber_train, strata = program_desc)

Next, let’s set up feature engineering. We will need to transform our text data into features useful for our model by tokenizing and computing either term frequency or tf-idf. Let’s also upsample since our dataset is imbalanced, with many more of some of the categories than others. Finally, for GLMnet penalties to work properly, all features must be normalized.

nber_rec_tfidf <-
  recipe(program_desc ~ title + pub_date,
    data = nber_train
  ) %>%
  step_tokenize(title) %>%
  step_stem(title) %>%
  step_ngram(title, num_tokens = 3) %>%
  step_tokenfilter(title, max_tokens = 200) %>%
  step_tfidf(title) %>%
  step_date(pub_date,
    features = c("month", "year"),
    keep_original_cols = FALSE
  ) %>%
  step_dummy(all_nominal_predictors()) %>%
  step_upsample(program_desc) %>%
  step_normalize(all_numeric_predictors()) %>%
  step_zv(all_predictors())

Then, let’s create our model specification for a lasso model. We need to use a model specification that can handle multiclass data, in this case multinom_reg().

multi_spec <-
  multinom_reg(
    penalty = tune(),
    mixture = 1
  ) %>%
  set_mode("classification") %>%
  set_engine("glmnet")

Since the lasso regularization penalty is a hyperparameter of the model (we can’t find the best value from fitting the model a single time), let’s tune over a grid of possible penalty parameters.

nber_grid <- grid_regular(penalty(range = c(-5, 0)),
  levels = 16
)

To speed things up, we will use all but one of the computer’s cores.

ctrl <- control_race(
  parallel_over = "everything",
  save_pred = TRUE
)

all_cores <- parallelly::availableCores(omit = 1)
all_cores

future::plan("multisession", workers = all_cores) # on Windows

Tidymodels will model every penalty parameter over the first couple of folds, but will begin to drop values unlikely to perform to save processing time.

nber_wf <-
  workflow(nber_rec_tfidf, multi_spec)

nber_rs <-
  nber_wf %>%
  tune_grid(
    resamples = nber_folds,
    verbose = TRUE,
    metric = metric_set(roc_aunu),
    grid = nber_grid
  )

How did it turn out?

autoplot(nber_rs)

(final_penalty <- select_by_one_std_err(nber_rs,
  metric = "roc_auc",
  desc(penalty)
))
final_rs <-
  nber_wf %>%
  finalize_workflow(final_penalty) %>%
  last_fit(nber_split)

final_rs

How did our final model perform on the training data?

collect_metrics(final_rs)

We can visualize the difference in performance across classes with a confusion matrix.

collect_predictions(final_rs) %>%
  conf_mat(program_desc, .pred_class) %>%
  autoplot() +
  theme_jim(base_size = 12) +
  theme(axis.text.x = element_blank()) +
  labs(title = "NBER Classification Confusion Matrix on Training")

A confusion matrix illustrating the counts of predictions versus the truth for the training set used in creating the model

We can also visualize the ROC curves for each class.

collect_predictions(final_rs) %>%
  roc_curve(
    truth = program_desc,
    `.pred_Asset Pricing`:`.pred_Public Economics`
  ) %>%
  ggplot(aes(1 - specificity, sensitivity, color = .level)) +
  geom_abline(
    slope = 1,
    color = "gray50",
    lty = 2,
    alpha = 0.8
  ) +
  geom_path(size = 1.5, alpha = 0.7) +
  scale_color_manual(values = viridis::viridis_pal(option = "H")(21)[c(1, 8, 15, 2, 9, 16, 3, 10, 17, 4, 11, 18, 5, 12, 19, 6, 13, 20, 7, 14, 21)], labels = scales::label_wrap(width = 12)) +
  labs(color = NULL) +
  coord_fixed() +
  labs(title = "ROC better for Development Economics than most others") +
  guides(color = guide_legend(ncol = 2)) +
  theme(legend.text = element_text(size = 8))

Finally, we can extract (and save, if we like) the fitted workflow from our results to use for predicting on new data.

For paper w0456, Sterilization and Offsetting Capital Movements: Evidence from West Germany, 1960-1970, the model makes the following prediction probabilities:

final_fitted <- extract_workflow(final_rs)
## can save this for prediction later with readr::write_rds()

predict(final_fitted, nber_test[85, ], type = "prob") %>%
  pivot_longer(cols = everything()) %>%
  arrange(desc(value))

The test data’s correct answer (truth) is International Finance and Macroeconomics

My tweet for the week:

tweetrmd::include_tweet("https://twitter.com/jim_gruman/status/1444715803631357958")

sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 22000)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252 
[2] LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] glmnet_4.1-2       Matrix_1.3-4       vctrs_0.3.8        rlang_0.4.11      
 [5] SnowballC_0.7.0    themis_0.1.4       finetune_0.1.0     textrecipes_0.4.1 
 [9] yardstick_0.0.8    workflowsets_0.1.0 workflows_0.2.3    tune_0.1.6        
[13] rsample_0.1.0      recipes_0.1.17     parsnip_0.1.7.900  modeldata_0.1.1   
[17] infer_1.0.0        dials_0.0.10       scales_1.1.1       broom_0.7.9       
[21] tidymodels_0.1.4   patchwork_1.1.1    igraph_1.2.6       ggraph_2.0.5      
[25] tidygraph_1.2.0    tidylo_0.1.0       tidytext_0.3.2     extrafont_0.17    
[29] treemapify_2.5.5   forcats_0.5.1      stringr_1.4.0      dplyr_1.0.7       
[33] purrr_0.3.4        readr_2.0.2        tidyr_1.1.4        tibble_3.1.4      
[37] ggplot2_3.3.5      tidyverse_1.3.1    workflowr_1.6.2   

loaded via a namespace (and not attached):
  [1] utf8_1.2.2         R.utils_2.11.0     tidyselect_1.1.1  
  [4] grid_4.1.1         pROC_1.18.0        munsell_0.5.0     
  [7] codetools_0.2-18   ragg_1.1.3         future_1.22.1     
 [10] withr_2.4.2        colorspace_2.0-2   highr_0.9         
 [13] knitr_1.36         rstudioapi_0.13    Rttf2pt1_1.3.8    
 [16] listenv_0.8.0      labeling_0.4.2     git2r_0.28.0      
 [19] polyclip_1.10-0    bit64_4.0.5        DiceDesign_1.9    
 [22] farver_2.1.0       rprojroot_2.0.2    mlr_2.19.0        
 [25] parallelly_1.28.1  generics_0.1.0     ipred_0.9-12      
 [28] xfun_0.26          R6_2.5.1           doParallel_1.0.16 
 [31] graphlayouts_0.7.1 lhs_1.1.3          assertthat_0.2.1  
 [34] promises_1.2.0.1   vroom_1.5.5        nnet_7.3-16       
 [37] gtable_0.3.0       globals_0.14.0     timeDate_3043.102 
 [40] BBmisc_1.11        systemfonts_1.0.2  splines_4.1.1     
 [43] extrafontdb_1.0    prismatic_1.0.0    checkmate_2.0.0   
 [46] yaml_2.2.1         modelr_0.1.8       backports_1.2.1   
 [49] httpuv_1.6.3       tokenizers_0.2.1   tools_4.1.1       
 [52] lava_1.6.10        ellipsis_0.3.2     jquerylib_0.1.4   
 [55] Rcpp_1.0.7         plyr_1.8.6         parallelMap_1.5.1 
 [58] rpart_4.1-15       ParamHelpers_1.14  viridis_0.6.1     
 [61] cowplot_1.1.1      haven_2.4.3        ggrepel_0.9.1     
 [64] fs_1.5.0           here_1.0.1         furrr_0.2.3       
 [67] unbalanced_2.0     magrittr_2.0.1     data.table_1.14.2 
 [70] magick_2.7.3       reprex_2.0.1       RANN_2.6.1        
 [73] GPfit_1.0-8        whisker_0.4        ROSE_0.0-4        
 [76] R.cache_0.15.0     hms_1.1.1          evaluate_0.14     
 [79] readxl_1.3.1       shape_1.4.6        gridExtra_2.3     
 [82] compiler_4.1.1     crayon_1.4.1       R.oo_1.24.0       
 [85] htmltools_0.5.2    later_1.3.0        tzdb_0.1.2        
 [88] lubridate_1.7.10   DBI_1.1.1          tweenr_1.0.2      
 [91] dbplyr_2.1.1       MASS_7.3-54        cli_3.0.1         
 [94] R.methodsS3_1.8.1  parallel_4.1.1     gower_0.2.2       
 [97] pkgconfig_2.0.3    tweetrmd_0.0.9     xml2_1.3.2        
[100] foreach_1.5.1      bslib_0.3.0        hardhat_0.1.6     
[103] prodlim_2019.11.13 rvest_1.0.1        janeaustenr_0.1.5 
[106] digest_0.6.28      rmarkdown_2.11     cellranger_1.1.0  
[109] fastmatch_1.1-3    curl_4.3.2         lifecycle_1.0.1   
[112] jsonlite_1.7.2     viridisLite_0.4.0  fansi_0.5.0       
[115] pillar_1.6.3       lattice_0.20-44    fastmap_1.1.0     
[118] httr_1.4.2         survival_3.2-11    glue_1.4.2        
[121] conflicted_1.0.4   FNN_1.1.3          iterators_1.0.13  
[124] bit_4.0.4          ggforce_0.3.3      class_7.3-19      
[127] stringi_1.7.5      sass_0.4.0         rematch2_2.1.2    
[130] ggfittext_0.9.1    textshaping_0.3.5  styler_1.6.2      
[133] future.apply_1.8.1
LS0tDQp0aXRsZTogIk5hdGlvbmFsIEJ1cmVhdSBvZiBFY29ub21pYyBSZXNlYXJjaCB3b3JraW5nIHBhcGVycyINCmF1dGhvcjogIkppbSBHcnVtYW4iDQpkYXRlOiAiU2VwdGVtYmVyIDMwLCAyMDIxIg0Kb3V0cHV0Og0KICB3b3JrZmxvd3I6OndmbG93X2h0bWw6DQogICAgdG9jOiBubw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQNCmVkaXRvcl9vcHRpb25zOg0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNClRoZSAjVGlkeVR1ZXNkYXkgZGF0YSBzZXRzIHRoaXMgd2VlayBjb21lIGZyb20gdGhlIE5hdGlvbmFsIEJ1cmVhdSBvZiBFY29ub21pYyBSZXNlYXJjaCBbTkJFUl0oaHR0cHM6Ly93d3cyLm5iZXIub3JnL1JlUEVjL25ici9uYmVyd28vKSBieSB3YXkgb2YgdGhlIFtgbmJlcndwYCBwYWNrYWdlIGJ5IEJlbiBEYXZpZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9ibGRhdmllcy9uYmVyd3ApLg0KDQo8aW1nIGFsdD0iTmF0aW9uYWwgQnVyZWF1IG9mIEVjb25vbWljIFJlc2VhcmNoIiBhcmlhLWxhYmVsPSJOQkVSIGxvZ28iIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9ibG9iL21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wOS0yOC9uYmVyLWxvZ28ucG5nP3Jhdz10cnVlIj4NCg0KQmVuIGFsc28gaGFzIGEgZGV0YWlsZWQgW2Jsb2dwb3N0XShodHRwczovL2JsZGF2aWVzLmNvbS9ibG9nL2ZlbWFsZS1yZXByZXNlbnRhdGlvbi1jb2xsYWJvcmF0aW9uLW5iZXIvKSBsb29raW5nIGF0IHRoaXMgZGF0YSwgYW5kIE5CRVIgZGlzY3Vzc2VzIHRoZSBoaXN0b3J5IG9mIHRoZSBvcmdhbml6YXRpb24gaGVyZSBbaGVyZV0oaHR0cHM6Ly93d3cubmJlci5vcmcvYWJvdXQtbmJlci9oaXN0b3J5KS4NCg0KTXkgcGxhbiBoZXJlIGlzIHRvIGxlYXJuIHNvbWV0aGluZyBhYm91dCB0aGUgZG9jdW1lbnRzIGluIHRoZSBzZXQgYW5kIGV4cGxvcmUgc29tZSBSIHBhY2thZ2UgZnVuY3Rpb25hbGl0eS4gIExldCdzIGxvYWQgdXAgUiBwYWNrYWdlczoNCg0KYGBge3Igc2V0dXB9DQpzb3VyY2UoaGVyZTo6aGVyZSgiY29kZSIsIl9jb21tb24uUiIpLA0KICAgICAgIHZlcmJvc2UgPSBGQUxTRSwNCiAgICAgICBsb2NhbCA9IGtuaXRyOjprbml0X2dsb2JhbCgpKQ0KDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoew0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBjbGVhbiBhbmQgdHJhbnNmb3JtIHJlY3Rhbmd1bGFyIGRhdGENCmxpYnJhcnkodHJlZW1hcGlmeSkNCmxpYnJhcnkoZXh0cmFmb250KQ0KbGlicmFyeSh0aWR5dGV4dCkNCmxpYnJhcnkodGlkeWxvKSANCiAgDQpsaWJyYXJ5KHRpZHlncmFwaCkNCmxpYnJhcnkoZ2dyYXBoKQ0KbGlicmFyeShpZ3JhcGgpDQpsaWJyYXJ5KHBhdGNod29yaykNCiAgDQpsaWJyYXJ5KHRpZHltb2RlbHMpICMgbWFjaGluZSBsZWFybmluZyB0b29scw0KbGlicmFyeSh0ZXh0cmVjaXBlcykNCmxpYnJhcnkoZmluZXR1bmUpICMgcmFjaW5nIG1ldGhvZHMgZm9yIGFjY2VsZXJhdGluZyBoeXBlcnBhcmFtZXRlciB0dW5pbmcNCg0KbGlicmFyeSh0aGVtaXMpICMgbWwgcHJlcCB0b29scyBmb3IgaGFuZGxpbmcgdW5iYWxhbmNlZCBkYXRhc2V0cw0KICB9KQ0KDQpleHRyYWZvbnQ6OmxvYWRmb250cyhxdWlldCA9IFRSVUUpDQoNCnRoZW1lX3NldCh0aGVtZV9qaW0oYmFzZV9zaXplID0gMTQpKQ0KDQpgYGANCg0KDQpgYGB7ciBsb2FkRGF0YX0NCnBhcGVycyA8LQ0KICByZWFkcjo6cmVhZF9jc3YoDQogICAgImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAyMS8yMDIxLTA5LTI4L3BhcGVycy5jc3YiDQogICkNCnByb2dyYW1zIDwtDQogIHJlYWRyOjpyZWFkX2NzdigNCiAgICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDktMjgvcHJvZ3JhbXMuY3N2Ig0KICApDQpwYXBlcl9hdXRob3JzIDwtDQogIHJlYWRyOjpyZWFkX2NzdigNCiAgICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDktMjgvcGFwZXJfYXV0aG9ycy5jc3YiDQogICkNCnBhcGVyX3Byb2dyYW1zIDwtDQogIHJlYWRyOjpyZWFkX2NzdigNCiAgICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDktMjgvcGFwZXJfcHJvZ3JhbXMuY3N2Ig0KICApDQpgYGANCg0KTGV04oCZcyBzdGFydCBieSBqb2luaW5nIHVwIHRoZXNlIGRhdGFzZXRzIHRvIGZpbmQgdGhlIGluZm8gd2UgbmVlZC4gVGhlIHBhcGVycyBhcmUgZ3JvdXBlZCBpbnRvIHR3ZW50eSBvbmUgcHJvZ3JhbXMgc28gbGV0J3MgdHJhaW4gYSBtdWx0aWNsYXNzIHByZWRpY3RpdmUgbW9kZWwgdG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlbS4gSSBhbSBnb2luZyB0byBjaG9vc2UgdG8gb21pdCB0aGUgdGVjaG5pY2FsIHdvcmtpbmcgcGFwZXJzLiANCg0KYGBge3IgcGFwZXJzX2pvaW5lZH0NCnBhcGVyc19qb2luZWQgPC0NCiAgcGFwZXJfcHJvZ3JhbXMgJT4lDQogIGxlZnRfam9pbihwcm9ncmFtcykgJT4lDQogIGxlZnRfam9pbihwYXBlcnMpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHByb2dyYW1fZGVzYykpICU+JQ0KICBtdXRhdGUocHViX2RhdGUgPSBsdWJyaWRhdGU6OnltKHBhc3RlKHllYXIsIi0iLG1vbnRoKSkpICU+JSANCiAgZGlzdGluY3QocGFwZXIsIHB1Yl9kYXRlLCB0aXRsZSwgcHJvZ3JhbV9kZXNjLCBwcm9ncmFtX2NhdGVnb3J5KSAlPiUgDQogIGZpbHRlcihwcm9ncmFtX2Rlc2MgIT0gIlRlY2huaWNhbCBXb3JraW5nIFBhcGVycyIsDQogICAgICAgICAhaXMubmEocHViX2RhdGUpLA0KICAgICAgICAgIWlzLm5hKHRpdGxlKSwNCiAgICAgICAgICFpcy5uYShwcm9ncmFtX2Rlc2MpKSAlPiUgDQogIG11dGF0ZShwcm9ncmFtX2Rlc2MgPSBmYWN0b3IocHJvZ3JhbV9kZXNjKSkNCg0KcGFwZXJzX2pvaW5lZCAlPiUgDQogIGNvdW50KHByb2dyYW1fZGVzYywgc29ydCA9IFRSVUUpIA0KYGBgDQoNClRoZSBkYXRhIGFyZSBub3QgYmFsYW5jZWQsIHNvIHdlIHdpbGwgaW52ZXN0aWdhdGUgc3RyYXRlZ2llcyBmb3IgZmluZGluZyBmZWF0dXJlcyB0aGF0IHdvcmsgYWNjb3JkaW5nbHkuDQoNCkZpcnN0LCB0aG91Z2gsIGxldCdzIGV4cGxvcmUgdGhlIGRhdGEgd2l0aCBzb21lIHZpc3VhbHMuIEp1bGlhIFNpbGdlJ3MgYXBwcm9hY2ggdG8gdGV4dCBtb2RlbGluZyBvZnRlbiBnb2VzIGRvd24gYSBwYXRoIG9mIGV4cGxvcmluZyB0aGUgbG9nIG9kZHMgdW5pcXVlbmVzcyBvZiB3b3JkcyB3aXRoaW4gdGhlIGRvY3VtZW50IGNhdGVnb3JpZXMuIExldCdzIHRyeSBoZXIgYXBwcm9hY2gsIFtmb3VuZCBoZXJlXShodHRwczovL2p1bGlhc2lsZ2UuY29tL2Jsb2cvbmJlci1wYXBlcnMvKS4gQW5kIEVtaWwgSHZpdGZlbGR0J3MgYHByaXNtYXRpY2AgcGFja2FnZSBvZmZlcnMgYSBuaWNlIG1ldGhvZCBvZiBidWlsZGluZyBjb250cmFzdGluZyB0ZXh0IG9uIHRoZSBiYXJzLg0KDQpgYGB7ciB0aXRsZV9sb2dfb2Rkc30NCiN8IGZpZy53aWR0aDogMTINCiN8IGZpZy5hc3A6IDEuNQ0KI3wgZmlnLmFsaWduOiAiY2VudGVyIg0KI3wgb3V0LndpZHRoOiAiMzAwJSINCiN8IGZpZy5hbHQ6ID4NCiN8ICBBIGZhY2V0ZWQgYmFyIHBsb3Qgc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuDQojfCAgd29yZHMgZm91bmQgaW4gTmF0aW9uYWwgQnVyZWF1IG9mIEVjb25vbWljcyBSZXNlYXJjaA0KI3wgIHBhcGVycyB0aXRsZXMgYW5kIHRoZSBwcm9ncmFtcyB0aGF0IHRoZSBwYXBlcnMgd2VyZQ0KI3wgIHN1Ym1pdHRlZCBpbnRvLCB1c2luZyBjb3VudHMgYW5kIGxvZyBvZGRzIHRvIGZpbmQNCiN8ICB0aG9zZSB0aGF0IGFyZSBtb3N0IGRpc3RpbmN0aXZlbHkgYXNzb2NpYXRlZA0KDQp0aXRsZV9sb2dfb2RkcyA8LQ0KICBwYXBlcnNfam9pbmVkICU+JQ0KICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRpdGxlKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShwcm9ncmFtX2Rlc2MpKSAlPiUNCiAgY291bnQocHJvZ3JhbV9kZXNjLCB3b3JkLCBzb3J0ID0gVFJVRSkgJT4lDQogIGJpbmRfbG9nX29kZHMocHJvZ3JhbV9kZXNjLCB3b3JkLCBuKQ0KDQpwIDwtIHRpdGxlX2xvZ19vZGRzICU+JQ0KICBncm91cF9ieShwcm9ncmFtX2Rlc2MpICU+JQ0KICBzbGljZV9tYXgobG9nX29kZHNfd2VpZ2h0ZWQsIG4gPSA1KSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUod29yZCA9IHJlb3JkZXJfd2l0aGluKHdvcmQsIGxvZ19vZGRzX3dlaWdodGVkLCBwcm9ncmFtX2Rlc2MpKSAlPiUgDQogIGdncGxvdChhZXMobG9nX29kZHNfd2VpZ2h0ZWQsIHdvcmQsIGZpbGwgPSBwcm9ncmFtX2Rlc2MpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgc2NhbGVfeV9yZW9yZGVyZWQoKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzdHJfZXh0cmFjdCh3b3JkLCAiWzphbG51bTpdKiIpLA0KICAgICAgICAgICAgICAgIGNvbG9yID0gYWZ0ZXJfc2NhbGUobWFwX2NocihmaWxsLCBiZXN0X2NvbnRyYXN0KSksIGhqdXN0ID0gMCksDQogICAgICAgICAgICB4ID0gMC4yLA0KICAgICAgICAgICAgc2l6ZSA9IHJlbCg0KSwNCiAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UsDQogICAgICAgICAgICBudWRnZV94ID0gNDApICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHZpcmlkaXM6OnZpcmlkaXNfcGFsKG9wdGlvbiA9ICJIIikoMjEpW2MoMSw4LDE1LDIsOSwxNiwzLDEwLDE3LDQsMTEsMTgsNSwxMiwxOSw2LDEzLDIwLDcsMTQsMjEpXSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB2aXJpZGlzOjp2aXJpZGlzX3BhbChvcHRpb24gPSAiSCIpKDIxKVtjKDEsOCwxNSwyLDksMTYsMywxMCwxNyw0LDExLDE4LDUsMTIsMTksNiwxMywyMCw3LDE0LDIxKV0pICsNCiAgZmFjZXRfd3JhcCh2YXJzKHByb2dyYW1fZGVzYyksIA0KICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlIiwNCiAgICAgICAgICAgICBsYWJlbGxlciA9IGxhYmVsX3dyYXBfZ2VuKCkpICsNCiAgbGFicyh4ID0gIkxvZyBvZGRzICh3ZWlnaHRlZCkiLCB5ID0gTlVMTCwNCiAgICAgICB0aXRsZSA9ICJOQkVSIFBhcGVyIFRpdGxlczogTW9zdCBEZXNjcmlwdGl2ZSBXb3JkcyBieSBQcm9ncmFtIiwNCiAgICAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBgbmJlcndwYCBwYWNrYWdlIGJ5IEJlbiBEYXZpZXMiKSArDQogIHRoZW1lX2ppbShiYXNlX3NpemUgPSAxMCkgKw0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpDQoNCmltZ2xvZ28gPC0gbWFnaWNrOjppbWFnZV9yZWFkKCJodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L2Jsb2IvbWFzdGVyL2RhdGEvMjAyMS8yMDIxLTA5LTI4L25iZXItbG9nby5wbmc/cmF3PXRydWUiKQ0KdGlkeV9sb2dvIDwtIG1hZ2ljazo6aW1hZ2VfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvc3RhdGljL3Bsb3RfbG9nby5wbmciKSAlPiUNCiAgbWFnaWNrOjppbWFnZV9yZXNpemUoIjMwMHgzMDAiKQ0KDQpjb3dwbG90OjpnZ2RyYXcoKSArDQogIGNvd3Bsb3Q6OmRyYXdfcGxvdChwKSArDQogIGNvd3Bsb3Q6OmRyYXdfaW1hZ2UoaW1nbG9nbywNCiAgICAgICAgICAgICAgICAgICAgICB4ID0gMC4wMSwNCiAgICAgICAgICAgICAgICAgICAgICB5ID0gLTAuNDgsDQogICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSAwLjIpICsNCiAgY293cGxvdDo6ZHJhd19pbWFnZSh0aWR5X2xvZ28sDQogICAgICAgICAgICAgICAgICAgICAgeCA9IDAuMiwNCiAgICAgICAgICAgICAgICAgICAgICB5ID0gLTAuNDgsDQogICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSAuMSkNCg0KYGBgDQoNCldpdGggdGhpcyBtYW55IGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgbGV0cyB0YWtlIGEgbG9vayBhdCBjb3VudHMgaW4gYSB0cmVlbWFwOg0KDQpgYGB7ciB0cmVlbWFwfQ0KcGFwZXJzX2pvaW5lZCAlPiUgDQogIGdyb3VwX2J5KHByb2dyYW1fZGVzYywgcHJvZ3JhbV9jYXRlZ29yeSkgJT4lDQogIHN1bW1hcmlzZShuID0gbigpLA0KICAgICAgICAgICAgbGFzdF9wYXBlciA9IGxhc3QocHViX2RhdGUpLA0KICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wIikgJT4lIA0KICBzbGljZV9tYXgob3JkZXJfYnkgPSBuLCBuID0gNjApICU+JQ0KICBnZ3Bsb3QoYWVzKGFyZWEgPSBuLCANCiAgICAgICAgICAgICBmaWxsID0gbGFzdF9wYXBlciwgDQogICAgICAgICAgICAgbGFiZWwgPSBwcm9ncmFtX2Rlc2MsIA0KICAgICAgICAgICAgIHN1Ymdyb3VwID0gcHJvZ3JhbV9jYXRlZ29yeSkpICsNCiAgZ2VvbV90cmVlbWFwKCkgKw0KICBnZW9tX3RyZWVtYXBfc3ViZ3JvdXBfYm9yZGVyKCkgKw0KICBnZW9tX3RyZWVtYXBfdGV4dChhZXMoY29sb3IgPSBhZnRlcl9zY2FsZShtYXBfY2hyKGZpbGwsIGJlc3RfY29udHJhc3QpKSksIA0KICAgICAgICAgICAgICAgICAgICBwbGFjZSA9ICJ0b3AiLCANCiAgICAgICAgICAgICAgICAgICAgcmVmbG93ID0gVFJVRSkgKw0KICBnZW9tX3RyZWVtYXBfc3ViZ3JvdXBfdGV4dCgNCiAgICBjb2xvciA9ICJ3aGl0ZSIsIA0KICAgIHBsYWNlID0gImJvdHRvbWxlZnQiLA0KICAgIGZvbnRmYWNlID0gIml0YWxpYyIsIA0KICAgIG1pbi5zaXplID0gMA0KICApICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIkgiKSArDQogIGxhYnMoDQogICAgZmlsbCA9IE5VTEwsIHRpdGxlID0gIk5hdGlvbmFsIEJ1cmVhdSBvZiBFY29ub21pYyBSZXNlYXJjaCBQYXBlcnMiLA0KICAgIHN1YnRpdGxlID0gIlRocmVlIHByaW1hcnkgcHJvZ3JhbSBjYXRlZ29yaWVzIGNvdmVyIDIwIHByb2dyYW1zLiBUaGUgYXJlYSBjb3JyZXNwb25kcyB0byB0aGVcbm51bWJlciBvZiBwYXBlcnMgb2YgZWFjaCBpbiB0aGUgZGF0YXNldC4iLA0KICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEJlbiBEYXZpZXMge25iZXJ3cH0iDQogICkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KV2UgY291bGQgY3JlYXRlIGEgY28tYXV0aG9yc2hpcCBuZXR3b3JrIGdyYXBoaWMgc3RyYWlnaHQgZnJvbSB0aGUgcGFwZXJfYXV0aG9ycyB0YWJsZSB0aGF0IG1ha2VzIHRoZSBsaW5rIGJldHdlZW4gdGhlIHBhcGVycyBhbmQgdGhlaXIgYXV0aG9ycy4NCg0KVG8gc3BlZWQgdXAgdGhlIHByb2Nlc3Mgb2YgcGxvdHRpbmcgZXZlcnkgeWVhciwgbGV0J3MgYnVpbGQgYSBmdW5jdGlvbjoNCg0KYGBge3IgZnVuX25ldH0NCg0KZ2dyYXBoX3RoZW1lIDwtIHRoZW1lX2ppbShiYXNlX3NpemUgPSAxMikgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQ0KICApIA0KDQpmdW5fbmV0IDwtIGZ1bmN0aW9uKHlyKXsNCiAgcGFwZXJfYXV0aG9yc195ZWFyIDwtIHBhcGVyX2F1dGhvcnMgJT4lIA0KICAgIGxlZnRfam9pbihwYXBlcnMpICU+JSANCiAgICBmaWx0ZXIoeWVhciA9PSB5cikNCiAgDQogIGVkZ2VzX2xpc3QgPC0gcGFwZXJfYXV0aG9yc195ZWFyICU+JSANCiAgICBtdXRhdGUoYXV0aG9yX2lkID0gYXMuaW50ZWdlcihhcy5mYWN0b3IoYXV0aG9yKSkpDQogIA0KICBlZGdlc19saXN0IDwtIGVkZ2VzX2xpc3QgJT4lIA0KICAgIGxlZnRfam9pbihlZGdlc19saXN0LCBieSA9ICdwYXBlcicpICU+JSANCiAgICBjb3VudChhdXRob3JfaWQueCwgYXV0aG9yX2lkLnkpICU+JSANCiAgICBtdXRhdGUoDQogICAgbWF4ID0gcG1heChhdXRob3JfaWQueCwgYXV0aG9yX2lkLnkpLA0KICAgIG1pbiA9IHBtaW4oYXV0aG9yX2lkLngsIGF1dGhvcl9pZC55KQ0KICApICU+JSANCiAgdW5pdGUoY2hlY2ssIGMobWluLCBtYXgpLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICBkaXN0aW5jdChjaGVjaywgLmtlZXBfYWxsID0gVFJVRSkgJT4lIA0KICBtdXRhdGUobiA9IGNhc2Vfd2hlbigNCiAgICAoYXV0aG9yX2lkLng9PWF1dGhvcl9pZC55KSB+IDBMLA0KICAgIFRSVUV+bg0KICApKSAlPiUgDQogIHJlbmFtZSgNCiAgICBmcm9tID0gYXV0aG9yX2lkLngsDQogICAgdG8gPSBhdXRob3JfaWQueQ0KICApICU+JSANCiAgc2VsZWN0KGZyb20sIHRvLCBuKQ0KICANCiAgbmV0d29yayA8LSBhc190YmxfZ3JhcGgoZWRnZXNfbGlzdCwgZGlyZWN0ZWQgPSBGQUxTRSkNCiAgDQogIHJldHVybihuZXR3b3JrKQ0KfQ0KDQpgYGANCg0KYGBge3IgZmlyc3RfZ2dyYXBofQ0KcGxvdDgwIDwtIGdncmFwaChncmFwaCA9IGZ1bl9uZXQoMTk4MCksDQogICAgICAgICAgICAgICAgIGxheW91dCA9ICJrayIpICsNCiAgZ2VvbV9ub2RlX3BvaW50KCkgKw0KICBnZW9tX2VkZ2VfZGlhZ29uYWwoY29sb3IgPSAiZGltZ3JleSIsIGFscGhhID0gMC44KSArDQogIGdncmFwaF90aGVtZQ0KDQpwbG90MDAgPC0gZ2dyYXBoKGdyYXBoID0gZnVuX25ldCgyMDAwKSwNCiAgICAgICAgICAgICAgICAgbGF5b3V0ID0gImtrIikgKw0KICBnZW9tX25vZGVfcG9pbnQoKSArDQogIGdlb21fZWRnZV9kaWFnb25hbChjb2xvciA9ICJkaW1ncmV5IiwgYWxwaGEgPSAwLjgpICsNCiAgZ2dyYXBoX3RoZW1lDQoNCmxheW91dCA8LSBjKGFyZWEoDQogIHQgPSAxLA0KICBsID0gMSwNCiAgYiA9IDQsDQogIHIgPSAyDQopLA0KYXJlYSgNCiAgdCA9IDEsDQogIGwgPSAzLA0KICBiID0gNCwNCiAgciA9IDQNCikpDQoNCnBsb3Q4MCArIHBsb3QwMCArDQogIHBsb3RfbGF5b3V0KGRlc2lnbiA9IGxheW91dCkgKw0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiRXZvbHV0aW9uIG9mIGNvLWF1dGhvcnNoaXAgbmV0d29ya3MgZm9yIE5CRVIgcGFwZXJzIiwNCiAgICBzdWJ0aXRsZSA9ICJBbGdvcml0aG06IEthbWRhLUthd2FpIiwNCiAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBgbmJlcndwYCBwYWNrYWdlIGJ5IEJlbiBEYXZpZXMiLA0KICAgIHRhZ19sZXZlbHMgPSBsaXN0KGMoIjE5ODAiLCIyMDAwIikpDQogICkNCmBgYA0KDQpXaGF0IGNhbiBiZSBkb25lIHRvIGZ1cnRoZXIgYW5ub3RhdGUgdGhlIHZpc3VhbCB3aXRoIG1ldHJpY3M/IEluIG5ldHdvcmsgYW5hbHlzaXMsIHRoZSBtb3N0IGNvbW1vbiBtZXRyaWMgZm9yIGVhY2ggbm9kZSBpcyBkZWdyZWUsIHRoYXQgaXMgdGhlIG51bWJlciBvZiBjb25uZWN0aW9ucyBmb3IgZWFjaCBub2RlLiANCg0KYGBge3IgbnVtYmVyX29mX2Nvbm5lY3Rpb25zX2F0X2VhY2hfbm9kZX0NCm5ldDgwIDwtIGZ1bl9uZXQoMTk4MCkgJT4lDQogIGFjdGl2YXRlKGVkZ2VzKSAlPiUNCiAgbXV0YXRlKHdlaWdodHMgPSBjYXNlX3doZW4oDQogICAgIyBTb2xvLWF1dGhvcmVkIHNldCB0byB3ZWlnaHQ9MA0KICAgIG4gPT0gMCB+IDAsDQogICAgIyBXZWlnaHQgPSAxIGZvciBhbGwgb3RoZXJzIGNvbGxhYm9yYXRpb25zDQogICAgVFJVRSB+MQ0KICApKSAlPiUNCiAgIyBOb3cgYWN0aXZhdGUgbm9kZXMNCiAgYWN0aXZhdGUobm9kZXMpICU+JQ0KICAjIENvbXB1dGUgZGVncmVlIGZvciBlYWNoIG5vZGUNCiAgbXV0YXRlKGRlZyA9IGNlbnRyYWxpdHlfZGVncmVlKHdlaWdodHMgPSB3ZWlnaHRzKSkgJT4lDQogICMgRmluZCBhdXRob3Igd2l0aCBtb3N0IGNvbGxhYm9yYXRpb24NCiAgIyAoaGlnaGVzdC1kZWdyZWUgbm9kZSkNCiAgbXV0YXRlKG1heF9kZWcgPSBtYXgoZGVnKSkgJT4lDQogIG11dGF0ZShtYXhfYXV0aG9yID0gY2FzZV93aGVuKA0KICAgIGRlZyA9PSBtYXhfZGVnIH4gMSwNCiAgICBUUlVFIH4gMA0KICApKQ0KDQojIFNhbWUgc3RlcHMgZm9yIHllYXIgMjAwMA0KbmV0MDAgPC0gZnVuX25ldCgyMDAwKSAlPiUNCiAgYWN0aXZhdGUoZWRnZXMpICU+JQ0KICBtdXRhdGUod2VpZ2h0cyA9IGNhc2Vfd2hlbigNCiAgICBuPT0wIH4gMCwNCiAgICBUUlVFIH4xDQogICkpICU+JQ0KICBhY3RpdmF0ZShub2RlcykgJT4lDQogIG11dGF0ZShkZWcgPSBjZW50cmFsaXR5X2RlZ3JlZSh3ZWlnaHRzID0gd2VpZ2h0cykpICU+JQ0KICBtdXRhdGUobWF4X2RlZyA9IG1heChkZWcpKSAlPiUNCiAgbXV0YXRlKG1heF9hdXRob3IgPSBjYXNlX3doZW4oDQogICAgZGVnID09IG1heF9kZWcgfiAxLA0KICAgIFRSVUUgfiAwDQogICkpDQoNCmBgYA0KV2UgbWF5IG5vdyBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgZGVncmVlIGZvciBlYWNoIG5ldHdvcmsuDQoNCmBgYHtyIGF2ZXJhZ2VfZGVncmVlX2VhY2hfbmV0d29ya30NCnN0YXRfZGVnXzgwIDwtIG5ldDgwICU+JQ0KICBhY3RpdmF0ZShub2RlcykgJT4lDQogIGFzX3RpYmJsZSgpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgeWVhciA9ICcxOTgwJywNCiAgICBtZWFuX2RlZyA9IG1lYW4oZGVnKSwNCiAgICBtYXhfZGVnID0gbWVhbihtYXhfZGVnKSkNCg0Kc3RhdF9kZWdfMDAgPC0gbmV0MDAgJT4lDQogIGFjdGl2YXRlKG5vZGVzKSAlPiUNCiAgYXNfdGliYmxlKCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICB5ZWFyID0gJzIwMDAnLA0KICAgIG1lYW5fZGVnID0gbWVhbihkZWcpLA0KICAgIG1heF9kZWcgPSBtZWFuKG1heF9kZWcpKQ0KDQpiaW5kX3Jvd3Moc3RhdF9kZWdfODAsIHN0YXRfZGVnXzAwKQ0KYGBgDQoNClRoZSBudW1iZXIgb2YgY29ubmVjdGlvbiBpcyBpbmNyZWFzaW5nIGJldHdlZW4gMTk4MCBhbmQgMjAwMC4gRm9yIHBhcGVycyBwdWJsaXNoZWQgaW4gMjAwMCwgZWFjaCBhdXRob3IgY29sbGFib3JhdGVkIG9uIGF2ZXJhZ2Ugd2l0aCBhcHByb3hpbWF0ZWx5IHR3byBvdGhlciBhdXRob3JzLg0KDQpXZSBtYXkgbm93IGFkZCB0aGVzZSBpbmZvcm1hdGlvbiB0byB0aGUgcGxvdHMuDQoNCmBgYHtyIHBhdGNod29ya2VkX2trX25ldHdvcmtfcGxvdHN9DQpwODAgPC0gZ2dyYXBoKG5ldDgwLA0KICAgICAgICAgICAgICBsYXlvdXQgPSAia2siKSArDQogIGdlb21fbm9kZV9wb2ludChhZXMoY29sID0gZGVnLCBzaXplID0gbWF4X2F1dGhvcikpICsNCiAgZ2VvbV9lZGdlX2RpYWdvbmFsKGNvbG9yID0gImRpbWdyZXkiLCBhbHBoYSA9IDAuOCkgKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2Iob3B0aW9uID0gIkgiKSArDQogIGd1aWRlcyhzaXplID0gIm5vbmUiLCBjb2xvciA9ICJub25lIikgKw0KICBnZ3JhcGhfdGhlbWUNCg0KcDAwIDwtIGdncmFwaChuZXQwMCwNCiAgICAgICAgICAgICAgbGF5b3V0ID0gImtrIikgKw0KICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbCA9IGRlZywgc2l6ZSA9IG1heF9hdXRob3IpKSArDQogIGd1aWRlcyhzaXplID0gIm5vbmUiKSArDQogIGxhYnMoY29sb3IgPSAiRGVncmVlIikgKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2Iob3B0aW9uID0gIkgiKSArDQogIGdlb21fZWRnZV9kaWFnb25hbChjb2xvciA9ICJkaW1ncmV5IiwgYWxwaGEgPSAwLjgpICsNCiAgZ2dyYXBoX3RoZW1lDQoNCnA4MCArIHAwMCArDQogIHBsb3RfbGF5b3V0KGRlc2lnbiA9IGxheW91dCwNCiAgICAgICAgICAgICAgZ3VpZGVzID0gImNvbGxlY3QiKSArDQogIHBsb3RfYW5ub3RhdGlvbigNCiAgICB0aXRsZSA9ICJFdm9sdXRpb24gb2YgY28tYXV0aG9yc2hpcCBuZXR3b3JrcyBmb3IgTkJFUiBwYXBlcnMiLA0KICAgIHN1YnRpdGxlID0gIkxhcmdlc3QgcG9pbnRzIHNob3cgbm9kZSB3aXRoIG1heGltdW0gZGVncmVlLiBBbGdvcml0aG06IEthbWRhLUthd2FpIiwNCiAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBgbmJlcndwYCBwYWNrYWdlIGJ5IEJlbiBEYXZpZXMiLA0KICAgIA0KICAgIHRhZ19sZXZlbHMgPSBsaXN0KGMoJzE5ODAnLCAnMjAwMCcpKQ0KICApDQpgYGANCg0KTW9yZSBtZXRyaWNzIHJlZ2FyZGluZyB0aGVzZSBuZXR3b3JrcyBhcmUgZGVzY3JpYmVkIGluIFtCZW4gRGF2aWVz4oCZIGJsb2cgcG9zdF0oaHR0cHM6Ly9ibGRhdmllcy5jb20vYmxvZy9mZW1hbGUtcmVwcmVzZW50YXRpb24tY29sbGFib3JhdGlvbi1uYmVyLykuDQoNCkJlbiBOb3dhaydzIHN1cGVyYiBjb21wbGV0ZSBuZXR3b3JrIGRpYWdyYW0gc3VibWlzc2lvbjoNCg0KYGBge3IgQmVuX05vd2Frc190d2VldH0NCnR3ZWV0cm1kOjppbmNsdWRlX3R3ZWV0KCJodHRwczovL3R3aXR0ZXIuY29tL0Jqbk5vd2FrL3N0YXR1cy8xNDQyODA3MDYwMzk2MTM0NDA4IikNCmBgYA0KDQpUaGUgdHlwZXMgb2YgcmVsYXRpb25zaGlwcywgYXMgaW5mZXJlbmNlcywgYmV0d2VlbiBwcm9ncmFtIGFuZCB0aXRsZSB3b3JkcyBhcmUgd2hhdCB3ZSB3YW50IHRvIGRpc2NvdmVyIG1vcmUgYWJvdXQgaW4gYSBwcmVkaWN0aXZlIG1vZGVsLg0KDQojIyBCdWlsZCBhbmQgVHVuZSBhIE1vZGVsDQoNCkxldOKAmXMgc3RhcnQgb3VyIG1vZGVsaW5nIGJ5IHNldHRpbmcgdXAgb3VyIOKAnGRhdGEgYnVkZ2V0LuKAnSBXZeKAmWxsIHN0cmF0aWZ5IGJ5IG91ciBvdXRjb21lIGBwcm9ncmFtX2Rlc2NgLg0KDQpgYGB7ciBpbml0aWFsX3NwbGl0fQ0KbmJlcl9zcGxpdCA8LSBpbml0aWFsX3NwbGl0KHBhcGVyc19qb2luZWQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmF0YSA9IHByb2dyYW1fZGVzYykNCm5iZXJfdHJhaW4gPC0gdHJhaW5pbmcobmJlcl9zcGxpdCkNCm5iZXJfdGVzdCA8LSB0ZXN0aW5nKG5iZXJfc3BsaXQpDQoNCnNldC5zZWVkKDIwMjEpDQpuYmVyX2ZvbGRzIDwtIHZmb2xkX2N2KG5iZXJfdHJhaW4sIHN0cmF0YSA9IHByb2dyYW1fZGVzYykNCg0KYGBgDQoNCk5leHQsIGxldOKAmXMgc2V0IHVwIGZlYXR1cmUgZW5naW5lZXJpbmcuIFdlIHdpbGwgbmVlZCB0byBbdHJhbnNmb3JtIG91ciB0ZXh0IGRhdGFdKGh0dHBzOi8vdGV4dHJlY2lwZXMudGlkeW1vZGVscy5vcmcvYXJ0aWNsZXMvV29ya2luZy13aXRoLW4tZ3JhbXMuaHRtbCkgaW50byBmZWF0dXJlcyB1c2VmdWwgZm9yIG91ciBtb2RlbCBieSB0b2tlbml6aW5nIGFuZCBjb21wdXRpbmcgZWl0aGVyIHRlcm0gZnJlcXVlbmN5IG9yIHRmLWlkZi4gTGV04oCZcyBhbHNvIHVwc2FtcGxlIHNpbmNlIG91ciBkYXRhc2V0IGlzIGltYmFsYW5jZWQsIHdpdGggbWFueSBtb3JlIG9mIHNvbWUgb2YgdGhlIGNhdGVnb3JpZXMgdGhhbiBvdGhlcnMuIEZpbmFsbHksIGZvciBHTE1uZXQgcGVuYWx0aWVzIHRvIHdvcmsgcHJvcGVybHksIGFsbCBmZWF0dXJlcyBtdXN0IGJlIG5vcm1hbGl6ZWQuDQoNCmBgYHtyIHJlY2lwZX0NCg0KbmJlcl9yZWNfdGZpZGYgPC0NCiAgcmVjaXBlKHByb2dyYW1fZGVzYyB+IHRpdGxlICsgcHViX2RhdGUsIA0KICAgICAgICAgZGF0YSA9IG5iZXJfdHJhaW4pICU+JQ0KICBzdGVwX3Rva2VuaXplKHRpdGxlKSAlPiUNCiAgc3RlcF9zdGVtKHRpdGxlKSAlPiUgDQogIHN0ZXBfbmdyYW0odGl0bGUsIG51bV90b2tlbnMgPSAzKSAlPiUgDQogIHN0ZXBfdG9rZW5maWx0ZXIodGl0bGUsIG1heF90b2tlbnMgPSAyMDApICU+JQ0KICBzdGVwX3RmaWRmKHRpdGxlKSAlPiUNCiAgc3RlcF9kYXRlKHB1Yl9kYXRlLA0KICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJtb250aCIsInllYXIiKSwNCiAgICAgICAgICAgIGtlZXBfb3JpZ2luYWxfY29scyA9IEZBTFNFKSAlPiUNCiAgc3RlcF9kdW1teShhbGxfbm9taW5hbF9wcmVkaWN0b3JzKCkpICU+JQ0KICBzdGVwX3Vwc2FtcGxlKHByb2dyYW1fZGVzYykgJT4lDQogIHN0ZXBfbm9ybWFsaXplKGFsbF9udW1lcmljX3ByZWRpY3RvcnMoKSkgJT4lIA0KICBzdGVwX3p2KGFsbF9wcmVkaWN0b3JzKCkpDQoNCmBgYA0KDQpUaGVuLCBsZXTigJlzIGNyZWF0ZSBvdXIgbW9kZWwgc3BlY2lmaWNhdGlvbiBmb3IgYSBsYXNzbyBtb2RlbC4gV2UgbmVlZCB0byB1c2UgYSBtb2RlbCBzcGVjaWZpY2F0aW9uIHRoYXQgY2FuIGhhbmRsZSBtdWx0aWNsYXNzIGRhdGEsIGluIHRoaXMgY2FzZSBgbXVsdGlub21fcmVnKClgLg0KDQpgYGB7ciBtdWx0aV9zcGVjfQ0KbXVsdGlfc3BlYyA8LQ0KICBtdWx0aW5vbV9yZWcocGVuYWx0eSA9IHR1bmUoKSwgDQogICAgICAgICAgICAgICBtaXh0dXJlID0gMSkgJT4lDQogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpICU+JQ0KICBzZXRfZW5naW5lKCJnbG1uZXQiKQ0KDQpgYGANCg0KU2luY2UgdGhlIGxhc3NvIHJlZ3VsYXJpemF0aW9uIGBwZW5hbHR5YCBpcyBhIGh5cGVycGFyYW1ldGVyIG9mIHRoZSBtb2RlbCAod2UgY2Fu4oCZdCBmaW5kIHRoZSBiZXN0IHZhbHVlIGZyb20gZml0dGluZyB0aGUgbW9kZWwgYSBzaW5nbGUgdGltZSksIGxldOKAmXMgdHVuZSBvdmVyIGEgZ3JpZCBvZiBwb3NzaWJsZSBwZW5hbHR5IHBhcmFtZXRlcnMuDQoNCmBgYHtyIHJlZ3VsYXJfZ3JpZH0NCm5iZXJfZ3JpZCA8LSBncmlkX3JlZ3VsYXIocGVuYWx0eShyYW5nZSA9IGMoLTUsIDApKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IDE2KQ0KDQpgYGANCg0KVG8gc3BlZWQgdGhpbmdzIHVwLCB3ZSB3aWxsIHVzZSBhbGwgYnV0IG9uZSBvZiB0aGUgY29tcHV0ZXIncyBjb3Jlcy4NCg0KYGBge3IgcGFyYWxsZWwsIGV2YWwgPSBGQUxTRX0NCmN0cmwgPC0gY29udHJvbF9yYWNlKHBhcmFsbGVsX292ZXIgPSAiZXZlcnl0aGluZyIsDQogICAgICAgICAgICAgICAgICAgICBzYXZlX3ByZWQgPSBUUlVFKQ0KDQphbGxfY29yZXMgPC0gcGFyYWxsZWxseTo6YXZhaWxhYmxlQ29yZXMob21pdCA9IDEpDQphbGxfY29yZXMNCg0KZnV0dXJlOjpwbGFuKCJtdWx0aXNlc3Npb24iLCB3b3JrZXJzID0gYWxsX2NvcmVzKSAjIG9uIFdpbmRvd3MNCmBgYA0KDQpUaWR5bW9kZWxzIHdpbGwgbW9kZWwgZXZlcnkgcGVuYWx0eSBwYXJhbWV0ZXIgb3ZlciB0aGUgZmlyc3QgY291cGxlIG9mIGZvbGRzLCBidXQgd2lsbCBiZWdpbiB0byBkcm9wIHZhbHVlcyB1bmxpa2VseSB0byBwZXJmb3JtIHRvIHNhdmUgcHJvY2Vzc2luZyB0aW1lLg0KDQpgYGB7ciB0dW5pbmcsIGV2YWw9RkFMU0V9DQoNCm5iZXJfd2YgPC0NCiAgd29ya2Zsb3cobmJlcl9yZWNfdGZpZGYsIG11bHRpX3NwZWMpDQoNCm5iZXJfcnMgPC0NCiAgbmJlcl93ZiAlPiUgDQogIHR1bmVfZ3JpZCgNCiAgICByZXNhbXBsZXMgPSBuYmVyX2ZvbGRzLA0KICAgIHZlcmJvc2UgPSBUUlVFLA0KICAgIG1ldHJpYyA9IG1ldHJpY19zZXQocm9jX2F1bnUpLA0KICAgIGdyaWQgPSBuYmVyX2dyaWQgICANCiAgKQ0KDQpgYGANCg0KYGBge3IgdHVuaW5nX3J1biwgaW5jbHVkZT1GQUxTRX0NCm5iZXJfd2YgPC0NCiAgd29ya2Zsb3cobmJlcl9yZWNfdGZpZGYsIG11bHRpX3NwZWMpDQoNCmlmIChmaWxlLmV4aXN0cyhoZXJlOjpoZXJlKCJkYXRhIiwibmJlcl9ycy5ybWQiKSkpIHsNCiAgbmJlcl9ycyA8LSByZWFkX3JkcyhoZXJlOjpoZXJlKCJkYXRhIiwibmJlcl9ycy5ybWQiKSkNCn0gZWxzZSB7DQoNCm5iZXJfcnMgPC0NCiAgbmJlcl93ZiAlPiUgDQogIHR1bmVfZ3JpZCgNCiAgICByZXNhbXBsZXMgPSBuYmVyX2ZvbGRzLA0KICAgIHZlcmJvc2UgPSBUUlVFLA0KICAgIG1ldHJpYyA9IG1ldHJpY19zZXQocm9jX2F1bnUpLA0KICAgIGdyaWQgPSBuYmVyX2dyaWQgICANCiAgKQ0KDQp3cml0ZV9yZHMobmJlcl9ycywgDQogICAgICAgICAgaGVyZTo6aGVyZSgiZGF0YSIsICJuYmVyX3JzLnJtZCIpKQ0KfQ0KYGBgDQoNCg0KSG93IGRpZCBpdCB0dXJuIG91dD8NCg0KYGBge3IgYXV0b3Bsb3RfdHJhaW5fcmVzdWx0c30NCmF1dG9wbG90KG5iZXJfcnMpDQoNCihmaW5hbF9wZW5hbHR5IDwtIHNlbGVjdF9ieV9vbmVfc3RkX2VycihuYmVyX3JzLCANCiAgICAgICAgICAgICAgICAgICAgICBtZXRyaWMgPSAicm9jX2F1YyIsDQogICAgICAgICAgICAgICAgICAgICAgZGVzYyhwZW5hbHR5KSkpDQpgYGANCg0KDQoNCmBgYHtyIGZpbmFsaXplX2ZpdH0NCmZpbmFsX3JzIDwtDQogIG5iZXJfd2YgJT4lDQogIGZpbmFsaXplX3dvcmtmbG93KGZpbmFsX3BlbmFsdHkpICU+JQ0KICBsYXN0X2ZpdChuYmVyX3NwbGl0KQ0KDQpmaW5hbF9ycw0KYGBgDQoNCkhvdyBkaWQgb3VyIGZpbmFsIG1vZGVsIHBlcmZvcm0gb24gdGhlIHRyYWluaW5nIGRhdGE/DQoNCmBgYHtyIG1ldHJpY3N9DQpjb2xsZWN0X21ldHJpY3MoZmluYWxfcnMpDQpgYGANCg0KV2UgY2FuIHZpc3VhbGl6ZSB0aGUgZGlmZmVyZW5jZSBpbiBwZXJmb3JtYW5jZSBhY3Jvc3MgY2xhc3NlcyB3aXRoIGEgY29uZnVzaW9uIG1hdHJpeC4NCg0KYGBge3IgbGFyZ2VfY29uZnVzaW9uX21hdHJpeH0NCiN8IGZpZy53aWR0aDogMTINCiN8IGZpZy5hc3A6IDENCiN8IGZpZy5hbGlnbjogImNlbnRlciINCiN8IG91dC53aWR0aDogIjMwMCUiDQojfCBmaWcuYWx0OiA+DQojfCAgIEEgY29uZnVzaW9uIG1hdHJpeCBpbGx1c3RyYXRpbmcgdGhlIGNvdW50cyBvZiBwcmVkaWN0aW9ucw0KI3wgICB2ZXJzdXMgdGhlIHRydXRoIGZvciB0aGUgdHJhaW5pbmcgc2V0IHVzZWQgaW4gDQojfCAgIGNyZWF0aW5nIHRoZSBtb2RlbA0KDQpjb2xsZWN0X3ByZWRpY3Rpb25zKGZpbmFsX3JzKSAlPiUNCiAgY29uZl9tYXQocHJvZ3JhbV9kZXNjLCAucHJlZF9jbGFzcykgJT4lDQogIGF1dG9wbG90KCkgKw0KICB0aGVtZV9qaW0oYmFzZV9zaXplID0gMTIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgbGFicyh0aXRsZSA9ICJOQkVSIENsYXNzaWZpY2F0aW9uIENvbmZ1c2lvbiBNYXRyaXggb24gVHJhaW5pbmciKQ0KYGBgDQoNCldlIGNhbiBhbHNvIHZpc3VhbGl6ZSB0aGUgUk9DIGN1cnZlcyBmb3IgZWFjaCBjbGFzcy4NCg0KYGBge3Igcm9jX2N1cnZlLCBmaWcuYXNwPTF9DQoNCmNvbGxlY3RfcHJlZGljdGlvbnMoZmluYWxfcnMpICU+JSANCiAgcm9jX2N1cnZlKHRydXRoID0gcHJvZ3JhbV9kZXNjLA0KICAgICAgICAgICAgYC5wcmVkX0Fzc2V0IFByaWNpbmdgOmAucHJlZF9QdWJsaWMgRWNvbm9taWNzYCkgJT4lDQogIGdncGxvdChhZXMoMSAtIHNwZWNpZmljaXR5LCBzZW5zaXRpdml0eSwgY29sb3IgPSAubGV2ZWwpKSArDQogIGdlb21fYWJsaW5lKA0KICAgIHNsb3BlID0gMSwNCiAgICBjb2xvciA9ICJncmF5NTAiLA0KICAgIGx0eSA9IDIsDQogICAgYWxwaGEgPSAwLjgNCiAgKSArDQogIGdlb21fcGF0aChzaXplID0gMS41LCBhbHBoYSA9IDAuNykgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdmlyaWRpczo6dmlyaWRpc19wYWwob3B0aW9uID0gIkgiKSgyMSlbYygxLDgsMTUsMiw5LDE2LDMsMTAsMTcsNCwxMSwxOCw1LDEyLDE5LDYsMTMsMjAsNywxNCwyMSldLCBsYWJlbHMgPSBzY2FsZXM6OmxhYmVsX3dyYXAod2lkdGggPSAxMikpICsNCiAgbGFicyhjb2xvciA9IE5VTEwpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGxhYnModGl0bGUgPSAiUk9DIGJldHRlciBmb3IgRGV2ZWxvcG1lbnQgRWNvbm9taWNzIHRoYW4gbW9zdCBvdGhlcnMiKSArDQogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpICsNCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQ0KDQpgYGANCg0KRmluYWxseSwgd2UgY2FuIGV4dHJhY3QgKGFuZCBzYXZlLCBpZiB3ZSBsaWtlKSB0aGUgZml0dGVkIHdvcmtmbG93IGZyb20gb3VyIHJlc3VsdHMgdG8gdXNlIGZvciBwcmVkaWN0aW5nIG9uIG5ldyBkYXRhLg0KDQpGb3IgcGFwZXIgdzA0NTYsIGByIG5iZXJfdGVzdFs4NSwgXSR0aXRsZWAsIHRoZSBtb2RlbCBtYWtlcyB0aGUgZm9sbG93aW5nIHByZWRpY3Rpb24gcHJvYmFiaWxpdGllczoNCg0KYGBge3J9DQpmaW5hbF9maXR0ZWQgPC0gZXh0cmFjdF93b3JrZmxvdyhmaW5hbF9ycykNCiMjIGNhbiBzYXZlIHRoaXMgZm9yIHByZWRpY3Rpb24gbGF0ZXIgd2l0aCByZWFkcjo6d3JpdGVfcmRzKCkNCg0KcHJlZGljdChmaW5hbF9maXR0ZWQsIG5iZXJfdGVzdFs4NSwgXSwgdHlwZSA9ICJwcm9iIikgJT4lIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSkgJT4lIA0KICBhcnJhbmdlKGRlc2ModmFsdWUpKQ0KYGBgDQoNClRoZSB0ZXN0IGRhdGEncyBjb3JyZWN0IGFuc3dlciAodHJ1dGgpIGlzIGByIG5iZXJfdGVzdFs4NSwgXSRwcm9ncmFtX2Rlc2NgDQoNCk15IHR3ZWV0IGZvciB0aGUgd2VlazoNCg0KYGBge3J9DQp0d2VldHJtZDo6aW5jbHVkZV90d2VldCgiaHR0cHM6Ly90d2l0dGVyLmNvbS9qaW1fZ3J1bWFuL3N0YXR1cy8xNDQ0NzE1ODAzNjMxMzU3OTU4IikNCmBgYA0KDQo=