# Clear variables
rm(list=ls())
library(knitr)
Data Prepossessing
Input the dataWind of power production. The total dataWind set can be divided into three categories:
- 3 variables to describe the time (days since 1/1-2003, month, and day of month),
- 1 variable describing the observed output (power)
- 2 meteorological variable.
The individual variables are described in Table 1. 
# Data Processing
dataWind <- read.table("tuno.dat")
r.day <- dataWind[ , 1]
month <- dataWind[ , 2]
day <- dataWind[ , 3]
power <- dataWind[ , 4] / 5000 # Normalize power output
windSpeed <- dataWind[ , 5]
windDirection <- dataWind[ , 6]
tuno <- data.frame(r.day, month, day, power, windSpeed, windDirection)
A wind power plant where all turbines stand still will actually have a negative production such numbers are set equal to zero in the original dataWind set. Hence the dataWind set consisted of numbers between 0 and 5,000 kW. Average daily values that are exactly zero are thought of as special phenomena where, for one reason or another the plant is down, these observations are removed from the dataWind set, which we will set as censored observation.
r.day <- setdiff(seq(1, length(tuno$r.day)), tuno$r.day)
month <- rep(0, length(r.day))
day <- rep(0, length(r.day))
power <- rep(0, length(r.day))
windSpeed <- rep(0, length(r.day))
windDirection <- rep(0, length(r.day))
tuno.zero <- data.frame(r.day, month, day, power, windSpeed, windDirection)
tuno.all <- rbind(tuno, tuno.zero)
Summary Statistics
## Plot: Normalized Power Production in Tuno Farm with Censored Observation
plot(tuno.all[order(tuno.all$r.day), ]$r.day, tuno.all[order(tuno.all$r.day), ]$windSpeed, type = "l", col = "blue", main = "Normalized Power Production in Tuno Farm with Censored Observation", xlab = "day since 1 Jan 2003", ylab = "Normalized Power Production")
points(tuno.zero$r.day, tuno.zero$power, pch = 4, col = 2)

Besides plotting the power output as a function of time, there is no meaning to look into the censored data point in other analysis, because there is no corresponding measured values of wind speed and wind direction. So we just continue analysis without censored values.
plot(tuno$windSpeed, tuno$power, main = "Normailized Power Production as a Function of Wind Speed in Tuno Farm", xlab = "Wind Speed (m/s)", ylab = "Normalized Power Production")

plot(tuno$windDirection, tuno$power, main = "Normailized Power Production as a Function of Wind Direction in Tuno Farm", xlab = "Wind Direction (rad)", ylab = "Normalized Power Production")

## Summary statistics by groups: Normailized Power Production as a Factor of Month in Tuno Farm
plot(factor(tuno$month), tuno$power, main = "Normailized Power Production as a Factor of Month in Tuno Farm", xlab = "Month", ylab = "Normalized Power Production")

We can’t derive any conclusion from this boxplot.
Simple Models of Wind Production
hist(tuno$power, prob = TRUE, breaks = 50, main = "Histogram of Power Production in Tuno Farm", xlab = "Normalized Power Production", col = "gray90")
lines(density(tuno$power), lwd = 2, col = 4)

From the histogram, we can say there is no obvious distribution of the power output, but we can plot the usual distribution to compare.
Graphically, it seems there are two candidate probability distributions for the wind power, namely the gamma and the beta distribution respectively.
Gamma Distribution of Power Production
# Define function that return negative log-likelihood for gamma distribution
negaLogL_gamma <- function(pars, dataWind){
alpha <- pars[1]
theta <- pars[2]
return (-sum(dgamma(x = dataWind, shape = alpha, scale = theta, log = TRUE)))
}
# Optimize alpha and theta parameters in gamma distribution for power production
pars_gammaD.optim_power <- optim(c(1, 1), fn = negaLogL_gamma, gr = NULL, dataWind = tuno$power)
# Plot optimized gamma distribution density and density of power production
density_power <- density(tuno$power, adjust = 0.7)
x_power = seq( 0, 1.2, length.out = 5000) # To smooth the curve ???
alpha_gammaD.optim_power <- pars_gammaD.optim_power$par[1]
theta_gammaD.optim_power <- pars_gammaD.optim_power$par[2]
gammaD.optim_power = dgamma(x_power, shape = alpha_gammaD.optim_power, scale = theta_gammaD.optim_power)
##
hist(tuno$power, breaks = 50, prob = TRUE, main = NULL, xlab = "Normalized Power Production (kW / 5000kW)", ylab = "Density", col = "gray95")
lines(density_power$x, density_power$y, type = 'l', xlim = c(0,1), ylim =c(0,3), col = 'blue', lwd = 2)
lines(x_power, gammaD.optim_power, xlim = c(0,1), col = 'red', lwd = 2, lty = 2)
title(main = "Gamma Distribution by MLE and Density of Power Production in Tuno Farm", font.main = 3)
legend("topright", inset = .02, legend = c("Gamma Distribution by MLE", "Density"), col = c("red", "blue"), lty = c(1, 2), lwd = c(2, 2), cex = 0.8)

Beta Distribution of Power Production
# Optimize alpha and beta parameters in beta distribution for power production
negaLogL_beta <- function(pars, dataWind){
alpha <- pars[1]
beta <- pars[2]
return(- sum(dbeta(x = dataWind, shape1 = alpha, shape2 = beta, log = TRUE)))
}
pars_betaD.optim_power <- optim(c(1,1), fn = negaLogL_beta, gr = NULL, dataWind = tuno$power)
# Initialize optimized beta distribution density and density of power production
alpha_betaD.optim_power <- pars_betaD.optim_power$par[1]
beta_betaD.optim_power <- pars_betaD.optim_power$par[2]
betaD.optim_power <- dbeta(x_power, shape1 = alpha_betaD.optim_power, shape2 = beta_betaD.optim_power)
## Beta Distribution by MLE and Density of Power Production in Tuno Farm
hist(tuno$power, breaks = 50, prob = TRUE, main = NULL, xlab = "Normalized Power Production (kW / 5000kW)", ylab = "Density", col = "gray95")
lines(density_power$x, density_power$y, type = 'l', xlim = c(0,1), ylim =c(0,3), col = 'blue', lwd = 2)
lines(x_power, betaD.optim_power, xlim = c(0,1), col = 'red', lwd = 2, lty = 2)
title(main = "Beta Distribution by MLE and Density of Power Production in Tuno Farm", font.main = 3)
legend("topright", inset = .02, legend = c("Beta Distribution by MLE", "Density"), col=c("red", "blue"), lty = c(1, 2), lwd = c(2, 2), cex = 0.8)

Transformation 1 of Wind Production
trans_1 <- function(lambda, y){
y.lambda <- 1 / lambda * log(y^lambda / (1 - y^lambda))
return(y.lambda)
}
profileLogL_1 <- function(lambda, y){
n <- length(y)
y.lambda <- trans_1(lambda, y)
mu <- 1 / n * sum(y.lambda) # Mean of y.lambda
sigma.square <- 1 / n * sum((y.lambda - mu)^2)
y.lambda.partial <- y^(lambda - 1) / (1 - y^lambda) + 1 / y
return(- n / 2 * log(sigma.square) - 1 / (2 * sigma.square) * sum((y.lambda - mu)^2) + sum(log(y.lambda.partial)))
}
lambda_1 <- seq(0.1, 0.5, by = 0.01)
profileLogL.1 <- sapply(lambda_1, profileLogL_1, y = tuno$power)
plot(lambda_1, profileLogL.1 - max(profileLogL.1), type = "l", ylab = "Log Likelihood")
lines(range(lambda_1),- qchisq(0.95, df = 1) / 2 * c(1,1), lty = 2, col = 2)
title("Profile Likelihood for Transformation 1 of Power Production")

profileLogL.1.optim <- optimize(profileLogL_1, c(0.1, 0.5), y = tuno$power, maximum = TRUE)
lambda_profileLogL.1.optim <- profileLogL.1.optim$maximum
power.trans_1.optim <- trans_1(lambda_profileLogL.1.optim, tuno$power)
qqnorm(power.trans_1.optim, main = "QQ-plot of Power Production after Transformation 1")
qqline(power.trans_1.optim)

hist(power.trans_1.optim, main = "Histogram of Power Production after MLE for Transformation 1", xlab = paste("Normalized power production when lambda = ", lambda_profileLogL.1.optim), ylab = "Density", col = "gray90")

Transformation 2 of Wind Production
trans_2 <- function(lambda, y){
y.lambda <- 2 * log(y^lambda / (1 - y)^(1 - lambda))
return(y.lambda)
}
profileLogL_2 <- function(lambda, y){
n <- length(y)
y.lambda <- trans_2(lambda, y)
mu <- 1 / n * sum(y.lambda) # Mean of y.lambda
sigma.square <- 1 / n * sum((y.lambda - mu)^2)
y.lambda.partial <- 2 * (lambda / y - (lambda - 1) / (1 - y))
return(- n / 2 * log(sigma.square) - 1 / (2 * sigma.square) * sum((y.lambda - mu)^2) + sum(log(y.lambda.partial)))
}
lambda_2 <- seq(0, 1, by = 0.01)
profileLogL.2 <- sapply(lambda_2, profileLogL_2, y = tuno$power)
plot(lambda_2, profileLogL.2 - max(profileLogL.2), type = "l", ylab = "Log Likelihood", ylim = c(-100, 0))
lines(range(lambda_2),- qchisq(0.95, df = 1) / 2 * c(1,1), lty = 2, col = 2)
title("Profile Likelihood for Transformation 2 of Power Production")

profileLogL.2.optim <- optimize(profileLogL_2, c(0, 1), y = tuno$power, maximum = TRUE)
lambda_profileLogL.2.optim <- profileLogL.2.optim$maximum
power.trans_2.optim <- trans_2(lambda_profileLogL.2.optim, tuno$power)
power.trans_2.optimMLE <- trans_2(0.2523298, tuno$power)
qqnorm(power.trans_2.optim, main = "QQ-plot of Power Production after Transformation 2")
qqline(power.trans_2.optim)

hist(power.trans_2.optim, main = "Histogram of Power Production after MLE for Transformation 2", xlab = paste("Normalized power production when lambda = ", lambda_profileLogL.2.optim), ylab = "Density", col = "gray90")

Cox-Box Transformation of Wind Production
trans_boxCox <- function(lambda,y){
y.lambda <- (y^lambda - 1) / lambda
if(lambda == 0){
y.lambda <- log(y)}
return(y.lambda)
}
# Profile Log-Likelihood for Lambda
profileLogL_boxCox <- function(lambda,y){
n <- length(y)
y.lambda <- trans_boxCox(lambda, y)
sigma.square <- 1 / n * sum((y.lambda - mean(y.lambda))^2)
return(- n / 2 * log(sigma.square) - n / 2 + (lambda - 1) * sum(log(y)))
}
# Plot the profile likelihood
lambda_boxCox <- seq(0.2, 0.5, by = 0.01)
profileLogL.BoxCox <- sapply(lambda_boxCox, profileLogL_boxCox, y = tuno$power)
plot(lambda_boxCox, profileLogL.BoxCox - max(profileLogL.BoxCox), type = "l", ylab = "Log Likelihood")
lines(range(lambda_boxCox),- qchisq(0.95, df = 1) / 2 * c(1,1), lty = 2, col = 2)
title("Profile Likelihood for Box-Cox Transformation of Power Production")

