Este problema es una de las competencias juego de la plataforma Kaggle, en la cual busca predecir el precio de venta de una propiedad en dólares (SalePrice) teniendo un gran número de características (79) como su área, número de habitaciones, área del garaje, altura del sótano, etc.
Se evalúa el nivel de ajuste del modelo en base a la raíz del error cuadrático medio (RMSE).
Enlace competencia: https://www.kaggle.com/c/house-prices-advanced-regression-techniques/overview
la competencia brinda una base de entrenamiento y una base de prueba, en esta última se debe predecir el precio de venta de un conjunto de propiedades desconociendo su valor original.
Con la ayuda de R se crea una función para calcular el RMSE de las predicciones obtenidas.
A continuación se presentarán los dos mejores modelos de ambos estudiantes, ambos seleccionados por el puntaje alcanzado en la competencia.
Jaime Andrés Molina Correa - Mejor modelo (0.15649 de puntaje)
Se realizaron modelos de regresión lineal, bagging y Random Forest para predecir el predecir el precio de venta de una propiedad. El mejor modelo obtenido fue un baggin con 12 variables consideradas. El procedimiento para llegar al modelo se enseña a continuación.
paquetes utilizados:
A continuación se enseñan el número de datos faltantes (NA) en las variables del conjunto de entrenamiento. Las que no se muestran no tienen datos faltantes.
## PoolQC MiscFeature Alley Fence FireplaceQu LotFrontage
## 1453 1406 1369 1179 690 259
## GarageType GarageYrBlt GarageFinish GarageQual GarageCond BsmtExposure
## 81 81 81 81 81 38
## BsmtFinType2 BsmtQual BsmtCond BsmtFinType1 MasVnrType MasVnrArea
## 38 37 37 37 8 8
## Electrical
## 1
Se eliminan las variables con mayor número de datos faltantes, variable con más de 50 faltantes en el conjunto de entrenamiento se desprecia.
train <- train %>% select(-c(PoolQC, MiscFeature, Alley, Fence,
FireplaceQu, LotFrontage, GarageType, GarageYrBlt,
GarageFinish, GarageQual,GarageCond))
Posteriormente se eliminan las observaciones con datos faltantes ya que ahora se podrían perder pocas observaciones.
Modelo Random Forest para selección de variables.
Para entrenar un modelo con el método Random Forest se hace uso de la función randomForest del paquete randomForest.
Se procede a la creación del modelo con el número de árboles que utiliza por defecto la función (500). El número de variables que selecciona aleatoriamente en cada muestra bootstrap es calculado según el número de variables consideradas, para este número de variables se usan 22 en cada muestra.
##
## Call:
## randomForest(formula = SalePrice ~ ., data = train[, -1], importance = TRUE)
## Type of random forest: regression
## Number of trees: 500
## No. of variables tried at each split: 22
##
## Mean of squared residuals: 781781463
## % Var explained: 87.55
A continación se muestran cuales son las variables que aportan más al modelo según dos metricas diferentes.

