Prepare analysis workflow

Set parameters

knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file(),
                     fig.width=15,
                     digit=5,
                     scipen=8)
options(readr.show_progress = FALSE,
        digits=5, 
        scipen=8,
        future.globals.maxSize = +Inf)

Set filepaths and parameters

project_dir <- rprojroot::find_rstudio_root_file()

if(is.null(project_dir)){
  project_dir <- getwd()
  warning(sprintf("No rstudio project root file  found. 
                  Setting project directory to current workflow.Rmd file location: %s. 
                  Override if needed.",
                  project_dir))
 
}
message(sprintf("Project directory: %s",
                project_dir))
Project directory: /home/rfarouni/Documents/index_hopping

Load libraries

library(rhdf5)
#library(DropletUtils) # install but not load
library(tidyverse)
library(matrixStats)
library(broom)
library(furrr)
library(tictoc)
library(data.table)
library(cowplot)
plan(multiprocess)

Load functions

code_dir <- file.path(project_dir, "code")
source(file.path(code_dir, "1_create_joined_counts_table.R"))
source(file.path(code_dir, "2_create_counts_by_outcome_table.R"))
source(file.path(code_dir, "3_estimate_sample_index_hopping_rate.R"))
source(file.path(code_dir, "4_compute_summary_statistics.R"))
source(file.path(code_dir, "5_reassign_hopped_reads.R"))
source(file.path(code_dir, "6_purge_phantom_molecules.R"))
source(file.path(code_dir, "7_call_cells.R"))
source(file.path(code_dir, "8_summarize_purge.R"))
source(file.path(code_dir, "9_plotting_functions.R"))

Load data

validation_output_dir <- file.path(project_dir, "data", "hiseq4000_validation")
figures_dir <- file.path(validation_output_dir, "figures")
data <- read_tsv(file.path(validation_output_dir,
                           "hiseq4000_inner_joined_with_labels_hg38_ensembl95.txt"))
Parsed with column specification:
cols(
  cell = col_character(),
  gene = col_character(),
  umi = col_double(),
  s1_nonplexed = col_double(),
  s2_nonplexed = col_double(),
  s1_plexed = col_double(),
  s2_plexed = col_double(),
  outcome = col_character(),
  label = col_character()
)
data

Compute Index hopping rate

Estimates conditional on duplication level and label

summary_counts <-
    data %>%
  mutate(r=as.integer(rowSums(.[c(6,7)]))) %>%
  arrange(r) %>%
  group_by(r, label) %>%
  summarize_at(vars(matches("^s1_pl|s2_pl")), 
               list(~ sum(.)))  %>%
  mutate(s1_hopped=if_else(label %in% c("0,f", "r,f"), s2_plexed,0),
         s2_hopped=if_else(label %in% c("f,0", "f,r"), s1_plexed,0),
         s1_nonhopped=if_else(label %in% c("r,0", "r,f"), s1_plexed,0),
         s2_nonhopped=if_else(label %in% c("0,r", "f,r"), s2_plexed,0)) %>%
  select(-s1_plexed,- s2_plexed)

summary_counts

Estimates conditional on duplication level

summary_counts_conditional <-
summary_counts  %>%
  group_by(r) %>%
  summarize_at(vars(matches("^s")), 
               list(~ sum(.)))%>%
  mutate(SIHR_12 = s1_hopped/(s1_hopped+s1_nonhopped),
         SIHR_21 = s2_hopped/(s2_hopped+s2_nonhopped),
         frac_s1= (s1_hopped+s1_nonhopped)/ (s1_hopped+s1_nonhopped+s2_hopped+s2_nonhopped))
summary_counts_conditional

Marginal estimates

summary_counts_marginal <- 
  summary_counts %>%
  ungroup() %>%
  summarize_at(vars(matches("^s")), 
               list(~ sum(.)))%>%
  mutate(SIHR_12 = s1_hopped/(s1_hopped+s1_nonhopped),
         SIHR_21 = s2_hopped/(s2_hopped+s2_nonhopped),
         SIHR=1-(s1_nonhopped+s2_nonhopped)/(s1_hopped+s1_nonhopped +s2_hopped+s2_nonhopped),
         frac_s1= (s1_hopped+s1_nonhopped)/ (s1_hopped+s1_nonhopped+s2_hopped+s2_nonhopped))
summary_counts_marginal
p1 <- 
  ggplot(summary_counts_conditional) +
    geom_line(aes(x = r,
                    y = SIHR_12*100,
               colour="SIHR_12"))+
    geom_line(aes(x = r,
                    y = SIHR_21*100,
               colour="SIHR_21")) +
      geom_line(aes(x = r,
                    y = frac_s1,
               colour="frac_s1")) +
    geom_hline(yintercept = unlist(summary_counts_marginal[5:7])* c(100, 100, 100),
               linetype="dashed") +
    xlim(0,90) + 
  ylim(0,1) 
p1

NA

Molecules

summary_mol_counts <-
  data %>%
  mutate(r=as.integer(rowSums(.[c(6,7)]))) %>%
  arrange(r) %>%  
  mutate_at(vars(matches("^s")), 
            list(~ as.integer(.!=0)))  %>%
  group_by(r, label) %>%
  summarize_at(vars(matches("^s1_pl|^s2_pl")), 
               list(~ sum(.)))  %>%
    mutate(s1_phantom=if_else(label %in% c("0,f", "r,f"), s2_plexed,0L),
         s2_phantom=if_else(label %in% c("f,0", "f,r"), s1_plexed,0L),
         s1_real=if_else(label %in% c("r,0", "r,f"), s1_plexed,0L),
         s2_real=if_else(label %in% c("0,r", "f,r"), s2_plexed,0L)) %>%
  select(-s1_plexed,- s2_plexed) 
summary_mol_counts 

Number of Fugue Molecules

summary_mol_counts %>%
  filter(label %in% c("0,f", "f,0")) %>%
  ungroup() %>%
  summarize_at(vars(ends_with("_phantom")), sum)

Marginal Summaries

summary_mol_counts_marginal <-
  summary_mol_counts%>%
  ungroup() %>%
  summarize_at(vars(matches("^s")), 
               list(~ sum(.))) %>%
  mutate(ppm_12 = s1_phantom/(s1_phantom+s1_real),# prop hopped phantom molec
         ppm_21 = s2_phantom/(s2_phantom+s2_real),
         ppm_1 = s2_phantom/(s2_phantom+s1_real), # prop phantom molec
         ppm_2 = s1_phantom/(s1_phantom+s2_real),
         ppm=(s1_phantom+s2_phantom)/(s1_phantom+s1_real+s2_phantom+s2_real),
         frac_mol_s1= (s1_phantom+s1_real)/ (s1_phantom+s1_real+s2_phantom+s2_real))
summary_mol_counts_marginal
summary_mol_counts_conditional <-
  summary_mol_counts%>%
  group_by(r) %>%
  summarize_at(vars(matches("^s")), 
               list(~ sum(.))) %>%
  mutate(ppm_12 = s1_phantom/(s1_phantom+s1_real),
         ppm_21 = s2_phantom/(s2_phantom+s2_real),
         ppm=(s1_phantom+s2_phantom)/(s1_phantom+s1_real+s2_phantom+s2_real),
         frac_mol_s1= (s1_phantom+s1_real)/ (s1_phantom+s1_real+s2_phantom+s2_real))
summary_mol_counts_conditional
p4 <- ggplot(summary_mol_counts_conditional) +
  geom_line(aes(x = r,
                  y = ppm_12,
             colour="ppm_12"))+
  geom_line(aes(x = r,
                  y =  ppm_21,
             colour=" ppm_21")) +
  #geom_hline(yintercept = unlist(summary_mol_counts_marginal[5:7]), linetype="dashed") +
  xlim(0,300) 
p4

 #ggsave("phantom_molecules_validation.pdf", p4,  width =8, height = 5)

Examine extent of contamination in cells

summary_mol_counts_cell<-
  data %>%
  filter(label!="NA") %>%
  mutate_at(vars(matches("^s")), 
            list(~ as.integer(.!=0)))  %>%
  group_by(cell, label) %>%
  summarize_at(vars(matches("^s1_pl|^s2_pl")), 
               list(~ sum(.)))  %>%
    mutate(s1_phantom=if_else(label %in% c("f,0", "f,r"), s1_plexed,0L),
         s2_phantom=if_else(label %in% c("0,f", "r,f"), s2_plexed,0L),
         s1_real=if_else(label %in% c("r,0", "r,f"), s1_plexed,0L),
         s2_real=if_else(label %in% c("0,r", "f,r"), s2_plexed,0L)) %>%
  select(-s1_plexed,- s2_plexed) %>%
  group_by(cell) %>%
  summarize_at(vars(matches("^s")), 
               list(~ sum(.))) %>% 
    mutate_at(vars(matches("^s")), 
            list(nonempty= ~ as.integer(.!=0)))  %>%
  unite(label,matches("nonempty"), sep=",") %>%
  mutate(cell_status= 
           case_when(label %in% c("0,0,0,1","0,0,1,0") ~ "real",
                     label %in% c("0,0,1,1") ~ "real",
                     label %in% c("1,0,0,0","0,1,0,0", "1,1,0,0") ~  "phantom",
                     label %in% c("1,0,0,1","0,1,1,0") ~  "phantom",
                     TRUE ~  "contaminated"))  %>%
  mutate(s1_total =(s1_phantom+s1_real),
         s2_total =(s2_phantom+s2_real),
         s1_ppm = s1_phantom/(s1_total),
         s2_ppm = s2_phantom/(s2_total))
  
