Problem 5
We have seen that we can fit an SVM with a non-linear kernel
in order to perform classification using a non-linear decision boundary.
We will now see that we can also obtain a non-linear decision boundary
by performing logistic regression using non-linear transformations of
the features.
(a) Generate a data set with n = 500 and p = 2, such that the
observations belong to two classes with a quadratic decision boundary
between them.
set.seed(1)
x1 <- runif(500) - 0.5
x2 <- runif(500) - 0.5
y <- 1 * (x1^2 - x2^2 > 0)
data <- data.frame(x1 = x1, x2 = x2, y = as.factor(y))
(b) Plot the observations, colored according to their class
labels. Your plot should display X1 on the x-axis, and X2 on the
y-axis.
plot(x1, x2, col = ifelse(y == 1, "red", "blue"), pch = 19,
xlab = "X1", ylab = "X2", main = "True Class Labels")

(c) Fit a logistic regression model to the data, using X1 and
X2 as predictors.
glm_fit <- glm(y ~ x1 + x2, data = data, family = binomial)
summary(glm_fit)
Call:
glm(formula = y ~ x1 + x2, family = binomial, data = data)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -0.087260 0.089579 -0.974 0.330
x1 0.196199 0.316864 0.619 0.536
x2 -0.002854 0.305712 -0.009 0.993
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 692.18 on 499 degrees of freedom
Residual deviance: 691.79 on 497 degrees of freedom
AIC: 697.79
Number of Fisher Scoring iterations: 3
(d) Apply this model to the training data in order to obtain
a predicted class label for each training observation. Plot the
observations, colored according to the predicted class labels. The
decision boundary should be linear.
glm_probs <- predict(glm_fit, data, type = "response")
glm_pred <- ifelse(glm_probs > 0.5, 1, 0)
plot(x1, x2, col = ifelse(glm_pred == 1, "red", "blue"), pch = 19,
xlab = "X1", ylab = "X2", main = "Logistic Regression (Linear Terms)")

(e) Now fit a logistic regression model to the data using
non-linear functions of X1 and X2 as predictors (e.g. X1², X1×X2,
log(X2), and so forth).
glm_fit2 <- glm(y ~ poly(x1, 2) + poly(x2, 2) + x1:x2, data = data, family = binomial)
summary(glm_fit2)
Call:
glm(formula = y ~ poly(x1, 2) + poly(x2, 2) + x1:x2, family = binomial,
data = data)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -102.2 4302.0 -0.024 0.981
poly(x1, 2)1 2715.3 141109.5 0.019 0.985
poly(x1, 2)2 27218.5 842987.2 0.032 0.974
poly(x2, 2)1 -279.7 97160.4 -0.003 0.998
poly(x2, 2)2 -28693.0 875451.3 -0.033 0.974
x1:x2 -206.4 41802.8 -0.005 0.996
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 6.9218e+02 on 499 degrees of freedom
Residual deviance: 3.5810e-06 on 494 degrees of freedom
AIC: 12
Number of Fisher Scoring iterations: 25
(f) Apply this model to the training data in order to obtain
a predicted class label for each training observation. Plot the
observations, colored according to the predicted class labels. The
decision boundary should be obviously non-linear.
glm_probs2 <- predict(glm_fit2, data, type = "response")
glm_pred2 <- ifelse(glm_probs2 > 0.5, 1, 0)
plot(x1, x2, col = ifelse(glm_pred2 == 1, "red", "blue"), pch = 19,
xlab = "X1", ylab = "X2", main = "Logistic Regression (Quadratic Terms)")

(g) Fit a support vector classifier to the data with X1 and
X2 as predictors. Obtain a class prediction for each training
observation. Plot the observations, colored according to the predicted
class labels.
library(caret)
train_control <- trainControl(method = "cv", number = 10)
svm_linear <- train(y~x1+x2, data = data, method = "svmLinear", trControl = train_control,
preProcess = c("center","scale"))
svm_linear_pred <- predict(svm_linear, data)
plot(x1, x2, col = ifelse(svm_linear_pred == 1, "red", "blue"), pch = 19,
xlab = "X1", ylab = "X2", main = "Support Vector Classifier (Linear)")

- The linear support vector classifier predicted every observation as
the same class. This suggests that a linear decision boundary is not
flexible enough to capture the non-linear relationship in the data.
(h) Fit a SVM using a non-linear kernel to the data. Obtain a
class prediction for each training observation. Plot the observations,
colored according to the predicted class labels.
train_control <- trainControl(method = "cv", number = 10)
svm_radial <- train(y~x1+x2, data = data, method = "svmRadial", trControl = train_control,
preProcess = c("center", "scale"))
svm_radial_pred <- predict(svm_radial, data)
plot(x1, x2, col = ifelse(svm_radial_pred == 1, "red", "blue"), pch = 19,
xlab = "X1", ylab = "X2", main = "Support Vector Machine (Radial Kernel)")

- The SVM with a radial kernel correctly classified nearly all of the
observations, indicating that the non-linear kernel was able to model
the underlying relationship between x1, x2, and y.
(i) Comment on your results.
- This exercise shows that both logistic regression and SVMs can model
non-linear decision boundaries when the appropriate transformations or
kernels are used. The linear models were unable to separate the classes,
while the non-linear logistic regression and radial SVM provided a much
better fit to the data.
Problem 7
In this problem, you will use support vector approaches in
order to predict whether a given car gets high or low gas mileage based
on the Auto data set.
(a) Create a binary variable that takes on a 1 for cars with
gas mileage above the median, and a 0 for cars with gas mileage below
the median.
library(ISLR2)
Auto$mpg01 <- as.factor(ifelse(Auto$mpg > median(Auto$mpg), 1, 0))
(b) Fit a support vector classifier to the data with various
values of cost, in order to predict whether a car gets high or low gas
mileage. Report the cross-validation errors associated with different
values of this parameter. Comment on your results. Note you will need to
fit the classifier without the gas mileage variable to produce sensible
results.
library(e1071)
set.seed(1)
Auto_svm <- Auto[, !(names(Auto) %in% c("mpg", "name"))]
train_control = trainControl(method = "cv", number = 10)
tune_linear <- train(mpg01~.,data=Auto_svm,method='svmLinear',trControl=train_control
,preProcess=c('center','scale'),tuneGrid=expand.grid(C=c(0.01, 0.1, 1, 5, 10, 100)))
tune_linear$results
- The cross-validation error ranged from approximately 8.7% to 9.5%
across the tested cost values. The lowest error occurred when C = 10,
but the differences between the models were minimal, indicating that
changing the cost parameter had little effect on classification
performance.
(c) Now repeat (b), this time using SVMs with radial and
polynomial basis kernels, with different values of gamma and degree and
cost. Comment on your results.
set.seed(1)
train_control = trainControl(method = "cv", number = 10)
tune_radial <- train(mpg01~.,data=Auto_svm,method='svmRadial',trControl=train_control
,preProcess=c('center','scale'),tuneGrid=
expand.grid(C = c(0.1, 1, 5, 10, 100),sigma = c(0.01, 0.1, 1, 5)))
tune_radial$results
- The radial SVM produced cross-validation accuracies between
approximately 89.0% and 91.3%. The best performance occurred when sigma
= 0.10 and C = 5 or 10, with an accuracy of about 91.3%. Compared with
the linear SVM, the radial kernel showed only a slight improvement,
suggesting that both models performed similarly on this dataset.
set.seed(1)
train_control = trainControl(method = "cv", number = 10)
tune_poly <- train(mpg01~.,data=Auto_svm,method='svmPoly',trControl=train_control
,preProcess=c('center','scale'),tuneGrid=
expand.grid(C = c(0.1, 1, 5, 10, 100), degree = c(2, 3, 4), scale = c(0.1,.1)))
tune_poly$results
- The polynomial SVM achieved a maximum cross-validation accuracy of
approximately 91.1% with a degree of 3 and C = 5. Its performance was
very similar to the linear and radial SVMs, indicating that the choice
of kernel had only a modest impact on prediction accuracy for this
dataset.
(d) Make some plots to back up your assertions in (b) and
(c).
svm_best_linear <- svm(mpg01 ~ ., data = Auto_svm, kernel = "linear",
cost = tune_linear$bestTune)
svm_best_radial <- svm(mpg01 ~ ., data = Auto_svm, kernel = "radial",
cost = tune_radial$bestTune,
sigma = tune_radial$bestTune)
plot(svm_best_linear, Auto_svm, horsepower ~ weight)

