1 Setup

# check where I am
if (basename(getwd()) == "programs"){
  path = "../"
} else {
  path = ""
}

library(tidyverse)
library(RocheRWDS)
library(dplyr)
library(DT)
library(tableone)
library(survival)
library(survminer)
library(DiagrammeR)
library(irr)
library(ICC)
library(raters)
library(rel)
library(psych)
library(BlandAltmanLeh)

2 Get data

d_lines <- readr::read_csv(
  paste0(path,"import/lines.csv"),
    col_types = cols(
      PatientID = col_character(),
      start_induct = col_date(format = ""),
      end_induct = col_date(format = ""),
      selectdrugs_induct = col_character(),
      Transplant = col_date(format = ""),
      start_cons = col_date(format = ""),
      end_cons = col_date(format = ""),
      selectdrugs_cons = col_character(),
      start_maint = col_date(format = ""),
      end_maint = col_date(format = ""),
      selectdrugs_maint = col_character(),
      coder = col_character(),
      line_notes = col_character()
  )
)

 d_lines %>%
   summarise(
     Rows = n(),
     Patients = n_distinct(PatientID),
     EarliestLineStart = min(start_induct)
   ) %>%
   knitr::kable(
     caption = "Patient count on lines table"
   )
Patient count on lines table
Rows Patients EarliestLineStart
357 100 2011-02-01

2.1 Clean data

# add in line count
  d_lines <- d_lines %>%
    group_by(PatientID,coder) %>%
    arrange(PatientID,start_induct) %>%
    mutate(
      LineNumber = row_number(),
      TotalLines = n()
    ) 

d_lines %>%
  head(3) %>% 
  knitr::kable(
    caption = "First 3 rows of the line table"
  )
First 3 rows of the line table
PatientID start_induct end_induct selectdrugs_induct Transplant start_cons end_cons selectdrugs_cons start_maint end_maint selectdrugs_maint coder line_notes LineNumber TotalLines
F00E4B82A2B05 2016-08-15 2016-11-14 bortezomib, dexamethasone, lenalidomide NA 2017-10-15 2017-10-15 No consolidation 2017-10-15 2017-10-15 No maintenance wassnere Add notes here 1 1
F00E4B82A2B05 2016-08-15 2016-09-25 bortezomib, dexamethasone NA 2017-11-08 2017-11-08 No consolidation 2017-11-08 2017-11-08 No maintenance byonj Add notes here 1 2
F00E4B82A2B05 2016-09-26 2017-06-22 bortezomib, dexamethasone, lenalidomide NA 2017-11-08 2017-11-08 No consolidation 2017-11-08 2017-11-08 No maintenance byonj Add notes here 2 2

Both PDCO colleagues identified an issue with FE0CCE4BF9D2B:

I ran into an issue on a pat. with double transplant: when recording the second transplant induction came up again in the record (Dble record for hte indcution regimen for Pat. )

This patient appears to have received a double auto transplant. Unable to code both transplants in the same line of therapy, but both transplants should be considered as part of the same line of therapy.

How to embed transplants in lines is a discussion point for Flatiron.

2.2 Summarise

  d_lines %>%
    group_by(coder) %>%
    summarise(
      `Line table` = n_distinct(PatientID)
    ) %>%
  knitr::kable(
    caption = "100 in both line table"
  )
100 in both line table
coder Line table
byonj 100
wassnere 100

3 Induction Analysis

  # take just first line
    d_lines_induction <- d_lines %>%
      filter(LineNumber == 1) %>% ungroup
  
  # put side by side
    d_lines_induction <- left_join(
      d_lines_induction %>%
        filter(coder == "wassnere") %>%
        select(
          PatientID,
          start_induct_w = start_induct,
          end_induct_w = end_induct,
          selectdrugs_induct_w = selectdrugs_induct,
          TotalLines_w=TotalLines
        ),
      d_lines_induction %>%
        filter(coder == "byonj") %>%
        select(
          PatientID,
          start_induct_b = start_induct,
          end_induct_b = end_induct,
          selectdrugs_induct_b = selectdrugs_induct,
          TotalLines_b=TotalLines
        ),
      by = "PatientID"
    )  %>%
    
    
  # check if lines are the same
    mutate(
      duration_b = as.numeric(end_induct_b - start_induct_b),
      duration_w = as.numeric(end_induct_w - start_induct_w),
      # check if same
      same_induction = ifelse(selectdrugs_induct_b == selectdrugs_induct_w,1,0),
      # pretty same
      same_inductionC = ifelse(same_induction == 1,"Same 1L induction","Different 1L induction"),
      start_date_diff = as.numeric(start_induct_b-start_induct_w),
      duration_diff = duration_b - duration_w,
      #check if same number of total lines
      same_numlines = ifelse(TotalLines_b == TotalLines_w, 1, 0),
      same_numLinesC = ifelse(same_numlines == 1, "Same number of lines", "Different number of lines")
    )

3.1 Induction First Line Name

Check if the induction 1L has the same name.

t_sameinduction <-  d_lines_induction %>%
  rename(`1L induction` = same_inductionC) %>%
  group_by(
    `1L induction`
  ) %>%
  summarise(
    n = n()
  ) %>%
  mutate(
    n = paste0(n," (",round(100*n/sum(n)),"%)")
  )

3.1.1 Number with the same 1L name

t_sameinduction %>% knitr::kable(
  caption = "Number with the same 1L"
)
Number with the same 1L
1L induction n
Different 1L induction 14 (14%)
Same 1L induction 86 (86%)

3.1.2 Inter-rater reliability using Fleiss’ Kappa

Method 1 = kappam.fleiss function Method 2 = ckap function

# Check for inter-rater reliability of induction line name
induction_1L <- d_lines_induction[, c("selectdrugs_induct_w", "selectdrugs_induct_b")]
result <- kappam.fleiss(
  induction_1L
  )

result
##  Fleiss' Kappa for m Raters
## 
##  Subjects = 100 
##    Raters = 2 
##     Kappa = 0.828 
## 
##         z = 18.4 
##   p-value = 0

Without needing to look at the p-value, the Kappa statistic is 0.8281049 using the kappam.fleiss function, suggesting a high level of agreement between the two raters.

result <- ckap(
  data = induction_1L, weight = c("unweighted"),
  std.err = c("Fleiss"), conf.level = 0.95, R = 0
  )

result
## Call:
## ckap(data = induction_1L, weight = c("unweighted"), std.err = c("Fleiss"), 
##     conf.level = 0.95, R = 0)
## 
##       Estimate   StdErr  LowerCB UpperCB
## Const 0.828242 0.041323 0.746248  0.9102
## 
## Maximum kappa = 0.89
## Kappa/maximum kappa = 0.93
## Confidence level = 95%
## Observations = 2
## Sample size = 100

This is repeated using ckap, where the Kappa statistic is 0.8282419 with a 0.95% confidence interval of 0.746 to 0.91.

3.2 Induction Number of Lines

t_same_numlines <- d_lines_induction %>%
  group_by(
    same_numLinesC
  ) %>%
  summarise(
    n = n()
  ) %>%
  mutate(
    n = paste0(n," (",round(100*n/sum(n)),"%)")
  )

