1 Define functions

## Define function that recodes to numeric, but watches out to coercion to not introduce NAs
colstonumeric <- function(df){
  tryCatch({
    df_num <- as.data.frame(
      lapply(df,
             function(x) { as.numeric(as.character(x))})) 
  },warning = function(stop_on_warning) {
    message("Stoped the execution of numeric conversion: ", conditionMessage(stop_on_warning))
  }) 
}
##
## Define function that reverse codes items
ReverseCode <- function(df, tonumeric = FALSE, min = NULL, max = NULL) {
  if(tonumeric) df <- colstonumeric(df)
  df <- (max + min) - df
}
##
## Define function that scores only rows with less than 10% NAs (returns NA if all or above threshold percentage of rows are NA); can reverse code if vector of column indexes and min, max are provided.
ScoreLikert <- function(df, napercent = .1, tonumeric = FALSE, reversecols = NULL, min = NULL, max = NULL, engine = "sum") {
  reverse_list <- list(reversecols = reversecols, min = min, max = max)
  reverse_check <- !sapply(reverse_list, is.null)
  
  # Recode to numeric, but watch out to coercion to not introduce NAs
  colstonumeric <- function(df){
    tryCatch({
      df_num <- as.data.frame(
        lapply(df,
               function(x) { as.numeric(as.character(x))})) 
    },warning = function(stop_on_warning) {
      message("Stoped the execution of numeric conversion: ", conditionMessage(stop_on_warning))
    }) 
  }
  
  if(tonumeric) df <- colstonumeric(df)
  
  if(all(reverse_check)){
    df[ ,reversecols] <- (max + min) - df[ ,reversecols]
  }else if(any(reverse_check)){
    stop("Insuficient info for reversing. Please provide: ", paste(names(reverse_list)[!reverse_check], collapse = ", "))
  }
  
  if(engine == "sum") {
    return(
      ifelse(rowSums(is.na(df)) > ncol(df) * napercent,
             NA,
             rowSums(df, na.rm = TRUE) * NA ^ (rowSums(!is.na(df)) == 0)
      )
    )  
  }
  
  if(engine == "mean") {
    return(
      ifelse(rowMeans(is.na(df)) > ncol(df) * napercent,
             NA,
             rowMeans(df, na.rm = TRUE) * NA ^ (rowSums(!is.na(df)) == 0)
      )       
    )
  }
  
    if(engine == "mean_na") {
      df[is.na(df)] <- 0
      rowMeans(df)
    }
}
# helper for mixed type to long format
select_to_longer <- function(data, var_prefix = NULL) {
  var_regex <- paste0("(", var_prefix, ")_(pre|post)")
  
  data %>%
    dplyr::select(ID, Conditia, dplyr::matches(var_regex)) %>%
    tidyr::pivot_longer(cols = dplyr::matches(var_regex), 
                 names_to = c("Variable", "PrePost"), 
                 names_pattern = "(.*)_(pre|post)",   
                 values_to = var_prefix
    ) %>% 
    dplyr::mutate(
      ID = as.factor(ID),
      Cond = factor(Conditia, levels = c("VR", "VR miros", "VR social")),
      PrePost = factor(PrePost, levels = c("pre", "post"))
    ) %>%
    dplyr::select(-c(Conditia, Variable))
}  # select_to_longer(df, "varpref)

2 Read data

2.1 Psych data

# # -------------------------------------------------------------------------
# # INTERACTIVE - Get the data from Google Drive ----------------------------
# library(googledrive)
# 
# # Get the data from Google Drive
# sheet_url <- "https://docs.google.com/spreadsheets/d/1ATSP4zEGt2mo25Kzu3lYlwu1LBA2WfwW/edit?usp=sharing&ouid=109354048369365876984&rtpof=true&sd=true"
# file_name <- paste0("DATE SAM,2022_", Sys.Date(), ".xlsx")
# 
# ###############################
# if(interactive()){
#   want_dl <- readline("Want to re-download data? 1-YES 2-NO: ")
#   if (want_dl == 1) {
#     # Call Google Drive authentication forcing interactive login and save in cache 
#     googledrive::drive_auth(use_oob = TRUE, cache = TRUE)      
#     
#     # Reuse token to Sheet authentification 
#     # googlesheets4::gs4_auth(token = googledrive::drive_token())    # the file is .xlsx not google sheet
#     
#     # Download the .xlsx from google drive
#     googledrive::drive_download(sheet_url, path = file.path(here::here(), file_name), overwrite = TRUE)
#   }
# }  
# ##################################################################################################################
# # INTERACTIVE - Read the latest .xlsx file for scalp ----------------------
# 
# # Read the latest .xlsx file for scalp SSM
# last_xlsx_file <- 
#   dir(here::here(), pattern = "DATE SAM.*xlsx", full.names = TRUE) %>% 
#   file.info() %>%
#   dplyr::arrange(dplyr::desc(ctime)) %>%
#   dplyr::slice(1) %>%
#   row.names()
# 
# # Check if it is the same that was downloaded this session
# check_file <- identical(file_name, basename(last_xlsx_file))
# check_file
# 
# # If it is read it, if not decide what to do
# if(interactive() & check_file){
#   df_arsq <- rio::import(file = last_xlsx_file, which = "ARSQ", skip = 1)
#   df_memo <- rio::import(file = last_xlsx_file, which = "Scala Memorabilitate")
#   df_pq <- rio::import(file = last_xlsx_file, which = "Chestionar prezenta")
#   df_itq <- rio::import(file = last_xlsx_file, which = "ITQ")  
# }
# # -------------------------------------------------------------------------

last_xlsx_file <-
  dir(here::here(), pattern = "DATE SAM.*xlsx", full.names = TRUE) %>%
  file.info() %>%
  dplyr::arrange(dplyr::desc(ctime)) %>%
  dplyr::slice(1) %>%
  row.names()

df_arsq <- rio::import(file = last_xlsx_file, which = "ARSQ", skip = 1)
New names:
df_memo <- rio::import(file = last_xlsx_file, which = "Scala Memorabilitate")
df_pq <- rio::import(file = last_xlsx_file, which = "Chestionar prezenta")
df_itq <- rio::import(file = last_xlsx_file, which = "ITQ")

2.2 OXT data

df_sam_ox <- rio::import("rezultate_oxy+protT_sam_sem nov 2022.xlsx", which = "SAM")
New names:
df_sam_ox <- df_sam_ox[, 2:5]
df_sam_ox <- 
  df_sam_ox %>% 
  janitor::clean_names() %>% 
  tidyr::separate_wider_delim(id_proba, delim = "/", names = c("id", "study", "prepost")) %>% 
  mutate(id = as.numeric(id))

# Wide data (to join to other dataframes)
df_sam_ox_wide <- 
  df_sam_ox %>% 
  dplyr::select(-study) %>% 
  pivot_wider(id_cols = id, names_from = prepost, values_from = starts_with("oxi")) %>% 
  dplyr::mutate(oxitocina_pg_ml_DIF = oxitocina_pg_ml_POST - oxitocina_pg_ml_PRE,
                oxitocina_normalizata_DIF = oxitocina_normalizata_POST - oxitocina_normalizata_PRE)

# Join dataframes to add condition 
df_sam_ox_long <- left_join(df_sam_ox, df_arsq[, 1:5], by = c("id" = "ID"))

3 Clean data

df_arsq <- 
  df_arsq %>%
  select_if(~ !all(is.na(.))) %>%                                           # drop columns with only NAs
  mutate(across(where(is.character), ~ na_if(.x, "na"))) %>%                # mark NAs
  mutate(across(i1:j55, ~ as.numeric(.x))) %>%                              # convert item cols to numeric
  rename_with(~ paste0("ascq_pre_", 1:55), .cols = i1:i55) %>%              # rename item cols
  rename_with(~ paste0("ascq_post_", 1:55), .cols = j1:j55) %>%
  mutate(
    ID = as.numeric(ID),
    Conditia = as.factor(Conditia),
    Gen = case_when(
      Gen == 1 ~ "f",
      Gen == 2 ~ "m",
      TRUE ~ NA_character_
    ),
    Gen = as.factor(Gen)
  )
  

df_pq <- 
  df_pq %>%
  select_if(~ !all(is.na(.))) %>%                                           # drop columns with only NAs
  mutate(across(where(is.character), ~ na_if(.x, "na"))) %>%                # mark NAs
  mutate(across(i1:i12, ~ as.numeric(.x))) %>%                              # convert item cols to numeric
  rename_with(~ paste0("pq_", 1:12), .cols = i1:i12) %>%                  # rename item cols
  mutate(
    ID = as.numeric(ID),
    Conditia = as.factor(Conditia),
    Gen = case_when(
      Gen == 1 ~ "f",
      Gen == 2 ~ "m",
      TRUE ~ NA_character_
    ),
    Gen = as.factor(Gen)
  )
  
  
df_itq <- 
  df_itq %>%
  select_if(~ !all(is.na(.))) %>%                                           # drop columns with only NAs
  mutate(across(where(is.character), ~ na_if(.x, "na"))) %>%                # mark NAs
  mutate(across(itq1:itq29, ~ as.numeric(.x))) %>%                          # convert item cols to numeric
  mutate(
    ID = as.numeric(ID),
    Conditia = as.factor(Conditia),
    Gen = case_when(
      Gen == 1 ~ "f",
      Gen == 2 ~ "m",
      TRUE ~ NA_character_
    ),
    Gen = as.factor(Gen)
  )
  
df_memo <-
  df_memo %>%
  select_if(~ !all(is.na(.))) %>%                                            # drop columns with only NAs
  filter(!all(is.na(.))) %>%                                                 # drop rows with only NAs
  mutate(across(starts_with("Varsta_amin_"), ~ as.numeric(.x))) %>%          # convert item cols to numeric
  mutate(across(starts_with("Val_"), ~ as.numeric(.x))) %>%              
  mutate(across(starts_with("Viv_"), ~ as.numeric(.x))) %>%
  mutate(across(starts_with("Relv_"), ~ as.numeric(.x))) %>%
  mutate(
    ID = as.numeric(ID),
    Conditia = as.factor(Conditia),
    Gen = case_when(
      Gen == 1 ~ "f",
      Gen == 2 ~ "m",
      TRUE ~ NA_character_
    ),
    Gen = as.factor(Gen)
  )
df_memo <- df_memo[1:80, ]   # only the first 80 rows have data

4 Compute scores

4.1 Memo Scales

df_memo <-
  df_memo %>%
  rowwise() %>%
  mutate(
    Valence =  mean(c_across(starts_with("Val_")), na.rm = TRUE),
    Vividness = mean(c_across(starts_with("Viv_")), na.rm = TRUE),
    Relevance = mean(c_across(starts_with("Relv_")), na.rm = TRUE)
  ) 
