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 KNN

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

kknn.grid <- expand.grid(kmax = c(3, 5, 7 ,9, 11), distance = c(1, 2),
                         kernel = "optimal")
# Entrenamiento con Knn
knn_model_roma<- caret::train(x = trn[,(2:ncol(trn))], y = as.factor(trn$layer),
               method = "kknn",
               metric="Accuracy",
               tuneGrid = kknn.grid,
               preProcess = c("center", "scale"),
               trControl = fitControl)

# Entrenamiento con Knn
knn_model_roma2<- caret::train(x = trn[,(2:ncol(trn))], y = as.factor(trn$layer),
               method = "kknn",
               metric="Accuracy",
               tuneLength = 10,
               preProcess = c("center", "scale"),
               trControl = fitControl)

Exploración del modelo

#Información modelo knn
print(knn_model_roma)
k-Nearest Neighbors 

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 5 times) 
Summary of sample sizes: 4051, 4051, 4052, 4051, 4052, 4051, ... 
Resampling results across tuning parameters:

  kmax  distance  Accuracy   Kappa    
   3    1         0.7360262  0.6852760
   3    2         0.7371033  0.6866898
   5    1         0.7537971  0.7062395
   5    2         0.7529610  0.7054210
   7    1         0.7591704  0.7123210
   7    2         0.7609103  0.7146487
   9    1         0.7646348  0.7186104
   9    2         0.7652625  0.7196621
  11    1         0.7664998  0.7207037
  11    2         0.7677933  0.7224904

Tuning parameter 'kernel' was held constant at a value of optimal
Accuracy was used to select the optimal model using the largest value.
The final values used for the model were kmax = 11, distance = 2 and kernel = optimal.
print(knn_model_roma2)
k-Nearest Neighbors 

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 5 times) 
Summary of sample sizes: 4050, 4054, 4051, 4051, 4052, 4050, ... 
Resampling results across tuning parameters:

  kmax  Accuracy   Kappa    
   5    0.7538018  0.7062783
   7    0.7616656  0.7153916
   9    0.7642395  0.7183027
  11    0.7677925  0.7223750
  13    0.7712565  0.7263738
  15    0.7734772  0.7289598
  17    0.7741836  0.7297714
  19    0.7739615  0.7294840
  21    0.7737398  0.7292299
  23    0.7737398  0.7292299

Tuning parameter 'distance' was held constant at a value of 2
Tuning parameter 'kernel' was
 held constant at a value of optimal
Accuracy was used to select the optimal model using the largest value.
The final values used for the model were kmax = 17, distance = 2 and kernel = optimal.
#Gráfica del modelo knn
plot(knn_model_roma)


plot(knn_model_roma2)

Evaluación del modelo 1.

pred_knn_model_roma<- predict(knn_model_roma,
                           newdata = eva)

cm <- confusionMatrix(data = pred_knn_model_roma, as.factor(eva$layer))

cm
Confusion Matrix and Statistics

          Reference
Prediction   2   3   5   6   8  10  11  12  14  17
        2  378  15  92   6   4   5   0   0   1   0
        3    1  13   3   3   1   0   0   0   0   0
        5   78   4 295  18  20   8   1  12   5   0
        6    5   0  32 100   1   1   0   7   4   0
        8    4   0  10   0 105   0   0   0   0   0
        10   0   0   0   0   0   2   0   0   0   0
        11   0   0   1   0   0   0  80   3   0   0
        12   0   0  11  10   0   0   3 128  15   0
        14   0   0   5   7   0   0   2  17 271   0
        17   0   0   0   0   0   0   0   0   0 150

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

Statistics by Class:

                     Class: 2 Class: 3 Class: 5 Class: 6 Class: 8
Sensitivity            0.8112 0.406250   0.6570  0.69444  0.80153
Specificity            0.9164 0.995801   0.9019  0.97211  0.99225
Pos Pred Value         0.7545 0.619048   0.6689  0.66667  0.88235
Neg Pred Value         0.9387 0.990084   0.8971  0.97538  0.98570
Prevalence             0.2406 0.016520   0.2318  0.07434  0.06763
Detection Rate         0.1951 0.006711   0.1523  0.05163  0.05421
Detection Prevalence   0.2586 0.010842   0.2277  0.07744  0.06144
Balanced Accuracy      0.8638 0.701025   0.7794  0.83328  0.89689
                     Class: 10 Class: 11 Class: 12 Class: 14 Class: 17
