This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.
TITULO: CLASIFICACION SUPERVISADA
Aqui exploramos la clasificacion supervisada. Existen varios algoritmos de clasificacion supervisados, y la eleccion del algoritmo puede afectar los resultados. Aqui exploramos dos algoritmos relacionados (CART y RandomForest).
En la clasificacion supervisada, tenemos conocimiento previo sobre algunos de los tipos de cobertura del suelo mediante, por ejemplo, trabajo de campo, datos espaciales de referencia o interpretación de imagenes de alta resolucion (como las disponibles en los mapas de Google). Se identifican sitios especificos en el area de estudio que representan ejemplos homogeneos de estos tipos de cobertura terrestre conocidos. Estas areas se conocen comunmente como sitios de entrenamiento porque las propiedades espectrales de estos sitios se usan para entrenar el algoritmo de clasificacion.
Los siguientes ejemplos utilizan un clasificador de arboles de clasificacion y regresion (CART) (Breiman et al. 1984) ( lecturas adicionales para predecir las clases de cobertura del suelo en el area de estudio).
Realizaremos los siguientes pasos:
Genere sitios de muestra basados en un raster de referencia
Extraer valores de celda de datos de Landsat para los sitios de muestra
Entrenar al clasificador usando muestras de entrenamiento
Clasifique los datos de Landsat usando el modelo entrenado
Evaluar la precision del modelo.
TITULO: DATOS DE REFERENCIA
La National Land Cover Database 2011 (NLCD 2011) es un producto de cobertura terrestre para los EE. UU. NLCD es una base de datos de cobertura del suelo basada en Landsat de 30 m que abarca 4 épocas (1992, 2001, 2006 y 2011). NLCD 2011 se basa principalmente en una clasificación de árbol de decisión de datos Landsat de alrededor de 2011.
Hicimos muchas cosas aqui. Da un paso atras y lee mas sobre ratify.
#####Nota No hay clase con valor 6.
TITULO: GENERAR SITIOS DE MUESTRA
Como discutimos en la clase, los datos de capacitacion y / o validacion pueden provenir de una variedad de fuentes. En este ejemplo, generaremos los sitios de muestra de capacitacion y validacion utilizando la referencia de NLCD RasterLayer. Alternativamente, puede usar sitios predefinidos que puede haber recopilado de otras fuentes. Generaremos los sitios de muestra siguiendo un muestreo aleatorio estratificado para asegurar muestras de cada clase LULC.
# Load the training sites locations
# Set the random number generator to reproduce the results
set.seed(99)
# Sampling
samp2011 = sampleStratified(nlcd2011, size = 200, na.rm = TRUE, sp = TRUE)
samp2011
class : SpatialPointsDataFrame
features : 1600
extent : -121.9257, -121.4225, 37.85415, 38.18536 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
variables : 2
names : cell, nlcd2011
min values : 413, 1
max values : 2307837, 9
## class : SpatialPointsDataFrame
## features : 1600
## extent : -121.9257, -121.4225, 37.85415, 38.18536 (xmin, xmax, ymin, ymax)
## crs : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
## variables : 2
## names : cell, nlcd2011
## min values : 413, 1
## max values : 2307837, 9
# Number of samples in each class
table(samp2011$nlcd2011)
1 2 3 4 5 7 8 9
200 200 200 200 200 200 200 200
##
## 1 2 3 4 5 7 8 9
## 200 200 200 200 200 200 200 200
Puedes ver que hay dos variables en -samp2011-. La -cell- columna contiene numeros de celdas de -nlcd2011- muestra. -nlcd2011- La columna contiene los valores de clase (1-9). Dejaremos la -cell- columna mas tarde.
-nlcd2011- Tracemos los sitios de entrenamiento sobre RasterLayer para visualizar la distribucion de las ubicaciones de muestreo.
library(rasterVis)
Loading required package: lattice
Loading required package: latticeExtra
plt = levelplot(nlcd2011, col.regions = classcolor, main = 'Distribution of Training Sites')
print(plt + layer(sp.points(samp2011, pch = 3, cex = 0.5, col = 1)))

-rasterVis- ofrece un trazado mas avanzado (enrejado / enrejado) de objetos Raster *. Instale el paquete si no esta disponible para su maquina.
Aqui estan nuestros datos de Landsat.
landsat5 = stack('C:/Users/David-PC/Documents/data/rs/centralvalley-2011LT5.tif')
names(landsat5) = c('blue', 'green', 'red', 'NIR', 'SWIR1', 'SWIR2')
Una vez que tenemos los sitios, podemos extraer los valores de las celdas de -landsat5- RasterStack. Estos valores de banda seran las variables predictoras y los “valores de clase” -nlcd2011- seran la variable de respuesta.
# Extract the layer values for the locations
sampvals = extract(landsat5, samp2011, df = TRUE)
# sampvals no longer has the spatial information. To keep the spatial information you use `sp=TRUE` argument in the `extract` function.
# drop the ID column
sampvals = sampvals[, -1]
# combine the class information with extracted values
sampdata = data.frame(classvalue = samp2011@data$nlcd2011, sampvals)
TITULO: ENTRENAR AL CLASIFICADOR
Ahora entrenaremos el algoritmo de clasificaciOn utilizando el -training2011- conjunto de datos.
library(rpart)
# Train the model
cart <- rpart(as.factor(classvalue)~., data=sampdata, method = 'class', minsplit = 5)
# print(model.class)
# Plot the trained classification tree
plot(cart, uniform=TRUE, main="Classification Tree")
text(cart, cex = 0.8)

En el diagrama de arbol de clasificacion, los valores de clase se imprimen en los nodos de hoja. Puede encontrar los nombres de cobertura de suelo correspondientes al uso del suelo en -classdf- data.frame.
Consulte -?rpart.control- para establecer diferentes parametros para construir el modelo.
Puede imprimir / trazar mas sobre el -cart- modelo creado en el ejemplo anterior. Por ejemplo, puede usar -plotcp(cart)- para aprender sobre la complejidad de costos ( -cp- argumento en -rpart-).
TITULO: CLASIFICAR
Ahora que tenemos nuestro modelo de clasificacion entrenado (-cart-), podemos usarlo para hacer predicciones, es decir, para clasificar todas las celdas en -landsat5- RasterStack.
Ahora trace el resultado de la clasificacion usando -rasterVis-. Ver establecera el -classnames- para el -classvalues-.
pr2011 = ratify(pr2011)
rat = levels(pr2011)[[1]]
rat$legend = classdf$classnames
levels(pr2011) = rat
levelplot(pr2011, maxpixels = 1e6,
col.regions = classcolor,
scales=list(draw=FALSE),
main = "Decision Tree classification of Landsat 5")

Pregunta 1 : Trace “nlcd2011” y “pr2011” lado a lado y comente sobre la precision de la prediccion (p. Ej., Mezcla entre cultivos, pastos, pastizales y arbustos).
Es posible que deba seleccionar mas muestras y usar variables predictoras adicionales. La eleccion del clasificador tambien juega un papel importante.
library(gridExtra)
plot2011nlcd = levelplot(nlcd2011,
maxpixels = 1e6,
xlab= NULL, ylab= NULL,
main='National Land Cover Database 2011',colorkey = FALSE,
scales=list(draw=FALSE), col.regions = classcolor,
labels = TRUE)
plot2011pr = levelplot(pr2011,
maxpixels = 1e6,
xlab= NULL,nylab= NULL,
main='Prediccion 2011', colorkey = FALSE,
scales=list(draw=FALSE), col.regions = classcolor,
labels = TRUE)
grid.arrange(plot2011nlcd, plot2011pr, ncol=2)

