Librerias aplicadas
library(caret)
Loading required package: ggplot2
Loading required package: lattice
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)

NA
NA
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 Random Forest
# Definiendo el control del entrenamiento
fitControl2 <- trainControl(method = "cv", # repeated cross-validation of the training data
number = 10)
# Entrenamiento con rf
rf_model1<- caret::train(x = trn[,(2:ncol(trn))], y = as.factor(trn$layer),
method = "rf",
metric="Accuracy",
trControl = fitControl2,
preProcess = c("center", "scale"),
tuneLength = 8)
rf_grid <- expand.grid(mtry=1:20)
rf_model2<- caret::train(x = trn[,(2:ncol(trn))], y = as.factor(trn$layer),
method = "rf",
metric="Accuracy",
trControl = fitControl2,
preProcess = c("center", "scale"),
tuneGrid = rf_grid)
Exploración del modelo
#Información modelo rf
print(rf_model1)
Random Forest
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, 4054, 4055, 4052, 4050, 4053, ...
Resampling results across tuning parameters:
mtry Accuracy Kappa
2 0.7756809 0.7309041
3 0.7750019 0.7302374
4 0.7763327 0.7319464
5 0.7774533 0.7332007
6 0.7778863 0.7338051
7 0.7765584 0.7322465
8 0.7745545 0.7298555
9 0.7752167 0.7306680
Accuracy was used to select the optimal model using the largest value.
The final value used for the model was mtry = 6.
print(rf_model1$finalModel)
Call:
randomForest(x = x, y = y, mtry = min(param$mtry, ncol(x)))
Type of random forest: classification
Number of trees: 500
No. of variables tried at each split: 6
OOB estimate of error rate: 22.61%
Confusion matrix:
2 3 5 6 8 10 11 12 14 17 class.error
2 856 11 194 8 13 1 0 0 2 0 0.21105991
3 34 18 16 4 0 0 0 0 0 0 0.75000000
5 213 4 706 53 24 4 1 20 21 0 0.32504780
6 10 2 62 198 0 1 0 44 19 0 0.41071429
8 12 0 40 1 249 0 0 0 2 0 0.18092105
10 11 0 10 3 0 11 0 0 0 0 0.68571429
11 0 0 1 0 0 0 192 4 1 0 0.03030303
12 1 0 37 21 0 1 5 286 37 0 0.26288660
14 0 0 24 16 2 0 0 28 618 0 0.10174419
17 0 0 0 0 0 0 0 0 0 350 0.00000000
#Gráfica del modelo rf
plot(rf_model1)

print(rf_model2) #modelo 2
Random Forest
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: 4052, 4053, 4050, 4049, 4052, 4050, ...
Resampling results across tuning parameters:
mtry Accuracy Kappa
1 0.7745519 0.7292963
2 0.7752407 0.7303372
3 0.7796803 0.7358375
4 0.7763415 0.7319764
5 0.7767909 0.7324974
6 0.7776748 0.7336103
7 0.7750096 0.7304041
8 0.7752426 0.7306268
9 0.7705848 0.7251480
10 0.7776926 0.7336253
11 0.7727987 0.7278105
12 0.7732461 0.7282577
13 0.7736925 0.7288293
14 0.7770155 0.7327538
15 0.7721334 0.7270123
16 0.7725750 0.7275754
17 0.7739039 0.7291407
18 0.7750170 0.7304012
19 0.7743503 0.7296546
20 0.7763557 0.7320517
Accuracy was used to select the optimal model using the largest value.
The final value used for the model was mtry = 3.
print(rf_model2$finalModel)
Call:
randomForest(x = x, y = y, mtry = min(param$mtry, ncol(x)))
Type of random forest: classification
Number of trees: 500
No. of variables tried at each split: 3
OOB estimate of error rate: 21.83%
Confusion matrix:
2 3 5 6 8 10 11 12 14 17 class.error
2 864 10 183 11 14 1 0 0 2 0 0.20368664
3 39 18 12 3 0 0 0 0 0 0 0.75000000
5 205 3 722 51 22 4 1 15 23 0 0.30975143
6 12 2 57 200 0 1 0 45 19 0 0.40476190
8 12 0 38 2 251 0 0 0 1 0 0.17434211
10 12 0 10 3 0 10 0 0 0 0 0.71428571
11 0 0 1 0 0 0 192 4 1 0 0.03030303
12 1 0 37 19 0 1 2 293 35 0 0.24484536
14 0 0 24 14 1 0 0 30 619 0 0.10029070
17 0 0 0 0 0 0 0 0 0 350 0.00000000
plot(rf_model2) #modelo 2

