EXPLORATION

Loading Training Dataset

vines_dataset<-readr::read_csv("datasets/vine_train_newnames.csv", col_types =cols())
#vines_dataset

Training dataset dimension 58 x 77

Removing unnecesary data

#removed_numeric_var<-c("ubi_x","ubi_y","ID")
removed_numeric_var<-c("ID")

vines_dataset <- vines_dataset %>% select(-removed_numeric_var)

Basic Distribution Information

Categorical Variables

vines_dataset_factors<-vines_dataset %>% select_if(~class(.) == 'character')
names(vines_dataset_factors) %>% as.data.frame()
NA
 skimr::skim(vines_dataset_factors %>% select(-Calidad)) %>% knitr::kable() %>% kable_styling(font_size = 9)
skim_type skim_variable n_missing complete_rate character.min character.max character.empty character.n_unique character.whitespace
character ubi_Finca 0 1.000000 5 22 0 29 0
character ubi_Zona 0 1.000000 5 12 0 3 0
character ubi_Distrito 0 1.000000 6 16 0 18 0
character eco_Estacion datos clima 0 1.000000 8 11 0 6 0
character suelo_Textura 3 0.961039 6 16 0 6 0
character eco_Winkler historico 0 1.000000 2 3 0 2 0

Numerical Variables

vines_dataset_numeric<-vines_dataset %>% select_if(~class(.) == 'numeric')
names(vines_dataset_numeric) %>% as.data.frame()
skimr::skim(vines_dataset_numeric) %>% knitr::kable() %>% kable_styling(font_size = 9)
skim_type skim_variable n_missing complete_rate numeric.mean numeric.sd numeric.p0 numeric.p25 numeric.p50 numeric.p75 numeric.p100 numeric.hist
numeric uva_L hollejo 0 1.0000000 2.874569e+01 5.459099e+00 1.821657e+01 2.550730e+01 2.807872e+01 3.218875e+01 4.261734e+01 ▅▇▇▅▁
numeric uva_a hollejo 0 1.0000000 6.289838e+01 4.801958e+00 5.251411e+01 6.018530e+01 6.263834e+01 6.652651e+01 7.421597e+01 ▃▅▇▆▂
numeric uva_b hollejo 0 1.0000000 4.555514e+01 6.056351e+00 3.140652e+01 4.208936e+01 4.653096e+01 5.020659e+01 5.634098e+01 ▂▃▆▇▃
numeric uva_h hollejo 0 1.0000000 3.579853e+01 2.875735e+00 2.421982e+01 3.424246e+01 3.669681e+01 3.772302e+01 3.934650e+01 ▁▁▂▃▇
numeric uva_C hollejo 0 1.0000000 7.776013e+01 6.661614e+00 6.121770e+01 7.405367e+01 7.862206e+01 8.297825e+01 8.889917e+01 ▂▃▆▇▆
numeric uva_FT(mg/g hollejo) 0 1.0000000 6.815455e+00 1.973878e+00 2.730000e+00 5.420000e+00 7.010000e+00 8.170000e+00 1.315000e+01 ▂▇▇▂▁
numeric uva_FT(mg/g baya) 0 1.0000000 5.184416e-01 1.816957e-01 2.300000e-01 4.000000e-01 5.200000e-01 6.400000e-01 1.200000e+00 ▇▇▅▁▁
numeric uva_FT(mg/baya) 0 1.0000000 1.138442e+00 3.360023e-01 4.300000e-01 9.300000e-01 1.160000e+00 1.350000e+00 2.090000e+00 ▂▆▇▃▁
numeric uva_TAN(mg/g hollejo) 0 1.0000000 1.475195e+00 4.707262e-01 4.200000e-01 1.170000e+00 1.450000e+00 1.850000e+00 2.580000e+00 ▃▇▇▆▂
numeric uva_TAN(mg/g baya) 0 1.0000000 1.109091e-01 3.970890e-02 4.000000e-02 8.000000e-02 1.000000e-01 1.300000e-01 2.400000e-01 ▃▇▅▁▁
numeric uva_TAN(mg/baya) 0 1.0000000 2.463636e-01 7.803350e-02 6.000000e-02 1.900000e-01 2.400000e-01 3.000000e-01 4.300000e-01 ▂▅▇▅▂
numeric uva_ANT(mg/g hollejo) 0 1.0000000 3.022987e+00 1.508207e+00 5.300000e-01 1.610000e+00 3.090000e+00 4.390000e+00 5.810000e+00 ▇▆▇▇▅
numeric uva_ANT(mg/g baya) 0 1.0000000 2.301299e-01 1.188928e-01 3.000000e-02 1.200000e-01 2.200000e-01 3.400000e-01 4.900000e-01 ▇▆▆▇▂
numeric uva_ANT(mg/baya) 0 1.0000000 5.041558e-01 2.554993e-01 9.000000e-02 2.500000e-01 5.100000e-01 7.100000e-01 9.400000e-01 ▇▅▇▅▆
numeric uva_Acti antirr hollejo 0 1.0000000 3.902727e-01 1.546606e-01 1.810000e-01 2.520000e-01 3.410000e-01 4.970000e-01 7.610000e-01 ▇▃▅▃▂
numeric ubi_x 0 1.0000000 4.944155e+05 1.516253e+04 4.722227e+05 4.824171e+05 4.895404e+05 5.108191e+05 5.211357e+05 ▆▇▅▂▆
numeric ubi_y 0 1.0000000 6.321765e+06 6.935472e+04 6.259165e+06 6.270718e+06 6.292645e+06 6.330100e+06 6.482843e+06 ▇▃▁▁▂
numeric planta_Nro de Racimos 1 0.9870130 3.216886e+01 1.252138e+01 8.750000e+00 2.475000e+01 3.237500e+01 3.993750e+01 6.066667e+01 ▅▇▇▅▂
numeric planta_Rendimiento 0 1.0000000 2.745681e+00 1.299585e+00 7.000000e-01 1.791250e+00 2.703750e+00 3.798333e+00 6.385000e+00 ▇▇▇▂▁
numeric planta_Peso racimo 1 0.9870130 8.598426e+01 2.326719e+01 4.317500e+01 6.995833e+01 8.418750e+01 9.958125e+01 1.489500e+02 ▃▇▇▂▁
numeric planta_Peso de poda 0 1.0000000 5.560946e-01 3.414147e-01 9.250000e-02 3.500000e-01 4.450000e-01 6.400000e-01 1.775000e+00 ▇▇▂▁▁
numeric planta_Long Cordon 0 1.0000000 9.831293e-01 2.207139e-01 4.525000e-01 8.500000e-01 9.666667e-01 1.110000e+00 1.570000e+00 ▂▃▇▃▁
numeric planta_Ravaz 0 1.0000000 6.870817e+00 4.079626e+00 1.199712e+00 4.444548e+00 6.121651e+00 8.740419e+00 2.237703e+01 ▇▇▃▁▁
numeric planta_Peso de baya 0 1.0000000 1.876786e+00 1.078832e+00 9.943200e-01 1.582320e+00 1.769080e+00 1.981160e+00 1.084352e+01 ▇▁▁▁▁
numeric planta_Bayas/racimo 1 0.9870130 4.936153e+01 1.610240e+01 9.500000e+00 3.750000e+01 4.875000e+01 5.668750e+01 9.775000e+01 ▁▇▇▂▁
numeric uva_Brix 0 1.0000000 2.495065e+01 1.113981e+00 2.270000e+01 2.420000e+01 2.480000e+01 2.560000e+01 2.830000e+01 ▂▇▅▁▁
numeric uva_pH mosto 0 1.0000000 3.650494e+00 2.499957e-01 3.291000e+00 3.550000e+00 3.620000e+00 3.678000e+00 5.581000e+00 ▇▁▁▁▁
numeric uva_Acidez Mosto 0 1.0000000 3.797727e+00 9.571079e-01 2.250000e+00 3.375000e+00 3.750000e+00 4.125000e+00 9.875000e+00 ▇▆▁▁▁
numeric ubi_Altura s.n.m. 0 1.0000000 1.115339e+03 1.676664e+02 8.050000e+02 9.673831e+02 1.094949e+03 1.237089e+03 1.405014e+03 ▂▇▇▂▅
numeric planta_Aspecto 0 1.0000000 1.600122e+02 8.213981e+01 6.343495e+01 9.567704e+01 1.233805e+02 2.045968e+02 3.482716e+02 ▇▂▃▁▂
numeric suelo_Pendiente 0 1.0000000 1.684270e+00 9.458563e-01 6.482576e-01 8.594052e-01 1.314315e+00 2.293767e+00 3.923592e+00 ▇▃▂▂▁
numeric suelo_Indice fondo Valle 0 1.0000000 2.565787e+00 1.444410e+00 0.000000e+00 2.000000e+00 2.000000e+00 3.000000e+00 6.000000e+00 ▅▇▇▁▃
numeric planta_ndvi 0 1.0000000 4.405046e-01 1.189833e-01 1.998800e-01 3.588160e-01 4.481651e-01 5.113118e-01 6.595923e-01 ▃▃▇▇▃
numeric suelo_Vol. Sedimentacion (%) 3 0.9610390 8.367568e+01 1.162909e+01 6.800000e+01 7.600000e+01 8.000000e+01 8.800000e+01 1.160000e+02 ▇▃▅▂▁
numeric eco_GDA historico 0 1.0000000 1.826931e+03 1.415219e+02 1.680219e+03 1.714931e+03 1.788594e+03 1.837215e+03 2.180023e+03 ▇▆▂▁▂
numeric eco_Temp. Mínima °C Temp 2020 0 1.0000000 1.143646e+01 1.306108e+00 1.019000e+01 1.019000e+01 1.125000e+01 1.187000e+01 1.478638e+01 ▇▆▁▃▁
numeric eco_Temp. Media °C Temp 2020 0 1.0000000 1.913327e+01 9.194734e-01 1.819000e+01 1.857000e+01 1.882000e+01 1.922000e+01 2.097658e+01 ▇▇▁▁▃
numeric eco_Temp. Máxima °C Temp 2020 0 1.0000000 2.670542e+01 1.390381e+00 2.486000e+01 2.558000e+01 2.656103e+01 2.704000e+01 2.926187e+01 ▇▃▇▁▃
numeric eco_Amplitud Térmica °C Temp 2020 0 1.0000000 1.512831e+01 1.500230e+00 1.177465e+01 1.434000e+01 1.468000e+01 1.685000e+01 1.685000e+01 ▁▃▇▃▇
numeric eco_Precipitaciones (mm) Temp 2020 16 0.7922078 2.291230e+02 9.511294e+01 8.500000e+01 1.780000e+02 2.195000e+02 3.350000e+02 3.350000e+02 ▃▆▃▁▇
numeric eco_Grados Días Acum. Temp 2020 0 1.0000000 1.969156e+03 1.905096e+02 1.777550e+03 1.851820e+03 1.900400e+03 1.992390e+03 2.349915e+03 ▇▇▁▁▃
numeric eco_Evapotranspiración mm Temp 2020 13 0.8311688 9.567577e+02 6.524567e+01 8.660000e+02 8.918000e+02 9.766000e+02 9.807800e+02 1.162590e+03 ▃▇▁▁▁
numeric eco_I. de Fresco Nocturno Temp 2020 0 1.0000000 1.286563e+01 1.497985e+00 1.149000e+01 1.149000e+01 1.254000e+01 1.348000e+01 1.667097e+01 ▇▆▁▃▁
numeric eco_Temp. Media Marzo °C Temp 2020 0 1.0000000 2.033224e+01 1.034615e+00 1.924000e+01 1.981000e+01 1.992000e+01 2.039000e+01 2.240323e+01 ▇▇▁▁▃
numeric eco_Ampl. Térmica Marzo °C Temp 2020 0 1.0000000 1.533956e+01 1.535967e+00 1.138710e+01 1.431000e+01 1.452000e+01 1.722000e+01 1.722000e+01 ▁▁▇▃▅
numeric eco_Horas de Frío Temp 2020 29 0.6233766 1.450938e+03 8.676488e+01 1.313000e+03 1.313000e+03 1.494000e+03 1.516000e+03 1.516000e+03 ▅▁▁▃▇
numeric eco_Días con Temp.>35°C Temp 2020 0 1.0000000 6.792208e+00 7.824283e+00 0.000000e+00 1.000000e+00 6.000000e+00 7.000000e+00 2.300000e+01 ▇▇▁▁▃
numeric eco_Días con Heladas Temp 2020 0 1.0000000 8.922078e+00 5.609593e+00 0.000000e+00 7.000000e+00 7.000000e+00 1.300000e+01 1.700000e+01 ▅▁▇▇▃
numeric eco_Radiacíon W/m2 Temp 2020 0 1.0000000 2.554018e+02 2.479439e+01 2.263900e+02 2.333100e+02 2.426128e+02 2.887100e+02 2.887100e+02 ▇▃▂▁▇
numeric eco_Velocidad Media Viento km/h Temp 2020 0 1.0000000 3.736633e+00 1.074010e+00 1.976061e+00 3.360000e+00 3.390000e+00 4.580000e+00 5.420649e+00 ▃▁▇▅▃
numeric eco_Humedad Relativa Media % Temp 2020 0 1.0000000 5.023523e+01 7.963722e+00 3.287000e+01 4.948000e+01 5.084000e+01 5.246000e+01 6.051096e+01 ▂▁▁▇▂