3.2.1 Number with the same number of total lines

t_same_numlines %>% knitr::kable(
  caption = "Number with the same number of total lines"
)
Number with the same number of total lines
same_numLinesC n
Different number of lines 23 (23%)
Same number of lines 77 (77%)

3.2.2 Inter-rater reliability

total_lines <-d_lines_induction[, c("TotalLines_w", "TotalLines_b")]
result <- kappam.fleiss(
  total_lines
  )

result
##  Fleiss' Kappa for m Raters
## 
##  Subjects = 100 
##    Raters = 2 
##     Kappa = 0.628 
## 
##         z = 9.81 
##   p-value = 0

Without needing to look at the p-value, the Kappa statistic is 0.6282528 using the kappam.fleiss function, suggesting a high level of agreement between the two raters.

result <- ckap(
  data = total_lines, weight = c("unweighted"),
  std.err = c("Fleiss"), conf.level = 0.95, R = 0
  )

result
## Call:
## ckap(data = total_lines, weight = c("unweighted"), std.err = c("Fleiss"), 
##     conf.level = 0.95, R = 0)
## 
##       Estimate   StdErr  LowerCB UpperCB
## Const 0.629689 0.060728 0.509191  0.7502
## 
## Maximum kappa = 0.86
## Kappa/maximum kappa = 0.74
## Confidence level = 95%
## Observations = 2
## Sample size = 100

This is repeated using ckap, where the Kappa statistic is 0.6296893 with a 0.95% confidence interval of 0.509 to 0.75.

3.3 Timing of lines

Among those with same 1L induction line name:

# Filter to those with same 1L induction line name
induction_data <- d_lines_induction %>%
  filter(same_induction==1) 

3.3.1 First line start

Difference in days between the two raters in the nrow(induction_data) patients with the same 1L.

jb_getstats <- function(x, round = 2){
  temp <- t.test(x, conf.level=0.95)
  mean <- paste0(
    round(temp$estimate,round),
    " (95%CI ",paste(round(temp$conf.int,round), collapse = ", "),")"
  )
  
  median = paste0(
    round(median(x),round)," (IQR ",
    round(quantile(x, probs = 0.25),round),", ",
    round(quantile(x, probs = 0.75),round),")"
  )
  
  range = range(x)
  
  return(
    list(
      mean = mean,
      median = median,
      range = range
    )
  )
}

result <- jb_getstats(induction_data$start_date_diff)
  • Mean: -14.09 (95%CI -39.4, 11.21)
  • Median: 0 (IQR 0, 0)
  • Range: -1095, 8

Histogram of difference in days between 1L start.

ggplot(
  induction_data,
  mapping=aes(start_date_diff)) +
  labs(
    title= "Difference in start day of 1L between PDCO coders",
    subtitle = paste0(
      "In the ",
      nrow(induction_data),
      " with the same 1L"),
    y = "Count",
    x = "Difference in 1L start (Days)"
    ) +
  geom_histogram(binwidth = 10) +
  theme_classic()

OUTLIER # F86D752B756B6: Coder W seems to have copied the wrong line start and end from the abstracted data.

 

3.3.2 1L Induction Start Date, ICC (TBD)

Only done for duration at the moment.

#Check for ICC

3.4 1L Induction duration

Among those with same 1L induction line name:

duration_1L <-induction_data[, c("duration_b", "duration_w")] 

3.4.1 1L Induction duration, ICC

induction_data %>%
  # reformat data for function
  select(
    duration_b, duration_w
  ) %>% 
  # run function
  irr::icc()
##  Single Score Intraclass Correlation
## 
##    Model: oneway 
##    Type : consistency 
## 
##    Subjects = 86 
##      Raters = 2 
##      ICC(1) = 0.962
## 
##  F-Test, H0: r0 = 0 ; H1: r0 > 0 
##    F(85,86) = 52 , p = 1.3e-50 
## 
##  95%-Confidence Interval for ICC Population Values:
##   0.943 < ICC < 0.975

3.4.2 1L Induction duration, Blant-Altman Plot

# temp not used currently - move to ggplot2 later?
#temp <- bland.altman.stats(induction_data$duration_b, induction_data$duration_w)

bland.altman.plot(
  induction_data$duration_b, 
  induction_data$duration_w, 
  main="Bland Altman Plot for Difference in 1L Duration",  
  xlab="Mean Duration", 
  ylab="Differences in Days",
  conf.int=.95
  )

## NULL

3.4.3 1L Induction duration, Histogram

result <- jb_getstats(induction_data$duration_diff)
  • Mean: -0.7 (95%CI -17.9, 16.5)
  • Median: 0 (IQR 0, 0)
  • Range: -365, 525
ggplot(
  d_lines_induction %>% filter(same_induction == 1),
  mapping=aes(duration_diff)) +
  labs(
    title= "Difference in duration of 1L between PDCO coders",
    subtitle = paste0(
      "In the ",
      nrow(induction_data),
      " with the same 1L"),
    y = "Count",
    x = "Difference in duration (Days)"
    ) +
  geom_histogram(binwidth = 10) +
  theme_classic()

OUTLIER # F15146C46C327: Coders agree on startdate, but coder W thinks ends >1 year later than coder B

OUTLIER # F5F02439862C4: Coders agree on startdate, but coder B thinks ends >1 year later than coder W

OUTLIER # F941EA1E93C3F: Coder B assigned (-) followup time.

 

4 All treatment sequence

Among the all patients, check how many have the same line names across all lines. Coders have coded up to 6 lines.

for(i in 1:6) {
  for(j in c("byonj", "wassnere")){
  assign(paste("d_lines_induction_", i, "_", j, sep=""), 
         subset(d_lines %>% ungroup, LineNumber== i & coder ==j)
         ) 
    }
}

d_lines_induction_new <-
left_join(d_lines_induction_1_byonj %>% 
              select(PatientID, induct_1_byonj = selectdrugs_induct), 
          d_lines_induction_1_wassnere %>%
              select(PatientID, induct_1_wassnere = selectdrugs_induct), 
          by='PatientID')  %>%
