Introduction
This module presents comprehensive methods for bootstrap hypothesis
testing. For each test (one-sample, two-sample, and alternative Wald,
Score, and likelihood ratio tests), we state the mathematical
algorithm/steps and provide pseudo-code before presenting numerical
examples. All implementations are provided in R with complete code
examples.
Bootstrap hypothesis testing involves resampling data to
approximate the sampling distribution of a test
statistic under the null hypothesis. The general
procedure consists of:
- Compute the test statistic from the original data: \(T_{obs}\)
- Transform/resample data to satisfy the
null hypothesis \(H_0\)
- Generate \(B\) bootstrap samples
and compute test statistics \(T_b^*\)
- Calculate p-value (for a two-tailed test) as:
\[
p = \frac{\#\{|T_b^*| \geq |T_{obs}|\} + 1}{B + 1}
\]
The bootstrap provides a nonparametric approach to inference that
doesn’t rely heavily on distributional assumptions (i.e., the sampling
distribution of the test statistic).
One-Sample Bootstrap
Tests
One-Sample Mean Test
(Parametric Bootstrap)
Mathematical Algorithm
Let \(X = \{x_1, x_2, ..., x_n\}\)
be the observed data.
Null hypothesis: \(H_0: \mu = \mu_0\)
Test statistic: for \(\bar{x} = \frac{1}{n}\sum_{i=1}^n x_i\) and
\(s = \sqrt{\frac{1}{n-1}\sum_{i=1}^n (x_i -
\bar{x})^2}\), the test statistic is defined to be \[
t_{obs} = \frac{\bar{x} - \mu_0}{s/\sqrt{n}}
\]
Center data under \(H_0\): \(x_i^\prime = x_i - \bar{x} + \mu_0\). For
\(b = 1\) to \(B\)}:
- Sample \(X_b^* = \{x_1^*, ...,
x_n^*\}\) from \(X'\) with
replacement
\[\bar{x}_b^* = \frac{1}{n}\sum_{i=1}^n
x_i^*\] \[s_b^* =
\sqrt{\frac{1}{n-1}\sum_{i=1}^n (x_i^* - \bar{x}_b^*)^2}\] \[t_b^* = \frac{\bar{x}_b^* -
\mu_0}{s_b^*/\sqrt{n}}\]
- **Calculate p-value(for two-sided test): \[
p = \frac{\#\{|t_b^*| \geq |t_{obs}|\} + 1}{B + 1}
\]
Pseudo-code
R Implementation
# One-sample mean test using parametric bootstrap
one_sample_bootstrap_test <- function(data, mu0, B = 9999) {
n <- length(data)
t_obs <- (mean(data) - mu0) / (sd(data) / sqrt(n))
# Center data under H0: μ = μ0
centered_data <- data - mean(data) + mu0
t_star <- numeric(B)
for (b in 1:B) {
bs_sample <- sample(centered_data, n, replace = TRUE)
t_star[b] <- (mean(bs_sample) - mu0) / (sd(bs_sample) / sqrt(n))
}
p_value <- (sum(abs(t_star) >= abs(t_obs)) + 1) / (B + 1)
return(list(
p_value = p_value,
t_obs = t_obs,
bootstrap_stats = t_star,
mean_obs = mean(data),
mu0 = mu0
))
}
# Example
set.seed(123)
data <- rnorm(30, mean = 105, sd = 15)
mu0 <- 100
result <- one_sample_bootstrap_test(data, mu0, B = 1999)
One-Sample Mean Test
(Percentile Method)
Mathematical Algorithm
Compute sample mean: \(\bar{x} =
\frac{1}{n}\sum_{i=1}^n x_i\), For \(b
= 1\) to \(B\):
- Sample \(X_b^*\) from \(X\) with replacement
- \(\bar{x}_b^* = \frac{1}{n}\sum_{i=1}^n
x_i^*\)
Construct \((1-\alpha)\%\)
confidence interval from percentiles. For \(\bar{x}_{(q)}\) is the \(q\)-th quantile of \(\{\bar{x}_b^*\}\) \[
CI = [\bar{x}_{(\alpha/2)}, \bar{x}_{(1-\alpha/2)}]
\]
Decision rule: Reject \(H_0: \mu =
\mu_0\) if \(\mu_0 \notin
CI\)
P-value approximation: \[
p \approx 2 \times \min\left[P(\bar{x}^* \leq \mu_0), P(\bar{x}^* \geq
\mu_0)\right]
\]
Two-Sample Bootstrap
Tests
Mathematical Algorithm
Let \(X = \{x_1, ..., x_m\}\) and
\(Y = \{y_1, ..., y_n\}\) be two
independent samples.
Null hypothesis: \(H_0: \mu_x = \mu_y\)
Pooled standard deviation (under \(H_0\)): \[
s_p = \sqrt{\frac{(m-1)s_x^2 + (n-1)s_y^2}{m+n-2}}
\]
Test statistic}: \[
t_{obs} = \frac{\bar{x} - \bar{y}}{s_p\sqrt{\frac{1}{m} + \frac{1}{n}}}
\]
Center and pool data under \(H_0\)}: \[z_i = \begin{cases}
x_i - \bar{x} & \text{for } i = 1,...,m \\
y_i - \bar{y} & \text{for } i = 1,...,n
\end{cases}\]
For \(b = 1\) to \(B\):
- \(X_b^* = \{z_1^*, ..., z_m^*\}\)
from pooled data with replacement
- Sample \(Y_b^* = \{z_{m+1}^*, ...,
z_{m+n}^*\}\) from pooled data with replacement
- Compute: for \(s_{p,b}^*\) is the
pooled SD of \(X_b^*\) and \(Y_b^*\) \[
t_b^* = \frac{\bar{x}_b^* - \bar{y}_b^*}{s_{p,b}^*\sqrt{\frac{1}{m} +
\frac{1}{n}}}
\]
Calculate p-value: \[
p = \frac{\#\{|t_b^*| \geq |t_{obs}|\} + 1}{B + 1}
\]
Likelihood-based
Bootstrap Tests
This section presents Bootstrap version of the three likelihood-based
\(\chi^2\) tests.
Bootstrap Wald
Test
Mathematical Formulation
For a parameter \(\theta\) with null
hypothesis \(H_0: \theta =
\theta_0\):
- Maximum likelihood estimator: \(\hat{\theta}\)
- Estimated variance \(\widehat{Var}(\hat{\theta})\)
- Wald statistic \[
W = \frac{(\hat{\theta} - \theta_0)^2}{\widehat{Var}(\hat{\theta})}
\] Under \(H_0\), \(W \sim \chi^2_1\) asymptotically
Bootstrap Algorithm
Compute \(\hat{\theta}\) and
\(W_{obs}\) from original data
For \(b = 1\) to \(B\):
Generate bootstrap sample \(X_b^*\)
Compute \(\hat{\theta}_b^*\)
from \(X_b^*\)
Estimate \(\widehat{Var}_b^*(\hat{\theta}_b^*)\) using
nested bootstrap
Compute: \[
W_b^* = \frac{(\hat{\theta}_b^* -
\hat{\theta})^2}{\widehat{Var}_b^*(\hat{\theta}_b^*)}
\]
Calculate p-value: \[
p = \frac{\#\{W_b^* \geq W_{obs}\} + 1}{B + 1}
\]
R Implementation
# Bootstrap Wald test for mean
bootstrap_wald_test <- function(data, theta0, B = 999, inner_B = 100) {
n <- length(data)
theta_hat <- mean(data)
# Bootstrap for variance estimation
boot_theta <- numeric(B)
for (b in 1:B) {
bs_sample <- sample(data, n, replace = TRUE)
boot_theta[b] <- mean(bs_sample)
}
# Wald statistic
var_theta <- var(boot_theta)
W_obs <- (theta_hat - theta0)^2 / var_theta
# Bootstrap distribution of Wald statistic
W_star <- numeric(B)
for (b in 1:B) {
bs_sample <- sample(data, n, replace = TRUE)
theta_star <- mean(bs_sample)
# Inner bootstrap for variance estimation
inner_boot <- numeric(inner_B)
for (j in 1:inner_B) {
inner_sample <- sample(bs_sample, n, replace = TRUE)
inner_boot[j] <- mean(inner_sample)
}
var_star <- var(inner_boot)
W_star[b] <- (theta_star - theta_hat)^2 / var_star
}
p_value <- (sum(W_star >= W_obs) + 1) / (B + 1)
return(list(
W_obs = W_obs,
p_value = p_value,
theta_hat = theta_hat,
theta0 = theta0
))
}
Bootstrap Score
Test
Mathematical Formulation
For a parameter \(\theta\):
- Score function: \(U(\theta) = \frac{\partial \ell(\theta)}{\partial
\theta}\) where \(\ell(\theta)\)
is the log-likelihood
- Fisher information: \(I(\theta) = -E\left[\frac{\partial^2
\ell(\theta)}{\partial \theta^2}\right]\)
- Score statistic}: Under \(H_0: \theta = \theta_0\), the following
score test statistic \(S \sim
\chi^2_1\) asymptotically \[
S = \frac{U(\theta_0)^2}{I(\theta_0)}
\]
Bootstrap Algorithm
- Compute \(U(\theta_0)\) and \(I(\theta_0)\) under \(H_0\)
- Compute \(S_{obs}\)
- For \(b = 1\) to \(B\):
- Generate data under \(H_0\): \(X_b^* \sim f(x|\theta_0)\)
- Compute \(U_b(\theta_0)\) from
\(X_b^*\)
- Compute: \[
S_b^* = \frac{U_b(\theta_0)^2}{I(\theta_0)}
\]
- Calculate p-value: \[
p = \frac{\#\{S_b^* \geq S_{obs}\} + 1}{B + 1}
\]
Bootstrap Likelihood
Ratio Test
Mathematical Formulation
- Likelihood function: \(L(\theta) = \prod_{i=1}^n
f(x_i|\theta)\)
- Maximum likelihood estimator: \(\hat{\theta} = \arg\max_\theta
L(\theta)\)
- Likelihood ratio: \[
\Lambda = \frac{L(\theta_0)}{L(\hat{\theta})}
\]
- Likelihood ratio statistic: Under \(H_0: \theta = \theta_0\), \(LR = -2\log\Lambda \sim \chi^2_1\)
asymptotically distributed as \(\chi^2_{d.f}\) \[
-2\log\Lambda = -2[\log L(\theta_0) - \log L(\hat{\theta})]
\]
Bootstrap Algorithm
- Compute \(-2\log\Lambda_{obs}\)
from original data
- For \(b = 1\) to \(B\):
- Generate data under \(H_0\): \(X_b^* \sim f(x|\theta_0)\)
- Compute MLE \(\hat{\theta}_b^*\)
from \(X_b^*\)
- Compute \(-2\log\Lambda_b^*\)
- Calculate p-value: \[
p = \frac{\#\{-2\log\Lambda_b^* \geq -2\log\Lambda_{obs}\} + 1}{B + 1}
\]
R Implementation
# Bootstrap Likelihood Ratio test for normal mean
bootstrap_lrt_test <- function(data, mu0, B = 9999) {
n <- length(data)
# MLEs (assuming unknown variance)
mu_hat <- mean(data)
sigma_hat <- sd(data) * sqrt((n - 1) / n) # MLE of σ
# Using same sigma estimate for both for fair comparison
sigma0 <- sd(data) * sqrt((n - 1) / n)
# Log-likelihoods
logL_hat <- sum(dnorm(data, mean = mu_hat, sd = sigma_hat, log = TRUE))
logL0 <- sum(dnorm(data, mean = mu0, sd = sigma0, log = TRUE))
# LR statistic
LR_obs <- -2 * (logL0 - logL_hat)
# Bootstrap distribution
LR_star <- numeric(B)
for (b in 1:B) {
# Generate data under H0: N(μ0, σ0)
bs_sample <- rnorm(n, mean = mu0, sd = sigma0)
# MLE from bootstrap sample
mu_star <- mean(bs_sample)
sigma_star <- sd(bs_sample) * sqrt((n - 1) / n)
# Likelihoods
logL_star <- sum(dnorm(bs_sample, mean = mu_star, sd = sigma_star, log = TRUE))
logL_star0 <- sum(dnorm(bs_sample, mean = mu0, sd = sigma0, log = TRUE))
LR_star[b] <- -2 * (logL_star0 - logL_star)
}
p_value <- (sum(LR_star >= LR_obs) + 1) / (B + 1)
return(list(
LR_obs = LR_obs,
p_value = p_value,
mu_hat = mu_hat,
mu0 = mu0
))
}
Non-Permutation Test
with Bootstrap
Mathematical Algorithm
- Combine groups: \(Z =
\{x_1, ..., x_m, y_1, ..., y_n\}\)
- Observed statistic: \(T_{obs} = \bar{x} - \bar{y}\)
- For \(b = 1\) to \(B\)}:
- Randomly permute \(Z\) to get \(Z_b^*\)
- Assign first \(m\) elements to
\(X_b^*\), remaining \(n\) to \(Y_b^*\)
- Compute \(T_b^* = \bar{x}_b^* -
\bar{y}_b^*\)
- Calculate p-value: \[
p = \frac{\#\{|T_b^*| \geq |T_{obs}|\} + 1}{B + 1}
\]
Choosing Number of Bootstrap Samples
The choice of \(B\) (number of
bootstrap samples) affects the precision of p-value estimation.
Recommended values:
- For p-values near 0.05: \(B \geq
999\)
- For publication quality: \(B \geq
9999\)
- For confidence intervals: \(B \geq
1000\) for 95% CI, more for higher confidence levels
The standard error of a bootstrap p-value is approximately: \[
\text{SE}(p) = \sqrt{\frac{p(1-p)}{B}}
\]
For \(p = 0.05\) and \(B = 1000\), \(\text{SE} \approx 0.0069\), giving about
1.4 percentage points of uncertainty.
Conclusion
This document has presented comprehensive methods for bootstrap
hypothesis testing in R, covering one-sample tests, two-sample tests,
and alternative test statistics (Wald, Score, and likelihood ratio
tests). Each method includes mathematical formulation, algorithmic
steps, pseudo-code, and practical R implementations. Bootstrap methods
provide robust alternatives to classical tests, especially when
distributional assumptions are violated or sample sizes are small.
LS0tDQp0aXRsZTogIkJvb3RzdHJhcCBUZXN0aW5nIEh5cG90aGVzaXMiDQphdXRob3I6ICJDaGVuZyBQZW5nIg0KZGF0ZTogIldlc3QgQ2hlc3RlciBVbml2ZXJzaXR5Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCiAgd29yZF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9DQojVE9DOjpiZWZvcmUgew0KICBjb250ZW50OiAiVGFibGUgb2YgQ29udGVudHMiOw0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1zaXplOiAxLjJlbTsNCiAgZGlzcGxheTogYmxvY2s7DQogIGNvbG9yOiBuYXZ5Ow0KICBtYXJnaW4tYm90dG9tOiAxMHB4Ow0KfQ0KDQoNCmRpdiNUT0MgbGkgeyAgICAgLyogdGFibGUgb2YgY29udGVudCAgKi8NCiAgICBsaXN0LXN0eWxlOnVwcGVyLXJvbWFuOw0KICAgIGJhY2tncm91bmQtaW1hZ2U6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXJlcGVhdDpub25lOw0KICAgIGJhY2tncm91bmQtcG9zaXRpb246MDsNCn0NCg0KaDEudGl0bGUgeyAgICAvKiBsZXZlbCAxIGhlYWRlciBvZiB0aXRsZSAgKi8NCiAgZm9udC1zaXplOiAyMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KDQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICBmb250LXNpemU6IDE1cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogbmF2eTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDEgeyAvKiBIZWFkZXIgMSAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCmgyIHsgLyogSGVhZGVyIDIgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMTZweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQovKiBBZGQgZG90cyBhZnRlciBudW1iZXJlZCBoZWFkZXJzICovDQouaGVhZGVyLXNlY3Rpb24tbnVtYmVyOjphZnRlciB7DQogIGNvbnRlbnQ6ICIuIjsNCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KfQ0KYGBgDQoNCmBgYHtodG1sLCBlY2hvPUZBTFNFfQ0KPGJ1dHRvbiBpZD0idG9nZ2xlVE9DQnRuIiBjbGFzcz0iYnRuIGJ0bi1kZWZhdWx0IiBzdHlsZT0icG9zaXRpb246IGZpeGVkOyBib3R0b206IDIwcHg7IHJpZ2h0OiAyMHB4OyB6LWluZGV4OiAxMDAwOyI+DQogIFRvZ2dsZSBUT0MNCjwvYnV0dG9uPg0KDQo8c2NyaXB0Pg0KZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInRvZ2dsZVRPQ0J0biIpLm9uY2xpY2sgPSBmdW5jdGlvbigpIHsNCiAgdmFyIHRvYyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5saXN0LWdyb3VwLCAjVE9DLCAudG9jaWZ5Iik7DQogIGlmICh0b2MpIHsNCiAgICBpZiAodG9jLnN0eWxlLmRpc3BsYXkgPT09ICJub25lIikgew0KICAgICAgdG9jLnN0eWxlLmRpc3BsYXkgPSAiIjsNCiAgICB9IGVsc2Ugew0KICAgICAgdG9jLnN0eWxlLmRpc3BsYXkgPSAibm9uZSI7DQogICAgfQ0KICB9DQp9Ow0KPC9zY3JpcHQ+DQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KICAgbGlicmFyeShwYW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCiAgbGlicmFyeShnZ3Bsb3QyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICBsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICBsaWJyYXJ5KHBsb3RseSkNCn0NCmlmICghcmVxdWlyZSgiZml0ZGlzdHJwbHVzIikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZml0ZGlzdHJwbHVzIikNCiAgbGlicmFyeShmaXRkaXN0cnBsdXMpDQp9DQojIyBsaWJyYXJ5KGZpdGRpc3RycGx1cykNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQ0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQoNClwNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KDQpUaGlzIG1vZHVsZSBwcmVzZW50cyBjb21wcmVoZW5zaXZlIG1ldGhvZHMgZm9yIGJvb3RzdHJhcCBoeXBvdGhlc2lzIHRlc3RpbmcuIEZvciBlYWNoIHRlc3QgKG9uZS1zYW1wbGUsIHR3by1zYW1wbGUsIGFuZCBhbHRlcm5hdGl2ZSBXYWxkLCBTY29yZSwgYW5kIGxpa2VsaWhvb2QgcmF0aW8gdGVzdHMpLCB3ZSBzdGF0ZSB0aGUgbWF0aGVtYXRpY2FsIGFsZ29yaXRobS9zdGVwcyBhbmQgcHJvdmlkZSBwc2V1ZG8tY29kZSBiZWZvcmUgcHJlc2VudGluZyBudW1lcmljYWwgZXhhbXBsZXMuIEFsbCBpbXBsZW1lbnRhdGlvbnMgYXJlIHByb3ZpZGVkIGluIFIgd2l0aCBjb21wbGV0ZSBjb2RlIGV4YW1wbGVzLg0KDQpCb290c3RyYXAgaHlwb3RoZXNpcyB0ZXN0aW5nIGludm9sdmVzIHJlc2FtcGxpbmcgZGF0YSB0byAqKmFwcHJveGltYXRlIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24qKiBvZiBhIHRlc3Qgc3RhdGlzdGljIHVuZGVyICoqdGhlIG51bGwgaHlwb3RoZXNpcyoqLiBUaGUgZ2VuZXJhbCBwcm9jZWR1cmUgY29uc2lzdHMgb2Y6DQoNCiogQ29tcHV0ZSB0aGUgdGVzdCBzdGF0aXN0aWMgZnJvbSB0aGUgb3JpZ2luYWwgZGF0YTogJFRfe29ic30kDQoqIFRyYW5zZm9ybS9yZXNhbXBsZSBkYXRhIHRvIHNhdGlzZnkgdGhlIDxmb250IGNvbG9yID0gInJlZCI+KipudWxsIGh5cG90aGVzaXMgJEhfMCQqKjwvZm9udD4NCiogR2VuZXJhdGUgJEIkIGJvb3RzdHJhcCBzYW1wbGVzIGFuZCBjb21wdXRlIHRlc3Qgc3RhdGlzdGljcyAkVF9iXiokDQoqIENhbGN1bGF0ZSBwLXZhbHVlIChmb3IgYSB0d28tdGFpbGVkIHRlc3QpIGFzOiANCg0KJCQNCnAgPSBcZnJhY3tcI1x7fFRfYl4qfCBcZ2VxIHxUX3tvYnN9fFx9ICsgMX17QiArIDF9DQokJA0KDQpUaGUgYm9vdHN0cmFwIHByb3ZpZGVzIGEgbm9ucGFyYW1ldHJpYyBhcHByb2FjaCB0byBpbmZlcmVuY2UgdGhhdCBkb2Vzbid0IHJlbHkgaGVhdmlseSBvbiBkaXN0cmlidXRpb25hbCBhc3N1bXB0aW9ucyAoaS5lLiwgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdGVzdCBzdGF0aXN0aWMpLg0KDQojIE9uZS1TYW1wbGUgQm9vdHN0cmFwIFRlc3RzDQoNCiMjIE9uZS1TYW1wbGUgTWVhbiBUZXN0IChQYXJhbWV0cmljIEJvb3RzdHJhcCkNCg0KKipNYXRoZW1hdGljYWwgQWxnb3JpdGhtKioNCg0KTGV0ICRYID0gXHt4XzEsIHhfMiwgLi4uLCB4X25cfSQgYmUgdGhlIG9ic2VydmVkIGRhdGEuDQoNCg0KKiAqKk51bGwgaHlwb3RoZXNpcyoqOiAkSF8wOiBcbXUgPSBcbXVfMCQNCg0KKiAqKlRlc3Qgc3RhdGlzdGljKio6ICBmb3IgJFxiYXJ7eH0gPSBcZnJhY3sxfXtufVxzdW1fe2k9MX1ebiB4X2kkIGFuZCAkcyA9IFxzcXJ0e1xmcmFjezF9e24tMX1cc3VtX3tpPTF9Xm4gKHhfaSAtIFxiYXJ7eH0pXjJ9JCwgdGhlIHRlc3Qgc3RhdGlzdGljIGlzIGRlZmluZWQgdG8gYmUNCiQkDQp0X3tvYnN9ID0gXGZyYWN7XGJhcnt4fSAtIFxtdV8wfXtzL1xzcXJ0e259fQ0KJCQNCg0KKiAqKkNlbnRlciBkYXRhIHVuZGVyICRIXzAkKio6ICR4X2leXHByaW1lID0geF9pIC0gXGJhcnt4fSArIFxtdV8wJC4gIEZvciAkYiA9IDEkIHRvICRCJH06DQogICsgU2FtcGxlICRYX2JeKiA9IFx7eF8xXiosIC4uLiwgeF9uXipcfSQgZnJvbSAkWCckIHdpdGggcmVwbGFjZW1lbnQNCiAgDQokJFxiYXJ7eH1fYl4qID0gXGZyYWN7MX17bn1cc3VtX3tpPTF9Xm4geF9pXiokJA0KJCRzX2JeKiA9IFxzcXJ0e1xmcmFjezF9e24tMX1cc3VtX3tpPTF9Xm4gKHhfaV4qIC0gXGJhcnt4fV9iXiopXjJ9JCQNCiQkdF9iXiogPSBcZnJhY3tcYmFye3h9X2JeKiAtIFxtdV8wfXtzX2JeKi9cc3FydHtufX0kJA0KDQoNCiogKipDYWxjdWxhdGUgcC12YWx1ZShmb3IgdHdvLXNpZGVkIHRlc3QpOg0KJCQNCnAgPSBcZnJhY3tcI1x7fHRfYl4qfCBcZ2VxIHx0X3tvYnN9fFx9ICsgMX17QiArIDF9DQokJA0KDQoNCioqUHNldWRvLWNvZGUqKg0KDQpcYmVnaW57YWxnb3JpdGhtfQ0KXGNhcHRpb257T25lLVNhbXBsZSBNZWFuIEJvb3RzdHJhcCBUZXN0fQ0KXGJlZ2lue2FsZ29yaXRobWljfVsxXQ0KXEZ1bmN0aW9ue29uZVxfc2FtcGxlXF9tZWFuXF90ZXN0fXtkYXRhLCAkXG11XzAkLCAkQj05OTk5JH0NCiAgICBcU3RhdGUgJG4gXGdldHMgXHRleHR7bGVuZ3RofShkYXRhKSQNCiAgICBcU3RhdGUgJHRfe29ic30gXGdldHMgKFx0ZXh0e21lYW59KGRhdGEpIC0gXG11XzApIC8gKFx0ZXh0e3NkfShkYXRhKS9cc3FydHtufSkkDQogICAgDQogICAgXENvbW1lbnR7Q2VudGVyIGRhdGEgdW5kZXIgJEhfMCR9DQogICAgXFN0YXRlICRjZW50ZXJlZFxfZGF0YSBcZ2V0cyBkYXRhIC0gXHRleHR7bWVhbn0oZGF0YSkgKyBcbXVfMCQNCiAgICANCiAgICBcU3RhdGUgJHReKiBcZ2V0cyBcdGV4dHtudW1lcmljfShCKSQNCiAgICBcRm9yeyRiID0gMSQgdG8gJEIkfQ0KICAgICAgICBcU3RhdGUgJGJvb3RzdHJhcFxfc2FtcGxlIFxnZXRzIFx0ZXh0e3NhbXBsZX0oY2VudGVyZWRcX2RhdGEsIG4sIFx0ZXh0e3JlcGxhY2V9PVRSVUUpJA0KICAgICAgICBcU3RhdGUgJHReKltiXSBcZ2V0cyAoXHRleHR7bWVhbn0oYm9vdHN0cmFwXF9zYW1wbGUpIC0gXG11XzApIC8gKFx0ZXh0e3NkfShib290c3RyYXBcX3NhbXBsZSkvXHNxcnR7bn0pJA0KICAgIFxFbmRGb3INCiAgICANCiAgICBcU3RhdGUgJHBcX3ZhbHVlIFxnZXRzIChcdGV4dHtzdW19KHx0Xip8IFxnZXEgfHRfe29ic318KSArIDEpIC8gKEIgKyAxKSQNCiAgICBcU3RhdGUgXFJldHVybiAkcFxfdmFsdWUsIHRfe29ic30sIHReKiQNClxFbmRGdW5jdGlvbg0KXGVuZHthbGdvcml0aG1pY30NClxlbmR7YWxnb3JpdGhtfQ0KDQoNCioqUiBJbXBsZW1lbnRhdGlvbioqDQoNCmBgYHtyfQ0KIyBPbmUtc2FtcGxlIG1lYW4gdGVzdCB1c2luZyBwYXJhbWV0cmljIGJvb3RzdHJhcA0Kb25lX3NhbXBsZV9ib290c3RyYXBfdGVzdCA8LSBmdW5jdGlvbihkYXRhLCBtdTAsIEIgPSA5OTk5KSB7DQogICAgbiA8LSBsZW5ndGgoZGF0YSkNCiAgICB0X29icyA8LSAobWVhbihkYXRhKSAtIG11MCkgLyAoc2QoZGF0YSkgLyBzcXJ0KG4pKQ0KICAgIA0KICAgICMgQ2VudGVyIGRhdGEgdW5kZXIgSDA6IM68ID0gzrwwDQogICAgY2VudGVyZWRfZGF0YSA8LSBkYXRhIC0gbWVhbihkYXRhKSArIG11MA0KICAgIA0KICAgIHRfc3RhciA8LSBudW1lcmljKEIpDQogICAgZm9yIChiIGluIDE6Qikgew0KICAgICAgICBic19zYW1wbGUgPC0gc2FtcGxlKGNlbnRlcmVkX2RhdGEsIG4sIHJlcGxhY2UgPSBUUlVFKQ0KICAgICAgICB0X3N0YXJbYl0gPC0gKG1lYW4oYnNfc2FtcGxlKSAtIG11MCkgLyAoc2QoYnNfc2FtcGxlKSAvIHNxcnQobikpDQogICAgfQ0KICAgIA0KICAgIHBfdmFsdWUgPC0gKHN1bShhYnModF9zdGFyKSA+PSBhYnModF9vYnMpKSArIDEpIC8gKEIgKyAxKQ0KICAgIA0KICAgIHJldHVybihsaXN0KA0KICAgICAgICBwX3ZhbHVlID0gcF92YWx1ZSwNCiAgICAgICAgdF9vYnMgPSB0X29icywNCiAgICAgICAgYm9vdHN0cmFwX3N0YXRzID0gdF9zdGFyLA0KICAgICAgICBtZWFuX29icyA9IG1lYW4oZGF0YSksDQogICAgICAgIG11MCA9IG11MA0KICAgICkpDQp9DQoNCiMgRXhhbXBsZQ0Kc2V0LnNlZWQoMTIzKQ0KZGF0YSA8LSBybm9ybSgzMCwgbWVhbiA9IDEwNSwgc2QgPSAxNSkNCm11MCA8LSAxMDANCg0KcmVzdWx0IDwtIG9uZV9zYW1wbGVfYm9vdHN0cmFwX3Rlc3QoZGF0YSwgbXUwLCBCID0gMTk5OSkNCmBgYA0KDQoNCiMjIE9uZS1TYW1wbGUgTWVhbiBUZXN0IChQZXJjZW50aWxlIE1ldGhvZCkNCg0KKipNYXRoZW1hdGljYWwgQWxnb3JpdGhtKioNCg0KKiBDb21wdXRlIHNhbXBsZSBtZWFuOiAkXGJhcnt4fSA9IFxmcmFjezF9e259XHN1bV97aT0xfV5uIHhfaSQsIEZvciAkYiA9IDEkIHRvICRCJDoNCiAgKyBTYW1wbGUgJFhfYl4qJCBmcm9tICRYJCB3aXRoIHJlcGxhY2VtZW50DQogICsgJFxiYXJ7eH1fYl4qID0gXGZyYWN7MX17bn1cc3VtX3tpPTF9Xm4geF9pXiokDQoNCiogQ29uc3RydWN0ICQoMS1cYWxwaGEpXCUkIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZnJvbSBwZXJjZW50aWxlcy4gRm9yICRcYmFye3h9X3socSl9JCBpcyB0aGUgJHEkLXRoIHF1YW50aWxlIG9mICRce1xiYXJ7eH1fYl4qXH0kDQokJA0KQ0kgPSBbXGJhcnt4fV97KFxhbHBoYS8yKX0sIFxiYXJ7eH1feygxLVxhbHBoYS8yKX1dDQokJA0KDQoqIERlY2lzaW9uIHJ1bGU6IFJlamVjdCAkSF8wOiBcbXUgPSBcbXVfMCQgaWYgJFxtdV8wIFxub3RpbiBDSSQNCiogUC12YWx1ZSBhcHByb3hpbWF0aW9uOg0KJCQNCnAgXGFwcHJveCAyIFx0aW1lcyBcbWluXGxlZnRbUChcYmFye3h9XiogXGxlcSBcbXVfMCksIFAoXGJhcnt4fV4qIFxnZXEgXG11XzApXHJpZ2h0XQ0KJCQNCg0KDQoNCiMgVHdvLVNhbXBsZSBCb290c3RyYXAgVGVzdHMNCg0KKipNYXRoZW1hdGljYWwgQWxnb3JpdGhtKioNCg0KTGV0ICRYID0gXHt4XzEsIC4uLiwgeF9tXH0kIGFuZCAkWSA9IFx7eV8xLCAuLi4sIHlfblx9JCBiZSB0d28gaW5kZXBlbmRlbnQgc2FtcGxlcy4NCg0KKiAqKk51bGwgaHlwb3RoZXNpcyoqOiAkSF8wOiBcbXVfeCA9IFxtdV95JA0KKiAqKlBvb2xlZCBzdGFuZGFyZCBkZXZpYXRpb24qKiAodW5kZXIgJEhfMCQpOg0KJCQNCnNfcCA9IFxzcXJ0e1xmcmFjeyhtLTEpc194XjIgKyAobi0xKXNfeV4yfXttK24tMn19DQokJA0KDQoqIFRlc3Qgc3RhdGlzdGljfToNCiQkDQp0X3tvYnN9ID0gXGZyYWN7XGJhcnt4fSAtIFxiYXJ7eX19e3NfcFxzcXJ0e1xmcmFjezF9e219ICsgXGZyYWN7MX17bn19fQ0KJCQNCg0KKiAqKkNlbnRlciBhbmQgcG9vbCBkYXRhIHVuZGVyICRIXzAkfSoqOg0KJCR6X2kgPSBcYmVnaW57Y2FzZXN9DQp4X2kgLSBcYmFye3h9ICYgXHRleHR7Zm9yIH0gaSA9IDEsLi4uLG0gXFwNCnlfaSAtIFxiYXJ7eX0gJiBcdGV4dHtmb3IgfSBpID0gMSwuLi4sbg0KXGVuZHtjYXNlc30kJA0KDQoqIEZvciAkYiA9IDEkIHRvICRCJDoNCiAgKyAgJFhfYl4qID0gXHt6XzFeKiwgLi4uLCB6X21eKlx9JCBmcm9tIHBvb2xlZCBkYXRhIHdpdGggcmVwbGFjZW1lbnQNCiAgKyAgU2FtcGxlICRZX2JeKiA9IFx7el97bSsxfV4qLCAuLi4sIHpfe20rbn1eKlx9JCBmcm9tIHBvb2xlZCBkYXRhIHdpdGggcmVwbGFjZW1lbnQNCiAgKyAgQ29tcHV0ZTogZm9yICAkc197cCxifV4qJCBpcyB0aGUgcG9vbGVkIFNEIG9mICRYX2JeKiQgYW5kICRZX2JeKiQNCiQkDQp0X2JeKiA9IFxmcmFje1xiYXJ7eH1fYl4qIC0gXGJhcnt5fV9iXip9e3Nfe3AsYn1eKlxzcXJ0e1xmcmFjezF9e219ICsgXGZyYWN7MX17bn19fQ0KJCQNCg0KKiAqKkNhbGN1bGF0ZSBwLXZhbHVlKio6DQokJA0KcCA9IFxmcmFje1wjXHt8dF9iXip8IFxnZXEgfHRfe29ic318XH0gKyAxfXtCICsgMX0NCiQkDQoNCg0KDQojIExpa2VsaWhvb2QtYmFzZWQgQm9vdHN0cmFwIFRlc3RzDQoNClRoaXMgc2VjdGlvbiBwcmVzZW50cyBCb290c3RyYXAgdmVyc2lvbiBvZiB0aGUgdGhyZWUgbGlrZWxpaG9vZC1iYXNlZCAkXGNoaV4yJCB0ZXN0cy4NCg0KIyMgQm9vdHN0cmFwIFdhbGQgVGVzdA0KDQoqKk1hdGhlbWF0aWNhbCBGb3JtdWxhdGlvbioqDQoNCkZvciBhIHBhcmFtZXRlciAkXHRoZXRhJCB3aXRoIG51bGwgaHlwb3RoZXNpcyAkSF8wOiBcdGhldGEgPSBcdGhldGFfMCQ6DQoNCiogKipNYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdG9yKio6ICRcaGF0e1x0aGV0YX0kDQoqICoqRXN0aW1hdGVkIHZhcmlhbmNlKiogJFx3aWRlaGF0e1Zhcn0oXGhhdHtcdGhldGF9KSQNCiogKipXYWxkIHN0YXRpc3RpYyoqDQokJA0KVyA9IFxmcmFjeyhcaGF0e1x0aGV0YX0gLSBcdGhldGFfMCleMn17XHdpZGVoYXR7VmFyfShcaGF0e1x0aGV0YX0pfQ0KJCQNClVuZGVyICRIXzAkLCAkVyBcc2ltIFxjaGleMl8xJCBhc3ltcHRvdGljYWxseQ0KDQoNCg0KKipCb290c3RyYXAgQWxnb3JpdGhtKioNCg0KKiAgQ29tcHV0ZSAkXGhhdHtcdGhldGF9JCBhbmQgJFdfe29ic30kIGZyb20gb3JpZ2luYWwgZGF0YQ0KKiAgRm9yICRiID0gMSQgdG8gJEIkOg0KICArICBHZW5lcmF0ZSBib290c3RyYXAgc2FtcGxlICRYX2JeKiQNCiAgKyAgQ29tcHV0ZSAkXGhhdHtcdGhldGF9X2JeKiQgZnJvbSAkWF9iXiokDQogICsgIEVzdGltYXRlICRcd2lkZWhhdHtWYXJ9X2JeKihcaGF0e1x0aGV0YX1fYl4qKSQgdXNpbmcgbmVzdGVkIGJvb3RzdHJhcA0KICArICBDb21wdXRlOg0KJCQNCldfYl4qID0gXGZyYWN7KFxoYXR7XHRoZXRhfV9iXiogLSBcaGF0e1x0aGV0YX0pXjJ9e1x3aWRlaGF0e1Zhcn1fYl4qKFxoYXR7XHRoZXRhfV9iXiopfQ0KJCQNCg0KKiBDYWxjdWxhdGUgcC12YWx1ZToNCiQkDQpwID0gXGZyYWN7XCNce1dfYl4qIFxnZXEgV197b2JzfVx9ICsgMX17QiArIDF9DQokJA0KDQoNCioqUiBJbXBsZW1lbnRhdGlvbioqDQoNCmBgYHtyfQ0KIyBCb290c3RyYXAgV2FsZCB0ZXN0IGZvciBtZWFuDQpib290c3RyYXBfd2FsZF90ZXN0IDwtIGZ1bmN0aW9uKGRhdGEsIHRoZXRhMCwgQiA9IDk5OSwgaW5uZXJfQiA9IDEwMCkgew0KICAgIG4gPC0gbGVuZ3RoKGRhdGEpDQogICAgdGhldGFfaGF0IDwtIG1lYW4oZGF0YSkNCiAgICANCiAgICAjIEJvb3RzdHJhcCBmb3IgdmFyaWFuY2UgZXN0aW1hdGlvbg0KICAgIGJvb3RfdGhldGEgPC0gbnVtZXJpYyhCKQ0KICAgIGZvciAoYiBpbiAxOkIpIHsNCiAgICAgICAgYnNfc2FtcGxlIDwtIHNhbXBsZShkYXRhLCBuLCByZXBsYWNlID0gVFJVRSkNCiAgICAgICAgYm9vdF90aGV0YVtiXSA8LSBtZWFuKGJzX3NhbXBsZSkNCiAgICB9DQogICAgDQogICAgIyBXYWxkIHN0YXRpc3RpYw0KICAgIHZhcl90aGV0YSA8LSB2YXIoYm9vdF90aGV0YSkNCiAgICBXX29icyA8LSAodGhldGFfaGF0IC0gdGhldGEwKV4yIC8gdmFyX3RoZXRhDQogICAgDQogICAgIyBCb290c3RyYXAgZGlzdHJpYnV0aW9uIG9mIFdhbGQgc3RhdGlzdGljDQogICAgV19zdGFyIDwtIG51bWVyaWMoQikNCiAgICBmb3IgKGIgaW4gMTpCKSB7DQogICAgICAgIGJzX3NhbXBsZSA8LSBzYW1wbGUoZGF0YSwgbiwgcmVwbGFjZSA9IFRSVUUpDQogICAgICAgIHRoZXRhX3N0YXIgPC0gbWVhbihic19zYW1wbGUpDQogICAgICAgIA0KICAgICAgICAjIElubmVyIGJvb3RzdHJhcCBmb3IgdmFyaWFuY2UgZXN0aW1hdGlvbg0KICAgICAgICBpbm5lcl9ib290IDwtIG51bWVyaWMoaW5uZXJfQikNCiAgICAgICAgZm9yIChqIGluIDE6aW5uZXJfQikgew0KICAgICAgICAgICAgaW5uZXJfc2FtcGxlIDwtIHNhbXBsZShic19zYW1wbGUsIG4sIHJlcGxhY2UgPSBUUlVFKQ0KICAgICAgICAgICAgaW5uZXJfYm9vdFtqXSA8LSBtZWFuKGlubmVyX3NhbXBsZSkNCiAgICAgICAgfQ0KICAgICAgICB2YXJfc3RhciA8LSB2YXIoaW5uZXJfYm9vdCkNCiAgICAgICAgDQogICAgICAgIFdfc3RhcltiXSA8LSAodGhldGFfc3RhciAtIHRoZXRhX2hhdCleMiAvIHZhcl9zdGFyDQogICAgfQ0KICAgIA0KICAgIHBfdmFsdWUgPC0gKHN1bShXX3N0YXIgPj0gV19vYnMpICsgMSkgLyAoQiArIDEpDQogICAgDQogICAgcmV0dXJuKGxpc3QoDQogICAgICAgIFdfb2JzID0gV19vYnMsDQogICAgICAgIHBfdmFsdWUgPSBwX3ZhbHVlLA0KICAgICAgICB0aGV0YV9oYXQgPSB0aGV0YV9oYXQsDQogICAgICAgIHRoZXRhMCA9IHRoZXRhMA0KICAgICkpDQp9DQpgYGANCg0KDQojIyBCb290c3RyYXAgU2NvcmUgVGVzdCANCg0KKipNYXRoZW1hdGljYWwgRm9ybXVsYXRpb24qKg0KDQpGb3IgYSBwYXJhbWV0ZXIgJFx0aGV0YSQ6DQoNCiogKipTY29yZSBmdW5jdGlvbioqOiAkVShcdGhldGEpID0gXGZyYWN7XHBhcnRpYWwgXGVsbChcdGhldGEpfXtccGFydGlhbCBcdGhldGF9JA0Kd2hlcmUgJFxlbGwoXHRoZXRhKSQgaXMgdGhlIGxvZy1saWtlbGlob29kDQoqICoqRmlzaGVyIGluZm9ybWF0aW9uKio6ICRJKFx0aGV0YSkgPSAtRVxsZWZ0W1xmcmFje1xwYXJ0aWFsXjIgXGVsbChcdGhldGEpfXtccGFydGlhbCBcdGhldGFeMn1ccmlnaHRdJA0KKiAqKlNjb3JlIHN0YXRpc3RpY30qKjogVW5kZXIgJEhfMDogXHRoZXRhID0gXHRoZXRhXzAkLCB0aGUgZm9sbG93aW5nIHNjb3JlIHRlc3Qgc3RhdGlzdGljICRTIFxzaW0gXGNoaV4yXzEkIGFzeW1wdG90aWNhbGx5DQokJA0KUyA9IFxmcmFje1UoXHRoZXRhXzApXjJ9e0koXHRoZXRhXzApfQ0KJCQNCg0KDQoqKkJvb3RzdHJhcCBBbGdvcml0aG0qKg0KDQoqIENvbXB1dGUgJFUoXHRoZXRhXzApJCBhbmQgJEkoXHRoZXRhXzApJCB1bmRlciAkSF8wJA0KKiBDb21wdXRlICRTX3tvYnN9JA0KKiBGb3IgJGIgPSAxJCB0byAkQiQ6DQogICsgR2VuZXJhdGUgZGF0YSB1bmRlciAkSF8wJDogJFhfYl4qIFxzaW0gZih4fFx0aGV0YV8wKSQNCiAgKyBDb21wdXRlICRVX2IoXHRoZXRhXzApJCBmcm9tICRYX2JeKiQNCiAgKyBDb21wdXRlOg0KJCQNClNfYl4qID0gXGZyYWN7VV9iKFx0aGV0YV8wKV4yfXtJKFx0aGV0YV8wKX0NCiQkDQoNCiogQ2FsY3VsYXRlIHAtdmFsdWU6DQokJA0KcCA9IFxmcmFje1wjXHtTX2JeKiBcZ2VxIFNfe29ic31cfSArIDF9e0IgKyAxfQ0KJCQNCiANCg0KIyMgQm9vdHN0cmFwIExpa2VsaWhvb2QgUmF0aW8gVGVzdCANCg0KKipNYXRoZW1hdGljYWwgRm9ybXVsYXRpb24qKg0KDQoqICoqTGlrZWxpaG9vZCBmdW5jdGlvbioqOiAkTChcdGhldGEpID0gXHByb2Rfe2k9MX1ebiBmKHhfaXxcdGhldGEpJA0KKiAqKk1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0b3IqKjogJFxoYXR7XHRoZXRhfSA9IFxhcmdcbWF4X1x0aGV0YSBMKFx0aGV0YSkkDQoqICoqTGlrZWxpaG9vZCByYXRpbyoqOg0KJCQNClxMYW1iZGEgPSBcZnJhY3tMKFx0aGV0YV8wKX17TChcaGF0e1x0aGV0YX0pfQ0KJCQNCiogKipMaWtlbGlob29kIHJhdGlvIHN0YXRpc3RpYyoqOiBVbmRlciAkSF8wOiBcdGhldGEgPSBcdGhldGFfMCQsICRMUiA9IC0yXGxvZ1xMYW1iZGEgXHNpbSBcY2hpXjJfMSQgYXN5bXB0b3RpY2FsbHkgZGlzdHJpYnV0ZWQgYXMgJFxjaGleMl97ZC5mfSQNCiQkDQotMlxsb2dcTGFtYmRhID0gLTJbXGxvZyBMKFx0aGV0YV8wKSAtIFxsb2cgTChcaGF0e1x0aGV0YX0pXQ0KJCQNCg0KDQoNCioqQm9vdHN0cmFwIEFsZ29yaXRobSoqDQoNCiogQ29tcHV0ZSAkLTJcbG9nXExhbWJkYV97b2JzfSQgZnJvbSBvcmlnaW5hbCBkYXRhDQoqIEZvciAkYiA9IDEkIHRvICRCJDoNCiAgKyAgR2VuZXJhdGUgZGF0YSB1bmRlciAkSF8wJDogJFhfYl4qIFxzaW0gZih4fFx0aGV0YV8wKSQNCiAgKyBDb21wdXRlIE1MRSAkXGhhdHtcdGhldGF9X2JeKiQgZnJvbSAkWF9iXiokDQogICsgQ29tcHV0ZSAkLTJcbG9nXExhbWJkYV9iXiokDQoNCiogIENhbGN1bGF0ZSBwLXZhbHVlOg0KJCQNCnAgPSBcZnJhY3tcI1x7LTJcbG9nXExhbWJkYV9iXiogXGdlcSAtMlxsb2dcTGFtYmRhX3tvYnN9XH0gKyAxfXtCICsgMX0NCiQkDQoNCg0KKipSIEltcGxlbWVudGF0aW9uKioNCg0KYGBge3J9DQojIEJvb3RzdHJhcCBMaWtlbGlob29kIFJhdGlvIHRlc3QgZm9yIG5vcm1hbCBtZWFuDQpib290c3RyYXBfbHJ0X3Rlc3QgPC0gZnVuY3Rpb24oZGF0YSwgbXUwLCBCID0gOTk5OSkgew0KICAgIG4gPC0gbGVuZ3RoKGRhdGEpDQogICAgDQogICAgIyBNTEVzIChhc3N1bWluZyB1bmtub3duIHZhcmlhbmNlKQ0KICAgIG11X2hhdCA8LSBtZWFuKGRhdGEpDQogICAgc2lnbWFfaGF0IDwtIHNkKGRhdGEpICogc3FydCgobiAtIDEpIC8gbikgICMgTUxFIG9mIM+DDQogICAgDQogICAgIyBVc2luZyBzYW1lIHNpZ21hIGVzdGltYXRlIGZvciBib3RoIGZvciBmYWlyIGNvbXBhcmlzb24NCiAgICBzaWdtYTAgPC0gc2QoZGF0YSkgKiBzcXJ0KChuIC0gMSkgLyBuKQ0KICAgIA0KICAgICMgTG9nLWxpa2VsaWhvb2RzDQogICAgbG9nTF9oYXQgPC0gc3VtKGRub3JtKGRhdGEsIG1lYW4gPSBtdV9oYXQsIHNkID0gc2lnbWFfaGF0LCBsb2cgPSBUUlVFKSkNCiAgICBsb2dMMCA8LSBzdW0oZG5vcm0oZGF0YSwgbWVhbiA9IG11MCwgc2QgPSBzaWdtYTAsIGxvZyA9IFRSVUUpKQ0KICAgIA0KICAgICMgTFIgc3RhdGlzdGljDQogICAgTFJfb2JzIDwtIC0yICogKGxvZ0wwIC0gbG9nTF9oYXQpDQogICAgDQogICAgIyBCb290c3RyYXAgZGlzdHJpYnV0aW9uDQogICAgTFJfc3RhciA8LSBudW1lcmljKEIpDQogICAgZm9yIChiIGluIDE6Qikgew0KICAgICAgICAjIEdlbmVyYXRlIGRhdGEgdW5kZXIgSDA6IE4ozrwwLCDPgzApDQogICAgICAgIGJzX3NhbXBsZSA8LSBybm9ybShuLCBtZWFuID0gbXUwLCBzZCA9IHNpZ21hMCkNCiAgICAgICAgDQogICAgICAgICMgTUxFIGZyb20gYm9vdHN0cmFwIHNhbXBsZQ0KICAgICAgICBtdV9zdGFyIDwtIG1lYW4oYnNfc2FtcGxlKQ0KICAgICAgICBzaWdtYV9zdGFyIDwtIHNkKGJzX3NhbXBsZSkgKiBzcXJ0KChuIC0gMSkgLyBuKQ0KICAgICAgICANCiAgICAgICAgIyBMaWtlbGlob29kcw0KICAgICAgICBsb2dMX3N0YXIgPC0gc3VtKGRub3JtKGJzX3NhbXBsZSwgbWVhbiA9IG11X3N0YXIsIHNkID0gc2lnbWFfc3RhciwgbG9nID0gVFJVRSkpDQogICAgICAgIGxvZ0xfc3RhcjAgPC0gc3VtKGRub3JtKGJzX3NhbXBsZSwgbWVhbiA9IG11MCwgc2QgPSBzaWdtYTAsIGxvZyA9IFRSVUUpKQ0KICAgICAgICANCiAgICAgICAgTFJfc3RhcltiXSA8LSAtMiAqIChsb2dMX3N0YXIwIC0gbG9nTF9zdGFyKQ0KICAgIH0NCiAgICANCiAgICBwX3ZhbHVlIDwtIChzdW0oTFJfc3RhciA+PSBMUl9vYnMpICsgMSkgLyAoQiArIDEpDQogICAgDQogICAgcmV0dXJuKGxpc3QoDQogICAgICAgIExSX29icyA9IExSX29icywNCiAgICAgICAgcF92YWx1ZSA9IHBfdmFsdWUsDQogICAgICAgIG11X2hhdCA9IG11X2hhdCwNCiAgICAgICAgbXUwID0gbXUwDQogICAgKSkNCn0NCmBgYA0KDQoNCiMjIE5vbi1QZXJtdXRhdGlvbiBUZXN0IHdpdGggQm9vdHN0cmFwDQoNCioqTWF0aGVtYXRpY2FsIEFsZ29yaXRobSoqDQoNCiogKipDb21iaW5lIGdyb3VwcyoqOiAkWiA9IFx7eF8xLCAuLi4sIHhfbSwgeV8xLCAuLi4sIHlfblx9JA0KKiAqKk9ic2VydmVkIHN0YXRpc3RpYyoqOiAkVF97b2JzfSA9IFxiYXJ7eH0gLSBcYmFye3l9JA0KKiBGb3IgJGIgPSAxJCB0byAkQiR9Og0KICArIFJhbmRvbWx5IHBlcm11dGUgJFokIHRvIGdldCAkWl9iXiokDQogICsgQXNzaWduIGZpcnN0ICRtJCBlbGVtZW50cyB0byAkWF9iXiokLCByZW1haW5pbmcgJG4kIHRvICRZX2JeKiQNCiAgKyBDb21wdXRlICRUX2JeKiA9IFxiYXJ7eH1fYl4qIC0gXGJhcnt5fV9iXiokDQoNCiogKipDYWxjdWxhdGUgcC12YWx1ZSoqOg0KJCQNCnAgPSBcZnJhY3tcI1x7fFRfYl4qfCBcZ2VxIHxUX3tvYnN9fFx9ICsgMX17QiArIDF9DQokJA0KDQoNCg0KKipDaG9vc2luZyBOdW1iZXIgb2YgQm9vdHN0cmFwIFNhbXBsZXMqKg0KDQpUaGUgY2hvaWNlIG9mICRCJCAobnVtYmVyIG9mIGJvb3RzdHJhcCBzYW1wbGVzKSBhZmZlY3RzIHRoZSBwcmVjaXNpb24gb2YgcC12YWx1ZSBlc3RpbWF0aW9uLiBSZWNvbW1lbmRlZCB2YWx1ZXM6DQoNCiogRm9yIHAtdmFsdWVzIG5lYXIgMC4wNTogJEIgXGdlcSA5OTkkDQoqIEZvciBwdWJsaWNhdGlvbiBxdWFsaXR5OiAkQiBcZ2VxIDk5OTkkDQoqIEZvciBjb25maWRlbmNlIGludGVydmFsczogJEIgXGdlcSAxMDAwJCBmb3IgOTVcJSBDSSwgbW9yZSBmb3IgaGlnaGVyIGNvbmZpZGVuY2UgbGV2ZWxzDQogDQoNClRoZSBzdGFuZGFyZCBlcnJvciBvZiBhIGJvb3RzdHJhcCBwLXZhbHVlIGlzIGFwcHJveGltYXRlbHk6DQokJA0KXHRleHR7U0V9KHApID0gXHNxcnR7XGZyYWN7cCgxLXApfXtCfX0NCiQkDQoNCkZvciAkcCA9IDAuMDUkIGFuZCAkQiA9IDEwMDAkLCAkXHRleHR7U0V9IFxhcHByb3ggMC4wMDY5JCwgZ2l2aW5nIGFib3V0IDEuNCBwZXJjZW50YWdlIHBvaW50cyBvZiB1bmNlcnRhaW50eS4NCg0KIyBDb25jbHVzaW9uDQoNClRoaXMgZG9jdW1lbnQgaGFzIHByZXNlbnRlZCBjb21wcmVoZW5zaXZlIG1ldGhvZHMgZm9yIGJvb3RzdHJhcCBoeXBvdGhlc2lzIHRlc3RpbmcgaW4gUiwgY292ZXJpbmcgb25lLXNhbXBsZSB0ZXN0cywgdHdvLXNhbXBsZSB0ZXN0cywgYW5kIGFsdGVybmF0aXZlIHRlc3Qgc3RhdGlzdGljcyAoV2FsZCwgU2NvcmUsIGFuZCBsaWtlbGlob29kIHJhdGlvIHRlc3RzKS4gRWFjaCBtZXRob2QgaW5jbHVkZXMgbWF0aGVtYXRpY2FsIGZvcm11bGF0aW9uLCBhbGdvcml0aG1pYyBzdGVwcywgcHNldWRvLWNvZGUsIGFuZCBwcmFjdGljYWwgUiBpbXBsZW1lbnRhdGlvbnMuIEJvb3RzdHJhcCBtZXRob2RzIHByb3ZpZGUgcm9idXN0IGFsdGVybmF0aXZlcyB0byBjbGFzc2ljYWwgdGVzdHMsIGVzcGVjaWFsbHkgd2hlbiBkaXN0cmlidXRpb25hbCBhc3N1bXB0aW9ucyBhcmUgdmlvbGF0ZWQgb3Igc2FtcGxlIHNpemVzIGFyZSBzbWFsbC4NCg0KDQo=