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
fitControl2 <- trainControl(method = "cv", # cross-validation of the training data
number = 10)
kknn.grid <- expand.grid(kmax = c(3, 5, 7 ,9, 11), distance = c(1, 2),
kernel = "optimal")
# Entrenamiento con Knn
knn_model1<- caret::train(x = trn[,(2:ncol(trn))], y = as.factor(trn$layer),
method = "kknn",
metric="Accuracy",
tuneGrid = kknn.grid,
preProcess = c("center", "scale"),
trControl = fitControl2)
# Entrenamiento con Knn
knn_model2<- caret::train(x = trn[,(2:ncol(trn))], y = as.factor(trn$layer),
method = "kknn",
metric="Accuracy",
tuneLength = 10,
preProcess = c("center", "scale"),
trControl = fitControl2)
Exploración del modelo
#Información modelo knn
print(knn_model1)
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)
Summary of sample sizes: 4050, 4053, 4055, 4053, 4052, 4052, ...
Resampling results across tuning parameters:
kmax distance Accuracy Kappa
3 1 0.7347554 0.6837141
3 2 0.7342916 0.6833186
5 1 0.7529774 0.7052921
5 2 0.7545141 0.7072748
7 1 0.7598525 0.7131221
7 2 0.7645113 0.7189057
9 1 0.7665108 0.7209409
9 2 0.7693993 0.7245885
11 1 0.7696280 0.7243541
11 2 0.7722739 0.7279195
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_model2)
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)
Summary of sample sizes: 4051, 4051, 4054, 4054, 4053, 4050, ...
Resampling results across tuning parameters:
kmax Accuracy Kappa
5 0.7527758 0.7051197
7 0.7596569 0.7130361
9 0.7638792 0.7178881
11 0.7661039 0.7204611
13 0.7696585 0.7245084
15 0.7729959 0.7283432
17 0.7729959 0.7283385
19 0.7729978 0.7283203
21 0.7729978 0.7283203
23 0.7725524 0.7277814
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 = 21, distance = 2 and kernel = optimal.
#Gráfica del modelo knn
plot(knn_model1)

plot(knn_model2)

