1 Arrange folders, sort files, read and merge

1.1 Create folders by conditions, copy files to them

# !!!!EVAL = FALSE
##################### Read file names ##################################################################################
file_names <- dir(pattern = "\\.xls$")
## if above isn't good enough try the following:
# file_names <- list.files(wd)
# file_names <- sop_files[!file.info(sop_files)$isdir]   # exclude directories
# file_names <- sop_files[grep(".xls", sop_files, fixed = TRUE)]


##################### Create folders with Condition names ###############################################################
# this part of script may be re-run if files from wd are updated
dir_names <- c("Unic_CTRL_Instr", "Unic_CTRL_Solo", "Unic_OGL_Instr", "Unic_OGL_Solo",
              "Repetat_CTRL_Instr", "Repetat_CTRL_Solo", "Repetat_OGL_Instr", "Repetat_OGL_Solo")
             
for(dir in dir_names){
  if(!dir.exists(file.path(wd, dir)))
  dir.create(file.path(wd, dir), showWarnings = FALSE)
}


##################### Use file names to sort them to folders ############################################################
sort_files_to_dirs <- function(wd, pattern, dir) {
  check_pattern <- outer(file_names, pattern, stringr::str_detect)               # if all TRUE bye row then it has full pattern
  index <- which(apply(check_pattern, 1, function(x) all(x==TRUE)))              # get index of file_names where all are TRUE
  sorted_files <- file_names[index]                                              # get names of files from indexes
  
  for(files in sorted_files) {                                                   # copy the files to corresponding folder
    file.copy(from = file.path(wd, files), to = file.path(wd, dir))
  }  
}

sort_files_to_dirs(wd = wd, pattern = c("unic", "ecran", "instructor"), dir = "Unic_CTRL_Instr")
sort_files_to_dirs(wd = wd, pattern = c("unic", "ecran", "solo"), dir = "Unic_CTRL_Solo")
sort_files_to_dirs(wd = wd, pattern = c("unic", "oglinda", "instructor"), dir = "Unic_OGL_Instr")
sort_files_to_dirs(wd = wd, pattern = c("unic", "oglinda", "solo"), dir = "Unic_OGL_Solo")

1.2 Read Pilot Data (these are on VAS)

wd <- "C:/Users/Mihai/Desktop/EEG_O.4c_1/O.4c - Poster/Date Pilot"
setwd(wd)
The working directory was changed to C:/Users/Mihai/Desktop/EEG_O.4c_1/O.4c - Poster/Date Pilot inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
folders <- list.files(wd)
folders <- folders[file.info(folders)$isdir]   # luam doar folderele
datasetnames <- NULL
for (i in 1:length(folders)) {
  datasetname <- folders[i]
  datasetnames <- c(datasetnames, datasetname)
  current_dir <- setwd(file.path(wd, folders[i]))
  print(paste0("current_dir: ", current_dir))
  
  paths <- dir(pattern = "\\.xls$")
  names(paths) <- basename(paths)
  assign( paste(datasetname), plyr::ldply(paths, rio::import) )
}
[1] "current_dir: C:/Users/Mihai/Desktop/EEG_O.4c_1/O.4c - Poster/Date Pilot"
[1] "current_dir: C:/Users/Mihai/Desktop/EEG_O.4c_1/O.4c - Poster/Date Pilot/Pilot_Unic_CTRL_Instr"
[1] "current_dir: C:/Users/Mihai/Desktop/EEG_O.4c_1/O.4c - Poster/Date Pilot/Pilot_Unic_CTRL_Solo"
[1] "current_dir: C:/Users/Mihai/Desktop/EEG_O.4c_1/O.4c - Poster/Date Pilot/Pilot_Unic_OGL_Instr"
setwd(wd)
## Function for Pilot Data processing
process_pilot_data <- function(df){
  df_modif <-
    df %>%
    mutate(MarkerStimuli = case_when(`Stimuli order` < 36 ~ 11,                  # Pilot data was correcte due to stimulus ordering problems
                                     `Stimuli order` >=36 & `Stimuli order` <= 70 ~ 21,
                                     `Stimuli order` > 70 ~ 31,
                                     TRUE                 ~ as.numeric(NA))) %>%   
    mutate(`Stimulus type` = case_when(MarkerStimuli %in% c(11, 12) ~ "negativ",           # Left the old coding also altough we have only 11,21,31
                                       MarkerStimuli %in% c(21, 22) ~ "neutru",
                                       MarkerStimuli %in% c(31, 32) ~ "pozitiv",
                                       TRUE                         ~ as.character(NA))) %>%
    filter(`Stimulus type` != "pozitiv")                               # exclude positive stimuly because only 1 subject has them
  
  df <- deparse(substitute(df))
  assign(df, df_modif, envir = globalenv())
}
## Process Pilot Data
process_pilot_data(Pilot_Unic_CTRL_Instr)
process_pilot_data(Pilot_Unic_CTRL_Solo)
process_pilot_data(Pilot_Unic_OGL_Instr)
process_pilot_data(Pilot_Unic_OGL_Solo)
## Now we work only with Pilot Data
Unic_CTRL_Instr <- Pilot_Unic_CTRL_Instr
Unic_CTRL_Solo <- Pilot_Unic_CTRL_Solo
Unic_OGL_Instr <- Pilot_Unic_OGL_Instr
Unic_OGL_Solo <- Pilot_Unic_OGL_Solo

1.3 Cleaning the data

#################################### Data Cleaning #####################################################################
# Check if ids have > 1 row of data (empty .xls have only 1 row)
# Careful! This function modfies the datasets in the global envinronment
delete_empty_id <- function(df){
  list_empty_id <- 
    df %>%
    dplyr::group_by(.id) %>%
    dplyr::summarise(row_count = n()) %>%
    dplyr::rename("empty_id" = .id) %>%
    mutate(delete_id = if_else(row_count < 3, TRUE, FALSE)) %>%
    filter(delete_id == TRUE)
  
  df_modif <- 
    df %>%
    filter(!.id %in% list_empty_id$empty_id)
  
  if(!identical(df, df_modif)){
    df <- deparse(substitute(df))
    cat("Deleting from ", print(as.name(df))); print(list_empty_id)                    # print out which ids are deleted from which dataset
    assign(df, df_modif, envir = globalenv())                                          # assign modified df to original dataset from Global
  }else cat("No empty datasets. Nothing to delete")
}
# Apply function to all datasets (tricky to do in for loop because of super assignment)
delete_empty_id(Unic_CTRL_Instr)
No empty datasets. Nothing to delete
delete_empty_id(Unic_CTRL_Solo)
No empty datasets. Nothing to delete
delete_empty_id(Unic_OGL_Instr)
No empty datasets. Nothing to delete
delete_empty_id(Unic_OGL_Solo)
No empty datasets. Nothing to delete

1.4 Exclude VAS_Resp based on RT outliers

# !!!!EVAL = FALSE
############################### Exclude Outliers based on RT (by subject and stimulus type) #######################################
## DONT RUN (unless it is needed) ----> eval=FALSE
# Exclude RT outliers (=- 2SD) - instead of simple filter, makeing them NA  is better for paired comparison
remove_outliers <- function(df) {
  df_modif <-
    df %>%
    dplyr::group_by(.id, `Stimulus type`) %>%                  # we could have done before:  dplyr::rename("Stim_type" = `Stimulus type`) 
    mutate(VAS_Resp = if_else(abs(VAS_RT - mean(VAS_RT, na.rm=TRUE)) > 2*sd(VAS_RT, na.rm=TRUE), as.numeric(NA), VAS_Resp))
  
  if(!identical(df, df_modif)){
    df <- deparse(substitute(df))
    cat("Deleting outliers from ", print(as.name(df)));                               # print out datasets which have been modified
    assign(df, df_modif, envir = globalenv())                                          # assign modified df to original dataset from Global
  }else cat("No outlier")
}

remove_outliers(Unic_CTRL_Instr)
remove_outliers(Unic_CTRL_Solo)
remove_outliers(Unic_OGL_Instr)
remove_outliers(Unic_OGL_Solo)

1.5 Test if datasets have same columns

unic_df_obj <- mget(c("Unic_CTRL_Instr", "Unic_CTRL_Solo", "Unic_OGL_Instr", "Unic_OGL_Solo"))
unic_df_obj <-lapply(unic_df_obj, colnames)
outer(unic_df_obj, unic_df_obj, Vectorize(identical))                           # if all are TRUE, all df have same columns
                Unic_CTRL_Instr Unic_CTRL_Solo Unic_OGL_Instr Unic_OGL_Solo
Unic_CTRL_Instr            TRUE           TRUE          FALSE         FALSE
Unic_CTRL_Solo             TRUE           TRUE          FALSE         FALSE
Unic_OGL_Instr            FALSE          FALSE           TRUE          TRUE
Unic_OGL_Solo             FALSE          FALSE           TRUE          TRUE



2 Analysis - UNICE

2.1 Descriptives

##########################################################################################################################
#################################### Analyses - UNICE ####################################################################
## Descriptives by condition dataset
descriptive_func <- function(df, Stim_type, By_ID = FALSE){
  df_name <- deparse(substitute(df))
  suppressWarnings({                                                                # if all NAs in VAS_Resp, NaNs and Infs will be produced
    df_modif <- 
      df %>%
      dplyr::rename("ID" = .id) %>%
      select_all(~gsub("\\s+|\\.", "_", .)) %>%                                     # replaces blancks with "_" in colnames 
      filter(Stimulus_type == Stim_type)                                            # filter by stimulus type
      
    if(isTRUE(By_ID)){                                                              # if true group by id, if not return descriptives for all ids
      by_id_text <- " by subject"
      df_modif %>%
      dplyr::group_by(ID) %>%
      tidystats::describe_data(VAS_Resp, VAS_RT, na.rm = TRUE) %>%
        knitr::kable(caption = paste(as.name(df_name), " ", Stim_type, by_id_text), format = "pandoc", digits = 2)
    }else{ 
      by_id_text <- " all subjects"
      df_modif %>%
      ungroup() %>%  
      tidystats::describe_data(VAS_Resp, VAS_RT, na.rm = TRUE) %>%
        knitr::kable(caption = paste(as.name(df_name), " ", Stim_type, by_id_text), format = "pandoc", digits = 2)
    }
  })
}  
descriptive_func(df = Unic_CTRL_Instr, Stim_type = "negativ", By_ID = FALSE)           # Negative - General

Unic_CTRL_Instr negativ all subjects
var missing n M SD SE min max range median mode skew kurtosis
VAS_Resp 0 105 44.70 27.45 2.68 0.0 100.00 100.00 41.42 0.00 0.00 1.94
VAS_RT 0 105 1.88 0.93 0.09 0.2 5.45 5.25 1.72 2.02 1.02 4.38

descriptive_func(df = Unic_CTRL_Solo, Stim_type = "negativ", By_ID = FALSE)

Unic_CTRL_Solo negativ all subjects
var missing n M SD SE min max range median mode skew kurtosis
VAS_Resp 0 105 46.53 27.64 2.70 0.00 100.00 100.00 47.08 0.00 0.10 2.01
VAS_RT 0 105 3.16 1.68 0.16 0.35 9.87 9.53 2.87 2.08 1.19 4.93