summary_mol_counts_cell
cell_status_tally <-
  summary_mol_counts_cell %>%
  group_by(cell_status,label) %>%
  tally(sort=TRUE)
cell_status_tally
n_affected_cells <- 
  cell_status_tally %>%
  ungroup() %>%
  filter(cell_status!="real") %>% 
  summarise(n=sum(n)) %>% 
  pull(n)
n_affected_cells <- n_affected_cells + 1502 +6
n_affected_cells
[1] 64579
n_total_cells <-
  summary_mol_counts_cell %>%
  mutate_at(vars(matches("_total")), 
            list(~ as.integer(.!=0))) %>%
  ungroup() %>%
  summarise_at(vars(matches("_total")), list(~sum(.))) %>%
  mutate(cells_total= s1_total+s2_total)%>%
  pull(cells_total)
n_total_cells 
[1] 322321

Proportion of affected cells

p_affected_cells <-  n_affected_cells/n_total_cells
p_affected_cells
[1] 0.20036

For each cell-barcode, plot the number of phantom molecules against the number of total molecules associated with it.

Sample 1 plot

p2 <- ggplot(summary_mol_counts_cell %>%
               filter(cell_status!="real" & s1_phantom >0 )) +
  geom_point(aes(x = s1_total,
                  y = s1_phantom,
                 colour=cell_status)) +
  scale_x_log10() +
  scale_y_log10()

p2

 #ggsave("phantom_molecules_validation.pdf", p2,  width =8, height = 5)

Sample 2 plot

p3 <- ggplot(summary_mol_counts_cell %>%
               filter(cell_status!="real" & s2_phantom >0 )) +
  geom_point(aes(x = s2_total,
                  y = s2_phantom,
                 colour=cell_status),
             size=0.7,
             alpha=0.6) +
  scale_x_log10() +
  scale_y_log10() +
    labs(x="Total Number of Molecules",
       y="Number of Phantom Molecules")  

p3


ggsave(file.path(figures_dir,"phantom_cells_validation_s2.pdf"), p3,  width =8, height = 5)

Run workflow on multiplexed data

read_counts <- 
  data %>% 
  filter(label!="NA")%>%
  select(-ends_with("nonplexed")) %>%
  set_names(c("cell", "gene", "umi", "s1", "s2", "outcome", "label"))
read_counts
S <- 2
sample_names <- colnames(read_counts)[4:(S+3)]
sample_names
[1] "s1" "s2"
tic("Step 2: creating outcome counts datatable with grouping vars")