profileLogL.boxCox.optim <- optimize(profileLogL_boxCox, c(0.2, 0.5), y = tuno$power, maximum = TRUE)
lambda_profileLogL.BoxCox.optim <- profileLogL.boxCox.optim$maximum
# so lambda in the range to (with 95% confidence)
power.trans_boxCox.optim <- trans_boxCox(lambda_profileLogL.BoxCox.optim, tuno$power)
qqnorm(power.trans_boxCox.optim, main = "QQ-plot of Power Production after Box-Cox Transformation")
qqline(power.trans_boxCox.optim)

hist(power.trans_boxCox.optim, main = "Histogram of Power Production after MLE for Box-Cox Transformation", xlab = paste("Normalized power production when lambda = ", lambda_profileLogL.BoxCox.optim), ylab = "Density", col = "gray90")

The result of the simple model is:
| Tranformation_1 |
0.2620908 |
| Tranformation_2 |
0.2523298 |
| Box-Cox Tranformation |
0.3467532 |
Regression Model


With two or more continuous explanatory variables (i.e. in a multiple regression; see p. 395) it is valuable to be able to check for subtle dependencies between the explanatory variables. The pairs function plots every variable in the dataframe on the y axis against every other variable on the x axis:
# Scatterplot Matrices
pairs(cbind(y.trans, x1, x2), labels = c('power', 'windSpeed', 'windDirection'), panel = panel.smooth)

The response variables are named in the rows and the explanatory variables are named in the columns.
Regression Model 1: Multiple, y.trans ~ x1, x1^2, x1^3, x2
The initial model is based on the dataWind that has not been transformed and will therefore be a non-normal model. A rather complex structure is initially chosen since that will allow us to either iteratively remove non-significant parameters or expand our model if it is necessary.
# Initialize regression model 1
regression_1 <- glm(y.trans ~ x1 + I(x1^2) + I(x1^3) + x2) # I() is the identity function that returns its argument unaltered.
# Plot the rediction result of regression model 1
predictRegression.windSpeed(x1.pred, x2, y.trans, regression_1)
title(main = 'Prediction of Linear Regression Model 1 of Wind Power Production')

Result of the regression analysis 1.
| (Intercept) |
-7.5562453 |
1.3392233 |
-5.6422592 |
0.0000000 |
| x1 |
1.4281383 |
0.3944942 |
3.6201756 |
0.0003487 |
| I(x1^2) |
-0.0293810 |
0.0352472 |
-0.8335693 |
0.4052267 |
| I(x1^3) |
0.0000464 |
0.0009144 |
0.0507881 |
0.9595302 |
| x2 |
0.0166491 |
0.1071807 |
0.1553372 |
0.8766662 |
qqnorm(regression_1$residuals, main = "Normal Q-Q Plot for Regression Model 1")
qqline(regression_1$residuals)

residuals_regression_1 <- regression_1$residuals # sample
hist_residuals_regression_1 <- hist(residuals_regression_1, plot = FALSE) # generate hist
plot(hist_residuals_regression_1, col = "grey", main = 'Histogram of residuals for Regression Model 1', xlab = 'Residuals') # plot hist
xlines <- seq(min(hist_residuals_regression_1$breaks), max(hist_residuals_regression_1$breaks), length.out = 100) # seq of x for pdf
lines(xlines, dnorm(xlines, mean(residuals_regression_1), sd(residuals_regression_1)) * length(residuals_regression_1) * diff(hist_residuals_regression_1$breaks)[1], col = "lightcoral", lwd = 2)

A histogram of the residuals is shown in the above figure, where it seems that they are quite well-behaved and approximately follows a normal distribution.
Regression Model 3: Multiple, y.trans ~ x1^2, x1^3, x2
# Initialize regression model 3 using glm
regression_3 <- glm(y.trans ~ I(x1^2) + I(x1^3) + x2) # I() is the identity function that returns its argument unaltered.
# Plot the rediction result of regression model 3
predictRegression.windSpeed(x1.pred, x2, y.trans, regression_3)
title(main = 'Prediction of Linear Regression Model 3 of Wind Power Production')

Result of the regression analysis 3.
| (Intercept) |
-3.0502607 |
0.5046647 |
-6.0441334 |
0.0000000 |
| I(x1^2) |
0.0954312 |
0.0074835 |
12.7522030 |
0.0000000 |
| I(x1^3) |
-0.0030520 |
0.0003286 |
-9.2870823 |
0.0000000 |
| x2 |
0.0119431 |
0.1094331 |
0.1091361 |
0.9131716 |
Regression Model 4: Multiple, y.trans ~ x1^3, x2
# Initialize regression model 4
regression_4 <- glm(y.trans ~ I(x1^3) + x2) # I() is the identity function that returns its argument unaltered.
# Plot the rediction result of regression model 4
predictRegression.windSpeed(x1.pred, x2, y.trans, regression_4)
title(main = 'Prediction of Linear Regression Model 4 of Wind Power Production')

Result of the regression analysis 4.
| (Intercept) |
0.4286595 |
0.5314930 |
0.8065197 |
0.4206158 |
| I(x1^3) |
0.0010285 |
0.0000937 |
10.9737963 |
0.0000000 |
| x2 |
0.1771172 |
0.1360289 |
1.3020556 |
0.1939487 |
qqnorm(regression_4$residuals, main = "Normal Q-Q Plot for Regression Model 4")
qqline(regression_4$residuals)

Regression Model 2: Polynomial, y.trans ~ x1, x1^2, x1^3
# Initialize regression model 2
regression_2 <- glm(y.trans ~ x1 + I(x1^2) + I(x1^3))
# Plot the rediction result of regression model 2
predictRegression.windSpeed(x1.pred, x2, y.trans, regression_2)
title(main = 'Prediction of Linear Regression Model 2 of Wind Power Production')

Result of the regression analysis 2.
| (Intercept) |
-7.5031520 |
1.2926466 |
-5.8044885 |
0.0000000 |
| x1 |
1.4273950 |
0.3937869 |
3.6247908 |
0.0003426 |
| I(x1^2) |
-0.0291813 |
0.0351632 |
-0.8298820 |
0.4073025 |
| I(x1^3) |
0.0000415 |
0.0009123 |
0.0454862 |
0.9637517 |
Regression Model 5: Polynomial, y.trans ~ x1^2, x1^3
# Initialize regression model 5
regression_5 <- glm(y.trans ~ I(x1^2) + I(x1^3))
# Plot the rediction result of regression model 5
predictRegression.windSpeed(x1.pred, x2, y.trans, regression_5)
title(main = 'Prediction of Linear Regression Model 5 of Wind Power Production')

Result of the regression analysis 5.
| (Intercept) |
-3.0138516 |
0.3780113 |
-7.972913 |
0 |
| I(x1^2) |
0.0955279 |
0.0074180 |
12.877829 |
0 |
| I(x1^3) |
-0.0030544 |
0.0003273 |
-9.331264 |
0 |
Regression Model 6: Simple, y.trans ~ x1^3
# Initialize regression model 6
regression_6 <- glm(y.trans ~ I(x1^3))
# Plot the rediction result of regression model 6
predictRegression.windSpeed(x1.pred, x2, y.trans, regression_6)
title(main = 'Prediction of Linear Regression Model 6 of Wind Power Production')

Result of the regression analysis 6.
| (Intercept) |
1.0292846 |
0.2643215 |
3.894063 |
0.0001228 |
| I(x1^3) |
0.0010547 |
0.0000916 |
11.508407 |
0.0000000 |
Regression 7: Multiple Regression, y ~ x1^3, x2
# Initialize regression model 1
regression_7 <- glm(y ~ I(x1^3) + x2) # I() is the identity function that returns its argument unaltered.
# Plot the rediction result of regression model 1
predictRegression.windSpeed(x1.pred, x2, y, regression_7)
title(main = 'Prediction of Linear Regression Model 7 of Wind Power Production')

Result of the regression analysis 7.
| (Intercept) |
0.1741396 |
0.0275446 |
6.3220960 |
0.0000000 |
| I(x1^3) |
0.0000627 |
0.0000049 |
12.9142492 |
0.0000000 |
| x2 |
0.0034833 |
0.0070497 |
0.4941097 |
0.6216093 |
qqnorm(regression_7$residuals, main = "Normal Q-Q Plot for Regression Model 7")
qqline(regression_7$residuals)

Regression 8: Multiple Regression, y ~ x2^3, x1^3, x2
# Initialize regression model 1
regression_8 <- glm(y ~ I(x1^2) + I(x1^3) + x2) # I() is the identity function that returns its argument unaltered.
# Plot the rediction result of regression model 1
predictRegression.windSpeed(x1.pred, x2, y, regression_8)
title(main = 'Prediction of Linear Regression Model 8 of Wind Power Production')

Result of the regression analysis 8.
| (Intercept) |
-0.0056105 |
0.0261994 |
-0.2141466 |
0.8305864 |
| I(x1^2) |
0.0049308 |
0.0003885 |
12.6917636 |
0.0000000 |
| I(x1^3) |
-0.0001481 |
0.0000171 |
-8.6811567 |
0.0000000 |
| x2 |
-0.0050510 |
0.0056812 |
-0.8890714 |
0.3747171 |
qqnorm(regression_8$residuals, main = "Normal Q-Q Plot for Regression Model 8")
qqline(regression_8$residuals)

