1 Read, Clean, Recode, Merge

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Read, Clean, Recode, Unite
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

## Read files
folder_teatru <- "E:/Cinetic idei noi/A13/Date grup teatru"
folder_psiho <- "E:/Cinetic idei noi/A13/Date grup psiho"
file_teatru <- "A13 Tabel date COMPLET28ian.xlsx"
file_psiho <- "A13P Tabel centralizat.xlsx"

setwd(folder_teatru)
Data_teatru <- rio::import(file.path(folder_teatru, file_teatru),
                           skip = 1)
setwd(folder_psiho)
Data_psiho <- rio::import(file.path(folder_psiho, file_psiho),
                           skip = 1)


## Tidy up data
# Function coalesce rows: colapse when NA, unite with "_" when not NA
coalesce2 <- function(...) {
  Reduce(function(x, y) {
    i <- which(is.na(x))
    j <- which(!is.na(x) & !is.na(y))
    x[i] <- y[i]
    x[j] <- paste(x[j], y[j], sep = "_")
    x},
    list(...))
}

colnames(Data_teatru) <- coalesce2(Data_teatru[2,], Data_teatru[3,])
Data_teatru <- Data_teatru[-c(1:3),]

colnames(Data_psiho) <- coalesce2(Data_psiho[2,], Data_psiho[3,])
Data_psiho <- Data_psiho[-c(1:3),]


## Check if both data sets have exact same column names
all.equal(colnames(Data_teatru), colnames(Data_psiho))           # not the same
diff_colname <- setdiff(colnames(Data_teatru), colnames(Data_psiho))
ind <- which(colnames(Data_teatru) ==  diff_colname)

colnames(Data_psiho)[ind] <- diff_colname                       # replace with same colname from Data_teatru
all.equal(colnames(Data_teatru), colnames(Data_psiho))           # now they are the same
## Solve duplicate names due to excel double header
# Function to paste a string before column name if it doesnt already start with that string
paste_tocolnames <- function(vec_colnames, string_paste){
  ind <- grep(pattern = string_paste, vec_colnames)                   # ignore column that already has string patterm
  vec_colnames[-ind] <- paste0(string_paste, vec_colnames[-ind])      # paste pattern to the rest of them
  return(vec_colnames)
}

colnames(Data_teatru)[6:21] <- paste_tocolnames(colnames(Data_teatru)[6:21], "APS pre_")
colnames(Data_teatru)[22:35] <- paste_tocolnames(colnames(Data_teatru)[22:35], "PPS pre_")
colnames(Data_teatru)[36:55] <- paste_tocolnames(colnames(Data_teatru)[36:55], "PANAS pre_")
colnames(Data_teatru)[76:79] <- paste_tocolnames(colnames(Data_teatru)[76:79], "SRS post_")
colnames(Data_teatru)[80:99] <- paste_tocolnames(colnames(Data_teatru)[80:99], "PANAS post_")
colnames(Data_teatru)[100:113] <- paste_tocolnames(colnames(Data_teatru)[100:113], "PPS post_")
colnames(Data_teatru)[114:129] <- paste_tocolnames(colnames(Data_teatru)[114:129], "APS post_")

colnames(Data_psiho)[6:21] <- paste_tocolnames(colnames(Data_psiho)[6:21], "APS pre_")
colnames(Data_psiho)[22:35] <- paste_tocolnames(colnames(Data_psiho)[22:35], "PPS pre_")
colnames(Data_psiho)[36:55] <- paste_tocolnames(colnames(Data_psiho)[36:55], "PANAS pre_")
colnames(Data_psiho)[76:79] <- paste_tocolnames(colnames(Data_psiho)[76:79], "SRS post_")
colnames(Data_psiho)[80:99] <- paste_tocolnames(colnames(Data_psiho)[80:99], "PANAS post_")
colnames(Data_psiho)[100:113] <- paste_tocolnames(colnames(Data_psiho)[100:113], "PPS post_")
colnames(Data_psiho)[114:129] <- paste_tocolnames(colnames(Data_psiho)[114:129], "APS post_")
# as.data.frame(colnames(Data_psiho))
# as.data.frame(colnames(Data_teatru))

colnames(Data_teatru) <- enc2native(colnames(Data_teatru))      # fix encoding
colnames(Data_psiho) <- enc2native(colnames(Data_psiho))


## Recode known missing values
# str(Data_psiho, list.len = ncol(Data_psiho))
# str(Data_psiho, list.len = ncol(Data_psiho))
Data_teatru <-
  Data_teatru %>%
  replace(. == "/", NA) %>%                                     # missing values are coded "/"
  replace(. == "-", NA) %>%                                     # missing values are coded "-"
  replace(. == "NA", NA)                                        # missing values are coded "NA"

Data_psiho <-
  Data_psiho %>%
  replace(.=="/", NA) %>%                                       # missing values are coded "/"
  replace(.=="-", NA) %>%                                       # missing values are coded "-"
  replace(. == "NA", NA)                                        # missing values are coded "NA"

  
## Check for non-numeric elements in data sets
check_numeric1 <- as.data.frame(sapply(Data_teatru, varhandle::check.numeric)) 
check_numeric2 <- as.data.frame(sapply(Data_psiho, varhandle::check.numeric))
# sapply(check_numeric1, function(x) length(which(!x)))     # look at columns with non-numeric and count of non-numeric values
# sapply(check_numeric2, function(x) length(which(!x)))

nonnumeric1 <- sapply(check_numeric1, function(x) which(!x, arr.ind = TRUE))    # find row numbers for non-numeric values
nonnumeric2 <- sapply(check_numeric2, function(x) which(!x, arr.ind = TRUE)) 
nonnumeric1[lapply(nonnumeric1, length) > 0]                                   # return only columns and rown numbers were non-numeric
nonnumeric2[lapply(nonnumeric2, length) > 0]
## Recode to numeric
Data_teatru[, -c(1:5)] <- sapply(Data_teatru[, -c(1:5)], as.numeric)    
Data_psiho[, -c(1:5)] <- sapply(Data_psiho[, -c(1:5)], as.numeric)     # cant do this because of encoding:  mutate_at(-c(1:5), ~as.numeric(.))
  
## Correct typos in IDs
#unique(Data_teatru$`Indicativ subiect`)
#unique(Data_psiho$`Indicativ subiect`)
Data_teatru$`Indicativ subiect`[Data_teatru$`Indicativ subiect` == "18.A.1:3"] <- "18.A.1.3"
Data_teatru$`Indicativ subiect`[Data_teatru$`Indicativ subiect` == "26.A.1.3."] <- "26.A.1.3"
Data_teatru$`Indicativ subiect`[Data_teatru$`Indicativ subiect` == "27.A.1.3."] <- "27.A.1.3"

Data_psiho$`Indicativ subiect` <- gsub(".P", "", Data_psiho$`Indicativ subiect`)               # delete .P from IDs
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "16.A.A.1.3"] <- "16.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "1.A.1.3"] <- "01.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "2.A.1.3"] <- "02.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "4.A.1.3"] <- "04.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "0.4.A.1.3"] <- "04.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "5.A.1.3"] <- "05.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "6.A.1.3"] <- "06.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "7.A.1.3"] <- "07.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "8.A.1.3"] <- "08.A.1.3"
Data_psiho$`Indicativ subiect`[Data_psiho$`Indicativ subiect` == "9.A.1.3"] <- "09.A.1.3"
Data_psiho$`Indicativ subiect` <- paste0(Data_psiho$`Indicativ subiect`, ".P")                # add .P to all IDs

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Scoring Questionnaire and Unite
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Define function that calculates RowSums but only for rows with less than 10% NAs; and return NA if all row values are NA 
SpecialRowSums <- function(df, napercent = .1) {
  ifelse(rowSums(is.na(df)) > ncol(df) * napercent,
         NA,
         rowSums(df, na.rm = TRUE) * NA ^ (rowSums(!is.na(df)) == 0)
  )
}

## APS: simple sum
Data_teatru$`APS pre_Total` <- SpecialRowSums(Data_teatru[ ,sprintf("APS pre_%d", 1:16)], napercent = .13)  # not more than 2 NAs for 16 items
Data_teatru$`APS post_Total` <- SpecialRowSums(Data_teatru[ ,sprintf("APS post_%d", 1:16)], napercent = .13)
Data_psiho$`APS pre_Total` <- SpecialRowSums(Data_psiho[ ,sprintf("APS pre_%d", 1:16)], napercent = .13)
Data_psiho$`APS post_Total` <- SpecialRowSums(Data_psiho[ ,sprintf("APS post_%d", 1:16)], napercent = .13)

## PSS-SF 14: Items 4, 5, 6, 7, 9, 10, and 13 are scored in reverse direction.
keys_PSS <- c(1,1,1,-1,-1,-1,-1,1,-1,-1,1,1,-1,1)

Data_teatru$`PPS pre_Total` <- 
  SpecialRowSums(
  psych::reverse.code(items = Data_teatru[ ,sprintf("PPS pre_%d", 1:14)], keys = keys_PSS,  mini = 0, maxi = 4),
  napercent = .1)  # not more than 1 NAs for 14 items 
Data_teatru$`PPS post_Total` <- 
  SpecialRowSums(
    psych::reverse.code(items = Data_teatru[ ,sprintf("PPS post_%d", 1:14)], keys = keys_PSS,  mini = 0, maxi = 4),
    napercent = .1)
Data_psiho$`PPS pre_Total` <- 
  SpecialRowSums(
    psych::reverse.code(items = Data_psiho[ ,sprintf("PPS pre_%d", 1:14)], keys = keys_PSS,  mini = 0, maxi = 4),
    napercent = .1)  # not more than 1 NAs for 14 items 
Data_psiho$`PPS post_Total` <- 
  SpecialRowSums(
    psych::reverse.code(items = Data_psiho[ ,sprintf("PPS post_%d", 1:14)], keys = keys_PSS,  mini = 0, maxi = 4),
    napercent = .1)

## PANAS: Positive Affect Score = sum items 1, 3, 5, 9, 10, 12, 14, 16, 17, 19. Negative Affect Score = sum items 2, 4, 6, 7, 8, 11, 13, 15, 18, 20.
Data_teatru$`PA pre_Total` <- SpecialRowSums(Data_teatru[ ,35 + c(1,3,5,9,10,12,14,16,17,19)], napercent = .11) # not more than 1 NAs for 10 items
Data_teatru$`NA pre_Total` <- SpecialRowSums(Data_teatru[ ,35 + c(2,4,6,7,8,11,13,15,18,20)], napercent = .11)
Data_psiho$`PA pre_Total` <- SpecialRowSums(Data_psiho[ ,35 + c(1,3,5,9,10,12,14,16,17,19)], napercent = .11) 
Data_psiho$`NA pre_Total` <- SpecialRowSums(Data_psiho[ ,35 + c(2,4,6,7,8,11,13,15,18,20)], napercent = .11)

Data_teatru$`PA post_Total` <- SpecialRowSums(Data_teatru[ ,79 + c(1,3,5,9,10,12,14,16,17,19)], napercent = .11) 
Data_teatru$`NA post_Total` <- SpecialRowSums(Data_teatru[ ,79 + c(2,4,6,7,8,11,13,15,18,20)], napercent = .11)
Data_psiho$`PA post_Total` <- SpecialRowSums(Data_psiho[ ,79 + c(1,3,5,9,10,12,14,16,17,19)], napercent = .11) 
Data_psiho$`NA post_Total` <- SpecialRowSums(Data_psiho[ ,79 + c(2,4,6,7,8,11,13,15,18,20)], napercent = .11)


# Define other grouping varibles
Data_teatru <- 
  Data_teatru %>%
    mutate(Etapa = case_when(`Etapă, zi` %in% c("I.1", "I.2") ~ "I",
                             `Etapă, zi` %in% c("II.1",  "II.2") ~ "II",
                             `Etapă, zi` %in% c("III.1", "III.2") ~ "III",
                             `Etapă, zi` %in% c("IV.1", "IV.2") ~ "IV",
                             TRUE ~ NA_character_),
           Zi = case_when(`Etapă, zi` == "I.1" ~ "zi1", 
                          `Etapă, zi` == "I.2" ~ "zi2",  
                          `Etapă, zi` == "II.1" ~ "zi3", 
                          `Etapă, zi` == "II.2" ~ "zi4",
                          `Etapă, zi` == "III.1" ~ "zi5", 
                          `Etapă, zi` == "III.2" ~ "zi6", 
                          `Etapă, zi` == "IV.1" ~ "zi7", 
                          `Etapă, zi` == "IV.2" ~ "zi8", 
                          TRUE ~ NA_character_)) %>%
  unite(col = "Etapa_Zi", Etapa, Zi, remove = FALSE)

Data_psiho <- 
  Data_psiho %>%
  mutate(Etapa = case_when(`Etapă, zi` %in% c("I.1", "I.2") ~ "I",
                           `Etapă, zi` %in% c("II.1",  "II.2") ~ "II",
                           `Etapă, zi` %in% c("III.1", "III.2") ~ "III",
                           `Etapă, zi` %in% c("IV.1", "IV.2") ~ "IV",
                           TRUE ~ NA_character_),
         Zi = case_when(`Etapă, zi` == "I.1" ~ "zi1", 
                        `Etapă, zi` == "I.2" ~ "zi2",  
                        `Etapă, zi` == "II.1" ~ "zi3", 
                        `Etapă, zi` == "II.2" ~ "zi4",
                        `Etapă, zi` == "III.1" ~ "zi5", 
                        `Etapă, zi` == "III.2" ~ "zi6", 
                        `Etapă, zi` == "IV.1" ~ "zi7", 
                        `Etapă, zi` == "IV.2" ~ "zi8", 
                        TRUE ~ NA_character_)) %>%
  unite(col = "Etapa_Zi", Etapa, Zi, remove = FALSE)

## Unite data sets
Data_teatru$Dataset <- rep("teatru", nrow(Data_teatru))
Data_psiho$Dataset <- rep("psiho", nrow(Data_psiho))

Data_United <- rbind(Data_teatru, Data_psiho)

2 Sample descriptives

## Number of subjects
## Number of subjects by Teatru/Psiho

