library(survival)
library(flexsurv)
## Warning: package 'flexsurv' was built under R version 4.4.3

Setup Code

Kaplan-Meier

survKM2 <- function(dat, cen=NA, conf=0.95, eps=1e-9) {
   n <- length(dat); if (is.na(cen[1])) cen <- rep(T, n); cen <- as.logical(cen)
   di <- table(dat[cen])
   ti <- as.numeric(names(di)) - eps
    m <- length(ti)
   ni <- NULL; for(i in 1:m) ni[i] <- sum(dat >= ti[i])
    S <- cumprod((ni-di)/ni)
    V <- S^2*cumsum(di/ni/(ni-di))
   ti <- c(0, ti); di <- c(0, as.vector(di)); ni <- c(n, ni); S <- c(1, S); V <- c(0, V)
   se <- sqrt(V); z <- qnorm(1-(1-conf)/2)
   CI.lb <- S - z*se; CI.lb <- CI.lb - (CI.lb < 0)* CI.lb
   CI.ub <- S + z*se; CI.ub <- CI.ub - (CI.ub > 1)*(CI.ub-1)

     h <- di/ni
     H <- cumsum(h)
   V.H <- cumsum(di/ni^2)
   S.H <- exp(-H)

   cat("Kaplan-Meier estimate, a.k.a. the product-limit estimate of S(t) \n")
   cat("Nonparametric estimate of survival function S(t) \n\n")   
   tab <- data.frame(time=ti, di=di, ni=ni, S=round(S,3), Var.S=round(V,3), se=round(se,3),
                     CI.lb=round(CI.lb,3), CI.ub=round(CI.ub,3), XXXXX="",
                     h=round(h,3), H=round(H,3), Var.H=round(V.H,3), S.na=round(S.H,3))
   print(tab, row.names=F)
   cat("\nh is nonparametric estimate of the hazard function h(t).")
   cat("\nH is Nelson-Aallen estimate of the cumulative hazard function H(t).")
   cat("\nS.na is Nelson-Aallen estimate of S(t). \n\n")

   par(mar=c(5,5,4,1))
   plot(c(ti, 1.2*max(ti)), c(S, min(S)), type="s", lwd=2, 
      xlab="Time, t", ylab=expression("Estimated Survival Probability,   "*hat(S)(t)),
      main="Kaplan-Meier estimate, a.k.a. the product-limit estimate of S(t)")   
   points(ti[-1], S[-1], pch=19)
   points(ti    , CI.lb, type="s", lty=2)
   points(ti    , CI.ub, type="s", lty=2)

    m <- m + 1
   St <- c(S[-m]*diff(ti), 0)
   mu <- sum(St)
   Ai <- rev(cumsum(rev(St)))   # = mu - cumsum(c(0, St[-m]))
   Vm <- sum((Ai^2*di/ni/(ni-di))[-m])
   se <- sqrt(Vm)

   # if (!cen[which.max(dat)]), could replace w/ limit L 
   # for Mean survival time limited to a time L.

   cat("\nMean survival time based on Kaplan-Meier estimate of S(t) \n\n")   
   print(data.frame(Estimate=round(mu,3), Var=round(Vm,3), se=round(se,3),
      CI.lb=round(mu-z*se,3), CI.ub=round(mu+z*se,3)), row.names=F)

   invisible(tab) }

Res

quiet <- function(fn) {   # suppress output messages
    sink(tempfile())
    on.exit(sink())
    invisible(force(fn)) }


###########################
plotR <- function(r, cen) {
   cen <- as.logical(cen)
   r[!cen] <- r[!cen] + 1   # or add 0.693
   KMobj <- quiet(survKM2(r, cen))
     r <- KMobj$time
    Sr <- KMobj$S
   fit <- lm(-log(Sr) ~ r, subset=(Sr != 0))
   tab <- list(residual=r, Sr=Sr, R2=summary(fit)$r.sq)

   plot(r, -log(Sr), xlab="Residual, r", ylab=expression(-log(hat(S)(r))), 
        main="Cox-Snell residual plot with uncensored/censored data")   
   abline(fit, lty=2, col="red")
   abline(a=0, b=1, lwd=2) 

   invisible(tab) }

MLE

nlL <- function(par, dist=c("exponential", "exp2par", "weibull", "lognormal", "gamma", "gengamma", "loglogistic", "gompertz"), 
                dat, cen=NA) {
   n <- length(dat); if (is.na(cen[1])) cen <- rep(T, n); cen <- as.logical(cen)

   if (dist[1]=="exponential") { 
      lambda <- par; if (!(lambda > 0)) return(Inf)
      pdf <- function(tt)   dexp(tt, rate=lambda)
       sf <- function(tt) 1-pexp(tt, rate=lambda) }
   if (dist[1]=="exp2par") {       
      lambda <- par[1]; if (!(lambda > 0   )) return(Inf)
           G <- par[2]; if (!(G <= min(dat))) return(Inf)
      pdf <- function(tt)   dexp(tt-G, rate=lambda)
       sf <- function(tt) 1-pexp(tt-G, rate=lambda) }
   if (dist[1]=="weibull") { 
      if (!all(par>0)) return(Inf)
      lambda <- par[1]; gam <- par[2]
      pdf <- function(tt)   dweibull(tt, shape=gam, scale=1/lambda)
       sf <- function(tt) 1-pweibull(tt, shape=gam, scale=1/lambda) }
   if (dist[1]=="lognormal") {       
         mu <- par[1]
      sigma <- par[2]; if (!(sigma > 0)) return(Inf)
      pdf <- function(tt)   dlnorm(tt, meanlog=mu, sdlog=sigma)
       sf <- function(tt) 1-plnorm(tt, meanlog=mu, sdlog=sigma) }
   if (dist[1]=="gamma") { 
      if (!all(par>0)) return(Inf)
      lambda <- par[1]; gam <- par[2]
      pdf <- function(tt)   dgamma(tt, shape=gam, scale=1/lambda)
       sf <- function(tt) 1-pgamma(tt, shape=gam, scale=1/lambda) }
   if (dist[1]=="gengamma") { 
      if (!all(par[-2]>0)) return(Inf)
      lambda <- par[1]; alpha <- par[2]; gam <- par[3]
      pdf <- function(tt) if (alpha < 0) 
           dgengamma(tt, mu=-log(lambda), sigma=-1/alpha/sqrt(gam), Q=-1/sqrt(gam)) else
           dgengamma(tt, mu=-log(lambda), sigma= 1/alpha/sqrt(gam), Q= 1/sqrt(gam)) 
       sf <- function(tt) if (alpha < 0) 
         1-pgengamma(tt, mu=-log(lambda), sigma=-1/alpha/sqrt(gam), Q=-1/sqrt(gam)) else
         1-pgengamma(tt, mu=-log(lambda), sigma= 1/alpha/sqrt(gam), Q= 1/sqrt(gam)) }
   if (dist[1]=="loglogistic") { 
      if (!all(par>0)) return(Inf)
      alpha <- par[1]; gam <- par[2]
      pdf <- function(tt)   dllogis(tt, shape=gam, scale=1/alpha^(1/gam))
       sf <- function(tt) 1-pllogis(tt, shape=gam, scale=1/alpha^(1/gam)) }
   if (dist[1]=="gompertz") { 
      if (!all(par>0)) return(Inf)
      lambda <- par[1]; gam <- par[2]
      pdf <- function(tt)   dgompertz(tt, shape=gam, rate=exp(lambda))
       sf <- function(tt) 1-pgompertz(tt, shape=gam, rate=exp(lambda)) }

   logL <- sum(log(pdf(dat[cen]))) + sum(log(sf(dat[!cen])))
   tt <- sort(dat)
   attr(logL, "H") <- -log(sf(tt))
   -logL }


###########################################################
MLE <- function(par0, dist, dat, cen=NA, se=T, conf=0.95) {
   suppressWarnings(
   MLobj <- optim(par0, nlL, dist=dist, dat=dat, cen=cen, hessian=se))
   mle <- MLobj$par
     V <- try(solve(MLobj$hessian), silent=T)
   if (se) se <- sqrt(diag(V)) else se <- NA
   z <- qnorm(1-(1-conf)/2)
   CI.lb <- mle - z*se
   CI.ub <- mle + z*se   # approximate confidence interval
 
   par <- switch(dist, 
      exponential=c("lambda"         ),
      exp2par    =c("lambda", "G"    ),
      weibull    =c("lambda", "gamma"),
      lognormal  =c("mu"    , "sigma"),
      gamma      =c("lambda", "gamma"),
      gengamma   =c("lambda", "alpha" , "gamma"),
      loglogistic=c("alpha" , "gamma"),
      gompertz   =c("lambda", "gamma"))
   tab <- data.frame(parameter=par, MLE=round(mle,3), se=round(se,3), 
                     CI.lb=round(CI.lb,3), CI.ub=round(CI.ub,3))
   logL <- -nlL(mle, dist, dat, cen)   # = -MLobj$val
   attr(tab, "max.logL") <- logL
   attr(tab, "residual") <- attr(logL, "H")   # Cox-Snell residuals 
   tab }


###############################################################
MLEexp <- function(dat, cen=NA, shift=F, conf=0.95, eps=1e-9) {
   n <- length(dat); if (is.na(cen[1])) cen <- rep(T, n); cen <- as.logical(cen)
   r <- n-sum(!cen) - shift; nc <- length(unique(dat[!cen]))
   dat[!cen] <- dat[!cen] + eps

   t1 <- min(dat)
   mle <- r/sum(dat - t1*shift); mle[2] <- mu <- 1/mle
   se <- mle/sqrt(r)
   z <- qnorm(1-(1-conf)/2)
   CI.lb <- mle - z*se
   CI.ub <- mle + z*se   # approximate confidence interval

   tab <- data.frame(parameter=c("lambda", "mu"), MLE=round(mle,3), se=round(se,3), 
                     approxCI.lb=round(CI.lb,3), approxCI.ub=round(CI.ub,3))

   if (shift) {   # dist="exp2par"
       G <- t1 - mu/n
      se <- mu/n*(1+1/r)
      CI.lb <- G - z*se
      CI.ub <- G + z*se   # approximate confidence interval

      tab <- rbind(tab, data.frame(parameter="G", MLE=round(G,3), se=round(se,3), 
                          approxCI.lb=round(CI.lb,3), approxCI.ub=round(CI.ub,3))) }

   if ((nc==0) || ((nc==1) && (!cen[which.max(dat)]))) {
      chiL <- qchisq(  (1-conf)/2, df=2*r)
      chiU <- qchisq(1-(1-conf)/2, df=2*r)
      CI <- 2*r*mu/c(chiU, chiL)
      CI <- rbind(rev(1/CI), CI)   # exact confidence interval

      if (shift) {   # dist=exp2par
         fU <- qf(conf, df1=2, df2=2*r)   # approx r*(1/(1-conf)^(1/r) - 1)
         CI <- rbind(CI, c(t1 - fU*mu/n, t1)) }

      colnames(CI) <- c("exactCI.lb", "exactCI.ub"); rownames(CI) <- NULL      
      tab <- cbind(tab, round(CI,3)) }

   if (shift) {
      cat("Note: For two-parameter exponential distribution (with a shift parameter), \n")
      cat("      the minimum variance unbiased estimates are provided rather than MLE. \n\n") }
   tab }


######################################
MLElnorm <- function(dat, conf=0.95) {
   n <- length(dat)
   mu <- mean(log(dat))
   s2 <-  var(log(dat)); sigma <- sqrt(s2*(n-1)/n)
   mle <- c(mu, sigma)
   se <- c(sigma/sqrt(n), sigma*sqrt(sqrt(2*(n-1))/n))
      tq <- qt(1-(1-conf)/2, df=n-1)
   CI.lb <- mu - tq*sqrt(s2/(n-1))
   CI.ub <- mu + tq*sqrt(s2/(n-1))         # exact confidence interval for mu
    chiL <- qchisq(  (1-conf)/2, df=n-1)
    chiU <- qchisq(1-(1-conf)/2, df=n-1)
   CI.lb <- c(CI.lb, sigma*sqrt(n/chiU))
   CI.ub <- c(CI.ub, sigma*sqrt(n/chiL))   # exact confidence interval for sigma

   tab <- data.frame(parameter=c("mu", "sigma"), MLE=round(mle,3), se=round(se,3), 
                     exactCI.lb=round(CI.lb,3), exactCI.ub=round(CI.ub,3))
   tab }

Plots

plotP <- function(dist=c("exponential", "weibull", "lognormal", "loglogistic"), dat) {
   n <- length(dat)
   mi <- table(dat)
   ti <- as.numeric(names(mi)); idx <- order(ti); ti <- ti[idx]; mi <- as.vector(mi[idx])
    F <- (cumsum(mi)-0.5)/n

   if (dist[1]=="exponential") { 
      x <- log(1/(1-F)); xlab <- expression(-log(1-hat(F)(t)))
      y <- ti          ; ylab <- "t"
      fit <- lm(y ~ -1 + x)
      tab <- data.frame(lambda=1/fit$coef) }
   if (dist[1]=="weibull") { 
      x <- log(log(1/(1-F))); xlab <- expression(log(-log(1-hat(F)(t))))
      y <- log(ti)          ; ylab <- "log(t)"
      fit <- lm(y ~ x)
      tab <- data.frame(lambda=exp(-fit$coef[1]), gam=1/fit$coef[2]) }
   if (dist[1]=="lognormal") {       
      x <- qnorm(F); xlab <- expression(Phi^-1*(hat(F)(t)))
      y <- log(ti) ; ylab <- "log(t)"
      fit <- lm(y ~ x)
      tab <- data.frame(mu=fit$coef[1], sigma=fit$coef[2]) }
   if (dist[1]=="loglogistic") { 
      x <- log(1/(1-F)-1); xlab <- expression(-log((1-hat(F)(t))/hat(F)(t)))
      y <- log(ti)       ; ylab <- "log(t)"
      fit <- lm(y ~ x)
      tab <- data.frame(alpha=exp(-fit$coef[1]/fit$coef[2]), gam=1/fit$coef[2]) }

   plot(x, y, xlab=xlab, ylab=ylab, main="Probability plot with uncensored data")   
   abline(fit, lwd=2)
   tab$R2 <- summary(fit)$r.sq; rownames(tab) <- NULL
   round(tab,3) }
 

##############################################################################################
plotH <- function(dist=c("exponential", "weibull", "lognormal", "loglogistic"), dat, cen=NA) {
   n <- length(dat); if (is.na(cen[1])) cen <- rep(T, n); cen <- as.logical(cen)
   idx <- order(dat)
   cen <- cen[idx]
   dat <- dat[idx]; dat <- dat[cen]

   ti <- unique(dat)
   h <- 1/(n:1); h.old <- h[cen]
   h <- NULL; for(tt in ti) h <- c(h, sum(h.old[tt==dat])) 
   H <- cumsum(h)

   if (dist[1]=="exponential") { 
      x <- H ; xlab <- expression(hat(H)(t))
      y <- ti; ylab <- "t"
      fit <- lm(y ~ -1 + x)
      tab <- data.frame(lambda=1/fit$coef) }
   if (dist[1]=="weibull") { 
      x <- log(H) ; xlab <- expression(log(hat(H)(t)))
      y <- log(ti); ylab <- "log(t)"
      fit <- lm(y ~ x)
      tab <- data.frame(lambda=exp(-fit$coef[1]), gam=1/fit$coef[2]) }
   if (dist[1]=="lognormal") {       
      x <- qnorm(1-exp(-H)); xlab <- expression(Phi^-1*(1-e^-hat(H)(t)))
      y <- log(ti)         ; ylab <- "log(t)"
      fit <- lm(y ~ x)
      tab <- data.frame(mu=fit$coef[1], sigma=fit$coef[2]) }
   if (dist[1]=="loglogistic") { 
      x <- log(exp(H)-1); xlab <- expression(log(e^hat(H)(t)*-1))
      y <- log(ti)      ; ylab <- "log(t)"
      fit <- lm(y ~ x)
      tab <- data.frame(alpha=exp(-fit$coef[1]/fit$coef[2]), gam=1/fit$coef[2]) }

   plot(x, y, xlab=xlab, ylab=ylab, main="Hazard plot with uncensored/censored data")   
   abline(fit, lwd=2)
   tab$R2 <- summary(fit)$r.sq; rownames(tab) <- NULL
   round(tab,3) }

Tests

sigcode <- function(p.val) ifelse(p.val < 0.001, "***", ifelse(p.val < 0.01, "**", ifelse(p.val < 0.05, "*", ifelse(p.val < 0.1, ".", ""))))

quiet <- function(fn) {   # suppress output messages
    sink(tempfile())
    on.exit(sink())
    invisible(force(fn)) }


##############################################
LR.test <- function(par0, dist, dat, cen=NA) {
    res <- MLE(par0, dist, dat, cen, se=F)
   logL  <- attr(res, "max.logL")
   logL0 <- -nlL(par0, dist, dat, cen) 
     LRT <- -2*(logL0 - logL)
   p.val <- 1-pchisq(LRT, df=length(par0))

   cat("Likelihood ratio test for a specific distribution with known parameters \n")
   cat("   H0: S = S0 \n")
   cat(paste("       or equivalently, the underlying distribution is", dist, 
             "with specified parameter values. \n\n"))
   res <- res[,1:2]; res$null.value <- par0
   print(res, row.names=F); cat("\n")
   print(data.frame(logL=round(logL,3), logL0=round(logL0,3), LRT=round(LRT,3), 
                    p.value=round(p.val,4), signif=sigcode(p.val)), row.names=F) }


