1. Introducción

Este cuaderno ilustra dos técnicas de interpolación espacial aplicadas a datos de carbono orgánico del suelo (SOC) a una profundidad de 15-30 cm en el departamento del Putumayo. Se emplean la técnica de ponderación de distancia inversa (IDW), que es determinista, y el kriging ordinario (OK), que es probabilístico. Ambas metodologías permiten generar superficies continuas a partir de datos puntuales obtenidos de SoilGrids a una resolución de 250 m.

El análisis y procesamiento de estos datos siguen la estructura de un cuaderno guía proporcionado por el profesor de Geomática Básica. Sin embargo, este documento amplía la documentación del código y proporciona una interpretación de los resultados obtenidos. El cuaderno guia ejecutado en R,se encuentra aquí

2. Preparación del espacio de trabajo

Antes de comenzar con la interpolación espacial en el departamento del Putumayo, es fundamental organizar nuestro entorno de trabajo en R. Esto incluye limpiar el espacio de trabajo y cargar las bibliotecas necesarias.

2.1 ¿Por qué es importante limpiar el espacio de trabajo?

Al limpiar el entorno, eliminamos variables y objetos creados en sesiones anteriores que podrían interferir con el análisis actual. Esto ayuda a:

Evitar conflictos entre objetos con nombres similares. Liberar memoria y mejorar el rendimiento de R. Garantizar reproducibilidad, asegurando que el código funcione desde cero sin dependencias ocultas. Podemos limpiar el espacio de trabajo con:

rm(list=ls())

2.2 ¿Por qué es importante cargar las bibliotecas?

Las bibliotecas contienen funciones especializadas que permiten manejar datos geoespaciales y realizar análisis avanzados. Puntualmente, para el desarrollo de este cuaderno, se utilizaron:

  • Lectura y escritura de datos geoespaciales: terra (datos ráster) y sf (datos vectoriales).
  • Interpolación espacial: gstat y automap.
  • Visualización de resultados: ggplot2, leaflet y tmap.

Antes de continuar, es importante asegurarse de tenerlas instaladas desde la consola de R (no en este cuaderno). Luego, cárgarlas con:

library(terra)
terra 1.8.21
library(sf)
Linking to GEOS 3.11.2, GDAL 3.8.2, PROJ 9.3.1; sf_use_s2()
is TRUE
library(sp)
library(stars)
Loading required package: abind
library(gstat)
library(automap)
library(RColorBrewer)
library(leaflet)
library(leafem)

3. Lectura de datos de entrada

Como se mencionó en la introducción, las muestras utilizadas corresponden a valores de SOC a una profundidad de 15-30 cm, obtenidos de SoilGrids 250 m a través de un cuaderno previo, . Estos datos fueron almacenados en formato GeoPackage dentro del directorio de datos, siguiendo la nomenclatura soc_{putu}.gpkg.

Antes de continuar con el análisis, es importante verificar que el archivo se encuentra disponible y listo para su uso.

list.files(path="DATOS", pattern = "*.gpkg")
[1] "Putumayo.gpkg" "soc_putu.gpkg"

3.1 Lectura de datos de entrada

summary(samples)
hist(samples$soc)
samples$soc = round(samples$soc,2)
pal <- colorNumeric(
  c("#E1F5C4", "#EDE574", "#F9D423", "#FC913A", "#FF4E50"),
  # colors depend on the count variable
  domain = samples$soc,
  )
munic <- munic[sf::st_geometry_type(munic) %in% c("POLYGON", "MULTIPOLYGON"), ]
# This a simple visualization
# Change the code to suit your preferences
leaflet() %>%
  addPolygons(
    data = munic,
    color = "gray",
    # set the opacity of the outline
    opacity = 1,
    # set the stroke width in pixels
    weight = 1,
    # set the fill opacity
    fillOpacity = 0.2) %>%
 addCircleMarkers(
    data = samples,
    radius= 1.5, 
    label = ~soc,
    color = ~pal(soc),
    fillOpacity = 1,
    stroke = F
  ) %>%
  addLegend(
    data = samples,
    pal = pal,
    values = ~soc,
    position = "bottomleft",
    title = "SOC:",
    opacity = 0.9) %>%
  addProviderTiles("OpenStreetMap")
g1 = gstat(formula = soc ~ 1, data = samples)
#Extensión  -77.1833333329999931,-0.5583333329999991 : -73.8416666669999984,1.4666666669999999
#Anchura    401
#Altura 243

