Задача 10
Податоците од задачата се:
library(ISLR)
Weekly
(a)
Од нумеричката анализа забележуваме дека прирастот за 1, 2, 3, 4 и 5 недели веројатно има слична дистибуција (Lag1, Lag2, Lag3, Lag4, Lag5). Истото може да се забележи и за Today.
Поголем дел од прирастот е позитивен (просечниот и среден прираст за сите колони е над 0), што може да се објасни со тоа дека пазарот воглавно растел во 20те години кои се разгледани тука. Истото може и да се забележи со тоа што има повеќе Up од Down појавувања (55% се Up).
summary(Weekly)
Year Lag1 Lag2 Lag3
Min. :1990 Min. :-18.1950 Min. :-18.1950 Min. :-18.1950
1st Qu.:1995 1st Qu.: -1.1540 1st Qu.: -1.1540 1st Qu.: -1.1580
Median :2000 Median : 0.2410 Median : 0.2410 Median : 0.2410
Mean :2000 Mean : 0.1506 Mean : 0.1511 Mean : 0.1472
3rd Qu.:2005 3rd Qu.: 1.4050 3rd Qu.: 1.4090 3rd Qu.: 1.4090
Max. :2010 Max. : 12.0260 Max. : 12.0260 Max. : 12.0260
Lag4 Lag5 Volume Today
Min. :-18.1950 Min. :-18.1950 Min. :0.08747 Min. :-18.1950
1st Qu.: -1.1580 1st Qu.: -1.1660 1st Qu.:0.33202 1st Qu.: -1.1540
Median : 0.2380 Median : 0.2340 Median :1.00268 Median : 0.2410
Mean : 0.1458 Mean : 0.1399 Mean :1.57462 Mean : 0.1499
3rd Qu.: 1.4090 3rd Qu.: 1.4050 3rd Qu.:2.05373 3rd Qu.: 1.4050
Max. : 12.0260 Max. : 12.0260 Max. :9.32821 Max. : 12.0260
Direction
Down:484
Up :605
Кога графички ќе ги претставиме податоците, забележуваме дека накај 950-от примерок се зголемува дисперзијата на прирастот. Ова е периодот околу 2008 година, кога се случи една од поголемите економски кризи.
plot(Weekly$Lag1, type="l", col="green", xlab="return", ylab="index")

plot(Weekly$Lag2, type="l", col="green", xlab="return", ylab="index")

plot(Weekly$Lag3, type="l", col="green", xlab="return", ylab="index")

plot(Weekly$Lag4, type="l", col="green", xlab="return", ylab="index")

plot(Weekly$Lag5, type="l", col="green", xlab="return", ylab="index")

(b)
Интерцептот е стратистички најзначаен. Неговата позитивна вредност укажува дека насоката има тенденција да е Up.
Lag2 е донекаде значајно, иако не премногу (сепак, p-вредноста не е претерано ниска). Ова укажува дека ако прирастот пред две недели бил позитивен, можеме да имаме некоја сигурност дека пазарот ќе се движи нагоре.
logistic_model = glm(
Direction ∼ Lag1 + Lag2 + Lag3 + Lag4 + Lag5 + Volume,
data = Weekly,
family = binomial
)
summary(logistic_model)
Call:
glm(formula = Direction ~ Lag1 + Lag2 + Lag3 + Lag4 + Lag5 +
Volume, family = binomial, data = Weekly)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.6949 -1.2565 0.9913 1.0849 1.4579
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 0.26686 0.08593 3.106 0.0019 **
Lag1 -0.04127 0.02641 -1.563 0.1181
Lag2 0.05844 0.02686 2.175 0.0296 *
Lag3 -0.01606 0.02666 -0.602 0.5469
Lag4 -0.02779 0.02646 -1.050 0.2937
Lag5 -0.01447 0.02638 -0.549 0.5833
Volume -0.02274 0.03690 -0.616 0.5377
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1496.2 on 1088 degrees of freedom
Residual deviance: 1486.4 on 1082 degrees of freedom
AIC: 1500.4
Number of Fisher Scoring iterations: 4
(c)
Можеме да забележиме дека моделот инклинира повеќе кон предикции од типот Up. Од 484 примероци каде што насоката била Down, логистичкиот модел 430 пати направил false negative за Down класата и предвидел Up.
accuracy на моделот е 56.1%.
probas = predict(logistic_model, type = "response")
preds = rep("Down", length(probas))
preds[probas > 0.5] = "Up"
trues = Weekly$Direction
table(preds, trues)
trues
preds Down Up
Down 54 48
Up 430 557
mean(preds == trues)
[1] 0.5610652
(d) (e) (f) (g) (h)
Ја правиме поделбата на тренирачко и тестирачко множество:
train_idxs = (Weekly$Year <= 2008)
train_weekly = Weekly[train_idxs, ]
test_weekly = Weekly[!train_idxs, ]
train_weekly
test_weekly
Можеме да ги направиме следниве заклучови за тестовите:
- логистичка регресија и LDA вадат исти резултати и се најдобри
- QDA учи скоро секогаш да вели Up, па е најлош од сите модели
- KNN со К=1 го има over-fittnato тренирачкото множество и не вади подобро од рандом
library(MASS)
library(class)
set.seed(1337)
get_conf_matrix <- function(name, preds, trues) {
print(paste("Conf. Matrix for", name, sep=" "))
print(table(preds, trues))
print(paste("Accuracy for", name, sep=" "))
mean(preds == trues)
}
get_logistic_conf_matrix <- function(name, model, data, trues) {
probas = predict(model, data, type="response")
preds = rep("Down", length(probas))
preds[probas > 0.5] = "Up"
get_conf_matrix(name, preds, trues)
}
get_da_conf_matrix <- function(name, model, data, trues) {
preds = predict(model, data)$class
get_conf_matrix(name, preds, trues)
}
get_knn_conf_matrix <- function(name, model, data, trues) {
get_conf_matrix(name, model, trues)
}
trues = test_weekly$Direction
logistic_model = glm(Direction ∼ Lag2, data=train_weekly, family=binomial)
lda_model = lda(Direction ∼ Lag2, data=train_weekly)
qda_model = qda(Direction ∼ Lag2, data=train_weekly)
knn_model = knn(
data.frame(train_weekly$Lag2),
data.frame(test_weekly$Lag2),
train_weekly$Direction,
k=1
)
get_logistic_conf_matrix(
"logistic regression",
logistic_model,
test_weekly,
trues
)
[1] "Conf. Matrix for logistic regression"
trues
preds Down Up
Down 9 5
Up 34 56
[1] "Accuracy for logistic regression"
[1] 0.625
get_da_conf_matrix("LDA", lda_model, test_weekly, trues)
[1] "Conf. Matrix for LDA"
trues
preds Down Up
Down 9 5
Up 34 56
[1] "Accuracy for LDA"
[1] 0.625
get_da_conf_matrix("QDA", qda_model, test_weekly, trues)
[1] "Conf. Matrix for QDA"
trues
preds Down Up
Down 0 0
Up 43 61
[1] "Accuracy for QDA"
[1] 0.5865385
get_knn_conf_matrix("KNN 1", knn_model, test_weekly, trues)
[1] "Conf. Matrix for KNN 1"
trues
preds Down Up
Down 21 29
Up 22 32
[1] "Accuracy for KNN 1"
[1] 0.5096154
(i)
Ги испробуваме сите можни комбинации на предиктори (2^6). За секое подмножество испорбуваме логистичка регресија, LDA, QDA и 200 КНН класификатори. Вкупно се испробани 2^6 * (200 + 3) = 12992 модели во околу 2 минути. Од нив, највисока точност има KNN со К=122, а точноста изнесува 66.35%, користејќи го предикторот Lag1.
set.seed(1337)
predictors = c("Lag1", "Lag2", "Lag3", "Lag4", "Lag5", "Volume")
get_logistic_model_acc <- function(formula, train_data, test_data) {
model = glm(as.formula(formula), data=train_data, family=binomial)
probas = predict(model, test_data, type="response")
preds = rep("Down", length(probas))
preds[probas > 0.5] = "Up"
return(mean(preds == trues))
}
get_da_model_acc <- function(formula, train_data, test_data, da) {
model = da(as.formula(formula), data=train_data)
preds = predict(model, test_data)$class
return(mean(preds == trues))
}
get_knn_model_acc <- function(predictors, train_data, test_data, k) {
preds = knn(
train_data[predictors],
test_data[predictors],
train_data$Direction,
k=k
)
return(mean(preds == trues))
}
best_model_acc = 0
best_model_formula = ""
best_model_type = ""
best_model_k = ""
for (num_predictors in 1:length(predictors)) {
predictor_combinations = combn(predictors, num_predictors)
num_combinations = dim(predictor_combinations)[2]
for (combination_idx in 1:num_combinations) {
formula = paste(
"Direction~",
paste(predictor_combinations[, combination_idx], collapse="+"),
sep=""
)
logistic_acc = get_logistic_model_acc(formula, train_weekly, test_weekly)
if (logistic_acc > best_model_acc) {
best_model_acc = logistic_acc
best_model_formula = formula
best_model_type = "logistic"
}
lda_acc = get_da_model_acc(formula, train_weekly, test_weekly, lda)
if (lda_acc > best_model_acc) {
best_model_acc = lda_acc
best_model_formula = formula
best_model_type = "LDA"
}
qda_acc = get_da_model_acc(formula, train_weekly, test_weekly, qda)
if (qda_acc > best_model_acc) {
best_model_acc = qda_acc
best_model_formula = formula
best_model_type = "QDA"
}
for (k in 1:200) {
knn_acc = get_knn_model_acc(predictors, train_weekly, test_weekly, k)
if (knn_acc > best_model_acc) {
best_model_acc = knn_acc
best_model_formula = formula
best_model_type = "KNN"
best_model_k = k
}
}
}
}
print(paste("Best model: ", best_model_type))
[1] "Best model: KNN"
print(paste("Acc: ", best_model_acc))
[1] "Acc: 0.663461538461538"
print(paste("Formula: ", best_model_formula))
[1] "Formula: Direction~Lag1"
if (best_model_type == "KNN") {
print(paste("K=", best_model_k))
}
[1] "K= 122"
Задача 11
Податоците од задачата се:
Auto
(a)
median_mpg = median(Auto$mpg)
mpg0 = rep(0, length(Auto$mpg))
mpg0[Auto$mpg > median_mpg] = 1
Auto$mpg0 = mpg0
Auto
(b)
Од boxplot-овите можеме да забележиме дека year и acceleration нема да бидат добри предиктори на mpg0. Останатите релативно добра сепарација прават помеѓу двете класи на mpg0. Најдобри се чинат weight, displacement и cylinders.
for (col in colnames(Auto)) {
if (col == "mpg" || col == "mpg0" || col == "name") {
next
}
boxplot(as.formula(paste(col, "~ mpg0")), data=Auto)
}







