Librería gstat:
Modelado, Predicción y Simulación Geoestadística Espacial y Espacio-Temporal
Modelado de variogramas; (co)kriging simple, ordinario y universal de punto o bloque; kriging espacio-temporal; (co)simulación secuencial gaussiana o de indicadores; funciones de utilidad de trazado de mapas de variogramas y variogramas; admite sf y estrellas.
library(sp) # Para el manejo de datos espaciales
library(gstat)
Análisis y descripción de los datos
Datos: Meuse
conjunto de datos de tipo “DataFrame” proporcionado por el paquete sp
que comprende cuatro metales pesados medidos en la capa superior del suelo en una llanura aluvial a lo largo del río Meuse.
data(meuse)
data(meuse.riv) # Rivera del río Meuse
str(meuse)
'data.frame': 155 obs. of 14 variables:
$ x : num 181072 181025 181165 181298 181307 ...
$ y : num 333611 333558 333537 333484 333330 ...
$ cadmium: num 11.7 8.6 6.5 2.6 2.8 3 3.2 2.8 2.4 1.6 ...
$ copper : num 85 81 68 81 48 61 31 29 37 24 ...
$ lead : num 299 277 199 116 117 137 132 150 133 80 ...
$ zinc : num 1022 1141 640 257 269 ...
$ elev : num 7.91 6.98 7.8 7.66 7.48 ...
$ dist : num 0.00136 0.01222 0.10303 0.19009 0.27709 ...
$ om : num 13.6 14 13 8 8.7 7.8 9.2 9.5 10.6 6.3 ...
$ ffreq : Factor w/ 3 levels "1","2","3": 1 1 1 1 1 1 1 1 1 1 ...
$ soil : Factor w/ 3 levels "1","2","3": 1 1 1 2 2 2 2 1 1 2 ...
$ lime : Factor w/ 2 levels "0","1": 2 2 2 1 1 1 1 1 1 1 ...
$ landuse: Factor w/ 15 levels "Aa","Ab","Ag",..: 4 4 4 11 4 11 4 2 2 15 ...
$ dist.m : num 50 30 150 270 380 470 240 120 240 420 ...
coordinates(meuse) = c("x", "y") # Convierte a datos espaciales
# Grafico
plot(meuse, asp = 1, pch = 1, main="Río Meuse")
lines (meuse.riv)

Exploración de la variable Zinc
En el siguiente diagrama de barras y el diagrama de cajas y bigotes, observamos que la distribución de la variable zinc no es simétrica, pues está sesgada a la derecha. En el resúmen, se observa que la media es mayor que la mediana y en el diagrama de cajas se calculan algunos valores atípicos.
hist(meuse$zinc)

boxplot(meuse$zinc, horizontal=T)

summary(meuse$zinc)
Min. 1st Qu. Median Mean 3rd Qu. Max.
113.0 198.0 326.0 469.7 674.5 1839.0
# Grafico
plot(meuse, asp = 1, cex = 4*meuse$zinc/max(meuse$zinc),
pch = 1, main="Río Meuse - Zinc")
lines (meuse.riv)

Calculando logaritmo
De acuerdo a lo anterior, al ser una distribución no simétrica, se aconseja aplicar logaritmo para transformar los valores y obtener una distribución simétrica (normal). Además, esto reduce los posibles outliers o valores atípicos.
meuse$logZn = log10(meuse$zinc)
summary(meuse$logZn)
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.053 2.297 2.513 2.556 2.829 3.265
boxplot(meuse$logZn, horizontal=T)

Análisis estructural
La primera etapa en el desarrollo de un análisis geoestadístico es la determinación de la dependencia espacial entre los datos medidos de una variable. Esta fase es también conocida como análisis estructural. Para llevarla a cabo, con base en la información muestral, se usan tres funciones: El semivariograma, el covariograma y el correlograma, aunque en la práctica se emplea el semivariograma y no las otras dos funciones.
Para interpretar el semivariograma experimental se parte del criterio de que a menor distancia entre los sitios mayor similitud o correlación espacial entre las observaciones. Por ello en presencia de autocorrelación se espera que para valores de h pequeños el semivariograma experimental tenga magnitudes menores a las que este toma cuando las distancias h se incrementan.
En nuestro caso, se concluye que hay autocorrelación espacial.
variogram:
Calcula el variograma muestral o residual a partir de los datos, o en caso de que se proporcione un modelo lineal, para los residuos, con opciones de variograma direccional, robusto y agrupado, y para intervalos de distancia irregulares.
Sus parámetros logZn~1
indican ausencia de regresores, meuse
los datos, cutoff=1300
la distancia de separación espacial hasta la cual los pares de puntos se incluyen en las estimaciones de semivarianza y width=90
, el ancho de los intervalos de distancia.
Este comando retorna un objeto con los siguientes atributos: np
: el número de pares de puntos para esta estimación; dist
: la distancia promedio de todos los pares de puntos considerados para esta estimación y gamma:
la estimación real del variograma de la muestra. Además indica la dirección horizontal, vertical y el id del par combinado.
ve = variogram(logZn ~ 1, meuse, cutoff = 1300, width = 90)
ve
np dist gamma dir.hor dir.ver id
1 41 72.24836 0.02649954 0 0 var1
2 212 142.88031 0.03242411 0 0 var1
3 320 227.32202 0.04818895 0 0 var1
4 371 315.85549 0.06543093 0 0 var1
5 423 406.44801 0.08025949 0 0 var1
6 458 496.09401 0.09509850 0 0 var1
7 455 586.78634 0.10656591 0 0 var1
8 466 677.39566 0.10333481 0 0 var1
9 503 764.55712 0.11461332 0 0 var1
10 480 856.69422 0.12924402 0 0 var1
11 468 944.02864 0.12290106 0 0 var1
12 460 1033.62277 0.12820318 0 0 var1
13 422 1125.63214 0.13206510 0 0 var1
14 408 1212.62350 0.11591294 0 0 var1
15 173 1280.65364 0.11719960 0 0 var1
plot(ve, plot.numbers = T)

Todos estos modelos tienen tres parámetros:
Pepita: Se denota por \(C_0\) y representa una discontinuidad puntual del semivariograma en el origen.
Meseta: Es la cota superior del semivariograma. Se denota por \(C_0+C_1\).
Rango: La separación o distancia entre pares de puntos en la cual ya no hay dependencia espacial.