#(rrr <- terra::rast(xmin=-74.55524, xmax=-72.38985, ymin=5.708308, ymax=8.145474, nrows=370, ncols = 329, vals = 1, crs = "epsg:4326"))
(rrr <- terra::rast(xmin=-77.183, xmax=-73.842, ymin=-0.558, ymax=1.47, nrows=243, ncols = 401, vals = 1, crs = "epsg:4326"))
stars.rrr <- stars::st_as_stars(rrr)
## [inverse distance weighted interpolation]
## running this code takes several minutes
z1 = predict(g1, stars.rrr)
# dealing with NA values
a <- which(is.na(z1[[1]]))
z1[[1]][a] = 0.0
z1
stars.rrr <- stars::st_as_stars(rrr)
## [inverse distance weighted interpolation]
## running this code takes several minutes
z1 = predict(g1, stars.rrr)
z1
# dealing with NA values
a <- which(is.na(z1[[1]]))
z1[[1]][a] = 0.0
z1
names(z1) = "soc"
# change this chunk to meet your preferences
paleta <- colorNumeric(palette = c("orange", "yellow", "cyan", "green"), domain = 10:120, na.color = "transparent")
m <- leaflet() %>%
  addTiles() %>%  
  leafem:::addGeoRaster(
      z1,
      opacity = 0.7,                
      colorOptions = colorOptions(palette = c("orange", "yellow", "cyan", "green"), 
                                  domain = 10:100)
    ) %>%
   addCircleMarkers(
    data = samples,
    radius= 1.5, 
    label = ~soc,
    color = ~paleta(soc),
    fillOpacity = 1,
    stroke = F
  ) %>%
    addLegend("bottomright", pal=paleta, values= z1$soc,
    title = "IDW SOC interpolation [%]"
    )
m  # Print the map
range(z1$soc, na.rm = TRUE)
# Obtener el rango de valores reales
soc_range <- range(z1$soc, na.rm = TRUE)

# Ajustar la paleta al rango correcto
paleta <- colorNumeric(palette = c("orange", "yellow", "cyan", "green"), 
                       domain = soc_range, na.color = "transparent")
leaflet() %>%
  addTiles() %>%  
  leafem:::addGeoRaster(
      z1,
      opacity = 0.7,                
      colorOptions = colorOptions(palette = c("orange", "yellow", "cyan", "green"), 
                                  domain = soc_range)  # Aquí aplicamos el rango real
    ) %>%
   addCircleMarkers(
    data = samples,
    radius= 1.5, 
    label = ~soc,
    color = ~paleta(soc),
    fillOpacity = 1,
    stroke = F
  ) %>%
    addLegend("bottomright", pal=paleta, values= z1$soc,
    title = "IDW SOC interpolation [%]"
    )
v_emp_ok = variogram(soc ~ 1, data=samples)
plot(v_emp_ok)
#make sure you understand the parameters needed to run this fitting function
v_mod_ok = automap::autofitVariogram(soc ~ 1, as(samples, "Spatial"))
plot(v_mod_ok)
v_mod_ok$var_model
## [using ordinary kriging]
## This takes several minutes 
## (or even ages depending on your raster cell size and your computer resources)
g2 = gstat(formula = soc ~ 1, model = v_mod_ok$var_model, data = samples)
z2= predict(g2, stars.rrr)
a <- which(is.na(z2[[1]]))
z2[[1]][a] = 0.0
names(z2) = "soc"
m <- leaflet() %>%
  addTiles() %>%  
  leafem:::addGeoRaster(
      z2,
      opacity = 0.7,                
      colorOptions = colorOptions(palette = c("orange", "yellow", "cyan", "green"), 
                                  domain = 10:100)
    ) %>%
   addCircleMarkers(
    data = samples,
    radius= 1.5, 
    label = ~soc,
    color = ~paleta(soc),
    fillOpacity = 1,
    stroke = F
  ) %>%
    addLegend("bottomright", pal = paleta, values= z2$soc,
    title = "OK SOC interpolation [%]"
    )
