Cargando Datos Simulados
datos = read_csv('https://raw.githubusercontent.com/JoaquinAmatRodrigo/Estadistica-machine-learning-python/master/data/blobs.csv')
Rows: 1500 Columns: 3
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (3): y, x_1, x_2
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
datos <- datos %>% mutate(y = as.factor(y))
ggplot(data = datos, aes(x = x_1, y = x_2, fill = y)) +
geom_point(shape = 21, size = 2) +
theme_fivethirtyeight() +
theme(
legend.position = "none",
text = element_blank(),
axis.ticks = element_blank()
)

Creando particiones train , test y validation:
- Asignando un ratio de 80,20 con
h2o.splitFrame:
- Random Seed de 123
df_h2o <- as.h2o(datos)
particiones <- h2o.splitFrame(data = df_h2o, ratios = c(0.6, 0.2), seed = 123)
df_train <- h2o.assign(data = particiones[[1]], key = "df_train")
df_val <- h2o.assign(data = particiones[[2]], key = "datos_validacion")
df_test <- h2o.assign(data = particiones[[3]], key = "df_test")
Observamos el shape o dimension de los datos particionados
library(glue)
glue("Dimension de datos de training {dim(df_train)}")
Dimension de datos de training 918
Dimension de datos de training 3
glue("Dimension de datos de validation {dim(df_val)}")
Dimension de datos de validation 281
Dimension de datos de validation 3
glue("Dimension de datos de testing {dim(df_test)}")
Dimension de datos de testing 301
Dimension de datos de testing 3
Existen 918,281 y 301 datos para training, validacion y testing
respectivamente.
Ahora se procedera a crear 4 modelos mediante
h20.deeplearning
Prediccion Modelos Originales
# Modelos
# ==============================================================================
modelo_1 <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = df_train,
standardize = TRUE,
activation = "Rectifier",
adaptive_rate = FALSE,
hidden = 1,
stopping_rounds = 0,
epochs = 1000,
seed = 123,
model_id = "modelo_1"
)
modelo_2 <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = df_train,
standardize = TRUE,
activation = "Rectifier",
adaptive_rate = FALSE,
hidden = 10,
stopping_rounds = 0,
epochs = 1000,
seed = 123,
model_id = "modelo_2"
)
modelo_3 <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = df_train,
standardize = TRUE,
activation = "Rectifier",
adaptive_rate = FALSE,
hidden = c(10, 10),
stopping_rounds = 0,
epochs = 1000,
seed = 123,
model_id = "modelo_3"
)
modelo_4 <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = df_train,
standardize = TRUE,
activation = "Rectifier",
adaptive_rate = FALSE,
hidden = c(50, 50, 50),
stopping_rounds = 0,
epochs = 1000,
seed = 123,
model_id = "modelo_4"
)
Definicion de Arquitectura Distinta - Mayor Cantidad de Neurons
Definicion de predicciones para los 4 modelos originales:
# Predicciones de cada modelo
# ==============================================================================
grid_predicciones <- expand.grid(
x_1 = seq(from = min(datos$x_1), to = max(datos$x_1), length = 75),
x_2 = seq(from = min(datos$x_2), to = max(datos$x_2), length = 75)
)
grid_predicciones_h2o <- as.h2o(grid_predicciones)
predicciones_1 <- h2o.predict(
object = modelo_1,
newdata = grid_predicciones_h2o
)
predicciones_2 <- h2o.predict(
object = modelo_2,
newdata = grid_predicciones_h2o
)
predicciones_3 <- h2o.predict(
object = modelo_3,
newdata = grid_predicciones_h2o
)
predicciones_4 <- h2o.predict(
object = modelo_4,
newdata = grid_predicciones_h2o
)
grid_predicciones$modelo_1 <- as.vector(predicciones_1$predict)
grid_predicciones$modelo_2 <- as.vector(predicciones_2$predict)
grid_predicciones$modelo_3 <- as.vector(predicciones_3$predict)
grid_predicciones$modelo_4 <- as.vector(predicciones_4$predict)
# Gráfico de predicciones
# ==============================================================================
p1 <- ggplot(data = grid_predicciones, aes(x = x_1, y = x_2, color = modelo_1)) +
geom_point(size = 0.5) +
theme_fivethirtyeight() +
labs(title = "Arquitectura: (5)") +
theme(legend.position = "none",
plot.title = element_text(size=11),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
p2 <- ggplot(data = grid_predicciones, aes(x = x_1, y = x_2, color =modelo_2)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura: (10)") +
theme_fivethirtyeight() +
theme(legend.position = "none",
plot.title = element_text(size=11),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
p3 <- ggplot(data = grid_predicciones, aes(x = x_1, y = x_2, color = modelo_3)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura: (20, 20)") +
theme_fivethirtyeight() +
theme(legend.position = "none",
plot.title = element_text(size=11),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
p4 <- ggplot(data = grid_predicciones, aes(x = x_1, y = x_2, color = modelo_4)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura: (50, 50, 50)") +
theme_fivethirtyeight() +
theme(legend.position = "none",
plot.title = element_text(size=11),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
ggarrange(p1, p2, p3, p4, nrow = 2, ncol = 2)

Creacion de Nuevos Modelos con mayor cantidad de hidden Layers
(Capas Ocultas)
A continuacion , se crea diferentes modelos aplicando una
arquitectura con diferentes parametros , en este caso:
- Modelo 1 : Modelo Con 6 capas ocultas de 30
neuronas cada una, activacion ReLU sin dropout
- Modelo 2 : Modelo con 1 sola capa oculta de 500
neuronas, activacion ReLU con dropout
- Modelo 3 : Modelo con 10 capas ocultas de 10
neuronas cada una , activacion tanh sin dropout
- Modelo 4 : Modelo con 3 capas ocultas de 10
neuronas cada una , activacion tanh sin dropout
Tanh:
\[ tanh = \frac{e^x – e^-x}{e^x +
e^-x}\] Se consideran valores negativos, mientras que en el rango
mÃnimo sigmoideo es 0 pero en Tanh, el rango mÃnimo es -1. Esta es la
razón por la que la función de activación de Tanh también se conoce como
función de activación centrada en cero.
Desventaja:
También enfrenta el mismo problema del problema del gradiente de fuga
como una función sigmoidea.
# Definicion Modelos Custom
# ==============================================================================
# Modelo 1
model_1 <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = df_train,
standardize = TRUE,
activation = "Rectifier",
adaptive_rate = FALSE,
hidden = c(30,30,30,30,30,30),
stopping_rounds = 0,
epochs = 1000,
seed = 123,
model_id = "model_1"
)
# Modelo 2
model_2 <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = df_train,
standardize = TRUE,
activation = "RectifierWithDropout",
adaptive_rate = FALSE,
hidden = 500,
stopping_rounds = 0,
epochs = 1000,
seed = 123,
model_id = "model_2"
)
# Modelo 3
model_3 <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = df_train,
standardize = TRUE,
activation = "Tanh",
adaptive_rate = FALSE,
hidden = c(10,10,10,10,10,10,10,10,10,10),
stopping_rounds = 0,
epochs = 1000,
seed = 123,
model_id = "model_3"
)
# Modelo 4
model_4 <- h2o.deeplearning(
x = c("x_1", "x_2"),
y = "y",
distribution = "multinomial",
training_frame = df_train,
standardize = TRUE,
activation = "Tanh",
adaptive_rate = FALSE,
hidden = c(10,10,10),
stopping_rounds = 0,
epochs = 1000,
seed = 123,
model_id = "model_4"
)
Generando predicciones con los nuevos modelos:
grid_new <- expand.grid(
x_1 = seq(from = min(datos$x_1), to = max(datos$x_1), length = 75),
x_2 = seq(from = min(datos$x_2), to = max(datos$x_2), length = 75)
)
grid_new_predict <- as.h2o(grid_new)
predict_1 <- h2o.predict(
object = model_1,
newdata = grid_new_predict
)
predict_2 <- h2o.predict(
object = model_2,
newdata = grid_new_predict
)
predict_3 <- h2o.predict(
object = model_3,
newdata = grid_new_predict
)
predict_4 <- h2o.predict(
object = model_4,
newdata = grid_new_predict
)
grid_new$model_1 <- as.vector(predict_1$predict)
grid_new$model_2 <- as.vector(predict_2$predict)
grid_new$model_3 <- as.vector(predict_3$predict)
grid_new$model_4 <- as.vector(predict_4$predict)
Predicciones con nuevos Modelos:
# Gráfico de predicciones
# ==============================================================================
p1 <- ggplot(data = grid_new, aes(x = x_1, y = x_2, color = model_1)) +
geom_point(size = 0.5) +
theme_economist() +
labs(title = "Arquitectura: (30,30,30,30,30,30) ReLU") +
theme(legend.position = "none",
plot.title = element_text(size=11),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
p2 <- ggplot(data = grid_new, aes(x = x_1, y = x_2, color = model_2)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura: Neurona Unica ReLU Dropout ") +
theme_economist() +
theme(legend.position = "none",
plot.title = element_text(size=11),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
p3 <- ggplot(data = grid_new, aes(x = x_1, y = x_2, color = model_3)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura c(10,10,10,10,10,10,10,10,10,10) TanH ") +
theme_economist() +
theme(legend.position = "none",
plot.title = element_text(size=11),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
p4 <- ggplot(data = grid_new, aes(x = x_1, y = x_2, color = model_4)) +
geom_point(size = 0.5) +
labs(title = "Arquitectura: c(10, 10, 10) tanh") +
theme_economist() +
theme(legend.position = "none",
plot.title = element_text(size=11),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())
ggarrange(p1, p2, p3, p4, nrow = 2, ncol = 2)

Comparacion de Accuracy entre modelos en el dataset de test:
predict_test <- h2o.predict(object = modelo_1,newdata = df_test)
accuracy_1 <- mean(predict_test["predict"] == df_test["y"])
predict_test <- h2o.predict(object = modelo_2,newdata = df_test)
accuracy_2 <- mean(predict_test["predict"] == df_test["y"])
predict_test <- h2o.predict(object = modelo_3,newdata = df_test)
accuracy_3 <- mean(predict_test["predict"] == df_test["y"])
predict_test <- h2o.predict(object = modelo_4,newdata = df_test)
accuracy_4 <- mean(predict_test["predict"] == df_test["y"])
glue("Accuracy del Modelo 1 : {accuracy_1} \n
Accuracy del Modelo 2 : {accuracy_2} \n
Accuracy del Modelo 3 : {accuracy_3} \n
Accuracy del Modelo 4 : {accuracy_4} \n
" )
Accuracy del Modelo 1 : 0.740863787375415
Accuracy del Modelo 2 : 0.857142857142857
Accuracy del Modelo 3 : 0.86046511627907
Accuracy del Modelo 4 : 0.857142857142857
predict_test <- h2o.predict(object = model_1,newdata = df_test)
accuracy_1_new <- mean(predict_test["predict"] == df_test["y"])
predict_test <- h2o.predict(object = model_2,newdata = df_test)
accuracy_2_new <- mean(predict_test["predict"] == df_test["y"])
predict_test <- h2o.predict(object = model_3,newdata = df_test)
accuracy_3_new <- mean(predict_test["predict"] == df_test["y"])
predict_test <- h2o.predict(object = model_4,newdata = df_test)
accuracy_4_new <- mean(predict_test["predict"] == df_test["y"])
Imprimiendo el accuracy de cada modelo:
glue("Accuracy del Modelo 1 Nuevo: {accuracy_1_new} \n
Accuracy del Modelo 2 Nuevo: {accuracy_2_new} \n
Accuracy del Modelo 3 Nuevo: {accuracy_3_new} \n
Accuracy del Modelo 4 Nuevo : {accuracy_4_new} \n
" )
Accuracy del Modelo 1 Nuevo: 0.837209302325581
Accuracy del Modelo 2 Nuevo: 0.850498338870432
Accuracy del Modelo 3 Nuevo: 0.830564784053156
Accuracy del Modelo 4 Nuevo : 0.837209302325581
Se grafica todas las accuracies de los modelos planteados:
accuracy <- c(accuracy_1,accuracy_2,accuracy_3,accuracy_4, accuracy_1_new, accuracy_2_new,accuracy_3_new,accuracy_4_new)
model <- c("Modelo 1", "Modelo 2","Modelo 3","Modelo 4", "Modelo 1 nuevo", "Modelo 2 Nuevo","Modelo 3 Nuevo","Modelo 4 Nuevo")
data <- data.frame(accuracy, model)
ggplot(data,aes(x= reorder(model,-accuracy),y= accuracy, fill=model ),title("Accuracy de Modelos Planteados "))+geom_bar(stat ="identity") + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

Se observa que el mejor creado corresponde al Modelo 3, con un 86% de
certeza en los datos de testing, recordando que el modelo 3 posee una
arquitectura c(10, 10) de 2 hidden layers con 10 neuronas
cada una, entre los modelos nuevos creados , el modelo 2 nuevo obtiene
el mejor accuracy con el 85%.
LS0tCnRpdGxlOiAiTmV1cmFsIE5ldHdvcmtzIGNvbiBSIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KCiMgKk5ldXJhbCBOZXR3b3JrcyogLSBSZWRlcyBOZXVyb25hbGVzIGNvbiBSCgojIyAqKk5vbWJyZSA6KiogTm9ib2EgQW5kcmVzCgojIyMgSW50cm9kdWNjaW9uCgojIyMjIFF1ZSBlcyB1bmEgUmVkIE5ldXJvbmFsID8KCiFbaW1nXSguL2Fubi1pbi1oaWRkZW4tb3V0LmpwZykKCgpVbmEgcmVkIG5ldXJvbmFsIGVzIHVuIG1vZGVsbyBjYXJhY3Rlcml6YWRvIHBvciB1bmEgZnVuY2nDs24gZGUgYWN0aXZhY2nDs24sIHF1ZSBlcyB1dGlsaXphZGEgcG9yIHVuaWRhZGVzIGRlIHByb2Nlc2FtaWVudG8gZGUgaW5mb3JtYWNpw7NuIGludGVyY29uZWN0YWRhcyBwYXJhIHRyYW5zZm9ybWFyIGxhIGVudHJhZGEgZW4gc2FsaWRhLiBVbmEgcmVkIG5ldXJvbmFsIHNpZW1wcmUgc2UgaGEgY29tcGFyYWRvIGNvbiBlbCBzaXN0ZW1hIG5lcnZpb3NvIGh1bWFuby4gTGEgaW5mb3JtYWNpw7NuIHBhc2EgYSB0cmF2w6lzIGRlIHVuaWRhZGVzIGludGVyY29uZWN0YWRhcyBkZSBmb3JtYSBhbsOhbG9nYSBhbCBwYXNvIGRlIGxhIGluZm9ybWFjacOzbiBwb3IgbGFzIG5ldXJvbmFzIGVuIGxvcyBzZXJlcyBodW1hbm9zLiBMYSBwcmltZXJhIGNhcGEgZGUgbGEgcmVkIG5ldXJvbmFsIHJlY2liZSBsYSBlbnRyYWRhIGJydXRhLCBsYSBwcm9jZXNhIHkgcGFzYSBsYSBpbmZvcm1hY2nDs24gcHJvY2VzYWRhIGEgbGFzIGNhcGFzIG9jdWx0YXMuIExhIGNhcGEgb2N1bHRhIHBhc2EgbGEgaW5mb3JtYWNpw7NuIGEgbGEgw7psdGltYSBjYXBhLCBxdWUgcHJvZHVjZSBsYSBzYWxpZGEuIExhIHZlbnRhamEgZGUgbGEgcmVkIG5ldXJvbmFsIGVzIHF1ZSBlcyBkZSBuYXR1cmFsZXphIGFkYXB0YXRpdmEuIEFwcmVuZGUgZGUgbGEgaW5mb3JtYWNpw7NuIHByb3BvcmNpb25hZGEsIGVzIGRlY2lyLCBzZSBlbnRyZW5hIGEgc8OtIG1pc21hIGEgcGFydGlyIGRlIGxvcyBkYXRvcyBxdWUgdGllbmVuIHVuIHJlc3VsdGFkbyBjb25vY2lkbyB5IG9wdGltaXphIHN1cyBwZXNvcyBwYXJhIHVuYSBtZWpvciBwcmVkaWNjacOzbiBlbiBzaXR1YWNpb25lcyBjb24gcmVzdWx0YWRvIGRlc2Nvbm9jaWRvLgoKKipQYXJ0ZXMgZGUgdW5hIHJlZCBuZXVyb25hbCoqCgpMYSBpZGVhIGVzIHNlbmNpbGxhOiBwcm9ncmFtYXIgY2FkYSBwYXJ0ZSBkZSBudWVzdHJhIHJlZCBuZXVyb25hbCBwb3Igc2VwYXJhZG8uIEFzw60gaXJlbW9zIGludHJvZHVjaWVuZG8gbnVldm9zIGNvbmNlcHRvcyBkZSBsYXMgcmVkZXMgbmV1cm9uYWxlcy4gVW5hIHZleiBxdWUgdGVuZ2Ftb3MgdG9kbyBwcm9ncmFtYWRvLCBwcm9iYXJlbW9zIG51ZXN0cmEgcmVkIG5ldXJvbmFsIGVuIGxhIGNsYXNpZmljYWNpw7NuIHBhcmEgY29tcHJvYmFyIHNpIGZ1bmNpb25hIGNvcnJlY3RhbWVudGUuCgpEaWNobyBlc3RvLCBwb2Ryw61hbW9zIGRlY2lyIHF1ZSB1bmEgcmVkIG5ldXJvbmFsIHRpZW5lIHRyZXMgcGFydGVzOgoKKipMYSBlc3RydWN0dXJhIGRlIGxhIHJlZCBuZXVyb25hbDoqKiBlbiBlc3RhIHBhcnRlIHNlIGRlZmluZSBlbCBuw7ptZXJvIGRlIGNhcGFzLCBsYXMgbmV1cm9uYXMgcG9yIGNhcGEsIGxhcyBmdW5jaW9uZXMgZGUgYWN0aXZhY2nDs24sIGV0Yy4gRW4gcmVzdW1lbiwgZXMgZWwgZXNxdWVsZXRvIGRlIGxhIHJlZC4KCioqRWwgUGVyY2VwdHLDs24qKgoKVW4gcGVyY2VwdHLDs24gdGllbmUgdW5hIG8gbcOhcyBlbnRyYWRhcywgdW4gc2VzZ28sIHVuYSBmdW5jacOzbiBkZSBhY3RpdmFjacOzbiB5IHVuYSDDum5pY2Egc2FsaWRhLiBFbCBwZXJjZXB0csOzbiByZWNpYmUgbGFzIGVudHJhZGFzLCBsYXMgbXVsdGlwbGljYSBwb3IgYWxnw7puIHBlc28geSBsdWVnbyBsYXMgcGFzYSBhIHVuYSBmdW5jacOzbiBkZSBhY3RpdmFjacOzbiBwYXJhIHByb2R1Y2lyIHVuYSBzYWxpZGEuCgohW2ltZ10oLi9wZXJjZXB0cm9uLmpwZykKCioqUHJvcGFnYWNpw7NuIGhhY2lhIGRlbGFudGU6KiogZW4gZXN0ZSBwYXNvIGNvbmVjdGFyZW1vcyB0b2RhcyBsYXMgcGFydGVzIHF1ZSB5YSBoZW1vcyBjb25zdHJ1aWRvLiBBbCBoYWNlcmxvLCBsYSByZWQgbmV1cm9uYWwgc2Vyw6EgY2FwYXogZGUgbGFuemFybm9zIHVuYSBwcmVkaWNjacOzbi4gU2luIGVtYmFyZ28sIGVsIHJlc3VsdGFkbyBzZXLDoSBhbGVhdG9yaW8geWEgcXVlIGHDum4gbm8gaGEgYXByZW5kaWRvLgoKKipSZXRyb3Byb3BhZ2FjacOzbjoqKiBlbiBlc3RlIHBhc28gY2FsY3VsYXJlbW9zIGVsIGVycm9yIGRlIGxhIHByZWRpY2Npw7NuIHkgcmV0cm9wcm9wYWdhcmVtb3MgZWwgZXJyb3IgeSBhanVzdGFyZW1vcyB0b2RvcyBsb3MgcGFyw6FtZXRyb3MgdXRpbGl6YW5kbyBlbCBkZXNjZW5zbyBkZSBncmFkaWVudGUuIERlIGVzdGUgbW9kbywgbGEgcmVkIGFwcmVuZGVyw6EuCgojIyMgSDIwCgpIMm86IEludGVyZmF6IFIgcGFyYSAnSDJPJywgbGEgcGxhdGFmb3JtYSBlc2NhbGFibGUgZGUgYXByZW5kaXphamUgYXV0b23DoXRpY28gZGUgY8OzZGlnbyBhYmllcnRvIHF1ZSBvZnJlY2UgaW1wbGVtZW50YWNpb25lcyBlbiBwYXJhbGVsbyBkZSBtdWNob3MgYWxnb3JpdG1vcyBkZSBhcHJlbmRpemFqZSBhdXRvbcOhdGljbyBzdXBlcnZpc2Fkb3MgeSBubyBzdXBlcnZpc2Fkb3MsIGNvbW8gTW9kZWxvcyBsaW5lYWxlcyBnZW5lcmFsaXphZG9zIChHTE0pLCBHcmFkaWVudCBCb29zdGluZyBNYWNoaW5lcyAoaW5jbHVpZG8gWEdCb29zdCksIFJhbmRvbSBGb3Jlc3RzLCBEZWVwIE5ldXJhbCBOZXR3b3JrcyAoIERlZXAgTGVhcm5pbmcpLCBTdGFja2VkIEVuc2VtYmxlcywgTmFpdmUgQmF5ZXMsIEdlbmVyYWxpemVkIEFkZGl0aXZlIE1vZGVscyAoR0FNKSwgQU5PVkEgR0xNLCBDb3ggUHJvcG9ydGlvbmFsIEhhemFyZHMsIEstTWVhbnMsIFBDQSwgTW9kZWxTZWxlY3Rpb24sIFdvcmQyVmVjLCBhc8OtIGNvbW8gdW4gYWxnb3JpdG1vIGRlIGFwcmVuZGl6YWplIGF1dG9tw6F0aWNvIGNvbXBsZXRhbWVudGUgYXV0b23DoXRpY28gKEgyTyBBdXRvTUwpLgoKU2UgY29taWVuemEgQ2FyZ2FuZG8gbGlicmVyaWEgSDJvOgoKYGBge3J9CmxpYnJhcnkoaDJvKQpgYGAKClBhcXVldGVzIHBhcmEgZWwgdHJhdGFtaWVudG8gZGUgZGF0b3MgeSBncmFmaWNvcwoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KGdncHVicikKYGBgCgpJbmljaWFjacOzbiBkZWwgY2x1c3RlcgoKVW5hIHZleiBxdWUgbGEgbGlicmVyw61hIEgyTyBoYSBzaWRvIGNhcmdhZGEsIGhheSBxdWUgaW5pY2lhciBlbCBjbHVzdGVyLiBQYXJhIGVzdGUgZWplbXBsbywgc2UgZW1wbGVhIHVuIMO6bmljbyBvcmRlbmFkb3IgZGVsIHF1ZSBzZSB1dGlsaXphbiB0b2RvcyBzdXMgY29yZXMgZW4gcGFyYWxlbG8uCgpgYGB7cn0KCmgyby5pbml0KAogIGlwICAgICAgID0gImxvY2FsaG9zdCIsCiAgbnRocmVhZHMgPSAtMSwKICBtYXhfbWVtX3NpemUgPSAiNmciCikKYGBgCgpTZSBvYnNlcnZhIGxhIGNhcmdhIGNvcnJlY3RhIGRlbCBwYXF1ZXRlIEgybwoKIyBDYXJnYW5kbyBEYXRvcyBTaW11bGFkb3MKCmBgYHtyfQpkYXRvcyA9IHJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vSm9hcXVpbkFtYXRSb2RyaWdvL0VzdGFkaXN0aWNhLW1hY2hpbmUtbGVhcm5pbmctcHl0aG9uL21hc3Rlci9kYXRhL2Jsb2JzLmNzdicpCgoKZGF0b3MgPC0gZGF0b3MgJT4lIG11dGF0ZSh5ID0gYXMuZmFjdG9yKHkpKQoKZ2dwbG90KGRhdGEgPSBkYXRvcywgYWVzKHggPSB4XzEsIHkgPSB4XzIsIGZpbGwgPSB5KSkgKyAKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIHNpemUgPSAyKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKyAKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIHRleHQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzID0gIGVsZW1lbnRfYmxhbmsoKQogICkKCmBgYAoKQ3JlYW5kbyBwYXJ0aWNpb25lcyB0cmFpbiAsIHRlc3QgeSB2YWxpZGF0aW9uOgoKLSAgIEFzaWduYW5kbyB1biByYXRpbyBkZSA4MCwyMCBjb24gYGgyby5zcGxpdEZyYW1lYDoKLSAgIFJhbmRvbSBTZWVkIGRlIDEyMwoKYGBge3J9CmRmX2gybyAgIDwtIGFzLmgybyhkYXRvcykKcGFydGljaW9uZXMgPC0gaDJvLnNwbGl0RnJhbWUoZGF0YSA9IGRmX2gybywgcmF0aW9zID0gYygwLjYsIDAuMiksIHNlZWQgPSAxMjMpCmRmX3RyYWluIDwtIGgyby5hc3NpZ24oZGF0YSA9IHBhcnRpY2lvbmVzW1sxXV0sIGtleSA9ICJkZl90cmFpbiIpCmRmX3ZhbCAgPC0gaDJvLmFzc2lnbihkYXRhID0gcGFydGljaW9uZXNbWzJdXSwga2V5ID0gImRhdG9zX3ZhbGlkYWNpb24iKQpkZl90ZXN0ICA8LSBoMm8uYXNzaWduKGRhdGEgPSBwYXJ0aWNpb25lc1tbM11dLCBrZXkgPSAiZGZfdGVzdCIpCmBgYAoKT2JzZXJ2YW1vcyBlbCBzaGFwZSBvIGRpbWVuc2lvbiBkZSBsb3MgZGF0b3MgcGFydGljaW9uYWRvcwoKYGBge3J9CmxpYnJhcnkoZ2x1ZSkKCmdsdWUoIkRpbWVuc2lvbiBkZSBkYXRvcyBkZSB0cmFpbmluZyB7ZGltKGRmX3RyYWluKX0iKQoKCmdsdWUoIkRpbWVuc2lvbiBkZSBkYXRvcyBkZSB2YWxpZGF0aW9uIHtkaW0oZGZfdmFsKX0iKQoKZ2x1ZSgiRGltZW5zaW9uIGRlIGRhdG9zIGRlIHRlc3Rpbmcge2RpbShkZl90ZXN0KX0iKQpgYGAKCkV4aXN0ZW4gOTE4LDI4MSB5IDMwMSBkYXRvcyBwYXJhIHRyYWluaW5nLCB2YWxpZGFjaW9uIHkgdGVzdGluZyByZXNwZWN0aXZhbWVudGUuCgpBaG9yYSBzZSBwcm9jZWRlcmEgYSBjcmVhciA0IG1vZGVsb3MgbWVkaWFudGUgYGgyMC5kZWVwbGVhcm5pbmdgCgojIyMgUHJlZGljY2lvbiBNb2RlbG9zIE9yaWdpbmFsZXMKCmBgYHtyfQojIE1vZGVsb3MKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbW9kZWxvXzEgPC0gaDJvLmRlZXBsZWFybmluZygKICAgICAgICAgICAgICAgICAgeCAgICAgICAgICAgICAgID0gYygieF8xIiwgInhfMiIpLAogICAgICAgICAgICAgICAgICB5ICAgICAgICAgICAgICAgPSAieSIsCiAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiAgICA9ICJtdWx0aW5vbWlhbCIsCiAgICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lICA9IGRmX3RyYWluLAogICAgICAgICAgICAgICAgICBzdGFuZGFyZGl6ZSAgICAgPSBUUlVFLAogICAgICAgICAgICAgICAgICBhY3RpdmF0aW9uICAgICAgPSAiUmVjdGlmaWVyIiwKICAgICAgICAgICAgICAgICAgYWRhcHRpdmVfcmF0ZSAgID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGhpZGRlbiAgICAgICAgICA9IDEsCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX3JvdW5kcyA9IDAsCiAgICAgICAgICAgICAgICAgIGVwb2NocyAgICAgICAgICA9IDEwMDAsCiAgICAgICAgICAgICAgICAgIHNlZWQgICAgICAgICAgICA9IDEyMywKICAgICAgICAgICAgICAgICAgbW9kZWxfaWQgICAgICAgID0gIm1vZGVsb18xIgogICAgICAgICAgICkKCm1vZGVsb18yIDwtIGgyby5kZWVwbGVhcm5pbmcoCiAgICAgICAgICAgICAgICB4ICAgICAgICAgICAgICAgPSBjKCJ4XzEiLCAieF8yIiksCiAgICAgICAgICAgICAgICB5ICAgICAgICAgICAgICAgPSAieSIsCiAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gICAgPSAibXVsdGlub21pYWwiLAogICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgID0gZGZfdHJhaW4sCiAgICAgICAgICAgICAgICBzdGFuZGFyZGl6ZSAgICAgPSBUUlVFLAogICAgICAgICAgICAgICAgYWN0aXZhdGlvbiAgICAgID0gIlJlY3RpZmllciIsCiAgICAgICAgICAgICAgICBhZGFwdGl2ZV9yYXRlICAgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGhpZGRlbiAgICAgICAgICA9IDEwLAogICAgICAgICAgICAgICAgc3RvcHBpbmdfcm91bmRzID0gMCwKICAgICAgICAgICAgICAgIGVwb2NocyAgICAgICAgICA9IDEwMDAsCiAgICAgICAgICAgICAgICBzZWVkICAgICAgICAgICAgPSAxMjMsCiAgICAgICAgICAgICAgICBtb2RlbF9pZCAgICAgICAgPSAibW9kZWxvXzIiCiAgICAgICAgICAgICkKCm1vZGVsb18zIDwtIGgyby5kZWVwbGVhcm5pbmcoCiAgICAgICAgICAgICAgICAgIHggICAgICAgICAgICAgICA9IGMoInhfMSIsICJ4XzIiKSwKICAgICAgICAgICAgICAgICAgeSAgICAgICAgICAgICAgID0gInkiLAogICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gICAgPSAibXVsdGlub21pYWwiLAogICAgICAgICAgICAgICAgICB0cmFpbmluZ19mcmFtZSAgPSBkZl90cmFpbiwKICAgICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgICAgID0gVFJVRSwKICAgICAgICAgICAgICAgICAgYWN0aXZhdGlvbiAgICAgID0gIlJlY3RpZmllciIsCiAgICAgICAgICAgICAgICAgIGFkYXB0aXZlX3JhdGUgICA9IEZBTFNFLAogICAgICAgICAgICAgICAgICBoaWRkZW4gICAgICAgICAgPSBjKDEwLCAxMCksCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX3JvdW5kcyA9IDAsCiAgICAgICAgICAgICAgICAgIGVwb2NocyAgICAgICAgICA9IDEwMDAsCiAgICAgICAgICAgICAgICAgIHNlZWQgICAgICAgICAgICA9IDEyMywKICAgICAgICAgICAgICAgICAgbW9kZWxfaWQgICAgICAgID0gIm1vZGVsb18zIgogICAgICAgICAgICApCgptb2RlbG9fNCA8LSBoMm8uZGVlcGxlYXJuaW5nKAogICAgICAgICAgICAgICAgICB4ICAgICAgICAgICAgICAgPSBjKCJ4XzEiLCAieF8yIiksCiAgICAgICAgICAgICAgICAgIHkgICAgICAgICAgICAgICA9ICJ5IiwKICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uICAgID0gIm11bHRpbm9taWFsIiwKICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgID0gZGZfdHJhaW4sCiAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplICAgICA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gICAgICA9ICJSZWN0aWZpZXIiLAogICAgICAgICAgICAgICAgICBhZGFwdGl2ZV9yYXRlICAgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgaGlkZGVuICAgICAgICAgID0gYyg1MCwgNTAsIDUwKSwKICAgICAgICAgICAgICAgICAgc3RvcHBpbmdfcm91bmRzID0gMCwKICAgICAgICAgICAgICAgICAgZXBvY2hzICAgICAgICAgID0gMTAwMCwKICAgICAgICAgICAgICAgICAgc2VlZCAgICAgICAgICAgID0gMTIzLAogICAgICAgICAgICAgICAgICBtb2RlbF9pZCAgICAgICAgPSAibW9kZWxvXzQiCiAgICAgICAgICAgICkKYGBgCgojIyMgRGVmaW5pY2lvbiBkZSBBcnF1aXRlY3R1cmEgRGlzdGludGEgLSBNYXlvciBDYW50aWRhZCBkZSBOZXVyb25zCgpEZWZpbmljaW9uIGRlIHByZWRpY2Npb25lcyBwYXJhIGxvcyA0IG1vZGVsb3Mgb3JpZ2luYWxlczoKCmBgYHtyfQojIFByZWRpY2Npb25lcyBkZSBjYWRhIG1vZGVsbwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKZ3JpZF9wcmVkaWNjaW9uZXMgPC0gZXhwYW5kLmdyaWQoCiAgICAgICAgICAgICAgICAgICAgICAgIHhfMSA9IHNlcShmcm9tID0gbWluKGRhdG9zJHhfMSksIHRvID0gbWF4KGRhdG9zJHhfMSksIGxlbmd0aCA9IDc1KSwKICAgICAgICAgICAgICAgICAgICAgICAgeF8yID0gc2VxKGZyb20gPSBtaW4oZGF0b3MkeF8yKSwgdG8gPSBtYXgoZGF0b3MkeF8yKSwgbGVuZ3RoID0gNzUpCiAgICAgICAgICAgICAgICAgICAgICkKCmdyaWRfcHJlZGljY2lvbmVzX2gybyA8LSBhcy5oMm8oZ3JpZF9wcmVkaWNjaW9uZXMpCgoKcHJlZGljY2lvbmVzXzEgPC0gaDJvLnByZWRpY3QoCiAgICAgICAgICAgICAgICAgICAgb2JqZWN0ICA9IG1vZGVsb18xLAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBncmlkX3ByZWRpY2Npb25lc19oMm8KICAgICAgICAgICAgICAgICAgKQoKcHJlZGljY2lvbmVzXzIgPC0gaDJvLnByZWRpY3QoCiAgICAgICAgICAgICAgICAgICAgb2JqZWN0ICA9IG1vZGVsb18yLAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBncmlkX3ByZWRpY2Npb25lc19oMm8KICAgICAgICAgICAgICAgICAgKQoKcHJlZGljY2lvbmVzXzMgPC0gaDJvLnByZWRpY3QoCiAgICAgICAgICAgICAgICAgICAgb2JqZWN0ICA9IG1vZGVsb18zLAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBncmlkX3ByZWRpY2Npb25lc19oMm8KICAgICAgICAgICAgICAgICAgKQoKcHJlZGljY2lvbmVzXzQgPC0gaDJvLnByZWRpY3QoCiAgICAgICAgICAgICAgICAgICAgb2JqZWN0ICA9IG1vZGVsb180LAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBncmlkX3ByZWRpY2Npb25lc19oMm8KICAgICAgICAgICAgICAgICAgKQoKCmdyaWRfcHJlZGljY2lvbmVzJG1vZGVsb18xIDwtIGFzLnZlY3RvcihwcmVkaWNjaW9uZXNfMSRwcmVkaWN0KQpncmlkX3ByZWRpY2Npb25lcyRtb2RlbG9fMiA8LSBhcy52ZWN0b3IocHJlZGljY2lvbmVzXzIkcHJlZGljdCkKZ3JpZF9wcmVkaWNjaW9uZXMkbW9kZWxvXzMgPC0gYXMudmVjdG9yKHByZWRpY2Npb25lc18zJHByZWRpY3QpCmdyaWRfcHJlZGljY2lvbmVzJG1vZGVsb180IDwtIGFzLnZlY3RvcihwcmVkaWNjaW9uZXNfNCRwcmVkaWN0KQoKYGBgCgpgYGB7cn0KIyBHcsOhZmljbyBkZSBwcmVkaWNjaW9uZXMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KcDEgPC0gZ2dwbG90KGRhdGEgPSBncmlkX3ByZWRpY2Npb25lcywgYWVzKHggPSB4XzEsIHkgPSB4XzIsIGNvbG9yID0gbW9kZWxvXzEpKSArIAogICAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICAgICAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKyAgCiAgICAgIGxhYnModGl0bGUgPSAiQXJxdWl0ZWN0dXJhOiAoNSkiKSArCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTExKSwKICAgICAgICAgICAgYXhpcy50ZXh0ICA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aWNrcyA9ICBlbGVtZW50X2JsYW5rKCkpCgpwMiA8LSBnZ3Bsb3QoZGF0YSA9IGdyaWRfcHJlZGljY2lvbmVzLCBhZXMoeCA9IHhfMSwgeSA9IHhfMiwgY29sb3IgPW1vZGVsb18yKSkgKyAKICAgICAgZ2VvbV9wb2ludChzaXplID0gMC41KSArCiAgICAgIGxhYnModGl0bGUgPSAiQXJxdWl0ZWN0dXJhOiAoMTApIikgKwogICAgICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArIAogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgICAgIGF4aXMudGV4dCAgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGlja3MgPSAgZWxlbWVudF9ibGFuaygpKQoKcDMgPC0gZ2dwbG90KGRhdGEgPSBncmlkX3ByZWRpY2Npb25lcywgYWVzKHggPSB4XzEsIHkgPSB4XzIsIGNvbG9yID0gbW9kZWxvXzMpKSArIAogICAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICAgICAgbGFicyh0aXRsZSA9ICJBcnF1aXRlY3R1cmE6ICgyMCwgMjApIikgKwogICAgICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArIAogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgICAgIGF4aXMudGV4dCAgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGlja3MgPSAgZWxlbWVudF9ibGFuaygpKQoKcDQgPC0gZ2dwbG90KGRhdGEgPSBncmlkX3ByZWRpY2Npb25lcywgYWVzKHggPSB4XzEsIHkgPSB4XzIsIGNvbG9yID0gbW9kZWxvXzQpKSArIAogICAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICAgICAgbGFicyh0aXRsZSA9ICJBcnF1aXRlY3R1cmE6ICg1MCwgNTAsIDUwKSIpICsKICAgICAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKyAKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgICAgICBheGlzLnRleHQgID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzID0gIGVsZW1lbnRfYmxhbmsoKSkKCgpnZ2FycmFuZ2UocDEsIHAyLCBwMywgcDQsIG5yb3cgPSAyLCBuY29sID0gMikKCmBgYAoKIyMjIENyZWFjaW9uIGRlIE51ZXZvcyBNb2RlbG9zIGNvbiBtYXlvciBjYW50aWRhZCBkZSBoaWRkZW4gTGF5ZXJzIChDYXBhcyBPY3VsdGFzKQoKQSBjb250aW51YWNpb24gLCBzZSBjcmVhIGRpZmVyZW50ZXMgbW9kZWxvcyBhcGxpY2FuZG8gdW5hIGFycXVpdGVjdHVyYSBjb24gZGlmZXJlbnRlcyBwYXJhbWV0cm9zICwgZW4gZXN0ZSBjYXNvOgoKMS4gICoqTW9kZWxvIDEqKiA6IE1vZGVsbyBDb24gNiBjYXBhcyBvY3VsdGFzIGRlIDMwIG5ldXJvbmFzIGNhZGEgdW5hLCBhY3RpdmFjaW9uIFJlTFUgc2luIGRyb3BvdXQKMi4gICoqTW9kZWxvIDIqKiA6IE1vZGVsbyBjb24gMSBzb2xhIGNhcGEgb2N1bHRhIGRlIDUwMCBuZXVyb25hcywgYWN0aXZhY2lvbiBSZUxVIGNvbiBkcm9wb3V0CjMuICAqKk1vZGVsbyAzKiogOiBNb2RlbG8gY29uIDEwIGNhcGFzIG9jdWx0YXMgZGUgMTAgbmV1cm9uYXMgY2FkYSB1bmEgLCBhY3RpdmFjaW9uIHRhbmggc2luIGRyb3BvdXQKNC4gICoqTW9kZWxvIDQqKiA6IE1vZGVsbyBjb24gMyBjYXBhcyBvY3VsdGFzIGRlIDEwIG5ldXJvbmFzIGNhZGEgdW5hICwgYWN0aXZhY2lvbiB0YW5oIHNpbiBkcm9wb3V0CgoqKlRhbmg6KioKCiQkIHRhbmggPSBcZnJhY3tlXngg4oCTIGVeLXh9e2VeeCArIGVeLXh9JCQgU2UgY29uc2lkZXJhbiB2YWxvcmVzIG5lZ2F0aXZvcywgbWllbnRyYXMgcXVlIGVuIGVsIHJhbmdvIG3DrW5pbW8gc2lnbW9pZGVvIGVzIDAgcGVybyBlbiBUYW5oLCBlbCByYW5nbyBtw61uaW1vIGVzIC0xLiBFc3RhIGVzIGxhIHJhesOzbiBwb3IgbGEgcXVlIGxhIGZ1bmNpw7NuIGRlIGFjdGl2YWNpw7NuIGRlIFRhbmggdGFtYmnDqW4gc2UgY29ub2NlIGNvbW8gZnVuY2nDs24gZGUgYWN0aXZhY2nDs24gY2VudHJhZGEgZW4gY2Vyby4KCkRlc3ZlbnRhamE6CgpUYW1iacOpbiBlbmZyZW50YSBlbCBtaXNtbyBwcm9ibGVtYSBkZWwgcHJvYmxlbWEgZGVsIGdyYWRpZW50ZSBkZSBmdWdhIGNvbW8gdW5hIGZ1bmNpw7NuIHNpZ21vaWRlYS4KCmBgYHtyfQojIERlZmluaWNpb24gTW9kZWxvcyBDdXN0b20KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCgojIE1vZGVsbyAxIAptb2RlbF8xIDwtIGgyby5kZWVwbGVhcm5pbmcoCiAgICAgICAgICAgICAgICAgIHggICAgICAgICAgICAgICA9IGMoInhfMSIsICJ4XzIiKSwKICAgICAgICAgICAgICAgICAgeSAgICAgICAgICAgICAgID0gInkiLAogICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gICAgPSAibXVsdGlub21pYWwiLAogICAgICAgICAgICAgICAgICB0cmFpbmluZ19mcmFtZSAgPSBkZl90cmFpbiwKICAgICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgICAgID0gVFJVRSwKICAgICAgICAgICAgICAgICAgYWN0aXZhdGlvbiAgICAgID0gIlJlY3RpZmllciIsCiAgICAgICAgICAgICAgICAgIGFkYXB0aXZlX3JhdGUgICA9IEZBTFNFLAogICAgICAgICAgICAgICAgICBoaWRkZW4gICAgICAgICAgPSBjKDMwLDMwLDMwLDMwLDMwLDMwKSwKICAgICAgICAgICAgICAgICAgc3RvcHBpbmdfcm91bmRzID0gMCwKICAgICAgICAgICAgICAgICAgZXBvY2hzICAgICAgICAgID0gMTAwMCwKICAgICAgICAgICAgICAgICAgc2VlZCAgICAgICAgICAgID0gMTIzLAogICAgICAgICAgICAgICAgICBtb2RlbF9pZCAgICAgICAgPSAibW9kZWxfMSIKICAgICAgICAgICApCgoKIyBNb2RlbG8gMgptb2RlbF8yIDwtIGgyby5kZWVwbGVhcm5pbmcoCiAgICAgICAgICAgICAgICAgIHggICAgICAgICAgICAgICA9IGMoInhfMSIsICJ4XzIiKSwKICAgICAgICAgICAgICAgICAgeSAgICAgICAgICAgICAgID0gInkiLAogICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gICAgPSAibXVsdGlub21pYWwiLAogICAgICAgICAgICAgICAgICB0cmFpbmluZ19mcmFtZSAgPSBkZl90cmFpbiwKICAgICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgICAgID0gVFJVRSwKICAgICAgICAgICAgICAgICAgYWN0aXZhdGlvbiAgICAgID0gIlJlY3RpZmllcldpdGhEcm9wb3V0IiwKICAgICAgICAgICAgICAgICAgYWRhcHRpdmVfcmF0ZSAgID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGhpZGRlbiAgICAgICAgICA9IDUwMCwKICAgICAgICAgICAgICAgICAgc3RvcHBpbmdfcm91bmRzID0gMCwKICAgICAgICAgICAgICAgICAgZXBvY2hzICAgICAgICAgID0gMTAwMCwKICAgICAgICAgICAgICAgICAgc2VlZCAgICAgICAgICAgID0gMTIzLAogICAgICAgICAgICAgICAgICBtb2RlbF9pZCAgICAgICAgPSAibW9kZWxfMiIKICAgICAgICAgICApCgoKIyBNb2RlbG8gMwptb2RlbF8zIDwtIGgyby5kZWVwbGVhcm5pbmcoCiAgICAgICAgICAgICAgICAgIHggICAgICAgICAgICAgICA9IGMoInhfMSIsICJ4XzIiKSwKICAgICAgICAgICAgICAgICAgeSAgICAgICAgICAgICAgID0gInkiLAogICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gICAgPSAibXVsdGlub21pYWwiLAogICAgICAgICAgICAgICAgICB0cmFpbmluZ19mcmFtZSAgPSBkZl90cmFpbiwKICAgICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgICAgID0gVFJVRSwKICAgICAgICAgICAgICAgICAgYWN0aXZhdGlvbiAgICAgID0gIlRhbmgiLAogICAgICAgICAgICAgICAgICBhZGFwdGl2ZV9yYXRlICAgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgaGlkZGVuICAgICAgICAgID0gYygxMCwxMCwxMCwxMCwxMCwxMCwxMCwxMCwxMCwxMCksCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX3JvdW5kcyA9IDAsCiAgICAgICAgICAgICAgICAgIGVwb2NocyAgICAgICAgICA9IDEwMDAsCiAgICAgICAgICAgICAgICAgIHNlZWQgICAgICAgICAgICA9IDEyMywKICAgICAgICAgICAgICAgICAgbW9kZWxfaWQgICAgICAgID0gIm1vZGVsXzMiCiAgICAgICAgICAgKQoKCiMgTW9kZWxvIDQKbW9kZWxfNCA8LSBoMm8uZGVlcGxlYXJuaW5nKAogICAgICAgICAgICAgICAgICB4ICAgICAgICAgICAgICAgPSBjKCJ4XzEiLCAieF8yIiksCiAgICAgICAgICAgICAgICAgIHkgICAgICAgICAgICAgICA9ICJ5IiwKICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uICAgID0gIm11bHRpbm9taWFsIiwKICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgID0gZGZfdHJhaW4sCiAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplICAgICA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gICAgICA9ICJUYW5oIiwKICAgICAgICAgICAgICAgICAgYWRhcHRpdmVfcmF0ZSAgID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGhpZGRlbiAgICAgICAgICA9IGMoMTAsMTAsMTApLAogICAgICAgICAgICAgICAgICBzdG9wcGluZ19yb3VuZHMgPSAwLAogICAgICAgICAgICAgICAgICBlcG9jaHMgICAgICAgICAgPSAxMDAwLAogICAgICAgICAgICAgICAgICBzZWVkICAgICAgICAgICAgPSAxMjMsCiAgICAgICAgICAgICAgICAgIG1vZGVsX2lkICAgICAgICA9ICJtb2RlbF80IgogICAgICAgICAgICkKCgoKCmBgYAoKIyMjIEdlbmVyYW5kbyBwcmVkaWNjaW9uZXMgY29uIGxvcyBudWV2b3MgbW9kZWxvczoKCmBgYHtyfQoKCmdyaWRfbmV3IDwtIGV4cGFuZC5ncmlkKAoKICAgICAgICAgICAgICAgICAgICAgICAgeF8xID0gc2VxKGZyb20gPSBtaW4oZGF0b3MkeF8xKSwgdG8gPSBtYXgoZGF0b3MkeF8xKSwgbGVuZ3RoID0gNzUpLAogICAgICAgICAgICAgICAgICAgICAgICB4XzIgPSBzZXEoZnJvbSA9IG1pbihkYXRvcyR4XzIpLCB0byA9IG1heChkYXRvcyR4XzIpLCBsZW5ndGggPSA3NSkKICAgICAgICAgICAgICAgICAgICAgKQoKZ3JpZF9uZXdfcHJlZGljdCA8LSBhcy5oMm8oZ3JpZF9uZXcpCgoKcHJlZGljdF8xIDwtIGgyby5wcmVkaWN0KAogICAgICAgICAgICAgICAgICAgIG9iamVjdCAgPSBtb2RlbF8xLAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBncmlkX25ld19wcmVkaWN0CiAgICAgICAgICAgICAgICAgICkKCnByZWRpY3RfMiA8LSBoMm8ucHJlZGljdCgKICAgICAgICAgICAgICAgICAgICBvYmplY3QgID0gbW9kZWxfMiwKICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9uZXdfcHJlZGljdAogICAgICAgICAgICAgICAgICApCgpwcmVkaWN0XzMgPC0gaDJvLnByZWRpY3QoCiAgICAgICAgICAgICAgICAgICAgb2JqZWN0ICA9IG1vZGVsXzMsCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGdyaWRfbmV3X3ByZWRpY3QgCiAgICAgICAgICAgICAgICAgICkKCnByZWRpY3RfNCA8LSBoMm8ucHJlZGljdCgKICAgICAgICAgICAgICAgICAgICBvYmplY3QgID0gbW9kZWxfNCwKICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9uZXdfcHJlZGljdAogICAgICAgICAgICAgICAgICApCgpncmlkX25ldyRtb2RlbF8xIDwtIGFzLnZlY3RvcihwcmVkaWN0XzEkcHJlZGljdCkKZ3JpZF9uZXckbW9kZWxfMiA8LSBhcy52ZWN0b3IocHJlZGljdF8yJHByZWRpY3QpCmdyaWRfbmV3JG1vZGVsXzMgPC0gYXMudmVjdG9yKHByZWRpY3RfMyRwcmVkaWN0KQpncmlkX25ldyRtb2RlbF80IDwtIGFzLnZlY3RvcihwcmVkaWN0XzQkcHJlZGljdCkKYGBgCgojIyMgUHJlZGljY2lvbmVzIGNvbiBudWV2b3MgTW9kZWxvczoKCmBgYHtyfQojIEdyw6FmaWNvIGRlIHByZWRpY2Npb25lcwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpwMSA8LSBnZ3Bsb3QoZGF0YSA9IGdyaWRfbmV3LCBhZXMoeCA9IHhfMSwgeSA9IHhfMiwgY29sb3IgPSBtb2RlbF8xKSkgKyAKICAgICAgZ2VvbV9wb2ludChzaXplID0gMC41KSArCiAgICAgIHRoZW1lX2Vjb25vbWlzdCgpICsgIAogICAgICBsYWJzKHRpdGxlID0gIkFycXVpdGVjdHVyYTogKDMwLDMwLDMwLDMwLDMwLDMwKSBSZUxVIikgKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgICAgIGF4aXMudGV4dCAgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGlja3MgPSAgZWxlbWVudF9ibGFuaygpKQoKcDIgPC0gZ2dwbG90KGRhdGEgPSBncmlkX25ldywgYWVzKHggPSB4XzEsIHkgPSB4XzIsIGNvbG9yID0gbW9kZWxfMikpICsgCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogICAgICBsYWJzKHRpdGxlID0gIkFycXVpdGVjdHVyYTogTmV1cm9uYSBVbmljYSBSZUxVIERyb3BvdXQgIikgKwogICAgICB0aGVtZV9lY29ub21pc3QoKSArIAogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgICAgIGF4aXMudGV4dCAgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGlja3MgPSAgZWxlbWVudF9ibGFuaygpKQoKcDMgPC0gZ2dwbG90KGRhdGEgPSBncmlkX25ldywgYWVzKHggPSB4XzEsIHkgPSB4XzIsIGNvbG9yID0gbW9kZWxfMykpICsgCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogICAgICBsYWJzKHRpdGxlID0gIkFycXVpdGVjdHVyYSBjKDEwLDEwLDEwLDEwLDEwLDEwLDEwLDEwLDEwLDEwKSBUYW5IICIpICsKICAgICAgdGhlbWVfZWNvbm9taXN0KCkgKyAKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgICAgICBheGlzLnRleHQgID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzID0gIGVsZW1lbnRfYmxhbmsoKSkKCnA0IDwtIGdncGxvdChkYXRhID0gZ3JpZF9uZXcsIGFlcyh4ID0geF8xLCB5ID0geF8yLCBjb2xvciA9IG1vZGVsXzQpKSArIAoKICAgICAgZ2VvbV9wb2ludChzaXplID0gMC41KSArCiAgICAgIGxhYnModGl0bGUgPSAiQXJxdWl0ZWN0dXJhOiBjKDEwLCAxMCwgMTApIHRhbmgiKSArCiAgICAgIHRoZW1lX2Vjb25vbWlzdCgpICsgCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTExKSwKICAgICAgICAgICAgYXhpcy50ZXh0ICA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aWNrcyA9ICBlbGVtZW50X2JsYW5rKCkpCgoKZ2dhcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBucm93ID0gMiwgbmNvbCA9IDIpCgpgYGAKCkNvbXBhcmFjaW9uIGRlIEFjY3VyYWN5IGVudHJlIG1vZGVsb3MgZW4gZWwgZGF0YXNldCBkZSB0ZXN0OgoKYGBge3J9CnByZWRpY3RfdGVzdCA8LSBoMm8ucHJlZGljdChvYmplY3QgID0gbW9kZWxvXzEsbmV3ZGF0YSA9IGRmX3Rlc3QpCmFjY3VyYWN5XzEgPC0gbWVhbihwcmVkaWN0X3Rlc3RbInByZWRpY3QiXSA9PSBkZl90ZXN0WyJ5Il0pCgpwcmVkaWN0X3Rlc3QgPC0gaDJvLnByZWRpY3Qob2JqZWN0ICA9IG1vZGVsb18yLG5ld2RhdGEgPSBkZl90ZXN0KQphY2N1cmFjeV8yIDwtIG1lYW4ocHJlZGljdF90ZXN0WyJwcmVkaWN0Il0gPT0gZGZfdGVzdFsieSJdKQoKcHJlZGljdF90ZXN0IDwtIGgyby5wcmVkaWN0KG9iamVjdCAgPSBtb2RlbG9fMyxuZXdkYXRhID0gZGZfdGVzdCkKYWNjdXJhY3lfMyA8LSBtZWFuKHByZWRpY3RfdGVzdFsicHJlZGljdCJdID09IGRmX3Rlc3RbInkiXSkKCnByZWRpY3RfdGVzdCA8LSBoMm8ucHJlZGljdChvYmplY3QgID0gbW9kZWxvXzQsbmV3ZGF0YSA9IGRmX3Rlc3QpCmFjY3VyYWN5XzQgPC0gbWVhbihwcmVkaWN0X3Rlc3RbInByZWRpY3QiXSA9PSBkZl90ZXN0WyJ5Il0pCgpgYGAKCmBgYHtyfQogIGdsdWUoIkFjY3VyYWN5IGRlbCBNb2RlbG8gMSA6IHthY2N1cmFjeV8xfSBcbgogICAgIEFjY3VyYWN5IGRlbCBNb2RlbG8gMiA6IHthY2N1cmFjeV8yfSBcbgogICAgIEFjY3VyYWN5IGRlbCBNb2RlbG8gMyA6IHthY2N1cmFjeV8zfSBcbgogICAgIEFjY3VyYWN5IGRlbCBNb2RlbG8gNCA6IHthY2N1cmFjeV80fSBcbgogICAgICIgKQoKCgpgYGAKCmBgYHtyfQoKcHJlZGljdF90ZXN0IDwtIGgyby5wcmVkaWN0KG9iamVjdCAgPSBtb2RlbF8xLG5ld2RhdGEgPSBkZl90ZXN0KQphY2N1cmFjeV8xX25ldyA8LSBtZWFuKHByZWRpY3RfdGVzdFsicHJlZGljdCJdID09IGRmX3Rlc3RbInkiXSkKCnByZWRpY3RfdGVzdCA8LSBoMm8ucHJlZGljdChvYmplY3QgID0gbW9kZWxfMixuZXdkYXRhID0gZGZfdGVzdCkKYWNjdXJhY3lfMl9uZXcgPC0gbWVhbihwcmVkaWN0X3Rlc3RbInByZWRpY3QiXSA9PSBkZl90ZXN0WyJ5Il0pCgpwcmVkaWN0X3Rlc3QgPC0gaDJvLnByZWRpY3Qob2JqZWN0ICA9IG1vZGVsXzMsbmV3ZGF0YSA9IGRmX3Rlc3QpCmFjY3VyYWN5XzNfbmV3IDwtIG1lYW4ocHJlZGljdF90ZXN0WyJwcmVkaWN0Il0gPT0gZGZfdGVzdFsieSJdKQoKcHJlZGljdF90ZXN0IDwtIGgyby5wcmVkaWN0KG9iamVjdCAgPSBtb2RlbF80LG5ld2RhdGEgPSBkZl90ZXN0KQphY2N1cmFjeV80X25ldyA8LSBtZWFuKHByZWRpY3RfdGVzdFsicHJlZGljdCJdID09IGRmX3Rlc3RbInkiXSkKYGBgCgpJbXByaW1pZW5kbyBlbCBhY2N1cmFjeSBkZSBjYWRhIG1vZGVsbzoKCmBgYHtyfQogIGdsdWUoIkFjY3VyYWN5IGRlbCBNb2RlbG8gMSAgTnVldm86IHthY2N1cmFjeV8xX25ld30gXG4KICAgICBBY2N1cmFjeSBkZWwgTW9kZWxvIDIgTnVldm86IHthY2N1cmFjeV8yX25ld30gXG4KICAgICBBY2N1cmFjeSBkZWwgTW9kZWxvIDMgTnVldm86IHthY2N1cmFjeV8zX25ld30gXG4KICAgICBBY2N1cmFjeSBkZWwgTW9kZWxvIDQgTnVldm8gOiB7YWNjdXJhY3lfNF9uZXd9IFxuCiAgICAgIiApCgoKCmBgYAoKU2UgZ3JhZmljYSB0b2RhcyBsYXMgYWNjdXJhY2llcyBkZSBsb3MgbW9kZWxvcyBwbGFudGVhZG9zOgoKYGBge3J9CgphY2N1cmFjeSA8LSBjKGFjY3VyYWN5XzEsYWNjdXJhY3lfMixhY2N1cmFjeV8zLGFjY3VyYWN5XzQsIGFjY3VyYWN5XzFfbmV3LCBhY2N1cmFjeV8yX25ldyxhY2N1cmFjeV8zX25ldyxhY2N1cmFjeV80X25ldykKCgptb2RlbCA8LSBjKCJNb2RlbG8gMSIsICJNb2RlbG8gMiIsIk1vZGVsbyAzIiwiTW9kZWxvIDQiLCAiTW9kZWxvIDEgbnVldm8iLCAiTW9kZWxvIDIgTnVldm8iLCJNb2RlbG8gMyBOdWV2byIsIk1vZGVsbyA0IE51ZXZvIikKCgoKCmRhdGEgPC0gZGF0YS5mcmFtZShhY2N1cmFjeSwgbW9kZWwpICAgIApnZ3Bsb3QoZGF0YSxhZXMoeD0gcmVvcmRlcihtb2RlbCwtYWNjdXJhY3kpLHk9IGFjY3VyYWN5LCBmaWxsPW1vZGVsICksdGl0bGUoIkFjY3VyYWN5IGRlIE1vZGVsb3MgUGxhbnRlYWRvcyAiKSkrZ2VvbV9iYXIoc3RhdCA9ImlkZW50aXR5IikgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpgYGAKCgpTZSBvYnNlcnZhIHF1ZSBlbCBtZWpvciBjcmVhZG8gY29ycmVzcG9uZGUgYWwgTW9kZWxvIDMsIGNvbiB1biA4NiUgZGUgY2VydGV6YSBlbiBsb3MgZGF0b3MgZGUgdGVzdGluZywgcmVjb3JkYW5kbyBxdWUgZWwgbW9kZWxvIDMgcG9zZWUgdW5hIGFycXVpdGVjdHVyYSAgYGMoMTAsIDEwKWAgZGUgMiBoaWRkZW4gbGF5ZXJzIGNvbiAxMCBuZXVyb25hcyBjYWRhIHVuYSwgZW50cmUgbG9zIG1vZGVsb3MgbnVldm9zIGNyZWFkb3MgLCBlbCBtb2RlbG8gMiBudWV2byBvYnRpZW5lIGVsIG1lam9yIGFjY3VyYWN5IGNvbiBlbCA4NSUuCgoKCgoK