left_join(., 
          d_lines_induction_2_byonj %>%
              select(PatientID, induct_2_byonj = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_2_wassnere %>%
              select(PatientID, induct_2_wassnere = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_3_byonj %>%
              select(PatientID, induct_3_byonj = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_3_wassnere %>%
              select(PatientID, induct_3_wassnere = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_4_byonj %>%
              select(PatientID, induct_4_byonj = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_4_wassnere %>%
              select(PatientID, induct_4_wassnere = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_5_byonj %>%
              select(PatientID, induct_5_byonj = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_5_wassnere %>%
              select(PatientID, induct_5_wassnere = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_6_byonj %>%
              select(PatientID, induct_6_byonj = selectdrugs_induct),
          by='PatientID') %>%
left_join(., 
          d_lines_induction_6_wassnere %>%
              select(PatientID, induct_6_wassnere = selectdrugs_induct),
          by='PatientID') %>%
  
  mutate(
    same_induction_1L=ifelse(induct_1_byonj==induct_1_wassnere, 1, 0),
    same_induction_2L=ifelse(induct_2_byonj==induct_2_wassnere|(is.na(induct_2_byonj) &is.na(induct_2_wassnere)), 1, 0),
    same_induction_3L=ifelse(induct_3_byonj==induct_3_wassnere|(is.na(induct_3_byonj) &is.na(induct_3_wassnere)), 1, 0),
    same_induction_4L=ifelse(induct_4_byonj==induct_4_wassnere|(is.na(induct_4_byonj) &is.na(induct_4_wassnere)), 1, 0),
    same_induction_5L=ifelse(induct_5_byonj==induct_5_wassnere|(is.na(induct_5_byonj) &is.na(induct_5_wassnere)), 1, 0),
    same_induction_6L=ifelse(induct_6_byonj==induct_6_wassnere|(is.na(induct_6_byonj) &is.na(induct_6_wassnere)), 1, 0),
  
    all_same_induction=ifelse((same_induction_1L==0|is.na(same_induction_1L))
       |(same_induction_2L==0|is.na(same_induction_2L))
       |(same_induction_3L==0|is.na(same_induction_3L))
       |(same_induction_4L==0|is.na(same_induction_4L))
       |(same_induction_5L==0|is.na(same_induction_5L))
       |(same_induction_6L==0|is.na(same_induction_6L)), "Different induction", "Same induction for all sequence")
)
all_same_induction <-  d_lines_induction_new %>%
  rename(`All induction` = all_same_induction) %>%
  group_by(
    `All induction`
  ) %>%
  summarise(
    n = n()
  ) %>%
  mutate(
    n = paste0(n," (",round(100*n/sum(n)),"%)")
  )

4.1 Number with the same induction line across all line numbers

all_same_induction %>% knitr::kable(
  caption = "Number with the same 1L"
)
Number with the same 1L
All induction n
Different induction 31 (31%)
Same induction for all sequence 69 (69%)

 

5 Version

RWDShelpers::gitStatus()
Git details
Git hash is: bc5dc92
Currently in the branch: master
Your branch is up-to-date with ‘origin/master’.
The remote is at: git@github.roche.com:RWDS/rwds_1052.git
LS0tCnRpdGxlOiAiTU0gTG9UIHZhbGlkYXRpb24gcmVzdWx0czogUERDTyBjb21wYXJlZCB0byBhbGdvcml0aG0gbGluZXMiCmF1dGhvcjogIiIKZGF0ZTogIkxhc3QgY29tcGlsZWQgYHIgU3lzLkRhdGUoKWAgZnJvbSBnaXRoYXNoIGByIFJXRFNoZWxwZXJzOjpnaXRTdGF0dXMoJ2hhc2gnKWAiCm91dHB1dDogIyBkZWxldGUgYm9uZXNfU2luZ2xlU2NyaXB0UERGIGlmIHlvdSBkb24ndCB3YW50IGEgcGRmIHZlcnNpb24gYXMgd2VsbAogIFJvY2hlVGVtcGxhdGVzOjpib25lc19TaW5nbGVTY3JpcHQ6IGRlZmF1bHQgCiAgIyBSb2NoZVRlbXBsYXRlczo6Ym9uZXNfU2luZ2xlU2NyaXB0UERGOiBkZWZhdWx0ICMgZGVsZXRlIGlmIG9ubHkgaHRtbCBuZWVkZWQKa25pdDogUm9jaGVUZW1wbGF0ZXM6OnJlbmRlcl8yX291dHB1dAotLS0KCiMgU2V0dXAKCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFfQojIGNoZWNrIHdoZXJlIEkgYW0KaWYgKGJhc2VuYW1lKGdldHdkKCkpID09ICJwcm9ncmFtcyIpewogIHBhdGggPSAiLi4vIgp9IGVsc2UgewogIHBhdGggPSAiIgp9CgpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7ciByb2NoZXBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KFJvY2hlUldEUykKbGlicmFyeShkcGx5cikKbGlicmFyeShEVCkKbGlicmFyeSh0YWJsZW9uZSkKbGlicmFyeShzdXJ2aXZhbCkKbGlicmFyeShzdXJ2bWluZXIpCmxpYnJhcnkoRGlhZ3JhbW1lUikKbGlicmFyeShpcnIpCmxpYnJhcnkoSUNDKQpsaWJyYXJ5KHJhdGVycykKbGlicmFyeShyZWwpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkoQmxhbmRBbHRtYW5MZWgpCmBgYAoKCiMgR2V0IGRhdGEKCmBgYHtyfQpkX2xpbmVzIDwtIHJlYWRyOjpyZWFkX2NzdigKICBwYXN0ZTAocGF0aCwiaW1wb3J0L2xpbmVzLmNzdiIpLAogICAgY29sX3R5cGVzID0gY29scygKICAgICAgUGF0aWVudElEID0gY29sX2NoYXJhY3RlcigpLAogICAgICBzdGFydF9pbmR1Y3QgPSBjb2xfZGF0ZShmb3JtYXQgPSAiIiksCiAgICAgIGVuZF9pbmR1Y3QgPSBjb2xfZGF0ZShmb3JtYXQgPSAiIiksCiAgICAgIHNlbGVjdGRydWdzX2luZHVjdCA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgICAgVHJhbnNwbGFudCA9IGNvbF9kYXRlKGZvcm1hdCA9ICIiKSwKICAgICAgc3RhcnRfY29ucyA9IGNvbF9kYXRlKGZvcm1hdCA9ICIiKSwKICAgICAgZW5kX2NvbnMgPSBjb2xfZGF0ZShmb3JtYXQgPSAiIiksCiAgICAgIHNlbGVjdGRydWdzX2NvbnMgPSBjb2xfY2hhcmFjdGVyKCksCiAgICAgIHN0YXJ0X21haW50ID0gY29sX2RhdGUoZm9ybWF0ID0gIiIpLAogICAgICBlbmRfbWFpbnQgPSBjb2xfZGF0ZShmb3JtYXQgPSAiIiksCiAgICAgIHNlbGVjdGRydWdzX21haW50ID0gY29sX2NoYXJhY3RlcigpLAogICAgICBjb2RlciA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgICAgbGluZV9ub3RlcyA9IGNvbF9jaGFyYWN0ZXIoKQogICkKKQoKIGRfbGluZXMgJT4lCiAgIHN1bW1hcmlzZSgKICAgICBSb3dzID0gbigpLAogICAgIFBhdGllbnRzID0gbl9kaXN0aW5jdChQYXRpZW50SUQpLAogICAgIEVhcmxpZXN0TGluZVN0YXJ0ID0gbWluKHN0YXJ0X2luZHVjdCkKICAgKSAlPiUKICAga25pdHI6OmthYmxlKAogICAgIGNhcHRpb24gPSAiUGF0aWVudCBjb3VudCBvbiBsaW5lcyB0YWJsZSIKICAgKQpgYGAKCiMjIENsZWFuIGRhdGEKCmBgYHtyfQojIGFkZCBpbiBsaW5lIGNvdW50CiAgZF9saW5lcyA8LSBkX2xpbmVzICU+JQogICAgZ3JvdXBfYnkoUGF0aWVudElELGNvZGVyKSAlPiUKICAgIGFycmFuZ2UoUGF0aWVudElELHN0YXJ0X2luZHVjdCkgJT4lCiAgICBtdXRhdGUoCiAgICAgIExpbmVOdW1iZXIgPSByb3dfbnVtYmVyKCksCiAgICAgIFRvdGFsTGluZXMgPSBuKCkKICAgICkgCgpkX2xpbmVzICU+JQogIGhlYWQoMykgJT4lIAogIGtuaXRyOjprYWJsZSgKICAgIGNhcHRpb24gPSAiRmlyc3QgMyByb3dzIG9mIHRoZSBsaW5lIHRhYmxlIgogICkKCmBgYAoKQm90aCBQRENPIGNvbGxlYWd1ZXMgaWRlbnRpZmllZCBhbiBpc3N1ZSB3aXRoIEZFMENDRTRCRjlEMkI6IAoKPiBJIHJhbiBpbnRvIGFuIGlzc3VlIG9uIGEgcGF0LiB3aXRoIGRvdWJsZSB0cmFuc3BsYW50OiB3aGVuIHJlY29yZGluZyB0aGUgc2Vjb25kIHRyYW5zcGxhbnQgaW5kdWN0aW9uIGNhbWUgdXAgYWdhaW4gaW4gdGhlIHJlY29yZCAoRGJsZSByZWNvcmQgZm9yIGh0ZSBpbmRjdXRpb24gcmVnaW1lbiBmb3IgUGF0LiApCgo+IFRoaXMgcGF0aWVudCBhcHBlYXJzIHRvIGhhdmUgcmVjZWl2ZWQgYSBkb3VibGUgYXV0byB0cmFuc3BsYW50LiBVbmFibGUgdG8gY29kZSBib3RoIHRyYW5zcGxhbnRzIGluIHRoZSBzYW1lIGxpbmUgb2YgdGhlcmFweSwgYnV0IGJvdGggdHJhbnNwbGFudHMgc2hvdWxkIGJlIGNvbnNpZGVyZWQgYXMgcGFydCBvZiB0aGUgc2FtZSBsaW5lIG9mIHRoZXJhcHkuCgpIb3cgdG8gZW1iZWQgdHJhbnNwbGFudHMgaW4gbGluZXMgaXMgYSBkaXNjdXNzaW9uIHBvaW50IGZvciBGbGF0aXJvbi4KCiMjIFN1bW1hcmlzZSAKCmBgYHtyfQogIGRfbGluZXMgJT4lCiAgICBncm91cF9ieShjb2RlcikgJT4lCiAgICBzdW1tYXJpc2UoCiAgICAgIGBMaW5lIHRhYmxlYCA9IG5fZGlzdGluY3QoUGF0aWVudElEKQogICAgKSAlPiUKICBrbml0cjo6a2FibGUoCiAgICBjYXB0aW9uID0gIjEwMCBpbiBib3RoIGxpbmUgdGFibGUiCiAgKQpgYGAKCiMgSW5kdWN0aW9uIEFuYWx5c2lzCgpgYGB7ciBpbmR1Y3Rpb259CiAgIyB0YWtlIGp1c3QgZmlyc3QgbGluZQogICAgZF9saW5lc19pbmR1Y3Rpb24gPC0gZF9saW5lcyAlPiUKICAgICAgZmlsdGVyKExpbmVOdW1iZXIgPT0gMSkgJT4lIHVuZ3JvdXAKICAKICAjIHB1dCBzaWRlIGJ5IHNpZGUKICAgIGRfbGluZXNfaW5kdWN0aW9uIDwtIGxlZnRfam9pbigKICAgICAgZF9saW5lc19pbmR1Y3Rpb24gJT4lCiAgICAgICAgZmlsdGVyKGNvZGVyID09ICJ3YXNzbmVyZSIpICU+JQogICAgICAgIHNlbGVjdCgKICAgICAgICAgIFBhdGllbnRJRCwKICAgICAgICAgIHN0YXJ0X2luZHVjdF93ID0gc3RhcnRfaW5kdWN0LAogICAgICAgICAgZW5kX2luZHVjdF93ID0gZW5kX2luZHVjdCwKICAgICAgICAgIHNlbGVjdGRydWdzX2luZHVjdF93ID0gc2VsZWN0ZHJ1Z3NfaW5kdWN0LAogICAgICAgICAgVG90YWxMaW5lc193PVRvdGFsTGluZXMKICAgICAgICApLAogICAgICBkX2xpbmVzX2luZHVjdGlvbiAlPiUKICAgICAgICBmaWx0ZXIoY29kZXIgPT0gImJ5b25qIikgJT4lCiAgICAgICAgc2VsZWN0KAogICAgICAgICAgUGF0aWVudElELAogICAgICAgICAgc3RhcnRfaW5kdWN0X2IgPSBzdGFydF9pbmR1Y3QsCiAgICAgICAgICBlbmRfaW5kdWN0X2IgPSBlbmRfaW5kdWN0LAogICAgICAgICAgc2VsZWN0ZHJ1Z3NfaW5kdWN0X2IgPSBzZWxlY3RkcnVnc19pbmR1Y3QsCiAgICAgICAgICBUb3RhbExpbmVzX2I9VG90YWxMaW5lcwogICAgICAgICksCiAgICAgIGJ5ID0gIlBhdGllbnRJRCIKICAgICkgICU+JQogICAgCiAgICAKICAjIGNoZWNrIGlmIGxpbmVzIGFyZSB0aGUgc2FtZQogICAgbXV0YXRlKAogICAgICBkdXJhdGlvbl9iID0gYXMubnVtZXJpYyhlbmRfaW5kdWN0X2IgLSBzdGFydF9pbmR1Y3RfYiksCiAgICAgIGR1cmF0aW9uX3cgPSBhcy5udW1lcmljKGVuZF9pbmR1Y3RfdyAtIHN0YXJ0X2luZHVjdF93KSwKICAgICAgIyBjaGVjayBpZiBzYW1lCiAgICAgIHNhbWVfaW5kdWN0aW9uID0gaWZlbHNlKHNlbGVjdGRydWdzX2luZHVjdF9iID09IHNlbGVjdGRydWdzX2luZHVjdF93LDEsMCksCiAgICAgICMgcHJldHR5IHNhbWUKICAgICAgc2FtZV9pbmR1Y3Rpb25DID0gaWZlbHNlKHNhbWVfaW5kdWN0aW9uID09IDEsIlNhbWUgMUwgaW5kdWN0aW9uIiwiRGlmZmVyZW50IDFMIGluZHVjdGlvbiIpLAogICAgICBzdGFydF9kYXRlX2RpZmYgPSBhcy5udW1lcmljKHN0YXJ0X2luZHVjdF9iLXN0YXJ0X2luZHVjdF93KSwKICAgICAgZHVyYXRpb25fZGlmZiA9IGR1cmF0aW9uX2IgLSBkdXJhdGlvbl93LAogICAgICAjY2hlY2sgaWYgc2FtZSBudW1iZXIgb2YgdG90YWwgbGluZXMKICAgICAgc2FtZV9udW1saW5lcyA9IGlmZWxzZShUb3RhbExpbmVzX2IgPT0gVG90YWxMaW5lc193LCAxLCAwKSwKICAgICAgc2FtZV9udW1MaW5lc0MgPSBpZmVsc2Uoc2FtZV9udW1saW5lcyA9PSAxLCAiU2FtZSBudW1iZXIgb2YgbGluZXMiLCAiRGlmZmVyZW50IG51bWJlciBvZiBsaW5lcyIpCiAgICApCiAgICAgIApgYGAKCgojIyBJbmR1Y3Rpb24gRmlyc3QgTGluZSBOYW1lCgpDaGVjayBpZiB0aGUgaW5kdWN0aW9uIDFMIGhhcyB0aGUgc2FtZSBuYW1lLgoKYGBge3J9CnRfc2FtZWluZHVjdGlvbiA8LSAgZF9saW5lc19pbmR1Y3Rpb24gJT4lCiAgcmVuYW1lKGAxTCBpbmR1Y3Rpb25gID0gc2FtZV9pbmR1Y3Rpb25DKSAlPiUKICBncm91cF9ieSgKICAgIGAxTCBpbmR1Y3Rpb25gCiAgKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpCiAgKSAlPiUKICBtdXRhdGUoCiAgICBuID0gcGFzdGUwKG4sIiAoIixyb3VuZCgxMDAqbi9zdW0obikpLCIlKSIpCiAgKQpgYGAKCiMjIyBOdW1iZXIgd2l0aCB0aGUgc2FtZSAxTCBuYW1lCgpgYGB7cn0KCnRfc2FtZWluZHVjdGlvbiAlPiUga25pdHI6OmthYmxlKAogIGNhcHRpb24gPSAiTnVtYmVyIHdpdGggdGhlIHNhbWUgMUwiCikKCmBgYAoKIyMjIEludGVyLXJhdGVyIHJlbGlhYmlsaXR5IHVzaW5nIEZsZWlzcycgS2FwcGEKCk1ldGhvZCAxID0gYGthcHBhbS5mbGVpc3NgIGZ1bmN0aW9uCk1ldGhvZCAyID0gY2thcCBmdW5jdGlvbgoKYGBge3J9CiMgQ2hlY2sgZm9yIGludGVyLXJhdGVyIHJlbGlhYmlsaXR5IG9mIGluZHVjdGlvbiBsaW5lIG5hbWUKaW5kdWN0aW9uXzFMIDwtIGRfbGluZXNfaW5kdWN0aW9uWywgYygic2VsZWN0ZHJ1Z3NfaW5kdWN0X3ciLCAic2VsZWN0ZHJ1Z3NfaW5kdWN0X2IiKV0KCmBgYAoKCmBgYHtyfQoKcmVzdWx0IDwtIGthcHBhbS5mbGVpc3MoCiAgaW5kdWN0aW9uXzFMCiAgKQoKcmVzdWx0CgpgYGAKCldpdGhvdXQgbmVlZGluZyB0byBsb29rIGF0IHRoZSBwLXZhbHVlLCB0aGUgS2FwcGEgc3RhdGlzdGljIGlzIGByIHJlc3VsdCR2YWx1ZWAgdXNpbmcgdGhlIGBrYXBwYW0uZmxlaXNzYCBmdW5jdGlvbiwgc3VnZ2VzdGluZyBhIGhpZ2ggbGV2ZWwgb2YgYWdyZWVtZW50IGJldHdlZW4gdGhlIHR3byByYXRlcnMuCgpgYGB7cn0KcmVzdWx0IDwtIGNrYXAoCiAgZGF0YSA9IGluZHVjdGlvbl8xTCwgd2VpZ2h0ID0gYygidW53ZWlnaHRlZCIpLAogIHN0ZC5lcnIgPSBjKCJGbGVpc3MiKSwgY29uZi5sZXZlbCA9IDAuOTUsIFIgPSAwCiAgKQoKcmVzdWx0CmBgYAoKVGhpcyBpcyByZXBlYXRlZCB1c2luZyBgY2thcGAsIHdoZXJlIHRoZSBLYXBwYSBzdGF0aXN0aWMgaXMgYHIgcmVzdWx0JGVzdGAgd2l0aCBhIGByIHJvdW5kKHJlc3VsdCRjb25mLmxldmVsLDMpYCUgIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgYHIgcGFzdGUocm91bmQocmVzdWx0JGxiLDMpLCJ0byIscm91bmQocmVzdWx0JHViLDMpKWAuCgojIyBJbmR1Y3Rpb24gTnVtYmVyIG9mIExpbmVzCgpgYGB7cn0KdF9zYW1lX251bWxpbmVzIDwtIGRfbGluZXNfaW5kdWN0aW9uICU+JQogIGdyb3VwX2J5KAogICAgc2FtZV9udW1MaW5lc0MKICApICU+JQogIHN1bW1hcmlzZSgKICAgIG4gPSBuKCkKICApICU+JQogIG11dGF0ZSgKICAgIG4gPSBwYXN0ZTAobiwiICgiLHJvdW5kKDEwMCpuL3N1bShuKSksIiUpIikKICApCmBgYAoKIyMjIE51bWJlciB3aXRoIHRoZSBzYW1lIG51bWJlciBvZiB0b3RhbCBsaW5lcwoKYGBge3J9CnRfc2FtZV9udW1saW5lcyAlPiUga25pdHI6OmthYmxlKAogIGNhcHRpb24gPSAiTnVtYmVyIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIHRvdGFsIGxpbmVzIgopCmBgYAoKIyMjIEludGVyLXJhdGVyIHJlbGlhYmlsaXR5CgoKCmBgYHtyfQp0b3RhbF9saW5lcyA8LWRfbGluZXNfaW5kdWN0aW9uWywgYygiVG90YWxMaW5lc193IiwgIlRvdGFsTGluZXNfYiIpXQoKYGBgCgpgYGB7cn0KCnJlc3VsdCA8LSBrYXBwYW0uZmxlaXNzKAogIHRvdGFsX2xpbmVzCiAgKQoKcmVzdWx0CgpgYGAKCldpdGhvdXQgbmVlZGluZyB0byBsb29rIGF0IHRoZSBwLXZhbHVlLCB0aGUgS2FwcGEgc3RhdGlzdGljIGlzIGByIHJlc3VsdCR2YWx1ZWAgdXNpbmcgdGhlIGBrYXBwYW0uZmxlaXNzYCBmdW5jdGlvbiwgc3VnZ2VzdGluZyBhIGhpZ2ggbGV2ZWwgb2YgYWdyZWVtZW50IGJldHdlZW4gdGhlIHR3byByYXRlcnMuCgpgYGB7cn0KcmVzdWx0IDwtIGNrYXAoCiAgZGF0YSA9IHRvdGFsX2xpbmVzLCB3ZWlnaHQgPSBjKCJ1bndlaWdodGVkIiksCiAgc3RkLmVyciA9IGMoIkZsZWlzcyIpLCBjb25mLmxldmVsID0gMC45NSwgUiA9IDAKICApCgpyZXN1bHQKYGBgCgpUaGlzIGlzIHJlcGVhdGVkIHVzaW5nIGBja2FwYCwgd2hlcmUgdGhlIEthcHBhIHN0YXRpc3RpYyBpcyBgciByZXN1bHQkZXN0YCB3aXRoIGEgYHIgcm91bmQocmVzdWx0JGNvbmYubGV2ZWwsMylgJSAgY29uZmlkZW5jZSBpbnRlcnZhbCBvZiBgciBwYXN0ZShyb3VuZChyZXN1bHQkbGIsMyksInRvIixyb3VuZChyZXN1bHQkdWIsMykpYC4KCiMjIFRpbWluZyBvZiBsaW5lcwoKQW1vbmcgdGhvc2Ugd2l0aCBzYW1lIDFMIGluZHVjdGlvbiBsaW5lIG5hbWU6IAoKYGBge3J9CiMgRmlsdGVyIHRvIHRob3NlIHdpdGggc2FtZSAxTCBpbmR1Y3Rpb24gbGluZSBuYW1lCmluZHVjdGlvbl9kYXRhIDwtIGRfbGluZXNfaW5kdWN0aW9uICU+JQogIGZpbHRlcihzYW1lX2luZHVjdGlvbj09MSkgCmBgYAoKIyMjIEZpcnN0IGxpbmUgc3RhcnQKCkRpZmZlcmVuY2UgaW4gZGF5cyBiZXR3ZWVuIHRoZSB0d28gcmF0ZXJzIGluIHRoZSBgbnJvdyhpbmR1Y3Rpb25fZGF0YSlgIHBhdGllbnRzIHdpdGggdGhlIHNhbWUgMUwuCgpgYGB7cn0KamJfZ2V0c3RhdHMgPC0gZnVuY3Rpb24oeCwgcm91bmQgPSAyKXsKICB0ZW1wIDwtIHQudGVzdCh4LCBjb25mLmxldmVsPTAuOTUpCiAgbWVhbiA8LSBwYXN0ZTAoCiAgICByb3VuZCh0ZW1wJGVzdGltYXRlLHJvdW5kKSwKICAgICIgKDk1JUNJICIscGFzdGUocm91bmQodGVtcCRjb25mLmludCxyb3VuZCksIGNvbGxhcHNlID0gIiwgIiksIikiCiAgKQogIAogIG1lZGlhbiA9IHBhc3RlMCgKICAgIHJvdW5kKG1lZGlhbih4KSxyb3VuZCksIiAoSVFSICIsCiAgICByb3VuZChxdWFudGlsZSh4LCBwcm9icyA9IDAuMjUpLHJvdW5kKSwiLCAiLAogICAgcm91bmQocXVhbnRpbGUoeCwgcHJvYnMgPSAwLjc1KSxyb3VuZCksIikiCiAgKQogIAogIHJhbmdlID0gcmFuZ2UoeCkKICAKICByZXR1cm4oCiAgICBsaXN0KAogICAgICBtZWFuID0gbWVhbiwKICAgICAgbWVkaWFuID0gbWVkaWFuLAogICAgICByYW5nZSA9IHJhbmdlCiAgICApCiAgKQp9CgpyZXN1bHQgPC0gamJfZ2V0c3RhdHMoaW5kdWN0aW9uX2RhdGEkc3RhcnRfZGF0ZV9kaWZmKQoKYGBgCgoqIE1lYW46IGByIHJlc3VsdCRtZWFuYAoqIE1lZGlhbjogYHIgcmVzdWx0JG1lZGlhbmAKKiBSYW5nZTogYHIgcmVzdWx0JHJhbmdlYAoKSGlzdG9ncmFtIG9mIGRpZmZlcmVuY2UgaW4gZGF5cyBiZXR3ZWVuIDFMIHN0YXJ0LgoKYGBge3J9CmdncGxvdCgKICBpbmR1Y3Rpb25fZGF0YSwKICBtYXBwaW5nPWFlcyhzdGFydF9kYXRlX2RpZmYpKSArCiAgbGFicygKICAgIHRpdGxlPSAiRGlmZmVyZW5jZSBpbiBzdGFydCBkYXkgb2YgMUwgYmV0d2VlbiBQRENPIGNvZGVycyIsCiAgICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICAgIkluIHRoZSAiLAogICAgICBucm93KGluZHVjdGlvbl9kYXRhKSwKICAgICAgIiB3aXRoIHRoZSBzYW1lIDFMIiksCiAgICB5ID0gIkNvdW50IiwKICAgIHggPSAiRGlmZmVyZW5jZSBpbiAxTCBzdGFydCAoRGF5cykiCiAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYApPVVRMSUVSICMgRjg2RDc1MkI3NTZCNjogQ29kZXIgVyBzZWVtcyB0byBoYXZlIGNvcGllZCB0aGUgd3JvbmcgbGluZSBzdGFydCBhbmQgZW5kIGZyb20gdGhlIGFic3RyYWN0ZWQgZGF0YS4KCiZuYnNwOwoKIyMjIDFMIEluZHVjdGlvbiBTdGFydCBEYXRlLCBJQ0MgKFRCRCkKCipPbmx5IGRvbmUgZm9yIGR1cmF0aW9uIGF0IHRoZSBtb21lbnQuKgoKYGBge3J9CiNDaGVjayBmb3IgSUNDCmBgYAoKIyMgMUwgSW5kdWN0aW9uIGR1cmF0aW9uCgpBbW9uZyB0aG9zZSB3aXRoIHNhbWUgMUwgaW5kdWN0aW9uIGxpbmUgbmFtZTogCgpgYGB7cn0KZHVyYXRpb25fMUwgPC1pbmR1Y3Rpb25fZGF0YVssIGMoImR1cmF0aW9uX2IiLCAiZHVyYXRpb25fdyIpXSAKYGBgCgoKIyMjIDFMIEluZHVjdGlvbiBkdXJhdGlvbiwgSUNDCgpgYGB7cn0KaW5kdWN0aW9uX2RhdGEgJT4lCiAgIyByZWZvcm1hdCBkYXRhIGZvciBmdW5jdGlvbgogIHNlbGVjdCgKICAgIGR1cmF0aW9uX2IsIGR1cmF0aW9uX3cKICApICU+JSAKICAjIHJ1biBmdW5jdGlvbgogIGlycjo6aWNjKCkKYGBgCgoKIyMjIDFMIEluZHVjdGlvbiBkdXJhdGlvbiwgQmxhbnQtQWx0bWFuIFBsb3QKCmBgYHtyfQojIHRlbXAgbm90IHVzZWQgY3VycmVudGx5IC0gbW92ZSB0byBnZ3Bsb3QyIGxhdGVyPwojdGVtcCA8LSBibGFuZC5hbHRtYW4uc3RhdHMoaW5kdWN0aW9uX2RhdGEkZHVyYXRpb25fYiwgaW5kdWN0aW9uX2RhdGEkZHVyYXRpb25fdykKCmJsYW5kLmFsdG1hbi5wbG90KAogIGluZHVjdGlvbl9kYXRhJGR1cmF0aW9uX2IsIAogIGluZHVjdGlvbl9kYXRhJGR1cmF0aW9uX3csIAogIG1haW49IkJsYW5kIEFsdG1hbiBQbG90IGZvciBEaWZmZXJlbmNlIGluIDFMIER1cmF0aW9uIiwgIAogIHhsYWI9Ik1lYW4gRHVyYXRpb24iLCAKICB5bGFiPSJEaWZmZXJlbmNlcyBpbiBEYXlzIiwKICBjb25mLmludD0uOTUKICApCmBgYAoKIyMjIDFMIEluZHVjdGlvbiBkdXJhdGlvbiwgSGlzdG9ncmFtCmBgYHtyfQpyZXN1bHQgPC0gamJfZ2V0c3RhdHMoaW5kdWN0aW9uX2RhdGEkZHVyYXRpb25fZGlmZikKYGBgCiogTWVhbjogYHIgcmVzdWx0JG1lYW5gCiogTWVkaWFuOiBgciByZXN1bHQkbWVkaWFuYAoqIFJhbmdlOiBgciByZXN1bHQkcmFuZ2VgCgpgYGB7cn0KZ2dwbG90KAogIGRfbGluZXNfaW5kdWN0aW9uICU+JSBmaWx0ZXIoc2FtZV9pbmR1Y3Rpb24gPT0gMSksCiAgbWFwcGluZz1hZXMoZHVyYXRpb25fZGlmZikpICsKICBsYWJzKAogICAgdGl0bGU9ICJEaWZmZXJlbmNlIGluIGR1cmF0aW9uIG9mIDFMIGJldHdlZW4gUERDTyBjb2RlcnMiLAogICAgc3VidGl0bGUgPSBwYXN0ZTAoCiAgICAgICJJbiB0aGUgIiwKICAgICAgbnJvdyhpbmR1Y3Rpb25fZGF0YSksCiAgICAgICIgd2l0aCB0aGUgc2FtZSAxTCIpLAogICAgeSA9ICJDb3VudCIsCiAgICB4ID0gIkRpZmZlcmVuY2UgaW4gZHVyYXRpb24gKERheXMpIgogICAgKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCkgKwogIHRoZW1lX2NsYXNzaWMoKQoKCmBgYApPVVRMSUVSICMgRjE1MTQ2QzQ2QzMyNzogIENvZGVycyBhZ3JlZSBvbiBzdGFydGRhdGUsIGJ1dCBjb2RlciBXIHRoaW5rcyBlbmRzID4xIHllYXIgbGF0ZXIgdGhhbiBjb2RlciBCCgpPVVRMSUVSICMgRjVGMDI0Mzk4NjJDNDogIENvZGVycyBhZ3JlZSBvbiBzdGFydGRhdGUsIGJ1dCBjb2RlciBCIHRoaW5rcyBlbmRzID4xIHllYXIgbGF0ZXIgdGhhbiBjb2RlciBXCgpPVVRMSUVSICMgRjk0MUVBMUU5M0MzRjogIENvZGVyIEIgYXNzaWduZWQgKC0pIGZvbGxvd3VwIHRpbWUuCgombmJzcDsKCiMgQWxsIHRyZWF0bWVudCBzZXF1ZW5jZQpBbW9uZyB0aGUgYWxsIHBhdGllbnRzLCBjaGVjayBob3cgbWFueSBoYXZlIHRoZSBzYW1lIGxpbmUgbmFtZXMgYWNyb3NzIGFsbCBsaW5lcy4gQ29kZXJzIGhhdmUgY29kZWQgdXAgdG8gNiBsaW5lcy4KCmBgYHtyfQoKZm9yKGkgaW4gMTo2KSB7CiAgZm9yKGogaW4gYygiYnlvbmoiLCAid2Fzc25lcmUiKSl7CiAgYXNzaWduKHBhc3RlKCJkX2xpbmVzX2luZHVjdGlvbl8iLCBpLCAiXyIsIGosIHNlcD0iIiksIAogICAgICAgICBzdWJzZXQoZF9saW5lcyAlPiUgdW5ncm91cCwgTGluZU51bWJlcj09IGkgJiBjb2RlciA9PWopCiAgICAgICAgICkgCiAgICB9Cn0KCmRfbGluZXNfaW5kdWN0aW9uX25ldyA8LQpsZWZ0X2pvaW4oZF9saW5lc19pbmR1Y3Rpb25fMV9ieW9uaiAlPiUgCiAgICAgICAgICAgICAgc2VsZWN0KFBhdGllbnRJRCwgaW5kdWN0XzFfYnlvbmogPSBzZWxlY3RkcnVnc19pbmR1Y3QpLCAKICAgICAgICAgIGRfbGluZXNfaW5kdWN0aW9uXzFfd2Fzc25lcmUgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KFBhdGllbnRJRCwgaW5kdWN0XzFfd2Fzc25lcmUgPSBzZWxlY3RkcnVnc19pbmR1Y3QpLCAKICAgICAgICAgIGJ5PSdQYXRpZW50SUQnKSAgJT4lCmxlZnRfam9pbiguLCAKICAgICAgICAgIGRfbGluZXNfaW5kdWN0aW9uXzJfYnlvbmogJT4lCiAgICAgICAgICAgICAgc2VsZWN0KFBhdGllbnRJRCwgaW5kdWN0XzJfYnlvbmogPSBzZWxlY3RkcnVnc19pbmR1Y3QpLAogICAgICAgICAgYnk9J1BhdGllbnRJRCcpICU+JQpsZWZ0X2pvaW4oLiwgCiAgICAgICAgICBkX2xpbmVzX2luZHVjdGlvbl8yX3dhc3NuZXJlICU+JQogICAgICAgICAgICAgIHNlbGVjdChQYXRpZW50SUQsIGluZHVjdF8yX3dhc3NuZXJlID0gc2VsZWN0ZHJ1Z3NfaW5kdWN0KSwKICAgICAgICAgIGJ5PSdQYXRpZW50SUQnKSAlPiUKbGVmdF9qb2luKC4sIAogICAgICAgICAgZF9saW5lc19pbmR1Y3Rpb25fM19ieW9uaiAlPiUKICAgICAgICAgICAgICBzZWxlY3QoUGF0aWVudElELCBpbmR1Y3RfM19ieW9uaiA9IHNlbGVjdGRydWdzX2luZHVjdCksCiAgICAgICAgICBieT0nUGF0aWVudElEJykgJT4lCmxlZnRfam9pbiguLCAKICAgICAgICAgIGRfbGluZXNfaW5kdWN0aW9uXzNfd2Fzc25lcmUgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KFBhdGllbnRJRCwgaW5kdWN0XzNfd2Fzc25lcmUgPSBzZWxlY3RkcnVnc19pbmR1Y3QpLAogICAgICAgICAgYnk9J1BhdGllbnRJRCcpICU+JQpsZWZ0X2pvaW4oLiwgCiAgICAgICAgICBkX2xpbmVzX2luZHVjdGlvbl80X2J5b25qICU+JQogICAgICAgICAgICAgIHNlbGVjdChQYXRpZW50SUQsIGluZHVjdF80X2J5b25qID0gc2VsZWN0ZHJ1Z3NfaW5kdWN0KSwKICAgICAgICAgIGJ5PSdQYXRpZW50SUQnKSAlPiUKbGVmdF9qb2luKC4sIAogICAgICAgICAgZF9saW5lc19pbmR1Y3Rpb25fNF93YXNzbmVyZSAlPiUKICAgICAgICAgICAgICBzZWxlY3QoUGF0aWVudElELCBpbmR1Y3RfNF93YXNzbmVyZSA9IHNlbGVjdGRydWdzX2luZHVjdCksCiAgICAgICAgICBieT0nUGF0aWVudElEJykgJT4lCmxlZnRfam9pbiguLCAKICAgICAgICAgIGRfbGluZXNfaW5kdWN0aW9uXzVfYnlvbmogJT4lCiAgICAgICAgICAgICAgc2VsZWN0KFBhdGllbnRJRCwgaW5kdWN0XzVfYnlvbmogPSBzZWxlY3RkcnVnc19pbmR1Y3QpLAogICAgICAgICAgYnk9J1BhdGllbnRJRCcpICU+JQpsZWZ0X2pvaW4oLiwgCiAgICAgICAgICBkX2xpbmVzX2luZHVjdGlvbl81X3dhc3NuZXJlICU+JQogICAgICAgICAgICAgIHNlbGVjdChQYXRpZW50SUQsIGluZHVjdF81X3dhc3NuZXJlID0gc2VsZWN0ZHJ1Z3NfaW5kdWN0KSwKICAgICAgICAgIGJ5PSdQYXRpZW50SUQnKSAlPiUKbGVmdF9qb2luKC4sIAogICAgICAgICAgZF9saW5lc19pbmR1Y3Rpb25fNl9ieW9uaiAlPiUKICAgICAgICAgICAgICBzZWxlY3QoUGF0aWVudElELCBpbmR1Y3RfNl9ieW9uaiA9IHNlbGVjdGRydWdzX2luZHVjdCksCiAgICAgICAgICBieT0nUGF0aWVudElEJykgJT4lCmxlZnRfam9pbiguLCAKICAgICAgICAgIGRfbGluZXNfaW5kdWN0aW9uXzZfd2Fzc25lcmUgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KFBhdGllbnRJRCwgaW5kdWN0XzZfd2Fzc25lcmUgPSBzZWxlY3RkcnVnc19pbmR1Y3QpLAogICAgICAgICAgYnk9J1BhdGllbnRJRCcpICU+JQogIAogIG11dGF0ZSgKICAgIHNhbWVfaW5kdWN0aW9uXzFMPWlmZWxzZShpbmR1Y3RfMV9ieW9uaj09aW5kdWN0XzFfd2Fzc25lcmUsIDEsIDApLAogICAgc2FtZV9pbmR1Y3Rpb25fMkw9aWZlbHNlKGluZHVjdF8yX2J5b25qPT1pbmR1Y3RfMl93YXNzbmVyZXwoaXMubmEoaW5kdWN0XzJfYnlvbmopICZpcy5uYShpbmR1Y3RfMl93YXNzbmVyZSkpLCAxLCAwKSwKICAgIHNhbWVfaW5kdWN0aW9uXzNMPWlmZWxzZShpbmR1Y3RfM19ieW9uaj09aW5kdWN0XzNfd2Fzc25lcmV8KGlzLm5hKGluZHVjdF8zX2J5b25qKSAmaXMubmEoaW5kdWN0XzNfd2Fzc25lcmUpKSwgMSwgMCksCiAgICBzYW1lX2luZHVjdGlvbl80TD1pZmVsc2UoaW5kdWN0XzRfYnlvbmo9PWluZHVjdF80X3dhc3NuZXJlfChpcy5uYShpbmR1Y3RfNF9ieW9uaikgJmlzLm5hKGluZHVjdF80X3dhc3NuZXJlKSksIDEsIDApLAogICAgc2FtZV9pbmR1Y3Rpb25fNUw9aWZlbHNlKGluZHVjdF81X2J5b25qPT1pbmR1Y3RfNV93YXNzbmVyZXwoaXMubmEoaW5kdWN0XzVfYnlvbmopICZpcy5uYShpbmR1Y3RfNV93YXNzbmVyZSkpLCAxLCAwKSwKICAgIHNhbWVfaW5kdWN0aW9uXzZMPWlmZWxzZShpbmR1Y3RfNl9ieW9uaj09aW5kdWN0XzZfd2Fzc25lcmV8KGlzLm5hKGluZHVjdF82X2J5b25qKSAmaXMubmEoaW5kdWN0XzZfd2Fzc25lcmUpKSwgMSwgMCksCiAgCiAgICBhbGxfc2FtZV9pbmR1Y3Rpb249aWZlbHNlKChzYW1lX2luZHVjdGlvbl8xTD09MHxpcy5uYShzYW1lX2luZHVjdGlvbl8xTCkpCiAgICAgICB8KHNhbWVfaW5kdWN0aW9uXzJMPT0wfGlzLm5hKHNhbWVfaW5kdWN0aW9uXzJMKSkKICAgICAgIHwoc2FtZV9pbmR1Y3Rpb25fM0w9PTB8aXMubmEoc2FtZV9pbmR1Y3Rpb25fM0wpKQogICAgICAgfChzYW1lX2luZHVjdGlvbl80TD09MHxpcy5uYShzYW1lX2luZHVjdGlvbl80TCkpCiAgICAgICB8KHNhbWVfaW5kdWN0aW9uXzVMPT0wfGlzLm5hKHNhbWVfaW5kdWN0aW9uXzVMKSkKICAgICAgIHwoc2FtZV9pbmR1Y3Rpb25fNkw9PTB8aXMubmEoc2FtZV9pbmR1Y3Rpb25fNkwpKSwgIkRpZmZlcmVudCBpbmR1Y3Rpb24iLCAiU2FtZSBpbmR1Y3Rpb24gZm9yIGFsbCBzZXF1ZW5jZSIpCikKCmBgYAoKYGBge3J9CmFsbF9zYW1lX2luZHVjdGlvbiA8LSAgZF9saW5lc19pbmR1Y3Rpb25fbmV3ICU+JQogIHJlbmFtZShgQWxsIGluZHVjdGlvbmAgPSBhbGxfc2FtZV9pbmR1Y3Rpb24pICU+JQogIGdyb3VwX2J5KAogICAgYEFsbCBpbmR1Y3Rpb25gCiAgKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpCiAgKSAlPiUKICBtdXRhdGUoCiAgICBuID0gcGFzdGUwKG4sIiAoIixyb3VuZCgxMDAqbi9zdW0obikpLCIlKSIpCiAgKQpgYGAKCiMjIE51bWJlciB3aXRoIHRoZSBzYW1lIGluZHVjdGlvbiBsaW5lIGFjcm9zcyBhbGwgbGluZSBudW1iZXJzCgpgYGB7cn0KYWxsX3NhbWVfaW5kdWN0aW9uICU+JSBrbml0cjo6a2FibGUoCiAgY2FwdGlvbiA9ICJOdW1iZXIgd2l0aCB0aGUgc2FtZSAxTCIKKQoKYGBgCgombmJzcDsKCiMgVmVyc2lvbgoKYGBge3J9ClJXRFNoZWxwZXJzOjpnaXRTdGF0dXMoKQpgYGAKCg==