Simulation Experiment Recipe

Objectives

The objective of this simulation experiment is to provide a basic example on how to use simChef and showcase the automated R Markdown-generated documentation. For the sake of illustration, this toy simulation experiment studies the performance of linear regression versus random forests under a linear data-generating process across varying noise levels.

[Typically, the objective of the simulation experiment (and this blurb) will be more scientific than instructive and will warrant additional context/background and domain knowledge.]

Data Generation

Linear DGP

We simulate the (test and training) covariate/design matrix \(\mathbf{X} \in \mathbb{R}^{n \times p}\) from a standard normal distribution and the response vector \(\mathbf{y} \in \mathbb{R}^n\) from a linear model. Specifically,

\[\begin{align*}\mathbf{y} = \mathbf{X} \boldsymbol{\beta} + \boldsymbol{\epsilon},\\\end{align*}\]

where\[\begin{align*}& \mathbf{X}_{ij} \stackrel{iid}{\sim} N\left(0, 1\right) \text{ for all } i = 1, \ldots, n \text{ and } j = 1, \ldots, p, \\& \boldsymbol{\epsilon}_i \stackrel{iid}{\sim} N(0, \sigma^2) \text{ for all } i = 1, \ldots, n.\end{align*}\]

Default Parameters in DGP

  • Number of training samples: \(n_{\text{train}} = 200\)
  • Number of test samples: \(n_{\text{test}} = 200\)
  • Number of features: \(p = 2\)
  • Amount of noise: \(\sigma = 1\)
  • Coefficients: \(\boldsymbol{\beta} = (1, 0)^\top\)

[In practice, documentation of DGPs should answer the questions “what” and “why”. That is, “what” is the DGP, and “why” are we using/studying it? As this simulation experiment is a contrived example, we omit the “why” here.]

Function

#> function(n_train, n_test, p, beta, noise_sd) {
#>   n <- n_train + n_test
#>   X <- matrix(rnorm(n * p), nrow = n, ncol = p)
#>   y <- X %*% beta + rnorm(n, sd = noise_sd)
#>   data_list <- list(
#>     X_train = X[1:n_train, , drop = FALSE],
#>     y_train = y[1:n_train],
#>     X_test = X[(n_train + 1):n, , drop = FALSE],
#>     y_test = y[(n_train + 1):n]
#>   )
#>   return(data_list)
#> }
#> <bytecode: 0x5645ddacb0d8>

Input Parameters

#> $n_train
#> [1] 200
#> 
#> $n_test
#> [1] 200
#> 
#> $p
#> [1] 2
#> 
#> $beta
#> [1] 1 0
#> 
#> $noise_sd
#> [1] 1

Methods and Evaluation

Methods

Linear Regression

Given some training covariate data \(\mathbf{X}\) and response vector \(\mathbf{y}\), we fit a linear regression model (i.e., ordinary least squares) on \(\mathbf{X}\) to predict \(\mathbf{y}\) by minimizing the following objective:

\[\begin{align*}\boldsymbol{\hat{\beta}} = \text{argmin}_{\boldsymbol{\beta}} || \mathbf{y} - \mathbf{X} \boldsymbol{\beta} ||_2^2.\end{align*}\]

Then, to make prediction given some test data \(\mathbf{X}^{\text{test}}\), we compute:

\[\begin{align*}\boldsymbol{\hat{y}}^{\text{test}} = \mathbf{X}^{\text{test}} \boldsymbol{\hat{\beta}}.\end{align*}\]

[In practice, documentation of methods should answer the questions “what” and “why”. That is, “what” is the method, and “why” are we using/studying it? As this simulation experiment is a contrived example, we omit the “why” here.]

Function

#> function(X_train, y_train, X_test, y_test) {
#>   train_df <- dplyr::bind_cols(data.frame(X_train), y = y_train)
#>   fit <- lm(y ~ ., data = train_df)
#>   predictions <- predict(fit, data.frame(X_test))
#>   return(list(predictions = predictions, y_test = y_test))
#> }
#> <bytecode: 0x5645dffc7630>

Input Parameters

#> list()

Random Forest

Given some training covariate data \(\mathbf{X}\) and response vector \(\mathbf{y}\), we fit a random forest on \(\mathbf{X}\) to predict \(\mathbf{y}\). At a high-level, a random forest is an ensemble of classification or regression trees (CART) that are fitted independently of one another on bootstrapped samples of the training data. Further, each CART model is fitted by performing recursive axis-aligned binary splits, where the optimal split at each node is chosen from a random subsample of features to minimize an impurity decrease criterion.

To make predictions, CART identifies the unique leaf node containing the test data point and predicts the mean response (of training data) in that node. For a random forest, the predictions are averaged across all CARTs in the forest.

For further details, we refer to Breiman (2001).

[In practice, documentation of methods should answer the questions “what” and “why”. That is, “what” is the method, and “why” are we using/studying it? As this simulation experiment is a contrived example, we omit the “why” here.]

Function

#> function(X_train, y_train, X_test, y_test, ...) {
#>   train_df <- dplyr::bind_cols(data.frame(X_train), y = y_train)
#>   fit <- ranger::ranger(y ~ ., data = train_df, ...)
#>   predictions <- predict(fit, data.frame(X_test))$predictions
#>   return(list(predictions = predictions, y_test = y_test))
#> }
#> <bytecode: 0x5645e128a460>

Input Parameters

#> $num.threads
#> [1] 1

Evaluation

Prediction Accuracy

Given the true responses \(\mathbf{y} \in \mathbb{R}^n\) and predicted responses \(\mathbf{\hat{y}} \in \mathbb{R}^n\) from various methods, we evaluate several prediction accuracy metrics, namely:

  • Root Mean Squared Error (RMSE): \(\sqrt{\frac{1}{n} || \mathbf{y} - \mathbf{\hat{y}} ||_2^2}\)
  • Mean Absolute Error (MAE): \(\frac{1}{n} || \mathbf{y} - \mathbf{\hat{y}} ||_1\)
  • R-squared (\(R^2\)): \(1 - \frac{|| \mathbf{y} - \mathbf{\hat{y}} ||_2^2}{|| \mathbf{y} - \mathbf{\bar{y}} ||_2^2}\)

We choose to evaluate both RMSE and MAE as these can convey different messages in the presence of outliers. Further, \(R^2\) provides a convenient normalization of RMSE that can often be more easily interpreted.

[In practice, documentation of evaluation metrics should answer the questions “what” and “why”. That is, “what” is the metric, and “why” are we using/studying it?]

Function

#> function (fit_results, vary_params = NULL, nested_cols = NULL, 
#>     truth_col, estimate_col, prob_cols = NULL, group_cols = NULL, 
#>     metrics = NULL, na_rm = FALSE, summary_funs = c("mean", "median", 
#>         "min", "max", "sd", "raw"), custom_summary_funs = NULL, 
#>     eval_id = "pred_err") 
#> {
#>     group_vars <- c(".dgp_name", ".method_name", vary_params, 
#>         group_cols, ".metric")
#>     eval_tib <- eval_pred_err(fit_results = fit_results, vary_params = vary_params, 
#>         nested_cols = nested_cols, truth_col = truth_col, estimate_col = estimate_col, 
#>         prob_cols = prob_cols, group_cols = group_cols, metrics = metrics, 
#>         na_rm = na_rm) %>% dplyr::group_by(dplyr::across(tidyselect::any_of(group_vars)))
#>     eval_summary <- eval_summarizer(eval_data = eval_tib, eval_id = eval_id, 
#>         value_col = ".estimate", summary_funs = summary_funs, 
#>         custom_summary_funs = custom_summary_funs, na_rm = na_rm)
#>     return(eval_summary)
#> }
#> <bytecode: 0x5645dd2cdc50>
#> <environment: namespace:simChef>

Input Parameters

#> $truth_col
#> [1] "y_test"
#> 
#> $estimate_col
#> [1] "predictions"

Visualizations

Prediction Accuracy Plot

We plot the prediction accuracy between the true and predicted responses, as measured via RMSE, MAE, and \(R^2\), to understand how characteristics of the DGP affect various prediction methods.

[In practice, documentation of the plotters should answer the questions “what” and “why”. That is, “what” is the plot, and “why” are we using/studying it?]

Function

#> function (fit_results = NULL, eval_results = NULL, eval_name = NULL, 
#>     eval_fun = "summarize_pred_err", eval_fun_options = NULL, 
#>     vary_params = NULL, metrics = NULL, show = c("point", "line"), 
#>     ...) 
#> {
#>     .metric <- NULL
#>     arg_list <- get_dot_args(user_args = rlang::list2(...), default_args = list(eval_id = "pred_err", 
#>         facet_formula = ~.metric, facet_type = "wrap", facet_args = list(scales = "free")))
#>     plot_data <- get_plot_data(fit_results = fit_results, eval_results = eval_results, 
#>         eval_name = eval_name, eval_fun = eval_fun, eval_fun_options = c(eval_fun_options, 
#>             list(metrics = metrics)))
#>     if (!is.null(metrics)) {
#>         if (!inherits(metrics, "metric_set")) {
#>             abort("Unknown metrics. metrics must be of class 'yardstick::metric_set' or NULL.")
#>         }
#>         metric_names <- names(attr(metrics, "metrics"))
#>         plot_data <- plot_data %>% dplyr::filter(.metric %in% 
#>             metric_names)
#>     }
#>     plt <- do.call(plot_eval_constructor, args = c(list(plot_data = plot_data, 
#>         vary_params = vary_params, show = show), arg_list))
#>     return(plt)
#> }
#> <bytecode: 0x5645dc992648>
#> <environment: namespace:simChef>

Input Parameters

#> $eval_name
#> [1] "Prediction Accuracy"

Linear DGP

Varying noise_sd

Prediction Accuracy

Prediction Accuracy Plot

Parameter Values

