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 = 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"])
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.744186046511628
Accuracy del Modelo 2 : 0.867109634551495
Accuracy del Modelo 3 : 0.880398671096346
Accuracy del Modelo 4 : 0.863787375415282
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.857142857142857
Accuracy del Modelo 2 Nuevo: 0.853820598006645
Accuracy del Modelo 3 Nuevo: 0.847176079734219
Accuracy del Modelo 4 Nuevo : 0.863787375415282
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 88% de
certeza en los datos de testing, recordando que el modelo 3 posee una
arquitectura con de 2 capas ocultas c(10, 10)
de 10
neuronas cada una.
LS0tCnRpdGxlOiAiTmV1cmFsIE5ldHdvcmtzIGNvbiBSIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KCiMgKk5ldXJhbCBOZXR3b3JrcyogLSBSZWRlcyBOZXVyb25hbGVzIGNvbiBSCgojIyAqKk5vbWJyZSA6KiogTm9ib2EgQW5kcmVzCgojIyMgSW50cm9kdWNjaW9uCgojIyMjIFF1ZSBlcyB1bmEgUmVkIE5ldXJvbmFsID8KCiFbXShodHRwczovL3d3dy5rZG51Z2dldHMuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy9hbm4taW4taGlkZGVuLW91dC5qcGcpCgpVbmEgcmVkIG5ldXJvbmFsIGVzIHVuIG1vZGVsbyBjYXJhY3Rlcml6YWRvIHBvciB1bmEgZnVuY2nDs24gZGUgYWN0aXZhY2nDs24sIHF1ZSBlcyB1dGlsaXphZGEgcG9yIHVuaWRhZGVzIGRlIHByb2Nlc2FtaWVudG8gZGUgaW5mb3JtYWNpw7NuIGludGVyY29uZWN0YWRhcyBwYXJhIHRyYW5zZm9ybWFyIGxhIGVudHJhZGEgZW4gc2FsaWRhLiBVbmEgcmVkIG5ldXJvbmFsIHNpZW1wcmUgc2UgaGEgY29tcGFyYWRvIGNvbiBlbCBzaXN0ZW1hIG5lcnZpb3NvIGh1bWFuby4gTGEgaW5mb3JtYWNpw7NuIHBhc2EgYSB0cmF2w6lzIGRlIHVuaWRhZGVzIGludGVyY29uZWN0YWRhcyBkZSBmb3JtYSBhbsOhbG9nYSBhbCBwYXNvIGRlIGxhIGluZm9ybWFjacOzbiBwb3IgbGFzIG5ldXJvbmFzIGVuIGxvcyBzZXJlcyBodW1hbm9zLiBMYSBwcmltZXJhIGNhcGEgZGUgbGEgcmVkIG5ldXJvbmFsIHJlY2liZSBsYSBlbnRyYWRhIGJydXRhLCBsYSBwcm9jZXNhIHkgcGFzYSBsYSBpbmZvcm1hY2nDs24gcHJvY2VzYWRhIGEgbGFzIGNhcGFzIG9jdWx0YXMuIExhIGNhcGEgb2N1bHRhIHBhc2EgbGEgaW5mb3JtYWNpw7NuIGEgbGEgw7psdGltYSBjYXBhLCBxdWUgcHJvZHVjZSBsYSBzYWxpZGEuIExhIHZlbnRhamEgZGUgbGEgcmVkIG5ldXJvbmFsIGVzIHF1ZSBlcyBkZSBuYXR1cmFsZXphIGFkYXB0YXRpdmEuIEFwcmVuZGUgZGUgbGEgaW5mb3JtYWNpw7NuIHByb3BvcmNpb25hZGEsIGVzIGRlY2lyLCBzZSBlbnRyZW5hIGEgc8OtIG1pc21hIGEgcGFydGlyIGRlIGxvcyBkYXRvcyBxdWUgdGllbmVuIHVuIHJlc3VsdGFkbyBjb25vY2lkbyB5IG9wdGltaXphIHN1cyBwZXNvcyBwYXJhIHVuYSBtZWpvciBwcmVkaWNjacOzbiBlbiBzaXR1YWNpb25lcyBjb24gcmVzdWx0YWRvIGRlc2Nvbm9jaWRvLgoKKipQYXJ0ZXMgZGUgdW5hIHJlZCBuZXVyb25hbCoqCgpMYSBpZGVhIGVzIHNlbmNpbGxhOiBwcm9ncmFtYXIgY2FkYSBwYXJ0ZSBkZSBudWVzdHJhIHJlZCBuZXVyb25hbCBwb3Igc2VwYXJhZG8uIEFzw60gaXJlbW9zIGludHJvZHVjaWVuZG8gbnVldm9zIGNvbmNlcHRvcyBkZSBsYXMgcmVkZXMgbmV1cm9uYWxlcy4gVW5hIHZleiBxdWUgdGVuZ2Ftb3MgdG9kbyBwcm9ncmFtYWRvLCBwcm9iYXJlbW9zIG51ZXN0cmEgcmVkIG5ldXJvbmFsIGVuIGxhIGNsYXNpZmljYWNpw7NuIHBhcmEgY29tcHJvYmFyIHNpIGZ1bmNpb25hIGNvcnJlY3RhbWVudGUuCgpEaWNobyBlc3RvLCBwb2Ryw61hbW9zIGRlY2lyIHF1ZSB1bmEgcmVkIG5ldXJvbmFsIHRpZW5lIHRyZXMgcGFydGVzOgoKKipMYSBlc3RydWN0dXJhIGRlIGxhIHJlZCBuZXVyb25hbDoqKiBlbiBlc3RhIHBhcnRlIHNlIGRlZmluZSBlbCBuw7ptZXJvIGRlIGNhcGFzLCBsYXMgbmV1cm9uYXMgcG9yIGNhcGEsIGxhcyBmdW5jaW9uZXMgZGUgYWN0aXZhY2nDs24sIGV0Yy4gRW4gcmVzdW1lbiwgZXMgZWwgZXNxdWVsZXRvIGRlIGxhIHJlZC4KCioqRWwgUGVyY2VwdHLDs24qKgoKVW4gcGVyY2VwdHLDs24gdGllbmUgdW5hIG8gbcOhcyBlbnRyYWRhcywgdW4gc2VzZ28sIHVuYSBmdW5jacOzbiBkZSBhY3RpdmFjacOzbiB5IHVuYSDDum5pY2Egc2FsaWRhLiBFbCBwZXJjZXB0csOzbiByZWNpYmUgbGFzIGVudHJhZGFzLCBsYXMgbXVsdGlwbGljYSBwb3IgYWxnw7puIHBlc28geSBsdWVnbyBsYXMgcGFzYSBhIHVuYSBmdW5jacOzbiBkZSBhY3RpdmFjacOzbiBwYXJhIHByb2R1Y2lyIHVuYSBzYWxpZGEuCgohW10oaHR0cHM6Ly93d3cua2RudWdnZXRzLmNvbS93cC1jb250ZW50L3VwbG9hZHMvcGVyY2VwdHJvbi5qcGcpCgoqKlByb3BhZ2FjacOzbiBoYWNpYSBkZWxhbnRlOioqIGVuIGVzdGUgcGFzbyBjb25lY3RhcmVtb3MgdG9kYXMgbGFzIHBhcnRlcyBxdWUgeWEgaGVtb3MgY29uc3RydWlkby4gQWwgaGFjZXJsbywgbGEgcmVkIG5ldXJvbmFsIHNlcsOhIGNhcGF6IGRlIGxhbnphcm5vcyB1bmEgcHJlZGljY2nDs24uIFNpbiBlbWJhcmdvLCBlbCByZXN1bHRhZG8gc2Vyw6EgYWxlYXRvcmlvIHlhIHF1ZSBhw7puIG5vIGhhIGFwcmVuZGlkby4KCioqUmV0cm9wcm9wYWdhY2nDs246KiogZW4gZXN0ZSBwYXNvIGNhbGN1bGFyZW1vcyBlbCBlcnJvciBkZSBsYSBwcmVkaWNjacOzbiB5IHJldHJvcHJvcGFnYXJlbW9zIGVsIGVycm9yIHkgYWp1c3RhcmVtb3MgdG9kb3MgbG9zIHBhcsOhbWV0cm9zIHV0aWxpemFuZG8gZWwgZGVzY2Vuc28gZGUgZ3JhZGllbnRlLiBEZSBlc3RlIG1vZG8sIGxhIHJlZCBhcHJlbmRlcsOhLgoKIyMjIEgyMAoKSDJvOiBJbnRlcmZheiBSIHBhcmEgJ0gyTycsIGxhIHBsYXRhZm9ybWEgZXNjYWxhYmxlIGRlIGFwcmVuZGl6YWplIGF1dG9tw6F0aWNvIGRlIGPDs2RpZ28gYWJpZXJ0byBxdWUgb2ZyZWNlIGltcGxlbWVudGFjaW9uZXMgZW4gcGFyYWxlbG8gZGUgbXVjaG9zIGFsZ29yaXRtb3MgZGUgYXByZW5kaXphamUgYXV0b23DoXRpY28gc3VwZXJ2aXNhZG9zIHkgbm8gc3VwZXJ2aXNhZG9zLCBjb21vIE1vZGVsb3MgbGluZWFsZXMgZ2VuZXJhbGl6YWRvcyAoR0xNKSwgR3JhZGllbnQgQm9vc3RpbmcgTWFjaGluZXMgKGluY2x1aWRvIFhHQm9vc3QpLCBSYW5kb20gRm9yZXN0cywgRGVlcCBOZXVyYWwgTmV0d29ya3MgKCBEZWVwIExlYXJuaW5nKSwgU3RhY2tlZCBFbnNlbWJsZXMsIE5haXZlIEJheWVzLCBHZW5lcmFsaXplZCBBZGRpdGl2ZSBNb2RlbHMgKEdBTSksIEFOT1ZBIEdMTSwgQ294IFByb3BvcnRpb25hbCBIYXphcmRzLCBLLU1lYW5zLCBQQ0EsIE1vZGVsU2VsZWN0aW9uLCBXb3JkMlZlYywgYXPDrSBjb21vIHVuIGFsZ29yaXRtbyBkZSBhcHJlbmRpemFqZSBhdXRvbcOhdGljbyBjb21wbGV0YW1lbnRlIGF1dG9tw6F0aWNvIChIMk8gQXV0b01MKS4KClNlIGNvbWllbnphIENhcmdhbmRvIGxpYnJlcmlhIEgybzoKCmBgYHtyfQpsaWJyYXJ5KGgybykKYGBgCgpQYXF1ZXRlcyBwYXJhIGVsIHRyYXRhbWllbnRvIGRlIGRhdG9zIHkgZ3JhZmljb3MKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShnZ3B1YnIpCmBgYAoKSW5pY2lhY2nDs24gZGVsIGNsdXN0ZXIKClVuYSB2ZXogcXVlIGxhIGxpYnJlcsOtYSBIMk8gaGEgc2lkbyBjYXJnYWRhLCBoYXkgcXVlIGluaWNpYXIgZWwgY2x1c3Rlci4gUGFyYSBlc3RlIGVqZW1wbG8sIHNlIGVtcGxlYSB1biDDum5pY28gb3JkZW5hZG9yIGRlbCBxdWUgc2UgdXRpbGl6YW4gdG9kb3Mgc3VzIGNvcmVzIGVuIHBhcmFsZWxvLgoKYGBge3J9CgpoMm8uaW5pdCgKICBpcCAgICAgICA9ICJsb2NhbGhvc3QiLAogIG50aHJlYWRzID0gLTEsCiAgbWF4X21lbV9zaXplID0gIjZnIgopCmBgYAoKU2Ugb2JzZXJ2YSBsYSBjYXJnYSBjb3JyZWN0YSBkZWwgcGFxdWV0ZSBIMm8KCiMgQ2FyZ2FuZG8gRGF0b3MgU2ltdWxhZG9zCgpgYGB7cn0KZGF0b3MgPSByZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0pvYXF1aW5BbWF0Um9kcmlnby9Fc3RhZGlzdGljYS1tYWNoaW5lLWxlYXJuaW5nLXB5dGhvbi9tYXN0ZXIvZGF0YS9ibG9icy5jc3YnKQoKCmRhdG9zIDwtIGRhdG9zICU+JSBtdXRhdGUoeSA9IGFzLmZhY3Rvcih5KSkKCmdncGxvdChkYXRhID0gZGF0b3MsIGFlcyh4ID0geF8xLCB5ID0geF8yLCBmaWxsID0geSkpICsgCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBzaXplID0gMikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsgCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICB0ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcyA9ICBlbGVtZW50X2JsYW5rKCkKICApCgpgYGAKCkNyZWFuZG8gcGFydGljaW9uZXMgdHJhaW4gLCB0ZXN0IHkgdmFsaWRhdGlvbjoKCi0gICBBc2lnbmFuZG8gdW4gcmF0aW8gZGUgODAsMjAgY29uIGBoMm8uc3BsaXRGcmFtZWA6Ci0gICBSYW5kb20gU2VlZCBkZSAxMjMKCmBgYHtyfQpkZl9oMm8gICA8LSBhcy5oMm8oZGF0b3MpCnBhcnRpY2lvbmVzIDwtIGgyby5zcGxpdEZyYW1lKGRhdGEgPSBkZl9oMm8sIHJhdGlvcyA9IGMoMC42LCAwLjIpLCBzZWVkID0gMTIzKQpkZl90cmFpbiA8LSBoMm8uYXNzaWduKGRhdGEgPSBwYXJ0aWNpb25lc1tbMV1dLCBrZXkgPSAiZGZfdHJhaW4iKQpkZl92YWwgIDwtIGgyby5hc3NpZ24oZGF0YSA9IHBhcnRpY2lvbmVzW1syXV0sIGtleSA9ICJkYXRvc192YWxpZGFjaW9uIikKZGZfdGVzdCAgPC0gaDJvLmFzc2lnbihkYXRhID0gcGFydGljaW9uZXNbWzNdXSwga2V5ID0gImRmX3Rlc3QiKQpgYGAKCk9ic2VydmFtb3MgZWwgc2hhcGUgbyBkaW1lbnNpb24gZGUgbG9zIGRhdG9zIHBhcnRpY2lvbmFkb3MKCmBgYHtyfQpsaWJyYXJ5KGdsdWUpCgpnbHVlKCJEaW1lbnNpb24gZGUgZGF0b3MgZGUgdHJhaW5pbmcge2RpbShkZl90cmFpbil9IikKCgpnbHVlKCJEaW1lbnNpb24gZGUgZGF0b3MgZGUgdmFsaWRhdGlvbiB7ZGltKGRmX3ZhbCl9IikKCmdsdWUoIkRpbWVuc2lvbiBkZSBkYXRvcyBkZSB0ZXN0aW5nIHtkaW0oZGZfdGVzdCl9IikKYGBgCgpFeGlzdGVuIDkxOCwyODEgeSAzMDEgZGF0b3MgcGFyYSB0cmFpbmluZywgdmFsaWRhY2lvbiB5IHRlc3RpbmcgcmVzcGVjdGl2YW1lbnRlLgoKQWhvcmEgc2UgcHJvY2VkZXJhIGEgY3JlYXIgNCBtb2RlbG9zIG1lZGlhbnRlIGBoMjAuZGVlcGxlYXJuaW5nYAoKIyMjIFByZWRpY2Npb24gTW9kZWxvcyBPcmlnaW5hbGVzCgpgYGB7cn0KIyBNb2RlbG9zCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cm1vZGVsb18xIDwtIGgyby5kZWVwbGVhcm5pbmcoCiAgICAgICAgICAgICAgICAgIHggICAgICAgICAgICAgICA9IGMoInhfMSIsICJ4XzIiKSwKICAgICAgICAgICAgICAgICAgeSAgICAgICAgICAgICAgID0gInkiLAogICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gICAgPSAibXVsdGlub21pYWwiLAogICAgICAgICAgICAgICAgICB0cmFpbmluZ19mcmFtZSAgPSBkZl90cmFpbiwKICAgICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgICAgID0gVFJVRSwKICAgICAgICAgICAgICAgICAgYWN0aXZhdGlvbiAgICAgID0gIlJlY3RpZmllciIsCiAgICAgICAgICAgICAgICAgIGFkYXB0aXZlX3JhdGUgICA9IEZBTFNFLAogICAgICAgICAgICAgICAgICBoaWRkZW4gICAgICAgICAgPSAxLAogICAgICAgICAgICAgICAgICBzdG9wcGluZ19yb3VuZHMgPSAwLAogICAgICAgICAgICAgICAgICBlcG9jaHMgICAgICAgICAgPSAxMDAwLAogICAgICAgICAgICAgICAgICBzZWVkICAgICAgICAgICAgPSAxMjMsCiAgICAgICAgICAgICAgICAgIG1vZGVsX2lkICAgICAgICA9ICJtb2RlbG9fMSIKICAgICAgICAgICApCgptb2RlbG9fMiA8LSBoMm8uZGVlcGxlYXJuaW5nKAogICAgICAgICAgICAgICAgeCAgICAgICAgICAgICAgID0gYygieF8xIiwgInhfMiIpLAogICAgICAgICAgICAgICAgeSAgICAgICAgICAgICAgID0gInkiLAogICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uICAgID0gIm11bHRpbm9taWFsIiwKICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lICA9IGRmX3RyYWluLAogICAgICAgICAgICAgICAgc3RhbmRhcmRpemUgICAgID0gVFJVRSwKICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gICAgICA9ICJSZWN0aWZpZXIiLAogICAgICAgICAgICAgICAgYWRhcHRpdmVfcmF0ZSAgID0gRkFMU0UsCiAgICAgICAgICAgICAgICBoaWRkZW4gICAgICAgICAgPSAxMCwKICAgICAgICAgICAgICAgIHN0b3BwaW5nX3JvdW5kcyA9IDAsCiAgICAgICAgICAgICAgICBlcG9jaHMgICAgICAgICAgPSAxMDAwLAogICAgICAgICAgICAgICAgc2VlZCAgICAgICAgICAgID0gMTIzLAogICAgICAgICAgICAgICAgbW9kZWxfaWQgICAgICAgID0gIm1vZGVsb18yIgogICAgICAgICAgICApCgptb2RlbG9fMyA8LSBoMm8uZGVlcGxlYXJuaW5nKAogICAgICAgICAgICAgICAgICB4ICAgICAgICAgICAgICAgPSBjKCJ4XzEiLCAieF8yIiksCiAgICAgICAgICAgICAgICAgIHkgICAgICAgICAgICAgICA9ICJ5IiwKICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uICAgID0gIm11bHRpbm9taWFsIiwKICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgID0gZGZfdHJhaW4sCiAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplICAgICA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gICAgICA9ICJSZWN0aWZpZXIiLAogICAgICAgICAgICAgICAgICBhZGFwdGl2ZV9yYXRlICAgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgaGlkZGVuICAgICAgICAgID0gYygxMCwgMTApLAogICAgICAgICAgICAgICAgICBzdG9wcGluZ19yb3VuZHMgPSAwLAogICAgICAgICAgICAgICAgICBlcG9jaHMgICAgICAgICAgPSAxMDAwLAogICAgICAgICAgICAgICAgICBzZWVkICAgICAgICAgICAgPSAxMjMsCiAgICAgICAgICAgICAgICAgIG1vZGVsX2lkICAgICAgICA9ICJtb2RlbG9fMyIKICAgICAgICAgICAgKQoKbW9kZWxvXzQgPC0gaDJvLmRlZXBsZWFybmluZygKICAgICAgICAgICAgICAgICAgeCAgICAgICAgICAgICAgID0gYygieF8xIiwgInhfMiIpLAogICAgICAgICAgICAgICAgICB5ICAgICAgICAgICAgICAgPSAieSIsCiAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiAgICA9ICJtdWx0aW5vbWlhbCIsCiAgICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lICA9IGRmX3RyYWluLAogICAgICAgICAgICAgICAgICBzdGFuZGFyZGl6ZSAgICAgPSBUUlVFLAogICAgICAgICAgICAgICAgICBhY3RpdmF0aW9uICAgICAgPSAiUmVjdGlmaWVyIiwKICAgICAgICAgICAgICAgICAgYWRhcHRpdmVfcmF0ZSAgID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGhpZGRlbiAgICAgICAgICA9IGMoNTAsIDUwLCA1MCksCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX3JvdW5kcyA9IDAsCiAgICAgICAgICAgICAgICAgIGVwb2NocyAgICAgICAgICA9IDEwMDAsCiAgICAgICAgICAgICAgICAgIHNlZWQgICAgICAgICAgICA9IDEyMywKICAgICAgICAgICAgICAgICAgbW9kZWxfaWQgICAgICAgID0gIm1vZGVsb180IgogICAgICAgICAgICApCmBgYAoKIyMjIERlZmluaWNpb24gZGUgQXJxdWl0ZWN0dXJhIERpc3RpbnRhIC0gTWF5b3IgQ2FudGlkYWQgZGUgTmV1cm9ucwoKRGVmaW5pY2lvbiBkZSBwcmVkaWNjaW9uZXMgcGFyYSBsb3MgNCBtb2RlbG9zIG9yaWdpbmFsZXM6CgpgYGB7cn0KIyBQcmVkaWNjaW9uZXMgZGUgY2FkYSBtb2RlbG8KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCmdyaWRfcHJlZGljY2lvbmVzIDwtIGV4cGFuZC5ncmlkKAogICAgICAgICAgICAgICAgICAgICAgICB4XzEgPSBzZXEoZnJvbSA9IG1pbihkYXRvcyR4XzEpLCB0byA9IG1heChkYXRvcyR4XzEpLCBsZW5ndGggPSA3NSksCiAgICAgICAgICAgICAgICAgICAgICAgIHhfMiA9IHNlcShmcm9tID0gbWluKGRhdG9zJHhfMiksIHRvID0gbWF4KGRhdG9zJHhfMiksIGxlbmd0aCA9IDc1KQogICAgICAgICAgICAgICAgICAgICApCgpncmlkX3ByZWRpY2Npb25lc19oMm8gPC0gYXMuaDJvKGdyaWRfcHJlZGljY2lvbmVzKQoKCnByZWRpY2Npb25lc18xIDwtIGgyby5wcmVkaWN0KAogICAgICAgICAgICAgICAgICAgIG9iamVjdCAgPSBtb2RlbG9fMSwKICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9wcmVkaWNjaW9uZXNfaDJvCiAgICAgICAgICAgICAgICAgICkKCnByZWRpY2Npb25lc18yIDwtIGgyby5wcmVkaWN0KAogICAgICAgICAgICAgICAgICAgIG9iamVjdCAgPSBtb2RlbG9fMiwKICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9wcmVkaWNjaW9uZXNfaDJvCiAgICAgICAgICAgICAgICAgICkKCnByZWRpY2Npb25lc18zIDwtIGgyby5wcmVkaWN0KAogICAgICAgICAgICAgICAgICAgIG9iamVjdCAgPSBtb2RlbG9fMywKICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9wcmVkaWNjaW9uZXNfaDJvCiAgICAgICAgICAgICAgICAgICkKCnByZWRpY2Npb25lc180IDwtIGgyby5wcmVkaWN0KAogICAgICAgICAgICAgICAgICAgIG9iamVjdCAgPSBtb2RlbG9fNCwKICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9wcmVkaWNjaW9uZXNfaDJvCiAgICAgICAgICAgICAgICAgICkKCgpncmlkX3ByZWRpY2Npb25lcyRtb2RlbG9fMSA8LSBhcy52ZWN0b3IocHJlZGljY2lvbmVzXzEkcHJlZGljdCkKZ3JpZF9wcmVkaWNjaW9uZXMkbW9kZWxvXzIgPC0gYXMudmVjdG9yKHByZWRpY2Npb25lc18yJHByZWRpY3QpCmdyaWRfcHJlZGljY2lvbmVzJG1vZGVsb18zIDwtIGFzLnZlY3RvcihwcmVkaWNjaW9uZXNfMyRwcmVkaWN0KQpncmlkX3ByZWRpY2Npb25lcyRtb2RlbG9fNCA8LSBhcy52ZWN0b3IocHJlZGljY2lvbmVzXzQkcHJlZGljdCkKCmBgYAoKYGBge3J9CiMgR3LDoWZpY28gZGUgcHJlZGljY2lvbmVzCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnAxIDwtIGdncGxvdChkYXRhID0gZ3JpZF9wcmVkaWNjaW9uZXMsIGFlcyh4ID0geF8xLCB5ID0geF8yLCBjb2xvciA9IG1vZGVsb18xKSkgKyAKICAgICAgZ2VvbV9wb2ludChzaXplID0gMC41KSArCiAgICAgIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsgIAogICAgICBsYWJzKHRpdGxlID0gIkFycXVpdGVjdHVyYTogKDUpIikgKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgICAgIGF4aXMudGV4dCAgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGlja3MgPSAgZWxlbWVudF9ibGFuaygpKQoKcDIgPC0gZ2dwbG90KGRhdGEgPSBncmlkX3ByZWRpY2Npb25lcywgYWVzKHggPSB4XzEsIHkgPSB4XzIsIGNvbG9yID1tb2RlbG9fMikpICsgCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogICAgICBsYWJzKHRpdGxlID0gIkFycXVpdGVjdHVyYTogKDEwKSIpICsKICAgICAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKyAKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgICAgICBheGlzLnRleHQgID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzID0gIGVsZW1lbnRfYmxhbmsoKSkKCnAzIDwtIGdncGxvdChkYXRhID0gZ3JpZF9wcmVkaWNjaW9uZXMsIGFlcyh4ID0geF8xLCB5ID0geF8yLCBjb2xvciA9IG1vZGVsb18zKSkgKyAKICAgICAgZ2VvbV9wb2ludChzaXplID0gMC41KSArCiAgICAgIGxhYnModGl0bGUgPSAiQXJxdWl0ZWN0dXJhOiAoMjAsIDIwKSIpICsKICAgICAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKyAKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgICAgICBheGlzLnRleHQgID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzID0gIGVsZW1lbnRfYmxhbmsoKSkKCnA0IDwtIGdncGxvdChkYXRhID0gZ3JpZF9wcmVkaWNjaW9uZXMsIGFlcyh4ID0geF8xLCB5ID0geF8yLCBjb2xvciA9IG1vZGVsb180KSkgKyAKICAgICAgZ2VvbV9wb2ludChzaXplID0gMC41KSArCiAgICAgIGxhYnModGl0bGUgPSAiQXJxdWl0ZWN0dXJhOiAoNTAsIDUwLCA1MCkiKSArCiAgICAgIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsgCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTExKSwKICAgICAgICAgICAgYXhpcy50ZXh0ICA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aWNrcyA9ICBlbGVtZW50X2JsYW5rKCkpCgoKZ2dhcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBucm93ID0gMiwgbmNvbCA9IDIpCgpgYGAKCiMjIyBDcmVhY2lvbiBkZSBOdWV2b3MgTW9kZWxvcyBjb24gbWF5b3IgY2FudGlkYWQgZGUgaGlkZGVuIExheWVycyAoQ2FwYXMgT2N1bHRhcykKCkEgY29udGludWFjaW9uICwgc2UgY3JlYSBkaWZlcmVudGVzIG1vZGVsb3MgYXBsaWNhbmRvIHVuYSBhcnF1aXRlY3R1cmEgY29uIGRpZmVyZW50ZXMgcGFyYW1ldHJvcyAsIGVuIGVzdGUgY2FzbzoKCjEuICAqKk1vZGVsbyAxKiogOiBNb2RlbG8gQ29uIDYgY2FwYXMgb2N1bHRhcyBkZSAzMCBuZXVyb25hcyBjYWRhIHVuYSwgYWN0aXZhY2lvbiBSZUxVIHNpbiBkcm9wb3V0CjIuICAqKk1vZGVsbyAyKiogOiBNb2RlbG8gY29uIDEgc29sYSBjYXBhIG9jdWx0YSBkZSA1MDAgbmV1cm9uYXMsIGFjdGl2YWNpb24gUmVMVSBjb24gZHJvcG91dAozLiAgKipNb2RlbG8gMyoqIDogTW9kZWxvIGNvbiAxMCBjYXBhcyBvY3VsdGFzIGRlIDEwIG5ldXJvbmFzIGNhZGEgdW5hICwgYWN0aXZhY2lvbiB0YW5oIHNpbiBkcm9wb3V0CjQuICAqKk1vZGVsbyA0KiogOiBNb2RlbG8gY29uIDMgY2FwYXMgb2N1bHRhcyBkZSAxMCBuZXVyb25hcyBjYWRhIHVuYSAsIGFjdGl2YWNpb24gdGFuaCBzaW4gZHJvcG91dAoKKipUYW5oOioqCgokJCB0YW5oID0gXGZyYWN7ZV54IOKAkyBlXi14fXtlXnggKyBlXi14fSQkIFNlIGNvbnNpZGVyYW4gdmFsb3JlcyBuZWdhdGl2b3MsIG1pZW50cmFzIHF1ZSBlbiBlbCByYW5nbyBtw61uaW1vIHNpZ21vaWRlbyBlcyAwIHBlcm8gZW4gVGFuaCwgZWwgcmFuZ28gbcOtbmltbyBlcyAtMS4gRXN0YSBlcyBsYSByYXrDs24gcG9yIGxhIHF1ZSBsYSBmdW5jacOzbiBkZSBhY3RpdmFjacOzbiBkZSBUYW5oIHRhbWJpw6luIHNlIGNvbm9jZSBjb21vIGZ1bmNpw7NuIGRlIGFjdGl2YWNpw7NuIGNlbnRyYWRhIGVuIGNlcm8uCgpEZXN2ZW50YWphOgoKVGFtYmnDqW4gZW5mcmVudGEgZWwgbWlzbW8gcHJvYmxlbWEgZGVsIHByb2JsZW1hIGRlbCBncmFkaWVudGUgZGUgZnVnYSBjb21vIHVuYSBmdW5jacOzbiBzaWdtb2lkZWEuCgpgYGB7cn0KIyBEZWZpbmljaW9uIE1vZGVsb3MgQ3VzdG9tCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgoKIyBNb2RlbG8gMSAKbW9kZWxfMSA8LSBoMm8uZGVlcGxlYXJuaW5nKAogICAgICAgICAgICAgICAgICB4ICAgICAgICAgICAgICAgPSBjKCJ4XzEiLCAieF8yIiksCiAgICAgICAgICAgICAgICAgIHkgICAgICAgICAgICAgICA9ICJ5IiwKICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uICAgID0gIm11bHRpbm9taWFsIiwKICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgID0gZGZfdHJhaW4sCiAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplICAgICA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gICAgICA9ICJSZWN0aWZpZXIiLAogICAgICAgICAgICAgICAgICBhZGFwdGl2ZV9yYXRlICAgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgaGlkZGVuICAgICAgICAgID0gYygzMCwzMCwzMCwzMCwzMCwzMCksCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX3JvdW5kcyA9IDAsCiAgICAgICAgICAgICAgICAgIGVwb2NocyAgICAgICAgICA9IDEwMDAsCiAgICAgICAgICAgICAgICAgIHNlZWQgICAgICAgICAgICA9IDEyMywKICAgICAgICAgICAgICAgICAgbW9kZWxfaWQgICAgICAgID0gIm1vZGVsXzEiCiAgICAgICAgICAgKQoKCiMgTW9kZWxvIDIKbW9kZWxfMiA8LSBoMm8uZGVlcGxlYXJuaW5nKAogICAgICAgICAgICAgICAgICB4ICAgICAgICAgICAgICAgPSBjKCJ4XzEiLCAieF8yIiksCiAgICAgICAgICAgICAgICAgIHkgICAgICAgICAgICAgICA9ICJ5IiwKICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uICAgID0gIm11bHRpbm9taWFsIiwKICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgID0gZGZfdHJhaW4sCiAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplICAgICA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gICAgICA9ICJSZWN0aWZpZXJXaXRoRHJvcG91dCIsCiAgICAgICAgICAgICAgICAgIGFkYXB0aXZlX3JhdGUgICA9IEZBTFNFLAogICAgICAgICAgICAgICAgICBoaWRkZW4gICAgICAgICAgPSA1MDAsCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX3JvdW5kcyA9IDAsCiAgICAgICAgICAgICAgICAgIGVwb2NocyAgICAgICAgICA9IDEwMDAsCiAgICAgICAgICAgICAgICAgIHNlZWQgICAgICAgICAgICA9IDEyMywKICAgICAgICAgICAgICAgICAgbW9kZWxfaWQgICAgICAgID0gIm1vZGVsXzIiCiAgICAgICAgICAgKQoKCiMgTW9kZWxvIDMKbW9kZWxfMyA8LSBoMm8uZGVlcGxlYXJuaW5nKAogICAgICAgICAgICAgICAgICB4ICAgICAgICAgICAgICAgPSBjKCJ4XzEiLCAieF8yIiksCiAgICAgICAgICAgICAgICAgIHkgICAgICAgICAgICAgICA9ICJ5IiwKICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uICAgID0gIm11bHRpbm9taWFsIiwKICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfZnJhbWUgID0gZGZfdHJhaW4sCiAgICAgICAgICAgICAgICAgIHN0YW5kYXJkaXplICAgICA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIGFjdGl2YXRpb24gICAgICA9ICJUYW5oIiwKICAgICAgICAgICAgICAgICAgYWRhcHRpdmVfcmF0ZSAgID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGhpZGRlbiAgICAgICAgICA9IGMoMTAsMTAsMTAsMTAsMTAsMTAsMTAsMTAsMTAsMTApLAogICAgICAgICAgICAgICAgICBzdG9wcGluZ19yb3VuZHMgPSAwLAogICAgICAgICAgICAgICAgICBlcG9jaHMgICAgICAgICAgPSAxMDAwLAogICAgICAgICAgICAgICAgICBzZWVkICAgICAgICAgICAgPSAxMjMsCiAgICAgICAgICAgICAgICAgIG1vZGVsX2lkICAgICAgICA9ICJtb2RlbF8zIgogICAgICAgICAgICkKCgojIE1vZGVsbyA0Cm1vZGVsXzQgPC0gaDJvLmRlZXBsZWFybmluZygKICAgICAgICAgICAgICAgICAgeCAgICAgICAgICAgICAgID0gYygieF8xIiwgInhfMiIpLAogICAgICAgICAgICAgICAgICB5ICAgICAgICAgICAgICAgPSAieSIsCiAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiAgICA9ICJtdWx0aW5vbWlhbCIsCiAgICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lICA9IGRmX3RyYWluLAogICAgICAgICAgICAgICAgICBzdGFuZGFyZGl6ZSAgICAgPSBUUlVFLAogICAgICAgICAgICAgICAgICBhY3RpdmF0aW9uICAgICAgPSAiVGFuaCIsCiAgICAgICAgICAgICAgICAgIGFkYXB0aXZlX3JhdGUgICA9IEZBTFNFLAogICAgICAgICAgICAgICAgICBoaWRkZW4gICAgICAgICAgPSBjKDEwLDEwLDEwKSwKICAgICAgICAgICAgICAgICAgc3RvcHBpbmdfcm91bmRzID0gMCwKICAgICAgICAgICAgICAgICAgZXBvY2hzICAgICAgICAgID0gMTAwMCwKICAgICAgICAgICAgICAgICAgc2VlZCAgICAgICAgICAgID0gMTIzLAogICAgICAgICAgICAgICAgICBtb2RlbF9pZCAgICAgICAgPSAibW9kZWxfNCIKICAgICAgICAgICApCgoKCgpgYGAKCiMjIyBHZW5lcmFuZG8gcHJlZGljY2lvbmVzIGNvbiBsb3MgbnVldm9zIG1vZGVsb3M6CgpgYGB7cn0KCgpncmlkX25ldyA8LSBleHBhbmQuZ3JpZCgKCiAgICAgICAgICAgICAgICAgICAgICAgIHhfMSA9IHNlcShmcm9tID0gbWluKGRhdG9zJHhfMSksIHRvID0gbWF4KGRhdG9zJHhfMSksIGxlbmd0aCA9IDc1KSwKICAgICAgICAgICAgICAgICAgICAgICAgeF8yID0gc2VxKGZyb20gPSBtaW4oZGF0b3MkeF8yKSwgdG8gPSBtYXgoZGF0b3MkeF8yKSwgbGVuZ3RoID0gNzUpCiAgICAgICAgICAgICAgICAgICAgICkKCmdyaWRfbmV3X3ByZWRpY3QgPC0gYXMuaDJvKGdyaWRfbmV3KQoKCnByZWRpY3RfMSA8LSBoMm8ucHJlZGljdCgKICAgICAgICAgICAgICAgICAgICBvYmplY3QgID0gbW9kZWxfMSwKICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZ3JpZF9uZXdfcHJlZGljdAogICAgICAgICAgICAgICAgICApCgpwcmVkaWN0XzIgPC0gaDJvLnByZWRpY3QoCiAgICAgICAgICAgICAgICAgICAgb2JqZWN0ICA9IG1vZGVsXzIsCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGdyaWRfbmV3X3ByZWRpY3QKICAgICAgICAgICAgICAgICAgKQoKcHJlZGljdF8zIDwtIGgyby5wcmVkaWN0KAogICAgICAgICAgICAgICAgICAgIG9iamVjdCAgPSBtb2RlbF8zLAogICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBncmlkX25ld19wcmVkaWN0IAogICAgICAgICAgICAgICAgICApCgpwcmVkaWN0XzQgPC0gaDJvLnByZWRpY3QoCiAgICAgICAgICAgICAgICAgICAgb2JqZWN0ICA9IG1vZGVsXzQsCiAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGdyaWRfbmV3X3ByZWRpY3QKICAgICAgICAgICAgICAgICAgKQoKZ3JpZF9uZXckbW9kZWxfMSA8LSBhcy52ZWN0b3IocHJlZGljdF8xJHByZWRpY3QpCmdyaWRfbmV3JG1vZGVsXzIgPC0gYXMudmVjdG9yKHByZWRpY3RfMiRwcmVkaWN0KQpncmlkX25ldyRtb2RlbF8zIDwtIGFzLnZlY3RvcihwcmVkaWN0XzMkcHJlZGljdCkKZ3JpZF9uZXckbW9kZWxfNCA8LSBhcy52ZWN0b3IocHJlZGljdF80JHByZWRpY3QpCmBgYAoKIyMjIFByZWRpY2Npb25lcyBjb24gbnVldm9zIE1vZGVsb3M6CgpgYGB7cn0KIyBHcsOhZmljbyBkZSBwcmVkaWNjaW9uZXMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KcDEgPC0gZ2dwbG90KGRhdGEgPSBncmlkX25ldywgYWVzKHggPSB4XzEsIHkgPSB4XzIsIGNvbG9yID0gbW9kZWxfMSkpICsgCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogICAgICB0aGVtZV9lY29ub21pc3QoKSArICAKICAgICAgbGFicyh0aXRsZSA9ICJBcnF1aXRlY3R1cmE6ICgzMCwzMCwzMCwzMCwzMCwzMCkgUmVMVSIpICsKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgICAgICBheGlzLnRleHQgID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzID0gIGVsZW1lbnRfYmxhbmsoKSkKCnAyIDwtIGdncGxvdChkYXRhID0gZ3JpZF9uZXcsIGFlcyh4ID0geF8xLCB5ID0geF8yLCBjb2xvciA9IG1vZGVsXzIpKSArIAogICAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICAgICAgbGFicyh0aXRsZSA9ICJBcnF1aXRlY3R1cmE6IE5ldXJvbmEgVW5pY2EgUmVMVSBEcm9wb3V0ICIpICsKICAgICAgdGhlbWVfZWNvbm9taXN0KCkgKyAKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgICAgICBheGlzLnRleHQgID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRpY2tzID0gIGVsZW1lbnRfYmxhbmsoKSkKCnAzIDwtIGdncGxvdChkYXRhID0gZ3JpZF9uZXcsIGFlcyh4ID0geF8xLCB5ID0geF8yLCBjb2xvciA9IG1vZGVsXzMpKSArIAogICAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICAgICAgbGFicyh0aXRsZSA9ICJBcnF1aXRlY3R1cmEgYygxMCwxMCwxMCwxMCwxMCwxMCwxMCwxMCwxMCwxMCkgVGFuSCAiKSArCiAgICAgIHRoZW1lX2Vjb25vbWlzdCgpICsgCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTExKSwKICAgICAgICAgICAgYXhpcy50ZXh0ICA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgYXhpcy50aWNrcyA9ICBlbGVtZW50X2JsYW5rKCkpCgpwNCA8LSBnZ3Bsb3QoZGF0YSA9IGdyaWRfbmV3LCBhZXMoeCA9IHhfMSwgeSA9IHhfMiwgY29sb3IgPSBtb2RlbF80KSkgKyAKCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogICAgICBsYWJzKHRpdGxlID0gIkFycXVpdGVjdHVyYTogYygxMCwgMTAsIDEwKSB0YW5oIikgKwogICAgICB0aGVtZV9lY29ub21pc3QoKSArIAogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgICAgIGF4aXMudGV4dCAgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGlja3MgPSAgZWxlbWVudF9ibGFuaygpKQoKCmdnYXJyYW5nZShwMSwgcDIsIHAzLCBwNCwgbnJvdyA9IDIsIG5jb2wgPSAyKQoKYGBgCgpDb21wYXJhY2lvbiBkZSBBY2N1cmFjeSBlbnRyZSBtb2RlbG9zIGVuIGVsIGRhdGFzZXQgZGUgdGVzdDoKCmBgYHtyfQpwcmVkaWN0X3Rlc3QgPC0gaDJvLnByZWRpY3Qob2JqZWN0ICA9IG1vZGVsXzEsbmV3ZGF0YSA9IGRmX3Rlc3QpCmFjY3VyYWN5XzFfbmV3IDwtIG1lYW4ocHJlZGljdF90ZXN0WyJwcmVkaWN0Il0gPT0gZGZfdGVzdFsieSJdKQoKcHJlZGljdF90ZXN0IDwtIGgyby5wcmVkaWN0KG9iamVjdCAgPSBtb2RlbF8yLG5ld2RhdGEgPSBkZl90ZXN0KQphY2N1cmFjeV8yX25ldyA8LSBtZWFuKHByZWRpY3RfdGVzdFsicHJlZGljdCJdID09IGRmX3Rlc3RbInkiXSkKCnByZWRpY3RfdGVzdCA8LSBoMm8ucHJlZGljdChvYmplY3QgID0gbW9kZWxfMyxuZXdkYXRhID0gZGZfdGVzdCkKYWNjdXJhY3lfM19uZXcgPC0gbWVhbihwcmVkaWN0X3Rlc3RbInByZWRpY3QiXSA9PSBkZl90ZXN0WyJ5Il0pCgpwcmVkaWN0X3Rlc3QgPC0gaDJvLnByZWRpY3Qob2JqZWN0ICA9IG1vZGVsXzQsbmV3ZGF0YSA9IGRmX3Rlc3QpCmFjY3VyYWN5XzRfbmV3IDwtIG1lYW4ocHJlZGljdF90ZXN0WyJwcmVkaWN0Il0gPT0gZGZfdGVzdFsieSJdKQoKYGBgCgpgYGB7cn0KICBnbHVlKCJBY2N1cmFjeSBkZWwgTW9kZWxvIDEgOiB7YWNjdXJhY3lfMX0gXG4KICAgICBBY2N1cmFjeSBkZWwgTW9kZWxvIDIgOiB7YWNjdXJhY3lfMn0gXG4KICAgICBBY2N1cmFjeSBkZWwgTW9kZWxvIDMgOiB7YWNjdXJhY3lfM30gXG4KICAgICBBY2N1cmFjeSBkZWwgTW9kZWxvIDQgOiB7YWNjdXJhY3lfNH0gXG4KICAgICAiICkKCgoKYGBgCgpgYGB7cn0KCnByZWRpY3RfdGVzdCA8LSBoMm8ucHJlZGljdChvYmplY3QgID0gbW9kZWxfMSxuZXdkYXRhID0gZGZfdGVzdCkKYWNjdXJhY3lfMV9uZXcgPC0gbWVhbihwcmVkaWN0X3Rlc3RbInByZWRpY3QiXSA9PSBkZl90ZXN0WyJ5Il0pCgpwcmVkaWN0X3Rlc3QgPC0gaDJvLnByZWRpY3Qob2JqZWN0ICA9IG1vZGVsXzIsbmV3ZGF0YSA9IGRmX3Rlc3QpCmFjY3VyYWN5XzJfbmV3IDwtIG1lYW4ocHJlZGljdF90ZXN0WyJwcmVkaWN0Il0gPT0gZGZfdGVzdFsieSJdKQoKcHJlZGljdF90ZXN0IDwtIGgyby5wcmVkaWN0KG9iamVjdCAgPSBtb2RlbF8zLG5ld2RhdGEgPSBkZl90ZXN0KQphY2N1cmFjeV8zX25ldyA8LSBtZWFuKHByZWRpY3RfdGVzdFsicHJlZGljdCJdID09IGRmX3Rlc3RbInkiXSkKCnByZWRpY3RfdGVzdCA8LSBoMm8ucHJlZGljdChvYmplY3QgID0gbW9kZWxfNCxuZXdkYXRhID0gZGZfdGVzdCkKYWNjdXJhY3lfNF9uZXcgPC0gbWVhbihwcmVkaWN0X3Rlc3RbInByZWRpY3QiXSA9PSBkZl90ZXN0WyJ5Il0pCmBgYAoKSW1wcmltaWVuZG8gZWwgYWNjdXJhY3kgZGUgY2FkYSBtb2RlbG86CgpgYGB7cn0KICBnbHVlKCJBY2N1cmFjeSBkZWwgTW9kZWxvIDEgIE51ZXZvOiB7YWNjdXJhY3lfMV9uZXd9IFxuCiAgICAgQWNjdXJhY3kgZGVsIE1vZGVsbyAyIE51ZXZvOiB7YWNjdXJhY3lfMl9uZXd9IFxuCiAgICAgQWNjdXJhY3kgZGVsIE1vZGVsbyAzIE51ZXZvOiB7YWNjdXJhY3lfM19uZXd9IFxuCiAgICAgQWNjdXJhY3kgZGVsIE1vZGVsbyA0IE51ZXZvIDoge2FjY3VyYWN5XzRfbmV3fSBcbgogICAgICIgKQoKCgpgYGAKClNlIGdyYWZpY2EgdG9kYXMgbGFzIGFjY3VyYWNpZXMgZGUgbG9zIG1vZGVsb3MgcGxhbnRlYWRvczoKCmBgYHtyfQoKYWNjdXJhY3kgPC0gYyhhY2N1cmFjeV8xLGFjY3VyYWN5XzIsYWNjdXJhY3lfMyxhY2N1cmFjeV80LCBhY2N1cmFjeV8xX25ldywgYWNjdXJhY3lfMl9uZXcsYWNjdXJhY3lfM19uZXcsYWNjdXJhY3lfNF9uZXcpCgoKbW9kZWwgPC0gYygiTW9kZWxvIDEiLCAiTW9kZWxvIDIiLCJNb2RlbG8gMyIsIk1vZGVsbyA0IiwgIk1vZGVsbyAxIG51ZXZvIiwgIk1vZGVsbyAyIE51ZXZvIiwiTW9kZWxvIDMgTnVldm8iLCJNb2RlbG8gNCBOdWV2byIpCgoKCgpkYXRhIDwtIGRhdGEuZnJhbWUoYWNjdXJhY3ksIG1vZGVsKSAgICAKZ2dwbG90KGRhdGEsYWVzKHg9IHJlb3JkZXIobW9kZWwsLWFjY3VyYWN5KSx5PSBhY2N1cmFjeSwgZmlsbD1tb2RlbCApLHRpdGxlKCJBY2N1cmFjeSBkZSBNb2RlbG9zIFBsYW50ZWFkb3MgIikpK2dlb21fYmFyKHN0YXQgPSJpZGVudGl0eSIpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQoKYGBgCgoKU2Ugb2JzZXJ2YSBxdWUgZWwgbWVqb3IgY3JlYWRvIGNvcnJlc3BvbmRlIGFsIE1vZGVsbyAzLCBjb24gdW4gODglIGRlIGNlcnRlemEgZW4gbG9zIGRhdG9zIGRlIHRlc3RpbmcsIHJlY29yZGFuZG8gcXVlIGVsIG1vZGVsbyAzIHBvc2VlIHVuYSBhcnF1aXRlY3R1cmEgIGNvbiBkZSAyIGNhcGFzIG9jdWx0YXMgYGMoMTAsIDEwKWAgZGUgMTAgbmV1cm9uYXMgY2FkYSB1bmEuCgo=