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")

1 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.

2 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.

2.1 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.

2.2 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]))
Figura 1. Distribución de frecuencias de la temperatura registrada en los árboles de aguacate durante el período 01/10/2020.

Figura 1. Distribución de frecuencias de la temperatura registrada en los árboles de aguacate durante el período 01/10/2020.

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.

2.3 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)

Figura 2. Mapa interactivo de distribución espacial de los árboles de aguacate con código de color por temperatura (Popayán, Cauca).

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.

2.4 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]))
Figura 3. Dispersión de la temperatura en función de la latitud. La línea de regresión permite evaluar tendencia espacial norte-sur.

Figura 3. Dispersión de la temperatura en función de la latitud. La línea de regresión permite evaluar tendencia espacial norte-sur.

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]))
Figura 4. Dispersión de la temperatura en función de la longitud. La línea de regresión permite evaluar tendencia espacial este-oeste.

Figura 4. Dispersión de la temperatura en función de la longitud. La línea de regresión permite evaluar tendencia espacial este-oeste.

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.

3 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()
Figura 5. Distribución espacial de los puntos de muestreo clasificados por cuartil de temperatura.

Figura 5. Distribución espacial de los puntos de muestreo clasificados por cuartil de temperatura.

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)
Figura 6. Temperatura en función de la latitud con suavizado LOESS. Panel diagnóstico de tendencia norte-sur del geodata.

Figura 6. Temperatura en función de la latitud con suavizado LOESS. Panel diagnóstico de tendencia norte-sur del geodata.

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)
Figura 7. Temperatura en función de la longitud con suavizado LOESS. Panel diagnóstico de tendencia este-oeste del geodata.

Figura 7. Temperatura en función de la longitud con suavizado LOESS. Panel diagnóstico de tendencia este-oeste del geodata.

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)
Figura 8. Distribución marginal de la temperatura con histograma y curva de densidad kernel.

Figura 8. Distribución marginal de la temperatura con histograma y curva de densidad kernel.

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.

4 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)
Figura 9. Semivariograma empírico omnidireccional de la temperatura con envolventes de Monte Carlo (99 simulaciones).

Figura 9. Semivariograma empírico omnidireccional de la temperatura con envolventes de Monte Carlo (99 simulaciones).

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.

5 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")
Figura 10. Semivariograma empírico con los tres modelos teóricos ajustados: exponencial, gaussiano y esférico.

Figura 10. Semivariograma empírico con los tres modelos teóricos ajustados: exponencial, gaussiano y esférico.

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.

6 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)
Figura 11. Grilla de predicción de 100 x 100 puntos (gris) superpuesta sobre los puntos de muestreo (verde).

Figura 11. Grilla de predicción de 100 x 100 puntos (gris) superpuesta sobre los puntos de muestreo (verde).

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")
Figura 12. Mapa de predicción espacial de la temperatura (°C) mediante Kriging Ordinario con isolíneas de contorno.

Figura 12. Mapa de predicción espacial de la temperatura (°C) mediante Kriging Ordinario con isolíneas de contorno.

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()
Figura 13. Mapa de desviación estándar del error de predicción Kriging. Las zonas de mayor error coinciden con menor densidad de muestreo.

Figura 13. Mapa de desviación estándar del error de predicción Kriging. Las zonas de mayor error coinciden con menor densidad de muestreo.

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.

7 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")
Figura 14. Raster de temperatura predicha mediante Kriging Ordinario, visualizado con la paleta BuRd.

Figura 14. Raster de temperatura predicha mediante Kriging Ordinario, visualizado con la paleta BuRd.

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")
Figura 15. Raster de desviación estándar del error de predicción Kriging.

Figura 15. Raster de desviación estándar del error de predicción Kriging.

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)

Figura 16. Mapa interactivo con el raster de temperatura predicha superpuesto sobre la cartografía base.

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.

8 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)
Figura 17. Histograma de errores de predicción en la validación cruzada leave-one-out.

Figura 17. Histograma de errores de predicción en la validación cruzada leave-one-out.

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]))
Figura 18. Diagrama de dispersión entre valores observados y predichos en la validación cruzada.

Figura 18. Diagrama de dispersión entre valores observados y predichos en la validación cruzada.

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.

9 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