Librerias aplicadas

library(caret)
library(raster)

Carga de las bandas de imágenes satelitales.

band1 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B1.TIF")
band2 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B2.TIF")
band3 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B3.TIF")
band4 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B4.TIF")
band5 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B5.TIF")
band6 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B6.TIF")
band7 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B7.TIF")
band10 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B10.TIF")
band11 <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/capturas-landsat/roma/LC81910312013208LGN00/LC81910312013208LGN00_B11.TIF")

roma_rst_lan <- brick(band1,band2,band3,band4,band5,band6,band7,band10,band11)
names(roma_rst_lan) <- c(paste("Banda",1:7,sep=""),"Banda10","Banda11")

Procesado de datos

#Carga de muestras de entrenamiento
rstTrainroma <- raster("/Users/elianaalizchuquillanquijulcapari/Documents/workspace-r/demos/DataTraining/rome_lcz_GT.tif")

# Remover ceros (fondo NA)
rstTrainroma[rstTrainroma==0] <- NA

# Convertir a tipo discreto
rstTrainroma <- ratify(rstTrainroma)

# Cambiamos el nombre
names(rstTrainroma) <- "layer"

#Muestras de valores
table(values(rstTrainroma))

   2    3    5    6    8   10   11   12   14   17 
1551  104 1495  480  435   51  284  555  984  500 
# Eliminación de NAs 
rstDFlanroma.df <- na.omit(values(stack(rstTrainroma, roma_rst_lan)))

# Viualizamos las muestras
cols <- c("#fde725","#b5de2b", "#6ece58","#35b779", "#1f9e89","#26828e", "#31688e", "#3e4989", "#482878", "#440154")
plot(rstTrainroma, main="Muestras de entrenamiento de Roma", col = cols)

Fase de Clasificación

#Si es la primera vez se ejecuta, sino ya esta guardado los datos del dataframe.
write.csv(rstDFlanroma.df,"rstDFRoma.csv", row.names = FALSE)

#Leemos los datos
samplesLanroma = read.csv("rstDFRoma.csv")

# Se divide las muestras en 70 y 30
trainx = list(0)
evalx = list(0)
for (i in 1:17){ # se itera en todas las posibles categorías
  cls = samplesLanroma[samplesLanroma$layer == i,]
  smpl <- floor(0.70 * nrow(cls))
  tt <- sample(seq_len(nrow(cls)), size = smpl)
  trainx[[i]] <- cls[tt,]
  evalx[[i]] <- cls[-tt,]
}


# Se combina en dataframe referenciados s entrenamiento y evaluación
trn = do.call(rbind, trainx) 
eva = do.call(rbind, evalx)

Algoritmo Redes neuronales

# Definiendo el control del entrenamiento
fitControl <- trainControl(method = "repeatedcv", # repeated cross-validation of the training data
                   number = 10, # number of folds
                   repeats = 3) # view the training iterations


# Entrenamiento con Redes Neuronales
nnet_grid <- expand.grid(size = c(5, 10, 15),
                         decay = c(0.001, 0.01, 0.1))

nnet_model_roma <- caret::train(x = trn[,(2:ncol(trn))], y = as.factor(trn$layer),
                               method = "nnet", metric="Accuracy", 
                               maxit = 100,
                               tuneGrid = nnet_grid,
                               preProcess = c("center", "scale"),
                               trControl = fitControl,
                               trace = FALSE)

nnet_grid2 = expand.grid(size = seq(from = 2, to = 10, by = 2), 
                        decay = seq(from = 0.1, to = 0.5, by = 0.1))

nnet_model_roma2 <- caret::train(x = trn[,(2:ncol(trn))], y = as.factor(trn$layer),
                               method = "nnet", metric="Accuracy",
                               maxit = 100,
                               tuneGrid = nnet_grid2,
                               preProcess = c("center", "scale"),
                               trControl = fitControl,
                               trace = FALSE)

Estructura de la red

library(NeuralNetTools)
plotnet(nnet_model_roma$finalModel)


#modelo 2
plotnet(nnet_model_roma2$finalModel)

