Résumé de l’étude
Cette étude vise à prédire le taux de réussite au baccalauréat d’un lycée sur la base de quelques unes de ses caractéristiques. Les différents modèles permettent de prédire le taux de réussite avec une erreur moyenne d’environ 3,6 points.
Données
Les données utilisées ont été mises à disposition sur le site d’ouverture des données publiques data.gouv.fr par le ministère de l’éducation nationale.
Elles sont disponibles à l’adresse suivante, ainsi que le dictionnaire des variables : https://www.data.gouv.fr/fr/datasets/indicateurs-de-resultat-des-lycees-denseignement-general-et-technologique/
Sur l’ensemble du jeu de données, nous ne retenons que les variables suivantes :
- département de l’établissement : le niveau communal impliquerait une variance faible voire nulle dans des communes qui ne comptent qu’un seul lycée, tandis que le niveau de l’académie semble trop agrégé ;
- secteur : statut public ou privé de l’établissement ;
- structure pédagogique de l’établissement : diversité de l’offre de formation de l’établissement. Par exemple, A désigne un lycée comprenant uniquement les séries L, ES et S, tandis que E désigne les lycées hôteliers ;
- nombre de présents au baccalauréats ;
- effectifs à la rentrée 2016 en terminale ;
- taux de réussite au baccalauréat, toutes séries confondues : il s’agit de la variable à prédire.
Le jeu de données a été retravaillé en conséquence ; les suppressions ont été réalisées en dehors de R et n’apparaissent donc pas dans le code ci-dessous.
library(readr)
library(ggplot2)
library(scatterplot3d)
library(rgl)
library(caret)
library(h2o)
library(xgboost)
lycees_bac_data <- read_csv("lycees_bac_data.csv")
lycees_bac_data <- data.frame(lycees_bac_data)
str(lycees_bac_data)
'data.frame': 2295 obs. of 6 variables:
$ Departement : chr "ALPES DE HTE PROVENCE" "ALPES DE HTE PROVENCE" "ALPES DE HTE PROVENCE" "ALPES DE HTE PROVENCE" ...
$ Secteur : chr "PU" "PU" "PR" "PU" ...
$ Structure.Pedagogique: chr "A" "C" "B" "F" ...
$ nombre_presents_bac : num 48 230 25 181 20 317 185 158 58 415 ...
$ taux_reussite_bac : num 96 88 96 100 100 93 91 89 97 95 ...
$ effectifs_rentree : num 56 228 25 166 50 334 183 158 61 483 ...
# change type of columns
lycees_bac_data$Departement <- factor(lycees_bac_data$Departement)
lycees_bac_data$Secteur <- factor(lycees_bac_data$Secteur)
lycees_bac_data$Structure.Pedagogique <- factor(lycees_bac_data$Structure.Pedagogique)
summary(lycees_bac_data)
Departement Secteur Structure.Pedagogique nombre_presents_bac taux_reussite_bac effectifs_rentree
PARIS : 112 PR: 771 A:456 Min. : 20.0 Min. : 42.00 Min. : 9.0
NORD : 98 PU:1524 B:606 1st Qu.:107.0 1st Qu.: 90.00 1st Qu.:113.5
BOUCHES DU RHONE : 76 C:443 Median :189.0 Median : 93.00 Median :202.0
RHONE : 69 D:259 Mean :206.3 Mean : 92.58 Mean :216.6
SEINE SAINT-DENIS: 55 E: 36 3rd Qu.:290.0 3rd Qu.: 97.00 3rd Qu.:305.0
SEINE ET MARNE : 54 F:395 Max. :858.0 Max. :100.00 Max. :913.0
(Other) :1831 G:100
sum(is.na(lycees_bac_data)) # no NAs
[1] 0
Exploration des données
La variable à prédire - le taux de réussite au baccalauréat - affiche une distribution relativement concentrée autour de sa tendance centrale, d’environ 93 %. Dans plus de trois établissements sur quatre, le taux de réussite dépasse 90 %.
La représentation graphique des données permet de dégager les principales indications suivantes :
- les établissements privés affichent en général un meilleur taux de réussite au baccalauréat que les lycées publics, sauf exception (filière E : hôtellerie). Leurs effectifs apparaissent en moyenne moins nombreux ;
- les taux de réussite selon les départements semblent relativement peu dispersés ;
- au-delà d’environ 50 élèves, la relation entre le taux de réussite au baccalauréat et les effectifs ou le nombre de présents semble légèrement négative ; autrement dit, un plus grand nombre d’élèves est associé à un taux de réussite légèrement plus faible. Ces corrélations négatives, d’ailleurs faibles (-0,12 et -0,14) n’impliquent pas nécessairement de relations causales entre ces variables.
# target
ggplot(data = lycees_bac_data) +
geom_histogram(mapping = aes(x = taux_reussite_bac), binwidth = 1) +
labs(title = "Distribution du taux de réussite au baccalauréat", x = "taux de réussite")

summary(lycees_bac_data$taux_reussite_bac)
Min. 1st Qu. Median Mean 3rd Qu. Max.
42.00 90.00 93.00 92.58 97.00 100.00
sd(lycees_bac_data$taux_reussite_bac)
[1] 5.994898
# predictors
ggplot(data = lycees_bac_data) +
geom_boxplot(mapping = aes(x = Departement, y = taux_reussite_bac)) +
coord_flip()

ggplot(data = lycees_bac_data) +
geom_boxplot(mapping = aes(x = Secteur, y = taux_reussite_bac)) +
labs(title = "Statut de l'établissement et taux de réussite", x = "Statut public ou privé",
y = "taux de réussite")

ggplot(data = lycees_bac_data) +
geom_boxplot(mapping = aes(x = Structure.Pedagogique, y = taux_reussite_bac, fill = Secteur)) +
labs(title = "Offre de formation de l'établissement, statut et taux de réussite",
x = "offre de formation", y = "taux de réussite")

ggplot(data = lycees_bac_data) +
geom_boxplot(mapping = aes(x = Secteur, y = taux_reussite_bac, fill = Structure.Pedagogique)) +
labs(title = "Offre de formation de l'établissement, statut et taux de réussite",
x = "Statut public ou privé", y = "taux de réussite")

ggplot(data = lycees_bac_data) +
geom_violin(mapping = aes(x = Secteur, y = taux_reussite_bac, fill = Structure.Pedagogique)) +
labs(title = "Distribution des variables Statut et Offre de formation")

ggplot(data = lycees_bac_data) +
geom_point(mapping = aes(x = nombre_presents_bac, y = taux_reussite_bac, color = Secteur)) +
geom_smooth(mapping = aes(x = nombre_presents_bac, y = taux_reussite_bac)) +
labs(title = "Nombre d'élèves présents au bac, statut et taux de réussite",
x = "Nombre d'élèves présents au bac", y = "taux de réussite")

ggplot(data = lycees_bac_data) +
geom_point(mapping = aes(x = effectifs_rentree, y = taux_reussite_bac, color = Secteur)) +
geom_smooth(mapping = aes(x = effectifs_rentree, y = taux_reussite_bac)) +
labs(title = "Effectifs en terminale, statut et taux de réussite",
x = "Effectifs en terminale", y = "taux de réussite")

ggplot(data = lycees_bac_data) +
geom_point(mapping = aes(x = nombre_presents_bac, y = taux_reussite_bac, color = Structure.Pedagogique)) +
geom_smooth(mapping = aes(x = nombre_presents_bac, y = taux_reussite_bac)) +
labs(title = "Nombre d'élèves présents au bac, offre de formation et taux de réussite",
x = "Nombre d'élèves présents au bac", y = "taux de réussite")

