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.

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:

Método para seleccionar el mejor ajuste del modelo teórico

  1. Conocer el proceso espacial
  2. Ajuste visual
  3. 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
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.

Vecindad de IDW del punto seleccionado

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=