#################################################
HP.test <- function(sf0, dat, cen=NA, eps=1e-9) {
   n <- length(dat); if (is.na(cen[1])) cen <- rep(T, n); cen <- as.logical(cen)
   dat[!cen] <- dat[!cen] + eps
   idx <- order(dat)
   dat <- dat[idx]
   del <- cen <- cen[idx]

   ft <- 1/n*c(1, cumprod(((n:2)/((n-1):1))^(1-del[-n])))   # jump of Kaplan-Meier estimate
   S0 <- sf0(dat)
   s2 <- 1/16*sum(n/(n:1)*(c(1, S0[-n]^4) - S0^4))
   C <- sum(del*S0*ft)
   Z <- (C - 0.5)/sqrt(s2/n)
   p.val <- c(2*pnorm(-abs(Z)), 1-pnorm(Z), pnorm(Z))

   cat("Hollander and Proschan's test for a specific distribution with known parameters \n")
   cat("   H0: S = S0 \n")
   cat("       where S0 is the underlying survival function with specified parameter values \n\n")
   print(data.frame(C=round(C,3), sigma2=round(s2,3), C.star=round(Z,3)), row.names=F); cat("\n")
   print(data.frame(H1=c("S != S0", "S < S0", "S > S0"), 
      p.value=round(p.val,4), signif=sapply(p.val, sigcode)), row.names=F)

   quiet(survKM2(dat, cen))
   tt <- seq(0, 1.2*max(dat[cen]), len=100) 
   S0 <- sf0(tt)
   points(tt, S0, type="l", lwd=2, col="red") }   # overlay hypothesized survival function S0(t)

Problem 1

The following data are on remission times, in weeks, for a group of 30 leukemia patients in a certain type of therapy.

            Remission Times (in weeks)
 1  1  2  4  4  6  6   6  7   8   9  9 10  12  13
14 18 19 24 26 29 31+ 42 45+ 50+ 57 60 71+ 85+ 91

Assume that one-parameter exponential model holds for this data.

(a) Find the MLE of the rate parameter λ.

dat <- c(1, 1, 2, 4, 4, 6, 6, 6, 7, 8, 9, 9, 10, 12, 13,
         14, 18, 19, 24, 26, 29, 31, 42, 45, 50, 57, 60, 71, 85, 91)

cen <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1)   # 1 if observed, 0 if censored

res <- MLEexp(dat, cen)

print(res)
##   parameter    MLE    se approxCI.lb approxCI.ub
## 1    lambda  0.033 0.007       0.020       0.046
## 2        mu 30.400 6.080      18.483      42.317

So, we get a lambda of 0.033 as shown above.

(b) Find the MLE of the median remission time and S(26) = P(T > 26), where T is the remission time (in weeks).

lambda <- res$MLE[1]

# Median estimate
median_time <- log(2) / lambda

# Survival probability at 26 weeks
S26 <- 1-pexp(26, rate=lambda);

# Output the results
cat("Estimated median remission time:", round(median_time, 2), "weeks\n")
## Estimated median remission time: 21 weeks
cat("Estimated survival probability at 26 weeks:", round(S26, 4), "\n")
## Estimated survival probability at 26 weeks: 0.424

We got an estimated median remission time of 21 weeks as provided by the code, while the MLE of S(26) = P(T > 26) is 0.424 as shown above.

(c) Find a 95% confidence interval for λ.

r <- sum(cen)  # number of observed (uncensored) events
mu_hat <- 1 / res$MLE[1]  # estimated mean

chiL <- qchisq(0.025, df = 2 * r)
chiU <- qchisq(0.975, df = 2 * r)

CI_mu <- 2 * r * mu_hat / c(chiU, chiL)  # CI for μ
CI_lambda <- rev(1 / CI_mu)              # CI for λ (inverted)

cat("Manual exact 95% CI for lambda:", round(CI_lambda, 4), "\n")
## Manual exact 95% CI for lambda: 0.0214 0.0471

We got a 95% confidence interval of (0.0214, 0.0471).

(d) Find a 95% confidence interval for the mean parameter µ.

r <- sum(cen)  # number of uncensored events
mu_hat <- 1 / res$MLE[1]  # estimated mean

chiL <- qchisq(0.025, df = 2 * r)
chiU <- qchisq(0.975, df = 2 * r)

CI_mu <- 2 * r * mu_hat / c(chiU, chiL)  # CI for μ

cat("Exact 95% CI for mu:", round(CI_mu, 2), "\n")
## Exact 95% CI for mu: 21.21 46.83

The exact 95% confidence interval for the mean remission time is (21.21, 46.83) weeks.


Problem 2

Refer to the data in Question 1.

(a) Compute and plot the Kaplan-Meier estimate of the survival function S(t).

dat <- c(1, 1, 2, 4, 4, 6, 6, 6, 7, 8, 9, 9, 10, 12, 13,
         14, 18, 19, 24, 26, 29, 31, 42, 45, 50, 57, 60, 71, 85, 91)

cen <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1)   # 1 if observed, 0 if censored

km_result <- survKM2(dat, cen)
## Kaplan-Meier estimate, a.k.a. the product-limit estimate of S(t) 
## Nonparametric estimate of survival function S(t) 
## 
##  time di ni     S Var.S    se CI.lb CI.ub XXXXX     h     H Var.H  S.na
##     0  0 30 1.000 0.000 0.000 1.000 1.000       0.000 0.000 0.000 1.000
##     1  2 30 0.933 0.002 0.046 0.844 1.000       0.067 0.067 0.002 0.936
##     2  1 28 0.900 0.003 0.055 0.793 1.000       0.036 0.102 0.003 0.903
##     4  2 27 0.833 0.005 0.068 0.700 0.967       0.074 0.176 0.006 0.838
##     6  3 25 0.733 0.007 0.081 0.575 0.892       0.120 0.296 0.011 0.743
##     7  1 22 0.700 0.007 0.084 0.536 0.864       0.045 0.342 0.013 0.710
##     8  1 21 0.667 0.007 0.086 0.498 0.835       0.048 0.390 0.015 0.677
##     9  2 20 0.600 0.008 0.089 0.425 0.775       0.100 0.490 0.020 0.613
##    10  1 18 0.567 0.008 0.090 0.389 0.744       0.056 0.545 0.023 0.580
##    12  1 17 0.533 0.008 0.091 0.355 0.712       0.059 0.604 0.027 0.547
##    13  1 16 0.500 0.008 0.091 0.321 0.679       0.062 0.666 0.031 0.514
##    14  1 15 0.467 0.008 0.091 0.288 0.645       0.067 0.733 0.035 0.480
##    18  1 14 0.433 0.008 0.090 0.256 0.611       0.071 0.805 0.040 0.447
##    19  1 13 0.400 0.008 0.089 0.225 0.575       0.077 0.881 0.046 0.414
##    24  1 12 0.367 0.008 0.088 0.194 0.539       0.083 0.965 0.053 0.381
##    26  1 11 0.333 0.007 0.086 0.165 0.502       0.091 1.056 0.062 0.348
##    29  1 10 0.300 0.007 0.084 0.136 0.464       0.100 1.156 0.072 0.315
##    42  1  8 0.262 0.007 0.081 0.103 0.422       0.125 1.281 0.087 0.278
##    57  1  5 0.210 0.006 0.080 0.053 0.367       0.200 1.481 0.127 0.227
##    60  1  4 0.158 0.006 0.075 0.010 0.305       0.250 1.731 0.190 0.177
##    91  1  1 0.000   NaN   NaN   NaN   NaN       1.000 2.731 1.190 0.065
## 
## h is nonparametric estimate of the hazard function h(t).
## H is Nelson-Aallen estimate of the cumulative hazard function H(t).
## S.na is Nelson-Aallen estimate of S(t).

## 
## Mean survival time based on Kaplan-Meier estimate of S(t) 
## 
##  Estimate   Var    se  CI.lb  CI.ub
##     29.65 34.05 5.835 18.213 41.087
plot(km_result$time, km_result$S, type = "s",
     xlab = "Time (weeks)", ylab = "Survival Probability",
     main = "Kaplan-Meier Estimate of S(t)")

(b) Obtain an estimate of the median remission time. Compare it with your answer obtained in Question 1(b).

median_KM <- km_result$time[min(which(km_result$S <= 0.5))]
cat("Kaplan-Meier Median Time:", median_KM, "weeks\n")
## Kaplan-Meier Median Time: 13 weeks

The median remission time estimated by the Kaplan-Meier method is 13 weeks, which is lower than the 21-week median estimated under the exponential model. This indicates that the exponential assumption may overestimate survival time, especially early in the follow-up period.

(c) Compute and plot the nonparametric estimate of the hazard function h(t) against time. Does it support the exponential assumption?

S <- km_result$S
time <- km_result$time
haz <- -diff(S) / head(S, -1)   # hazard increments
t_mid <- (head(time, -1) + tail(time, -1)) / 2  # midpoints for plotting

# Plot h(t)
plot(t_mid, haz, type = "b", pch = 16,
     xlab = "Time (weeks)", ylab = "Estimated Hazard h(t)",
     main = "Nonparametric Estimate of the Hazard Function")

The nonparametric hazard estimate is relatively flat early on but increases noticeably after 40 weeks, especially beyond 60 weeks. This suggests the hazard is not constant over time, indicating that the exponential model may not be appropriate across the full time range.

(d) Plot the MLE of the hazard rate obtained in Question 1(a) over the plot in (c). Comment on the assumption of exponentiality.

# Add the constant MLE hazard line (from Problem 1a)
plot(t_mid, haz, type = "b", pch = 16,
     xlab = "Time (weeks)", ylab = "Estimated Hazard h(t)",
     main = "Nonparametric Hazard Function with Exponential MLE")
abline(h = 0.033, col = "red", lwd = 2, lty = 2)
legend("topright", legend = c("Nonparametric", "MLE (Exponential)"),
       col = c("black", "red"), lty = c(1, 2), lwd = c(1, 2), pch = c(16, NA))

The MLE hazard rate under the exponential model is constant at 𝜆= 0.033, shown as a red dashed line. The nonparametric hazard rises over time, indicating that the assumption of constant hazard is violated. This suggests that the exponential model may not provide an adequate fit, particularly at later times.


Problem 3

Refer to the data in Question 1. Now assume that two-parameter Weibull distribution is appropriate for the data.

(a) Obtain the MLE of two parameters.

dat <- c(1, 1, 2, 4, 4, 6, 6, 6, 7, 8, 9, 9, 10, 12, 13,
         14, 18, 19, 24, 26, 29, 31, 42, 45, 50, 57, 60, 71, 85, 91)
cen <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1)   # 1 if observed, 0 if censored

MLE(c(0.04,0.831), "weibull", dat, cen);
##   parameter   MLE    se CI.lb CI.ub
## 1    lambda 0.034 0.008 0.018 0.051
## 2     gamma 0.835 0.135 0.570 1.099

We are given 0.034 for the scale parameter, and 0.835 for the shape parameter.

(b) Obtain an estimate of S(26), and compare it with your answer obtained in Question 1(b). Comment.

1-pweibull(26, shape=0.835, scale=1/0.034)
## [1] 0.405689

We get a Weibull estimate of 0.405689 for S(26), which is slightly below the exponential and KM values (~0.424). This suggests a modestly quicker early decline, but the difference is minimal—indicating a comparable fit at 26 weeks.

(c) Make an appropriate plot to see if the data follows a Weibull distribution.

plotH("weibull", dat, cen)

##   lambda   gam    R2
## 1   0.04 0.831 0.978

The log-log hazard plot shows that the points align closely with the reference line, suggesting that the Weibull distribution provides a reasonable fit for the data.


Problem 4

7.2 In order to computerize patients’ records, a data clerk is hired to transcribe medical data from the patients’ charts to computer coding forms. The number of correct entries between errors is listed in chronological order of occurrence over a period of five days as follows: 73, 12, 40, 65, 100, 15, 70, 40, 110, 64, 200, 6, 90, 102, 20, 102, 90, and 34. Assuming that the correct entries between errors follow the two-parameter exponential distribution, obtain:

(a) An estimate of G.

dat <- c(73, 12, 40, 65, 100, 15, 70, 40, 110, 64, 200, 6, 90, 102, 20, 102, 90, 34)
res <- MLE(c(1,1), "exp2par", dat, se=F)

G <- res$MLE[2]
cat("Estimate of G:", G, "\n")
## Estimate of G: 6

The estimate of G is 6, which corresponds to the minimum value in the dataset.

(b) The MLE of l.

lambda <- res$MLE[1]
cat("MLE of lambda:", lambda, "\n")
## MLE of lambda: 0.016

The MLE of λ is 0.016, based on the two-parameter exponential fit.

(c) The MLE of m.

1/lambda + G
## [1] 68.5

The MLE of μ is approximately 68.5

(d) The probability of at least 100 correct entries between two errors.

S100 <- 1-pexp(100-G, rate=lambda)

print(S100)
## [1] 0.2222394

The probability of at least 100 correct entries between two errors is approximately 0.2222.


Problem 5

7.3 In a clinical study, 28 patients with cancer of the head and neck did not respond to chemotherapy. Their survival times in weeks are given in the following.

1.7  8.3   14.0  22.7   6.0+  13.1+
5.1  9.6   15.9  33.0   7.4+  13.4+
5.3  11.3  16.7   3.7+  8.0+  16.1+
6.0  12.1  17.0   5.0+  8.3+
8.3  12.3  21.0   5.9+  9.1+

Obtain the MLE of the parameter(s) and mean survival times, assuming:

(a) A one-parameter exponential distribution.

dat <- c(1.7, 5.1, 5.3, 6.0, 8.3, 8.3, 9.6, 11.3, 12.1, 12.3, 14.0, 15.9, 16.7, 17.0, 21.0, 22.7, 33.0, 3.7, 5.0, 5.9, 6.0, 7.4, 8.0, 8.3, 9.1, 13.1, 13.4, 16.1)

cen <- c(rep(1, 17), rep(0, 11))   # 1 if observed, 0 if censored

res_exp <- MLEexp(dat, cen)
res_exp
##   parameter    MLE    se approxCI.lb approxCI.ub
## 1    lambda  0.054 0.013       0.028       0.079
## 2        mu 18.606 4.513       9.761      27.450

Based on the results above, under the one-parameter exponential model, the estimated mean survival time is approximately 18.6 weeks, with a rate of λ = 0.054.

(b) A Weibull distribution.

res_wei <- MLE(c(0.05,1), "weibull", dat, cen)
res_wei
##   parameter   MLE    se CI.lb CI.ub
## 1    lambda 0.059 0.007 0.046 0.073
## 2     gamma 2.042 0.351 1.353 2.731
l_hat <- res_wei$MLE[1]
g_hat <- res_wei$MLE[2]

mu_weib <- (1 / l_hat) * gamma(1 + 1 / g_hat)

The MLEs for the Weibull model are λ = 0.059 and γ = 2.042, both with a mean survival time of approximately 17.45 weeks


Problem 6

7.4 In a study of deep venous thrombosis, the following blood clot lysis times in hours were recorded from 20 patients: 2, 3, 4, 5.5, 9, 13, 16.5, 17.5, 12.5, 7, 6, 17.5, 11.5, 6, 14, 25, 49, 37.5, 49, and 28. Assume that the blood clot lysis times follow the lognormal distribution.

(a) Obtain MLEs of the parameters μ and σ^2.

dat <- c(2, 3, 4, 5.5, 9, 13, 16.5, 17.5, 12.5, 7, 6, 17.5, 11.5, 6, 14, 25, 49, 37.5, 49, 28)

res_6 <- MLE(c(1,1), "lognormal", dat, se=T)
res_6
##   parameter   MLE    se CI.lb CI.ub
## 1        mu 2.464 0.195 2.081 2.846
## 2     sigma 0.872 0.138 0.602 1.142
sigma2_hat <- res_6$MLE[2]^2
sigma2_hat
## [1] 0.760384

Based on the results above, we have μ = 2.464 and σ^2 = 0.760384.

(b) Obtain 95% confidence intervals for μ and σ^2.

As shown on the results above, we have a 95% confidence intervals of (2.081, 2.846) for μ, and (0.602, 1.142) for σ^2.


Problem 7

7.5 Consider the following tumor-free times in days of 10 animals: 2, 3.5, 5, 7, 9, 10, 15, 20, 30, and 40. Assume that the tumor-free times follow the log-logistic distribution. Estimate the parameters α and γ.

dat <- c(2, 3.5, 5, 7, 9, 10, 15, 20, 30, 40)
cen <- rep(1, length(dat))

res_7 <- MLE(c(1,1), "loglogistic", dat, cen, se=T)
res_7
##   parameter   MLE    se  CI.lb CI.ub
## 1     alpha 0.014 0.017 -0.019 0.047
## 2     gamma 1.868 0.470  0.947 2.789

Based on the results, by using the log-logistic model, the estimated parameters are: the scale of α = 0.014 (so approximately 71.43 days when inverted), and the shape of γ = 1.868. With this, the wide confidence interval for α suggests some uncertainty. The shape estimate indicates a moderately increasing hazard over time.


Problem 8

7.9 Twenty-five rats were injected with a give tumor inoculum. Their times, in days, to the development of a tumor of a certain size are given in the following.

30 53 77  91 118
38 54 78  95 120
45 58 81 101 125
46 66 84 108 134
50 69 85 115 135

Which of the distributions discussed in this chapter provide a reasonable fit to the data? Estimate graphically the parameters of the distribution chosen.

dat <- c(30, 38, 45, 46, 50, 53, 54, 58, 66, 69, 77, 78, 81, 84, 85, 91, 95, 101, 108, 115, 118, 120, 125, 134, 135)

plotP("exponential", dat)

##   lambda    R2
## 1  0.017 0.808
plotP("weibull", dat)

##   lambda   gam    R2
## 1  0.011 3.035 0.973
plotP("lognormal", dat)

##      mu sigma    R2
## 1 4.332 0.411 0.962
plotP("loglogistic", dat)

##   alpha   gam    R2
## 1     0 4.343 0.947

After comparing all four probability plots, we can see that the Weibull distribution fits the best. The points align closely to the straight line with the highest R-square value of 0.973, and with the given parameters of λ ≈ 0.011 and γ ≈ 3.035.


Problem 9

7.10 Consider the data in Exercise 7.3.

(a) Make a hazard plot for each of the following distributions: exponential, Weibull, lognormal, and log-logistic.