Evaluación del modelo 1.
pred_rf_model1<- predict(rf_model1,newdata = eva[,-1])
cm <- confusionMatrix(data = pred_rf_model1, as.factor(eva$layer))
cm
Confusion Matrix and Statistics
Reference
Prediction 2 3 5 6 8 10 11 12 14 17
2 372 15 80 3 7 3 0 0 1 0
3 2 13 1 3 0 0 0 0 0 0
5 85 3 317 18 19 9 1 12 3 0
6 3 1 24 102 1 1 0 10 7 0
8 3 0 8 0 104 0 0 0 0 0
10 0 0 0 0 0 2 0 0 0 0
11 0 0 0 0 0 0 80 3 0 0
12 0 0 13 11 0 0 3 123 13 0
14 1 0 6 7 0 1 2 19 272 0
17 0 0 0 0 0 0 0 0 0 150
Overall Statistics
Accuracy : 0.7925
95% CI : (0.7737, 0.8103)
No Information Rate : 0.2406
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.7515
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: 2 Class: 3 Class: 5 Class: 6 Class: 8 Class: 10
Sensitivity 0.7983 0.406250 0.7060 0.70833 0.79389 0.125000
Specificity 0.9259 0.996850 0.8992 0.97379 0.99391 1.000000
Pos Pred Value 0.7734 0.684211 0.6788 0.68456 0.90435 1.000000
Neg Pred Value 0.9354 0.990094 0.9102 0.97651 0.98518 0.992765
Prevalence 0.2406 0.016520 0.2318 0.07434 0.06763 0.008260
Detection Rate 0.1920 0.006711 0.1637 0.05266 0.05369 0.001033
Detection Prevalence 0.2483 0.009809 0.2411 0.07692 0.05937 0.001033
Balanced Accuracy 0.8621 0.701550 0.8026 0.84106 0.89390 0.562500
Class: 11 Class: 12 Class: 14 Class: 17
Sensitivity 0.93023 0.73653 0.9189 1.00000
Specificity 0.99838 0.97740 0.9781 1.00000
Pos Pred Value 0.96386 0.75460 0.8831 1.00000
Neg Pred Value 0.99676 0.97520 0.9853 1.00000
Prevalence 0.04440 0.08622 0.1528 0.07744
Detection Rate 0.04130 0.06350 0.1404 0.07744
Detection Prevalence 0.04285 0.08415 0.1590 0.07744
Balanced Accuracy 0.96431 0.85696 0.9485 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_rf_model2<- predict(rf_model2,newdata = eva[,-1])
cm <- confusionMatrix(data = pred_rf_model2, as.factor(eva$layer))
cm
Confusion Matrix and Statistics
Reference
Prediction 2 3 5 6 8 10 11 12 14 17
2 373 17 76 4 8 2 0 0 1 0
3 0 11 0 0 0 0 0 0 0 0
5 87 4 320 22 19 10 1 14 2 0
6 2 0 24 101 1 1 0 9 4 0
8 3 0 8 0 103 0 0 0 0 0
10 0 0 0 0 0 2 0 0 0 0
11 0 0 0 0 0 0 80 2 0 0
12 0 0 14 11 0 0 3 122 14 0
14 1 0 7 6 0 1 2 20 275 0
17 0 0 0 0 0 0 0 0 0 150
Overall Statistics
Accuracy : 0.7935
95% CI : (0.7748, 0.8113)
No Information Rate : 0.2406
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.7523
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: 2 Class: 3 Class: 5 Class: 6 Class: 8 Class: 10
Sensitivity 0.8004 0.343750 0.7127 0.70139 0.78626 0.125000
Specificity 0.9266 1.000000 0.8931 0.97713 0.99391 1.000000
Pos Pred Value 0.7755 1.000000 0.6681 0.71127 0.90351 1.000000
Neg Pred Value 0.9361 0.989097 0.9115 0.97604 0.98464 0.992765
Prevalence 0.2406 0.016520 0.2318 0.07434 0.06763 0.008260
Detection Rate 0.1926 0.005679 0.1652 0.05214 0.05318 0.001033
Detection Prevalence 0.2483 0.005679 0.2473 0.07331 0.05885 0.001033
Balanced Accuracy 0.8635 0.671875 0.8029 0.83926 0.89008 0.562500
Class: 11 Class: 12 Class: 14 Class: 17
Sensitivity 0.93023 0.73054 0.9291 1.00000
Specificity 0.99892 0.97627 0.9775 1.00000
Pos Pred Value 0.97561 0.74390 0.8814 1.00000
Neg Pred Value 0.99677 0.97462 0.9871 1.00000
Prevalence 0.04440 0.08622 0.1528 0.07744
Detection Rate 0.04130 0.06298 0.1420 0.07744
Detection Prevalence 0.04233 0.08467 0.1611 0.07744
Balanced Accuracy 0.96458 0.85341 0.9533 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 del modelo 1.
classi_rf_model1 <- raster::predict(roma_rst_lan, model=rf_model1)
plot(classi_rf_model1, main="Clasificación Random Forest Modelo 1",col = cols)