Class Distribution

vines_dataset %>% group_by(Calidad) %>% summarise(total=n()) %>%
  ggplot()+
  geom_col(aes(x=Calidad,y=total,fill=Calidad))+
  ggdark::dark_theme_bw()

ANALISYS NUMERICAL VARIABLES

Dealing Missing Data

res<-VIM::aggr(vines_dataset_numeric, combined = TRUE, 
          numbers = TRUE, 
          sortCombs = TRUE, 
          sortVars = TRUE, 
          labels=names(vines_dataset_numeric),
          cex.axis=.4, 
          varheight = FALSE,
          cex.numbers=0.8,
          cex.lab=0.8,
          prop = FALSE)

 Variables sorted by number of missings: 

Missing values imputation

vines_dataset_numeric_imputed <- kNN(vines_dataset_numeric)
vines_dataset_numeric<-vines_dataset_numeric_imputed %>% select(-ends_with("_imp"))
#vines_dataset_numeric %>% select(-NAME)

Correlation Matrix

Matrix considering all variables


library(d3heatmap)
vines_dataset_numeric_cor_matrix<-cor(vines_dataset_numeric ,method="spearman")
#heatmap(postop_data_cor_matrix)
d3heatmap(vines_dataset_numeric_cor_matrix ,colors = "Blues",cexRow = 0.8, cexCol = 0.8)

PCA Elipses for clases

library(FactoMineR)
#vines_dataset_numeric %>% select(-highlyCorrelated_var) %>% tibble::add_column(Calidad=vines_dataset$Calidad)
res_pca = PCA(vines_dataset_numeric  %>% 
                tibble::add_column(Calidad=vines_dataset$Calidad)
              , scale.unit=TRUE, 
              ncp=6, 
              graph=F,
              quali.sup=52, #colid for Calidad
              )

plot(res_pca,choix="ind",habillage=52)
par(mfcol=c(1,2))

plot(res_pca,choix="var",habillage="none",invisible = "ind") # para las variables

plotellipses(res_pca, invisible="ind",xlim=c(-6,6),ylim=c(-6,6))

List of highly correlated variables

highlyCorrelated <- caret::findCorrelation(vines_dataset_numeric_cor_matrix, cutoff=0.9, verbose = F)
highlyCorrelated_var<-vines_dataset_numeric_cor_matrix[,highlyCorrelated] %>% as.data.frame() %>% names()
highlyCorrelated_var
 [1] "eco_Temp. Máxima °C Temp 2020"          "eco_Precipitaciones (mm) Temp 2020"    
 [3] "eco_Humedad Relativa Media % Temp 2020" "eco_Temp. Media °C Temp 2020"          
 [5] "eco_Grados Días Acum. Temp 2020"        "eco_Amplitud Térmica °C Temp 2020"     
 [7] "uva_L hollejo"                          "uva_a hollejo"                         
 [9] "uva_FT(mg/baya)"                        "uva_TAN(mg/baya)"                      
[11] "uva_b hollejo"                          "eco_Temp. Mínima °C Temp 2020"         
[13] "uva_ANT(mg/g baya)"                     "uva_ANT(mg/baya)"                      

Correlation Matrix with highly correlated removed

#highlyCorrelated_var %>% length()
#vines_dataset_numeric_cor_matrix[-highlyCorrelated,-highlyCorrelated]  %>% nrow()
d3heatmap(vines_dataset_numeric_cor_matrix[-highlyCorrelated,-highlyCorrelated] ,colors = "Blues",cexRow = 0.8, cexCol = 0.8)
NA

Convert class to numeric

#vines_dataset %>% mutate()
#vines_dataset$Calidad %>% as.numeric(as.factor(vines_dataset$Calidad))

FEATURE SELECTION

Bootstrap resampling

100 new datasets used for evaluating feature selection algorithms

vines_dataset_numeric_reduced<-vines_dataset_numeric  %>% 
  select(-highlyCorrelated_var)  %>% 
  #select(names(var_importance[1:11])) %>%
  tibble::add_column(Calidad=as.factor(vines_dataset$Calidad))

resamples<-rsample::bootstraps(vines_dataset_numeric_reduced,strata= Calidad,times = 100  )
num_of_feat<-20

BORUTA for feature selection

vines_dataset_bootstrap<-rsample::analysis(resamples$splits[[sample(1:100,1)]])
val_dataset  <- vines_dataset_bootstrap   %>% group_by(Calidad) %>% sample_n(2) %>% ungroup()
train_dataset <-dplyr::setdiff(vines_dataset_numeric_reduced,val_dataset)

var_importance_boruta<-Boruta(Calidad ~ . ,data=train_dataset)

var_importance_boruta<-attStats(var_importance_boruta) %>% filter(decision=='Confirmed') %>% select(meanImp) %>% arrange(desc(meanImp)) %>% top_n(num_of_feat)
Selecting by meanImp
var_importance_boruta<-var_importance_boruta %>% add_rownames('variable')
var_importance_boruta_final<-apply(var_importance_boruta$variable %>% t() ,2, function(x) stringr::str_replace_all(x, pattern="`",replacement = "")) %>% as.data.frame()
names(var_importance_boruta_final)<-"variable"
var_importance_boruta_final

CART Features selection ad-hoc using importance metric

var_importance<-c()
for (i in  1:100){
vines_dataset_bootstrap<-rsample::analysis(resamples$splits[[i]])
val_dataset  <- vines_dataset_bootstrap   %>% group_by(Calidad) %>% sample_n(2) %>% ungroup()
train_dataset <-dplyr::setdiff(vines_dataset_numeric_reduced,val_dataset)

tree<-rpart::rpart(Calidad~.,
                   data=train_dataset,
                   control = rpart.control(minsplit = 10),)
var_importance<- c(var_importance,tree$variable.importance[1:num_of_feat])
#rpart.plot(tree,type=1,
#           extra=101, box.palette="GnBu",
#           branch.lty=3, shadow.col="gray", nn=TRUE
#        )
}
var_importance_cart_final<-data.frame(variable=names(var_importance),value=var_importance) %>% group_by(variable) %>% summarise(n=n()) %>% arrange(desc(n)) %>% top_n(20)
Selecting by n
var_importance_cart_final %>% as.data.frame()

RANDOM FORESTS feature importance

vines_dataset_bootstrap<-rsample::analysis(resamples$splits[[sample(1:100,1)]])
val_dataset  <- vines_dataset_bootstrap   %>% group_by(Calidad) %>% sample_n(2) %>% ungroup()
train_dataset <-dplyr::setdiff(vines_dataset_numeric_reduced,val_dataset)

rf_names<-apply(names(train_dataset) %>% t() ,2, function(x) stringr::str_replace_all(x, pattern=" ",replacement = "_"))
rf_names<-apply(rf_names %>% t() ,2, function(x) stringr::str_replace_all(x, pattern="[/()%°>]+",replacement = "_"))

train_dataset_rf<-train_dataset
names(train_dataset_rf)<-rf_names

rf<-randomForest::randomForest(Calidad ~ .,data=train_dataset_rf,importance=TRUE)
var_importance_rf_final<-randomForest::importance(rf) %>% as.data.frame() %>% arrange(desc(MeanDecreaseGini)) %>% add_rownames('variable')  %>% top_n(num_of_feat) %>% select(variable)
Selecting by MeanDecreaseGini
var_importance_rf_final<- data.frame(variable=names(train_dataset[,which(names(train_dataset_rf) %in% var_importance_rf_final$variable)]) )
var_importance_rf_final