ggplot(data = lycees_bac_data) +
geom_point(mapping = aes(x = effectifs_rentree, y = taux_reussite_bac, color = Structure.Pedagogique)) +
geom_smooth(mapping = aes(x = effectifs_rentree, y = taux_reussite_bac)) +
labs(title = "Effectifs en terminale, offre de formation et taux de réussite",
x = "Effectifs en terminale", y = "taux de réussite")

# numeric features
scatterplot3d(nombre_presents_bac, effectifs_rentree, taux_reussite_bac)

plot3d(nombre_presents_bac, effectifs_rentree, taux_reussite_bac)
cor(lycees_bac_data$nombre_presents_bac, lycees_bac_data$taux_reussite_bac)
[1] -0.1202353
cor(lycees_bac_data$effectifs_rentree, lycees_bac_data$taux_reussite_bac)
[1] -0.141762
Préparation des données
Les données pour 2016 seront utilisées pour l’entraînement des modèles, tandis que celles pour 2015 permettront d’évaluer leur capacité à prédire le taux de réussite au baccalauréat des établissements. A noter que le jeu de données pour 2015, disponible ici : https://www.data.gouv.fr/fr/datasets/indicateurs-de-resultat-des-lycees-denseignement-general-et-technologique/, a été retravaillé pareillement aux données pour 2016 afin de ne retenir que les six mêmes variables, dont celle à prédire.
La préparation des données implique d’abord de transformer en variables binaires (dummy variables) les trois variables catégoriques (statut de l’établissement, offre de formation, département) puis de centrer et réduire les variables prédictives.
target <- lycees_bac_data$taux_reussite_bac
# preprocess the train set
dummies <- dummyVars(taux_reussite_bac ~ ., data = lycees_bac_data)
train <- predict(dummies, newdata = lycees_bac_data)
train <- data.frame(train)
# transform
transform <- preProcess(train, method = c('center', 'scale'))
train <- predict(transform, newdata = train)
# append the target
train$taux_reussite_bac <- target
# test data
test_2015 <- read_delim("test_2015.csv", ";", escape_double = FALSE, trim_ws = TRUE)
test_2015 <- data.frame(test_2015)
test_2015$Departement <- factor(test_2015$Departement)
test_2015$Secteur <- factor(test_2015$Secteur)
test_2015$Structure.Pedagogique <- factor(test_2015$Structure.Pedagogique)
target_test <- test_2015$taux_reussite_bac
test_2015 <- predict(dummies, newdata = test_2015)
test_2015 <- data.frame(test_2015)
test_2015 <- predict(transform, newdata = test_2015)
Modélisation du taux de réussite au baccalauréat
Les modélisations suivantes sont essayées pour estimer la capacité d’un modèle à prédire le taux de réussite au baccalauréat d’un lycée donné :
- régression linéraire
- support vector machines (SVM)
- arbres de décision
- réseau de neurones
- gradient boosting (XGboost)
Les hyperparamètres sont choisis par validation croisée (cross validation) compte tenu du petit nombre d’observations. Les meilleurs modèles sont ensuite appliqués sur les données 2015 afin de calculer, pour chaque observation, l’écart entre la prédiction du modèle et la valeur constatée en 2015 du taux de réussite au baccalauréat de l’établissement. La mesure de performance retenue ici est l’erreur moyenne absolue (MAE, mean absolute error).
Régression linéaire
# linear regression
control <- trainControl(method = "repeatedcv",
number = 10,
repeats = 5)
lr1 <- train(taux_reussite_bac ~ ., data = train, method = "lm", trControl = control)
summary(lr1)
lr1 # MAE = 3.73 ; R2 = 25 %
# evaluate on test set
predicted_lr <- predict(lr1, newdata = test_2015)
predicted_err <- mean(abs(target_test - predicted_lr))
predicted_err # MAE on test set : 3.68
Support Vector Machines (SVM)
# linear
svmlinear1 <- train(taux_reussite_bac ~ ., data = train,
method = 'svmLinear', trControl = control)
svmlinear1 # MAE 3.618678
svmgrid2 <- expand.grid(cost = c(0.25,0.5,0.75))
svmlinear2 <- train(taux_reussite_bac ~ ., data = train,
method = 'svmLinear2', trControl = control, tuneGrid = svmgrid2)
svmlinear2 # 3.605985
# polynomial
svmpoly_grid <- expand.grid(degree = c(2,3), scale = F, C = c(0.25,0.5,0.75))
svmpoly <- train(taux_reussite_bac ~ ., data = train,
method = 'svmPoly', trControl = control, tuneGrid = svmpoly_grid)
svmpoly # MAE around 4.48 for all
# radial
svmradial <- train(taux_reussite_bac ~ ., data = train,
method = 'svmRadial', trControl = control)
svmradial # best model : C = 1, sigma = 0.006190923. MAE = 3.509334
# evaluate on test set
predicted_svmradial <- predict(svmradial, newdata = test_2015)
predicted_err_svmradial <- mean(abs(target_test - predicted_svmradial))
predicted_err_svmradial # MAE on test set : 3.397091
Modèles à base d’arbres de décision
baggedcart <- train(taux_reussite_bac ~ ., data = train,
method = 'treebag', trControl = control)
baggedcart # MAE : 3.684886
# evaluate on test set
predicted_baggedcart <- predict(baggedcart, newdata = test_2015)
predicted_err_baggedcart <- mean(abs(target_test - predicted_baggedcart))
predicted_err_baggedcart # 3.756589
rpart1 <- train(taux_reussite_bac ~ ., data = train,
method = 'rpart2', maxdepth = 3,
trControl = control)
rpart1 # best MAE for depth = 3 : 3.745707
# evaluate on test set
predicted_rpart <- predict(rpart1, newdata = test_2015)
predicted_err_rpart <- mean(abs(target_test - predicted_rpart))
predicted_err_rpart # MAE : 3.843929
Réseau de neurones
h2o.init(nthreads = -1, max_mem_size = '4g')
# prepare the datasets
train_h2o <- as.h2o(train)
test_h2o <- as.h2o(test_2015)
target_test_h2o <- as.h2o(target_test)
Y <- "taux_reussite_bac"
X <- setdiff(names(train_h2o), Y)
# train the neural network
DL.h2o.1 <- h2o.deeplearning(x = X,
y = Y,
training_frame = train_h2o,
nfolds = 5,
distribution = "gaussian",
activation = "Maxout",
hidden = c(32,32,32),
sparse = FALSE,
l1 = 0.5,
l2 = 0.5,
epochs = 10,
standardize = FALSE,
variable_importances = TRUE,
seed = 2)
DL.h2o.1 # MAE: 4.011346
DL.h2o.2 <- h2o.deeplearning(x = X,
y = Y,
training_frame = train_h2o,
nfolds = 5,
distribution = "gaussian",
activation = "Maxout",
hidden = c(100,100),
sparse = FALSE,
l1 = 0.5,
l2 = 0.5,
epochs = 10,
standardize = FALSE,
variable_importances = TRUE,
seed = 2)
DL.h2o.2 # MAE: 3.752703, no overfit
predicted_dl2 <- h2o.predict(DL.h2o.2, newdata = test_h2o)
predicted_dl2_err <- mean(abs(predicted_dl2 - target_test_h2o))
predicted_dl2_err # test MAE : 4.508496
XGboost
# prepare the train set
dtrain <- xgb.DMatrix(as.matrix(train[, -113]), label = as.matrix(train[, 113]))
# train with cross validation
xgb <- xgb.cv(data = dtrain,
nrounds = 100,
nthread = 2,
nfold = 5,
metrics = "mae",
max_depth = 3,
eta = 0.1,
objective = "reg:linear")
xgb
# model
xgb_model <- xgboost(data = dtrain,
nrounds = 100,
nthread = 2,
metrics = "mae",
max_depth = 3,
eta = 0.1,
objective = "reg:linear")
# evaluate on test set
xgb_predicted <- predict(xgb_model, newdata = as.matrix(test_2015))
xgb_err <- mean(abs(target_test - xgb_predicted))
xgb_err # MAE on test set : 3.59
Conclusion
La meilleure prédiction a été réalisée par le SVM à noyau non-linéaire radial (radial kernel), avec une erreur moyenne absolue de 3,39. Autrement dit, si le modèle prédit, pour un établissement donné, un taux de réussite de 85 % au baccalauréat, le véritable taux se situe probablement entre 81,6 % et 88,4 %. La plupart des modélisations dans cette étude atteignent une erreur moyenne d’environ 3,6.
Cette précision est-elle satisfaisante ? D’un côté, elle l’est compte tenu du faible nombre (5) de variables prédictives disponibles dans le jeu de données. D’un autre côté, elle s’avère modeste au regard de la distribution du taux de réussite au baccalauréat, assez concentrée autour de 93 %, avec un écart-type de 6 points pour 2016. Ce résultat ne doit pas étonner au vu de l’exploration des données (faibles niveaux des corrélations, faibles différences de taux de réussite entre les départements, etc.).
Pour améliorer la précision du modèle, de nouvelles variables prédictives seraient requises. Par hypothèse, on peut songer au budget de l’établissement rapporté au nombre d’élèves en terminale, au nombre d’élèves par enseignant, etc. De même, le niveau intercommunal pourrait s’avérer plus utile que le niveau départemental car il est probable que la dispersion au sein de chaque département soit supérieure à la dispersion entre départements.
LS0tDQp0aXRsZTogIkxlIHRhdXggZGUgcul1c3NpdGUgYXUgYmFjY2FsYXVy6WF0Ig0KYXV0aG9yOiBPbGl2aWVyIFZpb2xsZXQgLSBvbGl2aWVyLnZpb2xsZXRbYV1nbWFpbC5jb20NCmRhdGU6IEbpdnJpZXIsIDIwMTkNCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIFLpc3Vt6SBkZSBsJ+l0dWRlDQoNCkNldHRlIOl0dWRlIHZpc2Ug4CBwculkaXJlIGxlIHRhdXggZGUgcul1c3NpdGUgYXUgYmFjY2FsYXVy6WF0IGQndW4gbHlj6WUgc3VyIGxhIGJhc2UgZGUgcXVlbHF1ZXMgdW5lcyBkZSBzZXMgY2FyYWN06XJpc3RpcXVlcy4gTGVzIGRpZmbpcmVudHMgbW9k6GxlcyBwZXJtZXR0ZW50IGRlIHBy6WRpcmUgbGUgdGF1eCBkZSBy6XVzc2l0ZSBhdmVjIHVuZSBlcnJldXIgbW95ZW5uZSBkJ2Vudmlyb24gMyw2IHBvaW50cy4NCg0KIyMgRG9ubullcw0KDQpMZXMgZG9ubullcyB1dGlsaXPpZXMgb250IOl06SBtaXNlcyDgIGRpc3Bvc2l0aW9uIHN1ciBsZSBzaXRlIGQnb3V2ZXJ0dXJlIGRlcyBkb25u6WVzIHB1YmxpcXVlcyBkYXRhLmdvdXYuZnIgcGFyIGxlIG1pbmlzdOhyZSBkZSBsJ+lkdWNhdGlvbiBuYXRpb25hbGUuIA0KDQpFbGxlcyBzb250IGRpc3BvbmlibGVzIOAgbCdhZHJlc3NlIHN1aXZhbnRlLCBhaW5zaSBxdWUgbGUgZGljdGlvbm5haXJlIGRlcyB2YXJpYWJsZXMgOiBodHRwczovL3d3dy5kYXRhLmdvdXYuZnIvZnIvZGF0YXNldHMvaW5kaWNhdGV1cnMtZGUtcmVzdWx0YXQtZGVzLWx5Y2Vlcy1kZW5zZWlnbmVtZW50LWdlbmVyYWwtZXQtdGVjaG5vbG9naXF1ZS8gDQoNClN1ciBsJ2Vuc2VtYmxlIGR1IGpldSBkZSBkb25u6WVzLCBub3VzIG5lIHJldGVub25zIHF1ZSBsZXMgdmFyaWFibGVzIHN1aXZhbnRlcyA6DQoNCi0gKipk6XBhcnRlbWVudCoqIGRlIGwn6XRhYmxpc3NlbWVudCA6IGxlIG5pdmVhdSBjb21tdW5hbCBpbXBsaXF1ZXJhaXQgdW5lIHZhcmlhbmNlIGZhaWJsZSB2b2lyZSBudWxsZSBkYW5zIGRlcyBjb21tdW5lcyBxdWkgbmUgY29tcHRlbnQgcXUndW4gc2V1bCBseWPpZSwgdGFuZGlzIHF1ZSBsZSBuaXZlYXUgZGUgbCdhY2Fk6W1pZSBzZW1ibGUgdHJvcCBhZ3LpZ+kgOw0KLSAqKnNlY3RldXIqKiA6IHN0YXR1dCBwdWJsaWMgb3UgcHJpdukgZGUgbCfpdGFibGlzc2VtZW50IDsNCi0gKipzdHJ1Y3R1cmUgcOlkYWdvZ2lxdWUqKiBkZSBsJ+l0YWJsaXNzZW1lbnQgOiBkaXZlcnNpdOkgZGUgbCdvZmZyZSBkZSBmb3JtYXRpb24gZGUgbCfpdGFibGlzc2VtZW50LiBQYXIgZXhlbXBsZSwgQSBk6XNpZ25lIHVuIGx5Y+llIGNvbXByZW5hbnQgdW5pcXVlbWVudCBsZXMgc+lyaWVzIEwsIEVTIGV0IFMsIHRhbmRpcyBxdWUgRSBk6XNpZ25lIGxlcyBseWPpZXMgaPR0ZWxpZXJzIDsNCi0gKipub21icmUgZGUgcHLpc2VudHMqKiBhdSBiYWNjYWxhdXLpYXRzIDsNCi0gKiplZmZlY3RpZnMqKiDgIGxhIHJlbnRy6WUgMjAxNiBlbiB0ZXJtaW5hbGUgOw0KLSAqKnRhdXggZGUgcul1c3NpdGUqKiBhdSBiYWNjYWxhdXLpYXQsIHRvdXRlcyBz6XJpZXMgY29uZm9uZHVlcyA6IGlsIHMnYWdpdCBkZSBsYSB2YXJpYWJsZSDgIHBy6WRpcmUuIA0KDQpMZSBqZXUgZGUgZG9ubullcyBhIOl06SByZXRyYXZhaWxs6SBlbiBjb25z6XF1ZW5jZSA7IGxlcyBzdXBwcmVzc2lvbnMgb250IOl06SBy6WFsaXPpZXMgZW4gZGVob3JzIGRlIFIgZXQgbidhcHBhcmFpc3NlbnQgZG9uYyBwYXMgZGFucyBsZSBjb2RlIGNpLWRlc3NvdXMuIA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc2NhdHRlcnBsb3QzZCkNCmxpYnJhcnkocmdsKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoaDJvKQ0KbGlicmFyeSh4Z2Jvb3N0KQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpseWNlZXNfYmFjX2RhdGEgPC0gcmVhZF9jc3YoImx5Y2Vlc19iYWNfZGF0YS5jc3YiKQ0KbHljZWVzX2JhY19kYXRhIDwtIGRhdGEuZnJhbWUobHljZWVzX2JhY19kYXRhKQ0Kc3RyKGx5Y2Vlc19iYWNfZGF0YSkNCg0KIyBjaGFuZ2UgdHlwZSBvZiBjb2x1bW5zDQpseWNlZXNfYmFjX2RhdGEkRGVwYXJ0ZW1lbnQgPC0gZmFjdG9yKGx5Y2Vlc19iYWNfZGF0YSREZXBhcnRlbWVudCkNCmx5Y2Vlc19iYWNfZGF0YSRTZWN0ZXVyIDwtIGZhY3RvcihseWNlZXNfYmFjX2RhdGEkU2VjdGV1cikNCmx5Y2Vlc19iYWNfZGF0YSRTdHJ1Y3R1cmUuUGVkYWdvZ2lxdWUgPC0gZmFjdG9yKGx5Y2Vlc19iYWNfZGF0YSRTdHJ1Y3R1cmUuUGVkYWdvZ2lxdWUpDQoNCnN1bW1hcnkobHljZWVzX2JhY19kYXRhKQ0Kc3VtKGlzLm5hKGx5Y2Vlc19iYWNfZGF0YSkpICMgbm8gTkFzDQoNCmBgYA0KDQojIyBFeHBsb3JhdGlvbiBkZXMgZG9ubullcw0KDQpMYSB2YXJpYWJsZSDgIHBy6WRpcmUgLSBsZSB0YXV4IGRlIHLpdXNzaXRlIGF1IGJhY2NhbGF1culhdCAtIGFmZmljaGUgdW5lIGRpc3RyaWJ1dGlvbiByZWxhdGl2ZW1lbnQgY29uY2VudHLpZSBhdXRvdXIgZGUgc2EgdGVuZGFuY2UgY2VudHJhbGUsIGQnZW52aXJvbiA5MyAlLiBEYW5zIHBsdXMgZGUgdHJvaXMg6XRhYmxpc3NlbWVudHMgc3VyIHF1YXRyZSwgbGUgdGF1eCBkZSBy6XVzc2l0ZSBk6XBhc3NlIDkwICUuIA0KDQpMYSByZXBy6XNlbnRhdGlvbiBncmFwaGlxdWUgZGVzIGRvbm7pZXMgcGVybWV0IGRlIGTpZ2FnZXIgbGVzIHByaW5jaXBhbGVzIGluZGljYXRpb25zIHN1aXZhbnRlcyA6DQoNCi0gbGVzIOl0YWJsaXNzZW1lbnRzIHByaXbpcyBhZmZpY2hlbnQgZW4gZ+lu6XJhbCB1biBtZWlsbGV1ciB0YXV4IGRlIHLpdXNzaXRlIGF1IGJhY2NhbGF1culhdCBxdWUgbGVzIGx5Y+llcyBwdWJsaWNzLCBzYXVmIGV4Y2VwdGlvbiAoZmlsaehyZSBFIDogaPR0ZWxsZXJpZSkuIExldXJzIGVmZmVjdGlmcyBhcHBhcmFpc3NlbnQgZW4gbW95ZW5uZSBtb2lucyBub21icmV1eCA7IA0KLSBsZXMgdGF1eCBkZSBy6XVzc2l0ZSBzZWxvbiBsZXMgZOlwYXJ0ZW1lbnRzIHNlbWJsZW50IHJlbGF0aXZlbWVudCBwZXUgZGlzcGVyc+lzIDsNCi0gYXUtZGVs4CBkJ2Vudmlyb24gNTAg6WzodmVzLCBsYSByZWxhdGlvbiBlbnRyZSBsZSB0YXV4IGRlIHLpdXNzaXRlIGF1IGJhY2NhbGF1culhdCBldCBsZXMgZWZmZWN0aWZzIG91IGxlIG5vbWJyZSBkZSBwculzZW50cyBzZW1ibGUgbOln6HJlbWVudCBu6WdhdGl2ZSA7IGF1dHJlbWVudCBkaXQsIHVuIHBsdXMgZ3JhbmQgbm9tYnJlIGQn6WzodmVzIGVzdCBhc3NvY2npIOAgdW4gdGF1eCBkZSBy6XVzc2l0ZSBs6WfocmVtZW50IHBsdXMgZmFpYmxlLiBDZXMgY29yculsYXRpb25zIG7pZ2F0aXZlcywgZCdhaWxsZXVycyBmYWlibGVzICgtMCwxMiBldCAtMCwxNCkgbidpbXBsaXF1ZW50IHBhcyBu6WNlc3NhaXJlbWVudCBkZSByZWxhdGlvbnMgY2F1c2FsZXMgZW50cmUgY2VzIHZhcmlhYmxlcy4NCg0KDQpgYGB7cn0NCiMgdGFyZ2V0DQpnZ3Bsb3QoZGF0YSA9IGx5Y2Vlc19iYWNfZGF0YSkgKw0KICAgICAgICBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSB0YXV4X3JldXNzaXRlX2JhYyksIGJpbndpZHRoID0gMSkgKw0KICAgICAgICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBkdSB0YXV4IGRlIHLpdXNzaXRlIGF1IGJhY2NhbGF1culhdCIsIHggPSAidGF1eCBkZSBy6XVzc2l0ZSIpDQoNCnN1bW1hcnkobHljZWVzX2JhY19kYXRhJHRhdXhfcmV1c3NpdGVfYmFjKQ0Kc2QobHljZWVzX2JhY19kYXRhJHRhdXhfcmV1c3NpdGVfYmFjKQ0KDQojIHByZWRpY3RvcnMgDQpnZ3Bsb3QoZGF0YSA9IGx5Y2Vlc19iYWNfZGF0YSkgKw0KICAgICAgICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gRGVwYXJ0ZW1lbnQsIHkgPSB0YXV4X3JldXNzaXRlX2JhYykpICsNCiAgICAgICAgY29vcmRfZmxpcCgpDQoNCmdncGxvdChkYXRhID0gbHljZWVzX2JhY19kYXRhKSArDQogICAgICAgIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBTZWN0ZXVyLCB5ID0gdGF1eF9yZXVzc2l0ZV9iYWMpKSArDQogICAgICAgIGxhYnModGl0bGUgPSAiU3RhdHV0IGRlIGwn6XRhYmxpc3NlbWVudCBldCB0YXV4IGRlIHLpdXNzaXRlIiwgeCA9ICJTdGF0dXQgcHVibGljIG91IHByaXbpIiwgDQogICAgICAgICAgICAgeSA9ICJ0YXV4IGRlIHLpdXNzaXRlIikNCg0KZ2dwbG90KGRhdGEgPSBseWNlZXNfYmFjX2RhdGEpICsNCiAgICAgICAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IFN0cnVjdHVyZS5QZWRhZ29naXF1ZSwgeSA9IHRhdXhfcmV1c3NpdGVfYmFjLCBmaWxsID0gU2VjdGV1cikpICsNCiAgICAgICAgbGFicyh0aXRsZSA9ICJPZmZyZSBkZSBmb3JtYXRpb24gZGUgbCfpdGFibGlzc2VtZW50LCBzdGF0dXQgZXQgdGF1eCBkZSBy6XVzc2l0ZSIsDQogICAgICAgICAgICAgeCA9ICJvZmZyZSBkZSBmb3JtYXRpb24iLCB5ID0gInRhdXggZGUgcul1c3NpdGUiKQ0KDQpnZ3Bsb3QoZGF0YSA9IGx5Y2Vlc19iYWNfZGF0YSkgKw0KICAgICAgICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gU2VjdGV1ciwgeSA9IHRhdXhfcmV1c3NpdGVfYmFjLCBmaWxsID0gU3RydWN0dXJlLlBlZGFnb2dpcXVlKSkgKw0KICAgICAgICBsYWJzKHRpdGxlID0gIk9mZnJlIGRlIGZvcm1hdGlvbiBkZSBsJ+l0YWJsaXNzZW1lbnQsIHN0YXR1dCBldCB0YXV4IGRlIHLpdXNzaXRlIiwNCiAgICAgICAgICAgICB4ID0gIlN0YXR1dCBwdWJsaWMgb3UgcHJpdukiLCB5ID0gInRhdXggZGUgcul1c3NpdGUiKQ0KDQpnZ3Bsb3QoZGF0YSA9IGx5Y2Vlc19iYWNfZGF0YSkgKw0KICAgICAgICBnZW9tX3Zpb2xpbihtYXBwaW5nID0gYWVzKHggPSBTZWN0ZXVyLCB5ID0gdGF1eF9yZXVzc2l0ZV9iYWMsIGZpbGwgPSBTdHJ1Y3R1cmUuUGVkYWdvZ2lxdWUpKSArDQogICAgICAgIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIGRlcyB2YXJpYWJsZXMgU3RhdHV0IGV0IE9mZnJlIGRlIGZvcm1hdGlvbiIpDQoNCmdncGxvdChkYXRhID0gbHljZWVzX2JhY19kYXRhKSArDQogICAgICAgIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gbm9tYnJlX3ByZXNlbnRzX2JhYywgeSA9IHRhdXhfcmV1c3NpdGVfYmFjLCBjb2xvciA9IFNlY3RldXIpKSArDQogICAgICAgIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IG5vbWJyZV9wcmVzZW50c19iYWMsIHkgPSB0YXV4X3JldXNzaXRlX2JhYykpICsNCiAgICAgICAgbGFicyh0aXRsZSA9ICJOb21icmUgZCfpbOh2ZXMgcHLpc2VudHMgYXUgYmFjLCBzdGF0dXQgZXQgdGF1eCBkZSBy6XVzc2l0ZSIsDQogICAgICAgICAgICAgeCA9ICJOb21icmUgZCfpbOh2ZXMgcHLpc2VudHMgYXUgYmFjIiwgeSA9ICJ0YXV4IGRlIHLpdXNzaXRlIikNCg0KZ2dwbG90KGRhdGEgPSBseWNlZXNfYmFjX2RhdGEpICsNCiAgICAgICAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBlZmZlY3RpZnNfcmVudHJlZSwgeSA9IHRhdXhfcmV1c3NpdGVfYmFjLCBjb2xvciA9IFNlY3RldXIpKSArDQogICAgICAgIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGVmZmVjdGlmc19yZW50cmVlLCB5ID0gdGF1eF9yZXVzc2l0ZV9iYWMpKSArDQogICAgICAgIGxhYnModGl0bGUgPSAiRWZmZWN0aWZzIGVuIHRlcm1pbmFsZSwgc3RhdHV0IGV0IHRhdXggZGUgcul1c3NpdGUiLA0KICAgICAgICAgICAgIHggPSAiRWZmZWN0aWZzIGVuIHRlcm1pbmFsZSIsIHkgPSAidGF1eCBkZSBy6XVzc2l0ZSIpDQoNCmdncGxvdChkYXRhID0gbHljZWVzX2JhY19kYXRhKSArDQogICAgICAgIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gbm9tYnJlX3ByZXNlbnRzX2JhYywgeSA9IHRhdXhfcmV1c3NpdGVfYmFjLCBjb2xvciA9IFN0cnVjdHVyZS5QZWRhZ29naXF1ZSkpICsNCiAgICAgICAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gbm9tYnJlX3ByZXNlbnRzX2JhYywgeSA9IHRhdXhfcmV1c3NpdGVfYmFjKSkgKw0KICAgICAgICBsYWJzKHRpdGxlID0gIk5vbWJyZSBkJ+ls6HZlcyBwculzZW50cyBhdSBiYWMsIG9mZnJlIGRlIGZvcm1hdGlvbiBldCB0YXV4IGRlIHLpdXNzaXRlIiwNCiAgICAgICAgICAgICB4ID0gIk5vbWJyZSBkJ+ls6HZlcyBwculzZW50cyBhdSBiYWMiLCB5ID0gInRhdXggZGUgcul1c3NpdGUiKQ0KDQpnZ3Bsb3QoZGF0YSA9IGx5Y2Vlc19iYWNfZGF0YSkgKw0KICAgICAgICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGVmZmVjdGlmc19yZW50cmVlLCB5ID0gdGF1eF9yZXVzc2l0ZV9iYWMsIGNvbG9yID0gU3RydWN0dXJlLlBlZGFnb2dpcXVlKSkgKw0KICAgICAgICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBlZmZlY3RpZnNfcmVudHJlZSwgeSA9IHRhdXhfcmV1c3NpdGVfYmFjKSkgKw0KICAgICAgICBsYWJzKHRpdGxlID0gIkVmZmVjdGlmcyBlbiB0ZXJtaW5hbGUsIG9mZnJlIGRlIGZvcm1hdGlvbiBldCB0YXV4IGRlIHLpdXNzaXRlIiwNCiAgICAgICAgICAgICB4ID0gIkVmZmVjdGlmcyBlbiB0ZXJtaW5hbGUiLCB5ID0gInRhdXggZGUgcul1c3NpdGUiKQ0KDQojIG51bWVyaWMgZmVhdHVyZXMNCnNjYXR0ZXJwbG90M2Qobm9tYnJlX3ByZXNlbnRzX2JhYywgZWZmZWN0aWZzX3JlbnRyZWUsIHRhdXhfcmV1c3NpdGVfYmFjKQ0KcGxvdDNkKG5vbWJyZV9wcmVzZW50c19iYWMsIGVmZmVjdGlmc19yZW50cmVlLCB0YXV4X3JldXNzaXRlX2JhYykNCg0KYGBgDQoNCmBgYHtyfQ0KY29yKGx5Y2Vlc19iYWNfZGF0YSRub21icmVfcHJlc2VudHNfYmFjLCBseWNlZXNfYmFjX2RhdGEkdGF1eF9yZXVzc2l0ZV9iYWMpDQpjb3IobHljZWVzX2JhY19kYXRhJGVmZmVjdGlmc19yZW50cmVlLCBseWNlZXNfYmFjX2RhdGEkdGF1eF9yZXVzc2l0ZV9iYWMpDQoNCmBgYA0KDQoNCg0KDQojIyBQculwYXJhdGlvbiBkZXMgZG9ubullcw0KDQpMZXMgZG9ubullcyBwb3VyIDIwMTYgc2Vyb250IHV0aWxpc+llcyBwb3VyIGwnZW50cmHubmVtZW50IGRlcyBtb2TobGVzLCB0YW5kaXMgcXVlIGNlbGxlcyBwb3VyIDIwMTUgcGVybWV0dHJvbnQgZCfpdmFsdWVyIGxldXIgY2FwYWNpdOkg4CBwculkaXJlIGxlIHRhdXggZGUgcul1c3NpdGUgYXUgYmFjY2FsYXVy6WF0IGRlcyDpdGFibGlzc2VtZW50cy4gQSBub3RlciBxdWUgbGUgamV1IGRlIGRvbm7pZXMgcG91ciAyMDE1LCBkaXNwb25pYmxlIGljaSA6IGh0dHBzOi8vd3d3LmRhdGEuZ291di5mci9mci9kYXRhc2V0cy9pbmRpY2F0ZXVycy1kZS1yZXN1bHRhdC1kZXMtbHljZWVzLWRlbnNlaWduZW1lbnQtZ2VuZXJhbC1ldC10ZWNobm9sb2dpcXVlLywgYSDpdOkgcmV0cmF2YWlsbOkgcGFyZWlsbGVtZW50IGF1eCBkb25u6WVzIHBvdXIgMjAxNiBhZmluIGRlIG5lIHJldGVuaXIgcXVlIGxlcyBzaXggbeptZXMgdmFyaWFibGVzLCBkb250IGNlbGxlIOAgcHLpZGlyZS4gDQoNCkxhIHBy6XBhcmF0aW9uIGRlcyBkb25u6WVzIGltcGxpcXVlIGQnYWJvcmQgZGUgKip0cmFuc2Zvcm1lciBlbiB2YXJpYWJsZXMgYmluYWlyZXMqKiAoKmR1bW15IHZhcmlhYmxlcyopIGxlcyB0cm9pcyB2YXJpYWJsZXMgY2F06WdvcmlxdWVzIChzdGF0dXQgZGUgbCfpdGFibGlzc2VtZW50LCBvZmZyZSBkZSBmb3JtYXRpb24sIGTpcGFydGVtZW50KSBwdWlzIGRlICoqY2VudHJlciBldCBy6WR1aXJlKiogbGVzIHZhcmlhYmxlcyBwculkaWN0aXZlcy4gDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp0YXJnZXQgPC0gbHljZWVzX2JhY19kYXRhJHRhdXhfcmV1c3NpdGVfYmFjDQoNCiMgcHJlcHJvY2VzcyB0aGUgdHJhaW4gc2V0DQpkdW1taWVzIDwtIGR1bW15VmFycyh0YXV4X3JldXNzaXRlX2JhYyB+IC4sIGRhdGEgPSBseWNlZXNfYmFjX2RhdGEpDQp0cmFpbiA8LSBwcmVkaWN0KGR1bW1pZXMsIG5ld2RhdGEgPSBseWNlZXNfYmFjX2RhdGEpDQp0cmFpbiA8LSBkYXRhLmZyYW1lKHRyYWluKQ0KDQojIHRyYW5zZm9ybQ0KdHJhbnNmb3JtIDwtIHByZVByb2Nlc3ModHJhaW4sIG1ldGhvZCA9IGMoJ2NlbnRlcicsICdzY2FsZScpKQ0KdHJhaW4gPC0gcHJlZGljdCh0cmFuc2Zvcm0sIG5ld2RhdGEgPSB0cmFpbikNCg0KIyBhcHBlbmQgdGhlIHRhcmdldA0KdHJhaW4kdGF1eF9yZXVzc2l0ZV9iYWMgPC0gdGFyZ2V0DQoNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyB0ZXN0IGRhdGENCnRlc3RfMjAxNSA8LSByZWFkX2RlbGltKCJ0ZXN0XzIwMTUuY3N2IiwgIjsiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIHRyaW1fd3MgPSBUUlVFKQ0KdGVzdF8yMDE1IDwtIGRhdGEuZnJhbWUodGVzdF8yMDE1KQ0KDQp0ZXN0XzIwMTUkRGVwYXJ0ZW1lbnQgPC0gZmFjdG9yKHRlc3RfMjAxNSREZXBhcnRlbWVudCkNCnRlc3RfMjAxNSRTZWN0ZXVyIDwtIGZhY3Rvcih0ZXN0XzIwMTUkU2VjdGV1cikNCnRlc3RfMjAxNSRTdHJ1Y3R1cmUuUGVkYWdvZ2lxdWUgPC0gZmFjdG9yKHRlc3RfMjAxNSRTdHJ1Y3R1cmUuUGVkYWdvZ2lxdWUpDQoNCnRhcmdldF90ZXN0IDwtIHRlc3RfMjAxNSR0YXV4X3JldXNzaXRlX2JhYw0KDQp0ZXN0XzIwMTUgPC0gcHJlZGljdChkdW1taWVzLCBuZXdkYXRhID0gdGVzdF8yMDE1KQ0KdGVzdF8yMDE1IDwtIGRhdGEuZnJhbWUodGVzdF8yMDE1KQ0KDQp0ZXN0XzIwMTUgPC0gcHJlZGljdCh0cmFuc2Zvcm0sIG5ld2RhdGEgPSB0ZXN0XzIwMTUpDQoNCmBgYA0KDQojIyBNb2TpbGlzYXRpb24gZHUgdGF1eCBkZSBy6XVzc2l0ZSBhdSBiYWNjYWxhdXLpYXQNCg0KTGVzIG1vZOlsaXNhdGlvbnMgc3VpdmFudGVzIHNvbnQgZXNzYXnpZXMgcG91ciBlc3RpbWVyIGxhIGNhcGFjaXTpIGQndW4gbW9k6GxlIOAgcHLpZGlyZSBsZSB0YXV4IGRlIHLpdXNzaXRlIGF1IGJhY2NhbGF1culhdCBkJ3VuIGx5Y+llIGRvbm7pIDoNCg0KLSBy6WdyZXNzaW9uIGxpbulyYWlyZQ0KLSBzdXBwb3J0IHZlY3RvciBtYWNoaW5lcyAoU1ZNKQ0KLSBhcmJyZXMgZGUgZOljaXNpb24NCi0gculzZWF1IGRlIG5ldXJvbmVzDQotIGdyYWRpZW50IGJvb3N0aW5nIChYR2Jvb3N0KQ0KDQpMZXMgKipoeXBlcnBhcmFt6HRyZXMqKiBzb250IGNob2lzaXMgcGFyIHZhbGlkYXRpb24gY3JvaXPpZSAoKmNyb3NzIHZhbGlkYXRpb24qKSBjb21wdGUgdGVudSBkdSBwZXRpdCBub21icmUgZCdvYnNlcnZhdGlvbnMuIExlcyBtZWlsbGV1cnMgbW9k6GxlcyBzb250IGVuc3VpdGUgYXBwbGlxdelzIHN1ciBsZXMgZG9ubullcyAyMDE1IGFmaW4gZGUgY2FsY3VsZXIsIHBvdXIgY2hhcXVlIG9ic2VydmF0aW9uLCBsJ+ljYXJ0IGVudHJlIGxhIHBy6WRpY3Rpb24gZHUgbW9k6GxlIGV0IGxhIHZhbGV1ciBjb25zdGF06WUgZW4gMjAxNSBkdSB0YXV4IGRlIHLpdXNzaXRlIGF1IGJhY2NhbGF1culhdCBkZSBsJ+l0YWJsaXNzZW1lbnQuIExhIG1lc3VyZSBkZSBwZXJmb3JtYW5jZSByZXRlbnVlIGljaSBlc3QgbCdlcnJldXIgbW95ZW5uZSBhYnNvbHVlICgqTUFFLCBtZWFuIGFic29sdXRlIGVycm9yKikuIA0KDQojIyMgUulncmVzc2lvbiBsaW7pYWlyZQ0KDQpgYGB7cn0NCiMgbGluZWFyIHJlZ3Jlc3Npb24NCmNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDEwLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHJlcGVhdHMgPSA1KQ0KDQpscjEgPC0gdHJhaW4odGF1eF9yZXVzc2l0ZV9iYWMgfiAuLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJsbSIsIHRyQ29udHJvbCA9IGNvbnRyb2wpDQpzdW1tYXJ5KGxyMSkNCmxyMSAjIE1BRSA9IDMuNzMgOyBSMiA9IDI1ICUNCg0KIyBldmFsdWF0ZSBvbiB0ZXN0IHNldA0KcHJlZGljdGVkX2xyIDwtIHByZWRpY3QobHIxLCBuZXdkYXRhID0gdGVzdF8yMDE1KQ0KcHJlZGljdGVkX2VyciA8LSBtZWFuKGFicyh0YXJnZXRfdGVzdCAtIHByZWRpY3RlZF9scikpDQpwcmVkaWN0ZWRfZXJyICMgTUFFIG9uIHRlc3Qgc2V0IDogMy42OA0KDQpgYGANCg0KIyMjIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzIChTVk0pDQoNCmBgYHtyfQ0KIyBsaW5lYXINCnN2bWxpbmVhcjEgPC0gdHJhaW4odGF1eF9yZXVzc2l0ZV9iYWMgfiAuLCBkYXRhID0gdHJhaW4sIA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICdzdm1MaW5lYXInLCB0ckNvbnRyb2wgPSBjb250cm9sKQ0Kc3ZtbGluZWFyMSAjIE1BRSAzLjYxODY3OA0KDQpzdm1ncmlkMiA8LSBleHBhbmQuZ3JpZChjb3N0ID0gYygwLjI1LDAuNSwwLjc1KSkNCnN2bWxpbmVhcjIgPC0gdHJhaW4odGF1eF9yZXVzc2l0ZV9iYWMgfiAuLCBkYXRhID0gdHJhaW4sIA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICdzdm1MaW5lYXIyJywgdHJDb250cm9sID0gY29udHJvbCwgdHVuZUdyaWQgPSBzdm1ncmlkMikNCnN2bWxpbmVhcjIgIyAzLjYwNTk4NQ0KDQojIHBvbHlub21pYWwNCnN2bXBvbHlfZ3JpZCA8LSBleHBhbmQuZ3JpZChkZWdyZWUgPSBjKDIsMyksIHNjYWxlID0gRiwgQyA9IGMoMC4yNSwwLjUsMC43NSkpDQpzdm1wb2x5IDwtIHRyYWluKHRhdXhfcmV1c3NpdGVfYmFjIH4gLiwgZGF0YSA9IHRyYWluLCANCiAgICAgICAgICAgICAgICBtZXRob2QgPSAnc3ZtUG9seScsIHRyQ29udHJvbCA9IGNvbnRyb2wsIHR1bmVHcmlkID0gc3ZtcG9seV9ncmlkKQ0Kc3ZtcG9seSAjIE1BRSBhcm91bmQgNC40OCBmb3IgYWxsDQoNCiMgcmFkaWFsDQpzdm1yYWRpYWwgPC0gdHJhaW4odGF1eF9yZXVzc2l0ZV9iYWMgfiAuLCBkYXRhID0gdHJhaW4sIA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICdzdm1SYWRpYWwnLCB0ckNvbnRyb2wgPSBjb250cm9sKQ0Kc3ZtcmFkaWFsICMgYmVzdCBtb2RlbCA6IEMgPSAxLCBzaWdtYSA9IDAuMDA2MTkwOTIzLiBNQUUgPSAzLjUwOTMzNA0KDQojIGV2YWx1YXRlIG9uIHRlc3Qgc2V0DQpwcmVkaWN0ZWRfc3ZtcmFkaWFsIDwtIHByZWRpY3Qoc3ZtcmFkaWFsLCBuZXdkYXRhID0gdGVzdF8yMDE1KQ0KcHJlZGljdGVkX2Vycl9zdm1yYWRpYWwgPC0gbWVhbihhYnModGFyZ2V0X3Rlc3QgLSBwcmVkaWN0ZWRfc3ZtcmFkaWFsKSkNCnByZWRpY3RlZF9lcnJfc3ZtcmFkaWFsICMgTUFFIG9uIHRlc3Qgc2V0IDogMy4zOTcwOTENCg0KYGBgDQoNCiMjIyBNb2TobGVzIOAgYmFzZSBkJ2FyYnJlcyBkZSBk6WNpc2lvbg0KDQpgYGB7cn0NCmJhZ2dlZGNhcnQgPC0gdHJhaW4odGF1eF9yZXVzc2l0ZV9iYWMgfiAuLCBkYXRhID0gdHJhaW4sIA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICd0cmVlYmFnJywgdHJDb250cm9sID0gY29udHJvbCkNCmJhZ2dlZGNhcnQgIyBNQUUgOiAzLjY4NDg4Ng0KDQojIGV2YWx1YXRlIG9uIHRlc3Qgc2V0DQpwcmVkaWN0ZWRfYmFnZ2VkY2FydCA8LSBwcmVkaWN0KGJhZ2dlZGNhcnQsIG5ld2RhdGEgPSB0ZXN0XzIwMTUpDQpwcmVkaWN0ZWRfZXJyX2JhZ2dlZGNhcnQgPC0gbWVhbihhYnModGFyZ2V0X3Rlc3QgLSBwcmVkaWN0ZWRfYmFnZ2VkY2FydCkpDQpwcmVkaWN0ZWRfZXJyX2JhZ2dlZGNhcnQgIyAzLjc1NjU4OQ0KDQpgYGANCg0KYGBge3J9DQpycGFydDEgPC0gdHJhaW4odGF1eF9yZXVzc2l0ZV9iYWMgfiAuLCBkYXRhID0gdHJhaW4sIA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICdycGFydDInLCBtYXhkZXB0aCA9IDMsIA0KICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGNvbnRyb2wpDQpycGFydDEgIyBiZXN0IE1BRSBmb3IgZGVwdGggPSAzIDogMy43NDU3MDcNCg0KIyBldmFsdWF0ZSBvbiB0ZXN0IHNldA0KcHJlZGljdGVkX3JwYXJ0IDwtIHByZWRpY3QocnBhcnQxLCBuZXdkYXRhID0gdGVzdF8yMDE1KQ0KcHJlZGljdGVkX2Vycl9ycGFydCA8LSBtZWFuKGFicyh0YXJnZXRfdGVzdCAtIHByZWRpY3RlZF9ycGFydCkpDQpwcmVkaWN0ZWRfZXJyX3JwYXJ0ICMgTUFFIDogMy44NDM5MjkNCg0KYGBgDQoNCg0KIyMjIFLpc2VhdSBkZSBuZXVyb25lcw0KDQpgYGB7cn0NCmgyby5pbml0KG50aHJlYWRzID0gLTEsIG1heF9tZW1fc2l6ZSA9ICc0ZycpDQoNCiMgcHJlcGFyZSB0aGUgZGF0YXNldHMNCnRyYWluX2gybyA8LSBhcy5oMm8odHJhaW4pDQp0ZXN0X2gybyA8LSBhcy5oMm8odGVzdF8yMDE1KQ0KdGFyZ2V0X3Rlc3RfaDJvIDwtIGFzLmgybyh0YXJnZXRfdGVzdCkNCg0KWSA8LSAidGF1eF9yZXVzc2l0ZV9iYWMiDQpYIDwtIHNldGRpZmYobmFtZXModHJhaW5faDJvKSwgWSkNCg0KIyB0cmFpbiB0aGUgbmV1cmFsIG5ldHdvcmsNCkRMLmgyby4xIDwtIGgyby5kZWVwbGVhcm5pbmcoeCA9IFgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gWSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgPSB0cmFpbl9oMm8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5mb2xkcyA9IDUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gPSAiZ2F1c3NpYW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3RpdmF0aW9uID0gIk1heG91dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbiA9IGMoMzIsMzIsMzIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFyc2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGwxID0gMC41LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsMiA9IDAuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXBvY2hzID0gMTAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFuZGFyZGl6ZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZV9pbXBvcnRhbmNlcyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSAyKQ0KREwuaDJvLjEgIyBNQUU6IDQuMDExMzQ2DQoNCkRMLmgyby4yIDwtIGgyby5kZWVwbGVhcm5pbmcoeCA9IFgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gWSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgPSB0cmFpbl9oMm8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5mb2xkcyA9IDUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gPSAiZ2F1c3NpYW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3RpdmF0aW9uID0gIk1heG91dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbiA9IGMoMTAwLDEwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYXJzZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbDEgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGwyID0gMC41LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcG9jaHMgPSAxMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlX2ltcG9ydGFuY2VzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDIpDQpETC5oMm8uMiAjIE1BRTogMy43NTI3MDMsIG5vIG92ZXJmaXQNCg0KcHJlZGljdGVkX2RsMiA8LSBoMm8ucHJlZGljdChETC5oMm8uMiwgbmV3ZGF0YSA9IHRlc3RfaDJvKQ0KcHJlZGljdGVkX2RsMl9lcnIgPC0gbWVhbihhYnMocHJlZGljdGVkX2RsMiAtIHRhcmdldF90ZXN0X2gybykpDQpwcmVkaWN0ZWRfZGwyX2VyciAjIHRlc3QgTUFFIDogNC41MDg0OTYNCg0KDQpgYGANCg0KDQojIyMgWEdib29zdA0KDQpgYGB7cn0NCg0KIyBwcmVwYXJlIHRoZSB0cmFpbiBzZXQNCmR0cmFpbiA8LSB4Z2IuRE1hdHJpeChhcy5tYXRyaXgodHJhaW5bLCAtMTEzXSksIGxhYmVsID0gYXMubWF0cml4KHRyYWluWywgMTEzXSkpDQoNCiMgdHJhaW4gd2l0aCBjcm9zcyB2YWxpZGF0aW9uDQp4Z2IgPC0geGdiLmN2KGRhdGEgPSBkdHJhaW4sIA0KICAgICAgICAgICAgIG5yb3VuZHMgPSAxMDAsIA0KICAgICAgICAgICAgIG50aHJlYWQgPSAyLCANCiAgICAgICAgICAgICBuZm9sZCA9IDUsIA0KICAgICAgICAgICAgIG1ldHJpY3MgPSAibWFlIiwNCiAgICAgICAgICAgICBtYXhfZGVwdGggPSAzLCANCiAgICAgICAgICAgICBldGEgPSAwLjEsIA0KICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJyZWc6bGluZWFyIikNCnhnYg0KDQojIG1vZGVsDQp4Z2JfbW9kZWwgPC0geGdib29zdChkYXRhID0gZHRyYWluLCANCiAgICAgICAgICAgICBucm91bmRzID0gMTAwLCANCiAgICAgICAgICAgICBudGhyZWFkID0gMiwgDQogICAgICAgICAgICAgbWV0cmljcyA9ICJtYWUiLA0KICAgICAgICAgICAgIG1heF9kZXB0aCA9IDMsIA0KICAgICAgICAgICAgIGV0YSA9IDAuMSwgDQogICAgICAgICAgICAgb2JqZWN0aXZlID0gInJlZzpsaW5lYXIiKQ0KDQojIGV2YWx1YXRlIG9uIHRlc3Qgc2V0DQp4Z2JfcHJlZGljdGVkIDwtIHByZWRpY3QoeGdiX21vZGVsLCBuZXdkYXRhID0gYXMubWF0cml4KHRlc3RfMjAxNSkpDQp4Z2JfZXJyIDwtIG1lYW4oYWJzKHRhcmdldF90ZXN0IC0geGdiX3ByZWRpY3RlZCkpDQp4Z2JfZXJyICMgTUFFIG9uIHRlc3Qgc2V0IDogMy41OQ0KDQpgYGANCg0KIyMgQ29uY2x1c2lvbg0KDQpMYSBtZWlsbGV1cmUgcHLpZGljdGlvbiBhIOl06SBy6WFsaXPpZSBwYXIgbGUgKipTVk0g4CBub3lhdSBub24tbGlu6WFpcmUgcmFkaWFsKiogKCpyYWRpYWwga2VybmVsKiksIGF2ZWMgdW5lIGVycmV1ciBtb3llbm5lIGFic29sdWUgZGUgKiozLDM5KiouIEF1dHJlbWVudCBkaXQsIHNpIGxlIG1vZOhsZSBwculkaXQsIHBvdXIgdW4g6XRhYmxpc3NlbWVudCBkb25u6SwgdW4gdGF1eCBkZSBy6XVzc2l0ZSBkZSA4NSAlIGF1IGJhY2NhbGF1culhdCwgbGUgdulyaXRhYmxlIHRhdXggc2Ugc2l0dWUgcHJvYmFibGVtZW50IGVudHJlIDgxLDYgJSBldCA4OCw0ICUuIExhIHBsdXBhcnQgZGVzIG1vZOlsaXNhdGlvbnMgZGFucyBjZXR0ZSDpdHVkZSBhdHRlaWduZW50IHVuZSBlcnJldXIgbW95ZW5uZSBkJ2Vudmlyb24gMyw2Lg0KDQpDZXR0ZSBwculjaXNpb24gZXN0LWVsbGUgc2F0aXNmYWlzYW50ZSA/IEQndW4gY/R06SwgZWxsZSBsJ2VzdCBjb21wdGUgdGVudSBkdSBmYWlibGUgbm9tYnJlICg1KSBkZSB2YXJpYWJsZXMgcHLpZGljdGl2ZXMgZGlzcG9uaWJsZXMgZGFucyBsZSBqZXUgZGUgZG9ubullcy4gRCd1biBhdXRyZSBj9HTpLCBlbGxlIHMnYXbocmUgbW9kZXN0ZSBhdSByZWdhcmQgZGUgbGEgZGlzdHJpYnV0aW9uIGR1IHRhdXggZGUgcul1c3NpdGUgYXUgYmFjY2FsYXVy6WF0LCBhc3NleiBjb25jZW50cullIGF1dG91ciBkZSA5MyAlLCBhdmVjIHVuIOljYXJ0LXR5cGUgZGUgNiBwb2ludHMgcG91ciAyMDE2LiBDZSBy6XN1bHRhdCBuZSBkb2l0IHBhcyDpdG9ubmVyIGF1IHZ1IGRlIGwnZXhwbG9yYXRpb24gZGVzIGRvbm7pZXMgKGZhaWJsZXMgbml2ZWF1eCBkZXMgY29yculsYXRpb25zLCBmYWlibGVzIGRpZmbpcmVuY2VzIGRlIHRhdXggZGUgcul1c3NpdGUgZW50cmUgbGVzIGTpcGFydGVtZW50cywgZXRjLikuIA0KDQpQb3VyIGFt6Wxpb3JlciBsYSBwculjaXNpb24gZHUgbW9k6GxlLCBkZSAqKm5vdXZlbGxlcyB2YXJpYWJsZXMgcHLpZGljdGl2ZXMqKiBzZXJhaWVudCByZXF1aXNlcy4gUGFyIGh5cG90aOhzZSwgb24gcGV1dCBzb25nZXIgYXUgYnVkZ2V0IGRlIGwn6XRhYmxpc3NlbWVudCByYXBwb3J06SBhdSBub21icmUgZCfpbOh2ZXMgZW4gdGVybWluYWxlLCBhdSBub21icmUgZCfpbOh2ZXMgcGFyIGVuc2VpZ25hbnQsIGV0Yy4gRGUgbeptZSwgbGUgbml2ZWF1IGludGVyY29tbXVuYWwgcG91cnJhaXQgcydhdulyZXIgcGx1cyB1dGlsZSBxdWUgbGUgbml2ZWF1IGTpcGFydGVtZW50YWwgY2FyIGlsIGVzdCBwcm9iYWJsZSBxdWUgbGEgZGlzcGVyc2lvbiBhdSBzZWluIGRlIGNoYXF1ZSBk6XBhcnRlbWVudCBzb2l0IHN1cOlyaWV1cmUg4CBsYSBkaXNwZXJzaW9uIGVudHJlIGTpcGFydGVtZW50cy4=