# identical(df_memo$Valence, rowMeans(df_memo[, paste0("Val_", 1:10)], na.rm = TRUE))
# identical(df_memo$Vividness, rowMeans(df_memo[, paste0("Viv_", 1:10)], na.rm = TRUE))
# identical(df_memo$Relevance, rowMeans(df_memo[, paste0("Relv_", 1:10)], na.rm = TRUE))

4.2 ASCQ

# full scoring in EEG_O.4_resting_state_behavioral
## ARSQ (Likert 1-5) -- no reverse scoring, in total 30 items to score + 25 additional single items
# discont: 1, 2, 3
# tom: 4, 5, 6
# self: 7, 8, 9
# planning: 10, 11, 12
# sleep: 13, 14, 15
# comfort: 16, 17, 18
# somatic: 19, 20, 21
# health: 22, 23, 24
# visual: 25, 26, 27
# verbal: 28, 29, 30
# + 25 individual items

df_arsq$discont_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(1, 2, 3))], napercent = 1, engine = "mean")
df_arsq$tom_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(4, 5, 6))], napercent = 1, engine = "mean") 
df_arsq$self_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(7, 8, 9))], napercent = 1, engine = "mean")
df_arsq$planning_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(10, 11, 12))], napercent = 1, engine = "mean")
df_arsq$sleep_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(13, 14, 15))], napercent = 1, engine = "mean") 
df_arsq$comfort_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(16, 17, 18))], napercent = 1, engine = "mean")
df_arsq$somatic_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(19, 20, 21))], napercent = 1, engine = "mean")
df_arsq$health_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(22, 23, 24))], napercent = 1, engine = "mean")
df_arsq$visual_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(25, 26, 27))], napercent = 1, engine = "mean")
df_arsq$verbal_pre <- ScoreLikert(df_arsq[, paste0("ascq_pre_", c(28, 29, 30))], napercent = 1, engine = "mean")

df_arsq$discont_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(1, 2, 3))], napercent = 1, engine = "mean")
df_arsq$tom_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(4, 5, 6))], napercent = 1, engine = "mean") 
df_arsq$self_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(7, 8, 9))], napercent = 1, engine = "mean")
df_arsq$planning_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(10, 11, 12))], napercent = 1, engine = "mean")
df_arsq$sleep_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(13, 14, 15))], napercent = 1, engine = "mean") 
df_arsq$comfort_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(16, 17, 18))], napercent = 1, engine = "mean")
df_arsq$somatic_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(19, 20, 21))], napercent = 1, engine = "mean")
df_arsq$health_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(22, 23, 24))], napercent = 1, engine = "mean")
df_arsq$visual_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(25, 26, 27))], napercent = 1, engine = "mean")
df_arsq$verbal_post <- ScoreLikert(df_arsq[, paste0("ascq_post_", c(28, 29, 30))], napercent = 1, engine = "mean")

4.3 PQ

# Only total

df_pq$PQ <- ScoreLikert(df_pq[, paste0("pq_", 1:12)], napercent = 1, engine = "sum")

5 Merge all with OXT data

# Join dataframes to add condition 
df_memo_ox <- left_join(df_memo, df_sam_ox_wide, by = join_by(ID == id))
df_arsq_ox <- left_join(df_arsq, df_sam_ox_wide, by = join_by(ID == id))
df_pq_ox <- left_join(df_pq, df_sam_ox_wide, by = join_by(ID == id))

6 New data: duration

6.1 Read duration data

6.2 Merge all with duration data

df_memo_ox <- left_join(df_memo_ox, df_dur_ms, by = join_by(ID == id))
Error in `left_join()`:
! Join columns in `y` must be present in the data.
✖ Problem with `id`.
Backtrace:
 1. dplyr::left_join(df_memo_ox, df_dur_ms, by = join_by(ID == id))
 2. dplyr:::left_join.data.frame(df_memo_ox, df_dur_ms, by = join_by(ID == id))

7 Analyses

7.1 Memo

df_memo %>%
  ggstatsplot::ggbetweenstats(
    x = Conditia,
    y = Valence,
    outlier.label = ID,
    xlab = ""
  )


df_memo %>%
  ggstatsplot::ggbetweenstats(
    x = Conditia,
    y = Vividness,
    outlier.label = ID,
    xlab = ""
  )


df_memo %>%
  ggstatsplot::ggbetweenstats(
    x = Conditia,
    y = Relevance,
    outlier.label = ID,
    xlab = ""
  )

7.2 PQ

df_pq %>%
  ggstatsplot::ggbetweenstats(
    x = Conditia,
    y = PQ,
    outlier.label = ID,
    xlab = ""
  )

7.3 OXT

df_sam_ox_long %>%
  mutate(prepost = factor(prepost, levels = c("PRE", "POST"))) %>% 
  group_by(id, Conditia) %>% 
  filter(n() > 1) %>%                      # exclude incomplete
  ungroup() %>% 
  ggstatsplot::grouped_ggwithinstats(
    x = prepost,
    y = oxitocina_normalizata,
    grouping.var = Conditia, 
    type = "p",
    bf.message = FALSE,
    annotation.args = list(title = "Pre & Post OX data", subtitle = "")
  )


df_sam_ox_long %>%
  mutate(prepost = factor(prepost, levels = c("PRE", "POST"))) %>% 
  filter(oxitocina_normalizata < 3) %>%    # exclude outlier from "VR miros"
  group_by(id, Conditia) %>% 
  filter(n() > 1) %>%                      # exclude incomplete
  ungroup() %>% 
  ggstatsplot::grouped_ggwithinstats(
    x = prepost,
    y = oxitocina_normalizata,
    grouping.var = Conditia,
    type = "p",
    bf.message = FALSE,
    annotation.args = list(title = "Pre & Post OX data", subtitle = "Outlier excluded from VR miros")
  )

7.3.1 RM ANOVA

df_sam_ox_long_anova <-
  df_sam_ox_long %>%
  mutate(prepost = factor(prepost, levels = c("PRE", "POST"))) %>% 
  filter(oxitocina_normalizata < 3)

# ANOVA
anova_test(
  data = df_sam_ox_long_anova, wid = id,
  dv = oxitocina_normalizata, 
  within = prepost, between = Conditia
)
ANOVA Table (type III tests)

            Effect DFn DFd     F     p p<.05   ges
1         Conditia   2  60 0.979 0.382       0.025
2          prepost   1  60 6.580 0.013     * 0.025
3 Conditia:prepost   2  60 2.439 0.096       0.018
# comparisons for treatment variable
df_sam_ox_long_anova %>%
  group_by(prepost) %>% 
  pairwise_t_test(
    oxitocina_normalizata ~ Conditia, paired = FALSE, 
    p.adjust.method = "holm"
  )

# comparisons for time variable
df_sam_ox_long_anova %>%
  group_by(id, Conditia) %>% 
  filter(n() > 1) %>% 
  ungroup() %>% 
  group_by(Conditia) %>% 
  pairwise_t_test(
    oxitocina_normalizata ~ prepost, paired = TRUE, 
    p.adjust.method = "holm"
  )

7.4 ARSQ

8 Analyses duration

df_dur_ms %>%
  ggstatsplot::ggbetweenstats(
    x = Conditia,
    y = med_dur,
    outlier.label = ID,
    xlab = ""
  )


df_dur_ms %>%
  ggstatsplot::ggbetweenstats(
    x = Conditia,
    y = mean_dur,
    outlier.label = ID,
    xlab = ""
  )

8.1 Correlations - duration & memorability

df_memo_ox %>% 
  dplyr::select(
    "oxitocina_pg_ml_DIF", "oxitocina_normalizata_DIF", 
    "Valence", "Vividness", "Relevance", "med_dur", "mean_dur" 
  ) %>% 
  PerformanceAnalytics::chart.Correlation()

8.2 Correlations with OX difference

same_cols <- intersect(names(df_memo_ox), names(df_pq_ox))

dplyr::left_join(df_memo_ox, df_pq_ox, by = same_cols) %>% 
  dplyr::rename(Conditia = Conditia.x) %>% 
  dplyr::filter(Conditia == "VR") %>% 
  dplyr::select(
    "oxitocina_pg_ml_DIF", "oxitocina_normalizata_DIF", 
    "Valence", "Vividness", "Relevance", "PQ", "med_dur", "mean_dur" 
  ) %>% 
  PerformanceAnalytics::chart.Correlation()
title("VR")



dplyr::left_join(df_memo_ox, df_pq_ox, by = same_cols) %>% 
  dplyr::rename(Conditia = Conditia.x) %>%
  dplyr::filter(Conditia == "VR miros") %>% 
  dplyr::select(
    "oxitocina_pg_ml_DIF", "oxitocina_normalizata_DIF", 
    "Valence", "Vividness", "Relevance", "PQ", "med_dur", "mean_dur"  
  ) %>% 
  PerformanceAnalytics::chart.Correlation()
title("VR miros")


dplyr::left_join(df_memo_ox, df_pq_ox, by = same_cols) %>%
  dplyr::rename(Conditia = Conditia.x) %>%
  dplyr::filter(Conditia == "VR social") %>% 
  dplyr::select(
    "oxitocina_pg_ml_DIF", "oxitocina_normalizata_DIF", 
    "Valence", "Vividness", "Relevance", "PQ", "med_dur", "mean_dur"  
  ) %>% 
  PerformanceAnalytics::chart.Correlation()
title("VR social")

df_arsq_ox %>%
  dplyr::filter(Conditia == "VR") %>% 
  dplyr::select(
    "oxitocina_pg_ml_DIF", "oxitocina_normalizata_DIF", 
    contains("_dif") 
  ) %>% 
  PerformanceAnalytics::chart.Correlation()
title("VR")


df_arsq_ox %>%
  dplyr::filter(Conditia == "VR miros") %>% 
  dplyr::select(
    "oxitocina_pg_ml_DIF", "oxitocina_normalizata_DIF", 
    contains("_dif") 
  ) %>% 
  PerformanceAnalytics::chart.Correlation()
title("VR miros")


df_arsq_ox %>%
  dplyr::filter(Conditia == "VR social") %>% 
  dplyr::select(
    "oxitocina_pg_ml_DIF", "oxitocina_normalizata_DIF", 
    contains("_dif") 
  ) %>% 
  PerformanceAnalytics::chart.Correlation()
title("VR social")


9 Session Info

 

A work by Claudiu Papasteri

 