GLMNET Feature Selection

library(glmnet)
vines_dataset_bootstrap<-rsample::analysis(resamples$splits[[sample(1:100,1)]])
val_dataset  <- vines_dataset_bootstrap   %>% group_by(Calidad) %>% sample_n(2) %>% ungroup()
train_dataset <-dplyr::setdiff(vines_dataset_numeric_reduced,val_dataset)
glmfit<-cv.glmnet(x = train_dataset %>% select(-Calidad) %>% as.matrix(),
       y = train_dataset$Calidad %>% as.factor(),
       family = "multinomial")
one multinomial or binomial class has fewer than 8  observations; dangerous groundone multinomial or binomial class has fewer than 8  observations; dangerous groundone multinomial or binomial class has fewer than 8  observations; dangerous groundone multinomial or binomial class has fewer than 8  observations; dangerous groundone multinomial or binomial class has fewer than 8  observations; dangerous groundone multinomial or binomial class has fewer than 8  observations; dangerous groundone multinomial or binomial class has fewer than 8  observations; dangerous ground
#coef(glmfit,s = 'lambda.min')

'%ni%'<-Negate('%in%')
c<-coef(glmfit,s='lambda.min',exact=TRUE)


var_importance_glmnet<-purrr::map(c, function(x) {
  inds <- which(x != 0)
  variables <- row.names(x)[inds]
  variables <- variables[variables %ni% '(Intercept)']
}) 

#do.call(rbind,var_importance_glmnet) %>% as.data.frame()


var_importance_glmnet_final<-var_importance_glmnet %>% unlist() %>% unique() %>% as.data.frame()
names(var_importance_glmnet_final)<-"variable"

var_importance_glmnet_final

Selected variables per class

var_importance_glmnet
$A0
[1] "uva_ANT(mg/g hollejo)" "planta_Nro de Racimos" "eco_GDA historico"    

$A1
 [1] "uva_C hollejo"                  "uva_TAN(mg/g hollejo)"          "planta_Nro de Racimos"         
 [4] "planta_Rendimiento"             "planta_Long Cordon"             "uva_pH mosto"                  
 [7] "uva_Acidez Mosto"               "planta_Aspecto"                 "planta_ndvi"                   
[10] "suelo_Vol. Sedimentacion (%)"   "eco_Días con Heladas Temp 2020"

$A2
 [1] "uva_FT(mg/g baya)"              "uva_TAN(mg/g hollejo)"          "uva_Acti antirr hollejo"       
 [4] "planta_Peso de poda"            "planta_Long Cordon"             "planta_Ravaz"                  
 [7] "planta_Bayas/racimo"            "planta_ndvi"                    "eco_Horas de Frío Temp 2020"   
[10] "eco_Días con Heladas Temp 2020"

$A3
[1] "uva_TAN(mg/g hollejo)"                     "planta_Peso de baya"                      
[3] "planta_Bayas/racimo"                       "planta_Aspecto"                           
[5] "planta_ndvi"                               "suelo_Vol. Sedimentacion (%)"             
[7] "eco_Radiacíon W/m2 Temp 2020"              "eco_Velocidad Media Viento km/h Temp 2020"

$A5
[1] "uva_h hollejo"                "uva_ANT(mg/g hollejo)"        "planta_Rendimiento"          
[4] "planta_Peso de poda"          "ubi_Altura s.n.m."            "suelo_Indice fondo Valle"    
[7] "suelo_Vol. Sedimentacion (%)" "eco_Radiacíon W/m2 Temp 2020"

FINAL VARIABLE SELECTION

selected_variables<-var_importance_boruta_final
selected_variables
NA
NA
NA

Selected variables range


vines_dataset_numeric_reduced %>% select(selected_variables$variable) %>% reshape2::melt() %>%
  ggplot()+
  facet_wrap(~variable,scales = 'free',ncol = 10)+
  geom_boxplot(aes(x=variable,y=value,fill=variable),color='gray')+
  ggdark::dark_theme_bw()+
    theme(axis.text.x = element_text(angle = 45, hjust = 1))+
  theme(legend.position="none",
        axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank())+
  theme( strip.text = element_text(size = 6))
No id variables; using all as measure variables

NA

PCA Ellipses for classes with selected variables


calidad_index<-vines_dataset_numeric_reduced  %>% 
               select(selected_variables$variable,Calidad)
calidad_index<- which(colnames(calidad_index)=="Calidad")
library(FactoMineR)
res_pca = PCA(vines_dataset_numeric_reduced  %>% 
               select(selected_variables$variable,Calidad)
              , scale.unit=TRUE, 
              ncp=6, 
              graph=F,
              quali.sup=calidad_index, #colid for Calidad
              )

plot(res_pca,choix="ind",habillage=calidad_index)

#par(mfcol=c(1,2))
plot(res_pca,choix="var",habillage="none",invisible = "ind") # para las variables

plotellipses(res_pca, invisible="ind",xlim=c(-6,6),ylim=c(-6,6))

NA
NA

CART

vines_dataset_bootstrap<-rsample::analysis(resamples$splits[[sample(1:100,1)]])
val_dataset  <- vines_dataset_bootstrap   %>% group_by(Calidad) %>% sample_n(2) %>% ungroup()
train_dataset <-dplyr::setdiff(vines_dataset_numeric_reduced,val_dataset)

tree<-rpart::rpart(Calidad~.,
                   data=train_dataset %>% 
                     select(selected_variables$variable,Calidad),
                   control = rpart.control(minsplit = 5),)
rpart.plot(tree,type=1,
           extra=101, box.palette="GnBu",
           branch.lty=3, shadow.col="gray", nn=TRUE
        )

predictions<-predict(tree,val_dataset,type = 'class')
caret::confusionMatrix(val_dataset$Calidad %>% as.factor(),predictions)
Confusion Matrix and Statistics

          Reference
Prediction A0 A1 A2 A3 A5
        A0  2  0  0  0  0
        A1  0  2  0  0  0
        A2  0  0  2  0  0
        A3  0  0  0  2  0
        A5  0  0  0  0  2

Overall Statistics
                                     
               Accuracy : 1          
                 95% CI : (0.6915, 1)
    No Information Rate : 0.2        
    P-Value [Acc > NIR] : 1.024e-07  
                                     
                  Kappa : 1          
                                     
 Mcnemar's Test P-Value : NA         

Statistics by Class:

                     Class: A0 Class: A1 Class: A2 Class: A3 Class: A5
Sensitivity                1.0       1.0       1.0       1.0       1.0
Specificity                1.0       1.0       1.0       1.0       1.0
Pos Pred Value             1.0       1.0       1.0       1.0       1.0
Neg Pred Value             1.0       1.0       1.0       1.0       1.0
Prevalence                 0.2       0.2       0.2       0.2       0.2
Detection Rate             0.2       0.2       0.2       0.2       0.2
Detection Prevalence       0.2       0.2       0.2       0.2       0.2
Balanced Accuracy          1.0       1.0       1.0       1.0       1.0
#printcp(tree)
tree$variable.importance %>% as.data.frame()

MODEL EVALUATION

CART

Cross Validation 3x10

library(caret)
library(doMC)
registerDoMC(cores = 4)
ctrl_fast <- trainControl(
  method = "repeatedcv",
  repeats = 3,
  number = 10,
  returnResamp = 'final',
  savePredictions = 'final',
  verboseIter = F,
  classProbs = TRUE,
  allowParallel = T
)

#rf_grid <-  expand.grid(.mtry = c(5))
cartFit <- caret::train(
  x = vines_dataset_numeric_reduced %>% 
                     select(selected_variables$variable) %>% na.omit(),
  y = vines_dataset_numeric_reduced %>% 
                     select(Calidad) %>% unlist() %>% as.factor(),
  method = "rpart",
  tuneLength=10,
  #tuneGrid = rf_grid,
  #verbose = 2,
  trControl = ctrl_fast,
  #ntree = 200
)

cartFit$results %>%
  ggplot(aes(x = cp, y = Accuracy)) +
  geom_point(color = 'red') +
  geom_errorbar(
    aes(ymin = Accuracy - AccuracySD, ymax = Accuracy + AccuracySD),
    width = .02,
    color = 'yellow'
  ) +
  ggdark::dark_theme_bw() +
  labs(title="CART: Mean and Standard deviation after hyper-parameter (cp) tuning")+
  theme(axis.text.x = element_text(angle = 45, hjust = 1))


cartFit
CART 

77 samples
20 predictors
 5 classes: 'A0', 'A1', 'A2', 'A3', 'A5' 

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 70, 70, 70, 70, 68, 70, ... 
Resampling results across tuning parameters:

  cp          Accuracy   Kappa     
  0.00000000  0.7126323  0.63882743
  0.03434343  0.6941799  0.61294049
  0.06868687  0.6500000  0.55062754
  0.10303030  0.6419974  0.53926611
  0.13737374  0.5918651  0.46255796
  0.17171717  0.5008598  0.33608628
  0.20606061  0.4876323  0.31820216
  0.24040404  0.4263889  0.21813021
  0.27474747  0.3949074  0.16209794
  0.30909091  0.3172619  0.05389871

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was cp = 0.

Learning Curves

set.seed(21052025)
cart_data <-
  learning_curve_dat(dat = vines_dataset_numeric_reduced %>% 
                     select(selected_variables$variable,Calidad),
                     outcome = "Calidad",
                     #test_prop = 0.6,
                     proportion = seq(0.2,1,0.1),
                     ## `train` arguments1
                     method = "rpart",
                     metric = "Accuracy",
                     trControl = ctrl_fast,
                     verbose = F)
There were missing values in resampled performance measures.There were missing values in resampled performance measures.There were missing values in resampled performance measures.
ggplot(cart_data, aes(x = Training_Size, y = Accuracy, color = Data)) +
  geom_smooth(method = loess, span = .8) +
  ggdark::dark_theme_bw()+
  labs(title="CART: Learning curves on training and resampled datasets")

NA
NA

Random Forests

Cross Validation 3x10

#rf_grid <-  expand.grid(.mtry = c(5))
rfFit <- caret::train(
  x = vines_dataset_numeric_reduced %>% 
                     select(selected_variables$variable) %>% na.omit(),
  y = vines_dataset_numeric_reduced %>% 
                     select(Calidad) %>% unlist() %>% as.factor(),
  method = "rf",
  tuneLength=10,
  #tuneGrid = rf_grid,
  #verbose = 2,
  trControl = ctrl_fast,
  #ntree = 200
)