plot(svm_best_radial, Auto_svm, horsepower ~ weight)

plot(svm_best_linear, Auto_svm, displacement ~ acceleration)

- These pairwise plots show the classification results from the SVM
model using horsepower vs. weight and displacement vs. acceleration. The
variables provide a reasonable separation between the two mpg classes,
as heavier and more powerful vehicles tend to fall into the low-mpg
category, while lighter vehicles tend to fall into the high-mpg
category.
Problem 8
This problem involves the OJ data set which is part of the
ISLR2 package.
(a) Create a training set containing a random sample of 800
observations, and a test set containing the remaining
observations.
set.seed(1)
train_oj <- sample(1:nrow(OJ), 800)
oj_train <- OJ[train_oj, ]
oj_test <- OJ[-train_oj, ]
(b) Fit a support vector classifier to the training data
using cost = 0.01, with Purchase as the response and the other variables
as predictors. Use the summary() function to produce summary statistics,
and describe the results obtained.
svm_oj_linear <- train(Purchase~.,data = oj_train, method = "svmLinear",
trControl = train_control, preProcess = c("center", "scale"), tuneGrid = expand.grid(C=0.01))
svm_oj_linear$results
- With cost = 0.01, the SVM uses a large number of support vectors.
This is expected because a small cost value creates a wider margin and
allows more observations to contribute to defining the decision
boundary.
(c) What are the training and test error rates?
train_pred_linear <- predict(svm_oj_linear, oj_train)
test_pred_linear <- predict(svm_oj_linear, oj_test)
mean(train_pred_linear != oj_train$Purchase)
[1] 0.17625
mean(test_pred_linear != oj_test$Purchase)
[1] 0.1851852
- Both the training and test error rates are about 17-18%, showing us
the classifier generalizes well and isn’t overfitting despite the low
cost value.
(d) Use the tune() function to select an optimal cost.
Consider values in the range 0.01 to 10.
set.seed(1)
tune_oj_linear <- train(Purchase~., data = oj_train, method = "svmLinear", trControl = train_control,
preProcess = c("center", "scale"),
tuneGrid = expand.grid(C = seq(0.01, 10, length.out = 10)))
tune_oj_linear$results
tune_oj_linear$bestTune
(e) Compute the training and test error rates using this new
value for cost.
train_pred_linear <- predict(tune_oj_linear, oj_train)
test_pred_linear <- predict(tune_oj_linear, oj_test)
mean(train_pred != oj_train$Purchase)
[1] 0.16625
mean(test_pred != oj_test$Purchase)
[1] 0.1518519
- Using the tuned cost value produces a very slight improvement in
both training and test error compared to cost = 0.01, though the
difference is small since the original error rates were already pretty
low.
(f) Repeat parts (b) through (e) using a support vector
machine with a radial kernel. Use the default value for
gamma.
#Ploy results and best tune before tune
svm_oj_radial <- train(Purchase~.,data = oj_train, method = "svmRadial",
trControl = train_control, preProcess = c("center", "scale"),
tuneGrid = expand.grid(C=0.01,sigma = 0.01))
svm_oj_radial$results
NA
#radial test train errors before tune
train_pred_radial <- predict(svm_oj_radial, oj_train)
test_pred_radial <- predict(svm_oj_radial, oj_test)
mean(train_pred_radial != oj_train$Purchase)
[1] 0.39375
mean(test_pred_radial != oj_test$Purchase)
[1] 0.3777778
#Radial results and best tune
set.seed(1)
tune_oj_radial <- train(Purchase~., data = oj_train, method = "svmRadial", trControl = train_control,
preProcess = c("center", "scale"),
tuneLength = 10)
tune_oj_radial$results
tune_oj_radial$bestTune
NA
#radial test train errors
train_pred_radial <- predict(tune_oj_radial, oj_train)
test_pred_radial <- predict(tune_oj_radial, oj_test)
mean(train_pred_radial != oj_train$Purchase)
[1] 0.14875
mean(test_pred_radial != oj_test$Purchase)
[1] 0.1814815
- With cost = 0.01, the radial kernel essentially predicts the
majority class for every observation, since the default gamma combined
with such a low cost produces an overly simple boundary. After tuning
cost, the radial kernel’s performance improves substantially and becomes
competitive with the linear kernel, achieving a test error rate in a
similar range (roughly 17-19%).
(g) Repeat parts (b) through (e) using a support vector
machine with a polynomial kernel. Set degree = 2.
#Ploy results and best tune
svm_oj_poly <- train(Purchase~.,data = oj_train, method = "svmPoly",
trControl = train_control, preProcess = c("center", "scale"),
tuneGrid = expand.grid(C=0.01,scale = 0.01, degree = 2))
svm_oj_poly$results
#Poly test train errors before tune
train_pred_poly <- predict(svm_oj_poly, oj_train)
test_pred_poly <- predict(svm_oj_poly, oj_test)
mean(train_pred_poly != oj_train$Purchase)
[1] 0.39375
mean(test_pred_poly != oj_test$Purchase)
[1] 0.3777778
#Ploy results and best tune
set.seed(1)
tune_oj_poly <- train(Purchase~., data = oj_train, method = "svmPoly", trControl = train_control,
preProcess = c("center", "scale"),
tuneGrid = expand.grid(degree = c(2, 3, 4),scale = 0.01,C = c(0.01, 0.1, 1, 10)))
tune_oj_poly$results
tune_oj_poly$bestTune
#Poly test train errors
train_pred_poly <- predict(tune_oj_poly, oj_train)
test_pred_poly <- predict(tune_oj_poly, oj_test)
mean(train_pred_poly != oj_train$Purchase)
[1] 0.155
mean(test_pred_poly != oj_test$Purchase)
[1] 0.1777778
- The polynomial kernel with a small cost value (C = 0.01) performs
poorly compared with the tuned model. After tuning, the polynomial SVM
improves its classification performance, but it does not outperform the
linear or radial kernels. This suggests that the additional complexity
from the polynomial boundary does not significantly improve prediction
for this dataset.
(h) Overall, which approach seems to give the best results on
this data?
results_oj <- data.frame(
Kernel = c("Linear", "Radial", "Polynomial"),
Test_Error = c(
mean(test_pred_linear != oj_test$Purchase),
mean(test_pred_radial != oj_test$Purchase),
mean(test_pred_poly != oj_test$Purchase)
))
results_oj
- Overall, the tuned linear SVM produced the lowest test error rate,
outperforming both the radial and polynomial kernels. The radial and
polynomial kernels had similar performance, with slightly higher error
rates around 18%. This suggests that the additional complexity provided
by the non-linear kernels did not improve classification accuracy for
the OJ dataset, and a simpler linear decision boundary was
sufficient.
LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQ4Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMjIFByb2JsZW0gNQ0KDQpfX1dlIGhhdmUgc2VlbiB0aGF0IHdlIGNhbiBmaXQgYW4gU1ZNIHdpdGggYSBub24tbGluZWFyIGtlcm5lbCBpbiBvcmRlciB0byBwZXJmb3JtIGNsYXNzaWZpY2F0aW9uIHVzaW5nIGEgbm9uLWxpbmVhciBkZWNpc2lvbiBib3VuZGFyeS4gV2Ugd2lsbCBub3cgc2VlIHRoYXQgd2UgY2FuIGFsc28gb2J0YWluIGEgbm9uLWxpbmVhciBkZWNpc2lvbiBib3VuZGFyeSBieSBwZXJmb3JtaW5nIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdXNpbmcgbm9uLWxpbmVhciB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIGZlYXR1cmVzLl9fDQoNCl9fKGEpIEdlbmVyYXRlIGEgZGF0YSBzZXQgd2l0aCBuID0gNTAwIGFuZCBwID0gMiwgc3VjaCB0aGF0IHRoZSBvYnNlcnZhdGlvbnMgYmVsb25nIHRvIHR3byBjbGFzc2VzIHdpdGggYSBxdWFkcmF0aWMgZGVjaXNpb24gYm91bmRhcnkgYmV0d2VlbiB0aGVtLl9fDQpgYGB7cn0NCnNldC5zZWVkKDEpDQp4MSA8LSBydW5pZig1MDApIC0gMC41DQp4MiA8LSBydW5pZig1MDApIC0gMC41DQp5ICA8LSAxICogKHgxXjIgLSB4Ml4yID4gMCkNCmRhdGEgPC0gZGF0YS5mcmFtZSh4MSA9IHgxLCB4MiA9IHgyLCB5ID0gYXMuZmFjdG9yKHkpKQ0KYGBgDQoNCl9fKGIpIFBsb3QgdGhlIG9ic2VydmF0aW9ucywgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlaXIgY2xhc3MgbGFiZWxzLiBZb3VyIHBsb3Qgc2hvdWxkIGRpc3BsYXkgWDEgb24gdGhlIHgtYXhpcywgYW5kIFgyIG9uIHRoZSB5LWF4aXMuX18NCmBgYHtyfQ0KcGxvdCh4MSwgeDIsIGNvbCA9IGlmZWxzZSh5ID09IDEsICJyZWQiLCAiYmx1ZSIpLCBwY2ggPSAxOSwNCiAgICAgeGxhYiA9ICJYMSIsIHlsYWIgPSAiWDIiLCBtYWluID0gIlRydWUgQ2xhc3MgTGFiZWxzIikNCmBgYA0KDQoNCl9fKGMpIEZpdCBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgdG8gdGhlIGRhdGEsIHVzaW5nIFgxIGFuZCBYMiBhcyBwcmVkaWN0b3JzLl9fDQpgYGB7cn0NCmdsbV9maXQgPC0gZ2xtKHkgfiB4MSArIHgyLCBkYXRhID0gZGF0YSwgZmFtaWx5ID0gYmlub21pYWwpDQpzdW1tYXJ5KGdsbV9maXQpDQpgYGANCg0KX18oZCkgQXBwbHkgdGhpcyBtb2RlbCB0byB0aGUgdHJhaW5pbmcgZGF0YSBpbiBvcmRlciB0byBvYnRhaW4gYSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWwgZm9yIGVhY2ggdHJhaW5pbmcgb2JzZXJ2YXRpb24uIFBsb3QgdGhlIG9ic2VydmF0aW9ucywgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMuIFRoZSBkZWNpc2lvbiBib3VuZGFyeSBzaG91bGQgYmUgbGluZWFyLl9fDQpgYGB7cn0NCmdsbV9wcm9icyA8LSBwcmVkaWN0KGdsbV9maXQsIGRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQ0KZ2xtX3ByZWQgIDwtIGlmZWxzZShnbG1fcHJvYnMgPiAwLjUsIDEsIDApDQoNCnBsb3QoeDEsIHgyLCBjb2wgPSBpZmVsc2UoZ2xtX3ByZWQgPT0gMSwgInJlZCIsICJibHVlIiksIHBjaCA9IDE5LA0KICAgICB4bGFiID0gIlgxIiwgeWxhYiA9ICJYMiIsIG1haW4gPSAiTG9naXN0aWMgUmVncmVzc2lvbiAoTGluZWFyIFRlcm1zKSIpDQpgYGANCg0KDQpfXyhlKSBOb3cgZml0IGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0byB0aGUgZGF0YSB1c2luZyBub24tbGluZWFyIGZ1bmN0aW9ucyBvZiBYMSBhbmQgWDIgYXMgcHJlZGljdG9ycyAoZS5nLiBYMcKyLCBYMcOXWDIsIGxvZyhYMiksIGFuZCBzbyBmb3J0aCkuX18NCmBgYHtyfQ0KZ2xtX2ZpdDIgPC0gZ2xtKHkgfiBwb2x5KHgxLCAyKSArIHBvbHkoeDIsIDIpICsgeDE6eDIsIGRhdGEgPSBkYXRhLCBmYW1pbHkgPSBiaW5vbWlhbCkNCnN1bW1hcnkoZ2xtX2ZpdDIpDQpgYGANCg0KX18oZikgQXBwbHkgdGhpcyBtb2RlbCB0byB0aGUgdHJhaW5pbmcgZGF0YSBpbiBvcmRlciB0byBvYnRhaW4gYSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWwgZm9yIGVhY2ggdHJhaW5pbmcgb2JzZXJ2YXRpb24uIFBsb3QgdGhlIG9ic2VydmF0aW9ucywgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMuIFRoZSBkZWNpc2lvbiBib3VuZGFyeSBzaG91bGQgYmUgb2J2aW91c2x5IG5vbi1saW5lYXIuX18NCmBgYHtyfQ0KZ2xtX3Byb2JzMiA8LSBwcmVkaWN0KGdsbV9maXQyLCBkYXRhLCB0eXBlID0gInJlc3BvbnNlIikNCmdsbV9wcmVkMiAgPC0gaWZlbHNlKGdsbV9wcm9iczIgPiAwLjUsIDEsIDApDQoNCnBsb3QoeDEsIHgyLCBjb2wgPSBpZmVsc2UoZ2xtX3ByZWQyID09IDEsICJyZWQiLCAiYmx1ZSIpLCBwY2ggPSAxOSwNCiAgICAgeGxhYiA9ICJYMSIsIHlsYWIgPSAiWDIiLCBtYWluID0gIkxvZ2lzdGljIFJlZ3Jlc3Npb24gKFF1YWRyYXRpYyBUZXJtcykiKQ0KYGBgDQoNCg0KX18oZykgRml0IGEgc3VwcG9ydCB2ZWN0b3IgY2xhc3NpZmllciB0byB0aGUgZGF0YSB3aXRoIFgxIGFuZCBYMiBhcyBwcmVkaWN0b3JzLiBPYnRhaW4gYSBjbGFzcyBwcmVkaWN0aW9uIGZvciBlYWNoIHRyYWluaW5nIG9ic2VydmF0aW9uLiBQbG90IHRoZSBvYnNlcnZhdGlvbnMsIGNvbG9yZWQgYWNjb3JkaW5nIHRvIHRoZSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWxzLl9fDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQoNCnRyYWluX2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKQ0Kc3ZtX2xpbmVhciA8LSB0cmFpbih5fngxK3gyLCBkYXRhID0gZGF0YSwgbWV0aG9kID0gInN2bUxpbmVhciIsIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wsIA0KICAgICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gICBjKCJjZW50ZXIiLCJzY2FsZSIpKQ0Kc3ZtX2xpbmVhcl9wcmVkIDwtIHByZWRpY3Qoc3ZtX2xpbmVhciwgZGF0YSkNCg0KcGxvdCh4MSwgeDIsIGNvbCA9IGlmZWxzZShzdm1fbGluZWFyX3ByZWQgPT0gMSwgInJlZCIsICJibHVlIiksIHBjaCA9IDE5LA0KICAgICB4bGFiID0gIlgxIiwgeWxhYiA9ICJYMiIsIG1haW4gPSAiU3VwcG9ydCBWZWN0b3IgQ2xhc3NpZmllciAoTGluZWFyKSIpDQpgYGANCg0KICAtIFRoZSBsaW5lYXIgc3VwcG9ydCB2ZWN0b3IgY2xhc3NpZmllciBwcmVkaWN0ZWQgZXZlcnkgb2JzZXJ2YXRpb24gYXMgdGhlIHNhbWUgY2xhc3MuIFRoaXMgc3VnZ2VzdHMgdGhhdCBhIGxpbmVhciBkZWNpc2lvbiBib3VuZGFyeSBpcyBub3QgZmxleGlibGUgZW5vdWdoIHRvIGNhcHR1cmUgdGhlIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwIGluIHRoZSBkYXRhLg0KDQpfXyhoKSBGaXQgYSBTVk0gdXNpbmcgYSBub24tbGluZWFyIGtlcm5lbCB0byB0aGUgZGF0YS4gT2J0YWluIGEgY2xhc3MgcHJlZGljdGlvbiBmb3IgZWFjaCB0cmFpbmluZyBvYnNlcnZhdGlvbi4gUGxvdCB0aGUgb2JzZXJ2YXRpb25zLCBjb2xvcmVkIGFjY29yZGluZyB0byB0aGUgcHJlZGljdGVkIGNsYXNzIGxhYmVscy5fXw0KYGBge3J9DQp0cmFpbl9jb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCnN2bV9yYWRpYWwgPC0gdHJhaW4oeX54MSt4MiwgZGF0YSA9IGRhdGEsIG1ldGhvZCA9ICJzdm1SYWRpYWwiLCB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLCANCiAgICAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIpKQ0Kc3ZtX3JhZGlhbF9wcmVkIDwtIHByZWRpY3Qoc3ZtX3JhZGlhbCwgZGF0YSkNCg0KcGxvdCh4MSwgeDIsIGNvbCA9IGlmZWxzZShzdm1fcmFkaWFsX3ByZWQgPT0gMSwgInJlZCIsICJibHVlIiksIHBjaCA9IDE5LA0KICAgICB4bGFiID0gIlgxIiwgeWxhYiA9ICJYMiIsIG1haW4gPSAiU3VwcG9ydCBWZWN0b3IgTWFjaGluZSAoUmFkaWFsKSIpDQpgYGANCg0KICAtIFRoZSBTVk0gd2l0aCBhIHJhZGlhbCBrZXJuZWwgY29ycmVjdGx5IGNsYXNzaWZpZWQgbmVhcmx5IGFsbCBvZiB0aGUgb2JzZXJ2YXRpb25zLCBpbmRpY2F0aW5nIHRoYXQgdGhlIG5vbi1saW5lYXIga2VybmVsIHdhcyBhYmxlIHRvIG1vZGVsIHRoZSB1bmRlcmx5aW5nIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHgxLCB4MiwgYW5kIHkuDQoNCl9fKGkpIENvbW1lbnQgb24geW91ciByZXN1bHRzLl9fDQoNCiAgLSBUaGlzIGV4ZXJjaXNlIHNob3dzIHRoYXQgYm90aCBsb2dpc3RpYyByZWdyZXNzaW9uIGFuZCBTVk1zIGNhbiBtb2RlbCBub24tbGluZWFyIGRlY2lzaW9uIGJvdW5kYXJpZXMgd2hlbiB0aGUgYXBwcm9wcmlhdGUgdHJhbnNmb3JtYXRpb25zIG9yIGtlcm5lbHMgYXJlIHVzZWQuIFRoZSBsaW5lYXIgbW9kZWxzIHdlcmUgdW5hYmxlIHRvIHNlcGFyYXRlIHRoZSBjbGFzc2VzLCB3aGlsZSB0aGUgbm9uLWxpbmVhciBsb2dpc3RpYyByZWdyZXNzaW9uIGFuZCByYWRpYWwgU1ZNIHByb3ZpZGVkIGEgbXVjaCBiZXR0ZXIgZml0IHRvIHRoZSBkYXRhLg0KDQotLS0NCg0KIyMjIFByb2JsZW0gNw0KDQpfX0luIHRoaXMgcHJvYmxlbSwgeW91IHdpbGwgdXNlIHN1cHBvcnQgdmVjdG9yIGFwcHJvYWNoZXMgaW4gb3JkZXIgdG8gcHJlZGljdCB3aGV0aGVyIGEgZ2l2ZW4gY2FyIGdldHMgaGlnaCBvciBsb3cgZ2FzIG1pbGVhZ2UgYmFzZWQgb24gdGhlIEF1dG8gZGF0YSBzZXQuX18NCg0KX18oYSkgQ3JlYXRlIGEgYmluYXJ5IHZhcmlhYmxlIHRoYXQgdGFrZXMgb24gYSAxIGZvciBjYXJzIHdpdGggZ2FzIG1pbGVhZ2UgYWJvdmUgdGhlIG1lZGlhbiwgYW5kIGEgMCBmb3IgY2FycyB3aXRoIGdhcyBtaWxlYWdlIGJlbG93IHRoZSBtZWRpYW4uX18NCmBgYHtyfQ0KbGlicmFyeShJU0xSMikNCg0KQXV0byRtcGcwMSA8LSBhcy5mYWN0b3IoaWZlbHNlKEF1dG8kbXBnID4gbWVkaWFuKEF1dG8kbXBnKSwgMSwgMCkpDQpgYGANCg0KX18oYikgRml0IGEgc3VwcG9ydCB2ZWN0b3IgY2xhc3NpZmllciB0byB0aGUgZGF0YSB3aXRoIHZhcmlvdXMgdmFsdWVzIG9mIGNvc3QsIGluIG9yZGVyIHRvIHByZWRpY3Qgd2hldGhlciBhIGNhciBnZXRzIGhpZ2ggb3IgbG93IGdhcyBtaWxlYWdlLiBSZXBvcnQgdGhlIGNyb3NzLXZhbGlkYXRpb24gZXJyb3JzIGFzc29jaWF0ZWQgd2l0aCBkaWZmZXJlbnQgdmFsdWVzIG9mIHRoaXMgcGFyYW1ldGVyLiBDb21tZW50IG9uIHlvdXIgcmVzdWx0cy4gTm90ZSB5b3Ugd2lsbCBuZWVkIHRvIGZpdCB0aGUgY2xhc3NpZmllciB3aXRob3V0IHRoZSBnYXMgbWlsZWFnZSB2YXJpYWJsZSB0byBwcm9kdWNlIHNlbnNpYmxlIHJlc3VsdHMuX18NCmBgYHtyfQ0KbGlicmFyeShlMTA3MSkNCg0Kc2V0LnNlZWQoMSkNCkF1dG9fc3ZtIDwtIEF1dG9bLCAhKG5hbWVzKEF1dG8pICVpbiUgYygibXBnIiwgIm5hbWUiKSldDQoNCnRyYWluX2NvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTApDQp0dW5lX2xpbmVhciA8LSB0cmFpbihtcGcwMX4uLGRhdGE9QXV0b19zdm0sbWV0aG9kPSdzdm1MaW5lYXInLHRyQ29udHJvbD10cmFpbl9jb250cm9sDQogICAgICAgICAgICAgICAgICAgICAscHJlUHJvY2Vzcz1jKCdjZW50ZXInLCdzY2FsZScpLHR1bmVHcmlkPWV4cGFuZC5ncmlkKEM9YygwLjAxLCAwLjEsIDEsIDUsIDEwLCAxMDApKSkNCnR1bmVfbGluZWFyJHJlc3VsdHMNCmBgYA0KDQogIC0gVGhlIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IgcmFuZ2VkIGZyb20gYXBwcm94aW1hdGVseSA4LjclIHRvIDkuNSUgYWNyb3NzIHRoZSB0ZXN0ZWQgY29zdCB2YWx1ZXMuIFRoZSBsb3dlc3QgZXJyb3Igb2NjdXJyZWQgd2hlbiBDID0gMTAsIGJ1dCB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgbW9kZWxzIHdlcmUgbWluaW1hbCwgaW5kaWNhdGluZyB0aGF0IGNoYW5naW5nIHRoZSBjb3N0IHBhcmFtZXRlciBoYWQgbGl0dGxlIGVmZmVjdCBvbiBjbGFzc2lmaWNhdGlvbiBwZXJmb3JtYW5jZS4NCg0KX18oYykgTm93IHJlcGVhdCAoYiksIHRoaXMgdGltZSB1c2luZyBTVk1zIHdpdGggcmFkaWFsIGFuZCBwb2x5bm9taWFsIGJhc2lzIGtlcm5lbHMsIHdpdGggZGlmZmVyZW50IHZhbHVlcyBvZiBnYW1tYSBhbmQgZGVncmVlIGFuZCBjb3N0LiBDb21tZW50IG9uIHlvdXIgcmVzdWx0cy5fXw0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KdHJhaW5fY29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCnR1bmVfcmFkaWFsIDwtIHRyYWluKG1wZzAxfi4sZGF0YT1BdXRvX3N2bSxtZXRob2Q9J3N2bVJhZGlhbCcsdHJDb250cm9sPXRyYWluX2NvbnRyb2wNCiAgICAgICAgICAgICAgICAgICAgICxwcmVQcm9jZXNzPWMoJ2NlbnRlcicsJ3NjYWxlJyksdHVuZUdyaWQ9DQogICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZC5ncmlkKEMgPSBjKDAuMSwgMSwgNSwgMTAsIDEwMCksc2lnbWEgPSBjKDAuMDEsIDAuMSwgMSwgNSkpKQ0KDQp0dW5lX3JhZGlhbCRyZXN1bHRzDQpgYGANCg0KICAtIFRoZSByYWRpYWwgU1ZNIHByb2R1Y2VkIGNyb3NzLXZhbGlkYXRpb24gYWNjdXJhY2llcyBiZXR3ZWVuIGFwcHJveGltYXRlbHkgODkuMCUgYW5kIDkxLjMlLiBUaGUgYmVzdCBwZXJmb3JtYW5jZSBvY2N1cnJlZCB3aGVuIHNpZ21hID0gMC4xMCBhbmQgQyA9IDUgb3IgMTAsIHdpdGggYW4gYWNjdXJhY3kgb2YgYWJvdXQgOTEuMyUuIENvbXBhcmVkIHdpdGggdGhlIGxpbmVhciBTVk0sIHRoZSByYWRpYWwga2VybmVsIHNob3dlZCBvbmx5IGEgc2xpZ2h0IGltcHJvdmVtZW50LCBzdWdnZXN0aW5nIHRoYXQgYm90aCBtb2RlbHMgcGVyZm9ybWVkIHNpbWlsYXJseSBvbiB0aGlzIGRhdGFzZXQuDQogIA0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KdHJhaW5fY29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCnR1bmVfcG9seSA8LSB0cmFpbihtcGcwMX4uLGRhdGE9QXV0b19zdm0sbWV0aG9kPSdzdm1Qb2x5Jyx0ckNvbnRyb2w9dHJhaW5fY29udHJvbA0KICAgICAgICAgICAgICAgICAgICAgLHByZVByb2Nlc3M9YygnY2VudGVyJywnc2NhbGUnKSx0dW5lR3JpZD0NCiAgICAgICAgICAgICAgICAgICAgICBleHBhbmQuZ3JpZChDID0gYygwLjEsIDEsIDUsIDEwLCAxMDApLCBkZWdyZWUgPSBjKDIsIDMsIDQpLCBzY2FsZSA9IGMoMC4xLC4xKSkpDQp0dW5lX3BvbHkkcmVzdWx0cw0KYGBgDQoNCiAgLSBUaGUgcG9seW5vbWlhbCBTVk0gYWNoaWV2ZWQgYSBtYXhpbXVtIGNyb3NzLXZhbGlkYXRpb24gYWNjdXJhY3kgb2YgYXBwcm94aW1hdGVseSA5MS4xJSB3aXRoIGEgZGVncmVlIG9mIDMgYW5kIEMgPSA1LiBJdHMgcGVyZm9ybWFuY2Ugd2FzIHZlcnkgc2ltaWxhciB0byB0aGUgbGluZWFyIGFuZCByYWRpYWwgU1ZNcywgaW5kaWNhdGluZyB0aGF0IHRoZSBjaG9pY2Ugb2Yga2VybmVsIGhhZCBvbmx5IGEgbW9kZXN0IGltcGFjdCBvbiBwcmVkaWN0aW9uIGFjY3VyYWN5IGZvciB0aGlzIGRhdGFzZXQuDQoNCl9fKGQpIE1ha2Ugc29tZSBwbG90cyB0byBiYWNrIHVwIHlvdXIgYXNzZXJ0aW9ucyBpbiAoYikgYW5kIChjKS5fXw0KYGBge3J9DQpzdm1fYmVzdF9saW5lYXIgPC0gc3ZtKG1wZzAxIH4gLiwgZGF0YSA9IEF1dG9fc3ZtLCBrZXJuZWwgPSAibGluZWFyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgY29zdCA9IHR1bmVfbGluZWFyJGJlc3RUdW5lKQ0Kc3ZtX2Jlc3RfcmFkaWFsIDwtIHN2bShtcGcwMSB+IC4sIGRhdGEgPSBBdXRvX3N2bSwga2VybmVsID0gInJhZGlhbCIsDQogICAgICAgICAgICAgICAgICAgICAgIGNvc3QgPSB0dW5lX3JhZGlhbCRiZXN0VHVuZSwNCiAgICAgICAgICAgICAgICAgICAgICAgc2lnbWEgPSB0dW5lX3JhZGlhbCRiZXN0VHVuZSkNCg0KcGxvdChzdm1fYmVzdF9saW5lYXIsIEF1dG9fc3ZtLCBob3JzZXBvd2VyIH4gd2VpZ2h0KQ0KcGxvdChzdm1fYmVzdF9yYWRpYWwsIEF1dG9fc3ZtLCBob3JzZXBvd2VyIH4gd2VpZ2h0KQ0KcGxvdChzdm1fYmVzdF9saW5lYXIsIEF1dG9fc3ZtLCBkaXNwbGFjZW1lbnQgfiBhY2NlbGVyYXRpb24pDQpgYGANCg0KICAtIFRoZXNlIHBhaXJ3aXNlIHBsb3RzIHNob3cgdGhlIGNsYXNzaWZpY2F0aW9uIHJlc3VsdHMgZnJvbSB0aGUgU1ZNIG1vZGVsIHVzaW5nIGhvcnNlcG93ZXIgdnMuIHdlaWdodCBhbmQgZGlzcGxhY2VtZW50IHZzLiBhY2NlbGVyYXRpb24uIFRoZSB2YXJpYWJsZXMgcHJvdmlkZSBhIHJlYXNvbmFibGUgc2VwYXJhdGlvbiBiZXR3ZWVuIHRoZSB0d28gbXBnIGNsYXNzZXMsIGFzIGhlYXZpZXIgYW5kIG1vcmUgcG93ZXJmdWwgdmVoaWNsZXMgdGVuZCB0byBmYWxsIGludG8gdGhlIGxvdy1tcGcgY2F0ZWdvcnksIHdoaWxlIGxpZ2h0ZXIgdmVoaWNsZXMgdGVuZCB0byBmYWxsIGludG8gdGhlIGhpZ2gtbXBnIGNhdGVnb3J5Lg0KICANCi0tLQ0KDQojIyMgUHJvYmxlbSA4DQoNCl9fVGhpcyBwcm9ibGVtIGludm9sdmVzIHRoZSBPSiBkYXRhIHNldCB3aGljaCBpcyBwYXJ0IG9mIHRoZSBJU0xSMiBwYWNrYWdlLl9fDQoNCl9fKGEpIENyZWF0ZSBhIHRyYWluaW5nIHNldCBjb250YWluaW5nIGEgcmFuZG9tIHNhbXBsZSBvZiA4MDAgb2JzZXJ2YXRpb25zLCBhbmQgYSB0ZXN0IHNldCBjb250YWluaW5nIHRoZSByZW1haW5pbmcgb2JzZXJ2YXRpb25zLl9fDQpgYGB7cn0NCnNldC5zZWVkKDEpDQp0cmFpbl9vaiA8LSBzYW1wbGUoMTpucm93KE9KKSwgODAwKQ0Kb2pfdHJhaW4gPC0gT0pbdHJhaW5fb2osIF0NCm9qX3Rlc3QgIDwtIE9KWy10cmFpbl9vaiwgXQ0KYGBgDQoNCl9fKGIpIEZpdCBhIHN1cHBvcnQgdmVjdG9yIGNsYXNzaWZpZXIgdG8gdGhlIHRyYWluaW5nIGRhdGEgdXNpbmcgY29zdCA9IDAuMDEsIHdpdGggUHVyY2hhc2UgYXMgdGhlIHJlc3BvbnNlIGFuZCB0aGUgb3RoZXIgdmFyaWFibGVzIGFzIHByZWRpY3RvcnMuIFVzZSB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uIHRvIHByb2R1Y2Ugc3VtbWFyeSBzdGF0aXN0aWNzLCBhbmQgZGVzY3JpYmUgdGhlIHJlc3VsdHMgb2J0YWluZWQuX18NCmBgYHtyfQ0Kc3ZtX29qX2xpbmVhciA8LSB0cmFpbihQdXJjaGFzZX4uLGRhdGEgPSBval90cmFpbiwgbWV0aG9kID0gInN2bUxpbmVhciIsIA0KICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLCBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiksIHR1bmVHcmlkID0gZXhwYW5kLmdyaWQoQz0wLjAxKSkNCnN2bV9val9saW5lYXIkcmVzdWx0cw0KYGBgDQoNCiAgLSBXaXRoIGNvc3QgPSAwLjAxLCB0aGUgU1ZNIHVzZXMgYSBsYXJnZSBudW1iZXIgb2Ygc3VwcG9ydCB2ZWN0b3JzLiBUaGlzIGlzIGV4cGVjdGVkIGJlY2F1c2UgYSBzbWFsbCBjb3N0IHZhbHVlIGNyZWF0ZXMgYSB3aWRlciBtYXJnaW4gYW5kIGFsbG93cyBtb3JlIG9ic2VydmF0aW9ucyB0byBjb250cmlidXRlIHRvIGRlZmluaW5nIHRoZSBkZWNpc2lvbiBib3VuZGFyeS4NCg0KX18oYykgV2hhdCBhcmUgdGhlIHRyYWluaW5nIGFuZCB0ZXN0IGVycm9yIHJhdGVzP19fDQpgYGB7cn0NCnRyYWluX3ByZWRfbGluZWFyIDwtIHByZWRpY3Qoc3ZtX29qX2xpbmVhciwgb2pfdHJhaW4pDQp0ZXN0X3ByZWRfbGluZWFyICAgPC0gcHJlZGljdChzdm1fb2pfbGluZWFyLCBval90ZXN0KQ0KDQptZWFuKHRyYWluX3ByZWRfbGluZWFyICE9IG9qX3RyYWluJFB1cmNoYXNlKQ0KbWVhbih0ZXN0X3ByZWRfbGluZWFyICE9IG9qX3Rlc3QkUHVyY2hhc2UpDQpgYGANCg0KICAtIEJvdGggdGhlIHRyYWluaW5nIGFuZCB0ZXN0IGVycm9yIHJhdGVzIGFyZSBhYm91dCAxNy0xOCUsIHNob3dpbmcgdXMgdGhlIGNsYXNzaWZpZXIgZ2VuZXJhbGl6ZXMgd2VsbCBhbmQgaXNuJ3Qgb3ZlcmZpdHRpbmcgZGVzcGl0ZSB0aGUgbG93IGNvc3QgdmFsdWUuDQoNCl9fKGQpIFVzZSB0aGUgdHVuZSgpIGZ1bmN0aW9uIHRvIHNlbGVjdCBhbiBvcHRpbWFsIGNvc3QuIENvbnNpZGVyIHZhbHVlcyBpbiB0aGUgcmFuZ2UgMC4wMSB0byAxMC5fXw0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KdHVuZV9val9saW5lYXIgPC0gdHJhaW4oUHVyY2hhc2V+LiwgZGF0YSA9IG9qX3RyYWluLCBtZXRob2QgPSAic3ZtTGluZWFyIiwgdHJDb250cm9sID0gdHJhaW5fY29udHJvbCwNCiAgICAgICAgICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKEMgPSBzZXEoMC4wMSwgMTAsIGxlbmd0aC5vdXQgPSAxMCkpKQ0KdHVuZV9val9saW5lYXIkcmVzdWx0cw0KdHVuZV9val9saW5lYXIkYmVzdFR1bmUNCmBgYA0KDQpfXyhlKSBDb21wdXRlIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBlcnJvciByYXRlcyB1c2luZyB0aGlzIG5ldyB2YWx1ZSBmb3IgY29zdC5fXw0KYGBge3J9DQp0cmFpbl9wcmVkX2xpbmVhciA8LSBwcmVkaWN0KHR1bmVfb2pfbGluZWFyLCBval90cmFpbikNCnRlc3RfcHJlZF9saW5lYXIgPC0gcHJlZGljdCh0dW5lX29qX2xpbmVhciwgb2pfdGVzdCkNCg0KbWVhbih0cmFpbl9wcmVkICE9IG9qX3RyYWluJFB1cmNoYXNlKQ0KbWVhbih0ZXN0X3ByZWQgIT0gb2pfdGVzdCRQdXJjaGFzZSkNCg0KYGBgDQoNCiAgLSBVc2luZyB0aGUgdHVuZWQgY29zdCB2YWx1ZSBwcm9kdWNlcyBhIHZlcnkgc2xpZ2h0IGltcHJvdmVtZW50IGluIGJvdGggdHJhaW5pbmcgYW5kIHRlc3QgZXJyb3IgY29tcGFyZWQgdG8gY29zdCA9IDAuMDEsIHRob3VnaCB0aGUgZGlmZmVyZW5jZSBpcyBzbWFsbCBzaW5jZSB0aGUgb3JpZ2luYWwgZXJyb3IgcmF0ZXMgd2VyZSBhbHJlYWR5IHByZXR0eSBsb3cuDQoNCl9fKGYpIFJlcGVhdCBwYXJ0cyAoYikgdGhyb3VnaCAoZSkgdXNpbmcgYSBzdXBwb3J0IHZlY3RvciBtYWNoaW5lIHdpdGggYSByYWRpYWwga2VybmVsLiBVc2UgdGhlIGRlZmF1bHQgdmFsdWUgZm9yIGdhbW1hLl9fDQpgYGB7cn0NCiNQbG95IHJlc3VsdHMgYW5kIGJlc3QgdHVuZSBiZWZvcmUgdHVuZQ0Kc3ZtX29qX3JhZGlhbCA8LSB0cmFpbihQdXJjaGFzZX4uLGRhdGEgPSBval90cmFpbiwgbWV0aG9kID0gInN2bVJhZGlhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLCBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiksIA0KICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKEM9MC4wMSxzaWdtYSA9IDAuMDEpKQ0Kc3ZtX29qX3JhZGlhbCRyZXN1bHRzDQoNCmBgYA0KDQpgYGB7cn0NCiNyYWRpYWwgdGVzdCB0cmFpbiBlcnJvcnMgYmVmb3JlIHR1bmUNCnRyYWluX3ByZWRfcmFkaWFsIDwtIHByZWRpY3Qoc3ZtX29qX3JhZGlhbCwgb2pfdHJhaW4pDQp0ZXN0X3ByZWRfcmFkaWFsIDwtIHByZWRpY3Qoc3ZtX29qX3JhZGlhbCwgb2pfdGVzdCkNCg0KbWVhbih0cmFpbl9wcmVkX3JhZGlhbCAhPSBval90cmFpbiRQdXJjaGFzZSkNCm1lYW4odGVzdF9wcmVkX3JhZGlhbCAhPSBval90ZXN0JFB1cmNoYXNlKQ0KYGBgDQoNCmBgYHtyfQ0KI1JhZGlhbCByZXN1bHRzIGFuZCBiZXN0IHR1bmUNCnNldC5zZWVkKDEpDQp0dW5lX29qX3JhZGlhbCA8LSB0cmFpbihQdXJjaGFzZX4uLCBkYXRhID0gb2pfdHJhaW4sIG1ldGhvZCA9ICJzdm1SYWRpYWwiLCB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLA0KICAgICAgICAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAxMCkNCnR1bmVfb2pfcmFkaWFsJHJlc3VsdHMNCnR1bmVfb2pfcmFkaWFsJGJlc3RUdW5lDQoNCmBgYA0KDQpgYGB7cn0NCiNyYWRpYWwgdGVzdCB0cmFpbiBlcnJvcnMNCnRyYWluX3ByZWRfcmFkaWFsIDwtIHByZWRpY3QodHVuZV9val9yYWRpYWwsIG9qX3RyYWluKQ0KdGVzdF9wcmVkX3JhZGlhbCA8LSBwcmVkaWN0KHR1bmVfb2pfcmFkaWFsLCBval90ZXN0KQ0KDQptZWFuKHRyYWluX3ByZWRfcmFkaWFsICE9IG9qX3RyYWluJFB1cmNoYXNlKQ0KbWVhbih0ZXN0X3ByZWRfcmFkaWFsICE9IG9qX3Rlc3QkUHVyY2hhc2UpDQpgYGANCg0KICAtIFdpdGggY29zdCA9IDAuMDEsIHRoZSByYWRpYWwga2VybmVsIGVzc2VudGlhbGx5IHByZWRpY3RzIHRoZSBtYWpvcml0eSBjbGFzcyBmb3IgZXZlcnkgb2JzZXJ2YXRpb24sIHNpbmNlIHRoZSBkZWZhdWx0IGdhbW1hIGNvbWJpbmVkIHdpdGggc3VjaCBhIGxvdyBjb3N0IHByb2R1Y2VzIGFuIG92ZXJseSBzaW1wbGUgYm91bmRhcnkuIEFmdGVyIHR1bmluZyBjb3N0LCB0aGUgcmFkaWFsIGtlcm5lbCdzIHBlcmZvcm1hbmNlIGltcHJvdmVzIHN1YnN0YW50aWFsbHkgYW5kIGJlY29tZXMgY29tcGV0aXRpdmUgd2l0aCB0aGUgbGluZWFyIGtlcm5lbCwgYWNoaWV2aW5nIGEgdGVzdCBlcnJvciByYXRlIGluIGEgc2ltaWxhciByYW5nZSAocm91Z2hseSAxNy0xOSUpLg0KDQpfXyhnKSBSZXBlYXQgcGFydHMgKGIpIHRocm91Z2ggKGUpIHVzaW5nIGEgc3VwcG9ydCB2ZWN0b3IgbWFjaGluZSB3aXRoIGEgcG9seW5vbWlhbCBrZXJuZWwuIFNldCBkZWdyZWUgPSAyLl9fDQpgYGB7cn0NCiNQbG95IHJlc3VsdHMgYW5kIGJlc3QgdHVuZQ0Kc3ZtX29qX3BvbHkgPC0gdHJhaW4oUHVyY2hhc2V+LixkYXRhID0gb2pfdHJhaW4sIG1ldGhvZCA9ICJzdm1Qb2x5IiwgDQogICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wsIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiKSwgDQogICAgICAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZXhwYW5kLmdyaWQoQz0wLjAxLHNjYWxlID0gMC4wMSwgZGVncmVlID0gMikpDQpzdm1fb2pfcG9seSRyZXN1bHRzDQpgYGANCg0KYGBge3J9DQojUG9seSB0ZXN0IHRyYWluIGVycm9ycyBiZWZvcmUgdHVuZQ0KdHJhaW5fcHJlZF9wb2x5IDwtIHByZWRpY3Qoc3ZtX29qX3BvbHksIG9qX3RyYWluKQ0KdGVzdF9wcmVkX3BvbHkgPC0gcHJlZGljdChzdm1fb2pfcG9seSwgb2pfdGVzdCkNCg0KbWVhbih0cmFpbl9wcmVkX3BvbHkgIT0gb2pfdHJhaW4kUHVyY2hhc2UpDQptZWFuKHRlc3RfcHJlZF9wb2x5ICE9IG9qX3Rlc3QkUHVyY2hhc2UpDQpgYGANCg0KYGBge3J9DQojUGxveSByZXN1bHRzIGFuZCBiZXN0IHR1bmUNCnNldC5zZWVkKDEpDQp0dW5lX29qX3BvbHkgPC0gdHJhaW4oUHVyY2hhc2V+LiwgZGF0YSA9IG9qX3RyYWluLCBtZXRob2QgPSAic3ZtUG9seSIsIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wsDQogICAgICAgICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChkZWdyZWUgPSBjKDIsIDMsIDQpLHNjYWxlID0gMC4wMSxDID0gYygwLjAxLCAwLjEsIDEsIDEwKSkpDQp0dW5lX29qX3BvbHkkcmVzdWx0cw0KdHVuZV9val9wb2x5JGJlc3RUdW5lDQpgYGANCg0KYGBge3J9DQojUG9seSB0ZXN0IHRyYWluIGVycm9ycw0KdHJhaW5fcHJlZF9wb2x5IDwtIHByZWRpY3QodHVuZV9val9wb2x5LCBval90cmFpbikNCnRlc3RfcHJlZF9wb2x5IDwtIHByZWRpY3QodHVuZV9val9wb2x5LCBval90ZXN0KQ0KDQptZWFuKHRyYWluX3ByZWRfcG9seSAhPSBval90cmFpbiRQdXJjaGFzZSkNCm1lYW4odGVzdF9wcmVkX3BvbHkgIT0gb2pfdGVzdCRQdXJjaGFzZSkNCmBgYA0KDQogIC0gVGhlIHBvbHlub21pYWwga2VybmVsIHdpdGggYSBzbWFsbCBjb3N0IHZhbHVlIChDID0gMC4wMSkgcGVyZm9ybXMgcG9vcmx5IGNvbXBhcmVkIHdpdGggdGhlIHR1bmVkIG1vZGVsLiBBZnRlciB0dW5pbmcsIHRoZSBwb2x5bm9taWFsIFNWTSBpbXByb3ZlcyBpdHMgY2xhc3NpZmljYXRpb24gcGVyZm9ybWFuY2UsIGJ1dCBpdCBkb2VzIG5vdCBvdXRwZXJmb3JtIHRoZSBsaW5lYXIgb3IgcmFkaWFsIGtlcm5lbHMuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgYWRkaXRpb25hbCBjb21wbGV4aXR5IGZyb20gdGhlIHBvbHlub21pYWwgYm91bmRhcnkgZG9lcyBub3Qgc2lnbmlmaWNhbnRseSBpbXByb3ZlIHByZWRpY3Rpb24gZm9yIHRoaXMgZGF0YXNldC4NCg0KX18oaCkgT3ZlcmFsbCwgd2hpY2ggYXBwcm9hY2ggc2VlbXMgdG8gZ2l2ZSB0aGUgYmVzdCByZXN1bHRzIG9uIHRoaXMgZGF0YT9fXw0KYGBge3J9DQpyZXN1bHRzX29qIDwtIGRhdGEuZnJhbWUoDQogIEtlcm5lbCA9IGMoIkxpbmVhciIsICJSYWRpYWwiLCAiUG9seW5vbWlhbCIpLA0KICBUZXN0X0Vycm9yID0gYygNCiAgICBtZWFuKHRlc3RfcHJlZF9saW5lYXIgIT0gb2pfdGVzdCRQdXJjaGFzZSksDQogICAgbWVhbih0ZXN0X3ByZWRfcmFkaWFsICE9IG9qX3Rlc3QkUHVyY2hhc2UpLA0KICAgIG1lYW4odGVzdF9wcmVkX3BvbHkgIT0gb2pfdGVzdCRQdXJjaGFzZSkNCiAgKSkNCnJlc3VsdHNfb2oNCmBgYA0KDQogIC0gT3ZlcmFsbCwgdGhlIHR1bmVkIGxpbmVhciBTVk0gcHJvZHVjZWQgdGhlIGxvd2VzdCB0ZXN0IGVycm9yIHJhdGUsIG91dHBlcmZvcm1pbmcgYm90aCB0aGUgcmFkaWFsIGFuZCBwb2x5bm9taWFsIGtlcm5lbHMuIFRoZSByYWRpYWwgYW5kIHBvbHlub21pYWwga2VybmVscyBoYWQgc2ltaWxhciBwZXJmb3JtYW5jZSwgd2l0aCBzbGlnaHRseSBoaWdoZXIgZXJyb3IgcmF0ZXMgYXJvdW5kIDE4JS4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBhZGRpdGlvbmFsIGNvbXBsZXhpdHkgcHJvdmlkZWQgYnkgdGhlIG5vbi1saW5lYXIga2VybmVscyBkaWQgbm90IGltcHJvdmUgY2xhc3NpZmljYXRpb24gYWNjdXJhY3kgZm9yIHRoZSBPSiBkYXRhc2V0LCBhbmQgYSBzaW1wbGVyIGxpbmVhciBkZWNpc2lvbiBib3VuZGFyeSB3YXMgc3VmZmljaWVudC4NCg==