Modelo Bagging.
En un principio se pensó en utilizar las 3 variables que son más informativas según el MSE, pero al no tener muy buenos resultados en el puntaje de la competencia, se decide utilizar las primeras 12. A continuación se crea el modelo.
##
## Call:
## randomForest(formula = SalePrice ~ GrLivArea + Neighborhood + OverallQual + TotalBsmtSF + X1stFlrSF + X2ndFlrSF + GarageCars + GarageArea + LotArea + BsmtFinSF1 + ExterQual + FullBath, data = train, importance = TRUE, ntree = 500, mtry = 12)
## Type of random forest: regression
## Number of trees: 500
## No. of variables tried at each split: 12
##
## Mean of squared residuals: 847029853
## % Var explained: 86.51
Conjunto de prueba kaggle
Se seleccionan solo las variables usadas en el modelo final
test <- test %>% select(Id, GrLivArea, Neighborhood, OverallQual, TotalBsmtSF,
X1stFlrSF, X2ndFlrSF, GarageCars, GarageArea, LotArea,
BsmtFinSF1, ExterQual, FullBath)
número de datos faltantes en el conjunto de prueba.
## TotalBsmtSF GarageCars GarageArea BsmtFinSF1 Id GrLivArea
## 1 1 1 1 0 0
## Neighborhood OverallQual X1stFlrSF X2ndFlrSF LotArea ExterQual
## 0 0 0 0 0 0
## FullBath
## 0
Se puede observar que cuatro de las variables usadas en el modelo tienen un valor faltante, por lo que se deben reemplazar. Como son variables continuas se decide reemplazar el valor faltante con la mediana de la respectiva variable en el conjunto de entrenamiento
Predicción y creación de base de datos
Finalmente, con la ayuda de la función predict() se obtienen los valores ajustados para subir a la competencia.
Por último, se crea la base de datos.
Valentina Garcia Velasquez - Mejor modelo (0.13398 de puntaje)
Se lee nuevamente el conjunto de datos y entrenamiento
Se seleccionan únicamente las columnas numéricas y se reeemplazan los NA con el valor -99.
Se crea una matriz con las covariables y otra para la variable respuesta, re realiza el mismo procedimiento para el conjunto de prueba.
Modelo xgboost con grid search (Mejor modelo)
Se ajusta un modelo XGBoost, se realiza una rejilla con valores para los diferentes parámetros y se obtiene el más óptimo con el método cross validation.
# Se deben preparar los datos de otra forma
X <- xgb.DMatrix(as.matrix(train_x))
y <- train_y
xgb_trcontrol = trainControl(
method = "cv", # Cross validation
number = 10,
allowParallel = TRUE,
verboseIter = FALSE,
returnData = FALSE
)
xgbGrid <- expand.grid(nrounds=c(100,200,300,400),
max_depth = c(3:7),
eta = seq(0.05, 1,10),
gamma = seq(0.01,1,10),
colsample_bytree = c(0.75),
subsample = c(0.50),
min_child_weight = c(0))
Entrenamiento del modelo
Se guardan las predicciones
Modelo XGBoost con gridsearch y variables seleccionadas anteriormente con RF
SalePrice ~ ,
train_x2 <- numeric_columns[, -c(1,38)]
train_x2 <- data.matrix(train_x2[,c('GrLivArea','OverallQual',
'TotalBsmtSF','X1stFlrSF','X2ndFlrSF','GarageCars',
'GarageArea', 'LotArea', 'BsmtFinSF1','FullBath')])
train_y2 <- numeric_columns[, 38]
test_x2 <- numeric_columns2[, -1]
test_x2 <- data.matrix(test_x2[,c('GrLivArea','OverallQual',
'TotalBsmtSF','X1stFlrSF','X2ndFlrSF','GarageCars',
'GarageArea', 'LotArea', 'BsmtFinSF1','FullBath')])
# Se deben preparar los datos de otra forma
X2 <- xgb.DMatrix(as.matrix(train_x2))
y2 <- train_y2
xgb_trcontrol = trainControl(
method = "cv", # Cross validation
number = 10,
allowParallel = TRUE,
verboseIter = FALSE,
returnData = FALSE
)
xgbGrid <- expand.grid(nrounds=c(100,200,300,400),
max_depth = c(3:7),
eta = seq(0.05, 1,10),
gamma = seq(0.01,1,10),
colsample_bytree = c(0.75),
subsample = c(0.50),
min_child_weight = c(0))
Evidencias
Ranking alcanzado

Mejor puntaje Valentina Garcia

Mejor puntaje Jaime Molina