LS0tDQp0aXRsZTogIjxicj4gU1RBRCBTQU0iIA0Kc3VidGl0bGU6ICJJbml0aWFsIEFuYWx5c2lzIg0KYXV0aG9yOiAiPGJyPiBDbGF1ZGl1IFBhcGFzdGVyaSINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVtICVZJylgIg0Kb3V0cHV0OiANCiAgICBodG1sX25vdGVib29rOg0KICAgICAgICAgICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgICAgICAgICB0b2M6IHRydWUNCiAgICAgICAgICAgIHRvY19kZXB0aDogMg0KICAgICAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgICAgICAgICB0aGVtZTogc3BhY2VsYWINCiAgICAgICAgICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICAgICAgICAgIGZvbnQtZmFtaWx5OiBBcmlhbA0KICAgICAgICAgICAgZmlnX3dpZHRoOiAxMA0KICAgICAgICAgICAgZmlnX2hlaWdodDogOQ0KICAgICMgcGRmX2RvY3VtZW50OiANCiAgICAgICAgICAgICMgdG9jOiB0cnVlDQogICAgICAgICAgICAjIHRvY19kZXB0aDogMg0KICAgICAgICAgICAgIyBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICAgICAgICAgICMgZm9udHNpemU6IDExcHQNCiAgICAgICAgICAgICMgZ2VvbWV0cnk6IG1hcmdpbj0xaW4NCiAgICAgICAgICAgICMgZmlnX3dpZHRoOiA3DQogICAgICAgICAgICAjIGZpZ19oZWlnaHQ6IDYNCiAgICAgICAgICAgICMgZmlnX2NhcHRpb246IHRydWUNCiAgICAjIGdpdGh1Yl9kb2N1bWVudDogDQogICAgICAgICAgICAjIHRvYzogdHJ1ZQ0KICAgICAgICAgICAgIyB0b2NfZGVwdGg6IDINCiAgICAgICAgICAgICMgaHRtbF9wcmV2aWV3OiBmYWxzZQ0KICAgICAgICAgICAgIyBmaWdfd2lkdGg6IDUNCiAgICAgICAgICAgICMgZmlnX2hlaWdodDogNQ0KICAgICAgICAgICAgIyBkZXY6IGpwZWcNCi0tLQ0KDQoNCjwhLS0gU2V0dXAgLS0+DQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIEdlbmVyYWwgUiBvcHRpb25zDQpzZXQuc2VlZCgxMTEpICAgICAgICAgICAgICAgIyBpbiBjYXNlIHdlIHVzZSByYW5kb21pemVkIHByb2NlZHVyZXMgICAgICAgDQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgICAgICAgIyBwb3NpdGl2ZSB2YWx1ZXMgYmlhcyB0b3dhcmRzIGZpeGVkIGFuZCBuZWdhdGl2ZSB0b3dhcmRzIHNjaWVudGlmaWMgbm90YXRpb24NCm9wdGlvbnMocmVwb3MgPSBjKGdldE9wdGlvbigicmVwb3MiKVsiQ1JBTiJdLCBDUkFOZXh0cmEgPSAiaHR0cHM6Ly9taXJyb3IuY2xpZW50dnBzLmNvbS9DUkFOLyIpKSAgIyB1c2UgQ1JBTiBhcyBkZWZhdWx0LCBzZXQgQ1JBTmV4dHJhIHRvIE7DvHJuYmVyZyBtaXJyb3INCg0KIyBMb2FkIHBhY2thZ2VzDQojIGlmKCFyZXF1aXJlKCJsaWJyYXJpYW4iKSkgaW5zdGFsbC5wYWNrYWdlcygibGlicmFyaWFuIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiMgbGlicmFyaWFuOjpzaGVsZigNCiMgICB1cGRhdGVfYWxsID0gRkFMU0UsDQojICAgcXVpZXQgPSBUUlVFLA0KIyAgICMgcGFja2FnZSBsaXN0DQojICAgcGFwYWphLA0KIyAgIGhlcmUsIGZzLA0KIyAgIGNvbmZsaWN0ZWQsDQojICAgcmlvLA0KIyAgIHRpZHl2ZXJzZSwgDQojICAgcHN5Y2gsICAgICAgICAgIA0KIyAgIHJzdGF0aXgsIGdnc3RhdHNwbG90LA0KIyAgIGdncGxvdDIsIGdncHViciwgc2NhbGVzLA0KIyAgIHJlcG9ydA0KIyAgICMgLCAuLi4NCiMgKQ0KDQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KcGFja2FnZXMgPC0gYygNCiAgInBhcGFqYSIsDQogICJoZXJlIiwgImZzIiwNCiAgImNvbmZsaWN0ZWQiLA0KICAicmlvIiwNCiAgInRpZHl2ZXJzZSIsIA0KICAicHN5Y2giLCAgICAgICAgICANCiAgInJzdGF0aXgiLCAiZ2dzdGF0c3Bsb3QiLA0KICAiZ2dwbG90MiIsICJnZ3B1YnIiLCAic2NhbGVzIiwNCiAgInJlcG9ydCIsDQogICJsdWJyaWRhdGUiLA0KICAibWF0cml4U3RhdHMiDQogICMgLCAuLi4NCikNCnBhY21hbjo6cF9sb2FkKGNoYXIgPSBwYWNrYWdlcykNCg0KIyBTZXQgaGVyZSB0byBSbm90ZWJvb2sgZGlyZWN0b3J5DQpoZXJlOjpzZXRfaGVyZSgpDQp1bmxvYWROYW1lc3BhY2UoImhlcmUiKSAgICAgICAgICAgICAgICAgICAjIG5lZWQgbmV3IFIgc2Vzc2lvbiBvciB1bmxvYWQgbmFtZXNwYWNlIGZvciAuaGVyZSBmaWxlIHRvIHRha2UgcHJlY2VkZW5jZSBvdmVyIC5ScHJvag0Kbm90ZWJvb2tfbmFtZSA8LSBmczo6cGF0aF9maWxlKGhlcmU6OmhlcmUoKSkNCg0KIyBTb2x2ZSBjb25mbGljdHMgaW4gZmF2b3Igb2YgdGlkeXZlcnNlDQpjb25mbGljdGVkOjpjb25mbGljdF9wcmVmZXIoImZpbHRlciIsIHdpbm5lciA9ICJkcGx5ciIpDQpjb25mbGljdGVkOjpjb25mbGljdF9wcmVmZXIoInNlbGVjdCIsIHdpbm5lciA9ICJkcGx5ciIpDQpjb25mbGljdGVkOjpjb25mbGljdF9wcmVmZXIoInNsaWNlIiwgd2lubmVyID0gImRwbHlyIikNCmNvbmZsaWN0ZWQ6OmNvbmZsaWN0X3ByZWZlcigicmVuYW1lIiwgd2lubmVyID0gImRwbHlyIikNCmNvbmZsaWN0ZWQ6OmNvbmZsaWN0X3ByZWZlcigiY291bnQiLCB3aW5uZXIgPSAiZHBseXIiKQ0KDQojIFNldCBraW50ciBvcHRpb25zIGluY2x1ZGluZyByb290LmRpciBwb2ludGluZyB0byB0aGUgLmhlcmUgZmlsZSBpbiBSbm90ZWJvb2sgZGlyZWN0b3J5DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIHJvb3QuZGlyID0gaGVyZTo6aGVyZSgpLA0KICAjZmlnLndpZHRoID0gNSwgZmlnLmFzcCA9IDEvMywgDQogIGNvbW1lbnQgPSAiIyIsDQogIGNvbGxhcHNlID0gVFJVRSwNCiAgZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBUUlVFLCBtZXNzYWdlID0gVFJVRSwgY2FjaGUgPSBUUlVFICAgICAgICMgZWNobyA9IEZhbHNlIGZvciBnaXRodWJfZG9jdW1lbnQsIGJ1dCB3aWxsIGJlIGZvbGRlZCBpbiBodG1sX25vdGVib29rDQopDQoNCiMgVGhlbWVzIGZvciBnZ3Bsb3QyIHBsb3R0aW5nIChoZXJlIHVzZWQgQVBBIHN0eWxlKQ0KdGhlbWVfc2V0KHBhcGFqYTo6dGhlbWVfYXBhKCkpDQpgYGANCg0KDQoNCg0KDQo8IS0tIEZ1bmN0aW9ucyAtLT4NCg0KIyBEZWZpbmUgZnVuY3Rpb25zDQoNCmBgYHtyfQ0KIyMgRGVmaW5lIGZ1bmN0aW9uIHRoYXQgcmVjb2RlcyB0byBudW1lcmljLCBidXQgd2F0Y2hlcyBvdXQgdG8gY29lcmNpb24gdG8gbm90IGludHJvZHVjZSBOQXMNCmNvbHN0b251bWVyaWMgPC0gZnVuY3Rpb24oZGYpew0KICB0cnlDYXRjaCh7DQogICAgZGZfbnVtIDwtIGFzLmRhdGEuZnJhbWUoDQogICAgICBsYXBwbHkoZGYsDQogICAgICAgICAgICAgZnVuY3Rpb24oeCkgeyBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcih4KSl9KSkgDQogIH0sd2FybmluZyA9IGZ1bmN0aW9uKHN0b3Bfb25fd2FybmluZykgew0KICAgIG1lc3NhZ2UoIlN0b3BlZCB0aGUgZXhlY3V0aW9uIG9mIG51bWVyaWMgY29udmVyc2lvbjogIiwgY29uZGl0aW9uTWVzc2FnZShzdG9wX29uX3dhcm5pbmcpKQ0KICB9KSANCn0NCiMjDQojIyBEZWZpbmUgZnVuY3Rpb24gdGhhdCByZXZlcnNlIGNvZGVzIGl0ZW1zDQpSZXZlcnNlQ29kZSA8LSBmdW5jdGlvbihkZiwgdG9udW1lcmljID0gRkFMU0UsIG1pbiA9IE5VTEwsIG1heCA9IE5VTEwpIHsNCiAgaWYodG9udW1lcmljKSBkZiA8LSBjb2xzdG9udW1lcmljKGRmKQ0KICBkZiA8LSAobWF4ICsgbWluKSAtIGRmDQp9DQojIw0KIyMgRGVmaW5lIGZ1bmN0aW9uIHRoYXQgc2NvcmVzIG9ubHkgcm93cyB3aXRoIGxlc3MgdGhhbiAxMCUgTkFzIChyZXR1cm5zIE5BIGlmIGFsbCBvciBhYm92ZSB0aHJlc2hvbGQgcGVyY2VudGFnZSBvZiByb3dzIGFyZSBOQSk7IGNhbiByZXZlcnNlIGNvZGUgaWYgdmVjdG9yIG9mIGNvbHVtbiBpbmRleGVzIGFuZCBtaW4sIG1heCBhcmUgcHJvdmlkZWQuDQpTY29yZUxpa2VydCA8LSBmdW5jdGlvbihkZiwgbmFwZXJjZW50ID0gLjEsIHRvbnVtZXJpYyA9IEZBTFNFLCByZXZlcnNlY29scyA9IE5VTEwsIG1pbiA9IE5VTEwsIG1heCA9IE5VTEwsIGVuZ2luZSA9ICJzdW0iKSB7DQogIHJldmVyc2VfbGlzdCA8LSBsaXN0KHJldmVyc2Vjb2xzID0gcmV2ZXJzZWNvbHMsIG1pbiA9IG1pbiwgbWF4ID0gbWF4KQ0KICByZXZlcnNlX2NoZWNrIDwtICFzYXBwbHkocmV2ZXJzZV9saXN0LCBpcy5udWxsKQ0KICANCiAgIyBSZWNvZGUgdG8gbnVtZXJpYywgYnV0IHdhdGNoIG91dCB0byBjb2VyY2lvbiB0byBub3QgaW50cm9kdWNlIE5Bcw0KICBjb2xzdG9udW1lcmljIDwtIGZ1bmN0aW9uKGRmKXsNCiAgICB0cnlDYXRjaCh7DQogICAgICBkZl9udW0gPC0gYXMuZGF0YS5mcmFtZSgNCiAgICAgICAgbGFwcGx5KGRmLA0KICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgeyBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcih4KSl9KSkgDQogICAgfSx3YXJuaW5nID0gZnVuY3Rpb24oc3RvcF9vbl93YXJuaW5nKSB7DQogICAgICBtZXNzYWdlKCJTdG9wZWQgdGhlIGV4ZWN1dGlvbiBvZiBudW1lcmljIGNvbnZlcnNpb246ICIsIGNvbmRpdGlvbk1lc3NhZ2Uoc3RvcF9vbl93YXJuaW5nKSkNCiAgICB9KSANCiAgfQ0KICANCiAgaWYodG9udW1lcmljKSBkZiA8LSBjb2xzdG9udW1lcmljKGRmKQ0KICANCiAgaWYoYWxsKHJldmVyc2VfY2hlY2spKXsNCiAgICBkZlsgLHJldmVyc2Vjb2xzXSA8LSAobWF4ICsgbWluKSAtIGRmWyAscmV2ZXJzZWNvbHNdDQogIH1lbHNlIGlmKGFueShyZXZlcnNlX2NoZWNrKSl7DQogICAgc3RvcCgiSW5zdWZpY2llbnQgaW5mbyBmb3IgcmV2ZXJzaW5nLiBQbGVhc2UgcHJvdmlkZTogIiwgcGFzdGUobmFtZXMocmV2ZXJzZV9saXN0KVshcmV2ZXJzZV9jaGVja10sIGNvbGxhcHNlID0gIiwgIikpDQogIH0NCiAgDQogIGlmKGVuZ2luZSA9PSAic3VtIikgew0KICAgIHJldHVybigNCiAgICAgIGlmZWxzZShyb3dTdW1zKGlzLm5hKGRmKSkgPiBuY29sKGRmKSAqIG5hcGVyY2VudCwNCiAgICAgICAgICAgICBOQSwNCiAgICAgICAgICAgICByb3dTdW1zKGRmLCBuYS5ybSA9IFRSVUUpICogTkEgXiAocm93U3VtcyghaXMubmEoZGYpKSA9PSAwKQ0KICAgICAgKQ0KICAgICkgIA0KICB9DQogIA0KICBpZihlbmdpbmUgPT0gIm1lYW4iKSB7DQogICAgcmV0dXJuKA0KICAgICAgaWZlbHNlKHJvd01lYW5zKGlzLm5hKGRmKSkgPiBuY29sKGRmKSAqIG5hcGVyY2VudCwNCiAgICAgICAgICAgICBOQSwNCiAgICAgICAgICAgICByb3dNZWFucyhkZiwgbmEucm0gPSBUUlVFKSAqIE5BIF4gKHJvd1N1bXMoIWlzLm5hKGRmKSkgPT0gMCkNCiAgICAgICkgICAgICAgDQogICAgKQ0KICB9DQogIA0KICAgIGlmKGVuZ2luZSA9PSAibWVhbl9uYSIpIHsNCiAgICAgIGRmW2lzLm5hKGRmKV0gPC0gMA0KICAgICAgcm93TWVhbnMoZGYpDQogICAgfQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQojIGhlbHBlciBmb3IgbWl4ZWQgdHlwZSB0byBsb25nIGZvcm1hdA0Kc2VsZWN0X3RvX2xvbmdlciA8LSBmdW5jdGlvbihkYXRhLCB2YXJfcHJlZml4ID0gTlVMTCkgew0KICB2YXJfcmVnZXggPC0gcGFzdGUwKCIoIiwgdmFyX3ByZWZpeCwgIilfKHByZXxwb3N0KSIpDQogIA0KICBkYXRhICU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoSUQsIENvbmRpdGlhLCBkcGx5cjo6bWF0Y2hlcyh2YXJfcmVnZXgpKSAlPiUNCiAgICB0aWR5cjo6cGl2b3RfbG9uZ2VyKGNvbHMgPSBkcGx5cjo6bWF0Y2hlcyh2YXJfcmVnZXgpLCANCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSBjKCJWYXJpYWJsZSIsICJQcmVQb3N0IiksIA0KICAgICAgICAgICAgICAgICBuYW1lc19wYXR0ZXJuID0gIiguKilfKHByZXxwb3N0KSIsICAgDQogICAgICAgICAgICAgICAgIHZhbHVlc190byA9IHZhcl9wcmVmaXgNCiAgICApICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKA0KICAgICAgSUQgPSBhcy5mYWN0b3IoSUQpLA0KICAgICAgQ29uZCA9IGZhY3RvcihDb25kaXRpYSwgbGV2ZWxzID0gYygiVlIiLCAiVlIgbWlyb3MiLCAiVlIgc29jaWFsIikpLA0KICAgICAgUHJlUG9zdCA9IGZhY3RvcihQcmVQb3N0LCBsZXZlbHMgPSBjKCJwcmUiLCAicG9zdCIpKQ0KICAgICkgJT4lDQogICAgZHBseXI6OnNlbGVjdCgtYyhDb25kaXRpYSwgVmFyaWFibGUpKQ0KfSAgIyBzZWxlY3RfdG9fbG9uZ2VyKGRmLCAidmFycHJlZikNCmBgYA0KDQoNCg0KPCEtLSBSZXBvcnQgLS0+DQoNCiMgUmVhZCBkYXRhDQoNCiMjIFBzeWNoIGRhdGENCg0KYGBge3J9DQojICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAjIElOVEVSQUNUSVZFIC0gR2V0IHRoZSBkYXRhIGZyb20gR29vZ2xlIERyaXZlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgbGlicmFyeShnb29nbGVkcml2ZSkNCiMgDQojICMgR2V0IHRoZSBkYXRhIGZyb20gR29vZ2xlIERyaXZlDQojIHNoZWV0X3VybCA8LSAiaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMUFUU1A0ekVHdDJtbzI1S3p1M2xZbHd1MUxCQTJXZndXL2VkaXQ/dXNwPXNoYXJpbmcmb3VpZD0xMDkzNTQwNDgzNjkzNjU4NzY5ODQmcnRwb2Y9dHJ1ZSZzZD10cnVlIg0KIyBmaWxlX25hbWUgPC0gcGFzdGUwKCJEQVRFIFNBTSwyMDIyXyIsIFN5cy5EYXRlKCksICIueGxzeCIpDQojIA0KIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIGlmKGludGVyYWN0aXZlKCkpew0KIyAgIHdhbnRfZGwgPC0gcmVhZGxpbmUoIldhbnQgdG8gcmUtZG93bmxvYWQgZGF0YT8gMS1ZRVMgMi1OTzogIikNCiMgICBpZiAod2FudF9kbCA9PSAxKSB7DQojICAgICAjIENhbGwgR29vZ2xlIERyaXZlIGF1dGhlbnRpY2F0aW9uIGZvcmNpbmcgaW50ZXJhY3RpdmUgbG9naW4gYW5kIHNhdmUgaW4gY2FjaGUgDQojICAgICBnb29nbGVkcml2ZTo6ZHJpdmVfYXV0aCh1c2Vfb29iID0gVFJVRSwgY2FjaGUgPSBUUlVFKSAgICAgIA0KIyAgICAgDQojICAgICAjIFJldXNlIHRva2VuIHRvIFNoZWV0IGF1dGhlbnRpZmljYXRpb24gDQojICAgICAjIGdvb2dsZXNoZWV0czQ6OmdzNF9hdXRoKHRva2VuID0gZ29vZ2xlZHJpdmU6OmRyaXZlX3Rva2VuKCkpICAgICMgdGhlIGZpbGUgaXMgLnhsc3ggbm90IGdvb2dsZSBzaGVldA0KIyAgICAgDQojICAgICAjIERvd25sb2FkIHRoZSAueGxzeCBmcm9tIGdvb2dsZSBkcml2ZQ0KIyAgICAgZ29vZ2xlZHJpdmU6OmRyaXZlX2Rvd25sb2FkKHNoZWV0X3VybCwgcGF0aCA9IGZpbGUucGF0aChoZXJlOjpoZXJlKCksIGZpbGVfbmFtZSksIG92ZXJ3cml0ZSA9IFRSVUUpDQojICAgfQ0KIyB9ICANCiMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojICMgSU5URVJBQ1RJVkUgLSBSZWFkIHRoZSBsYXRlc3QgLnhsc3ggZmlsZSBmb3Igc2NhbHAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyANCiMgIyBSZWFkIHRoZSBsYXRlc3QgLnhsc3ggZmlsZSBmb3Igc2NhbHAgU1NNDQojIGxhc3RfeGxzeF9maWxlIDwtIA0KIyAgIGRpcihoZXJlOjpoZXJlKCksIHBhdHRlcm4gPSAiREFURSBTQU0uKnhsc3giLCBmdWxsLm5hbWVzID0gVFJVRSkgJT4lIA0KIyAgIGZpbGUuaW5mbygpICU+JQ0KIyAgIGRwbHlyOjphcnJhbmdlKGRwbHlyOjpkZXNjKGN0aW1lKSkgJT4lDQojICAgZHBseXI6OnNsaWNlKDEpICU+JQ0KIyAgIHJvdy5uYW1lcygpDQojIA0KIyAjIENoZWNrIGlmIGl0IGlzIHRoZSBzYW1lIHRoYXQgd2FzIGRvd25sb2FkZWQgdGhpcyBzZXNzaW9uDQojIGNoZWNrX2ZpbGUgPC0gaWRlbnRpY2FsKGZpbGVfbmFtZSwgYmFzZW5hbWUobGFzdF94bHN4X2ZpbGUpKQ0KIyBjaGVja19maWxlDQojIA0KIyAjIElmIGl0IGlzIHJlYWQgaXQsIGlmIG5vdCBkZWNpZGUgd2hhdCB0byBkbw0KIyBpZihpbnRlcmFjdGl2ZSgpICYgY2hlY2tfZmlsZSl7DQojICAgZGZfYXJzcSA8LSByaW86OmltcG9ydChmaWxlID0gbGFzdF94bHN4X2ZpbGUsIHdoaWNoID0gIkFSU1EiLCBza2lwID0gMSkNCiMgICBkZl9tZW1vIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSBsYXN0X3hsc3hfZmlsZSwgd2hpY2ggPSAiU2NhbGEgTWVtb3JhYmlsaXRhdGUiKQ0KIyAgIGRmX3BxIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSBsYXN0X3hsc3hfZmlsZSwgd2hpY2ggPSAiQ2hlc3Rpb25hciBwcmV6ZW50YSIpDQojICAgZGZfaXRxIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSBsYXN0X3hsc3hfZmlsZSwgd2hpY2ggPSAiSVRRIikgIA0KIyB9DQojICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpsYXN0X3hsc3hfZmlsZSA8LQ0KICBkaXIoaGVyZTo6aGVyZSgpLCBwYXR0ZXJuID0gIkRBVEUgU0FNLip4bHN4IiwgZnVsbC5uYW1lcyA9IFRSVUUpICU+JQ0KICBmaWxlLmluZm8oKSAlPiUNCiAgZHBseXI6OmFycmFuZ2UoZHBseXI6OmRlc2MoY3RpbWUpKSAlPiUNCiAgZHBseXI6OnNsaWNlKDEpICU+JQ0KICByb3cubmFtZXMoKQ0KDQpkZl9hcnNxIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSBsYXN0X3hsc3hfZmlsZSwgd2hpY2ggPSAiQVJTUSIsIHNraXAgPSAxKQ0KZGZfbWVtbyA8LSByaW86OmltcG9ydChmaWxlID0gbGFzdF94bHN4X2ZpbGUsIHdoaWNoID0gIlNjYWxhIE1lbW9yYWJpbGl0YXRlIikNCmRmX3BxIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSBsYXN0X3hsc3hfZmlsZSwgd2hpY2ggPSAiQ2hlc3Rpb25hciBwcmV6ZW50YSIpDQpkZl9pdHEgPC0gcmlvOjppbXBvcnQoZmlsZSA9IGxhc3RfeGxzeF9maWxlLCB3aGljaCA9ICJJVFEiKQ0KYGBgDQoNCg0KIyMgT1hUIGRhdGEgDQoNCmBgYHtyfQ0KZGZfc2FtX294IDwtIHJpbzo6aW1wb3J0KCJyZXp1bHRhdGVfb3h5K3Byb3RUX3NhbV9zZW0gbm92IDIwMjIueGxzeCIsIHdoaWNoID0gIlNBTSIpDQpkZl9zYW1fb3ggPC0gZGZfc2FtX294WywgMjo1XQ0KZGZfc2FtX294IDwtIA0KICBkZl9zYW1fb3ggJT4lIA0KICBqYW5pdG9yOjpjbGVhbl9uYW1lcygpICU+JSANCiAgdGlkeXI6OnNlcGFyYXRlX3dpZGVyX2RlbGltKGlkX3Byb2JhLCBkZWxpbSA9ICIvIiwgbmFtZXMgPSBjKCJpZCIsICJzdHVkeSIsICJwcmVwb3N0IikpICU+JSANCiAgbXV0YXRlKGlkID0gYXMubnVtZXJpYyhpZCkpDQoNCiMgV2lkZSBkYXRhICh0byBqb2luIHRvIG90aGVyIGRhdGFmcmFtZXMpDQpkZl9zYW1fb3hfd2lkZSA8LSANCiAgZGZfc2FtX294ICU+JSANCiAgZHBseXI6OnNlbGVjdCgtc3R1ZHkpICU+JSANCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IGlkLCBuYW1lc19mcm9tID0gcHJlcG9zdCwgdmFsdWVzX2Zyb20gPSBzdGFydHNfd2l0aCgib3hpIikpICU+JSANCiAgZHBseXI6Om11dGF0ZShveGl0b2NpbmFfcGdfbWxfRElGID0gb3hpdG9jaW5hX3BnX21sX1BPU1QgLSBveGl0b2NpbmFfcGdfbWxfUFJFLA0KICAgICAgICAgICAgICAgIG94aXRvY2luYV9ub3JtYWxpemF0YV9ESUYgPSBveGl0b2NpbmFfbm9ybWFsaXphdGFfUE9TVCAtIG94aXRvY2luYV9ub3JtYWxpemF0YV9QUkUpDQoNCiMgSm9pbiBkYXRhZnJhbWVzIHRvIGFkZCBjb25kaXRpb24gDQpkZl9zYW1fb3hfbG9uZyA8LSBsZWZ0X2pvaW4oZGZfc2FtX294LCBkZl9hcnNxWywgMTo1XSwgYnkgPSBjKCJpZCIgPSAiSUQiKSkNCmBgYA0KDQoNCiMgQ2xlYW4gZGF0YQ0KDQpgYGB7cn0NCmRmX2Fyc3EgPC0gDQogIGRmX2Fyc3EgJT4lDQogIHNlbGVjdF9pZih+ICFhbGwoaXMubmEoLikpKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBkcm9wIGNvbHVtbnMgd2l0aCBvbmx5IE5Bcw0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIH4gbmFfaWYoLngsICJuYSIpKSkgJT4lICAgICAgICAgICAgICAgICMgbWFyayBOQXMNCiAgbXV0YXRlKGFjcm9zcyhpMTpqNTUsIH4gYXMubnVtZXJpYygueCkpKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNvbnZlcnQgaXRlbSBjb2xzIHRvIG51bWVyaWMNCiAgcmVuYW1lX3dpdGgofiBwYXN0ZTAoImFzY3FfcHJlXyIsIDE6NTUpLCAuY29scyA9IGkxOmk1NSkgJT4lICAgICAgICAgICAgICAjIHJlbmFtZSBpdGVtIGNvbHMNCiAgcmVuYW1lX3dpdGgofiBwYXN0ZTAoImFzY3FfcG9zdF8iLCAxOjU1KSwgLmNvbHMgPSBqMTpqNTUpICU+JQ0KICBtdXRhdGUoDQogICAgSUQgPSBhcy5udW1lcmljKElEKSwNCiAgICBDb25kaXRpYSA9IGFzLmZhY3RvcihDb25kaXRpYSksDQogICAgR2VuID0gY2FzZV93aGVuKA0KICAgICAgR2VuID09IDEgfiAiZiIsDQogICAgICBHZW4gPT0gMiB+ICJtIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBHZW4gPSBhcy5mYWN0b3IoR2VuKQ0KICApDQogIA0KDQpkZl9wcSA8LSANCiAgZGZfcHEgJT4lDQogIHNlbGVjdF9pZih+ICFhbGwoaXMubmEoLikpKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBkcm9wIGNvbHVtbnMgd2l0aCBvbmx5IE5Bcw0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIH4gbmFfaWYoLngsICJuYSIpKSkgJT4lICAgICAgICAgICAgICAgICMgbWFyayBOQXMNCiAgbXV0YXRlKGFjcm9zcyhpMTppMTIsIH4gYXMubnVtZXJpYygueCkpKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNvbnZlcnQgaXRlbSBjb2xzIHRvIG51bWVyaWMNCiAgcmVuYW1lX3dpdGgofiBwYXN0ZTAoInBxXyIsIDE6MTIpLCAuY29scyA9IGkxOmkxMikgJT4lICAgICAgICAgICAgICAgICAgIyByZW5hbWUgaXRlbSBjb2xzDQogIG11dGF0ZSgNCiAgICBJRCA9IGFzLm51bWVyaWMoSUQpLA0KICAgIENvbmRpdGlhID0gYXMuZmFjdG9yKENvbmRpdGlhKSwNCiAgICBHZW4gPSBjYXNlX3doZW4oDQogICAgICBHZW4gPT0gMSB+ICJmIiwNCiAgICAgIEdlbiA9PSAyIH4gIm0iLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIEdlbiA9IGFzLmZhY3RvcihHZW4pDQogICkNCiAgDQogIA0KZGZfaXRxIDwtIA0KICBkZl9pdHEgJT4lDQogIHNlbGVjdF9pZih+ICFhbGwoaXMubmEoLikpKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBkcm9wIGNvbHVtbnMgd2l0aCBvbmx5IE5Bcw0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIH4gbmFfaWYoLngsICJuYSIpKSkgJT4lICAgICAgICAgICAgICAgICMgbWFyayBOQXMNCiAgbXV0YXRlKGFjcm9zcyhpdHExOml0cTI5LCB+IGFzLm51bWVyaWMoLngpKSkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNvbnZlcnQgaXRlbSBjb2xzIHRvIG51bWVyaWMNCiAgbXV0YXRlKA0KICAgIElEID0gYXMubnVtZXJpYyhJRCksDQogICAgQ29uZGl0aWEgPSBhcy5mYWN0b3IoQ29uZGl0aWEpLA0KICAgIEdlbiA9IGNhc2Vfd2hlbigNCiAgICAgIEdlbiA9PSAxIH4gImYiLA0KICAgICAgR2VuID09IDIgfiAibSIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgR2VuID0gYXMuZmFjdG9yKEdlbikNCiAgKQ0KICANCmRmX21lbW8gPC0NCiAgZGZfbWVtbyAlPiUNCiAgc2VsZWN0X2lmKH4gIWFsbChpcy5uYSguKSkpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBkcm9wIGNvbHVtbnMgd2l0aCBvbmx5IE5Bcw0KICBmaWx0ZXIoIWFsbChpcy5uYSguKSkpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRyb3Agcm93cyB3aXRoIG9ubHkgTkFzDQogIG11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoIlZhcnN0YV9hbWluXyIpLCB+IGFzLm51bWVyaWMoLngpKSkgJT4lICAgICAgICAgICMgY29udmVydCBpdGVtIGNvbHMgdG8gbnVtZXJpYw0KICBtdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCJWYWxfIiksIH4gYXMubnVtZXJpYygueCkpKSAlPiUgICAgICAgICAgICAgIA0KICBtdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCJWaXZfIiksIH4gYXMubnVtZXJpYygueCkpKSAlPiUNCiAgbXV0YXRlKGFjcm9zcyhzdGFydHNfd2l0aCgiUmVsdl8iKSwgfiBhcy5udW1lcmljKC54KSkpICU+JQ0KICBtdXRhdGUoDQogICAgSUQgPSBhcy5udW1lcmljKElEKSwNCiAgICBDb25kaXRpYSA9IGFzLmZhY3RvcihDb25kaXRpYSksDQogICAgR2VuID0gY2FzZV93aGVuKA0KICAgICAgR2VuID09IDEgfiAiZiIsDQogICAgICBHZW4gPT0gMiB+ICJtIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBHZW4gPSBhcy5mYWN0b3IoR2VuKQ0KICApDQpkZl9tZW1vIDwtIGRmX21lbW9bMTo4MCwgXSAgICMgb25seSB0aGUgZmlyc3QgODAgcm93cyBoYXZlIGRhdGENCmBgYA0KDQoNCiMgQ29tcHV0ZSBzY29yZXMNCg0KIyMgTWVtbyBTY2FsZXMNCg0KYGBge3J9DQpkZl9tZW1vIDwtDQogIGRmX21lbW8gJT4lDQogIHJvd3dpc2UoKSAlPiUNCiAgbXV0YXRlKA0KICAgIFZhbGVuY2UgPSAgbWVhbihjX2Fjcm9zcyhzdGFydHNfd2l0aCgiVmFsXyIpKSwgbmEucm0gPSBUUlVFKSwNCiAgICBWaXZpZG5lc3MgPSBtZWFuKGNfYWNyb3NzKHN0YXJ0c193aXRoKCJWaXZfIikpLCBuYS5ybSA9IFRSVUUpLA0KICAgIFJlbGV2YW5jZSA9IG1lYW4oY19hY3Jvc3Moc3RhcnRzX3dpdGgoIlJlbHZfIikpLCBuYS5ybSA9IFRSVUUpDQogICkgDQojIGlkZW50aWNhbChkZl9tZW1vJFZhbGVuY2UsIHJvd01lYW5zKGRmX21lbW9bLCBwYXN0ZTAoIlZhbF8iLCAxOjEwKV0sIG5hLnJtID0gVFJVRSkpDQojIGlkZW50aWNhbChkZl9tZW1vJFZpdmlkbmVzcywgcm93TWVhbnMoZGZfbWVtb1ssIHBhc3RlMCgiVml2XyIsIDE6MTApXSwgbmEucm0gPSBUUlVFKSkNCiMgaWRlbnRpY2FsKGRmX21lbW8kUmVsZXZhbmNlLCByb3dNZWFucyhkZl9tZW1vWywgcGFzdGUwKCJSZWx2XyIsIDE6MTApXSwgbmEucm0gPSBUUlVFKSkNCmBgYA0KDQojIyBBU0NRDQoNCmBgYHtyfQ0KIyBmdWxsIHNjb3JpbmcgaW4gRUVHX08uNF9yZXN0aW5nX3N0YXRlX2JlaGF2aW9yYWwNCiMjIEFSU1EgKExpa2VydCAxLTUpIC0tIG5vIHJldmVyc2Ugc2NvcmluZywgaW4gdG90YWwgMzAgaXRlbXMgdG8gc2NvcmUgKyAyNSBhZGRpdGlvbmFsIHNpbmdsZSBpdGVtcw0KIyBkaXNjb250OiAxLCAyLCAzDQojIHRvbTogNCwgNSwgNg0KIyBzZWxmOiA3LCA4LCA5DQojIHBsYW5uaW5nOiAxMCwgMTEsIDEyDQojIHNsZWVwOiAxMywgMTQsIDE1DQojIGNvbWZvcnQ6IDE2LCAxNywgMTgNCiMgc29tYXRpYzogMTksIDIwLCAyMQ0KIyBoZWFsdGg6IDIyLCAyMywgMjQNCiMgdmlzdWFsOiAyNSwgMjYsIDI3DQojIHZlcmJhbDogMjgsIDI5LCAzMA0KIyArIDI1IGluZGl2aWR1YWwgaXRlbXMNCg0KZGZfYXJzcSRkaXNjb250X3ByZSA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3ByZV8iLCBjKDEsIDIsIDMpKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikNCmRmX2Fyc3EkdG9tX3ByZSA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3ByZV8iLCBjKDQsIDUsIDYpKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikgDQpkZl9hcnNxJHNlbGZfcHJlIDwtIFNjb3JlTGlrZXJ0KGRmX2Fyc3FbLCBwYXN0ZTAoImFzY3FfcHJlXyIsIGMoNywgOCwgOSkpXSwgbmFwZXJjZW50ID0gMSwgZW5naW5lID0gIm1lYW4iKQ0KZGZfYXJzcSRwbGFubmluZ19wcmUgPC0gU2NvcmVMaWtlcnQoZGZfYXJzcVssIHBhc3RlMCgiYXNjcV9wcmVfIiwgYygxMCwgMTEsIDEyKSldLCBuYXBlcmNlbnQgPSAxLCBlbmdpbmUgPSAibWVhbiIpDQpkZl9hcnNxJHNsZWVwX3ByZSA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3ByZV8iLCBjKDEzLCAxNCwgMTUpKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikgDQpkZl9hcnNxJGNvbWZvcnRfcHJlIDwtIFNjb3JlTGlrZXJ0KGRmX2Fyc3FbLCBwYXN0ZTAoImFzY3FfcHJlXyIsIGMoMTYsIDE3LCAxOCkpXSwgbmFwZXJjZW50ID0gMSwgZW5naW5lID0gIm1lYW4iKQ0KZGZfYXJzcSRzb21hdGljX3ByZSA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3ByZV8iLCBjKDE5LCAyMCwgMjEpKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikNCmRmX2Fyc3EkaGVhbHRoX3ByZSA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3ByZV8iLCBjKDIyLCAyMywgMjQpKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikNCmRmX2Fyc3EkdmlzdWFsX3ByZSA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3ByZV8iLCBjKDI1LCAyNiwgMjcpKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikNCmRmX2Fyc3EkdmVyYmFsX3ByZSA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3ByZV8iLCBjKDI4LCAyOSwgMzApKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikNCg0KZGZfYXJzcSRkaXNjb250X3Bvc3QgPC0gU2NvcmVMaWtlcnQoZGZfYXJzcVssIHBhc3RlMCgiYXNjcV9wb3N0XyIsIGMoMSwgMiwgMykpXSwgbmFwZXJjZW50ID0gMSwgZW5naW5lID0gIm1lYW4iKQ0KZGZfYXJzcSR0b21fcG9zdCA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3Bvc3RfIiwgYyg0LCA1LCA2KSldLCBuYXBlcmNlbnQgPSAxLCBlbmdpbmUgPSAibWVhbiIpIA0KZGZfYXJzcSRzZWxmX3Bvc3QgPC0gU2NvcmVMaWtlcnQoZGZfYXJzcVssIHBhc3RlMCgiYXNjcV9wb3N0XyIsIGMoNywgOCwgOSkpXSwgbmFwZXJjZW50ID0gMSwgZW5naW5lID0gIm1lYW4iKQ0KZGZfYXJzcSRwbGFubmluZ19wb3N0IDwtIFNjb3JlTGlrZXJ0KGRmX2Fyc3FbLCBwYXN0ZTAoImFzY3FfcG9zdF8iLCBjKDEwLCAxMSwgMTIpKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikNCmRmX2Fyc3Ekc2xlZXBfcG9zdCA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3Bvc3RfIiwgYygxMywgMTQsIDE1KSldLCBuYXBlcmNlbnQgPSAxLCBlbmdpbmUgPSAibWVhbiIpIA0KZGZfYXJzcSRjb21mb3J0X3Bvc3QgPC0gU2NvcmVMaWtlcnQoZGZfYXJzcVssIHBhc3RlMCgiYXNjcV9wb3N0XyIsIGMoMTYsIDE3LCAxOCkpXSwgbmFwZXJjZW50ID0gMSwgZW5naW5lID0gIm1lYW4iKQ0KZGZfYXJzcSRzb21hdGljX3Bvc3QgPC0gU2NvcmVMaWtlcnQoZGZfYXJzcVssIHBhc3RlMCgiYXNjcV9wb3N0XyIsIGMoMTksIDIwLCAyMSkpXSwgbmFwZXJjZW50ID0gMSwgZW5naW5lID0gIm1lYW4iKQ0KZGZfYXJzcSRoZWFsdGhfcG9zdCA8LSBTY29yZUxpa2VydChkZl9hcnNxWywgcGFzdGUwKCJhc2NxX3Bvc3RfIiwgYygyMiwgMjMsIDI0KSldLCBuYXBlcmNlbnQgPSAxLCBlbmdpbmUgPSAibWVhbiIpDQpkZl9hcnNxJHZpc3VhbF9wb3N0IDwtIFNjb3JlTGlrZXJ0KGRmX2Fyc3FbLCBwYXN0ZTAoImFzY3FfcG9zdF8iLCBjKDI1LCAyNiwgMjcpKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJtZWFuIikNCmRmX2Fyc3EkdmVyYmFsX3Bvc3QgPC0gU2NvcmVMaWtlcnQoZGZfYXJzcVssIHBhc3RlMCgiYXNjcV9wb3N0XyIsIGMoMjgsIDI5LCAzMCkpXSwgbmFwZXJjZW50ID0gMSwgZW5naW5lID0gIm1lYW4iKQ0KYGBgDQoNCiMjIFBRDQoNCmBgYHtyfQ0KIyBPbmx5IHRvdGFsDQoNCmRmX3BxJFBRIDwtIFNjb3JlTGlrZXJ0KGRmX3BxWywgcGFzdGUwKCJwcV8iLCAxOjEyKV0sIG5hcGVyY2VudCA9IDEsIGVuZ2luZSA9ICJzdW0iKQ0KYGBgDQoNCiMgTWVyZ2UgYWxsIHdpdGggT1hUIGRhdGENCg0KYGBge3J9DQojIEpvaW4gZGF0YWZyYW1lcyB0byBhZGQgY29uZGl0aW9uIA0KZGZfbWVtb19veCA8LSBsZWZ0X2pvaW4oZGZfbWVtbywgZGZfc2FtX294X3dpZGUsIGJ5ID0gam9pbl9ieShJRCA9PSBpZCkpDQpkZl9hcnNxX294IDwtIGxlZnRfam9pbihkZl9hcnNxLCBkZl9zYW1fb3hfd2lkZSwgYnkgPSBqb2luX2J5KElEID09IGlkKSkNCmRmX3BxX294IDwtIGxlZnRfam9pbihkZl9wcSwgZGZfc2FtX294X3dpZGUsIGJ5ID0gam9pbl9ieShJRCA9PSBpZCkpDQpgYGANCg0KDQojIE5ldyBkYXRhOiBkdXJhdGlvbg0KDQojIyBSZWFkIGR1cmF0aW9uIGRhdGENCg0KYGBge3IsIGVjaG89RkFMU0V9DQpsYXN0X3hsc3hfZmlsZSA8LQ0KICBkaXIoaGVyZTo6aGVyZSgpLCBwYXR0ZXJuID0gIkRBVEUgU0FNLip4bHN4IiwgZnVsbC5uYW1lcyA9IFRSVUUpICU+JQ0KICBmaWxlLmluZm8oKSAlPiUNCiAgZHBseXI6OmFycmFuZ2UoZHBseXI6OmRlc2MoY3RpbWUpKSAlPiUNCiAgZHBseXI6OnNsaWNlKDEpICU+JQ0KICByb3cubmFtZXMoKQ0KDQpkZl9kdXIgPC0gcmVhZHhsOjpyZWFkX3hsc3gocGF0aCA9IGxhc3RfeGxzeF9maWxlLCANCiAgc2hlZXQgPSAiZHVyYXRhSU5SRUdJU1RSQVJJIHNhbTIwMjIiLCBza2lwID0gMCwgDQogIGNvbF90eXBlcyA9IGMocmVwKCJ0ZXh0IiwgMyksIHJlcCgiZGF0ZSIsIDM1KSkpICAjIHN0dXBpZCBmb3JtYXQ6IGRhdGEgZm9ybWF0ZSBoaDptbSBBTQ0KDQpkZl9kdXIgPC0gDQogIGRmX2R1ciAlPiUgDQogIHNlbGVjdCgxOjMsIGNvbnRhaW5zKCJwb3Zlc3RpcmlpIikpDQoNCmRmX2R1cl9tcyA8LSANCiAgZGZfZHVyICU+JSANCiAgbXV0YXRlKENvbmRpdGlhID0gdG91cHBlcihDb25kaXRpYSkpICU+JSANCiAgbXV0YXRlKA0KICAgIENvbmRpdGlhID0NCiAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgc3RyX2RldGVjdChDb25kaXRpYSwgIlZSIE1JUk9TIikgfiAiVlIgTUlST1MiLA0KICAgICAgICAuZGVmYXVsdCA9IENvbmRpdGlhDQogICAgICApDQogICkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBleGNlbCBlbmNvZGluZzogSD1NLCBNPVMNCiAgbXV0YXRlKGFjcm9zcyg0OjEyLCB+Zm9ybWF0KGFzLlBPU0lYY3QoLngpLCBmb3JtYXQgPSAiJUg6JU0iKSkpICU+JSAgICMgZXh0cmFjdCBvbmx5IE0gYW5kIFMNCiAgbXV0YXRlKGFjcm9zcyg0OjEyLCB+cGVyaW9kX3RvX3NlY29uZHMobXMoLngpKSkpICU+JSAgICAgICAgICAgICAgICMgY29udmVydCB0byBTDQogICMgbXV0YXRlKGFjcm9zcyg0OjEyLCB+c2Vjb25kcykpDQogICMgdHJhbnNmb3JtIHRvIHNlY29uZHMNCiAgZHJvcF9uYShDb25kaXRpYSkgJT4lIA0KICBtdXRhdGUobWVkX2R1ciA9IHJvd01lZGlhbnMoYXMubWF0cml4KC5bLCA0OjEyXSksIG5hLnJtID0gVFJVRSksDQogICAgICAgICBtZWFuX2R1ciA9IHJvd01lYW5zKC5bLCA0OjEyXSwgbmEucm0gPSBUUlVFKSkNCg0KYGBgDQoNCiMjIE1lcmdlIGFsbCB3aXRoIGR1cmF0aW9uIGRhdGENCg0KYGBge3J9DQpkZl9kdXJfbXMkSUQgPC0gYXMubnVtZXJpYyhkZl9kdXJfbXMkSUQpDQoNCiMgSm9pbiBkYXRhZnJhbWVzIHRvIGFkZCBjb25kaXRpb24gDQpkZl9tZW1vX294IDwtIGxlZnRfam9pbihkZl9tZW1vX294LCBkZl9kdXJfbXMsIGJ5ID0gam9pbl9ieShJRCA9PSBJRCkpDQpkZl9hcnNxX294IDwtIGxlZnRfam9pbihkZl9hcnNxX294LCBkZl9kdXJfbXMsIGJ5ID0gam9pbl9ieShJRCA9PSBJRCkpDQpkZl9wcV9veCA8LSBsZWZ0X2pvaW4oZGZfcHFfb3gsIGRmX2R1cl9tcywgYnkgPSBqb2luX2J5KElEID09IElEKSkNCmBgYA0KDQoNCg0KIyBBbmFseXNlcyANCg0KIyMgTWVtbw0KDQpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9N30NCmRmX21lbW8gJT4lDQogIGdnc3RhdHNwbG90OjpnZ2JldHdlZW5zdGF0cygNCiAgICB4ID0gQ29uZGl0aWEsDQogICAgeSA9IFZhbGVuY2UsDQogICAgb3V0bGllci5sYWJlbCA9IElELA0KICAgIHhsYWIgPSAiIg0KICApDQoNCmRmX21lbW8gJT4lDQogIGdnc3RhdHNwbG90OjpnZ2JldHdlZW5zdGF0cygNCiAgICB4ID0gQ29uZGl0aWEsDQogICAgeSA9IFZpdmlkbmVzcywNCiAgICBvdXRsaWVyLmxhYmVsID0gSUQsDQogICAgeGxhYiA9ICIiDQogICkNCg0KZGZfbWVtbyAlPiUNCiAgZ2dzdGF0c3Bsb3Q6OmdnYmV0d2VlbnN0YXRzKA0KICAgIHggPSBDb25kaXRpYSwNCiAgICB5ID0gUmVsZXZhbmNlLA0KICAgIG91dGxpZXIubGFiZWwgPSBJRCwNCiAgICB4bGFiID0gIiINCiAgKQ0KYGBgDQoNCg0KIyMgUFENCg0KYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTd9DQpkZl9wcSAlPiUNCiAgZ2dzdGF0c3Bsb3Q6OmdnYmV0d2VlbnN0YXRzKA0KICAgIHggPSBDb25kaXRpYSwNCiAgICB5ID0gUFEsDQogICAgb3V0bGllci5sYWJlbCA9IElELA0KICAgIHhsYWIgPSAiIg0KICApDQpgYGANCg0KDQojIyBPWFQNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD03fQ0KZGZfc2FtX294X2xvbmcgJT4lDQogIG11dGF0ZShwcmVwb3N0ID0gZmFjdG9yKHByZXBvc3QsIGxldmVscyA9IGMoIlBSRSIsICJQT1NUIikpKSAlPiUgDQogIGdyb3VwX2J5KGlkLCBDb25kaXRpYSkgJT4lIA0KICBmaWx0ZXIobigpID4gMSkgJT4lICAgICAgICAgICAgICAgICAgICAgICMgZXhjbHVkZSBpbmNvbXBsZXRlDQogIHVuZ3JvdXAoKSAlPiUgDQogIGdnc3RhdHNwbG90Ojpncm91cGVkX2dnd2l0aGluc3RhdHMoDQogICAgeCA9IHByZXBvc3QsDQogICAgeSA9IG94aXRvY2luYV9ub3JtYWxpemF0YSwNCiAgICBncm91cGluZy52YXIgPSBDb25kaXRpYSwgDQogICAgdHlwZSA9ICJwIiwNCiAgICBiZi5tZXNzYWdlID0gRkFMU0UsDQogICAgYW5ub3RhdGlvbi5hcmdzID0gbGlzdCh0aXRsZSA9ICJQcmUgJiBQb3N0IE9YIGRhdGEiLCBzdWJ0aXRsZSA9ICIiKQ0KICApDQoNCmRmX3NhbV9veF9sb25nICU+JQ0KICBtdXRhdGUocHJlcG9zdCA9IGZhY3RvcihwcmVwb3N0LCBsZXZlbHMgPSBjKCJQUkUiLCAiUE9TVCIpKSkgJT4lIA0KICBmaWx0ZXIob3hpdG9jaW5hX25vcm1hbGl6YXRhIDwgMykgJT4lICAgICMgZXhjbHVkZSBvdXRsaWVyIGZyb20gIlZSIG1pcm9zIg0KICBncm91cF9ieShpZCwgQ29uZGl0aWEpICU+JSANCiAgZmlsdGVyKG4oKSA+IDEpICU+JSAgICAgICAgICAgICAgICAgICAgICAjIGV4Y2x1ZGUgaW5jb21wbGV0ZQ0KICB1bmdyb3VwKCkgJT4lIA0KICBnZ3N0YXRzcGxvdDo6Z3JvdXBlZF9nZ3dpdGhpbnN0YXRzKA0KICAgIHggPSBwcmVwb3N0LA0KICAgIHkgPSBveGl0b2NpbmFfbm9ybWFsaXphdGEsDQogICAgZ3JvdXBpbmcudmFyID0gQ29uZGl0aWEsDQogICAgdHlwZSA9ICJwIiwNCiAgICBiZi5tZXNzYWdlID0gRkFMU0UsDQogICAgYW5ub3RhdGlvbi5hcmdzID0gbGlzdCh0aXRsZSA9ICJQcmUgJiBQb3N0IE9YIGRhdGEiLCBzdWJ0aXRsZSA9ICJPdXRsaWVyIGV4Y2x1ZGVkIGZyb20gVlIgbWlyb3MiKQ0KICApDQpgYGANCg0KIyMjIFJNIEFOT1ZBDQoNCmBgYHtyfQ0KZGZfc2FtX294X2xvbmdfYW5vdmEgPC0NCiAgZGZfc2FtX294X2xvbmcgJT4lDQogIG11dGF0ZShwcmVwb3N0ID0gZmFjdG9yKHByZXBvc3QsIGxldmVscyA9IGMoIlBSRSIsICJQT1NUIikpKSAlPiUgDQogIGZpbHRlcihveGl0b2NpbmFfbm9ybWFsaXphdGEgPCAzKQ0KDQojIEFOT1ZBDQphbm92YV90ZXN0KA0KICBkYXRhID0gZGZfc2FtX294X2xvbmdfYW5vdmEsIHdpZCA9IGlkLA0KICBkdiA9IG94aXRvY2luYV9ub3JtYWxpemF0YSwgDQogIHdpdGhpbiA9IHByZXBvc3QsIGJldHdlZW4gPSBDb25kaXRpYQ0KKQ0KDQojIGNvbXBhcmlzb25zIGZvciB0cmVhdG1lbnQgdmFyaWFibGUNCmRmX3NhbV9veF9sb25nX2Fub3ZhICU+JQ0KICBncm91cF9ieShwcmVwb3N0KSAlPiUgDQogIHBhaXJ3aXNlX3RfdGVzdCgNCiAgICBveGl0b2NpbmFfbm9ybWFsaXphdGEgfiBDb25kaXRpYSwgcGFpcmVkID0gRkFMU0UsIA0KICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJob2xtIg0KICApDQoNCiMgY29tcGFyaXNvbnMgZm9yIHRpbWUgdmFyaWFibGUNCmRmX3NhbV9veF9sb25nX2Fub3ZhICU+JQ0KICBncm91cF9ieShpZCwgQ29uZGl0aWEpICU+JSANCiAgZmlsdGVyKG4oKSA+IDEpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ3JvdXBfYnkoQ29uZGl0aWEpICU+JSANCiAgcGFpcndpc2VfdF90ZXN0KA0KICAgIG94aXRvY2luYV9ub3JtYWxpemF0YSB+IHByZXBvc3QsIHBhaXJlZCA9IFRSVUUsIA0KICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJob2xtIg0KICApDQpgYGANCg0KIyMgQVJTUSANCg0KYGBge3J9DQpkZl9hcnNxX294IDwtIA0KICBkZl9hcnNxX294ICU+JSANCiAgZHBseXI6Om11dGF0ZSgNCiAgICB0b21fZGlmID0gdG9tX3Bvc3QgLSB0b21fcHJlLA0KICAgIHNlbGZfZGlmID0gc2VsZl9wb3N0IC0gc2VsZl9wcmUsDQogICAgcGxhbm5pbmdfZGlmID0gcGxhbm5pbmdfcG9zdCAtIHBsYW5uaW5nX3ByZSwNCiAgICBzbGVlcF9kaWYgPSBzbGVlcF9wb3N0IC0gc2xlZXBfcHJlLA0KICAgIGNvbWZvcnRfZGlmID0gY29tZm9ydF9wb3N0IC0gY29tZm9ydF9wcmUsDQogICAgc29tYXRpY19kaWYgPSBzb21hdGljX3Bvc3QgLSBzb21hdGljX3ByZSwNCiAgICBoZWFsdGhfZGlmID0gaGVhbHRoX3Bvc3QgLSBoZWFsdGhfcHJlLA0KICAgIHZpc3VhbF9kaWYgPSB2aXN1YWxfcG9zdCAtIHZpc3VhbF9wcmUsDQogICAgdmVyYmFsX2RpZiA9IHZlcmJhbF9wb3N0IC0gdmVyYmFsX3ByZSwNCiAgKSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoQ29uZGl0aWEgPSBDb25kaXRpYS54KQ0KDQogIA0KZm9yIChjb2wgaW4gZ3JlcCgiX2RpZiIsIG5hbWVzKGRmX2Fyc3Ffb3gpLCB2YWx1ZSA9IFRSVUUpKSB7IA0KICBnZ3N0YXRzcGxvdDo6Z2diZXR3ZWVuc3RhdHMoDQogICAgZGF0YSA9IGRmX2Fyc3Ffb3gsDQogICAgeCA9IENvbmRpdGlhLA0KICAgIHkgPSB7eyBjb2wgfX0sDQogICAgZ3JvdXBpbmcudmFyID0gQ29uZGl0aWEsDQogICAgdHlwZSA9ICJucCIsDQogICAgYmYubWVzc2FnZSA9IEZBTFNFDQogICkgJT4lIHByaW50KCkNCn0NCmBgYA0KDQoNCiMgQW5hbHlzZXMgZHVyYXRpb24NCg0KYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTd9DQpkZl9kdXJfbXMgJT4lDQogIGdnc3RhdHNwbG90OjpnZ2JldHdlZW5zdGF0cygNCiAgICB4ID0gQ29uZGl0aWEsDQogICAgeSA9IG1lZF9kdXIsDQogICAgb3V0bGllci5sYWJlbCA9IElELA0KICAgIHhsYWIgPSAiIg0KICApDQoNCmRmX2R1cl9tcyAlPiUNCiAgZ2dzdGF0c3Bsb3Q6OmdnYmV0d2VlbnN0YXRzKA0KICAgIHggPSBDb25kaXRpYSwNCiAgICB5ID0gbWVhbl9kdXIsDQogICAgb3V0bGllci5sYWJlbCA9IElELA0KICAgIHhsYWIgPSAiIg0KICApDQpgYGANCg0KDQojIyBDb3JyZWxhdGlvbnMgLSBkdXJhdGlvbiAmIG1lbW9yYWJpbGl0eQ0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCmRmX21lbW9fb3ggJT4lIA0KICBkcGx5cjo6c2VsZWN0KA0KICAgICJveGl0b2NpbmFfcGdfbWxfRElGIiwgIm94aXRvY2luYV9ub3JtYWxpemF0YV9ESUYiLCANCiAgICAiVmFsZW5jZSIsICJWaXZpZG5lc3MiLCAiUmVsZXZhbmNlIiwgIm1lZF9kdXIiLCAibWVhbl9kdXIiIA0KICApICU+JSANCiAgUGVyZm9ybWFuY2VBbmFseXRpY3M6OmNoYXJ0LkNvcnJlbGF0aW9uKCkNCmBgYA0KDQoNCiMjIENvcnJlbGF0aW9ucyB3aXRoIE9YIGRpZmZlcmVuY2UNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpzYW1lX2NvbHMgPC0gaW50ZXJzZWN0KG5hbWVzKGRmX21lbW9fb3gpLCBuYW1lcyhkZl9wcV9veCkpDQoNCmRwbHlyOjpsZWZ0X2pvaW4oZGZfbWVtb19veCwgZGZfcHFfb3gsIGJ5ID0gc2FtZV9jb2xzKSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoQ29uZGl0aWEgPSBDb25kaXRpYS54KSAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoQ29uZGl0aWEgPT0gIlZSIikgJT4lIA0KICBkcGx5cjo6c2VsZWN0KA0KICAgICJveGl0b2NpbmFfcGdfbWxfRElGIiwgIm94aXRvY2luYV9ub3JtYWxpemF0YV9ESUYiLCANCiAgICAiVmFsZW5jZSIsICJWaXZpZG5lc3MiLCAiUmVsZXZhbmNlIiwgIlBRIiwgIm1lZF9kdXIiLCAibWVhbl9kdXIiIA0KICApICU+JSANCiAgUGVyZm9ybWFuY2VBbmFseXRpY3M6OmNoYXJ0LkNvcnJlbGF0aW9uKCkNCnRpdGxlKCJWUiIpDQoNCg0KZHBseXI6OmxlZnRfam9pbihkZl9tZW1vX294LCBkZl9wcV9veCwgYnkgPSBzYW1lX2NvbHMpICU+JSANCiAgZHBseXI6OnJlbmFtZShDb25kaXRpYSA9IENvbmRpdGlhLngpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKENvbmRpdGlhID09ICJWUiBtaXJvcyIpICU+JSANCiAgZHBseXI6OnNlbGVjdCgNCiAgICAib3hpdG9jaW5hX3BnX21sX0RJRiIsICJveGl0b2NpbmFfbm9ybWFsaXphdGFfRElGIiwgDQogICAgIlZhbGVuY2UiLCAiVml2aWRuZXNzIiwgIlJlbGV2YW5jZSIsICJQUSIsICJtZWRfZHVyIiwgIm1lYW5fZHVyIiAgDQogICkgJT4lIA0KICBQZXJmb3JtYW5jZUFuYWx5dGljczo6Y2hhcnQuQ29ycmVsYXRpb24oKQ0KdGl0bGUoIlZSIG1pcm9zIikNCg0KZHBseXI6OmxlZnRfam9pbihkZl9tZW1vX294LCBkZl9wcV9veCwgYnkgPSBzYW1lX2NvbHMpICU+JQ0KICBkcGx5cjo6cmVuYW1lKENvbmRpdGlhID0gQ29uZGl0aWEueCkgJT4lDQogIGRwbHlyOjpmaWx0ZXIoQ29uZGl0aWEgPT0gIlZSIHNvY2lhbCIpICU+JSANCiAgZHBseXI6OnNlbGVjdCgNCiAgICAib3hpdG9jaW5hX3BnX21sX0RJRiIsICJveGl0b2NpbmFfbm9ybWFsaXphdGFfRElGIiwgDQogICAgIlZhbGVuY2UiLCAiVml2aWRuZXNzIiwgIlJlbGV2YW5jZSIsICJQUSIsICJtZWRfZHVyIiwgIm1lYW5fZHVyIiAgDQogICkgJT4lIA0KICBQZXJmb3JtYW5jZUFuYWx5dGljczo6Y2hhcnQuQ29ycmVsYXRpb24oKQ0KdGl0bGUoIlZSIHNvY2lhbCIpDQpgYGANCg0KDQpgYGB7ciwgZmlnLndpZHRoPTExLCBmaWcuaGVpZ2h0PTExLCB3YXJuaW5nPUZBTFNFfQ0KZGZfYXJzcV9veCAlPiUNCiAgZHBseXI6OmZpbHRlcihDb25kaXRpYSA9PSAiVlIiKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoDQogICAgIm94aXRvY2luYV9wZ19tbF9ESUYiLCAib3hpdG9jaW5hX25vcm1hbGl6YXRhX0RJRiIsIA0KICAgIGNvbnRhaW5zKCJfZGlmIikgDQogICkgJT4lIA0KICBQZXJmb3JtYW5jZUFuYWx5dGljczo6Y2hhcnQuQ29ycmVsYXRpb24oKQ0KdGl0bGUoIlZSIikNCg0KZGZfYXJzcV9veCAlPiUNCiAgZHBseXI6OmZpbHRlcihDb25kaXRpYSA9PSAiVlIgbWlyb3MiKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoDQogICAgIm94aXRvY2luYV9wZ19tbF9ESUYiLCAib3hpdG9jaW5hX25vcm1hbGl6YXRhX0RJRiIsIA0KICAgIGNvbnRhaW5zKCJfZGlmIikgDQogICkgJT4lIA0KICBQZXJmb3JtYW5jZUFuYWx5dGljczo6Y2hhcnQuQ29ycmVsYXRpb24oKQ0KdGl0bGUoIlZSIG1pcm9zIikNCg0KZGZfYXJzcV9veCAlPiUNCiAgZHBseXI6OmZpbHRlcihDb25kaXRpYSA9PSAiVlIgc29jaWFsIikgJT4lIA0KICBkcGx5cjo6c2VsZWN0KA0KICAgICJveGl0b2NpbmFfcGdfbWxfRElGIiwgIm94aXRvY2luYV9ub3JtYWxpemF0YV9ESUYiLCANCiAgICBjb250YWlucygiX2RpZiIpIA0KICApICU+JSANCiAgUGVyZm9ybWFuY2VBbmFseXRpY3M6OmNoYXJ0LkNvcnJlbGF0aW9uKCkNCnRpdGxlKCJWUiBzb2NpYWwiKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQo8IS0tIFNlc3Npb24gSW5mbyBhbmQgTGljZW5zZSAtLT4NCg0KPGJyPg0KDQojIFNlc3Npb24gSW5mbw0KYGBge3Igc2Vzc2lvbl9pbmZvLCBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnbWFya3VwJ30NCnNlc3Npb25JbmZvKCkgICAgDQpgYGANCg0KPCEtLSBGb290ZXIgLS0+DQombmJzcDsNCjxociAvPg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPkEgd29yayBieSA8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vQ2xhdWRpdVBhcGFzdGVyaS8iPkNsYXVkaXUgUGFwYXN0ZXJpPC9hPjwvcD4NCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBzdHlsZT0iY29sb3I6ICM4MDgwODA7Ij48ZW0+Y2xhdWRpdS5wYXBhc3RlcmlAZ21haWwuY29tPC9lbT48L3NwYW4+PC9wPg0KJm5ic3A7DQo=