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 :

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 :

# 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é :

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=