descriptive_func(df = Unic_OGL_Instr, Stim_type = "negativ", By_ID = FALSE)

Unic_OGL_Instr negativ all subjects
var missing n M SD SE min max range median mode skew kurtosis
VAS_Resp 0 105 48.10 33.12 3.23 0.00 100.00 100.00 45.99 0.00 0.00 1.67
VAS_RT 0 105 2.73 1.47 0.14 0.61 7.92 7.31 2.35 2.23 0.97 3.53

descriptive_func(df = Unic_OGL_Solo, Stim_type = "negativ", By_ID = FALSE)

Unic_OGL_Solo negativ all subjects
var missing n M SD SE min max range median mode skew kurtosis
VAS_Resp 0 105 47.07 34.73 3.39 0.00 100.00 100.00 44.89 0.00 0.09 1.55
VAS_RT 0 105 2.80 1.85 0.18 0.33 13.47 13.14 2.53 3.43 2.24 12.42

descriptive_func(df = Unic_CTRL_Instr, Stim_type = "negativ", By_ID = TRUE)            # Negative - by id

Unic_CTRL_Instr negativ by subject
var ID missing n M SD SE min max range median mode skew kurtosis
VAS_Resp Corect_EEGscalpID02o4c2ecraninst.xls 0 35 34.70 21.25 3.59 0.00 72.26 72.26 38.14 0.00 -0.14 1.97
VAS_Resp Corect_EEGscalpID03bcontcuinstr.xls 0 35 42.73 30.37 5.13 0.00 90.88 90.88 35.40 0.00 0.01 1.71
VAS_Resp Corect_EEGscalpID05o4cBu2instr5ecraninst.xls 0 35 56.66 26.03 4.40 10.58 100.00 89.42 64.42 66.79 -0.27 1.83
VAS_RT Corect_EEGscalpID02o4c2ecraninst.xls 0 35 1.64 0.64 0.11 0.57 3.72 3.15 1.52 2.02 0.93 4.48
VAS_RT Corect_EEGscalpID03bcontcuinstr.xls 0 35 2.17 1.15 0.19 0.20 5.45 5.25 2.10 1.43 0.42 3.43
VAS_RT Corect_EEGscalpID05o4cBu2instr5ecraninst.xls 0 35 1.84 0.87 0.15 0.80 4.09 3.29 1.60 1.48 1.32 3.89

descriptive_func(df = Unic_CTRL_Solo, Stim_type = "negativ", By_ID = TRUE)

Unic_CTRL_Solo negativ by subject
var ID missing n M SD SE min max range median mode skew kurtosis
VAS_Resp Corect_EEGscalpID02o4c2ecransolo.xls 0 35 36.93 19.37 3.27 7.30 72.99 65.69 34.12 18.98 0.13 1.83
VAS_Resp Corect_EEGscalpID03contsolo.xls 0 35 46.90 32.63 5.52 0.00 100.00 100.00 43.25 0.00 0.01 1.77
VAS_Resp Corect_EEGscalpID05o4Busolo5ecransolo.xls 0 35 55.75 26.72 4.52 6.93 97.26 90.33 56.57 61.86 -0.26 1.98
VAS_RT Corect_EEGscalpID02o4c2ecransolo.xls 0 35 3.27 1.47 0.25 1.63 7.34 5.71 2.70 2.08 0.98 3.31
VAS_RT Corect_EEGscalpID03contsolo.xls 0 35 3.50 2.16 0.36 0.35 9.87 9.53 3.08 6.18 0.90 3.85
VAS_RT Corect_EEGscalpID05o4Busolo5ecransolo.xls 0 35 2.71 1.23 0.21 1.07 6.22 5.15 2.58 1.63 1.07 3.99

descriptive_func(df = Unic_OGL_Instr, Stim_type = "negativ", By_ID = TRUE)

Unic_OGL_Instr negativ by subject
var ID missing n M SD SE min max range median mode skew kurtosis
VAS_Resp Corect_EEGscalpID02ucuinstr.xls 0 35 45.81 35.23 5.95 0.00 97.08 97.08 46.72 0.00 0.00 1.55
VAS_Resp Corect_EEGscalpID03u2instr.xls 0 35 32.22 26.13 4.42 0.00 84.85 84.85 29.93 0.00 0.38 2.12
VAS_Resp Corect_EEGscalpID05o45oglindainst.xls 0 35 66.26 28.75 4.86 7.85 100.00 92.15 79.01 100.00 -0.56 1.97
VAS_RT Corect_EEGscalpID02ucuinstr.xls 0 35 1.63 0.51 0.09 0.61 2.46 1.85 1.63 2.23 -0.29 2.44
VAS_RT Corect_EEGscalpID03u2instr.xls 0 35 4.07 1.44 0.24 1.43 7.92 6.49 4.12 5.23 0.21 3.23
VAS_RT Corect_EEGscalpID05o45oglindainst.xls 0 35 2.48 1.06 0.18 0.93 5.28 4.35 2.45 2.88 0.81 3.38

descriptive_func(df = Unic_OGL_Solo, Stim_type = "negativ", By_ID = TRUE)

Unic_OGL_Solo negativ by subject
var ID missing n M SD SE min max range median mode skew kurtosis
VAS_Resp Corect_EEGscalpID02ufara.xls 0 35 45.94 37.43 6.33 0.00 100.00 100.00 38.14 0.00 0.17 1.42
VAS_Resp Corect_EEGscalpID03u1fara.xls 0 35 27.98 26.17 4.42 0.00 85.04 85.04 20.26 0.00 0.66 2.34
VAS_Resp Corect_EEGscalpID05o4c5oglindasolo.xls 0 35 67.30 28.38 4.80 0.36 100.00 99.64 77.19 100.00 -0.66 2.24
VAS_RT Corect_EEGscalpID02ufara.xls 0 35 2.78 1.51 0.25 0.38 7.27 6.89 2.43 3.43 1.15 4.39
VAS_RT Corect_EEGscalpID03u1fara.xls 0 35 3.13 1.78 0.30 0.33 7.19 6.86 2.90 3.55 0.36 2.57
VAS_RT Corect_EEGscalpID05o4c5oglindasolo.xls 0 35 2.50 2.19 0.37 0.60 13.47 12.88 2.01 2.53 3.74 19.42

descriptive_func(df = Unic_CTRL_Instr, Stim_type = "neutru", By_ID = FALSE)           # Neutral - General

Unic_CTRL_Instr neutru all subjects
var missing n M SD SE min max range median mode skew kurtosis
VAS_Resp 0 105 16.53 21.82 2.13 0.0 89.78 89.78 6.39 0.00 1.34 4.04
VAS_RT 0 105 1.39 0.99 0.10 0.2 4.18 3.99 1.30 0.55 0.90 3.13

descriptive_func(df = Unic_CTRL_Solo, Stim_type = "neutru", By_ID = FALSE)

Unic_CTRL_Solo neutru all subjects
var missing n M SD SE min max range median mode skew kurtosis
VAS_Resp 0 105 19.61 24.76 2.42 0.00 89.60 89.60 5.11 0.00 1.08 2.98
VAS_RT 0 105 2.29 1.82 0.18 0.01 10.34 10.33 1.97 3.93 1.74 7.46

descriptive_func(df = Unic_OGL_Instr, Stim_type = "neutru", By_ID = FALSE)

Unic_OGL_Instr neutru all subjects
var missing n M SD SE min max range median mode skew kurtosis
VAS_Resp 0 105 27.43 31.58 3.08 0.00 97.08 97.08 12.59 0.00 0.85 2.33
VAS_RT 0 105 2.62 1.78 0.17 0.48 11.89 11.41 2.25 1.58 1.74 8.58

descriptive_func(df = Unic_OGL_Solo, Stim_type = "neutru", By_ID = FALSE)

Unic_OGL_Solo neutru all subjects
var missing n M SD SE min max range median mode skew kurtosis
VAS_Resp 0 105 31.21 33.41 3.26 0.00 99.45 99.45 18.07 0.00 0.68 1.98
VAS_RT 0 105 2.90 1.95 0.19 0.38 10.44 10.06 2.51 1.81 1.13 4.44

descriptive_func(df = Unic_CTRL_Instr, Stim_type = "neutru", By_ID = TRUE)            # Neutral - by id

Unic_CTRL_Instr neutru by subject
var ID missing n M SD SE min max range median mode skew kurtosis
VAS_Resp Corect_EEGscalpID02o4c2ecraninst.xls 0 35 4.93 12.21 2.06 0.00 56.02 56.02 0.00 0.00 2.76 10.44
VAS_Resp Corect_EEGscalpID03bcontcuinstr.xls 0 35 20.08 25.55 4.32 0.00 89.78 89.78 0.00 0.00 0.96 2.95
VAS_Resp Corect_EEGscalpID05o4cBu2instr5ecraninst.xls 0 35 24.58 20.90 3.53 0.00 83.03 83.03 16.97 16.97 1.14 3.47
VAS_RT Corect_EEGscalpID02o4c2ecraninst.xls 0 35 0.78 0.49 0.08 0.35 2.65 2.30 0.58 0.55 2.02 7.25
VAS_RT Corect_EEGscalpID03bcontcuinstr.xls 0 35 1.44 1.17 0.20 0.20 3.93 3.74 1.43 2.02 0.65 2.21
VAS_RT Corect_EEGscalpID05o4cBu2instr5ecraninst.xls 0 35 1.97 0.80 0.13 0.86 4.18 3.32 1.73 4.18 1.18 3.91

descriptive_func(df = Unic_CTRL_Solo, Stim_type = "neutru", By_ID = TRUE)

Unic_CTRL_Solo neutru by subject
var ID missing n M SD SE min max range median mode skew kurtosis
VAS_Resp Corect_EEGscalpID02o4c2ecransolo.xls 0 35 5.57 14.64 2.47 0.00 61.31 61.31 0.00 0.00 2.95 10.71
VAS_Resp Corect_EEGscalpID03contsolo.xls 0 35 18.86 25.24 4.27 0.00 89.60 89.60 0.00 0.00 1.18 3.42
VAS_Resp Corect_EEGscalpID05o4Busolo5ecransolo.xls 0 35 34.40 24.39 4.12 1.09 84.49 83.39 27.74 20.44 0.40 1.96
VAS_RT Corect_EEGscalpID02o4c2ecransolo.xls 0 35 1.47 1.09 0.18 0.33 3.93 3.60 1.00 3.93 0.98 2.51
VAS_RT Corect_EEGscalpID03contsolo.xls 0 35 2.42 2.17 0.37 0.01 10.34 10.33 2.06 2.96 1.48 6.07
VAS_RT Corect_EEGscalpID05o4Busolo5ecransolo.xls 0 35 2.97 1.75 0.30 1.00 8.94 7.94 2.43 2.98 2.03 7.31

descriptive_func(df = Unic_OGL_Instr, Stim_type = "neutru", By_ID = TRUE)