Sensitivity           0.125000   0.93023   0.76647    0.9155   1.00000
Specificity           1.000000   0.99784   0.97797    0.9811   1.00000
Pos Pred Value        1.000000   0.95238   0.76647    0.8974   1.00000
Neg Pred Value        0.992765   0.99676   0.97797    0.9847   1.00000
Prevalence            0.008260   0.04440   0.08622    0.1528   0.07744
Detection Rate        0.001033   0.04130   0.06608    0.1399   0.07744
Detection Prevalence  0.001033   0.04337   0.08622    0.1559   0.07744
Balanced Accuracy     0.562500   0.96404   0.87222    0.9483   1.00000

Representación de Matriz de confusión

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_knn_model_roma2<- predict(knn_model_roma2,
                           newdata = eva)

cm <- confusionMatrix(data = pred_knn_model_roma2, as.factor(eva$layer))

cm
Confusion Matrix and Statistics

          Reference
Prediction   2   3   5   6   8  10  11  12  14  17
        2  381  18  95   6   6   5   0   0   1   0
        3    0  10   1   2   0   0   0   0   0   0
        5   78   4 295  19  19   8   1  14   6   0
        6    3   0  35  99   1   1   0   7   5   0
        8    4   0   8   0 105   0   0   0   0   0
        10   0   0   0   0   0   2   0   0   0   0
        11   0   0   1   0   0   0  80   3   0   0
        12   0   0  11  11   0   0   3 128  13   0
        14   0   0   3   7   0   0   2  15 271   0
        17   0   0   0   0   0   0   0   0   0 150

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

Statistics by Class:

                     Class: 2 Class: 3 Class: 5 Class: 6 Class: 8
Sensitivity            0.8176 0.312500   0.6570  0.68750  0.80153
Specificity            0.9109 0.998425   0.8999  0.97100  0.99336
Pos Pred Value         0.7441 0.769231   0.6644  0.65563  0.89744
Neg Pred Value         0.9404 0.988565   0.8969  0.97480  0.98571
Prevalence             0.2406 0.016520   0.2318  0.07434  0.06763
Detection Rate         0.1967 0.005163   0.1523  0.05111  0.05421
Detection Prevalence   0.2643 0.006711   0.2292  0.07796  0.06040
Balanced Accuracy      0.8643 0.655463   0.7784  0.82925  0.89744
                     Class: 10 Class: 11 Class: 12 Class: 14 Class: 17
Sensitivity           0.125000   0.93023   0.76647    0.9155   1.00000
Specificity           1.000000   0.99784   0.97853    0.9835   1.00000
Pos Pred Value        1.000000   0.95238   0.77108    0.9094   1.00000
Neg Pred Value        0.992765   0.99676   0.97798    0.9847   1.00000
Prevalence            0.008260   0.04440   0.08622    0.1528   0.07744
Detection Rate        0.001033   0.04130   0.06608    0.1399   0.07744
Detection Prevalence  0.001033   0.04337   0.08570    0.1538   0.07744
Balanced Accuracy     0.562500   0.96404   0.87250    0.9495   1.00000

Representación de Matriz de confusión

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_knn_model_roma <- raster::predict(roma_rst_lan, model=knn_model_roma)
plot(classi_knn_model_roma, main="Clasificación KNN Modelo 1", col = cols)

Predicción y visualización Modelo 2.

classi_knn_model_roma2 <- raster::predict(roma_rst_lan, model=knn_model_roma2)
plot(classi_knn_model_roma2, main="Clasificación KNN Modelo 2",col = cols)