NA
NA
NA
El modelo de prediccion es mucho mas detallado, lo que infiere que, probablemnete, es un modelo mucho mas acorde a la realidad de los datos
TITULO: EVALUACION DEL MODELO
Ahora, evaluemos la precision del modelo para tener una idea de la precision del mapa clasificado. Dos medidas ampliamente utilizadas en la teledeteccion son “precision general” y “kappa”. Puede realizar la evaluacion de precision utilizando las muestras independientes ( -validation2011-).
Para evaluar cualquier modelo, puede usar la validacion cruzada k-fold. En esta tecnica, los datos utilizados para ajustar el modelo se dividen en -k- grupos (tipicamente 5 grupos). A su vez, uno de los grupos se usara para la prueba del modelo, mientras que el resto de los datos se usara para el entrenamiento del modelo (ajuste).
library(dismo)
set.seed(99)
j = kfold(sampdata, k = 5, by=sampdata$classvalue)
table(j)
j
1 2 3 4 5
320 320 320 320 320
## j
## 1 2 3 4 5
## 320 320 320 320 320
Ahora entrenamos y probamos el modelo cinco veces, cada vez calculando una matriz de confusion que almacenamos en una lista.
x = list()
for (k in 1:5) {
train = sampdata[j!= k, ]
test = sampdata[j == k, ]
cart = rpart(as.factor(classvalue)~., data=train, method = 'class', minsplit = 5)
pclass = predict(cart, test, type='class')
# create a data.frame using the reference and prediction
x[[k]] = cbind(test$classvalue, as.integer(pclass))
}
Ahora combine los cinco elementos de la lista en un solo data.frame, usando -do.cally- calcule una matriz de confusion.
y = do.call(rbind, x)
y = data.frame(y)
colnames(y) = c('observed', 'predicted')
conmat = table(y)
# change the name of the classes
colnames(conmat) = classdf$classnames
rownames(conmat) = classdf$classnames
conmat
predicted
observed Water Developed Barren Forest Shrubland Herbaceous Planted/Cultivated Wetlands
Water 175 6 0 3 0 0 7 9
Developed 2 90 51 8 10 22 11 6
Barren 7 39 82 4 19 38 5 6
Forest 0 2 1 106 57 1 6 27
Shrubland 0 3 5 59 102 12 12 7
Herbaceous 0 9 36 10 27 109 8 1
Planted/Cultivated 0 7 11 34 42 19 69 18
Wetlands 18 10 6 36 29 5 33 63
## predicted
## observed Water Developed Barren Forest Shrubland Herbaceous
## Water 175 6 0 3 0 0
## Developed 2 90 51 8 10 22
## Barren 7 39 82 4 19 38
## Forest 0 2 1 106 57 1
## Shrubland 0 3 5 59 102 12
## Herbaceous 0 9 36 10 27 109
## Planted/Cultivated 0 7 11 34 42 19
## Wetlands 18 10 6 36 29 5
## predicted
## observed Planted/Cultivated Wetlands
## Water 7 9
## Developed 11 6
## Barren 5 6
## Forest 6 27
## Shrubland 12 7
## Herbaceous 8 1
## Planted/Cultivated 69 18
## Wetlands 33 63
Pregunta 2 : Comente sobre la clasificación erronea entre diferentes clases.
Pregunta 3 : ¿Se te ocurren formas de mejorar la precision?
Calcule la precision general y la estadistica “Kappa”.
Precision general:
# number of cases
n = sum(conmat)
n
[1] 1600
## [1] 1600
# number of correctly classified cases per class
diag = diag(conmat)
# Overall Accuracy
OA = sum(diag) / n
OA
[1] 0.4975
## [1] 0.4975
Kappa:
# observed (true) cases per class
rowsums = apply(conmat, 1, sum)
p = rowsums / n
# predicted cases per class
colsums = apply(conmat, 2, sum)
q = colsums / n
expAccuracy = sum(p*q)
kappa = (OA - expAccuracy) / (1 - expAccuracy)
kappa
[1] 0.4257143
## [1] 0.4257143
Productor y precision del usuario
# Producer accuracy
PA = diag / colsums
# User accuracy
UA = diag / rowsums
outAcc = data.frame(producerAccuracy = PA, userAccuracy = UA)
outAcc
## producerAccuracy userAccuracy
## Water 0.8663366 0.875
## Developed 0.5421687 0.450
## Barren 0.4270833 0.410
## Forest 0.4076923 0.530
## Shrubland 0.3566434 0.510
## Herbaceous 0.5291262 0.545
## Planted/Cultivated 0.4569536 0.345
## Wetlands 0.4598540 0.315
Pregunta 4 : Realice la clasificacion utilizando clasificadores de bosque aleatorio del paquete “randomForest”
Pregunta 5 : Grafique los resultados del clasificador rpart y Random Forest uno al lado del otro.
Pregunta 6 (opcional) : repita los pasos para el año 2001 utilizando Random Forest . Use la imagen compuesta sin nubes -data/centralvalley-2001LE7.tif-. Estos son datos de Landsat 7 . Utilice como datos de referencia la Base de Datos Nacional de Cobertura de Tierras 2001 (NLCD 2001) para el subconjunto del Valle Central de California. *
Pregunta 7 (opcional) : Hemos entrenado a los clasificadores utilizando 200 muestras para cada clase. Investigue el efecto del tamaño de la muestra en la clasificacion. Repita los pasos con diferentes subconjuntos, por ejemplo, un tamaño de muestra de 150, 100, 50 por clase, y compare los resultados. Use las mismas muestras de reserva para la evaluacion del modelo.
Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.
LS0tDQp0aXRsZTogIioqSW1hZ2VuIExhbmRTYXQgOCwgU2liYXRlIDIwMTUgLSBDbGFzaWZpY2FjaW9uIHN1cGVydmlzYWRhKioiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIA0KDQpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIA0KDQoNCiMjIyAqKlRJVFVMTzogQ0xBU0lGSUNBQ0lPTiBTVVBFUlZJU0FEQSoqDQoNCg0KIyMjIyMgQXF1aSBleHBsb3JhbW9zIGxhIGNsYXNpZmljYWNpb24gc3VwZXJ2aXNhZGEuIEV4aXN0ZW4gdmFyaW9zIGFsZ29yaXRtb3MgZGUgY2xhc2lmaWNhY2lvbiBzdXBlcnZpc2Fkb3MsIHkgbGEgZWxlY2Npb24gZGVsIGFsZ29yaXRtbyBwdWVkZSBhZmVjdGFyIGxvcyByZXN1bHRhZG9zLiBBcXVpIGV4cGxvcmFtb3MgZG9zIGFsZ29yaXRtb3MgcmVsYWNpb25hZG9zIChDQVJUIHkgUmFuZG9tRm9yZXN0KS4NCg0KIyMjIyMgRW4gbGEgY2xhc2lmaWNhY2lvbiBzdXBlcnZpc2FkYSwgdGVuZW1vcyBjb25vY2ltaWVudG8gcHJldmlvIHNvYnJlIGFsZ3Vub3MgZGUgbG9zIHRpcG9zIGRlIGNvYmVydHVyYSBkZWwgc3VlbG8gbWVkaWFudGUsIHBvciBlamVtcGxvLCB0cmFiYWpvIGRlIGNhbXBvLCBkYXRvcyBlc3BhY2lhbGVzIGRlIHJlZmVyZW5jaWEgbyBpbnRlcnByZXRhY2nDs24gZGUgaW1hZ2VuZXMgZGUgYWx0YSByZXNvbHVjaW9uIChjb21vIGxhcyBkaXNwb25pYmxlcyBlbiBsb3MgbWFwYXMgZGUgR29vZ2xlKS4gU2UgaWRlbnRpZmljYW4gc2l0aW9zIGVzcGVjaWZpY29zIGVuIGVsIGFyZWEgZGUgZXN0dWRpbyBxdWUgcmVwcmVzZW50YW4gZWplbXBsb3MgaG9tb2dlbmVvcyBkZSBlc3RvcyB0aXBvcyBkZSBjb2JlcnR1cmEgdGVycmVzdHJlIGNvbm9jaWRvcy4gRXN0YXMgYXJlYXMgc2UgY29ub2NlbiBjb211bm1lbnRlIGNvbW8gc2l0aW9zIGRlIGVudHJlbmFtaWVudG8gcG9ycXVlIGxhcyBwcm9waWVkYWRlcyBlc3BlY3RyYWxlcyBkZSBlc3RvcyBzaXRpb3Mgc2UgdXNhbiBwYXJhIGVudHJlbmFyIGVsIGFsZ29yaXRtbyBkZSBjbGFzaWZpY2FjaW9uLg0KDQojIyMjIyBMb3Mgc2lndWllbnRlcyBlamVtcGxvcyB1dGlsaXphbiB1biBjbGFzaWZpY2Fkb3IgZGUgYXJib2xlcyBkZSBjbGFzaWZpY2FjaW9uIHkgcmVncmVzaW9uIChDQVJUKSAoQnJlaW1hbiBldCBhbC4gMTk4NCkgKCBsZWN0dXJhcyBhZGljaW9uYWxlcyBwYXJhIHByZWRlY2lyIGxhcyBjbGFzZXMgZGUgY29iZXJ0dXJhIGRlbCBzdWVsbyBlbiBlbCBhcmVhIGRlIGVzdHVkaW8pLg0KDQojIyMjIyBSZWFsaXphcmVtb3MgbG9zIHNpZ3VpZW50ZXMgcGFzb3M6DQoNCiMjIyMjIEdlbmVyZSBzaXRpb3MgZGUgbXVlc3RyYSBiYXNhZG9zIGVuIHVuIHJhc3RlciBkZSByZWZlcmVuY2lhDQojIyMjIyBFeHRyYWVyIHZhbG9yZXMgZGUgY2VsZGEgZGUgZGF0b3MgZGUgTGFuZHNhdCBwYXJhIGxvcyBzaXRpb3MgZGUgbXVlc3RyYQ0KIyMjIyMgRW50cmVuYXIgYWwgY2xhc2lmaWNhZG9yIHVzYW5kbyBtdWVzdHJhcyBkZSBlbnRyZW5hbWllbnRvDQojIyMjIyBDbGFzaWZpcXVlIGxvcyBkYXRvcyBkZSBMYW5kc2F0IHVzYW5kbyBlbCBtb2RlbG8gZW50cmVuYWRvDQojIyMjIyBFdmFsdWFyIGxhIHByZWNpc2lvbiBkZWwgbW9kZWxvLg0KDQoNCiMjIyAqKlRJVFVMTzogREFUT1MgREUgUkVGRVJFTkNJQSoqDQoNCg0KIyMjIyMgTGEgTmF0aW9uYWwgTGFuZCBDb3ZlciBEYXRhYmFzZSAyMDExIChOTENEIDIwMTEpIGVzIHVuIHByb2R1Y3RvIGRlIGNvYmVydHVyYSB0ZXJyZXN0cmUgcGFyYSBsb3MgRUUuIFVVLiBOTENEIGVzIHVuYSBiYXNlIGRlIGRhdG9zIGRlIGNvYmVydHVyYSBkZWwgc3VlbG8gYmFzYWRhIGVuIExhbmRzYXQgZGUgMzAgbSBxdWUgYWJhcmNhIDQgw6lwb2NhcyAoMTk5MiwgMjAwMSwgMjAwNiB5IDIwMTEpLiBOTENEIDIwMTEgc2UgYmFzYSBwcmluY2lwYWxtZW50ZSBlbiB1bmEgY2xhc2lmaWNhY2nDs24gZGUgw6FyYm9sIGRlIGRlY2lzacOzbiBkZSBkYXRvcyBMYW5kc2F0IGRlIGFscmVkZWRvciBkZSAyMDExLg0KDQoNCiMjIyMjIFB1ZWRlIGVuY29udHJhciBsb3Mgbm9tYnJlcyBkZSBjbGFzZSBlbiBOQ0xEIDIwMTEgKGFxdcOtKSAtWyBodHRwczovL3d3dy5tcmxjLmdvdi9ubGNkMTFfbGVnLnBocCBdLS4gVGllbmUgZG9zIHBhcmVzIGRlIHZhbG9yZXMgZGUgY2xhc2UgeSBub21icmVzIHF1ZSBjb3JyZXNwb25kZW4gYSBsb3Mgbml2ZWxlcyBkZSB1c28gZGVsIHN1ZWxvIHkgZWwgc2lzdGVtYSBkZSBjbGFzaWZpY2FjaW9uIGRlIGxhIGNvYmVydHVyYSBkZWwgc3VlbG8uIEVzdG9zIG5pdmVsZXMgZ2VuZXJhbG1lbnRlIHJlcHJlc2VudGFuIGVsIG5pdmVsIGRlIGNvbXBsZWppZGFkLCBzaWVuZG8gZWwgbml2ZWwgSSBlbCBtYXMgc2ltcGxlIGNvbiBhbXBsaWFzIGNhdGVnb3JpYXMgZGUgY29iZXJ0dXJhIGRlbCBzdWVsby4gTGVhIGVzdGUgaW5mb3JtZSBkZSBBbmRlcnNvbiBldCBhbC4gUGFyYSBvYnRlbmVyIG1hcyBpbmZvcm1hY2lvbiBzb2JyZSBlc3RlIHNpc3RlbWEgZGUgY2xhc2lmaWNhY2lvbiBkZSB1c28geSBjb2JlcnR1cmEgZGUgbGEgdGllcnJhLg0KDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHJhc3RlcikNCm5sY2QgPSBicmljaygnZGF0YS9ycy9ubGNkLUwxLnRpZicpDQpuYW1lcyhubGNkKSAgPSAgYygibmxjZDIwMDEiLCAibmxjZDIwMTEiKQ0KDQoNCiMgVGhlIGNsYXNzIG5hbWVzIGFuZCBjb2xvcnMgZm9yIHBsb3R0aW5nDQpubGNkY2xhc3MgID0gIGMoIldhdGVyIiwgIkRldmVsb3BlZCIsICJCYXJyZW4iLCAiRm9yZXN0IiwgIlNocnVibGFuZCIsICJIZXJiYWNlb3VzIiwgIlBsYW50ZWQvQ3VsdGl2YXRlZCIsICJXZXRsYW5kcyIpDQpjbGFzc2RmIDwtIGRhdGEuZnJhbWUoY2xhc3N2YWx1ZTEgPSBjKDEsMiwzLDQsNSw3LDgsOSksIGNsYXNzbmFtZXMxID0gbmxjZGNsYXNzKQ0KIyBIZXggY29kZXMgb2YgY29sb3JzDQpjbGFzc2NvbG9yICA9ICBjKCIjNTQ3NUE4IiwgIiNCNTAwMDAiLCAiI0QyQ0RDMCIsICIjMzg4MTRFIiwgIiNBRjk2M0MiLCAiI0QxRDE4MiIsICIjRkJGNjVEIiwgIiNDOEU2RjgiKQ0KIyBOb3cgd2UgcmF0aWZ5IChSQVQgPSAiUmFzdGVyIEF0dHJpYnV0ZSBUYWJsZSIpIHRoZSBuY2xkMjAxMSAoZGVmaW5lIFJhc3RlckxheWVyIGFzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUpLiBUaGlzIGlzIGhlbHBmdWwgZm9yIHBsb3R0aW5nLg0KbmxjZDIwMTEgPSBubGNkW1syXV0NCm5sY2QyMDExID0gcmF0aWZ5KG5sY2QyMDExKQ0KcmF0ICA9ICBsZXZlbHMobmxjZDIwMTEpW1sxXV0NCiMNCnJhdCRsYW5kY292ZXIgPSBubGNkY2xhc3MNCmxldmVscyhubGNkMjAxMSkgID0gIHJhdA0KDQoNCmBgYA0KDQoNCiMjIyMjIEhpY2ltb3MgbXVjaGFzIGNvc2FzIGFxdWkuIERhIHVuIHBhc28gYXRyYXMgeSBsZWUgbWFzIHNvYnJlIHJhdGlmeS4NCg0KDQojIyMjI05vdGEgTm8gaGF5IGNsYXNlIGNvbiB2YWxvciA2Lg0KDQoNCiMjIyAqKlRJVFVMTzogR0VORVJBUiBTSVRJT1MgREUgTVVFU1RSQSoqDQoNCg0KIyMjIyMgQ29tbyBkaXNjdXRpbW9zIGVuIGxhIGNsYXNlLCBsb3MgZGF0b3MgZGUgY2FwYWNpdGFjaW9uIHkgLyBvIHZhbGlkYWNpb24gcHVlZGVuIHByb3ZlbmlyIGRlIHVuYSB2YXJpZWRhZCBkZSBmdWVudGVzLiBFbiBlc3RlIGVqZW1wbG8sIGdlbmVyYXJlbW9zIGxvcyBzaXRpb3MgZGUgbXVlc3RyYSBkZSBjYXBhY2l0YWNpb24geSB2YWxpZGFjaW9uIHV0aWxpemFuZG8gbGEgcmVmZXJlbmNpYSBkZSBOTENEIFJhc3RlckxheWVyLiBBbHRlcm5hdGl2YW1lbnRlLCBwdWVkZSB1c2FyIHNpdGlvcyBwcmVkZWZpbmlkb3MgcXVlIHB1ZWRlIGhhYmVyIHJlY29waWxhZG8gZGUgb3RyYXMgZnVlbnRlcy4gR2VuZXJhcmVtb3MgbG9zIHNpdGlvcyBkZSBtdWVzdHJhIHNpZ3VpZW5kbyB1biBtdWVzdHJlbyBhbGVhdG9yaW8gZXN0cmF0aWZpY2FkbyBwYXJhIGFzZWd1cmFyIG11ZXN0cmFzIGRlIGNhZGEgY2xhc2UgTFVMQy4NCg0KDQoNCmBgYHtyfQ0KIyBMb2FkIHRoZSB0cmFpbmluZyBzaXRlcyBsb2NhdGlvbnMNCiMgU2V0IHRoZSByYW5kb20gbnVtYmVyIGdlbmVyYXRvciB0byByZXByb2R1Y2UgdGhlIHJlc3VsdHMNCnNldC5zZWVkKDk5KQ0KIyBTYW1wbGluZw0Kc2FtcDIwMTEgPSBzYW1wbGVTdHJhdGlmaWVkKG5sY2QyMDExLCBzaXplID0gMjAwLCBuYS5ybSA9IFRSVUUsIHNwID0gVFJVRSkNCnNhbXAyMDExDQojIyBjbGFzcyAgICAgICA6IFNwYXRpYWxQb2ludHNEYXRhRnJhbWUNCiMjIGZlYXR1cmVzICAgIDogMTYwMA0KIyMgZXh0ZW50ICAgICAgOiAtMTIxLjkyNTcsIC0xMjEuNDIyNSwgMzcuODU0MTUsIDM4LjE4NTM2ICAoeG1pbiwgeG1heCwgeW1pbiwgeW1heCkNCiMjIGNycyAgICAgICAgIDogK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwDQojIyB2YXJpYWJsZXMgICA6IDINCiMjIG5hbWVzICAgICAgIDogICAgY2VsbCwgbmxjZDIwMTENCiMjIG1pbiB2YWx1ZXMgIDogICAgIDQxMywgICAgICAgIDENCiMjIG1heCB2YWx1ZXMgIDogMjMwNzgzNywgICAgICAgIDkNCiMgTnVtYmVyIG9mIHNhbXBsZXMgaW4gZWFjaCBjbGFzcw0KdGFibGUoc2FtcDIwMTEkbmxjZDIwMTEpDQojIw0KIyMgICAxICAgMiAgIDMgICA0ICAgNSAgIDcgICA4ICAgOQ0KIyMgMjAwIDIwMCAyMDAgMjAwIDIwMCAyMDAgMjAwIDIwMA0KYGBgDQoNCg0KIyMjIyMgUHVlZGVzIHZlciBxdWUgaGF5IGRvcyB2YXJpYWJsZXMgZW4gLXNhbXAyMDExLS4gTGEgLWNlbGwtIGNvbHVtbmEgY29udGllbmUgbnVtZXJvcyBkZSBjZWxkYXMgZGUgLW5sY2QyMDExLSBtdWVzdHJhLiAtbmxjZDIwMTEtIExhIGNvbHVtbmEgY29udGllbmUgbG9zIHZhbG9yZXMgZGUgY2xhc2UgKDEtOSkuIERlamFyZW1vcyBsYSAtY2VsbC0gY29sdW1uYSBtYXMgdGFyZGUuDQoNCiMjIyMjIEFxdWkgLW5sY2QtIHRpZW5lIHZhbG9yZXMgZW50ZXJvcyBlbnRyZSAxLTkuIEEgbWVudWRvIGVuY29udHJhcmEgcXVlIGxvcyBub21icmVzIGRlIGNsYXNlIHNlIHByb3BvcmNpb25hbiBjb21vIGV0aXF1ZXRhcyBkZSBjYWRlbmEgKHBvciBlamVtcGxvLCBhZ3VhLCBjdWx0aXZvLCB2ZWdldGFjaW9uKS4gTmVjZXNpdGFyYSAndm9sdmVyIGEgZXRpcXVldGFyJyBsb3Mgbm9tYnJlcyBkZSBjbGFzZSBhIGVudGVyb3MgbyBmYWN0b3JlcyBzaSBzb2xvIHNlIHByb3BvcmNpb25hbiBldGlxdWV0YXMgZGUgY2FkZW5hIGFudGVzIGRlIHVzYXJsYXMgY29tbyB2YXJpYWJsZSBkZSByZXNwdWVzdGEgZW4gbGEgY2xhc2lmaWNhY2lvbi4gSGF5IHZhcmlvcyBlbmZvcXVlcyBxdWUgcG9kcmlhbiB1c2Fyc2UgcGFyYSBjb252ZXJ0aXIgZXN0YXMgY2xhc2VzIGEgY29kaWdvcyBlbnRlcm9zLiBQb2RlbW9zIGhhY2VyIHVuYSBmdW5jaW9uIHF1ZSByZWNsYXNpZmlxdWUgbGFzIGNhZGVuYXMgZGUgY2FyYWN0ZXJlcyBxdWUgcmVwcmVzZW50YW4gbGFzIGNsYXNlcyBkZSBjb2JlcnR1cmEgZGVsIHN1ZWxvIGVuIG51bWVyb3MgZW50ZXJvcyBiYXNhZG9zIGVuIGxvcyBuaXZlbGVzIGRlIGZhY3RvcmVzIGV4aXN0ZW50ZXMuDQoNCiMjIyMjIC1ubGNkMjAxMS0gVHJhY2Vtb3MgbG9zIHNpdGlvcyBkZSBlbnRyZW5hbWllbnRvIHNvYnJlIFJhc3RlckxheWVyIHBhcmEgdmlzdWFsaXphciBsYSBkaXN0cmlidWNpb24gZGUgbGFzIHViaWNhY2lvbmVzIGRlIG11ZXN0cmVvLg0KDQpgYGB7cn0NCg0KbGlicmFyeShyYXN0ZXJWaXMpDQpwbHQgPSBsZXZlbHBsb3QobmxjZDIwMTEsIGNvbC5yZWdpb25zID0gY2xhc3Njb2xvciwgbWFpbiA9ICdEaXN0cmlidXRpb24gb2YgVHJhaW5pbmcgU2l0ZXMnKQ0KcHJpbnQocGx0ICsgbGF5ZXIoc3AucG9pbnRzKHNhbXAyMDExLCBwY2ggPSAzLCBjZXggPSAwLjUsIGNvbCA9IDEpKSkNCg0KYGBgDQoNCg0KIyMjIyMgLXJhc3RlclZpcy0gb2ZyZWNlIHVuIHRyYXphZG8gbWFzIGF2YW56YWRvIChlbnJlamFkbyAvIGVucmVqYWRvKSBkZSBvYmpldG9zIFJhc3RlciAqLiBJbnN0YWxlIGVsIHBhcXVldGUgc2kgbm8gZXN0YSBkaXNwb25pYmxlIHBhcmEgc3UgbWFxdWluYS4NCg0KDQojIyMjIyBBcXVpIGVzdGFuIG51ZXN0cm9zIGRhdG9zIGRlIExhbmRzYXQuDQoNCg0KYGBge3J9DQoNCmxhbmRzYXQ1ID0gc3RhY2soJ0M6L1VzZXJzL0RhdmlkLVBDL0RvY3VtZW50cy9kYXRhL3JzL2NlbnRyYWx2YWxsZXktMjAxMUxUNS50aWYnKQ0KbmFtZXMobGFuZHNhdDUpID0gYygnYmx1ZScsICdncmVlbicsICdyZWQnLCAnTklSJywgJ1NXSVIxJywgJ1NXSVIyJykNCg0KDQpgYGANCg0KDQoNCiMjIyMjIFVuYSB2ZXogcXVlIHRlbmVtb3MgbG9zIHNpdGlvcywgcG9kZW1vcyBleHRyYWVyIGxvcyB2YWxvcmVzIGRlIGxhcyBjZWxkYXMgZGUgLWxhbmRzYXQ1LSBSYXN0ZXJTdGFjay4gRXN0b3MgdmFsb3JlcyBkZSBiYW5kYSBzZXJhbiBsYXMgdmFyaWFibGVzIHByZWRpY3RvcmFzIHkgbG9zICJ2YWxvcmVzIGRlIGNsYXNlIiAtbmxjZDIwMTEtIHNlcmFuIGxhIHZhcmlhYmxlIGRlIHJlc3B1ZXN0YS4NCg0KDQpgYGB7cn0NCg0KIyBFeHRyYWN0IHRoZSBsYXllciB2YWx1ZXMgZm9yIHRoZSBsb2NhdGlvbnMNCnNhbXB2YWxzID0gZXh0cmFjdChsYW5kc2F0NSwgc2FtcDIwMTEsIGRmID0gVFJVRSkNCiMgc2FtcHZhbHMgbm8gbG9uZ2VyIGhhcyB0aGUgc3BhdGlhbCBpbmZvcm1hdGlvbi4gVG8ga2VlcCB0aGUgc3BhdGlhbCBpbmZvcm1hdGlvbiB5b3UgdXNlIGBzcD1UUlVFYCBhcmd1bWVudCBpbiB0aGUgYGV4dHJhY3RgIGZ1bmN0aW9uLg0KIyBkcm9wIHRoZSBJRCBjb2x1bW4NCnNhbXB2YWxzID0gc2FtcHZhbHNbLCAtMV0NCiMgY29tYmluZSB0aGUgY2xhc3MgaW5mb3JtYXRpb24gd2l0aCBleHRyYWN0ZWQgdmFsdWVzDQpzYW1wZGF0YSA9IGRhdGEuZnJhbWUoY2xhc3N2YWx1ZSA9IHNhbXAyMDExQGRhdGEkbmxjZDIwMTEsIHNhbXB2YWxzKQ0KDQpgYGANCg0KDQojIyMgKipUSVRVTE86IEVOVFJFTkFSIEFMIENMQVNJRklDQURPUioqDQoNCg0KIyMjIyMgQWhvcmEgZW50cmVuYXJlbW9zIGVsIGFsZ29yaXRtbyBkZSBjbGFzaWZpY2FjaU9uIHV0aWxpemFuZG8gZWwgLXRyYWluaW5nMjAxMS0gY29uanVudG8gZGUgZGF0b3MuDQoNCg0KYGBge3J9DQoNCmxpYnJhcnkocnBhcnQpDQojIFRyYWluIHRoZSBtb2RlbA0KY2FydCA8LSBycGFydChhcy5mYWN0b3IoY2xhc3N2YWx1ZSl+LiwgZGF0YT1zYW1wZGF0YSwgbWV0aG9kID0gJ2NsYXNzJywgbWluc3BsaXQgPSA1KQ0KIyBwcmludChtb2RlbC5jbGFzcykNCiMgUGxvdCB0aGUgdHJhaW5lZCBjbGFzc2lmaWNhdGlvbiB0cmVlDQpwbG90KGNhcnQsIHVuaWZvcm09VFJVRSwgbWFpbj0iQ2xhc3NpZmljYXRpb24gVHJlZSIpDQp0ZXh0KGNhcnQsIGNleCA9IDAuOCkNCg0KYGBgDQoNCg0KIyMjIyMgRW4gZWwgZGlhZ3JhbWEgZGUgYXJib2wgZGUgY2xhc2lmaWNhY2lvbiwgbG9zIHZhbG9yZXMgZGUgY2xhc2Ugc2UgaW1wcmltZW4gZW4gbG9zIG5vZG9zIGRlIGhvamEuIFB1ZWRlIGVuY29udHJhciBsb3Mgbm9tYnJlcyBkZSBjb2JlcnR1cmEgZGUgc3VlbG8gY29ycmVzcG9uZGllbnRlcyBhbCB1c28gZGVsIHN1ZWxvIGVuIC1jbGFzc2RmLSBkYXRhLmZyYW1lLg0KDQojIyMjIyBDb25zdWx0ZSAtP3JwYXJ0LmNvbnRyb2wtIHBhcmEgZXN0YWJsZWNlciBkaWZlcmVudGVzIHBhcmFtZXRyb3MgcGFyYSBjb25zdHJ1aXIgZWwgbW9kZWxvLg0KDQojIyMjIyBQdWVkZSBpbXByaW1pciAvIHRyYXphciBtYXMgc29icmUgZWwgLWNhcnQtIG1vZGVsbyBjcmVhZG8gZW4gZWwgZWplbXBsbyBhbnRlcmlvci4gUG9yIGVqZW1wbG8sIHB1ZWRlIHVzYXIgLXBsb3RjcChjYXJ0KS0gcGFyYSBhcHJlbmRlciBzb2JyZSBsYSBjb21wbGVqaWRhZCBkZSBjb3N0b3MgKCAtY3AtIGFyZ3VtZW50byBlbiAtcnBhcnQtKS4NCg0KDQojIyMgKipUSVRVTE86IENMQVNJRklDQVIqKg0KDQoNCiMjIyMjIEFob3JhIHF1ZSB0ZW5lbW9zIG51ZXN0cm8gbW9kZWxvIGRlIGNsYXNpZmljYWNpb24gZW50cmVuYWRvICgtY2FydC0pLCBwb2RlbW9zIHVzYXJsbyBwYXJhIGhhY2VyIHByZWRpY2Npb25lcywgZXMgZGVjaXIsIHBhcmEgY2xhc2lmaWNhciB0b2RhcyBsYXMgY2VsZGFzIGVuIC1sYW5kc2F0NS0gUmFzdGVyU3RhY2suDQoNCiMjIyMjIEltcG9ydGFudGUgTG9zIG5vbWJyZXMgZW4gZWwgb2JqZXRvIFJhc3RlciBhIGNsYXNpZmljYXIgZGViZW4gY29pbmNpZGlyIGV4YWN0YW1lbnRlIGNvbiBsb3MgZXNwZXJhZG9zIHBvciBlbCBtb2RlbG8uIEVzdGUgc2VyYSBlbCBjYXNvIHNpIHNlIHV0aWxpem8gZWwgbWlzbW8gb2JqZXRvIFJhc3RlciAoYSB0cmF2ZXMgZGUgZXh0cmFjdG8pIHBhcmEgb2J0ZW5lciBsb3MgdmFsb3JlcyBxdWUgc2UgYWp1c3RhbiBhbCBtb2RlbG8uDQoNCmBgYHtyfQ0KIyBOb3cgcHJlZGljdCB0aGUgc3Vic2V0IGRhdGEgYmFzZWQgb24gdGhlIG1vZGVsOyBwcmVkaWN0aW9uIGZvciBlbnRpcmUgYXJlYSB0YWtlcyBsb25nZXIgdGltZQ0KcHIyMDExID0gcHJlZGljdChsYW5kc2F0NSwgY2FydCwgdHlwZT0nY2xhc3MnKQ0KcHIyMDExDQojIyBjbGFzcyAgICAgIDogUmFzdGVyTGF5ZXINCiMjIGRpbWVuc2lvbnMgOiAxMjMwLCAxODc3LCAyMzA4NzEwICAobnJvdywgbmNvbCwgbmNlbGwpDQojIyByZXNvbHV0aW9uIDogMC4wMDAyNjk0OTQ2LCAwLjAwMDI2OTQ5NDYgICh4LCB5KQ0KIyMgZXh0ZW50ICAgICA6IC0xMjEuOTI1OCwgLTEyMS40MiwgMzcuODU0MDIsIDM4LjE4NTUgICh4bWluLCB4bWF4LCB5bWluLCB5bWF4KQ0KIyMgY3JzICAgICAgICA6ICtwcm9qPWxvbmdsYXQgK2RhdHVtPVdHUzg0ICtub19kZWZzICtlbGxwcz1XR1M4NCArdG93Z3M4ND0wLDAsMA0KIyMgc291cmNlICAgICA6IG1lbW9yeQ0KIyMgbmFtZXMgICAgICA6IGxheWVyDQojIyB2YWx1ZXMgICAgIDogMSwgOSAgKG1pbiwgbWF4KQ0KIyMgYXR0cmlidXRlcyA6DQojIyAgICAgICAgSUQgdmFsdWUNCiMjICBmcm9tOiAgMSAgICAgMQ0KIyMgICB0byA6ICA4ICAgICA5DQoNCg0KYGBgDQoNCg0KIyMjIyMgQWhvcmEgdHJhY2UgZWwgcmVzdWx0YWRvIGRlIGxhIGNsYXNpZmljYWNpb24gdXNhbmRvIC1yYXN0ZXJWaXMtLiBWZXIgZXN0YWJsZWNlcmEgZWwgLWNsYXNzbmFtZXMtIHBhcmEgZWwgLWNsYXNzdmFsdWVzLS4NCg0KDQpgYGB7cn0NCg0KcHIyMDExID0gcmF0aWZ5KHByMjAxMSkNCnJhdCA9IGxldmVscyhwcjIwMTEpW1sxXV0NCnJhdCRsZWdlbmQgPSBjbGFzc2RmJGNsYXNzbmFtZXMNCmxldmVscyhwcjIwMTEpID0gcmF0DQpsZXZlbHBsb3QocHIyMDExLCBtYXhwaXhlbHMgPSAxZTYsDQogICAgICAgICAgY29sLnJlZ2lvbnMgPSBjbGFzc2NvbG9yLA0KICAgICAgICAgIHNjYWxlcz1saXN0KGRyYXc9RkFMU0UpLA0KICAgICAgICAgIG1haW4gPSAiRGVjaXNpb24gVHJlZSBjbGFzc2lmaWNhdGlvbiBvZiBMYW5kc2F0IDUiKQ0KDQpgYGANCg0KDQojIyMjIyBQcmVndW50YSAxIDogVHJhY2UgIm5sY2QyMDExIiB5ICJwcjIwMTEiIGxhZG8gYSBsYWRvIHkgY29tZW50ZSBzb2JyZSBsYSBwcmVjaXNpb24gZGUgbGEgcHJlZGljY2lvbiAocC4gRWouLCBNZXpjbGEgZW50cmUgY3VsdGl2b3MsIHBhc3RvcywgcGFzdGl6YWxlcyB5IGFyYnVzdG9zKS4NCg0KDQojIyMjIyBFcyBwb3NpYmxlIHF1ZSBkZWJhIHNlbGVjY2lvbmFyIG1hcyBtdWVzdHJhcyB5IHVzYXIgdmFyaWFibGVzIHByZWRpY3RvcmFzIGFkaWNpb25hbGVzLiBMYSBlbGVjY2lvbiBkZWwgY2xhc2lmaWNhZG9yIHRhbWJpZW4ganVlZ2EgdW4gcGFwZWwgaW1wb3J0YW50ZS4NCg0KDQpgYGB7cn0NCg0KbGlicmFyeShncmlkRXh0cmEpDQpwbG90MjAxMW5sY2QgPSBsZXZlbHBsb3QobmxjZDIwMTEsDQogICAgICAgICAgICAgICAgICAgICAgIG1heHBpeGVscyA9IDFlNiwgDQogICAgICAgICAgICAgICAgICAgICAgIHhsYWI9IE5VTEwsIHlsYWI9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgIG1haW49J05hdGlvbmFsIExhbmQgQ292ZXIgRGF0YWJhc2UgMjAxMScsY29sb3JrZXkgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVzPWxpc3QoZHJhdz1GQUxTRSksIGNvbC5yZWdpb25zID0gY2xhc3Njb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gVFJVRSkNCg0KDQpwbG90MjAxMXByID0gbGV2ZWxwbG90KHByMjAxMSwNCiAgICAgICAgICAgICAgICAgICAgICAgbWF4cGl4ZWxzID0gMWU2LCANCiAgICAgICAgICAgICAgICAgICAgICAgeGxhYj0gTlVMTCxueWxhYj0gTlVMTCwNCiAgICAgICAgICAgICAgICAgICAgICAgbWFpbj0nUHJlZGljY2lvbiAyMDExJywgY29sb3JrZXkgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVzPWxpc3QoZHJhdz1GQUxTRSksIGNvbC5yZWdpb25zID0gY2xhc3Njb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gVFJVRSkNCg0KZ3JpZC5hcnJhbmdlKHBsb3QyMDExbmxjZCwgcGxvdDIwMTFwciwgbmNvbD0yKQ0KDQoNCg0KYGBgDQoNCg0KIyMjIyMjIEVsIG1vZGVsbyBkZSBwcmVkaWNjaW9uIGVzIG11Y2hvIG1hcyBkZXRhbGxhZG8sIGxvIHF1ZSBpbmZpZXJlIHF1ZSwgcHJvYmFibGVtbmV0ZSwgZXMgdW4gbW9kZWxvIG11Y2hvIG1hcyBhY29yZGUgYSBsYSByZWFsaWRhZCBkZSBsb3MgZGF0b3MNCg0KDQojIyMgKipUSVRVTE86IEVWQUxVQUNJT04gREVMIE1PREVMTyoqDQoNCg0KIyMjIyMgQWhvcmEsIGV2YWx1ZW1vcyBsYSBwcmVjaXNpb24gZGVsIG1vZGVsbyBwYXJhIHRlbmVyIHVuYSBpZGVhIGRlIGxhIHByZWNpc2lvbiBkZWwgbWFwYSBjbGFzaWZpY2Fkby4gRG9zIG1lZGlkYXMgYW1wbGlhbWVudGUgdXRpbGl6YWRhcyBlbiBsYSB0ZWxlZGV0ZWNjaW9uIHNvbiAicHJlY2lzaW9uIGdlbmVyYWwiIHkgImthcHBhIi4gUHVlZGUgcmVhbGl6YXIgbGEgZXZhbHVhY2lvbiBkZSBwcmVjaXNpb24gdXRpbGl6YW5kbyBsYXMgbXVlc3RyYXMgaW5kZXBlbmRpZW50ZXMgKCAtdmFsaWRhdGlvbjIwMTEtKS4NCg0KDQojIyMjIyBQYXJhIGV2YWx1YXIgY3VhbHF1aWVyIG1vZGVsbywgcHVlZGUgdXNhciBsYSB2YWxpZGFjaW9uIGNydXphZGEgay1mb2xkLiBFbiBlc3RhIHRlY25pY2EsIGxvcyBkYXRvcyB1dGlsaXphZG9zIHBhcmEgYWp1c3RhciBlbCBtb2RlbG8gc2UgZGl2aWRlbiBlbiAtay0gZ3J1cG9zICh0aXBpY2FtZW50ZSA1IGdydXBvcykuIEEgc3UgdmV6LCB1bm8gZGUgbG9zIGdydXBvcyBzZSB1c2FyYSBwYXJhIGxhIHBydWViYSBkZWwgbW9kZWxvLCBtaWVudHJhcyBxdWUgZWwgcmVzdG8gZGUgbG9zIGRhdG9zIHNlIHVzYXJhIHBhcmEgZWwgZW50cmVuYW1pZW50byBkZWwgbW9kZWxvIChhanVzdGUpLg0KDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KGRpc21vKQ0Kc2V0LnNlZWQoOTkpDQpqID0ga2ZvbGQoc2FtcGRhdGEsIGsgPSA1LCBieT1zYW1wZGF0YSRjbGFzc3ZhbHVlKQ0KdGFibGUoaikNCiMjIGoNCiMjICAgMSAgIDIgICAzICAgNCAgIDUNCiMjIDMyMCAzMjAgMzIwIDMyMCAzMjANCg0KYGBgDQoNCg0KIyMjIyMgQWhvcmEgZW50cmVuYW1vcyB5IHByb2JhbW9zIGVsIG1vZGVsbyBjaW5jbyB2ZWNlcywgY2FkYSB2ZXogY2FsY3VsYW5kbyB1bmEgbWF0cml6IGRlIGNvbmZ1c2lvbiBxdWUgYWxtYWNlbmFtb3MgZW4gdW5hIGxpc3RhLg0KDQoNCmBgYHtyfQ0KDQp4ID0gbGlzdCgpDQpmb3IgKGsgaW4gMTo1KSB7DQogICAgdHJhaW4gPSBzYW1wZGF0YVtqIT0gaywgXQ0KICAgIHRlc3QgPSBzYW1wZGF0YVtqID09IGssIF0NCiAgICBjYXJ0ID0gcnBhcnQoYXMuZmFjdG9yKGNsYXNzdmFsdWUpfi4sIGRhdGE9dHJhaW4sIG1ldGhvZCA9ICdjbGFzcycsIG1pbnNwbGl0ID0gNSkNCiAgICBwY2xhc3MgPSBwcmVkaWN0KGNhcnQsIHRlc3QsIHR5cGU9J2NsYXNzJykNCiAgICANCiAgICAjIGNyZWF0ZSBhIGRhdGEuZnJhbWUgdXNpbmcgdGhlIHJlZmVyZW5jZSBhbmQgcHJlZGljdGlvbg0KICAgIHhbW2tdXSA9IGNiaW5kKHRlc3QkY2xhc3N2YWx1ZSwgYXMuaW50ZWdlcihwY2xhc3MpKQ0KfQ0KDQpgYGANCg0KDQojIyMjIyBBaG9yYSBjb21iaW5lIGxvcyBjaW5jbyBlbGVtZW50b3MgZGUgbGEgbGlzdGEgZW4gdW4gc29sbyBkYXRhLmZyYW1lLCB1c2FuZG8gLWRvLmNhbGx5LSBjYWxjdWxlIHVuYSBtYXRyaXogZGUgY29uZnVzaW9uLg0KDQoNCmBgYHtyfQ0KDQp5ID0gZG8uY2FsbChyYmluZCwgeCkNCnkgPSBkYXRhLmZyYW1lKHkpDQpjb2xuYW1lcyh5KSA9IGMoJ29ic2VydmVkJywgJ3ByZWRpY3RlZCcpDQpjb25tYXQgPSB0YWJsZSh5KQ0KDQojIGNoYW5nZSB0aGUgbmFtZSBvZiB0aGUgY2xhc3Nlcw0KY29sbmFtZXMoY29ubWF0KSA9IGNsYXNzZGYkY2xhc3NuYW1lcw0Kcm93bmFtZXMoY29ubWF0KSA9IGNsYXNzZGYkY2xhc3NuYW1lcw0KY29ubWF0DQojIyAgICAgICAgICAgICAgICAgICAgIHByZWRpY3RlZA0KIyMgb2JzZXJ2ZWQgICAgICAgICAgICAgV2F0ZXIgRGV2ZWxvcGVkIEJhcnJlbiBGb3Jlc3QgU2hydWJsYW5kIEhlcmJhY2VvdXMNCiMjICAgV2F0ZXIgICAgICAgICAgICAgICAgMTc1ICAgICAgICAgNiAgICAgIDAgICAgICAzICAgICAgICAgMCAgICAgICAgICAwDQojIyAgIERldmVsb3BlZCAgICAgICAgICAgICAgMiAgICAgICAgOTAgICAgIDUxICAgICAgOCAgICAgICAgMTAgICAgICAgICAyMg0KIyMgICBCYXJyZW4gICAgICAgICAgICAgICAgIDcgICAgICAgIDM5ICAgICA4MiAgICAgIDQgICAgICAgIDE5ICAgICAgICAgMzgNCiMjICAgRm9yZXN0ICAgICAgICAgICAgICAgICAwICAgICAgICAgMiAgICAgIDEgICAgMTA2ICAgICAgICA1NyAgICAgICAgICAxDQojIyAgIFNocnVibGFuZCAgICAgICAgICAgICAgMCAgICAgICAgIDMgICAgICA1ICAgICA1OSAgICAgICAxMDIgICAgICAgICAxMg0KIyMgICBIZXJiYWNlb3VzICAgICAgICAgICAgIDAgICAgICAgICA5ICAgICAzNiAgICAgMTAgICAgICAgIDI3ICAgICAgICAxMDkNCiMjICAgUGxhbnRlZC9DdWx0aXZhdGVkICAgICAwICAgICAgICAgNyAgICAgMTEgICAgIDM0ICAgICAgICA0MiAgICAgICAgIDE5DQojIyAgIFdldGxhbmRzICAgICAgICAgICAgICAxOCAgICAgICAgMTAgICAgICA2ICAgICAzNiAgICAgICAgMjkgICAgICAgICAgNQ0KIyMgICAgICAgICAgICAgICAgICAgICBwcmVkaWN0ZWQNCiMjIG9ic2VydmVkICAgICAgICAgICAgIFBsYW50ZWQvQ3VsdGl2YXRlZCBXZXRsYW5kcw0KIyMgICBXYXRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA3ICAgICAgICA5DQojIyAgIERldmVsb3BlZCAgICAgICAgICAgICAgICAgICAgICAgICAgMTEgICAgICAgIDYNCiMjICAgQmFycmVuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNSAgICAgICAgNg0KIyMgICBGb3Jlc3QgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA2ICAgICAgIDI3DQojIyAgIFNocnVibGFuZCAgICAgICAgICAgICAgICAgICAgICAgICAgMTIgICAgICAgIDcNCiMjICAgSGVyYmFjZW91cyAgICAgICAgICAgICAgICAgICAgICAgICAgOCAgICAgICAgMQ0KIyMgICBQbGFudGVkL0N1bHRpdmF0ZWQgICAgICAgICAgICAgICAgIDY5ICAgICAgIDE4DQojIyAgIFdldGxhbmRzICAgICAgICAgICAgICAgICAgICAgICAgICAgMzMgICAgICAgNjMNCg0KYGBgDQoNCg0KIyMjIyMgUHJlZ3VudGEgMiA6IENvbWVudGUgc29icmUgbGEgY2xhc2lmaWNhY2nDs24gZXJyb25lYSBlbnRyZSBkaWZlcmVudGVzIGNsYXNlcy4NCg0KIyMjIyMgUHJlZ3VudGEgMyA6IMK/U2UgdGUgb2N1cnJlbiBmb3JtYXMgZGUgbWVqb3JhciBsYSBwcmVjaXNpb24/DQoNCg0KDQojIyMjIyBDYWxjdWxlIGxhIHByZWNpc2lvbiBnZW5lcmFsIHkgbGEgZXN0YWRpc3RpY2EgIkthcHBhIi4NCg0KDQojIyMjIyBQcmVjaXNpb24gZ2VuZXJhbDoNCg0KDQpgYGB7cn0NCg0KIyBudW1iZXIgb2YgY2FzZXMNCm4gPSBzdW0oY29ubWF0KQ0Kbg0KIyMgWzFdIDE2MDANCiMgbnVtYmVyIG9mIGNvcnJlY3RseSBjbGFzc2lmaWVkIGNhc2VzIHBlciBjbGFzcw0KZGlhZyA9IGRpYWcoY29ubWF0KQ0KIyBPdmVyYWxsIEFjY3VyYWN5DQpPQSA9IHN1bShkaWFnKSAvIG4NCk9BDQojIyBbMV0gMC40OTc1DQoNCmBgYA0KDQoNCiMjIyMjIEthcHBhOg0KDQoNCmBgYHtyfQ0KDQojIG9ic2VydmVkICh0cnVlKSBjYXNlcyBwZXIgY2xhc3MNCnJvd3N1bXMgPSBhcHBseShjb25tYXQsIDEsIHN1bSkNCnAgPSByb3dzdW1zIC8gbg0KIyBwcmVkaWN0ZWQgY2FzZXMgcGVyIGNsYXNzDQpjb2xzdW1zID0gYXBwbHkoY29ubWF0LCAyLCBzdW0pDQpxID0gY29sc3VtcyAvIG4NCmV4cEFjY3VyYWN5ID0gc3VtKHAqcSkNCmthcHBhID0gKE9BIC0gZXhwQWNjdXJhY3kpIC8gKDEgLSBleHBBY2N1cmFjeSkNCmthcHBhDQojIyBbMV0gMC40MjU3MTQzDQoNCmBgYA0KDQoNCiMjIyMjIFByb2R1Y3RvciB5IHByZWNpc2lvbiBkZWwgdXN1YXJpbw0KDQoNCmBgYHtyfQ0KDQojIFByb2R1Y2VyIGFjY3VyYWN5DQpQQSA9IGRpYWcgLyBjb2xzdW1zDQoNCiMgVXNlciBhY2N1cmFjeQ0KVUEgPSBkaWFnIC8gcm93c3Vtcw0Kb3V0QWNjID0gZGF0YS5mcmFtZShwcm9kdWNlckFjY3VyYWN5ID0gUEEsIHVzZXJBY2N1cmFjeSA9IFVBKQ0Kb3V0QWNjDQojIyAgICAgICAgICAgICAgICAgICAgcHJvZHVjZXJBY2N1cmFjeSB1c2VyQWNjdXJhY3kNCiMjIFdhdGVyICAgICAgICAgICAgICAgICAgICAgMC44NjYzMzY2ICAgICAgICAwLjg3NQ0KIyMgRGV2ZWxvcGVkICAgICAgICAgICAgICAgICAwLjU0MjE2ODcgICAgICAgIDAuNDUwDQojIyBCYXJyZW4gICAgICAgICAgICAgICAgICAgIDAuNDI3MDgzMyAgICAgICAgMC40MTANCiMjIEZvcmVzdCAgICAgICAgICAgICAgICAgICAgMC40MDc2OTIzICAgICAgICAwLjUzMA0KIyMgU2hydWJsYW5kICAgICAgICAgICAgICAgICAwLjM1NjY0MzQgICAgICAgIDAuNTEwDQojIyBIZXJiYWNlb3VzICAgICAgICAgICAgICAgIDAuNTI5MTI2MiAgICAgICAgMC41NDUNCiMjIFBsYW50ZWQvQ3VsdGl2YXRlZCAgICAgICAgMC40NTY5NTM2ICAgICAgICAwLjM0NQ0KIyMgV2V0bGFuZHMgICAgICAgICAgICAgICAgICAwLjQ1OTg1NDAgICAgICAgIDAuMzE1DQoNCmBgYA0KDQoNCiMjIyMjIFByZWd1bnRhIDQgOiBSZWFsaWNlIGxhIGNsYXNpZmljYWNpb24gdXRpbGl6YW5kbyBjbGFzaWZpY2Fkb3JlcyBkZSBib3NxdWUgYWxlYXRvcmlvIGRlbCBwYXF1ZXRlICJyYW5kb21Gb3Jlc3QiDQoNCiMjIyMjIFByZWd1bnRhIDUgOiBHcmFmaXF1ZSBsb3MgcmVzdWx0YWRvcyBkZWwgY2xhc2lmaWNhZG9yIHJwYXJ0IHkgUmFuZG9tIEZvcmVzdCB1bm8gYWwgbGFkbyBkZWwgb3Ryby4NCg0KIyMjIyMgUHJlZ3VudGEgNiAob3BjaW9uYWwpIDogcmVwaXRhIGxvcyBwYXNvcyBwYXJhIGVsIGHDsW8gMjAwMSB1dGlsaXphbmRvIFJhbmRvbSBGb3Jlc3QgLiBVc2UgbGEgaW1hZ2VuIGNvbXB1ZXN0YSBzaW4gbnViZXMgLWRhdGEvY2VudHJhbHZhbGxleS0yMDAxTEU3LnRpZi0uIEVzdG9zIHNvbiBkYXRvcyBkZSBMYW5kc2F0IDcgLiBVdGlsaWNlIGNvbW8gZGF0b3MgZGUgcmVmZXJlbmNpYSBsYSBCYXNlIGRlIERhdG9zIE5hY2lvbmFsIGRlIENvYmVydHVyYSBkZSBUaWVycmFzIDIwMDEgKE5MQ0QgMjAwMSkgcGFyYSBlbCBzdWJjb25qdW50byBkZWwgVmFsbGUgQ2VudHJhbCBkZSBDYWxpZm9ybmlhLiAqDQoNCiMjIyMjIFByZWd1bnRhIDcgKG9wY2lvbmFsKSA6IEhlbW9zIGVudHJlbmFkbyBhIGxvcyBjbGFzaWZpY2Fkb3JlcyB1dGlsaXphbmRvIDIwMCBtdWVzdHJhcyBwYXJhIGNhZGEgY2xhc2UuIEludmVzdGlndWUgZWwgZWZlY3RvIGRlbCB0YW1hw7FvIGRlIGxhIG11ZXN0cmEgZW4gbGEgY2xhc2lmaWNhY2lvbi4gUmVwaXRhIGxvcyBwYXNvcyBjb24gZGlmZXJlbnRlcyBzdWJjb25qdW50b3MsIHBvciBlamVtcGxvLCB1biB0YW1hw7FvIGRlIG11ZXN0cmEgZGUgMTUwLCAxMDAsIDUwIHBvciBjbGFzZSwgeSBjb21wYXJlIGxvcyByZXN1bHRhZG9zLiBVc2UgbGFzIG1pc21hcyBtdWVzdHJhcyBkZSByZXNlcnZhIHBhcmEgbGEgZXZhbHVhY2lvbiBkZWwgbW9kZWxvLg0KDQoNCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4NCg0KV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuDQoNClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4NCg==