Predicción y visualización del modelo 2.
classi_rf_model2 <- raster::predict(roma_rst_lan, model=rf_model2)
plot(classi_rf_model2, main="Clasificación Random Forest Modelo 2",col = cols)

LS0tCnRpdGxlOiAiQ2xhc2lmaWNhY2nDs24gZGUgaW3DoWdlbmVzIGNvbiBSYW5kb20gRm9yZXN0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIExpYnJlcmlhcyBhcGxpY2FkYXMKCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KHJhc3RlcikKYGBgCgojIENhcmdhIGRlIGxhcyBiYW5kYXMgZGUgaW3DoWdlbmVzIHNhdGVsaXRhbGVzLgoKYGBge3J9CmJhbmQxIDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CMS5USUYiKQpiYW5kMiA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjIuVElGIikKYmFuZDMgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0IzLlRJRiIpCmJhbmQ0IDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CNC5USUYiKQpiYW5kNSA8LSByYXN0ZXIoIi9Vc2Vycy9lbGlhbmFhbGl6Y2h1cXVpbGxhbnF1aWp1bGNhcGFyaS9Eb2N1bWVudHMvd29ya3NwYWNlLXIvZGVtb3MvY2FwdHVyYXMtbGFuZHNhdC9yb21hL0xDODE5MTAzMTIwMTMyMDhMR04wMC9MQzgxOTEwMzEyMDEzMjA4TEdOMDBfQjUuVElGIikKYmFuZDYgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0I2LlRJRiIpCmJhbmQ3IDwtIHJhc3RlcigiL1VzZXJzL2VsaWFuYWFsaXpjaHVxdWlsbGFucXVpanVsY2FwYXJpL0RvY3VtZW50cy93b3Jrc3BhY2Utci9kZW1vcy9jYXB0dXJhcy1sYW5kc2F0L3JvbWEvTEM4MTkxMDMxMjAxMzIwOExHTjAwL0xDODE5MTAzMTIwMTMyMDhMR04wMF9CNy5USUYiKQpiYW5kMTAgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0IxMC5USUYiKQpiYW5kMTEgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL2NhcHR1cmFzLWxhbmRzYXQvcm9tYS9MQzgxOTEwMzEyMDEzMjA4TEdOMDAvTEM4MTkxMDMxMjAxMzIwOExHTjAwX0IxMS5USUYiKQoKcm9tYV9yc3RfbGFuIDwtIGJyaWNrKGJhbmQxLGJhbmQyLGJhbmQzLGJhbmQ0LGJhbmQ1LGJhbmQ2LGJhbmQ3LGJhbmQxMCxiYW5kMTEpCm5hbWVzKHJvbWFfcnN0X2xhbikgPC0gYyhwYXN0ZSgiQmFuZGEiLDE6NyxzZXA9IiIpLCJCYW5kYTEwIiwiQmFuZGExMSIpCmBgYAoKIyBQcm9jZXNhZG8gZGUgZGF0b3MKCmBgYHtyfQojQ2FyZ2EgZGUgbXVlc3RyYXMgZGUgZW50cmVuYW1pZW50bwpyc3RUcmFpbnJvbWEgPC0gcmFzdGVyKCIvVXNlcnMvZWxpYW5hYWxpemNodXF1aWxsYW5xdWlqdWxjYXBhcmkvRG9jdW1lbnRzL3dvcmtzcGFjZS1yL2RlbW9zL0RhdGFUcmFpbmluZy9yb21lX2xjel9HVC50aWYiKQoKIyBSZW1vdmVyIGNlcm9zIChmb25kbyBOQSkKcnN0VHJhaW5yb21hW3JzdFRyYWlucm9tYT09MF0gPC0gTkEKCiMgQ29udmVydGlyIGEgdGlwbyBkaXNjcmV0bwpyc3RUcmFpbnJvbWEgPC0gcmF0aWZ5KHJzdFRyYWlucm9tYSkKCiMgQ2FtYmlhbW9zIGVsIG5vbWJyZQpuYW1lcyhyc3RUcmFpbnJvbWEpIDwtICJsYXllciIKCiNNdWVzdHJhcyBkZSB2YWxvcmVzCnRhYmxlKHZhbHVlcyhyc3RUcmFpbnJvbWEpKQoKIyBFbGltaW5hY2nDs24gZGUgTkFzIApyc3RERmxhbnJvbWEuZGYgPC0gbmEub21pdCh2YWx1ZXMoc3RhY2socnN0VHJhaW5yb21hLCByb21hX3JzdF9sYW4pKSkKCiMgVml1YWxpemFtb3MgbGFzIG11ZXN0cmFzCmNvbHMgPC0gYygiI2ZkZTcyNSIsIiNiNWRlMmIiLCAiIzZlY2U1OCIsIiMzNWI3NzkiLCAiIzFmOWU4OSIsIiMyNjgyOGUiLCAiIzMxNjg4ZSIsICIjM2U0OTg5IiwgIiM0ODI4NzgiLCAiIzQ0MDE1NCIpCnBsb3QocnN0VHJhaW5yb21hLCBtYWluPSJNdWVzdHJhcyBkZSBlbnRyZW5hbWllbnRvIGRlIFJvbWEiLCBjb2wgPSBjb2xzKQoKCmBgYAoKIyBGYXNlIGRlIENsYXNpZmljYWNpw7NuCgpgYGB7cn0KI1NpIGVzIGxhIHByaW1lcmEgdmV6IHNlIGVqZWN1dGEsIHNpbm8geWEgZXN0YSBndWFyZGFkbyBsb3MgZGF0b3MgZGVsIGRhdGFmcmFtZS4KI3dyaXRlLmNzdihyc3RERmxhbnJvbWEuZGYsInJzdERGUm9tYS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiNMZWVtb3MgbG9zIGRhdG9zCnNhbXBsZXNMYW5yb21hID0gcmVhZC5jc3YoInJzdERGUm9tYS5jc3YiKQoKIyBTZSBkaXZpZGUgbGFzIG11ZXN0cmFzIGVuIDcwIHkgMzAKdHJhaW54ID0gbGlzdCgwKQpldmFseCA9IGxpc3QoMCkKZm9yIChpIGluIDE6MTcpeyAjIHNlIGl0ZXJhIGVuIHRvZGFzIGxhcyBwb3NpYmxlcyBjYXRlZ29yw61hcwogIGNscyA9IHNhbXBsZXNMYW5yb21hW3NhbXBsZXNMYW5yb21hJGxheWVyID09IGksXQogIHNtcGwgPC0gZmxvb3IoMC43MCAqIG5yb3coY2xzKSkKICB0dCA8LSBzYW1wbGUoc2VxX2xlbihucm93KGNscykpLCBzaXplID0gc21wbCkKICB0cmFpbnhbW2ldXSA8LSBjbHNbdHQsXQogIGV2YWx4W1tpXV0gPC0gY2xzWy10dCxdCn0KCgojIFNlIGNvbWJpbmEgZW4gZGF0YWZyYW1lIHJlZmVyZW5jaWFkb3MgcyBlbnRyZW5hbWllbnRvIHkgZXZhbHVhY2nDs24KdHJuID0gZG8uY2FsbChyYmluZCwgdHJhaW54KSAKZXZhID0gZG8uY2FsbChyYmluZCwgZXZhbHgpCmBgYAoKIyBBbGdvcml0bW8gUmFuZG9tIEZvcmVzdAoKYGBge3J9CiMgRGVmaW5pZW5kbyBlbCBjb250cm9sIGRlbCBlbnRyZW5hbWllbnRvCmZpdENvbnRyb2wyIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCAjIHJlcGVhdGVkIGNyb3NzLXZhbGlkYXRpb24gb2YgdGhlIHRyYWluaW5nIGRhdGEKICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDEwKSAKCgojIEVudHJlbmFtaWVudG8gY29uIHJmCnJmX21vZGVsMTwtIGNhcmV0Ojp0cmFpbih4ID0gdHJuWywoMjpuY29sKHRybikpXSwgeSA9IGFzLmZhY3Rvcih0cm4kbGF5ZXIpLAogICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbWV0cmljPSJBY2N1cmFjeSIsIAogICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRDb250cm9sMiwKICAgICAgICAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIpLAogICAgICAgICAgICAgICAgICAgICAgICB0dW5lTGVuZ3RoICA9IDgpCgpyZl9ncmlkIDwtIGV4cGFuZC5ncmlkKG10cnk9MToyMCkKCnJmX21vZGVsMjwtIGNhcmV0Ojp0cmFpbih4ID0gdHJuWywoMjpuY29sKHRybikpXSwgeSA9IGFzLmZhY3Rvcih0cm4kbGF5ZXIpLAogICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbWV0cmljPSJBY2N1cmFjeSIsIAogICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRDb250cm9sMiwKICAgICAgICAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIpLAogICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IHJmX2dyaWQpCgpgYGAKCiMgRXhwbG9yYWNpw7NuIGRlbCBtb2RlbG8KCmBgYHtyfQojSW5mb3JtYWNpw7NuIG1vZGVsbyByZgpwcmludChyZl9tb2RlbDEpCgpwcmludChyZl9tb2RlbDEkZmluYWxNb2RlbCkKCiNHcsOhZmljYSBkZWwgbW9kZWxvIHJmCnBsb3QocmZfbW9kZWwxKQoKcHJpbnQocmZfbW9kZWwyKSAjbW9kZWxvIDIKCnByaW50KHJmX21vZGVsMiRmaW5hbE1vZGVsKQoKcGxvdChyZl9tb2RlbDIpICNtb2RlbG8gMgpgYGAKCgojIEV2YWx1YWNpw7NuIGRlbCBtb2RlbG8gMS4KCmBgYHtyfQpwcmVkX3JmX21vZGVsMTwtIHByZWRpY3QocmZfbW9kZWwxLG5ld2RhdGEgPSBldmFbLC0xXSkKCgpjbSA8LSBjb25mdXNpb25NYXRyaXgoZGF0YSA9IHByZWRfcmZfbW9kZWwxLCBhcy5mYWN0b3IoZXZhJGxheWVyKSkKY20KYGBgCgojIFJlcHJlc2VudGFjacOzbiBkZSBNYXRyaXogZGUgY29uZnVzacOzbgoKYGBge3J9CmNtX2QgPC0gYXMuZGF0YS5mcmFtZShjbSR0YWJsZSkKCmNtX3N0IDwtZGF0YS5mcmFtZShjbSRvdmVyYWxsKQoKY21fc3QkY20ub3ZlcmFsbCA8LSByb3VuZChjbV9zdCRjbS5vdmVyYWxsLDIpCgpjbV9wIDwtIGFzLmRhdGEuZnJhbWUocHJvcC50YWJsZShjbSR0YWJsZSkpCmNtX2QkUGVyYyA8LSByb3VuZChjbV9wJEZyZXEqMTAwLDIpCgojIGRpYnVqYW5kbyBsYSBtYXRyaXoKZ2dwbG90KGRhdGEgPSBjbV9kLCBhZXMoeCA9IFByZWRpY3Rpb24gLCB5ID0gIFJlZmVyZW5jZSwgZmlsbCA9IEZyZXEpKSsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCBsd2QgPSAwLjUpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUoIiIsRnJlcSkpLCBjb2xvciA9ICd3aGl0ZScsIHNpemUgPSAzLjUpKyAKICB0aGVtZV9taW5pbWFsKCkgKwogIGdndGl0bGUoIk1hdHJpeiBkZSBDb25mdXNpw7NuIikKYGBgCgojIEV2YWx1YWNpw7NuIGRlbCBtb2RlbG8gMgoKYGBge3J9CnByZWRfcmZfbW9kZWwyPC0gcHJlZGljdChyZl9tb2RlbDIsbmV3ZGF0YSA9IGV2YVssLTFdKQoKCmNtIDwtIGNvbmZ1c2lvbk1hdHJpeChkYXRhID0gcHJlZF9yZl9tb2RlbDIsIGFzLmZhY3RvcihldmEkbGF5ZXIpKQpjbQpgYGAKCiMgUmVwcmVzZW50YWNpw7NuIGRlIE1hdHJpeiBkZSBjb25mdXNpw7NuCgpgYGB7cn0KY21fZCA8LSBhcy5kYXRhLmZyYW1lKGNtJHRhYmxlKQoKY21fc3QgPC1kYXRhLmZyYW1lKGNtJG92ZXJhbGwpCgpjbV9zdCRjbS5vdmVyYWxsIDwtIHJvdW5kKGNtX3N0JGNtLm92ZXJhbGwsMikKCmNtX3AgPC0gYXMuZGF0YS5mcmFtZShwcm9wLnRhYmxlKGNtJHRhYmxlKSkKY21fZCRQZXJjIDwtIHJvdW5kKGNtX3AkRnJlcSoxMDAsMikKCiMgZGlidWphbmRvIGxhIG1hdHJpegpnZ3Bsb3QoZGF0YSA9IGNtX2QsIGFlcyh4ID0gUHJlZGljdGlvbiAsIHkgPSAgUmVmZXJlbmNlLCBmaWxsID0gRnJlcSkpKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIGx3ZCA9IDAuNSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZSgiIixGcmVxKSksIGNvbG9yID0gJ3doaXRlJywgc2l6ZSA9IDMuNSkrIAogIHRoZW1lX21pbmltYWwoKSArCiAgZ2d0aXRsZSgiTWF0cml6IGRlIENvbmZ1c2nDs24iKQpgYGAKCiMgUHJlZGljY2nDs24geSB2aXN1YWxpemFjacOzbiBkZWwgbW9kZWxvIDEuCgpgYGB7cn0KY2xhc3NpX3JmX21vZGVsMSA8LSByYXN0ZXI6OnByZWRpY3Qocm9tYV9yc3RfbGFuLCBtb2RlbD1yZl9tb2RlbDEpCnBsb3QoY2xhc3NpX3JmX21vZGVsMSwgbWFpbj0iQ2xhc2lmaWNhY2nDs24gUmFuZG9tIEZvcmVzdCBNb2RlbG8gMSIsY29sID0gY29scykKYGBgCiMgUHJlZGljY2nDs24geSB2aXN1YWxpemFjacOzbiBkZWwgbW9kZWxvIDIuCgpgYGB7cn0KY2xhc3NpX3JmX21vZGVsMiA8LSByYXN0ZXI6OnByZWRpY3Qocm9tYV9yc3RfbGFuLCBtb2RlbD1yZl9tb2RlbDIpCnBsb3QoY2xhc3NpX3JmX21vZGVsMiwgbWFpbj0iQ2xhc2lmaWNhY2nDs24gUmFuZG9tIEZvcmVzdCBNb2RlbG8gMiIsY29sID0gY29scykKYGBgCgoK