m  # Print the map
colores <- colorOptions(palette = c("orange", "yellow", "cyan", "green"), domain = 10:100, na.color = "transparent")
m <- leaflet() %>%
  addTiles() %>%  
  addGeoRaster(z1, colorOptions = colores, opacity = 0.8, group= "IDW")  %>%
  addGeoRaster(z2, colorOptions = colores, opacity = 0.8, group= "OK")  %>%
  # Add layers controls
  addLayersControl(
    overlayGroups = c("IDW", "OK"),
    options = layersControlOptions(collapsed = FALSE)
  ) %>% 
    addLegend("bottomright", pal = paleta, values= z1$soc,
    title = "Soil organic carbon [%]"
)
m  # Print the map
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KIA0KIyMgMS4gSW50cm9kdWNjacOzbg0KDQpFc3RlIGN1YWRlcm5vIGlsdXN0cmEgZG9zIHTDqWNuaWNhcyBkZSBpbnRlcnBvbGFjacOzbiBlc3BhY2lhbCBhcGxpY2FkYXMgYSBkYXRvcyBkZSBjYXJib25vIG9yZ8OhbmljbyBkZWwgc3VlbG8gKFNPQykgYSB1bmEgcHJvZnVuZGlkYWQgZGUgMTUtMzAgY20gZW4gZWwgZGVwYXJ0YW1lbnRvIGRlbCBQdXR1bWF5by4gU2UgZW1wbGVhbiBsYSB0w6ljbmljYSBkZSBwb25kZXJhY2nDs24gZGUgZGlzdGFuY2lhIGludmVyc2EgKElEVyksIHF1ZSBlcyBkZXRlcm1pbmlzdGEsIHkgZWwga3JpZ2luZyBvcmRpbmFyaW8gKE9LKSwgcXVlIGVzIHByb2JhYmlsw61zdGljby4gQW1iYXMgbWV0b2RvbG9nw61hcyBwZXJtaXRlbiBnZW5lcmFyIHN1cGVyZmljaWVzIGNvbnRpbnVhcyBhIHBhcnRpciBkZSBkYXRvcyBwdW50dWFsZXMgb2J0ZW5pZG9zIGRlIFNvaWxHcmlkcyBhIHVuYSByZXNvbHVjacOzbiBkZSAyNTAgbS4NCg0KRWwgYW7DoWxpc2lzIHkgcHJvY2VzYW1pZW50byBkZSBlc3RvcyBkYXRvcyBzaWd1ZW4gbGEgZXN0cnVjdHVyYSBkZSB1biBjdWFkZXJubyBndcOtYSBwcm9wb3JjaW9uYWRvIHBvciBlbCBwcm9mZXNvciBkZSBHZW9tw6F0aWNhIELDoXNpY2EuIFNpbiBlbWJhcmdvLCBlc3RlIGRvY3VtZW50byBhbXBsw61hIGxhIGRvY3VtZW50YWNpw7NuIGRlbCBjw7NkaWdvIHkgcHJvcG9yY2lvbmEgdW5hIGludGVycHJldGFjacOzbiBkZSBsb3MgcmVzdWx0YWRvcyBvYnRlbmlkb3MuIEVsIGN1YWRlcm5vIGd1aWEgZWplY3V0YWRvIGVuIFIsW3NlIGVuY3VlbnRyYSBhcXXDrV0oaHR0cHM6Ly9ycHVicy5jb20vaWFsczJ1bi9kb3dubG9hZFNvaWxHcmlkcykNCg0KIyMgMi4gUHJlcGFyYWNpw7NuIGRlbCBlc3BhY2lvIGRlIHRyYWJham8NCg0KQW50ZXMgZGUgY29tZW56YXIgY29uIGxhIGludGVycG9sYWNpw7NuIGVzcGFjaWFsIGVuIGVsIGRlcGFydGFtZW50byBkZWwgUHV0dW1heW8sIGVzIGZ1bmRhbWVudGFsIG9yZ2FuaXphciBudWVzdHJvIGVudG9ybm8gZGUgdHJhYmFqbyBlbiBSLiBFc3RvIGluY2x1eWUgbGltcGlhciBlbCBlc3BhY2lvIGRlIHRyYWJham8geSBjYXJnYXIgbGFzIGJpYmxpb3RlY2FzIG5lY2VzYXJpYXMuDQoNCiMjIyAyLjEgwr9Qb3IgcXXDqSBlcyBpbXBvcnRhbnRlIGxpbXBpYXIgZWwgZXNwYWNpbyBkZSB0cmFiYWpvPw0KDQpBbCBsaW1waWFyIGVsIGVudG9ybm8sIGVsaW1pbmFtb3MgdmFyaWFibGVzIHkgb2JqZXRvcyBjcmVhZG9zIGVuIHNlc2lvbmVzIGFudGVyaW9yZXMgcXVlIHBvZHLDrWFuIGludGVyZmVyaXIgY29uIGVsIGFuw6FsaXNpcyBhY3R1YWwuIEVzdG8gYXl1ZGEgYToNCg0KRXZpdGFyIGNvbmZsaWN0b3MgZW50cmUgb2JqZXRvcyBjb24gbm9tYnJlcyBzaW1pbGFyZXMuDQpMaWJlcmFyIG1lbW9yaWEgeSBtZWpvcmFyIGVsIHJlbmRpbWllbnRvIGRlIFIuDQpHYXJhbnRpemFyIHJlcHJvZHVjaWJpbGlkYWQsIGFzZWd1cmFuZG8gcXVlIGVsIGPDs2RpZ28gZnVuY2lvbmUgZGVzZGUgY2VybyBzaW4gZGVwZW5kZW5jaWFzIG9jdWx0YXMuDQpQb2RlbW9zIGxpbXBpYXIgZWwgZXNwYWNpbyBkZSB0cmFiYWpvIGNvbjoNCg0KYGBge3J9DQpybShsaXN0PWxzKCkpDQpgYGANCg0KIyMjIDIuMiDCv1BvciBxdcOpIGVzIGltcG9ydGFudGUgY2FyZ2FyIGxhcyBiaWJsaW90ZWNhcz8NCg0KTGFzIGJpYmxpb3RlY2FzIGNvbnRpZW5lbiBmdW5jaW9uZXMgZXNwZWNpYWxpemFkYXMgcXVlIHBlcm1pdGVuIG1hbmVqYXIgZGF0b3MgZ2VvZXNwYWNpYWxlcyB5IHJlYWxpemFyIGFuw6FsaXNpcyBhdmFuemFkb3MuIFB1bnR1YWxtZW50ZSwgcGFyYSBlbCBkZXNhcnJvbGxvIGRlIGVzdGUgY3VhZGVybm8sIHNlIHV0aWxpemFyb246DQoNCi0gKkxlY3R1cmEgeSBlc2NyaXR1cmEgZGUgZGF0b3MgZ2VvZXNwYWNpYWxlczoqIHRlcnJhIChkYXRvcyByw6FzdGVyKSB5IHNmIChkYXRvcyB2ZWN0b3JpYWxlcykuDQotICpJbnRlcnBvbGFjacOzbiBlc3BhY2lhbDoqIGdzdGF0IHkgYXV0b21hcC4NCi0gKlZpc3VhbGl6YWNpw7NuIGRlIHJlc3VsdGFkb3M6KiBnZ3Bsb3QyLCBsZWFmbGV0IHkgdG1hcC4NCg0KQW50ZXMgZGUgY29udGludWFyLCBlcyBpbXBvcnRhbnRlIGFzZWd1cmFyc2UgZGUgdGVuZXJsYXMgaW5zdGFsYWRhcyBkZXNkZSBsYSBjb25zb2xhIGRlIFIgKG5vIGVuIGVzdGUgY3VhZGVybm8pLiBMdWVnbywgY8OhcmdhcmxhcyBjb246DQoNCmBgYHtyfQ0KbGlicmFyeSh0ZXJyYSkNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHNwKQ0KbGlicmFyeShzdGFycykNCmxpYnJhcnkoZ3N0YXQpDQpsaWJyYXJ5KGF1dG9tYXApDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkobGVhZmVtKQ0KYGBgDQoNCiMjIDMuIExlY3R1cmEgZGUgZGF0b3MgZGUgZW50cmFkYQ0KDQpDb21vIHNlIG1lbmNpb27DsyBlbiBsYSBpbnRyb2R1Y2Npw7NuLCBsYXMgbXVlc3RyYXMgdXRpbGl6YWRhcyBjb3JyZXNwb25kZW4gYSB2YWxvcmVzIGRlIFNPQyBhIHVuYSBwcm9mdW5kaWRhZCBkZSAxNS0zMCBjbSwgb2J0ZW5pZG9zIGRlIFNvaWxHcmlkcyAyNTAgbSBhIHRyYXbDqXMgZGUgdW4gW2N1YWRlcm5vIHByZXZpb10oaHR0cHM6Ly9ycHVicy5jb20vaWFsczJ1bi9kb3dubG9hZFNvaWxHcmlkcyksIC4gRXN0b3MgZGF0b3MgZnVlcm9uIGFsbWFjZW5hZG9zIGVuIGZvcm1hdG8gR2VvUGFja2FnZSBkZW50cm8gZGVsIGRpcmVjdG9yaW8gZGUgZGF0b3MsIHNpZ3VpZW5kbyBsYSBub21lbmNsYXR1cmEgc29jX3twdXR1fS5ncGtnLg0KDQpBbnRlcyBkZSBjb250aW51YXIgY29uIGVsIGFuw6FsaXNpcywgZXMgaW1wb3J0YW50ZSB2ZXJpZmljYXIgcXVlIGVsIGFyY2hpdm8gc2UgZW5jdWVudHJhIGRpc3BvbmlibGUgeSBsaXN0byBwYXJhIHN1IHVzby4NCg0KYGBge3J9DQpsaXN0LmZpbGVzKHBhdGg9IkRBVE9TIiwgcGF0dGVybiA9ICIqLmdwa2ciKQ0KYGBgDQoNCiMjIyAzLjEgTGVjdHVyYSBkZSBkYXRvcyBkZSBlbnRyYWRhDQoNCg0KDQoNCmBgYHtyfQ0Kc2FtcGxlcyA8LSBzZjo6c3RfcmVhZCgiREFUT1Mvc29jX3B1dHUuZ3BrZyIpDQpgYGANCg0KDQoNCg0KYGBge3J9DQptdW5pYyA8LSBzZjo6c3RfcmVhZCgiREFUT1Mvc29jX3B1dHUuZ3BrZyIpDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5KHNhbXBsZXMpDQpgYGANCg0KDQpgYGB7cn0NCmhpc3Qoc2FtcGxlcyRzb2MpDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc2FtcGxlcyRzb2MgPSByb3VuZChzYW1wbGVzJHNvYywyKQ0KYGBgDQoNCmBgYHtyfQ0KcGFsIDwtIGNvbG9yTnVtZXJpYygNCiAgYygiI0UxRjVDNCIsICIjRURFNTc0IiwgIiNGOUQ0MjMiLCAiI0ZDOTEzQSIsICIjRkY0RTUwIiksDQogICMgY29sb3JzIGRlcGVuZCBvbiB0aGUgY291bnQgdmFyaWFibGUNCiAgZG9tYWluID0gc2FtcGxlcyRzb2MsDQogICkNCmBgYA0KDQoNCg0KYGBge3J9DQptdW5pYyA8LSBtdW5pY1tzZjo6c3RfZ2VvbWV0cnlfdHlwZShtdW5pYykgJWluJSBjKCJQT0xZR09OIiwgIk1VTFRJUE9MWUdPTiIpLCBdDQoNCmBgYA0KDQoNCmBgYHtyfQ0KIyBUaGlzIGEgc2ltcGxlIHZpc3VhbGl6YXRpb24NCiMgQ2hhbmdlIHRoZSBjb2RlIHRvIHN1aXQgeW91ciBwcmVmZXJlbmNlcw0KbGVhZmxldCgpICU+JQ0KICBhZGRQb2x5Z29ucygNCiAgICBkYXRhID0gbXVuaWMsDQogICAgY29sb3IgPSAiZ3JheSIsDQogICAgIyBzZXQgdGhlIG9wYWNpdHkgb2YgdGhlIG91dGxpbmUNCiAgICBvcGFjaXR5ID0gMSwNCiAgICAjIHNldCB0aGUgc3Ryb2tlIHdpZHRoIGluIHBpeGVscw0KICAgIHdlaWdodCA9IDEsDQogICAgIyBzZXQgdGhlIGZpbGwgb3BhY2l0eQ0KICAgIGZpbGxPcGFjaXR5ID0gMC4yKSAlPiUNCiBhZGRDaXJjbGVNYXJrZXJzKA0KICAgIGRhdGEgPSBzYW1wbGVzLA0KICAgIHJhZGl1cz0gMS41LCANCiAgICBsYWJlbCA9IH5zb2MsDQogICAgY29sb3IgPSB+cGFsKHNvYyksDQogICAgZmlsbE9wYWNpdHkgPSAxLA0KICAgIHN0cm9rZSA9IEYNCiAgKSAlPiUNCiAgYWRkTGVnZW5kKA0KICAgIGRhdGEgPSBzYW1wbGVzLA0KICAgIHBhbCA9IHBhbCwNCiAgICB2YWx1ZXMgPSB+c29jLA0KICAgIHBvc2l0aW9uID0gImJvdHRvbWxlZnQiLA0KICAgIHRpdGxlID0gIlNPQzoiLA0KICAgIG9wYWNpdHkgPSAwLjkpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKCJPcGVuU3RyZWV0TWFwIikNCmBgYA0KDQoNCmBgYHtyfQ0KZzEgPSBnc3RhdChmb3JtdWxhID0gc29jIH4gMSwgZGF0YSA9IHNhbXBsZXMpDQpgYGANCg0KDQpgYGB7cn0NCiNFeHRlbnNpw7NuCS03Ny4xODMzMzMzMzI5OTk5OTMxLC0wLjU1ODMzMzMzMjk5OTk5OTEgOiAtNzMuODQxNjY2NjY2OTk5OTk4NCwxLjQ2NjY2NjY2Njk5OTk5OTkNCiNBbmNodXJhCTQwMQ0KI0FsdHVyYQkyNDMNCg0KIyhycnIgPC0gdGVycmE6OnJhc3QoeG1pbj0tNzQuNTU1MjQsIHhtYXg9LTcyLjM4OTg1LCB5bWluPTUuNzA4MzA4LCB5bWF4PTguMTQ1NDc0LCBucm93cz0zNzAsIG5jb2xzID0gMzI5LCB2YWxzID0gMSwgY3JzID0gImVwc2c6NDMyNiIpKQ0KKHJyciA8LSB0ZXJyYTo6cmFzdCh4bWluPS03Ny4xODMsIHhtYXg9LTczLjg0MiwgeW1pbj0tMC41NTgsIHltYXg9MS40NywgbnJvd3M9MjQzLCBuY29scyA9IDQwMSwgdmFscyA9IDEsIGNycyA9ICJlcHNnOjQzMjYiKSkNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCnN0YXJzLnJyciA8LSBzdGFyczo6c3RfYXNfc3RhcnMocnJyKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgW2ludmVyc2UgZGlzdGFuY2Ugd2VpZ2h0ZWQgaW50ZXJwb2xhdGlvbl0NCiMjIHJ1bm5pbmcgdGhpcyBjb2RlIHRha2VzIHNldmVyYWwgbWludXRlcw0KejEgPSBwcmVkaWN0KGcxLCBzdGFycy5ycnIpDQpgYGANCg0KDQpgYGB7cn0NCiMgZGVhbGluZyB3aXRoIE5BIHZhbHVlcw0KYSA8LSB3aGljaChpcy5uYSh6MVtbMV1dKSkNCnoxW1sxXV1bYV0gPSAwLjANCmBgYA0KDQpgYGB7cn0NCnoxDQpgYGANCg0KDQpgYGB7cn0NCnN0YXJzLnJyciA8LSBzdGFyczo6c3RfYXNfc3RhcnMocnJyKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgW2ludmVyc2UgZGlzdGFuY2Ugd2VpZ2h0ZWQgaW50ZXJwb2xhdGlvbl0NCiMjIHJ1bm5pbmcgdGhpcyBjb2RlIHRha2VzIHNldmVyYWwgbWludXRlcw0KejEgPSBwcmVkaWN0KGcxLCBzdGFycy5ycnIpDQpgYGANCmBgYHtyfQ0KejENCmBgYA0KDQoNCmBgYHtyfQ0KIyBkZWFsaW5nIHdpdGggTkEgdmFsdWVzDQphIDwtIHdoaWNoKGlzLm5hKHoxW1sxXV0pKQ0KejFbWzFdXVthXSA9IDAuMA0KYGBgDQoNCg0KYGBge3J9DQp6MQ0KYGBgDQoNCg0KYGBge3J9DQpuYW1lcyh6MSkgPSAic29jIg0KYGBgDQoNCg0KYGBge3J9DQojIGNoYW5nZSB0aGlzIGNodW5rIHRvIG1lZXQgeW91ciBwcmVmZXJlbmNlcw0KcGFsZXRhIDwtIGNvbG9yTnVtZXJpYyhwYWxldHRlID0gYygib3JhbmdlIiwgInllbGxvdyIsICJjeWFuIiwgImdyZWVuIiksIGRvbWFpbiA9IDEwOjEyMCwgbmEuY29sb3IgPSAidHJhbnNwYXJlbnQiKQ0KYGBgDQoNCg0KYGBge3J9DQptIDwtIGxlYWZsZXQoKSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUgIA0KICBsZWFmZW06OjphZGRHZW9SYXN0ZXIoDQogICAgICB6MSwNCiAgICAgIG9wYWNpdHkgPSAwLjcsICAgICAgICAgICAgICAgIA0KICAgICAgY29sb3JPcHRpb25zID0gY29sb3JPcHRpb25zKHBhbGV0dGUgPSBjKCJvcmFuZ2UiLCAieWVsbG93IiwgImN5YW4iLCAiZ3JlZW4iKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9tYWluID0gMTA6MTAwKQ0KICAgICkgJT4lDQogICBhZGRDaXJjbGVNYXJrZXJzKA0KICAgIGRhdGEgPSBzYW1wbGVzLA0KICAgIHJhZGl1cz0gMS41LCANCiAgICBsYWJlbCA9IH5zb2MsDQogICAgY29sb3IgPSB+cGFsZXRhKHNvYyksDQogICAgZmlsbE9wYWNpdHkgPSAxLA0KICAgIHN0cm9rZSA9IEYNCiAgKSAlPiUNCiAgICBhZGRMZWdlbmQoImJvdHRvbXJpZ2h0IiwgcGFsPXBhbGV0YSwgdmFsdWVzPSB6MSRzb2MsDQogICAgdGl0bGUgPSAiSURXIFNPQyBpbnRlcnBvbGF0aW9uIFslXSINCiAgICApDQptICAjIFByaW50IHRoZSBtYXANCmBgYA0KDQoNCmBgYHtyfQ0KcmFuZ2UoejEkc29jLCBuYS5ybSA9IFRSVUUpDQpgYGANCg0KDQpgYGB7cn0NCiMgT2J0ZW5lciBlbCByYW5nbyBkZSB2YWxvcmVzIHJlYWxlcw0Kc29jX3JhbmdlIDwtIHJhbmdlKHoxJHNvYywgbmEucm0gPSBUUlVFKQ0KDQojIEFqdXN0YXIgbGEgcGFsZXRhIGFsIHJhbmdvIGNvcnJlY3RvDQpwYWxldGEgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSBjKCJvcmFuZ2UiLCAieWVsbG93IiwgImN5YW4iLCAiZ3JlZW4iKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbiA9IHNvY19yYW5nZSwgbmEuY29sb3IgPSAidHJhbnNwYXJlbnQiKQ0KDQpgYGANCg0KYGBge3J9DQpsZWFmbGV0KCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lICANCiAgbGVhZmVtOjo6YWRkR2VvUmFzdGVyKA0KICAgICAgejEsDQogICAgICBvcGFjaXR5ID0gMC43LCAgICAgICAgICAgICAgICANCiAgICAgIGNvbG9yT3B0aW9ucyA9IGNvbG9yT3B0aW9ucyhwYWxldHRlID0gYygib3JhbmdlIiwgInllbGxvdyIsICJjeWFuIiwgImdyZWVuIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbiA9IHNvY19yYW5nZSkgICMgQXF1w60gYXBsaWNhbW9zIGVsIHJhbmdvIHJlYWwNCiAgICApICU+JQ0KICAgYWRkQ2lyY2xlTWFya2VycygNCiAgICBkYXRhID0gc2FtcGxlcywNCiAgICByYWRpdXM9IDEuNSwgDQogICAgbGFiZWwgPSB+c29jLA0KICAgIGNvbG9yID0gfnBhbGV0YShzb2MpLA0KICAgIGZpbGxPcGFjaXR5ID0gMSwNCiAgICBzdHJva2UgPSBGDQogICkgJT4lDQogICAgYWRkTGVnZW5kKCJib3R0b21yaWdodCIsIHBhbD1wYWxldGEsIHZhbHVlcz0gejEkc29jLA0KICAgIHRpdGxlID0gIklEVyBTT0MgaW50ZXJwb2xhdGlvbiBbJV0iDQogICAgKQ0KDQpgYGANCg0KYGBge3J9DQp2X2VtcF9vayA9IHZhcmlvZ3JhbShzb2MgfiAxLCBkYXRhPXNhbXBsZXMpDQpgYGANCg0KYGBge3J9DQpwbG90KHZfZW1wX29rKQ0KYGBgDQoNCg0KYGBge3J9DQojbWFrZSBzdXJlIHlvdSB1bmRlcnN0YW5kIHRoZSBwYXJhbWV0ZXJzIG5lZWRlZCB0byBydW4gdGhpcyBmaXR0aW5nIGZ1bmN0aW9uDQp2X21vZF9vayA9IGF1dG9tYXA6OmF1dG9maXRWYXJpb2dyYW0oc29jIH4gMSwgYXMoc2FtcGxlcywgIlNwYXRpYWwiKSkNCmBgYA0KDQpgYGB7cn0NCnBsb3Qodl9tb2Rfb2spDQpgYGANCg0KDQpgYGB7cn0NCnZfbW9kX29rJHZhcl9tb2RlbA0KYGBgDQoNCmBgYHtyfQ0KIyMgW3VzaW5nIG9yZGluYXJ5IGtyaWdpbmddDQojIyBUaGlzIHRha2VzIHNldmVyYWwgbWludXRlcyANCiMjIChvciBldmVuIGFnZXMgZGVwZW5kaW5nIG9uIHlvdXIgcmFzdGVyIGNlbGwgc2l6ZSBhbmQgeW91ciBjb21wdXRlciByZXNvdXJjZXMpDQpnMiA9IGdzdGF0KGZvcm11bGEgPSBzb2MgfiAxLCBtb2RlbCA9IHZfbW9kX29rJHZhcl9tb2RlbCwgZGF0YSA9IHNhbXBsZXMpDQp6Mj0gcHJlZGljdChnMiwgc3RhcnMucnJyKQ0KYGBgDQoNCg0KYGBge3J9DQphIDwtIHdoaWNoKGlzLm5hKHoyW1sxXV0pKQ0KejJbWzFdXVthXSA9IDAuMA0KbmFtZXMoejIpID0gInNvYyINCmBgYA0KDQoNCmBgYHtyfQ0KbSA8LSBsZWFmbGV0KCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lICANCiAgbGVhZmVtOjo6YWRkR2VvUmFzdGVyKA0KICAgICAgejIsDQogICAgICBvcGFjaXR5ID0gMC43LCAgICAgICAgICAgICAgICANCiAgICAgIGNvbG9yT3B0aW9ucyA9IGNvbG9yT3B0aW9ucyhwYWxldHRlID0gYygib3JhbmdlIiwgInllbGxvdyIsICJjeWFuIiwgImdyZWVuIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbiA9IDEwOjEwMCkNCiAgICApICU+JQ0KICAgYWRkQ2lyY2xlTWFya2VycygNCiAgICBkYXRhID0gc2FtcGxlcywNCiAgICByYWRpdXM9IDEuNSwgDQogICAgbGFiZWwgPSB+c29jLA0KICAgIGNvbG9yID0gfnBhbGV0YShzb2MpLA0KICAgIGZpbGxPcGFjaXR5ID0gMSwNCiAgICBzdHJva2UgPSBGDQogICkgJT4lDQogICAgYWRkTGVnZW5kKCJib3R0b21yaWdodCIsIHBhbCA9IHBhbGV0YSwgdmFsdWVzPSB6MiRzb2MsDQogICAgdGl0bGUgPSAiT0sgU09DIGludGVycG9sYXRpb24gWyVdIg0KICAgICkNCmBgYA0KYGBge3J9DQptICAjIFByaW50IHRoZSBtYXANCmBgYA0KDQpgYGB7cn0NCmNvbG9yZXMgPC0gY29sb3JPcHRpb25zKHBhbGV0dGUgPSBjKCJvcmFuZ2UiLCAieWVsbG93IiwgImN5YW4iLCAiZ3JlZW4iKSwgZG9tYWluID0gMTA6MTAwLCBuYS5jb2xvciA9ICJ0cmFuc3BhcmVudCIpDQpgYGANCg0KYGBge3J9DQptIDwtIGxlYWZsZXQoKSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUgIA0KICBhZGRHZW9SYXN0ZXIoejEsIGNvbG9yT3B0aW9ucyA9IGNvbG9yZXMsIG9wYWNpdHkgPSAwLjgsIGdyb3VwPSAiSURXIikgICU+JQ0KICBhZGRHZW9SYXN0ZXIoejIsIGNvbG9yT3B0aW9ucyA9IGNvbG9yZXMsIG9wYWNpdHkgPSAwLjgsIGdyb3VwPSAiT0siKSAgJT4lDQogICMgQWRkIGxheWVycyBjb250cm9scw0KICBhZGRMYXllcnNDb250cm9sKA0KICAgIG92ZXJsYXlHcm91cHMgPSBjKCJJRFciLCAiT0siKSwNCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpDQogICkgJT4lIA0KICAgIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLCBwYWwgPSBwYWxldGEsIHZhbHVlcz0gejEkc29jLA0KICAgIHRpdGxlID0gIlNvaWwgb3JnYW5pYyBjYXJib24gWyVdIg0KKQ0KYGBgDQpgYGB7cn0NCm0gICMgUHJpbnQgdGhlIG1hcA0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==