Johdanto

Tämä dokumentti sisältää “Epidemiologian alkeet”-kirjan sisältämät R-ohjelman koodit ja niiden tuottamat tulokset.

Mittaaminen

library(Epi)

# Binomial
tmp.x <- 0:10
tmp.y <- dbinom(tmp.x, size = 10, prob = 0.18)
plot(tmp.x, 100 * tmp.y, type = "h", lwd = 8, xlab = "Lukumäärä", ylab = "Todennäköisyys (%)")
Binomitodennäköisyydet kun p=0,18 ja otoskoko 10

Binomitodennäköisyydet kun p=0,18 ja otoskoko 10

par(mar = c(4, 4, 2, 1))
plot(tmp.x, 100 * tmp.y, type = "h", lwd = 8, xlab = "Lukumäärä", ylab = "Todennäköisyys (%)")
# require(ztable)
# options(ztable.type=ifelse(knitr:::is_html_output(),'html','latex'))
# print(options()$ztable.type)
library(magrittr)
require(stargazer)
library(kableExtra)
load(file = "nhghV2.RData")
Ikä <- with(nhgh, cut(age, c(0, 30, 50, Inf)))

tbl <- with(nhgh, addmargins(table(Ikä, dx.f)))
class(tbl) <- "matrix"
# stargazer(tbl, summary=FALSE, rownames=TRUE,header = FALSE,title='NHANES
# tutkimus. Diabetes (ei/on)ja ikä vuosissa.',label='NHANESperus')
# tbl<-ztable(tbl)
kbl(tbl, caption = "NHANES tutkimus. Diabetes (ei/on)ja ikä vuosissa.", label = "NHANESperus",
    booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)
NHANES tutkimus. Diabetes (ei/on)ja ikä vuosissa.
ei on Sum
(0,30] 2053 41 2094
(30,50] 1778 178 1956
(50,Inf] 2050 695 2745
Sum 5881 914 6795
# print(update_ztable(tbl,digits=0),caption='Ikä ja
# diabetes.',type=options()$ztable.type) htmlTable(tbl,rnames=levels(nhgh$re),)
library(vcd)
mosaic(with(nhgh, table(sex, Ikä, dx.f)), shade = TRUE, legend = TRUE, cex = 45)
Mosaiikkikuvio frekvenssitaulusta.

Mosaiikkikuvio frekvenssitaulusta.

# mosaicplot(~ sex+Ikä+dx.f, data = nhgh, color = TRUE, las = 1)
load(file = "YhtData/daF3752.RData")
tbl <- with(daF3752, addmargins(table(bv1, MP4)))
class(tbl) <- "matrix"
# stargazer(tbl, summary=FALSE, rownames=TRUE,header =
# FALSE,title='Turvallisuuspolitiikka- ja maanpuolustustutkimus 2022. Vastaajan
# ammatti/asema ja vastaus 'Jos Suomeen hyökätään, olisitteko itse valmis
# osallistumaan maanpuolustuksen eri tehtäviin kykyjenne ja taitojenne
# mukaan?'',label='MP1Tab')

kbl(tbl, caption = "Turvallisuuspolitiikka- ja maanpuolustustutkimus 2022. Vastaajan ammatti/asema ja vastaus 'Jos Suomeen hyökätään, olisitteko itse valmis osallistumaan maanpuolustuksen
 eri tehtäviin kykyjenne ja taitojenne mukaan?'",
    label = "MP1Tab", booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)
Turvallisuuspolitiikka- ja maanpuolustustutkimus 2022. Vastaajan ammatti/asema ja vastaus ‘Jos Suomeen hyökätään, olisitteko itse valmis osallistumaan maanpuolustuksen eri tehtäviin kykyjenne ja taitojenne mukaan?’
Kyllä Ei Ei osaa sanoa Sum
Nainen 381 75 65 521
Mies 449 34 29 512
Sum 830 109 94 1033
tbl <- with(daF3752, addmargins(table(MP12_1, bv1)))
# class(tbl)<-'matrix' stargazer(tbl, summary=FALSE, rownames=TRUE,header =
# FALSE,title='Turvallisuuspolitiikka- ja maanpuolustustutkimus 2022. Vastaajan
# asuinpaikka ja vastaus 'Miten hyvin mielestänne Suomessa on varauduttu
# seuraavien turvallisuusuhkien torjuntaan?: Aseellinen hyökkäys
# '',label='MP121Tab')

kbl(tbl, caption = "Turvallisuuspolitiikka- ja maanpuolustustutkimus 2022. Vastaajan asuinpaikka ja vastaus 'Miten hyvin mielestänne Suomessa on varauduttu seuraavien turvallisuusuhkien torjuntaan?: Aseellinen hyökkäys '",
    label = "MP121Tab", booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)
Turvallisuuspolitiikka- ja maanpuolustustutkimus 2022. Vastaajan asuinpaikka ja vastaus ‘Miten hyvin mielestänne Suomessa on varauduttu seuraavien turvallisuusuhkien torjuntaan?: Aseellinen hyökkäys’
Nainen Mies Sum
Erittäin huonosti 4 1 5
Melko huonosti 33 28 61
Melko hyvin 304 279 583
Erittäin hyvin 124 185 309
Ei osaa sanoa 56 19 75
Sum 521 512 1033
# Transparent colors
addTrans <- function(color, trans) {
    # This function adds transparancy to a color.  Define transparancy with an
    # integer between 0 and 255 0 being fully transparant and 255 being fully
    # visable Works with either color and trans a vector of equal length, or
    # one of the two of length 1.

    if (length(color) != length(trans) & !any(c(length(color), length(trans)) ==
        1))
        stop("Vector lengths not correct")
    if (length(color) == 1 & length(trans) > 1)
        color <- rep(color, length(trans))
    if (length(trans) == 1 & length(color) > 1)
        trans <- rep(trans, length(color))

    num2hex <- function(x) {
        hex <- unlist(strsplit("0123456789ABCDEF", split = ""))
        return(paste(hex[(x - x%%16)/16 + 1], hex[x%%16 + 1], sep = ""))
    }
    rgb <- rbind(col2rgb(color), trans)
    res <- paste("#", apply(apply(rgb, 2, num2hex), 2, paste, collapse = ""), sep = "")
    return(res)
}

# Normal
tmp.x <- seq(from = -3, to = 3, length = 50)
par(mar = c(4, 4, 2, 1))

curve(dnorm, from = -3, to = 3, lwd = 2, col = "navy", ylab = "Tiheysfunktion arvo",
    ylim = c(0, 0.85))
curve(dnorm(x, mean = -1, sd = 0.5), from = -2.5, to = 2.5, lwd = 2, col = "navy",
    add = TRUE)

polygon(x = c(tmp.x), y = c(dnorm(tmp.x)), col = addTrans("blue", 20))
polygon(x = c(tmp.x), y = c(dnorm(tmp.x, mean = -1, sd = 0.5)), col = addTrans("navy",
    35))

abline(v = c(-1, 0), lty = 2)
Normaalijakaumia, vas. odotusarvo -1 ja hajonta 0,5; oik. 0 ja 1.

Normaalijakaumia, vas. odotusarvo -1 ja hajonta 0,5; oik. 0 ja 1.

# {r PoissonEsimTn, echo = TRUE,results='asis',message=FALSE,warning=FALSE}
tmp.x <- c(0:10)
tmp.lambda <- c(0.5, 1, 2, 5)
tmp.tab <- sapply(tmp.lambda, dpois, x = tmp.x)
dimnames(tmp.tab) <- list(paste(tmp.x), paste("$lambda=$", tmp.lambda))
# print(knitr::kable(tmp.tab,digits = 3, format.args =
# list(digits=2,nsmall=2,decimal.mark =','), caption='Poisson jaukaman
# toteumien todennäköisyys eri parameterin $\\lambda$ arvoilla
# \\label{tab:PoissonEsimTn}')) knitr::kable(tmp.tab,digits = 3, format.args =
# list(digits=2,nsmall=2,decimal.mark =','), caption='Poisson jaukaman
# toteumien todennäköisyys eri parameterin $\\lambda$ arvoilla
# \\label{tab:PoissonEsimTn}',format = 'latex')

kbl(tmp.tab, caption = "Poisson jaukaman toteumien todennäköisyys eri parameterin $lambda$ arvoilla.",
    label = "PoissonEsimTn", booktabs = T, format.args = list(digits = 3, nsmall = 2,
        decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)
Poisson jaukaman toteumien todennäköisyys eri parameterin \(lambda\) arvoilla.
\(lambda=\) 0.5 \(lambda=\) 1 \(lambda=\) 2 \(lambda=\) 5
0 6,07e-01 3,68e-01 1,35e-01 0,00674
1 3,03e-01 3,68e-01 2,71e-01 0,03369
2 7,58e-02 1,84e-01 2,71e-01 0,08422
3 1,26e-02 6,13e-02 1,80e-01 0,14037
4 1,58e-03 1,53e-02 9,02e-02 0,17547
5 1,58e-04 3,07e-03 3,61e-02 0,17547
6 1,32e-05 5,11e-04 1,20e-02 0,14622
7 9,00e-07 7,30e-05 3,44e-03 0,10444
8 1,00e-07 9,10e-06 8,59e-04 0,06528
9 0,00e+00 1,00e-06 1,91e-04 0,03627
10 0,00e+00 1,00e-07 3,82e-05 0,01813
require(ggplot2)

tmp.data <- data.frame(Tn = c(tmp.tab[, 1], tmp.tab[, 2], tmp.tab[, 3], tmp.tab[,
    4]), Freq = factor(rep(tmp.x, 4)), Lambda = factor(rep(tmp.lambda, rep(11, 4))))

ggplot(tmp.data, aes(Freq, Tn)) + geom_bar(aes(fill = Lambda), width = 0.6, position = position_dodge(width = 3.5),
    stat = "identity") + theme(legend.position = "right", legend.title = element_blank(),
    legend.box = "horizontal") + xlab("Lukumäärä") + ylab("Todennäköisyys")
Poisson-jakauman todennäköisyyksiä eri $\lambda$ arvoilla.

Poisson-jakauman todennäköisyyksiä eri \(\lambda\) arvoilla.

# tmp.x<-c(0:10);tmp.lambda<-c(0.5,1,2,5)
# tmp.tab<-sapply(tmp.lambda,dpois,x=tmp.x)
# dimnames(tmp.tab)<-list(paste(tmp.x),paste(tmp.lambda))
# tmp.1<-data.frame(x=tmp.x,y=c(tmp.tab[,1],tmp.tab[,2],tmp.tab[,3],tmp.tab[,4]),
# param=factor(rep(tmp.lambda,each=11)))
# ggplot(tmp.1,aes(x=x,y=y,col=param))+geom_line(size=1)+geom_point()+ #
# geom_segment(aes(x=x+.2,xend = x+.2, yend = 0), linewidth = 10, lineend =
# 'butt',size=2)+ labs(x='Lukumäärä',y='Todennäköisyys',col='Parametri')+
# theme(legend.position=c(.6,.8))+ scale_x_continuous(breaks=c(0,2,4,6,8,10))
library(ztable)

load(file = "nhghV2.RData")

tmp.fun <- function(x) {
    c(mean(x, na.rm = TRUE), sd(x, na.rm = TRUE), quantile(x, probs = c(25, 50, 75)/100,
        na.rm = TRUE), sum(is.na(x)), length(x))
}
tmp.fun1 <- function(x, catvar) {
    matrix(unlist(with(nhgh, tapply(waist, catvar, tmp.fun))), nrow = length(levels(catvar)),
        byrow = TRUE, dimnames = list(levels(catvar), c("KA", "KH", "25%", "50%",
            "75%", "N puut", "N")))
}

tmp.tbl <- with(nhgh, rbind(tmp.fun(waist), tmp.fun1(waist, sex), tmp.fun1(waist,
    re)))
rownames(tmp.tbl)[c(1)] <- " "
rownames(tmp.tbl)[c(8)] <- "Other"
colnames(tmp.tbl) <- colnames(tmp.fun1(nhgh$waist, nhgh$sex))

library(kableExtra)
kbl(tmp.tbl[, -c(3, 5)], caption = "NHANES aineisto, keskiarvo (KA), keskihajonta (KH), mediaani, puuttuvien lukumäärä ja lukumäärä (N).",
    format.args = list(decimal.mark = ","), booktabs = T, digits = 2) %>%
    kable_styling() %>%
    pack_rows("Kaikki", 1, 1) %>%
    pack_rows("Sukupuoli", 2, 3) %>%
    pack_rows("Etninen tausta", 4, 8)
NHANES aineisto, keskiarvo (KA), keskihajonta (KH), mediaani, puuttuvien lukumäärä ja lukumäärä (N).
KA KH 50% N puut N
Kaikki
96,25 17,06 95,3 239 6795
Sukupuoli
male 97,87 17,07 97,3 100 3372
female 94,65 16,89 93,0 139 3423
Etninen tausta
Mexican American 96,03 15,38 96,0 26 1366
Other Hispanic 94,55 16,03 93,4 23 706
Non-Hispanic White 97,45 17,15 96,4 108 3117
Non-Hispanic Black 96,75 19,08 95,4 64 1217
Other 88,96 15,15 88,0 18 389
# knitr::kable(tmp.tbl,caption='Jatkuvien muuttujien yhteeveto')
# tmp.tbl<-ztable(tmp.tbl)
# print(update_ztable(addrgroup(tmp.tbl,rgroup=c('Ikä','Vyötärön ympärys
# (cm)'),n.rgroup=c(8,8)),digits=c(rep(2,8),0,0)),caption='Jatkuvien muuttujien
# yhteeveto')
# print(update_ztable(addrgroup(tmp.tbl,rgroup=c('Kaikki','Sukupuoli','Etninen
# tausta'),n.rgroup=c(1,2,5)), digits=c(rep(1,8),0,0)),caption='Jatkuvien
# muuttujien yhteenveto. Vyötärön ympärys (cm)')
library(ggplot2)
library(gridExtra)

tmp.p1 <- ggplot(nhgh, aes(x = waist)) + geom_histogram(aes(y = ..density..), colour = "black",
    fill = "white") + geom_density(alpha = 0.2, fill = "#FFFFFF") + geom_vline(aes(xintercept = quantile(nhgh$waist,
    na.rm = TRUE, probs = c(0.5))), linetype = "dashed", size = 1) + labs(y = "Tiheysfunktio",
    y = "Vyötärön ympärys")

tmp.p2 <- ggplot(nhgh, aes(x = 0, y = waist)) + geom_violin() + labs(x = "", y = "Vyötärön ympärys")

grid.arrange(tmp.p1, tmp.p2, nrow = 1)
Vyötärön ympärys. Vasemmalla histogrammi ja tiheysfunktio, oikealla viulukuvio. Pystyviiva osoittaa mediaanin

Vyötärön ympärys. Vasemmalla histogrammi ja tiheysfunktio, oikealla viulukuvio. Pystyviiva osoittaa mediaanin

tmp.p3 <- ggplot(nhgh, aes(x = waist)) + geom_histogram(color = "black", fill = "white") +
    facet_grid(sex ~ .) + scale_color_grey() + scale_fill_grey() + labs(y = "Lukumäärä",
    x = "Vyötärön ympärys")
# tmp.p4+ scale_color_grey()+scale_fill_grey() #+theme_classic() tmp.p3

tmp.p2 <- ggplot(nhgh, aes(x = 0, y = waist)) + geom_violin() + xlab(" ") + facet_grid(sex ~
    .) + labs(x = "", y = "Vyötärön ympärys")
# tmp.p2
grid.arrange(tmp.p3, tmp.p2, nrow = 1)
Vyötärön ympärys. Vasemmalla histogrammi sukupuolet eriteltyinä. Y-akselilla lukumäärä. Oikealla viulukuvio, jossa esitetty tiheysfunktio.

Vyötärön ympärys. Vasemmalla histogrammi sukupuolet eriteltyinä. Y-akselilla lukumäärä. Oikealla viulukuvio, jossa esitetty tiheysfunktio.

tmp.p4 <- ggplot(nhgh, aes(x = re, y = waist)) + geom_boxplot(notch = TRUE, outlier.size = 0.5) +
    coord_flip() + geom_jitter(shape = 16, position = position_jitter(0.2), size = 0.1)
tmp.p4 + labs(x = "Etninen tausta", y = "Vyötärön ympärys")
Vyötärön ympärys. Viiksilaatikkokuvio etnisen taustan mukaan

Vyötärön ympärys. Viiksilaatikkokuvio etnisen taustan mukaan

# grid.arrange(tmp.p3, tmp.p4, nrow = 1)
tmp.p5 <- ggplot(nhgh, aes(x = age, y = waist, color = sex)) + geom_point(size = 0.5) +
    xlab("Ikä (v)") + ylab("Vyötärön ympärys (cm)")  #+facet_grid(sex ~.)
tmp.p5 + scale_colour_grey() + labs(color = "Sukupuoli") + theme(legend.position = "right",
    axis.title = element_text(size = 8), legend.text = element_text(size = 8), legend.title = element_text(size = 8))
Ikä ja vyötärön ympärys, sukupuolen mukaan eriteltynä.

Ikä ja vyötärön ympärys, sukupuolen mukaan eriteltynä.

library(GGally)
tmp.p6 <- ggpairs(data = head(nhgh, 20), columns = c(3, 8, 9, 10, 14), lower = list(continuous = wrap(ggally_points,
    size = 0.1, color = "navy")), columnLabels = c("Ikä", "Paino (kg)", "Pituus (cm)",
    "BMI", "Vyöt. (cm)"), size = 0.1, progress = FALSE)
tmp.p6 + theme(text = element_text(size = 4))
Hajontakuviomatriisi (1000 havaintoa).

Hajontakuviomatriisi (1000 havaintoa).

w_ggally_points <- wrap(ggally_points, size = 0.2, color = "navy")

pm <- ggpairs(data = head(nhgh, 1000), columns = c(3, 8, 9, 10, 14), columnLabels = c("Ikä",
    "Paino (kg)", "Pituus (cm)", "BMI", "Vyöt. (cm)"), progress = FALSE, lower = list(continuous = wrap(ggally_points,
    size = 0.1, color = "navy")))

pm + scale_colour_grey()
Hajontakuviomatriisi (1000 havaintoa).

Hajontakuviomatriisi (1000 havaintoa).

# Luetaan keskiväkiluku ja kuolleiden määrä
load(file = "./YhtData/tmpSuomiesim.RData")
require(ggplot2)
tmp.Suomi.esim$kuolleisuus <- with(tmp.Suomi.esim, 1e+05 * Kuolleet/Keskivakiluku)
tmp.Suomi.esim$syop.kuol <- with(tmp.Suomi.esim, 1e+05 * Syovat/Keskivakiluku)
tmp.Suomi.esim$verenk.kuol <- with(tmp.Suomi.esim, 1e+05 * Verenkierto/Keskivakiluku)
tmp.Suomi.esim$tapat.kuol <- with(tmp.Suomi.esim, 1e+05 * Tapaturmat/Keskivakiluku)

suomi.k1 <- ggplot(tmp.Suomi.esim, aes(Vuosi, syop.kuol)) + labs(x = "Vuosi", y = "Kuolemanvaara\n(1/100 000)")
suomi.k2 <- suomi.k1 + geom_line() + geom_line(aes(y = verenk.kuol), lty = 2) + ylim(c(150,
    600))
suomi.k2
Kuolemanvaara Suomessa 1980-2015, syöpä (viiva) ja  verenkiertoelimiston sairaudet (katkoviiva)

Kuolemanvaara Suomessa 1980-2015, syöpä (viiva) ja verenkiertoelimiston sairaudet (katkoviiva)

tmp.Suomi.esim.out <- as.matrix(tmp.Suomi.esim)
# dimnames(tmp.Suomi.esim.out)<-list(NULL,c('Vuosi','Keskiväkiluku','Kaikki
# kuolleet','Syöpä', 'Verenkierto','Tapaturmat',
# 'Kuolemanvaara','Syöpäkuol.\\n(1/100 000)', 'Verenk. kuol.\\n(1/100
# 000)','Tapaturmakuol.'))

# print(knitr::kable(tmp.Suomi.esim.out[-(1:15),c(1:5,8,9)],digits =
# c(rep(0,5),1,1),row.names =FALSE, format.args = list(decimal.mark
# =','),#format.args = list(digits=0,nsmall=8,decimal.mark =','),
# caption='Kuolemien lukumäärä ja kuolemanvaara (1/100 000) Suomessa
# 1995-2015,\\label{tab:EsimSuomiKuol}'))
# fcr_2018-09-19.csv library(pxR)
# apu.tyo<-as.data.frame(read.px(filename='https://pxdata.stat.fi:443/PxWeb/sq/2204d4d1-524d-4afc-bfd9-166399641d2d'))
# apu.tyo$Vuosi<-as.numeric(substr(apu.tyo$Vuosineljännes,1,4))+
# as.numeric(substr(apu.tyo$Vuosineljännes,6,6))/4-(1/8) tyollisuus.dt<-apu.tyo
# save(tyollisuus.dt,file='./YhtData/tyollisuusdt.RData')
load(file = "./YhtData/tyollisuusdt.RData")
ggplot(tyollisuus.dt, aes(x = Vuosi, y = value, group = Sukupuoli)) + geom_line(aes(color = Sukupuoli,
    linetype = Sukupuoli), lwd = 0.7) + labs(x = "Vuosi", y = "Työllisyys % (20-64 vuotiaat)") +
    theme(legend.position = c(0.1, 0.85), axis.title = element_text(size = 8), legend.text = element_text(size = 8),
        legend.title = element_text(size = 8))
Suomen työllisyys 2020-2022

Suomen työllisyys 2020-2022

# scale_color_manual(values=c('navy','tomato2'))
require(Epi)
load(file = "./YhtData/esimcoh.RData")

# tmp.tbl<-ztable(esim.coh.Lx[,c(9,11,3,12,13)])
# print(update_ztable(tmp.tbl,digits=0,caption='10 henkilön seuranta. Age=Ikä
# alussa; Age.end=Ikä lopussa; period=alku vuosi; Diagn.age=ikä diagnoosiin
# aikaan; Person.yrs=kertyneet seurantavuodet.'))
# knitr::include_graphics(c('KuvatLuku3/LexisKuva.png'),dpi=300)

# png(filename =
# './YhtKuvat/LexisIka.png',width=1600,height=1600/sqrt(2),pointsize=36)
plot(esim.coh.Lx, time.scale = "age", xlab = "Ikä", ylab = "Henkilö", type = "l",
    lwd = 4, cex.axis = 1, cex.lab = 1, ylim = c(1, 11))
points(esim.coh.Lx, pch = c(NA, 16)[esim.coh.Lx$lex.Xst + 1], col = "tomato4", cex = 1)
PY.ann(esim.coh.Lx, cex = 1, digits = 0)
box()
Seurantatutkimuksen Lexis-kuvio iän suhteen. Seuranta-ajan pituus on merkitty, samoin päätetapahtumat (täplä).

Seurantatutkimuksen Lexis-kuvio iän suhteen. Seuranta-ajan pituus on merkitty, samoin päätetapahtumat (täplä).

# apu<-dev.off() knitr::include_graphics(c('./YhtKuvat/LexisIka.png'),dpi=300)
require(Epi)
require(survival)

# tmp.tbl<-ztable(head(lung,10)) print(update_ztable(tmp.tbl,digits=0,
# caption='Viisi riviä keuhkosyöpä aineistosta. Muuttujien selitykset:
# inst:\tInstitution code; time:\tSurvival time in days; status:\tcensoring
# status 1=censored, 2=dead; age:\tAge in years; sex:\tMale=1 Female=2
# ph.ecog:\tECOG performance score (0=good 5=dead); ph.karno:\tKarnofsky
# performance score (bad=0-good=100) rated by physician; pat.karno:\tKarnofsky
# performance score as rated by patient; meal.cal:\tCalories consumed at meals;
# wt.loss:\tWeight loss in last six months; '))
tmp.s2 <- survfit(Surv(time, status) ~ 1, data = lung)
library(ggsurvfit)

gg.lung <- ggsurvfit(tmp.s2) + add_confidence_interval() + labs(title = "", x = "Seuranta-aika (d)",
    y = "Elossa olon\ntodennäköisyys") + theme(legend.position = c(0.1, 0.85),
    axis.title = element_text(size = 8))

gg.lung
Kaplan-Meier käyrä.

Kaplan-Meier käyrä.

library(haven)
load(file = "YhtData/divorce.RData")

tmp.s2.div <- survfit(Surv(years, div) ~ mixed, data = divorce)

gg.div <- ggsurvfit(tmp.s2.div, linetype_aes = TRUE, type = "risk") + add_confidence_interval() +
    labs(title = "", x = "Seuranta-aika (a)", y = "Avioeron\ntodennäköisyys")
gg.div <- gg.div + theme(legend.position = c(0.2, 0.85), axis.title = element_text(size = 8),
    legend.title = element_text(size = 6), legend.text = element_text(size = 6)) +
    coord_cartesian(xlim = c(0, 40))
gg.div
Avioeron todennäköisyys yhdysvaltalaisen aineiston mukaan. Avioliitot jaoteltu puolisoiden etnisen taustan mukaan (mixed) (sama=No, eri=Yes).

Avioeron todennäköisyys yhdysvaltalaisen aineiston mukaan. Avioliitot jaoteltu puolisoiden etnisen taustan mukaan (mixed) (sama=No, eri=Yes).

require(cohorttools)
# lung$y<-rep(0,nrow(lung))
lung$Sukupuoli <- factor(lung$sex, levels = 1:2, labels = c("Miehet", "Naiset"))
tmp.data.1 <- estim.hazard(Surv(time/365.25, status) ~ Sukupuoli, data = lung, use.GAM = TRUE)
ggplot(tmp.data.1, aes(x = time.eval, y = haz, linetype = Sukupuoli)) + geom_line(colour = "blue") +
    xlab("Seuranta-aika (a)") + ylab("Hasardi") + geom_ribbon(aes(ymin = haz.lo,
    ymax = haz.hi), alpha = 0.2) + coord_cartesian(ylim = c(0, 2), xlim = c(0, 1.5)) +
    theme(legend.position = c(0.2, 0.85), axis.title = element_text(size = 8), legend.title = element_text(size = 8),
        legend.text = element_text(size = 8))
Hasardi käyrä ja sen 95\% luottamusväli.

Hasardi käyrä ja sen 95% luottamusväli.

tmp.haz.div <- estim.hazard(Surv(years, div) ~ mixed, data = divorce, use.GAM = TRUE)
p.div <- ggplot(tmp.haz.div, aes(x = time.eval, y = haz, linetype = mixed))
p.div <- p.div + geom_line(aes(colour = mixed, linetype = mixed), lwd = 0.7) + labs(x = "Avioliiton kesto (a)",
    y = "Avioeron tapahtumatiheys\n(hasardi)") + coord_cartesian(ylim = c(0, 0.03),
    xlim = c(0, 40)) + theme(legend.position = c(0.8, 0.8), axis.title = element_text(size = 8),
    legend.title = element_text(size = 8), legend.text = element_text(size = 8)) +
    geom_vline(xintercept = 7, linetype = 2)
p.div
Avioeron tapahtumatiheys yhdysvaltalaisen aineiston mukaan. Avioliitot jaoteltu puolisoiden etnisen taustan mukaan (mixed) (sama=No, eri=Yes).

Avioeron tapahtumatiheys yhdysvaltalaisen aineiston mukaan. Avioliitot jaoteltu puolisoiden etnisen taustan mukaan (mixed) (sama=No, eri=Yes).

tmp.x <- c(0, 2, 5, 9)
tmp.y <- c(0.2, 0.4, 0.1, 0.3)
plot(tmp.x, tmp.y, type = "s", xlab = "Aika (t)", ylab = "Hasardi")
points(tmp.x, tmp.y, pch = 1)
box()
Esimerkki porrasfunktiosta

Esimerkki porrasfunktiosta

Erojen mittaaminen

# require(knitr);require(ztable);require(Epi);require(survival)
# options(ztable.type='latex')

# print(kable(tmp.ulos,caption=' Epidemiologiassa käytetty 2\\*2 -taulu.
# \\label{tab:kissataulu}',align='rrr')) Girardis M, Busani S, Damiani E, et
# al. Effect of Conservative vs Conventional Oxygen Therapy on Mortality Among
# Patients in an Intensive Care Unit: The Oxygen-ICU Randomized Clinical Trial.
# JAMA Published Online First: 5 October 2016. doi:10.1001/jama.2016.11993
tmp.icu <- matrix(c(a = 25, b = 216 - 25, c = 44, d = 218 - 44), dimnames = list(c("Konservatiivinen",
    "Tavanomainen"), c("Kuollut", "Elossa")), ncol = 2, byrow = TRUE)

tmp.icu1 <- addmargins(tmp.icu)
tmp.icu1 <- matrix(paste(tmp.icu1, matrix(paste0("(", c("a", "b", "a+b", "c", "d",
    "c+d", "a+c", "b+d", "a+b+c+d"), ")"), byrow = TRUE, nrow = 3)), ncol = 3)
dimnames(tmp.icu1) <- list(c("Konservatiivinen", "Tavanomainen", "Kaikki"), c("Kuollut",
    "Elossa", "Kaikki"))
# tmp.icu1<-ztable(tmp.icu1) tmp.icu1<-addrgroup(tmp.icu1,n.rgroup =
# c(2,1),rgroup = c('',''))
# print(update_ztable(tmp.icu1,digits=0,align=c('l',rep('r',3)), caption='Tulos
# tehostetun hoidon kliinisestä kokeesta. Kirjaimet suluissa ovat 2*2-taulun
# solujen nimet.\\label{tab:icutaulu}')) knitr::kable(tmp.icu1,caption='Tulos
# tehostetun hoidon kliinisestä kokeesta. Kirjaimet suluissa ovat 2*2-taulun
# solujen nimet.\\label{tab:icutaulu}')

kbl(tmp.icu1, booktabs = T, digits = 2, caption = "Tulos tehostetun hoidon kliinisestä kokeesta. Kirjaimet suluissa ovat 2*2-taulun solujen nimet.",
    label = "icutaulu") %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)
Tulos tehostetun hoidon kliinisestä kokeesta. Kirjaimet suluissa ovat 2*2-taulun solujen nimet.
Kuollut Elossa Kaikki
Konservatiivinen 25 (a) 191 (b) 216 (a+b)
Tavanomainen 44 (c) 174 (d) 218 (c+d)
Kaikki 69 (a+c) 365 (b+d) 434 (a+b+c+d)
# Taulukko 2 Kahden hapen annostelutavan vertailu tehohoidossa, kliininen koe
# jossa päätemuuttujana oli #kuolleisuus(2).
tmp.icu <- matrix(c(a = 25, b = 216 - 25, c = 44, d = 218 - 44), dimnames = list(c("Konservatiivinen",
    "Tavanomainen"), c("Kuollut", "Elossa")), ncol = 2, byrow = TRUE)

Epi::twoby2(tmp.icu)
## 2 by 2 table analysis: 
## ------------------------------------------------------ 
## Outcome   : Kuollut 
## Comparing : Konservatiivinen vs. Tavanomainen 
## 
##                  Kuollut Elossa    P(Kuollut) 95% conf. interval
## Konservatiivinen      25    191        0.1157    0.0794   0.1657
## Tavanomainen          44    174        0.2018    0.1537   0.2604
## 
##                                     95% conf. interval
##              Relative Risk:  0.5734    0.3644   0.9024
##          Sample Odds Ratio:  0.5176    0.3040   0.8813
## Conditional MLE Odds Ratio:  0.5184    0.2909   0.9071
##     Probability difference: -0.0861   -0.1546  -0.0172
## 
##              Exact P-value: 0.0178 
##         Asymptotic P-value: 0.0153 
## ------------------------------------------------------
tmp.tbl <- matrix(paste0("(", c("a", "b", "a+b", "c", "d", "c+d", "a+c", "b+d", "a+b+c+d"),
    ")"), nrow = 3, byrow = TRUE)
dimnames(tmp.tbl) <- list(c("sairas", "terve", "kaikki"), c("altistunut", "altistumaton",
    "kaikki"))

# knitr::kable(tmp.tbl,caption='Epidemiologian 2*2-taulun solujen
# nimeäminen',label='kaksikertaakaksi')

kbl(tmp.tbl, booktabs = T, digits = 2, format.args = list(decimal.mark = ","), caption = "Epidemiologian 2*2-taulun solujen nimeäminen",
    label = "kaksikertaakaksi") %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)  #%>%row_spec(bold =TRUE,row=1)
Epidemiologian 2*2-taulun solujen nimeäminen
altistunut altistumaton kaikki
sairas
(a+b)
terve
(c+d)
kaikki (a+c) (b+d) (a+b+c+d)
# if(!knitr::is_html_output()){
# print(ztable(tmp.tbl,align='r',caption='Epidemiologian 2*2-taulun solujen
# nimeäminen', type=ifelse(knitr::is_html_output(),'html','latex')))}
# Kissa esimerkki
require(epiR)
require(Epi)
tmp.kissa <- matrix(c(13, 5, 2163, 3349), nrow = 2)
# class(tmp.kissa)<-'table' dat <- t(matrix(c(13,5,2163,3349), nrow = 2, byrow
# = TRUE)) rownames(dat) <- c('FUS+', 'FUS-'); colnames(dat) <- c('DF+',
# 'DF-');

tmp.kissa.txt <- matrix(c("a", "c", "b", "d"), ncol = 2)
tmp.ulos <- matrix(paste0(tmp.kissa.txt, " (", tmp.kissa, ")"), ncol = 2, byrow = FALSE)
tmp.ulos <- cbind(tmp.ulos, c("a+b (18)", "c+d (5512)"))
tmp.ulos <- rbind(tmp.ulos, c("a+c (2176)", "b+d (3354)", "a+b+c+d (5530)"))
dimnames(tmp.ulos) <- list(c("kuivaruoka", "muu ruoka", "Kaikki"), c("FUS", "terve",
    "Kaikki"))
# tmp.ulos; dat
# require(knitr);require(ztable) print(kable(tmp.ulos,caption='
# Epidemiologiassa käytetty 2\\*2 -taulu.
# \\label{tab:kissataulu}',align='rrr'))
require(Epi)
require(survival)
# options(ztable.type='latex') tmp.tbl<-ztable(tmp.ulos)
# tmp.tbl.1<-update_ztable(tmp.tbl,digits=0,align=c('l','r','r','r'),caption='Kissanruokatutkimuksen
# 2 $\\times$ 2 -taulu.')

# print(xtable(tmp.ulos,caption='Kissanruokatutkimuksen 2 $\\times$ 2 -taulu',
# label='kissataulu'),comment=FALSE)

kbl(tmp.ulos, caption = "Kissanruokatutkimuksen 2 $\\times$ 2 -taulu", label = "kissataulu",
    booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)  #%>%row_spec(bold =TRUE,row=1)
Kissanruokatutkimuksen 2 \(\times\) 2 -taulu
FUS terve Kaikki
kuivaruoka a (13) b (2163) a+b (18)
muu ruoka c (5) d (3349) c+d (5512)
Kaikki a+c (2176) b+d (3354) a+b+c+d (5530)
library(epiR)
epi.2by2(dat = tmp.kissa, method = "cross.sectional", conf.level = 0.95, units = 100,
    outcome = "as.columns")
##              Outcome +    Outcome -      Total               Prev risk *
## Exposed +           13         2163       2176       0.60 (0.32 to 1.02)
## Exposed -            5         3349       3354       0.15 (0.05 to 0.35)
## Total               18         5512       5530       0.33 (0.19 to 0.51)
## 
## Point estimates and 95% CIs:
## -------------------------------------------------------------------
## Prev risk ratio                                4.01 (1.43, 11.23)
## Prev odds ratio                                4.03 (1.43, 11.31)
## Attrib prev in the exposed *                   0.45 (0.10, 0.80)
## Attrib fraction in the exposed (%)            75.05 (30.11, 91.09)
## Attrib prev in the population *                0.18 (-0.02, 0.38)
## Attrib fraction in the population (%)         54.20 (3.61, 78.24)
## -------------------------------------------------------------------
## Uncorrected chi2 test that OR = 1: chi2(1) = 8.177 Pr>chi2 = 0.004
## Fisher exact test that OR = 1: Pr>chi2 = 0.006
##  Wald confidence limits
##  CI: confidence interval
##  * Outcomes per 100 population units
# twoby2(t(dat))
load(file = "YhtData/daF0154.RData")
tmp.urheilu <- table(daF0154$Q84, Relevel(daF0154$Q5B_K, ref = 2))

# print(xtable(tmp.urheilu,caption='Vapaaehtoistyö urheilussa ja sukupuoli',
# label='urheilutaulu'),comment=FALSE)

kbl(tmp.urheilu, caption = "Vapaaehtoistyö urheilussa ja sukupuoli", label = "urheilutaulu",
    booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)
Vapaaehtoistyö urheilussa ja sukupuoli
Mainitsi Ei maininnut
A. Mies 78 425
B. Nainen 52 481
twoby2(tmp.urheilu)
## 2 by 2 table analysis: 
## ------------------------------------------------------ 
## Outcome   : Mainitsi 
## Comparing : A. Mies vs. B. Nainen 
## 
##           Mainitsi Ei maininnut    P(Mainitsi) 95% conf. interval
## A. Mies         78          425         0.1551    0.1260   0.1894
## B. Nainen       52          481         0.0976    0.0751   0.1258
## 
##                                    95% conf. interval
##              Relative Risk: 1.5895    1.1438   2.2088
##          Sample Odds Ratio: 1.6976    1.1675   2.4685
## Conditional MLE Odds Ratio: 1.6968    1.1496   2.5204
##     Probability difference: 0.0575    0.0171   0.0984
## 
##              Exact P-value: 0.0064 
##         Asymptotic P-value: 0.0056 
## ------------------------------------------------------
require(survival)
tmp.lung.py <- pyears(Surv(time, status) ~ factor(sex, label = c("mies", "nainen")),
    data = lung, data.frame = TRUE)
dat.lung <- as.matrix(tmp.lung.py$data[, c(4, 2)])
rownames(dat.lung) <- c("male", "female")
epi.2by2(dat = dat.lung, method = "cohort.time", unit = 1)
##              Outcome +     Time at risk                 Inc rate *
## Exposed +          112 107.011635865845        1.05 (0.86 to 1.26)
## Exposed -           53  83.523613963039        0.63 (0.48 to 0.83)
## Total              165 190.535249828884        0.87 (0.74 to 1.01)
## 
## Point estimates and 95% CIs:
## -------------------------------------------------------------------
## Inc rate ratio                                 1.65 (1.18, 2.33)
## Attrib rate in the exposed *                   0.41 (0.15, 0.67)
## Attrib fraction in the exposed (%)            39.37 (15.21, 57.12)
## Attrib rate in the population *                0.23 (0.02, 0.45)
## Attrib fraction in the population (%)         26.72 (17.71, 35.67)
## -------------------------------------------------------------------
##  Wald confidence limits
##  CI: confidence interval
##  * Outcomes per unit of population time at risk
require(survival)
load(file = "YhtData/divorce.RData")
tmp.div.py <- pyears(Surv(years, div) ~ mixed, data = divorce, data.frame = TRUE,
    scale = 100)
dat.div <- as.matrix(tmp.div.py$data[, c(4, 2)])
rownames(dat.div) <- c("No", "Yes")
epi.2by2(dat = dat.div, method = "cohort.time", unit = 1)
##              Outcome +     Time at risk                 Inc rate *
## Exposed +          797 508.947165472433        1.57 (1.46 to 1.68)
## Exposed -          235 111.656944858432        2.10 (1.84 to 2.39)
## Total             1032 620.604110330865        1.66 (1.56 to 1.77)
## 
## Point estimates and 95% CIs:
## -------------------------------------------------------------------
## Inc rate ratio                                 0.74 (0.64, 0.86)
## Attrib rate in the exposed *                   -0.54 (-0.83, -0.25)
## Attrib fraction in the exposed (%)            -34.40 (-55.64, -15.70)
## Attrib rate in the population *                -0.44 (-0.73, -0.15)
## Attrib fraction in the population (%)         -26.57 (-35.31, -17.99)
## -------------------------------------------------------------------
##  Wald confidence limits
##  CI: confidence interval
##  * Outcomes per unit of population time at risk
# https://seer.cancer.gov/stdpopulations/stdpopdic.html
# esim.std.popul<-read.fwf(file='https://seer.cancer.gov/stdpopulations/stdpop.18ages.txt',widths=c(3,3,8),
# as.is=TRUE, colClasses=c('character','character',NA),
# col.names=c('Luokitus','Ikalk','Popul'))
# esim.std.popul$Ikalk<-factor(esim.std.popul$Ikalk,
# label=paste0(5*(0:17),'-',paste0(c(5*(1:17)-1,'yli 85')))) # 003 = European
# (Scandinavian 1960) Std Million (18 age groups) # 001 = World (Segi 1960) Std
# Million (18 age groups) # 009 = World (WHO 2000-2025) Std Million (18 age
# groups) apu<-with(esim.std.popul,cbind(esim.std.popul[Luokitus=='003',-1],
# esim.std.popul[Luokitus=='001',3]))
# colnames(apu)<-c('Ikäluokka','Maailma','Eurooppa')
library(popEpi)
stdpop18.V1 <- stdpop18
colnames(stdpop18.V1) <- c("Ikäluokka", "Maailma", "Eurooppa", "Pohjoismaat")
# print(knitr::kable(stdpop18.V1,row.names=FALSE,caption='Standardiväestöjen
# painokertoimia. \\label{tab:populpainoja}'))

kbl(stdpop18.V1, caption = "Standardiväestöjen painokertoimia.", label = "populpainoja",
    booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)  #%>%row_spec(bold =TRUE,row=1)
Standardiväestöjen painokertoimia.
Ikäluokka Maailma Eurooppa Pohjoismaat
0-4 12000 8000 5900
5-9 10000 7000 6600
10-14 9000 7000 6200
15-19 9000 7000 5800
20-24 8000 7000 6100
25-29 8000 7000 6800
30-34 6000 7000 7300
35-39 6000 7000 7300
40-44 6000 7000 7000
45-49 6000 7000 6900
50-54 5000 7000 7400
55-59 4000 6000 6100
60-64 4000 5000 4800
65-69 3000 4000 4100
70-74 2000 3000 3900
75-79 1000 2000 3500
80-84 500 1000 2400
85 500 1000 1900
require(Epi)
# data('DMlate') dml <- Lexis( entry=list(Per=dodm, Age=dodm-dobth, DMdur=0 ),
# exit=list(Per=dox), entry.status=0, exit.status=ifelse(is.na(dodth),1,2),
# data=DMlate ) save(dml,file='YhtData/dml.RData')
load(file = "YhtData/dml.RData")
dml.1 <- splitLexis(dml, breaks = c(5 * (0:17)), time.scale = "Age")
apu <- table(cut(dml$Age, c(5 * (0:17), Inf)))

apu <- data.frame(Ikaluokka = as.vector(stdpop18.V1[, 1]), Kuol.lkm = tapply(status(dml.1,
    "exit") == 2, timeBand(dml.1, "Age", "left"), sum), Henk.vuodet.1000 = tapply(dur(dml.1),
    timeBand(dml.1, "Age", "left"), sum)/1000)

apu$Kuol.1000 <- apu$Kuol.lkm/apu$Henk.vuodet.1000
# print(knitr::kable(apu,row.names=FALSE,digits = 2, longtable = TRUE,
# format.args = list(digits=2,nsmall=2,decimal.mark =','),
# caption='Esimerkkiaineiston ikäluokittaiset kuolleisuudet 1000 henkilövuotta
# kohti. \\label{tab:populDenEsim}'))

kbl(apu, caption = "Esimerkkiaineiston  ikäluokittaiset kuolleisuudet 1000 henkilövuotta kohti.",
    label = "populDenEsim", booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)
Esimerkkiaineiston ikäluokittaiset kuolleisuudet 1000 henkilövuotta kohti.
Ikäluokka Kuol.lkm Henk.vuodet.1000 Kuol.1000
0 0-4 0 0,04 0,00
5 5-9 0 0,15 0,00
10 10-14 1 0,28 3,59
15 15-19 1 0,34 2,98
20 20-24 0 0,45 0,00
25 25-29 0 0,64 0,00
30 30-34 1 1,12 0,89
35 35-39 8 1,68 4,75
40 40-44 16 2,41 6,63
45 45-49 31 3,36 9,22
50 50-54 51 4,53 11,25
55 55-59 130 6,23 20,86
60 60-64 183 7,02 26,08
65 65-69 249 7,04 35,39
70 70-74 346 6,66 51,92
75 75-79 471 5,56 84,69
80 80-84 436 3,93 110,87
85 85 575 2,81 204,26

Diagnostiset testit

tmp.P0 <- function(P0, LR) {
    (P0 * LR)/(1 - P0 + P0 * LR)
}
# tmp.P0(P0=seq(from=0.001,to=0.005,length=10),LR=10)
library(ggplot2)

tmp.data <- data.frame(prev = rep(seq(0.001, 0.5, length = 200), 3))
tmp.data$P <- c(tmp.P0(P0 = tmp.data$prev[1:200], LR = 10), tmp.P0(P0 = tmp.data$prev[1:200],
    LR = 6), tmp.P0(P0 = tmp.data$prev[1:200], LR = 2))
tmp.data$LR <- factor(rep(c(10, 6, 2), rep(200, 3)))

# summary(tmp.data)
p1 <- ggplot(tmp.data, aes(x = prev, y = P, color = LR, linetype = LR))
p1 + geom_line(size = 0.76) + labs(x = "Todennäköisyys ennen testiä (P0)", y = "Todennäköisyys testin jälkeen (P')") +
    theme(legend.position = c(0.8, 0.3), axis.title = element_text(size = 8), legend.title = element_text(size = 8),
        legend.text = element_text(size = 8))
Kolmen eri testin (LR 2, 6, ja 10) positiivisen testin jälkeiset todennäköisyydet ennen testiä todennäköisyyksien funktiona.

Kolmen eri testin (LR 2, 6, ja 10) positiivisen testin jälkeiset todennäköisyydet ennen testiä todennäköisyyksien funktiona.

tmp.tbl <- matrix(c("10", "0,1", "6", "0,2", "2", "0,5", "1", "1"), ncol = 2, byrow = TRUE)
dimnames(tmp.tbl) <- list(c("Erinomainen", "Hyvä", "Kohtalainen", "Ei käyttöarvoa"),
    c("LR+", "LR-"))

# knitr::kable(tmp.tbl,caption='Diagnostisen testin käyttökelpoisuus ja
# uskottavuusosamäärä (LR)')

kbl(tmp.tbl, caption = "Diagnostisen testin käyttökelpoisuus ja uskottavuusosamäärä (LR).",
    label = "diagtestithyvyys", booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling()  # %>%column_spec(bold =TRUE,column=1)%>%row_spec(bold =TRUE,row=1)
Diagnostisen testin käyttökelpoisuus ja uskottavuusosamäärä (LR).
LR+ LR-
Erinomainen 10 0,1
Hyvä 6 0,2
Kohtalainen 2 0,5
Ei käyttöarvoa 1 1
# if(!knitr::is_html_output()){
# print(ztable(tmp.tbl,align='r',caption='Epidemiologian 2*2-taulun solujen
# nimeäminen', type=ifelse(knitr::is_html_output(),'html','latex')))
tmp.tbl <- matrix(c("+ (alle 65 mmol/l)", "a 731", "b 270", "- yli 65 mmol/l)", "c 78",
    "d 1500"), ncol = 3, byrow = TRUE)
dimnames(tmp.tbl) <- list(c("Anemia", "Ei anemiaa"), c("S-ferritiini", "Anemia",
    "Ei anemiaa"))

kbl(tmp.tbl, caption = "S-ferritiini ja anemia", booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling()  # %>%column_spec(bold =TRUE,column=1)%>%row_spec(bold =TRUE,row=1)
S-ferritiini ja anemia
S-ferritiini Anemia Ei anemiaa
Anemia
  • (alle 65 mmol/l)
a 731 b 270
Ei anemiaa
  • yli 65 mmol/l)
c 78 d 1500

Tutkimusasetelmat

require(ztable)
require(DiagrammeR)

options(ztable.type = "html")
tab.designs <- structure(list(Interventio = c("Ei", "Ei", "Ei", "Ei", "On", "On",
    "On"), Tutkimusasetelma = c("Ekologinen, korrelaatio tutkimus", "Poikkileikkaus, prevalenssi",
    "Sairauslähtöinen", "Seuranta, kohortti", "Yhteisöinterventio", "Kenttäkoe",
    "Satunnaistettu kliininen koe, RCT"), Havaintoyksikkö = c("Populaatio", "Yksilöt",
    "Yksilöt", "Yksilöt", "Yhteisö", "Terveet henkilöt", "Yksilöt")), class = "data.frame",
    row.names = c(NA, -7L))

# knitr::kable(tab.designs[,c(2,3,1)],caption='Muutamien tutkimusasetemien
# luokittelu.')

tab.designs.1 <- as.matrix(tab.designs)
dimnames(tab.designs.1) <- list(NULL, names(tab.designs))

kbl(tab.designs.1[, c(2, 3, 1)], caption = "Muutamien tutkimusasetelmien luokittelu.",
    booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling()  #%>%column_spec(bold =TRUE,column=1)%>%row_spec(bold =TRUE,row=1)
Muutamien tutkimusasetelmien luokittelu.
Tutkimusasetelma Havaintoyksikkö Interventio
Ekologinen, korrelaatio tutkimus Populaatio Ei
Poikkileikkaus, prevalenssi Yksilöt Ei
Sairauslähtöinen Yksilöt Ei
Seuranta, kohortti Yksilöt Ei
Yhteisöinterventio Yhteisö On
Kenttäkoe Terveet henkilöt On
Satunnaistettu kliininen koe, RCT Yksilöt On
library(Epi)
library(survival)
# with(lep,twoby2(exposure=sex,outcome=d)) Example of cohort data
esim.coh.data <- structure(list(No. = 1:10, Age = c(34L, 39L, 31L, 36L, 38L, 38L,
    41L, 32L, 39L, 42L), FU.time = c(15L, 20L, 12L, 17L, 9L, 16L, 11L, 20L, 18L,
    17L), Age.end = c(49L, 59L, 43L, 53L, 47L, 54L, 52L, 52L, 57L, 59L), Diagn.age = c(NA,
    54L, NA, 41L, NA, 51L, NA, NA, NA, NA), Person.yrs = c(15L, 15L, 12L, 5L, 9L,
    13L, 11L, 20L, 18L, 1L)), .Names = c("No.", "Age", "FU.time", "Age.end", "Diagn.age",
    "Person.yrs"), class = "data.frame", row.names = c(NA, -10L))

# esim.coh.data<-read.table(file='clipboard',header=TRUE,sep='\t')
# names(esim.coh.data)<-c('No.','Age','FU.time','Age.end','Diagn.age','Person.yrs')
# dump(objects(1,patt='esim.coh.data'),file='')

esim.coh.Lx <- Lexis(entry = list(age = Age, fu.time = rep(0, length(Age)), period = c(1980,
    1981, 1985, 1988, 1981, 1988, 1988, 1985, 1987, 1982)), exit = list(age = ifelse(is.na(Diagn.age),
    Age.end, Diagn.age)), exit.status = ifelse(is.na(Diagn.age), 0, 1), entry.status = 0,
    id = No., data = esim.coh.data)
# Create Lexis object for cohort data Include two time scales: age and
# follow-up time (fu.time)


plot(esim.coh.Lx, time.scale = "age", xlab = "Ikä", ylab = "Henkilö", lwd = 2,
    cex.axis = 1, cex.lab = 1.4, ylim = c(1, 11), axes = FALSE)
axis(1, at = c(30, 35, 40, 45, 50, 55, 60))
axis(2, at = 1:10)
points(esim.coh.Lx, pch = c(NA, 16)[esim.coh.Lx$lex.Xst + 1], col = "tomato4", cex = 1.4)
PY.ann(esim.coh.Lx, cex = 1.2, digits = 0)
points(x = rep(51, 5), y = c(2, 7:10), pch = 1, cex = 1.4, lwd = 2)
abline(v = 51, lty = 2, lwd = 2)
abline(v = 45, lty = 5, lwd = 1)
box()
with(esim.coh.Lx, segments(x0 = Age + lex.dur, y0 = No., x1 = rep(60, 10), y1 = No.,
    lwd = 2, lty = 2, col = "grey"))
Kymmenen henkilön seuranta.

Kymmenen henkilön seuranta.

demo.OECD <- read.table(file = "HEALTH_LVNG_03022017110046265.csv", header = TRUE,
    sep = ",")
# library(lattice) #levels(demo.OECD$Measure) xyplot(Value~Year|
# Country,data=demo.OECD[demo.OECD$Measure=='% of population aged 15+ who are
# daily smokers',])
# xyplot(Value~Year,group=Country,data=demo.OECD[demo.OECD$Measure=='Cigarettes
# per smoker per day',], auto.key=list(columns = 2), type='b')
# apu<-simpleKey(text = levels(demo.OECD$Country),columns=4);
# apu$points$pch<-1:4 png(file='C:/Users/jkhaukka/OneDrive - University of
# Helsinki/Opetus/EpiKirja/EpiKirja/OECDTupakointi.png',width=9*72*7,
# height=9*72*7/sqrt(2),res=600)

# xyplot(Value~Year,group=Country,data=demo.OECD[demo.OECD$Measure=='Cigarettes
# per smoker per day',], key=apu,outer=TRUE, type='b',pch=1:4,ylab='Savukkeita
# päivässä(vain tupakoijat)',xlab='Vuosi') dev.off()
# xyplot(Value~Year,group=Country,data=demo.OECD[demo.OECD$Measure=='% of
# population aged 15+ who are daily smokers',], key=apu,outer=TRUE,
# type='b',pch=1:4,ylab='Tupakoijien % osuus',xlab='Vuosi')
require(ggplot2)
apu <- demo.OECD[demo.OECD$Measure == "Cigarettes per smoker per day", ]
p1 <- ggplot(apu, aes(y = Value, x = Year, color = Country, shape = Country)) + geom_point(size = 2) +
    geom_path() + labs(shape = "Maa", color = "Maa", x = "Vuosi", y = "Tupakoivien henkilöiden\nsavukkeiden kulutus päivässä.") +
    theme(legend.position = c(0.15, 0.8), axis.title = element_text(size = 8), legend.title = element_text(size = 8),
        legend.text = element_text(size = 8))
p1
Tupakoivien savukkeiden kulutus. Perustuu OECD:n aikasarjoihin.

Tupakoivien savukkeiden kulutus. Perustuu OECD:n aikasarjoihin.

load(file = "./YhtData/sotka1dt.RData")

ggplot(sotka1.dt, aes(x = Erot, y = Mieli)) + geom_point() + geom_smooth(size = 0.7) +
    coord_cartesian(xlim = c(0, 30)) + labs(x = "Avioeroja 25-64-vuotiailla/1 000 ",
    y = "Mielenterveyden häiriöihin sairaalahoitoa\n saaneet 0-17-vuotiaat/1 000") +
    theme(axis.title = element_text(size = 8), legend.title = element_text(size = 8),
        legend.text = element_text(size = 8))
Avioerojen ja nuorten mielenterveyden häiriöiden riippuvuus kunnittain vuonna 2020.

Avioerojen ja nuorten mielenterveyden häiriöiden riippuvuus kunnittain vuonna 2020.

require(ggplot2)
# Simulation of the data
set.seed(1917)
tmp.data <- data.frame(area = factor(rep(LETTERS[1:5], rep(20, 5))))
tmp.data$x <- 10 + rnorm(nrow(tmp.data)) + as.numeric(tmp.data$area)
tmp.data$y <- 5 + -1 * tmp.data$x + rnorm(nrow(tmp.data)) + 3 * as.numeric(tmp.data$area)
# Mean values of areas A-E
tmp.data.1 <- with(tmp.data, aggregate(list(x = x, y = y), list(area = area), mean))
# 'Ecological' regression model
tmp.m1.coef <- coefficients(lm(y ~ x, data = tmp.data.1))
# Plots
p1.eco1 <- ggplot(tmp.data, aes(x = x, y = y, color = area)) + geom_point()
p1.eco1 + geom_smooth(formula = y ~ x, method = "lm") + geom_abline(slope = tmp.m1.coef[2],
    intercept = tmp.m1.coef[1]) + geom_point(data = tmp.data.1, aes(x = x, y = y),
    pch = 18, cex = 4, col = "navy") + labs(color = "Alapopulaatio") + theme(legend.position = "bottom") +
    theme(legend.position = c(0.1, 0.75), axis.title = element_text(size = 8), legend.title = element_text(size = 6),
        legend.text = element_text(size = 6))
Ekologinen harha (ecological fallacy). Yksilötasolla mitattaessa x-muuttujan kasvu on yhteydessä y-muuttujan vähenemiseen, mutta ryhmätason analyysi osoittaa päinvastaista (musta viiva)

Ekologinen harha (ecological fallacy). Yksilötasolla mitattaessa x-muuttujan kasvu on yhteydessä y-muuttujan vähenemiseen, mutta ryhmätason analyysi osoittaa päinvastaista (musta viiva)

# {r fig.cap='Ekologisen tutkimuksen vahvuuksia ja heikkouksia.', echo =
# TRUE,message=FALSE,out.width = '90%',fig.fullwidth=TRUE}
# library(DiagrammeR);library(DiagrammeRsvg);library(magrittr);library(rsvg);library(png)

# apu<-grViz(scan(file='./Laatikot/VahvuusHeikkousEkologinen.gv',sep='\n',what='',quiet
# =TRUE)) apu %>% export_svg %>% charToRaw %>% rsvg_png
# (,width=4000,file='./YhtKuvat/VahvuusHeikkousEkologinen.png')
# knitr::include_graphics(c('./YhtKuvat/VahvuusHeikkousEkologinen.png'))
# cohorttools::gv2image(gv=apu,type='png',file='./YhtKuvat/VahvuusHeikkousEkologinen')
library(ggplot2)
library(gridExtra)

# load(url('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/nhgh.rda'))
load("nhghV2.RData")
levels(nhgh$re)[5] <- "Other Race\nIncluding Multi-Racial"
cros.1 <- ggplot(nhgh, aes(re, fill = factor(dx))) + xlab("Etnisyys") + ylab("Lukumäärä")
tmp.p1 <- cros.1 + scale_fill_manual(name = "Diabetes\nlääke", values = c("turquoise",
    "red")) + geom_bar() + coord_flip() + theme(legend.position = c(0.8, 0.8), axis.title = element_text(size = 8),
    text = element_text(size = 8), legend.title = element_text(size = 8), legend.text = element_text(size = 8))
# tmp.p2<-cros.1+scale_fill_manual(name='Diabetes\nlääke',values
# =c('turquoise','red'))+coord_flip()+geom_bar(position='fill')+ylab('Osuus')+
# theme(legend.position = 'nome') grid.arrange(tmp.p1, tmp.p2, nrow = 2)
tmp.p1
Diabetesta sairastavien lukumäärä

Diabetesta sairastavien lukumäärä

library(ggplot2)
cros.3 <- ggplot(nhgh, aes(factor(dx), bmi)) + xlab("Diabetes") + ylab("Painoindeksi (BMI)")
cros.3 + geom_boxplot(varwidth = TRUE) + theme(legend.position = c(0.8, 0.8), axis.title = element_text(size = 8),
    legend.title = element_text(size = 8), legend.text = element_text(size = 8))
Diabetesta (1) sairastavien painoindeksi (BMI) verrattuna muihin

Diabetesta (1) sairastavien painoindeksi (BMI) verrattuna muihin

cros.5 <- ggplot(nhgh, aes(age, gh)) + xlab("Ikä") + ylab("Sokerihemoglobiini (%)")
cros.5 + geom_point(col = "navy", alpha = 0.1, size = 0.2) + facet_grid(factor(dx,
    labels = c("ei DM", "DM")) ~ re) + geom_smooth(method = "loess", span = 0.75) +
    ylim(4, 8) + theme(strip.text = element_text(size = 4), axis.text = element_text(size = 6),
    legend.position = c(0.8, 0.8), axis.title = element_text(size = 8), legend.title = element_text(size = 8),
    legend.text = element_text(size = 8))
Diabetesta (DM) sairastavien ja sairastamattomien sokerihemoglobiini (\%) iän suhteen.

Diabetesta (DM) sairastavien ja sairastamattomien sokerihemoglobiini (%) iän suhteen.

library(dplyr)
nhgh$Diabetes <- factor(nhgh$dx, levels = c(0, 1), label = c("ei", "on"))
tmp.dx.gr <- group_by(nhgh, Diabetes)
count.na <- function(x) sum(is.na(x))
# knitr::kable(summarise(tmp.dx.gr,
# Keskiarvo=mean(age,na.rm=TRUE),SD=sd(bmi,na.rm=TRUE),
# Mediaani=median(bmi,na.rm=TRUE),P.25=quantile(bmi,probs = c(0.25)),
# P.75=quantile(bmi,probs = c(0.75))),digits=2, caption='Ikä diabeteksen
# suhteen, keskiarvo, mediaani, keskihajonta, ja ala- ja ylä kvartiilit.')

library(tableone)

tmp1 <- CreateTableOne(nhgh, vars = c("age", "bmi"), strata = "Diabetes", test = FALSE,
    addOverall = TRUE)
tmp1 <- print(tmp1, printToggle = FALSE)
tmp1 <- gsub(pattern = ".", replacement = ",", x = tmp1, fixed = TRUE)

kbl(tmp1, label = "NHANESDiabetesTaulu", caption = "Ikä diabeteksen suhteen, keskiarvo, mediaani, keskihajonta, ja ala- ja ylä kvartiilit.",
    booktabs = T, digits = 2, format.args = list(decimal.mark = ","))
Ikä diabeteksen suhteen, keskiarvo, mediaani, keskihajonta, ja ala- ja ylä kvartiilit.
Overall ei on
n 6795 5881 914
age (mean (SD)) 44,29 (20,59) 41,85 (20,25) 59,98 (15,14)
bmi (mean (SD)) 28,32 (6,95) 27,69 (6,61) 32,41 (7,67)
# tmp.tab<-summarise(tmp.dx.gr,
# Keskiarvo=mean(age,na.rm=TRUE),SD=sd(bmi,na.rm=TRUE),
# Mediaani=median(bmi,na.rm=TRUE),P.25=quantile(bmi,probs = c(0.25)),
# P.75=quantile(bmi,probs = c(0.75))) kbl(tmp.tab,digits=2, caption='Ikä
# diabeteksen suhteen, keskiarvo, mediaani, keskihajonta, ja ala- ja ylä
# kvartiilit.', booktabs = T,digits=2,format.args = list(decimal.mark =','))#
# %>% kable_styling() %>%column_spec(bold =TRUE,column=1)%>%row_spec(bold
# =TRUE,row=1)
cros.4 <- ggplot(nhgh, aes(factor(re), bmi)) + xlab("Etninen ryhmä") + ylab("Painoindeksi (BMI)")
cros.4 + geom_boxplot(varwidth = TRUE, notch = TRUE) + coord_flip() + theme(strip.text = element_text(size = 4),
    axis.text = element_text(size = 6), legend.position = c(0.8, 0.8), axis.title = element_text(size = 8),
    legend.title = element_text(size = 8), legend.text = element_text(size = 8))
Painoindelsin (BMI) riippuvuus etnisesta ryhmästä.

Painoindelsin (BMI) riippuvuus etnisesta ryhmästä.

library(Epi)
require(ztable)
library(kableExtra)
tmp.m.bmi <- lm(bmi ~ re, data = nhgh)
# print(ztable(tmp.m.bmi,caption='Tulokset lineaarisesta regressiomallista
# jossa selitettävä muuttujan on BMI. Vertailuryhmänä on 'Mexican
# American'.'),type='latex') knitr::kable(summary(tmp.m.bmi)$coefficients,
# caption='Tulokset lineaarisesta regressiomallista jossa selitettävä muuttujan
# on BMI. Vertailuryhmänä on 'Mexican American'.', digits=4,booktabs=TRUE)

apu <- ci.lin(tmp.m.bmi)[, c(1, 2, 5, 6)]

kbl(apu, caption = "Tulokset lineaarisesta regressiomallista jossa selitettävä muuttujan on BMI. Vertailuryhmänä on 'Mexican American'. Sarakkeet 2.5\\% ja 97.5\\% osoittavat 95\\% luottamusvälin.",
    booktabs = T, digits = 2, label = "tulosnhanes1", format.args = list(decimal.mark = ",")) %>%
    kable_styling(latex_options = c("hold_position", "scale_down"), full_width = TRUE) %>%
    column_spec(bold = TRUE, column = 1)
Tulokset lineaarisesta regressiomallista jossa selitettävä muuttujan on BMI. Vertailuryhmänä on ‘Mexican American’. Sarakkeet 2.5% ja 97.5% osoittavat 95% luottamusvälin.
Estimate StdErr 2.5% 97.5%
(Intercept) 28,56 0,19 28,19 28,92
reOther Hispanic -0,47 0,32 -1,10 0,15
reNon-Hispanic White -0,50 0,22 -0,94 -0,06
reNon-Hispanic Black 1,15 0,27 0,62 1,68
reOther Race Including Multi-Racial -2,84 0,40 -3,62 -2,07
knitr::kable(apu, caption = "Tulokset lineaarisesta regressiomallista jossa selitettävä muuttujan on BMI. Vertailuryhmänä on 'Mexican American'. Sarakkeet 2.5\\% ja 97.5\\% osoittavat 95\\% luottamusvälin.",
    digits = 2, booktabs = TRUE, label = "tulosnhanes1", format = "latex")
library(stargazer)
tmp.m.bmi.1 <- update(tmp.m.bmi, ~. + age + sex)
apu1 <- ci.lin(tmp.m.bmi.1)[, c(1, 2, 5, 6)]

# kbl(apu1, caption='Tulokset lineaarisesta regressiomallista jossa selitettävä
# muuttujan on BMI. Vertailuryhmänä on 'Mexican American'. Mukana
# taustamuuttujat. Sarakkeet 2.5\\% ja 97.5\\% osoittavat 95\\%
# luottamusvälin.', booktabs = T,digits=2,label = 'tuloslosnhanes2',
# format.args = list(decimal.mark =',')) %>% kable_styling()
# %>%column_spec(bold =TRUE,column=1)

# knitr::kable(ci.lin(tmp.m.bmi.1,subset='re')[,1:4],digit=c(4,4,4,4,4,4))
# print(stargazer(tmp.m.bmi,tmp.m.bmi.1,type=ifelse(knitr::is_html_output(),'html','latex')))

# print(ztable(tmp.m.bmi.1,caption='Tulokset lineaarisesta regressiomallista
# jossa selitettävä muuttujan on BMI. Vertailuryhmänä on 'Mexican American'.
# Mukana taustamuuttujat'),type='latex')


knitr::kable(apu1, caption = "Tulokset lineaarisesta regressiomallista jossa selitettävä muuttujan on BMI. Vertailuryhmänä on 'Mexican American'. Sarakkeet 2.5\\% ja 97.5\\% osoittavat 95\\% luottamusvälin.",
    digits = 2, booktabs = TRUE, label = "tulosnhanes1", format = "latex")
# Create Lexis object for cohort data Include two time scales: age and
# follow-up time (fu.time)
require(Epi)
require(survival)

load(file = "./YhtData/esimcoh.RData")

# png(filename =
# './YhtKuvat/esimcoh.png',width=1600,height=1600/sqrt(2),pointsize=36)
plot(esim.coh.Lx, time.scale = "age", xlab = "Ikä", ylab = "\nHenkilö", lwd = 8,
    cex.axis = 0.8, cex.lab = 1.4, ylim = c(1, 11))
# axis(1,at=c(30,40,50,60)) axis(2,at=1:10)
points(esim.coh.Lx, pch = c(NA, 16)[esim.coh.Lx$lex.Xst + 1], col = "tomato4", cex = 1.4)
PY.ann(esim.coh.Lx, cex = 1.2, digits = 0)
box()
with(esim.coh.Lx, segments(x0 = Age + lex.dur, y0 = No., x1 = rep(60, 10), y1 = No.,
    lwd = 2, lty = 2, col = "grey"))
tmp.id.6.expo <- c(40, 45)
segments(x0 = tmp.id.6.expo[1], y0 = 6.15, x1 = tmp.id.6.expo[2], y1 = 6.15, lwd = 8,
    col = "navy", lty = 1)
Kymmenen henkilön seuranta. Yhdelle henkilölle (nro 6) on merkitty aikariippuva altiste sinisellä vaakaviivalla.

Kymmenen henkilön seuranta. Yhdelle henkilölle (nro 6) on merkitty aikariippuva altiste sinisellä vaakaviivalla.

# dev.off() knitr::include_graphics(c('./YhtKuvat/esimcoh.png'))
library(DiagrammeR)
library(DiagrammeRsvg)
library(magrittr)
library(rsvg)
library(png)

grViz(scan(file = "./YhtKuvat/KohorttiKuvio1V2.gv", sep = "\n", what = "", quiet = TRUE))

Tosiaikainen ja historiallinen seurantatutkimus

require(Epi)

# png(filename ='./YhtKuvat/LexisKuva3A.png',width = 1500,height =
# 1500/sqrt(2),pointsize = 36 )
vanhat.par <- par(mfrow = c(1, 2), mar = c(5, 2, 1, 1))
plot(esim.coh.Lx, xlab = "Ikä", ylab = " ", ylim = c(0, 10.9), time.scale = c("age"),
    lwd = 4)
points(esim.coh.Lx, pch = c(NA, 16)[esim.coh.Lx$lex.Xst + 1], col = "tomato4")
PY.ann(esim.coh.Lx, cex = 0.7, digits = 0)

plot(esim.coh.Lx, xlab = "Vuosi", ylab = " ", ylim = c(0, 10.9), time.scale = c("period"),
    lwd = 4)
points(esim.coh.Lx, pch = c(NA, 16)[esim.coh.Lx$lex.Xst + 1], col = "tomato4")
PY.ann(esim.coh.Lx, cex = 0.7, digits = 0)
Lexis-diagrammi, 1-ulotteinen, ikä ja seuranta-aika

Lexis-diagrammi, 1-ulotteinen, ikä ja seuranta-aika

par(vanhat.par)
# apu<-dev.off()

# knitr::include_graphics(c('./YhtKuvat/LexisKuva3A.png'))
# png(filename ='./YhtKuvat/LexisKuva4A.png',width = 1500,height =
# 1500/sqrt(2),pointsize = 36 )
vanhat.par <- par(mfrow = c(1, 2), mar = c(5, 4, 1, 0))
plot(esim.coh.Lx, time.scale = c("period", "age"), xlab = "Vuosi", ylab = "Ikä",
    lwd = 4)
points(esim.coh.Lx, pch = c(NA, 16)[esim.coh.Lx$lex.Xst + 1], col = "tomato4")
PY.ann(esim.coh.Lx, cex = 0.7, digits = 0)

plot(esim.coh.Lx, time.scale = c("fu.time", "age"), xlab = "Seuranta-aika", ylab = "Ikä",
    lwd = 4)
points(esim.coh.Lx, pch = c(NA, 16)[esim.coh.Lx$lex.Xst + 1], col = "tomato4")
PY.ann(esim.coh.Lx, cex = 0.7, digits = 0)
Lexis-diagrammi, 2-ulotteinen, kalenterivuosi, seuranta-aika ja ikä

Lexis-diagrammi, 2-ulotteinen, kalenterivuosi, seuranta-aika ja ikä

par(vanhat.par)
# apu<-dev.off()

# knitr::include_graphics(c('./YhtKuvat/LexisKuva4A.png'))
require(survival)
require(ggplot2)
require(survminer)
require(ggsurvfit)
load(file = "TADA11.RData")
# Select 54 — Virginia for practicals
TADA.11.54 <- TADA.11[TADA.11$stater == "54", ]
survfit.54.sex <- survfit2(Surv(follow/365.25, inddea == "1") ~ sex, data = TADA.11.54)

kmtenure <- ggsurvfit(survfit.54.sex, linetype_aes = TRUE) + add_censor_mark() +
    scale_color_grey() + scale_fill_grey() + add_confidence_interval() + add_quantile() +
    labs(x = "Aika (d)", y = "Elossa olevien\n osuus") + add_risktable(size = 3.5,
    stats_label = list(n.risk = "Pop. koko", n.event = "N"), risktable_stats = c("n.risk",
        "n.event"), theme = theme(strip.text = element_text(size = 4), axis.text = element_text(size = 6),
        text = element_text(size = 8), legend.position = c(0.8, 0.8), axis.title = element_text(size = 8),
        legend.title = element_text(size = 8), legend.text = element_text(size = 8)))

kmtenure
Kokonaiskuolleisuus sukupuolen mukaan

Kokonaiskuolleisuus sukupuolen mukaan

# ggsave(kmtenure,filename = './YhtKuvat/kmtenure.png',dpi=300)
# knitr::include_graphics('./YhtKuvat/kmtenure.png',)
library(epitools)
require(cohorttools)
# Aggregate events and person-years (1000)
tmp.54.tenure <- mkratetable(Surv(follow, inddea == "1") ~ sex, add.RR = TRUE, data = TADA.11.54,
    scale = (365.25 * 1000))

colnames(tmp.54.tenure) <- c("Ryhmä", "Seur.aika", "Lukumäärä", "Kuolleisuus",
    "95% alaraja", "95% yläraja", "MRR", "95% alaraja", "95% yläraja")

apu <- cbind(as.character(tmp.54.tenure[, 1]), format(tmp.54.tenure[, 2], decimal.mark = ",",
    digits = 2, nsmall = 2), tmp.54.tenure[, 3], paste0(format(tmp.54.tenure[, 4],
    decimal.mark = ",", digits = 2, nsmall = 2), " (", format(tmp.54.tenure[, 5],
    decimal.mark = ",", digits = 2, nsmall = 2), "-", format(tmp.54.tenure[, 6],
    decimal.mark = ",", digits = 2, nsmall = 2), ")"), paste0(format(tmp.54.tenure[,
    7], decimal.mark = ",", digits = 2, nsmall = 2), " (", format(tmp.54.tenure[,
    8], decimal.mark = ",", digits = 2, nsmall = 2), "-", format(tmp.54.tenure[,
    9], decimal.mark = ",", digits = 2, nsmall = 2), ")"))

colnames(apu) <- c("Ryhmä", "Seur.aika", "Lukumäärä", "Kuolleisuus", "MRR")

kbl(apu, caption = "Kokonaiskuolleisuus. Kuolleiden lukumäärä (N), kertynyt seuranta-aika (1/1000), kuolleisuus ja suhteellinen kuoleman riski (MRR)",
    label = "tenure54", booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling()  # %>%column_spec(bold =TRUE,column=1)%>%row_spec(bold =TRUE,row=1)
Kokonaiskuolleisuus. Kuolleiden lukumäärä (N), kertynyt seuranta-aika (1/1000), kuolleisuus ja suhteellinen kuoleman riski (MRR)
Ryhmä Seur.aika Lukumäärä Kuolleisuus MRR
Male 154,59 1298 8,40 (7,95-8,87) 1,00 (1,00-1,00)
Female 168,54 1257 7,46 (7,05-7,88) 0,89 (0,82-0,96)
# knitr::kable(apu,caption='Kokonaiskuolleisuus. Kuolleiden lukumäärä (N),
# kertynyt seuranta-aika (1/1000), kuolleisuus ja suhteellinen kuoleman riski
# (MRR)',format = 'latex')

# knitr::kable(tmp.54.tenure,caption='Kokonaiskuolleisuus. Kuolleiden lukumäärä
# (N), kertynyt seuranta-aika (1/1000), kuolleisuus',
# digits=c(0,2,2,2,2,2,2,2,2))

# knitr::kable(tmp.tab1,caption='Kokonaiskuolleisuus. Kuolleiden lukumäärä (N),
# kertynyt seuranta-aika (1/1000), kuolleisuus, ja vertailut.',
# digits=c(0,2,2,2,2)) options(ztable.type='viewer') Korjaa
# print(ztable(tmp.tab1,digits=c(0,0,2,2,2,2), caption='Kokonaiskuolleisuus.
# Kuolleiden lukumäärä (N), kertynyt seuranta-aika (1/1000), kuolleisuus, ja
# vertailut.', align='lrrrrrrrr'))
Lepra-tutkimuksen muuttujat:
  
* age:  ikä (5-9, 10-14, 15-19, 20-24, 25-29, 30-44, over 45)
* sex:   sukupuoli (male, female)
* bcg:  Bacillus Calmette–Guerin (BCG) rokotteen arpi, (no, yes)
* school:   koulutuksen pituus (1-5yrs, 6-8yrs, sec, tert)
* house:    asuinrakennuksen materiaali (brick, sunbrick, wattle, temp)
library(xtable)
library(epitab)
library(epiR)
data(lep)
lep$Lepra <- factor(lep$d, levels = c(1, 0), label = c("Case", "Control"))
lep$bcg.1 <- Relevel(lep$bcg, ref = "yes")
# lep.tab<-with(lep,twoby2(exposure=bcg.1,outcome=Lepra,print =TRUE))
lep.tab <- with(lep, table(bcg.1, outcome = Lepra))

epi.2by2(dat = lep.tab, method = "case.control", conf.level = 0.95, units = 100,
    outcome = "as.columns")
##              Outcome +    Outcome -      Total                       Odds
## Exposed +           47          500        547        0.09 (0.07 to 0.12)
## Exposed -          227          596        823        0.38 (0.33 to 0.44)
## Total              274         1096       1370        0.25 (0.22 to 0.28)
## 
## Point estimates and 95% CIs:
## -------------------------------------------------------------------
## Exposure odds ratio                            0.25 (0.18, 0.35)
## Attrib fraction (est) in the exposed (%)      -304.81 (-479.42, -187.47)
## Attrib fraction (est) in the population (%)   -52.35 (-64.45, -41.14)
## -------------------------------------------------------------------
## Uncorrected chi2 test that OR = 1: chi2(1) = 74.060 Pr>chi2 = <0.001
## Fisher exact test that OR = 1: Pr>chi2 = <0.001
##  Wald confidence limits
##  CI: confidence interval
# with(lep,twoby2(exposure=bcg.1,outcome=Lepra))
# print(ztable(apu$table[,1:2],caption='Sairauslähtöinen tutkimus. Lepra ja
# BCG-arpi'),digits=0, type=ifelse(knitr::is_html_output(),'html','latex'))
# print(xtable(apu$table[,1:2],caption='Sairauslähtöinen tutkimus. Lepra ja
# BCG-arpi'),digits=0, type=ifelse(knitr::is_html_output(),'html','latex'))
# apu.tab<-with(lep,epitab(x=table(bcg.1,Lepra),method='oddsratio'))$tab[,-8]

# print(ztable(apu.tab,caption='Sairauslähtöinen tutkimus. Lepra ja
# BCG-arpi'),digits=c(0,0,4,0,4,4,4,4),
# type=ifelse(knitr::is_html_output(),'html','latex'))
# knitr::kable(apu.tab,caption='Sairauslähtöinen tutkimus. Lepra ja
# BCG-arpi'),digits=c(0,0,4,0,4,4,4,4))

Kokeellinen tutkimus

teksti.on.HTML <- !knitr:::is_html_output()
require(ztable)
require(DiagrammeR)

options(ztable.type = "html")

# apu<-read.table(pipe('pbpaste'), sep='\t', header=TRUE) head(apu)
# table(apu$X7)
source("Funktioita.R", echo = TRUE)
## 
## > Piirra.Laatikko <- function(Otsikko = "Otsikko", Kohdat = "1", 
## +     item.mark = "&#9724; ", Otsikko.size = 20, L.leveys = 25, 
## +     rows = "", ta .... [TRUNCATED] 
## 
## > Piirra.LaatikkoV2 <- function(Otsikko = "Otsikko", 
## +     Kohdat = "1", Otsikko2, Kohdat2, item.mark = "&#9724; ", 
## +     Otsikko.size = 20, L.levey .... [TRUNCATED]
grViz(Piirra.LaatikkoV2(Otsikko = "Kliinisen kokeen<br/> tavoitteet", Kohdat = c(" Tutkitaan uuden lääkkeen paremmuutta vanhaan verrattuna (superiority)",
    " Tutkitaan ovatko lääkkeet yhtä hyviä (equivalence)", " Tutkitaan onko lääke toista huonompi (non-inferiority)",
    " Annosvasteen määrittäminen (dose-response)"), Otsikko.size = 16, L.leveys = 30,
    table.attr = "CELLBORDER=\"0\" style=\"rounded\" "))

Kliinisen lääkekokeen tavoitteita.

Epidemiologisen tutkimuksen suunnittelu

# library(pxR) apu.ms.dt<-as.data.frame(read.px(filename =
# 'https://pxdata.stat.fi:443/PxWeb/sq/c1632b5c-5d05-4dd5-8523-cc2187afbbc3'))
# https://pxdata.stat.fi:443/PxWeb/sq/7350f4b1-f329-4974-94cd-fd2c60e39401
# save(apu.ms.dt,file='./YhtData/apumsdt.RData')
load(file = "./YhtData/apumsdt.RData")

ggplot(apu.ms.dt, aes(x = as.numeric(as.character(Vuosi)), y = value, col = Sukupuoli,
    linetype = Sukupuoli)) + geom_line(size = 0.7) + labs(x = "Vuosi", y = "Kuolleiden lukumäärä") +
    theme(legend.position = c(0.15, 0.3), axis.title = element_text(size = 8), legend.title = element_text(size = 8),
        legend.text = element_text(size = 8))
Mahasyöpään (ICD10 C16) kuolleet Suomessa.

Mahasyöpään (ICD10 C16) kuolleet Suomessa.

# knitr::include_graphics(c('./YhtKuvat/MahaSyopaTK.png'),dpi=300)
# apu.ravint.dt<-as.data.frame(read.px(filename =
# 'https://pxdata.stat.fi:443/PxWeb/sq/4faa6cf8-f5c2-45dc-a81e-57f950f75873'))
# https://pxdata.stat.fi:443/PxWeb/sq/7350f4b1-f329-4974-94cd-fd2c60e39401
# apu.ravint.dt$Vuosi.num<-with(apu.ravint.dt,as.numeric(substr(Kuukausi,1,4))+as.numeric(substr(Kuukausi,6,7))/12-(1/24))
# save(apu.ravint.dt,file='./YhtData/apuravintdt.RData')

load(file = "./YhtData/apuravintdt.RData")

ggplot(apu.ravint.dt, aes(x = Vuosi.num, y = value)) + geom_line(size = 0.5) + labs(x = "Vuosi",
    y = "Määrä kuukaudessa (htv).")
Ravintoloiden konkurssit kuukausittain yritysten henkilötyövuosien (htv) mukaan.

Ravintoloiden konkurssit kuukausittain yritysten henkilötyövuosien (htv) mukaan.

# https://pxdata.stat.fi:443/PxWeb/sq/bdd08272-cbcf-4d06-a09e-1544d7292b6e
# knitr::include_graphics(c('./YhtKuvat/RavintolaKonkurssit.png'))

Epidemiologinen päättely

library(DiagrammeR)
library(DiagrammeRsvg)
library(magrittr)
library(rsvg)
library(png)

DiagrammeR::grViz(scan("YhtKuvat/TupakkaFisherDAG.gv", what = "", sep = "\n"))

Fisherin näkemys tupakoinnin ja keuhkosyövän suhteesta

grViz(scan("KuvatLuku2/SyopaDAGKaksi.gv", what = "", sep = "\n"))

Diabetes ja syöpä, DAG

Harhat ja niiden lähteet

# ```{r Luku4HarhaKuva, echo = TRUE,results='asis',fig.cap = 'Harha ja
# tarkkuus. Pieni tarkkuus, #harhaton (vas. ylä), tarkka ja harhaton (oik.
# ylä), suuri tarkkuus, harhainen (vas. ala), #pieni tarkkuus, harhainen (oik.
# ala)'}

# png('./YhtKuvat/harhataulukuvio.png',width = 1000, height = 1000,pointsize =
# 24)
set.seed(seed = 30121999)
apu.harha.1 <- list(x = rnorm(20, sd = 1.2), y = rnorm(20, sd = 1.2))
apu.harha.2 <- list(x = rnorm(20, sd = 0.25), y = rnorm(20, sd = 0.15))

apu.harha.3 <- list(x = rnorm(20, mean = 1.5, sd = 0.25), y = rnorm(20, mean = -0.7,
    sd = 0.25))
apu.harha.4 <- list(x = rnorm(20, mean = 1.5, sd = 1), y = rnorm(20, mean = -0.7,
    sd = 1))

par.old <- par(mar = rep(0, 4), mfrow = c(2, 2))

with(apu.harha.1, plot(x, y, pch = 16, xlim = 0.8 * c(-6, 6), ylim = 0.8 * c(-6,
    6), xlab = "", ylab = "", axes = FALSE))
symbols(x = rep(0, 5), y = rep(0, 5), circles = 1:5, add = TRUE, inches = FALSE,
    lwd = 2)

with(apu.harha.2, plot(x, y, pch = 16, xlim = 0.8 * c(-6, 6), ylim = 0.8 * c(-6,
    6), xlab = "", ylab = "", axes = FALSE))
symbols(x = rep(0, 5), y = rep(0, 5), circles = 1:5, add = TRUE, inches = FALSE,
    lwd = 2)

with(apu.harha.3, plot(x, y, pch = 16, xlim = 0.8 * c(-6, 6), ylim = 0.8 * c(-6,
    6), xlab = "", ylab = "", axes = FALSE))
symbols(x = rep(0, 5), y = rep(0, 5), circles = 1:5, add = TRUE, inches = FALSE,
    lwd = 2)

with(apu.harha.4, plot(x, y, pch = 16, xlim = 0.8 * c(-6, 6), ylim = 0.8 * c(-6,
    6), xlab = "", ylab = "", axes = FALSE))
symbols(x = rep(0, 5), y = rep(0, 5), circles = 1:5, add = TRUE, inches = FALSE,
    lwd = 2)
Harha ja tarkkuus. Pieni tarkkuus, harhaton (vas. ylä), tarkka ja harhaton (oik. ylä), suuri tarkkuus, harhainen (vas. ala), pieni tarkkuus, harhainen (oik. ala)

Harha ja tarkkuus. Pieni tarkkuus, harhaton (vas. ylä), tarkka ja harhaton (oik. ylä), suuri tarkkuus, harhainen (vas. ala), pieni tarkkuus, harhainen (oik. ala)

par((par.old))
# dev.off() knitr::include_graphics(path = './YhtKuvat/harhataulukuvio.png')
library(DiagrammeR)
library(DiagrammeRsvg)
library(magrittr)
# library(svglite)
library(rsvg)
library(png)

grViz("digraph G
{node [shape=box, color=white, height=0.1, width=0.1];
rankdir=TB;
C [color=black, height=0.1, width=0.1];
R->E;E->C;D->C;}")

Berksonin harha

Epidemiologisen päättelyn apuneuvoja

library(cohorttools)
load("./YhtData/TADA.11.54.RData")
TADA.11.54$Omistus <- Relevel(TADA.11.54$tenure, list(Owned = 1, 2:4))
TADA.11.54$Omistus <- Relevel(TADA.11.54$Omistus, ref = 2)
levels(TADA.11.54$Omistus) <- c("Muu", "Omistus")
tmp.rt <- mkratetable(Surv(follow, as.numeric(inddea)) ~ Omistus, data = TADA.11.54,
    scale = 1000 * 365.25, add.RR = FALSE)
TADA.54.m0 <- glm(as.numeric(inddea) ~ Omistus + offset(log(follow/365.25)), family = poisson,
    data = TADA.11.54)
TADA.54.m1 <- update(TADA.54.m0, ~. + age)

# knitr::kable(tmp.rt,digits=3,caption='Kuolleisuus (1/100 henkilövuotta)
# asunnon omistuksen mukaan jaoteltuna')
# round(ci.exp(TADA.54.m0,subset='Omistus'),3)
# round(ci.exp(TADA.54.m1,subset='Omistus'),3)
colnames(tmp.rt) <- c("", "Henk.vuodet", "N", "MR", "2,5%", "97,5%")
kbl(tmp.rt, digits = 3, format.args = list(decimal.mark = ","), caption = "Kuolleiden lukumäärä (N), kuolleisuus (MR) ja sen 95\\% luottamusväli (1/100 henkilövuotta) asunnon omistuksen mukaan jaoteltuna") %>%
    kable_classic()
Kuolleiden lukumäärä (N), kuolleisuus (MR) ja sen 95% luottamusväli (1/100 henkilövuotta) asunnon omistuksen mukaan jaoteltuna
Henk.vuodet N MR 2,5% 97,5%
Muu 97,965 558 5,696 5,233 6,189
Omistus 225,164 1997 8,869 8,484 9,267
tmp.tenuremalli <- ci.exp(TADA.54.m1, subset = -1)
colnames(tmp.tenuremalli) <- c("MRR", "2,5%", "97,5%")
kbl(tmp.tenuremalli, digits = 2, format.args = list(decimal.mark = ","), caption = "Suhteellinen kuolleisuus (MRR) ja sen 95\\% luottamusväli Poisson regressio mallin mukaan. Omistajia verrattu muihin hallintamuotoihin, ikä yhtä
    vuotta kohti.") %>%
    kable_classic()
Suhteellinen kuolleisuus (MRR) ja sen 95% luottamusväli Poisson regressio mallin mukaan. Omistajia verrattu muihin hallintamuotoihin, ikä yhtä vuotta kohti.
MRR 2,5% 97,5%
OmistusOmistus 0,77 0,70 0,85
age 1,09 1,09 1,09
grViz(scan(file = "./YhtKuvat/DAGBDSimple.gv", sep = "\n", what = ""))

Asumismuoto ja kuolleisuus

IV.kuvio <- "digraph graphname {
           rankdir=TB
           style=filled;
           color=white;
           Inst [label=\"Instrumentti\nmuuttuja\" fontsize=24, shape=box, fontname=Calibri]      // node T
           Trea [label=\"Altiste\" fontsize=24, shape=box, fontname=Calibri]  // node P
           D  [label=\"Tutkittava\nvaste\"  fontsize=24, shape=box, fontname=Calibri]  // node P
           U [fontsize=12, fontname=Calibri]  // node P
           V [fontsize=12, fontname=Calibri]

           Inst->Trea [label=\"b\"];
           Trea->D [label=\"a\"];
           U->Trea;
           V->D;
           U->V [label=\"\t r\" arrowhead=\"none\"];
{ rank=same; U; V }
{ rank=same; Trea; D }
           }"
grViz(IV.kuvio)

Nuoli “a” osoittaa altisteen suoran yhteyden vasteeseen, joka on riippumaton jäännösvaikutusten (U ja V) korrelaatiosta (r). Vaikutus havaitaan instrumentti muuttujan avulla, koska sen oletetaan vaikuttavan vasteeseen vain altisteen kautta (b).

Mendel-satunnaistus (Mendelian randomization) on muunnelma IM tekniikasta. Siinä IM käytetään henkilön genotyyppiä instrumenttimuuttujana (Davies, Holmes, and Smith 2018).

Mend.kuvio <- "digraph graphname {
           rankdir=TB
           style=filled;
           color=white;
           Inst [label=\"ALDH2\ngeeni\" fontsize=24, shape=box, fontname=Calibri]      // node T
           Trea [label=\"Alkoholin\nkulutus\" fontsize=24, shape=box, fontname=Calibri]  // node P
           D  [label=\"Verenpaine\"  fontsize=24, shape=box, fontname=Calibri]  // node P

           Inst->Trea [label=\"\t b\"];
           Trea->D [label=\"a\"];
{ rank=same; Trea; D }
           }"
DiagrammeR::grViz(Mend.kuvio)

Mendel-satunnaistus.

library(foreign)
library(rdd)
AEJfigs = read.dta("./YhtData/AEJfigs.dta")
# All = all deaths
AEJfigs$age = AEJfigs$agecell - 21
AEJfigs$over21 = ifelse(AEJfigs$agecell >= 21, 1, 0)
reg.1 = RDestimate(all ~ agecell, data = AEJfigs, cutpoint = 21)
plot(reg.1)
title(main = "All Causes of Death", xlab = "AGE", ylab = "Mortality rate from all causes (per 100,000)")
Kuolleisuus tieliikenneonnettomuuksissa Yhdysvalloissa 1997–2005. Havainnot (pisteet) ja mallin ennuste ja 95 prosentin luottamusväli.

Kuolleisuus tieliikenneonnettomuuksissa Yhdysvalloissa 1997–2005. Havainnot (pisteet) ja mallin ennuste ja 95 prosentin luottamusväli.

summary(reg.1)
## 
## Call:
## RDestimate(formula = all ~ agecell, data = AEJfigs, cutpoint = 21)
## 
## Type:
## sharp 
## 
## Estimates:
##            Bandwidth  Observations  Estimate  Std. Error  z value  Pr(>|z|) 
## LATE       1.6561     40            9.001     1.480       6.080    1.199e-09
## Half-BW    0.8281     20            9.579     1.914       5.004    5.609e-07
## Double-BW  3.3123     48            7.953     1.278       6.223    4.882e-10
##               
## LATE       ***
## Half-BW    ***
## Double-BW  ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## F-statistics:
##            F      Num. DoF  Denom. DoF  p        
## LATE       33.08  3         36          3.799e-10
## Half-BW    29.05  3         16          2.078e-06
## Double-BW  32.54  3         44          6.129e-11
# png(file='./YhtKuvat/RDDUSATraffic.png',width=2400,height=2400/sqrt(2),res=300)
reg.2 = RDestimate(mva ~ agecell, data = AEJfigs, cutpoint = 21)
plot(reg.2)
title(main = "", xlab = "Ikä", ylab = "Kuolleisuus (1/100 000)")
Kuolleisuus tieliikenneonnettomuuksissa Yhdysvalloissa 1997–2005. Havainnot (pisteet) ja mallin ennuste ja 95 prosentin luottamusväli.

Kuolleisuus tieliikenneonnettomuuksissa Yhdysvalloissa 1997–2005. Havainnot (pisteet) ja mallin ennuste ja 95 prosentin luottamusväli.

# dev.off() knitr::include_graphics(c('YhtKuvat/RDDUSATraffic.png'),dpi=300)
# Kuvioiden teko cohorttools::gv2image(
# scan(file='./YhtKuvat/Mediation.gv',sep='\n',what=''),file='./YhtKuvat/Mediation',type
# = 'png',width=500) cohorttools::gv2image(
# scan(file='./YhtKuvat/MediationFlow.gv',sep='\n',what=''),file='./YhtKuvat/MediationFlow',type
# = 'png',width=1200)

# Download data online. This is a simulated dataset for this post.  myData
# <-read.csv(file=url('http://static.lib.virginia.edu/statlab/materials/data/mediationData.csv'))
# save(myData,file='./YhtData/myData.RData')
load(file = "./YhtData/myData.RData")
model.M <- lm(M ~ X, myData)
model.Y <- lm(Y ~ X + M, myData)
results <- mediation::mediate(model.M, model.Y, treat = "X", mediator = "M", boot = TRUE,
    sims = 100)
summary(results)
## 
## Causal Mediation Analysis 
## 
## Nonparametric Bootstrap Confidence Intervals with the Percentile Method
## 
##                Estimate 95% CI Lower 95% CI Upper p-value    
## ACME             0.3565       0.2242         0.51  <2e-16 ***
## ADE              0.0396      -0.1970         0.25     0.8    
## Total Effect     0.3961       0.1817         0.64  <2e-16 ***
## Prop. Mediated   0.9000       0.5078         1.90  <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Sample Size Used: 100 
## 
## 
## Simulations: 100

#Meta-analyysi

require(metafor)
options(width = 60)
dat <- escalc(measure = "RR", ai = tpos, bi = tneg, ci = cpos, di = cneg, data = dat.bcg,
    append = TRUE)
res <- rma(ai = tpos, bi = tneg, ci = cpos, di = cneg, data = dat, measure = "RR")
res
## 
## Random-Effects Model (k = 13; tau^2 estimator: REML)
## 
## tau^2 (estimated amount of total heterogeneity): 0.3132 (SE = 0.1664)
## tau (square root of estimated tau^2 value):      0.5597
## I^2 (total heterogeneity / total variability):   92.22%
## H^2 (total variability / sampling variability):  12.86
## 
## Test for Heterogeneity:
## Q(df = 12) = 152.2330, p-val < .0001
## 
## Model Results:
## 
## estimate      se     zval    pval    ci.lb    ci.ub      
##  -0.7145  0.1798  -3.9744  <.0001  -1.0669  -0.3622  *** 
## 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# png(file='./YhtKuvat/metafig1.png',width = 1200, height =
# 1200,pointsize = 30)
forest(res, slab = paste(dat$author, dat$year, sep = ", "), xlim = c(-16,
    6), at = log(c(0.05, 0.25, 1, 4)), atransf = exp, ilab = cbind(dat$tpos,
    dat$tneg, dat$cpos, dat$cneg), ilab.xpos = c(-9.5, -8, -6,
    -4.5), cex = 0.9)
op <- par(cex = 0.75, font = 2)
text(c(-9.5, -8, -6, -4.5), 15, c("TB+", "TB-", "TB+", "TB-"))
text(c(-8.75, -5.25), 16, c("Vaccinated", "Control"))
text(-16, 15, "Author(s) and Year", pos = 4)
text(6, 15, "Relative Risk [95% CI]", pos = 2)
Meta-analyysin tulokset. Piste-esimaaattia merkitsevän mustan neliön koko on suhteessa tutkimuksen kokoon. Alimpana kaikkien osatutkimusten yhteinen tulos (vinoneliö).

Meta-analyysin tulokset. Piste-esimaaattia merkitsevän mustan neliön koko on suhteessa tutkimuksen kokoon. Alimpana kaikkien osatutkimusten yhteinen tulos (vinoneliö).

par(op)
# apu<-dev.off()
# knitr::include_graphics(path='./YhtKuvat/metafig1.png',dpi=300)
# png(filename = './YhtKuvat/MetaSuppilo.png',width =
# 2000,height = 2000/sqrt(2),pointsize =48 )
funnel(res, xlab = "Log Riski suhde", ylab = "Keskivirhe")
Meta-analyysin tulokset. Suppilokuvio.

Meta-analyysin tulokset. Suppilokuvio.

# dev.off() knitr::include_graphics(path =
# './YhtKuvat/MetaSuppilo.png')

Suunnatut syklittömät verkot epidemiologiassa

library(DiagrammeR)
library(DiagrammeRsvg)
library(magrittr)
library(svglite)
library(rsvg)
library(png)
library(cowplot)
library(ggplot2)
#
p1 <- ggdraw() + draw_image("./YhtKuvat/DAGCausation.png", scale = 0.9)
p2 <- ggdraw() + draw_image("./YhtKuvat/DAGReCausation.png",
    scale = 0.9)
p3 <- ggdraw() + draw_image("./YhtKuvat/DAGConfounding.png",
    scale = 0.9)
p4 <- ggdraw() + draw_image("./YhtKuvat/DAGMittaaminen.png",
    scale = 0.9)

print(plot_grid(p1, p2, p3, p4, labels = c("A", "B", "C", "D")))
Kausaalidiagrammeja eli suunnattuja syklittömiä verkkoja (directed acyclical graph eli DAG) masennuksen ja aivohalvauksen yhteydestä.

Kausaalidiagrammeja eli suunnattuja syklittömiä verkkoja (directed acyclical graph eli DAG) masennuksen ja aivohalvauksen yhteydestä.

# ggsave(plot = plot_grid(p1, p2, p3,
# p4,labels=c('A','B','C','D')),file='./YhtKuvat/DAGCausationKaikki.png')

# knitr::include_graphics('./YhtKuvat/DAGCausationKaikki.png',dpi=600)
# gv2image(
# scan(file='./YhtKuvat/DagEsim2.gv',sep='\n',what=''),file='./YhtKuvat/DagEsim2',type
# = 'png',width=1000)
# knitr::include_graphics(c('./YhtKuvat/DagEsim2.png'),dpi=600)

DAG ja sekoittavat tekijät

Voimme esittää DAG:in avulla havainnollisesti erilaisia olettamuksia assosiaatioista ja kausaalisuhteista. Ne ovat myös hyödyllisiä pyrittäessä määrittämään mitä sekoittavia tekijöitä (confounders) on otettava huomioon tilastollisissa malleissa.

# gv2image(
# scan(file='./YhtKuvat/DAGCausation.gv',sep='\n',what=''),file='./YhtKuvat/DAGCDAGCausation',type
# = 'png',width=1000) gv2image(
# scan(file='./YhtKuvat/DAGReCausation.gv',sep='\n',what=''),file='./YhtKuvat/DAGCDAGReCausation',type
# = 'png',width=1000) gv2image(
# scan(file='./YhtKuvat/DAGConfounding.gv',sep='\n',what=''),file='./YhtKuvat/DAGConfounding',type
# = 'png',width=1000) gv2image(
# scan(file='./YhtKuvat/DAGMittaaminen.gv',sep='\n',what=''),file='./YhtKuvat/DAGMittaaminen',type
# = 'png',width=1000)
grViz(scan(file = "./YhtKuvat/DAGSets.gv", sep = "\n", what = ""))
grViz(scan(file = "./YhtKuvat/DAGBD.gv", sep = "\n", what = ""))

DAG ja sekoittavat tekijät.

library(dagitty)
g1 <- dagitty("dag {
    Ika -> Glaukooma
    Ika -> DM
    DM -> Glaukooma
    Statiinit -> Glaukooma
    Statiinit -> DM
    DM -> SSRI
    SSRI -> Glaukooma
    Beeta.salpaaja -> Glaukooma
    Kalsium.estaja -> Glaukooma
}")
# png(file='./YhtKuvat/DAGg1.png',width=1500,pointsize =
# 36) plot(graphLayout(g1)) dev.off()
# knitr::include_graphics('./YhtKuvat/DAGg1.png',dpi=300)
g1.1 <- as.character(g1)
g1.1 <- gsub(x = g1.1, pattern = "dag", replacement = "digraph G ")
g1.1 <- gsub(x = g1.1, pattern = "{", replacement = "{ node [shape=box]; ",
    fixed = TRUE)
g1.1 <- gsub(x = g1.1, pattern = ".", replacement = "_", fixed = TRUE)
DiagrammeR::grViz(g1.1)

Glaukooman ilmaantuvuuteen vaikuttavia tekijöitä.

# cohorttools::gv2image(g1.1,file='./YhtKuvat/DAGg1V1',type
# = 'png',width=1000)

# knitr::include_graphics('./YhtKuvat/DAGg1V1.png',dpi=300)
print(adjustmentSets(x = g1, exposure = "DM", outcome = "Glaukooma",
    effect = "direct"))

{ Ika, SSRI, Statiinit }

library(ggdag)

# bigger_dag <- dagify(y ~ x + a + b,
#   x ~ a + b,
#   exposure = "x",
#   outcome = "y",
#   labels = c("y"="Vaste")
# )
# 
# ggdag_adjustment_set(bigger_dag)

# theme_bare <- theme(
#   axis.line = element_blank(), 
#   axis.text.x = element_blank(), 
#   axis.text.y = element_blank(),
#   axis.ticks = element_blank(), 
#   axis.title.x = element_blank(), 
#   axis.title.y = element_blank(), 
#   #axis.ticks.length = unit(0, "lines"), # Error 
#   axis.ticks.margin = unit(c(0,0,0,0), "lines"), 
#   legend.position = "none", 
#   panel.background = element_rect(fill = "gray"), 
#   panel.border = element_blank(), 
#   panel.grid.major = element_blank(), 
#   panel.grid.minor = element_blank(), 
#   panel.margin = unit(c(0,0,0,0), "lines"), 
#   plot.background = element_rect(fill = "blue"),
#   plot.margin = unit(c(0,0,0,0), "lines")
# )

force_dag <- dagify(force ~ suspicious+stop+minority,
  stop ~ minority+suspicious,
  labels = c(
    "minority" = "Kuuluu\nvähemmistöön",
    "force" = "Voimankäyttö",
    "stop" = "Pysäytys",
    "suspicious" = "Epäilys"
  ),
  # latent = "unhealthy",
  exposure = "minority",
  outcome = "force"
)

ggplot(force_dag,aes(x = x, y = y, xend = xend, yend = yend)) +
  geom_dag_point() +
  geom_dag_edges() +
  # geom_dag_text() +
  geom_dag_label_repel(aes(label = label),col = "black", show.legend = FALSE)+
  theme_dag()
Knox et al. DAG poliisiväkivallasta.

Knox et al. DAG poliisiväkivallasta.

# ggdag(force_dag, text = FALSE, use_labels = "label")+labs(x="",y="")+
#   theme(axis.line = element_blank(),
#         axis.text.x = element_blank(),
#         axis.text.y = element_blank())
# ggdag_adjustment_set(force_dag,text = FALSE,use_labels = "label",
#                      shadow = TRUE)#+theme(text ,text = element_text(colour="black"))

Epävarmuuden mittaaminen

require(knitr)
# Transparent colors
addTrans <- function(color, trans) {
    # This function adds transparancy to a color.  Define
    # transparancy with an integer between 0 and 255 0
    # being fully transparant and 255 being fully visable
    # Works with either color and trans a vector of equal
    # length, or one of the two of length 1.

    if (length(color) != length(trans) & !any(c(length(color),
        length(trans)) == 1))
        stop("Vector lengths not correct")
    if (length(color) == 1 & length(trans) > 1)
        color <- rep(color, length(trans))
    if (length(trans) == 1 & length(color) > 1)
        trans <- rep(trans, length(color))

    num2hex <- function(x) {
        hex <- unlist(strsplit("0123456789ABCDEF", split = ""))
        return(paste(hex[(x - x%%16)/16 + 1], hex[x%%16 + 1],
            sep = ""))
    }
    rgb <- rbind(col2rgb(color), trans)
    res <- paste("#", apply(apply(rgb, 2, num2hex), 2, paste,
        collapse = ""), sep = "")
    return(res)
}

# Normal
tmp.x <- seq(from = -3, to = 3, length = 50)
# png(file='C:/HY-Data/JKHAUKKA/OneDriveForBusiness/OneDrive
# for Business/Opetus/EpiKirja/MS/KuvatLuku4/normCI.png',
# pointsize = 18)
par(mar = c(4, 4, 2, 1))
par(mai = c(1.02, 0.82, 0.82, 0.42))
curve(dnorm, from = -3, to = 3, lwd = 2, col = "navy", ylab = "Tiheysfunktion arvo",
    xlab = "", ylim = c(0, 0.4))
polygon(x = c(tmp.x), y = c(dnorm(tmp.x)), col = addTrans("blue",
    20))
abline(v = c(0, qnorm(c(0.025, 0.975))), lty = 2)
text(x = qnorm(c(0.025, 0.975)), y = c(0.15, 0.15), round(qnorm(c(0.025,
    0.975)), 4), srt = 90, cex = 1.4)
# dev.off()
abline(v = qnorm(c(0.025, 0.975)), lty = 2)
Normaalijakauma ja sen 2,5\% ja 97,5\% pisteet.

Normaalijakauma ja sen 2,5% ja 97,5% pisteet.

require(Epi)

# M??ritet??n kohdepopulaatio FEV1 <-
# read.table('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/FEV.csv',
# header=TRUE, sep=',', na.strings='NA', dec='.',
# strip.white=TRUE)
# apu.pituudet<-round(2.54*FEV1$height[1:10],1)
# dump(objects(1,patt='apu.pituudet'),file='')

# T?m? laskee keskiarvon keskivirheen
fun.se <- function(x) sqrt(var(x)/length(x))

# Kohdejoukko
esim.pituus <- c(144.8, 171.4, 138.4, 134.6, 144.8, 154.9, 147.3,
    142.2, 148.6, 152.4)

set.seed(seed = 31121999)
apu.1 <- t(sapply(1:10, function(x) {
    lv.1 <- sample(10, 5)
    c(mean(esim.pituus[lv.1]), fun.se(esim.pituus[lv.1]))
}))

apu.1 <- cbind(apu.1, apu.1[, 1] + qnorm(0.025) * apu.1[, 2],
    apu.1[, 1] + qnorm(1 - 0.025) * apu.1[, 2])
colnames(apu.1) <- c("ka", "SE", "ala", "yla")
apu1.1.ulos <- cbind(format(apu.1[, 1], decimal.mark = ",", nsmall = 1,
    digits = 2), format(apu.1[, 1], decimal.mark = ",", nsmall = 1,
    digits = 2), paste0("(", format(apu.1[, 3], decimal.mark = ",",
    nsmall = 1, digits = 2), "-", format(apu.1[, 4], decimal.mark = ",",
    nsmall = 1, digits = 2), ")"))
colnames(apu1.1.ulos) <- c("ka", "SE", "95% LV")
# knitr::kable(apu.1,digits=1,caption='Kymmenen (N=5)
# otoksen keskiarvo (ka),keskivirhe (SE) ja 95\\%
# luottamusväli. Perusjoukon keskiarvo on
# 147,9.',format.args=list(decimal.mark =','))

knitr::kable(apu1.1.ulos, caption = "Kymmenen (N=5) otoksen keskiarvo (ka),keskivirhe (SE) ja 95\\% luottamusväli. Perusjoukon keskiarvo on 147,9.")
Kymmenen (N=5) otoksen keskiarvo (ka),keskivirhe (SE) ja 95% luottamusväli. Perusjoukon keskiarvo on 147,9.
ka SE 95% LV
151,4 151,4 (139,6-163,1)
145,0 145,0 (139,2-150,9)
143,0 143,0 (136,8-149,2)
149,6 149,6 (146,1-153,1)
152,4 152,4 (142,2-162,6)
147,6 147,6 (134,9-160,2)
146,3 146,3 (141,7-150,9)
149,1 149,1 (136,8-161,4)
147,6 147,6 (143,4-151,7)
146,5 146,5 (139,3-153,7)
# Taulukko 2 Kahden hapen annostelutavan vertailu
# tehohoidossa, kliininen koe jossa päätemuuttujana oli
# #kuolleisuus(2).
require(epiR)
require(epitools)
require(Epi)
# Girardis M, Busani S, Damiani E, et al. Effect of
# Conservative vs Conventional Oxygen Therapy on Mortality
# Among Pa-tients in an Intensive Care Unit: The Oxygen-ICU
# Random-ized Clinical Trial. JAMA Published Online First:
# 5 October 2016. doi:10.1001/jama.2016.11993

tmp.icu <- matrix(c(a = 25, b = 216 - 25, c = 44, d = 218 - 44),
    dimnames = list(c("Konservatiivinen", "Tavanomainen"), c("Kuollut",
        "Elossa")), ncol = 2, byrow = TRUE)
twoby2(tmp.icu)
## 2 by 2 table analysis: 
## ------------------------------------------------------ 
## Outcome   : Kuollut 
## Comparing : Konservatiivinen vs. Tavanomainen 
## 
##                  Kuollut Elossa    P(Kuollut) 95% conf.
## Konservatiivinen      25    191        0.1157    0.0794
## Tavanomainen          44    174        0.2018    0.1537
##                  interval
## Konservatiivinen   0.1657
## Tavanomainen       0.2604
## 
##                                     95% conf. interval
##              Relative Risk:  0.5734    0.3644   0.9024
##          Sample Odds Ratio:  0.5176    0.3040   0.8813
## Conditional MLE Odds Ratio:  0.5184    0.2909   0.9071
##     Probability difference: -0.0861   -0.1546  -0.0172
## 
##              Exact P-value: 0.0178 
##         Asymptotic P-value: 0.0153 
## ------------------------------------------------------
# knitr::kable(tmp.icu, caption='Kahden hapen
# annostelutavan vertailu tehohoidossa, kliininen koe jossa
# päätemuuttujana oli \\label{tab:icu}')

Hypoteesi, testaaminen ja P-arvo

require(ggplot2)
# FEV1 <-
# read.table('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/FEV.csv',
# header = TRUE, sep = ',', na.strings = 'NA', dec = '.',
# strip.white = TRUE)
FEV1 <- read.table(file = "./YhtData/FEV.csv", header = TRUE,
    sep = ",", na.strings = "NA", dec = ".", strip.white = TRUE)
# p <- ggplot(FEV1, aes(fev, colour=sex, fill=sex))+
# geom_density(alpha=0.55,show.legend =
# FALSE)+scale_fill_manual('SP',values=c('orange','red'))
p <- ggplot(subset(FEV1, age %in% c(7, 8)), aes((fev), colour = sex,
    fill = sex))
#+ guides(fill=guide_legend(name='SP'))
#+ 
p + labs(x = "FEV", y = "Tiheysfunktio") + geom_density(alpha = 0.35) +
    theme(legend.position = c(0.15, 0.7), axis.title = element_text(size = 8),
        legend.title = element_text(size = 8), legend.text = element_text(size = 8))
Tyttöjen ja poikien (ikä 7-8) spirometria mittausten jakauma.

Tyttöjen ja poikien (ikä 7-8) spirometria mittausten jakauma.

# with(subset(FEV1,age%in%c(7,8)),shapiro.test(fev))
t.test(fev ~ sex, data = subset(FEV1, age %in% c(7, 8)))
## 
##  Welch Two Sample t-test
## 
## data:  fev by sex
## t = 0.23033, df = 135.76, p-value = 0.8182
## alternative hypothesis: true difference in means between group female and group male is not equal to 0
## 95 percent confidence interval:
##  -0.1152517  0.1456375
## sample estimates:
## mean in group female   mean in group male 
##             2.026787             2.011594

Bayes-päättelystä ja P-arvoista

P-arvo ja nollahypoteesin todennäköisyys

P-arvoa ei voida suoraan tulkita todennäköisyydeksi, mutta sen avulla voidaan tietyin oletuksin laskea nollahypoteesiin todennäköisyys (Sellke, Bayarri, and Berger 2001; Goodman 2001; Haukka and Pitkäniemi 2015).

Ensin määritetään minimi Bayes-faktori (minimum Bayes factor, BF) P-arvolle (\(p\)), kaavalla \(\eqref{kaavaBF}\).

\[\begin{equation} BF(p) = \begin{cases} -e\cdot p\cdot log(p) & \text{, jos p<1/e eli p<0,3678794 } \\ 1 & \text{, muulloin } \end{cases} \label{kaavaBF} \end{equation}\]

BF:n avulla voidaan määrittää P-arvoa vastaava minimi posteriori todennäköisyys kaavalla \(\eqref{kaavaminpost}\), kun \(q\) on annettu priori vetosuhde.

\[\begin{equation} \text{minimi posteriori}=\left( 1+ \left(BF\cdot q\right)^{-1} \right)^{-1} \label{kaavaminpost} \end{equation}\]

Jos siis \(H_0\) ja \(H_1\) pidetään ennen tutkimuksen toteuttamista ja aineiston keräämistä yhtä todennäköisinä on priori vetosuhde 1 (\(\frac{0,5}{0,5}\)), jos taas oletetaan \(H_0\):n olevan 10 kertaa todennäköisempi kuin \(H_1\):n asetetaan priori vetosuhteeksi 10. Näin tutkija voi asettaa tarkan arvon omalle arviolleen.

Minimi posterioritodennäköisyys nollahypoteesille eri p-arvoilla (sarakkeet) ja eri priori vetokertoimille.
0,0500 0,0100 0,0050 0,0010 0,0001
1 0,2893 0,1113 0,0672 0,0184 0,0025
5 0,6706 0,3850 0,2647 0,0858 0,0124
10 0,8028 0,5559 0,4186 0,1581 0,0244
50 0,9532 0,8622 0,7826 0,4842 0,1113
100 0,9760 0,9260 0,8781 0,6525 0,2002

Taulukosta \(\ref{tab:MPPtaulu}\) havaitaan, että esim. P-arvoa 0,05 vastaava nollahypoteesin posterioritodennäköisyys on 0,2893 kun priori vetosuhde on 1. Toisín sanoen, jos pidämme ennen tutkimusta nollahypoteesia (\(H_0\)) ja vaihtoehtoa (\(H_1\)) yhtä todennäköisinä, niin havaittuamme p-arvon 0,05 on \(H_0\) todennäköisyys alentunut 28,9%:iin. Tämä tarkoittaa, että jos \(H_1\) olisi kliinisen tutkimuksen kohteena oleva uusi lääke, jota verrataan vanhaan (\(H_0\)), se olisi vain 71,1% (\(100\cdot(1-0,2893)\)) todennäköisyydellä vanhaa parempi. Tätä tuskin voidaan pitää kovin vahvana näyttönä \(H_0\) vastaan. Vastaavasti, jos havaitsemme p-arvon 0,005 on \(H_0\) todennäköisyys 6,7% eli uusi lääke olisi 93,2% todennäköisyydellä vanhaa parempi. Tässä on sovellettu 2-suuntaista testiä ja oletettu, että vaikutuksen suunta on odotettu.

Jos ennakko oletuksemme on hieman varovaisempi ja oletamme, että \(H_0\) on 10 kertaa todennäköisempi kuin \(H_01\) ja havaitaan p-arvo 0,005, on \(H_0\) todennäköisyys vielä varsin suuri eli 41,8%.

Näitä P-arvojen perusteella laskettuja todennäköisyyksiä kutsutaan kalibroiduiksi P-arvoiksi (calibrated p values).

# Esimerkki otoskoon laskemisesta Oletukset
muA = 4
muB = 10
kappa = 1
sd = 10
alpha = 0.05
beta = 0.2
# Otoskoon laskeminen
nB <- (1 + 1/kappa) * (sd * (qnorm(1 - alpha/2) + qnorm(1 - beta))/(muA -
    muB))^2
z = (muA - muB)/(sd * sqrt((1 + 1/kappa)/nB))
Power = pnorm(z - qnorm(1 - alpha/2)) + pnorm(-z - qnorm(1 -
    alpha/2))
cat("$\\mu_A$=", muA, " $\\mu_B$=", muB, " $\\kappa$=", kappa,
    " sd=", sd, "\n $\\alpha$=", alpha, " $\\beta$=", beta, " $n_B$=",
    ceiling(nB), " Voimakkuus=", round(Power, 3))
## $\mu_A$= 4  $\mu_B$= 10  $\kappa$= 1  sd= 10 
##  $\alpha$= 0.05  $\beta$= 0.2  $n_B$= 44  Voimakkuus= 0.8
pA = 0.05
pB = 0.1  # kahden ryhmän todennäköisyydet
kappa = 1  # molempinn ryhmiin sama lukumäärä
alpha = 0.05
beta = 0.2  # I-tyypin virhe; II-tyypin virhe
nB = (pA * (1 - pA)/kappa + pB * (1 - pB)) * ((qnorm(1 - alpha/2) +
    qnorm(1 - beta))/(pA - pB))^2
z = (pA - pB)/sqrt(pA * (1 - pA)/nB/kappa + pB * (1 - pB)/nB)
cat("Otoskoko=", ceiling(nB), "Voimakkuus=", round(pnorm(z -
    qnorm(1 - alpha/2)) + pnorm(-z - qnorm(1 - alpha/2)), 2),
    "Tyypin I virhe=", alpha, "\n")
## Otoskoko= 432 Voimakkuus= 0.8 Tyypin I virhe= 0.05

Regressioanalyysi

Iän ja spirometrimittauksen välinen yhteys, lineaarinen regressiosuora. Kuvioon on merkitty jäännökset pystyviivoin.

Iän ja spirometrimittauksen välinen yhteys, lineaarinen regressiosuora. Kuvioon on merkitty jäännökset pystyviivoin.

Mallin kertoimet ja 95% luottamusväli .
Estimate StdErr z P 2.5% 97.5%
(Intercept) 0,4316 0,0779 5,5414 0 0,2790 0,5843
age 0,2220 0,0075 29,5328 0 0,2073 0,2368
## 
## Call:
## lm(formula = fev ~ age + smoke, data = FEV1, x = TRUE)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.6653 -0.3564 -0.0508  0.3494  2.0894 
## 
## Coefficients:
##                         Estimate Std. Error t value
## (Intercept)             0.158378   0.131003   1.209
## age                     0.230605   0.008184  28.176
## smokenon-current smoker 0.208995   0.080745   2.588
##                         Pr(>|t|)    
## (Intercept)              0.22712    
## age                      < 2e-16 ***
## smokenon-current smoker  0.00986 ** 
## ---
## Signif. codes:  
## 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.5651 on 651 degrees of freedom
## Multiple R-squared:  0.5766, Adjusted R-squared:  0.5753 
## F-statistic: 443.3 on 2 and 651 DF,  p-value: < 2.2e-16
library(ggplot2)
# load(url('https://hbiostat.org/data/repo/nhgh.rda'))
# save(nhgh,file='nhgh.RData') load(file='nhgh.RData')
# nhgh$tx.f<-factor(nhgh$tx,levels=c(0,1),label=c('ei','on'))
# set.seed(11223344);nhgh.1<-nhgh[sample(seq(nrow(nhgh)),500),]#
# N=500 otos p1 <- ggplot(nhgh.1, aes(x = bmi, y = gh))
# p2<-p1+scale_colour_manual(name='Diabetes\nlääke', values
# =c('turquoise','red')) p3<-p2 +
# geom_point(aes(color=tx.f),alpha=0.6,size=1,show.legend=TRUE)
# p3
## 
## Call:
## lm(formula = gh ~ bmi * tx.f, data = nhgh.1)
## 
## Residuals:
## Glycohemoglobin [%] 
##     Min      1Q  Median      3Q     Max 
## -2.4609 -0.3155 -0.0563  0.2287  8.6371 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  4.991745   0.160979  31.009  < 2e-16 ***
## bmi          0.018353   0.005462   3.360 0.000839 ***
## tx.fon       5.350371   0.581411   9.202  < 2e-16 ***
## bmi:tx.fon  -0.096709   0.016976  -5.697 2.09e-08 ***
## ---
## Signif. codes:  
## 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8292 on 496 degrees of freedom
## Multiple R-squared:  0.3723, Adjusted R-squared:  0.3685 
## F-statistic: 98.08 on 3 and 496 DF,  p-value: < 2.2e-16
Painoindeksin (BMI) ja sokerihemoblobiinin (HbA1) ja välinen yhteys.

Painoindeksin (BMI) ja sokerihemoblobiinin (HbA1) ja välinen yhteys.

p1 <- ggplot(nhgh.1, aes(x = age, y = bmi)) + geom_point(aes(color = tx.f),
    alpha = 0.6, size = 0.7, show.legend = TRUE)
p1 + geom_smooth(formula = y ~ x + I(x^2), method = "lm") + labs(x = "Ikä (v)",
    y = "BMI", color = "DM lääke") + theme(legend.position = "right",
    legend.text = element_text(size = 6), legend.title = element_text(size = 6))
Painoindeksin (BMI) ja iän välinen yhteys.

Painoindeksin (BMI) ja iän välinen yhteys.

tmp.m4 <- lm(bmi ~ age + I(age^2), data = nhgh.1)
print(summary(tmp.m4))
## 
## Call:
## lm(formula = bmi ~ age + I(age^2), data = nhgh.1)
## 
## Residuals:
## Body Mass Index [kg/m^2] 
##     Min      1Q  Median      3Q     Max 
## -14.408  -4.720  -1.478   3.545  33.866 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 18.6948320  1.6100140  11.612  < 2e-16 ***
## age          0.4873982  0.0783838   6.218 1.07e-09 ***
## I(age^2)    -0.0047626  0.0008403  -5.668 2.46e-08 ***
## ---
## Signif. codes:  
## 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 7.035 on 497 degrees of freedom
## Multiple R-squared:  0.07981,    Adjusted R-squared:  0.07611 
## F-statistic: 21.55 on 2 and 497 DF,  p-value: 1.056e-09
Logistinen muunnos

Logistinen muunnos

## 
## Call:
## glm(formula = tx ~ sex + bmi, family = binomial(logit), data = nhgh)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -4.820080   0.175611  -27.45   <2e-16 ***
## sexfemale   -0.135595   0.086937   -1.56    0.119    
## bmi          0.086299   0.005364   16.09   <2e-16 ***
## ---
## Signif. codes:  
## 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 4168.8  on 6794  degrees of freedom
## Residual deviance: 3909.2  on 6792  degrees of freedom
## AIC: 3915.2
## 
## Number of Fisher Scoring iterations: 5
tmp.nd <- data.frame(bmi = seq(15, 40, by = 1))
tmp.nd$sex <- factor(rep(1, nrow(tmp.nd)), levels = c(1, 2),
    label = levels(nhgh$sex))
apu.pred <- predict(tmp.m.tx, newdata = tmp.nd, type = "response",
    se.fit = TRUE)
tmp.nd$ennuste <- apu.pred$fit
tmp.nd$lv.ala <- apu.pred$fit - (1.96 * apu.pred$se.fit)
tmp.nd$lv.yla <- apu.pred$fit + (1.96 * apu.pred$se.fit)

# with(tmp.nd,plot(bmi,100*ennuste,type='l',xlab='BMI',ylab='Diabetes
# lääke (\\%)',lwd=2,col='navy'))
# with(tmp.nd,lines(bmi,100*lv.ala,lwd=1,col='navy',lty=2))
# with(tmp.nd,lines(bmi,100*lv.yla,lwd=1,col='navy',lty=2))
# abline(v=c(25,35),h=100*tmp.nd$ennuste[tmp.nd$bmi%in%c(25,35)],lty=5)

p1 <- ggplot(tmp.nd, aes(x = bmi, y = 100 * ennuste)) + xlab("BMI") +
    ylab("Diabetes lääke (%)")
p1 + geom_line() + geom_line(aes(x = bmi, y = 100 * lv.ala),
    lty = 2) + geom_line(aes(x = bmi, y = 100 * lv.yla), lty = 2)
Logistisen mallin ennuste

Logistisen mallin ennuste

require(Epi)
tmp.or <- ci.exp(tmp.m.tx, subset = -1)
colnames(tmp.or) <- c("OR", "ala 95 pro", "ylä 95 pro")
# knitr::kable(tmp.or,digit=3,format.args
# =list(decimal.mark =',') , caption='Vetosuhteet ja 95\\%
# LV')

kbl(tmp.or, caption = "Vetosuhteet ja 95\\% LV", label = "vetosuhde1",
    booktabs = T, digits = 2, format.args = list(decimal.mark = ","))  #%>% 
Vetosuhteet ja 95% LV
OR ala 95 pro ylä 95 pro
sexfemale 0,87 0,74 1,04
bmi 1,09 1,08 1,10
# kable_styling() %>%column_spec(bold
# =TRUE,column=1)#%>%row_spec(bold =TRUE,row=1)
Riskisuhteen (RR) ja vetosuhteen (OR) yhteys prevalenssin eri arvoilla (eri viivatyypit)

Riskisuhteen (RR) ja vetosuhteen (OR) yhteys prevalenssin eri arvoilla (eri viivatyypit)

# library(sandwich) library(lmtest) library(Epi)

# Function that calculates OR
# mk.OR.poilog<-function(x,alpha=0.05) {
# lv.1<-qnorm(1-alpha/2)
# lv.2<-exp(cbind(x[,'Estimate'],x[,'Estimate']-x[,'Std.
# Error']*lv.1, x[,'Estimate']+x[,'Std. Error']*lv.1 ))
# colnames(lv.2)<-c('OR',paste(c('lo','hi'),1-alpha,sep='.'))
# lv.2 }


# Taulukon 3 aineisto
# zou.Tab3<-data.frame(Treat=factor(c(1,1,1,2,2,2),levels=c(2,1),label=c('Control','Diaspirin')),
# Baseline.risk=factor(c(1,2,3,1,2,3),levels=rev(1:3),label=c('Low','Med','High')),
# ndeath=c(12,5,5,6,1,1), total=c(13,13,23,10,12,22))

# Binomial with logistic link
# zou.m1<-glm(I(ndeath/total)~Treat+Baseline.risk,data=zou.Tab3,family=binomial,weights=total)
# ci.exp(zou.m1)


# Binomial with log link
# zou.m3<-glm(I(ndeath/total)~Treat+Baseline.risk,data=zou.Tab3,family=binomial(link='log'),weights=total)
# mk.OR.poilog(coeftest(zou.m3,vcov=vcovHC(zou.m2, type =
# 'HC0')))

# Muodostetaan yksilötason aineisto apu<-NULL;apu1<-NULL;
# apu2<-NULL for(tmp.i in seq(nrow(zou.Tab3))){
# apu<-c(apu,rep(c(1,0),c(zou.Tab3$ndeath[tmp.i],zou.Tab3$total[tmp.i]-zou.Tab3$ndeath[tmp.i])))
# apu1<-c(apu1,rep(zou.Tab3$Treat[tmp.i],zou.Tab3$total[tmp.i]))
# apu2<-c(apu2,rep(zou.Tab3$Baseline.risk[tmp.i],zou.Tab3$total[tmp.i]))
# }
# zou.Tab3.ind<-data.frame(Treat=apu1,Baseline.risk=apu2,death=apu)
# attributes(zou.Tab3.ind$Treat)<-attributes(zou.Tab3$Treat)
# attributes(zou.Tab3.ind$Baseline.risk)<-attributes(zou.Tab3$Baseline.risk)

# Mallitetaan yksilötason dataa
# zou.m2.ind<-glm(death~Treat+Baseline.risk,data=zou.Tab3.ind,family=poisson)
# mk.OR.poilog(coeftest(zou.m2.ind,vcov=vcovHC(zou.m2.ind,
# type = 'HC0')))
library(survival)
# Coxin suhteellisen hasardin malli
tmp.m1.cox <- coxph(Surv(time, status) ~ age + factor(sex), data = lung)
summary(tmp.m1.cox)
## Call:
## coxph(formula = Surv(time, status) ~ age + factor(sex), data = lung)
## 
##   n= 228, number of events= 165 
## 
##                   coef exp(coef)  se(coef)      z Pr(>|z|)
## age           0.017045  1.017191  0.009223  1.848  0.06459
## factor(sex)2 -0.513219  0.598566  0.167458 -3.065  0.00218
##                
## age          . 
## factor(sex)2 **
## ---
## Signif. codes:  
## 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##              exp(coef) exp(-coef) lower .95 upper .95
## age             1.0172     0.9831    0.9990    1.0357
## factor(sex)2    0.5986     1.6707    0.4311    0.8311
## 
## Concordance= 0.603  (se = 0.025 )
## Likelihood ratio test= 14.12  on 2 df,   p=9e-04
## Wald test            = 13.47  on 2 df,   p=0.001
## Score (logrank) test = 13.72  on 2 df,   p=0.001
# Coxin mallin suhteellisuusoletuksen testaaminen
cox.zph(tmp.m1.cox)
##             chisq df    p
## age         0.209  1 0.65
## factor(sex) 2.608  1 0.11
## GLOBAL      2.771  2 0.25
# print(knitr::kable(summary(tmp.m1.cox)$conf.int,caption='Hasardien
# suhde (HR, exp(coef)) ja 95\\%luottamusväli Coxin
# mallista.',digits=3))

kbl(summary(tmp.m1.cox)$conf.int, caption = "Hasardien suhde (HR, exp(coef)) ja 95\\% luottamusväli Coxin mallista.",
    booktabs = T, digits = 2, format.args = list(decimal.mark = ",")) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)  #%>%row_spec(bold =TRUE,row=1)
Hasardien suhde (HR, exp(coef)) ja 95% luottamusväli Coxin mallista.
exp(coef) exp(-coef) lower .95 upper .95
age 1,02 0,98 1,00 1,04
factor(sex)2 0,60 1,67 0,43 0,83
library(cohorttools)
# Tapahtumataulun laskeminen
lung.LT <- mkratetable(Surv(time, status) ~ sex, data = lung,
    scale = 365.25)
# knitr::kable(lung.LT,
#              digit=c(2,2,0,2,2,2),
#              caption="Tapahtumataulu keuhkosyöpäaineistosta sukupuolen suhteen.\\label{tab:lungLT}")

kbl(lung.LT, caption = "Tapahtumataulu keuhkosyöpäaineistosta sukupuolen suhteen.",
    label="lungLT", booktabs = T,digit=c(2,2,0,2,2,2),
    format.args = list(decimal.mark =",")) %>% 
  kable_styling() %>%column_spec(bold =TRUE,column=1)%>% #%>%row_spec(bold =TRUE,row=1)
footnote(general ="",
           general_title="",
           number=c("sex=sukupuoli","pyears=kertyneet henkilövuodet","event=tapahtumen lukumäärä","rate=ilmaantuvuus",
                    "low, high = 95% LV ala- ja yläraja"),
           footnote_order=c("number","general")
           )
Tapahtumataulu keuhkosyöpäaineistosta sukupuolen suhteen.
sex pyears event rate low high
1 107,01 112 1,05 0,86 1,26
2 83,52 53 0,63 0,48 0,83
1 sex=sukupuoli
2 pyears=kertyneet henkilövuodet
3 event=tapahtumen lukumäärä
4 rate=ilmaantuvuus
5 low, high = 95% LV ala- ja yläraja
tmp.m1.poi <- glm(ifelse(status == 2, 1, 0) ~ factor(sex) + offset(log(time)),
    data = lung, family = poisson)
summary(tmp.m1.poi)
## 
## Call:
## glm(formula = ifelse(status == 2, 1, 0) ~ factor(sex) + offset(log(time)), 
##     family = poisson, data = lung)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -5.85502    0.09449 -61.965  < 2e-16 ***
## factor(sex)2 -0.50040    0.16672  -3.001  0.00269 ** 
## ---
## Signif. codes:  
## 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 246.22  on 227  degrees of freedom
## Residual deviance: 236.74  on 226  degrees of freedom
## AIC: 570.74
## 
## Number of Fisher Scoring iterations: 5
# Lasketaan IRR ja luottamusvälit
ci.exp(tmp.m1.poi, subset = -1)
##              exp(Est.)      2.5%  97.5%
## factor(sex)2 0.6062888 0.4372902 0.8406
require(survival)
require(ggplot2)
require(survminer)
load(file = "TADA11.RData")
TADA.11.54 <- TADA.11[TADA.11$stater == "54", ]
# Select 54 — Virginia for practicals
# TADA.11.54<-TADA.11[TADA.11$stater=='54',]
# survfit.54.tenure <- survfit(Surv(follow/365.25,
# inddea=='1') ~tenure , data=TADA.11.54)
# p1<-ggsurvplot(survfit.54.tenure, # survfit object with
# calculated statistics.  data = TADA.11.54, # data used to
# fit survival curves.  risk.table = TRUE, # show risk
# table.  pval = TRUE, # show p-value of log-rank test.
# conf.int = TRUE, # show confidence intervals for
# ylim=c(0.85,1), # point estimaes of survival curves.  #
# xlim = c(0,20), # present narrower X axis, but not affect
# # survival estimates.  break.time.by = 5, # break X axis
# in time intervals by 2 censor=FALSE,
# pval.coord=c(1,0.93),pval.size=6, linetype = 'strata',
# palette = 'grey', legend=c(0.3,0.3), legend.title =
# 'Asunnon hallinta', # legend.labs = c('no','yes'),
# ggtheme = theme_classic(), # customize plot and risk
# table with a theme.  risk.table.fontsize=5,
# risk.table.y.text.col = T, # colour risk table text
# annotations.  risk.table.y.text = FALSE, # show bars
# instead of names in text annotations fontsize=5,font.x =
# 5,font.tickslabs=12,font.legend=12, font.title=4,
# font.subtitle=4, font.caption=4, ylab='Elossa olevien
# osuus', title=' ')

# print(p1) library(export)
# graph2tif(x=p1$plot,file='./YhtKuvat/kmtenure.tiff',
# aspectr=1, font = 'Arial',width = 3.4, bg =
# 'transparent')
# graph2eps(x=p1$plot,file='./YhtKuvat/kmtenure.eps',
# aspectr=sqrt(2), font = 'Arial',width = 3.4, bg =
# 'transparent')

# png(file='./YhtKuvat/kmtenure.png', width=sqrt(2)*9*72*7,
# height=9*72*7,res=600) print(p1) dev.off()


# surviplot(Surv(follow/365.25, inddea=='1') ~tenure ,
# data=TADA.11.54, lty = 1:3, main = 'Patients stratified
# by treatment', xlab = 'Time (Years)')

# p1<-ggsurvplot(survfit.54.tenure, data =
# TADA.11.54,ylim=c(0.85,1), palette = 'grey', size = 1, #
# change line size conf.int = TRUE, # Add confidence
# interval pval = TRUE, # Add p-value risk.table = TRUE, #
# Add risk table risk.table.col = 'strata',# Risk table
# color by groups risk.table.height = 0.35, # Useful to
# change when you have multiple groups
# legend='top',legend.labs=levels(TADA.11.54$tenure),legend.title='Tenure',
# # fontsize=3,font.x = 4,font.tickslabs=8,font.legend=8, #
# font.title=4, font.subtitle=4, font.caption=4,
# risk.table.title='', xlab='',ylab='' #ggtheme =
# theme_bw() # Change ggplot2 theme )

# png(filename ='kmtenure.png',width = 1500,height =
# 1500/sqrt(2),pointsize = 60);print(p1);dev.off()
# knitr::include_graphics('./YhtKuvat/kmtenure.png',)
library(stargazer)
load("./YhtData/TADA.11.54.RData")
TADA.54.m0 <- glm(as.numeric(inddea) ~ tenure + offset(log(follow/365.25)),
    family = poisson, data = TADA.11.54)
TADA.54.m1 <- update(TADA.54.m0, ~. + age)
# stargazer::stargazer(TADA.54.m0,TADA.54.m1,type='text',ci=TRUE,apply.coef=exp,
# dep.var.caption='IRR ja 95% luottamusväli.')
# stargazer::stargazer(TADA.54.m0,TADA.54.m1,type=ifelse(knitr::is_html_output(),'html','latex'),ci=TRUE,apply.coef=exp,
# dep.var.caption='IRR ja 95% luottamusväli.')
# print(TADA.54.m0) print(TADA.54.m1)
library(kableExtra)
tmp.tab <- rbind(ci.exp(TADA.54.m0, subset = "tenure"), ci.exp(TADA.54.m1,
    subset = "tenure"))
colnames(tmp.tab) <- c("MRR", "ala", "yla")
tmp.tab <- data.frame(Malli = c("Malli 1", rep("", 2), "Malli 2",
    rep("", 2)), Var = rownames(tmp.tab), tmp.tab)
rownames(tmp.tab) <- NULL

kbl(tmp.tab, digits = 2, format.args = list(decimal.mark = ","),
    caption = "Kuolleisuuksien suhteen estimaatit ja 95\\% LV kahdesta mallista. Malli 1 vain yksi muuttuja, Malli 2 ikä korjattu. Vertailutasona 'Owned'",
    label = "poissonmallit1ja2") %>%
    kable_classic()  #%>%
Kuolleisuuksien suhteen estimaatit ja 95% LV kahdesta mallista. Malli 1 vain yksi muuttuja, Malli 2 ikä korjattu. Vertailutasona ‘Owned’
Malli Var MRR ala yla
Malli 1 tenureRented 0,62 0,57 0,69
tenureRented for noncash 1,33 1,04 1,71
tenureUnknown 0,27 0,16 0,45
Malli 2 tenureRented 1,29 1,17 1,43
tenureRented for noncash 1,38 1,08 1,77
tenureUnknown 0,94 0,57 1,56
# add_header_above(c(' ' = 1, 'Malli 1' = 3, 'Malli 2' =
# 3))
library(haven)
load(file = "YhtData/divorce.RData")

tmp.m.div <- glm(div ~ heduc + mixed + heblack + offset(log(years)),
    family = poisson, data = divorce)

tmp.tab <- rbind(ci.exp(tmp.m.div, subset = -1))
colnames(tmp.tab) <- c("IRR", "ala", "yla")
tmp.tab <- data.frame(Var = rownames(tmp.tab), tmp.tab)
rownames(tmp.tab) <- NULL
rownames(tmp.tab) <- c("heduc 12-15 years vs. < 12 years", "heduc 16+ years vs. < 12 years",
    "mixed Yes vs. No", "heblack Yes vs. No")

kbl(tmp.tab[, -1], digits = 2, format.args = list(decimal.mark = ","),
    caption = "Avioeron ilmaantuvuuden suhde (IRR) yhdysvaltalaisen aineiston mukaan. Avioliitot jaoteltu miehen koulutuksen (heduc), puolisoiden etnisen taustan mukaan (mixed) (sama=No, eri=Yes) ja aviomiehen ihonvärin mukaan (heblack). ",
    label = "divorcemalli") %>%
    kable_classic()
Avioeron ilmaantuvuuden suhde (IRR) yhdysvaltalaisen aineiston mukaan. Avioliitot jaoteltu miehen koulutuksen (heduc), puolisoiden etnisen taustan mukaan (mixed) (sama=No, eri=Yes) ja aviomiehen ihonvärin mukaan (heblack).
IRR ala yla
heduc 12-15 years vs. < 12 years 1,51 1,32 1,72
heduc 16+ years vs. < 12 years 1,15 0,93 1,43
mixed Yes vs. No 1,32 1,13 1,55
heblack Yes vs. No 1,31 1,12 1,53
# Coxin malli
tmp.m0.cox <- coxph(Surv(time, status) ~ factor(sex), data = lung)
summary(tmp.m0.cox)$conf.int
##              exp(coef) exp(-coef) lower .95 upper .95
## factor(sex)2 0.5880028   1.700672 0.4237178 0.8159848
# Poisson regressio
ci.exp(tmp.m1.poi, subset = -1)
##              exp(Est.)      2.5%  97.5%
## factor(sex)2 0.6062888 0.4372902 0.8406
# NOTE! you must give starting values for parameters
# 'start=c(0.1,0.1)'' Lasketaan vuotta kohti, alunperin oli
# päiviä
tmp.m1.poi.A <- glm(I(ifelse(status == 2, 1, 0)/(time/365.25)) ~
    factor(sex), data = lung, weight = (time/365.25), start = c(0.1,
    0.1), family = poisson("identity"))
ci.lin(tmp.m1.poi.A, subset = -1)
##                Estimate    StdErr         z           P
## factor(sex)2 -0.4120641 0.1318243 -3.125858 0.001772873
##                   2.5%      97.5%
## factor(sex)2 -0.670435 -0.1536931
# Tapahtumataulu tmp.tab.1
require(MASS)
head(as.data.frame(housing), 3)
##      Sat Infl  Type Cont Freq
## 1    Low  Low Tower  Low   21
## 2 Medium  Low Tower  Low   21
## 3   High  Low Tower  Low   28
summary(housing)
##      Sat         Infl           Type      Cont   
##  Low   :24   Low   :24   Tower    :18   Low :36  
##  Medium:24   Medium:24   Apartment:18   High:36  
##  High  :24   High  :24   Atrium   :18            
##                          Terrace  :18            
##                                                  
##                                                  
##       Freq      
##  Min.   : 3.00  
##  1st Qu.:10.00  
##  Median :19.50  
##  Mean   :23.35  
##  3rd Qu.:31.75  
##  Max.   :86.00
options(contrasts = c("contr.treatment", "contr.poly"))
house.plr <- polr(Sat ~ Infl + Type + Cont, weights = Freq, data = housing)
summary(house.plr, digits = 3)
## Call:
## polr(formula = Sat ~ Infl + Type + Cont, data = housing, weights = Freq)
## 
## Coefficients:
##                Value Std. Error t value
## InflMedium     0.566     0.1047    5.41
## InflHigh       1.289     0.1272   10.14
## TypeApartment -0.572     0.1192   -4.80
## TypeAtrium    -0.366     0.1552   -2.36
## TypeTerrace   -1.091     0.1515   -7.20
## ContHigh       0.360     0.0955    3.77
## 
## Intercepts:
##             Value  Std. Error t value
## Low|Medium  -0.496  0.125     -3.974 
## Medium|High  0.691  0.125      5.505 
## 
## Residual Deviance: 3479.149 
## AIC: 3495.149
house.plr <- update(house.plr, Hess = TRUE)
pr <- profile(house.plr)
house.plr.OR <- exp(cbind(house.plr$coefficients, confint(pr)))
colnames(house.plr.OR)[1] <- "OR"
kbl(house.plr.OR, digits = 3, caption = "Vetosuhteet ja 95\\% luottamusvälit asumisviihtyvyyden mallista") %>%
    kable_classic() %>%
    column_spec(bold = TRUE, column = 1)
Vetosuhteet ja 95% luottamusvälit asumisviihtyvyyden mallista
OR 2.5 % 97.5 %
InflMedium 1.762 1.436 2.164
InflHigh 3.628 2.832 4.663
TypeApartment 0.564 0.446 0.712
TypeAtrium 0.693 0.511 0.940
TypeTerrace 0.336 0.249 0.451
ContHigh 1.434 1.189 1.730
require(MASS)
require(nnet)
require(foreign)
# oc <- options(contrasts = c('contr.treatment',
# 'contr.poly')) # ml <-
# read.dta('https://stats.idre.ucla.edu/stat/data/hsbdemo.dta')
# # save(ml,file='./yhtData/ml.RData')
# load(file='./yhtData/ml.RData') ml$prog2 <-
# relevel(ml$prog, ref = 'academic')
load(file = "./YhtData/crash2dt.RData")
crash2.dt$btransf <- factor(crash2.dt$btransf)
mn.malli <- multinom(condition ~ injurytype + sex + age + btransf,
    data = crash2.dt)
summary(mn.malli)
tmp.tb <- with(crash2.dt, table(condition, injurytype))

mn.malli.OR <- exp(coef(mn.malli))
apu.coef <- coef(mn.malli)
apu.se <- summary(mn.malli)$standard.errors
apu1 <- exp(apu.coef)
apu2 <- exp(apu.coef + qnorm(0.025) * apu.se)
apu3 <- exp(apu.coef + qnorm(1 - 0.025) * apu.se)
#
apu4 <- paste0(format(apu1[, -1], digits = 2), " (", format(apu2[,
    -1], digits = 2), "-", format(apu3[, -1], digits = 2), ")")
#
apu5 <- matrix(apu4, nrow = length(unique(crash2.dt$condition)) -
    1)
#
rownames(apu5) <- rownames(apu1)
colnames(apu5) <- colnames(apu1)[-1]
colnames(apu5) <- gsub(x = colnames(apu5), pattern = "injurytype|sex",
    replacement = "")
kbl(apu5, digits = 2, format.args = list(decimal.mark = ","),
    booktabs = TRUE, format = "html", caption = "Vetosuhteet ja 95\\% luottamusvälit multinomiregressio mallista",
    ) %>%
    kable_styling() %>%
    column_spec(bold = TRUE, column = 1)

# %>%kable_classic() %>%column_spec(bold =TRUE,column=1)


# mn.malli.OR<-exp(coef(mn.malli)) apu.coef<-coef(mn.malli)
# apu.se<-summary(mn.malli)$standard.errors
# apu1<-exp(apu.coef)
# apu2<-exp(apu.coef+qnorm(0.025)*apu.se)
# apu3<-exp(apu.coef+qnorm(1-0.025)*apu.se)
# apu4<-paste0(format(apu1[,-1],digits = 2),' (',
# format(apu2[,-1],digits = 2),'-', format(apu3[,-1],digits
# = 2),')') apu5<-matrix(apu4,nrow=2)
# rownames(apu5)<-rownames(apu1)
# colnames(apu5)<-colnames(apu1)[-1] kbl(apu5,digits = 2,
# caption = 'Vetosuhteet ja 95\\% luottamusvälit
# multinomiregressio mallista') %>%kable_classic()
# %>%column_spec(bold =TRUE,column=1)
Davies, Neil M., Michael V. Holmes, and George Davey Smith. 2018. “Reading Mendelian Randomisation Studies: A Guide, Glossary, and Checklist for Clinicians.” BMJ 362 (July): k601. https://doi.org/10.1136/bmj.k601.
Goodman, S N. 2001. Of P-Values and Bayes: A Modest Proposal.” Epidemiology 12 (3): 295–97.
Haukka, Jari, and Janne Pitkäniemi. 2015. “Enemmän Irti p-Arvosta.” Suomen Lääkärilehti 70 (10): 665–68.
Sellke, Thomas, M. J. Bayarri, and James O. Berger. 2001. “Calibration of P Values for Testing Precise Null Hypotheses.” The American Statistician 55 (1): 62. http://www.questia.com/library/journal/1G1-71202492/calibration-of-p-values-for-testing-precise-null-hypotheses.
LS0tCnRpdGxlOiAiRXBpZGVtaW9sb2dpYW4gYWxrZWV0IgphdXRob3I6ICJKYXJpIEhhdWtrYSIKZGF0ZTogJ2ByIFN5cy5EYXRlKClgJwpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogImhpZGUiCiAgICB0b2M6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKICAgIHByZXR0eWRvYzo6aHRtbF9wcmV0dHk6CiAgICAgIHRoZW1lOiBhcmNoaXRlY3QKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCmJpYmxpb2dyYXBoeToKICAtIEVwaUtpcmphblZpaXR0ZWV0LmJpYgogIC0gY3NsOiBibWouY3NsCi0tLQogIApgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSxyZXN1bHRzPSdoaWRlJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodHVmdGUpCiMgdGhlbWU6IHVuaXRlZAojIGludmFsaWRhdGUgY2FjaGUgd2hlbiB0aGUgdHVmdGUgdmVyc2lvbiBjaGFuZ2VzCmtuaXRyOjpvcHRzX2NodW5rJHNldCh0aWR5ID0gVFJVRSwgY2FjaGUuZXh0cmEgPSBwYWNrYWdlVmVyc2lvbigndHVmdGUnKSkKCgpvcHRpb25zKGh0bWx0b29scy5kaXIudmVyc2lvbiA9IEZBTFNFKQpzb3VyY2UoZmlsZT0iRnVua3Rpb2l0YS5SIixlY2hvID0gVFJVRSkKbGlicmFyeShFcGkpO2xpYnJhcnkoZXBpdG9vbHMpO2xpYnJhcnkoZXBpUikKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGhhdmVuKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocHJldHR5ZG9jKQoKbGlicmFyeShnZ3NjaSkKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ2JiYy9iYnBsb3QnKQpsaWJyYXJ5KGJicGxvdCkKbGlicmFyeShocmJydGhlbWVzKQpsaWJyYXJ5KHRhYmxlb25lKQpsaWJyYXJ5KG1hZ3JpdHRyKQoKIyBTZXQgZ2xvYmFsIG9wdGlvbnMKIyBvcHRpb25zKAojICAgZ2dwbG90Mi5kaXNjcmV0ZS5jb2xvdXIgPSBnZ3NjaTo6c2NhbGVfY29sb3VyX2QzLAojICAgZ2dwbG90Mi5kaXNjcmV0ZS5maWxsID0gZ2dzY2k6OnNjYWxlX2ZpbGxfZDMKIyApCgpvcHRpb25zKAogIGdncGxvdDIuZGlzY3JldGUuY29sb3VyID0gZ2dzY2k6OnNjYWxlX2NvbG91cl9uZWptLAogIGdncGxvdDIuZGlzY3JldGUuZmlsbCA9IGdnc2NpOjpzY2FsZV9maWxsX25lam0KKQoKIyBvcHRpb25zKAojICAgZ2dwbG90Mi5kaXNjcmV0ZS5jb2xvdXIgPSBOVUxMLAojICAgZ2dwbG90Mi5kaXNjcmV0ZS5maWxsID0gTlVMTAojICkKCgpgYGAKCiMgSm9oZGFudG8KClTDpG3DpCBkb2t1bWVudHRpIHNpc8OkbHTDpMOkICJFcGlkZW1pb2xvZ2lhbiBhbGtlZXQiLWtpcmphbiBzaXPDpGx0w6Rtw6R0IFItb2hqZWxtYW4ga29vZGl0IGphIG5paWRlbiB0dW90dGFtYXQgdHVsb2tzZXQuCgojIE1pdHRhYW1pbmVuCgogCmBgYHtyIEt1dmExTHVrdTMsIGVjaG8gPSBUUlVFLCBmaWcubWFyZ2luID0gVFJVRSxmaWcuY2FwID0gJ0Jpbm9taXRvZGVubsOka8O2aXN5eWRldCBrdW4gcD0wLDE4IGphIG90b3Nrb2tvIDEwJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoRXBpKQoKIyBCaW5vbWlhbAp0bXAueDwtMDoxMAp0bXAueTwtZGJpbm9tKHRtcC54LCBzaXplPTEwLCBwcm9iPS4xOCkKcGxvdCh0bXAueCwxMDAqdG1wLnksdHlwZT0iaCIsbHdkPTgsCiAgICAgeGxhYj0iTHVrdW3DpMOkcsOkIix5bGFiPSJUb2Rlbm7DpGvDtmlzeXlzICglKSIpCnBhcihtYXI9Yyg0LDQsMiwxKSkKcGxvdCh0bXAueCwxMDAqdG1wLnksdHlwZT0iaCIsbHdkPTgsCiAgICAgeGxhYj0iTHVrdW3DpMOkcsOkIix5bGFiPSdUb2Rlbm7DpGvDtmlzeXlzICglKScpCgpgYGAKCgoKYGBge3IgLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPSdhc2lzJyxlY2hvID0gVFJVRX0KI3JlcXVpcmUoenRhYmxlKQojIG9wdGlvbnMoenRhYmxlLnR5cGU9aWZlbHNlKGtuaXRyOjo6aXNfaHRtbF9vdXRwdXQoKSwiaHRtbCIsImxhdGV4IikpCiMgcHJpbnQob3B0aW9ucygpJHp0YWJsZS50eXBlKQpsaWJyYXJ5KG1hZ3JpdHRyKQpyZXF1aXJlKHN0YXJnYXplcikgICAKbGlicmFyeShrYWJsZUV4dHJhKQpsb2FkKGZpbGU9Im5oZ2hWMi5SRGF0YSIpCklrw6Q8LXdpdGgobmhnaCxjdXQoYWdlLGMoMCwzMCw1MCxJbmYpKSkKCnRibCA8LSB3aXRoKG5oZ2gsYWRkbWFyZ2lucyh0YWJsZShJa8OkLGR4LmYpKSkKY2xhc3ModGJsKTwtIm1hdHJpeCIKIyBzdGFyZ2F6ZXIodGJsLCBzdW1tYXJ5PUZBTFNFLCByb3duYW1lcz1UUlVFLGhlYWRlciA9IEZBTFNFLHRpdGxlPSJOSEFORVMgdHV0a2ltdXMuIERpYWJldGVzIChlaS9vbilqYSBpa8OkIHZ1b3Npc3NhLiIsbGFiZWw9Ik5IQU5FU3BlcnVzIikKI3RibDwtenRhYmxlKHRibCkKa2JsKHRibCxjYXB0aW9uPSJOSEFORVMgdHV0a2ltdXMuIERpYWJldGVzIChlaS9vbilqYSBpa8OkIHZ1b3Npc3NhLiIsbGFiZWw9Ik5IQU5FU3BlcnVzIiwgYm9va3RhYnMgPSBULGRpZ2l0cz0yLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpICU+JSAKICBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkKCgojcHJpbnQodXBkYXRlX3p0YWJsZSh0YmwsZGlnaXRzPTApLGNhcHRpb249Iklrw6QgamEgZGlhYmV0ZXMuIix0eXBlPW9wdGlvbnMoKSR6dGFibGUudHlwZSkKI2h0bWxUYWJsZSh0Ymwscm5hbWVzPWxldmVscyhuaGdoJHJlKSwpCmBgYAoKCiAKYGBge3IgbW9zYWlpa2tpLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPSdhc2lzJyxlY2hvID0gVFJVRSxmaWcuY2FwPSJNb3NhaWlra2lrdXZpbyBmcmVrdmVuc3NpdGF1bHVzdGEuIn0KbGlicmFyeSh2Y2QpCm1vc2FpYyh3aXRoKG5oZ2gsdGFibGUoc2V4LElrw6QsZHguZikpLCBzaGFkZT1UUlVFLCBsZWdlbmQ9VFJVRSxjZXg9NDUpCgojIG1vc2FpY3Bsb3QofiBzZXgrSWvDpCtkeC5mLCBkYXRhID0gbmhnaCwgY29sb3IgPSBUUlVFLCBsYXMgPSAxKQpgYGAKIApgYGB7ciBNUDEsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLHJlc3VsdHM9J2FzaXMnLGVjaG8gPSBUUlVFfQpsb2FkKGZpbGU9IllodERhdGEvZGFGMzc1Mi5SRGF0YSIpCnRibCA8LSB3aXRoKGRhRjM3NTIsYWRkbWFyZ2lucyh0YWJsZShidjEsTVA0KSkpCmNsYXNzKHRibCk8LSJtYXRyaXgiCiMgc3RhcmdhemVyKHRibCwgc3VtbWFyeT1GQUxTRSwgcm93bmFtZXM9VFJVRSxoZWFkZXIgPSBGQUxTRSx0aXRsZT0iVHVydmFsbGlzdXVzcG9saXRpaWtrYS0gamEgbWFhbnB1b2x1c3R1c3R1dGtpbXVzIDIwMjIuIFZhc3RhYWphbiBhbW1hdHRpL2FzZW1hIGphIHZhc3RhdXMgJ0pvcyBTdW9tZWVuIGh5w7Zrw6R0w6TDpG4sIG9saXNpdHRla28gaXRzZSB2YWxtaXMgb3NhbGxpc3R1bWFhbiBtYWFucHVvbHVzdHVrc2VuCiMgIGVyaSB0ZWh0w6R2aWluIGt5a3lqZW5uZSBqYSB0YWl0b2plbm5lIG11a2Fhbj8nIixsYWJlbD0iTVAxVGFiIikKCmtibCh0YmwsIGNhcHRpb24gPSAiVHVydmFsbGlzdXVzcG9saXRpaWtrYS0gamEgbWFhbnB1b2x1c3R1c3R1dGtpbXVzIDIwMjIuIFZhc3RhYWphbiBhbW1hdHRpL2FzZW1hIGphIHZhc3RhdXMgJ0pvcyBTdW9tZWVuIGh5w7Zrw6R0w6TDpG4sIG9saXNpdHRla28gaXRzZSB2YWxtaXMgb3NhbGxpc3R1bWFhbiBtYWFucHVvbHVzdHVrc2VuCiBlcmkgdGVodMOkdmlpbiBreWt5amVubmUgamEgdGFpdG9qZW5uZSBtdWthYW4/JyIsIGxhYmVsPSJNUDFUYWIiLAogYm9va3RhYnMgPSBULGRpZ2l0cz0yLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpICU+JSAKICBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkKCgpgYGAKIAogCiAKYGBge3IgLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPSdhc2lzJyxlY2hvID0gVFJVRX0KIAp0YmwgPC0gd2l0aChkYUYzNzUyLGFkZG1hcmdpbnModGFibGUoTVAxMl8xLGJ2MSkpKQojIGNsYXNzKHRibCk8LSJtYXRyaXgiCiMgc3RhcmdhemVyKHRibCwgc3VtbWFyeT1GQUxTRSwgcm93bmFtZXM9VFJVRSxoZWFkZXIgPSBGQUxTRSx0aXRsZT0iVHVydmFsbGlzdXVzcG9saXRpaWtrYS0gamEgbWFhbnB1b2x1c3R1c3R1dGtpbXVzIDIwMjIuIFZhc3RhYWphbiBhc3VpbnBhaWtrYSBqYSB2YXN0YXVzICdNaXRlbiBoeXZpbiBtaWVsZXN0w6RubmUgU3VvbWVzc2Egb24gdmFyYXVkdXR0dSBzZXVyYWF2aWVuIHR1cnZhbGxpc3V1c3Voa2llbiB0b3JqdW50YWFuPzogQXNlZWxsaW5lbiBoecO2a2vDpHlzICciLGxhYmVsPSJNUDEyMVRhYiIpCgprYmwodGJsLCBjYXB0aW9uPSJUdXJ2YWxsaXN1dXNwb2xpdGlpa2thLSBqYSBtYWFucHVvbHVzdHVzdHV0a2ltdXMgMjAyMi4gVmFzdGFhamFuIGFzdWlucGFpa2thIGphIHZhc3RhdXMgJ01pdGVuIGh5dmluIG1pZWxlc3TDpG5uZSBTdW9tZXNzYSBvbiB2YXJhdWR1dHR1IHNldXJhYXZpZW4gdHVydmFsbGlzdXVzdWhraWVuIHRvcmp1bnRhYW4/OiBBc2VlbGxpbmVuIGh5w7Zra8OkeXMgJyIsCiAgICBsYWJlbD0iTVAxMjFUYWIiLCBib29rdGFicyA9IFQsZGlnaXRzPTIsZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9IiwiKSkgJT4lIAogIGthYmxlX3N0eWxpbmcoKSAlPiVjb2x1bW5fc3BlYyhib2xkID1UUlVFLGNvbHVtbj0xKQoKYGBgCgogCmBgYHtyIEt1dmEzTHVrdTMsIGVjaG8gPSBUUlVFLCBmaWcubWFyZ2luID0gVFJVRSxmaWcud2lkdGg9My41LCBmaWcuaGVpZ2h0PTMuNSxmaWcuY2FwID0gJ05vcm1hYWxpamFrYXVtaWEsIHZhcy4gb2RvdHVzYXJ2byAtMSBqYSBoYWpvbnRhIDAsNTsgb2lrLiAwIGphIDEuJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CiMgVHJhbnNwYXJlbnQgY29sb3JzCmFkZFRyYW5zIDwtIGZ1bmN0aW9uKGNvbG9yLHRyYW5zKQp7CiAgIyBUaGlzIGZ1bmN0aW9uIGFkZHMgdHJhbnNwYXJhbmN5IHRvIGEgY29sb3IuCiAgIyBEZWZpbmUgdHJhbnNwYXJhbmN5IHdpdGggYW4gaW50ZWdlciBiZXR3ZWVuIDAgYW5kIDI1NQogICMgMCBiZWluZyBmdWxseSB0cmFuc3BhcmFudCBhbmQgMjU1IGJlaW5nIGZ1bGx5IHZpc2FibGUKICAjIFdvcmtzIHdpdGggZWl0aGVyIGNvbG9yIGFuZCB0cmFucyBhIHZlY3RvciBvZiBlcXVhbCBsZW5ndGgsCiAgIyBvciBvbmUgb2YgdGhlIHR3byBvZiBsZW5ndGggMS4KICAKICBpZiAobGVuZ3RoKGNvbG9yKSE9bGVuZ3RoKHRyYW5zKSYhYW55KGMobGVuZ3RoKGNvbG9yKSxsZW5ndGgodHJhbnMpKT09MSkpIHN0b3AoIlZlY3RvciBsZW5ndGhzIG5vdCBjb3JyZWN0IikKICBpZiAobGVuZ3RoKGNvbG9yKT09MSAmIGxlbmd0aCh0cmFucyk+MSkgY29sb3IgPC0gcmVwKGNvbG9yLGxlbmd0aCh0cmFucykpCiAgaWYgKGxlbmd0aCh0cmFucyk9PTEgJiBsZW5ndGgoY29sb3IpPjEpIHRyYW5zIDwtIHJlcCh0cmFucyxsZW5ndGgoY29sb3IpKQogIAogIG51bTJoZXggPC0gZnVuY3Rpb24oeCkKICB7CiAgICBoZXggPC0gdW5saXN0KHN0cnNwbGl0KCIwMTIzNDU2Nzg5QUJDREVGIixzcGxpdD0iIikpCiAgICByZXR1cm4ocGFzdGUoaGV4Wyh4LXglJTE2KS8xNisxXSxoZXhbeCUlMTYrMV0sc2VwPSIiKSkKICB9CiAgcmdiIDwtIHJiaW5kKGNvbDJyZ2IoY29sb3IpLHRyYW5zKQogIHJlcyA8LSBwYXN0ZSgiIyIsYXBwbHkoYXBwbHkocmdiLDIsbnVtMmhleCksMixwYXN0ZSxjb2xsYXBzZT0iIiksc2VwPSIiKQogIHJldHVybihyZXMpCn0KCiMgTm9ybWFsCnRtcC54PC1zZXEoZnJvbT0tMyx0bz0zLGxlbmd0aD01MCkKcGFyKG1hcj1jKDQsNCwyLDEpKQoKY3VydmUoZG5vcm0sZnJvbT0tMyx0bz0zLGx3ZD0yLGNvbD0ibmF2eSIseWxhYj0iVGloZXlzZnVua3Rpb24gYXJ2byIseWxpbT1jKDAsLjg1KSkKY3VydmUoZG5vcm0oeCxtZWFuPS0xLHNkPS41KSxmcm9tPS0yLjUsdG89Mi41LGx3ZD0yLGNvbD0ibmF2eSIsYWRkPVRSVUUpCgpwb2x5Z29uKHg9Yyh0bXAueCkseT1jKGRub3JtKHRtcC54KSksY29sPWFkZFRyYW5zKCJibHVlIiwyMCkpCnBvbHlnb24oeD1jKHRtcC54KSx5PWMoZG5vcm0odG1wLngsbWVhbj0tMSxzZD0uNSkpLGNvbD1hZGRUcmFucygibmF2eSIsMzUpKQoKYWJsaW5lKHY9YygtMSwwKSxsdHk9MikKYGBgCgogIAogCiAKYGBge3IgLCAgZWNobyA9IFRSVUV9CiMge3IgUG9pc3NvbkVzaW1UbiwgZWNobyA9IFRSVUUscmVzdWx0cz0nYXNpcycsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQp0bXAueDwtYygwOjEwKTt0bXAubGFtYmRhPC1jKDAuNSwxLDIsNSkKdG1wLnRhYjwtc2FwcGx5KHRtcC5sYW1iZGEsZHBvaXMseD10bXAueCkKZGltbmFtZXModG1wLnRhYik8LWxpc3QocGFzdGUodG1wLngpLHBhc3RlKCIkbGFtYmRhPSQiLHRtcC5sYW1iZGEpKQojIHByaW50KGtuaXRyOjprYWJsZSh0bXAudGFiLGRpZ2l0cyA9IDMsIAojICAgICAgICAgICAgIGZvcm1hdC5hcmdzID0gbGlzdChkaWdpdHM9Mixuc21hbGw9MixkZWNpbWFsLm1hcmsgPSIsIiksCiMgICAgICAgICAgICAgY2FwdGlvbj0iUG9pc3NvbiBqYXVrYW1hbiB0b3RldW1pZW4gdG9kZW5uw6Rrw7Zpc3l5cyBlcmkgcGFyYW1ldGVyaW4gJFxcbGFtYmRhJCBhcnZvaWxsYSBcXGxhYmVse3RhYjpQb2lzc29uRXNpbVRufSIpKQojIAojIGtuaXRyOjprYWJsZSh0bXAudGFiLGRpZ2l0cyA9IDMsIAojICAgICAgICAgICAgIGZvcm1hdC5hcmdzID0gbGlzdChkaWdpdHM9Mixuc21hbGw9MixkZWNpbWFsLm1hcmsgPSIsIiksCiMgICAgICAgICAgICAgY2FwdGlvbj0iUG9pc3NvbiBqYXVrYW1hbiB0b3RldW1pZW4gdG9kZW5uw6Rrw7Zpc3l5cyBlcmkgcGFyYW1ldGVyaW4gJFxcbGFtYmRhJCBhcnZvaWxsYSBcXGxhYmVse3RhYjpQb2lzc29uRXNpbVRufSIsZm9ybWF0ID0gImxhdGV4IikKCmtibCh0bXAudGFiLCBjYXB0aW9uID0gIlBvaXNzb24gamF1a2FtYW4gdG90ZXVtaWVuIHRvZGVubsOka8O2aXN5eXMgZXJpIHBhcmFtZXRlcmluICRsYW1iZGEkIGFydm9pbGxhLiIsCiAgICBsYWJlbD0iUG9pc3NvbkVzaW1UbiIsYm9va3RhYnMgPSBULAogICAgZm9ybWF0LmFyZ3MgPWxpc3QoZGlnaXRzPTMsbnNtYWxsPTIsZGVjaW1hbC5tYXJrID0iLCIpKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkKCmBgYAoKYGBgez1sYXRleH0KXGJlZ2lue3RhYmxlfQoKXGNhcHRpb257UG9pc3Nvbi1qYWthdW1hbiB0b3RldW1pZW4gdG9kZW5uw6Rrw7Zpc3l5c3BhcmFtZXRyaW4gJFxsYW1iZGEkIGVyaSBhcnZvaWxsYSAoTiBvbiBoYXZhaXR0dSBsdWt1bcOkw6Ryw6QpIFxsYWJlbHt0YWI6UG9pc3NvbkVzaW1Ubn19ClxjZW50ZXJpbmcKXGJlZ2lue3RhYnVsYXJ9W3Rde2x8cnxyfHJ8cn0KXGhsaW5lCk4gICYgJFxsYW1iZGEkPSAwLjUgJiAkXGxhbWJkYSQ9IDEgJiAkXGxhbWJkYSQ9IDIgJiAkXGxhbWJkYSQ9IDVcXApcaGxpbmUKMCAmIDAsNjA3ICYgMCwzNjggJiAwLDEzNSAmIDAsMDA3XFwKXGhsaW5lCjEgJiAwLDMwMyAmIDAsMzY4ICYgMCwyNzEgJiAwLDAzNFxcClxobGluZQoyICYgMCwwNzYgJiAwLDE4NCAmIDAsMjcxICYgMCwwODRcXApcaGxpbmUKMyAmIDAsMDEzICYgMCwwNjEgJiAwLDE4MCAmIDAsMTQwXFwKXGhsaW5lCjQgJiAwLDAwMiAmIDAsMDE1ICYgMCwwOTAgJiAwLDE3NVxcClxobGluZQo1ICYgMCwwMDAgJiAwLDAwMyAmIDAsMDM2ICYgMCwxNzVcXApcaGxpbmUKNiAmIDAsMDAwICYgMCwwMDEgJiAwLDAxMiAmIDAsMTQ2XFwKXGhsaW5lCjcgJiAwLDAwMCAmIDAsMDAwICYgMCwwMDMgJiAwLDEwNFxcClxobGluZQo4ICYgMCwwMDAgJiAwLDAwMCAmIDAsMDAxICYgMCwwNjVcXApcaGxpbmUKOSAmIDAsMDAwICYgMCwwMDAgJiAwLDAwMCAmIDAsMDM2XFwKXGhsaW5lCjEwICYgMCwwMDAgJiAwLDAwMCAmIDAsMDAwICYgMCwwMThcXApcaGxpbmUKXGVuZHt0YWJ1bGFyfQpcZW5ke3RhYmxlfQoKYGBgCgoKCgpgYGB7ciBQb2lzc29uVG5GaWcsIGVjaG8gPSBUUlVFLGZpZy5jYXAgPSAnUG9pc3Nvbi1qYWthdW1hbiB0b2Rlbm7DpGvDtmlzeXlrc2nDpCBlcmkgJFxcbGFtYmRhJCBhcnZvaWxsYS4nLGZpZy5tYXJnaW4gPSBGQUxTRSwgIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSwgZmlnLmZ1bGx3aWR0aCA9IEZBTFNFLCBjYWNoZT1UUlVFfQpyZXF1aXJlKGdncGxvdDIpCgp0bXAuZGF0YTwtZGF0YS5mcmFtZShUbj1jKHRtcC50YWJbLDFdLHRtcC50YWJbLDJdLHRtcC50YWJbLDNdLHRtcC50YWJbLDRdKSwKICAgICAgICAgICAgICAgICAgICAgRnJlcT1mYWN0b3IocmVwKHRtcC54LDQpKSwKICAgICAgICAgICAgICAgICAgICAgTGFtYmRhPWZhY3RvcihyZXAodG1wLmxhbWJkYSxyZXAoMTEsNCkpKSkKCmdncGxvdCh0bXAuZGF0YSwgYWVzKEZyZXEsIFRuKSkgKyBnZW9tX2JhcihhZXMoZmlsbCA9IExhbWJkYSksIAogICB3aWR0aCA9IDAuNiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aD0zLjUpLCBzdGF0PSJpZGVudGl0eSIpICsgIAogICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IixsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksbGVnZW5kLmJveCA9ICJob3Jpem9udGFsIikrCiAgeGxhYigiTHVrdW3DpMOkcsOkIikreWxhYigiVG9kZW5uw6Rrw7Zpc3l5cyIpCiAgCiAKCiMgdG1wLng8LWMoMDoxMCk7dG1wLmxhbWJkYTwtYygwLjUsMSwyLDUpCiMgdG1wLnRhYjwtc2FwcGx5KHRtcC5sYW1iZGEsZHBvaXMseD10bXAueCkKIyBkaW1uYW1lcyh0bXAudGFiKTwtbGlzdChwYXN0ZSh0bXAueCkscGFzdGUodG1wLmxhbWJkYSkpCiMgdG1wLjE8LWRhdGEuZnJhbWUoeD10bXAueCx5PWModG1wLnRhYlssMV0sdG1wLnRhYlssMl0sdG1wLnRhYlssM10sdG1wLnRhYlssNF0pLAojICAgICAgICAgICAgICAgICAgIHBhcmFtPWZhY3RvcihyZXAodG1wLmxhbWJkYSxlYWNoPTExKSkpCiMgZ2dwbG90KHRtcC4xLGFlcyh4PXgseT15LGNvbD1wYXJhbSkpK2dlb21fbGluZShzaXplPTEpK2dlb21fcG9pbnQoKSsKIyAgICMgZ2VvbV9zZWdtZW50KGFlcyh4PXgrLjIseGVuZCA9IHgrLjIsIHllbmQgPSAwKSwgbGluZXdpZHRoID0gMTAsIGxpbmVlbmQgPSAiYnV0dCIsc2l6ZT0yKSsKIyAgIGxhYnMoeD0iTHVrdW3DpMOkcsOkIix5PSJUb2Rlbm7DpGvDtmlzeXlzIixjb2w9IlBhcmFtZXRyaSIpKyB0aGVtZShsZWdlbmQucG9zaXRpb249YyguNiwuOCkpKwojICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDAsMiw0LDYsOCwxMCkpCgpgYGAKCgogCgpgYGB7ciBOSEFORVNXYWlzdCxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZWNobyA9IFRSVUV9CmxpYnJhcnkoenRhYmxlKQoKbG9hZChmaWxlPSJuaGdoVjIuUkRhdGEiKQoKdG1wLmZ1bjwtZnVuY3Rpb24oeCl7CiAgYyhtZWFuKHgsbmEucm09VFJVRSksc2QoeCxuYS5ybT1UUlVFKSxxdWFudGlsZSh4LHByb2JzPWMoMjUsNTAsNzUpLzEwMCxuYS5ybT1UUlVFKSxzdW0oaXMubmEoeCkpLGxlbmd0aCh4KSkKfQp0bXAuZnVuMTwtZnVuY3Rpb24oeCxjYXR2YXIpewogIG1hdHJpeCh1bmxpc3Qod2l0aChuaGdoLHRhcHBseSh3YWlzdCxjYXR2YXIsdG1wLmZ1bikpKSxucm93PWxlbmd0aChsZXZlbHMoY2F0dmFyKSksYnlyb3c9VFJVRSwKICAgICAgICAgZGltbmFtZXM9bGlzdChsZXZlbHMoY2F0dmFyKSxjKCJLQSIsIktIIiwiMjUlIiwiNTAlIiwiNzUlIiwiTiBwdXV0IiwiTiIpKSkKfQoKdG1wLnRibDwtd2l0aChuaGdoLAogICAgIHJiaW5kKHRtcC5mdW4od2Fpc3QpLCB0bXAuZnVuMSh3YWlzdCxzZXgpLHRtcC5mdW4xKHdhaXN0LHJlKSkpCnJvd25hbWVzKHRtcC50YmwpW2MoMSldPC0iICIKcm93bmFtZXModG1wLnRibClbYyg4KV08LSJPdGhlciIKY29sbmFtZXModG1wLnRibCk8LWNvbG5hbWVzKHRtcC5mdW4xKG5oZ2gkd2Fpc3QsbmhnaCRzZXgpKQoKbGlicmFyeShrYWJsZUV4dHJhKQprYmwodG1wLnRibFssLWMoMyw1KV0sIGNhcHRpb24gPSAiTkhBTkVTIGFpbmVpc3RvLCBrZXNraWFydm8gKEtBKSwga2Vza2loYWpvbnRhIChLSCksIG1lZGlhYW5pLCBwdXV0dHV2aWVuIGx1a3Vtw6TDpHLDpCBqYSBsdWt1bcOkw6Ryw6QgKE4pLiIsIAogICAgZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9IiwiKSwKICAgIGJvb2t0YWJzID0gVCxkaWdpdHM9MikgJT4lIGthYmxlX3N0eWxpbmcoKSAlPiUKICBwYWNrX3Jvd3MoIkthaWtraSIsIDEsIDEpICU+JQogIHBhY2tfcm93cygiU3VrdXB1b2xpIiwgMiwgMykgJT4lCiAgcGFja19yb3dzKCJFdG5pbmVuIHRhdXN0YSIsIDQsIDgpCiAgCiMga25pdHI6OmthYmxlKHRtcC50YmwsY2FwdGlvbj0iSmF0a3V2aWVuIG11dXR0dWppZW4geWh0ZWV2ZXRvIikKIyB0bXAudGJsPC16dGFibGUodG1wLnRibCkKIyBwcmludCh1cGRhdGVfenRhYmxlKGFkZHJncm91cCh0bXAudGJsLHJncm91cD1jKCJJa8OkIiwiVnnDtnTDpHLDtm4geW1ww6RyeXMgKGNtKSIpLG4ucmdyb3VwPWMoOCw4KSksZGlnaXRzPWMocmVwKDIsOCksMCwwKSksY2FwdGlvbj0iSmF0a3V2aWVuIG11dXR0dWppZW4geWh0ZWV2ZXRvIikKI3ByaW50KHVwZGF0ZV96dGFibGUoYWRkcmdyb3VwKHRtcC50Ymwscmdyb3VwPWMoIkthaWtraSIsIlN1a3VwdW9saSIsIkV0bmluZW4gdGF1c3RhIiksbi5yZ3JvdXA9YygxLDIsNSkpLAojICAgICAgICAgICAgICAgICAgICBkaWdpdHM9YyhyZXAoMSw4KSwwLDApKSxjYXB0aW9uPSJKYXRrdXZpZW4gbXV1dHR1amllbiB5aHRlZW52ZXRvLiBWecO2dMOkcsO2biB5bXDDpHJ5cyAoY20pIikKCmBgYAogCmBgYHtyICxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UscmVzdWx0cz0naGlkZScsZWNobyA9IFRSVUUsZmlnLmNhcD0iVnnDtnTDpHLDtm4geW1ww6RyeXMuIFZhc2VtbWFsbGEgaGlzdG9ncmFtbWkgamEgdGloZXlzZnVua3Rpbywgb2lrZWFsbGEgdml1bHVrdXZpby4gUHlzdHl2aWl2YSBvc29pdHRhYSBtZWRpYWFuaW4ifQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ3JpZEV4dHJhKQoKdG1wLnAxPC1nZ3Bsb3QobmhnaCwgYWVzKHg9d2Fpc3QpKSArIAogZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvdXI9ImJsYWNrIiwgZmlsbD0id2hpdGUiKSsKIGdlb21fZGVuc2l0eShhbHBoYT0uMiwgZmlsbD0iI0ZGRkZGRiIpICsgIAogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9cXVhbnRpbGUobmhnaCR3YWlzdCxuYS5ybT1UUlVFLHByb2JzPWMoMC41KSkpLGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpKwogIGxhYnMoeT0iVGloZXlzZnVua3RpbyIseT0iVnnDtnTDpHLDtm4geW1ww6RyeXMiKQoKdG1wLnAyPC1nZ3Bsb3QobmhnaCwgYWVzKHg9MCx5PXdhaXN0KSkgKyBnZW9tX3Zpb2xpbigpK2xhYnMoeD0iIix5PSJWecO2dMOkcsO2biB5bXDDpHJ5cyIpCgpncmlkLmFycmFuZ2UodG1wLnAxLCB0bXAucDIsIG5yb3cgPSAxKQoKYGBgCgogCmBgYHtyIGhpc3RvdmlvbGluLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPSdhc2lzJyxlY2hvID0gVFJVRSxmaWcuY2FwPSJWecO2dMOkcsO2biB5bXDDpHJ5cy4gVmFzZW1tYWxsYSBoaXN0b2dyYW1taSBzdWt1cHVvbGV0IGVyaXRlbHR5aW7DpC4gWS1ha3NlbGlsbGEgbHVrdW3DpMOkcsOkLiBPaWtlYWxsYSB2aXVsdWt1dmlvLCBqb3NzYSBlc2l0ZXR0eSB0aWhleXNmdW5rdGlvLiJ9CnRtcC5wMzwtZ2dwbG90KG5oZ2gsIGFlcyh4PXdhaXN0KSkrZ2VvbV9oaXN0b2dyYW0oY29sb3I9ImJsYWNrIiwgZmlsbD0id2hpdGUiKStmYWNldF9ncmlkKHNleCB+LikrIHNjYWxlX2NvbG9yX2dyZXkoKStzY2FsZV9maWxsX2dyZXkoKStsYWJzKHk9Ikx1a3Vtw6TDpHLDpCIseD0iVnnDtnTDpHLDtm4geW1ww6RyeXMiKQojdG1wLnA0KyBzY2FsZV9jb2xvcl9ncmV5KCkrc2NhbGVfZmlsbF9ncmV5KCkgIyt0aGVtZV9jbGFzc2ljKCkKI3RtcC5wMwoKdG1wLnAyPC1nZ3Bsb3QobmhnaCwgYWVzKHg9MCx5PXdhaXN0KSkgKyBnZW9tX3Zpb2xpbigpK3hsYWIoIiAiKStmYWNldF9ncmlkKHNleCB+LikrCiAgbGFicyh4PSIiLHk9IlZ5w7Z0w6Ryw7ZuIHltcMOkcnlzIikKI3RtcC5wMgpncmlkLmFycmFuZ2UodG1wLnAzLCB0bXAucDIsIG5yb3cgPSAxKQpgYGAKCmBgYHtyIHZpaWtzaWxhYXRpa2tvLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPSdhc2lzJyxlY2hvID0gVFJVRSxmaWcuY2FwPSJWecO2dMOkcsO2biB5bXDDpHJ5cy4gVmlpa3NpbGFhdGlra29rdXZpbyBldG5pc2VuIHRhdXN0YW4gbXVrYWFuIn0KCnRtcC5wNDwtZ2dwbG90KG5oZ2gsIGFlcyh4PXJlLCB5PXdhaXN0KSkgKyAgZ2VvbV9ib3hwbG90KG5vdGNoPVRSVUUsb3V0bGllci5zaXplPS41KSsgY29vcmRfZmxpcCgpKyBnZW9tX2ppdHRlcihzaGFwZT0xNiwgcG9zaXRpb249cG9zaXRpb25faml0dGVyKDAuMiksc2l6ZT0uMSkKdG1wLnA0K2xhYnMoeD0iRXRuaW5lbiB0YXVzdGEiLHk9IlZ5w7Z0w6Ryw7ZuIHltcMOkcnlzIikKI2dyaWQuYXJyYW5nZSh0bXAucDMsIHRtcC5wNCwgbnJvdyA9IDEpCmBgYAoKIApgYGB7ciBoYWpvbnRha3V2aW8sbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLHJlc3VsdHM9J2FzaXMnLGVjaG8gPSBUUlVFLGZpZy5jYXA9Iklrw6QgamEgdnnDtnTDpHLDtm4geW1ww6RyeXMsIHN1a3VwdW9sZW4gbXVrYWFuIGVyaXRlbHR5bsOkLiJ9CnRtcC5wNTwtZ2dwbG90KG5oZ2gsIGFlcyh4PWFnZSx5PXdhaXN0LGNvbG9yPXNleCkpK2dlb21fcG9pbnQoc2l6ZT0uNSkrCiAgeGxhYigiSWvDpCAodikiKSt5bGFiKCJWecO2dMOkcsO2biB5bXDDpHJ5cyAoY20pIikjK2ZhY2V0X2dyaWQoc2V4IH4uKQp0bXAucDUrICBzY2FsZV9jb2xvdXJfZ3JleSgpK2xhYnMoY29sb3I9IlN1a3VwdW9saSIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCkpCgoKYGBgCgoKCgpgYGB7ciBoYWpvbnRha3V2aW9tYXRyaWlzaSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UscmVzdWx0cz0nYXNpcycsZWNobyA9IFRSVUUsZmlnLmNhcD0iSGFqb250YWt1dmlvbWF0cmlpc2kgKDEwMDAgaGF2YWludG9hKS4iLGZpZy5mdWxsd2lkdGg9VFJVRSxvdXQud2lkdGggPSAiODAlIn0KbGlicmFyeShHR2FsbHkpCnRtcC5wNjwtZ2dwYWlycyhkYXRhPWhlYWQobmhnaCwyMCksIGNvbHVtbnMgPSBjKDMsOCw5LDEwLDE0KSwKICAgICAgICAgICAgICAgIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gd3JhcChnZ2FsbHlfcG9pbnRzLCBzaXplID0gLjEsIGNvbG9yID0gIm5hdnkiKSApLAogICAgICAgICAgICAgICAgY29sdW1uTGFiZWxzID1jKCJJa8OkIiwiUGFpbm8gKGtnKSIsIlBpdHV1cyAoY20pIiwiQk1JIiwiVnnDtnQuIChjbSkiKSwKICAgICAgICAgICAgICAgIHNpemU9LjEscHJvZ3Jlc3M9RkFMU0UpCnRtcC5wNit0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTQpKQoKCndfZ2dhbGx5X3BvaW50cyA8LSB3cmFwKGdnYWxseV9wb2ludHMsIHNpemUgPSAuMiwgY29sb3IgPSAibmF2eSIpCgpwbTwtZ2dwYWlycyhkYXRhPWhlYWQobmhnaCwxMDAwKSwgY29sdW1ucyA9IGMoMyw4LDksMTAsMTQpLAogICAgICAgICAgICBjb2x1bW5MYWJlbHMgPWMoIklrw6QiLCJQYWlubyAoa2cpIiwiUGl0dXVzIChjbSkiLCJCTUkiLCJWecO2dC4gKGNtKSIpLAogICAgICAgICAgICBwcm9ncmVzcz1GQUxTRSwKICAgICAgICAgICAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKGdnYWxseV9wb2ludHMsIHNpemUgPSAuMSwgY29sb3IgPSAibmF2eSIpKSkKCnBtKyAgc2NhbGVfY29sb3VyX2dyZXkoKQoKCmBgYAoKCgogCmBgYHtyICxlY2hvID0gVFJVRX0KIyBMdWV0YWFuIGtlc2tpdsOka2lsdWt1IGphIGt1b2xsZWlkZW4gbcOkw6Ryw6QKbG9hZChmaWxlPSIuL1lodERhdGEvdG1wU3VvbWllc2ltLlJEYXRhIikKYGBgCgogCmBgYHtyIFN1b21pS3VvbCwgZWNobyA9IFRSVUUsIGZpZy5jYXAgPSAnS3VvbGVtYW52YWFyYSBTdW9tZXNzYSAxOTgwLTIwMTUsIHN5w7Zww6QgKHZpaXZhKSBqYSAgdmVyZW5raWVydG9lbGltaXN0b24gc2FpcmF1ZGV0IChrYXRrb3ZpaXZhKScsIGZpZy5hbGlnbj0nY2VudGVyJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsZXJyb3I9RkFMU0V9CnJlcXVpcmUoZ2dwbG90MikKdG1wLlN1b21pLmVzaW0ka3VvbGxlaXN1dXM8LXdpdGgodG1wLlN1b21pLmVzaW0sMTAwMDAwKkt1b2xsZWV0L0tlc2tpdmFraWx1a3UpCnRtcC5TdW9taS5lc2ltJHN5b3Aua3VvbDwtd2l0aCh0bXAuU3VvbWkuZXNpbSwxMDAwMDAqU3lvdmF0L0tlc2tpdmFraWx1a3UpCnRtcC5TdW9taS5lc2ltJHZlcmVuay5rdW9sPC13aXRoKHRtcC5TdW9taS5lc2ltLDEwMDAwMCpWZXJlbmtpZXJ0by9LZXNraXZha2lsdWt1KQp0bXAuU3VvbWkuZXNpbSR0YXBhdC5rdW9sPC13aXRoKHRtcC5TdW9taS5lc2ltLDEwMDAwMCpUYXBhdHVybWF0L0tlc2tpdmFraWx1a3UpCgpzdW9taS5rMSA8LSBnZ3Bsb3QodG1wLlN1b21pLmVzaW0sIGFlcyhWdW9zaSxzeW9wLmt1b2wpKStsYWJzKHg9IlZ1b3NpIix5PSJLdW9sZW1hbnZhYXJhXG4oMS8xMDAgMDAwKSIpIApzdW9taS5rMiA8LXN1b21pLmsxKwogIGdlb21fbGluZSgpKwogIGdlb21fbGluZShhZXMoeT12ZXJlbmsua3VvbCksbHR5PTIpKwogIHlsaW0oYygxNTAsNjAwKSkgCnN1b21pLmsyCgpgYGAKIAoKYGBge3IgRXNpbVN1b21pS3VvbCwgZWNobyA9IFRSVUUscmVzdWx0cz0nYXNpcycsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQp0bXAuU3VvbWkuZXNpbS5vdXQ8LWFzLm1hdHJpeCh0bXAuU3VvbWkuZXNpbSkKIyBkaW1uYW1lcyh0bXAuU3VvbWkuZXNpbS5vdXQpPC1saXN0KE5VTEwsYygiVnVvc2kiLCJLZXNraXbDpGtpbHVrdSIsIkthaWtraSBrdW9sbGVldCIsIlN5w7Zww6QiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVmVyZW5raWVydG8iLCJUYXBhdHVybWF0IiwgIkt1b2xlbWFudmFhcmEiLCJTecO2cMOka3VvbC5cXG4oMS8xMDAgMDAwKSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJWZXJlbmsuIGt1b2wuXFxuKDEvMTAwIDAwMCkiLCJUYXBhdHVybWFrdW9sLiIpKQoKIyBwcmludChrbml0cjo6a2FibGUodG1wLlN1b21pLmVzaW0ub3V0Wy0oMToxNSksYygxOjUsOCw5KV0sZGlnaXRzID0gYyhyZXAoMCw1KSwxLDEpLHJvdy5uYW1lcyA9RkFMU0UsCiMgICAgICAgICAgICAgZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9IiwiKSwjZm9ybWF0LmFyZ3MgPSBsaXN0KGRpZ2l0cz0wLG5zbWFsbD04LGRlY2ltYWwubWFyayA9IiwiKSwKIyAgICAgICAgICAgICBjYXB0aW9uPSJLdW9sZW1pZW4gbHVrdW3DpMOkcsOkIGphIGt1b2xlbWFudmFhcmEgKDEvMTAwIDAwMCkgU3VvbWVzc2EgMTk5NS0yMDE1LFxcbGFiZWx7dGFiOkVzaW1TdW9taUt1b2x9IikpCgpgYGAKCgogCiAKYGBge3IgdHlvbGxpc3V1c2t1dmEsIGVjaG8gPSBUUlVFLCBmaWcuY2FwID0gJ1N1b21lbiB0ecO2bGxpc3l5cyAyMDIwLTIwMjInLGZpZy5tYXJnaW4gPSBGQUxTRSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0UsZmlnLmZ1bGx3aWR0aD1GQUxTRX0KIwojIGZjcl8yMDE4LTA5LTE5LmNzdgojIGxpYnJhcnkocHhSKQojIGFwdS50eW88LWFzLmRhdGEuZnJhbWUocmVhZC5weChmaWxlbmFtZT0iaHR0cHM6Ly9weGRhdGEuc3RhdC5maTo0NDMvUHhXZWIvc3EvMjIwNGQ0ZDEtNTI0ZC00YWZjLWJmZDktMTY2Mzk5NjQxZDJkIikpCiMgYXB1LnR5byRWdW9zaTwtYXMubnVtZXJpYyhzdWJzdHIoYXB1LnR5byRWdW9zaW5lbGrDpG5uZXMsMSw0KSkrCiMgICBhcy5udW1lcmljKHN1YnN0cihhcHUudHlvJFZ1b3NpbmVsasOkbm5lcyw2LDYpKS80LSgxLzgpCiMgdHlvbGxpc3V1cy5kdDwtYXB1LnR5bwojIHNhdmUodHlvbGxpc3V1cy5kdCxmaWxlPSIuL1lodERhdGEvdHlvbGxpc3V1c2R0LlJEYXRhIikKbG9hZChmaWxlPSIuL1lodERhdGEvdHlvbGxpc3V1c2R0LlJEYXRhIikKZ2dwbG90KHR5b2xsaXN1dXMuZHQsYWVzKHg9VnVvc2kseT12YWx1ZSxncm91cD1TdWt1cHVvbGkpKSsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPVN1a3VwdW9saSxsaW5ldHlwZT1TdWt1cHVvbGkpLGx3ZD0uNykrCiAgbGFicyh4PSJWdW9zaSIseT0iVHnDtmxsaXN5eXMgJSAoMjAtNjQgdnVvdGlhYXQpIikrCnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4xLDAuODUpLGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSxsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTgpLGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpKQogICMgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCduYXZ5JywndG9tYXRvMicpKQogIAogIApgYGAKCiAKCmBgYHtyIExleGlzU2ltcGxlLCBlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0UscmVzdWx0cz0nYXNpcyd9CnJlcXVpcmUoRXBpKQpsb2FkKGZpbGU9Ii4vWWh0RGF0YS9lc2ltY29oLlJEYXRhIikKCiN0bXAudGJsPC16dGFibGUoZXNpbS5jb2guTHhbLGMoOSwxMSwzLDEyLDEzKV0pCiNwcmludCh1cGRhdGVfenRhYmxlKHRtcC50YmwsZGlnaXRzPTAsY2FwdGlvbj0iMTAgaGVua2lsw7ZuIHNldXJhbnRhLiBBZ2U9SWvDpCBhbHVzc2E7IEFnZS5lbmQ9SWvDpCBsb3B1c3NhOyBwZXJpb2Q9YWxrdSB2dW9zaTsgRGlhZ24uYWdlPWlrw6QgZGlhZ25vb3NpaW4gYWlrYWFuOyBQZXJzb24ueXJzPWtlcnR5bmVldCBzZXVyYW50YXZ1b2RldC4iKSkKYGBgCgogCmBgYHtyIExleGlzU2ltcGxlMSwgZWNobyA9IFRSVUUsIGZpZy5jYXAgPSAnU2V1cmFudGF0dXRraW11a3NlbiBMZXhpcy1rdXZpbyBpw6RuIHN1aHRlZW4uIFNldXJhbnRhLWFqYW4gcGl0dXVzIG9uIG1lcmtpdHR5LCBzYW1vaW4gcMOkw6R0ZXRhcGFodHVtYXQgKHTDpHBsw6QpLicsZmlnLm1hcmdpbiA9IEZBTFNFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxlcnJvcj1GQUxTRX0KI2tuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoIkt1dmF0THVrdTMvTGV4aXNLdXZhLnBuZyIpLGRwaT0zMDApCgojIHBuZyhmaWxlbmFtZSA9ICIuL1lodEt1dmF0L0xleGlzSWthLnBuZyIsd2lkdGg9MTYwMCxoZWlnaHQ9MTYwMC9zcXJ0KDIpLHBvaW50c2l6ZT0zNikKcGxvdChlc2ltLmNvaC5MeCx0aW1lLnNjYWxlPSJhZ2UiLHhsYWI9Iklrw6QiLHlsYWI9IkhlbmtpbMO2Iix0eXBlPSJsIiwKICAgICBsd2Q9NCxjZXguYXhpcz0xLGNleC5sYWI9MSx5bGltPWMoMSwxMSkpCnBvaW50cyhlc2ltLmNvaC5MeCxwY2g9YyhOQSwxNilbZXNpbS5jb2guTHgkbGV4LlhzdCsxXSxjb2w9InRvbWF0bzQiLGNleD0xKQpQWS5hbm4oZXNpbS5jb2guTHgsY2V4PTEsZGlnaXRzID0gMCkKYm94KCkKIyBhcHU8LWRldi5vZmYoKQojIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoIi4vWWh0S3V2YXQvTGV4aXNJa2EucG5nIiksZHBpPTMwMCkKCmBgYAogCgoKCmBgYHtyIEx1bmcsIGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxlcnJvcj1GQUxTRSxyZXN1bHRzPSdhc2lzJ30KcmVxdWlyZShFcGkpO3JlcXVpcmUoc3Vydml2YWwpCgojIHRtcC50Ymw8LXp0YWJsZShoZWFkKGx1bmcsMTApKQojIHByaW50KHVwZGF0ZV96dGFibGUodG1wLnRibCxkaWdpdHM9MCwKIyAgICAgICAgICAgICAgICAgICAgIGNhcHRpb249IlZpaXNpIHJpdmnDpCBrZXVoa29zecO2cMOkIGFpbmVpc3Rvc3RhLiBNdXV0dHVqaWVuIHNlbGl0eWtzZXQ6IGluc3Q6CUluc3RpdHV0aW9uIGNvZGU7ICB0aW1lOglTdXJ2aXZhbCB0aW1lIGluIGRheXM7ICBzdGF0dXM6CWNlbnNvcmluZyBzdGF0dXMgMT1jZW5zb3JlZCwgMj1kZWFkOyAgYWdlOglBZ2UgaW4geWVhcnM7ICBzZXg6CU1hbGU9MSBGZW1hbGU9MiBwaC5lY29nOglFQ09HIHBlcmZvcm1hbmNlIHNjb3JlICgwPWdvb2QgNT1kZWFkKTsgIHBoLmthcm5vOglLYXJub2Zza3kgcGVyZm9ybWFuY2Ugc2NvcmUgKGJhZD0wLWdvb2Q9MTAwKSByYXRlZCBieSBwaHlzaWNpYW47ICBwYXQua2Fybm86CUthcm5vZnNreSBwZXJmb3JtYW5jZSBzY29yZSBhcyByYXRlZCBieSBwYXRpZW50OyAgbWVhbC5jYWw6CUNhbG9yaWVzIGNvbnN1bWVkIGF0IG1lYWxzOyAgd3QubG9zczoJV2VpZ2h0IGxvc3MgaW4gbGFzdCBzaXggbW9udGhzOyAgIikpCgpgYGAKCgpgYGB7ciBLTUVzaW1lcmtraSwgZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxmaWcubWFyZ2luID0gRkFMU0UsZmlnLmNhcD0nS2FwbGFuLU1laWVyIGvDpHlyw6QuJywgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGVycm9yPUZBTFNFfQogCnRtcC5zMjwtc3VydmZpdChTdXJ2KHRpbWUsc3RhdHVzKX4xLGRhdGE9bHVuZykKbGlicmFyeShnZ3N1cnZmaXQpCgpnZy5sdW5nIDwtZ2dzdXJ2Zml0KHRtcC5zMikgKwogIGFkZF9jb25maWRlbmNlX2ludGVydmFsKCkgKwogIGxhYnModGl0bGUgPSAiIix4PSJTZXVyYW50YS1haWthIChkKSIseT0iRWxvc3NhIG9sb25cbnRvZGVubsOka8O2aXN5eXMiKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMSwwLjg1KSxheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCkpCgpnZy5sdW5nCmBgYAogCgpgYGB7ciBLTWRpdm9yY2UsIGVjaG8gPSBUUlVFLCBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZmlnLm1hcmdpbiA9IEZBTFNFLGZpZy5jYXA9J0F2aW9lcm9uIHRvZGVubsOka8O2aXN5eXMgeWhkeXN2YWx0YWxhaXNlbiBhaW5laXN0b24gbXVrYWFuLiBBdmlvbGlpdG90IGphb3RlbHR1IHB1b2xpc29pZGVuIGV0bmlzZW4gdGF1c3RhbiBtdWthYW4gKG1peGVkKSAoc2FtYT1ObywgZXJpPVllcykuJyxmaWcuZnVsbHdpZHRoPUZBTFNFfQoKbGlicmFyeShoYXZlbikKbG9hZChmaWxlPSJZaHREYXRhL2Rpdm9yY2UuUkRhdGEiKQoKdG1wLnMyLmRpdjwtc3VydmZpdChTdXJ2KHllYXJzLGRpdil+bWl4ZWQsZGF0YT1kaXZvcmNlKQoKZ2cuZGl2IDwtZ2dzdXJ2Zml0KHRtcC5zMi5kaXYsbGluZXR5cGVfYWVzID1UUlVFLHR5cGU9InJpc2siKSArCiAgYWRkX2NvbmZpZGVuY2VfaW50ZXJ2YWwoKSArCiAgbGFicyh0aXRsZSA9ICIiLHg9IlNldXJhbnRhLWFpa2EgKGEpIix5PSJBdmlvZXJvblxudG9kZW5uw6Rrw7Zpc3l5cyIpCmdnLmRpdjwtZ2cuZGl2KyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwwLjg1KSxheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NiksbGVnZW5kLnRleHQgPWVsZW1lbnRfdGV4dChzaXplPTYpICkrCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsNDApKQpnZy5kaXYKYGBgCgoKCmBgYHtyIEhhemFyZEVzaW0sIGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxmaWcuZnVsbHdpZHRoID0gRkFMU0UsZXJyb3I9RkFMU0Usb3V0d2lkdGg9IjkwJSIsZmlnLmNhcD0nSGFzYXJkaSBrw6R5csOkIGphIHNlbiA5NVxcJSBsdW90dGFtdXN2w6RsaS4nfQogCnJlcXVpcmUoY29ob3J0dG9vbHMpCiMgbHVuZyR5PC1yZXAoMCxucm93KGx1bmcpKQpsdW5nJFN1a3VwdW9saTwtZmFjdG9yKGx1bmckc2V4LGxldmVscz0xOjIsbGFiZWxzID0gYygiTWllaGV0IiwiTmFpc2V0IikpCnRtcC5kYXRhLjE8LWVzdGltLmhhemFyZChTdXJ2KHRpbWUvMzY1LjI1LHN0YXR1cykgfiBTdWt1cHVvbGksIGRhdGE9bHVuZyx1c2UuR0FNID0gVFJVRSkKZ2dwbG90KHRtcC5kYXRhLjEsIGFlcyh4PXRpbWUuZXZhbCx5PWhheixsaW5ldHlwZT1TdWt1cHVvbGkpKSArIAogIGdlb21fbGluZShjb2xvdXI9ImJsdWUiKSArCiAgeGxhYigiU2V1cmFudGEtYWlrYSAoYSkiKSt5bGFiKCJIYXNhcmRpIikrCiAgZ2VvbV9yaWJib24oYWVzKHltaW49aGF6LmxvLCB5bWF4PWhhei5oaSksIGFscGhhPTAuMikrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDIpLHhsaW09YygwLDEuNSkpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLDAuODUpLGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSxsZWdlbmQudGV4dCA9ZWxlbWVudF90ZXh0KHNpemU9OCkpCgpgYGAKCiAKIAoKYGBge3IgaGF6ZGl2b3JjZSwgZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxmaWcubWFyZ2luID0gRkFMU0UsZmlnLmNhcD0nQXZpb2Vyb24gdGFwYWh0dW1hdGloZXlzIHloZHlzdmFsdGFsYWlzZW4gYWluZWlzdG9uIG11a2Fhbi4gQXZpb2xpaXRvdCBqYW90ZWx0dSBwdW9saXNvaWRlbiBldG5pc2VuIHRhdXN0YW4gbXVrYWFuIChtaXhlZCkgKHNhbWE9Tm8sIGVyaT1ZZXMpLicsZmlnLmZ1bGx3aWR0aD1GQUxTRX0KCnRtcC5oYXouZGl2PC1lc3RpbS5oYXphcmQoU3Vydih5ZWFycyxkaXYpfm1peGVkLGRhdGE9ZGl2b3JjZSx1c2UuR0FNID0gVFJVRSkKcC5kaXY8LWdncGxvdCh0bXAuaGF6LmRpdiwgYWVzKHg9dGltZS5ldmFsLHk9aGF6LGxpbmV0eXBlPW1peGVkKSkKcC5kaXY8LXAuZGl2KyBnZW9tX2xpbmUoYWVzKGNvbG91cj1taXhlZCxsaW5ldHlwZT1taXhlZCksbHdkPS43KSArCiAgbGFicyh4PSJBdmlvbGlpdG9uIGtlc3RvIChhKSIsIHk9IkF2aW9lcm9uIHRhcGFodHVtYXRpaGV5c1xuKGhhc2FyZGkpIikrCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDAuMDMpLHhsaW09YygwLDQwKSkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjgsMC44KSxheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksbGVnZW5kLnRleHQgPWVsZW1lbnRfdGV4dChzaXplPTgpKStnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA3LGxpbmV0eXBlPTIpCnAuZGl2CgpgYGAKCiAKIApgYGB7ciBwYWxhaGFzYXJkaSxlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLCBmaWcubWFyZ2luID0gVFJVRSxmaWcuY2FwPSJFc2ltZXJra2kgcG9ycmFzZnVua3Rpb3N0YSJ9CnRtcC54PC1jKDAsMiw1LDkpCnRtcC55PC1jKDAuMiwwLjQsMC4xLDAuMykKcGxvdCh0bXAueCx0bXAueSx0eXBlPSJzIix4bGFiPSJBaWthICh0KSIseWxhYj0iSGFzYXJkaSIpCnBvaW50cyh0bXAueCx0bXAueSxwY2g9MSkKYm94KCkKYGBgCgogCgojIEVyb2plbiBtaXR0YWFtaW5lbgoKCiAKYGBge3IgaWN1dGF1bHUsIGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KCiMgcmVxdWlyZShrbml0cik7cmVxdWlyZSh6dGFibGUpO3JlcXVpcmUoRXBpKTtyZXF1aXJlKHN1cnZpdmFsKQojIG9wdGlvbnMoenRhYmxlLnR5cGU9ImxhdGV4IikKCiNwcmludChrYWJsZSh0bXAudWxvcyxjYXB0aW9uPSIgRXBpZGVtaW9sb2dpYXNzYSBrw6R5dGV0dHkgMlxcKjIgLXRhdWx1LiBcXGxhYmVse3RhYjpraXNzYXRhdWx1fSIsYWxpZ249InJyciIpKQojIEdpcmFyZGlzIE0sIEJ1c2FuaSBTLCBEYW1pYW5pIEUsIGV0IGFsLiBFZmZlY3Qgb2YgQ29uc2VydmF0aXZlIHZzIENvbnZlbnRpb25hbAojIE94eWdlbiBUaGVyYXB5IG9uIE1vcnRhbGl0eSBBbW9uZyBQYXRpZW50cyBpbiBhbiBJbnRlbnNpdmUgQ2FyZSBVbml0OiBUaGUKIyBPeHlnZW4tSUNVIFJhbmRvbWl6ZWQgQ2xpbmljYWwgVHJpYWwuIEpBTUEgUHVibGlzaGVkIE9ubGluZSBGaXJzdDogNSBPY3RvYmVyCiMgMjAxNi4gZG9pOjEwLjEwMDEvamFtYS4yMDE2LjExOTkzCnRtcC5pY3U8LW1hdHJpeChjKGEgPSAyNSwgYiA9IDIxNi0yNSwgYyA9IDQ0LCBkID0yMTgtNDQpLAogICAgICAgICAgICAgICAgZGltbmFtZXM9bGlzdChjKCJLb25zZXJ2YXRpaXZpbmVuIiwiVGF2YW5vbWFpbmVuIiksYygiS3VvbGx1dCIsIkVsb3NzYSIpKSwKICAgICAgICAgICAgICAgIG5jb2w9MixieXJvdz1UUlVFKQoKdG1wLmljdTE8LWFkZG1hcmdpbnModG1wLmljdSkKdG1wLmljdTE8LW1hdHJpeChwYXN0ZSh0bXAuaWN1MSxtYXRyaXgocGFzdGUwKCIoIixjKCJhIiwiYiIsImErYiIsImMiLCJkIiwiYytkIiwiYStjIiwiYitkIiwiYStiK2MrZCIpLCIpIiksYnlyb3c9VFJVRSxucm93PTMpKSxuY29sPTMpCmRpbW5hbWVzKHRtcC5pY3UxKTwtbGlzdChjKCJLb25zZXJ2YXRpaXZpbmVuIiwiVGF2YW5vbWFpbmVuIiwiS2Fpa2tpIiksYygiS3VvbGx1dCIsIkVsb3NzYSIsIkthaWtraSIpKQojIHRtcC5pY3UxPC16dGFibGUodG1wLmljdTEpCiMgdG1wLmljdTE8LWFkZHJncm91cCh0bXAuaWN1MSxuLnJncm91cCA9IGMoMiwxKSxyZ3JvdXAgPSBjKCIiLCIiKSkKIyBwcmludCh1cGRhdGVfenRhYmxlKHRtcC5pY3UxLGRpZ2l0cz0wLGFsaWduPWMoImwiLHJlcCgiciIsMykpLAojICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbj0iVHVsb3MgdGVob3N0ZXR1biBob2lkb24ga2xpaW5pc2VzdMOkIGtva2Vlc3RhLiBLaXJqYWltZXQgc3VsdWlzc2Egb3ZhdCAyKjItdGF1bHVuIHNvbHVqZW4gbmltZXQuXFxsYWJlbHt0YWI6aWN1dGF1bHV9IikpCiMga25pdHI6OmthYmxlKHRtcC5pY3UxLGNhcHRpb249IlR1bG9zIHRlaG9zdGV0dW4gaG9pZG9uIGtsaWluaXNlc3TDpCBrb2tlZXN0YS4gS2lyamFpbWV0IHN1bHVpc3NhIG92YXQgMioyLXRhdWx1biBzb2x1amVuIG5pbWV0LlxcbGFiZWx7dGFiOmljdXRhdWx1fSIpCgprYmwodG1wLmljdTEsIGJvb2t0YWJzID0gVCxkaWdpdHM9MiwKICAgIGNhcHRpb249IlR1bG9zIHRlaG9zdGV0dW4gaG9pZG9uIGtsaWluaXNlc3TDpCBrb2tlZXN0YS4gS2lyamFpbWV0IHN1bHVpc3NhIG92YXQgMioyLXRhdWx1biBzb2x1amVuIG5pbWV0LiIsCiAgICBsYWJlbD0iaWN1dGF1bHUiKSAlPiUga2FibGVfc3R5bGluZygpICU+JWNvbHVtbl9zcGVjKGJvbGQgPVRSVUUsY29sdW1uPTEpCmBgYCAKIAoKYGBge3IsIGVjaG8gPSBUUlVFLHJlc3VsdHM9J21hcmt1cCcsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQojVGF1bHVra28gMiBLYWhkZW4gaGFwZW4gYW5ub3N0ZWx1dGF2YW4gdmVydGFpbHUgdGVob2hvaWRvc3NhLCBrbGlpbmluZW4ga29lIGpvc3NhIHDDpMOkdGVtdXV0dHVqYW5hIG9saSAja3VvbGxlaXN1dXMoMikuICAKdG1wLmljdTwtbWF0cml4KGMoYSA9IDI1LCBiID0gMjE2LTI1LCBjID0gNDQsIGQgPTIxOC00NCksCiAgICAgICAgICAgICAgICBkaW1uYW1lcz1saXN0KGMoIktvbnNlcnZhdGlpdmluZW4iLCJUYXZhbm9tYWluZW4iKSxjKCJLdW9sbHV0IiwiRWxvc3NhIikpLAogICAgICAgICAgICAgICAgbmNvbD0yLGJ5cm93PVRSVUUpCgpFcGk6OnR3b2J5Mih0bXAuaWN1KQoKYGBgCiAKIApgYGB7ciAsIGVjaG8gPSBUUlVFLHJlc3VsdHM9J2FzaXMnfQp0bXAudGJsPC1tYXRyaXgocGFzdGUwKCIoIixjKCJhIiwiYiIsImErYiIsImMiLCJkIiwiYytkIiwiYStjIiwiYitkIiwiYStiK2MrZCIpLCIpIiksbnJvdyA9IDMsYnlyb3c9VFJVRSkKZGltbmFtZXModG1wLnRibCk8LWxpc3QoYygic2FpcmFzIiwidGVydmUiLCJrYWlra2kiKSxjKCJhbHRpc3R1bnV0IiwiYWx0aXN0dW1hdG9uIiwia2Fpa2tpIikpCgojIGtuaXRyOjprYWJsZSh0bXAudGJsLGNhcHRpb249IkVwaWRlbWlvbG9naWFuIDIqMi10YXVsdW4gc29sdWplbiBuaW1lw6RtaW5lbiIsbGFiZWw9Imtha3Npa2VydGFha2Frc2kiKQoKa2JsKHRtcC50YmwsIGJvb2t0YWJzID0gVCxkaWdpdHM9Mixmb3JtYXQuYXJncyA9IGxpc3QoZGVjaW1hbC5tYXJrID0iLCIpLAogICAgY2FwdGlvbj0iRXBpZGVtaW9sb2dpYW4gMioyLXRhdWx1biBzb2x1amVuIG5pbWXDpG1pbmVuIixsYWJlbD0ia2Frc2lrZXJ0YWFrYWtzaSIpICU+JSAKICBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkjJT4lcm93X3NwZWMoYm9sZCA9VFJVRSxyb3c9MSkKCiMgaWYoIWtuaXRyOjppc19odG1sX291dHB1dCgpKXsKIyBwcmludCh6dGFibGUodG1wLnRibCxhbGlnbj0iciIsY2FwdGlvbj0iRXBpZGVtaW9sb2dpYW4gMioyLXRhdWx1biBzb2x1amVuIG5pbWXDpG1pbmVuIiwKIyAgICAgICAgICAgICAgdHlwZT1pZmVsc2Uoa25pdHI6OmlzX2h0bWxfb3V0cHV0KCksImh0bWwiLCJsYXRleCIpKSl9CmBgYAoKCgogCgoKIAoKYGBge3IsIHJlc3VsdHM9J2hpZGUnLGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KIyBLaXNzYSBlc2ltZXJra2kKcmVxdWlyZShlcGlSKQpyZXF1aXJlKEVwaSkKdG1wLmtpc3NhPC1tYXRyaXgoYygxMyw1LDIxNjMsMzM0OSksIG5yb3cgPSAyKQojIGNsYXNzKHRtcC5raXNzYSk8LSJ0YWJsZSIKIyBkYXQgPC0gdChtYXRyaXgoYygxMyw1LDIxNjMsMzM0OSksIG5yb3cgPSAyLCBieXJvdyA9IFRSVUUpKQojIHJvd25hbWVzKGRhdCkgPC0gYygiRlVTKyIsICJGVVMtIik7IGNvbG5hbWVzKGRhdCkgPC0gYygiREYrIiwgIkRGLSIpOwoKdG1wLmtpc3NhLnR4dDwtbWF0cml4KGMoImEiLCJjIiwiYiIsImQiKSxuY29sPTIpCnRtcC51bG9zPC1tYXRyaXgocGFzdGUwKHRtcC5raXNzYS50eHQsIiAoIix0bXAua2lzc2EsIikiKSxuY29sPTIsYnlyb3c9RkFMU0UpCnRtcC51bG9zPC1jYmluZCh0bXAudWxvcyxjKCJhK2IgKDE4KSIsImMrZCAoNTUxMikiKSkKdG1wLnVsb3M8LXJiaW5kKHRtcC51bG9zLGMoImErYyAoMjE3NikiLCJiK2QgKDMzNTQpIiwiYStiK2MrZCAoNTUzMCkiKSkKZGltbmFtZXModG1wLnVsb3MpPC1saXN0KGMoImt1aXZhcnVva2EiLCJtdXUgcnVva2EiLCJLYWlra2kiKSxjKCJGVVMiLCJ0ZXJ2ZSIsIkthaWtraSIpKQojdG1wLnVsb3M7IGRhdApgYGAgCgogCgpgYGB7ciBraXNzYXRhdWx1LCByZXN1bHRzPSdhc2lzJyxlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CiNyZXF1aXJlKGtuaXRyKTtyZXF1aXJlKHp0YWJsZSkKI3ByaW50KGthYmxlKHRtcC51bG9zLGNhcHRpb249IiBFcGlkZW1pb2xvZ2lhc3NhIGvDpHl0ZXR0eSAyXFwqMiAtdGF1bHUuIFxcbGFiZWx7dGFiOmtpc3NhdGF1bHV9IixhbGlnbj0icnJyIikpCnJlcXVpcmUoRXBpKTtyZXF1aXJlKHN1cnZpdmFsKQojb3B0aW9ucyh6dGFibGUudHlwZT0ibGF0ZXgiKQojIHRtcC50Ymw8LXp0YWJsZSh0bXAudWxvcykKIyB0bXAudGJsLjE8LXVwZGF0ZV96dGFibGUodG1wLnRibCxkaWdpdHM9MCxhbGlnbj1jKCJsIiwiciIsInIiLCJyIiksY2FwdGlvbj0iS2lzc2FucnVva2F0dXRraW11a3NlbiAyICRcXHRpbWVzJCAyIC10YXVsdS4iKQoKIyBwcmludCh4dGFibGUodG1wLnVsb3MsY2FwdGlvbj0iS2lzc2FucnVva2F0dXRraW11a3NlbiAyICRcXHRpbWVzJCAyIC10YXVsdSIsCiMgICAgICAgICAgICAgIGxhYmVsPSJraXNzYXRhdWx1IiksY29tbWVudD1GQUxTRSkKCmtibCh0bXAudWxvcyxjYXB0aW9uPSJLaXNzYW5ydW9rYXR1dGtpbXVrc2VuIDIgJFxcdGltZXMkIDIgLXRhdWx1IiwKICAgICAgICAgICAgIGxhYmVsPSJraXNzYXRhdWx1IiwgYm9va3RhYnMgPSBULGRpZ2l0cz0yLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpICU+JSAKICBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkjJT4lcm93X3NwZWMoYm9sZCA9VFJVRSxyb3c9MSkKCmBgYCAKIApgYGB7ciwgcmVzdWx0cz0nbWFya3VwJyxlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZXBpUikKIGVwaS4yYnkyKGRhdCA9ICB0bXAua2lzc2EsIG1ldGhvZCA9ICJjcm9zcy5zZWN0aW9uYWwiLCAKICAgIGNvbmYubGV2ZWwgPSAwLjk1LCB1bml0cyA9IDEwMCwgIAogICAgb3V0Y29tZSA9ICJhcy5jb2x1bW5zIikKI3R3b2J5Mih0KGRhdCkpCmBgYAogCiAKCmBgYHtyIHVyaGVpbHV0YXVsdSwgcmVzdWx0cz0nYXNpcycsZWNobyA9IFRSVUUsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQpsb2FkKGZpbGU9IllodERhdGEvZGFGMDE1NC5SRGF0YSIpCnRtcC51cmhlaWx1PC10YWJsZShkYUYwMTU0JFE4NCxSZWxldmVsKGRhRjAxNTQkUTVCX0sscmVmPTIpKQoKIyBwcmludCh4dGFibGUodG1wLnVyaGVpbHUsY2FwdGlvbj0iVmFwYWFlaHRvaXN0ecO2IHVyaGVpbHVzc2EgamEgc3VrdXB1b2xpIiwKIyAgICAgICAgICAgICAgbGFiZWw9InVyaGVpbHV0YXVsdSIpLGNvbW1lbnQ9RkFMU0UpCgprYmwodG1wLnVyaGVpbHUsY2FwdGlvbj0iVmFwYWFlaHRvaXN0ecO2IHVyaGVpbHVzc2EgamEgc3VrdXB1b2xpIiwKICAgICAgICAgICAgIGxhYmVsPSJ1cmhlaWx1dGF1bHUiLCBib29rdGFicyA9IFQsZGlnaXRzPTIsZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9IiwiKSkgJT4lIAogIGthYmxlX3N0eWxpbmcoKSAlPiVjb2x1bW5fc3BlYyhib2xkID1UUlVFLGNvbHVtbj0xKQoKYGBgIAogCgogCmBgYHtyLCByZXN1bHRzPSdtYXJrdXAnLGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KCnR3b2J5Mih0bXAudXJoZWlsdSkKYGBgCiAKIApgYGB7ciwgZWNobyA9IFRSVUUscmVzdWx0cz0nbWFya3VwJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CnJlcXVpcmUoc3Vydml2YWwpCnRtcC5sdW5nLnB5PC1weWVhcnMoU3Vydih0aW1lLHN0YXR1cyl+ZmFjdG9yKHNleCxsYWJlbD1jKCJtaWVzIiwibmFpbmVuIikpLGRhdGE9bHVuZyxkYXRhLmZyYW1lID1UUlVFKQpkYXQubHVuZzwtYXMubWF0cml4KHRtcC5sdW5nLnB5JGRhdGFbLGMoNCwyKV0pCnJvd25hbWVzKGRhdC5sdW5nKTwtYygibWFsZSIsImZlbWFsZSIpCmVwaS4yYnkyKGRhdD1kYXQubHVuZyxtZXRob2QgPSJjb2hvcnQudGltZSIsdW5pdD0xKQpgYGAKCiAKCmBgYHtyLCBlY2hvID0gVFJVRSxyZXN1bHRzPSdtYXJrdXAnLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KcmVxdWlyZShzdXJ2aXZhbCkKbG9hZChmaWxlPSJZaHREYXRhL2Rpdm9yY2UuUkRhdGEiKQp0bXAuZGl2LnB5PC1weWVhcnMoU3Vydih5ZWFycyxkaXYpfm1peGVkLGRhdGE9ZGl2b3JjZSxkYXRhLmZyYW1lID1UUlVFLHNjYWxlPTEwMCkKZGF0LmRpdjwtYXMubWF0cml4KHRtcC5kaXYucHkkZGF0YVssYyg0LDIpXSkKcm93bmFtZXMoZGF0LmRpdik8LWMoIk5vIiwiWWVzIikKZXBpLjJieTIoZGF0PWRhdC5kaXYsbWV0aG9kID0iY29ob3J0LnRpbWUiLHVuaXQ9MSkKYGBgCiAKIAogCgpgYGB7ciBzdGRwb3B1bGFhdGlvLCBlY2hvID0gVFJVRSxyZXN1bHRzPSdhc2lzJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CiMgaHR0cHM6Ly9zZWVyLmNhbmNlci5nb3Yvc3RkcG9wdWxhdGlvbnMvc3RkcG9wZGljLmh0bWwKIyBlc2ltLnN0ZC5wb3B1bDwtcmVhZC5md2YoZmlsZT0iaHR0cHM6Ly9zZWVyLmNhbmNlci5nb3Yvc3RkcG9wdWxhdGlvbnMvc3RkcG9wLjE4YWdlcy50eHQiLHdpZHRocz1jKDMsMyw4KSwKIyAgICAgICAgICBhcy5pcz1UUlVFLCBjb2xDbGFzc2VzPWMoImNoYXJhY3RlciIsImNoYXJhY3RlciIsTkEpLAojICAgICAgICAgIGNvbC5uYW1lcz1jKCJMdW9raXR1cyIsIklrYWxrIiwiUG9wdWwiKSkKIyBlc2ltLnN0ZC5wb3B1bCRJa2FsazwtZmFjdG9yKGVzaW0uc3RkLnBvcHVsJElrYWxrLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9cGFzdGUwKDUqKDA6MTcpLCItIixwYXN0ZTAoYyg1KigxOjE3KS0xLCJ5bGkgODUiKSkpKQojICMgMDAzID0gRXVyb3BlYW4gKFNjYW5kaW5hdmlhbiAxOTYwKSBTdGQgTWlsbGlvbiAoMTggYWdlIGdyb3VwcykKIyAjIDAwMSA9IFdvcmxkIChTZWdpIDE5NjApIFN0ZCBNaWxsaW9uICgxOCBhZ2UgZ3JvdXBzKQojICMgMDA5ID0gV29ybGQgKFdITyAyMDAwLTIwMjUpIFN0ZCBNaWxsaW9uICgxOCBhZ2UgZ3JvdXBzKQojIGFwdTwtd2l0aChlc2ltLnN0ZC5wb3B1bCxjYmluZChlc2ltLnN0ZC5wb3B1bFtMdW9raXR1cz09IjAwMyIsLTFdLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlc2ltLnN0ZC5wb3B1bFtMdW9raXR1cz09IjAwMSIsM10pKQojIGNvbG5hbWVzKGFwdSk8LWMoIklrw6RsdW9ra2EiLCJNYWFpbG1hIiwiRXVyb29wcGEiKQpsaWJyYXJ5KHBvcEVwaSkKc3RkcG9wMTguVjE8LXN0ZHBvcDE4CmNvbG5hbWVzKHN0ZHBvcDE4LlYxKTwtYygiSWvDpGx1b2trYSIsIk1hYWlsbWEiLCJFdXJvb3BwYSIsIlBvaGpvaXNtYWF0IikKIyBwcmludChrbml0cjo6a2FibGUoc3RkcG9wMTguVjEscm93Lm5hbWVzPUZBTFNFLGNhcHRpb249IlN0YW5kYXJkaXbDpGVzdMO2amVuIHBhaW5va2VydG9pbWlhLiBcXGxhYmVse3RhYjpwb3B1bHBhaW5vamF9IikpCgprYmwoc3RkcG9wMTguVjEsY2FwdGlvbj0iU3RhbmRhcmRpdsOkZXN0w7ZqZW4gcGFpbm9rZXJ0b2ltaWEuIixsYWJlbD0icG9wdWxwYWlub2phIiwgCiAgICBib29rdGFicyA9IFQsZGlnaXRzPTIsZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9IiwiKSkgJT4lIAogIGthYmxlX3N0eWxpbmcoKSAlPiVjb2x1bW5fc3BlYyhib2xkID1UUlVFLGNvbHVtbj0xKSMlPiVyb3dfc3BlYyhib2xkID1UUlVFLHJvdz0xKQoKYGBgCgogIAoKCmBgYHtyIHN0ZHBvcHVsYWF0aW9ERU4sIGVjaG8gPSBUUlVFLHJlc3VsdHM9J2FzaXMnLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KcmVxdWlyZShFcGkpCiMgZGF0YSgiRE1sYXRlIikKIyBkbWwgPC0gTGV4aXMoIGVudHJ5PWxpc3QoUGVyPWRvZG0sIEFnZT1kb2RtLWRvYnRoLCBETWR1cj0wICksCiMgICAgICAgICAgICAgICAgZXhpdD1saXN0KFBlcj1kb3gpLAojICAgICAgICAgICAgICAgZW50cnkuc3RhdHVzPTAsCiMgICAgICAgICBleGl0LnN0YXR1cz1pZmVsc2UoaXMubmEoZG9kdGgpLDEsMiksCiMgICAgICAgICAgICAgICAgZGF0YT1ETWxhdGUgKQojIHNhdmUoZG1sLGZpbGU9IllodERhdGEvZG1sLlJEYXRhIikKbG9hZChmaWxlPSJZaHREYXRhL2RtbC5SRGF0YSIpCmRtbC4xPC1zcGxpdExleGlzKGRtbCxicmVha3M9Yyg1KigwOjE3KSksdGltZS5zY2FsZSA9ICJBZ2UiKQphcHU8LXRhYmxlKGN1dChkbWwkQWdlLGMoNSooMDoxNyksSW5mKSkpCgphcHU8LWRhdGEuZnJhbWUoSWthbHVva2thPWFzLnZlY3RvcihzdGRwb3AxOC5WMVssMV0pLAogICAgICAgICAgICAgICAgS3VvbC5sa209dGFwcGx5KCBzdGF0dXMoZG1sLjEsImV4aXQiKT09Mix0aW1lQmFuZChkbWwuMSwiQWdlIiwibGVmdCIpLHN1bSksCiAgICAgICAgICAgICAgICBIZW5rLnZ1b2RldC4xMDAwPXRhcHBseShkdXIoZG1sLjEpLHRpbWVCYW5kKGRtbC4xLCJBZ2UiLCJsZWZ0Iiksc3VtKS8xMDAwKQoKYXB1JEt1b2wuMTAwMDwtYXB1JEt1b2wubGttL2FwdSRIZW5rLnZ1b2RldC4xMDAwCiMgcHJpbnQoa25pdHI6OmthYmxlKGFwdSxyb3cubmFtZXM9RkFMU0UsZGlnaXRzID0gMiwgbG9uZ3RhYmxlID0gVFJVRSwKIyAgICAgICAgICAgICBmb3JtYXQuYXJncyA9IGxpc3QoZGlnaXRzPTIsbnNtYWxsPTIsZGVjaW1hbC5tYXJrID0iLCIpLAojICAgICAgICAgICAgIGNhcHRpb249IkVzaW1lcmtraWFpbmVpc3RvbiAgaWvDpGx1b2tpdHRhaXNldCBrdW9sbGVpc3V1ZGV0IDEwMDAgaGVua2lsw7Z2dW90dGEga29odGkuIFxcbGFiZWx7dGFiOnBvcHVsRGVuRXNpbX0iKSkKCmtibChhcHUsY2FwdGlvbj0iRXNpbWVya2tpYWluZWlzdG9uICBpa8OkbHVva2l0dGFpc2V0IGt1b2xsZWlzdXVkZXQgMTAwMCBoZW5raWzDtnZ1b3R0YSBrb2h0aS4iLAogICAgbGFiZWw9InBvcHVsRGVuRXNpbSIsIGJvb2t0YWJzID0gVCxkaWdpdHM9Mixmb3JtYXQuYXJncyA9IGxpc3QoZGVjaW1hbC5tYXJrID0iLCIpKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkKCmBgYAoKCiMgRGlhZ25vc3Rpc2V0IHRlc3RpdAoKIAoKYGBge3IgS29sbWVEZ25UZXN0aWEsZWNobyA9IFRSVUUscmVzdWx0cz0nYXNpcycsd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLGZpZy5jYXA9IktvbG1lbiBlcmkgdGVzdGluIChMUiAyLCA2LCBqYSAxMCkgcG9zaXRpaXZpc2VuIHRlc3RpbiBqw6Rsa2Vpc2V0IHRvZGVubsOka8O2aXN5eWRldCBlbm5lbiB0ZXN0acOkIHRvZGVubsOka8O2aXN5eWtzaWVuIGZ1bmt0aW9uYS4ifQp0bXAuUDA8LWZ1bmN0aW9uKFAwLExSKXsgKFAwKkxSKS8oMS1QMCsgUDAqTFIpfQojdG1wLlAwKFAwPXNlcShmcm9tPTAuMDAxLHRvPTAuMDA1LGxlbmd0aD0xMCksTFI9MTApCmxpYnJhcnkoZ2dwbG90MikKCnRtcC5kYXRhPC1kYXRhLmZyYW1lKHByZXY9cmVwKHNlcSgwLjAwMSwwLjUsbGVuZ3RoPTIwMCksMykpCnRtcC5kYXRhJFA8LWModG1wLlAwKFAwPXRtcC5kYXRhJHByZXZbMToyMDBdLExSPTEwKSwKICAgICAgICAgICAgICB0bXAuUDAoUDA9dG1wLmRhdGEkcHJldlsxOjIwMF0sTFI9NiksCiAgICAgICAgICAgICAgdG1wLlAwKFAwPXRtcC5kYXRhJHByZXZbMToyMDBdLExSPTIpKQp0bXAuZGF0YSRMUjwtZmFjdG9yKHJlcChjKDEwLDYsMikscmVwKDIwMCwzKSkpCgojc3VtbWFyeSh0bXAuZGF0YSkgICAgICAgICAgICAgICAgIApwMTwtZ2dwbG90KHRtcC5kYXRhLGFlcyh4PXByZXYseT1QLGNvbG9yPUxSLGxpbmV0eXBlPUxSKSkKcDErZ2VvbV9saW5lKHNpemU9Ljc2KStsYWJzKHg9IlRvZGVubsOka8O2aXN5eXMgZW5uZW4gdGVzdGnDpCAoUDApIix5PSJUb2Rlbm7DpGvDtmlzeXlzIHRlc3RpbiBqw6Rsa2VlbiAoUCcpIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjgsMC4zKSxheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksbGVnZW5kLnRleHQgPWVsZW1lbnRfdGV4dChzaXplPTgpKQoKCmBgYAoKICAKYGBge3IgZWNobyA9IFRSVUUscmVzdWx0cz0nYXNpcycsd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQogCnRtcC50Ymw8LW1hdHJpeChjKCIxMCIsIjAsMSIsIjYiLCIwLDIiLCIyIiwiMCw1IiwiMSIsIjEiKSxuY29sID0gMixieXJvdz1UUlVFKQpkaW1uYW1lcyh0bXAudGJsKTwtbGlzdChjKCJFcmlub21haW5lbiIsIkh5dsOkIiwiS29odGFsYWluZW4iLCJFaSBrw6R5dHTDtmFydm9hIiksYygiTFIrIiwiTFItIikpCgojIGtuaXRyOjprYWJsZSh0bXAudGJsLGNhcHRpb249IkRpYWdub3N0aXNlbiB0ZXN0aW4ga8OkeXR0w7ZrZWxwb2lzdXVzIGphIHVza290dGF2dXVzb3NhbcOkw6Ryw6QgKExSKSIpCgprYmwodG1wLnRibCxjYXB0aW9uPSJEaWFnbm9zdGlzZW4gdGVzdGluIGvDpHl0dMO2a2VscG9pc3V1cyBqYSB1c2tvdHRhdnV1c29zYW3DpMOkcsOkIChMUikuIiwKICAgIGxhYmVsPSJkaWFndGVzdGl0aHl2eXlzIiwKICAgIGJvb2t0YWJzID0gVCxkaWdpdHM9Mixmb3JtYXQuYXJncyA9IGxpc3QoZGVjaW1hbC5tYXJrID0iLCIpKSAlPiUgCiAga2FibGVfc3R5bGluZygpIyAlPiVjb2x1bW5fc3BlYyhib2xkID1UUlVFLGNvbHVtbj0xKSU+JXJvd19zcGVjKGJvbGQgPVRSVUUscm93PTEpCgojIGlmKCFrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSl7CiAjIHByaW50KHp0YWJsZSh0bXAudGJsLGFsaWduPSJyIixjYXB0aW9uPSJFcGlkZW1pb2xvZ2lhbiAyKjItdGF1bHVuIHNvbHVqZW4gbmltZcOkbWluZW4iLAogIyB0eXBlPWlmZWxzZShrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSwiaHRtbCIsImxhdGV4IikpKQpgYGAKCgogIApgYGB7ciBTZmVycml0aWluaSwgZWNobyA9IFRSVUUscmVzdWx0cz0nYXNpcycsd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQoKdG1wLnRibDwtbWF0cml4KGMoIisgKGFsbGUgNjUgbW1vbC9sKSIsImEgNzMxIiwiYiAyNzAiLCItIHlsaSA2NSBtbW9sL2wpIiwiYyA3OCIsImQgMTUwMCIpLG5jb2wgPSAzLGJ5cm93PVRSVUUpCmRpbW5hbWVzKHRtcC50YmwpPC1saXN0KGMoIkFuZW1pYSIsIkVpIGFuZW1pYWEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgYygiUy1mZXJyaXRpaW5pIiwiQW5lbWlhIiwiRWkgYW5lbWlhYSIpKQogCmtibCh0bXAudGJsLGNhcHRpb249IlMtZmVycml0aWluaSBqYSBhbmVtaWEiLCAKICAgIGJvb2t0YWJzID0gVCxkaWdpdHM9Mixmb3JtYXQuYXJncyA9IGxpc3QoZGVjaW1hbC5tYXJrID0iLCIpKSAlPiUgCiAga2FibGVfc3R5bGluZygpIyAlPiVjb2x1bW5fc3BlYyhib2xkID1UUlVFLGNvbHVtbj0xKSU+JXJvd19zcGVjKGJvbGQgPVRSVUUscm93PTEpCgogCmBgYAoKCiMgVHV0a2ltdXNhc2V0ZWxtYXQKCmBgYHtyIHNldHVwS3V2YWlsZXZhLCBlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0V9CiAKcmVxdWlyZSh6dGFibGUpCnJlcXVpcmUoRGlhZ3JhbW1lUikKCm9wdGlvbnMoenRhYmxlLnR5cGU9Imh0bWwiKQoKYGBgCgogCmBgYHtyIHRhdWx1ZGVzaWduLCBlY2hvID0gVFJVRSwgcmVzdWx0cz0nYXNpcycsd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQp0YWIuZGVzaWduczwtc3RydWN0dXJlKGxpc3QoSW50ZXJ2ZW50aW8gPSBjKCJFaSIsICJFaSIsICJFaSIsICJFaSIsICJPbiIsIAoiT24iLCAiT24iKSwgVHV0a2ltdXNhc2V0ZWxtYSA9IGMoIkVrb2xvZ2luZW4sIGtvcnJlbGFhdGlvIHR1dGtpbXVzIiwgIlBvaWtraWxlaWtrYXVzLCBwcmV2YWxlbnNzaSIsIAoiU2FpcmF1c2zDpGh0w7ZpbmVuIiwgIlNldXJhbnRhLCBrb2hvcnR0aSIsICJZaHRlaXPDtmludGVydmVudGlvIiwgIktlbnR0w6Rrb2UiLCAKIlNhdHVubmFpc3RldHR1IGtsaWluaW5lbiBrb2UsIFJDVCIpLCBIYXZhaW50b3lrc2lra8O2ID0gYygiUG9wdWxhYXRpbyIsIAoiWWtzaWzDtnQiLCAiWWtzaWzDtnQiLCAiWWtzaWzDtnQiLCAiWWh0ZWlzw7YiLCAiVGVydmVldCBoZW5raWzDtnQiLCAKIllrc2lsw7Z0IikpLCBjbGFzcyA9ICJkYXRhLmZyYW1lIiwgcm93Lm5hbWVzID0gYyhOQSwgLTdMKSkKCiMga25pdHI6OmthYmxlKHRhYi5kZXNpZ25zWyxjKDIsMywxKV0sY2FwdGlvbj0iTXV1dGFtaWVuIHR1dGtpbXVzYXNldGVtaWVuIGx1b2tpdHRlbHUuIikKCnRhYi5kZXNpZ25zLjE8LWFzLm1hdHJpeCh0YWIuZGVzaWducykKZGltbmFtZXModGFiLmRlc2lnbnMuMSk8LWxpc3QoTlVMTCxuYW1lcyh0YWIuZGVzaWducykpCgprYmwodGFiLmRlc2lnbnMuMVssYygyLDMsMSldLGNhcHRpb249Ik11dXRhbWllbiB0dXRraW11c2FzZXRlbG1pZW4gbHVva2l0dGVsdS4iLAogICAgYm9va3RhYnMgPSBULGRpZ2l0cz0yLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpICU+JQogIGthYmxlX3N0eWxpbmcoKSAjJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSklPiVyb3dfc3BlYyhib2xkID1UUlVFLHJvdz0xKQoKCmBgYAoKCgoKIApgYGB7ciAsIGVjaG8gPSBUUlVFLCByZXN1bHRzPSdoaWRlJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoRXBpKTtsaWJyYXJ5KHN1cnZpdmFsKQojd2l0aChsZXAsdHdvYnkyKGV4cG9zdXJlPXNleCxvdXRjb21lPWQpKQojIEV4YW1wbGUgb2YgY29ob3J0IGRhdGEKZXNpbS5jb2guZGF0YSA8LXN0cnVjdHVyZShsaXN0KE5vLiA9IDE6MTAsIEFnZSA9IGMoMzRMLCAzOUwsIDMxTCwgMzZMLCAzOEwsIDM4TCwgCjQxTCwgMzJMLCAzOUwsIDQyTCksIEZVLnRpbWUgPSBjKDE1TCwgMjBMLCAxMkwsIDE3TCwgOUwsIDE2TCwgCjExTCwgMjBMLCAxOEwsIDE3TCksIEFnZS5lbmQgPSBjKDQ5TCwgNTlMLCA0M0wsIDUzTCwgNDdMLCA1NEwsIAo1MkwsIDUyTCwgNTdMLCA1OUwpLCBEaWFnbi5hZ2UgPSBjKE5BLCA1NEwsIE5BLCA0MUwsIE5BLCA1MUwsIApOQSwgTkEsIE5BLCBOQSksIFBlcnNvbi55cnMgPSBjKDE1TCwgMTVMLCAxMkwsIDVMLCA5TCwgMTNMLCAxMUwsIAoyMEwsIDE4TCwgMUwpKSwgLk5hbWVzID0gYygiTm8uIiwgIkFnZSIsICJGVS50aW1lIiwgIkFnZS5lbmQiLCAKIkRpYWduLmFnZSIsICJQZXJzb24ueXJzIiksIGNsYXNzID0gImRhdGEuZnJhbWUiLCByb3cubmFtZXMgPSBjKE5BLCAKLTEwTCkpCgojZXNpbS5jb2guZGF0YTwtcmVhZC50YWJsZShmaWxlPSJjbGlwYm9hcmQiLGhlYWRlcj1UUlVFLHNlcD0iXHQiKQojbmFtZXMoZXNpbS5jb2guZGF0YSk8LWMoIk5vLiIsIkFnZSIsIkZVLnRpbWUiLCJBZ2UuZW5kIiwiRGlhZ24uYWdlIiwiUGVyc29uLnlycyIpCiNkdW1wKG9iamVjdHMoMSxwYXR0PSJlc2ltLmNvaC5kYXRhIiksZmlsZT0iIikKCmVzaW0uY29oLkx4PC1MZXhpcyhlbnRyeT1saXN0KGFnZT1BZ2UsZnUudGltZT1yZXAoMCxsZW5ndGgoQWdlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJpb2Q9YygxOTgwLDE5ODEsMTk4NSwxOTg4LDE5ODEsMTk4OCwxOTg4LDE5ODUsMTk4NywxOTgyKSksCiAgICAgICAgICAgICAgICAgICBleGl0PWxpc3QoYWdlPWlmZWxzZShpcy5uYShEaWFnbi5hZ2UpLEFnZS5lbmQsRGlhZ24uYWdlKSksCiAgICAgICAgICAgICAgICAgICBleGl0LnN0YXR1cyA9aWZlbHNlKGlzLm5hKERpYWduLmFnZSksMCwxKSAsZW50cnkuc3RhdHVzID0gMCwKICAgICAgICAgICAgICAgICAgIGlkPU5vLixkYXRhPWVzaW0uY29oLmRhdGEpCmBgYAoKCiAKCmBgYHtyIEt1dmE2TGV4aXMsIGVjaG8gPSBUUlVFLCBmaWcuY2FwID0gJ0t5bW1lbmVuIGhlbmtpbMO2biBzZXVyYW50YS4nLCBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDUuNiwgZmlnLmZ1bGx3aWR0aCA9IEZBTFNFfQojIENyZWF0ZSBMZXhpcyBvYmplY3QgZm9yIGNvaG9ydCBkYXRhCiMgSW5jbHVkZSB0d28gdGltZSBzY2FsZXM6IGFnZSBhbmQgZm9sbG93LXVwIHRpbWUgKGZ1LnRpbWUpCgoKcGxvdChlc2ltLmNvaC5MeCx0aW1lLnNjYWxlPSJhZ2UiLHhsYWI9Iklrw6QiLHlsYWI9IkhlbmtpbMO2IiwKICAgICBsd2Q9MixjZXguYXhpcz0xLGNleC5sYWI9MS40LHlsaW09YygxLDExKSxheGVzPUZBTFNFKQpheGlzKDEsYXQ9YygzMCwzNSw0MCw0NSw1MCw1NSw2MCkpCmF4aXMoMixhdD0xOjEwKQpwb2ludHMoZXNpbS5jb2guTHgscGNoPWMoTkEsMTYpW2VzaW0uY29oLkx4JGxleC5Yc3QrMV0sY29sPSJ0b21hdG80IixjZXg9MS40KQpQWS5hbm4oZXNpbS5jb2guTHgsY2V4PTEuMixkaWdpdHMgPSAwKQpwb2ludHMoeD1yZXAoNTEsNSkseT1jKDIsNzoxMCkscGNoPTEsY2V4PTEuNCxsd2Q9MikKYWJsaW5lKHY9NTEsbHR5PTIsbHdkPTIpO2FibGluZSh2PTQ1LGx0eT01LGx3ZD0xKTtib3goKQp3aXRoKGVzaW0uY29oLkx4LHNlZ21lbnRzKHgwPUFnZStsZXguZHVyLHkwPU5vLix4MT1yZXAoNjAsMTApLHkxPU5vLiwKICAgICBsd2Q9MixsdHk9Mixjb2w9ImdyZXkiKSkKIApgYGAKIAogCiAKYGBge3IgS3V2YTZPRUNELCBlY2hvID0gVFJVRSwgZmlnLmNhcCA9ICdUdXBha29pdmllbiBzYXZ1a2tlaWRlbiBrdWx1dHVzLiBQZXJ1c3R1dSBPRUNEOm4gYWlrYXNhcmpvaWhpbi4nLCBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZmlnLmZ1bGx3aWR0aCA9IFRSVUUsZmlnLmhlaWdodCA9IDQuMixmaWcud2lkdGggPSA2fQpkZW1vLk9FQ0Q8LXJlYWQudGFibGUoZmlsZT0iSEVBTFRIX0xWTkdfMDMwMjIwMTcxMTAwNDYyNjUuY3N2IixoZWFkZXI9VFJVRSxzZXA9IiwiKQojbGlicmFyeShsYXR0aWNlKQojICNsZXZlbHMoZGVtby5PRUNEJE1lYXN1cmUpCiMgeHlwbG90KFZhbHVlflllYXJ8IENvdW50cnksZGF0YT1kZW1vLk9FQ0RbZGVtby5PRUNEJE1lYXN1cmU9PSIlIG9mIHBvcHVsYXRpb24gYWdlZCAxNSsgd2hvIGFyZSBkYWlseSBzbW9rZXJzIixdKQojICAKIyB4eXBsb3QoVmFsdWV+WWVhcixncm91cD1Db3VudHJ5LGRhdGE9ZGVtby5PRUNEW2RlbW8uT0VDRCRNZWFzdXJlPT0iQ2lnYXJldHRlcyBwZXIgc21va2VyIHBlciBkYXkiLF0sCiMgICAgICAgIGF1dG8ua2V5PWxpc3QoY29sdW1ucyA9IDIpLCB0eXBlPSJiIikKI2FwdTwtc2ltcGxlS2V5KHRleHQgPSBsZXZlbHMoZGVtby5PRUNEJENvdW50cnkpLGNvbHVtbnM9NCk7IGFwdSRwb2ludHMkcGNoPC0xOjQKI3BuZyhmaWxlPSJDOi9Vc2Vycy9qa2hhdWtrYS9PbmVEcml2ZSAtIFVuaXZlcnNpdHkgb2YgSGVsc2lua2kvT3BldHVzL0VwaUtpcmphL0VwaUtpcmphL09FQ0RUdXBha29pbnRpLnBuZyIsd2lkdGg9OSo3Mio3LCBoZWlnaHQ9OSo3Mio3L3NxcnQoMikscmVzPTYwMCkKCiN4eXBsb3QoVmFsdWV+WWVhcixncm91cD1Db3VudHJ5LGRhdGE9ZGVtby5PRUNEW2RlbW8uT0VDRCRNZWFzdXJlPT0iQ2lnYXJldHRlcyBwZXIgc21va2VyIHBlciBkYXkiLF0sCiMgICAgICAga2V5PWFwdSxvdXRlcj1UUlVFLCB0eXBlPSJiIixwY2g9MTo0LHlsYWI9IlNhdnVra2VpdGEgcMOkaXbDpHNzw6QodmFpbiB0dXBha29pamF0KSIseGxhYj0iVnVvc2kiKQojZGV2Lm9mZigpCiMgeHlwbG90KFZhbHVlflllYXIsZ3JvdXA9Q291bnRyeSxkYXRhPWRlbW8uT0VDRFtkZW1vLk9FQ0QkTWVhc3VyZT09IiUgb2YgcG9wdWxhdGlvbiBhZ2VkIDE1KyB3aG8gYXJlIGRhaWx5IHNtb2tlcnMiLF0sCiMgICAgICAgIGtleT1hcHUsb3V0ZXI9VFJVRSwgdHlwZT0iYiIscGNoPTE6NCx5bGFiPSJUdXBha29pamllbiAlIG9zdXVzIix4bGFiPSJWdW9zaSIpCnJlcXVpcmUoZ2dwbG90MikKYXB1PC1kZW1vLk9FQ0RbZGVtby5PRUNEJE1lYXN1cmU9PSJDaWdhcmV0dGVzIHBlciBzbW9rZXIgcGVyIGRheSIsXQpwMTwtZ2dwbG90KGFwdSxhZXMoeT1WYWx1ZSx4PVllYXIsY29sb3I9Q291bnRyeSxzaGFwZT1Db3VudHJ5KSkrIGdlb21fcG9pbnQoc2l6ZT0yKStnZW9tX3BhdGgoKSsKICBsYWJzKHNoYXBlPSJNYWEiLGNvbG9yPSJNYWEiLHg9IlZ1b3NpIix5PSJUdXBha29pdmllbiBoZW5raWzDtmlkZW5cbnNhdnVra2VpZGVuIGt1bHV0dXMgcMOkaXbDpHNzw6QuIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjE1LDAuOCksYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLGxlZ2VuZC50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZT04KSkKcDEKYGBgCgogCiAKYGBge3IgS3V2YVNPVEtBLCBlY2hvID0gVFJVRSwgZmlnLmNhcCA9ICdBdmlvZXJvamVuIGphIG51b3J0ZW4gbWllbGVudGVydmV5ZGVuIGjDpGlyacO2aWRlbiByaWlwcHV2dXVzIGt1bm5pdHRhaW4gdnVvbm5hIDIwMjAuJywgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGZpZy5mdWxsd2lkdGggPSBGQUxTRSxmaWcuaGVpZ2h0ID0gNC4yLGZpZy53aWR0aCA9IDZ9Cgpsb2FkKGZpbGU9Ii4vWWh0RGF0YS9zb3RrYTFkdC5SRGF0YSIpCgpnZ3Bsb3Qoc290a2ExLmR0LGFlcyh4PUVyb3QseT1NaWVsaSkpK2dlb21fcG9pbnQoKStnZW9tX3Ntb290aChzaXplPS43KSsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMzApKSsKICBsYWJzKHg9IkF2aW9lcm9qYSAyNS02NC12dW90aWFpbGxhLzEgMDAwICIsCiAgICAgICB5PSJNaWVsZW50ZXJ2ZXlkZW4gaMOkaXJpw7ZpaGluIHNhaXJhYWxhaG9pdG9hXG4gc2FhbmVldCAwLTE3LXZ1b3RpYWF0LzEgMDAwIikrCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLGxlZ2VuZC50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZT04KSkKCmBgYAoKYGBge3Iga3V2YWVrb2hhcmhhLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdhc2lzJyxmaWcuZnVsbHdpZHRoID0gRkFMU0UsZWNobyA9IFRSVUUsZmlnLmNhcD0iRWtvbG9naW5lbiBoYXJoYSAoZWNvbG9naWNhbCBmYWxsYWN5KS4gWWtzaWzDtnRhc29sbGEgbWl0YXR0YWVzc2EgeC1tdXV0dHVqYW4ga2FzdnUgb24geWh0ZXlkZXNzw6QgeS1tdXV0dHVqYW4gdsOkaGVuZW1pc2VlbiwgbXV0dGEgcnlobcOkdGFzb24gYW5hbHl5c2kgb3NvaXR0YWEgcMOkaW52YXN0YWlzdGEgKG11c3RhIHZpaXZhKSIsZWNobyA9IFRSVUUgfQpyZXF1aXJlKGdncGxvdDIpCiMgU2ltdWxhdGlvbiBvZiB0aGUgZGF0YQpzZXQuc2VlZCgxOTE3KQp0bXAuZGF0YTwtZGF0YS5mcmFtZShhcmVhPWZhY3RvcihyZXAoTEVUVEVSU1sxOjVdLHJlcCgyMCw1KSkpKQp0bXAuZGF0YSR4PC0xMCtybm9ybShucm93KHRtcC5kYXRhKSkrYXMubnVtZXJpYyh0bXAuZGF0YSRhcmVhKQp0bXAuZGF0YSR5PC0gNSsgLTEqdG1wLmRhdGEkeCsgcm5vcm0obnJvdyh0bXAuZGF0YSkpKzMqYXMubnVtZXJpYyh0bXAuZGF0YSRhcmVhKQojIE1lYW4gdmFsdWVzIG9mIGFyZWFzIEEtRQp0bXAuZGF0YS4xPC13aXRoKHRtcC5kYXRhLGFnZ3JlZ2F0ZShsaXN0KHg9eCx5PXkpLGxpc3QoYXJlYT1hcmVhKSxtZWFuKSkKIyAnRWNvbG9naWNhbCcgcmVncmVzc2lvbiBtb2RlbAp0bXAubTEuY29lZjwtY29lZmZpY2llbnRzKGxtKHl+eCxkYXRhPXRtcC5kYXRhLjEpKQojIFBsb3RzCnAxLmVjbzEgPC0gZ2dwbG90KHRtcC5kYXRhLCBhZXMoeCA9IHgsIHkgPSB5LGNvbG9yPWFyZWEpKStnZW9tX3BvaW50KCkKcDEuZWNvMStnZW9tX3Ntb290aChmb3JtdWxhID0geSB+IHgsbWV0aG9kID0gImxtIikrCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSB0bXAubTEuY29lZlsyXSxpbnRlcmNlcHQgPSB0bXAubTEuY29lZlsxXSkrCiAgZ2VvbV9wb2ludChkYXRhPXRtcC5kYXRhLjEsYWVzKHg9eCx5PXkpLHBjaD0xOCxjZXg9NCxjb2w9Im5hdnkiKSsKICBsYWJzKGNvbG9yPSJBbGFwb3B1bGFhdGlvIikrdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4xLDAuNzUpLGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT02KSxsZWdlbmQudGV4dCA9ZWxlbWVudF90ZXh0KHNpemU9NikpCgpgYGAKCiAKYGBge3IsIGVjaG8gPSBUUlVFfQoKIyB7ciBmaWcuY2FwPSJFa29sb2dpc2VuIHR1dGtpbXVrc2VuIHZhaHZ1dWtzaWEgamEgaGVpa2tvdWtzaWEuIiwgZWNobyA9IFRSVUUsbWVzc2FnZT1GQUxTRSxvdXQud2lkdGggPSAnOTAlJyxmaWcuZnVsbHdpZHRoPVRSVUV9CiMgbGlicmFyeShEaWFncmFtbWVSKTtsaWJyYXJ5KERpYWdyYW1tZVJzdmcpO2xpYnJhcnkobWFncml0dHIpO2xpYnJhcnkocnN2Zyk7bGlicmFyeShwbmcpCgojIGFwdTwtZ3JWaXooc2NhbihmaWxlPSIuL0xhYXRpa290L1ZhaHZ1dXNIZWlra291c0Vrb2xvZ2luZW4uZ3YiLHNlcD0iXG4iLHdoYXQ9IiIscXVpZXQgPVRSVUUpKQojIGFwdSAlPiUgZXhwb3J0X3N2ZyAlPiUgY2hhclRvUmF3ICU+JSByc3ZnX3BuZyAoLHdpZHRoPTQwMDAsZmlsZT0iLi9ZaHRLdXZhdC9WYWh2dXVzSGVpa2tvdXNFa29sb2dpbmVuLnBuZyIpCiMga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoYygiLi9ZaHRLdXZhdC9WYWh2dXVzSGVpa2tvdXNFa29sb2dpbmVuLnBuZyIpKQojIGNvaG9ydHRvb2xzOjpndjJpbWFnZShndj1hcHUsdHlwZT0icG5nIixmaWxlPSIuL1lodEt1dmF0L1ZhaHZ1dXNIZWlra291c0Vrb2xvZ2luZW4iKQoKYGBgCgogCiAgCmBgYHtyIE5IQU5FU0sxLGVjaG8gPSBUUlVFLCBmaWcuY2FwPSJEaWFiZXRlc3RhIHNhaXJhc3RhdmllbiBsdWt1bcOkw6Ryw6QiLCBvdXQud2lkdGggPSAiOTAlIixmaWcubWFyZ2luID0gRkFMU0UsZmlnLmZ1bGx3aWR0aCA9IFRSVUV9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShncmlkRXh0cmEpCgojIGxvYWQodXJsKCJodHRwOi8vYmlvc3RhdC5tYy52YW5kZXJiaWx0LmVkdS93aWtpL3B1Yi9NYWluL0RhdGFTZXRzL25oZ2gucmRhIikpCmxvYWQoIm5oZ2hWMi5SRGF0YSIpCmxldmVscyhuaGdoJHJlKVs1XTwtIk90aGVyIFJhY2VcbkluY2x1ZGluZyBNdWx0aS1SYWNpYWwiCmNyb3MuMSA8LSBnZ3Bsb3QobmhnaCwgYWVzKHJlLGZpbGw9ZmFjdG9yKGR4KSkpK3hsYWIoIkV0bmlzeXlzIikreWxhYigiTHVrdW3DpMOkcsOkIikgCnRtcC5wMTwtY3Jvcy4xK3NjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkRpYWJldGVzXG5sw6TDpGtlIix2YWx1ZXMgPWMoInR1cnF1b2lzZSIsInJlZCIpKStnZW9tX2JhcigpK2Nvb3JkX2ZsaXAoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuOCwwLjgpLGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksbGVnZW5kLnRleHQgPWVsZW1lbnRfdGV4dChzaXplPTgpKQojIHRtcC5wMjwtY3Jvcy4xK3NjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkRpYWJldGVzXG5sw6TDpGtlIix2YWx1ZXMgPWMoInR1cnF1b2lzZSIsInJlZCIpKStjb29yZF9mbGlwKCkrZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiKSt5bGFiKCJPc3V1cyIpKwojICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbWUiKQojIGdyaWQuYXJyYW5nZSh0bXAucDEsIHRtcC5wMiwgbnJvdyA9IDIpCnRtcC5wMQpgYGAKCmBgYHtyIE5IQU5FU0syLGVjaG8gPSBUUlVFLCBmaWcuY2FwPSJEaWFiZXRlc3RhICgxKSBzYWlyYXN0YXZpZW4gcGFpbm9pbmRla3NpIChCTUkpIHZlcnJhdHR1bmEgbXVpaGluIn0KbGlicmFyeShnZ3Bsb3QyKQpjcm9zLjMgPC0gZ2dwbG90KG5oZ2gsIGFlcyhmYWN0b3IoZHgpLGJtaSkpK3hsYWIoIkRpYWJldGVzIikreWxhYigiUGFpbm9pbmRla3NpIChCTUkpIikgCmNyb3MuMysgZ2VvbV9ib3hwbG90KHZhcndpZHRoPVRSVUUpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LDAuOCksYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLGxlZ2VuZC50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZT04KSkKYGBgCgogCmBgYHtyIE5IQU5FU0szLGVjaG8gPSBUUlVFLCBmaWcuY2FwPSJEaWFiZXRlc3RhIChETSkgc2FpcmFzdGF2aWVuIGphIHNhaXJhc3RhbWF0dG9taWVuIHNva2VyaWhlbW9nbG9iaWluaSAoXFwlKSBpw6RuIHN1aHRlZW4uIixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZmlnLmZ1bGx3aWR0aD1UUlVFfQoKCmNyb3MuNSA8LSBnZ3Bsb3QobmhnaCwgYWVzKGFnZSxnaCkpK3hsYWIoIklrw6QiKSt5bGFiKCJTb2tlcmloZW1vZ2xvYmlpbmkgKCUpIikgCmNyb3MuNSsgZ2VvbV9wb2ludChjb2w9Im5hdnkiLGFscGhhPS4xLHNpemU9LjIpKyAKICBmYWNldF9ncmlkKGZhY3RvcihkeCxsYWJlbHM9YygiZWkgRE0iLCJETSIpKX5yZSkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsc3Bhbj0wLjc1KSt5bGltKDQsOCkrCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNCksYXhpcy50ZXh0PSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LDAuOCksYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLGxlZ2VuZC50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZT04KSkKCmBgYAoKCmBgYHtyLCBlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQpsaWJyYXJ5KGRwbHlyKQpuaGdoJERpYWJldGVzPC1mYWN0b3IobmhnaCRkeCxsZXZlbHM9YygwLDEpLGxhYmVsPWMoImVpIiwib24iKSkKdG1wLmR4LmdyPC1ncm91cF9ieShuaGdoLERpYWJldGVzKQpjb3VudC5uYSA8LSBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpCiMga25pdHI6OmthYmxlKHN1bW1hcmlzZSh0bXAuZHguZ3IsCiMgICAgICAgICAgIEtlc2tpYXJ2bz1tZWFuKGFnZSxuYS5ybT1UUlVFKSxTRD1zZChibWksbmEucm09VFJVRSksCiMgICAgICAgICAgIE1lZGlhYW5pPW1lZGlhbihibWksbmEucm09VFJVRSksUC4yNT1xdWFudGlsZShibWkscHJvYnMgPSBjKDAuMjUpKSwKIyAgICAgICAgICAgUC43NT1xdWFudGlsZShibWkscHJvYnMgPSBjKDAuNzUpKSksZGlnaXRzPTIsCiMgICAgICAgICAgIGNhcHRpb249Iklrw6QgZGlhYmV0ZWtzZW4gc3VodGVlbiwga2Vza2lhcnZvLCBtZWRpYWFuaSwga2Vza2loYWpvbnRhLCBqYSBhbGEtIGphIHlsw6Qga3ZhcnRpaWxpdC4iKQoKbGlicmFyeSh0YWJsZW9uZSkKCnRtcDE8LUNyZWF0ZVRhYmxlT25lKG5oZ2gsdmFycz1jKCJhZ2UiLCJibWkiKSxzdHJhdGE9IkRpYWJldGVzIix0ZXN0ID1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgYWRkT3ZlcmFsbD1UUlVFKQp0bXAxPC1wcmludCh0bXAxLHByaW50VG9nZ2xlID0gRkFMU0UpCnRtcDE8LWdzdWIocGF0dGVybiA9ICIuIiwgcmVwbGFjZW1lbnQgPSAiLCIseD10bXAxLGZpeGVkID0gVFJVRSkKCmtibCh0bXAxLGxhYmVsPSJOSEFORVNEaWFiZXRlc1RhdWx1IiwKICAgIGNhcHRpb249Iklrw6QgZGlhYmV0ZWtzZW4gc3VodGVlbiwga2Vza2lhcnZvLCBtZWRpYWFuaSwga2Vza2loYWpvbnRhLCBqYSBhbGEtIGphIHlsw6Qga3ZhcnRpaWxpdC4iLAogICAgYm9va3RhYnMgPSBULGRpZ2l0cz0yLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpCgojIHRtcC50YWI8LXN1bW1hcmlzZSh0bXAuZHguZ3IsCiMgICAgICAgICAgIEtlc2tpYXJ2bz1tZWFuKGFnZSxuYS5ybT1UUlVFKSxTRD1zZChibWksbmEucm09VFJVRSksCiMgICAgICAgICAgIE1lZGlhYW5pPW1lZGlhbihibWksbmEucm09VFJVRSksUC4yNT1xdWFudGlsZShibWkscHJvYnMgPSBjKDAuMjUpKSwKIyAgICAgICAgICAgUC43NT1xdWFudGlsZShibWkscHJvYnMgPSBjKDAuNzUpKSkKIyAKIyBrYmwodG1wLnRhYixkaWdpdHM9MiwKIyAgICAgICAgICAgY2FwdGlvbj0iSWvDpCBkaWFiZXRla3NlbiBzdWh0ZWVuLCBrZXNraWFydm8sIG1lZGlhYW5pLCBrZXNraWhham9udGEsIGphIGFsYS0gamEgeWzDpCBrdmFydGlpbGl0LiIsCiMgICAgIGJvb2t0YWJzID0gVCxkaWdpdHM9Mixmb3JtYXQuYXJncyA9IGxpc3QoZGVjaW1hbC5tYXJrID0iLCIpKSMgJT4lCiAgIyBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSklPiVyb3dfc3BlYyhib2xkID1UUlVFLHJvdz0xKQoKCgoKYGBgCiAKIAoKYGBge3IgTkhBTkVTSzQsIGVjaG8gPSBUUlVFLCBlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLGZpZy5jYXA9IlBhaW5vaW5kZWxzaW4gKEJNSSkgcmlpcHB1dnV1cyBldG5pc2VzdGEgcnlobcOkc3TDpC4ifQpjcm9zLjQgPC0gZ2dwbG90KG5oZ2gsIGFlcyhmYWN0b3IocmUpLGJtaSkpK3hsYWIoIkV0bmluZW4gcnlobcOkIikreWxhYigiUGFpbm9pbmRla3NpIChCTUkpIikgCmNyb3MuNCsgZ2VvbV9ib3hwbG90KHZhcndpZHRoPVRSVUUsbm90Y2ggPSBUUlVFKStjb29yZF9mbGlwKCkrCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNCksYXhpcy50ZXh0PSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LDAuOCksYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLGxlZ2VuZC50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZT04KSkKCmBgYAoKCmBgYHtyICxlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQpsaWJyYXJ5KEVwaSk7cmVxdWlyZSh6dGFibGUpO2xpYnJhcnkoa2FibGVFeHRyYSkKdG1wLm0uYm1pPC1sbShibWl+cmUsZGF0YT1uaGdoKQojIHByaW50KHp0YWJsZSh0bXAubS5ibWksY2FwdGlvbj0iVHVsb2tzZXQgbGluZWFhcmlzZXN0YSByZWdyZXNzaW9tYWxsaXN0YSBqb3NzYSBzZWxpdGV0dMOkdsOkIG11dXR0dWphbiBvbiBCTUkuIFZlcnRhaWx1cnlobcOkbsOkIG9uICdNZXhpY2FuIEFtZXJpY2FuJy4iKSx0eXBlPSJsYXRleCIpCiMga25pdHI6OmthYmxlKHN1bW1hcnkodG1wLm0uYm1pKSRjb2VmZmljaWVudHMsCiMgICAgICAgICAgICAgIGNhcHRpb249IlR1bG9rc2V0IGxpbmVhYXJpc2VzdGEgcmVncmVzc2lvbWFsbGlzdGEgam9zc2Egc2VsaXRldHTDpHbDpCBtdXV0dHVqYW4gb24gQk1JLiBWZXJ0YWlsdXJ5aG3DpG7DpCBvbiAnTWV4aWNhbiBBbWVyaWNhbicuIiwKIyAgICAgICAgICAgICAgZGlnaXRzPTQsYm9va3RhYnM9VFJVRSkKCmFwdTwtY2kubGluKHRtcC5tLmJtaSlbLGMoMSwyLDUsNildCgprYmwoYXB1LAogICAgICAgICAgICAgY2FwdGlvbj0iVHVsb2tzZXQgbGluZWFhcmlzZXN0YSByZWdyZXNzaW9tYWxsaXN0YSBqb3NzYSBzZWxpdGV0dMOkdsOkIG11dXR0dWphbiBvbiBCTUkuIFZlcnRhaWx1cnlobcOkbsOkIG9uICdNZXhpY2FuIEFtZXJpY2FuJy4gU2FyYWtrZWV0IDIuNVxcJSBqYSA5Ny41XFwlIG9zb2l0dGF2YXQgOTVcXCUgbHVvdHRhbXVzdsOkbGluLiIsCiAgICBib29rdGFicyA9IFQsZGlnaXRzPTIsbGFiZWwgPSAidHVsb3NuaGFuZXMxIiwKICAgIGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpICU+JQogIGthYmxlX3N0eWxpbmcobGF0ZXhfb3B0aW9ucyA9IGMoImhvbGRfcG9zaXRpb24iLCJzY2FsZV9kb3duIiksIGZ1bGxfd2lkdGg9VFJVRSkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkKCmtuaXRyOjprYWJsZShhcHUsY2FwdGlvbj0iVHVsb2tzZXQgbGluZWFhcmlzZXN0YSByZWdyZXNzaW9tYWxsaXN0YSBqb3NzYSBzZWxpdGV0dMOkdsOkIG11dXR0dWphbiBvbiBCTUkuIFZlcnRhaWx1cnlobcOkbsOkIG9uICdNZXhpY2FuIEFtZXJpY2FuJy4gU2FyYWtrZWV0IDIuNVxcJSBqYSA5Ny41XFwlIG9zb2l0dGF2YXQgOTVcXCUgbHVvdHRhbXVzdsOkbGluLiIsZGlnaXRzPTIsCiAgICAgICAgICAgICBib29rdGFicyA9IFRSVUUsbGFiZWw9InR1bG9zbmhhbmVzMSIsZm9ybWF0PSJsYXRleCIpCgoKCgpgYGAKIAogCmBgYHtyICxlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdhc2lzJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc3RhcmdhemVyKQp0bXAubS5ibWkuMTwtdXBkYXRlKHRtcC5tLmJtaSx+LithZ2Urc2V4KQphcHUxPC1jaS5saW4odG1wLm0uYm1pLjEpWyxjKDEsMiw1LDYpXQoKIyBrYmwoYXB1MSwKIyAgICAgICAgICAgICAgY2FwdGlvbj0iVHVsb2tzZXQgbGluZWFhcmlzZXN0YSByZWdyZXNzaW9tYWxsaXN0YSBqb3NzYSBzZWxpdGV0dMOkdsOkIG11dXR0dWphbiBvbiBCTUkuIFZlcnRhaWx1cnlobcOkbsOkIG9uICdNZXhpY2FuIEFtZXJpY2FuJy4gTXVrYW5hIHRhdXN0YW11dXR0dWphdC4gU2FyYWtrZWV0IDIuNVxcJSBqYSA5Ny41XFwlIG9zb2l0dGF2YXQgOTVcXCUgbHVvdHRhbXVzdsOkbGluLiIsIGJvb2t0YWJzID0gVCxkaWdpdHM9MixsYWJlbCA9ICJ0dWxvc2xvc25oYW5lczIiLAojICAgICBmb3JtYXQuYXJncyA9IGxpc3QoZGVjaW1hbC5tYXJrID0iLCIpKSAlPiUKIyAgIGthYmxlX3N0eWxpbmcoKSAlPiVjb2x1bW5fc3BlYyhib2xkID1UUlVFLGNvbHVtbj0xKQoKIyBrbml0cjo6a2FibGUoY2kubGluKHRtcC5tLmJtaS4xLHN1YnNldD0icmUiKVssMTo0XSxkaWdpdD1jKDQsNCw0LDQsNCw0KSkKI3ByaW50KHN0YXJnYXplcih0bXAubS5ibWksdG1wLm0uYm1pLjEsdHlwZT1pZmVsc2Uoa25pdHI6OmlzX2h0bWxfb3V0cHV0KCksImh0bWwiLCJsYXRleCIpKSkKCiMgcHJpbnQoenRhYmxlKHRtcC5tLmJtaS4xLGNhcHRpb249IlR1bG9rc2V0IGxpbmVhYXJpc2VzdGEgcmVncmVzc2lvbWFsbGlzdGEgam9zc2Egc2VsaXRldHTDpHbDpCBtdXV0dHVqYW4gb24gQk1JLiBWZXJ0YWlsdXJ5aG3DpG7DpCBvbiAnTWV4aWNhbiBBbWVyaWNhbicuIE11a2FuYSB0YXVzdGFtdXV0dHVqYXQiKSx0eXBlPSJsYXRleCIpCgoKa25pdHI6OmthYmxlKGFwdTEsY2FwdGlvbj0iVHVsb2tzZXQgbGluZWFhcmlzZXN0YSByZWdyZXNzaW9tYWxsaXN0YSBqb3NzYSBzZWxpdGV0dMOkdsOkIG11dXR0dWphbiBvbiBCTUkuIFZlcnRhaWx1cnlobcOkbsOkIG9uICdNZXhpY2FuIEFtZXJpY2FuJy4gU2FyYWtrZWV0IDIuNVxcJSBqYSA5Ny41XFwlIG9zb2l0dGF2YXQgOTVcXCUgbHVvdHRhbXVzdsOkbGluLiIsZGlnaXRzPTIsCiAgICAgICAgICAgICBib29rdGFicyA9IFRSVUUsbGFiZWw9InR1bG9zbmhhbmVzMSIsZm9ybWF0PSJsYXRleCIpCgoKYGBgCgogCiAKIAoKYGBge3IgS3V2YTA5TGV4aXMsIGVjaG8gPSBUUlVFLCBmaWcuY2FwID0gJ0t5bW1lbmVuIGhlbmtpbMO2biBzZXVyYW50YS4gWWhkZWxsZSBoZW5raWzDtmxsZSAobnJvIDYpIG9uIG1lcmtpdHR5IGFpa2FyaWlwcHV2YSBhbHRpc3RlIHNpbmlzZWxsw6QgdmFha2F2aWl2YWxsYS4nLCBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0Usb3V0LndpZHRoPSI5MCUifQojIENyZWF0ZSBMZXhpcyBvYmplY3QgZm9yIGNvaG9ydCBkYXRhCiMgSW5jbHVkZSB0d28gdGltZSBzY2FsZXM6IGFnZSBhbmQgZm9sbG93LXVwIHRpbWUgKGZ1LnRpbWUpCnJlcXVpcmUoRXBpKTtyZXF1aXJlKHN1cnZpdmFsKQoKbG9hZChmaWxlPSIuL1lodERhdGEvZXNpbWNvaC5SRGF0YSIpCgojIHBuZyhmaWxlbmFtZSA9ICIuL1lodEt1dmF0L2VzaW1jb2gucG5nIix3aWR0aD0xNjAwLGhlaWdodD0xNjAwL3NxcnQoMikscG9pbnRzaXplPTM2KQpwbG90KGVzaW0uY29oLkx4LHRpbWUuc2NhbGU9ImFnZSIseGxhYj0iSWvDpCIseWxhYj0iXG5IZW5raWzDtiIsCiAgICAgbHdkPTgsY2V4LmF4aXM9MC44LGNleC5sYWI9MS40LHlsaW09YygxLDExKSkKI2F4aXMoMSxhdD1jKDMwLDQwLDUwLDYwKSkKI2F4aXMoMixhdD0xOjEwKQpwb2ludHMoZXNpbS5jb2guTHgscGNoPWMoTkEsMTYpW2VzaW0uY29oLkx4JGxleC5Yc3QrMV0sY29sPSJ0b21hdG80IixjZXg9MS40KQpQWS5hbm4oZXNpbS5jb2guTHgsY2V4PTEuMixkaWdpdHMgPSAwKQpib3goKQp3aXRoKGVzaW0uY29oLkx4LHNlZ21lbnRzKHgwPUFnZStsZXguZHVyLHkwPU5vLix4MT1yZXAoNjAsMTApLHkxPU5vLiwKICAgICBsd2Q9MixsdHk9Mixjb2w9ImdyZXkiKSkKdG1wLmlkLjYuZXhwbzwtYyg0MCw0NSkKc2VnbWVudHMoeDA9dG1wLmlkLjYuZXhwb1sxXSx5MD02LjE1LHgxPXRtcC5pZC42LmV4cG9bMl0seTE9Ni4xNSxsd2Q9OCxjb2w9Im5hdnkiLGx0eT0xKQojIGRldi5vZmYoKQojIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoIi4vWWh0S3V2YXQvZXNpbWNvaC5wbmciKSkKCmBgYAoKIApgYGB7ciBrb2hvcnR0aWphYWlrYSxmaWcuY2FwPSJUb3NpYWlrYWluZW4gamEgaGlzdG9yaWFsbGluZW4gc2V1cmFudGF0dXRraW11cyIsIGVjaG8gPSBUUlVFLCBmaWcubWFyZ2luID0gRkFMU0UsbWVzc2FnZT1GQUxTRSxvdXQud2lkdGggPSAnOTAlJyxmaWcuZnVsbHdpZHRoPUZBTFNFfQpsaWJyYXJ5KERpYWdyYW1tZVIpO2xpYnJhcnkoRGlhZ3JhbW1lUnN2Zyk7bGlicmFyeShtYWdyaXR0cik7bGlicmFyeShyc3ZnKTtsaWJyYXJ5KHBuZykKCmdyVml6KHNjYW4oZmlsZT0iLi9ZaHRLdXZhdC9Lb2hvcnR0aUt1dmlvMVYyLmd2IixzZXA9IlxuIix3aGF0PSIiLHF1aWV0ID1UUlVFKSkKCmBgYAoKIAogCgoKCmBgYHtyIExleGlzS3V2YTNBLCBmaWcuY2FwPSJMZXhpcy1kaWFncmFtbWksIDEtdWxvdHRlaW5lbiwgaWvDpCBqYSBzZXVyYW50YS1haWthIiwgZWNobyA9IFRSVUUsIGZpZy5tYXJnaW4gPSBGQUxTRSxvdXQud2lkdGg9IjkwJSJ9CnJlcXVpcmUoRXBpKQoKIyBwbmcoZmlsZW5hbWUgPSIuL1lodEt1dmF0L0xleGlzS3V2YTNBLnBuZyIsd2lkdGggPSAxNTAwLGhlaWdodCA9IDE1MDAvc3FydCgyKSxwb2ludHNpemUgPSAzNiApCnZhbmhhdC5wYXI8LXBhcihtZnJvdz1jKDEsMiksbWFyPWMoNSwyLDEsMSkpCnBsb3QoZXNpbS5jb2guTHgseGxhYj0iSWvDpCIseWxhYj0iICIseWxpbT1jKDAsMTAuOSksdGltZS5zY2FsZT1jKCJhZ2UiKSxsd2Q9NCkKcG9pbnRzKGVzaW0uY29oLkx4LHBjaD1jKE5BLDE2KVtlc2ltLmNvaC5MeCRsZXguWHN0KzFdLGNvbD0idG9tYXRvNCIpClBZLmFubihlc2ltLmNvaC5MeCxjZXg9LjcsZGlnaXRzID0gMCkKCnBsb3QoZXNpbS5jb2guTHgseGxhYj0iVnVvc2kiLHlsYWI9IiAiLHlsaW09YygwLDEwLjkpLHRpbWUuc2NhbGU9YygicGVyaW9kIiksbHdkPTQpCnBvaW50cyhlc2ltLmNvaC5MeCxwY2g9YyhOQSwxNilbZXNpbS5jb2guTHgkbGV4LlhzdCsxXSxjb2w9InRvbWF0bzQiKQpQWS5hbm4oZXNpbS5jb2guTHgsY2V4PS43LGRpZ2l0cyA9IDApCnBhcih2YW5oYXQucGFyKQojIGFwdTwtZGV2Lm9mZigpCgojIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoIi4vWWh0S3V2YXQvTGV4aXNLdXZhM0EucG5nIikpCgpgYGAKCgpgYGB7ciBMZXhpc0t1dmE0QSwgZmlnLmNhcD0iTGV4aXMtZGlhZ3JhbW1pLCAyLXVsb3R0ZWluZW4sIGthbGVudGVyaXZ1b3NpLCBzZXVyYW50YS1haWthIGphIGlrw6QiLCBlY2hvID0gVFJVRSwgZmlnLm1hcmdpbiA9RkFMU0Usb3V0LndpZHRoPSI5MCUifQoKIyBwbmcoZmlsZW5hbWUgPSIuL1lodEt1dmF0L0xleGlzS3V2YTRBLnBuZyIsd2lkdGggPSAxNTAwLGhlaWdodCA9IDE1MDAvc3FydCgyKSxwb2ludHNpemUgPSAzNiApCnZhbmhhdC5wYXI8LXBhcihtZnJvdz1jKDEsMiksbWFyPWMoNSw0LDEsMCkpCnBsb3QoZXNpbS5jb2guTHgsdGltZS5zY2FsZT1jKCJwZXJpb2QiLCJhZ2UiKSx4bGFiPSJWdW9zaSIseWxhYj0iSWvDpCIsbHdkPTQpCnBvaW50cyhlc2ltLmNvaC5MeCxwY2g9YyhOQSwxNilbZXNpbS5jb2guTHgkbGV4LlhzdCsxXSxjb2w9InRvbWF0bzQiKQpQWS5hbm4oZXNpbS5jb2guTHgsY2V4PS43LGRpZ2l0cyA9IDApCgpwbG90KGVzaW0uY29oLkx4LHRpbWUuc2NhbGU9YygiZnUudGltZSIsImFnZSIpLHhsYWI9IlNldXJhbnRhLWFpa2EiLHlsYWI9Iklrw6QiLGx3ZD00KQpwb2ludHMoZXNpbS5jb2guTHgscGNoPWMoTkEsMTYpW2VzaW0uY29oLkx4JGxleC5Yc3QrMV0sY29sPSJ0b21hdG80IikKUFkuYW5uKGVzaW0uY29oLkx4LGNleD0uNyxkaWdpdHMgPSAwKQpwYXIodmFuaGF0LnBhcikKIyBhcHU8LWRldi5vZmYoKQoKIyBrbml0cjo6aW5jbHVkZV9ncmFwaGljcyhjKCIuL1lodEt1dmF0L0xleGlzS3V2YTRBLnBuZyIpKQoKYGBgCgoKICAKYGBge3Iga210ZW51cmUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSxlcnJvcj1GQUxTRSxlY2hvID0gVFJVRSxmaWcuY2FwPSJLb2tvbmFpc2t1b2xsZWlzdXVzIHN1a3VwdW9sZW4gbXVrYWFuIixmaWcuZnVsbHdpZHRoID0gRkFMU0Usb3V0LndpZHRoPSI4MCUifQpyZXF1aXJlKHN1cnZpdmFsKTtyZXF1aXJlKGdncGxvdDIpO3JlcXVpcmUoc3Vydm1pbmVyKTtyZXF1aXJlKGdnc3VydmZpdCkKbG9hZChmaWxlPSJUQURBMTEuUkRhdGEiKQojIFNlbGVjdCA1NCDigJQgVmlyZ2luaWEgZm9yIHByYWN0aWNhbHMKVEFEQS4xMS41NDwtVEFEQS4xMVtUQURBLjExJHN0YXRlcj09IjU0IixdCnN1cnZmaXQuNTQuc2V4IDwtIHN1cnZmaXQyKFN1cnYoZm9sbG93LzM2NS4yNSwgaW5kZGVhPT0iMSIpIH5zZXggLCBkYXRhPVRBREEuMTEuNTQpCgprbXRlbnVyZTwtZ2dzdXJ2Zml0KHN1cnZmaXQuNTQuc2V4LGxpbmV0eXBlX2FlcyA9IFRSVUUpKwogIGFkZF9jZW5zb3JfbWFyaygpICsKICBzY2FsZV9jb2xvcl9ncmV5KCkgKwogIHNjYWxlX2ZpbGxfZ3JleSgpICsKICBhZGRfY29uZmlkZW5jZV9pbnRlcnZhbCgpICsKICBhZGRfcXVhbnRpbGUoKSArbGFicyh4PSJBaWthIChkKSIseT0iRWxvc3NhIG9sZXZpZW5cbiBvc3V1cyIpKwogIGFkZF9yaXNrdGFibGUoc2l6ZSA9IDMuNSAsCiAgICAgICAgICAgICAgICBzdGF0c19sYWJlbD1saXN0KG4ucmlzaz0iUG9wLiBrb2tvIiwgbi5ldmVudD0iTiIpLAogICAgICAgICAgICAgICAgcmlza3RhYmxlX3N0YXRzID0gYygibi5yaXNrIiwgIm4uZXZlbnQiKSwKICAgICAgICAgICAgICAgIHRoZW1lID0gdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNCksYXhpcy50ZXh0PSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjgsMC44KSxheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksbGVnZW5kLnRleHQgPWVsZW1lbnRfdGV4dChzaXplPTgpKSkKCmttdGVudXJlCiMgZ2dzYXZlKGttdGVudXJlLGZpbGVuYW1lID0gIi4vWWh0S3V2YXQva210ZW51cmUucG5nIixkcGk9MzAwKQojIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL1lodEt1dmF0L2ttdGVudXJlLnBuZyIsKQoKCmBgYAoKIApgYGB7ciAsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSxlcnJvcj1GQUxTRSxlY2hvID0gVFJVRX0KbGlicmFyeShlcGl0b29scyk7cmVxdWlyZShjb2hvcnR0b29scykKIyBBZ2dyZWdhdGUgZXZlbnRzIGFuZCBwZXJzb24teWVhcnMgKDEwMDApCnRtcC41NC50ZW51cmU8LW1rcmF0ZXRhYmxlKFN1cnYoZm9sbG93LCBpbmRkZWE9PSIxIikgfnNleCAsIGFkZC5SUiA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9VEFEQS4xMS41NCxzY2FsZT0oMzY1LjI1KjEwMDApKQoKY29sbmFtZXModG1wLjU0LnRlbnVyZSk8LWMoIlJ5aG3DpCIsIlNldXIuYWlrYSIsIkx1a3Vtw6TDpHLDpCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJLdW9sbGVpc3V1cyIsIjk1JSBhbGFyYWphIiwiOTUlIHlsw6RyYWphIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1SUiIsIjk1JSBhbGFyYWphIiwiOTUlIHlsw6RyYWphIikKCmFwdTwtY2JpbmQoYXMuY2hhcmFjdGVyKHRtcC41NC50ZW51cmVbLDFdKSwKICAgICAgICAgICBmb3JtYXQodG1wLjU0LnRlbnVyZVssMl0sZGVjaW1hbC5tYXJrID0iLCIsZGlnaXRzPTIsbnNtYWxsID0gMiksCiAgICAgICAgICAgdG1wLjU0LnRlbnVyZVssM10sCiAgICAgIHBhc3RlMCgKZm9ybWF0KHRtcC41NC50ZW51cmVbLDRdLGRlY2ltYWwubWFyayA9IiwiLGRpZ2l0cz0yLG5zbWFsbCA9IDIpLCIgKCIsCmZvcm1hdCh0bXAuNTQudGVudXJlWyw1XSxkZWNpbWFsLm1hcmsgPSIsIixkaWdpdHM9Mixuc21hbGwgPSAyKSwiLSIsCmZvcm1hdCh0bXAuNTQudGVudXJlWyw2XSxkZWNpbWFsLm1hcmsgPSIsIixkaWdpdHM9Mixuc21hbGwgPSAyKSwiKSIpLApwYXN0ZTAoCmZvcm1hdCh0bXAuNTQudGVudXJlWyw3XSxkZWNpbWFsLm1hcmsgPSIsIixkaWdpdHM9Mixuc21hbGwgPSAyKSwiICgiLApmb3JtYXQodG1wLjU0LnRlbnVyZVssOF0sZGVjaW1hbC5tYXJrID0iLCIsZGlnaXRzPTIsbnNtYWxsID0gMiksIi0iLApmb3JtYXQodG1wLjU0LnRlbnVyZVssOV0sZGVjaW1hbC5tYXJrID0iLCIsZGlnaXRzPTIsbnNtYWxsID0gMiksIikiKQopCgpjb2xuYW1lcyhhcHUpPC1jKCJSeWhtw6QiLCJTZXVyLmFpa2EiLCJMdWt1bcOkw6Ryw6QiLCJLdW9sbGVpc3V1cyIsIk1SUiIpCgprYmwoYXB1LCBjYXB0aW9uID0gIktva29uYWlza3VvbGxlaXN1dXMuIEt1b2xsZWlkZW4gbHVrdW3DpMOkcsOkIChOKSwga2VydHlueXQgc2V1cmFudGEtYWlrYSAoMS8xMDAwKSwga3VvbGxlaXN1dXMgamEgc3VodGVlbGxpbmVuIGt1b2xlbWFuIHJpc2tpIChNUlIpIiwgCiAgICBsYWJlbD0idGVudXJlNTQiLAogICAgYm9va3RhYnMgPSBULGRpZ2l0cz0yLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpICU+JSAKICBrYWJsZV9zdHlsaW5nKCkjICU+JWNvbHVtbl9zcGVjKGJvbGQgPVRSVUUsY29sdW1uPTEpJT4lcm93X3NwZWMoYm9sZCA9VFJVRSxyb3c9MSkKCiMga25pdHI6OmthYmxlKGFwdSxjYXB0aW9uPSJLb2tvbmFpc2t1b2xsZWlzdXVzLiBLdW9sbGVpZGVuIGx1a3Vtw6TDpHLDpCAoTiksIGtlcnR5bnl0IHNldXJhbnRhLWFpa2EgKDEvMTAwMCksIGt1b2xsZWlzdXVzIGphIHN1aHRlZWxsaW5lbiBrdW9sZW1hbiByaXNraSAoTVJSKSIsZm9ybWF0ID0gImxhdGV4IikKCiMga25pdHI6OmthYmxlKHRtcC41NC50ZW51cmUsY2FwdGlvbj0iS29rb25haXNrdW9sbGVpc3V1cy4gS3VvbGxlaWRlbiBsdWt1bcOkw6Ryw6QgKE4pLCBrZXJ0eW55dCBzZXVyYW50YS1haWthICgxLzEwMDApLCBrdW9sbGVpc3V1cyIsCiMgICAgICAgICAgICAgIGRpZ2l0cz1jKDAsMiwyLDIsMiwyLDIsMiwyKSkKCiMga25pdHI6OmthYmxlKHRtcC50YWIxLGNhcHRpb249Iktva29uYWlza3VvbGxlaXN1dXMuIEt1b2xsZWlkZW4gbHVrdW3DpMOkcsOkIChOKSwga2VydHlueXQgc2V1cmFudGEtYWlrYSAoMS8xMDAwKSwga3VvbGxlaXN1dXMsIGphIHZlcnRhaWx1dC4iLAojICAgICAgICAgICAgICBkaWdpdHM9YygwLDIsMiwyLDIpKQojb3B0aW9ucyh6dGFibGUudHlwZT0idmlld2VyIikKIyBLb3JqYWEKIyBwcmludCh6dGFibGUodG1wLnRhYjEsZGlnaXRzPWMoMCwwLDIsMiwyLDIpLAojICAgICAgICAgICAgICBjYXB0aW9uPSJLb2tvbmFpc2t1b2xsZWlzdXVzLiBLdW9sbGVpZGVuIGx1a3Vtw6TDpHLDpCAoTiksIGtlcnR5bnl0IHNldXJhbnRhLWFpa2EgKDEvMTAwMCksIGt1b2xsZWlzdXVzLCBqYSB2ZXJ0YWlsdXQuIiwKIyAgICAgICAgICAgICAgYWxpZ249ImxycnJycnJyciIpKQpgYGAKCiAKYGBgCkxlcHJhLXR1dGtpbXVrc2VuIG11dXR0dWphdDoKICAKKiBhZ2U6CWlrw6QgKDUtOSwgMTAtMTQsIDE1LTE5LCAyMC0yNCwgMjUtMjksIDMwLTQ0LCBvdmVyIDQ1KQoqIHNleDoJIHN1a3VwdW9saSAobWFsZSwgZmVtYWxlKQoqIGJjZzoJQmFjaWxsdXMgQ2FsbWV0dGXigJNHdWVyaW4gKEJDRykgcm9rb3R0ZWVuIGFycGksIChubywgeWVzKQoqIHNjaG9vbDoJa291bHV0dWtzZW4gcGl0dXVzICgxLTV5cnMsIDYtOHlycywgc2VjLCB0ZXJ0KQoqIGhvdXNlOglhc3VpbnJha2VubnVrc2VuIG1hdGVyaWFhbGkgKGJyaWNrLCBzdW5icmljaywgd2F0dGxlLCB0ZW1wKQpgYGAKCiAKYGBge3IgbGVwcmExLCBlY2hvID0gVFJVRSxyZXN1bHRzPSdtYXJrdXAnLHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0V9CmxpYnJhcnkoeHRhYmxlKTtsaWJyYXJ5KGVwaXRhYik7bGlicmFyeShlcGlSKQpkYXRhKGxlcCkKbGVwJExlcHJhPC1mYWN0b3IobGVwJGQsbGV2ZWxzPWMoMSwwKSxsYWJlbD1jKCJDYXNlIiwiQ29udHJvbCIpKQpsZXAkYmNnLjE8LVJlbGV2ZWwobGVwJGJjZyxyZWY9InllcyIpCiMgbGVwLnRhYjwtd2l0aChsZXAsdHdvYnkyKGV4cG9zdXJlPWJjZy4xLG91dGNvbWU9TGVwcmEscHJpbnQgPVRSVUUpKQpsZXAudGFiPC13aXRoKGxlcCx0YWJsZShiY2cuMSxvdXRjb21lPUxlcHJhKSkKCiBlcGkuMmJ5MihkYXQgPSAgbGVwLnRhYiwgbWV0aG9kID0gImNhc2UuY29udHJvbCIsIAogICAgY29uZi5sZXZlbCA9IDAuOTUsIHVuaXRzID0gMTAwLCAgCiAgICBvdXRjb21lID0gImFzLmNvbHVtbnMiKQoKI3dpdGgobGVwLHR3b2J5MihleHBvc3VyZT1iY2cuMSxvdXRjb21lPUxlcHJhKSkKIyBwcmludCh6dGFibGUoYXB1JHRhYmxlWywxOjJdLGNhcHRpb249IlNhaXJhdXNsw6RodMO2aW5lbiB0dXRraW11cy4gTGVwcmEgamEgQkNHLWFycGkiKSxkaWdpdHM9MCwKIyAgICAgICB0eXBlPWlmZWxzZShrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSwiaHRtbCIsImxhdGV4IikpCiMgcHJpbnQoeHRhYmxlKGFwdSR0YWJsZVssMToyXSxjYXB0aW9uPSJTYWlyYXVzbMOkaHTDtmluZW4gdHV0a2ltdXMuIExlcHJhIGphIEJDRy1hcnBpIiksZGlnaXRzPTAsCiMgICAgICAgdHlwZT1pZmVsc2Uoa25pdHI6OmlzX2h0bWxfb3V0cHV0KCksImh0bWwiLCJsYXRleCIpKQojIGFwdS50YWI8LXdpdGgobGVwLGVwaXRhYih4PXRhYmxlKGJjZy4xLExlcHJhKSxtZXRob2Q9Im9kZHNyYXRpbyIpKSR0YWJbLC04XQoKIyBwcmludCh6dGFibGUoYXB1LnRhYixjYXB0aW9uPSJTYWlyYXVzbMOkaHTDtmluZW4gdHV0a2ltdXMuIExlcHJhIGphIEJDRy1hcnBpIiksZGlnaXRzPWMoMCwwLDQsMCw0LDQsNCw0KSwKIyAgICAgICB0eXBlPWlmZWxzZShrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSwiaHRtbCIsImxhdGV4IikpCiMga25pdHI6OmthYmxlKGFwdS50YWIsY2FwdGlvbj0iU2FpcmF1c2zDpGh0w7ZpbmVuIHR1dGtpbXVzLiBMZXByYSBqYSBCQ0ctYXJwaSIpLGRpZ2l0cz1jKDAsMCw0LDAsNCw0LDQsNCkpCgpgYGAKIAoKIAoKIyBLb2tlZWxsaW5lbiB0dXRraW11cyAKCmBgYHtyIHNldHVwSW50ZXJ2ZW50aW8sIGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxlcnJvcj1GQUxTRX0KdGVrc3RpLm9uLkhUTUwgPC0ha25pdHI6Ojppc19odG1sX291dHB1dCgpCnJlcXVpcmUoenRhYmxlKQpyZXF1aXJlKERpYWdyYW1tZVIpCgpvcHRpb25zKHp0YWJsZS50eXBlPSJodG1sIikKCiMgYXB1PC1yZWFkLnRhYmxlKHBpcGUoInBicGFzdGUiKSwgc2VwPSJcdCIsIGhlYWRlcj1UUlVFKQojIGhlYWQoYXB1KQojIHRhYmxlKGFwdSRYNykKc291cmNlKCJGdW5rdGlvaXRhLlIiLGVjaG8gPSBUUlVFKQpgYGAKCiAKYGBge3IgSW50ZXJ2VGF2b2l0ZUxhYXRpa2tvLCBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnLGZpZy5jYXA9IktsaWluaXNlbiBsw6TDpGtla29rZWVuIHRhdm9pdHRlaXRhLiIsZmlnLm1hcmdpbj1UUlVFLG91dC53aWR0aD0iOTklIn0KCmdyVml6KFBpaXJyYS5MYWF0aWtrb1YyKE90c2lra289IktsaWluaXNlbiBrb2tlZW48YnIvPiB0YXZvaXR0ZWV0IixLb2hkYXQgPSBjKCIgVHV0a2l0YWFuIHV1ZGVuIGzDpMOka2tlZW4gcGFyZW1tdXV0dGEgdmFuaGFhbiB2ZXJyYXR0dW5hIChzdXBlcmlvcml0eSkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIFR1dGtpdGFhbiBvdmF0a28gbMOkw6Rra2VldCB5aHTDpCBoeXZpw6QgKGVxdWl2YWxlbmNlKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIgVHV0a2l0YWFuIG9ua28gbMOkw6RrZSB0b2lzdGEgaHVvbm9tcGkgKG5vbi1pbmZlcmlvcml0eSkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIEFubm9zdmFzdGVlbiBtw6TDpHJpdHTDpG1pbmVuIChkb3NlLXJlc3BvbnNlKSIpLAogICAgICAgICAgICAgICAgICAgICAgT3RzaWtrby5zaXplID0gMTYsTC5sZXZleXMgPSAzMCwgdGFibGUuYXR0cj0nQ0VMTEJPUkRFUj0iMCIgc3R5bGU9InJvdW5kZWQiICcpKQoKIApgYGAKCiAKIAogCiAKIyBFcGlkZW1pb2xvZ2lzZW4gdHV0a2ltdWtzZW4gc3V1bm5pdHRlbHUKCiAKYGBge3IgTWFoYVN5b3BhVEssIGVjaG8gPSBUUlVFLCBmaWcuY2FwID0gJ01haGFzecO2cMOkw6RuIChJQ0QxMCBDMTYpIGt1b2xsZWV0IFN1b21lc3NhLicsZmlnLm1hcmdpbiA9IEZBTFNFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxlcnJvcj1GQUxTRX0KCiMgbGlicmFyeShweFIpCiMgYXB1Lm1zLmR0PC1hcy5kYXRhLmZyYW1lKHJlYWQucHgoZmlsZW5hbWUgPSAiaHR0cHM6Ly9weGRhdGEuc3RhdC5maTo0NDMvUHhXZWIvc3EvYzE2MzJiNWMtNWQwNS00ZGQ1LTg1MjMtY2MyMTg3YWZiYmMzIikpCiMgaHR0cHM6Ly9weGRhdGEuc3RhdC5maTo0NDMvUHhXZWIvc3EvNzM1MGY0YjEtZjMyOS00OTc0LTk0Y2QtZmQyYzYwZTM5NDAxCiMgc2F2ZShhcHUubXMuZHQsZmlsZT0iLi9ZaHREYXRhL2FwdW1zZHQuUkRhdGEiKQpsb2FkKGZpbGU9Ii4vWWh0RGF0YS9hcHVtc2R0LlJEYXRhIikKCmdncGxvdChhcHUubXMuZHQsYWVzKHg9YXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoVnVvc2kpKSx5PXZhbHVlLGNvbD1TdWt1cHVvbGksbGluZXR5cGU9U3VrdXB1b2xpICkpKwogIGdlb21fbGluZShzaXplPTAuNykrbGFicyh4PSJWdW9zaSIseT0iS3VvbGxlaWRlbiBsdWt1bcOkw6Ryw6QiKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMTUsMC4zKSxheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksbGVnZW5kLnRleHQgPWVsZW1lbnRfdGV4dChzaXplPTgpKQoKIyBrbml0cjo6aW5jbHVkZV9ncmFwaGljcyhjKCIuL1lodEt1dmF0L01haGFTeW9wYVRLLnBuZyIpLGRwaT0zMDApCgpgYGAKCgpgYGB7ciBSYXZpbnRvbGFLb25rdXJzc2l0LCBlY2hvID0gVFJVRSwgZmlnLmNhcCA9ICdSYXZpbnRvbG9pZGVuIGtvbmt1cnNzaXQga3V1a2F1c2l0dGFpbiB5cml0eXN0ZW4gaGVua2lsw7Z0ecO2dnVvc2llbiAoaHR2KSBtdWthYW4uJyxmaWcuZnVsbHdpZHRoID0gRkFMU0UsZmlnLm1hcmdpbiA9IEZBTFNFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxlcnJvcj1GQUxTRX0KCiMgYXB1LnJhdmludC5kdDwtYXMuZGF0YS5mcmFtZShyZWFkLnB4KGZpbGVuYW1lID0gImh0dHBzOi8vcHhkYXRhLnN0YXQuZmk6NDQzL1B4V2ViL3NxLzRmYWE2Y2Y4LWY1YzItNDVkYy1hODFlLTU3Zjk1MGY3NTg3MyIpKQojIGh0dHBzOi8vcHhkYXRhLnN0YXQuZmk6NDQzL1B4V2ViL3NxLzczNTBmNGIxLWYzMjktNDk3NC05NGNkLWZkMmM2MGUzOTQwMQojIGFwdS5yYXZpbnQuZHQkVnVvc2kubnVtPC13aXRoKGFwdS5yYXZpbnQuZHQsYXMubnVtZXJpYyhzdWJzdHIoS3V1a2F1c2ksMSw0KSkrYXMubnVtZXJpYyhzdWJzdHIoS3V1a2F1c2ksNiw3KSkvMTItKDEvMjQpKQojIHNhdmUoYXB1LnJhdmludC5kdCxmaWxlPSIuL1lodERhdGEvYXB1cmF2aW50ZHQuUkRhdGEiKQoKbG9hZChmaWxlPSIuL1lodERhdGEvYXB1cmF2aW50ZHQuUkRhdGEiKQoKZ2dwbG90KGFwdS5yYXZpbnQuZHQsYWVzKHg9VnVvc2kubnVtLHk9dmFsdWUgKSkrCiAgZ2VvbV9saW5lKHNpemU9MC41KStsYWJzKHg9IlZ1b3NpIix5PSJNw6TDpHLDpCBrdXVrYXVkZXNzYSAoaHR2KS4iKQoKCiMgaHR0cHM6Ly9weGRhdGEuc3RhdC5maTo0NDMvUHhXZWIvc3EvYmRkMDgyNzItY2JjZi00ZDA2LWEwOWUtMTU0NGQ3MjkyYjZlCiMga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoYygiLi9ZaHRLdXZhdC9SYXZpbnRvbGFLb25rdXJzc2l0LnBuZyIpKQoKYGBgCgoKIyAgRXBpZGVtaW9sb2dpbmVuIHDDpMOkdHRlbHkgCgogCgpgYGB7ciBLdXZhRG9sbEx1a3UySXNvS3V2YVR1cGFra2FEQUcsIGVjaG8gPSBUUlVFLCBmaWcubWFyZ2luPVRSVUUsIGZpZy5jYXAgPSAnRmlzaGVyaW4gbsOka2VteXMgdHVwYWtvaW5uaW4gamEga2V1aGtvc3nDtnbDpG4gc3VodGVlc3RhJywgb3V0LndpZHRoPSI5OSUiLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoRGlhZ3JhbW1lUik7bGlicmFyeShEaWFncmFtbWVSc3ZnKTtsaWJyYXJ5KG1hZ3JpdHRyKTtsaWJyYXJ5KHJzdmcpO2xpYnJhcnkocG5nKQoKRGlhZ3JhbW1lUjo6Z3JWaXooc2NhbigiWWh0S3V2YXQvVHVwYWtrYUZpc2hlckRBRy5ndiIsd2hhdD0iIixzZXA9IlxuIikpCiAKYGBgCiAKYGBge3IgU3lvcGFEQUdLYWtzaSwgZWNobyA9IFRSVUUsIGZpZy5jYXAgPSAnRGlhYmV0ZXMgamEgc3nDtnDDpCwgIERBRycsIGZpZy5tYXJnaW4gPSBUUlVFLG91dC53aWR0aD0iOTklIn0KIApnclZpeihzY2FuKCJLdXZhdEx1a3UyL1N5b3BhREFHS2Frc2kuZ3YiLHdoYXQ9IiIsc2VwPSJcbiIpKQogCmBgYAogICAKIAogCiAKIAoKCiMgSGFyaGF0IGphIG5paWRlbiBsw6RodGVldCAKCiAKCmBgYHtyIEx1a3U0SGFyaGFLdXZhLCBlY2hvID0gVFJVRSwgZmlnLmNhcCA9ICdIYXJoYSBqYSB0YXJra3V1cy4gUGllbmkgdGFya2t1dXMsIGhhcmhhdG9uICh2YXMuIHlsw6QpLCB0YXJra2EgamEgaGFyaGF0b24gKG9pay4geWzDpCksIHN1dXJpIHRhcmtrdXVzLCBoYXJoYWluZW4gKHZhcy4gYWxhKSwgcGllbmkgdGFya2t1dXMsIGhhcmhhaW5lbiAob2lrLiBhbGEpJyxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZmlnLm1hcmdpbiA9IFRSVUUsb3V0LndpZHRoPSI2MCUifQoKI2BgYHtyIEx1a3U0SGFyaGFLdXZhLCBlY2hvID0gVFJVRSxyZXN1bHRzPSdhc2lzJyxmaWcuY2FwID0gJ0hhcmhhIGphIHRhcmtrdXVzLiBQaWVuaSB0YXJra3V1cywgI2hhcmhhdG9uICh2YXMuIHlsw6QpLCB0YXJra2EgamEgaGFyaGF0b24gKG9pay4geWzDpCksIHN1dXJpIHRhcmtrdXVzLCBoYXJoYWluZW4gKHZhcy4gYWxhKSwgI3BpZW5pIHRhcmtrdXVzLCBoYXJoYWluZW4gKG9pay4gYWxhKSd9CgojIHBuZygiLi9ZaHRLdXZhdC9oYXJoYXRhdWx1a3V2aW8ucG5nIix3aWR0aCA9IDEwMDAsIGhlaWdodCA9IDEwMDAscG9pbnRzaXplID0gMjQpCnNldC5zZWVkKHNlZWQ9MzAxMjE5OTkpCmFwdS5oYXJoYS4xPC1saXN0KHg9cm5vcm0oMjAsc2Q9MS4yKSx5PXJub3JtKDIwLHNkPTEuMikpCmFwdS5oYXJoYS4yPC1saXN0KHg9cm5vcm0oMjAsc2Q9LjI1KSx5PXJub3JtKDIwLHNkPS4xNSkpCgphcHUuaGFyaGEuMzwtbGlzdCh4PXJub3JtKDIwLG1lYW49MS41LHNkPS4yNSkseT1ybm9ybSgyMCxtZWFuPS0uNyxzZD0uMjUpKQphcHUuaGFyaGEuNDwtbGlzdCh4PXJub3JtKDIwLG1lYW49MS41LHNkPTEpLHk9cm5vcm0oMjAsbWVhbj0tLjcsc2Q9MSkpCgpwYXIub2xkPC1wYXIobWFyPXJlcCgwLDQpLG1mcm93PWMoMiwyKSkKCndpdGgoYXB1LmhhcmhhLjEscGxvdCh4LHkscGNoPTE2LHhsaW09MC44MCpjKC02LDYpLHlsaW09MC44MCpjKC02LDYpLHhsYWI9IiIseWxhYj0iIixheGVzPUZBTFNFKSkKc3ltYm9scyh4PXJlcCgwLDUpLHk9cmVwKDAsNSksY2lyY2xlcz0xOjUsYWRkPVRSVUUsaW5jaGVzID1GQUxTRSxsd2Q9MikKCndpdGgoYXB1LmhhcmhhLjIscGxvdCh4LHkscGNoPTE2LHhsaW09MC44MCpjKC02LDYpLHlsaW09MC44MCpjKC02LDYpLHhsYWI9IiIseWxhYj0iIixheGVzPUZBTFNFKSkKc3ltYm9scyh4PXJlcCgwLDUpLHk9cmVwKDAsNSksY2lyY2xlcz0xOjUsYWRkPVRSVUUsaW5jaGVzID1GQUxTRSxsd2Q9MikKCndpdGgoYXB1LmhhcmhhLjMscGxvdCh4LHkscGNoPTE2LHhsaW09MC44MCpjKC02LDYpLHlsaW09MC44MCpjKC02LDYpLHhsYWI9IiIseWxhYj0iIixheGVzPUZBTFNFKSkKc3ltYm9scyh4PXJlcCgwLDUpLHk9cmVwKDAsNSksY2lyY2xlcz0xOjUsYWRkPVRSVUUsaW5jaGVzID1GQUxTRSxsd2Q9MikKCndpdGgoYXB1LmhhcmhhLjQscGxvdCh4LHkscGNoPTE2LHhsaW09MC44MCpjKC02LDYpLHlsaW09MC44MCpjKC02LDYpLHhsYWI9IiIseWxhYj0iIixheGVzPUZBTFNFKSkKc3ltYm9scyh4PXJlcCgwLDUpLHk9cmVwKDAsNSksY2lyY2xlcz0xOjUsYWRkPVRSVUUsaW5jaGVzID1GQUxTRSxsd2Q9MikKCnBhcigocGFyLm9sZCkpCiMgZGV2Lm9mZigpCiMga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGF0aCA9ICIuL1lodEt1dmF0L2hhcmhhdGF1bHVrdXZpby5wbmciKQpgYGAKCgoKYGBge3IgQmVyZ3NvbkhhcmhhVjIsIGVjaG8gPSBUUlVFLGZpZy5jYXAgPSAnQmVya3NvbmluIGhhcmhhJyxmaWcubWFyZ2luID0gVFJVRSxmaWcud2lkdGg9My41LCBmaWcuaGVpZ2h0PTMuNSwgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLCBmaWcuZnVsbHdpZHRoID0gVFJVRSwgY2FjaGU9VFJVRX0KbGlicmFyeShEaWFncmFtbWVSKQpsaWJyYXJ5KERpYWdyYW1tZVJzdmcpCmxpYnJhcnkobWFncml0dHIpCiNsaWJyYXJ5KHN2Z2xpdGUpCmxpYnJhcnkocnN2ZykKbGlicmFyeShwbmcpCgpnclZpeigiZGlncmFwaCBHCntub2RlIFtzaGFwZT1ib3gsIGNvbG9yPXdoaXRlLCBoZWlnaHQ9MC4xLCB3aWR0aD0wLjFdOwpyYW5rZGlyPVRCOwpDIFtjb2xvcj1ibGFjaywgaGVpZ2h0PTAuMSwgd2lkdGg9MC4xXTsKUi0+RTtFLT5DO0QtPkM7fSIpCgpgYGAKCgoKIAogCgoKCgoKCgoKCiMgRXBpZGVtaW9sb2dpc2VuIHDDpMOkdHRlbHluIGFwdW5ldXZvamEgCgoKYGBge3IgLCBlY2hvID0gVFJVRSx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoY29ob3J0dG9vbHMpCmxvYWQoIi4vWWh0RGF0YS9UQURBLjExLjU0LlJEYXRhIikKVEFEQS4xMS41NCRPbWlzdHVzPC1SZWxldmVsKFRBREEuMTEuNTQkdGVudXJlLGxpc3QoT3duZWQ9MSwyOjQpKQpUQURBLjExLjU0JE9taXN0dXM8LVJlbGV2ZWwoVEFEQS4xMS41NCRPbWlzdHVzLHJlZj0yKQpsZXZlbHMoVEFEQS4xMS41NCRPbWlzdHVzKTwtYygiTXV1IiwiT21pc3R1cyIpCmBgYAoKCmBgYHtyIGVzaW10ZW51cmUsIGVjaG8gPSBUUlVFfQp0bXAucnQ8LW1rcmF0ZXRhYmxlKFN1cnYoZm9sbG93LGFzLm51bWVyaWMoaW5kZGVhKSl+T21pc3R1cywKICAgICAgICAgICAgZGF0YT1UQURBLjExLjU0LHNjYWxlPTEwMDAqMzY1LjI1LGFkZC5SUiA9IEZBTFNFKQpUQURBLjU0Lm0wPC1nbG0oYXMubnVtZXJpYyhpbmRkZWEpfk9taXN0dXMrb2Zmc2V0KGxvZyhmb2xsb3cvMzY1LjI1KSksZmFtaWx5PXBvaXNzb24sZGF0YT1UQURBLjExLjU0KQpUQURBLjU0Lm0xPC11cGRhdGUoVEFEQS41NC5tMCx+LithZ2UpCgojIGtuaXRyOjprYWJsZSh0bXAucnQsZGlnaXRzPTMsY2FwdGlvbj0iS3VvbGxlaXN1dXMgKDEvMTAwIGhlbmtpbMO2dnVvdHRhKSBhc3Vubm9uIG9taXN0dWtzZW4gbXVrYWFuIGphb3RlbHR1bmEiKQojIHJvdW5kKGNpLmV4cChUQURBLjU0Lm0wLHN1YnNldD0iT21pc3R1cyIpLDMpCiMgcm91bmQoY2kuZXhwKFRBREEuNTQubTEsc3Vic2V0PSJPbWlzdHVzIiksMykKY29sbmFtZXModG1wLnJ0KTwtYygiIiwiSGVuay52dW9kZXQiLCJOIiwiTVIiLCIyLDUlIiwiOTcsNSUiKQprYmwodG1wLnJ0LGRpZ2l0cyA9IDMsZm9ybWF0LmFyZ3M9bGlzdChkZWNpbWFsLm1hcmsgPSAiLCIpLAogICAgY2FwdGlvbiA9ICJLdW9sbGVpZGVuIGx1a3Vtw6TDpHLDpCAoTiksIGt1b2xsZWlzdXVzIChNUikgamEgc2VuIDk1XFwlIGx1b3R0YW11c3bDpGxpICgxLzEwMCBoZW5raWzDtnZ1b3R0YSkgYXN1bm5vbiBvbWlzdHVrc2VuIG11a2FhbiBqYW90ZWx0dW5hIikgJT4la2FibGVfY2xhc3NpYygpIAoKYGBgCiAKYGBge3IgZXNpbXRlbnVyZW1hbGxpLCBlY2hvID0gVFJVRX0KdG1wLnRlbnVyZW1hbGxpPC1jaS5leHAoVEFEQS41NC5tMSxzdWJzZXQ9LTEpCmNvbG5hbWVzKHRtcC50ZW51cmVtYWxsaSk8LWMoIk1SUiIsIjIsNSUiLCI5Nyw1JSIpCmtibCh0bXAudGVudXJlbWFsbGksZGlnaXRzID0gMixmb3JtYXQuYXJncz1saXN0KGRlY2ltYWwubWFyayA9ICIsIiksCiAgICBjYXB0aW9uID0gIlN1aHRlZWxsaW5lbiBrdW9sbGVpc3V1cyAoTVJSKSBqYSBzZW4gOTVcXCUgbHVvdHRhbXVzdsOkbGkgUG9pc3NvbiByZWdyZXNzaW8gbWFsbGluIG11a2Fhbi4gT21pc3RhamlhIHZlcnJhdHR1IG11aWhpbiBoYWxsaW50YW11b3RvaWhpbiwgaWvDpCB5aHTDpAogICAgdnVvdHRhIGtvaHRpLiIpICU+JWthYmxlX2NsYXNzaWMoKSAKCmBgYCAKCmBgYHtyIGVzaW10ZW51cmVkYWcsIGVjaG8gPSBUUlVFLGZpZy5jYXA9IkFzdW1pc211b3RvIGphIGt1b2xsZWlzdXVzIixvdXQud2lkdGg9IjUwJSIsZmlnLm1hcmdpbiA9IFRSVUV9CmdyVml6KCBzY2FuKGZpbGU9Ii4vWWh0S3V2YXQvREFHQkRTaW1wbGUuZ3YiLHNlcD0iXG4iLHdoYXQ9IiIpKQogCmBgYAogCgpgYGB7ciBJVm11dXR0dWphLCBlY2hvID0gVFJVRSwgZmlnLmNhcCA9ICdOdW9saSAiYSIgb3NvaXR0YWEgYWx0aXN0ZWVuIHN1b3JhbiB5aHRleWRlbiB2YXN0ZWVzZWVuLCBqb2thIG9uIHJpaXBwdW1hdG9uIGrDpMOkbm7DtnN2YWlrdXR1c3RlbiAoVSBqYSBWKSBrb3JyZWxhYXRpb3N0YSAocikuIFZhaWt1dHVzIGhhdmFpdGFhbiBpbnN0cnVtZW50dGkgbXV1dHR1amFuIGF2dWxsYSwga29za2Egc2VuIG9sZXRldGFhbiB2YWlrdXR0YXZhbiB2YXN0ZWVzZWVuIHZhaW4gYWx0aXN0ZWVuIGthdXR0YSAoYikuJywgZmlnLm1hcmdpbiA9IFRSVUV9CgoKSVYua3V2aW88LSdkaWdyYXBoIGdyYXBobmFtZSB7CiAgICAgICAgICAgcmFua2Rpcj1UQgogICAgICAgICAgIHN0eWxlPWZpbGxlZDsKICAgICAgICAgICBjb2xvcj13aGl0ZTsKICAgICAgICAgICBJbnN0IFtsYWJlbD0iSW5zdHJ1bWVudHRpXG5tdXV0dHVqYSIgZm9udHNpemU9MjQsIHNoYXBlPWJveCwgZm9udG5hbWU9Q2FsaWJyaV0gICAgICAvLyBub2RlIFQKICAgICAgICAgICBUcmVhIFtsYWJlbD0iQWx0aXN0ZSIgZm9udHNpemU9MjQsIHNoYXBlPWJveCwgZm9udG5hbWU9Q2FsaWJyaV0gIC8vIG5vZGUgUAogICAgICAgICAgIEQgIFtsYWJlbD0iVHV0a2l0dGF2YVxudmFzdGUiICBmb250c2l6ZT0yNCwgc2hhcGU9Ym94LCBmb250bmFtZT1DYWxpYnJpXSAgLy8gbm9kZSBQCiAgICAgICAgICAgVSBbZm9udHNpemU9MTIsIGZvbnRuYW1lPUNhbGlicmldICAvLyBub2RlIFAKICAgICAgICAgICBWIFtmb250c2l6ZT0xMiwgZm9udG5hbWU9Q2FsaWJyaV0KCiAgICAgICAgICAgSW5zdC0+VHJlYSBbbGFiZWw9ImIiXTsKICAgICAgICAgICBUcmVhLT5EIFtsYWJlbD0iYSJdOwogICAgICAgICAgIFUtPlRyZWE7CiAgICAgICAgICAgVi0+RDsKICAgICAgICAgICBVLT5WIFtsYWJlbD0iXHQgciIgYXJyb3doZWFkPSJub25lIl07CnsgcmFuaz1zYW1lOyBVOyBWIH0KeyByYW5rPXNhbWU7IFRyZWE7IEQgfQogICAgICAgICAgIH0nCmdyVml6KElWLmt1dmlvKQoKYGBgCgoKTWVuZGVsLXNhdHVubmFpc3R1cyAoKk1lbmRlbGlhbiByYW5kb21pemF0aW9uKikgb24gbXV1bm5lbG1hIElNIHRla25paWthc3RhLgpTaWluw6QgSU0ga8OkeXRldMOkw6RuIGhlbmtpbMO2biBnZW5vdHl5cHBpw6QgaW5zdHJ1bWVudHRpbXV1dHR1amFuYSBbQGRhdmllc19yZWFkaW5nXzIwMThdLgoKYGBge3IgTWVuZEt1dmlvLCBlY2hvID0gVFJVRSwgZmlnLmNhcCA9ICdNZW5kZWwtc2F0dW5uYWlzdHVzLicsIGZpZy5tYXJnaW4gPSBUUlVFfQogCk1lbmQua3V2aW88LSdkaWdyYXBoIGdyYXBobmFtZSB7CiAgICAgICAgICAgcmFua2Rpcj1UQgogICAgICAgICAgIHN0eWxlPWZpbGxlZDsKICAgICAgICAgICBjb2xvcj13aGl0ZTsKICAgICAgICAgICBJbnN0IFtsYWJlbD0iQUxESDJcbmdlZW5pIiBmb250c2l6ZT0yNCwgc2hhcGU9Ym94LCBmb250bmFtZT1DYWxpYnJpXSAgICAgIC8vIG5vZGUgVAogICAgICAgICAgIFRyZWEgW2xhYmVsPSJBbGtvaG9saW5cbmt1bHV0dXMiIGZvbnRzaXplPTI0LCBzaGFwZT1ib3gsIGZvbnRuYW1lPUNhbGlicmldICAvLyBub2RlIFAKICAgICAgICAgICBEICBbbGFiZWw9IlZlcmVucGFpbmUiICBmb250c2l6ZT0yNCwgc2hhcGU9Ym94LCBmb250bmFtZT1DYWxpYnJpXSAgLy8gbm9kZSBQCgogICAgICAgICAgIEluc3QtPlRyZWEgW2xhYmVsPSJcdCBiIl07CiAgICAgICAgICAgVHJlYS0+RCBbbGFiZWw9ImEiXTsKeyByYW5rPXNhbWU7IFRyZWE7IEQgfQogICAgICAgICAgIH0nCkRpYWdyYW1tZVI6OmdyVml6KE1lbmQua3V2aW8pCiAKYGBgCgoKIAogIAogCmBgYHtyICBSRERVU0FUcmFmZmljLGVjaG8gPSBUUlVFLCBmaWcuY2FwID0gIkt1b2xsZWlzdXVzIHRpZWxpaWtlbm5lb25uZXR0b211dWtzaXNzYSBZaGR5c3ZhbGxvaXNzYSAxOTk34oCTMjAwNS4gSGF2YWlubm90IChwaXN0ZWV0KSBqYSBtYWxsaW4gZW5udXN0ZSBqYSA5NSBwcm9zZW50aW4gbHVvdHRhbXVzdsOkbGkuIiwgZmlnLm1hcmdpbiA9IEZBTFNFLGZpZy5mdWxsd2lkdGg9RkFMU0Usb3V0LndpZHRoID0gJzEwMCUnLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KbGlicmFyeShmb3JlaWduKQpsaWJyYXJ5KHJkZCkKQUVKZmlncz1yZWFkLmR0YSgiLi9ZaHREYXRhL0FFSmZpZ3MuZHRhIikKIyBBbGwgPSBhbGwgZGVhdGhzCkFFSmZpZ3MkYWdlID0gQUVKZmlncyRhZ2VjZWxsIC0gMjEKQUVKZmlncyRvdmVyMjEgPSBpZmVsc2UoQUVKZmlncyRhZ2VjZWxsID49IDIxLDEsMCkKcmVnLjE9UkRlc3RpbWF0ZShhbGx+YWdlY2VsbCxkYXRhPUFFSmZpZ3MsY3V0cG9pbnQgPSAyMSkKcGxvdChyZWcuMSkKdGl0bGUobWFpbj0iQWxsIENhdXNlcyBvZiBEZWF0aCIsIHhsYWI9IkFHRSIseWxhYj0iTW9ydGFsaXR5IHJhdGUgZnJvbSBhbGwgY2F1c2VzIChwZXIgMTAwLDAwMCkiKQpzdW1tYXJ5KHJlZy4xKQoKIyBwbmcoZmlsZT0iLi9ZaHRLdXZhdC9SRERVU0FUcmFmZmljLnBuZyIsd2lkdGg9MjQwMCxoZWlnaHQ9MjQwMC9zcXJ0KDIpLHJlcz0zMDApCnJlZy4yPVJEZXN0aW1hdGUobXZhfmFnZWNlbGwsZGF0YT1BRUpmaWdzLGN1dHBvaW50ID0yMSkKcGxvdChyZWcuMikKdGl0bGUobWFpbj0iIiwgeGxhYj0iSWvDpCIseWxhYj0iS3VvbGxlaXN1dXMgKDEvMTAwIDAwMCkiKQojIGRldi5vZmYoKQojIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoIllodEt1dmF0L1JERFVTQVRyYWZmaWMucG5nIiksZHBpPTMwMCkKYGBgCiAKIApgYGB7ciAgLGVjaG8gPSBUUlVFLCBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIEt1dmlvaWRlbiB0ZWtvCiMgY29ob3J0dG9vbHM6Omd2MmltYWdlKCBzY2FuKGZpbGU9Ii4vWWh0S3V2YXQvTWVkaWF0aW9uLmd2IixzZXA9IlxuIix3aGF0PSIiKSxmaWxlPSIuL1lodEt1dmF0L01lZGlhdGlvbiIsdHlwZSA9ICJwbmciLHdpZHRoPTUwMCkKIyBjb2hvcnR0b29sczo6Z3YyaW1hZ2UoIHNjYW4oZmlsZT0iLi9ZaHRLdXZhdC9NZWRpYXRpb25GbG93Lmd2IixzZXA9IlxuIix3aGF0PSIiKSxmaWxlPSIuL1lodEt1dmF0L01lZGlhdGlvbkZsb3ciLHR5cGUgPSAicG5nIix3aWR0aD0xMjAwKQoKIyBEb3dubG9hZCBkYXRhIG9ubGluZS4gVGhpcyBpcyBhIHNpbXVsYXRlZCBkYXRhc2V0IGZvciB0aGlzIHBvc3QuCiMgbXlEYXRhIDwtcmVhZC5jc3YoZmlsZT11cmwoJ2h0dHA6Ly9zdGF0aWMubGliLnZpcmdpbmlhLmVkdS9zdGF0bGFiL21hdGVyaWFscy9kYXRhL21lZGlhdGlvbkRhdGEuY3N2JykpCiMgc2F2ZShteURhdGEsZmlsZT0iLi9ZaHREYXRhL215RGF0YS5SRGF0YSIpCmxvYWQoZmlsZT0iLi9ZaHREYXRhL215RGF0YS5SRGF0YSIpCmBgYAoKIAogCiAKYGBge3IsIGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cgptb2RlbC5NIDwtIGxtKE0gfiBYLCBteURhdGEpIAptb2RlbC5ZIDwtIGxtKFkgfiBYICsgTSwgbXlEYXRhKSAKcmVzdWx0cyA8LSBtZWRpYXRpb246Om1lZGlhdGUobW9kZWwuTSwgbW9kZWwuWSwgdHJlYXQ9J1gnLCBtZWRpYXRvcj0nTScsIGJvb3Q9VFJVRSwgc2ltcz0xMDApIApzdW1tYXJ5KHJlc3VsdHMpCmBgYAogCiAgCiAgCgojTWV0YS1hbmFseXlzaQoKCmBgYHtyICwgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGVycm9yPUZBTFNFLGVjaG8gPSBUUlVFfQpyZXF1aXJlKG1ldGFmb3IpO29wdGlvbnMod2lkdGg9NjApCmRhdCA8LSBlc2NhbGMobWVhc3VyZSA9ICJSUiIsIGFpID0gdHBvcywgYmkgPSB0bmVnLCBjaSA9IGNwb3MsCiAgICAgICAgICAgICAgZGkgPSBjbmVnLCBkYXRhID0gZGF0LmJjZywgYXBwZW5kID0gVFJVRSkKcmVzIDwtIHJtYShhaSA9IHRwb3MsIGJpID0gdG5lZywgY2kgPSBjcG9zLCBkaSA9IGNuZWcsIGRhdGEgPSBkYXQsCiAgICAgICAgICAgbWVhc3VyZSA9ICJSUiIpCnJlcwpgYGAKIAoKYGBge3IgbWV0YWZpZzEsZmlnLmNhcD0iTWV0YS1hbmFseXlzaW4gdHVsb2tzZXQuIFBpc3RlLWVzaW1hYWF0dGlhIG1lcmtpdHNldsOkbiBtdXN0YW4gbmVsacO2biBrb2tvIG9uIHN1aHRlZXNzYSB0dXRraW11a3NlbiBrb2tvb24uIEFsaW1wYW5hIGthaWtraWVuIG9zYXR1dGtpbXVzdGVuIHlodGVpbmVuIHR1bG9zICh2aW5vbmVsacO2KS4iLGVjaG8gPSBUUlVFLGZpZy5mdWxsd2lkdGg9RkFMU0V9CiMgcG5nKGZpbGU9Ii4vWWh0S3V2YXQvbWV0YWZpZzEucG5nIix3aWR0aCA9IDEyMDAsIGhlaWdodCA9IDEyMDAscG9pbnRzaXplID0gMzApCmZvcmVzdChyZXMsIHNsYWIgPSBwYXN0ZShkYXQkYXV0aG9yLCBkYXQkeWVhciwgc2VwID0gIiwgIiksCiAgICAgICB4bGltID0gYygtMTYsIDYpLCBhdCA9IGxvZyhjKDAuMDUsIDAuMjUsIDEsIDQpKSwgYXRyYW5zZiA9IGV4cCwKICAgICAgIGlsYWIgPSBjYmluZChkYXQkdHBvcywgZGF0JHRuZWcsIGRhdCRjcG9zLCBkYXQkY25lZyksCiAgICAgICBpbGFiLnhwb3MgPSBjKC05LjUsIC04LCAtNiwgLTQuNSksIGNleCA9IDAuOSkKb3AgPC0gcGFyKGNleCA9IDAuNzUsIGZvbnQgPSAyKQp0ZXh0KGMoLTkuNSwgLTgsIC02LCAtNC41KSwgMTUsIGMoIlRCKyIsICJUQi0iLCAiVEIrIiwgIlRCLSIpKQp0ZXh0KGMoLTguNzUsIC01LjI1KSwgMTYsIGMoIlZhY2NpbmF0ZWQiLCAiQ29udHJvbCIpKQp0ZXh0KC0xNiwgMTUsICJBdXRob3IocykgYW5kIFllYXIiLCBwb3MgPSA0KQp0ZXh0KDYsIDE1LCAiUmVsYXRpdmUgUmlzayBbOTUlIENJXSIsIHBvcyA9IDIpCnBhcihvcCkKIyBhcHU8LWRldi5vZmYoKQojIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHBhdGg9Ii4vWWh0S3V2YXQvbWV0YWZpZzEucG5nIixkcGk9MzAwKQoKYGBgIAoKICAKYGBge3IgbWV0YWZ1bm5lbCxmaWcuY2FwPSJNZXRhLWFuYWx5eXNpbiB0dWxva3NldC4gU3VwcGlsb2t1dmlvLiIsZWNobyA9IFRSVUUsZmlnLmZ1bGx3aWR0aD1GQUxTRSxvdXQud2lkdGg9Ijk1JSJ9CiMgcG5nKGZpbGVuYW1lID0gIi4vWWh0S3V2YXQvTWV0YVN1cHBpbG8ucG5nIix3aWR0aCA9IDIwMDAsaGVpZ2h0ID0gMjAwMC9zcXJ0KDIpLHBvaW50c2l6ZSA9NDggKQpmdW5uZWwocmVzLHhsYWI9IkxvZyBSaXNraSBzdWhkZSIseWxhYj0iS2Vza2l2aXJoZSIpCiMgZGV2Lm9mZigpCiMga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGF0aCA9ICIuL1lodEt1dmF0L01ldGFTdXBwaWxvLnBuZyIpCmBgYAoKIAoKIyAgU3V1bm5hdHV0IHN5a2xpdHTDtm3DpHQgdmVya290ICBlcGlkZW1pb2xvZ2lhc3NhIAoKIAoKYGBge3IgREFHTW9udGEsIGVjaG8gPSBUUlVFLGZpZy5tYXJnaW4gPSBGQUxTRSxmaWcuY2FwPSdLYXVzYWFsaWRpYWdyYW1tZWphIGVsaSBzdXVubmF0dHVqYSBzeWtsaXR0w7ZtacOkIHZlcmtrb2phIChkaXJlY3RlZCBhY3ljbGljYWwgZ3JhcGggZWxpIERBRykgbWFzZW5udWtzZW4gamEgYWl2b2hhbHZhdWtzZW4geWh0ZXlkZXN0w6QuJyxmaWcuZnVsbHdpZHRoID1GQUxTRSxlcnJvcj1GQUxTRSxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoRGlhZ3JhbW1lUikKbGlicmFyeShEaWFncmFtbWVSc3ZnKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHN2Z2xpdGUpCmxpYnJhcnkocnN2ZykKbGlicmFyeShwbmcpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShnZ3Bsb3QyKQojIApwMSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoIi4vWWh0S3V2YXQvREFHQ2F1c2F0aW9uLnBuZyIsIHNjYWxlID0gMC45KQpwMiA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoIi4vWWh0S3V2YXQvREFHUmVDYXVzYXRpb24ucG5nIiwgc2NhbGUgPSAwLjkpCnAzIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZSgiLi9ZaHRLdXZhdC9EQUdDb25mb3VuZGluZy5wbmciLCBzY2FsZSA9IDAuOSkKcDQgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKCIuL1lodEt1dmF0L0RBR01pdHRhYW1pbmVuLnBuZyIsIHNjYWxlID0gMC45KQoKcHJpbnQocGxvdF9ncmlkKHAxLCBwMiwgcDMsIHA0LGxhYmVscz1jKCJBIiwiQiIsIkMiLCJEIikpKQojIGdnc2F2ZShwbG90ID0gcGxvdF9ncmlkKHAxLCBwMiwgcDMsIHA0LGxhYmVscz1jKCJBIiwiQiIsIkMiLCJEIikpLGZpbGU9Ii4vWWh0S3V2YXQvREFHQ2F1c2F0aW9uS2Fpa2tpLnBuZyIpCgojIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL1lodEt1dmF0L0RBR0NhdXNhdGlvbkthaWtraS5wbmciLGRwaT02MDApCgpgYGAKCgpgYGB7ciBEQUdFc2ltMiwgZWNobyA9IFRSVUUsZmlnLmNhcCA9ICdEQUcgZXNpbWVya2tpJyxmaWcubWFyZ2luID0gVFJVRX0KCiMgZ3YyaW1hZ2UoIHNjYW4oZmlsZT0iLi9ZaHRLdXZhdC9EYWdFc2ltMi5ndiIsc2VwPSJcbiIsd2hhdD0iIiksZmlsZT0iLi9ZaHRLdXZhdC9EYWdFc2ltMiIsdHlwZSA9ICJwbmciLHdpZHRoPTEwMDApCiMga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoYygiLi9ZaHRLdXZhdC9EYWdFc2ltMi5wbmciKSxkcGk9NjAwKQpgYGAKCiMjIERBRyBqYSBzZWtvaXR0YXZhdCB0ZWtpasOkdAoKVm9pbW1lIGVzaXR0w6TDpCBEQUc6aW4gYXZ1bGxhIGhhdmFpbm5vbGxpc2VzdGkgZXJpbGFpc2lhIG9sZXR0YW11a3NpYQphc3Nvc2lhYXRpb2lzdGEgamEga2F1c2FhbGlzdWh0ZWlzdGEuIE5lIG92YXQgbXnDtnMgaHnDtmR5bGxpc2nDpCBweXJpdHTDpGVzc8OkIG3DpMOkcml0dMOkbcOkw6RuCm1pdMOkIHNla29pdHRhdmlhIHRla2lqw7ZpdMOkICgqY29uZm91bmRlcnMqKSBvbiBvdGV0dGF2YSBodW9taW9vbiB0aWxhc3RvbGxpc2lzc2EgbWFsbGVpc3NhLgoKYGBge3IgLHJlc3VsdHM9J2hpZGUnLGVjaG8gPSBUUlVFfQojIAojIGd2MmltYWdlKCBzY2FuKGZpbGU9Ii4vWWh0S3V2YXQvREFHQ2F1c2F0aW9uLmd2IixzZXA9IlxuIix3aGF0PSIiKSxmaWxlPSIuL1lodEt1dmF0L0RBR0NEQUdDYXVzYXRpb24iLHR5cGUgPSAicG5nIix3aWR0aD0xMDAwKQojIAojIGd2MmltYWdlKCBzY2FuKGZpbGU9Ii4vWWh0S3V2YXQvREFHUmVDYXVzYXRpb24uZ3YiLHNlcD0iXG4iLHdoYXQ9IiIpLGZpbGU9Ii4vWWh0S3V2YXQvREFHQ0RBR1JlQ2F1c2F0aW9uIix0eXBlID0gInBuZyIsd2lkdGg9MTAwMCkKIyAKIyBndjJpbWFnZSggc2NhbihmaWxlPSIuL1lodEt1dmF0L0RBR0NvbmZvdW5kaW5nLmd2IixzZXA9IlxuIix3aGF0PSIiKSxmaWxlPSIuL1lodEt1dmF0L0RBR0NvbmZvdW5kaW5nIix0eXBlID0gInBuZyIsd2lkdGg9MTAwMCkKIyAKIyBndjJpbWFnZSggc2NhbihmaWxlPSIuL1lodEt1dmF0L0RBR01pdHRhYW1pbmVuLmd2IixzZXA9IlxuIix3aGF0PSIiKSxmaWxlPSIuL1lodEt1dmF0L0RBR01pdHRhYW1pbmVuIix0eXBlID0gInBuZyIsd2lkdGg9MTAwMCkKIyAKZ3JWaXooIHNjYW4oZmlsZT0iLi9ZaHRLdXZhdC9EQUdTZXRzLmd2IixzZXA9IlxuIix3aGF0PSIiKSkKCgpgYGAKCgoKIAoKYGBge3IgREFHU2V0cywgZWNobyA9IFRSVUUsZmlnLm1hcmdpbiA9IEZBTFNFLGZpZy5jYXA9IkRBRyBqYSBzZWtvaXR0YXZhdCB0ZWtpasOkdC4iLCBmaWcuZnVsbHdpZHRoID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRX0KZ3JWaXooIHNjYW4oZmlsZT0iLi9ZaHRLdXZhdC9EQUdCRC5ndiIsc2VwPSJcbiIsd2hhdD0iIikpCgpgYGAKCgogCgpgYGB7ciBEQUdFc2ltUmVhbCwgZmlnLm1hcmdpbiA9IEZBTFNFLGZpZy5mdWxsd2lkdGggPUZBTFNFLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVycm9yPVRSVUUsZmlnLmNhcD0iR2xhdWtvb21hbiBpbG1hYW50dXZ1dXRlZW4gdmFpa3V0dGF2aWEgdGVraWrDtml0w6QuIixlY2hvID0gVFJVRX0KbGlicmFyeShkYWdpdHR5KQpnMSA8LSBkYWdpdHR5KCAiZGFnIHsKICAgIElrYSAtPiBHbGF1a29vbWEKICAgIElrYSAtPiBETQogICAgRE0gLT4gR2xhdWtvb21hCiAgICBTdGF0aWluaXQgLT4gR2xhdWtvb21hCiAgICBTdGF0aWluaXQgLT4gRE0KICAgIERNIC0+IFNTUkkKICAgIFNTUkkgLT4gR2xhdWtvb21hCiAgICBCZWV0YS5zYWxwYWFqYSAtPiBHbGF1a29vbWEKICAgIEthbHNpdW0uZXN0YWphIC0+IEdsYXVrb29tYQp9IikKIyBwbmcoZmlsZT0iLi9ZaHRLdXZhdC9EQUdnMS5wbmciLHdpZHRoPTE1MDAscG9pbnRzaXplID0gMzYpCiMgcGxvdChncmFwaExheW91dChnMSkpCiMgZGV2Lm9mZigpCiMga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vWWh0S3V2YXQvREFHZzEucG5nIixkcGk9MzAwKQojIApnMS4xPC1hcy5jaGFyYWN0ZXIoZzEpCmcxLjE8LWdzdWIoeD1nMS4xLHBhdHRlcm4gPSAiZGFnIixyZXBsYWNlbWVudCA9ICJkaWdyYXBoIEcgIikKZzEuMTwtZ3N1Yih4PWcxLjEscGF0dGVybiA9ICJ7IixyZXBsYWNlbWVudCA9ICd7IG5vZGUgW3NoYXBlPWJveF07ICcsZml4ZWQgPSBUUlVFKQpnMS4xPC1nc3ViKHg9ZzEuMSxwYXR0ZXJuID0gIi4iLHJlcGxhY2VtZW50ID0gIl8iLGZpeGVkID0gVFJVRSkKRGlhZ3JhbW1lUjo6Z3JWaXooZzEuMSkKIyBjb2hvcnR0b29sczo6Z3YyaW1hZ2UoZzEuMSxmaWxlPSIuL1lodEt1dmF0L0RBR2cxVjEiLHR5cGUgPSAicG5nIix3aWR0aD0xMDAwKQoKIyBrbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9ZaHRLdXZhdC9EQUdnMVYxLnBuZyIsZHBpPTMwMCkKCmBgYAogCmBgYHtyLGVjaG8gPSBUUlVFLHJlc3VsdHM9J2FzaXMnfQpwcmludChhZGp1c3RtZW50U2V0cyh4PWcxLCBleHBvc3VyZT0iRE0iLCBvdXRjb21lPSJHbGF1a29vbWEiLCBlZmZlY3Q9ImRpcmVjdCIpKQpgYGAKCiAgCmBgYHtyIGtub3hkYWcsIGZpZy5tYXJnaW4gPSBGQUxTRSxmaWcuZnVsbHdpZHRoID1GQUxTRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSJLbm94IGV0IGFsLiBEQUcgcG9saWlzaXbDpGtpdmFsbGFzdGEuIixlY2hvID0gVFJVRSxvdXQud2lkdGg9IjEwMCUifQpsaWJyYXJ5KGdnZGFnKQoKIyBiaWdnZXJfZGFnIDwtIGRhZ2lmeSh5IH4geCArIGEgKyBiLAojICAgeCB+IGEgKyBiLAojICAgZXhwb3N1cmUgPSAieCIsCiMgICBvdXRjb21lID0gInkiLAojICAgbGFiZWxzID0gYygieSI9IlZhc3RlIikKIyApCiMgCiMgZ2dkYWdfYWRqdXN0bWVudF9zZXQoYmlnZ2VyX2RhZykKCiMgdGhlbWVfYmFyZSA8LSB0aGVtZSgKIyAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiMgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiMgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIAojICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCAKIyAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiMgICAjYXhpcy50aWNrcy5sZW5ndGggPSB1bml0KDAsICJsaW5lcyIpLCAjIEVycm9yIAojICAgYXhpcy50aWNrcy5tYXJnaW4gPSB1bml0KGMoMCwwLDAsMCksICJsaW5lcyIpLCAKIyAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiMgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheSIpLCAKIyAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgCiMgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKIyAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAojICAgcGFuZWwubWFyZ2luID0gdW5pdChjKDAsMCwwLDApLCAibGluZXMiKSwgCiMgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJibHVlIiksCiMgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLDAsMCwwKSwgImxpbmVzIikKIyApCgpmb3JjZV9kYWcgPC0gZGFnaWZ5KGZvcmNlIH4gc3VzcGljaW91cytzdG9wK21pbm9yaXR5LAogIHN0b3AgfiBtaW5vcml0eStzdXNwaWNpb3VzLAogIGxhYmVscyA9IGMoCiAgICAibWlub3JpdHkiID0gIkt1dWx1dVxudsOkaGVtbWlzdMO2w7ZuIiwKICAgICJmb3JjZSIgPSAiVm9pbWFua8OkeXR0w7YiLAogICAgInN0b3AiID0gIlB5c8OkeXR5cyIsCiAgICAic3VzcGljaW91cyIgPSAiRXDDpGlseXMiCiAgKSwKICAjIGxhdGVudCA9ICJ1bmhlYWx0aHkiLAogIGV4cG9zdXJlID0gIm1pbm9yaXR5IiwKICBvdXRjb21lID0gImZvcmNlIgopCgpnZ3Bsb3QoZm9yY2VfZGFnLGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCkpICsKICBnZW9tX2RhZ19wb2ludCgpICsKICBnZW9tX2RhZ19lZGdlcygpICsKICAjIGdlb21fZGFnX3RleHQoKSArCiAgZ2VvbV9kYWdfbGFiZWxfcmVwZWwoYWVzKGxhYmVsID0gbGFiZWwpLGNvbCA9ICJibGFjayIsIHNob3cubGVnZW5kID0gRkFMU0UpKwogIHRoZW1lX2RhZygpCgojIGdnZGFnKGZvcmNlX2RhZywgdGV4dCA9IEZBTFNFLCB1c2VfbGFiZWxzID0gImxhYmVsIikrbGFicyh4PSIiLHk9IiIpKwojICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAojICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiMgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKIyBnZ2RhZ19hZGp1c3RtZW50X3NldChmb3JjZV9kYWcsdGV4dCA9IEZBTFNFLHVzZV9sYWJlbHMgPSAibGFiZWwiLAojICAgICAgICAgICAgICAgICAgICAgIHNoYWRvdyA9IFRSVUUpIyt0aGVtZSh0ZXh0ICx0ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siKSkKCgpgYGAKCiAKIyBFcMOkdmFybXV1ZGVuIG1pdHRhYW1pbmVuICAKIAoKYGBge3IgS3V2YTNMdWt1NCwgZWNobyA9IFRSVUUsIGZpZy5jYXAgPSAnTm9ybWFhbGlqYWthdW1hIGphIHNlbiAyLDVcXCUgamEgOTcsNVxcJSBwaXN0ZWV0LicsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSxmaWcubWFyZ2luID0gVFJVRSxmaWcud2lkdGg9My41LCBmaWcuaGVpZ2h0PTMuNSwgY2FjaGU9VFJVRX0KcmVxdWlyZShrbml0cikKIyBUcmFuc3BhcmVudCBjb2xvcnMKYWRkVHJhbnMgPC0gZnVuY3Rpb24oY29sb3IsdHJhbnMpCnsKICAjIFRoaXMgZnVuY3Rpb24gYWRkcyB0cmFuc3BhcmFuY3kgdG8gYSBjb2xvci4KICAjIERlZmluZSB0cmFuc3BhcmFuY3kgd2l0aCBhbiBpbnRlZ2VyIGJldHdlZW4gMCBhbmQgMjU1CiAgIyAwIGJlaW5nIGZ1bGx5IHRyYW5zcGFyYW50IGFuZCAyNTUgYmVpbmcgZnVsbHkgdmlzYWJsZQogICMgV29ya3Mgd2l0aCBlaXRoZXIgY29sb3IgYW5kIHRyYW5zIGEgdmVjdG9yIG9mIGVxdWFsIGxlbmd0aCwKICAjIG9yIG9uZSBvZiB0aGUgdHdvIG9mIGxlbmd0aCAxLgogIAogIGlmIChsZW5ndGgoY29sb3IpIT1sZW5ndGgodHJhbnMpJiFhbnkoYyhsZW5ndGgoY29sb3IpLGxlbmd0aCh0cmFucykpPT0xKSkgc3RvcCgiVmVjdG9yIGxlbmd0aHMgbm90IGNvcnJlY3QiKQogIGlmIChsZW5ndGgoY29sb3IpPT0xICYgbGVuZ3RoKHRyYW5zKT4xKSBjb2xvciA8LSByZXAoY29sb3IsbGVuZ3RoKHRyYW5zKSkKICBpZiAobGVuZ3RoKHRyYW5zKT09MSAmIGxlbmd0aChjb2xvcik+MSkgdHJhbnMgPC0gcmVwKHRyYW5zLGxlbmd0aChjb2xvcikpCiAgCiAgbnVtMmhleCA8LSBmdW5jdGlvbih4KQogIHsKICAgIGhleCA8LSB1bmxpc3Qoc3Ryc3BsaXQoIjAxMjM0NTY3ODlBQkNERUYiLHNwbGl0PSIiKSkKICAgIHJldHVybihwYXN0ZShoZXhbKHgteCUlMTYpLzE2KzFdLGhleFt4JSUxNisxXSxzZXA9IiIpKQogIH0KICByZ2IgPC0gcmJpbmQoY29sMnJnYihjb2xvciksdHJhbnMpCiAgcmVzIDwtIHBhc3RlKCIjIixhcHBseShhcHBseShyZ2IsMixudW0yaGV4KSwyLHBhc3RlLGNvbGxhcHNlPSIiKSxzZXA9IiIpCiAgcmV0dXJuKHJlcykKfQoKIyBOb3JtYWwKdG1wLng8LXNlcShmcm9tPS0zLHRvPTMsbGVuZ3RoPTUwKQojcG5nKGZpbGU9IkM6L0hZLURhdGEvSktIQVVLS0EvT25lRHJpdmVGb3JCdXNpbmVzcy9PbmVEcml2ZSBmb3IgQnVzaW5lc3MvT3BldHVzL0VwaUtpcmphL01TL0t1dmF0THVrdTQvbm9ybUNJLnBuZyIsCiMgICAgcG9pbnRzaXplID0gMTgpCnBhcihtYXI9Yyg0LDQsMiwxKSkKcGFyKG1haT1jKDEuMDIsMC44MiAsMC44MiwgMC40MikpCmN1cnZlKGRub3JtLGZyb209LTMsdG89Myxsd2Q9Mixjb2w9Im5hdnkiLHlsYWI9IlRpaGV5c2Z1bmt0aW9uIGFydm8iLHhsYWI9IiIseWxpbT1jKDAsLjQwKSkKcG9seWdvbih4PWModG1wLngpLHk9Yyhkbm9ybSh0bXAueCkpLGNvbD1hZGRUcmFucygiYmx1ZSIsMjApKQphYmxpbmUodj1jKDAscW5vcm0oYygwLjAyNSwwLjk3NSkpKSxsdHk9MikKdGV4dCh4PXFub3JtKGMoMC4wMjUsMC45NzUpKSx5PWMoMC4xNSwwLjE1KSxyb3VuZChxbm9ybShjKDAuMDI1LDAuOTc1KSksNCksc3J0PTkwLGNleD0xLjQpCiNkZXYub2ZmKCkKYWJsaW5lKHY9cW5vcm0oYygwLjAyNSwwLjk3NSkpLGx0eT0yKQoKcmVxdWlyZShFcGkpCgojIE0/P3JpdGV0Pz9uIGtvaGRlcG9wdWxhYXRpbwojIEZFVjEgPC0gcmVhZC50YWJsZSgiaHR0cDovL2Jpb3N0YXQubWMudmFuZGVyYmlsdC5lZHUvd2lraS9wdWIvTWFpbi9EYXRhU2V0cy9GRVYuY3N2IiwKIyAgICAgICAgICAgICAgICAgICBoZWFkZXI9VFJVRSwgc2VwPSIsIiwgbmEuc3RyaW5ncz0iTkEiLCBkZWM9Ii4iLCBzdHJpcC53aGl0ZT1UUlVFKQojIGFwdS5waXR1dWRldDwtcm91bmQoMi41NCpGRVYxJGhlaWdodFsxOjEwXSwxKQojIGR1bXAob2JqZWN0cygxLHBhdHQ9ImFwdS5waXR1dWRldCIpLGZpbGU9IiIpCgojIFQ/bT8gbGFza2VlIGtlc2tpYXJ2b24ga2Vza2l2aXJoZWVuCmZ1bi5zZSA8LSBmdW5jdGlvbih4KSBzcXJ0KHZhcih4KS9sZW5ndGgoeCkpCgojIEtvaGRlam91a2tvCmVzaW0ucGl0dXVzPC1jKDE0NC44LCAxNzEuNCwgMTM4LjQsIDEzNC42LCAxNDQuOCwgMTU0LjksIDE0Ny4zLCAxNDIuMiwgMTQ4LjYsIDE1Mi40KQoKc2V0LnNlZWQoc2VlZD0zMTEyMTk5OSkKYXB1LjE8LXQoc2FwcGx5KDE6MTAsZnVuY3Rpb24oeCkKICB7bHYuMTwtc2FtcGxlKDEwLDUpCiAgYyhtZWFuKGVzaW0ucGl0dXVzW2x2LjFdKSxmdW4uc2UoZXNpbS5waXR1dXNbbHYuMV0pKQp9KSkKCmFwdS4xPC1jYmluZChhcHUuMSxhcHUuMVssMV0rcW5vcm0oMC4wMjUpKmFwdS4xWywyXSxhcHUuMVssMV0rcW5vcm0oMS0wLjAyNSkqYXB1LjFbLDJdKQpjb2xuYW1lcyhhcHUuMSk8LWMoImthIiwiU0UiLCJhbGEiLCJ5bGEiKQpgYGAKCgpgYGB7ciBlc2ltb3Rvc3RhdWx1LCBlY2hvID0gVFJVRX0KYXB1MS4xLnVsb3M8LWNiaW5kKGZvcm1hdChhcHUuMVssMV0sZGVjaW1hbC5tYXJrID0iLCIsbnNtYWxsID0gMSxkaWdpdHM9MiksCiAgICAgIGZvcm1hdChhcHUuMVssMV0sZGVjaW1hbC5tYXJrID0iLCIsbnNtYWxsID0gMSxkaWdpdHM9MiksCiAgICAgIHBhc3RlMCgiKCIsZm9ybWF0KGFwdS4xWywzXSxkZWNpbWFsLm1hcmsgPSIsIixuc21hbGwgPSAxLGRpZ2l0cz0yKSwiLSIsCiAgICAgICAgICAgICBmb3JtYXQoYXB1LjFbLDRdLGRlY2ltYWwubWFyayA9IiwiLG5zbWFsbCA9IDEsZGlnaXRzPTIpLCIpIikKKQpjb2xuYW1lcyhhcHUxLjEudWxvcyk8LWMoImthIiwiU0UiLCI5NSUgTFYiKSAgICAgICAgICAgIAojIGtuaXRyOjprYWJsZShhcHUuMSxkaWdpdHM9MSxjYXB0aW9uPSJLeW1tZW5lbiAoTj01KSBvdG9rc2VuIGtlc2tpYXJ2byAoa2EpLGtlc2tpdmlyaGUgKFNFKSBqYSA5NVxcJSBsdW90dGFtdXN2w6RsaS4gUGVydXNqb3Vrb24ga2Vza2lhcnZvIG9uIDE0Nyw5LiIsZm9ybWF0LmFyZ3M9bGlzdChkZWNpbWFsLm1hcmsgPSIsIikpCgprbml0cjo6a2FibGUoYXB1MS4xLnVsb3MsY2FwdGlvbj0iS3ltbWVuZW4gKE49NSkgb3Rva3NlbiBrZXNraWFydm8gKGthKSxrZXNraXZpcmhlIChTRSkgamEgOTVcXCUgbHVvdHRhbXVzdsOkbGkuIFBlcnVzam91a29uIGtlc2tpYXJ2byBvbiAxNDcsOS4iKQpgYGAKCiAKYGBge3IsIGVjaG8gPSBUUlVFLHJlc3VsdHM9J21hcmt1cCcsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLHNjcmVlbnNob3QuZm9yY2UgPSBUUlVFfQojVGF1bHVra28gMiBLYWhkZW4gaGFwZW4gYW5ub3N0ZWx1dGF2YW4gdmVydGFpbHUgdGVob2hvaWRvc3NhLCBrbGlpbmluZW4ga29lIGpvc3NhIHDDpMOkdGVtdXV0dHVqYW5hIG9saSAja3VvbGxlaXN1dXMoMikuICAKcmVxdWlyZShlcGlSKQpyZXF1aXJlKGVwaXRvb2xzKQpyZXF1aXJlKEVwaSkKIyBHaXJhcmRpcyBNLCBCdXNhbmkgUywgRGFtaWFuaSBFLCBldCBhbC4gRWZmZWN0IG9mIENvbnNlcnZhdGl2ZSB2cyBDb252ZW50aW9uYWwKIyBPeHlnZW4gVGhlcmFweSBvbiBNb3J0YWxpdHkgQW1vbmcgUGEtdGllbnRzIGluIGFuIEludGVuc2l2ZSBDYXJlIFVuaXQ6IFRoZQojIE94eWdlbi1JQ1UgUmFuZG9tLWl6ZWQgQ2xpbmljYWwgVHJpYWwuIEpBTUEgUHVibGlzaGVkIE9ubGluZSBGaXJzdDogNSBPY3RvYmVyCiMgMjAxNi4gZG9pOjEwLjEwMDEvamFtYS4yMDE2LjExOTkzCgp0bXAuaWN1PC1tYXRyaXgoYyhhID0gMjUsIGIgPSAyMTYtMjUsIGMgPSA0NCwgZCA9IDIxOC00NCksCiAgICAgICAgICAgICAgICBkaW1uYW1lcz1saXN0KGMoIktvbnNlcnZhdGlpdmluZW4iLCJUYXZhbm9tYWluZW4iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiS3VvbGx1dCIsIkVsb3NzYSIpKSwKICAgICAgICAgICAgICAgIG5jb2w9MixieXJvdz1UUlVFKQp0d29ieTIodG1wLmljdSkKI2tuaXRyOjprYWJsZSh0bXAuaWN1LCBjYXB0aW9uPSJLYWhkZW4gaGFwZW4gYW5ub3N0ZWx1dGF2YW4gdmVydGFpbHUgdGVob2hvaWRvc3NhLCBrbGlpbmluZW4ga29lIGpvc3NhIHDDpMOkdGVtdXV0dHVqYW5hIG9saSBcXGxhYmVse3RhYjppY3V9IikKYGBgCgogCgojIEh5cG90ZWVzaSwgdGVzdGFhbWluZW4gamEgUC1hcnZvCgoKCmBgYHtyIEZFVnRpaGV5cywgcmVzdWx0cz0nYXNpcycsZWNobyA9IFRSVUUsZmlnLm1hcmdpbiA9IEZBTFNFLGZpZy5jYXA9IlR5dHTDtmplbiBqYSBwb2lraWVuIChpa8OkIDctOCkgc3Bpcm9tZXRyaWEgbWl0dGF1c3RlbiBqYWthdW1hLiIsd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQpyZXF1aXJlKGdncGxvdDIpCiNGRVYxIDwtIHJlYWQudGFibGUoImh0dHA6Ly9iaW9zdGF0Lm1jLnZhbmRlcmJpbHQuZWR1L3dpa2kvcHViL01haW4vRGF0YVNldHMvRkVWLmNzdiIsIAojICAgIGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgbmEuc3RyaW5ncyA9ICJOQSIsIGRlYyA9ICIuIiwgc3RyaXAud2hpdGUgPSBUUlVFKQpGRVYxIDwtIHJlYWQudGFibGUoZmlsZT0iLi9ZaHREYXRhL0ZFVi5jc3YiLCAKICAgIGhlYWRlciA9IFRSVUUsIHNlcCA9ICIsIiwgbmEuc3RyaW5ncyA9ICJOQSIsIGRlYyA9ICIuIiwgc3RyaXAud2hpdGUgPSBUUlVFKQojIHAgIDwtIGdncGxvdChGRVYxLCBhZXMoZmV2LCBjb2xvdXI9c2V4LCBmaWxsPXNleCkpKyBnZW9tX2RlbnNpdHkoYWxwaGE9MC41NSxzaG93LmxlZ2VuZCA9IEZBTFNFKStzY2FsZV9maWxsX21hbnVhbCgiU1AiLHZhbHVlcz1jKCJvcmFuZ2UiLCJyZWQiKSkKcCAgPC0gZ2dwbG90KHN1YnNldChGRVYxLGFnZSVpbiVjKDcsOCkpLCBhZXMoKGZldiksIGNvbG91cj1zZXgsIGZpbGw9c2V4KSkKIysgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5hbWU9IlNQIikpCiMrIApwK2xhYnMoeD0iRkVWIix5PSJUaWhleXNmdW5rdGlvIikgKyBnZW9tX2RlbnNpdHkoYWxwaGE9MC4zNSkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjE1LDAuNyksYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLGxlZ2VuZC50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZT04KSkKIyB3aXRoKHN1YnNldChGRVYxLGFnZSVpbiVjKDcsOCkpLHNoYXBpcm8udGVzdChmZXYpKQpgYGAKCiAKYGBge3J9CnQudGVzdChmZXYgfiBzZXgsZGF0YT1zdWJzZXQoRkVWMSxhZ2UlaW4lYyg3LDgpKSkKYGBgCiAKCgoKIyMgQmF5ZXMtcMOkw6R0dGVseXN0w6QgamEgUC1hcnZvaXN0YQoKIyMjIFAtYXJ2byBqYSBub2xsYWh5cG90ZWVzaW4gdG9kZW5uw6Rrw7Zpc3l5cwoKUC1hcnZvYSBlaSB2b2lkYSBzdW9yYWFuIHR1bGtpdGEgdG9kZW5uw6Rrw7Zpc3l5ZGVrc2ksIG11dHRhIHNlbiBhdnVsbGEgCnZvaWRhYW4gdGlldHlpbiBvbGV0dWtzaW4gbGFza2VhIG5vbGxhaHlwb3RlZXNpaW4gCnRvZGVubsOka8O2aXN5eXMgW0BzZWxsa2VfY2FsaWJyYXRpb25fMjAwMTtAZ29vZG1hbl9wLXZhbHVlc18yMDAxO0BoYXVra2FfZW5lbW1hbl8yMDE1XS4KCkVuc2luIG3DpMOkcml0ZXTDpMOkbiBtaW5pbWkgQmF5ZXMtZmFrdG9yaSAoKm1pbmltdW0gQmF5ZXMgZmFjdG9yKiwgKkJGKikgUC1hcnZvbGxlICgkcCQpLCBrYWF2YWxsYSBcZXFyZWZ7a2FhdmFCRn0uCgpcYmVnaW57ZXF1YXRpb259CkJGKHApID0gCiAgXGJlZ2lue2Nhc2VzfSAKICAgLWVcY2RvdCBwXGNkb3QgbG9nKHApICYgXHRleHR7LCBqb3MgcDwxL2UgZWxpIHA8MCwzNjc4Nzk0IH0gIFxcCiAgIDEgICAgICAgJiBcdGV4dHssIG11dWxsb2luIH0gCiAgXGVuZHtjYXNlc30KXGxhYmVse2thYXZhQkZ9ClxlbmR7ZXF1YXRpb259CgoKQkY6biBhdnVsbGEgdm9pZGFhbiBtw6TDpHJpdHTDpMOkIFAtYXJ2b2EgdmFzdGFhdmEgbWluaW1pIHBvc3RlcmlvcmkgdG9kZW5uw6Rrw7Zpc3l5cyBrYWF2YWxsYSBcZXFyZWZ7a2FhdmFtaW5wb3N0fSwgCmt1biAkcSQgb24gYW5uZXR0dSBwcmlvcmkgdmV0b3N1aGRlLgoKClxiZWdpbntlcXVhdGlvbn0KXHRleHR7bWluaW1pIHBvc3Rlcmlvcml9PVxsZWZ0KCAxKyBcbGVmdChCRlxjZG90IHFccmlnaHQpXnstMX0gXHJpZ2h0KV57LTF9ClxsYWJlbHtrYWF2YW1pbnBvc3R9ClxlbmR7ZXF1YXRpb259CgpKb3Mgc2lpcyAkSF8wJCBqYSAkSF8xJCBwaWRldMOkw6RuCmVubmVuIHR1dGtpbXVrc2VuIHRvdGV1dHRhbWlzdGEgamEgYWluZWlzdG9uIGtlcsOkw6RtaXN0w6QgeWh0w6QgdG9kZW5uw6Rrw7Zpc2luw6Qgb24gCnByaW9yaSB2ZXRvc3VoZGUgMSAoJFxmcmFjezAsNX17MCw1fSQpLApqb3MgdGFhcyBvbGV0ZXRhYW4gJEhfMCQ6biAgb2xldmFuIDEwIGtlcnRhYSB0b2Rlbm7DpGvDtmlzZW1waSBrdWluICRIXzEkOm4gYXNldGV0YWFuIHByaW9yaSB2ZXRvc3VodGVla3NpIDEwLgpOw6RpbiB0dXRraWphIHZvaSBhc2V0dGFhIHRhcmthbiBhcnZvbiBvbWFsbGUgYXJ2aW9sbGVlbi4KCgpgYGB7ciwgcmVzdWx0cz0nYXNpcycsZWNobz1GQUxTRSx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CnJlcXVpcmUoa2FibGVFeHRyYSkKIyBNaW5pbXVtIHBvc3RlcmlvciBwcm9iLgojIFNlbGxrZSBULCBCYXlhcnJpIE1KLCBCZXJnZXIgSk8uIENhbGlicmF0aW9uIG9mIFAgVmFsdWVzIGZvciBUZXN0aW5nIFByZWNpc2UgTnVsbCBIeXBvdGhlc2VzLiBUaGUgQW1lcmljYW4gU3RhdGlzdGljaWFuIDIwMDE7NTU6NjIuCnRtcC5CRjwtZnVuY3Rpb24ocCl7CiAgaWYocDwoMS9leHAoMSkpKXtsdi5iZjwtIC1leHAoMSkqcCpsb2cocCl9CiAgZWxzZSB7bHYuYmY8LTF9CiAgbHYuYmYKfQoKIyB0bXAuQkYoMC4wNSkKIyB0bXAuTVBQKDAuNSx0bXAuQkYoMC4wNSkpCiMgdG1wLk1QUDwtZnVuY3Rpb24ocSxCRil7CiMgICAjIHByaW50KHEvKDEtcSkpCiMgICAxLygxKyAxLyhCRioocS8oMS1xKSkpKQojIH0KIyAKIyB0bXAuTVBQLnZldG8odG1wLkJGKDAuMDUpKQojIHRtcC5NUFAudmV0byh0bXAuQkYoMC4wNSksMSkKCnRtcC5NUFAudmV0bzwtZnVuY3Rpb24oQkYscHJpb3IudmV0bz0xKXsKICAxLygxKyAxLyhCRipwcmlvci52ZXRvKSkKfQoKCnRtcC5wPC1jKDAuMDUsMC4wMSwwLjAwNSwwLjAwMSwwLjAwMDEpCnRtcC5wcmlvci52ZXRvPC1jKDEsNSwxMCw1MCwxMDApCnRtcC5NUFAudGFibGU8LWNiaW5kKHNhcHBseSh0bXAucHJpb3IudmV0byx0bXAuTVBQLnZldG8sQkY9dG1wLkJGKHRtcC5wWzFdKSksCiAgICAgICAgICAgICAgICAgICAgIHNhcHBseSh0bXAucHJpb3IudmV0byx0bXAuTVBQLnZldG8sQkY9dG1wLkJGKHRtcC5wWzJdKSksCiAgICAgICAgICAgICAgICAgICAgIHNhcHBseSh0bXAucHJpb3IudmV0byx0bXAuTVBQLnZldG8sQkY9dG1wLkJGKHRtcC5wWzNdKSksCiAgICAgICAgICAgICAgICAgICAgIHNhcHBseSh0bXAucHJpb3IudmV0byx0bXAuTVBQLnZldG8sQkY9dG1wLkJGKHRtcC5wWzRdKSksCiAgICAgICAgICAgICAgICAgICAgIHNhcHBseSh0bXAucHJpb3IudmV0byx0bXAuTVBQLnZldG8sQkY9dG1wLkJGKHRtcC5wWzVdKSkpCmRpbW5hbWVzKHRtcC5NUFAudGFibGUpPC1saXN0KGZvcm1hdCh0bXAucHJpb3IudmV0byxkaWdpdHM9MiksZm9ybWF0KHRtcC5wLG5zbWFsbCA9IDQsc2NpZW50aWZpYz1GQUxTRSkpCgojIAojIGtuaXRyOjprYWJsZSh0bXAuTVBQLnRhYmxlLGRpZ2l0cyA9IDQsY2FwdGlvbj0iTWluaW1pIHBvc3RlcmlvcmkgdG9kZW5uw6Rrw7Zpc3l5cyBub2xsYWh5cG90ZWVzaWxsZSBlcmkgcC1hcnZvaWxsYSAoc2FyYWtrZWV0KSBqYSBlcmkgcHJpb3JpIHRvZGVubsOka8O2aXN5eWtzaWxsw6QgKHEpIikKcm93bmFtZXModG1wLk1QUC50YWJsZSk8LWdzdWIoeD1yb3duYW1lcyh0bXAuTVBQLnRhYmxlKSxwYXR0ZXJuID0gIi4iLHJlcGxhY2VtZW50ID0gIiwiLGZpeGVkID0gVFJVRSkKY29sbmFtZXModG1wLk1QUC50YWJsZSk8LWdzdWIoeD1jb2xuYW1lcyh0bXAuTVBQLnRhYmxlKSxwYXR0ZXJuID0gIi4iLHJlcGxhY2VtZW50ID0gIiwiLGZpeGVkID0gVFJVRSkKCmtibCh0bXAuTVBQLnRhYmxlLGRpZ2l0cz00LGNhcHRpb249Ik1pbmltaSBwb3N0ZXJpb3JpdG9kZW5uw6Rrw7Zpc3l5cyBub2xsYWh5cG90ZWVzaWxsZSBlcmkgcC1hcnZvaWxsYSAoc2FyYWtrZWV0KSBqYSBlcmkgcHJpb3JpIHZldG9rZXJ0b2ltaWxsZS4iLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcms9IiwiKSwKICAgIGxhYmVsPSJNUFB0YXVsdSIpICU+JQogIHJvd19zcGVjKDAsYm9sZCA9IFRSVUUpJT4lCiAgY29sdW1uX3NwZWMoMSxib2xkID0gVFJVRSklPiUKICBrYWJsZV9jbGFzc2ljKCkgCiMgcGFja19yb3dzKCJQcmlvcmkgKHEpIiwgMSw5KSAKCmBgYCAKCgpUYXVsdWtvc3RhIFxyZWZ7dGFiOk1QUHRhdWx1fSBoYXZhaXRhYW4sIGV0dMOkIGVzaW0uIFAtYXJ2b2EgMCwwNSB2YXN0YWF2YSBub2xsYWh5cG90ZWVzaW4gcG9zdGVyaW9yaXRvZGVubsOka8O2aXN5eXMgCm9uIDAsMjg5MyBrdW4gcHJpb3JpIHZldG9zdWhkZSBvbiAxLgpUb2lzw61uIHNhbm9lbiwgam9zIHBpZMOkbW1lIGVubmVuIHR1dGtpbXVzdGEgbm9sbGFoeXBvdGVlc2lhICgkSF8wJCkgCmphIHZhaWh0b2VodG9hICgkSF8xJCkgeWh0w6QgdG9kZW5uw6Rrw7Zpc2luw6QsIG5paW4gaGF2YWl0dHVhbW1lIHAtYXJ2b24gMCwwNSBvbiAkSF8wJCB0b2Rlbm7DpGvDtmlzeXlzIGFsZW50dW51dCAyOCw5JTppaW4uClTDpG3DpCB0YXJrb2l0dGFhLCBldHTDpCBqb3MgJEhfMSQgb2xpc2kga2xpaW5pc2VuIHR1dGtpbXVrc2VuIGtvaHRlZW5hIG9sZXZhIHV1c2kKbMOkw6RrZSwgam90YSB2ZXJyYXRhYW4gdmFuaGFhbiAoJEhfMCQpLCBzZSBvbGlzaSB2YWluIDcxLDElICgkMTAwXGNkb3QoMS0wLDI4OTMpJCkgdG9kZW5uw6Rrw7Zpc3l5ZGVsbMOkIHZhbmhhYSBwYXJlbXBpLgpUw6R0w6QgdHVza2luIHZvaWRhYW4gcGl0w6TDpCBrb3ZpbiB2YWh2YW5hIG7DpHl0dMO2bsOkICRIXzAkIHZhc3RhYW4uClZhc3RhYXZhc3RpLCBqb3MgaGF2YWl0c2VtbWUgcC1hcnZvbiAwLDAwNSBvbiAgJEhfMCQgdG9kZW5uw6Rrw7Zpc3l5cyA2LDclIGVsaQp1dXNpIGzDpMOka2Ugb2xpc2kgOTMsMiUgdG9kZW5uw6Rrw7Zpc3l5ZGVsbMOkIHZhbmhhYSBwYXJlbXBpLiAKVMOkc3PDpCBvbiBzb3ZlbGxldHR1IDItc3V1bnRhaXN0YSB0ZXN0acOkIGphIG9sZXRldHR1LCBldHTDpCB2YWlrdXR1a3NlbiBzdXVudGEgb24gb2RvdGV0dHUuCgpKb3MgZW5uYWtrbyBvbGV0dWtzZW1tZSBvbiBoaWVtYW4gdmFyb3ZhaXNlbXBpIGphIG9sZXRhbW1lLCBldHTDpCAkSF8wJCBvbiAxMCBrZXJ0YWEgdG9kZW5uw6Rrw7Zpc2VtcGkga3VpbiAKJEhfMDEkIGphIGhhdmFpdGFhbiBwLWFydm8gMCwwMDUsIG9uICRIXzAkIHRvZGVubsOka8O2aXN5eXMgdmllbMOkIHZhcnNpbiBzdXVyaSBlbGkgNDEsOCUuCgpOw6RpdMOkIFAtYXJ2b2plbiBwZXJ1c3RlZWxsYSBsYXNrZXR0dWphIHRvZGVubsOka8O2aXN5eWtzacOkIGt1dHN1dGFhbiBrYWxpYnJvaWR1aWtzaSAKUC1hcnZvaWtzaSAoKmNhbGlicmF0ZWQgcCB2YWx1ZXMqKS5caW5kZXh7e1AtYXJ2b30he2thbGlicm9pdHV9fQoKIAogCgogCiAKIAoKYGBge3IsIHJlc3VsdHM9J21hcmt1cCcsZWNobz1UUlVFfQojIEVzaW1lcmtraSBvdG9za29vbiBsYXNrZW1pc2VzdGEgCiMgT2xldHVrc2V0Cm11QT00O211Qj0xMDtrYXBwYT0xO3NkPTEwO2FscGhhPTAuMDU7YmV0YT0wLjIwCiMgT3Rvc2tvb24gbGFza2VtaW5lbgpuQjwtKDErMS9rYXBwYSkqKHNkKihxbm9ybSgxLWFscGhhLzIpK3Fub3JtKDEtYmV0YSkpLyhtdUEtbXVCKSleMgp6PShtdUEtbXVCKS8oc2Qqc3FydCgoMSsxL2thcHBhKS9uQikpClBvd2VyPXBub3JtKHotcW5vcm0oMS1hbHBoYS8yKSkrcG5vcm0oLXotcW5vcm0oMS1hbHBoYS8yKSkKY2F0KCIkXFxtdV9BJD0iLG11QSwiICRcXG11X0IkPSIsbXVCLAogICAgIiAkXFxrYXBwYSQ9IixrYXBwYSwiIHNkPSIsc2QsCiAgICAiXG4gJFxcYWxwaGEkPSIsYWxwaGEsIiAkXFxiZXRhJD0iLAogICAgYmV0YSwgIiAkbl9CJD0iLGNlaWxpbmcobkIpLCIgVm9pbWFra3V1cz0iLHJvdW5kKFBvd2VyLDMpKQpgYGAgCgogCmBgYHtyLCBlY2hvID0gVFJVRX0KCnBBPTAuMDU7cEI9MC4xMCAjIGthaGRlbiByeWhtw6RuIHRvZGVubsOka8O2aXN5eWRldAprYXBwYT0xICMgbW9sZW1waW5uIHJ5aG1paW4gc2FtYSBsdWt1bcOkw6Ryw6QKYWxwaGE9MC4wNTtiZXRhPTAuMjAgIyBJLXR5eXBpbiB2aXJoZTsgSUktdHl5cGluIHZpcmhlCm5CPShwQSooMS1wQSkva2FwcGErcEIqKDEtcEIpKSooKHFub3JtKDEtYWxwaGEvMikrcW5vcm0oMS1iZXRhKSkvKHBBLXBCKSleMgp6PShwQS1wQikvc3FydChwQSooMS1wQSkvbkIva2FwcGErcEIqKDEtcEIpL25CKQpjYXQoIk90b3Nrb2tvPSIsY2VpbGluZyhuQiksIlZvaW1ha2t1dXM9Iixyb3VuZChwbm9ybSh6LXFub3JtKDEtYWxwaGEvMikpK3Bub3JtKC16LXFub3JtKDEtYWxwaGEvMikpLDIpLCJUeXlwaW4gSSB2aXJoZT0iLGFscGhhLCJcbiIpCgpgYGAKCgoKIyBSZWdyZXNzaW9hbmFseXlzaQoKCgoKCgoKYGBge3IgMDdSZWdLdXZhMSxmaWcud2lkdGg9NywgZmlnLmhlaWdodD02LGVjaG89dGVrc3RpLm9uLkhUTUwsZmlnLmNhcD0iScOkbiBqYSBzcGlyb21ldHJpbWl0dGF1a3NlbiB2w6RsaW5lbiB5aHRleXMsIGxpbmVhYXJpbmVuIHJlZ3Jlc3Npb3N1b3JhLiBLdXZpb29uIG9uIG1lcmtpdHR5IGrDpMOkbm7DtmtzZXQgcHlzdHl2aWl2b2luLiJ9CiMgRkVWIGRhdGEKI0ZFVjEgPC0gcmVhZC50YWJsZSgiaHR0cDovL2Jpb3N0YXQubWMudmFuZGVyYmlsdC5lZHUvd2lraS9wdWIvTWFpbi9EYXRhU2V0cy9GRVYuY3N2IiwKIyAgICAgICAgICAgICAgICAgICBoZWFkZXI9VFJVRSwgc2VwPSIsIiwgbmEuc3RyaW5ncz0iTkEiLCBkZWM9Ii4iLCBzdHJpcC53aGl0ZT1UUlVFKQojRkVWMSRoZWlnaHQuY208LTIuNTQqRkVWMSRoZWlnaHQKI3NhdmUoRkVWMSxmaWxlPSJGRVYxLlJEYXRhIikKIyBGaXJzdCB0aHJlZSBsaW5lcyBvZiBkYXRhCmxvYWQoZmlsZT0iRkVWMS5SRGF0YSIpCiMgTWFsbGkgZXNpbWVya2tpCnBsb3QoZmV2fmFnZSxkYXRhPUZFVjEseGxhYj0iSWvDpCAodikiLHlsYWI9IkZFViAodGlsYXZ1dXMgTC9zKSIpCiMgTGluZWFhcmluZW4gcmVncmVzc2lvbWFsbGkKTDMubWFsbGkwPC1sbShmZXZ+YWdlLGRhdGE9RkVWMSkKYWJsaW5lKEwzLm1hbGxpMCxsd2Q9Mixjb2w9Im5hdnkiKQojIFBpaXJyZXTDpMOkbiB5a3NpIGrDpMOkbm7DtnMKI3NldC5zZWVkKDEyMzQ1Nik7dG1wLm90b3M8LXNhbXBsZShucm93KEZFVjEpLDE1KTsKdG1wLm90b3M8LShhYnMocmVzaWR1YWxzKEwzLm1hbGxpMCkpPjEuMikKd2l0aChGRVYxW3RtcC5vdG9zLF0sc2VnbWVudHMoYWdlLGZldixhZ2UsZmV2LXJlc2lkdWFscyhMMy5tYWxsaTApW3RtcC5vdG9zXSxjb2w9InRvbWF0bzIiLGx3ZD0yKSkKCmBgYAogCmBgYHtyIDA3UmVnTWFsbGkxQixlY2hvPXRla3N0aS5vbi5IVE1MLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxoaWdobGlnaHQ9VFJVRX0KIyBrbml0cjo6a2FibGUoRXBpOjpjaS5saW4oTDMubWFsbGkwKSxjYXB0aW9uPSJNYWxsaW4ga2VydG9pbWV0IGphIDk1XFwlIGx1b3R0YW11c3bDpGxpIFxcbGFiZWx7dGFiOjA3UmVnTWFsbGkxQn0uIixkaWdpdHM9NCkKCmtibChFcGk6OmNpLmxpbihMMy5tYWxsaTApLGNhcHRpb249Ik1hbGxpbiBrZXJ0b2ltZXQgamEgOTVcXCUgbHVvdHRhbXVzdsOkbGkgXFxsYWJlbHt0YWI6MDdSZWdNYWxsaTFCfS4iLAogICAgbGFiZWw9IjA3UmVnTWFsbGkxQiIsCiAgICBib29rdGFicyA9IFQsZGlnaXRzPTQsZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9IiwiKSkgJT4lIAogIGthYmxlX3N0eWxpbmcoKSAlPiVjb2x1bW5fc3BlYyhib2xkID1UUlVFLGNvbHVtbj0xKSMlPiVyb3dfc3BlYyhib2xkID1UUlVFLHJvdz0xKQoKCgpgYGAKCiAKYGBge3IgMDdSZWdNYWxsaTJCLGVjaG89dGVrc3RpLm9uLkhUTUwsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGhpZ2hsaWdodD1UUlVFfQojIE11dXRhbWEgcml2aSBhaW5laXN0b3N0YQojIEZFVjFbYygxLDQsNTIyKSxdCiMgTWFsbGkgam9zc2EgaWvDpCBqYSB0dXBha29pbnRpIHNlbGl0dMOkamluw6QKTDMubWFsbGkxPC1sbShmZXZ+YWdlK3Ntb2tlLGRhdGE9RkVWMSx4PVRSVUUpCnN1bW1hcnkoTDMubWFsbGkxKQpgYGAKIAogCgpgYGB7ciAsZWNobyA9IFRSVUUsZmlnLmNhcD0iIn0KbGlicmFyeShnZ3Bsb3QyKQojbG9hZCh1cmwoImh0dHBzOi8vaGJpb3N0YXQub3JnL2RhdGEvcmVwby9uaGdoLnJkYSIpKQojc2F2ZShuaGdoLGZpbGU9Im5oZ2guUkRhdGEiKQojIGxvYWQoZmlsZT0ibmhnaC5SRGF0YSIpCiMgbmhnaCR0eC5mPC1mYWN0b3IobmhnaCR0eCxsZXZlbHM9YygwLDEpLGxhYmVsPWMoImVpIiwib24iKSkKIyBzZXQuc2VlZCgxMTIyMzM0NCk7bmhnaC4xPC1uaGdoW3NhbXBsZShzZXEobnJvdyhuaGdoKSksNTAwKSxdIyBOPTUwMCBvdG9zCiMgcDEgPC0gZ2dwbG90KG5oZ2guMSwgYWVzKHggPSBibWksIHkgPSBnaCkpCiMgcDI8LXAxK3NjYWxlX2NvbG91cl9tYW51YWwobmFtZT0iRGlhYmV0ZXNcbmzDpMOka2UiLCAgdmFsdWVzID1jKCJ0dXJxdW9pc2UiLCJyZWQiKSkKIyBwMzwtcDIgKyBnZW9tX3BvaW50KGFlcyhjb2xvcj10eC5mKSxhbHBoYT0wLjYsc2l6ZT0xLHNob3cubGVnZW5kPVRSVUUpIAojIHAzCmBgYAoKYGBge3IsZWNobz10ZWtzdGkub24uSFRNTCxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZXZhbD10ZWtzdGkub24uSFRNTH0KIyBsaWJyYXJ5KHBsb3RseSkKIyAjIExldCdzIG1ha2UgaXQgaW50ZXJhY3RpdmUgdXNpbmcgdGhlIGdncGxvdGx5IGZ1bmN0aW9uICEKIyBwNDwtZ2dwbG90bHkocDMpCiMgcDQKYGBgCgogCgpgYGB7ciAsZWNobz10ZWtzdGkub24uSFRNTCxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsaGlnaGxpZ2h0PVRSVUV9CmxvYWQoZmlsZT0ibmhnaC5SRGF0YSIpCm5oZ2gkdHguZjwtZmFjdG9yKG5oZ2gkdHgsbGV2ZWxzPWMoMCwxKSxsYWJlbD1jKCJlaSIsIm9uIikpCnNldC5zZWVkKDExMjIzMzQ0KQpuaGdoLjE8LW5oZ2hbc2FtcGxlKHNlcShucm93KG5oZ2gpKSw1MDApLF0jIE49NTAwIG90b3MKCnRtcC5tMzwtbG0oZ2h+Ym1pKnR4LmYsZGF0YT1uaGdoLjEpCnN1bW1hcnkodG1wLm0zKQpgYGAKCiAKYGBge3IgeWhkeXN2YWlrdXR1c2t1dmEsZWNobz10ZWtzdGkub24uSFRNTCxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsaGlnaGxpZ2h0PVRSVUUsZmlnLmNhcD0iUGFpbm9pbmRla3NpbiAoQk1JKSBqYSBzb2tlcmloZW1vYmxvYmlpbmluIChIYkExKSBqYSB2w6RsaW5lbiB5aHRleXMuIn0KCnAxIDwtIGdncGxvdChuaGdoLjEsIGFlcyh4ID0gYm1pLCB5ID0gZ2gpKQojIHAyPC1wMStzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWU9IkRpYWJldGVzXG5sw6TDpGtlIiwgIHZhbHVlcyA9YygibmF2eSIsInJlZCIpKQojIHAzPC1wMiArIGdlb21fcG9pbnQoYWVzKGNvbG9yPXR4LmYsbGluZXR5cGU9dHguZiksYWxwaGE9MC42LHNpemU9MSxzaG93LmxlZ2VuZD1UUlVFKQoKdG1wLmQwPC1kYXRhLmZyYW1lKGJtaT1uaGdoLjEkYm1pLHR4LmY9ZmFjdG9yKHJlcCgwLG5yb3cobmhnaC4xKSksbGV2ZWxzPWMoMCwxKSxsYWJlbD1jKCJlaSIsIm9uIikpKQp0bXAuZDE8LWRhdGEuZnJhbWUoYm1pPW5oZ2guMSRibWksdHguZj1mYWN0b3IocmVwKDEsbnJvdyhuaGdoLjEpKSxsZXZlbHM9YygwLDEpLGxhYmVsPWMoImVpIiwib24iKSkpCnRtcC54MDwtcHJlZGljdCh0bXAubTMsbmV3ZGF0YSA9IHRtcC5kMCkKdG1wLngxPC1wcmVkaWN0KHRtcC5tMyxuZXdkYXRhID0gdG1wLmQxKQpwMjwtcDErCiAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lPSJEaWFiZXRlcyIsICB2YWx1ZXMgPWMoIm5hdnkiLCJyZWQiKSkrCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz0xOjIpCnAyICsgZ2VvbV9wb2ludChhZXMoY29sb3I9dHguZiksYWxwaGE9MC42LHNpemU9Ljcsc2hvdy5sZWdlbmQ9VFJVRSkrCiAgZ2VvbV9saW5lKGFlcyh5ID0gdG1wLngxKSxjb2xvcj0icmVkIixsaW5ldHlwZT0xKSsKICBnZW9tX2xpbmUoYWVzKHkgPSB0bXAueDApLGNvbG9yPSJuYXZ5IixsaW5ldHlwZT0yKSsKICBsYWJzKHg9IkJNSSIseT0iSGJBMSIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LDAuOCksbGVnZW5kLnRleHQgPWVsZW1lbnRfdGV4dChzaXplPTYpLGxlZ2VuZC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9NikpCiAKYGBgCiAgIAoKIApgYGB7ciByZWdtdXV0dHVqYW11dW5ub3MsZWNobyA9IFRSVUUsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGhpZ2hsaWdodD1UUlVFLGZpZy5jYXA9IlBhaW5vaW5kZWtzaW4gKEJNSSkgamEgacOkbiB2w6RsaW5lbiB5aHRleXMuIixmaWcubWFyZ2luID0gRkFMU0V9CnAxIDwtIGdncGxvdChuaGdoLjEsIGFlcyh4ID0gYWdlLCB5ID1ibWkpKSsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj10eC5mKSxhbHBoYT0wLjYsc2l6ZT0wLjcsc2hvdy5sZWdlbmQ9VFJVRSkKcDErZ2VvbV9zbW9vdGgoZm9ybXVsYSA9IHkgfiB4K0koeF4yKSxtZXRob2QgPSAibG0iKSsKICBsYWJzKHg9Iklrw6QgKHYpIix5PSJCTUkiLGNvbG9yPSJETSBsw6TDpGtlIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICBsZWdlbmQudGV4dCA9ZWxlbWVudF90ZXh0KHNpemU9NiksbGVnZW5kLnRpdGxlID1lbGVtZW50X3RleHQoc2l6ZT02KSkKCgpgYGAgCgogCmBgYHtyICwgZXJyb3I9RkFMU0UsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLHJlc3VsdHM9J21hcmt1cCd9CnRtcC5tNDwtbG0oYm1pIH4gYWdlK0koYWdlXjIpLCBkYXRhID0gbmhnaC4xKQpwcmludChzdW1tYXJ5KHRtcC5tNCkpCmBgYAogCiAKIApgYGB7ciBsb2dpdGt1dmEsZWNobz10ZWtzdGkub24uSFRNTCxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsaGlnaGxpZ2h0PVRSVUUsZmlnLmNhcD0iTG9naXN0aW5lbiBtdXVubm9zIixvdXQud2lkdGg9Ijk1JSIsZmlnLm1hcmdpbiA9IEZBTFNFfQojTG9naXQgbGluawoKdG1wLng8LXNlcSgwLjAxLDAuOTksbGVuZ3RoPTEwMCkKIyBwbG90KHRtcC54LGxvZyh0bXAueC8oMS10bXAueCkpLHR5cGU9ImwiLHhsYWI9InAiLHlsYWI9ImxvZyhwLygxLXApKSIsbHdkPTIsY29sPSJuYXZ5IikgCiMgYWJsaW5lKGg9MCx2PTAuNSxsdHk9MikKCnRtcC5kdC5sPC1kYXRhLmZyYW1lKHg9dG1wLngseT1sb2codG1wLngvKDEtdG1wLngpKSkKbGlicmFyeShsYXRleDJleHApCgpnZ3Bsb3QodG1wLmR0LmwsYWVzKHg9eCx5PXkpKStnZW9tX2xpbmUoKSsKICBsYWJzKHg9InAiLHk9VGVYKHNwcmludGYoIiRsb2dpdChwKT1cXGxvZyhcXGZyYWN7cH17MS1wfSkkIikpKSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLGxpbmV0eXBlPTIpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAuNSxsaW5ldHlwZT0yKQoKYGBgCiAKIApgYGB7ciAsZWNobz10ZWtzdGkub24uSFRNTCxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsaGlnaGxpZ2h0PVRSVUV9CnRtcC5tLnR4PC1nbG0odHggfiBzZXgrYm1pLCBkYXRhID0gbmhnaCxmYW1pbHk9Ymlub21pYWwobG9naXQpKQpzdW1tYXJ5KHRtcC5tLnR4KQpgYGAKIAoKYGBge3IgLGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxoaWdobGlnaHQ9VFJVRSxmaWcuY2FwPSJMb2dpc3Rpc2VuIG1hbGxpbiBlbm51c3RlIn0KdG1wLm5kPC1kYXRhLmZyYW1lKGJtaT1zZXEoMTUsNDAsYnk9MSkpCnRtcC5uZCRzZXg8LWZhY3RvcihyZXAoMSxucm93KHRtcC5uZCkpLGxldmVscz1jKDEsMiksbGFiZWw9bGV2ZWxzKG5oZ2gkc2V4KSkKYXB1LnByZWQ8LXByZWRpY3QodG1wLm0udHgsbmV3ZGF0YT10bXAubmQsdHlwZT0icmVzcG9uc2UiLHNlLmZpdD1UUlVFKQp0bXAubmQkZW5udXN0ZTwtYXB1LnByZWQkZml0CnRtcC5uZCRsdi5hbGE8LWFwdS5wcmVkJGZpdC0gKDEuOTYqYXB1LnByZWQkc2UuZml0KQp0bXAubmQkbHYueWxhPC1hcHUucHJlZCRmaXQrICgxLjk2KmFwdS5wcmVkJHNlLmZpdCkKCiMgd2l0aCh0bXAubmQscGxvdChibWksMTAwKmVubnVzdGUsdHlwZT0ibCIseGxhYj0iQk1JIix5bGFiPSJEaWFiZXRlcyBsw6TDpGtlIChcXCUpIixsd2Q9Mixjb2w9Im5hdnkiKSkKIyB3aXRoKHRtcC5uZCxsaW5lcyhibWksMTAwKmx2LmFsYSxsd2Q9MSxjb2w9Im5hdnkiLGx0eT0yKSkKIyB3aXRoKHRtcC5uZCxsaW5lcyhibWksMTAwKmx2LnlsYSxsd2Q9MSxjb2w9Im5hdnkiLGx0eT0yKSkKIyBhYmxpbmUodj1jKDI1LDM1KSxoPTEwMCp0bXAubmQkZW5udXN0ZVt0bXAubmQkYm1pJWluJWMoMjUsMzUpXSxsdHk9NSkKCnAxPC1nZ3Bsb3QodG1wLm5kLGFlcyh4PWJtaSx5PTEwMCplbm51c3RlKSkreGxhYigiQk1JIikreWxhYigiRGlhYmV0ZXMgbMOkw6RrZSAoJSkiKQpwMStnZW9tX2xpbmUoKStnZW9tX2xpbmUoYWVzKHg9Ym1pLHk9MTAwKmx2LmFsYSksbHR5PTIpK2dlb21fbGluZShhZXMoeD1ibWkseT0xMDAqbHYueWxhKSxsdHk9MikKICAgICAgICAgICAgICAgICAgICAgICAgIApgYGAgCiAgCgpgYGB7cixyZXN1bHRzPSdhc2lzJyxlY2hvID0gVFJVRX0KcmVxdWlyZShFcGkpCnRtcC5vcjwtY2kuZXhwKHRtcC5tLnR4LHN1YnNldD0tMSkKY29sbmFtZXModG1wLm9yKTwtYygiT1IiLCJhbGEgOTUgcHJvIiwieWzDpCA5NSBwcm8iKQojIGtuaXRyOjprYWJsZSh0bXAub3IsZGlnaXQ9Myxmb3JtYXQuYXJncyA9bGlzdChkZWNpbWFsLm1hcmsgPSIsIikgLAojICAgICAgICAgICAgICBjYXB0aW9uPSJWZXRvc3VodGVldCBqYSA5NVxcJSBMViIpCgprYmwodG1wLm9yLCBjYXB0aW9uID0gIlZldG9zdWh0ZWV0IGphIDk1XFwlIExWIiwgbGFiZWw9InZldG9zdWhkZTEiLAogICAgYm9va3RhYnMgPSBULGRpZ2l0cz0yLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpICMlPiUgCiAgIyBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkjJT4lcm93X3NwZWMoYm9sZCA9VFJVRSxyb3c9MSkKCgpgYGAKIAogIApgYGB7ciBPUmphUlIsZWNobz10ZWtzdGkub24uSFRNTCxtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsaGlnaGxpZ2h0PVRSVUUsZmlnLmNhcD0iUmlza2lzdWh0ZWVuIChSUikgamEgdmV0b3N1aHRlZW4gKE9SKSB5aHRleXMgcHJldmFsZW5zc2luIGVyaSBhcnZvaWxsYSAoZXJpIHZpaXZhdHl5cGl0KSIsb3V0LndpZHRoPSIxMDAlIixmaWcubWFyZ2luID0gRkFMU0UsZmlnLmZ1bGx3aWR0aCA9IEZBTFNFfQoKI1JSIC0+T1IKIyB0bXAuUlIuZnVuPC0gZnVuY3Rpb24oT1IsTDAsTDEpe09SKigxLUwxKS8oMS1MMCl9CnRtcC5PUi5mdW48LSBmdW5jdGlvbihSUixMMCxMMSl7bHYuMTwtUlIqKDEtTDApLygxLUwxKTtpZmVsc2UobHYuMTwwLE5BLGx2LjEpfQoKdG1wLlJSPC1zZXEoMSw5LGxlbmd0aD0xMDApCnRtcC5kdC5SUjwtZGF0YS5mcmFtZShSUj1yZXAodG1wLlJSLDYpLAogICAgICAgICAgICAgICAgICAgICAgT1I9Yyh0bXAuT1IuZnVuKHRtcC5SUixyZXAoMC4wMSw5KSxyZXAoMC4wMSw5KSp0bXAuUlIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0bXAuT1IuZnVuKHRtcC5SUixyZXAoMC4wNSw5KSxyZXAoMC4wNSw5KSp0bXAuUlIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0bXAuT1IuZnVuKHRtcC5SUixyZXAoMC4xLDkpLHJlcCgwLjEsOSkqdG1wLlJSKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdG1wLk9SLmZ1bih0bXAuUlIscmVwKDAuMiw5KSxyZXAoMC4yLDkpKnRtcC5SUiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRtcC5PUi5mdW4odG1wLlJSLHJlcCgwLjMsOSkscmVwKDAuMyw5KSp0bXAuUlIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0bXAuT1IuZnVuKHRtcC5SUixyZXAoMC41LDkpLHJlcCgwLjUsOSkqdG1wLlJSKQogICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgIEluY2k9ZmFjdG9yKHJlcChjKCIwLDAxIiwiMCwwNSIsIjAsMSIsIjAsMiIsIjAsMyIsIjAsNSIpLGVhY2g9MTAwKSkKKQpnZ3Bsb3QodG1wLmR0LlJSLGFlcyh4PVJSLHk9T1IsbGluZXR5cGU9SW5jaSkpK2dlb21fbGluZSgpKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxLDkpLHlsaW0gPSBjKDEsMTApKSsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsaW50ZXJjZXB0ID0gMCxzaXplPTEsY29sPSJuYXZ5IikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0icmlnaHQiLGxlZ2VuZC50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZT02KSwKICAgICAgICBsZWdlbmQudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTYpKSsKICBsYWJzKGxpbmV0eXBlPSJQcmV2YWxlbnNzaSIpCiAgIyB0aGVtZShsZWdlbmQucG9zaXRpb24gPWMoMC44LDAuMyksbGVnZW5kLnRleHQgPWVsZW1lbnRfdGV4dChzaXplPTYpKSsjLGxlZ2VuZC5rZXkgPWVsZW1lbnRfdGV4dChzaXplPTYpKSsKICAjIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LDAuNDUpLGxlZ2VuZC50ZXh0ID1lbGVtZW50X3RleHQoc2l6ZT02KSxsZWdlbmQua2V5ID1lbGVtZW50X3RleHQoc2l6ZT02KSApKwogIAojIGxlZ2VuZC5rZXkuaGVpZ2h0PSB1bml0KDAuMDEsICducGMnKSxsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjA0LCAnbnBjJykpKwoKYGBgCgogCgpgYGB7cixlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2hpZGUnfQojIGxpYnJhcnkoc2FuZHdpY2gpCiMgbGlicmFyeShsbXRlc3QpCiMgbGlicmFyeShFcGkpCgojIEZ1bmN0aW9uIHRoYXQgY2FsY3VsYXRlcyBPUgojIG1rLk9SLnBvaWxvZzwtZnVuY3Rpb24oeCxhbHBoYT0wLjA1KQojICAgewojICAgbHYuMTwtcW5vcm0oMS1hbHBoYS8yKQojICAgbHYuMjwtZXhwKGNiaW5kKHhbLCJFc3RpbWF0ZSJdLHhbLCJFc3RpbWF0ZSJdLXhbLCJTdGQuIEVycm9yIl0qbHYuMSwKIyAgICAgICAgICAgICAgICAgICB4WywiRXN0aW1hdGUiXSt4WywiU3RkLiBFcnJvciJdKmx2LjEgKSkKIyAgIGNvbG5hbWVzKGx2LjIpPC1jKCJPUiIscGFzdGUoYygibG8iLCJoaSIpLDEtYWxwaGEsc2VwPSIuIikpCiMgICBsdi4yCiMgICB9CgoKIyBUYXVsdWtvbiAzIGFpbmVpc3RvCiMgem91LlRhYjM8LWRhdGEuZnJhbWUoVHJlYXQ9ZmFjdG9yKGMoMSwxLDEsMiwyLDIpLGxldmVscz1jKDIsMSksbGFiZWw9YygiQ29udHJvbCIsIkRpYXNwaXJpbiIpKSwKIyAgICAgICAgICAgICAgICAgICAgICBCYXNlbGluZS5yaXNrPWZhY3RvcihjKDEsMiwzLDEsMiwzKSxsZXZlbHM9cmV2KDE6MyksbGFiZWw9YygiTG93IiwiTWVkIiwiSGlnaCIpKSwKIyAgICAgICAgICAgICAgICAgICAgICBuZGVhdGg9YygxMiw1LDUsNiwxLDEpLAojICAgICAgICAgICAgICAgICAgICAgIHRvdGFsPWMoMTMsMTMsMjMsMTAsMTIsMjIpKQoKIyBCaW5vbWlhbCB3aXRoIGxvZ2lzdGljIGxpbmsKIyB6b3UubTE8LWdsbShJKG5kZWF0aC90b3RhbCl+VHJlYXQrQmFzZWxpbmUucmlzayxkYXRhPXpvdS5UYWIzLGZhbWlseT1iaW5vbWlhbCx3ZWlnaHRzPXRvdGFsKQojIAojIGNpLmV4cCh6b3UubTEpCgoKIyBCaW5vbWlhbCB3aXRoIGxvZyBsaW5rCiMgem91Lm0zPC1nbG0oSShuZGVhdGgvdG90YWwpflRyZWF0K0Jhc2VsaW5lLnJpc2ssZGF0YT16b3UuVGFiMyxmYW1pbHk9Ymlub21pYWwobGluaz0ibG9nIiksd2VpZ2h0cz10b3RhbCkKIyBtay5PUi5wb2lsb2coY29lZnRlc3Qoem91Lm0zLHZjb3Y9dmNvdkhDKHpvdS5tMiwgdHlwZSA9ICJIQzAiKSkpCgojIE11b2Rvc3RldGFhbiB5a3NpbMO2dGFzb24gYWluZWlzdG8KIyBhcHU8LU5VTEw7YXB1MTwtTlVMTDsgYXB1MjwtTlVMTAojIGZvcih0bXAuaSBpbiBzZXEobnJvdyh6b3UuVGFiMykpKXsKIyAgIGFwdTwtYyhhcHUscmVwKGMoMSwwKSxjKHpvdS5UYWIzJG5kZWF0aFt0bXAuaV0sem91LlRhYjMkdG90YWxbdG1wLmldLXpvdS5UYWIzJG5kZWF0aFt0bXAuaV0pKSkKIyAgIGFwdTE8LWMoYXB1MSxyZXAoem91LlRhYjMkVHJlYXRbdG1wLmldLHpvdS5UYWIzJHRvdGFsW3RtcC5pXSkpCiMgICBhcHUyPC1jKGFwdTIscmVwKHpvdS5UYWIzJEJhc2VsaW5lLnJpc2tbdG1wLmldLHpvdS5UYWIzJHRvdGFsW3RtcC5pXSkpIAojICAgfQojIHpvdS5UYWIzLmluZDwtZGF0YS5mcmFtZShUcmVhdD1hcHUxLEJhc2VsaW5lLnJpc2s9YXB1MixkZWF0aD1hcHUpCiMgYXR0cmlidXRlcyh6b3UuVGFiMy5pbmQkVHJlYXQpPC1hdHRyaWJ1dGVzKHpvdS5UYWIzJFRyZWF0KQojIGF0dHJpYnV0ZXMoem91LlRhYjMuaW5kJEJhc2VsaW5lLnJpc2spPC1hdHRyaWJ1dGVzKHpvdS5UYWIzJEJhc2VsaW5lLnJpc2spCgojIE1hbGxpdGV0YWFuIHlrc2lsw7Z0YXNvbiBkYXRhYQojIHpvdS5tMi5pbmQ8LWdsbShkZWF0aH5UcmVhdCtCYXNlbGluZS5yaXNrLGRhdGE9em91LlRhYjMuaW5kLGZhbWlseT1wb2lzc29uKQojIG1rLk9SLnBvaWxvZyhjb2VmdGVzdCh6b3UubTIuaW5kLHZjb3Y9dmNvdkhDKHpvdS5tMi5pbmQsIHR5cGUgPSAiSEMwIikpKQpgYGAKIApgYGB7cixlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHN1cnZpdmFsKQojIENveGluIHN1aHRlZWxsaXNlbiBoYXNhcmRpbiBtYWxsaQp0bXAubTEuY294PC1jb3hwaChTdXJ2KHRpbWUsc3RhdHVzKX5hZ2UrZmFjdG9yKHNleCksIGRhdGE9bHVuZykKc3VtbWFyeSh0bXAubTEuY294KQpgYGAgCiAKYGBge3IsZWNobz1UUlVFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KIyBDb3hpbiBtYWxsaW4gc3VodGVlbGxpc3V1c29sZXR1a3NlbiB0ZXN0YWFtaW5lbgpjb3guenBoKHRtcC5tMS5jb3gpCmBgYAogCgoKYGBge3IsIHJlc3VsdHM9J2FzaXMnLGVjaG8gPSBUUlVFfQojIHByaW50KGtuaXRyOjprYWJsZShzdW1tYXJ5KHRtcC5tMS5jb3gpJGNvbmYuaW50LGNhcHRpb249Ikhhc2FyZGllbiBzdWhkZSAoSFIsIGV4cChjb2VmKSkgamEgOTVcXCVsdW90dGFtdXN2w6RsaSAgQ294aW4gbWFsbGlzdGEuIixkaWdpdHM9MykpCgprYmwoc3VtbWFyeSh0bXAubTEuY294KSRjb25mLmludCxjYXB0aW9uPSJIYXNhcmRpZW4gc3VoZGUgKEhSLCBleHAoY29lZikpIGphIDk1XFwlIGx1b3R0YW11c3bDpGxpIENveGluIG1hbGxpc3RhLiIsIAogICAgYm9va3RhYnMgPSBULGRpZ2l0cz0yLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIikpICU+JSAKICBrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkjJT4lcm93X3NwZWMoYm9sZCA9VFJVRSxyb3c9MSkKCmBgYAoKCgogCmBgYHtyLHJlc3VsdHM9J21hcmt1cCcsZWNobz1UUlVFLCByZXN1bHRzPSdoaWRlJywgZWNobyA9IFRSVUUsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvaG9ydHRvb2xzKQojIFRhcGFodHVtYXRhdWx1biBsYXNrZW1pbmVuCmx1bmcuTFQ8LW1rcmF0ZXRhYmxlKFN1cnYodGltZSxzdGF0dXMpfnNleCxkYXRhPWx1bmcsc2NhbGU9MzY1LjI1KQpgYGAKCmBgYHtyLHJlc3VsdHM9J2FzaXMnLGVjaG8gPSBUUlVFfQoKIyBrbml0cjo6a2FibGUobHVuZy5MVCwKIyAgICAgICAgICAgICAgZGlnaXQ9YygyLDIsMCwyLDIsMiksCiMgICAgICAgICAgICAgIGNhcHRpb249IlRhcGFodHVtYXRhdWx1IGtldWhrb3N5w7Zww6RhaW5laXN0b3N0YSBzdWt1cHVvbGVuIHN1aHRlZW4uXFxsYWJlbHt0YWI6bHVuZ0xUfSIpCgprYmwobHVuZy5MVCwgY2FwdGlvbiA9ICJUYXBhaHR1bWF0YXVsdSBrZXVoa29zecO2cMOkYWluZWlzdG9zdGEgc3VrdXB1b2xlbiBzdWh0ZWVuLiIsCiAgICBsYWJlbD0ibHVuZ0xUIiwgYm9va3RhYnMgPSBULGRpZ2l0PWMoMiwyLDAsMiwyLDIpLAogICAgZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9IiwiKSkgJT4lIAogIGthYmxlX3N0eWxpbmcoKSAlPiVjb2x1bW5fc3BlYyhib2xkID1UUlVFLGNvbHVtbj0xKSU+JSAjJT4lcm93X3NwZWMoYm9sZCA9VFJVRSxyb3c9MSkKZm9vdG5vdGUoZ2VuZXJhbCA9IiIsCiAgICAgICAgICAgZ2VuZXJhbF90aXRsZT0iIiwKICAgICAgICAgICBudW1iZXI9Yygic2V4PXN1a3VwdW9saSIsInB5ZWFycz1rZXJ0eW5lZXQgaGVua2lsw7Z2dW9kZXQiLCJldmVudD10YXBhaHR1bWVuIGx1a3Vtw6TDpHLDpCIsInJhdGU9aWxtYWFudHV2dXVzIiwKICAgICAgICAgICAgICAgICAgICAibG93LCBoaWdoID0gOTUlIExWIGFsYS0gamEgeWzDpHJhamEiKSwKICAgICAgICAgICBmb290bm90ZV9vcmRlcj1jKCJudW1iZXIiLCJnZW5lcmFsIikKICAgICAgICAgICApCgpgYGAKIApgYGB7cn0KdG1wLm0xLnBvaTwtZ2xtKGlmZWxzZShzdGF0dXM9PTIsMSwwKX5mYWN0b3Ioc2V4KStvZmZzZXQobG9nKHRpbWUpKSwKICAgICAgICAgICAgICAgIGRhdGE9bHVuZyxmYW1pbHk9cG9pc3NvbikKc3VtbWFyeSh0bXAubTEucG9pKQojIExhc2tldGFhbiBJUlIgamEgbHVvdHRhbXVzdsOkbGl0CmNpLmV4cCh0bXAubTEucG9pLHN1YnNldD0tMSkKYGBgCgogCmBgYHtyICxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLGVycm9yPUZBTFNFLGVjaG8gPSBUUlVFLGZpZy5jYXA9Iktva29uYWlza3VvbGxlaXN1dXMgYXN1bm5vbiBoYWxsaW50YW11b2RvbiBtdWthYW4iLGZpZy5mdWxsd2lkdGggPSBUUlVFLG91dC53aWR0aD0iOTAlIn0KcmVxdWlyZShzdXJ2aXZhbCk7cmVxdWlyZShnZ3Bsb3QyKTtyZXF1aXJlKHN1cnZtaW5lcikKbG9hZChmaWxlPSJUQURBMTEuUkRhdGEiKQpUQURBLjExLjU0PC1UQURBLjExW1RBREEuMTEkc3RhdGVyPT0iNTQiLF0KIyBTZWxlY3QgNTQg4oCUIFZpcmdpbmlhIGZvciBwcmFjdGljYWxzCiMgVEFEQS4xMS41NDwtVEFEQS4xMVtUQURBLjExJHN0YXRlcj09IjU0IixdCiMgCiMgc3VydmZpdC41NC50ZW51cmUgPC0gc3VydmZpdChTdXJ2KGZvbGxvdy8zNjUuMjUsIGluZGRlYT09IjEiKSB+dGVudXJlICwgZGF0YT1UQURBLjExLjU0KQojIAojIHAxPC1nZ3N1cnZwbG90KHN1cnZmaXQuNTQudGVudXJlLCAgICAgICAgICAgICAgICAgICAgICMgc3VydmZpdCBvYmplY3Qgd2l0aCBjYWxjdWxhdGVkIHN0YXRpc3RpY3MuCiMgICAgICAgICAgICAgICAgZGF0YSA9IFRBREEuMTEuNTQsICAjIGRhdGEgdXNlZCB0byBmaXQgc3Vydml2YWwgY3VydmVzLiAKIyAgICAgICAgICAgICAgICByaXNrLnRhYmxlID0gVFJVRSwgICAgICAgIyBzaG93IHJpc2sgdGFibGUuCiMgICAgICAgICAgICAgICAgcHZhbCA9IFRSVUUsICAgICAgICAgICAgICMgc2hvdyBwLXZhbHVlIG9mIGxvZy1yYW5rIHRlc3QuCiMgICAgICAgICAgICAgICAgY29uZi5pbnQgPSBUUlVFLCAgICAgICAgICMgc2hvdyBjb25maWRlbmNlIGludGVydmFscyBmb3IgCiMgICAgICAgICAgICAgICAgeWxpbT1jKDAuODUsMSksICAgICAgICAgICAgICAgICAgICAgICAgICAjIHBvaW50IGVzdGltYWVzIG9mIHN1cnZpdmFsIGN1cnZlcy4KIyAgICAgICAgICAgICAgICAjIHhsaW0gPSBjKDAsMjApLCAgICAgICAgIyBwcmVzZW50IG5hcnJvd2VyIFggYXhpcywgYnV0IG5vdCBhZmZlY3QKIyAgICAgICAgICAgICAgICAjIHN1cnZpdmFsIGVzdGltYXRlcy4KIyAgICAgICAgICAgICAgICBicmVhay50aW1lLmJ5ID0gNSwgICAgICMgYnJlYWsgWCBheGlzIGluIHRpbWUgaW50ZXJ2YWxzIGJ5IDIKIyAgICAgICAgICAgICAgICBjZW5zb3I9RkFMU0UsIHB2YWwuY29vcmQ9YygxLDAuOTMpLHB2YWwuc2l6ZT02LAojICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gInN0cmF0YSIsCiMgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJncmV5IiwKIyAgICAgICAgICAgICAgICBsZWdlbmQ9YygwLjMsMC4zKSwKIyAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSAiQXN1bm5vbiBoYWxsaW50YSIsCiMgICAgICAgICAgICAgICAgIyBsZWdlbmQubGFicyA9IGMoIm5vIiwieWVzIiksCiMgICAgICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX2NsYXNzaWMoKSwgIyBjdXN0b21pemUgcGxvdCBhbmQgcmlzayB0YWJsZSB3aXRoIGEgdGhlbWUuCiMgICAgICAgICAgICAgICAgcmlzay50YWJsZS5mb250c2l6ZT01LAojICAgICAgICAgICAgICAgIHJpc2sudGFibGUueS50ZXh0LmNvbCA9IFQsICMgY29sb3VyIHJpc2sgdGFibGUgdGV4dCBhbm5vdGF0aW9ucy4KIyAgICAgICAgICAgICAgICByaXNrLnRhYmxlLnkudGV4dCA9IEZBTFNFLCAjIHNob3cgYmFycyBpbnN0ZWFkIG9mIG5hbWVzIGluIHRleHQgYW5ub3RhdGlvbnMKIyAgICAgICAgICAgICAgICBmb250c2l6ZT01LGZvbnQueCA9IDUsZm9udC50aWNrc2xhYnM9MTIsZm9udC5sZWdlbmQ9MTIsCiMgICAgICAgICAgICAgICAgZm9udC50aXRsZT00LCBmb250LnN1YnRpdGxlPTQsIGZvbnQuY2FwdGlvbj00LAojICAgICAgICAgICAgICAgIHlsYWI9IkVsb3NzYSBvbGV2aWVuIG9zdXVzIiwKIyAgICAgICAgICAgICAgICB0aXRsZT0iICIpCgojIHByaW50KHAxKQojIGxpYnJhcnkoZXhwb3J0KQojIGdyYXBoMnRpZih4PXAxJHBsb3QsZmlsZT0iLi9ZaHRLdXZhdC9rbXRlbnVyZS50aWZmIiwgCiMgICAgICAgICAgIGFzcGVjdHI9MSwgZm9udCA9ICJBcmlhbCIsd2lkdGggPSAzLjQsIGJnID0gInRyYW5zcGFyZW50IikKIyAKIyBncmFwaDJlcHMoeD1wMSRwbG90LGZpbGU9Ii4vWWh0S3V2YXQva210ZW51cmUuZXBzIiwgCiMgICAgICAgICAgIGFzcGVjdHI9c3FydCgyKSwgZm9udCA9ICJBcmlhbCIsd2lkdGggPSAzLjQsIGJnID0gInRyYW5zcGFyZW50IikKCiMgcG5nKGZpbGU9Ii4vWWh0S3V2YXQva210ZW51cmUucG5nIiwKIyAgICAgd2lkdGg9c3FydCgyKSo5KjcyKjcsIGhlaWdodD05KjcyKjcscmVzPTYwMCkKIyBwcmludChwMSkKIyBkZXYub2ZmKCkKCgojIHN1cnZpcGxvdChTdXJ2KGZvbGxvdy8zNjUuMjUsIGluZGRlYT09IjEiKSB+dGVudXJlICwgZGF0YT1UQURBLjExLjU0LAojICAgbHR5ID0gMTozLCAKIyAgIG1haW4gPSAnUGF0aWVudHMgc3RyYXRpZmllZCBieSB0cmVhdG1lbnQnLCAKIyAgIHhsYWIgPSAnVGltZSAoWWVhcnMpJykKCiMgcDE8LWdnc3VydnBsb3Qoc3VydmZpdC41NC50ZW51cmUsCiMgICBkYXRhID0gVEFEQS4xMS41NCx5bGltPWMoMC44NSwxKSwKIyAgIHBhbGV0dGUgPSAiZ3JleSIsCiMgICBzaXplID0gMSwgICAgICAgICAgICAgICAgICMgY2hhbmdlIGxpbmUgc2l6ZQojICAgY29uZi5pbnQgPSBUUlVFLCAgICAgICAgICAjIEFkZCBjb25maWRlbmNlIGludGVydmFsCiMgICBwdmFsID0gVFJVRSwgICAgICAgICAgICAgICMgQWRkIHAtdmFsdWUKIyAgIHJpc2sudGFibGUgPSBUUlVFLCAgICAgICAgIyBBZGQgcmlzayB0YWJsZQojICAgcmlzay50YWJsZS5jb2wgPSAic3RyYXRhIiwjIFJpc2sgdGFibGUgY29sb3IgYnkgZ3JvdXBzCiMgICByaXNrLnRhYmxlLmhlaWdodCA9IDAuMzUsICMgVXNlZnVsIHRvIGNoYW5nZSB3aGVuIHlvdSBoYXZlIG11bHRpcGxlIGdyb3VwcwojICAgbGVnZW5kPSJ0b3AiLGxlZ2VuZC5sYWJzPWxldmVscyhUQURBLjExLjU0JHRlbnVyZSksbGVnZW5kLnRpdGxlPSJUZW51cmUiLAojICAgIyBmb250c2l6ZT0zLGZvbnQueCA9IDQsZm9udC50aWNrc2xhYnM9OCxmb250LmxlZ2VuZD04LAojICAgIyBmb250LnRpdGxlPTQsIGZvbnQuc3VidGl0bGU9NCwgZm9udC5jYXB0aW9uPTQsIHJpc2sudGFibGUudGl0bGU9IiIsCiMgICB4bGFiPSIiLHlsYWI9IiIKIyAgICNnZ3RoZW1lID0gdGhlbWVfYncoKSAgICAgICMgQ2hhbmdlIGdncGxvdDIgdGhlbWUKIyApCgojIHBuZyhmaWxlbmFtZSA9ImttdGVudXJlLnBuZyIsd2lkdGggPSAxNTAwLGhlaWdodCA9IDE1MDAvc3FydCgyKSxwb2ludHNpemUgPSA2MCk7cHJpbnQocDEpO2Rldi5vZmYoKQojIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL1lodEt1dmF0L2ttdGVudXJlLnBuZyIsKQoKYGBgCgogCmBgYHtyICxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLGVycm9yPUZBTFNFLGVjaG8gPSBUUlVFLHJlc3VsdHM9J2FzaXMnfQpsaWJyYXJ5KHN0YXJnYXplcikKbG9hZCgiLi9ZaHREYXRhL1RBREEuMTEuNTQuUkRhdGEiKQpUQURBLjU0Lm0wPC1nbG0oYXMubnVtZXJpYyhpbmRkZWEpfnRlbnVyZStvZmZzZXQobG9nKGZvbGxvdy8zNjUuMjUpKSxmYW1pbHk9cG9pc3NvbixkYXRhPVRBREEuMTEuNTQpClRBREEuNTQubTE8LXVwZGF0ZShUQURBLjU0Lm0wLH4uK2FnZSkKIyBzdGFyZ2F6ZXI6OnN0YXJnYXplcihUQURBLjU0Lm0wLFRBREEuNTQubTEsdHlwZT0idGV4dCIsY2k9VFJVRSxhcHBseS5jb2VmPWV4cCwKIyAgICAgICAgICAgZGVwLnZhci5jYXB0aW9uPSJJUlIgamEgOTUlIGx1b3R0YW11c3bDpGxpLiIpCiMgc3RhcmdhemVyOjpzdGFyZ2F6ZXIoVEFEQS41NC5tMCxUQURBLjU0Lm0xLHR5cGU9aWZlbHNlKGtuaXRyOjppc19odG1sX291dHB1dCgpLCJodG1sIiwibGF0ZXgiKSxjaT1UUlVFLGFwcGx5LmNvZWY9ZXhwLAojICAgICAgICAgICBkZXAudmFyLmNhcHRpb249IklSUiBqYSA5NSUgbHVvdHRhbXVzdsOkbGkuIikKIyBwcmludChUQURBLjU0Lm0wKQojIHByaW50KFRBREEuNTQubTEpCmBgYAoKIApgYGB7ciAsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSxlcnJvcj1GQUxTRSxlY2hvID0gVFJVRSxyZXN1bHRzPSdhc2lzJyxmaWcuZnVsbHdpZHRoID0gVFJVRX0KbGlicmFyeShrYWJsZUV4dHJhKQp0bXAudGFiPC1yYmluZChjaS5leHAoVEFEQS41NC5tMCxzdWJzZXQ9InRlbnVyZSIpLGNpLmV4cChUQURBLjU0Lm0xLHN1YnNldD0idGVudXJlIikpCmNvbG5hbWVzKHRtcC50YWIpPC1jKCJNUlIiLCJhbGEiLCJ5bGEiKQp0bXAudGFiPC1kYXRhLmZyYW1lKE1hbGxpPWMoIk1hbGxpIDEiLHJlcCgiIiwyKSwiTWFsbGkgMiIscmVwKCIiLDIpICksCiAgICAgICAgICAgVmFyPXJvd25hbWVzKHRtcC50YWIpLAogICAgICAgICAgIHRtcC50YWIpCnJvd25hbWVzKHRtcC50YWIpPC1OVUxMCgprYmwodG1wLnRhYixkaWdpdHMgPSAyLGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIiksCiAgICBjYXB0aW9uPSJLdW9sbGVpc3V1a3NpZW4gc3VodGVlbiBlc3RpbWFhdGl0IGphIDk1XFwlIExWIGthaGRlc3RhIG1hbGxpc3RhLiBNYWxsaSAxIHZhaW4geWtzaSBtdXV0dHVqYSwgTWFsbGkgMiBpa8OkIGtvcmphdHR1LiBWZXJ0YWlsdXRhc29uYSAnT3duZWQnIiwKICAgIGxhYmVsPSJwb2lzc29ubWFsbGl0MWphMiIpICU+JQogIGthYmxlX2NsYXNzaWMoKSAjJT4lCiAgIyBhZGRfaGVhZGVyX2Fib3ZlKGMoIiAiID0gMSwgICJNYWxsaSAxIiA9IDMsICAiTWFsbGkgMiIgPSAzKSkKCmBgYAogCiAKYGBge3IgSVJSZGl2b3JjZSwgZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KCmxpYnJhcnkoaGF2ZW4pCmxvYWQoZmlsZT0iWWh0RGF0YS9kaXZvcmNlLlJEYXRhIikKCnRtcC5tLmRpdjwtZ2xtKGRpdn5oZWR1YyttaXhlZCtoZWJsYWNrK29mZnNldChsb2coeWVhcnMpKSxmYW1pbHk9cG9pc3NvbixkYXRhPWRpdm9yY2UpCgp0bXAudGFiPC1yYmluZChjaS5leHAodG1wLm0uZGl2LHN1YnNldD0tMSkpCmNvbG5hbWVzKHRtcC50YWIpPC1jKCJJUlIiLCJhbGEiLCJ5bGEiKQp0bXAudGFiPC1kYXRhLmZyYW1lKFZhcj1yb3duYW1lcyh0bXAudGFiKSx0bXAudGFiKQpyb3duYW1lcyh0bXAudGFiKTwtTlVMTApyb3duYW1lcyh0bXAudGFiKTwtYygiaGVkdWMgMTItMTUgeWVhcnMgdnMuIDwgMTIgeWVhcnMiLCAiaGVkdWMgMTYrIHllYXJzIHZzLiA8IDEyIHllYXJzIiwgCiAgICAgICAgICAgICAgICAgICAgICJtaXhlZCBZZXMgdnMuIE5vIiwgImhlYmxhY2sgWWVzIHZzLiBObyIpCgprYmwodG1wLnRhYlssLTFdLGRpZ2l0cyA9IDIsZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9IiwiKSwKICAgIGNhcHRpb249IkF2aW9lcm9uIGlsbWFhbnR1dnV1ZGVuIHN1aGRlIChJUlIpIHloZHlzdmFsdGFsYWlzZW4gYWluZWlzdG9uIG11a2Fhbi4gQXZpb2xpaXRvdCBqYW90ZWx0dSBtaWVoZW4ga291bHV0dWtzZW4gKGhlZHVjKSwgcHVvbGlzb2lkZW4gZXRuaXNlbiB0YXVzdGFuIG11a2FhbiAobWl4ZWQpIChzYW1hPU5vLCBlcmk9WWVzKSBqYSBhdmlvbWllaGVuIGlob252w6RyaW4gbXVrYWFuIChoZWJsYWNrKS4gIiwKICAgIGxhYmVsPSJkaXZvcmNlbWFsbGkiKSAlPiUKICBrYWJsZV9jbGFzc2ljKCkgCgpgYGAKCiAKYGBge3IsIGVjaG89VFJVRX0KIyBDb3hpbiBtYWxsaQp0bXAubTAuY294PC1jb3hwaChTdXJ2KHRpbWUsc3RhdHVzKX5mYWN0b3Ioc2V4KSxkYXRhPWx1bmcpCnN1bW1hcnkodG1wLm0wLmNveCkkY29uZi5pbnQKIyBQb2lzc29uIHJlZ3Jlc3NpbwpjaS5leHAodG1wLm0xLnBvaSxzdWJzZXQ9LTEpCmBgYAogCgogCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLGVjaG8gPSBUUlVFfQojIE5PVEUhIHlvdSBtdXN0IGdpdmUgc3RhcnRpbmcgdmFsdWVzIGZvciBwYXJhbWV0ZXJzICJzdGFydD1jKDAuMSwwLjEpIiIKIyBMYXNrZXRhYW4gdnVvdHRhIGtvaHRpLCBhbHVucGVyaW4gb2xpIHDDpGl2acOkCnRtcC5tMS5wb2kuQTwtZ2xtKEkoaWZlbHNlKHN0YXR1cz09MiwxLDApLyh0aW1lLzM2NS4yNSkpfmZhY3RvcihzZXgpLAogICAgICAgICAgICAgICAgICBkYXRhPWx1bmcsd2VpZ2h0PSh0aW1lLzM2NS4yNSksCiAgICAgICAgICAgICAgICAgIHN0YXJ0PWMoMC4xLDAuMSksZmFtaWx5PXBvaXNzb24oImlkZW50aXR5IikpCmNpLmxpbih0bXAubTEucG9pLkEsc3Vic2V0PS0xKQojIFRhcGFodHVtYXRhdWx1CiMgdG1wLnRhYi4xCmBgYAoKICAKIApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFRSVUV9CnJlcXVpcmUoTUFTUykKYGBgCiAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG8gPSBUUlVFfQpoZWFkKGFzLmRhdGEuZnJhbWUoaG91c2luZyksMykKc3VtbWFyeShob3VzaW5nKQpgYGAKIApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFRSVUV9CgpvcHRpb25zKGNvbnRyYXN0cyA9IGMoImNvbnRyLnRyZWF0bWVudCIsICJjb250ci5wb2x5IikpCmhvdXNlLnBsciA8LSBwb2xyKFNhdCB+IEluZmwgKyBUeXBlICsgQ29udCwgd2VpZ2h0cyA9IEZyZXEsIGRhdGEgPSBob3VzaW5nKQpzdW1tYXJ5KGhvdXNlLnBsciwgZGlnaXRzID0gMykKYGBgCiAKCmBgYHtyIGhvdXNpbmdtYWxsaW9yLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0UsZWNobyA9IFRSVUUscmVzdWx0cz0nYXNpcyd9CmhvdXNlLnBsciA8LSB1cGRhdGUoaG91c2UucGxyLCBIZXNzPVRSVUUpCnByIDwtIHByb2ZpbGUoaG91c2UucGxyKQpob3VzZS5wbHIuT1I8LWV4cChjYmluZChob3VzZS5wbHIkY29lZmZpY2llbnRzLGNvbmZpbnQocHIpKSkKY29sbmFtZXMoaG91c2UucGxyLk9SKVsxXTwtIk9SIgprYmwoaG91c2UucGxyLk9SLGRpZ2l0cyA9IDMsY2FwdGlvbiA9ICJWZXRvc3VodGVldCBqYSA5NVxcJSBsdW90dGFtdXN2w6RsaXQgYXN1bWlzdmlpaHR5dnl5ZGVuIG1hbGxpc3RhIikgJT4lCiAga2FibGVfY2xhc3NpYygpICU+JWNvbHVtbl9zcGVjKGJvbGQgPVRSVUUsY29sdW1uPTEpCgpgYGAKCiAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG8gPSBUUlVFLHJlc3VsdHM9J2hpZGUnfQpyZXF1aXJlKE1BU1MpCnJlcXVpcmUobm5ldCkKcmVxdWlyZShmb3JlaWduKQojIG9jIDwtIG9wdGlvbnMoY29udHJhc3RzID0gYygiY29udHIudHJlYXRtZW50IiwgImNvbnRyLnBvbHkiKSkKIyAjIG1sIDwtIHJlYWQuZHRhKCJodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvc3RhdC9kYXRhL2hzYmRlbW8uZHRhIikKIyAjIHNhdmUobWwsZmlsZT0iLi95aHREYXRhL21sLlJEYXRhIikKIyBsb2FkKGZpbGU9Ii4veWh0RGF0YS9tbC5SRGF0YSIpCiMgbWwkcHJvZzIgPC0gcmVsZXZlbChtbCRwcm9nLCByZWYgPSAiYWNhZGVtaWMiKQpsb2FkKGZpbGU9Ii4vWWh0RGF0YS9jcmFzaDJkdC5SRGF0YSIpCmNyYXNoMi5kdCRidHJhbnNmPC1mYWN0b3IoY3Jhc2gyLmR0JGJ0cmFuc2YpCm1uLm1hbGxpIDwtIG11bHRpbm9tKGNvbmRpdGlvbiB+aW5qdXJ5dHlwZStzZXgrYWdlK2J0cmFuc2YsIGRhdGEgPSBjcmFzaDIuZHQpCnN1bW1hcnkobW4ubWFsbGkpCmBgYAoKIAogCgpgYGB7ciBtdWx0aW5vbWltYWxsaW9yLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0UsZWNobyA9IFRSVUUscmVzdWx0cz0naGlkZSd9Cgp0bXAudGI8LXdpdGgoY3Jhc2gyLmR0LHRhYmxlKGNvbmRpdGlvbixpbmp1cnl0eXBlKSkKCm1uLm1hbGxpLk9SPC1leHAoY29lZihtbi5tYWxsaSkpCmFwdS5jb2VmPC1jb2VmKG1uLm1hbGxpKQphcHUuc2U8LXN1bW1hcnkobW4ubWFsbGkpJHN0YW5kYXJkLmVycm9ycwphcHUxPC1leHAoYXB1LmNvZWYpCmFwdTI8LWV4cChhcHUuY29lZitxbm9ybSgwLjAyNSkqYXB1LnNlKQphcHUzPC1leHAoYXB1LmNvZWYrcW5vcm0oMS0wLjAyNSkqYXB1LnNlKQojCmFwdTQ8LXBhc3RlMChmb3JtYXQoYXB1MVssLTFdLGRpZ2l0cyA9IDIpLCIgKCIsCiAgICAgICAgICAgICBmb3JtYXQoYXB1MlssLTFdLGRpZ2l0cyA9IDIpLCItIiwKICAgICAgICAgICAgIGZvcm1hdChhcHUzWywtMV0sZGlnaXRzID0gMiksIikiKQojCmFwdTU8LW1hdHJpeChhcHU0LG5yb3cgPSBsZW5ndGgodW5pcXVlKGNyYXNoMi5kdCRjb25kaXRpb24pKS0xKQojIApyb3duYW1lcyhhcHU1KTwtcm93bmFtZXMoYXB1MSkKY29sbmFtZXMoYXB1NSk8LWNvbG5hbWVzKGFwdTEpWy0xXQpjb2xuYW1lcyhhcHU1KTwtZ3N1Yih4PWNvbG5hbWVzKGFwdTUpLHBhdHRlcm4gPSAiaW5qdXJ5dHlwZXxzZXgiLHJlcGxhY2VtZW50ID0gIiIpCmtibChhcHU1LGRpZ2l0cyA9IDIsIGZvcm1hdC5hcmdzID0gbGlzdChkZWNpbWFsLm1hcmsgPSIsIiksYm9va3RhYnMgPSBUUlVFLGZvcm1hdD0iaHRtbCIsCiAgICBjYXB0aW9uID0gIlZldG9zdWh0ZWV0IGphIDk1XFwlIGx1b3R0YW11c3bDpGxpdCBtdWx0aW5vbWlyZWdyZXNzaW8gbWFsbGlzdGEiLAogICAgKSAlPiVrYWJsZV9zdHlsaW5nKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkKCiMgJT4la2FibGVfY2xhc3NpYygpICU+JWNvbHVtbl9zcGVjKGJvbGQgPVRSVUUsY29sdW1uPTEpCgoKIyBtbi5tYWxsaS5PUjwtZXhwKGNvZWYobW4ubWFsbGkpKQojIGFwdS5jb2VmPC1jb2VmKG1uLm1hbGxpKQojIGFwdS5zZTwtc3VtbWFyeShtbi5tYWxsaSkkc3RhbmRhcmQuZXJyb3JzCiMgYXB1MTwtZXhwKGFwdS5jb2VmKQojIGFwdTI8LWV4cChhcHUuY29lZitxbm9ybSgwLjAyNSkqYXB1LnNlKQojIGFwdTM8LWV4cChhcHUuY29lZitxbm9ybSgxLTAuMDI1KSphcHUuc2UpCiMgIAojIGFwdTQ8LXBhc3RlMChmb3JtYXQoYXB1MVssLTFdLGRpZ2l0cyA9IDIpLCIgKCIsCiMgICAgICAgIGZvcm1hdChhcHUyWywtMV0sZGlnaXRzID0gMiksIi0iLAojICAgICAgICBmb3JtYXQoYXB1M1ssLTFdLGRpZ2l0cyA9IDIpLCIpIikKIyBhcHU1PC1tYXRyaXgoYXB1NCxucm93PTIpCiMgcm93bmFtZXMoYXB1NSk8LXJvd25hbWVzKGFwdTEpCiMgY29sbmFtZXMoYXB1NSk8LWNvbG5hbWVzKGFwdTEpWy0xXQojIAojIGtibChhcHU1LGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiVmV0b3N1aHRlZXQgamEgOTVcXCUgbHVvdHRhbXVzdsOkbGl0IAojICAgICBtdWx0aW5vbWlyZWdyZXNzaW8gbWFsbGlzdGEiKSAlPiVrYWJsZV9jbGFzc2ljKCkgJT4lY29sdW1uX3NwZWMoYm9sZCA9VFJVRSxjb2x1bW49MSkKCmBgYAoKIAoKCgoKCgoKCgoKIAo=