The Effect of 401(k) Eligibility on Net Financial Assets

Data

  • hdm パッケージに含まれており、ダウンロード可能である
# install.packages("renv")
# renv::restore()
# remotes::install_github("mlr-org/mlr3extralearners", force = TRUE) # needed to run boosting

pacman::p_load(
  xtable,
  hdm,
  sandwich,
  ggplot2,
  randomForest,
  data.table,
  glmnet,
  rpart,
  gbm,
  DoubleML, 
  mlr3learners, 
  mlr3, 
  data.table, 
  randomForest, 
  ranger,
  mlr3extralearners,
  mboost
)
data(pension)
data <- pension
dim(data)
[1] 9915   44
help(pension)
hist_e401 <- ggplot(data, aes(x = e401, fill = factor(e401))) +
  geom_bar()
hist_e401

dens_net_tfa <- ggplot(data, aes(x = net_tfa, color = factor(e401), fill = factor(e401))) +
  geom_density() +
  xlim(c(-20000, 150000)) +
  facet_wrap(. ~ e401)

dens_net_tfa

e1 <- data[data$e401 == 1, ]
e0 <- data[data$e401 == 0, ]
round(mean(e1$net_tfa) - mean(e0$net_tfa), 0)
[1] 19559
p1 <- data[data$p401 == 1, ]
p0 <- data[data$p401 == 0, ]
round(mean(p1$net_tfa) - mean(p0$net_tfa), 0)
[1] 27372
# outcome variable
y <- data[, "net_tfa"]
# treatment variable
D <- data[, "e401"]
D2 <- data[, "p401"]
D3 <- data[, "a401"]

columns_to_drop <- c(
  "e401", "p401", "a401", "tw", "tfa", "net_tfa", "tfa_he",
  "hval", "hmort", "hequity",
  "nifa", "net_nifa", "net_n401", "ira",
  "dum91", "icat", "ecat", "zhat",
  "i1", "i2", "i3", "i4", "i5", "i6", "i7",
  "a1", "a2", "a3", "a4", "a5"
)

# covariates
X <- data[, !(names(data) %in% columns_to_drop)]
# Constructing the controls
x_formula <- paste("~ 0 + poly(age, 6, raw=TRUE) + poly(inc, 8, raw=TRUE) + poly(educ, 4, raw=TRUE) ",
                   "+ poly(fsize, 2, raw=TRUE) + male + marr + twoearn + db + pira + hown")
X <- as.data.table(model.frame(x_formula, X))
head(X)

Partiall Linear Model (PLM)

dml2_for_plm <- function(x, d, y, dreg, yreg, nfold = 3, method = "regression") {
  nobs <- nrow(x) # number of observations
  foldid <- rep.int(1:nfold, times = ceiling(nobs / nfold))[sample.int(nobs)] # define folds indices
  I <- split(1:nobs, foldid) # split observation indices into folds
  ytil <- dtil <- rep(NA, nobs)
  cat("fold: ")
  for (b in seq_along(I)) {
    if (method == "regression") {
      dfit <- dreg(x[-I[[b]], ], d[-I[[b]]]) # take a fold out
      yfit <- yreg(x[-I[[b]], ], y[-I[[b]]]) # take a foldt out
      dhat <- predict(dfit, x[I[[b]], ], type = "response") # predict the left-out fold
      yhat <- predict(yfit, x[I[[b]], ], type = "response") # predict the left-out fold
      dtil[I[[b]]] <- (d[I[[b]]] - dhat) # record residual for the left-out fold
      ytil[I[[b]]] <- (y[I[[b]]] - yhat) # record residial for the left-out fold
    } else if (method == "randomforest") {
      dfit <- dreg(x[-I[[b]], ], as.factor(d)[-I[[b]]]) # take a fold out
      yfit <- yreg(x[-I[[b]], ], y[-I[[b]]]) # take a fold out
      dhat <- predict(dfit, x[I[[b]], ], type = "prob")[, 2] # predict the left-out fold
      yhat <- predict(yfit, x[I[[b]], ], type = "response") # predict the left-out fold
      dtil[I[[b]]] <- (d[I[[b]]] - dhat) # record residual for the left-out fold
      ytil[I[[b]]] <- (y[I[[b]]] - yhat) # record residial for the left-out fold
    } else if (method == "decisiontrees") {
      dfit <- dreg(x[-I[[b]], ], as.factor(d)[-I[[b]]]) # take a fold out
      yfit <- yreg(x[-I[[b]], ], y[-I[[b]]]) # take a fold out
      dhat <- predict(dfit, x[I[[b]], ])[, 2] # predict the left-out fold
      yhat <- predict(yfit, x[I[[b]], ]) # predict the left-out fold
      dtil[I[[b]]] <- (d[I[[b]]] - dhat) # record residual for the left-out fold
      ytil[I[[b]]] <- (y[I[[b]]] - yhat) # record residial for the left-out fold
    } else if (method == "boostedtrees") {
      dfit <- dreg(x[-I[[b]], ], d[-I[[b]]]) # take a fold out
      yfit <- yreg(x[-I[[b]], ], y[-I[[b]]]) # take a fold out
      dhat <- predict(dfit, x[I[[b]], ], type = "response") # predict the left-out fold
      yhat <- predict(yfit, x[I[[b]], ], type = "response") # predict the left-out fold
      dtil[I[[b]]] <- (d[I[[b]]] - dhat) # record residual for the left-out fold
      ytil[I[[b]]] <- (y[I[[b]]] - yhat) # record residial for the left-out fold
    }
    cat(b, " ")
  }
  rfit <- lm(ytil ~ dtil) # estimate the main parameter by regressing one residual on the other
  coef_est <- coef(rfit)[2] # extract coefficient
  se <- sqrt(vcovHC(rfit)[2, 2]) # record robust standard error
  cat(sprintf("\ncoef (se) = %g (%g)\n", coef_est, se)) # printing output
  return(list(coef_est = coef_est, se = se, dtil = dtil, ytil = ytil)) # save output and residuals
}
summaryPLR <- function(point, stderr, resD, resy, name) {
  data <- data.frame(
    estimate = point, # point estimate
    stderr = stderr, # standard error
    lower = point - 1.96 * stderr, # lower end of 95% confidence interval
    upper = point + 1.96 * stderr, # upper end of 95% confidence interval
    `rmse y` = sqrt(mean(resy^2)), # RMSE of model that predicts outcome y
    `rmse D` = sqrt(mean(resD^2)), # RMSE of model that predicts treatment D
    `accuracy D` = mean(abs(resD) < 0.5) # binary classification accuracy of model for D
  )
  rownames(data) <- name
  return(data)
}

Double Lasso

  • 処置の推定において、関数型を線形確率モデルにする場合
# DML with LassoCV
set.seed(123)
cat(sprintf("\nDML with Lasso CV \n"))

DML with Lasso CV 
dreg_lasso_cv <- function(x, d) {
  cv.glmnet(x, d, family = "gaussian", alpha = 1, nfolds = 5)
}
yreg_lasso_cv <- function(x, y) {
  cv.glmnet(x, y, family = "gaussian", alpha = 1, nfolds = 5)
}

dml2_results <- dml2_for_plm(as.matrix(X), D, y, dreg_lasso_cv, yreg_lasso_cv, nfold = 5)
fold: 1  2  3  4  5  
coef (se) = 8954.5 (1483.1)
sum_lasso_cv <- summaryPLR(dml2_results$coef_est, dml2_results$se, dml2_results$dtil,
                           dml2_results$ytil, name = "LassoCV")
tableplr <- data.frame()
tableplr <- rbind(sum_lasso_cv)
tableplr
# Because residuals are output, reconstruct fitted values for use in ensemble
dhat_lasso <- D - dml2_results$dtil
yhat_lasso <- y - dml2_results$ytil
  • 処置の推定において、Logistic 回帰を用いる場合
# DML with Lasso/Logistic
set.seed(123)
cat(sprintf("\nDML with Lasso/Logistic \n"))

DML with Lasso/Logistic 
dreg_logistic_cv <- function(x, d) {
  cv.glmnet(x, d, family = "binomial", alpha = 0, nfolds = 5)
}
yreg_lasso_cv <- function(x, y) {
  cv.glmnet(x, y, family = "gaussian", alpha = 1, nfolds = 5)
}

dml2_results <- dml2_for_plm(as.matrix(X), D, y, dreg_logistic_cv, yreg_lasso_cv, nfold = 5)
fold: 1  2  3  4  5  
coef (se) = 9418.17 (1476.17)
sum_lasso_logistic_cv <- summaryPLR(dml2_results$coef_est, dml2_results$se, dml2_results$dtil,
                                    dml2_results$ytil, name = "LassoCV/LogisticCV")
tableplr <- rbind(tableplr, sum_lasso_logistic_cv)
tableplr
# Because residuals are output, reconstruct fitted values for use in ensemble
dhat_lasso_logistic <- D - dml2_results$dtil
yhat_lasso_logistic <- y - dml2_results$ytil

Random Forest

# DML with Random Forest
set.seed(123)
cat(sprintf("\nDML with Random Forest \n"))

DML with Random Forest 
dreg_rf <- function(x, d) {
  randomForest(x, d, ntree = 1000, nodesize = 10)
} # ML method=Forest
yreg_rf <- function(x, y) {
  randomForest(x, y, ntree = 1000, nodesize = 10)
} # ML method=Forest

dml2_results <- dml2_for_plm(as.matrix(X), D, y, dreg_rf, yreg_rf, nfold = 5, method = "randomforest")
fold: 1  2  3  4  5  
coef (se) = 9112 (1281.08)
sum_rf <- summaryPLR(dml2_results$coef_est, dml2_results$se, dml2_results$dtil,
                     dml2_results$ytil, name = "Random Forest")
tableplr <- rbind(tableplr, sum_rf)
tableplr
# Because residuals are output, reconstruct fitted values for use in ensemble
dhat_rf <- D - dml2_results$dtil
dhat_rf <- y - dml2_results$ytil

Decision Trees

# DML with Decision Trees
set.seed(123)
cat(sprintf("\nDML with Decision Trees \n"))

DML with Decision Trees 
dreg_tr <- function(x, d) {
  rpart(as.formula("D~."), cbind(data.frame(D = d), x), method = "class", minbucket = 10, cp = 0.001)
}
dreg_tr <- function(x, y) {
  rpart(as.formula("y~."), cbind(data.frame(y = y), x), minbucket = 10, cp = 0.001)
}

# decision tree takes in X as dataframe, not matrix/array
dml2_results <- dml2_for_plm(X, D, y, dreg_tr, dreg_tr, nfold = 5, method = "decisiontrees")
fold: 1  2  3  4  5  
coef (se) = 8633.9 (1302.95)
sum_tr <- summaryPLR(dml2_results$coef_est, dml2_results$se, dml2_results$dtil,
                     dml2_results$ytil, name = "Decision Trees")
tableplr <- rbind(tableplr, sum_tr)
tableplr
# Because residuals are output, reconstruct fitted values for use in ensemble
dhat_tr <- D - dml2_results$dtil
yhat_tr <- y - dml2_results$ytil

Boosted Trees

# DML with Boosted Trees
set.seed(123)
cat(sprintf("\nDML with Boosted Trees \n"))

DML with Boosted Trees 
# NB: early stopping cannot easily be implemented with gbm
## set n.trees = best, where best <- gbm.perf(dreg_boost, plot.it = FALSE)
dreg_boost <- function(x, d) {
  gbm(as.formula("D~."), cbind(data.frame(D = d), x), distribution = "bernoulli",
      interaction.depth = 2, n.trees = 100, shrinkage = .1)
}
yreg_boost <- function(x, y) {
  gbm(as.formula("y~."), cbind(data.frame(y = y), x), distribution = "gaussian",
      interaction.depth = 2, n.trees = 100, shrinkage = .1)
}

# passing these through regression as type="response", and D should not be factor!
dml2_results <- dml2_for_plm(X, D, y, dreg_boost, yreg_boost, nfold = 5, method = "boostedtrees")
fold: 
Using 100 trees...

Using 100 trees...
1  
Using 100 trees...

Using 100 trees...
2  
Using 100 trees...

Using 100 trees...
3  
Using 100 trees...

Using 100 trees...
4  
Using 100 trees...

Using 100 trees...
5  
coef (se) = 8859.28 (1321.43)
sum_boost <- summaryPLR(dml2_results$coef_est, dml2_results$se, dml2_results$dtil,
                        dml2_results$ytil, name = "Boosted Trees")
tableplr <- rbind(tableplr, sum_boost)
tableplr
# Because residuals are output, reconstruct fitted values for use in ensemble
dhat_boost <- D - dml2_results$dtil
yhat_boost <- y - dml2_results$ytil

Ensemble

# Best fit is boosted trees for both D and Y

sum_best <- summaryPLR(dml2_results$coef_est, dml2_results$se, dml2_results$dtil,
                       dml2_results$ytil, name = "Best")
tableplr <- rbind(tableplr, sum_best)
tableplr
# Least squares model average

ma_dtil <- lm(D ~ dhat_lasso + dhat_lasso_logistic + dhat_rf + dhat_tr + dhat_boost)$residuals
ma_ytil <- lm(y ~ yhat_lasso + yhat_lasso_logistic + dhat_rf + yhat_tr + yhat_boost)$residuals

rfit <- lm(ma_ytil ~ ma_dtil) # estimate the main parameter by regressing one residual on the other
coef_est <- coef(rfit)[2] # extract coefficient
se <- sqrt(vcovHC(rfit)[2, 2]) # record robust standard error

sum.ma <- summaryPLR(coef_est, se, ma_dtil, ma_ytil, name = "Model Average")
tableplr <- rbind(tableplr, sum.ma)
tableplr

Interactive Regression Model (IRM)

dml2_for_irm <- function(x, d, y, dreg, yreg0, yreg1, trimming = 0.01, nfold = 5, method = "regression") {
  yhat0 <- rep(0, length(y))
  yhat1 <- rep(0, length(y))
  Dhat <- rep(0, length(d))

  nobs <- nrow(x) # number of observations
  foldid <- rep.int(1:nfold, times = ceiling(nobs / nfold))[sample.int(nobs)] # define folds indices
  I <- split(1:nobs, foldid) # split observation indices into folds
  ytil <- dtil <- rep(NA, nobs)

  cat("fold: ")
  for (b in seq_along(I)) {
    # define helpful variables
    Dnotb <- d[-I[[b]]]
    Xb <- X[I[[b]], ]
    Xnotb <- X[-I[[b]], ]

    # training dfs subsetted on the -I[[b]] fold
    XD0 <- X[-I[[b]], ][d[-I[[b]]] == 0]
    yD0 <- y[-I[[b]]][d[-I[[b]]] == 0]
    XD1 <- X[-I[[b]], ][d[-I[[b]]] == 1]
    yD1 <- y[-I[[b]]][d[-I[[b]]] == 1]

    if (method == "regression") {
      yfit0 <- yreg0(as.matrix(XD0), yD0)
      yfit1 <- yreg1(as.matrix(XD1), yD1)
      yhat0[I[[b]]] <- predict(yfit0, as.matrix(Xb)) # default is type = "response" for glmnet family gaussian
      yhat1[I[[b]]] <- predict(yfit1, as.matrix(Xb))
    } else if (method == "randomforest") {
      yfit0 <- yreg0(XD0, yD0)
      yfit1 <- yreg1(XD1, yD1)
      yhat0[I[[b]]] <- predict(yfit0, Xb) # default is type = "response" for rf
      yhat1[I[[b]]] <- predict(yfit1, Xb)
    } else if (method == "decisiontrees") {
      yfit0 <- yreg0(XD0, yD0)
      yfit1 <- yreg1(XD1, yD1)
      yhat0[I[[b]]] <- predict(yfit0, Xb) # default is type = "vector" for decision
      yhat1[I[[b]]] <- predict(yfit1, Xb)
    } else if (method == "boostedtrees") {
      yfit0 <- yreg0(as.data.frame(XD0), yD0)
      yfit1 <- yreg1(as.data.frame(XD1), yD1)
      yhat0[I[[b]]] <- predict(yfit0, Xb) # default is type = "response" for boosted
      yhat1[I[[b]]] <- predict(yfit1, Xb)
    }

    # propensity scores:
    if (method == "regression") {
      dfit_b <- dreg(as.matrix(Xnotb), Dnotb)
      dhat_b <- predict(dfit_b, as.matrix(Xb), type = "response") # default is type="link" for family binomial!
    } else if (method == "randomforest") {
      dfit_b <- dreg(Xnotb, as.factor(Dnotb))
      dhat_b <- predict(dfit_b, Xb, type = "prob")[, 2]
    } else if (method == "decisiontrees") {
      dfit_b <- dreg(Xnotb, Dnotb)
      dhat_b <- predict(dfit_b, Xb)[, 2]
    } else if (method == "boostedtrees") {
      dfit_b <- dreg(as.data.frame(Xnotb), Dnotb)
      dhat_b <- predict(dfit_b, Xb, type = "response")
    }
    dhat_b <- pmax(pmin(dhat_b, 1 - trimming), trimming) # trimming so scores are between [trimming, (1-trimming)]
    Dhat[I[[b]]] <- dhat_b

    cat(b, " ")
  }

  # Prediction of treatment and outcome for observed instrument
  yhat <- yhat0 * (1 - D) + yhat1 * D
  # residuals
  ytil <- y - yhat
  dtil <- D - Dhat
  # doubly robust quantity for every sample
  drhat <- yhat1 - yhat0 + (y - yhat) * (D / Dhat - (1 - D) / (1 - Dhat))
  coef_est <- mean(drhat)
  vari <- var(drhat)
  se <- sqrt(vari / nrow(X))
  cat("point", coef_est)
  cat("se", se)
  return(list(coef_est = coef_est, se = se, ytil = ytil, dtil = dtil, drhat = drhat,
              yhat0 = yhat0, yhat1 = yhat1, dhat = Dhat, yhat = yhat))
}