Evaluación del modelo 1.
pred_knn_model1<- predict(knn_model1,
newdata = eva)
cm <- confusionMatrix(data = pred_knn_model1, as.factor(eva$layer))
cm
Confusion Matrix and Statistics
Reference
Prediction 2 3 5 6 8 10 11 12 14 17
2 398 9 54 2 4 9 0 0 0 0
3 3 21 1 1 0 0 0 0 0 0
5 54 2 359 14 14 5 0 9 1 0
6 4 0 14 114 0 0 0 4 7 0
8 7 0 8 0 113 0 0 0 0 0
10 0 0 1 0 0 2 0 0 0 0
11 0 0 2 0 0 0 83 3 0 0
12 0 0 7 9 0 0 2 138 8 0
14 0 0 3 4 0 0 1 13 280 0
17 0 0 0 0 0 0 0 0 0 150
Overall Statistics
Accuracy : 0.856
95% CI : (0.8395, 0.8713)
No Information Rate : 0.2406
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.8279
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: 2 Class: 3 Class: 5 Class: 6 Class: 8 Class: 10
Sensitivity 0.8541 0.65625 0.7996 0.79167 0.86260 0.125000
Specificity 0.9470 0.99738 0.9335 0.98383 0.99169 0.999479
Pos Pred Value 0.8361 0.80769 0.7838 0.79720 0.88281 0.666667
Neg Pred Value 0.9535 0.99424 0.9391 0.98328 0.99005 0.992761
Prevalence 0.2406 0.01652 0.2318 0.07434 0.06763 0.008260
Detection Rate 0.2055 0.01084 0.1853 0.05885 0.05834 0.001033
Detection Prevalence 0.2457 0.01342 0.2364 0.07383 0.06608 0.001549
Balanced Accuracy 0.9005 0.82681 0.8665 0.88775 0.92714 0.562240
Class: 11 Class: 12 Class: 14 Class: 17
Sensitivity 0.96512 0.82635 0.9459 1.00000
Specificity 0.99730 0.98531 0.9872 1.00000
Pos Pred Value 0.94318 0.84146 0.9302 1.00000
Neg Pred Value 0.99838 0.98364 0.9902 1.00000
Prevalence 0.04440 0.08622 0.1528 0.07744
Detection Rate 0.04285 0.07124 0.1446 0.07744
Detection Prevalence 0.04543 0.08467 0.1554 0.07744
Balanced Accuracy 0.98121 0.90583 0.9666 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_model2<- predict(knn_model2,
newdata = eva)
cm <- confusionMatrix(data = pred_knn_model2, as.factor(eva$layer))
cm
Confusion Matrix and Statistics
Reference
Prediction 2 3 5 6 8 10 11 12 14 17
2 395 13 63 1 6 9 0 0 0 0
3 3 17 2 1 0 0 0 0 0 0
5 57 2 342 16 20 5 0 11 1 0
6 4 0 21 111 0 0 0 4 9 0
8 7 0 9 0 105 1 0 0 0 0
10 0 0 1 0 0 1 0 0 0 0
11 0 0 2 0 0 0 83 3 0 0
12 0 0 7 10 0 0 2 134 8 0
14 0 0 2 5 0 0 1 15 278 0
17 0 0 0 0 0 0 0 0 0 150
Overall Statistics
Accuracy : 0.8343
95% CI : (0.817, 0.8506)
No Information Rate : 0.2406
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.8018
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: 2 Class: 3 Class: 5 Class: 6 Class: 8 Class: 10
Sensitivity 0.8476 0.531250 0.7617 0.77083 0.80153 0.0625000
Specificity 0.9375 0.996850 0.9247 0.97881 0.99059 0.9994794
Pos Pred Value 0.8111 0.739130 0.7533 0.74497 0.86066 0.5000000
Neg Pred Value 0.9510 0.992163 0.9278 0.98154 0.98567 0.9922481
Prevalence 0.2406 0.016520 0.2318 0.07434 0.06763 0.0082602
Detection Rate 0.2039 0.008776 0.1766 0.05731 0.05421 0.0005163
Detection Prevalence 0.2514 0.011874 0.2344 0.07692 0.06298 0.0010325
Balanced Accuracy 0.8925 0.764050 0.8432 0.87482 0.89606 0.5309897
Class: 11 Class: 12 Class: 14 Class: 17
Sensitivity 0.96512 0.80240 0.9392 1.00000
Specificity 0.99730 0.98475 0.9860 1.00000
Pos Pred Value 0.94318 0.83230 0.9236 1.00000
Neg Pred Value 0.99838 0.98142 0.9890 1.00000
Prevalence 0.04440 0.08622 0.1528 0.07744
Detection Rate 0.04285 0.06918 0.1435 0.07744
Detection Prevalence 0.04543 0.08312 0.1554 0.07744
Balanced Accuracy 0.98121 0.89357 0.9626 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_model1 <- raster::predict(roma_rst_lan, model=knn_model1)
plot(classi_knn_model1, main="Clasificación KNN Modelo 1", col = cols)

Predicción y visualización Modelo 2.
classi_knn_model2 <- raster::predict(roma_rst_lan, model=knn_model2)
plot(classi_knn_model2, main="Clasificación KNN Modelo 2",col = cols)

