Pakker
Laster opp de nødvendige pakkene til valideringsarbeid.
Data
Ferdig renset data fra filen datavask2018.R.
rawdt = readRDS("arskontroll2018.rds")
setDT(rawdt)
Format FDato
til %d-%m-%Y
og lage variabel dob
for fødselsdato med Dato format.
rawdt[, str(FDato)]
rawdt[ ,`:=`(dob = FDato, FDato = format(FDato, "%d-%m-%Y"))]
rawdt[, str(dob)]
Sti til RDS filer for pasient lister for de utvalgte variabler for vasking
sti.vask <- "./datavask/rds"
Funksjoner
Flere funksjoner for å sjekke data og deretter lager Excel-fil det man ønsker. Filen som er laget ligger i sub-mappen ./datavask
.
Missing verdi
Funksjon naTest
for å sjekke missing verdi.
naTest <- function(dt, var, file, save = FALSE, mvar = NULL){
# 'mvar' er for tillegg variabel
# 'var' for missing variabel som skal sjekkes
# "file" for filnavn f.eks file.xlsx
if (!is.null(mvar)) {
var.list <- c("PasientID", "Pnr", "FDato", "FNavn", "ENavn", "hospital", var, mvar)
} else {
var.list <- c("PasientID", "Pnr", "FDato", "FNavn", "ENavn", "hospital", var)
}
dtMiss <- dt[is.na(get(var)), ..var.list]
# dtMiss[, FDato := format(FDato, "%d-%m-%Y")][]
setkey(dtMiss, hospital)
if(save){
openxlsx::write.xlsx(dtMiss, file.path("./datavask",
paste(format(Sys.Date(), "%Y%m%d"), file, sep = "_"),
fsep = '/'),
asTable = TRUE, colWidths = "auto")
}
return(dtMiss)
}
Lager sykehus ID
Lager alternativ ID til å bruke i loop.
listHosp <- unique(rawdt$hospital)
hosDT <- data.table(hospital = sort(listHosp), hospID = 1:length(listHosp))
setkey(hosDT, hospID)
# saveRDS(hosDT, "hospitalID.rds")
# hosDT <- readRDS("hospitalID.rds")
hosDT
hosNavnKort <- c("Akershus", "Forde", "Haugesund", "Haukeland", "Sandessjoen", "Finnmark",
"Kristiansund", "Molde", "Namsos", "Bodo", "Trondheim",
"Stavanger", "Vestfold", "Gjovik", "Lillehammer", "Elverum", "Levanger",
"Telemark",
"Ostfold",
"Arendal",
"Kristiansand",
"Ulleval",
"Nord_norge",
"Harstad",
"Drammen",
"Alesund")
# hosDT[, kortID := hospID]
# hosDT[.(kortID = 1:26, to = hosNavnKort), on = "kortID", hosKort := i.to]
hosDT[.(hospID = 1:26, to = hosNavnKort), on = "hospID", hosKort := i.to]
Merge hospID
i datasettet
setkey(hosDT, hospital)
setkey(rawdt, hospital)
dt <- hosDT[rawdt, on = "hospital"]
setkey(dt, hospID)
Objekt per sykehus
Lage objekt for hvert sykehus som en LIST.
# hospList <- unique(dt$hospital)
hospObj <- function(x){
listHos <- unique(x$hospID)
hospLIST = list()
for (i in listHos) {
hospLIST[[i]] <- x[hospID == i, ]
}
return(hospLIST)
}
Sjekk verdi
For å sjekke at verdi skal være et vist nivå.
chkVerdi <- function(x, var, verdi, over = TRUE, mvar = NULL, all = FALSE){
# var : valgte variabel som skal sjekkes
# over : skal verdien over eller under
# mvar : tillegg variabler som skal inkluderes i output
# all : er det bare de med T1D eller alle i datasettet
# standard variabler å ha
if (is.null(mvar)){
std.var <- c("PasientID", "Pnr", "FDato", "FNavn", "ENavn", "hospID", "hospital", var)
} else {
std.var <- c("PasientID", "Pnr", "FDato", "FNavn", "ENavn", "hospID", "hospital", var, mvar)
}
if (all == FALSE) {
xt <- x[diabetes_Type1 == "Ja", ]
} else {
xt = x
}
if (over == TRUE){
.chkdt <- xt[get(var) > verdi, ..std.var]
} else {
.chkdt <- xt[get(var) < verdi, ..std.var]
}
return(.chkdt)
}
Person variabler
Lager objekt som inneholder alle person variabler
person.var <- c("PasientID", "Pnr", "FDato", "FNavn", "ENavn", "hospID", "hospital")
Nasjonalitet
Viser nasjonalitet.
list.var <- c("PasientID", "Pnr", "FDato", "FNavn", "ENavn", "hospID", "hospital", "Nasjonalitet", "nasMor", "nasFar")
dtNasj <- dt[is.na(Nasjonalitet), list.var, with = FALSE]
setkey(dtNasj, hospID)
dim(dtNasj)
# openxlsx::write.xlsx(dtNasj, file.path("./datavask",
# paste(format(Sys.Date(), "%Y%m%d"), "Nasjonalitet.xlsx", sep = "_"),
# fsep = '/'),
# asTable = TRUE, colWidths = "auto")
Retting
Koblet fra Excel fil med lister over missing nasjonalitet som Siv Janne har rettet.
xlNasj <- rio::import("./datavask/arkiv/20190507_Nasjonalitet.xlsx")
names(xlNasj)
str(xlNasj)
setDT(xlNasj)
setkey(xlNasj, Pnr)
dim(xlNasj)
xlNasj[is.na(Nasjonalitet), .N]
Koblet xlsx
Koblet filen Siv Janne rettet med utrekk fra eReg.
dtNasj2 <- xlNasj[dtNasj, on = c(Pnr = "Pnr")]
dim(dtNasj2)
names(dtNasj2)
# beholder variabler fra Excel som ble utfylt av Siv Janne
i.navn <- grep("^i.*", names(dtNasj2), value = T)
dtNasj2[, (i.navn) := NULL]
names(dtNasj2)
dtNasj2[is.na(Nasjonalitet), .N]
Koble fil med den grønne databasen
Koble direkte til Access DB, men fikk ikke til å fungere.
grennDB <- odbcDriverConnect("Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=K:\\Sensitivt\\Forskning02\\Barnediabetesregisteret_2015-5739\\Nyoppdaget diabetes\\Nyoppdaget_Type_1_diabetes_kopi.mdb")
Den grønne databasen som excelfil
greenPath <- "K:\\Sensitivt\\Forskning02\\Barnediabetesregisteret_2015-5739\\Barnediabetes og kvalitet\\Datafiler\\accessfil"
greenDT <- rio::import(file.path(greenPath, "20190507_greendb.xlsx", fsep = "\\"))
data.table::setDT(greenDT)
fdt <- grep("selsdato", names(greenDT), value = T)
greenDT[, Pnr := as.numeric(sprintf("%s%s", format(get(fdt), "%d%m%y"), Personnr))]
names(greenDT)
setkey(greenDT, Pnr)
greenSelect <- greenDT[, .(Pnr, Personnr, Fornavn, Etternavn, Etnisk_bakgr_pas, Etnisk_bakgr_mor, Etnisk_bakgr_far)]
Koble missing nasjonalitet med Grønn Access hvis de finnes info om nasjonalitet der.
names(dtNasj2)
setkey(dtNasj2, Pnr)
db2db <- greenSelect[dtNasj2, on = c(Pnr = "Pnr")]
db2db[!is.na(Etnisk_bakgr_pas), .N]
Koble info om nasjonalitet fra den grønne
db2db[is.na(Nasjonalitet), .N]
db2db[is.na(Nasjonalitet), Nasjonalitet := Etnisk_bakgr_pas] # Nasjonalitet
db2db[is.na(nasMor), nasMor := Etnisk_bakgr_mor] # Nasj. mor
db2db[is.na(nasFar), nasFar := Etnisk_bakgr_far] # Nasj. far
Beholder bare variable som kommer fra eReg
dtNasj3 <- db2db[ , ..list.var]
dtNasj3[, FDato := format(FDato, "%d-%m-%Y")]
dim(dtNasj3)
dtNasj3[is.na(Nasjonalitet), .N]
Lager renset missing Nasjonalitet og koblet med grønn datasettet.
saveRDS(dtNasj3, "./datavask/renset/nasjonalitet.rds")
# dtNasj3 <- readRDS("./datavask/renset/nasjonalitet.rds")
Merge tilgengelige nasjonalitet fra grønn datasettet og halv bearbeid Excel fra Siv Janne til hele datasettet.
names(dtNasj3)
kob_nasj <- dtNasj3[, .(Pnr, Nasjonalitet, nasMor, nasFar)] #velger relevant variabler for nasjonlitet
# Dtt <- copy(dt)
dt[dtNasj3, Nasjonalitet := ifelse(is.na(Nasjonalitet), i.Nasjonalitet, Nasjonalitet), on = "Pnr"] #erstater missing Nasjonalitet
# Dtt[is.na(Nasjonalitet), Nasjonalitet := dtNasj3[.SD, Nasjonalitet, on = "Pnr"]]
dt[dtNasj3, nasMor := ifelse(is.na(nasMor), i.nasMor, nasMor), on = "Pnr"] #erstater missing nasMor
dt[dtNasj3, nasFar := ifelse(is.na(nasFar), i.nasFar, nasFar), on = "Pnr"] #erstater missing nasFar
Liste pasienter som fortsatt mangler info for Nasjonalitet per sykehus
dtNasj4 <- dtNasj3[is.na(Nasjonalitet), ]
chk_Nasj <- hospObj(dtNasj4)
saveRDS(chk_Nasj, file.path(sti.vask, "chk_Nasj.rds"))
HbA1c
Variabelnavn lab_HbA1cAkerVerdi
. Sjekk antall missing, HbA1c < 4 og > 15.
# hba1c = validator(.file = 'hba1c.yaml')
v_hba <- validator(
lab_HbA1cAkerVerdi > 4
, lab_HbA1cAkerVerdi < 15
, !is.na(lab_HbA1cAkerVerdi)
)
c_hba <- confront(dt, v_hba, key = "Pnr")
summary(c_hba)
# dthba <- as.data.frame(c_hba)
# setDT(dthba)
# dthba[, .N, by=list(expression, value)]
# dtt = aggregate(c_hba, by='record')
# setDT(dtt)
# dtt$
# dtt[rel.fail==1, ]
barplot(c_hba, main = "HbA1c verdi")
Lager Excel fil for missing verdi HbA1c.
Liste pasienter med missing HbA1c per sykehus
chk_hba1c <- hospObj(NA_hba1c)
saveRDS(chk_hba1c, file.path(sti.vask, "chk_hba1c.rds"))
Diabetes Type
Missing diabetes Type
Sjekk missing diabetes type inkludert insulinbehandling. Hvis det er Type 1 så må det være en av de insulinbehandlingene svart JA.
DTtype <- dt[is.na(diabetes_Type1) & is.na(diabetes_Type2) & is.na(diabetes_Mody) & is.na(diabetes_AnnenDiabetes),
c(person.var,
"diabetes_Type1",
"diabetes_Type2",
"diabetes_Mody",
"diabetes_AnnenDiabetes",
"beh_ferdig_bland",
"beh_ins_beh",
"beh_ins_pumpe"), with=FALSE]
DTtype
setkey(DTtype, hospID)
chk_diabtype <- hospObj(DTtype)
saveRDS(chk_diabtype, file.path(sti.vask, "chk_diabtype.rds"))
# openxlsx::write.xlsx(DTtype, file.path("./datavask", paste(format(Sys.Date(), "%Y%m%d"), "diabetesType.xlsx", sep = "_"), fsep = '/'),
# asTable = TRUE, colWidths = "auto")
Diet og diabetes Type 1
Hvis det er diett så kan det ikke være diabetes Type 1.
dt[beh_diett == "Ja", .N, by=.(diabetes_Type1)]
diet1 <- dt[diabetes_Type1 == "Ja" & beh_diett == "Ja", c(person.var, "diabetes_Type1", "beh_anti_tab", "beh_diett"), with=F]
diet1
Hvis bruker antidiabetes tabeletter kan ikke være diabetes Type 1.
dt[beh_anti_tab == "Ja", .N, by=.(diabetes_Type1)]
diet2 <- dt[diabetes_Type1 == "Ja" & beh_anti_tab == "Ja", c(person.var, "diabetes_Type1", "beh_anti_tab", "beh_diett"), with=F]
diet2
Koble de to sammen
diet22 <- diet2[!diet1, on="Pnr"] #ekluderer den som finnes på begge
diet22
diet12 <- rbindlist(list(diet1, diet22))
Lager liste
chk_diet <- hospObj(diet12)
saveRDS(chk_diet, file.path(sti.vask, "chk_diet.rds"))
Vekt og høyde
Kontrollerer at Høyde under 2 meter og Vekt under 100 kg. Variabel BMI
lages med formel: \[\frac{Vekt(kg)}{Høyde(m)^2}\]
Lager __BMI_ variabel.
## BMI
dt[!is.na(inn_Vekt) & !is.na(inn_Lengde), bmi := (inn_Vekt/inn_Lengde/inn_Lengde)*10000]
Sjekk antall for unvanlige tall bl.a høyde, vekt og bmi.
## validering
v_kg = validator(
inn_Lengde > 10,
inn_Lengde < 200,
inn_Vekt > 20,
inn_Vekt < 100,
BMI := (inn_Vekt/inn_Lengde/inn_Lengde)*10000,
BMI < 30,
BMI < 35,
BMI > 11
)
con_kg = confront(dt, v_kg, key="Pnr")
# con_kg
summary(con_kg)
barplot(con_kg, main = "Vekt, Høyde og BMI")
Høyde under 10 cm
dt[inn_Lengde < 10, person.var, with=F]
BMI under 11
bmi_pas.list=c("PasientID", "Pnr", "FDato", "FNavn", "ENavn", "hospID", "hospital", "inn_Lengde", "inn_Vekt")
pasBMI_11 <- dt[bmi < 12, ..bmi_pas.list]
setkey(pasBMI_11, hospID)
dim(pasBMI_11)
chk_bmi_under12 <- hospObj(pasBMI_11)
saveRDS(chk_bmi_under12, file.path(sti.vask, "chk_bmi_under12.rds"))
#
# openxlsx::write.xlsx(pasBMI, file.path("./datavask", paste(format(Sys.Date(), "%Y%m%d"),
# "bmi_under11.xlsx", sep = "_"), fsep = '/'),
# asTable = TRUE, colWidths = "auto")
BMI over 35
pasBMI35 <- dt[bmi > 35, ..bmi_pas.list]
dim(pasBMI35)
chk_bmi_over35 <- hospObj(pasBMI35)
saveRDS(chk_bmi_over35, file.path(sti.vask, "chk_bmi_over35.rds"))
Alder
Sjekk minimum alder under 0 og maksimum over 18 år. Variabel alder
må lages først.
Validerings data
v_age <- validator(
alder > 0,
alder < 19
)
c_age <- confront(dt, v_age, key="Pnr")
summary(c_age)
Plotting valideringsdata
barplot(c_age, main = "Alder under 0 og over 18 år")
Liste av pasienter over 19 år per sykehus.
alder19 <- dt[alder > 18.999, c(person.var, "inn_Dato", "alder"), with=F]
dim(alder19)
chk_alder19 <- hospObj(alder19)
saveRDS(chk_alder19, file.path(sti.vask, "chk_alder19.rds"))
Diabetesvarighet
Diabetesvarighet definert som lengde fra diagnose dato inn_DiagDato
til kontroll tidspunktet inn_Dato
. Ny variabel lages for diabetesvarighet som heter diagVar
.
dt[, diagVar := as.numeric(round(as.period(interval(diagDato, inn_Dato)/duration(n=1, unit="years")), digits = 1))]
Sjekk for diabetesvarighet under 0 år.
c_diagv <- check_that(dt, diagVar > 0)
summary(c_diagv)
validate::aggregate(c_diagv)
head(aggregate(c_diagv, by='record')) #see hvilken linje som har feil
sort(c_diagv) # se hvilken verdi som gjør feilen
List av pasienter
varighet0 <- dt[diagVar < 0.0007, c(person.var, "diagDato", "inn_Dato", "diagVar"), with=F]
dim(varighet0)
chk_varihet0 <- hospObj(varighet0)
saveRDS(chk_varihet0, file.path(sti.vask, "chk_varihet0.rds"))
Menarche
Alder ved menarche. Sjekk for minimun alder av 9 år og maksimum av 16.5 år. Ny variabel alderArc
lages. OBS! husk dob brukes for fødselsdato siden FDato er string.
dt[!is.na(inn_MenarcheTidspunkt),
alderArc := as.numeric(round(as.period(interval(dob, inn_MenarcheTidspunkt)/duration(n=1, units = 'years')),
digits = 1))]
Validering for alderArc
.
v_ageMrch <- validator(
alderArc > 9,
alderArc < 16.5
)
c_ageMrch <- confront(dt, v_ageMrch)
Tabell for antall som er under 9 år og over 16.5 år.
summary(c_ageMrch)
Liste over pasienter med menarche under 9 år.
Liste over pasienter med menarche over 16.5 år.
Diagnose Alder
Diagnose alder min 0 og max 18 år. Først lage variabel diagAlder.
dt[, diagAlder := as.numeric(round(as.period(interval(dob, diagDato)/duration(n=1, units = 'years')), digits = 1))]
v_alderDiag <- validator(
diagAlder > 0,
diagAlder < 18
)
c_alderDiag <- confront(dt, v_alderDiag)
sort(c_alderDiag) # se på verdier for å være feil
summary(c_alderDiag)
List over pasienter for diagnose alder som ikke møter forventingene. Diagnose alder under 1 år.
diagAlder0 <- dt[diagAlder < 0.9, c(person.var, "diagDato", "diagAlder", "diabetes_Type1"), with=F]
dim(diagAlder0)
setkey(diagAlder0, hospID)
chk_diagAlder0 <- hospObj(diagAlder0)
saveRDS(chk_diagAlder0, file.path(sti.vask, "chk_diagAlder0.rds"))
Blodtrykk
Systolisk skal ikke være over 140 og diastolisk skal ikke være under 45
v_blodtrykk <- validator(
inn_Blodtrykk_s < 140,
inn_Blodtrykk_d > 45
)
c_blodtrykk <- confront(dt, v_blodtrykk)
summary(c_blodtrykk)
Lister over pasienter som ikke møter kravet.
blodtrk_s <- dt[inn_Blodtrykk_s > 140, c(person.var, "inn_Blodtrykk_s", "inn_Blodtrykk_d"), with=F]
blodtrk_d <- dt[inn_Blodtrykk_d < 45, c(person.var, "inn_Blodtrykk_s", "inn_Blodtrykk_d"), with=F]
blodtrk <- rbindlist(list(blodtrk_d, blodtrk_s), use.names = TRUE)
setkey(blodtrk, hospID)
chk_blodtrykk <- hospObj(blodtrk)
saveRDS(chk_blodtrykk, file.path(sti.vask, "chk_blodtrykk.rds"))
LDL
Liste over alle LDL som er >=2.6
. Bruk LDL ikke fastende lab_lip_LDL_2
, men hvis missing fastende LDL skal brukes hvis det finnes lab_lip_LDL
.
Lager variabel ldl3 for ikke fastende LDL og legger til fastende hvis missing.
dt[, lapply(.SD, str), .SDcols = c("lab_lip_LDL_2", "lab_lip_LDL")]
dt[ , ldl3 := lab_lip_LDL_2][is.na(lab_lip_LDL_2), ldl3 := lab_lip_LDL]
dt[is.na(lab_lip_LDL_2), .N]
dt[is.na(ldl3), .N]
Sjekk antall som er over og lik 2.6.
v_ldl <- validator(
ldl3 <= 2.6,
ldl3 < 2.7
)
c_ldl <- confront(dt, v_ldl)
sort(c_ldl)
summary(c_ldl)
Liste pasienter
ldl26 <- dt[ldl3 >=2.6, c(person.var, "lab_lip_LDL_2", "lab_lip_LDL"), with=F]
dim(ldl26)
setkey(ldl26, hospID)
chk_LDL26 <- hospObj(ldl26)
saveRDS(chk_LDL26, file.path(sti.vask, "chk_LDL26.rds"))
Behandling
Forskjellige variabler for behandling av diabetes. Gjelder bare for T1D.
Multiinjuksjon
Gjelder bare for pasienter med T1D. Superhurtigvirkende beh_ins_beh_hurtig_ie_dogn
plus langtidsvirkende beh_ins_beh_lang_ie_dogn
del med kroppsvekt inn_Vekt
. Normal verdi skal være < 2.
dt[, str(.SD), .SDcols = c("beh_ins_beh_hurtig_ie_dogn", "beh_ins_beh_lang_ie_dogn", "inn_Vekt")]
# dt[, lapply(.SD, str), .SDcols = c("beh_ins_beh_hurtig_ie_dogn", "beh_ins_beh_lang_ie_dogn")]
Endre NA i begge variablene til 0. Lager variabel multinj for summan av superhurtigvirkende og langtidsvirkende behandling og del med kroppsvekt.
dt[ , beh_hurtigNA0 := beh_ins_beh_hurtig_ie_dogn][is.na(beh_hurtigNA0), beh_hurtigNA0 := 0]
dt[ , beh_langtidNA0 := beh_ins_beh_lang_ie_dogn][is.na(beh_langtidNA0), beh_langtidNA0 := 0]
dt[, multinj := round((beh_hurtigNA0 + beh_langtidNA0)/inn_Vekt, digits = 2)]
dt[, .(beh_ins_beh_hurtig_ie_dogn, beh_ins_beh_lang_ie_dogn, inn_Vekt, multinj)]
Liste pasienter. Gjelder bare pasienter med T1D.
beh_multi <- dt[diabetes_Type1=="Ja" & multinj > 2, c(person.var, "beh_ins_beh_hurtig_ie_dogn", "beh_ins_beh_lang_ie_dogn", "inn_Vekt"), with=F]
dim(beh_multi)
setkey(beh_multi, hospID)
chk_multi <- hospObj(beh_multi)
saveRDS(chk_multi, file.path(sti.vask, "chk_multi.rds"))
Insulinpumpe
Normal verdig skal ikke være < 2
dt[, str(.SD), .SDcols = c("beh_ins_type_ie_basal", "beh_ins_type_ie_bolus", "inn_Vekt")]
Endre NA i begge variablene til 0. Lager variabel pumpe for summan av superhurtigvirkende og langtidsvirkende behandling og del med kroppsvekt.
dt[ , beh_basalNA0 := beh_ins_type_ie_basal][is.na(beh_basalNA0), beh_basalNA0 := 0]
dt[ , beh_bolusNA0 := beh_ins_type_ie_bolus][is.na(beh_bolusNA0), beh_bolusNA0 := 0]
dt[, pumpe := round((beh_basalNA0 + beh_bolusNA0)/inn_Vekt, digits = 2)]
dt[, .(beh_ins_type_ie_basal, beh_ins_type_ie_bolus, inn_Vekt, pumpe)]
Lister av pasienter med T1D
beh_pumpe <- dt[diabetes_Type1 == "Ja" & pumpe > 2, c(person.var,"beh_ins_type_ie_basal", "beh_ins_type_ie_bolus", "inn_Vekt"), with=F]
dim(beh_pumpe)
chk_pumpe <- hospObj(beh_pumpe)
saveRDS(chk_pumpe, file.path(sti.vask, "chk_pumpe.rds"))
Pumpe annet
Variabelnavn beh_ins_pumpe_annet
er en fritekst. De må standardiseres.
pumpeDT
pumpe_ny beh_ins_pumpe_annet pumpe_all
1: Medtronic Minimed 640G MINIMED 640G Medtronic Minimed 640G
2: Roche? Spirit Combo accu check combo spirit Roche Spirit Combo
3: Roche? accu-check Roche
4: Medtronic Paradigme Veo PARADIGM Medtronic Paradigme Veo
5: Roche? Spirit Combo ACCU CHECK SPIRIT COMBO Roche Spirit Combo
6: Roche? Spirit Combo ACCU CHECK COMBO SPRIT Roche Spirit Combo
7: Roche? accucheck Roche
8: Medtronic Paradigme Veo paradigm Medtronic Paradigme Veo
9: Medtronic Minimed 640G MINIMED 640 G Medtronic Minimed 640G
10: Roche? Spirit Combo accu-chech combo spirit Roche Spirit Combo
11: Roche Insight Accucheck inlight Roche Insight
12: Roche? accocheck Roche
13: Roche? Accu-check aviva Roche
14: Medtronic Minimed 640G Metronic Minimed 640g Medtronic Minimed 640G
15: Medtronic Paradigme Veo Medtronic Paradign Medtronic Paradigme Veo
16: Roche? Accucheck Roche
17: Roche? Accu Check Roche
18: Medtronic Minimed 640G Medtronic Minimed 640 G Medtronic Minimed 640G
19: Roche? ACCU Check Roche
20: Medtronic Minimed 640G Medtronic Minimed 640G Medtronic Minimed 640G
21: Animas Vibe Animas Vibe Animas Vibe
22: Medtronic Paradigme Veo Medtronic Paradigme Veo Medtronic Paradigme Veo
23: Medtronic Medtronic Mini Med Medtronic
24: Omnipod Omnipod Omnipod
25: Medtronic Minimed 640 G Medtronic 640 G Medtronic Minimed 640 G
26: ukjent ukjent ukjent
27: Roche? Spirit Combo Spirit Combo Roche Spirit Combo
28: Medtronic 715 Medtronic 715 Medtronic 715
29: Omnipod Ominpod Omnipod
30: Omnipod Omipod Omnipod
31: Roche Insight Accucheck Insight Roche Insight
32: Roche? Spirit Combo ACCU CHECK COMBO SPIRIT Roche Spirit Combo
33: Roche? Spirit Combo Accucheck Combo Spirit Roche Spirit Combo
34: Roche? Spirit Combo Accu chek cambo spirit Roche Spirit Combo
35: Roche? Spirit Combo Accu chek combospint Roche Spirit Combo
36: Roche Insight Accu chek Insight Roche Insight
37: Roche Insight Accu chek linsight Roche Insight
38: Roche Insight Roche Insight Roche Insight
39: Medtronic Paradigme Veo Medtronic Paradigme Medtronic Paradigme Veo
40: Medtronic Minimed 640G Medtronic Minimed (fra nov.17) Medtronic Minimed 640G
41: Medtronic Minimed 640G multimed640 Medtronic Minimed 640G
42: Medtronic Paradigme Veo Paradigm Veo Medtronic Paradigme Veo
43: Medtronic Paradigme Veo paradigme Medtronic Paradigme Veo
44: Medtronic? Minimed 640G multimed 640g Medtronic Minimed 640G
45: Medtronic? Minimed 640G minimed 640g Medtronic Minimed 640G
46: Medtronic? Minimed 640G multimed Medtronic Minimed 640G
47: Medtronic? Minimed 640G Medtronic 640G Medtronic Minimed 640G
48: Medtronic? Minimed 640G Minimed40g Medtronic Minimed 640G
49: Medtronic? Minimed 640G multimed 640G Medtronic Minimed 640G
50: Medtronic Paradigme Veo veo Medtronic Paradigme Veo
51: Medtronic? Minimed 640G multimed640 g Medtronic Minimed 640G
52: Roche? Accu check Roche
53: Roche? Accu-Chek Roche
54: Roche? accu check Roche
pumpe_ny beh_ins_pumpe_annet pumpe_all
Merge de nye standardiserte navnene til hele datasettet.
dt[.(beh_ins_pumpe_annet = pumpeOld, to = pumpeNy), on = "beh_ins_pumpe_annet", pumpe_txt := i.to ]
Injeksjoner hurtigvirk
Antall injeksjoner hurtigvirkende insulin per døgn. Den skal ikke være mer en 10
str(dt$beh_ins_beh_hurtig_inj_dogn)
Liste over pasienter med verdi over 10.
injek.dt <- chkVerdi(dt, "beh_ins_beh_hurtig_inj_dogn", verdi = 10)
dim(injek.dt)
chk_injek <- hospObj(injek.dt)
saveRDS(chk_injek, file.path(sti.vask, "chk_injek.rds"))
Injeksjoner langtidvirk
Antall injeksjoner langtidsvirkende insulin per døgn. Den skal ikke være mer enn 2
str(dt$beh_ins_beh_lang_ie_dogn)
Liste over pasienter
injek.lang <- chkVerdi(dt, "beh_ins_beh_lang_inj_dogn", verdi = 2)
dim(injek.lang)
Bolusdoser
Antall bolusdoser per døgn. Den skal ikke være mer enn 12.
str(dt$beh_ins_tot_enh_dogn_bolus)
bolusdos <- chkVerdi(dt, "beh_ins_tot_enh_dogn_bolus", verdi = 12)
dim(bolusdos)
chk_bolusdos <- hospObj(bolusdos)
saveRDS(chk_bolusdos, file.path(sti.vask, "chk_bolusdos.rds"))
Nålskifte
Antall nålskifte. Skal ikke være mer enn 5
str(dt$beh_ins_nal_skift_dogn)
Liste over pasienter
nalskift <- chkVerdi(dt, "beh_ins_nal_skift_dogn", verdi = 5)
dim(nalskift)
setkey(nalskift, hospID)
chk_nalskift <- hospObj(nalskift)
saveRDS(chk_nalskift, file.path(sti.vask, "chk_nalskift.rds"))
Albuminundersøkelse
Urin prøve missing
Sjekk for mangler eller missing for urinprøve lab_res_1prove
. Hvis missing skal det sjekkes om de er riktig missing ved å se at det ble besvart NEI for lab_urinprove
eller at det var skrevet årsak i lab_urinprove_hvorfor
.
dt[, str(.SD), .SDcols = c("lab_res_1prove", "lab_urinprove", "lab_urinprove_hvorfor")]
Pasient lister
urinprov <- dt[is.na(lab_res_1prove), ][!is.na(lab_urinprove_hvorfor) | lab_urinprove == TRUE, c(person.var, "lab_res_1prove"), with=F]
dim(urinprov)
setkey(urinprov, hospID)
chk_urinprove <- hospObj(urinprov)
saveRDS(chk_urinprove, file.path(sti.vask, "chk_urinprove.rds"))
Albumin/kreatinin
Henter kategoriske variabelnavn lab_res_1menhet
siden den ikke er kodet i tall.
mgmol <- grep("^mg/mmol", dt$lab_res_1menhet, value = T)[1]
mgl <- grep("^mg/l", dt$lab_res_1menhet, value = T)[1]
mg/mmol
Hvis (lab_res_1menhet) = 2 (mg/mmol) skal (lab_res_1prove) skal det IKKE være >=2.5.
mgmol.dt <- dt[lab_res_1menhet == mgmol, ]
mgmol.sub <- chkVerdi(mgmol.dt, var = "lab_res_1prove", verdi = 2.4999)
dim(mgmol.sub)
chk_mgmol <- hospObj(mgmol.sub)
saveRDS(chk_mgmol, file.path(sti.vask, "chk_mgmol.rds"))
mg/l
Hvis (lab_res_1menhet) = 1 (mg/l) skal (lab_res_1prove) skal det IKKE være >=30
mgl.dt <- dt[lab_res_1menhet == mgl, ]
mgl.sub <- chkVerdi(mgl.dt, var = "lab_res_1prove", verdi = 29.9999)
dim(mgl.sub)
chk_mgl <- hospObj(mgl.sub)
saveRDS(chk_mgl, file.path(sti.vask, "chk_mgl.rds"))
mikrog/min
Hvis (lab_res_1menhet) = 3 (mikrog/min) skal (lab_res_1prove) skal det IKKE være >=20
dt[, .N, by=.(lab_res_1menhet)]
Ikke gjort fordi ingen har den verdien.
Persisterende microalbumniuri
Hvis svar til variabel lab_res_persmikro
er JA, tar ut liste av pasienter.
permicro <- dt[lab_res_persmikro == "Ja", c(person.var, "lab_res_persmikro"), with=F]
dim(permicro)
chk_permicro <- hospObj(permicro)
saveRDS(chk_permicro, file.path(sti.vask, "chk_permicro.rds"))
Insulinsjokk
Sjekk at und_inssjokk_ant
er IKKE mer enn 8
str(dt$und_inssjokk_ant)
dt[und_inssjokk_ant > 8, .(und_inssjokk_ant, hospital)]
ins_sjokk <- chkVerdi(dt, "und_inssjokk_ant", verdi = 8)
dim(ins_sjokk)
chk_inssjokk <- hospObj(ins_sjokk)
saveRDS(chk_inssjokk, file.path(sti.vask, "chk_inssjokk.rds"))
DKA
Sjekk at und_ketoacidose_ant
er IKKE mer en 5
str(dt$und_ketoacidose_ant)
Liste av pasienter
ketdose <- chkVerdi(dt, "und_ketoacidose_ant", verdi = 5)
dim(ketdose)
chk_dka <- hospObj(ketdose)
chk_dka
Følinger
Sjekk at und_foling_ant
er IKKE mer enn 10
str(dt$und_foling_ant)
Liste over pasienter
foling.dt <- chkVerdi(dt, "und_foling_ant", 10)
dim(foling.dt)
chk_foling <- hospObj(foling.dt)
saveRDS(chk_foling, file.path(sti.vask, "chk_foling.rds"))
Undersøkelse
dt[, .N, by=.(und_laser)]
dt[, .N, by=.(und_Retinopati)]
dt[, .N, by=.(und_Retinopati_valg)]
dt[, .N, by=.(und_periferneu)]
Laserbehandling
Liste for alle som svarte JA
laserJa <- dt[und_laser == "Ja", c(person.var, "und_laser"), with=F]
dim(laserJa)
chk_laserJa <- hospObj(laserJa)
saveRDS(chk_laserJa, file.path(sti.vask, "chk_laserJa.rds"))
Retinopati
Liste over alle som svarte Ja på und_Retinopati
og har svart på und_Retinopati_valg
retino1 <- dt[und_Retinopati == "Ja", c(person.var, "und_Retinopati", "und_Retinopati_valg"), with=F]
retino2 <- dt[!is.na(und_Retinopati_valg), c(person.var, "und_Retinopati", "und_Retinopati_valg"), with=F]
ret11 = retino1[!retino2, on = "Pnr"] #pasient som befinnes bare i retino1
retino12 <- rbindlist(list(ret11, retino2))
dim(retino12)
chk_retino <- hospObj(retino12)
saveRDS(chk_retino, file.path(sti.vask, "chk_retino.rds"))
Perifer neuropati
Liste over alle som svarte JA
dt[, .N, by=.(und_periferneu)]
Liste av pasienter
perif.dt <- dt[und_periferneu == "Ja", c(person.var, "und_periferneu"), with=F]
dim(perif.dt)
chk_perineuro <- hospObj(perif.dt)
saveRDS(chk_perineuro, file.path(sti.vask, "chk_perineuro.rds"))
Pasienter med flere årskontroll
Disse er pasienter som gjennomført årskontroll mer enn to ganger. Årskontroll skal bare telles en gang. Derfor skal det være KUN en årskontroll per pasient og sykehuset må kontaktes for å bekrefte hvilket av dem skal beholdes.
dim(dt)
dt[duplicated(Pnr), .(Pnr)]
dt[PasientID == 7071, .(PasientID, Pnr, OppholdID, hospital, FNavn, ENavn, diagDato, diabetes_Type1, inn_Dato, yr)]
sykehus i Vestfold har bekreftet at årskontroll som var tatt i 18.04.2018 er den gjeldene.
dt <- dt[OppholdID != 115449, ]
dim(dt)
Pasienter med ingen eller flere diagnoser
Noen pasienter har blitt registrert med flere diagnoser eller ingen. Hvem er disse? Når liste av pasienter er laget så skal Siv Janne kontakter sykehusene for varifisering.
## Demografisk variabler
demoVar <- c("PasientID", "Pnr", "hospital", "hospID", "hosKort", "alder", "Kjonn", "diagVar", "diagAlder")
## diabetes variabler
diabetesVar = c("diabetes_Type1", "diabetes_Type2", "diabetes_Mody", "diabetes_Kir62", "diabetes_SekDiabetes", "diabetes_AnnenDiabetes", "diabetes_UkjentDiabetes")
## lage subset
diabDT <- dt[, c(demoVar, diabetesVar), with = F]
## trim whitespace
##-----------------------
for (j in diabetesVar){
if(class(diabDT[[j]]) == 'character')
set(diabDT, j = j, value = trimws(diabDT[[j]]))
}
## ## vanlig loop for whitespace deletion. OBS!! Treggere enn loop set
## for (j in diabetesVar){
## diabDT[, (j) := trimws(get(j))]
## }
## Check whitespace er borte
diabDT[Pnr == 7090197481, .(diabetes_Kir62)][[1]]
## ## måtte gjøre det nesten manuelt siden trimws for alle kolonne i data.table ikke
## ## funker som det bør være
## diabDT[!is.na(diabetes_Kir62), diabetes_Kir62 := trimws(diabetes_Kir62)]
## Lage en long dataset
diabLg <- melt(data = diabDT,
id.vars = demoVar,
measure.vars = diabetesVar,
variable.name = "diabType",
value.name = "janei")
diabLL <- copy(diabLg)
## omkode all som svarte Ja til diabetes Type til 1
diabLL[.(janei = "Ja", diabType = diabetesVar, to = 1L),
on = c("janei", "diabType"), diab := i.to]
## Konvertere til wide
diabWide <- dcast(data = diabLL,
formula = Pnr + hospID + hosKort ~ diabType, value.var = "diab")
### Sjekk
diabWide
diabWide[diabetes_Kir62 == 1, .(Pnr, hospID, hosKort, diabetes_Kir62)]
## bytt NA til 0
for (j in diabetesVar){
set(diabWide, which(is.na(diabWide[[j]])), j, value = 0L)
}
## Summere for å finne missing for alt diabetes type
diabWide[, hvem := rowSums(.SD), .SDcols = diabetesVar ]
diabWide[, .N, by = hvem]
## som har ingen type diabetes
## ----------------------------
diaN <- diabWide[hvem == 0, .(Pnr)][[1]]
diaNoN <- list()
for (j in diaN){
nv <- paste0("a", j) #må hav valid navn og ikke bare tall
dd <- dt[Pnr == j, c("Pnr", "hospID", "hosKort", diabetesVar), with = F]
diaNoN[[nv]] <- dd
}
diaNoNperson <- rbindlist(diaNoN)
diaNoNperson
diaNoNperson[, .(Pnr, hospID, hosKort, diabetes_Kir62)]
vetIkkeDiab <- diaNoNperson[[1]]
## Siv Janne har kontrollet og disse er resultat som må rettes i datasettet
vetdt1 <- vetIkkeDiab[c(1:2, 4:5, 8:12)] #DT1
vetMody <- vetIkkeDiab[6] #Mody
diaNoDiag <- copy(diaNoNperson)
diaNoDiag[Pnr %in% vetdt1, diabetes_Type1 := "Ja"] #DT1
diaNoDiag[Pnr == vetMody, diabetes_Mody := "Ja"]
diaNoDiag
## replace main datasets value with the corrected values
diabVar.i <- paste0("i.", diabetesVar)
dt[diaNoDiag, (diabetesVar) := mget(diabVar.i), on = "Pnr"]
## pasienter med to type diabetes
## -----------------------------
dia2 <- diabWide[hvem == 2, .(Pnr)][[1]]
dia2DT <- list()
for (j in dia2){
nv <- paste0("a", j) #må hav valid navn og ikke bare tall
dd <- dt[Pnr == j, c("Pnr", "hospID", "hosKort", diabetesVar), with = F]
dia2DT[[nv]] <- dd
}
dia2DTperson <- rbindlist(dia2DT)
dia2DTperson
diabVar1 = c("diabetes_Type2", "diabetes_Mody", "diabetes_Kir62", "diabetes_SekDiabetes", "diabetes_AnnenDiabetes", "diabetes_UkjentDiabetes")
dia2DTperson[diabetes_Type1 == "Ja", (diabVar1) := NA]
## personer med flere diagnoser
dt1person <- dia2DTperson[, Pnr]
dt1person
## De som har DT1 og annen type blir bare med DT1.
perdt1 <- dt1person[-c(1:2)] #DT1
perkir <- dt1person[2] #kir62
perann <- dt1person[1] #annen
## Tømme alle verdie for de med minst to så legge tilbake det riktige etter at
## Siv Janne har kontorllert
dia2diag <- copy(dia2DTperson)
dia2diag[, (diabetesVar) := NA]
dia2diag[Pnr %in% perdt1, diabetes_Type1 := "Ja"] #DT1
dia2diag[Pnr == perkir, diabetes_Kir62 := "Ja"] #Kira62
dia2diag[Pnr == perann, diabetes_AnnenDiabetes := "Ja"] #Annen
dia2diag
## bytte verdi i hoved datasettet med korrigert verdi
dt[dia2diag, (diabetesVar) := mget(diabVar.i), on = "Pnr"]
Save Datasett
Lager bearbeidet datasettet for validering. Datasettet inneholder alle nye lagde variablene som brukes til å gjøre validering og senere skal brukes i analysen bl.a
- hospital : Sykehus
- dob : Fødselsdato i dato format
- alder : Alder for pasienter
- diagVar : Diabetes varighet
- alderArc : Alder ved menarche
- diagAlder : Alder ved diagnose
- diagDato : Diagnose dato for årskontroll siden inn_Diagdato finnes bare i førstegangsregistrering
- pump_txt : Omkodet beh_ins_pumpe_annet som er fritekst til et standardiserte liste etter at Siv Janne har gått gjennom listen.
saveRDS(dt, "validert_arskontroll2018.rds")
dt <- readRDS("validert_arskontroll2018.rds")
Excel per sykehus
Lager Excel fil per Sykehus
## lage tom dataset hvis NULL
nullDT <- function(x){
nullNames <- names(x[lengths(x)!=0][[1]]) #får colnames
missCol <- data.table(matrix(ncol = length(nullNames), nrow = 0)) #lage tom data.table
data.table::setnames(missCol, names(missCol), nullNames) #gir tom data.table riktig colnames
ikkeMis <- which(lengths(x)!=0) #index som ikke er NULL
# nullInd <- which(lengths(x)==0) #find index with NULL
sykInd <- hosDT$hospID
missInd <- which(!(sykInd %in% ikkeMis))
for (i in missInd){
x[[i]] <- missCol
}
return(x)
}
setkey(hosDT, hospID)
## Lage Excel per sykehus
for (i in hosDT$hospID) {
# liste over CHK variabelene
var1 <- nullDT(chk_hba1c)[[i]]
var2 <- nullDT(chk_Nasj)[[i]]
var3 <- nullDT(chk_diabtype)[[i]]
var4 <- nullDT(chk_bmi_under12)[[i]]
var5 <- nullDT(chk_bmi_over35)[[i]]
var6 <- nullDT(chk_diet)[[i]]
var7 <- nullDT(chk_alder19)[[i]]
var8 <- nullDT(chk_varihet0)[[i]]
var9 <- nullDT(chk_menarch9)[[i]]
var10 <- nullDT(chk_menarch16)[[i]]
var11 <- nullDT(chk_diagAlder0)[[i]]
var12 <- nullDT(chk_blodtrykk)[[i]]
var13 <- nullDT(chk_LDL26)[[i]]
var14 <- nullDT(chk_multi)[[i]]
var15 <- nullDT(chk_pumpe)[[i]]
var16 <- nullDT(chk_injek)[[i]]
var17 <- nullDT(chk_bolusdos)[[i]]
var18 <- nullDT(chk_nalskift)[[i]]
var19 <- nullDT(chk_urinprove)[[i]]
var20 <- nullDT(chk_mgmol)[[i]]
var21 <- nullDT(chk_mgl)[[i]]
var22 <- nullDT(chk_permicro)[[i]]
var23 <- nullDT(chk_inssjokk)[[i]]
var24 <- nullDT(chk_foling)[[i]]
var25 <- nullDT(chk_laserJa)[[i]]
var26 <- nullDT(chk_retino)[[i]]
var27 <- nullDT(chk_perineuro)[[i]]
syk.list <- list("hba1c" = var1,
"nasjonalitet" = var2,
"diaType" = var3,
"bmi_under12" = var4,
"bmi_over35" = var5,
"diet" = var6,
"alder19" = var7,
"varighet" = var8,
"menarch9" = var9,
"menarch16" = var10,
"diagAlder0" = var11,
"blodtrykk" = var12,
"LDL26" = var13,
"multiinjekt2" = var14,
"pumpe2" = var15,
"injek_hurtig10" = var16,
"bolus_dose" = var17,
"naalskift" = var18,
"urin" = var19,
"urin_mg_mmol" = var20,
"urin_mgl" = var21,
"persist_micro" = var22,
"insulinsjokk" = var23,
"foling" = var24,
"laserJA" = var25,
"retinopati" = var26,
"perifer_neuro" = var27
)
sykehusNavn <- hosDT$hosKort[hosDT$hospID==i]
stil <- openxlsx::createStyle(border = "top", fgFill = "#42ebf4")
openxlsx::write.xlsx(syk.list, file.path("./datavask/per_sykehus", paste0(format(Sys.Date(), "%Y%m%d"), "_", sykehusNavn, ".xlsx"), fsep = '/'),
headerStyle = stil, borders = "rows", colWidths = "auto")
}
Excel test
Manuelt test
# var1 <- chk_hba1c[[1]]
# var2 <- chk_Nasj[[1]]
# var3 <- chk_diabtype[[1]]
# var4 <- chk_bmi_under12[[1]]
# var5 <- chk_bmi_over35[[1]]
# var6 <- chk_diet[[1]]
#
# ## tar bort alle NULL
# chk_diet[lengths(chk_diet) != 0]
#
# ## get colnames if missing
# nullNames <- names(chk_diet[lengths(chk_diet)!=0][[1]])
# missCol <- data.table(matrix(ncol = length(nullNames), nrow = 0))
# missCol
# data.table::setnames(missCol, names(missCol), nullNames)
#
#
# data.table::setcolorder(var1, person.var)
# var1
#
# syk.list <- list("hba1c" = var1,
# "nasjonalitet" = var2,
# "diaType" = var3,
# "bmi_under12" = var4,
# "bmi_over35" = var5,
# "diet" = var6)
#
# openxlsx::write.xlsx(syk.list, file.path("./datavask", paste(format(Sys.Date(), "%Y%m%d"), "Akershus.xlsx", sep = "_"), fsep = '/'),
# asTable = TRUE, colWidths = "auto")
Prøver å løse ved bruk av funksjon.
## List over "CHECK" variabler
## ------------------------------
# chk_var <- grep("^chk_", ls(), value = TRUE)
# eval(as.name(chk_var[1]))
# chk_var <- c("chk_Nasj",
# "chk_hba1c",
# "chk_diabtype",
# "chk_diet",
# "chk_bmi_under12",
# "chk_bmi_over35")
#
#
# list_var <- list(chk_Nasj,
# chk_hba1c,
# chk_diabtype,
# chk_diet,
# chk_bmi_under12,
# chk_bmi_over35)
#
#
# tabNavn <- c("nasjonalitet",
# "hba1c",
# "diaType",
# "diet",
# "bmi_under12",
# "bmi_over35")
#
#
# names(list_var) <- paste("Varr", 1:length(list_var), sep = "")
# list2env(list_var, envir = .GlobalEnv)
#
#
# var.List <- list()
#
# for (f in seq_along(chk_var)){
#
# #gir object navn
# assign(x = paste(".var", f, sep = ""), value = chk_var[f])
# #
# # lhs <- paste("as.character(", tabNavn[f], ")", sep = "")
# # rhs <- paste("eval(as.name(get(paste('.var'", f, "sep = ''))))", sep = '')
# # lrhs <- paste(lhs, rhs, sep = "=")
# # var.List[[f]] <- eval(parse(text=lrhs))
#
# testVar <- as.name(get(paste(".var", f, sep = "")))
#
# var.List[[f]] <- paste(tabNavn[f], testVar, sep = "=")
#
# }
# list2env()
#
# x <- as.list(rnorm(10))
# x
# names(x) <- paste("aa", 1:length(x), sep = "")
# list2env(x, envir = .GlobalEnv)
Anonymisering
Tar bort identifisertbar variabler for anonymisering.
anonymVar <- c("FNr", "ENavn", "FNavn", "MNavn", "PostNr", "PostSted", "Adresse", "Addresse", "Pnr", "fnr")
dt <- readRDS("validert_arskontroll2018.rds")
dim(dt)
anonymDT <- dt[, (anonymVar) := NULL]
saveRDS(anonymDT, "annonym_dt2018.RDS")
Excel fil
Lager Excelfil for datasom brukes til analyse for årsrapport.
rio::export(dt, "./Excelfiler/arskontroll2018.xlsx")
LS0tDQp0aXRsZTogIkRhdGEgdmFsaWRlcmluZyINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICAjIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogdHJ1ZQ0KLS0tDQoNCiMgUGFra2VyDQpMYXN0ZXIgb3BwIGRlIG7DuGR2ZW5kaWdlIHBha2tlbmUgdGlsIHZhbGlkZXJpbmdzYXJiZWlkLg0KDQpgYGB7ciByZXN1bHRzPSdoaWRlJ30NCnBrZ3MgPSBjKCJkYXRhLnRhYmxlIiwgInN0cmluZ2kiLCAidmFsaWRhdGUiLCAiZ2dwbG90MiIsICJsdWJyaWRhdGUiLCAicmlvIikNCnNhcHBseShwa2dzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQpgYGANCg0KIyBEYXRhDQoNCkZlcmRpZyByZW5zZXQgZGF0YSBmcmEgZmlsZW4gKipkYXRhdmFzazIwMTguUioqLg0KDQpgYGB7cn0NCnJhd2R0ID0gcmVhZFJEUygiYXJza29udHJvbGwyMDE4LnJkcyIpDQpzZXREVChyYXdkdCkNCmBgYA0KDQpGb3JtYXQgYEZEYXRvYCB0aWwgYCVkLSVtLSVZYCBvZyBsYWdlIHZhcmlhYmVsIGBkb2JgIGZvciBmw7hkc2Vsc2RhdG8gbWVkIERhdG8gZm9ybWF0Lg0KDQpgYGB7cn0NCnJhd2R0Wywgc3RyKEZEYXRvKV0NCnJhd2R0WyAsYDo9YChkb2IgPSBGRGF0bywgRkRhdG8gPSBmb3JtYXQoRkRhdG8sICIlZC0lbS0lWSIpKV0NCnJhd2R0Wywgc3RyKGRvYildDQpgYGANCg0KU3RpIHRpbCBSRFMgZmlsZXIgZm9yIHBhc2llbnQgbGlzdGVyIGZvciBkZSB1dHZhbGd0ZSB2YXJpYWJsZXIgZm9yIHZhc2tpbmcNCg0KYGBge3J9DQpzdGkudmFzayA8LSAiLi9kYXRhdmFzay9yZHMiDQpgYGANCg0KIyBGdW5rc2pvbmVyDQoNCkZsZXJlIGZ1bmtzam9uZXIgZm9yIMOlIHNqZWtrZSBkYXRhIG9nIGRlcmV0dGVyIGxhZ2VyIEV4Y2VsLWZpbCBkZXQgbWFuIMO4bnNrZXIuIEZpbGVuIHNvbSBlciBsYWdldCBsaWdnZXIgaSBzdWItbWFwcGVuIGAuL2RhdGF2YXNrYC4NCg0KIyMgTWlzc2luZyB2ZXJkaQ0KDQpGdW5rc2pvbiBgbmFUZXN0YCBmb3Igw6Ugc2pla2tlIG1pc3NpbmcgdmVyZGkuDQoNCmBgYHtyfQ0KbmFUZXN0IDwtIGZ1bmN0aW9uKGR0LCB2YXIsIGZpbGUsIHNhdmUgPSBGQUxTRSwgbXZhciA9IE5VTEwpew0KICAjICdtdmFyJyBlciBmb3IgdGlsbGVnZyB2YXJpYWJlbA0KICAjICd2YXInIGZvciBtaXNzaW5nIHZhcmlhYmVsIHNvbSBza2FsIHNqZWtrZXMNCiAgIyAiZmlsZSIgZm9yIGZpbG5hdm4gZi5la3MgZmlsZS54bHN4DQogIA0KICBpZiAoIWlzLm51bGwobXZhcikpIHsNCiAgICB2YXIubGlzdCA8LSBjKCJQYXNpZW50SUQiLCAiUG5yIiwgIkZEYXRvIiwgIkZOYXZuIiwgIkVOYXZuIiwgImhvc3BpdGFsIiwgdmFyLCBtdmFyKQ0KICAgIH0gZWxzZSB7DQogICAgdmFyLmxpc3QgPC0gYygiUGFzaWVudElEIiwgIlBuciIsICJGRGF0byIsICJGTmF2biIsICJFTmF2biIsICJob3NwaXRhbCIsIHZhcikNCiAgICB9DQogIA0KICBkdE1pc3MgPC0gZHRbaXMubmEoZ2V0KHZhcikpLCAuLnZhci5saXN0XQ0KDQogICMgZHRNaXNzWywgRkRhdG8gOj0gZm9ybWF0KEZEYXRvLCAiJWQtJW0tJVkiKV1bXQ0KICBzZXRrZXkoZHRNaXNzLCBob3NwaXRhbCkNCiAgDQogIGlmKHNhdmUpew0KICAgIG9wZW54bHN4Ojp3cml0ZS54bHN4KGR0TWlzcywgZmlsZS5wYXRoKCIuL2RhdGF2YXNrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKGZvcm1hdChTeXMuRGF0ZSgpLCAiJVklbSVkIiksIGZpbGUsIHNlcCA9ICJfIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnNlcCA9ICcvJyksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFzVGFibGUgPSBUUlVFLCBjb2xXaWR0aHMgPSAiYXV0byIpDQogIH0NCiAgDQogIHJldHVybihkdE1pc3MpDQp9DQoNCmBgYA0KDQojIyBMYWdlciBzeWtlaHVzIElEDQoNCkxhZ2VyIGFsdGVybmF0aXYgSUQgdGlsIMOlIGJydWtlIGkgKmxvb3AqLg0KDQpgYGB7cn0NCmxpc3RIb3NwIDwtIHVuaXF1ZShyYXdkdCRob3NwaXRhbCkNCmhvc0RUIDwtIGRhdGEudGFibGUoaG9zcGl0YWwgPSBzb3J0KGxpc3RIb3NwKSwgaG9zcElEID0gMTpsZW5ndGgobGlzdEhvc3ApKQ0KDQpzZXRrZXkoaG9zRFQsIGhvc3BJRCkNCiMgc2F2ZVJEUyhob3NEVCwgImhvc3BpdGFsSUQucmRzIikNCiMgaG9zRFQgPC0gcmVhZFJEUygiaG9zcGl0YWxJRC5yZHMiKQ0KaG9zRFQNCmhvc05hdm5Lb3J0IDwtIGMoIkFrZXJzaHVzIiwgIkZvcmRlIiwgIkhhdWdlc3VuZCIsICJIYXVrZWxhbmQiLCAiU2FuZGVzc2pvZW4iLCAiRmlubm1hcmsiLA0KICAgICAgICAgICAgICJLcmlzdGlhbnN1bmQiLCAiTW9sZGUiLCAiTmFtc29zIiwgIkJvZG8iLCAiVHJvbmRoZWltIiwNCiAgICAgICAgICAgICAiU3RhdmFuZ2VyIiwgIlZlc3Rmb2xkIiwgIkdqb3ZpayIsICJMaWxsZWhhbW1lciIsICJFbHZlcnVtIiwgIkxldmFuZ2VyIiwNCiAgICAgICAgICAgICAiVGVsZW1hcmsiLA0KICAgICAgICAgICAgICJPc3Rmb2xkIiwNCiAgICAgICAgICAgICAiQXJlbmRhbCIsDQogICAgICAgICAgICAgIktyaXN0aWFuc2FuZCIsDQogICAgICAgICAgICAgIlVsbGV2YWwiLA0KICAgICAgICAgICAgICJOb3JkX25vcmdlIiwNCiAgICAgICAgICAgICAiSGFyc3RhZCIsDQogICAgICAgICAgICAgIkRyYW1tZW4iLA0KICAgICAgICAgICAgICJBbGVzdW5kIikNCg0KIyBob3NEVFssIGtvcnRJRCA6PSBob3NwSURdDQojIGhvc0RUWy4oa29ydElEID0gMToyNiwgdG8gPSBob3NOYXZuS29ydCksIG9uID0gImtvcnRJRCIsIGhvc0tvcnQgOj0gaS50b10NCmhvc0RUWy4oaG9zcElEID0gMToyNiwgdG8gPSBob3NOYXZuS29ydCksIG9uID0gImhvc3BJRCIsIGhvc0tvcnQgOj0gaS50b10NCg0KYGBgDQoNCk1lcmdlIGBob3NwSURgIGkgZGF0YXNldHRldA0KDQpgYGB7cn0NCnNldGtleShob3NEVCwgaG9zcGl0YWwpDQpzZXRrZXkocmF3ZHQsIGhvc3BpdGFsKQ0KZHQgPC0gaG9zRFRbcmF3ZHQsIG9uID0gImhvc3BpdGFsIl0NCnNldGtleShkdCwgaG9zcElEKQ0KYGBgDQoNCiMjIE9iamVrdCBwZXIgc3lrZWh1cw0KDQpMYWdlIG9iamVrdCBmb3IgaHZlcnQgc3lrZWh1cyBzb20gZW4gKkxJU1QqLg0KDQpgYGB7cn0NCiMgaG9zcExpc3QgPC0gdW5pcXVlKGR0JGhvc3BpdGFsKQ0KaG9zcE9iaiA8LSBmdW5jdGlvbih4KXsNCiAgbGlzdEhvcyA8LSB1bmlxdWUoeCRob3NwSUQpDQogIA0KICBob3NwTElTVCA9IGxpc3QoKSANCiAgDQogIGZvciAoaSBpbiBsaXN0SG9zKSB7DQogICAgaG9zcExJU1RbW2ldXSA8LSB4W2hvc3BJRCA9PSBpLCBdDQogIH0NCiAgDQogIHJldHVybihob3NwTElTVCkNCn0NCg0KYGBgDQoNCiMjIFNqZWtrIHZlcmRpDQoNCkZvciDDpSBzamVra2UgYXQgdmVyZGkgc2thbCB2w6ZyZSBldCB2aXN0IG5pdsOlLg0KDQpgYGB7cn0NCmNoa1ZlcmRpIDwtIGZ1bmN0aW9uKHgsIHZhciwgdmVyZGksIG92ZXIgPSBUUlVFLCBtdmFyID0gTlVMTCwgYWxsID0gRkFMU0Upew0KDQogICMgdmFyIDogdmFsZ3RlIHZhcmlhYmVsIHNvbSBza2FsIHNqZWtrZXMNCiAgIyBvdmVyIDogc2thbCB2ZXJkaWVuIG92ZXIgZWxsZXIgdW5kZXINCiAgIyBtdmFyIDogdGlsbGVnZyB2YXJpYWJsZXIgc29tIHNrYWwgaW5rbHVkZXJlcyBpIG91dHB1dA0KICAjIGFsbCA6IGVyIGRldCBiYXJlIGRlIG1lZCBUMUQgZWxsZXIgYWxsZSBpIGRhdGFzZXR0ZXQNCiAgDQogICMgc3RhbmRhcmQgdmFyaWFibGVyIMOlIGhhDQogIGlmIChpcy5udWxsKG12YXIpKXsNCiAgICBzdGQudmFyIDwtIGMoIlBhc2llbnRJRCIsICJQbnIiLCAiRkRhdG8iLCAiRk5hdm4iLCAiRU5hdm4iLCAiaG9zcElEIiwgImhvc3BpdGFsIiwgdmFyKQ0KICB9IGVsc2Ugew0KICAgIHN0ZC52YXIgPC0gYygiUGFzaWVudElEIiwgIlBuciIsICJGRGF0byIsICJGTmF2biIsICJFTmF2biIsICJob3NwSUQiLCAiaG9zcGl0YWwiLCB2YXIsIG12YXIpDQogIH0NCg0KICBpZiAoYWxsID09IEZBTFNFKSB7DQogICAgeHQgPC0gIHhbZGlhYmV0ZXNfVHlwZTEgPT0gIkphIiwgXQ0KICB9IGVsc2Ugew0KICAgIHh0ID0geCANCiAgfQ0KICANCiAgaWYgKG92ZXIgPT0gVFJVRSl7DQogIC5jaGtkdCA8LSB4dFtnZXQodmFyKSA+IHZlcmRpLCAuLnN0ZC52YXJdDQogIH0gZWxzZSB7DQogIC5jaGtkdCA8LSB4dFtnZXQodmFyKSA8IHZlcmRpLCAuLnN0ZC52YXJdDQogIH0NCiAgDQogIHJldHVybiguY2hrZHQpDQp9DQpgYGANCg0KDQojIyBQZXJzb24gdmFyaWFibGVyDQoNCkxhZ2VyIG9iamVrdCBzb20gaW5uZWhvbGRlciBhbGxlIHBlcnNvbiB2YXJpYWJsZXINCg0KYGBge3J9DQpwZXJzb24udmFyIDwtIGMoIlBhc2llbnRJRCIsICJQbnIiLCAiRkRhdG8iLCAiRk5hdm4iLCAiRU5hdm4iLCAiaG9zcElEIiwgImhvc3BpdGFsIikNCmBgYA0KDQoNCiMgTmFzam9uYWxpdGV0DQoNClZpc2VyIG5hc2pvbmFsaXRldC4NCg0KYGBge3J9DQpsaXN0LnZhciA8LSBjKCJQYXNpZW50SUQiLCAiUG5yIiwgIkZEYXRvIiwgIkZOYXZuIiwgIkVOYXZuIiwgImhvc3BJRCIsICJob3NwaXRhbCIsICJOYXNqb25hbGl0ZXQiLCAibmFzTW9yIiwgIm5hc0ZhciIpDQpkdE5hc2ogPC0gZHRbaXMubmEoTmFzam9uYWxpdGV0KSwgbGlzdC52YXIsIHdpdGggPSBGQUxTRV0NCnNldGtleShkdE5hc2osIGhvc3BJRCkNCmRpbShkdE5hc2opDQoNCiMgb3Blbnhsc3g6OndyaXRlLnhsc3goZHROYXNqLCBmaWxlLnBhdGgoIi4vZGF0YXZhc2siLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoZm9ybWF0KFN5cy5EYXRlKCksICIlWSVtJWQiKSwgIk5hc2pvbmFsaXRldC54bHN4Iiwgc2VwID0gIl8iKSwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmc2VwID0gJy8nKSwgDQojICAgICAgICAgICAgICAgICAgICAgIGFzVGFibGUgPSBUUlVFLCBjb2xXaWR0aHMgPSAiYXV0byIpDQpgYGANCg0KDQojIyBSZXR0aW5nDQoNCktvYmxldCBmcmEgRXhjZWwgZmlsIG1lZCBsaXN0ZXIgb3ZlciBtaXNzaW5nIG5hc2pvbmFsaXRldCBzb20gU2l2IEphbm5lIGhhciByZXR0ZXQuDQoNCmBgYHtyfQ0KeGxOYXNqIDwtIHJpbzo6aW1wb3J0KCIuL2RhdGF2YXNrL2Fya2l2LzIwMTkwNTA3X05hc2pvbmFsaXRldC54bHN4IikNCm5hbWVzKHhsTmFzaikNCnN0cih4bE5hc2opDQpzZXREVCh4bE5hc2opDQpzZXRrZXkoeGxOYXNqLCBQbnIpDQpkaW0oeGxOYXNqKQ0KeGxOYXNqW2lzLm5hKE5hc2pvbmFsaXRldCksIC5OXQ0KYGBgDQoNCiMjIEtvYmxldCB4bHN4DQoNCktvYmxldCBmaWxlbiBTaXYgSmFubmUgcmV0dGV0IG1lZCB1dHJla2sgZnJhIGVSZWcuDQoNCmBgYHtyfQ0KZHROYXNqMiA8LSB4bE5hc2pbZHROYXNqLCBvbiA9IGMoUG5yID0gIlBuciIpXQ0KZGltKGR0TmFzajIpDQpuYW1lcyhkdE5hc2oyKQ0KDQojIGJlaG9sZGVyIHZhcmlhYmxlciBmcmEgRXhjZWwgc29tIGJsZSB1dGZ5bHQgYXYgU2l2IEphbm5lDQppLm5hdm4gPC0gZ3JlcCgiXmkuKiIsIG5hbWVzKGR0TmFzajIpLCB2YWx1ZSA9IFQpDQpkdE5hc2oyWywgKGkubmF2bikgOj0gTlVMTF0NCm5hbWVzKGR0TmFzajIpDQpkdE5hc2oyW2lzLm5hKE5hc2pvbmFsaXRldCksIC5OXQ0KDQpgYGANCg0KDQojIyBLb2JsZSBmaWwgbWVkIGRlbiBncsO4bm5lIGRhdGFiYXNlbg0KDQpLb2JsZSBkaXJla3RlIHRpbCBBY2Nlc3MgREIsIG1lbiBmaWtrIGlra2UgdGlsIMOlIGZ1bmdlcmUuDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpncmVubkRCIDwtIG9kYmNEcml2ZXJDb25uZWN0KCJEcml2ZXI9e01pY3Jvc29mdCBBY2Nlc3MgRHJpdmVyICgqLm1kYiwgKi5hY2NkYil9O0RCUT1LOlxcU2Vuc2l0aXZ0XFxGb3Jza25pbmcwMlxcQmFybmVkaWFiZXRlc3JlZ2lzdGVyZXRfMjAxNS01NzM5XFxOeW9wcGRhZ2V0IGRpYWJldGVzXFxOeW9wcGRhZ2V0X1R5cGVfMV9kaWFiZXRlc19rb3BpLm1kYiIpDQpgYGANCg0KDQpEZW4gZ3LDuG5uZSBkYXRhYmFzZW4gc29tIGV4Y2VsZmlsDQoNCmBgYHtyfQ0KZ3JlZW5QYXRoIDwtICJLOlxcU2Vuc2l0aXZ0XFxGb3Jza25pbmcwMlxcQmFybmVkaWFiZXRlc3JlZ2lzdGVyZXRfMjAxNS01NzM5XFxCYXJuZWRpYWJldGVzIG9nIGt2YWxpdGV0XFxEYXRhZmlsZXJcXGFjY2Vzc2ZpbCIgDQoNCmdyZWVuRFQgPC0gcmlvOjppbXBvcnQoZmlsZS5wYXRoKGdyZWVuUGF0aCwgIjIwMTkwNTA3X2dyZWVuZGIueGxzeCIsIGZzZXAgPSAiXFwiKSkNCmRhdGEudGFibGU6OnNldERUKGdyZWVuRFQpDQpmZHQgPC0gZ3JlcCgic2Vsc2RhdG8iLCBuYW1lcyhncmVlbkRUKSwgdmFsdWUgPSBUKQ0KZ3JlZW5EVFssIFBuciA6PSBhcy5udW1lcmljKHNwcmludGYoIiVzJXMiLCBmb3JtYXQoZ2V0KGZkdCksICIlZCVtJXkiKSwgUGVyc29ubnIpKV0NCm5hbWVzKGdyZWVuRFQpDQpzZXRrZXkoZ3JlZW5EVCwgUG5yKQ0KZ3JlZW5TZWxlY3QgPC0gZ3JlZW5EVFssIC4oUG5yLCBQZXJzb25uciwgRm9ybmF2biwgRXR0ZXJuYXZuLCBFdG5pc2tfYmFrZ3JfcGFzLCBFdG5pc2tfYmFrZ3JfbW9yLCBFdG5pc2tfYmFrZ3JfZmFyKV0NCg0KYGBgDQoNCktvYmxlIG1pc3NpbmcgbmFzam9uYWxpdGV0IG1lZCAqR3LDuG5uIEFjY2VzcyogaHZpcyBkZSBmaW5uZXMgaW5mbyBvbSBuYXNqb25hbGl0ZXQgZGVyLg0KDQpgYGB7cn0NCm5hbWVzKGR0TmFzajIpDQpzZXRrZXkoZHROYXNqMiwgUG5yKQ0KDQpkYjJkYiA8LSBncmVlblNlbGVjdFtkdE5hc2oyLCBvbiA9IGMoUG5yID0gIlBuciIpXQ0KZGIyZGJbIWlzLm5hKEV0bmlza19iYWtncl9wYXMpLCAuTl0NCg0KYGBgDQoNCktvYmxlIGluZm8gb20gbmFzam9uYWxpdGV0IGZyYSBkZW4gZ3LDuG5uZQ0KDQpgYGB7cn0NCmRiMmRiW2lzLm5hKE5hc2pvbmFsaXRldCksIC5OXQ0KZGIyZGJbaXMubmEoTmFzam9uYWxpdGV0KSwgTmFzam9uYWxpdGV0IDo9IEV0bmlza19iYWtncl9wYXNdICMgTmFzam9uYWxpdGV0DQpkYjJkYltpcy5uYShuYXNNb3IpLCBuYXNNb3IgOj0gRXRuaXNrX2Jha2dyX21vcl0gIyBOYXNqLiBtb3INCmRiMmRiW2lzLm5hKG5hc0ZhciksIG5hc0ZhciA6PSBFdG5pc2tfYmFrZ3JfZmFyXSAjIE5hc2ouIGZhcg0KYGBgDQoNCkJlaG9sZGVyIGJhcmUgdmFyaWFibGUgc29tIGtvbW1lciBmcmEgZVJlZw0KDQpgYGB7cn0NCmR0TmFzajMgPC0gZGIyZGJbICwgLi5saXN0LnZhcl0NCmR0TmFzajNbLCBGRGF0byA6PSBmb3JtYXQoRkRhdG8sICIlZC0lbS0lWSIpXQ0KZGltKGR0TmFzajMpDQpkdE5hc2ozW2lzLm5hKE5hc2pvbmFsaXRldCksIC5OXQ0KDQpgYGANCg0KTGFnZXIgcmVuc2V0IG1pc3NpbmcgTmFzam9uYWxpdGV0IG9nIGtvYmxldCBtZWQgZ3LDuG5uIGRhdGFzZXR0ZXQuDQoNCmBgYHtyLCBldmFsPUZ9DQpzYXZlUkRTKGR0TmFzajMsICIuL2RhdGF2YXNrL3JlbnNldC9uYXNqb25hbGl0ZXQucmRzIikNCiMgZHROYXNqMyA8LSByZWFkUkRTKCIuL2RhdGF2YXNrL3JlbnNldC9uYXNqb25hbGl0ZXQucmRzIikNCmBgYA0KDQpNZXJnZSB0aWxnZW5nZWxpZ2UgbmFzam9uYWxpdGV0IGZyYSBncsO4bm4gZGF0YXNldHRldCBvZyBoYWx2IGJlYXJiZWlkIEV4Y2VsIGZyYSBTaXYgSmFubmUgdGlsIGhlbGUgZGF0YXNldHRldC4NCg0KYGBge3J9DQpuYW1lcyhkdE5hc2ozKQ0Ka29iX25hc2ogPC0gZHROYXNqM1ssIC4oUG5yLCBOYXNqb25hbGl0ZXQsIG5hc01vciwgbmFzRmFyKV0gI3ZlbGdlciByZWxldmFudCB2YXJpYWJsZXIgZm9yIG5hc2pvbmxpdGV0DQoNCiMgRHR0IDwtIGNvcHkoZHQpDQoNCmR0W2R0TmFzajMsIE5hc2pvbmFsaXRldCA6PSBpZmVsc2UoaXMubmEoTmFzam9uYWxpdGV0KSwgaS5OYXNqb25hbGl0ZXQsIE5hc2pvbmFsaXRldCksIG9uID0gIlBuciJdICNlcnN0YXRlciBtaXNzaW5nIE5hc2pvbmFsaXRldA0KIyBEdHRbaXMubmEoTmFzam9uYWxpdGV0KSwgTmFzam9uYWxpdGV0IDo9IGR0TmFzajNbLlNELCBOYXNqb25hbGl0ZXQsIG9uID0gIlBuciJdXQ0KDQpkdFtkdE5hc2ozLCBuYXNNb3IgOj0gaWZlbHNlKGlzLm5hKG5hc01vciksIGkubmFzTW9yLCBuYXNNb3IpLCBvbiA9ICJQbnIiXSAjZXJzdGF0ZXIgbWlzc2luZyBuYXNNb3INCmR0W2R0TmFzajMsIG5hc0ZhciA6PSBpZmVsc2UoaXMubmEobmFzRmFyKSwgaS5uYXNGYXIsIG5hc0ZhciksIG9uID0gIlBuciJdICNlcnN0YXRlciBtaXNzaW5nIG5hc0Zhcg0KYGBgDQoNCg0KTGlzdGUgcGFzaWVudGVyIHNvbSBmb3J0c2F0dCBtYW5nbGVyIGluZm8gZm9yIE5hc2pvbmFsaXRldCBwZXIgc3lrZWh1cw0KDQpgYGB7cn0NCmR0TmFzajQgPC0gZHROYXNqM1tpcy5uYShOYXNqb25hbGl0ZXQpLCBdIA0KY2hrX05hc2ogPC0gaG9zcE9iaihkdE5hc2o0KQ0KDQpzYXZlUkRTKGNoa19OYXNqLCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfTmFzai5yZHMiKSkNCmBgYA0KDQoNCiMgSGJBMWMNCg0KVmFyaWFiZWxuYXZuIGBsYWJfSGJBMWNBa2VyVmVyZGlgLiBTamVrayBhbnRhbGwgbWlzc2luZywgSGJBMWMgPCA0IG9nID4gMTUuDQoNCmBgYHtyfQ0KIyBoYmExYyA9IHZhbGlkYXRvciguZmlsZSA9ICdoYmExYy55YW1sJykNCnZfaGJhIDwtIHZhbGlkYXRvcigNCiAgIGxhYl9IYkExY0FrZXJWZXJkaSA+IDQNCiAgICwgbGFiX0hiQTFjQWtlclZlcmRpIDwgMTUNCiAgICwgIWlzLm5hKGxhYl9IYkExY0FrZXJWZXJkaSkNCikNCg0KY19oYmEgPC0gY29uZnJvbnQoZHQsIHZfaGJhLCBrZXkgPSAiUG5yIikNCnN1bW1hcnkoY19oYmEpDQojIGR0aGJhIDwtIGFzLmRhdGEuZnJhbWUoY19oYmEpDQojIHNldERUKGR0aGJhKQ0KIyBkdGhiYVssIC5OLCBieT1saXN0KGV4cHJlc3Npb24sIHZhbHVlKV0NCg0KIyBkdHQgPSBhZ2dyZWdhdGUoY19oYmEsIGJ5PSdyZWNvcmQnKQ0KIyBzZXREVChkdHQpDQojIGR0dCQNCiMgZHR0W3JlbC5mYWlsPT0xLCBdDQpgYGANCmBgYHtyIGV2YWw9RkFMU0V9DQpiYXJwbG90KGNfaGJhLCBtYWluID0gIkhiQTFjIHZlcmRpIikNCmBgYA0KDQpMYWdlciBFeGNlbCBmaWwgZm9yIG1pc3NpbmcgdmVyZGkgSGJBMWMuDQoNCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQ0KTkFfaGJhMWMgPSBuYVRlc3QoZHQsICJsYWJfSGJBMWNBa2VyVmVyZGkiLCBwYXN0ZShmb3JtYXQoU3lzLkRhdGUoKSwgIiVZJW0lZCIpLCAiaGJhMWMueGxzeCIsIHNlcD0iXyIpLCBzYXZlID0gRkFMU0UsIG12YXIgPSAiaG9zcElEIikNCmBgYA0KDQpMaXN0ZSBwYXNpZW50ZXIgbWVkIG1pc3NpbmcgKipIYkExYyoqIHBlciBzeWtlaHVzDQoNCmBgYHtyfQ0KY2hrX2hiYTFjIDwtIGhvc3BPYmooTkFfaGJhMWMpDQoNCnNhdmVSRFMoY2hrX2hiYTFjLCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfaGJhMWMucmRzIikpDQpgYGANCg0KDQojIERpYWJldGVzIFR5cGUNCg0KIyMgTWlzc2luZyBkaWFiZXRlcyBUeXBlDQoNClNqZWtrIG1pc3NpbmcgZGlhYmV0ZXMgdHlwZSBpbmtsdWRlcnQgaW5zdWxpbmJlaGFuZGxpbmcuIEh2aXMgZGV0IGVyIFR5cGUgMSBzw6UgbcOlIGRldCB2w6ZyZSBlbiBhdiBkZSBpbnN1bGluYmVoYW5kbGluZ2VuZSBzdmFydCAqSkEqLg0KDQoNCmBgYHtyfQ0KRFR0eXBlIDwtIGR0W2lzLm5hKGRpYWJldGVzX1R5cGUxKSAmIGlzLm5hKGRpYWJldGVzX1R5cGUyKSAmIGlzLm5hKGRpYWJldGVzX01vZHkpICYgaXMubmEoZGlhYmV0ZXNfQW5uZW5EaWFiZXRlcyksIA0KICAgICAgICAgICAgIGMocGVyc29uLnZhciwgDQogICAgICAgICAgICAgICAiZGlhYmV0ZXNfVHlwZTEiLA0KICAgICAgICAgICAgICAgImRpYWJldGVzX1R5cGUyIiwNCiAgICAgICAgICAgICAgICJkaWFiZXRlc19Nb2R5IiwNCiAgICAgICAgICAgICAgICJkaWFiZXRlc19Bbm5lbkRpYWJldGVzIiwNCiAgICAgICAgICAgICAgICJiZWhfZmVyZGlnX2JsYW5kIiwNCiAgICAgICAgICAgICAgICJiZWhfaW5zX2JlaCIsDQogICAgICAgICAgICAgICAiYmVoX2luc19wdW1wZSIpLCB3aXRoPUZBTFNFXQ0KDQpEVHR5cGUNCnNldGtleShEVHR5cGUsIGhvc3BJRCkNCg0KY2hrX2RpYWJ0eXBlIDwtIGhvc3BPYmooRFR0eXBlKQ0KDQpzYXZlUkRTKGNoa19kaWFidHlwZSwgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX2RpYWJ0eXBlLnJkcyIpKQ0KDQojIG9wZW54bHN4Ojp3cml0ZS54bHN4KERUdHlwZSwgZmlsZS5wYXRoKCIuL2RhdGF2YXNrIiwgcGFzdGUoZm9ybWF0KFN5cy5EYXRlKCksICIlWSVtJWQiKSwgImRpYWJldGVzVHlwZS54bHN4Iiwgc2VwID0gIl8iKSwgZnNlcCA9ICcvJyksIA0KIyAgICAgICAgICAgICAgICAgICAgICBhc1RhYmxlID0gVFJVRSwgY29sV2lkdGhzID0gImF1dG8iKQ0KYGBgDQoNCg0KIyMjIERpZXQgb2cgZGlhYmV0ZXMgVHlwZSAxDQoNCkh2aXMgZGV0IGVyIGRpZXR0IHPDpSBrYW4gZGV0IGlra2UgdsOmcmUgZGlhYmV0ZXMgVHlwZSAxLg0KDQoNCmBgYHtyfQ0KZHRbYmVoX2RpZXR0ID09ICJKYSIsIC5OLCBieT0uKGRpYWJldGVzX1R5cGUxKV0NCmRpZXQxIDwtIGR0W2RpYWJldGVzX1R5cGUxID09ICJKYSIgJiBiZWhfZGlldHQgPT0gIkphIiwgYyhwZXJzb24udmFyLCAiZGlhYmV0ZXNfVHlwZTEiLCAiYmVoX2FudGlfdGFiIiwgImJlaF9kaWV0dCIpLCB3aXRoPUZdDQpkaWV0MQ0KYGBgDQoNCkh2aXMgYnJ1a2VyIGFudGlkaWFiZXRlcyB0YWJlbGV0dGVyIGthbiBpa2tlIHbDpnJlIGRpYWJldGVzIFR5cGUgMS4NCg0KDQpgYGB7cn0NCmR0W2JlaF9hbnRpX3RhYiA9PSAiSmEiLCAuTiwgYnk9LihkaWFiZXRlc19UeXBlMSldDQpkaWV0MiA8LSBkdFtkaWFiZXRlc19UeXBlMSA9PSAiSmEiICYgYmVoX2FudGlfdGFiID09ICJKYSIsIGMocGVyc29uLnZhciwgImRpYWJldGVzX1R5cGUxIiwgImJlaF9hbnRpX3RhYiIsICJiZWhfZGlldHQiKSwgd2l0aD1GXQ0KZGlldDINCmBgYA0KDQoNCktvYmxlIGRlIHRvIHNhbW1lbg0KDQoNCmBgYHtyfQ0KZGlldDIyIDwtIGRpZXQyWyFkaWV0MSwgb249IlBuciJdICNla2x1ZGVyZXIgZGVuIHNvbSBmaW5uZXMgcMOlIGJlZ2dlDQpkaWV0MjINCg0KZGlldDEyIDwtIHJiaW5kbGlzdChsaXN0KGRpZXQxLCBkaWV0MjIpKQ0KYGBgDQoNCg0KTGFnZXIgbGlzdGUNCg0KDQpgYGB7cn0NCmNoa19kaWV0IDwtIGhvc3BPYmooZGlldDEyKQ0KDQpzYXZlUkRTKGNoa19kaWV0LCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfZGlldC5yZHMiKSkNCmBgYA0KDQoNCiMgVmVrdCBvZyBow7h5ZGUNCg0KS29udHJvbGxlcmVyIGF0ICoqSMO4eWRlKiogdW5kZXIgMiBtZXRlciBvZyAqKlZla3QqKiB1bmRlciAxMDAga2cuIFZhcmlhYmVsIGBCTUlgIGxhZ2VzIG1lZCBmb3JtZWw6ICQkXGZyYWN7VmVrdChrZyl9e0jDuHlkZShtKV4yfSQkDQoNCkxhZ2VyIF9fQk1JXyB2YXJpYWJlbC4NCg0KYGBge3J9DQojIyBCTUkNCmR0WyFpcy5uYShpbm5fVmVrdCkgJiAhaXMubmEoaW5uX0xlbmdkZSksIGJtaSA6PSAoaW5uX1Zla3QvaW5uX0xlbmdkZS9pbm5fTGVuZ2RlKSoxMDAwMF0NCg0KYGBgDQoNClNqZWtrIGFudGFsbCBmb3IgdW52YW5saWdlIHRhbGwgYmwuYSBow7h5ZGUsIHZla3Qgb2cgYm1pLg0KDQpgYGB7cn0NCg0KIyMgdmFsaWRlcmluZw0Kdl9rZyA9IHZhbGlkYXRvcigNCiAgaW5uX0xlbmdkZSA+IDEwLA0KICBpbm5fTGVuZ2RlIDwgMjAwLA0KICBpbm5fVmVrdCA+IDIwLA0KICBpbm5fVmVrdCA8IDEwMCwNCiAgQk1JIDo9IChpbm5fVmVrdC9pbm5fTGVuZ2RlL2lubl9MZW5nZGUpKjEwMDAwLA0KICBCTUkgPCAzMCwNCiAgQk1JIDwgMzUsDQogIEJNSSA+IDExDQogICkNCg0KY29uX2tnID0gY29uZnJvbnQoZHQsIHZfa2csIGtleT0iUG5yIikNCiMgY29uX2tnDQpzdW1tYXJ5KGNvbl9rZykNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KYmFycGxvdChjb25fa2csIG1haW4gPSAiVmVrdCwgSMO4eWRlIG9nIEJNSSIpDQpgYGANCg0KSMO4eWRlIHVuZGVyIDEwIGNtDQoNCmBgYHtyfQ0KZHRbaW5uX0xlbmdkZSA8IDEwLCBwZXJzb24udmFyLCB3aXRoPUZdDQpgYGANCg0KDQpCTUkgdW5kZXIgMTENCg0KYGBge3J9DQpibWlfcGFzLmxpc3Q9YygiUGFzaWVudElEIiwgIlBuciIsICJGRGF0byIsICJGTmF2biIsICJFTmF2biIsICJob3NwSUQiLCAiaG9zcGl0YWwiLCAiaW5uX0xlbmdkZSIsICJpbm5fVmVrdCIpDQpwYXNCTUlfMTEgPC0gZHRbYm1pIDwgMTIsIC4uYm1pX3Bhcy5saXN0XQ0Kc2V0a2V5KHBhc0JNSV8xMSwgaG9zcElEKQ0KZGltKHBhc0JNSV8xMSkNCg0KY2hrX2JtaV91bmRlcjEyIDwtIGhvc3BPYmoocGFzQk1JXzExKQ0KDQpzYXZlUkRTKGNoa19ibWlfdW5kZXIxMiwgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX2JtaV91bmRlcjEyLnJkcyIpKQ0KDQojIA0KIyBvcGVueGxzeDo6d3JpdGUueGxzeChwYXNCTUksIGZpbGUucGF0aCgiLi9kYXRhdmFzayIsIHBhc3RlKGZvcm1hdChTeXMuRGF0ZSgpLCAiJVklbSVkIiksIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJibWlfdW5kZXIxMS54bHN4Iiwgc2VwID0gIl8iKSwgZnNlcCA9ICcvJyksIA0KIyAgICAgICAgICAgICAgICAgICAgICBhc1RhYmxlID0gVFJVRSwgY29sV2lkdGhzID0gImF1dG8iKQ0KDQpgYGANCg0KDQpCTUkgb3ZlciAzNQ0KDQoNCmBgYHtyfQ0KcGFzQk1JMzUgPC0gZHRbYm1pID4gMzUsIC4uYm1pX3Bhcy5saXN0XQ0KZGltKHBhc0JNSTM1KQ0KY2hrX2JtaV9vdmVyMzUgPC0gaG9zcE9iaihwYXNCTUkzNSkNCg0Kc2F2ZVJEUyhjaGtfYm1pX292ZXIzNSwgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX2JtaV9vdmVyMzUucmRzIikpDQpgYGANCg0KIyBBbGRlcg0KDQpTamVrayBtaW5pbXVtIGFsZGVyIHVuZGVyIDAgb2cgbWFrc2ltdW0gb3ZlciAxOCDDpXIuIFZhcmlhYmVsIGBhbGRlcmAgbcOlIGxhZ2VzIGbDuHJzdC4NCg0KDQpgYGB7ciByZXN1bHRzPSdoaWRlJ30NCmR0WywgYWxkZXIgOj0gYXMubnVtZXJpYyhyb3VuZChhcy5wZXJpb2QoaW50ZXJ2YWwoZG9iLCBpbm5fRGF0bykvZHVyYXRpb24obj0xLCB1bml0PSJ5ZWFycyIpKSwgZGlnaXRzID0gMSkpXQ0KYGBgDQoNClZhbGlkZXJpbmdzIGRhdGENCg0KYGBge3J9DQp2X2FnZSA8LSB2YWxpZGF0b3IoDQogIGFsZGVyID4gMCwNCiAgYWxkZXIgPCAxOQ0KKQ0KDQpjX2FnZSA8LSBjb25mcm9udChkdCwgdl9hZ2UsIGtleT0iUG5yIikNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkoY19hZ2UpDQpgYGANCg0KUGxvdHRpbmcgdmFsaWRlcmluZ3NkYXRhDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpiYXJwbG90KGNfYWdlLCBtYWluID0gIkFsZGVyIHVuZGVyIDAgb2cgb3ZlciAxOCDDpXIiKQ0KYGBgDQoNCkxpc3RlIGF2IHBhc2llbnRlciBvdmVyIDE5IMOlciBwZXIgc3lrZWh1cy4NCg0KYGBge3J9DQphbGRlcjE5IDwtIGR0W2FsZGVyID4gMTguOTk5LCBjKHBlcnNvbi52YXIsICJpbm5fRGF0byIsICJhbGRlciIpLCB3aXRoPUZdDQpkaW0oYWxkZXIxOSkNCmNoa19hbGRlcjE5IDwtIGhvc3BPYmooYWxkZXIxOSkNCg0Kc2F2ZVJEUyhjaGtfYWxkZXIxOSwgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX2FsZGVyMTkucmRzIikpDQpgYGANCg0KIyBEaWFiZXRlc3ZhcmlnaGV0DQoNCkRpYWJldGVzdmFyaWdoZXQgZGVmaW5lcnQgc29tIGxlbmdkZSBmcmEgZGlhZ25vc2UgZGF0byBgaW5uX0RpYWdEYXRvYCB0aWwga29udHJvbGwgdGlkc3B1bmt0ZXQgYGlubl9EYXRvYC4gTnkgdmFyaWFiZWwgbGFnZXMgZm9yIGRpYWJldGVzdmFyaWdoZXQgc29tIGhldGVyIGBkaWFnVmFyYC4NCg0KYGBge3J9DQpkdFssIGRpYWdWYXIgOj0gYXMubnVtZXJpYyhyb3VuZChhcy5wZXJpb2QoaW50ZXJ2YWwoZGlhZ0RhdG8sIGlubl9EYXRvKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpLCBkaWdpdHMgPSAxKSldDQpgYGANCg0KU2pla2sgZm9yIGRpYWJldGVzdmFyaWdoZXQgdW5kZXIgMCDDpXIuDQoNCmBgYHtyfQ0KY19kaWFndiA8LSBjaGVja190aGF0KGR0LCBkaWFnVmFyID4gMCkNCnN1bW1hcnkoY19kaWFndikNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KdmFsaWRhdGU6OmFnZ3JlZ2F0ZShjX2RpYWd2KQ0KaGVhZChhZ2dyZWdhdGUoY19kaWFndiwgYnk9J3JlY29yZCcpKSAjc2VlIGh2aWxrZW4gbGluamUgc29tIGhhciBmZWlsDQpzb3J0KGNfZGlhZ3YpICMgc2UgaHZpbGtlbiB2ZXJkaSBzb20gZ2rDuHIgZmVpbGVuDQpgYGANCg0KDQpMaXN0IGF2IHBhc2llbnRlcg0KDQpgYGB7cn0NCnZhcmlnaGV0MCA8LSBkdFtkaWFnVmFyIDwgMC4wMDA3LCBjKHBlcnNvbi52YXIsICJkaWFnRGF0byIsICJpbm5fRGF0byIsICJkaWFnVmFyIiksIHdpdGg9Rl0NCmRpbSh2YXJpZ2hldDApDQpjaGtfdmFyaWhldDAgPC0gaG9zcE9iaih2YXJpZ2hldDApDQoNCnNhdmVSRFMoY2hrX3ZhcmloZXQwLCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfdmFyaWhldDAucmRzIikpDQpgYGANCg0KIyBNZW5hcmNoZQ0KDQpBbGRlciB2ZWQgbWVuYXJjaGUuIFNqZWtrIGZvciBtaW5pbXVuIGFsZGVyIGF2IDkgw6VyIG9nIG1ha3NpbXVtIGF2IDE2LjUgw6VyLiBOeSB2YXJpYWJlbCBgYWxkZXJBcmNgIGxhZ2VzLiBPQlMhIGh1c2sgX19kb2JfXyBicnVrZXMgZm9yIGbDuGRzZWxzZGF0byBzaWRlbiBfX0ZEYXRvX18gZXIgc3RyaW5nLg0KDQpgYGB7cn0NCmR0WyFpcy5uYShpbm5fTWVuYXJjaGVUaWRzcHVua3QpLCANCiAgIGFsZGVyQXJjIDo9IGFzLm51bWVyaWMocm91bmQoYXMucGVyaW9kKGludGVydmFsKGRvYiwgaW5uX01lbmFyY2hlVGlkc3B1bmt0KS9kdXJhdGlvbihuPTEsIHVuaXRzID0gJ3llYXJzJykpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMSkpXQ0KYGBgDQoNClZhbGlkZXJpbmcgZm9yIGBhbGRlckFyY2AuDQoNCmBgYHtyfQ0Kdl9hZ2VNcmNoIDwtIHZhbGlkYXRvcigNCiAgYWxkZXJBcmMgPiA5LA0KICBhbGRlckFyYyA8IDE2LjUNCikNCg0KY19hZ2VNcmNoIDwtIGNvbmZyb250KGR0LCB2X2FnZU1yY2gpDQpgYGANCg0KVGFiZWxsIGZvciBhbnRhbGwgc29tIGVyIHVuZGVyIDkgw6VyIG9nIG92ZXIgMTYuNSDDpXIuDQoNCmBgYHtyfQ0Kc3VtbWFyeShjX2FnZU1yY2gpDQpgYGANCg0KDQpMaXN0ZSBvdmVyIHBhc2llbnRlciBtZWQgbWVuYXJjaGUgdW5kZXIgOSDDpXIuDQoNCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQ0KIyBtZW5hclZhciA9ICBjKCJQYXNpZW50SUQiLCAiUG5yIiwgIkZEYXRvIiwgIkZOYXZuIiwgIkVOYXZuIiwgImhvc3BpdGFsIiwgImlubl9NZW5hcmNoZVRpZHNwdW5rdCIsICJhbGRlckFyYyIpDQptZW5hcmNoOSA8LSBkdFthbGRlckFyYyA8IDksIGMocGVyc29uLnZhciwgImlubl9NZW5hcmNoZVRpZHNwdW5rdCIsICJhbGRlckFyYyIpLCB3aXRoPUZdDQpzZXRrZXkobWVuYXJjaDksIGhvc3BJRCkNCiMgb3Blbnhsc3g6OndyaXRlLnhsc3gobWVuYXJjaDksIGZpbGUucGF0aCgiLi9kYXRhdmFzayIsIHBhc3RlKGZvcm1hdChTeXMuRGF0ZSgpLCAiJVklbSVkIiksICJtZW5hcmNoX3VuZGVyOXlycy54bHN4Iiwgc2VwID0gIl8iKSwgZnNlcCA9ICcvJyksIA0KICAgICAgICAgICAgICAgICAgICAgIyBhc1RhYmxlID0gVFJVRSwgY29sV2lkdGhzID0gImF1dG8iKQ0KDQpkaW0obWVuYXJjaDkpDQpjaGtfbWVuYXJjaDkgPC0gaG9zcE9iaihtZW5hcmNoOSkNCg0Kc2F2ZVJEUyhjaGtfbWVuYXJjaDksIGZpbGUucGF0aChzdGkudmFzaywgImNoa19tZW5hcmNoOS5yZHMiKSkNCmBgYA0KDQpMaXN0ZSBvdmVyIHBhc2llbnRlciBtZWQgbWVuYXJjaGUgb3ZlciAxNi41IMOlci4NCg0KYGBge3IgcmVzdWx0cz0naGlkZSd9DQptZW5hcmNoMTYgPC0gZHRbYWxkZXJBcmMgPiAxNi41LCAgYyhwZXJzb24udmFyLCAiaW5uX01lbmFyY2hlVGlkc3B1bmt0IiwgImFsZGVyQXJjIiksIHdpdGg9Rl0NCnNldGtleShtZW5hcmNoMTYsIGhvc3BJRCkNCmRpbShtZW5hcmNoMTYpDQpjaGtfbWVuYXJjaDE2IDwtIGhvc3BPYmoobWVuYXJjaDE2KQ0KDQpzYXZlUkRTKGNoa19tZW5hcmNoMTYsIGZpbGUucGF0aChzdGkudmFzaywgImNoa19tZW5hcmNoMTYucmRzIikpDQoNCiMgb3Blbnhsc3g6OndyaXRlLnhsc3gobWVuYXJjaDE2LCBmaWxlLnBhdGgoIi4vZGF0YXZhc2siLCBwYXN0ZShmb3JtYXQoU3lzLkRhdGUoKSwgIiVZJW0lZCIpLCAibWVuYXJjaF9vdmVyMTY1eXJzLnhsc3giLCBzZXAgPSAiXyIpLCBmc2VwID0gJy8nKSwgDQojICAgICAgICAgICAgICAgICAgICAgIGFzVGFibGUgPSBUUlVFLCBjb2xXaWR0aHMgPSAiYXV0byIpDQoNCmBgYA0KDQoNCiMgRGlhZ25vc2UgQWxkZXINCg0KRGlhZ25vc2UgYWxkZXIgbWluIDAgb2cgbWF4IDE4IMOlci4gRsO4cnN0IGxhZ2UgdmFyaWFiZWwgX19kaWFnQWxkZXJfXy4NCg0KYGBge3J9DQpkdFssIGRpYWdBbGRlciA6PSBhcy5udW1lcmljKHJvdW5kKGFzLnBlcmlvZChpbnRlcnZhbChkb2IsIGRpYWdEYXRvKS9kdXJhdGlvbihuPTEsIHVuaXRzID0gJ3llYXJzJykpLCBkaWdpdHMgPSAxKSldDQpgYGANCg0KYGBge3J9DQp2X2FsZGVyRGlhZyA8LSB2YWxpZGF0b3IoDQogIGRpYWdBbGRlciA+IDAsDQogIGRpYWdBbGRlciA8IDE4DQopDQoNCmNfYWxkZXJEaWFnIDwtIGNvbmZyb250KGR0LCB2X2FsZGVyRGlhZykNCnNvcnQoY19hbGRlckRpYWcpICMgc2UgcMOlIHZlcmRpZXIgZm9yIMOlIHbDpnJlIGZlaWwNCnN1bW1hcnkoY19hbGRlckRpYWcpDQpgYGANCg0KTGlzdCBvdmVyIHBhc2llbnRlciBmb3IgZGlhZ25vc2UgYWxkZXIgc29tIGlra2UgbcO4dGVyIGZvcnZlbnRpbmdlbmUuIERpYWdub3NlIGFsZGVyIHVuZGVyIDEgw6VyLg0KDQpgYGB7cn0NCmRpYWdBbGRlcjAgPC0gZHRbZGlhZ0FsZGVyIDwgMC45LCBjKHBlcnNvbi52YXIsICJkaWFnRGF0byIsICJkaWFnQWxkZXIiLCAiZGlhYmV0ZXNfVHlwZTEiKSwgd2l0aD1GXQ0KZGltKGRpYWdBbGRlcjApDQpzZXRrZXkoZGlhZ0FsZGVyMCwgaG9zcElEKQ0KDQpjaGtfZGlhZ0FsZGVyMCA8LSBob3NwT2JqKGRpYWdBbGRlcjApDQoNCnNhdmVSRFMoY2hrX2RpYWdBbGRlcjAsIGZpbGUucGF0aChzdGkudmFzaywgImNoa19kaWFnQWxkZXIwLnJkcyIpKQ0KYGBgDQoNCiMgQmxvZHRyeWtrDQoNClN5c3RvbGlzayBza2FsIGlra2UgdsOmcmUgb3ZlciAxNDAgb2cgZGlhc3RvbGlzayBza2FsIGlra2UgdsOmcmUgdW5kZXIgNDUNCg0KYGBge3J9DQp2X2Jsb2R0cnlrayA8LSB2YWxpZGF0b3IoDQogIGlubl9CbG9kdHJ5a2tfcyA8IDE0MCwNCiAgaW5uX0Jsb2R0cnlra19kID4gNDUNCikNCg0KY19ibG9kdHJ5a2sgPC0gY29uZnJvbnQoZHQsIHZfYmxvZHRyeWtrKQ0Kc3VtbWFyeShjX2Jsb2R0cnlraykNCmBgYA0KDQpMaXN0ZXIgb3ZlciBwYXNpZW50ZXIgc29tIGlra2UgbcO4dGVyIGtyYXZldC4NCg0KYGBge3J9DQpibG9kdHJrX3MgPC0gZHRbaW5uX0Jsb2R0cnlra19zID4gMTQwLCBjKHBlcnNvbi52YXIsICJpbm5fQmxvZHRyeWtrX3MiLCAiaW5uX0Jsb2R0cnlra19kIiksIHdpdGg9Rl0NCmJsb2R0cmtfZCA8LSBkdFtpbm5fQmxvZHRyeWtrX2QgPCA0NSwgYyhwZXJzb24udmFyLCAiaW5uX0Jsb2R0cnlra19zIiwgImlubl9CbG9kdHJ5a2tfZCIpLCB3aXRoPUZdDQoNCmJsb2R0cmsgPC0gcmJpbmRsaXN0KGxpc3QoYmxvZHRya19kLCBibG9kdHJrX3MpLCB1c2UubmFtZXMgPSBUUlVFKQ0KDQpzZXRrZXkoYmxvZHRyaywgaG9zcElEKQ0KDQpjaGtfYmxvZHRyeWtrIDwtIGhvc3BPYmooYmxvZHRyaykNCg0Kc2F2ZVJEUyhjaGtfYmxvZHRyeWtrLCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfYmxvZHRyeWtrLnJkcyIpKQ0KDQpgYGANCg0KDQojIExETA0KDQpMaXN0ZSBvdmVyIGFsbGUgTERMIHNvbSBlciBgPj0yLjZgLiBCcnVrIExETCBpa2tlIGZhc3RlbmRlIGBsYWJfbGlwX0xETF8yYCwgbWVuIGh2aXMgbWlzc2luZyBmYXN0ZW5kZSBMREwgc2thbCBicnVrZXMgaHZpcyBkZXQgZmlubmVzIGBsYWJfbGlwX0xETGAuDQoNCkxhZ2VyIHZhcmlhYmVsIF9fbGRsM19fIGZvciBpa2tlIGZhc3RlbmRlIExETCBvZyBsZWdnZXIgdGlsIGZhc3RlbmRlIGh2aXMgbWlzc2luZy4NCg0KYGBge3J9DQpkdFssIGxhcHBseSguU0QsIHN0ciksIC5TRGNvbHMgPSBjKCJsYWJfbGlwX0xETF8yIiwgImxhYl9saXBfTERMIildDQoNCmR0WyAsIGxkbDMgOj0gbGFiX2xpcF9MRExfMl1baXMubmEobGFiX2xpcF9MRExfMiksIGxkbDMgOj0gbGFiX2xpcF9MRExdDQpkdFtpcy5uYShsYWJfbGlwX0xETF8yKSwgLk5dDQpkdFtpcy5uYShsZGwzKSwgLk5dDQoNCmBgYA0KDQpTamVrayBhbnRhbGwgc29tIGVyIG92ZXIgb2cgbGlrIDIuNi4NCg0KYGBge3J9DQp2X2xkbCA8LSB2YWxpZGF0b3IoDQogIGxkbDMgPD0gMi42LA0KICBsZGwzIDwgMi43DQopDQoNCmNfbGRsIDwtIGNvbmZyb250KGR0LCB2X2xkbCkNCnNvcnQoY19sZGwpDQpzdW1tYXJ5KGNfbGRsKQ0KDQpgYGANCg0KTGlzdGUgcGFzaWVudGVyDQoNCmBgYHtyfQ0KbGRsMjYgPC0gZHRbbGRsMyA+PTIuNiwgYyhwZXJzb24udmFyLCAibGFiX2xpcF9MRExfMiIsICJsYWJfbGlwX0xETCIpLCB3aXRoPUZdDQpkaW0obGRsMjYpDQoNCnNldGtleShsZGwyNiwgaG9zcElEKQ0KDQpjaGtfTERMMjYgPC0gaG9zcE9iaihsZGwyNikNCnNhdmVSRFMoY2hrX0xETDI2LCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfTERMMjYucmRzIikpDQpgYGANCg0KIyBCZWhhbmRsaW5nDQoNCkZvcnNramVsbGlnZSB2YXJpYWJsZXIgZm9yIGJlaGFuZGxpbmcgYXYgZGlhYmV0ZXMuIEdqZWxkZXIgYmFyZSBmb3IgVDFELg0KDQojIyBNdWx0aWluanVrc2pvbg0KDQpHamVsZGVyIGJhcmUgZm9yIHBhc2llbnRlciBtZWQgVDFELiBTdXBlcmh1cnRpZ3ZpcmtlbmRlIGBiZWhfaW5zX2JlaF9odXJ0aWdfaWVfZG9nbmAgcGx1cyBsYW5ndGlkc3ZpcmtlbmRlIGBiZWhfaW5zX2JlaF9sYW5nX2llX2RvZ25gIGRlbCBtZWQga3JvcHBzdmVrdCBgaW5uX1Zla3RgLiBOb3JtYWwgdmVyZGkgc2thbCB2w6ZyZSA8IDIuDQoNCg0KYGBge3IgZXZhbD1GQUxTRX0NCmR0Wywgc3RyKC5TRCksIC5TRGNvbHMgPSBjKCJiZWhfaW5zX2JlaF9odXJ0aWdfaWVfZG9nbiIsICJiZWhfaW5zX2JlaF9sYW5nX2llX2RvZ24iLCAiaW5uX1Zla3QiKV0NCiMgZHRbLCBsYXBwbHkoLlNELCBzdHIpLCAuU0Rjb2xzID0gYygiYmVoX2luc19iZWhfaHVydGlnX2llX2RvZ24iLCAiYmVoX2luc19iZWhfbGFuZ19pZV9kb2duIildDQpgYGANCg0KRW5kcmUgTkEgaSBiZWdnZSB2YXJpYWJsZW5lIHRpbCAwLiBMYWdlciB2YXJpYWJlbCBfX211bHRpbmpfXyBmb3Igc3VtbWFuIGF2IHN1cGVyaHVydGlndmlya2VuZGUgb2cgbGFuZ3RpZHN2aXJrZW5kZSBiZWhhbmRsaW5nIG9nIGRlbCBtZWQga3JvcHBzdmVrdC4NCg0KYGBge3J9DQpkdFsgLCBiZWhfaHVydGlnTkEwIDo9IGJlaF9pbnNfYmVoX2h1cnRpZ19pZV9kb2duXVtpcy5uYShiZWhfaHVydGlnTkEwKSwgYmVoX2h1cnRpZ05BMCA6PSAwXSANCmR0WyAsIGJlaF9sYW5ndGlkTkEwIDo9IGJlaF9pbnNfYmVoX2xhbmdfaWVfZG9nbl1baXMubmEoYmVoX2xhbmd0aWROQTApLCBiZWhfbGFuZ3RpZE5BMCA6PSAwXQ0KZHRbLCBtdWx0aW5qIDo9IHJvdW5kKChiZWhfaHVydGlnTkEwICsgYmVoX2xhbmd0aWROQTApL2lubl9WZWt0LCBkaWdpdHMgPSAyKV0NCg0KZHRbLCAuKGJlaF9pbnNfYmVoX2h1cnRpZ19pZV9kb2duLCBiZWhfaW5zX2JlaF9sYW5nX2llX2RvZ24sIGlubl9WZWt0LCBtdWx0aW5qKV0NCg0KYGBgDQoNCkxpc3RlIHBhc2llbnRlci4gR2plbGRlciBiYXJlIHBhc2llbnRlciBtZWQgVDFELg0KDQpgYGB7cn0NCmJlaF9tdWx0aSA8LSBkdFtkaWFiZXRlc19UeXBlMT09IkphIiAmIG11bHRpbmogPiAyLCBjKHBlcnNvbi52YXIsICJiZWhfaW5zX2JlaF9odXJ0aWdfaWVfZG9nbiIsICJiZWhfaW5zX2JlaF9sYW5nX2llX2RvZ24iLCAiaW5uX1Zla3QiKSwgd2l0aD1GXQ0KZGltKGJlaF9tdWx0aSkNCg0Kc2V0a2V5KGJlaF9tdWx0aSwgaG9zcElEKQ0KDQpjaGtfbXVsdGkgPC0gaG9zcE9iaihiZWhfbXVsdGkpDQpzYXZlUkRTKGNoa19tdWx0aSwgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX211bHRpLnJkcyIpKQ0KYGBgDQoNCiMjIEluc3VsaW5wdW1wZQ0KDQpOb3JtYWwgdmVyZGlnIHNrYWwgaWtrZSB2w6ZyZSA8IDINCg0KYGBge3J9DQpkdFssIHN0ciguU0QpLCAuU0Rjb2xzID0gYygiYmVoX2luc190eXBlX2llX2Jhc2FsIiwgImJlaF9pbnNfdHlwZV9pZV9ib2x1cyIsICJpbm5fVmVrdCIpXQ0KYGBgDQoNCkVuZHJlIE5BIGkgYmVnZ2UgdmFyaWFibGVuZSB0aWwgMC4gTGFnZXIgdmFyaWFiZWwgX19wdW1wZV9fIGZvciBzdW1tYW4gYXYgc3VwZXJodXJ0aWd2aXJrZW5kZSBvZyBsYW5ndGlkc3ZpcmtlbmRlIGJlaGFuZGxpbmcgb2cgZGVsIG1lZCBrcm9wcHN2ZWt0Lg0KDQpgYGB7cn0NCmR0WyAsIGJlaF9iYXNhbE5BMCA6PSBiZWhfaW5zX3R5cGVfaWVfYmFzYWxdW2lzLm5hKGJlaF9iYXNhbE5BMCksIGJlaF9iYXNhbE5BMCA6PSAwXSANCmR0WyAsIGJlaF9ib2x1c05BMCA6PSBiZWhfaW5zX3R5cGVfaWVfYm9sdXNdW2lzLm5hKGJlaF9ib2x1c05BMCksIGJlaF9ib2x1c05BMCA6PSAwXQ0KZHRbLCBwdW1wZSA6PSByb3VuZCgoYmVoX2Jhc2FsTkEwICsgYmVoX2JvbHVzTkEwKS9pbm5fVmVrdCwgZGlnaXRzID0gMildDQoNCmR0WywgLihiZWhfaW5zX3R5cGVfaWVfYmFzYWwsIGJlaF9pbnNfdHlwZV9pZV9ib2x1cywgaW5uX1Zla3QsIHB1bXBlKV0NCg0KYGBgDQoNCkxpc3RlciBhdiBwYXNpZW50ZXIgbWVkIFQxRA0KDQpgYGB7cn0NCmJlaF9wdW1wZSA8LSBkdFtkaWFiZXRlc19UeXBlMSA9PSAiSmEiICYgcHVtcGUgPiAyLCBjKHBlcnNvbi52YXIsImJlaF9pbnNfdHlwZV9pZV9iYXNhbCIsICJiZWhfaW5zX3R5cGVfaWVfYm9sdXMiLCAiaW5uX1Zla3QiKSwgd2l0aD1GXQ0KZGltKGJlaF9wdW1wZSkNCg0KY2hrX3B1bXBlIDwtIGhvc3BPYmooYmVoX3B1bXBlKQ0Kc2F2ZVJEUyhjaGtfcHVtcGUsIGZpbGUucGF0aChzdGkudmFzaywgImNoa19wdW1wZS5yZHMiKSkNCmBgYA0KDQojIyBQdW1wZSBhbm5ldA0KDQpWYXJpYWJlbG5hdm4gYGJlaF9pbnNfcHVtcGVfYW5uZXRgIGVyIGVuIGZyaXRla3N0LiBEZSBtw6Ugc3RhbmRhcmRpc2VyZXMuDQoNCmBgYHtyfQ0KcHVtcHN0aSA9ICIuL2RhdGF2YXNrIg0KDQpwdW1wbnkgPC0gcmVhZC5jc3YyKGZpbGUucGF0aChwdW1wc3RpLCAicHVtcGUuY3N2IikpDQpzZXREVChwdW1wbnkpDQoNCnB1bXBlRFQgPC0gc3Vic2V0KHB1bXBueSwgc2VsZWN0ID0gMToyKQ0KbGlicmFyeShzdHJpbmdpKQ0KcHVtcGVEVFssIHB1bXBlX2FsbCA6PSBzdHJpX3JlcGxhY2VfYWxsX2ZpeGVkKHB1bXBlX255LCAiPyIsICIiKV0NCg0KcHVtcGVEVA0KcHVtcGVPbGQgPC0gcHVtcGVEVFtbImJlaF9pbnNfcHVtcGVfYW5uZXQiXV0NCnB1bXBlTnkgPC0gcHVtcGVEVFtbInB1bXBlX2FsbCJdXQ0KDQpgYGANCg0KTWVyZ2UgZGUgbnllIHN0YW5kYXJkaXNlcnRlIG5hdm5lbmUgdGlsIGhlbGUgZGF0YXNldHRldC4NCg0KYGBge3J9DQpkdFsuKGJlaF9pbnNfcHVtcGVfYW5uZXQgPSBwdW1wZU9sZCwgdG8gPSBwdW1wZU55KSwgb24gPSAiYmVoX2luc19wdW1wZV9hbm5ldCIsIHB1bXBlX3R4dCA6PSBpLnRvIF0NCg0KYGBgDQoNCg0KIyMgSW5qZWtzam9uZXIgaHVydGlndmlyaw0KDQpBbnRhbGwgaW5qZWtzam9uZXIgaHVydGlndmlya2VuZGUgaW5zdWxpbiBwZXIgZMO4Z24uIERlbiBza2FsIGlra2UgdsOmcmUgbWVyIGVuIDEwDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpzdHIoZHQkYmVoX2luc19iZWhfaHVydGlnX2lual9kb2duKQ0KYGBgDQoNCkxpc3RlIG92ZXIgcGFzaWVudGVyIG1lZCB2ZXJkaSBvdmVyIDEwLg0KDQpgYGB7cn0NCmluamVrLmR0IDwtIGNoa1ZlcmRpKGR0LCAiYmVoX2luc19iZWhfaHVydGlnX2lual9kb2duIiwgdmVyZGkgPSAxMCkNCmRpbShpbmplay5kdCkNCg0KY2hrX2luamVrIDwtIGhvc3BPYmooaW5qZWsuZHQpDQpzYXZlUkRTKGNoa19pbmplaywgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX2luamVrLnJkcyIpKQ0KYGBgDQoNCiMjIEluamVrc2pvbmVyIGxhbmd0aWR2aXJrDQoNCkFudGFsbCBpbmpla3Nqb25lciBsYW5ndGlkc3ZpcmtlbmRlIGluc3VsaW4gcGVyIGTDuGduLiBEZW4gc2thbCBpa2tlIHbDpnJlIG1lciBlbm4gMg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0Kc3RyKGR0JGJlaF9pbnNfYmVoX2xhbmdfaWVfZG9nbikNCmBgYA0KDQpMaXN0ZSBvdmVyIHBhc2llbnRlcg0KDQpgYGB7cn0NCmluamVrLmxhbmcgPC0gY2hrVmVyZGkoZHQsICJiZWhfaW5zX2JlaF9sYW5nX2lual9kb2duIiwgdmVyZGkgPSAyKQ0KZGltKGluamVrLmxhbmcpDQpgYGANCg0KIyMgQm9sdXNkb3Nlcg0KDQpBbnRhbGwgYm9sdXNkb3NlciBwZXIgZMO4Z24uIERlbiBza2FsIGlra2UgdsOmcmUgbWVyIGVubiAxMi4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCnN0cihkdCRiZWhfaW5zX3RvdF9lbmhfZG9nbl9ib2x1cykNCmBgYA0KDQoNCmBgYHtyfQ0KYm9sdXNkb3MgPC0gY2hrVmVyZGkoZHQsICJiZWhfaW5zX3RvdF9lbmhfZG9nbl9ib2x1cyIsIHZlcmRpID0gMTIpDQpkaW0oYm9sdXNkb3MpDQoNCmNoa19ib2x1c2RvcyA8LSBob3NwT2JqKGJvbHVzZG9zKQ0Kc2F2ZVJEUyhjaGtfYm9sdXNkb3MsIGZpbGUucGF0aChzdGkudmFzaywgImNoa19ib2x1c2Rvcy5yZHMiKSkNCmBgYA0KDQojIyBOw6Vsc2tpZnRlDQoNCkFudGFsbCBuw6Vsc2tpZnRlLiBTa2FsIGlra2UgdsOmcmUgbWVyIGVubiA1DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpzdHIoZHQkYmVoX2luc19uYWxfc2tpZnRfZG9nbikNCmBgYA0KDQpMaXN0ZSBvdmVyIHBhc2llbnRlcg0KDQpgYGB7cn0NCm5hbHNraWZ0IDwtIGNoa1ZlcmRpKGR0LCAiYmVoX2luc19uYWxfc2tpZnRfZG9nbiIsIHZlcmRpID0gNSkNCmRpbShuYWxza2lmdCkNCg0Kc2V0a2V5KG5hbHNraWZ0LCBob3NwSUQpDQpjaGtfbmFsc2tpZnQgPC0gaG9zcE9iaihuYWxza2lmdCkNCnNhdmVSRFMoY2hrX25hbHNraWZ0LCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfbmFsc2tpZnQucmRzIikpDQpgYGANCg0KIyBBbGJ1bWludW5kZXJzw7hrZWxzZQ0KDQojIyBVcmluIHByw7h2ZSBtaXNzaW5nDQoNClNqZWtrIGZvciBtYW5nbGVyIGVsbGVyIG1pc3NpbmcgZm9yIHVyaW5wcsO4dmUgYGxhYl9yZXNfMXByb3ZlYC4gSHZpcyBtaXNzaW5nIHNrYWwgZGV0IHNqZWtrZXMgb20gZGUgZXIgcmlrdGlnIG1pc3NpbmcgdmVkIMOlIHNlIGF0IGRldCBibGUgYmVzdmFydA0KTkVJIGZvciBgbGFiX3VyaW5wcm92ZWAgZWxsZXIgYXQgZGV0IHZhciBza3JldmV0IMOlcnNhayBpIGBsYWJfdXJpbnByb3ZlX2h2b3Jmb3JgLg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KZHRbLCBzdHIoLlNEKSwgLlNEY29scyA9IGMoImxhYl9yZXNfMXByb3ZlIiwgImxhYl91cmlucHJvdmUiLCAibGFiX3VyaW5wcm92ZV9odm9yZm9yIildDQpgYGANCg0KUGFzaWVudCBsaXN0ZXINCg0KYGBge3J9DQp1cmlucHJvdiA8LSBkdFtpcy5uYShsYWJfcmVzXzFwcm92ZSksIF1bIWlzLm5hKGxhYl91cmlucHJvdmVfaHZvcmZvcikgfCBsYWJfdXJpbnByb3ZlID09IFRSVUUsIGMocGVyc29uLnZhciwgImxhYl9yZXNfMXByb3ZlIiksIHdpdGg9Rl0NCmRpbSh1cmlucHJvdikNCmBgYA0KDQpgYGB7cn0NCnNldGtleSh1cmlucHJvdiwgaG9zcElEKQ0KY2hrX3VyaW5wcm92ZSA8LSBob3NwT2JqKHVyaW5wcm92KQ0Kc2F2ZVJEUyhjaGtfdXJpbnByb3ZlLCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfdXJpbnByb3ZlLnJkcyIpKQ0KYGBgDQoNCiMjIEFsYnVtaW4va3JlYXRpbmluDQoNCkhlbnRlciBrYXRlZ29yaXNrZSB2YXJpYWJlbG5hdm4gYGxhYl9yZXNfMW1lbmhldGAgc2lkZW4gZGVuIGlra2UgZXIga29kZXQgaSB0YWxsLg0KDQpgYGB7cn0NCm1nbW9sIDwtIGdyZXAoIl5tZy9tbW9sIiwgZHQkbGFiX3Jlc18xbWVuaGV0LCB2YWx1ZSA9IFQpWzFdDQptZ2wgPC0gZ3JlcCgiXm1nL2wiLCBkdCRsYWJfcmVzXzFtZW5oZXQsIHZhbHVlID0gVClbMV0NCmBgYA0KDQojIyMgbWcvbW1vbA0KDQpIdmlzIChsYWJfcmVzXzFtZW5oZXQpID0gMiAobWcvbW1vbCkgc2thbCAobGFiX3Jlc18xcHJvdmUpIHNrYWwgZGV0IElLS0UgdsOmcmUgPj0yLjUuDQoNCmBgYHtyfQ0KbWdtb2wuZHQgPC0gZHRbbGFiX3Jlc18xbWVuaGV0ID09IG1nbW9sLCBdDQoNCm1nbW9sLnN1YiA8LSBjaGtWZXJkaShtZ21vbC5kdCwgdmFyID0gImxhYl9yZXNfMXByb3ZlIiwgdmVyZGkgPSAyLjQ5OTkpDQpkaW0obWdtb2wuc3ViKQ0KDQpjaGtfbWdtb2wgPC0gaG9zcE9iaihtZ21vbC5zdWIpDQpzYXZlUkRTKGNoa19tZ21vbCwgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX21nbW9sLnJkcyIpKQ0KYGBgDQoNCg0KIyMjIG1nL2wNCg0KSHZpcyAobGFiX3Jlc18xbWVuaGV0KSA9IDEgKG1nL2wpIHNrYWwgKGxhYl9yZXNfMXByb3ZlKSBza2FsIGRldCBJS0tFIHbDpnJlID49MzANCg0KYGBge3J9DQptZ2wuZHQgPC0gZHRbbGFiX3Jlc18xbWVuaGV0ID09IG1nbCwgXQ0KDQptZ2wuc3ViIDwtIGNoa1ZlcmRpKG1nbC5kdCwgdmFyID0gImxhYl9yZXNfMXByb3ZlIiwgdmVyZGkgPSAyOS45OTk5KQ0KZGltKG1nbC5zdWIpDQoNCmNoa19tZ2wgPC0gaG9zcE9iaihtZ2wuc3ViKQ0Kc2F2ZVJEUyhjaGtfbWdsLCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfbWdsLnJkcyIpKQ0KYGBgDQoNCiMjIyBtaWtyb2cvbWluDQoNCkh2aXMgKGxhYl9yZXNfMW1lbmhldCkgPSAzIChtaWtyb2cvbWluKSBza2FsIChsYWJfcmVzXzFwcm92ZSkgc2thbCBkZXQgSUtLRSB2w6ZyZSA+PTIwDQoNCmBgYHtyfQ0KZHRbLCAuTiwgYnk9LihsYWJfcmVzXzFtZW5oZXQpXQ0KYGBgDQoNCklra2UgZ2pvcnQgZm9yZGkgaW5nZW4gaGFyIGRlbiB2ZXJkaWVuLg0KDQojIFBlcnNpc3RlcmVuZGUgbWljcm9hbGJ1bW5pdXJpDQoNCkh2aXMgc3ZhciB0aWwgdmFyaWFiZWwgYGxhYl9yZXNfcGVyc21pa3JvYCBlciBKQSwgdGFyIHV0IGxpc3RlIGF2IHBhc2llbnRlci4NCg0KYGBge3J9DQpwZXJtaWNybyA8LSBkdFtsYWJfcmVzX3BlcnNtaWtybyA9PSAiSmEiLCBjKHBlcnNvbi52YXIsICJsYWJfcmVzX3BlcnNtaWtybyIpLCB3aXRoPUZdDQpkaW0ocGVybWljcm8pDQoNCmNoa19wZXJtaWNybyA8LSBob3NwT2JqKHBlcm1pY3JvKQ0Kc2F2ZVJEUyhjaGtfcGVybWljcm8sIGZpbGUucGF0aChzdGkudmFzaywgImNoa19wZXJtaWNyby5yZHMiKSkNCg0KYGBgDQoNCg0KIyBJbnN1bGluc2pva2sNCg0KU2pla2sgYXQgYHVuZF9pbnNzam9ra19hbnRgIGVyIElLS0UgbWVyIGVubiA4DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpzdHIoZHQkdW5kX2luc3Nqb2trX2FudCkNCmR0W3VuZF9pbnNzam9ra19hbnQgPiA4LCAuKHVuZF9pbnNzam9ra19hbnQsIGhvc3BpdGFsKV0NCmBgYA0KDQpgYGB7cn0NCmluc19zam9rayA8LSBjaGtWZXJkaShkdCwgInVuZF9pbnNzam9ra19hbnQiLCB2ZXJkaSA9IDgpDQpkaW0oaW5zX3Nqb2trKQ0KDQpjaGtfaW5zc2pva2sgPC0gaG9zcE9iaihpbnNfc2pva2spDQpzYXZlUkRTKGNoa19pbnNzam9raywgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX2luc3Nqb2trLnJkcyIpKQ0KYGBgDQoNCiMgREtBDQoNClNqZWtrIGF0IGB1bmRfa2V0b2FjaWRvc2VfYW50YCBlciBJS0tFIG1lciBlbiA1DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpzdHIoZHQkdW5kX2tldG9hY2lkb3NlX2FudCkNCmBgYA0KDQpMaXN0ZSBhdiBwYXNpZW50ZXINCg0KYGBge3J9DQprZXRkb3NlIDwtIGNoa1ZlcmRpKGR0LCAidW5kX2tldG9hY2lkb3NlX2FudCIsIHZlcmRpID0gNSkNCmRpbShrZXRkb3NlKQ0KDQpjaGtfZGthIDwtIGhvc3BPYmooa2V0ZG9zZSkNCmNoa19ka2ENCmBgYA0KDQojIEbDuGxpbmdlcg0KDQpTamVrayBhdCBgdW5kX2ZvbGluZ19hbnRgIGVyIElLS0UgbWVyIGVubiAxMA0KDQpgYGB7ciBldmFsPUZBTFNFfQ0Kc3RyKGR0JHVuZF9mb2xpbmdfYW50KQ0KYGBgDQoNCkxpc3RlIG92ZXIgcGFzaWVudGVyDQoNCmBgYHtyfQ0KZm9saW5nLmR0IDwtIGNoa1ZlcmRpKGR0LCAidW5kX2ZvbGluZ19hbnQiLCAxMCkNCmRpbShmb2xpbmcuZHQpDQoNCmNoa19mb2xpbmcgPC0gaG9zcE9iaihmb2xpbmcuZHQpDQpzYXZlUkRTKGNoa19mb2xpbmcsIGZpbGUucGF0aChzdGkudmFzaywgImNoa19mb2xpbmcucmRzIikpDQpgYGANCg0KIyBVbmRlcnPDuGtlbHNlDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpkdFssIC5OLCBieT0uKHVuZF9sYXNlcildDQpkdFssIC5OLCBieT0uKHVuZF9SZXRpbm9wYXRpKV0NCmR0WywgLk4sIGJ5PS4odW5kX1JldGlub3BhdGlfdmFsZyldDQpkdFssIC5OLCBieT0uKHVuZF9wZXJpZmVybmV1KV0NCmBgYA0KDQojIyBMYXNlcmJlaGFuZGxpbmcNCg0KTGlzdGUgZm9yIGFsbGUgc29tIHN2YXJ0ZSBKQQ0KDQpgYGB7cn0NCmxhc2VySmEgPC0gZHRbdW5kX2xhc2VyID09ICJKYSIsIGMocGVyc29uLnZhciwgInVuZF9sYXNlciIpLCB3aXRoPUZdDQpkaW0obGFzZXJKYSkNCg0KY2hrX2xhc2VySmEgPC0gaG9zcE9iaihsYXNlckphKQ0Kc2F2ZVJEUyhjaGtfbGFzZXJKYSwgZmlsZS5wYXRoKHN0aS52YXNrLCAiY2hrX2xhc2VySmEucmRzIikpDQpgYGANCg0KIyMgUmV0aW5vcGF0aQ0KDQpMaXN0ZSBvdmVyIGFsbGUgc29tIHN2YXJ0ZSBKYSBww6UgYHVuZF9SZXRpbm9wYXRpYCBvZyBoYXIgc3ZhcnQgcMOlIGB1bmRfUmV0aW5vcGF0aV92YWxnYA0KDQpgYGB7cn0NCnJldGlubzEgPC0gZHRbdW5kX1JldGlub3BhdGkgPT0gIkphIiwgYyhwZXJzb24udmFyLCAidW5kX1JldGlub3BhdGkiLCAidW5kX1JldGlub3BhdGlfdmFsZyIpLCB3aXRoPUZdDQpyZXRpbm8yIDwtIGR0WyFpcy5uYSh1bmRfUmV0aW5vcGF0aV92YWxnKSwgYyhwZXJzb24udmFyLCAidW5kX1JldGlub3BhdGkiLCAidW5kX1JldGlub3BhdGlfdmFsZyIpLCB3aXRoPUZdDQoNCnJldDExID0gcmV0aW5vMVshcmV0aW5vMiwgb24gPSAiUG5yIl0gI3Bhc2llbnQgc29tIGJlZmlubmVzIGJhcmUgaSByZXRpbm8xDQoNCnJldGlubzEyIDwtIHJiaW5kbGlzdChsaXN0KHJldDExLCByZXRpbm8yKSkNCmRpbShyZXRpbm8xMikNCg0KY2hrX3JldGlubyA8LSBob3NwT2JqKHJldGlubzEyKQ0Kc2F2ZVJEUyhjaGtfcmV0aW5vLCBmaWxlLnBhdGgoc3RpLnZhc2ssICJjaGtfcmV0aW5vLnJkcyIpKQ0KYGBgDQoNCiMjIFBlcmlmZXIgbmV1cm9wYXRpDQoNCkxpc3RlIG92ZXIgYWxsZSBzb20gc3ZhcnRlIEpBDQoNCmBgYHtyfQ0KZHRbLCAuTiwgYnk9Lih1bmRfcGVyaWZlcm5ldSldDQpgYGANCg0KTGlzdGUgYXYgcGFzaWVudGVyDQoNCmBgYHtyfQ0KcGVyaWYuZHQgPC0gZHRbdW5kX3BlcmlmZXJuZXUgPT0gIkphIiwgYyhwZXJzb24udmFyLCAidW5kX3BlcmlmZXJuZXUiKSwgd2l0aD1GXQ0KZGltKHBlcmlmLmR0KQ0KDQpjaGtfcGVyaW5ldXJvIDwtIGhvc3BPYmoocGVyaWYuZHQpDQpzYXZlUkRTKGNoa19wZXJpbmV1cm8sIGZpbGUucGF0aChzdGkudmFzaywgImNoa19wZXJpbmV1cm8ucmRzIikpDQpgYGANCg0KIyBQYXNpZW50ZXIgbWVkIGZsZXJlIMOlcnNrb250cm9sbA0KDQpEaXNzZSBlciBwYXNpZW50ZXIgc29tIGdqZW5ub21mw7hydCDDpXJza29udHJvbGwgbWVyIGVubiB0byBnYW5nZXIuIMOFcnNrb250cm9sbCBza2FsIGJhcmUgdGVsbGVzIGVuIGdhbmcuIERlcmZvciBza2FsIGRldCB2w6ZyZSBLVU4gZW4gw6Vyc2tvbnRyb2xsIHBlciBwYXNpZW50IG9nIHN5a2VodXNldA0KbcOlIGtvbnRha3RlcyBmb3Igw6UgYmVrcmVmdGUgaHZpbGtldCBhdiBkZW0gc2thbCBiZWhvbGRlcy4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCmRpbShkdCkNCmR0W2R1cGxpY2F0ZWQoUG5yKSwgLihQbnIpXQ0KZHRbUGFzaWVudElEID09IDcwNzEsIC4oUGFzaWVudElELCBQbnIsIE9wcGhvbGRJRCwgIGhvc3BpdGFsLCBGTmF2biwgRU5hdm4sIGRpYWdEYXRvLCBkaWFiZXRlc19UeXBlMSwgaW5uX0RhdG8sIHlyKV0NCmBgYA0KDQpzeWtlaHVzIGkgVmVzdGZvbGQgaGFyIGJla3JlZnRldCBhdCDDpXJza29udHJvbGwgc29tIHZhciB0YXR0IGkgMTguMDQuMjAxOCBlciBkZW4gZ2plbGRlbmUuDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpkdCA8LSBkdFtPcHBob2xkSUQgIT0gMTE1NDQ5LCBdDQpkaW0oZHQpDQpgYGANCg0KDQoNCiMgUGFzaWVudGVyICBtZWQgaW5nZW4gZWxsZXIgZmxlcmUgZGlhZ25vc2VyDQoNCk5vZW4gcGFzaWVudGVyIGhhciBibGl0dCByZWdpc3RyZXJ0IG1lZCBmbGVyZSBkaWFnbm9zZXIgZWxsZXIgaW5nZW4uIEh2ZW0gZXIgZGlzc2U/DQpOw6VyIGxpc3RlIGF2IHBhc2llbnRlciBlciBsYWdldCBzw6Ugc2thbCBTaXYgSmFubmUga29udGFrdGVyIHN5a2VodXNlbmUgZm9yIHZhcmlmaXNlcmluZy4NCg0KYGBge3IgZHR5cGUsIGV2YWw9Rn0NCg0KIyMgRGVtb2dyYWZpc2sgdmFyaWFibGVyDQpkZW1vVmFyIDwtICBjKCJQYXNpZW50SUQiLCAiUG5yIiwgImhvc3BpdGFsIiwgImhvc3BJRCIsICJob3NLb3J0IiwgImFsZGVyIiwgIktqb25uIiwgImRpYWdWYXIiLCAiZGlhZ0FsZGVyIikNCg0KIyMgZGlhYmV0ZXMgdmFyaWFibGVyDQpkaWFiZXRlc1ZhciA9IGMoImRpYWJldGVzX1R5cGUxIiwgImRpYWJldGVzX1R5cGUyIiwgImRpYWJldGVzX01vZHkiLCAiZGlhYmV0ZXNfS2lyNjIiLCAiZGlhYmV0ZXNfU2VrRGlhYmV0ZXMiLCAiZGlhYmV0ZXNfQW5uZW5EaWFiZXRlcyIsICJkaWFiZXRlc19Va2plbnREaWFiZXRlcyIpDQoNCiMjIGxhZ2Ugc3Vic2V0DQpkaWFiRFQgPC0gZHRbLCBjKGRlbW9WYXIsIGRpYWJldGVzVmFyKSwgd2l0aCA9IEZdDQoNCiMjIHRyaW0gd2hpdGVzcGFjZQ0KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KZm9yIChqIGluIGRpYWJldGVzVmFyKXsNCiAgaWYoY2xhc3MoZGlhYkRUW1tqXV0pID09ICdjaGFyYWN0ZXInKQ0KICAgIHNldChkaWFiRFQsIGogPSBqLCB2YWx1ZSA9IHRyaW13cyhkaWFiRFRbW2pdXSkpDQp9DQoNCiMjICMjIHZhbmxpZyBsb29wIGZvciB3aGl0ZXNwYWNlIGRlbGV0aW9uLiBPQlMhISBUcmVnZ2VyZSBlbm4gbG9vcCBzZXQNCiMjIGZvciAoaiBpbiBkaWFiZXRlc1Zhcil7DQojIyAgZGlhYkRUWywgKGopIDo9IHRyaW13cyhnZXQoaikpXQ0KIyMgfQ0KDQoNCiMjIENoZWNrIHdoaXRlc3BhY2UgZXIgYm9ydGUNCmRpYWJEVFtQbnIgPT0gNzA5MDE5NzQ4MSwgLihkaWFiZXRlc19LaXI2MildW1sxXV0NCg0KIyMgIyMgbcOldHRlIGdqw7hyZSBkZXQgbmVzdGVuIG1hbnVlbHQgc2lkZW4gdHJpbXdzIGZvciBhbGxlIGtvbG9ubmUgaSBkYXRhLnRhYmxlIGlra2UNCiMjICMjIGZ1bmtlciBzb20gZGV0IGLDuHIgdsOmcmUNCiMjIGRpYWJEVFshaXMubmEoZGlhYmV0ZXNfS2lyNjIpLCBkaWFiZXRlc19LaXI2MiAgOj0gdHJpbXdzKGRpYWJldGVzX0tpcjYyKV0NCg0KIyMgTGFnZSBlbiBsb25nIGRhdGFzZXQNCmRpYWJMZyA8LSBtZWx0KGRhdGEgPSBkaWFiRFQsDQogIGlkLnZhcnMgPSBkZW1vVmFyLA0KICBtZWFzdXJlLnZhcnMgPSBkaWFiZXRlc1ZhciwNCiAgdmFyaWFibGUubmFtZSA9ICJkaWFiVHlwZSIsDQogIHZhbHVlLm5hbWUgPSAiamFuZWkiKQ0KDQpkaWFiTEwgPC0gY29weShkaWFiTGcpDQoNCiMjIG9ta29kZSBhbGwgc29tIHN2YXJ0ZSBKYSB0aWwgZGlhYmV0ZXMgVHlwZSB0aWwgMQ0KZGlhYkxMWy4oamFuZWkgPSAiSmEiLCBkaWFiVHlwZSA9IGRpYWJldGVzVmFyLCB0byA9IDFMKSwNCiAgb24gPSBjKCJqYW5laSIsICJkaWFiVHlwZSIpLCBkaWFiIDo9IGkudG9dDQoNCiMjIEtvbnZlcnRlcmUgdGlsIHdpZGUNCmRpYWJXaWRlIDwtIGRjYXN0KGRhdGEgPSBkaWFiTEwsDQogIGZvcm11bGEgPSBQbnIgKyBob3NwSUQgKyBob3NLb3J0IH4gZGlhYlR5cGUsIHZhbHVlLnZhciA9ICJkaWFiIikNCg0KIyMjIFNqZWtrDQpkaWFiV2lkZQ0KZGlhYldpZGVbZGlhYmV0ZXNfS2lyNjIgPT0gMSwgLihQbnIsIGhvc3BJRCwgaG9zS29ydCwgZGlhYmV0ZXNfS2lyNjIpXQ0KDQojIyBieXR0IE5BIHRpbCAwDQpmb3IgKGogaW4gZGlhYmV0ZXNWYXIpew0KICBzZXQoZGlhYldpZGUsIHdoaWNoKGlzLm5hKGRpYWJXaWRlW1tqXV0pKSwgaiwgdmFsdWUgPSAwTCkNCn0NCg0KDQojIyBTdW1tZXJlIGZvciDDpSBmaW5uZSBtaXNzaW5nIGZvciBhbHQgZGlhYmV0ZXMgdHlwZQ0KZGlhYldpZGVbLCBodmVtIDo9IHJvd1N1bXMoLlNEKSwgLlNEY29scyA9IGRpYWJldGVzVmFyIF0NCg0KZGlhYldpZGVbLCAuTiwgYnkgPSBodmVtXQ0KDQojIyBzb20gaGFyIGluZ2VuIHR5cGUgZGlhYmV0ZXMNCiMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmRpYU4gPC0gZGlhYldpZGVbaHZlbSA9PSAwLCAuKFBucildW1sxXV0NCg0KZGlhTm9OIDwtIGxpc3QoKQ0KZm9yIChqIGluIGRpYU4pew0KICBudiA8LSBwYXN0ZTAoImEiLCBqKSAjbcOlIGhhdiB2YWxpZCBuYXZuIG9nIGlra2UgYmFyZSB0YWxsDQogIGRkIDwtIGR0W1BuciA9PSBqLCBjKCJQbnIiLCAiaG9zcElEIiwgImhvc0tvcnQiLCBkaWFiZXRlc1ZhciksIHdpdGggPSBGXQ0KICBkaWFOb05bW252XV0gPC0gZGQNCn0NCg0KZGlhTm9OcGVyc29uIDwtIHJiaW5kbGlzdChkaWFOb04pDQpkaWFOb05wZXJzb24NCmRpYU5vTnBlcnNvblssIC4oUG5yLCBob3NwSUQsIGhvc0tvcnQsIGRpYWJldGVzX0tpcjYyKV0NCg0KdmV0SWtrZURpYWIgPC0gZGlhTm9OcGVyc29uW1sxXV0NCg0KDQojIyBTaXYgSmFubmUgaGFyIGtvbnRyb2xsZXQgb2cgZGlzc2UgZXIgcmVzdWx0YXQgc29tIG3DpSByZXR0ZXMgaSBkYXRhc2V0dGV0DQp2ZXRkdDEgPC0gdmV0SWtrZURpYWJbYygxOjIsIDQ6NSwgODoxMildICNEVDENCnZldE1vZHkgPC0gdmV0SWtrZURpYWJbNl0gI01vZHkNCg0KZGlhTm9EaWFnIDwtIGNvcHkoZGlhTm9OcGVyc29uKQ0KZGlhTm9EaWFnW1BuciAlaW4lIHZldGR0MSwgZGlhYmV0ZXNfVHlwZTEgOj0gIkphIl0gI0RUMQ0KZGlhTm9EaWFnW1BuciA9PSB2ZXRNb2R5LCBkaWFiZXRlc19Nb2R5IDo9ICJKYSJdDQpkaWFOb0RpYWcNCg0KIyMgcmVwbGFjZSBtYWluIGRhdGFzZXRzIHZhbHVlIHdpdGggdGhlIGNvcnJlY3RlZCB2YWx1ZXMNCmRpYWJWYXIuaSA8LSBwYXN0ZTAoImkuIiwgZGlhYmV0ZXNWYXIpDQpkdFtkaWFOb0RpYWcsIChkaWFiZXRlc1ZhcikgOj0gbWdldChkaWFiVmFyLmkpLCBvbiA9ICJQbnIiXQ0KDQojIyBwYXNpZW50ZXIgbWVkIHRvIHR5cGUgZGlhYmV0ZXMNCiMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpkaWEyIDwtIGRpYWJXaWRlW2h2ZW0gPT0gMiwgLihQbnIpXVtbMV1dDQoNCmRpYTJEVCA8LSBsaXN0KCkNCmZvciAoaiBpbiBkaWEyKXsNCiAgbnYgPC0gcGFzdGUwKCJhIiwgaikgI23DpSBoYXYgdmFsaWQgbmF2biBvZyBpa2tlIGJhcmUgdGFsbA0KICBkZCA8LSBkdFtQbnIgPT0gaiwgYygiUG5yIiwgImhvc3BJRCIsICJob3NLb3J0IiwgZGlhYmV0ZXNWYXIpLCB3aXRoID0gRl0NCiAgZGlhMkRUW1tudl1dIDwtIGRkDQp9DQoNCmRpYTJEVHBlcnNvbiA8LSByYmluZGxpc3QoZGlhMkRUKQ0KZGlhMkRUcGVyc29uDQoNCmRpYWJWYXIxID0gYygiZGlhYmV0ZXNfVHlwZTIiLCAiZGlhYmV0ZXNfTW9keSIsICJkaWFiZXRlc19LaXI2MiIsICJkaWFiZXRlc19TZWtEaWFiZXRlcyIsICJkaWFiZXRlc19Bbm5lbkRpYWJldGVzIiwgImRpYWJldGVzX1VramVudERpYWJldGVzIikNCg0KZGlhMkRUcGVyc29uW2RpYWJldGVzX1R5cGUxID09ICJKYSIsIChkaWFiVmFyMSkgIDo9IE5BXQ0KIyMgcGVyc29uZXIgbWVkIGZsZXJlIGRpYWdub3Nlcg0KZHQxcGVyc29uIDwtIGRpYTJEVHBlcnNvblssIFBucl0NCmR0MXBlcnNvbg0KDQojIyBEZSBzb20gaGFyIERUMSBvZyBhbm5lbiB0eXBlIGJsaXIgYmFyZSBtZWQgRFQxLg0KcGVyZHQxIDwtIGR0MXBlcnNvblstYygxOjIpXSAjRFQxDQpwZXJraXIgPC0gZHQxcGVyc29uWzJdICNraXI2Mg0KcGVyYW5uIDwtIGR0MXBlcnNvblsxXSAjYW5uZW4NCg0KIyMgVMO4bW1lIGFsbGUgdmVyZGllIGZvciBkZSBtZWQgbWluc3QgdG8gc8OlIGxlZ2dlIHRpbGJha2UgZGV0IHJpa3RpZ2UgZXR0ZXIgYXQNCiMjIFNpdiBKYW5uZSBoYXIga29udG9ybGxlcnQNCmRpYTJkaWFnIDwtIGNvcHkoZGlhMkRUcGVyc29uKQ0KZGlhMmRpYWdbLCAoZGlhYmV0ZXNWYXIpIDo9IE5BXQ0KZGlhMmRpYWdbUG5yICAlaW4lIHBlcmR0MSwgZGlhYmV0ZXNfVHlwZTEgOj0gIkphIl0gI0RUMQ0KZGlhMmRpYWdbUG5yID09IHBlcmtpciwgZGlhYmV0ZXNfS2lyNjIgOj0gIkphIl0gI0tpcmE2Mg0KZGlhMmRpYWdbUG5yID09IHBlcmFubiwgZGlhYmV0ZXNfQW5uZW5EaWFiZXRlcyA6PSAiSmEiXSAjQW5uZW4NCmRpYTJkaWFnDQoNCiMjIGJ5dHRlIHZlcmRpIGkgaG92ZWQgZGF0YXNldHRldCBtZWQga29ycmlnZXJ0IHZlcmRpDQpkdFtkaWEyZGlhZywgKGRpYWJldGVzVmFyKSA6PSBtZ2V0KGRpYWJWYXIuaSksIG9uID0gIlBuciJdDQoNCmBgYA0KDQojIFNhdmUgRGF0YXNldHQNCg0KTGFnZXIgYmVhcmJlaWRldCBkYXRhc2V0dGV0IGZvciB2YWxpZGVyaW5nLiBEYXRhc2V0dGV0IGlubmVob2xkZXIgYWxsZSBueWUgbGFnZGUgdmFyaWFibGVuZSBzb20gYnJ1a2VzIHRpbCDDpSBnasO4cmUgdmFsaWRlcmluZyBvZyBzZW5lcmUgc2thbCBicnVrZXMgaSBhbmFseXNlbiBibC5hDQoNCi0gX2hvc3BpdGFsXyAgICAgOiBTeWtlaHVzDQotIF9kb2JfICAgICAgICAgIDogRsO4ZHNlbHNkYXRvIGkgZGF0byBmb3JtYXQNCi0gX2FsZGVyXyAgICAgICAgOiBBbGRlciBmb3IgcGFzaWVudGVyDQotIF9kaWFnVmFyXyAgICAgIDogRGlhYmV0ZXMgdmFyaWdoZXQNCi0gX2FsZGVyQXJjXyAgICAgOiBBbGRlciB2ZWQgbWVuYXJjaGUNCi0gX2RpYWdBbGRlcl8gICAgOiBBbGRlciB2ZWQgZGlhZ25vc2UNCi0gX2RpYWdEYXRvXyAgICAgOiBEaWFnbm9zZSBkYXRvIGZvciDDpXJza29udHJvbGwgc2lkZW4gKmlubl9EaWFnZGF0byogZmlubmVzIGJhcmUgaSBmw7hyc3RlZ2FuZ3NyZWdpc3RyZXJpbmcNCi0gX3B1bXBfdHh0XyAgICAgOiBPbWtvZGV0ICpiZWhfaW5zX3B1bXBlX2FubmV0KiBzb20gZXIgZnJpdGVrc3QgdGlsIGV0IHN0YW5kYXJkaXNlcnRlIGxpc3RlIGV0dGVyIGF0IFNpdiBKYW5uZSBoYXIgZ8OldHQgZ2plbm5vbSBsaXN0ZW4uDQoNCg0KYGBge3IgZXZhbD1GQUxTRX0NCnNhdmVSRFMoZHQsICJ2YWxpZGVydF9hcnNrb250cm9sbDIwMTgucmRzIikNCmR0IDwtIHJlYWRSRFMoInZhbGlkZXJ0X2Fyc2tvbnRyb2xsMjAxOC5yZHMiKQ0KYGBgDQoNCiMgRXhjZWwgcGVyIHN5a2VodXMNCg0KTGFnZXIgRXhjZWwgZmlsIHBlciBTeWtlaHVzDQoNCg0KYGBge3J9DQojIyBsYWdlIHRvbSBkYXRhc2V0IGh2aXMgTlVMTA0KbnVsbERUIDwtIGZ1bmN0aW9uKHgpew0KICBudWxsTmFtZXMgPC0gbmFtZXMoeFtsZW5ndGhzKHgpIT0wXVtbMV1dKSAjZsOlciBjb2xuYW1lcw0KICBtaXNzQ29sIDwtIGRhdGEudGFibGUobWF0cml4KG5jb2wgPSBsZW5ndGgobnVsbE5hbWVzKSwgbnJvdyA9IDApKSAjbGFnZSB0b20gZGF0YS50YWJsZQ0KICBkYXRhLnRhYmxlOjpzZXRuYW1lcyhtaXNzQ29sLCBuYW1lcyhtaXNzQ29sKSwgbnVsbE5hbWVzKSAjZ2lyIHRvbSBkYXRhLnRhYmxlIHJpa3RpZyBjb2xuYW1lcw0KICANCiAgaWtrZU1pcyA8LSB3aGljaChsZW5ndGhzKHgpIT0wKSAjaW5kZXggc29tIGlra2UgZXIgTlVMTA0KICAjIG51bGxJbmQgPC0gd2hpY2gobGVuZ3Rocyh4KT09MCkgI2ZpbmQgaW5kZXggd2l0aCBOVUxMDQogIA0KICBzeWtJbmQgPC0gaG9zRFQkaG9zcElEDQogIG1pc3NJbmQgPC0gd2hpY2goIShzeWtJbmQgJWluJSBpa2tlTWlzKSkNCiAgDQogIGZvciAoaSBpbiBtaXNzSW5kKXsNCiAgICB4W1tpXV0gPC0gbWlzc0NvbA0KICB9DQogIA0KICByZXR1cm4oeCkNCn0NCg0KDQpzZXRrZXkoaG9zRFQsIGhvc3BJRCkNCg0KIyMgTGFnZSBFeGNlbCBwZXIgc3lrZWh1cw0KDQpmb3IgKGkgaW4gaG9zRFQkaG9zcElEKSB7DQogIA0KICAjIGxpc3RlIG92ZXIgQ0hLIHZhcmlhYmVsZW5lDQogIHZhcjEgPC0gbnVsbERUKGNoa19oYmExYylbW2ldXQ0KICB2YXIyIDwtIG51bGxEVChjaGtfTmFzailbW2ldXQ0KICB2YXIzIDwtIG51bGxEVChjaGtfZGlhYnR5cGUpW1tpXV0NCiAgdmFyNCA8LSBudWxsRFQoY2hrX2JtaV91bmRlcjEyKVtbaV1dDQogIHZhcjUgPC0gbnVsbERUKGNoa19ibWlfb3ZlcjM1KVtbaV1dDQogIHZhcjYgPC0gbnVsbERUKGNoa19kaWV0KVtbaV1dDQogIHZhcjcgPC0gbnVsbERUKGNoa19hbGRlcjE5KVtbaV1dDQogIHZhcjggPC0gbnVsbERUKGNoa192YXJpaGV0MClbW2ldXQ0KICB2YXI5IDwtIG51bGxEVChjaGtfbWVuYXJjaDkpW1tpXV0NCiAgdmFyMTAgPC0gbnVsbERUKGNoa19tZW5hcmNoMTYpW1tpXV0NCiAgdmFyMTEgPC0gbnVsbERUKGNoa19kaWFnQWxkZXIwKVtbaV1dDQogIHZhcjEyIDwtIG51bGxEVChjaGtfYmxvZHRyeWtrKVtbaV1dDQogIHZhcjEzIDwtIG51bGxEVChjaGtfTERMMjYpW1tpXV0NCiAgdmFyMTQgPC0gbnVsbERUKGNoa19tdWx0aSlbW2ldXQ0KICB2YXIxNSA8LSBudWxsRFQoY2hrX3B1bXBlKVtbaV1dDQogIHZhcjE2IDwtIG51bGxEVChjaGtfaW5qZWspW1tpXV0NCiAgdmFyMTcgPC0gbnVsbERUKGNoa19ib2x1c2RvcylbW2ldXQ0KICB2YXIxOCA8LSBudWxsRFQoY2hrX25hbHNraWZ0KVtbaV1dDQogIHZhcjE5IDwtIG51bGxEVChjaGtfdXJpbnByb3ZlKVtbaV1dDQogIHZhcjIwIDwtIG51bGxEVChjaGtfbWdtb2wpW1tpXV0NCiAgdmFyMjEgPC0gbnVsbERUKGNoa19tZ2wpW1tpXV0NCiAgdmFyMjIgPC0gbnVsbERUKGNoa19wZXJtaWNybylbW2ldXQ0KICB2YXIyMyA8LSBudWxsRFQoY2hrX2luc3Nqb2trKVtbaV1dDQogIHZhcjI0IDwtIG51bGxEVChjaGtfZm9saW5nKVtbaV1dDQogIHZhcjI1IDwtIG51bGxEVChjaGtfbGFzZXJKYSlbW2ldXQ0KICB2YXIyNiA8LSBudWxsRFQoY2hrX3JldGlubylbW2ldXQ0KICB2YXIyNyA8LSBudWxsRFQoY2hrX3BlcmluZXVybylbW2ldXQ0KDQogIHN5ay5saXN0IDwtIGxpc3QoImhiYTFjIiA9IHZhcjEsDQogICAgICAgICAgICAgICAgICJuYXNqb25hbGl0ZXQiID0gdmFyMiwNCiAgICAgICAgICAgICAgICAgImRpYVR5cGUiID0gdmFyMywNCiAgICAgICAgICAgICAgICAgImJtaV91bmRlcjEyIiA9IHZhcjQsDQogICAgICAgICAgICAgICAgICJibWlfb3ZlcjM1IiA9IHZhcjUsDQogICAgICAgICAgICAgICAgICJkaWV0IiA9IHZhcjYsDQogICAgICAgICAgICAgICAgICJhbGRlcjE5IiA9IHZhcjcsDQogICAgICAgICAgICAgICAgICJ2YXJpZ2hldCIgPSB2YXI4LA0KICAgICAgICAgICAgICAgICAibWVuYXJjaDkiID0gdmFyOSwNCiAgICAgICAgICAgICAgICAgIm1lbmFyY2gxNiIgPSB2YXIxMCwNCiAgICAgICAgICAgICAgICAgImRpYWdBbGRlcjAiID0gdmFyMTEsDQogICAgICAgICAgICAgICAgICJibG9kdHJ5a2siID0gdmFyMTIsDQogICAgICAgICAgICAgICAgICJMREwyNiIgPSB2YXIxMywNCiAgICAgICAgICAgICAgICAgIm11bHRpaW5qZWt0MiIgPSB2YXIxNCwNCiAgICAgICAgICAgICAgICAgInB1bXBlMiIgPSB2YXIxNSwNCiAgICAgICAgICAgICAgICAgImluamVrX2h1cnRpZzEwIiA9IHZhcjE2LA0KICAgICAgICAgICAgICAgICAiYm9sdXNfZG9zZSIgPSB2YXIxNywNCiAgICAgICAgICAgICAgICAgIm5hYWxza2lmdCIgPSB2YXIxOCwNCiAgICAgICAgICAgICAgICAgInVyaW4iID0gdmFyMTksDQogICAgICAgICAgICAgICAgICJ1cmluX21nX21tb2wiID0gdmFyMjAsDQogICAgICAgICAgICAgICAgICJ1cmluX21nbCIgPSB2YXIyMSwNCiAgICAgICAgICAgICAgICAgInBlcnNpc3RfbWljcm8iID0gdmFyMjIsDQogICAgICAgICAgICAgICAgICJpbnN1bGluc2pva2siID0gdmFyMjMsDQogICAgICAgICAgICAgICAgICJmb2xpbmciID0gdmFyMjQsDQogICAgICAgICAgICAgICAgICJsYXNlckpBIiA9IHZhcjI1LA0KICAgICAgICAgICAgICAgICAicmV0aW5vcGF0aSIgPSB2YXIyNiwNCiAgICAgICAgICAgICAgICAgInBlcmlmZXJfbmV1cm8iID0gdmFyMjcNCiAgICAgICAgICAgICAgICAgKQ0KDQoNCiAgc3lrZWh1c05hdm4gPC0gaG9zRFQkaG9zS29ydFtob3NEVCRob3NwSUQ9PWldDQogIA0KICBzdGlsIDwtIG9wZW54bHN4OjpjcmVhdGVTdHlsZShib3JkZXIgPSAidG9wIiwgZmdGaWxsID0gIiM0MmViZjQiKQ0KICANCiAgb3Blbnhsc3g6OndyaXRlLnhsc3goc3lrLmxpc3QsIGZpbGUucGF0aCgiLi9kYXRhdmFzay9wZXJfc3lrZWh1cyIsIHBhc3RlMChmb3JtYXQoU3lzLkRhdGUoKSwgIiVZJW0lZCIpLCAiXyIsIHN5a2VodXNOYXZuLCAiLnhsc3giKSwgZnNlcCA9ICcvJyksDQogICAgICAgICAgICAgICAgICAgICBoZWFkZXJTdHlsZSA9IHN0aWwsIGJvcmRlcnMgPSAicm93cyIsIGNvbFdpZHRocyA9ICJhdXRvIikNCiAgDQp9DQoNCg0KYGBgDQoNCiMjIEV4Y2VsIHRlc3QNCg0KTWFudWVsdCB0ZXN0DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQojIHZhcjEgPC0gY2hrX2hiYTFjW1sxXV0NCiMgdmFyMiA8LSBjaGtfTmFzaltbMV1dDQojIHZhcjMgPC0gY2hrX2RpYWJ0eXBlW1sxXV0NCiMgdmFyNCA8LSBjaGtfYm1pX3VuZGVyMTJbWzFdXQ0KIyB2YXI1IDwtIGNoa19ibWlfb3ZlcjM1W1sxXV0NCiMgdmFyNiA8LSBjaGtfZGlldFtbMV1dDQojIA0KIyAjIyB0YXIgYm9ydCBhbGxlIE5VTEwNCiMgY2hrX2RpZXRbbGVuZ3RocyhjaGtfZGlldCkgIT0gMF0NCiMgDQojICMjIGdldCBjb2xuYW1lcyBpZiBtaXNzaW5nDQojIG51bGxOYW1lcyA8LSBuYW1lcyhjaGtfZGlldFtsZW5ndGhzKGNoa19kaWV0KSE9MF1bWzFdXSkNCiMgbWlzc0NvbCA8LSBkYXRhLnRhYmxlKG1hdHJpeChuY29sID0gbGVuZ3RoKG51bGxOYW1lcyksIG5yb3cgPSAwKSkNCiMgbWlzc0NvbA0KIyBkYXRhLnRhYmxlOjpzZXRuYW1lcyhtaXNzQ29sLCBuYW1lcyhtaXNzQ29sKSwgbnVsbE5hbWVzKQ0KIyANCiMgDQojIGRhdGEudGFibGU6OnNldGNvbG9yZGVyKHZhcjEsIHBlcnNvbi52YXIpDQojIHZhcjENCiMgDQojIHN5ay5saXN0IDwtIGxpc3QoImhiYTFjIiA9IHZhcjEsDQojICAgICAgICAgICAgICAgICAgIm5hc2pvbmFsaXRldCIgPSB2YXIyLA0KIyAgICAgICAgICAgICAgICAgICJkaWFUeXBlIiA9IHZhcjMsDQojICAgICAgICAgICAgICAgICAgImJtaV91bmRlcjEyIiA9IHZhcjQsDQojICAgICAgICAgICAgICAgICAgImJtaV9vdmVyMzUiID0gdmFyNSwNCiMgICAgICAgICAgICAgICAgICAiZGlldCIgPSB2YXI2KQ0KIyANCiMgb3Blbnhsc3g6OndyaXRlLnhsc3goc3lrLmxpc3QsIGZpbGUucGF0aCgiLi9kYXRhdmFzayIsIHBhc3RlKGZvcm1hdChTeXMuRGF0ZSgpLCAiJVklbSVkIiksICJBa2Vyc2h1cy54bHN4Iiwgc2VwID0gIl8iKSwgZnNlcCA9ICcvJyksDQojICAgICAgICAgICAgICAgICAgICAgICBhc1RhYmxlID0gVFJVRSwgY29sV2lkdGhzID0gImF1dG8iKQ0KDQpgYGANCg0KDQpQcsO4dmVyIMOlIGzDuHNlIHZlZCBicnVrIGF2IGZ1bmtzam9uLg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KDQojIyBMaXN0IG92ZXIgIkNIRUNLIiB2YXJpYWJsZXINCiMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBjaGtfdmFyIDwtIGdyZXAoIl5jaGtfIiwgbHMoKSwgdmFsdWUgPSBUUlVFKQ0KIyBldmFsKGFzLm5hbWUoY2hrX3ZhclsxXSkpDQoNCiMgY2hrX3ZhciA8LSBjKCJjaGtfTmFzaiIsIA0KIyAgICAgICAgICAgICAiY2hrX2hiYTFjIiwgDQojICAgICAgICAgICAgICJjaGtfZGlhYnR5cGUiLCANCiMgICAgICAgICAgICAgImNoa19kaWV0IiwgDQojICAgICAgICAgICAgICJjaGtfYm1pX3VuZGVyMTIiLCANCiMgICAgICAgICAgICAgImNoa19ibWlfb3ZlcjM1IikNCiMgDQojIA0KIyBsaXN0X3ZhciA8LSBsaXN0KGNoa19OYXNqLCANCiMgICAgICAgICAgICAgICAgICBjaGtfaGJhMWMsIA0KIyAgICAgICAgICAgICAgICAgIGNoa19kaWFidHlwZSwgDQojICAgICAgICAgICAgICAgICAgY2hrX2RpZXQsIA0KIyAgICAgICAgICAgICAgICAgIGNoa19ibWlfdW5kZXIxMiwgDQojICAgICAgICAgICAgICAgICAgY2hrX2JtaV9vdmVyMzUpDQojIA0KIyANCiMgdGFiTmF2biA8LSBjKCJuYXNqb25hbGl0ZXQiLA0KIyAgICAgICAgICAgICAgImhiYTFjIiwNCiMgICAgICAgICAgICAgICJkaWFUeXBlIiwNCiMgICAgICAgICAgICAgICJkaWV0IiwNCiMgICAgICAgICAgICAgICJibWlfdW5kZXIxMiIsIA0KIyAgICAgICAgICAgICAgImJtaV9vdmVyMzUiKQ0KIyANCiMgDQojIG5hbWVzKGxpc3RfdmFyKSA8LSBwYXN0ZSgiVmFyciIsIDE6bGVuZ3RoKGxpc3RfdmFyKSwgc2VwID0gIiIpDQojIGxpc3QyZW52KGxpc3RfdmFyLCBlbnZpciA9IC5HbG9iYWxFbnYpDQojIA0KIyANCiMgdmFyLkxpc3QgPC0gbGlzdCgpDQojIA0KIyBmb3IgKGYgaW4gc2VxX2Fsb25nKGNoa192YXIpKXsNCiMgICANCiMgICAjZ2lyIG9iamVjdCBuYXZuDQojICAgYXNzaWduKHggPSBwYXN0ZSgiLnZhciIsIGYsIHNlcCA9ICIiKSwgdmFsdWUgPSBjaGtfdmFyW2ZdKQ0KIyAgICMgDQojICAgIyBsaHMgPC0gcGFzdGUoImFzLmNoYXJhY3RlcigiLCB0YWJOYXZuW2ZdLCAiKSIsIHNlcCA9ICIiKQ0KIyAgICMgcmhzIDwtIHBhc3RlKCJldmFsKGFzLm5hbWUoZ2V0KHBhc3RlKCcudmFyJyIsIGYsICJzZXAgPSAnJykpKSkiLCBzZXAgPSAnJykNCiMgICAjIGxyaHMgPC0gcGFzdGUobGhzLCByaHMsIHNlcCA9ICI9IikNCiMgICAjIHZhci5MaXN0W1tmXV0gPC0gZXZhbChwYXJzZSh0ZXh0PWxyaHMpKQ0KIyAgIA0KIyAgIHRlc3RWYXIgPC0gYXMubmFtZShnZXQocGFzdGUoIi52YXIiLCBmLCBzZXAgPSAiIikpKQ0KIyAgIA0KIyAgIHZhci5MaXN0W1tmXV0gPC0gcGFzdGUodGFiTmF2bltmXSwgdGVzdFZhciwgc2VwID0gIj0iKQ0KIyAgIA0KIyB9DQoNCg0KIyBsaXN0MmVudigpDQojIA0KIyB4IDwtIGFzLmxpc3Qocm5vcm0oMTApKQ0KIyB4DQojIG5hbWVzKHgpIDwtIHBhc3RlKCJhYSIsIDE6bGVuZ3RoKHgpLCBzZXAgPSAiIikNCiMgbGlzdDJlbnYoeCwgZW52aXIgPSAuR2xvYmFsRW52KQ0KYGBgDQoNCiMgQW5vbnltaXNlcmluZw0KDQpUYXIgYm9ydCBpZGVudGlmaXNlcnRiYXIgdmFyaWFibGVyIGZvciBhbm9ueW1pc2VyaW5nLg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KYW5vbnltVmFyIDwtIGMoIkZOciIsICJFTmF2biIsICJGTmF2biIsICJNTmF2biIsICJQb3N0TnIiLCAiUG9zdFN0ZWQiLCAiQWRyZXNzZSIsICJBZGRyZXNzZSIsICJQbnIiLCAiZm5yIikNCg0KZHQgPC0gcmVhZFJEUygidmFsaWRlcnRfYXJza29udHJvbGwyMDE4LnJkcyIpDQpkaW0oZHQpDQoNCmFub255bURUIDwtIGR0WywgKGFub255bVZhcikgOj0gTlVMTF0NCnNhdmVSRFMoYW5vbnltRFQsICJhbm5vbnltX2R0MjAxOC5SRFMiKQ0KDQpgYGANCg0KDQojIEV4Y2VsIGZpbA0KDQpMYWdlciBFeGNlbGZpbCBmb3IgZGF0YXNvbSBicnVrZXMgdGlsIGFuYWx5c2UgZm9yIMOlcnNyYXBwb3J0Lg0KDQpgYGB7ciwgZXZhbD1GfQ0KcmlvOjpleHBvcnQoZHQsICIuL0V4Y2VsZmlsZXIvYXJza29udHJvbGwyMDE4Lnhsc3giKQ0KYGBgDQoNCg==