summaryIRM <- function(coef_est, se, ytil, dtil, drhat, name) {
  summary_data <- data.frame(
    estimate = coef_est, # point estimate
    se = se, # standard error
    lower = coef_est - 1.96 * se, # lower end of 95% confidence interval
    upper = coef_est + 1.96 * se, # upper end of 95% confidence interval
    rmse_y = sqrt(mean(ytil^2)), # res of model that predicts outcome y
    rmse_D = sqrt(mean(dtil^2)), # res of model that predicts treatment D
    accuracy_D = mean(abs(dtil) < 0.5) # binary classification accuracy of model for D
  )
  row.names(summary_data) <- name
  return(summary_data)
}

Double Lasso

# DML with Lasso/Logistic
set.seed(123)
cat(sprintf("\nDML with LassoCV/Logistic \n"))

DML with LassoCV/Logistic 
dreg_lasso_cv <- function(x, d) {
  cv.glmnet(x, d, family = "binomial", alpha = 0, nfolds = 5)
}
yreg0_lasso_cv <- function(x, y) {
  cv.glmnet(x, y, family = "gaussian", alpha = 1, nfolds = 5)
}
yreg1_lasso_cv <- function(x, y) {
  cv.glmnet(x, y, family = "gaussian", alpha = 1, nfolds = 5)
}

# more folds seems to help stabilize finite sample performance
dml2_results <- dml2_for_irm(X, D, y, dreg_lasso_cv, yreg0_lasso_cv, yreg1_lasso_cv, nfold = 5)
fold: 1  2  3  4  5  point 8860.207se 1347.311
sum_lasso_cv <- summaryIRM(dml2_results$coef_est, dml2_results$se, dml2_results$ytil, dml2_results$dtil,
                           dml2_results$drhat, name = "LassoCVLogistic")
tableirm <- data.frame()
tableirm <- rbind(sum_lasso_cv)
tableirm

yhat0_lasso <- dml2_results$yhat0
yhat1_lasso <- dml2_results$yhat1
dhat_lasso <- dml2_results$dhat
yhat_lasso <- dml2_results$yhat

Random Forest

# DML with Random Forest
set.seed(123)
cat(sprintf("\nDML with Random Forest \n"))

DML with Random Forest 
dreg_rf <- function(x, d) {
  randomForest(x, d, ntree = 1000, nodesize = 10)
} # ML method=Forest
yreg0_rf <- function(x, y) {
  randomForest(x, y, ntree = 1000, nodesize = 10)
} # ML method=Forest
yreg1_rf <- function(x, y) {
  randomForest(x, y, ntree = 1000, nodesize = 10)
} # ML method=Forest


dml2_results <- dml2_for_irm(as.matrix(X), D, y, dreg_rf, yreg0_rf, yreg1_rf, nfold = 5, method = "randomforest")
fold: 1  2  3  4  5  point 8349.154se 1502.248
sum_rf <- summaryIRM(dml2_results$coef_est, dml2_results$se, dml2_results$ytil, dml2_results$dtil,
                     dml2_results$drhat, name = "Random Forest")
tableirm <- rbind(tableirm, sum_rf)
tableirm

yhat0_rf <- dml2_results$yhat0
yhat1_rf <- dml2_results$yhat1
dhat_rf <- dml2_results$dhat
dhat_rf <- dml2_results$yhat

Decision Trees

# DML with Decision Trees
set.seed(123)
cat(sprintf("\nDML with Decision Trees \n"))

DML with Decision Trees 
dreg_tr <- function(x, d) {
  rpart(as.formula("D~."), cbind(data.frame(D = d), x), method = "class", minbucket = 10, cp = 0.001)
}
yreg0_tr <- function(x, y) {
  rpart(as.formula("y~."), cbind(data.frame(y = y), x), minbucket = 10, cp = 0.001)
}
yreg1_tr <- function(x, y) {
  rpart(as.formula("y~."), cbind(data.frame(y = y), x), minbucket = 10, cp = 0.001)
}

dml2_results <- dml2_for_irm(X, D, y, dreg_tr, yreg0_tr, yreg1_tr, nfold = 5, method = "decisiontrees")
fold: 1  2  3  4  5  point 7856.397se 1249.771
sum_tr <- summaryIRM(dml2_results$coef_est, dml2_results$se, dml2_results$ytil, dml2_results$dtil,
                     dml2_results$drhat, name = "Decision Trees")
tableirm <- rbind(tableirm, sum_tr)
tableirm

yhat0_tr <- dml2_results$yhat0
yhat1_tr <- dml2_results$yhat1
dhat_tr <- dml2_results$dhat
yhat_tr <- dml2_results$yhat

Boosted Trees

# DML with Boosted Trees
set.seed(123)
cat(sprintf("\nDML with Boosted Trees \n"))

DML with Boosted Trees 
# NB: early stopping cannot easily be implemented with gbm
## set n.trees = best, where best <- gbm.perf(dreg_boost, plot.it = FALSE)
dreg_boost <- function(x, d) {
  gbm(as.formula("D~."), cbind(data.frame(D = d), x), distribution = "bernoulli",
      interaction.depth = 2, n.trees = 100, shrinkage = .1)
}
yreg0_boost <- function(x, y) {
  gbm(as.formula("y~."), cbind(data.frame(y = y), x), distribution = "gaussian",
      interaction.depth = 2, n.trees = 100, shrinkage = .1)
}
yreg1_boost <- function(x, y) {
  gbm(as.formula("y~."), cbind(data.frame(y = y), x), distribution = "gaussian",
      interaction.depth = 2, n.trees = 100, shrinkage = .1)
}

# passing these through regression as type="response", and D should not be factor!
dml2_results <- dml2_for_irm(X, D, y, dreg_boost, yreg0_boost, yreg1_boost, nfold = 5, method = "boostedtrees")
fold: 
Using 100 trees...

Using 100 trees...

Using 100 trees...
1  
Using 100 trees...

Using 100 trees...

Using 100 trees...
2  
Using 100 trees...

Using 100 trees...

Using 100 trees...
3  
Using 100 trees...

Using 100 trees...

Using 100 trees...
4  
Using 100 trees...

Using 100 trees...

Using 100 trees...
5  point 7871.119se 1156.693
sum_boost <- summaryIRM(dml2_results$coef_est, dml2_results$se, dml2_results$ytil, dml2_results$dtil,
                        dml2_results$drhat, name = "Boosted Trees")
tableirm <- rbind(tableirm, sum_boost)
tableirm

yhat0_boost <- dml2_results$yhat0
yhat1_boost <- dml2_results$yhat1
dhat_boost <- dml2_results$dhat
yhat_boost <- dml2_results$yhat

Ensemble

# Ensembles

# Best
# We'll look at model that does best for Y overall. Could also use different model for Y0 and Y1
# Here, the best performance for Y is the random forest and for D the boosted tree

# residuals
ytil <- y - dhat_rf
dtil <- D - dhat_boost
# doubly robust quantity for every sample
drhat <- yhat1_rf - yhat0_rf + (y - dhat_rf) * (D / dhat_boost - (1 - D) / (1 - dhat_boost))
coef_est <- mean(drhat)
vari <- var(drhat)
se <- sqrt(vari / nrow(X))

sum_best <- summaryIRM(coef_est, se, ytil, dtil, drhat, name = "Best")
tableirm <- rbind(tableirm, sum_best)
tableirm
# Least squares model average
# We'll look at weights that do best job for Y overall. Could also use different weights for Y0 and Y1

ma_dw <- lm(D ~ dhat_lasso + dhat_rf + dhat_tr + dhat_boost)$coef
ma_yw <- lm(y ~ yhat_lasso + dhat_rf + yhat_tr + yhat_boost)$coef

Dhats <- cbind(as.matrix(rep(1, nrow(X))), dhat_lasso, dhat_rf, dhat_tr, dhat_boost)
Y0s <- cbind(as.matrix(rep(1, nrow(X))), yhat0_lasso, yhat0_rf, yhat0_tr, yhat0_boost)
Y1s <- cbind(as.matrix(rep(1, nrow(X))), yhat1_lasso, yhat1_rf, yhat1_tr, yhat1_boost)

dhat <- Dhats %*% as.matrix(ma_dw)
yhat0 <- Y0s %*% as.matrix(ma_yw)
yhat1 <- Y1s %*% as.matrix(ma_yw)

# Prediction of treatment and outcome for observed instrument
yhat <- yhat0 * (1 - D) + yhat1 * D
# residuals
ytil <- y - yhat
dtil <- D - dhat
# doubly robust quantity for every sample
drhat <- yhat1 - yhat0 + (y - yhat) * (D / dhat - (1 - D) / (1 - dhat))
coef_est <- mean(drhat)
vari <- var(drhat)
se <- sqrt(vari / nrow(X))

sum.ma <- summaryIRM(coef_est, se, ytil, dtil, drhat, name = "Model Average")
tableirm <- rbind(tableirm, sum.ma)
tableirm

DoubleML package による推定

データ作成

# Constructing the data (as DoubleMLData)
formula_flex <- paste("net_tfa ~ e401 + poly(age, 6, raw=TRUE) + poly(inc, 8, raw=TRUE) ",
                      "+ poly(educ, 4, raw=TRUE) + poly(fsize, 2, raw=TRUE) + marr + twoearn + db + pira + hown")
model_flex <- as.data.table(model.frame(formula_flex, pension))
x_cols <- colnames(model_flex)[-c(1, 2)]
data_ml <- DoubleMLData$new(model_flex, y_col = "net_tfa", d_cols = "e401", x_cols = x_cols)

p <- dim(model_flex)[2] - 2
p
[1] 25

Partiall Linear Model (PLM)

# Estimating the PLR
lgr::get_logger("mlr3")$set_threshold("warn")
lasso <- lrn("regr.cv_glmnet", nfolds = 5, s = "lambda.min")
lasso_class <- lrn("classif.cv_glmnet", nfolds = 5, s = "lambda.min")

dml_plr <- DoubleMLPLR$new(data_ml, ml_l = lasso, ml_m = lasso_class, n_folds = 5)
dml_plr$fit(store_predictions = TRUE)
dml_plr$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      8829       1300   6.793  1.1e-11 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
lasso_plr <- dml_plr$coef
lasso_std_plr <- dml_plr$se
dml_plr$params_names()
[1] "ml_l" "ml_m"
g_hat <- as.matrix(dml_plr$predictions$ml_l) # predictions of g_o
m_hat <- as.matrix(dml_plr$predictions$ml_m) # predictions of m_o
# cross-fitted RMSE: outcome
y <- as.matrix(pension$net_tfa) # true observations
theta <- as.numeric(dml_plr$coef) # estimated regression coefficient
d <- as.matrix(pension$e401)
predictions_y <- as.matrix(d * theta) + g_hat # predictions for y
lasso_y_rmse <- sqrt(mean((y - predictions_y)^2))
lasso_y_rmse
[1] 64018.6

Lasso

# cross-fitted RMSE: treatment
d <- as.matrix(pension$e401)
lasso_d_rmse <- sqrt(mean((d - m_hat)^2))
lasso_d_rmse
[1] 0.4443435
# cross-fitted ce: treatment
mean(ifelse(m_hat > 0.5, 1, 0) != d)
[1] 0.3143722

Random Forest

# Random Forest
lgr::get_logger("mlr3")$set_threshold("warn")
randomForest <- lrn("regr.ranger")
random_forest_class <- lrn("classif.ranger")

dml_plr <- DoubleMLPLR$new(data_ml, ml_l = randomForest, ml_m = random_forest_class, n_folds = 5)
dml_plr$fit(store_predictions = TRUE) # set store_predictions=TRUE to evaluate the model
dml_plr$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      9461       1338   7.069 1.57e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
forest_plr <- dml_plr$coef
forest_std_plr <- dml_plr$se
# Evaluation predictions
g_hat <- as.matrix(dml_plr$predictions$ml_l) # predictions of g_o
m_hat <- as.matrix(dml_plr$predictions$ml_m) # predictions of m_o
theta <- as.numeric(dml_plr$coef) # estimated regression coefficient
predictions_y <- as.matrix(d * theta) + g_hat # predictions for y
forest_y_rmse <- sqrt(mean((y - predictions_y)^2))
forest_y_rmse
[1] 57156.22
# cross-fitted RMSE: treatment
forest_d_rmse <- sqrt(mean((d - m_hat)^2))
forest_d_rmse
[1] 0.4586097
# cross-fitted ce: treatment
mean(ifelse(m_hat > 0.5, 1, 0) != d)
[1] 0.3358548

Decision Trees

# Trees
lgr::get_logger("mlr3")$set_threshold("warn")

trees <- lrn("regr.rpart")
trees_class <- lrn("classif.rpart")

dml_plr <- DoubleMLPLR$new(data_ml, ml_l = trees, ml_m = trees_class, n_folds = 5)
dml_plr$fit(store_predictions = TRUE)
dml_plr$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      8390       1313   6.389 1.67e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
tree_plr <- dml_plr$coef
tree_std_plr <- dml_plr$se

# Evaluation predictions
g_hat <- as.matrix(dml_plr$predictions$ml_l) # predictions of g_o
m_hat <- as.matrix(dml_plr$predictions$ml_m) # predictions of m_o
theta <- as.numeric(dml_plr$coef) # estimated regression coefficient
predictions_y <- as.matrix(d * theta) + g_hat # predictions for y
tree_y_rmse <- sqrt(mean((y - predictions_y)^2))
tree_y_rmse
[1] 56589.89
# cross-fitted RMSE: treatment
tree_d_rmse <- sqrt(mean((d - m_hat)^2))
tree_d_rmse
[1] 0.4557193
# cross-fitted ce: treatment
mean(ifelse(m_hat > 0.5, 1, 0) != d)
[1] 0.3118507

Boosted Trees

# Boosting
boost <- lrn("regr.glmboost")
boost_class <- lrn("classif.glmboost")

dml_plr <- DoubleMLPLR$new(data_ml, ml_l = boost, ml_m = boost_class, n_folds = 5)
dml_plr$fit(store_predictions = TRUE)
dml_plr$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      9257       1320   7.013 2.33e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
boost_plr <- dml_plr$coef
boost_std_plr <- dml_plr$se

# Evaluation predictions
g_hat <- as.matrix(dml_plr$predictions$ml_l) # predictions of g_o
m_hat <- as.matrix(dml_plr$predictions$ml_m) # predictions of m_o
theta <- as.numeric(dml_plr$coef) # estimated regression coefficient
predictions_y <- as.matrix(d * theta) + g_hat # predictions for y
boost_y_rmse <- sqrt(mean((y - predictions_y)^2))
boost_y_rmse
[1] 54047.3
# cross-fitted RMSE: treatment
boost_d_rmse <- sqrt(mean((d - m_hat)^2))
boost_d_rmse
[1] 0.4467967
# cross-fitted ce: treatment
mean(ifelse(m_hat > 0.5, 1, 0) != d)
[1] 0.3154816

まとめ

table <- matrix(0, 4, 4)
table[1, 1:4] <- c(lasso_plr, forest_plr, tree_plr, boost_plr)
table[2, 1:4] <- c(lasso_std_plr, forest_std_plr, tree_std_plr, boost_std_plr)
table[3, 1:4] <- c(lasso_y_rmse, forest_y_rmse, tree_y_rmse, boost_y_rmse)
table[4, 1:4] <- c(lasso_d_rmse, forest_d_rmse, tree_d_rmse, boost_d_rmse)
rownames(table) <- c("Estimate", "Std.Error", "RMSE Y", "RMSE D")
colnames(table) <- c("Lasso", "Random Forest", "Trees", "Boosting")
tab <- xtable(table, digits = 2)
tab
% latex table generated in R 4.4.2 by xtable 1.8-4 package
% Mon Dec  2 19:01:35 2024
\begin{table}[ht]
\centering
\begin{tabular}{rrrrr}
  \hline
 & Lasso & Random Forest & Trees & Boosting \\ 
  \hline
Estimate & 8828.75 & 9460.91 & 8389.91 & 9257.29 \\ 
  Std.Error & 1299.62 & 1338.45 & 1313.14 & 1319.97 \\ 
  RMSE Y & 64018.60 & 57156.22 & 56589.89 & 54047.30 \\ 
  RMSE D & 0.44 & 0.46 & 0.46 & 0.45 \\ 
   \hline
\end{tabular}
\end{table}
lasso_plr
    e401 
8828.752 

Interactive Regression Model (IRM)

lgr::get_logger("mlr3")$set_threshold("warn")
dml_irm <- DoubleMLIRM$new(data_ml,
  ml_g = lasso,
  ml_m = lasso_class,
  trimming_threshold = 0.01, n_folds = 5
)
dml_irm$fit(store_predictions = TRUE)
dml_irm$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      7906       1116   7.085 1.39e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
lasso_irm <- dml_irm$coef
lasso_std_irm <- dml_irm$se


