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)
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
| 357 |
100 |
2011-02-01 |
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
| 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.
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
| byonj |
100 |
| wassnere |
100 |
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")
)
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)),"%)")
)
Number with the same 1L name
t_sameinduction %>% knitr::kable(
caption = "Number with the same 1L"
)
Number with the same 1L
| Different 1L induction |
14 (14%) |
| Same 1L induction |
86 (86%) |
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.
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)),"%)")
)
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
| Different number of lines |
23 (23%) |
| Same number of lines |
77 (77%) |
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.
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)
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.
1L Induction Start Date, ICC (TBD)
Only done for duration at the moment.
#Check for ICC
1L Induction duration
Among those with same 1L induction line name:
duration_1L <-induction_data[, c("duration_b", "duration_w")]
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
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
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.
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)),"%)")
)
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
| Different induction |
31 (31%) |
| Same induction for all sequence |
69 (69%) |
Version
RWDShelpers::gitStatus()
| 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==