library(readr)
library(ggplot2)
library(gridExtra)
library(tidyverse)
[30m── [1mAttaching packages[22m ────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──[39m
[30m[32m✔[30m [34mtibble [30m 1.4.2 [32m✔[30m [34mdplyr [30m 0.7.6
[32m✔[30m [34mtidyr [30m 0.8.1 [32m✔[30m [34mstringr[30m 1.3.1
[32m✔[30m [34mpurrr [30m 0.2.5 [32m✔[30m [34mforcats[30m 0.3.0[39m
[30m── [1mConflicts[22m ───────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
[31m✖[30m [34mdplyr[30m::[32mcombine()[30m masks [34mgridExtra[30m::combine()
[31m✖[30m [34mdplyr[30m::[32mfilter()[30m masks [34mstats[30m::filter()
[31m✖[30m [34mdplyr[30m::[32mlag()[30m masks [34mstats[30m::lag()[39m
library(lazyeval)
Attaching package: ‘lazyeval’
The following objects are masked from ‘package:purrr’:
is_atomic, is_formula
dataset <- read_csv("classification-results.csv")
Parsed with column specification:
cols(
.default = col_double(),
dataset = col_character(),
var = col_character(),
config.train = col_character(),
config.vars = col_character(),
T = col_integer(),
alg = col_character()
)
See spec(...) for full column specifications.
dataset[,8:ncol(dataset)] <- round(dataset[,8:ncol(dataset)],digits = 3)
stations <- unique(dataset$var)
algoritmos <- unique(dataset$alg)
metrics <- c("FAR","Sensitivity","Specificity","Accuracy","Kappa","F1","Precision")
#' ## Carga dataset
#' Creación de dataset genérico para resultados clasificación
df <- dataset %>%
unite(dataset,
col=label,c("dataset","var","config.train","config.vars","T","alg"),
sep = "-",remove=FALSE) %>%
select(label,dataset,var,config.train,config.vars,T,alg,FAR,Sensitivity,Specificity,Accuracy,Kappa,F1,Precision)
Variabilidad de la Sensitivity por cada estación
p <-ggplot(aes(y = Sensitivity, x = var, fill = config.vars), data = df) +
geom_boxplot() + coord_flip()
print(p)

Los modelos con config-all presentaron mejor desempeño en sensitivity, excepto para Tunuyan y la Llave (este está parejo)
Variabilidad de la Sensitivity por según dataset usado
p <-ggplot(aes(y = Sensitivity, x = dataset, fill = config.vars), data = df) +
geom_boxplot() + coord_flip()
print(p)

Variabilidad de F1 por según dataset usado
p <-ggplot(aes(y = F1, x = dataset, fill = config.vars), data = df) +
geom_boxplot() + coord_flip()
print(p)

No se visualizan gran variabilidad entre los datasets usados entre F1, Sensitivity y precision
Variabilidad de Sensitivity según dataset usado
p <-ggplot(aes(y = Sensitivity, x = dataset, fill = config.train), data = df) +
geom_boxplot() + coord_flip()
print(p)

SMOTE aumenta sensitivity en ambos datasets
Variabilidad de F1 por cada estación
p <-ggplot(aes(y = F1, x = var, fill = config.vars), data = df) + geom_boxplot() + coord_flip()
print(p)

Los modelos con config-all presentaron mejor desempeño en terminos medios de F1, excepto para Tunuyan y la Llave (este está parejo)
Variabilidad de precisión por cada estación
p <-ggplot(aes(y = Precision , x = var, fill = config.vars), data = df) + geom_boxplot() + coord_flip()
print(p)

En términos medios la precision mejora en las Paredes y Junín con config-local. La llave, tunuyan y agua amarga “parejos”
Según cuántos días anteriores de información sumamos.
#' ## Según cuantos días anteriores de información sumamos.
#'
p <-ggplot(aes(y = Precision , x = var, fill = as.factor(T)), data = df) + geom_boxplot() + coord_flip()
print(p)

p <-ggplot(aes(y = Sensitivity , x = var, fill = as.factor(T)), data = df) + geom_boxplot() + coord_flip()
print(p)

p <-ggplot(aes(y = F1 , x = var, fill = as.factor(T)), data = df) +
geom_boxplot() + coord_flip() + labs(fill="T")
print(p)

En general, con un día o dos se obtienen buenos resultados en términos de precision,recall y F1, ¿podría prescindirse de agregar más? Hay varios casos particulares, por ejemplo Agua amarga en sensitivity para T=1, su media es más baja respecto al T=2, sin embargo el máximo de sensitivity es con T=1.
Comparación normal vs smote por estación para métricas sensitivity y precision
p <-ggplot(aes(y = Precision , x = var, fill = config.train), data = df) + geom_boxplot() + coord_flip()
print(p)

p <-ggplot(aes(y = Sensitivity , x = var, fill = config.train), data = df) + geom_boxplot() + coord_flip()
print(p)

p <-ggplot(aes(y = F1 , x = var, fill = config.train), data = df) + geom_boxplot() + coord_flip()
print(p)

La configuración SMoTE aumenta la sensitivity/F1 en detrimento de la precision. Es una consecuencia esperable.
Comportamiento general de local vs all de los algoritmos por cada estación según métrica
for(s in stations){
df1 <- df %>% filter( var == s )
p <-ggplot(aes(y = F1 , x = alg, fill = config.vars), data = df1) +
geom_boxplot() + coord_flip() + labs(title=paste("Estación",s,sep=" "))
print(p)
p <-ggplot(aes(y = Sensitivity , x = alg, fill = config.vars), data = df1) +
geom_boxplot() + coord_flip() + labs(title=paste("Estación",s,sep=" "))
print(p)
p <-ggplot(aes(y = Precision , x = alg, fill = config.vars), data = df1) +
geom_boxplot() + coord_flip() + labs(title=paste("Estación",s,sep=" "))
print(p)
}















library(reshape)
df3 <- df %>% filter( dataset == "dacc") %>% select(-one_of(c("FAR")))
# melt(df3,id.vars="label")
df4 <- melt(as.data.frame(df3),
id.vars =(c("label","dataset","var","config.train","config.vars","T","alg")),
measure.vars = metrics[-1])
p <-ggplot(aes(y = value , x = var, fill = variable), data = df4) +
geom_boxplot() + coord_flip() + labs(title="Variabilidad de las métricas por las estaciones")
print(p)
df4 <- df %>% filter( dataset == "dacc")
df4 <- melt(as.data.frame(df4),
id.vars =(c("label","dataset","var","config.train","config.vars","T","alg")),
measure.vars = metrics)
for(a in algoritmos)
{
for(m in metrics)
{
df5 <- df4 %>% filter( alg == a & variable == m)
p <-ggplot(aes(y = value , x = var, fill = config.vars), data = df5) +
geom_boxplot() + coord_flip() + labs(title=paste("Comportamiento de ",m," en modelo ",a,sep=""))
print(p)
}
}
Random forest
- En agua amarga y Tunuyan, el FAR es menor en modelos config all. También para estas estaciones config all tiene mayores valores de specificity y precision que config local, lo contrario para las restantes. Lo contrario para el resto de las estaciones (gana local)
- Los mejores modelos en terminos de sensitivity/recall son config all
- Valores de accuracy entre (aproximado) 0.89 y 0.94
- Valor de Kappa aumenta (hay mayores valores) con config all, analizando las medias
df3 <- df %>%
select(label,dataset,var,config.train,config.vars,T,alg,Sensitivity,Specificity,Accuracy,Kappa,F1,Precision) %>%
filter( var == stations[1] & alg==algoritmos[1]) # & T == 1 & dataset == DATASET &
df3
for(s in stations)
{
for(a in algoritmos)
{
df3 <- df %>%
select(label,dataset,var,config.train,config.vars,T,alg,Sensitivity,Specificity,Accuracy,Kappa,F1,Precision) %>%
filter( var == s & alg==a & T == 1 & dataset == "dacc") # & # <- AQUI FILTRO
df7 <- melt(as.data.frame(df3),id=(c("label","dataset","var","config.train","config.vars","T","alg")))
p<- ggplot(data=df7, aes(x= reorder(label,value), y=value, fill=variable)) +
geom_bar(stat="identity",position=position_dodge())+
coord_flip()+
theme(legend.position="bottom") +
labs(x = a, y = "metricas", title=paste("comparacion",a,s,sep="-"))
print(p)
# for(m in metrics){
# plot_ranking_alg(df3,m,s,a)
#
# }
}
}




















Filtramos para quedarnos con los modelos cuya Sensitivity y Precision haya sido mayor o igual a 0.7, observar el comportamiento por estación y algoritmo
Lo siguiente da error: verificar/chequear
for(s in stations)
{
for(a in algoritmos)
{
df3 <- df %>%
select(label,dataset,var,config.train,config.vars,T,alg,Sensitivity,Precision,F1) %>% #,Specificity,Accuracy,Kappa,F1
filter( Sensitivity > 0.7 , Precision > 0.7 , var==s, dataset == "dacc",alg==a ) # & T == 1 & dataset == DATASET &
df7 <- melt(data.frame(df3),id=(c("label","dataset","var","config.train","config.vars","T","alg")))
p<- ggplot(data=df7, aes(x= label, y=value, fill=variable)) +
geom_bar(stat="identity",position=position_dodge())+
coord_flip()+
theme(legend.position="bottom") +
labs(title=paste("Estacion ",s," algoritmo ", a))
print(p)
}
}
stations
[1] "tunuyan.temp_min" "agua_amarga.temp_min" "junin.temp_min" "la_llave.temp_min" "las_paredes.temp_min"
Si queremos correr lo anterior fuera del for o bucle
df3 <- df %>%
select(label,dataset,var,config.train,config.vars,T,alg,Sensitivity,Precision) %>% #,Specificity,Accuracy,Kappa,F1
filter( Sensitivity > 0.7 , Precision > 0.7 , var==stations[1], dataset == "dacc",alg=="rf" ) # & T == 1 & dataset == DATASET &
df7 <- melt(as.data.frame(df3),id=(c("label","dataset","var","config.train","config.vars","T","alg")))
p<- ggplot(data=df7, aes(x= label, y=value, fill=variable)) +
geom_bar(stat="identity",position=position_dodge())+
coord_flip()+
theme(legend.position="bottom") +
labs(title="")
print(p)

LS0tCnRpdGxlOiAiR3LDoWZpY29zIHkgc3UgaW50ZXJwcmVzdGFjacOzbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGxhenlldmFsKQoKZGF0YXNldCA8LSByZWFkX2NzdigiY2xhc3NpZmljYXRpb24tcmVzdWx0cy5jc3YiKQpkYXRhc2V0Wyw4Om5jb2woZGF0YXNldCldIDwtIHJvdW5kKGRhdGFzZXRbLDg6bmNvbChkYXRhc2V0KV0sZGlnaXRzID0gMykKCnN0YXRpb25zIDwtIHVuaXF1ZShkYXRhc2V0JHZhcikKYWxnb3JpdG1vcyA8LSB1bmlxdWUoZGF0YXNldCRhbGcpCm1ldHJpY3MgPC0gYygiRkFSIiwiU2Vuc2l0aXZpdHkiLCJTcGVjaWZpY2l0eSIsIkFjY3VyYWN5IiwiS2FwcGEiLCJGMSIsIlByZWNpc2lvbiIpCgojJyAjIyBDYXJnYSBkYXRhc2V0CiMnIENyZWFjacOzbiBkZSBkYXRhc2V0IGdlbsOpcmljbyBwYXJhIHJlc3VsdGFkb3MgY2xhc2lmaWNhY2nDs24KZGYgPC0gZGF0YXNldCAlPiUKICB1bml0ZShkYXRhc2V0LCAKICAgICAgICBjb2w9bGFiZWwsYygiZGF0YXNldCIsInZhciIsImNvbmZpZy50cmFpbiIsImNvbmZpZy52YXJzIiwiVCIsImFsZyIpLAogICAgICAgIHNlcCA9ICItIixyZW1vdmU9RkFMU0UpICU+JSAKICBzZWxlY3QobGFiZWwsZGF0YXNldCx2YXIsY29uZmlnLnRyYWluLGNvbmZpZy52YXJzLFQsYWxnLEZBUixTZW5zaXRpdml0eSxTcGVjaWZpY2l0eSxBY2N1cmFjeSxLYXBwYSxGMSxQcmVjaXNpb24pIAoKYGBgCgpWYXJpYWJpbGlkYWQgZGUgbGEgU2Vuc2l0aXZpdHkgcG9yIGNhZGEgZXN0YWNpw7NuCgpgYGB7cn0KcCA8LWdncGxvdChhZXMoeSA9IFNlbnNpdGl2aXR5LCB4ID0gdmFyLCBmaWxsID0gY29uZmlnLnZhcnMpLCBkYXRhID0gZGYpICsgCiAgZ2VvbV9ib3hwbG90KCkgICsgY29vcmRfZmxpcCgpCnByaW50KHApCmBgYApMb3MgbW9kZWxvcyBjb24gY29uZmlnLWFsbCBwcmVzZW50YXJvbiBtZWpvciBkZXNlbXBlw7FvIGVuIHNlbnNpdGl2aXR5LCBleGNlcHRvIHBhcmEgVHVudXlhbiB5IGxhIExsYXZlIChlc3RlIGVzdMOhIHBhcmVqbykKClZhcmlhYmlsaWRhZCBkZSBsYSBTZW5zaXRpdml0eSBwb3Igc2Vnw7puIGRhdGFzZXQgdXNhZG8KCmBgYHtyfQpwIDwtZ2dwbG90KGFlcyh5ID0gU2Vuc2l0aXZpdHksIHggPSBkYXRhc2V0LCBmaWxsID0gY29uZmlnLnZhcnMpLCBkYXRhID0gZGYpICsgCiAgZ2VvbV9ib3hwbG90KCkgICsgY29vcmRfZmxpcCgpCnByaW50KHApCmBgYAoKVmFyaWFiaWxpZGFkIGRlIEYxIHBvciBzZWfDum4gZGF0YXNldCB1c2FkbwoKYGBge3J9CnAgPC1nZ3Bsb3QoYWVzKHkgPSBGMSwgeCA9IGRhdGFzZXQsIGZpbGwgPSBjb25maWcudmFycyksIGRhdGEgPSBkZikgKyAKICBnZW9tX2JveHBsb3QoKSAgKyBjb29yZF9mbGlwKCkKcHJpbnQocCkKYGBgCgoKTm8gc2UgdmlzdWFsaXphbiBncmFuIHZhcmlhYmlsaWRhZCBlbnRyZSBsb3MgZGF0YXNldHMgdXNhZG9zIGVudHJlIEYxLCBTZW5zaXRpdml0eSB5IHByZWNpc2lvbgoKVmFyaWFiaWxpZGFkIGRlIFNlbnNpdGl2aXR5IHNlZ8O6biBkYXRhc2V0IHVzYWRvCgpgYGB7cn0KcCA8LWdncGxvdChhZXMoeSA9IFNlbnNpdGl2aXR5LCB4ID0gZGF0YXNldCwgZmlsbCA9IGNvbmZpZy50cmFpbiksIGRhdGEgPSBkZikgKyAKICBnZW9tX2JveHBsb3QoKSAgKyBjb29yZF9mbGlwKCkKcHJpbnQocCkKYGBgClNNT1RFIGF1bWVudGEgc2Vuc2l0aXZpdHkgZW4gYW1ib3MgZGF0YXNldHMKClZhcmlhYmlsaWRhZCBkZSBGMSBwb3IgY2FkYSBlc3RhY2nDs24KYGBge3J9CgpwIDwtZ2dwbG90KGFlcyh5ID0gRjEsIHggPSB2YXIsIGZpbGwgPSBjb25maWcudmFycyksIGRhdGEgPSBkZikgKyBnZW9tX2JveHBsb3QoKSArIGNvb3JkX2ZsaXAoKQpwcmludChwKQoKYGBgCkxvcyBtb2RlbG9zIGNvbiBjb25maWctYWxsIHByZXNlbnRhcm9uIG1lam9yIGRlc2VtcGXDsW8gZW4gdGVybWlub3MgbWVkaW9zIGRlIEYxLCBleGNlcHRvIHBhcmEgVHVudXlhbiB5IGxhIExsYXZlIChlc3RlIGVzdMOhIHBhcmVqbykKCgpWYXJpYWJpbGlkYWQgZGUgcHJlY2lzacOzbiBwb3IgY2FkYSBlc3RhY2nDs24KYGBge3J9CnAgPC1nZ3Bsb3QoYWVzKHkgPSBQcmVjaXNpb24gLCB4ID0gdmFyLCBmaWxsID0gY29uZmlnLnZhcnMpLCBkYXRhID0gZGYpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkKcHJpbnQocCkKCmBgYAoKRW4gdMOpcm1pbm9zIG1lZGlvcyBsYSBwcmVjaXNpb24gbWVqb3JhIGVuIGxhcyBQYXJlZGVzIHkgSnVuw61uIGNvbiBjb25maWctbG9jYWwuIExhIGxsYXZlLCB0dW51eWFuIHkgYWd1YSBhbWFyZ2EgInBhcmVqb3MiCgpTZWfDum4gY3XDoW50b3MgZMOtYXMgYW50ZXJpb3JlcyBkZSBpbmZvcm1hY2nDs24gc3VtYW1vcy4KYGBge3J9CgojJyAjIyBTZWfDum4gY3VhbnRvcyBkw61hcyBhbnRlcmlvcmVzIGRlIGluZm9ybWFjacOzbiBzdW1hbW9zLgojJyAKcCA8LWdncGxvdChhZXMoeSA9IFByZWNpc2lvbiAsIHggPSB2YXIsIGZpbGwgPSBhcy5mYWN0b3IoVCkpLCBkYXRhID0gZGYpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkKcHJpbnQocCkKCnAgPC1nZ3Bsb3QoYWVzKHkgPSBTZW5zaXRpdml0eSAsIHggPSB2YXIsIGZpbGwgPSBhcy5mYWN0b3IoVCkpLCBkYXRhID0gZGYpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkKcHJpbnQocCkKCnAgPC1nZ3Bsb3QoYWVzKHkgPSBGMSAsIHggPSB2YXIsIGZpbGwgPSBhcy5mYWN0b3IoVCkpLCBkYXRhID0gZGYpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkgKyBsYWJzKGZpbGw9IlQiKQpwcmludChwKQpgYGAKCkVuIGdlbmVyYWwsIGNvbiB1biBkw61hIG8gZG9zIHNlIG9idGllbmVuIGJ1ZW5vcyByZXN1bHRhZG9zIGVuIHTDqXJtaW5vcyBkZSBwcmVjaXNpb24scmVjYWxsIHkgRjEsIMK/cG9kcsOtYSBwcmVzY2luZGlyc2UgZGUgYWdyZWdhciBtw6FzPyAKSGF5IHZhcmlvcyBjYXNvcyBwYXJ0aWN1bGFyZXMsIHBvciBlamVtcGxvIEFndWEgYW1hcmdhIGVuIHNlbnNpdGl2aXR5IHBhcmEgVD0xLCBzdSBtZWRpYSBlcyBtw6FzIGJhamEgcmVzcGVjdG8gYWwgVD0yLCBzaW4gZW1iYXJnbyBlbCBtw6F4aW1vIGRlIHNlbnNpdGl2aXR5IGVzIGNvbiBUPTEuCgoKQ29tcGFyYWNpw7NuIG5vcm1hbCB2cyBzbW90ZSBwb3IgZXN0YWNpw7NuIHBhcmEgbcOpdHJpY2FzIHNlbnNpdGl2aXR5IHkgcHJlY2lzaW9uCgpgYGB7cn0KcCA8LWdncGxvdChhZXMoeSA9IFByZWNpc2lvbiAsIHggPSB2YXIsIGZpbGwgPSBjb25maWcudHJhaW4pLCBkYXRhID0gZGYpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkKcHJpbnQocCkKCnAgPC1nZ3Bsb3QoYWVzKHkgPSBTZW5zaXRpdml0eSAsIHggPSB2YXIsIGZpbGwgPSBjb25maWcudHJhaW4pLCBkYXRhID0gZGYpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkKcHJpbnQocCkKCnAgPC1nZ3Bsb3QoYWVzKHkgPSBGMSAsIHggPSB2YXIsIGZpbGwgPSBjb25maWcudHJhaW4pLCBkYXRhID0gZGYpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkKcHJpbnQocCkKCmBgYApMYSBjb25maWd1cmFjacOzbiBTTW9URSBhdW1lbnRhIGxhIHNlbnNpdGl2aXR5L0YxIGVuIGRldHJpbWVudG8gZGUgbGEgcHJlY2lzaW9uLiBFcyB1bmEgY29uc2VjdWVuY2lhIGVzcGVyYWJsZS4KCgpDb21wb3J0YW1pZW50byBnZW5lcmFsIGRlIGxvY2FsIHZzIGFsbCBkZSBsb3MgYWxnb3JpdG1vcyBwb3IgY2FkYSBlc3RhY2nDs24gc2Vnw7puIG3DqXRyaWNhCgpgYGB7cn0KCmZvcihzIGluIHN0YXRpb25zKXsKICAKICBkZjEgPC0gZGYgJT4lIGZpbHRlciggdmFyID09IHMgKSAKICAKICBwIDwtZ2dwbG90KGFlcyh5ID0gRjEgLCB4ID0gYWxnLCBmaWxsID0gY29uZmlnLnZhcnMpLCBkYXRhID0gZGYxKSArIAogICAgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHRpdGxlPXBhc3RlKCJFc3RhY2nDs24iLHMsc2VwPSIgICIpKQogIHByaW50KHApCiAgcCA8LWdncGxvdChhZXMoeSA9IFNlbnNpdGl2aXR5ICwgeCA9IGFsZywgZmlsbCA9IGNvbmZpZy52YXJzKSwgZGF0YSA9IGRmMSkgKyAKICAgIGdlb21fYm94cGxvdCgpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZT1wYXN0ZSgiRXN0YWNpw7NuIixzLHNlcD0iICAiKSkKICBwcmludChwKQogIHAgPC1nZ3Bsb3QoYWVzKHkgPSBQcmVjaXNpb24gLCB4ID0gYWxnLCBmaWxsID0gY29uZmlnLnZhcnMpLCBkYXRhID0gZGYxKSArIAogICAgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHRpdGxlPXBhc3RlKCJFc3RhY2nDs24iLHMsc2VwPSIgICIpKQogIHByaW50KHApCgp9CgpgYGAKCmBgYHtyfQpsaWJyYXJ5KHJlc2hhcGUpCmRmMyA8LSAgZGYgJT4lIGZpbHRlciggZGF0YXNldCA9PSAiZGFjYyIpICU+JSBzZWxlY3QoLW9uZV9vZihjKCJGQVIiKSkpCgojICBtZWx0KGRmMyxpZC52YXJzPSJsYWJlbCIpCmRmNCA8LSBtZWx0KGFzLmRhdGEuZnJhbWUoZGYzKSwKICAgICAgICAgICAgaWQudmFycyA9KGMoImxhYmVsIiwiZGF0YXNldCIsInZhciIsImNvbmZpZy50cmFpbiIsImNvbmZpZy52YXJzIiwiVCIsImFsZyIpKSwKICAgICAgICAgICAgbWVhc3VyZS52YXJzID0gbWV0cmljc1stMV0pCgpwIDwtZ2dwbG90KGFlcyh5ID0gdmFsdWUgLCB4ID0gdmFyLCBmaWxsID0gdmFyaWFibGUpLCBkYXRhID0gZGY0KSArIAogIGdlb21fYm94cGxvdCgpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZT0iVmFyaWFiaWxpZGFkIGRlIGxhcyBtw6l0cmljYXMgcG9yIGxhcyBlc3RhY2lvbmVzIikKcHJpbnQocCkKCmRmNCA8LSBkZiAlPiUgZmlsdGVyKCBkYXRhc2V0ID09ICJkYWNjIikgICAKZGY0IDwtIG1lbHQoYXMuZGF0YS5mcmFtZShkZjQpLAogICAgICAgICAgICBpZC52YXJzID0oYygibGFiZWwiLCJkYXRhc2V0IiwidmFyIiwiY29uZmlnLnRyYWluIiwiY29uZmlnLnZhcnMiLCJUIiwiYWxnIikpLAogICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSBtZXRyaWNzKQoKZm9yKGEgaW4gYWxnb3JpdG1vcykKewogIGZvcihtIGluIG1ldHJpY3MpCiAgewogICAgZGY1IDwtIGRmNCAlPiUgZmlsdGVyKCBhbGcgPT0gYSAmIHZhcmlhYmxlID09IG0pIAogICAgcCA8LWdncGxvdChhZXMoeSA9IHZhbHVlICwgeCA9IHZhciwgZmlsbCA9IGNvbmZpZy52YXJzKSwgZGF0YSA9IGRmNSkgKyAKICAgICAgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHRpdGxlPXBhc3RlKCJDb21wb3J0YW1pZW50byBkZSAiLG0sIiBlbiBtb2RlbG8gIixhLHNlcD0iIikpCiAgICBwcmludChwKQogIH0KfQpgYGAKClJhbmRvbSBmb3Jlc3QKCiogRW4gYWd1YSAgYW1hcmdhIHkgVHVudXlhbiwgZWwgRkFSIGVzIG1lbm9yIGVuIG1vZGVsb3MgY29uZmlnIGFsbC4gVGFtYmnDqW4gcGFyYSBlc3RhcyBlc3RhY2lvbmVzIGNvbmZpZyBhbGwgdGllbmUgbWF5b3JlcyB2YWxvcmVzIGRlIHNwZWNpZmljaXR5IHkgcHJlY2lzaW9uIHF1ZSBjb25maWcgbG9jYWwsIGxvIGNvbnRyYXJpbyBwYXJhIGxhcyByZXN0YW50ZXMuIExvIGNvbnRyYXJpbyBwYXJhIGVsIHJlc3RvIGRlIGxhcyBlc3RhY2lvbmVzIChnYW5hIGxvY2FsKQoqIExvcyBtZWpvcmVzIG1vZGVsb3MgZW4gdGVybWlub3MgZGUgc2Vuc2l0aXZpdHkvcmVjYWxsIHNvbiBjb25maWcgYWxsCiogVmFsb3JlcyBkZSBhY2N1cmFjeSBlbnRyZSAoYXByb3hpbWFkbykgMC44OSB5IDAuOTQKKiBWYWxvciBkZSBLYXBwYSBhdW1lbnRhIChoYXkgbWF5b3JlcyB2YWxvcmVzKSBjb24gY29uZmlnIGFsbCwgYW5hbGl6YW5kbyBsYXMgbWVkaWFzCgpgYGB7cn0KZGYzIDwtIGRmICU+JQogICAgICBzZWxlY3QobGFiZWwsZGF0YXNldCx2YXIsY29uZmlnLnRyYWluLGNvbmZpZy52YXJzLFQsYWxnLFNlbnNpdGl2aXR5LFNwZWNpZmljaXR5LEFjY3VyYWN5LEthcHBhLEYxLFByZWNpc2lvbikgJT4lCiAgICAgIGZpbHRlciggdmFyID09IHN0YXRpb25zWzFdICAmIGFsZz09YWxnb3JpdG1vc1sxXSkgIyAmIFQgPT0gMSAmIGRhdGFzZXQgPT0gREFUQVNFVCAmCiAgICAKZGYzCmBgYAoKYGBge3J9Cgpmb3IocyBpbiBzdGF0aW9ucykKewogIGZvcihhIGluIGFsZ29yaXRtb3MpCiAgewogICAgCiAgICBkZjMgPC0gZGYgJT4lCiAgICAgIHNlbGVjdChsYWJlbCxkYXRhc2V0LHZhcixjb25maWcudHJhaW4sY29uZmlnLnZhcnMsVCxhbGcsU2Vuc2l0aXZpdHksU3BlY2lmaWNpdHksQWNjdXJhY3ksS2FwcGEsRjEsUHJlY2lzaW9uKSAlPiUKICAgICAgZmlsdGVyKCB2YXIgPT0gcyAgJiBhbGc9PWEgJiBUID09IDEgJiBkYXRhc2V0ID09ICJkYWNjIikgIyAmICAjIDwtIEFRVUkgRklMVFJPCiAgICAKICAgIGRmNyA8LSBtZWx0KGFzLmRhdGEuZnJhbWUoZGYzKSxpZD0oYygibGFiZWwiLCJkYXRhc2V0IiwidmFyIiwiY29uZmlnLnRyYWluIiwiY29uZmlnLnZhcnMiLCJUIiwiYWxnIikpKQogICAgCiAgICAKICAgcDwtICBnZ3Bsb3QoZGF0YT1kZjcsIGFlcyh4PSByZW9yZGVyKGxhYmVsLHZhbHVlKSwgeT12YWx1ZSwgZmlsbD12YXJpYWJsZSkpICsKICAgICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpKwogICAgICBjb29yZF9mbGlwKCkrCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKwogICAgICBsYWJzKHggPSBhLCB5ID0gIm1ldHJpY2FzIiwgdGl0bGU9cGFzdGUoImNvbXBhcmFjaW9uIixhLHMsc2VwPSItIikpCiAgIHByaW50KHApCiAgICAKICAgICMgZm9yKG0gaW4gbWV0cmljcyl7CiAgICAjICAgcGxvdF9yYW5raW5nX2FsZyhkZjMsbSxzLGEpCiAgICAjICAgCiAgICAjIH0KICB9Cn0gICAgCmBgYAoKRmlsdHJhbW9zIHBhcmEgcXVlZGFybm9zIGNvbiBsb3MgbW9kZWxvcyBjdXlhIFNlbnNpdGl2aXR5IHkgUHJlY2lzaW9uIGhheWEgc2lkbyBtYXlvciBvIGlndWFsIGEgMC43LCBvYnNlcnZhciBlbCBjb21wb3J0YW1pZW50byBwb3IgZXN0YWNpw7NuIHkgYWxnb3JpdG1vCgpMbyBzaWd1aWVudGUgZGEgZXJyb3I6IHZlcmlmaWNhci9jaGVxdWVhcgpgYGB7cn0KZm9yKHMgaW4gc3RhdGlvbnMpCnsKICBmb3IoYSBpbiBhbGdvcml0bW9zKQogIHsKICAgIAogICAgZGYzIDwtIGRmICU+JQogICAgICAgICAgc2VsZWN0KGxhYmVsLGRhdGFzZXQsdmFyLGNvbmZpZy50cmFpbixjb25maWcudmFycyxULGFsZyxTZW5zaXRpdml0eSxQcmVjaXNpb24sRjEpICU+JSAjLFNwZWNpZmljaXR5LEFjY3VyYWN5LEthcHBhLEYxCiAgICAgICAgICBmaWx0ZXIoIFNlbnNpdGl2aXR5ID4gMC43ICwgUHJlY2lzaW9uID4gMC43ICwgdmFyPT1zLCBkYXRhc2V0ID09ICJkYWNjIixhbGc9PWEgKSAjICYgVCA9PSAxICYgZGF0YXNldCA9PSBEQVRBU0VUICYKICAgICAgICAKICAgIAogICAgZGY3IDwtIG1lbHQoZGF0YS5mcmFtZShkZjMpLGlkPShjKCJsYWJlbCIsImRhdGFzZXQiLCJ2YXIiLCJjb25maWcudHJhaW4iLCJjb25maWcudmFycyIsIlQiLCJhbGciKSkpCiAgICAgICAgCiAgICAgIAogICAgcDwtICBnZ3Bsb3QoZGF0YT1kZjcsIGFlcyh4PSBsYWJlbCwgeT12YWx1ZSwgZmlsbD12YXJpYWJsZSkpICsKICAgICAgICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iixwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgpKSsKICAgICAgICAgIGNvb3JkX2ZsaXAoKSsKICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKwogICAgICAgICAgbGFicyh0aXRsZT1wYXN0ZSgiRXN0YWNpb24gIixzLCIgYWxnb3JpdG1vICIsIGEpKQogICAgICAgcHJpbnQocCkKICB9Cn0gIApgYGAKYGBge3J9CnN0YXRpb25zCmBgYAoKClNpIHF1ZXJlbW9zIGNvcnJlciBsbyBhbnRlcmlvciBmdWVyYSBkZWwgZm9yIG8gYnVjbGUKYGBge3J9CmRmMyA8LSBkZiAlPiUKICAgICAgc2VsZWN0KGxhYmVsLGRhdGFzZXQsdmFyLGNvbmZpZy50cmFpbixjb25maWcudmFycyxULGFsZyxTZW5zaXRpdml0eSxQcmVjaXNpb24pICU+JSAjLFNwZWNpZmljaXR5LEFjY3VyYWN5LEthcHBhLEYxCiAgICAgIGZpbHRlciggU2Vuc2l0aXZpdHkgPiAwLjcgLCBQcmVjaXNpb24gPiAwLjcgLCB2YXI9PXN0YXRpb25zWzFdLCBkYXRhc2V0ID09ICJkYWNjIixhbGc9PSJyZiIgKSAjICYgVCA9PSAxICYgZGF0YXNldCA9PSBEQVRBU0VUICYKICAgIAoKZGY3IDwtIG1lbHQoYXMuZGF0YS5mcmFtZShkZjMpLGlkPShjKCJsYWJlbCIsImRhdGFzZXQiLCJ2YXIiLCJjb25maWcudHJhaW4iLCJjb25maWcudmFycyIsIlQiLCJhbGciKSkpCiAgICAKICAgIAogICAgCmBgYApgYGB7cn0KcDwtICBnZ3Bsb3QoZGF0YT1kZjcsIGFlcyh4PSBsYWJlbCwgeT12YWx1ZSwgZmlsbD12YXJpYWJsZSkpICsKICAgICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpKwogICAgICBjb29yZF9mbGlwKCkrCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKwogICAgICBsYWJzKHRpdGxlPSIiKQogICBwcmludChwKQpgYGAKCg==