Método para seleccionar el mejor ajuste del modelo teórico
- Conocer el proceso espacial
- Ajuste visual
- Ajuste automático y comparar la bondad del ajuste
A continuación se muestra un gráfico (Trellis) de los modelos diferentes teóricos de variogramas mediante la función show.vgms()
show.vgms()

Ajuste visual (Modelo esférico)
En nuestro ejemplo, visualmente se observa:
Rango: Aproximadamente 850 m.
Pepita = Aproximadamente 0.01
Meseta = 0.13
vgm
: Genera un modelo de variograma o agrega a un modelo existente ingresando los parámetros: rango, silla sin \(C_0\), pepita \(C_0\), y el modelo que para este caso, es esférico (Sph).
vt = vgm(psill=0.12, range=850, nugget=0.01,
model="Sph")
vt
plot(ve, pl = T, model = vt)

Ajuste automático
fit.variogram:
Ajusta rangos y/o umbrales de un modelo de variograma simple o anidado a un variograma de muestra. Cuyos parámetros son el variograma y el variograma ajustado.
va = fit.variogram(ve, vt)
va
plot(ve, pl=T, model=va)

Interpolación: Kriging ordinario
Lo primero es crear una cuadrícula dónde se predecirán la concentración de Zinc.
data(meuse.grid) # Cuadrícula
coordinates(meuse.grid) = c("x", "y")
plot(meuse.grid)

# Promueve una estructura no cuadriculada a una cuadrícula
gridded(meuse.grid) = T
krige:
Función para kriging simple, ordinario o universal (a veces llamado kriging de deriva externa), función para la interpolación ponderada de distancia inversa. (Para nuesto caso kriging ordinario)
OK = krige(logZn~1, locations=meuse, newdata=meuse.grid, model=va)
[using ordinary kriging]
names(OK)
[1] "var1.pred" "var1.var"
# Retomando los valores originales del Zinc.
OK$pred = 10^(OK$var1.pred)
# Graficando
pts.s = list("sp.points", meuse, col="white",
pch=1, cex = 4*meuse$zinc/max(meuse$zinc))
print(spplot(OK, "pred", asp=1,
col.regions=rev(bpy.colors(64)),
main = "Predicciones modelo Esférico",
sp.layout=list(pts.s)))

Ejercicio:
Al variograma experimental calculado ve
, realizar el ajuste e interpolación con el modelo teórico Exponencial
# Ajuste visual:
vt1 = vgm(psill=0.12, range=850, nugget=0.01, model="Exp")
plot(ve, pl = T, model = vt1)

vt1
model psill range
1 Nug 0.01 0
2 Exp 0.12 850
# Ajuste automatico:
va1 = fit.variogram(ve, vt1)
plot(ve, pl=T, model=va1)

va1
model psill range
1 Nug 0.004989641 0.0000
2 Exp 0.154851224 635.6953
# Interpolacion:
OK1 = krige(logZn~1, locations=meuse, newdata=meuse.grid, model=va1)
[using ordinary kriging]
OK1$pred = 10^(OK1$var1.pred)
print(spplot(OK1, "pred", asp=1,
col.regions=rev(bpy.colors(64)),
main = "Predicciones modelo Exponencial",
sp.layout=list(pts.s)))

Validación cruzada
Consiste en excluir la observación de uno de los n puntos muestrales y con los n-1 valores restantes y el modelo de semivariograma escogido, predecir vía kriging el valor de la variable en estudio en la ubicación del punto que se excluyó.
krige.cv
Funciones de validación cruzada para (co) kriging, kriging de puntos simples, ordinarios o universales en un vecindario local. Retorna un DataFrame que contiene las coordenadas de los datos o las de la primera variable en el objeto, y columnas de predicción y varianza de predicción de puntos de datos validados cruzados, valores observados, residuales, zscore (residuo dividido por el error estándar de kriging).
# Modelo esferico
valida = krige.cv(log(zinc)~1, meuse, va, nmax=40, nfold=5) # Esferico
# Modelo exponencial
valida1 = krige.cv(log(zinc)~1, meuse, va1, nmax=40, nfold=5)
bubble(valida, "residual",
main = "Log(zinc): 5-fold cv residuales \nModelo esférico ")

Errores de predicción
Se piensa que si el modelo de semivarianza elegido describe bien la estructura de autocorrelación espacial, entonces la diferencia entre el valor observado y el valor predicho debe ser pequeña. Este procedimiento se realiza en forma secuencial con cada uno de los puntos muestrales y así se obtiene un conjunto de n “errores de predicción”.
Inicialmente podemos usar las siguientes medidas: error medio (ME), error absoluto medio (MAE), error cuadrático medio (RMSE) y relación de desviación cuadrática media (MSDR) de los residuos con varianza kriging.
Errores de predicción para el modelo esférico
# Mean Error (ME)
ME = round(mean(valida$residual),3)
ME
[1] -0.006
# Mean Absolute Error
MAE = round(mean(abs(valida$residual)),3)
MAE
[1] 0.309
# Root Mean Squre Error (RMSE)
RMSE = round(sqrt(mean(valida$residual^2)),3)
RMSE
[1] 0.408
# Mean Squared Deviation Ratio (MSDR)
MSDR = mean(valida$residual^2/valida$var1.var)
MSDR
[1] 4.513082
Errores de predicción para el modelo exponencial
# Mean Error (ME)
ME1 = round(mean(valida1$residual),3)
ME1
[1] 0.007
# Mean Absolute Error
MAE1 = round(mean(abs(valida1$residual)),3)
MAE1
[1] 0.313
# Root Mean Squre Error (RMSE)
RMSE1 = round(sqrt(mean(valida1$residual^2)),3)
RMSE1
[1] 0.444
# Mean Squared Deviation Ratio (MSDR)
MSDR1 = mean(valida1$residual^2/valida1$var1.var)
MSDR1
[1] 4.642942
Se puede decir que aunque los errores son muy similares, el modelo esférico tiene un mejor comportamiento ya que sus errores son más pequeños por milésimas.
lm.cv
Esta función proporciona medidas de validación interna y cruzada de precisión predictiva para la regresión lineal ordinaria
lm.cv = lm(valida$var1.pred ~ valida$observed)
summary(lm.cv)
Call:
lm(formula = valida$var1.pred ~ valida$observed)
Residuals:
Min 1Q Median 3Q Max
-1.09336 -0.22331 -0.01363 0.23209 0.72965
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.11730 0.22426 9.441 <2e-16 ***
valida$observed 0.63943 0.03782 16.907 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3388 on 153 degrees of freedom
Multiple R-squared: 0.6514, Adjusted R-squared: 0.6491
F-statistic: 285.8 on 1 and 153 DF, p-value: < 2.2e-16
plot(valida$var1.pred ~ valida$observed)
par(new=TRUE)
plot(1.96640+0.66687*valida$observed~valida$observed,
type="l", ylab="valida$var1.pred", axes=FALSE)

