Simulation Experiment Recipe

Objectives

The objective of this simulation experiment is to provide a toy 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 at the surface-level with the sole purpose of facilitating an easy-to-understand walkthrough.

[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 Gaussian DGP

In the Linear Gaussian DGP, we simulate the feature/design matrix \(\mathbf{X} \in \mathbb{R}^{n \times p}\) from a normal distribution and the response vector \(\mathbf{y} \in \mathbb{R}^n\) from a linear model. Specifically,

\[\begin{gather*}\mathbf{X} \sim N\left(\mathbf{0}, \begin{pmatrix} 1 & \rho \\ \rho & 1 \end{pmatrix}\right), \\\mathbf{y} = \mathbf{X} \boldsymbol{\beta} + \boldsymbol{\epsilon},\\\boldsymbol{\epsilon} \sim N(\mathbf{0}, \sigma^2 \mathbf{I}_n)\end{gather*}\]

Default Parameters in DGP

  • Number of samples: \(n = 200\)
  • Number of features: \(p = 2\)
  • Correlation among features: \(\rho = 0\)
  • 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, beta, rho, sigma) {
#>   cov_mat <- matrix(c(1, rho, rho, 1), byrow = T, nrow = 2, ncol = 2)
#>   X <- MASS::mvrnorm(n = n, mu = rep(0, 2), Sigma = cov_mat)
#>   y <- X %*% beta + rnorm(n, sd = sigma)
#>   return(list(X = X, y = y))
#> }
#> <bytecode: 0x564b4ddbb488>

Input Parameters

#> $n
#> [1] 200
#> 
#> $beta
#> [1] 1 0
#> 
#> $rho
#> [1] 0
#> 
#> $sigma
#> [1] 1

Methods and Evaluation

Methods

OLS

Given some data \(\mathbf{X}\) and \(\mathbf{y}\), we fit ordinary least squares (OLS) and examine the p-values for each coefficient in the model. The p-values are computed using a two-sided t-test (see summary.lm()).

Elaborating further on the testing, we are interested in testing:

\[\begin{align*}H_0: \beta_i = 0 \quad vs. \quad H_1: \beta_i \neq 0.\end{align*}\]

To test this, we compute the observed T-statistic, defined as

\[\begin{align*}T = \frac{\hat{\beta}_i}{\hat{SE}(\hat{\beta_i})},\end{align*}\]

and then compute the two-sided p-value under the t distribution with \(n - p - 1\) degrees of freedom. If the p-value is lower than some significance value \(\alpha\), then there is sufficient evidence to reject the null hypothesis \(H_0\). Otherwise, there is not sufficient evidence, and we fail to reject the null hypothesis.

[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, y, cols = c("X1", "X2")) {
#>   lm_fit <- lm(y ~ X)
#>   pvals <- summary(lm_fit)$coefficients[cols, "Pr(>|t|)"] %>%
#>     setNames(paste(names(.), "p-value"))
#>   return(pvals)
#> }
#> <bytecode: 0x564b4d193390>

Input Parameters

#> list()

Evaluation

Rejection Prob. (alpha = 0.1)

We define the rejection probability as the proportion of repetitions in the simulation experiment that result in a p-value \(\leq \alpha\). Here, we choose to set the significance level \(\alpha = 0.1\).

[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? As this simulation experiment is a contrived example, we omit the “why” here.]

Base

Function

#> function(fit_results, alpha = 0.05) {
#>   group_vars <- c(".dgp_name", ".method_name")
#>   eval_out <- fit_results %>%
#>     dplyr::group_by(across({{group_vars}})) %>%
#>     dplyr::summarise(
#>       `X1 Reject Prob.` = mean(`X1 p-value` < alpha),
#>       `X2 Reject Prob.` = mean(`X2 p-value` < alpha)
#>     )
#>   return(eval_out)
#> }

Input Parameters

#> $alpha
#> [1] 0.1
Linear Gaussian DGP - Varying beta

Function

#> function(fit_results, vary_params = NULL, alpha = 0.05) {
#>   group_vars <- c(".dgp_name", ".method_name", vary_params)
#>   eval_out <- fit_results %>%
#>     dplyr::group_by(across({{group_vars}})) %>%
#>     dplyr::summarise(
#>       `X1 Reject Prob.` = mean(`X1 p-value` < alpha),
#>       `X2 Reject Prob.` = mean(`X2 p-value` < alpha)
#>     )
#>   return(eval_out)
#> }
#> <bytecode: 0x564b4e919d18>

Input Parameters

#> $alpha
#> [1] 0.1
Linear Gaussian DGP - Varying rho

Function

#> function(fit_results, vary_params = NULL, alpha = 0.05) {
#>   group_vars <- c(".dgp_name", ".method_name", vary_params)
#>   eval_out <- fit_results %>%
#>     dplyr::group_by(across({{group_vars}})) %>%
#>     dplyr::summarise(
#>       `X1 Reject Prob.` = mean(`X1 p-value` < alpha),
#>       `X2 Reject Prob.` = mean(`X2 p-value` < alpha)
#>     )
#>   return(eval_out)
#> }
#> <bytecode: 0x564b4ec36538>

Input Parameters

#> $alpha
#> [1] 0.1
Linear Gaussian DGP - Varying sigma

Function

#> function(fit_results, vary_params = NULL, alpha = 0.05) {
#>   group_vars <- c(".dgp_name", ".method_name", vary_params)
#>   eval_out <- fit_results %>%
#>     dplyr::group_by(across({{group_vars}})) %>%
#>     dplyr::summarise(
#>       `X1 Reject Prob.` = mean(`X1 p-value` < alpha),
#>       `X2 Reject Prob.` = mean(`X2 p-value` < alpha)
#>     )
#>   return(eval_out)
#> }

Input Parameters

#> $alpha
#> [1] 0.1

Visualizations

Power

To examine the power of the test, we plot the rejection probability as a function of \(\alpha\), that is, \(\mathbb{P}(\text{p-value} \leq \alpha)\) vs. \(\alpha\). If the coefficient is non-zero in the underlying DGP, then a larger AUC would indicate better performance in terms of the power. We will primarily focus on plotting the power of the first coefficient \(\beta_1\).

[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? As this simulation experiment is a contrived example, we omit the “why” here.]

Base

Function

#> function(fit_results, col = "X1") {
#>   plt <- ggplot2::ggplot(fit_results) +
#>     ggplot2::aes(x = .data[[paste(col, "p-value")]],
#>                  color = as.factor(.method_name)) +
#>     ggplot2::geom_abline(slope = 1, intercept = 0,
#>                          color = "darkgray", linetype = "solid", size = 1) +
#>     ggplot2::stat_ecdf(size = 1) +
#>     ggplot2::scale_x_continuous(limits = c(0, 1)) +
#>     ggplot2::labs(x = "t", y = "P( p-value \u2264 t )",
#>                   linetype = "", color = "Method")
#>   return(plt)
#> }

Input Parameters

#> list()
Linear Gaussian DGP - Varying beta

Function

#> function(fit_results, vary_params = NULL, col = "X1") {
#> 
#>   if (!is.null(vary_params)) {
#>     # deal with the case when we vary across a parameter that is vector-valued
#>     if (is.list(fit_results[[vary_params]])) {
#>       fit_results[[vary_params]] <- list_col_to_chr(fit_results[[vary_params]],
#>                                                     name = vary_params,
#>                                                     verbatim = TRUE)
#>     }
#>   }
#> 
#>   plt <- ggplot2::ggplot(fit_results) +
#>     ggplot2::aes(x = .data[[paste(col, "p-value")]],
#>                  color = as.factor(.method_name)) +
#>     ggplot2::geom_abline(slope = 1, intercept = 0,
#>                          color = "darkgray", linetype = "solid", linewidth = 1) +
#>     ggplot2::stat_ecdf(size = 1) +
#>     ggplot2::scale_x_continuous(limits = c(0, 1)) +
#>     ggplot2::labs(x = "t", y = "P( p-value \u2264 t )",
#>                   linetype = "", color = "Method")
#>   if (!is.null(vary_params)) {
#>     plt <- plt + ggplot2::facet_wrap(~ .data[[vary_params]])
#>   }
#>   return(plt)
#> }
#> <bytecode: 0x564b49065620>

Input Parameters

#> list()
Linear Gaussian DGP - Varying rho

Function

#> function(fit_results, vary_params = NULL, col = "X1") {
#> 
#>   if (!is.null(vary_params)) {
#>     # deal with the case when we vary across a parameter that is vector-valued
#>     if (is.list(fit_results[[vary_params]])) {
#>       fit_results[[vary_params]] <- list_col_to_chr(fit_results[[vary_params]],
#>                                                     name = vary_params,
#>                                                     verbatim = TRUE)
#>     }
#>   }
#> 
#>   plt <- ggplot2::ggplot(fit_results) +
#>     ggplot2::aes(x = .data[[paste(col, "p-value")]],
#>                  color = as.factor(.method_name)) +
#>     ggplot2::geom_abline(slope = 1, intercept = 0,
#>                          color = "darkgray", linetype = "solid", linewidth = 1) +
#>     ggplot2::stat_ecdf(size = 1) +
#>     ggplot2::scale_x_continuous(limits = c(0, 1)) +
#>     ggplot2::labs(x = "t", y = "P( p-value \u2264 t )",
#>                   linetype = "", color = "Method")
#>   if (!is.null(vary_params)) {
#>     plt <- plt + ggplot2::facet_wrap(~ .data[[vary_params]])
#>   }
#>   return(plt)
#> }
#> <bytecode: 0x564b48bc18f8>

Input Parameters

#> list()
Linear Gaussian DGP - Varying sigma

Function

#> function(fit_results, vary_params = NULL, col = "X1") {
#> 
#>   if (!is.null(vary_params)) {
#>     # deal with the case when we vary across a parameter that is vector-valued
#>     if (is.list(fit_results[[vary_params]])) {
#>       fit_results[[vary_params]] <- list_col_to_chr(fit_results[[vary_params]],
#>                                                     name = vary_params,
#>                                                     verbatim = TRUE)
#>     }
#>   }
#> 
#>   plt <- ggplot2::ggplot(fit_results) +
#>     ggplot2::aes(x = .data[[paste(col, "p-value")]],
#>                  color = as.factor(.method_name)) +
#>     ggplot2::geom_abline(slope = 1, intercept = 0,
#>                          color = "darkgray", linetype = "solid", linewidth = 1) +
#>     ggplot2::stat_ecdf(size = 1) +
#>     ggplot2::scale_x_continuous(limits = c(0, 1)) +
#>     ggplot2::labs(x = "t", y = "P( p-value \u2264 t )",
#>                   linetype = "", color = "Method")
#>   if (!is.null(vary_params)) {
#>     plt <- plt + ggplot2::facet_wrap(~ .data[[vary_params]])
#>   }
#>   return(plt)
#> }
#> <bytecode: 0x564b47c45170>

Input Parameters

#> list()

Rejection Prob. (alpha = 0.1) Plot

We plot the rejection probability for \(\beta_1\) across varying parameters of the DGP to understand how characteristics of the DGP affect the test.We define the rejection probability as the proportion of repetitions in the simulation experiment that result in a p-value \(\leq \alpha\). Here, we choose to set the significance level \(\alpha = 0.1\).

[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? As this simulation experiment is a contrived example, we omit the “why” here.]

Linear Gaussian DGP - Varying beta

Function

#> function(eval_results, vary_params = NULL,
#>                                  alpha = 0.05) {
#>   eval_results <- eval_results$`Rejection Prob. (alpha = 0.1)`
#>   if (is.list(eval_results[[vary_params]])) {
#>     # deal with the case when we vary across a parameter that is vector-valued
#>     eval_results[[vary_params]] <- list_col_to_chr(eval_results[[vary_params]],
#>                                                    name = vary_params,
#>                                                    verbatim = TRUE)
#>   }
#>   plt <- ggplot2::ggplot(eval_results) +
#>     ggplot2::aes(x = .data[[vary_params]], y = `X1 Reject Prob.`,
#>                  color = as.factor(.method_name),
#>                  fill = as.factor(.method_name)) +
#>     ggplot2::labs(x = vary_params,
#>                   y = sprintf("Rejection Probability (alpha = %s)", alpha),
#>                   color = "Method", fill = "Method") +
#>     ggplot2::scale_y_continuous(limits = c(0, 1))
#>   if (is.numeric(eval_results[[vary_params]])) {
#>     plt <- plt +
#>       ggplot2::geom_line() +
#>       ggplot2::geom_point(size = 2)
#>   } else {
#>     plt <- plt +
#>       ggplot2::geom_bar(stat = "identity")
#>   }
#>   return(plotly::ggplotly(plt))
#> }
#> <bytecode: 0x564b4900dc40>

Input Parameters

#> $alpha
#> [1] 0.1
Linear Gaussian DGP - Varying rho

Function

#> function(eval_results, vary_params = NULL,
#>                                  alpha = 0.05) {
#>   eval_results <- eval_results$`Rejection Prob. (alpha = 0.1)`
#>   if (is.list(eval_results[[vary_params]])) {
#>     # deal with the case when we vary across a parameter that is vector-valued
#>     eval_results[[vary_params]] <- list_col_to_chr(eval_results[[vary_params]],
#>                                                    name = vary_params,
#>                                                    verbatim = TRUE)
#>   }
#>   plt <- ggplot2::ggplot(eval_results) +
#>     ggplot2::aes(x = .data[[vary_params]], y = `X1 Reject Prob.`,
#>                  color = as.factor(.method_name),
#>                  fill = as.factor(.method_name)) +
#>     ggplot2::labs(x = vary_params,
#>                   y = sprintf("Rejection Probability (alpha = %s)", alpha),
#>                   color = "Method", fill = "Method") +
#>     ggplot2::scale_y_continuous(limits = c(0, 1))
#>   if (is.numeric(eval_results[[vary_params]])) {
#>     plt <- plt +
#>       ggplot2::geom_line() +
#>       ggplot2::geom_point(size = 2)
#>   } else {
#>     plt <- plt +
#>       ggplot2::geom_bar(stat = "identity")
#>   }
#>   return(plotly::ggplotly(plt))
#> }
#> <bytecode: 0x564b48b95098>

Input Parameters

#> $alpha
#> [1] 0.1

Base Linear Regression Experiment

Rejection Prob. (alpha = 0.1)

Power

Linear Gaussian DGP

Varying beta

Rejection Prob. (alpha = 0.1)

Power

Rejection Prob. (alpha = 0.1) Plot

Parameter Values

#> $dgp
#> $dgp$`Linear Gaussian DGP`
#> $dgp$`Linear Gaussian DGP`$beta
#> $dgp$`Linear Gaussian DGP`$beta[[1]]
#> [1] 1 0
#> 
#> $dgp$`Linear Gaussian DGP`$beta[[2]]
#> [1] 1.0 0.5
#> 
#> $dgp$`Linear Gaussian DGP`$beta[[3]]
#> [1] 1 1
#> 
#> $dgp$`Linear Gaussian DGP`$beta[[4]]
#> [1] 1.0 1.5
#> 
#> $dgp$`Linear Gaussian DGP`$beta[[5]]
#> [1] 1 2
#> 
#> 
#> 
#> 
#> $method
#> list()

Varying rho

Rejection Prob. (alpha = 0.1)

Power

Rejection Prob. (alpha = 0.1) Plot

Parameter Values

#> $dgp
#> $dgp$`Linear Gaussian DGP`
#> $dgp$`Linear Gaussian DGP`$rho
#> [1] 0.0 0.2 0.5 0.9
#> 
#> 
#> 
#> $method
#> list()

Varying sigma

Rejection Prob. (alpha = 0.1)

Power

Parameter Values

#> $dgp
#> $dgp$`Linear Gaussian DGP`
#> $dgp$`Linear Gaussian DGP`$sigma
#> [1] 1 2 4 8
#> 
#> 
#> 
#> $method
#> list()
LS0tCnRpdGxlOiAiYHIgcGFyYW1zJHNpbV9uYW1lYCIKYXV0aG9yOiAiYHIgcGFyYW1zJGF1dGhvcmAiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKb3V0cHV0OiBybWFya2Rvd246Omh0bWxfZG9jdW1lbnQKcGFyYW1zOgogIGF1dGhvcjogCiAgICBsYWJlbDogIkF1dGhvcjoiCiAgICB2YWx1ZTogIiIKICBzaW1fbmFtZToKICAgIGxhYmVsOiAiU2ltdWxhdGlvbiBFeHBlcmltZW50IE5hbWU6IgogICAgdmFsdWU6ICIiCiAgc2ltX3BhdGg6CiAgICBsYWJlbDogIlBhdGggdG8gU2ltdWxhdGlvbiBFeHBlcmltZW50IEZvbGRlcjoiCiAgICB2YWx1ZTogIiIKICB3cml0ZV9maWxlbmFtZToKICAgIGxhYmVsOiAiT3V0cHV0IEZpbGU6IgogICAgdmFsdWU6ICIiCiAgc2hvd19jb2RlOgogICAgbGFiZWw6ICJTaG93IENvZGU6IgogICAgdmFsdWU6IFRSVUUKICBzaG93X2V2YWw6CiAgICBsYWJlbDogIlNob3cgRXZhbHVhdG9yczoiCiAgICB2YWx1ZTogVFJVRQogIHNob3dfdml6OgogICAgbGFiZWw6ICJTaG93IFZpc3VhbGl6ZXJzOiIKICAgIHZhbHVlOiBUUlVFCiAgZXZhbF9vcmRlcjoKICAgIGxhYmVsOiAiT3JkZXIgb2YgRXZhbHVhdG9yczoiCiAgICB2YWx1ZTogTlVMTAogIHZpel9vcmRlcjoKICAgIGxhYmVsOiAiT3JkZXIgb2YgVmlzdWFsaXplcnM6IgogICAgdmFsdWU6IE5VTEwKICB1c2VfaWNvbnM6CiAgICBsYWJlbDogIlVzZSBJY29uczoiCiAgICB2YWx1ZTogVFJVRQogIHVzZV92bW9kZXJuOgogICAgbGFiZWw6ICJVc2UgdnRoZW1lczo6dm1vZGVybjoiCiAgICB2YWx1ZTogVFJVRQogIHdyaXRlOgogICAgbGFiZWw6ICJXcml0ZSBGaWxlOiIKICAgIHZhbHVlOiBGQUxTRQogIHZlcmJvc2U6CiAgICBsYWJlbDogIlZlcmJvc2UgTGV2ZWw6IgogICAgdmFsdWU6IDIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBGQUxTRSwKICB3YXJuaW5nID0gRkFMU0UsCiAgbWVzc2FnZSA9IEZBTFNFLAogIGNhY2hlID0gRkFMU0UsCiAgZmlnLmFsaWduID0gImNlbnRlciIsCiAgZmlnLnBvcyA9ICJIIiwKICBmaWcuaGVpZ2h0ID0gMTIsCiAgZmlnLndpZHRoID0gMTAKKQoKb3B0aW9ucygKICB3aWR0aCA9IDEwMDAwLAogIGtuaXRyLmthYmxlLk5BID0gIk5BIgopCgojIHNjcm9sbGFibGUgdGV4dCBvdXRwdXQKbG9jYWwoewogIGhvb2tfb3V0cHV0IDwtIGtuaXRyOjprbml0X2hvb2tzJGdldCgib3V0cHV0IikKICBrbml0cjo6a25pdF9ob29rcyRzZXQob3V0cHV0ID0gZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgaWYgKCFpcy5udWxsKG9wdGlvbnMkbWF4LmhlaWdodCkpIHsKICAgICAgb3B0aW9ucyRhdHRyLm91dHB1dCA8LSBjKAogICAgICAgIG9wdGlvbnMkYXR0ci5vdXRwdXQsCiAgICAgICAgc3ByaW50Zignc3R5bGU9Im1heC1oZWlnaHQ6ICVzOyInLCBvcHRpb25zJG1heC5oZWlnaHQpCiAgICAgICkKICAgIH0KICAgIGhvb2tfb3V0cHV0KHgsIG9wdGlvbnMpCiAgfSkKfSkKCmNodW5rX2lkeCA8LSAxCmRvY19kaXIgPC0gZmlsZS5wYXRoKHBhcmFtcyRzaW1fcGF0aCwgImRvY3MiKQp3cml0ZV9maWxlbmFtZSA8LSBwYXJhbXMkd3JpdGVfZmlsZW5hbWUKYGBgCgpgYGB7ciBoZWxwZXItZnVuc30KCiMnIFdyYXAgdGV4dC9jb2RlIGluIGtuaXRyIGNvZGUgY2h1bmsgc3RyaW5nCiMnCiMnIEBwYXJhbSBjb2RlIFN0cmluZyBvZiBjb2RlIHRvIHdyYXAgaW4ga25pdHIgY29kZSBjaHVuawojJyBAcGFyYW0gY2h1bmtfYXJncyBTdHJpbmcgb2YgYXJndW1lbnRzIHRvIHBsYWNlIGluIHRoZSBrbml0ciBjb2RlIGNodW5rIGhlYWRlcgojJyBAcmV0dXJuIFN0cmluZyBvZiBjb2RlLCB3cmFwcGVkIGluc2lkZSB0aGUga25pdHIgY29kZSBjaHVuayBgYGAgbWFya2Vycwp3cml0ZV9jb2RlX2NodW5rIDwtIGZ1bmN0aW9uKGNvZGUgPSAiIiwgY2h1bmtfYXJncyA9ICIiKSB7CiAgc3ByaW50ZigiXG5gYGB7ciwgJXN9XG4lc1xuYGBgXG4iLCBjaHVua19hcmdzLCBjb2RlKQp9CgojJyBXcml0ZSB0ZXh0IHRvIHZlY3RvciAod3JpdGVfZmxhZyA9IFRSVUUpIG9yIHRvIGNvbnNvbGUgKHdyaXRlX2ZsYWcgPSBGQUxTRSkKIycKIycgQHBhcmFtIC4uLiBUZXh0IHRvIHdyaXRlIHRvIHZlY3RvciBvciB0byBjb25zb2xlCiMnIEBwYXJhbSBvbGRfdGV4dCBQcmV2aW91cyB0ZXh0IHRvIGFwcGVuZCB0byB3aGVuIHdyaXRpbmcgdG8gYSB2ZWN0b3IKIycgQHBhcmFtIHdyaXRlX2ZsYWcgQm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdG8gd3JpdGUgdGV4dCB0byBhIHZlY3RvcgojJyAgICh3cml0ZV9mbGFnID0gVFJVRSkgb3IgdG8gY29uc29sZSAod3JpdGVfZmxhZyA9IEZBTFNFKQojJyBAcmV0dXJuIElmIHdyaXRlX2ZsYWcgPSBUUlVFLCByZXR1cm5zIHZlY3RvciBvZiB0ZXh0LiBPdGhlcndpc2UsIHRleHQgaXMKIycgICB3cml0dGVuIHRvIGNvbnNvbGUgdmlhIGBjYXQoKWAuCndyaXRlIDwtIGZ1bmN0aW9uKC4uLiwgb2xkX3RleHQgPSBOVUxMLCB3cml0ZV9mbGFnKSB7CiAgaWYgKHdyaXRlX2ZsYWcpIHsKICAgIHJldHVybihjKG9sZF90ZXh0LCAuLi4pKQogIH0gZWxzZSB7CiAgICBkb3RzX2xpc3QgPC0gbGlzdCguLi4pICU+JQogICAgICBwdXJycjo6bWFwKAogICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgIGlmIChzdHJpbmdyOjpzdHJfZGV0ZWN0KHgsICJgciAuKmAiKSkgewogICAgICAgICAgICAjIHJ1biByIGNvZGUgYmVmb3JlIHByaW50aW5nIHJlc3VsdHMgaW4gY2F0KCkKICAgICAgICAgICAgb3V0IDwtIHN0cmluZ3I6OnN0cl9yZXBsYWNlKAogICAgICAgICAgICAgIHgsICJgciAuKmAiLAogICAgICAgICAgICAgIGV2YWwocGFyc2UodGV4dCA9IHN0cmluZ3I6OnN0cl9leHRyYWN0KHgsICIoPzw9YHIgKSguKj8pKD89YCkiKSkpCiAgICAgICAgICAgICkKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIG91dCA8LSB4CiAgICAgICAgICB9CiAgICAgICAgICByZXR1cm4ob3V0KQogICAgICAgIH0KICAgICAgKQogICAgZG8uY2FsbChjYXQsIGFyZ3MgPSBjKGRvdHNfbGlzdCwgbGlzdChzZXAgPSAiIikpKQogIH0KfQoKIycgV3JpdGUgdGV4dCB0byBmaWxlCiMnCiMnIEBwYXJhbSBwYXRoIFBhdGggdG8gb3V0cHV0IGZpbGUKIycgQHBhcmFtIC4uLiBUZXh0IHRvIG91dHB1dCB0byBmaWxlCndyaXRlX3RvX2ZpbGUgPC0gZnVuY3Rpb24ocGF0aCwgLi4uKSB7CiAgc3RvcmVsaW5lcyA8LSByZWFkTGluZXMocGF0aCkKICBzdG9yZWxpbmVzIDwtIGMoc3RvcmVsaW5lcywgLi4uKQogIHdyaXRlTGluZXMoc3RvcmVsaW5lcywgcGF0aCkKfQoKIycgR2V0IG9yZGVyIG9mIG9iamVjdHMgdG8gZGlzcGxheQojJwojJyBAcGFyYW0gb2JqX25hbWVzIFZlY3RvciBvZiBhbGwgb2JqZWN0IG5hbWVzIHRoYXQgbmVlZCB0byBiZSBkaXNwbGF5ZWQuCiMnIEBwYXJhbSBvYmpfb3JkZXIgVmVjdG9yIG9mIG9iamVjdCBuYW1lcyBpbiB0aGUgZGVzaXJlZCBhcHBlYXJhbmNlIG9yZGVyLgojJyBAcmV0dXJuIFZlY3RvciBvZiBvYmplY3QgbmFtZXMgaW4gdGhlIG9yZGVyIGluIHdoaWNoIHRoZXkgd2lsbCBiZSBkaXNwbGF5ZWQuCmdldF9vYmplY3Rfb3JkZXIgPC0gZnVuY3Rpb24ob2JqX25hbWVzLCBvYmpfb3JkZXIgPSBOVUxMKSB7CiAgaWYgKGlzLm51bGwob2JqX29yZGVyKSkgewogICAgcmV0dXJuKG9ial9uYW1lcykKICB9IGVsc2UgewogICAgcmV0dXJuKGludGVyc2VjdChvYmpfb3JkZXIsIG9ial9uYW1lcykpCiAgfQp9CgojJyBHZXQgYWxsIGV4cGVyaW1lbnRzIHVuZGVyIGEgZ2l2ZW4gZGlyZWN0b3J5IG5hbWUKIycKIycgQHBhcmFtIGRpcl9uYW1lIG5hbWUgb2YgZGlyZWN0b3J5CiMnIEByZXR1cm4gbGlzdCBvZiBuYW1lZCBleHBlcmltZW50cwpnZXRfZGVzY2VuZGFudHMgPC0gZnVuY3Rpb24oZGlyX25hbWUpIHsKICBleHBlcmltZW50cyA8LSBsaXN0KCkKICBmb3IgKGQgaW4gbGlzdC5kaXJzKGRpcl9uYW1lKSkgewogICAgaWYgKGZpbGUuZXhpc3RzKGZpbGUucGF0aChkLCAiZXhwZXJpbWVudC5yZHMiKSkpIHsKICAgICAgaWYgKGlkZW50aWNhbChkLCBwYXJhbXMkc2ltX3BhdGgpKSB7CiAgICAgICAgZXhwX25hbWUgPC0gIkJhc2UiCiAgICAgIH0gZWxzZSB7CiAgICAgICAgZXhwX25hbWUgPC0gc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKAogICAgICAgICAgc3RyaW5ncjo6c3RyX3JlbW92ZShkLCBwYXN0ZTAocGFyYW1zJHNpbV9wYXRoLCAiLyIpKSwKICAgICAgICAgICIvIiwgIiAtICIKICAgICAgICApCiAgICAgIH0KICAgICAgZXhwZXJpbWVudHNbW2V4cF9uYW1lXV0gPC0gcmVhZFJEUyhmaWxlLnBhdGgoZCwgImV4cGVyaW1lbnQucmRzIikpCiAgICB9CiAgfQogIHJldHVybihleHBlcmltZW50cykKfQoKIycgQ2hlY2sgaWYgZXhwZXJpbWVudCBleGlzdHMKIycKIycgQHBhcmFtIGRpcl9uYW1lIG5hbWUgb2YgZGlyZWN0b3J5IG9yIHZlY3RvciB0aGVyZW9mCiMnIEBwYXJhbSByZWN1cnNpdmUgbG9naWNhbDsgaWYgVFJVRSwgY2hlY2tzIGlmIGV4cGVyaW1lbnQgZXhpc3RzIHVuZGVyIHRoZQojJyAgIGdpdmVuIGRpcmVjdG9yeShzKTsgaWYgRkFMU0UsIGNoZWNrcyBpZiBhbnkgZXhwZXJpbWVudCBleGlzdHMgdW5kZXIgdGhlCiMnICAgZGlyZWN0b3J5KHMpIGFuZCBpdHMgZGVzY2VuZGFudHMKIycgQHJldHVybiBUUlVFIGlmIGV4cGVyaW1lbnQgZXhpc3RzIGFuZCBGQUxTRSBvdGhlcndpc2UKZXhwZXJpbWVudF9leGlzdHMgPC0gZnVuY3Rpb24oZGlyX25hbWUsIHJlY3Vyc2l2ZSA9IEZBTFNFKSB7CiAgcmVzIDwtIHB1cnJyOjptYXBfbGdsKAogICAgZGlyX25hbWUsCiAgICBmdW5jdGlvbihkKSB7CiAgICAgIGlmICghcmVjdXJzaXZlKSB7CiAgICAgICAgZXhwX2ZuYW1lIDwtIGZpbGUucGF0aChkLCAiZXhwZXJpbWVudC5yZHMiKQogICAgICAgIHJldHVybihmaWxlLmV4aXN0cyhleHBfZm5hbWUpKQogICAgICB9IGVsc2UgewogICAgICAgIGRlc2NlbmRhbnRzIDwtIGdldF9kZXNjZW5kYW50cyhkKQogICAgICAgIHJldHVybihsZW5ndGgoZGVzY2VuZGFudHMpID4gMCkKICAgICAgfQogICAgfQogICkKICByZXR1cm4oYW55KHJlcykpCn0KCiMnIERpc3BsYXlzIGNvbnRlbnQgZm9yIHNwZWNpZmllZCBwYXJ0IG9mIHJlY2lwZQojJwojJyBAcGFyYW0gZmllbGRfbmFtZSBwYXJ0IG9mIHJlY2lwZSB0byBzaG93OyBtdXN0IGJlIG9uZSBvZiAiZGdwIiwgIm1ldGhvZCIsCiMnICAgImV2YWx1YXRvciIsIG9yICJ2aXN1YWxpemVyIgojJyBAcGFyYW0gd3JpdGVfZmxhZyBCb29sZWFuIGluZGljYXRpbmcgd2hldGhlciB0byB3cml0ZSB0ZXh0IHRvIGEgdmVjdG9yCiMnICAgKHdyaXRlX2ZsYWcgPSBUUlVFKSBvciB0byBjb25zb2xlICh3cml0ZV9mbGFnID0gRkFMU0UpCiMnIEByZXR1cm4gY29udGVudCBmb3IgcmVjaXBlCnNob3dfcmVjaXBlIDwtIGZ1bmN0aW9uKGZpZWxkX25hbWUgPSBjKAogICAgICAgICAgICAgICAgICAgICAgICAgICJkZ3AiLCAibWV0aG9kIiwgImV2YWx1YXRvciIsICJ2aXN1YWxpemVyIgogICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICB3cml0ZV9mbGFnID0gRkFMU0UpIHsKICBmaWVsZF9uYW1lIDwtIG1hdGNoLmFyZyhmaWVsZF9uYW1lKQogIGZ1bmNfbmFtZSA8LSBkcGx5cjo6Y2FzZV93aGVuKAogICAgZmllbGRfbmFtZSA9PSAiZXZhbHVhdG9yIiB+ICJldmFsIiwKICAgIGZpZWxkX25hbWUgPT0gInZpc3VhbGl6ZXIiIH4gInZpeiIsCiAgICBUUlVFIH4gZmllbGRfbmFtZQogICkKICBkZXNjZW5kYW50cyA8LSBnZXRfZGVzY2VuZGFudHMoZGlyX25hbWUgPSBwYXJhbXMkc2ltX3BhdGgpCiAgb2JqcyA8LSBwdXJycjo6bWFwKGRlc2NlbmRhbnRzLCB+IC54W1twYXN0ZTAoImdldF8iLCBmaWVsZF9uYW1lLCAicyIpXV0oKSkKICBvYmpfbmFtZXMgPC0gdW5pcXVlKHB1cnJyOjpyZWR1Y2Uoc2FwcGx5KG9ianMsIG5hbWVzKSwgYykpCgogIGlmIChmaWVsZF9uYW1lICVpbiUgYygibWV0aG9kIiwgImV2YWx1YXRvciIpKSB7CiAgICBvYmpfaGVhZGVyIDwtICJcblxuIyMjIyAlcyB7LnRhYnNldCAudGFic2V0LXBpbGxzIC50YWJzZXQtY2lyY2xlIC50YWJzZXQtcmVjaXBlfVxuXG4iCiAgICBzaG93dHlwZV9oZWFkZXIgPC0gIlxuXG4jIyMjIyAlcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfVxuXG4iCiAgICBleHBfaGVhZGVyIDwtICJcblxuIyMjIyMjICVzIFxuXG4iCiAgfSBlbHNlIHsKICAgIG9ial9oZWFkZXIgPC0gIlxuXG4jIyMgJXMgey50YWJzZXQgLnRhYnNldC1waWxscyAudGFic2V0LWNpcmNsZSAudGFic2V0LXJlY2lwZX1cblxuIgogICAgc2hvd3R5cGVfaGVhZGVyIDwtICJcblxuIyMjIyAlcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfVxuXG4iCiAgICBleHBfaGVhZGVyIDwtICJcblxuIyMjIyMgJXMgXG5cbiIKICB9CgogIGlmIChwYXJhbXMkdXNlX2ljb25zKSB7CiAgICBpZiAocGFyYW1zJHVzZV92bW9kZXJuKSB7CiAgICAgIGRlc2NyaXB0aW9uX2xhYmVsIDwtICJgciBmb250YXdlc29tZTo6ZmEoJ3JlYWRtZScsIGZpbGwgPSAnd2hpdGUnKWAiCiAgICAgIGNvZGVfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgnY29kZScsIGZpbGwgPSAnd2hpdGUnKWAiCiAgICB9IGVsc2UgewogICAgICBkZXNjcmlwdGlvbl9sYWJlbCA8LSAiYHIgZm9udGF3ZXNvbWU6OmZhKCdyZWFkbWUnKWAiCiAgICAgIGNvZGVfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgnY29kZScpYCIKICAgIH0KICB9IGVsc2UgewogICAgZGVzY3JpcHRpb25fbGFiZWwgPC0gIkRlc2NyaXB0aW9uIgogICAgY29kZV9sYWJlbCA8LSAiQ29kZSIKICB9CgogIGlmIChhbGwoc2FwcGx5KG9ianMsIGxlbmd0aCkgPT0gMCkpIHsKICAgIGlmICh3cml0ZV9mbGFnKSB7CiAgICAgIHJldHVybigiTi9BIikKICAgIH0gZWxzZSB7CiAgICAgIHJldHVybihjYXQoIk4vQSIpKQogICAgfQogIH0KCiAgcmVjaXBlIDwtIGMoKQogIGZvciAoaWR4IGluIDE6bGVuZ3RoKG9ial9uYW1lcykpIHsKICAgIG9ial9uYW1lIDwtIG9ial9uYW1lc1tpZHhdCiAgICBkZXNjcmlwdGlvbl9mcGF0aCA8LSBmaWxlLnBhdGgoCiAgICAgIGRvY19kaXIsIHBhc3RlMChmaWVsZF9uYW1lLCAicyIpLCBwYXN0ZTAob2JqX25hbWUsICIubWQiKQogICAgKQogICAgCiAgICBpZiAocGFyYW1zJHVzZV92bW9kZXJuKSB7CiAgICAgIHJlY2lwZSA8LSB3cml0ZSgKICAgICAgICAiXG5cbjxkaXYgY2xhc3M9J3BhbmVsIHBhbmVsLWRlZmF1bHQgcGFkZGVkLXBhbmVsJz5cblxuIiwKICAgICAgICBvbGRfdGV4dCA9IHJlY2lwZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICAgKQogICAgfQoKICAgIHJlY2lwZSA8LSB3cml0ZSgKICAgICAgc3ByaW50ZihvYmpfaGVhZGVyLCBvYmpfbmFtZSksCiAgICAgIHNwcmludGYoc2hvd3R5cGVfaGVhZGVyLCBkZXNjcmlwdGlvbl9sYWJlbCksCiAgICAgIHBhc3RlTWQoZGVzY3JpcHRpb25fZnBhdGgpLAogICAgICBvbGRfdGV4dCA9IHJlY2lwZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICkKCiAgICBpZiAocGFyYW1zJHNob3dfY29kZSkgewogICAgICByZWNpcGUgPC0gd3JpdGUoCiAgICAgICAgc3ByaW50ZihzaG93dHlwZV9oZWFkZXIsIGNvZGVfbGFiZWwpLAogICAgICAgIG9sZF90ZXh0ID0gcmVjaXBlLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICApCgogICAgICBrZWVwX29ianMgPC0gcHVycnI6OmNvbXBhY3QocHVycnI6Om1hcChvYmpzLCBvYmpfbmFtZSkpCiAgICAgIGlzX2lkZW50aWNhbCA8LSBhbGwoCiAgICAgICAgcHVycnI6Om1hcF9sZ2woa2VlcF9vYmpzLCB+IGlzVFJVRShjaGVja19lcXVhbCgueCwga2VlcF9vYmpzW1sxXV0pKSkKICAgICAgKQogICAgICBmb3IgKGV4cCBpbiBuYW1lcyhrZWVwX29ianMpKSB7CiAgICAgICAgb2JqIDwtIGtlZXBfb2Jqc1tbZXhwXV0KICAgICAgICBpZiAoIWlzX2lkZW50aWNhbCkgewogICAgICAgICAgcmVjaXBlIDwtIHdyaXRlKAogICAgICAgICAgICBzcHJpbnRmKGV4cF9oZWFkZXIsIGV4cCksCiAgICAgICAgICAgIG9sZF90ZXh0ID0gcmVjaXBlLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICAgICAgKQogICAgICAgIH0KCiAgICAgICAgcmVjaXBlIDwtIHdyaXRlKAogICAgICAgICAgIlxuXG4qKkZ1bmN0aW9uKipcblxuIiwKICAgICAgICAgIG9sZF90ZXh0ID0gcmVjaXBlLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICAgICkKICAgICAgICBpZiAod3JpdGVfZmxhZykgewogICAgICAgICAgcmVjaXBlIDwtIHNwcmludGYoCiAgICAgICAgICAgICJzaG93X3JlY2lwZSglc19vYmpzLCAnJXMnLCAnJXMnLCB3aGF0ID0gJ2Z1bmN0aW9uJykiLAogICAgICAgICAgICBmdW5jX25hbWUsIG9ial9uYW1lLCBleHAKICAgICAgICAgICkgJT4lCiAgICAgICAgICAgIHdyaXRlX2NvZGVfY2h1bmsoY2h1bmtfYXJncyA9ICJtYXguaGVpZ2h0PScyMDBweCciKSAlPiUKICAgICAgICAgICAgd3JpdGUob2xkX3RleHQgPSByZWNpcGUsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICB2dGhlbWVzOjpzdWJjaHVua2lmeSgKICAgICAgICAgICAgb2JqW1twYXN0ZTAoZnVuY19uYW1lLCAiX2Z1biIpXV0sIGNodW5rX2lkeCwKICAgICAgICAgICAgb3RoZXJfYXJncyA9ICJtYXguaGVpZ2h0PScyMDBweCciCiAgICAgICAgICApCiAgICAgICAgICBjaHVua19pZHggPDwtIGNodW5rX2lkeCArIDEKICAgICAgICB9CgogICAgICAgIHJlY2lwZSA8LSB3cml0ZSgKICAgICAgICAgICJcblxuKipJbnB1dCBQYXJhbWV0ZXJzKipcblxuIiwKICAgICAgICAgIG9sZF90ZXh0ID0gcmVjaXBlLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICAgICkKICAgICAgICBpZiAod3JpdGVfZmxhZykgewogICAgICAgICAgcmVjaXBlIDwtIHNwcmludGYoCiAgICAgICAgICAgICJzaG93X3JlY2lwZSglc19vYmpzLCAnJXMnLCAnJXMnLCB3aGF0ID0gJ3BhcmFtZXRlcnMnKSIsCiAgICAgICAgICAgIGZ1bmNfbmFtZSwgb2JqX25hbWUsIGV4cAogICAgICAgICAgKSAlPiUKICAgICAgICAgICAgd3JpdGVfY29kZV9jaHVuayhjaHVua19hcmdzID0gIm1heC5oZWlnaHQ9JzIwMHB4JyIpICU+JQogICAgICAgICAgICB3cml0ZShvbGRfdGV4dCA9IHJlY2lwZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcpCiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIHZ0aGVtZXM6OnN1YmNodW5raWZ5KAogICAgICAgICAgICBvYmpbW3Bhc3RlMChmdW5jX25hbWUsICJfcGFyYW1zIildXSwgY2h1bmtfaWR4LAogICAgICAgICAgICBvdGhlcl9hcmdzID0gIm1heC5oZWlnaHQ9JzIwMHB4JyIKICAgICAgICAgICkKICAgICAgICAgIGNodW5rX2lkeCA8PC0gY2h1bmtfaWR4ICsgMQogICAgICAgIH0KCiAgICAgICAgaWYgKGlzX2lkZW50aWNhbCkgewogICAgICAgICAgYnJlYWsKICAgICAgICB9CiAgICAgIH0KICAgIH0KCiAgICBpZiAocGFyYW1zJHVzZV92bW9kZXJuKSB7CiAgICAgIHJlY2lwZSA8LSB3cml0ZSgKICAgICAgICAiXG5cbjwvZGl2PlxuXG4iLCBvbGRfdGV4dCA9IHJlY2lwZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICAgKQogICAgfQogIH0KCiAgcmV0dXJuKHJlY2lwZSkKfQoKIycgUmVhZHMgaW4gZmlsZSBpZiBpdCBleGlzdHMgYW5kIHJldHVybnMgTlVMTCBpZiB0aGUgZmlsZSBkb2VzIG5vdCBleGlzdAojJwojJyBAcGFyYW0gZmlsZW5hbWUgbmFtZSBvZiAucmRzIGZpbGUgdG8gdHJ5IHJlYWRpbmcgaW4KIycgQHJldHVybiBvdXRwdXQgb2YgZmlsZW5hbWUucmRzIGlmIHRoZSBmaWxlIGV4aXN0cyBhbmQgTlVMTCBvdGhlcndpc2UKZ2V0X3Jlc3VsdHMgPC0gZnVuY3Rpb24oZmlsZW5hbWUpIHsKICBpZiAoZmlsZS5leGlzdHMoZmlsZW5hbWUpKSB7CiAgICByZXN1bHRzIDwtIHJlYWRSRFMoZmlsZW5hbWUpCiAgfSBlbHNlIHsKICAgIHJlc3VsdHMgPC0gTlVMTAogIH0KICByZXR1cm4ocmVzdWx0cykKfQoKIycgRGlzcGxheXMgb3V0cHV0IChib3RoIGZyb20gZXZhbHVhdGUoKSBhbmQgdmlzdWFsaXplKCkpIGZyb20gc2F2ZWQgcmVzdWx0cyB1bmRlcgojJyBhIHNwZWNpZmllZCBkaXJlY3RvcnkKIycKIycgQHBhcmFtIGRpcl9uYW1lIG5hbWUgb2YgZGlyZWN0b3J5CiMnIEBwYXJhbSBkZXB0aCBpbnRlZ2VyOyBkZXB0aCBvZiBkaXJlY3RvcnkgZnJvbSBwYXJlbnQvYmFzZSBleHBlcmltZW50J3MgZm9sZGVyCiMnIEBwYXJhbSBiYXNlIGxvZ2ljYWw7IHdoZXRoZXIgb3Igbm90IHRoaXMgaXMgYSBiYXNlIGV4cGVyaW1lbnQKIycgQHBhcmFtIHNob3dfaGVhZGVyIGxvZ2ljYWw7IHdoZXRoZXIgb3Igbm90IHRvIHNob3cgc2VjdGlvbiBoZWFkZXIKIycgQHBhcmFtIHZlcmJvc2UgaW50ZWdlcjsgMCA9IG5vIG1lc3NhZ2VzOyAxID0gcHJpbnQgb3V0IGRpcmVjdG9yeSBuYW1lIG9ubHk7CiMnICAgMiA9IHByaW50IG91dCBkaXJlY3RvcnkgbmFtZSBhbmQgbmFtZSBvZiBldmFsdWF0b3JzL3Zpc3VhbGl6ZXJzCiMnIEBwYXJhbSB3cml0ZV9mbGFnIEJvb2xlYW4gaW5kaWNhdGluZyB3aGV0aGVyIHRvIHdyaXRlIHRleHQgdG8gYSB2ZWN0b3IKIycgICAod3JpdGVfZmxhZyA9IFRSVUUpIG9yIHRvIGNvbnNvbGUgKHdyaXRlX2ZsYWcgPSBGQUxTRSkKIycgQHJldHVybiBjb250ZW50IHJlc3VsdHMgZnJvbSBldmFsdWF0ZSgpIGFuZCB2aXN1YWxpemUoKSBmcm9tIHRoZSBleHBlcmltZW50CnNob3dfcmVzdWx0cyA8LSBmdW5jdGlvbihkaXJfbmFtZSwgZGVwdGgsIGJhc2UgPSBGQUxTRSwgc2hvd19oZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IDEsIHdyaXRlX2ZsYWcgPSBGQUxTRSkgewogIGlmICh2ZXJib3NlID49IDEpIHsKICAgIGluZm9ybShwYXN0ZTAocGFzdGUocmVwKCIqIiwgZGVwdGgpLCBjb2xsYXBzZSA9ICIiKSwgYmFzZW5hbWUoZGlyX25hbWUpKSkKICB9CgogIGlmIChkZXB0aCA9PSAxKSB7CiAgICBoZWFkZXJfdGVtcGxhdGUgPC0gIlxuXG4lcyAlcyB7LnRhYnNldCAudGFic2V0LXBpbGxzIC50YWJzZXQtdm1vZGVybn1cblxuIgogIH0gZWxzZSB7CiAgICBpZiAoYmFzZSB8fCAhZXhwZXJpbWVudF9leGlzdHMoZGlyX25hbWUpKSB7CiAgICAgIGhlYWRlcl90ZW1wbGF0ZSA8LSAiXG5cbiVzICVzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9XG5cbiIKICAgIH0gZWxzZSB7CiAgICAgIGhlYWRlcl90ZW1wbGF0ZSA8LSAiXG5cbiVzICVzIHsudGFic2V0IC50YWJzZXQtcGlsbHMgLnRhYnNldC1jaXJjbGV9XG5cbiIKICAgIH0KICB9CgogIHJlc3VsdHMgPC0gYygpCiAgaWYgKHNob3dfaGVhZGVyKSB7CiAgICByZXN1bHRzIDwtIHNwcmludGYoCiAgICAgIGhlYWRlcl90ZW1wbGF0ZSwKICAgICAgcGFzdGUocmVwKCIjIiwgZGVwdGgpLCBjb2xsYXBzZSA9ICIiKSwKICAgICAgYmFzZW5hbWUoZGlyX25hbWUpCiAgICApICU+JQogICAgICB3cml0ZShvbGRfdGV4dCA9IHJlc3VsdHMsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnKQogIH0KCiAgaWYgKGJhc2UpIHsKICAgIHJlc3VsdHMgPC0gc3ByaW50ZigKICAgICAgIlxuXG4lcyBCYXNlIC0gJXMgey50YWJzZXQgLnRhYnNldC1waWxscyAudGFic2V0LWNpcmNsZX1cblxuIiwKICAgICAgcGFzdGUocmVwKCIjIiwgZGVwdGggKyAxKSwgY29sbGFwc2UgPSAiIiksCiAgICAgIGJhc2VuYW1lKGRpcl9uYW1lKQogICAgKSAlPiUKICAgICAgd3JpdGUob2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZykKICAgIGRlcHRoIDwtIGRlcHRoICsgMQogIH0KCiAgc2hvd3R5cGVfdGVtcGxhdGUgPC0gcGFzdGUwKAogICAgIlxuXG4iLCBwYXN0ZShyZXAoIiMiLCBkZXB0aCArIDEpLCBjb2xsYXBzZSA9ICIiKSwgIiAlc1xuXG4iCiAgKQogIGZpZ25hbWVfdGVtcGxhdGUgPC0gcGFzdGUwKAogICAgIlxuXG4iLCBwYXN0ZShyZXAoIiMiLCBkZXB0aCArIDIpLCBjb2xsYXBzZSA9ICIiKSwgIiAlc1xuXG4iCiAgKQogIGludmlzaWJsZV9oZWFkZXIgPC0gcGFzdGUwKAogICAgIlxuXG4iLCBwYXN0ZShyZXAoIiMiLCBkZXB0aCArIDMpLCBjb2xsYXBzZSA9ICIiKSwKICAgICIgey50YWJzZXQgLnRhYnNldC1waWxsc31cblxuIgogICkKICBwbHRfdGVtcGxhdGUgPC0gcGFzdGUwKAogICAgIlxuXG4iLCBwYXN0ZShyZXAoIiMiLCBkZXB0aCArIDQpLCBjb2xsYXBzZSA9ICIiKSwgIiAlc1xuXG4iCiAgKQoKICBpZiAocGFyYW1zJHVzZV9pY29ucykgewogICAgaWYgKHBhcmFtcyR1c2Vfdm1vZGVybikgewogICAgICBldmFsdWF0b3JfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgndGFibGUnLCBmaWxsID0gJ3doaXRlJylgIgogICAgICB2aXN1YWxpemVyX2xhYmVsIDwtICJgciBmb250YXdlc29tZTo6ZmEoJ2NoYXJ0LWJhcicsIGZpbGwgPSAnd2hpdGUnKWAiCiAgICAgIGNvZGVfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgnY29kZScsIGZpbGwgPSAnd2hpdGUnKWAiCiAgICB9IGVsc2UgewogICAgICBldmFsdWF0b3JfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgndGFibGUnKWAiCiAgICAgIHZpc3VhbGl6ZXJfbGFiZWwgPC0gImByIGZvbnRhd2Vzb21lOjpmYSgnY2hhcnQtYmFyJylgIgogICAgICBjb2RlX2xhYmVsIDwtICJgciBmb250YXdlc29tZTo6ZmEoJ2NvZGUnKWAiCiAgICB9CiAgfSBlbHNlIHsKICAgIGV2YWx1YXRvcl9sYWJlbCA8LSAiRXZhbHVhdG9ycyIKICAgIHZpc3VhbGl6ZXJfbGFiZWwgPC0gIlZpc3VhbGl6ZXJzIgogICAgY29kZV9sYWJlbCA8LSAiVmFyeWluZyBQYXJhbWV0ZXJzIgogIH0KCiAgZXhwX2ZuYW1lIDwtIGZpbGUucGF0aChkaXJfbmFtZSwgImV4cGVyaW1lbnQucmRzIikKICBldmFsX2ZuYW1lIDwtIGZpbGUucGF0aChkaXJfbmFtZSwgImV2YWxfcmVzdWx0cy5yZHMiKQogIHZpel9mbmFtZSA8LSBmaWxlLnBhdGgoZGlyX25hbWUsICJ2aXpfcmVzdWx0cy5yZHMiKQoKICBleHAgPC0gZ2V0X3Jlc3VsdHMoZXhwX2ZuYW1lKQogIGV2YWxfcmVzdWx0cyA8LSBnZXRfcmVzdWx0cyhldmFsX2ZuYW1lKQogIHZpel9yZXN1bHRzIDwtIGdldF9yZXN1bHRzKHZpel9mbmFtZSkKCiAgaWYgKCFpcy5udWxsKGV2YWxfcmVzdWx0cykgJiYgcGFyYW1zJHNob3dfZXZhbCkgewogICAgcmVzdWx0cyA8LSB3cml0ZSgKICAgICAgc3ByaW50ZihzaG93dHlwZV90ZW1wbGF0ZSwgZXZhbHVhdG9yX2xhYmVsKSwKICAgICAgb2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgKQoKICAgIGV2YWxfbmFtZXMgPC0gZ2V0X29iamVjdF9vcmRlcihuYW1lcyhldmFsX3Jlc3VsdHMpLCBwYXJhbXMkZXZhbF9vcmRlcikKICAgIGZvciAoZXZhbF9uYW1lIGluIGV2YWxfbmFtZXMpIHsKICAgICAgZXZhbHVhdG9yIDwtIGV4cCRnZXRfZXZhbHVhdG9ycygpW1tldmFsX25hbWVdXQogICAgICBpZiAoZXZhbHVhdG9yJGRvY19zaG93KSB7CiAgICAgICAgaWYgKHZlcmJvc2UgPj0gMSkgewogICAgICAgICAgaW5mb3JtKHBhc3RlMChwYXN0ZShyZXAoIiAiLCBkZXB0aCArIDEpLCBjb2xsYXBzZSA9ICIiKSwgZXZhbF9uYW1lKSkKICAgICAgICB9CiAgICAgICAgcmVzdWx0cyA8LSB3cml0ZSgKICAgICAgICAgIHNwcmludGYoZmlnbmFtZV90ZW1wbGF0ZSwgZXZhbF9uYW1lKSwKICAgICAgICAgIG9sZF90ZXh0ID0gcmVzdWx0cywgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcKICAgICAgICApCiAgICAgICAgaWYgKHdyaXRlX2ZsYWcpIHsKICAgICAgICAgIHJlc3VsdHMgPC0gc3ByaW50ZigKICAgICAgICAgICAgInNob3dfcmVzdWx0cygnJXMnLCAnJXMnLCAnZXZhbHVhdG9yJykiLCBkaXJfbmFtZSwgZXZhbF9uYW1lCiAgICAgICAgICApICU+JQogICAgICAgICAgICB3cml0ZV9jb2RlX2NodW5rKGNodW5rX2FyZ3MgPSAicmVzdWx0cyA9ICdhc2lzJyIpICU+JQogICAgICAgICAgICB3cml0ZShvbGRfdGV4dCA9IHJlc3VsdHMsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBkby5jYWxsKAogICAgICAgICAgICB2dGhlbWVzOjpwcmV0dHlfRFQsCiAgICAgICAgICAgIGMobGlzdChldmFsX3Jlc3VsdHNbW2V2YWxfbmFtZV1dKSwgZXZhbHVhdG9yJGRvY19vcHRpb25zKQogICAgICAgICAgKSAlPiUKICAgICAgICAgICAgdnRoZW1lczo6c3ViY2h1bmtpZnkoaSA9IGNodW5rX2lkeCkKICAgICAgICAgIGNodW5rX2lkeCA8PC0gY2h1bmtfaWR4ICsgMQogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KCiAgaWYgKCFpcy5udWxsKHZpel9yZXN1bHRzKSAmJiBwYXJhbXMkc2hvd192aXopIHsKICAgIHJlc3VsdHMgPC0gd3JpdGUoCiAgICAgIHNwcmludGYoc2hvd3R5cGVfdGVtcGxhdGUsIHZpc3VhbGl6ZXJfbGFiZWwpLAogICAgICBvbGRfdGV4dCA9IHJlc3VsdHMsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnCiAgICApCgogICAgdml6X25hbWVzIDwtIGdldF9vYmplY3Rfb3JkZXIobmFtZXModml6X3Jlc3VsdHMpLCBwYXJhbXMkdml6X29yZGVyKQogICAgZm9yICh2aXpfbmFtZSBpbiB2aXpfbmFtZXMpIHsKICAgICAgdmlzdWFsaXplciA8LSBleHAkZ2V0X3Zpc3VhbGl6ZXJzKClbW3Zpel9uYW1lXV0KICAgICAgaWYgKHZpc3VhbGl6ZXIkZG9jX3Nob3cpIHsKICAgICAgICBpZiAodmVyYm9zZSA+PSAxKSB7CiAgICAgICAgICBpbmZvcm0ocGFzdGUwKHBhc3RlKHJlcCgiICIsIGRlcHRoICsgMSksIGNvbGxhcHNlID0gIiIpLCB2aXpfbmFtZSkpCiAgICAgICAgfQogICAgICAgIHJlc3VsdHMgPC0gd3JpdGUoCiAgICAgICAgICBzcHJpbnRmKGZpZ25hbWVfdGVtcGxhdGUsIHZpel9uYW1lKSwKICAgICAgICAgIGludmlzaWJsZV9oZWFkZXIsCiAgICAgICAgICBvbGRfdGV4dCA9IHJlc3VsdHMsIHdyaXRlX2ZsYWcgPSB3cml0ZV9mbGFnCiAgICAgICAgKQogICAgICAgIHBsdHMgPC0gdml6X3Jlc3VsdHNbW3Zpel9uYW1lXV0KICAgICAgICBpZiAoIWluaGVyaXRzKHBsdHMsICJsaXN0IikpIHsKICAgICAgICAgIHBsdHMgPC0gbGlzdChwbHQgPSBwbHRzKQogICAgICAgIH0KICAgICAgICBpZiAoaXMubnVsbChuYW1lcyhwbHRzKSkpIHsKICAgICAgICAgIG5hbWVzKHBsdHMpIDwtIDE6bGVuZ3RoKHBsdHMpCiAgICAgICAgfQogICAgICAgIGZvciAocGx0X25hbWUgaW4gbmFtZXMocGx0cykpIHsKICAgICAgICAgIGlmIChsZW5ndGgocGx0cykgIT0gMSkgewogICAgICAgICAgICByZXN1bHRzIDwtIHdyaXRlKAogICAgICAgICAgICAgIHNwcmludGYocGx0X3RlbXBsYXRlLCBwbHRfbmFtZSksCiAgICAgICAgICAgICAgb2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICAgICAgICApCiAgICAgICAgICB9CiAgICAgICAgICBwbHQgPC0gcGx0c1tbcGx0X25hbWVdXQogICAgICAgICAgaXNfcGxvdCA8LSBpbmhlcml0cyhwbHQsICJwbG90bHkiKSB8fCAKICAgICAgICAgICAgaW5oZXJpdHMocGx0LCAiZ2ciKSB8fCAKICAgICAgICAgICAgaW5oZXJpdHMocGx0LCAiZ2dwbG90IikKICAgICAgICAgIAogICAgICAgICAgaWYgKHBhcmFtcyR1c2Vfdm1vZGVybiAmJiBpc19wbG90KSB7CiAgICAgICAgICAgIGNodW5rX2FyZ3MgPC0gImZpZy5oZWlnaHQgPSAlcywgZmlnLndpZHRoID0gJXMsIG91dC53aWR0aCA9ICcxMDAlJScsIGFkZC5wYW5lbCA9IFRSVUUiCiAgICAgICAgICAgIGFkZF9jbGFzcyA8LSAicGFuZWwgcGFuZWwtZGVmYXVsdCBwYWRkZWQtcGFuZWwiCiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBjaHVua19hcmdzIDwtICJmaWcuaGVpZ2h0ID0gJXMsIGZpZy53aWR0aCA9ICVzLCBvdXQud2lkdGggPSAnMTAwJSUnIgogICAgICAgICAgICBhZGRfY2xhc3MgPC0gTlVMTAogICAgICAgICAgfQogICAgICAgICAgCiAgICAgICAgICBpZiAod3JpdGVfZmxhZykgewogICAgICAgICAgICByZXN1bHRzIDwtIHNwcmludGYoCiAgICAgICAgICAgICAgInNob3dfcmVzdWx0cygnJXMnLCAnJXMnLCAndmlzdWFsaXplcicpIiwgZGlyX25hbWUsIHZpel9uYW1lCiAgICAgICAgICAgICkgJT4lCiAgICAgICAgICAgICAgd3JpdGVfY29kZV9jaHVuaygKICAgICAgICAgICAgICAgIGNodW5rX2FyZ3MgPSBzcHJpbnRmKAogICAgICAgICAgICAgICAgICBjaHVua19hcmdzLAogICAgICAgICAgICAgICAgICB2aXN1YWxpemVyJGRvY19vcHRpb25zJGhlaWdodCwgdmlzdWFsaXplciRkb2Nfb3B0aW9ucyR3aWR0aAogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICkgJT4lCiAgICAgICAgICAgICAgd3JpdGUob2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZykKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHZ0aGVtZXM6OnN1YmNodW5raWZ5KHBsdCwKICAgICAgICAgICAgICBpID0gY2h1bmtfaWR4LAogICAgICAgICAgICAgIGZpZ19oZWlnaHQgPSB2aXN1YWxpemVyJGRvY19vcHRpb25zJGhlaWdodCwKICAgICAgICAgICAgICBmaWdfd2lkdGggPSB2aXN1YWxpemVyJGRvY19vcHRpb25zJHdpZHRoLAogICAgICAgICAgICAgIG90aGVyX2FyZ3MgPSAib3V0LndpZHRoID0gJzEwMCUnIiwgCiAgICAgICAgICAgICAgYWRkX2NsYXNzID0gYWRkX2NsYXNzCiAgICAgICAgICAgICkKICAgICAgICAgICAgY2h1bmtfaWR4IDw8LSBjaHVua19pZHggKyAxCiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgfQoKICBpZiAoIWlzLm51bGwoZXhwKSAmJiBwYXJhbXMkc2hvd19jb2RlKSB7CiAgICBpZiAoKGxlbmd0aChleHAkZ2V0X3ZhcnlfYWNyb3NzKCkkZGdwKSAhPSAwKSB8fAogICAgICAgIChsZW5ndGgoZXhwJGdldF92YXJ5X2Fjcm9zcygpJG1ldGhvZCkgIT0gMCkpIHsKICAgICAgcmVzdWx0cyA8LSB3cml0ZSgKICAgICAgICBzcHJpbnRmKHNob3d0eXBlX3RlbXBsYXRlLCBjb2RlX2xhYmVsKSwKICAgICAgICAiXG5cbioqUGFyYW1ldGVyIFZhbHVlcyoqXG5cbiIsCiAgICAgICAgb2xkX3RleHQgPSByZXN1bHRzLCB3cml0ZV9mbGFnID0gd3JpdGVfZmxhZwogICAgICApCiAgICAgIGlmICh3cml0ZV9mbGFnKSB7CiAgICAgICAgcmVzdWx0cyA8LSBzcHJpbnRmKAogICAgICAgICAgInNob3dfcmVzdWx0cygnJXMnLCBOVUxMLCAndmFyeV9wYXJhbXMnKSIsIGRpcl9uYW1lCiAgICAgICAgKSAlPiUKICAgICAgICAgIHdyaXRlX2NvZGVfY2h1bmsoY2h1bmtfYXJncyA9ICJtYXguaGVpZ2h0PScyMDBweCciKSAlPiUKICAgICAgICAgIHdyaXRlKG9sZF90ZXh0ID0gcmVzdWx0cywgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcpCiAgICAgIH0gZWxzZSB7CiAgICAgICAgdnRoZW1lczo6c3ViY2h1bmtpZnkoZXhwJGdldF92YXJ5X2Fjcm9zcygpLAogICAgICAgICAgY2h1bmtfaWR4LAogICAgICAgICAgb3RoZXJfYXJncyA9ICJtYXguaGVpZ2h0PScyMDBweCciCiAgICAgICAgKQogICAgICAgIGNodW5rX2lkeCA8PC0gY2h1bmtfaWR4ICsgMQogICAgICB9CiAgICB9CiAgfQoKICByZXR1cm4ocmVzdWx0cykKfQoKIycgRGlzcGxheXMgb3V0cHV0IG9mIGV4cGVyaW1lbnQgZm9yIGFsbCBvZiBpdHMgKHNhdmVkKSBkZXNjZW5kYW50cwojJwojJyBAcGFyYW0gZGlyX25hbWUgbmFtZSBvZiBwYXJlbnQgZXhwZXJpbWVudCBkaXJlY3RvcnkKIycgQHBhcmFtIGRlcHRoIHBsYWNlaG9sZGVyIGZvciByZWN1cnNpb247IHNob3VsZCBub3QgYmUgbWVzc2VkIHdpdGgKIycgQHBhcmFtIHdyaXRlX2ZsYWcgQm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdG8gd3JpdGUgdGV4dCB0byBhIGZpbGUKIycgICAod3JpdGVfZmxhZyA9IFRSVUUpIG9yIHRvIGNvbnNvbGUgKHdyaXRlX2ZsYWcgPSBGQUxTRSkKIycgQHBhcmFtIHdyaXRlX2ZpbGVuYW1lIE5hbWUgb2YgZmlsZSB0byB3cml0ZSB0byBpZiB3cml0ZV9mbGFnID0gVFJVRQojJyBAcGFyYW0gLi4uIG90aGVyIGFyZ3VtZW50cyB0byBwYXNzIGludG8gc2hvd19yZXN1bHRzKCkKc2hvd19kZXNjZW5kYW50X3Jlc3VsdHMgPC0gZnVuY3Rpb24oZGlyX25hbWUsIGRlcHRoID0gMSwgd3JpdGVfZmxhZyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3cml0ZV9maWxlbmFtZSA9IE5VTEwsIC4uLikgewogIGNoaWxkcmVuIDwtIGxpc3QuZGlycyhkaXJfbmFtZSwgcmVjdXJzaXZlID0gRkFMU0UpCiAgaWYgKGxlbmd0aChjaGlsZHJlbikgPT0gMCkgewogICAgcmV0dXJuKCkKICB9CiAgZm9yIChjaGlsZF9pZHggaW4gMTpsZW5ndGgoY2hpbGRyZW4pKSB7CiAgICBjaGlsZCA8LSBjaGlsZHJlbltjaGlsZF9pZHhdCiAgICBpZiAoIWV4cGVyaW1lbnRfZXhpc3RzKGNoaWxkLCByZWN1cnNpdmUgPSBUUlVFKSkgewogICAgICBuZXh0CiAgICB9CiAgICBpZiAoZXhwZXJpbWVudF9leGlzdHMoY2hpbGQsIHJlY3Vyc2l2ZSA9IEZBTFNFKSAmJgogICAgICAoZXhwZXJpbWVudF9leGlzdHMobGlzdC5kaXJzKGNoaWxkLCByZWN1cnNpdmUgPSBUUlVFKVstMV0pIHx8CiAgICAgICAgKGRlcHRoID09IDEpKSkgewogICAgICBiYXNlIDwtIFRSVUUKICAgIH0gZWxzZSB7CiAgICAgIGJhc2UgPC0gRkFMU0UKICAgIH0KICAgIHJlc3VsdHMgPC0gc2hvd19yZXN1bHRzKGNoaWxkLCBkZXB0aCwgYmFzZSwgd3JpdGVfZmxhZyA9IHdyaXRlX2ZsYWcsIC4uLikKICAgIGlmICh3cml0ZV9mbGFnKSB7CiAgICAgIHdyaXRlX3RvX2ZpbGUocGF0aCA9IHdyaXRlX2ZpbGVuYW1lLCByZXN1bHRzKQogICAgfQogICAgc2hvd19kZXNjZW5kYW50X3Jlc3VsdHMoY2hpbGQsIGRlcHRoICsgMSwgd3JpdGVfZmxhZywgd3JpdGVfZmlsZW5hbWUsIC4uLikKICB9Cn0KCiMnIENsZWFuIG91dHB1dCBmaWxlIChlLmcuLCByZW1vdmUgZXhjZXNzaXZlIGJsYW5rIGxpbmVzKQojJwojJyBAcGFyYW0gcGF0aCBQYXRoIHRvIG91dHB1dCBmaWxlCmNsZWFuX2ZpbGUgPC0gZnVuY3Rpb24ocGF0aCkgewogIHN0b3JlbGluZXMgPC0gcmVhZExpbmVzKHBhdGgpCiAgcmxlX291dCA8LSBybGUoc3RvcmVsaW5lcyA9PSAiIikKICBsaW5lX2lkcyA8LSB3aGljaCgocmxlX291dCRsZW5ndGhzID4gMikgJiBybGVfb3V0JHZhbHVlcykKICBrZWVwX2xpbmVzIDwtIHJlcChUUlVFLCBsZW5ndGgoc3RvcmVsaW5lcykpCiAgZm9yIChsaW5lX2lkIGluIGxpbmVfaWRzKSB7CiAgICBudW1fYmxhbmsgPC0gcmxlX291dCRsZW5ndGhzW2xpbmVfaWRdCiAgICBsaW5lX3B0ciA8LSBzdW0ocmxlX291dCRsZW5ndGhzWzE6bGluZV9pZF0pCiAgICAjIG9ubHkgYWxsb3cgZm9yIG1heCBvZiB0d28gY29uc2VjdXRpdmUgYmxhbmsgbGluZXMKICAgIGtlZXBfbGluZXNbKGxpbmVfcHRyIC0gbnVtX2JsYW5rICsgMyk6bGluZV9wdHJdIDwtIEZBTFNFCiAgfQogIHdyaXRlTGluZXMoc3RvcmVsaW5lc1trZWVwX2xpbmVzXSwgcGF0aCkKfQoKIycgSW5zZXJ0IGxpbmVzIHRvIGFkZCBleHRyYSByZXNvdXJjZXMgKGNzcy9qcykgZm9yIHNpbUNoZWYgUiBNYXJrZG93biB0aGVtZQojJyAKIycgQHBhcmFtIHBhdGggUGF0aCB0byBvdXRwdXQgZmlsZQppbnNlcnRfc2ltQ2hlZl9yZXNvdXJjZXMgPC0gZnVuY3Rpb24ocGF0aCkgewogIHN0b3JlbGluZXMgPC0gcmVhZExpbmVzKHBhdGgpCiAgcGF0dGVybiA8LSAiPEluc2VydCBleHRyYSBzaW1DaGVmIHJlc291cmNlcyBoZXJlPiIKICByZXBsYWNlIDwtIHNwcmludGYoCiAgICAnPHNjcmlwdCBzcmM9IiVzIj48L3NjcmlwdD5cblxuPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSIlcyI+JywgCiAgICBzeXN0ZW0uZmlsZSgicm1kIiwgImpzIiwgInNpbWNoZWZOYXZDbGFzcy5qcyIsCiAgICAgICAgICAgICAgICBwYWNrYWdlID0gdXRpbHM6OnBhY2thZ2VOYW1lKCkpLAogICAgc3lzdGVtLmZpbGUoInJtZCIsICJjc3MiLCAic2ltY2hlZi5jc3MiLAogICAgICAgICAgICAgICAgcGFja2FnZSA9IHV0aWxzOjpwYWNrYWdlTmFtZSgpKQogICkgCiAgc3RvcmVsaW5lc1tzdG9yZWxpbmVzID09IHBhdHRlcm5dIDwtIHJlcGxhY2UKICB3cml0ZUxpbmVzKHN0b3JlbGluZXMsIHBhdGgpCn0KCiMnIFJlbW92ZSBsaW5lcyB3aXRoIHNpbUNoZWYgUiBNYXJrZG93biB0aGVtZS1zcGVjaWZpYyBjb2RlCiMnIAojJyBAcGFyYW0gcGF0aCBQYXRoIHRvIG91dHB1dCBmaWxlCnJlbW92ZV9zaW1DaGVmX3Jlc291cmNlcyA8LSBmdW5jdGlvbihwYXRoKSB7CiAgc3RvcmVsaW5lcyA8LSByZWFkTGluZXMocGF0aCkKICAKICBwYXR0ZXJuIDwtICJhZGQucGFuZWwgPSBmdW5jdGlvbiIKICBsaW5lX2lkIDwtIHdoaWNoKHN0cmluZ3I6OnN0cl9kZXRlY3Qoc3RvcmVsaW5lcywgcGF0dGVybikpCiAgcmVtb3ZlX2xpbmVzIDwtIChsaW5lX2lkIC0gMik6KGxpbmVfaWQgKyA1KQogIHN0b3JlbGluZXMgPC0gc3RvcmVsaW5lc1stcmVtb3ZlX2xpbmVzXQogIAogIHBhdHRlcm4gPC0gIjxJbnNlcnQgZXh0cmEgc2ltQ2hlZiByZXNvdXJjZXMgaGVyZT4iCiAgcmVtb3ZlX2xpbmVzIDwtIHdoaWNoKHN0cmluZ3I6OnN0cl9kZXRlY3Qoc3RvcmVsaW5lcywgcGF0dGVybikpCiAgc3RvcmVsaW5lcyA8LSBzdG9yZWxpbmVzWy1yZW1vdmVfbGluZXNdCiAgCiAgd3JpdGVMaW5lcyhzdG9yZWxpbmVzLCBwYXRoKQp9CgpgYGAKCmBgYHtyfQppZiAocGFyYW1zJHdyaXRlKSB7CiAgaWYgKHBhcmFtcyR1c2Vfdm1vZGVybikgewogICAgaW5zZXJ0X3NpbUNoZWZfcmVzb3VyY2VzKHdyaXRlX2ZpbGVuYW1lKQogIH0gZWxzZSB7CiAgICByZW1vdmVfc2ltQ2hlZl9yZXNvdXJjZXMod3JpdGVfZmlsZW5hbWUpCiAgfQp9IGVsc2UgewogIGlmIChwYXJhbXMkdXNlX3Ztb2Rlcm4pIHsKICAgIGh0bWx0b29sczo6SFRNTCgnPHNjcmlwdCBzcmM9ImpzL3NpbWNoZWZOYXZDbGFzcy5qcyI+PC9zY3JpcHQ+XG5cbjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL3NpbWNoZWYuY3NzIj4nKQogIH0KfQpgYGAKCgojIFNpbXVsYXRpb24gRXhwZXJpbWVudCBSZWNpcGUgey50YWJzZXQgLnRhYnNldC12bW9kZXJufQoKIyMgT2JqZWN0aXZlcwoKYGBge3Igb2JqZWN0aXZlcywgcmVzdWx0cyA9ICJhc2lzIn0KaWYgKHBhcmFtcyR1c2Vfdm1vZGVybikgewogIG9iamVjdGl2ZXMgPC0gd3JpdGUoCiAgICAiXG5cbjxkaXYgY2xhc3M9J3BhbmVsIHBhbmVsLWRlZmF1bHQgcGFkZGVkLXBhbmVsJz5cblxuIiwKICAgIHBhc3RlTWQoZmlsZS5wYXRoKGRvY19kaXIsICJvYmplY3RpdmVzLm1kIikpLAogICAgIlxuXG48L2Rpdj5cblxuIiwKICAgIHdyaXRlX2ZsYWcgPSBwYXJhbXMkd3JpdGUKICApCn0gZWxzZSB7CiAgb2JqZWN0aXZlcyA8LSB3cml0ZSgKICAgIHBhc3RlTWQoZmlsZS5wYXRoKGRvY19kaXIsICJvYmplY3RpdmVzLm1kIikpLAogICAgd3JpdGVfZmxhZyA9IHBhcmFtcyR3cml0ZQogICkKfQppZiAocGFyYW1zJHdyaXRlKSB7CiAgd3JpdGVfdG9fZmlsZShwYXRoID0gd3JpdGVfZmlsZW5hbWUsICJcblxuIyMgT2JqZWN0aXZlc1xuXG4iLCBvYmplY3RpdmVzKQp9CmBgYAoKIyMgRGF0YSBHZW5lcmF0aW9uCgpgYGB7ciBkZ3BzLCByZXN1bHRzID0gImFzaXMifQpkZ3BfcmVjaXBlIDwtIHNob3dfcmVjaXBlKGZpZWxkX25hbWUgPSAiZGdwIiwgd3JpdGVfZmxhZyA9IHBhcmFtcyR3cml0ZSkKaWYgKHBhcmFtcyR3cml0ZSkgewogIHdyaXRlX3RvX2ZpbGUocGF0aCA9IHdyaXRlX2ZpbGVuYW1lLCAiXG5cbiMjIERhdGEgR2VuZXJhdGlvblxuXG4iLCBkZ3BfcmVjaXBlKQp9CmBgYAoKIyMgTWV0aG9kcyBhbmQgRXZhbHVhdGlvbgoKIyMjIE1ldGhvZHMKCmBgYHtyIG1ldGhvZHMsIHJlc3VsdHMgPSAiYXNpcyJ9Cm1ldGhvZF9yZWNpcGUgPC0gc2hvd19yZWNpcGUoZmllbGRfbmFtZSA9ICJtZXRob2QiLCB3cml0ZV9mbGFnID0gcGFyYW1zJHdyaXRlKQppZiAocGFyYW1zJHdyaXRlKSB7CiAgd3JpdGVfdG9fZmlsZSgKICAgIHBhdGggPSB3cml0ZV9maWxlbmFtZSwKICAgICJcblxuIyMgTWV0aG9kcyBhbmQgRXZhbHVhdGlvblxuXG4iLCAiXG5cbiMjIyBNZXRob2RzXG5cbiIsCiAgICBtZXRob2RfcmVjaXBlCiAgKQp9CmBgYAoKIyMjIEV2YWx1YXRpb24KCmBgYHtyIGV2YWx1YXRvcnMsIHJlc3VsdHMgPSAiYXNpcyJ9CmV2YWxfcmVjaXBlIDwtIHNob3dfcmVjaXBlKGZpZWxkX25hbWUgPSAiZXZhbHVhdG9yIiwgd3JpdGVfZmxhZyA9IHBhcmFtcyR3cml0ZSkKaWYgKHBhcmFtcyR3cml0ZSkgewogIHdyaXRlX3RvX2ZpbGUocGF0aCA9IHdyaXRlX2ZpbGVuYW1lLCAiXG5cbiMjIyBFdmFsdWF0aW9uXG5cbiIsIGV2YWxfcmVjaXBlKQp9CmBgYAoKIyMgVmlzdWFsaXphdGlvbnMKCmBgYHtyIHZpc3VhbGl6ZXJzLCByZXN1bHRzID0gImFzaXMifQp2aXpfcmVjaXBlIDwtIHNob3dfcmVjaXBlKGZpZWxkX25hbWUgPSAidmlzdWFsaXplciIsIHdyaXRlX2ZsYWcgPSBwYXJhbXMkd3JpdGUpCmlmIChwYXJhbXMkd3JpdGUpIHsKICB3cml0ZV90b19maWxlKHBhdGggPSB3cml0ZV9maWxlbmFtZSwgIlxuXG4jIyBWaXN1YWxpemF0aW9uc1xuXG4iLCB2aXpfcmVjaXBlKQp9CmBgYAoKCgpgYGB7ciByZXMsIHJlc3VsdHMgPSAiYXNpcyJ9CmlmIChwYXJhbXMkdmVyYm9zZSA+IDApIHsKICBpbmZvcm0oc3ByaW50ZigiQ3JlYXRpbmcgUiBNYXJrZG93biByZXBvcnQgZm9yICVzLi4uIiwgcGFyYW1zJHNpbV9uYW1lKSkKfQoKIyBzaG93IHJlc3VsdHMKaWYgKGV4cGVyaW1lbnRfZXhpc3RzKHBhcmFtcyRzaW1fcGF0aCkpIHsKICBiYXNlX2hlYWRlciA8LSB3cml0ZSgKICAgIHNwcmludGYoIlxuXG4jIEJhc2UgJXMgXG5cbiIsIHBhcmFtcyRzaW1fbmFtZSksCiAgICAiXG5cbiMjIHsudGFic2V0IC50YWJzZXQtcGlsbHMgLnRhYnNldC1jaXJjbGV9XG5cbiIsCiAgICB3cml0ZV9mbGFnID0gcGFyYW1zJHdyaXRlCiAgKQogIGJhc2VfcmVzdWx0cyA8LSBzaG93X3Jlc3VsdHMoCiAgICBwYXJhbXMkc2ltX3BhdGgsCiAgICBkZXB0aCA9IDIsIGJhc2UgPSBGQUxTRSwgc2hvd19oZWFkZXIgPSBGQUxTRSwKICAgIHZlcmJvc2UgPSBwYXJhbXMkdmVyYm9zZSwgd3JpdGVfZmxhZyA9IHBhcmFtcyR3cml0ZQogICkKCiAgaWYgKHBhcmFtcyR3cml0ZSkgewogICAgd3JpdGVfdG9fZmlsZShwYXRoID0gd3JpdGVfZmlsZW5hbWUsIGJhc2VfaGVhZGVyLCBiYXNlX3Jlc3VsdHMpCiAgfQp9CgpzaG93X2Rlc2NlbmRhbnRfcmVzdWx0cygKICBwYXJhbXMkc2ltX3BhdGgsCiAgdmVyYm9zZSA9IHBhcmFtcyR2ZXJib3NlLAogIHdyaXRlX2ZsYWcgPSBwYXJhbXMkd3JpdGUsIAogIHdyaXRlX2ZpbGVuYW1lID0gd3JpdGVfZmlsZW5hbWUKKQpgYGAKCgoKYGBge3J9CmlmIChwYXJhbXMkd3JpdGUpIHsKICBjbGVhbl9maWxlKHBhdGggPSB3cml0ZV9maWxlbmFtZSkKfQpgYGAK