Unsupervised Classification

library(raster)
landsat5 <- stack("C:/Users/USS/Desktop/Pedro/2019 - 2020/Percepcion remota/P4/rsdata/rs/centralvalley-2011LT5.tif")
names(landsat5) <- c('blue', 'green', 'red', 'NIR', 'SWIR1', 'SWIR2')
landsat5
class      : RasterStack 
dimensions : 1230, 1877, 2308710, 6  (nrow, ncol, ncell, nlayers)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.9258, -121.42, 37.85402, 38.1855  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
names      : blue, green, red, NIR, SWIR1, SWIR2 

Question 1: Make a 3-band False Color Composite plot of “landsat5”.

landsatRGB <- landsat5[[c(3,2,1)]] # Creamos un objeto con las capas RGB de landsat
# Representamos True color y False color para comparar

par(mfrow = c(1,2)) 
plotRGB(landsatRGB, r=3, g=2, b=1, axes=TRUE, stretch="lin", main="Landsat5 True Color Composite")
plotRGB(landsatRGB, r=1, g=2, b=3, axes=TRUE, stretch="lin", main="Landsat5 False Color Composite")

# Realizaremos una clasificación no supervisada en un subconjunto espacial de la capa ndvi

ndvi <- (landsat5[['NIR']] - landsat5[['red']]) / (landsat5[['NIR']] + landsat5[['red']])
ndvi
class      : RasterLayer 
dimensions : 1230, 1877, 2308710  (nrow, ncol, ncell)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.9258, -121.42, 37.85402, 38.1855  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : layer 
values     : -0.4932148, 0.8103668  (min, max)

kmeans classification

# Creamos una extensión para recortar la capa ndvi

e <- extent(-121.807, -121.725, 38.004, 38.072)
# Se recorta la imagen Landsat con la extensión

ndvi <- crop(ndvi, e)
ndvi # Si comparamos con el antiguo NVDI se ve que ha sido recortado (menores dimensiones)
class      : RasterLayer 
dimensions : 252, 304, 76608  (nrow, ncol, ncell)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.807, -121.725, 38.00413, 38.07204  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : layer 
values     : -0.3360085, 0.7756007  (min, max)
# convertir el ráster a vector / matriz

nr <- getValues(ndvi)
str(nr)
 num [1:76608] 0.245 0.236 0.272 0.277 0.277 ...
# Es importante configurar el generador de semillas porque "kmeans" inicia los centros en ubicaciones aleatorias

set.seed (99)
# Queremos crear 10 grupos, permitir 500 iteraciones, comenzar con 5 conjuntos aleatorios usando el método "Lloyd"

kmncluster <- kmeans(na.omit(nr), centers = 10, iter.max = 500, nstart = 5, algorithm="Lloyd")
# kmeans devuelve un objeto de la clase "kmeans"

str(kmncluster)
List of 9
 $ cluster     : int [1:76608] 4 4 3 3 3 3 3 4 4 4 ...
 $ centers     : num [1:10, 1] 0.55425 0.00498 0.29997 0.20892 -0.20902 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:10] "1" "2" "3" "4" ...
  .. ..$ : NULL
 $ totss       : num 6459
 $ withinss    : num [1:10] 5.69 6.13 4.91 4.9 5.75 ...
 $ tot.withinss: num 55.8
 $ betweenss   : num 6403
 $ size        : int [1:10] 8932 4550 7156 6807 11672 8624 8736 5040 9893 5198
 $ iter        : int 108
 $ ifault      : NULL
 - attr(*, "class")= chr "kmeans"
# Se usa el objeto ndvi para establecer los valores del clúster en un nuevo ráster

knr <- setValues(ndvi, kmncluster$cluster)
# También se puede hacer de esta forma

knr <- raster(ndvi)
values(knr) <- kmncluster$cluster
knr
class      : RasterLayer 
dimensions : 252, 304, 76608  (nrow, ncol, ncell)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.807, -121.725, 38.00413, 38.07204  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : layer 
values     : 1, 10  (min, max)
head(knr)
   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1  4 4 3 3 3 3 3 4 4  4  4  4  4  4  4  4  4  4  4  4