Exploración del modelo

#Información modelo nnet
print(nnet_model_roma)
Neural Network 

4502 samples
   9 predictor
  10 classes: '2', '3', '5', '6', '8', '10', '11', '12', '14', '17' 

Pre-processing: centered (9), scaled (9) 
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 4051, 4052, 4054, 4050, 4052, 4052, ... 
Resampling results across tuning parameters:

  size  decay  Accuracy   Kappa    
   5    0.001  0.7892113  0.7474936
   5    0.010  0.7864620  0.7441364
   5    0.100  0.7852068  0.7422215
  10    0.001  0.8109036  0.7739284
  10    0.010  0.8115595  0.7747506
  10    0.100  0.8074964  0.7697054
  15    0.001  0.8173485  0.7818004
  15    0.010  0.8158028  0.7799476
  15    0.100  0.8162342  0.7802712

Accuracy was used to select the optimal model using the largest value.
The final values used for the model were size = 15 and decay = 0.001.
print(nnet_model_roma2)
Neural Network 

4502 samples
   9 predictor
  10 classes: '2', '3', '5', '6', '8', '10', '11', '12', '14', '17' 

Pre-processing: centered (9), scaled (9) 
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 4051, 4052, 4052, 4051, 4053, 4052, ... 
Resampling results across tuning parameters:

  size  decay  Accuracy   Kappa    
   2    0.1    0.7056884  0.6428292
   2    0.2    0.7033307  0.6397061
   2    0.3    0.6973017  0.6317135
   2    0.4    0.7033842  0.6392924
   2    0.5    0.6879770  0.6189391
   4    0.1    0.7688343  0.7222316
   4    0.2    0.7647635  0.7172468
   4    0.3    0.7568483  0.7073791
   4    0.4    0.7555907  0.7056245
   4    0.5    0.7553609  0.7054173
   6    0.1    0.7941506  0.7533604
   6    0.2    0.7902373  0.7484191
   6    0.3    0.7884554  0.7461338
   6    0.4    0.7871946  0.7446072
   6    0.5    0.7837177  0.7402162
   8    0.1    0.8028976  0.7641201
   8    0.2    0.7968212  0.7564817
   8    0.3    0.7931874  0.7519018
   8    0.4    0.7923107  0.7507579
   8    0.5    0.7917109  0.7499887
  10    0.1    0.8051750  0.7668082
  10    0.2    0.8014789  0.7621894
  10    0.3    0.7974046  0.7571338
  10    0.4    0.7968178  0.7562008
  10    0.5    0.7928177  0.7512634

Accuracy was used to select the optimal model using the largest value.
The final values used for the model were size = 10 and decay = 0.1.
#Gráfica del modelo nnet
plot(nnet_model_roma)


plot(nnet_model_roma2)

Evaluación del modelo 1.

pred_nnet_model_roma<- predict(nnet_model_roma,
                              newdata = eva)

cm <- confusionMatrix(data = pred_nnet_model_roma, as.factor(eva$layer))
cm
Confusion Matrix and Statistics

          Reference
Prediction   2   3   5   6   8  10  11  12  14  17
        2  376  13  79   4   3   2   0   0   0   0
        3    4  13   3   2   0   0   0   0   0   0
        5   80   3 321  28  10   8   1   8   1   0
        6    3   3  22  99   0   0   1   3   3   0
        8    2   0   9   0 117   1   0   0   0   0
        10   0   0   0   0   0   4   0   0   0   0
        11   0   0   0   0   0   0  80   2   0   0
        12   0   0  10   5   0   0   4 146   6   0
        14   1   0   5   6   1   1   0   8 286   0
        17   0   0   0   0   0   0   0   0   0 150

Overall Statistics
                                          
               Accuracy : 0.8219          
                 95% CI : (0.8041, 0.8387)
    No Information Rate : 0.2406          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.787           
                                          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: 2 Class: 3 Class: 5 Class: 6 Class: 8 Class: 10