Final Model
| regression_1 |
y.trans ~ x1, x1^2, x1^3, x2 |
Transformed Multiple Regression |
1465.73568 |
| regression_2 |
y.trans ~ x1, x1^2, x1^3 |
Transformed Polynomial Regression |
1463.76023 |
| regression_3 |
y.trans ~ x1^2, x1^3, x2 |
Transformed Multiple Regression |
1476.77329 |
| regression_4 |
y.trans ~ x1^3, x2 |
Transformed Multiple Regression |
1605.15980 |
| regression_5 |
y.trans ~ x1^2, x1^3 |
Transformed Polynomial Regression |
1474.78537 |
| regression_6 |
y.trans ~ x1^3 |
Transformed Simple Regression |
1604.86792 |
| regression_7 |
y ~ x1^3, x2 |
Multiple Regression |
-99.73305 |
| regression_8 |
y ~ x1^2, x1^3, x2 |
Multiple Regression |
-227.12619 |
According to AIC, we choose transformed multiple regression model “regression_4”, which expresses the relationship of transformed normalized power output and cubic of wind speed as well as wind direction.
LS0tCnRpdGxlOiAiQXNzaWdubWVudCAxLCBXaW5kIFBvd2VyIEZvcmVjYXNlLCBEVFUwMjQxOCIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhlcjogRWR3YXJkIEouIFh1CmRlcGFydG1lbnQ6IFN5c3RlbSBBbmFseXNpcywgRFRVIE1hbmFnZW1lbnQKVmVyc2lvbjogMi4xCkRhdGU6IERlYyAxNHRoLCAyMDE4Ci0tLQoKYGBge3IsIGZpZy53aWR0aCA9IDEwfQojIENsZWFyIHZhcmlhYmxlcwpybShsaXN0PWxzKCkpCmxpYnJhcnkoa25pdHIpCmBgYAoKIyMgRGF0YSBQcmVwb3NzZXNzaW5nCgpJbnB1dCB0aGUgZGF0YVdpbmQgb2YgcG93ZXIgcHJvZHVjdGlvbi4gVGhlIHRvdGFsIGRhdGFXaW5kIHNldCBjYW4gYmUgZGl2aWRlZCBpbnRvIHRocmVlIGNhdGVnb3JpZXM6CgoqIDMgdmFyaWFibGVzIHRvIGRlc2NyaWJlIHRoZSB0aW1lIChkYXlzIHNpbmNlIDEvMS0yMDAzLCBtb250aCwgYW5kIGRheSBvZiBtb250aCksCiogMSB2YXJpYWJsZSBkZXNjcmliaW5nIHRoZSBvYnNlcnZlZCBvdXRwdXQgKHBvd2VyKQoqIDIgbWV0ZW9yb2xvZ2ljYWwgdmFyaWFibGUuIAoKVGhlIGluZGl2aWR1YWwgdmFyaWFibGVzIGFyZSBkZXNjcmliZWQgaW4gVGFibGUgMS4KIVtPdmVydmlldyBvZiBhbGwgdmFyaWFibGVzXShPdmVydmlldyBvZiBhbGwgdmFyaWFibGVzLnBuZykKCmBgYHtyLCBmaWcud2lkdGggPSAxMH0KIyBEYXRhIFByb2Nlc3NpbmcKZGF0YVdpbmQgPC0gcmVhZC50YWJsZSgidHVuby5kYXQiKQpyLmRheSA8LSBkYXRhV2luZFsgLCAxXQptb250aCA8LSBkYXRhV2luZFsgLCAyXQpkYXkgPC0gZGF0YVdpbmRbICwgM10KcG93ZXIgPC0gZGF0YVdpbmRbICwgNF0gLyA1MDAwICAjIE5vcm1hbGl6ZSBwb3dlciBvdXRwdXQKd2luZFNwZWVkIDwtIGRhdGFXaW5kWyAsIDVdCndpbmREaXJlY3Rpb24gPC0gZGF0YVdpbmRbICwgNl0KdHVubyA8LSBkYXRhLmZyYW1lKHIuZGF5LCBtb250aCwgZGF5LCBwb3dlciwgd2luZFNwZWVkLCB3aW5kRGlyZWN0aW9uKQpgYGAKCkEgd2luZCBwb3dlciBwbGFudCB3aGVyZSBhbGwgdHVyYmluZXMgc3RhbmQgc3RpbGwgd2lsbCBhY3R1YWxseSBoYXZlIGEgbmVnYXRpdmUgcHJvZHVjdGlvbiBzdWNoIG51bWJlcnMgYXJlIHNldCBlcXVhbCB0byB6ZXJvIGluIHRoZSBvcmlnaW5hbCBkYXRhV2luZCBzZXQuIEhlbmNlIHRoZSBkYXRhV2luZCBzZXQgY29uc2lzdGVkIG9mIG51bWJlcnMgYmV0d2VlbiAwIGFuZCA1LDAwMCBrVy4gQXZlcmFnZSBkYWlseSB2YWx1ZXMgdGhhdCBhcmUgZXhhY3RseSB6ZXJvIGFyZSB0aG91Z2h0IG9mIGFzIHNwZWNpYWwgcGhlbm9tZW5hIHdoZXJlLCBmb3Igb25lIHJlYXNvbiBvciBhbm90aGVyIHRoZSBwbGFudCBpcyBkb3duLCB0aGVzZSBvYnNlcnZhdGlvbnMgYXJlIHJlbW92ZWQgZnJvbSB0aGUgZGF0YVdpbmQgc2V0LCB3aGljaCB3ZSB3aWxsIHNldCBhcyBjZW5zb3JlZCBvYnNlcnZhdGlvbi4KCmBgYHtyLCBmaWcud2lkdGggPSAxMH0Kci5kYXkgPC0gc2V0ZGlmZihzZXEoMSwgbGVuZ3RoKHR1bm8kci5kYXkpKSwgdHVubyRyLmRheSkKbW9udGggPC0gcmVwKDAsIGxlbmd0aChyLmRheSkpCmRheSA8LSByZXAoMCwgbGVuZ3RoKHIuZGF5KSkKcG93ZXIgPC0gcmVwKDAsIGxlbmd0aChyLmRheSkpCndpbmRTcGVlZCA8LSByZXAoMCwgbGVuZ3RoKHIuZGF5KSkKd2luZERpcmVjdGlvbiA8LSByZXAoMCwgbGVuZ3RoKHIuZGF5KSkKdHVuby56ZXJvIDwtIGRhdGEuZnJhbWUoci5kYXksIG1vbnRoLCBkYXksIHBvd2VyLCB3aW5kU3BlZWQsIHdpbmREaXJlY3Rpb24pCgp0dW5vLmFsbCA8LSByYmluZCh0dW5vLCB0dW5vLnplcm8pCmBgYAoKIyMgU3VtbWFyeSBTdGF0aXN0aWNzCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CiMjIFBsb3Q6IE5vcm1hbGl6ZWQgUG93ZXIgUHJvZHVjdGlvbiBpbiBUdW5vIEZhcm0gd2l0aCBDZW5zb3JlZCBPYnNlcnZhdGlvbgpwbG90KHR1bm8uYWxsW29yZGVyKHR1bm8uYWxsJHIuZGF5KSwgXSRyLmRheSwgdHVuby5hbGxbb3JkZXIodHVuby5hbGwkci5kYXkpLCBdJHdpbmRTcGVlZCwgdHlwZSA9ICJsIiwgY29sID0gImJsdWUiLCBtYWluID0gIk5vcm1hbGl6ZWQgUG93ZXIgUHJvZHVjdGlvbiBpbiBUdW5vIEZhcm0gd2l0aCBDZW5zb3JlZCBPYnNlcnZhdGlvbiIsIHhsYWIgPSAiZGF5IHNpbmNlIDEgSmFuIDIwMDMiLCB5bGFiID0gIk5vcm1hbGl6ZWQgUG93ZXIgUHJvZHVjdGlvbiIpCnBvaW50cyh0dW5vLnplcm8kci5kYXksIHR1bm8uemVybyRwb3dlciwgcGNoID0gNCwgY29sID0gMikKYGBgCgpCZXNpZGVzIHBsb3R0aW5nIHRoZSBwb3dlciBvdXRwdXQgYXMgYSBmdW5jdGlvbiBvZiB0aW1lLCB0aGVyZSBpcyBubyBtZWFuaW5nIHRvIGxvb2sgaW50byB0aGUgY2Vuc29yZWQgZGF0YSBwb2ludCBpbiBvdGhlciBhbmFseXNpcywgYmVjYXVzZSB0aGVyZSBpcyBubyBjb3JyZXNwb25kaW5nIG1lYXN1cmVkIHZhbHVlcyBvZiB3aW5kIHNwZWVkIGFuZCB3aW5kIGRpcmVjdGlvbi4gU28gd2UganVzdCBjb250aW51ZSBhbmFseXNpcyB3aXRob3V0IGNlbnNvcmVkIHZhbHVlcy4KCmBgYHtyLCBmaWcud2lkdGggPSAxMH0KcGxvdCh0dW5vJHdpbmRTcGVlZCwgdHVubyRwb3dlciwgbWFpbiA9ICJOb3JtYWlsaXplZCBQb3dlciBQcm9kdWN0aW9uIGFzIGEgRnVuY3Rpb24gb2YgV2luZCBTcGVlZCBpbiBUdW5vIEZhcm0iLCB4bGFiID0gIldpbmQgU3BlZWQgKG0vcykiLCB5bGFiID0gIk5vcm1hbGl6ZWQgUG93ZXIgUHJvZHVjdGlvbiIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDEwfQpwbG90KHR1bm8kd2luZERpcmVjdGlvbiwgdHVubyRwb3dlciwgbWFpbiA9ICJOb3JtYWlsaXplZCBQb3dlciBQcm9kdWN0aW9uIGFzIGEgRnVuY3Rpb24gb2YgV2luZCBEaXJlY3Rpb24gaW4gVHVubyBGYXJtIiwgeGxhYiA9ICJXaW5kIERpcmVjdGlvbiAocmFkKSIsIHlsYWIgPSAiTm9ybWFsaXplZCBQb3dlciBQcm9kdWN0aW9uIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CiMjIFN1bW1hcnkgc3RhdGlzdGljcyBieSBncm91cHM6IE5vcm1haWxpemVkIFBvd2VyIFByb2R1Y3Rpb24gYXMgYSBGYWN0b3Igb2YgTW9udGggaW4gVHVubyBGYXJtCnBsb3QoZmFjdG9yKHR1bm8kbW9udGgpLCB0dW5vJHBvd2VyLCBtYWluID0gIk5vcm1haWxpemVkIFBvd2VyIFByb2R1Y3Rpb24gYXMgYSBGYWN0b3Igb2YgTW9udGggaW4gVHVubyBGYXJtIiwgeGxhYiA9ICJNb250aCIsIHlsYWIgPSAiTm9ybWFsaXplZCBQb3dlciBQcm9kdWN0aW9uIikKYGBgCgpXZSBjYW4ndCBkZXJpdmUgYW55IGNvbmNsdXNpb24gZnJvbSB0aGlzIGJveHBsb3QuCgojIyBTaW1wbGUgTW9kZWxzIG9mIFdpbmQgUHJvZHVjdGlvbgoKYGBge3IsIGZpZy53aWR0aCA9IDEwfQpoaXN0KHR1bm8kcG93ZXIsIHByb2IgPSBUUlVFLCBicmVha3MgPSA1MCwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUG93ZXIgUHJvZHVjdGlvbiBpbiBUdW5vIEZhcm0iLCB4bGFiID0gIk5vcm1hbGl6ZWQgUG93ZXIgUHJvZHVjdGlvbiIsIGNvbCA9ICJncmF5OTAiKQpsaW5lcyhkZW5zaXR5KHR1bm8kcG93ZXIpLCBsd2QgPSAyLCBjb2wgPSA0KQpgYGAKCkZyb20gdGhlIGhpc3RvZ3JhbSwgd2UgY2FuIHNheSB0aGVyZSBpcyBubyBvYnZpb3VzIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcG93ZXIgb3V0cHV0LCBidXQgd2UgY2FuIHBsb3QgdGhlIHVzdWFsIGRpc3RyaWJ1dGlvbiB0byBjb21wYXJlLgoKR3JhcGhpY2FsbHksIGl0IHNlZW1zIHRoZXJlIGFyZSB0d28gY2FuZGlkYXRlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbnMgZm9yIHRoZSB3aW5kIHBvd2VyLCBuYW1lbHkgdGhlIGdhbW1hIGFuZCB0aGUgYmV0YSBkaXN0cmlidXRpb24gcmVzcGVjdGl2ZWx5LgoKIyMjIEdhbW1hIERpc3RyaWJ1dGlvbiBvZiBQb3dlciBQcm9kdWN0aW9uCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CiMgRGVmaW5lIGZ1bmN0aW9uIHRoYXQgcmV0dXJuIG5lZ2F0aXZlIGxvZy1saWtlbGlob29kIGZvciBnYW1tYSBkaXN0cmlidXRpb24KbmVnYUxvZ0xfZ2FtbWEgPC0gZnVuY3Rpb24ocGFycywgZGF0YVdpbmQpewogIGFscGhhIDwtIHBhcnNbMV0KICB0aGV0YSA8LSBwYXJzWzJdCiAgcmV0dXJuICgtc3VtKGRnYW1tYSh4ID0gZGF0YVdpbmQsIHNoYXBlID0gYWxwaGEsIHNjYWxlID0gdGhldGEsIGxvZyA9IFRSVUUpKSkKfQoKIyBPcHRpbWl6ZSBhbHBoYSBhbmQgdGhldGEgcGFyYW1ldGVycyBpbiBnYW1tYSBkaXN0cmlidXRpb24gZm9yIHBvd2VyIHByb2R1Y3Rpb24KcGFyc19nYW1tYUQub3B0aW1fcG93ZXIgPC0gb3B0aW0oYygxLCAxKSwgZm4gPSBuZWdhTG9nTF9nYW1tYSwgZ3IgPSBOVUxMLCBkYXRhV2luZCA9IHR1bm8kcG93ZXIpCgojIFBsb3Qgb3B0aW1pemVkIGdhbW1hIGRpc3RyaWJ1dGlvbiBkZW5zaXR5IGFuZCBkZW5zaXR5IG9mIHBvd2VyIHByb2R1Y3Rpb24KZGVuc2l0eV9wb3dlciA8LSBkZW5zaXR5KHR1bm8kcG93ZXIsIGFkanVzdCA9IDAuNykKeF9wb3dlciA9IHNlcSggMCwgMS4yLCBsZW5ndGgub3V0ID0gNTAwMCkgICMgVG8gc21vb3RoIHRoZSBjdXJ2ZSA/Pz8gCgphbHBoYV9nYW1tYUQub3B0aW1fcG93ZXIgPC0gcGFyc19nYW1tYUQub3B0aW1fcG93ZXIkcGFyWzFdCnRoZXRhX2dhbW1hRC5vcHRpbV9wb3dlciA8LSBwYXJzX2dhbW1hRC5vcHRpbV9wb3dlciRwYXJbMl0KZ2FtbWFELm9wdGltX3Bvd2VyID0gZGdhbW1hKHhfcG93ZXIsIHNoYXBlID0gYWxwaGFfZ2FtbWFELm9wdGltX3Bvd2VyLCBzY2FsZSA9IHRoZXRhX2dhbW1hRC5vcHRpbV9wb3dlcikKCiMjIApoaXN0KHR1bm8kcG93ZXIsIGJyZWFrcyA9IDUwLCBwcm9iID0gVFJVRSwgbWFpbiA9IE5VTEwsIHhsYWIgPSAiTm9ybWFsaXplZCBQb3dlciBQcm9kdWN0aW9uIChrVyAvIDUwMDBrVykiLCB5bGFiID0gIkRlbnNpdHkiLCBjb2wgPSAiZ3JheTk1IikKbGluZXMoZGVuc2l0eV9wb3dlciR4LCBkZW5zaXR5X3Bvd2VyJHksIHR5cGUgPSAnbCcsIHhsaW0gPSBjKDAsMSksIHlsaW0gPWMoMCwzKSwgY29sID0gJ2JsdWUnLCBsd2QgPSAyKQpsaW5lcyh4X3Bvd2VyLCBnYW1tYUQub3B0aW1fcG93ZXIsIHhsaW0gPSBjKDAsMSksIGNvbCA9ICdyZWQnLCBsd2QgPSAyLCBsdHkgPSAyKQp0aXRsZShtYWluID0gIkdhbW1hIERpc3RyaWJ1dGlvbiBieSBNTEUgYW5kIERlbnNpdHkgb2YgUG93ZXIgUHJvZHVjdGlvbiBpbiBUdW5vIEZhcm0iLCBmb250Lm1haW4gPSAzKQpsZWdlbmQoInRvcHJpZ2h0IiwgaW5zZXQgPSAuMDIsIGxlZ2VuZCA9IGMoIkdhbW1hIERpc3RyaWJ1dGlvbiBieSBNTEUiLCAiRGVuc2l0eSIpLCBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIpLCBsdHkgPSBjKDEsIDIpLCBsd2QgPSBjKDIsIDIpLCBjZXggPSAwLjgpCmBgYAoKIyMgQmV0YSBEaXN0cmlidXRpb24gb2YgUG93ZXIgUHJvZHVjdGlvbgoKYGBge3IsIGZpZy53aWR0aCA9IDEwfQojIE9wdGltaXplIGFscGhhIGFuZCBiZXRhIHBhcmFtZXRlcnMgaW4gYmV0YSBkaXN0cmlidXRpb24gZm9yIHBvd2VyIHByb2R1Y3Rpb24KbmVnYUxvZ0xfYmV0YSA8LSBmdW5jdGlvbihwYXJzLCBkYXRhV2luZCl7CiAgICBhbHBoYSA8LSBwYXJzWzFdCiAgICBiZXRhIDwtIHBhcnNbMl0KICAgIHJldHVybigtIHN1bShkYmV0YSh4ID0gZGF0YVdpbmQsIHNoYXBlMSA9IGFscGhhLCBzaGFwZTIgPSBiZXRhLCBsb2cgPSBUUlVFKSkpCn0KcGFyc19iZXRhRC5vcHRpbV9wb3dlciA8LSBvcHRpbShjKDEsMSksIGZuID0gbmVnYUxvZ0xfYmV0YSwgZ3IgPSBOVUxMLCBkYXRhV2luZCA9IHR1bm8kcG93ZXIpCgojIEluaXRpYWxpemUgb3B0aW1pemVkIGJldGEgZGlzdHJpYnV0aW9uIGRlbnNpdHkgYW5kIGRlbnNpdHkgb2YgcG93ZXIgcHJvZHVjdGlvbgphbHBoYV9iZXRhRC5vcHRpbV9wb3dlciA8LSBwYXJzX2JldGFELm9wdGltX3Bvd2VyJHBhclsxXQpiZXRhX2JldGFELm9wdGltX3Bvd2VyIDwtIHBhcnNfYmV0YUQub3B0aW1fcG93ZXIkcGFyWzJdCmJldGFELm9wdGltX3Bvd2VyIDwtIGRiZXRhKHhfcG93ZXIsIHNoYXBlMSA9IGFscGhhX2JldGFELm9wdGltX3Bvd2VyLCBzaGFwZTIgPSBiZXRhX2JldGFELm9wdGltX3Bvd2VyKQoKIyMgQmV0YSBEaXN0cmlidXRpb24gYnkgTUxFIGFuZCBEZW5zaXR5IG9mIFBvd2VyIFByb2R1Y3Rpb24gaW4gVHVubyBGYXJtCmhpc3QodHVubyRwb3dlciwgYnJlYWtzID0gNTAsIHByb2IgPSBUUlVFLCBtYWluID0gTlVMTCwgeGxhYiA9ICJOb3JtYWxpemVkIFBvd2VyIFByb2R1Y3Rpb24gKGtXIC8gNTAwMGtXKSIsIHlsYWIgPSAiRGVuc2l0eSIsIGNvbCA9ICJncmF5OTUiKQpsaW5lcyhkZW5zaXR5X3Bvd2VyJHgsIGRlbnNpdHlfcG93ZXIkeSwgdHlwZSA9ICdsJywgeGxpbSA9IGMoMCwxKSwgeWxpbSA9YygwLDMpLCBjb2wgPSAnYmx1ZScsIGx3ZCA9IDIpCmxpbmVzKHhfcG93ZXIsIGJldGFELm9wdGltX3Bvd2VyLCB4bGltID0gYygwLDEpLCBjb2wgPSAncmVkJywgbHdkID0gMiwgbHR5ID0gMikKdGl0bGUobWFpbiA9ICJCZXRhIERpc3RyaWJ1dGlvbiBieSBNTEUgYW5kIERlbnNpdHkgb2YgUG93ZXIgUHJvZHVjdGlvbiBpbiBUdW5vIEZhcm0iLCBmb250Lm1haW4gPSAzKQpsZWdlbmQoInRvcHJpZ2h0IiwgaW5zZXQgPSAuMDIsIGxlZ2VuZCA9IGMoIkJldGEgRGlzdHJpYnV0aW9uIGJ5IE1MRSIsICJEZW5zaXR5IiksIGNvbD1jKCJyZWQiLCAiYmx1ZSIpLCBsdHkgPSBjKDEsIDIpLCBsd2QgPSBjKDIsIDIpLCBjZXggPSAwLjgpCmBgYAoKIyMgQWthaWtlIEluZm9ybWF0aW9uIENyaXRlcmlvbiBmb3IgR2FtbWEgYW5kIEJldGEgRGlzdHJpYnV0aW9uCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CmxvZ0xpa2VsaWhvb2RfZ2FtbWFELm9wdGltX3Bvd2VyIDwtIC0gbmVnYUxvZ0xfZ2FtbWEoYyhhbHBoYV9nYW1tYUQub3B0aW1fcG93ZXIsIHRoZXRhX2dhbW1hRC5vcHRpbV9wb3dlciksIHR1bm8kcG93ZXIpCmFpY19nYW1tYUQub3B0aW1fcG93ZXIgPC0gLSAyICogbG9nTGlrZWxpaG9vZF9nYW1tYUQub3B0aW1fcG93ZXIgKyAyICogMgpsb2dMaWtlbGlob29kX2JldGFELm9wdGltX3Bvd2VyIDwtIC0gbmVnYUxvZ0xfYmV0YShjKGFscGhhX2JldGFELm9wdGltX3Bvd2VyLCBiZXRhX2JldGFELm9wdGltX3Bvd2VyKSwgdHVubyRwb3dlcikKYWljX2JldGFELm9wdGltX3Bvd2VyIDwtIC0gMiAqIGxvZ0xpa2VsaWhvb2RfYmV0YUQub3B0aW1fcG93ZXIgKyAyICogMgphaWNfZ2FtbWFELm9wdGltX3Bvd2VyCmFpY19iZXRhRC5vcHRpbV9wb3dlcgpgYGAKClRvIG1ha2UgYSBkZWNpc2lvbiBiZXR3ZWVuIHRoZXNlIHR3byBtb2RlbHMsIHRoZSBBSUMgY2FuIGJlIGNhbGN1bGF0ZWQgZnJvbSB0aGUgZm9ybXVsYS4KCkFzIHRoZSBBSUMgZm9yIHRoZSBCZXRhLWRpc3RyaWJ1dGlvbiBpcyBsZXNzIHRoYW4gZm9yIHRoZSBHYW1tYS1kaXN0cmlidXRpb24sIGl0IGNhbiBiZSBjb25jbHVkZWQgdGhhdCBmb3IgdGhlIHdpbmQgcG93ZXIgdmFyaWFibGUsIHRoZSBCZXRhLWRpc3RyaWJ1dGlvbiBpcyB0aGUgbW9zdCBhcHByb3ByaWF0ZSBtb2RlbC4KCiMjIERhdGEgVHJhbnNmb3JtYXRpb24gZm9yIE5vcm1hbCBNb2RlbCBvZiBXaW5kIFByb2R1Y3Rpb24KCmBgYHtyLCBmaWcud2lkdGggPSAxMH0KcXFub3JtKHR1bm8kcG93ZXIpCnFxbGluZSh0dW5vJHBvd2VyKQpgYGAKCiMjIFRyYW5zZm9ybWF0aW9uIDEgb2YgV2luZCBQcm9kdWN0aW9uCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CnRyYW5zXzEgPC0gZnVuY3Rpb24obGFtYmRhLCB5KXsKICAgIHkubGFtYmRhIDwtIDEgLyBsYW1iZGEgKiBsb2coeV5sYW1iZGEgLyAoMSAtIHlebGFtYmRhKSkKICAgIHJldHVybih5LmxhbWJkYSkKfQoKcHJvZmlsZUxvZ0xfMSA8LSBmdW5jdGlvbihsYW1iZGEsIHkpewogICAgbiA8LSBsZW5ndGgoeSkKICAgIHkubGFtYmRhIDwtIHRyYW5zXzEobGFtYmRhLCB5KQogICAgbXUgPC0gMSAvIG4gKiBzdW0oeS5sYW1iZGEpICAjIE1lYW4gb2YgeS5sYW1iZGEKICAgIHNpZ21hLnNxdWFyZSA8LSAxIC8gbiAqIHN1bSgoeS5sYW1iZGEgLSBtdSleMikKICAgIHkubGFtYmRhLnBhcnRpYWwgPC0geV4obGFtYmRhIC0gMSkgLyAoMSAtIHlebGFtYmRhKSArIDEgLyB5CiAgICByZXR1cm4oLSBuIC8gMiAqIGxvZyhzaWdtYS5zcXVhcmUpIC0gMSAvICgyICogc2lnbWEuc3F1YXJlKSAqIHN1bSgoeS5sYW1iZGEgLSBtdSleMikgKyBzdW0obG9nKHkubGFtYmRhLnBhcnRpYWwpKSkKfQoKbGFtYmRhXzEgPC0gc2VxKDAuMSwgMC41LCBieSA9IDAuMDEpCnByb2ZpbGVMb2dMLjEgPC0gc2FwcGx5KGxhbWJkYV8xLCBwcm9maWxlTG9nTF8xLCB5ID0gdHVubyRwb3dlcikKcGxvdChsYW1iZGFfMSwgcHJvZmlsZUxvZ0wuMSAtIG1heChwcm9maWxlTG9nTC4xKSwgdHlwZSA9ICJsIiwgeWxhYiA9ICJMb2cgTGlrZWxpaG9vZCIpCmxpbmVzKHJhbmdlKGxhbWJkYV8xKSwtIHFjaGlzcSgwLjk1LCBkZiA9IDEpIC8gMiAqIGMoMSwxKSwgbHR5ID0gMiwgY29sID0gMikKdGl0bGUoIlByb2ZpbGUgTGlrZWxpaG9vZCBmb3IgVHJhbnNmb3JtYXRpb24gMSBvZiBQb3dlciBQcm9kdWN0aW9uIikKcHJvZmlsZUxvZ0wuMS5vcHRpbSA8LSBvcHRpbWl6ZShwcm9maWxlTG9nTF8xLCBjKDAuMSwgMC41KSwgeSA9IHR1bm8kcG93ZXIsIG1heGltdW0gPSBUUlVFKQpsYW1iZGFfcHJvZmlsZUxvZ0wuMS5vcHRpbSA8LSBwcm9maWxlTG9nTC4xLm9wdGltJG1heGltdW0KYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CnBvd2VyLnRyYW5zXzEub3B0aW0gPC0gdHJhbnNfMShsYW1iZGFfcHJvZmlsZUxvZ0wuMS5vcHRpbSwgdHVubyRwb3dlcikKcXFub3JtKHBvd2VyLnRyYW5zXzEub3B0aW0sIG1haW4gPSAiUVEtcGxvdCBvZiBQb3dlciBQcm9kdWN0aW9uIGFmdGVyIFRyYW5zZm9ybWF0aW9uIDEiKQpxcWxpbmUocG93ZXIudHJhbnNfMS5vcHRpbSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9Cmhpc3QocG93ZXIudHJhbnNfMS5vcHRpbSwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUG93ZXIgUHJvZHVjdGlvbiBhZnRlciBNTEUgZm9yIFRyYW5zZm9ybWF0aW9uIDEiLCB4bGFiID0gcGFzdGUoIk5vcm1hbGl6ZWQgcG93ZXIgcHJvZHVjdGlvbiB3aGVuIGxhbWJkYSA9ICIsIGxhbWJkYV9wcm9maWxlTG9nTC4xLm9wdGltKSwgeWxhYiA9ICJEZW5zaXR5IiwgY29sID0gImdyYXk5MCIpCmBgYAoKIyMgVHJhbnNmb3JtYXRpb24gMiBvZiBXaW5kIFByb2R1Y3Rpb24KCmBgYHtyLCBmaWcud2lkdGggPSAxMH0KdHJhbnNfMiA8LSBmdW5jdGlvbihsYW1iZGEsIHkpewogICAgeS5sYW1iZGEgPC0gMiAqIGxvZyh5XmxhbWJkYSAvICgxIC0geSleKDEgLSBsYW1iZGEpKQogICAgcmV0dXJuKHkubGFtYmRhKQp9CnByb2ZpbGVMb2dMXzIgPC0gZnVuY3Rpb24obGFtYmRhLCB5KXsKICAgIG4gPC0gbGVuZ3RoKHkpCiAgICB5LmxhbWJkYSA8LSB0cmFuc18yKGxhbWJkYSwgeSkKICAgIG11IDwtIDEgLyBuICogc3VtKHkubGFtYmRhKSAgIyBNZWFuIG9mIHkubGFtYmRhCiAgICBzaWdtYS5zcXVhcmUgPC0gMSAvIG4gKiBzdW0oKHkubGFtYmRhIC0gbXUpXjIpCiAgICB5LmxhbWJkYS5wYXJ0aWFsIDwtIDIgKiAobGFtYmRhIC8geSAtIChsYW1iZGEgLSAxKSAvICgxIC0geSkpCiAgICByZXR1cm4oLSBuIC8gMiAqIGxvZyhzaWdtYS5zcXVhcmUpIC0gMSAvICgyICogc2lnbWEuc3F1YXJlKSAqIHN1bSgoeS5sYW1iZGEgLSBtdSleMikgKyBzdW0obG9nKHkubGFtYmRhLnBhcnRpYWwpKSkKfQoKbGFtYmRhXzIgPC0gc2VxKDAsIDEsIGJ5ID0gMC4wMSkKcHJvZmlsZUxvZ0wuMiA8LSBzYXBwbHkobGFtYmRhXzIsIHByb2ZpbGVMb2dMXzIsIHkgPSB0dW5vJHBvd2VyKQpwbG90KGxhbWJkYV8yLCBwcm9maWxlTG9nTC4yIC0gbWF4KHByb2ZpbGVMb2dMLjIpLCB0eXBlID0gImwiLCB5bGFiID0gIkxvZyBMaWtlbGlob29kIiwgeWxpbSA9IGMoLTEwMCwgMCkpCmxpbmVzKHJhbmdlKGxhbWJkYV8yKSwtIHFjaGlzcSgwLjk1LCBkZiA9IDEpIC8gMiAqIGMoMSwxKSwgbHR5ID0gMiwgY29sID0gMikKdGl0bGUoIlByb2ZpbGUgTGlrZWxpaG9vZCBmb3IgVHJhbnNmb3JtYXRpb24gMiBvZiBQb3dlciBQcm9kdWN0aW9uIikKcHJvZmlsZUxvZ0wuMi5vcHRpbSA8LSBvcHRpbWl6ZShwcm9maWxlTG9nTF8yLCBjKDAsIDEpLCB5ID0gdHVubyRwb3dlciwgbWF4aW11bSA9IFRSVUUpCmxhbWJkYV9wcm9maWxlTG9nTC4yLm9wdGltIDwtIHByb2ZpbGVMb2dMLjIub3B0aW0kbWF4aW11bQoKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CnBvd2VyLnRyYW5zXzIub3B0aW0gPC0gdHJhbnNfMihsYW1iZGFfcHJvZmlsZUxvZ0wuMi5vcHRpbSwgdHVubyRwb3dlcikKcG93ZXIudHJhbnNfMi5vcHRpbU1MRSA8LSB0cmFuc18yKDAuMjUyMzI5OCwgdHVubyRwb3dlcikKcXFub3JtKHBvd2VyLnRyYW5zXzIub3B0aW0sIG1haW4gPSAiUVEtcGxvdCBvZiBQb3dlciBQcm9kdWN0aW9uIGFmdGVyIFRyYW5zZm9ybWF0aW9uIDIiKQpxcWxpbmUocG93ZXIudHJhbnNfMi5vcHRpbSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9Cmhpc3QocG93ZXIudHJhbnNfMi5vcHRpbSwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUG93ZXIgUHJvZHVjdGlvbiBhZnRlciBNTEUgZm9yIFRyYW5zZm9ybWF0aW9uIDIiLCB4bGFiID0gcGFzdGUoIk5vcm1hbGl6ZWQgcG93ZXIgcHJvZHVjdGlvbiB3aGVuIGxhbWJkYSA9ICIsIGxhbWJkYV9wcm9maWxlTG9nTC4yLm9wdGltKSwgeWxhYiA9ICJEZW5zaXR5IiwgY29sID0gImdyYXk5MCIpCmBgYAoKIyMgQ294LUJveCBUcmFuc2Zvcm1hdGlvbiBvZiBXaW5kIFByb2R1Y3Rpb24KCmBgYHtyLCBmaWcud2lkdGggPSAxMH0KdHJhbnNfYm94Q294IDwtIGZ1bmN0aW9uKGxhbWJkYSx5KXsKICAgIHkubGFtYmRhIDwtICh5XmxhbWJkYSAtIDEpIC8gbGFtYmRhCiAgICBpZihsYW1iZGEgPT0gMCl7CiAgICAgICAgeS5sYW1iZGEgPC0gbG9nKHkpfQogICAgcmV0dXJuKHkubGFtYmRhKQp9CgojIFByb2ZpbGUgTG9nLUxpa2VsaWhvb2QgZm9yIExhbWJkYQpwcm9maWxlTG9nTF9ib3hDb3ggPC0gZnVuY3Rpb24obGFtYmRhLHkpewogICAgbiA8LSBsZW5ndGgoeSkKICAgIHkubGFtYmRhIDwtIHRyYW5zX2JveENveChsYW1iZGEsIHkpCiAgICBzaWdtYS5zcXVhcmUgPC0gMSAvIG4gKiBzdW0oKHkubGFtYmRhIC0gbWVhbih5LmxhbWJkYSkpXjIpCiAgICByZXR1cm4oLSBuIC8gMiAqIGxvZyhzaWdtYS5zcXVhcmUpIC0gbiAvIDIgKyAobGFtYmRhIC0gMSkgKiBzdW0obG9nKHkpKSkKfQoKIyBQbG90IHRoZSBwcm9maWxlIGxpa2VsaWhvb2QKbGFtYmRhX2JveENveCA8LSBzZXEoMC4yLCAwLjUsIGJ5ID0gMC4wMSkKcHJvZmlsZUxvZ0wuQm94Q294IDwtIHNhcHBseShsYW1iZGFfYm94Q294LCBwcm9maWxlTG9nTF9ib3hDb3gsIHkgPSB0dW5vJHBvd2VyKQpwbG90KGxhbWJkYV9ib3hDb3gsIHByb2ZpbGVMb2dMLkJveENveCAtIG1heChwcm9maWxlTG9nTC5Cb3hDb3gpLCB0eXBlID0gImwiLCB5bGFiID0gIkxvZyBMaWtlbGlob29kIikKbGluZXMocmFuZ2UobGFtYmRhX2JveENveCksLSBxY2hpc3EoMC45NSwgZGYgPSAxKSAvIDIgKiBjKDEsMSksIGx0eSA9IDIsIGNvbCA9IDIpCnRpdGxlKCJQcm9maWxlIExpa2VsaWhvb2QgZm9yIEJveC1Db3ggVHJhbnNmb3JtYXRpb24gb2YgUG93ZXIgUHJvZHVjdGlvbiIpCnByb2ZpbGVMb2dMLmJveENveC5vcHRpbSA8LSBvcHRpbWl6ZShwcm9maWxlTG9nTF9ib3hDb3gsIGMoMC4yLCAwLjUpLCB5ID0gdHVubyRwb3dlciwgbWF4aW11bSA9IFRSVUUpCmxhbWJkYV9wcm9maWxlTG9nTC5Cb3hDb3gub3B0aW0gPC0gcHJvZmlsZUxvZ0wuYm94Q294Lm9wdGltJG1heGltdW0KIyBzbyBsYW1iZGEgaW4gdGhlIHJhbmdlICB0byAgKHdpdGggOTUlIGNvbmZpZGVuY2UpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDEwfQpwb3dlci50cmFuc19ib3hDb3gub3B0aW0gPC0gdHJhbnNfYm94Q294KGxhbWJkYV9wcm9maWxlTG9nTC5Cb3hDb3gub3B0aW0sIHR1bm8kcG93ZXIpCnFxbm9ybShwb3dlci50cmFuc19ib3hDb3gub3B0aW0sIG1haW4gPSAiUVEtcGxvdCBvZiBQb3dlciBQcm9kdWN0aW9uIGFmdGVyIEJveC1Db3ggVHJhbnNmb3JtYXRpb24iKQpxcWxpbmUocG93ZXIudHJhbnNfYm94Q294Lm9wdGltKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMH0KaGlzdChwb3dlci50cmFuc19ib3hDb3gub3B0aW0sIG1haW4gPSAiSGlzdG9ncmFtIG9mIFBvd2VyIFByb2R1Y3Rpb24gYWZ0ZXIgTUxFIGZvciBCb3gtQ294IFRyYW5zZm9ybWF0aW9uIiwgeGxhYiA9IHBhc3RlKCJOb3JtYWxpemVkIHBvd2VyIHByb2R1Y3Rpb24gd2hlbiBsYW1iZGEgPSAiLCBsYW1iZGFfcHJvZmlsZUxvZ0wuQm94Q294Lm9wdGltKSwgeWxhYiA9ICJEZW5zaXR5IiwgY29sID0gImdyYXk5MCIpCmBgYAoKVGhlIHJlc3VsdCBvZiB0aGUgc2ltcGxlIG1vZGVsIGlzOgoKYGBge3IsIGVjaG8gPSBGQUxTRX0KbnVtYmVyVHJhbnMgPC0gYygnVHJhbmZvcm1hdGlvbl8xJywgJ1RyYW5mb3JtYXRpb25fMicsICdCb3gtQ294IFRyYW5mb3JtYXRpb24nKQpsYW1iZGEuTUxFIDwtIGMobGFtYmRhX3Byb2ZpbGVMb2dMLjEub3B0aW0sIGxhbWJkYV9wcm9maWxlTG9nTC4yLm9wdGltLCBsYW1iZGFfcHJvZmlsZUxvZ0wuQm94Q294Lm9wdGltKQp0YWJsZV9sYW1iZGEuTUxFIDwtIGRhdGEuZnJhbWUobnVtYmVyVHJhbnMsIGxhbWJkYS5NTEUpCmthYmxlKHRhYmxlX2xhbWJkYS5NTEUsIGNvbC5uYW1lcyA9IGMoJ1RyYW5zZm9ybWF0aW9uJywgJ01MRSBvZiBsYW1iZGEnKSwgY2FwdGlvbiA9ICdNTEUgb2YgbGFtYmRhIGluIHRyYW5zZm9ybWF0aW9uJywgYWxpZ24gPSAibCIpCmBgYAoKIyBSZWdyZXNzaW9uIE1vZGVsCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGVjaG8gPSBGQUxTRX0KeSA8LSB0dW5vJHBvd2VyCnkudHJhbnMgPC0gdHJhbnNfMShsYW1iZGFfcHJvZmlsZUxvZ0wuMS5vcHRpbSwgeSkKeDEgPC0gdHVubyR3aW5kU3BlZWQKeDEuc3F1YXJlIDwtIHgxXjIKeDEuY3ViaWMgPC0geDFeMwp4MiA8LSB0dW5vJHdpbmREaXJlY3Rpb24KeDIuc3F1YXJlIDwtIHgyXjIKbiA8LSBsZW5ndGgoeS50cmFucykKeDEucHJlZCA8LSBzZXEoMCwgMzAsIDAuMSkKIyBUcmFuc2Zvcm1lZCBOb3JtYWlsaXplZCBQb3dlciBQcm9kdWN0aW9uIGFzIGEgRnVuY3Rpb24gb2YgV2luZCBTcGVlZCBpbiBUdW5vIEZhcm0KcGxvdCh4MSwgeS50cmFucywgbWFpbiA9ICJUcmFuc2Zvcm1lZCBOb3JtYWlsaXplZCBQb3dlciBQcm9kdWN0aW9uIGFzIGEgRnVuY3Rpb24gb2YgV2luZCBTcGVlZCIsIHhsYWIgPSAiV2luZCBTcGVlZCAobS9zKSIsIHlsYWIgPSAiVHJhbnNmb3JtZWQgTm9ybWFsaXplZCBQb3dlciBQcm9kdWN0aW9uIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGVjaG8gPSBGQUxTRX0KcGxvdCh4MiwgeS50cmFucywgbWFpbiA9ICJOb3JtYWlsaXplZCBQb3dlciBQcm9kdWN0aW9uIGFzIGEgRnVuY3Rpb24gb2YgV2luZCBEaXJlY3Rpb24iLCB4bGFiID0gIldpbmQgRGlyZWN0aW9uIChyYWQpIiwgeWxhYiA9ICJOb3JtYWxpemVkIFBvd2VyIFByb2R1Y3Rpb24iKQpgYGAKCldpdGggdHdvIG9yIG1vcmUgY29udGludW91cyBleHBsYW5hdG9yeSB2YXJpYWJsZXMgKGkuZS4gaW4gYSBtdWx0aXBsZSByZWdyZXNzaW9uOyBzZWUgcC4gMzk1KSBpdCBpcyB2YWx1YWJsZSB0byBiZSBhYmxlIHRvIGNoZWNrIGZvciBzdWJ0bGUgZGVwZW5kZW5jaWVzIGJldHdlZW4gdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcy4gVGhlIHBhaXJzIGZ1bmN0aW9uIHBsb3RzIGV2ZXJ5IHZhcmlhYmxlIGluIHRoZSBkYXRhZnJhbWUgb24gdGhlIHkgYXhpcyBhZ2FpbnN0IGV2ZXJ5IG90aGVyIHZhcmlhYmxlIG9uIHRoZSB4IGF4aXM6CgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CiMgU2NhdHRlcnBsb3QgTWF0cmljZXMKcGFpcnMoY2JpbmQoeS50cmFucywgeDEsIHgyKSwgbGFiZWxzID0gYygncG93ZXInLCAnd2luZFNwZWVkJywgJ3dpbmREaXJlY3Rpb24nKSwgcGFuZWwgPSBwYW5lbC5zbW9vdGgpCmBgYAoKVGhlIHJlc3BvbnNlIHZhcmlhYmxlcyBhcmUgbmFtZWQgaW4gdGhlIHJvd3MgYW5kIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgYXJlIG5hbWVkIGluIHRoZSBjb2x1bW5zLgoKYGBge3IsIGVjaG8gPSBGQUxTRX0KIyBGdW5jdGlvbiB0byBwbG90IHRoZSBwcmVkaWN0aW9uIHJlc3VsdCBvZiByZWdyZXNzaW9uIG1vZGVsIGJhc2VkIG9uIHdpbmQgc3BlZWQuCnByZWRpY3RSZWdyZXNzaW9uLndpbmRTcGVlZCA8LSBmdW5jdGlvbih4MS5wcmVkLCB4MiwgeSwgbW9kZWxSZWdyZXNzaW9uKXsKICAgIGRhdGFGcmFtZVByZWRpY3QgPC0gZGF0YS5mcmFtZSh4MSA9IHgxLnByZWQsIHgyID0gcmVwKG1lYW4oeDIpLCBsZW5ndGgoeDEucHJlZCkpKQogICAgdmFsdWVQcmVkIDwtIHByZWRpY3QubG0obW9kZWxSZWdyZXNzaW9uLCBkYXRhRnJhbWVQcmVkaWN0LCBzZS5maXQgPSBUUlVFKQogICAgaW50ZXJ2YWxQcmVkIDwtIHByZWRpY3QubG0obW9kZWxSZWdyZXNzaW9uLCBuZXdkYXRhID0gZGF0YUZyYW1lUHJlZGljdCwgaW50ZXJ2YWwgPSAicHJlZCIsIGxldmVsID0gMC45NSkKICAgIGludGVydmFsQ29uZiA8LSBwcmVkaWN0LmxtKG1vZGVsUmVncmVzc2lvbiwgbmV3ZGF0YSA9IGRhdGFGcmFtZVByZWRpY3QsIGludGVydmFsID0gImNvbmYiLCBsZXZlbCA9IDAuOTUpCiAgICBwbG90KGRhdGFGcmFtZVByZWRpY3QkeDEsIHZhbHVlUHJlZCRmaXQsIHBjaCA9IDIwLCB5bGltID0gYyhtaW4oeSwgaW50ZXJ2YWxQcmVkKSwgbWF4KHksIGludGVydmFsUHJlZCkpLCB4bGltID0gYyhtaW4oeDEsIGRhdGFGcmFtZVByZWRpY3QkeDEpLCBtYXgoeDEsIGRhdGFGcmFtZVByZWRpY3QkeDEpKSwgeGxhYiA9ICdXaW5kIFNwZWVkJywgeWxhYiA9ICdOb3JtYWxpemVkIFBvd2VyIFByb2R1Y3Rpb24nKQogICAgbWF0bGluZXMoZGF0YUZyYW1lUHJlZGljdCR4MSwgaW50ZXJ2YWxQcmVkLCBsdHkgPSBjKDEsIDIsIDIpLCBjb2wgPSBjKCJibGFjayIsICJibHVlIiwgImJsdWUiKSwgbHdkID0gMikKICAgIHBvaW50cyh4MSwgeSkgICMgVHJhbnNmb3JtZWQgb2JzZXJ2YXRpb25zCiAgICBsaW5lcyhkYXRhRnJhbWVQcmVkaWN0JHgxLCBpbnRlcnZhbENvbmZbLCAyXSwgbHR5ID0gMywgY29sID0gInJlZCIsIGx3ZCA9IDIpCiAgICBsaW5lcyhkYXRhRnJhbWVQcmVkaWN0JHgxLCBpbnRlcnZhbENvbmZbLCAzXSwgbHR5ID0gMywgY29sID0gInJlZCIsIGx3ZCA9IDIpCiAgICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgaW5zZXQgPSAuMDIsIGxlZ2VuZCA9IGMoIlJlZ3Jlc3Npb24gQ3VydmUiLCAiUHJlZGljdGlvbiBJbnRlcnZhbCIsICJDb25maWRlbmNlIEludGVyY2FsIiksIGNvbCA9IGMoImJsYWNrIiwgImJsdWUiLCAicmVkIiksIGx0eSA9IGMoMSwgMiwgMyksIGx3ZCA9IGMoMiwgMiwgMiksIGNleCA9IDAuOCkKfQpgYGAKCiMjIFJlZ3Jlc3Npb24gTW9kZWwgMTogTXVsdGlwbGUsIHkudHJhbnMgfiB4MSwgeDFeMiwgeDFeMywgeDIKClRoZSBpbml0aWFsIG1vZGVsIGlzIGJhc2VkIG9uIHRoZSBkYXRhV2luZCB0aGF0IGhhcyBub3QgYmVlbiB0cmFuc2Zvcm1lZCBhbmQgd2lsbCB0aGVyZWZvcmUgYmUgYSBub24tbm9ybWFsIG1vZGVsLiBBIHJhdGhlciBjb21wbGV4IHN0cnVjdHVyZSBpcyBpbml0aWFsbHkgY2hvc2VuIHNpbmNlIHRoYXQgd2lsbCBhbGxvdyB1cyB0byBlaXRoZXIgaXRlcmF0aXZlbHkgcmVtb3ZlIG5vbi1zaWduaWZpY2FudCBwYXJhbWV0ZXJzIG9yIGV4cGFuZCBvdXIgbW9kZWwgaWYgaXQgaXMgbmVjZXNzYXJ5LgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBpbmNsdWRlID0gRkFMU0V9CiMjIEhhbmQgY2FsY3VsYXRpb24gb2YgcmVncmVzc2lvbiBtb2RlbCAxClhfMSA8LSBjYmluZCgxLCB4MSwgeDEuc3F1YXJlLCB4MS5jdWJpYywgeDIpCmJldGFfMSA8LSBzb2x2ZSh0KFhfMSkgJSolIFhfMSkgJSolIHQoWF8xKSAlKiUgeS50cmFucwp5LnByZWQudHJhbnNfMSA8LSBiZXRhXzFbMV0gKyBiZXRhXzFbMl0gKiB4MS5wcmVkICsgYmV0YV8xWzNdICogeDEucHJlZF4yICsgYmV0YV8xWzRdICogeDEucHJlZF4zICsgYmV0YV8xWzVdICogbWVhbih4MikKcG9pbnRzX3lfMSA8LSBiZXRhXzFbMV0gKyBiZXRhXzFbMl0gKiB4MSArIGJldGFfMVszXSAqIHgxXjIgKyBiZXRhXzFbNF0gKiB4MV4zICsgYmV0YV8xWzVdICogeDIKcG93ZXIubWVhbkRpcmVjdGlvbiA8LSB0dW5vJHBvd2VyIC0gYmV0YV8xWzVdICogeDIgKyBiZXRhXzFbNV0gKiAoIG1lYW4oeDIpKQpwbG90KHgxLCB5LnRyYW5zLCB4bGFiID0gIldpbmQgU3BlZWQgKG0vcykiLCB5bGFiID0gIk5vcm1hbGl6ZWQgUG93ZXIgUHJvZHVjdGlvbiIsIG1haW4gPSAiTm9ybWFsaXplZCBQb3dlciBQcm9kdWN0aW9uIGFuZCBXaW5kIFNwZWVkIGZvciBSZWdyZXNzaW9uIE1vZGVsIDEiKQpsaW5lcyh4MS5wcmVkLCB5LnByZWQudHJhbnNfMSwgY29sID0gJ2JsdWUnLCBsd2QgPSAyKSAgIyBQcmVkaWN0aW9uCnBvaW50cyh4MSwgcG9pbnRzX3lfMSwgcGNoID0gOCwgY29sID0gMikgICMgUHJlZGljdGlvbiBvZiBvcmlnaW5hbCBvYnNlcnZhdGlvbgpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgcmVzdWx0cz0naGlkZSd9CiMgSW5pdGlhbGl6ZSByZWdyZXNzaW9uIG1vZGVsIDEKcmVncmVzc2lvbl8xIDwtIGdsbSh5LnRyYW5zIH4geDEgKyBJKHgxXjIpICsgSSh4MV4zKSArIHgyKSAgIyBJKCkgaXMgdGhlIGlkZW50aXR5IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBpdHMgYXJndW1lbnQgdW5hbHRlcmVkLgoKIyBQbG90IHRoZSByZWRpY3Rpb24gcmVzdWx0IG9mIHJlZ3Jlc3Npb24gbW9kZWwgMQpwcmVkaWN0UmVncmVzc2lvbi53aW5kU3BlZWQoeDEucHJlZCwgeDIsIHkudHJhbnMsIHJlZ3Jlc3Npb25fMSkKdGl0bGUobWFpbiA9ICdQcmVkaWN0aW9uIG9mIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsIDEgb2YgV2luZCBQb3dlciBQcm9kdWN0aW9uJykKYGBgCgpSZXN1bHQgb2YgdGhlIHJlZ3Jlc3Npb24gYW5hbHlzaXMgMS4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CmthYmxlKGNvZWYoc3VtbWFyeShyZWdyZXNzaW9uXzEpKSwgYWxpZ24gPSAibCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDEwfQpxcW5vcm0ocmVncmVzc2lvbl8xJHJlc2lkdWFscywgbWFpbiA9ICJOb3JtYWwgUS1RIFBsb3QgZm9yIFJlZ3Jlc3Npb24gTW9kZWwgMSIpCnFxbGluZShyZWdyZXNzaW9uXzEkcmVzaWR1YWxzKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMH0KcmVzaWR1YWxzX3JlZ3Jlc3Npb25fMSA8LSByZWdyZXNzaW9uXzEkcmVzaWR1YWxzICMgc2FtcGxlCmhpc3RfcmVzaWR1YWxzX3JlZ3Jlc3Npb25fMSA8LSBoaXN0KHJlc2lkdWFsc19yZWdyZXNzaW9uXzEsIHBsb3QgPSBGQUxTRSkgIyBnZW5lcmF0ZSBoaXN0CnBsb3QoaGlzdF9yZXNpZHVhbHNfcmVncmVzc2lvbl8xLCBjb2wgPSAiZ3JleSIsIG1haW4gPSAnSGlzdG9ncmFtIG9mIHJlc2lkdWFscyBmb3IgUmVncmVzc2lvbiBNb2RlbCAxJywgeGxhYiA9ICdSZXNpZHVhbHMnKSAjIHBsb3QgaGlzdAp4bGluZXMgPC0gc2VxKG1pbihoaXN0X3Jlc2lkdWFsc19yZWdyZXNzaW9uXzEkYnJlYWtzKSwgbWF4KGhpc3RfcmVzaWR1YWxzX3JlZ3Jlc3Npb25fMSRicmVha3MpLCBsZW5ndGgub3V0ID0gMTAwKSAjIHNlcSBvZiB4IGZvciBwZGYKbGluZXMoeGxpbmVzLCBkbm9ybSh4bGluZXMsIG1lYW4ocmVzaWR1YWxzX3JlZ3Jlc3Npb25fMSksIHNkKHJlc2lkdWFsc19yZWdyZXNzaW9uXzEpKSAqICBsZW5ndGgocmVzaWR1YWxzX3JlZ3Jlc3Npb25fMSkgKiBkaWZmKGhpc3RfcmVzaWR1YWxzX3JlZ3Jlc3Npb25fMSRicmVha3MpWzFdLCBjb2wgPSAibGlnaHRjb3JhbCIsIGx3ZCA9IDIpCmBgYAoKQSBoaXN0b2dyYW0gb2YgdGhlIHJlc2lkdWFscyBpcyBzaG93biBpbiB0aGUgYWJvdmUgZmlndXJlLCB3aGVyZSBpdCBzZWVtcyB0aGF0IHRoZXkgYXJlIHF1aXRlIHdlbGwtYmVoYXZlZCBhbmQgYXBwcm94aW1hdGVseSBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4KCiMjIFJlZ3Jlc3Npb24gTW9kZWwgMzogTXVsdGlwbGUsIHkudHJhbnMgfiB4MV4yLCB4MV4zLCB4MgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCByZXN1bHRzPSdoaWRlJ30KIyBJbml0aWFsaXplIHJlZ3Jlc3Npb24gbW9kZWwgMyB1c2luZyBnbG0KcmVncmVzc2lvbl8zIDwtIGdsbSh5LnRyYW5zIH4gSSh4MV4yKSArIEkoeDFeMykgKyB4MikgICMgSSgpIGlzIHRoZSBpZGVudGl0eSBmdW5jdGlvbiB0aGF0IHJldHVybnMgaXRzIGFyZ3VtZW50IHVuYWx0ZXJlZC4KCiMgUGxvdCB0aGUgcmVkaWN0aW9uIHJlc3VsdCBvZiByZWdyZXNzaW9uIG1vZGVsIDMKcHJlZGljdFJlZ3Jlc3Npb24ud2luZFNwZWVkKHgxLnByZWQsIHgyLCB5LnRyYW5zLCByZWdyZXNzaW9uXzMpCnRpdGxlKG1haW4gPSAnUHJlZGljdGlvbiBvZiBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCAzIG9mIFdpbmQgUG93ZXIgUHJvZHVjdGlvbicpCmBgYAoKUmVzdWx0IG9mIHRoZSByZWdyZXNzaW9uIGFuYWx5c2lzIDMuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQprYWJsZShjb2VmKHN1bW1hcnkocmVncmVzc2lvbl8zKSksIGFsaWduID0gImwiKQpgYGAKCiMjIFJlZ3Jlc3Npb24gTW9kZWwgNDogTXVsdGlwbGUsIHkudHJhbnMgfiB4MV4zLCB4MgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCByZXN1bHRzPSdoaWRlJ30KIyBJbml0aWFsaXplIHJlZ3Jlc3Npb24gbW9kZWwgNApyZWdyZXNzaW9uXzQgPC0gZ2xtKHkudHJhbnMgfiBJKHgxXjMpICsgeDIpICAjIEkoKSBpcyB0aGUgaWRlbnRpdHkgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGl0cyBhcmd1bWVudCB1bmFsdGVyZWQuCgojIFBsb3QgdGhlIHJlZGljdGlvbiByZXN1bHQgb2YgcmVncmVzc2lvbiBtb2RlbCA0CnByZWRpY3RSZWdyZXNzaW9uLndpbmRTcGVlZCh4MS5wcmVkLCB4MiwgeS50cmFucywgcmVncmVzc2lvbl80KQp0aXRsZShtYWluID0gJ1ByZWRpY3Rpb24gb2YgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwgNCBvZiBXaW5kIFBvd2VyIFByb2R1Y3Rpb24nKQpgYGAKClJlc3VsdCBvZiB0aGUgcmVncmVzc2lvbiBhbmFseXNpcyA0LgoKYGBge3IsIGVjaG8gPSBGQUxTRX0Ka2FibGUoY29lZihzdW1tYXJ5KHJlZ3Jlc3Npb25fNCkpLCBhbGlnbiA9ICJsIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CnFxbm9ybShyZWdyZXNzaW9uXzQkcmVzaWR1YWxzLCBtYWluID0gIk5vcm1hbCBRLVEgUGxvdCBmb3IgUmVncmVzc2lvbiBNb2RlbCA0IikKcXFsaW5lKHJlZ3Jlc3Npb25fNCRyZXNpZHVhbHMpCmBgYAoKIyMgUmVncmVzc2lvbiBNb2RlbCAyOiBQb2x5bm9taWFsLCB5LnRyYW5zIH4geDEsIHgxXjIsIHgxXjMKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgcmVzdWx0cz0naGlkZSd9CiMgSW5pdGlhbGl6ZSByZWdyZXNzaW9uIG1vZGVsIDIKcmVncmVzc2lvbl8yIDwtIGdsbSh5LnRyYW5zIH4geDEgKyBJKHgxXjIpICsgSSh4MV4zKSkKCiMgUGxvdCB0aGUgcmVkaWN0aW9uIHJlc3VsdCBvZiByZWdyZXNzaW9uIG1vZGVsIDIKcHJlZGljdFJlZ3Jlc3Npb24ud2luZFNwZWVkKHgxLnByZWQsIHgyLCB5LnRyYW5zLCByZWdyZXNzaW9uXzIpCnRpdGxlKG1haW4gPSAnUHJlZGljdGlvbiBvZiBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCAyIG9mIFdpbmQgUG93ZXIgUHJvZHVjdGlvbicpCmBgYAoKUmVzdWx0IG9mIHRoZSByZWdyZXNzaW9uIGFuYWx5c2lzIDIuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQprYWJsZShjb2VmKHN1bW1hcnkocmVncmVzc2lvbl8yKSksIGFsaWduID0gImwiKQpgYGAKCiMjIFJlZ3Jlc3Npb24gTW9kZWwgNTogUG9seW5vbWlhbCwgeS50cmFucyB+IHgxXjIsIHgxXjMKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgcmVzdWx0cz0naGlkZSd9CiMgSW5pdGlhbGl6ZSByZWdyZXNzaW9uIG1vZGVsIDUKcmVncmVzc2lvbl81IDwtIGdsbSh5LnRyYW5zIH4gSSh4MV4yKSArIEkoeDFeMykpCgojIFBsb3QgdGhlIHJlZGljdGlvbiByZXN1bHQgb2YgcmVncmVzc2lvbiBtb2RlbCA1CnByZWRpY3RSZWdyZXNzaW9uLndpbmRTcGVlZCh4MS5wcmVkLCB4MiwgeS50cmFucywgcmVncmVzc2lvbl81KQp0aXRsZShtYWluID0gJ1ByZWRpY3Rpb24gb2YgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwgNSBvZiBXaW5kIFBvd2VyIFByb2R1Y3Rpb24nKQpgYGAKClJlc3VsdCBvZiB0aGUgcmVncmVzc2lvbiBhbmFseXNpcyA1LgoKYGBge3IsIGVjaG8gPSBGQUxTRX0Ka2FibGUoY29lZihzdW1tYXJ5KHJlZ3Jlc3Npb25fNSkpLCBhbGlnbiA9ICJsIikKYGBgCgojIyBSZWdyZXNzaW9uIE1vZGVsIDY6IFNpbXBsZSwgeS50cmFucyB+IHgxXjMKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgcmVzdWx0cz0naGlkZSd9CiMgSW5pdGlhbGl6ZSByZWdyZXNzaW9uIG1vZGVsIDYKcmVncmVzc2lvbl82IDwtIGdsbSh5LnRyYW5zIH4gSSh4MV4zKSkKCiMgUGxvdCB0aGUgcmVkaWN0aW9uIHJlc3VsdCBvZiByZWdyZXNzaW9uIG1vZGVsIDYKcHJlZGljdFJlZ3Jlc3Npb24ud2luZFNwZWVkKHgxLnByZWQsIHgyLCB5LnRyYW5zLCByZWdyZXNzaW9uXzYpCnRpdGxlKG1haW4gPSAnUHJlZGljdGlvbiBvZiBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCA2IG9mIFdpbmQgUG93ZXIgUHJvZHVjdGlvbicpCmBgYAoKUmVzdWx0IG9mIHRoZSByZWdyZXNzaW9uIGFuYWx5c2lzIDYuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQprYWJsZShjb2VmKHN1bW1hcnkocmVncmVzc2lvbl82KSksIGFsaWduID0gImwiKQpgYGAKCiMjIFJlZ3Jlc3Npb24gNzogTXVsdGlwbGUgUmVncmVzc2lvbiwgeSB+IHgxXjMsIHgyCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIHJlc3VsdHM9J2hpZGUnfQojIEluaXRpYWxpemUgcmVncmVzc2lvbiBtb2RlbCAxCnJlZ3Jlc3Npb25fNyA8LSBnbG0oeSB+IEkoeDFeMykgKyB4MikgICMgSSgpIGlzIHRoZSBpZGVudGl0eSBmdW5jdGlvbiB0aGF0IHJldHVybnMgaXRzIGFyZ3VtZW50IHVuYWx0ZXJlZC4KCiMgUGxvdCB0aGUgcmVkaWN0aW9uIHJlc3VsdCBvZiByZWdyZXNzaW9uIG1vZGVsIDEKcHJlZGljdFJlZ3Jlc3Npb24ud2luZFNwZWVkKHgxLnByZWQsIHgyLCB5LCByZWdyZXNzaW9uXzcpCnRpdGxlKG1haW4gPSAnUHJlZGljdGlvbiBvZiBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCA3IG9mIFdpbmQgUG93ZXIgUHJvZHVjdGlvbicpCmBgYAoKUmVzdWx0IG9mIHRoZSByZWdyZXNzaW9uIGFuYWx5c2lzIDcuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQprYWJsZShjb2VmKHN1bW1hcnkocmVncmVzc2lvbl83KSksIGFsaWduID0gImwiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMH0KcXFub3JtKHJlZ3Jlc3Npb25fNyRyZXNpZHVhbHMsIG1haW4gPSAiTm9ybWFsIFEtUSBQbG90IGZvciBSZWdyZXNzaW9uIE1vZGVsIDciKQpxcWxpbmUocmVncmVzc2lvbl83JHJlc2lkdWFscykKYGBgCgojIyBSZWdyZXNzaW9uIDg6IE11bHRpcGxlIFJlZ3Jlc3Npb24sIHkgfiB4Ml4zLCB4MV4zLCB4MgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCByZXN1bHRzPSdoaWRlJ30KIyBJbml0aWFsaXplIHJlZ3Jlc3Npb24gbW9kZWwgMQpyZWdyZXNzaW9uXzggPC0gZ2xtKHkgfiBJKHgxXjIpICsgSSh4MV4zKSArIHgyKSAgIyBJKCkgaXMgdGhlIGlkZW50aXR5IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBpdHMgYXJndW1lbnQgdW5hbHRlcmVkLgoKIyBQbG90IHRoZSByZWRpY3Rpb24gcmVzdWx0IG9mIHJlZ3Jlc3Npb24gbW9kZWwgMQpwcmVkaWN0UmVncmVzc2lvbi53aW5kU3BlZWQoeDEucHJlZCwgeDIsIHksIHJlZ3Jlc3Npb25fOCkKdGl0bGUobWFpbiA9ICdQcmVkaWN0aW9uIG9mIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsIDggb2YgV2luZCBQb3dlciBQcm9kdWN0aW9uJykKYGBgCgpSZXN1bHQgb2YgdGhlIHJlZ3Jlc3Npb24gYW5hbHlzaXMgOC4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CmthYmxlKGNvZWYoc3VtbWFyeShyZWdyZXNzaW9uXzgpKSwgYWxpZ24gPSAibCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDEwfQpxcW5vcm0ocmVncmVzc2lvbl84JHJlc2lkdWFscywgbWFpbiA9ICJOb3JtYWwgUS1RIFBsb3QgZm9yIFJlZ3Jlc3Npb24gTW9kZWwgOCIpCnFxbGluZShyZWdyZXNzaW9uXzgkcmVzaWR1YWxzKQpgYGAKCiMjIEZpbmFsIE1vZGVsCgpgYGB7ciwgZWNobyA9IEZBTFNFfQphaWMuZnJhbWUgPC0gZGF0YS5mcmFtZSgKICAgIG5hbWUgPSBjKCdyZWdyZXNzaW9uXzEnLCAncmVncmVzc2lvbl8yJywgJ3JlZ3Jlc3Npb25fMycsICdyZWdyZXNzaW9uXzQnLCAncmVncmVzc2lvbl81JywgJ3JlZ3Jlc3Npb25fNicsICdyZWdyZXNzaW9uXzcnLCAncmVncmVzc2lvbl84JyksIAogICAgZXhwcmVzc2lvbiA9IGMoJ3kudHJhbnMgfiB4MSwgeDFeMiwgeDFeMywgeDInLCAneS50cmFucyB+IHgxLCB4MV4yLCB4MV4zJywgJ3kudHJhbnMgfiB4MV4yLCB4MV4zLCB4MicsICd5LnRyYW5zIH4geDFeMywgeDInLCAneS50cmFucyB+IHgxXjIsIHgxXjMnLCAneS50cmFucyB+IHgxXjMnLCAneSB+IHgxXjMsIHgyJywgJ3kgfiB4MV4yLCB4MV4zLCB4MicpLAogICAgdHlwZSA9IGMoJ1RyYW5zZm9ybWVkIE11bHRpcGxlIFJlZ3Jlc3Npb24nLCAnVHJhbnNmb3JtZWQgUG9seW5vbWlhbCBSZWdyZXNzaW9uJywgJ1RyYW5zZm9ybWVkIE11bHRpcGxlIFJlZ3Jlc3Npb24nLCAnVHJhbnNmb3JtZWQgTXVsdGlwbGUgUmVncmVzc2lvbicsICdUcmFuc2Zvcm1lZCBQb2x5bm9taWFsIFJlZ3Jlc3Npb24nLCAnVHJhbnNmb3JtZWQgU2ltcGxlIFJlZ3Jlc3Npb24nLCAnTXVsdGlwbGUgUmVncmVzc2lvbicsICdNdWx0aXBsZSBSZWdyZXNzaW9uJyksCiAgICBhaWMgPSBjKHJlZ3Jlc3Npb25fMSRhaWMsIHJlZ3Jlc3Npb25fMiRhaWMsIHJlZ3Jlc3Npb25fMyRhaWMsIHJlZ3Jlc3Npb25fNCRhaWMsIHJlZ3Jlc3Npb25fNSRhaWMsIHJlZ3Jlc3Npb25fNiRhaWMsIHJlZ3Jlc3Npb25fNyRhaWMsIHJlZ3Jlc3Npb25fOCRhaWMpKQprbml0cjo6a2FibGUoYWljLmZyYW1lLCBjb2wubmFtZXMgPSBjKCdOYW1lJywgJ0V4cHJlc3Npb24nLCAnVHlwZScsICdBSUMnKSwgY2FwdGlvbiA9ICdNTEUgb2YgbGFtYmRhIGluIHRyYW5zZm9ybWF0aW9uJywgYWxpZ24gPSAibCIpCmBgYAoKQWNjb3JkaW5nIHRvIEFJQywgd2UgY2hvb3NlIHRyYW5zZm9ybWVkIG11bHRpcGxlIHJlZ3Jlc3Npb24gbW9kZWwgInJlZ3Jlc3Npb25fNCIsIHdoaWNoIGV4cHJlc3NlcyB0aGUgcmVsYXRpb25zaGlwIG9mIHRyYW5zZm9ybWVkIG5vcm1hbGl6ZWQgcG93ZXIgb3V0cHV0IGFuZCBjdWJpYyBvZiB3aW5kIHNwZWVkIGFzIHdlbGwgYXMgd2luZCBkaXJlY3Rpb24uCgo=