2  4 4 3 3 3 3 3 4 4  4  4  4  4  4  4  4  4  4  4  4
3  4 3 3 3 3 3 3 4 4  4  4  4  4  4  4  4  4  4  4 10
4  3 3 3 3 3 3 3 4 4  4  4  3  4  4  4  4  4  4  4  4
5  3 3 4 4 4 3 3 4 4  4  4  3  4  4  4  4  4  4  4  4
6  4 4 4 4 4 4 4 4 4  4  4  4  4  4  4  3  4  4  4  4
7  4 4 4 4 4 4 4 4 4  4  4  4  4  4  4  3  3  3  3  4
8  4 4 4 4 4 4 4 4 4  4  3  3  3  7  7  7  7  7  7  7
9  3 3 3 4 4 4 4 4 3  3  3  3  7  9  9  9  9  7  7  7
10 3 3 3 3 3 3 3 4 3  3  3  7  7  9  9  9  9  9  7  7
# Se define un vector de color para los 10 clústers

mycolor <- c("#fef65b","#ff0000", "#daa520","#0000ff","#0000ff","#00ff00","#cbbeb5",
             "#c3ff5b", "#ff7373", "#00ff00", "#808080")
# Se representa el NDVI y la clasificación supervisada para comprobar los grupos creados

par(mfrow = c(1,2))
plot(ndvi, col = rev(terrain.colors(10)), main = 'Landsat-NDVI')
plot(knr, main = 'Unsupervised classification', col = mycolor )

Question 2: Plot 3-band RGB of “landsat5” for the subset (extent “e”) and result of “kmeans” clustering side-by-side and make a table of land-use land-cover labels for the clusters. E.g. cluster 4 and 5 are water.

# Se recorta la imagen Landsat5 con la extensión "e"

Landsat5_crop <- crop(landsat5, e)
Landsat5_crop
class      : RasterBrick 
dimensions : 252, 304, 76608, 6  (nrow, ncol, ncell, nlayers)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.807, -121.725, 38.00413, 38.07204  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      :        blue,       green,         red,         NIR,       SWIR1,       SWIR2 
min values : 0.084590562, 0.055765331, 0.040395390, 0.034050025, 0.010290771, 0.006807715 
max values :   0.3684652,   0.5407562,   0.5847761,   0.5988820,   0.5135725,   0.5523322 
# Comenzamos con la banda 1 (Azul) de Landsat5

L_Blue <- Landsat5_crop[[1]]
L_Blue
class      : RasterLayer 
dimensions : 252, 304, 76608  (nrow, ncol, ncell)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.807, -121.725, 38.00413, 38.07204  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : blue 
values     : 0.08459056, 0.3684652  (min, max)
# convertir el ráster a vector / matriz

matriz_Blue <- getValues(L_Blue)
str(matriz_Blue)
 num [1:76608] 0.136 0.139 0.139 0.147 0.147 ...
# Establecemos semillas 

set.seed (99)
# Queremos crear 10 grupos, permitir 500 iteraciones, comenzar con 5 conjuntos aleatorios usando el método "Lloyd"

Cluster_Blue <- kmeans(na.omit(matriz_Blue), centers = 10, iter.max = 500, nstart = 5, algorithm="Lloyd")
# kmeans devuelve un objeto de la clase "kmeans"

str(Cluster_Blue)
List of 9
 $ cluster     : int [1:76608] 9 10 10 10 10 10 9 9 9 9 ...
 $ centers     : num [1:10, 1] 0.111 0.192 0.105 0.164 0.262 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:10] "1" "2" "3" "4" ...
  .. ..$ : NULL
 $ totss       : num 30.8
 $ withinss    : num [1:10] 0.0889 0.1003 0.0555 0.0878 0.1034 ...
 $ tot.withinss: num 0.831
 $ betweenss   : num 30
 $ size        : int [1:10] 21214 771 13839 1845 200 99 17248 9229 7297 4866
 $ iter        : int 136
 $ ifault      : NULL
 - attr(*, "class")= chr "kmeans"
# Se usa el objeto L_Blue recortado para establecer los valores del clúster en un nuevo ráster

Lay_Blue <- setValues(L_Blue, Cluster_Blue$cluster)
Lay_Blue
class      : RasterLayer 
dimensions : 252, 304, 76608  (nrow, ncol, ncell)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.807, -121.725, 38.00413, 38.07204  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : blue 
values     : 1, 10  (min, max)
head(Lay_Blue)
    1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
1   9 10 10 10 10 10  9  9  9  9  9  9  9  9  9  9 10 10 10 10
2  10 10 10 10 10 10  9  9  9  9  9  9 10 10 10 10 10 10 10 10
3  10  9  9  9  9  9  9  9  9  9  9  9 10 10 10 10 10 10 10 10
4  10 10  9  9  9  9  9  9  9  9  7  9 10 10 10 10 10 10 10 10
5  10 10 10  9  9  9  9  9  9  9  9 10 10 10 10 10  9  9  9  9
6   4  4 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
7  10  4 10  4  4  4  4  4  4  4 10 10 10 10 10 10 10 10 10 10
8  10 10 10 10 10  4  4  4  4  4 10 10 10 10 10  9  9  9  9  9
9  10 10 10 10 10 10  4  4 10 10 10 10  9  9  9  9  9  9  9  9
10 10 10 10 10 10 10 10 10 10 10 10 10  9  9  9  9  9  9  9  9
# Banda 2 (Verde)