Sensitivity            0.8069 0.406250   0.7149  0.68750  0.89313  0.250000
Specificity            0.9313 0.995276   0.9066  0.98048  0.99336  1.000000
Pos Pred Value         0.7883 0.590909   0.6978  0.73881  0.90698  1.000000
Neg Pred Value         0.9384 0.990078   0.9133  0.97504  0.99226  0.993792
Prevalence             0.2406 0.016520   0.2318  0.07434  0.06763  0.008260
Detection Rate         0.1941 0.006711   0.1657  0.05111  0.06040  0.002065
Detection Prevalence   0.2463 0.011358   0.2375  0.06918  0.06660  0.002065
Balanced Accuracy      0.8691 0.700763   0.8108  0.83399  0.94324  0.625000
                     Class: 11 Class: 12 Class: 14 Class: 17
Sensitivity            0.93023   0.87425    0.9662   1.00000
Specificity            0.99892   0.98588    0.9866   1.00000
Pos Pred Value         0.97561   0.85380    0.9286   1.00000
Neg Pred Value         0.99677   0.98811    0.9939   1.00000
Prevalence             0.04440   0.08622    0.1528   0.07744
Detection Rate         0.04130   0.07537    0.1477   0.07744
Detection Prevalence   0.04233   0.08828    0.1590   0.07744
Balanced Accuracy      0.96458   0.93006    0.9764   1.00000

Matriz de confusión modelo 1

cm_d <- as.data.frame(cm$table)

cm_st <-data.frame(cm$overall)

cm_st$cm.overall <- round(cm_st$cm.overall,2)

cm_p <- as.data.frame(prop.table(cm$table))
cm_d$Perc <- round(cm_p$Freq*100,2)

# dibujando la matriz
ggplot(data = cm_d, aes(x = Prediction , y =  Reference, fill = Freq))+
  geom_tile(color = "white", lwd = 0.5) +
  geom_text(aes(label = paste("",Freq)), color = 'white', size = 3.5)+ 
  theme_minimal() +
  ggtitle("Matriz de Confusión")

Evaluación del modelo 2

pred_nnet_model_roma2<- predict(nnet_model_roma2,
                              newdata = eva)

cm <- confusionMatrix(data = pred_nnet_model_roma2, as.factor(eva$layer))
cm
Confusion Matrix and Statistics

          Reference
Prediction   2   3   5   6   8  10  11  12  14  17
        2  394  13  91   3   4   2   0   0   0   0
        3    1  14   2   6   0   0   0   0   0   0
        5   66   2 313  27  12   7   2  12   3   0
        6    3   3  19  95   1   0   0   1   4   0
        8    2   0  10   0 113   0   0   0   0   0
        10   0   0   1   0   1   5   0   0   0   0
        11   0   0   1   0   0   0  79   3   0   0
        12   0   0   5   9   0   0   4 136  11   0
        14   0   0   7   4   0   2   1  15 278   0
        17   0   0   0   0   0   0   0   0   0 150

Overall Statistics
                                          
               Accuracy : 0.8141          
                 95% CI : (0.7961, 0.8312)
    No Information Rate : 0.2406          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.7774          
                                          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: 2 Class: 3 Class: 5 Class: 6 Class: 8 Class: 10
Sensitivity            0.8455 0.437500   0.6971  0.65972  0.86260  0.312500
Specificity            0.9232 0.995276   0.9120  0.98271  0.99336  0.998959
Pos Pred Value         0.7771 0.608696   0.7050  0.75397  0.90400  0.714286
Neg Pred Value         0.9497 0.990596   0.9089  0.97294  0.99007  0.994301
Prevalence             0.2406 0.016520   0.2318  0.07434  0.06763  0.008260
Detection Rate         0.2034 0.007228   0.1616  0.04904  0.05834  0.002581
Detection Prevalence   0.2617 0.011874   0.2292  0.06505  0.06453  0.003614
Balanced Accuracy      0.8843 0.716388   0.8045  0.82122  0.92798  0.655729
                     Class: 11 Class: 12 Class: 14 Class: 17