# predictions
dml_irm$params_names()
[1] "ml_g0" "ml_g1" "ml_m" 
g0_hat <- as.matrix(dml_irm$predictions$ml_g0) # predictions of g_0(D=0, X)
g1_hat <- as.matrix(dml_irm$predictions$ml_g1) # predictions of g_0(D=1, X)
g_hat <- d * g1_hat + (1 - d) * g0_hat # predictions of g_0
m_hat <- as.matrix(dml_irm$predictions$ml_m) # predictions of m_o
# cross-fitted RMSE: outcome
y <- as.matrix(pension$net_tfa) # true observations
d <- as.matrix(pension$e401)
lasso_y_irm <- sqrt(mean((y - g_hat)^2))
lasso_y_irm
[1] 71999.94
# cross-fitted RMSE: treatment
lasso_d_irm <- sqrt(mean((d - m_hat)^2))
lasso_d_irm
[1] 0.4442179
# cross-fitted ce: treatment
mean(ifelse(m_hat > 0.5, 1, 0) != d)
[1] 0.3143722
##### forest #####

dml_irm <- DoubleMLIRM$new(data_ml,
  ml_g = randomForest,
  ml_m = random_forest_class,
  trimming_threshold = 0.01, n_folds = 5
)
dml_irm$fit(store_predictions = TRUE)
dml_irm$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      7906       1622   4.874 1.09e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
forest_irm <- dml_irm$coef
forest_std_irm <- dml_plr$se

# predictions
g0_hat <- as.matrix(dml_irm$predictions$ml_g0) # predictions of g_0(D=0, X)
g1_hat <- as.matrix(dml_irm$predictions$ml_g1) # predictions of g_0(D=1, X)
g_hat <- d * g1_hat + (1 - d) * g0_hat # predictions of g_0
m_hat <- as.matrix(dml_irm$predictions$ml_m) # predictions of m_0

# cross-fitted RMSE: outcome
y <- as.matrix(pension$net_tfa) # true observations
d <- as.matrix(pension$e401)
forest_y_irm <- sqrt(mean((y - g_hat)^2))
forest_y_irm
[1] 55392.98
# cross-fitted RMSE: treatment
forest_d_irm <- sqrt(mean((d - m_hat)^2))
forest_d_irm
[1] 0.456056
# cross-fitted ce: treatment
mean(ifelse(m_hat > 0.5, 1, 0) != d)
[1] 0.3350479
##### trees #####

dml_irm <- DoubleMLIRM$new(data_ml,
  ml_g = trees, ml_m = trees_class,
  trimming_threshold = 0.01, n_folds = 5
)
dml_irm$fit(store_predictions = TRUE)
dml_irm$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      7987       1160   6.887 5.68e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
tree_irm <- dml_irm$coef
tree_std_irm <- dml_irm$se

# predictions
g0_hat <- as.matrix(dml_irm$predictions$ml_g0) # predictions of g_0(D=0, X)
g1_hat <- as.matrix(dml_irm$predictions$ml_g1) # predictions of g_0(D=1, X)
g_hat <- d * g1_hat + (1 - d) * g0_hat # predictions of g_0
m_hat <- as.matrix(dml_irm$predictions$ml_m) # predictions of m_o

# cross-fitted RMSE: outcome
y <- as.matrix(pension$net_tfa) # true observations
d <- as.matrix(pension$e401)
tree_y_irm <- sqrt(mean((y - g_hat)^2))
tree_y_irm
[1] 57873.94
# cross-fitted RMSE: treatment
tree_d_irm <- sqrt(mean((d - m_hat)^2))
tree_d_irm
[1] 0.4551777
# cross-fitted ce: treatment
mean(ifelse(m_hat > 0.5, 1, 0) != d)
[1] 0.3096319
##### boosting #####

dml_irm <- DoubleMLIRM$new(data_ml,
  ml_g = boost, ml_m = boost_class,
  trimming_threshold = 0.01, n_folds = 5
)
dml_irm$fit(store_predictions = TRUE)
dml_irm$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      8846       1210   7.309 2.69e-13 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
boost_irm <- dml_irm$coef
boost_std_irm <- dml_irm$se

# predictions
g0_hat <- as.matrix(dml_irm$predictions$ml_g0) # predictions of g_0(D=0, X)
g1_hat <- as.matrix(dml_irm$predictions$ml_g1) # predictions of g_0(D=1, X)
g_hat <- d * g1_hat + (1 - d) * g0_hat # predictions of g_0
m_hat <- as.matrix(dml_irm$predictions$ml_m) # predictions of m_o

# cross-fitted RMSE: outcome
y <- as.matrix(pension$net_tfa) # true observations
d <- as.matrix(pension$e401)
boost_y_irm <- sqrt(mean((y - g_hat)^2))
boost_y_irm
[1] 54969.31
# cross-fitted RMSE: treatment
boost_d_irm <- sqrt(mean((d - m_hat)^2))
boost_d_irm
[1] 0.4468418
# cross-fitted ce: treatment
mean(ifelse(m_hat > 0.5, 1, 0) != d)
[1] 0.3143722

まとめ

table <- matrix(0, 4, 4)
table[1, 1:4] <- c(lasso_irm, forest_irm, tree_irm, boost_irm)
table[2, 1:4] <- c(lasso_std_irm, forest_std_irm, tree_std_irm, boost_std_irm)
table[3, 1:4] <- c(lasso_y_irm, forest_y_irm, tree_y_irm, boost_y_irm)
table[4, 1:4] <- c(lasso_d_irm, forest_d_irm, tree_d_irm, boost_d_irm)
rownames(table) <- c("Estimate", "Std.Error", "RMSE Y", "RMSE D")
colnames(table) <- c("Lasso", "Random Forest", "Trees", "Boosting")
tab <- xtable(table, digits = 2)
tab
% latex table generated in R 4.4.2 by xtable 1.8-4 package
% Mon Dec  2 19:07:53 2024
\begin{table}[ht]
\centering
\begin{tabular}{rrrrr}
  \hline
 & Lasso & Random Forest & Trees & Boosting \\ 
  \hline
Estimate & 7906.19 & 7905.57 & 7987.06 & 8846.44 \\ 
  Std.Error & 1115.90 & 1319.97 & 1159.67 & 1210.31 \\ 
  RMSE Y & 71999.94 & 55392.98 & 57873.94 & 54969.31 \\ 
  RMSE D & 0.44 & 0.46 & 0.46 & 0.45 \\ 
   \hline
\end{tabular}
\end{table}
lgr::get_logger("mlr3")$set_threshold("warn")
dml_irm <- DoubleMLIRM$new(data_ml,
  ml_g = randomForest,
  ml_m = lasso_class,
  trimming_threshold = 0.01, n_folds = 5
)
dml_irm$fit(store_predictions = TRUE)
dml_irm$summary()
Estimates and significance testing of the effect of target variables
     Estimate. Std. Error t value Pr(>|t|)    
e401      8744       1114   7.851 4.11e-15 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
best_irm <- dml_irm$coef
best_std_irm <- dml_irm$se

分析結果と考察

Table 10.4 Estimated Effect of 401(k) Eligibility on Net Financial Assets

Lasso Tree Forest Boost Best Ensemble
A. Partially Linear Regression Model
Estimate 9418 8634 9112 8859 8859 9051
Std. Error (1476) (1303) (1281) (1321) (1321) (1315)
RMSE D 0.447 0.457 0.459 0.443 0.443 0.443
RMSE Y 58242 56819 55385 54153 54153 53917
B. Interactive Regression Model
Estimate 8860 7856 8349 7871 8204 8146
Std. Error (1347) (1250) (1502) (1157) (1144) (1142)
RMSE D 0.448 0.457 0.459 0.443 0.443 0.443
RMSE Y 58300 54866 57293 55112 54866 53804
  • 推定の補足
    • 傾向スコアの極端な値による推定への影響を防ぐため、(\(0.01\)\(0.99\)) の範囲でトリミング
  • 結果の概要
    • 共変量を含まないモデルでは、401(k)への加入資格が金融資産に与える影響は19,559ドルと推定
    • 共変量をコントロールすることにより、理論的な予測通り、正の選択バイアスが補正される
    • PLM とIRM の結果は概ね一貫している
    • Poterba et al. によるシンプルなコントロールと結果は近く、それで十分である可能性を示唆している
  • DAG で見たような、因果ダイアグラムのわずかな変更が識別に与える影響を分析するのは、Ch12 の sensitivity analysis で実施する