L_Green <- Landsat5_crop[[2]]
# convertir el ráster a vector / matriz

matriz_Green <- getValues(L_Green)
str(matriz_Green)
 num [1:76608] 0.139 0.14 0.143 0.144 0.144 ...
# Establecemos semillas 

set.seed (99)
# Queremos crear 10 grupos, permitir 500 iteraciones, comenzar con 5 conjuntos aleatorios usando el método "Lloyd"

Cluster_Green <- kmeans(na.omit(matriz_Green), centers = 10, iter.max = 500, nstart = 5, algorithm="Lloyd")
# kmeans devuelve un objeto de la clase "kmeans"

str(Cluster_Green)
List of 9
 $ cluster     : int [1:76608] 6 6 6 6 6 6 6 6 6 6 ...
 $ centers     : num [1:10, 1] 0.095 0.199 0.1595 0.1073 0.0846 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:10] "1" "2" "3" "4" ...
  .. ..$ : NULL
 $ totss       : num 60.6
 $ withinss    : num [1:10] 0.193 0.269 0.238 0.22 0.128 ...
 $ tot.withinss: num 1.95
 $ betweenss   : num 58.6
 $ size        : int [1:10] 19215 1059 3648 19585 12313 7599 9887 194 89 3019
 $ iter        : int 65
 $ ifault      : NULL
 - attr(*, "class")= chr "kmeans"
# Se usa el objeto L_Green recortado para establecer los valores del clúster en un nuevo ráster

Lay_Green <- setValues(L_Green, Cluster_Green$cluster)
Lay_Green
class      : RasterLayer 
dimensions : 252, 304, 76608  (nrow, ncol, ncell)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.807, -121.725, 38.00413, 38.07204  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : green 
values     : 1, 10  (min, max)
head(Lay_Green)
   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1  6 6 6 6 6 6 6 6 6  6  6  6  6  6  6  6  6  3  3  3
2  6 6 6 6 6 6 6 6 6  6  6  6  6  6  6  6  3  3  3  3
3  6 6 6 6 6 6 6 6 6  6  6  6  3  3  3  3  3  3  3  3
4  6 6 6 6 6 6 6 7 7  7  7  6  3  3  3  3  3  6  6  6
5  3 6 3 6 6 6 7 7 6  6  6  6  6  6  6  3  3  6  6  6
6  3 3 3 3 3 3 3 3 3  3  3  3  3  3  3  3  3  3  3  3
7  3 3 3 3 3 3 3 3 3  3  3  3  3  3  3  3  3  3  3  3
8  3 3 3 3 3 3 3 3 3  3  3  3  3  6  6  6  6  6  6  6
9  3 3 3 3 3 3 3 3 3  3  3  3  6  6  6  6  6  6  6  6
10 3 3 3 3 3 3 3 3 3  3  3  3  6  7  7  7  6  6  6  6
# Banda 3 (Roja)

L_Red <- Landsat5_crop[[3]]
# convertir el ráster a vector / matriz

matriz_Red <- getValues(L_Red)
str(matriz_Red)
 num [1:76608] 0.15 0.153 0.157 0.159 0.159 ...
# Establecemos semillas 

set.seed (99)
# Queremos crear 10 grupos, permitir 500 iteraciones, comenzar con 5 conjuntos aleatorios usando el método "Lloyd"

Cluster_Red <- kmeans(na.omit(matriz_Red), centers = 10, iter.max = 500, nstart = 5, algorithm="Lloyd")
# kmeans devuelve un objeto de la clase "kmeans"

str(Cluster_Red)
List of 9
 $ cluster     : int [1:76608] 5 5 8 8 8 8 8 8 5 5 ...
 $ centers     : num [1:10, 1] 0.2012 0.0848 0.0587 0.4989 0.1419 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:10] "1" "2" "3" "4" ...
  .. ..$ : NULL
 $ totss       : num 114
 $ withinss    : num [1:10] 0.415 0.255 0.231 0.214 0.285 ...
 $ tot.withinss: num 2.87
 $ betweenss   : num 111
 $ size        : int [1:10] 1938 17063 7546 89 6824 9985 242 4517 19849 8555
 $ iter        : int 67
 $ ifault      : NULL
 - attr(*, "class")= chr "kmeans"