Sensitivity            0.91860   0.81437    0.9392   1.00000
Specificity            0.99784   0.98362    0.9823   1.00000
Pos Pred Value         0.95181   0.82424    0.9055   1.00000
Neg Pred Value         0.99622   0.98251    0.9890   1.00000
Prevalence             0.04440   0.08622    0.1528   0.07744
Detection Rate         0.04078   0.07021    0.1435   0.07744
Detection Prevalence   0.04285   0.08518    0.1585   0.07744
Balanced Accuracy      0.95822   0.89899    0.9608   1.00000

Matriz de confusión modelo 2

cm_d <- as.data.frame(cm$table)

cm_st <-data.frame(cm$overall)

cm_st$cm.overall <- round(cm_st$cm.overall,2)

cm_p <- as.data.frame(prop.table(cm$table))
cm_d$Perc <- round(cm_p$Freq*100,2)

# dibujando la matriz
ggplot(data = cm_d, aes(x = Prediction , y =  Reference, fill = Freq))+
  geom_tile(color = "white", lwd = 0.5) +
  geom_text(aes(label = paste("",Freq)), color = 'white', size = 3.5)+ 
  theme_minimal() +
  ggtitle("Matriz de Confusión")

Predicción y visualización Modelo 1.

classi_nnet_model_roma <- raster::predict(roma_rst_lan, model=nnet_model_roma)
plot(classi_nnet_model_roma, main="Clasificación Redes neuronales Modelo 1",col = cols)

Predicción y visualización Modelo 2.

classi_nnet_model_roma2 <- raster::predict(roma_rst_lan, model=nnet_model_roma2)
plot(classi_nnet_model_roma2, main="Clasificación Redes neuronales Modelo 2", col = cols)