3 Outcomes Pre-Post Intervention

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   OUTCOMES PRE-POST INTERVENTION   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Function to run all for teatru, psiho and United
func_prepost_tot <- function(df){
  
  Data_melt <-
    df[, c("Indicativ subiect", "Grupa", "Nume Prenume", "Etapă, zi", 
           "APS pre_Total", "APS post_Total", 
           "PPS pre_Total", "PPS post_Total")] %>% 
    gather("APS pre_Total":"PPS post_Total", key = "variable", value = "value")  %>% 
    mutate_at(vars(c(1:5)), funs(as.factor)) %>% 
    mutate(variable = factor(variable, levels = c("APS pre_Total", "APS post_Total", 
                                                  "PPS pre_Total", "PPS post_Total")))
  
  # APS t test - paired
  aps_ttest <- 
    df %>%
    select(`Indicativ subiect`, `APS pre_Total`, `APS post_Total`) %>%
    group_by(`Indicativ subiect`) %>%
    summarise_all(funs(na.omit(.)[1])) %>%                        # squash rows with NAs per id
    # filter_all(all_vars(!is.na(.))) %>%                         # drop row if any column is NA -- dont use here
    do(broom::tidy(t.test(.$`APS pre_Total`,
                          .$`APS post_Total`,
                          mu = 0,
                          alt = "two.sided",
                          paired = TRUE,
                          conf.level = 0.95))) 
  aps_ttest_out <- knitr::kable(aps_ttest, caption = paste0(deparse(substitute(df)), " APS"), format = 'pandoc')
  
  # APS plot t test - unpaired
  aps_plot <-   
    Data_melt %>%
    filter(variable %in% c("APS pre_Total", "APS post_Total")) %>%
    #group_by(`Etapă, zi`) %>%
    ggplot(aes(x = variable, y = value)) +
    geom_boxplot() +
    stat_summary(fun.data = mean_se,  colour = "darkred") +
    xlab("") +
    ggtitle(deparse(substitute(df))) +
    #facet_wrap(~`Etapă, zi`) +
    ggpubr::stat_compare_means(method = "t.test", 
                               label = "p.signif",                                         # to avoid scientific notation of very small p-values
                               #paired = TRUE, 
                               comparisons = list(c("APS pre_Total", "APS post_Total")))  
  
  
  # PPS t test - paired
  pps_ttest <- 
    df %>%
    select(`Indicativ subiect`, `PPS pre_Total`, `PPS post_Total`) %>%
    group_by(`Indicativ subiect`) %>%
    summarise_all(funs(na.omit(.)[1])) %>%                        # squash rows with NAs per id
    # filter_all(all_vars(!is.na(.))) %>%                         # drop row if any column is NA -- dont use here
    do(broom::tidy(t.test(.$`PPS pre_Total`,
                          .$`PPS post_Total`,
                          mu = 0,
                          alt = "two.sided",
                          paired = TRUE,
                          conf.level = 0.95)))
  pps_ttest_out <- knitr::kable(pps_ttest, caption = paste0(deparse(substitute(df)), " PPS"), format = 'pandoc') 
  
  # PPS plot t test - unpaired
  pps_plot <-
    Data_melt %>%
    filter(variable %in% c("PPS pre_Total", "PPS post_Total")) %>%
    #group_by(`Etapă, zi`) %>%
    ggplot(aes(x = variable, y = value)) +
    geom_boxplot() +
    stat_summary(fun.data = mean_se,  colour = "darkred") +
    xlab("") +
    ggtitle(deparse(substitute(df))) +
    #facet_wrap(~`Etapă, zi`) +
    ggpubr::stat_compare_means(method = "t.test", 
                               label = "p.signif",                                         # to avoid scientific notation of very small p-values
                               #paired = TRUE, 
                               comparisons = list(c("PPS pre_Total", "PPS post_Total"))) 
  
  print(aps_ttest_out)
  print(aps_plot)
  print(pps_ttest_out)
  print(pps_plot)
}


cat("### Teatru")

3.0.1 Teatru

Data_teatru APS
estimate statistic p.value parameter conf.low conf.high method alternative
1.4 0.9058555 0.3803385 14 -1.914769 4.714769 Paired t-test two.sided
Data_teatru PPS
estimate statistic p.value parameter conf.low conf.high method alternative
-0.3 -0.1705189 0.8664048 19 -3.982333 3.382333 Paired t-test two.sided

3.0.2 Psiho

Data_psiho APS
estimate statistic p.value parameter conf.low conf.high method alternative
-1.666667 -1.011977 0.3257369 17 -5.141411 1.808077 Paired t-test two.sided
Data_psiho PPS
estimate statistic p.value parameter conf.low conf.high method alternative
3.3 1.759827 0.0945285 19 -0.6248056 7.224806 Paired t-test two.sided

3.0.3 United

Data_United APS
estimate statistic p.value parameter conf.low conf.high method alternative
-0.2727273 -0.236143 0.8148267 32 -2.625231 2.079776 Paired t-test two.sided
Data_United PPS
estimate statistic p.value parameter conf.low conf.high method alternative
1.5 1.152623 0.2560814 39 -1.132289 4.132289 Paired t-test two.sided

4 PANAS (PANAS for zi1, zi3, zi5, zi7 are Pre; zi2, zi4, zi6, zi8 are Post )

4.0.1 Teatru

4.0.2 Psiho

4.0.3 United

5 IOS (Pre on begining of Etapa, Post on end of Etapa)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   IOS - pre on begining of etapa, post on end  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Function plot pre post data faceted by zi
plot_prepost_zi <- function(df, pre_var, post_var){
  df %>%
    gather(pre_var, post_var, key = "variable", value = "value") %>%
    mutate(variable = factor(variable, levels = c(pre_var, post_var))) %>%
    ggplot(aes(y = value, x = variable)) +
    facet_wrap(~Zi, nrow = 1) + 
    ggtitle(deparse(substitute(df))) + 
    geom_boxplot() + stat_summary(fun.data = mean_se,  colour = "darkred")  +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    ggpubr::stat_compare_means(method = "t.test",
                               label = "p.signif",                                         # to avoid scientific notation of very small p-values
                               #paired = TRUE,
                               comparisons = list(c(pre_var, post_var)))
}


## Function plot pre post data adjusted comparisons of all 
plot_prepost_zi2 <- function(df, pre_var, post_var){
  df_modif <- 
    df %>%
    gather(pre_var, post_var, key = "variable", value = "value") %>%
    mutate(variable = factor(variable, levels = c(pre_var, post_var)),
           condition = interaction(variable, Zi),
           condition = as.factor(condition)) 
  
  stat.test <-
    df_modif %>%
    # group_by(`Indicativ subiect`) %>%
    rstatix::t_test(value ~ condition) %>%
    # rstatix::adjust_pvalue() %>%
    rstatix::add_significance("p") %>%
    filter(p.signif != "ns") 
  
  p <- 
    ggplot(df_modif, aes(y = value, x = condition)) +
      ggtitle(deparse(substitute(df))) + 
      geom_boxplot() + stat_summary(fun.data = mean_se,  colour = "darkred") +
      theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
      stat_pvalue_manual(stat.test, label = "p.signif", 
                         y.position = seq(max(df_modif$value, na.rm = TRUE)+2, max(df_modif$value, na.rm = TRUE)*2, 
                                          length.out = nrow(stat.test)))                                                  # very hacky
  
  print(p)
  return(stat.test) %>% print(n = Inf)
  
}


# cat("## By Zi")
cat("### Teatru")

5.0.1 Teatru

5.0.2 Psiho

5.0.3 United

5.1 ANOVA for Baseline each Etapa

5.2 By Etapa

6 VAS variables (multiple measurements on each zi)

6.2 VAS stres

6.2.1 Teatru

6.2.2 Psiho

6.2.3 United

6.3 VAS stare de bine

6.3.1 Teatru

6.3.2 Psiho

6.3.3 United

6.4 VAS corp

6.4.1 Teatru

6.4.2 Psiho

6.4.3 United

6.5 By Etapa - Only United

6.5.1 VAS Stres

6.5.2 VAS St bine

6.5.3 VAS St bine corp

7 Define function Moderation

find_mod <- function(df, dfp = NULL, num_only = TRUE, verbose = TRUE) {
  count = 0
  moderation_model_list <<- list()

  if(num_only == TRUE){
  numeric_cols <- unlist(lapply(df, is.numeric))                                      # get only numeric columns
  df <- df[, numeric_cols]
  }
  
  # restricted permutations for Moderation - Check: choose(len_names, 3)*3
  names <- colnames(df)
  len_names = length(names)
  
  if(is.null(dfp)){
    dfp <- lapply(1:len_names, function(i){
      tmp <- lapply(1:(len_names-1), function(j){
        tmp <- lapply((j+1):len_names, function(k){
          if(j != i & k != i) c(names[i], names[j], names[k])
        })
        do.call(rbind, tmp)
      })
      do.call(rbind, tmp)
    })
    dfp <- do.call(rbind.data.frame, dfp)
    names(dfp) <- paste("var", 1:3, sep = "_")
    dfp[, ] <- lapply(dfp[, ], as.character)    
  } else {
    dfp <- dfp
  }
  
  for (row in 1:nrow(dfp)) {                
    
    results <- medmod::mod(data = df,                                                   # mod does centering automatically
                          dep = dfp[row, 1], mod = dfp[row, 2], pred = dfp[row, 3], 
                          estMethod = "standard", test = TRUE, 
                          simpleSlopeEst = FALSE, simpleSlopePlot = FALSE)             # when testing use estMethod = 'bootstrap', bootstrap = 500 
    
    pmod <- as.data.frame(results$mod)[3,5]

    if(pmod < 0.05 && !is.na(pmod)) {
      count <- count + 1
        if(verbose == TRUE) {
        cat("Dependent Variable:", dfp[row, 1])
        print(results$mod) 
        }
      moderation_model_list[["Model"]][[paste("model", count, sep = "_")]] <<- as.data.frame(results$mod)   # return as list of dataframes
      moderation_model_list[["Syntax"]][[paste("model", count, sep = "_")]] <<- results$modelSyntax
      
    }
  }
  cat("\n","Report: ", count, "significant moderations out of", row, "total tries.")
}

## ex dfp
# dfp <- data.frame(
#   var1 = colnames(Data_med_melt)[grep("_post", colnames(Data_med_melt))],
#   var2 = rep("Condition", 13),
#   var3 = colnames(Data_med_melt)[grep("_pre", colnames(Data_med_melt))],
#   stringsAsFactors = FALSE
# )
# find_med(df = Data_med_melt, dfp = dfp, num_only = FALSE) 

8 Moderation on PrePost (APS, PSS)

term est se lower upper z p
PPS pre_Total 0.73 0.18 0.41 1.11 4.04 0.00
SRS post_Rela<U+021B>ia -0.07 0.14 -0.37 0.20 -0.50 0.62
PPS pre_Total:SRS post_Rela<U+021B>ia -0.04 0.02 -0.09 0.00 -1.73 0.08

term est se lower upper z p
Average 0.74 0.18 0.42 1.15 3.99 0.00
Low (-1SD) 1.11 0.31 0.61 1.86 3.65 0.00
High (+1SD) 0.36 0.26 -0.10 0.88 1.39 0.16

Scale for ‘colour’ is already present. Adding another scale for ‘colour’, which will replace the existing scale.


9 Session Info

R version 3.6.1 (2019-07-05)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 8.1 x64 (build 9600)

Matrix products: default

locale:
[1] LC_COLLATE=Romanian_Romania.1250  LC_CTYPE=Romanian_Romania.1250    LC_MONETARY=Romanian_Romania.1250 LC_NUMERIC=C                     
[5] LC_TIME=Romanian_Romania.1250    

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

other attached packages:
 [1] rio_0.5.16                 scales_1.0.0               ggpubr_0.2.5               magrittr_1.5               tadaatoolbox_0.16.1       
 [6] summarytools_0.8.8         rstatix_0.2.0              broom_0.5.2                PerformanceAnalytics_1.5.2 xts_0.11-2                
[11] zoo_1.8-4                  psych_1.8.12               plyr_1.8.4                 forcats_0.4.0              stringr_1.4.0             
[16] dplyr_0.8.3                purrr_0.3.2                readr_1.3.1                tidyr_1.0.0                tibble_2.1.3              
[21] ggplot2_3.2.1              tidyverse_1.2.1            papaja_0.1.0.9842          pacman_0.5.1              

loaded via a namespace (and not attached):
 [1] nlme_3.1-140       bitops_1.0-6       matrixStats_0.54.0 lubridate_1.7.4    httr_1.4.0         tools_3.6.1        backports_1.1.4   
 [8] R6_2.4.0           nortest_1.0-4      lazyeval_0.2.2     colorspace_1.4-1   withr_2.1.2        tidyselect_0.2.5   gridExtra_2.3     
[15] mnormt_1.5-5       pixiedust_0.8.6    curl_3.2           compiler_3.6.1     cli_1.1.0          rvest_0.3.2        expm_0.999-3      
[22] xml2_1.2.0         labeling_0.3       mvtnorm_1.0-11     quadprog_1.5-5     pbivnorm_0.6.0     digest_0.6.21      foreign_0.8-71    
[29] rmarkdown_1.17     base64enc_0.1-3    pkgconfig_2.0.3    htmltools_0.3.6    highr_0.8          pwr_1.2-2          rlang_0.4.0       
[36] readxl_1.1.0       rstudioapi_0.8     pryr_0.1.4         jmvcore_1.0.8      generics_0.0.2     jsonlite_1.6       zip_1.0.0         
[43] car_3.0-2          RCurl_1.95-4.11    rapportools_1.0    Matrix_1.2-17      Rcpp_1.0.2         DescTools_0.99.29  munsell_0.5.0     
[50] abind_1.4-5        viridis_0.5.1      lifecycle_0.1.0    stringi_1.4.3      carData_3.0-2      MASS_7.3-51.4      lavaan_0.6-3      
[57] grid_3.6.1         parallel_3.6.1     crayon_1.3.4       lattice_0.20-38    haven_2.1.1        pander_0.6.3       hms_0.5.1         
[64] zeallot_0.1.0      knitr_1.25         pillar_1.4.2       varhandle_2.0.4    rjson_0.2.20       boot_1.3-22        ggsignif_0.4.0    
[71] stats4_3.6.1       codetools_0.2-16   glue_1.3.1         evaluate_0.14      data.table_1.11.8  modelr_0.1.5       vctrs_0.2.0       
[78] cellranger_1.1.0   gtable_0.3.0       assertthat_0.2.1   xfun_0.9           openxlsx_4.1.0     viridisLite_0.3.0  medmod_1.0.0      
[85] jmv_1.0.8          ellipsis_0.3.0    
 

A work by Claudiu Papasteri

 