outcome_counts <- create_outcome_counts(read_counts%>%
                                          select(-label), 
                                        sample_names,  
                                        min_frac=0.8)
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(sample_names)` instead of `sample_names` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
toc()
Step 2: creating outcome counts datatable with grouping vars: 1.437 sec elapsed
outcome_counts
tic("Step 3: creating a chimera counts datatable and estimating hopping rate")
  fit_out <-
    estimate_hopping_rate(
      outcome_counts,
      S
    )
`...` must not be empty for ungrouped data frames.
Did you want `data = everything()`?unnest() has a new interface. See ?unnest for details.
Try `df %>% unnest(c(tidied, confint_tidied, max_r))`, with `mutate()` if neededThe `.drop` argument of `unnest()` is deprecated as of tidyr 1.0.0.
All list-columns are now preserved.
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.
  toc()
Step 3: creating a chimera counts datatable and estimating hopping rate: 0.204 sec elapsed
fit_out 
$glm_estimates

$chimera_counts
NA
  # compute_molecular_complexity_profile
  tic("Step 4: compute molecular complexity profile and other summary statistics")
  summary_stats <-
    compute_summary_stats(
      outcome_counts,
      fit_out$glm_estimates$phat,
      sample_names
    )
  toc()
Step 4: compute molecular complexity profile and other summary statistics: 0.2 sec elapsed
summary_stats
$summary_estimates

$marginal

$conditional

$pi_r_hat
NA

Set the trade-off ratio cost cutoff (torc). The parameter torc represents the number of real molecules one is willing to incorrectly discard in order to correctly purge one phantom molecule. Since discarding a large proportion of the data is undesirable, reasonable values of torc are expected to be within the range of 1-5.

torc <- 3 
tic("Step 5: reassign read counts, determine cutoff, and mark retained observations")

  outcome_counts <-
    reassign_reads_and_mark_retained_observations(
      outcome_counts,
      summary_stats,
      sample_names,
      fit_out,
      torc
    )
  # get the tradoff ratio cutoff
  summary_stats <- get_threshold(outcome_counts, summary_stats)

  toc()
Step 5: reassign read counts, determine cutoff, and mark retained observations: 1.142 sec elapsed
summary_counts_marginal
tic("Step 6: Purge and save read counts datatable to disk")

read_counts <-
  left_join(read_counts %>%
    select(outcome, cell, umi, gene, sample_names, label),
  outcome_counts,
  by = c("outcome")
  ) %>%
  select(-outcome)

toc()
Step 6: Purge and save read counts datatable to disk: 2.957 sec elapsed

Compare the SIHR estimates with ground truth estimates

p5 <- 
  ggplot(summary_counts_conditional) +
  geom_line(aes(x = r,
                  y = SIHR_12,
             colour="12"))+
  geom_line(aes(x = r,
                  y = SIHR_21,
             colour="21"))  +
geom_hline(aes(yintercept =  summary_counts_marginal$SIHR, 
               colour="true mean"), 
           linetype="solid",
           size=.5)   +
geom_hline(aes(yintercept =  fit_out$glm_estimates$SIHR, 
               colour="estimate"),
           linetype="solid",
           size=.1)   +
    geom_linerange(data=summary_counts_conditional, 
                  aes(x=r,
                      ymax=1-fit_out$glm_estimates$phat_low,
                      ymin=1-fit_out$glm_estimates$phat_high,
                      colour="estimate"), 
                  size=.5)+ 
  xlim(1,210) +
  ylim(0.002,0.005)
#ggsave("index_hopping_rate_200.pdf", p5, width=9, height=6)
p5

Determine the number of false positives and false negatives

read_counts <-
    read_counts %>%
  ungroup() %>%
  arrange(-qr) %>%
  mutate(t= case_when(
      label %in% c("f,r","0,r") ~ 2,
      label %in% c("r,f","r,0") ~ 1    ),
    f= case_when(
      label %in% c("f,r","f,0") ~ 1,
      label %in% c("r,f","0,f") ~ 2    )) %>%
  mutate(tp= if_else( t == s, 1L, 0L, missing =0L),
         fp= if_else( f == s, 1L, 0L, missing =0L),
         tn= if_else( f != s, 1L, 0L, missing =0L),
         fn= if_else( t != s, 1L, 0L, missing =0L),
         tp0= if_else( t == 0, 1L, 0L, missing =0L), #0 if  predict all molecules to be phantom
         fn0= if_else( t != 0, 1L, 0L, missing =0L),
         tn0= if_else( f != 0, 1L, 0L, missing =0L),
         fp0= if_else( f == 0, 1L, 0L, missing =0L),
         tp_max= if_else( t == s_maxprop, 1L, 0L, missing =0L),
         fp_max= if_else( f == s_maxprop, 1L, 0L, missing =0L),
         tn_max= if_else( f != s_maxprop, 1L, 0L, missing =0L),
         fn_max= if_else( t != s_maxprop, 1L, 0L, missing =0L)) 

The maximum read fraction method

false_counts_maxprop <-
  read_counts %>%
  summarize_at(vars(c("tp_max", "fp_max", "tn_max", "fn_max")),
            list( ~ sum(.))) %>%
  set_names(c("tp", "fp", "tn", "fn"))
false_counts_maxprop

The tor method

false_counts_min_cutoff <-
  read_counts %>%
  summarize_at(vars(c("tp", "fp", "tn", "fn")),
            list( ~ sum(.))) 
false_counts_min_cutoff

No purging

false_counts_nopurging <- 
  read_counts%>%
  summarize(n_cugs = n(),
            n_real= sum(t>0, na.rm = TRUE),
            n_fantom = sum(f>0, na.rm = TRUE),
            n_mol=n_real+n_fantom,
            g =n_cugs- n_real,
            u = n_mol-n_cugs,
            tp=n_cugs-g,
            fp=u+g,
            tn=0,
            fn=0)

false_counts_nopurging

TOR cutoff

read_counts <-
  read_counts %>%
  mutate_at(vars(c("tp", "fp", "tn", "fn","tp0", "fp0", "tn0", "fn0")),
            list(cum= ~ cumsum(.)))  %>%
  mutate_at(vars(c("tp_cum", "fp_cum", "tn_cum", "fn_cum")), 
            list( ~ (last(.)-lag(., default =0)))) %>%
  mutate(tp_t=tp_cum + tp0_cum,
         fp_t=fp_cum + fp0_cum,
         tn_t=tn_cum + tn0_cum,
         fn_t=fn_cum + fn0_cum,
         fpm= first(fp_t)- fp_t, 
         fnm= fn_t-first(fn_t),
         tor_true= fnm/fpm)
false_counts_tor_cutoff <-
  read_counts%>%
  filter(retain) %>%
  slice(1)%>%
  select(c("s1", "s2", "qr", "tor", "tp_t", "fp_t", "tn_t", "fn_t", "fpm", "fnm", "tor_true"))  
false_counts_tor_cutoff

Create comparison datatable

false_counts_dt <-
  bind_rows(
    list(no_purging=false_counts_nopurging %>%
           select(c("tp", "fp", "tn", "fn")),
         no_discarding=false_counts_min_cutoff,
         tor_cutoff=
           false_counts_tor_cutoff %>%
           select(c("tp_t", "fp_t", "tn_t", "fn_t")) %>%
           set_names(c("tp", "fp", "tn", "fn")),
         max_frac=false_counts_maxprop),
    .id="approach") %>%
  select(approach, fp,fn, tp, tn) %>%
  mutate(fpr=fp/false_counts_nopurging$n_fantom,
         fnr=fn/false_counts_nopurging$n_real)
false_counts_dt
# Hmisc::latex( false_counts_dt %>% 
#                 mutate(fpr =round(fpr,4),
#                        fnr =round(fnr,4)), 
#               file="",
#               rowname=NULL,
#               booktabs=TRUE, size="small")

Plots

Datatable for plotting

classification_curves <-
  read_counts %>% 
  group_by(qr) %>%
  slice(1L) %>%
  ungroup() %>%
  select( qr, qs, tor, retain, fp_t, FP, fn_t, FN, tp_t, tn_t, TP, TN, FPm, FNm, fpm, fnm, tor_true,o,r) %>%
  mutate(fpr=fp_t/false_counts_nopurging$n_fantom,
         fnr=fn_t/false_counts_nopurging$n_real)
classification_curves

Preformance Plots

p_tradeoff <-  
    ggplot(classification_curves) + 
    geom_point(
      aes(x = FPm,
          y = FNm),
      size=.5)+
    geom_line(
      aes(x = FPm,
          y = FPm,
          colour="1")
    ) +
    geom_line(
      aes(x = FPm,
          y = 2*FPm,
          colour="2"))+
    geom_line(
      aes(x = FPm,
          y = 3*FPm,
          colour="3"))+
    geom_line(
      aes(x = FPm,
          y = 4*FPm,
          colour="4"))+
    geom_line(
      aes(x = FPm,
          y = 5*FPm,
          colour="5"))+
    geom_line(
      aes(x = FPm,
          y = 9*FPm,
          colour="9"))+
    scale_y_log10() +
    theme_bw()  +
      theme(
        legend.title = element_text(face = "bold")) + 
      scale_color_discrete(name = "TORC") +
    labs(x="Marginal Decrease in False Positives (reduce phantom molecs) ",
         y="Marginal Increase in False Negatives (discard real molecs)") 
    

ggsave(file.path(figures_dir, "validation_tradeoff.pdf"), p_tradeoff, width=9, height=6)

p_tradeoff

p6 <-
  ggplot(classification_curves)  + 
    geom_point(
             aes(x = fp_t,
                  y = fn_t,
                 colour="true")) +

  geom_line(
             aes(x = fp_t,
                  y = fn_t,
                 colour="true")) +
    geom_line(
             aes(x = FP,
                  y = FN,
                 colour="predicted"))+
      geom_point(
             aes(x = FP,
                  y = FN,
                 colour="predicted"))+ 

  geom_point(data=false_counts_dt ,
             aes(x = fp,
                  y = fn,
                  shape=approach),
             size=2) +
    labs(x="False Positive Count",
       y="False Negative Count")  + 
    scale_y_sqrt() + 
  scale_x_sqrt() 
ggsave(file.path(figures_dir,"peformance_groundtruth.pdf"), p6, width=9, height=6)
 p6

p7 <- ggplot(false_counts_dt
              %>% filter(approach %in% c("no_discarding", "tor_cutoff", "max_frac")))  + 
  geom_point(
             aes(x = fp ,
                  y = fn,
                  color=approach),
             size=2)  +
    labs(x="False Positive Count",
       y="False Negative Count") 


ggsave(file.path(figures_dir,"peformance_zoom.pdf"), p7, width=9, height=6)
p7

sessionInfo()
R version 3.6.3 (2020-02-29)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.4 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

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

other attached packages:
 [1] cowplot_1.0.0      data.table_1.12.8  tictoc_1.0         furrr_0.1.0        future_1.16.0     
 [6] broom_0.5.5        matrixStats_0.56.0 forcats_0.5.0      stringr_1.4.0      dplyr_0.8.5       
[11] purrr_0.3.3        readr_1.3.1        tidyr_1.0.2        tibble_3.0.0       ggplot2_3.3.0     
[16] tidyverse_1.3.0    rhdf5_2.30.1      

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.4       lubridate_1.7.8  lattice_0.20-40  listenv_0.8.0    assertthat_0.2.1
 [6] rprojroot_1.3-2  packrat_0.5.0    digest_0.6.25    R6_2.4.1         cellranger_1.1.0
[11] backports_1.1.6  reprex_0.3.0     httr_1.4.1       pillar_1.4.3     rlang_0.4.5     
[16] readxl_1.3.1     rstudioapi_0.11  labeling_0.3     munsell_0.5.0    compiler_3.6.3  
[21] modelr_0.1.6     xfun_0.12        pkgconfig_2.0.3  globals_0.12.5   tidyselect_1.0.0
[26] codetools_0.2-16 fansi_0.4.1      crayon_1.3.4     dbplyr_1.4.2     withr_2.1.2     
[31] MASS_7.3-51.5    grid_3.6.3       nlme_3.1-144     jsonlite_1.6.1   gtable_0.3.0    
[36] lifecycle_0.2.0  DBI_1.1.0        magrittr_1.5     scales_1.1.0     cli_2.0.2       
[41] stringi_1.4.6    farver_2.0.3     fs_1.4.1         xml2_1.3.0       ellipsis_0.3.0  
[46] generics_0.0.2   vctrs_0.2.4      Rhdf5lib_1.8.0   tools_3.6.3      glue_1.4.0      
[51] hms_0.5.3        parallel_3.6.3   colorspace_1.4-1 rvest_0.3.5      knitr_1.28      
[56] haven_2.2.0     
LS0tCnRpdGxlOiAiUGhhbnRvbSBQdXJnZSIKc3VidGl0bGU6ICJWYWxpZGF0aW9uIEFuYWx5c2lzOiBQYXJ0IElJIgphdXRob3I6IAotIG5hbWU6IFJpY2sgRmFyb3VuaQogIGFmZmlsaWF0aW9uOgogIC0gJmNydWsgR8Opbm9tZSBRdcOpYmVjIElubm92YXRpb24gQ2VudHJlLCBNY0dpbGwgVW5pdmVyc2l0eSwgTW9udHJlYWwsIENhbmFkYQpkYXRlOiAnYHIgZm9ybWF0KFN5cy5EYXRlKCksICIlWS0lQi0lZCIpYCcKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdG9jOiBubwogICAgdG9jX2Zsb2F0OiAKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCiMgUHJlcGFyZSBhbmFseXNpcyB3b3JrZmxvdwoKIyMjIFNldCBwYXJhbWV0ZXJzCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBycHJvanJvb3Q6OmZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSwKICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoPTE1LAogICAgICAgICAgICAgICAgICAgICBkaWdpdD01LAogICAgICAgICAgICAgICAgICAgICBzY2lwZW49OCkKb3B0aW9ucyhyZWFkci5zaG93X3Byb2dyZXNzID0gRkFMU0UsCiAgICAgICAgZGlnaXRzPTUsIAogICAgICAgIHNjaXBlbj04LAogICAgICAgIGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSArSW5mKQpgYGAKCgojIyMgU2V0IGZpbGVwYXRocyBhbmQgcGFyYW1ldGVycwoKYGBge3J9CnByb2plY3RfZGlyIDwtIHJwcm9qcm9vdDo6ZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpCgppZihpcy5udWxsKHByb2plY3RfZGlyKSl7CiAgcHJvamVjdF9kaXIgPC0gZ2V0d2QoKQogIHdhcm5pbmcoc3ByaW50ZigiTm8gcnN0dWRpbyBwcm9qZWN0IHJvb3QgZmlsZSAgZm91bmQuIAogICAgICAgICAgICAgICAgICBTZXR0aW5nIHByb2plY3QgZGlyZWN0b3J5IHRvIGN1cnJlbnQgd29ya2Zsb3cuUm1kIGZpbGUgbG9jYXRpb246ICVzLiAKICAgICAgICAgICAgICAgICAgT3ZlcnJpZGUgaWYgbmVlZGVkLiIsCiAgICAgICAgICAgICAgICAgIHByb2plY3RfZGlyKSkKIAp9Cm1lc3NhZ2Uoc3ByaW50ZigiUHJvamVjdCBkaXJlY3Rvcnk6ICVzIiwKICAgICAgICAgICAgICAgIHByb2plY3RfZGlyKSkKYGBgCgojIyMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkocmhkZjUpCiNsaWJyYXJ5KERyb3BsZXRVdGlscykgIyBpbnN0YWxsIGJ1dCBub3QgbG9hZApsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShicm9vbSkKbGlicmFyeShmdXJycikKbGlicmFyeSh0aWN0b2MpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShjb3dwbG90KQpwbGFuKG11bHRpcHJvY2VzcykKYGBgCgoKIyMjIExvYWQgZnVuY3Rpb25zCgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KY29kZV9kaXIgPC0gZmlsZS5wYXRoKHByb2plY3RfZGlyLCAiY29kZSIpCnNvdXJjZShmaWxlLnBhdGgoY29kZV9kaXIsICIxX2NyZWF0ZV9qb2luZWRfY291bnRzX3RhYmxlLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjJfY3JlYXRlX2NvdW50c19ieV9vdXRjb21lX3RhYmxlLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjNfZXN0aW1hdGVfc2FtcGxlX2luZGV4X2hvcHBpbmdfcmF0ZS5SIikpCnNvdXJjZShmaWxlLnBhdGgoY29kZV9kaXIsICI0X2NvbXB1dGVfc3VtbWFyeV9zdGF0aXN0aWNzLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjVfcmVhc3NpZ25faG9wcGVkX3JlYWRzLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjZfcHVyZ2VfcGhhbnRvbV9tb2xlY3VsZXMuUiIpKQpzb3VyY2UoZmlsZS5wYXRoKGNvZGVfZGlyLCAiN19jYWxsX2NlbGxzLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjhfc3VtbWFyaXplX3B1cmdlLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjlfcGxvdHRpbmdfZnVuY3Rpb25zLlIiKSkKYGBgCgojIyMgTG9hZCBkYXRhCmBgYHtyfQp2YWxpZGF0aW9uX291dHB1dF9kaXIgPC0gZmlsZS5wYXRoKHByb2plY3RfZGlyLCAiZGF0YSIsICJoaXNlcTQwMDBfdmFsaWRhdGlvbiIpCmZpZ3VyZXNfZGlyIDwtIGZpbGUucGF0aCh2YWxpZGF0aW9uX291dHB1dF9kaXIsICJmaWd1cmVzIikKYGBgCgpgYGB7cn0KZGF0YSA8LSByZWFkX3RzdihmaWxlLnBhdGgodmFsaWRhdGlvbl9vdXRwdXRfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlzZXE0MDAwX2lubmVyX2pvaW5lZF93aXRoX2xhYmVsc19oZzM4X2Vuc2VtYmw5NS50eHQiKSkKZGF0YQpgYGAKCgojIENvbXB1dGUgSW5kZXggaG9wcGluZyByYXRlCgojIyAgRXN0aW1hdGVzIGNvbmRpdGlvbmFsIG9uIGR1cGxpY2F0aW9uIGxldmVsIGFuZCBsYWJlbAoKYGBge3J9CnN1bW1hcnlfY291bnRzIDwtCiAgICBkYXRhICU+JQogIG11dGF0ZShyPWFzLmludGVnZXIocm93U3VtcyguW2MoNiw3KV0pKSkgJT4lCiAgYXJyYW5nZShyKSAlPiUKICBncm91cF9ieShyLCBsYWJlbCkgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMxX3BsfHMyX3BsIikpLCAKICAgICAgICAgICAgICAgbGlzdCh+IHN1bSguKSkpICAlPiUKICBtdXRhdGUoczFfaG9wcGVkPWlmX2Vsc2UobGFiZWwgJWluJSBjKCIwLGYiLCAicixmIiksIHMyX3BsZXhlZCwwKSwKICAgICAgICAgczJfaG9wcGVkPWlmX2Vsc2UobGFiZWwgJWluJSBjKCJmLDAiLCAiZixyIiksIHMxX3BsZXhlZCwwKSwKICAgICAgICAgczFfbm9uaG9wcGVkPWlmX2Vsc2UobGFiZWwgJWluJSBjKCJyLDAiLCAicixmIiksIHMxX3BsZXhlZCwwKSwKICAgICAgICAgczJfbm9uaG9wcGVkPWlmX2Vsc2UobGFiZWwgJWluJSBjKCIwLHIiLCAiZixyIiksIHMyX3BsZXhlZCwwKSkgJT4lCiAgc2VsZWN0KC1zMV9wbGV4ZWQsLSBzMl9wbGV4ZWQpCgpzdW1tYXJ5X2NvdW50cwpgYGAKCiMjIEVzdGltYXRlcyBjb25kaXRpb25hbCBvbiBkdXBsaWNhdGlvbiBsZXZlbAoKYGBge3J9CnN1bW1hcnlfY291bnRzX2NvbmRpdGlvbmFsIDwtCnN1bW1hcnlfY291bnRzICAlPiUKICBncm91cF9ieShyKSAlPiUKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgCiAgICAgICAgICAgICAgIGxpc3QofiBzdW0oLikpKSU+JQogIG11dGF0ZShTSUhSXzEyID0gczFfaG9wcGVkLyhzMV9ob3BwZWQrczFfbm9uaG9wcGVkKSwKICAgICAgICAgU0lIUl8yMSA9IHMyX2hvcHBlZC8oczJfaG9wcGVkK3MyX25vbmhvcHBlZCksCiAgICAgICAgIGZyYWNfczE9IChzMV9ob3BwZWQrczFfbm9uaG9wcGVkKS8gKHMxX2hvcHBlZCtzMV9ub25ob3BwZWQrczJfaG9wcGVkK3MyX25vbmhvcHBlZCkpCnN1bW1hcnlfY291bnRzX2NvbmRpdGlvbmFsCmBgYAoKCgojIyBNYXJnaW5hbCBlc3RpbWF0ZXMKCmBgYHtyfQpzdW1tYXJ5X2NvdW50c19tYXJnaW5hbCA8LSAKICBzdW1tYXJ5X2NvdW50cyAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIAogICAgICAgICAgICAgICBsaXN0KH4gc3VtKC4pKSklPiUKICBtdXRhdGUoU0lIUl8xMiA9IHMxX2hvcHBlZC8oczFfaG9wcGVkK3MxX25vbmhvcHBlZCksCiAgICAgICAgIFNJSFJfMjEgPSBzMl9ob3BwZWQvKHMyX2hvcHBlZCtzMl9ub25ob3BwZWQpLAogICAgICAgICBTSUhSPTEtKHMxX25vbmhvcHBlZCtzMl9ub25ob3BwZWQpLyhzMV9ob3BwZWQrczFfbm9uaG9wcGVkICtzMl9ob3BwZWQrczJfbm9uaG9wcGVkKSwKICAgICAgICAgZnJhY19zMT0gKHMxX2hvcHBlZCtzMV9ub25ob3BwZWQpLyAoczFfaG9wcGVkK3MxX25vbmhvcHBlZCtzMl9ob3BwZWQrczJfbm9uaG9wcGVkKSkKc3VtbWFyeV9jb3VudHNfbWFyZ2luYWwKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CnAxIDwtIAogIGdncGxvdChzdW1tYXJ5X2NvdW50c19jb25kaXRpb25hbCkgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0gciwKICAgICAgICAgICAgICAgICAgICB5ID0gU0lIUl8xMioxMDAsCiAgICAgICAgICAgICAgIGNvbG91cj0iU0lIUl8xMiIpKSsKICAgIGdlb21fbGluZShhZXMoeCA9IHIsCiAgICAgICAgICAgICAgICAgICAgeSA9IFNJSFJfMjEqMTAwLAogICAgICAgICAgICAgICBjb2xvdXI9IlNJSFJfMjEiKSkgKwogICAgICBnZW9tX2xpbmUoYWVzKHggPSByLAogICAgICAgICAgICAgICAgICAgIHkgPSBmcmFjX3MxLAogICAgICAgICAgICAgICBjb2xvdXI9ImZyYWNfczEiKSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdW5saXN0KHN1bW1hcnlfY291bnRzX21hcmdpbmFsWzU6N10pKiBjKDEwMCwgMTAwLCAxMDApLAogICAgICAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIikgKwogICAgeGxpbSgwLDkwKSArIAogIHlsaW0oMCwxKSAKcDEKIApgYGAKCgojIyBNb2xlY3VsZXMKCmBgYHtyfQpzdW1tYXJ5X21vbF9jb3VudHMgPC0KICBkYXRhICU+JQogIG11dGF0ZShyPWFzLmludGVnZXIocm93U3VtcyguW2MoNiw3KV0pKSkgJT4lCiAgYXJyYW5nZShyKSAlPiUgIAogIG11dGF0ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLCAKICAgICAgICAgICAgbGlzdCh+IGFzLmludGVnZXIoLiE9MCkpKSAgJT4lCiAgZ3JvdXBfYnkociwgbGFiZWwpICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zMV9wbHxeczJfcGwiKSksIAogICAgICAgICAgICAgICBsaXN0KH4gc3VtKC4pKSkgICU+JQogICAgbXV0YXRlKHMxX3BoYW50b209aWZfZWxzZShsYWJlbCAlaW4lIGMoIjAsZiIsICJyLGYiKSwgczJfcGxleGVkLDBMKSwKICAgICAgICAgczJfcGhhbnRvbT1pZl9lbHNlKGxhYmVsICVpbiUgYygiZiwwIiwgImYsciIpLCBzMV9wbGV4ZWQsMEwpLAogICAgICAgICBzMV9yZWFsPWlmX2Vsc2UobGFiZWwgJWluJSBjKCJyLDAiLCAicixmIiksIHMxX3BsZXhlZCwwTCksCiAgICAgICAgIHMyX3JlYWw9aWZfZWxzZShsYWJlbCAlaW4lIGMoIjAsciIsICJmLHIiKSwgczJfcGxleGVkLDBMKSkgJT4lCiAgc2VsZWN0KC1zMV9wbGV4ZWQsLSBzMl9wbGV4ZWQpIApzdW1tYXJ5X21vbF9jb3VudHMgCmBgYAoKCiMjIyBOdW1iZXIgb2YgRnVndWUgTW9sZWN1bGVzCgpgYGB7cn0Kc3VtbWFyeV9tb2xfY291bnRzICU+JQogIGZpbHRlcihsYWJlbCAlaW4lIGMoIjAsZiIsICJmLDAiKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKGVuZHNfd2l0aCgiX3BoYW50b20iKSksIHN1bSkKYGBgCgojIyMgTWFyZ2luYWwgU3VtbWFyaWVzCgpgYGB7cn0Kc3VtbWFyeV9tb2xfY291bnRzX21hcmdpbmFsIDwtCiAgc3VtbWFyeV9tb2xfY291bnRzJT4lCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLCAKICAgICAgICAgICAgICAgbGlzdCh+IHN1bSguKSkpICU+JQogIG11dGF0ZShwcG1fMTIgPSBzMV9waGFudG9tLyhzMV9waGFudG9tK3MxX3JlYWwpLCMgcHJvcCBob3BwZWQgcGhhbnRvbSBtb2xlYwogICAgICAgICBwcG1fMjEgPSBzMl9waGFudG9tLyhzMl9waGFudG9tK3MyX3JlYWwpLAogICAgICAgICBwcG1fMSA9IHMyX3BoYW50b20vKHMyX3BoYW50b20rczFfcmVhbCksICMgcHJvcCBwaGFudG9tIG1vbGVjCiAgICAgICAgIHBwbV8yID0gczFfcGhhbnRvbS8oczFfcGhhbnRvbStzMl9yZWFsKSwKICAgICAgICAgcHBtPShzMV9waGFudG9tK3MyX3BoYW50b20pLyhzMV9waGFudG9tK3MxX3JlYWwrczJfcGhhbnRvbStzMl9yZWFsKSwKICAgICAgICAgZnJhY19tb2xfczE9IChzMV9waGFudG9tK3MxX3JlYWwpLyAoczFfcGhhbnRvbStzMV9yZWFsK3MyX3BoYW50b20rczJfcmVhbCkpCnN1bW1hcnlfbW9sX2NvdW50c19tYXJnaW5hbApgYGAKCgpgYGB7cn0Kc3VtbWFyeV9tb2xfY291bnRzX2NvbmRpdGlvbmFsIDwtCiAgc3VtbWFyeV9tb2xfY291bnRzJT4lCiAgZ3JvdXBfYnkocikgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIAogICAgICAgICAgICAgICBsaXN0KH4gc3VtKC4pKSkgJT4lCiAgbXV0YXRlKHBwbV8xMiA9IHMxX3BoYW50b20vKHMxX3BoYW50b20rczFfcmVhbCksCiAgICAgICAgIHBwbV8yMSA9IHMyX3BoYW50b20vKHMyX3BoYW50b20rczJfcmVhbCksCiAgICAgICAgIHBwbT0oczFfcGhhbnRvbStzMl9waGFudG9tKS8oczFfcGhhbnRvbStzMV9yZWFsK3MyX3BoYW50b20rczJfcmVhbCksCiAgICAgICAgIGZyYWNfbW9sX3MxPSAoczFfcGhhbnRvbStzMV9yZWFsKS8gKHMxX3BoYW50b20rczFfcmVhbCtzMl9waGFudG9tK3MyX3JlYWwpKQpzdW1tYXJ5X21vbF9jb3VudHNfY29uZGl0aW9uYWwKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KcDQgPC0gZ2dwbG90KHN1bW1hcnlfbW9sX2NvdW50c19jb25kaXRpb25hbCkgKwogIGdlb21fbGluZShhZXMoeCA9IHIsCiAgICAgICAgICAgICAgICAgIHkgPSBwcG1fMTIsCiAgICAgICAgICAgICBjb2xvdXI9InBwbV8xMiIpKSsKICBnZW9tX2xpbmUoYWVzKHggPSByLAogICAgICAgICAgICAgICAgICB5ID0gIHBwbV8yMSwKICAgICAgICAgICAgIGNvbG91cj0iIHBwbV8yMSIpKSArCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IHVubGlzdChzdW1tYXJ5X21vbF9jb3VudHNfbWFyZ2luYWxbNTo3XSksIGxpbmV0eXBlPSJkYXNoZWQiKSArCiAgeGxpbSgwLDMwMCkgCnA0CiAjZ2dzYXZlKCJwaGFudG9tX21vbGVjdWxlc192YWxpZGF0aW9uLnBkZiIsIHA0LCAgd2lkdGggPTgsIGhlaWdodCA9IDUpCmBgYAoKCgojIyBFeGFtaW5lIGV4dGVudCBvZiBjb250YW1pbmF0aW9uIGluIGNlbGxzCgpgYGB7cn0Kc3VtbWFyeV9tb2xfY291bnRzX2NlbGw8LQogIGRhdGEgJT4lCiAgZmlsdGVyKGxhYmVsIT0iTkEiKSAlPiUKICBtdXRhdGVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgCiAgICAgICAgICAgIGxpc3QofiBhcy5pbnRlZ2VyKC4hPTApKSkgICU+JQogIGdyb3VwX2J5KGNlbGwsIGxhYmVsKSAlPiUKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJeczFfcGx8XnMyX3BsIikpLCAKICAgICAgICAgICAgICAgbGlzdCh+IHN1bSguKSkpICAlPiUKICAgIG11dGF0ZShzMV9waGFudG9tPWlmX2Vsc2UobGFiZWwgJWluJSBjKCJmLDAiLCAiZixyIiksIHMxX3BsZXhlZCwwTCksCiAgICAgICAgIHMyX3BoYW50b209aWZfZWxzZShsYWJlbCAlaW4lIGMoIjAsZiIsICJyLGYiKSwgczJfcGxleGVkLDBMKSwKICAgICAgICAgczFfcmVhbD1pZl9lbHNlKGxhYmVsICVpbiUgYygiciwwIiwgInIsZiIpLCBzMV9wbGV4ZWQsMEwpLAogICAgICAgICBzMl9yZWFsPWlmX2Vsc2UobGFiZWwgJWluJSBjKCIwLHIiLCAiZixyIiksIHMyX3BsZXhlZCwwTCkpICU+JQogIHNlbGVjdCgtczFfcGxleGVkLC0gczJfcGxleGVkKSAlPiUKICBncm91cF9ieShjZWxsKSAlPiUKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgCiAgICAgICAgICAgICAgIGxpc3QofiBzdW0oLikpKSAlPiUgCiAgICBtdXRhdGVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgCiAgICAgICAgICAgIGxpc3Qobm9uZW1wdHk9IH4gYXMuaW50ZWdlciguIT0wKSkpICAlPiUKICB1bml0ZShsYWJlbCxtYXRjaGVzKCJub25lbXB0eSIpLCBzZXA9IiwiKSAlPiUKICBtdXRhdGUoY2VsbF9zdGF0dXM9IAogICAgICAgICAgIGNhc2Vfd2hlbihsYWJlbCAlaW4lIGMoIjAsMCwwLDEiLCIwLDAsMSwwIikgfiAicmVhbCIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsICVpbiUgYygiMCwwLDEsMSIpIH4gInJlYWwiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbCAlaW4lIGMoIjEsMCwwLDAiLCIwLDEsMCwwIiwgIjEsMSwwLDAiKSB+ICAicGhhbnRvbSIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsICVpbiUgYygiMSwwLDAsMSIsIjAsMSwxLDAiKSB+ICAicGhhbnRvbSIsCiAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAgImNvbnRhbWluYXRlZCIpKSAgJT4lCiAgbXV0YXRlKHMxX3RvdGFsID0oczFfcGhhbnRvbStzMV9yZWFsKSwKICAgICAgICAgczJfdG90YWwgPShzMl9waGFudG9tK3MyX3JlYWwpLAogICAgICAgICBzMV9wcG0gPSBzMV9waGFudG9tLyhzMV90b3RhbCksCiAgICAgICAgIHMyX3BwbSA9IHMyX3BoYW50b20vKHMyX3RvdGFsKSkKICAKc3VtbWFyeV9tb2xfY291bnRzX2NlbGwKYGBgCmBgYHtyfQpjZWxsX3N0YXR1c190YWxseSA8LQogIHN1bW1hcnlfbW9sX2NvdW50c19jZWxsICU+JQogIGdyb3VwX2J5KGNlbGxfc3RhdHVzLGxhYmVsKSAlPiUKICB0YWxseShzb3J0PVRSVUUpCmNlbGxfc3RhdHVzX3RhbGx5CmBgYAoKYGBge3J9Cm5fYWZmZWN0ZWRfY2VsbHMgPC0gCiAgY2VsbF9zdGF0dXNfdGFsbHkgJT4lCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcihjZWxsX3N0YXR1cyE9InJlYWwiKSAlPiUgCiAgc3VtbWFyaXNlKG49c3VtKG4pKSAlPiUgCiAgcHVsbChuKQpuX2FmZmVjdGVkX2NlbGxzIDwtIG5fYWZmZWN0ZWRfY2VsbHMgKyAxNTAyICs2Cm5fYWZmZWN0ZWRfY2VsbHMKYGBgCgoKYGBge3J9Cm5fdG90YWxfY2VsbHMgPC0KICBzdW1tYXJ5X21vbF9jb3VudHNfY2VsbCAlPiUKICBtdXRhdGVfYXQodmFycyhtYXRjaGVzKCJfdG90YWwiKSksIAogICAgICAgICAgICBsaXN0KH4gYXMuaW50ZWdlciguIT0wKSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBzdW1tYXJpc2VfYXQodmFycyhtYXRjaGVzKCJfdG90YWwiKSksIGxpc3QofnN1bSguKSkpICU+JQogIG11dGF0ZShjZWxsc190b3RhbD0gczFfdG90YWwrczJfdG90YWwpJT4lCiAgcHVsbChjZWxsc190b3RhbCkKbl90b3RhbF9jZWxscyAKYGBgCgpQcm9wb3J0aW9uIG9mIGFmZmVjdGVkIGNlbGxzCgpgYGB7cn0KcF9hZmZlY3RlZF9jZWxscyA8LSAgbl9hZmZlY3RlZF9jZWxscy9uX3RvdGFsX2NlbGxzCnBfYWZmZWN0ZWRfY2VsbHMKYGBgCgpGb3IgZWFjaCBjZWxsLWJhcmNvZGUsIHBsb3QgdGhlIG51bWJlciBvZiBwaGFudG9tIG1vbGVjdWxlcyBhZ2FpbnN0IHRoZSBudW1iZXIgb2YgdG90YWwgbW9sZWN1bGVzIGFzc29jaWF0ZWQgd2l0aCBpdC4KClNhbXBsZSAxIHBsb3QKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpwMiA8LSBnZ3Bsb3Qoc3VtbWFyeV9tb2xfY291bnRzX2NlbGwgJT4lCiAgICAgICAgICAgICAgIGZpbHRlcihjZWxsX3N0YXR1cyE9InJlYWwiICYgczFfcGhhbnRvbSA+MCApKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHMxX3RvdGFsLAogICAgICAgICAgICAgICAgICB5ID0gczFfcGhhbnRvbSwKICAgICAgICAgICAgICAgICBjb2xvdXI9Y2VsbF9zdGF0dXMpKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkKCnAyCiAjZ2dzYXZlKCJwaGFudG9tX21vbGVjdWxlc192YWxpZGF0aW9uLnBkZiIsIHAyLCAgd2lkdGggPTgsIGhlaWdodCA9IDUpCmBgYAoKU2FtcGxlIDIgcGxvdAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpwMyA8LSBnZ3Bsb3Qoc3VtbWFyeV9tb2xfY291bnRzX2NlbGwgJT4lCiAgICAgICAgICAgICAgIGZpbHRlcihjZWxsX3N0YXR1cyE9InJlYWwiICYgczJfcGhhbnRvbSA+MCApKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHMyX3RvdGFsLAogICAgICAgICAgICAgICAgICB5ID0gczJfcGhhbnRvbSwKICAgICAgICAgICAgICAgICBjb2xvdXI9Y2VsbF9zdGF0dXMpLAogICAgICAgICAgICAgc2l6ZT0wLjcsCiAgICAgICAgICAgICBhbHBoYT0wLjYpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgICBsYWJzKHg9IlRvdGFsIE51bWJlciBvZiBNb2xlY3VsZXMiLAogICAgICAgeT0iTnVtYmVyIG9mIFBoYW50b20gTW9sZWN1bGVzIikgIAoKcDMKCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsInBoYW50b21fY2VsbHNfdmFsaWRhdGlvbl9zMi5wZGYiKSwgcDMsICB3aWR0aCA9OCwgaGVpZ2h0ID0gNSkKYGBgCgojIFJ1biB3b3JrZmxvdyBvbiBtdWx0aXBsZXhlZCBkYXRhCgpgYGB7cn0KcmVhZF9jb3VudHMgPC0gCiAgZGF0YSAlPiUgCiAgZmlsdGVyKGxhYmVsIT0iTkEiKSU+JQogIHNlbGVjdCgtZW5kc193aXRoKCJub25wbGV4ZWQiKSkgJT4lCiAgc2V0X25hbWVzKGMoImNlbGwiLCAiZ2VuZSIsICJ1bWkiLCAiczEiLCAiczIiLCAib3V0Y29tZSIsICJsYWJlbCIpKQpyZWFkX2NvdW50cwpgYGAKCgpgYGB7cn0KUyA8LSAyCnNhbXBsZV9uYW1lcyA8LSBjb2xuYW1lcyhyZWFkX2NvdW50cylbNDooUyszKV0Kc2FtcGxlX25hbWVzCmBgYAoKCgpgYGB7cn0KdGljKCJTdGVwIDI6IGNyZWF0aW5nIG91dGNvbWUgY291bnRzIGRhdGF0YWJsZSB3aXRoIGdyb3VwaW5nIHZhcnMiKQoKb3V0Y29tZV9jb3VudHMgPC0gY3JlYXRlX291dGNvbWVfY291bnRzKHJlYWRfY291bnRzJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtbGFiZWwpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9uYW1lcywgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX2ZyYWM9MC44KQp0b2MoKQoKCm91dGNvbWVfY291bnRzCmBgYAoKYGBge3J9CnRpYygiU3RlcCAzOiBjcmVhdGluZyBhIGNoaW1lcmEgY291bnRzIGRhdGF0YWJsZSBhbmQgZXN0aW1hdGluZyBob3BwaW5nIHJhdGUiKQogIGZpdF9vdXQgPC0KICAgIGVzdGltYXRlX2hvcHBpbmdfcmF0ZSgKICAgICAgb3V0Y29tZV9jb3VudHMsCiAgICAgIFMKICAgICkKICB0b2MoKQpmaXRfb3V0IApgYGAKCgoKYGBge3J9CiAgIyBjb21wdXRlX21vbGVjdWxhcl9jb21wbGV4aXR5X3Byb2ZpbGUKICB0aWMoIlN0ZXAgNDogY29tcHV0ZSBtb2xlY3VsYXIgY29tcGxleGl0eSBwcm9maWxlIGFuZCBvdGhlciBzdW1tYXJ5IHN0YXRpc3RpY3MiKQogIHN1bW1hcnlfc3RhdHMgPC0KICAgIGNvbXB1dGVfc3VtbWFyeV9zdGF0cygKICAgICAgb3V0Y29tZV9jb3VudHMsCiAgICAgIGZpdF9vdXQkZ2xtX2VzdGltYXRlcyRwaGF0LAogICAgICBzYW1wbGVfbmFtZXMKICAgICkKICB0b2MoKQpzdW1tYXJ5X3N0YXRzCmBgYAoKU2V0IHRoZSB0cmFkZS1vZmYgcmF0aW8gY29zdCBjdXRvZmYgKCp0b3JjKikuIFRoZSBwYXJhbWV0ZXIgKnRvcmMqIHJlcHJlc2VudHMgdGhlIG51bWJlciBvZiByZWFsIG1vbGVjdWxlcyBvbmUgaXMgd2lsbGluZyB0byBpbmNvcnJlY3RseSBkaXNjYXJkIGluIG9yZGVyIHRvIGNvcnJlY3RseSBwdXJnZSBvbmUgcGhhbnRvbSBtb2xlY3VsZS4gU2luY2UgZGlzY2FyZGluZyBhIGxhcmdlIHByb3BvcnRpb24gb2YgdGhlIGRhdGEgaXMgdW5kZXNpcmFibGUsIHJlYXNvbmFibGUgdmFsdWVzIG9mICp0b3JjKiBhcmUgZXhwZWN0ZWQgdG8gYmUgd2l0aGluIHRoZSByYW5nZSBvZiAxLTUuCgpgYGB7cn0KdG9yYyA8LSAzIApgYGAKCgpgYGB7cn0KdGljKCJTdGVwIDU6IHJlYXNzaWduIHJlYWQgY291bnRzLCBkZXRlcm1pbmUgY3V0b2ZmLCBhbmQgbWFyayByZXRhaW5lZCBvYnNlcnZhdGlvbnMiKQoKICBvdXRjb21lX2NvdW50cyA8LQogICAgcmVhc3NpZ25fcmVhZHNfYW5kX21hcmtfcmV0YWluZWRfb2JzZXJ2YXRpb25zKAogICAgICBvdXRjb21lX2NvdW50cywKICAgICAgc3VtbWFyeV9zdGF0cywKICAgICAgc2FtcGxlX25hbWVzLAogICAgICBmaXRfb3V0LAogICAgICB0b3JjCiAgICApCiAgIyBnZXQgdGhlIHRyYWRvZmYgcmF0aW8gY3V0b2ZmCiAgc3VtbWFyeV9zdGF0cyA8LSBnZXRfdGhyZXNob2xkKG91dGNvbWVfY291bnRzLCBzdW1tYXJ5X3N0YXRzKQoKICB0b2MoKQpgYGAKCmBgYHtyfQpzdW1tYXJ5X2NvdW50c19tYXJnaW5hbApgYGAKCgpgYGB7cn0KdGljKCJTdGVwIDY6IFB1cmdlIGFuZCBzYXZlIHJlYWQgY291bnRzIGRhdGF0YWJsZSB0byBkaXNrIikKCnJlYWRfY291bnRzIDwtCiAgbGVmdF9qb2luKHJlYWRfY291bnRzICU+JQogICAgc2VsZWN0KG91dGNvbWUsIGNlbGwsIHVtaSwgZ2VuZSwgc2FtcGxlX25hbWVzLCBsYWJlbCksCiAgb3V0Y29tZV9jb3VudHMsCiAgYnkgPSBjKCJvdXRjb21lIikKICApICU+JQogIHNlbGVjdCgtb3V0Y29tZSkKCnRvYygpCgpgYGAKCiMjIyBDb21wYXJlIHRoZSBTSUhSIGVzdGltYXRlcyB3aXRoIGdyb3VuZCB0cnV0aCBlc3RpbWF0ZXMKCmBgYHtyLCBmaWcud2lkdGg9MTB9CnA1IDwtIAogIGdncGxvdChzdW1tYXJ5X2NvdW50c19jb25kaXRpb25hbCkgKwogIGdlb21fbGluZShhZXMoeCA9IHIsCiAgICAgICAgICAgICAgICAgIHkgPSBTSUhSXzEyLAogICAgICAgICAgICAgY29sb3VyPSIxMiIpKSsKICBnZW9tX2xpbmUoYWVzKHggPSByLAogICAgICAgICAgICAgICAgICB5ID0gU0lIUl8yMSwKICAgICAgICAgICAgIGNvbG91cj0iMjEiKSkgICsKZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9ICBzdW1tYXJ5X2NvdW50c19tYXJnaW5hbCRTSUhSLCAKICAgICAgICAgICAgICAgY29sb3VyPSJ0cnVlIG1lYW4iKSwgCiAgICAgICAgICAgbGluZXR5cGU9InNvbGlkIiwKICAgICAgICAgICBzaXplPS41KSAgICsKZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9ICBmaXRfb3V0JGdsbV9lc3RpbWF0ZXMkU0lIUiwgCiAgICAgICAgICAgICAgIGNvbG91cj0iZXN0aW1hdGUiKSwKICAgICAgICAgICBsaW5ldHlwZT0ic29saWQiLAogICAgICAgICAgIHNpemU9LjEpICAgKwogICAgZ2VvbV9saW5lcmFuZ2UoZGF0YT1zdW1tYXJ5X2NvdW50c19jb25kaXRpb25hbCwgCiAgICAgICAgICAgICAgICAgIGFlcyh4PXIsCiAgICAgICAgICAgICAgICAgICAgICB5bWF4PTEtZml0X291dCRnbG1fZXN0aW1hdGVzJHBoYXRfbG93LAogICAgICAgICAgICAgICAgICAgICAgeW1pbj0xLWZpdF9vdXQkZ2xtX2VzdGltYXRlcyRwaGF0X2hpZ2gsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvdXI9ImVzdGltYXRlIiksIAogICAgICAgICAgICAgICAgICBzaXplPS41KSsgCiAgeGxpbSgxLDIxMCkgKwogIHlsaW0oMC4wMDIsMC4wMDUpCiNnZ3NhdmUoImluZGV4X2hvcHBpbmdfcmF0ZV8yMDAucGRmIiwgcDUsIHdpZHRoPTksIGhlaWdodD02KQpwNQpgYGAKCgoKIyBEZXRlcm1pbmUgdGhlIG51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMgYW5kIGZhbHNlIG5lZ2F0aXZlcwoKYGBge3J9CnJlYWRfY291bnRzIDwtCiAgICByZWFkX2NvdW50cyAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZSgtcXIpICU+JQogIG11dGF0ZSh0PSBjYXNlX3doZW4oCiAgICAgIGxhYmVsICVpbiUgYygiZixyIiwiMCxyIikgfiAyLAogICAgICBsYWJlbCAlaW4lIGMoInIsZiIsInIsMCIpIH4gMSAgICApLAogICAgZj0gY2FzZV93aGVuKAogICAgICBsYWJlbCAlaW4lIGMoImYsciIsImYsMCIpIH4gMSwKICAgICAgbGFiZWwgJWluJSBjKCJyLGYiLCIwLGYiKSB+IDIgICAgKSkgJT4lCiAgbXV0YXRlKHRwPSBpZl9lbHNlKCB0ID09IHMsIDFMLCAwTCwgbWlzc2luZyA9MEwpLAogICAgICAgICBmcD0gaWZfZWxzZSggZiA9PSBzLCAxTCwgMEwsIG1pc3NpbmcgPTBMKSwKICAgICAgICAgdG49IGlmX2Vsc2UoIGYgIT0gcywgMUwsIDBMLCBtaXNzaW5nID0wTCksCiAgICAgICAgIGZuPSBpZl9lbHNlKCB0ICE9IHMsIDFMLCAwTCwgbWlzc2luZyA9MEwpLAogICAgICAgICB0cDA9IGlmX2Vsc2UoIHQgPT0gMCwgMUwsIDBMLCBtaXNzaW5nID0wTCksICMwIGlmICBwcmVkaWN0IGFsbCBtb2xlY3VsZXMgdG8gYmUgcGhhbnRvbQogICAgICAgICBmbjA9IGlmX2Vsc2UoIHQgIT0gMCwgMUwsIDBMLCBtaXNzaW5nID0wTCksCiAgICAgICAgIHRuMD0gaWZfZWxzZSggZiAhPSAwLCAxTCwgMEwsIG1pc3NpbmcgPTBMKSwKICAgICAgICAgZnAwPSBpZl9lbHNlKCBmID09IDAsIDFMLCAwTCwgbWlzc2luZyA9MEwpLAogICAgICAgICB0cF9tYXg9IGlmX2Vsc2UoIHQgPT0gc19tYXhwcm9wLCAxTCwgMEwsIG1pc3NpbmcgPTBMKSwKICAgICAgICAgZnBfbWF4PSBpZl9lbHNlKCBmID09IHNfbWF4cHJvcCwgMUwsIDBMLCBtaXNzaW5nID0wTCksCiAgICAgICAgIHRuX21heD0gaWZfZWxzZSggZiAhPSBzX21heHByb3AsIDFMLCAwTCwgbWlzc2luZyA9MEwpLAogICAgICAgICBmbl9tYXg9IGlmX2Vsc2UoIHQgIT0gc19tYXhwcm9wLCAxTCwgMEwsIG1pc3NpbmcgPTBMKSkgCmBgYAoKCiMjIyBUaGUgbWF4aW11bSByZWFkIGZyYWN0aW9uIG1ldGhvZAoKCmBgYHtyfQpmYWxzZV9jb3VudHNfbWF4cHJvcCA8LQogIHJlYWRfY291bnRzICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKGMoInRwX21heCIsICJmcF9tYXgiLCAidG5fbWF4IiwgImZuX21heCIpKSwKICAgICAgICAgICAgbGlzdCggfiBzdW0oLikpKSAlPiUKICBzZXRfbmFtZXMoYygidHAiLCAiZnAiLCAidG4iLCAiZm4iKSkKZmFsc2VfY291bnRzX21heHByb3AKYGBgCgojIyMgVGhlIHRvciBtZXRob2QKCgpgYGB7cn0KZmFsc2VfY291bnRzX21pbl9jdXRvZmYgPC0KICByZWFkX2NvdW50cyAlPiUKICBzdW1tYXJpemVfYXQodmFycyhjKCJ0cCIsICJmcCIsICJ0biIsICJmbiIpKSwKICAgICAgICAgICAgbGlzdCggfiBzdW0oLikpKSAKZmFsc2VfY291bnRzX21pbl9jdXRvZmYKYGBgCgojIyMgTm8gcHVyZ2luZwoKYGBge3J9CmZhbHNlX2NvdW50c19ub3B1cmdpbmcgPC0gCiAgcmVhZF9jb3VudHMlPiUKICBzdW1tYXJpemUobl9jdWdzID0gbigpLAogICAgICAgICAgICBuX3JlYWw9IHN1bSh0PjAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG5fZmFudG9tID0gc3VtKGY+MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbl9tb2w9bl9yZWFsK25fZmFudG9tLAogICAgICAgICAgICBnID1uX2N1Z3MtIG5fcmVhbCwKICAgICAgICAgICAgdSA9IG5fbW9sLW5fY3VncywKICAgICAgICAgICAgdHA9bl9jdWdzLWcsCiAgICAgICAgICAgIGZwPXUrZywKICAgICAgICAgICAgdG49MCwKICAgICAgICAgICAgZm49MCkKCmZhbHNlX2NvdW50c19ub3B1cmdpbmcKYGBgCgoKIyMjICBUT1IgY3V0b2ZmCgpgYGB7cn0KcmVhZF9jb3VudHMgPC0KICByZWFkX2NvdW50cyAlPiUKICBtdXRhdGVfYXQodmFycyhjKCJ0cCIsICJmcCIsICJ0biIsICJmbiIsInRwMCIsICJmcDAiLCAidG4wIiwgImZuMCIpKSwKICAgICAgICAgICAgbGlzdChjdW09IH4gY3Vtc3VtKC4pKSkgICU+JQogIG11dGF0ZV9hdCh2YXJzKGMoInRwX2N1bSIsICJmcF9jdW0iLCAidG5fY3VtIiwgImZuX2N1bSIpKSwgCiAgICAgICAgICAgIGxpc3QoIH4gKGxhc3QoLiktbGFnKC4sIGRlZmF1bHQgPTApKSkpICU+JQogIG11dGF0ZSh0cF90PXRwX2N1bSArIHRwMF9jdW0sCiAgICAgICAgIGZwX3Q9ZnBfY3VtICsgZnAwX2N1bSwKICAgICAgICAgdG5fdD10bl9jdW0gKyB0bjBfY3VtLAogICAgICAgICBmbl90PWZuX2N1bSArIGZuMF9jdW0sCiAgICAgICAgIGZwbT0gZmlyc3QoZnBfdCktIGZwX3QsIAogICAgICAgICBmbm09IGZuX3QtZmlyc3QoZm5fdCksCiAgICAgICAgIHRvcl90cnVlPSBmbm0vZnBtKQpgYGAKCgoKYGBge3J9CmZhbHNlX2NvdW50c190b3JfY3V0b2ZmIDwtCiAgcmVhZF9jb3VudHMlPiUKICBmaWx0ZXIocmV0YWluKSAlPiUKICBzbGljZSgxKSU+JQogIHNlbGVjdChjKCJzMSIsICJzMiIsICJxciIsICJ0b3IiLCAidHBfdCIsICJmcF90IiwgInRuX3QiLCAiZm5fdCIsICJmcG0iLCAiZm5tIiwgInRvcl90cnVlIikpICAKZmFsc2VfY291bnRzX3Rvcl9jdXRvZmYKYGBgCiMjIENyZWF0ZSBjb21wYXJpc29uIGRhdGF0YWJsZQoKYGBge3J9CmZhbHNlX2NvdW50c19kdCA8LQogIGJpbmRfcm93cygKICAgIGxpc3Qobm9fcHVyZ2luZz1mYWxzZV9jb3VudHNfbm9wdXJnaW5nICU+JQogICAgICAgICAgIHNlbGVjdChjKCJ0cCIsICJmcCIsICJ0biIsICJmbiIpKSwKICAgICAgICAgbm9fZGlzY2FyZGluZz1mYWxzZV9jb3VudHNfbWluX2N1dG9mZiwKICAgICAgICAgdG9yX2N1dG9mZj0KICAgICAgICAgICBmYWxzZV9jb3VudHNfdG9yX2N1dG9mZiAlPiUKICAgICAgICAgICBzZWxlY3QoYygidHBfdCIsICJmcF90IiwgInRuX3QiLCAiZm5fdCIpKSAlPiUKICAgICAgICAgICBzZXRfbmFtZXMoYygidHAiLCAiZnAiLCAidG4iLCAiZm4iKSksCiAgICAgICAgIG1heF9mcmFjPWZhbHNlX2NvdW50c19tYXhwcm9wKSwKICAgIC5pZD0iYXBwcm9hY2giKSAlPiUKICBzZWxlY3QoYXBwcm9hY2gsIGZwLGZuLCB0cCwgdG4pICU+JQogIG11dGF0ZShmcHI9ZnAvZmFsc2VfY291bnRzX25vcHVyZ2luZyRuX2ZhbnRvbSwKICAgICAgICAgZm5yPWZuL2ZhbHNlX2NvdW50c19ub3B1cmdpbmckbl9yZWFsKQpmYWxzZV9jb3VudHNfZHQKYGBgCgpgYGB7cn0KIyBIbWlzYzo6bGF0ZXgoIGZhbHNlX2NvdW50c19kdCAlPiUgCiMgICAgICAgICAgICAgICAgIG11dGF0ZShmcHIgPXJvdW5kKGZwciw0KSwKIyAgICAgICAgICAgICAgICAgICAgICAgIGZuciA9cm91bmQoZm5yLDQpKSwgCiMgICAgICAgICAgICAgICBmaWxlPSIiLAojICAgICAgICAgICAgICAgcm93bmFtZT1OVUxMLAojICAgICAgICAgICAgICAgYm9va3RhYnM9VFJVRSwgc2l6ZT0ic21hbGwiKQpgYGAKCiMjIFBsb3RzCgojIyMgRGF0YXRhYmxlIGZvciBwbG90dGluZwoKCmBgYHtyfQpjbGFzc2lmaWNhdGlvbl9jdXJ2ZXMgPC0KICByZWFkX2NvdW50cyAlPiUgCiAgZ3JvdXBfYnkocXIpICU+JQogIHNsaWNlKDFMKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KCBxciwgcXMsIHRvciwgcmV0YWluLCBmcF90LCBGUCwgZm5fdCwgRk4sIHRwX3QsIHRuX3QsIFRQLCBUTiwgRlBtLCBGTm0sIGZwbSwgZm5tLCB0b3JfdHJ1ZSxvLHIpICU+JQogIG11dGF0ZShmcHI9ZnBfdC9mYWxzZV9jb3VudHNfbm9wdXJnaW5nJG5fZmFudG9tLAogICAgICAgICBmbnI9Zm5fdC9mYWxzZV9jb3VudHNfbm9wdXJnaW5nJG5fcmVhbCkKY2xhc3NpZmljYXRpb25fY3VydmVzCmBgYAoKIyMjIFByZWZvcm1hbmNlIFBsb3RzCgoKCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwX3RyYWRlb2ZmIDwtICAKICAgIGdncGxvdChjbGFzc2lmaWNhdGlvbl9jdXJ2ZXMpICsgCiAgICBnZW9tX3BvaW50KAogICAgICBhZXMoeCA9IEZQbSwKICAgICAgICAgIHkgPSBGTm0pLAogICAgICBzaXplPS41KSsKICAgIGdlb21fbGluZSgKICAgICAgYWVzKHggPSBGUG0sCiAgICAgICAgICB5ID0gRlBtLAogICAgICAgICAgY29sb3VyPSIxIikKICAgICkgKwogICAgZ2VvbV9saW5lKAogICAgICBhZXMoeCA9IEZQbSwKICAgICAgICAgIHkgPSAyKkZQbSwKICAgICAgICAgIGNvbG91cj0iMiIpKSsKICAgIGdlb21fbGluZSgKICAgICAgYWVzKHggPSBGUG0sCiAgICAgICAgICB5ID0gMypGUG0sCiAgICAgICAgICBjb2xvdXI9IjMiKSkrCiAgICBnZW9tX2xpbmUoCiAgICAgIGFlcyh4ID0gRlBtLAogICAgICAgICAgeSA9IDQqRlBtLAogICAgICAgICAgY29sb3VyPSI0IikpKwogICAgZ2VvbV9saW5lKAogICAgICBhZXMoeCA9IEZQbSwKICAgICAgICAgIHkgPSA1KkZQbSwKICAgICAgICAgIGNvbG91cj0iNSIpKSsKICAgIGdlb21fbGluZSgKICAgICAgYWVzKHggPSBGUG0sCiAgICAgICAgICB5ID0gOSpGUG0sCiAgICAgICAgICBjb2xvdXI9IjkiKSkrCiAgICBzY2FsZV95X2xvZzEwKCkgKwogICAgdGhlbWVfYncoKSAgKwogICAgICB0aGVtZSgKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpICsgCiAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiVE9SQyIpICsKICAgIGxhYnMoeD0iTWFyZ2luYWwgRGVjcmVhc2UgaW4gRmFsc2UgUG9zaXRpdmVzIChyZWR1Y2UgcGhhbnRvbSBtb2xlY3MpICIsCiAgICAgICAgIHk9Ik1hcmdpbmFsIEluY3JlYXNlIGluIEZhbHNlIE5lZ2F0aXZlcyAoZGlzY2FyZCByZWFsIG1vbGVjcykiKSAKICAgIAoKZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwgInZhbGlkYXRpb25fdHJhZGVvZmYucGRmIiksIHBfdHJhZGVvZmYsIHdpZHRoPTksIGhlaWdodD02KQoKcF90cmFkZW9mZgpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9Nn0KcDYgPC0KICBnZ3Bsb3QoY2xhc3NpZmljYXRpb25fY3VydmVzKSAgKyAKICAgIGdlb21fcG9pbnQoCiAgICAgICAgICAgICBhZXMoeCA9IGZwX3QsCiAgICAgICAgICAgICAgICAgIHkgPSBmbl90LAogICAgICAgICAgICAgICAgIGNvbG91cj0idHJ1ZSIpKSArCgogIGdlb21fbGluZSgKICAgICAgICAgICAgIGFlcyh4ID0gZnBfdCwKICAgICAgICAgICAgICAgICAgeSA9IGZuX3QsCiAgICAgICAgICAgICAgICAgY29sb3VyPSJ0cnVlIikpICsKICAgIGdlb21fbGluZSgKICAgICAgICAgICAgIGFlcyh4ID0gRlAsCiAgICAgICAgICAgICAgICAgIHkgPSBGTiwKICAgICAgICAgICAgICAgICBjb2xvdXI9InByZWRpY3RlZCIpKSsKICAgICAgZ2VvbV9wb2ludCgKICAgICAgICAgICAgIGFlcyh4ID0gRlAsCiAgICAgICAgICAgICAgICAgIHkgPSBGTiwKICAgICAgICAgICAgICAgICBjb2xvdXI9InByZWRpY3RlZCIpKSsgCgogIGdlb21fcG9pbnQoZGF0YT1mYWxzZV9jb3VudHNfZHQgLAogICAgICAgICAgICAgYWVzKHggPSBmcCwKICAgICAgICAgICAgICAgICAgeSA9IGZuLAogICAgICAgICAgICAgICAgICBzaGFwZT1hcHByb2FjaCksCiAgICAgICAgICAgICBzaXplPTIpICsKICAgIGxhYnMoeD0iRmFsc2UgUG9zaXRpdmUgQ291bnQiLAogICAgICAgeT0iRmFsc2UgTmVnYXRpdmUgQ291bnQiKSAgKyAKICAgIHNjYWxlX3lfc3FydCgpICsgCiAgc2NhbGVfeF9zcXJ0KCkgCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsInBlZm9ybWFuY2VfZ3JvdW5kdHJ1dGgucGRmIiksIHA2LCB3aWR0aD05LCBoZWlnaHQ9NikKIHA2CmBgYAoKCgpgYGB7ciBmaWcuaGVpZ2h0PTh9CnA3IDwtIGdncGxvdChmYWxzZV9jb3VudHNfZHQKICAgICAgICAgICAgICAlPiUgZmlsdGVyKGFwcHJvYWNoICVpbiUgYygibm9fZGlzY2FyZGluZyIsICJ0b3JfY3V0b2ZmIiwgIm1heF9mcmFjIikpKSAgKyAKICBnZW9tX3BvaW50KAogICAgICAgICAgICAgYWVzKHggPSBmcCAsCiAgICAgICAgICAgICAgICAgIHkgPSBmbiwKICAgICAgICAgICAgICAgICAgY29sb3I9YXBwcm9hY2gpLAogICAgICAgICAgICAgc2l6ZT0yKSAgKwogICAgbGFicyh4PSJGYWxzZSBQb3NpdGl2ZSBDb3VudCIsCiAgICAgICB5PSJGYWxzZSBOZWdhdGl2ZSBDb3VudCIpIAoKCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsInBlZm9ybWFuY2Vfem9vbS5wZGYiKSwgcDcsIHdpZHRoPTksIGhlaWdodD02KQpwNwpgYGAKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=