LS0tCnRpdGxlOiAiQ2xhc2lmaWNhY2nDs24gZGUgaW3DoWdlbmVzIGNvbiBSZWRlcyBOZXVyb25hbGVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIExpYnJlcmlhcyBhcGxpY2FkYXMKCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KHJhc3RlcikKYGBgCgojIENhcmdhIGRlIGxhcyBiYW5kYXMgZGUgaW3DoWdlbmVzIHNhdGVsaXRhbGVzLgoKYGBge3J9CmJhbmQxIDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CMS5USUYiKQpiYW5kMiA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjIuVElGIikKYmFuZDMgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0IzLlRJRiIpCmJhbmQ0IDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CNC5USUYiKQpiYW5kNSA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjUuVElGIikKYmFuZDYgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0I2LlRJRiIpCmJhbmQ3IDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CNy5USUYiKQpiYW5kMTAgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0IxMC5USUYiKQpiYW5kMTEgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0IxMS5USUYiKQoKcm9tYV9yc3RfbGFuIDwtIGJyaWNrKGJhbmQxLGJhbmQyLGJhbmQzLGJhbmQ0LGJhbmQ1LGJhbmQ2LGJhbmQ3LGJhbmQxMCxiYW5kMTEpCm5hbWVzKHJvbWFfcnN0X2xhbikgPC0gYyhwYXN0ZSgiQmFuZGEiLDE6NyxzZXA9IiIpLCJCYW5kYTEwIiwiQmFuZGExMSIpCmBgYAoKIyBQcm9jZXNhZG8gZGUgZGF0b3MKCmBgYHtyfQojQ2FyZ2EgZGUgbXVlc3RyYXMgZGUgZW50cmVuYW1pZW50bwpyc3RUcmFpbnJvbWEgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL0RhdGFUcmFpbmluZy9yb21lX2xjel9HVC50aWYiKQoKIyBSZW1vdmVyIGNlcm9zIChmb25kbyBOQSkKcnN0VHJhaW5yb21hW3JzdFRyYWlucm9tYT09MF0gPC0gTkEKCiMgQ29udmVydGlyIGEgdGlwbyBkaXNjcmV0bwpyc3RUcmFpbnJvbWEgPC0gcmF0aWZ5KHJzdFRyYWlucm9tYSkKCiMgQ2FtYmlhbW9zIGVsIG5vbWJyZQpuYW1lcyhyc3RUcmFpbnJvbWEpIDwtICJsYXllciIKCiNNdWVzdHJhcyBkZSB2YWxvcmVzCnRhYmxlKHZhbHVlcyhyc3RUcmFpbnJvbWEpKQoKIyBFbGltaW5hY2nDs24gZGUgTkFzIApyc3RERmxhbnJvbWEuZGYgPC0gbmEub21pdCh2YWx1ZXMoc3RhY2socnN0VHJhaW5yb21hLCByb21hX3JzdF9sYW4pKSkKCiMgVml1YWxpemFtb3MgbGFzIG11ZXN0cmFzCmNvbHMgPC0gYygiI2ZkZTcyNSIsIiNiNWRlMmIiLCAiIzZlY2U1OCIsIiMzNWI3NzkiLCAiIzFmOWU4OSIsIiMyNjgyOGUiLCAiIzMxNjg4ZSIsICIjM2U0OTg5IiwgIiM0ODI4NzgiLCAiIzQ0MDE1NCIpCnBsb3QocnN0VHJhaW5yb21hLCBtYWluPSJNdWVzdHJhcyBkZSBlbnRyZW5hbWllbnRvIGRlIFJvbWEiLCBjb2wgPSBjb2xzKQoKYGBgCgojIEZhc2UgZGUgQ2xhc2lmaWNhY2nDs24KCmBgYHtyfQojU2kgZXMgbGEgcHJpbWVyYSB2ZXogc2UgZWplY3V0YSwgc2lubyB5YSBlc3RhIGd1YXJkYWRvIGxvcyBkYXRvcyBkZWwgZGF0YWZyYW1lLgp3cml0ZS5jc3YocnN0REZsYW5yb21hLmRmLCJyc3RERlJvbWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojTGVlbW9zIGxvcyBkYXRvcwpzYW1wbGVzTGFucm9tYSA9IHJlYWQuY3N2KCJyc3RERlJvbWEuY3N2IikKCiMgU2UgZGl2aWRlIGxhcyBtdWVzdHJhcyBlbiA3MCB5IDMwCnRyYWlueCA9IGxpc3QoMCkKZXZhbHggPSBsaXN0KDApCmZvciAoaSBpbiAxOjE3KXsgIyBzZSBpdGVyYSBlbiB0b2RhcyBsYXMgcG9zaWJsZXMgY2F0ZWdvcsOtYXMKICBjbHMgPSBzYW1wbGVzTGFucm9tYVtzYW1wbGVzTGFucm9tYSRsYXllciA9PSBpLF0KICBzbXBsIDwtIGZsb29yKDAuNzAgKiBucm93KGNscykpCiAgdHQgPC0gc2FtcGxlKHNlcV9sZW4obnJvdyhjbHMpKSwgc2l6ZSA9IHNtcGwpCiAgdHJhaW54W1tpXV0gPC0gY2xzW3R0LF0KICBldmFseFtbaV1dIDwtIGNsc1stdHQsXQp9CgoKIyBTZSBjb21iaW5hIGVuIGRhdGFmcmFtZSByZWZlcmVuY2lhZG9zIHMgZW50cmVuYW1pZW50byB5IGV2YWx1YWNpw7NuCnRybiA9IGRvLmNhbGwocmJpbmQsIHRyYWlueCkgCmV2YSA9IGRvLmNhbGwocmJpbmQsIGV2YWx4KQpgYGAKCiMgQWxnb3JpdG1vIFJlZGVzIG5ldXJvbmFsZXMKCmBgYHtyfQojIERlZmluaWVuZG8gZWwgY29udHJvbCBkZWwgZW50cmVuYW1pZW50bwpmaXRDb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAicmVwZWF0ZWRjdiIsICMgcmVwZWF0ZWQgY3Jvc3MtdmFsaWRhdGlvbiBvZiB0aGUgdHJhaW5pbmcgZGF0YQogICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTAsICMgbnVtYmVyIG9mIGZvbGRzCiAgICAgICAgICAgICAgICAgICByZXBlYXRzID0gMykgIyB2aWV3IHRoZSB0cmFpbmluZyBpdGVyYXRpb25zCgoKIyBFbnRyZW5hbWllbnRvIGNvbiBSZWRlcyBOZXVyb25hbGVzCm5uZXRfZ3JpZCA8LSBleHBhbmQuZ3JpZChzaXplID0gYyg1LCAxMCwgMTUpLAogICAgICAgICAgICAgICAgICAgICAgICAgZGVjYXkgPSBjKDAuMDAxLCAwLjAxLCAwLjEpKQoKbm5ldF9tb2RlbF9yb21hIDwtIGNhcmV0Ojp0cmFpbih4ID0gdHJuWywoMjpuY29sKHRybikpXSwgeSA9IGFzLmZhY3Rvcih0cm4kbGF5ZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIm5uZXQiLCBtZXRyaWM9IkFjY3VyYWN5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhpdCA9IDEwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gbm5ldF9ncmlkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gZml0Q29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWNlID0gRkFMU0UpCgpubmV0X2dyaWQyID0gZXhwYW5kLmdyaWQoc2l6ZSA9IHNlcShmcm9tID0gMiwgdG8gPSAxMCwgYnkgPSAyKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGRlY2F5ID0gc2VxKGZyb20gPSAwLjEsIHRvID0gMC41LCBieSA9IDAuMSkpCgpubmV0X21vZGVsX3JvbWEyIDwtIGNhcmV0Ojp0cmFpbih4ID0gdHJuWywoMjpuY29sKHRybikpXSwgeSA9IGFzLmZhY3Rvcih0cm4kbGF5ZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIm5uZXQiLCBtZXRyaWM9IkFjY3VyYWN5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heGl0ID0gMTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBubmV0X2dyaWQyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gZml0Q29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWNlID0gRkFMU0UpCgpgYGAKCiMgRXN0cnVjdHVyYSBkZSBsYSByZWQKCmBgYHtyfQpsaWJyYXJ5KE5ldXJhbE5ldFRvb2xzKQpwbG90bmV0KG5uZXRfbW9kZWxfcm9tYSRmaW5hbE1vZGVsKQoKI21vZGVsbyAyCnBsb3RuZXQobm5ldF9tb2RlbF9yb21hMiRmaW5hbE1vZGVsKQpgYGAKCiMgRXhwbG9yYWNpw7NuIGRlbCBtb2RlbG8KCmBgYHtyfQojSW5mb3JtYWNpw7NuIG1vZGVsbyBubmV0CnByaW50KG5uZXRfbW9kZWxfcm9tYSkKCnByaW50KG5uZXRfbW9kZWxfcm9tYTIpCgojR3LDoWZpY2EgZGVsIG1vZGVsbyBubmV0CnBsb3Qobm5ldF9tb2RlbF9yb21hKQoKcGxvdChubmV0X21vZGVsX3JvbWEyKQpgYGAKCiMgRXZhbHVhY2nDs24gZGVsIG1vZGVsbyAxLgoKYGBge3J9CnByZWRfbm5ldF9tb2RlbF9yb21hPC0gcHJlZGljdChubmV0X21vZGVsX3JvbWEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBldmEpCgpjbSA8LSBjb25mdXNpb25NYXRyaXgoZGF0YSA9IHByZWRfbm5ldF9tb2RlbF9yb21hLCBhcy5mYWN0b3IoZXZhJGxheWVyKSkKY20KYGBgCgojIE1hdHJpeiBkZSBjb25mdXNpw7NuIG1vZGVsbyAxCgpgYGB7cn0KY21fZCA8LSBhcy5kYXRhLmZyYW1lKGNtJHRhYmxlKQoKY21fc3QgPC1kYXRhLmZyYW1lKGNtJG92ZXJhbGwpCgpjbV9zdCRjbS5vdmVyYWxsIDwtIHJvdW5kKGNtX3N0JGNtLm92ZXJhbGwsMikKCmNtX3AgPC0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKGNtJHRhYmxlKSkKY21fZCRQZXJjIDwtIHJvdW5kKGNtX3AkRnJlcSoxMDAsMikKCiMgZGlidWphbmRvIGxhIG1hdHJpegpnZ3Bsb3QoZGF0YSA9IGNtX2QsIGFlcyh4ID0gUHJlZGljdGlvbiAsIHkgPSAgUmVmZXJlbmNlLCBmaWxsID0gRnJlcSkpKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIGx3ZCA9IDAuNSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZSgiIixGcmVxKSksIGNvbG9yID0gJ3doaXRlJywgc2l6ZSA9IDMuNSkrIAogIHRoZW1lX21pbmltYWwoKSArCiAgZ2d0aXRsZSgiTWF0cml6IGRlIENvbmZ1c2nDs24iKQpgYGAKCiMgRXZhbHVhY2nDs24gZGVsIG1vZGVsbyAyCgpgYGB7cn0KcHJlZF9ubmV0X21vZGVsX3JvbWEyPC0gcHJlZGljdChubmV0X21vZGVsX3JvbWEyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZXZhKQoKY20gPC0gY29uZnVzaW9uTWF0cml4KGRhdGEgPSBwcmVkX25uZXRfbW9kZWxfcm9tYTIsIGFzLmZhY3RvcihldmEkbGF5ZXIpKQpjbQpgYGAKCiMgTWF0cml6IGRlIGNvbmZ1c2nDs24gbW9kZWxvIDIKCmBgYHtyfQpjbV9kIDwtIGFzLmRhdGEuZnJhbWUoY20kdGFibGUpCgpjbV9zdCA8LWRhdGEuZnJhbWUoY20kb3ZlcmFsbCkKCmNtX3N0JGNtLm92ZXJhbGwgPC0gcm91bmQoY21fc3QkY20ub3ZlcmFsbCwyKQoKY21fcCA8LSBhcy5kYXRhLmZyYW1lKHByb3AudGFibGUoY20kdGFibGUpKQpjbV9kJFBlcmMgPC0gcm91bmQoY21fcCRGcmVxKjEwMCwyKQoKIyBkaWJ1amFuZG8gbGEgbWF0cml6CmdncGxvdChkYXRhID0gY21fZCwgYWVzKHggPSBQcmVkaWN0aW9uICwgeSA9ICBSZWZlcmVuY2UsIGZpbGwgPSBGcmVxKSkrCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIiwgbHdkID0gMC41KSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlKCIiLEZyZXEpKSwgY29sb3IgPSAnd2hpdGUnLCBzaXplID0gMy41KSsgCiAgdGhlbWVfbWluaW1hbCgpICsKICBnZ3RpdGxlKCJNYXRyaXogZGUgQ29uZnVzacOzbiIpCmBgYAoKIyBQcmVkaWNjacOzbiB5IHZpc3VhbGl6YWNpw7NuIE1vZGVsbyAxLgoKYGBge3J9CmNsYXNzaV9ubmV0X21vZGVsX3JvbWEgPC0gcmFzdGVyOjpwcmVkaWN0KHJvbWFfcnN0X2xhbiwgbW9kZWw9bm5ldF9tb2RlbF9yb21hKQpwbG90KGNsYXNzaV9ubmV0X21vZGVsX3JvbWEsIG1haW49IkNsYXNpZmljYWNpw7NuIFJlZGVzIG5ldXJvbmFsZXMgTW9kZWxvIDEiLGNvbCA9IGNvbHMpCmBgYAoKIyBQcmVkaWNjacOzbiB5IHZpc3VhbGl6YWNpw7NuIE1vZGVsbyAyLgoKYGBge3J9CmNsYXNzaV9ubmV0X21vZGVsX3JvbWEyIDwtIHJhc3Rlcjo6cHJlZGljdChyb21hX3JzdF9sYW4sIG1vZGVsPW5uZXRfbW9kZWxfcm9tYTIpCnBsb3QoY2xhc3NpX25uZXRfbW9kZWxfcm9tYTIsIG1haW49IkNsYXNpZmljYWNpw7NuIFJlZGVzIG5ldXJvbmFsZXMgTW9kZWxvIDIiLCBjb2wgPSBjb2xzKQpgYGAKCg==