(c)
Правиме рандом поделба 80 - 20.
split = round(length(Auto$mpg) * 0.8)
idxs = sample(1:length(Auto$mpg))
train_idxs = idxs[1:split]
test_idxs = idxs[(split + 1):length(Auto$mpg)]
train_auto = Auto[train_idxs,]
test_auto = Auto[test_idxs,]
train_auto
test_auto
(d) (e) (f) (g)
set.seed(1337)
logistic_model = glm(
mpg0 ~ weight + displacement + cylinders,
data=train_auto,
family=binomial
)
probas = predict(logistic_model, test_auto, type="response")
preds = rep(0, length(probas))
preds[probas > 0.5] = 1
print(
paste(
"Logistic regression error rate:",
1 - mean(preds == test_auto$mpg0),
sep=" "
)
)
[1] "Logistic regression error rate: 0.0897435897435898"
lda_model = lda(mpg0 ~ weight + displacement + cylinders, data=train_auto)
preds = predict(lda_model, test_auto)$class
print(paste("LDA error rate:", 1 - mean(preds == test_auto$mpg0), sep=" "))
[1] "LDA error rate: 0.0769230769230769"
qda_model = qda(mpg0 ~ weight + displacement + cylinders, data=train_auto)
preds = predict(qda_model, test_auto)$class
print(paste("QDA error rate:", 1 - mean(preds == test_auto$mpg0), sep=" "))
[1] "QDA error rate: 0.0641025641025641"
best_knn_k = 0
best_knn_acc = 0
for (k in 1:300) {
preds = knn(
train_auto[c("weight", "displacement", "cylinders")],
test_auto[c("weight", "displacement", "cylinders")],
train_auto$mpg0,
k=k
)
acc = mean(preds == test_auto$mpg0)
if (acc > best_knn_acc) {
best_knn_acc = acc
best_knn_k = k
}
}
print(paste(best_knn_k, "KNN error rate:", 1 - best_knn_acc, sep=" "))
[1] "5 KNN error rate: 0.0641025641025641"
Задача 13
Ја додаваме медијаната:
median_crim = median(Boston$crim)
crim0 = rep(0, length(Boston$crim))
crim0[Boston$crim > median_crim] = 1
Boston$crim0 = crim0
Boston
Делиме на тренирачко и тестирачко множество:
split = round(length(Boston$crim0) * 0.8)
idxs = sample(1:length(Boston$crim0))
train_idxs = idxs[1:split]
test_idxs = idxs[(split + 1):length(Boston$crim0)]
train_boston = Boston[train_idxs,]
test_boston = Boston[test_idxs,]
train_boston
test_boston
Ги испробуваме сите можни комбинации на предиктори (2^13). За секое подмножество испробуваме логистичка регресија, LDA, QDA и 20 КНН класификатори. Вкупно се испробани 2^13 * (20 + 3) = 188416 модели во околу 7 минути. Од нив, највисока точност има логистичката регресија, а точноста изнесува 97.03%, користејќи ги предикторите indus, nox, rm, age, dis, rad, ptratio, black, lstat и medv.
Би испробале и повеќе опции за K, но времето на извршување расте многу (накај 3 часа би било за сите К од 1 до 200). За тоа нема потреба, бидејќи точноста и вака е доволно висока.
set.seed(1337)
predictors = c(
"zn", "indus", "chas", "nox", "rm", "age", "dis", "rad", "tax",
"ptratio", "black", "lstat", "medv"
)
trues = test_boston$crim0
get_logistic_model_acc <- function(formula, train_data, test_data) {
model = glm(as.formula(formula), data=train_data, family=binomial)
probas = predict(model, test_data, type="response")
preds = rep(0, length(probas))
preds[probas > 0.5] = 1
return(mean(preds == trues))
}
get_da_model_acc <- function(formula, train_data, test_data, da) {
model = da(as.formula(formula), data=train_data)
preds = predict(model, test_data)$class
return(mean(preds == trues))
}
get_knn_model_acc <- function(predictors, train_data, test_data, k) {
preds = knn(
train_data[predictors],
test_data[predictors],
train_data$crim0,
k=k
)
return(mean(preds == trues))
}
best_model_acc = 0
best_model_formula = ""
best_model_type = ""
best_model_k = ""
for (num_predictors in 1:length(predictors)) {
predictor_combinations = combn(predictors, num_predictors)
num_combinations = dim(predictor_combinations)[2]
for (combination_idx in 1:num_combinations) {
formula = paste(
"crim0~",
paste(predictor_combinations[, combination_idx], collapse="+"),
sep=""
)
logistic_acc = get_logistic_model_acc(formula, train_boston, test_boston)
if (logistic_acc > best_model_acc) {
best_model_acc = logistic_acc
best_model_formula = formula
best_model_type = "logistic"
}
lda_acc = get_da_model_acc(formula, train_boston, test_boston, lda)
if (lda_acc > best_model_acc) {
best_model_acc = lda_acc
best_model_formula = formula
best_model_type = "LDA"
}
qda_acc = get_da_model_acc(formula, train_boston, test_boston, qda)
if (qda_acc > best_model_acc) {
best_model_acc = qda_acc
best_model_formula = formula
best_model_type = "QDA"
}
for (k in 1:20) {
knn_acc = get_knn_model_acc(predictors, train_boston, test_boston, k)
if (knn_acc > best_model_acc) {
best_model_acc = knn_acc
best_model_formula = formula
best_model_type = "KNN"
best_model_k = k
}
}
}
}
print(paste("Best model: ", best_model_type))
[1] "Best model: logistic"
print(paste("Acc: ", best_model_acc))
[1] "Acc: 0.97029702970297"
print(paste("Formula: ", best_model_formula))
[1] "Formula: crim0~indus+nox+rm+age+dis+rad+ptratio+black+lstat+medv"
if (best_model_type == "KNN") {
print(paste("K:", best_model_k))
}
LS0tDQp0aXRsZTogIkNsYXNzaWZpY2F0aW9uIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoTUFTUykNCmxpYnJhcnkoSVNMUikNCmBgYA0KDQojINCX0LDQtNCw0YfQsCAxDQoNCiQkDQpwKFgpID0gXGZyYWN7ZV57XGJldGFfMCtcYmV0YV8xWH19ezErZV57XGJldGFfMCtcYmV0YV8xWH19XFwNCjEtcCh4KSA9IDEgLSBcZnJhY3tlXntcYmV0YV8wK1xiZXRhXzFYfX17MStlXntcYmV0YV8wK1xiZXRhXzFYfX0gPSBcZnJhY3sxK2Vee1xiZXRhXzArXGJldGFfMVh9LWVee1xiZXRhXzArXGJldGFfMVh9fXsxK2Vee1xiZXRhXzArXGJldGFfMVh9fT1cZnJhY3sxfXsxK2Vee1xiZXRhXzArXGJldGFfMVh9fVxcDQpcZnJhY3twKFgpfXsxLXAoeCl9PVxmcmFje2Vee1xiZXRhXzArXGJldGFfMVh9fXsxK2Vee1xiZXRhXzArXGJldGFfMVh9fSAqIFxmcmFjezErZV57XGJldGFfMCtcYmV0YV8xWH19ezF9PWVee1xiZXRhXzArXGJldGFfMVh9DQokJA0KDQojINCX0LDQtNCw0YfQsCAyDQoNCtCY0LzQtdC90LjRgtC10LvQvtGCINC80L7QttC10LzQtSDRgdC70L7QsdC+0LTQvdC+INC00LAg0LPQviDQuNCz0L3QvtGA0LjRgNCw0LzQtSwg0LHQuNC00LXRmNGc0Lgg0LUg0LjRgdGCINC30LAg0YHQtdC60L7RmNCwINC60LvQsNGB0LAuINCS0LXQtNC90LDRiCDRnNC1INC/0YDQtdC80LjQvdC10LzQtSDQvdCwINC30LDQv9C40YjRg9Cy0LDRmtC1INC90LAg0L7RgdGC0LDRgtC+0LrQvtGCINC+0LQg0LjQt9GA0LDQt9C+0YIg0LLQviDQu9C+0LPQsNGA0LjRgtCw0LzRgdC60LAg0YTQvtGA0LzQsC4g0JjQt9GA0LDQt9C40YLQtSDQkCwgQiwgQywgLi4uINC40LzQsNCw0YIg0LzQsNC60YHQuNC80YPQvCDQt9CwINC40YHRgtC+INC6ICjQtNC+0LHQuNC10L3QuCDRgdC1INGB0L4g0L7RgtGB0YLRgNCw0L3Rg9Cy0LDRmtC1INC90LAg0YHQvtCx0LjRgNC+0YbQuNGC0LUg0LLQviDQutC+0Lgg0L3QtSDRgdC1INC/0L7RmNCw0LLRg9Cy0LAg0LopLiDQn9GA0LXRgtC/0L7RgdGC0LDQstGD0LLQsNC80LUg0LTQtdC60LAg0YDQsNCx0L7RgtC40LzQtSDRgdC+INC/0YDQuNGA0L7QtNC10L0g0LvQvtCz0LDRgNC40YLQsNC8INGCLtC1LiBsb2coZSkgPSAxLg0KDQokJA0KQT1sb2coXHBpX2spLWxvZyhcc3FydHsyXHBpXHNpZ21hfSktKFxmcmFjeyh4LVxtdV9rKV4yfXsyXHNpZ21hXjJ9KWxvZyhlKVxcDQpCPWxvZyhccGlfayktXGZyYWN7KHgtXG11X2spXjJ9ezJcc2lnbWFeMn09bG9nKFxwaV9rKS1cZnJhY3t4XjIrMnhcbXVfay1cbXVfa14yfXsyXHNpZ21hXjJ9XFwNCkM9bG9nKFxwaV9rKS1cZnJhY3t4XG11X2t9e1xzaWdtYV4yfStcZnJhY3tcbXVfa14yfXsyXHNpZ21hXjJ9PVxkZWx0YV9rKHgpDQokJA0KDQojINCX0LDQtNCw0YfQsCAzDQoNCtCh0LvQuNGH0L3QviDQvdCwINC30LDQtNCw0YfQsCAyLCDQuNC30LLQtdC00YPQstCw0LzQtSDQtNC10LvRgtCwINC6INC30LAgeCwg0L3QviDQvtCy0L7RmCDQv9Cw0YIg0YHQviDRgNCw0LfQu9C40YfQvdCwINGB0LjQs9C80LAg0LfQsCDRgdC10LrQvtGY0LAg0LrQu9Cw0YHQsC4g0J7QtCDRgtC+0LAg0YjRgtC+IHgg0L3QsCDQutCy0LDQtNGA0LDRgiDRhNC40LPRg9GA0LjRgNCwINCy0L4g0LTQtdC70YLQsCDQuiDQt9CwIHgsINGB0LvQtdC00Lgg0LTQtdC60LAg0LHQsNGY0LXRgdC+0LLQuNC+0YIg0LrQu9Cw0YHQuNGE0LjQutCw0YLQvtGAINC1INC60LLQsNC00YDQsNGC0LXQvS4NCg0KJCQNCkE9bG9nKFxwaV9rKS1sb2coXHNxcnR7MlxwaVxzaWdtYV9rfSktKFxmcmFjeyh4LVxtdV9rKV4yfXsyXHNpZ21hX2teMn0pbG9nKGUpXFwNCkI9bG9nKFxwaV9rKS1sb2coXHNxcnR7MlxwaVxzaWdtYV9rfSktXGZyYWN7KHgtXG11X2spXjJ9ezJcc2lnbWFfa14yfT1sb2coXHBpX2spLWxvZyhcc3FydHsyXHBpXHNpZ21hX2t9KS1cZnJhY3t4XjIrMnhcbXVfay1cbXVfa14yfXsyXHNpZ21hX2teMn1cXA0KQz1sb2coXHBpX2spbG9nKFxzcXJ0ezJccGlcc2lnbWFfa30pLVxmcmFje3heMn17MlxzaWdtYV9rXjJ9LVxmcmFje3hcbXVfa317XHNpZ21hX2teMn0rXGZyYWN7XG11X2teMn17MlxzaWdtYV9rXjJ9PVxkZWx0YV9rKHgpDQokJA0KDQojINCX0LDQtNCw0YfQsCA0DQoNCiMjIyAoYSkNCg0KJCQNCjJcaW50X3swfV57MC4wNX0oeCArIDAuMDUpZHggKyBcaW50X3swLjA1fV57MC45NX0wLjFkeD0yXGludF97MH1eezAuMDV9eGR4ICsgXGludF97MH1eezAuMDV9MC4xZHggKyBcaW50X3swLjA1fV57MC45NX0wLjFkeD1cXA0KMC4wNV4yKzAuMDk1PTAuMDk3NSA9IDkuNzVcJQ0KJCQNCg0KIyMjIChiKQ0KDQrQoNC10YjQtdC90LjQtdGC0L4g0LUg0LTQstC+0LXQvSDQuNC90YLQtdCz0YDQsNC7INC90LAg0LTQtdC70L7QstC4ICjRgdC70LjRh9C90L4g0LrQsNC60L4g0L/QvtC0INCwKSwg0YHQviDRgtC+0LAg0YjRgtC+INCy0L3QsNGC0YDQtdGI0L3QuNC+0YIg0LjQvdGC0LXQs9GA0LDQuyDQtSDQs9C+0YDQvdC40L7RgiDRgNC10LfRg9C70YLQsNGCLiDQl9CwINC/0L7QtdC00L3QvtGB0YLQsNCy0LXQvSDQt9Cw0L/QuNGBLCDQstC90LDRgtGA0LXRiNC90LjQvtGCINC40L3RgtC10LPRgNCw0Lsg0ZzQtSDQs9C+INGC0YDQtdGC0LjRgNCw0LzQtSDQutCw0LrQviDQv9GA0LXRgdC80LXRgtCw0L0uDQoNCiQkDQoyXGludF97MH1eezAuMDV9KHggKyAwLjA1KSAqIDAuMDk3NWR4ICsgXGludF97MC4wNX1eezAuOTV9MC4xICogMC4wOTc1ZHg9MlxpbnRfezB9XnswLjA1fXhkeCArIFxpbnRfezB9XnswLjA1fTAuMWR4ICsgXGludF97MC4wNX1eezAuOTV9MC4xZHg9XFwNCjAuMDk3NSAqICgwLjA1XjIrMC4wOTUpPTAuMDk3NV4yID0gMC4wMDk1MDYyNSA9IDAuOTUwNjI1XCUNCiQkDQoNCiMjIyAoYykNCg0K0KjQtdC80LDRgtCwINC1INC/0YDQvtGB0YLQsC4g0KHQviDRgdC10LrQvtC1INC90LDQs9C+0LvQtdC80YPQstCw0ZrQtSDQvdCwINC00LjQvNC10L3Qt9C40ZjQsNGC0LAsINC/0YDQvtGG0LXQvdGC0L7RgiDRgdC1INC60LLQsNC00YDQuNGA0LAuINCX0LAgMTAwINC00L7QsdC40LLQsNC80LU6DQoNCiQkDQowLjA5NzVeezJeezk5fX0gXGFwcHJveCAwDQokJA0KDQojIyMgKGQpDQoNCtCe0YfQuNCz0LvQtdC00L3QviDQtSDQtNC10LrQsCDRgdC+INC90LDQs9C+0LvQtdC80YPQstCw0ZrQtSDQvdCwINC00LjQvNC10L3Qt9C40LjRgtC1LCDQtNCy0L7RmNC90L4g0LXQutGB0L/QvtC90LXQvdGG0LjRmNCw0LvQvdC+INGA0LDRgdGC0LUg0LHRgNC+0ZjQvtGCINC90LAg0L3QtdC40YHQutC+0YDQuNGB0YLQtdC90Lgg0L/RgNC40LzQtdGA0L7RhtC4INC30LAg0LrQu9Cw0YHQuNGE0LjQutCw0YbQuNGY0LAuINCc0L3QvtCz0YMg0LHRgNC30L4g0LHRgNC+0ZjQvtGCINC90LAg0LHQu9C40YHQutC4INC/0YDQtdC80LXRgNC+0YbQuCDRgdGC0LDQvdGD0LLQsCDQvNC90L7Qs9GDINC90LjQt9C+0LouDQoNCiMjIyAoZSkNCg0K0JfQsCBwPTEsIDEwJSDQvtC0INC00L7Qu9C20LjQvdCw0YLQsCDQvdCwINC70LjQvdC40ZjQsNGC0LAg0LUgMC4xLCDQv9CwINGB0YLRgNCw0L3QsNGC0LAg0L3QsCDRhdC40L/QtdGA0LrQvtGG0LrQsNGC0LAg0YHQviDQtNC40LzQtdC90LfQuNGY0LAgMSDQtSAwLjEuDQoNCtCX0LAgcD0yLCAxMCUg0L7QtCDQv9C70L7RiNGC0LjQvdCw0YLQsCDQvdCwINC60LLQsNC00YDQsNGC0L7RgiAwLjEsINC/0LAg0YHRgtGA0LDQvdCw0YLQsCDQvdCwINGF0LjQv9C10YDQutC+0YbQutCw0YLQsCDQtSBzcXJ0KDAuMSkgPSAwLjMxNjIyNzgNCg0K0JfQsCBwPTEwMCwgMTAlINC+0LQg0LzQtdGA0LDRgtCwINC90LAg0YXQuNC/0LXRgNC/0L7QstGA0YjQuNC90LDRgtCwINC1IDAuMSwg0L/QsCDRgdGC0YDQsNC90LDRgtCwINC90LAg0YXQuNC/0LXRgNC60L7RhtC60LDRgtCwINC1INCwLCDQutCw0LTQtSDRiNGC0L4g0LBeMTAwPTAuMS4NCg0KIyDQl9Cw0LTQsNGH0LAgNQ0KDQojIyMgKGEpDQoNCkxEQSwg0Lgg0L3QsCDRgtGA0LXQvdC40YDQsNGH0LrQvtGC0L4g0Lgg0L3QsCDRgtC10YHRgtC40YDQsNGH0LrQvtGC0L4g0LzQvdC+0LbQtdGB0YLQstC+Lg0KDQojIyMgKGIpDQoNClFEQSwg0Lgg0L3QsCDRgtGA0LXQvdC40YDQsNGH0LrQvtGC0L4g0Lgg0L3QsCDRgtC10YHRgtC40YDQsNGH0LrQvtGC0L4g0LzQvdC+0LbQtdGB0YLQstC+Lg0KDQojIyMgKGMpDQoNCtCS0L4g0L7Qv9GI0YIg0YHQu9GD0YfQsNGYLCBRREEg0YLRgNC10LHQsCDQtNCwINGB0YLQsNC90YPQstCwINC/0L7QtNC+0LHRgNC+INGB0L4g0LfQs9C+0LvQtdC80YPQstCw0ZrQtSDQvdCwINCx0YDQvtGY0L7RgiDQvdCwINC/0YDQuNC80LXRgNC+0YbQuCwg0LHQuNC00LXRmNGc0Lgg0ZzQtSDRgdC1INC90LDQvNCw0LvRg9Cy0LAg0LPRgNC10YjQutCw0YLQsCDQv9C+0YDQsNC00Lgg0YHRgtC10L/QtdC90L7RgiDQvdCwIG92ZXItZml0dGluZyDQvdCwIFFEQSwg0LTQvtC00LXQutCwINCz0YDQtdGI0LrQsNGC0LAg0L/QvtGA0LDQtNC4INGB0YLQtdC/0LXQvdC+0YIg0L3QsCB1bmRlci1maXR0aW5nINC90LAgTERBINC90LXQvNCwINC80L3QvtCz0YMg0LTQsCDRgdC1INC/0YDQvtC80LXQvdC4LiDQldC00LjQvdGB0YLQstC10L3QviDQutC+0LPQsCDQsdCw0ZjQtdGB0L7QstCw0YLQsCBkZWNpc2lvbiBib3VuZGFyeSDQtSDQu9C40L3QtdCw0YDQvdCwLCDQvtC00LPQvtCy0L7RgNC+0YIg0ZzQtSDQsdC40LTQtSDQv9C+0LjQvdCw0LrQvtCyLg0KDQojIyMgKGQpDQoNCtCd0LUuIFFEQSDQstC+INC90LDRmNC00L7QsdCw0YAg0YHQu9GD0YfQsNGYINGc0LUg0L3QsNC/0YDQsNCy0Lggb3Zlci1maXR0aW5nINC90LAg0YLRgNC10L3QuNGA0LDRh9C60L7RgtC+INC80L3QvtC20LXRgdGC0LLQvi4NCg0KIyDQl9Cw0LTQsNGH0LAgNg0KDQojIyMgKGEpDQoNCmBgYHtyfQ0KZSA9IGV4cCgtNiArIDAuMDUgKiA0MCArIDEgKiAzLjUpDQpwcm9iYWJpbGl0eSA9IGUgLyAoMSArIGUpDQpwcmludChwcm9iYWJpbGl0eSkNCmBgYA0KDQojIyMgKGIpDQoNCmBgYHtyfQ0KZGVzaXJlZExvZ09kZHMgPSAxDQpjdXJyZW50TG9nT2RkcyA9IHByb2JhYmlsaXR5IC8gKDEgLSBwcm9iYWJpbGl0eSkNCmFkZGl0aW9uYWxIb3VycyA9IChkZXNpcmVkTG9nT2RkcyAtIGN1cnJlbnRMb2dPZGRzKSAvIDAuMDUNCnByaW50KGFkZGl0aW9uYWxIb3VycykNCmBgYA0KIyDQl9Cw0LTQsNGH0LAgNw0KDQpgYGB7cn0NCm1lYW5zID0gYygxMCwgMCkNCnNkcyA9IGMoNiwgNikNCnBkZnNUaW1lc1ByaW9ycyA9IGMoMCwgMCkNCnByaW9ycyA9IGMoMC44LCAwLjIpDQpmb3IgKGsgaW4gMToyKSB7DQogIHBkZnNUaW1lc1ByaW9yc1trXSA9IHByaW9yc1trXSAqIHBub3JtKDQsIG1lYW5zW2tdLCBzZHNba10pDQp9DQpwcmludChwZGZzVGltZXNQcmlvcnNbMV0gLyBzdW0ocGRmc1RpbWVzUHJpb3JzKSkNCmBgYA0KDQojINCX0LDQtNCw0YfQsCA4DQoNCtCR0LjQtNC10ZjRnNC4INC60L7RgNC40YHRgtC40LzQtSBLTk4g0YHQviDQuj0xLCBlcnJvciByYXRlINC90LAg0YLRgNC10L3QuNGA0LDRh9C60L7RgtC+INC80L3QvtC20LXRgdGC0LLQviDRnNC1INC90Lgg0LHQuNC00LUgMCAo0L3QsCDRgdC10LrQvtGY0LAg0YLQvtGH0LrQsCwg0L3QsNGY0LHQu9C40YHQutCw0YLQsCDQtSDRgdCw0LzQsNGC0LAg0YLQsNCwINC4INGB0LDQvNC+INGC0LDQsCDRmNCwINC30LXQvNCw0LzQtSDQv9GA0LXQtNCy0LjQtCkuINCR0LjQtNC10ZjRnNC4INC/0YDQvtGB0LXQutC+0YIg0LUgMTglIGVycm9yIHJhdGUsIGVycm9yIHJhdGUg0L3QsCDRgtC10YHRgtC40YDQsNGH0LrQvtGC0L4g0LzQvdC+0LbQtdGB0YLQstC+INC1IDM2JSwg0YjRgtC+INC1INC/0L7Qu9C+0YjQviDQvtC0IDMwJSDQvdCwINC70L7Qs9C40YHRgtC40YfQutCwINGA0LXQs9GA0LXRgdC40ZjQsC4g0JHQuNGA0LDQvNC1INC70L7Qs9C40YHRgtC40YfQutCwINGA0LXQs9GA0LXRgdC40ZjQsC4NCg0KIyDQl9Cw0LTQsNGH0LAgOQ0KDQojIyMgKGEpDQoNCmBgYHtyfQ0Kb2RkcyA9IDAuMzcNCnByaW50KG9kZHMgLyAoMSArIG9kZHMpKQ0KYGBgDQoNCiMjIyAoYikNCg0KYGBge3J9DQpwID0gMC4xNg0KcHJpbnQocCAvICgxIC0gcCkpDQpgYGANCg0KIyDQl9Cw0LTQsNGH0LAgMTANCg0K0J/QvtC00LDRgtC+0YbQuNGC0LUg0L7QtCDQt9Cw0LTQsNGH0LDRgtCwINGB0LU6DQoNCmBgYHtyfQ0KbGlicmFyeShJU0xSKQ0KDQpXZWVrbHkNCmBgYA0KDQojIyMgKGEpDQoNCtCe0LQg0L3Rg9C80LXRgNC40YfQutCw0YLQsCDQsNC90LDQu9C40LfQsCDQt9Cw0LHQtdC70LXQttGD0LLQsNC80LUg0LTQtdC60LAg0L/RgNC40YDQsNGB0YLQvtGCINC30LAgMSwgMiwgMywgNCDQuCA1INC90LXQtNC10LvQuCDQstC10YDQvtGY0LDRgtC90L4g0LjQvNCwINGB0LvQuNGH0L3QsCDQtNC40YHRgtC40LHRg9GG0LjRmNCwIChMYWcxLCBMYWcyLCBMYWczLCBMYWc0LCBMYWc1KS4g0JjRgdGC0L7RgtC+INC80L7QttC1INC00LAg0YHQtSDQt9Cw0LHQtdC70LXQttC4INC4INC30LAgVG9kYXkuDQoNCtCf0L7Qs9C+0LvQtdC8INC00LXQuyDQvtC0INC/0YDQuNGA0LDRgdGC0L7RgiDQtSDQv9C+0LfQuNGC0LjQstC10L0gKNC/0YDQvtGB0LXRh9C90LjQvtGCINC4INGB0YDQtdC00LXQvSDQv9GA0LjRgNCw0YHRgiDQt9CwINGB0LjRgtC1INC60L7Qu9C+0L3QuCDQtSDQvdCw0LQgMCksINGI0YLQviDQvNC+0LbQtSDQtNCwINGB0LUg0L7QsdGY0LDRgdC90Lgg0YHQviDRgtC+0LAg0LTQtdC60LAg0L/QsNC30LDRgNC+0YIg0LLQvtCz0LvQsNCy0L3QviDRgNCw0YHRgtC10Lsg0LLQviAyMNGC0LUg0LPQvtC00LjQvdC4INC60L7QuCDRgdC1INGA0LDQt9Cz0LvQtdC00LDQvdC4INGC0YPQutCwLiDQmNGB0YLQvtGC0L4g0LzQvtC20LUg0Lgg0LTQsCDRgdC1INC30LDQsdC10LvQtdC20Lgg0YHQviDRgtC+0LAg0YjRgtC+INC40LzQsCDQv9C+0LLQtdGc0LUgVXAg0L7QtCBEb3duINC/0L7RmNCw0LLRg9Cy0LDRmtCwICg1NSUg0YHQtSBVcCkuDQoNCmBgYHtyfQ0Kc3VtbWFyeShXZWVrbHkpDQpgYGANCg0K0JrQvtCz0LAg0LPRgNCw0YTQuNGH0LrQuCDRnNC1INCz0Lgg0L/RgNC10YLRgdGC0LDQstC40LzQtSDQv9C+0LTQsNGC0L7RhtC40YLQtSwg0LfQsNCx0LXQu9C10LbRg9Cy0LDQvNC1INC00LXQutCwINC90LDQutCw0ZggOTUwLdC+0YIg0L/RgNC40LzQtdGA0L7QuiDRgdC1INC30LPQvtC70LXQvNGD0LLQsCDQtNC40YHQv9C10YDQt9C40ZjQsNGC0LAg0L3QsCDQv9GA0LjRgNCw0YHRgtC+0YIuINCe0LLQsCDQtSDQv9C10YDQuNC+0LTQvtGCINC+0LrQvtC70YMgMjAwOCDQs9C+0LTQuNC90LAsINC60L7Qs9CwINGB0LUg0YHQu9GD0YfQuCDQtdC00L3QsCDQvtC0INC/0L7Qs9C+0LvQtdC80LjRgtC1INC10LrQvtC90L7QvNGB0LrQuCDQutGA0LjQt9C4Lg0KDQpgYGB7cn0NCnBsb3QoV2Vla2x5JExhZzEsIHR5cGU9ImwiLCBjb2w9ImdyZWVuIiwgeGxhYj0icmV0dXJuIiwgeWxhYj0iaW5kZXgiKQ0KcGxvdChXZWVrbHkkTGFnMiwgdHlwZT0ibCIsIGNvbD0iZ3JlZW4iLCB4bGFiPSJyZXR1cm4iLCB5bGFiPSJpbmRleCIpDQpwbG90KFdlZWtseSRMYWczLCB0eXBlPSJsIiwgY29sPSJncmVlbiIsIHhsYWI9InJldHVybiIsIHlsYWI9ImluZGV4IikNCnBsb3QoV2Vla2x5JExhZzQsIHR5cGU9ImwiLCBjb2w9ImdyZWVuIiwgeGxhYj0icmV0dXJuIiwgeWxhYj0iaW5kZXgiKQ0KcGxvdChXZWVrbHkkTGFnNSwgdHlwZT0ibCIsIGNvbD0iZ3JlZW4iLCB4bGFiPSJyZXR1cm4iLCB5bGFiPSJpbmRleCIpDQpgYGANCg0KIyMjIChiKQ0KDQrQmNC90YLQtdGA0YbQtdC/0YLQvtGCINC1INGB0YLRgNCw0YLQuNGB0YLQuNGH0LrQuCDQvdCw0ZjQt9C90LDRh9Cw0LXQvS4g0J3QtdCz0L7QstCw0YLQsCDQv9C+0LfQuNGC0LjQstC90LAg0LLRgNC10LTQvdC+0YHRgiDRg9C60LDQttGD0LLQsCDQtNC10LrQsCDQvdCw0YHQvtC60LDRgtCwINC40LzQsCDRgtC10L3QtNC10L3RhtC40ZjQsCDQtNCwINC1IFVwLg0KDQpMYWcyINC1INC00L7QvdC10LrQsNC00LUg0LfQvdCw0YfQsNGY0L3Qviwg0LjQsNC60L4g0L3QtSDQv9GA0LXQvNC90L7Qs9GDICjRgdC10L/QsNC6LCBwLdCy0YDQtdC00L3QvtGB0YLQsCDQvdC1INC1INC/0YDQtdGC0LXRgNCw0L3QviDQvdC40YHQutCwKS4g0J7QstCwINGD0LrQsNC20YPQstCwINC00LXQutCwINCw0LrQviDQv9GA0LjRgNCw0YHRgtC+0YIg0L/RgNC10LQg0LTQstC1INC90LXQtNC10LvQuCDQsdC40Lsg0L/QvtC30LjRgtC40LLQtdC9LCDQvNC+0LbQtdC80LUg0LTQsCDQuNC80LDQvNC1INC90LXQutC+0ZjQsCDRgdC40LPRg9GA0L3QvtGB0YIg0LTQtdC60LAg0L/QsNC30LDRgNC+0YIg0ZzQtSDRgdC1INC00LLQuNC20Lgg0L3QsNCz0L7RgNC1Lg0KDQpgYGB7cn0NCmxvZ2lzdGljX21vZGVsID0gZ2xtKA0KICBEaXJlY3Rpb24g4oi8IExhZzEgKyBMYWcyICsgTGFnMyArIExhZzQgKyBMYWc1ICsgVm9sdW1lLA0KICBkYXRhID0gV2Vla2x5LA0KICBmYW1pbHkgPSBiaW5vbWlhbCANCikNCnN1bW1hcnkobG9naXN0aWNfbW9kZWwpDQpgYGANCg0KIyMjIChjKQ0KDQrQnNC+0LbQtdC80LUg0LTQsCDQt9Cw0LHQtdC70LXQttC40LzQtSDQtNC10LrQsCDQvNC+0LTQtdC70L7RgiDQuNC90LrQu9C40L3QuNGA0LAg0L/QvtCy0LXRnNC1INC60L7QvSDQv9GA0LXQtNC40LrRhtC40Lgg0L7QtCDRgtC40L/QvtGCIFVwLiDQntC0IDQ4NCDQv9GA0LjQvNC10YDQvtGG0Lgg0LrQsNC00LUg0YjRgtC+INC90LDRgdC+0LrQsNGC0LAg0LHQuNC70LAgRG93biwg0LvQvtCz0LjRgdGC0LjRh9C60LjQvtGCINC80L7QtNC10LsgNDMwINC/0LDRgtC4INC90LDQv9GA0LDQstC40LsgZmFsc2UgbmVnYXRpdmUg0LfQsCBEb3duINC60LvQsNGB0LDRgtCwINC4INC/0YDQtdC00LLQuNC00LXQuyBVcC4NCg0KYWNjdXJhY3kg0L3QsCDQvNC+0LTQtdC70L7RgiDQtSA1Ni4xJS4NCg0KYGBge3J9DQpwcm9iYXMgPSBwcmVkaWN0KGxvZ2lzdGljX21vZGVsLCB0eXBlID0gInJlc3BvbnNlIikNCnByZWRzID0gcmVwKCJEb3duIiwgbGVuZ3RoKHByb2JhcykpDQpwcmVkc1twcm9iYXMgPiAwLjVdID0gIlVwIg0KdHJ1ZXMgPSBXZWVrbHkkRGlyZWN0aW9uDQp0YWJsZShwcmVkcywgdHJ1ZXMpDQptZWFuKHByZWRzID09IHRydWVzKQ0KYGBgDQoNCiMjIyAoZCkgKGUpIChmKSAoZykgKGgpDQoNCtCI0LAg0L/RgNCw0LLQuNC80LUg0L/QvtC00LXQu9Cx0LDRgtCwINC90LAg0YLRgNC10L3QuNGA0LDRh9C60L4g0Lgg0YLQtdGB0YLQuNGA0LDRh9C60L4g0LzQvdC+0LbQtdGB0YLQstC+Og0KDQpgYGB7cn0NCnRyYWluX2lkeHMgPSAoV2Vla2x5JFllYXIgPD0gMjAwOCkNCnRyYWluX3dlZWtseSA9IFdlZWtseVt0cmFpbl9pZHhzLCBdDQp0ZXN0X3dlZWtseSA9IFdlZWtseVshdHJhaW5faWR4cywgXQ0KdHJhaW5fd2Vla2x5DQp0ZXN0X3dlZWtseQ0KYGBgDQoNCtCc0L7QttC10LzQtSDQtNCwINCz0Lgg0L3QsNC/0YDQsNCy0LjQvNC1INGB0LvQtdC00L3QuNCy0LUg0LfQsNC60LvRg9GH0L7QstC4INC30LAg0YLQtdGB0YLQvtCy0LjRgtC1Og0KDQotINC70L7Qs9C40YHRgtC40YfQutCwINGA0LXQs9GA0LXRgdC40ZjQsCDQuCBMREEg0LLQsNC00LDRgiDQuNGB0YLQuCDRgNC10LfRg9C70YLQsNGC0Lgg0Lgg0YHQtSDQvdCw0ZjQtNC+0LHRgNC4DQotIFFEQSDRg9GH0Lgg0YHQutC+0YDQviDRgdC10LrQvtCz0LDRiCDQtNCwINCy0LXQu9C4IFVwLCDQv9CwINC1INC90LDRmNC70L7RiCDQvtC0INGB0LjRgtC1INC80L7QtNC10LvQuA0KLSBLTk4g0YHQviDQmj0xINCz0L4g0LjQvNCwIG92ZXItZml0dG5hdG8g0YLRgNC10L3QuNGA0LDRh9C60L7RgtC+INC80L3QvtC20LXRgdGC0LLQviDQuCDQvdC1INCy0LDQtNC4INC/0L7QtNC+0LHRgNC+INC+0LQg0YDQsNC90LTQvtC8DQoNCmBgYHtyfQ0KbGlicmFyeShNQVNTKQ0KbGlicmFyeShjbGFzcykNCnNldC5zZWVkKDEzMzcpDQoNCmdldF9jb25mX21hdHJpeCA8LSBmdW5jdGlvbihuYW1lLCBwcmVkcywgdHJ1ZXMpIHsNCiAgcHJpbnQocGFzdGUoIkNvbmYuIE1hdHJpeCBmb3IiLCBuYW1lLCBzZXA9IiAiKSkNCiAgcHJpbnQodGFibGUocHJlZHMsIHRydWVzKSkNCiAgcHJpbnQocGFzdGUoIkFjY3VyYWN5IGZvciIsIG5hbWUsIHNlcD0iICIpKQ0KICBtZWFuKHByZWRzID09IHRydWVzKQ0KfQ0KDQpnZXRfbG9naXN0aWNfY29uZl9tYXRyaXggPC0gZnVuY3Rpb24obmFtZSwgbW9kZWwsIGRhdGEsIHRydWVzKSB7DQogIHByb2JhcyA9IHByZWRpY3QobW9kZWwsIGRhdGEsIHR5cGU9InJlc3BvbnNlIikNCiAgcHJlZHMgPSByZXAoIkRvd24iLCBsZW5ndGgocHJvYmFzKSkNCiAgcHJlZHNbcHJvYmFzID4gMC41XSA9ICJVcCINCiAgZ2V0X2NvbmZfbWF0cml4KG5hbWUsIHByZWRzLCB0cnVlcykNCn0NCg0KZ2V0X2RhX2NvbmZfbWF0cml4IDwtIGZ1bmN0aW9uKG5hbWUsIG1vZGVsLCBkYXRhLCB0cnVlcykgew0KICBwcmVkcyA9IHByZWRpY3QobW9kZWwsIGRhdGEpJGNsYXNzDQogIGdldF9jb25mX21hdHJpeChuYW1lLCBwcmVkcywgdHJ1ZXMpDQp9DQoNCmdldF9rbm5fY29uZl9tYXRyaXggPC0gZnVuY3Rpb24obmFtZSwgbW9kZWwsIGRhdGEsIHRydWVzKSB7DQogIGdldF9jb25mX21hdHJpeChuYW1lLCBtb2RlbCwgdHJ1ZXMpDQp9DQoNCnRydWVzID0gdGVzdF93ZWVrbHkkRGlyZWN0aW9uDQoNCmxvZ2lzdGljX21vZGVsID0gZ2xtKERpcmVjdGlvbiDiiLwgTGFnMiwgZGF0YT10cmFpbl93ZWVrbHksIGZhbWlseT1iaW5vbWlhbCkNCmxkYV9tb2RlbCA9IGxkYShEaXJlY3Rpb24g4oi8IExhZzIsIGRhdGE9dHJhaW5fd2Vla2x5KQ0KcWRhX21vZGVsID0gcWRhKERpcmVjdGlvbiDiiLwgTGFnMiwgZGF0YT10cmFpbl93ZWVrbHkpDQprbm5fbW9kZWwgPSBrbm4oDQogIGRhdGEuZnJhbWUodHJhaW5fd2Vla2x5JExhZzIpLCANCiAgZGF0YS5mcmFtZSh0ZXN0X3dlZWtseSRMYWcyKSwgDQogIHRyYWluX3dlZWtseSREaXJlY3Rpb24sIA0KICBrPTENCikNCg0KZ2V0X2xvZ2lzdGljX2NvbmZfbWF0cml4KA0KICAibG9naXN0aWMgcmVncmVzc2lvbiIsIA0KICBsb2dpc3RpY19tb2RlbCwgDQogIHRlc3Rfd2Vla2x5LCANCiAgdHJ1ZXMNCikNCmdldF9kYV9jb25mX21hdHJpeCgiTERBIiwgbGRhX21vZGVsLCB0ZXN0X3dlZWtseSwgdHJ1ZXMpDQpnZXRfZGFfY29uZl9tYXRyaXgoIlFEQSIsIHFkYV9tb2RlbCwgdGVzdF93ZWVrbHksIHRydWVzKQ0KZ2V0X2tubl9jb25mX21hdHJpeCgiS05OIDEiLCBrbm5fbW9kZWwsIHRlc3Rfd2Vla2x5LCB0cnVlcykNCmBgYA0KDQojIyMgKGkpDQoNCtCT0Lgg0LjRgdC/0YDQvtCx0YPQstCw0LzQtSDRgdC40YLQtSDQvNC+0LbQvdC4INC60L7QvNCx0LjQvdCw0YbQuNC4INC90LAg0L/RgNC10LTQuNC60YLQvtGA0LggKDJeNikuINCX0LAg0YHQtdC60L7QtSDQv9C+0LTQvNC90L7QttC10YHRgtCy0L4g0LjRgdC/0L7RgNCx0YPQstCw0LzQtSDQu9C+0LPQuNGB0YLQuNGH0LrQsCDRgNC10LPRgNC10YHQuNGY0LAsIExEQSwgUURBINC4IDIwMCDQmtCd0J0g0LrQu9Cw0YHQuNGE0LjQutCw0YLQvtGA0LguINCS0LrRg9C/0L3QviDRgdC1INC40YHQv9GA0L7QsdCw0L3QuCAyXjYgKiAoMjAwICsgMykgPSAxMjk5MiDQvNC+0LTQtdC70Lgg0LLQviDQvtC60L7Qu9GDIDIg0LzQuNC90YPRgtC4LiDQntC0INC90LjQsiwg0L3QsNGY0LLQuNGB0L7QutCwINGC0L7Rh9C90L7RgdGCINC40LzQsCBLTk4g0YHQviDQmj0xMjIsINCwINGC0L7Rh9C90L7RgdGC0LAg0LjQt9C90LXRgdGD0LLQsCA2Ni4zNSUsINC60L7RgNC40YHRgtC10ZjRnNC4INCz0L4g0L/RgNC10LTQuNC60YLQvtGA0L7RgiBMYWcxLg0KDQpgYGB7cn0NCnNldC5zZWVkKDEzMzcpDQpwcmVkaWN0b3JzID0gYygiTGFnMSIsICJMYWcyIiwgIkxhZzMiLCAiTGFnNCIsICJMYWc1IiwgIlZvbHVtZSIpDQoNCmdldF9sb2dpc3RpY19tb2RlbF9hY2MgPC0gZnVuY3Rpb24oZm9ybXVsYSwgdHJhaW5fZGF0YSwgdGVzdF9kYXRhKSB7DQogIG1vZGVsID0gZ2xtKGFzLmZvcm11bGEoZm9ybXVsYSksIGRhdGE9dHJhaW5fZGF0YSwgZmFtaWx5PWJpbm9taWFsKQ0KICBwcm9iYXMgPSBwcmVkaWN0KG1vZGVsLCB0ZXN0X2RhdGEsIHR5cGU9InJlc3BvbnNlIikNCiAgcHJlZHMgPSByZXAoIkRvd24iLCBsZW5ndGgocHJvYmFzKSkNCiAgcHJlZHNbcHJvYmFzID4gMC41XSA9ICJVcCINCiAgcmV0dXJuKG1lYW4ocHJlZHMgPT0gdHJ1ZXMpKQ0KfQ0KDQpnZXRfZGFfbW9kZWxfYWNjIDwtIGZ1bmN0aW9uKGZvcm11bGEsIHRyYWluX2RhdGEsIHRlc3RfZGF0YSwgZGEpIHsNCiAgbW9kZWwgPSBkYShhcy5mb3JtdWxhKGZvcm11bGEpLCBkYXRhPXRyYWluX2RhdGEpDQogIHByZWRzID0gcHJlZGljdChtb2RlbCwgdGVzdF9kYXRhKSRjbGFzcw0KICByZXR1cm4obWVhbihwcmVkcyA9PSB0cnVlcykpDQp9DQoNCmdldF9rbm5fbW9kZWxfYWNjIDwtIGZ1bmN0aW9uKHByZWRpY3RvcnMsIHRyYWluX2RhdGEsIHRlc3RfZGF0YSwgaykgew0KICBwcmVkcyA9IGtubigNCiAgICB0cmFpbl9kYXRhW3ByZWRpY3RvcnNdLCANCiAgICB0ZXN0X2RhdGFbcHJlZGljdG9yc10sIA0KICAgIHRyYWluX2RhdGEkRGlyZWN0aW9uLCANCiAgICBrPWsNCiAgKQ0KICByZXR1cm4obWVhbihwcmVkcyA9PSB0cnVlcykpDQp9DQoNCmJlc3RfbW9kZWxfYWNjID0gMA0KYmVzdF9tb2RlbF9mb3JtdWxhID0gIiINCmJlc3RfbW9kZWxfdHlwZSA9ICIiDQpiZXN0X21vZGVsX2sgPSAiIg0KDQpmb3IgKG51bV9wcmVkaWN0b3JzIGluIDE6bGVuZ3RoKHByZWRpY3RvcnMpKSB7DQogIHByZWRpY3Rvcl9jb21iaW5hdGlvbnMgPSBjb21ibihwcmVkaWN0b3JzLCBudW1fcHJlZGljdG9ycykNCiAgbnVtX2NvbWJpbmF0aW9ucyA9IGRpbShwcmVkaWN0b3JfY29tYmluYXRpb25zKVsyXQ0KICBmb3IgKGNvbWJpbmF0aW9uX2lkeCBpbiAxOm51bV9jb21iaW5hdGlvbnMpIHsNCiAgICBmb3JtdWxhID0gcGFzdGUoDQogICAgICAiRGlyZWN0aW9ufiIsDQogICAgICBwYXN0ZShwcmVkaWN0b3JfY29tYmluYXRpb25zWywgY29tYmluYXRpb25faWR4XSwgY29sbGFwc2U9IisiKSwNCiAgICAgIHNlcD0iIg0KICAgICkNCiAgICANCiAgICBsb2dpc3RpY19hY2MgPSBnZXRfbG9naXN0aWNfbW9kZWxfYWNjKGZvcm11bGEsIHRyYWluX3dlZWtseSwgdGVzdF93ZWVrbHkpDQogICAgaWYgKGxvZ2lzdGljX2FjYyA+IGJlc3RfbW9kZWxfYWNjKSB7DQogICAgICBiZXN0X21vZGVsX2FjYyA9IGxvZ2lzdGljX2FjYw0KICAgICAgYmVzdF9tb2RlbF9mb3JtdWxhID0gZm9ybXVsYQ0KICAgICAgYmVzdF9tb2RlbF90eXBlID0gImxvZ2lzdGljIg0KICAgIH0NCiAgICANCiAgICBsZGFfYWNjID0gZ2V0X2RhX21vZGVsX2FjYyhmb3JtdWxhLCB0cmFpbl93ZWVrbHksIHRlc3Rfd2Vla2x5LCBsZGEpDQogICAgaWYgKGxkYV9hY2MgPiBiZXN0X21vZGVsX2FjYykgew0KICAgICAgYmVzdF9tb2RlbF9hY2MgPSBsZGFfYWNjDQogICAgICBiZXN0X21vZGVsX2Zvcm11bGEgPSBmb3JtdWxhDQogICAgICBiZXN0X21vZGVsX3R5cGUgPSAiTERBIg0KICAgIH0NCiAgICANCiAgICBxZGFfYWNjID0gZ2V0X2RhX21vZGVsX2FjYyhmb3JtdWxhLCB0cmFpbl93ZWVrbHksIHRlc3Rfd2Vla2x5LCBxZGEpDQogICAgaWYgKHFkYV9hY2MgPiBiZXN0X21vZGVsX2FjYykgew0KICAgICAgYmVzdF9tb2RlbF9hY2MgPSBxZGFfYWNjDQogICAgICBiZXN0X21vZGVsX2Zvcm11bGEgPSBmb3JtdWxhDQogICAgICBiZXN0X21vZGVsX3R5cGUgPSAiUURBIg0KICAgIH0NCiAgICANCiAgICBmb3IgKGsgaW4gMToyMDApIHsNCiAgICAgIGtubl9hY2MgPSBnZXRfa25uX21vZGVsX2FjYyhwcmVkaWN0b3JzLCB0cmFpbl93ZWVrbHksIHRlc3Rfd2Vla2x5LCBrKQ0KICAgICAgaWYgKGtubl9hY2MgPiBiZXN0X21vZGVsX2FjYykgew0KICAgICAgICBiZXN0X21vZGVsX2FjYyA9IGtubl9hY2MNCiAgICAgICAgYmVzdF9tb2RlbF9mb3JtdWxhID0gZm9ybXVsYQ0KICAgICAgICBiZXN0X21vZGVsX3R5cGUgPSAiS05OIg0KICAgICAgICBiZXN0X21vZGVsX2sgPSBrDQogICAgICB9DQogICAgfQ0KICB9DQp9DQoNCnByaW50KHBhc3RlKCJCZXN0IG1vZGVsOiAiLCBiZXN0X21vZGVsX3R5cGUpKQ0KcHJpbnQocGFzdGUoIkFjYzogIiwgYmVzdF9tb2RlbF9hY2MpKQ0KcHJpbnQocGFzdGUoIkZvcm11bGE6ICIsIGJlc3RfbW9kZWxfZm9ybXVsYSkpDQppZiAoYmVzdF9tb2RlbF90eXBlID09ICJLTk4iKSB7DQogIHByaW50KHBhc3RlKCJLPSIsIGJlc3RfbW9kZWxfaykpDQp9DQpgYGANCg0KIyDQl9Cw0LTQsNGH0LAgMTENCg0K0J/QvtC00LDRgtC+0YbQuNGC0LUg0L7QtCDQt9Cw0LTQsNGH0LDRgtCwINGB0LU6DQoNCmBgYHtyfQ0KQXV0bw0KYGBgDQoNCiMjIyAoYSkNCg0KYGBge3J9DQptZWRpYW5fbXBnID0gbWVkaWFuKEF1dG8kbXBnKQ0KbXBnMCA9IHJlcCgwLCBsZW5ndGgoQXV0byRtcGcpKQ0KbXBnMFtBdXRvJG1wZyA+IG1lZGlhbl9tcGddID0gMQ0KQXV0byRtcGcwID0gbXBnMA0KQXV0bw0KYGBgDQoNCiMjIyAoYikNCg0K0J7QtCBib3hwbG90LdC+0LLQuNGC0LUg0LzQvtC20LXQvNC1INC00LAg0LfQsNCx0LXQu9C10LbQuNC80LUg0LTQtdC60LAgeWVhciDQuCBhY2NlbGVyYXRpb24g0L3QtdC80LAg0LTQsCDQsdC40LTQsNGCINC00L7QsdGA0Lgg0L/RgNC10LTQuNC60YLQvtGA0Lgg0L3QsCBtcGcwLiDQntGB0YLQsNC90LDRgtC40YLQtSDRgNC10LvQsNGC0LjQstC90L4g0LTQvtCx0YDQsCDRgdC10L/QsNGA0LDRhtC40ZjQsCDQv9GA0LDQstCw0YIg0L/QvtC80LXRk9GDINC00LLQtdGC0LUg0LrQu9Cw0YHQuCDQvdCwIG1wZzAuINCd0LDRmNC00L7QsdGA0Lgg0YHQtSDRh9C40L3QsNGCIHdlaWdodCwgZGlzcGxhY2VtZW50INC4IGN5bGluZGVycy4NCg0KYGBge3J9DQpmb3IgKGNvbCBpbiBjb2xuYW1lcyhBdXRvKSkgew0KICBpZiAoY29sID09ICJtcGciIHx8IGNvbCA9PSAibXBnMCIgfHwgY29sID09ICJuYW1lIikgew0KICAgIG5leHQNCiAgfQ0KICANCiAgYm94cGxvdChhcy5mb3JtdWxhKHBhc3RlKGNvbCwgIn4gbXBnMCIpKSwgZGF0YT1BdXRvKQ0KfQ0KYGBgDQoNCiMjIyAoYykNCg0K0J/RgNCw0LLQuNC80LUg0YDQsNC90LTQvtC8INC/0L7QtNC10LvQsdCwIDgwIC0gMjAuDQoNCmBgYHtyfQ0Kc3BsaXQgPSByb3VuZChsZW5ndGgoQXV0byRtcGcpICogMC44KQ0KaWR4cyA9IHNhbXBsZSgxOmxlbmd0aChBdXRvJG1wZykpDQp0cmFpbl9pZHhzID0gaWR4c1sxOnNwbGl0XQ0KdGVzdF9pZHhzID0gaWR4c1soc3BsaXQgKyAxKTpsZW5ndGgoQXV0byRtcGcpXQ0KdHJhaW5fYXV0byA9IEF1dG9bdHJhaW5faWR4cyxdDQp0ZXN0X2F1dG8gPSBBdXRvW3Rlc3RfaWR4cyxdDQp0cmFpbl9hdXRvDQp0ZXN0X2F1dG8NCmBgYA0KDQojIyMgKGQpIChlKSAoZikgKGcpDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTMzNykNCg0KbG9naXN0aWNfbW9kZWwgPSBnbG0oDQogIG1wZzAgfiB3ZWlnaHQgKyBkaXNwbGFjZW1lbnQgKyBjeWxpbmRlcnMsDQogIGRhdGE9dHJhaW5fYXV0bywgDQogIGZhbWlseT1iaW5vbWlhbA0KKQ0KcHJvYmFzID0gcHJlZGljdChsb2dpc3RpY19tb2RlbCwgdGVzdF9hdXRvLCB0eXBlPSJyZXNwb25zZSIpDQpwcmVkcyA9IHJlcCgwLCBsZW5ndGgocHJvYmFzKSkNCnByZWRzW3Byb2JhcyA+IDAuNV0gPSAxDQpwcmludCgNCiAgcGFzdGUoDQogICAgIkxvZ2lzdGljIHJlZ3Jlc3Npb24gZXJyb3IgcmF0ZToiLCANCiAgICAxIC0gbWVhbihwcmVkcyA9PSB0ZXN0X2F1dG8kbXBnMCksIA0KICAgIHNlcD0iICINCiAgKQ0KKQ0KDQpsZGFfbW9kZWwgPSBsZGEobXBnMCB+IHdlaWdodCArIGRpc3BsYWNlbWVudCArIGN5bGluZGVycywgZGF0YT10cmFpbl9hdXRvKQ0KcHJlZHMgPSBwcmVkaWN0KGxkYV9tb2RlbCwgdGVzdF9hdXRvKSRjbGFzcw0KcHJpbnQocGFzdGUoIkxEQSBlcnJvciByYXRlOiIsIDEgLSBtZWFuKHByZWRzID09IHRlc3RfYXV0byRtcGcwKSwgc2VwPSIgIikpDQoNCnFkYV9tb2RlbCA9IHFkYShtcGcwIH4gd2VpZ2h0ICsgZGlzcGxhY2VtZW50ICsgY3lsaW5kZXJzLCBkYXRhPXRyYWluX2F1dG8pDQpwcmVkcyA9IHByZWRpY3QocWRhX21vZGVsLCB0ZXN0X2F1dG8pJGNsYXNzDQpwcmludChwYXN0ZSgiUURBIGVycm9yIHJhdGU6IiwgMSAtIG1lYW4ocHJlZHMgPT0gdGVzdF9hdXRvJG1wZzApLCBzZXA9IiAiKSkNCg0KYmVzdF9rbm5fayA9IDANCmJlc3Rfa25uX2FjYyA9IDANCmZvciAoayBpbiAxOjMwMCkgew0KICBwcmVkcyA9IGtubigNCiAgICB0cmFpbl9hdXRvW2MoIndlaWdodCIsICJkaXNwbGFjZW1lbnQiLCAiY3lsaW5kZXJzIildLCANCiAgICB0ZXN0X2F1dG9bYygid2VpZ2h0IiwgImRpc3BsYWNlbWVudCIsICJjeWxpbmRlcnMiKV0sIA0KICAgIHRyYWluX2F1dG8kbXBnMCwgDQogICAgaz1rDQogICkNCiAgYWNjID0gbWVhbihwcmVkcyA9PSB0ZXN0X2F1dG8kbXBnMCkNCiAgaWYgKGFjYyA+IGJlc3Rfa25uX2FjYykgew0KICAgIGJlc3Rfa25uX2FjYyA9IGFjYw0KICAgIGJlc3Rfa25uX2sgPSBrDQogIH0NCn0NCnByaW50KHBhc3RlKGJlc3Rfa25uX2ssICJLTk4gZXJyb3IgcmF0ZToiLCAxIC0gYmVzdF9rbm5fYWNjLCBzZXA9IiAiKSkNCmBgYA0KDQojINCX0LDQtNCw0YfQsCAxMg0KDQrQntCy0LDQsCDQt9Cw0LTQsNGH0LAg0LUg0LfQsCDQtdC00L3QvtGB0YLQsNCy0L3QuCDRhNGD0L3QutGG0LjQuCDQuCDQvdC10LzQsCDQvdC10LrQsNC60LLQsCDQv9C+0LLRgNC30LDQvdC+0YHRgiDRgdC+INC80LDRgtC10YDQuNGY0LDRgtCwLiDQndC40Lcg0LTQvtC80LDRiNC90LDQstCwINC40LzQsNC8INC60L7RgNC40YHRgtC10L3QviDQtNC+0YHRgtCwINGE0YPQvdC60YbQuNC4LCDRgtCw0LrQsCDRiNGC0L4g0ZjQsCDRgdC60L7QutC90LDQsiDQvtCy0LDQsCDQt9Cw0LTQsNGH0LAuDQoNCiMg0JfQsNC00LDRh9CwIDEzDQoNCtCI0LAg0LTQvtC00LDQstCw0LzQtSDQvNC10LTQuNGY0LDQvdCw0YLQsDoNCg0KYGBge3J9DQptZWRpYW5fY3JpbSA9IG1lZGlhbihCb3N0b24kY3JpbSkNCmNyaW0wID0gcmVwKDAsIGxlbmd0aChCb3N0b24kY3JpbSkpDQpjcmltMFtCb3N0b24kY3JpbSA+IG1lZGlhbl9jcmltXSA9IDENCkJvc3RvbiRjcmltMCA9IGNyaW0wDQpCb3N0b24NCmBgYA0KDQrQlNC10LvQuNC80LUg0L3QsCDRgtGA0LXQvdC40YDQsNGH0LrQviDQuCDRgtC10YHRgtC40YDQsNGH0LrQviDQvNC90L7QttC10YHRgtCy0L46DQoNCmBgYHtyfQ0Kc3BsaXQgPSByb3VuZChsZW5ndGgoQm9zdG9uJGNyaW0wKSAqIDAuOCkNCmlkeHMgPSBzYW1wbGUoMTpsZW5ndGgoQm9zdG9uJGNyaW0wKSkNCnRyYWluX2lkeHMgPSBpZHhzWzE6c3BsaXRdDQp0ZXN0X2lkeHMgPSBpZHhzWyhzcGxpdCArIDEpOmxlbmd0aChCb3N0b24kY3JpbTApXQ0KdHJhaW5fYm9zdG9uID0gQm9zdG9uW3RyYWluX2lkeHMsXQ0KdGVzdF9ib3N0b24gPSBCb3N0b25bdGVzdF9pZHhzLF0NCnRyYWluX2Jvc3Rvbg0KdGVzdF9ib3N0b24NCmBgYA0KDQrQk9C4INC40YHQv9GA0L7QsdGD0LLQsNC80LUg0YHQuNGC0LUg0LzQvtC20L3QuCDQutC+0LzQsdC40L3QsNGG0LjQuCDQvdCwINC/0YDQtdC00LjQutGC0L7RgNC4ICgyXjEzKS4g0JfQsCDRgdC10LrQvtC1INC/0L7QtNC80L3QvtC20LXRgdGC0LLQviDQuNGB0L/RgNC+0LHRg9Cy0LDQvNC1INC70L7Qs9C40YHRgtC40YfQutCwINGA0LXQs9GA0LXRgdC40ZjQsCwgTERBLCBRREEg0LggMjAg0JrQndCdINC60LvQsNGB0LjRhNC40LrQsNGC0L7RgNC4LiDQktC60YPQv9C90L4g0YHQtSDQuNGB0L/RgNC+0LHQsNC90LggMl4xMyAqICgyMCArIDMpID0gMTg4NDE2INC80L7QtNC10LvQuCDQstC+INC+0LrQvtC70YMgNyDQvNC40L3Rg9GC0LguINCe0LQg0L3QuNCyLCDQvdCw0ZjQstC40YHQvtC60LAg0YLQvtGH0L3QvtGB0YIg0LjQvNCwINC70L7Qs9C40YHRgtC40YfQutCw0YLQsCDRgNC10LPRgNC10YHQuNGY0LAsINCwINGC0L7Rh9C90L7RgdGC0LAg0LjQt9C90LXRgdGD0LLQsCA5Ny4wMyUsINC60L7RgNC40YHRgtC10ZjRnNC4INCz0Lgg0L/RgNC10LTQuNC60YLQvtGA0LjRgtC1IGluZHVzLCBub3gsIHJtLCBhZ2UsIGRpcywgcmFkLCBwdHJhdGlvLCBibGFjaywgbHN0YXQg0LggbWVkdi4NCg0K0JHQuCDQuNGB0L/RgNC+0LHQsNC70LUg0Lgg0L/QvtCy0LXRnNC1INC+0L/RhtC40Lgg0LfQsCBLLCDQvdC+INCy0YDQtdC80LXRgtC+INC90LAg0LjQt9Cy0YDRiNGD0LLQsNGa0LUg0YDQsNGB0YLQtSDQvNC90L7Qs9GDICjQvdCw0LrQsNGYIDMg0YfQsNGB0LAg0LHQuCDQsdC40LvQviDQt9CwINGB0LjRgtC1INCaINC+0LQgMSDQtNC+IDIwMCkuINCX0LAg0YLQvtCwINC90LXQvNCwINC/0L7RgtGA0LXQsdCwLCDQsdC40LTQtdGY0ZzQuCDRgtC+0YfQvdC+0YHRgtCwINC4INCy0LDQutCwINC1INC00L7QstC+0LvQvdC+INCy0LjRgdC+0LrQsC4NCg0KYGBge3J9DQpzZXQuc2VlZCgxMzM3KQ0KcHJlZGljdG9ycyA9IGMoDQogICJ6biIsICJpbmR1cyIsICJjaGFzIiwgIm5veCIsICJybSIsICJhZ2UiLCAiZGlzIiwgInJhZCIsICJ0YXgiLCANCiAgInB0cmF0aW8iLCAiYmxhY2siLCAibHN0YXQiLCAibWVkdiINCikNCnRydWVzID0gdGVzdF9ib3N0b24kY3JpbTANCg0KZ2V0X2xvZ2lzdGljX21vZGVsX2FjYyA8LSBmdW5jdGlvbihmb3JtdWxhLCB0cmFpbl9kYXRhLCB0ZXN0X2RhdGEpIHsNCiAgbW9kZWwgPSBnbG0oYXMuZm9ybXVsYShmb3JtdWxhKSwgZGF0YT10cmFpbl9kYXRhLCBmYW1pbHk9Ymlub21pYWwpDQogIHByb2JhcyA9IHByZWRpY3QobW9kZWwsIHRlc3RfZGF0YSwgdHlwZT0icmVzcG9uc2UiKQ0KICBwcmVkcyA9IHJlcCgwLCBsZW5ndGgocHJvYmFzKSkNCiAgcHJlZHNbcHJvYmFzID4gMC41XSA9IDENCiAgcmV0dXJuKG1lYW4ocHJlZHMgPT0gdHJ1ZXMpKQ0KfQ0KDQpnZXRfZGFfbW9kZWxfYWNjIDwtIGZ1bmN0aW9uKGZvcm11bGEsIHRyYWluX2RhdGEsIHRlc3RfZGF0YSwgZGEpIHsNCiAgbW9kZWwgPSBkYShhcy5mb3JtdWxhKGZvcm11bGEpLCBkYXRhPXRyYWluX2RhdGEpDQogIHByZWRzID0gcHJlZGljdChtb2RlbCwgdGVzdF9kYXRhKSRjbGFzcw0KICByZXR1cm4obWVhbihwcmVkcyA9PSB0cnVlcykpDQp9DQoNCmdldF9rbm5fbW9kZWxfYWNjIDwtIGZ1bmN0aW9uKHByZWRpY3RvcnMsIHRyYWluX2RhdGEsIHRlc3RfZGF0YSwgaykgew0KICBwcmVkcyA9IGtubigNCiAgICB0cmFpbl9kYXRhW3ByZWRpY3RvcnNdLCANCiAgICB0ZXN0X2RhdGFbcHJlZGljdG9yc10sIA0KICAgIHRyYWluX2RhdGEkY3JpbTAsIA0KICAgIGs9aw0KICApDQogIHJldHVybihtZWFuKHByZWRzID09IHRydWVzKSkNCn0NCg0KYmVzdF9tb2RlbF9hY2MgPSAwDQpiZXN0X21vZGVsX2Zvcm11bGEgPSAiIg0KYmVzdF9tb2RlbF90eXBlID0gIiINCmJlc3RfbW9kZWxfayA9ICIiDQoNCmZvciAobnVtX3ByZWRpY3RvcnMgaW4gMTpsZW5ndGgocHJlZGljdG9ycykpIHsNCiAgcHJlZGljdG9yX2NvbWJpbmF0aW9ucyA9IGNvbWJuKHByZWRpY3RvcnMsIG51bV9wcmVkaWN0b3JzKQ0KICBudW1fY29tYmluYXRpb25zID0gZGltKHByZWRpY3Rvcl9jb21iaW5hdGlvbnMpWzJdDQogIGZvciAoY29tYmluYXRpb25faWR4IGluIDE6bnVtX2NvbWJpbmF0aW9ucykgew0KICAgIGZvcm11bGEgPSBwYXN0ZSgNCiAgICAgICJjcmltMH4iLA0KICAgICAgcGFzdGUocHJlZGljdG9yX2NvbWJpbmF0aW9uc1ssIGNvbWJpbmF0aW9uX2lkeF0sIGNvbGxhcHNlPSIrIiksDQogICAgICBzZXA9IiINCiAgICApDQogICAgDQogICAgbG9naXN0aWNfYWNjID0gZ2V0X2xvZ2lzdGljX21vZGVsX2FjYyhmb3JtdWxhLCB0cmFpbl9ib3N0b24sIHRlc3RfYm9zdG9uKQ0KICAgIGlmIChsb2dpc3RpY19hY2MgPiBiZXN0X21vZGVsX2FjYykgew0KICAgICAgYmVzdF9tb2RlbF9hY2MgPSBsb2dpc3RpY19hY2MNCiAgICAgIGJlc3RfbW9kZWxfZm9ybXVsYSA9IGZvcm11bGENCiAgICAgIGJlc3RfbW9kZWxfdHlwZSA9ICJsb2dpc3RpYyINCiAgICB9DQogICAgDQogICAgbGRhX2FjYyA9IGdldF9kYV9tb2RlbF9hY2MoZm9ybXVsYSwgdHJhaW5fYm9zdG9uLCB0ZXN0X2Jvc3RvbiwgbGRhKQ0KICAgIGlmIChsZGFfYWNjID4gYmVzdF9tb2RlbF9hY2MpIHsNCiAgICAgIGJlc3RfbW9kZWxfYWNjID0gbGRhX2FjYw0KICAgICAgYmVzdF9tb2RlbF9mb3JtdWxhID0gZm9ybXVsYQ0KICAgICAgYmVzdF9tb2RlbF90eXBlID0gIkxEQSINCiAgICB9DQoNCiAgICBxZGFfYWNjID0gZ2V0X2RhX21vZGVsX2FjYyhmb3JtdWxhLCB0cmFpbl9ib3N0b24sIHRlc3RfYm9zdG9uLCBxZGEpDQogICAgaWYgKHFkYV9hY2MgPiBiZXN0X21vZGVsX2FjYykgew0KICAgICAgYmVzdF9tb2RlbF9hY2MgPSBxZGFfYWNjDQogICAgICBiZXN0X21vZGVsX2Zvcm11bGEgPSBmb3JtdWxhDQogICAgICBiZXN0X21vZGVsX3R5cGUgPSAiUURBIg0KICAgIH0NCg0KICAgIGZvciAoayBpbiAxOjIwKSB7DQogICAgICBrbm5fYWNjID0gZ2V0X2tubl9tb2RlbF9hY2MocHJlZGljdG9ycywgdHJhaW5fYm9zdG9uLCB0ZXN0X2Jvc3RvbiwgaykNCiAgICAgIGlmIChrbm5fYWNjID4gYmVzdF9tb2RlbF9hY2MpIHsNCiAgICAgICAgYmVzdF9tb2RlbF9hY2MgPSBrbm5fYWNjDQogICAgICAgIGJlc3RfbW9kZWxfZm9ybXVsYSA9IGZvcm11bGENCiAgICAgICAgYmVzdF9tb2RlbF90eXBlID0gIktOTiINCiAgICAgICAgYmVzdF9tb2RlbF9rID0gaw0KICAgICAgfQ0KICAgIH0NCiAgfQ0KfQ0KDQpwcmludChwYXN0ZSgiQmVzdCBtb2RlbDogIiwgYmVzdF9tb2RlbF90eXBlKSkNCnByaW50KHBhc3RlKCJBY2M6ICIsIGJlc3RfbW9kZWxfYWNjKSkNCnByaW50KHBhc3RlKCJGb3JtdWxhOiAiLCBiZXN0X21vZGVsX2Zvcm11bGEpKQ0KaWYgKGJlc3RfbW9kZWxfdHlwZSA9PSAiS05OIikgew0KICBwcmludChwYXN0ZSgiSzoiLCBiZXN0X21vZGVsX2spKQ0KfQ0KYGBgDQoNCg==