LS0tCnRpdGxlOiAiSG91c2UgUHJpY2VzOiBBZHZhbmNlZCBSZWdyZXNzaW9uIFRlY2huaXF1ZXMiCmF1dGhvcjogIlZhbGVudGluYSBHYXJjw61hIFZlbGFzcXVleiAtIEphaW1lIEFuZHJlcyBNb2xpbmEgQ29ycmVhIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBjb3NtbwogICAgaGlnaGxpZ2h0OiBrYXRlCiAgICBjc3M6IGZvcm1hdC5jc3MKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IFRSVUUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKYGBgCgoKRXN0ZSBwcm9ibGVtYSBlcyB1bmEgZGUgbGFzIGNvbXBldGVuY2lhcyBqdWVnbyBkZSBsYSBwbGF0YWZvcm1hIEthZ2dsZSwgZW4gbGEgY3VhbCBidXNjYSBwcmVkZWNpciBlbCBwcmVjaW8gZGUgdmVudGEgZGUgdW5hIHByb3BpZWRhZCBlbiBkw7NsYXJlcyAoU2FsZVByaWNlKSB0ZW5pZW5kbyB1biBncmFuIG7Dum1lcm8gZGUgY2FyYWN0ZXLDrXN0aWNhcyAoNzkpIGNvbW8gc3Ugw6FyZWEsIG7Dum1lcm8gZGUgaGFiaXRhY2lvbmVzLCDDoXJlYSBkZWwgZ2FyYWplLCBhbHR1cmEgZGVsIHPDs3Rhbm8sIGV0Yy4KClNlIGV2YWzDumEgZWwgbml2ZWwgZGUgYWp1c3RlIGRlbCBtb2RlbG8gZW4gYmFzZSBhIGxhIHJhw616IGRlbCBlcnJvciBjdWFkcsOhdGljbyBtZWRpbyAoUk1TRSkuCgpFbmxhY2UgY29tcGV0ZW5jaWE6IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYy9ob3VzZS1wcmljZXMtYWR2YW5jZWQtcmVncmVzc2lvbi10ZWNobmlxdWVzL292ZXJ2aWV3CgpsYSBjb21wZXRlbmNpYSBicmluZGEgdW5hIGJhc2UgZGUgZW50cmVuYW1pZW50byB5IHVuYSBiYXNlIGRlIHBydWViYSwgZW4gZXN0YSDDumx0aW1hIHNlIGRlYmUgcHJlZGVjaXIgZWwgcHJlY2lvIGRlIHZlbnRhIGRlIHVuIGNvbmp1bnRvIGRlIHByb3BpZWRhZGVzIGRlc2Nvbm9jaWVuZG8gc3UgdmFsb3Igb3JpZ2luYWwuCgpgYGB7cn0KIyBCYXNlIGRlIGVudHJlbmFtaWVudG8KdHJhaW4gPC0gcmVhZC5jc3YoZmlsZSA9ICJ0cmFpbi5jc3YiLCBoZWFkZXIgPSBULCBzdHJpbmdzQXNGYWN0b3JzID0gVCkKCiMgQmFzZSBkZSBwcnVlYmEKdGVzdCA8LSByZWFkLmNzdihmaWxlID0gInRlc3QuY3N2IiwgaGVhZGVyID0gVCwgc3RyaW5nc0FzRmFjdG9ycyA9IFQpCmBgYAoKQ29uIGxhIGF5dWRhIGRlIFIgc2UgY3JlYSB1bmEgZnVuY2nDs24gcGFyYSBjYWxjdWxhciBlbCBSTVNFIGRlIGxhcyBwcmVkaWNjaW9uZXMgb2J0ZW5pZGFzLiAKCmBgYHtyfQpSTVNFIDwtIGZ1bmN0aW9uKHksIHloYXQpIHNxcnQobWVhbigoeS15aGF0KSoqMikpCmBgYAoKQSBjb250aW51YWNpw7NuIHNlIHByZXNlbnRhcsOhbiBsb3MgZG9zIG1lam9yZXMgbW9kZWxvcyBkZSBhbWJvcyBlc3R1ZGlhbnRlcywgYW1ib3Mgc2VsZWNjaW9uYWRvcyBwb3IgZWwgcHVudGFqZSBhbGNhbnphZG8gZW4gbGEgY29tcGV0ZW5jaWEuCgojIEphaW1lIEFuZHLDqXMgTW9saW5hIENvcnJlYSAtIE1lam9yIG1vZGVsbyAoMC4xNTY0OSBkZSBwdW50YWplKQoKU2UgcmVhbGl6YXJvbiBtb2RlbG9zIGRlIHJlZ3Jlc2nDs24gbGluZWFsLCBiYWdnaW5nIHkgUmFuZG9tIEZvcmVzdCBwYXJhIHByZWRlY2lyIGVsIHByZWRlY2lyIGVsIHByZWNpbyBkZSB2ZW50YSBkZSB1bmEgcHJvcGllZGFkLiBFbCBtZWpvciBtb2RlbG8gb2J0ZW5pZG8gZnVlIHVuIGJhZ2dpbiBjb24gMTIgdmFyaWFibGVzIGNvbnNpZGVyYWRhcy4gRWwgcHJvY2VkaW1pZW50byBwYXJhIGxsZWdhciBhbCBtb2RlbG8gc2UgZW5zZcOxYSBhIGNvbnRpbnVhY2nDs24uCgoqKnBhcXVldGVzIHV0aWxpemFkb3M6KioKCmBgYHtyfQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkgIyBQYXJhIGNyZWFyIG1vZGVsb3MgZGUgYm9zcXVlcyBhbGVhdG9yaW9zIHkgYmFnZ2luZwpsaWJyYXJ5KGRwbHlyKSAgICAgICAgIyBQYXJhIG1vZGlmaWNhciBsb3MgY29uanVudG9zIGRlIGRhdG9zLgpgYGAKCmBgYHtyfQpzZW1pbGxhIDwtIDE5OTggIyBTZSBjcmVhIHVuYSBzZW1pbGxhIHBhcmEgcmVwcm9kdWN0aXZpZGFkLgpgYGAKCkEgY29udGludWFjacOzbiBzZSBlbnNlw7FhbiBlbCBuw7ptZXJvIGRlIGRhdG9zIGZhbHRhbnRlcyAoYE5BYCkgZW4gbGFzIHZhcmlhYmxlcyBkZWwgY29uanVudG8gZGUgZW50cmVuYW1pZW50by4gTGFzIHF1ZSBubyBzZSBtdWVzdHJhbiBubyB0aWVuZW4gZGF0b3MgZmFsdGFudGVzLgoKYGBge3J9CnNvcnQoc2FwcGx5KHRyYWluLCBmdW5jdGlvbih5KSBzdW0obGVuZ3RoKHdoaWNoKGlzLm5hKHkpKSkpKSwgZGVjcmVhc2luZyA9IFQpWzE6MTldCmBgYAoKU2UgZWxpbWluYW4gbGFzIHZhcmlhYmxlcyBjb24gbWF5b3IgbsO6bWVybyBkZSBkYXRvcyBmYWx0YW50ZXMsIHZhcmlhYmxlIGNvbiBtw6FzIGRlIDUwIGZhbHRhbnRlcyBlbiBlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvIHNlIGRlc3ByZWNpYS4KCmBgYHtyfQp0cmFpbiA8LSB0cmFpbiAlPiUgc2VsZWN0KC1jKFBvb2xRQywgTWlzY0ZlYXR1cmUsIEFsbGV5LCBGZW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGaXJlcGxhY2VRdSwgTG90RnJvbnRhZ2UsIEdhcmFnZVR5cGUsIEdhcmFnZVlyQmx0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdhcmFnZUZpbmlzaCwgR2FyYWdlUXVhbCxHYXJhZ2VDb25kKSkKYGBgCgpQb3N0ZXJpb3JtZW50ZSBzZSBlbGltaW5hbiBsYXMgb2JzZXJ2YWNpb25lcyBjb24gZGF0b3MgZmFsdGFudGVzIHlhIHF1ZSBhaG9yYSBzZSBwb2Ryw61hbiBwZXJkZXIgcG9jYXMgb2JzZXJ2YWNpb25lcy4KCmBgYHtyfQp0cmFpbiA8LSBuYS5vbWl0KHRyYWluKQpgYGAKCgojIyBNb2RlbG8gUmFuZG9tIEZvcmVzdCBwYXJhIHNlbGVjY2nDs24gZGUgdmFyaWFibGVzLgoKUGFyYSBlbnRyZW5hciB1biBtb2RlbG8gY29uIGVsIG3DqXRvZG8gKlJhbmRvbSBGb3Jlc3QqIHNlIGhhY2UgdXNvIGRlIGxhIGZ1bmNpw7NuIGByYW5kb21Gb3Jlc3RgIGRlbCBwYXF1ZXRlIGByYW5kb21Gb3Jlc3RgLgoKU2UgcHJvY2VkZSBhIGxhIGNyZWFjacOzbiBkZWwgbW9kZWxvIGNvbiBlbCBuw7ptZXJvIGRlIMOhcmJvbGVzIHF1ZSB1dGlsaXphIHBvciBkZWZlY3RvIGxhIGZ1bmNpw7NuICg1MDApLiBFbCBuw7ptZXJvIGRlIHZhcmlhYmxlcyBxdWUgc2VsZWNjaW9uYSBhbGVhdG9yaWFtZW50ZSBlbiBjYWRhIG11ZXN0cmEgYm9vdHN0cmFwIGVzIGNhbGN1bGFkbyBzZWfDum4gZWwgbsO6bWVybyBkZSB2YXJpYWJsZXMgY29uc2lkZXJhZGFzLCBwYXJhIGVzdGUgbsO6bWVybyBkZSB2YXJpYWJsZXMgc2UgdXNhbiAyMiBlbiBjYWRhIG11ZXN0cmEuCgpgYGB7cn0Kc2V0LnNlZWQoc2VtaWxsYSkKbW9kUkYgPC0gcmFuZG9tRm9yZXN0KFNhbGVQcmljZSB+IC4sIGRhdGEgPSB0cmFpblssLTFdLCBpbXBvcnRhbmNlID0gVFJVRSkKbW9kUkYKYGBgCgpBIGNvbnRpbmFjacOzbiBzZSBtdWVzdHJhbiBjdWFsZXMgc29uIGxhcyB2YXJpYWJsZXMgcXVlIGFwb3J0YW4gbcOhcyBhbCBtb2RlbG8gc2Vnw7puIGRvcyBtZXRyaWNhcyBkaWZlcmVudGVzLgoKYGBge3J9CnZhckltcFBsb3QobW9kUkYsIGNvbCA9IDQsIHBjaCA9IDE2LCBtYWluID0gIk1vZGVsbyBSYW5kb20gRm9yZXN0IiwgY2V4ID0gMC42NSkKYGBgCgojIyBNb2RlbG8gQmFnZ2luZy4KCkVuIHVuIHByaW5jaXBpbyBzZSBwZW5zw7MgZW4gdXRpbGl6YXIgbGFzIDMgdmFyaWFibGVzIHF1ZSBzb24gbcOhcyBpbmZvcm1hdGl2YXMgc2Vnw7puIGVsIE1TRSwgcGVybyBhbCBubyB0ZW5lciBtdXkgYnVlbm9zIHJlc3VsdGFkb3MgZW4gZWwgcHVudGFqZSBkZSBsYSBjb21wZXRlbmNpYSwgc2UgZGVjaWRlIHV0aWxpemFyIGxhcyBwcmltZXJhcyAxMi4gQSBjb250aW51YWNpw7NuIHNlIGNyZWEgZWwgbW9kZWxvLgoKCmBgYHtyfQpzZXQuc2VlZChzZW1pbGxhKQptb2RiYWcgPC0gcmFuZG9tRm9yZXN0KFNhbGVQcmljZSB+IEdyTGl2QXJlYSArIE5laWdoYm9yaG9vZCArIE92ZXJhbGxRdWFsICsKICAgICAgICAgICAgICAgICAgICAgICAgVG90YWxCc210U0YgKyBYMXN0RmxyU0YgKyBYMm5kRmxyU0YgKyBHYXJhZ2VDYXJzICsKICAgICAgICAgICAgICAgICAgICAgICAgR2FyYWdlQXJlYSArIExvdEFyZWEgKyBCc210RmluU0YxICsgRXh0ZXJRdWFsICsgRnVsbEJhdGgsCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW4sCiAgICAgICAgICAgICAgICAgICAgICBpbXBvcnRhbmNlID0gVFJVRSwgbnRyZWUgPSA1MDAsIG10cnkgPSAxMikKCm1vZGJhZwpgYGAKCgojIyBDb25qdW50byBkZSBwcnVlYmEga2FnZ2xlCgpTZSBzZWxlY2Npb25hbiBzb2xvIGxhcyB2YXJpYWJsZXMgdXNhZGFzIGVuIGVsIG1vZGVsbyBmaW5hbAoKYGBge3J9CnRlc3QgPC0gdGVzdCAlPiUgc2VsZWN0KElkLCBHckxpdkFyZWEsIE5laWdoYm9yaG9vZCwgT3ZlcmFsbFF1YWwsIFRvdGFsQnNtdFNGLAogICAgICAgICAgICAgICAgICAgICAgICBYMXN0RmxyU0YsIFgybmRGbHJTRiwgR2FyYWdlQ2FycywgR2FyYWdlQXJlYSwgTG90QXJlYSwKICAgICAgICAgICAgICAgICAgICAgICAgQnNtdEZpblNGMSwgRXh0ZXJRdWFsLCBGdWxsQmF0aCkKYGBgCgpuw7ptZXJvIGRlIGRhdG9zIGZhbHRhbnRlcyBlbiBlbCBjb25qdW50byBkZSBwcnVlYmEuCgpgYGB7cn0Kc29ydChzYXBwbHkodGVzdCwgZnVuY3Rpb24oeSkgc3VtKGxlbmd0aCh3aGljaChpcy5uYSh5KSkpKSksIGRlY3JlYXNpbmcgPSBUKQpgYGAKClNlIHB1ZWRlIG9ic2VydmFyIHF1ZSBjdWF0cm8gZGUgbGFzIHZhcmlhYmxlcyB1c2FkYXMgZW4gZWwgbW9kZWxvIHRpZW5lbiB1biB2YWxvciBmYWx0YW50ZSwgcG9yIGxvIHF1ZSBzZSBkZWJlbiByZWVtcGxhemFyLiBDb21vIHNvbiB2YXJpYWJsZXMgY29udGludWFzIHNlIGRlY2lkZSByZWVtcGxhemFyIGVsIHZhbG9yIGZhbHRhbnRlIGNvbiBsYSBtZWRpYW5hIGRlIGxhIHJlc3BlY3RpdmEgdmFyaWFibGUgZW4gZWwgY29uanVudG8gZGUgZW50cmVuYW1pZW50bwoKYGBge3J9CnRlc3QkVG90YWxCc210U0ZbaXMubmEodGVzdCRUb3RhbEJzbXRTRildIDwtIG1lZGlhbih0cmFpbiRUb3RhbEJzbXRTRiwgbmEucm0gPSBUKQp0ZXN0JEdhcmFnZUNhcnNbaXMubmEodGVzdCRHYXJhZ2VDYXJzKV0gPC0gbWVkaWFuKHRyYWluJEdhcmFnZUNhcnMsIG5hLnJtID0gVCkKdGVzdCRHYXJhZ2VBcmVhW2lzLm5hKHRlc3QkR2FyYWdlQXJlYSldIDwtIG1lZGlhbih0cmFpbiRHYXJhZ2VBcmVhLCBuYS5ybSA9IFQpCnRlc3QkQnNtdEZpblNGMVtpcy5uYSh0ZXN0JEJzbXRGaW5TRjEpXSA8LSBtZWRpYW4odHJhaW4kQnNtdEZpblNGMSwgbmEucm0gPSBUKQpgYGAKCiMjIFByZWRpY2Npw7NuIHkgY3JlYWNpw7NuIGRlIGJhc2UgZGUgZGF0b3MKCkZpbmFsbWVudGUsIGNvbiBsYSBheXVkYSBkZSBsYSBmdW5jacOzbiBgcHJlZGljdCgpYCBzZSBvYnRpZW5lbiBsb3MgdmFsb3JlcyBhanVzdGFkb3MgcGFyYSBzdWJpciBhIGxhIGNvbXBldGVuY2lhLgoKYGBge3J9CnRlc3QuU2FsZXMgPC0gcHJlZGljdChtb2RiYWcsIG5ld2RhdGEgPSB0ZXN0LCB0eXBlID0gInJlc3BvbnNlIikKYGBgCgpQb3Igw7psdGltbywgc2UgY3JlYSBsYSBiYXNlIGRlIGRhdG9zLgoKYGBge3IsIGV2YWw9RkFMU0V9CndyaXRlLmNzdihkYXRhLmZyYW1lKElkID0gdGVzdCRJZCwgU2FsZVByaWNlID0gdGVzdC5TYWxlcyksIGZpbGUgPSAiYmFnZ2luMTIuY3N2IiwKICAgICAgICAgIHJvdy5uYW1lcyA9IEYpCmBgYAoKCiMgVmFsZW50aW5hIEdhcmNpYSBWZWxhc3F1ZXogLSBNZWpvciBtb2RlbG8gKDAuMTMzOTggZGUgcHVudGFqZSkKCgpgYGB7cn0KbGlicmFyeSh4Z2Jvb3N0KQpsaWJyYXJ5KGNhcmV0KQpgYGAKClNlIGxlZSBudWV2YW1lbnRlIGVsIGNvbmp1bnRvIGRlIGRhdG9zIHkgZW50cmVuYW1pZW50bwoKCmBgYHtyfQojIExlZXIgZGF0b3MgZGUgZW50cmVuYW1pZW50bwpkZiA8LSByZWFkLmNzdigndHJhaW4uY3N2Jywgc3RyaW5nc0FzRmFjdG9ycyA9IFQpCnRlc3QgPC0gcmVhZC5jc3YoJ3Rlc3QuY3N2Jywgc3RyaW5nc0FzRmFjdG9ycyA9IFQpCmBgYAoKU2Ugc2VsZWNjaW9uYW4gw7puaWNhbWVudGUgbGFzIGNvbHVtbmFzIG51bcOpcmljYXMgeSBzZSByZWVlbXBsYXphbiBsb3MgTkEgY29uIGVsIHZhbG9yIC05OS4KCmBgYHtyfQpudW1lcmljX2NvbHVtbnMgPC0gZGYgJT4lIGRwbHlyOjpzZWxlY3Qod2hlcmUoaXMubnVtZXJpYykpCm51bWVyaWNfY29sdW1uc1tpcy5uYShudW1lcmljX2NvbHVtbnMpXSA8LSAtOTkKYGBgCgoKU2UgY3JlYSB1bmEgbWF0cml6IGNvbiBsYXMgY292YXJpYWJsZXMgeSBvdHJhIHBhcmEgbGEgdmFyaWFibGUgcmVzcHVlc3RhLCByZSByZWFsaXphIGVsIG1pc21vIHByb2NlZGltaWVudG8gcGFyYSBlbCBjb25qdW50byBkZSBwcnVlYmEuCgpgYGB7cn0KdHJhaW5feCA8LSBkYXRhLm1hdHJpeChudW1lcmljX2NvbHVtbnNbLCAtYygxLDM4KV0pCnRyYWluX3kgPC0gbnVtZXJpY19jb2x1bW5zWywgMzhdCmBgYAoKCmBgYHtyfQpudW1lcmljX2NvbHVtbnMyIDwtIHRlc3QgJT4lIGRwbHlyOjpzZWxlY3Qod2hlcmUoaXMubnVtZXJpYykpCm51bWVyaWNfY29sdW1uczJbaXMubmEobnVtZXJpY19jb2x1bW5zMildIDwtIC05OQpgYGAKCmBgYHtyfQp0ZXN0X3ggPSBkYXRhLm1hdHJpeChudW1lcmljX2NvbHVtbnMyWywgLTFdKQpgYGAKCiMjIE1vZGVsbyB4Z2Jvb3N0IGNvbiBncmlkIHNlYXJjaCAoTWVqb3IgbW9kZWxvKQoKU2UgYWp1c3RhIHVuIG1vZGVsbyBYR0Jvb3N0LCBzZSByZWFsaXphIHVuYSByZWppbGxhIGNvbiB2YWxvcmVzIHBhcmEgbG9zIGRpZmVyZW50ZXMgcGFyw6FtZXRyb3MgeSBzZSBvYnRpZW5lIGVsIG3DoXMgw7NwdGltbyBjb24gZWwgbcOpdG9kbyBjcm9zcyB2YWxpZGF0aW9uLgoKYGBge3J9CiMgU2UgZGViZW4gcHJlcGFyYXIgbG9zIGRhdG9zIGRlIG90cmEgZm9ybWEKWCA8LSB4Z2IuRE1hdHJpeChhcy5tYXRyaXgodHJhaW5feCkpCnkgPC0gdHJhaW5feQoKeGdiX3RyY29udHJvbCA9IHRyYWluQ29udHJvbCgKICBtZXRob2QgPSAiY3YiLCAjIENyb3NzIHZhbGlkYXRpb24KICBudW1iZXIgPSAxMCwgIAogIGFsbG93UGFyYWxsZWwgPSBUUlVFLAogIHZlcmJvc2VJdGVyID0gRkFMU0UsCiAgcmV0dXJuRGF0YSA9IEZBTFNFCikKCnhnYkdyaWQgPC0gZXhwYW5kLmdyaWQobnJvdW5kcz1jKDEwMCwyMDAsMzAwLDQwMCksCiAgICAgICAgICAgICAgICAgICAgICAgbWF4X2RlcHRoID0gYygzOjcpLAogICAgICAgICAgICAgICAgICAgICAgIGV0YSA9IHNlcSgwLjA1LCAxLDEwKSwKICAgICAgICAgICAgICAgICAgICAgICBnYW1tYSA9IHNlcSgwLjAxLDEsMTApLAogICAgICAgICAgICAgICAgICAgICAgIGNvbHNhbXBsZV9ieXRyZWUgPSBjKDAuNzUpLAogICAgICAgICAgICAgICAgICAgICAgIHN1YnNhbXBsZSA9IGMoMC41MCksCiAgICAgICAgICAgICAgICAgICAgICAgbWluX2NoaWxkX3dlaWdodCA9IGMoMCkpCmBgYAoKRW50cmVuYW1pZW50byBkZWwgbW9kZWxvCgpgYGB7ciwgd2FybmluZz1GfQptb2QyIDwtIHRyYWluKAogIFgsIHksCiAgdHJDb250cm9sID0geGdiX3RyY29udHJvbCwKICB0dW5lR3JpZCA9IHhnYkdyaWQsCiAgbWV0aG9kID0gInhnYlRyZWUiLAogIG9iamVjdGl2ZSA9ICdyZWc6c3F1YXJlZGVycm9yJwopCgpgYGAKClNlIGd1YXJkYW4gbGFzIHByZWRpY2Npb25lcwpgYGB7cn0KeGdiX3Rlc3QgPC0geGdiLkRNYXRyaXgoYXMubWF0cml4KHRlc3RfeCkpCnByZWRfeSA8LSBwcmVkaWN0KG1vZDIsIHhnYl90ZXN0KQp3cml0ZS5jc3YoZGF0YS5mcmFtZShJZCA9IG51bWVyaWNfY29sdW1uczIkSWQsIFNhbGVQcmljZSA9IHByZWRfeSksJ3NhbXBsZTMuY3N2Jyxyb3cubmFtZXMgPSBGKQpgYGAKCgoKIyMgTW9kZWxvIFhHQm9vc3QgY29uIGdyaWRzZWFyY2ggeSB2YXJpYWJsZXMgc2VsZWNjaW9uYWRhcyBhbnRlcmlvcm1lbnRlIGNvbiBSRgoKU2FsZVByaWNlIH4gLAogICAgICAgICAgICAgICAgICAgICAgICAKYGBge3J9CnRyYWluX3gyIDwtIG51bWVyaWNfY29sdW1uc1ssIC1jKDEsMzgpXQp0cmFpbl94MiA8LSBkYXRhLm1hdHJpeCh0cmFpbl94MlssYygnR3JMaXZBcmVhJywnT3ZlcmFsbFF1YWwnLAogICAgICAgICAgICAgICAgICAgICAgICAnVG90YWxCc210U0YnLCdYMXN0RmxyU0YnLCdYMm5kRmxyU0YnLCdHYXJhZ2VDYXJzJywKICAgICAgICAgICAgICAgICAgICAgICAgJ0dhcmFnZUFyZWEnLCAnTG90QXJlYScsICdCc210RmluU0YxJywnRnVsbEJhdGgnKV0pCnRyYWluX3kyIDwtIG51bWVyaWNfY29sdW1uc1ssIDM4XQp0ZXN0X3gyIDwtIG51bWVyaWNfY29sdW1uczJbLCAtMV0KdGVzdF94MiA8LSBkYXRhLm1hdHJpeCh0ZXN0X3gyWyxjKCdHckxpdkFyZWEnLCdPdmVyYWxsUXVhbCcsCiAgICAgICAgICAgICAgICAgICAgICAgICdUb3RhbEJzbXRTRicsJ1gxc3RGbHJTRicsJ1gybmRGbHJTRicsJ0dhcmFnZUNhcnMnLAogICAgICAgICAgICAgICAgICAgICAgICAnR2FyYWdlQXJlYScsICdMb3RBcmVhJywgJ0JzbXRGaW5TRjEnLCdGdWxsQmF0aCcpXSkKCgojIFNlIGRlYmVuIHByZXBhcmFyIGxvcyBkYXRvcyBkZSBvdHJhIGZvcm1hClgyIDwtIHhnYi5ETWF0cml4KGFzLm1hdHJpeCh0cmFpbl94MikpCnkyIDwtIHRyYWluX3kyCgp4Z2JfdHJjb250cm9sID0gdHJhaW5Db250cm9sKAogIG1ldGhvZCA9ICJjdiIsICMgQ3Jvc3MgdmFsaWRhdGlvbgogIG51bWJlciA9IDEwLCAgCiAgYWxsb3dQYXJhbGxlbCA9IFRSVUUsCiAgdmVyYm9zZUl0ZXIgPSBGQUxTRSwKICByZXR1cm5EYXRhID0gRkFMU0UKKQoKeGdiR3JpZCA8LSBleHBhbmQuZ3JpZChucm91bmRzPWMoMTAwLDIwMCwzMDAsNDAwKSwKICAgICAgICAgICAgICAgICAgICAgICBtYXhfZGVwdGggPSBjKDM6NyksCiAgICAgICAgICAgICAgICAgICAgICAgZXRhID0gc2VxKDAuMDUsIDEsMTApLAogICAgICAgICAgICAgICAgICAgICAgIGdhbW1hID0gc2VxKDAuMDEsMSwxMCksCiAgICAgICAgICAgICAgICAgICAgICAgY29sc2FtcGxlX2J5dHJlZSA9IGMoMC43NSksCiAgICAgICAgICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gYygwLjUwKSwKICAgICAgICAgICAgICAgICAgICAgICBtaW5fY2hpbGRfd2VpZ2h0ID0gYygwKSkKYGBgCgoKYGBge3IsIHdhcm5pbmc9Rn0KbW9kMyA8LSB0cmFpbigKICBYMiwgeTIsCiAgdHJDb250cm9sID0geGdiX3RyY29udHJvbCwKICB0dW5lR3JpZCA9IHhnYkdyaWQsCiAgbWV0aG9kID0gInhnYlRyZWUiLAogIG9iamVjdGl2ZSA9ICdyZWc6c3F1YXJlZGVycm9yJwopCmBgYAoKYGBge3J9CnhnYl90ZXN0MiA8LSB4Z2IuRE1hdHJpeChhcy5tYXRyaXgodGVzdF94MikpCnByZWRfeTIgPC0gcHJlZGljdChtb2QzLCB4Z2JfdGVzdDIpCndyaXRlLmNzdihkYXRhLmZyYW1lKElkID0gbnVtZXJpY19jb2x1bW5zMiRJZCwgU2FsZVByaWNlID0gcHJlZF95MiksJ2ZpbmFsX3NhbXBsZS5jc3YnLHJvdy5uYW1lcyA9IEYpCmBgYAoKIyBFdmlkZW5jaWFzCgoKIyMgUmFua2luZyBhbGNhbnphZG8KCjxpbWcgc3JjPSJSYW5raW5nLkpQRyIgd2lkdGg9IjYwMCIvPgoKIyMgTWVqb3IgcHVudGFqZSBWYWxlbnRpbmEgR2FyY2lhCgo8aW1nIHNyYz0icHVudGFqZV92YWxlbi5KUEciIHdpZHRoPSI2MDAiLz4KCiMjIE1lam9yIHB1bnRhamUgSmFpbWUgTW9saW5hCgo8aW1nIHNyYz0icHVudGFqZV9qYWltZS5KUEciIHdpZHRoPSI2MDAiLz4KCg==