dat <- c(1.7, 5.1, 5.3, 6.0, 8.3, 8.3, 9.6, 11.3, 12.1, 12.3, 14.0, 15.9, 16.7, 17.0, 21.0, 22.7, 33.0, 3.7, 5.0, 5.9, 6.0, 7.4, 8.0, 8.3, 9.1, 13.1, 13.4, 16.1)
cen <- c(rep(1, 17), rep(0, 11))

plotH("exponential", dat, cen)

##   lambda    R2
## 1  0.081 0.917
plotH("weibull", dat, cen)

##   lambda   gam    R2
## 1   0.06 1.761 0.962
plotH("lognormal", dat, cen)

##      mu sigma    R2
## 1 2.493 0.702 0.903
plotH("loglogistic", dat, cen)

##   alpha   gam    R2
## 1 0.002 2.471 0.916

(b) Which distribution provides a reasonable fit to the data? Estimate graphically the parameters of the distribution chosen.

Based on the plots above, the exponential hazard is constant but does not match the trend in the data, as the log-normal and log-logistic appear to be non-monotonic. The Weibull plot is the best fit with the R-square of 0.962 with the parameters of λ ≈ 0.06 and γ ≈ 1.761.


Problem 10

Redo Problem 7.10 by plotting the Cox-Snell residuals.

dat <- c(1.7, 5.1, 5.3, 6.0, 8.3, 8.3, 9.6, 11.3, 12.1, 12.3, 14.0, 15.9, 16.7, 17.0, 21.0, 22.7, 33.0, 3.7, 5.0, 5.9, 6.0, 7.4, 8.0, 8.3, 9.1, 13.1, 13.4, 16.1)
cen <- c(rep(1, 17), rep(0, 11))

res_exp <- MLE(c(1), "exponential", dat, cen)
r_exp <- attr(res_exp, "residual")
plotR(r_exp, cen)

res_wei <- MLE(c(0.05, 1), "weibull", dat, cen)
r_wei <- attr(res_wei, "residual")
plotR(r_wei, cen)

res_norm <- MLE(c(1,1), "lognormal", dat, cen)
r_lnorm <- attr(res_norm, "residual")
plotR(r_lnorm, cen)

res_log <- MLE(c(1,1), "loglogistic", dat, cen, se=F)
r_llog <- attr(res_log, "residual")
plotR(r_llog, cen)

Each Cox-Snell residual plot shows noticeable deviation from the 45-degree reference line, suggesting none of the distributions fit the data perfectly, but the lognormal distribution appears to come closest.


Problem 11

8.5 Consider the survival time of 28 cancer patients in Exercise 7.3

(a) Obtain the log-likelihoods for the exponential, Weibull, log-normal, and the extended generalized gamma distributions. Perform the likelihood ratio test and select the best distribution among these four distributions.

dat <- c(1.7, 5.1, 5.3, 6.0, 8.3, 8.3, 9.6, 11.3, 12.1, 12.3, 14.0, 15.9, 16.7, 17.0, 21.0, 22.7, 33.0, 3.7, 5.0, 5.9, 6.0, 7.4, 8.0, 8.3, 9.1, 13.1, 13.4, 16.1)
cen <- c(rep(1, 17), rep(0, 11))

sigcode <- function(p.val) ifelse(p.val < 0.001, "***", ifelse(p.val < 0.01, "**", ifelse(p.val < 0.05, "*", ifelse(p.val < 0.1, ".", ""))))

res <- list()   ### Example 8.2 & 8.4 in the textbook 4E
res$exponential <- MLE(  1     , "exponential", dat, cen, se=F)
res$weibull     <- MLE(c(1,1)  , "weibull"    , dat, cen, se=F)
res$lognormal   <- MLE(c(1,1)  , "lognormal"  , dat, cen, se=F)
res$gengamma    <- MLE(c(1,1,1), "gengamma"   , dat, cen, se=F)

p <- unlist(lapply(res, function(tab) length(tab$par)))
logL <- unlist(lapply(res, function(tab) attr(tab, "max.logL")))
tab <- data.frame(dist=names(logL), p=p, logL=logL); rownames(tab) <- NULL
tab
##          dist p      logL
## 1 exponential 1 -66.69912
## 2     weibull 2 -60.88686
## 3   lognormal 2 -62.48707
## 4    gengamma 3 -60.88234
ll <- rbind(cbind(tab[1,], tab[2,]),
            cbind(tab[1,], tab[4,]),
            cbind(tab[2,], tab[4,]),
            cbind(tab[3,], tab[4,]))
colnames(ll)[1:3] <- c("dist0", "p0", "logL0")
LRT <- -2*(ll$logL0 - ll$logL)
p.val <- 1-pchisq(LRT, df=(ll$p-ll$p0))
GOF <- ll[,c(1,4)]; rownames(GOF) <- NULL
GOF <- cbind(GOF, LRT=round(LRT,3), p.value=round(p.val,4), signif=sapply(p.val, sigcode))
GOF
##         dist0     dist    LRT p.value signif
## 1 exponential  weibull 11.625  0.0007    ***
## 2 exponential gengamma 11.634  0.0030     **
## 3     weibull gengamma  0.009  0.9242       
## 4   lognormal gengamma  3.209  0.0732      .

The likelihood ratio test suggests that the generalized gamma distribution fits the data best, as it significantly improves the fit over the exponential and log-normal models.

(b) Use the BIC and AIC procedures to select the best distribution among the four distributions in (a) plus the log-logistic distribution.

n <- length(dat)
tab$AIC <- logL-2*p
tab$BIC <- logL-p/2*log(n)
tab
##          dist p      logL       AIC       BIC
## 1 exponential 1 -66.69912 -68.69912 -68.36523
## 2     weibull 2 -60.88686 -64.88686 -64.21907
## 3   lognormal 2 -62.48707 -66.48707 -65.81928
## 4    gengamma 3 -60.88234 -66.88234 -65.88065

Based on both AIC and BIC values, the Weibull distribution provides the best fit among the four considered models.

(c) Compare the results obtained in (a) and (b) earlier and those obtained in Exercise 7.10.

The Weibull distribution, which was favored in Exercises 7.10 and 8.5(b), consistently shows the best fit across both the Cox-Snell residual analysis and model selection criteria, reinforcing its suitability for modeling the survival data.


Problem 12

Consider the following survival time in weeks of 10 mice with injection of tumor cells: 5, 16, 18+, 20, 22+, 24+, 25, 30+, 35, 40+. Do the data follow the exponential distribution with l = 0.02?

(a) Use the likelihood ratio test.

dat <- c(5, 16, 18, 20, 22, 24, 25, 30, 35, 40)
cen <- c(1,  1,  0,  1,  0,  0,  1,  0,  1,  0)

lambda0 <- 0.02
LR.test(lambda0, "exponential", dat, cen)
## Likelihood ratio test for a specific distribution with known parameters 
##    H0: S = S0 
##        or equivalently, the underlying distribution is exponential with specified parameter values. 
## 
##  parameter   MLE null.value
##     lambda 0.021       0.02
## 
##     logL  logL0   LRT p.value signif
##  -24.251 -24.26 0.019  0.8911

Based on the output, we get a p-value of 0.8911, which is quite high So, we do not reject the null hypothesis. This suggests that the exponential distribution with λ = 0.02 provides a reasonable fit for the data.

(b) Use the Hollander and Proschan’s test statistic.

sf0 <- function(tt) 1-pexp(tt, rate=lambda0)
HP.test(sf0, dat, cen)
## Hollander and Proschan's test for a specific distribution with known parameters 
##    H0: S = S0 
##        where S0 is the underlying survival function with specified parameter values 
## 
##      C sigma2 C.star
##  0.471  0.093 -0.296
## 
##       H1 p.value signif
##  S != S0  0.7671       
##   S < S0  0.6164       
##   S > S0  0.3836

We can see from the output that the p-value is large, so we fail to reject the null hypothesis, which means that there is no significant deviation from the exponential distribution.

(c) Plot the Kaplan–Meier estimator of S(t) and the hypothesized distribution.

Since the Kaplan-Meier estimate is provided in part b, we can see that the red line closely follows the steps of the KM curve within confidence bounds, supporting a good fit.


Problem 13

Suppose that T has an exponential distribution with parameter λ. However, the hazard function λ varies across the individuals (i.e., population heterogeneity). Specifically, suppose that the distribution of T given λ has the probability density function expressed as

π(λ) = λ^(k−1) e^(−λ/α) / α^(k) Γ(k) ,    λ > 0.

(a) Find the unconditional probability density function f(t).

(b) Find the unconditional survival function S(t).

(c) Find the unconditional hazard function h(t). Does the unconditional distribution of T have the increasing failure rate (IFR) property? or the decreasing failure rate (DFR) property? Justify.