LS0tCnRpdGxlOiAiQ2xhc2lmaWNhY2nDs24gZGUgaW3DoWdlbmVzIGNvbiBLTk4iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgTGlicmVyaWFzIGFwbGljYWRhcwoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocmFzdGVyKQpgYGAKCiMgQ2FyZ2EgZGUgbGFzIGJhbmRhcyBkZSBpbcOhZ2VuZXMgc2F0ZWxpdGFsZXMuCgpgYGB7cn0KYmFuZDEgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0IxLlRJRiIpCmJhbmQyIDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CMi5USUYiKQpiYW5kMyA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjMuVElGIikKYmFuZDQgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0I0LlRJRiIpCmJhbmQ1IDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CNS5USUYiKQpiYW5kNiA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjYuVElGIikKYmFuZDcgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0I3LlRJRiIpCmJhbmQxMCA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjEwLlRJRiIpCmJhbmQxMSA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjExLlRJRiIpCgpyb21hX3JzdF9sYW4gPC0gYnJpY2soYmFuZDEsYmFuZDIsYmFuZDMsYmFuZDQsYmFuZDUsYmFuZDYsYmFuZDcsYmFuZDEwLGJhbmQxMSkKbmFtZXMocm9tYV9yc3RfbGFuKSA8LSBjKHBhc3RlKCJCYW5kYSIsMTo3LHNlcD0iIiksIkJhbmRhMTAiLCJCYW5kYTExIikKYGBgCgojIFByb2Nlc2FkbyBkZSBkYXRvcwoKYGBge3J9CiNDYXJnYSBkZSBtdWVzdHJhcyBkZSBlbnRyZW5hbWllbnRvCnJzdFRyYWlucm9tYSA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvRGF0YVRyYWluaW5nL3JvbWVfbGN6X0dULnRpZiIpCgojIFJlbW92ZXIgY2Vyb3MgKGZvbmRvIE5BKQpyc3RUcmFpbnJvbWFbcnN0VHJhaW5yb21hPT0wXSA8LSBOQQoKIyBDb252ZXJ0aXIgYSB0aXBvIGRpc2NyZXRvCnJzdFRyYWlucm9tYSA8LSByYXRpZnkocnN0VHJhaW5yb21hKQoKIyBDYW1iaWFtb3MgZWwgbm9tYnJlCm5hbWVzKHJzdFRyYWlucm9tYSkgPC0gImxheWVyIgoKI011ZXN0cmFzIGRlIHZhbG9yZXMKdGFibGUodmFsdWVzKHJzdFRyYWlucm9tYSkpCgojIEVsaW1pbmFjacOzbiBkZSBOQXMgCnJzdERGbGFucm9tYS5kZiA8LSBuYS5vbWl0KHZhbHVlcyhzdGFjayhyc3RUcmFpbnJvbWEsIHJvbWFfcnN0X2xhbikpKQoKIyBWaXVhbGl6YW1vcyBsYXMgbXVlc3RyYXMKY29scyA8LSBjKCIjZmRlNzI1IiwiI2I1ZGUyYiIsICIjNmVjZTU4IiwiIzM1Yjc3OSIsICIjMWY5ZTg5IiwiIzI2ODI4ZSIsICIjMzE2ODhlIiwgIiMzZTQ5ODkiLCAiIzQ4Mjg3OCIsICIjNDQwMTU0IikKcGxvdChyc3RUcmFpbnJvbWEsIG1haW49Ik11ZXN0cmFzIGRlIGVudHJlbmFtaWVudG8gZGUgUm9tYSIsIGNvbCA9IGNvbHMpCgpgYGAKCiMgRmFzZSBkZSBDbGFzaWZpY2FjacOzbgoKYGBge3J9CiNTaSBlcyBsYSBwcmltZXJhIHZleiBzZSBlamVjdXRhLCBzaW5vIHlhIGVzdGEgZ3VhcmRhZG8gbG9zIGRhdG9zIGRlbCBkYXRhZnJhbWUuCiN3cml0ZS5jc3YocnN0REZsYW5yb21hLmRmLCJyc3RERlJvbWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojTGVlbW9zIGxvcyBkYXRvcwpzYW1wbGVzTGFucm9tYSA9IHJlYWQuY3N2KCJyc3RERlJvbWEuY3N2IikKCiMgU2UgZGl2aWRlIGxhcyBtdWVzdHJhcyBlbiA3MCB5IDMwCnRyYWlueCA9IGxpc3QoMCkKZXZhbHggPSBsaXN0KDApCmZvciAoaSBpbiAxOjE3KXsgIyBzZSBpdGVyYSBlbiB0b2RhcyBsYXMgcG9zaWJsZXMgY2F0ZWdvcsOtYXMKICBjbHMgPSBzYW1wbGVzTGFucm9tYVtzYW1wbGVzTGFucm9tYSRsYXllciA9PSBpLF0KICBzbXBsIDwtIGZsb29yKDAuNzAgKiBucm93KGNscykpCiAgdHQgPC0gc2FtcGxlKHNlcV9sZW4obnJvdyhjbHMpKSwgc2l6ZSA9IHNtcGwpCiAgdHJhaW54W1tpXV0gPC0gY2xzW3R0LF0KICBldmFseFtbaV1dIDwtIGNsc1stdHQsXQp9CgoKIyBTZSBjb21iaW5hIGVuIGRhdGFmcmFtZSByZWZlcmVuY2lhZG9zIHMgZW50cmVuYW1pZW50byB5IGV2YWx1YWNpw7NuCnRybiA9IGRvLmNhbGwocmJpbmQsIHRyYWlueCkgCmV2YSA9IGRvLmNhbGwocmJpbmQsIGV2YWx4KQpgYGAKCiMgQWxnb3JpdG1vIEtOTgoKYGBge3J9CiMgRGVmaW5pZW5kbyBlbCBjb250cm9sIGRlbCBlbnRyZW5hbWllbnRvCmZpdENvbnRyb2wyIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCAjIGNyb3NzLXZhbGlkYXRpb24gb2YgdGhlIHRyYWluaW5nIGRhdGEKICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDEwKQoKa2tubi5ncmlkIDwtIGV4cGFuZC5ncmlkKGttYXggPSBjKDMsIDUsIDcgLDksIDExKSwgZGlzdGFuY2UgPSBjKDEsIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAga2VybmVsID0gIm9wdGltYWwiKQojIEVudHJlbmFtaWVudG8gY29uIEtubgprbm5fbW9kZWwxPC0gY2FyZXQ6OnRyYWluKHggPSB0cm5bLCgyOm5jb2wodHJuKSldLCB5ID0gYXMuZmFjdG9yKHRybiRsYXllciksCiAgICAgICAgICAgICAgIG1ldGhvZCA9ICJra25uIiwKICAgICAgICAgICAgICAgbWV0cmljPSJBY2N1cmFjeSIsCiAgICAgICAgICAgICAgIHR1bmVHcmlkID0ga2tubi5ncmlkLAogICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiksCiAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGZpdENvbnRyb2wyKQoKIyBFbnRyZW5hbWllbnRvIGNvbiBLbm4Ka25uX21vZGVsMjwtIGNhcmV0Ojp0cmFpbih4ID0gdHJuWywoMjpuY29sKHRybikpXSwgeSA9IGFzLmZhY3Rvcih0cm4kbGF5ZXIpLAogICAgICAgICAgICAgICBtZXRob2QgPSAia2tubiIsCiAgICAgICAgICAgICAgIG1ldHJpYz0iQWNjdXJhY3kiLAogICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMTAsCiAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiKSwKICAgICAgICAgICAgICAgdHJDb250cm9sID0gZml0Q29udHJvbDIpCgpgYGAKCiMgRXhwbG9yYWNpw7NuIGRlbCBtb2RlbG8KCmBgYHtyfQojSW5mb3JtYWNpw7NuIG1vZGVsbyBrbm4KcHJpbnQoa25uX21vZGVsMSkKCnByaW50KGtubl9tb2RlbDIpCgoKI0dyw6FmaWNhIGRlbCBtb2RlbG8ga25uCnBsb3Qoa25uX21vZGVsMSkKCnBsb3Qoa25uX21vZGVsMikKCmBgYAoKIyBFdmFsdWFjacOzbiBkZWwgbW9kZWxvIDEuCgpgYGB7cn0KcHJlZF9rbm5fbW9kZWwxPC0gcHJlZGljdChrbm5fbW9kZWwxLAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZXZhKQoKY20gPC0gY29uZnVzaW9uTWF0cml4KGRhdGEgPSBwcmVkX2tubl9tb2RlbDEsIGFzLmZhY3RvcihldmEkbGF5ZXIpKQoKY20KYGBgCgojIFJlcHJlc2VudGFjacOzbiBkZSBNYXRyaXogZGUgY29uZnVzacOzbgoKYGBge3J9CmNtX2QgPC0gYXMuZGF0YS5mcmFtZShjbSR0YWJsZSkKCmNtX3N0IDwtZGF0YS5mcmFtZShjbSRvdmVyYWxsKQoKY21fc3QkY20ub3ZlcmFsbCA8LSByb3VuZChjbV9zdCRjbS5vdmVyYWxsLDIpCgpjbV9wIDwtIGFzLmRhdGEuZnJhbWUocHJvcC50YWJsZShjbSR0YWJsZSkpCmNtX2QkUGVyYyA8LSByb3VuZChjbV9wJEZyZXEqMTAwLDIpCgojIGRpYnVqYW5kbyBsYSBtYXRyaXoKZ2dwbG90KGRhdGEgPSBjbV9kLCBhZXMoeCA9IFByZWRpY3Rpb24gLCB5ID0gIFJlZmVyZW5jZSwgZmlsbCA9IEZyZXEpKSsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCBsd2QgPSAwLjUpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUoIiIsRnJlcSkpLCBjb2xvciA9ICd3aGl0ZScsIHNpemUgPSAzLjUpKyAKICB0aGVtZV9taW5pbWFsKCkgKwogIGdndGl0bGUoIk1hdHJpeiBkZSBDb25mdXNpw7NuIikKYGBgCgojIEV2YWx1YWNpw7NuIGRlbCBtb2RlbG8gMi4KCmBgYHtyfQpwcmVkX2tubl9tb2RlbDI8LSBwcmVkaWN0KGtubl9tb2RlbDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBldmEpCgpjbSA8LSBjb25mdXNpb25NYXRyaXgoZGF0YSA9IHByZWRfa25uX21vZGVsMiwgYXMuZmFjdG9yKGV2YSRsYXllcikpCgpjbQpgYGAKCiMgUmVwcmVzZW50YWNpw7NuIGRlIE1hdHJpeiBkZSBjb25mdXNpw7NuCgpgYGB7cn0KY21fZCA8LSBhcy5kYXRhLmZyYW1lKGNtJHRhYmxlKQoKY21fc3QgPC1kYXRhLmZyYW1lKGNtJG92ZXJhbGwpCgpjbV9zdCRjbS5vdmVyYWxsIDwtIHJvdW5kKGNtX3N0JGNtLm92ZXJhbGwsMikKCmNtX3AgPC0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKGNtJHRhYmxlKSkKY21fZCRQZXJjIDwtIHJvdW5kKGNtX3AkRnJlcSoxMDAsMikKCiMgZGlidWphbmRvIGxhIG1hdHJpegpnZ3Bsb3QoZGF0YSA9IGNtX2QsIGFlcyh4ID0gUHJlZGljdGlvbiAsIHkgPSAgUmVmZXJlbmNlLCBmaWxsID0gRnJlcSkpKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIGx3ZCA9IDAuNSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZSgiIixGcmVxKSksIGNvbG9yID0gJ3doaXRlJywgc2l6ZSA9IDMuNSkrIAogIHRoZW1lX21pbmltYWwoKSArCiAgZ2d0aXRsZSgiTWF0cml6IGRlIENvbmZ1c2nDs24iKQpgYGAKCiMgUHJlZGljY2nDs24geSB2aXN1YWxpemFjacOzbiBNb2RlbG8gMS4KCmBgYHtyfQpjbGFzc2lfa25uX21vZGVsMSA8LSByYXN0ZXI6OnByZWRpY3Qocm9tYV9yc3RfbGFuLCBtb2RlbD1rbm5fbW9kZWwxKQpwbG90KGNsYXNzaV9rbm5fbW9kZWwxLCBtYWluPSJDbGFzaWZpY2FjacOzbiBLTk4gTW9kZWxvIDEiLCBjb2wgPSBjb2xzKQpgYGAKIyBQcmVkaWNjacOzbiB5IHZpc3VhbGl6YWNpw7NuIE1vZGVsbyAyLgoKYGBge3J9CmNsYXNzaV9rbm5fbW9kZWwyIDwtIHJhc3Rlcjo6cHJlZGljdChyb21hX3JzdF9sYW4sIG1vZGVsPWtubl9tb2RlbDIpCnBsb3QoY2xhc3NpX2tubl9tb2RlbDIsIG1haW49IkNsYXNpZmljYWNpw7NuIEtOTiBNb2RlbG8gMiIsY29sID0gY29scykKYGBgCgoK