This is an R Markdown
Notebook outlining steps for LIMS data processing and GBS carriage
estimation from ECM-based testing data from the GBS3 trial
Set main working directory
Install and load packages
Load functions
setwd(wd)
source("R scripts_carriage/functions_GBS_carriage.R")
Import LIMS data from ECM-based GBS testing (datasets provided by
micro labs) Pre-check - open csv and check that in correct template
format - describe the raw data
# setwd(wd)
#////////////////////
# for Royal Stoke
testpos <- import(here("Data_carriage","ECM_carriage","Edited","ECM_pos_06_02_2023to01_04_2024_Royal_stoke.xlsx"),
format = "xlsx",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
testneg <- import(here("Data_carriage","ECM_carriage","Edited","ECM_neg_06_02_2023to01_04_2024_Royal_stoke.xlsx"),
format = "xlsx",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
testpos2 <- import(here("Data_carriage","ECM_carriage","Edited","ECM_pos_30_10_2022to05_02_2023_Royal_stoke.xls"),
format = "xls",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
testneg2 <- import(here("Data_carriage","ECM_carriage","Edited","ECM_neg_30_10_2022to05_02_2023_Royal_stoke.xls"),
format = "xls",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
colnames(testpos) %in% colnames(testneg) # last 3 cols distinct in testneg
testpos <- lims.stnd.format.fun(testpos)
testpos2 <- lims.stnd.format.fun(testpos2)
testneg <- lims.stnd.format.fun(testneg)
testneg2 <- lims.stnd.format.fun(testneg2)
alltest <- rbind(testpos,testpos2,testneg,testneg2)
alltest1 <- alltest %>% mutate(site="Royal Stoke")
rm(testpos,testpos2,testneg,testneg2)
#////////////////////
# for Bedford
alltest <- import(here("Data_carriage","ECM_carriage","Edited","Bedford_GBS3_CleanedData.csv"),
format = "csv",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
alltest$`Patient NHS Number` <- as.numeric(alltest$`Patient NHS Number`)
alltest2 <- lims.stnd.format.fun(alltest)
alltest2 <- alltest2 %>% mutate(site="Bedford")
#////////////////////
# for Derriford
alltest <- import(here("Data_carriage","ECM_carriage","Edited","Derriford_GBS3_CleanedData.csv"),
format = "csv",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
alltest$`Patient NHS Number` <- as.numeric(alltest$`Patient NHS Number`)
alltest3 <- lims.stnd.format.fun(alltest)
alltest3 <- alltest3 %>% mutate(site="Derriford")
#////////////////////
# for Freeman
alltest <- import(here("Data_carriage","ECM_carriage","Edited","Freeman_GBS3_CleanedData.csv"),
format = "csv",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
alltest <- alltest %>%
mutate(
`Patient NHS Number` = as.numeric(`Patient NHS Number`),
`Specimen Date` = as.Date(`Specimen Date`, format = "%d/%m/%Y"),
`Patient Date of Birth` = as.Date(`Patient Date of Birth`, format = "%d/%m/%Y")
)
# CONTINUE HERE - specimen date format different for Freeman vs other sites. Resolve here before applying df$dt <- lubridate::ymd(df$specdate) step in obai process
# handle format for strings in Ethnicity column which have symbols
alltest$Ethnicity <- iconv(alltest$Ethnicity, from = "ISO-8859-1", to = "UTF-8")
alltest4 <- lims.stnd.format.fun(alltest)
alltest4 <- alltest4 %>% mutate(site="Freeman")
#////////////////////
# for Epsom and St Helier (St George's)
alltest5 <- read.csv("\\\\IISNISCOL01\\GBSStudy\\Processed\\Epsom_StHelier_GBS3_UID.csv")
alltest5 <- alltest5 %>% mutate(site="Epsom") %>% relocate(site, .after = last_col()) %>% mutate(species=case_when(
is.na(species) ~ "GBS not isolated",
(species!="Beta Haemolytic Streptococcus Group B" & species!="Group B Streptococcus") ~ "GBS not isolated",
(species=="Beta Haemolytic Streptococcus Group B" | species=="Group B Streptococcus") ~ "Group B Streptococcus",
TRUE ~ species
)) %>% rename(ethnicity=Ethnicity_group) %>% relocate(ethnicity, .after = sex) %>%
mutate(
dob = as.Date(dob, format = "%d/%m/%Y"), # Character string "DD/MM/YYYY" to Date
specdate = as.Date(specdate, format = "%d/%m/%Y")
) %>% select(-c("pt_id"))
#////////////////////
# for Royal Blackburn
alltest6 <- import(here("Data_carriage","ECM_carriage","Edited","UKHSA GB3 trial_Royal_Blackburn.csv"),
format = "csv",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
alltest6 <- lims.stnd.format.fun(alltest6)
alltest6 <- alltest6 %>% mutate(site="Royal Blackburn",
dob=as.Date(dob, format = "%d/%m/%Y")) %>%
mutate(species=case_when(
is.na(species) ~ "GBS not isolated",
(species!="beta Haemolytic Strep, group B" & species!="Group B Streptococcus") ~ "GBS not isolated",
(species=="beta Haemolytic Strep, group B" | species=="Group B Streptococcus") ~ "Group B Streptococcus",
TRUE ~ species
))# need to apply additional date conversion as not handled in flexible reformatting function for dates in char string "19/06/1991" in Royal Blackburn LIMS data
#//////////////////
# combine into single df
allsites <- rbind(alltest1,alltest2,alltest3,alltest4,alltest5,alltest6)
allsites$nhsno <- as.numeric(allsites$nhsno)
rm(alltest,alltest1,alltest2,alltest3,alltest4,alltest5,alltest6)
# apply ethnicity categorization function to group into 5 main ethnicities
allsites <- lims.ethcat.fun(allsites)
# convert instances of sex=="FALSE" into "U"
allsites <- allsites %>% mutate(sex = case_when(
sex=="FALSE" ~ "U",
TRUE ~ sex
))
# NOTE: CHECK at this stage that dob and specdate fields have consistent date formatting across sites
Process LIMS linelist using adapted version of obai
sgss_infections
# Remove quality control samples
allsites <- qc.umbrella.fun(allsites)
# Proceed to dedpup LIMS linelist to patient-level with AMR results
allsites <- obai.dedup.fun(allsites)
## perform initial checks
# total episodes (ECM test result within 9 month window)
length(unique(allsites$epids)) # 12,916
# total patients
length(unique(allsites$pid_str))# 12,916
# total unique NHS number
length(unique(allsites$nhsno)) # 12,445
# total records where NHS number missing
sum(is.na(allsites$nhsno)) # 472
# isolate duplicates in nhsno field
duplicates_only <- allsites %>%
filter(nhsno %in% nhsno[duplicated(nhsno)]) %>% arrange(forename)
nrow(duplicates_only) # 472 - all of which nhsno missing
# identify true (likely) duplicates using patient name fields
true_dups <- duplicates_only %>% select(specno:site) %>% mutate(name_concat=paste(forename,"_",surname),
name_concat=gsub(" ","",name_concat)) %>%
filter(name_concat %in% name_concat[duplicated(name_concat)]) %>% arrange(name_concat)
length(unique(true_dups$name_concat))
# 7 true duplicates (across name fields), of which 1 ("FEBUARY_IQAMIC2024") a QC sample and 1 pertains to 2 samples a year apart for a patient
# remove remaining QC sample rows
allsites <- allsites %>% filter(surname!="IQAMIC2024")
# //////////// key checkpoint 1 //////////////
## save as static base dataset
write.csv(allsites, "Data_carriage/Linelist/static_basedata.csv", row.names=F) # REPLACE WITH SAVE TO SERVER
Prepare processed LIMS data for DBS tracing
## load static base linelist
allsites <- read.csv("Data_carriage/Linelist/static_basedata.csv")
allsites$specdate <- as.Date(allsites$specdate, format = "%Y-%m-%d")
allsites$dob <- as.Date(allsites$dob, format = "%Y-%m-%d")
allsites$pid_str <- as.character(allsites$pid_str)
dbsprep <- allsites %>% mutate(year = format(specdate, "%Y")) %>% select(year, everything()) %>% select(1:match("epids",names(allsites)))
# generate .csv files for sending to DBS trace team (generates two files, one with sufficient PII and one where some/all PII missing. Send BOTH for tracing)
dbs.trace.prep.fun(dbsprep,
mainfile="Data_carriage/DBS_tracing/LIMS_linelist_for_tracing.csv",
excludefile="Data_carriage/DBS_tracing/excluded_records_for_tracing.csv")
# NOTE: always open .cvs files for tracing and check that columns formatted correctly, prior to sending to DBS team
# # state NHS number completion before DBS tracing: percent nhsno missing
sum_na_nhs <- sum(is.na(dbsprep$nhsno))/nrow(dbsprep)*100 # 3.6%
na_nhs <- dbsprep %>% filter(is.na(nhsno)) # generate df of records where NHS number missing
Integrate PII-enriched DBS trace return into linelist and retain
records which didn’t have successful trace first time around for second
attempt
## import and format trace return csv files
# These are saved initially in 'response holding bay' subfolder of 'DBS_tracing' with original DBS file names that have long numeric suffixes. Rename, save in main 'DBS_tracing' folder (overwriting previous versions from prior runs of process which should first be moved to Archive) and import:
setwd(wd)
lims_trace_return <- read.csv("Data_carriage/DBS_tracing/LIMS_linelist_response.csv", header = F) # update input file name as needed
lims_excl_trace_return <- read.csv("Data_carriage/DBS_tracing/excluded_records_response.csv", header = F)
# confirm total number of records returned
sum(nrow(lims_trace_return)+nrow(lims_excl_trace_return)) # 12,917
lims_trace_return <- rbind(lims_trace_return,lims_excl_trace_return) %>% filter(!grepl("^0011",V1) & !grepl("^9911",V1))
# retain records which NOT successfully traced for subsequent QC and re-tracing
no_trace <- no.trace.wrap.fun(lims_trace_return)
# retain records where sex unknown (may overlap with no_trace generated above)
sex_unknown <- unknown.sex.wrap.fun(lims_trace_return)
# keep ONLY records which successfully traced in first round of tracing and merge with LIMS linelist
allsites <- traced.wrap.fun(lims_trace_return) # 12,913
# state NHS number completion after DBS tracing
sum(is.na(allsites$nhsno))
# 260 NHS number missing
Prepare subsets of data for second round of DBS tracing
dbsprep2 <- allsites %>% mutate(year = as.numeric(format(specdate, '%Y'))) %>% select(year, everything()) %>% select(1:match("epids", names(allsites))) %>% filter(pid_str %in% no_trace$pid_str | pid_str %in% sex_unknown$pid_str) %>%
mutate(forename = sub(" .*", "", forename)) %>% # reformat name fields in no_trace to resend for tracing (where middle names not compatible with DBS trace algorithm)
mutate(sex = case_when( # take all records where sex=="U" and presumptively recode to "F" to resend for tracing (if trace returns sex == 2 then confident that pertains to females)
sex == "U" ~ "F",
TRUE ~ sex
))
# generate .csv files for sending to DBS trace team for second trace (generates two files, one with sufficient PII and one where some/all PII missing. Send BOTH for tracing)
dbs.trace.prep.fun(dbsprep2,
mainfile="Data_carriage/DBS_tracing/LIMS_linelist_for_tracing_rnd2.csv",
excludefile="Data_carriage/DBS_tracing/excluded_records_for_tracing_rnd2.csv")
Import second set of DBS tracing results
#///////////////////////////
#TEMPORARY CODE CHUNK
# review content of latest files sent for DBS tracing (Jan 2025)
test <- read.csv("Data_carriage/DBS_tracing/LIMS_linelist_for_tracing_rnd2.csv", header = T)
test2 <- read.csv("Data_carriage/DBS_tracing/excluded_records_for_tracing_rnd2.csv", header = T)
test <- rbind(test,test2)
sites <- allsites %>% filter(nhsno %in% unique(test$NHS_number))
unique(sites$site)
# has data for all 6 sites
# percent of total records in linelist which in second trace request files
perc <- nrow(sites)/nrow(allsites)*100
# 19.3%
# current percent records missing nhsno, prior to linking back in second trace return
perc <- (sum(is.na(allsites$nhsno))/nrow(allsites))*100
print(perc)
# 3.6%
#///////////////////////////
# import and format trace return csv files
setwd(wd)
lims_trace_return2 <- read.csv("Data_carriage/DBS_tracing/LIMS_linelist_response2.csv", header = F) # update input file name as needed
lims_excl_trace_return2 <- read.csv("Data_carriage/DBS_tracing/excluded_records_response2.csv", header = F)
lims_trace_return2 <- rbind(lims_trace_return2,lims_excl_trace_return2) # combine into single df once checked that non-overlapping patient IDs
# retain records which NOT successfully traced for subsequent QC and re-tracing
no_trace2 <- no.trace.wrap.fun(lims_trace_return2)
# keep ONLY records which successfully traced in second round of tracing and merge with LIMS linelist
allsites$pid_str <- as.integer(allsites$pid_str) # ensure pid_str column in both datasets used for merge in same format
allsites <- traced.wrap.fun(lims_trace_return2) # merge these records back into main linelist
# current percent records missing nhsno, after linking back in second trace return
perc <- (sum(is.na(allsites$nhsno))/nrow(allsites))*100
print(perc)
# 2.01%
# //////////// key checkpoint 2 //////////////
## save as static base dataset **after tracing**
write.csv(allsites, "Data_carriage/Linelist/static_basedata_traced.csv", row.names=F)
Preliminary analysis results prep and presentation
linelist <- read.csv("Data_carriage/Linelist/static_basedata_traced.csv")
linelist$specdate <- as.Date(linelist$specdate, format = "%Y-%m-%d")
linelist$dob <- as.Date(linelist$dob, format = "%Y-%m-%d")
linelist <- linelist %>%
mutate(year = as.numeric(format(specdate, '%Y'))) %>%
select(year, everything()) %>% select(-any_of(c("V1"))) %>%
filter(!grepl("IQAM", surname)) %>% filter(sex!="M") %>%
mutate(ym = format(specdate, "%Y/%m")) %>%
mutate(ym_date = as.Date(paste0(ym, "/01"), format = "%Y/%m/%d"))
# /////////////
# 1) monthly ECM test totals by site
# Summarize totals by year-month and site
monthly_test <- linelist %>% group_by(ym_date,site) %>% summarize(total_tested=sum(z), .groups= 'drop')
# Create a complete set of year-month and site combinations
month_complete <- monthly_test %>% complete(ym_date,site)
# Join the summarized data, allowing for NA in missing combinations
month_complete <- month_complete %>%
left_join(monthly_test, by = c("ym_date", "site")) %>%
mutate(total_tested = ifelse(is.na(total_tested.y), NA, total_tested.y)) %>%
select(ym_date, site, total_tested) %>% mutate(total_tested = case_when(
total_tested<=30 ~ NA,
TRUE ~ total_tested)
)
# plot of monthly test numbers by site
plot <- ggplot(month_complete, aes(x = ym_date, y = total_tested, color = site, group = site)) +
geom_line(size = 1) +
geom_point(size = 2) +
scale_x_date(date_labels = "%b %Y", date_breaks = "1 month") + # Format x-axis
labs(title = "Total ECM tests by Year-Month and Site",
x = "Year-Month", y = "Total Value", color = "Site") +
theme_minimal() +
theme(panel.grid = element_blank(),
axis.line = element_line(color = "black", size = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(size = 14, hjust= 0.5))
# save plot
ggsave("Charts_carriage/ym_ecm_test_bysite.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
# now looking 1 site at a time, comparing monthly test numbers in linelist vs original ECM test numbers from GBS3
# Summarize totals across whole period for each site
test_bysite <- linelist %>% group_by(site) %>% summarize(total_tested=sum(z), .groups= 'drop')
# Create a bar graph of totals by site
plot <- ggplot(test_bysite, aes(x = site, y = total_tested)) +
geom_bar(stat = "identity", fill = "skyblue", color = "black") +
labs(title = "Total ECM tests by Site", x = "Site", y = "Totals") +
theme_minimal() +
theme(
axis.text.x = element_text(size = 14), # Increase size of x-axis labels
axis.text.y = element_text(size = 14), # Increase size of y-axis labels
axis.title.x = element_text(size = 16), # Increase size of x-axis title
axis.title.y = element_text(size = 16) # Increase size of y-axis title
)
# save plot
ggsave("Charts_carriage/all_ecm_test_bysite.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
# /////////////
# 2) age distributions by site and for all sites combined
agedis <- linelist %>% group_by(site) %>%
summarise(
median_age=median(age, na.rm=T),
IQR_age=IQR(age, na.rm=T),
age_min=min(age, na.rm=T),
age_max=max(age, na.rm=T),
Q1 = quantile(age, 0.25, na.rm=T),
Q3 = quantile(age, 0.75, na.rm=T)
)
# Create a plot showing median and IQRs
plot <- ggplot(agedis, aes(x = site, y = median_age)) +
geom_point(size = 3, color = "blue") + # Plot the median age as points
geom_errorbar(aes(ymin = Q1, ymax = Q3), width = 0.2, color = "red") + # Add IQR as error bars
labs(title = "Median Age and IQRs by Category", x = "Category", y = "Median Age") +
theme_minimal()
# save plot
ggsave("Charts_carriage/median_age_bysite.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
# save table
write.csv(agedis, "Data_carriage/ECM_carriage/Stat_summaries/Age_distribution/age_dis.csv")
Apply geo - IMD lookup to link to IMD
# apply IMD linkage function to linelist without AMR data (to be linked back in later)
linelist <- imd.linkage.fun(linelist)
write.csv(linelist, paste0(wd,"/Data_carriage/Linelist/static_basedata_imdlinked_raw.csv"), row.names=F)
linelist <- read.csv("Data_carriage/Linelist/static_basedata_imdlinked_raw.csv")
linelist <- linelist %>%
mutate(
specdate = as.Date(specdate, format = "%Y-%m-%d"),
dob = as.Date(dob, format = "%Y-%m-%d"))
HES linkage for ethnicity enrichment
linelist <- linelist.prep.fun(linelist)
linelist <- hes.ethnlink.fun(linelist)
# //////////// key checkpoint 3a //////////////
## save as static base dataset **after HES CHIME ethnicity enrichment*
write.csv(linelist, "Data_carriage/Linelist/static_basedata_ethnlinked_raw.csv", row.names=F)
linelist <- read.csv("Data_carriage/Linelist/static_basedata_ethnlinked_raw.csv")
linelist$specdate <- as.Date(linelist$specdate, format = "%Y-%m-%d")
linelist$dob <- as.Date(linelist$dob, format = "%Y-%m-%d")
# quantify ethnicity categories (numbers and % total in linelist, including not linked - NA)
hes_eth_allcat <- linelist %>% mutate(z=1) %>% group_by(Ethnic_group) %>% summarize(total=sum(z)) %>% mutate(pct=total/sum(total)*100)
# % not linked = 2.5% (denom = all records)
# to document % not linked from CHIME method, as well as of those linked, the % which not known or not stated
hes_linked_only <- linelist %>% filter(!is.na(Ethnic_group)) %>% mutate(z=1) %>% group_by(Ethnic_group) %>% summarize(total=sum(z)) %>% mutate(pct=total/sum(total)*100)
# % not known = 0.67
# % not states = 1.63 (denom = all HES-linked records)
# if HES returned unknown, not linked, not stated then pass to ECDS method to fill in gaps. Log that ECDS data source
for_ecds <- linelist %>% filter(Ethnic_group == "99 Not Known" | Ethnic_group == "Z Not stated" | is.na(Ethnic_group)) %>% mutate(nhsno_status=case_when(is.na(nhsno) ~ "missing",
!is.na(nhsno) ~ "known"))
# 528 rows (4.1% of linelist)
# tabulate HES ethnicity assignment by NHS number status (known or missing)
tab <- for_ecds %>% mutate(z=1) %>% group_by(nhsno_status,Ethnic_group) %>% summarize(total=sum(z)) %>% mutate(pct=total/sum(total)*100)
# among known nhsno, 205 (76.2%) were HES linked but assigned as "ethnicity not stated"; all records missing nhsno were not linked to HES
write.csv(tab, "Data_carriage/ECM_carriage/Stat_summaries/hes_noethn_by_nhsno_status.csv", row.names=F)
ecds_linked <- linelist.prep.fun(for_ecds)
ecds_linked <- ecds.ethnlink.fun(ecds_linked)
# //////////// key checkpoint 3b //////////////
## save subset of ECDS-linked linelist
write.csv(ecds_linked, "Data_carriage/Linelist/ecds_linked_subset.csv", row.names=F)
ecds_linked <- read.csv("Data_carriage/Linelist/ecds_linked_subset.csv")
ecds_linked <- ecds_linked %>% rename(ethn_code=PatientEthnicCategoryCode) %>%
mutate(ecds_ethnicity=case_when(ethn_code=="A" ~ "British (White)",
ethn_code=="N" ~ "African (Black or Black British)",
ethn_code=="L" ~ "Any other Asian background",
ethn_code=="G" ~ "Any other Mixed background",
ethn_code=="C" ~ "Any other White background",
ethn_code=="S" ~ "Any other ethnic group",
ethn_code=="K" ~ "Bangladeshi (Asian or Asian British)",
ethn_code=="M" ~ "Caribbean (Black or Black British)",
ethn_code=="R" ~ "Chinese (other ethnic group)",
ethn_code=="H" ~ "Indian (Asian or Asian British)",
ethn_code=="J" ~ "Pakistani (Asian or Asian British)",
ethn_code=="F" ~ "White and Asian (Mixed)",
ethn_code=="E" ~ "White and Black African (Mixed)",
ethn_code=="D" ~ "White and Black Caribbean (Mixed)",
ethn_code=="Z" ~ "Z Not Stated",
ethn_code=="B" ~ "White Irish",
ethn_code=="9" ~ "Not Known",
ethn_code=="P" ~ "Any other Black background (Black or Black British)"))
# identify and flag duplicate records resulting from ECDS linkage i.e., where ECDS returned 2 or more ethnicity assignments for a given patient
ecds_linked$dup_flag <- duplicated(ecds_linked$pid_str) | duplicated(ecds_linked$pid_str, fromLast = TRUE)
dups <- ecds_linked %>% filter(dup_flag==T) %>% select(pid_str,specdate,Ethnic_group,ecds_ethnicity,dup_flag) %>% arrange(pid_str,ecds_ethnicity) # explore duplicates and assign a rule set for retaining records
length(unique(dups$pid_str)) # 14 pid_str with duplicate rows
pid_str_dups <- unique(dups$pid_str)
# if 'Z not Stated' and 'Not Known' retain either
# if 'Z not Stated'/'Not Known' and a named ethnicity group, retain ethnicity group
# if NA and 'Z not Stated'/'Not Known' or a named ethnicity group, retain latter (non-NA entry)
# if 'Any other Black background (Black or Black British)' and 'Any other ethnic group' retain the higher specificity ethnicity assignment (in this case Black or Black British > Any other ethnicity group)
#////// ecds_ethnicity hierarchy /////////
# max specificity:
max_spec <- c("British (White)",
"African (Black or Black British)",
"Bangladeshi (Asian or Asian British)",
"Caribbean (Black or Black British)",
"Chinese (other ethnic group)",
"Indian (Asian or Asian British)",
"Pakistani (Asian or Asian British)",
"White and Asian (Mixed)",
"White and Black African (Mixed)",
"White and Black Caribbean (Mixed)",
"White Irish")
# high specificity
high_spec <- c("Any other Black background (Black or Black British)",
"Any other Asian background",
"Any other Mixed background",
"Any other White background")
# medium specificity:
med_spec <- c("Any other ethnic group","Z Not Stated")
# low specificity:
low_spec <- c("Not Known")
# no specificity/missing
no_spec <- NA
# define hierarchy for category or cat
category_hierarchy <- c("Max" = 1, "High" = 2, "Medium" = 3, "Low" = 4, "No" = 5)
# assign each row as per above hierarchy
dups <- dups %>% mutate(cat=case_when(ecds_ethnicity %in% max_spec ~ "Max",
ecds_ethnicity %in% high_spec ~ "High",
ecds_ethnicity %in% med_spec ~ "Medium",
ecds_ethnicity %in% low_spec ~ "Low",
ecds_ethnicity %in% no_spec ~ "No"),
cat_rank = category_hierarchy[cat]) %>%
arrange(pid_str, cat_rank) %>%
distinct(pid_str, .keep_all = T)
# now apply to ecds_linked df
ecds_linked <- ecds_linked %>% mutate(cat=case_when(ecds_ethnicity %in% max_spec ~ "Max",
ecds_ethnicity %in% high_spec ~ "High",
ecds_ethnicity %in% med_spec ~ "Medium",
ecds_ethnicity %in% low_spec ~ "Low",
ecds_ethnicity %in% no_spec ~ "No"),
cat_rank = category_hierarchy[cat]) %>%
group_by(pid_str) %>%
mutate(is_selected = if_else(dup_flag, cat_rank == min(cat_rank[dup_flag]), TRUE)) %>%
ungroup() %>%
filter(is_selected) %>% select(-c("dup_flag","cat","cat_rank","is_selected"))
# check that non-duplicate rows now in data for pid_str vals flagged above
pid_str_dups %in% ecds_linked$pid_str # TRUE in all instances
# check that pid_str retained correspond to those identified as having duplicate entries above
unique(dups$pid_str) %in% pid_str_dups # TRUE at all positions in vector
# tabulate ECDS ethnicity assignment by NHS number status (known or missing)
tab <- ecds_linked %>% mutate(nhsno_status=case_when(is.na(nhsno) ~ "missing",
!is.na(nhsno) ~ "known"),
heslink_status=case_when(is.na(Ethnic_group) ~ "not linked",
!is.na(Ethnic_group) ~ "linked")
) %>% mutate(z=1) %>% group_by(nhsno_status,heslink_status,ecds_ethnicity) %>% summarize(total=sum(z)) %>% mutate(pct=total/sum(total)*100)
write.csv(tab, "Data_carriage/ECM_carriage/Stat_summaries/hes_noethn_ecds_cat.csv", row.names=F)
# among known nhsno, 218 (77%) were HES linked but returned "99 Not Known" or "Z Not stated". Only 8 of these 218 records had an ethnicity assignment in ECDS. For those records not HES linked, ECDS enrichment returned 6 records with known ethnicity. Overall, ECDS enrichment provided an ethnicity for 4.9% records that had an NHS number
ecds_linked <- ecds_linked %>% select(pid_str,ecds_ethnicity) %>% filter(!is.na(ecds_ethnicity)) # CONTINUE HERE - THERE ARE DUPLICATES OF pid_str
testmerge <- merge(linelist,ecds_linked, by = "pid_str", all=T) # merge back deduplicated, ecds-linked subset of records into linelist to retain new ecds_ethnicity col
testmerge <- testmerge %>% mutate(Ethnic_group=case_when((ecds_ethnicity!=Ethnic_group & # assign ECDS ethnicity where not HES linked or where ethnicity not known in HES but returned in ECDS linkage
ecds_ethnicity!="Not Known" &
ecds_ethnicity!="Z Not Stated") |
is.na(Ethnic_group) ~ ecds_ethnicity,
TRUE ~ Ethnic_group)) %>% select(-c("ecds_ethnicity")) %>%
mutate(Ethnicity_group=case_when(Ethnicity_group=="Asian" ~ "Any other Asian background", # re code LIMS ethnicity categories to align with HES categorization (defaulting to Any other...)
Ethnicity_group=="White" ~ "Any other White background",
Ethnicity_group=="Black" ~ "Any other Black background",
Ethnicity_group=="Mixed" ~ "Any other Mixed background",
Ethnicity_group=="" ~ "Z Not stated")) %>%
mutate(Ethnic_group=case_when((Ethnic_group=="Z Not stated" | Ethnic_group=="99 Not Known" | Ethnic_group=="Z not stated" | is.na(Ethnic_group)) & Ethnicity_group!="" & Ethnicity_group!="Unknown" ~ Ethnicity_group,
TRUE ~ Ethnic_group)) # assign LIMS ethnicity where recorded, if not HES linked or ethnicity not stated in HES, and not returned in ECDS linkage (last resort ethnicity assignment)
# DEDUPLICATE HERE AFTER ETHNICITY LINKAGE
# //////////// key checkpoint 4 //////////////
## save as static base dataset **after full ethnicity assignment and categorisation**
write.csv(linelist, "Data_carriage/Linelist/static_basedata_ethnlinked_final.csv", row.names=F)
linelist <- read.csv("Data_carriage/Linelist/static_basedata_ethnlinked_raw.csv")
linelist$specdate <- as.Date(linelist$specdate, format = "%Y-%m-%d")
linelist$dob <- as.Date(linelist$dob, format = "%Y-%m-%d")
# CONTINUE HERE
linelist <- linelist.ethcat5.fun(linelist)
# use ethnicity field in LIMS if hes ethnicity not returned for a patient
linelist <- linelist %>% mutate(hes_ethnicity = case_when(
(hes_ethnicity=="Not linked" & lim_ethnicity!="Unknown") ~ paste0(lim_ethnicity,"_fromLIMS"),
TRUE ~ hes_ethnicity
))
# # SAVE ETH LINKED LINELIST NOW
# write.csv(linelist, "Data_carriage/linelist/ethnlinked_linelist2.csv")
# record the data source for ethnicity by site
# recording attrition by site (where ethnicity missing too, as this can't be used to estimate carriage by ethnicity)
# - raw data (from site) NHS number completion
# enhanced data NHS number completion
# for automated report
# compare data quality by site - are some biasing the results (sensitivity analysis)
# IMPORT MAIN ETHNICITY ENRICHED LINELIST HERE (ethnicity in 5 categories)
ethnlinked_linelist=import(here("Data_carriage","Linelist","ethnlinked_linelist2.csv"),
format = "csv",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
linelist <- ethnlinked_linelist
linelist <- linelist %>% mutate(species=case_when(species=="beta Haemolytic Strep, group B" ~ "Group B Streptococcus",
TRUE ~ species)) # mop up of any variants of GBS pos test field
# success of ethnicity ascertainment from CHIME linkage (and supplemented by ECDS), overall and by site, and then % of ethn filled in from local LIMS data, also overall and by site
Summarise ethnicity ascertainment and estimate carriage by
ethnicity
# filter for first 4 sites data analysed
four_site <- c("Bedford","Derriford","Freeman","Royal Stoke")
linelist <- linelist %>% filter(site %in% four_site)
gbscarr_ethn <- gbs.carr.byethn.fun(linelist)
write.csv(gbscarr_ethn, "Data_carriage/ECM_carriage/Carriage_estimates/gbscarr_ethn.csv")
# prep eth categories for plot
# Create a bar graph of gbs carriage by ethn (where denom doesn't include ethnicity unknown/link to HES not successful)
gbscarr_forplot <- gbscarr_ethn %>%
select(-c("carriage_bythn")) %>%
mutate(ethn_forplot = case_when(
hes_ethnicity=="Asian_fromLIMS" ~ "Asian",
hes_ethnicity=="Black_fromLIMS" ~ "Black",
hes_ethnicity=="Mixed_fromLIMS" ~ "Mixed",
hes_ethnicity=="White_fromLIMS" ~ "White",
hes_ethnicity=="Other_fromLIMS" ~ "Other",
TRUE ~ hes_ethnicity
)) %>%
select(-c("hes_ethnicity")) %>%
group_by(ethn_forplot) %>%
summarize(across(everything(), sum)) %>%
mutate(gbc_carr = gbspos/totalecm*100) %>%
filter(ethn_forplot!="Not linked" & ethn_forplot!="Unknown") %>%
arrange(gbc_carr) %>% mutate(ethn_forplot=factor(ethn_forplot, levels = c("White","Black","Asian","Other","Mixed")))
plot <- ggplot(gbscarr_forplot, aes(x = ethn_forplot, y = gbc_carr)) +
geom_bar(stat = "identity", fill = "skyblue", color = "black") +
geom_errorbar(
aes(ymin = lower_ci, ymax = upper_ci),
width = 0.2,
color = "black"
) +
geom_hline(yintercept = unique(gbscarr_forplot$total_carriage), linetype = "dashed", color = "red", size = 1) +
labs(title = "GBS carriage by ethnicity", x = "Ethnicity", y = "GBS carriage (%)") +
theme_minimal() +
theme(
panel.grid = element_blank(),
axis.line = element_line(color = "black", size = 0.5),
axis.text.x = element_text(size = 12), # Increase size of x-axis labels
axis.text.y = element_text(size = 12), # Increase size of y-axis labels
axis.title.x = element_text(size = 14,
margin = margin(b = 20)), # Increase size of x-axis title
axis.title.y = element_text(size = 14, # Increase size of y-axis title
margin = margin(r = 10)),
plot.title = element_text(size = 14, hjust= 0.5)
)
# save plot
ggsave("Charts_carriage/gbscarr_ethn.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
Estimate carriage by ethnicity for Asian subgroupings Indian,
Pakistani and Bangladeshi, using linelist prior to 5 category grouping
of ethnicity field
# import linelist with full set of HES ethnicity categories
linelist_ethnsub=import(here("Data_carriage","Linelist","ethnlinked_linelist.csv"),
format = "csv",
fill = T,
comment.char="",
na = c(""),
skip = 0,
full.names = T)
nrow(linelist_ethnsub)
unique(linelist_ethnsub$Ethnic_group)
gbscarr_subethn <- gbs.carr.bysubethn.fun(linelist_ethnsub)
# now generate GBS carriage summaries for all 16 HES ethnicity categories
gbscarr_allsubeth <- gbs.carr.byallsubethn.fun(linelist_ethnsub)
write.csv(gbscarr_allsubeth, "Data_carriage/ECM_carriage/Carriage_estimates/gbscarr_allsubeth.csv")
Estimate carriage by deprivation
gbscarr_imd <- gbs.carr.byimd.fun(linelist)
gbscarr_forplot <- gbscarr_imd %>% filter(imd_quintile!="Not linked" & imd_quintile!="NA" & !is.na(imd_quintile)) %>%
mutate(imd_quintile=factor(imd_quintile, levels = c("20% least deprived", "20% to 40%", "40% to 60%", "60% to 80%", "20% most deprived"), ordered = TRUE))
write.csv(gbscarr_imd, "Data_carriage/ECM_carriage/Carriage_estimates/gbscarr_imd.csv")
# Create a bar graph of gbs carriage by IMD quintile
plot <- ggplot(gbscarr_forplot, aes(x = imd_quintile, y = carriage_byimd)) +
geom_bar(stat = "identity", fill = "skyblue", color = "black") +
geom_errorbar(
aes(ymin = lower_ci, ymax = upper_ci),
width = 0.2,
color = "black"
) +
geom_hline(yintercept = unique(gbscarr_forplot$total_carriage), linetype = "dashed", color = "red", size = 1) +
labs(title = "GBS carriage by IMD quintile", x = "IMD quintile", y = "GBS carriage (%)") +
theme_minimal() +
theme(
panel.grid = element_blank(),
axis.line = element_line(color = "black", size = 0.5),
axis.text.x = element_text(size = 12), # Increase size of x-axis labels
axis.text.y = element_text(size = 12), # Increase size of y-axis labels
axis.title.x = element_text(size = 14,
margin = margin(b = 20)), # Increase size of x-axis title
axis.title.y = element_text(size = 14, # Increase size of y-axis title
margin = margin(r = 10)),
plot.title = element_text(size = 14, hjust= 0.5)
)
# save plot
ggsave("Charts_carriage/gbs_carr_imd.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
Estimate carriage by age group
gbscarr_age <- gbs.carr.byage.fun(linelist)
write.csv(gbscarr_age, "Data_carriage/ECM_carriage/Carriage_estimates/gbscarr_age.csv")
# repeat but restrict to each ethnicity in turn
ll_black <- linelist %>% filter(hes_ethnicity=="Black" | hes_ethnicity=="Black_fromLIMS")
gbscarr_age <- gbs.carr.byage.fun(ll_black)
ll_asian <- linelist %>% filter(hes_ethnicity=="Asian" | hes_ethnicity=="Asian_fromLIMS")
gbscarr_age <- gbs.carr.byage.fun(ll_asian)
ll_mixed <- linelist %>% filter(hes_ethnicity=="Mixed" | hes_ethnicity=="Mixed_fromLIMS")
gbscarr_age <- gbs.carr.byage.fun(ll_mixed)
ll_other <- linelist %>% filter(hes_ethnicity=="Other" | hes_ethnicity=="Other_fromLIMS")
gbscarr_age <- gbs.carr.byage.fun(ll_other)
ll_white <- linelist %>% filter(hes_ethnicity=="White" | hes_ethnicity=="White_fromLIMS")
gbscarr_age <- gbs.carr.byage.fun(ll_white)
# plot % carriage by age group in bar chart
gbscarr_forplot <- gbscarr_age %>% filter(!agegroup %in% c("10-14","50-54","55-59","60-64") & !is.na(agegroup))
# Create a bar graph of gbs carriage by age group
plot <- ggplot(gbscarr_forplot, aes(x = agegroup, y = carriage_byage)) +
geom_bar(stat = "identity", fill = "skyblue", color = "black") +
geom_errorbar(
aes(ymin = lower_ci, ymax = upper_ci),
width = 0.2,
color = "black"
) +
geom_hline(yintercept = unique(gbscarr_forplot$total_carriage), linetype = "dashed", color = "red", size = 1) +
labs(title = "GBS carriage by age group", x = "Age group", y = "GBS carriage (%)") +
theme_minimal() +
theme(
panel.grid = element_blank(),
axis.line = element_line(color = "black", size = 0.5),
axis.text.x = element_text(size = 12), # Increase size of x-axis labels
axis.text.y = element_text(size = 12), # Increase size of y-axis labels
axis.title.x = element_text(size = 14,
margin = margin(b = 20)), # Increase size of x-axis title
axis.title.y = element_text(size = 14, # Increase size of y-axis title
margin = margin(r = 10)),
plot.title = element_text(size = 14, hjust= 0.5)
)
# save plot
ggsave("Charts_carriage/gbs_carr_agegroup.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
# Create a bar graph of totals by age group
gbstest_age <- gbscarr_age %>% select(-c("carriage_byage","total_carriage","lower_ci","upper_ci")) %>% filter(!is.na(agegroup)) %>% mutate(agegroup = case_when(
agegroup %in% c("40-44","45-49") ~ "40-49",
agegroup %in% c("50-54","60-64") ~ "50+",
TRUE ~ agegroup
)) %>% group_by(agegroup) %>% summarize(across(everything(), sum))
plot <- ggplot(gbstest_age, aes(x = agegroup, y = pct)) +
geom_bar(stat = "identity", fill = "skyblue", color = "black") +
labs(title = "% total ECM tests by age group", x = "Age group", y = "% total") +
theme_minimal() +
theme(
panel.grid = element_blank(),
axis.line = element_line(color = "black", size = 0.5),
axis.text.x = element_text(size = 12), # Increase size of x-axis labels
axis.text.y = element_text(size = 12), # Increase size of y-axis labels
axis.title.x = element_text(size = 14,
margin = margin(b = 20)), # Increase size of x-axis title
axis.title.y = element_text(size = 14, # Increase size of y-axis title
margin = margin(r = 10)),
plot.title = element_text(size = 14, hjust= 0.5)
)
# # save plot
ggsave("Charts_carriage/totals_agegroup.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
# saving age dist by eth plots
# save plot
ggsave("Charts_carriage/agegroup_black.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
ggsave("Charts_carriage/agegroup_asian.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
ggsave("Charts_carriage/agegroup_mixed.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
ggsave("Charts_carriage/agegroup_other.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
ggsave("Charts_carriage/agegroup_white.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
Estimate carriage by site
gbscarr_site <- gbs.carr.bysite.fun(linelist)
# Create a bar graph of gbs carriage by site
plot <- ggplot(gbscarr_site, aes(x = site, y = carriage_bysite)) +
geom_bar(stat = "identity", fill = "skyblue", color = "black") +
geom_errorbar(
aes(ymin = lower_ci, ymax = upper_ci),
width = 0.2,
color = "black"
) +
geom_hline(yintercept = unique(gbscarr_forplot$total_carriage), linetype = "dashed", color = "red", size = 1) +
labs(title = "GBS carriage (%) by ECM site", x = "Site", y = "%") +
theme_minimal() +
theme(
panel.grid = element_blank(),
axis.line = element_line(color = "black", size = 0.5),
axis.text.x = element_text(size = 12), # Increase size of x-axis labels
axis.text.y = element_text(size = 12), # Increase size of y-axis labels
axis.title.x = element_text(size = 14,
margin = margin(b = 20)), # Increase size of x-axis title
axis.title.y = element_text(size = 14, # Increase size of y-axis title
margin = margin(r = 10)),
plot.title = element_text(size = 14, hjust= 0.5)
)
# save plot
ggsave("Charts_carriage/gbs_carr_sites.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
# conf int for overall GBS carr estimate
total_carr <- gbscarr_site %>% select(c("total","gbspos")) %>% summarize(across(everything(), sum)) %>%
rowwise() %>%
mutate(
conf = list(binom.confint(gbspos, total, methods = "exact")),
lower_ci = conf[[1, "lower"]] * 100,
upper_ci = conf[[1, "upper"]] * 100
) %>%
select(-conf)
Estimate carriage by month of ECM implementation, for all sites
combined
gbscarr_month <- gbs.carr.byym.fun(linelist)
write.csv(gbscarr_month, "Data_carriage/ECM_carriage/Carriage_estimates/gbscarr_byym.csv")
gbscarr_month$month_year <- ymd(paste0(gbscarr_month$month_year, "-01")) # Append day to create a complete date
# Create a bar graph of gbs carriage by age group
plot <- ggplot(gbscarr_month, aes(x = ym, y = carriage_byym)) +
geom_bar(stat = "identity", fill = "skyblue", color = "black") +
labs(title = "GBS carriage (%) by month and year (all 4 sites)", x = "month and year", y = "%") +
theme_minimal() +
theme(axis.text.x = element_text(size = 10, angle = 45, hjust = 1)) +
scale_x_date(date_labels = "%Y-%m", date_breaks = "1 month") + # Format x-axis
theme(
axis.text.x = element_text(size = 14), # Increase size of x-axis labels
axis.text.y = element_text(size = 14), # Increase size of y-axis labels
axis.title.x = element_text(size = 16), # Increase size of x-axis title
axis.title.y = element_text(size = 16) # Increase size of y-axis title
)
# save plot
ggsave("Charts_carriage/gbscarr_bymonth.jpeg", plot = plot, device = "jpeg", width = 8, height = 6, dpi = 300)
Estimate carriage by year of ECM implementation, for all sites
combined
gbs_byyear <- gbs.carr.byyear.fun(linelist)
write.csv(gbs_byyear, "Data_carriage/ECM_carriage/Carriage_estimates/gbscarr_byyear.csv")
Produce summaries for flow chart for data linkage support request
# total unique PIDs per site
# Get unique site names
unique_sites <- unique(linelist$site)
# Use lapply to calculate counts for each site
site_counts <- lapply(unique_sites, function(site) sum(linelist$site == site))
# Combine results into a named vector or data frame for clarity
names(site_counts) <- unique_sites
site_counts <- unlist(site_counts)
# Print the results
print(site_counts)
length(unique(linelist$pid_str))
# date range by site
range(linelist$year, na.rm=T)
range(linelist$specdate, na.rm=T)
Logistic regression - association between maternal ethnicity and GBS
carriage, controlling for age and deprivation
# ///////////////////
# model with ethnicity as five categories
model_data <- linelist %>% filter(!is.na(hes_ethnicity)) %>% mutate(gbs_car = case_when(species=="Group B Streptococcus" ~ 1,
species=="GBS not isolated" | species=="No organism recorded" ~ 0),
hes_ethnicity = case_when((hes_ethnicity=="Asian" | hes_ethnicity=="Asian_fromLIMS") ~ "Asian",
(hes_ethnicity=="Black" | hes_ethnicity=="Black_fromLIMS") ~ "Black",
(hes_ethnicity=="Mixed" | hes_ethnicity=="Mixed_fromLIMS") ~ "Mixed",
(hes_ethnicity=="Other" | hes_ethnicity=="Other_fromLIMS") ~ "Other",
(hes_ethnicity=="White" | hes_ethnicity=="White_fromLIMS") ~ "White"),
hes_ethnicity = as.factor(hes_ethnicity)) %>% filter(agegroup!="not known") %>% filter(!is.na(age)) %>% mutate(z=1,
agegroup = cut(age,
breaks = seq(0, 100, by = 5), # Specify the breaks for the age bands
right = FALSE, # Include the left endpoint, exclude the right
labels = paste(seq(0, 95, by = 5), seq(4, 99, by = 5), sep = "-")),
agegroup = fct_collapse(agegroup,
"0-14" = c("0-4","5-9","10-14"),
"40+" = c("40-44","40-44","45-49","50-54","55-59","60-64","65-69","70-74","75-79","80-84","85-89","90-94","95-99"))) %>% select(year,agegroup,imd_quintile,hes_ethnicity,gbs_car) %>% filter(!is.na(hes_ethnicity))
# %>% filter(!is.na(imd_quintile))
model_data <- imd.fac.label.fun(model_data)
model_data$imd_quintile <- as.factor(model_data$imd_quintile)
model_data$hes_ethnicity <- relevel(model_data$hes_ethnicity, ref = "White")
model_data$imd_quintile <- relevel(model_data$imd_quintile, ref = "20% least deprived")
model_data$agegroup <- relevel(model_data$agegroup, ref = "20-24")
model_data$gbs_car <- as.factor(model_data$gbs_car)
model_data <- droplevels(model_data)
successes <- sum(as.numeric(model_data$gbs_car == 1))
trials <- nrow(model_data)
result <- prop.test(successes, trials, conf.level = 0.95)
model <- glm(gbs_car ~ hes_ethnicity + agegroup + imd_quintile,
data = model_data,
family = binomial(link = "logit"))
sapply(model_data, function(x) if(is.factor(x)) levels(x))
# Summary of the model to see coefficients and statistics
summary(model)
# Exponentiate the coefficients to get odds ratios
exp(coef(model))
model_out <- tidy(model, exponentiate = TRUE, conf.int = TRUE)
write.csv(model_out, "Data_carriage/Model/multilog.csv", row.names=F)
# ///////////////////////
# rerun model with ethnicity as binary (white vs any non-white ethnicity)
model_data_binary <- ethn.binary.fac.fun(model_data)
model <- glm(gbs_car ~ hes_ethnicity + agegroup + imd_quintile + year,
data = model_data_binary,
family = binomial(link = "logit"))
sapply(model_data, function(x) if(is.factor(x)) levels(x))
# Summary of the model to see coefficients and statistics
summary(model)
# Exponentiate the coefficients to get odds ratios
exp(coef(model))
model_out <- tidy(model, exponentiate = TRUE, conf.int = TRUE)
write.csv(model_out, "Data_carriage/Model/multilog_ethn_binary.csv", row.names=F)
Add a new chunk by clicking the Insert Chunk button on the
toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and
output will be saved alongside it (click the Preview button or
press Ctrl+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the
editor. Consequently, unlike Knit, Preview does not
run any R code chunks. Instead, the output of the chunk when it was last
run in the editor is displayed.
LS0tDQp0aXRsZTogIkdCUyBjYXJyaWFnZSBlc3RpbWF0aW9uIHByb2Nlc3MiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2sgb3V0bGluaW5nIHN0ZXBzIGZvciBMSU1TIGRhdGEgcHJvY2Vzc2luZyBhbmQgR0JTIGNhcnJpYWdlIGVzdGltYXRpb24gZnJvbSBFQ00tYmFzZWQgdGVzdGluZyBkYXRhIGZyb20gdGhlIEdCUzMgdHJpYWwNCg0KU2V0IG1haW4gd29ya2luZyBkaXJlY3RvcnkNCg0KYGBge3Igc2V0IHdkLCBpbmNsdWRlPUZ9DQoNCndkIDwtICIvL0ZJTEVDT0wxNy5waGUuZ292LnVrL1Byb2plY3REYXRhL0hDQUkvV29ya3N0cmVhbSA0IEZvbGRlci9zdHJlcHRvY29jY2FsIGluZmVjdGlvbnMvR3JvdXAgQiBzdHJlcC9HQlMzL0dCUyBtYXRlcm5hbCBjYXJyaWFnZSBzdHVkeS9HQlMgY2FycmlhZ2UgZXN0aW1hdGlvbiINCg0KYGBgDQoNCkluc3RhbGwgYW5kIGxvYWQgcGFja2FnZXMNCg0KYGBge3IgcGFja2FnZXMsIGluY2x1ZGU9Rn0NCg0Kc2V0d2Qod2QpDQpzb3VyY2UoIlIgc2NyaXB0c19jYXJyaWFnZS9wYWNrYWdlc19HQlNfY2FycmlhZ2UuUiIpDQoNCiMgcGFzc3dvcmQgcHJvbXB0IGlzIHRvIGFjY2VzcyBQSEUgaW5kaWNhdG9yIG1ldGhvZHMgZnVuY3Rpb25zDQoNCmBgYA0KDQpMb2FkIGZ1bmN0aW9ucw0KDQpgYGB7ciBmdW5jdGlvbnMsIGluY2x1ZGU9VFJVRX0NCg0Kc2V0d2Qod2QpDQpzb3VyY2UoIlIgc2NyaXB0c19jYXJyaWFnZS9mdW5jdGlvbnNfR0JTX2NhcnJpYWdlLlIiKQ0KDQpgYGANCg0KSW1wb3J0IExJTVMgZGF0YSBmcm9tIEVDTS1iYXNlZCBHQlMgdGVzdGluZyAoZGF0YXNldHMgcHJvdmlkZWQgYnkgbWljcm8gbGFicykNClByZS1jaGVjayAtIG9wZW4gY3N2IGFuZCBjaGVjayB0aGF0IGluIGNvcnJlY3QgdGVtcGxhdGUgZm9ybWF0IC0gZGVzY3JpYmUgdGhlIHJhdyBkYXRhDQoNCmBgYHtyIGxpbXMgZGF0YSBpbXBvcnQsIGluY2x1ZGU9VFJVRX0NCg0KIyBzZXR3ZCh3ZCkNCg0KIy8vLy8vLy8vLy8vLy8vLy8vLy8vDQojIGZvciBSb3lhbCBTdG9rZQ0KDQp0ZXN0cG9zIDwtIGltcG9ydChoZXJlKCJEYXRhX2NhcnJpYWdlIiwiRUNNX2NhcnJpYWdlIiwiRWRpdGVkIiwiRUNNX3Bvc18wNl8wMl8yMDIzdG8wMV8wNF8yMDI0X1JveWFsX3N0b2tlLnhsc3giKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gInhsc3giLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gVCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbWVudC5jaGFyPSIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYSA9IGMoIiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2lwID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpDQoNCnRlc3RuZWcgPC0gaW1wb3J0KGhlcmUoIkRhdGFfY2FycmlhZ2UiLCJFQ01fY2FycmlhZ2UiLCJFZGl0ZWQiLCJFQ01fbmVnXzA2XzAyXzIwMjN0bzAxXzA0XzIwMjRfUm95YWxfc3Rva2UueGxzeCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAieGxzeCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBULA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tZW50LmNoYXI9IiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hID0gYygiIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVCkNCg0KdGVzdHBvczIgPC0gaW1wb3J0KGhlcmUoIkRhdGFfY2FycmlhZ2UiLCJFQ01fY2FycmlhZ2UiLCJFZGl0ZWQiLCJFQ01fcG9zXzMwXzEwXzIwMjJ0bzA1XzAyXzIwMjNfUm95YWxfc3Rva2UueGxzIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICJ4bHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gVCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbWVudC5jaGFyPSIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYSA9IGMoIiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2lwID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpDQoNCnRlc3RuZWcyIDwtIGltcG9ydChoZXJlKCJEYXRhX2NhcnJpYWdlIiwiRUNNX2NhcnJpYWdlIiwiRWRpdGVkIiwiRUNNX25lZ18zMF8xMF8yMDIydG8wNV8wMl8yMDIzX1JveWFsX3N0b2tlLnhscyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAieGxzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQuY2hhcj0iIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEgPSBjKCIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2tpcCA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUKQ0KDQoNCmNvbG5hbWVzKHRlc3Rwb3MpICVpbiUgY29sbmFtZXModGVzdG5lZykgIyBsYXN0IDMgY29scyBkaXN0aW5jdCBpbiB0ZXN0bmVnDQoNCnRlc3Rwb3MgPC0gbGltcy5zdG5kLmZvcm1hdC5mdW4odGVzdHBvcykNCnRlc3Rwb3MyIDwtIGxpbXMuc3RuZC5mb3JtYXQuZnVuKHRlc3Rwb3MyKQ0KDQp0ZXN0bmVnIDwtIGxpbXMuc3RuZC5mb3JtYXQuZnVuKHRlc3RuZWcpDQp0ZXN0bmVnMiA8LSBsaW1zLnN0bmQuZm9ybWF0LmZ1bih0ZXN0bmVnMikNCg0KYWxsdGVzdCA8LSByYmluZCh0ZXN0cG9zLHRlc3Rwb3MyLHRlc3RuZWcsdGVzdG5lZzIpDQphbGx0ZXN0MSA8LSBhbGx0ZXN0ICU+JSBtdXRhdGUoc2l0ZT0iUm95YWwgU3Rva2UiKQ0Kcm0odGVzdHBvcyx0ZXN0cG9zMix0ZXN0bmVnLHRlc3RuZWcyKQ0KDQoNCiMvLy8vLy8vLy8vLy8vLy8vLy8vLw0KIyBmb3IgQmVkZm9yZA0KYWxsdGVzdCA8LSBpbXBvcnQoaGVyZSgiRGF0YV9jYXJyaWFnZSIsIkVDTV9jYXJyaWFnZSIsIkVkaXRlZCIsIkJlZGZvcmRfR0JTM19DbGVhbmVkRGF0YS5jc3YiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImNzdiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBULA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tZW50LmNoYXI9IiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hID0gYygiIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVCkNCmFsbHRlc3QkYFBhdGllbnQgTkhTIE51bWJlcmAgPC0gYXMubnVtZXJpYyhhbGx0ZXN0JGBQYXRpZW50IE5IUyBOdW1iZXJgKQ0KDQphbGx0ZXN0MiA8LSBsaW1zLnN0bmQuZm9ybWF0LmZ1bihhbGx0ZXN0KQ0KYWxsdGVzdDIgPC0gYWxsdGVzdDIgJT4lIG11dGF0ZShzaXRlPSJCZWRmb3JkIikNCg0KDQojLy8vLy8vLy8vLy8vLy8vLy8vLy8NCiMgZm9yIERlcnJpZm9yZA0KYWxsdGVzdCA8LSBpbXBvcnQoaGVyZSgiRGF0YV9jYXJyaWFnZSIsIkVDTV9jYXJyaWFnZSIsIkVkaXRlZCIsIkRlcnJpZm9yZF9HQlMzX0NsZWFuZWREYXRhLmNzdiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiY3N2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQuY2hhcj0iIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEgPSBjKCIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2tpcCA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUKQ0KYWxsdGVzdCRgUGF0aWVudCBOSFMgTnVtYmVyYCA8LSBhcy5udW1lcmljKGFsbHRlc3QkYFBhdGllbnQgTkhTIE51bWJlcmApDQoNCg0KYWxsdGVzdDMgPC0gbGltcy5zdG5kLmZvcm1hdC5mdW4oYWxsdGVzdCkNCmFsbHRlc3QzIDwtIGFsbHRlc3QzICU+JSBtdXRhdGUoc2l0ZT0iRGVycmlmb3JkIikNCg0KDQojLy8vLy8vLy8vLy8vLy8vLy8vLy8NCiMgZm9yIEZyZWVtYW4NCmFsbHRlc3QgPC0gaW1wb3J0KGhlcmUoIkRhdGFfY2FycmlhZ2UiLCJFQ01fY2FycmlhZ2UiLCJFZGl0ZWQiLCJGcmVlbWFuX0dCUzNfQ2xlYW5lZERhdGEuY3N2IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICJjc3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gVCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbWVudC5jaGFyPSIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYSA9IGMoIiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2lwID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpDQphbGx0ZXN0IDwtIGFsbHRlc3QgJT4lDQogIG11dGF0ZSgNCiAgICBgUGF0aWVudCBOSFMgTnVtYmVyYCA9IGFzLm51bWVyaWMoYFBhdGllbnQgTkhTIE51bWJlcmApLA0KICAgIGBTcGVjaW1lbiBEYXRlYCA9IGFzLkRhdGUoYFNwZWNpbWVuIERhdGVgLCBmb3JtYXQgPSAiJWQvJW0vJVkiKSwNCiAgICBgUGF0aWVudCBEYXRlIG9mIEJpcnRoYCA9IGFzLkRhdGUoYFBhdGllbnQgRGF0ZSBvZiBCaXJ0aGAsIGZvcm1hdCA9ICIlZC8lbS8lWSIpDQogICkNCg0KIyBDT05USU5VRSBIRVJFIC0gc3BlY2ltZW4gZGF0ZSBmb3JtYXQgZGlmZmVyZW50IGZvciBGcmVlbWFuIHZzIG90aGVyIHNpdGVzLiBSZXNvbHZlIGhlcmUgYmVmb3JlIGFwcGx5aW5nIGRmJGR0IDwtIGx1YnJpZGF0ZTo6eW1kKGRmJHNwZWNkYXRlKSBzdGVwIGluIG9iYWkgcHJvY2Vzcw0KDQojIGhhbmRsZSBmb3JtYXQgZm9yIHN0cmluZ3MgaW4gRXRobmljaXR5IGNvbHVtbiB3aGljaCBoYXZlIHN5bWJvbHMNCmFsbHRlc3QkRXRobmljaXR5IDwtIGljb252KGFsbHRlc3QkRXRobmljaXR5LCBmcm9tID0gIklTTy04ODU5LTEiLCB0byA9ICJVVEYtOCIpDQoNCg0KYWxsdGVzdDQgPC0gbGltcy5zdG5kLmZvcm1hdC5mdW4oYWxsdGVzdCkNCmFsbHRlc3Q0IDwtIGFsbHRlc3Q0ICU+JSBtdXRhdGUoc2l0ZT0iRnJlZW1hbiIpDQoNCg0KIy8vLy8vLy8vLy8vLy8vLy8vLy8vDQojIGZvciBFcHNvbSBhbmQgU3QgSGVsaWVyIChTdCBHZW9yZ2UncykNCmFsbHRlc3Q1IDwtIHJlYWQuY3N2KCJcXFxcSUlTTklTQ09MMDFcXEdCU1N0dWR5XFxQcm9jZXNzZWRcXEVwc29tX1N0SGVsaWVyX0dCUzNfVUlELmNzdiIpDQphbGx0ZXN0NSA8LSBhbGx0ZXN0NSAlPiUgbXV0YXRlKHNpdGU9IkVwc29tIikgJT4lIHJlbG9jYXRlKHNpdGUsIC5hZnRlciA9IGxhc3RfY29sKCkpICU+JSBtdXRhdGUoc3BlY2llcz1jYXNlX3doZW4oDQogIGlzLm5hKHNwZWNpZXMpIH4gIkdCUyBub3QgaXNvbGF0ZWQiLA0KICAoc3BlY2llcyE9IkJldGEgSGFlbW9seXRpYyBTdHJlcHRvY29jY3VzIEdyb3VwIEIiICYgc3BlY2llcyE9Ikdyb3VwIEIgU3RyZXB0b2NvY2N1cyIpIH4gIkdCUyBub3QgaXNvbGF0ZWQiLA0KICAoc3BlY2llcz09IkJldGEgSGFlbW9seXRpYyBTdHJlcHRvY29jY3VzIEdyb3VwIEIiIHwgc3BlY2llcz09Ikdyb3VwIEIgU3RyZXB0b2NvY2N1cyIpIH4gIkdyb3VwIEIgU3RyZXB0b2NvY2N1cyIsDQogIFRSVUUgfiBzcGVjaWVzDQopKSAlPiUgcmVuYW1lKGV0aG5pY2l0eT1FdGhuaWNpdHlfZ3JvdXApICU+JSByZWxvY2F0ZShldGhuaWNpdHksIC5hZnRlciA9IHNleCkgJT4lDQogICAgbXV0YXRlKA0KICAgICAgZG9iID0gYXMuRGF0ZShkb2IsIGZvcm1hdCA9ICIlZC8lbS8lWSIpLCAgIyBDaGFyYWN0ZXIgc3RyaW5nICJERC9NTS9ZWVlZIiB0byBEYXRlDQogICAgICBzcGVjZGF0ZSA9IGFzLkRhdGUoc3BlY2RhdGUsIGZvcm1hdCA9ICIlZC8lbS8lWSIpDQogICAgKSAlPiUgc2VsZWN0KC1jKCJwdF9pZCIpKQ0KDQoNCg0KIy8vLy8vLy8vLy8vLy8vLy8vLy8vDQojIGZvciBSb3lhbCBCbGFja2J1cm4NCmFsbHRlc3Q2IDwtIGltcG9ydChoZXJlKCJEYXRhX2NhcnJpYWdlIiwiRUNNX2NhcnJpYWdlIiwiRWRpdGVkIiwiVUtIU0EgR0IzIHRyaWFsX1JveWFsX0JsYWNrYnVybi5jc3YiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImNzdiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBULA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tZW50LmNoYXI9IiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hID0gYygiIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVCkNCg0KYWxsdGVzdDYgPC0gbGltcy5zdG5kLmZvcm1hdC5mdW4oYWxsdGVzdDYpDQphbGx0ZXN0NiA8LSBhbGx0ZXN0NiAlPiUgbXV0YXRlKHNpdGU9IlJveWFsIEJsYWNrYnVybiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvYj1hcy5EYXRlKGRvYiwgZm9ybWF0ID0gIiVkLyVtLyVZIikpICU+JSANCiAgbXV0YXRlKHNwZWNpZXM9Y2FzZV93aGVuKA0KICBpcy5uYShzcGVjaWVzKSB+ICJHQlMgbm90IGlzb2xhdGVkIiwNCiAgKHNwZWNpZXMhPSJiZXRhIEhhZW1vbHl0aWMgU3RyZXAsIGdyb3VwIEIiICYgc3BlY2llcyE9Ikdyb3VwIEIgU3RyZXB0b2NvY2N1cyIpIH4gIkdCUyBub3QgaXNvbGF0ZWQiLA0KICAoc3BlY2llcz09ImJldGEgSGFlbW9seXRpYyBTdHJlcCwgZ3JvdXAgQiIgfCBzcGVjaWVzPT0iR3JvdXAgQiBTdHJlcHRvY29jY3VzIikgfiAiR3JvdXAgQiBTdHJlcHRvY29jY3VzIiwNCiAgVFJVRSB+IHNwZWNpZXMNCikpIyBuZWVkIHRvIGFwcGx5IGFkZGl0aW9uYWwgZGF0ZSBjb252ZXJzaW9uIGFzIG5vdCBoYW5kbGVkIGluIGZsZXhpYmxlIHJlZm9ybWF0dGluZyBmdW5jdGlvbiBmb3IgZGF0ZXMgaW4gY2hhciBzdHJpbmcgIjE5LzA2LzE5OTEiIGluIFJveWFsIEJsYWNrYnVybiBMSU1TIGRhdGENCg0KIy8vLy8vLy8vLy8vLy8vLy8vLw0KIyBjb21iaW5lIGludG8gc2luZ2xlIGRmDQphbGxzaXRlcyA8LSByYmluZChhbGx0ZXN0MSxhbGx0ZXN0MixhbGx0ZXN0MyxhbGx0ZXN0NCxhbGx0ZXN0NSxhbGx0ZXN0NikNCmFsbHNpdGVzJG5oc25vIDwtIGFzLm51bWVyaWMoYWxsc2l0ZXMkbmhzbm8pDQpybShhbGx0ZXN0LGFsbHRlc3QxLGFsbHRlc3QyLGFsbHRlc3QzLGFsbHRlc3Q0LGFsbHRlc3Q1LGFsbHRlc3Q2KQ0KDQojIGFwcGx5IGV0aG5pY2l0eSBjYXRlZ29yaXphdGlvbiBmdW5jdGlvbiB0byBncm91cCBpbnRvIDUgbWFpbiBldGhuaWNpdGllcw0KYWxsc2l0ZXMgPC0gbGltcy5ldGhjYXQuZnVuKGFsbHNpdGVzKQ0KDQojIGNvbnZlcnQgaW5zdGFuY2VzIG9mIHNleD09IkZBTFNFIiBpbnRvICJVIg0KYWxsc2l0ZXMgPC0gYWxsc2l0ZXMgJT4lIG11dGF0ZShzZXggPSBjYXNlX3doZW4oDQogIHNleD09IkZBTFNFIiB+ICJVIiwNCiAgVFJVRSB+IHNleA0KKSkNCg0KDQojIE5PVEU6IENIRUNLIGF0IHRoaXMgc3RhZ2UgdGhhdCBkb2IgYW5kIHNwZWNkYXRlIGZpZWxkcyBoYXZlIGNvbnNpc3RlbnQgZGF0ZSBmb3JtYXR0aW5nIGFjcm9zcyBzaXRlcw0KDQpgYGANCg0KUHJvY2VzcyBMSU1TIGxpbmVsaXN0IHVzaW5nIGFkYXB0ZWQgdmVyc2lvbiBvZiBvYmFpIHNnc3NfaW5mZWN0aW9ucyANCg0KYGBge3IgbGltcyBkYXRhIHByb2Nlc3NpbmcsIGluY2x1ZGU9VFJVRX0NCg0KIyBSZW1vdmUgcXVhbGl0eSBjb250cm9sIHNhbXBsZXMNCmFsbHNpdGVzIDwtIHFjLnVtYnJlbGxhLmZ1bihhbGxzaXRlcykNCg0KIyBQcm9jZWVkIHRvIGRlZHB1cCBMSU1TIGxpbmVsaXN0IHRvIHBhdGllbnQtbGV2ZWwgd2l0aCBBTVIgcmVzdWx0cw0KYWxsc2l0ZXMgPC0gb2JhaS5kZWR1cC5mdW4oYWxsc2l0ZXMpDQoNCiMjIHBlcmZvcm0gaW5pdGlhbCBjaGVja3MNCg0KIyB0b3RhbCBlcGlzb2RlcyAoRUNNIHRlc3QgcmVzdWx0IHdpdGhpbiA5IG1vbnRoIHdpbmRvdykNCiAgbGVuZ3RoKHVuaXF1ZShhbGxzaXRlcyRlcGlkcykpICAjIDEyLDkxNg0KIyB0b3RhbCBwYXRpZW50cw0KICBsZW5ndGgodW5pcXVlKGFsbHNpdGVzJHBpZF9zdHIpKSMgMTIsOTE2DQojIHRvdGFsIHVuaXF1ZSBOSFMgbnVtYmVyIA0KICBsZW5ndGgodW5pcXVlKGFsbHNpdGVzJG5oc25vKSkgICMgMTIsNDQ1DQojIHRvdGFsIHJlY29yZHMgd2hlcmUgTkhTIG51bWJlciBtaXNzaW5nDQogIHN1bShpcy5uYShhbGxzaXRlcyRuaHNubykpICAgICAgIyA0NzINCiAgDQogICMgaXNvbGF0ZSBkdXBsaWNhdGVzIGluIG5oc25vIGZpZWxkDQogIGR1cGxpY2F0ZXNfb25seSA8LSBhbGxzaXRlcyAlPiUNCiAgZmlsdGVyKG5oc25vICVpbiUgbmhzbm9bZHVwbGljYXRlZChuaHNubyldKSAlPiUgYXJyYW5nZShmb3JlbmFtZSkNCiAgbnJvdyhkdXBsaWNhdGVzX29ubHkpICAgICAgICAgICAjIDQ3MiAtIGFsbCBvZiB3aGljaCBuaHNubyBtaXNzaW5nDQogIA0KICAjIGlkZW50aWZ5IHRydWUgKGxpa2VseSkgZHVwbGljYXRlcyB1c2luZyBwYXRpZW50IG5hbWUgZmllbGRzDQogIHRydWVfZHVwcyA8LSBkdXBsaWNhdGVzX29ubHkgJT4lIHNlbGVjdChzcGVjbm86c2l0ZSkgJT4lIG11dGF0ZShuYW1lX2NvbmNhdD1wYXN0ZShmb3JlbmFtZSwiXyIsc3VybmFtZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lX2NvbmNhdD1nc3ViKCIgIiwiIixuYW1lX2NvbmNhdCkpICU+JSANCiAgICAgZmlsdGVyKG5hbWVfY29uY2F0ICVpbiUgbmFtZV9jb25jYXRbZHVwbGljYXRlZChuYW1lX2NvbmNhdCldKSAlPiUgYXJyYW5nZShuYW1lX2NvbmNhdCkNCiAgbGVuZ3RoKHVuaXF1ZSh0cnVlX2R1cHMkbmFtZV9jb25jYXQpKSAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyA3IHRydWUgZHVwbGljYXRlcyAoYWNyb3NzIG5hbWUgZmllbGRzKSwgb2Ygd2hpY2ggMSAoIkZFQlVBUllfSVFBTUlDMjAyNCIpIGEgUUMgc2FtcGxlIGFuZCAxIHBlcnRhaW5zIHRvIDIgc2FtcGxlcyBhIHllYXIgYXBhcnQgZm9yIGEgcGF0aWVudA0KICANCiAgIyByZW1vdmUgcmVtYWluaW5nIFFDIHNhbXBsZSByb3dzDQogIGFsbHNpdGVzIDwtIGFsbHNpdGVzICU+JSBmaWx0ZXIoc3VybmFtZSE9IklRQU1JQzIwMjQiKQ0KDQogICMgLy8vLy8vLy8vLy8vIGtleSBjaGVja3BvaW50IDEgLy8vLy8vLy8vLy8vLy8NCiAgIyMgc2F2ZSBhcyBzdGF0aWMgYmFzZSBkYXRhc2V0IA0Kd3JpdGUuY3N2KGFsbHNpdGVzLCAiRGF0YV9jYXJyaWFnZS9MaW5lbGlzdC9zdGF0aWNfYmFzZWRhdGEuY3N2Iiwgcm93Lm5hbWVzPUYpICAjIFJFUExBQ0UgV0lUSCBTQVZFIFRPIFNFUlZFUg0KICANCmBgYA0KDQpQcmVwYXJlIHByb2Nlc3NlZCBMSU1TIGRhdGEgZm9yIERCUyB0cmFjaW5nDQoNCmBgYHtyIGRicyB0cmFjZSBwcmVwLCBpbmNsdWRlPVRSVUV9DQoNCiMjIGxvYWQgc3RhdGljIGJhc2UgbGluZWxpc3QNCmFsbHNpdGVzIDwtIHJlYWQuY3N2KCJEYXRhX2NhcnJpYWdlL0xpbmVsaXN0L3N0YXRpY19iYXNlZGF0YS5jc3YiKQ0KYWxsc2l0ZXMkc3BlY2RhdGUgPC0gYXMuRGF0ZShhbGxzaXRlcyRzcGVjZGF0ZSwgZm9ybWF0ID0gIiVZLSVtLSVkIikNCmFsbHNpdGVzJGRvYiA8LSBhcy5EYXRlKGFsbHNpdGVzJGRvYiwgZm9ybWF0ID0gIiVZLSVtLSVkIikNCmFsbHNpdGVzJHBpZF9zdHIgPC0gYXMuY2hhcmFjdGVyKGFsbHNpdGVzJHBpZF9zdHIpDQoNCmRic3ByZXAgPC0gYWxsc2l0ZXMgJT4lIG11dGF0ZSh5ZWFyID0gZm9ybWF0KHNwZWNkYXRlLCAiJVkiKSkgJT4lIHNlbGVjdCh5ZWFyLCBldmVyeXRoaW5nKCkpICU+JSBzZWxlY3QoMTptYXRjaCgiZXBpZHMiLG5hbWVzKGFsbHNpdGVzKSkpDQoNCg0KIyBnZW5lcmF0ZSAuY3N2IGZpbGVzIGZvciBzZW5kaW5nIHRvIERCUyB0cmFjZSB0ZWFtIChnZW5lcmF0ZXMgdHdvIGZpbGVzLCBvbmUgd2l0aCBzdWZmaWNpZW50IFBJSSBhbmQgb25lIHdoZXJlIHNvbWUvYWxsIFBJSSBtaXNzaW5nLiBTZW5kIEJPVEggZm9yIHRyYWNpbmcpDQpkYnMudHJhY2UucHJlcC5mdW4oZGJzcHJlcCwgDQogICAgICAgICAgICAgICAgICAgbWFpbmZpbGU9IkRhdGFfY2FycmlhZ2UvREJTX3RyYWNpbmcvTElNU19saW5lbGlzdF9mb3JfdHJhY2luZy5jc3YiLCANCiAgICAgICAgICAgICAgICAgICBleGNsdWRlZmlsZT0iRGF0YV9jYXJyaWFnZS9EQlNfdHJhY2luZy9leGNsdWRlZF9yZWNvcmRzX2Zvcl90cmFjaW5nLmNzdiIpDQojIE5PVEU6IGFsd2F5cyBvcGVuIC5jdnMgZmlsZXMgZm9yIHRyYWNpbmcgYW5kIGNoZWNrIHRoYXQgY29sdW1ucyBmb3JtYXR0ZWQgY29ycmVjdGx5LCBwcmlvciB0byBzZW5kaW5nIHRvIERCUyB0ZWFtDQoNCiMgIyBzdGF0ZSBOSFMgbnVtYmVyIGNvbXBsZXRpb24gYmVmb3JlIERCUyB0cmFjaW5nOiBwZXJjZW50IG5oc25vIG1pc3NpbmcNCnN1bV9uYV9uaHMgPC0gc3VtKGlzLm5hKGRic3ByZXAkbmhzbm8pKS9ucm93KGRic3ByZXApKjEwMCAgICMgMy42JQ0KbmFfbmhzIDwtIGRic3ByZXAgJT4lIGZpbHRlcihpcy5uYShuaHNubykpICAgICAgICAgICAgICAgICAgIyBnZW5lcmF0ZSBkZiBvZiByZWNvcmRzIHdoZXJlIE5IUyBudW1iZXIgbWlzc2luZw0KDQpgYGANCg0KSW50ZWdyYXRlIFBJSS1lbnJpY2hlZCBEQlMgdHJhY2UgcmV0dXJuIGludG8gbGluZWxpc3QgYW5kIHJldGFpbiByZWNvcmRzIHdoaWNoIGRpZG4ndCBoYXZlIHN1Y2Nlc3NmdWwgdHJhY2UgZmlyc3QgdGltZSBhcm91bmQgZm9yIHNlY29uZCBhdHRlbXB0DQoNCmBgYHtyIGRicyB0cmFjZSByZXR1cm4sIGluY2x1ZGU9VFJVRX0NCg0KIyMgaW1wb3J0IGFuZCBmb3JtYXQgdHJhY2UgcmV0dXJuIGNzdiBmaWxlcw0KDQojIFRoZXNlIGFyZSBzYXZlZCBpbml0aWFsbHkgaW4gJ3Jlc3BvbnNlIGhvbGRpbmcgYmF5JyBzdWJmb2xkZXIgb2YgJ0RCU190cmFjaW5nJyB3aXRoIG9yaWdpbmFsIERCUyBmaWxlIG5hbWVzIHRoYXQgaGF2ZSBsb25nIG51bWVyaWMgc3VmZml4ZXMuIFJlbmFtZSwgc2F2ZSBpbiBtYWluICdEQlNfdHJhY2luZycgZm9sZGVyIChvdmVyd3JpdGluZyBwcmV2aW91cyB2ZXJzaW9ucyBmcm9tIHByaW9yIHJ1bnMgb2YgcHJvY2VzcyB3aGljaCBzaG91bGQgZmlyc3QgYmUgbW92ZWQgdG8gQXJjaGl2ZSkgYW5kIGltcG9ydDoNCg0Kc2V0d2Qod2QpDQpsaW1zX3RyYWNlX3JldHVybiA8LSByZWFkLmNzdigiRGF0YV9jYXJyaWFnZS9EQlNfdHJhY2luZy9MSU1TX2xpbmVsaXN0X3Jlc3BvbnNlLmNzdiIsIGhlYWRlciA9IEYpICAgICAgICAgICAgICAgICAjIHVwZGF0ZSBpbnB1dCBmaWxlIG5hbWUgYXMgbmVlZGVkDQpsaW1zX2V4Y2xfdHJhY2VfcmV0dXJuIDwtIHJlYWQuY3N2KCJEYXRhX2NhcnJpYWdlL0RCU190cmFjaW5nL2V4Y2x1ZGVkX3JlY29yZHNfcmVzcG9uc2UuY3N2IiwgaGVhZGVyID0gRikNCg0KIyBjb25maXJtIHRvdGFsIG51bWJlciBvZiByZWNvcmRzIHJldHVybmVkDQpzdW0obnJvdyhsaW1zX3RyYWNlX3JldHVybikrbnJvdyhsaW1zX2V4Y2xfdHJhY2VfcmV0dXJuKSkgICAgIyAxMiw5MTcNCiAgICANCmxpbXNfdHJhY2VfcmV0dXJuIDwtIHJiaW5kKGxpbXNfdHJhY2VfcmV0dXJuLGxpbXNfZXhjbF90cmFjZV9yZXR1cm4pICU+JSBmaWx0ZXIoIWdyZXBsKCJeMDAxMSIsVjEpICYgIWdyZXBsKCJeOTkxMSIsVjEpKQ0KDQojIHJldGFpbiByZWNvcmRzIHdoaWNoIE5PVCBzdWNjZXNzZnVsbHkgdHJhY2VkIGZvciBzdWJzZXF1ZW50IFFDIGFuZCByZS10cmFjaW5nDQpub190cmFjZSA8LSBuby50cmFjZS53cmFwLmZ1bihsaW1zX3RyYWNlX3JldHVybikNCg0KIyByZXRhaW4gcmVjb3JkcyB3aGVyZSBzZXggdW5rbm93biAobWF5IG92ZXJsYXAgd2l0aCBub190cmFjZSBnZW5lcmF0ZWQgYWJvdmUpDQpzZXhfdW5rbm93biA8LSB1bmtub3duLnNleC53cmFwLmZ1bihsaW1zX3RyYWNlX3JldHVybikNCg0KIyBrZWVwIE9OTFkgcmVjb3JkcyB3aGljaCBzdWNjZXNzZnVsbHkgdHJhY2VkIGluIGZpcnN0IHJvdW5kIG9mIHRyYWNpbmcgYW5kIG1lcmdlIHdpdGggTElNUyBsaW5lbGlzdCANCmFsbHNpdGVzIDwtIHRyYWNlZC53cmFwLmZ1bihsaW1zX3RyYWNlX3JldHVybikgICAgICAgICAgICAgICAjIDEyLDkxMw0KDQojIHN0YXRlIE5IUyBudW1iZXIgY29tcGxldGlvbiBhZnRlciBEQlMgdHJhY2luZw0Kc3VtKGlzLm5hKGFsbHNpdGVzJG5oc25vKSkNCiMgMjYwIE5IUyBudW1iZXIgbWlzc2luZw0KDQpgYGANCg0KUHJlcGFyZSBzdWJzZXRzIG9mIGRhdGEgZm9yIHNlY29uZCByb3VuZCBvZiBEQlMgdHJhY2luZw0KDQpgYGB7ciBzZWNvbmQgZGJzIHRyYWNlIHByZXAsIGluY2x1ZGU9VFJVRX0NCg0KZGJzcHJlcDIgPC0gYWxsc2l0ZXMgJT4lIG11dGF0ZSh5ZWFyID0gYXMubnVtZXJpYyhmb3JtYXQoc3BlY2RhdGUsICclWScpKSkgJT4lIHNlbGVjdCh5ZWFyLCBldmVyeXRoaW5nKCkpICU+JSBzZWxlY3QoMTptYXRjaCgiZXBpZHMiLCBuYW1lcyhhbGxzaXRlcykpKSAlPiUgZmlsdGVyKHBpZF9zdHIgJWluJSBub190cmFjZSRwaWRfc3RyIHwgcGlkX3N0ciAlaW4lIHNleF91bmtub3duJHBpZF9zdHIpICU+JSANCiAgbXV0YXRlKGZvcmVuYW1lID0gc3ViKCIgLioiLCAiIiwgZm9yZW5hbWUpKSAlPiUgICAjIHJlZm9ybWF0IG5hbWUgZmllbGRzIGluIG5vX3RyYWNlIHRvIHJlc2VuZCBmb3IgdHJhY2luZyAod2hlcmUgbWlkZGxlIG5hbWVzIG5vdCBjb21wYXRpYmxlIHdpdGggREJTIHRyYWNlIGFsZ29yaXRobSkNCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbiggICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRha2UgYWxsIHJlY29yZHMgd2hlcmUgc2V4PT0iVSIgYW5kIHByZXN1bXB0aXZlbHkgcmVjb2RlIHRvICJGIiB0byByZXNlbmQgZm9yIHRyYWNpbmcgKGlmIHRyYWNlIHJldHVybnMgc2V4ID09IDIgdGhlbiBjb25maWRlbnQgdGhhdCBwZXJ0YWlucyB0byBmZW1hbGVzKQ0KICAgIHNleCA9PSAiVSIgfiAiRiIsDQogICAgVFJVRSB+IHNleA0KICApKQ0KDQojIGdlbmVyYXRlIC5jc3YgZmlsZXMgZm9yIHNlbmRpbmcgdG8gREJTIHRyYWNlIHRlYW0gZm9yIHNlY29uZCB0cmFjZSAoZ2VuZXJhdGVzIHR3byBmaWxlcywgb25lIHdpdGggc3VmZmljaWVudCBQSUkgYW5kIG9uZSB3aGVyZSBzb21lL2FsbCBQSUkgbWlzc2luZy4gU2VuZCBCT1RIIGZvciB0cmFjaW5nKQ0KZGJzLnRyYWNlLnByZXAuZnVuKGRic3ByZXAyLCANCiAgICAgICAgICAgICAgICAgICBtYWluZmlsZT0iRGF0YV9jYXJyaWFnZS9EQlNfdHJhY2luZy9MSU1TX2xpbmVsaXN0X2Zvcl90cmFjaW5nX3JuZDIuY3N2IiwgDQogICAgICAgICAgICAgICAgICAgZXhjbHVkZWZpbGU9IkRhdGFfY2FycmlhZ2UvREJTX3RyYWNpbmcvZXhjbHVkZWRfcmVjb3Jkc19mb3JfdHJhY2luZ19ybmQyLmNzdiIpICAgDQoNCmBgYA0KDQpJbXBvcnQgc2Vjb25kIHNldCBvZiBEQlMgdHJhY2luZyByZXN1bHRzDQoNCmBgYHtyIHNlY29uZCBkYnMgdHJhY2UgcmVzdWx0LCBpbmNsdWRlPVRSVUV9DQoNCiMvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8NCiNURU1QT1JBUlkgQ09ERSBDSFVOSw0KICAjIHJldmlldyBjb250ZW50IG9mIGxhdGVzdCBmaWxlcyBzZW50IGZvciBEQlMgdHJhY2luZyAoSmFuIDIwMjUpDQp0ZXN0IDwtIHJlYWQuY3N2KCJEYXRhX2NhcnJpYWdlL0RCU190cmFjaW5nL0xJTVNfbGluZWxpc3RfZm9yX3RyYWNpbmdfcm5kMi5jc3YiLCBoZWFkZXIgPSBUKQ0KdGVzdDIgPC0gcmVhZC5jc3YoIkRhdGFfY2FycmlhZ2UvREJTX3RyYWNpbmcvZXhjbHVkZWRfcmVjb3Jkc19mb3JfdHJhY2luZ19ybmQyLmNzdiIsIGhlYWRlciA9IFQpDQp0ZXN0IDwtIHJiaW5kKHRlc3QsdGVzdDIpDQpzaXRlcyA8LSBhbGxzaXRlcyAlPiUgZmlsdGVyKG5oc25vICVpbiUgdW5pcXVlKHRlc3QkTkhTX251bWJlcikpDQp1bmlxdWUoc2l0ZXMkc2l0ZSkNCiMgaGFzIGRhdGEgZm9yIGFsbCA2IHNpdGVzDQojIHBlcmNlbnQgb2YgdG90YWwgcmVjb3JkcyBpbiBsaW5lbGlzdCB3aGljaCBpbiBzZWNvbmQgdHJhY2UgcmVxdWVzdCBmaWxlcw0KcGVyYyA8LSBucm93KHNpdGVzKS9ucm93KGFsbHNpdGVzKSoxMDANCiMgMTkuMyUNCiMgY3VycmVudCBwZXJjZW50IHJlY29yZHMgbWlzc2luZyBuaHNubywgcHJpb3IgdG8gbGlua2luZyBiYWNrIGluIHNlY29uZCB0cmFjZSByZXR1cm4NCnBlcmMgPC0gKHN1bShpcy5uYShhbGxzaXRlcyRuaHNubykpL25yb3coYWxsc2l0ZXMpKSoxMDANCnByaW50KHBlcmMpDQojIDMuNiUNCiMvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8NCg0KIyBpbXBvcnQgYW5kIGZvcm1hdCB0cmFjZSByZXR1cm4gY3N2IGZpbGVzDQpzZXR3ZCh3ZCkNCg0KbGltc190cmFjZV9yZXR1cm4yIDwtIHJlYWQuY3N2KCJEYXRhX2NhcnJpYWdlL0RCU190cmFjaW5nL0xJTVNfbGluZWxpc3RfcmVzcG9uc2UyLmNzdiIsIGhlYWRlciA9IEYpICAgICAgICAgICAgICAgICAjIHVwZGF0ZSBpbnB1dCBmaWxlIG5hbWUgYXMgbmVlZGVkDQpsaW1zX2V4Y2xfdHJhY2VfcmV0dXJuMiA8LSByZWFkLmNzdigiRGF0YV9jYXJyaWFnZS9EQlNfdHJhY2luZy9leGNsdWRlZF9yZWNvcmRzX3Jlc3BvbnNlMi5jc3YiLCBoZWFkZXIgPSBGKSAgDQoNCmxpbXNfdHJhY2VfcmV0dXJuMiA8LSByYmluZChsaW1zX3RyYWNlX3JldHVybjIsbGltc19leGNsX3RyYWNlX3JldHVybjIpICAgICMgY29tYmluZSBpbnRvIHNpbmdsZSBkZiBvbmNlIGNoZWNrZWQgdGhhdCBub24tb3ZlcmxhcHBpbmcgcGF0aWVudCBJRHMNCg0KIyByZXRhaW4gcmVjb3JkcyB3aGljaCBOT1Qgc3VjY2Vzc2Z1bGx5IHRyYWNlZCBmb3Igc3Vic2VxdWVudCBRQyBhbmQgcmUtdHJhY2luZw0Kbm9fdHJhY2UyIDwtIG5vLnRyYWNlLndyYXAuZnVuKGxpbXNfdHJhY2VfcmV0dXJuMikNCg0KIyBrZWVwIE9OTFkgcmVjb3JkcyB3aGljaCBzdWNjZXNzZnVsbHkgdHJhY2VkIGluIHNlY29uZCByb3VuZCBvZiB0cmFjaW5nIGFuZCBtZXJnZSB3aXRoIExJTVMgbGluZWxpc3QNCmFsbHNpdGVzJHBpZF9zdHIgPC0gYXMuaW50ZWdlcihhbGxzaXRlcyRwaWRfc3RyKSAgIyBlbnN1cmUgcGlkX3N0ciBjb2x1bW4gaW4gYm90aCBkYXRhc2V0cyB1c2VkIGZvciBtZXJnZSBpbiBzYW1lIGZvcm1hdA0KYWxsc2l0ZXMgPC0gdHJhY2VkLndyYXAuZnVuKGxpbXNfdHJhY2VfcmV0dXJuMikgICAgIyBtZXJnZSB0aGVzZSByZWNvcmRzIGJhY2sgaW50byBtYWluIGxpbmVsaXN0DQoNCiMgY3VycmVudCBwZXJjZW50IHJlY29yZHMgbWlzc2luZyBuaHNubywgYWZ0ZXIgbGlua2luZyBiYWNrIGluIHNlY29uZCB0cmFjZSByZXR1cm4NCnBlcmMgPC0gKHN1bShpcy5uYShhbGxzaXRlcyRuaHNubykpL25yb3coYWxsc2l0ZXMpKSoxMDANCnByaW50KHBlcmMpDQojIDIuMDElDQoNCiAgIyAvLy8vLy8vLy8vLy8ga2V5IGNoZWNrcG9pbnQgMiAvLy8vLy8vLy8vLy8vLw0KICAjIyBzYXZlIGFzIHN0YXRpYyBiYXNlIGRhdGFzZXQgKiphZnRlciB0cmFjaW5nKioNCndyaXRlLmNzdihhbGxzaXRlcywgIkRhdGFfY2FycmlhZ2UvTGluZWxpc3Qvc3RhdGljX2Jhc2VkYXRhX3RyYWNlZC5jc3YiLCByb3cubmFtZXM9RikNCg0KDQpgYGANCg0KUHJlbGltaW5hcnkgYW5hbHlzaXMgcmVzdWx0cyBwcmVwIGFuZCBwcmVzZW50YXRpb24NCg0KYGBge3IgcHJlbGltIHJlc3VsdHMgcHJlcCwgaW5jbHVkZT1UUlVFfQ0KDQpsaW5lbGlzdCA8LSByZWFkLmNzdigiRGF0YV9jYXJyaWFnZS9MaW5lbGlzdC9zdGF0aWNfYmFzZWRhdGFfdHJhY2VkLmNzdiIpDQogIGxpbmVsaXN0JHNwZWNkYXRlIDwtIGFzLkRhdGUobGluZWxpc3Qkc3BlY2RhdGUsIGZvcm1hdCA9ICIlWS0lbS0lZCIpDQogIGxpbmVsaXN0JGRvYiA8LSBhcy5EYXRlKGxpbmVsaXN0JGRvYiwgZm9ybWF0ID0gIiVZLSVtLSVkIikNCiAgDQoNCmxpbmVsaXN0IDwtIGxpbmVsaXN0ICU+JQ0KICBtdXRhdGUoeWVhciA9IGFzLm51bWVyaWMoZm9ybWF0KHNwZWNkYXRlLCAnJVknKSkpICU+JQ0KICBzZWxlY3QoeWVhciwgZXZlcnl0aGluZygpKSAlPiUgc2VsZWN0KC1hbnlfb2YoYygiVjEiKSkpICU+JSANCiAgZmlsdGVyKCFncmVwbCgiSVFBTSIsIHN1cm5hbWUpKSAlPiUgZmlsdGVyKHNleCE9Ik0iKSAlPiUNCiAgbXV0YXRlKHltID0gZm9ybWF0KHNwZWNkYXRlLCAiJVkvJW0iKSkgJT4lIA0KICBtdXRhdGUoeW1fZGF0ZSA9IGFzLkRhdGUocGFzdGUwKHltLCAiLzAxIiksIGZvcm1hdCA9ICIlWS8lbS8lZCIpKQ0KDQoNCiMgLy8vLy8vLy8vLy8vLw0KIyAxKSBtb250aGx5IEVDTSB0ZXN0IHRvdGFscyBieSBzaXRlDQoNCiMgU3VtbWFyaXplIHRvdGFscyBieSB5ZWFyLW1vbnRoIGFuZCBzaXRlDQptb250aGx5X3Rlc3QgPC0gbGluZWxpc3QgJT4lIGdyb3VwX2J5KHltX2RhdGUsc2l0ZSkgJT4lIHN1bW1hcml6ZSh0b3RhbF90ZXN0ZWQ9c3VtKHopLCAuZ3JvdXBzPSAnZHJvcCcpIA0KDQojIENyZWF0ZSBhIGNvbXBsZXRlIHNldCBvZiB5ZWFyLW1vbnRoIGFuZCBzaXRlIGNvbWJpbmF0aW9ucw0KbW9udGhfY29tcGxldGUgPC0gbW9udGhseV90ZXN0ICU+JSBjb21wbGV0ZSh5bV9kYXRlLHNpdGUpDQoNCiMgSm9pbiB0aGUgc3VtbWFyaXplZCBkYXRhLCBhbGxvd2luZyBmb3IgTkEgaW4gbWlzc2luZyBjb21iaW5hdGlvbnMNCm1vbnRoX2NvbXBsZXRlIDwtIG1vbnRoX2NvbXBsZXRlICU+JQ0KICBsZWZ0X2pvaW4obW9udGhseV90ZXN0LCBieSA9IGMoInltX2RhdGUiLCAic2l0ZSIpKSAlPiUNCiAgbXV0YXRlKHRvdGFsX3Rlc3RlZCA9IGlmZWxzZShpcy5uYSh0b3RhbF90ZXN0ZWQueSksIE5BLCB0b3RhbF90ZXN0ZWQueSkpICU+JQ0KICBzZWxlY3QoeW1fZGF0ZSwgc2l0ZSwgdG90YWxfdGVzdGVkKSAlPiUgbXV0YXRlKHRvdGFsX3Rlc3RlZCA9IGNhc2Vfd2hlbigNCiAgICB0b3RhbF90ZXN0ZWQ8PTMwIH4gTkEsDQogICAgVFJVRSB+IHRvdGFsX3Rlc3RlZCkNCiAgKQ0KDQojIHBsb3Qgb2YgbW9udGhseSB0ZXN0IG51bWJlcnMgYnkgc2l0ZSANCnBsb3QgPC0gZ2dwbG90KG1vbnRoX2NvbXBsZXRlLCBhZXMoeCA9IHltX2RhdGUsIHkgPSB0b3RhbF90ZXN0ZWQsIGNvbG9yID0gc2l0ZSwgZ3JvdXAgPSBzaXRlKSkgKw0KIGdlb21fbGluZShzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlYiAlWSIsIGRhdGVfYnJlYWtzID0gIjEgbW9udGgiKSArICAjIEZvcm1hdCB4LWF4aXMNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBFQ00gdGVzdHMgYnkgWWVhci1Nb250aCBhbmQgU2l0ZSIsIA0KICAgICAgIHggPSAiWWVhci1Nb250aCIsIHkgPSAiVG90YWwgVmFsdWUiLCBjb2xvciA9ICJTaXRlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC41KSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0PSAwLjUpKQ0KDQojIHNhdmUgcGxvdA0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UveW1fZWNtX3Rlc3RfYnlzaXRlLmpwZWciLCBwbG90ID0gcGxvdCwgZGV2aWNlID0gImpwZWciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkNCg0KDQojIG5vdyBsb29raW5nIDEgc2l0ZSBhdCBhIHRpbWUsIGNvbXBhcmluZyBtb250aGx5IHRlc3QgbnVtYmVycyBpbiBsaW5lbGlzdCB2cyBvcmlnaW5hbCBFQ00gdGVzdCBudW1iZXJzIGZyb20gR0JTMw0KDQoNCiMgU3VtbWFyaXplIHRvdGFscyBhY3Jvc3Mgd2hvbGUgcGVyaW9kIGZvciBlYWNoIHNpdGUNCnRlc3RfYnlzaXRlIDwtIGxpbmVsaXN0ICU+JSBncm91cF9ieShzaXRlKSAlPiUgc3VtbWFyaXplKHRvdGFsX3Rlc3RlZD1zdW0oeiksIC5ncm91cHM9ICdkcm9wJykNCg0KIyBDcmVhdGUgYSBiYXIgZ3JhcGggb2YgdG90YWxzIGJ5IHNpdGUNCnBsb3QgPC0gZ2dwbG90KHRlc3RfYnlzaXRlLCBhZXMoeCA9IHNpdGUsIHkgPSB0b3RhbF90ZXN0ZWQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInNreWJsdWUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBFQ00gdGVzdHMgYnkgU2l0ZSIsIHggPSAiU2l0ZSIsIHkgPSAiVG90YWxzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCAgIyBJbmNyZWFzZSBzaXplIG9mIHgtYXhpcyBsYWJlbHMNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLCAgIyBJbmNyZWFzZSBzaXplIG9mIHktYXhpcyBsYWJlbHMNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB4LWF4aXMgdGl0bGUNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSAgICMgSW5jcmVhc2Ugc2l6ZSBvZiB5LWF4aXMgdGl0bGUNCiAgKQ0KDQojIHNhdmUgcGxvdA0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvYWxsX2VjbV90ZXN0X2J5c2l0ZS5qcGVnIiwgcGxvdCA9IHBsb3QsIGRldmljZSA9ICJqcGVnIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDApDQoNCg0KIyAvLy8vLy8vLy8vLy8vDQojIDIpIGFnZSBkaXN0cmlidXRpb25zIGJ5IHNpdGUgYW5kIGZvciBhbGwgc2l0ZXMgY29tYmluZWQNCmFnZWRpcyA8LSBsaW5lbGlzdCAlPiUgZ3JvdXBfYnkoc2l0ZSkgJT4lIA0KICBzdW1tYXJpc2UoDQogICAgbWVkaWFuX2FnZT1tZWRpYW4oYWdlLCBuYS5ybT1UKSwNCiAgICBJUVJfYWdlPUlRUihhZ2UsIG5hLnJtPVQpLA0KICAgIGFnZV9taW49bWluKGFnZSwgbmEucm09VCksDQogICAgYWdlX21heD1tYXgoYWdlLCBuYS5ybT1UKSwNCiAgICBRMSA9IHF1YW50aWxlKGFnZSwgMC4yNSwgbmEucm09VCksDQogICAgUTMgPSBxdWFudGlsZShhZ2UsIDAuNzUsIG5hLnJtPVQpDQogICkNCg0KIyBDcmVhdGUgYSBwbG90IHNob3dpbmcgbWVkaWFuIGFuZCBJUVJzDQpwbG90IDwtIGdncGxvdChhZ2VkaXMsIGFlcyh4ID0gc2l0ZSwgeSA9IG1lZGlhbl9hZ2UpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGNvbG9yID0gImJsdWUiKSArICAjIFBsb3QgdGhlIG1lZGlhbiBhZ2UgYXMgcG9pbnRzDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBRMSwgeW1heCA9IFEzKSwgd2lkdGggPSAwLjIsIGNvbG9yID0gInJlZCIpICsgICMgQWRkIElRUiBhcyBlcnJvciBiYXJzDQogIGxhYnModGl0bGUgPSAiTWVkaWFuIEFnZSBhbmQgSVFScyBieSBDYXRlZ29yeSIsIHggPSAiQ2F0ZWdvcnkiLCB5ID0gIk1lZGlhbiBBZ2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojIHNhdmUgcGxvdA0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvbWVkaWFuX2FnZV9ieXNpdGUuanBlZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSAianBlZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQ0KDQojIHNhdmUgdGFibGUNCndyaXRlLmNzdihhZ2VkaXMsICJEYXRhX2NhcnJpYWdlL0VDTV9jYXJyaWFnZS9TdGF0X3N1bW1hcmllcy9BZ2VfZGlzdHJpYnV0aW9uL2FnZV9kaXMuY3N2IikNCg0KYGBgDQoNCkFwcGx5IGdlbyAtIElNRCBsb29rdXAgdG8gbGluayB0byBJTUQNCg0KYGBge3IgaW1kIGxpbmthZ2UsIGluY2x1ZGU9VFJVRX0NCg0KIyBhcHBseSBJTUQgbGlua2FnZSBmdW5jdGlvbiB0byBsaW5lbGlzdCB3aXRob3V0IEFNUiBkYXRhICh0byBiZSBsaW5rZWQgYmFjayBpbiBsYXRlcikNCmxpbmVsaXN0IDwtIGltZC5saW5rYWdlLmZ1bihsaW5lbGlzdCkNCg0KICB3cml0ZS5jc3YobGluZWxpc3QsIHBhc3RlMCh3ZCwiL0RhdGFfY2FycmlhZ2UvTGluZWxpc3Qvc3RhdGljX2Jhc2VkYXRhX2ltZGxpbmtlZF9yYXcuY3N2IiksIHJvdy5uYW1lcz1GKQ0KDQogIGxpbmVsaXN0IDwtIHJlYWQuY3N2KCJEYXRhX2NhcnJpYWdlL0xpbmVsaXN0L3N0YXRpY19iYXNlZGF0YV9pbWRsaW5rZWRfcmF3LmNzdiIpDQogIGxpbmVsaXN0IDwtIGxpbmVsaXN0ICU+JQ0KICBtdXRhdGUoDQogICAgc3BlY2RhdGUgPSBhcy5EYXRlKHNwZWNkYXRlLCBmb3JtYXQgPSAiJVktJW0tJWQiKSwNCiAgICBkb2IgPSBhcy5EYXRlKGRvYiwgZm9ybWF0ID0gIiVZLSVtLSVkIikpDQoNCmBgYA0KDQpIRVMgbGlua2FnZSBmb3IgZXRobmljaXR5IGVucmljaG1lbnQNCg0KYGBge3IgaGVzIGV0aG4gbGlua2FnZSwgaW5jbHVkZT1UUlVFfQ0KDQpsaW5lbGlzdCA8LSBsaW5lbGlzdC5wcmVwLmZ1bihsaW5lbGlzdCkNCmxpbmVsaXN0IDwtIGhlcy5ldGhubGluay5mdW4obGluZWxpc3QpDQoNCiMgLy8vLy8vLy8vLy8vIGtleSBjaGVja3BvaW50IDNhIC8vLy8vLy8vLy8vLy8vDQogICMjIHNhdmUgYXMgc3RhdGljIGJhc2UgZGF0YXNldCAqKmFmdGVyIEhFUyBDSElNRSBldGhuaWNpdHkgZW5yaWNobWVudCoNCiAgd3JpdGUuY3N2KGxpbmVsaXN0LCAiRGF0YV9jYXJyaWFnZS9MaW5lbGlzdC9zdGF0aWNfYmFzZWRhdGFfZXRobmxpbmtlZF9yYXcuY3N2Iiwgcm93Lm5hbWVzPUYpDQogIA0KICBsaW5lbGlzdCA8LSByZWFkLmNzdigiRGF0YV9jYXJyaWFnZS9MaW5lbGlzdC9zdGF0aWNfYmFzZWRhdGFfZXRobmxpbmtlZF9yYXcuY3N2IikNCiAgbGluZWxpc3Qkc3BlY2RhdGUgPC0gYXMuRGF0ZShsaW5lbGlzdCRzcGVjZGF0ZSwgZm9ybWF0ID0gIiVZLSVtLSVkIikNCiAgbGluZWxpc3QkZG9iIDwtIGFzLkRhdGUobGluZWxpc3QkZG9iLCBmb3JtYXQgPSAiJVktJW0tJWQiKQ0KICANCiAgIyBxdWFudGlmeSBldGhuaWNpdHkgY2F0ZWdvcmllcyAobnVtYmVycyBhbmQgJSB0b3RhbCBpbiBsaW5lbGlzdCwgaW5jbHVkaW5nIG5vdCBsaW5rZWQgLSBOQSkNCiAgaGVzX2V0aF9hbGxjYXQgPC0gbGluZWxpc3QgJT4lIG11dGF0ZSh6PTEpICU+JSBncm91cF9ieShFdGhuaWNfZ3JvdXApICU+JSBzdW1tYXJpemUodG90YWw9c3VtKHopKSAlPiUgbXV0YXRlKHBjdD10b3RhbC9zdW0odG90YWwpKjEwMCkNCiAgIyAlIG5vdCBsaW5rZWQgPSAyLjUlIChkZW5vbSA9IGFsbCByZWNvcmRzKQ0KICANCiAgIyB0byBkb2N1bWVudCAlIG5vdCBsaW5rZWQgZnJvbSBDSElNRSBtZXRob2QsIGFzIHdlbGwgYXMgb2YgdGhvc2UgbGlua2VkLCB0aGUgJSB3aGljaCBub3Qga25vd24gb3Igbm90IHN0YXRlZA0KICBoZXNfbGlua2VkX29ubHkgPC0gbGluZWxpc3QgJT4lIGZpbHRlcighaXMubmEoRXRobmljX2dyb3VwKSkgJT4lIG11dGF0ZSh6PTEpICU+JSBncm91cF9ieShFdGhuaWNfZ3JvdXApICU+JSBzdW1tYXJpemUodG90YWw9c3VtKHopKSAlPiUgbXV0YXRlKHBjdD10b3RhbC9zdW0odG90YWwpKjEwMCkNCiAgIyAlIG5vdCBrbm93biA9IDAuNjcNCiAgIyAlIG5vdCBzdGF0ZXMgPSAxLjYzIChkZW5vbSA9IGFsbCBIRVMtbGlua2VkIHJlY29yZHMpDQogIA0KIyBpZiBIRVMgcmV0dXJuZWQgdW5rbm93biwgbm90IGxpbmtlZCwgbm90IHN0YXRlZCB0aGVuIHBhc3MgdG8gRUNEUyBtZXRob2QgdG8gZmlsbCBpbiBnYXBzLiBMb2cgdGhhdCBFQ0RTIGRhdGEgc291cmNlDQpmb3JfZWNkcyA8LSBsaW5lbGlzdCAlPiUgZmlsdGVyKEV0aG5pY19ncm91cCA9PSAiOTkgTm90IEtub3duIiB8IEV0aG5pY19ncm91cCA9PSAiWiBOb3Qgc3RhdGVkIiB8IGlzLm5hKEV0aG5pY19ncm91cCkpICU+JSBtdXRhdGUobmhzbm9fc3RhdHVzPWNhc2Vfd2hlbihpcy5uYShuaHNubykgfiAibWlzc2luZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKG5oc25vKSB+ICJrbm93biIpKQ0KIyA1Mjggcm93cyAoNC4xJSBvZiBsaW5lbGlzdCkNCg0KIyB0YWJ1bGF0ZSBIRVMgZXRobmljaXR5IGFzc2lnbm1lbnQgYnkgTkhTIG51bWJlciBzdGF0dXMgKGtub3duIG9yIG1pc3NpbmcpDQp0YWIgPC0gZm9yX2VjZHMgJT4lIG11dGF0ZSh6PTEpICU+JSBncm91cF9ieShuaHNub19zdGF0dXMsRXRobmljX2dyb3VwKSAlPiUgc3VtbWFyaXplKHRvdGFsPXN1bSh6KSkgJT4lIG11dGF0ZShwY3Q9dG90YWwvc3VtKHRvdGFsKSoxMDApDQojIGFtb25nIGtub3duIG5oc25vLCAyMDUgKDc2LjIlKSB3ZXJlIEhFUyBsaW5rZWQgYnV0IGFzc2lnbmVkIGFzICJldGhuaWNpdHkgbm90IHN0YXRlZCI7IGFsbCByZWNvcmRzIG1pc3Npbmcgbmhzbm8gd2VyZSBub3QgbGlua2VkIHRvIEhFUw0Kd3JpdGUuY3N2KHRhYiwgIkRhdGFfY2FycmlhZ2UvRUNNX2NhcnJpYWdlL1N0YXRfc3VtbWFyaWVzL2hlc19ub2V0aG5fYnlfbmhzbm9fc3RhdHVzLmNzdiIsIHJvdy5uYW1lcz1GKQ0KDQplY2RzX2xpbmtlZCA8LSBsaW5lbGlzdC5wcmVwLmZ1bihmb3JfZWNkcykNCmVjZHNfbGlua2VkIDwtIGVjZHMuZXRobmxpbmsuZnVuKGVjZHNfbGlua2VkKQ0KDQojIC8vLy8vLy8vLy8vLyBrZXkgY2hlY2twb2ludCAzYiAvLy8vLy8vLy8vLy8vLw0KICAjIyBzYXZlIHN1YnNldCBvZiBFQ0RTLWxpbmtlZCBsaW5lbGlzdA0Kd3JpdGUuY3N2KGVjZHNfbGlua2VkLCAiRGF0YV9jYXJyaWFnZS9MaW5lbGlzdC9lY2RzX2xpbmtlZF9zdWJzZXQuY3N2Iiwgcm93Lm5hbWVzPUYpDQplY2RzX2xpbmtlZCA8LSByZWFkLmNzdigiRGF0YV9jYXJyaWFnZS9MaW5lbGlzdC9lY2RzX2xpbmtlZF9zdWJzZXQuY3N2IikNCg0KZWNkc19saW5rZWQgPC0gZWNkc19saW5rZWQgJT4lIHJlbmFtZShldGhuX2NvZGU9UGF0aWVudEV0aG5pY0NhdGVnb3J5Q29kZSkgJT4lDQogIG11dGF0ZShlY2RzX2V0aG5pY2l0eT1jYXNlX3doZW4oZXRobl9jb2RlPT0iQSIgfiAiQnJpdGlzaCAoV2hpdGUpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV0aG5fY29kZT09Ik4iIH4gIkFmcmljYW4gKEJsYWNrIG9yIEJsYWNrIEJyaXRpc2gpIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldGhuX2NvZGU9PSJMIiB+ICJBbnkgb3RoZXIgQXNpYW4gYmFja2dyb3VuZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXRobl9jb2RlPT0iRyIgfiAiQW55IG90aGVyIE1peGVkIGJhY2tncm91bmQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV0aG5fY29kZT09IkMiIH4gIkFueSBvdGhlciBXaGl0ZSBiYWNrZ3JvdW5kIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldGhuX2NvZGU9PSJTIiB+ICJBbnkgb3RoZXIgZXRobmljIGdyb3VwIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldGhuX2NvZGU9PSJLIiB+ICJCYW5nbGFkZXNoaSAoQXNpYW4gb3IgQXNpYW4gQnJpdGlzaCkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV0aG5fY29kZT09Ik0iIH4gIkNhcmliYmVhbiAoQmxhY2sgb3IgQmxhY2sgQnJpdGlzaCkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV0aG5fY29kZT09IlIiIH4gIkNoaW5lc2UgKG90aGVyIGV0aG5pYyBncm91cCkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV0aG5fY29kZT09IkgiIH4gIkluZGlhbiAoQXNpYW4gb3IgQXNpYW4gQnJpdGlzaCkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXRobl9jb2RlPT0iSiIgfiAiUGFraXN0YW5pIChBc2lhbiBvciBBc2lhbiBCcml0aXNoKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldGhuX2NvZGU9PSJGIiB+ICJXaGl0ZSBhbmQgQXNpYW4gKE1peGVkKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldGhuX2NvZGU9PSJFIiB+ICJXaGl0ZSBhbmQgQmxhY2sgQWZyaWNhbiAoTWl4ZWQpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV0aG5fY29kZT09IkQiIH4gIldoaXRlIGFuZCBCbGFjayBDYXJpYmJlYW4gKE1peGVkKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldGhuX2NvZGU9PSJaIiB+ICJaIE5vdCBTdGF0ZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXRobl9jb2RlPT0iQiIgfiAiV2hpdGUgSXJpc2giLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXRobl9jb2RlPT0iOSIgfiAiTm90IEtub3duIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV0aG5fY29kZT09IlAiIH4gIkFueSBvdGhlciBCbGFjayBiYWNrZ3JvdW5kIChCbGFjayBvciBCbGFjayBCcml0aXNoKSIpKSANCg0KIyBpZGVudGlmeSBhbmQgZmxhZyBkdXBsaWNhdGUgcmVjb3JkcyByZXN1bHRpbmcgZnJvbSBFQ0RTIGxpbmthZ2UgaS5lLiwgd2hlcmUgRUNEUyByZXR1cm5lZCAyIG9yIG1vcmUgZXRobmljaXR5IGFzc2lnbm1lbnRzIGZvciBhIGdpdmVuIHBhdGllbnQNCmVjZHNfbGlua2VkJGR1cF9mbGFnIDwtIGR1cGxpY2F0ZWQoZWNkc19saW5rZWQkcGlkX3N0cikgfCAgZHVwbGljYXRlZChlY2RzX2xpbmtlZCRwaWRfc3RyLCBmcm9tTGFzdCA9IFRSVUUpDQpkdXBzIDwtIGVjZHNfbGlua2VkICU+JSBmaWx0ZXIoZHVwX2ZsYWc9PVQpICU+JSBzZWxlY3QocGlkX3N0cixzcGVjZGF0ZSxFdGhuaWNfZ3JvdXAsZWNkc19ldGhuaWNpdHksZHVwX2ZsYWcpICU+JSBhcnJhbmdlKHBpZF9zdHIsZWNkc19ldGhuaWNpdHkpICMgZXhwbG9yZSBkdXBsaWNhdGVzIGFuZCBhc3NpZ24gYSBydWxlIHNldCBmb3IgcmV0YWluaW5nIHJlY29yZHMNCmxlbmd0aCh1bmlxdWUoZHVwcyRwaWRfc3RyKSkgICMgMTQgcGlkX3N0ciB3aXRoIGR1cGxpY2F0ZSByb3dzDQpwaWRfc3RyX2R1cHMgPC0gdW5pcXVlKGR1cHMkcGlkX3N0cikNCg0KIyBpZiAnWiBub3QgU3RhdGVkJyBhbmQgJ05vdCBLbm93bicgcmV0YWluIGVpdGhlcg0KIyBpZiAnWiBub3QgU3RhdGVkJy8nTm90IEtub3duJyBhbmQgYSBuYW1lZCBldGhuaWNpdHkgZ3JvdXAsIHJldGFpbiBldGhuaWNpdHkgZ3JvdXANCiMgaWYgTkEgYW5kICdaIG5vdCBTdGF0ZWQnLydOb3QgS25vd24nIG9yIGEgbmFtZWQgZXRobmljaXR5IGdyb3VwLCByZXRhaW4gbGF0dGVyIChub24tTkEgZW50cnkpDQojIGlmICdBbnkgb3RoZXIgQmxhY2sgYmFja2dyb3VuZCAoQmxhY2sgb3IgQmxhY2sgQnJpdGlzaCknIGFuZCAnQW55IG90aGVyIGV0aG5pYyBncm91cCcgcmV0YWluIHRoZSBoaWdoZXIgc3BlY2lmaWNpdHkgZXRobmljaXR5IGFzc2lnbm1lbnQgKGluIHRoaXMgY2FzZSBCbGFjayBvciBCbGFjayBCcml0aXNoID4gQW55IG90aGVyIGV0aG5pY2l0eSBncm91cCkNCg0KIy8vLy8vLyBlY2RzX2V0aG5pY2l0eSBoaWVyYXJjaHkgLy8vLy8vLy8vDQojIG1heCBzcGVjaWZpY2l0eToNCm1heF9zcGVjIDwtIGMoIkJyaXRpc2ggKFdoaXRlKSIsDQogICJBZnJpY2FuIChCbGFjayBvciBCbGFjayBCcml0aXNoKSIsDQogICJCYW5nbGFkZXNoaSAoQXNpYW4gb3IgQXNpYW4gQnJpdGlzaCkiLA0KICAiQ2FyaWJiZWFuIChCbGFjayBvciBCbGFjayBCcml0aXNoKSIsDQogICJDaGluZXNlIChvdGhlciBldGhuaWMgZ3JvdXApIiwNCiAgIkluZGlhbiAoQXNpYW4gb3IgQXNpYW4gQnJpdGlzaCkiLA0KICAiUGFraXN0YW5pIChBc2lhbiBvciBBc2lhbiBCcml0aXNoKSIsDQogICJXaGl0ZSBhbmQgQXNpYW4gKE1peGVkKSIsDQogICJXaGl0ZSBhbmQgQmxhY2sgQWZyaWNhbiAoTWl4ZWQpIiwNCiAgIldoaXRlIGFuZCBCbGFjayBDYXJpYmJlYW4gKE1peGVkKSIsDQogICJXaGl0ZSBJcmlzaCIpDQoNCiMgaGlnaCBzcGVjaWZpY2l0eQ0KaGlnaF9zcGVjIDwtIGMoIkFueSBvdGhlciBCbGFjayBiYWNrZ3JvdW5kIChCbGFjayBvciBCbGFjayBCcml0aXNoKSIsDQogICAgICAgICAgICAgICJBbnkgb3RoZXIgQXNpYW4gYmFja2dyb3VuZCIsIA0KICAgICAgICAgICAgICAiQW55IG90aGVyIE1peGVkIGJhY2tncm91bmQiLA0KICAgICAgICAgICAgICAiQW55IG90aGVyIFdoaXRlIGJhY2tncm91bmQiKQ0KDQojIG1lZGl1bSBzcGVjaWZpY2l0eToNCm1lZF9zcGVjIDwtIGMoIkFueSBvdGhlciBldGhuaWMgZ3JvdXAiLCJaIE5vdCBTdGF0ZWQiKQ0KDQojIGxvdyBzcGVjaWZpY2l0eToNCmxvd19zcGVjIDwtIGMoIk5vdCBLbm93biIpDQoNCiMgbm8gc3BlY2lmaWNpdHkvbWlzc2luZw0Kbm9fc3BlYyA8LSBOQQ0KDQojIGRlZmluZSBoaWVyYXJjaHkgZm9yIGNhdGVnb3J5IG9yIGNhdA0KY2F0ZWdvcnlfaGllcmFyY2h5IDwtIGMoIk1heCIgPSAxLCAiSGlnaCIgPSAyLCAiTWVkaXVtIiA9IDMsICJMb3ciID0gNCwgIk5vIiA9IDUpDQoNCiMgYXNzaWduIGVhY2ggcm93IGFzIHBlciBhYm92ZSBoaWVyYXJjaHkNCmR1cHMgPC0gZHVwcyAlPiUgbXV0YXRlKGNhdD1jYXNlX3doZW4oZWNkc19ldGhuaWNpdHkgJWluJSBtYXhfc3BlYyAgfiAiTWF4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWNkc19ldGhuaWNpdHkgJWluJSBoaWdoX3NwZWMgfiAiSGlnaCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjZHNfZXRobmljaXR5ICVpbiUgbWVkX3NwZWMgfiAiTWVkaXVtIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWNkc19ldGhuaWNpdHkgJWluJSBsb3dfc3BlYyB+ICJMb3ciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2RzX2V0aG5pY2l0eSAlaW4lIG5vX3NwZWMgfiAiTm8iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNhdF9yYW5rID0gY2F0ZWdvcnlfaGllcmFyY2h5W2NhdF0pICU+JSANCiAgYXJyYW5nZShwaWRfc3RyLCBjYXRfcmFuaykgJT4lIA0KICBkaXN0aW5jdChwaWRfc3RyLCAua2VlcF9hbGwgPSBUKQ0KDQojIG5vdyBhcHBseSB0byBlY2RzX2xpbmtlZCBkZiANCmVjZHNfbGlua2VkIDwtIGVjZHNfbGlua2VkICU+JSBtdXRhdGUoY2F0PWNhc2Vfd2hlbihlY2RzX2V0aG5pY2l0eSAlaW4lIG1heF9zcGVjICB+ICJNYXgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjZHNfZXRobmljaXR5ICVpbiUgaGlnaF9zcGVjIH4gIkhpZ2giLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjZHNfZXRobmljaXR5ICVpbiUgbWVkX3NwZWMgfiAiTWVkaXVtIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2RzX2V0aG5pY2l0eSAlaW4lIGxvd19zcGVjIH4gIkxvdyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWNkc19ldGhuaWNpdHkgJWluJSBub19zcGVjIH4gIk5vIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdF9yYW5rID0gY2F0ZWdvcnlfaGllcmFyY2h5W2NhdF0pICU+JQ0KICBncm91cF9ieShwaWRfc3RyKSAlPiUNCiAgbXV0YXRlKGlzX3NlbGVjdGVkID0gaWZfZWxzZShkdXBfZmxhZywgY2F0X3JhbmsgPT0gbWluKGNhdF9yYW5rW2R1cF9mbGFnXSksIFRSVUUpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBmaWx0ZXIoaXNfc2VsZWN0ZWQpICU+JSBzZWxlY3QoLWMoImR1cF9mbGFnIiwiY2F0IiwiY2F0X3JhbmsiLCJpc19zZWxlY3RlZCIpKQ0KDQojIGNoZWNrIHRoYXQgbm9uLWR1cGxpY2F0ZSByb3dzIG5vdyBpbiBkYXRhIGZvciBwaWRfc3RyIHZhbHMgZmxhZ2dlZCBhYm92ZQ0KcGlkX3N0cl9kdXBzICVpbiUgZWNkc19saW5rZWQkcGlkX3N0ciAgIyBUUlVFIGluIGFsbCBpbnN0YW5jZXMNCg0KIyBjaGVjayB0aGF0IHBpZF9zdHIgcmV0YWluZWQgY29ycmVzcG9uZCB0byB0aG9zZSBpZGVudGlmaWVkIGFzIGhhdmluZyBkdXBsaWNhdGUgZW50cmllcyBhYm92ZQ0KdW5pcXVlKGR1cHMkcGlkX3N0cikgJWluJSBwaWRfc3RyX2R1cHMgIyBUUlVFIGF0IGFsbCBwb3NpdGlvbnMgaW4gdmVjdG9yDQoNCiMgdGFidWxhdGUgRUNEUyBldGhuaWNpdHkgYXNzaWdubWVudCBieSBOSFMgbnVtYmVyIHN0YXR1cyAoa25vd24gb3IgbWlzc2luZykNCnRhYiA8LSBlY2RzX2xpbmtlZCAlPiUgbXV0YXRlKG5oc25vX3N0YXR1cz1jYXNlX3doZW4oaXMubmEobmhzbm8pIH4gIm1pc3NpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEobmhzbm8pIH4gImtub3duIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZXNsaW5rX3N0YXR1cz1jYXNlX3doZW4oaXMubmEoRXRobmljX2dyb3VwKSB+ICJub3QgbGlua2VkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEoRXRobmljX2dyb3VwKSB+ICJsaW5rZWQiKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgbXV0YXRlKHo9MSkgJT4lIGdyb3VwX2J5KG5oc25vX3N0YXR1cyxoZXNsaW5rX3N0YXR1cyxlY2RzX2V0aG5pY2l0eSkgJT4lIHN1bW1hcml6ZSh0b3RhbD1zdW0oeikpICU+JSBtdXRhdGUocGN0PXRvdGFsL3N1bSh0b3RhbCkqMTAwKQ0KDQp3cml0ZS5jc3YodGFiLCAiRGF0YV9jYXJyaWFnZS9FQ01fY2FycmlhZ2UvU3RhdF9zdW1tYXJpZXMvaGVzX25vZXRobl9lY2RzX2NhdC5jc3YiLCByb3cubmFtZXM9RikNCiMgYW1vbmcga25vd24gbmhzbm8sIDIxOCAoNzclKSB3ZXJlIEhFUyBsaW5rZWQgYnV0IHJldHVybmVkICI5OSBOb3QgS25vd24iIG9yICJaIE5vdCBzdGF0ZWQiLiBPbmx5IDggb2YgdGhlc2UgMjE4IHJlY29yZHMgaGFkIGFuIGV0aG5pY2l0eSBhc3NpZ25tZW50IGluIEVDRFMuIEZvciB0aG9zZSByZWNvcmRzIG5vdCBIRVMgbGlua2VkLCBFQ0RTIGVucmljaG1lbnQgcmV0dXJuZWQgNiByZWNvcmRzIHdpdGgga25vd24gZXRobmljaXR5LiBPdmVyYWxsLCBFQ0RTIGVucmljaG1lbnQgcHJvdmlkZWQgYW4gZXRobmljaXR5IGZvciA0LjklIHJlY29yZHMgdGhhdCBoYWQgYW4gTkhTIG51bWJlcg0KDQplY2RzX2xpbmtlZCA8LSBlY2RzX2xpbmtlZCAlPiUgc2VsZWN0KHBpZF9zdHIsZWNkc19ldGhuaWNpdHkpICU+JSBmaWx0ZXIoIWlzLm5hKGVjZHNfZXRobmljaXR5KSkgIyBDT05USU5VRSBIRVJFIC0gVEhFUkUgQVJFIERVUExJQ0FURVMgT0YgcGlkX3N0cg0KDQp0ZXN0bWVyZ2UgPC0gbWVyZ2UobGluZWxpc3QsZWNkc19saW5rZWQsIGJ5ID0gInBpZF9zdHIiLCBhbGw9VCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG1lcmdlIGJhY2sgZGVkdXBsaWNhdGVkLCBlY2RzLWxpbmtlZCBzdWJzZXQgb2YgcmVjb3JkcyBpbnRvIGxpbmVsaXN0IHRvIHJldGFpbiBuZXcgZWNkc19ldGhuaWNpdHkgY29sDQogIA0KdGVzdG1lcmdlIDwtIHRlc3RtZXJnZSAlPiUgbXV0YXRlKEV0aG5pY19ncm91cD1jYXNlX3doZW4oKGVjZHNfZXRobmljaXR5IT1FdGhuaWNfZ3JvdXAgJiAgICAgICAgIyBhc3NpZ24gRUNEUyBldGhuaWNpdHkgd2hlcmUgbm90IEhFUyBsaW5rZWQgb3Igd2hlcmUgZXRobmljaXR5IG5vdCBrbm93biBpbiBIRVMgYnV0IHJldHVybmVkIGluIEVDRFMgbGlua2FnZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjZHNfZXRobmljaXR5IT0iTm90IEtub3duIiAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjZHNfZXRobmljaXR5IT0iWiBOb3QgU3RhdGVkIikgfA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKEV0aG5pY19ncm91cCkgfiBlY2RzX2V0aG5pY2l0eSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBFdGhuaWNfZ3JvdXApKSAlPiUgc2VsZWN0KC1jKCJlY2RzX2V0aG5pY2l0eSIpKSAlPiUgDQogIG11dGF0ZShFdGhuaWNpdHlfZ3JvdXA9Y2FzZV93aGVuKEV0aG5pY2l0eV9ncm91cD09IkFzaWFuIiB+ICJBbnkgb3RoZXIgQXNpYW4gYmFja2dyb3VuZCIsICAgICAgIyByZSBjb2RlIExJTVMgZXRobmljaXR5IGNhdGVnb3JpZXMgdG8gYWxpZ24gd2l0aCBIRVMgY2F0ZWdvcml6YXRpb24gKGRlZmF1bHRpbmcgdG8gQW55IG90aGVyLi4uKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdGhuaWNpdHlfZ3JvdXA9PSJXaGl0ZSIgfiAiQW55IG90aGVyIFdoaXRlIGJhY2tncm91bmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdGhuaWNpdHlfZ3JvdXA9PSJCbGFjayIgfiAiQW55IG90aGVyIEJsYWNrIGJhY2tncm91bmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdGhuaWNpdHlfZ3JvdXA9PSJNaXhlZCIgfiAiQW55IG90aGVyIE1peGVkIGJhY2tncm91bmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdGhuaWNpdHlfZ3JvdXA9PSIiIH4gIlogTm90IHN0YXRlZCIpKSAlPiUgDQogIG11dGF0ZShFdGhuaWNfZ3JvdXA9Y2FzZV93aGVuKChFdGhuaWNfZ3JvdXA9PSJaIE5vdCBzdGF0ZWQiIHwgRXRobmljX2dyb3VwPT0iOTkgTm90IEtub3duIiB8IEV0aG5pY19ncm91cD09Ilogbm90IHN0YXRlZCIgfCBpcy5uYShFdGhuaWNfZ3JvdXApKSAmIEV0aG5pY2l0eV9ncm91cCE9IiIgJiBFdGhuaWNpdHlfZ3JvdXAhPSJVbmtub3duIiB+IEV0aG5pY2l0eV9ncm91cCwgICAgDQogICAgICAgICBUUlVFIH4gRXRobmljX2dyb3VwKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFzc2lnbiBMSU1TIGV0aG5pY2l0eSB3aGVyZSByZWNvcmRlZCwgaWYgbm90IEhFUyBsaW5rZWQgb3IgZXRobmljaXR5IG5vdCBzdGF0ZWQgaW4gSEVTLCBhbmQgbm90IHJldHVybmVkIGluIEVDRFMgbGlua2FnZSAobGFzdCByZXNvcnQgZXRobmljaXR5IGFzc2lnbm1lbnQpDQoNCiMgREVEVVBMSUNBVEUgSEVSRSBBRlRFUiBFVEhOSUNJVFkgTElOS0FHRSANCg0KIyAvLy8vLy8vLy8vLy8ga2V5IGNoZWNrcG9pbnQgNCAvLy8vLy8vLy8vLy8vLw0KICAjIyBzYXZlIGFzIHN0YXRpYyBiYXNlIGRhdGFzZXQgKiphZnRlciBmdWxsIGV0aG5pY2l0eSBhc3NpZ25tZW50IGFuZCBjYXRlZ29yaXNhdGlvbioqDQogIHdyaXRlLmNzdihsaW5lbGlzdCwgIkRhdGFfY2FycmlhZ2UvTGluZWxpc3Qvc3RhdGljX2Jhc2VkYXRhX2V0aG5saW5rZWRfZmluYWwuY3N2Iiwgcm93Lm5hbWVzPUYpDQogIA0KICBsaW5lbGlzdCA8LSByZWFkLmNzdigiRGF0YV9jYXJyaWFnZS9MaW5lbGlzdC9zdGF0aWNfYmFzZWRhdGFfZXRobmxpbmtlZF9yYXcuY3N2IikNCiAgbGluZWxpc3Qkc3BlY2RhdGUgPC0gYXMuRGF0ZShsaW5lbGlzdCRzcGVjZGF0ZSwgZm9ybWF0ID0gIiVZLSVtLSVkIikNCiAgbGluZWxpc3QkZG9iIDwtIGFzLkRhdGUobGluZWxpc3QkZG9iLCBmb3JtYXQgPSAiJVktJW0tJWQiKQ0KDQogICMgQ09OVElOVUUgSEVSRSAgDQpsaW5lbGlzdCA8LSBsaW5lbGlzdC5ldGhjYXQ1LmZ1bihsaW5lbGlzdCkNCg0KIyB1c2UgZXRobmljaXR5IGZpZWxkIGluIExJTVMgaWYgaGVzIGV0aG5pY2l0eSBub3QgcmV0dXJuZWQgZm9yIGEgcGF0aWVudA0KbGluZWxpc3QgPC0gbGluZWxpc3QgJT4lIG11dGF0ZShoZXNfZXRobmljaXR5ID0gY2FzZV93aGVuKA0KICAoaGVzX2V0aG5pY2l0eT09Ik5vdCBsaW5rZWQiICYgbGltX2V0aG5pY2l0eSE9IlVua25vd24iKSB+IHBhc3RlMChsaW1fZXRobmljaXR5LCJfZnJvbUxJTVMiKSwNCiAgVFJVRSB+IGhlc19ldGhuaWNpdHkNCikpDQoNCiMgIyBTQVZFIEVUSCBMSU5LRUQgTElORUxJU1QgTk9XDQojIHdyaXRlLmNzdihsaW5lbGlzdCwgIkRhdGFfY2FycmlhZ2UvbGluZWxpc3QvZXRobmxpbmtlZF9saW5lbGlzdDIuY3N2IikNCg0KIyByZWNvcmQgdGhlIGRhdGEgc291cmNlIGZvciBldGhuaWNpdHkgYnkgc2l0ZSANCiMgcmVjb3JkaW5nIGF0dHJpdGlvbiBieSBzaXRlICh3aGVyZSBldGhuaWNpdHkgbWlzc2luZyB0b28sIGFzIHRoaXMgY2FuJ3QgYmUgdXNlZCB0byBlc3RpbWF0ZSBjYXJyaWFnZSBieSBldGhuaWNpdHkpDQojIC0gcmF3IGRhdGEgKGZyb20gc2l0ZSkgTkhTIG51bWJlciBjb21wbGV0aW9uDQojIGVuaGFuY2VkIGRhdGEgTkhTIG51bWJlciBjb21wbGV0aW9uDQojIGZvciBhdXRvbWF0ZWQgcmVwb3J0IA0KIyBjb21wYXJlIGRhdGEgcXVhbGl0eSBieSBzaXRlIC0gYXJlIHNvbWUgYmlhc2luZyB0aGUgcmVzdWx0cyAoc2Vuc2l0aXZpdHkgYW5hbHlzaXMpDQoNCiMgSU1QT1JUIE1BSU4gRVRITklDSVRZIEVOUklDSEVEIExJTkVMSVNUIEhFUkUgKGV0aG5pY2l0eSBpbiA1IGNhdGVnb3JpZXMpDQpldGhubGlua2VkX2xpbmVsaXN0PWltcG9ydChoZXJlKCJEYXRhX2NhcnJpYWdlIiwiTGluZWxpc3QiLCJldGhubGlua2VkX2xpbmVsaXN0Mi5jc3YiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImNzdiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBULA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tZW50LmNoYXI9IiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hID0gYygiIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVCkNCg0KbGluZWxpc3QgPC0gZXRobmxpbmtlZF9saW5lbGlzdA0KbGluZWxpc3QgPC0gbGluZWxpc3QgJT4lIG11dGF0ZShzcGVjaWVzPWNhc2Vfd2hlbihzcGVjaWVzPT0iYmV0YSBIYWVtb2x5dGljIFN0cmVwLCBncm91cCBCIiB+ICJHcm91cCBCIFN0cmVwdG9jb2NjdXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBzcGVjaWVzKSkgICMgbW9wIHVwIG9mIGFueSB2YXJpYW50cyBvZiBHQlMgcG9zIHRlc3QgZmllbGQNCg0KIA0KIyBzdWNjZXNzIG9mIGV0aG5pY2l0eSBhc2NlcnRhaW5tZW50IGZyb20gQ0hJTUUgbGlua2FnZSAoYW5kIHN1cHBsZW1lbnRlZCBieSBFQ0RTKSwgb3ZlcmFsbCBhbmQgYnkgc2l0ZSwgYW5kIHRoZW4gJSBvZiBldGhuIGZpbGxlZCBpbiBmcm9tIGxvY2FsIExJTVMgZGF0YSwgYWxzbyBvdmVyYWxsIGFuZCBieSBzaXRlDQoNCmBgYA0KDQpTdW1tYXJpc2UgZXRobmljaXR5IGFzY2VydGFpbm1lbnQgYW5kIGVzdGltYXRlIGNhcnJpYWdlIGJ5IGV0aG5pY2l0eSANCg0KYGBge3IgY2FycmlhZ2UgYnkgZXRobiwgaW5jbHVkZT1UUlVFfQ0KDQojIGZpbHRlciBmb3IgZmlyc3QgNCBzaXRlcyBkYXRhIGFuYWx5c2VkDQpmb3VyX3NpdGUgPC0gYygiQmVkZm9yZCIsIkRlcnJpZm9yZCIsIkZyZWVtYW4iLCJSb3lhbCBTdG9rZSIpDQoNCmxpbmVsaXN0IDwtIGxpbmVsaXN0ICU+JSBmaWx0ZXIoc2l0ZSAlaW4lIGZvdXJfc2l0ZSkNCmdic2NhcnJfZXRobiA8LSBnYnMuY2Fyci5ieWV0aG4uZnVuKGxpbmVsaXN0KQ0KDQp3cml0ZS5jc3YoZ2JzY2Fycl9ldGhuLCAiRGF0YV9jYXJyaWFnZS9FQ01fY2FycmlhZ2UvQ2FycmlhZ2VfZXN0aW1hdGVzL2dic2NhcnJfZXRobi5jc3YiKQ0KDQojIHByZXAgZXRoIGNhdGVnb3JpZXMgZm9yIHBsb3QNCg0KDQojIENyZWF0ZSBhIGJhciBncmFwaCBvZiBnYnMgY2FycmlhZ2UgYnkgZXRobiAod2hlcmUgZGVub20gZG9lc24ndCBpbmNsdWRlIGV0aG5pY2l0eSB1bmtub3duL2xpbmsgdG8gSEVTIG5vdCBzdWNjZXNzZnVsKQ0KZ2JzY2Fycl9mb3JwbG90IDwtIGdic2NhcnJfZXRobiAlPiUgDQogIHNlbGVjdCgtYygiY2FycmlhZ2VfYnl0aG4iKSkgJT4lIA0KICBtdXRhdGUoZXRobl9mb3JwbG90ID0gY2FzZV93aGVuKA0KICBoZXNfZXRobmljaXR5PT0iQXNpYW5fZnJvbUxJTVMiIH4gIkFzaWFuIiwNCiAgaGVzX2V0aG5pY2l0eT09IkJsYWNrX2Zyb21MSU1TIiB+ICJCbGFjayIsDQogIGhlc19ldGhuaWNpdHk9PSJNaXhlZF9mcm9tTElNUyIgfiAiTWl4ZWQiLA0KICBoZXNfZXRobmljaXR5PT0iV2hpdGVfZnJvbUxJTVMiIH4gIldoaXRlIiwNCiAgaGVzX2V0aG5pY2l0eT09Ik90aGVyX2Zyb21MSU1TIiB+ICJPdGhlciIsDQogIFRSVUUgfiBoZXNfZXRobmljaXR5DQopKSAlPiUgDQogIHNlbGVjdCgtYygiaGVzX2V0aG5pY2l0eSIpKSAlPiUgDQogIGdyb3VwX2J5KGV0aG5fZm9ycGxvdCkgJT4lIA0KICBzdW1tYXJpemUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgc3VtKSkgJT4lIA0KICBtdXRhdGUoZ2JjX2NhcnIgPSBnYnNwb3MvdG90YWxlY20qMTAwKSAlPiUNCiAgZmlsdGVyKGV0aG5fZm9ycGxvdCE9Ik5vdCBsaW5rZWQiICYgZXRobl9mb3JwbG90IT0iVW5rbm93biIpICU+JSANCiAgYXJyYW5nZShnYmNfY2FycikgJT4lIG11dGF0ZShldGhuX2ZvcnBsb3Q9ZmFjdG9yKGV0aG5fZm9ycGxvdCwgbGV2ZWxzID0gYygiV2hpdGUiLCJCbGFjayIsIkFzaWFuIiwiT3RoZXIiLCJNaXhlZCIpKSkgDQoNCnBsb3QgPC0gZ2dwbG90KGdic2NhcnJfZm9ycGxvdCwgYWVzKHggPSBldGhuX2ZvcnBsb3QsIHkgPSBnYmNfY2FycikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic2t5Ymx1ZSIsIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2Vycm9yYmFyKA0KICAgIGFlcyh5bWluID0gbG93ZXJfY2ksIHltYXggPSB1cHBlcl9jaSksDQogICAgd2lkdGggPSAwLjIsDQogICAgY29sb3IgPSAiYmxhY2siDQogICkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB1bmlxdWUoZ2JzY2Fycl9mb3JwbG90JHRvdGFsX2NhcnJpYWdlKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJHQlMgY2FycmlhZ2UgYnkgZXRobmljaXR5IiwgeCA9ICJFdGhuaWNpdHkiLCB5ID0gIkdCUyBjYXJyaWFnZSAoJSkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB4LWF4aXMgbGFiZWxzDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB5LWF4aXMgbGFiZWxzDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKGIgPSAyMCkpLCAgIyBJbmNyZWFzZSBzaXplIG9mIHgtYXhpcyB0aXRsZQ0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsICAgIyBJbmNyZWFzZSBzaXplIG9mIHktYXhpcyB0aXRsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksICAgDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0PSAwLjUpDQogICkNCg0KDQojIHNhdmUgcGxvdA0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvZ2JzY2Fycl9ldGhuLmpwZWciLCBwbG90ID0gcGxvdCwgZGV2aWNlID0gImpwZWciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkNCg0KYGBgDQoNCkVzdGltYXRlIGNhcnJpYWdlIGJ5IGV0aG5pY2l0eSBmb3IgQXNpYW4gc3ViZ3JvdXBpbmdzIEluZGlhbiwgUGFraXN0YW5pIGFuZCBCYW5nbGFkZXNoaSwgdXNpbmcgbGluZWxpc3QgcHJpb3IgdG8gNSBjYXRlZ29yeSBncm91cGluZyBvZiBldGhuaWNpdHkgZmllbGQNCg0KYGBge3IgZXRobiBhc2lhbiBzdWJjYXQsIGluY2x1ZGU9VFJVRX0NCg0KIyBpbXBvcnQgbGluZWxpc3Qgd2l0aCBmdWxsIHNldCBvZiBIRVMgZXRobmljaXR5IGNhdGVnb3JpZXMNCmxpbmVsaXN0X2V0aG5zdWI9aW1wb3J0KGhlcmUoIkRhdGFfY2FycmlhZ2UiLCJMaW5lbGlzdCIsImV0aG5saW5rZWRfbGluZWxpc3QuY3N2IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICJjc3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gVCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbWVudC5jaGFyPSIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYSA9IGMoIiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2lwID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpDQoNCm5yb3cobGluZWxpc3RfZXRobnN1YikNCg0KdW5pcXVlKGxpbmVsaXN0X2V0aG5zdWIkRXRobmljX2dyb3VwKQ0KDQpnYnNjYXJyX3N1YmV0aG4gPC0gZ2JzLmNhcnIuYnlzdWJldGhuLmZ1bihsaW5lbGlzdF9ldGhuc3ViKQ0KDQojIG5vdyBnZW5lcmF0ZSBHQlMgY2FycmlhZ2Ugc3VtbWFyaWVzIGZvciBhbGwgMTYgSEVTIGV0aG5pY2l0eSBjYXRlZ29yaWVzDQoNCmdic2NhcnJfYWxsc3ViZXRoIDwtIGdicy5jYXJyLmJ5YWxsc3ViZXRobi5mdW4obGluZWxpc3RfZXRobnN1YikNCndyaXRlLmNzdihnYnNjYXJyX2FsbHN1YmV0aCwgIkRhdGFfY2FycmlhZ2UvRUNNX2NhcnJpYWdlL0NhcnJpYWdlX2VzdGltYXRlcy9nYnNjYXJyX2FsbHN1YmV0aC5jc3YiKQ0KDQpgYGANCg0KRXN0aW1hdGUgY2FycmlhZ2UgYnkgZGVwcml2YXRpb24NCg0KYGBge3IgY2FycmlhZ2UgYnkgaW1kLCBpbmNsdWRlPVRSVUV9DQoNCmdic2NhcnJfaW1kIDwtIGdicy5jYXJyLmJ5aW1kLmZ1bihsaW5lbGlzdCkNCg0KZ2JzY2Fycl9mb3JwbG90IDwtIGdic2NhcnJfaW1kICU+JSBmaWx0ZXIoaW1kX3F1aW50aWxlIT0iTm90IGxpbmtlZCIgJiBpbWRfcXVpbnRpbGUhPSJOQSIgJiAhaXMubmEoaW1kX3F1aW50aWxlKSkgJT4lIA0KICBtdXRhdGUoaW1kX3F1aW50aWxlPWZhY3RvcihpbWRfcXVpbnRpbGUsIGxldmVscyA9IGMoIjIwJSBsZWFzdCBkZXByaXZlZCIsICIyMCUgdG8gNDAlIiwgIjQwJSB0byA2MCUiLCAiNjAlIHRvIDgwJSIsICIyMCUgbW9zdCBkZXByaXZlZCIpLCBvcmRlcmVkID0gVFJVRSkpDQoNCndyaXRlLmNzdihnYnNjYXJyX2ltZCwgIkRhdGFfY2FycmlhZ2UvRUNNX2NhcnJpYWdlL0NhcnJpYWdlX2VzdGltYXRlcy9nYnNjYXJyX2ltZC5jc3YiKQ0KDQojIENyZWF0ZSBhIGJhciBncmFwaCBvZiBnYnMgY2FycmlhZ2UgYnkgSU1EIHF1aW50aWxlDQpwbG90IDwtIGdncGxvdChnYnNjYXJyX2ZvcnBsb3QsIGFlcyh4ID0gaW1kX3F1aW50aWxlLCB5ID0gY2FycmlhZ2VfYnlpbWQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInNreWJsdWUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgIGdlb21fZXJyb3JiYXIoDQogICAgYWVzKHltaW4gPSBsb3dlcl9jaSwgeW1heCA9IHVwcGVyX2NpKSwNCiAgICB3aWR0aCA9IDAuMiwNCiAgICBjb2xvciA9ICJibGFjayINCiAgKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHVuaXF1ZShnYnNjYXJyX2ZvcnBsb3QkdG90YWxfY2FycmlhZ2UpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIkdCUyBjYXJyaWFnZSBieSBJTUQgcXVpbnRpbGUiLCB4ID0gIklNRCBxdWludGlsZSIsIHkgPSAiR0JTIGNhcnJpYWdlICglKSIpICsNCiAgIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB4LWF4aXMgbGFiZWxzDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB5LWF4aXMgbGFiZWxzDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKGIgPSAyMCkpLCAgIyBJbmNyZWFzZSBzaXplIG9mIHgtYXhpcyB0aXRsZQ0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsICAgIyBJbmNyZWFzZSBzaXplIG9mIHktYXhpcyB0aXRsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksICAgDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0PSAwLjUpDQogICkNCg0KIA0KIyBzYXZlIHBsb3QNCmdnc2F2ZSgiQ2hhcnRzX2NhcnJpYWdlL2dic19jYXJyX2ltZC5qcGVnIiwgcGxvdCA9IHBsb3QsIGRldmljZSA9ICJqcGVnIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDApDQoNCg0KYGBgDQoNCkVzdGltYXRlIGNhcnJpYWdlIGJ5IGFnZSBncm91cA0KDQpgYGB7ciBjYXJyaWFnZSBieSBhZ2UsIGluY2x1ZGU9VFJVRX0NCg0KZ2JzY2Fycl9hZ2UgPC0gZ2JzLmNhcnIuYnlhZ2UuZnVuKGxpbmVsaXN0KQ0KDQp3cml0ZS5jc3YoZ2JzY2Fycl9hZ2UsICJEYXRhX2NhcnJpYWdlL0VDTV9jYXJyaWFnZS9DYXJyaWFnZV9lc3RpbWF0ZXMvZ2JzY2Fycl9hZ2UuY3N2IikNCg0KIyByZXBlYXQgYnV0IHJlc3RyaWN0IHRvIGVhY2ggZXRobmljaXR5IGluIHR1cm4NCmxsX2JsYWNrIDwtIGxpbmVsaXN0ICU+JSBmaWx0ZXIoaGVzX2V0aG5pY2l0eT09IkJsYWNrIiB8IGhlc19ldGhuaWNpdHk9PSJCbGFja19mcm9tTElNUyIpDQpnYnNjYXJyX2FnZSA8LSBnYnMuY2Fyci5ieWFnZS5mdW4obGxfYmxhY2spDQoNCmxsX2FzaWFuIDwtIGxpbmVsaXN0ICU+JSBmaWx0ZXIoaGVzX2V0aG5pY2l0eT09IkFzaWFuIiB8IGhlc19ldGhuaWNpdHk9PSJBc2lhbl9mcm9tTElNUyIpDQpnYnNjYXJyX2FnZSA8LSBnYnMuY2Fyci5ieWFnZS5mdW4obGxfYXNpYW4pDQoNCg0KbGxfbWl4ZWQgPC0gbGluZWxpc3QgJT4lIGZpbHRlcihoZXNfZXRobmljaXR5PT0iTWl4ZWQiIHwgaGVzX2V0aG5pY2l0eT09Ik1peGVkX2Zyb21MSU1TIikNCmdic2NhcnJfYWdlIDwtIGdicy5jYXJyLmJ5YWdlLmZ1bihsbF9taXhlZCkNCg0KDQpsbF9vdGhlciA8LSBsaW5lbGlzdCAlPiUgZmlsdGVyKGhlc19ldGhuaWNpdHk9PSJPdGhlciIgfCBoZXNfZXRobmljaXR5PT0iT3RoZXJfZnJvbUxJTVMiKQ0KZ2JzY2Fycl9hZ2UgPC0gZ2JzLmNhcnIuYnlhZ2UuZnVuKGxsX290aGVyKQ0KDQpsbF93aGl0ZSA8LSBsaW5lbGlzdCAlPiUgZmlsdGVyKGhlc19ldGhuaWNpdHk9PSJXaGl0ZSIgfCBoZXNfZXRobmljaXR5PT0iV2hpdGVfZnJvbUxJTVMiKQ0KZ2JzY2Fycl9hZ2UgPC0gZ2JzLmNhcnIuYnlhZ2UuZnVuKGxsX3doaXRlKQ0KIyBwbG90ICUgY2FycmlhZ2UgYnkgYWdlIGdyb3VwIGluIGJhciBjaGFydA0KDQpnYnNjYXJyX2ZvcnBsb3QgPC0gZ2JzY2Fycl9hZ2UgJT4lIGZpbHRlcighYWdlZ3JvdXAgJWluJSBjKCIxMC0xNCIsIjUwLTU0IiwiNTUtNTkiLCI2MC02NCIpICYgIWlzLm5hKGFnZWdyb3VwKSkNCg0KIyBDcmVhdGUgYSBiYXIgZ3JhcGggb2YgZ2JzIGNhcnJpYWdlIGJ5IGFnZSBncm91cA0KcGxvdCA8LSBnZ3Bsb3QoZ2JzY2Fycl9mb3JwbG90LCBhZXMoeCA9IGFnZWdyb3VwLCB5ID0gY2FycmlhZ2VfYnlhZ2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInNreWJsdWUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgICBnZW9tX2Vycm9yYmFyKA0KICAgIGFlcyh5bWluID0gbG93ZXJfY2ksIHltYXggPSB1cHBlcl9jaSksDQogICAgd2lkdGggPSAwLjIsDQogICAgY29sb3IgPSAiYmxhY2siDQogICkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB1bmlxdWUoZ2JzY2Fycl9mb3JwbG90JHRvdGFsX2NhcnJpYWdlKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJHQlMgY2FycmlhZ2UgYnkgYWdlIGdyb3VwIiwgeCA9ICJBZ2UgZ3JvdXAiLCB5ID0gIkdCUyBjYXJyaWFnZSAoJSkiKSArDQogICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIsIHNpemUgPSAwLjUpLA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIEluY3JlYXNlIHNpemUgb2YgeC1heGlzIGxhYmVscw0KICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIEluY3JlYXNlIHNpemUgb2YgeS1heGlzIGxhYmVscw0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IG1hcmdpbihiID0gMjApKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB4LWF4aXMgdGl0bGUNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCAgICMgSW5jcmVhc2Ugc2l6ZSBvZiB5LWF4aXMgdGl0bGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKHIgPSAxMCkpLCAgIA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdD0gMC41KQ0KICApDQoNCiANCiMgc2F2ZSBwbG90DQpnZ3NhdmUoIkNoYXJ0c19jYXJyaWFnZS9nYnNfY2Fycl9hZ2Vncm91cC5qcGVnIiwgcGxvdCA9IHBsb3QsIGRldmljZSA9ICJqcGVnIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDApDQoNCiMgQ3JlYXRlIGEgYmFyIGdyYXBoIG9mIHRvdGFscyBieSBhZ2UgZ3JvdXANCg0KZ2JzdGVzdF9hZ2UgPC0gZ2JzY2Fycl9hZ2UgJT4lIHNlbGVjdCgtYygiY2FycmlhZ2VfYnlhZ2UiLCJ0b3RhbF9jYXJyaWFnZSIsImxvd2VyX2NpIiwidXBwZXJfY2kiKSkgJT4lIGZpbHRlcighaXMubmEoYWdlZ3JvdXApKSAlPiUgbXV0YXRlKGFnZWdyb3VwID0gY2FzZV93aGVuKA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2Vncm91cCAlaW4lIGMoIjQwLTQ0IiwiNDUtNDkiKSB+ICI0MC00OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFnZWdyb3VwICVpbiUgYygiNTAtNTQiLCI2MC02NCIpIH4gIjUwKyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBhZ2Vncm91cA0KICAgICAgICAgICAgICAgICAgICAgICAgKSkgJT4lIGdyb3VwX2J5KGFnZWdyb3VwKSAlPiUgc3VtbWFyaXplKGFjcm9zcyhldmVyeXRoaW5nKCksIHN1bSkpDQoNCnBsb3QgPC0gZ2dwbG90KGdic3Rlc3RfYWdlLCBhZXMoeCA9IGFnZWdyb3VwLCB5ID0gcGN0KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJza3libHVlIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnModGl0bGUgPSAiJSB0b3RhbCBFQ00gdGVzdHMgYnkgYWdlIGdyb3VwIiwgeCA9ICJBZ2UgZ3JvdXAiLCB5ID0gIiUgdG90YWwiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB4LWF4aXMgbGFiZWxzDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB5LWF4aXMgbGFiZWxzDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKGIgPSAyMCkpLCAgIyBJbmNyZWFzZSBzaXplIG9mIHgtYXhpcyB0aXRsZQ0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsICAgIyBJbmNyZWFzZSBzaXplIG9mIHktYXhpcyB0aXRsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksICAgDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0PSAwLjUpDQogICkNCg0KIyAjIHNhdmUgcGxvdA0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvdG90YWxzX2FnZWdyb3VwLmpwZWciLCBwbG90ID0gcGxvdCwgZGV2aWNlID0gImpwZWciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkNCg0KIyBzYXZpbmcgYWdlIGRpc3QgYnkgZXRoIHBsb3RzDQojIHNhdmUgcGxvdA0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvYWdlZ3JvdXBfYmxhY2suanBlZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSAianBlZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQ0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvYWdlZ3JvdXBfYXNpYW4uanBlZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSAianBlZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQ0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvYWdlZ3JvdXBfbWl4ZWQuanBlZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSAianBlZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQ0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvYWdlZ3JvdXBfb3RoZXIuanBlZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSAianBlZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQ0KZ2dzYXZlKCJDaGFydHNfY2FycmlhZ2UvYWdlZ3JvdXBfd2hpdGUuanBlZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSAianBlZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQ0KDQpgYGANCg0KRXN0aW1hdGUgY2FycmlhZ2UgYnkgc2l0ZQ0KDQpgYGB7ciBjYXJyaWFnZSBieSBzaXRlLCBpbmNsdWRlPVR9DQoNCmdic2NhcnJfc2l0ZSA8LSBnYnMuY2Fyci5ieXNpdGUuZnVuKGxpbmVsaXN0KSANCg0KDQojIENyZWF0ZSBhIGJhciBncmFwaCBvZiBnYnMgY2FycmlhZ2UgYnkgc2l0ZQ0KcGxvdCA8LSBnZ3Bsb3QoZ2JzY2Fycl9zaXRlLCBhZXMoeCA9IHNpdGUsIHkgPSBjYXJyaWFnZV9ieXNpdGUpKSArDQogICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJza3libHVlIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fZXJyb3JiYXIoDQogICAgYWVzKHltaW4gPSBsb3dlcl9jaSwgeW1heCA9IHVwcGVyX2NpKSwNCiAgICB3aWR0aCA9IDAuMiwNCiAgICBjb2xvciA9ICJibGFjayINCiAgKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHVuaXF1ZShnYnNjYXJyX2ZvcnBsb3QkdG90YWxfY2FycmlhZ2UpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIkdCUyBjYXJyaWFnZSAoJSkgYnkgRUNNIHNpdGUiLCB4ID0gIlNpdGUiLCB5ID0gIiUiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB4LWF4aXMgbGFiZWxzDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgSW5jcmVhc2Ugc2l6ZSBvZiB5LWF4aXMgbGFiZWxzDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKGIgPSAyMCkpLCAgIyBJbmNyZWFzZSBzaXplIG9mIHgtYXhpcyB0aXRsZQ0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsICAgIyBJbmNyZWFzZSBzaXplIG9mIHktYXhpcyB0aXRsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSksICAgDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0PSAwLjUpDQogICkNCg0KIyBzYXZlIHBsb3QNCmdnc2F2ZSgiQ2hhcnRzX2NhcnJpYWdlL2dic19jYXJyX3NpdGVzLmpwZWciLCBwbG90ID0gcGxvdCwgZGV2aWNlID0gImpwZWciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkNCg0KIyBjb25mIGludCBmb3Igb3ZlcmFsbCBHQlMgY2FyciBlc3RpbWF0ZQ0KdG90YWxfY2FyciA8LSBnYnNjYXJyX3NpdGUgJT4lIHNlbGVjdChjKCJ0b3RhbCIsImdic3BvcyIpKSAlPiUgc3VtbWFyaXplKGFjcm9zcyhldmVyeXRoaW5nKCksIHN1bSkpICU+JSANCiAgcm93d2lzZSgpICU+JSANCiAgICBtdXRhdGUoDQogICAgICBjb25mID0gbGlzdChiaW5vbS5jb25maW50KGdic3BvcywgdG90YWwsIG1ldGhvZHMgPSAiZXhhY3QiKSksDQogICAgICBsb3dlcl9jaSA9IGNvbmZbWzEsICJsb3dlciJdXSAqIDEwMCwNCiAgICAgIHVwcGVyX2NpID0gY29uZltbMSwgInVwcGVyIl1dICogMTAwDQogICAgKSAlPiUNCiAgICBzZWxlY3QoLWNvbmYpIA0KDQpgYGANCg0KRXN0aW1hdGUgY2FycmlhZ2UgYnkgbW9udGggb2YgRUNNIGltcGxlbWVudGF0aW9uLCBmb3IgYWxsIHNpdGVzIGNvbWJpbmVkDQoNCmBgYHtyIGNhcnJpYWdlIGJ5IG1vbnRoLCBpbmNsdWRlPVRSVUV9DQoNCmdic2NhcnJfbW9udGggPC0gZ2JzLmNhcnIuYnl5bS5mdW4obGluZWxpc3QpDQp3cml0ZS5jc3YoZ2JzY2Fycl9tb250aCwgIkRhdGFfY2FycmlhZ2UvRUNNX2NhcnJpYWdlL0NhcnJpYWdlX2VzdGltYXRlcy9nYnNjYXJyX2J5eW0uY3N2IikNCg0KIGdic2NhcnJfbW9udGgkbW9udGhfeWVhciA8LSB5bWQocGFzdGUwKGdic2NhcnJfbW9udGgkbW9udGhfeWVhciwgIi0wMSIpKSAgIyBBcHBlbmQgZGF5IHRvIGNyZWF0ZSBhIGNvbXBsZXRlIGRhdGUNCg0KIyBDcmVhdGUgYSBiYXIgZ3JhcGggb2YgZ2JzIGNhcnJpYWdlIGJ5IGFnZSBncm91cA0KcGxvdCA8LSBnZ3Bsb3QoZ2JzY2Fycl9tb250aCwgYWVzKHggPSB5bSwgeSA9IGNhcnJpYWdlX2J5eW0pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInNreWJsdWUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJHQlMgY2FycmlhZ2UgKCUpIGJ5IG1vbnRoIGFuZCB5ZWFyIChhbGwgNCBzaXRlcykiLCB4ID0gIm1vbnRoIGFuZCB5ZWFyIiwgeSA9ICIlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogICAgICBzY2FsZV94X2RhdGUoZGF0ZV9sYWJlbHMgPSAiJVktJW0iLCBkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIikgKyAgIyBGb3JtYXQgeC1heGlzDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksICAjIEluY3JlYXNlIHNpemUgb2YgeC1heGlzIGxhYmVscw0KICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksICAjIEluY3JlYXNlIHNpemUgb2YgeS1heGlzIGxhYmVscw0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLCAgIyBJbmNyZWFzZSBzaXplIG9mIHgtYXhpcyB0aXRsZQ0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpICAgIyBJbmNyZWFzZSBzaXplIG9mIHktYXhpcyB0aXRsZQ0KICApDQoNCiMgc2F2ZSBwbG90DQpnZ3NhdmUoIkNoYXJ0c19jYXJyaWFnZS9nYnNjYXJyX2J5bW9udGguanBlZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSAianBlZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQ0KDQpgYGANCg0KRXN0aW1hdGUgY2FycmlhZ2UgYnkgeWVhciBvZiBFQ00gaW1wbGVtZW50YXRpb24sIGZvciBhbGwgc2l0ZXMgY29tYmluZWQNCg0KYGBge3IgY2FycmlhZ2UgYnkgeWVhciwgaW5jbHVkZT1UUlVFfQ0KDQpnYnNfYnl5ZWFyIDwtIGdicy5jYXJyLmJ5eWVhci5mdW4obGluZWxpc3QpDQp3cml0ZS5jc3YoZ2JzX2J5eWVhciwgIkRhdGFfY2FycmlhZ2UvRUNNX2NhcnJpYWdlL0NhcnJpYWdlX2VzdGltYXRlcy9nYnNjYXJyX2J5eWVhci5jc3YiKQ0KDQpgYGANCg0KUHJvZHVjZSBzdW1tYXJpZXMgZm9yIGZsb3cgY2hhcnQgZm9yIGRhdGEgbGlua2FnZSBzdXBwb3J0IHJlcXVlc3QNCg0KYGBge3IgZmxvdyBjaGFydCBzdW1tYXJpZXN9DQoNCiMgdG90YWwgdW5pcXVlIFBJRHMgcGVyIHNpdGUNCiMgR2V0IHVuaXF1ZSBzaXRlIG5hbWVzDQp1bmlxdWVfc2l0ZXMgPC0gdW5pcXVlKGxpbmVsaXN0JHNpdGUpDQoNCiMgVXNlIGxhcHBseSB0byBjYWxjdWxhdGUgY291bnRzIGZvciBlYWNoIHNpdGUNCnNpdGVfY291bnRzIDwtIGxhcHBseSh1bmlxdWVfc2l0ZXMsIGZ1bmN0aW9uKHNpdGUpIHN1bShsaW5lbGlzdCRzaXRlID09IHNpdGUpKQ0KDQojIENvbWJpbmUgcmVzdWx0cyBpbnRvIGEgbmFtZWQgdmVjdG9yIG9yIGRhdGEgZnJhbWUgZm9yIGNsYXJpdHkNCm5hbWVzKHNpdGVfY291bnRzKSA8LSB1bmlxdWVfc2l0ZXMNCnNpdGVfY291bnRzIDwtIHVubGlzdChzaXRlX2NvdW50cykNCg0KIyBQcmludCB0aGUgcmVzdWx0cw0KcHJpbnQoc2l0ZV9jb3VudHMpDQoNCmxlbmd0aCh1bmlxdWUobGluZWxpc3QkcGlkX3N0cikpDQoNCiMgZGF0ZSByYW5nZSBieSBzaXRlDQpyYW5nZShsaW5lbGlzdCR5ZWFyLCBuYS5ybT1UKQ0KcmFuZ2UobGluZWxpc3Qkc3BlY2RhdGUsIG5hLnJtPVQpDQoNCmBgYA0KDQpMb2dpc3RpYyByZWdyZXNzaW9uIC0gYXNzb2NpYXRpb24gYmV0d2VlbiBtYXRlcm5hbCBldGhuaWNpdHkgYW5kIEdCUyBjYXJyaWFnZSwgY29udHJvbGxpbmcgZm9yIGFnZSBhbmQgZGVwcml2YXRpb24NCg0KYGBge3IgbG9naXQgcmVnLCBpbmNsdWRlPVR9DQoNCiMgLy8vLy8vLy8vLy8vLy8vLy8vLw0KIyBtb2RlbCB3aXRoIGV0aG5pY2l0eSBhcyBmaXZlIGNhdGVnb3JpZXMNCm1vZGVsX2RhdGEgPC0gbGluZWxpc3QgJT4lIGZpbHRlcighaXMubmEoaGVzX2V0aG5pY2l0eSkpICU+JSBtdXRhdGUoZ2JzX2NhciA9IGNhc2Vfd2hlbihzcGVjaWVzPT0iR3JvdXAgQiBTdHJlcHRvY29jY3VzIiB+IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGVjaWVzPT0iR0JTIG5vdCBpc29sYXRlZCIgfCBzcGVjaWVzPT0iTm8gb3JnYW5pc20gcmVjb3JkZWQiIH4gMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVzX2V0aG5pY2l0eSA9IGNhc2Vfd2hlbigoaGVzX2V0aG5pY2l0eT09IkFzaWFuIiB8IGhlc19ldGhuaWNpdHk9PSJBc2lhbl9mcm9tTElNUyIpIH4gIkFzaWFuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoaGVzX2V0aG5pY2l0eT09IkJsYWNrIiB8IGhlc19ldGhuaWNpdHk9PSJCbGFja19mcm9tTElNUyIpIH4gIkJsYWNrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoaGVzX2V0aG5pY2l0eT09Ik1peGVkIiB8IGhlc19ldGhuaWNpdHk9PSJNaXhlZF9mcm9tTElNUyIpIH4gIk1peGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoaGVzX2V0aG5pY2l0eT09Ik90aGVyIiB8IGhlc19ldGhuaWNpdHk9PSJPdGhlcl9mcm9tTElNUyIpIH4gIk90aGVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoaGVzX2V0aG5pY2l0eT09IldoaXRlIiB8IGhlc19ldGhuaWNpdHk9PSJXaGl0ZV9mcm9tTElNUyIpIH4gIldoaXRlIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVzX2V0aG5pY2l0eSA9IGFzLmZhY3RvcihoZXNfZXRobmljaXR5KSkgJT4lIGZpbHRlcihhZ2Vncm91cCE9Im5vdCBrbm93biIpICU+JSBmaWx0ZXIoIWlzLm5hKGFnZSkpICU+JSBtdXRhdGUoej0xLA0KICAgICAgICAgICAgICAgICAgICAgICAgYWdlZ3JvdXAgPSBjdXQoYWdlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gNSksICAjIFNwZWNpZnkgdGhlIGJyZWFrcyBmb3IgdGhlIGFnZSBiYW5kcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmlnaHQgPSBGQUxTRSwgICMgSW5jbHVkZSB0aGUgbGVmdCBlbmRwb2ludCwgZXhjbHVkZSB0aGUgcmlnaHQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHBhc3RlKHNlcSgwLCA5NSwgYnkgPSA1KSwgc2VxKDQsIDk5LCBieSA9IDUpLCBzZXAgPSAiLSIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGFnZWdyb3VwID0gZmN0X2NvbGxhcHNlKGFnZWdyb3VwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjAtMTQiID0gYygiMC00IiwiNS05IiwiMTAtMTQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0MCsiID0gYygiNDAtNDQiLCI0MC00NCIsIjQ1LTQ5IiwiNTAtNTQiLCI1NS01OSIsIjYwLTY0IiwiNjUtNjkiLCI3MC03NCIsIjc1LTc5IiwiODAtODQiLCI4NS04OSIsIjkwLTk0IiwiOTUtOTkiKSkpICU+JSBzZWxlY3QoeWVhcixhZ2Vncm91cCxpbWRfcXVpbnRpbGUsaGVzX2V0aG5pY2l0eSxnYnNfY2FyKSAlPiUgZmlsdGVyKCFpcy5uYShoZXNfZXRobmljaXR5KSkgDQojICU+JSBmaWx0ZXIoIWlzLm5hKGltZF9xdWludGlsZSkpDQptb2RlbF9kYXRhIDwtIGltZC5mYWMubGFiZWwuZnVuKG1vZGVsX2RhdGEpDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KbW9kZWxfZGF0YSRpbWRfcXVpbnRpbGUgPC0gYXMuZmFjdG9yKG1vZGVsX2RhdGEkaW1kX3F1aW50aWxlKQ0KbW9kZWxfZGF0YSRoZXNfZXRobmljaXR5IDwtIHJlbGV2ZWwobW9kZWxfZGF0YSRoZXNfZXRobmljaXR5LCByZWYgPSAiV2hpdGUiKQ0KbW9kZWxfZGF0YSRpbWRfcXVpbnRpbGUgPC0gcmVsZXZlbChtb2RlbF9kYXRhJGltZF9xdWludGlsZSwgcmVmID0gIjIwJSBsZWFzdCBkZXByaXZlZCIpDQptb2RlbF9kYXRhJGFnZWdyb3VwIDwtIHJlbGV2ZWwobW9kZWxfZGF0YSRhZ2Vncm91cCwgcmVmID0gIjIwLTI0IikNCm1vZGVsX2RhdGEkZ2JzX2NhciA8LSBhcy5mYWN0b3IobW9kZWxfZGF0YSRnYnNfY2FyKSANCm1vZGVsX2RhdGEgPC0gZHJvcGxldmVscyhtb2RlbF9kYXRhKQ0KDQpzdWNjZXNzZXMgPC0gc3VtKGFzLm51bWVyaWMobW9kZWxfZGF0YSRnYnNfY2FyID09IDEpKQ0KdHJpYWxzIDwtIG5yb3cobW9kZWxfZGF0YSkNCnJlc3VsdCA8LSBwcm9wLnRlc3Qoc3VjY2Vzc2VzLCB0cmlhbHMsIGNvbmYubGV2ZWwgPSAwLjk1KQ0KDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCm1vZGVsIDwtIGdsbShnYnNfY2FyIH4gaGVzX2V0aG5pY2l0eSArIGFnZWdyb3VwICsgaW1kX3F1aW50aWxlLA0KICAgICAgICAgICAgIGRhdGEgPSBtb2RlbF9kYXRhLA0KICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkNCg0Kc2FwcGx5KG1vZGVsX2RhdGEsIGZ1bmN0aW9uKHgpIGlmKGlzLmZhY3Rvcih4KSkgbGV2ZWxzKHgpKQ0KDQojIFN1bW1hcnkgb2YgdGhlIG1vZGVsIHRvIHNlZSBjb2VmZmljaWVudHMgYW5kIHN0YXRpc3RpY3MNCiAgc3VtbWFyeShtb2RlbCkNCiAgDQogICMgRXhwb25lbnRpYXRlIHRoZSBjb2VmZmljaWVudHMgdG8gZ2V0IG9kZHMgcmF0aW9zDQogIGV4cChjb2VmKG1vZGVsKSkNCiAgDQogIG1vZGVsX291dCA8LSB0aWR5KG1vZGVsLCBleHBvbmVudGlhdGUgPSBUUlVFLCBjb25mLmludCA9IFRSVUUpDQogIA0Kd3JpdGUuY3N2KG1vZGVsX291dCwgIkRhdGFfY2FycmlhZ2UvTW9kZWwvbXVsdGlsb2cuY3N2Iiwgcm93Lm5hbWVzPUYpDQoNCg0KIyAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLw0KIyByZXJ1biBtb2RlbCB3aXRoIGV0aG5pY2l0eSBhcyBiaW5hcnkgKHdoaXRlIHZzIGFueSBub24td2hpdGUgZXRobmljaXR5KQ0KDQptb2RlbF9kYXRhX2JpbmFyeSA8LSBldGhuLmJpbmFyeS5mYWMuZnVuKG1vZGVsX2RhdGEpDQoNCm1vZGVsIDwtIGdsbShnYnNfY2FyIH4gaGVzX2V0aG5pY2l0eSArIGFnZWdyb3VwICsgaW1kX3F1aW50aWxlICsgeWVhciwNCiAgICAgICAgICAgICBkYXRhID0gbW9kZWxfZGF0YV9iaW5hcnksDQogICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQ0KDQpzYXBwbHkobW9kZWxfZGF0YSwgZnVuY3Rpb24oeCkgaWYoaXMuZmFjdG9yKHgpKSBsZXZlbHMoeCkpDQoNCiMgU3VtbWFyeSBvZiB0aGUgbW9kZWwgdG8gc2VlIGNvZWZmaWNpZW50cyBhbmQgc3RhdGlzdGljcw0KICBzdW1tYXJ5KG1vZGVsKQ0KICANCiAgIyBFeHBvbmVudGlhdGUgdGhlIGNvZWZmaWNpZW50cyB0byBnZXQgb2RkcyByYXRpb3MNCiAgZXhwKGNvZWYobW9kZWwpKQ0KICANCiAgbW9kZWxfb3V0IDwtIHRpZHkobW9kZWwsIGV4cG9uZW50aWF0ZSA9IFRSVUUsIGNvbmYuaW50ID0gVFJVRSkNCiAgDQogICAgDQp3cml0ZS5jc3YobW9kZWxfb3V0LCAiRGF0YV9jYXJyaWFnZS9Nb2RlbC9tdWx0aWxvZ19ldGhuX2JpbmFyeS5jc3YiLCByb3cubmFtZXM9RikNCg0KYGBgDQoNCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4NCg0KV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuDQoNClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4=