# Se usa el objeto La_Red recortado para establecer los valores del clúster en un nuevo ráster

Lay_Red <- setValues(L_Red, Cluster_Red$cluster)
Lay_Red
class      : RasterLayer 
dimensions : 252, 304, 76608  (nrow, ncol, ncell)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.807, -121.725, 38.00413, 38.07204  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : red 
values     : 1, 10  (min, max)
# Comprobamos que cada capa tiene grupos distintos

head(Lay_Blue)
    1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
1   9 10 10 10 10 10  9  9  9  9  9  9  9  9  9  9 10 10 10 10
2  10 10 10 10 10 10  9  9  9  9  9  9 10 10 10 10 10 10 10 10
3  10  9  9  9  9  9  9  9  9  9  9  9 10 10 10 10 10 10 10 10
4  10 10  9  9  9  9  9  9  9  9  7  9 10 10 10 10 10 10 10 10
5  10 10 10  9  9  9  9  9  9  9  9 10 10 10 10 10  9  9  9  9
6   4  4 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
7  10  4 10  4  4  4  4  4  4  4 10 10 10 10 10 10 10 10 10 10
8  10 10 10 10 10  4  4  4  4  4 10 10 10 10 10  9  9  9  9  9
9  10 10 10 10 10 10  4  4 10 10 10 10  9  9  9  9  9  9  9  9
10 10 10 10 10 10 10 10 10 10 10 10 10  9  9  9  9  9  9  9  9
head(Lay_Green)
   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1  6 6 6 6 6 6 6 6 6  6  6  6  6  6  6  6  6  3  3  3
2  6 6 6 6 6 6 6 6 6  6  6  6  6  6  6  6  3  3  3  3
3  6 6 6 6 6 6 6 6 6  6  6  6  3  3  3  3  3  3  3  3
4  6 6 6 6 6 6 6 7 7  7  7  6  3  3  3  3  3  6  6  6
5  3 6 3 6 6 6 7 7 6  6  6  6  6  6  6  3  3  6  6  6
6  3 3 3 3 3 3 3 3 3  3  3  3  3  3  3  3  3  3  3  3
7  3 3 3 3 3 3 3 3 3  3  3  3  3  3  3  3  3  3  3  3
8  3 3 3 3 3 3 3 3 3  3  3  3  3  6  6  6  6  6  6  6
9  3 3 3 3 3 3 3 3 3  3  3  3  6  6  6  6  6  6  6  6
10 3 3 3 3 3 3 3 3 3  3  3  3  6  7  7  7  6  6  6  6
head(Lay_Red)
   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1  5 5 8 8 8 8 8 8 5  5  5  5  5  5  5  5  8  8  8  8
2  5 8 8 8 8 8 8 5 8  8  8  5  8  8  8  8  8  1  1  1
3  8 5 5 5 5 5 5 5 8  8  5  5  8  8  8  8  8  8  8  8
4  8 8 8 5 5 5 5 5 5  5  5  5  8  8  8  8  8  8  8  8
5  8 8 8 8 8 8 5 5 5  5  8  8  8  8  8  8  8  8  8  8
6  1 1 1 1 1 1 8 8 8  8  8  8  8  8  8  8  8  8  8  8
7  1 1 1 1 1 1 1 1 1  1  1  1  1  1  1  1  1  1  1  1
8  8 1 1 1 1 1 1 1 1  1  1  8  8  8  8  8  5  5  5  8
9  8 8 8 1 1 1 1 1 8  8  8  8  5  5  5  5  5  5  5  8
10 8 8 8 8 8 8 8 1 8  8  8  8  5 10 10 10  5  5  5  5
# Creamos un raster con las 3 bandas RGB

LandsatRGB <- stack(Lay_Blue, Lay_Green, Lay_Red)
LandsatRGB
class      : RasterStack 
dimensions : 252, 304, 76608, 3  (nrow, ncol, ncell, nlayers)
resolution : 0.0002694946, 0.0002694946  (x, y)
extent     : -121.807, -121.725, 38.00413, 38.07204  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
names      : blue, green, red 
min values :    1,     1,   1 
max values :   10,    10,  10 
# Se representa el Landsat5_crop y la clasificación supervisada para comprobar los grupos creados

plotRGB(LandsatRGB, r = 3, g = 2, b = 1, axes = TRUE, stretch = "lin", main = "Landsat5 True Color Composite")