#> $dgp
#> $dgp$`Linear DGP`
#> $dgp$`Linear DGP`$noise_sd
#> [1] 0.1 0.5 1.0 2.0
#> 
#> 
#> 
#> $method
#> list()
LS0tCnRpdGxlOiAiYHIgcGFyYW1zJHNpbV9uYW1lYCIKYXV0aG9yOiAiYHIgcGFyYW1zJGF1dGhvcmAiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKb3V0cHV0OiBybWFya2Rvd246Omh0bWxfZG9jdW1lbnQKcGFyYW1zOgogIGF1dGhvcjogCiAgICBsYWJlbDogIkF1dGhvcjoiCiAgICB2YWx1ZTogIiIKICBzaW1fbmFtZToKICAgIGxhYmVsOiAiU2ltdWxhdGlvbiBFeHBlcmltZW50IE5hbWU6IgogICAgdmFsdWU6ICIiCiAgc2ltX3BhdGg6CiAgICBsYWJlbDogIlBhdGggdG8gU2ltdWxhdGlvbiBFeHBlcmltZW50IEZvbGRlcjoiCiAgICB2YWx1ZTogIiIKICB3cml0ZV9maWxlbmFtZToKICAgIGxhYmVsOiAiT3V0cHV0IEZpbGU6IgogICAgdmFsdWU6ICIiCiAgc2hvd19jb2RlOgogICAgbGFiZWw6ICJTaG93IENvZGU6IgogICAgdmFsdWU6IFRSVUUKICBzaG93X2V2YWw6CiAgICBsYWJlbDogIlNob3cgRXZhbHVhdG9yczoiCiAgICB2YWx1ZTogVFJVRQogIHNob3dfdml6OgogICAgbGFiZWw6ICJTaG93IFZpc3VhbGl6ZXJzOiIKICAgIHZhbHVlOiBUUlVFCiAgZXZhbF9vcmRlcjoKICAgIGxhYmVsOiAiT3JkZXIgb2YgRXZhbHVhdG9yczoiCiAgICB2YWx1ZTogTlVMTAogIHZpel9vcmRlcjoKICAgIGxhYmVsOiAiT3JkZXIgb2YgVmlzdWFsaXplcnM6IgogICAgdmFsdWU6IE5VTEwKICB1c2VfaWNvbnM6CiAgICBsYWJlbDogIlVzZSBJY29uczoiCiAgICB2YWx1ZTogVFJVRQogIHVzZV92bW9kZXJuOgogICAgbGFiZWw6ICJVc2UgdnRoZW1lczo6dm1vZGVybjoiCiAgICB2YWx1ZTogVFJVRQogIHdyaXRlOgogICAgbGFiZWw6ICJXcml0ZSBGaWxlOiIKICAgIHZhbHVlOiBGQUxTRQogIHZlcmJvc2U6CiAgICBsYWJlbDogIlZlcmJvc2UgTGV2ZWw6IgogICAgdmFsdWU6IDIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBGQUxTRSwKICB3YXJuaW5nID0gRkFMU0UsCiAgbWVzc2FnZSA9IEZBTFNFLAogIGNhY2hlID0gRkFMU0UsCiAgZmlnLmFsaWduID0gImNlbnRlciIsCiAgZmlnLnBvcyA9ICJIIiwKICBmaWcuaGVpZ2h0ID0gMTIsCiAgZmlnLndpZHRoID0gMTAKKQoKb3B0aW9ucygKICB3aWR0aCA9IDEwMDAwLAogIGtuaXRyLmthYmxlLk5BID0gIk5BIgopCgojIHNjcm9sbGFibGUgdGV4dCBvdXRwdXQKbG9jYWwoewogIGhvb2tfb3V0cHV0IDwtIGtuaXRyOjprbml0X2hvb2tzJGdldCgib3V0cHV0IikKICBrbml0cjo6a25pdF9ob29rcyRzZXQob3V0cHV0ID0gZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgaWYgKCFpcy5udWxsKG9wdGlvbnMkbWF4LmhlaWdodCkpIHsKICAgICAgb3B0aW9ucyRhdHRyLm91dHB1dCA8LSBjKAogICAgICAgIG9wdGlvbnMkYXR0ci5vdXRwdXQsCiAgICAgICAgc3ByaW50Zignc3R5bGU9Im1heC1oZWlnaHQ6ICVzOyInLCBvcHRpb25zJG1heC5oZWlnaHQpCiAgICAgICkKICAgIH0KICAgIGhvb2tfb3V0cHV0KHgsIG9wdGlvbnMpCiAgfSkKfSkKCmNodW5rX2lkeCA8LSAxCmRvY19kaXIgPC0gZmlsZS5wYXRoKHBhcmFtcyRzaW1fcGF0aCwgImRvY3MiKQp3cml0ZV9maWxlbmFtZSA8LSBwYXJhbXMkd3JpdGVfZmlsZW5hbWUKYGBgCgpgYGB7ciBoZWxwZXItZnVuc30KCiMnIFdyYXAgdGV4dC9jb2RlIGluIGtuaXRyIGNvZGUgY2h1bmsgc3RyaW5nCiMnCiMnIEBwYXJhbSBjb2RlIFN0cmluZyBvZiBjb2RlIHRvIHdyYXAgaW4ga25pdHIgY29kZSBjaHVuawojJyBAcGFyYW0gY2h1bmtfYXJncyBTdHJpbmcgb2YgYXJndW1lbnRzIHRvIHBsYWNlIGluIHRoZSBrbml0ciBjb2RlIGNodW5rIGhlYWRlcgojJyBAcmV0dXJuIFN0cmluZyBvZiBjb2RlLCB3cmFwcGVkIGluc2lkZSB0aGUga25pdHIgY29kZSBjaHVuayBgYGAgbWFya2Vycwp3cml0ZV9jb2RlX2NodW5rIDwtIGZ1bmN0aW9uKGNvZGUgPSAiIiwgY2h1bmtfYXJncyA9ICIiKSB7CiAgc3ByaW50ZigiXG5gYGB7ciwgJXN9XG4lc1xuYGBgXG4iLCBjaHVua19hcmdzLCBjb2RlKQp9CgojJyBXcml0ZSB0ZXh0IHRvIHZlY3RvciAod3JpdGVfZmxhZyA9IFRSVUUpIG9yIHRvIGNvbnNvbGUgKHdyaXRlX2ZsYWcgPSBGQUxTRSkKIycKIycgQHBhcmFtIC4uLiBUZXh0IHRvIHdyaXRlIHRvIHZlY3RvciBvciB0byBjb25zb2xlCiMnIEBwYXJhbSBvbGRfdGV4dCBQcmV2aW91cyB0ZXh0IHRvIGFwcGVuZCB0byB3aGVuIHdyaXRpbmcgdG8gYSB2ZWN0b3IKIycgQHBhcmFtIHdyaXRlX2ZsYWcgQm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdG8gd3JpdGUgdGV4dCB0byBhIHZlY3RvcgojJyAgICh3cml0ZV9mbGFnID0gVFJVRSkgb3IgdG8gY29uc29sZSAod3JpdGVfZmxhZyA9IEZBTFNFKQojJyBAcmV0dXJuIElmIHdyaXRlX2ZsYWcgPSBUUlVFLCByZXR1cm5zIHZlY3RvciBvZiB0ZXh0LiBPdGhlcndpc2UsIHRleHQgaXMKIycgICB3cml0dGVuIHRvIGNvbnNvbGUgdmlhIGBjYXQoKWAuCndyaXRlIDwtIGZ1bmN0aW9uKC4uLiwgb2xkX3RleHQgPSBOVUxMLCB3cml0ZV9mbGFnKSB7CiAgaWYgKHdyaXRlX2ZsYWcpIHsKICAgIHJldHVybihjKG9sZF90ZXh0LCAuLi4pKQogIH0gZWxzZSB7CiAgICBkb3RzX2xpc3QgPC0gbGlzdCguLi4pICU+JQogICAgICBwdXJycjo6bWFwKAogICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgIGlmIChzdHJpbmdyOjpzdHJfZGV0ZWN0KHgsICJgciAuKmAiKSkgewogICAgICAgICAgICAjIHJ1biByIGNvZGUgYmVmb3JlIHByaW50aW5nIHJlc3VsdHMgaW4gY2F0KCkKICAgICAgICAgICAgb3V0IDwtIHN0cmluZ3I6OnN0cl9yZXBsYWNlKAogICAgICAgICAgICAgIHgsICJgciAuKmAiLAogICAgICAgICAgICAgIGV2YWwocGFyc2UodGV4dCA9IHN0cmluZ3I6OnN0cl9leHRyYWN0KHgsICIoPzw9YHIgKSguKj8pKD89YCkiKSkpCiAgICAgICAgICAgICkKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIG91dCA8LSB4CiAgICAgICAgICB9CiAgICAgICAgICByZXR1cm4ob3V0KQogICAgICAgIH0KICAgICAgKQogICAgZG8uY2FsbChjYXQsIGFyZ3MgPSBjKGRvdHNfbGlzdCwgbGlzdChzZXAgPSAiIikpKQogIH0KfQoKIycgV3JpdGUgdGV4dCB0byBmaWxlCiMnCiMnIEBwYXJhbSBwYXRoIFBhdGggdG8gb3V0cHV0IGZpbGUKIycgQHBhcmFtIC4uLiBUZXh0IHRvIG91dHB1dCB0byBmaWxlCndyaXRlX3RvX2ZpbGUgPC0gZnVuY3Rpb24ocGF0aCwgLi4uKSB7CiAgc3RvcmVsaW5lcyA8LSByZWFkTGluZXMocGF0aCkKICBzdG9yZWxpbmVzIDwtIGMoc3RvcmVsaW5lcywgLi4uKQogIHdyaXRlTGluZXMoc3RvcmVsaW5lcywgcGF0aCkKfQoKIycgR2V0IG9yZGVyIG9mIG9iamVjdHMgdG8gZGlzcGxheQojJwojJyBAcGFyYW0gb2JqX25hbWVzIFZlY3RvciBvZiBhbGwgb2JqZWN0IG5hbWVzIHRoYXQgbmVlZCB0byBiZSBkaXNwbGF5ZWQuCiMnIEBwYXJhbSBvYmpfb3JkZXIgVmVjdG9yIG9mIG9iamVjdCBuYW1lcyBpbiB0aGUgZGVzaXJlZCBhcHBlYXJhbmNlIG9yZGVyLgojJyBAcmV0dXJuIFZlY3RvciBvZiBvYmplY3QgbmFtZXMgaW4gdGhlIG9yZGVyIGluIHdoaWNoIHRoZXkgd2lsbCBiZSBkaXNwbGF5ZWQuCmdldF9vYmplY3Rfb3JkZXIgPC0gZnVuY3Rpb24ob2JqX25hbWVzLCBvYmpfb3JkZXIgPSBOVUxMKSB7CiAgaWYgKGlzLm51bGwob2JqX29yZGVyKSkgewogICAgcmV0dXJuKG9ial9uYW1lcykKICB9IGVsc2UgewogICAgcmV0dXJuKGludGVyc2VjdChvYmpfb3JkZXIsIG9ial9uYW1lcykpCiAgfQp9CgojJyBHZXQgYWxsIGV4cGVyaW1lbnRzIHVuZGVyIGEgZ2l2ZW4gZGlyZWN0b3J5IG5hbWUKIycKIycgQHBhcmFtIGRpcl9uYW1lIG5hbWUgb2YgZGlyZWN0b3J5CiMnIEByZXR1cm4gbGlzdCBvZiBuYW1lZCBleHBlcmltZW50cwpnZXRfZGVzY2VuZGFudHMgPC0gZnVuY3Rpb24oZGlyX25hbWUpIHsKICBleHBlcmltZW50cyA8LSBsaXN0KCkKICBmb3IgKGQgaW4gbGlzdC5kaXJzKGRpcl9uYW1lKSkgewogICAgaWYgKGZpbGUuZXhpc3RzKGZpbGUucGF0aChkLCAiZXhwZXJpbWVudC5yZHMiKSkpIHsKICAgICAgaWYgKGlkZW50aWNhbChkLCBwYXJhbXMkc2ltX3BhdGgpKSB7CiAgICAgICAgZXhwX25hbWUgPC0gIkJhc2UiCiAgICAgIH0gZWxzZSB7CiAgICAgICAgZXhwX25hbWUgPC0gc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKAogICAgICAgICAgc3RyaW5ncjo6c3RyX3JlbW92ZShkLCBwYXN0ZTAocGFyYW1zJHNpbV9wYXRoLCAiLyIpKSwKICAgICAgICAgICIvIiwgIiAtICIKICAgICAgICApCiAgICAgIH0KICAgICAgZXhwZXJpbWVudHNbW2V4cF9uYW1lXV0gPC0gcmVhZFJEUyhmaWxlLnBhdGgoZCwgImV4cGVyaW1lbnQucmRzIikpCiAgICB9CiAgfQogIHJldHVybihleHBlcmltZW50cykKfQoKIycgQ2hlY2sgaWYgZXhwZXJpbWVudCBleGlzdHMKIycKIycgQHBhcmFtIGRpcl9uYW1lIG5hbWUgb2YgZGlyZWN0b3J5IG9yIHZlY3RvciB0aGVyZW9mCiMnIEBwYXJhbSByZWN1cnNpdmUgbG9naWNhbDsgaWYgVFJVRSwgY2hlY2tzIGlmIGV4cGVyaW1lbnQgZXhpc3RzIHVuZGVyIHRoZQojJyAgIGdpdmVuIGRpcmVjdG9yeShzKTsgaWYgRkFMU0UsIGNoZWNrcyBpZiBhbnkgZXhwZXJpbWVudCBleGlzdHMgdW5kZXIgdGhlCiMnICAgZGlyZWN0b3J5KHMpIGFuZCBpdHMgZGVzY2VuZGFudHMKIycgQHJldHVybiBUUlVFIGlmIGV4cGVyaW1lbnQgZXhpc3RzIGFuZCBGQUxTRSBvdGhlcndpc2UKZXhwZXJpbWVudF9leGlzdHMgPC0gZnVuY3Rpb24oZGlyX25hbWUsIHJlY3Vyc2l2ZSA9IEZBTFNFKSB7CiAgcmVzIDwtIHB1cnJyOjptYXBfbGdsKAogICAgZGlyX25hbWUsCiAgICBmdW5jdGlvbihkKSB7CiAgICAgIGlmICghcmVjdXJzaXZlKSB7CiAgICAgICAgZXhwX2ZuYW1lIDwtIGZpbGUucGF0aChkLCAiZXhwZXJpbWVudC5yZHMiKQogICAgICAgIHJldHVybihmaWxlLmV4aXN0cyhleHBfZm5hbWUpKQogICAgICB9IGVsc2UgewogICAgICAgIGRlc2NlbmRhbnRzIDwtIGdldF9kZXNjZW5kYW50cyhkKQogICAgICAgIHJldHVybihsZW5ndGgoZGVzY2VuZGFudHMpID4gMCkKICAgICAgfQogICAgfQogICkKICByZXR1cm4oYW55KHJlcykpCn0KCiMnIERpc3BsYXlzIGNvbnRlbnQgZm9yIHNwZWNpZmllZCBwYXJ0IG9mIHJlY2lwZQojJwojJyBAcGFyYW0gZmllbGRfbmFtZSBwYXJ0IG9mIHJlY2lwZSB0byBzaG93OyBtdXN0IGJlIG9uZSBvZiAiZGdwIiwgIm1ldGhvZCIsCiMnICAgImV2YWx1YXRvciIsIG9yICJ2aXN1YWxpemVyIgojJyBAcGFyYW0gd3JpdGVfZmxhZyBCb29sZWFuIGluZGljYXRpbmcgd2hldGhlciB0byB3cml0ZSB0ZXh0IHRvIGEgdmVjdG9yCiMnICAgKHdyaXRlX2ZsYWcgPSBUUlVFKSBvciB0byBjb25zb2xlICh3cml0ZV9mbGFnID0gRkFMU0UpCiMnIEByZXR1cm4gY29udGVudCBmb3IgcmVjaXBlCnNob3dfcmVjaXBlIDwtIGZ1bmN0aW9uKGZpZWxkX25hbWUgPSBjKAogICAgICAgICAgICAgICAgICAgICAgICAgICJkZ3AiLCAibWV0aG9kIiwgImV2YWx1YXRvciIsICJ2aXN1YWxpemVyIgogICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICB3cml0ZV9mbGFnID0gRkFMU0UpIHsKICBmaWVsZF9uYW1lIDwtIG1hdGNoLmFyZyhmaWVsZF9uYW1lKQogIGZ1bmNfbmFtZSA8LSBkcGx5cjo6Y2FzZV93aGVuKAogICAgZmllbGRfbmFtZSA9PSAiZXZhbHVhdG9yIiB+ICJldmFsIiwKICAgIGZpZWxkX25hbWUgPT0gInZpc3VhbGl6ZXIiIH4gInZpeiIsCiAgICBUUlVFIH4gZmllbGRfbmFtZQogICkKICBkZXNjZW5kYW50cyA8LSBnZXRfZGVzY2VuZGFudHMoZGlyX25hbWUgPSBwYXJhbXMkc2ltX3BhdGgpCiAgb2JqcyA8LSBwdXJycjo6bWFwKGRlc2NlbmRhbnRzLCB+IC54W1twYXN0ZTAoImdldF8iLCBmaWVsZF9uYW1lLCAicyIpXV0oKSkKICBvYmpfbmFtZXMgPC0gdW5pcXVlKHB1cnJyOjpyZWR1Y2Uoc2FwcGx5KG9ianMsIG5hbWVzKSwgYykpCgogIGlmIChmaWVsZF9uYW1lICVpbiUgYygibWV0aG9kIiwgImV2YWx1YXRvciIpKSB7CiAgICBvYmpfaGVhZGVyIDwtICJcblxuIyMjIyAlcyB7LnRhYnNldCAudGFic2V0LXBpbGxzIC50YWJzZXQtY2lyY2xlIC50YWJzZXQtcmVjaXBlfVxuXG4iCiAgICBzaG93dHlwZV9oZWFkZXIgPC0gIlxuXG4jIyMjIyAlcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfVxuXG4iCiAgICBleHBfaGVhZGVyIDwtICJcblxuIyMjIyMjICVzIFxuXG4iCiAgfSBlbHNlIHsKICAgIG9ial9oZWFkZXIgPC0gIlxuXG4jIyMgJXMgey50YWJzZXQgLnRhYnNldC1waWxscyAudGFic2V0LWNpcmNsZSAudGFic2V0LXJlY2lwZX1cblxuIgogICAgc2hvd3R5cGVfaGVhZGVyIDwtICJcblxuIyMjIyAlcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfVxuXG4iCiAgICBleHBfaGVhZGVyIDwtICJcblxuIyMjIyMgJXMgXG5cbiIKICB9CgogIGlmIChwYXJhbXMkdXNlX2ljb25zKSB7CiAgICBpZiAocGFyYW1zJHVzZV92bW9kZXJuKSB7CiAgICAgIGRlc2NyaXB0aW9uX2xhYmVsIDwtICJgciBmb250YXdlc29tZTo6ZmEoJ3JlYWRtZScsIGZpbGwgPSAnd2hpdGUnKWAiCiAgICAgIGNvZGVfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgnY29kZScsIGZpbGwgPSAnd2hpdGUnKWAiCiAgICB9IGVsc2UgewogICAgICBkZXNjcmlwdGlvbl9sYWJlbCA8LSAiYHIgZm9udGF3ZXNvbWU6OmZhKCdyZWFkbWUnKWAiCiAgICAgIGNvZGVfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgnY29kZScpYCIKICAgIH0KICB9IGVsc2UgewogICAgZGVzY3JpcHRpb25fbGFiZWwgPC0gIkRlc2NyaXB0aW9uIgogICAgY29kZV9sYWJlbCA8LSAiQ29kZSIKICB9CgogIGlmIChhbGwoc2FwcGx5KG9ianMsIGxlbmd0aCkgPT0gMCkpIHsKICAgIGlmICh3cml0ZV9mbGFnKSB7CiAgICAgIHJldHVybigiTi9BIikKICAgIH0gZWxzZSB7CiAgICAgIHJldHVybihjYXQoIk4vQSIpKQogICAgfQogIH0KCiAgcmVjaXBlIDwtIGMoKQogIGZvciAoaWR4IGluIDE6bGVuZ3RoKG9ial9uYW1lcykpIHsKICAgIG9ial9uYW1lIDwtIG9ial9uYW1lc1tpZHhdCiAgICBkZXNjcmlwdGlvbl9mcGF0aCA8LSBmaWxlLnBhdGgoCiAgICAgIGRvY19kaXIsIHBhc3RlMChmaWVsZF9uYW1lLCAicyIpLCBwYXN0ZTAob2JqX25hbWUsICIubWQiKQogICAgKQogICAgCiAgICBpZiAocGFyYW1zJHVzZV92bW9kZXJuKSB7CiAgICAgIHJlY2lwZSA8LSB3cml0ZSgKICAgICAgICAiXG5cbjxkaXYgY2xhc3M9J3BhbmVsIHBhbmVsLWRlZmF1bHQgcGFkZGVkLXBhbmVsJz5cblxuIiwKICAgICAgICBvbGRfdGV4dCA9IHJlY2lwZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICAgKQogICAgfQoKICAgIHJlY2lwZSA8LSB3cml0ZSgKICAgICAgc3ByaW50ZihvYmpfaGVhZGVyLCBvYmpfbmFtZSksCiAgICAgIHNwcmludGYoc2hvd3R5cGVfaGVhZGVyLCBkZXNjcmlwdGlvbl9sYWJlbCksCiAgICAgIHBhc3RlTWQoZGVzY3JpcHRpb25fZnBhdGgpLAogICAgICBvbGRfdGV4dCA9IHJlY2lwZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICkKCiAgICBpZiAocGFyYW1zJHNob3dfY29kZSkgewogICAgICByZWNpcGUgPC0gd3JpdGUoCiAgICAgICAgc3ByaW50ZihzaG93dHlwZV9oZWFkZXIsIGNvZGVfbGFiZWwpLAogICAgICAgIG9sZF90ZXh0ID0gcmVjaXBlLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICApCgogICAgICBrZWVwX29ianMgPC0gcHVycnI6OmNvbXBhY3QocHVycnI6Om1hcChvYmpzLCBvYmpfbmFtZSkpCiAgICAgIGlzX2lkZW50aWNhbCA8LSBhbGwoCiAgICAgICAgcHVycnI6Om1hcF9sZ2woa2VlcF9vYmpzLCB+IGlzVFJVRShjaGVja19lcXVhbCgueCwga2VlcF9vYmpzW1sxXV0pKSkKICAgICAgKQogICAgICBmb3IgKGV4cCBpbiBuYW1lcyhrZWVwX29ianMpKSB7CiAgICAgICAgb2JqIDwtIGtlZXBfb2Jqc1tbZXhwXV0KICAgICAgICBpZiAoIWlzX2lkZW50aWNhbCkgewogICAgICAgICAgcmVjaXBlIDwtIHdyaXRlKAogICAgICAgICAgICBzcHJpbnRmKGV4cF9oZWFkZXIsIGV4cCksCiAgICAgICAgICAgIG9sZF90ZXh0ID0gcmVjaXBlLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICAgICAgKQogICAgICAgIH0KCiAgICAgICAgcmVjaXBlIDwtIHdyaXRlKAogICAgICAgICAgIlxuXG4qKkZ1bmN0aW9uKipcblxuIiwKICAgICAgICAgIG9sZF90ZXh0ID0gcmVjaXBlLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICAgICkKICAgICAgICBpZiAod3JpdGVfZmxhZykgewogICAgICAgICAgcmVjaXBlIDwtIHNwcmludGYoCiAgICAgICAgICAgICJzaG93X3JlY2lwZSglc19vYmpzLCAnJXMnLCAnJXMnLCB3aGF0ID0gJ2Z1bmN0aW9uJykiLAogICAgICAgICAgICBmdW5jX25hbWUsIG9ial9uYW1lLCBleHAKICAgICAgICAgICkgJT4lCiAgICAgICAgICAgIHdyaXRlX2NvZGVfY2h1bmsoY2h1bmtfYXJncyA9ICJtYXguaGVpZ2h0PScyMDBweCciKSAlPiUKICAgICAgICAgICAgd3JpdGUob2xkX3RleHQgPSByZWNpcGUsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICB2dGhlbWVzOjpzdWJjaHVua2lmeSgKICAgICAgICAgICAgb2JqW1twYXN0ZTAoZnVuY19uYW1lLCAiX2Z1biIpXV0sIGNodW5rX2lkeCwKICAgICAgICAgICAgb3RoZXJfYXJncyA9ICJtYXguaGVpZ2h0PScyMDBweCciCiAgICAgICAgICApCiAgICAgICAgICBjaHVua19pZHggPDwtIGNodW5rX2lkeCArIDEKICAgICAgICB9CgogICAgICAgIHJlY2lwZSA8LSB3cml0ZSgKICAgICAgICAgICJcblxuKipJbnB1dCBQYXJhbWV0ZXJzKipcblxuIiwKICAgICAgICAgIG9sZF90ZXh0ID0gcmVjaXBlLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICAgICkKICAgICAgICBpZiAod3JpdGVfZmxhZykgewogICAgICAgICAgcmVjaXBlIDwtIHNwcmludGYoCiAgICAgICAgICAgICJzaG93X3JlY2lwZSglc19vYmpzLCAnJXMnLCAnJXMnLCB3aGF0ID0gJ3BhcmFtZXRlcnMnKSIsCiAgICAgICAgICAgIGZ1bmNfbmFtZSwgb2JqX25hbWUsIGV4cAogICAgICAgICAgKSAlPiUKICAgICAgICAgICAgd3JpdGVfY29kZV9jaHVuayhjaHVua19hcmdzID0gIm1heC5oZWlnaHQ9JzIwMHB4JyIpICU+JQogICAgICAgICAgICB3cml0ZShvbGRfdGV4dCA9IHJlY2lwZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcpCiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIHZ0aGVtZXM6OnN1YmNodW5raWZ5KAogICAgICAgICAgICBvYmpbW3Bhc3RlMChmdW5jX25hbWUsICJfcGFyYW1zIildXSwgY2h1bmtfaWR4LAogICAgICAgICAgICBvdGhlcl9hcmdzID0gIm1heC5oZWlnaHQ9JzIwMHB4JyIKICAgICAgICAgICkKICAgICAgICAgIGNodW5rX2lkeCA8PC0gY2h1bmtfaWR4ICsgMQogICAgICAgIH0KCiAgICAgICAgaWYgKGlzX2lkZW50aWNhbCkgewogICAgICAgICAgYnJlYWsKICAgICAgICB9CiAgICAgIH0KICAgIH0KCiAgICBpZiAocGFyYW1zJHVzZV92bW9kZXJuKSB7CiAgICAgIHJlY2lwZSA8LSB3cml0ZSgKICAgICAgICAiXG5cbjwvZGl2PlxuXG4iLCBvbGRfdGV4dCA9IHJlY2lwZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICAgKQogICAgfQogIH0KCiAgcmV0dXJuKHJlY2lwZSkKfQoKIycgUmVhZHMgaW4gZmlsZSBpZiBpdCBleGlzdHMgYW5kIHJldHVybnMgTlVMTCBpZiB0aGUgZmlsZSBkb2VzIG5vdCBleGlzdAojJwojJyBAcGFyYW0gZmlsZW5hbWUgbmFtZSBvZiAucmRzIGZpbGUgdG8gdHJ5IHJlYWRpbmcgaW4KIycgQHJldHVybiBvdXRwdXQgb2YgZmlsZW5hbWUucmRzIGlmIHRoZSBmaWxlIGV4aXN0cyBhbmQgTlVMTCBvdGhlcndpc2UKZ2V0X3Jlc3VsdHMgPC0gZnVuY3Rpb24oZmlsZW5hbWUpIHsKICBpZiAoZmlsZS5leGlzdHMoZmlsZW5hbWUpKSB7CiAgICByZXN1bHRzIDwtIHJlYWRSRFMoZmlsZW5hbWUpCiAgfSBlbHNlIHsKICAgIHJlc3VsdHMgPC0gTlVMTAogIH0KICByZXR1cm4ocmVzdWx0cykKfQoKIycgRGlzcGxheXMgb3V0cHV0IChib3RoIGZyb20gZXZhbHVhdGUoKSBhbmQgdmlzdWFsaXplKCkpIGZyb20gc2F2ZWQgcmVzdWx0cyB1bmRlcgojJyBhIHNwZWNpZmllZCBkaXJlY3RvcnkKIycKIycgQHBhcmFtIGRpcl9uYW1lIG5hbWUgb2YgZGlyZWN0b3J5CiMnIEBwYXJhbSBkZXB0aCBpbnRlZ2VyOyBkZXB0aCBvZiBkaXJlY3RvcnkgZnJvbSBwYXJlbnQvYmFzZSBleHBlcmltZW50J3MgZm9sZGVyCiMnIEBwYXJhbSBiYXNlIGxvZ2ljYWw7IHdoZXRoZXIgb3Igbm90IHRoaXMgaXMgYSBiYXNlIGV4cGVyaW1lbnQKIycgQHBhcmFtIHNob3dfaGVhZGVyIGxvZ2ljYWw7IHdoZXRoZXIgb3Igbm90IHRvIHNob3cgc2VjdGlvbiBoZWFkZXIKIycgQHBhcmFtIHZlcmJvc2UgaW50ZWdlcjsgMCA9IG5vIG1lc3NhZ2VzOyAxID0gcHJpbnQgb3V0IGRpcmVjdG9yeSBuYW1lIG9ubHk7CiMnICAgMiA9IHByaW50IG91dCBkaXJlY3RvcnkgbmFtZSBhbmQgbmFtZSBvZiBldmFsdWF0b3JzL3Zpc3VhbGl6ZXJzCiMnIEBwYXJhbSB3cml0ZV9mbGFnIEJvb2xlYW4gaW5kaWNhdGluZyB3aGV0aGVyIHRvIHdyaXRlIHRleHQgdG8gYSB2ZWN0b3IKIycgICAod3JpdGVfZmxhZyA9IFRSVUUpIG9yIHRvIGNvbnNvbGUgKHdyaXRlX2ZsYWcgPSBGQUxTRSkKIycgQHJldHVybiBjb250ZW50IHJlc3VsdHMgZnJvbSBldmFsdWF0ZSgpIGFuZCB2aXN1YWxpemUoKSBmcm9tIHRoZSBleHBlcmltZW50CnNob3dfcmVzdWx0cyA8LSBmdW5jdGlvbihkaXJfbmFtZSwgZGVwdGgsIGJhc2UgPSBGQUxTRSwgc2hvd19oZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IDEsIHdyaXRlX2ZsYWcgPSBGQUxTRSkgewogIGlmICh2ZXJib3NlID49IDEpIHsKICAgIGluZm9ybShwYXN0ZTAocGFzdGUocmVwKCIqIiwgZGVwdGgpLCBjb2xsYXBzZSA9ICIiKSwgYmFzZW5hbWUoZGlyX25hbWUpKSkKICB9CgogIGlmIChkZXB0aCA9PSAxKSB7CiAgICBoZWFkZXJfdGVtcGxhdGUgPC0gIlxuXG4lcyAlcyB7LnRhYnNldCAudGFic2V0LXBpbGxzIC50YWJzZXQtdm1vZGVybn1cblxuIgogIH0gZWxzZSB7CiAgICBpZiAoYmFzZSB8fCAhZXhwZXJpbWVudF9leGlzdHMoZGlyX25hbWUpKSB7CiAgICAgIGhlYWRlcl90ZW1wbGF0ZSA8LSAiXG5cbiVzICVzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9XG5cbiIKICAgIH0gZWxzZSB7CiAgICAgIGhlYWRlcl90ZW1wbGF0ZSA8LSAiXG5cbiVzICVzIHsudGFic2V0IC50YWJzZXQtcGlsbHMgLnRhYnNldC1jaXJjbGV9XG5cbiIKICAgIH0KICB9CgogIHJlc3VsdHMgPC0gYygpCiAgaWYgKHNob3dfaGVhZGVyKSB7CiAgICByZXN1bHRzIDwtIHNwcmludGYoCiAgICAgIGhlYWRlcl90ZW1wbGF0ZSwKICAgICAgcGFzdGUocmVwKCIjIiwgZGVwdGgpLCBjb2xsYXBzZSA9ICIiKSwKICAgICAgYmFzZW5hbWUoZGlyX25hbWUpCiAgICApICU+JQogICAgICB3cml0ZShvbGRfdGV4dCA9IHJlc3VsdHMsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnKQogIH0KCiAgaWYgKGJhc2UpIHsKICAgIHJlc3VsdHMgPC0gc3ByaW50ZigKICAgICAgIlxuXG4lcyBCYXNlIC0gJXMgey50YWJzZXQgLnRhYnNldC1waWxscyAudGFic2V0LWNpcmNsZX1cblxuIiwKICAgICAgcGFzdGUocmVwKCIjIiwgZGVwdGggKyAxKSwgY29sbGFwc2UgPSAiIiksCiAgICAgIGJhc2VuYW1lKGRpcl9uYW1lKQogICAgKSAlPiUKICAgICAgd3JpdGUob2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZykKICAgIGRlcHRoIDwtIGRlcHRoICsgMQogIH0KCiAgc2hvd3R5cGVfdGVtcGxhdGUgPC0gcGFzdGUwKAogICAgIlxuXG4iLCBwYXN0ZShyZXAoIiMiLCBkZXB0aCArIDEpLCBjb2xsYXBzZSA9ICIiKSwgIiAlc1xuXG4iCiAgKQogIGZpZ25hbWVfdGVtcGxhdGUgPC0gcGFzdGUwKAogICAgIlxuXG4iLCBwYXN0ZShyZXAoIiMiLCBkZXB0aCArIDIpLCBjb2xsYXBzZSA9ICIiKSwgIiAlc1xuXG4iCiAgKQogIGludmlzaWJsZV9oZWFkZXIgPC0gcGFzdGUwKAogICAgIlxuXG4iLCBwYXN0ZShyZXAoIiMiLCBkZXB0aCArIDMpLCBjb2xsYXBzZSA9ICIiKSwKICAgICIgey50YWJzZXQgLnRhYnNldC1waWxsc31cblxuIgogICkKICBwbHRfdGVtcGxhdGUgPC0gcGFzdGUwKAogICAgIlxuXG4iLCBwYXN0ZShyZXAoIiMiLCBkZXB0aCArIDQpLCBjb2xsYXBzZSA9ICIiKSwgIiAlc1xuXG4iCiAgKQoKICBpZiAocGFyYW1zJHVzZV9pY29ucykgewogICAgaWYgKHBhcmFtcyR1c2Vfdm1vZGVybikgewogICAgICBldmFsdWF0b3JfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgndGFibGUnLCBmaWxsID0gJ3doaXRlJylgIgogICAgICB2aXN1YWxpemVyX2xhYmVsIDwtICJgciBmb250YXdlc29tZTo6ZmEoJ2NoYXJ0LWJhcicsIGZpbGwgPSAnd2hpdGUnKWAiCiAgICAgIGNvZGVfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgnY29kZScsIGZpbGwgPSAnd2hpdGUnKWAiCiAgICB9IGVsc2UgewogICAgICBldmFsdWF0b3JfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgndGFibGUnKWAiCiAgICAgIHZpc3VhbGl6ZXJfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgnY2hhcnQtYmFyJylgIgogICAgICBjb2RlX2xhYmVsIDwtICJgciBmb250YXdlc29tZTo6ZmEoJ2NvZGUnKWAiCiAgICB9CiAgfSBlbHNlIHsKICAgIGV2YWx1YXRvcl9sYWJlbCA8LSAiRXZhbHVhdG9ycyIKICAgIHZpc3VhbGl6ZXJfbGFiZWwgPC0gIlZpc3VhbGl6ZXJzIgogICAgY29kZV9sYWJlbCA8LSAiVmFyeWluZyBQYXJhbWV0ZXJzIgogIH0KCiAgZXhwX2ZuYW1lIDwtIGZpbGUucGF0aChkaXJfbmFtZSwgImV4cGVyaW1lbnQucmRzIikKICBldmFsX2ZuYW1lIDwtIGZpbGUucGF0aChkaXJfbmFtZSwgImV2YWxfcmVzdWx0cy5yZHMiKQogIHZpel9mbmFtZSA8LSBmaWxlLnBhdGgoZGlyX25hbWUsICJ2aXpfcmVzdWx0cy5yZHMiKQoKICBleHAgPC0gZ2V0X3Jlc3VsdHMoZXhwX2ZuYW1lKQogIGV2YWxfcmVzdWx0cyA8LSBnZXRfcmVzdWx0cyhldmFsX2ZuYW1lKQogIHZpel9yZXN1bHRzIDwtIGdldF9yZXN1bHRzKHZpel9mbmFtZSkKCiAgaWYgKCFpcy5udWxsKGV2YWxfcmVzdWx0cykgJiYgcGFyYW1zJHNob3dfZXZhbCkgewogICAgcmVzdWx0cyA8LSB3cml0ZSgKICAgICAgc3ByaW50ZihzaG93dHlwZV90ZW1wbGF0ZSwgZXZhbHVhdG9yX2xhYmVsKSwKICAgICAgb2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgKQoKICAgIGV2YWxfbmFtZXMgPC0gZ2V0X29iamVjdF9vcmRlcihuYW1lcyhldmFsX3Jlc3VsdHMpLCBwYXJhbXMkZXZhbF9vcmRlcikKICAgIGZvciAoZXZhbF9uYW1lIGluIGV2YWxfbmFtZXMpIHsKICAgICAgZXZhbHVhdG9yIDwtIGV4cCRnZXRfZXZhbHVhdG9ycygpW1tldmFsX25hbWVdXQogICAgICBpZiAoZXZhbHVhdG9yJGRvY19zaG93KSB7CiAgICAgICAgaWYgKHZlcmJvc2UgPj0gMSkgewogICAgICAgICAgaW5mb3JtKHBhc3RlMChwYXN0ZShyZXAoIiAiLCBkZXB0aCArIDEpLCBjb2xsYXBzZSA9ICIiKSwgZXZhbF9uYW1lKSkKICAgICAgICB9CiAgICAgICAgcmVzdWx0cyA8LSB3cml0ZSgKICAgICAgICAgIHNwcmludGYoZmlnbmFtZV90ZW1wbGF0ZSwgZXZhbF9uYW1lKSwKICAgICAgICAgIG9sZF90ZXh0ID0gcmVzdWx0cywgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICAgICApCiAgICAgICAgaWYgKGlzLm51bGwoZXZhbHVhdG9yJGRvY19ucm93cykpIHsKICAgICAgICAgIGV2YWxfcmVzdWx0c19zaG93IDwtIGV2YWxfcmVzdWx0c1tbZXZhbF9uYW1lXV0KICAgICAgICB9IGVsc2UgewogICAgICAgICAga2VlcF9yb3dzIDwtIDE6bWluKGV2YWx1YXRvciRkb2NfbnJvd3MsIG5yb3coZXZhbF9yZXN1bHRzW1tldmFsX25hbWVdXSkpCiAgICAgICAgICBldmFsX3Jlc3VsdHNfc2hvdyA8LSBldmFsX3Jlc3VsdHNbW2V2YWxfbmFtZV1dW2tlZXBfcm93cywgXQogICAgICAgICAgaWYgKG5yb3coZXZhbF9yZXN1bHRzW1tldmFsX25hbWVdXSkgPiBldmFsdWF0b3IkZG9jX25yb3dzKSB7CiAgICAgICAgICAgIG9taXR0ZWRfbnJvd3MgPC0gbnJvdyhldmFsX3Jlc3VsdHNbW2V2YWxfbmFtZV1dKSAtIGV2YWx1YXRvciRkb2NfbnJvd3MKICAgICAgICAgICAgcmVzdWx0cyA8LSB3cml0ZSgKICAgICAgICAgICAgICBzcHJpbnRmKAogICAgICAgICAgICAgICAgIlNob3dpbmcgcHJldmlldyBvZiAlcyByZXN1bHRzLiAlcyByb3dzIGhhdmUgYmVlbiBvbWl0dGVkLlxuXG4iLAogICAgICAgICAgICAgICAgZXZhbF9uYW1lLCBvbWl0dGVkX25yb3dzCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICBvbGRfdGV4dCA9IHJlc3VsdHMsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnCiAgICAgICAgICAgICkKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgaWYgKHdyaXRlX2ZsYWcpIHsKICAgICAgICAgIHJlc3VsdHMgPC0gc3ByaW50ZigKICAgICAgICAgICAgInNob3dfcmVzdWx0cygnJXMnLCAnJXMnLCAnZXZhbHVhdG9yJykiLCBkaXJfbmFtZSwgZXZhbF9uYW1lCiAgICAgICAgICApICU+JQogICAgICAgICAgICB3cml0ZV9jb2RlX2NodW5rKGNodW5rX2FyZ3MgPSAicmVzdWx0cyA9ICdhc2lzJyIpICU+JQogICAgICAgICAgICB3cml0ZShvbGRfdGV4dCA9IHJlc3VsdHMsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBkby5jYWxsKAogICAgICAgICAgICB2dGhlbWVzOjpwcmV0dHlfRFQsCiAgICAgICAgICAgIGMobGlzdChldmFsX3Jlc3VsdHNfc2hvdyksIGV2YWx1YXRvciRkb2Nfb3B0aW9ucykKICAgICAgICAgICkgJT4lCiAgICAgICAgICAgIHZ0aGVtZXM6OnN1YmNodW5raWZ5KGkgPSBjaHVua19pZHgpCiAgICAgICAgICBjaHVua19pZHggPDwtIGNodW5rX2lkeCArIDEKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9CgogIGlmICghaXMubnVsbCh2aXpfcmVzdWx0cykgJiYgcGFyYW1zJHNob3dfdml6KSB7CiAgICByZXN1bHRzIDwtIHdyaXRlKAogICAgICBzcHJpbnRmKHNob3d0eXBlX3RlbXBsYXRlLCB2aXN1YWxpemVyX2xhYmVsKSwKICAgICAgb2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgKQoKICAgIHZpel9uYW1lcyA8LSBnZXRfb2JqZWN0X29yZGVyKG5hbWVzKHZpel9yZXN1bHRzKSwgcGFyYW1zJHZpel9vcmRlcikKICAgIGZvciAodml6X25hbWUgaW4gdml6X25hbWVzKSB7CiAgICAgIHZpc3VhbGl6ZXIgPC0gZXhwJGdldF92aXN1YWxpemVycygpW1t2aXpfbmFtZV1dCiAgICAgIGlmICh2aXN1YWxpemVyJGRvY19zaG93KSB7CiAgICAgICAgaWYgKHZlcmJvc2UgPj0gMSkgewogICAgICAgICAgaW5mb3JtKHBhc3RlMChwYXN0ZShyZXAoIiAiLCBkZXB0aCArIDEpLCBjb2xsYXBzZSA9ICIiKSwgdml6X25hbWUpKQogICAgICAgIH0KICAgICAgICByZXN1bHRzIDwtIHdyaXRlKAogICAgICAgICAgc3ByaW50ZihmaWduYW1lX3RlbXBsYXRlLCB2aXpfbmFtZSksCiAgICAgICAgICBpbnZpc2libGVfaGVhZGVyLAogICAgICAgICAgb2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICAgICkKICAgICAgICBwbHRzIDwtIHZpel9yZXN1bHRzW1t2aXpfbmFtZV1dCiAgICAgICAgaWYgKCFpbmhlcml0cyhwbHRzLCAibGlzdCIpKSB7CiAgICAgICAgICBwbHRzIDwtIGxpc3QocGx0ID0gcGx0cykKICAgICAgICB9CiAgICAgICAgaWYgKGlzLm51bGwobmFtZXMocGx0cykpKSB7CiAgICAgICAgICBuYW1lcyhwbHRzKSA8LSAxOmxlbmd0aChwbHRzKQogICAgICAgIH0KICAgICAgICBmb3IgKHBsdF9uYW1lIGluIG5hbWVzKHBsdHMpKSB7CiAgICAgICAgICBpZiAobGVuZ3RoKHBsdHMpICE9IDEpIHsKICAgICAgICAgICAgcmVzdWx0cyA8LSB3cml0ZSgKICAgICAgICAgICAgICBzcHJpbnRmKHBsdF90ZW1wbGF0ZSwgcGx0X25hbWUpLAogICAgICAgICAgICAgIG9sZF90ZXh0ID0gcmVzdWx0cywgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICAgICAgICAgKQogICAgICAgICAgfQogICAgICAgICAgcGx0IDwtIHBsdHNbW3BsdF9uYW1lXV0KICAgICAgICAgIGlzX3Bsb3QgPC0gaW5oZXJpdHMocGx0LCAicGxvdGx5IikgfHwgCiAgICAgICAgICAgIGluaGVyaXRzKHBsdCwgImdnIikgfHwgCiAgICAgICAgICAgIGluaGVyaXRzKHBsdCwgImdncGxvdCIpCiAgICAgICAgICAKICAgICAgICAgIGlmIChwYXJhbXMkdXNlX3Ztb2Rlcm4gJiYgaXNfcGxvdCkgewogICAgICAgICAgICBjaHVua19hcmdzIDwtICJmaWcuaGVpZ2h0ID0gJXMsIGZpZy53aWR0aCA9ICVzLCBvdXQud2lkdGggPSAnMTAwJSUnLCBhZGQucGFuZWwgPSBUUlVFIgogICAgICAgICAgICBhZGRfY2xhc3MgPC0gInBhbmVsIHBhbmVsLWRlZmF1bHQgcGFkZGVkLXBhbmVsIgogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgY2h1bmtfYXJncyA8LSAiZmlnLmhlaWdodCA9ICVzLCBmaWcud2lkdGggPSAlcywgb3V0LndpZHRoID0gJzEwMCUlJyIKICAgICAgICAgICAgYWRkX2NsYXNzIDwtIE5VTEwKICAgICAgICAgIH0KICAgICAgICAgIAogICAgICAgICAgaWYgKHdyaXRlX2ZsYWcpIHsKICAgICAgICAgICAgcmVzdWx0cyA8LSBzcHJpbnRmKAogICAgICAgICAgICAgICJzaG93X3Jlc3VsdHMoJyVzJywgJyVzJywgJ3Zpc3VhbGl6ZXInKSIsIGRpcl9uYW1lLCB2aXpfbmFtZQogICAgICAgICAgICApICU+JQogICAgICAgICAgICAgIHdyaXRlX2NvZGVfY2h1bmsoCiAgICAgICAgICAgICAgICBjaHVua19hcmdzID0gc3ByaW50ZigKICAgICAgICAgICAgICAgICAgY2h1bmtfYXJncywKICAgICAgICAgICAgICAgICAgdmlzdWFsaXplciRkb2Nfb3B0aW9ucyRoZWlnaHQsIHZpc3VhbGl6ZXIkZG9jX29wdGlvbnMkd2lkdGgKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICApICU+JQogICAgICAgICAgICAgIHdyaXRlKG9sZF90ZXh0ID0gcmVzdWx0cywgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcpCiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICB2dGhlbWVzOjpzdWJjaHVua2lmeShwbHQsCiAgICAgICAgICAgICAgaSA9IGNodW5rX2lkeCwKICAgICAgICAgICAgICBmaWdfaGVpZ2h0ID0gdmlzdWFsaXplciRkb2Nfb3B0aW9ucyRoZWlnaHQsCiAgICAgICAgICAgICAgZmlnX3dpZHRoID0gdmlzdWFsaXplciRkb2Nfb3B0aW9ucyR3aWR0aCwKICAgICAgICAgICAgICBvdGhlcl9hcmdzID0gIm91dC53aWR0aCA9ICcxMDAlJyIsIAogICAgICAgICAgICAgIGFkZF9jbGFzcyA9IGFkZF9jbGFzcwogICAgICAgICAgICApCiAgICAgICAgICAgIGNodW5rX2lkeCA8PC0gY2h1bmtfaWR4ICsgMQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KCiAgaWYgKCFpcy5udWxsKGV4cCkgJiYgcGFyYW1zJHNob3dfY29kZSkgewogICAgaWYgKChsZW5ndGgoZXhwJGdldF92YXJ5X2Fjcm9zcygpJGRncCkgIT0gMCkgfHwKICAgICAgICAobGVuZ3RoKGV4cCRnZXRfdmFyeV9hY3Jvc3MoKSRtZXRob2QpICE9IDApKSB7CiAgICAgIHJlc3VsdHMgPC0gd3JpdGUoCiAgICAgICAgc3ByaW50ZihzaG93dHlwZV90ZW1wbGF0ZSwgY29kZV9sYWJlbCksCiAgICAgICAgIlxuXG4qKlBhcmFtZXRlciBWYWx1ZXMqKlxuXG4iLAogICAgICAgIG9sZF90ZXh0ID0gcmVzdWx0cywgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICAgKQogICAgICBpZiAod3JpdGVfZmxhZykgewogICAgICAgIHJlc3VsdHMgPC0gc3ByaW50ZigKICAgICAgICAgICJzaG93X3Jlc3VsdHMoJyVzJywgTlVMTCwgJ3ZhcnlfcGFyYW1zJykiLCBkaXJfbmFtZQogICAgICAgICkgJT4lCiAgICAgICAgICB3cml0ZV9jb2RlX2NodW5rKGNodW5rX2FyZ3MgPSAibWF4LmhlaWdodD0nMjAwcHgnIikgJT4lCiAgICAgICAgICB3cml0ZShvbGRfdGV4dCA9IHJlc3VsdHMsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnKQogICAgICB9IGVsc2UgewogICAgICAgIHZ0aGVtZXM6OnN1YmNodW5raWZ5KGV4cCRnZXRfdmFyeV9hY3Jvc3MoKSwKICAgICAgICAgIGNodW5rX2lkeCwKICAgICAgICAgIG90aGVyX2FyZ3MgPSAibWF4LmhlaWdodD0nMjAwcHgnIgogICAgICAgICkKICAgICAgICBjaHVua19pZHggPDwtIGNodW5rX2lkeCArIDEKICAgICAgfQogICAgfQogIH0KCiAgcmV0dXJuKHJlc3VsdHMpCn0KCiMnIERpc3BsYXlzIG91dHB1dCBvZiBleHBlcmltZW50IGZvciBhbGwgb2YgaXRzIChzYXZlZCkgZGVzY2VuZGFudHMKIycKIycgQHBhcmFtIGRpcl9uYW1lIG5hbWUgb2YgcGFyZW50IGV4cGVyaW1lbnQgZGlyZWN0b3J5CiMnIEBwYXJhbSBkZXB0aCBwbGFjZWhvbGRlciBmb3IgcmVjdXJzaW9uOyBzaG91bGQgbm90IGJlIG1lc3NlZCB3aXRoCiMnIEBwYXJhbSB3cml0ZV9mbGFnIEJvb2xlYW4gaW5kaWNhdGluZyB3aGV0aGVyIHRvIHdyaXRlIHRleHQgdG8gYSBmaWxlCiMnICAgKHdyaXRlX2ZsYWcgPSBUUlVFKSBvciB0byBjb25zb2xlICh3cml0ZV9mbGFnID0gRkFMU0UpCiMnIEBwYXJhbSB3cml0ZV9maWxlbmFtZSBOYW1lIG9mIGZpbGUgdG8gd3JpdGUgdG8gaWYgd3JpdGVfZmxhZyA9IFRSVUUKIycgQHBhcmFtIC4uLiBvdGhlciBhcmd1bWVudHMgdG8gcGFzcyBpbnRvIHNob3dfcmVzdWx0cygpCnNob3dfZGVzY2VuZGFudF9yZXN1bHRzIDwtIGZ1bmN0aW9uKGRpcl9uYW1lLCBkZXB0aCA9IDEsIHdyaXRlX2ZsYWcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd3JpdGVfZmlsZW5hbWUgPSBOVUxMLCAuLi4pIHsKICBjaGlsZHJlbiA8LSBsaXN0LmRpcnMoZGlyX25hbWUsIHJlY3Vyc2l2ZSA9IEZBTFNFKQogIGlmIChsZW5ndGgoY2hpbGRyZW4pID09IDApIHsKICAgIHJldHVybigpCiAgfQogIGZvciAoY2hpbGRfaWR4IGluIDE6bGVuZ3RoKGNoaWxkcmVuKSkgewogICAgY2hpbGQgPC0gY2hpbGRyZW5bY2hpbGRfaWR4XQogICAgaWYgKCFleHBlcmltZW50X2V4aXN0cyhjaGlsZCwgcmVjdXJzaXZlID0gVFJVRSkpIHsKICAgICAgbmV4dAogICAgfQogICAgaWYgKGV4cGVyaW1lbnRfZXhpc3RzKGNoaWxkLCByZWN1cnNpdmUgPSBGQUxTRSkgJiYKICAgICAgKGV4cGVyaW1lbnRfZXhpc3RzKGxpc3QuZGlycyhjaGlsZCwgcmVjdXJzaXZlID0gVFJVRSlbLTFdKSB8fAogICAgICAgIChkZXB0aCA9PSAxKSkpIHsKICAgICAgYmFzZSA8LSBUUlVFCiAgICB9IGVsc2UgewogICAgICBiYXNlIDwtIEZBTFNFCiAgICB9CiAgICByZXN1bHRzIDwtIHNob3dfcmVzdWx0cyhjaGlsZCwgZGVwdGgsIGJhc2UsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnLCAuLi4pCiAgICBpZiAod3JpdGVfZmxhZykgewogICAgICB3cml0ZV90b19maWxlKHBhdGggPSB3cml0ZV9maWxlbmFtZSwgcmVzdWx0cykKICAgIH0KICAgIHNob3dfZGVzY2VuZGFudF9yZXN1bHRzKGNoaWxkLCBkZXB0aCArIDEsIHdyaXRlX2ZsYWcsIHdyaXRlX2ZpbGVuYW1lLCAuLi4pCiAgfQp9CgojJyBDbGVhbiBvdXRwdXQgZmlsZSAoZS5nLiwgcmVtb3ZlIGV4Y2Vzc2l2ZSBibGFuayBsaW5lcykKIycKIycgQHBhcmFtIHBhdGggUGF0aCB0byBvdXRwdXQgZmlsZQpjbGVhbl9maWxlIDwtIGZ1bmN0aW9uKHBhdGgpIHsKICBzdG9yZWxpbmVzIDwtIHJlYWRMaW5lcyhwYXRoKQogIHJsZV9vdXQgPC0gcmxlKHN0b3JlbGluZXMgPT0gIiIpCiAgbGluZV9pZHMgPC0gd2hpY2goKHJsZV9vdXQkbGVuZ3RocyA+IDIpICYgcmxlX291dCR2YWx1ZXMpCiAga2VlcF9saW5lcyA8LSByZXAoVFJVRSwgbGVuZ3RoKHN0b3JlbGluZXMpKQogIGZvciAobGluZV9pZCBpbiBsaW5lX2lkcykgewogICAgbnVtX2JsYW5rIDwtIHJsZV9vdXQkbGVuZ3Roc1tsaW5lX2lkXQogICAgbGluZV9wdHIgPC0gc3VtKHJsZV9vdXQkbGVuZ3Roc1sxOmxpbmVfaWRdKQogICAgIyBvbmx5IGFsbG93IGZvciBtYXggb2YgdHdvIGNvbnNlY3V0aXZlIGJsYW5rIGxpbmVzCiAgICBrZWVwX2xpbmVzWyhsaW5lX3B0ciAtIG51bV9ibGFuayArIDMpOmxpbmVfcHRyXSA8LSBGQUxTRQogIH0KICB3cml0ZUxpbmVzKHN0b3JlbGluZXNba2VlcF9saW5lc10sIHBhdGgpCn0KCiMnIEluc2VydCBsaW5lcyB0byBhZGQgZXh0cmEgcmVzb3VyY2VzIChjc3MvanMpIGZvciBzaW1DaGVmIFIgTWFya2Rvd24gdGhlbWUKIycgCiMnIEBwYXJhbSBwYXRoIFBhdGggdG8gb3V0cHV0IGZpbGUKaW5zZXJ0X3NpbUNoZWZfcmVzb3VyY2VzIDwtIGZ1bmN0aW9uKHBhdGgpIHsKICBzdG9yZWxpbmVzIDwtIHJlYWRMaW5lcyhwYXRoKQogIHBhdHRlcm4gPC0gIjxJbnNlcnQgZXh0cmEgc2ltQ2hlZiByZXNvdXJjZXMgaGVyZT4iCiAgcmVwbGFjZSA8LSBzcHJpbnRmKAogICAgJzxzY3JpcHQgc3JjPSIlcyI+PC9zY3JpcHQ+XG5cbjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iJXMiPicsIAogICAgc3lzdGVtLmZpbGUoInJtZCIsICJqcyIsICJzaW1jaGVmTmF2Q2xhc3MuanMiLAogICAgICAgICAgICAgICAgcGFja2FnZSA9IHV0aWxzOjpwYWNrYWdlTmFtZSgpKSwKICAgIHN5c3RlbS5maWxlKCJybWQiLCAiY3NzIiwgInNpbWNoZWYuY3NzIiwKICAgICAgICAgICAgICAgIHBhY2thZ2UgPSB1dGlsczo6cGFja2FnZU5hbWUoKSkKICApIAogIHN0b3JlbGluZXNbc3RvcmVsaW5lcyA9PSBwYXR0ZXJuXSA8LSByZXBsYWNlCiAgd3JpdGVMaW5lcyhzdG9yZWxpbmVzLCBwYXRoKQp9CgojJyBSZW1vdmUgbGluZXMgd2l0aCBzaW1DaGVmIFIgTWFya2Rvd24gdGhlbWUtc3BlY2lmaWMgY29kZQojJyAKIycgQHBhcmFtIHBhdGggUGF0aCB0byBvdXRwdXQgZmlsZQpyZW1vdmVfc2ltQ2hlZl9yZXNvdXJjZXMgPC0gZnVuY3Rpb24ocGF0aCkgewogIHN0b3JlbGluZXMgPC0gcmVhZExpbmVzKHBhdGgpCiAgCiAgcGF0dGVybiA8LSAiYWRkLnBhbmVsID0gZnVuY3Rpb24iCiAgbGluZV9pZCA8LSB3aGljaChzdHJpbmdyOjpzdHJfZGV0ZWN0KHN0b3JlbGluZXMsIHBhdHRlcm4pKQogIHJlbW92ZV9saW5lcyA8LSAobGluZV9pZCAtIDIpOihsaW5lX2lkICsgNSkKICBzdG9yZWxpbmVzIDwtIHN0b3JlbGluZXNbLXJlbW92ZV9saW5lc10KICAKICBwYXR0ZXJuIDwtICI8SW5zZXJ0IGV4dHJhIHNpbUNoZWYgcmVzb3VyY2VzIGhlcmU+IgogIHJlbW92ZV9saW5lcyA8LSB3aGljaChzdHJpbmdyOjpzdHJfZGV0ZWN0KHN0b3JlbGluZXMsIHBhdHRlcm4pKQogIHN0b3JlbGluZXMgPC0gc3RvcmVsaW5lc1stcmVtb3ZlX2xpbmVzXQogIAogIHdyaXRlTGluZXMoc3RvcmVsaW5lcywgcGF0aCkKfQoKYGBgCgpgYGB7cn0KaWYgKHBhcmFtcyR3cml0ZSkgewogIGlmIChwYXJhbXMkdXNlX3Ztb2Rlcm4pIHsKICAgIGluc2VydF9zaW1DaGVmX3Jlc291cmNlcyh3cml0ZV9maWxlbmFtZSkKICB9IGVsc2UgewogICAgcmVtb3ZlX3NpbUNoZWZfcmVzb3VyY2VzKHdyaXRlX2ZpbGVuYW1lKQogIH0KfSBlbHNlIHsKICBpZiAocGFyYW1zJHVzZV92bW9kZXJuKSB7CiAgICBodG1sdG9vbHM6OkhUTUwoJzxzY3JpcHQgc3JjPSJqcy9zaW1jaGVmTmF2Q2xhc3MuanMiPjwvc2NyaXB0PlxuXG48bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9ImNzcy9zaW1jaGVmLmNzcyI+JykKICB9Cn0KYGBgCgoKIyBTaW11bGF0aW9uIEV4cGVyaW1lbnQgUmVjaXBlIHsudGFic2V0IC50YWJzZXQtdm1vZGVybn0KCiMjIE9iamVjdGl2ZXMKCmBgYHtyIG9iamVjdGl2ZXMsIHJlc3VsdHMgPSAiYXNpcyJ9CmlmIChwYXJhbXMkdXNlX3Ztb2Rlcm4pIHsKICBvYmplY3RpdmVzIDwtIHdyaXRlKAogICAgIlxuXG48ZGl2IGNsYXNzPSdwYW5lbCBwYW5lbC1kZWZhdWx0IHBhZGRlZC1wYW5lbCc+XG5cbiIsCiAgICBwYXN0ZU1kKGZpbGUucGF0aChkb2NfZGlyLCAib2JqZWN0aXZlcy5tZCIpKSwKICAgICJcblxuPC9kaXY+XG5cbiIsCiAgICB3cml0ZV9mbGFnID0gcGFyYW1zJHdyaXRlCiAgKQp9IGVsc2UgewogIG9iamVjdGl2ZXMgPC0gd3JpdGUoCiAgICBwYXN0ZU1kKGZpbGUucGF0aChkb2NfZGlyLCAib2JqZWN0aXZlcy5tZCIpKSwKICAgIHdyaXRlX2ZsYWcgPSBwYXJhbXMkd3JpdGUKICApCn0KaWYgKHBhcmFtcyR3cml0ZSkgewogIHdyaXRlX3RvX2ZpbGUocGF0aCA9IHdyaXRlX2ZpbGVuYW1lLCAiXG5cbiMjIE9iamVjdGl2ZXNcblxuIiwgb2JqZWN0aXZlcykKfQpgYGAKCiMjIERhdGEgR2VuZXJhdGlvbgoKYGBge3IgZGdwcywgcmVzdWx0cyA9ICJhc2lzIn0KZGdwX3JlY2lwZSA8LSBzaG93X3JlY2lwZShmaWVsZF9uYW1lID0gImRncCIsIHdyaXRlX2ZsYWcgPSBwYXJhbXMkd3JpdGUpCmlmIChwYXJhbXMkd3JpdGUpIHsKICB3cml0ZV90b19maWxlKHBhdGggPSB3cml0ZV9maWxlbmFtZSwgIlxuXG4jIyBEYXRhIEdlbmVyYXRpb25cblxuIiwgZGdwX3JlY2lwZSkKfQpgYGAKCiMjIE1ldGhvZHMgYW5kIEV2YWx1YXRpb24KCiMjIyBNZXRob2RzCgpgYGB7ciBtZXRob2RzLCByZXN1bHRzID0gImFzaXMifQptZXRob2RfcmVjaXBlIDwtIHNob3dfcmVjaXBlKGZpZWxkX25hbWUgPSAibWV0aG9kIiwgd3JpdGVfZmxhZyA9IHBhcmFtcyR3cml0ZSkKaWYgKHBhcmFtcyR3cml0ZSkgewogIHdyaXRlX3RvX2ZpbGUoCiAgICBwYXRoID0gd3JpdGVfZmlsZW5hbWUsCiAgICAiXG5cbiMjIE1ldGhvZHMgYW5kIEV2YWx1YXRpb25cblxuIiwgIlxuXG4jIyMgTWV0aG9kc1xuXG4iLAogICAgbWV0aG9kX3JlY2lwZQogICkKfQpgYGAKCiMjIyBFdmFsdWF0aW9uCgpgYGB7ciBldmFsdWF0b3JzLCByZXN1bHRzID0gImFzaXMifQpldmFsX3JlY2lwZSA8LSBzaG93X3JlY2lwZShmaWVsZF9uYW1lID0gImV2YWx1YXRvciIsIHdyaXRlX2ZsYWcgPSBwYXJhbXMkd3JpdGUpCmlmIChwYXJhbXMkd3JpdGUpIHsKICB3cml0ZV90b19maWxlKHBhdGggPSB3cml0ZV9maWxlbmFtZSwgIlxuXG4jIyMgRXZhbHVhdGlvblxuXG4iLCBldmFsX3JlY2lwZSkKfQpgYGAKCiMjIFZpc3VhbGl6YXRpb25zCgpgYGB7ciB2aXN1YWxpemVycywgcmVzdWx0cyA9ICJhc2lzIn0Kdml6X3JlY2lwZSA8LSBzaG93X3JlY2lwZShmaWVsZF9uYW1lID0gInZpc3VhbGl6ZXIiLCB3cml0ZV9mbGFnID0gcGFyYW1zJHdyaXRlKQppZiAocGFyYW1zJHdyaXRlKSB7CiAgd3JpdGVfdG9fZmlsZShwYXRoID0gd3JpdGVfZmlsZW5hbWUsICJcblxuIyMgVmlzdWFsaXphdGlvbnNcblxuIiwgdml6X3JlY2lwZSkKfQpgYGAKCgoKYGBge3IgcmVzLCByZXN1bHRzID0gImFzaXMifQppZiAocGFyYW1zJHZlcmJvc2UgPiAwKSB7CiAgaW5mb3JtKHNwcmludGYoIkNyZWF0aW5nIFIgTWFya2Rvd24gcmVwb3J0IGZvciAlcy4uLiIsIHBhcmFtcyRzaW1fbmFtZSkpCn0KCiMgc2hvdyByZXN1bHRzCmlmIChleHBlcmltZW50X2V4aXN0cyhwYXJhbXMkc2ltX3BhdGgpKSB7CiAgYmFzZV9oZWFkZXIgPC0gd3JpdGUoCiAgICBzcHJpbnRmKCJcblxuIyBCYXNlICVzIFxuXG4iLCBwYXJhbXMkc2ltX25hbWUpLAogICAgIlxuXG4jIyB7LnRhYnNldCAudGFic2V0LXBpbGxzIC50YWJzZXQtY2lyY2xlfVxuXG4iLAogICAgd3JpdGVfZmxhZyA9IHBhcmFtcyR3cml0ZQogICkKICBiYXNlX3Jlc3VsdHMgPC0gc2hvd19yZXN1bHRzKAogICAgcGFyYW1zJHNpbV9wYXRoLAogICAgZGVwdGggPSAyLCBiYXNlID0gRkFMU0UsIHNob3dfaGVhZGVyID0gRkFMU0UsCiAgICB2ZXJib3NlID0gcGFyYW1zJHZlcmJvc2UsIHdyaXRlX2ZsYWcgPSBwYXJhbXMkd3JpdGUKICApCgogIGlmIChwYXJhbXMkd3JpdGUpIHsKICAgIHdyaXRlX3RvX2ZpbGUocGF0aCA9IHdyaXRlX2ZpbGVuYW1lLCBiYXNlX2hlYWRlciwgYmFzZV9yZXN1bHRzKQogIH0KfQoKc2hvd19kZXNjZW5kYW50X3Jlc3VsdHMoCiAgcGFyYW1zJHNpbV9wYXRoLAogIHZlcmJvc2UgPSBwYXJhbXMkdmVyYm9zZSwKICB3cml0ZV9mbGFnID0gcGFyYW1zJHdyaXRlLCAKICB3cml0ZV9maWxlbmFtZSA9IHdyaXRlX2ZpbGVuYW1lCikKYGBgCgoKCmBgYHtyfQppZiAocGFyYW1zJHdyaXRlKSB7CiAgY2xlYW5fZmlsZShwYXRoID0gd3JpdGVfZmlsZW5hbWUpCn0KYGBgCg==