Unic_OGL_Instr neutru by subject
var ID missing n M SD SE min max range median mode skew kurtosis
VAS_Resp Corect_EEGscalpID02ucuinstr.xls 0 35 23.33 37.07 6.27 0.00 97.08 97.08 0.00 0.00 1.12 2.47
VAS_Resp Corect_EEGscalpID03u2instr.xls 0 35 27.91 29.69 5.02 0.00 85.04 85.04 14.05 0.00 0.60 1.85
VAS_Resp Corect_EEGscalpID05o45oglindainst.xls 0 35 31.06 27.65 4.67 0.00 94.53 94.53 27.01 0.00 0.83 2.71
VAS_RT Corect_EEGscalpID02ucuinstr.xls 0 35 1.26 0.62 0.10 0.48 2.60 2.12 1.03 1.58 0.56 1.92
VAS_RT Corect_EEGscalpID03u2instr.xls 0 35 3.61 1.43 0.24 1.00 6.17 5.17 3.60 4.53 -0.11 1.95
VAS_RT Corect_EEGscalpID05o45oglindainst.xls 0 35 2.99 2.05 0.35 0.71 11.89 11.18 2.48 2.25 2.61 11.41

descriptive_func(df = Unic_OGL_Solo, Stim_type = "neutru", By_ID = TRUE)
Unic_OGL_Solo neutru by subject
var ID missing n M SD SE min max range median mode skew kurtosis
VAS_Resp Corect_EEGscalpID02ufara.xls 0 35 24.57 36.92 6.24 0.00 99.45 99.45 0.55 0.00 1.24 2.81
VAS_Resp Corect_EEGscalpID03u1fara.xls 0 35 24.14 28.04 4.74 0.00 89.96 89.96 10.22 0.00 0.80 2.34
VAS_Resp Corect_EEGscalpID05o4c5oglindasolo.xls 0 35 44.91 31.21 5.28 0.36 93.07 92.70 49.82 60.40 0.00 1.52
VAS_RT Corect_EEGscalpID02ufara.xls 0 35 2.50 1.76 0.30 0.51 6.70 6.19 1.81 1.81 0.95 2.94
VAS_RT Corect_EEGscalpID03u1fara.xls 0 35 3.10 2.02 0.34 0.38 8.92 8.54 2.83 2.51 0.87 3.40
VAS_RT Corect_EEGscalpID05o4c5oglindasolo.xls 0 35 3.10 2.04 0.34 0.71 10.44 9.73 2.70 1.98 1.43 5.85

2.2 Merge

############################## Merge condition dataset ############################################################
# Must first rename .id to ID in oder to have .id for df names
ID_rename <- function(df){
  if(".id" %in% colnames(df)) {
    df_modif <- 
      df %>%
      dplyr::rename("ID" = .id)
    df <- deparse(substitute(df))
    cat("Changed .id to ID for: ", as.name(df))
    assign(df, df_modif, envir = globalenv())
  }  
}
ID_rename(Unic_CTRL_Instr)
Changed .id to ID for:  Unic_CTRL_Instr
ID_rename(Unic_CTRL_Solo)
Changed .id to ID for:  Unic_CTRL_Solo
ID_rename(Unic_OGL_Instr)
Changed .id to ID for:  Unic_OGL_Instr
ID_rename(Unic_OGL_Solo)
Changed .id to ID for:  Unic_OGL_Solo
# Merge into one df
list_df_merge <- list(Unic_CTRL_Instr, Unic_CTRL_Solo, Unic_OGL_Instr, Unic_OGL_Solo)
names(list_df_merge) <- c("Unic_CTRL_Instr", "Unic_CTRL_Solo", "Unic_OGL_Instr", "Unic_OGL_Solo")
Unic_merged <- plyr::ldply(list_df_merge, data.frame)                    # also works for this job bind_rows(list_df_merge, .id = "column_label")

2.3 Analyses on merged (Anova & post-hoc)

############################## Analyses on Merged ################################################################
## Just a Test 
  # Unic_merged_spread_Neg <- 
  #   Unic_merged %>%
  #   filter(!is.na(VAS_Resp)) %>%                                           # some files had only NA on VAS_Resp and VAS_RT
  #   select(.id, ID, Subj_id, 
  #          Stimuli.order, MarkerStimuli, Stimulus.type, 
  #          VAS_Resp, VAS_RT) %>%
  #   filter(Stimulus.type == "negativ") %>%                                 # dont forget to pick stymulus type
  #   spread(.id, VAS_Resp)
  # 
  # t.test(Unic_merged_spread_Neg$Unic_CTRL_Instr, Unic_merged_spread_Neg$Unic_CTRL_Solo, na.rm = TRUE)
  # t.test(Unic_merged_spread_Neg$Unic_OGL_Instr, Unic_merged_spread_Neg$Unic_OGL_Solo, na.rm = TRUE)
  # t.test(Unic_merged_spread_Neg$Unic_OGL_Instr, Unic_merged_spread_Neg$Unic_CTRL_Instr, na.rm = TRUE)
  # t.test(Unic_merged_spread_Neg$Unic_OGL_Solo, Unic_merged_spread_Neg$Unic_CTRL_Solo, na.rm = TRUE)
## Function prepair data for analyses
prepaire_merged_func <- function(Stim_type){
  Unic_merged %>%
    filter(!is.na(VAS_Resp)) %>%                                           # some files had only NA on VAS_Resp and VAS_RT
    select(.id, ID, Subj_id, 
           Stimuli.order, MarkerStimuli, Stimulus.type, 
           VAS_Resp, VAS_RT) %>%
    dplyr::rename(Cond = .id) %>% 
    filter(Stimulus.type == Stim_type) %>%                                 # dont forget to pick stymulus type
    mutate(Cond = as.factor(Cond))                                         # tunr to factor for aov family functions
}
Unic_merged_Neg <- prepaire_merged_func("negativ")
Unic_merged_Neu <- prepaire_merged_func("neutru")
Unic_merged_Poz <- prepaire_merged_func("pozitiv")
## Anova and Post-Hoc  - NEGATIV
# Stimulus type
cat("### Negativ")

2.3.1 Negativ

# Normality
cat("#### Normality Test")

2.3.1.1 Normality Test

Unic_merged_Neg %>%
  select(VAS_Resp) %>%                                                     # must select variables outside function 
  tadaatoolbox::tadaa_normtest(method = "shapiro")                         # , print = "markdown"  for Notebook
# Levene Test (p>.05 = homogeneity of variances)
cat("#### Levene Test")

2.3.1.2 Levene Test

Unic_merged_Neg %>%
  tadaatoolbox::tadaa_levene(data = ., VAS_Resp ~ Cond)                    # , print = "markdown"  for Notebook
# Anova
cat("#### Anova")

2.3.1.3 Anova

Unic_merged_Neg %>%
  #do(broom::glance(aov(.$VAS_Resp ~ .$Cond)))                             # regular anova do(broom::tidy(aov(.$VAS_Resp ~ .$Cond)))
  tadaatoolbox::tadaa_aov(data = ., VAS_Resp ~ Cond, type = 1)             # , print = "markdown"  for Notebook
# Post-Hoc 
cat("#### Post-Hoc Games Howell")

2.3.1.4 Post-Hoc Games Howell

Unic_merged_Neg %>%
  # Tukey for equal variance 
  tadaatoolbox::tadaa_pairwise_tukey(data = ., VAS_Resp, Cond)             # , print = "markdown"  for Notebook
  # Games Howell does not assume equal variances
  #tadaatoolbox::tadaa_pairwise_gh(data = ., VAS_Resp, Cond)                # , print = "markdown"  for Notebook
## Anova and Post-Hoc  - NEUTRU
# Stimulus type
cat("### Neutru")

2.3.2 Neutru

# Normality
cat("#### Normality Test")

2.3.2.1 Normality Test

Unic_merged_Neu %>%
  select(VAS_Resp) %>%                                                     # must select variables outside function 
  tadaatoolbox::tadaa_normtest(method = "shapiro")                         # , print = "markdown"  for Notebook
# Levene Test (p>.05 = homogeneity of variances)
cat("#### Levene Test")

2.3.2.2 Levene Test

Unic_merged_Neu %>%
  tadaatoolbox::tadaa_levene(data = ., VAS_Resp ~ Cond)                    # , print = "markdown"  for Notebook
# Anova
cat("#### Anova")

2.3.2.3 Anova

Unic_merged_Neu %>%
  #do(broom::glance(aov(.$VAS_Resp ~ .$Cond)))                             # regular anova do(broom::tidy(aov(.$VAS_Resp ~ .$Cond)))
  tadaatoolbox::tadaa_aov(data = ., VAS_Resp ~ Cond, type = 1)             # , print = "markdown"  for Notebook
# Post-Hoc 
cat("#### Post-Hoc Games Howell")

2.3.2.4 Post-Hoc Games Howell

Unic_merged_Neu %>%
  # Tukey for equal variance 
  tadaatoolbox::tadaa_pairwise_tukey(data = ., VAS_Resp, Cond)             # , print = "markdown"  for Notebook
  # Games Howell does not assume equal variances
  #tadaatoolbox::tadaa_pairwise_gh(data = ., VAS_Resp, Cond)                # , print = "markdown"  for Notebook

2.4 Plots with p values

# by dataset
ggplot(Unic_merged, aes(x = Stimulus.type, y = VAS_Resp)) +
  geom_boxplot() +
  stat_summary(fun.data = mean_se,  colour = "darkred") +
  xlab("") +
  facet_wrap(~.id) +
  ggpubr::stat_compare_means(method = "t.test", 
                             label = "p.signif",                                         # to avoid scientific notation of very small p-values
                             #paired = TRUE, 
                             comparisons = list(c("negativ", "neutru")))  

# by Stimulus type
ggplot(Unic_merged, aes(x = .id, y = VAS_Resp)) +
  geom_boxplot() +
  stat_summary(fun.data = mean_se,  colour = "darkred") +
  xlab("") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  facet_wrap(~Stimulus.type) +
  ggpubr::stat_compare_means(method = "t.test",
                             label = "p.format",                                         # formated p-values
                             #paired = TRUE, 
                             comparisons = list(c("Unic_CTRL_Instr", "Unic_CTRL_Solo"),
                                                c("Unic_CTRL_Instr", "Unic_OGL_Instr"),
                                                c("Unic_CTRL_Solo", "Unic_OGL_Instr"),
                                                c("Unic_CTRL_Solo", "Unic_OGL_Solo"),
                                                c("Unic_OGL_Instr", "Unic_OGL_Solo"),
                                                c("Unic_CTRL_Instr", "Unic_OGL_Solo"))) 

# drop to CTRL vs OGL - by Stimulus type
Unic_merged %>%
  mutate(.id = case_when(.id %in% c("Unic_CTRL_Instr", "Unic_CTRL_Solo") ~ "Unic_CTRL",
                         .id %in% c("Unic_OGL_Instr", "Unic_OGL_Solo") ~ "Unic_OGL",
                         TRUE ~ as.character(.id))) %>%
    ggplot(aes(x = .id, y = VAS_Resp)) +
    geom_boxplot() +
    stat_summary(fun.data = mean_se,  colour = "darkred") +
    xlab("") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    facet_wrap(~Stimulus.type) +
    ggpubr::stat_compare_means(method = "t.test",
                               label = "p.format",                                         # formated p-values
                               #paired = TRUE, 
                               comparisons = list(c("Unic_CTRL", "Unic_OGL")))          