LS0tDQp0aXRsZTogIlRha2UtSG9tZSBNaWR0ZXJtIDMiDQpvdXRwdXQ6IG9wZW5pbnRybzo6bGFiX3JlcG9ydA0KLS0tDQoNCmBgYHtyfQ0KbGlicmFyeShzdXJ2aXZhbCkNCmxpYnJhcnkoZmxleHN1cnYpDQpgYGANCg0KLS0tDQoNCg0KIyMjIFNldHVwIENvZGUNCg0KDQoqKkthcGxhbi1NZWllcioqDQoNCmBgYHtyfQ0Kc3VydktNMiA8LSBmdW5jdGlvbihkYXQsIGNlbj1OQSwgY29uZj0wLjk1LCBlcHM9MWUtOSkgew0KICAgbiA8LSBsZW5ndGgoZGF0KTsgaWYgKGlzLm5hKGNlblsxXSkpIGNlbiA8LSByZXAoVCwgbik7IGNlbiA8LSBhcy5sb2dpY2FsKGNlbikNCiAgIGRpIDwtIHRhYmxlKGRhdFtjZW5dKQ0KICAgdGkgPC0gYXMubnVtZXJpYyhuYW1lcyhkaSkpIC0gZXBzDQogICAgbSA8LSBsZW5ndGgodGkpDQogICBuaSA8LSBOVUxMOyBmb3IoaSBpbiAxOm0pIG5pW2ldIDwtIHN1bShkYXQgPj0gdGlbaV0pDQogICAgUyA8LSBjdW1wcm9kKChuaS1kaSkvbmkpDQogICAgViA8LSBTXjIqY3Vtc3VtKGRpL25pLyhuaS1kaSkpDQogICB0aSA8LSBjKDAsIHRpKTsgZGkgPC0gYygwLCBhcy52ZWN0b3IoZGkpKTsgbmkgPC0gYyhuLCBuaSk7IFMgPC0gYygxLCBTKTsgViA8LSBjKDAsIFYpDQogICBzZSA8LSBzcXJ0KFYpOyB6IDwtIHFub3JtKDEtKDEtY29uZikvMikNCiAgIENJLmxiIDwtIFMgLSB6KnNlOyBDSS5sYiA8LSBDSS5sYiAtIChDSS5sYiA8IDApKiBDSS5sYg0KICAgQ0kudWIgPC0gUyArIHoqc2U7IENJLnViIDwtIENJLnViIC0gKENJLnViID4gMSkqKENJLnViLTEpDQoNCiAgICAgaCA8LSBkaS9uaQ0KICAgICBIIDwtIGN1bXN1bShoKQ0KICAgVi5IIDwtIGN1bXN1bShkaS9uaV4yKQ0KICAgUy5IIDwtIGV4cCgtSCkNCg0KICAgY2F0KCJLYXBsYW4tTWVpZXIgZXN0aW1hdGUsIGEuay5hLiB0aGUgcHJvZHVjdC1saW1pdCBlc3RpbWF0ZSBvZiBTKHQpIFxuIikNCiAgIGNhdCgiTm9ucGFyYW1ldHJpYyBlc3RpbWF0ZSBvZiBzdXJ2aXZhbCBmdW5jdGlvbiBTKHQpIFxuXG4iKSAgIA0KICAgdGFiIDwtIGRhdGEuZnJhbWUodGltZT10aSwgZGk9ZGksIG5pPW5pLCBTPXJvdW5kKFMsMyksIFZhci5TPXJvdW5kKFYsMyksIHNlPXJvdW5kKHNlLDMpLA0KICAgICAgICAgICAgICAgICAgICAgQ0kubGI9cm91bmQoQ0kubGIsMyksIENJLnViPXJvdW5kKENJLnViLDMpLCBYWFhYWD0iIiwNCiAgICAgICAgICAgICAgICAgICAgIGg9cm91bmQoaCwzKSwgSD1yb3VuZChILDMpLCBWYXIuSD1yb3VuZChWLkgsMyksIFMubmE9cm91bmQoUy5ILDMpKQ0KICAgcHJpbnQodGFiLCByb3cubmFtZXM9RikNCiAgIGNhdCgiXG5oIGlzIG5vbnBhcmFtZXRyaWMgZXN0aW1hdGUgb2YgdGhlIGhhemFyZCBmdW5jdGlvbiBoKHQpLiIpDQogICBjYXQoIlxuSCBpcyBOZWxzb24tQWFsbGVuIGVzdGltYXRlIG9mIHRoZSBjdW11bGF0aXZlIGhhemFyZCBmdW5jdGlvbiBIKHQpLiIpDQogICBjYXQoIlxuUy5uYSBpcyBOZWxzb24tQWFsbGVuIGVzdGltYXRlIG9mIFModCkuIFxuXG4iKQ0KDQogICBwYXIobWFyPWMoNSw1LDQsMSkpDQogICBwbG90KGModGksIDEuMiptYXgodGkpKSwgYyhTLCBtaW4oUykpLCB0eXBlPSJzIiwgbHdkPTIsIA0KICAgICAgeGxhYj0iVGltZSwgdCIsIHlsYWI9ZXhwcmVzc2lvbigiRXN0aW1hdGVkIFN1cnZpdmFsIFByb2JhYmlsaXR5LCAgICIqaGF0KFMpKHQpKSwNCiAgICAgIG1haW49IkthcGxhbi1NZWllciBlc3RpbWF0ZSwgYS5rLmEuIHRoZSBwcm9kdWN0LWxpbWl0IGVzdGltYXRlIG9mIFModCkiKSAgIA0KICAgcG9pbnRzKHRpWy0xXSwgU1stMV0sIHBjaD0xOSkNCiAgIHBvaW50cyh0aSAgICAsIENJLmxiLCB0eXBlPSJzIiwgbHR5PTIpDQogICBwb2ludHModGkgICAgLCBDSS51YiwgdHlwZT0icyIsIGx0eT0yKQ0KDQogICAgbSA8LSBtICsgMQ0KICAgU3QgPC0gYyhTWy1tXSpkaWZmKHRpKSwgMCkNCiAgIG11IDwtIHN1bShTdCkNCiAgIEFpIDwtIHJldihjdW1zdW0ocmV2KFN0KSkpICAgIyA9IG11IC0gY3Vtc3VtKGMoMCwgU3RbLW1dKSkNCiAgIFZtIDwtIHN1bSgoQWleMipkaS9uaS8obmktZGkpKVstbV0pDQogICBzZSA8LSBzcXJ0KFZtKQ0KDQogICAjIGlmICghY2VuW3doaWNoLm1heChkYXQpXSksIGNvdWxkIHJlcGxhY2Ugdy8gbGltaXQgTCANCiAgICMgZm9yIE1lYW4gc3Vydml2YWwgdGltZSBsaW1pdGVkIHRvIGEgdGltZSBMLg0KDQogICBjYXQoIlxuTWVhbiBzdXJ2aXZhbCB0aW1lIGJhc2VkIG9uIEthcGxhbi1NZWllciBlc3RpbWF0ZSBvZiBTKHQpIFxuXG4iKSAgIA0KICAgcHJpbnQoZGF0YS5mcmFtZShFc3RpbWF0ZT1yb3VuZChtdSwzKSwgVmFyPXJvdW5kKFZtLDMpLCBzZT1yb3VuZChzZSwzKSwNCiAgICAgIENJLmxiPXJvdW5kKG11LXoqc2UsMyksIENJLnViPXJvdW5kKG11K3oqc2UsMykpLCByb3cubmFtZXM9RikNCg0KICAgaW52aXNpYmxlKHRhYikgfQ0KYGBgDQoNCg0KKipSZXMqKg0KDQpgYGB7cn0NCnF1aWV0IDwtIGZ1bmN0aW9uKGZuKSB7ICAgIyBzdXBwcmVzcyBvdXRwdXQgbWVzc2FnZXMNCiAgICBzaW5rKHRlbXBmaWxlKCkpDQogICAgb24uZXhpdChzaW5rKCkpDQogICAgaW52aXNpYmxlKGZvcmNlKGZuKSkgfQ0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KcGxvdFIgPC0gZnVuY3Rpb24ociwgY2VuKSB7DQogICBjZW4gPC0gYXMubG9naWNhbChjZW4pDQogICByWyFjZW5dIDwtIHJbIWNlbl0gKyAxICAgIyBvciBhZGQgMC42OTMNCiAgIEtNb2JqIDwtIHF1aWV0KHN1cnZLTTIociwgY2VuKSkNCiAgICAgciA8LSBLTW9iaiR0aW1lDQogICAgU3IgPC0gS01vYmokUw0KICAgZml0IDwtIGxtKC1sb2coU3IpIH4gciwgc3Vic2V0PShTciAhPSAwKSkNCiAgIHRhYiA8LSBsaXN0KHJlc2lkdWFsPXIsIFNyPVNyLCBSMj1zdW1tYXJ5KGZpdCkkci5zcSkNCg0KICAgcGxvdChyLCAtbG9nKFNyKSwgeGxhYj0iUmVzaWR1YWwsIHIiLCB5bGFiPWV4cHJlc3Npb24oLWxvZyhoYXQoUykocikpKSwgDQogICAgICAgIG1haW49IkNveC1TbmVsbCByZXNpZHVhbCBwbG90IHdpdGggdW5jZW5zb3JlZC9jZW5zb3JlZCBkYXRhIikgICANCiAgIGFibGluZShmaXQsIGx0eT0yLCBjb2w9InJlZCIpDQogICBhYmxpbmUoYT0wLCBiPTEsIGx3ZD0yKSANCg0KICAgaW52aXNpYmxlKHRhYikgfQ0KYGBgDQoNCg0KKipNTEUqKg0KDQpgYGB7cn0NCm5sTCA8LSBmdW5jdGlvbihwYXIsIGRpc3Q9YygiZXhwb25lbnRpYWwiLCAiZXhwMnBhciIsICJ3ZWlidWxsIiwgImxvZ25vcm1hbCIsICJnYW1tYSIsICJnZW5nYW1tYSIsICJsb2dsb2dpc3RpYyIsICJnb21wZXJ0eiIpLCANCiAgICAgICAgICAgICAgICBkYXQsIGNlbj1OQSkgew0KICAgbiA8LSBsZW5ndGgoZGF0KTsgaWYgKGlzLm5hKGNlblsxXSkpIGNlbiA8LSByZXAoVCwgbik7IGNlbiA8LSBhcy5sb2dpY2FsKGNlbikNCg0KICAgaWYgKGRpc3RbMV09PSJleHBvbmVudGlhbCIpIHsgDQogICAgICBsYW1iZGEgPC0gcGFyOyBpZiAoIShsYW1iZGEgPiAwKSkgcmV0dXJuKEluZikNCiAgICAgIHBkZiA8LSBmdW5jdGlvbih0dCkgICBkZXhwKHR0LCByYXRlPWxhbWJkYSkNCiAgICAgICBzZiA8LSBmdW5jdGlvbih0dCkgMS1wZXhwKHR0LCByYXRlPWxhbWJkYSkgfQ0KICAgaWYgKGRpc3RbMV09PSJleHAycGFyIikgeyAgICAgICANCiAgICAgIGxhbWJkYSA8LSBwYXJbMV07IGlmICghKGxhbWJkYSA+IDAgICApKSByZXR1cm4oSW5mKQ0KICAgICAgICAgICBHIDwtIHBhclsyXTsgaWYgKCEoRyA8PSBtaW4oZGF0KSkpIHJldHVybihJbmYpDQogICAgICBwZGYgPC0gZnVuY3Rpb24odHQpICAgZGV4cCh0dC1HLCByYXRlPWxhbWJkYSkNCiAgICAgICBzZiA8LSBmdW5jdGlvbih0dCkgMS1wZXhwKHR0LUcsIHJhdGU9bGFtYmRhKSB9DQogICBpZiAoZGlzdFsxXT09IndlaWJ1bGwiKSB7IA0KICAgICAgaWYgKCFhbGwocGFyPjApKSByZXR1cm4oSW5mKQ0KICAgICAgbGFtYmRhIDwtIHBhclsxXTsgZ2FtIDwtIHBhclsyXQ0KICAgICAgcGRmIDwtIGZ1bmN0aW9uKHR0KSAgIGR3ZWlidWxsKHR0LCBzaGFwZT1nYW0sIHNjYWxlPTEvbGFtYmRhKQ0KICAgICAgIHNmIDwtIGZ1bmN0aW9uKHR0KSAxLXB3ZWlidWxsKHR0LCBzaGFwZT1nYW0sIHNjYWxlPTEvbGFtYmRhKSB9DQogICBpZiAoZGlzdFsxXT09ImxvZ25vcm1hbCIpIHsgICAgICAgDQogICAgICAgICBtdSA8LSBwYXJbMV0NCiAgICAgIHNpZ21hIDwtIHBhclsyXTsgaWYgKCEoc2lnbWEgPiAwKSkgcmV0dXJuKEluZikNCiAgICAgIHBkZiA8LSBmdW5jdGlvbih0dCkgICBkbG5vcm0odHQsIG1lYW5sb2c9bXUsIHNkbG9nPXNpZ21hKQ0KICAgICAgIHNmIDwtIGZ1bmN0aW9uKHR0KSAxLXBsbm9ybSh0dCwgbWVhbmxvZz1tdSwgc2Rsb2c9c2lnbWEpIH0NCiAgIGlmIChkaXN0WzFdPT0iZ2FtbWEiKSB7IA0KICAgICAgaWYgKCFhbGwocGFyPjApKSByZXR1cm4oSW5mKQ0KICAgICAgbGFtYmRhIDwtIHBhclsxXTsgZ2FtIDwtIHBhclsyXQ0KICAgICAgcGRmIDwtIGZ1bmN0aW9uKHR0KSAgIGRnYW1tYSh0dCwgc2hhcGU9Z2FtLCBzY2FsZT0xL2xhbWJkYSkNCiAgICAgICBzZiA8LSBmdW5jdGlvbih0dCkgMS1wZ2FtbWEodHQsIHNoYXBlPWdhbSwgc2NhbGU9MS9sYW1iZGEpIH0NCiAgIGlmIChkaXN0WzFdPT0iZ2VuZ2FtbWEiKSB7IA0KICAgICAgaWYgKCFhbGwocGFyWy0yXT4wKSkgcmV0dXJuKEluZikNCiAgICAgIGxhbWJkYSA8LSBwYXJbMV07IGFscGhhIDwtIHBhclsyXTsgZ2FtIDwtIHBhclszXQ0KICAgICAgcGRmIDwtIGZ1bmN0aW9uKHR0KSBpZiAoYWxwaGEgPCAwKSANCiAgICAgICAgICAgZGdlbmdhbW1hKHR0LCBtdT0tbG9nKGxhbWJkYSksIHNpZ21hPS0xL2FscGhhL3NxcnQoZ2FtKSwgUT0tMS9zcXJ0KGdhbSkpIGVsc2UNCiAgICAgICAgICAgZGdlbmdhbW1hKHR0LCBtdT0tbG9nKGxhbWJkYSksIHNpZ21hPSAxL2FscGhhL3NxcnQoZ2FtKSwgUT0gMS9zcXJ0KGdhbSkpIA0KICAgICAgIHNmIDwtIGZ1bmN0aW9uKHR0KSBpZiAoYWxwaGEgPCAwKSANCiAgICAgICAgIDEtcGdlbmdhbW1hKHR0LCBtdT0tbG9nKGxhbWJkYSksIHNpZ21hPS0xL2FscGhhL3NxcnQoZ2FtKSwgUT0tMS9zcXJ0KGdhbSkpIGVsc2UNCiAgICAgICAgIDEtcGdlbmdhbW1hKHR0LCBtdT0tbG9nKGxhbWJkYSksIHNpZ21hPSAxL2FscGhhL3NxcnQoZ2FtKSwgUT0gMS9zcXJ0KGdhbSkpIH0NCiAgIGlmIChkaXN0WzFdPT0ibG9nbG9naXN0aWMiKSB7IA0KICAgICAgaWYgKCFhbGwocGFyPjApKSByZXR1cm4oSW5mKQ0KICAgICAgYWxwaGEgPC0gcGFyWzFdOyBnYW0gPC0gcGFyWzJdDQogICAgICBwZGYgPC0gZnVuY3Rpb24odHQpICAgZGxsb2dpcyh0dCwgc2hhcGU9Z2FtLCBzY2FsZT0xL2FscGhhXigxL2dhbSkpDQogICAgICAgc2YgPC0gZnVuY3Rpb24odHQpIDEtcGxsb2dpcyh0dCwgc2hhcGU9Z2FtLCBzY2FsZT0xL2FscGhhXigxL2dhbSkpIH0NCiAgIGlmIChkaXN0WzFdPT0iZ29tcGVydHoiKSB7IA0KICAgICAgaWYgKCFhbGwocGFyPjApKSByZXR1cm4oSW5mKQ0KICAgICAgbGFtYmRhIDwtIHBhclsxXTsgZ2FtIDwtIHBhclsyXQ0KICAgICAgcGRmIDwtIGZ1bmN0aW9uKHR0KSAgIGRnb21wZXJ0eih0dCwgc2hhcGU9Z2FtLCByYXRlPWV4cChsYW1iZGEpKQ0KICAgICAgIHNmIDwtIGZ1bmN0aW9uKHR0KSAxLXBnb21wZXJ0eih0dCwgc2hhcGU9Z2FtLCByYXRlPWV4cChsYW1iZGEpKSB9DQoNCiAgIGxvZ0wgPC0gc3VtKGxvZyhwZGYoZGF0W2Nlbl0pKSkgKyBzdW0obG9nKHNmKGRhdFshY2VuXSkpKQ0KICAgdHQgPC0gc29ydChkYXQpDQogICBhdHRyKGxvZ0wsICJIIikgPC0gLWxvZyhzZih0dCkpDQogICAtbG9nTCB9DQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCk1MRSA8LSBmdW5jdGlvbihwYXIwLCBkaXN0LCBkYXQsIGNlbj1OQSwgc2U9VCwgY29uZj0wLjk1KSB7DQogICBzdXBwcmVzc1dhcm5pbmdzKA0KICAgTUxvYmogPC0gb3B0aW0ocGFyMCwgbmxMLCBkaXN0PWRpc3QsIGRhdD1kYXQsIGNlbj1jZW4sIGhlc3NpYW49c2UpKQ0KICAgbWxlIDwtIE1Mb2JqJHBhcg0KICAgICBWIDwtIHRyeShzb2x2ZShNTG9iaiRoZXNzaWFuKSwgc2lsZW50PVQpDQogICBpZiAoc2UpIHNlIDwtIHNxcnQoZGlhZyhWKSkgZWxzZSBzZSA8LSBOQQ0KICAgeiA8LSBxbm9ybSgxLSgxLWNvbmYpLzIpDQogICBDSS5sYiA8LSBtbGUgLSB6KnNlDQogICBDSS51YiA8LSBtbGUgKyB6KnNlICAgIyBhcHByb3hpbWF0ZSBjb25maWRlbmNlIGludGVydmFsDQogDQogICBwYXIgPC0gc3dpdGNoKGRpc3QsIA0KICAgICAgZXhwb25lbnRpYWw9YygibGFtYmRhIiAgICAgICAgICksDQogICAgICBleHAycGFyICAgID1jKCJsYW1iZGEiLCAiRyIgICAgKSwNCiAgICAgIHdlaWJ1bGwgICAgPWMoImxhbWJkYSIsICJnYW1tYSIpLA0KICAgICAgbG9nbm9ybWFsICA9YygibXUiICAgICwgInNpZ21hIiksDQogICAgICBnYW1tYSAgICAgID1jKCJsYW1iZGEiLCAiZ2FtbWEiKSwNCiAgICAgIGdlbmdhbW1hICAgPWMoImxhbWJkYSIsICJhbHBoYSIgLCAiZ2FtbWEiKSwNCiAgICAgIGxvZ2xvZ2lzdGljPWMoImFscGhhIiAsICJnYW1tYSIpLA0KICAgICAgZ29tcGVydHogICA9YygibGFtYmRhIiwgImdhbW1hIikpDQogICB0YWIgPC0gZGF0YS5mcmFtZShwYXJhbWV0ZXI9cGFyLCBNTEU9cm91bmQobWxlLDMpLCBzZT1yb3VuZChzZSwzKSwgDQogICAgICAgICAgICAgICAgICAgICBDSS5sYj1yb3VuZChDSS5sYiwzKSwgQ0kudWI9cm91bmQoQ0kudWIsMykpDQogICBsb2dMIDwtIC1ubEwobWxlLCBkaXN0LCBkYXQsIGNlbikgICAjID0gLU1Mb2JqJHZhbA0KICAgYXR0cih0YWIsICJtYXgubG9nTCIpIDwtIGxvZ0wNCiAgIGF0dHIodGFiLCAicmVzaWR1YWwiKSA8LSBhdHRyKGxvZ0wsICJIIikgICAjIENveC1TbmVsbCByZXNpZHVhbHMgDQogICB0YWIgfQ0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KTUxFZXhwIDwtIGZ1bmN0aW9uKGRhdCwgY2VuPU5BLCBzaGlmdD1GLCBjb25mPTAuOTUsIGVwcz0xZS05KSB7DQogICBuIDwtIGxlbmd0aChkYXQpOyBpZiAoaXMubmEoY2VuWzFdKSkgY2VuIDwtIHJlcChULCBuKTsgY2VuIDwtIGFzLmxvZ2ljYWwoY2VuKQ0KICAgciA8LSBuLXN1bSghY2VuKSAtIHNoaWZ0OyBuYyA8LSBsZW5ndGgodW5pcXVlKGRhdFshY2VuXSkpDQogICBkYXRbIWNlbl0gPC0gZGF0WyFjZW5dICsgZXBzDQoNCiAgIHQxIDwtIG1pbihkYXQpDQogICBtbGUgPC0gci9zdW0oZGF0IC0gdDEqc2hpZnQpOyBtbGVbMl0gPC0gbXUgPC0gMS9tbGUNCiAgIHNlIDwtIG1sZS9zcXJ0KHIpDQogICB6IDwtIHFub3JtKDEtKDEtY29uZikvMikNCiAgIENJLmxiIDwtIG1sZSAtIHoqc2UNCiAgIENJLnViIDwtIG1sZSArIHoqc2UgICAjIGFwcHJveGltYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWwNCg0KICAgdGFiIDwtIGRhdGEuZnJhbWUocGFyYW1ldGVyPWMoImxhbWJkYSIsICJtdSIpLCBNTEU9cm91bmQobWxlLDMpLCBzZT1yb3VuZChzZSwzKSwgDQogICAgICAgICAgICAgICAgICAgICBhcHByb3hDSS5sYj1yb3VuZChDSS5sYiwzKSwgYXBwcm94Q0kudWI9cm91bmQoQ0kudWIsMykpDQoNCiAgIGlmIChzaGlmdCkgeyAgICMgZGlzdD0iZXhwMnBhciINCiAgICAgICBHIDwtIHQxIC0gbXUvbg0KICAgICAgc2UgPC0gbXUvbiooMSsxL3IpDQogICAgICBDSS5sYiA8LSBHIC0geipzZQ0KICAgICAgQ0kudWIgPC0gRyArIHoqc2UgICAjIGFwcHJveGltYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWwNCg0KICAgICAgdGFiIDwtIHJiaW5kKHRhYiwgZGF0YS5mcmFtZShwYXJhbWV0ZXI9IkciLCBNTEU9cm91bmQoRywzKSwgc2U9cm91bmQoc2UsMyksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhcHByb3hDSS5sYj1yb3VuZChDSS5sYiwzKSwgYXBwcm94Q0kudWI9cm91bmQoQ0kudWIsMykpKSB9DQoNCiAgIGlmICgobmM9PTApIHx8ICgobmM9PTEpICYmICghY2VuW3doaWNoLm1heChkYXQpXSkpKSB7DQogICAgICBjaGlMIDwtIHFjaGlzcSggICgxLWNvbmYpLzIsIGRmPTIqcikNCiAgICAgIGNoaVUgPC0gcWNoaXNxKDEtKDEtY29uZikvMiwgZGY9MipyKQ0KICAgICAgQ0kgPC0gMipyKm11L2MoY2hpVSwgY2hpTCkNCiAgICAgIENJIDwtIHJiaW5kKHJldigxL0NJKSwgQ0kpICAgIyBleGFjdCBjb25maWRlbmNlIGludGVydmFsDQoNCiAgICAgIGlmIChzaGlmdCkgeyAgICMgZGlzdD1leHAycGFyDQogICAgICAgICBmVSA8LSBxZihjb25mLCBkZjE9MiwgZGYyPTIqcikgICAjIGFwcHJveCByKigxLygxLWNvbmYpXigxL3IpIC0gMSkNCiAgICAgICAgIENJIDwtIHJiaW5kKENJLCBjKHQxIC0gZlUqbXUvbiwgdDEpKSB9DQoNCiAgICAgIGNvbG5hbWVzKENJKSA8LSBjKCJleGFjdENJLmxiIiwgImV4YWN0Q0kudWIiKTsgcm93bmFtZXMoQ0kpIDwtIE5VTEwgICAgICANCiAgICAgIHRhYiA8LSBjYmluZCh0YWIsIHJvdW5kKENJLDMpKSB9DQoNCiAgIGlmIChzaGlmdCkgew0KICAgICAgY2F0KCJOb3RlOiBGb3IgdHdvLXBhcmFtZXRlciBleHBvbmVudGlhbCBkaXN0cmlidXRpb24gKHdpdGggYSBzaGlmdCBwYXJhbWV0ZXIpLCBcbiIpDQogICAgICBjYXQoIiAgICAgIHRoZSBtaW5pbXVtIHZhcmlhbmNlIHVuYmlhc2VkIGVzdGltYXRlcyBhcmUgcHJvdmlkZWQgcmF0aGVyIHRoYW4gTUxFLiBcblxuIikgfQ0KICAgdGFiIH0NCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KTUxFbG5vcm0gPC0gZnVuY3Rpb24oZGF0LCBjb25mPTAuOTUpIHsNCiAgIG4gPC0gbGVuZ3RoKGRhdCkNCiAgIG11IDwtIG1lYW4obG9nKGRhdCkpDQogICBzMiA8LSAgdmFyKGxvZyhkYXQpKTsgc2lnbWEgPC0gc3FydChzMioobi0xKS9uKQ0KICAgbWxlIDwtIGMobXUsIHNpZ21hKQ0KICAgc2UgPC0gYyhzaWdtYS9zcXJ0KG4pLCBzaWdtYSpzcXJ0KHNxcnQoMioobi0xKSkvbikpDQogICAgICB0cSA8LSBxdCgxLSgxLWNvbmYpLzIsIGRmPW4tMSkNCiAgIENJLmxiIDwtIG11IC0gdHEqc3FydChzMi8obi0xKSkNCiAgIENJLnViIDwtIG11ICsgdHEqc3FydChzMi8obi0xKSkgICAgICAgICAjIGV4YWN0IGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIG11DQogICAgY2hpTCA8LSBxY2hpc3EoICAoMS1jb25mKS8yLCBkZj1uLTEpDQogICAgY2hpVSA8LSBxY2hpc3EoMS0oMS1jb25mKS8yLCBkZj1uLTEpDQogICBDSS5sYiA8LSBjKENJLmxiLCBzaWdtYSpzcXJ0KG4vY2hpVSkpDQogICBDSS51YiA8LSBjKENJLnViLCBzaWdtYSpzcXJ0KG4vY2hpTCkpICAgIyBleGFjdCBjb25maWRlbmNlIGludGVydmFsIGZvciBzaWdtYQ0KDQogICB0YWIgPC0gZGF0YS5mcmFtZShwYXJhbWV0ZXI9YygibXUiLCAic2lnbWEiKSwgTUxFPXJvdW5kKG1sZSwzKSwgc2U9cm91bmQoc2UsMyksIA0KICAgICAgICAgICAgICAgICAgICAgZXhhY3RDSS5sYj1yb3VuZChDSS5sYiwzKSwgZXhhY3RDSS51Yj1yb3VuZChDSS51YiwzKSkNCiAgIHRhYiB9DQpgYGANCg0KDQoqKlBsb3RzKioNCg0KYGBge3J9DQpwbG90UCA8LSBmdW5jdGlvbihkaXN0PWMoImV4cG9uZW50aWFsIiwgIndlaWJ1bGwiLCAibG9nbm9ybWFsIiwgImxvZ2xvZ2lzdGljIiksIGRhdCkgew0KICAgbiA8LSBsZW5ndGgoZGF0KQ0KICAgbWkgPC0gdGFibGUoZGF0KQ0KICAgdGkgPC0gYXMubnVtZXJpYyhuYW1lcyhtaSkpOyBpZHggPC0gb3JkZXIodGkpOyB0aSA8LSB0aVtpZHhdOyBtaSA8LSBhcy52ZWN0b3IobWlbaWR4XSkNCiAgICBGIDwtIChjdW1zdW0obWkpLTAuNSkvbg0KDQogICBpZiAoZGlzdFsxXT09ImV4cG9uZW50aWFsIikgeyANCiAgICAgIHggPC0gbG9nKDEvKDEtRikpOyB4bGFiIDwtIGV4cHJlc3Npb24oLWxvZygxLWhhdChGKSh0KSkpDQogICAgICB5IDwtIHRpICAgICAgICAgIDsgeWxhYiA8LSAidCINCiAgICAgIGZpdCA8LSBsbSh5IH4gLTEgKyB4KQ0KICAgICAgdGFiIDwtIGRhdGEuZnJhbWUobGFtYmRhPTEvZml0JGNvZWYpIH0NCiAgIGlmIChkaXN0WzFdPT0id2VpYnVsbCIpIHsgDQogICAgICB4IDwtIGxvZyhsb2coMS8oMS1GKSkpOyB4bGFiIDwtIGV4cHJlc3Npb24obG9nKC1sb2coMS1oYXQoRikodCkpKSkNCiAgICAgIHkgPC0gbG9nKHRpKSAgICAgICAgICA7IHlsYWIgPC0gImxvZyh0KSINCiAgICAgIGZpdCA8LSBsbSh5IH4geCkNCiAgICAgIHRhYiA8LSBkYXRhLmZyYW1lKGxhbWJkYT1leHAoLWZpdCRjb2VmWzFdKSwgZ2FtPTEvZml0JGNvZWZbMl0pIH0NCiAgIGlmIChkaXN0WzFdPT0ibG9nbm9ybWFsIikgeyAgICAgICANCiAgICAgIHggPC0gcW5vcm0oRik7IHhsYWIgPC0gZXhwcmVzc2lvbihQaGleLTEqKGhhdChGKSh0KSkpDQogICAgICB5IDwtIGxvZyh0aSkgOyB5bGFiIDwtICJsb2codCkiDQogICAgICBmaXQgPC0gbG0oeSB+IHgpDQogICAgICB0YWIgPC0gZGF0YS5mcmFtZShtdT1maXQkY29lZlsxXSwgc2lnbWE9Zml0JGNvZWZbMl0pIH0NCiAgIGlmIChkaXN0WzFdPT0ibG9nbG9naXN0aWMiKSB7IA0KICAgICAgeCA8LSBsb2coMS8oMS1GKS0xKTsgeGxhYiA8LSBleHByZXNzaW9uKC1sb2coKDEtaGF0KEYpKHQpKS9oYXQoRikodCkpKQ0KICAgICAgeSA8LSBsb2codGkpICAgICAgIDsgeWxhYiA8LSAibG9nKHQpIg0KICAgICAgZml0IDwtIGxtKHkgfiB4KQ0KICAgICAgdGFiIDwtIGRhdGEuZnJhbWUoYWxwaGE9ZXhwKC1maXQkY29lZlsxXS9maXQkY29lZlsyXSksIGdhbT0xL2ZpdCRjb2VmWzJdKSB9DQoNCiAgIHBsb3QoeCwgeSwgeGxhYj14bGFiLCB5bGFiPXlsYWIsIG1haW49IlByb2JhYmlsaXR5IHBsb3Qgd2l0aCB1bmNlbnNvcmVkIGRhdGEiKSAgIA0KICAgYWJsaW5lKGZpdCwgbHdkPTIpDQogICB0YWIkUjIgPC0gc3VtbWFyeShmaXQpJHIuc3E7IHJvd25hbWVzKHRhYikgPC0gTlVMTA0KICAgcm91bmQodGFiLDMpIH0NCiANCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KcGxvdEggPC0gZnVuY3Rpb24oZGlzdD1jKCJleHBvbmVudGlhbCIsICJ3ZWlidWxsIiwgImxvZ25vcm1hbCIsICJsb2dsb2dpc3RpYyIpLCBkYXQsIGNlbj1OQSkgew0KICAgbiA8LSBsZW5ndGgoZGF0KTsgaWYgKGlzLm5hKGNlblsxXSkpIGNlbiA8LSByZXAoVCwgbik7IGNlbiA8LSBhcy5sb2dpY2FsKGNlbikNCiAgIGlkeCA8LSBvcmRlcihkYXQpDQogICBjZW4gPC0gY2VuW2lkeF0NCiAgIGRhdCA8LSBkYXRbaWR4XTsgZGF0IDwtIGRhdFtjZW5dDQoNCiAgIHRpIDwtIHVuaXF1ZShkYXQpDQogICBoIDwtIDEvKG46MSk7IGgub2xkIDwtIGhbY2VuXQ0KICAgaCA8LSBOVUxMOyBmb3IodHQgaW4gdGkpIGggPC0gYyhoLCBzdW0oaC5vbGRbdHQ9PWRhdF0pKSANCiAgIEggPC0gY3Vtc3VtKGgpDQoNCiAgIGlmIChkaXN0WzFdPT0iZXhwb25lbnRpYWwiKSB7IA0KICAgICAgeCA8LSBIIDsgeGxhYiA8LSBleHByZXNzaW9uKGhhdChIKSh0KSkNCiAgICAgIHkgPC0gdGk7IHlsYWIgPC0gInQiDQogICAgICBmaXQgPC0gbG0oeSB+IC0xICsgeCkNCiAgICAgIHRhYiA8LSBkYXRhLmZyYW1lKGxhbWJkYT0xL2ZpdCRjb2VmKSB9DQogICBpZiAoZGlzdFsxXT09IndlaWJ1bGwiKSB7IA0KICAgICAgeCA8LSBsb2coSCkgOyB4bGFiIDwtIGV4cHJlc3Npb24obG9nKGhhdChIKSh0KSkpDQogICAgICB5IDwtIGxvZyh0aSk7IHlsYWIgPC0gImxvZyh0KSINCiAgICAgIGZpdCA8LSBsbSh5IH4geCkNCiAgICAgIHRhYiA8LSBkYXRhLmZyYW1lKGxhbWJkYT1leHAoLWZpdCRjb2VmWzFdKSwgZ2FtPTEvZml0JGNvZWZbMl0pIH0NCiAgIGlmIChkaXN0WzFdPT0ibG9nbm9ybWFsIikgeyAgICAgICANCiAgICAgIHggPC0gcW5vcm0oMS1leHAoLUgpKTsgeGxhYiA8LSBleHByZXNzaW9uKFBoaV4tMSooMS1lXi1oYXQoSCkodCkpKQ0KICAgICAgeSA8LSBsb2codGkpICAgICAgICAgOyB5bGFiIDwtICJsb2codCkiDQogICAgICBmaXQgPC0gbG0oeSB+IHgpDQogICAgICB0YWIgPC0gZGF0YS5mcmFtZShtdT1maXQkY29lZlsxXSwgc2lnbWE9Zml0JGNvZWZbMl0pIH0NCiAgIGlmIChkaXN0WzFdPT0ibG9nbG9naXN0aWMiKSB7IA0KICAgICAgeCA8LSBsb2coZXhwKEgpLTEpOyB4bGFiIDwtIGV4cHJlc3Npb24obG9nKGVeaGF0KEgpKHQpKi0xKSkNCiAgICAgIHkgPC0gbG9nKHRpKSAgICAgIDsgeWxhYiA8LSAibG9nKHQpIg0KICAgICAgZml0IDwtIGxtKHkgfiB4KQ0KICAgICAgdGFiIDwtIGRhdGEuZnJhbWUoYWxwaGE9ZXhwKC1maXQkY29lZlsxXS9maXQkY29lZlsyXSksIGdhbT0xL2ZpdCRjb2VmWzJdKSB9DQoNCiAgIHBsb3QoeCwgeSwgeGxhYj14bGFiLCB5bGFiPXlsYWIsIG1haW49IkhhemFyZCBwbG90IHdpdGggdW5jZW5zb3JlZC9jZW5zb3JlZCBkYXRhIikgICANCiAgIGFibGluZShmaXQsIGx3ZD0yKQ0KICAgdGFiJFIyIDwtIHN1bW1hcnkoZml0KSRyLnNxOyByb3duYW1lcyh0YWIpIDwtIE5VTEwNCiAgIHJvdW5kKHRhYiwzKSB9DQpgYGANCg0KDQoqKlRlc3RzKioNCg0KYGBge3J9DQpzaWdjb2RlIDwtIGZ1bmN0aW9uKHAudmFsKSBpZmVsc2UocC52YWwgPCAwLjAwMSwgIioqKiIsIGlmZWxzZShwLnZhbCA8IDAuMDEsICIqKiIsIGlmZWxzZShwLnZhbCA8IDAuMDUsICIqIiwgaWZlbHNlKHAudmFsIDwgMC4xLCAiLiIsICIiKSkpKQ0KDQpxdWlldCA8LSBmdW5jdGlvbihmbikgeyAgICMgc3VwcHJlc3Mgb3V0cHV0IG1lc3NhZ2VzDQogICAgc2luayh0ZW1wZmlsZSgpKQ0KICAgIG9uLmV4aXQoc2luaygpKQ0KICAgIGludmlzaWJsZShmb3JjZShmbikpIH0NCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpMUi50ZXN0IDwtIGZ1bmN0aW9uKHBhcjAsIGRpc3QsIGRhdCwgY2VuPU5BKSB7DQogICAgcmVzIDwtIE1MRShwYXIwLCBkaXN0LCBkYXQsIGNlbiwgc2U9RikNCiAgIGxvZ0wgIDwtIGF0dHIocmVzLCAibWF4LmxvZ0wiKQ0KICAgbG9nTDAgPC0gLW5sTChwYXIwLCBkaXN0LCBkYXQsIGNlbikgDQogICAgIExSVCA8LSAtMioobG9nTDAgLSBsb2dMKQ0KICAgcC52YWwgPC0gMS1wY2hpc3EoTFJULCBkZj1sZW5ndGgocGFyMCkpDQoNCiAgIGNhdCgiTGlrZWxpaG9vZCByYXRpbyB0ZXN0IGZvciBhIHNwZWNpZmljIGRpc3RyaWJ1dGlvbiB3aXRoIGtub3duIHBhcmFtZXRlcnMgXG4iKQ0KICAgY2F0KCIgICBIMDogUyA9IFMwIFxuIikNCiAgIGNhdChwYXN0ZSgiICAgICAgIG9yIGVxdWl2YWxlbnRseSwgdGhlIHVuZGVybHlpbmcgZGlzdHJpYnV0aW9uIGlzIiwgZGlzdCwgDQogICAgICAgICAgICAgIndpdGggc3BlY2lmaWVkIHBhcmFtZXRlciB2YWx1ZXMuIFxuXG4iKSkNCiAgIHJlcyA8LSByZXNbLDE6Ml07IHJlcyRudWxsLnZhbHVlIDwtIHBhcjANCiAgIHByaW50KHJlcywgcm93Lm5hbWVzPUYpOyBjYXQoIlxuIikNCiAgIHByaW50KGRhdGEuZnJhbWUobG9nTD1yb3VuZChsb2dMLDMpLCBsb2dMMD1yb3VuZChsb2dMMCwzKSwgTFJUPXJvdW5kKExSVCwzKSwgDQogICAgICAgICAgICAgICAgICAgIHAudmFsdWU9cm91bmQocC52YWwsNCksIHNpZ25pZj1zaWdjb2RlKHAudmFsKSksIHJvdy5uYW1lcz1GKSB9DQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KSFAudGVzdCA8LSBmdW5jdGlvbihzZjAsIGRhdCwgY2VuPU5BLCBlcHM9MWUtOSkgew0KICAgbiA8LSBsZW5ndGgoZGF0KTsgaWYgKGlzLm5hKGNlblsxXSkpIGNlbiA8LSByZXAoVCwgbik7IGNlbiA8LSBhcy5sb2dpY2FsKGNlbikNCiAgIGRhdFshY2VuXSA8LSBkYXRbIWNlbl0gKyBlcHMNCiAgIGlkeCA8LSBvcmRlcihkYXQpDQogICBkYXQgPC0gZGF0W2lkeF0NCiAgIGRlbCA8LSBjZW4gPC0gY2VuW2lkeF0NCg0KICAgZnQgPC0gMS9uKmMoMSwgY3VtcHJvZCgoKG46MikvKChuLTEpOjEpKV4oMS1kZWxbLW5dKSkpICAgIyBqdW1wIG9mIEthcGxhbi1NZWllciBlc3RpbWF0ZQ0KICAgUzAgPC0gc2YwKGRhdCkNCiAgIHMyIDwtIDEvMTYqc3VtKG4vKG46MSkqKGMoMSwgUzBbLW5dXjQpIC0gUzBeNCkpDQogICBDIDwtIHN1bShkZWwqUzAqZnQpDQogICBaIDwtIChDIC0gMC41KS9zcXJ0KHMyL24pDQogICBwLnZhbCA8LSBjKDIqcG5vcm0oLWFicyhaKSksIDEtcG5vcm0oWiksIHBub3JtKFopKQ0KDQogICBjYXQoIkhvbGxhbmRlciBhbmQgUHJvc2NoYW4ncyB0ZXN0IGZvciBhIHNwZWNpZmljIGRpc3RyaWJ1dGlvbiB3aXRoIGtub3duIHBhcmFtZXRlcnMgXG4iKQ0KICAgY2F0KCIgICBIMDogUyA9IFMwIFxuIikNCiAgIGNhdCgiICAgICAgIHdoZXJlIFMwIGlzIHRoZSB1bmRlcmx5aW5nIHN1cnZpdmFsIGZ1bmN0aW9uIHdpdGggc3BlY2lmaWVkIHBhcmFtZXRlciB2YWx1ZXMgXG5cbiIpDQogICBwcmludChkYXRhLmZyYW1lKEM9cm91bmQoQywzKSwgc2lnbWEyPXJvdW5kKHMyLDMpLCBDLnN0YXI9cm91bmQoWiwzKSksIHJvdy5uYW1lcz1GKTsgY2F0KCJcbiIpDQogICBwcmludChkYXRhLmZyYW1lKEgxPWMoIlMgIT0gUzAiLCAiUyA8IFMwIiwgIlMgPiBTMCIpLCANCiAgICAgIHAudmFsdWU9cm91bmQocC52YWwsNCksIHNpZ25pZj1zYXBwbHkocC52YWwsIHNpZ2NvZGUpKSwgcm93Lm5hbWVzPUYpDQoNCiAgIHF1aWV0KHN1cnZLTTIoZGF0LCBjZW4pKQ0KICAgdHQgPC0gc2VxKDAsIDEuMiptYXgoZGF0W2Nlbl0pLCBsZW49MTAwKSANCiAgIFMwIDwtIHNmMCh0dCkNCiAgIHBvaW50cyh0dCwgUzAsIHR5cGU9ImwiLCBsd2Q9MiwgY29sPSJyZWQiKSB9ICAgIyBvdmVybGF5IGh5cG90aGVzaXplZCBzdXJ2aXZhbCBmdW5jdGlvbiBTMCh0KQ0KDQpgYGANCg0KDQoNCi0tLQ0KDQoNCiMjIyBQcm9ibGVtIDENCg0KDQoqKlRoZSBmb2xsb3dpbmcgZGF0YSBhcmUgb24gcmVtaXNzaW9uIHRpbWVzLCBpbiB3ZWVrcywgZm9yIGEgZ3JvdXAgb2YgMzAgbGV1a2VtaWEgcGF0aWVudHMgaW4gYSBjZXJ0YWluIHR5cGUgb2YgdGhlcmFweS4qKg0KDQogICAgICAgICAgICAgICAgUmVtaXNzaW9uIFRpbWVzIChpbiB3ZWVrcykNCiAgICAgMSAgMSAgMiAgNCAgNCAgNiAgNiAgIDYgIDcgICA4ICAgOSAgOSAxMCAgMTIgIDEzDQogICAgMTQgMTggMTkgMjQgMjYgMjkgMzErIDQyIDQ1KyA1MCsgNTcgNjAgNzErIDg1KyA5MQ0KDQoqKkFzc3VtZSB0aGF0IG9uZS1wYXJhbWV0ZXIgZXhwb25lbnRpYWwgbW9kZWwgaG9sZHMgZm9yIHRoaXMgZGF0YS4qKg0KDQoqKihhKSoqICBGaW5kIHRoZSBNTEUgb2YgdGhlIHJhdGUgcGFyYW1ldGVyIM67Lg0KDQpgYGB7cn0NCmRhdCA8LSBjKDEsIDEsIDIsIDQsIDQsIDYsIDYsIDYsIDcsIDgsIDksIDksIDEwLCAxMiwgMTMsDQogICAgICAgICAxNCwgMTgsIDE5LCAyNCwgMjYsIDI5LCAzMSwgNDIsIDQ1LCA1MCwgNTcsIDYwLCA3MSwgODUsIDkxKQ0KDQpjZW4gPC0gYygxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLA0KICAgICAgICAgMSwgMSwgMSwgMSwgMSwgMSwgMCwgMSwgMCwgMCwgMSwgMSwgMCwgMCwgMSkgICAjIDEgaWYgb2JzZXJ2ZWQsIDAgaWYgY2Vuc29yZWQNCg0KcmVzIDwtIE1MRWV4cChkYXQsIGNlbikNCg0KcHJpbnQocmVzKQ0KYGBgDQoNCipTbywgd2UgZ2V0IGEgbGFtYmRhIG9mIDAuMDMzIGFzIHNob3duIGFib3ZlLioNCg0KDQoqKihiKSoqICBGaW5kIHRoZSBNTEUgb2YgdGhlIG1lZGlhbiByZW1pc3Npb24gdGltZSBhbmQgUygyNikgPSBQKFQgPiAyNiksIHdoZXJlIFQgaXMgdGhlIHJlbWlzc2lvbiB0aW1lIChpbiB3ZWVrcykuDQoNCg0KYGBge3J9DQpsYW1iZGEgPC0gcmVzJE1MRVsxXQ0KDQojIE1lZGlhbiBlc3RpbWF0ZQ0KbWVkaWFuX3RpbWUgPC0gbG9nKDIpIC8gbGFtYmRhDQoNCiMgU3Vydml2YWwgcHJvYmFiaWxpdHkgYXQgMjYgd2Vla3MNClMyNiA8LSAxLXBleHAoMjYsIHJhdGU9bGFtYmRhKTsNCg0KIyBPdXRwdXQgdGhlIHJlc3VsdHMNCmNhdCgiRXN0aW1hdGVkIG1lZGlhbiByZW1pc3Npb24gdGltZToiLCByb3VuZChtZWRpYW5fdGltZSwgMiksICJ3ZWVrc1xuIikNCmNhdCgiRXN0aW1hdGVkIHN1cnZpdmFsIHByb2JhYmlsaXR5IGF0IDI2IHdlZWtzOiIsIHJvdW5kKFMyNiwgNCksICJcbiIpDQpgYGANCg0KKldlIGdvdCBhbiBlc3RpbWF0ZWQgbWVkaWFuIHJlbWlzc2lvbiB0aW1lIG9mIDIxIHdlZWtzIGFzIHByb3ZpZGVkIGJ5IHRoZSBjb2RlLCB3aGlsZSB0aGUgTUxFIG9mIFMoMjYpID0gUChUID4gMjYpIGlzIDAuNDI0IGFzIHNob3duIGFib3ZlLioNCg0KDQoqKihjKSoqIEZpbmQgYSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgzrsuDQoNCmBgYHtyfQ0KciA8LSBzdW0oY2VuKSAgIyBudW1iZXIgb2Ygb2JzZXJ2ZWQgKHVuY2Vuc29yZWQpIGV2ZW50cw0KbXVfaGF0IDwtIDEgLyByZXMkTUxFWzFdICAjIGVzdGltYXRlZCBtZWFuDQoNCmNoaUwgPC0gcWNoaXNxKDAuMDI1LCBkZiA9IDIgKiByKQ0KY2hpVSA8LSBxY2hpc3EoMC45NzUsIGRmID0gMiAqIHIpDQoNCkNJX211IDwtIDIgKiByICogbXVfaGF0IC8gYyhjaGlVLCBjaGlMKSAgIyBDSSBmb3IgzrwNCkNJX2xhbWJkYSA8LSByZXYoMSAvIENJX211KSAgICAgICAgICAgICAgIyBDSSBmb3IgzrsgKGludmVydGVkKQ0KDQpjYXQoIk1hbnVhbCBleGFjdCA5NSUgQ0kgZm9yIGxhbWJkYToiLCByb3VuZChDSV9sYW1iZGEsIDQpLCAiXG4iKQ0KYGBgDQoNCipXZSBnb3QgYSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBvZiAoMC4wMjE0LCAwLjA0NzEpLioNCg0KDQoqKihkKSoqICBGaW5kIGEgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSBtZWFuIHBhcmFtZXRlciDCtS4NCg0KYGBge3J9DQpyIDwtIHN1bShjZW4pICAjIG51bWJlciBvZiB1bmNlbnNvcmVkIGV2ZW50cw0KbXVfaGF0IDwtIDEgLyByZXMkTUxFWzFdICAjIGVzdGltYXRlZCBtZWFuDQoNCmNoaUwgPC0gcWNoaXNxKDAuMDI1LCBkZiA9IDIgKiByKQ0KY2hpVSA8LSBxY2hpc3EoMC45NzUsIGRmID0gMiAqIHIpDQoNCkNJX211IDwtIDIgKiByICogbXVfaGF0IC8gYyhjaGlVLCBjaGlMKSAgIyBDSSBmb3IgzrwNCg0KY2F0KCJFeGFjdCA5NSUgQ0kgZm9yIG11OiIsIHJvdW5kKENJX211LCAyKSwgIlxuIikNCmBgYA0KDQoqVGhlIGV4YWN0IDk1JSBjb25maWRlbmNlIGludGVydmFsIGZvciB0aGUgbWVhbiByZW1pc3Npb24gdGltZSBpcyAoMjEuMjEsIDQ2LjgzKSB3ZWVrcy4qDQoNCg0KLS0tDQoNCg0KIyMjIFByb2JsZW0gMg0KDQoNCioqUmVmZXIgdG8gdGhlIGRhdGEgaW4gUXVlc3Rpb24gMS4qKg0KDQoqKihhKSoqICBDb21wdXRlIGFuZCBwbG90IHRoZSBLYXBsYW4tTWVpZXIgZXN0aW1hdGUgb2YgdGhlIHN1cnZpdmFsIGZ1bmN0aW9uIFModCkuDQoNCmBgYHtyfQ0KZGF0IDwtIGMoMSwgMSwgMiwgNCwgNCwgNiwgNiwgNiwgNywgOCwgOSwgOSwgMTAsIDEyLCAxMywNCiAgICAgICAgIDE0LCAxOCwgMTksIDI0LCAyNiwgMjksIDMxLCA0MiwgNDUsIDUwLCA1NywgNjAsIDcxLCA4NSwgOTEpDQoNCmNlbiA8LSBjKDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsDQogICAgICAgICAxLCAxLCAxLCAxLCAxLCAxLCAwLCAxLCAwLCAwLCAxLCAxLCAwLCAwLCAxKSAgICMgMSBpZiBvYnNlcnZlZCwgMCBpZiBjZW5zb3JlZA0KDQprbV9yZXN1bHQgPC0gc3VydktNMihkYXQsIGNlbikNCg0KcGxvdChrbV9yZXN1bHQkdGltZSwga21fcmVzdWx0JFMsIHR5cGUgPSAicyIsDQogICAgIHhsYWIgPSAiVGltZSAod2Vla3MpIiwgeWxhYiA9ICJTdXJ2aXZhbCBQcm9iYWJpbGl0eSIsDQogICAgIG1haW4gPSAiS2FwbGFuLU1laWVyIEVzdGltYXRlIG9mIFModCkiKQ0KYGBgDQoNCg0KKiooYikqKiBPYnRhaW4gYW4gZXN0aW1hdGUgb2YgdGhlIG1lZGlhbiByZW1pc3Npb24gdGltZS4gQ29tcGFyZSBpdCB3aXRoIHlvdXIgYW5zd2VyIG9idGFpbmVkIGluIFF1ZXN0aW9uIDEoYikuDQoNCmBgYHtyfQ0KbWVkaWFuX0tNIDwtIGttX3Jlc3VsdCR0aW1lW21pbih3aGljaChrbV9yZXN1bHQkUyA8PSAwLjUpKV0NCmNhdCgiS2FwbGFuLU1laWVyIE1lZGlhbiBUaW1lOiIsIG1lZGlhbl9LTSwgIndlZWtzXG4iKQ0KYGBgDQoNCipUaGUgbWVkaWFuIHJlbWlzc2lvbiB0aW1lIGVzdGltYXRlZCBieSB0aGUgS2FwbGFuLU1laWVyIG1ldGhvZCBpcyAxMyB3ZWVrcywgd2hpY2ggaXMgbG93ZXIgdGhhbiB0aGUgMjEtd2VlayBtZWRpYW4gZXN0aW1hdGVkIHVuZGVyIHRoZSBleHBvbmVudGlhbCBtb2RlbC4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgZXhwb25lbnRpYWwgYXNzdW1wdGlvbiBtYXkgb3ZlcmVzdGltYXRlIHN1cnZpdmFsIHRpbWUsIGVzcGVjaWFsbHkgZWFybHkgaW4gdGhlIGZvbGxvdy11cCBwZXJpb2QuKg0KDQoNCioqKGMpKiogQ29tcHV0ZSBhbmQgcGxvdCB0aGUgbm9ucGFyYW1ldHJpYyBlc3RpbWF0ZSBvZiB0aGUgaGF6YXJkIGZ1bmN0aW9uIGgodCkgYWdhaW5zdCB0aW1lLiBEb2VzIGl0IHN1cHBvcnQgdGhlIGV4cG9uZW50aWFsIGFzc3VtcHRpb24/DQoNCmBgYHtyfQ0KUyA8LSBrbV9yZXN1bHQkUw0KdGltZSA8LSBrbV9yZXN1bHQkdGltZQ0KaGF6IDwtIC1kaWZmKFMpIC8gaGVhZChTLCAtMSkgICAjIGhhemFyZCBpbmNyZW1lbnRzDQp0X21pZCA8LSAoaGVhZCh0aW1lLCAtMSkgKyB0YWlsKHRpbWUsIC0xKSkgLyAyICAjIG1pZHBvaW50cyBmb3IgcGxvdHRpbmcNCg0KIyBQbG90IGgodCkNCnBsb3QodF9taWQsIGhheiwgdHlwZSA9ICJiIiwgcGNoID0gMTYsDQogICAgIHhsYWIgPSAiVGltZSAod2Vla3MpIiwgeWxhYiA9ICJFc3RpbWF0ZWQgSGF6YXJkIGgodCkiLA0KICAgICBtYWluID0gIk5vbnBhcmFtZXRyaWMgRXN0aW1hdGUgb2YgdGhlIEhhemFyZCBGdW5jdGlvbiIpDQpgYGANCg0KKlRoZSBub25wYXJhbWV0cmljIGhhemFyZCBlc3RpbWF0ZSBpcyByZWxhdGl2ZWx5IGZsYXQgZWFybHkgb24gYnV0IGluY3JlYXNlcyBub3RpY2VhYmx5IGFmdGVyIDQwIHdlZWtzLCBlc3BlY2lhbGx5IGJleW9uZCA2MCB3ZWVrcy4gVGhpcyBzdWdnZXN0cyB0aGUgaGF6YXJkIGlzIG5vdCBjb25zdGFudCBvdmVyIHRpbWUsIGluZGljYXRpbmcgdGhhdCB0aGUgZXhwb25lbnRpYWwgbW9kZWwgbWF5IG5vdCBiZSBhcHByb3ByaWF0ZSBhY3Jvc3MgdGhlIGZ1bGwgdGltZSByYW5nZS4qDQoNCg0KKiooZCkqKiBQbG90IHRoZSBNTEUgb2YgdGhlIGhhemFyZCByYXRlIG9idGFpbmVkIGluIFF1ZXN0aW9uIDEoYSkgb3ZlciB0aGUgcGxvdCBpbiAoYykuIENvbW1lbnQgb24gdGhlIGFzc3VtcHRpb24gb2YgZXhwb25lbnRpYWxpdHkuDQoNCmBgYHtyfQ0KIyBBZGQgdGhlIGNvbnN0YW50IE1MRSBoYXphcmQgbGluZSAoZnJvbSBQcm9ibGVtIDFhKQ0KcGxvdCh0X21pZCwgaGF6LCB0eXBlID0gImIiLCBwY2ggPSAxNiwNCiAgICAgeGxhYiA9ICJUaW1lICh3ZWVrcykiLCB5bGFiID0gIkVzdGltYXRlZCBIYXphcmQgaCh0KSIsDQogICAgIG1haW4gPSAiTm9ucGFyYW1ldHJpYyBIYXphcmQgRnVuY3Rpb24gd2l0aCBFeHBvbmVudGlhbCBNTEUiKQ0KYWJsaW5lKGggPSAwLjAzMywgY29sID0gInJlZCIsIGx3ZCA9IDIsIGx0eSA9IDIpDQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gYygiTm9ucGFyYW1ldHJpYyIsICJNTEUgKEV4cG9uZW50aWFsKSIpLA0KICAgICAgIGNvbCA9IGMoImJsYWNrIiwgInJlZCIpLCBsdHkgPSBjKDEsIDIpLCBsd2QgPSBjKDEsIDIpLCBwY2ggPSBjKDE2LCBOQSkpDQpgYGANCg0KKlRoZSBNTEUgaGF6YXJkIHJhdGUgdW5kZXIgdGhlIGV4cG9uZW50aWFsIG1vZGVsIGlzIGNvbnN0YW50IGF0IPCdnIY9IDAuMDMzLCBzaG93biBhcyBhIHJlZCBkYXNoZWQgbGluZS4gVGhlIG5vbnBhcmFtZXRyaWMgaGF6YXJkIHJpc2VzIG92ZXIgdGltZSwgaW5kaWNhdGluZyB0aGF0IHRoZSBhc3N1bXB0aW9uIG9mIGNvbnN0YW50IGhhemFyZCBpcyB2aW9sYXRlZC4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBleHBvbmVudGlhbCBtb2RlbCBtYXkgbm90IHByb3ZpZGUgYW4gYWRlcXVhdGUgZml0LCBwYXJ0aWN1bGFybHkgYXQgbGF0ZXIgdGltZXMuKg0KDQoNCi0tLQ0KDQoNCiMjIyBQcm9ibGVtIDMNCg0KDQoqKlJlZmVyIHRvIHRoZSBkYXRhIGluIFF1ZXN0aW9uIDEuIE5vdyBhc3N1bWUgdGhhdCB0d28tcGFyYW1ldGVyIFdlaWJ1bGwgZGlzdHJpYnV0aW9uIGlzIGFwcHJvcHJpYXRlIGZvciB0aGUgZGF0YS4qKg0KDQoqKihhKSoqICBPYnRhaW4gdGhlIE1MRSBvZiB0d28gcGFyYW1ldGVycy4NCg0KYGBge3J9DQpkYXQgPC0gYygxLCAxLCAyLCA0LCA0LCA2LCA2LCA2LCA3LCA4LCA5LCA5LCAxMCwgMTIsIDEzLA0KICAgICAgICAgMTQsIDE4LCAxOSwgMjQsIDI2LCAyOSwgMzEsIDQyLCA0NSwgNTAsIDU3LCA2MCwgNzEsIDg1LCA5MSkNCmNlbiA8LSBjKDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsDQogICAgICAgICAxLCAxLCAxLCAxLCAxLCAxLCAwLCAxLCAwLCAwLCAxLCAxLCAwLCAwLCAxKSAgICMgMSBpZiBvYnNlcnZlZCwgMCBpZiBjZW5zb3JlZA0KDQpNTEUoYygwLjA0LDAuODMxKSwgIndlaWJ1bGwiLCBkYXQsIGNlbik7DQpgYGANCg0KKldlIGFyZSBnaXZlbiAwLjAzNCBmb3IgdGhlIHNjYWxlIHBhcmFtZXRlciwgYW5kIDAuODM1IGZvciB0aGUgc2hhcGUgcGFyYW1ldGVyLioNCg0KDQoqKihiKSoqIE9idGFpbiBhbiBlc3RpbWF0ZSBvZiBTKDI2KSwgYW5kIGNvbXBhcmUgaXQgd2l0aCB5b3VyIGFuc3dlciBvYnRhaW5lZCBpbiBRdWVzdGlvbiAxKGIpLiBDb21tZW50Lg0KDQoNCmBgYHtyfQ0KMS1wd2VpYnVsbCgyNiwgc2hhcGU9MC44MzUsIHNjYWxlPTEvMC4wMzQpDQpgYGANCg0KKldlIGdldCBhIFdlaWJ1bGwgZXN0aW1hdGUgb2YgMC40MDU2ODkgZm9yIFMoMjYpLCB3aGljaCBpcyBzbGlnaHRseSBiZWxvdyB0aGUgZXhwb25lbnRpYWwgYW5kIEtNIHZhbHVlcyAofjAuNDI0KS4gVGhpcyBzdWdnZXN0cyBhIG1vZGVzdGx5IHF1aWNrZXIgZWFybHkgZGVjbGluZSwgYnV0IHRoZSBkaWZmZXJlbmNlIGlzIG1pbmltYWzigJRpbmRpY2F0aW5nIGEgY29tcGFyYWJsZSBmaXQgYXQgMjYgd2Vla3MuKg0KDQoNCioqKGMpKiogTWFrZSBhbiBhcHByb3ByaWF0ZSBwbG90IHRvIHNlZSBpZiB0aGUgZGF0YSBmb2xsb3dzIGEgV2VpYnVsbCBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KcGxvdEgoIndlaWJ1bGwiLCBkYXQsIGNlbikNCmBgYA0KDQoqVGhlIGxvZy1sb2cgaGF6YXJkIHBsb3Qgc2hvd3MgdGhhdCB0aGUgcG9pbnRzIGFsaWduIGNsb3NlbHkgd2l0aCB0aGUgcmVmZXJlbmNlIGxpbmUsIHN1Z2dlc3RpbmcgdGhhdCB0aGUgV2VpYnVsbCBkaXN0cmlidXRpb24gcHJvdmlkZXMgYSByZWFzb25hYmxlIGZpdCBmb3IgdGhlIGRhdGEuKg0KDQoNCi0tLQ0KDQoNCiMjIyBQcm9ibGVtIDQNCg0KDQoqKjcuMiAgIEluIG9yZGVyIHRvIGNvbXB1dGVyaXplIHBhdGllbnRz4oCZIHJlY29yZHMsIGEgZGF0YSBjbGVyayBpcyBoaXJlZCB0byB0cmFuc2NyaWJlIG1lZGljYWwgZGF0YSBmcm9tIHRoZSBwYXRpZW50c+KAmSBjaGFydHMgdG8gY29tcHV0ZXIgY29kaW5nIGZvcm1zLiBUaGUgbnVtYmVyIG9mIGNvcnJlY3QgZW50cmllcyBiZXR3ZWVuIGVycm9ycyBpcyBsaXN0ZWQgaW4gY2hyb25vbG9naWNhbCBvcmRlciBvZiBvY2N1cnJlbmNlIG92ZXIgYSBwZXJpb2Qgb2YgZml2ZSBkYXlzIGFzIGZvbGxvd3M6IDczLCAxMiwgNDAsIDY1LCAxMDAsIDE1LCA3MCwgNDAsIDExMCwgNjQsIDIwMCwgNiwgOTAsIDEwMiwgMjAsIDEwMiwgOTAsIGFuZCAzNC4gQXNzdW1pbmcgdGhhdCB0aGUgY29ycmVjdCBlbnRyaWVzIGJldHdlZW4gZXJyb3JzIGZvbGxvdyB0aGUgdHdvLXBhcmFtZXRlciBleHBvbmVudGlhbCBkaXN0cmlidXRpb24sIG9idGFpbjoqKg0KDQoqKihhKSoqIEFuIGVzdGltYXRlIG9mIEcuDQoNCmBgYHtyfQ0KZGF0IDwtIGMoNzMsIDEyLCA0MCwgNjUsIDEwMCwgMTUsIDcwLCA0MCwgMTEwLCA2NCwgMjAwLCA2LCA5MCwgMTAyLCAyMCwgMTAyLCA5MCwgMzQpDQpyZXMgPC0gTUxFKGMoMSwxKSwgImV4cDJwYXIiLCBkYXQsIHNlPUYpDQoNCkcgPC0gcmVzJE1MRVsyXQ0KY2F0KCJFc3RpbWF0ZSBvZiBHOiIsIEcsICJcbiIpDQpgYGANCg0KKlRoZSBlc3RpbWF0ZSBvZiBHIGlzIDYsIHdoaWNoIGNvcnJlc3BvbmRzIHRvIHRoZSBtaW5pbXVtIHZhbHVlIGluIHRoZSBkYXRhc2V0LioNCg0KDQoqKihiKSoqIFRoZSBNTEUgb2YgbC4NCg0KYGBge3J9DQpsYW1iZGEgPC0gcmVzJE1MRVsxXQ0KY2F0KCJNTEUgb2YgbGFtYmRhOiIsIGxhbWJkYSwgIlxuIikNCmBgYA0KDQoqVGhlIE1MRSBvZiDOuyBpcyAwLjAxNiwgYmFzZWQgb24gdGhlIHR3by1wYXJhbWV0ZXIgZXhwb25lbnRpYWwgZml0LioNCg0KKiooYykqKiBUaGUgTUxFIG9mIG0uDQoNCmBgYHtyfQ0KMS9sYW1iZGEgKyBHDQpgYGANCg0KKlRoZSBNTEUgb2YgzrwgaXMgYXBwcm94aW1hdGVseSA2OC41Kg0KDQoNCioqKGQpKiogIFRoZSBwcm9iYWJpbGl0eSBvZiBhdCBsZWFzdCAxMDAgY29ycmVjdCBlbnRyaWVzIGJldHdlZW4gdHdvIGVycm9ycy4NCg0KYGBge3J9DQpTMTAwIDwtIDEtcGV4cCgxMDAtRywgcmF0ZT1sYW1iZGEpDQoNCnByaW50KFMxMDApDQpgYGANCg0KKlRoZSBwcm9iYWJpbGl0eSBvZiBhdCBsZWFzdCAxMDAgY29ycmVjdCBlbnRyaWVzIGJldHdlZW4gdHdvIGVycm9ycyBpcyBhcHByb3hpbWF0ZWx5IDAuMjIyMi4qDQoNCg0KLS0tDQoNCg0KIyMjIFByb2JsZW0gNQ0KDQoNCioqNy4zICAgSW4gYSBjbGluaWNhbCBzdHVkeSwgMjggcGF0aWVudHMgd2l0aCBjYW5jZXIgb2YgdGhlIGhlYWQgYW5kIG5lY2sgZGlkIG5vdCByZXNwb25kIHRvIGNoZW1vdGhlcmFweS4gVGhlaXIgc3Vydml2YWwgdGltZXMgaW4gd2Vla3MgYXJlIGdpdmVuIGluIHRoZSBmb2xsb3dpbmcuKioNCg0KICAgIDEuNyAgOC4zICAgMTQuMCAgMjIuNyAgIDYuMCsgIDEzLjErDQogICAgNS4xICA5LjYgICAxNS45ICAzMy4wICAgNy40KyAgMTMuNCsNCiAgICA1LjMgIDExLjMgIDE2LjcgICAzLjcrICA4LjArICAxNi4xKw0KICAgIDYuMCAgMTIuMSAgMTcuMCAgIDUuMCsgIDguMysNCiAgICA4LjMgIDEyLjMgIDIxLjAgICA1LjkrICA5LjErDQoNCioqT2J0YWluIHRoZSBNTEUgb2YgdGhlIHBhcmFtZXRlcihzKSBhbmQgbWVhbiBzdXJ2aXZhbCB0aW1lcywgYXNzdW1pbmc6KioNCg0KKiooYSkqKiBBIG9uZS1wYXJhbWV0ZXIgZXhwb25lbnRpYWwgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0NCmRhdCA8LSBjKDEuNywgNS4xLCA1LjMsIDYuMCwgOC4zLCA4LjMsIDkuNiwgMTEuMywgMTIuMSwgMTIuMywgMTQuMCwgMTUuOSwgMTYuNywgMTcuMCwgMjEuMCwgMjIuNywgMzMuMCwgMy43LCA1LjAsIDUuOSwgNi4wLCA3LjQsIDguMCwgOC4zLCA5LjEsIDEzLjEsIDEzLjQsIDE2LjEpDQoNCmNlbiA8LSBjKHJlcCgxLCAxNyksIHJlcCgwLCAxMSkpICAgIyAxIGlmIG9ic2VydmVkLCAwIGlmIGNlbnNvcmVkDQoNCnJlc19leHAgPC0gTUxFZXhwKGRhdCwgY2VuKQ0KcmVzX2V4cA0KYGBgDQoNCipCYXNlZCBvbiB0aGUgcmVzdWx0cyBhYm92ZSwgdW5kZXIgdGhlIG9uZS1wYXJhbWV0ZXIgZXhwb25lbnRpYWwgbW9kZWwsIHRoZSBlc3RpbWF0ZWQgbWVhbiBzdXJ2aXZhbCB0aW1lIGlzIGFwcHJveGltYXRlbHkgMTguNiB3ZWVrcywgd2l0aCBhIHJhdGUgb2YgzrsgPSAwLjA1NC4qDQoNCg0KKiooYikqKiBBIFdlaWJ1bGwgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0NCnJlc193ZWkgPC0gTUxFKGMoMC4wNSwxKSwgIndlaWJ1bGwiLCBkYXQsIGNlbikNCnJlc193ZWkNCg0KbF9oYXQgPC0gcmVzX3dlaSRNTEVbMV0NCmdfaGF0IDwtIHJlc193ZWkkTUxFWzJdDQoNCm11X3dlaWIgPC0gKDEgLyBsX2hhdCkgKiBnYW1tYSgxICsgMSAvIGdfaGF0KQ0KYGBgDQoNCipUaGUgTUxFcyBmb3IgdGhlIFdlaWJ1bGwgbW9kZWwgYXJlIM67ID0gMC4wNTkgYW5kIM6zID0gMi4wNDIsIGJvdGggd2l0aCBhIG1lYW4gc3Vydml2YWwgdGltZSBvZiBhcHByb3hpbWF0ZWx5IDE3LjQ1IHdlZWtzKg0KDQoNCi0tLQ0KDQoNCiMjIyBQcm9ibGVtIDYNCg0KDQoqKjcuNCAgIEluIGEgc3R1ZHkgb2YgZGVlcCB2ZW5vdXMgdGhyb21ib3NpcywgdGhlIGZvbGxvd2luZyBibG9vZCBjbG90IGx5c2lzIHRpbWVzIGluIGhvdXJzIHdlcmUgcmVjb3JkZWQgZnJvbSAyMCBwYXRpZW50czogMiwgMywgNCwgNS41LCA5LCAxMywgMTYuNSwgMTcuNSwgMTIuNSwgNywgNiwgMTcuNSwgMTEuNSwgNiwgMTQsIDI1LCA0OSwgMzcuNSwgNDksIGFuZCAyOC4gQXNzdW1lIHRoYXQgdGhlIGJsb29kIGNsb3QgbHlzaXMgdGltZXMgZm9sbG93IHRoZSBsb2dub3JtYWwgZGlzdHJpYnV0aW9uLioqDQoNCioqKGEpKiogT2J0YWluIE1MRXMgb2YgdGhlIHBhcmFtZXRlcnMgzrwgYW5kIM+DXjIuDQoNCmBgYHtyfQ0KZGF0IDwtIGMoMiwgMywgNCwgNS41LCA5LCAxMywgMTYuNSwgMTcuNSwgMTIuNSwgNywgNiwgMTcuNSwgMTEuNSwgNiwgMTQsIDI1LCA0OSwgMzcuNSwgNDksIDI4KQ0KDQpyZXNfNiA8LSBNTEUoYygxLDEpLCAibG9nbm9ybWFsIiwgZGF0LCBzZT1UKQ0KcmVzXzYNCg0Kc2lnbWEyX2hhdCA8LSByZXNfNiRNTEVbMl1eMg0Kc2lnbWEyX2hhdA0KYGBgDQoNCipCYXNlZCBvbiB0aGUgcmVzdWx0cyBhYm92ZSwgd2UgaGF2ZSDOvCA9IDIuNDY0IGFuZCDPg14yID0gMC43NjAzODQuKg0KDQoNCioqKGIpKiogT2J0YWluIDk1JSBjb25maWRlbmNlIGludGVydmFscyBmb3IgzrwgYW5kIM+DXjIuDQoNCipBcyBzaG93biBvbiB0aGUgcmVzdWx0cyBhYm92ZSwgd2UgaGF2ZSBhIDk1JSBjb25maWRlbmNlIGludGVydmFscyBvZiAoMi4wODEsIDIuODQ2KSBmb3IgzrwsIGFuZCAoMC42MDIsIDEuMTQyKSBmb3Igz4NeMi4qDQoNCg0KLS0tDQoNCg0KIyMjIFByb2JsZW0gNw0KDQoNCioqNy41ICAgQ29uc2lkZXIgdGhlIGZvbGxvd2luZyB0dW1vci1mcmVlIHRpbWVzIGluIGRheXMgb2YgMTAgYW5pbWFsczogMiwgMy41LCA1LCA3LCA5LCAxMCwgMTUsIDIwLCAzMCwgYW5kIDQwLiBBc3N1bWUgdGhhdCB0aGUgdHVtb3ItZnJlZSB0aW1lcyBmb2xsb3cgdGhlIGxvZy1sb2dpc3RpYyBkaXN0cmlidXRpb24uIEVzdGltYXRlIHRoZSBwYXJhbWV0ZXJzIM6xIGFuZCDOsy4qKg0KDQpgYGB7cn0NCmRhdCA8LSBjKDIsIDMuNSwgNSwgNywgOSwgMTAsIDE1LCAyMCwgMzAsIDQwKQ0KY2VuIDwtIHJlcCgxLCBsZW5ndGgoZGF0KSkNCg0KcmVzXzcgPC0gTUxFKGMoMSwxKSwgImxvZ2xvZ2lzdGljIiwgZGF0LCBjZW4sIHNlPVQpDQpyZXNfNw0KYGBgDQoNCipCYXNlZCBvbiB0aGUgcmVzdWx0cywgYnkgdXNpbmcgdGhlIGxvZy1sb2dpc3RpYyBtb2RlbCwgdGhlIGVzdGltYXRlZCBwYXJhbWV0ZXJzIGFyZTogdGhlIHNjYWxlIG9mIM6xID0gMC4wMTQgKHNvIGFwcHJveGltYXRlbHkgNzEuNDMgZGF5cyB3aGVuIGludmVydGVkKSwgYW5kIHRoZSBzaGFwZSBvZiDOsyA9IDEuODY4LiBXaXRoIHRoaXMsIHRoZSB3aWRlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIM6xIHN1Z2dlc3RzIHNvbWUgdW5jZXJ0YWludHkuIFRoZSBzaGFwZSBlc3RpbWF0ZSBpbmRpY2F0ZXMgYSBtb2RlcmF0ZWx5IGluY3JlYXNpbmcgaGF6YXJkIG92ZXIgdGltZS4qDQoNCg0KLS0tDQoNCg0KIyMjIFByb2JsZW0gOA0KDQoqKjcuOSAgIFR3ZW50eS1maXZlIHJhdHMgd2VyZSBpbmplY3RlZCB3aXRoIGEgZ2l2ZSB0dW1vciBpbm9jdWx1bS4gVGhlaXIgdGltZXMsIGluIGRheXMsIHRvIHRoZSBkZXZlbG9wbWVudCBvZiBhIHR1bW9yIG9mIGEgY2VydGFpbiBzaXplIGFyZSBnaXZlbiBpbiB0aGUgZm9sbG93aW5nLioqDQoNCiAgICAzMCA1MyA3NyAgOTEgMTE4DQogICAgMzggNTQgNzggIDk1IDEyMA0KICAgIDQ1IDU4IDgxIDEwMSAxMjUNCiAgICA0NiA2NiA4NCAxMDggMTM0DQogICAgNTAgNjkgODUgMTE1IDEzNQ0KDQoqKldoaWNoIG9mIHRoZSBkaXN0cmlidXRpb25zIGRpc2N1c3NlZCBpbiB0aGlzIGNoYXB0ZXIgcHJvdmlkZSBhIHJlYXNvbmFibGUgZml0IHRvIHRoZSBkYXRhPyBFc3RpbWF0ZSBncmFwaGljYWxseSB0aGUgcGFyYW1ldGVycyBvZiB0aGUgZGlzdHJpYnV0aW9uIGNob3Nlbi4qKg0KDQpgYGB7cn0NCmRhdCA8LSBjKDMwLCAzOCwgNDUsIDQ2LCA1MCwgNTMsIDU0LCA1OCwgNjYsIDY5LCA3NywgNzgsIDgxLCA4NCwgODUsIDkxLCA5NSwgMTAxLCAxMDgsIDExNSwgMTE4LCAxMjAsIDEyNSwgMTM0LCAxMzUpDQoNCnBsb3RQKCJleHBvbmVudGlhbCIsIGRhdCkNCnBsb3RQKCJ3ZWlidWxsIiwgZGF0KQ0KcGxvdFAoImxvZ25vcm1hbCIsIGRhdCkNCnBsb3RQKCJsb2dsb2dpc3RpYyIsIGRhdCkNCmBgYA0KDQoqQWZ0ZXIgY29tcGFyaW5nIGFsbCBmb3VyIHByb2JhYmlsaXR5IHBsb3RzLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIFdlaWJ1bGwgZGlzdHJpYnV0aW9uIGZpdHMgdGhlIGJlc3QuIFRoZSBwb2ludHMgYWxpZ24gY2xvc2VseSB0byB0aGUgc3RyYWlnaHQgbGluZSB3aXRoIHRoZSBoaWdoZXN0IFItc3F1YXJlIHZhbHVlIG9mIDAuOTczLCBhbmQgd2l0aCB0aGUgZ2l2ZW4gcGFyYW1ldGVycyBvZiDOuyDiiYggMC4wMTEgYW5kIM6zIOKJiCAzLjAzNS4qDQoNCg0KLS0tDQoNCg0KIyMjIFByb2JsZW0gOQ0KDQoNCioqNy4xMCBDb25zaWRlciB0aGUgZGF0YSBpbiBFeGVyY2lzZSA3LjMuKioNCg0KKiooYSkqKiBNYWtlIGEgaGF6YXJkIHBsb3QgZm9yIGVhY2ggb2YgdGhlIGZvbGxvd2luZyBkaXN0cmlidXRpb25zOiBleHBvbmVudGlhbCwgV2VpYnVsbCwgbG9nbm9ybWFsLCBhbmQgbG9nLWxvZ2lzdGljLg0KDQpgYGB7cn0NCmRhdCA8LSBjKDEuNywgNS4xLCA1LjMsIDYuMCwgOC4zLCA4LjMsIDkuNiwgMTEuMywgMTIuMSwgMTIuMywgMTQuMCwgMTUuOSwgMTYuNywgMTcuMCwgMjEuMCwgMjIuNywgMzMuMCwgMy43LCA1LjAsIDUuOSwgNi4wLCA3LjQsIDguMCwgOC4zLCA5LjEsIDEzLjEsIDEzLjQsIDE2LjEpDQpjZW4gPC0gYyhyZXAoMSwgMTcpLCByZXAoMCwgMTEpKQ0KDQpwbG90SCgiZXhwb25lbnRpYWwiLCBkYXQsIGNlbikNCnBsb3RIKCJ3ZWlidWxsIiwgZGF0LCBjZW4pDQpwbG90SCgibG9nbm9ybWFsIiwgZGF0LCBjZW4pDQpwbG90SCgibG9nbG9naXN0aWMiLCBkYXQsIGNlbikNCmBgYA0KDQoNCioqKGIpKiogV2hpY2ggZGlzdHJpYnV0aW9uIHByb3ZpZGVzIGEgcmVhc29uYWJsZSBmaXQgdG8gdGhlIGRhdGE/IEVzdGltYXRlIGdyYXBoaWNhbGx5IHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBkaXN0cmlidXRpb24gY2hvc2VuLg0KDQoqQmFzZWQgb24gdGhlIHBsb3RzIGFib3ZlLCB0aGUgZXhwb25lbnRpYWwgaGF6YXJkIGlzIGNvbnN0YW50IGJ1dCBkb2VzIG5vdCBtYXRjaCB0aGUgdHJlbmQgaW4gdGhlIGRhdGEsIGFzIHRoZSBsb2ctbm9ybWFsIGFuZCBsb2ctbG9naXN0aWMgYXBwZWFyIHRvIGJlIG5vbi1tb25vdG9uaWMuIFRoZSBXZWlidWxsIHBsb3QgaXMgdGhlIGJlc3QgZml0IHdpdGggdGhlIFItc3F1YXJlIG9mIDAuOTYyIHdpdGggdGhlIHBhcmFtZXRlcnMgb2Ygzrsg4omIIDAuMDYgYW5kIM6zIOKJiCAxLjc2MS4qDQoNCg0KLS0tDQoNCg0KIyMjIFByb2JsZW0gMTANCg0KDQoqKlJlZG8gUHJvYmxlbSA3LjEwIGJ5IHBsb3R0aW5nIHRoZSBDb3gtU25lbGwgcmVzaWR1YWxzLioqDQoNCmBgYHtyfQ0KZGF0IDwtIGMoMS43LCA1LjEsIDUuMywgNi4wLCA4LjMsIDguMywgOS42LCAxMS4zLCAxMi4xLCAxMi4zLCAxNC4wLCAxNS45LCAxNi43LCAxNy4wLCAyMS4wLCAyMi43LCAzMy4wLCAzLjcsIDUuMCwgNS45LCA2LjAsIDcuNCwgOC4wLCA4LjMsIDkuMSwgMTMuMSwgMTMuNCwgMTYuMSkNCmNlbiA8LSBjKHJlcCgxLCAxNyksIHJlcCgwLCAxMSkpDQoNCnJlc19leHAgPC0gTUxFKGMoMSksICJleHBvbmVudGlhbCIsIGRhdCwgY2VuKQ0Kcl9leHAgPC0gYXR0cihyZXNfZXhwLCAicmVzaWR1YWwiKQ0KcGxvdFIocl9leHAsIGNlbikNCg0KcmVzX3dlaSA8LSBNTEUoYygwLjA1LCAxKSwgIndlaWJ1bGwiLCBkYXQsIGNlbikNCnJfd2VpIDwtIGF0dHIocmVzX3dlaSwgInJlc2lkdWFsIikNCnBsb3RSKHJfd2VpLCBjZW4pDQoNCnJlc19ub3JtIDwtIE1MRShjKDEsMSksICJsb2dub3JtYWwiLCBkYXQsIGNlbikNCnJfbG5vcm0gPC0gYXR0cihyZXNfbm9ybSwgInJlc2lkdWFsIikNCnBsb3RSKHJfbG5vcm0sIGNlbikNCg0KcmVzX2xvZyA8LSBNTEUoYygxLDEpLCAibG9nbG9naXN0aWMiLCBkYXQsIGNlbiwgc2U9RikNCnJfbGxvZyA8LSBhdHRyKHJlc19sb2csICJyZXNpZHVhbCIpDQpwbG90UihyX2xsb2csIGNlbikNCmBgYA0KDQoqRWFjaCBDb3gtU25lbGwgcmVzaWR1YWwgcGxvdCBzaG93cyBub3RpY2VhYmxlIGRldmlhdGlvbiBmcm9tIHRoZSA0NS1kZWdyZWUgcmVmZXJlbmNlIGxpbmUsIHN1Z2dlc3Rpbmcgbm9uZSBvZiB0aGUgZGlzdHJpYnV0aW9ucyBmaXQgdGhlIGRhdGEgcGVyZmVjdGx5LCBidXQgdGhlIGxvZ25vcm1hbCBkaXN0cmlidXRpb24gYXBwZWFycyB0byBjb21lIGNsb3Nlc3QuKg0KDQoNCi0tLQ0KDQoNCiMjIyBQcm9ibGVtIDExDQoNCg0KKio4LjUgQ29uc2lkZXIgdGhlIHN1cnZpdmFsIHRpbWUgb2YgMjggY2FuY2VyIHBhdGllbnRzIGluIEV4ZXJjaXNlIDcuMyoqDQoNCioqKGEpKiogT2J0YWluIHRoZSBsb2ctbGlrZWxpaG9vZHMgZm9yIHRoZSBleHBvbmVudGlhbCwgV2VpYnVsbCwgbG9nLW5vcm1hbCwgYW5kIHRoZSBleHRlbmRlZCBnZW5lcmFsaXplZCBnYW1tYSBkaXN0cmlidXRpb25zLiBQZXJmb3JtIHRoZSBsaWtlbGlob29kIHJhdGlvIHRlc3QgYW5kIHNlbGVjdCB0aGUgYmVzdCBkaXN0cmlidXRpb24gYW1vbmcgdGhlc2UgZm91ciBkaXN0cmlidXRpb25zLg0KDQpgYGB7cn0NCmRhdCA8LSBjKDEuNywgNS4xLCA1LjMsIDYuMCwgOC4zLCA4LjMsIDkuNiwgMTEuMywgMTIuMSwgMTIuMywgMTQuMCwgMTUuOSwgMTYuNywgMTcuMCwgMjEuMCwgMjIuNywgMzMuMCwgMy43LCA1LjAsIDUuOSwgNi4wLCA3LjQsIDguMCwgOC4zLCA5LjEsIDEzLjEsIDEzLjQsIDE2LjEpDQpjZW4gPC0gYyhyZXAoMSwgMTcpLCByZXAoMCwgMTEpKQ0KDQpzaWdjb2RlIDwtIGZ1bmN0aW9uKHAudmFsKSBpZmVsc2UocC52YWwgPCAwLjAwMSwgIioqKiIsIGlmZWxzZShwLnZhbCA8IDAuMDEsICIqKiIsIGlmZWxzZShwLnZhbCA8IDAuMDUsICIqIiwgaWZlbHNlKHAudmFsIDwgMC4xLCAiLiIsICIiKSkpKQ0KDQpyZXMgPC0gbGlzdCgpICAgIyMjIEV4YW1wbGUgOC4yICYgOC40IGluIHRoZSB0ZXh0Ym9vayA0RQ0KcmVzJGV4cG9uZW50aWFsIDwtIE1MRSggIDEgICAgICwgImV4cG9uZW50aWFsIiwgZGF0LCBjZW4sIHNlPUYpDQpyZXMkd2VpYnVsbCAgICAgPC0gTUxFKGMoMSwxKSAgLCAid2VpYnVsbCIgICAgLCBkYXQsIGNlbiwgc2U9RikNCnJlcyRsb2dub3JtYWwgICA8LSBNTEUoYygxLDEpICAsICJsb2dub3JtYWwiICAsIGRhdCwgY2VuLCBzZT1GKQ0KcmVzJGdlbmdhbW1hICAgIDwtIE1MRShjKDEsMSwxKSwgImdlbmdhbW1hIiAgICwgZGF0LCBjZW4sIHNlPUYpDQoNCnAgPC0gdW5saXN0KGxhcHBseShyZXMsIGZ1bmN0aW9uKHRhYikgbGVuZ3RoKHRhYiRwYXIpKSkNCmxvZ0wgPC0gdW5saXN0KGxhcHBseShyZXMsIGZ1bmN0aW9uKHRhYikgYXR0cih0YWIsICJtYXgubG9nTCIpKSkNCnRhYiA8LSBkYXRhLmZyYW1lKGRpc3Q9bmFtZXMobG9nTCksIHA9cCwgbG9nTD1sb2dMKTsgcm93bmFtZXModGFiKSA8LSBOVUxMDQp0YWINCg0KbGwgPC0gcmJpbmQoY2JpbmQodGFiWzEsXSwgdGFiWzIsXSksDQogICAgICAgICAgICBjYmluZCh0YWJbMSxdLCB0YWJbNCxdKSwNCiAgICAgICAgICAgIGNiaW5kKHRhYlsyLF0sIHRhYls0LF0pLA0KICAgICAgICAgICAgY2JpbmQodGFiWzMsXSwgdGFiWzQsXSkpDQpjb2xuYW1lcyhsbClbMTozXSA8LSBjKCJkaXN0MCIsICJwMCIsICJsb2dMMCIpDQpMUlQgPC0gLTIqKGxsJGxvZ0wwIC0gbGwkbG9nTCkNCnAudmFsIDwtIDEtcGNoaXNxKExSVCwgZGY9KGxsJHAtbGwkcDApKQ0KR09GIDwtIGxsWyxjKDEsNCldOyByb3duYW1lcyhHT0YpIDwtIE5VTEwNCkdPRiA8LSBjYmluZChHT0YsIExSVD1yb3VuZChMUlQsMyksIHAudmFsdWU9cm91bmQocC52YWwsNCksIHNpZ25pZj1zYXBwbHkocC52YWwsIHNpZ2NvZGUpKQ0KR09GDQpgYGANCg0KKlRoZSBsaWtlbGlob29kIHJhdGlvIHRlc3Qgc3VnZ2VzdHMgdGhhdCB0aGUgZ2VuZXJhbGl6ZWQgZ2FtbWEgZGlzdHJpYnV0aW9uIGZpdHMgdGhlIGRhdGEgYmVzdCwgYXMgaXQgc2lnbmlmaWNhbnRseSBpbXByb3ZlcyB0aGUgZml0IG92ZXIgdGhlIGV4cG9uZW50aWFsIGFuZCBsb2ctbm9ybWFsIG1vZGVscy4qDQoNCg0KKiooYikqKiBVc2UgdGhlIEJJQyBhbmQgQUlDIHByb2NlZHVyZXMgdG8gc2VsZWN0IHRoZSBiZXN0IGRpc3RyaWJ1dGlvbiBhbW9uZyB0aGUgZm91ciBkaXN0cmlidXRpb25zIGluIChhKSBwbHVzIHRoZSBsb2ctbG9naXN0aWMgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0NCm4gPC0gbGVuZ3RoKGRhdCkNCnRhYiRBSUMgPC0gbG9nTC0yKnANCnRhYiRCSUMgPC0gbG9nTC1wLzIqbG9nKG4pDQp0YWINCmBgYA0KDQoqQmFzZWQgb24gYm90aCBBSUMgYW5kIEJJQyB2YWx1ZXMsIHRoZSBXZWlidWxsIGRpc3RyaWJ1dGlvbiBwcm92aWRlcyB0aGUgYmVzdCBmaXQgYW1vbmcgdGhlIGZvdXIgY29uc2lkZXJlZCBtb2RlbHMuKg0KDQoNCioqKGMpKiogQ29tcGFyZSB0aGUgcmVzdWx0cyBvYnRhaW5lZCBpbiAoYSkgYW5kIChiKSBlYXJsaWVyIGFuZCB0aG9zZSBvYnRhaW5lZCBpbiBFeGVyY2lzZSA3LjEwLg0KDQoqVGhlIFdlaWJ1bGwgZGlzdHJpYnV0aW9uLCB3aGljaCB3YXMgZmF2b3JlZCBpbiBFeGVyY2lzZXMgNy4xMCBhbmQgOC41KGIpLCBjb25zaXN0ZW50bHkgc2hvd3MgdGhlIGJlc3QgZml0IGFjcm9zcyBib3RoIHRoZSBDb3gtU25lbGwgcmVzaWR1YWwgYW5hbHlzaXMgYW5kIG1vZGVsIHNlbGVjdGlvbiBjcml0ZXJpYSwgcmVpbmZvcmNpbmcgaXRzIHN1aXRhYmlsaXR5IGZvciBtb2RlbGluZyB0aGUgc3Vydml2YWwgZGF0YS4qDQoNCg0KLS0tDQoNCg0KIyMjIFByb2JsZW0gMTINCg0KDQoqKkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgc3Vydml2YWwgdGltZSBpbiB3ZWVrcyBvZiAxMCBtaWNlIHdpdGggaW5qZWN0aW9uIG9mIHR1bW9yIGNlbGxzOiA1LCAxNiwgMTgrLCAyMCwgMjIrLCAyNCssIDI1LCAzMCssIDM1LCA0MCsuIERvIHRoZSBkYXRhIGZvbGxvdyB0aGUgZXhwb25lbnRpYWwgZGlzdHJpYnV0aW9uIHdpdGggbCA9IDAuMDI/KioNCg0KKiooYSkqKiBVc2UgdGhlIGxpa2VsaWhvb2QgcmF0aW8gdGVzdC4NCg0KYGBge3J9DQpkYXQgPC0gYyg1LCAxNiwgMTgsIDIwLCAyMiwgMjQsIDI1LCAzMCwgMzUsIDQwKQ0KY2VuIDwtIGMoMSwgIDEsICAwLCAgMSwgIDAsICAwLCAgMSwgIDAsICAxLCAgMCkNCg0KbGFtYmRhMCA8LSAwLjAyDQpMUi50ZXN0KGxhbWJkYTAsICJleHBvbmVudGlhbCIsIGRhdCwgY2VuKQ0KYGBgDQoNCipCYXNlZCBvbiB0aGUgb3V0cHV0LCB3ZSBnZXQgYSBwLXZhbHVlIG9mIDAuODkxMSwgd2hpY2ggaXMgcXVpdGUgaGlnaCBTbywgd2UgZG8gbm90IHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIGV4cG9uZW50aWFsIGRpc3RyaWJ1dGlvbiB3aXRoIM67ID0gMC4wMiBwcm92aWRlcyBhIHJlYXNvbmFibGUgZml0IGZvciB0aGUgZGF0YS4qDQoNCg0KKiooYikqKiBVc2UgdGhlIEhvbGxhbmRlciBhbmQgUHJvc2NoYW7igJlzIHRlc3Qgc3RhdGlzdGljLg0KDQpgYGB7cn0NCnNmMCA8LSBmdW5jdGlvbih0dCkgMS1wZXhwKHR0LCByYXRlPWxhbWJkYTApDQpIUC50ZXN0KHNmMCwgZGF0LCBjZW4pDQpgYGANCg0KKldlIGNhbiBzZWUgZnJvbSB0aGUgb3V0cHV0IHRoYXQgdGhlIHAtdmFsdWUgaXMgbGFyZ2UsIHNvIHdlIGZhaWwgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMsIHdoaWNoIG1lYW5zIHRoYXQgdGhlcmUgaXMgbm8gc2lnbmlmaWNhbnQgZGV2aWF0aW9uIGZyb20gdGhlIGV4cG9uZW50aWFsIGRpc3RyaWJ1dGlvbi4qDQoNCg0KKiooYykqKiBQbG90IHRoZSBLYXBsYW7igJNNZWllciBlc3RpbWF0b3Igb2YgUyh0KSBhbmQgdGhlIGh5cG90aGVzaXplZCBkaXN0cmlidXRpb24uDQoNCipTaW5jZSB0aGUgS2FwbGFuLU1laWVyIGVzdGltYXRlIGlzIHByb3ZpZGVkIGluIHBhcnQgYiwgd2UgY2FuIHNlZSB0aGF0IHRoZSByZWQgbGluZSBjbG9zZWx5IGZvbGxvd3MgdGhlIHN0ZXBzIG9mIHRoZSBLTSBjdXJ2ZSB3aXRoaW4gY29uZmlkZW5jZSBib3VuZHMsIHN1cHBvcnRpbmcgYSBnb29kIGZpdC4qDQoNCg0KLS0tDQoNCg0KIyMjIFByb2JsZW0gMTMNCg0KDQoqKlN1cHBvc2UgdGhhdCBUIGhhcyBhbiBleHBvbmVudGlhbCBkaXN0cmlidXRpb24gd2l0aCBwYXJhbWV0ZXIgzrsuIEhvd2V2ZXIsIHRoZSBoYXphcmQgZnVuY3Rpb24gzrsgdmFyaWVzIGFjcm9zcyB0aGUgaW5kaXZpZHVhbHMgKGkuZS4sIHBvcHVsYXRpb24gaGV0ZXJvZ2VuZWl0eSkuIFNwZWNpZmljYWxseSwgc3VwcG9zZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgVCBnaXZlbiDOuyBoYXMgdGhlIHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24gZXhwcmVzc2VkIGFzKioNCg0KICAgIM+AKM67KSA9IM67Xihr4oiSMSkgZV4o4oiSzrsvzrEpIC8gzrFeKGspIM6TKGspICwgICAgzrsgPiAwLg0KDQoqKihhKSoqIEZpbmQgdGhlIHVuY29uZGl0aW9uYWwgcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiBmKHQpLg0KDQohW10oUGFydCBBLmpwZykNCg0KKiooYikqKiBGaW5kIHRoZSB1bmNvbmRpdGlvbmFsIHN1cnZpdmFsIGZ1bmN0aW9uIFModCkuDQoNCiFbXShQYXJ0IEIuanBnKQ0KDQoNCioqKGMpKiogRmluZCB0aGUgdW5jb25kaXRpb25hbCBoYXphcmQgZnVuY3Rpb24gaCh0KS4gRG9lcyB0aGUgdW5jb25kaXRpb25hbCBkaXN0cmlidXRpb24gb2YgVCBoYXZlIHRoZSBpbmNyZWFzaW5nIGZhaWx1cmUgcmF0ZSAoSUZSKSBwcm9wZXJ0eT8gb3IgdGhlIGRlY3JlYXNpbmcgZmFpbHVyZSByYXRlIChERlIpIHByb3BlcnR5PyBKdXN0aWZ5Lg0KDQohW10oUGFydCBDLmpwZykNCg0KDQouLi4NCg0K