Tarea: Distancia Inversa Ponderada (IDW)
La interpolación mediante distancia inversa ponderada determina los valores de celda a través de una combinación ponderada linealmente de un conjunto de puntos de muestra. La ponderación es una función de la distancia inversa. La superficie que se interpola debe ser la de una variable dependiente de la ubicación.
Este método presupone que la variable que se representa cartográficamente disminuye su influencia a mayor distancia desde su ubicación de muestra. Por ejemplo, al interpolar una superficie de poder adquisitivo de los consumidores para analizar las ventas minoristas de un sitio, el poder adquisitivo de una ubicación más distante tendrá menos influencia porque es más probable que las personas compren cerca de sus casas.
La Distancia Inversa Ponderada (IDW) es matemática (determinista) asumiendo que los valores más cercanos están más relacionados que otros con su función.
Referencias
[1] https://desktop.arcgis.com/es/arcmap/10.3/tools/spatial-analyst-toolbox/how-idw-works.htm
[2] https://acolita.com/interpolacion-con-la-distancia-inversa-ponderada-idw/
LS0tDQp0aXRsZTogIlByZWRpY2Npw7NuIEVzcGFjaWFsIChLcmlnaW5nKSINCmF1dGhvcjogIkVpbGluIEx1bmEgTS4iDQpkYXRlOiAiMTEgZGUgYWJyaWwgZGUgMjAyMSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyAqTGlicmVyw61hIGdzdGF0OioNCioqTW9kZWxhZG8sIFByZWRpY2Npw7NuIHkgU2ltdWxhY2nDs24gR2VvZXN0YWTDrXN0aWNhIEVzcGFjaWFsIHkgRXNwYWNpby1UZW1wb3JhbCoqICANCk1vZGVsYWRvIGRlIHZhcmlvZ3JhbWFzOyAoY28pa3JpZ2luZyBzaW1wbGUsICpvcmRpbmFyaW8qIHkgdW5pdmVyc2FsIGRlIHB1bnRvIG8gYmxvcXVlOyBrcmlnaW5nIGVzcGFjaW8tdGVtcG9yYWw7IChjbylzaW11bGFjacOzbiBzZWN1ZW5jaWFsIGdhdXNzaWFuYSBvIGRlIGluZGljYWRvcmVzOyBmdW5jaW9uZXMgZGUgdXRpbGlkYWQgZGUgdHJhemFkbyBkZSBtYXBhcyBkZSB2YXJpb2dyYW1hcyB5IHZhcmlvZ3JhbWFzOyBhZG1pdGUgc2YgeSBlc3RyZWxsYXMuDQoNCmBgYHtyfQ0KbGlicmFyeShzcCkgIyBQYXJhIGVsIG1hbmVqbyBkZSBkYXRvcyBlc3BhY2lhbGVzDQpsaWJyYXJ5KGdzdGF0KSANCmBgYA0KDQojIyAqKkFuw6FsaXNpcyB5IGRlc2NyaXBjacOzbiBkZSBsb3MgZGF0b3MqKg0KDQojIyMgKkRhdG9zOiBNZXVzZSoNCmNvbmp1bnRvIGRlIGRhdG9zIGRlIHRpcG8g4oCcRGF0YUZyYW1l4oCdIHByb3BvcmNpb25hZG8gcG9yIGVsIHBhcXVldGUgYHNwYCBxdWUgY29tcHJlbmRlIGN1YXRybyBtZXRhbGVzIHBlc2Fkb3MgbWVkaWRvcyBlbiBsYSBjYXBhIHN1cGVyaW9yIGRlbCBzdWVsbyBlbiB1bmEgbGxhbnVyYSBhbHV2aWFsIGEgbG8gbGFyZ28gZGVsIHLDrW8gTWV1c2UuIA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCmRhdGEobWV1c2UpDQpkYXRhKG1ldXNlLnJpdikgICMgUml2ZXJhIGRlbCByw61vIE1ldXNlDQpzdHIobWV1c2UpDQpjb29yZGluYXRlcyhtZXVzZSkgPSBjKCJ4IiwgInkiKSAjIENvbnZpZXJ0ZSBhIGRhdG9zIGVzcGFjaWFsZXMNCg0KIyBHcmFmaWNvDQpwbG90KG1ldXNlLCBhc3AgPSAxLCBwY2ggPSAxLCBtYWluPSJSw61vIE1ldXNlIikNCmxpbmVzIChtZXVzZS5yaXYpIA0KYGBgDQoNCiMjIyAqRXhwbG9yYWNpw7NuIGRlIGxhIHZhcmlhYmxlIFppbmMqDQoNCkVuIGVsIHNpZ3VpZW50ZSBkaWFncmFtYSBkZSBiYXJyYXMgeSBlbCBkaWFncmFtYSBkZSBjYWphcyB5IGJpZ290ZXMsIG9ic2VydmFtb3MgcXVlIGxhIGRpc3RyaWJ1Y2nDs24gZGUgbGEgdmFyaWFibGUgemluYyBubyBlcyBzaW3DqXRyaWNhLCBwdWVzIGVzdMOhIHNlc2dhZGEgYSBsYSBkZXJlY2hhLiBFbiBlbCByZXPDum1lbiwgc2Ugb2JzZXJ2YSBxdWUgbGEgbWVkaWEgZXMgbWF5b3IgcXVlIGxhIG1lZGlhbmEgeSBlbiBlbCBkaWFncmFtYSBkZSBjYWphcyBzZSBjYWxjdWxhbiBhbGd1bm9zIHZhbG9yZXMgYXTDrXBpY29zLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCmhpc3QobWV1c2UkemluYykgDQpib3hwbG90KG1ldXNlJHppbmMsIGhvcml6b250YWw9VCkNCnN1bW1hcnkobWV1c2UkemluYykNCg0KIyBHcmFmaWNvDQpwbG90KG1ldXNlLCBhc3AgPSAxLCBjZXggPSA0Km1ldXNlJHppbmMvbWF4KG1ldXNlJHppbmMpLCANCiAgICAgcGNoID0gMSwgIG1haW49IlLDrW8gTWV1c2UgLSBaaW5jIikNCmxpbmVzIChtZXVzZS5yaXYpIA0KYGBgDQojIyMgKkNhbGN1bGFuZG8gbG9nYXJpdG1vKg0KDQpEZSBhY3VlcmRvIGEgbG8gYW50ZXJpb3IsIGFsIHNlciB1bmEgZGlzdHJpYnVjacOzbiBubyBzaW3DqXRyaWNhLCBzZSBhY29uc2VqYSBhcGxpY2FyIGxvZ2FyaXRtbyBwYXJhIHRyYW5zZm9ybWFyIGxvcyB2YWxvcmVzIHkgb2J0ZW5lciB1bmEgZGlzdHJpYnVjacOzbiBzaW3DqXRyaWNhIChub3JtYWwpLiBBZGVtw6FzLCBlc3RvIHJlZHVjZSBsb3MgcG9zaWJsZXMgb3V0bGllcnMgbyB2YWxvcmVzIGF0w61waWNvcy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1ldXNlJGxvZ1puID0gbG9nMTAobWV1c2UkemluYykNCnN1bW1hcnkobWV1c2UkbG9nWm4pDQpib3hwbG90KG1ldXNlJGxvZ1puLCBob3Jpem9udGFsPVQpDQpgYGANCg0KIyMgKipBbsOhbGlzaXMgZXN0cnVjdHVyYWwqKg0KDQpMYSBwcmltZXJhIGV0YXBhIGVuIGVsIGRlc2Fycm9sbG8gZGUgdW4gYW7DoWxpc2lzIGdlb2VzdGFkw61zdGljbyBlcyBsYSBkZXRlcm1pbmFjacOzbiBkZSBsYSBkZXBlbmRlbmNpYSBlc3BhY2lhbCBlbnRyZSBsb3MgZGF0b3MgbWVkaWRvcyBkZSB1bmEgdmFyaWFibGUuIEVzdGEgZmFzZSBlcyB0YW1iacOpbiBjb25vY2lkYSBjb21vIGFuw6FsaXNpcyBlc3RydWN0dXJhbC4gUGFyYSBsbGV2YXJsYSBhIGNhYm8sIGNvbiBiYXNlIGVuIGxhIGluZm9ybWFjacOzbiBtdWVzdHJhbCwgc2UgdXNhbiB0cmVzIGZ1bmNpb25lczogRWwgc2VtaXZhcmlvZ3JhbWEsIGVsIGNvdmFyaW9ncmFtYSB5IGVsIGNvcnJlbG9ncmFtYSwgYXVucXVlIGVuIGxhIHByw6FjdGljYSBzZSBlbXBsZWEgZWwgc2VtaXZhcmlvZ3JhbWEgeSBubyBsYXMgb3RyYXMgZG9zIGZ1bmNpb25lcy4NCg0KUGFyYSBpbnRlcnByZXRhciBlbCAqKnNlbWl2YXJpb2dyYW1hIGV4cGVyaW1lbnRhbCoqIHNlIHBhcnRlIGRlbCBjcml0ZXJpbyBkZSBxdWUgYSBtZW5vciBkaXN0YW5jaWEgZW50cmUgbG9zIHNpdGlvcyBtYXlvciBzaW1pbGl0dWQgbyBjb3JyZWxhY2nDs24gZXNwYWNpYWwgZW50cmUgbGFzIG9ic2VydmFjaW9uZXMuIFBvciBlbGxvIGVuIHByZXNlbmNpYSBkZSBhdXRvY29ycmVsYWNpw7NuIHNlIGVzcGVyYSBxdWUgcGFyYSB2YWxvcmVzIGRlIGggcGVxdWXDsW9zIGVsIHNlbWl2YXJpb2dyYW1hIGV4cGVyaW1lbnRhbCB0ZW5nYSBtYWduaXR1ZGVzIG1lbm9yZXMgYSBsYXMgcXVlIGVzdGUgdG9tYSBjdWFuZG8gbGFzIGRpc3RhbmNpYXMgaCBzZSBpbmNyZW1lbnRhbi4gDQoNCkVuIG51ZXN0cm8gY2Fzbywgc2UgY29uY2x1eWUgcXVlIGhheSBhdXRvY29ycmVsYWNpw7NuIGVzcGFjaWFsLg0KDQorIGB2YXJpb2dyYW06YCAqKkNhbGN1bGEgZWwgdmFyaW9ncmFtYSBtdWVzdHJhbCBvIHJlc2lkdWFsKiogYSBwYXJ0aXIgZGUgbG9zIGRhdG9zLCBvIGVuIGNhc28gZGUgcXVlIHNlIHByb3BvcmNpb25lIHVuIG1vZGVsbyBsaW5lYWwsIHBhcmEgbG9zIHJlc2lkdW9zLCBjb24gb3BjaW9uZXMgZGUgdmFyaW9ncmFtYSBkaXJlY2Npb25hbCwgcm9idXN0byB5IGFncnVwYWRvLCB5IHBhcmEgaW50ZXJ2YWxvcyBkZSBkaXN0YW5jaWEgaXJyZWd1bGFyZXMuICAgDQogIFN1cyBwYXLDoW1ldHJvcyBgbG9nWm5+MWAgaW5kaWNhbiBhdXNlbmNpYSBkZSByZWdyZXNvcmVzLCBgbWV1c2VgIGxvcyBkYXRvcywgYGN1dG9mZj0xMzAwYCBsYSBkaXN0YW5jaWEgZGUgc2VwYXJhY2nDs24gZXNwYWNpYWwgaGFzdGEgbGEgY3VhbCBsb3MgcGFyZXMgZGUgcHVudG9zIHNlIGluY2x1eWVuIGVuIGxhcyBlc3RpbWFjaW9uZXMgZGUgc2VtaXZhcmlhbnphIHkgYHdpZHRoPTkwYCwgZWwgYW5jaG8gZGUgbG9zIGludGVydmFsb3MgZGUgZGlzdGFuY2lhLiAgIA0KICBFc3RlIGNvbWFuZG8gcmV0b3JuYSB1biBvYmpldG8gY29uIGxvcyBzaWd1aWVudGVzIGF0cmlidXRvczogYG5wYDogZWwgbsO6bWVybyBkZSBwYXJlcyBkZSBwdW50b3MgcGFyYSBlc3RhIGVzdGltYWNpw7NuOyBgZGlzdGA6IGxhIGRpc3RhbmNpYSBwcm9tZWRpbyBkZSB0b2RvcyBsb3MgcGFyZXMgZGUgcHVudG9zIGNvbnNpZGVyYWRvcyBwYXJhIGVzdGEgZXN0aW1hY2nDs24geSBgZ2FtbWE6YCBsYSBlc3RpbWFjacOzbiByZWFsIGRlbCB2YXJpb2dyYW1hIGRlIGxhIG11ZXN0cmEuIEFkZW3DoXMgaW5kaWNhIGxhIGRpcmVjY2nDs24gaG9yaXpvbnRhbCwgdmVydGljYWwgeSBlbCBpZCBkZWwgcGFyIGNvbWJpbmFkby4gIA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCnZlID0gdmFyaW9ncmFtKGxvZ1puIH4gMSwgbWV1c2UsIGN1dG9mZiA9IDEzMDAsIHdpZHRoID0gOTApDQp2ZQ0KcGxvdCh2ZSwgcGxvdC5udW1iZXJzID0gVCkNCmBgYA0KVG9kb3MgZXN0b3MgbW9kZWxvcyB0aWVuZW4gdHJlcyBwYXLDoW1ldHJvczogIA0KDQorICoqUGVwaXRhOioqIFNlIGRlbm90YSBwb3IgJENfMCQgeSByZXByZXNlbnRhIHVuYSBkaXNjb250aW51aWRhZCBwdW50dWFsIGRlbCBzZW1pdmFyaW9ncmFtYSBlbiBlbCBvcmlnZW4uICANCisgKipNZXNldGE6KiogRXMgbGEgY290YSBzdXBlcmlvciBkZWwgc2VtaXZhcmlvZ3JhbWEuIFNlIGRlbm90YSBwb3IgJENfMCtDXzEkLiAgDQoNCisgKipSYW5nbzoqKiBMYSBzZXBhcmFjacOzbiBvIGRpc3RhbmNpYSBlbnRyZSBwYXJlcyBkZSBwdW50b3MgZW4gbGEgY3VhbCB5YSBubyBoYXkgZGVwZW5kZW5jaWEgZXNwYWNpYWwuICANCg0KDQohW10oaHR0cHM6Ly93d3cucmVzZWFyY2hnYXRlLm5ldC9wcm9maWxlL1NhbWlyYW4tQmFuZXJqZWUvcHVibGljYXRpb24vMjk5ODIwMTY1L2ZpZ3VyZS9maWcxL0FTOjM0ODAyNDg0NTk0Njg4MEAxNDU5OTg2OTk3NTM5L0FuLWVtcGlyaWNhbC1zZW1pdmFyaW9ncmFtLXNob3dpbmctbnVnZ2V0LXNpbGwtYW5kLXJhbmdlLVJhbmdlLWluZGljYXRlcy10aGUtYXJlYS1vZi5wbmcpDQoNCiMjICoqTcOpdG9kbyBwYXJhIHNlbGVjY2lvbmFyIGVsIG1lam9yIGFqdXN0ZSBkZWwgbW9kZWxvIHRlw7NyaWNvKioNCjEuCUNvbm9jZXIgZWwgcHJvY2VzbyBlc3BhY2lhbCANCjIuCUFqdXN0ZSB2aXN1YWwNCjMuCUFqdXN0ZSBhdXRvbcOhdGljbyB5IGNvbXBhcmFyIGxhIGJvbmRhZCBkZWwgYWp1c3RlDQoNCkEgY29udGludWFjacOzbiBzZSBtdWVzdHJhIHVuIGdyw6FmaWNvIChUcmVsbGlzKSBkZSBsb3MgbW9kZWxvcyBkaWZlcmVudGVzIHRlw7NyaWNvcyBkZSB2YXJpb2dyYW1hcyBtZWRpYW50ZSBsYSBmdW5jacOzbiBgc2hvdy52Z21zKClgDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0Kc2hvdy52Z21zKCkNCmBgYA0KIyMjICpBanVzdGUgdmlzdWFsIChNb2RlbG8gZXNmw6lyaWNvKSoNCg0KRW4gbnVlc3RybyBlamVtcGxvLCB2aXN1YWxtZW50ZSBzZSBvYnNlcnZhOiAgICANCg0KKiBSYW5nbzogQXByb3hpbWFkYW1lbnRlIDg1MCBtLiAgDQoqIFBlcGl0YSA9IEFwcm94aW1hZGFtZW50ZSAwLjAxICANCiogTWVzZXRhID0gMC4xMyAgDQoNCisgYHZnbWA6IEdlbmVyYSB1biBtb2RlbG8gZGUgdmFyaW9ncmFtYSBvIGFncmVnYSBhIHVuIG1vZGVsbyBleGlzdGVudGUgaW5ncmVzYW5kbyBsb3MgcGFyw6FtZXRyb3M6IHJhbmdvLCBzaWxsYSBzaW4gJENfMCQsIHBlcGl0YSAkQ18wJCwgeSBlbCBtb2RlbG8gcXVlIHBhcmEgZXN0ZSBjYXNvLCBlcyBlc2bDqXJpY28gKFNwaCkuDQoNCmBgYHtyfQ0KdnQgPSB2Z20ocHNpbGw9MC4xMiwgcmFuZ2U9ODUwLCBudWdnZXQ9MC4wMSwNCiAgICAgICAgIG1vZGVsPSJTcGgiKQ0KdnQNCnBsb3QodmUsIHBsID0gVCwgbW9kZWwgPSB2dCkNCmBgYA0KDQojIyMgKkFqdXN0ZSAgYXV0b23DoXRpY28qDQoNCisgYGZpdC52YXJpb2dyYW06YCBBanVzdGEgcmFuZ29zIHkvbyB1bWJyYWxlcyBkZSB1biBtb2RlbG8gZGUgIHZhcmlvZ3JhbWEgc2ltcGxlIG8gYW5pZGFkbyBhIHVuIHZhcmlvZ3JhbWEgZGUgbXVlc3RyYS4gQ3V5b3MgcGFyw6FtZXRyb3Mgc29uIGVsIHZhcmlvZ3JhbWEgeSBlbCB2YXJpb2dyYW1hIGFqdXN0YWRvLg0KDQpgYGB7cn0NCnZhID0gZml0LnZhcmlvZ3JhbSh2ZSwgdnQpDQp2YQ0KcGxvdCh2ZSwgcGw9VCwgbW9kZWw9dmEpDQpgYGANCg0KIyMgKkludGVycG9sYWNpw7NuOiBLcmlnaW5nIG9yZGluYXJpbyoNCg0KTG8gcHJpbWVybyBlcyBjcmVhciB1bmEgY3VhZHLDrWN1bGEgZMOzbmRlIHNlIHByZWRlY2lyw6FuIGxhIGNvbmNlbnRyYWNpw7NuIGRlIFppbmMuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KZGF0YShtZXVzZS5ncmlkKSAjIEN1YWRyw61jdWxhDQpjb29yZGluYXRlcyhtZXVzZS5ncmlkKSA9IGMoIngiLCAieSIpDQpwbG90KG1ldXNlLmdyaWQpDQpgYGANCg0KYGBge3J9DQojIFByb211ZXZlIHVuYSBlc3RydWN0dXJhIG5vIGN1YWRyaWN1bGFkYSBhIHVuYSBjdWFkcsOtY3VsYQ0KZ3JpZGRlZChtZXVzZS5ncmlkKSA9IFQNCmBgYA0KDQorIGBrcmlnZTpgIEZ1bmNpw7NuIHBhcmEga3JpZ2luZyBzaW1wbGUsIG9yZGluYXJpbyBvIHVuaXZlcnNhbCAoYSB2ZWNlcyBsbGFtYWRvIGtyaWdpbmcgZGUgZGVyaXZhIGV4dGVybmEpLCBmdW5jacOzbiBwYXJhIGxhIGludGVycG9sYWNpw7NuIHBvbmRlcmFkYSBkZSBkaXN0YW5jaWEgaW52ZXJzYS4gKFBhcmEgbnVlc3RvIGNhc28ga3JpZ2luZyBvcmRpbmFyaW8pDQoNCmBgYHtyfQ0KT0sgPSBrcmlnZShsb2dabn4xLCBsb2NhdGlvbnM9bWV1c2UsIG5ld2RhdGE9bWV1c2UuZ3JpZCwgbW9kZWw9dmEpDQpuYW1lcyhPSykNCmBgYA0KDQpgYGB7cn0NCiMgUmV0b21hbmRvIGxvcyB2YWxvcmVzIG9yaWdpbmFsZXMgZGVsIFppbmMuDQpPSyRwcmVkID0gMTBeKE9LJHZhcjEucHJlZCkgDQpgYGANCg0KYGBge3J9DQojIEdyYWZpY2FuZG8NCnB0cy5zID0gbGlzdCgic3AucG9pbnRzIiwgbWV1c2UsIGNvbD0id2hpdGUiLCANCiAgICAgICAgICAgICBwY2g9MSwgY2V4ID0gNCptZXVzZSR6aW5jL21heChtZXVzZSR6aW5jKSkNCg0KcHJpbnQoc3BwbG90KE9LLCAicHJlZCIsIGFzcD0xLCANCiAgICAgICAgICAgICBjb2wucmVnaW9ucz1yZXYoYnB5LmNvbG9ycyg2NCkpLA0KICAgICAgICAgICAgIG1haW4gPSAiUHJlZGljY2lvbmVzIG1vZGVsbyBFc2bDqXJpY28iLA0KICAgICAgICAgICAgIHNwLmxheW91dD1saXN0KHB0cy5zKSkpDQpgYGANCg0KIyMgKipFamVyY2ljaW86KioNCiMjIyAqQWwgdmFyaW9ncmFtYSBleHBlcmltZW50YWwgY2FsY3VsYWRvIGB2ZWAsIHJlYWxpemFyIGVsIGFqdXN0ZSBlIGludGVycG9sYWNpw7NuIGNvbiBlbCBtb2RlbG8gdGXDs3JpY28gRXhwb25lbmNpYWwqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KIyBBanVzdGUgdmlzdWFsOg0KdnQxID0gdmdtKHBzaWxsPTAuMTIsIHJhbmdlPTg1MCwgbnVnZ2V0PTAuMDEsIG1vZGVsPSJFeHAiKQ0KcGxvdCh2ZSwgcGwgPSBULCBtb2RlbCA9IHZ0MSkNCnZ0MQ0KDQojIEFqdXN0ZSBhdXRvbWF0aWNvOg0KdmExID0gZml0LnZhcmlvZ3JhbSh2ZSwgdnQxKQ0KcGxvdCh2ZSwgcGw9VCwgbW9kZWw9dmExKQ0KdmExDQoNCiMgSW50ZXJwb2xhY2lvbjoNCk9LMSA9IGtyaWdlKGxvZ1pufjEsIGxvY2F0aW9ucz1tZXVzZSwgbmV3ZGF0YT1tZXVzZS5ncmlkLCBtb2RlbD12YTEpDQpPSzEkcHJlZCA9IDEwXihPSzEkdmFyMS5wcmVkKSANCnByaW50KHNwcGxvdChPSzEsICJwcmVkIiwgYXNwPTEsIA0KICAgICAgICAgICAgIGNvbC5yZWdpb25zPXJldihicHkuY29sb3JzKDY0KSksDQogICAgICAgICAgICAgbWFpbiA9ICJQcmVkaWNjaW9uZXMgbW9kZWxvIEV4cG9uZW5jaWFsIiwNCiAgICAgICAgICAgICBzcC5sYXlvdXQ9bGlzdChwdHMucykpKQ0KYGBgDQoNCg0KDQojIyAqKlZhbGlkYWNpw7NuIGNydXphZGEqKg0KDQpDb25zaXN0ZSBlbiBleGNsdWlyIGxhIG9ic2VydmFjacOzbiBkZSB1bm8gZGUgbG9zIG4gcHVudG9zIG11ZXN0cmFsZXMgeSBjb24gbG9zIG4tMSB2YWxvcmVzIHJlc3RhbnRlcyB5IGVsIG1vZGVsbyBkZSBzZW1pdmFyaW9ncmFtYSBlc2NvZ2lkbywgcHJlZGVjaXIgdsOtYSBrcmlnaW5nIGVsIHZhbG9yIGRlIGxhIHZhcmlhYmxlIGVuIGVzdHVkaW8gZW4gbGEgdWJpY2FjacOzbiBkZWwgcHVudG8gcXVlIHNlIGV4Y2x1ecOzLiAgDQoNCmBrcmlnZS5jdmAgRnVuY2lvbmVzIGRlIHZhbGlkYWNpw7NuIGNydXphZGEgcGFyYSAoY28pIGtyaWdpbmcsIGtyaWdpbmcgZGUgcHVudG9zIHNpbXBsZXMsIG9yZGluYXJpb3MgbyB1bml2ZXJzYWxlcyBlbiB1biB2ZWNpbmRhcmlvIGxvY2FsLiBSZXRvcm5hIHVuIERhdGFGcmFtZSBxdWUgY29udGllbmUgbGFzIGNvb3JkZW5hZGFzIGRlIGxvcyBkYXRvcyBvIGxhcyBkZSBsYSBwcmltZXJhIHZhcmlhYmxlIGVuIGVsIG9iamV0bywgeSBjb2x1bW5hcyBkZSBwcmVkaWNjacOzbiB5IHZhcmlhbnphIGRlIHByZWRpY2Npw7NuIGRlIHB1bnRvcyBkZSBkYXRvcyB2YWxpZGFkb3MgY3J1emFkb3MsIHZhbG9yZXMgb2JzZXJ2YWRvcywgcmVzaWR1YWxlcywgenNjb3JlIChyZXNpZHVvIGRpdmlkaWRvIHBvciBlbCBlcnJvciBlc3TDoW5kYXIgZGUga3JpZ2luZykuDQoNCg0KYGBge3IgY3Jvc3MtdmFsaWRhdGlvbiwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCiMgTW9kZWxvIGVzZmVyaWNvDQp2YWxpZGEgPSBrcmlnZS5jdihsb2coemluYyl+MSwgbWV1c2UsIHZhLCBubWF4PTQwLCBuZm9sZD01KSAjIEVzZmVyaWNvDQoNCiMgTW9kZWxvIGV4cG9uZW5jaWFsDQp2YWxpZGExID0ga3JpZ2UuY3YobG9nKHppbmMpfjEsIG1ldXNlLCB2YTEsIG5tYXg9NDAsIG5mb2xkPTUpDQpgYGANCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCmJ1YmJsZSh2YWxpZGEsICJyZXNpZHVhbCIsIA0KICAgICAgIG1haW4gPSAiTG9nKHppbmMpOiA1LWZvbGQgY3YgcmVzaWR1YWxlcyBcbk1vZGVsbyBlc2bDqXJpY28gICAgIikNCmBgYA0KDQoqKkVycm9yZXMgZGUgcHJlZGljY2nDs24qKg0KDQpTZSBwaWVuc2EgcXVlIHNpIGVsIG1vZGVsbyBkZSBzZW1pdmFyaWFuemEgZWxlZ2lkbyBkZXNjcmliZSBiaWVuIGxhIGVzdHJ1Y3R1cmEgZGUgYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbCwgZW50b25jZXMgbGEgZGlmZXJlbmNpYSBlbnRyZSBlbCB2YWxvciBvYnNlcnZhZG8geSBlbCB2YWxvciBwcmVkaWNobyBkZWJlIHNlciBwZXF1ZcOxYS4gRXN0ZSBwcm9jZWRpbWllbnRvIHNlIHJlYWxpemEgZW4gZm9ybWEgc2VjdWVuY2lhbCBjb24gY2FkYSB1bm8gZGUgbG9zIHB1bnRvcyBtdWVzdHJhbGVzIHkgYXPDrSBzZSBvYnRpZW5lIHVuIGNvbmp1bnRvIGRlIG4g4oCcZXJyb3JlcyBkZSBwcmVkaWNjacOzbuKAnS4gDQoNCkluaWNpYWxtZW50ZSBwb2RlbW9zIHVzYXIgbGFzIHNpZ3VpZW50ZXMgbWVkaWRhczogZXJyb3IgbWVkaW8gKE1FKSwgZXJyb3IgYWJzb2x1dG8gbWVkaW8gKE1BRSksIGVycm9yIGN1YWRyw6F0aWNvIG1lZGlvIChSTVNFKSB5IHJlbGFjacOzbiBkZSBkZXN2aWFjacOzbiBjdWFkcsOhdGljYSBtZWRpYSAoTVNEUikgZGUgbG9zIHJlc2lkdW9zIGNvbiB2YXJpYW56YSBrcmlnaW5nLg0KDQoNCioqRXJyb3JlcyBkZSBwcmVkaWNjacOzbiBwYXJhIGVsIG1vZGVsbyBlc2bDqXJpY28qKg0KYGBge3J9DQojIE1lYW4gRXJyb3IgKE1FKQ0KTUUgPSByb3VuZChtZWFuKHZhbGlkYSRyZXNpZHVhbCksMykNCk1FDQojIE1lYW4gQWJzb2x1dGUgRXJyb3INCk1BRSA9IHJvdW5kKG1lYW4oYWJzKHZhbGlkYSRyZXNpZHVhbCkpLDMpDQpNQUUNCiMgUm9vdCBNZWFuIFNxdXJlIEVycm9yIChSTVNFKQ0KUk1TRSA9IHJvdW5kKHNxcnQobWVhbih2YWxpZGEkcmVzaWR1YWxeMikpLDMpDQpSTVNFDQojIE1lYW4gU3F1YXJlZCBEZXZpYXRpb24gUmF0aW8gKE1TRFIpDQpNU0RSID0gbWVhbih2YWxpZGEkcmVzaWR1YWxeMi92YWxpZGEkdmFyMS52YXIpDQpNU0RSDQpgYGANCg0KKipFcnJvcmVzIGRlIHByZWRpY2Npw7NuIHBhcmEgZWwgbW9kZWxvIGV4cG9uZW5jaWFsKioNCmBgYHtyfQ0KIyBNZWFuIEVycm9yIChNRSkNCk1FMSA9IHJvdW5kKG1lYW4odmFsaWRhMSRyZXNpZHVhbCksMykNCk1FMQ0KIyBNZWFuIEFic29sdXRlIEVycm9yDQpNQUUxID0gcm91bmQobWVhbihhYnModmFsaWRhMSRyZXNpZHVhbCkpLDMpDQpNQUUxDQojIFJvb3QgTWVhbiBTcXVyZSBFcnJvciAoUk1TRSkNClJNU0UxID0gcm91bmQoc3FydChtZWFuKHZhbGlkYTEkcmVzaWR1YWxeMikpLDMpDQpSTVNFMQ0KIyBNZWFuIFNxdWFyZWQgRGV2aWF0aW9uIFJhdGlvIChNU0RSKQ0KTVNEUjEgPSBtZWFuKHZhbGlkYTEkcmVzaWR1YWxeMi92YWxpZGExJHZhcjEudmFyKQ0KTVNEUjENCmBgYA0KDQpTZSBwdWVkZSBkZWNpciBxdWUgYXVucXVlIGxvcyBlcnJvcmVzIHNvbiBtdXkgc2ltaWxhcmVzLCBlbCBtb2RlbG8gZXNmw6lyaWNvIHRpZW5lIHVuIG1lam9yIGNvbXBvcnRhbWllbnRvIHlhIHF1ZSBzdXMgZXJyb3JlcyBzb24gbcOhcyBwZXF1ZcOxb3MgcG9yIG1pbMOpc2ltYXMuDQoNCg0KYGxtLmN2YCBFc3RhIGZ1bmNpw7NuIHByb3BvcmNpb25hIG1lZGlkYXMgZGUgdmFsaWRhY2nDs24gaW50ZXJuYSB5IGNydXphZGEgZGUgcHJlY2lzacOzbiBwcmVkaWN0aXZhIHBhcmEgbGEgcmVncmVzacOzbiBsaW5lYWwgb3JkaW5hcmlhDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KbG0uY3YgPSBsbSh2YWxpZGEkdmFyMS5wcmVkIH4gdmFsaWRhJG9ic2VydmVkKQ0Kc3VtbWFyeShsbS5jdikNCnBsb3QodmFsaWRhJHZhcjEucHJlZCB+IHZhbGlkYSRvYnNlcnZlZCkNCnBhcihuZXc9VFJVRSkNCnBsb3QoMS45NjY0MCswLjY2Njg3KnZhbGlkYSRvYnNlcnZlZH52YWxpZGEkb2JzZXJ2ZWQsDQogICAgIHR5cGU9ImwiLCB5bGFiPSJ2YWxpZGEkdmFyMS5wcmVkIiwgYXhlcz1GQUxTRSkNCg0KYGBgDQoNCiMjICoqVGFyZWE6IERpc3RhbmNpYSBJbnZlcnNhIFBvbmRlcmFkYSAoSURXKSoqDQoNCkxhIGludGVycG9sYWNpw7NuIG1lZGlhbnRlIGRpc3RhbmNpYSBpbnZlcnNhIHBvbmRlcmFkYSBkZXRlcm1pbmEgbG9zIHZhbG9yZXMgZGUgY2VsZGEgYSB0cmF2w6lzIGRlIHVuYSBjb21iaW5hY2nDs24gcG9uZGVyYWRhIGxpbmVhbG1lbnRlIGRlIHVuIGNvbmp1bnRvIGRlIHB1bnRvcyBkZSBtdWVzdHJhLiBMYSBwb25kZXJhY2nDs24gZXMgdW5hIGZ1bmNpw7NuIGRlIGxhIGRpc3RhbmNpYSBpbnZlcnNhLiBMYSBzdXBlcmZpY2llIHF1ZSBzZSBpbnRlcnBvbGEgZGViZSBzZXIgbGEgZGUgdW5hIHZhcmlhYmxlIGRlcGVuZGllbnRlIGRlIGxhIHViaWNhY2nDs24uDQoNCiFbVmVjaW5kYWQgZGUgSURXIGRlbCBwdW50byBzZWxlY2Npb25hZG9dKGh0dHBzOi8vZGVza3RvcC5hcmNnaXMuY29tL2VzL2FyY21hcC8xMC4zL3Rvb2xzL3NwYXRpYWwtYW5hbHlzdC10b29sYm94L0dVSUQtN0VCNUI4QjEtOUM3Mi00MDhGLUFFNjItNDFDMkQ3NEJGQTUwLXdlYi5naWYpDQoNCkVzdGUgbcOpdG9kbyBwcmVzdXBvbmUgcXVlIGxhIHZhcmlhYmxlIHF1ZSBzZSByZXByZXNlbnRhIGNhcnRvZ3LDoWZpY2FtZW50ZSBkaXNtaW51eWUgc3UgaW5mbHVlbmNpYSBhIG1heW9yIGRpc3RhbmNpYSBkZXNkZSBzdSB1YmljYWNpw7NuIGRlIG11ZXN0cmEuIFBvciBlamVtcGxvLCBhbCBpbnRlcnBvbGFyIHVuYSBzdXBlcmZpY2llIGRlIHBvZGVyIGFkcXVpc2l0aXZvIGRlIGxvcyBjb25zdW1pZG9yZXMgcGFyYSBhbmFsaXphciBsYXMgdmVudGFzIG1pbm9yaXN0YXMgZGUgdW4gc2l0aW8sIGVsIHBvZGVyIGFkcXVpc2l0aXZvIGRlIHVuYSB1YmljYWNpw7NuIG3DoXMgZGlzdGFudGUgdGVuZHLDoSBtZW5vcyBpbmZsdWVuY2lhIHBvcnF1ZSBlcyBtw6FzIHByb2JhYmxlIHF1ZSBsYXMgcGVyc29uYXMgY29tcHJlbiBjZXJjYSBkZSBzdXMgY2FzYXMuDQoNCkxhIERpc3RhbmNpYSBJbnZlcnNhIFBvbmRlcmFkYSAoSURXKSBlcyBtYXRlbcOhdGljYSAoZGV0ZXJtaW5pc3RhKSBhc3VtaWVuZG8gcXVlIGxvcyB2YWxvcmVzIG3DoXMgY2VyY2Fub3MgZXN0w6FuIG3DoXMgcmVsYWNpb25hZG9zIHF1ZSBvdHJvcyBjb24gc3UgZnVuY2nDs24uDQoNCioqUmVmZXJlbmNpYXMqKiAgDQpbMV0gaHR0cHM6Ly9kZXNrdG9wLmFyY2dpcy5jb20vZXMvYXJjbWFwLzEwLjMvdG9vbHMvc3BhdGlhbC1hbmFseXN0LXRvb2xib3gvaG93LWlkdy13b3Jrcy5odG0gIA0KWzJdIGh0dHBzOi8vYWNvbGl0YS5jb20vaW50ZXJwb2xhY2lvbi1jb24tbGEtZGlzdGFuY2lhLWludmVyc2EtcG9uZGVyYWRhLWlkdy8=