# drop to Instr vs Solo - by Stimulus type
Unic_merged %>%
  mutate(.id = case_when(.id %in% c("Unic_CTRL_Instr", "Unic_OGL_Instr") ~ "Unic_Instr",
                         .id %in% c("Unic_CTRL_Solo", "Unic_OGL_Solo") ~ "Unic_Solo",
                         TRUE ~ as.character(.id))) %>%
    ggplot(aes(x = .id, y = VAS_Resp)) +
    geom_boxplot() +
    stat_summary(fun.data = mean_se,  colour = "darkred") +
    xlab("") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    facet_wrap(~Stimulus.type) +
    ggpubr::stat_compare_means(method = "t.test",
                               label = "p.format",                                         # formated p-values
                               #paired = TRUE, 
                               comparisons = list(c("Unic_Instr", "Unic_Solo"))) 

3 Download Data

Unic_merged %>% 
    select(-c(3:10)) %>%
    DT::datatable(                                  # excel downloadable  DT table
      extensions = 'Buttons',
      options = list(pageLength = 20,
                     scrollX='500px', 
                     dom = 'Bfrtip', 
                     buttons = c('excel', "csv")))


4 Session Info

 

A work by Claudiu Papasteri

claudiu.papasteri@gmail.com

 

LS0tDQp0aXRsZTogIjxicj4gTy40IC0gVUNMIFBvc3RlciIgDQpzdWJ0aXRsZTogIlByZWxpbWluYXJ5IFJlcG9ydCBmb3IgVW5pY2UgLSAzIFBpbG90IFBhcnRpY2lwYW50cyINCmF1dGhvcjogIjxicj4gQ2xhdWRpdSBQYXBhc3RlcmkiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlbSAlWScpYCINCm91dHB1dDogDQogICAgaHRtbF9ub3RlYm9vazoNCiAgICAgICAgICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgICAgICAgICAgdG9jOiB0cnVlDQogICAgICAgICAgICB0b2NfZGVwdGg6IDINCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgICAgICAgICAgdGhlbWU6IHNwYWNlbGFiDQogICAgICAgICAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgICAgICAgICBmb250LWZhbWlseTogQXJpYWwNCiAgICAgICAgICAgIGZpZ193aWR0aDogMTANCiAgICAgICAgICAgIGZpZ19oZWlnaHQ6IDkNCiAgICBwZGZfZG9jdW1lbnQ6IA0KICAgICAgICAgICAgdG9jOiB0cnVlDQogICAgICAgICAgICB0b2NfZGVwdGg6IDINCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgICAgICAgICAgIyBmb250c2l6ZTogMTFwdA0KICAgICAgICAgICAgIyBnZW9tZXRyeTogbWFyZ2luPTFpbg0KICAgICAgICAgICAgIyBmaWdfd2lkdGg6IDcNCiAgICAgICAgICAgICMgZmlnX2hlaWdodDogNg0KICAgICAgICAgICAgIyBmaWdfY2FwdGlvbjogdHJ1ZQ0KICAgICMgZ2l0aHViX2RvY3VtZW50OiANCiAgICAgICAgICAgICMgdG9jOiB0cnVlDQogICAgICAgICAgICAjIHRvY19kZXB0aDogMg0KICAgICAgICAgICAgIyBodG1sX3ByZXZpZXc6IGZhbHNlDQogICAgICAgICAgICAjIGZpZ193aWR0aDogNQ0KICAgICAgICAgICAgIyBmaWdfaGVpZ2h0OiA1DQogICAgICAgICAgICAjIGRldjoganBlZw0KLS0tDQoNCg0KPCEtLSBTZXR1cCAtLT4NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMga2ludHIgb3B0aW9ucw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBjb21tZW50ID0gIiMiLA0KICBjb2xsYXBzZSA9IFRSVUUsDQogIGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gVFJVRSwgbWVzc2FnZSA9IFRSVUUsIGNhY2hlID0gVFJVRSAgICAgICAjIGVjaG8gPSBGYWxzZSBmb3IgZ2l0aHViX2RvY3VtZW50LCBidXQgd2lsbCBiZSBmb2xkZWQgaW4gaHRtbF9ub3RlYm9vaw0KKQ0KDQojIEdlbmVyYWwgUiBvcHRpb25zIGFuZCBpbmZvDQpzZXQuc2VlZCgxMTEpICAgICAgICAgICAgICAgIyBpbiBjYXNlIHdlIHVzZSByYW5kb21pemVkIHByb2NlZHVyZXMgICAgICAgDQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgICAgICAgIyBwb3NpdGl2ZSB2YWx1ZXMgYmlhcyB0b3dhcmRzIGZpeGVkIGFuZCBuZWdhdGl2ZSB0b3dhcmRzIHNjaWVudGlmaWMgbm90YXRpb24NCg0KIyBMb2FkIHBhY2thZ2VzDQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KcGFja2FnZXMgPC0gYygNCiAgInRpZHl2ZXJzZSIsICAgICAgIyBiZXN0IHRoaW5nIHRoYXQgaGFwcGVuZCB0byBtZQ0KICAicHN5Y2giLCAgICAgICAgICAjIGdlbmVyYWwgcHVycG9zZSB0b29sYm94IGZvciBwZXJzb25hbGl0eSwgcHN5Y2hvbWV0cmljIHRoZW9yeSBhbmQgZXhwZXJpbWVudGFsIHBzeWNob2xvZ3kNCiAgInBhcGFqYSIsICAgICAgICAgIyBmb3IgQVBBIHN0eWxlDQogICJicm9vbSIsICAgICAgICAgICMgZm9yIHRpZHkgbW9kZWxsaW5nDQogICJnZ3Bsb3QyIiwgICAgICAgICMgYmVzdCBwbG90cw0KICAiZ2dwdWJyIiwgICAgICAgICAjIGdncGxvdDIgdG8gcHVibGljYXRpb24gcXVhbGl0eQ0KICAiRFQiLCAgICAgICAgICAgICAjIG5pY2Ugc2VhcmNoYWJsZSBhbmQgZG93bmxvYWRhYmxlIHRhYmxlcw0KICAic3VtbWFyeXRvb2xzIiwNCiAgInBseXIiLCANCiAgInN0cmluZ3IiDQogICMgLCAuLi4NCikNCmlmICghcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpDQpwYWNtYW46OnBfbG9hZChjaGFyID0gcGFja2FnZXMpDQoNCiMgVGhlbWVzIGZvciBnZ3Bsb3QyIHBsb3RpbmcgKGhlcmUgdXNlZCBBUEEgc3R5bGUpDQp0aGVtZV9zZXQodGhlbWVfYXBhKCkpDQpgYGANCg0KYGBge3Igd29ya2luZ19kaXJlY3RvcnksIGluY2x1ZGUgPSBGQUxTRX0NCiMjIyBPLjQgUiBjb2RlIC0gVW5pY2UgYW5kIFJldGF0YXRlDQojIyMgUiBjb2RlIGZvciBzb3J0aW5nLCBpbnRlZ3JhdGluZyBhbmQgYW5hbHlzZXMgDQojIFRhc2sgb3V0cHV0IGZpbGVzOiANCiMgSUQsIGV4cGVyaW1lbnRhbCBjb25kaXRpb24gKCJvZ2xpbmRhIiAvICJlY3JhbiIpLCBjb25kaXRpb24gKCJpbnN0cnVjdG9yIiwgInNvbG8iKSwgdHlwZSBvZiB0YXNrICgidW5pYyIsICJyZXBldGF0IikNCndkIDwtICJDOi9Vc2Vycy9NaWhhaS9EZXNrdG9wL0VFR19PLjRjXzEvTy40YyAtIFBvc3RlciINCnNldHdkKHdkKQ0KYGBgDQoNCg0KPCEtLSBSZXBvcnQgLS0+DQoNCg0KIyBBcnJhbmdlIGZvbGRlcnMsIHNvcnQgZmlsZXMsIHJlYWQgYW5kIG1lcmdlDQoNCiMjIENyZWF0ZSBmb2xkZXJzIGJ5IGNvbmRpdGlvbnMsIGNvcHkgZmlsZXMgdG8gdGhlbQ0KDQpgYGB7ciBtYWtlZGlyX3NvcnRmaWxlcywgZXZhbD1GQUxTRX0NCiMgISEhIUVWQUwgPSBGQUxTRQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIFJlYWQgZmlsZSBuYW1lcyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpmaWxlX25hbWVzIDwtIGRpcihwYXR0ZXJuID0gIlxcLnhscyQiKQ0KIyMgaWYgYWJvdmUgaXNuJ3QgZ29vZCBlbm91Z2ggdHJ5IHRoZSBmb2xsb3dpbmc6DQojIGZpbGVfbmFtZXMgPC0gbGlzdC5maWxlcyh3ZCkNCiMgZmlsZV9uYW1lcyA8LSBzb3BfZmlsZXNbIWZpbGUuaW5mbyhzb3BfZmlsZXMpJGlzZGlyXSAgICMgZXhjbHVkZSBkaXJlY3Rvcmllcw0KIyBmaWxlX25hbWVzIDwtIHNvcF9maWxlc1tncmVwKCIueGxzIiwgc29wX2ZpbGVzLCBmaXhlZCA9IFRSVUUpXQ0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyBDcmVhdGUgZm9sZGVycyB3aXRoIENvbmRpdGlvbiBuYW1lcyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgdGhpcyBwYXJ0IG9mIHNjcmlwdCBtYXkgYmUgcmUtcnVuIGlmIGZpbGVzIGZyb20gd2QgYXJlIHVwZGF0ZWQNCmRpcl9uYW1lcyA8LSBjKCJVbmljX0NUUkxfSW5zdHIiLCAiVW5pY19DVFJMX1NvbG8iLCAiVW5pY19PR0xfSW5zdHIiLCAiVW5pY19PR0xfU29sbyIsDQogICAgICAgICAgICAgICJSZXBldGF0X0NUUkxfSW5zdHIiLCAiUmVwZXRhdF9DVFJMX1NvbG8iLCAiUmVwZXRhdF9PR0xfSW5zdHIiLCAiUmVwZXRhdF9PR0xfU29sbyIpDQogICAgICAgICAgICAgDQpmb3IoZGlyIGluIGRpcl9uYW1lcyl7DQogIGlmKCFkaXIuZXhpc3RzKGZpbGUucGF0aCh3ZCwgZGlyKSkpDQogIGRpci5jcmVhdGUoZmlsZS5wYXRoKHdkLCBkaXIpLCBzaG93V2FybmluZ3MgPSBGQUxTRSkNCn0NCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMgVXNlIGZpbGUgbmFtZXMgdG8gc29ydCB0aGVtIHRvIGZvbGRlcnMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpzb3J0X2ZpbGVzX3RvX2RpcnMgPC0gZnVuY3Rpb24od2QsIHBhdHRlcm4sIGRpcikgew0KICBjaGVja19wYXR0ZXJuIDwtIG91dGVyKGZpbGVfbmFtZXMsIHBhdHRlcm4sIHN0cmluZ3I6OnN0cl9kZXRlY3QpICAgICAgICAgICAgICAgIyBpZiBhbGwgVFJVRSBieWUgcm93IHRoZW4gaXQgaGFzIGZ1bGwgcGF0dGVybg0KICBpbmRleCA8LSB3aGljaChhcHBseShjaGVja19wYXR0ZXJuLCAxLCBmdW5jdGlvbih4KSBhbGwoeD09VFJVRSkpKSAgICAgICAgICAgICAgIyBnZXQgaW5kZXggb2YgZmlsZV9uYW1lcyB3aGVyZSBhbGwgYXJlIFRSVUUNCiAgc29ydGVkX2ZpbGVzIDwtIGZpbGVfbmFtZXNbaW5kZXhdICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ2V0IG5hbWVzIG9mIGZpbGVzIGZyb20gaW5kZXhlcw0KICANCiAgZm9yKGZpbGVzIGluIHNvcnRlZF9maWxlcykgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY29weSB0aGUgZmlsZXMgdG8gY29ycmVzcG9uZGluZyBmb2xkZXINCiAgICBmaWxlLmNvcHkoZnJvbSA9IGZpbGUucGF0aCh3ZCwgZmlsZXMpLCB0byA9IGZpbGUucGF0aCh3ZCwgZGlyKSkNCiAgfSAgDQp9DQoNCnNvcnRfZmlsZXNfdG9fZGlycyh3ZCA9IHdkLCBwYXR0ZXJuID0gYygidW5pYyIsICJlY3JhbiIsICJpbnN0cnVjdG9yIiksIGRpciA9ICJVbmljX0NUUkxfSW5zdHIiKQ0Kc29ydF9maWxlc190b19kaXJzKHdkID0gd2QsIHBhdHRlcm4gPSBjKCJ1bmljIiwgImVjcmFuIiwgInNvbG8iKSwgZGlyID0gIlVuaWNfQ1RSTF9Tb2xvIikNCnNvcnRfZmlsZXNfdG9fZGlycyh3ZCA9IHdkLCBwYXR0ZXJuID0gYygidW5pYyIsICJvZ2xpbmRhIiwgImluc3RydWN0b3IiKSwgZGlyID0gIlVuaWNfT0dMX0luc3RyIikNCnNvcnRfZmlsZXNfdG9fZGlycyh3ZCA9IHdkLCBwYXR0ZXJuID0gYygidW5pYyIsICJvZ2xpbmRhIiwgInNvbG8iKSwgZGlyID0gIlVuaWNfT0dMX1NvbG8iKQ0KYGBgDQoNCg0KIyMgUmVhZCBQaWxvdCBEYXRhICh0aGVzZSBhcmUgb24gVkFTKQ0KYGBge3IgcGlsb3RfZGF0YSwgaGlkZT1UUlVFfQ0Kd2QgPC0gIkM6L1VzZXJzL01paGFpL0Rlc2t0b3AvRUVHX08uNGNfMS9PLjRjIC0gUG9zdGVyL0RhdGUgUGlsb3QiDQpzZXR3ZCh3ZCkNCmZvbGRlcnMgPC0gbGlzdC5maWxlcyh3ZCkNCmZvbGRlcnMgPC0gZm9sZGVyc1tmaWxlLmluZm8oZm9sZGVycykkaXNkaXJdICAgIyBsdWFtIGRvYXIgZm9sZGVyZWxlDQpkYXRhc2V0bmFtZXMgPC0gTlVMTA0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoZm9sZGVycykpIHsNCiAgZGF0YXNldG5hbWUgPC0gZm9sZGVyc1tpXQ0KICBkYXRhc2V0bmFtZXMgPC0gYyhkYXRhc2V0bmFtZXMsIGRhdGFzZXRuYW1lKQ0KICBjdXJyZW50X2RpciA8LSBzZXR3ZChmaWxlLnBhdGgod2QsIGZvbGRlcnNbaV0pKQ0KICBwcmludChwYXN0ZTAoImN1cnJlbnRfZGlyOiAiLCBjdXJyZW50X2RpcikpDQogIA0KICBwYXRocyA8LSBkaXIocGF0dGVybiA9ICJcXC54bHMkIikNCiAgbmFtZXMocGF0aHMpIDwtIGJhc2VuYW1lKHBhdGhzKQ0KDQogIGFzc2lnbiggcGFzdGUoZGF0YXNldG5hbWUpLCBwbHlyOjpsZHBseShwYXRocywgcmlvOjppbXBvcnQpICkNCn0NCg0Kc2V0d2Qod2QpDQoNCiMjIEZ1bmN0aW9uIGZvciBQaWxvdCBEYXRhIHByb2Nlc3NpbmcNCnByb2Nlc3NfcGlsb3RfZGF0YSA8LSBmdW5jdGlvbihkZil7DQogIGRmX21vZGlmIDwtDQogICAgZGYgJT4lDQogICAgbXV0YXRlKE1hcmtlclN0aW11bGkgPSBjYXNlX3doZW4oYFN0aW11bGkgb3JkZXJgIDwgMzYgfiAxMSwgICAgICAgICAgICAgICAgICAjIFBpbG90IGRhdGEgd2FzIGNvcnJlY3RlIGR1ZSB0byBzdGltdWx1cyBvcmRlcmluZyBwcm9ibGVtcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBTdGltdWxpIG9yZGVyYCA+PTM2ICYgYFN0aW11bGkgb3JkZXJgIDw9IDcwIH4gMjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFN0aW11bGkgb3JkZXJgID4gNzAgfiAzMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFICAgICAgICAgICAgICAgICB+IGFzLm51bWVyaWMoTkEpKSkgJT4lICAgDQogICAgbXV0YXRlKGBTdGltdWx1cyB0eXBlYCA9IGNhc2Vfd2hlbihNYXJrZXJTdGltdWxpICVpbiUgYygxMSwgMTIpIH4gIm5lZ2F0aXYiLCAgICAgICAgICAgIyBMZWZ0IHRoZSBvbGQgY29kaW5nIGFsc28gYWx0b3VnaCB3ZSBoYXZlIG9ubHkgMTEsMjEsMzENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1hcmtlclN0aW11bGkgJWluJSBjKDIxLCAyMikgfiAibmV1dHJ1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1hcmtlclN0aW11bGkgJWluJSBjKDMxLCAzMikgfiAicG96aXRpdiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFICAgICAgICAgICAgICAgICAgICAgICAgIH4gYXMuY2hhcmFjdGVyKE5BKSkpICU+JQ0KICAgIGZpbHRlcihgU3RpbXVsdXMgdHlwZWAgIT0gInBveml0aXYiKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGV4Y2x1ZGUgcG9zaXRpdmUgc3RpbXVseSBiZWNhdXNlIG9ubHkgMSBzdWJqZWN0IGhhcyB0aGVtDQogIA0KICBkZiA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoZGYpKQ0KICBhc3NpZ24oZGYsIGRmX21vZGlmLCBlbnZpciA9IGdsb2JhbGVudigpKQ0KfQ0KDQojIyBQcm9jZXNzIFBpbG90IERhdGENCnByb2Nlc3NfcGlsb3RfZGF0YShQaWxvdF9VbmljX0NUUkxfSW5zdHIpDQpwcm9jZXNzX3BpbG90X2RhdGEoUGlsb3RfVW5pY19DVFJMX1NvbG8pDQpwcm9jZXNzX3BpbG90X2RhdGEoUGlsb3RfVW5pY19PR0xfSW5zdHIpDQpwcm9jZXNzX3BpbG90X2RhdGEoUGlsb3RfVW5pY19PR0xfU29sbykNCg0KIyMgTm93IHdlIHdvcmsgb25seSB3aXRoIFBpbG90IERhdGENClVuaWNfQ1RSTF9JbnN0ciA8LSBQaWxvdF9VbmljX0NUUkxfSW5zdHINClVuaWNfQ1RSTF9Tb2xvIDwtIFBpbG90X1VuaWNfQ1RSTF9Tb2xvDQpVbmljX09HTF9JbnN0ciA8LSBQaWxvdF9VbmljX09HTF9JbnN0cg0KVW5pY19PR0xfU29sbyA8LSBQaWxvdF9VbmljX09HTF9Tb2xvDQpgYGANCg0KDQojIyBDbGVhbmluZyB0aGUgZGF0YQ0KDQpgYGB7ciBjbGVhbl9kYXRhLCBoaWRlPVRSVUV9DQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgRGF0YSBDbGVhbmluZyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgQ2hlY2sgaWYgaWRzIGhhdmUgPiAxIHJvdyBvZiBkYXRhIChlbXB0eSAueGxzIGhhdmUgb25seSAxIHJvdykNCiMgQ2FyZWZ1bCEgVGhpcyBmdW5jdGlvbiBtb2RmaWVzIHRoZSBkYXRhc2V0cyBpbiB0aGUgZ2xvYmFsIGVudmlucm9ubWVudA0KZGVsZXRlX2VtcHR5X2lkIDwtIGZ1bmN0aW9uKGRmKXsNCiAgbGlzdF9lbXB0eV9pZCA8LSANCiAgICBkZiAlPiUNCiAgICBkcGx5cjo6Z3JvdXBfYnkoLmlkKSAlPiUNCiAgICBkcGx5cjo6c3VtbWFyaXNlKHJvd19jb3VudCA9IG4oKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZSgiZW1wdHlfaWQiID0gLmlkKSAlPiUNCiAgICBtdXRhdGUoZGVsZXRlX2lkID0gaWZfZWxzZShyb3dfY291bnQgPCAzLCBUUlVFLCBGQUxTRSkpICU+JQ0KICAgIGZpbHRlcihkZWxldGVfaWQgPT0gVFJVRSkNCiAgDQogIGRmX21vZGlmIDwtIA0KICAgIGRmICU+JQ0KICAgIGZpbHRlcighLmlkICVpbiUgbGlzdF9lbXB0eV9pZCRlbXB0eV9pZCkNCiAgDQogIGlmKCFpZGVudGljYWwoZGYsIGRmX21vZGlmKSl7DQogICAgZGYgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGRmKSkNCiAgICBjYXQoIkRlbGV0aW5nIGZyb20gIiwgcHJpbnQoYXMubmFtZShkZikpKTsgcHJpbnQobGlzdF9lbXB0eV9pZCkgICAgICAgICAgICAgICAgICAgICMgcHJpbnQgb3V0IHdoaWNoIGlkcyBhcmUgZGVsZXRlZCBmcm9tIHdoaWNoIGRhdGFzZXQNCiAgICBhc3NpZ24oZGYsIGRmX21vZGlmLCBlbnZpciA9IGdsb2JhbGVudigpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYXNzaWduIG1vZGlmaWVkIGRmIHRvIG9yaWdpbmFsIGRhdGFzZXQgZnJvbSBHbG9iYWwNCiAgfWVsc2UgY2F0KCJObyBlbXB0eSBkYXRhc2V0cy4gTm90aGluZyB0byBkZWxldGUiKQ0KfQ0KDQojIEFwcGx5IGZ1bmN0aW9uIHRvIGFsbCBkYXRhc2V0cyAodHJpY2t5IHRvIGRvIGluIGZvciBsb29wIGJlY2F1c2Ugb2Ygc3VwZXIgYXNzaWdubWVudCkNCmRlbGV0ZV9lbXB0eV9pZChVbmljX0NUUkxfSW5zdHIpDQpkZWxldGVfZW1wdHlfaWQoVW5pY19DVFJMX1NvbG8pDQpkZWxldGVfZW1wdHlfaWQoVW5pY19PR0xfSW5zdHIpDQpkZWxldGVfZW1wdHlfaWQoVW5pY19PR0xfU29sbykNCmBgYA0KDQoNCiMjIEV4Y2x1ZGUgVkFTX1Jlc3AgYmFzZWQgb24gUlQgb3V0bGllcnMNCg0KYGBge3Igbm9vdXRsaWVyX2RhdGEsIGV2YWw9RkFMU0V9DQojICEhISFFVkFMID0gRkFMU0UNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgRXhjbHVkZSBPdXRsaWVycyBiYXNlZCBvbiBSVCAoYnkgc3ViamVjdCBhbmQgc3RpbXVsdXMgdHlwZSkgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyBET05UIFJVTiAodW5sZXNzIGl0IGlzIG5lZWRlZCkgLS0tLT4gZXZhbD1GQUxTRQ0KIyBFeGNsdWRlIFJUIG91dGxpZXJzICg9LSAyU0QpIC0gaW5zdGVhZCBvZiBzaW1wbGUgZmlsdGVyLCBtYWtlaW5nIHRoZW0gTkEgIGlzIGJldHRlciBmb3IgcGFpcmVkIGNvbXBhcmlzb24NCnJlbW92ZV9vdXRsaWVycyA8LSBmdW5jdGlvbihkZikgew0KICBkZl9tb2RpZiA8LQ0KICAgIGRmICU+JQ0KICAgIGRwbHlyOjpncm91cF9ieSguaWQsIGBTdGltdWx1cyB0eXBlYCkgJT4lICAgICAgICAgICAgICAgICAgIyB3ZSBjb3VsZCBoYXZlIGRvbmUgYmVmb3JlOiAgZHBseXI6OnJlbmFtZSgiU3RpbV90eXBlIiA9IGBTdGltdWx1cyB0eXBlYCkgDQogICAgbXV0YXRlKFZBU19SZXNwID0gaWZfZWxzZShhYnMoVkFTX1JUIC0gbWVhbihWQVNfUlQsIG5hLnJtPVRSVUUpKSA+IDIqc2QoVkFTX1JULCBuYS5ybT1UUlVFKSwgYXMubnVtZXJpYyhOQSksIFZBU19SZXNwKSkNCiAgDQogIGlmKCFpZGVudGljYWwoZGYsIGRmX21vZGlmKSl7DQogICAgZGYgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGRmKSkNCiAgICBjYXQoIkRlbGV0aW5nIG91dGxpZXJzIGZyb20gIiwgcHJpbnQoYXMubmFtZShkZikpKTsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBwcmludCBvdXQgZGF0YXNldHMgd2hpY2ggaGF2ZSBiZWVuIG1vZGlmaWVkDQogICAgYXNzaWduKGRmLCBkZl9tb2RpZiwgZW52aXIgPSBnbG9iYWxlbnYoKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFzc2lnbiBtb2RpZmllZCBkZiB0byBvcmlnaW5hbCBkYXRhc2V0IGZyb20gR2xvYmFsDQogIH1lbHNlIGNhdCgiTm8gb3V0bGllciIpDQp9DQoNCnJlbW92ZV9vdXRsaWVycyhVbmljX0NUUkxfSW5zdHIpDQpyZW1vdmVfb3V0bGllcnMoVW5pY19DVFJMX1NvbG8pDQpyZW1vdmVfb3V0bGllcnMoVW5pY19PR0xfSW5zdHIpDQpyZW1vdmVfb3V0bGllcnMoVW5pY19PR0xfU29sbykNCmBgYA0KDQoNCiMjIFRlc3QgaWYgZGF0YXNldHMgaGF2ZSBzYW1lIGNvbHVtbnMNCg0KYGBge3IgdGVzdF9jb2xzfQ0KdW5pY19kZl9vYmogPC0gbWdldChjKCJVbmljX0NUUkxfSW5zdHIiLCAiVW5pY19DVFJMX1NvbG8iLCAiVW5pY19PR0xfSW5zdHIiLCAiVW5pY19PR0xfU29sbyIpKQ0KdW5pY19kZl9vYmogPC1sYXBwbHkodW5pY19kZl9vYmosIGNvbG5hbWVzKQ0Kb3V0ZXIodW5pY19kZl9vYmosIHVuaWNfZGZfb2JqLCBWZWN0b3JpemUoaWRlbnRpY2FsKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGlmIGFsbCBhcmUgVFJVRSwgYWxsIGRmIGhhdmUgc2FtZSBjb2x1bW5zDQpgYGANCg0KPGJyPg0KPGJyPg0KDQoNCiMgQW5hbHlzaXMgLSBVTklDRQ0KDQojIyBEZXNjcmlwdGl2ZXMNCg0KYGBge3IgZGVzY191bmljZX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgQW5hbHlzZXMgLSBVTklDRSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgRGVzY3JpcHRpdmVzIGJ5IGNvbmRpdGlvbiBkYXRhc2V0DQpkZXNjcmlwdGl2ZV9mdW5jIDwtIGZ1bmN0aW9uKGRmLCBTdGltX3R5cGUsIEJ5X0lEID0gRkFMU0Upew0KICBkZl9uYW1lIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShkZikpDQogIHN1cHByZXNzV2FybmluZ3MoeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGlmIGFsbCBOQXMgaW4gVkFTX1Jlc3AsIE5hTnMgYW5kIEluZnMgd2lsbCBiZSBwcm9kdWNlZA0KICAgIGRmX21vZGlmIDwtIA0KICAgICAgZGYgJT4lDQogICAgICBkcGx5cjo6cmVuYW1lKCJJRCIgPSAuaWQpICU+JQ0KICAgICAgc2VsZWN0X2FsbCh+Z3N1YigiXFxzK3xcXC4iLCAiXyIsIC4pKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyByZXBsYWNlcyBibGFuY2tzIHdpdGggIl8iIGluIGNvbG5hbWVzIA0KICAgICAgZmlsdGVyKFN0aW11bHVzX3R5cGUgPT0gU3RpbV90eXBlKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmaWx0ZXIgYnkgc3RpbXVsdXMgdHlwZQ0KICAgICAgDQogICAgaWYoaXNUUlVFKEJ5X0lEKSl7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGlmIHRydWUgZ3JvdXAgYnkgaWQsIGlmIG5vdCByZXR1cm4gZGVzY3JpcHRpdmVzIGZvciBhbGwgaWRzDQogICAgICBieV9pZF90ZXh0IDwtICIgYnkgc3ViamVjdCINCiAgICAgIGRmX21vZGlmICU+JQ0KICAgICAgZHBseXI6Omdyb3VwX2J5KElEKSAlPiUNCiAgICAgIHRpZHlzdGF0czo6ZGVzY3JpYmVfZGF0YShWQVNfUmVzcCwgVkFTX1JULCBuYS5ybSA9IFRSVUUpICU+JQ0KICAgICAgICBrbml0cjo6a2FibGUoY2FwdGlvbiA9IHBhc3RlKGFzLm5hbWUoZGZfbmFtZSksICIgIiwgU3RpbV90eXBlLCBieV9pZF90ZXh0KSwgZm9ybWF0ID0gInBhbmRvYyIsIGRpZ2l0cyA9IDIpDQogICAgfWVsc2V7IA0KICAgICAgYnlfaWRfdGV4dCA8LSAiIGFsbCBzdWJqZWN0cyINCiAgICAgIGRmX21vZGlmICU+JQ0KICAgICAgdW5ncm91cCgpICU+JSAgDQogICAgICB0aWR5c3RhdHM6OmRlc2NyaWJlX2RhdGEoVkFTX1Jlc3AsIFZBU19SVCwgbmEucm0gPSBUUlVFKSAlPiUNCiAgICAgICAga25pdHI6OmthYmxlKGNhcHRpb24gPSBwYXN0ZShhcy5uYW1lKGRmX25hbWUpLCAiICIsIFN0aW1fdHlwZSwgYnlfaWRfdGV4dCksIGZvcm1hdCA9ICJwYW5kb2MiLCBkaWdpdHMgPSAyKQ0KICAgIH0NCiAgfSkNCn0gIA0KDQoNCmRlc2NyaXB0aXZlX2Z1bmMoZGYgPSBVbmljX0NUUkxfSW5zdHIsIFN0aW1fdHlwZSA9ICJuZWdhdGl2IiwgQnlfSUQgPSBGQUxTRSkgICAgICAgICAgICMgTmVnYXRpdmUgLSBHZW5lcmFsDQpkZXNjcmlwdGl2ZV9mdW5jKGRmID0gVW5pY19DVFJMX1NvbG8sIFN0aW1fdHlwZSA9ICJuZWdhdGl2IiwgQnlfSUQgPSBGQUxTRSkNCmRlc2NyaXB0aXZlX2Z1bmMoZGYgPSBVbmljX09HTF9JbnN0ciwgU3RpbV90eXBlID0gIm5lZ2F0aXYiLCBCeV9JRCA9IEZBTFNFKQ0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfT0dMX1NvbG8sIFN0aW1fdHlwZSA9ICJuZWdhdGl2IiwgQnlfSUQgPSBGQUxTRSkNCg0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfQ1RSTF9JbnN0ciwgU3RpbV90eXBlID0gIm5lZ2F0aXYiLCBCeV9JRCA9IFRSVUUpICAgICAgICAgICAgIyBOZWdhdGl2ZSAtIGJ5IGlkDQpkZXNjcmlwdGl2ZV9mdW5jKGRmID0gVW5pY19DVFJMX1NvbG8sIFN0aW1fdHlwZSA9ICJuZWdhdGl2IiwgQnlfSUQgPSBUUlVFKQ0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfT0dMX0luc3RyLCBTdGltX3R5cGUgPSAibmVnYXRpdiIsIEJ5X0lEID0gVFJVRSkNCmRlc2NyaXB0aXZlX2Z1bmMoZGYgPSBVbmljX09HTF9Tb2xvLCBTdGltX3R5cGUgPSAibmVnYXRpdiIsIEJ5X0lEID0gVFJVRSkNCg0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfQ1RSTF9JbnN0ciwgU3RpbV90eXBlID0gIm5ldXRydSIsIEJ5X0lEID0gRkFMU0UpICAgICAgICAgICAjIE5ldXRyYWwgLSBHZW5lcmFsDQpkZXNjcmlwdGl2ZV9mdW5jKGRmID0gVW5pY19DVFJMX1NvbG8sIFN0aW1fdHlwZSA9ICJuZXV0cnUiLCBCeV9JRCA9IEZBTFNFKQ0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfT0dMX0luc3RyLCBTdGltX3R5cGUgPSAibmV1dHJ1IiwgQnlfSUQgPSBGQUxTRSkNCmRlc2NyaXB0aXZlX2Z1bmMoZGYgPSBVbmljX09HTF9Tb2xvLCBTdGltX3R5cGUgPSAibmV1dHJ1IiwgQnlfSUQgPSBGQUxTRSkNCg0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfQ1RSTF9JbnN0ciwgU3RpbV90eXBlID0gIm5ldXRydSIsIEJ5X0lEID0gVFJVRSkgICAgICAgICAgICAjIE5ldXRyYWwgLSBieSBpZA0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfQ1RSTF9Tb2xvLCBTdGltX3R5cGUgPSAibmV1dHJ1IiwgQnlfSUQgPSBUUlVFKQ0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfT0dMX0luc3RyLCBTdGltX3R5cGUgPSAibmV1dHJ1IiwgQnlfSUQgPSBUUlVFKQ0KZGVzY3JpcHRpdmVfZnVuYyhkZiA9IFVuaWNfT0dMX1NvbG8sIFN0aW1fdHlwZSA9ICJuZXV0cnUiLCBCeV9JRCA9IFRSVUUpDQpgYGANCg0KDQojIyBNZXJnZQ0KDQpgYGB7ciBtZXJnZWRfdW5pY2VfZGF0YSwgaGlkZT1UUlVFfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIE1lcmdlIGNvbmRpdGlvbiBkYXRhc2V0ICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBNdXN0IGZpcnN0IHJlbmFtZSAuaWQgdG8gSUQgaW4gb2RlciB0byBoYXZlIC5pZCBmb3IgZGYgbmFtZXMNCklEX3JlbmFtZSA8LSBmdW5jdGlvbihkZil7DQogIGlmKCIuaWQiICVpbiUgY29sbmFtZXMoZGYpKSB7DQogICAgZGZfbW9kaWYgPC0gDQogICAgICBkZiAlPiUNCiAgICAgIGRwbHlyOjpyZW5hbWUoIklEIiA9IC5pZCkNCiAgICBkZiA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoZGYpKQ0KICAgIGNhdCgiQ2hhbmdlZCAuaWQgdG8gSUQgZm9yOiAiLCBhcy5uYW1lKGRmKSkNCiAgICBhc3NpZ24oZGYsIGRmX21vZGlmLCBlbnZpciA9IGdsb2JhbGVudigpKQ0KICB9ICANCn0NCg0KSURfcmVuYW1lKFVuaWNfQ1RSTF9JbnN0cikNCklEX3JlbmFtZShVbmljX0NUUkxfU29sbykNCklEX3JlbmFtZShVbmljX09HTF9JbnN0cikNCklEX3JlbmFtZShVbmljX09HTF9Tb2xvKQ0KDQojIE1lcmdlIGludG8gb25lIGRmDQpsaXN0X2RmX21lcmdlIDwtIGxpc3QoVW5pY19DVFJMX0luc3RyLCBVbmljX0NUUkxfU29sbywgVW5pY19PR0xfSW5zdHIsIFVuaWNfT0dMX1NvbG8pDQpuYW1lcyhsaXN0X2RmX21lcmdlKSA8LSBjKCJVbmljX0NUUkxfSW5zdHIiLCAiVW5pY19DVFJMX1NvbG8iLCAiVW5pY19PR0xfSW5zdHIiLCAiVW5pY19PR0xfU29sbyIpDQpVbmljX21lcmdlZCA8LSBwbHlyOjpsZHBseShsaXN0X2RmX21lcmdlLCBkYXRhLmZyYW1lKSAgICAgICAgICAgICAgICAgICAgIyBhbHNvIHdvcmtzIGZvciB0aGlzIGpvYiBiaW5kX3Jvd3MobGlzdF9kZl9tZXJnZSwgLmlkID0gImNvbHVtbl9sYWJlbCIpDQpgYGANCg0KDQojIyBBbmFseXNlcyBvbiBtZXJnZWQgKEFub3ZhICYgcG9zdC1ob2MpDQoNCmBgYHtyIGFub3ZhX3VuaWNlLCByZXN1bHRzPSdhc2lzJ30NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyBBbmFseXNlcyBvbiBNZXJnZWQgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgSnVzdCBhIFRlc3QgDQogICMgVW5pY19tZXJnZWRfc3ByZWFkX05lZyA8LSANCiAgIyAgIFVuaWNfbWVyZ2VkICU+JQ0KICAjICAgZmlsdGVyKCFpcy5uYShWQVNfUmVzcCkpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNvbWUgZmlsZXMgaGFkIG9ubHkgTkEgb24gVkFTX1Jlc3AgYW5kIFZBU19SVA0KICAjICAgc2VsZWN0KC5pZCwgSUQsIFN1YmpfaWQsIA0KICAjICAgICAgICAgIFN0aW11bGkub3JkZXIsIE1hcmtlclN0aW11bGksIFN0aW11bHVzLnR5cGUsIA0KICAjICAgICAgICAgIFZBU19SZXNwLCBWQVNfUlQpICU+JQ0KICAjICAgZmlsdGVyKFN0aW11bHVzLnR5cGUgPT0gIm5lZ2F0aXYiKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRvbnQgZm9yZ2V0IHRvIHBpY2sgc3R5bXVsdXMgdHlwZQ0KICAjICAgc3ByZWFkKC5pZCwgVkFTX1Jlc3ApDQogICMgDQogICMgdC50ZXN0KFVuaWNfbWVyZ2VkX3NwcmVhZF9OZWckVW5pY19DVFJMX0luc3RyLCBVbmljX21lcmdlZF9zcHJlYWRfTmVnJFVuaWNfQ1RSTF9Tb2xvLCBuYS5ybSA9IFRSVUUpDQogICMgdC50ZXN0KFVuaWNfbWVyZ2VkX3NwcmVhZF9OZWckVW5pY19PR0xfSW5zdHIsIFVuaWNfbWVyZ2VkX3NwcmVhZF9OZWckVW5pY19PR0xfU29sbywgbmEucm0gPSBUUlVFKQ0KICAjIHQudGVzdChVbmljX21lcmdlZF9zcHJlYWRfTmVnJFVuaWNfT0dMX0luc3RyLCBVbmljX21lcmdlZF9zcHJlYWRfTmVnJFVuaWNfQ1RSTF9JbnN0ciwgbmEucm0gPSBUUlVFKQ0KICAjIHQudGVzdChVbmljX21lcmdlZF9zcHJlYWRfTmVnJFVuaWNfT0dMX1NvbG8sIFVuaWNfbWVyZ2VkX3NwcmVhZF9OZWckVW5pY19DVFJMX1NvbG8sIG5hLnJtID0gVFJVRSkNCg0KIyMgRnVuY3Rpb24gcHJlcGFpciBkYXRhIGZvciBhbmFseXNlcw0KcHJlcGFpcmVfbWVyZ2VkX2Z1bmMgPC0gZnVuY3Rpb24oU3RpbV90eXBlKXsNCiAgVW5pY19tZXJnZWQgJT4lDQogICAgZmlsdGVyKCFpcy5uYShWQVNfUmVzcCkpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNvbWUgZmlsZXMgaGFkIG9ubHkgTkEgb24gVkFTX1Jlc3AgYW5kIFZBU19SVA0KICAgIHNlbGVjdCguaWQsIElELCBTdWJqX2lkLCANCiAgICAgICAgICAgU3RpbXVsaS5vcmRlciwgTWFya2VyU3RpbXVsaSwgU3RpbXVsdXMudHlwZSwgDQogICAgICAgICAgIFZBU19SZXNwLCBWQVNfUlQpICU+JQ0KICAgIGRwbHlyOjpyZW5hbWUoQ29uZCA9IC5pZCkgJT4lIA0KICAgIGZpbHRlcihTdGltdWx1cy50eXBlID09IFN0aW1fdHlwZSkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBkb250IGZvcmdldCB0byBwaWNrIHN0eW11bHVzIHR5cGUNCiAgICBtdXRhdGUoQ29uZCA9IGFzLmZhY3RvcihDb25kKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdHVuciB0byBmYWN0b3IgZm9yIGFvdiBmYW1pbHkgZnVuY3Rpb25zDQp9DQoNClVuaWNfbWVyZ2VkX05lZyA8LSBwcmVwYWlyZV9tZXJnZWRfZnVuYygibmVnYXRpdiIpDQpVbmljX21lcmdlZF9OZXUgPC0gcHJlcGFpcmVfbWVyZ2VkX2Z1bmMoIm5ldXRydSIpDQpVbmljX21lcmdlZF9Qb3ogPC0gcHJlcGFpcmVfbWVyZ2VkX2Z1bmMoInBveml0aXYiKQ0KDQoNCiMjIEFub3ZhIGFuZCBQb3N0LUhvYyAgLSBORUdBVElWDQojIFN0aW11bHVzIHR5cGUNCmNhdCgiIyMjIE5lZ2F0aXYiKQ0KDQojIE5vcm1hbGl0eQ0KY2F0KCIjIyMjIE5vcm1hbGl0eSBUZXN0IikNClVuaWNfbWVyZ2VkX05lZyAlPiUNCiAgc2VsZWN0KFZBU19SZXNwKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbXVzdCBzZWxlY3QgdmFyaWFibGVzIG91dHNpZGUgZnVuY3Rpb24gDQogIHRhZGFhdG9vbGJveDo6dGFkYWFfbm9ybXRlc3QobWV0aG9kID0gInNoYXBpcm8iKSAgICAgICAgICAgICAgICAgICAgICAgICAjICwgcHJpbnQgPSAibWFya2Rvd24iICBmb3IgTm90ZWJvb2sNCg0KIyBMZXZlbmUgVGVzdCAocD4uMDUgPSBob21vZ2VuZWl0eSBvZiB2YXJpYW5jZXMpDQpjYXQoIiMjIyMgTGV2ZW5lIFRlc3QiKQ0KVW5pY19tZXJnZWRfTmVnICU+JQ0KICB0YWRhYXRvb2xib3g6OnRhZGFhX2xldmVuZShkYXRhID0gLiwgVkFTX1Jlc3AgfiBDb25kKSAgICAgICAgICAgICAgICAgICAgIyAsIHByaW50ID0gIm1hcmtkb3duIiAgZm9yIE5vdGVib29rDQoNCiMgQW5vdmENCmNhdCgiIyMjIyBBbm92YSIpDQpVbmljX21lcmdlZF9OZWcgJT4lDQogICNkbyhicm9vbTo6Z2xhbmNlKGFvdiguJFZBU19SZXNwIH4gLiRDb25kKSkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHJlZ3VsYXIgYW5vdmEgZG8oYnJvb206OnRpZHkoYW92KC4kVkFTX1Jlc3AgfiAuJENvbmQpKSkNCiAgdGFkYWF0b29sYm94Ojp0YWRhYV9hb3YoZGF0YSA9IC4sIFZBU19SZXNwIH4gQ29uZCwgdHlwZSA9IDEpICAgICAgICAgICAgICMgLCBwcmludCA9ICJtYXJrZG93biIgIGZvciBOb3RlYm9vaw0KDQojIFBvc3QtSG9jIA0KY2F0KCIjIyMjIFBvc3QtSG9jIEdhbWVzIEhvd2VsbCIpDQpVbmljX21lcmdlZF9OZWcgJT4lDQogICMgVHVrZXkgZm9yIGVxdWFsIHZhcmlhbmNlIA0KICB0YWRhYXRvb2xib3g6OnRhZGFhX3BhaXJ3aXNlX3R1a2V5KGRhdGEgPSAuLCBWQVNfUmVzcCwgQ29uZCkgICAgICAgICAgICAgIyAsIHByaW50ID0gIm1hcmtkb3duIiAgZm9yIE5vdGVib29rDQogICMgR2FtZXMgSG93ZWxsIGRvZXMgbm90IGFzc3VtZSBlcXVhbCB2YXJpYW5jZXMNCiAgI3RhZGFhdG9vbGJveDo6dGFkYWFfcGFpcndpc2VfZ2goZGF0YSA9IC4sIFZBU19SZXNwLCBDb25kKSAgICAgICAgICAgICAgICAjICwgcHJpbnQgPSAibWFya2Rvd24iICBmb3IgTm90ZWJvb2sNCg0KDQojIyBBbm92YSBhbmQgUG9zdC1Ib2MgIC0gTkVVVFJVDQojIFN0aW11bHVzIHR5cGUNCmNhdCgiIyMjIE5ldXRydSIpDQoNCiMgTm9ybWFsaXR5DQpjYXQoIiMjIyMgTm9ybWFsaXR5IFRlc3QiKQ0KVW5pY19tZXJnZWRfTmV1ICU+JQ0KICBzZWxlY3QoVkFTX1Jlc3ApICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBtdXN0IHNlbGVjdCB2YXJpYWJsZXMgb3V0c2lkZSBmdW5jdGlvbiANCiAgdGFkYWF0b29sYm94Ojp0YWRhYV9ub3JtdGVzdChtZXRob2QgPSAic2hhcGlybyIpICAgICAgICAgICAgICAgICAgICAgICAgICMgLCBwcmludCA9ICJtYXJrZG93biIgIGZvciBOb3RlYm9vaw0KDQojIExldmVuZSBUZXN0IChwPi4wNSA9IGhvbW9nZW5laXR5IG9mIHZhcmlhbmNlcykNCmNhdCgiIyMjIyBMZXZlbmUgVGVzdCIpDQpVbmljX21lcmdlZF9OZXUgJT4lDQogIHRhZGFhdG9vbGJveDo6dGFkYWFfbGV2ZW5lKGRhdGEgPSAuLCBWQVNfUmVzcCB+IENvbmQpICAgICAgICAgICAgICAgICAgICAjICwgcHJpbnQgPSAibWFya2Rvd24iICBmb3IgTm90ZWJvb2sNCg0KIyBBbm92YQ0KY2F0KCIjIyMjIEFub3ZhIikNClVuaWNfbWVyZ2VkX05ldSAlPiUNCiAgI2RvKGJyb29tOjpnbGFuY2UoYW92KC4kVkFTX1Jlc3AgfiAuJENvbmQpKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcmVndWxhciBhbm92YSBkbyhicm9vbTo6dGlkeShhb3YoLiRWQVNfUmVzcCB+IC4kQ29uZCkpKQ0KICB0YWRhYXRvb2xib3g6OnRhZGFhX2FvdihkYXRhID0gLiwgVkFTX1Jlc3AgfiBDb25kLCB0eXBlID0gMSkgICAgICAgICAgICAgIyAsIHByaW50ID0gIm1hcmtkb3duIiAgZm9yIE5vdGVib29rDQoNCiMgUG9zdC1Ib2MgDQpjYXQoIiMjIyMgUG9zdC1Ib2MgR2FtZXMgSG93ZWxsIikNClVuaWNfbWVyZ2VkX05ldSAlPiUNCiAgIyBUdWtleSBmb3IgZXF1YWwgdmFyaWFuY2UgDQogIHRhZGFhdG9vbGJveDo6dGFkYWFfcGFpcndpc2VfdHVrZXkoZGF0YSA9IC4sIFZBU19SZXNwLCBDb25kKSAgICAgICAgICAgICAjICwgcHJpbnQgPSAibWFya2Rvd24iICBmb3IgTm90ZWJvb2sNCiAgIyBHYW1lcyBIb3dlbGwgZG9lcyBub3QgYXNzdW1lIGVxdWFsIHZhcmlhbmNlcw0KICAjdGFkYWF0b29sYm94Ojp0YWRhYV9wYWlyd2lzZV9naChkYXRhID0gLiwgVkFTX1Jlc3AsIENvbmQpICAgICAgICAgICAgICAgICMgLCBwcmludCA9ICJtYXJrZG93biIgIGZvciBOb3RlYm9vaw0KYGBgDQoNCg0KIyMgUGxvdHMgd2l0aCBwIHZhbHVlcw0KYGBge3IgcGxvdF91bmljZSwgZmlnLmhlaWdodD03fQ0KIyBieSBkYXRhc2V0DQpnZ3Bsb3QoVW5pY19tZXJnZWQsIGFlcyh4ID0gU3RpbXVsdXMudHlwZSwgeSA9IFZBU19SZXNwKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2UsICBjb2xvdXIgPSAiZGFya3JlZCIpICsNCiAgeGxhYigiIikgKw0KICBmYWNldF93cmFwKH4uaWQpICsNCiAgZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gInQudGVzdCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLnNpZ25pZiIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIGF2b2lkIHNjaWVudGlmaWMgbm90YXRpb24gb2YgdmVyeSBzbWFsbCBwLXZhbHVlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjcGFpcmVkID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhcmlzb25zID0gbGlzdChjKCJuZWdhdGl2IiwgIm5ldXRydSIpKSkgIA0KDQojIGJ5IFN0aW11bHVzIHR5cGUNCmdncGxvdChVbmljX21lcmdlZCwgYWVzKHggPSAuaWQsIHkgPSBWQVNfUmVzcCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCAgY29sb3VyID0gImRhcmtyZWQiKSArDQogIHhsYWIoIiIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKw0KICBmYWNldF93cmFwKH5TdGltdWx1cy50eXBlKSArDQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLmZvcm1hdCIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvcm1hdGVkIHAtdmFsdWVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICNwYWlyZWQgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGFyaXNvbnMgPSBsaXN0KGMoIlVuaWNfQ1RSTF9JbnN0ciIsICJVbmljX0NUUkxfU29sbyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiVW5pY19DVFJMX0luc3RyIiwgIlVuaWNfT0dMX0luc3RyIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJVbmljX0NUUkxfU29sbyIsICJVbmljX09HTF9JbnN0ciIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiVW5pY19DVFJMX1NvbG8iLCAiVW5pY19PR0xfU29sbyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiVW5pY19PR0xfSW5zdHIiLCAiVW5pY19PR0xfU29sbyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiVW5pY19DVFJMX0luc3RyIiwgIlVuaWNfT0dMX1NvbG8iKSkpIA0KDQoNCiMgZHJvcCB0byBDVFJMIHZzIE9HTCAtIGJ5IFN0aW11bHVzIHR5cGUNClVuaWNfbWVyZ2VkICU+JQ0KICBtdXRhdGUoLmlkID0gY2FzZV93aGVuKC5pZCAlaW4lIGMoIlVuaWNfQ1RSTF9JbnN0ciIsICJVbmljX0NUUkxfU29sbyIpIH4gIlVuaWNfQ1RSTCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgLmlkICVpbiUgYygiVW5pY19PR0xfSW5zdHIiLCAiVW5pY19PR0xfU29sbyIpIH4gIlVuaWNfT0dMIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gYXMuY2hhcmFjdGVyKC5pZCkpKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKHggPSAuaWQsIHkgPSBWQVNfUmVzcCkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgIGNvbG91ciA9ICJkYXJrcmVkIikgKw0KICAgIHhsYWIoIiIpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogICAgZmFjZXRfd3JhcCh+U3RpbXVsdXMudHlwZSkgKw0KICAgIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gInAuZm9ybWF0IiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9ybWF0ZWQgcC12YWx1ZXMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjcGFpcmVkID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGFyaXNvbnMgPSBsaXN0KGMoIlVuaWNfQ1RSTCIsICJVbmljX09HTCIpKSkgICAgICAgICAgDQoNCiMgZHJvcCB0byBJbnN0ciB2cyBTb2xvIC0gYnkgU3RpbXVsdXMgdHlwZQ0KVW5pY19tZXJnZWQgJT4lDQogIG11dGF0ZSguaWQgPSBjYXNlX3doZW4oLmlkICVpbiUgYygiVW5pY19DVFJMX0luc3RyIiwgIlVuaWNfT0dMX0luc3RyIikgfiAiVW5pY19JbnN0ciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgLmlkICVpbiUgYygiVW5pY19DVFJMX1NvbG8iLCAiVW5pY19PR0xfU29sbyIpIH4gIlVuaWNfU29sbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IGFzLmNoYXJhY3RlciguaWQpKSkgJT4lDQogICAgZ2dwbG90KGFlcyh4ID0gLmlkLCB5ID0gVkFTX1Jlc3ApKSArDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2UsICBjb2xvdXIgPSAiZGFya3JlZCIpICsNCiAgICB4bGFiKCIiKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKw0KICAgIGZhY2V0X3dyYXAoflN0aW11bHVzLnR5cGUpICsNCiAgICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAidC50ZXN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLmZvcm1hdCIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvcm1hdGVkIHAtdmFsdWVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI3BhaXJlZCA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhcmlzb25zID0gbGlzdChjKCJVbmljX0luc3RyIiwgIlVuaWNfU29sbyIpKSkgDQpgYGANCg0KDQojIERvd25sb2FkIERhdGENCg0KYGBge3IgZG93bmxvYWRfZGF0YX0NClVuaWNfbWVyZ2VkICU+JSANCiAgICBzZWxlY3QoLWMoMzoxMCkpICU+JQ0KICAgIERUOjpkYXRhdGFibGUoICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZXhjZWwgZG93bmxvYWRhYmxlICBEVCB0YWJsZQ0KICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywNCiAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAyMCwNCiAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFg9JzUwMHB4JywgDQogICAgICAgICAgICAgICAgICAgICBkb20gPSAnQmZydGlwJywgDQogICAgICAgICAgICAgICAgICAgICBidXR0b25zID0gYygnZXhjZWwnLCAiY3N2IikpKQ0KYGBgDQoNCg0KDQoNCjwhLS0gU2Vzc2lvbiBJbmZvIGFuZCBMaWNlbnNlIC0tPg0KDQo8YnI+DQoNCiMgU2Vzc2lvbiBJbmZvDQpgYGB7ciBzZXNzaW9uX2luZm8sIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9ICdtYXJrdXAnfQ0Kc2Vzc2lvbkluZm8oKSAgICANCmBgYA0KDQo8IS0tIEZvb3RlciAtLT4NCiZuYnNwOw0KPGhyIC8+DQo8cCBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+QSB3b3JrIGJ5IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9DbGF1ZGl1UGFwYXN0ZXJpLyI+Q2xhdWRpdSBQYXBhc3Rlcmk8L2E+PC9wPg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIHN0eWxlPSJjb2xvcjogIzgwODA4MDsiPjxlbT5jbGF1ZGl1LnBhcGFzdGVyaUBnbWFpbC5jb208L2VtPjwvc3Bhbj48L3A+DQombmJzcDsNCg==