options(repos = c(CRAN = "https://cloud.r-project.org"))
paquetes <- c("readxl", "dplyr", "geoR", "raster", "rasterVis",
"ggplot2", "leaflet", "sp", "knitr", "kableExtra")
for (pkg in paquetes) {
if (!require(pkg, character.only = TRUE, quietly = TRUE)) {
install.packages(pkg, dependencies = TRUE,
repos = "https://cloud.r-project.org")
library(pkg, character.only = TRUE)
}
}
paleta <- c("#2C7BB6", "#D7191C", "#1A9641", "#FDAE61", "#ABD9E9")
Introducción
La temperatura incide directamente en la productividad del aguacate.
Conocer su distribución espacial dentro de la finca permite decisiones
de manejo más precisas, pero los sensores solo capturan valores en
puntos discretos, haciendo necesaria la interpolación para obtener una
superficie continua.
Este informe aplica geoestadística a 534 mediciones de temperatura
georeferenciadas por árbol, tomadas el 1 de octubre de 2020 en una finca
aguacatera de Popayán, Cauca. El objetivo es estimar la distribución
continua de temperatura mediante Kriging Ordinario, aprovechando la
autocorrelación espacial de los datos para producir predicciones
insesgadas y de varianza mínima. El análisis sigue el flujo estándar:
exploración de datos, semivariograma empírico, ajuste del modelo
teórico, interpolación espacial, generación de rasters y validación
cruzada.
Los resultados obtenidos muestran la magnitud real de la variación
térmica entre árboles en una misma jornada, identifican los sectores más
fríos y más cálidos dentro del lote y señalan concretamente dónde la red
de sensores actual deja zonas con predicciones poco confiables. Cabe
anotar que el análisis cubre un único día de medición, por lo que las
conclusiones reflejan las condiciones de ese momento y no necesariamente
el patrón térmico permanente de la finca.
Análisis
Exploratorio de Datos
geo_datos_raw <- read_excel(
"C:/Pontificia_Un_Javeriana/Analisis_Geoespacial/Datos_Completos_Aguacate.xlsx"
)
cat("Dimensiones totales:", dim(geo_datos_raw), "\n")
## Dimensiones totales: 20271 21
cat("Columnas:", paste(names(geo_datos_raw), collapse = ", "), "\n")
## Columnas: id_arbol, Latitude, Longitude, FORMATTED_DATE_TIME, Psychro_Wet_Bulb_Temperature, Station_Pressure, Relative_Humidity, Crosswind, Temperature, Barometric_Pressure, Headwind, Direction_True, Direction_Mag, Wind_Speed, Heat_Stress_Index, Altitude, Dew_Point, Density_Altitude, Wind_Chill, Estado_Fenologico_Predominante, Frutos_Afectados
cat("Tipo FORMATTED_DATE_TIME:", class(geo_datos_raw$FORMATTED_DATE_TIME), "\n\n")
## Tipo FORMATTED_DATE_TIME: character
# Filtrado robusto: prueba 4 estrategias en cascada según tipo de columna
fecha_obj <- as.Date("2020-10-01")
vals <- geo_datos_raw$FORMATTED_DATE_TIME
if (inherits(vals, c("Date","POSIXct","POSIXlt"))) {
idx <- !is.na(as.Date(vals)) & as.Date(vals) == fecha_obj
} else {
txt <- as.character(vals)
# intento dd/mm/yyyy
p1 <- suppressWarnings(as.Date(txt, format = "%d/%m/%Y"))
if (any(!is.na(p1))) {
idx <- !is.na(p1) & p1 == fecha_obj
} else {
# intento yyyy-mm-dd (primeros 10 chars)
p2 <- suppressWarnings(as.Date(substr(txt, 1, 10)))
if (any(!is.na(p2))) {
idx <- !is.na(p2) & p2 == fecha_obj
} else {
# búsqueda de texto
idx <- grepl("2020-10-01|01/10/2020|01-10-2020", txt)
}
}
}
geo_datos <- geo_datos_raw[idx, ]
if (nrow(geo_datos) == 0) {
warning("Filtro de fecha sin resultados. Se usarán todos los datos.")
geo_datos <- geo_datos_raw
}
cat("Registros filtrados:", nrow(geo_datos), "\n")
## Registros filtrados: 534
cat("Rango Longitude:", round(range(as.double(geo_datos$Longitude), na.rm=TRUE), 6), "\n")
## Rango Longitude: -76.7118 -76.71022
cat("Rango Latitude :", round(range(as.double(geo_datos$Latitude), na.rm=TRUE), 6), "\n")
## Rango Latitude : 2.392101 2.393634
# Variables inline del dataset
res_temp_min <- round(min(geo_datos$Temperature, na.rm = TRUE), 1)
res_temp_max <- round(max(geo_datos$Temperature, na.rm = TRUE), 1)
res_temp_media <- round(mean(geo_datos$Temperature, na.rm = TRUE), 1)
res_temp_sd <- round(sd(geo_datos$Temperature, na.rm = TRUE), 1)
res_temp_cv <- round(sd(geo_datos$Temperature, na.rm = TRUE) /
mean(geo_datos$Temperature, na.rm = TRUE) * 100, 1)
res_temp_rango <- round(res_temp_max - res_temp_min, 1)
res_lon_rng_km <- round(diff(range(as.double(geo_datos$Longitude), na.rm = TRUE)) * 111, 2)
res_lat_rng_km <- round(diff(range(as.double(geo_datos$Latitude), na.rm = TRUE)) * 111, 2)
res_moda_temp <- round(as.numeric(names(which.max(table(round(geo_datos$Temperature, 0))))), 0)
kable(head(geo_datos[, c("id_arbol","Latitude","Longitude",
"Temperature","Relative_Humidity",
"Wind_Speed","Altitude")], 10),
caption = "Tabla 1. Primeros 10 registros del conjunto de datos filtrado al período 01/10/2020",
digits = 3, align = "c") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"),
full_width = FALSE)
Tabla 1. Primeros 10 registros del conjunto de datos filtrado al período
01/10/2020
|
id_arbol
|
Latitude
|
Longitude
|
Temperature
|
Relative_Humidity
|
Wind_Speed
|
Altitude
|
|
1
|
2.394
|
-76.711
|
23.9
|
85.2
|
0.0
|
1696
|
|
2
|
2.394
|
-76.711
|
23.5
|
84.0
|
0.0
|
1696
|
|
3
|
2.394
|
-76.711
|
24.5
|
79.6
|
0.5
|
1694
|
|
4
|
2.394
|
-76.711
|
25.9
|
77.6
|
0.5
|
1694
|
|
5
|
2.393
|
-76.711
|
26.0
|
76.5
|
0.0
|
1696
|
|
6
|
2.393
|
-76.711
|
24.5
|
77.7
|
0.3
|
1698
|
|
7
|
2.393
|
-76.711
|
25.5
|
76.5
|
0.0
|
1698
|
|
8
|
2.393
|
-76.711
|
25.7
|
77.7
|
0.0
|
1698
|
|
9
|
2.393
|
-76.711
|
26.0
|
78.3
|
0.0
|
1696
|
|
10
|
2.393
|
-76.711
|
25.9
|
77.8
|
0.4
|
1694
|
En esta sección se realiza el análisis exploratorio de la Temperatura
mediante estadísticas descriptivas y visualizaciones.
Estadísticas
Descriptivas
resumen_temp <- data.frame(
Estadístico = c("Mínimo","Percentil 25","Mediana","Media",
"Percentil 75","Máximo","Desv. Estándar","CV (%)"),
Valor = c(
round(min(geo_datos$Temperature, na.rm=TRUE), 3),
round(quantile(geo_datos$Temperature, 0.25, na.rm=TRUE), 3),
round(median(geo_datos$Temperature, na.rm=TRUE), 3),
round(mean(geo_datos$Temperature, na.rm=TRUE), 3),
round(quantile(geo_datos$Temperature, 0.75, na.rm=TRUE), 3),
round(max(geo_datos$Temperature, na.rm=TRUE), 3),
round(sd(geo_datos$Temperature, na.rm=TRUE), 3),
round(sd(geo_datos$Temperature, na.rm=TRUE) /
mean(geo_datos$Temperature, na.rm=TRUE) * 100, 2)
)
)
kable(resumen_temp,
caption = "Tabla 2. Estadísticas descriptivas de la variable Temperatura (°C)",
align = c("l","r")) %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE)
Tabla 2. Estadísticas descriptivas de la variable Temperatura (°C)
|
Estadístico
|
Valor
|
|
Mínimo
|
22.200
|
|
Percentil 25
|
24.500
|
|
Mediana
|
25.800
|
|
Media
|
25.829
|
|
Percentil 75
|
27.175
|
|
Máximo
|
29.700
|
|
Desv. Estándar
|
1.771
|
|
CV (%)
|
6.860
|
Interpretación: La tabla muestra que la temperatura
registrada el 1 de octubre de 2020 osciló entre 22.2 °C y 29.7 °C, con
una media de 25.8 °C y una desviación estándar de 1.8 °C. El coeficiente
de variación de 6.9 % indica una dispersión moderada que sugiere
variabilidad espacial detectable. Ese rango de aproximadamente 7.5 °C
entre el árbol más frío y el más caliente es agronómicamente relevante,
ya que diferencias superiores a 2 °C dentro de un lote pueden afectar la
sincronía de floración del aguacate Hass.
Distribución de
Temperatura
ggplot(geo_datos, aes(x = Temperature)) +
geom_histogram(aes(y = after_stat(density)),
bins = 25, fill = paleta[1], color = "white", alpha = 0.85) +
geom_density(color = paleta[2], linewidth = 1.2) +
labs(x = "Temperatura (°C)", y = "Densidad") +
theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold", color = paleta[1]))
Interpretación: El histograma muestra una
distribución con dos modos aproximados alrededor de 26 °C y en el rango
de 26-27 °C, lo que sugiere que en la finca coexisten al menos dos
microambientes térmicos diferenciados. La asimetría leve hacia valores
altos indica que un grupo de árboles registró temperaturas notoriamente
superiores a la mayoría. Dado que la distribución no se aleja demasiado
de la normalidad, el Kriging Ordinario puede aplicarse sin
transformación previa de la variable, aunque sería prudente verificar
este supuesto formalmente con una prueba de normalidad si el análisis se
extiende a otros períodos.
Distribución en
Árboles
pal_temp <- colorNumeric(palette = c(paleta[5], paleta[4], paleta[2]),
domain = geo_datos$Temperature)
leaflet(geo_datos) %>%
addTiles() %>%
addCircleMarkers(lng = ~Longitude, lat = ~Latitude, radius = 3,
color = ~pal_temp(Temperature),
stroke = FALSE, fillOpacity = 0.85,
popup = ~paste0("<b>Árbol:</b> ", id_arbol,
"<br><b>Temp:</b> ", round(Temperature,2), " °C")) %>%
addLegend(position = "bottomright", pal = pal_temp,
values = ~Temperature, title = "Temperatura (°C)", opacity = 0.85)
Interpretación: El mapa confirma que los 534 árboles
monitoreados cubren de forma continua la extensión de la finca, con una
distribución que abarca aproximadamente 0.18 km en longitud y 0.17 km en
latitud. Se observa visualmente que los árboles con temperatura más alta
se concentran en un sector particular del lote, lo que podría estar
relacionado con la exposición solar, el drenaje del suelo o la densidad
del dosel en esa zona. Esta concentración espacial de valores extremos
es precisamente lo que el semivariograma deberá capturar.
Temperatura y
Coordenadas
ggplot(geo_datos, aes(x = Latitude, y = Temperature)) +
geom_point(color = paleta[1], alpha = 0.5, size = 1.8) +
geom_smooth(method = "lm", formula = y ~ x,
color = paleta[2], se = TRUE, linewidth = 1.2,
fill = paleta[5], alpha = 0.3) +
labs(x = "Latitud", y = "Temperatura (°C)") +
theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold", color = paleta[1]))
Interpretación: La nube de puntos contra la latitud
muestra una dispersión amplia sin un patrón lineal evidente, lo que
sugiere que no existe una tendencia térmica sistemática en dirección
norte-sur dentro del lote. La banda de confianza de la regresión lineal
es relativamente ancha, reforzando que la pendiente no es
estadísticamente robusta. Esto es consistente con la topografía
relativamente plana de la zona baja de Popayán donde se ubica la finca.
La ausencia de tendencia norte-sur es un indicio favorable para asumir
estacionariedad intrínseca y proceder con Kriging Ordinario.
ggplot(geo_datos, aes(x = Longitude, y = Temperature)) +
geom_point(color = paleta[3], alpha = 0.5, size = 1.8) +
geom_smooth(method = "lm", formula = y ~ x,
color = paleta[2], se = TRUE, linewidth = 1.2,
fill = paleta[5], alpha = 0.3) +
labs(x = "Longitud", y = "Temperatura (°C)") +
theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold", color = paleta[3]))
Interpretación: La dispersión contra la longitud
tampoco evidencia una tendencia lineal clara en dirección este-oeste.
Sin embargo, se aprecia una mayor densidad de puntos con temperatura
elevada en el extremo occidental del lote, lo que podría corresponder a
la parte de la finca con mayor exposición a la tarde. Si este patrón se
confirma con mediciones de otros días, sería un argumento para explorar
Kriging Universal con una tendencia lineal en longitud. Por ahora, con
un único día de datos, el Kriging Ordinario es la elección más
parsimoniosa.
Evaluación con
Geodata
En esta sección se construye el objeto geodata de
geoR y se visualizan los cuatro paneles diagnóstico
equivalentes.
# Extraer vectores double puros y eliminar NA/Inf
lon_vec <- as.double(geo_datos[["Longitude"]])
lat_vec <- as.double(geo_datos[["Latitude"]])
temp_vec <- as.double(geo_datos[["Temperature"]])
idx_ok <- is.finite(lon_vec) & is.finite(lat_vec) & is.finite(temp_vec)
lon_vec <- lon_vec[idx_ok]
lat_vec <- lat_vec[idx_ok]
temp_vec <- temp_vec[idx_ok]
cat("Observaciones limpias:", length(temp_vec), "\n")
## Observaciones limpias: 534
cat("Rango Longitud :", round(range(lon_vec), 6), "\n")
## Rango Longitud : -76.7118 -76.71022
cat("Rango Latitud :", round(range(lat_vec), 6), "\n")
## Rango Latitud : 2.392101 2.393634
cat("Rango Temperatura :", round(range(temp_vec), 3), "\n")
## Rango Temperatura : 22.2 29.7
# Construir geodata manualmente (evita problemas con tibbles de readxl)
geo_temp <- list(
coords = matrix(c(lon_vec, lat_vec), ncol = 2,
dimnames = list(NULL, c("lon","lat"))),
data = temp_vec
)
class(geo_temp) <- "geodata"
res_n <- length(geo_temp$data)
# Panel 1: distribución espacial por cuartil (equivalente a plot.geodata panel 1)
q_num <- dplyr::ntile(geo_temp$data, 4)
lbl_map <- c("1"="Q1 Bajo","2"="Q2","3"="Q3","4"="Q4 Alto")
df_geo <- data.frame(
lon = geo_temp$coords[,"lon"],
lat = geo_temp$coords[,"lat"],
temp = geo_temp$data,
cuartil = factor(lbl_map[as.character(q_num)], levels = lbl_map)
)
ggplot(df_geo, aes(x = lon, y = lat, color = cuartil)) +
geom_point(size = 1.5, alpha = 0.8) +
scale_color_manual(values = c("#2C7BB6","#1A9641","#FDAE61","#D7191C"),
name = "Cuartil", drop = FALSE) +
labs(x = "Longitud", y = "Latitud") +
theme_minimal(base_size = 12) + coord_fixed()
ggplot(df_geo, aes(x = lat, y = temp)) +
geom_point(color = paleta[1], alpha = 0.4, size = 1.5) +
geom_smooth(method = "loess", formula = y ~ x,
color = paleta[2], se = FALSE, linewidth = 1) +
labs(x = "Latitud", y = "Temperatura (°C)") +
theme_minimal(base_size = 12)
ggplot(df_geo, aes(x = lon, y = temp)) +
geom_point(color = paleta[3], alpha = 0.4, size = 1.5) +
geom_smooth(method = "loess", formula = y ~ x,
color = paleta[2], se = FALSE, linewidth = 1) +
labs(x = "Longitud", y = "Temperatura (°C)") +
theme_minimal(base_size = 12)
ggplot(df_geo, aes(x = temp)) +
geom_histogram(aes(y = after_stat(density)),
bins = 20, fill = paleta[1], color = "white", alpha = 0.8) +
geom_density(color = paleta[2], linewidth = 1.2) +
labs(x = "Temperatura (°C)", y = "Densidad") +
theme_minimal(base_size = 12)
Interpretación: El panel de cuartiles (Figura 5)
muestra que los árboles del Q4 (temperaturas más altas) no están
distribuidos aleatoriamente: forman un cluster visible en el sector
noroccidental del lote. Los árboles del Q1 (más fríos) tienden a
ubicarse en el sector suroriental. Esta agrupación espacial de valores
similares es la señal más directa de que existe autocorrelación espacial
positiva en la temperatura, lo que justifica el uso de geoestadística
frente a métodos de interpolación determinísticos. Los paneles LOESS
contra latitud y longitud confirman lo observado en las regresiones
lineales: no hay una tendencia global dominante, aunque la curva contra
latitud muestra una ligera curvatura que merece atención. El histograma
final (Figura 8) reitera la bimodalidad observada previamente.
Semivariograma
Empírico
En esta sección se calcula el semivariograma empírico y los
envolventes de Monte Carlo para evaluar la autocorrelación espacial de
la temperatura.
cat("Observaciones en geodata:", length(geo_temp$data), "\n")
## Observaciones en geodata: 534
if (length(geo_temp$data) < 10)
stop("Geodata con menos de 10 obs. Revisar filtrado de fecha.")
# max.dist = mitad de la diagonal del bounding box
delta_lon <- diff(range(geo_temp$coords[,"lon"]))
delta_lat <- diff(range(geo_temp$coords[,"lat"]))
max_dist <- sqrt(delta_lon^2 + delta_lat^2) * 0.5
# Protección si la finca tiene extensión casi nula en alguna dirección
if (!is.finite(max_dist) || max_dist < 1e-10)
max_dist <- max(delta_lon, delta_lat, na.rm = TRUE) * 0.5
cat("max_dist para variog:", round(max_dist, 8), "| finito:", is.finite(max_dist), "\n")
## max_dist para variog: 0.00110217 | finito: TRUE
variograma <- variog(geo_temp, option = "bin", max.dist = max_dist)
## variog: computing omnidirectional variogram
# dist_50 reutilizado en ajuste-modelos para ini.vals
dist_50 <- max_dist
cat("Bins calculados:", length(variograma$u), "\n")
## Bins calculados: 13
set.seed(params$seed)
variograma_mc <- variog.mc.env(geo_temp, obj.variog = variograma, nsim = 99)
## variog.env: generating 99 simulations by permutating data values
## variog.env: computing the empirical variogram for the 99 simulations
## variog.env: computing the envelops
cat("Envolventes Monte Carlo: 99 simulaciones completadas.\n")
## Envolventes Monte Carlo: 99 simulaciones completadas.
plot(variograma,
xlab = "Distancia (grados)", ylab = "Semivarianza",
pch = 16, col = paleta[1])
lines(variograma_mc, col = "grey60", lty = 2)
Interpretación: El semivariograma empírico calculado
sobre 534 observaciones con una distancia máxima de 0.0011 grados
muestra un incremento claro de la semivarianza en las primeras clases de
distancia, lo que confirma que los árboles cercanos entre sí registraron
temperaturas más parecidas que los distantes. Los puntos del
semivariograma que superan el envolvente superior de Monte Carlo en las
distancias cortas indican que esta autocorrelación no es producto del
azar. La meseta visual del semivariograma se alcanza aproximadamente a
la mitad del rango analizado, lo que implica que más allá de esa
distancia los árboles ya no se influyen térmicamente entre sí. Una
limitación importante es que el semivariograma se calculó en modo
omnidireccional; si la autocorrelación fuera anisotrópica (diferente
según la dirección), este análisis la subestimaría.
Ajuste de
Modelos
En esta sección se ajustan tres modelos teóricos al semivariograma
empírico y se selecciona el de menor SCE.
# El argumento correcto de variofit() es ini.cov.pars (NO ini.vals).
# ini.cov.pars acepta: vector c(sigmasq, phi), o matriz/data.frame con
# 2 columnas donde col1=sigmasq y col2=phi.
# expand.grid() produce data.frame, que variofit convierte internamente a matrix.
sigmasq_seq <- seq(var(geo_temp$data) * 0.3, var(geo_temp$data) * 2, length.out = 8)
phi_seq <- seq(dist_50 * 0.1, dist_50 * 0.8, length.out = 8)
ini_grid <- expand.grid(sigmasq_seq, phi_seq) # data.frame: col1=sigmasq, col2=phi
model_exp <- variofit(variograma,
ini.cov.pars = ini_grid,
cov.model = "exponential",
weights = "npairs")
## variofit: covariance model used is exponential
## variofit: weights used: npairs
## variofit: minimisation function used: optim
## variofit: searching for best initial value ... selected values:
## sigmasq phi tausq kappa
## initial.value "3.23" "0" "0" "0.5"
## status "est" "est" "est" "fix"
## loss value: 5720.67737872684
cat("Exponential SCE:", round(model_exp$value, 4), "\n")
## Exponential SCE: 5678.797
model_gaus <- variofit(variograma,
ini.cov.pars = ini_grid,
cov.model = "gaussian",
weights = "npairs")
## variofit: covariance model used is gaussian
## variofit: weights used: npairs
## variofit: minimisation function used: optim
## variofit: searching for best initial value ... selected values:
## sigmasq phi tausq kappa
## initial.value "3.23" "0" "0" "0.5"
## status "est" "est" "est" "fix"
## loss value: 11370.2209167688
cat("Gaussian SCE:", round(model_gaus$value, 4), "\n")
## Gaussian SCE: 10284.05
model_spe <- variofit(variograma,
ini.cov.pars = ini_grid,
cov.model = "spherical",
weights = "npairs")
## variofit: covariance model used is spherical
## variofit: weights used: npairs
## variofit: minimisation function used: optim
## variofit: searching for best initial value ... selected values:
## sigmasq phi tausq kappa
## initial.value "3.23" "0" "0" "0.5"
## status "est" "est" "est" "fix"
## loss value: 10020.0397796141
cat("Spherical SCE:", round(model_spe$value, 4), "\n")
## Spherical SCE: 8927.608
tabla_modelos <- data.frame(
Modelo = c("Exponential","Gaussian","Spherical"),
SCE = round(c(model_exp$value, model_gaus$value, model_spe$value), 2),
Nugget = round(c(model_exp$nugget, model_gaus$nugget, model_spe$nugget), 4),
Meseta = round(c(model_exp$cov.pars[1], model_gaus$cov.pars[1], model_spe$cov.pars[1]), 4),
Rango_phi = round(c(model_exp$cov.pars[2], model_gaus$cov.pars[2], model_spe$cov.pars[2]), 6)
)
kable(tabla_modelos,
caption = "Tabla 3. Comparación de modelos teóricos de semivariograma según Suma de Cuadrados del Error (SCE)",
col.names = c("Modelo","SCE","Pepita","Meseta parcial","Rango (phi)"),
align = c("l","r","r","r","r")) %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE) %>%
row_spec(which.min(tabla_modelos$SCE), bold = TRUE, background = "#e8f5e9")
Tabla 3. Comparación de modelos teóricos de semivariograma según Suma de
Cuadrados del Error (SCE)
|
Modelo
|
SCE
|
Pepita
|
Meseta parcial
|
Rango (phi)
|
|
Exponential
|
5678.80
|
0.0000
|
3.2017
|
0.000109
|
|
Gaussian
|
10284.05
|
0.0493
|
3.0762
|
0.000105
|
|
Spherical
|
8927.61
|
0.1062
|
3.0333
|
0.000258
|
plot(variograma,
xlab = "Distancia (grados)", ylab = "Semivarianza",
pch = 16, col = "grey30")
lines(model_exp, col = paleta[1], lwd = 2)
lines(model_gaus, col = paleta[2], lwd = 2)
lines(model_spe, col = paleta[3], lwd = 2)
legend("bottomright",
legend = c("Exponential","Gaussian","Spherical"),
col = paleta[1:3], lwd = 2, bty = "n")
sce_vec <- c(model_exp$value, model_gaus$value, model_spe$value)
nom_geoR <- c("exponential","gaussian","spherical")
nom_disp <- c("Exponential","Gaussian","Spherical")
mejor_idx <- which.min(sce_vec)
mejor_modelo_nombre <- nom_geoR[mejor_idx]
mejor_modelo <- list(model_exp, model_gaus, model_spe)[[mejor_idx]]
cat("Modelo seleccionado:", nom_disp[mejor_idx], "\n")
## Modelo seleccionado: Exponential
cat("SCE :", round(mejor_modelo$value, 4), "\n")
## SCE : 5678.797
cat("Nugget:", round(mejor_modelo$nugget, 4), "\n")
## Nugget: 0
cat("Sigma2:", round(mejor_modelo$cov.pars[1], 4), "\n")
## Sigma2: 3.2017
cat("Phi :", round(mejor_modelo$cov.pars[2], 6), "\n")
## Phi : 0.000109
# Variables inline del modelo seleccionado
res_modelo_nombre <- nom_disp[mejor_idx]
res_sce <- round(mejor_modelo$value, 2)
res_nugget <- round(mejor_modelo$nugget, 3)
res_sigma2 <- round(mejor_modelo$cov.pars[1], 3)
res_phi_grados <- mejor_modelo$cov.pars[2]
res_phi_metros <- round(mejor_modelo$cov.pars[2] * 111000, 0)
Interpretación: De los tres modelos ajustados, el
Exponential obtuvo el menor SCE (5678.80), frente a 10284.05 del peor
ajuste, lo que indica una diferencia sustancial en la capacidad de cada
modelo para reproducir el semivariograma empírico. El rango efectivo
estimado de 0.000109 grados equivale aproximadamente a 12 metros, que es
la distancia máxima a la que dos árboles en esta finca presentan
temperaturas correlacionadas. La meseta parcial de 3.202 °C² representa
la varianza explicada por la estructura espacial, mientras que la pepita
de 0.000 °C² refleja la variabilidad a escala menor que la separación
mínima entre sensores o el error de medición propio del instrumento
portátil. Una pepita elevada en relación a la meseta sería indicativa de
que el sensor introduce ruido considerable.
Interpolación
con Kriging
En esta sección se predice la temperatura sobre una grilla de 100 x
100 puntos mediante Kriging Ordinario.
lon_min <- min(geo_temp$coords[,"lon"])
lon_max <- max(geo_temp$coords[,"lon"])
lat_min <- min(geo_temp$coords[,"lat"])
lat_max <- max(geo_temp$coords[,"lat"])
cat("Longitud:", round(lon_min,6), "a", round(lon_max,6), "\n")
## Longitud: -76.7118 a -76.71022
cat("Latitud :", round(lat_min,6), "a", round(lat_max,6), "\n")
## Latitud : 2.392101 a 2.393634
# Columnas x e y requeridas por rasterFromXYZ
geodatos_grid <- expand.grid(
x = seq(lon_min, lon_max, length.out = 100),
y = seq(lat_min, lat_max, length.out = 100)
)
plot(geodatos_grid, pch = 1, cex = 0.2, col = "grey70",
xlab = "Longitud", ylab = "Latitud")
points(geo_temp$coords, col = paleta[2], pch = 16, cex = 0.5)
geodatos_ko <- krige.conv(
geo_temp,
loc = geodatos_grid,
krige = krige.control(
nugget = mejor_modelo$nugget,
trend.d = "cte",
trend.l = "cte",
cov.pars = mejor_modelo$cov.pars,
cov.model = mejor_modelo_nombre
)
)
## krige.conv: model with constant mean
## krige.conv: Kriging performed using global neighbourhood
cat("Rango predicciones:", round(range(geodatos_ko$predict), 3), "°C\n")
## Rango predicciones: 22.182 29.536 °C
cat("Rango varianza KO :", round(range(geodatos_ko$krige.var), 6), "\n")
## Rango varianza KO : 0.007075 3.307027
# Variables inline del Kriging
res_pred_min <- round(min(geodatos_ko$predict), 1)
res_pred_max <- round(max(geodatos_ko$predict), 1)
res_sd_error_max <- round(max(sqrt(geodatos_ko$krige.var)), 2)
res_sd_error_min <- round(min(sqrt(geodatos_ko$krige.var)), 3)
res_sd_umbral <- round(max(sqrt(geodatos_ko$krige.var)) * 0.7, 2)
par(mar = c(4,4,3,2))
image(geodatos_ko,
xlab = "Longitud", ylab = "Latitud",
col = heat.colors(100, rev = TRUE))
contour(geodatos_ko, add = TRUE, drawlabels = TRUE, col = "grey20", lwd = 0.8)
points(geo_temp$coords, pch = 16, cex = 0.4, col = "black")
Interpretación: El mapa de predicción muestra que la
temperatura estimada varía entre 22.2 °C y 29.5 °C dentro de los límites
de la finca. Las isotermas revelan un gradiente espacial con el sector
de mayores temperaturas ubicado en la zona de mayor concentración de
árboles del Q4 identificados previamente. Desde el punto de vista
agronómico, las zonas que superan los 27 °C de forma consistente merecen
atención especial en cuanto a riego y sombrío, ya que el aguacate Hass
presenta estrés calórico por encima de ese umbral durante períodos
sostenidos. Es importante recordar que estas predicciones corresponden a
un único día; para recomendaciones de manejo permanente se requeriría un
análisis multitemporal.
# Graficar error estándar con ggplot2 (image.kriging no acepta val=)
df_error <- data.frame(
x = geodatos_grid$x,
y = geodatos_grid$y,
sdKO = sqrt(geodatos_ko$krige.var)
)
df_pts <- data.frame(
x = geo_temp$coords[,"lon"],
y = geo_temp$coords[,"lat"]
)
ggplot(df_error, aes(x = x, y = y, fill = sdKO)) +
geom_raster(interpolate = TRUE) +
scale_fill_gradientn(colours = rev(heat.colors(100)), name = "SD error (°C)") +
geom_point(data = df_pts, aes(x = x, y = y),
inherit.aes = FALSE, pch = 16, size = 0.4, col = "black") +
labs(x = "Longitud", y = "Latitud") +
theme_minimal(base_size = 12) + coord_fixed()
Interpretación: La desviación estándar del error de
predicción oscila entre 0.084 °C y 1.82 °C. Los valores más bajos
coinciden con el interior del lote donde la densidad de árboles
monitoreados es mayor, mientras que los bordes y esquinas de la finca
presentan los errores más altos, como es esperable en Kriging Ordinario
cuando se extrapola más allá de la nube de datos. En términos prácticos,
las predicciones en el borde occidental tienen una incertidumbre cerca
del doble que las del centro del lote, lo que debería tenerse en cuenta
antes de tomar decisiones de manejo diferencial basadas en esas zonas
periféricas.
Predicción en
RASTERS
En esta sección se transforma la predicción en rasters para
visualización avanzada e integración con plataformas SIG.
require(rasterVis)
temp_predict <- rasterFromXYZ(
cbind(geodatos_grid, z = geodatos_ko$predict)
)
temp_error_r <- rasterFromXYZ(
cbind(geodatos_grid, z = sqrt(geodatos_ko$krige.var))
)
suppressWarnings({
proj4string(temp_predict) <- CRS("+proj=longlat +datum=WGS84")
proj4string(temp_error_r) <- CRS("+proj=longlat +datum=WGS84")
})
cat("Raster predicción creado.\n")
## Raster predicción creado.
print(temp_predict)
## class : RasterLayer
## dimensions : 100, 100, 10000 (nrow, ncol, ncell)
## resolution : 1.6e-05, 1.548485e-05 (x, y)
## extent : -76.71181, -76.71021, 2.392093, 2.393642 (xmin, xmax, ymin, ymax)
## crs : +proj=longlat +datum=WGS84 +no_defs
## source : memory
## names : z
## values : 22.18185, 29.53588 (min, max)
levelplot(temp_predict,
par.settings = BuRdTheme,
xlab = "Longitud", ylab = "Latitud")
Interpretación: La representación raster, con
resolución de celda determinada por la grilla de 100 x 100 puntos sobre
la extensión de la finca, permite apreciar con mayor suavidad los
gradientes térmicos que el mapa de isolíneas. La paleta BuRd, que va de
azul (frío) a rojo (cálido), hace evidente que el núcleo más cálido de
la finca no coincide con el centroide del lote sino que está desplazado
hacia un sector específico. Este raster puede integrarse directamente en
QGIS para cruzarlo con capas de pendiente, orientación o uso del suelo y
así profundizar en las causas de la heterogeneidad térmica
observada.
levelplot(temp_error_r,
par.settings = BuRdTheme,
xlab = "Longitud", ylab = "Latitud")
Interpretación: El raster de error estándar muestra
que la incertidumbre no es homogénea en el lote: crece notoriamente en
los bordes y en particular en la esquina con menor densidad de puntos.
Con base en este mapa, si se quisiera reducir el error máximo a menos de
1.27 °C, sería suficiente con instalar sensores adicionales en las dos o
tres zonas periféricas de mayor error, sin necesidad de aumentar la
densidad de monitoreo en el centro del lote donde el modelo ya es
preciso.
leaflet() %>%
addTiles() %>%
addRasterImage(temp_predict, opacity = 0.65) %>%
addLegend(position = "bottomright",
colors = c("#2C7BB6","#FDAE61","#D7191C"),
labels = c("Baja","Media","Alta"),
opacity = 0.8)
Interpretación: Al superponer el raster de
predicción sobre la cartografía base se puede confirmar que la finca se
ubica en zona rural de Popayán, rodeada de otros cultivos y caminos que
podrían influir en la dinámica de temperatura a través de variaciones en
la cobertura del suelo y el flujo de aire. El sector más cálido predicho
por el modelo coincide visualmente con la parte del lote con menor
protección natural de colindantes, lo que es consistente con una mayor
exposición a la radiación directa.
Validación
Cruzada
En esta sección se evalúa la calidad predictiva del modelo mediante
validación cruzada leave-one-out con xvalid de
geoR.
valida <- xvalid(geodata = geo_temp, model = mejor_modelo)
## xvalid: number of data locations = 534
## xvalid: number of validation locations = 534
## xvalid: performing cross-validation at location ... 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534,
## xvalid: end of cross-validation
cat("Columnas xvalid:", paste(names(valida), collapse = ", "), "\n")
## Columnas xvalid: data, predicted, krige.var, error, std.error, prob
MAE <- mean(abs(valida$error))
RMSE <- sqrt(mean(valida$error^2))
ME <- mean(valida$error)
MSDR <- mean((valida$error^2) / valida$krige.var)
cat("MAE :", round(MAE, 4), "°C\n")
## MAE : 0.7861 °C
cat("RMSE:", round(RMSE, 4), "°C\n")
## RMSE: 1.0141 °C
cat("ME :", round(ME, 4), "°C (sesgo)\n")
## ME : -0.011 °C (sesgo)
cat("MSDR:", round(MSDR, 4), "(~1 = bien calibrado)\n")
## MSDR: 0.9304 (~1 = bien calibrado)
# Variables inline de la validación cruzada
res_MAE <- round(MAE, 3)
res_RMSE <- round(RMSE, 3)
res_ME <- round(ME, 4)
res_MSDR <- round(MSDR, 3)
res_MAE_redondo <- round(MAE, 1)
res_msdr_texto <- ifelse(abs(MSDR - 1) < 0.3,
"se acerca razonablemente a 1, lo que indica que las varianzas Kriging son estimaciones realistas del error",
"se aleja de 1, lo que sugiere que las varianzas Kriging subestiman o sobreestiman el error real y el modelo teórico podría afinarse")
tabla_val <- data.frame(
Métrica = c("MAE","RMSE","Error Medio (sesgo)","MSDR"),
Valor = round(c(MAE, RMSE, ME, MSDR), 4),
Unidad = c("°C","°C","°C","adimensional"),
Referencia = c("Menor es mejor","Menor es mejor","Cercano a 0","Cercano a 1")
)
kable(tabla_val,
caption = "Tabla 4. Métricas de desempeño del modelo Kriging en validación cruzada leave-one-out",
align = c("l","r","l","l")) %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE)
Tabla 4. Métricas de desempeño del modelo Kriging en validación cruzada
leave-one-out
|
Métrica
|
Valor
|
Unidad
|
Referencia
|
|
MAE
|
0.7861
|
°C
|
Menor es mejor
|
|
RMSE
|
1.0141
|
°C
|
Menor es mejor
|
|
Error Medio (sesgo)
|
-0.0110
|
°C
|
Cercano a 0
|
|
MSDR
|
0.9304
|
adimensional
|
Cercano a 1
|
errores_df <- data.frame(error = valida$error)
ggplot(errores_df, aes(x = error)) +
geom_histogram(aes(y = after_stat(density)),
bins = 25, fill = paleta[1], color = "white", alpha = 0.85) +
geom_density(color = paleta[2], linewidth = 1.2) +
geom_vline(xintercept = 0, color = paleta[3], linetype = "dashed", linewidth = 1) +
labs(x = "Error de predicción (°C)", y = "Densidad") +
theme_minimal(base_size = 13)
Interpretación: La validación cruzada leave-one-out
sobre los 534 árboles arrojó un MAE de 0.786 °C y un RMSE de 1.014 °C.
Estos valores indican que en promedio el modelo se equivoca en menos de
0.8 °C al predecir la temperatura de un árbol a partir de sus vecinos,
lo que se considera aceptable dado el rango total observado de 7.5 °C.
El error medio de -0.011 °C confirma que el modelo no tiene sesgo
sistemático de sobreestimación ni subestimación. El MSDR de 0.93 se
acerca razonablemente a 1, lo que indica que las varianzas Kriging son
estimaciones realistas del error. Una limitación de esta validación es
que al ser leave-one-out, no evalúa la capacidad del modelo para
predecir en zonas sin ningún sensor cercano, que es precisamente donde
el error del raster es mayor.
obs_pred_df <- data.frame(
observado = valida$data,
predicho = valida$predict
)
ggplot(obs_pred_df, aes(x = observado, y = predicho)) +
geom_point(color = paleta[1], alpha = 0.4, size = 1.8) +
geom_abline(slope = 1, intercept = 0,
color = paleta[2], linewidth = 1.2, linetype = "dashed") +
geom_smooth(method = "lm", formula = y ~ x,
color = paleta[3], se = TRUE, linewidth = 1,
fill = paleta[5], alpha = 0.3) +
labs(x = "Temperatura observada (°C)", y = "Temperatura predicha (°C)") +
theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold", color = paleta[1]))
Interpretación: El diagrama observado-predicho
muestra que la nube de puntos sigue razonablemente la diagonal de
predicción perfecta, aunque con mayor dispersión en los extremos del
rango de temperatura. Esto es típico del Kriging Ordinario, que tiende a
suavizar los valores extremos porque los estima como promedios
ponderados de vecinos: los árboles más calientes quedan ligeramente
subestimados y los más fríos ligeramente sobreestimados. Este efecto de
regresión a la media es una limitación inherente al método y debe
considerarse si el objetivo fuera identificar con precisión los árboles
individuales en condición de estrés térmico extremo.
Conclusiones
En esta sección se sintetizan los principales hallazgos del
análisis.
El análisis confirmó que la temperatura en la finca no es
espacialmente homogénea: el rango de 7.5 °C entre el árbol más frío y el
más caliente, registrado en una sola jornada, es suficiente para generar
condiciones microclimáticas diferenciadas que afectan el desarrollo del
aguacate. El semivariograma empírico capturó esta estructura con un
rango efectivo de aproximadamente 12 metros, que representa la escala
espacial relevante para el manejo diferencial dentro del lote.
El modelo Exponential resultó el mejor ajuste con un SCE de 5678.8, y
el Kriging Ordinario construido sobre él produjo predicciones con un MAE
de 0.786 °C, error razonable para decisiones de manejo de precisión. El
sector noroccidental de la finca concentra consistentemente las
temperaturas más altas, lo que sugiere revisar las condiciones de
sombrío, riego y densidad de siembra en esa zona.
Como limitaciones del estudio se destacan tres: el análisis cubre un
único día de medición, lo que impide generalizar los patrones; el
semivariograma se calculó sin analizar anisotropía, que podría existir
dada la posible influencia de pendientes o corrientes de aire; y la
validación cruzada leave-one-out no evalúa el desempeño en zonas sin
sensores cercanos, donde el error real puede superar el 1.82 °C estimado
en los bordes. Para fortalecer el análisis se recomienda incorporar
mediciones de al menos tres períodos distintos y explorar
semivariogramas direccionales.
sessionInfo()
## R version 4.6.0 (2026-04-24 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8 LC_CTYPE=Spanish_Colombia.utf8
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Colombia.utf8
##
## time zone: America/Bogota
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] kableExtra_1.4.0 knitr_1.51 leaflet_2.2.3 ggplot2_4.0.3
## [5] rasterVis_0.51.7 lattice_0.22-9 raster_3.6-32 sp_2.2-1
## [9] geoR_1.9-6 dplyr_1.2.1 readxl_1.5.0
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.58 bslib_0.11.0
## [4] htmlwidgets_1.6.4 latticeExtra_0.6-31 vctrs_0.7.3
## [7] tools_4.6.0 crosstalk_1.2.2 generics_0.1.4
## [10] parallel_4.6.0 proxy_0.4-29 tibble_3.3.1
## [13] pkgconfig_2.0.3 KernSmooth_2.23-26 Matrix_1.7-5
## [16] RColorBrewer_1.1-3 S7_0.2.2 lifecycle_1.0.5
## [19] compiler_4.6.0 farver_2.1.2 stringr_1.6.0
## [22] deldir_2.0-4 textshaping_1.0.5 terra_1.9-27
## [25] codetools_0.2-20 class_7.3-23 htmltools_0.5.9
## [28] sass_0.4.10 yaml_2.3.12 hexbin_1.28.5
## [31] pillar_1.11.1 jquerylib_0.1.4 MASS_7.3-65
## [34] classInt_0.4-11 cachem_1.1.0 nlme_3.1-169
## [37] tidyselect_1.2.1 digest_0.6.39 stringi_1.8.7
## [40] sf_1.1-1 labeling_0.4.3 splines_4.6.0
## [43] fastmap_1.2.0 grid_4.6.0 cli_3.6.6
## [46] magrittr_2.0.5 e1071_1.7-17 withr_3.0.2
## [49] scales_1.4.0 rmarkdown_2.31 jpeg_0.1-11
## [52] interp_1.1-6 otel_0.2.0 cellranger_1.1.0
## [55] zoo_1.8-15 png_0.1-9 evaluate_1.0.5
## [58] splancs_2.01-45 tcltk_4.6.0 viridisLite_0.4.3
## [61] mgcv_1.9-4 rlang_1.2.0 Rcpp_1.1.1-1.1
## [64] DBI_1.3.0 glue_1.8.1 xml2_1.5.2
## [67] svglite_2.2.2 rstudioapi_0.18.0 jsonlite_2.0.0
## [70] R6_2.6.1 units_1.0-1 systemfonts_1.3.2
LS0tCnRpdGxlOiAiKipNMlUxIC0gQ2FzbzogQW7DoWxpc2lzIEdlb2VzdGFkw61zdGljbyBkZSBDbGltYSBlbiBGaW5jYSBkZSBBZ3VhY2F0ZSAoUG9wYXnDoW4pKioiCmF1dGhvcjogIlJhZmFlbCBKb3NlIERlbCBDYXN0aWxsbyBQYXZhamVhdSIKZGF0ZTogIjIwMjYtMDYtMDYiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiBjb3NtbwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKbGFuZ3VhZ2U6CiAgbGFiZWw6CiAgICBmaWc6ICJGaWd1cmEgIgogICAgdGFiOiAiVGFibGEgIgpwYXJhbXM6CiAgc2VlZDogODk3Mjc5MQplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCmBgYHtjc3MsIGVjaG89RkFMU0V9CmJvZHksIGgxLCBoMiwgaDMsIHAsIGxpLCB0ZCwgdGggeyBmb250LWZhbWlseTogJ0FwdG9zIERpc3BsYXknLCBzYW5zLXNlcmlmOyB9CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvICAgICAgPSBUUlVFLAogIHdhcm5pbmcgICA9IEZBTFNFLAogIG1lc3NhZ2UgICA9IEZBTFNFLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIGZpZy53aWR0aCA9IDgsCiAgZmlnLmhlaWdodCA9IDUKKQpzZXQuc2VlZChwYXJhbXMkc2VlZCkKYGBgCgpgYGB7ciBsaWJyZXJpYXN9Cm9wdGlvbnMocmVwb3MgPSBjKENSQU4gPSAiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnIikpCgpwYXF1ZXRlcyA8LSBjKCJyZWFkeGwiLCAiZHBseXIiLCAiZ2VvUiIsICJyYXN0ZXIiLCAicmFzdGVyVmlzIiwKICAgICAgICAgICAgICAiZ2dwbG90MiIsICJsZWFmbGV0IiwgInNwIiwgImtuaXRyIiwgImthYmxlRXh0cmEiKQoKZm9yIChwa2cgaW4gcGFxdWV0ZXMpIHsKICBpZiAoIXJlcXVpcmUocGtnLCBjaGFyYWN0ZXIub25seSA9IFRSVUUsIHF1aWV0bHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwa2csIGRlcGVuZGVuY2llcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIHJlcG9zID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpCiAgICBsaWJyYXJ5KHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQogIH0KfQoKcGFsZXRhIDwtIGMoIiMyQzdCQjYiLCAiI0Q3MTkxQyIsICIjMUE5NjQxIiwgIiNGREFFNjEiLCAiI0FCRDlFOSIpCmBgYAoKIyAqKkludHJvZHVjY2nDs24qKgoKTGEgdGVtcGVyYXR1cmEgaW5jaWRlIGRpcmVjdGFtZW50ZSBlbiBsYSBwcm9kdWN0aXZpZGFkIGRlbCBhZ3VhY2F0ZS4gQ29ub2NlciBzdSBkaXN0cmlidWNpw7NuIGVzcGFjaWFsIGRlbnRybyBkZSBsYSBmaW5jYSBwZXJtaXRlIGRlY2lzaW9uZXMgZGUgbWFuZWpvIG3DoXMgcHJlY2lzYXMsIHBlcm8gbG9zIHNlbnNvcmVzIHNvbG8gY2FwdHVyYW4gdmFsb3JlcyBlbiBwdW50b3MgZGlzY3JldG9zLCBoYWNpZW5kbyBuZWNlc2FyaWEgbGEgaW50ZXJwb2xhY2nDs24gcGFyYSBvYnRlbmVyIHVuYSBzdXBlcmZpY2llIGNvbnRpbnVhLgoKRXN0ZSBpbmZvcm1lIGFwbGljYSBnZW9lc3RhZMOtc3RpY2EgYSA1MzQgbWVkaWNpb25lcyBkZSB0ZW1wZXJhdHVyYSBnZW9yZWZlcmVuY2lhZGFzIHBvciDDoXJib2wsIHRvbWFkYXMgZWwgMSBkZSBvY3R1YnJlIGRlIDIwMjAgZW4gdW5hIGZpbmNhIGFndWFjYXRlcmEgZGUgUG9wYXnDoW4sIENhdWNhLiBFbCBvYmpldGl2byBlcyBlc3RpbWFyIGxhIGRpc3RyaWJ1Y2nDs24gY29udGludWEgZGUgdGVtcGVyYXR1cmEgbWVkaWFudGUgS3JpZ2luZyBPcmRpbmFyaW8sIGFwcm92ZWNoYW5kbyBsYSBhdXRvY29ycmVsYWNpw7NuIGVzcGFjaWFsIGRlIGxvcyBkYXRvcyBwYXJhIHByb2R1Y2lyIHByZWRpY2Npb25lcyBpbnNlc2dhZGFzIHkgZGUgdmFyaWFuemEgbcOtbmltYS4gRWwgYW7DoWxpc2lzIHNpZ3VlIGVsIGZsdWpvIGVzdMOhbmRhcjogZXhwbG9yYWNpw7NuIGRlIGRhdG9zLCBzZW1pdmFyaW9ncmFtYSBlbXDDrXJpY28sIGFqdXN0ZSBkZWwgbW9kZWxvIHRlw7NyaWNvLCBpbnRlcnBvbGFjacOzbiBlc3BhY2lhbCwgZ2VuZXJhY2nDs24gZGUgcmFzdGVycyB5IHZhbGlkYWNpw7NuIGNydXphZGEuCgpMb3MgcmVzdWx0YWRvcyBvYnRlbmlkb3MgbXVlc3RyYW4gbGEgbWFnbml0dWQgcmVhbCBkZSBsYSB2YXJpYWNpw7NuIHTDqXJtaWNhIGVudHJlIMOhcmJvbGVzIGVuIHVuYSBtaXNtYSBqb3JuYWRhLCBpZGVudGlmaWNhbiBsb3Mgc2VjdG9yZXMgbcOhcyBmcsOtb3MgeSBtw6FzIGPDoWxpZG9zIGRlbnRybyBkZWwgbG90ZSB5IHNlw7FhbGFuIGNvbmNyZXRhbWVudGUgZMOzbmRlIGxhIHJlZCBkZSBzZW5zb3JlcyBhY3R1YWwgZGVqYSB6b25hcyBjb24gcHJlZGljY2lvbmVzIHBvY28gY29uZmlhYmxlcy4gQ2FiZSBhbm90YXIgcXVlIGVsIGFuw6FsaXNpcyBjdWJyZSB1biDDum5pY28gZMOtYSBkZSBtZWRpY2nDs24sIHBvciBsbyBxdWUgbGFzIGNvbmNsdXNpb25lcyByZWZsZWphbiBsYXMgY29uZGljaW9uZXMgZGUgZXNlIG1vbWVudG8geSBubyBuZWNlc2FyaWFtZW50ZSBlbCBwYXRyw7NuIHTDqXJtaWNvIHBlcm1hbmVudGUgZGUgbGEgZmluY2EuCgojICoqQW7DoWxpc2lzIEV4cGxvcmF0b3JpbyBkZSBEYXRvcyoqCgpgYGB7ciBpbXBvcnRhci1kYXRvc30KZ2VvX2RhdG9zX3JhdyA8LSByZWFkX2V4Y2VsKAogICJDOi9Qb250aWZpY2lhX1VuX0phdmVyaWFuYS9BbmFsaXNpc19HZW9lc3BhY2lhbC9EYXRvc19Db21wbGV0b3NfQWd1YWNhdGUueGxzeCIKKQoKY2F0KCJEaW1lbnNpb25lcyB0b3RhbGVzOiIsIGRpbShnZW9fZGF0b3NfcmF3KSwgIlxuIikKY2F0KCJDb2x1bW5hczoiLCBwYXN0ZShuYW1lcyhnZW9fZGF0b3NfcmF3KSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKY2F0KCJUaXBvIEZPUk1BVFRFRF9EQVRFX1RJTUU6IiwgY2xhc3MoZ2VvX2RhdG9zX3JhdyRGT1JNQVRURURfREFURV9USU1FKSwgIlxuXG4iKQoKIyBGaWx0cmFkbyByb2J1c3RvOiBwcnVlYmEgNCBlc3RyYXRlZ2lhcyBlbiBjYXNjYWRhIHNlZ8O6biB0aXBvIGRlIGNvbHVtbmEKZmVjaGFfb2JqIDwtIGFzLkRhdGUoIjIwMjAtMTAtMDEiKQp2YWxzIDwtIGdlb19kYXRvc19yYXckRk9STUFUVEVEX0RBVEVfVElNRQoKaWYgKGluaGVyaXRzKHZhbHMsIGMoIkRhdGUiLCJQT1NJWGN0IiwiUE9TSVhsdCIpKSkgewogIGlkeCA8LSAhaXMubmEoYXMuRGF0ZSh2YWxzKSkgJiBhcy5EYXRlKHZhbHMpID09IGZlY2hhX29iagp9IGVsc2UgewogIHR4dCA8LSBhcy5jaGFyYWN0ZXIodmFscykKICAjIGludGVudG8gZGQvbW0veXl5eQogIHAxIDwtIHN1cHByZXNzV2FybmluZ3MoYXMuRGF0ZSh0eHQsIGZvcm1hdCA9ICIlZC8lbS8lWSIpKQogIGlmIChhbnkoIWlzLm5hKHAxKSkpIHsKICAgIGlkeCA8LSAhaXMubmEocDEpICYgcDEgPT0gZmVjaGFfb2JqCiAgfSBlbHNlIHsKICAgICMgaW50ZW50byB5eXl5LW1tLWRkIChwcmltZXJvcyAxMCBjaGFycykKICAgIHAyIDwtIHN1cHByZXNzV2FybmluZ3MoYXMuRGF0ZShzdWJzdHIodHh0LCAxLCAxMCkpKQogICAgaWYgKGFueSghaXMubmEocDIpKSkgewogICAgICBpZHggPC0gIWlzLm5hKHAyKSAmIHAyID09IGZlY2hhX29iagogICAgfSBlbHNlIHsKICAgICAgIyBiw7pzcXVlZGEgZGUgdGV4dG8KICAgICAgaWR4IDwtIGdyZXBsKCIyMDIwLTEwLTAxfDAxLzEwLzIwMjB8MDEtMTAtMjAyMCIsIHR4dCkKICAgIH0KICB9Cn0KCmdlb19kYXRvcyA8LSBnZW9fZGF0b3NfcmF3W2lkeCwgXQoKaWYgKG5yb3coZ2VvX2RhdG9zKSA9PSAwKSB7CiAgd2FybmluZygiRmlsdHJvIGRlIGZlY2hhIHNpbiByZXN1bHRhZG9zLiBTZSB1c2Fyw6FuIHRvZG9zIGxvcyBkYXRvcy4iKQogIGdlb19kYXRvcyA8LSBnZW9fZGF0b3NfcmF3Cn0KCmNhdCgiUmVnaXN0cm9zIGZpbHRyYWRvczoiLCBucm93KGdlb19kYXRvcyksICJcbiIpCmNhdCgiUmFuZ28gTG9uZ2l0dWRlOiIsIHJvdW5kKHJhbmdlKGFzLmRvdWJsZShnZW9fZGF0b3MkTG9uZ2l0dWRlKSwgbmEucm09VFJVRSksIDYpLCAiXG4iKQpjYXQoIlJhbmdvIExhdGl0dWRlIDoiLCByb3VuZChyYW5nZShhcy5kb3VibGUoZ2VvX2RhdG9zJExhdGl0dWRlKSwgIG5hLnJtPVRSVUUpLCA2KSwgIlxuIikKIyBWYXJpYWJsZXMgaW5saW5lIGRlbCBkYXRhc2V0CnJlc190ZW1wX21pbiAgIDwtIHJvdW5kKG1pbihnZW9fZGF0b3MkVGVtcGVyYXR1cmUsICBuYS5ybSA9IFRSVUUpLCAxKQpyZXNfdGVtcF9tYXggICA8LSByb3VuZChtYXgoZ2VvX2RhdG9zJFRlbXBlcmF0dXJlLCAgbmEucm0gPSBUUlVFKSwgMSkKcmVzX3RlbXBfbWVkaWEgPC0gcm91bmQobWVhbihnZW9fZGF0b3MkVGVtcGVyYXR1cmUsIG5hLnJtID0gVFJVRSksIDEpCnJlc190ZW1wX3NkICAgIDwtIHJvdW5kKHNkKGdlb19kYXRvcyRUZW1wZXJhdHVyZSwgICBuYS5ybSA9IFRSVUUpLCAxKQpyZXNfdGVtcF9jdiAgICA8LSByb3VuZChzZChnZW9fZGF0b3MkVGVtcGVyYXR1cmUsICAgbmEucm0gPSBUUlVFKSAvCiAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuKGdlb19kYXRvcyRUZW1wZXJhdHVyZSwgbmEucm0gPSBUUlVFKSAqIDEwMCwgMSkKcmVzX3RlbXBfcmFuZ28gPC0gcm91bmQocmVzX3RlbXBfbWF4IC0gcmVzX3RlbXBfbWluLCAxKQpyZXNfbG9uX3JuZ19rbSA8LSByb3VuZChkaWZmKHJhbmdlKGFzLmRvdWJsZShnZW9fZGF0b3MkTG9uZ2l0dWRlKSwgbmEucm0gPSBUUlVFKSkgKiAxMTEsIDIpCnJlc19sYXRfcm5nX2ttIDwtIHJvdW5kKGRpZmYocmFuZ2UoYXMuZG91YmxlKGdlb19kYXRvcyRMYXRpdHVkZSksICBuYS5ybSA9IFRSVUUpKSAqIDExMSwgMikKcmVzX21vZGFfdGVtcCAgPC0gcm91bmQoYXMubnVtZXJpYyhuYW1lcyh3aGljaC5tYXgodGFibGUocm91bmQoZ2VvX2RhdG9zJFRlbXBlcmF0dXJlLCAwKSkpKSksIDApCmBgYAoKYGBge3IgdGFibGEtcmVzdW1lbi1kYXRvc30Ka2FibGUoaGVhZChnZW9fZGF0b3NbLCBjKCJpZF9hcmJvbCIsIkxhdGl0dWRlIiwiTG9uZ2l0dWRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiVGVtcGVyYXR1cmUiLCJSZWxhdGl2ZV9IdW1pZGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIldpbmRfU3BlZWQiLCJBbHRpdHVkZSIpXSwgMTApLAogICAgICBjYXB0aW9uID0gIlRhYmxhIDEuIFByaW1lcm9zIDEwIHJlZ2lzdHJvcyBkZWwgY29uanVudG8gZGUgZGF0b3MgZmlsdHJhZG8gYWwgcGVyw61vZG8gMDEvMTAvMjAyMCIsCiAgICAgIGRpZ2l0cyA9IDMsIGFsaWduID0gImMiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsImhvdmVyIiwiY29uZGVuc2VkIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKRW4gZXN0YSBzZWNjacOzbiBzZSByZWFsaXphIGVsIGFuw6FsaXNpcyBleHBsb3JhdG9yaW8gZGUgbGEgVGVtcGVyYXR1cmEgbWVkaWFudGUgZXN0YWTDrXN0aWNhcyBkZXNjcmlwdGl2YXMgeSB2aXN1YWxpemFjaW9uZXMuCgojIyBFc3RhZMOtc3RpY2FzIERlc2NyaXB0aXZhcwoKYGBge3IgZXN0YWRpc3RpY2FzLWRlc2NyaXB0aXZhc30KcmVzdW1lbl90ZW1wIDwtIGRhdGEuZnJhbWUoCiAgRXN0YWTDrXN0aWNvID0gYygiTcOtbmltbyIsIlBlcmNlbnRpbCAyNSIsIk1lZGlhbmEiLCJNZWRpYSIsCiAgICAgICAgICAgICAgICAgICJQZXJjZW50aWwgNzUiLCJNw6F4aW1vIiwiRGVzdi4gRXN0w6FuZGFyIiwiQ1YgKCUpIiksCiAgVmFsb3IgPSBjKAogICAgcm91bmQobWluKGdlb19kYXRvcyRUZW1wZXJhdHVyZSwgICAgICAgICAgICAgICAgICAgICBuYS5ybT1UUlVFKSwgMyksCiAgICByb3VuZChxdWFudGlsZShnZW9fZGF0b3MkVGVtcGVyYXR1cmUsIDAuMjUsICAgICAgICAgIG5hLnJtPVRSVUUpLCAzKSwKICAgIHJvdW5kKG1lZGlhbihnZW9fZGF0b3MkVGVtcGVyYXR1cmUsICAgICAgICAgICAgICAgICAgbmEucm09VFJVRSksIDMpLAogICAgcm91bmQobWVhbihnZW9fZGF0b3MkVGVtcGVyYXR1cmUsICAgICAgICAgICAgICAgICAgICBuYS5ybT1UUlVFKSwgMyksCiAgICByb3VuZChxdWFudGlsZShnZW9fZGF0b3MkVGVtcGVyYXR1cmUsIDAuNzUsICAgICAgICAgIG5hLnJtPVRSVUUpLCAzKSwKICAgIHJvdW5kKG1heChnZW9fZGF0b3MkVGVtcGVyYXR1cmUsICAgICAgICAgICAgICAgICAgICAgbmEucm09VFJVRSksIDMpLAogICAgcm91bmQoc2QoZ2VvX2RhdG9zJFRlbXBlcmF0dXJlLCAgICAgICAgICAgICAgICAgICAgICBuYS5ybT1UUlVFKSwgMyksCiAgICByb3VuZChzZChnZW9fZGF0b3MkVGVtcGVyYXR1cmUsICAgbmEucm09VFJVRSkgLwogICAgICAgICAgICBtZWFuKGdlb19kYXRvcyRUZW1wZXJhdHVyZSwgbmEucm09VFJVRSkgKiAxMDAsIDIpCiAgKQopCgprYWJsZShyZXN1bWVuX3RlbXAsCiAgICAgIGNhcHRpb24gPSAiVGFibGEgMi4gRXN0YWTDrXN0aWNhcyBkZXNjcmlwdGl2YXMgZGUgbGEgdmFyaWFibGUgVGVtcGVyYXR1cmEgKMKwQykiLAogICAgICBhbGlnbiA9IGMoImwiLCJyIikpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIExhIHRhYmxhIG11ZXN0cmEgcXVlIGxhIHRlbXBlcmF0dXJhIHJlZ2lzdHJhZGEgZWwgMSBkZSBvY3R1YnJlIGRlIDIwMjAgb3NjaWzDsyBlbnRyZSBgciByZXNfdGVtcF9taW5gIMKwQyB5IGByIHJlc190ZW1wX21heGAgwrBDLCBjb24gdW5hIG1lZGlhIGRlIGByIHJlc190ZW1wX21lZGlhYCDCsEMgeSB1bmEgZGVzdmlhY2nDs24gZXN0w6FuZGFyIGRlIGByIHJlc190ZW1wX3NkYCDCsEMuIEVsIGNvZWZpY2llbnRlIGRlIHZhcmlhY2nDs24gZGUgYHIgcmVzX3RlbXBfY3ZgICUgaW5kaWNhIHVuYSBkaXNwZXJzacOzbiBtb2RlcmFkYSBxdWUgc3VnaWVyZSB2YXJpYWJpbGlkYWQgZXNwYWNpYWwgZGV0ZWN0YWJsZS4gRXNlIHJhbmdvIGRlIGFwcm94aW1hZGFtZW50ZSBgciByZXNfdGVtcF9yYW5nb2AgwrBDIGVudHJlIGVsIMOhcmJvbCBtw6FzIGZyw61vIHkgZWwgbcOhcyBjYWxpZW50ZSBlcyBhZ3JvbsOzbWljYW1lbnRlIHJlbGV2YW50ZSwgeWEgcXVlIGRpZmVyZW5jaWFzIHN1cGVyaW9yZXMgYSAyIMKwQyBkZW50cm8gZGUgdW4gbG90ZSBwdWVkZW4gYWZlY3RhciBsYSBzaW5jcm9uw61hIGRlIGZsb3JhY2nDs24gZGVsIGFndWFjYXRlIEhhc3MuCgojIyBEaXN0cmlidWNpw7NuIGRlIFRlbXBlcmF0dXJhCgpgYGB7ciBoaXN0b2dyYW1hLXRlbXBlcmF0dXJhLCBmaWcuY2FwPSJGaWd1cmEgMS4gRGlzdHJpYnVjacOzbiBkZSBmcmVjdWVuY2lhcyBkZSBsYSB0ZW1wZXJhdHVyYSByZWdpc3RyYWRhIGVuIGxvcyDDoXJib2xlcyBkZSBhZ3VhY2F0ZSBkdXJhbnRlIGVsIHBlcsOtb2RvIDAxLzEwLzIwMjAuIn0KZ2dwbG90KGdlb19kYXRvcywgYWVzKHggPSBUZW1wZXJhdHVyZSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IGFmdGVyX3N0YXQoZGVuc2l0eSkpLAogICAgICAgICAgICAgICAgIGJpbnMgPSAyNSwgZmlsbCA9IHBhbGV0YVsxXSwgY29sb3IgPSAid2hpdGUiLCBhbHBoYSA9IDAuODUpICsKICBnZW9tX2RlbnNpdHkoY29sb3IgPSBwYWxldGFbMl0sIGxpbmV3aWR0aCA9IDEuMikgKwogIGxhYnMoeCA9ICJUZW1wZXJhdHVyYSAowrBDKSIsIHkgPSAiRGVuc2lkYWQiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMykgKwogIHRoZW1lKHBsb3QudGl0bGUgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgY29sb3IgPSBwYWxldGFbMV0pKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIEVsIGhpc3RvZ3JhbWEgbXVlc3RyYSB1bmEgZGlzdHJpYnVjacOzbiBjb24gZG9zIG1vZG9zIGFwcm94aW1hZG9zIGFscmVkZWRvciBkZSBgciByZXNfbW9kYV90ZW1wYCDCsEMgeSBlbiBlbCByYW5nbyBkZSAyNi0yNyDCsEMsIGxvIHF1ZSBzdWdpZXJlIHF1ZSBlbiBsYSBmaW5jYSBjb2V4aXN0ZW4gYWwgbWVub3MgZG9zIG1pY3JvYW1iaWVudGVzIHTDqXJtaWNvcyBkaWZlcmVuY2lhZG9zLiBMYSBhc2ltZXRyw61hIGxldmUgaGFjaWEgdmFsb3JlcyBhbHRvcyBpbmRpY2EgcXVlIHVuIGdydXBvIGRlIMOhcmJvbGVzIHJlZ2lzdHLDsyB0ZW1wZXJhdHVyYXMgbm90b3JpYW1lbnRlIHN1cGVyaW9yZXMgYSBsYSBtYXlvcsOtYS4gRGFkbyBxdWUgbGEgZGlzdHJpYnVjacOzbiBubyBzZSBhbGVqYSBkZW1hc2lhZG8gZGUgbGEgbm9ybWFsaWRhZCwgZWwgS3JpZ2luZyBPcmRpbmFyaW8gcHVlZGUgYXBsaWNhcnNlIHNpbiB0cmFuc2Zvcm1hY2nDs24gcHJldmlhIGRlIGxhIHZhcmlhYmxlLCBhdW5xdWUgc2Vyw61hIHBydWRlbnRlIHZlcmlmaWNhciBlc3RlIHN1cHVlc3RvIGZvcm1hbG1lbnRlIGNvbiB1bmEgcHJ1ZWJhIGRlIG5vcm1hbGlkYWQgc2kgZWwgYW7DoWxpc2lzIHNlIGV4dGllbmRlIGEgb3Ryb3MgcGVyw61vZG9zLgoKIyMgRGlzdHJpYnVjacOzbiBlbiDDgXJib2xlcwoKYGBge3IgbWFwYS1kaXN0cmlidWNpb24tbGVhZmxldCwgZmlnLmNhcD0iRmlndXJhIDIuIE1hcGEgaW50ZXJhY3Rpdm8gZGUgZGlzdHJpYnVjacOzbiBlc3BhY2lhbCBkZSBsb3Mgw6FyYm9sZXMgZGUgYWd1YWNhdGUgY29uIGPDs2RpZ28gZGUgY29sb3IgcG9yIHRlbXBlcmF0dXJhIChQb3BhecOhbiwgQ2F1Y2EpLiJ9CnBhbF90ZW1wIDwtIGNvbG9yTnVtZXJpYyhwYWxldHRlID0gYyhwYWxldGFbNV0sIHBhbGV0YVs0XSwgcGFsZXRhWzJdKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbiAgPSBnZW9fZGF0b3MkVGVtcGVyYXR1cmUpCgpsZWFmbGV0KGdlb19kYXRvcykgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRDaXJjbGVNYXJrZXJzKGxuZyA9IH5Mb25naXR1ZGUsIGxhdCA9IH5MYXRpdHVkZSwgcmFkaXVzID0gMywKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnBhbF90ZW1wKFRlbXBlcmF0dXJlKSwKICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCBmaWxsT3BhY2l0eSA9IDAuODUsCiAgICAgICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZTAoIjxiPsOBcmJvbDo8L2I+ICIsIGlkX2FyYm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+PGI+VGVtcDo8L2I+ICIsIHJvdW5kKFRlbXBlcmF0dXJlLDIpLCAiIMKwQyIpKSAlPiUKICBhZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLCBwYWwgPSBwYWxfdGVtcCwKICAgICAgICAgICAgdmFsdWVzID0gflRlbXBlcmF0dXJlLCB0aXRsZSA9ICJUZW1wZXJhdHVyYSAowrBDKSIsIG9wYWNpdHkgPSAwLjg1KQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIEVsIG1hcGEgY29uZmlybWEgcXVlIGxvcyBgciBucm93KGdlb19kYXRvcylgIMOhcmJvbGVzIG1vbml0b3JlYWRvcyBjdWJyZW4gZGUgZm9ybWEgY29udGludWEgbGEgZXh0ZW5zacOzbiBkZSBsYSBmaW5jYSwgY29uIHVuYSBkaXN0cmlidWNpw7NuIHF1ZSBhYmFyY2EgYXByb3hpbWFkYW1lbnRlIGByIHJlc19sb25fcm5nX2ttYCBrbSBlbiBsb25naXR1ZCB5IGByIHJlc19sYXRfcm5nX2ttYCBrbSBlbiBsYXRpdHVkLiBTZSBvYnNlcnZhIHZpc3VhbG1lbnRlIHF1ZSBsb3Mgw6FyYm9sZXMgY29uIHRlbXBlcmF0dXJhIG3DoXMgYWx0YSBzZSBjb25jZW50cmFuIGVuIHVuIHNlY3RvciBwYXJ0aWN1bGFyIGRlbCBsb3RlLCBsbyBxdWUgcG9kcsOtYSBlc3RhciByZWxhY2lvbmFkbyBjb24gbGEgZXhwb3NpY2nDs24gc29sYXIsIGVsIGRyZW5hamUgZGVsIHN1ZWxvIG8gbGEgZGVuc2lkYWQgZGVsIGRvc2VsIGVuIGVzYSB6b25hLiBFc3RhIGNvbmNlbnRyYWNpw7NuIGVzcGFjaWFsIGRlIHZhbG9yZXMgZXh0cmVtb3MgZXMgcHJlY2lzYW1lbnRlIGxvIHF1ZSBlbCBzZW1pdmFyaW9ncmFtYSBkZWJlcsOhIGNhcHR1cmFyLgoKIyMgVGVtcGVyYXR1cmEgeSBDb29yZGVuYWRhcwoKYGBge3Igc2NhdHRlci10ZW1wLWxhdCwgZmlnLmNhcD0iRmlndXJhIDMuIERpc3BlcnNpw7NuIGRlIGxhIHRlbXBlcmF0dXJhIGVuIGZ1bmNpw7NuIGRlIGxhIGxhdGl0dWQuIExhIGzDrW5lYSBkZSByZWdyZXNpw7NuIHBlcm1pdGUgZXZhbHVhciB0ZW5kZW5jaWEgZXNwYWNpYWwgbm9ydGUtc3VyLiJ9CmdncGxvdChnZW9fZGF0b3MsIGFlcyh4ID0gTGF0aXR1ZGUsIHkgPSBUZW1wZXJhdHVyZSkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gcGFsZXRhWzFdLCBhbHBoYSA9IDAuNSwgc2l6ZSA9IDEuOCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4geCwKICAgICAgICAgICAgICBjb2xvciA9IHBhbGV0YVsyXSwgc2UgPSBUUlVFLCBsaW5ld2lkdGggPSAxLjIsCiAgICAgICAgICAgICAgZmlsbCA9IHBhbGV0YVs1XSwgYWxwaGEgPSAwLjMpICsKICBsYWJzKHggPSAiTGF0aXR1ZCIsIHkgPSAiVGVtcGVyYXR1cmEgKMKwQykiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMykgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgY29sb3IgPSBwYWxldGFbMV0pKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIExhIG51YmUgZGUgcHVudG9zIGNvbnRyYSBsYSBsYXRpdHVkIG11ZXN0cmEgdW5hIGRpc3BlcnNpw7NuIGFtcGxpYSBzaW4gdW4gcGF0csOzbiBsaW5lYWwgZXZpZGVudGUsIGxvIHF1ZSBzdWdpZXJlIHF1ZSBubyBleGlzdGUgdW5hIHRlbmRlbmNpYSB0w6lybWljYSBzaXN0ZW3DoXRpY2EgZW4gZGlyZWNjacOzbiBub3J0ZS1zdXIgZGVudHJvIGRlbCBsb3RlLiBMYSBiYW5kYSBkZSBjb25maWFuemEgZGUgbGEgcmVncmVzacOzbiBsaW5lYWwgZXMgcmVsYXRpdmFtZW50ZSBhbmNoYSwgcmVmb3J6YW5kbyBxdWUgbGEgcGVuZGllbnRlIG5vIGVzIGVzdGFkw61zdGljYW1lbnRlIHJvYnVzdGEuIEVzdG8gZXMgY29uc2lzdGVudGUgY29uIGxhIHRvcG9ncmFmw61hIHJlbGF0aXZhbWVudGUgcGxhbmEgZGUgbGEgem9uYSBiYWphIGRlIFBvcGF5w6FuIGRvbmRlIHNlIHViaWNhIGxhIGZpbmNhLiBMYSBhdXNlbmNpYSBkZSB0ZW5kZW5jaWEgbm9ydGUtc3VyIGVzIHVuIGluZGljaW8gZmF2b3JhYmxlIHBhcmEgYXN1bWlyIGVzdGFjaW9uYXJpZWRhZCBpbnRyw61uc2VjYSB5IHByb2NlZGVyIGNvbiBLcmlnaW5nIE9yZGluYXJpby4KCmBgYHtyIHNjYXR0ZXItdGVtcC1sb24sIGZpZy5jYXA9IkZpZ3VyYSA0LiBEaXNwZXJzacOzbiBkZSBsYSB0ZW1wZXJhdHVyYSBlbiBmdW5jacOzbiBkZSBsYSBsb25naXR1ZC4gTGEgbMOtbmVhIGRlIHJlZ3Jlc2nDs24gcGVybWl0ZSBldmFsdWFyIHRlbmRlbmNpYSBlc3BhY2lhbCBlc3RlLW9lc3RlLiJ9CmdncGxvdChnZW9fZGF0b3MsIGFlcyh4ID0gTG9uZ2l0dWRlLCB5ID0gVGVtcGVyYXR1cmUpKSArCiAgZ2VvbV9wb2ludChjb2xvciA9IHBhbGV0YVszXSwgYWxwaGEgPSAwLjUsIHNpemUgPSAxLjgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHgsCiAgICAgICAgICAgICAgY29sb3IgPSBwYWxldGFbMl0sIHNlID0gVFJVRSwgbGluZXdpZHRoID0gMS4yLAogICAgICAgICAgICAgIGZpbGwgPSBwYWxldGFbNV0sIGFscGhhID0gMC4zKSArCiAgbGFicyh4ID0gIkxvbmdpdHVkIiwgeSA9ICJUZW1wZXJhdHVyYSAowrBDKSIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBjb2xvciA9IHBhbGV0YVszXSkpCmBgYAoKKipJbnRlcnByZXRhY2nDs246KiogTGEgZGlzcGVyc2nDs24gY29udHJhIGxhIGxvbmdpdHVkIHRhbXBvY28gZXZpZGVuY2lhIHVuYSB0ZW5kZW5jaWEgbGluZWFsIGNsYXJhIGVuIGRpcmVjY2nDs24gZXN0ZS1vZXN0ZS4gU2luIGVtYmFyZ28sIHNlIGFwcmVjaWEgdW5hIG1heW9yIGRlbnNpZGFkIGRlIHB1bnRvcyBjb24gdGVtcGVyYXR1cmEgZWxldmFkYSBlbiBlbCBleHRyZW1vIG9jY2lkZW50YWwgZGVsIGxvdGUsIGxvIHF1ZSBwb2Ryw61hIGNvcnJlc3BvbmRlciBhIGxhIHBhcnRlIGRlIGxhIGZpbmNhIGNvbiBtYXlvciBleHBvc2ljacOzbiBhIGxhIHRhcmRlLiBTaSBlc3RlIHBhdHLDs24gc2UgY29uZmlybWEgY29uIG1lZGljaW9uZXMgZGUgb3Ryb3MgZMOtYXMsIHNlcsOtYSB1biBhcmd1bWVudG8gcGFyYSBleHBsb3JhciBLcmlnaW5nIFVuaXZlcnNhbCBjb24gdW5hIHRlbmRlbmNpYSBsaW5lYWwgZW4gbG9uZ2l0dWQuIFBvciBhaG9yYSwgY29uIHVuIMO6bmljbyBkw61hIGRlIGRhdG9zLCBlbCBLcmlnaW5nIE9yZGluYXJpbyBlcyBsYSBlbGVjY2nDs24gbcOhcyBwYXJzaW1vbmlvc2EuCgojICoqRXZhbHVhY2nDs24gY29uIEdlb2RhdGEqKgoKRW4gZXN0YSBzZWNjacOzbiBzZSBjb25zdHJ1eWUgZWwgb2JqZXRvIGBnZW9kYXRhYCBkZSBgZ2VvUmAgeSBzZSB2aXN1YWxpemFuIGxvcyBjdWF0cm8gcGFuZWxlcyBkaWFnbsOzc3RpY28gZXF1aXZhbGVudGVzLgoKYGBge3IgY3JlYXItZ2VvZGF0YX0KIyBFeHRyYWVyIHZlY3RvcmVzIGRvdWJsZSBwdXJvcyB5IGVsaW1pbmFyIE5BL0luZgpsb25fdmVjICA8LSBhcy5kb3VibGUoZ2VvX2RhdG9zW1siTG9uZ2l0dWRlIl1dKQpsYXRfdmVjICA8LSBhcy5kb3VibGUoZ2VvX2RhdG9zW1siTGF0aXR1ZGUiXV0pCnRlbXBfdmVjIDwtIGFzLmRvdWJsZShnZW9fZGF0b3NbWyJUZW1wZXJhdHVyZSJdXSkKCmlkeF9vayAgIDwtIGlzLmZpbml0ZShsb25fdmVjKSAmIGlzLmZpbml0ZShsYXRfdmVjKSAmIGlzLmZpbml0ZSh0ZW1wX3ZlYykKbG9uX3ZlYyAgPC0gbG9uX3ZlY1tpZHhfb2tdCmxhdF92ZWMgIDwtIGxhdF92ZWNbaWR4X29rXQp0ZW1wX3ZlYyA8LSB0ZW1wX3ZlY1tpZHhfb2tdCgpjYXQoIk9ic2VydmFjaW9uZXMgbGltcGlhczoiLCBsZW5ndGgodGVtcF92ZWMpLCAiXG4iKQpjYXQoIlJhbmdvIExvbmdpdHVkICAgICAgIDoiLCByb3VuZChyYW5nZShsb25fdmVjKSwgNiksICJcbiIpCmNhdCgiUmFuZ28gTGF0aXR1ZCAgICAgICAgOiIsIHJvdW5kKHJhbmdlKGxhdF92ZWMpLCA2KSwgIlxuIikKY2F0KCJSYW5nbyBUZW1wZXJhdHVyYSAgICA6Iiwgcm91bmQocmFuZ2UodGVtcF92ZWMpLCAzKSwgIlxuIikKCiMgQ29uc3RydWlyIGdlb2RhdGEgbWFudWFsbWVudGUgKGV2aXRhIHByb2JsZW1hcyBjb24gdGliYmxlcyBkZSByZWFkeGwpCmdlb190ZW1wIDwtIGxpc3QoCiAgY29vcmRzID0gbWF0cml4KGMobG9uX3ZlYywgbGF0X3ZlYyksIG5jb2wgPSAyLAogICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QoTlVMTCwgYygibG9uIiwibGF0IikpKSwKICBkYXRhICAgPSB0ZW1wX3ZlYwopCmNsYXNzKGdlb190ZW1wKSA8LSAiZ2VvZGF0YSIKcmVzX24gPC0gbGVuZ3RoKGdlb190ZW1wJGRhdGEpCmBgYAoKYGBge3IgcGxvdC1nZW9kYXRhLXBhbmVsMSwgZmlnLmNhcD0iRmlndXJhIDUuIERpc3RyaWJ1Y2nDs24gZXNwYWNpYWwgZGUgbG9zIHB1bnRvcyBkZSBtdWVzdHJlbyBjbGFzaWZpY2Fkb3MgcG9yIGN1YXJ0aWwgZGUgdGVtcGVyYXR1cmEuIn0KIyBQYW5lbCAxOiBkaXN0cmlidWNpw7NuIGVzcGFjaWFsIHBvciBjdWFydGlsIChlcXVpdmFsZW50ZSBhIHBsb3QuZ2VvZGF0YSBwYW5lbCAxKQpxX251bSA8LSBkcGx5cjo6bnRpbGUoZ2VvX3RlbXAkZGF0YSwgNCkKbGJsX21hcCA8LSBjKCIxIj0iUTEgQmFqbyIsIjIiPSJRMiIsIjMiPSJRMyIsIjQiPSJRNCBBbHRvIikKCmRmX2dlbyA8LSBkYXRhLmZyYW1lKAogIGxvbiAgICAgPSBnZW9fdGVtcCRjb29yZHNbLCJsb24iXSwKICBsYXQgICAgID0gZ2VvX3RlbXAkY29vcmRzWywibGF0Il0sCiAgdGVtcCAgICA9IGdlb190ZW1wJGRhdGEsCiAgY3VhcnRpbCA9IGZhY3RvcihsYmxfbWFwW2FzLmNoYXJhY3RlcihxX251bSldLCBsZXZlbHMgPSBsYmxfbWFwKQopCgpnZ3Bsb3QoZGZfZ2VvLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgY29sb3IgPSBjdWFydGlsKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgYWxwaGEgPSAwLjgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzJDN0JCNiIsIiMxQTk2NDEiLCIjRkRBRTYxIiwiI0Q3MTkxQyIpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkN1YXJ0aWwiLCBkcm9wID0gRkFMU0UpICsKICBsYWJzKHggPSAiTG9uZ2l0dWQiLCB5ID0gIkxhdGl0dWQiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMikgKyBjb29yZF9maXhlZCgpCmBgYAoKYGBge3IgcGxvdC1nZW9kYXRhLXBhbmVsMiwgZmlnLmNhcD0iRmlndXJhIDYuIFRlbXBlcmF0dXJhIGVuIGZ1bmNpw7NuIGRlIGxhIGxhdGl0dWQgY29uIHN1YXZpemFkbyBMT0VTUy4gUGFuZWwgZGlhZ27Ds3N0aWNvIGRlIHRlbmRlbmNpYSBub3J0ZS1zdXIgZGVsIGdlb2RhdGEuIn0KZ2dwbG90KGRmX2dlbywgYWVzKHggPSBsYXQsIHkgPSB0ZW1wKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSBwYWxldGFbMV0sIGFscGhhID0gMC40LCBzaXplID0gMS41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgZm9ybXVsYSA9IHkgfiB4LAogICAgICAgICAgICAgIGNvbG9yID0gcGFsZXRhWzJdLCBzZSA9IEZBTFNFLCBsaW5ld2lkdGggPSAxKSArCiAgbGFicyh4ID0gIkxhdGl0dWQiLCB5ID0gIlRlbXBlcmF0dXJhICjCsEMpIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpCmBgYAoKYGBge3IgcGxvdC1nZW9kYXRhLXBhbmVsMywgZmlnLmNhcD0iRmlndXJhIDcuIFRlbXBlcmF0dXJhIGVuIGZ1bmNpw7NuIGRlIGxhIGxvbmdpdHVkIGNvbiBzdWF2aXphZG8gTE9FU1MuIFBhbmVsIGRpYWduw7NzdGljbyBkZSB0ZW5kZW5jaWEgZXN0ZS1vZXN0ZSBkZWwgZ2VvZGF0YS4ifQpnZ3Bsb3QoZGZfZ2VvLCBhZXMoeCA9IGxvbiwgeSA9IHRlbXApKSArCiAgZ2VvbV9wb2ludChjb2xvciA9IHBhbGV0YVszXSwgYWxwaGEgPSAwLjQsIHNpemUgPSAxLjUpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBmb3JtdWxhID0geSB+IHgsCiAgICAgICAgICAgICAgY29sb3IgPSBwYWxldGFbMl0sIHNlID0gRkFMU0UsIGxpbmV3aWR0aCA9IDEpICsKICBsYWJzKHggPSAiTG9uZ2l0dWQiLCB5ID0gIlRlbXBlcmF0dXJhICjCsEMpIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpCmBgYAoKYGBge3IgcGxvdC1nZW9kYXRhLXBhbmVsNCwgZmlnLmNhcD0iRmlndXJhIDguIERpc3RyaWJ1Y2nDs24gbWFyZ2luYWwgZGUgbGEgdGVtcGVyYXR1cmEgY29uIGhpc3RvZ3JhbWEgeSBjdXJ2YSBkZSBkZW5zaWRhZCBrZXJuZWwuIn0KZ2dwbG90KGRmX2dlbywgYWVzKHggPSB0ZW1wKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gYWZ0ZXJfc3RhdChkZW5zaXR5KSksCiAgICAgICAgICAgICAgICAgYmlucyA9IDIwLCBmaWxsID0gcGFsZXRhWzFdLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC44KSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gcGFsZXRhWzJdLCBsaW5ld2lkdGggPSAxLjIpICsKICBsYWJzKHggPSAiVGVtcGVyYXR1cmEgKMKwQykiLCB5ID0gIkRlbnNpZGFkIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpCmBgYAoKKipJbnRlcnByZXRhY2nDs246KiogRWwgcGFuZWwgZGUgY3VhcnRpbGVzIChGaWd1cmEgNSkgbXVlc3RyYSBxdWUgbG9zIMOhcmJvbGVzIGRlbCBRNCAodGVtcGVyYXR1cmFzIG3DoXMgYWx0YXMpIG5vIGVzdMOhbiBkaXN0cmlidWlkb3MgYWxlYXRvcmlhbWVudGU6IGZvcm1hbiB1biBjbHVzdGVyIHZpc2libGUgZW4gZWwgc2VjdG9yIG5vcm9jY2lkZW50YWwgZGVsIGxvdGUuIExvcyDDoXJib2xlcyBkZWwgUTEgKG3DoXMgZnLDrW9zKSB0aWVuZGVuIGEgdWJpY2Fyc2UgZW4gZWwgc2VjdG9yIHN1cm9yaWVudGFsLiBFc3RhIGFncnVwYWNpw7NuIGVzcGFjaWFsIGRlIHZhbG9yZXMgc2ltaWxhcmVzIGVzIGxhIHNlw7FhbCBtw6FzIGRpcmVjdGEgZGUgcXVlIGV4aXN0ZSBhdXRvY29ycmVsYWNpw7NuIGVzcGFjaWFsIHBvc2l0aXZhIGVuIGxhIHRlbXBlcmF0dXJhLCBsbyBxdWUganVzdGlmaWNhIGVsIHVzbyBkZSBnZW9lc3RhZMOtc3RpY2EgZnJlbnRlIGEgbcOpdG9kb3MgZGUgaW50ZXJwb2xhY2nDs24gZGV0ZXJtaW7DrXN0aWNvcy4gTG9zIHBhbmVsZXMgTE9FU1MgY29udHJhIGxhdGl0dWQgeSBsb25naXR1ZCBjb25maXJtYW4gbG8gb2JzZXJ2YWRvIGVuIGxhcyByZWdyZXNpb25lcyBsaW5lYWxlczogbm8gaGF5IHVuYSB0ZW5kZW5jaWEgZ2xvYmFsIGRvbWluYW50ZSwgYXVucXVlIGxhIGN1cnZhIGNvbnRyYSBsYXRpdHVkIG11ZXN0cmEgdW5hIGxpZ2VyYSBjdXJ2YXR1cmEgcXVlIG1lcmVjZSBhdGVuY2nDs24uIEVsIGhpc3RvZ3JhbWEgZmluYWwgKEZpZ3VyYSA4KSByZWl0ZXJhIGxhIGJpbW9kYWxpZGFkIG9ic2VydmFkYSBwcmV2aWFtZW50ZS4KCiMgKipTZW1pdmFyaW9ncmFtYSBFbXDDrXJpY28qKgoKRW4gZXN0YSBzZWNjacOzbiBzZSBjYWxjdWxhIGVsIHNlbWl2YXJpb2dyYW1hIGVtcMOtcmljbyB5IGxvcyBlbnZvbHZlbnRlcyBkZSBNb250ZSBDYXJsbyBwYXJhIGV2YWx1YXIgbGEgYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbCBkZSBsYSB0ZW1wZXJhdHVyYS4KCmBgYHtyIHNlbWl2YXJpb2dyYW1hLWVtcGlyaWNvfQpjYXQoIk9ic2VydmFjaW9uZXMgZW4gZ2VvZGF0YToiLCBsZW5ndGgoZ2VvX3RlbXAkZGF0YSksICJcbiIpCgppZiAobGVuZ3RoKGdlb190ZW1wJGRhdGEpIDwgMTApCiAgc3RvcCgiR2VvZGF0YSBjb24gbWVub3MgZGUgMTAgb2JzLiBSZXZpc2FyIGZpbHRyYWRvIGRlIGZlY2hhLiIpCgojIG1heC5kaXN0ID0gbWl0YWQgZGUgbGEgZGlhZ29uYWwgZGVsIGJvdW5kaW5nIGJveApkZWx0YV9sb24gPC0gZGlmZihyYW5nZShnZW9fdGVtcCRjb29yZHNbLCJsb24iXSkpCmRlbHRhX2xhdCA8LSBkaWZmKHJhbmdlKGdlb190ZW1wJGNvb3Jkc1ssImxhdCJdKSkKbWF4X2Rpc3QgIDwtIHNxcnQoZGVsdGFfbG9uXjIgKyBkZWx0YV9sYXReMikgKiAwLjUKCiMgUHJvdGVjY2nDs24gc2kgbGEgZmluY2EgdGllbmUgZXh0ZW5zacOzbiBjYXNpIG51bGEgZW4gYWxndW5hIGRpcmVjY2nDs24KaWYgKCFpcy5maW5pdGUobWF4X2Rpc3QpIHx8IG1heF9kaXN0IDwgMWUtMTApCiAgbWF4X2Rpc3QgPC0gbWF4KGRlbHRhX2xvbiwgZGVsdGFfbGF0LCBuYS5ybSA9IFRSVUUpICogMC41CgpjYXQoIm1heF9kaXN0IHBhcmEgdmFyaW9nOiIsIHJvdW5kKG1heF9kaXN0LCA4KSwgInwgZmluaXRvOiIsIGlzLmZpbml0ZShtYXhfZGlzdCksICJcbiIpCgp2YXJpb2dyYW1hIDwtIHZhcmlvZyhnZW9fdGVtcCwgb3B0aW9uID0gImJpbiIsIG1heC5kaXN0ID0gbWF4X2Rpc3QpCgojIGRpc3RfNTAgcmV1dGlsaXphZG8gZW4gYWp1c3RlLW1vZGVsb3MgcGFyYSBpbmkudmFscwpkaXN0XzUwIDwtIG1heF9kaXN0CgpjYXQoIkJpbnMgY2FsY3VsYWRvczoiLCBsZW5ndGgodmFyaW9ncmFtYSR1KSwgIlxuIikKYGBgCgpgYGB7ciBlbnZvbHZlbnRlLW1vbnRlY2FybG99CnNldC5zZWVkKHBhcmFtcyRzZWVkKQp2YXJpb2dyYW1hX21jIDwtIHZhcmlvZy5tYy5lbnYoZ2VvX3RlbXAsIG9iai52YXJpb2cgPSB2YXJpb2dyYW1hLCBuc2ltID0gOTkpCmNhdCgiRW52b2x2ZW50ZXMgTW9udGUgQ2FybG86IDk5IHNpbXVsYWNpb25lcyBjb21wbGV0YWRhcy5cbiIpCmBgYAoKYGBge3IgcGxvdC12YXJpb2dyYW1hLWVtcGlyaWNvLCBmaWcuY2FwPSJGaWd1cmEgOS4gU2VtaXZhcmlvZ3JhbWEgZW1ww61yaWNvIG9tbmlkaXJlY2Npb25hbCBkZSBsYSB0ZW1wZXJhdHVyYSBjb24gZW52b2x2ZW50ZXMgZGUgTW9udGUgQ2FybG8gKDk5IHNpbXVsYWNpb25lcykuIn0KcGxvdCh2YXJpb2dyYW1hLAogICAgIHhsYWIgPSAiRGlzdGFuY2lhIChncmFkb3MpIiwgeWxhYiA9ICJTZW1pdmFyaWFuemEiLAogICAgIHBjaCA9IDE2LCBjb2wgPSBwYWxldGFbMV0pCmxpbmVzKHZhcmlvZ3JhbWFfbWMsIGNvbCA9ICJncmV5NjAiLCBsdHkgPSAyKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIEVsIHNlbWl2YXJpb2dyYW1hIGVtcMOtcmljbyBjYWxjdWxhZG8gc29icmUgYHIgcmVzX25gIG9ic2VydmFjaW9uZXMgY29uIHVuYSBkaXN0YW5jaWEgbcOheGltYSBkZSBgciByb3VuZChtYXhfZGlzdCwgNSlgIGdyYWRvcyBtdWVzdHJhIHVuIGluY3JlbWVudG8gY2xhcm8gZGUgbGEgc2VtaXZhcmlhbnphIGVuIGxhcyBwcmltZXJhcyBjbGFzZXMgZGUgZGlzdGFuY2lhLCBsbyBxdWUgY29uZmlybWEgcXVlIGxvcyDDoXJib2xlcyBjZXJjYW5vcyBlbnRyZSBzw60gcmVnaXN0cmFyb24gdGVtcGVyYXR1cmFzIG3DoXMgcGFyZWNpZGFzIHF1ZSBsb3MgZGlzdGFudGVzLiBMb3MgcHVudG9zIGRlbCBzZW1pdmFyaW9ncmFtYSBxdWUgc3VwZXJhbiBlbCBlbnZvbHZlbnRlIHN1cGVyaW9yIGRlIE1vbnRlIENhcmxvIGVuIGxhcyBkaXN0YW5jaWFzIGNvcnRhcyBpbmRpY2FuIHF1ZSBlc3RhIGF1dG9jb3JyZWxhY2nDs24gbm8gZXMgcHJvZHVjdG8gZGVsIGF6YXIuIExhIG1lc2V0YSB2aXN1YWwgZGVsIHNlbWl2YXJpb2dyYW1hIHNlIGFsY2FuemEgYXByb3hpbWFkYW1lbnRlIGEgbGEgbWl0YWQgZGVsIHJhbmdvIGFuYWxpemFkbywgbG8gcXVlIGltcGxpY2EgcXVlIG3DoXMgYWxsw6EgZGUgZXNhIGRpc3RhbmNpYSBsb3Mgw6FyYm9sZXMgeWEgbm8gc2UgaW5mbHV5ZW4gdMOpcm1pY2FtZW50ZSBlbnRyZSBzw60uIFVuYSBsaW1pdGFjacOzbiBpbXBvcnRhbnRlIGVzIHF1ZSBlbCBzZW1pdmFyaW9ncmFtYSBzZSBjYWxjdWzDsyBlbiBtb2RvIG9tbmlkaXJlY2Npb25hbDsgc2kgbGEgYXV0b2NvcnJlbGFjacOzbiBmdWVyYSBhbmlzb3Ryw7NwaWNhIChkaWZlcmVudGUgc2Vnw7puIGxhIGRpcmVjY2nDs24pLCBlc3RlIGFuw6FsaXNpcyBsYSBzdWJlc3RpbWFyw61hLgoKIyAqKkFqdXN0ZSBkZSBNb2RlbG9zKioKCkVuIGVzdGEgc2VjY2nDs24gc2UgYWp1c3RhbiB0cmVzIG1vZGVsb3MgdGXDs3JpY29zIGFsIHNlbWl2YXJpb2dyYW1hIGVtcMOtcmljbyB5IHNlIHNlbGVjY2lvbmEgZWwgZGUgbWVub3IgU0NFLgoKYGBge3IgYWp1c3RlLW1vZGVsb3N9CiMgRWwgYXJndW1lbnRvIGNvcnJlY3RvIGRlIHZhcmlvZml0KCkgZXMgaW5pLmNvdi5wYXJzIChOTyBpbmkudmFscykuCiMgaW5pLmNvdi5wYXJzIGFjZXB0YTogdmVjdG9yIGMoc2lnbWFzcSwgcGhpKSwgbyBtYXRyaXovZGF0YS5mcmFtZSBjb24KIyAyIGNvbHVtbmFzIGRvbmRlIGNvbDE9c2lnbWFzcSB5IGNvbDI9cGhpLgojIGV4cGFuZC5ncmlkKCkgcHJvZHVjZSBkYXRhLmZyYW1lLCBxdWUgdmFyaW9maXQgY29udmllcnRlIGludGVybmFtZW50ZSBhIG1hdHJpeC4Kc2lnbWFzcV9zZXEgPC0gc2VxKHZhcihnZW9fdGVtcCRkYXRhKSAqIDAuMywgdmFyKGdlb190ZW1wJGRhdGEpICogMiwgbGVuZ3RoLm91dCA9IDgpCnBoaV9zZXEgICAgIDwtIHNlcShkaXN0XzUwICogMC4xLCBkaXN0XzUwICogMC44LCBsZW5ndGgub3V0ID0gOCkKaW5pX2dyaWQgICAgPC0gZXhwYW5kLmdyaWQoc2lnbWFzcV9zZXEsIHBoaV9zZXEpICAgIyBkYXRhLmZyYW1lOiBjb2wxPXNpZ21hc3EsIGNvbDI9cGhpCgptb2RlbF9leHAgIDwtIHZhcmlvZml0KHZhcmlvZ3JhbWEsCiAgICAgICAgICAgICAgICAgICAgICAgaW5pLmNvdi5wYXJzID0gaW5pX2dyaWQsCiAgICAgICAgICAgICAgICAgICAgICAgY292Lm1vZGVsICAgID0gImV4cG9uZW50aWFsIiwKICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRzICAgICAgPSAibnBhaXJzIikKY2F0KCJFeHBvbmVudGlhbCBTQ0U6Iiwgcm91bmQobW9kZWxfZXhwJHZhbHVlLCA0KSwgIlxuIikKCm1vZGVsX2dhdXMgPC0gdmFyaW9maXQodmFyaW9ncmFtYSwKICAgICAgICAgICAgICAgICAgICAgICBpbmkuY292LnBhcnMgPSBpbmlfZ3JpZCwKICAgICAgICAgICAgICAgICAgICAgICBjb3YubW9kZWwgICAgPSAiZ2F1c3NpYW4iLAogICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgICAgICA9ICJucGFpcnMiKQpjYXQoIkdhdXNzaWFuICAgIFNDRToiLCByb3VuZChtb2RlbF9nYXVzJHZhbHVlLCA0KSwgIlxuIikKCm1vZGVsX3NwZSAgPC0gdmFyaW9maXQodmFyaW9ncmFtYSwKICAgICAgICAgICAgICAgICAgICAgICBpbmkuY292LnBhcnMgPSBpbmlfZ3JpZCwKICAgICAgICAgICAgICAgICAgICAgICBjb3YubW9kZWwgICAgPSAic3BoZXJpY2FsIiwKICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRzICAgICAgPSAibnBhaXJzIikKY2F0KCJTcGhlcmljYWwgICBTQ0U6Iiwgcm91bmQobW9kZWxfc3BlJHZhbHVlLCA0KSwgIlxuIikKYGBgCgpgYGB7ciB0YWJsYS1jb21wYXJhY2lvbi1tb2RlbG9zfQp0YWJsYV9tb2RlbG9zIDwtIGRhdGEuZnJhbWUoCiAgTW9kZWxvICAgID0gYygiRXhwb25lbnRpYWwiLCJHYXVzc2lhbiIsIlNwaGVyaWNhbCIpLAogIFNDRSAgICAgICA9IHJvdW5kKGMobW9kZWxfZXhwJHZhbHVlLCAgICAgICBtb2RlbF9nYXVzJHZhbHVlLCAgICAgICBtb2RlbF9zcGUkdmFsdWUpLCAgMiksCiAgTnVnZ2V0ICAgID0gcm91bmQoYyhtb2RlbF9leHAkbnVnZ2V0LCAgICAgICBtb2RlbF9nYXVzJG51Z2dldCwgICAgICBtb2RlbF9zcGUkbnVnZ2V0KSwgNCksCiAgTWVzZXRhICAgID0gcm91bmQoYyhtb2RlbF9leHAkY292LnBhcnNbMV0sICBtb2RlbF9nYXVzJGNvdi5wYXJzWzFdLCBtb2RlbF9zcGUkY292LnBhcnNbMV0pLCA0KSwKICBSYW5nb19waGkgPSByb3VuZChjKG1vZGVsX2V4cCRjb3YucGFyc1syXSwgIG1vZGVsX2dhdXMkY292LnBhcnNbMl0sIG1vZGVsX3NwZSRjb3YucGFyc1syXSksIDYpCikKCmthYmxlKHRhYmxhX21vZGVsb3MsCiAgICAgIGNhcHRpb24gICA9ICJUYWJsYSAzLiBDb21wYXJhY2nDs24gZGUgbW9kZWxvcyB0ZcOzcmljb3MgZGUgc2VtaXZhcmlvZ3JhbWEgc2Vnw7puIFN1bWEgZGUgQ3VhZHJhZG9zIGRlbCBFcnJvciAoU0NFKSIsCiAgICAgIGNvbC5uYW1lcyA9IGMoIk1vZGVsbyIsIlNDRSIsIlBlcGl0YSIsIk1lc2V0YSBwYXJjaWFsIiwiUmFuZ28gKHBoaSkiKSwKICAgICAgYWxpZ24gICAgID0gYygibCIsInIiLCJyIiwiciIsInIiKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpICU+JQogIHJvd19zcGVjKHdoaWNoLm1pbih0YWJsYV9tb2RlbG9zJFNDRSksIGJvbGQgPSBUUlVFLCBiYWNrZ3JvdW5kID0gIiNlOGY1ZTkiKQpgYGAKCmBgYHtyIHBsb3QtYWp1c3RlLW1vZGVsb3MsIGZpZy5jYXA9IkZpZ3VyYSAxMC4gU2VtaXZhcmlvZ3JhbWEgZW1ww61yaWNvIGNvbiBsb3MgdHJlcyBtb2RlbG9zIHRlw7NyaWNvcyBhanVzdGFkb3M6IGV4cG9uZW5jaWFsLCBnYXVzc2lhbm8geSBlc2bDqXJpY28uIn0KcGxvdCh2YXJpb2dyYW1hLAogICAgIHhsYWIgPSAiRGlzdGFuY2lhIChncmFkb3MpIiwgeWxhYiA9ICJTZW1pdmFyaWFuemEiLAogICAgIHBjaCA9IDE2LCBjb2wgPSAiZ3JleTMwIikKbGluZXMobW9kZWxfZXhwLCAgY29sID0gcGFsZXRhWzFdLCBsd2QgPSAyKQpsaW5lcyhtb2RlbF9nYXVzLCBjb2wgPSBwYWxldGFbMl0sIGx3ZCA9IDIpCmxpbmVzKG1vZGVsX3NwZSwgIGNvbCA9IHBhbGV0YVszXSwgbHdkID0gMikKbGVnZW5kKCJib3R0b21yaWdodCIsCiAgICAgICBsZWdlbmQgPSBjKCJFeHBvbmVudGlhbCIsIkdhdXNzaWFuIiwiU3BoZXJpY2FsIiksCiAgICAgICBjb2wgPSBwYWxldGFbMTozXSwgbHdkID0gMiwgYnR5ID0gIm4iKQpgYGAKCmBgYHtyIHNlbGVjY2lvbmFyLW1lam9yLW1vZGVsb30Kc2NlX3ZlYyAgICA8LSBjKG1vZGVsX2V4cCR2YWx1ZSwgbW9kZWxfZ2F1cyR2YWx1ZSwgbW9kZWxfc3BlJHZhbHVlKQpub21fZ2VvUiAgIDwtIGMoImV4cG9uZW50aWFsIiwiZ2F1c3NpYW4iLCJzcGhlcmljYWwiKQpub21fZGlzcCAgIDwtIGMoIkV4cG9uZW50aWFsIiwiR2F1c3NpYW4iLCJTcGhlcmljYWwiKQptZWpvcl9pZHggIDwtIHdoaWNoLm1pbihzY2VfdmVjKQoKbWVqb3JfbW9kZWxvX25vbWJyZSA8LSBub21fZ2VvUlttZWpvcl9pZHhdCm1lam9yX21vZGVsbyAgICAgICAgPC0gbGlzdChtb2RlbF9leHAsIG1vZGVsX2dhdXMsIG1vZGVsX3NwZSlbW21lam9yX2lkeF1dCgpjYXQoIk1vZGVsbyBzZWxlY2Npb25hZG86Iiwgbm9tX2Rpc3BbbWVqb3JfaWR4XSwgIlxuIikKY2F0KCJTQ0UgIDoiLCByb3VuZChtZWpvcl9tb2RlbG8kdmFsdWUsICAgICAgIDQpLCAiXG4iKQpjYXQoIk51Z2dldDoiLCByb3VuZChtZWpvcl9tb2RlbG8kbnVnZ2V0LCAgICAgIDQpLCAiXG4iKQpjYXQoIlNpZ21hMjoiLCByb3VuZChtZWpvcl9tb2RlbG8kY292LnBhcnNbMV0sIDQpLCAiXG4iKQpjYXQoIlBoaSAgIDoiLCByb3VuZChtZWpvcl9tb2RlbG8kY292LnBhcnNbMl0sIDYpLCAiXG4iKQojIFZhcmlhYmxlcyBpbmxpbmUgZGVsIG1vZGVsbyBzZWxlY2Npb25hZG8KcmVzX21vZGVsb19ub21icmUgPC0gbm9tX2Rpc3BbbWVqb3JfaWR4XQpyZXNfc2NlICAgICAgICAgICA8LSByb3VuZChtZWpvcl9tb2RlbG8kdmFsdWUsIDIpCnJlc19udWdnZXQgICAgICAgIDwtIHJvdW5kKG1lam9yX21vZGVsbyRudWdnZXQsIDMpCnJlc19zaWdtYTIgICAgICAgIDwtIHJvdW5kKG1lam9yX21vZGVsbyRjb3YucGFyc1sxXSwgMykKcmVzX3BoaV9ncmFkb3MgICAgPC0gbWVqb3JfbW9kZWxvJGNvdi5wYXJzWzJdCnJlc19waGlfbWV0cm9zICAgIDwtIHJvdW5kKG1lam9yX21vZGVsbyRjb3YucGFyc1syXSAqIDExMTAwMCwgMCkKYGBgCgoqKkludGVycHJldGFjacOzbjoqKiBEZSBsb3MgdHJlcyBtb2RlbG9zIGFqdXN0YWRvcywgZWwgYHIgcmVzX21vZGVsb19ub21icmVgIG9idHV2byBlbCBtZW5vciBTQ0UgKGByIGZvcm1hdEMocmVzX3NjZSwgZm9ybWF0PSdmJywgZGlnaXRzPTIpYCksIGZyZW50ZSBhIGByIGZvcm1hdEMobWF4KHNjZV92ZWMpLCBmb3JtYXQ9J2YnLCBkaWdpdHM9MilgIGRlbCBwZW9yIGFqdXN0ZSwgbG8gcXVlIGluZGljYSB1bmEgZGlmZXJlbmNpYSBzdXN0YW5jaWFsIGVuIGxhIGNhcGFjaWRhZCBkZSBjYWRhIG1vZGVsbyBwYXJhIHJlcHJvZHVjaXIgZWwgc2VtaXZhcmlvZ3JhbWEgZW1ww61yaWNvLiBFbCByYW5nbyBlZmVjdGl2byBlc3RpbWFkbyBkZSBgciBmb3JtYXRDKHJlc19waGlfZ3JhZG9zLCBmb3JtYXQ9J2YnLCBkaWdpdHM9NilgIGdyYWRvcyBlcXVpdmFsZSBhcHJveGltYWRhbWVudGUgYSBgciByZXNfcGhpX21ldHJvc2AgbWV0cm9zLCBxdWUgZXMgbGEgZGlzdGFuY2lhIG3DoXhpbWEgYSBsYSBxdWUgZG9zIMOhcmJvbGVzIGVuIGVzdGEgZmluY2EgcHJlc2VudGFuIHRlbXBlcmF0dXJhcyBjb3JyZWxhY2lvbmFkYXMuIExhIG1lc2V0YSBwYXJjaWFsIGRlIGByIGZvcm1hdEMocmVzX3NpZ21hMiwgZm9ybWF0PSdmJywgZGlnaXRzPTMpYCDCsEPCsiByZXByZXNlbnRhIGxhIHZhcmlhbnphIGV4cGxpY2FkYSBwb3IgbGEgZXN0cnVjdHVyYSBlc3BhY2lhbCwgbWllbnRyYXMgcXVlIGxhIHBlcGl0YSBkZSBgciBmb3JtYXRDKHJlc19udWdnZXQsIGZvcm1hdD0nZicsIGRpZ2l0cz0zKWAgwrBDwrIgcmVmbGVqYSBsYSB2YXJpYWJpbGlkYWQgYSBlc2NhbGEgbWVub3IgcXVlIGxhIHNlcGFyYWNpw7NuIG3DrW5pbWEgZW50cmUgc2Vuc29yZXMgbyBlbCBlcnJvciBkZSBtZWRpY2nDs24gcHJvcGlvIGRlbCBpbnN0cnVtZW50byBwb3J0w6F0aWwuIFVuYSBwZXBpdGEgZWxldmFkYSBlbiByZWxhY2nDs24gYSBsYSBtZXNldGEgc2Vyw61hIGluZGljYXRpdmEgZGUgcXVlIGVsIHNlbnNvciBpbnRyb2R1Y2UgcnVpZG8gY29uc2lkZXJhYmxlLgoKIyAqKkludGVycG9sYWNpw7NuIGNvbiBLcmlnaW5nKioKCkVuIGVzdGEgc2VjY2nDs24gc2UgcHJlZGljZSBsYSB0ZW1wZXJhdHVyYSBzb2JyZSB1bmEgZ3JpbGxhIGRlIDEwMCB4IDEwMCBwdW50b3MgbWVkaWFudGUgS3JpZ2luZyBPcmRpbmFyaW8uCgpgYGB7ciBjcmVhci1ncmlsbGEta3JpZ2luZywgZmlnLmNhcD0iRmlndXJhIDExLiBHcmlsbGEgZGUgcHJlZGljY2nDs24gZGUgMTAwIHggMTAwIHB1bnRvcyAoZ3Jpcykgc3VwZXJwdWVzdGEgc29icmUgbG9zIHB1bnRvcyBkZSBtdWVzdHJlbyAodmVyZGUpLiJ9Cmxvbl9taW4gPC0gbWluKGdlb190ZW1wJGNvb3Jkc1ssImxvbiJdKQpsb25fbWF4IDwtIG1heChnZW9fdGVtcCRjb29yZHNbLCJsb24iXSkKbGF0X21pbiA8LSBtaW4oZ2VvX3RlbXAkY29vcmRzWywibGF0Il0pCmxhdF9tYXggPC0gbWF4KGdlb190ZW1wJGNvb3Jkc1ssImxhdCJdKQoKY2F0KCJMb25naXR1ZDoiLCByb3VuZChsb25fbWluLDYpLCAiYSIsIHJvdW5kKGxvbl9tYXgsNiksICJcbiIpCmNhdCgiTGF0aXR1ZCA6Iiwgcm91bmQobGF0X21pbiw2KSwgImEiLCByb3VuZChsYXRfbWF4LDYpLCAiXG4iKQoKIyBDb2x1bW5hcyB4IGUgeSByZXF1ZXJpZGFzIHBvciByYXN0ZXJGcm9tWFlaCmdlb2RhdG9zX2dyaWQgPC0gZXhwYW5kLmdyaWQoCiAgeCA9IHNlcShsb25fbWluLCBsb25fbWF4LCBsZW5ndGgub3V0ID0gMTAwKSwKICB5ID0gc2VxKGxhdF9taW4sIGxhdF9tYXgsIGxlbmd0aC5vdXQgPSAxMDApCikKCnBsb3QoZ2VvZGF0b3NfZ3JpZCwgcGNoID0gMSwgY2V4ID0gMC4yLCBjb2wgPSAiZ3JleTcwIiwKICAgICB4bGFiID0gIkxvbmdpdHVkIiwgeWxhYiA9ICJMYXRpdHVkIikKcG9pbnRzKGdlb190ZW1wJGNvb3JkcywgY29sID0gcGFsZXRhWzJdLCBwY2ggPSAxNiwgY2V4ID0gMC41KQpgYGAKCmBgYHtyIGVqZWN1dGFyLWtyaWdpbmd9Cmdlb2RhdG9zX2tvIDwtIGtyaWdlLmNvbnYoCiAgZ2VvX3RlbXAsCiAgbG9jICAgPSBnZW9kYXRvc19ncmlkLAogIGtyaWdlID0ga3JpZ2UuY29udHJvbCgKICAgIG51Z2dldCAgICA9IG1lam9yX21vZGVsbyRudWdnZXQsCiAgICB0cmVuZC5kICAgPSAiY3RlIiwKICAgIHRyZW5kLmwgICA9ICJjdGUiLAogICAgY292LnBhcnMgID0gbWVqb3JfbW9kZWxvJGNvdi5wYXJzLAogICAgY292Lm1vZGVsID0gbWVqb3JfbW9kZWxvX25vbWJyZQogICkKKQoKY2F0KCJSYW5nbyBwcmVkaWNjaW9uZXM6Iiwgcm91bmQocmFuZ2UoZ2VvZGF0b3Nfa28kcHJlZGljdCksICAgMyksICLCsENcbiIpCmNhdCgiUmFuZ28gdmFyaWFuemEgS08gOiIsIHJvdW5kKHJhbmdlKGdlb2RhdG9zX2tvJGtyaWdlLnZhciksIDYpLCAiXG4iKQojIFZhcmlhYmxlcyBpbmxpbmUgZGVsIEtyaWdpbmcKcmVzX3ByZWRfbWluICAgICA8LSByb3VuZChtaW4oZ2VvZGF0b3Nfa28kcHJlZGljdCksIDEpCnJlc19wcmVkX21heCAgICAgPC0gcm91bmQobWF4KGdlb2RhdG9zX2tvJHByZWRpY3QpLCAxKQpyZXNfc2RfZXJyb3JfbWF4IDwtIHJvdW5kKG1heChzcXJ0KGdlb2RhdG9zX2tvJGtyaWdlLnZhcikpLCAyKQpyZXNfc2RfZXJyb3JfbWluIDwtIHJvdW5kKG1pbihzcXJ0KGdlb2RhdG9zX2tvJGtyaWdlLnZhcikpLCAzKQpyZXNfc2RfdW1icmFsICAgIDwtIHJvdW5kKG1heChzcXJ0KGdlb2RhdG9zX2tvJGtyaWdlLnZhcikpICogMC43LCAyKQpgYGAKCmBgYHtyIHBsb3Qta3JpZ2luZy1wcmVkaWNjaW9uLCBmaWcuY2FwPSJGaWd1cmEgMTIuIE1hcGEgZGUgcHJlZGljY2nDs24gZXNwYWNpYWwgZGUgbGEgdGVtcGVyYXR1cmEgKMKwQykgbWVkaWFudGUgS3JpZ2luZyBPcmRpbmFyaW8gY29uIGlzb2zDrW5lYXMgZGUgY29udG9ybm8uIn0KcGFyKG1hciA9IGMoNCw0LDMsMikpCmltYWdlKGdlb2RhdG9zX2tvLAogICAgICB4bGFiID0gIkxvbmdpdHVkIiwgeWxhYiA9ICJMYXRpdHVkIiwKICAgICAgY29sICA9IGhlYXQuY29sb3JzKDEwMCwgcmV2ID0gVFJVRSkpCmNvbnRvdXIoZ2VvZGF0b3Nfa28sIGFkZCA9IFRSVUUsIGRyYXdsYWJlbHMgPSBUUlVFLCBjb2wgPSAiZ3JleTIwIiwgbHdkID0gMC44KQpwb2ludHMoZ2VvX3RlbXAkY29vcmRzLCBwY2ggPSAxNiwgY2V4ID0gMC40LCBjb2wgPSAiYmxhY2siKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIEVsIG1hcGEgZGUgcHJlZGljY2nDs24gbXVlc3RyYSBxdWUgbGEgdGVtcGVyYXR1cmEgZXN0aW1hZGEgdmFyw61hIGVudHJlIGByIHJlc19wcmVkX21pbmAgwrBDIHkgYHIgcmVzX3ByZWRfbWF4YCDCsEMgZGVudHJvIGRlIGxvcyBsw61taXRlcyBkZSBsYSBmaW5jYS4gTGFzIGlzb3Rlcm1hcyByZXZlbGFuIHVuIGdyYWRpZW50ZSBlc3BhY2lhbCBjb24gZWwgc2VjdG9yIGRlIG1heW9yZXMgdGVtcGVyYXR1cmFzIHViaWNhZG8gZW4gbGEgem9uYSBkZSBtYXlvciBjb25jZW50cmFjacOzbiBkZSDDoXJib2xlcyBkZWwgUTQgaWRlbnRpZmljYWRvcyBwcmV2aWFtZW50ZS4gRGVzZGUgZWwgcHVudG8gZGUgdmlzdGEgYWdyb27Ds21pY28sIGxhcyB6b25hcyBxdWUgc3VwZXJhbiBsb3MgMjcgwrBDIGRlIGZvcm1hIGNvbnNpc3RlbnRlIG1lcmVjZW4gYXRlbmNpw7NuIGVzcGVjaWFsIGVuIGN1YW50byBhIHJpZWdvIHkgc29tYnLDrW8sIHlhIHF1ZSBlbCBhZ3VhY2F0ZSBIYXNzIHByZXNlbnRhIGVzdHLDqXMgY2Fsw7NyaWNvIHBvciBlbmNpbWEgZGUgZXNlIHVtYnJhbCBkdXJhbnRlIHBlcsOtb2RvcyBzb3N0ZW5pZG9zLiBFcyBpbXBvcnRhbnRlIHJlY29yZGFyIHF1ZSBlc3RhcyBwcmVkaWNjaW9uZXMgY29ycmVzcG9uZGVuIGEgdW4gw7puaWNvIGTDrWE7IHBhcmEgcmVjb21lbmRhY2lvbmVzIGRlIG1hbmVqbyBwZXJtYW5lbnRlIHNlIHJlcXVlcmlyw61hIHVuIGFuw6FsaXNpcyBtdWx0aXRlbXBvcmFsLgoKYGBge3IgcGxvdC1rcmlnaW5nLWVycm9yLCBmaWcuY2FwPSJGaWd1cmEgMTMuIE1hcGEgZGUgZGVzdmlhY2nDs24gZXN0w6FuZGFyIGRlbCBlcnJvciBkZSBwcmVkaWNjacOzbiBLcmlnaW5nLiBMYXMgem9uYXMgZGUgbWF5b3IgZXJyb3IgY29pbmNpZGVuIGNvbiBtZW5vciBkZW5zaWRhZCBkZSBtdWVzdHJlby4ifQojIEdyYWZpY2FyIGVycm9yIGVzdMOhbmRhciBjb24gZ2dwbG90MiAoaW1hZ2Uua3JpZ2luZyBubyBhY2VwdGEgdmFsPSkKZGZfZXJyb3IgPC0gZGF0YS5mcmFtZSgKICB4ICAgID0gZ2VvZGF0b3NfZ3JpZCR4LAogIHkgICAgPSBnZW9kYXRvc19ncmlkJHksCiAgc2RLTyA9IHNxcnQoZ2VvZGF0b3Nfa28ka3JpZ2UudmFyKQopCmRmX3B0cyA8LSBkYXRhLmZyYW1lKAogIHggPSBnZW9fdGVtcCRjb29yZHNbLCJsb24iXSwKICB5ID0gZ2VvX3RlbXAkY29vcmRzWywibGF0Il0KKQoKZ2dwbG90KGRmX2Vycm9yLCBhZXMoeCA9IHgsIHkgPSB5LCBmaWxsID0gc2RLTykpICsKICBnZW9tX3Jhc3RlcihpbnRlcnBvbGF0ZSA9IFRSVUUpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gcmV2KGhlYXQuY29sb3JzKDEwMCkpLCBuYW1lID0gIlNEIGVycm9yICjCsEMpIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRmX3B0cywgYWVzKHggPSB4LCB5ID0geSksCiAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFLCBwY2ggPSAxNiwgc2l6ZSA9IDAuNCwgY29sID0gImJsYWNrIikgKwogIGxhYnMoeCA9ICJMb25naXR1ZCIsIHkgPSAiTGF0aXR1ZCIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEyKSArIGNvb3JkX2ZpeGVkKCkKYGBgCgoqKkludGVycHJldGFjacOzbjoqKiBMYSBkZXN2aWFjacOzbiBlc3TDoW5kYXIgZGVsIGVycm9yIGRlIHByZWRpY2Npw7NuIG9zY2lsYSBlbnRyZSBgciByZXNfc2RfZXJyb3JfbWluYCDCsEMgeSBgciByZXNfc2RfZXJyb3JfbWF4YCDCsEMuIExvcyB2YWxvcmVzIG3DoXMgYmFqb3MgY29pbmNpZGVuIGNvbiBlbCBpbnRlcmlvciBkZWwgbG90ZSBkb25kZSBsYSBkZW5zaWRhZCBkZSDDoXJib2xlcyBtb25pdG9yZWFkb3MgZXMgbWF5b3IsIG1pZW50cmFzIHF1ZSBsb3MgYm9yZGVzIHkgZXNxdWluYXMgZGUgbGEgZmluY2EgcHJlc2VudGFuIGxvcyBlcnJvcmVzIG3DoXMgYWx0b3MsIGNvbW8gZXMgZXNwZXJhYmxlIGVuIEtyaWdpbmcgT3JkaW5hcmlvIGN1YW5kbyBzZSBleHRyYXBvbGEgbcOhcyBhbGzDoSBkZSBsYSBudWJlIGRlIGRhdG9zLiBFbiB0w6lybWlub3MgcHLDoWN0aWNvcywgbGFzIHByZWRpY2Npb25lcyBlbiBlbCBib3JkZSBvY2NpZGVudGFsIHRpZW5lbiB1bmEgaW5jZXJ0aWR1bWJyZSBjZXJjYSBkZWwgZG9ibGUgcXVlIGxhcyBkZWwgY2VudHJvIGRlbCBsb3RlLCBsbyBxdWUgZGViZXLDrWEgdGVuZXJzZSBlbiBjdWVudGEgYW50ZXMgZGUgdG9tYXIgZGVjaXNpb25lcyBkZSBtYW5lam8gZGlmZXJlbmNpYWwgYmFzYWRhcyBlbiBlc2FzIHpvbmFzIHBlcmlmw6lyaWNhcy4KCiMgKipQcmVkaWNjacOzbiBlbiBSQVNURVJTKioKCkVuIGVzdGEgc2VjY2nDs24gc2UgdHJhbnNmb3JtYSBsYSBwcmVkaWNjacOzbiBlbiByYXN0ZXJzIHBhcmEgdmlzdWFsaXphY2nDs24gYXZhbnphZGEgZSBpbnRlZ3JhY2nDs24gY29uIHBsYXRhZm9ybWFzIFNJRy4KCmBgYHtyIGNyZWFyLXJhc3Rlci1wcmVkaWNjaW9ufQpyZXF1aXJlKHJhc3RlclZpcykKCnRlbXBfcHJlZGljdCA8LSByYXN0ZXJGcm9tWFlaKAogIGNiaW5kKGdlb2RhdG9zX2dyaWQsIHogPSBnZW9kYXRvc19rbyRwcmVkaWN0KQopCnRlbXBfZXJyb3JfciA8LSByYXN0ZXJGcm9tWFlaKAogIGNiaW5kKGdlb2RhdG9zX2dyaWQsIHogPSBzcXJ0KGdlb2RhdG9zX2tvJGtyaWdlLnZhcikpCikKCnN1cHByZXNzV2FybmluZ3MoewogIHByb2o0c3RyaW5nKHRlbXBfcHJlZGljdCkgIDwtIENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiKQogIHByb2o0c3RyaW5nKHRlbXBfZXJyb3JfcikgIDwtIENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiKQp9KQoKY2F0KCJSYXN0ZXIgcHJlZGljY2nDs24gY3JlYWRvLlxuIikKcHJpbnQodGVtcF9wcmVkaWN0KQpgYGAKCmBgYHtyIHBsb3QtcmFzdGVyLXByZWRpY2Npb24sIGZpZy5jYXA9IkZpZ3VyYSAxNC4gUmFzdGVyIGRlIHRlbXBlcmF0dXJhIHByZWRpY2hhIG1lZGlhbnRlIEtyaWdpbmcgT3JkaW5hcmlvLCB2aXN1YWxpemFkbyBjb24gbGEgcGFsZXRhIEJ1UmQuIn0KbGV2ZWxwbG90KHRlbXBfcHJlZGljdCwKICAgICAgICAgIHBhci5zZXR0aW5ncyA9IEJ1UmRUaGVtZSwKICAgICAgICAgIHhsYWIgPSAiTG9uZ2l0dWQiLCB5bGFiID0gIkxhdGl0dWQiKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIExhIHJlcHJlc2VudGFjacOzbiByYXN0ZXIsIGNvbiByZXNvbHVjacOzbiBkZSBjZWxkYSBkZXRlcm1pbmFkYSBwb3IgbGEgZ3JpbGxhIGRlIDEwMCB4IDEwMCBwdW50b3Mgc29icmUgbGEgZXh0ZW5zacOzbiBkZSBsYSBmaW5jYSwgcGVybWl0ZSBhcHJlY2lhciBjb24gbWF5b3Igc3VhdmlkYWQgbG9zIGdyYWRpZW50ZXMgdMOpcm1pY29zIHF1ZSBlbCBtYXBhIGRlIGlzb2zDrW5lYXMuIExhIHBhbGV0YSBCdVJkLCBxdWUgdmEgZGUgYXp1bCAoZnLDrW8pIGEgcm9qbyAoY8OhbGlkbyksIGhhY2UgZXZpZGVudGUgcXVlIGVsIG7DumNsZW8gbcOhcyBjw6FsaWRvIGRlIGxhIGZpbmNhIG5vIGNvaW5jaWRlIGNvbiBlbCBjZW50cm9pZGUgZGVsIGxvdGUgc2lubyBxdWUgZXN0w6EgZGVzcGxhemFkbyBoYWNpYSB1biBzZWN0b3IgZXNwZWPDrWZpY28uIEVzdGUgcmFzdGVyIHB1ZWRlIGludGVncmFyc2UgZGlyZWN0YW1lbnRlIGVuIFFHSVMgcGFyYSBjcnV6YXJsbyBjb24gY2FwYXMgZGUgcGVuZGllbnRlLCBvcmllbnRhY2nDs24gbyB1c28gZGVsIHN1ZWxvIHkgYXPDrSBwcm9mdW5kaXphciBlbiBsYXMgY2F1c2FzIGRlIGxhIGhldGVyb2dlbmVpZGFkIHTDqXJtaWNhIG9ic2VydmFkYS4KCmBgYHtyIHBsb3QtcmFzdGVyLWVycm9yLCBmaWcuY2FwPSJGaWd1cmEgMTUuIFJhc3RlciBkZSBkZXN2aWFjacOzbiBlc3TDoW5kYXIgZGVsIGVycm9yIGRlIHByZWRpY2Npw7NuIEtyaWdpbmcuIn0KbGV2ZWxwbG90KHRlbXBfZXJyb3JfciwKICAgICAgICAgIHBhci5zZXR0aW5ncyA9IEJ1UmRUaGVtZSwKICAgICAgICAgIHhsYWIgPSAiTG9uZ2l0dWQiLCB5bGFiID0gIkxhdGl0dWQiKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIEVsIHJhc3RlciBkZSBlcnJvciBlc3TDoW5kYXIgbXVlc3RyYSBxdWUgbGEgaW5jZXJ0aWR1bWJyZSBubyBlcyBob21vZ8OpbmVhIGVuIGVsIGxvdGU6IGNyZWNlIG5vdG9yaWFtZW50ZSBlbiBsb3MgYm9yZGVzIHkgZW4gcGFydGljdWxhciBlbiBsYSBlc3F1aW5hIGNvbiBtZW5vciBkZW5zaWRhZCBkZSBwdW50b3MuIENvbiBiYXNlIGVuIGVzdGUgbWFwYSwgc2kgc2UgcXVpc2llcmEgcmVkdWNpciBlbCBlcnJvciBtw6F4aW1vIGEgbWVub3MgZGUgYHIgcmVzX3NkX3VtYnJhbGAgwrBDLCBzZXLDrWEgc3VmaWNpZW50ZSBjb24gaW5zdGFsYXIgc2Vuc29yZXMgYWRpY2lvbmFsZXMgZW4gbGFzIGRvcyBvIHRyZXMgem9uYXMgcGVyaWbDqXJpY2FzIGRlIG1heW9yIGVycm9yLCBzaW4gbmVjZXNpZGFkIGRlIGF1bWVudGFyIGxhIGRlbnNpZGFkIGRlIG1vbml0b3JlbyBlbiBlbCBjZW50cm8gZGVsIGxvdGUgZG9uZGUgZWwgbW9kZWxvIHlhIGVzIHByZWNpc28uCgpgYGB7ciBtYXBhLWxlYWZsZXQtcmFzdGVyLCBmaWcuY2FwPSJGaWd1cmEgMTYuIE1hcGEgaW50ZXJhY3Rpdm8gY29uIGVsIHJhc3RlciBkZSB0ZW1wZXJhdHVyYSBwcmVkaWNoYSBzdXBlcnB1ZXN0byBzb2JyZSBsYSBjYXJ0b2dyYWbDrWEgYmFzZS4ifQpsZWFmbGV0KCkgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRSYXN0ZXJJbWFnZSh0ZW1wX3ByZWRpY3QsIG9wYWNpdHkgPSAwLjY1KSAlPiUKICBhZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLAogICAgICAgICAgICBjb2xvcnMgICA9IGMoIiMyQzdCQjYiLCIjRkRBRTYxIiwiI0Q3MTkxQyIpLAogICAgICAgICAgICBsYWJlbHMgICA9IGMoIkJhamEiLCJNZWRpYSIsIkFsdGEiKSwKICAgICAgICAgICAgb3BhY2l0eSAgPSAwLjgpCmBgYAoKKipJbnRlcnByZXRhY2nDs246KiogQWwgc3VwZXJwb25lciBlbCByYXN0ZXIgZGUgcHJlZGljY2nDs24gc29icmUgbGEgY2FydG9ncmFmw61hIGJhc2Ugc2UgcHVlZGUgY29uZmlybWFyIHF1ZSBsYSBmaW5jYSBzZSB1YmljYSBlbiB6b25hIHJ1cmFsIGRlIFBvcGF5w6FuLCByb2RlYWRhIGRlIG90cm9zIGN1bHRpdm9zIHkgY2FtaW5vcyBxdWUgcG9kcsOtYW4gaW5mbHVpciBlbiBsYSBkaW7DoW1pY2EgZGUgdGVtcGVyYXR1cmEgYSB0cmF2w6lzIGRlIHZhcmlhY2lvbmVzIGVuIGxhIGNvYmVydHVyYSBkZWwgc3VlbG8geSBlbCBmbHVqbyBkZSBhaXJlLiBFbCBzZWN0b3IgbcOhcyBjw6FsaWRvIHByZWRpY2hvIHBvciBlbCBtb2RlbG8gY29pbmNpZGUgdmlzdWFsbWVudGUgY29uIGxhIHBhcnRlIGRlbCBsb3RlIGNvbiBtZW5vciBwcm90ZWNjacOzbiBuYXR1cmFsIGRlIGNvbGluZGFudGVzLCBsbyBxdWUgZXMgY29uc2lzdGVudGUgY29uIHVuYSBtYXlvciBleHBvc2ljacOzbiBhIGxhIHJhZGlhY2nDs24gZGlyZWN0YS4KCiMgKipWYWxpZGFjacOzbiBDcnV6YWRhKioKCkVuIGVzdGEgc2VjY2nDs24gc2UgZXZhbMO6YSBsYSBjYWxpZGFkIHByZWRpY3RpdmEgZGVsIG1vZGVsbyBtZWRpYW50ZSB2YWxpZGFjacOzbiBjcnV6YWRhIGxlYXZlLW9uZS1vdXQgY29uIGB4dmFsaWRgIGRlIGBnZW9SYC4KCmBgYHtyIHZhbGlkYWNpb24tY3J1emFkYX0KdmFsaWRhIDwtIHh2YWxpZChnZW9kYXRhID0gZ2VvX3RlbXAsIG1vZGVsID0gbWVqb3JfbW9kZWxvKQpjYXQoIkNvbHVtbmFzIHh2YWxpZDoiLCBwYXN0ZShuYW1lcyh2YWxpZGEpLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQpgYGAKCmBgYHtyIG1ldHJpY2FzLXZhbGlkYWNpb259Ck1BRSAgPC0gbWVhbihhYnModmFsaWRhJGVycm9yKSkKUk1TRSA8LSBzcXJ0KG1lYW4odmFsaWRhJGVycm9yXjIpKQpNRSAgIDwtIG1lYW4odmFsaWRhJGVycm9yKQpNU0RSIDwtIG1lYW4oKHZhbGlkYSRlcnJvcl4yKSAvIHZhbGlkYSRrcmlnZS52YXIpCgpjYXQoIk1BRSA6Iiwgcm91bmQoTUFFLCAgNCksICLCsENcbiIpCmNhdCgiUk1TRToiLCByb3VuZChSTVNFLCA0KSwgIsKwQ1xuIikKY2F0KCJNRSAgOiIsIHJvdW5kKE1FLCAgIDQpLCAiwrBDIChzZXNnbylcbiIpCmNhdCgiTVNEUjoiLCByb3VuZChNU0RSLCA0KSwgIih+MSA9IGJpZW4gY2FsaWJyYWRvKVxuIikKIyBWYXJpYWJsZXMgaW5saW5lIGRlIGxhIHZhbGlkYWNpw7NuIGNydXphZGEKcmVzX01BRSAgICAgICAgPC0gcm91bmQoTUFFLCAzKQpyZXNfUk1TRSAgICAgICA8LSByb3VuZChSTVNFLCAzKQpyZXNfTUUgICAgICAgICA8LSByb3VuZChNRSwgNCkKcmVzX01TRFIgICAgICAgPC0gcm91bmQoTVNEUiwgMykKcmVzX01BRV9yZWRvbmRvIDwtIHJvdW5kKE1BRSwgMSkKcmVzX21zZHJfdGV4dG8gPC0gaWZlbHNlKGFicyhNU0RSIC0gMSkgPCAwLjMsCiAgInNlIGFjZXJjYSByYXpvbmFibGVtZW50ZSBhIDEsIGxvIHF1ZSBpbmRpY2EgcXVlIGxhcyB2YXJpYW56YXMgS3JpZ2luZyBzb24gZXN0aW1hY2lvbmVzIHJlYWxpc3RhcyBkZWwgZXJyb3IiLAogICJzZSBhbGVqYSBkZSAxLCBsbyBxdWUgc3VnaWVyZSBxdWUgbGFzIHZhcmlhbnphcyBLcmlnaW5nIHN1YmVzdGltYW4gbyBzb2JyZWVzdGltYW4gZWwgZXJyb3IgcmVhbCB5IGVsIG1vZGVsbyB0ZcOzcmljbyBwb2Ryw61hIGFmaW5hcnNlIikKYGBgCgoKYGBge3IgdGFibGEtbWV0cmljYXMtdmFsaWRhY2lvbn0KdGFibGFfdmFsIDwtIGRhdGEuZnJhbWUoCiAgTcOpdHJpY2EgICAgPSBjKCJNQUUiLCJSTVNFIiwiRXJyb3IgTWVkaW8gKHNlc2dvKSIsIk1TRFIiKSwKICBWYWxvciAgICAgID0gcm91bmQoYyhNQUUsIFJNU0UsIE1FLCBNU0RSKSwgNCksCiAgVW5pZGFkICAgICA9IGMoIsKwQyIsIsKwQyIsIsKwQyIsImFkaW1lbnNpb25hbCIpLAogIFJlZmVyZW5jaWEgPSBjKCJNZW5vciBlcyBtZWpvciIsIk1lbm9yIGVzIG1lam9yIiwiQ2VyY2FubyBhIDAiLCJDZXJjYW5vIGEgMSIpCikKCmthYmxlKHRhYmxhX3ZhbCwKICAgICAgY2FwdGlvbiA9ICJUYWJsYSA0LiBNw6l0cmljYXMgZGUgZGVzZW1wZcOxbyBkZWwgbW9kZWxvIEtyaWdpbmcgZW4gdmFsaWRhY2nDs24gY3J1emFkYSBsZWF2ZS1vbmUtb3V0IiwKICAgICAgYWxpZ24gICA9IGMoImwiLCJyIiwibCIsImwiKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKYGBge3IgcGxvdC1lcnJvcmVzLXZhbGlkYWNpb24sIGZpZy5jYXA9IkZpZ3VyYSAxNy4gSGlzdG9ncmFtYSBkZSBlcnJvcmVzIGRlIHByZWRpY2Npw7NuIGVuIGxhIHZhbGlkYWNpw7NuIGNydXphZGEgbGVhdmUtb25lLW91dC4ifQplcnJvcmVzX2RmIDwtIGRhdGEuZnJhbWUoZXJyb3IgPSB2YWxpZGEkZXJyb3IpCgpnZ3Bsb3QoZXJyb3Jlc19kZiwgYWVzKHggPSBlcnJvcikpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IGFmdGVyX3N0YXQoZGVuc2l0eSkpLAogICAgICAgICAgICAgICAgIGJpbnMgPSAyNSwgZmlsbCA9IHBhbGV0YVsxXSwgY29sb3IgPSAid2hpdGUiLCBhbHBoYSA9IDAuODUpICsKICBnZW9tX2RlbnNpdHkoY29sb3IgPSBwYWxldGFbMl0sIGxpbmV3aWR0aCA9IDEuMikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbG9yID0gcGFsZXRhWzNdLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBsaW5ld2lkdGggPSAxKSArCiAgbGFicyh4ID0gIkVycm9yIGRlIHByZWRpY2Npw7NuICjCsEMpIiwgeSA9ICJEZW5zaWRhZCIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIExhIHZhbGlkYWNpw7NuIGNydXphZGEgbGVhdmUtb25lLW91dCBzb2JyZSBsb3MgYHIgcmVzX25gIMOhcmJvbGVzIGFycm9qw7MgdW4gTUFFIGRlIGByIHJlc19NQUVgIMKwQyB5IHVuIFJNU0UgZGUgYHIgcmVzX1JNU0VgIMKwQy4gRXN0b3MgdmFsb3JlcyBpbmRpY2FuIHF1ZSBlbiBwcm9tZWRpbyBlbCBtb2RlbG8gc2UgZXF1aXZvY2EgZW4gbWVub3MgZGUgYHIgcmVzX01BRV9yZWRvbmRvYCDCsEMgYWwgcHJlZGVjaXIgbGEgdGVtcGVyYXR1cmEgZGUgdW4gw6FyYm9sIGEgcGFydGlyIGRlIHN1cyB2ZWNpbm9zLCBsbyBxdWUgc2UgY29uc2lkZXJhIGFjZXB0YWJsZSBkYWRvIGVsIHJhbmdvIHRvdGFsIG9ic2VydmFkbyBkZSBgciByZXNfdGVtcF9yYW5nb2AgwrBDLiBFbCBlcnJvciBtZWRpbyBkZSBgciByZXNfTUVgIMKwQyBjb25maXJtYSBxdWUgZWwgbW9kZWxvIG5vIHRpZW5lIHNlc2dvIHNpc3RlbcOhdGljbyBkZSBzb2JyZWVzdGltYWNpw7NuIG5pIHN1YmVzdGltYWNpw7NuLiBFbCBNU0RSIGRlIGByIHJlc19NU0RSYCBgciByZXNfbXNkcl90ZXh0b2AuIFVuYSBsaW1pdGFjacOzbiBkZSBlc3RhIHZhbGlkYWNpw7NuIGVzIHF1ZSBhbCBzZXIgbGVhdmUtb25lLW91dCwgbm8gZXZhbMO6YSBsYSBjYXBhY2lkYWQgZGVsIG1vZGVsbyBwYXJhIHByZWRlY2lyIGVuIHpvbmFzIHNpbiBuaW5nw7puIHNlbnNvciBjZXJjYW5vLCBxdWUgZXMgcHJlY2lzYW1lbnRlIGRvbmRlIGVsIGVycm9yIGRlbCByYXN0ZXIgZXMgbWF5b3IuCgpgYGB7ciBwbG90LW9ic2VydmFkby12cy1wcmVkaWNobywgZmlnLmNhcD0iRmlndXJhIDE4LiBEaWFncmFtYSBkZSBkaXNwZXJzacOzbiBlbnRyZSB2YWxvcmVzIG9ic2VydmFkb3MgeSBwcmVkaWNob3MgZW4gbGEgdmFsaWRhY2nDs24gY3J1emFkYS4ifQpvYnNfcHJlZF9kZiA8LSBkYXRhLmZyYW1lKAogIG9ic2VydmFkbyA9IHZhbGlkYSRkYXRhLAogIHByZWRpY2hvICA9IHZhbGlkYSRwcmVkaWN0CikKCmdncGxvdChvYnNfcHJlZF9kZiwgYWVzKHggPSBvYnNlcnZhZG8sIHkgPSBwcmVkaWNobykpICsKICBnZW9tX3BvaW50KGNvbG9yID0gcGFsZXRhWzFdLCBhbHBoYSA9IDAuNCwgc2l6ZSA9IDEuOCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwKICAgICAgICAgICAgICBjb2xvciA9IHBhbGV0YVsyXSwgbGluZXdpZHRoID0gMS4yLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiB4LAogICAgICAgICAgICAgIGNvbG9yID0gcGFsZXRhWzNdLCBzZSA9IFRSVUUsIGxpbmV3aWR0aCA9IDEsCiAgICAgICAgICAgICAgZmlsbCA9IHBhbGV0YVs1XSwgYWxwaGEgPSAwLjMpICsKICBsYWJzKHggPSAiVGVtcGVyYXR1cmEgb2JzZXJ2YWRhICjCsEMpIiwgeSA9ICJUZW1wZXJhdHVyYSBwcmVkaWNoYSAowrBDKSIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKSArCiAgdGhlbWUocGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBjb2xvciA9IHBhbGV0YVsxXSkpCmBgYAoKKipJbnRlcnByZXRhY2nDs246KiogRWwgZGlhZ3JhbWEgb2JzZXJ2YWRvLXByZWRpY2hvIG11ZXN0cmEgcXVlIGxhIG51YmUgZGUgcHVudG9zIHNpZ3VlIHJhem9uYWJsZW1lbnRlIGxhIGRpYWdvbmFsIGRlIHByZWRpY2Npw7NuIHBlcmZlY3RhLCBhdW5xdWUgY29uIG1heW9yIGRpc3BlcnNpw7NuIGVuIGxvcyBleHRyZW1vcyBkZWwgcmFuZ28gZGUgdGVtcGVyYXR1cmEuIEVzdG8gZXMgdMOtcGljbyBkZWwgS3JpZ2luZyBPcmRpbmFyaW8sIHF1ZSB0aWVuZGUgYSBzdWF2aXphciBsb3MgdmFsb3JlcyBleHRyZW1vcyBwb3JxdWUgbG9zIGVzdGltYSBjb21vIHByb21lZGlvcyBwb25kZXJhZG9zIGRlIHZlY2lub3M6IGxvcyDDoXJib2xlcyBtw6FzIGNhbGllbnRlcyBxdWVkYW4gbGlnZXJhbWVudGUgc3ViZXN0aW1hZG9zIHkgbG9zIG3DoXMgZnLDrW9zIGxpZ2VyYW1lbnRlIHNvYnJlZXN0aW1hZG9zLiBFc3RlIGVmZWN0byBkZSByZWdyZXNpw7NuIGEgbGEgbWVkaWEgZXMgdW5hIGxpbWl0YWNpw7NuIGluaGVyZW50ZSBhbCBtw6l0b2RvIHkgZGViZSBjb25zaWRlcmFyc2Ugc2kgZWwgb2JqZXRpdm8gZnVlcmEgaWRlbnRpZmljYXIgY29uIHByZWNpc2nDs24gbG9zIMOhcmJvbGVzIGluZGl2aWR1YWxlcyBlbiBjb25kaWNpw7NuIGRlIGVzdHLDqXMgdMOpcm1pY28gZXh0cmVtby4KCiMgKipDb25jbHVzaW9uZXMqKgoKRW4gZXN0YSBzZWNjacOzbiBzZSBzaW50ZXRpemFuIGxvcyBwcmluY2lwYWxlcyBoYWxsYXpnb3MgZGVsIGFuw6FsaXNpcy4KCkVsIGFuw6FsaXNpcyBjb25maXJtw7MgcXVlIGxhIHRlbXBlcmF0dXJhIGVuIGxhIGZpbmNhIG5vIGVzIGVzcGFjaWFsbWVudGUgaG9tb2fDqW5lYTogZWwgcmFuZ28gZGUgYHIgcmVzX3RlbXBfcmFuZ29gIMKwQyBlbnRyZSBlbCDDoXJib2wgbcOhcyBmcsOtbyB5IGVsIG3DoXMgY2FsaWVudGUsIHJlZ2lzdHJhZG8gZW4gdW5hIHNvbGEgam9ybmFkYSwgZXMgc3VmaWNpZW50ZSBwYXJhIGdlbmVyYXIgY29uZGljaW9uZXMgbWljcm9jbGltw6F0aWNhcyBkaWZlcmVuY2lhZGFzIHF1ZSBhZmVjdGFuIGVsIGRlc2Fycm9sbG8gZGVsIGFndWFjYXRlLiBFbCBzZW1pdmFyaW9ncmFtYSBlbXDDrXJpY28gY2FwdHVyw7MgZXN0YSBlc3RydWN0dXJhIGNvbiB1biByYW5nbyBlZmVjdGl2byBkZSBhcHJveGltYWRhbWVudGUgYHIgcmVzX3BoaV9tZXRyb3NgIG1ldHJvcywgcXVlIHJlcHJlc2VudGEgbGEgZXNjYWxhIGVzcGFjaWFsIHJlbGV2YW50ZSBwYXJhIGVsIG1hbmVqbyBkaWZlcmVuY2lhbCBkZW50cm8gZGVsIGxvdGUuCgpFbCBtb2RlbG8gYHIgcmVzX21vZGVsb19ub21icmVgIHJlc3VsdMOzIGVsIG1lam9yIGFqdXN0ZSBjb24gdW4gU0NFIGRlIGByIHJlc19zY2VgLCB5IGVsIEtyaWdpbmcgT3JkaW5hcmlvIGNvbnN0cnVpZG8gc29icmUgw6lsIHByb2R1am8gcHJlZGljY2lvbmVzIGNvbiB1biBNQUUgZGUgYHIgcmVzX01BRWAgwrBDLCBlcnJvciByYXpvbmFibGUgcGFyYSBkZWNpc2lvbmVzIGRlIG1hbmVqbyBkZSBwcmVjaXNpw7NuLiBFbCBzZWN0b3Igbm9yb2NjaWRlbnRhbCBkZSBsYSBmaW5jYSBjb25jZW50cmEgY29uc2lzdGVudGVtZW50ZSBsYXMgdGVtcGVyYXR1cmFzIG3DoXMgYWx0YXMsIGxvIHF1ZSBzdWdpZXJlIHJldmlzYXIgbGFzIGNvbmRpY2lvbmVzIGRlIHNvbWJyw61vLCByaWVnbyB5IGRlbnNpZGFkIGRlIHNpZW1icmEgZW4gZXNhIHpvbmEuCgpDb21vIGxpbWl0YWNpb25lcyBkZWwgZXN0dWRpbyBzZSBkZXN0YWNhbiB0cmVzOiBlbCBhbsOhbGlzaXMgY3VicmUgdW4gw7puaWNvIGTDrWEgZGUgbWVkaWNpw7NuLCBsbyBxdWUgaW1waWRlIGdlbmVyYWxpemFyIGxvcyBwYXRyb25lczsgZWwgc2VtaXZhcmlvZ3JhbWEgc2UgY2FsY3Vsw7Mgc2luIGFuYWxpemFyIGFuaXNvdHJvcMOtYSwgcXVlIHBvZHLDrWEgZXhpc3RpciBkYWRhIGxhIHBvc2libGUgaW5mbHVlbmNpYSBkZSBwZW5kaWVudGVzIG8gY29ycmllbnRlcyBkZSBhaXJlOyB5IGxhIHZhbGlkYWNpw7NuIGNydXphZGEgbGVhdmUtb25lLW91dCBubyBldmFsw7phIGVsIGRlc2VtcGXDsW8gZW4gem9uYXMgc2luIHNlbnNvcmVzIGNlcmNhbm9zLCBkb25kZSBlbCBlcnJvciByZWFsIHB1ZWRlIHN1cGVyYXIgZWwgYHIgcmVzX3NkX2Vycm9yX21heGAgwrBDIGVzdGltYWRvIGVuIGxvcyBib3JkZXMuIFBhcmEgZm9ydGFsZWNlciBlbCBhbsOhbGlzaXMgc2UgcmVjb21pZW5kYSBpbmNvcnBvcmFyIG1lZGljaW9uZXMgZGUgYWwgbWVub3MgdHJlcyBwZXLDrW9kb3MgZGlzdGludG9zIHkgZXhwbG9yYXIgc2VtaXZhcmlvZ3JhbWFzIGRpcmVjY2lvbmFsZXMuCgotLS0KCmBgYHtyIGluZm9ybWFjaW9uLXNlc2lvbn0Kc2Vzc2lvbkluZm8oKQpgYGAK