Sommaire
- Objectif de l’étude
- Données
- Transformation du jeu de données
- Modélisation
- Evaluation de la modélisation
- Conclusion
Objectif de l’étude
Cette étude vise à modéliser le niveau des dépassements d’honoraires des professionnels de santé en France. Ce modèle peut être utile à l’Assurance maladie pour anticiper les dépassements d’honoraires, pour détecter des tendances ou contribuer à repérer des anomalies dans l’évolution des dépassements.
Données
Nous recourons aux données mises à disposition par l’Assurance maladie sur le site d’ouverture des données publiques https://www.data.gouv.fr
Au moment de la réalisation de cette étude, les données les plus récentes sont celles du mois d’août 2018. Toutes les données sont disponibles à l’adresse suivante, ainsi que le dictionnaire des variables : https://www.data.gouv.fr/fr/datasets/depenses-d-assurance-maladie-hors-prestations-hospitalieres-donnees-nationales/
Le code qui suit télécharge les extensions requises ainsi que les données, puis prépare ces dernières en modifiant la nature de certaines variables et en harmonisant la syntaxe de séparation des décimales, la rendant compatible avec R. De plus, certaines colonnes sont supprimées :
- certaines constituent de fait, sous forme de codage, des doublons avec d’autres colonnes : statistique mensuelle, etc.
- certaines contiennent de nombreux niveaux, nous préférons conserver les colonnes qui agrègent ces niveaux : spécialité prescripteur, etc.
- la colonne remboursement final est abandonnée, nous préférons conserver celle qui correspond à la base du remboursement
Dès lors qu’un niveau plus agrégé a été retenu pour certaines variables, il apparait qu’environ 10 % des observations sont en double ; elles sont donc retirées du jeu de données, qui contient au final environ 185 000 observations et 15 colonnes, dont la variable à prédire (dépassements d’honoraires, “dep_mon”).
La visualisation des données est rendue difficile par la grande dispersion de la distribution de certaines d’entre elles, en particulier des dépassements d’honoraires, dont la médiane est 0 tandis que la moyenne est de 1789 euros et l’écart type de 108 262 euros ; le maximum s’élève à 31 524 586 euros, le minimum à -3900 euros, soit un écart extrêmement élevé qui rendra difficile la prédiction. Pour 83 % des observations, aucun dépassement d’honoraire n’est constaté sur ce mois d’août 2018. A noter que la présence de valeurs négatives dans le jeu de données pour plusieurs colonnes s’explique par des mesures de régularisation pour des soins anciens (source : discussion relative à ce jeu de données sur le site data.gouv.fr) ; il convient de conserver ces valeurs négatives puisque de telles régularisations sont opérées chaque mois.
# required packages
library(readr)
library(caret)
library(h2o)
August2018 <- read_delim("n201808.csv", ";", escape_double = FALSE, locale = locale(encoding = "ISO-8859-1"), trim_ws = TRUE)
August2018 <- data.frame(August2018)
# change the nature of some columns
August2018$l_serie <- factor(August2018$l_serie)
August2018$l_prs_nat <- factor(August2018$l_prs_nat)
August2018$sns_date <- factor(August2018$sns_date)
August2018$l_asu_nat <- factor(August2018$l_asu_nat)
August2018$l_cpl_cod <- factor(August2018$l_cpl_cod)
August2018$l_pre_spe1 <- factor(August2018$l_pre_spe1)
August2018$l_exe_spe1 <- factor(August2018$l_exe_spe1)
August2018$l_pre_stj1 <- factor(August2018$l_pre_stj1)
August2018$l_exe_stj1 <- factor(August2018$l_exe_stj1)
August2018$rem_date <- factor(August2018$rem_date)
# syntax
August2018$dep_mon <- gsub('.', "", fixed = TRUE, x = August2018$dep_mon)
August2018$dep_mon <- gsub(',', ".", fixed = TRUE, x = August2018$dep_mon)
August2018$dep_mon <- as.numeric(August2018$dep_mon)
August2018$rec_mon <- gsub('.', "", fixed = TRUE, x = August2018$rec_mon)
August2018$rec_mon <- gsub(',', ".", fixed = TRUE, x = August2018$rec_mon)
August2018$rec_mon <- as.numeric(August2018$rec_mon)
August2018$act_coe <- gsub('.', "", fixed = TRUE, x = August2018$act_coe)
August2018$act_coe <- gsub(',', ".", fixed = TRUE, x = August2018$act_coe)
August2018$act_coe <- as.numeric(August2018$act_coe)
# remove duplicated or useless columns
August2018$asu_nat <- NULL
August2018$prs_nat <- NULL
August2018$SERIE <- NULL
August2018$cpl_cod <- NULL
August2018$pre_stj1 <- NULL
August2018$exe_stj1 <- NULL
August2018$l_pre_spe <- NULL # many levels, use the grouping column instead (l_pre_spe1)
August2018$pre_spe <- NULL # many levels, use the grouping column instead (l_pre_spe1)
August2018$pre_spe1 <- NULL
August2018$l_exe_spe <- NULL # many levels, use the grouping column instead (l_exe_spe1)
August2018$exe_spe <- NULL # many levels, use the grouping column instead (l_exe_spe1)
August2018$exe_spe1 <- NULL
August2018$rem_mon <- NULL # keep instead the reimbursement base and the rate
# duplicated rows
cat("The number of duplicated rows is", nrow(August2018) - nrow(unique(August2018)))
# There are now 20725 duplicated rows (approximately 10 % of the dataset) because we removed some columns to keep a more aggregated level (l_exe_spe1, l_pre_spe1)
August2018 <- unique(August2018) # remove the duplicated rows
# target variable
summary(August2018$dep_mon)
sd(August2018$dep_mon)
sum(August2018$dep_mon == 0) / nrow(August2018)
Transformation du jeu de données
Avant d’opérer la modélisation des dépassements d’honoraires, le jeu de données est coupée aléatoirement en deux, l’un pour entraîner les modèles (avec 75 % des observations), l’autre pour affiner leurs paramètres et réduire le sur-apprentissage (overfitting).
Les variables discrètes sont encodées de manière binaire (one hot encoding). Les variables prédictives sont transformées en les centrant et les réduisant et en appliquant la méthode de Yeo-Johnson. En outre, les nombreuses variables binaires dont la variance est très faible sont retirées des jeux de données. Il demeure 33 variables, dont la variable prédictive qui n’a subi aucune transformation.
Enfin, les noms des colonnes sont modifiées afin d’éviter les caractères spéciaux, qui rendent leur lecture difficile voire impossible dans l’interface d’h2o à laquelle nous recourerons pour modéliser.
# Split the training set into a train and a test set
# Step 1: Get row numbers for the training data
set.seed(1)
trainRowNumbers <- createDataPartition(August2018$dep_mon, p = 0.75, list = FALSE)
# Step 2: Create the training dataset
trainData <- August2018[trainRowNumbers,]
# Step 3: Create the test dataset
testData <- August2018[-trainRowNumbers,]
# Store Y for later use.
y <- trainData$dep_mon
y_test <- testData$dep_mon
# pre process the training data
# One hot encoding
dummies_model <- dummyVars(dep_mon ~ ., data = trainData)
trainData <- predict(dummies_model, newdata = trainData)
trainData <- data.frame(trainData)
# Transform the training data
preProcess_yeojohnson_model <- preProcess(trainData, method = c("center", "scale", "YeoJohnson", "nzv"))
trainData <- predict(preProcess_yeojohnson_model, newdata = trainData)
# Append the training response variable, that remains untransformed
trainData$dep_mon <- y
# pre process the test data
testData <- predict(dummies_model, newdata = testData) # One hot encoding
testData <- data.frame(testData)
testData <- predict(preProcess_yeojohnson_model, newdata = testData) # Transform
testData$dep_mon <- y_test # Append the response variable
# change the columns'names to avoid special characters
# training set
colnames(trainData)[1:21] <- c("lserieAMIsoinsinfirmiers", "lserieCSpecialistes",
"lserieTotalfraisdedeplacementdesauxiliairesmedicaux", "remdate201807",
"remdate201808", "snsdate201804", "snsdate201805", "snsdate201806",
"snsdate201807", "snsdate201808", "lasunatAccidentduTravailMaladieProfessionnelle",
"lasunatMaladie", "lasunatMaternite", "lcplcodMajorationdenuit", "lcplcodSansmajoration",
"REMTAU", "lprespe1Autres", "lprespe1MedecinsOmnipraticiensliberaux",
"lprespe1MedecinsSpecialistesliberaux", "lprestj1Liberal", "lprestj1Salarie" )
colnames(trainData)[22:33] <- c("lexespe1Autres", "lexespe1Infirmiers", "lexespe1Laboratoires",
"lexespe1MasseursKinesitherapeutes", "lexespe1MedecinsOmnipraticiens",
"lexespe1MedecinsSpecialistes", "lexestj1Liberal", "lexestj1Salarie", "recmon",
"actdnb", "actcoe", "depmon")
# test set
colnames(testData)[1:21] <- c("lserieAMIsoinsinfirmiers", "lserieCSpecialistes",
"lserieTotalfraisdedeplacementdesauxiliairesmedicaux", "remdate201807",
"remdate201808", "snsdate201804", "snsdate201805", "snsdate201806",
"snsdate201807", "snsdate201808", "lasunatAccidentduTravailMaladieProfessionnelle",
"lasunatMaladie", "lasunatMaternite", "lcplcodMajorationdenuit", "lcplcodSansmajoration",
"REMTAU", "lprespe1Autres", "lprespe1MedecinsOmnipraticiensliberaux",
"lprespe1MedecinsSpecialistesliberaux", "lprestj1Liberal", "lprestj1Salarie")
colnames(testData)[22:33] <- c("lexespe1Autres", "lexespe1Infirmiers", "lexespe1Laboratoires",
"lexespe1MasseursKinesitherapeutes", "lexespe1MedecinsOmnipraticiens",
"lexespe1MedecinsSpecialistes", "lexestj1Liberal", "lexestj1Salarie", "recmon",
"actdnb", "actcoe", "depmon")
Modélisation
Nous choisissons de traiter la modélisation des dépassements d’honoraires en problème de régression car il existe une très grande diversité dans le niveau des dépassements. Autrement dit, la question est moins de savoir si un professionnel de santé recourt aux dépassements d’honoraires, que de savoir dans quelle mesure le prix qu’il pratique est supérieur au tarif de convention fixé pour chaque acte médical par la sécurité sociale.
Les algorithmes suivants sont utilisés :
- Modèle linéaire généralisé
- Forêts aléatoires
- Réseaux de neurones
- Gradient boosting
Comme métrique de performance, nous retenons l’erreur moyenne absolue (mean absolute error, MAE), dont l’interprétation est simple.
Modèle linéaire généralisé
Sans surprise, les résultats obtenus du modèle linéaire généralisé sont médiocres: la MAE s’élève à 3639 euros, certes sans fort sur-apprentissage. Des modèles plus complexes sont donc nécessaires.
h2o.init(nthreads = -1, max_mem_size = '4g')
training_data_yeojohnson_h2o <- as.h2o(trainData)
testing_data_yeojohnson_h2o <- as.h2o(testData)
Y <- "depmon"
X <- setdiff(names(training_data_yeojohnson_h2o), Y)
# train the model
glm1 <- h2o.glm(y = Y,
x = X,
training_frame = training_data_yeojohnson_h2o,
family = "gaussian",
nfolds = 5,
alpha = 0.5,
seed = 1)
glm1
# 3229 on both training and cross validation data
# train the model
glm2 <- h2o.glm(y = Y,
x = X,
training_frame = training_data_yeojohnson_h2o,
family = "gaussian",
nfolds = 5,
seed = 1)
glm2
# same
glm2.predicted <- h2o.predict(glm2, newdata = testing_data_yeojohnson_h2o)
glm2.err <- mean(abs(testing_data_yeojohnson_h2o$depmon- glm2.predicted))
glm2.err # MAE on test set : 3639.823
Forêts aléatoires
Les différentes forêts aléatoires apportent de meilleurs résultats, en divisant par deux l’erreur moyenne du modèle linéraire généralisé, qui tombe à environ 1800 euros, sans sur-apprentissage d’après la validation croisée (cross validation). La manipulation des hyperparamètres (nombre d’arbres, etc.) n’a pas permis d’améliorer l’erreur moyenne.
h2o.init(nthreads = -1, max_mem_size = '4g')
# prepare the datasets
training_data_yeojohnson_h2o <- as.h2o(trainData)
testing_data_yeojohnson_h2o <- as.h2o(testData)
Y <- "depmon"
X <- setdiff(names(training_data_yeojohnson_h2o), Y)
# train the model
set.seed(1)
drf1 <- h2o.randomForest(training_frame = training_data_yeojohnson_h2o,
nfolds = 3,
y = Y,
x = X,
ntrees = 50,
max_depth = 15,
min_rows = 2,
mtries = -1,
seed = 1)
drf1
# MAE : 2019.645 on cross validation data
# change the parameters
drf2 <- h2o.randomForest(training_frame = training_data_yeojohnson_h2o,
nfolds = 5,
y = Y,
x = X,
ntrees = 100,
max_depth = 15,
min_rows = 2,
mtries = -1,
seed = 2)
drf2
# MAE : 1863.483 on cross validation data, 1818.103 on training
Réseaux de neurones
Plusieurs réseaux de neurones relativement simples ont été essayés à travers trois fonctions d’activations différentes (Tanh, Maxout, ReLU), des architectures plus ou moins profondes, etc. Les meilleurs réseaux sont parvenus à diminuer l’erreur moyenne à moins de 1700 euros. Des tentatives (non apparentes ici) de régularisation n’ont pas permis de réduire le sur-apprentissage, patent au regard de la validation croisée.
h2o.init(nthreads = -1, max_mem_size = '4g')
training_data_yeojohnson_h2o <- as.h2o(trainData)
testing_data_yeojohnson_h2o <- as.h2o(testData)
Y <- "depmon"
X <- setdiff(names(training_data_yeojohnson_h2o), Y)
# train the model
DL.h2o.1 <- h2o.deeplearning(x = X,
y = Y,
training_frame = training_data_yeojohnson_h2o,
nfolds = 5,
distribution = "gaussian",
activation = "Maxout",
hidden = c(32,32,32),
sparse = TRUE,
l1 = 0,
epochs = 100,
variable_importances = TRUE,
standardize = FALSE,
seed = 1)
DL.h2o.1
# MAE : 1402 on training and 2005 on cross validation data
DL.h2o.2 <- h2o.deeplearning(x = X,
y = Y,
training_frame = training_data_yeojohnson_h2o,
nfolds = 3,
distribution = "gaussian",
activation = "Tanh",
hidden = c(32,32,32),
sparse = TRUE,
l1 = 0,
epochs = 100,
variable_importances = TRUE,
standardize = FALSE,
seed = 2)
DL.h2o.2
# MAE : 1060.002 on training and 1690.504 on cross validation data
DL.h2o.3 <- h2o.deeplearning(x = X,
y = Y,
training_frame = training_data_yeojohnson_h2o,
nfolds = 3,
distribution = "gaussian",
activation = "Rectifier",
hidden = c(32,32,32),
sparse = TRUE,
l1 = 0,
epochs = 100,
variable_importances = TRUE,
standardize = FALSE,
seed = 2)
DL.h2o.3
# MAE : 1360.989 on training and 1794.588 on cross validation data
DL.h2o.4 <- h2o.deeplearning(x = X,
y = Y,
training_frame = training_data_yeojohnson_h2o,
nfolds = 3,
distribution = "gaussian",
activation = "Tanh",
hidden = c(32,32,32,32,32),
sparse = TRUE,
l1 = 0,
epochs = 150,
variable_importances = TRUE,
standardize = FALSE,
seed = 4)
DL.h2o.4
# MAE : 1691.333 on cross validation data
DL.h2o.5 <- h2o.deeplearning(x = X,
y = Y,
training_frame = training_data_yeojohnson_h2o,
nfolds = 3,
distribution = "gaussian",
activation = "Tanh",
hidden = c(100,100),
sparse = TRUE,
l1 = 0,
epochs = 100,
variable_importances = TRUE,
standardize = FALSE,
seed = 5)
DL.h2o.5
# MAE: 474.2974 on training and 1684.774 on cross validation data
Gradient boosting
Enfin, la méthode ensembliste du gradient boosting est mobilisée et donne des résultats analogues aux meilleurs réseaux de neurones, avec une MAE d’environ 1700 euros. Le sur-apprentissage est de nouveau élevé, malgré des tentatives de régularisation.
h2o.init(nthreads = -1, max_mem_size = '4g')
# prepare the datasets
training_data_yeojohnson_h2o <- as.h2o(trainData)
testing_data_yeojohnson_h2o <- as.h2o(testData)
Y <- "depmon"
X <- setdiff(names(training_data_yeojohnson_h2o), Y)
# train the model
set.seed(5)
gbm.h2o.1 <- h2o.gbm(training_frame = training_data_yeojohnson_h2o,
x = X,
y = Y,
distribution = "gaussian",
ntrees = 50,
max_depth = 10,
learn_rate = 0.2,
min_rows = 2,
nfolds = 5)
gbm.h2o.1
# MAE : 483 on training ; 2023 on cross validation data
gbm.h2o.2 <- h2o.gbm(training_frame = training_data_yeojohnson_h2o,
x = X,
y = Y,
distribution = "gaussian",
ntrees = 50,
max_depth = 10,
learn_rate = 0.1,
min_rows = 2,
nfolds = 5)
gbm.h2o.2
# 593 on training ; 1886.904 on cross validation data
gbm.h2o.3 <- h2o.gbm(training_frame = training_data_yeojohnson_h2o,
x = X,
y = Y,
distribution = "gaussian",
ntrees = 50,
max_depth = 15,
learn_rate = 0.1,
min_rows = 2,
nfolds = 5)
gbm.h2o.3
# 288.4684 on training ; 1855.77 on cross validation data
gbm.h2o.4 <- h2o.gbm(training_frame = training_data_yeojohnson_h2o,
x = X,
y = Y,
distribution = "gaussian",
ntrees = 30,
max_depth = 15,
learn_rate = 0.1,
min_rows = 2,
nfolds = 5)
gbm.h2o.4
# 463.5689 on training ; 1756.26 on cross validation data
gbm.h2o.5 <- h2o.gbm(training_frame = training_data_yeojohnson_h2o,
x = X,
y = Y,
distribution = "gaussian",
ntrees = 30,
max_depth = 15,
learn_rate = 0.1,
min_rows = 2,
col_sample_rate = 0.8,
nfolds = 5)
gbm.h2o.5
# MAE : 1706.235 on cross validation data
gbm5.predicted <- h2o.predict(gbm.h2o.5, newdata = testing_data_yeojohnson_h2o)
gbm5.err <- mean(abs(testing_data_yeojohnson_h2o$depmon - gbm5.predicted))
gbm5.err # MAE : 2141.069 on test set
Entrainement final du meilleur modèle
Nous choisissons le dernier modèle ensembliste (gbm.h2o.5) comme meilleur modèle dans la mesure où il parvient à des résultats très comparables aux meilleurs réseaux de neurones mais que son temps de calcul est bien plus rapide.
Aussi, nous pouvons désormais rassembler en un jeu de données unique les deux jeux qui nous ont permis de choisir ce meilleur modèle, et entraîner le modèle sur ce grand jeu de données du mois d’août 2018 afin d’améliorer sa puissance prédictive.
h2o.init(nthreads = -1, max_mem_size = '4g')
# merge the datasets
TrainingAugust2018 <- rbind.data.frame(trainData, testData)
# prepare the full dataset
TrainingAugust2018_h2o <- as.h2o(TrainingAugust2018)
Y <- "depmon"
X <- setdiff(names(TrainingAugust2018_h2o), Y)
# train the final model
gbm.h2o.final <- h2o.gbm(training_frame = TrainingAugust2018_h2o,
x = X,
y = Y,
distribution = "gaussian",
ntrees = 30,
max_depth = 15,
learn_rate = 0.1,
min_rows = 2,
col_sample_rate = 0.8)
h2o.varimp_plot(gbm.h2o.final, 32) # most important variables to predict the response
Evaluation de la modélisation
Enfin, nous utilisons notre meilleur modèle, désormais entraîné sur un grand jeu de données, et l’appliquons sur un nouveau jeu de données, celui du mois d’août 2017, disponible sur le site data.gouv.fr, afin d’évaluer la capacité de notre modèle à se généraliser. Nous retenons le même mois car il est possible qu’existent certaines régularités mensuelles chaque année.
Le code suivant prépare les données du mois d’août 2017 pour qu’elles soient dans le même format que celles du mois d’août 2018, sans quoi le modèle ne trouverait pas à s’appliquer puisque les variables seraient différentes.
Etant données les observations des variables prédictives, notre modèle parvient à prédire le niveau des dépassements d’honoraires avec une erreur moyenne de 1156 euros par rapport aux dépassements réellement constatés.
# load the new test dataset
August2017 <- read_delim("N201708.csv", ";", escape_double = FALSE, locale = locale(encoding = "ISO-8859-1"), trim_ws = TRUE)
August2017 <- data.frame(August2017)
# remove duplicated or useless columns
August2017$asu_nat <- NULL
August2017$prs_nat <- NULL
August2017$SERIE <- NULL
August2017$cpl_cod <- NULL
August2017$pre_stj1 <- NULL
August2017$exe_stj1 <- NULL
August2017$l_pre_spe <- NULL
August2017$pre_spe <- NULL
August2017$pre_spe1 <- NULL
August2017$l_exe_spe <- NULL
August2017$exe_spe <- NULL
August2017$exe_spe1 <- NULL
August2017$rem_mon <- NULL
# change the nature of columns
August2017$l_serie <- factor(August2017$l_serie)
August2017$l_prs_nat <- factor(August2017$l_prs_nat)
August2017$sns_date <- factor(August2017$sns_date)
August2017$l_asu_nat <- factor(August2017$l_asu_nat)
August2017$l_cpl_cod <- factor(August2017$l_cpl_cod)
August2017$l_pre_spe1 <- factor(August2017$l_pre_spe1)
August2017$l_exe_spe1 <- factor(August2017$l_exe_spe1)
August2017$l_pre_stj1 <- factor(August2017$l_pre_stj1)
August2017$l_exe_stj1 <- factor(August2017$l_exe_stj1)
August2017$rem_date <- factor(August2017$rem_date)
# syntax
August2017$dep_mon <- gsub('.', "", fixed = TRUE, x = August2017$dep_mon)
August2017$dep_mon <- gsub(',', ".", fixed = TRUE, x = August2017$dep_mon)
August2017$dep_mon <- as.numeric(August2017$dep_mon)
August2017$rec_mon <- gsub('.', "", fixed = TRUE, x = August2017$rec_mon)
August2017$rec_mon <- gsub(',', ".", fixed = TRUE, x = August2017$rec_mon)
August2017$rec_mon <- as.numeric(August2017$rec_mon)
August2017$act_coe <- gsub('.', "", fixed = TRUE, x = August2017$act_coe)
August2017$act_coe <- gsub(',', ".", fixed = TRUE, x = August2017$act_coe)
August2017$act_coe <- as.numeric(August2017$act_coe)
# store the target variable for later use
y_test <- August2017$dep_mon
# pre process the new test data
# dummy variables
dummies_model <- dummyVars(dep_mon ~ ., data = August2017)
August2017 <- predict(dummies_model, newdata = August2017)
August2017 <- data.frame(August2017)
# transformation
preProcess_yeojohnson_model <- preProcess(August2017, method = c("center", "scale", "YeoJohnson", "nzv"))
August2017 <- predict(preProcess_yeojohnson_model, newdata = August2017)
# append the target, that remains unchanged
August2017$dep_mon <- y_test
str(August2017)
# the variables after this pre-processing are exactly the same as the ones used during training
# this is a good sign that the model is going to generalize well
# change the columns' names
# note : we keep the same names as the ones used during training, even for the dates
# this is not a problem since the variables are the same
colnames(August2017)[1:21] <- c("lserieAMIsoinsinfirmiers", "lserieCSpecialistes",
"lserieTotalfraisdedeplacementdesauxiliairesmedicaux", "remdate201807",
"remdate201808", "snsdate201804", "snsdate201805", "snsdate201806",
"snsdate201807", "snsdate201808", "lasunatAccidentduTravailMaladieProfessionnelle",
"lasunatMaladie", "lasunatMaternite", "lcplcodMajorationdenuit", "lcplcodSansmajoration",
"REMTAU", "lprespe1Autres", "lprespe1MedecinsOmnipraticiensliberaux",
"lprespe1MedecinsSpecialistesliberaux", "lprestj1Liberal", "lprestj1Salarie" )
colnames(August2017)[22:33] <- c("lexespe1Autres", "lexespe1Infirmiers", "lexespe1Laboratoires",
"lexespe1MasseursKinesitherapeutes", "lexespe1MedecinsOmnipraticiens",
"lexespe1MedecinsSpecialistes", "lexestj1Liberal", "lexestj1Salarie", "recmon",
"actdnb", "actcoe", "depmon")
# h2o format
August2017_h2o <- as.h2o(August2017)
# use the gbm model that was trained on the entire August2018
gbm.final.predictions <- h2o.predict(gbm.h2o.final, newdata = August2017_h2o)
gbm.err <- mean(abs(August2017_h2o$depmon - gbm.final.predictions))
gbm.err # new test MAE : 1156
gbm.final.predictions_df <- as.data.frame(gbm.final.predictions)
# summary of the predictions
summary(gbm.final.predictions_df)
# 1st Quartile : 75 ; median : 77 ; 3rd Quartile : 103
# mean : 1841
# minimum : -576188
# maximum : 19092275
Conclusion
Notre meilleur algorithme a modélisé les dépassements d’honoraires des professionnels de santé et son évaluation sur les données d’août 2017 fait apparaître une erreur moyenne de 1156 euros. Autrement dit, pour chacune des observations du jeu de données d’août 2017, la valeur prédite par l’algorithme se trompe, en moyenne, de 1156 euros. Comment interpréter ce résultat ?
D’un côté, cette erreur moyenne peut sembler élevée dans la mesure où il n’existe en fait aucun dépassement d’honoraires pour de très nombreux actes médicaux.
D’un autre côté, cette erreur moyenne de 1156 euros est faible au regard de la très grande dispersion des observations, comprises entre -3900 euros et plus de 31 000 000 euros, ou de l’écart type, d’environ 108 000 euros.
Par ailleurs, l’algorithme a mis en évidence les variables les plus importantes pour prédire le niveau, qu’il soit négatif, nul, ou élevé, des dépassements d’honoraires. Il s’agit principalement du montant de base du remboursement, du nombre d’actes médicaux pratiqués, du taux de remboursement, du coefficient global. De plus, sans surprise, la spécialité du prescripteur en “médecin spécialiste libéral” ou en “médecin omnipraticien libéral”, le mode d’exercice des exécutants en “médecin spécialiste” et le statut “libéral” de l’exécutant sont également utiles pour prédire le niveau des dépassements. Il ne s’agit toutefois pas là d’un lien de causalité, qui ne pourrait être mis en évidence que par des méthodes économétriques, tandis que les algorithmes d’apprentissage des données recherchent plutôt des corrélations.
LS0tDQp0aXRsZTogIlBldXQtb24gcHLpZGlyZSBsZSBuaXZlYXUgZGVzIGTpcGFzc2VtZW50cyBkJ2hvbm9yYWlyZXMgPyINCmF1dGhvcjogIk9saXZpZXIgVmlvbGxldCA6IG9saXZpZXIudmlvbGxldFthXWdtYWlsLmNvbSINCmRhdGU6ICJKYW52aWVyLCAyMDE5Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBS6XN1bekNCg0KRW4gbW9iaWxpc2FudCBsZXMgZG9ubullcyBkZSBsJ0Fzc3VyYW5jZSBtYWxhZGllLCBub3VzIGNoZXJjaG9ucyDgIG1vZOlsaXNlciBsZXMgZOlwYXNzZW1lbnRzIGQnaG9ub3JhaXJlcyBkZXMgcHJvZmVzc2lvbm5lbHMgZGUgc2FudOkgZW4gRnJhbmNlIGVuIDIwMTguIExlIG1laWxsZXVyIGFsZ29yaXRobWUgcGFydmllbnQg4CBwculkaXJlIGxlIG5pdmVhdSBkZSBjZXMgZOlwYXNzZW1lbnRzIGF2ZWMgdW5lIGVycmV1ciBtb3llbm5lIGQnZW52aXJvbiAxMTUwIGV1cm9zLiANCg0KIyBTb21tYWlyZQ0KDQotIE9iamVjdGlmIGRlIGwn6XR1ZGUNCi0gRG9ubullcw0KLSBUcmFuc2Zvcm1hdGlvbiBkdSBqZXUgZGUgZG9ubullcyANCi0gTW9k6Wxpc2F0aW9uDQotIEV2YWx1YXRpb24gZGUgbGEgbW9k6Wxpc2F0aW9uIA0KLSBDb25jbHVzaW9uDQoNCiMjIE9iamVjdGlmIGRlIGwn6XR1ZGUgDQoNCkNldHRlIOl0dWRlIHZpc2Ug4CBtb2TpbGlzZXIgbGUgbml2ZWF1IGRlcyBk6XBhc3NlbWVudHMgZCdob25vcmFpcmVzIGRlcyBwcm9mZXNzaW9ubmVscyBkZSBzYW506SBlbiBGcmFuY2UuIENlIG1vZOhsZSBwZXV0IOp0cmUgdXRpbGUg4CBsJ0Fzc3VyYW5jZSBtYWxhZGllIHBvdXIgYW50aWNpcGVyIGxlcyBk6XBhc3NlbWVudHMgZCdob25vcmFpcmVzLCBwb3VyIGTpdGVjdGVyIGRlcyB0ZW5kYW5jZXMgb3UgY29udHJpYnVlciDgIHJlcOlyZXIgZGVzIGFub21hbGllcyBkYW5zIGwn6XZvbHV0aW9uIGRlcyBk6XBhc3NlbWVudHMuIA0KDQojIyBEb25u6WVzDQoNCk5vdXMgcmVjb3Vyb25zIGF1eCBkb25u6WVzIG1pc2VzIOAgZGlzcG9zaXRpb24gcGFyIGwnKipBc3N1cmFuY2UgbWFsYWRpZSoqIHN1ciBsZSBzaXRlIGQnb3V2ZXJ0dXJlIGRlcyBkb25u6WVzIHB1YmxpcXVlcyBodHRwczovL3d3dy5kYXRhLmdvdXYuZnINCg0KQXUgbW9tZW50IGRlIGxhIHLpYWxpc2F0aW9uIGRlIGNldHRlIOl0dWRlLCBsZXMgZG9ubullcyBsZXMgcGx1cyBy6WNlbnRlcyBzb250IGNlbGxlcyBkdSBtb2lzIGQnKiphb/t0IDIwMTgqKi4gVG91dGVzIGxlcyBkb25u6WVzIHNvbnQgZGlzcG9uaWJsZXMg4CBsJ2FkcmVzc2Ugc3VpdmFudGUsIGFpbnNpIHF1ZSBsZSBkaWN0aW9ubmFpcmUgZGVzIHZhcmlhYmxlcyA6IGh0dHBzOi8vd3d3LmRhdGEuZ291di5mci9mci9kYXRhc2V0cy9kZXBlbnNlcy1kLWFzc3VyYW5jZS1tYWxhZGllLWhvcnMtcHJlc3RhdGlvbnMtaG9zcGl0YWxpZXJlcy1kb25uZWVzLW5hdGlvbmFsZXMvDQoNCkxlIGNvZGUgcXVpIHN1aXQgdOls6WNoYXJnZSBsZXMgZXh0ZW5zaW9ucyByZXF1aXNlcyBhaW5zaSBxdWUgbGVzIGRvbm7pZXMsIHB1aXMgcHLpcGFyZSBjZXMgZGVybmnocmVzIGVuIG1vZGlmaWFudCBsYSBuYXR1cmUgZGUgY2VydGFpbmVzIHZhcmlhYmxlcyBldCBlbiBoYXJtb25pc2FudCBsYSBzeW50YXhlIGRlIHPpcGFyYXRpb24gZGVzIGTpY2ltYWxlcywgbGEgcmVuZGFudCBjb21wYXRpYmxlIGF2ZWMgUi4gRGUgcGx1cywgY2VydGFpbmVzIGNvbG9ubmVzIHNvbnQgKipzdXBwcmlt6WVzKiogOiANCg0KLSBjZXJ0YWluZXMgY29uc3RpdHVlbnQgZGUgZmFpdCwgc291cyBmb3JtZSBkZSBjb2RhZ2UsIGRlcyAqZG91YmxvbnMqIGF2ZWMgZCdhdXRyZXMgY29sb25uZXMgOiBzdGF0aXN0aXF1ZSBtZW5zdWVsbGUsIGV0Yy4NCi0gY2VydGFpbmVzIGNvbnRpZW5uZW50IGRlICpub21icmV1eCBuaXZlYXV4Kiwgbm91cyBwculm6XJvbnMgY29uc2VydmVyIGxlcyBjb2xvbm5lcyBxdWkgYWdy6GdlbnQgY2VzIG5pdmVhdXggOiBzcOljaWFsaXTpIHByZXNjcmlwdGV1ciwgZXRjLiANCi0gbGEgY29sb25uZSByZW1ib3Vyc2VtZW50IGZpbmFsIGVzdCBhYmFuZG9ubullLCBub3VzIHBy6Wbpcm9ucyBjb25zZXJ2ZXIgY2VsbGUgcXVpIGNvcnJlc3BvbmQg4CBsYSBiYXNlIGR1IHJlbWJvdXJzZW1lbnQgDQoNCkTocyBsb3JzIHF1J3VuIG5pdmVhdSBwbHVzIGFnculn6SBhIOl06SByZXRlbnUgcG91ciBjZXJ0YWluZXMgdmFyaWFibGVzLCBpbCBhcHBhcmFpdCBxdSdlbnZpcm9uIDEwICUgZGVzIG9ic2VydmF0aW9ucyBzb250IGVuIGRvdWJsZSA7IGVsbGVzIHNvbnQgZG9uYyByZXRpcullcyBkdSBqZXUgZGUgZG9ubullcywgcXVpIGNvbnRpZW50IGF1IGZpbmFsIGVudmlyb24gKioxODUgMDAwIG9ic2VydmF0aW9ucyoqIGV0ICoqMTUgY29sb25uZXMqKiwgZG9udCBsYSB2YXJpYWJsZSDgIHBy6WRpcmUgKGTpcGFzc2VtZW50cyBkJ2hvbm9yYWlyZXMsICJkZXBfbW9uIikuIA0KDQpMYSB2aXN1YWxpc2F0aW9uIGRlcyBkb25u6WVzIGVzdCByZW5kdWUgZGlmZmljaWxlIHBhciBsYSAqKmdyYW5kZSBkaXNwZXJzaW9uKiogZGUgbGEgZGlzdHJpYnV0aW9uIGRlIGNlcnRhaW5lcyBkJ2VudHJlIGVsbGVzLCBlbiBwYXJ0aWN1bGllciBkZXMgKipk6XBhc3NlbWVudHMgZCdob25vcmFpcmVzKiosIGRvbnQgbGEgbelkaWFuZSBlc3QgMCB0YW5kaXMgcXVlIGxhIG1veWVubmUgZXN0IGRlIDE3ODkgZXVyb3MgZXQgbCcqKuljYXJ0IHR5cGUgZGUgMTA4IDI2MiBldXJvcyoqIDsgbGUgbWF4aW11bSBzJ+ls6HZlIOAgMzEgNTI0IDU4NiBldXJvcywgbGUgbWluaW11bSDgIC0zOTAwIGV1cm9zLCBzb2l0IHVuIOljYXJ0IGV4dHLqbWVtZW50IOlsZXbpIHF1aSByZW5kcmEgZGlmZmljaWxlIGxhIHBy6WRpY3Rpb24uIFBvdXIgODMgJSBkZXMgb2JzZXJ2YXRpb25zLCBhdWN1biBk6XBhc3NlbWVudCBkJ2hvbm9yYWlyZSBuJ2VzdCBjb25zdGF06SBzdXIgY2UgbW9pcyBkJ2Fv+3QgMjAxOC4gQSBub3RlciBxdWUgbGEgcHLpc2VuY2UgZGUgdmFsZXVycyBu6WdhdGl2ZXMgZGFucyBsZSBqZXUgZGUgZG9ubullcyBwb3VyIHBsdXNpZXVycyBjb2xvbm5lcyBzJ2V4cGxpcXVlIHBhciBkZXMgbWVzdXJlcyBkZSBy6Wd1bGFyaXNhdGlvbiBwb3VyIGRlcyBzb2lucyBhbmNpZW5zIChzb3VyY2UgOiBkaXNjdXNzaW9uIHJlbGF0aXZlIOAgY2UgamV1IGRlIGRvbm7pZXMgc3VyIGxlIHNpdGUgZGF0YS5nb3V2LmZyKSA7IGlsIGNvbnZpZW50IGRlIGNvbnNlcnZlciBjZXMgdmFsZXVycyBu6WdhdGl2ZXMgcHVpc3F1ZSBkZSB0ZWxsZXMgculndWxhcmlzYXRpb25zIHNvbnQgb3DpcullcyBjaGFxdWUgbW9pcy4gDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIHJlcXVpcmVkIHBhY2thZ2VzDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoaDJvKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9VFJVRX0NCg0KQXVndXN0MjAxOCA8LSByZWFkX2RlbGltKCJuMjAxODA4LmNzdiIsICI7IiwgZXNjYXBlX2RvdWJsZSA9IEZBTFNFLCBsb2NhbGUgPSBsb2NhbGUoZW5jb2RpbmcgPSAiSVNPLTg4NTktMSIpLCB0cmltX3dzID0gVFJVRSkNCg0KQXVndXN0MjAxOCA8LSBkYXRhLmZyYW1lKEF1Z3VzdDIwMTgpDQoNCiMgY2hhbmdlIHRoZSBuYXR1cmUgb2Ygc29tZSBjb2x1bW5zDQpBdWd1c3QyMDE4JGxfc2VyaWUgPC0gZmFjdG9yKEF1Z3VzdDIwMTgkbF9zZXJpZSkNCkF1Z3VzdDIwMTgkbF9wcnNfbmF0IDwtIGZhY3RvcihBdWd1c3QyMDE4JGxfcHJzX25hdCkNCkF1Z3VzdDIwMTgkc25zX2RhdGUgPC0gZmFjdG9yKEF1Z3VzdDIwMTgkc25zX2RhdGUpDQpBdWd1c3QyMDE4JGxfYXN1X25hdCA8LSBmYWN0b3IoQXVndXN0MjAxOCRsX2FzdV9uYXQpDQpBdWd1c3QyMDE4JGxfY3BsX2NvZCA8LSBmYWN0b3IoQXVndXN0MjAxOCRsX2NwbF9jb2QpDQpBdWd1c3QyMDE4JGxfcHJlX3NwZTEgPC0gZmFjdG9yKEF1Z3VzdDIwMTgkbF9wcmVfc3BlMSkNCkF1Z3VzdDIwMTgkbF9leGVfc3BlMSA8LSBmYWN0b3IoQXVndXN0MjAxOCRsX2V4ZV9zcGUxKQ0KQXVndXN0MjAxOCRsX3ByZV9zdGoxIDwtIGZhY3RvcihBdWd1c3QyMDE4JGxfcHJlX3N0ajEpDQpBdWd1c3QyMDE4JGxfZXhlX3N0ajEgPC0gZmFjdG9yKEF1Z3VzdDIwMTgkbF9leGVfc3RqMSkNCkF1Z3VzdDIwMTgkcmVtX2RhdGUgPC0gZmFjdG9yKEF1Z3VzdDIwMTgkcmVtX2RhdGUpDQoNCiMgc3ludGF4DQpBdWd1c3QyMDE4JGRlcF9tb24gPC0gZ3N1YignLicsICIiLCBmaXhlZCA9IFRSVUUsIHggPSBBdWd1c3QyMDE4JGRlcF9tb24pDQpBdWd1c3QyMDE4JGRlcF9tb24gPC0gZ3N1YignLCcsICIuIiwgZml4ZWQgPSBUUlVFLCB4ID0gQXVndXN0MjAxOCRkZXBfbW9uKQ0KQXVndXN0MjAxOCRkZXBfbW9uIDwtIGFzLm51bWVyaWMoQXVndXN0MjAxOCRkZXBfbW9uKQ0KDQpBdWd1c3QyMDE4JHJlY19tb24gPC0gZ3N1YignLicsICIiLCBmaXhlZCA9IFRSVUUsIHggPSBBdWd1c3QyMDE4JHJlY19tb24pDQpBdWd1c3QyMDE4JHJlY19tb24gPC0gZ3N1YignLCcsICIuIiwgZml4ZWQgPSBUUlVFLCB4ID0gQXVndXN0MjAxOCRyZWNfbW9uKQ0KQXVndXN0MjAxOCRyZWNfbW9uIDwtIGFzLm51bWVyaWMoQXVndXN0MjAxOCRyZWNfbW9uKQ0KDQpBdWd1c3QyMDE4JGFjdF9jb2UgPC0gZ3N1YignLicsICIiLCBmaXhlZCA9IFRSVUUsIHggPSBBdWd1c3QyMDE4JGFjdF9jb2UpDQpBdWd1c3QyMDE4JGFjdF9jb2UgPC0gZ3N1YignLCcsICIuIiwgZml4ZWQgPSBUUlVFLCB4ID0gQXVndXN0MjAxOCRhY3RfY29lKQ0KQXVndXN0MjAxOCRhY3RfY29lIDwtIGFzLm51bWVyaWMoQXVndXN0MjAxOCRhY3RfY29lKQ0KDQojIHJlbW92ZSBkdXBsaWNhdGVkIG9yIHVzZWxlc3MgY29sdW1ucw0KQXVndXN0MjAxOCRhc3VfbmF0IDwtIE5VTEwNCkF1Z3VzdDIwMTgkcHJzX25hdCA8LSBOVUxMDQpBdWd1c3QyMDE4JFNFUklFIDwtIE5VTEwNCkF1Z3VzdDIwMTgkY3BsX2NvZCA8LSBOVUxMDQpBdWd1c3QyMDE4JHByZV9zdGoxIDwtIE5VTEwNCkF1Z3VzdDIwMTgkZXhlX3N0ajEgPC0gTlVMTA0KQXVndXN0MjAxOCRsX3ByZV9zcGUgPC0gTlVMTCAjIG1hbnkgbGV2ZWxzLCB1c2UgdGhlIGdyb3VwaW5nIGNvbHVtbiBpbnN0ZWFkIChsX3ByZV9zcGUxKQ0KQXVndXN0MjAxOCRwcmVfc3BlIDwtIE5VTEwgICMgbWFueSBsZXZlbHMsIHVzZSB0aGUgZ3JvdXBpbmcgY29sdW1uIGluc3RlYWQgKGxfcHJlX3NwZTEpDQpBdWd1c3QyMDE4JHByZV9zcGUxIDwtIE5VTEwNCkF1Z3VzdDIwMTgkbF9leGVfc3BlIDwtIE5VTEwgIyBtYW55IGxldmVscywgdXNlIHRoZSBncm91cGluZyBjb2x1bW4gaW5zdGVhZCAobF9leGVfc3BlMSkNCkF1Z3VzdDIwMTgkZXhlX3NwZSA8LSBOVUxMICMgbWFueSBsZXZlbHMsIHVzZSB0aGUgZ3JvdXBpbmcgY29sdW1uIGluc3RlYWQgKGxfZXhlX3NwZTEpDQpBdWd1c3QyMDE4JGV4ZV9zcGUxIDwtIE5VTEwgDQpBdWd1c3QyMDE4JHJlbV9tb24gPC0gTlVMTCAjIGtlZXAgaW5zdGVhZCB0aGUgcmVpbWJ1cnNlbWVudCBiYXNlIGFuZCB0aGUgcmF0ZQ0KDQojIGR1cGxpY2F0ZWQgcm93cw0KY2F0KCJUaGUgbnVtYmVyIG9mIGR1cGxpY2F0ZWQgcm93cyBpcyIsIG5yb3coQXVndXN0MjAxOCkgLSBucm93KHVuaXF1ZShBdWd1c3QyMDE4KSkpIA0KIyBUaGVyZSBhcmUgbm93IDIwNzI1IGR1cGxpY2F0ZWQgcm93cyAoYXBwcm94aW1hdGVseSAxMCAlIG9mIHRoZSBkYXRhc2V0KSBiZWNhdXNlIHdlIHJlbW92ZWQgc29tZSBjb2x1bW5zIHRvIGtlZXAgYSBtb3JlIGFnZ3JlZ2F0ZWQgbGV2ZWwgKGxfZXhlX3NwZTEsIGxfcHJlX3NwZTEpDQoNCkF1Z3VzdDIwMTggPC0gdW5pcXVlKEF1Z3VzdDIwMTgpICMgcmVtb3ZlIHRoZSBkdXBsaWNhdGVkIHJvd3MNCg0KIyB0YXJnZXQgdmFyaWFibGUNCnN1bW1hcnkoQXVndXN0MjAxOCRkZXBfbW9uKQ0Kc2QoQXVndXN0MjAxOCRkZXBfbW9uKQ0Kc3VtKEF1Z3VzdDIwMTgkZGVwX21vbiA9PSAwKSAvIG5yb3coQXVndXN0MjAxOCkNCg0KYGBgDQoNCiMjIFRyYW5zZm9ybWF0aW9uIGR1IGpldSBkZSBkb25u6WVzIA0KDQpBdmFudCBkJ29w6XJlciBsYSBtb2TpbGlzYXRpb24gZGVzIGTpcGFzc2VtZW50cyBkJ2hvbm9yYWlyZXMsIGxlIGpldSBkZSBkb25u6WVzIGVzdCBjb3Vw6WUgYWzpYXRvaXJlbWVudCBlbiBkZXV4LCBsJ3VuIHBvdXIgZW50cmHubmVyIGxlcyBtb2TobGVzIChhdmVjIDc1ICUgZGVzIG9ic2VydmF0aW9ucyksIGwnYXV0cmUgcG91ciBhZmZpbmVyIGxldXJzIHBhcmFt6HRyZXMgZXQgculkdWlyZSBsZSBzdXItYXBwcmVudGlzc2FnZSAoKm92ZXJmaXR0aW5nKikuIA0KDQpMZXMgdmFyaWFibGVzIGRpc2Ny6HRlcyBzb250IGVuY29k6WVzIGRlIG1hbmnocmUgYmluYWlyZSAoKm9uZSBob3QgZW5jb2RpbmcqKS4gTGVzIHZhcmlhYmxlcyBwculkaWN0aXZlcyBzb250IHRyYW5zZm9ybellcyBlbiBsZXMgY2VudHJhbnQgZXQgbGVzIHLpZHVpc2FudCBldCBlbiBhcHBsaXF1YW50IGxhIG3pdGhvZGUgZGUgWWVvLUpvaG5zb24uIEVuIG91dHJlLCBsZXMgbm9tYnJldXNlcyB2YXJpYWJsZXMgYmluYWlyZXMgZG9udCBsYSB2YXJpYW5jZSBlc3QgdHLocyBmYWlibGUgc29udCByZXRpcullcyBkZXMgamV1eCBkZSBkb25u6WVzLiBJbCBkZW1ldXJlIDMzIHZhcmlhYmxlcywgZG9udCBsYSB2YXJpYWJsZSBwculkaWN0aXZlIHF1aSBuJ2Egc3ViaSBhdWN1bmUgdHJhbnNmb3JtYXRpb24uIA0KDQpFbmZpbiwgbGVzIG5vbXMgZGVzIGNvbG9ubmVzIHNvbnQgbW9kaWZp6WVzIGFmaW4gZCfpdml0ZXIgbGVzIGNhcmFjdOhyZXMgc3DpY2lhdXgsIHF1aSByZW5kZW50IGxldXIgbGVjdHVyZSBkaWZmaWNpbGUgdm9pcmUgaW1wb3NzaWJsZSBkYW5zIGwnaW50ZXJmYWNlIGQnaDJvIOAgbGFxdWVsbGUgbm91cyByZWNvdXJlcm9ucyBwb3VyIG1vZOlsaXNlci4gDQoNCmBgYHtyfQ0KIyBTcGxpdCB0aGUgdHJhaW5pbmcgc2V0IGludG8gYSB0cmFpbiBhbmQgYSB0ZXN0IHNldA0KDQogICAgICAgICMgU3RlcCAxOiBHZXQgcm93IG51bWJlcnMgZm9yIHRoZSB0cmFpbmluZyBkYXRhDQpzZXQuc2VlZCgxKQ0KdHJhaW5Sb3dOdW1iZXJzIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oQXVndXN0MjAxOCRkZXBfbW9uLCBwID0gMC43NSwgbGlzdCA9IEZBTFNFKQ0KDQogICAgICAgICMgU3RlcCAyOiBDcmVhdGUgdGhlIHRyYWluaW5nIGRhdGFzZXQNCnRyYWluRGF0YSA8LSBBdWd1c3QyMDE4W3RyYWluUm93TnVtYmVycyxdDQoNCiAgICAgICAgIyBTdGVwIDM6IENyZWF0ZSB0aGUgdGVzdCBkYXRhc2V0DQp0ZXN0RGF0YSA8LSBBdWd1c3QyMDE4Wy10cmFpblJvd051bWJlcnMsXQ0KDQogICAgICAgICMgU3RvcmUgWSBmb3IgbGF0ZXIgdXNlLg0KeSA8LSB0cmFpbkRhdGEkZGVwX21vbg0KeV90ZXN0IDwtIHRlc3REYXRhJGRlcF9tb24NCg0KIyBwcmUgcHJvY2VzcyB0aGUgdHJhaW5pbmcgZGF0YQ0KDQogICAgICAgICMgT25lIGhvdCBlbmNvZGluZw0KZHVtbWllc19tb2RlbCA8LSBkdW1teVZhcnMoZGVwX21vbiB+IC4sIGRhdGEgPSB0cmFpbkRhdGEpDQp0cmFpbkRhdGEgPC0gcHJlZGljdChkdW1taWVzX21vZGVsLCBuZXdkYXRhID0gdHJhaW5EYXRhKSANCnRyYWluRGF0YSA8LSBkYXRhLmZyYW1lKHRyYWluRGF0YSkNCg0KICAgICAgICAjIFRyYW5zZm9ybSB0aGUgdHJhaW5pbmcgZGF0YQ0KcHJlUHJvY2Vzc195ZW9qb2huc29uX21vZGVsIDwtIHByZVByb2Nlc3ModHJhaW5EYXRhLCBtZXRob2QgPSBjKCJjZW50ZXIiLCAic2NhbGUiLCAiWWVvSm9obnNvbiIsICJuenYiKSkNCnRyYWluRGF0YSA8LSBwcmVkaWN0KHByZVByb2Nlc3NfeWVvam9obnNvbl9tb2RlbCwgbmV3ZGF0YSA9IHRyYWluRGF0YSkNCg0KICAgICAgICAjIEFwcGVuZCB0aGUgdHJhaW5pbmcgcmVzcG9uc2UgdmFyaWFibGUsIHRoYXQgcmVtYWlucyB1bnRyYW5zZm9ybWVkDQp0cmFpbkRhdGEkZGVwX21vbiA8LSB5DQoNCg0KIyBwcmUgcHJvY2VzcyB0aGUgdGVzdCBkYXRhDQp0ZXN0RGF0YSA8LSBwcmVkaWN0KGR1bW1pZXNfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0RGF0YSkgIyBPbmUgaG90IGVuY29kaW5nDQp0ZXN0RGF0YSA8LSBkYXRhLmZyYW1lKHRlc3REYXRhKQ0KdGVzdERhdGEgPC0gcHJlZGljdChwcmVQcm9jZXNzX3llb2pvaG5zb25fbW9kZWwsIG5ld2RhdGEgPSB0ZXN0RGF0YSkgIyBUcmFuc2Zvcm0NCnRlc3REYXRhJGRlcF9tb24gPC0geV90ZXN0ICMgQXBwZW5kIHRoZSByZXNwb25zZSB2YXJpYWJsZQ0KDQoNCiMgY2hhbmdlIHRoZSBjb2x1bW5zJ25hbWVzIHRvIGF2b2lkIHNwZWNpYWwgY2hhcmFjdGVycw0KICAgICAgICAjIHRyYWluaW5nIHNldA0KY29sbmFtZXModHJhaW5EYXRhKVsxOjIxXSA8LSBjKCJsc2VyaWVBTUlzb2luc2luZmlybWllcnMiLCAibHNlcmllQ1NwZWNpYWxpc3RlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxzZXJpZVRvdGFsZnJhaXNkZWRlcGxhY2VtZW50ZGVzYXV4aWxpYWlyZXNtZWRpY2F1eCIsICJyZW1kYXRlMjAxODA3IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJlbWRhdGUyMDE4MDgiLCAic25zZGF0ZTIwMTgwNCIsICJzbnNkYXRlMjAxODA1IiwgInNuc2RhdGUyMDE4MDYiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic25zZGF0ZTIwMTgwNyIsICJzbnNkYXRlMjAxODA4IiwgImxhc3VuYXRBY2NpZGVudGR1VHJhdmFpbE1hbGFkaWVQcm9mZXNzaW9ubmVsbGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsYXN1bmF0TWFsYWRpZSIsICJsYXN1bmF0TWF0ZXJuaXRlIiwgImxjcGxjb2RNYWpvcmF0aW9uZGVudWl0IiwgImxjcGxjb2RTYW5zbWFqb3JhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJFTVRBVSIsICJscHJlc3BlMUF1dHJlcyIsICJscHJlc3BlMU1lZGVjaW5zT21uaXByYXRpY2llbnNsaWJlcmF1eCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxwcmVzcGUxTWVkZWNpbnNTcGVjaWFsaXN0ZXNsaWJlcmF1eCIsICJscHJlc3RqMUxpYmVyYWwiLCAibHByZXN0ajFTYWxhcmllIiAgKQ0KDQpjb2xuYW1lcyh0cmFpbkRhdGEpWzIyOjMzXSA8LSBjKCJsZXhlc3BlMUF1dHJlcyIsICJsZXhlc3BlMUluZmlybWllcnMiLCAibGV4ZXNwZTFMYWJvcmF0b2lyZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGV4ZXNwZTFNYXNzZXVyc0tpbmVzaXRoZXJhcGV1dGVzIiwgImxleGVzcGUxTWVkZWNpbnNPbW5pcHJhdGljaWVucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsZXhlc3BlMU1lZGVjaW5zU3BlY2lhbGlzdGVzIiwgImxleGVzdGoxTGliZXJhbCIsICJsZXhlc3RqMVNhbGFyaWUiLCAicmVjbW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFjdGRuYiIsICJhY3Rjb2UiLCAiZGVwbW9uIikNCg0KICAgICAgICAjIHRlc3Qgc2V0DQpjb2xuYW1lcyh0ZXN0RGF0YSlbMToyMV0gPC0gYygibHNlcmllQU1Jc29pbnNpbmZpcm1pZXJzIiwgImxzZXJpZUNTcGVjaWFsaXN0ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsc2VyaWVUb3RhbGZyYWlzZGVkZXBsYWNlbWVudGRlc2F1eGlsaWFpcmVzbWVkaWNhdXgiLCAicmVtZGF0ZTIwMTgwNyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyZW1kYXRlMjAxODA4IiwgInNuc2RhdGUyMDE4MDQiLCAic25zZGF0ZTIwMTgwNSIsICJzbnNkYXRlMjAxODA2IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNuc2RhdGUyMDE4MDciLCAic25zZGF0ZTIwMTgwOCIsICJsYXN1bmF0QWNjaWRlbnRkdVRyYXZhaWxNYWxhZGllUHJvZmVzc2lvbm5lbGxlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGFzdW5hdE1hbGFkaWUiLCAibGFzdW5hdE1hdGVybml0ZSIsICJsY3BsY29kTWFqb3JhdGlvbmRlbnVpdCIsICJsY3BsY29kU2Fuc21ham9yYXRpb24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSRU1UQVUiLCAibHByZXNwZTFBdXRyZXMiLCAibHByZXNwZTFNZWRlY2luc09tbmlwcmF0aWNpZW5zbGliZXJhdXgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJscHJlc3BlMU1lZGVjaW5zU3BlY2lhbGlzdGVzbGliZXJhdXgiLCAibHByZXN0ajFMaWJlcmFsIiwgImxwcmVzdGoxU2FsYXJpZSIpDQoNCmNvbG5hbWVzKHRlc3REYXRhKVsyMjozM10gPC0gYygibGV4ZXNwZTFBdXRyZXMiLCAibGV4ZXNwZTFJbmZpcm1pZXJzIiwgImxleGVzcGUxTGFib3JhdG9pcmVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxleGVzcGUxTWFzc2V1cnNLaW5lc2l0aGVyYXBldXRlcyIsICJsZXhlc3BlMU1lZGVjaW5zT21uaXByYXRpY2llbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGV4ZXNwZTFNZWRlY2luc1NwZWNpYWxpc3RlcyIsICJsZXhlc3RqMUxpYmVyYWwiLCAibGV4ZXN0ajFTYWxhcmllIiwgInJlY21vbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhY3RkbmIiLCAiYWN0Y29lIiwgImRlcG1vbiIpDQoNCmBgYA0KDQojIyBNb2TpbGlzYXRpb24NCg0KTm91cyBjaG9pc2lzc29ucyBkZSB0cmFpdGVyIGxhIG1vZOlsaXNhdGlvbiBkZXMgZOlwYXNzZW1lbnRzIGQnaG9ub3JhaXJlcyBlbiBwcm9ibOhtZSBkZSAqKnLpZ3Jlc3Npb24qKiBjYXIgaWwgZXhpc3RlIHVuZSB0cuhzIGdyYW5kZSBkaXZlcnNpdOkgZGFucyBsZSBuaXZlYXUgZGVzIGTpcGFzc2VtZW50cy4gQXV0cmVtZW50IGRpdCwgbGEgcXVlc3Rpb24gZXN0IG1vaW5zIGRlIHNhdm9pciBzaSB1biBwcm9mZXNzaW9ubmVsIGRlIHNhbnTpIHJlY291cnQgYXV4IGTpcGFzc2VtZW50cyBkJ2hvbm9yYWlyZXMsIHF1ZSBkZSBzYXZvaXIgZGFucyBxdWVsbGUgbWVzdXJlIGxlIHByaXggcXUnaWwgcHJhdGlxdWUgZXN0IHN1cOlyaWV1ciBhdSB0YXJpZiBkZSBjb252ZW50aW9uIGZpeOkgcG91ciBjaGFxdWUgYWN0ZSBt6WRpY2FsIHBhciBsYSBz6WN1cml06SBzb2NpYWxlLiANCg0KTGVzIGFsZ29yaXRobWVzIHN1aXZhbnRzIHNvbnQgdXRpbGlz6XMgOiANCg0KLSBNb2TobGUgbGlu6WFpcmUgZ+lu6XJhbGlz6Q0KLSBGb3LqdHMgYWzpYXRvaXJlcw0KLSBS6XNlYXV4IGRlIG5ldXJvbmVzDQotIEdyYWRpZW50IGJvb3N0aW5nDQoNCkNvbW1lIG3pdHJpcXVlIGRlIHBlcmZvcm1hbmNlLCBub3VzIHJldGVub25zIGwnZXJyZXVyIG1veWVubmUgYWJzb2x1ZSAoKiptZWFuIGFic29sdXRlIGVycm9yLCBNQUUqKiksIGRvbnQgbCdpbnRlcnBy6XRhdGlvbiBlc3Qgc2ltcGxlLiANCg0KIyMjIE1vZOhsZSBsaW7pYWlyZSBn6W7pcmFsaXPpDQoNClNhbnMgc3VycHJpc2UsIGxlcyBy6XN1bHRhdHMgb2J0ZW51cyBkdSBtb2TobGUgbGlu6WFpcmUgZ+lu6XJhbGlz6SBzb250IG3pZGlvY3JlczogbGEgTUFFIHMn6WzodmUg4CAzNjM5IGV1cm9zLCBjZXJ0ZXMgc2FucyBmb3J0IHN1ci1hcHByZW50aXNzYWdlLiBEZXMgbW9k6GxlcyBwbHVzIGNvbXBsZXhlcyBzb250IGRvbmMgbuljZXNzYWlyZXMuIA0KDQpgYGB7cn0NCmgyby5pbml0KG50aHJlYWRzID0gLTEsIG1heF9tZW1fc2l6ZSA9ICc0ZycpDQoNCnRyYWluaW5nX2RhdGFfeWVvam9obnNvbl9oMm8gPC0gYXMuaDJvKHRyYWluRGF0YSkNCnRlc3RpbmdfZGF0YV95ZW9qb2huc29uX2gybyA8LSBhcy5oMm8odGVzdERhdGEpDQoNClkgPC0gImRlcG1vbiINClggPC0gc2V0ZGlmZihuYW1lcyh0cmFpbmluZ19kYXRhX3llb2pvaG5zb25faDJvKSwgWSkNCg0KIyB0cmFpbiB0aGUgbW9kZWwNCmdsbTEgPC0gaDJvLmdsbSh5ID0gWSwNCiAgICAgICAgICAgICAgICB4ICA9ICBYLA0KICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lID0gdHJhaW5pbmdfZGF0YV95ZW9qb2huc29uX2gybywNCiAgICAgICAgICAgICAgICBmYW1pbHkgPSAiZ2F1c3NpYW4iLA0KICAgICAgICAgICAgICAgIG5mb2xkcyA9IDUsIA0KICAgICAgICAgICAgICAgIGFscGhhID0gMC41LA0KICAgICAgICAgICAgICAgIHNlZWQgPSAxKQ0KZ2xtMQ0KIyAzMjI5IG9uIGJvdGggdHJhaW5pbmcgYW5kIGNyb3NzIHZhbGlkYXRpb24gZGF0YQ0KDQojIHRyYWluIHRoZSBtb2RlbA0KZ2xtMiA8LSBoMm8uZ2xtKHkgPSBZLA0KICAgICAgICAgICAgICAgIHggID0gIFgsDQogICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgPSB0cmFpbmluZ19kYXRhX3llb2pvaG5zb25faDJvLA0KICAgICAgICAgICAgICAgIGZhbWlseSA9ICJnYXVzc2lhbiIsDQogICAgICAgICAgICAgICAgbmZvbGRzID0gNSwNCiAgICAgICAgICAgICAgICBzZWVkID0gMSkNCmdsbTINCiMgc2FtZQ0KDQpnbG0yLnByZWRpY3RlZCA8LSBoMm8ucHJlZGljdChnbG0yLCBuZXdkYXRhID0gdGVzdGluZ19kYXRhX3llb2pvaG5zb25faDJvKQ0KZ2xtMi5lcnIgPC0gbWVhbihhYnModGVzdGluZ19kYXRhX3llb2pvaG5zb25faDJvJGRlcG1vbi0gZ2xtMi5wcmVkaWN0ZWQpKQ0KZ2xtMi5lcnIgIyBNQUUgb24gdGVzdCBzZXQgOiAzNjM5LjgyMw0KDQpgYGANCg0KDQojIyMgRm9y6nRzIGFs6WF0b2lyZXMNCg0KTGVzIGRpZmbpcmVudGVzIGZvcup0cyBhbOlhdG9pcmVzIGFwcG9ydGVudCBkZSBtZWlsbGV1cnMgculzdWx0YXRzLCBlbiBkaXZpc2FudCBwYXIgZGV1eCBsJ2VycmV1ciBtb3llbm5lIGR1IG1vZOhsZSBsaW7pcmFpcmUgZ+lu6XJhbGlz6SwgcXVpIHRvbWJlIOAgZW52aXJvbiAqKjE4MDAgZXVyb3MqKiwgc2FucyBzdXItYXBwcmVudGlzc2FnZSBkJ2FwcuhzIGxhIHZhbGlkYXRpb24gY3JvaXPpZSAoKmNyb3NzIHZhbGlkYXRpb24qKS4gTGEgbWFuaXB1bGF0aW9uIGRlcyBoeXBlcnBhcmFt6HRyZXMgKG5vbWJyZSBkJ2FyYnJlcywgZXRjLikgbidhIHBhcyBwZXJtaXMgZCdhbelsaW9yZXIgbCdlcnJldXIgbW95ZW5uZS4gDQoNCmBgYHtyfQ0KDQpoMm8uaW5pdChudGhyZWFkcyA9IC0xLCBtYXhfbWVtX3NpemUgPSAnNGcnKQ0KDQojIHByZXBhcmUgdGhlIGRhdGFzZXRzDQp0cmFpbmluZ19kYXRhX3llb2pvaG5zb25faDJvIDwtIGFzLmgybyh0cmFpbkRhdGEpDQp0ZXN0aW5nX2RhdGFfeWVvam9obnNvbl9oMm8gPC0gYXMuaDJvKHRlc3REYXRhKQ0KDQpZIDwtICJkZXBtb24iDQpYIDwtIHNldGRpZmYobmFtZXModHJhaW5pbmdfZGF0YV95ZW9qb2huc29uX2gybyksIFkpDQoNCiMgdHJhaW4gdGhlIG1vZGVsDQpzZXQuc2VlZCgxKQ0KZHJmMSA8LSBoMm8ucmFuZG9tRm9yZXN0KHRyYWluaW5nX2ZyYW1lID0gdHJhaW5pbmdfZGF0YV95ZW9qb2huc29uX2gybywNCiAgICAgICAgICAgICAgICBuZm9sZHMgPSAzLA0KICAgICAgICAgICAgICAgIHkgPSBZLA0KICAgICAgICAgICAgICAgIHggPSBYLA0KICAgICAgICAgICAgICAgIG50cmVlcyA9IDUwLA0KICAgICAgICAgICAgICAgIG1heF9kZXB0aCA9IDE1LA0KICAgICAgICAgICAgICAgIG1pbl9yb3dzID0gMiwNCiAgICAgICAgICAgICAgICBtdHJpZXMgPSAtMSwNCiAgICAgICAgICAgICAgICBzZWVkID0gMSkNCmRyZjEgDQojIE1BRSA6IDIwMTkuNjQ1IG9uIGNyb3NzIHZhbGlkYXRpb24gZGF0YQ0KDQojIGNoYW5nZSB0aGUgcGFyYW1ldGVycw0KZHJmMiA8LSBoMm8ucmFuZG9tRm9yZXN0KHRyYWluaW5nX2ZyYW1lID0gdHJhaW5pbmdfZGF0YV95ZW9qb2huc29uX2gybywNCiAgICAgICAgICAgICAgICBuZm9sZHMgPSA1LA0KICAgICAgICAgICAgICAgIHkgPSBZLA0KICAgICAgICAgICAgICAgIHggPSBYLA0KICAgICAgICAgICAgICAgIG50cmVlcyA9IDEwMCwNCiAgICAgICAgICAgICAgICBtYXhfZGVwdGggPSAxNSwNCiAgICAgICAgICAgICAgICBtaW5fcm93cyA9IDIsDQogICAgICAgICAgICAgICAgbXRyaWVzID0gLTEsDQogICAgICAgICAgICAgICAgc2VlZCA9IDIpDQpkcmYyDQojIE1BRSA6IDE4NjMuNDgzIG9uIGNyb3NzIHZhbGlkYXRpb24gZGF0YSwgMTgxOC4xMDMgb24gdHJhaW5pbmcNCg0KYGBgDQoNCg0KIyMjIFLpc2VhdXggZGUgbmV1cm9uZXMNCg0KUGx1c2lldXJzIHLpc2VhdXggZGUgbmV1cm9uZXMgcmVsYXRpdmVtZW50IHNpbXBsZXMgb250IOl06SBlc3NheelzIOAgdHJhdmVycyB0cm9pcyBmb25jdGlvbnMgZCdhY3RpdmF0aW9ucyBkaWZm6XJlbnRlcyAoVGFuaCwgTWF4b3V0LCBSZUxVKSwgZGVzIGFyY2hpdGVjdHVyZXMgcGx1cyBvdSBtb2lucyBwcm9mb25kZXMsIGV0Yy4gTGVzIG1laWxsZXVycyBy6XNlYXV4IHNvbnQgcGFydmVudXMg4CBkaW1pbnVlciBsJ2VycmV1ciBtb3llbm5lIOAgKiptb2lucyBkZSAxNzAwIGV1cm9zKiouIERlcyB0ZW50YXRpdmVzIChub24gYXBwYXJlbnRlcyBpY2kpIGRlIHLpZ3VsYXJpc2F0aW9uIG4nb250IHBhcyBwZXJtaXMgZGUgculkdWlyZSBsZSBzdXItYXBwcmVudGlzc2FnZSwgcGF0ZW50IGF1IHJlZ2FyZCBkZSBsYSB2YWxpZGF0aW9uIGNyb2lz6WUuICANCg0KYGBge3J9DQpoMm8uaW5pdChudGhyZWFkcyA9IC0xLCBtYXhfbWVtX3NpemUgPSAnNGcnKQ0KDQp0cmFpbmluZ19kYXRhX3llb2pvaG5zb25faDJvIDwtIGFzLmgybyh0cmFpbkRhdGEpDQp0ZXN0aW5nX2RhdGFfeWVvam9obnNvbl9oMm8gPC0gYXMuaDJvKHRlc3REYXRhKQ0KDQpZIDwtICJkZXBtb24iDQpYIDwtIHNldGRpZmYobmFtZXModHJhaW5pbmdfZGF0YV95ZW9qb2huc29uX2gybyksIFkpDQoNCiMgdHJhaW4gdGhlIG1vZGVsDQpETC5oMm8uMSA8LSBoMm8uZGVlcGxlYXJuaW5nKHggPSBYLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lID0gdHJhaW5pbmdfZGF0YV95ZW9qb2huc29uX2gybywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZvbGRzID0gNSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gPSAiTWF4b3V0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGlkZGVuID0gYygzMiwzMiwzMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYXJzZSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsMSA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVwb2NocyA9IDEwMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlX2ltcG9ydGFuY2VzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDEpDQpETC5oMm8uMQ0KIyBNQUUgOiAxNDAyIG9uIHRyYWluaW5nIGFuZCAyMDA1IG9uIGNyb3NzIHZhbGlkYXRpb24gZGF0YQ0KDQpETC5oMm8uMiA8LSBoMm8uZGVlcGxlYXJuaW5nKHggPSBYLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lID0gdHJhaW5pbmdfZGF0YV95ZW9qb2huc29uX2gybywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZvbGRzID0gMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gPSAiVGFuaCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbiA9IGMoMzIsMzIsMzIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFyc2UgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbDEgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcG9jaHMgPSAxMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZV9pbXBvcnRhbmNlcyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSAyKQ0KREwuaDJvLjINCiMgTUFFIDogMTA2MC4wMDIgb24gdHJhaW5pbmcgYW5kIDE2OTAuNTA0IG9uIGNyb3NzIHZhbGlkYXRpb24gZGF0YQ0KDQpETC5oMm8uMyA8LSBoMm8uZGVlcGxlYXJuaW5nKHggPSBYLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lID0gdHJhaW5pbmdfZGF0YV95ZW9qb2huc29uX2gybywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZvbGRzID0gMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gPSAiUmVjdGlmaWVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGlkZGVuID0gYygzMiwzMiwzMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYXJzZSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsMSA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVwb2NocyA9IDEwMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlX2ltcG9ydGFuY2VzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDIpDQpETC5oMm8uMw0KIyBNQUUgOiAxMzYwLjk4OSBvbiB0cmFpbmluZyBhbmQgMTc5NC41ODggb24gY3Jvc3MgdmFsaWRhdGlvbiBkYXRhDQoNCkRMLmgyby40IDwtIGgyby5kZWVwbGVhcm5pbmcoeCA9IFgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gWSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgPSB0cmFpbmluZ19kYXRhX3llb2pvaG5zb25faDJvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZm9sZHMgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gImdhdXNzaWFuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWN0aXZhdGlvbiA9ICJUYW5oIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGlkZGVuID0gYygzMiwzMiwzMiwzMiwzMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYXJzZSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsMSA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVwb2NocyA9IDE1MCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlX2ltcG9ydGFuY2VzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDQpDQpETC5oMm8uNA0KIyBNQUUgOiAxNjkxLjMzMyBvbiBjcm9zcyB2YWxpZGF0aW9uIGRhdGENCg0KREwuaDJvLjUgPC0gaDJvLmRlZXBsZWFybmluZyh4ID0gWCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBZLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpbmluZ19mcmFtZSA9IHRyYWluaW5nX2RhdGFfeWVvam9obnNvbl9oMm8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5mb2xkcyA9IDMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gPSAiZ2F1c3NpYW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3RpdmF0aW9uID0gIlRhbmgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4gPSBjKDEwMCwxMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFyc2UgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbDEgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcG9jaHMgPSAxMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZV9pbXBvcnRhbmNlcyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSA1KQ0KREwuaDJvLjUNCiMgTUFFOiAgNDc0LjI5NzQgb24gdHJhaW5pbmcgYW5kIDE2ODQuNzc0IG9uIGNyb3NzIHZhbGlkYXRpb24gZGF0YQ0KDQpgYGANCg0KIyMjIEdyYWRpZW50IGJvb3N0aW5nDQoNCkVuZmluLCBsYSBt6XRob2RlIGVuc2VtYmxpc3RlIGR1ICpncmFkaWVudCBib29zdGluZyogZXN0IG1vYmlsaXPpZSBldCBkb25uZSBkZXMgculzdWx0YXRzIGFuYWxvZ3VlcyBhdXggbWVpbGxldXJzIHLpc2VhdXggZGUgbmV1cm9uZXMsIGF2ZWMgdW5lIE1BRSBkJ2Vudmlyb24gKioxNzAwIGV1cm9zKiouIExlIHN1ci1hcHByZW50aXNzYWdlIGVzdCBkZSBub3V2ZWF1IOlsZXbpLCBtYWxncukgZGVzIHRlbnRhdGl2ZXMgZGUgculndWxhcmlzYXRpb24uIA0KDQpgYGB7cn0NCmgyby5pbml0KG50aHJlYWRzID0gLTEsIG1heF9tZW1fc2l6ZSA9ICc0ZycpDQoNCiMgcHJlcGFyZSB0aGUgZGF0YXNldHMNCnRyYWluaW5nX2RhdGFfeWVvam9obnNvbl9oMm8gPC0gYXMuaDJvKHRyYWluRGF0YSkNCnRlc3RpbmdfZGF0YV95ZW9qb2huc29uX2gybyA8LSBhcy5oMm8odGVzdERhdGEpDQoNClkgPC0gImRlcG1vbiINClggPC0gc2V0ZGlmZihuYW1lcyh0cmFpbmluZ19kYXRhX3llb2pvaG5zb25faDJvKSwgWSkNCg0KIyB0cmFpbiB0aGUgbW9kZWwNCnNldC5zZWVkKDUpDQpnYm0uaDJvLjEgPC0gaDJvLmdibSh0cmFpbmluZ19mcmFtZSA9IHRyYWluaW5nX2RhdGFfeWVvam9obnNvbl9oMm8sDQogICAgICAgICAgICAgICAgICAgIHggPSBYLA0KICAgICAgICAgICAgICAgICAgICB5ID0gWSwNCiAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gImdhdXNzaWFuIiwNCiAgICAgICAgICAgICAgICAgICAgbnRyZWVzID0gNTAsDQogICAgICAgICAgICAgICAgICAgIG1heF9kZXB0aCA9IDEwLA0KICAgICAgICAgICAgICAgICAgICBsZWFybl9yYXRlID0gMC4yLA0KICAgICAgICAgICAgICAgICAgICBtaW5fcm93cyA9IDIsDQogICAgICAgICAgICAgICAgICAgIG5mb2xkcyA9IDUpDQpnYm0uaDJvLjENCiMgTUFFIDogNDgzIG9uIHRyYWluaW5nIDsgMjAyMyBvbiBjcm9zcyB2YWxpZGF0aW9uIGRhdGENCg0KZ2JtLmgyby4yIDwtIGgyby5nYm0odHJhaW5pbmdfZnJhbWUgPSB0cmFpbmluZ19kYXRhX3llb2pvaG5zb25faDJvLA0KICAgICAgICAgICAgICAgICAgICB4ID0gWCwNCiAgICAgICAgICAgICAgICAgICAgeSA9IFksDQogICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsDQogICAgICAgICAgICAgICAgICAgIG50cmVlcyA9IDUwLA0KICAgICAgICAgICAgICAgICAgICBtYXhfZGVwdGggPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgbGVhcm5fcmF0ZSA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgbWluX3Jvd3MgPSAyLA0KICAgICAgICAgICAgICAgICAgICBuZm9sZHMgPSA1KQ0KZ2JtLmgyby4yDQojIDU5MyBvbiB0cmFpbmluZyA7IDE4ODYuOTA0IG9uIGNyb3NzIHZhbGlkYXRpb24gZGF0YQ0KDQpnYm0uaDJvLjMgPC0gaDJvLmdibSh0cmFpbmluZ19mcmFtZSA9IHRyYWluaW5nX2RhdGFfeWVvam9obnNvbl9oMm8sDQogICAgICAgICAgICAgICAgICAgIHggPSBYLA0KICAgICAgICAgICAgICAgICAgICB5ID0gWSwNCiAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gImdhdXNzaWFuIiwNCiAgICAgICAgICAgICAgICAgICAgbnRyZWVzID0gNTAsDQogICAgICAgICAgICAgICAgICAgIG1heF9kZXB0aCA9IDE1LA0KICAgICAgICAgICAgICAgICAgICBsZWFybl9yYXRlID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICBtaW5fcm93cyA9IDIsDQogICAgICAgICAgICAgICAgICAgIG5mb2xkcyA9IDUpDQpnYm0uaDJvLjMNCiMgMjg4LjQ2ODQgb24gdHJhaW5pbmcgOyAxODU1Ljc3IG9uIGNyb3NzIHZhbGlkYXRpb24gZGF0YQ0KDQpnYm0uaDJvLjQgPC0gaDJvLmdibSh0cmFpbmluZ19mcmFtZSA9IHRyYWluaW5nX2RhdGFfeWVvam9obnNvbl9oMm8sDQogICAgICAgICAgICAgICAgICAgIHggPSBYLA0KICAgICAgICAgICAgICAgICAgICB5ID0gWSwNCiAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gImdhdXNzaWFuIiwNCiAgICAgICAgICAgICAgICAgICAgbnRyZWVzID0gMzAsDQogICAgICAgICAgICAgICAgICAgIG1heF9kZXB0aCA9IDE1LA0KICAgICAgICAgICAgICAgICAgICBsZWFybl9yYXRlID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICBtaW5fcm93cyA9IDIsDQogICAgICAgICAgICAgICAgICAgIG5mb2xkcyA9IDUpDQpnYm0uaDJvLjQNCiMgNDYzLjU2ODkgb24gdHJhaW5pbmcgOyAxNzU2LjI2IG9uIGNyb3NzIHZhbGlkYXRpb24gZGF0YQ0KDQpnYm0uaDJvLjUgPC0gaDJvLmdibSh0cmFpbmluZ19mcmFtZSA9IHRyYWluaW5nX2RhdGFfeWVvam9obnNvbl9oMm8sDQogICAgICAgICAgICAgICAgICAgIHggPSBYLA0KICAgICAgICAgICAgICAgICAgICB5ID0gWSwNCiAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gImdhdXNzaWFuIiwNCiAgICAgICAgICAgICAgICAgICAgbnRyZWVzID0gMzAsDQogICAgICAgICAgICAgICAgICAgIG1heF9kZXB0aCA9IDE1LA0KICAgICAgICAgICAgICAgICAgICBsZWFybl9yYXRlID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICBtaW5fcm93cyA9IDIsDQogICAgICAgICAgICAgICAgICAgIGNvbF9zYW1wbGVfcmF0ZSA9IDAuOCwNCiAgICAgICAgICAgICAgICAgICAgbmZvbGRzID0gNSkNCmdibS5oMm8uNQ0KIyBNQUUgOiAxNzA2LjIzNSBvbiBjcm9zcyB2YWxpZGF0aW9uIGRhdGENCg0KZ2JtNS5wcmVkaWN0ZWQgPC0gaDJvLnByZWRpY3QoZ2JtLmgyby41LCBuZXdkYXRhID0gdGVzdGluZ19kYXRhX3llb2pvaG5zb25faDJvKQ0KZ2JtNS5lcnIgPC0gbWVhbihhYnModGVzdGluZ19kYXRhX3llb2pvaG5zb25faDJvJGRlcG1vbiAtIGdibTUucHJlZGljdGVkKSkNCmdibTUuZXJyICMgTUFFIDogMjE0MS4wNjkgb24gdGVzdCBzZXQNCg0KYGBgDQoNCiMjIyBFbnRyYWluZW1lbnQgZmluYWwgZHUgbWVpbGxldXIgbW9k6GxlIA0KDQpOb3VzIGNob2lzaXNzb25zIGxlIGRlcm5pZXIgbW9k6GxlIGVuc2VtYmxpc3RlICgqKmdibS5oMm8uNSoqKSBjb21tZSAqKm1laWxsZXVyIG1vZOhsZSoqIGRhbnMgbGEgbWVzdXJlIG/5IGlsIHBhcnZpZW50IOAgZGVzIHLpc3VsdGF0cyB0cuhzIGNvbXBhcmFibGVzIGF1eCBtZWlsbGV1cnMgculzZWF1eCBkZSBuZXVyb25lcyBtYWlzIHF1ZSBzb24gdGVtcHMgZGUgY2FsY3VsIGVzdCBiaWVuIHBsdXMgcmFwaWRlLiANCg0KQXVzc2ksIG5vdXMgcG91dm9ucyBk6XNvcm1haXMgcmFzc2VtYmxlciBlbiB1biBqZXUgZGUgZG9ubullcyB1bmlxdWUgbGVzIGRldXggamV1eCBxdWkgbm91cyBvbnQgcGVybWlzIGRlIGNob2lzaXIgY2UgbWVpbGxldXIgbW9k6GxlLCBldCBlbnRyYe5uZXIgbGUgbW9k6GxlIHN1ciBjZSBncmFuZCBqZXUgZGUgZG9ubullcyBkdSBtb2lzIGQnYW/7dCAyMDE4IGFmaW4gZCdhbelsaW9yZXIgc2EgcHVpc3NhbmNlIHBy6WRpY3RpdmUuIA0KDQpgYGB7cn0NCmgyby5pbml0KG50aHJlYWRzID0gLTEsIG1heF9tZW1fc2l6ZSA9ICc0ZycpDQoNCiMgbWVyZ2UgdGhlIGRhdGFzZXRzDQpUcmFpbmluZ0F1Z3VzdDIwMTggPC0gcmJpbmQuZGF0YS5mcmFtZSh0cmFpbkRhdGEsIHRlc3REYXRhKQ0KDQojIHByZXBhcmUgdGhlIGZ1bGwgZGF0YXNldA0KVHJhaW5pbmdBdWd1c3QyMDE4X2gybyA8LSBhcy5oMm8oVHJhaW5pbmdBdWd1c3QyMDE4KQ0KWSA8LSAiZGVwbW9uIg0KWCA8LSBzZXRkaWZmKG5hbWVzKFRyYWluaW5nQXVndXN0MjAxOF9oMm8pLCBZKQ0KDQojIHRyYWluIHRoZSBmaW5hbCBtb2RlbA0KZ2JtLmgyby5maW5hbCA8LSBoMm8uZ2JtKHRyYWluaW5nX2ZyYW1lID0gVHJhaW5pbmdBdWd1c3QyMDE4X2gybywNCiAgICAgICAgICAgICAgICAgICAgeCA9IFgsDQogICAgICAgICAgICAgICAgICAgIHkgPSBZLA0KICAgICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gPSAiZ2F1c3NpYW4iLA0KICAgICAgICAgICAgICAgICAgICBudHJlZXMgPSAzMCwNCiAgICAgICAgICAgICAgICAgICAgbWF4X2RlcHRoID0gMTUsDQogICAgICAgICAgICAgICAgICAgIGxlYXJuX3JhdGUgPSAwLjEsDQogICAgICAgICAgICAgICAgICAgIG1pbl9yb3dzID0gMiwNCiAgICAgICAgICAgICAgICAgICAgY29sX3NhbXBsZV9yYXRlID0gMC44KQ0KDQpoMm8udmFyaW1wX3Bsb3QoZ2JtLmgyby5maW5hbCwgMzIpICMgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzIHRvIHByZWRpY3QgdGhlIHJlc3BvbnNlDQoNCmBgYA0KDQojIyBFdmFsdWF0aW9uIGRlIGxhIG1vZOlsaXNhdGlvbg0KDQpFbmZpbiwgbm91cyB1dGlsaXNvbnMgbm90cmUgbWVpbGxldXIgbW9k6GxlLCBk6XNvcm1haXMgZW50cmHubukgc3VyIHVuIGdyYW5kIGpldSBkZSBkb25u6WVzLCBldCBsJ2FwcGxpcXVvbnMgc3VyIHVuIG5vdXZlYXUgamV1IGRlIGRvbm7pZXMsIGNlbHVpIGR1IG1vaXMgZCcqKmFv+3QgMjAxNyoqLCBkaXNwb25pYmxlIHN1ciBsZSBzaXRlIGRhdGEuZ291di5mciwgYWZpbiBkJ+l2YWx1ZXIgbGEgY2FwYWNpdOkgZGUgbm90cmUgbW9k6GxlIOAgc2UgZ+lu6XJhbGlzZXIuIE5vdXMgcmV0ZW5vbnMgbGUgbeptZSBtb2lzIGNhciBpbCBlc3QgcG9zc2libGUgcXUnZXhpc3RlbnQgY2VydGFpbmVzIHLpZ3VsYXJpdOlzIG1lbnN1ZWxsZXMgY2hhcXVlIGFubullLiAgDQoNCkxlIGNvZGUgc3VpdmFudCBwculwYXJlIGxlcyBkb25u6WVzIGR1IG1vaXMgZCdhb/t0IDIwMTcgcG91ciBxdSdlbGxlcyBzb2llbnQgZGFucyBsZSBt6m1lIGZvcm1hdCBxdWUgY2VsbGVzIGR1IG1vaXMgZCdhb/t0IDIwMTgsIHNhbnMgcXVvaSBsZSBtb2TobGUgbmUgdHJvdXZlcmFpdCBwYXMg4CBzJ2FwcGxpcXVlciBwdWlzcXVlIGxlcyB2YXJpYWJsZXMgc2VyYWllbnQgZGlmZulyZW50ZXMuIA0KDQpFdGFudCBkb25u6WVzIGxlcyBvYnNlcnZhdGlvbnMgZGVzIHZhcmlhYmxlcyBwculkaWN0aXZlcywgbm90cmUgbW9k6GxlIHBhcnZpZW50IOAgcHLpZGlyZSBsZSBuaXZlYXUgZGVzIGTpcGFzc2VtZW50cyBkJ2hvbm9yYWlyZXMgYXZlYyB1bmUgKiplcnJldXIgbW95ZW5uZSBkZSAxMTU2IGV1cm9zKiogcGFyIHJhcHBvcnQgYXV4IGTpcGFzc2VtZW50cyBy6WVsbGVtZW50IGNvbnN0YXTpcy4NCg0KDQpgYGB7cn0NCiMgbG9hZCB0aGUgbmV3IHRlc3QgZGF0YXNldCANCkF1Z3VzdDIwMTcgPC0gcmVhZF9kZWxpbSgiTjIwMTcwOC5jc3YiLCAiOyIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgbG9jYWxlID0gbG9jYWxlKGVuY29kaW5nID0gIklTTy04ODU5LTEiKSwgdHJpbV93cyA9IFRSVUUpDQpBdWd1c3QyMDE3IDwtIGRhdGEuZnJhbWUoQXVndXN0MjAxNykNCg0KIyByZW1vdmUgZHVwbGljYXRlZCBvciB1c2VsZXNzIGNvbHVtbnMNCkF1Z3VzdDIwMTckYXN1X25hdCA8LSBOVUxMDQpBdWd1c3QyMDE3JHByc19uYXQgPC0gTlVMTA0KQXVndXN0MjAxNyRTRVJJRSA8LSBOVUxMDQpBdWd1c3QyMDE3JGNwbF9jb2QgPC0gTlVMTA0KQXVndXN0MjAxNyRwcmVfc3RqMSA8LSBOVUxMDQpBdWd1c3QyMDE3JGV4ZV9zdGoxIDwtIE5VTEwNCkF1Z3VzdDIwMTckbF9wcmVfc3BlIDwtIE5VTEwgDQpBdWd1c3QyMDE3JHByZV9zcGUgPC0gTlVMTCANCkF1Z3VzdDIwMTckcHJlX3NwZTEgPC0gTlVMTA0KQXVndXN0MjAxNyRsX2V4ZV9zcGUgPC0gTlVMTCANCkF1Z3VzdDIwMTckZXhlX3NwZSA8LSBOVUxMIA0KQXVndXN0MjAxNyRleGVfc3BlMSA8LSBOVUxMIA0KQXVndXN0MjAxNyRyZW1fbW9uIDwtIE5VTEwgDQoNCiMgY2hhbmdlIHRoZSBuYXR1cmUgb2YgY29sdW1ucw0KQXVndXN0MjAxNyRsX3NlcmllIDwtIGZhY3RvcihBdWd1c3QyMDE3JGxfc2VyaWUpDQpBdWd1c3QyMDE3JGxfcHJzX25hdCA8LSBmYWN0b3IoQXVndXN0MjAxNyRsX3Byc19uYXQpDQpBdWd1c3QyMDE3JHNuc19kYXRlIDwtIGZhY3RvcihBdWd1c3QyMDE3JHNuc19kYXRlKQ0KQXVndXN0MjAxNyRsX2FzdV9uYXQgPC0gZmFjdG9yKEF1Z3VzdDIwMTckbF9hc3VfbmF0KQ0KQXVndXN0MjAxNyRsX2NwbF9jb2QgPC0gZmFjdG9yKEF1Z3VzdDIwMTckbF9jcGxfY29kKQ0KQXVndXN0MjAxNyRsX3ByZV9zcGUxIDwtIGZhY3RvcihBdWd1c3QyMDE3JGxfcHJlX3NwZTEpDQpBdWd1c3QyMDE3JGxfZXhlX3NwZTEgPC0gZmFjdG9yKEF1Z3VzdDIwMTckbF9leGVfc3BlMSkNCkF1Z3VzdDIwMTckbF9wcmVfc3RqMSA8LSBmYWN0b3IoQXVndXN0MjAxNyRsX3ByZV9zdGoxKQ0KQXVndXN0MjAxNyRsX2V4ZV9zdGoxIDwtIGZhY3RvcihBdWd1c3QyMDE3JGxfZXhlX3N0ajEpDQpBdWd1c3QyMDE3JHJlbV9kYXRlIDwtIGZhY3RvcihBdWd1c3QyMDE3JHJlbV9kYXRlKQ0KDQojIHN5bnRheA0KQXVndXN0MjAxNyRkZXBfbW9uIDwtIGdzdWIoJy4nLCAiIiwgZml4ZWQgPSBUUlVFLCB4ID0gQXVndXN0MjAxNyRkZXBfbW9uKQ0KQXVndXN0MjAxNyRkZXBfbW9uIDwtIGdzdWIoJywnLCAiLiIsIGZpeGVkID0gVFJVRSwgeCA9IEF1Z3VzdDIwMTckZGVwX21vbikNCkF1Z3VzdDIwMTckZGVwX21vbiA8LSBhcy5udW1lcmljKEF1Z3VzdDIwMTckZGVwX21vbikNCg0KQXVndXN0MjAxNyRyZWNfbW9uIDwtIGdzdWIoJy4nLCAiIiwgZml4ZWQgPSBUUlVFLCB4ID0gQXVndXN0MjAxNyRyZWNfbW9uKQ0KQXVndXN0MjAxNyRyZWNfbW9uIDwtIGdzdWIoJywnLCAiLiIsIGZpeGVkID0gVFJVRSwgeCA9IEF1Z3VzdDIwMTckcmVjX21vbikNCkF1Z3VzdDIwMTckcmVjX21vbiA8LSBhcy5udW1lcmljKEF1Z3VzdDIwMTckcmVjX21vbikNCg0KQXVndXN0MjAxNyRhY3RfY29lIDwtIGdzdWIoJy4nLCAiIiwgZml4ZWQgPSBUUlVFLCB4ID0gQXVndXN0MjAxNyRhY3RfY29lKQ0KQXVndXN0MjAxNyRhY3RfY29lIDwtIGdzdWIoJywnLCAiLiIsIGZpeGVkID0gVFJVRSwgeCA9IEF1Z3VzdDIwMTckYWN0X2NvZSkNCkF1Z3VzdDIwMTckYWN0X2NvZSA8LSBhcy5udW1lcmljKEF1Z3VzdDIwMTckYWN0X2NvZSkNCg0KIyBzdG9yZSB0aGUgdGFyZ2V0IHZhcmlhYmxlIGZvciBsYXRlciB1c2UNCnlfdGVzdCA8LSBBdWd1c3QyMDE3JGRlcF9tb24NCg0KIyBwcmUgcHJvY2VzcyB0aGUgbmV3IHRlc3QgZGF0YQ0KICAgICAgICAjIGR1bW15IHZhcmlhYmxlcw0KZHVtbWllc19tb2RlbCA8LSBkdW1teVZhcnMoZGVwX21vbiB+IC4sIGRhdGEgPSBBdWd1c3QyMDE3KQ0KQXVndXN0MjAxNyA8LSBwcmVkaWN0KGR1bW1pZXNfbW9kZWwsIG5ld2RhdGEgPSBBdWd1c3QyMDE3KQ0KQXVndXN0MjAxNyA8LSBkYXRhLmZyYW1lKEF1Z3VzdDIwMTcpDQoNCiAgICAgICAgIyB0cmFuc2Zvcm1hdGlvbg0KcHJlUHJvY2Vzc195ZW9qb2huc29uX21vZGVsIDwtIHByZVByb2Nlc3MoQXVndXN0MjAxNywgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIiwgIlllb0pvaG5zb24iLCAibnp2IikpDQpBdWd1c3QyMDE3IDwtIHByZWRpY3QocHJlUHJvY2Vzc195ZW9qb2huc29uX21vZGVsLCBuZXdkYXRhID0gQXVndXN0MjAxNykNCg0KICAgICAgICAjIGFwcGVuZCB0aGUgdGFyZ2V0LCB0aGF0IHJlbWFpbnMgdW5jaGFuZ2VkDQpBdWd1c3QyMDE3JGRlcF9tb24gPC0geV90ZXN0DQoNCnN0cihBdWd1c3QyMDE3KSANCiMgdGhlIHZhcmlhYmxlcyBhZnRlciB0aGlzIHByZS1wcm9jZXNzaW5nIGFyZSBleGFjdGx5IHRoZSBzYW1lIGFzIHRoZSBvbmVzIHVzZWQgZHVyaW5nIHRyYWluaW5nDQojIHRoaXMgaXMgYSBnb29kIHNpZ24gdGhhdCB0aGUgbW9kZWwgaXMgZ29pbmcgdG8gZ2VuZXJhbGl6ZSB3ZWxsDQoNCiAgICAgICAgIyBjaGFuZ2UgdGhlIGNvbHVtbnMnIG5hbWVzDQogICAgICAgICAgICAgICAgIyBub3RlIDogd2Uga2VlcCB0aGUgc2FtZSBuYW1lcyBhcyB0aGUgb25lcyB1c2VkIGR1cmluZyB0cmFpbmluZywgZXZlbiBmb3IgdGhlIGRhdGVzDQogICAgICAgICAgICAgICAgIyB0aGlzIGlzIG5vdCBhIHByb2JsZW0gc2luY2UgdGhlIHZhcmlhYmxlcyBhcmUgdGhlIHNhbWUNCmNvbG5hbWVzKEF1Z3VzdDIwMTcpWzE6MjFdIDwtIGMoImxzZXJpZUFNSXNvaW5zaW5maXJtaWVycyIsICJsc2VyaWVDU3BlY2lhbGlzdGVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibHNlcmllVG90YWxmcmFpc2RlZGVwbGFjZW1lbnRkZXNhdXhpbGlhaXJlc21lZGljYXV4IiwgInJlbWRhdGUyMDE4MDciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVtZGF0ZTIwMTgwOCIsICJzbnNkYXRlMjAxODA0IiwgInNuc2RhdGUyMDE4MDUiLCAic25zZGF0ZTIwMTgwNiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzbnNkYXRlMjAxODA3IiwgInNuc2RhdGUyMDE4MDgiLCAibGFzdW5hdEFjY2lkZW50ZHVUcmF2YWlsTWFsYWRpZVByb2Zlc3Npb25uZWxsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxhc3VuYXRNYWxhZGllIiwgImxhc3VuYXRNYXRlcm5pdGUiLCAibGNwbGNvZE1ham9yYXRpb25kZW51aXQiLCAibGNwbGNvZFNhbnNtYWpvcmF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUkVNVEFVIiwgImxwcmVzcGUxQXV0cmVzIiwgImxwcmVzcGUxTWVkZWNpbnNPbW5pcHJhdGljaWVuc2xpYmVyYXV4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibHByZXNwZTFNZWRlY2luc1NwZWNpYWxpc3Rlc2xpYmVyYXV4IiwgImxwcmVzdGoxTGliZXJhbCIsICJscHJlc3RqMVNhbGFyaWUiICApDQoNCmNvbG5hbWVzKEF1Z3VzdDIwMTcpWzIyOjMzXSA8LSBjKCJsZXhlc3BlMUF1dHJlcyIsICJsZXhlc3BlMUluZmlybWllcnMiLCAibGV4ZXNwZTFMYWJvcmF0b2lyZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGV4ZXNwZTFNYXNzZXVyc0tpbmVzaXRoZXJhcGV1dGVzIiwgImxleGVzcGUxTWVkZWNpbnNPbW5pcHJhdGljaWVucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsZXhlc3BlMU1lZGVjaW5zU3BlY2lhbGlzdGVzIiwgImxleGVzdGoxTGliZXJhbCIsICJsZXhlc3RqMVNhbGFyaWUiLCAicmVjbW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFjdGRuYiIsICJhY3Rjb2UiLCAiZGVwbW9uIikNCg0KIyBoMm8gZm9ybWF0DQpBdWd1c3QyMDE3X2gybyA8LSBhcy5oMm8oQXVndXN0MjAxNykNCg0KIyB1c2UgdGhlIGdibSBtb2RlbCB0aGF0IHdhcyB0cmFpbmVkIG9uIHRoZSBlbnRpcmUgQXVndXN0MjAxOA0KZ2JtLmZpbmFsLnByZWRpY3Rpb25zIDwtIGgyby5wcmVkaWN0KGdibS5oMm8uZmluYWwsIG5ld2RhdGEgPSBBdWd1c3QyMDE3X2gybykNCmdibS5lcnIgPC0gbWVhbihhYnMoQXVndXN0MjAxN19oMm8kZGVwbW9uIC0gZ2JtLmZpbmFsLnByZWRpY3Rpb25zKSkNCmdibS5lcnIgIyBuZXcgdGVzdCBNQUUgOiAxMTU2DQoNCmdibS5maW5hbC5wcmVkaWN0aW9uc19kZiA8LSBhcy5kYXRhLmZyYW1lKGdibS5maW5hbC5wcmVkaWN0aW9ucykNCg0KIyBzdW1tYXJ5IG9mIHRoZSBwcmVkaWN0aW9ucw0Kc3VtbWFyeShnYm0uZmluYWwucHJlZGljdGlvbnNfZGYpIA0KIyAxc3QgUXVhcnRpbGUgOiA3NSA7IG1lZGlhbiA6IDc3IDsgM3JkIFF1YXJ0aWxlIDogMTAzDQojIG1lYW4gOiAxODQxIA0KIyBtaW5pbXVtIDogLTU3NjE4OA0KIyBtYXhpbXVtIDogMTkwOTIyNzUNCg0KYGBgDQoNCiMjIENvbmNsdXNpb24NCg0KTm90cmUgbWVpbGxldXIgYWxnb3JpdGhtZSBhIG1vZOlsaXPpIGxlcyBk6XBhc3NlbWVudHMgZCdob25vcmFpcmVzIGRlcyBwcm9mZXNzaW9ubmVscyBkZSBzYW506SBldCBzb24g6XZhbHVhdGlvbiBzdXIgbGVzIGRvbm7pZXMgZCdhb/t0IDIwMTcgZmFpdCBhcHBhcmHudHJlIHVuZSAqKmVycmV1ciBtb3llbm5lIGRlIDExNTYgZXVyb3MqKi4gQXV0cmVtZW50IGRpdCwgcG91ciBjaGFjdW5lIGRlcyBvYnNlcnZhdGlvbnMgZHUgamV1IGRlIGRvbm7pZXMgZCdhb/t0IDIwMTcsIGxhIHZhbGV1ciBwculkaXRlIHBhciBsJ2FsZ29yaXRobWUgc2UgdHJvbXBlLCBlbiBtb3llbm5lLCBkZSAxMTU2IGV1cm9zLiBDb21tZW50IGludGVycHLpdGVyIGNlIHLpc3VsdGF0ID8NCg0KRCd1biBj9HTpLCBjZXR0ZSBlcnJldXIgbW95ZW5uZSBwZXV0IHNlbWJsZXIgKirpbGV26WUqKiBkYW5zIGxhIG1lc3VyZSBv+SBpbCBuJ2V4aXN0ZSBlbiBmYWl0IGF1Y3VuIGTpcGFzc2VtZW50IGQnaG9ub3JhaXJlcyBwb3VyIGRlIHRy6HMgbm9tYnJldXggYWN0ZXMgbelkaWNhdXguIA0KDQpEJ3VuIGF1dHJlIGP0dOksIGNldHRlIGVycmV1ciBtb3llbm5lIGRlIDExNTYgZXVyb3MgZXN0ICoqZmFpYmxlKiogYXUgcmVnYXJkIGRlIGxhIHRy6HMgZ3JhbmRlIGRpc3BlcnNpb24gZGVzIG9ic2VydmF0aW9ucywgY29tcHJpc2VzIGVudHJlIC0zOTAwIGV1cm9zIGV0IHBsdXMgZGUgMzEgMDAwIDAwMCBldXJvcywgb3UgZGUgbCfpY2FydCB0eXBlLCBkJ2Vudmlyb24gMTA4IDAwMCBldXJvcy4gDQoNClBhciBhaWxsZXVycywgbCdhbGdvcml0aG1lIGEgbWlzIGVuIOl2aWRlbmNlIGxlcyAqKnZhcmlhYmxlcyBsZXMgcGx1cyBpbXBvcnRhbnRlcyoqIHBvdXIgcHLpZGlyZSBsZSBuaXZlYXUsIHF1J2lsIHNvaXQgbulnYXRpZiwgbnVsLCBvdSDpbGV26SwgZGVzIGTpcGFzc2VtZW50cyBkJ2hvbm9yYWlyZXMuIElsIHMnYWdpdCBwcmluY2lwYWxlbWVudCBkdSBtb250YW50IGRlIGJhc2UgZHUgcmVtYm91cnNlbWVudCwgZHUgbm9tYnJlIGQnYWN0ZXMgbelkaWNhdXggcHJhdGlxdelzLCBkdSB0YXV4IGRlIHJlbWJvdXJzZW1lbnQsIGR1IGNvZWZmaWNpZW50IGdsb2JhbC4gRGUgcGx1cywgc2FucyBzdXJwcmlzZSwgbGEgc3DpY2lhbGl06SBkdSBwcmVzY3JpcHRldXIgZW4gIm3pZGVjaW4gc3DpY2lhbGlzdGUgbGli6XJhbCIgb3UgZW4gIm3pZGVjaW4gb21uaXByYXRpY2llbiBsaWLpcmFsIiwgbGUgbW9kZSBkJ2V4ZXJjaWNlIGRlcyBleOljdXRhbnRzIGVuICJt6WRlY2luIHNw6WNpYWxpc3RlIiBldCBsZSBzdGF0dXQgImxpYulyYWwiIGRlIGwnZXjpY3V0YW50IHNvbnQg6WdhbGVtZW50IHV0aWxlcyBwb3VyIHBy6WRpcmUgbGUgbml2ZWF1IGRlcyBk6XBhc3NlbWVudHMuIElsIG5lIHMnYWdpdCB0b3V0ZWZvaXMgcGFzIGzgIGQndW4gbGllbiBkZSBjYXVzYWxpdOksIHF1aSBuZSBwb3VycmFpdCDqdHJlIG1pcyBlbiDpdmlkZW5jZSBxdWUgcGFyIGRlcyBt6XRob2RlcyDpY29ub23pdHJpcXVlcywgdGFuZGlzIHF1ZSBsZXMgYWxnb3JpdGhtZXMgZCdhcHByZW50aXNzYWdlIGRlcyBkb25u6WVzIHJlY2hlcmNoZW50IHBsdXT0dCBkZXMgY29yculsYXRpb25zLiANCg0KDQo=