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==