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=