LS0tCnRpdGxlOiAiQ2xhc2lmaWNhY2nDs24gZGUgaW3DoWdlbmVzIGNvbiBLTk4iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgTGlicmVyaWFzIGFwbGljYWRhcwoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocmFzdGVyKQpgYGAKCiMgQ2FyZ2EgZGUgbGFzIGJhbmRhcyBkZSBpbcOhZ2VuZXMgc2F0ZWxpdGFsZXMuCgpgYGB7cn0KYmFuZDEgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0IxLlRJRiIpCmJhbmQyIDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CMi5USUYiKQpiYW5kMyA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjMuVElGIikKYmFuZDQgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0I0LlRJRiIpCmJhbmQ1IDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CNS5USUYiKQpiYW5kNiA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjYuVElGIikKYmFuZDcgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0I3LlRJRiIpCmJhbmQxMCA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjEwLlRJRiIpCmJhbmQxMSA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjExLlRJRiIpCgpyb21hX3JzdF9sYW4gPC0gYnJpY2soYmFuZDEsYmFuZDIsYmFuZDMsYmFuZDQsYmFuZDUsYmFuZDYsYmFuZDcsYmFuZDEwLGJhbmQxMSkKbmFtZXMocm9tYV9yc3RfbGFuKSA8LSBjKHBhc3RlKCJCYW5kYSIsMTo3LHNlcD0iIiksIkJhbmRhMTAiLCJCYW5kYTExIikKYGBgCgojIFByb2Nlc2FkbyBkZSBkYXRvcwoKYGBge3J9CiNDYXJnYSBkZSBtdWVzdHJhcyBkZSBlbnRyZW5hbWllbnRvCnJzdFRyYWlucm9tYSA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvRGF0YVRyYWluaW5nL3JvbWVfbGN6X0dULnRpZiIpCgojIFJlbW92ZXIgY2Vyb3MgKGZvbmRvIE5BKQpyc3RUcmFpbnJvbWFbcnN0VHJhaW5yb21hPT0wXSA8LSBOQQoKIyBDb252ZXJ0aXIgYSB0aXBvIGRpc2NyZXRvCnJzdFRyYWlucm9tYSA8LSByYXRpZnkocnN0VHJhaW5yb21hKQoKIyBDYW1iaWFtb3MgZWwgbm9tYnJlCm5hbWVzKHJzdFRyYWlucm9tYSkgPC0gImxheWVyIgoKI011ZXN0cmFzIGRlIHZhbG9yZXMKdGFibGUodmFsdWVzKHJzdFRyYWlucm9tYSkpCgojIEVsaW1pbmFjacOzbiBkZSBOQXMgCnJzdERGbGFucm9tYS5kZiA8LSBuYS5vbWl0KHZhbHVlcyhzdGFjayhyc3RUcmFpbnJvbWEsIHJvbWFfcnN0X2xhbikpKQoKIyBWaXVhbGl6YW1vcyBsYXMgbXVlc3RyYXMKY29scyA8LSBjKCIjZmRlNzI1IiwiI2I1ZGUyYiIsICIjNmVjZTU4IiwiIzM1Yjc3OSIsICIjMWY5ZTg5IiwiIzI2ODI4ZSIsICIjMzE2ODhlIiwgIiMzZTQ5ODkiLCAiIzQ4Mjg3OCIsICIjNDQwMTU0IikKcGxvdChyc3RUcmFpbnJvbWEsIG1haW49Ik11ZXN0cmFzIGRlIGVudHJlbmFtaWVudG8gZGUgUm9tYSIsIGNvbCA9IGNvbHMpCgpgYGAKCiMgRmFzZSBkZSBDbGFzaWZpY2FjacOzbgoKYGBge3J9CiNTaSBlcyBsYSBwcmltZXJhIHZleiBzZSBlamVjdXRhLCBzaW5vIHlhIGVzdGEgZ3VhcmRhZG8gbG9zIGRhdG9zIGRlbCBkYXRhZnJhbWUuCiN3cml0ZS5jc3YocnN0REZsYW5yb21hLmRmLCJyc3RERlJvbWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojTGVlbW9zIGxvcyBkYXRvcwpzYW1wbGVzTGFucm9tYSA9IHJlYWQuY3N2KCJyc3RERlJvbWEuY3N2IikKCiMgU2UgZGl2aWRlIGxhcyBtdWVzdHJhcyBlbiA3MCB5IDMwCnRyYWlueCA9IGxpc3QoMCkKZXZhbHggPSBsaXN0KDApCmZvciAoaSBpbiAxOjE3KXsgIyBzZSBpdGVyYSBlbiB0b2RhcyBsYXMgcG9zaWJsZXMgY2F0ZWdvcsOtYXMKICBjbHMgPSBzYW1wbGVzTGFucm9tYVtzYW1wbGVzTGFucm9tYSRsYXllciA9PSBpLF0KICBzbXBsIDwtIGZsb29yKDAuNzAgKiBucm93KGNscykpCiAgdHQgPC0gc2FtcGxlKHNlcV9sZW4obnJvdyhjbHMpKSwgc2l6ZSA9IHNtcGwpCiAgdHJhaW54W1tpXV0gPC0gY2xzW3R0LF0KICBldmFseFtbaV1dIDwtIGNsc1stdHQsXQp9CgoKIyBTZSBjb21iaW5hIGVuIGRhdGFmcmFtZSByZWZlcmVuY2lhZG9zIHMgZW50cmVuYW1pZW50byB5IGV2YWx1YWNpw7NuCnRybiA9IGRvLmNhbGwocmJpbmQsIHRyYWlueCkgCmV2YSA9IGRvLmNhbGwocmJpbmQsIGV2YWx4KQpgYGAKCiMgQWxnb3JpdG1vIEtOTgoKYGBge3J9CiMgRGVmaW5pZW5kbyBlbCBjb250cm9sIGRlbCBlbnRyZW5hbWllbnRvCmZpdENvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgIyByZXBlYXRlZCBjcm9zcy12YWxpZGF0aW9uIG9mIHRoZSB0cmFpbmluZyBkYXRhCiAgICAgICAgICAgICAgICAgICBudW1iZXIgPSAxMCwgIyBudW1iZXIgb2YgZm9sZHMKICAgICAgICAgICAgICAgICAgIHJlcGVhdHMgPSA1KSAjIHZpZXcgdGhlIHRyYWluaW5nIGl0ZXJhdGlvbnMKCmtrbm4uZ3JpZCA8LSBleHBhbmQuZ3JpZChrbWF4ID0gYygzLCA1LCA3ICw5LCAxMSksIGRpc3RhbmNlID0gYygxLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGtlcm5lbCA9ICJvcHRpbWFsIikKIyBFbnRyZW5hbWllbnRvIGNvbiBLbm4Ka25uX21vZGVsX3JvbWE8LSBjYXJldDo6dHJhaW4oeCA9IHRyblssKDI6bmNvbCh0cm4pKV0sIHkgPSBhcy5mYWN0b3IodHJuJGxheWVyKSwKICAgICAgICAgICAgICAgbWV0aG9kID0gImtrbm4iLAogICAgICAgICAgICAgICBtZXRyaWM9IkFjY3VyYWN5IiwKICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBra25uLmdyaWQsCiAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiKSwKICAgICAgICAgICAgICAgdHJDb250cm9sID0gZml0Q29udHJvbCkKCiMgRW50cmVuYW1pZW50byBjb24gS25uCmtubl9tb2RlbF9yb21hMjwtIGNhcmV0Ojp0cmFpbih4ID0gdHJuWywoMjpuY29sKHRybikpXSwgeSA9IGFzLmZhY3Rvcih0cm4kbGF5ZXIpLAogICAgICAgICAgICAgICBtZXRob2QgPSAia2tubiIsCiAgICAgICAgICAgICAgIG1ldHJpYz0iQWNjdXJhY3kiLAogICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMTAsCiAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiKSwKICAgICAgICAgICAgICAgdHJDb250cm9sID0gZml0Q29udHJvbCkKCmBgYAoKIyBFeHBsb3JhY2nDs24gZGVsIG1vZGVsbwoKYGBge3J9CiNJbmZvcm1hY2nDs24gbW9kZWxvIGtubgpwcmludChrbm5fbW9kZWxfcm9tYSkKCnByaW50KGtubl9tb2RlbF9yb21hMikKCgojR3LDoWZpY2EgZGVsIG1vZGVsbyBrbm4KcGxvdChrbm5fbW9kZWxfcm9tYSkKCnBsb3Qoa25uX21vZGVsX3JvbWEyKQoKYGBgCgojIEV2YWx1YWNpw7NuIGRlbCBtb2RlbG8gMS4KCmBgYHtyfQpwcmVkX2tubl9tb2RlbF9yb21hPC0gcHJlZGljdChrbm5fbW9kZWxfcm9tYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGV2YSkKCmNtIDwtIGNvbmZ1c2lvbk1hdHJpeChkYXRhID0gcHJlZF9rbm5fbW9kZWxfcm9tYSwgYXMuZmFjdG9yKGV2YSRsYXllcikpCgpjbQpgYGAKCiMgUmVwcmVzZW50YWNpw7NuIGRlIE1hdHJpeiBkZSBjb25mdXNpw7NuCgpgYGB7cn0KY21fZCA8LSBhcy5kYXRhLmZyYW1lKGNtJHRhYmxlKQoKY21fc3QgPC1kYXRhLmZyYW1lKGNtJG92ZXJhbGwpCgpjbV9zdCRjbS5vdmVyYWxsIDwtIHJvdW5kKGNtX3N0JGNtLm92ZXJhbGwsMikKCmNtX3AgPC0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKGNtJHRhYmxlKSkKY21fZCRQZXJjIDwtIHJvdW5kKGNtX3AkRnJlcSoxMDAsMikKCiMgZGlidWphbmRvIGxhIG1hdHJpegpnZ3Bsb3QoZGF0YSA9IGNtX2QsIGFlcyh4ID0gUHJlZGljdGlvbiAsIHkgPSAgUmVmZXJlbmNlLCBmaWxsID0gRnJlcSkpKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIGx3ZCA9IDAuNSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZSgiIixGcmVxKSksIGNvbG9yID0gJ3doaXRlJywgc2l6ZSA9IDMuNSkrIAogIHRoZW1lX21pbmltYWwoKSArCiAgZ2d0aXRsZSgiTWF0cml6IGRlIENvbmZ1c2nDs24iKQpgYGAKCiMgRXZhbHVhY2nDs24gZGVsIG1vZGVsbyAyLgoKYGBge3J9CnByZWRfa25uX21vZGVsX3JvbWEyPC0gcHJlZGljdChrbm5fbW9kZWxfcm9tYTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBldmEpCgpjbSA8LSBjb25mdXNpb25NYXRyaXgoZGF0YSA9IHByZWRfa25uX21vZGVsX3JvbWEyLCBhcy5mYWN0b3IoZXZhJGxheWVyKSkKCmNtCmBgYAoKIyBSZXByZXNlbnRhY2nDs24gZGUgTWF0cml6IGRlIGNvbmZ1c2nDs24KCmBgYHtyfQpjbV9kIDwtIGFzLmRhdGEuZnJhbWUoY20kdGFibGUpCgpjbV9zdCA8LWRhdGEuZnJhbWUoY20kb3ZlcmFsbCkKCmNtX3N0JGNtLm92ZXJhbGwgPC0gcm91bmQoY21fc3QkY20ub3ZlcmFsbCwyKQoKY21fcCA8LSBhcy5kYXRhLmZyYW1lKHByb3AudGFibGUoY20kdGFibGUpKQpjbV9kJFBlcmMgPC0gcm91bmQoY21fcCRGcmVxKjEwMCwyKQoKIyBkaWJ1amFuZG8gbGEgbWF0cml6CmdncGxvdChkYXRhID0gY21fZCwgYWVzKHggPSBQcmVkaWN0aW9uICwgeSA9ICBSZWZlcmVuY2UsIGZpbGwgPSBGcmVxKSkrCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIiwgbHdkID0gMC41KSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlKCIiLEZyZXEpKSwgY29sb3IgPSAnd2hpdGUnLCBzaXplID0gMy41KSsgCiAgdGhlbWVfbWluaW1hbCgpICsKICBnZ3RpdGxlKCJNYXRyaXogZGUgQ29uZnVzacOzbiIpCmBgYAoKIyBQcmVkaWNjacOzbiB5IHZpc3VhbGl6YWNpw7NuIE1vZGVsbyAxLgoKYGBge3J9CmNsYXNzaV9rbm5fbW9kZWxfcm9tYSA8LSByYXN0ZXI6OnByZWRpY3Qocm9tYV9yc3RfbGFuLCBtb2RlbD1rbm5fbW9kZWxfcm9tYSkKcGxvdChjbGFzc2lfa25uX21vZGVsX3JvbWEsIG1haW49IkNsYXNpZmljYWNpw7NuIEtOTiBNb2RlbG8gMSIsIGNvbCA9IGNvbHMpCmBgYAojIFByZWRpY2Npw7NuIHkgdmlzdWFsaXphY2nDs24gTW9kZWxvIDIuCgpgYGB7cn0KY2xhc3NpX2tubl9tb2RlbF9yb21hMiA8LSByYXN0ZXI6OnByZWRpY3Qocm9tYV9yc3RfbGFuLCBtb2RlbD1rbm5fbW9kZWxfcm9tYTIpCnBsb3QoY2xhc3NpX2tubl9tb2RlbF9yb21hMiwgbWFpbj0iQ2xhc2lmaWNhY2nDs24gS05OIE1vZGVsbyAyIixjb2wgPSBjb2xzKQpgYGAKCgo=