LS0tDQp0aXRsZTogIlByw6FjdGljYSA0IHBlcmNlcGNpw7NuIHJlbW90YSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgVW5zdXBlcnZpc2VkIENsYXNzaWZpY2F0aW9uDQoNCmBgYHtyfQ0KbGlicmFyeShyYXN0ZXIpDQpgYGANCg0KYGBge3J9DQpsYW5kc2F0NSA8LSBzdGFjaygiQzovVXNlcnMvVVNTL0Rlc2t0b3AvUGVkcm8vMjAxOSAtIDIwMjAvUGVyY2VwY2lvbiByZW1vdGEvUDQvcnNkYXRhL3JzL2NlbnRyYWx2YWxsZXktMjAxMUxUNS50aWYiKQ0KYGBgDQoNCmBgYHtyfQ0KbmFtZXMobGFuZHNhdDUpIDwtIGMoJ2JsdWUnLCAnZ3JlZW4nLCAncmVkJywgJ05JUicsICdTV0lSMScsICdTV0lSMicpDQpgYGANCg0KYGBge3J9DQpsYW5kc2F0NQ0KYGBgDQoNCiMjIyBRdWVzdGlvbiAxOiBNYWtlIGEgMy1iYW5kIEZhbHNlIENvbG9yIENvbXBvc2l0ZSBwbG90IG9mICJsYW5kc2F0NSIuDQoNCmBgYHtyfQ0KbGFuZHNhdFJHQiA8LSBsYW5kc2F0NVtbYygzLDIsMSldXSAjIENyZWFtb3MgdW4gb2JqZXRvIGNvbiBsYXMgY2FwYXMgUkdCIGRlIGxhbmRzYXQNCmBgYA0KDQpgYGB7cn0NCiMgUmVwcmVzZW50YW1vcyBUcnVlIGNvbG9yIHkgRmFsc2UgY29sb3IgcGFyYSBjb21wYXJhcg0KDQpwYXIobWZyb3cgPSBjKDEsMikpIA0KcGxvdFJHQihsYW5kc2F0UkdCLCByPTMsIGc9MiwgYj0xLCBheGVzPVRSVUUsIHN0cmV0Y2g9ImxpbiIsIG1haW49IkxhbmRzYXQ1IFRydWUgQ29sb3IgQ29tcG9zaXRlIikNCnBsb3RSR0IobGFuZHNhdFJHQiwgcj0xLCBnPTIsIGI9MywgYXhlcz1UUlVFLCBzdHJldGNoPSJsaW4iLCBtYWluPSJMYW5kc2F0NSBGYWxzZSBDb2xvciBDb21wb3NpdGUiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZWFsaXphcmVtb3MgdW5hIGNsYXNpZmljYWNpw7NuIG5vIHN1cGVydmlzYWRhIGVuIHVuIHN1YmNvbmp1bnRvIGVzcGFjaWFsIGRlIGxhIGNhcGEgbmR2aQ0KDQpuZHZpIDwtIChsYW5kc2F0NVtbJ05JUiddXSAtIGxhbmRzYXQ1W1sncmVkJ11dKSAvIChsYW5kc2F0NVtbJ05JUiddXSArIGxhbmRzYXQ1W1sncmVkJ11dKQ0KYGBgDQoNCmBgYHtyfQ0KbmR2aQ0KYGBgDQoNCiMga21lYW5zIGNsYXNzaWZpY2F0aW9uDQoNCmBgYHtyfQ0KIyBDcmVhbW9zIHVuYSBleHRlbnNpw7NuIHBhcmEgcmVjb3J0YXIgbGEgY2FwYSBuZHZpDQoNCmUgPC0gZXh0ZW50KC0xMjEuODA3LCAtMTIxLjcyNSwgMzguMDA0LCAzOC4wNzIpDQpgYGANCg0KYGBge3J9DQojIFNlIHJlY29ydGEgbGEgaW1hZ2VuIExhbmRzYXQgY29uIGxhIGV4dGVuc2nDs24NCg0KbmR2aSA8LSBjcm9wKG5kdmksIGUpDQpgYGANCg0KYGBge3J9DQpuZHZpICMgU2kgY29tcGFyYW1vcyBjb24gZWwgYW50aWd1byBOVkRJIHNlIHZlIHF1ZSBoYSBzaWRvIHJlY29ydGFkbyAobWVub3JlcyBkaW1lbnNpb25lcykNCmBgYA0KDQpgYGB7cn0NCiMgY29udmVydGlyIGVsIHLDoXN0ZXIgYSB2ZWN0b3IgLyBtYXRyaXoNCg0KbnIgPC0gZ2V0VmFsdWVzKG5kdmkpDQpzdHIobnIpDQpgYGANCg0KYGBge3J9DQojIEVzIGltcG9ydGFudGUgY29uZmlndXJhciBlbCBnZW5lcmFkb3IgZGUgc2VtaWxsYXMgcG9ycXVlICJrbWVhbnMiIGluaWNpYSBsb3MgY2VudHJvcyBlbiB1YmljYWNpb25lcyBhbGVhdG9yaWFzDQoNCnNldC5zZWVkICg5OSkNCmBgYA0KDQpgYGB7cn0NCiMgUXVlcmVtb3MgY3JlYXIgMTAgZ3J1cG9zLCBwZXJtaXRpciA1MDAgaXRlcmFjaW9uZXMsIGNvbWVuemFyIGNvbiA1IGNvbmp1bnRvcyBhbGVhdG9yaW9zIHVzYW5kbyBlbCBtw6l0b2RvICJMbG95ZCINCg0Ka21uY2x1c3RlciA8LSBrbWVhbnMobmEub21pdChuciksIGNlbnRlcnMgPSAxMCwgaXRlci5tYXggPSA1MDAsIG5zdGFydCA9IDUsIGFsZ29yaXRobT0iTGxveWQiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBrbWVhbnMgZGV2dWVsdmUgdW4gb2JqZXRvIGRlIGxhIGNsYXNlICJrbWVhbnMiDQoNCnN0cihrbW5jbHVzdGVyKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTZSB1c2EgZWwgb2JqZXRvIG5kdmkgcGFyYSBlc3RhYmxlY2VyIGxvcyB2YWxvcmVzIGRlbCBjbMO6c3RlciBlbiB1biBudWV2byByw6FzdGVyDQoNCmtuciA8LSBzZXRWYWx1ZXMobmR2aSwga21uY2x1c3RlciRjbHVzdGVyKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUYW1iacOpbiBzZSBwdWVkZSBoYWNlciBkZSBlc3RhIGZvcm1hDQoNCmtuciA8LSByYXN0ZXIobmR2aSkNCnZhbHVlcyhrbnIpIDwtIGttbmNsdXN0ZXIkY2x1c3Rlcg0KYGBgDQoNCmBgYHtyfQ0Ka25yDQpgYGANCg0KYGBge3J9DQpoZWFkKGtucikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBTZSBkZWZpbmUgdW4gdmVjdG9yIGRlIGNvbG9yIHBhcmEgbG9zIDEwIGNsw7pzdGVycw0KDQpteWNvbG9yIDwtIGMoIiNmZWY2NWIiLCIjZmYwMDAwIiwgIiNkYWE1MjAiLCIjMDAwMGZmIiwiIzAwMDBmZiIsIiMwMGZmMDAiLCIjY2JiZWI1IiwNCiAgICAgICAgICAgICAiI2MzZmY1YiIsICIjZmY3MzczIiwgIiMwMGZmMDAiLCAiIzgwODA4MCIpDQpgYGANCg0KYGBge3J9DQojIFNlIHJlcHJlc2VudGEgZWwgTkRWSSB5IGxhIGNsYXNpZmljYWNpw7NuIHN1cGVydmlzYWRhIHBhcmEgY29tcHJvYmFyIGxvcyBncnVwb3MgY3JlYWRvcw0KDQpwYXIobWZyb3cgPSBjKDEsMikpDQpwbG90KG5kdmksIGNvbCA9IHJldih0ZXJyYWluLmNvbG9ycygxMCkpLCBtYWluID0gJ0xhbmRzYXQtTkRWSScpDQpwbG90KGtuciwgbWFpbiA9ICdVbnN1cGVydmlzZWQgY2xhc3NpZmljYXRpb24nLCBjb2wgPSBteWNvbG9yICkNCmBgYA0KDQojIyMgUXVlc3Rpb24gMjogUGxvdCAzLWJhbmQgUkdCIG9mICJsYW5kc2F0NSIgZm9yIHRoZSBzdWJzZXQgKGV4dGVudCAiZSIpIGFuZCByZXN1bHQgb2YgImttZWFucyIgY2x1c3RlcmluZyBzaWRlLWJ5LXNpZGUgYW5kIG1ha2UgYSB0YWJsZSBvZiBsYW5kLXVzZSBsYW5kLWNvdmVyIGxhYmVscyBmb3IgdGhlIGNsdXN0ZXJzLiBFLmcuIGNsdXN0ZXIgNCBhbmQgNSBhcmUgd2F0ZXIuDQoNCmBgYHtyfQ0KIyBTZSByZWNvcnRhIGxhIGltYWdlbiBMYW5kc2F0NSBjb24gbGEgZXh0ZW5zacOzbiAiZSINCg0KTGFuZHNhdDVfY3JvcCA8LSBjcm9wKGxhbmRzYXQ1LCBlKQ0KYGBgDQoNCmBgYHtyfQ0KTGFuZHNhdDVfY3JvcA0KYGBgDQoNCmBgYHtyfQ0KIyBDb21lbnphbW9zIGNvbiBsYSBiYW5kYSAxIChBenVsKSBkZSBMYW5kc2F0NQ0KDQpMX0JsdWUgPC0gTGFuZHNhdDVfY3JvcFtbMV1dDQpgYGANCg0KYGBge3J9DQpMX0JsdWUNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjb252ZXJ0aXIgZWwgcsOhc3RlciBhIHZlY3RvciAvIG1hdHJpeg0KDQptYXRyaXpfQmx1ZSA8LSBnZXRWYWx1ZXMoTF9CbHVlKQ0Kc3RyKG1hdHJpel9CbHVlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBFc3RhYmxlY2Vtb3Mgc2VtaWxsYXMgDQoNCnNldC5zZWVkICg5OSkNCmBgYA0KDQpgYGB7cn0NCiMgUXVlcmVtb3MgY3JlYXIgMTAgZ3J1cG9zLCBwZXJtaXRpciA1MDAgaXRlcmFjaW9uZXMsIGNvbWVuemFyIGNvbiA1IGNvbmp1bnRvcyBhbGVhdG9yaW9zIHVzYW5kbyBlbCBtw6l0b2RvICJMbG95ZCINCg0KQ2x1c3Rlcl9CbHVlIDwtIGttZWFucyhuYS5vbWl0KG1hdHJpel9CbHVlKSwgY2VudGVycyA9IDEwLCBpdGVyLm1heCA9IDUwMCwgbnN0YXJ0ID0gNSwgYWxnb3JpdGhtPSJMbG95ZCIpDQpgYGANCg0KYGBge3J9DQojIGttZWFucyBkZXZ1ZWx2ZSB1biBvYmpldG8gZGUgbGEgY2xhc2UgImttZWFucyINCg0Kc3RyKENsdXN0ZXJfQmx1ZSkNCmBgYA0KDQpgYGB7cn0NCiMgU2UgdXNhIGVsIG9iamV0byBMX0JsdWUgcmVjb3J0YWRvIHBhcmEgZXN0YWJsZWNlciBsb3MgdmFsb3JlcyBkZWwgY2zDunN0ZXIgZW4gdW4gbnVldm8gcsOhc3Rlcg0KDQpMYXlfQmx1ZSA8LSBzZXRWYWx1ZXMoTF9CbHVlLCBDbHVzdGVyX0JsdWUkY2x1c3RlcikNCmBgYA0KDQpgYGB7cn0NCkxheV9CbHVlDQpgYGANCg0KYGBge3J9DQpoZWFkKExheV9CbHVlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBCYW5kYSAyIChWZXJkZSkNCg0KTF9HcmVlbiA8LSBMYW5kc2F0NV9jcm9wW1syXV0NCmBgYA0KDQpgYGB7cn0NCiMgY29udmVydGlyIGVsIHLDoXN0ZXIgYSB2ZWN0b3IgLyBtYXRyaXoNCg0KbWF0cml6X0dyZWVuIDwtIGdldFZhbHVlcyhMX0dyZWVuKQ0Kc3RyKG1hdHJpel9HcmVlbikNCmBgYA0KDQpgYGB7cn0NCiMgRXN0YWJsZWNlbW9zIHNlbWlsbGFzIA0KDQpzZXQuc2VlZCAoOTkpDQpgYGANCg0KYGBge3J9DQojIFF1ZXJlbW9zIGNyZWFyIDEwIGdydXBvcywgcGVybWl0aXIgNTAwIGl0ZXJhY2lvbmVzLCBjb21lbnphciBjb24gNSBjb25qdW50b3MgYWxlYXRvcmlvcyB1c2FuZG8gZWwgbcOpdG9kbyAiTGxveWQiDQoNCkNsdXN0ZXJfR3JlZW4gPC0ga21lYW5zKG5hLm9taXQobWF0cml6X0dyZWVuKSwgY2VudGVycyA9IDEwLCBpdGVyLm1heCA9IDUwMCwgbnN0YXJ0ID0gNSwgYWxnb3JpdGhtPSJMbG95ZCIpDQpgYGANCg0KYGBge3J9DQojIGttZWFucyBkZXZ1ZWx2ZSB1biBvYmpldG8gZGUgbGEgY2xhc2UgImttZWFucyINCg0Kc3RyKENsdXN0ZXJfR3JlZW4pDQpgYGANCg0KYGBge3J9DQojIFNlIHVzYSBlbCBvYmpldG8gTF9HcmVlbiByZWNvcnRhZG8gcGFyYSBlc3RhYmxlY2VyIGxvcyB2YWxvcmVzIGRlbCBjbMO6c3RlciBlbiB1biBudWV2byByw6FzdGVyDQoNCkxheV9HcmVlbiA8LSBzZXRWYWx1ZXMoTF9HcmVlbiwgQ2x1c3Rlcl9HcmVlbiRjbHVzdGVyKQ0KYGBgDQoNCmBgYHtyfQ0KTGF5X0dyZWVuDQpgYGANCg0KYGBge3J9DQpoZWFkKExheV9HcmVlbikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBCYW5kYSAzIChSb2phKQ0KDQpMX1JlZCA8LSBMYW5kc2F0NV9jcm9wW1szXV0NCmBgYA0KDQpgYGB7cn0NCiMgY29udmVydGlyIGVsIHLDoXN0ZXIgYSB2ZWN0b3IgLyBtYXRyaXoNCg0KbWF0cml6X1JlZCA8LSBnZXRWYWx1ZXMoTF9SZWQpDQpzdHIobWF0cml6X1JlZCkNCmBgYA0KDQpgYGB7cn0NCiMgRXN0YWJsZWNlbW9zIHNlbWlsbGFzIA0KDQpzZXQuc2VlZCAoOTkpDQpgYGANCg0KYGBge3J9DQojIFF1ZXJlbW9zIGNyZWFyIDEwIGdydXBvcywgcGVybWl0aXIgNTAwIGl0ZXJhY2lvbmVzLCBjb21lbnphciBjb24gNSBjb25qdW50b3MgYWxlYXRvcmlvcyB1c2FuZG8gZWwgbcOpdG9kbyAiTGxveWQiDQoNCkNsdXN0ZXJfUmVkIDwtIGttZWFucyhuYS5vbWl0KG1hdHJpel9SZWQpLCBjZW50ZXJzID0gMTAsIGl0ZXIubWF4ID0gNTAwLCBuc3RhcnQgPSA1LCBhbGdvcml0aG09Ikxsb3lkIikNCmBgYA0KDQpgYGB7cn0NCiMga21lYW5zIGRldnVlbHZlIHVuIG9iamV0byBkZSBsYSBjbGFzZSAia21lYW5zIg0KDQpzdHIoQ2x1c3Rlcl9SZWQpDQpgYGANCg0KYGBge3J9DQojIFNlIHVzYSBlbCBvYmpldG8gTGFfUmVkIHJlY29ydGFkbyBwYXJhIGVzdGFibGVjZXIgbG9zIHZhbG9yZXMgZGVsIGNsw7pzdGVyIGVuIHVuIG51ZXZvIHLDoXN0ZXINCg0KTGF5X1JlZCA8LSBzZXRWYWx1ZXMoTF9SZWQsIENsdXN0ZXJfUmVkJGNsdXN0ZXIpDQpgYGANCg0KYGBge3J9DQpMYXlfUmVkDQpgYGANCg0KYGBge3J9DQojIENvbXByb2JhbW9zIHF1ZSBjYWRhIGNhcGEgdGllbmUgZ3J1cG9zIGRpc3RpbnRvcw0KDQpoZWFkKExheV9CbHVlKQ0KaGVhZChMYXlfR3JlZW4pDQpoZWFkKExheV9SZWQpDQpgYGANCg0KYGBge3J9DQojIENyZWFtb3MgdW4gcmFzdGVyIGNvbiBsYXMgMyBiYW5kYXMgUkdCDQoNCkxhbmRzYXRSR0IgPC0gc3RhY2soTGF5X0JsdWUsIExheV9HcmVlbiwgTGF5X1JlZCkNCkxhbmRzYXRSR0INCmBgYA0KDQpgYGB7cn0NCiMgU2UgcmVwcmVzZW50YSBlbCBMYW5kc2F0NV9jcm9wIHkgbGEgY2xhc2lmaWNhY2nDs24gc3VwZXJ2aXNhZGEgcGFyYSBjb21wcm9iYXIgbG9zIGdydXBvcyBjcmVhZG9zDQoNCnBsb3RSR0IoTGFuZHNhdFJHQiwgciA9IDMsIGcgPSAyLCBiID0gMSwgYXhlcyA9IFRSVUUsIHN0cmV0Y2ggPSAibGluIiwgbWFpbiA9ICJMYW5kc2F0NSBUcnVlIENvbG9yIENvbXBvc2l0ZSIpDQoNCmBgYA0KDQo=