rfFit$results %>%
  ggplot(aes(x = mtry, y = Accuracy)) +
  geom_point(color = 'red') +
  geom_errorbar(
    aes(ymin = Accuracy - AccuracySD, ymax = Accuracy + AccuracySD),
    width = .02,
    color = 'yellow'
  ) +
  ggdark::dark_theme_bw() +
  labs(title="Random Forest: Mean and Standard deviation after hyper-parameter (mtry) tuning")+
  theme(axis.text.x = element_text(angle = 45, hjust = 1))


rfFit
Random Forest 

77 samples
20 predictors
 5 classes: 'A0', 'A1', 'A2', 'A3', 'A5' 

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 68, 69, 70, 69, 70, 70, ... 
Resampling results across tuning parameters:

  mtry  Accuracy   Kappa    
   2    0.9726190  0.9646631
   4    0.9726190  0.9646631
   6    0.9630952  0.9522164
   8    0.9541667  0.9404679
  10    0.9583333  0.9457442
  12    0.9494048  0.9341616
  14    0.9589286  0.9466083
  16    0.9494048  0.9339957
  18    0.9541667  0.9403020
  20    0.9494048  0.9339957

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was mtry = 2.

Learning Curves

set.seed(21052025)
rf_data <-
  learning_curve_dat(dat = vines_dataset_numeric_reduced %>% 
                     select(selected_variables$variable,Calidad),
                     outcome = "Calidad",
                     #test_prop = 0.6,
                     proportion = seq(0.2,1,0.1),
                     ## `train` arguments1
                     method = "rf",
                     metric = "Accuracy",
                     trControl = ctrl_fast,
                     verbose = F)
There were missing values in resampled performance measures.There were missing values in resampled performance measures.
ggplot(rf_data, aes(x = Training_Size, y = Accuracy, color = Data)) +
  geom_smooth(method = loess, span = .8) +
  ggdark::dark_theme_bw()+
  labs(title="Random Forests: Learning curves on training and resampled datasets")

GLMNET

Cross Validation 3x10

glm_results %>% ggplot(aes(x = parameters, y = Accuracy)) +
  geom_point(color = 'red') +
  geom_errorbar(
    aes(ymin = Accuracy - AccuracySD, ymax = Accuracy + AccuracySD),
    width = 0.02,
    color = 'yellow'
  ) +
  ggdark::dark_theme_bw() +
  labs(title="Elastic Net: Mean and Standard deviation after hyper-parameter (mtry) tuning")+
  theme(axis.text.x = element_text(angle = 45, hjust = 1))+
  theme(axis.text=element_text(size=6))

glmFit
glmnet 

77 samples
20 predictors
 5 classes: 'A0', 'A1', 'A2', 'A3', 'A5' 

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 69, 69, 69, 69, 70, 69, ... 
Resampling results across tuning parameters:

  alpha  lambda        Accuracy   Kappa    
  0.1    0.0001221811  0.9520503  0.9378532
  0.1    0.0002822542  0.9520503  0.9378532
  0.1    0.0006520439  0.9520503  0.9378532
  0.1    0.0015063059  0.9520503  0.9378532
  0.1    0.0034797622  0.9478836  0.9326244
  0.1    0.0080387021  0.9389550  0.9214128
  0.1    0.0185704450  0.9389550  0.9214128
  0.1    0.0429001375  0.9160053  0.8914522
  0.1    0.0991048843  0.8819444  0.8472580
  0.1    0.2289451423  0.8437169  0.7960484
  0.2    0.0001221811  0.9520503  0.9378532
  0.2    0.0002822542  0.9520503  0.9378532
  0.2    0.0006520439  0.9520503  0.9378532
  0.2    0.0015063059  0.9520503  0.9378532
  0.2    0.0034797622  0.9431217  0.9266415
  0.2    0.0080387021  0.9389550  0.9214128
  0.2    0.0185704450  0.9333995  0.9142065
  0.2    0.0429001375  0.9160053  0.8914522
  0.2    0.0991048843  0.8736111  0.8362648
  0.2    0.2289451423  0.8232143  0.7678246
  0.3    0.0001221811  0.9603836  0.9490925
  0.3    0.0002822542  0.9603836  0.9490925
  0.3    0.0006520439  0.9562169  0.9436503
  0.3    0.0015063059  0.9520503  0.9378532
  0.3    0.0034797622  0.9431217  0.9266415
  0.3    0.0080387021  0.9389550  0.9216139
  0.3    0.0185704450  0.9389550  0.9216139
  0.3    0.0429001375  0.9160053  0.8914522
  0.3    0.0991048843  0.8694444  0.8309315
  0.3    0.2289451423  0.8008598  0.7354319
  0.4    0.0001221811  0.9659392  0.9564999
  0.4    0.0002822542  0.9603836  0.9490925
  0.4    0.0006520439  0.9603836  0.9490925
  0.4    0.0015063059  0.9603836  0.9490925
  0.4    0.0034797622  0.9431217  0.9268426
  0.4    0.0080387021  0.9389550  0.9216139
  0.4    0.0185704450  0.9389550  0.9216139
  0.4    0.0429001375  0.9029101  0.8744275
  0.4    0.0991048843  0.8652778  0.8246074
  0.4    0.2289451423  0.7445767  0.6602432
  0.5    0.0001221811  0.9659392  0.9564999
  0.5    0.0002822542  0.9659392  0.9564999
  0.5    0.0006520439  0.9659392  0.9564999
  0.5    0.0015063059  0.9603836  0.9490925
  0.5    0.0034797622  0.9514550  0.9380819
  0.5    0.0080387021  0.9389550  0.9216139
  0.5    0.0185704450  0.9389550  0.9216139
  0.5    0.0429001375  0.8939815  0.8630067
  0.5    0.0991048843  0.8568122  0.8133011
  0.5    0.2289451423  0.6128307  0.4747423
  0.6    0.0001221811  0.9659392  0.9564999
  0.6    0.0002822542  0.9659392  0.9564999
  0.6    0.0006520439  0.9659392  0.9564999
  0.6    0.0015063059  0.9659392  0.9567010
  0.6    0.0034797622  0.9570106  0.9454893
  0.6    0.0080387021  0.9472884  0.9328532
  0.6    0.0185704450  0.9389550  0.9216139
  0.6    0.0429001375  0.8939815  0.8630067
  0.6    0.0991048843  0.8441799  0.7960461
  0.6    0.2289451423  0.5425265  0.3780085
  0.7    0.0001221811  0.9659392  0.9564999
  0.7    0.0002822542  0.9659392  0.9564999
  0.7    0.0006520439  0.9659392  0.9564999
  0.7    0.0015063059  0.9659392  0.9567010
  0.7    0.0034797622  0.9570106  0.9454893
  0.7    0.0080387021  0.9528439  0.9402606
  0.7    0.0185704450  0.9389550  0.9216139
  0.7    0.0429001375  0.8902778  0.8579247
  0.7    0.0991048843  0.8460317  0.7983793
  0.7    0.2289451423  0.5170635  0.3432009
  0.8    0.0001221811  0.9659392  0.9564999
  0.8    0.0002822542  0.9659392  0.9564999
  0.8    0.0006520439  0.9659392  0.9567010
  0.8    0.0015063059  0.9659392  0.9567010
  0.8    0.0034797622  0.9570106  0.9454893
  0.8    0.0080387021  0.9528439  0.9402606
  0.8    0.0185704450  0.9431217  0.9270561
  0.8    0.0429001375  0.8819444  0.8472580
  0.8    0.0991048843  0.8376984  0.7877634
  0.8    0.2289451423  0.5072090  0.3250596
  0.9    0.0001221811  0.9659392  0.9564999
  0.9    0.0002822542  0.9659392  0.9567010
  0.9    0.0006520439  0.9659392  0.9567010
  0.9    0.0015063059  0.9617725  0.9514722
  0.9    0.0034797622  0.9570106  0.9454893
  0.9    0.0080387021  0.9570106  0.9454893
  0.9    0.0185704450  0.9528439  0.9402606
  0.9    0.0429001375  0.8736111  0.8354917
  0.9    0.0991048843  0.8555556  0.8118500
  0.9    0.2289451423  0.4982804  0.3128374
  1.0    0.0001221811  0.9576058  0.9460343
  1.0    0.0002822542  0.9617725  0.9513676
  1.0    0.0006520439  0.9576058  0.9460343
  1.0    0.0015063059  0.9576058  0.9460343
  1.0    0.0034797622  0.9528439  0.9401560
  1.0    0.0080387021  0.9486772  0.9348227
  1.0    0.0185704450  0.9383598  0.9217486
  1.0    0.0429001375  0.8750000  0.8389257
  1.0    0.0991048843  0.8472222  0.8013882
  1.0    0.2289451423  0.4423280  0.2364550

Accuracy was used to select the optimal model using the largest value.
The final values used for the model were alpha = 0.6 and lambda = 0.001506306.

Learning Curves

