library(readr)
library(ggplot2)
library(gridExtra)
library(tidyverse)
── Attaching packages ────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ tibble  1.4.2     ✔ dplyr   0.7.6
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ purrr   0.2.5     ✔ forcats 0.3.0
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::combine() masks gridExtra::combine()
✖ dplyr::filter()  masks stats::filter()
✖ dplyr::lag()     masks stats::lag()
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

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==