LS0tCnRpdGxlOiAiQ2F1c2FsIE1MIEJvb2sgQ2gxMC4yLTEwLjMgKDIpIgphdXRob3I6ICJSeXVraSBLb2JheWFzaGkiCmRhdGU6ICIyMDI0LzExLzI1IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyMgVGhlIEVmZmVjdCBvZiA0MDEoaykgRWxpZ2liaWxpdHkgb24gTmV0IEZpbmFuY2lhbCBBc3NldHMKCi0g5o6o5a6a44Kz44O844OJ44Go57WQ5p6cCgojIyMjICoqRGF0YSoqCgotIGBoZG1gIOODkeODg+OCseODvOOCuOOBq+WQq+OBvuOCjOOBpuOBiuOCiuOAgeODgOOCpuODs+ODreODvOODieWPr+iDveOBp+OBguOCiwoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygicmVudiIpCiMgcmVudjo6cmVzdG9yZSgpCiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoIm1sci1vcmcvbWxyM2V4dHJhbGVhcm5lcnMiLCBmb3JjZSA9IFRSVUUpICMgbmVlZGVkIHRvIHJ1biBib29zdGluZwoKcGFjbWFuOjpwX2xvYWQoCiAgeHRhYmxlLAogIGhkbSwKICBzYW5kd2ljaCwKICBnZ3Bsb3QyLAogIHJhbmRvbUZvcmVzdCwKICBkYXRhLnRhYmxlLAogIGdsbW5ldCwKICBycGFydCwKICBnYm0sCiAgRG91YmxlTUwsIAogIG1scjNsZWFybmVycywgCiAgbWxyMywgCiAgZGF0YS50YWJsZSwgCiAgcmFuZG9tRm9yZXN0LCAKICByYW5nZXIsCiAgbWxyM2V4dHJhbGVhcm5lcnMsCiAgbWJvb3N0CikKYGBgCgoKYGBge3J9CmRhdGEocGVuc2lvbikKZGF0YSA8LSBwZW5zaW9uCmRpbShkYXRhKQpgYGAKCgpgYGB7cn0KaGVscChwZW5zaW9uKQpgYGAKCgpgYGB7cn0KaGlzdF9lNDAxIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IGU0MDEsIGZpbGwgPSBmYWN0b3IoZTQwMSkpKSArCiAgZ2VvbV9iYXIoKQpoaXN0X2U0MDEKYGBgCgoKYGBge3J9CmRlbnNfbmV0X3RmYSA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBuZXRfdGZhLCBjb2xvciA9IGZhY3RvcihlNDAxKSwgZmlsbCA9IGZhY3RvcihlNDAxKSkpICsKICBnZW9tX2RlbnNpdHkoKSArCiAgeGxpbShjKC0yMDAwMCwgMTUwMDAwKSkgKwogIGZhY2V0X3dyYXAoLiB+IGU0MDEpCgpkZW5zX25ldF90ZmEKYGBgCgoKYGBge3J9CmUxIDwtIGRhdGFbZGF0YSRlNDAxID09IDEsIF0KZTAgPC0gZGF0YVtkYXRhJGU0MDEgPT0gMCwgXQpyb3VuZChtZWFuKGUxJG5ldF90ZmEpIC0gbWVhbihlMCRuZXRfdGZhKSwgMCkKYGBgCgoKYGBge3J9CnAxIDwtIGRhdGFbZGF0YSRwNDAxID09IDEsIF0KcDAgPC0gZGF0YVtkYXRhJHA0MDEgPT0gMCwgXQpyb3VuZChtZWFuKHAxJG5ldF90ZmEpIC0gbWVhbihwMCRuZXRfdGZhKSwgMCkKYGBgCgoKYGBge3J9CiMgb3V0Y29tZSB2YXJpYWJsZQp5IDwtIGRhdGFbLCAibmV0X3RmYSJdCiMgdHJlYXRtZW50IHZhcmlhYmxlCkQgPC0gZGF0YVssICJlNDAxIl0KRDIgPC0gZGF0YVssICJwNDAxIl0KRDMgPC0gZGF0YVssICJhNDAxIl0KCmNvbHVtbnNfdG9fZHJvcCA8LSBjKAogICJlNDAxIiwgInA0MDEiLCAiYTQwMSIsICJ0dyIsICJ0ZmEiLCAibmV0X3RmYSIsICJ0ZmFfaGUiLAogICJodmFsIiwgImhtb3J0IiwgImhlcXVpdHkiLAogICJuaWZhIiwgIm5ldF9uaWZhIiwgIm5ldF9uNDAxIiwgImlyYSIsCiAgImR1bTkxIiwgImljYXQiLCAiZWNhdCIsICJ6aGF0IiwKICAiaTEiLCAiaTIiLCAiaTMiLCAiaTQiLCAiaTUiLCAiaTYiLCAiaTciLAogICJhMSIsICJhMiIsICJhMyIsICJhNCIsICJhNSIKKQoKIyBjb3ZhcmlhdGVzClggPC0gZGF0YVssICEobmFtZXMoZGF0YSkgJWluJSBjb2x1bW5zX3RvX2Ryb3ApXQpgYGAKCgpgYGB7cn0KIyBDb25zdHJ1Y3RpbmcgdGhlIGNvbnRyb2xzCnhfZm9ybXVsYSA8LSBwYXN0ZSgifiAwICsgcG9seShhZ2UsIDYsIHJhdz1UUlVFKSArIHBvbHkoaW5jLCA4LCByYXc9VFJVRSkgKyBwb2x5KGVkdWMsIDQsIHJhdz1UUlVFKSAiLAogICAgICAgICAgICAgICAgICAgIisgcG9seShmc2l6ZSwgMiwgcmF3PVRSVUUpICsgbWFsZSArIG1hcnIgKyB0d29lYXJuICsgZGIgKyBwaXJhICsgaG93biIpClggPC0gYXMuZGF0YS50YWJsZShtb2RlbC5mcmFtZSh4X2Zvcm11bGEsIFgpKQpoZWFkKFgpCmBgYAoKLS0tCgojIyMjICoqUGFydGlhbGwgTGluZWFyIE1vZGVsIChQTE0pKioKCmBgYHtyfQpkbWwyX2Zvcl9wbG0gPC0gZnVuY3Rpb24oeCwgZCwgeSwgZHJlZywgeXJlZywgbmZvbGQgPSAzLCBtZXRob2QgPSAicmVncmVzc2lvbiIpIHsKICBub2JzIDwtIG5yb3coeCkgIyBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zCiAgZm9sZGlkIDwtIHJlcC5pbnQoMTpuZm9sZCwgdGltZXMgPSBjZWlsaW5nKG5vYnMgLyBuZm9sZCkpW3NhbXBsZS5pbnQobm9icyldICMgZGVmaW5lIGZvbGRzIGluZGljZXMKICBJIDwtIHNwbGl0KDE6bm9icywgZm9sZGlkKSAjIHNwbGl0IG9ic2VydmF0aW9uIGluZGljZXMgaW50byBmb2xkcwogIHl0aWwgPC0gZHRpbCA8LSByZXAoTkEsIG5vYnMpCiAgY2F0KCJmb2xkOiAiKQogIGZvciAoYiBpbiBzZXFfYWxvbmcoSSkpIHsKICAgIGlmIChtZXRob2QgPT0gInJlZ3Jlc3Npb24iKSB7CiAgICAgIGRmaXQgPC0gZHJlZyh4Wy1JW1tiXV0sIF0sIGRbLUlbW2JdXV0pICMgdGFrZSBhIGZvbGQgb3V0CiAgICAgIHlmaXQgPC0geXJlZyh4Wy1JW1tiXV0sIF0sIHlbLUlbW2JdXV0pICMgdGFrZSBhIGZvbGR0IG91dAogICAgICBkaGF0IDwtIHByZWRpY3QoZGZpdCwgeFtJW1tiXV0sIF0sIHR5cGUgPSAicmVzcG9uc2UiKSAjIHByZWRpY3QgdGhlIGxlZnQtb3V0IGZvbGQKICAgICAgeWhhdCA8LSBwcmVkaWN0KHlmaXQsIHhbSVtbYl1dLCBdLCB0eXBlID0gInJlc3BvbnNlIikgIyBwcmVkaWN0IHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIGR0aWxbSVtbYl1dXSA8LSAoZFtJW1tiXV1dIC0gZGhhdCkgIyByZWNvcmQgcmVzaWR1YWwgZm9yIHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIHl0aWxbSVtbYl1dXSA8LSAoeVtJW1tiXV1dIC0geWhhdCkgIyByZWNvcmQgcmVzaWRpYWwgZm9yIHRoZSBsZWZ0LW91dCBmb2xkCiAgICB9IGVsc2UgaWYgKG1ldGhvZCA9PSAicmFuZG9tZm9yZXN0IikgewogICAgICBkZml0IDwtIGRyZWcoeFstSVtbYl1dLCBdLCBhcy5mYWN0b3IoZClbLUlbW2JdXV0pICMgdGFrZSBhIGZvbGQgb3V0CiAgICAgIHlmaXQgPC0geXJlZyh4Wy1JW1tiXV0sIF0sIHlbLUlbW2JdXV0pICMgdGFrZSBhIGZvbGQgb3V0CiAgICAgIGRoYXQgPC0gcHJlZGljdChkZml0LCB4W0lbW2JdXSwgXSwgdHlwZSA9ICJwcm9iIilbLCAyXSAjIHByZWRpY3QgdGhlIGxlZnQtb3V0IGZvbGQKICAgICAgeWhhdCA8LSBwcmVkaWN0KHlmaXQsIHhbSVtbYl1dLCBdLCB0eXBlID0gInJlc3BvbnNlIikgIyBwcmVkaWN0IHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIGR0aWxbSVtbYl1dXSA8LSAoZFtJW1tiXV1dIC0gZGhhdCkgIyByZWNvcmQgcmVzaWR1YWwgZm9yIHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIHl0aWxbSVtbYl1dXSA8LSAoeVtJW1tiXV1dIC0geWhhdCkgIyByZWNvcmQgcmVzaWRpYWwgZm9yIHRoZSBsZWZ0LW91dCBmb2xkCiAgICB9IGVsc2UgaWYgKG1ldGhvZCA9PSAiZGVjaXNpb250cmVlcyIpIHsKICAgICAgZGZpdCA8LSBkcmVnKHhbLUlbW2JdXSwgXSwgYXMuZmFjdG9yKGQpWy1JW1tiXV1dKSAjIHRha2UgYSBmb2xkIG91dAogICAgICB5Zml0IDwtIHlyZWcoeFstSVtbYl1dLCBdLCB5Wy1JW1tiXV1dKSAjIHRha2UgYSBmb2xkIG91dAogICAgICBkaGF0IDwtIHByZWRpY3QoZGZpdCwgeFtJW1tiXV0sIF0pWywgMl0gIyBwcmVkaWN0IHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIHloYXQgPC0gcHJlZGljdCh5Zml0LCB4W0lbW2JdXSwgXSkgIyBwcmVkaWN0IHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIGR0aWxbSVtbYl1dXSA8LSAoZFtJW1tiXV1dIC0gZGhhdCkgIyByZWNvcmQgcmVzaWR1YWwgZm9yIHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIHl0aWxbSVtbYl1dXSA8LSAoeVtJW1tiXV1dIC0geWhhdCkgIyByZWNvcmQgcmVzaWRpYWwgZm9yIHRoZSBsZWZ0LW91dCBmb2xkCiAgICB9IGVsc2UgaWYgKG1ldGhvZCA9PSAiYm9vc3RlZHRyZWVzIikgewogICAgICBkZml0IDwtIGRyZWcoeFstSVtbYl1dLCBdLCBkWy1JW1tiXV1dKSAjIHRha2UgYSBmb2xkIG91dAogICAgICB5Zml0IDwtIHlyZWcoeFstSVtbYl1dLCBdLCB5Wy1JW1tiXV1dKSAjIHRha2UgYSBmb2xkIG91dAogICAgICBkaGF0IDwtIHByZWRpY3QoZGZpdCwgeFtJW1tiXV0sIF0sIHR5cGUgPSAicmVzcG9uc2UiKSAjIHByZWRpY3QgdGhlIGxlZnQtb3V0IGZvbGQKICAgICAgeWhhdCA8LSBwcmVkaWN0KHlmaXQsIHhbSVtbYl1dLCBdLCB0eXBlID0gInJlc3BvbnNlIikgIyBwcmVkaWN0IHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIGR0aWxbSVtbYl1dXSA8LSAoZFtJW1tiXV1dIC0gZGhhdCkgIyByZWNvcmQgcmVzaWR1YWwgZm9yIHRoZSBsZWZ0LW91dCBmb2xkCiAgICAgIHl0aWxbSVtbYl1dXSA8LSAoeVtJW1tiXV1dIC0geWhhdCkgIyByZWNvcmQgcmVzaWRpYWwgZm9yIHRoZSBsZWZ0LW91dCBmb2xkCiAgICB9CiAgICBjYXQoYiwgIiAiKQogIH0KICByZml0IDwtIGxtKHl0aWwgfiBkdGlsKSAjIGVzdGltYXRlIHRoZSBtYWluIHBhcmFtZXRlciBieSByZWdyZXNzaW5nIG9uZSByZXNpZHVhbCBvbiB0aGUgb3RoZXIKICBjb2VmX2VzdCA8LSBjb2VmKHJmaXQpWzJdICMgZXh0cmFjdCBjb2VmZmljaWVudAogIHNlIDwtIHNxcnQodmNvdkhDKHJmaXQpWzIsIDJdKSAjIHJlY29yZCByb2J1c3Qgc3RhbmRhcmQgZXJyb3IKICBjYXQoc3ByaW50ZigiXG5jb2VmIChzZSkgPSAlZyAoJWcpXG4iLCBjb2VmX2VzdCwgc2UpKSAjIHByaW50aW5nIG91dHB1dAogIHJldHVybihsaXN0KGNvZWZfZXN0ID0gY29lZl9lc3QsIHNlID0gc2UsIGR0aWwgPSBkdGlsLCB5dGlsID0geXRpbCkpICMgc2F2ZSBvdXRwdXQgYW5kIHJlc2lkdWFscwp9CmBgYAoKCmBgYHtyfQpzdW1tYXJ5UExSIDwtIGZ1bmN0aW9uKHBvaW50LCBzdGRlcnIsIHJlc0QsIHJlc3ksIG5hbWUpIHsKICBkYXRhIDwtIGRhdGEuZnJhbWUoCiAgICBlc3RpbWF0ZSA9IHBvaW50LCAjIHBvaW50IGVzdGltYXRlCiAgICBzdGRlcnIgPSBzdGRlcnIsICMgc3RhbmRhcmQgZXJyb3IKICAgIGxvd2VyID0gcG9pbnQgLSAxLjk2ICogc3RkZXJyLCAjIGxvd2VyIGVuZCBvZiA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbAogICAgdXBwZXIgPSBwb2ludCArIDEuOTYgKiBzdGRlcnIsICMgdXBwZXIgZW5kIG9mIDk1JSBjb25maWRlbmNlIGludGVydmFsCiAgICBgcm1zZSB5YCA9IHNxcnQobWVhbihyZXN5XjIpKSwgIyBSTVNFIG9mIG1vZGVsIHRoYXQgcHJlZGljdHMgb3V0Y29tZSB5CiAgICBgcm1zZSBEYCA9IHNxcnQobWVhbihyZXNEXjIpKSwgIyBSTVNFIG9mIG1vZGVsIHRoYXQgcHJlZGljdHMgdHJlYXRtZW50IEQKICAgIGBhY2N1cmFjeSBEYCA9IG1lYW4oYWJzKHJlc0QpIDwgMC41KSAjIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBhY2N1cmFjeSBvZiBtb2RlbCBmb3IgRAogICkKICByb3duYW1lcyhkYXRhKSA8LSBuYW1lCiAgcmV0dXJuKGRhdGEpCn0KYGBgCgoqKkRvdWJsZSBMYXNzbyoqCgotIOWHpue9ruOBruaOqOWumuOBq+OBiuOBhOOBpuOAgemWouaVsOWei+OCkue3muW9oueiuueOh+ODouODh+ODq+OBq+OBmeOCi+WgtOWQiAoKYGBge3J9CiMgRE1MIHdpdGggTGFzc29DVgpzZXQuc2VlZCgxMjMpCmNhdChzcHJpbnRmKCJcbkRNTCB3aXRoIExhc3NvIENWIFxuIikpCgpkcmVnX2xhc3NvX2N2IDwtIGZ1bmN0aW9uKHgsIGQpIHsKICBjdi5nbG1uZXQoeCwgZCwgZmFtaWx5ID0gImdhdXNzaWFuIiwgYWxwaGEgPSAxLCBuZm9sZHMgPSA1KQp9CnlyZWdfbGFzc29fY3YgPC0gZnVuY3Rpb24oeCwgeSkgewogIGN2LmdsbW5ldCh4LCB5LCBmYW1pbHkgPSAiZ2F1c3NpYW4iLCBhbHBoYSA9IDEsIG5mb2xkcyA9IDUpCn0KCmRtbDJfcmVzdWx0cyA8LSBkbWwyX2Zvcl9wbG0oYXMubWF0cml4KFgpLCBELCB5LCBkcmVnX2xhc3NvX2N2LCB5cmVnX2xhc3NvX2N2LCBuZm9sZCA9IDUpCgpzdW1fbGFzc29fY3YgPC0gc3VtbWFyeVBMUihkbWwyX3Jlc3VsdHMkY29lZl9lc3QsIGRtbDJfcmVzdWx0cyRzZSwgZG1sMl9yZXN1bHRzJGR0aWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRtbDJfcmVzdWx0cyR5dGlsLCBuYW1lID0gIkxhc3NvQ1YiKQp0YWJsZXBsciA8LSBkYXRhLmZyYW1lKCkKdGFibGVwbHIgPC0gcmJpbmQoc3VtX2xhc3NvX2N2KQp0YWJsZXBscgpgYGAgCgoKYGBge3J9CiMgQmVjYXVzZSByZXNpZHVhbHMgYXJlIG91dHB1dCwgcmVjb25zdHJ1Y3QgZml0dGVkIHZhbHVlcyBmb3IgdXNlIGluIGVuc2VtYmxlCmRoYXRfbGFzc28gPC0gRCAtIGRtbDJfcmVzdWx0cyRkdGlsCnloYXRfbGFzc28gPC0geSAtIGRtbDJfcmVzdWx0cyR5dGlsCmBgYAoKLSDlh6bnva7jga7mjqjlrprjgavjgYrjgYTjgabjgIFMb2dpc3RpYyDlm57luLDjgpLnlKjjgYTjgovloLTlkIgKCmBgYHtyfQojIERNTCB3aXRoIExhc3NvL0xvZ2lzdGljCnNldC5zZWVkKDEyMykKY2F0KHNwcmludGYoIlxuRE1MIHdpdGggTGFzc28vTG9naXN0aWMgXG4iKSkKCmRyZWdfbG9naXN0aWNfY3YgPC0gZnVuY3Rpb24oeCwgZCkgewogIGN2LmdsbW5ldCh4LCBkLCBmYW1pbHkgPSAiYmlub21pYWwiLCBhbHBoYSA9IDAsIG5mb2xkcyA9IDUpCn0KeXJlZ19sYXNzb19jdiA8LSBmdW5jdGlvbih4LCB5KSB7CiAgY3YuZ2xtbmV0KHgsIHksIGZhbWlseSA9ICJnYXVzc2lhbiIsIGFscGhhID0gMSwgbmZvbGRzID0gNSkKfQoKZG1sMl9yZXN1bHRzIDwtIGRtbDJfZm9yX3BsbShhcy5tYXRyaXgoWCksIEQsIHksIGRyZWdfbG9naXN0aWNfY3YsIHlyZWdfbGFzc29fY3YsIG5mb2xkID0gNSkKc3VtX2xhc3NvX2xvZ2lzdGljX2N2IDwtIHN1bW1hcnlQTFIoZG1sMl9yZXN1bHRzJGNvZWZfZXN0LCBkbWwyX3Jlc3VsdHMkc2UsIGRtbDJfcmVzdWx0cyRkdGlsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkbWwyX3Jlc3VsdHMkeXRpbCwgbmFtZSA9ICJMYXNzb0NWL0xvZ2lzdGljQ1YiKQp0YWJsZXBsciA8LSByYmluZCh0YWJsZXBsciwgc3VtX2xhc3NvX2xvZ2lzdGljX2N2KQp0YWJsZXBscgpgYGAKCmBgYHtyfQojIEJlY2F1c2UgcmVzaWR1YWxzIGFyZSBvdXRwdXQsIHJlY29uc3RydWN0IGZpdHRlZCB2YWx1ZXMgZm9yIHVzZSBpbiBlbnNlbWJsZQpkaGF0X2xhc3NvX2xvZ2lzdGljIDwtIEQgLSBkbWwyX3Jlc3VsdHMkZHRpbAp5aGF0X2xhc3NvX2xvZ2lzdGljIDwtIHkgLSBkbWwyX3Jlc3VsdHMkeXRpbApgYGAKCgoqKlJhbmRvbSBGb3Jlc3QqKgoKYGBge3J9CiMgRE1MIHdpdGggUmFuZG9tIEZvcmVzdApzZXQuc2VlZCgxMjMpCmNhdChzcHJpbnRmKCJcbkRNTCB3aXRoIFJhbmRvbSBGb3Jlc3QgXG4iKSkKCmRyZWdfcmYgPC0gZnVuY3Rpb24oeCwgZCkgewogIHJhbmRvbUZvcmVzdCh4LCBkLCBudHJlZSA9IDEwMDAsIG5vZGVzaXplID0gMTApCn0gIyBNTCBtZXRob2Q9Rm9yZXN0CnlyZWdfcmYgPC0gZnVuY3Rpb24oeCwgeSkgewogIHJhbmRvbUZvcmVzdCh4LCB5LCBudHJlZSA9IDEwMDAsIG5vZGVzaXplID0gMTApCn0gIyBNTCBtZXRob2Q9Rm9yZXN0CgpkbWwyX3Jlc3VsdHMgPC0gZG1sMl9mb3JfcGxtKGFzLm1hdHJpeChYKSwgRCwgeSwgZHJlZ19yZiwgeXJlZ19yZiwgbmZvbGQgPSA1LCBtZXRob2QgPSAicmFuZG9tZm9yZXN0IikKc3VtX3JmIDwtIHN1bW1hcnlQTFIoZG1sMl9yZXN1bHRzJGNvZWZfZXN0LCBkbWwyX3Jlc3VsdHMkc2UsIGRtbDJfcmVzdWx0cyRkdGlsLAogICAgICAgICAgICAgICAgICAgICBkbWwyX3Jlc3VsdHMkeXRpbCwgbmFtZSA9ICJSYW5kb20gRm9yZXN0IikKdGFibGVwbHIgPC0gcmJpbmQodGFibGVwbHIsIHN1bV9yZikKdGFibGVwbHIKYGBgCgoKYGBge3J9CiMgQmVjYXVzZSByZXNpZHVhbHMgYXJlIG91dHB1dCwgcmVjb25zdHJ1Y3QgZml0dGVkIHZhbHVlcyBmb3IgdXNlIGluIGVuc2VtYmxlCmRoYXRfcmYgPC0gRCAtIGRtbDJfcmVzdWx0cyRkdGlsCmRoYXRfcmYgPC0geSAtIGRtbDJfcmVzdWx0cyR5dGlsCmBgYAoKKipEZWNpc2lvbiBUcmVlcyoqCgpgYGB7cn0KIyBETUwgd2l0aCBEZWNpc2lvbiBUcmVlcwpzZXQuc2VlZCgxMjMpCmNhdChzcHJpbnRmKCJcbkRNTCB3aXRoIERlY2lzaW9uIFRyZWVzIFxuIikpCgpkcmVnX3RyIDwtIGZ1bmN0aW9uKHgsIGQpIHsKICBycGFydChhcy5mb3JtdWxhKCJEfi4iKSwgY2JpbmQoZGF0YS5mcmFtZShEID0gZCksIHgpLCBtZXRob2QgPSAiY2xhc3MiLCBtaW5idWNrZXQgPSAxMCwgY3AgPSAwLjAwMSkKfQpkcmVnX3RyIDwtIGZ1bmN0aW9uKHgsIHkpIHsKICBycGFydChhcy5mb3JtdWxhKCJ5fi4iKSwgY2JpbmQoZGF0YS5mcmFtZSh5ID0geSksIHgpLCBtaW5idWNrZXQgPSAxMCwgY3AgPSAwLjAwMSkKfQoKIyBkZWNpc2lvbiB0cmVlIHRha2VzIGluIFggYXMgZGF0YWZyYW1lLCBub3QgbWF0cml4L2FycmF5CmRtbDJfcmVzdWx0cyA8LSBkbWwyX2Zvcl9wbG0oWCwgRCwgeSwgZHJlZ190ciwgZHJlZ190ciwgbmZvbGQgPSA1LCBtZXRob2QgPSAiZGVjaXNpb250cmVlcyIpCnN1bV90ciA8LSBzdW1tYXJ5UExSKGRtbDJfcmVzdWx0cyRjb2VmX2VzdCwgZG1sMl9yZXN1bHRzJHNlLCBkbWwyX3Jlc3VsdHMkZHRpbCwKICAgICAgICAgICAgICAgICAgICAgZG1sMl9yZXN1bHRzJHl0aWwsIG5hbWUgPSAiRGVjaXNpb24gVHJlZXMiKQp0YWJsZXBsciA8LSByYmluZCh0YWJsZXBsciwgc3VtX3RyKQp0YWJsZXBscgpgYGAKCmBgYHtyfQojIEJlY2F1c2UgcmVzaWR1YWxzIGFyZSBvdXRwdXQsIHJlY29uc3RydWN0IGZpdHRlZCB2YWx1ZXMgZm9yIHVzZSBpbiBlbnNlbWJsZQpkaGF0X3RyIDwtIEQgLSBkbWwyX3Jlc3VsdHMkZHRpbAp5aGF0X3RyIDwtIHkgLSBkbWwyX3Jlc3VsdHMkeXRpbApgYGAKCioqQm9vc3RlZCBUcmVlcyoqCgpgYGB7cn0KIyBETUwgd2l0aCBCb29zdGVkIFRyZWVzCnNldC5zZWVkKDEyMykKY2F0KHNwcmludGYoIlxuRE1MIHdpdGggQm9vc3RlZCBUcmVlcyBcbiIpKQoKIyBOQjogZWFybHkgc3RvcHBpbmcgY2Fubm90IGVhc2lseSBiZSBpbXBsZW1lbnRlZCB3aXRoIGdibQojIyBzZXQgbi50cmVlcyA9IGJlc3QsIHdoZXJlIGJlc3QgPC0gZ2JtLnBlcmYoZHJlZ19ib29zdCwgcGxvdC5pdCA9IEZBTFNFKQpkcmVnX2Jvb3N0IDwtIGZ1bmN0aW9uKHgsIGQpIHsKICBnYm0oYXMuZm9ybXVsYSgiRH4uIiksIGNiaW5kKGRhdGEuZnJhbWUoRCA9IGQpLCB4KSwgZGlzdHJpYnV0aW9uID0gImJlcm5vdWxsaSIsCiAgICAgIGludGVyYWN0aW9uLmRlcHRoID0gMiwgbi50cmVlcyA9IDEwMCwgc2hyaW5rYWdlID0gLjEpCn0KeXJlZ19ib29zdCA8LSBmdW5jdGlvbih4LCB5KSB7CiAgZ2JtKGFzLmZvcm11bGEoInl+LiIpLCBjYmluZChkYXRhLmZyYW1lKHkgPSB5KSwgeCksIGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsCiAgICAgIGludGVyYWN0aW9uLmRlcHRoID0gMiwgbi50cmVlcyA9IDEwMCwgc2hyaW5rYWdlID0gLjEpCn0KCiMgcGFzc2luZyB0aGVzZSB0aHJvdWdoIHJlZ3Jlc3Npb24gYXMgdHlwZT0icmVzcG9uc2UiLCBhbmQgRCBzaG91bGQgbm90IGJlIGZhY3RvciEKZG1sMl9yZXN1bHRzIDwtIGRtbDJfZm9yX3BsbShYLCBELCB5LCBkcmVnX2Jvb3N0LCB5cmVnX2Jvb3N0LCBuZm9sZCA9IDUsIG1ldGhvZCA9ICJib29zdGVkdHJlZXMiKQpzdW1fYm9vc3QgPC0gc3VtbWFyeVBMUihkbWwyX3Jlc3VsdHMkY29lZl9lc3QsIGRtbDJfcmVzdWx0cyRzZSwgZG1sMl9yZXN1bHRzJGR0aWwsCiAgICAgICAgICAgICAgICAgICAgICAgIGRtbDJfcmVzdWx0cyR5dGlsLCBuYW1lID0gIkJvb3N0ZWQgVHJlZXMiKQp0YWJsZXBsciA8LSByYmluZCh0YWJsZXBsciwgc3VtX2Jvb3N0KQp0YWJsZXBscgpgYGAKCmBgYHtyfQojIEJlY2F1c2UgcmVzaWR1YWxzIGFyZSBvdXRwdXQsIHJlY29uc3RydWN0IGZpdHRlZCB2YWx1ZXMgZm9yIHVzZSBpbiBlbnNlbWJsZQpkaGF0X2Jvb3N0IDwtIEQgLSBkbWwyX3Jlc3VsdHMkZHRpbAp5aGF0X2Jvb3N0IDwtIHkgLSBkbWwyX3Jlc3VsdHMkeXRpbApgYGAKCioqRW5zZW1ibGUqKgoKYGBge3J9CiMgQmVzdCBmaXQgaXMgYm9vc3RlZCB0cmVlcyBmb3IgYm90aCBEIGFuZCBZCgpzdW1fYmVzdCA8LSBzdW1tYXJ5UExSKGRtbDJfcmVzdWx0cyRjb2VmX2VzdCwgZG1sMl9yZXN1bHRzJHNlLCBkbWwyX3Jlc3VsdHMkZHRpbCwKICAgICAgICAgICAgICAgICAgICAgICBkbWwyX3Jlc3VsdHMkeXRpbCwgbmFtZSA9ICJCZXN0IikKdGFibGVwbHIgPC0gcmJpbmQodGFibGVwbHIsIHN1bV9iZXN0KQp0YWJsZXBscgpgYGAKCmBgYHtyfQojIExlYXN0IHNxdWFyZXMgbW9kZWwgYXZlcmFnZQoKbWFfZHRpbCA8LSBsbShEIH4gZGhhdF9sYXNzbyArIGRoYXRfbGFzc29fbG9naXN0aWMgKyBkaGF0X3JmICsgZGhhdF90ciArIGRoYXRfYm9vc3QpJHJlc2lkdWFscwptYV95dGlsIDwtIGxtKHkgfiB5aGF0X2xhc3NvICsgeWhhdF9sYXNzb19sb2dpc3RpYyArIGRoYXRfcmYgKyB5aGF0X3RyICsgeWhhdF9ib29zdCkkcmVzaWR1YWxzCgpyZml0IDwtIGxtKG1hX3l0aWwgfiBtYV9kdGlsKSAjIGVzdGltYXRlIHRoZSBtYWluIHBhcmFtZXRlciBieSByZWdyZXNzaW5nIG9uZSByZXNpZHVhbCBvbiB0aGUgb3RoZXIKY29lZl9lc3QgPC0gY29lZihyZml0KVsyXSAjIGV4dHJhY3QgY29lZmZpY2llbnQKc2UgPC0gc3FydCh2Y292SEMocmZpdClbMiwgMl0pICMgcmVjb3JkIHJvYnVzdCBzdGFuZGFyZCBlcnJvcgoKc3VtLm1hIDwtIHN1bW1hcnlQTFIoY29lZl9lc3QsIHNlLCBtYV9kdGlsLCBtYV95dGlsLCBuYW1lID0gIk1vZGVsIEF2ZXJhZ2UiKQp0YWJsZXBsciA8LSByYmluZCh0YWJsZXBsciwgc3VtLm1hKQp0YWJsZXBscgpgYGAKCgotLS0KCiMjIyMgKipJbnRlcmFjdGl2ZSBSZWdyZXNzaW9uIE1vZGVsIChJUk0pKioKCmBgYHtyfQpkbWwyX2Zvcl9pcm0gPC0gZnVuY3Rpb24oeCwgZCwgeSwgZHJlZywgeXJlZzAsIHlyZWcxLCB0cmltbWluZyA9IDAuMDEsIG5mb2xkID0gNSwgbWV0aG9kID0gInJlZ3Jlc3Npb24iKSB7CiAgeWhhdDAgPC0gcmVwKDAsIGxlbmd0aCh5KSkKICB5aGF0MSA8LSByZXAoMCwgbGVuZ3RoKHkpKQogIERoYXQgPC0gcmVwKDAsIGxlbmd0aChkKSkKCiAgbm9icyA8LSBucm93KHgpICMgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwogIGZvbGRpZCA8LSByZXAuaW50KDE6bmZvbGQsIHRpbWVzID0gY2VpbGluZyhub2JzIC8gbmZvbGQpKVtzYW1wbGUuaW50KG5vYnMpXSAjIGRlZmluZSBmb2xkcyBpbmRpY2VzCiAgSSA8LSBzcGxpdCgxOm5vYnMsIGZvbGRpZCkgIyBzcGxpdCBvYnNlcnZhdGlvbiBpbmRpY2VzIGludG8gZm9sZHMKICB5dGlsIDwtIGR0aWwgPC0gcmVwKE5BLCBub2JzKQoKICBjYXQoImZvbGQ6ICIpCiAgZm9yIChiIGluIHNlcV9hbG9uZyhJKSkgewogICAgIyBkZWZpbmUgaGVscGZ1bCB2YXJpYWJsZXMKICAgIERub3RiIDwtIGRbLUlbW2JdXV0KICAgIFhiIDwtIFhbSVtbYl1dLCBdCiAgICBYbm90YiA8LSBYWy1JW1tiXV0sIF0KCiAgICAjIHRyYWluaW5nIGRmcyBzdWJzZXR0ZWQgb24gdGhlIC1JW1tiXV0gZm9sZAogICAgWEQwIDwtIFhbLUlbW2JdXSwgXVtkWy1JW1tiXV1dID09IDBdCiAgICB5RDAgPC0geVstSVtbYl1dXVtkWy1JW1tiXV1dID09IDBdCiAgICBYRDEgPC0gWFstSVtbYl1dLCBdW2RbLUlbW2JdXV0gPT0gMV0KICAgIHlEMSA8LSB5Wy1JW1tiXV1dW2RbLUlbW2JdXV0gPT0gMV0KCiAgICBpZiAobWV0aG9kID09ICJyZWdyZXNzaW9uIikgewogICAgICB5Zml0MCA8LSB5cmVnMChhcy5tYXRyaXgoWEQwKSwgeUQwKQogICAgICB5Zml0MSA8LSB5cmVnMShhcy5tYXRyaXgoWEQxKSwgeUQxKQogICAgICB5aGF0MFtJW1tiXV1dIDwtIHByZWRpY3QoeWZpdDAsIGFzLm1hdHJpeChYYikpICMgZGVmYXVsdCBpcyB0eXBlID0gInJlc3BvbnNlIiBmb3IgZ2xtbmV0IGZhbWlseSBnYXVzc2lhbgogICAgICB5aGF0MVtJW1tiXV1dIDwtIHByZWRpY3QoeWZpdDEsIGFzLm1hdHJpeChYYikpCiAgICB9IGVsc2UgaWYgKG1ldGhvZCA9PSAicmFuZG9tZm9yZXN0IikgewogICAgICB5Zml0MCA8LSB5cmVnMChYRDAsIHlEMCkKICAgICAgeWZpdDEgPC0geXJlZzEoWEQxLCB5RDEpCiAgICAgIHloYXQwW0lbW2JdXV0gPC0gcHJlZGljdCh5Zml0MCwgWGIpICMgZGVmYXVsdCBpcyB0eXBlID0gInJlc3BvbnNlIiBmb3IgcmYKICAgICAgeWhhdDFbSVtbYl1dXSA8LSBwcmVkaWN0KHlmaXQxLCBYYikKICAgIH0gZWxzZSBpZiAobWV0aG9kID09ICJkZWNpc2lvbnRyZWVzIikgewogICAgICB5Zml0MCA8LSB5cmVnMChYRDAsIHlEMCkKICAgICAgeWZpdDEgPC0geXJlZzEoWEQxLCB5RDEpCiAgICAgIHloYXQwW0lbW2JdXV0gPC0gcHJlZGljdCh5Zml0MCwgWGIpICMgZGVmYXVsdCBpcyB0eXBlID0gInZlY3RvciIgZm9yIGRlY2lzaW9uCiAgICAgIHloYXQxW0lbW2JdXV0gPC0gcHJlZGljdCh5Zml0MSwgWGIpCiAgICB9IGVsc2UgaWYgKG1ldGhvZCA9PSAiYm9vc3RlZHRyZWVzIikgewogICAgICB5Zml0MCA8LSB5cmVnMChhcy5kYXRhLmZyYW1lKFhEMCksIHlEMCkKICAgICAgeWZpdDEgPC0geXJlZzEoYXMuZGF0YS5mcmFtZShYRDEpLCB5RDEpCiAgICAgIHloYXQwW0lbW2JdXV0gPC0gcHJlZGljdCh5Zml0MCwgWGIpICMgZGVmYXVsdCBpcyB0eXBlID0gInJlc3BvbnNlIiBmb3IgYm9vc3RlZAogICAgICB5aGF0MVtJW1tiXV1dIDwtIHByZWRpY3QoeWZpdDEsIFhiKQogICAgfQoKICAgICMgcHJvcGVuc2l0eSBzY29yZXM6CiAgICBpZiAobWV0aG9kID09ICJyZWdyZXNzaW9uIikgewogICAgICBkZml0X2IgPC0gZHJlZyhhcy5tYXRyaXgoWG5vdGIpLCBEbm90YikKICAgICAgZGhhdF9iIDwtIHByZWRpY3QoZGZpdF9iLCBhcy5tYXRyaXgoWGIpLCB0eXBlID0gInJlc3BvbnNlIikgIyBkZWZhdWx0IGlzIHR5cGU9ImxpbmsiIGZvciBmYW1pbHkgYmlub21pYWwhCiAgICB9IGVsc2UgaWYgKG1ldGhvZCA9PSAicmFuZG9tZm9yZXN0IikgewogICAgICBkZml0X2IgPC0gZHJlZyhYbm90YiwgYXMuZmFjdG9yKERub3RiKSkKICAgICAgZGhhdF9iIDwtIHByZWRpY3QoZGZpdF9iLCBYYiwgdHlwZSA9ICJwcm9iIilbLCAyXQogICAgfSBlbHNlIGlmIChtZXRob2QgPT0gImRlY2lzaW9udHJlZXMiKSB7CiAgICAgIGRmaXRfYiA8LSBkcmVnKFhub3RiLCBEbm90YikKICAgICAgZGhhdF9iIDwtIHByZWRpY3QoZGZpdF9iLCBYYilbLCAyXQogICAgfSBlbHNlIGlmIChtZXRob2QgPT0gImJvb3N0ZWR0cmVlcyIpIHsKICAgICAgZGZpdF9iIDwtIGRyZWcoYXMuZGF0YS5mcmFtZShYbm90YiksIERub3RiKQogICAgICBkaGF0X2IgPC0gcHJlZGljdChkZml0X2IsIFhiLCB0eXBlID0gInJlc3BvbnNlIikKICAgIH0KICAgIGRoYXRfYiA8LSBwbWF4KHBtaW4oZGhhdF9iLCAxIC0gdHJpbW1pbmcpLCB0cmltbWluZykgIyB0cmltbWluZyBzbyBzY29yZXMgYXJlIGJldHdlZW4gW3RyaW1taW5nLCAoMS10cmltbWluZyldCiAgICBEaGF0W0lbW2JdXV0gPC0gZGhhdF9iCgogICAgY2F0KGIsICIgIikKICB9CgogICMgUHJlZGljdGlvbiBvZiB0cmVhdG1lbnQgYW5kIG91dGNvbWUgZm9yIG9ic2VydmVkIGluc3RydW1lbnQKICB5aGF0IDwtIHloYXQwICogKDEgLSBEKSArIHloYXQxICogRAogICMgcmVzaWR1YWxzCiAgeXRpbCA8LSB5IC0geWhhdAogIGR0aWwgPC0gRCAtIERoYXQKICAjIGRvdWJseSByb2J1c3QgcXVhbnRpdHkgZm9yIGV2ZXJ5IHNhbXBsZQogIGRyaGF0IDwtIHloYXQxIC0geWhhdDAgKyAoeSAtIHloYXQpICogKEQgLyBEaGF0IC0gKDEgLSBEKSAvICgxIC0gRGhhdCkpCiAgY29lZl9lc3QgPC0gbWVhbihkcmhhdCkKICB2YXJpIDwtIHZhcihkcmhhdCkKICBzZSA8LSBzcXJ0KHZhcmkgLyBucm93KFgpKQogIGNhdCgicG9pbnQiLCBjb2VmX2VzdCkKICBjYXQoInNlIiwgc2UpCiAgcmV0dXJuKGxpc3QoY29lZl9lc3QgPSBjb2VmX2VzdCwgc2UgPSBzZSwgeXRpbCA9IHl0aWwsIGR0aWwgPSBkdGlsLCBkcmhhdCA9IGRyaGF0LAogICAgICAgICAgICAgIHloYXQwID0geWhhdDAsIHloYXQxID0geWhhdDEsIGRoYXQgPSBEaGF0LCB5aGF0ID0geWhhdCkpCn0KCnN1bW1hcnlJUk0gPC0gZnVuY3Rpb24oY29lZl9lc3QsIHNlLCB5dGlsLCBkdGlsLCBkcmhhdCwgbmFtZSkgewogIHN1bW1hcnlfZGF0YSA8LSBkYXRhLmZyYW1lKAogICAgZXN0aW1hdGUgPSBjb2VmX2VzdCwgIyBwb2ludCBlc3RpbWF0ZQogICAgc2UgPSBzZSwgIyBzdGFuZGFyZCBlcnJvcgogICAgbG93ZXIgPSBjb2VmX2VzdCAtIDEuOTYgKiBzZSwgIyBsb3dlciBlbmQgb2YgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwKICAgIHVwcGVyID0gY29lZl9lc3QgKyAxLjk2ICogc2UsICMgdXBwZXIgZW5kIG9mIDk1JSBjb25maWRlbmNlIGludGVydmFsCiAgICBybXNlX3kgPSBzcXJ0KG1lYW4oeXRpbF4yKSksICMgcmVzIG9mIG1vZGVsIHRoYXQgcHJlZGljdHMgb3V0Y29tZSB5CiAgICBybXNlX0QgPSBzcXJ0KG1lYW4oZHRpbF4yKSksICMgcmVzIG9mIG1vZGVsIHRoYXQgcHJlZGljdHMgdHJlYXRtZW50IEQKICAgIGFjY3VyYWN5X0QgPSBtZWFuKGFicyhkdGlsKSA8IDAuNSkgIyBiaW5hcnkgY2xhc3NpZmljYXRpb24gYWNjdXJhY3kgb2YgbW9kZWwgZm9yIEQKICApCiAgcm93Lm5hbWVzKHN1bW1hcnlfZGF0YSkgPC0gbmFtZQogIHJldHVybihzdW1tYXJ5X2RhdGEpCn0KYGBgCgoqKkRvdWJsZSBMYXNzbyoqCgpgYGB7cn0KIyBETUwgd2l0aCBMYXNzby9Mb2dpc3RpYwpzZXQuc2VlZCgxMjMpCmNhdChzcHJpbnRmKCJcbkRNTCB3aXRoIExhc3NvQ1YvTG9naXN0aWMgXG4iKSkKCmRyZWdfbGFzc29fY3YgPC0gZnVuY3Rpb24oeCwgZCkgewogIGN2LmdsbW5ldCh4LCBkLCBmYW1pbHkgPSAiYmlub21pYWwiLCBhbHBoYSA9IDAsIG5mb2xkcyA9IDUpCn0KeXJlZzBfbGFzc29fY3YgPC0gZnVuY3Rpb24oeCwgeSkgewogIGN2LmdsbW5ldCh4LCB5LCBmYW1pbHkgPSAiZ2F1c3NpYW4iLCBhbHBoYSA9IDEsIG5mb2xkcyA9IDUpCn0KeXJlZzFfbGFzc29fY3YgPC0gZnVuY3Rpb24oeCwgeSkgewogIGN2LmdsbW5ldCh4LCB5LCBmYW1pbHkgPSAiZ2F1c3NpYW4iLCBhbHBoYSA9IDEsIG5mb2xkcyA9IDUpCn0KCiMgbW9yZSBmb2xkcyBzZWVtcyB0byBoZWxwIHN0YWJpbGl6ZSBmaW5pdGUgc2FtcGxlIHBlcmZvcm1hbmNlCmRtbDJfcmVzdWx0cyA8LSBkbWwyX2Zvcl9pcm0oWCwgRCwgeSwgZHJlZ19sYXNzb19jdiwgeXJlZzBfbGFzc29fY3YsIHlyZWcxX2xhc3NvX2N2LCBuZm9sZCA9IDUpCnN1bV9sYXNzb19jdiA8LSBzdW1tYXJ5SVJNKGRtbDJfcmVzdWx0cyRjb2VmX2VzdCwgZG1sMl9yZXN1bHRzJHNlLCBkbWwyX3Jlc3VsdHMkeXRpbCwgZG1sMl9yZXN1bHRzJGR0aWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRtbDJfcmVzdWx0cyRkcmhhdCwgbmFtZSA9ICJMYXNzb0NWTG9naXN0aWMiKQp0YWJsZWlybSA8LSBkYXRhLmZyYW1lKCkKdGFibGVpcm0gPC0gcmJpbmQoc3VtX2xhc3NvX2N2KQp0YWJsZWlybQoKeWhhdDBfbGFzc28gPC0gZG1sMl9yZXN1bHRzJHloYXQwCnloYXQxX2xhc3NvIDwtIGRtbDJfcmVzdWx0cyR5aGF0MQpkaGF0X2xhc3NvIDwtIGRtbDJfcmVzdWx0cyRkaGF0CnloYXRfbGFzc28gPC0gZG1sMl9yZXN1bHRzJHloYXQKYGBgCgoqKlJhbmRvbSBGb3Jlc3QqKgoKYGBge3J9CiMgRE1MIHdpdGggUmFuZG9tIEZvcmVzdApzZXQuc2VlZCgxMjMpCmNhdChzcHJpbnRmKCJcbkRNTCB3aXRoIFJhbmRvbSBGb3Jlc3QgXG4iKSkKCmRyZWdfcmYgPC0gZnVuY3Rpb24oeCwgZCkgewogIHJhbmRvbUZvcmVzdCh4LCBkLCBudHJlZSA9IDEwMDAsIG5vZGVzaXplID0gMTApCn0gIyBNTCBtZXRob2Q9Rm9yZXN0CnlyZWcwX3JmIDwtIGZ1bmN0aW9uKHgsIHkpIHsKICByYW5kb21Gb3Jlc3QoeCwgeSwgbnRyZWUgPSAxMDAwLCBub2Rlc2l6ZSA9IDEwKQp9ICMgTUwgbWV0aG9kPUZvcmVzdAp5cmVnMV9yZiA8LSBmdW5jdGlvbih4LCB5KSB7CiAgcmFuZG9tRm9yZXN0KHgsIHksIG50cmVlID0gMTAwMCwgbm9kZXNpemUgPSAxMCkKfSAjIE1MIG1ldGhvZD1Gb3Jlc3QKCgpkbWwyX3Jlc3VsdHMgPC0gZG1sMl9mb3JfaXJtKGFzLm1hdHJpeChYKSwgRCwgeSwgZHJlZ19yZiwgeXJlZzBfcmYsIHlyZWcxX3JmLCBuZm9sZCA9IDUsIG1ldGhvZCA9ICJyYW5kb21mb3Jlc3QiKQpzdW1fcmYgPC0gc3VtbWFyeUlSTShkbWwyX3Jlc3VsdHMkY29lZl9lc3QsIGRtbDJfcmVzdWx0cyRzZSwgZG1sMl9yZXN1bHRzJHl0aWwsIGRtbDJfcmVzdWx0cyRkdGlsLAogICAgICAgICAgICAgICAgICAgICBkbWwyX3Jlc3VsdHMkZHJoYXQsIG5hbWUgPSAiUmFuZG9tIEZvcmVzdCIpCnRhYmxlaXJtIDwtIHJiaW5kKHRhYmxlaXJtLCBzdW1fcmYpCnRhYmxlaXJtCgp5aGF0MF9yZiA8LSBkbWwyX3Jlc3VsdHMkeWhhdDAKeWhhdDFfcmYgPC0gZG1sMl9yZXN1bHRzJHloYXQxCmRoYXRfcmYgPC0gZG1sMl9yZXN1bHRzJGRoYXQKZGhhdF9yZiA8LSBkbWwyX3Jlc3VsdHMkeWhhdApgYGAKCioqRGVjaXNpb24gVHJlZXMqKgoKYGBge3J9CiMgRE1MIHdpdGggRGVjaXNpb24gVHJlZXMKc2V0LnNlZWQoMTIzKQpjYXQoc3ByaW50ZigiXG5ETUwgd2l0aCBEZWNpc2lvbiBUcmVlcyBcbiIpKQoKZHJlZ190ciA8LSBmdW5jdGlvbih4LCBkKSB7CiAgcnBhcnQoYXMuZm9ybXVsYSgiRH4uIiksIGNiaW5kKGRhdGEuZnJhbWUoRCA9IGQpLCB4KSwgbWV0aG9kID0gImNsYXNzIiwgbWluYnVja2V0ID0gMTAsIGNwID0gMC4wMDEpCn0KeXJlZzBfdHIgPC0gZnVuY3Rpb24oeCwgeSkgewogIHJwYXJ0KGFzLmZvcm11bGEoInl+LiIpLCBjYmluZChkYXRhLmZyYW1lKHkgPSB5KSwgeCksIG1pbmJ1Y2tldCA9IDEwLCBjcCA9IDAuMDAxKQp9CnlyZWcxX3RyIDwtIGZ1bmN0aW9uKHgsIHkpIHsKICBycGFydChhcy5mb3JtdWxhKCJ5fi4iKSwgY2JpbmQoZGF0YS5mcmFtZSh5ID0geSksIHgpLCBtaW5idWNrZXQgPSAxMCwgY3AgPSAwLjAwMSkKfQoKZG1sMl9yZXN1bHRzIDwtIGRtbDJfZm9yX2lybShYLCBELCB5LCBkcmVnX3RyLCB5cmVnMF90ciwgeXJlZzFfdHIsIG5mb2xkID0gNSwgbWV0aG9kID0gImRlY2lzaW9udHJlZXMiKQpzdW1fdHIgPC0gc3VtbWFyeUlSTShkbWwyX3Jlc3VsdHMkY29lZl9lc3QsIGRtbDJfcmVzdWx0cyRzZSwgZG1sMl9yZXN1bHRzJHl0aWwsIGRtbDJfcmVzdWx0cyRkdGlsLAogICAgICAgICAgICAgICAgICAgICBkbWwyX3Jlc3VsdHMkZHJoYXQsIG5hbWUgPSAiRGVjaXNpb24gVHJlZXMiKQp0YWJsZWlybSA8LSByYmluZCh0YWJsZWlybSwgc3VtX3RyKQp0YWJsZWlybQoKeWhhdDBfdHIgPC0gZG1sMl9yZXN1bHRzJHloYXQwCnloYXQxX3RyIDwtIGRtbDJfcmVzdWx0cyR5aGF0MQpkaGF0X3RyIDwtIGRtbDJfcmVzdWx0cyRkaGF0CnloYXRfdHIgPC0gZG1sMl9yZXN1bHRzJHloYXQKYGBgCgoqKkJvb3N0ZWQgVHJlZXMqKgoKYGBge3J9CiMgRE1MIHdpdGggQm9vc3RlZCBUcmVlcwpzZXQuc2VlZCgxMjMpCmNhdChzcHJpbnRmKCJcbkRNTCB3aXRoIEJvb3N0ZWQgVHJlZXMgXG4iKSkKCiMgTkI6IGVhcmx5IHN0b3BwaW5nIGNhbm5vdCBlYXNpbHkgYmUgaW1wbGVtZW50ZWQgd2l0aCBnYm0KIyMgc2V0IG4udHJlZXMgPSBiZXN0LCB3aGVyZSBiZXN0IDwtIGdibS5wZXJmKGRyZWdfYm9vc3QsIHBsb3QuaXQgPSBGQUxTRSkKZHJlZ19ib29zdCA8LSBmdW5jdGlvbih4LCBkKSB7CiAgZ2JtKGFzLmZvcm11bGEoIkR+LiIpLCBjYmluZChkYXRhLmZyYW1lKEQgPSBkKSwgeCksIGRpc3RyaWJ1dGlvbiA9ICJiZXJub3VsbGkiLAogICAgICBpbnRlcmFjdGlvbi5kZXB0aCA9IDIsIG4udHJlZXMgPSAxMDAsIHNocmlua2FnZSA9IC4xKQp9CnlyZWcwX2Jvb3N0IDwtIGZ1bmN0aW9uKHgsIHkpIHsKICBnYm0oYXMuZm9ybXVsYSgieX4uIiksIGNiaW5kKGRhdGEuZnJhbWUoeSA9IHkpLCB4KSwgZGlzdHJpYnV0aW9uID0gImdhdXNzaWFuIiwKICAgICAgaW50ZXJhY3Rpb24uZGVwdGggPSAyLCBuLnRyZWVzID0gMTAwLCBzaHJpbmthZ2UgPSAuMSkKfQp5cmVnMV9ib29zdCA8LSBmdW5jdGlvbih4LCB5KSB7CiAgZ2JtKGFzLmZvcm11bGEoInl+LiIpLCBjYmluZChkYXRhLmZyYW1lKHkgPSB5KSwgeCksIGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsCiAgICAgIGludGVyYWN0aW9uLmRlcHRoID0gMiwgbi50cmVlcyA9IDEwMCwgc2hyaW5rYWdlID0gLjEpCn0KCiMgcGFzc2luZyB0aGVzZSB0aHJvdWdoIHJlZ3Jlc3Npb24gYXMgdHlwZT0icmVzcG9uc2UiLCBhbmQgRCBzaG91bGQgbm90IGJlIGZhY3RvciEKZG1sMl9yZXN1bHRzIDwtIGRtbDJfZm9yX2lybShYLCBELCB5LCBkcmVnX2Jvb3N0LCB5cmVnMF9ib29zdCwgeXJlZzFfYm9vc3QsIG5mb2xkID0gNSwgbWV0aG9kID0gImJvb3N0ZWR0cmVlcyIpCnN1bV9ib29zdCA8LSBzdW1tYXJ5SVJNKGRtbDJfcmVzdWx0cyRjb2VmX2VzdCwgZG1sMl9yZXN1bHRzJHNlLCBkbWwyX3Jlc3VsdHMkeXRpbCwgZG1sMl9yZXN1bHRzJGR0aWwsCiAgICAgICAgICAgICAgICAgICAgICAgIGRtbDJfcmVzdWx0cyRkcmhhdCwgbmFtZSA9ICJCb29zdGVkIFRyZWVzIikKdGFibGVpcm0gPC0gcmJpbmQodGFibGVpcm0sIHN1bV9ib29zdCkKdGFibGVpcm0KCnloYXQwX2Jvb3N0IDwtIGRtbDJfcmVzdWx0cyR5aGF0MAp5aGF0MV9ib29zdCA8LSBkbWwyX3Jlc3VsdHMkeWhhdDEKZGhhdF9ib29zdCA8LSBkbWwyX3Jlc3VsdHMkZGhhdAp5aGF0X2Jvb3N0IDwtIGRtbDJfcmVzdWx0cyR5aGF0CmBgYAoKKipFbnNlbWJsZSoqCgpgYGB7cn0KIyBFbnNlbWJsZXMKCiMgQmVzdAojIFdlJ2xsIGxvb2sgYXQgbW9kZWwgdGhhdCBkb2VzIGJlc3QgZm9yIFkgb3ZlcmFsbC4gQ291bGQgYWxzbyB1c2UgZGlmZmVyZW50IG1vZGVsIGZvciBZMCBhbmQgWTEKIyBIZXJlLCB0aGUgYmVzdCBwZXJmb3JtYW5jZSBmb3IgWSBpcyB0aGUgcmFuZG9tIGZvcmVzdCBhbmQgZm9yIEQgdGhlIGJvb3N0ZWQgdHJlZQoKIyByZXNpZHVhbHMKeXRpbCA8LSB5IC0gZGhhdF9yZgpkdGlsIDwtIEQgLSBkaGF0X2Jvb3N0CiMgZG91Ymx5IHJvYnVzdCBxdWFudGl0eSBmb3IgZXZlcnkgc2FtcGxlCmRyaGF0IDwtIHloYXQxX3JmIC0geWhhdDBfcmYgKyAoeSAtIGRoYXRfcmYpICogKEQgLyBkaGF0X2Jvb3N0IC0gKDEgLSBEKSAvICgxIC0gZGhhdF9ib29zdCkpCmNvZWZfZXN0IDwtIG1lYW4oZHJoYXQpCnZhcmkgPC0gdmFyKGRyaGF0KQpzZSA8LSBzcXJ0KHZhcmkgLyBucm93KFgpKQoKc3VtX2Jlc3QgPC0gc3VtbWFyeUlSTShjb2VmX2VzdCwgc2UsIHl0aWwsIGR0aWwsIGRyaGF0LCBuYW1lID0gIkJlc3QiKQp0YWJsZWlybSA8LSByYmluZCh0YWJsZWlybSwgc3VtX2Jlc3QpCnRhYmxlaXJtCmBgYAoKYGBge3J9CiMgTGVhc3Qgc3F1YXJlcyBtb2RlbCBhdmVyYWdlCiMgV2UnbGwgbG9vayBhdCB3ZWlnaHRzIHRoYXQgZG8gYmVzdCBqb2IgZm9yIFkgb3ZlcmFsbC4gQ291bGQgYWxzbyB1c2UgZGlmZmVyZW50IHdlaWdodHMgZm9yIFkwIGFuZCBZMQoKbWFfZHcgPC0gbG0oRCB+IGRoYXRfbGFzc28gKyBkaGF0X3JmICsgZGhhdF90ciArIGRoYXRfYm9vc3QpJGNvZWYKbWFfeXcgPC0gbG0oeSB+IHloYXRfbGFzc28gKyBkaGF0X3JmICsgeWhhdF90ciArIHloYXRfYm9vc3QpJGNvZWYKCkRoYXRzIDwtIGNiaW5kKGFzLm1hdHJpeChyZXAoMSwgbnJvdyhYKSkpLCBkaGF0X2xhc3NvLCBkaGF0X3JmLCBkaGF0X3RyLCBkaGF0X2Jvb3N0KQpZMHMgPC0gY2JpbmQoYXMubWF0cml4KHJlcCgxLCBucm93KFgpKSksIHloYXQwX2xhc3NvLCB5aGF0MF9yZiwgeWhhdDBfdHIsIHloYXQwX2Jvb3N0KQpZMXMgPC0gY2JpbmQoYXMubWF0cml4KHJlcCgxLCBucm93KFgpKSksIHloYXQxX2xhc3NvLCB5aGF0MV9yZiwgeWhhdDFfdHIsIHloYXQxX2Jvb3N0KQoKZGhhdCA8LSBEaGF0cyAlKiUgYXMubWF0cml4KG1hX2R3KQp5aGF0MCA8LSBZMHMgJSolIGFzLm1hdHJpeChtYV95dykKeWhhdDEgPC0gWTFzICUqJSBhcy5tYXRyaXgobWFfeXcpCgojIFByZWRpY3Rpb24gb2YgdHJlYXRtZW50IGFuZCBvdXRjb21lIGZvciBvYnNlcnZlZCBpbnN0cnVtZW50CnloYXQgPC0geWhhdDAgKiAoMSAtIEQpICsgeWhhdDEgKiBECiMgcmVzaWR1YWxzCnl0aWwgPC0geSAtIHloYXQKZHRpbCA8LSBEIC0gZGhhdAojIGRvdWJseSByb2J1c3QgcXVhbnRpdHkgZm9yIGV2ZXJ5IHNhbXBsZQpkcmhhdCA8LSB5aGF0MSAtIHloYXQwICsgKHkgLSB5aGF0KSAqIChEIC8gZGhhdCAtICgxIC0gRCkgLyAoMSAtIGRoYXQpKQpjb2VmX2VzdCA8LSBtZWFuKGRyaGF0KQp2YXJpIDwtIHZhcihkcmhhdCkKc2UgPC0gc3FydCh2YXJpIC8gbnJvdyhYKSkKCnN1bS5tYSA8LSBzdW1tYXJ5SVJNKGNvZWZfZXN0LCBzZSwgeXRpbCwgZHRpbCwgZHJoYXQsIG5hbWUgPSAiTW9kZWwgQXZlcmFnZSIpCnRhYmxlaXJtIDwtIHJiaW5kKHRhYmxlaXJtLCBzdW0ubWEpCnRhYmxlaXJtCmBgYAoKCi0tLQoKIyMjIERvdWJsZU1MIHBhY2thZ2Ug44Gr44KI44KL5o6o5a6aCgotIOS7iuOBvuOBp+OBr+iHquOCiemWouaVsOOCkuabuOOBhOOBpuOBhOOBn+OBjOOAgWBFY29uTUxgIChQeXRob24pIOOChCBgRG91YmxlTUxgIChSICYgUHl0aG9uKSDjgarjganjga7jg5Hjg4PjgrHjg7zjgrjjgYzliKnnlKjlj6/og73jgafjgYLjgosKICAtIFtEb3VibGVNTCDjg5Hjg4PjgrHjg7zjgrjjga7jgrXjgqTjg4hdKGh0dHBzOi8vZG9jcy5kb3VibGVtbC5vcmcvc3RhYmxlL2luZGV4Lmh0bWwpCiAgLSBbbWwzIOODkeODg+OCseODvOOCuF0oaHR0cHM6Ly9tbHIzYm9vay5tbHItb3JnLmNvbSkg44KS5YaF6YOo44Gn5YuV44GL44GX44Gm44GE44KLCgoqKuODh+ODvOOCv+S9nOaIkCoqCgpgYGB7cn0KIyBDb25zdHJ1Y3RpbmcgdGhlIGRhdGEgKGFzIERvdWJsZU1MRGF0YSkKZm9ybXVsYV9mbGV4IDwtIHBhc3RlKCJuZXRfdGZhIH4gZTQwMSArIHBvbHkoYWdlLCA2LCByYXc9VFJVRSkgKyBwb2x5KGluYywgOCwgcmF3PVRSVUUpICIsCiAgICAgICAgICAgICAgICAgICAgICAiKyBwb2x5KGVkdWMsIDQsIHJhdz1UUlVFKSArIHBvbHkoZnNpemUsIDIsIHJhdz1UUlVFKSArIG1hcnIgKyB0d29lYXJuICsgZGIgKyBwaXJhICsgaG93biIpCm1vZGVsX2ZsZXggPC0gYXMuZGF0YS50YWJsZShtb2RlbC5mcmFtZShmb3JtdWxhX2ZsZXgsIHBlbnNpb24pKQp4X2NvbHMgPC0gY29sbmFtZXMobW9kZWxfZmxleClbLWMoMSwgMildCmRhdGFfbWwgPC0gRG91YmxlTUxEYXRhJG5ldyhtb2RlbF9mbGV4LCB5X2NvbCA9ICJuZXRfdGZhIiwgZF9jb2xzID0gImU0MDEiLCB4X2NvbHMgPSB4X2NvbHMpCgpwIDwtIGRpbShtb2RlbF9mbGV4KVsyXSAtIDIKcApgYGAKCgojIyMjICoqUGFydGlhbGwgTGluZWFyIE1vZGVsIChQTE0pKioKCmBgYHtyfQojIEVzdGltYXRpbmcgdGhlIFBMUgpsZ3I6OmdldF9sb2dnZXIoIm1scjMiKSRzZXRfdGhyZXNob2xkKCJ3YXJuIikKbGFzc28gPC0gbHJuKCJyZWdyLmN2X2dsbW5ldCIsIG5mb2xkcyA9IDUsIHMgPSAibGFtYmRhLm1pbiIpCmxhc3NvX2NsYXNzIDwtIGxybigiY2xhc3NpZi5jdl9nbG1uZXQiLCBuZm9sZHMgPSA1LCBzID0gImxhbWJkYS5taW4iKQoKZG1sX3BsciA8LSBEb3VibGVNTFBMUiRuZXcoZGF0YV9tbCwgbWxfbCA9IGxhc3NvLCBtbF9tID0gbGFzc29fY2xhc3MsIG5fZm9sZHMgPSA1KQpkbWxfcGxyJGZpdChzdG9yZV9wcmVkaWN0aW9ucyA9IFRSVUUpCmRtbF9wbHIkc3VtbWFyeSgpCmxhc3NvX3BsciA8LSBkbWxfcGxyJGNvZWYKbGFzc29fc3RkX3BsciA8LSBkbWxfcGxyJHNlCmBgYAoKYGBge3J9CmRtbF9wbHIkcGFyYW1zX25hbWVzKCkKZ19oYXQgPC0gYXMubWF0cml4KGRtbF9wbHIkcHJlZGljdGlvbnMkbWxfbCkgIyBwcmVkaWN0aW9ucyBvZiBnX28KbV9oYXQgPC0gYXMubWF0cml4KGRtbF9wbHIkcHJlZGljdGlvbnMkbWxfbSkgIyBwcmVkaWN0aW9ucyBvZiBtX28KYGBgCgpgYGB7cn0KIyBjcm9zcy1maXR0ZWQgUk1TRTogb3V0Y29tZQp5IDwtIGFzLm1hdHJpeChwZW5zaW9uJG5ldF90ZmEpICMgdHJ1ZSBvYnNlcnZhdGlvbnMKdGhldGEgPC0gYXMubnVtZXJpYyhkbWxfcGxyJGNvZWYpICMgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQKZCA8LSBhcy5tYXRyaXgocGVuc2lvbiRlNDAxKQpwcmVkaWN0aW9uc195IDwtIGFzLm1hdHJpeChkICogdGhldGEpICsgZ19oYXQgIyBwcmVkaWN0aW9ucyBmb3IgeQpsYXNzb195X3Jtc2UgPC0gc3FydChtZWFuKCh5IC0gcHJlZGljdGlvbnNfeSleMikpCmxhc3NvX3lfcm1zZQpgYGAKCioqTGFzc28qKgoKYGBge3J9CiMgY3Jvc3MtZml0dGVkIFJNU0U6IHRyZWF0bWVudApkIDwtIGFzLm1hdHJpeChwZW5zaW9uJGU0MDEpCmxhc3NvX2Rfcm1zZSA8LSBzcXJ0KG1lYW4oKGQgLSBtX2hhdCleMikpCmxhc3NvX2Rfcm1zZQoKIyBjcm9zcy1maXR0ZWQgY2U6IHRyZWF0bWVudAptZWFuKGlmZWxzZShtX2hhdCA+IDAuNSwgMSwgMCkgIT0gZCkKYGBgCgoqKlJhbmRvbSBGb3Jlc3QqKgoKYGBge3J9CiMgUmFuZG9tIEZvcmVzdApsZ3I6OmdldF9sb2dnZXIoIm1scjMiKSRzZXRfdGhyZXNob2xkKCJ3YXJuIikKcmFuZG9tRm9yZXN0IDwtIGxybigicmVnci5yYW5nZXIiKQpyYW5kb21fZm9yZXN0X2NsYXNzIDwtIGxybigiY2xhc3NpZi5yYW5nZXIiKQoKZG1sX3BsciA8LSBEb3VibGVNTFBMUiRuZXcoZGF0YV9tbCwgbWxfbCA9IHJhbmRvbUZvcmVzdCwgbWxfbSA9IHJhbmRvbV9mb3Jlc3RfY2xhc3MsIG5fZm9sZHMgPSA1KQpkbWxfcGxyJGZpdChzdG9yZV9wcmVkaWN0aW9ucyA9IFRSVUUpICMgc2V0IHN0b3JlX3ByZWRpY3Rpb25zPVRSVUUgdG8gZXZhbHVhdGUgdGhlIG1vZGVsCmRtbF9wbHIkc3VtbWFyeSgpCmZvcmVzdF9wbHIgPC0gZG1sX3BsciRjb2VmCmZvcmVzdF9zdGRfcGxyIDwtIGRtbF9wbHIkc2UKYGBgCgoKYGBge3J9CiMgRXZhbHVhdGlvbiBwcmVkaWN0aW9ucwpnX2hhdCA8LSBhcy5tYXRyaXgoZG1sX3BsciRwcmVkaWN0aW9ucyRtbF9sKSAjIHByZWRpY3Rpb25zIG9mIGdfbwptX2hhdCA8LSBhcy5tYXRyaXgoZG1sX3BsciRwcmVkaWN0aW9ucyRtbF9tKSAjIHByZWRpY3Rpb25zIG9mIG1fbwp0aGV0YSA8LSBhcy5udW1lcmljKGRtbF9wbHIkY29lZikgIyBlc3RpbWF0ZWQgcmVncmVzc2lvbiBjb2VmZmljaWVudApwcmVkaWN0aW9uc195IDwtIGFzLm1hdHJpeChkICogdGhldGEpICsgZ19oYXQgIyBwcmVkaWN0aW9ucyBmb3IgeQpmb3Jlc3RfeV9ybXNlIDwtIHNxcnQobWVhbigoeSAtIHByZWRpY3Rpb25zX3kpXjIpKQpmb3Jlc3RfeV9ybXNlCgojIGNyb3NzLWZpdHRlZCBSTVNFOiB0cmVhdG1lbnQKZm9yZXN0X2Rfcm1zZSA8LSBzcXJ0KG1lYW4oKGQgLSBtX2hhdCleMikpCmZvcmVzdF9kX3Jtc2UKCiMgY3Jvc3MtZml0dGVkIGNlOiB0cmVhdG1lbnQKbWVhbihpZmVsc2UobV9oYXQgPiAwLjUsIDEsIDApICE9IGQpCmBgYAoKCioqRGVjaXNpb24gVHJlZXMqKgoKYGBge3J9CiMgVHJlZXMKbGdyOjpnZXRfbG9nZ2VyKCJtbHIzIikkc2V0X3RocmVzaG9sZCgid2FybiIpCgp0cmVlcyA8LSBscm4oInJlZ3IucnBhcnQiKQp0cmVlc19jbGFzcyA8LSBscm4oImNsYXNzaWYucnBhcnQiKQoKZG1sX3BsciA8LSBEb3VibGVNTFBMUiRuZXcoZGF0YV9tbCwgbWxfbCA9IHRyZWVzLCBtbF9tID0gdHJlZXNfY2xhc3MsIG5fZm9sZHMgPSA1KQpkbWxfcGxyJGZpdChzdG9yZV9wcmVkaWN0aW9ucyA9IFRSVUUpCmRtbF9wbHIkc3VtbWFyeSgpCnRyZWVfcGxyIDwtIGRtbF9wbHIkY29lZgp0cmVlX3N0ZF9wbHIgPC0gZG1sX3BsciRzZQoKIyBFdmFsdWF0aW9uIHByZWRpY3Rpb25zCmdfaGF0IDwtIGFzLm1hdHJpeChkbWxfcGxyJHByZWRpY3Rpb25zJG1sX2wpICMgcHJlZGljdGlvbnMgb2YgZ19vCm1faGF0IDwtIGFzLm1hdHJpeChkbWxfcGxyJHByZWRpY3Rpb25zJG1sX20pICMgcHJlZGljdGlvbnMgb2YgbV9vCnRoZXRhIDwtIGFzLm51bWVyaWMoZG1sX3BsciRjb2VmKSAjIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50CnByZWRpY3Rpb25zX3kgPC0gYXMubWF0cml4KGQgKiB0aGV0YSkgKyBnX2hhdCAjIHByZWRpY3Rpb25zIGZvciB5CnRyZWVfeV9ybXNlIDwtIHNxcnQobWVhbigoeSAtIHByZWRpY3Rpb25zX3kpXjIpKQp0cmVlX3lfcm1zZQoKIyBjcm9zcy1maXR0ZWQgUk1TRTogdHJlYXRtZW50CnRyZWVfZF9ybXNlIDwtIHNxcnQobWVhbigoZCAtIG1faGF0KV4yKSkKdHJlZV9kX3Jtc2UKCiMgY3Jvc3MtZml0dGVkIGNlOiB0cmVhdG1lbnQKbWVhbihpZmVsc2UobV9oYXQgPiAwLjUsIDEsIDApICE9IGQpCmBgYAoKCioqQm9vc3RlZCBUcmVlcyoqCgpgYGB7cn0KIyBCb29zdGluZwpib29zdCA8LSBscm4oInJlZ3IuZ2xtYm9vc3QiKQpib29zdF9jbGFzcyA8LSBscm4oImNsYXNzaWYuZ2xtYm9vc3QiKQoKZG1sX3BsciA8LSBEb3VibGVNTFBMUiRuZXcoZGF0YV9tbCwgbWxfbCA9IGJvb3N0LCBtbF9tID0gYm9vc3RfY2xhc3MsIG5fZm9sZHMgPSA1KQpkbWxfcGxyJGZpdChzdG9yZV9wcmVkaWN0aW9ucyA9IFRSVUUpCmRtbF9wbHIkc3VtbWFyeSgpCmJvb3N0X3BsciA8LSBkbWxfcGxyJGNvZWYKYm9vc3Rfc3RkX3BsciA8LSBkbWxfcGxyJHNlCgojIEV2YWx1YXRpb24gcHJlZGljdGlvbnMKZ19oYXQgPC0gYXMubWF0cml4KGRtbF9wbHIkcHJlZGljdGlvbnMkbWxfbCkgIyBwcmVkaWN0aW9ucyBvZiBnX28KbV9oYXQgPC0gYXMubWF0cml4KGRtbF9wbHIkcHJlZGljdGlvbnMkbWxfbSkgIyBwcmVkaWN0aW9ucyBvZiBtX28KdGhldGEgPC0gYXMubnVtZXJpYyhkbWxfcGxyJGNvZWYpICMgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQKcHJlZGljdGlvbnNfeSA8LSBhcy5tYXRyaXgoZCAqIHRoZXRhKSArIGdfaGF0ICMgcHJlZGljdGlvbnMgZm9yIHkKYm9vc3RfeV9ybXNlIDwtIHNxcnQobWVhbigoeSAtIHByZWRpY3Rpb25zX3kpXjIpKQpib29zdF95X3Jtc2UKCiMgY3Jvc3MtZml0dGVkIFJNU0U6IHRyZWF0bWVudApib29zdF9kX3Jtc2UgPC0gc3FydChtZWFuKChkIC0gbV9oYXQpXjIpKQpib29zdF9kX3Jtc2UKCiMgY3Jvc3MtZml0dGVkIGNlOiB0cmVhdG1lbnQKbWVhbihpZmVsc2UobV9oYXQgPiAwLjUsIDEsIDApICE9IGQpCmBgYAoKKirjgb7jgajjgoEqKgoKYGBge3J9CnRhYmxlIDwtIG1hdHJpeCgwLCA0LCA0KQp0YWJsZVsxLCAxOjRdIDwtIGMobGFzc29fcGxyLCBmb3Jlc3RfcGxyLCB0cmVlX3BsciwgYm9vc3RfcGxyKQp0YWJsZVsyLCAxOjRdIDwtIGMobGFzc29fc3RkX3BsciwgZm9yZXN0X3N0ZF9wbHIsIHRyZWVfc3RkX3BsciwgYm9vc3Rfc3RkX3BscikKdGFibGVbMywgMTo0XSA8LSBjKGxhc3NvX3lfcm1zZSwgZm9yZXN0X3lfcm1zZSwgdHJlZV95X3Jtc2UsIGJvb3N0X3lfcm1zZSkKdGFibGVbNCwgMTo0XSA8LSBjKGxhc3NvX2Rfcm1zZSwgZm9yZXN0X2Rfcm1zZSwgdHJlZV9kX3Jtc2UsIGJvb3N0X2Rfcm1zZSkKcm93bmFtZXModGFibGUpIDwtIGMoIkVzdGltYXRlIiwgIlN0ZC5FcnJvciIsICJSTVNFIFkiLCAiUk1TRSBEIikKY29sbmFtZXModGFibGUpIDwtIGMoIkxhc3NvIiwgIlJhbmRvbSBGb3Jlc3QiLCAiVHJlZXMiLCAiQm9vc3RpbmciKQp0YWIgPC0geHRhYmxlKHRhYmxlLCBkaWdpdHMgPSAyKQp0YWIKCmxhc3NvX3BscgpgYGAKLS0tCgojIyMjICoqSW50ZXJhY3RpdmUgUmVncmVzc2lvbiBNb2RlbCAoSVJNKSoqCgpgYGB7cn0KbGdyOjpnZXRfbG9nZ2VyKCJtbHIzIikkc2V0X3RocmVzaG9sZCgid2FybiIpCmRtbF9pcm0gPC0gRG91YmxlTUxJUk0kbmV3KGRhdGFfbWwsCiAgbWxfZyA9IGxhc3NvLAogIG1sX20gPSBsYXNzb19jbGFzcywKICB0cmltbWluZ190aHJlc2hvbGQgPSAwLjAxLCBuX2ZvbGRzID0gNQopCmRtbF9pcm0kZml0KHN0b3JlX3ByZWRpY3Rpb25zID0gVFJVRSkKZG1sX2lybSRzdW1tYXJ5KCkKbGFzc29faXJtIDwtIGRtbF9pcm0kY29lZgpsYXNzb19zdGRfaXJtIDwtIGRtbF9pcm0kc2UKCgojIHByZWRpY3Rpb25zCmRtbF9pcm0kcGFyYW1zX25hbWVzKCkKZzBfaGF0IDwtIGFzLm1hdHJpeChkbWxfaXJtJHByZWRpY3Rpb25zJG1sX2cwKSAjIHByZWRpY3Rpb25zIG9mIGdfMChEPTAsIFgpCmcxX2hhdCA8LSBhcy5tYXRyaXgoZG1sX2lybSRwcmVkaWN0aW9ucyRtbF9nMSkgIyBwcmVkaWN0aW9ucyBvZiBnXzAoRD0xLCBYKQpnX2hhdCA8LSBkICogZzFfaGF0ICsgKDEgLSBkKSAqIGcwX2hhdCAjIHByZWRpY3Rpb25zIG9mIGdfMAptX2hhdCA8LSBhcy5tYXRyaXgoZG1sX2lybSRwcmVkaWN0aW9ucyRtbF9tKSAjIHByZWRpY3Rpb25zIG9mIG1fbwpgYGAKCmBgYHtyfQojIGNyb3NzLWZpdHRlZCBSTVNFOiBvdXRjb21lCnkgPC0gYXMubWF0cml4KHBlbnNpb24kbmV0X3RmYSkgIyB0cnVlIG9ic2VydmF0aW9ucwpkIDwtIGFzLm1hdHJpeChwZW5zaW9uJGU0MDEpCmxhc3NvX3lfaXJtIDwtIHNxcnQobWVhbigoeSAtIGdfaGF0KV4yKSkKbGFzc29feV9pcm0KCiMgY3Jvc3MtZml0dGVkIFJNU0U6IHRyZWF0bWVudApsYXNzb19kX2lybSA8LSBzcXJ0KG1lYW4oKGQgLSBtX2hhdCleMikpCmxhc3NvX2RfaXJtCgojIGNyb3NzLWZpdHRlZCBjZTogdHJlYXRtZW50Cm1lYW4oaWZlbHNlKG1faGF0ID4gMC41LCAxLCAwKSAhPSBkKQpgYGAKCgoKYGBge3J9CiMjIyMjIGZvcmVzdCAjIyMjIwoKZG1sX2lybSA8LSBEb3VibGVNTElSTSRuZXcoZGF0YV9tbCwKICBtbF9nID0gcmFuZG9tRm9yZXN0LAogIG1sX20gPSByYW5kb21fZm9yZXN0X2NsYXNzLAogIHRyaW1taW5nX3RocmVzaG9sZCA9IDAuMDEsIG5fZm9sZHMgPSA1CikKZG1sX2lybSRmaXQoc3RvcmVfcHJlZGljdGlvbnMgPSBUUlVFKQpkbWxfaXJtJHN1bW1hcnkoKQpmb3Jlc3RfaXJtIDwtIGRtbF9pcm0kY29lZgpmb3Jlc3Rfc3RkX2lybSA8LSBkbWxfcGxyJHNlCgojIHByZWRpY3Rpb25zCmcwX2hhdCA8LSBhcy5tYXRyaXgoZG1sX2lybSRwcmVkaWN0aW9ucyRtbF9nMCkgIyBwcmVkaWN0aW9ucyBvZiBnXzAoRD0wLCBYKQpnMV9oYXQgPC0gYXMubWF0cml4KGRtbF9pcm0kcHJlZGljdGlvbnMkbWxfZzEpICMgcHJlZGljdGlvbnMgb2YgZ18wKEQ9MSwgWCkKZ19oYXQgPC0gZCAqIGcxX2hhdCArICgxIC0gZCkgKiBnMF9oYXQgIyBwcmVkaWN0aW9ucyBvZiBnXzAKbV9oYXQgPC0gYXMubWF0cml4KGRtbF9pcm0kcHJlZGljdGlvbnMkbWxfbSkgIyBwcmVkaWN0aW9ucyBvZiBtXzAKCiMgY3Jvc3MtZml0dGVkIFJNU0U6IG91dGNvbWUKeSA8LSBhcy5tYXRyaXgocGVuc2lvbiRuZXRfdGZhKSAjIHRydWUgb2JzZXJ2YXRpb25zCmQgPC0gYXMubWF0cml4KHBlbnNpb24kZTQwMSkKZm9yZXN0X3lfaXJtIDwtIHNxcnQobWVhbigoeSAtIGdfaGF0KV4yKSkKZm9yZXN0X3lfaXJtCgojIGNyb3NzLWZpdHRlZCBSTVNFOiB0cmVhdG1lbnQKZm9yZXN0X2RfaXJtIDwtIHNxcnQobWVhbigoZCAtIG1faGF0KV4yKSkKZm9yZXN0X2RfaXJtCgojIGNyb3NzLWZpdHRlZCBjZTogdHJlYXRtZW50Cm1lYW4oaWZlbHNlKG1faGF0ID4gMC41LCAxLCAwKSAhPSBkKQoKIyMjIyMgdHJlZXMgIyMjIyMKCmRtbF9pcm0gPC0gRG91YmxlTUxJUk0kbmV3KGRhdGFfbWwsCiAgbWxfZyA9IHRyZWVzLCBtbF9tID0gdHJlZXNfY2xhc3MsCiAgdHJpbW1pbmdfdGhyZXNob2xkID0gMC4wMSwgbl9mb2xkcyA9IDUKKQpkbWxfaXJtJGZpdChzdG9yZV9wcmVkaWN0aW9ucyA9IFRSVUUpCmRtbF9pcm0kc3VtbWFyeSgpCnRyZWVfaXJtIDwtIGRtbF9pcm0kY29lZgp0cmVlX3N0ZF9pcm0gPC0gZG1sX2lybSRzZQoKIyBwcmVkaWN0aW9ucwpnMF9oYXQgPC0gYXMubWF0cml4KGRtbF9pcm0kcHJlZGljdGlvbnMkbWxfZzApICMgcHJlZGljdGlvbnMgb2YgZ18wKEQ9MCwgWCkKZzFfaGF0IDwtIGFzLm1hdHJpeChkbWxfaXJtJHByZWRpY3Rpb25zJG1sX2cxKSAjIHByZWRpY3Rpb25zIG9mIGdfMChEPTEsIFgpCmdfaGF0IDwtIGQgKiBnMV9oYXQgKyAoMSAtIGQpICogZzBfaGF0ICMgcHJlZGljdGlvbnMgb2YgZ18wCm1faGF0IDwtIGFzLm1hdHJpeChkbWxfaXJtJHByZWRpY3Rpb25zJG1sX20pICMgcHJlZGljdGlvbnMgb2YgbV9vCgojIGNyb3NzLWZpdHRlZCBSTVNFOiBvdXRjb21lCnkgPC0gYXMubWF0cml4KHBlbnNpb24kbmV0X3RmYSkgIyB0cnVlIG9ic2VydmF0aW9ucwpkIDwtIGFzLm1hdHJpeChwZW5zaW9uJGU0MDEpCnRyZWVfeV9pcm0gPC0gc3FydChtZWFuKCh5IC0gZ19oYXQpXjIpKQp0cmVlX3lfaXJtCgojIGNyb3NzLWZpdHRlZCBSTVNFOiB0cmVhdG1lbnQKdHJlZV9kX2lybSA8LSBzcXJ0KG1lYW4oKGQgLSBtX2hhdCleMikpCnRyZWVfZF9pcm0KCiMgY3Jvc3MtZml0dGVkIGNlOiB0cmVhdG1lbnQKbWVhbihpZmVsc2UobV9oYXQgPiAwLjUsIDEsIDApICE9IGQpCgoKIyMjIyMgYm9vc3RpbmcgIyMjIyMKCmRtbF9pcm0gPC0gRG91YmxlTUxJUk0kbmV3KGRhdGFfbWwsCiAgbWxfZyA9IGJvb3N0LCBtbF9tID0gYm9vc3RfY2xhc3MsCiAgdHJpbW1pbmdfdGhyZXNob2xkID0gMC4wMSwgbl9mb2xkcyA9IDUKKQpkbWxfaXJtJGZpdChzdG9yZV9wcmVkaWN0aW9ucyA9IFRSVUUpCmRtbF9pcm0kc3VtbWFyeSgpCmJvb3N0X2lybSA8LSBkbWxfaXJtJGNvZWYKYm9vc3Rfc3RkX2lybSA8LSBkbWxfaXJtJHNlCgojIHByZWRpY3Rpb25zCmcwX2hhdCA8LSBhcy5tYXRyaXgoZG1sX2lybSRwcmVkaWN0aW9ucyRtbF9nMCkgIyBwcmVkaWN0aW9ucyBvZiBnXzAoRD0wLCBYKQpnMV9oYXQgPC0gYXMubWF0cml4KGRtbF9pcm0kcHJlZGljdGlvbnMkbWxfZzEpICMgcHJlZGljdGlvbnMgb2YgZ18wKEQ9MSwgWCkKZ19oYXQgPC0gZCAqIGcxX2hhdCArICgxIC0gZCkgKiBnMF9oYXQgIyBwcmVkaWN0aW9ucyBvZiBnXzAKbV9oYXQgPC0gYXMubWF0cml4KGRtbF9pcm0kcHJlZGljdGlvbnMkbWxfbSkgIyBwcmVkaWN0aW9ucyBvZiBtX28KCiMgY3Jvc3MtZml0dGVkIFJNU0U6IG91dGNvbWUKeSA8LSBhcy5tYXRyaXgocGVuc2lvbiRuZXRfdGZhKSAjIHRydWUgb2JzZXJ2YXRpb25zCmQgPC0gYXMubWF0cml4KHBlbnNpb24kZTQwMSkKYm9vc3RfeV9pcm0gPC0gc3FydChtZWFuKCh5IC0gZ19oYXQpXjIpKQpib29zdF95X2lybQoKIyBjcm9zcy1maXR0ZWQgUk1TRTogdHJlYXRtZW50CmJvb3N0X2RfaXJtIDwtIHNxcnQobWVhbigoZCAtIG1faGF0KV4yKSkKYm9vc3RfZF9pcm0KCiMgY3Jvc3MtZml0dGVkIGNlOiB0cmVhdG1lbnQKbWVhbihpZmVsc2UobV9oYXQgPiAwLjUsIDEsIDApICE9IGQpCmBgYAoKCioq44G+44Go44KBKioKCmBgYHtyfQp0YWJsZSA8LSBtYXRyaXgoMCwgNCwgNCkKdGFibGVbMSwgMTo0XSA8LSBjKGxhc3NvX2lybSwgZm9yZXN0X2lybSwgdHJlZV9pcm0sIGJvb3N0X2lybSkKdGFibGVbMiwgMTo0XSA8LSBjKGxhc3NvX3N0ZF9pcm0sIGZvcmVzdF9zdGRfaXJtLCB0cmVlX3N0ZF9pcm0sIGJvb3N0X3N0ZF9pcm0pCnRhYmxlWzMsIDE6NF0gPC0gYyhsYXNzb195X2lybSwgZm9yZXN0X3lfaXJtLCB0cmVlX3lfaXJtLCBib29zdF95X2lybSkKdGFibGVbNCwgMTo0XSA8LSBjKGxhc3NvX2RfaXJtLCBmb3Jlc3RfZF9pcm0sIHRyZWVfZF9pcm0sIGJvb3N0X2RfaXJtKQpyb3duYW1lcyh0YWJsZSkgPC0gYygiRXN0aW1hdGUiLCAiU3RkLkVycm9yIiwgIlJNU0UgWSIsICJSTVNFIEQiKQpjb2xuYW1lcyh0YWJsZSkgPC0gYygiTGFzc28iLCAiUmFuZG9tIEZvcmVzdCIsICJUcmVlcyIsICJCb29zdGluZyIpCnRhYiA8LSB4dGFibGUodGFibGUsIGRpZ2l0cyA9IDIpCnRhYgpgYGAKCgpgYGB7cn0KbGdyOjpnZXRfbG9nZ2VyKCJtbHIzIikkc2V0X3RocmVzaG9sZCgid2FybiIpCmRtbF9pcm0gPC0gRG91YmxlTUxJUk0kbmV3KGRhdGFfbWwsCiAgbWxfZyA9IHJhbmRvbUZvcmVzdCwKICBtbF9tID0gbGFzc29fY2xhc3MsCiAgdHJpbW1pbmdfdGhyZXNob2xkID0gMC4wMSwgbl9mb2xkcyA9IDUKKQpkbWxfaXJtJGZpdChzdG9yZV9wcmVkaWN0aW9ucyA9IFRSVUUpCmRtbF9pcm0kc3VtbWFyeSgpCmJlc3RfaXJtIDwtIGRtbF9pcm0kY29lZgpiZXN0X3N0ZF9pcm0gPC0gZG1sX2lybSRzZQpgYGAKCi0tLQoKIyMjICoq5YiG5p6Q57WQ5p6c44Go6ICD5a+fKioKCiMjIyMgKipUYWJsZSAxMC40IEVzdGltYXRlZCBFZmZlY3Qgb2YgNDAxKGspIEVsaWdpYmlsaXR5IG9uIE5ldCBGaW5hbmNpYWwgQXNzZXRzKioKCnwgICAgICAgICAgICAgICAgICAgICAgICAgICB8IExhc3NvICB8IFRyZWUgICB8IEZvcmVzdCB8IEJvb3N0ICB8IEJlc3QgICB8IEVuc2VtYmxlIHwKfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLS0tfAp8ICoqQS4gUGFydGlhbGx5IExpbmVhciBSZWdyZXNzaW9uIE1vZGVsKiogfCAgICAgICAgfCAgICAgICAgfCAgICAgICAgfCAgICAgICAgfCAgICAgICAgfCAgICAgICAgICB8CnwgRXN0aW1hdGUgICAgICAgICAgICAgICAgICB8IDk0MTggICB8IDg2MzQgICB8IDkxMTIgICB8IDg4NTkgICB8IDg4NTkgICB8IDkwNTEgICAgIHwKfCBTdGQuIEVycm9yICAgICAgICAgICAgICAgIHwgKDE0NzYpIHwgKDEzMDMpIHwgKDEyODEpIHwgKDEzMjEpIHwgKDEzMjEpIHwgKDEzMTUpICAgfAp8IFJNU0UgRCAgICAgICAgICAgICAgICAgICAgfCAwLjQ0NyAgfCAwLjQ1NyAgfCAwLjQ1OSAgfCAwLjQ0MyAgfCAwLjQ0MyAgfCAwLjQ0MyAgICB8CnwgUk1TRSBZICAgICAgICAgICAgICAgICAgICB8IDU4MjQyICB8IDU2ODE5ICB8IDU1Mzg1ICB8IDU0MTUzICB8IDU0MTUzICB8IDUzOTE3ICAgIHwKfCAqKkIuIEludGVyYWN0aXZlIFJlZ3Jlc3Npb24gTW9kZWwqKiAgICAgfCAgICAgICAgfCAgICAgICAgfCAgICAgICAgfCAgICAgICAgfCAgICAgICAgfCAgICAgICAgICB8CnwgRXN0aW1hdGUgICAgICAgICAgICAgICAgICB8IDg4NjAgICB8IDc4NTYgICB8IDgzNDkgICB8IDc4NzEgICB8IDgyMDQgICB8IDgxNDYgICAgIHwKfCBTdGQuIEVycm9yICAgICAgICAgICAgICAgIHwgKDEzNDcpIHwgKDEyNTApIHwgKDE1MDIpIHwgKDExNTcpIHwgKDExNDQpIHwgKDExNDIpICAgfAp8IFJNU0UgRCAgICAgICAgICAgICAgICAgICAgfCAwLjQ0OCAgfCAwLjQ1NyAgfCAwLjQ1OSAgfCAwLjQ0MyAgfCAwLjQ0MyAgfCAwLjQ0MyAgICB8CnwgUk1TRSBZICAgICAgICAgICAgICAgICAgICB8IDU4MzAwICB8IDU0ODY2ICB8IDU3MjkzICB8IDU1MTEyICB8IDU0ODY2ICB8IDUzODA0ICAgIHwKCi0g5o6o5a6a44Gu6KOc6LazCiAgLSDlgr7lkJHjgrnjgrPjgqLjga7mpbXnq6/jgarlgKTjgavjgojjgovmjqjlrprjgbjjga7lvbHpn7/jgpLpmLLjgZDjgZ/jgoHjgIEoXCgwLjAxXCkg44GoIFwoMC45OVwpKSDjga7nr4Tlm7Ljgafjg4jjg6rjg5/jg7PjgrAKCi0gKirntZDmnpzjga7mpoLopoEqKgogICAtIOWFseWkiemHj+OCkuWQq+OBvuOBquOBhOODouODh+ODq+OBp+OBr+OAgTQwMShrKeOBuOOBruWKoOWFpeizh+agvOOBjOmHkeiejeizh+eUo+OBq+S4juOBiOOCi+W9semfv+OBrzE5LDU1OeODieODq+OBqOaOqOWumgogICAtIOWFseWkiemHj+OCkuOCs+ODs+ODiOODreODvOODq+OBmeOCi+OBk+OBqOOBq+OCiOOCiuOAgeeQhuirlueahOOBquS6iOa4rOmAmuOCiuOAgeato+OBrumBuOaKnuODkOOCpOOCouOCueOBjOijnOato+OBleOCjOOCiwogICAtIFBMTSDjgahJUk0g44Gu57WQ5p6c44Gv5qaC44Gt5LiA6LKr44GX44Gm44GE44KLCiAgIC0gUG90ZXJiYSBldCBhbC4g44Gr44KI44KL44K344Oz44OX44Or44Gq44Kz44Oz44OI44Ot44O844Or44Go57WQ5p6c44Gv6L+R44GP44CB44Gd44KM44Gn5Y2B5YiG44Gn44GC44KL5Y+v6IO95oCn44KS56S65ZSG44GX44Gm44GE44KLCi0gREFHIOOBp+imi+OBn+OCiOOBhuOBquOAgeWboOaenOODgOOCpOOCouOCsOODqeODoOOBruOCj+OBmuOBi+OBquWkieabtOOBjOitmOWIpeOBq+S4juOBiOOCi+W9semfv+OCkuWIhuaekOOBmeOCi+OBruOBr+OAgUNoMTIg44GuIHNlbnNpdGl2aXR5IGFuYWx5c2lzIOOBp+Wun+aWveOBmeOCiwoK