LS0tDQp0aXRsZTogIjxicj4gRHJhbWEgRXhlcmNpc2VzIiANCnN1YnRpdGxlOiAiUmVwb3J0IGZvciBUaGVhdGVyIEdyb3VwIGFuZCBQc3ljaG9sb2d5IEdyb3VwIg0KYXV0aG9yOiAiPGJyPiBDbGF1ZGl1IFBhcGFzdGVyaSINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVtICVZJylgIg0Kb3V0cHV0OiANCiAgICBodG1sX25vdGVib29rOg0KICAgICAgICAgICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgICAgICAgICB0b2M6IHRydWUNCiAgICAgICAgICAgIHRvY19kZXB0aDogMg0KICAgICAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgICAgICAgICB0aGVtZTogc3BhY2VsYWINCiAgICAgICAgICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICAgICAgICAgIGZvbnQtZmFtaWx5OiBBcmlhbA0KICAgICAgICAgICAgZmlnX3dpZHRoOiAxMA0KICAgICAgICAgICAgZmlnX2hlaWdodDogOQ0KICAgICMgcGRmX2RvY3VtZW50OiANCiAgICAgICAgICAgICMgdG9jOiB0cnVlDQogICAgICAgICAgICAjICB0b2NfZGVwdGg6IDINCiAgICAgICAgICAgICMgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgICAgICAgICAgIyBmb250c2l6ZTogMTFwdA0KICAgICAgICAgICAgIyBnZW9tZXRyeTogbWFyZ2luPTFpbg0KICAgICAgICAgICAgIyBmaWdfd2lkdGg6IDcNCiAgICAgICAgICAgICMgZmlnX2hlaWdodDogNg0KICAgICAgICAgICAgIyBmaWdfY2FwdGlvbjogdHJ1ZQ0KICAgICMgZ2l0aHViX2RvY3VtZW50OiANCiAgICAgICAgICAgICMgdG9jOiB0cnVlDQogICAgICAgICAgICAjIHRvY19kZXB0aDogMg0KICAgICAgICAgICAgIyBodG1sX3ByZXZpZXc6IGZhbHNlDQogICAgICAgICAgICAjIGZpZ193aWR0aDogNQ0KICAgICAgICAgICAgIyBmaWdfaGVpZ2h0OiA1DQogICAgICAgICAgICAjIGRldjoganBlZw0KLS0tDQoNCg0KPCEtLSBTZXR1cCAtLT4NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMga2ludHIgb3B0aW9ucw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBjb21tZW50ID0gIiMiLA0KICBjb2xsYXBzZSA9IFRSVUUsDQogIGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBUUlVFLCBjYWNoZSA9IFRSVUUgICAgICAgIyBlY2hvID0gRmFsc2UgZm9yIGdpdGh1Yl9kb2N1bWVudCwgYnV0IHdpbGwgYmUgZm9sZGVkIGluIGh0bWxfbm90ZWJvb2sNCikNCg0KIyBHZW5lcmFsIFIgb3B0aW9ucyBhbmQgaW5mbw0Kc2V0LnNlZWQoMTExKSAgICAgICAgICAgICAgICMgaW4gY2FzZSB3ZSB1c2UgcmFuZG9taXplZCBwcm9jZWR1cmVzICAgICAgIA0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpICAgICAgICMgcG9zaXRpdmUgdmFsdWVzIGJpYXMgdG93YXJkcyBmaXhlZCBhbmQgbmVnYXRpdmUgdG93YXJkcyBzY2llbnRpZmljIG5vdGF0aW9uDQoNCiMgTG9hZCBwYWNrYWdlcw0KaWYgKCFyZXF1aXJlKCJwYWNtYW4iKSkgaW5zdGFsbC5wYWNrYWdlcygicGFjbWFuIikNCnBhY2thZ2VzIDwtIGMoDQogICJwYXBhamEiLA0KICAidGlkeXZlcnNlIiwgInBseXIiLCAgICAgIA0KICAicHN5Y2giLCAiUGVyZm9ybWFuY2VBbmFseXRpY3MiLCAgICAgICAgICANCiAgImJyb29tIiwgInJzdGF0aXgiLA0KICAic3VtbWFyeXRvb2xzIiwgInRhZGFhdG9vbGJveCIsICAgICAgICAgICANCiAgImdncGxvdDIiLCAiZ2dwdWJyIiwgInNjYWxlcyIsICAgICAgICANCiAgInJpbyINCiAgIyAsIC4uLg0KKQ0KaWYgKCFyZXF1aXJlKCJwYWNtYW4iKSkgaW5zdGFsbC5wYWNrYWdlcygicGFjbWFuIikNCnBhY21hbjo6cF9sb2FkKGNoYXIgPSBwYWNrYWdlcykNCg0KIyBUaGVtZXMgZm9yIGdncGxvdDIgcGxvdGluZyAoaGVyZSB1c2VkIEFQQSBzdHlsZSkNCnRoZW1lX3NldCh0aGVtZV9hcGEoKSkNCmBgYA0KDQoNCg0KDQoNCjwhLS0gUmVwb3J0IC0tPg0KDQoNCiMgUmVhZCwgQ2xlYW4sIFJlY29kZSwgTWVyZ2UNCg0KYGBge3IgcmVkX2NsZWFuX3JlY29kZV9tZXJnZSwgcmVzdWx0cz0naGlkZSd9DQojfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fg0KIyBSZWFkLCBDbGVhbiwgUmVjb2RlLCBVbml0ZQ0KI35+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn4NCg0KIyMgUmVhZCBmaWxlcw0KZm9sZGVyX3RlYXRydSA8LSAiRTovQ2luZXRpYyBpZGVpIG5vaS9BMTMvRGF0ZSBncnVwIHRlYXRydSINCmZvbGRlcl9wc2lobyA8LSAiRTovQ2luZXRpYyBpZGVpIG5vaS9BMTMvRGF0ZSBncnVwIHBzaWhvIg0KZmlsZV90ZWF0cnUgPC0gIkExMyBUYWJlbCBkYXRlIENPTVBMRVQyOGlhbi54bHN4Ig0KZmlsZV9wc2lobyA8LSAiQTEzUCBUYWJlbCBjZW50cmFsaXphdC54bHN4Ig0KDQpzZXR3ZChmb2xkZXJfdGVhdHJ1KQ0KRGF0YV90ZWF0cnUgPC0gcmlvOjppbXBvcnQoZmlsZS5wYXRoKGZvbGRlcl90ZWF0cnUsIGZpbGVfdGVhdHJ1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAxKQ0Kc2V0d2QoZm9sZGVyX3BzaWhvKQ0KRGF0YV9wc2lobyA8LSByaW86OmltcG9ydChmaWxlLnBhdGgoZm9sZGVyX3BzaWhvLCBmaWxlX3BzaWhvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAxKQ0KDQoNCiMjIFRpZHkgdXAgZGF0YQ0KIyBGdW5jdGlvbiBjb2FsZXNjZSByb3dzOiBjb2xhcHNlIHdoZW4gTkEsIHVuaXRlIHdpdGggIl8iIHdoZW4gbm90IE5BDQpjb2FsZXNjZTIgPC0gZnVuY3Rpb24oLi4uKSB7DQogIFJlZHVjZShmdW5jdGlvbih4LCB5KSB7DQogICAgaSA8LSB3aGljaChpcy5uYSh4KSkNCiAgICBqIDwtIHdoaWNoKCFpcy5uYSh4KSAmICFpcy5uYSh5KSkNCiAgICB4W2ldIDwtIHlbaV0NCiAgICB4W2pdIDwtIHBhc3RlKHhbal0sIHlbal0sIHNlcCA9ICJfIikNCiAgICB4fSwNCiAgICBsaXN0KC4uLikpDQp9DQoNCmNvbG5hbWVzKERhdGFfdGVhdHJ1KSA8LSBjb2FsZXNjZTIoRGF0YV90ZWF0cnVbMixdLCBEYXRhX3RlYXRydVszLF0pDQpEYXRhX3RlYXRydSA8LSBEYXRhX3RlYXRydVstYygxOjMpLF0NCg0KY29sbmFtZXMoRGF0YV9wc2lobykgPC0gY29hbGVzY2UyKERhdGFfcHNpaG9bMixdLCBEYXRhX3BzaWhvWzMsXSkNCkRhdGFfcHNpaG8gPC0gRGF0YV9wc2lob1stYygxOjMpLF0NCg0KDQojIyBDaGVjayBpZiBib3RoIGRhdGEgc2V0cyBoYXZlIGV4YWN0IHNhbWUgY29sdW1uIG5hbWVzDQphbGwuZXF1YWwoY29sbmFtZXMoRGF0YV90ZWF0cnUpLCBjb2xuYW1lcyhEYXRhX3BzaWhvKSkgICAgICAgICAgICMgbm90IHRoZSBzYW1lDQpkaWZmX2NvbG5hbWUgPC0gc2V0ZGlmZihjb2xuYW1lcyhEYXRhX3RlYXRydSksIGNvbG5hbWVzKERhdGFfcHNpaG8pKQ0KaW5kIDwtIHdoaWNoKGNvbG5hbWVzKERhdGFfdGVhdHJ1KSA9PSAgZGlmZl9jb2xuYW1lKQ0KDQpjb2xuYW1lcyhEYXRhX3BzaWhvKVtpbmRdIDwtIGRpZmZfY29sbmFtZSAgICAgICAgICAgICAgICAgICAgICAgIyByZXBsYWNlIHdpdGggc2FtZSBjb2xuYW1lIGZyb20gRGF0YV90ZWF0cnUNCmFsbC5lcXVhbChjb2xuYW1lcyhEYXRhX3RlYXRydSksIGNvbG5hbWVzKERhdGFfcHNpaG8pKSAgICAgICAgICAgIyBub3cgdGhleSBhcmUgdGhlIHNhbWUNCg0KDQojIyBTb2x2ZSBkdXBsaWNhdGUgbmFtZXMgZHVlIHRvIGV4Y2VsIGRvdWJsZSBoZWFkZXINCiMgRnVuY3Rpb24gdG8gcGFzdGUgYSBzdHJpbmcgYmVmb3JlIGNvbHVtbiBuYW1lIGlmIGl0IGRvZXNudCBhbHJlYWR5IHN0YXJ0IHdpdGggdGhhdCBzdHJpbmcNCnBhc3RlX3RvY29sbmFtZXMgPC0gZnVuY3Rpb24odmVjX2NvbG5hbWVzLCBzdHJpbmdfcGFzdGUpew0KICBpbmQgPC0gZ3JlcChwYXR0ZXJuID0gc3RyaW5nX3Bhc3RlLCB2ZWNfY29sbmFtZXMpICAgICAgICAgICAgICAgICAgICMgaWdub3JlIGNvbHVtbiB0aGF0IGFscmVhZHkgaGFzIHN0cmluZyBwYXR0ZXJtDQogIHZlY19jb2xuYW1lc1staW5kXSA8LSBwYXN0ZTAoc3RyaW5nX3Bhc3RlLCB2ZWNfY29sbmFtZXNbLWluZF0pICAgICAgIyBwYXN0ZSBwYXR0ZXJuIHRvIHRoZSByZXN0IG9mIHRoZW0NCiAgcmV0dXJuKHZlY19jb2xuYW1lcykNCn0NCg0KY29sbmFtZXMoRGF0YV90ZWF0cnUpWzY6MjFdIDwtIHBhc3RlX3RvY29sbmFtZXMoY29sbmFtZXMoRGF0YV90ZWF0cnUpWzY6MjFdLCAiQVBTIHByZV8iKQ0KY29sbmFtZXMoRGF0YV90ZWF0cnUpWzIyOjM1XSA8LSBwYXN0ZV90b2NvbG5hbWVzKGNvbG5hbWVzKERhdGFfdGVhdHJ1KVsyMjozNV0sICJQUFMgcHJlXyIpDQpjb2xuYW1lcyhEYXRhX3RlYXRydSlbMzY6NTVdIDwtIHBhc3RlX3RvY29sbmFtZXMoY29sbmFtZXMoRGF0YV90ZWF0cnUpWzM2OjU1XSwgIlBBTkFTIHByZV8iKQ0KY29sbmFtZXMoRGF0YV90ZWF0cnUpWzc2Ojc5XSA8LSBwYXN0ZV90b2NvbG5hbWVzKGNvbG5hbWVzKERhdGFfdGVhdHJ1KVs3Njo3OV0sICJTUlMgcG9zdF8iKQ0KY29sbmFtZXMoRGF0YV90ZWF0cnUpWzgwOjk5XSA8LSBwYXN0ZV90b2NvbG5hbWVzKGNvbG5hbWVzKERhdGFfdGVhdHJ1KVs4MDo5OV0sICJQQU5BUyBwb3N0XyIpDQpjb2xuYW1lcyhEYXRhX3RlYXRydSlbMTAwOjExM10gPC0gcGFzdGVfdG9jb2xuYW1lcyhjb2xuYW1lcyhEYXRhX3RlYXRydSlbMTAwOjExM10sICJQUFMgcG9zdF8iKQ0KY29sbmFtZXMoRGF0YV90ZWF0cnUpWzExNDoxMjldIDwtIHBhc3RlX3RvY29sbmFtZXMoY29sbmFtZXMoRGF0YV90ZWF0cnUpWzExNDoxMjldLCAiQVBTIHBvc3RfIikNCg0KY29sbmFtZXMoRGF0YV9wc2lobylbNjoyMV0gPC0gcGFzdGVfdG9jb2xuYW1lcyhjb2xuYW1lcyhEYXRhX3BzaWhvKVs2OjIxXSwgIkFQUyBwcmVfIikNCmNvbG5hbWVzKERhdGFfcHNpaG8pWzIyOjM1XSA8LSBwYXN0ZV90b2NvbG5hbWVzKGNvbG5hbWVzKERhdGFfcHNpaG8pWzIyOjM1XSwgIlBQUyBwcmVfIikNCmNvbG5hbWVzKERhdGFfcHNpaG8pWzM2OjU1XSA8LSBwYXN0ZV90b2NvbG5hbWVzKGNvbG5hbWVzKERhdGFfcHNpaG8pWzM2OjU1XSwgIlBBTkFTIHByZV8iKQ0KY29sbmFtZXMoRGF0YV9wc2lobylbNzY6NzldIDwtIHBhc3RlX3RvY29sbmFtZXMoY29sbmFtZXMoRGF0YV9wc2lobylbNzY6NzldLCAiU1JTIHBvc3RfIikNCmNvbG5hbWVzKERhdGFfcHNpaG8pWzgwOjk5XSA8LSBwYXN0ZV90b2NvbG5hbWVzKGNvbG5hbWVzKERhdGFfcHNpaG8pWzgwOjk5XSwgIlBBTkFTIHBvc3RfIikNCmNvbG5hbWVzKERhdGFfcHNpaG8pWzEwMDoxMTNdIDwtIHBhc3RlX3RvY29sbmFtZXMoY29sbmFtZXMoRGF0YV9wc2lobylbMTAwOjExM10sICJQUFMgcG9zdF8iKQ0KY29sbmFtZXMoRGF0YV9wc2lobylbMTE0OjEyOV0gPC0gcGFzdGVfdG9jb2xuYW1lcyhjb2xuYW1lcyhEYXRhX3BzaWhvKVsxMTQ6MTI5XSwgIkFQUyBwb3N0XyIpDQojIGFzLmRhdGEuZnJhbWUoY29sbmFtZXMoRGF0YV9wc2lobykpDQojIGFzLmRhdGEuZnJhbWUoY29sbmFtZXMoRGF0YV90ZWF0cnUpKQ0KDQpjb2xuYW1lcyhEYXRhX3RlYXRydSkgPC0gZW5jMm5hdGl2ZShjb2xuYW1lcyhEYXRhX3RlYXRydSkpICAgICAgIyBmaXggZW5jb2RpbmcNCmNvbG5hbWVzKERhdGFfcHNpaG8pIDwtIGVuYzJuYXRpdmUoY29sbmFtZXMoRGF0YV9wc2lobykpDQoNCg0KIyMgUmVjb2RlIGtub3duIG1pc3NpbmcgdmFsdWVzDQojIHN0cihEYXRhX3BzaWhvLCBsaXN0LmxlbiA9IG5jb2woRGF0YV9wc2lobykpDQojIHN0cihEYXRhX3BzaWhvLCBsaXN0LmxlbiA9IG5jb2woRGF0YV9wc2lobykpDQpEYXRhX3RlYXRydSA8LQ0KICBEYXRhX3RlYXRydSAlPiUNCiAgcmVwbGFjZSguID09ICIvIiwgTkEpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG1pc3NpbmcgdmFsdWVzIGFyZSBjb2RlZCAiLyINCiAgcmVwbGFjZSguID09ICItIiwgTkEpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG1pc3NpbmcgdmFsdWVzIGFyZSBjb2RlZCAiLSINCiAgcmVwbGFjZSguID09ICJOQSIsIE5BKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG1pc3NpbmcgdmFsdWVzIGFyZSBjb2RlZCAiTkEiDQoNCkRhdGFfcHNpaG8gPC0NCiAgRGF0YV9wc2lobyAlPiUNCiAgcmVwbGFjZSguPT0iLyIsIE5BKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG1pc3NpbmcgdmFsdWVzIGFyZSBjb2RlZCAiLyINCiAgcmVwbGFjZSguPT0iLSIsIE5BKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG1pc3NpbmcgdmFsdWVzIGFyZSBjb2RlZCAiLSINCiAgcmVwbGFjZSguID09ICJOQSIsIE5BKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG1pc3NpbmcgdmFsdWVzIGFyZSBjb2RlZCAiTkEiDQoNCiAgDQojIyBDaGVjayBmb3Igbm9uLW51bWVyaWMgZWxlbWVudHMgaW4gZGF0YSBzZXRzDQpjaGVja19udW1lcmljMSA8LSBhcy5kYXRhLmZyYW1lKHNhcHBseShEYXRhX3RlYXRydSwgdmFyaGFuZGxlOjpjaGVjay5udW1lcmljKSkgDQpjaGVja19udW1lcmljMiA8LSBhcy5kYXRhLmZyYW1lKHNhcHBseShEYXRhX3BzaWhvLCB2YXJoYW5kbGU6OmNoZWNrLm51bWVyaWMpKQ0KIyBzYXBwbHkoY2hlY2tfbnVtZXJpYzEsIGZ1bmN0aW9uKHgpIGxlbmd0aCh3aGljaCgheCkpKSAgICAgIyBsb29rIGF0IGNvbHVtbnMgd2l0aCBub24tbnVtZXJpYyBhbmQgY291bnQgb2Ygbm9uLW51bWVyaWMgdmFsdWVzDQojIHNhcHBseShjaGVja19udW1lcmljMiwgZnVuY3Rpb24oeCkgbGVuZ3RoKHdoaWNoKCF4KSkpDQoNCm5vbm51bWVyaWMxIDwtIHNhcHBseShjaGVja19udW1lcmljMSwgZnVuY3Rpb24oeCkgd2hpY2goIXgsIGFyci5pbmQgPSBUUlVFKSkgICAgIyBmaW5kIHJvdyBudW1iZXJzIGZvciBub24tbnVtZXJpYyB2YWx1ZXMNCm5vbm51bWVyaWMyIDwtIHNhcHBseShjaGVja19udW1lcmljMiwgZnVuY3Rpb24oeCkgd2hpY2goIXgsIGFyci5pbmQgPSBUUlVFKSkgDQpub25udW1lcmljMVtsYXBwbHkobm9ubnVtZXJpYzEsIGxlbmd0aCkgPiAwXSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyByZXR1cm4gb25seSBjb2x1bW5zIGFuZCByb3duIG51bWJlcnMgd2VyZSBub24tbnVtZXJpYw0Kbm9ubnVtZXJpYzJbbGFwcGx5KG5vbm51bWVyaWMyLCBsZW5ndGgpID4gMF0NCg0KIyMgUmVjb2RlIHRvIG51bWVyaWMNCkRhdGFfdGVhdHJ1WywgLWMoMTo1KV0gPC0gc2FwcGx5KERhdGFfdGVhdHJ1WywgLWMoMTo1KV0sIGFzLm51bWVyaWMpICAgIA0KRGF0YV9wc2lob1ssIC1jKDE6NSldIDwtIHNhcHBseShEYXRhX3BzaWhvWywgLWMoMTo1KV0sIGFzLm51bWVyaWMpICAgICAjIGNhbnQgZG8gdGhpcyBiZWNhdXNlIG9mIGVuY29kaW5nOiAgbXV0YXRlX2F0KC1jKDE6NSksIH5hcy5udW1lcmljKC4pKQ0KICANCiMjIENvcnJlY3QgdHlwb3MgaW4gSURzDQojdW5pcXVlKERhdGFfdGVhdHJ1JGBJbmRpY2F0aXYgc3ViaWVjdGApDQojdW5pcXVlKERhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YCkNCkRhdGFfdGVhdHJ1JGBJbmRpY2F0aXYgc3ViaWVjdGBbRGF0YV90ZWF0cnUkYEluZGljYXRpdiBzdWJpZWN0YCA9PSAiMTguQS4xOjMiXSA8LSAiMTguQS4xLjMiDQpEYXRhX3RlYXRydSRgSW5kaWNhdGl2IHN1YmllY3RgW0RhdGFfdGVhdHJ1JGBJbmRpY2F0aXYgc3ViaWVjdGAgPT0gIjI2LkEuMS4zLiJdIDwtICIyNi5BLjEuMyINCkRhdGFfdGVhdHJ1JGBJbmRpY2F0aXYgc3ViaWVjdGBbRGF0YV90ZWF0cnUkYEluZGljYXRpdiBzdWJpZWN0YCA9PSAiMjcuQS4xLjMuIl0gPC0gIjI3LkEuMS4zIg0KDQpEYXRhX3BzaWhvJGBJbmRpY2F0aXYgc3ViaWVjdGAgPC0gZ3N1YigiLlAiLCAiIiwgRGF0YV9wc2lobyRgSW5kaWNhdGl2IHN1YmllY3RgKSAgICAgICAgICAgICAgICMgZGVsZXRlIC5QIGZyb20gSURzDQpEYXRhX3BzaWhvJGBJbmRpY2F0aXYgc3ViaWVjdGBbRGF0YV9wc2lobyRgSW5kaWNhdGl2IHN1YmllY3RgID09ICIxNi5BLkEuMS4zIl0gPC0gIjE2LkEuMS4zIg0KRGF0YV9wc2lobyRgSW5kaWNhdGl2IHN1YmllY3RgW0RhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YCA9PSAiMS5BLjEuMyJdIDwtICIwMS5BLjEuMyINCkRhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YFtEYXRhX3BzaWhvJGBJbmRpY2F0aXYgc3ViaWVjdGAgPT0gIjIuQS4xLjMiXSA8LSAiMDIuQS4xLjMiDQpEYXRhX3BzaWhvJGBJbmRpY2F0aXYgc3ViaWVjdGBbRGF0YV9wc2lobyRgSW5kaWNhdGl2IHN1YmllY3RgID09ICI0LkEuMS4zIl0gPC0gIjA0LkEuMS4zIg0KRGF0YV9wc2lobyRgSW5kaWNhdGl2IHN1YmllY3RgW0RhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YCA9PSAiMC40LkEuMS4zIl0gPC0gIjA0LkEuMS4zIg0KRGF0YV9wc2lobyRgSW5kaWNhdGl2IHN1YmllY3RgW0RhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YCA9PSAiNS5BLjEuMyJdIDwtICIwNS5BLjEuMyINCkRhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YFtEYXRhX3BzaWhvJGBJbmRpY2F0aXYgc3ViaWVjdGAgPT0gIjYuQS4xLjMiXSA8LSAiMDYuQS4xLjMiDQpEYXRhX3BzaWhvJGBJbmRpY2F0aXYgc3ViaWVjdGBbRGF0YV9wc2lobyRgSW5kaWNhdGl2IHN1YmllY3RgID09ICI3LkEuMS4zIl0gPC0gIjA3LkEuMS4zIg0KRGF0YV9wc2lobyRgSW5kaWNhdGl2IHN1YmllY3RgW0RhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YCA9PSAiOC5BLjEuMyJdIDwtICIwOC5BLjEuMyINCkRhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YFtEYXRhX3BzaWhvJGBJbmRpY2F0aXYgc3ViaWVjdGAgPT0gIjkuQS4xLjMiXSA8LSAiMDkuQS4xLjMiDQpEYXRhX3BzaWhvJGBJbmRpY2F0aXYgc3ViaWVjdGAgPC0gcGFzdGUwKERhdGFfcHNpaG8kYEluZGljYXRpdiBzdWJpZWN0YCwgIi5QIikgICAgICAgICAgICAgICAgIyBhZGQgLlAgdG8gYWxsIElEcw0KDQojfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fg0KIyBTY29yaW5nIFF1ZXN0aW9ubmFpcmUgYW5kIFVuaXRlDQojfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fg0KIyMgRGVmaW5lIGZ1bmN0aW9uIHRoYXQgY2FsY3VsYXRlcyBSb3dTdW1zIGJ1dCBvbmx5IGZvciByb3dzIHdpdGggbGVzcyB0aGFuIDEwJSBOQXM7IGFuZCByZXR1cm4gTkEgaWYgYWxsIHJvdyB2YWx1ZXMgYXJlIE5BIA0KU3BlY2lhbFJvd1N1bXMgPC0gZnVuY3Rpb24oZGYsIG5hcGVyY2VudCA9IC4xKSB7DQogIGlmZWxzZShyb3dTdW1zKGlzLm5hKGRmKSkgPiBuY29sKGRmKSAqIG5hcGVyY2VudCwNCiAgICAgICAgIE5BLA0KICAgICAgICAgcm93U3VtcyhkZiwgbmEucm0gPSBUUlVFKSAqIE5BIF4gKHJvd1N1bXMoIWlzLm5hKGRmKSkgPT0gMCkNCiAgKQ0KfQ0KDQojIyBBUFM6IHNpbXBsZSBzdW0NCkRhdGFfdGVhdHJ1JGBBUFMgcHJlX1RvdGFsYCA8LSBTcGVjaWFsUm93U3VtcyhEYXRhX3RlYXRydVsgLHNwcmludGYoIkFQUyBwcmVfJWQiLCAxOjE2KV0sIG5hcGVyY2VudCA9IC4xMykgICMgbm90IG1vcmUgdGhhbiAyIE5BcyBmb3IgMTYgaXRlbXMNCkRhdGFfdGVhdHJ1JGBBUFMgcG9zdF9Ub3RhbGAgPC0gU3BlY2lhbFJvd1N1bXMoRGF0YV90ZWF0cnVbICxzcHJpbnRmKCJBUFMgcG9zdF8lZCIsIDE6MTYpXSwgbmFwZXJjZW50ID0gLjEzKQ0KRGF0YV9wc2lobyRgQVBTIHByZV9Ub3RhbGAgPC0gU3BlY2lhbFJvd1N1bXMoRGF0YV9wc2lob1sgLHNwcmludGYoIkFQUyBwcmVfJWQiLCAxOjE2KV0sIG5hcGVyY2VudCA9IC4xMykNCkRhdGFfcHNpaG8kYEFQUyBwb3N0X1RvdGFsYCA8LSBTcGVjaWFsUm93U3VtcyhEYXRhX3BzaWhvWyAsc3ByaW50ZigiQVBTIHBvc3RfJWQiLCAxOjE2KV0sIG5hcGVyY2VudCA9IC4xMykNCg0KIyMgUFNTLVNGIDE0OiBJdGVtcyA0LCA1LCA2LCA3LCA5LCAxMCwgYW5kIDEzIGFyZSBzY29yZWQgaW4gcmV2ZXJzZSBkaXJlY3Rpb24uDQprZXlzX1BTUyA8LSBjKDEsMSwxLC0xLC0xLC0xLC0xLDEsLTEsLTEsMSwxLC0xLDEpDQoNCkRhdGFfdGVhdHJ1JGBQUFMgcHJlX1RvdGFsYCA8LSANCiAgU3BlY2lhbFJvd1N1bXMoDQogIHBzeWNoOjpyZXZlcnNlLmNvZGUoaXRlbXMgPSBEYXRhX3RlYXRydVsgLHNwcmludGYoIlBQUyBwcmVfJWQiLCAxOjE0KV0sIGtleXMgPSBrZXlzX1BTUywgIG1pbmkgPSAwLCBtYXhpID0gNCksDQogIG5hcGVyY2VudCA9IC4xKSAgIyBub3QgbW9yZSB0aGFuIDEgTkFzIGZvciAxNCBpdGVtcyANCkRhdGFfdGVhdHJ1JGBQUFMgcG9zdF9Ub3RhbGAgPC0gDQogIFNwZWNpYWxSb3dTdW1zKA0KICAgIHBzeWNoOjpyZXZlcnNlLmNvZGUoaXRlbXMgPSBEYXRhX3RlYXRydVsgLHNwcmludGYoIlBQUyBwb3N0XyVkIiwgMToxNCldLCBrZXlzID0ga2V5c19QU1MsICBtaW5pID0gMCwgbWF4aSA9IDQpLA0KICAgIG5hcGVyY2VudCA9IC4xKQ0KRGF0YV9wc2lobyRgUFBTIHByZV9Ub3RhbGAgPC0gDQogIFNwZWNpYWxSb3dTdW1zKA0KICAgIHBzeWNoOjpyZXZlcnNlLmNvZGUoaXRlbXMgPSBEYXRhX3BzaWhvWyAsc3ByaW50ZigiUFBTIHByZV8lZCIsIDE6MTQpXSwga2V5cyA9IGtleXNfUFNTLCAgbWluaSA9IDAsIG1heGkgPSA0KSwNCiAgICBuYXBlcmNlbnQgPSAuMSkgICMgbm90IG1vcmUgdGhhbiAxIE5BcyBmb3IgMTQgaXRlbXMgDQpEYXRhX3BzaWhvJGBQUFMgcG9zdF9Ub3RhbGAgPC0gDQogIFNwZWNpYWxSb3dTdW1zKA0KICAgIHBzeWNoOjpyZXZlcnNlLmNvZGUoaXRlbXMgPSBEYXRhX3BzaWhvWyAsc3ByaW50ZigiUFBTIHBvc3RfJWQiLCAxOjE0KV0sIGtleXMgPSBrZXlzX1BTUywgIG1pbmkgPSAwLCBtYXhpID0gNCksDQogICAgbmFwZXJjZW50ID0gLjEpDQoNCiMjIFBBTkFTOiBQb3NpdGl2ZSBBZmZlY3QgU2NvcmUgPSBzdW0gaXRlbXMgMSwgMywgNSwgOSwgMTAsIDEyLCAxNCwgMTYsIDE3LCAxOS4gTmVnYXRpdmUgQWZmZWN0IFNjb3JlID0gc3VtIGl0ZW1zIDIsIDQsIDYsIDcsIDgsIDExLCAxMywgMTUsIDE4LCAyMC4NCkRhdGFfdGVhdHJ1JGBQQSBwcmVfVG90YWxgIDwtIFNwZWNpYWxSb3dTdW1zKERhdGFfdGVhdHJ1WyAsMzUgKyBjKDEsMyw1LDksMTAsMTIsMTQsMTYsMTcsMTkpXSwgbmFwZXJjZW50ID0gLjExKSAjIG5vdCBtb3JlIHRoYW4gMSBOQXMgZm9yIDEwIGl0ZW1zDQpEYXRhX3RlYXRydSRgTkEgcHJlX1RvdGFsYCA8LSBTcGVjaWFsUm93U3VtcyhEYXRhX3RlYXRydVsgLDM1ICsgYygyLDQsNiw3LDgsMTEsMTMsMTUsMTgsMjApXSwgbmFwZXJjZW50ID0gLjExKQ0KRGF0YV9wc2lobyRgUEEgcHJlX1RvdGFsYCA8LSBTcGVjaWFsUm93U3VtcyhEYXRhX3BzaWhvWyAsMzUgKyBjKDEsMyw1LDksMTAsMTIsMTQsMTYsMTcsMTkpXSwgbmFwZXJjZW50ID0gLjExKSANCkRhdGFfcHNpaG8kYE5BIHByZV9Ub3RhbGAgPC0gU3BlY2lhbFJvd1N1bXMoRGF0YV9wc2lob1sgLDM1ICsgYygyLDQsNiw3LDgsMTEsMTMsMTUsMTgsMjApXSwgbmFwZXJjZW50ID0gLjExKQ0KDQpEYXRhX3RlYXRydSRgUEEgcG9zdF9Ub3RhbGAgPC0gU3BlY2lhbFJvd1N1bXMoRGF0YV90ZWF0cnVbICw3OSArIGMoMSwzLDUsOSwxMCwxMiwxNCwxNiwxNywxOSldLCBuYXBlcmNlbnQgPSAuMTEpIA0KRGF0YV90ZWF0cnUkYE5BIHBvc3RfVG90YWxgIDwtIFNwZWNpYWxSb3dTdW1zKERhdGFfdGVhdHJ1WyAsNzkgKyBjKDIsNCw2LDcsOCwxMSwxMywxNSwxOCwyMCldLCBuYXBlcmNlbnQgPSAuMTEpDQpEYXRhX3BzaWhvJGBQQSBwb3N0X1RvdGFsYCA8LSBTcGVjaWFsUm93U3VtcyhEYXRhX3BzaWhvWyAsNzkgKyBjKDEsMyw1LDksMTAsMTIsMTQsMTYsMTcsMTkpXSwgbmFwZXJjZW50ID0gLjExKSANCkRhdGFfcHNpaG8kYE5BIHBvc3RfVG90YWxgIDwtIFNwZWNpYWxSb3dTdW1zKERhdGFfcHNpaG9bICw3OSArIGMoMiw0LDYsNyw4LDExLDEzLDE1LDE4LDIwKV0sIG5hcGVyY2VudCA9IC4xMSkNCg0KDQojIERlZmluZSBvdGhlciBncm91cGluZyB2YXJpYmxlcw0KRGF0YV90ZWF0cnUgPC0gDQogIERhdGFfdGVhdHJ1ICU+JQ0KICAgIG11dGF0ZShFdGFwYSA9IGNhc2Vfd2hlbihgRXRhcMSDLCB6aWAgJWluJSBjKCJJLjEiLCAiSS4yIikgfiAiSSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBFdGFwxIMsIHppYCAlaW4lIGMoIklJLjEiLCAgIklJLjIiKSB+ICJJSSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBFdGFwxIMsIHppYCAlaW4lIGMoIklJSS4xIiwgIklJSS4yIikgfiAiSUlJIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgICVpbiUgYygiSVYuMSIsICJJVi4yIikgfiAiSVYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXyksDQogICAgICAgICAgIFppID0gY2FzZV93aGVuKGBFdGFwxIMsIHppYCA9PSAiSS4xIiB+ICJ6aTEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgID09ICJJLjIiIH4gInppMiIsICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgID09ICJJSS4xIiB+ICJ6aTMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgID09ICJJSS4yIiB+ICJ6aTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBgRXRhcMSDLCB6aWAgPT0gIklJSS4xIiB+ICJ6aTUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgID09ICJJSUkuMiIgfiAiemk2IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGBFdGFwxIMsIHppYCA9PSAiSVYuMSIgfiAiemk3IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGBFdGFwxIMsIHppYCA9PSAiSVYuMiIgfiAiemk4IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSkgJT4lDQogIHVuaXRlKGNvbCA9ICJFdGFwYV9aaSIsIEV0YXBhLCBaaSwgcmVtb3ZlID0gRkFMU0UpDQoNCkRhdGFfcHNpaG8gPC0gDQogIERhdGFfcHNpaG8gJT4lDQogIG11dGF0ZShFdGFwYSA9IGNhc2Vfd2hlbihgRXRhcMSDLCB6aWAgJWluJSBjKCJJLjEiLCAiSS4yIikgfiAiSSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBgRXRhcMSDLCB6aWAgJWluJSBjKCJJSS4xIiwgICJJSS4yIikgfiAiSUkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgICVpbiUgYygiSUlJLjEiLCAiSUlJLjIiKSB+ICJJSUkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgICVpbiUgYygiSVYuMSIsICJJVi4yIikgfiAiSVYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8pLA0KICAgICAgICAgWmkgPSBjYXNlX3doZW4oYEV0YXDEgywgemlgID09ICJJLjEiIH4gInppMSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgID09ICJJLjIiIH4gInppMiIsICANCiAgICAgICAgICAgICAgICAgICAgICAgIGBFdGFwxIMsIHppYCA9PSAiSUkuMSIgfiAiemkzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICBgRXRhcMSDLCB6aWAgPT0gIklJLjIiIH4gInppNCIsDQogICAgICAgICAgICAgICAgICAgICAgICBgRXRhcMSDLCB6aWAgPT0gIklJSS4xIiB+ICJ6aTUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGBFdGFwxIMsIHppYCA9PSAiSUlJLjIiIH4gInppNiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgYEV0YXDEgywgemlgID09ICJJVi4xIiB+ICJ6aTciLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGBFdGFwxIMsIHppYCA9PSAiSVYuMiIgfiAiemk4IiwgDQogICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykpICU+JQ0KICB1bml0ZShjb2wgPSAiRXRhcGFfWmkiLCBFdGFwYSwgWmksIHJlbW92ZSA9IEZBTFNFKQ0KDQojIyBVbml0ZSBkYXRhIHNldHMNCkRhdGFfdGVhdHJ1JERhdGFzZXQgPC0gcmVwKCJ0ZWF0cnUiLCBucm93KERhdGFfdGVhdHJ1KSkNCkRhdGFfcHNpaG8kRGF0YXNldCA8LSByZXAoInBzaWhvIiwgbnJvdyhEYXRhX3BzaWhvKSkNCg0KRGF0YV9Vbml0ZWQgPC0gcmJpbmQoRGF0YV90ZWF0cnUsIERhdGFfcHNpaG8pDQpgYGANCg0KDQojIFNhbXBsZSBkZXNjcmlwdGl2ZXMNCg0KYGBge3Igc2FtcGxlX2Rlc2N9DQpjYXQoIiMjIE51bWJlciBvZiBzdWJqZWN0cyIpDQpEYXRhX1VuaXRlZCAlPiUgDQogZHBseXI6OnJlbmFtZShJRCA9IGBJbmRpY2F0aXYgc3ViaWVjdGApICU+JSANCiBkcGx5cjo6c3VtbWFyaXNlKGNvdW50ID0gZHBseXI6Om5fZGlzdGluY3QoSUQpKQ0KDQpjYXQoIiMjIE51bWJlciBvZiBzdWJqZWN0cyBieSBUZWF0cnUvUHNpaG8iKQ0KRGF0YV9Vbml0ZWQgJT4lDQogZHBseXI6OnJlbmFtZShJRCA9IGBJbmRpY2F0aXYgc3ViaWVjdGApICU+JSANCiBncm91cF9ieShEYXRhc2V0KSAlPiUNCiBkcGx5cjo6c3VtbWFyaXNlKGNvdW50ID0gZHBseXI6Om5fZGlzdGluY3QoSUQpKQ0KYGBgDQoNCiMgT3V0Y29tZXMgUHJlLVBvc3QgSW50ZXJ2ZW50aW9uDQoNCmBgYHtyIG91dGNvbWVzX2Fwc19wcHMsIGZpZy53aWR0aD02LCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5oZWlnaHQ9NSwgcmVzdWx0cz0nYXNpcyd9DQojfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+ICAgT1VUQ09NRVMgUFJFLVBPU1QgSU5URVJWRU5USU9OICAgfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fg0KIyMgRnVuY3Rpb24gdG8gcnVuIGFsbCBmb3IgdGVhdHJ1LCBwc2lobyBhbmQgVW5pdGVkDQpmdW5jX3ByZXBvc3RfdG90IDwtIGZ1bmN0aW9uKGRmKXsNCiAgDQogIERhdGFfbWVsdCA8LQ0KICAgIGRmWywgYygiSW5kaWNhdGl2IHN1YmllY3QiLCAiR3J1cGEiLCAiTnVtZSBQcmVudW1lIiwgIkV0YXDEgywgemkiLCANCiAgICAgICAgICAgIkFQUyBwcmVfVG90YWwiLCAiQVBTIHBvc3RfVG90YWwiLCANCiAgICAgICAgICAgIlBQUyBwcmVfVG90YWwiLCAiUFBTIHBvc3RfVG90YWwiKV0gJT4lIA0KICAgIGdhdGhlcigiQVBTIHByZV9Ub3RhbCI6IlBQUyBwb3N0X1RvdGFsIiwga2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsdWUiKSAgJT4lIA0KICAgIG11dGF0ZV9hdCh2YXJzKGMoMTo1KSksIGZ1bnMoYXMuZmFjdG9yKSkgJT4lIA0KICAgIG11dGF0ZSh2YXJpYWJsZSA9IGZhY3Rvcih2YXJpYWJsZSwgbGV2ZWxzID0gYygiQVBTIHByZV9Ub3RhbCIsICJBUFMgcG9zdF9Ub3RhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUFBTIHByZV9Ub3RhbCIsICJQUFMgcG9zdF9Ub3RhbCIpKSkNCiAgDQogICMgQVBTIHQgdGVzdCAtIHBhaXJlZA0KICBhcHNfdHRlc3QgPC0gDQogICAgZGYgJT4lDQogICAgc2VsZWN0KGBJbmRpY2F0aXYgc3ViaWVjdGAsIGBBUFMgcHJlX1RvdGFsYCwgYEFQUyBwb3N0X1RvdGFsYCkgJT4lDQogICAgZ3JvdXBfYnkoYEluZGljYXRpdiBzdWJpZWN0YCkgJT4lDQogICAgc3VtbWFyaXNlX2FsbChmdW5zKG5hLm9taXQoLilbMV0pKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAjIHNxdWFzaCByb3dzIHdpdGggTkFzIHBlciBpZA0KICAgICMgZmlsdGVyX2FsbChhbGxfdmFycyghaXMubmEoLikpKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgIyBkcm9wIHJvdyBpZiBhbnkgY29sdW1uIGlzIE5BIC0tIGRvbnQgdXNlIGhlcmUNCiAgICBkbyhicm9vbTo6dGlkeSh0LnRlc3QoLiRgQVBTIHByZV9Ub3RhbGAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIC4kYEFQUyBwb3N0X1RvdGFsYCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbXUgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhbHQgPSAidHdvLnNpZGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFpcmVkID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUpKSkgDQogIGFwc190dGVzdF9vdXQgPC0ga25pdHI6OmthYmxlKGFwc190dGVzdCwgY2FwdGlvbiA9IHBhc3RlMChkZXBhcnNlKHN1YnN0aXR1dGUoZGYpKSwgIiBBUFMiKSwgZm9ybWF0ID0gJ3BhbmRvYycpDQogIA0KICAjIEFQUyBwbG90IHQgdGVzdCAtIHVucGFpcmVkDQogIGFwc19wbG90IDwtICAgDQogICAgRGF0YV9tZWx0ICU+JQ0KICAgIGZpbHRlcih2YXJpYWJsZSAlaW4lIGMoIkFQUyBwcmVfVG90YWwiLCAiQVBTIHBvc3RfVG90YWwiKSkgJT4lDQogICAgI2dyb3VwX2J5KGBFdGFwxIMsIHppYCkgJT4lDQogICAgZ2dwbG90KGFlcyh4ID0gdmFyaWFibGUsIHkgPSB2YWx1ZSkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgIGNvbG91ciA9ICJkYXJrcmVkIikgKw0KICAgIHhsYWIoIiIpICsNCiAgICBnZ3RpdGxlKGRlcGFyc2Uoc3Vic3RpdHV0ZShkZikpKSArDQogICAgI2ZhY2V0X3dyYXAofmBFdGFwxIMsIHppYCkgKw0KICAgIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLnNpZ25pZiIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIGF2b2lkIHNjaWVudGlmaWMgbm90YXRpb24gb2YgdmVyeSBzbWFsbCBwLXZhbHVlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNwYWlyZWQgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYXJpc29ucyA9IGxpc3QoYygiQVBTIHByZV9Ub3RhbCIsICJBUFMgcG9zdF9Ub3RhbCIpKSkgIA0KICANCiAgDQogICMgUFBTIHQgdGVzdCAtIHBhaXJlZA0KICBwcHNfdHRlc3QgPC0gDQogICAgZGYgJT4lDQogICAgc2VsZWN0KGBJbmRpY2F0aXYgc3ViaWVjdGAsIGBQUFMgcHJlX1RvdGFsYCwgYFBQUyBwb3N0X1RvdGFsYCkgJT4lDQogICAgZ3JvdXBfYnkoYEluZGljYXRpdiBzdWJpZWN0YCkgJT4lDQogICAgc3VtbWFyaXNlX2FsbChmdW5zKG5hLm9taXQoLilbMV0pKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAjIHNxdWFzaCByb3dzIHdpdGggTkFzIHBlciBpZA0KICAgICMgZmlsdGVyX2FsbChhbGxfdmFycyghaXMubmEoLikpKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgIyBkcm9wIHJvdyBpZiBhbnkgY29sdW1uIGlzIE5BIC0tIGRvbnQgdXNlIGhlcmUNCiAgICBkbyhicm9vbTo6dGlkeSh0LnRlc3QoLiRgUFBTIHByZV9Ub3RhbGAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIC4kYFBQUyBwb3N0X1RvdGFsYCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbXUgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhbHQgPSAidHdvLnNpZGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFpcmVkID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUpKSkNCiAgcHBzX3R0ZXN0X291dCA8LSBrbml0cjo6a2FibGUocHBzX3R0ZXN0LCBjYXB0aW9uID0gcGFzdGUwKGRlcGFyc2Uoc3Vic3RpdHV0ZShkZikpLCAiIFBQUyIpLCBmb3JtYXQgPSAncGFuZG9jJykgDQogIA0KICAjIFBQUyBwbG90IHQgdGVzdCAtIHVucGFpcmVkDQogIHBwc19wbG90IDwtDQogICAgRGF0YV9tZWx0ICU+JQ0KICAgIGZpbHRlcih2YXJpYWJsZSAlaW4lIGMoIlBQUyBwcmVfVG90YWwiLCAiUFBTIHBvc3RfVG90YWwiKSkgJT4lDQogICAgI2dyb3VwX2J5KGBFdGFwxIMsIHppYCkgJT4lDQogICAgZ2dwbG90KGFlcyh4ID0gdmFyaWFibGUsIHkgPSB2YWx1ZSkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgIGNvbG91ciA9ICJkYXJrcmVkIikgKw0KICAgIHhsYWIoIiIpICsNCiAgICBnZ3RpdGxlKGRlcGFyc2Uoc3Vic3RpdHV0ZShkZikpKSArDQogICAgI2ZhY2V0X3dyYXAofmBFdGFwxIMsIHppYCkgKw0KICAgIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLnNpZ25pZiIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIGF2b2lkIHNjaWVudGlmaWMgbm90YXRpb24gb2YgdmVyeSBzbWFsbCBwLXZhbHVlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNwYWlyZWQgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYXJpc29ucyA9IGxpc3QoYygiUFBTIHByZV9Ub3RhbCIsICJQUFMgcG9zdF9Ub3RhbCIpKSkgDQogIA0KICBwcmludChhcHNfdHRlc3Rfb3V0KQ0KICBwcmludChhcHNfcGxvdCkNCiAgcHJpbnQocHBzX3R0ZXN0X291dCkNCiAgcHJpbnQocHBzX3Bsb3QpDQp9DQoNCg0KY2F0KCIjIyMgVGVhdHJ1IikNCmZ1bmNfcHJlcG9zdF90b3QoRGF0YV90ZWF0cnUpDQpjYXQoIiMjIyBQc2lobyIpDQpmdW5jX3ByZXBvc3RfdG90KERhdGFfcHNpaG8pDQpjYXQoIiMjIyBVbml0ZWQiKQ0KZnVuY19wcmVwb3N0X3RvdChEYXRhX1VuaXRlZCkNCmBgYA0KDQoNCiMgUEFOQVMgKFBBTkFTIGZvciB6aTEsIHppMywgemk1LCB6aTcgYXJlIFByZTsgemkyLCB6aTQsIHppNiwgemk4IGFyZSBQb3N0ICkNCg0KYGBge3Igb3V0Y29tZV9wYW5hcywgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTksIHJlc3VsdHM9J2FzaXMnfQ0KI35+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fiAgIE9VVENPTUVTIFBSRS1QT1NUIGZvciBQQU5BUyAgICB+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+DQojIFBBTkFTIGZvciB6aTEsIHppMywgemk1LCB6aTcgYXJlIFByZTsgemkyLCB6aTQsIHppNiwgemk4IGFyZSBQb3N0IA0KIyMgRnVuY3Rpb24gcGxvdCBQQU5BUywgY29tcGFyZSBieSB6aQ0KcGxvdF9wYW5hc196aSA8LSBmdW5jdGlvbihkZiwgcHJlX3ZhciwgcG9zdF92YXIpew0KICBkZl9tb2RpZiA8LQ0KICAgIGRmICU+JQ0KICAgIGdhdGhlcihwcmVfdmFyLCBwb3N0X3Zhciwga2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsdWUiKSANCiAgDQogIHN0YXQudGVzdCA8LQ0KICAgIGRmX21vZGlmICU+JQ0KICAgIHNlbGVjdCh2YWx1ZSwgdmFyaWFibGUsIFppKSAlPiUNCiAgICB0aWR5cjo6ZHJvcF9uYSgpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgIyBuZWVkIHRvIHJlbW92ZSBOQXMgc28gZmFjdG9yIGxldmVsIG9mIGNvbmRpdGlvbiBjYW4gYmUgZHJvcGVkIGFzIHdlbGwgICAgICAgICANCiAgICByc3RhdGl4Ojp0X3Rlc3QodmFsdWUgfiBaaSkgJT4lICAgICAgICAgICAgICAgICMgYXV0b21hdGljYWxseSBkb2VzIHBhaXJ3aXNlLCBidXQgaGFzIHByb2JsZW1zIHdlcmUgZmFjdG9yIGxldmVscyBvZiBncm91cGluZyB3aXRoIE5BDQogICAgIyByc3RhdGl4OjphZGp1c3RfcHZhbHVlKCkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICByc3RhdGl4OjphZGRfc2lnbmlmaWNhbmNlKCJwIikgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgIGZpbHRlcihwLnNpZ25pZiAhPSAibnMiKSANCiAgDQogIHA8LQ0KICAgIGdncGxvdChkZl9tb2RpZiwgYWVzKHkgPSB2YWx1ZSwgeCA9IFppKSkgKw0KICAgIGdndGl0bGUocGFzdGUwKGRlcGFyc2Uoc3Vic3RpdHV0ZShkZikpLCAiIDogIiwgcHJlX3ZhciwgIiAtICIgLHBvc3RfdmFyKSkgKyANCiAgICBnZW9tX2JveHBsb3QoKSArIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2UsICBjb2xvdXIgPSAiZGFya3JlZCIpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQ0KICANCiAgaWYobnJvdyhzdGF0LnRlc3QpID4gMCl7DQogICAgcCA8LSANCiAgICAgIHAgKyBzdGF0X3B2YWx1ZV9tYW51YWwoc3RhdC50ZXN0LCBsYWJlbCA9ICJwLnNpZ25pZiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5LnBvc2l0aW9uID0gc2VxKG1heChkZl9tb2RpZiR2YWx1ZSwgbmEucm0gPSBUUlVFKSsyLCBtYXgoZGZfbW9kaWYkdmFsdWUsIG5hLnJtID0gVFJVRSkqMS40LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSBucm93KHN0YXQudGVzdCkpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB2ZXJ5IGhhY2t5DQogIH0NCiAgDQogIHJldHVybihzdGF0LnRlc3QpICU+JSBwcmludChuID0gSW5mKQ0KICBwcmludChwKQ0KfQ0KDQpjYXQoIiMjIyBUZWF0cnUiKQ0KcGxvdF9wYW5hc196aShEYXRhX3RlYXRydSwgIlBBIHByZV9Ub3RhbCIsICJQQSBwb3N0X1RvdGFsIikNCnBsb3RfcGFuYXNfemkoRGF0YV90ZWF0cnUsICJOQSBwcmVfVG90YWwiLCAiTkEgcG9zdF9Ub3RhbCIpDQpjYXQoIiMjIyBQc2lobyIpDQpwbG90X3BhbmFzX3ppKERhdGFfcHNpaG8sICJQQSBwcmVfVG90YWwiLCAiUEEgcG9zdF9Ub3RhbCIpDQpwbG90X3BhbmFzX3ppKERhdGFfcHNpaG8sICJOQSBwcmVfVG90YWwiLCAiTkEgcG9zdF9Ub3RhbCIpDQpjYXQoIiMjIyBVbml0ZWQiKQ0KcGxvdF9wYW5hc196aShEYXRhX1VuaXRlZCwgIlBBIHByZV9Ub3RhbCIsICJQQSBwb3N0X1RvdGFsIikNCnBsb3RfcGFuYXNfemkoRGF0YV9Vbml0ZWQsICJOQSBwcmVfVG90YWwiLCAiTkEgcG9zdF9Ub3RhbCIpDQpgYGANCg0KDQojIElPUyAoUHJlIG9uIGJlZ2luaW5nIG9mIEV0YXBhLCBQb3N0IG9uIGVuZCBvZiBFdGFwYSkNCg0KYGBge3IgaW9zLCBmaWcud2lkdGg9MTMsIGZpZy5oZWlnaHQ9MTEsIHJlc3VsdHM9J2FzaXMnfQ0KI35+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fiAgIElPUyAtIHByZSBvbiBiZWdpbmluZyBvZiBldGFwYSwgcG9zdCBvbiBlbmQgIH5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn4NCiMjIEZ1bmN0aW9uIHBsb3QgcHJlIHBvc3QgZGF0YSBmYWNldGVkIGJ5IHppDQpwbG90X3ByZXBvc3RfemkgPC0gZnVuY3Rpb24oZGYsIHByZV92YXIsIHBvc3RfdmFyKXsNCiAgZGYgJT4lDQogICAgZ2F0aGVyKHByZV92YXIsIHBvc3RfdmFyLCBrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWx1ZSIpICU+JQ0KICAgIG11dGF0ZSh2YXJpYWJsZSA9IGZhY3Rvcih2YXJpYWJsZSwgbGV2ZWxzID0gYyhwcmVfdmFyLCBwb3N0X3ZhcikpKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKHkgPSB2YWx1ZSwgeCA9IHZhcmlhYmxlKSkgKw0KICAgIGZhY2V0X3dyYXAoflppLCBucm93ID0gMSkgKyANCiAgICBnZ3RpdGxlKGRlcGFyc2Uoc3Vic3RpdHV0ZShkZikpKSArIA0KICAgIGdlb21fYm94cGxvdCgpICsgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgIGNvbG91ciA9ICJkYXJrcmVkIikgICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogICAgZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gInQudGVzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAicC5zaWduaWYiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0byBhdm9pZCBzY2llbnRpZmljIG5vdGF0aW9uIG9mIHZlcnkgc21hbGwgcC12YWx1ZXMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjcGFpcmVkID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYXJpc29ucyA9IGxpc3QoYyhwcmVfdmFyLCBwb3N0X3ZhcikpKQ0KfQ0KDQoNCiMjIEZ1bmN0aW9uIHBsb3QgcHJlIHBvc3QgZGF0YSBhZGp1c3RlZCBjb21wYXJpc29ucyBvZiBhbGwgDQpwbG90X3ByZXBvc3RfemkyIDwtIGZ1bmN0aW9uKGRmLCBwcmVfdmFyLCBwb3N0X3Zhcil7DQogIGRmX21vZGlmIDwtIA0KICAgIGRmICU+JQ0KICAgIGdhdGhlcihwcmVfdmFyLCBwb3N0X3Zhciwga2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsdWUiKSAlPiUNCiAgICBtdXRhdGUodmFyaWFibGUgPSBmYWN0b3IodmFyaWFibGUsIGxldmVscyA9IGMocHJlX3ZhciwgcG9zdF92YXIpKSwNCiAgICAgICAgICAgY29uZGl0aW9uID0gaW50ZXJhY3Rpb24odmFyaWFibGUsIFppKSwNCiAgICAgICAgICAgY29uZGl0aW9uID0gYXMuZmFjdG9yKGNvbmRpdGlvbikpIA0KICANCiAgc3RhdC50ZXN0IDwtDQogICAgZGZfbW9kaWYgJT4lDQogICAgIyBncm91cF9ieShgSW5kaWNhdGl2IHN1YmllY3RgKSAlPiUNCiAgICByc3RhdGl4Ojp0X3Rlc3QodmFsdWUgfiBjb25kaXRpb24pICU+JQ0KICAgICMgcnN0YXRpeDo6YWRqdXN0X3B2YWx1ZSgpICU+JQ0KICAgIHJzdGF0aXg6OmFkZF9zaWduaWZpY2FuY2UoInAiKSAlPiUNCiAgICBmaWx0ZXIocC5zaWduaWYgIT0gIm5zIikgDQogIA0KICBwIDwtIA0KICAgIGdncGxvdChkZl9tb2RpZiwgYWVzKHkgPSB2YWx1ZSwgeCA9IGNvbmRpdGlvbikpICsNCiAgICAgIGdndGl0bGUoZGVwYXJzZShzdWJzdGl0dXRlKGRmKSkpICsgDQogICAgICBnZW9tX2JveHBsb3QoKSArIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2UsICBjb2xvdXIgPSAiZGFya3JlZCIpICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsNCiAgICAgIHN0YXRfcHZhbHVlX21hbnVhbChzdGF0LnRlc3QsIGxhYmVsID0gInAuc2lnbmlmIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgeS5wb3NpdGlvbiA9IHNlcShtYXgoZGZfbW9kaWYkdmFsdWUsIG5hLnJtID0gVFJVRSkrMiwgbWF4KGRmX21vZGlmJHZhbHVlLCBuYS5ybSA9IFRSVUUpKjIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IG5yb3coc3RhdC50ZXN0KSkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHZlcnkgaGFja3kNCiAgDQogIHByaW50KHApDQogIHJldHVybihzdGF0LnRlc3QpICU+JSBwcmludChuID0gSW5mKQ0KICANCn0NCg0KDQojIGNhdCgiIyMgQnkgWmkiKQ0KY2F0KCIjIyMgVGVhdHJ1IikNCnBsb3RfcHJlcG9zdF96aShEYXRhX3RlYXRydSwgIklPUyBwcmUiLCAiSU9TIHBvc3QiKQ0KcGxvdF9wcmVwb3N0X3ppMihEYXRhX3RlYXRydSwgIklPUyBwcmUiLCAiSU9TIHBvc3QiKQ0KY2F0KCIjIyMgUHNpaG8iKQ0KcGxvdF9wcmVwb3N0X3ppKERhdGFfcHNpaG8sICJJT1MgcHJlIiwgIklPUyBwb3N0IikNCnBsb3RfcHJlcG9zdF96aTIoRGF0YV9wc2lobywgIklPUyBwcmUiLCAiSU9TIHBvc3QiKQ0KY2F0KCIjIyMgVW5pdGVkIikNCnBsb3RfcHJlcG9zdF96aShEYXRhX1VuaXRlZCwgIklPUyBwcmUiLCAiSU9TIHBvc3QiKQ0KcGxvdF9wcmVwb3N0X3ppMihEYXRhX1VuaXRlZCwgIklPUyBwcmUiLCAiSU9TIHBvc3QiKQ0KDQoNCmNhdCgiIyMgQU5PVkEgZm9yIEJhc2VsaW5lIGVhY2ggRXRhcGEiKQ0KcGxvdF9hbm92YV9iYXNlIDwtIGZ1bmN0aW9uKGRmLCBwcmVfdmFyLCBwb3N0X3Zhcil7DQogIGRmICU+JQ0KICAgIHNlbGVjdChFdGFwYSwgWmksIHByZV92YXIsIHBvc3RfdmFyKSAlPiUNCiAgICBnYXRoZXIocHJlX3ZhciwgcG9zdF92YXIsIGtleSA9ICJ2YXJpYWJsZSIsIHZhbHVlID0gInZhbHVlIikgJT4lDQogICAgbXV0YXRlKHZhcmlhYmxlID0gZmFjdG9yKHZhcmlhYmxlLCBsZXZlbHMgPSBjKHByZV92YXIsIHBvc3RfdmFyKSkpICU+JQ0KICAgIGZpbHRlcih2YXJpYWJsZSA9PSBwcmVfdmFyKSAgJT4lDQogICAgICANCiAgICBqbXY6OkFOT1ZBKGZvcm11bGEgPSB2YWx1ZSB+IFppLA0KICAgICAgICAgICAgICAgZWZmZWN0U2l6ZSA9IGxpc3QoJ2V0YScsICdwYXJ0RXRhJykpIC0+IGFub3ZhX2Jhc2UNCiAgICANCiAgICBwcmludCh0aWJibGU6OmFzLnRpYmJsZShhbm92YV9iYXNlJG1haW4pKQ0KfQ0KcGxvdF9hbm92YV9iYXNlKERhdGFfVW5pdGVkLCAiSU9TIHByZSIsICJJT1MgcG9zdCIpDQoNCg0KY2F0KCIjIyBCeSBFdGFwYSIpDQpwbG90X3ByZXBvc3RfZXRhcGEgPC0gZnVuY3Rpb24oZGYsIHByZV92YXIsIHBvc3RfdmFyKXsNCiAgZGZfbW9kaWYgPC0NCiAgICBkZiAlPiUNCiAgICAgIHNlbGVjdChFdGFwYSwgWmksIHByZV92YXIsIHBvc3RfdmFyKSAlPiUNCiAgICAgIGdhdGhlcihwcmVfdmFyLCBwb3N0X3Zhciwga2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsdWUiKSAlPiUNCiAgICAgIG11dGF0ZSh2YXJpYWJsZSA9IGZhY3Rvcih2YXJpYWJsZSwgbGV2ZWxzID0gYyhwcmVfdmFyLCBwb3N0X3ZhcikpKSAlPiUNCiAgICAgIGZpbHRlcih2YXJpYWJsZSA9PSBwcmVfdmFyICYgWmkgJWluJSBzcHJpbnRmKCJ6aSVkIiwgc2VxKDEsIDgsIGJ5ID0gMikpICB8DQogICAgICAgICAgICAgdmFyaWFibGUgPT0gcG9zdF92YXIgJiBaaSAlaW4lIHNwcmludGYoInppJWQiLCBzZXEoMiwgOCwgYnkgPSAyKSkpICANCiAgDQogIHN0YXQudGVzdCA8LSANCiAgICBkZl9tb2RpZiAlPiUgICAgIA0KICAgICAgZ3JvdXBfYnkoRXRhcGEpICU+JQ0KICAgICAgcnN0YXRpeDo6dF90ZXN0KHZhbHVlIH4gWmkpICU+JQ0KICAgICAgIyByc3RhdGl4OjphZGp1c3RfcHZhbHVlKCkgJT4lDQogICAgICByc3RhdGl4OjphZGRfc2lnbmlmaWNhbmNlKCJwIikgJT4lDQogICAgICBmaWx0ZXIocC5zaWduaWYgIT0gIm5zIikNCiAgDQogIHAgPC0gDQogICAgZ2dwbG90KGRhdGEgPSBkZl9tb2RpZiwgYWVzKHkgPSB2YWx1ZSwgeCA9IHZhcmlhYmxlKSkgKw0KICAgIGZhY2V0X3dyYXAofkV0YXBhLCBucm93ID0gMSkgKyANCiAgICBnZ3RpdGxlKGRlcGFyc2Uoc3Vic3RpdHV0ZShkZikpKSArIA0KICAgIGdlb21fYm94cGxvdCgpICsgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgIGNvbG91ciA9ICJkYXJrcmVkIikgICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogICAgZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gInQudGVzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAicC5zaWduaWYiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0byBhdm9pZCBzY2llbnRpZmljIG5vdGF0aW9uIG9mIHZlcnkgc21hbGwgcC12YWx1ZXMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjcGFpcmVkID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYXJpc29ucyA9IGxpc3QoYyhwcmVfdmFyLCBwb3N0X3ZhcikpKQ0KDQogIHByaW50KHApDQogIHJldHVybihzdGF0LnRlc3QpICU+JSBwcmludChuID0gSW5mKQ0KfQ0KcGxvdF9wcmVwb3N0X2V0YXBhKERhdGFfdGVhdHJ1LCAiSU9TIHByZSIsICJJT1MgcG9zdCIpDQoNCg0KYGBgDQoNCg0KIyBWQVMgdmFyaWFibGVzIChtdWx0aXBsZSBtZWFzdXJlbWVudHMgb24gZWFjaCB6aSkNCg0KYGBge3IgdmFzX3ZhcmlhYmxlcywgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTExLCByZXN1bHRzPSdhc2lzJ30NCiN+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn4gICBWYXJpYWJsZXMgbXVsdGlwbGUgbWVhc3VyZW1lbnRzIGluIHppICAgIH5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+DQojIyBGdW5jdGlvbiBwbG90IHByZS1leDEtZXgyLS4uLXBvc3QgZGF0YSBmYWNldGVkIGJ5IHppDQpwbG90X3ByZWV4cG9zdF96aSA8LSBmdW5jdGlvbihkZiwgcHJlX3ZhciwgcG9zdF94MV92YXIsIHBvc3RfeDJfdmFyLCBwb3N0X3gzX3Zhcil7DQogIGRmX21vZGlmIDwtDQogICAgZGYgJT4lDQogICAgZ2F0aGVyKHByZV92YXIsIHBvc3RfeDFfdmFyLCBwb3N0X3gyX3ZhciwgcG9zdF94M192YXIsIGtleSA9ICJ2YXJpYWJsZSIsIHZhbHVlID0gInZhbHVlIikgJT4lDQogICAgbXV0YXRlKHZhcmlhYmxlID0gZmFjdG9yKHZhcmlhYmxlLCBsZXZlbHMgPSBjKHByZV92YXIsIHBvc3RfeDFfdmFyLCBwb3N0X3gyX3ZhciwgcG9zdF94M192YXIpKSkNCiAgICANCiAgc3RhdC50ZXN0IDwtDQogICAgZGZfbW9kaWYgJT4lDQogICAgZ3JvdXBfYnkoWmkpICU+JQ0KICAgIHRpZHlyOjpkcm9wX25hKHZhbHVlKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmaWx0ZXIgc28gZ3JvdXBpbmcgZmFjdG9yIGxldmVscyBnZXQgZHJvcGVkIGFuZCB3ZSBjYW4gY29tcGFyZSB3aXRoIHVuZXZlbCBsZXZlbHMNCiAgICByc3RhdGl4Ojp0X3Rlc3QodmFsdWUgfiB2YXJpYWJsZSkgJT4lICAgICAgICAgICAgICAgICMgcGFpcndpc2UNCiAgICByc3RhdGl4OjphZGRfc2lnbmlmaWNhbmNlKCJwIikgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgZmlsdGVyKHAuc2lnbmlmICE9ICJucyIpIA0KICANCiAgcDwtDQogICAgZ2dwbG90KGRmX21vZGlmLCBhZXMoeSA9IHZhbHVlLCB4ID0gdmFyaWFibGUpKSArDQogICAgICBmYWNldF93cmFwKH5aaSwgbnJvdyA9IDEpICsgDQogICAgICBnZ3RpdGxlKGRlcGFyc2Uoc3Vic3RpdHV0ZShkZikpKSArIA0KICAgICAgZ2VvbV9ib3hwbG90KCkgKyBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCAgY29sb3VyID0gImRhcmtyZWQiKSArDQogICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogICAgICBzdGF0X3B2YWx1ZV9tYW51YWwoc3RhdC50ZXN0LCBsYWJlbCA9ICJwLnNpZ25pZiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHkucG9zaXRpb24gPSBzZXEobWF4KGRmX21vZGlmJHZhbHVlLCBuYS5ybSA9IFRSVUUpKzIsIG1heChkZl9tb2RpZiR2YWx1ZSwgbmEucm0gPSBUUlVFKSoyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSBucm93KHN0YXQudGVzdCkpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB2ZXJ5IGhhY2t5DQogIA0KICByZXR1cm4oc3RhdC50ZXN0KSAlPiUgcHJpbnQobiA9IEluZikNCiAgcA0KfQ0KDQpjYXQoIiMjIEJ5IFppIikNCmNhdCgiIyMgVkFTIHN0cmVzIikNCmNhdCgiIyMjIFRlYXRydSIpDQpwbG90X3ByZWV4cG9zdF96aShEYXRhX3RlYXRydSwgIlZBUyBzdHJlcyBwcmUiLCAiVkFTIHN0cmVzIHBvc3QgZXgxIiwgIlZBUyBzdHJlcyBwb3N0IGV4MiIsICJWQVMgc3RyZXMgcG9zdCBleDMiKQ0KY2F0KCIjIyMgUHNpaG8iKQ0KcGxvdF9wcmVleHBvc3RfemkoRGF0YV9wc2lobywgIlZBUyBzdHJlcyBwcmUiLCAiVkFTIHN0cmVzIHBvc3QgZXgxIiwgIlZBUyBzdHJlcyBwb3N0IGV4MiIsICJWQVMgc3RyZXMgcG9zdCBleDMiKQ0KY2F0KCIjIyMgVW5pdGVkIikNCnBsb3RfcHJlZXhwb3N0X3ppKERhdGFfVW5pdGVkLCAiVkFTIHN0cmVzIHByZSIsICJWQVMgc3RyZXMgcG9zdCBleDEiLCAiVkFTIHN0cmVzIHBvc3QgZXgyIiwgIlZBUyBzdHJlcyBwb3N0IGV4MyIpDQoNCg0KY2F0KCIjIyBWQVMgc3RhcmUgZGUgYmluZSIpIA0KY2F0KCIjIyMgVGVhdHJ1IikNCnBsb3RfcHJlZXhwb3N0X3ppKERhdGFfdGVhdHJ1LCAiVkFTIHN0YXJlIGRlIGJpbmUgcHJlIiwgIlZBUyBzdGFyZSBkZSBiaW5lIHBvc3QgZXgxIiwgIlZBUyBzdGFyZSBkZSBiaW5lIHBvc3QgZXgyIiwgIlZBUyBzdGFyZSBkZSBiaW5lIHBvc3QgZXgzIikNCmNhdCgiIyMjIFBzaWhvIikNCnBsb3RfcHJlZXhwb3N0X3ppKERhdGFfcHNpaG8sICJWQVMgc3RhcmUgZGUgYmluZSBwcmUiLCAiVkFTIHN0YXJlIGRlIGJpbmUgcG9zdCBleDEiLCAiVkFTIHN0YXJlIGRlIGJpbmUgcG9zdCBleDIiLCAiVkFTIHN0YXJlIGRlIGJpbmUgcG9zdCBleDMiKQ0KY2F0KCIjIyMgVW5pdGVkIikNCnBsb3RfcHJlZXhwb3N0X3ppKERhdGFfVW5pdGVkLCAiVkFTIHN0YXJlIGRlIGJpbmUgcHJlIiwgIlZBUyBzdGFyZSBkZSBiaW5lIHBvc3QgZXgxIiwgIlZBUyBzdGFyZSBkZSBiaW5lIHBvc3QgZXgyIiwgIlZBUyBzdGFyZSBkZSBiaW5lIHBvc3QgZXgzIikNCg0KDQpjYXQoIiMjIFZBUyBjb3JwIikNCmNhdCgiIyMjIFRlYXRydSIpDQpwbG90X3ByZWV4cG9zdF96aShEYXRhX3RlYXRydSwgIlZBUyBjb3JwIHByZSIsICJWQVMgY29ycCBwb3N0IGV4MSIsICJWQVMgY29ycCBwb3N0IGV4MiIsICJWQVMgY29ycCBwb3N0IGV4MyIpDQpjYXQoIiMjIyBQc2lobyIpDQpwbG90X3ByZWV4cG9zdF96aShEYXRhX3BzaWhvLCAiVkFTIGNvcnAgcHJlIiwgIlZBUyBjb3JwIHBvc3QgZXgxIiwgIlZBUyBjb3JwIHBvc3QgZXgyIiwgIlZBUyBjb3JwIHBvc3QgZXgzIikNCmNhdCgiIyMjIFVuaXRlZCIpDQpwbG90X3ByZWV4cG9zdF96aShEYXRhX1VuaXRlZCwgIlZBUyBjb3JwIHByZSIsICJWQVMgY29ycCBwb3N0IGV4MSIsICJWQVMgY29ycCBwb3N0IGV4MiIsICJWQVMgY29ycCBwb3N0IGV4MyIpDQoNCg0KDQoNCnBsb3RfcHJlZXhwb3N0X2V0YXBhIDwtIGZ1bmN0aW9uKGRmLCBwcmVfdmFyLCBwb3N0X3gxX3ZhciwgcG9zdF94Ml92YXIsIHBvc3RfeDNfdmFyKXsNCiAgZGZfbW9kaWYgPC0NCiAgICBkZiAlPiUNCiAgICBnYXRoZXIocHJlX3ZhciwgcG9zdF94MV92YXIsIHBvc3RfeDJfdmFyLCBwb3N0X3gzX3Zhciwga2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsdWUiKSAlPiUNCiAgICBmaWx0ZXIoKHZhcmlhYmxlID09IHByZV92YXIgJiBaaSAlaW4lIHNwcmludGYoInppJWQiLCBzZXEoMSwgOCwgYnkgPSAyKSkpICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBwcmUgIA0KICAgICAgICAgICAodmFyaWFibGUgPT0gcG9zdF94MV92YXIgJiBaaSAlaW4lIGMoInppMiIsICJ6aTgiKSkgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHBvc3QgdmFyaWF0aW9ucw0KICAgICAgICAgICAodmFyaWFibGUgPT0gcG9zdF94Ml92YXIgJiBaaSA9PSAiemk0IikgIHwNCiAgICAgICAgICAgKHZhcmlhYmxlID09IHBvc3RfeDNfdmFyICYgWmkgPT0gInppNiIpKSAgJT4lDQogICAgbXV0YXRlKHZhcmlhYmxlID0gY2FzZV93aGVuKHN0cmluZ3I6OnN0cl9kZXRlY3QodmFyaWFibGUsICJwcmUiKSB+ICJwcmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KHZhcmlhYmxlLCAicG9zdCIpIH4gInBvc3QiKSkgJT4lDQogICAgbXV0YXRlKHZhcmlhYmxlID0gZmFjdG9yKHZhcmlhYmxlLCBsZXZlbHMgPSBjKCJwcmUiLCAicG9zdCIpKSkNCg0KICBzdGF0LnRlc3QgPC0NCiAgICBkZl9tb2RpZiAlPiUNCiAgICBncm91cF9ieShFdGFwYSkgJT4lDQogICAgdGlkeXI6OmRyb3BfbmEodmFsdWUpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZpbHRlciBzbyBncm91cGluZyBmYWN0b3IgbGV2ZWxzIGdldCBkcm9wZWQgYW5kIHdlIGNhbiBjb21wYXJlIHdpdGggdW5ldmVsIGxldmVscw0KICAgIHJzdGF0aXg6OnRfdGVzdCh2YWx1ZSB+IHZhcmlhYmxlKSAlPiUgICAgICAgICAgICAgICAgIyBwYWlyd2lzZQ0KICAgIHJzdGF0aXg6OmFkZF9zaWduaWZpY2FuY2UoInAiKSAlPiUNCiAgICBmaWx0ZXIocC5zaWduaWYgIT0gIm5zIikNCg0KICBwPC0NCiAgICBnZ3Bsb3QoZGZfbW9kaWYsIGFlcyh5ID0gdmFsdWUsIHggPSB2YXJpYWJsZSkpICsNCiAgICAgIGZhY2V0X3dyYXAofkV0YXBhLCBucm93ID0gMSkgKw0KICAgICAgZ2d0aXRsZShkZXBhcnNlKHN1YnN0aXR1dGUoZGYpKSkgKw0KICAgICAgZ2VvbV9ib3hwbG90KCkgKyBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCAgY29sb3VyID0gImRhcmtyZWQiKSArDQogICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogICAgICBzdGF0X3B2YWx1ZV9tYW51YWwoc3RhdC50ZXN0LCBsYWJlbCA9ICJwLnNpZ25pZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgeS5wb3NpdGlvbiA9IHNlcShtYXgoZGZfbW9kaWYkdmFsdWUsIG5hLnJtID0gVFJVRSkrMiwgbWF4KGRmX21vZGlmJHZhbHVlLCBuYS5ybSA9IFRSVUUpKjIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gbnJvdyhzdGF0LnRlc3QpKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdmVyeSBoYWNreQ0KDQogIHByaW50KHApDQogIHJldHVybihzdGF0LnRlc3QpICU+JSBwcmludChuID0gSW5mKQ0KfQ0KDQpjYXQoIiMjIEJ5IEV0YXBhIC0gT25seSBVbml0ZWQiKQ0KY2F0KCIjIyMgVkFTIFN0cmVzIikNCnBsb3RfcHJlZXhwb3N0X2V0YXBhKERhdGFfVW5pdGVkLCAiVkFTIHN0cmVzIHByZSIsICJWQVMgc3RyZXMgcG9zdCBleDEiLCAiVkFTIHN0cmVzIHBvc3QgZXgyIiwgIlZBUyBzdHJlcyBwb3N0IGV4MyIpDQpjYXQoIiMjIyBWQVMgU3QgYmluZSIpDQpwbG90X3ByZWV4cG9zdF9ldGFwYShEYXRhX1VuaXRlZCwgIlZBUyBzdGFyZSBkZSBiaW5lIHByZSIsICJWQVMgc3RhcmUgZGUgYmluZSBwb3N0IGV4MSIsICJWQVMgc3RhcmUgZGUgYmluZSBwb3N0IGV4MiIsICJWQVMgc3RhcmUgZGUgYmluZSBwb3N0IGV4MyIpDQpjYXQoIiMjIyBWQVMgU3QgYmluZSBjb3JwIikNCnBsb3RfcHJlZXhwb3N0X2V0YXBhKERhdGFfVW5pdGVkLCAiVkFTIGNvcnAgcHJlIiwgIlZBUyBjb3JwIHBvc3QgZXgxIiwgIlZBUyBjb3JwIHBvc3QgZXgyIiwgIlZBUyBjb3JwIHBvc3QgZXgzIikNCmBgYA0KDQoNCiMgRGVmaW5lIGZ1bmN0aW9uIE1vZGVyYXRpb24NCg0KYGBge3IgZGVmX2Z1bl9maW5kX21vZH0NCmZpbmRfbW9kIDwtIGZ1bmN0aW9uKGRmLCBkZnAgPSBOVUxMLCBudW1fb25seSA9IFRSVUUsIHZlcmJvc2UgPSBUUlVFKSB7DQogIGNvdW50ID0gMA0KICBtb2RlcmF0aW9uX21vZGVsX2xpc3QgPDwtIGxpc3QoKQ0KDQogIGlmKG51bV9vbmx5ID09IFRSVUUpew0KICBudW1lcmljX2NvbHMgPC0gdW5saXN0KGxhcHBseShkZiwgaXMubnVtZXJpYykpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGdldCBvbmx5IG51bWVyaWMgY29sdW1ucw0KICBkZiA8LSBkZlssIG51bWVyaWNfY29sc10NCiAgfQ0KICANCiAgIyByZXN0cmljdGVkIHBlcm11dGF0aW9ucyBmb3IgTW9kZXJhdGlvbiAtIENoZWNrOiBjaG9vc2UobGVuX25hbWVzLCAzKSozDQogIG5hbWVzIDwtIGNvbG5hbWVzKGRmKQ0KICBsZW5fbmFtZXMgPSBsZW5ndGgobmFtZXMpDQogIA0KICBpZihpcy5udWxsKGRmcCkpew0KICAgIGRmcCA8LSBsYXBwbHkoMTpsZW5fbmFtZXMsIGZ1bmN0aW9uKGkpew0KICAgICAgdG1wIDwtIGxhcHBseSgxOihsZW5fbmFtZXMtMSksIGZ1bmN0aW9uKGopew0KICAgICAgICB0bXAgPC0gbGFwcGx5KChqKzEpOmxlbl9uYW1lcywgZnVuY3Rpb24oayl7DQogICAgICAgICAgaWYoaiAhPSBpICYgayAhPSBpKSBjKG5hbWVzW2ldLCBuYW1lc1tqXSwgbmFtZXNba10pDQogICAgICAgIH0pDQogICAgICAgIGRvLmNhbGwocmJpbmQsIHRtcCkNCiAgICAgIH0pDQogICAgICBkby5jYWxsKHJiaW5kLCB0bXApDQogICAgfSkNCiAgICBkZnAgPC0gZG8uY2FsbChyYmluZC5kYXRhLmZyYW1lLCBkZnApDQogICAgbmFtZXMoZGZwKSA8LSBwYXN0ZSgidmFyIiwgMTozLCBzZXAgPSAiXyIpDQogICAgZGZwWywgXSA8LSBsYXBwbHkoZGZwWywgXSwgYXMuY2hhcmFjdGVyKSAgICANCiAgfSBlbHNlIHsNCiAgICBkZnAgPC0gZGZwDQogIH0NCiAgDQogIGZvciAocm93IGluIDE6bnJvdyhkZnApKSB7ICAgICAgICAgICAgICAgIA0KICAgIA0KICAgIHJlc3VsdHMgPC0gbWVkbW9kOjptb2QoZGF0YSA9IGRmLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbW9kIGRvZXMgY2VudGVyaW5nIGF1dG9tYXRpY2FsbHkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGVwID0gZGZwW3JvdywgMV0sIG1vZCA9IGRmcFtyb3csIDJdLCBwcmVkID0gZGZwW3JvdywgM10sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBlc3RNZXRob2QgPSAic3RhbmRhcmQiLCB0ZXN0ID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHNpbXBsZVNsb3BlRXN0ID0gRkFMU0UsIHNpbXBsZVNsb3BlUGxvdCA9IEZBTFNFKSAgICAgICAgICAgICAjIHdoZW4gdGVzdGluZyB1c2UgZXN0TWV0aG9kID0gJ2Jvb3RzdHJhcCcsIGJvb3RzdHJhcCA9IDUwMCANCiAgICANCiAgICBwbW9kIDwtIGFzLmRhdGEuZnJhbWUocmVzdWx0cyRtb2QpWzMsNV0NCg0KICAgIGlmKHBtb2QgPCAwLjA1ICYmICFpcy5uYShwbW9kKSkgew0KICAgICAgY291bnQgPC0gY291bnQgKyAxDQogICAgICAgIGlmKHZlcmJvc2UgPT0gVFJVRSkgew0KICAgICAgICBjYXQoIkRlcGVuZGVudCBWYXJpYWJsZToiLCBkZnBbcm93LCAxXSkNCiAgICAgICAgcHJpbnQocmVzdWx0cyRtb2QpIA0KICAgICAgICB9DQogICAgICBtb2RlcmF0aW9uX21vZGVsX2xpc3RbWyJNb2RlbCJdXVtbcGFzdGUoIm1vZGVsIiwgY291bnQsIHNlcCA9ICJfIildXSA8PC0gYXMuZGF0YS5mcmFtZShyZXN1bHRzJG1vZCkgICAjIHJldHVybiBhcyBsaXN0IG9mIGRhdGFmcmFtZXMNCiAgICAgIG1vZGVyYXRpb25fbW9kZWxfbGlzdFtbIlN5bnRheCJdXVtbcGFzdGUoIm1vZGVsIiwgY291bnQsIHNlcCA9ICJfIildXSA8PC0gcmVzdWx0cyRtb2RlbFN5bnRheA0KICAgICAgDQogICAgfQ0KICB9DQogIGNhdCgiXG4iLCJSZXBvcnQ6ICIsIGNvdW50LCAic2lnbmlmaWNhbnQgbW9kZXJhdGlvbnMgb3V0IG9mIiwgcm93LCAidG90YWwgdHJpZXMuIikNCn0NCg0KIyMgZXggZGZwDQojIGRmcCA8LSBkYXRhLmZyYW1lKA0KIyAgIHZhcjEgPSBjb2xuYW1lcyhEYXRhX21lZF9tZWx0KVtncmVwKCJfcG9zdCIsIGNvbG5hbWVzKERhdGFfbWVkX21lbHQpKV0sDQojICAgdmFyMiA9IHJlcCgiQ29uZGl0aW9uIiwgMTMpLA0KIyAgIHZhcjMgPSBjb2xuYW1lcyhEYXRhX21lZF9tZWx0KVtncmVwKCJfcHJlIiwgY29sbmFtZXMoRGF0YV9tZWRfbWVsdCkpXSwNCiMgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiMgKQ0KIyBmaW5kX21lZChkZiA9IERhdGFfbWVkX21lbHQsIGRmcCA9IGRmcCwgbnVtX29ubHkgPSBGQUxTRSkgDQoNCmBgYA0KDQojIE1vZGVyYXRpb24gb24gUHJlUG9zdCAoQVBTLCBQU1MpDQoNCmBgYHtyIG1vZF9maW5kLCByZXN1bHRzPSdhc2lzJywgZWNobz1GQUxTRX0NCiMgTWFrZSBkYXRhZnJhbWUgb2YgdmFycyBhbmQgbW9kcw0KbW9kcyA8LSBjKCJJT1MgcG9zdCIsICJTUlMgcG9zdF9SZWxhPFUrMDIxQj5pYSIsICJTUlMgcG9zdF9HbG9iYWwiKSAgIyBkZWZpbmUgbW9kZXJhdG9ycw0KcHJlcyA8LSBjKCJQUFMgcHJlX1RvdGFsIiwgIkFQUyBwcmVfVG90YWwiKQ0KcG9zdHMgPC0gYygiUFBTIHBvc3RfVG90YWwiLCAiQVBTIHBvc3RfVG90YWwiKSAgIA0KZGZwIDwtIG1lcmdlKG1vZHMsIGNiaW5kKHByZXMsIHBvc3RzKSwgYnkgPSBOVUxMKSAgIyBieSA9IE5VTEwgY2F1c2VzIG1lcmdlIHRvIGRvIHNpbXBsZSBjb21iaW5hdG9yaWFsIGRhdGEgcmVwbGljYXRpb24NCmRmcCA8LSBkZnBbLCBjKCJwb3N0cyIsICJ4IiwgInByZXMiKV0gICAgICMgcmVvcmRlciBjb2x1bW5zIGZvciBmaW5kX21vZA0KZGZwIDwtIGRhdGEuZnJhbWUobGFwcGx5KGRmcCwgYXMuY2hhcmFjdGVyKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIERhdGEgLSBTdW1tYXJpemluZyBnaXZlcyBtZWFucyBmb3IgcmVwZWF0ZWQgbW9kZXJhdG9ycyB3aGlsZSBwcmVwb3N0IG91dGNvbWVzIGFyZSBzdGlsbCBzYW1lIHZhbHVlcw0KbW9kX3ByZXBvc3QgPC0gDQogIERhdGFfVW5pdGVkICU+JQ0KICBzZWxlY3QoIkluZGljYXRpdiBzdWJpZWN0IiwgIkV0YXBhIiwgIlppIiwgbW9kcywgIHByZXMsIHBvc3RzKSAlPiUNCiAgZ3JvdXBfYnkoYEluZGljYXRpdiBzdWJpZWN0YCkgJT4lDQogIHN1bW1hcmlzZV9pZihpcy5udW1lcmljLCBtZWFuLCBuYS5ybSA9IFRSVUUpICU+JSAgICAgIyB0aGlzIGNyZWF0ZXMgTmFOcyB3ZXJlIHRoZXJlIHdlcmUgTkFzDQogIG11dGF0ZV9hbGwofmlmZWxzZShpcy5uYW4oLiksIE5BLCAuKSkgICAgDQogIA0KIyBGb3IgUHJlUG9zdCBPdXRjb21lIChub3QgbWVhc3VyZWQgZm9yIEV0YXBlKQ0KZmluZF9tb2QoZGYgPSBtb2RfcHJlcG9zdCwgZGZwID0gZGZwLCBudW1fb25seSA9IEZBTFNFKSAgICAjIGZvdW5kIDEgbW9kZXJhdGlvbg0KDQoNCmBgYA0KDQoNCmBgYHtyIG1vZDFzcnMsIHJlc3VsdHM9J2FzaXMnfQ0KbW9kMSA8LSANCiAgbWVkbW9kOjptb2QoZGF0YSA9IG1vZF9wcmVwb3N0LA0KICAgICAgICAgICAgICBkZXAgPSAiUFBTIHBvc3RfVG90YWwiLCBtb2QgPSAiU1JTIHBvc3RfUmVsYTxVKzAyMUI+aWEiLCBwcmVkID0gIlBQUyBwcmVfVG90YWwiLA0KICAgICAgICAgICAgICBlc3RNZXRob2QgPSAnYm9vdHN0cmFwJywgYm9vdHN0cmFwID0gNTAwLCANCiAgICAgICAgICAgICAgdGVzdCA9IFRSVUUsIGNpID0gVFJVRSwNCiAgICAgICAgICAgICAgc2ltcGxlU2xvcGVFc3QgPSBUUlVFLCBzaW1wbGVTbG9wZVBsb3QgPSBUUlVFKQ0KDQptb2QxJG1vZCAlPiUgDQogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKSANCg0KbW9kMSRzaW1wbGVTbG9wZSRlc3RpbWF0ZXMgJT4lIA0KICBrbml0cjo6a2FibGUoZGlnaXRzID0gMikJDQoNCm1vZDEkc2ltcGxlU2xvcGUkcGxvdA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyBpZGVhIG5vdCB1c2VkIGhlcmUNCiAgIyBmaWx0ZXIoWmkgJWluJSBjKCJ6aTEiLCAiemk4IikpICU+JSANCiAgIyBwaXZvdF9sb25nZXIoY29scyA9IG1hdGNoZXMoInByZXxwb3N0IiksIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInZhbHVlIikNCg0KDQoNCiMgRGF0YV9Vbml0ZWQgJT4lIA0KIyAgIGZpbHRlcihFdGFwYSA9PSAiSSIpICU+JQ0KIyAgIG1lZG1vZDo6bW9kKGRhdGEgPSAuLCANCiMgICAgICAgICAgICAgZGVwID0gIlBQUyBwb3N0X1RvdGFsIiwgbW9kID0gIklPUyBwb3N0IiwgcHJlZCA9ICJQUFMgcHJlX1RvdGFsIiwgDQojICAgICAgICAgICAgIGVzdE1ldGhvZCA9ICJzdGFuZGFyZCIsIHRlc3QgPSBUUlVFLCANCiMgICAgICAgICAgICAgc2ltcGxlU2xvcGVFc3QgPSBGQUxTRSwgc2ltcGxlU2xvcGVQbG90ID0gVFJVRSkNCg0KYGBgDQoNCg0KDQoNCg0KPCEtLSBTZXNzaW9uIEluZm8gYW5kIExpY2Vuc2UgLS0+DQoNCjxicj4NCg0KIyBTZXNzaW9uIEluZm8NCmBgYHtyIHNlc3Npb25faW5mbywgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gJ21hcmt1cCd9DQpzZXNzaW9uSW5mbygpICAgIA0KYGBgDQoNCjwhLS0gRm9vdGVyIC0tPg0KJm5ic3A7DQo8aHIgLz4NCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij5BIHdvcmsgYnkgPGEgaHJlZj0iaHR0cHM6Ly9naXRodWIuY29tL0NsYXVkaXVQYXBhc3RlcmkvIj5DbGF1ZGl1IFBhcGFzdGVyaTwvYT48L3A+DQo8cCBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gc3R5bGU9ImNvbG9yOiAjODA4MDgwOyI+PGVtPmNsYXVkaXUucGFwYXN0ZXJpQGdtYWlsLmNvbTwvZW0+PC9zcGFuPjwvcD4NCiZuYnNwOw0K