LS0tCnRpdGxlOiAiUC4gRXhwbG9yYXRvcnkgQW5hbHlzaXM6IFZpbmVzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgZmlnX3dpZHRoOiAxMAogICAgZmlnX2hlaWdodDogNQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQoKLS0tCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2tpbXIpCmxpYnJhcnkoVklNKQpsaWJyYXJ5KHJwYXJ0KQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkoQm9ydXRhKSAjZm9yIGZlYXR1cmUgc2VsZWN0aW9uCmxpYnJhcnkoa2FibGVFeHRyYSkgIyBmb3IgcHJldHR5IHByaW50aW5nIHRhYmxlcwoKYGBgCgoKIyBFWFBMT1JBVElPTgojIyBMb2FkaW5nIFRyYWluaW5nIERhdGFzZXQKCmBgYHtyfQp2aW5lc19kYXRhc2V0PC1yZWFkcjo6cmVhZF9jc3YoImRhdGFzZXRzL3ZpbmVfdHJhaW5fbmV3bmFtZXMuY3N2IiwgY29sX3R5cGVzID1jb2xzKCkpCiN2aW5lc19kYXRhc2V0CmBgYAoKVHJhaW5pbmcgZGF0YXNldCBkaW1lbnNpb24gYHIgdmluZXNfZGF0YXNldCAlPiUgbmNvbCgpYCB4IGByIHZpbmVzX2RhdGFzZXQgJT4lIG5yb3coKWAKCgojIyBSZW1vdmluZyB1bm5lY2VzYXJ5IGRhdGEKYGBge3J9CiNyZW1vdmVkX251bWVyaWNfdmFyPC1jKCJ1YmlfeCIsInViaV95IiwiSUQiKQpyZW1vdmVkX251bWVyaWNfdmFyPC1jKCJJRCIpCgp2aW5lc19kYXRhc2V0IDwtIHZpbmVzX2RhdGFzZXQgJT4lIHNlbGVjdCgtcmVtb3ZlZF9udW1lcmljX3ZhcikKYGBgCgojIyBCYXNpYyBEaXN0cmlidXRpb24gSW5mb3JtYXRpb24KIyMjIENhdGVnb3JpY2FsIFZhcmlhYmxlcwpgYGB7cn0KdmluZXNfZGF0YXNldF9mYWN0b3JzPC12aW5lc19kYXRhc2V0ICU+JSBzZWxlY3RfaWYofmNsYXNzKC4pID09ICdjaGFyYWN0ZXInKQpuYW1lcyh2aW5lc19kYXRhc2V0X2ZhY3RvcnMpICU+JSBhcy5kYXRhLmZyYW1lKCkKCmBgYApgYGB7cn0KIHNraW1yOjpza2ltKHZpbmVzX2RhdGFzZXRfZmFjdG9ycyAlPiUgc2VsZWN0KC1DYWxpZGFkKSkgJT4lIGtuaXRyOjprYWJsZSgpICU+JSBrYWJsZV9zdHlsaW5nKGZvbnRfc2l6ZSA9IDkpCmBgYAoKIyMjIE51bWVyaWNhbCBWYXJpYWJsZXMKCmBgYHtyfQp2aW5lc19kYXRhc2V0X251bWVyaWM8LXZpbmVzX2RhdGFzZXQgJT4lIHNlbGVjdF9pZih+Y2xhc3MoLikgPT0gJ251bWVyaWMnKQpuYW1lcyh2aW5lc19kYXRhc2V0X251bWVyaWMpICU+JSBhcy5kYXRhLmZyYW1lKCkKYGBgCgpgYGB7cn0Kc2tpbXI6OnNraW0odmluZXNfZGF0YXNldF9udW1lcmljKSAlPiUga25pdHI6OmthYmxlKCkgJT4lIGthYmxlX3N0eWxpbmcoZm9udF9zaXplID0gOSkKYGBgCgoKIyMjIENsYXNzIERpc3RyaWJ1dGlvbgoKYGBge3J9CnZpbmVzX2RhdGFzZXQgJT4lIGdyb3VwX2J5KENhbGlkYWQpICU+JSBzdW1tYXJpc2UodG90YWw9bigpKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2NvbChhZXMoeD1DYWxpZGFkLHk9dG90YWwsZmlsbD1DYWxpZGFkKSkrCiAgZ2dkYXJrOjpkYXJrX3RoZW1lX2J3KCkKYGBgCgoKIyBBTkFMSVNZUyBOVU1FUklDQUwgVkFSSUFCTEVTCiMjIyBEZWFsaW5nIE1pc3NpbmcgRGF0YQoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KcmVzPC1WSU06OmFnZ3IodmluZXNfZGF0YXNldF9udW1lcmljLCBjb21iaW5lZCA9IFRSVUUsIAogICAgICAgICAgbnVtYmVycyA9IFRSVUUsIAogICAgICAgICAgc29ydENvbWJzID0gVFJVRSwgCiAgICAgICAgICBzb3J0VmFycyA9IFRSVUUsIAogICAgICAgICAgbGFiZWxzPW5hbWVzKHZpbmVzX2RhdGFzZXRfbnVtZXJpYyksCiAgICAgICAgICBjZXguYXhpcz0uNCwgCiAgICAgICAgICB2YXJoZWlnaHQgPSBGQUxTRSwKICAgICAgICAgIGNleC5udW1iZXJzPTAuOCwKICAgICAgICAgIGNleC5sYWI9MC44LAogICAgICAgICAgcHJvcCA9IEZBTFNFKQpgYGAKCiMjIyBNaXNzaW5nIHZhbHVlcyBpbXB1dGF0aW9uCmBgYHtyfQp2aW5lc19kYXRhc2V0X251bWVyaWNfaW1wdXRlZCA8LSBrTk4odmluZXNfZGF0YXNldF9udW1lcmljKQp2aW5lc19kYXRhc2V0X251bWVyaWM8LXZpbmVzX2RhdGFzZXRfbnVtZXJpY19pbXB1dGVkICU+JSBzZWxlY3QoLWVuZHNfd2l0aCgiX2ltcCIpKQojdmluZXNfZGF0YXNldF9udW1lcmljICU+JSBzZWxlY3QoLU5BTUUpCmBgYAoKIyMgQ29ycmVsYXRpb24gTWF0cml4CiMjIyBNYXRyaXggY29uc2lkZXJpbmcgYWxsIHZhcmlhYmxlcwpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQoKbGlicmFyeShkM2hlYXRtYXApCnZpbmVzX2RhdGFzZXRfbnVtZXJpY19jb3JfbWF0cml4PC1jb3IodmluZXNfZGF0YXNldF9udW1lcmljICxtZXRob2Q9InNwZWFybWFuIikKI2hlYXRtYXAocG9zdG9wX2RhdGFfY29yX21hdHJpeCkKZDNoZWF0bWFwKHZpbmVzX2RhdGFzZXRfbnVtZXJpY19jb3JfbWF0cml4ICxjb2xvcnMgPSAiQmx1ZXMiLGNleFJvdyA9IDAuOCwgY2V4Q29sID0gMC44KQpgYGAKCgoKIyMgUENBIEVsaXBzZXMgZm9yIGNsYXNlcyAKYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEyfQpsaWJyYXJ5KEZhY3RvTWluZVIpCiN2aW5lc19kYXRhc2V0X251bWVyaWMgJT4lIHNlbGVjdCgtaGlnaGx5Q29ycmVsYXRlZF92YXIpICU+JSB0aWJibGU6OmFkZF9jb2x1bW4oQ2FsaWRhZD12aW5lc19kYXRhc2V0JENhbGlkYWQpCnJlc19wY2EgPSBQQ0EodmluZXNfZGF0YXNldF9udW1lcmljICAlPiUgCiAgICAgICAgICAgICAgICB0aWJibGU6OmFkZF9jb2x1bW4oQ2FsaWRhZD12aW5lc19kYXRhc2V0JENhbGlkYWQpCiAgICAgICAgICAgICAgLCBzY2FsZS51bml0PVRSVUUsIAogICAgICAgICAgICAgIG5jcD02LCAKICAgICAgICAgICAgICBncmFwaD1GLAogICAgICAgICAgICAgIHF1YWxpLnN1cD01MiwgI2NvbGlkIGZvciBDYWxpZGFkCiAgICAgICAgICAgICAgKQoKcGxvdChyZXNfcGNhLGNob2l4PSJpbmQiLGhhYmlsbGFnZT01MikKcGFyKG1mY29sPWMoMSwyKSkKcGxvdChyZXNfcGNhLGNob2l4PSJ2YXIiLGhhYmlsbGFnZT0ibm9uZSIsaW52aXNpYmxlID0gImluZCIpICMgcGFyYSBsYXMgdmFyaWFibGVzCnBsb3RlbGxpcHNlcyhyZXNfcGNhLCBpbnZpc2libGU9ImluZCIseGxpbT1jKC02LDYpLHlsaW09YygtNiw2KSkKYGBgCgoKIyMgTGlzdCBvZiBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMKYGBge3J9CmhpZ2hseUNvcnJlbGF0ZWQgPC0gY2FyZXQ6OmZpbmRDb3JyZWxhdGlvbih2aW5lc19kYXRhc2V0X251bWVyaWNfY29yX21hdHJpeCwgY3V0b2ZmPTAuOSwgdmVyYm9zZSA9IEYpCmhpZ2hseUNvcnJlbGF0ZWRfdmFyPC12aW5lc19kYXRhc2V0X251bWVyaWNfY29yX21hdHJpeFssaGlnaGx5Q29ycmVsYXRlZF0gJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgbmFtZXMoKQpoaWdobHlDb3JyZWxhdGVkX3ZhcgpgYGAKCiMjIENvcnJlbGF0aW9uIE1hdHJpeCB3aXRoICBoaWdobHkgY29ycmVsYXRlZCByZW1vdmVkCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQojaGlnaGx5Q29ycmVsYXRlZF92YXIgJT4lIGxlbmd0aCgpCiN2aW5lc19kYXRhc2V0X251bWVyaWNfY29yX21hdHJpeFstaGlnaGx5Q29ycmVsYXRlZCwtaGlnaGx5Q29ycmVsYXRlZF0gICU+JSBucm93KCkKZDNoZWF0bWFwKHZpbmVzX2RhdGFzZXRfbnVtZXJpY19jb3JfbWF0cml4Wy1oaWdobHlDb3JyZWxhdGVkLC1oaWdobHlDb3JyZWxhdGVkXSAsY29sb3JzID0gIkJsdWVzIixjZXhSb3cgPSAwLjgsIGNleENvbCA9IDAuOCkKCmBgYAoKCgoKIyMgQ29udmVydCBjbGFzcyB0byBudW1lcmljCmBgYHtyfQojdmluZXNfZGF0YXNldCAlPiUgbXV0YXRlKCkKI3ZpbmVzX2RhdGFzZXQkQ2FsaWRhZCAlPiUgYXMubnVtZXJpYyhhcy5mYWN0b3IodmluZXNfZGF0YXNldCRDYWxpZGFkKSkKYGBgCgojIEZFQVRVUkUgU0VMRUNUSU9OCiMjIEJvb3RzdHJhcCByZXNhbXBsaW5nCjEwMCBuZXcgZGF0YXNldHMgdXNlZCBmb3IgZXZhbHVhdGluZyBmZWF0dXJlIHNlbGVjdGlvbiBhbGdvcml0aG1zCmBgYHtyfQp2aW5lc19kYXRhc2V0X251bWVyaWNfcmVkdWNlZDwtdmluZXNfZGF0YXNldF9udW1lcmljICAlPiUgCiAgc2VsZWN0KC1oaWdobHlDb3JyZWxhdGVkX3ZhcikgICU+JSAKICAjc2VsZWN0KG5hbWVzKHZhcl9pbXBvcnRhbmNlWzE6MTFdKSkgJT4lCiAgdGliYmxlOjphZGRfY29sdW1uKENhbGlkYWQ9YXMuZmFjdG9yKHZpbmVzX2RhdGFzZXQkQ2FsaWRhZCkpCgpyZXNhbXBsZXM8LXJzYW1wbGU6OmJvb3RzdHJhcHModmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQsc3RyYXRhPSBDYWxpZGFkLHRpbWVzID0gMTAwICApCm51bV9vZl9mZWF0PC0yMApgYGAKCiMjIEJPUlVUQSBmb3IgZmVhdHVyZSBzZWxlY3Rpb24KYGBge3J9CnZpbmVzX2RhdGFzZXRfYm9vdHN0cmFwPC1yc2FtcGxlOjphbmFseXNpcyhyZXNhbXBsZXMkc3BsaXRzW1tzYW1wbGUoMToxMDAsMSldXSkKdmFsX2RhdGFzZXQgIDwtIHZpbmVzX2RhdGFzZXRfYm9vdHN0cmFwICAgJT4lIGdyb3VwX2J5KENhbGlkYWQpICU+JSBzYW1wbGVfbigyKSAlPiUgdW5ncm91cCgpCnRyYWluX2RhdGFzZXQgPC1kcGx5cjo6c2V0ZGlmZih2aW5lc19kYXRhc2V0X251bWVyaWNfcmVkdWNlZCx2YWxfZGF0YXNldCkKCnZhcl9pbXBvcnRhbmNlX2JvcnV0YTwtQm9ydXRhKENhbGlkYWQgfiAuICxkYXRhPXRyYWluX2RhdGFzZXQpCgp2YXJfaW1wb3J0YW5jZV9ib3J1dGE8LWF0dFN0YXRzKHZhcl9pbXBvcnRhbmNlX2JvcnV0YSkgJT4lIGZpbHRlcihkZWNpc2lvbj09J0NvbmZpcm1lZCcpICU+JSBzZWxlY3QobWVhbkltcCkgJT4lIGFycmFuZ2UoZGVzYyhtZWFuSW1wKSkgJT4lIHRvcF9uKG51bV9vZl9mZWF0KQp2YXJfaW1wb3J0YW5jZV9ib3J1dGE8LXZhcl9pbXBvcnRhbmNlX2JvcnV0YSAlPiUgYWRkX3Jvd25hbWVzKCd2YXJpYWJsZScpCnZhcl9pbXBvcnRhbmNlX2JvcnV0YV9maW5hbDwtYXBwbHkodmFyX2ltcG9ydGFuY2VfYm9ydXRhJHZhcmlhYmxlICU+JSB0KCkgLDIsIGZ1bmN0aW9uKHgpIHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbCh4LCBwYXR0ZXJuPSJgIixyZXBsYWNlbWVudCA9ICIiKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQpuYW1lcyh2YXJfaW1wb3J0YW5jZV9ib3J1dGFfZmluYWwpPC0idmFyaWFibGUiCnZhcl9pbXBvcnRhbmNlX2JvcnV0YV9maW5hbApgYGAKCiMjIENBUlQgRmVhdHVyZXMgc2VsZWN0aW9uIGFkLWhvYyB1c2luZyBpbXBvcnRhbmNlIG1ldHJpYwpgYGB7cn0KdmFyX2ltcG9ydGFuY2U8LWMoKQpmb3IgKGkgaW4gIDE6MTAwKXsKdmluZXNfZGF0YXNldF9ib290c3RyYXA8LXJzYW1wbGU6OmFuYWx5c2lzKHJlc2FtcGxlcyRzcGxpdHNbW2ldXSkKdmFsX2RhdGFzZXQgIDwtIHZpbmVzX2RhdGFzZXRfYm9vdHN0cmFwICAgJT4lIGdyb3VwX2J5KENhbGlkYWQpICU+JSBzYW1wbGVfbigyKSAlPiUgdW5ncm91cCgpCnRyYWluX2RhdGFzZXQgPC1kcGx5cjo6c2V0ZGlmZih2aW5lc19kYXRhc2V0X251bWVyaWNfcmVkdWNlZCx2YWxfZGF0YXNldCkKCnRyZWU8LXJwYXJ0OjpycGFydChDYWxpZGFkfi4sCiAgICAgICAgICAgICAgICAgICBkYXRhPXRyYWluX2RhdGFzZXQsCiAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChtaW5zcGxpdCA9IDEwKSwpCnZhcl9pbXBvcnRhbmNlPC0gYyh2YXJfaW1wb3J0YW5jZSx0cmVlJHZhcmlhYmxlLmltcG9ydGFuY2VbMTpudW1fb2ZfZmVhdF0pCiNycGFydC5wbG90KHRyZWUsdHlwZT0xLAojICAgICAgICAgICBleHRyYT0xMDEsIGJveC5wYWxldHRlPSJHbkJ1IiwKIyAgICAgICAgICAgYnJhbmNoLmx0eT0zLCBzaGFkb3cuY29sPSJncmF5Iiwgbm49VFJVRQojICAgICAgICApCn0KCmBgYAoKCmBgYHtyfQp2YXJfaW1wb3J0YW5jZV9jYXJ0X2ZpbmFsPC1kYXRhLmZyYW1lKHZhcmlhYmxlPW5hbWVzKHZhcl9pbXBvcnRhbmNlKSx2YWx1ZT12YXJfaW1wb3J0YW5jZSkgJT4lIGdyb3VwX2J5KHZhcmlhYmxlKSAlPiUgc3VtbWFyaXNlKG49bigpKSAlPiUgYXJyYW5nZShkZXNjKG4pKSAlPiUgdG9wX24oMjApCnZhcl9pbXBvcnRhbmNlX2NhcnRfZmluYWwgJT4lIGFzLmRhdGEuZnJhbWUoKQpgYGAKIyMgUkFORE9NIEZPUkVTVFMgZmVhdHVyZSBpbXBvcnRhbmNlCmBgYHtyfQp2aW5lc19kYXRhc2V0X2Jvb3RzdHJhcDwtcnNhbXBsZTo6YW5hbHlzaXMocmVzYW1wbGVzJHNwbGl0c1tbc2FtcGxlKDE6MTAwLDEpXV0pCnZhbF9kYXRhc2V0ICA8LSB2aW5lc19kYXRhc2V0X2Jvb3RzdHJhcCAgICU+JSBncm91cF9ieShDYWxpZGFkKSAlPiUgc2FtcGxlX24oMikgJT4lIHVuZ3JvdXAoKQp0cmFpbl9kYXRhc2V0IDwtZHBseXI6OnNldGRpZmYodmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQsdmFsX2RhdGFzZXQpCgpyZl9uYW1lczwtYXBwbHkobmFtZXModHJhaW5fZGF0YXNldCkgJT4lIHQoKSAsMiwgZnVuY3Rpb24oeCkgc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKHgsIHBhdHRlcm49IiAiLHJlcGxhY2VtZW50ID0gIl8iKSkKcmZfbmFtZXM8LWFwcGx5KHJmX25hbWVzICU+JSB0KCkgLDIsIGZ1bmN0aW9uKHgpIHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbCh4LCBwYXR0ZXJuPSJbLygpJcKwPl0rIixyZXBsYWNlbWVudCA9ICJfIikpCgp0cmFpbl9kYXRhc2V0X3JmPC10cmFpbl9kYXRhc2V0Cm5hbWVzKHRyYWluX2RhdGFzZXRfcmYpPC1yZl9uYW1lcwoKcmY8LXJhbmRvbUZvcmVzdDo6cmFuZG9tRm9yZXN0KENhbGlkYWQgfiAuLGRhdGE9dHJhaW5fZGF0YXNldF9yZixpbXBvcnRhbmNlPVRSVUUpCnZhcl9pbXBvcnRhbmNlX3JmX2ZpbmFsPC1yYW5kb21Gb3Jlc3Q6OmltcG9ydGFuY2UocmYpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGFycmFuZ2UoZGVzYyhNZWFuRGVjcmVhc2VHaW5pKSkgJT4lIGFkZF9yb3duYW1lcygndmFyaWFibGUnKSAgJT4lIHRvcF9uKG51bV9vZl9mZWF0KSAlPiUgc2VsZWN0KHZhcmlhYmxlKQp2YXJfaW1wb3J0YW5jZV9yZl9maW5hbDwtIGRhdGEuZnJhbWUodmFyaWFibGU9bmFtZXModHJhaW5fZGF0YXNldFssd2hpY2gobmFtZXModHJhaW5fZGF0YXNldF9yZikgJWluJSB2YXJfaW1wb3J0YW5jZV9yZl9maW5hbCR2YXJpYWJsZSldKSApCnZhcl9pbXBvcnRhbmNlX3JmX2ZpbmFsCmBgYAojIyBHTE1ORVQgRmVhdHVyZSBTZWxlY3Rpb24KYGBge3J9CmxpYnJhcnkoZ2xtbmV0KQp2aW5lc19kYXRhc2V0X2Jvb3RzdHJhcDwtcnNhbXBsZTo6YW5hbHlzaXMocmVzYW1wbGVzJHNwbGl0c1tbc2FtcGxlKDE6MTAwLDEpXV0pCnZhbF9kYXRhc2V0ICA8LSB2aW5lc19kYXRhc2V0X2Jvb3RzdHJhcCAgICU+JSBncm91cF9ieShDYWxpZGFkKSAlPiUgc2FtcGxlX24oMikgJT4lIHVuZ3JvdXAoKQp0cmFpbl9kYXRhc2V0IDwtZHBseXI6OnNldGRpZmYodmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQsdmFsX2RhdGFzZXQpCmdsbWZpdDwtY3YuZ2xtbmV0KHggPSB0cmFpbl9kYXRhc2V0ICU+JSBzZWxlY3QoLUNhbGlkYWQpICU+JSBhcy5tYXRyaXgoKSwKICAgICAgIHkgPSB0cmFpbl9kYXRhc2V0JENhbGlkYWQgJT4lIGFzLmZhY3RvcigpLAogICAgICAgZmFtaWx5ID0gIm11bHRpbm9taWFsIikKI2NvZWYoZ2xtZml0LHMgPSAnbGFtYmRhLm1pbicpCgonJW5pJSc8LU5lZ2F0ZSgnJWluJScpCmM8LWNvZWYoZ2xtZml0LHM9J2xhbWJkYS5taW4nLGV4YWN0PVRSVUUpCgoKdmFyX2ltcG9ydGFuY2VfZ2xtbmV0PC1wdXJycjo6bWFwKGMsIGZ1bmN0aW9uKHgpIHsKICBpbmRzIDwtIHdoaWNoKHggIT0gMCkKICB2YXJpYWJsZXMgPC0gcm93Lm5hbWVzKHgpW2luZHNdCiAgdmFyaWFibGVzIDwtIHZhcmlhYmxlc1t2YXJpYWJsZXMgJW5pJSAnKEludGVyY2VwdCknXQp9KSAKCiNkby5jYWxsKHJiaW5kLHZhcl9pbXBvcnRhbmNlX2dsbW5ldCkgJT4lIGFzLmRhdGEuZnJhbWUoKQoKCnZhcl9pbXBvcnRhbmNlX2dsbW5ldF9maW5hbDwtdmFyX2ltcG9ydGFuY2VfZ2xtbmV0ICU+JSB1bmxpc3QoKSAlPiUgdW5pcXVlKCkgJT4lIGFzLmRhdGEuZnJhbWUoKQpuYW1lcyh2YXJfaW1wb3J0YW5jZV9nbG1uZXRfZmluYWwpPC0idmFyaWFibGUiCgp2YXJfaW1wb3J0YW5jZV9nbG1uZXRfZmluYWwKYGBgCgojIyMgU2VsZWN0ZWQgdmFyaWFibGVzIHBlciBjbGFzcwpgYGB7cn0KdmFyX2ltcG9ydGFuY2VfZ2xtbmV0CgpgYGAKCiMgRklOQUwgVkFSSUFCTEUgU0VMRUNUSU9OCmBgYHtyfQpzZWxlY3RlZF92YXJpYWJsZXM8LXZhcl9pbXBvcnRhbmNlX2JvcnV0YV9maW5hbApzZWxlY3RlZF92YXJpYWJsZXMKCgoKYGBgCiMjIyBTZWxlY3RlZCB2YXJpYWJsZXMgcmFuZ2UKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTJ9Cgp2aW5lc19kYXRhc2V0X251bWVyaWNfcmVkdWNlZCAlPiUgc2VsZWN0KHNlbGVjdGVkX3ZhcmlhYmxlcyR2YXJpYWJsZSkgJT4lIHJlc2hhcGUyOjptZWx0KCkgJT4lCiAgZ2dwbG90KCkrCiAgZmFjZXRfd3JhcCh+dmFyaWFibGUsc2NhbGVzID0gJ2ZyZWUnLG5jb2wgPSAxMCkrCiAgZ2VvbV9ib3hwbG90KGFlcyh4PXZhcmlhYmxlLHk9dmFsdWUsZmlsbD12YXJpYWJsZSksY29sb3I9J2dyYXknKSsKICBnZ2Rhcms6OmRhcmtfdGhlbWVfYncoKSsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkrCiAgdGhlbWUoIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpKQogIApgYGAKCgoKIyMgUENBIEVsbGlwc2VzIGZvciBjbGFzc2VzIHdpdGggc2VsZWN0ZWQgIHZhcmlhYmxlcwpgYGB7ciBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CgpjYWxpZGFkX2luZGV4PC12aW5lc19kYXRhc2V0X251bWVyaWNfcmVkdWNlZCAgJT4lIAogICAgICAgICAgICAgICBzZWxlY3Qoc2VsZWN0ZWRfdmFyaWFibGVzJHZhcmlhYmxlLENhbGlkYWQpCmNhbGlkYWRfaW5kZXg8LSB3aGljaChjb2xuYW1lcyhjYWxpZGFkX2luZGV4KT09IkNhbGlkYWQiKQpsaWJyYXJ5KEZhY3RvTWluZVIpCnJlc19wY2EgPSBQQ0EodmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQgICU+JSAKICAgICAgICAgICAgICAgc2VsZWN0KHNlbGVjdGVkX3ZhcmlhYmxlcyR2YXJpYWJsZSxDYWxpZGFkKQogICAgICAgICAgICAgICwgc2NhbGUudW5pdD1UUlVFLCAKICAgICAgICAgICAgICBuY3A9NiwgCiAgICAgICAgICAgICAgZ3JhcGg9RiwKICAgICAgICAgICAgICBxdWFsaS5zdXA9Y2FsaWRhZF9pbmRleCwgI2NvbGlkIGZvciBDYWxpZGFkCiAgICAgICAgICAgICAgKQoKcGxvdChyZXNfcGNhLGNob2l4PSJpbmQiLGhhYmlsbGFnZT1jYWxpZGFkX2luZGV4KQojcGFyKG1mY29sPWMoMSwyKSkKcGxvdChyZXNfcGNhLGNob2l4PSJ2YXIiLGhhYmlsbGFnZT0ibm9uZSIsaW52aXNpYmxlID0gImluZCIpICMgcGFyYSBsYXMgdmFyaWFibGVzCnBsb3RlbGxpcHNlcyhyZXNfcGNhLCBpbnZpc2libGU9ImluZCIseGxpbT1jKC02LDYpLHlsaW09YygtNiw2KSkKCgpgYGAKCiMjIENBUlQKYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEyfQp2aW5lc19kYXRhc2V0X2Jvb3RzdHJhcDwtcnNhbXBsZTo6YW5hbHlzaXMocmVzYW1wbGVzJHNwbGl0c1tbc2FtcGxlKDE6MTAwLDEpXV0pCnZhbF9kYXRhc2V0ICA8LSB2aW5lc19kYXRhc2V0X2Jvb3RzdHJhcCAgICU+JSBncm91cF9ieShDYWxpZGFkKSAlPiUgc2FtcGxlX24oMikgJT4lIHVuZ3JvdXAoKQp0cmFpbl9kYXRhc2V0IDwtZHBseXI6OnNldGRpZmYodmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQsdmFsX2RhdGFzZXQpCgp0cmVlPC1ycGFydDo6cnBhcnQoQ2FsaWRhZH4uLAogICAgICAgICAgICAgICAgICAgZGF0YT10cmFpbl9kYXRhc2V0ICU+JSAKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHNlbGVjdGVkX3ZhcmlhYmxlcyR2YXJpYWJsZSxDYWxpZGFkKSwKICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1pbnNwbGl0ID0gNSksKQpycGFydC5wbG90KHRyZWUsdHlwZT0xLAogICAgICAgICAgIGV4dHJhPTEwMSwgYm94LnBhbGV0dGU9IkduQnUiLAogICAgICAgICAgIGJyYW5jaC5sdHk9Mywgc2hhZG93LmNvbD0iZ3JheSIsIG5uPVRSVUUKICAgICAgICApCnByZWRpY3Rpb25zPC1wcmVkaWN0KHRyZWUsdmFsX2RhdGFzZXQsdHlwZSA9ICdjbGFzcycpCmNhcmV0Ojpjb25mdXNpb25NYXRyaXgodmFsX2RhdGFzZXQkQ2FsaWRhZCAlPiUgYXMuZmFjdG9yKCkscHJlZGljdGlvbnMpCiNwcmludGNwKHRyZWUpCgpgYGAKCmBgYHtyfQp0cmVlJHZhcmlhYmxlLmltcG9ydGFuY2UgJT4lIGFzLmRhdGEuZnJhbWUoKQpgYGAKCiMgTU9ERUwgRVZBTFVBVElPTgojIyBDQVJUICAKIyMjIENyb3NzIFZhbGlkYXRpb24gM3gxMApgYGB7cn0KbGlicmFyeShjYXJldCkKbGlicmFyeShkb01DKQpyZWdpc3RlckRvTUMoY29yZXMgPSA0KQpjdHJsX2Zhc3QgPC0gdHJhaW5Db250cm9sKAogIG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwKICByZXBlYXRzID0gMywKICBudW1iZXIgPSAxMCwKICByZXR1cm5SZXNhbXAgPSAnZmluYWwnLAogIHNhdmVQcmVkaWN0aW9ucyA9ICdmaW5hbCcsCiAgdmVyYm9zZUl0ZXIgPSBGLAogIGNsYXNzUHJvYnMgPSBUUlVFLAogIGFsbG93UGFyYWxsZWwgPSBUCikKCiNyZl9ncmlkIDwtICBleHBhbmQuZ3JpZCgubXRyeSA9IGMoNSkpCmNhcnRGaXQgPC0gY2FyZXQ6OnRyYWluKAogIHggPSB2aW5lc19kYXRhc2V0X251bWVyaWNfcmVkdWNlZCAlPiUgCiAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChzZWxlY3RlZF92YXJpYWJsZXMkdmFyaWFibGUpICU+JSBuYS5vbWl0KCksCiAgeSA9IHZpbmVzX2RhdGFzZXRfbnVtZXJpY19yZWR1Y2VkICU+JSAKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KENhbGlkYWQpICU+JSB1bmxpc3QoKSAlPiUgYXMuZmFjdG9yKCksCiAgbWV0aG9kID0gInJwYXJ0IiwKICB0dW5lTGVuZ3RoPTEwLAogICN0dW5lR3JpZCA9IHJmX2dyaWQsCiAgI3ZlcmJvc2UgPSAyLAogIHRyQ29udHJvbCA9IGN0cmxfZmFzdCwKICAjbnRyZWUgPSAyMDAKKQoKY2FydEZpdCRyZXN1bHRzICU+JQogIGdncGxvdChhZXMoeCA9IGNwLCB5ID0gQWNjdXJhY3kpKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICdyZWQnKSArCiAgZ2VvbV9lcnJvcmJhcigKICAgIGFlcyh5bWluID0gQWNjdXJhY3kgLSBBY2N1cmFjeVNELCB5bWF4ID0gQWNjdXJhY3kgKyBBY2N1cmFjeVNEKSwKICAgIHdpZHRoID0gLjAyLAogICAgY29sb3IgPSAneWVsbG93JwogICkgKwogIGdnZGFyazo6ZGFya190aGVtZV9idygpICsKICBsYWJzKHRpdGxlPSJDQVJUOiBNZWFuIGFuZCBTdGFuZGFyZCBkZXZpYXRpb24gYWZ0ZXIgaHlwZXItcGFyYW1ldGVyIChjcCkgdHVuaW5nIikrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKCmNhcnRGaXQKYGBgCiMjIyBMZWFybmluZyBDdXJ2ZXMKYGBge3J9CnNldC5zZWVkKDIxMDUyMDI1KQpjYXJ0X2RhdGEgPC0KICBsZWFybmluZ19jdXJ2ZV9kYXQoZGF0ID0gdmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQgJT4lIAogICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoc2VsZWN0ZWRfdmFyaWFibGVzJHZhcmlhYmxlLENhbGlkYWQpLAogICAgICAgICAgICAgICAgICAgICBvdXRjb21lID0gIkNhbGlkYWQiLAogICAgICAgICAgICAgICAgICAgICAjdGVzdF9wcm9wID0gMC42LAogICAgICAgICAgICAgICAgICAgICBwcm9wb3J0aW9uID0gc2VxKDAuMiwxLDAuMSksCiAgICAgICAgICAgICAgICAgICAgICMjIGB0cmFpbmAgYXJndW1lbnRzMQogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicnBhcnQiLAogICAgICAgICAgICAgICAgICAgICBtZXRyaWMgPSAiQWNjdXJhY3kiLAogICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsX2Zhc3QsCiAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKQoKZ2dwbG90KGNhcnRfZGF0YSwgYWVzKHggPSBUcmFpbmluZ19TaXplLCB5ID0gQWNjdXJhY3ksIGNvbG9yID0gRGF0YSkpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsb2Vzcywgc3BhbiA9IC44KSArCiAgZ2dkYXJrOjpkYXJrX3RoZW1lX2J3KCkrCiAgbGFicyh0aXRsZT0iQ0FSVDogTGVhcm5pbmcgY3VydmVzIG9uIHRyYWluaW5nIGFuZCByZXNhbXBsZWQgZGF0YXNldHMiKQoKCmBgYAoKIyMgUmFuZG9tIEZvcmVzdHMgCiMjIyBDcm9zcyBWYWxpZGF0aW9uIDN4MTAKYGBge3J9CiNyZl9ncmlkIDwtICBleHBhbmQuZ3JpZCgubXRyeSA9IGMoNSkpCnJmRml0IDwtIGNhcmV0Ojp0cmFpbigKICB4ID0gdmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQgJT4lIAogICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoc2VsZWN0ZWRfdmFyaWFibGVzJHZhcmlhYmxlKSAlPiUgbmEub21pdCgpLAogIHkgPSB2aW5lc19kYXRhc2V0X251bWVyaWNfcmVkdWNlZCAlPiUgCiAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChDYWxpZGFkKSAlPiUgdW5saXN0KCkgJT4lIGFzLmZhY3RvcigpLAogIG1ldGhvZCA9ICJyZiIsCiAgdHVuZUxlbmd0aD0xMCwKICAjdHVuZUdyaWQgPSByZl9ncmlkLAogICN2ZXJib3NlID0gMiwKICB0ckNvbnRyb2wgPSBjdHJsX2Zhc3QsCiAgI250cmVlID0gMjAwCikKCnJmRml0JHJlc3VsdHMgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbXRyeSwgeSA9IEFjY3VyYWN5KSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAncmVkJykgKwogIGdlb21fZXJyb3JiYXIoCiAgICBhZXMoeW1pbiA9IEFjY3VyYWN5IC0gQWNjdXJhY3lTRCwgeW1heCA9IEFjY3VyYWN5ICsgQWNjdXJhY3lTRCksCiAgICB3aWR0aCA9IC4wMiwKICAgIGNvbG9yID0gJ3llbGxvdycKICApICsKICBnZ2Rhcms6OmRhcmtfdGhlbWVfYncoKSArCiAgbGFicyh0aXRsZT0iUmFuZG9tIEZvcmVzdDogTWVhbiBhbmQgU3RhbmRhcmQgZGV2aWF0aW9uIGFmdGVyIGh5cGVyLXBhcmFtZXRlciAobXRyeSkgdHVuaW5nIikrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKCnJmRml0CmBgYAojIyMgTGVhcm5pbmcgQ3VydmVzIApgYGB7cn0Kc2V0LnNlZWQoMjEwNTIwMjUpCnJmX2RhdGEgPC0KICBsZWFybmluZ19jdXJ2ZV9kYXQoZGF0ID0gdmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQgJT4lIAogICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoc2VsZWN0ZWRfdmFyaWFibGVzJHZhcmlhYmxlLENhbGlkYWQpLAogICAgICAgICAgICAgICAgICAgICBvdXRjb21lID0gIkNhbGlkYWQiLAogICAgICAgICAgICAgICAgICAgICAjdGVzdF9wcm9wID0gMC42LAogICAgICAgICAgICAgICAgICAgICBwcm9wb3J0aW9uID0gc2VxKDAuMiwxLDAuMSksCiAgICAgICAgICAgICAgICAgICAgICMjIGB0cmFpbmAgYXJndW1lbnRzMQogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICAgICAgICBtZXRyaWMgPSAiQWNjdXJhY3kiLAogICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsX2Zhc3QsCiAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKQoKZ2dwbG90KHJmX2RhdGEsIGFlcyh4ID0gVHJhaW5pbmdfU2l6ZSwgeSA9IEFjY3VyYWN5LCBjb2xvciA9IERhdGEpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG9lc3MsIHNwYW4gPSAuOCkgKwogIGdnZGFyazo6ZGFya190aGVtZV9idygpKwogIGxhYnModGl0bGU9IlJhbmRvbSBGb3Jlc3RzOiBMZWFybmluZyBjdXJ2ZXMgb24gdHJhaW5pbmcgYW5kIHJlc2FtcGxlZCBkYXRhc2V0cyIpCmBgYAoKIyMgR0xNTkVUCiMjIyBDcm9zcyBWYWxpZGF0aW9uIDN4MTAKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTIsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNyZl9ncmlkIDwtICBleHBhbmQuZ3JpZCgubXRyeSA9IGMoNSkpCmdsbUZpdCA8LSBjYXJldDo6dHJhaW4oCiAgeCA9IHZpbmVzX2RhdGFzZXRfbnVtZXJpY19yZWR1Y2VkICU+JSAKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHNlbGVjdGVkX3ZhcmlhYmxlcyR2YXJpYWJsZSkgJT4lIG5hLm9taXQoKSwKICB5ID0gdmluZXNfZGF0YXNldF9udW1lcmljX3JlZHVjZWQgJT4lIAogICAgICAgICAgICAgICAgICAgICBzZWxlY3QoQ2FsaWRhZCkgJT4lIHVubGlzdCgpICU+JSBhcy5mYWN0b3IoKSwKICBtZXRob2QgPSAiZ2xtbmV0IiwKICB0dW5lTGVuZ3RoPTEwLAogICN0dW5lR3JpZCA9IHJmX2dyaWQsCiAgI3ZlcmJvc2UgPSAyLAogIHRyQ29udHJvbCA9IGN0cmxfZmFzdCwKICAjbnRyZWUgPSAyMDAKKQoKZ2xtX3Jlc3VsdHM8LWdsbUZpdCRyZXN1bHRzICU+JSBtdXRhdGVfYXQoMiwgcm91bmQsIDQpICU+JSBhcnJhbmdlKGRlc2MoQWNjdXJhY3kpKSAlPiV0aWR5cjo6dW5pdGUocGFyYW1ldGVycyxhbHBoYTpsYW1iZGEpIApnbG1fcmVzdWx0cyRwYXJhbWV0ZXJzPC1mYWN0b3IoZ2xtX3Jlc3VsdHMkcGFyYW1ldGVycyxsZXZlbHM9dW5pcXVlKGdsbV9yZXN1bHRzJHBhcmFtZXRlcnMpKQpnbG1fcmVzdWx0cyAlPiUgZ2dwbG90KGFlcyh4ID0gcGFyYW1ldGVycywgeSA9IEFjY3VyYWN5KSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAncmVkJykgKwogIGdlb21fZXJyb3JiYXIoCiAgICBhZXMoeW1pbiA9IEFjY3VyYWN5IC0gQWNjdXJhY3lTRCwgeW1heCA9IEFjY3VyYWN5ICsgQWNjdXJhY3lTRCksCiAgICB3aWR0aCA9IDAuMDIsCiAgICBjb2xvciA9ICd5ZWxsb3cnCiAgKSArCiAgZ2dkYXJrOjpkYXJrX3RoZW1lX2J3KCkgKwogIGxhYnModGl0bGU9IkVsYXN0aWMgTmV0OiBNZWFuIGFuZCBTdGFuZGFyZCBkZXZpYXRpb24gYWZ0ZXIgaHlwZXItcGFyYW1ldGVyIChtdHJ5KSB0dW5pbmciKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSsKICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NikpCgpnbG1GaXQKYGBgCgoKIyMjIExlYXJuaW5nIEN1cnZlcwpgYGB7ciBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRX0Kc2V0LnNlZWQoMjEwNTIwMjUpCmdsbW5ldF9kYXRhIDwtCiAgbGVhcm5pbmdfY3VydmVfZGF0KGRhdCA9IHZpbmVzX2RhdGFzZXRfbnVtZXJpY19yZWR1Y2VkICU+JSAKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHNlbGVjdGVkX3ZhcmlhYmxlcyR2YXJpYWJsZSxDYWxpZGFkKSwKICAgICAgICAgICAgICAgICAgICAgb3V0Y29tZSA9ICJDYWxpZGFkIiwKICAgICAgICAgICAgICAgICAgICAgI3Rlc3RfcHJvcCA9IDAuNiwKICAgICAgICAgICAgICAgICAgICAgcHJvcG9ydGlvbiA9IHNlcSgwLjIsMSwwLjEpLAogICAgICAgICAgICAgICAgICAgICAjIyBgdHJhaW5gIGFyZ3VtZW50czEKICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsbW5ldCIsCiAgICAgICAgICAgICAgICAgICAgIG1ldHJpYyA9ICJBY2N1cmFjeSIsCiAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmxfZmFzdCwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEYpCgpnZ3Bsb3QoZ2xtbmV0X2RhdGEsIGFlcyh4ID0gVHJhaW5pbmdfU2l6ZSwgeSA9IEFjY3VyYWN5LCBjb2xvciA9IERhdGEpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG9lc3MsIHNwYW4gPSAuOCkgKwogIGdnZGFyazo6ZGFya190aGVtZV9idygpKwogIGxhYnModGl0bGU9IkVsYXN0aWMgTmV0OiBMZWFybmluZyBjdXJ2ZXMgb24gdHJhaW5pbmcgYW5kIHJlc2FtcGxlZCBkYXRhc2V0cyIpCmBgYAo=