knitr:: opts_chunk$ set (fig.align = "center" ,
warning = FALSE ,
message = FALSE ,
fig.width = 9 )
VIDEO
Bibliotecas
library (rworldxtra)
library (tidyverse)
library (sf)
library (raster)
Datos Solenopsis
datos <- read_csv ("https://raw.githubusercontent.com/Edimer/Spatial-Data-Science/main/SIG_R/data/solenopsis.csv" )
datos
Área de estudio
Área en Colombia
colombia <- raster:: getData (name = "GADM" , country = "COL" , level = 0 )
colombia_sf <- st_as_sf (colombia)
colombia_sf %>%
ggplot () +
geom_sf () +
geom_sf (data = datos_sf)
Área específica
El siguiente mapa corresponde a una región específica de Colombia.
# Mapa del mundo
data ("countriesHigh" )
mapa <- countriesHigh %>%
st_as_sf () %>%
st_crop (datos_sf)
ggplot () +
geom_sf (data = mapa) +
geom_sf (data = datos_sf)
Otros mapas
Gráfico con color por especie y tamaño por abundancia: filtro los NA para que no aparezcan en la leyenda.
ggplot () +
geom_sf (data = mapa) +
geom_sf (data = datos_sf %>% filter (! is.na (Species)),
aes (color = Species, size = Measurement))
Gráfico por especie: filtro los NA para que no aparezcan en la leyenda.
ggplot () +
geom_sf (data = mapa) +
geom_sf (data = datos_sf %>% filter (! is.na (Species)),
aes (color = Species, size = Measurement)) +
facet_wrap (~ Species)
Resumen descriptivo
datos %>%
group_by (Species) %>%
summarise (Total = n (),
AbundanciaPromedio = mean (Measurement),
MáximoAbundancia = max (Measurement),
MínimoAbundancia = min (Measurement))
Modelación
Especie Geminata
Para los ejemplos siguientes sólo uso la especie Geminata . Se observa mayor abundancia en el norte de Colombia.
data_geminata <- datos_sf %>% filter (Species == "geminata" )
ggplot () +
geom_sf (data = mapa) +
geom_sf (data = data_geminata, aes (size = Measurement))
Datos ambientales
Aquí puede encontrar información acerca de las variables bioclimáticas que se descargan con el siguiente código.
Se realiza el corte del área de interés con la función crop() del paquete raster.
Cuando utilizamos formato shape usamos la función st_crop y con formatos raster utilizamos la función crop().
clima <- getData ("worldclim" , var = "bio" , res = 2.5 )
Error in getData("worldclim", var = "bio", res = 2.5) :
unused arguments (var = "bio", res = 2.5)
Número de capas o variables bioclimáticas:
[1] 19
Para el ejemplo sólo uso las capas 1, 7, 12, 15 y 19. La elección la hago de forma aleatoria para el ejemplo.
clima3 <- clima2[[c (1 , 7 , 12 , 15 , 19 )]]
Relaciones
Nota: para esta especie en particular el número de datos es bajo.
Lineales
data_final %>%
as.data.frame () %>%
dplyr:: select (bio1: bio19, Measurement) %>%
gather (key = "key" , value = "value" , - Measurement) %>%
ggplot (aes (x = value, y = Measurement)) +
facet_wrap (~ key, scales = "free" , ncol = 3 ) +
geom_point () +
geom_smooth (method = "lm" , color = "red" , se = FALSE ) +
theme_bw ()
Lineales (Logaritmos de abundancia)
data_final %>%
as.data.frame () %>%
dplyr:: select (bio1: bio19, Measurement) %>%
gather (key = "key" , value = "value" , - Measurement) %>%
ggplot (aes (x = value, y = Measurement)) +
facet_wrap (~ key, scales = "free" , ncol = 3 ) +
geom_point () +
geom_smooth (method = "lm" , color = "red" , se = FALSE ) +
scale_y_log10 () +
theme_bw ()
Modelos
Se prueban tres modelos y basado en el criterio de información de Akaike se elige el mejor.
El primero modelo es una regresión poisson, es decir, un modelo lineal generalizado con distribución de errores Poisson.
El segundo es un modelo de regresión poisson con un polinomio de segundo grado para bio1 y bio15.
El tercero es un modelo aditivo generalizado (GAM).
GLM Poisson
modelo1 <- glm (Measurement ~ bio1 + bio7 + bio12 + bio15 + bio19,
data = data_final, family = "poisson" )
summary (modelo1)
Call:
glm(formula = Measurement ~ bio1 + bio7 + bio12 + bio15 + bio19,
family = "poisson", data = data_final)
Deviance Residuals:
Min 1Q Median 3Q Max
-12.0620 -1.6910 -1.2246 0.5594 5.8436
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 55.808756 23.859812 2.339 0.019334 *
bio1 -0.080275 0.075201 -1.067 0.285763
bio7 -0.326484 0.084470 -3.865 0.000111 ***
bio12 0.008604 0.002602 3.307 0.000942 ***
bio15 0.087892 0.065417 1.344 0.179087
bio19 -0.024663 0.008683 -2.840 0.004506 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 1169.74 on 38 degrees of freedom
Residual deviance: 309.68 on 33 degrees of freedom
(13 observations deleted due to missingness)
AIC: 378.12
Number of Fisher Scoring iterations: 6
par (mfrow = c (2 ,2 ))
plot (modelo1)
GLM Poisson + Polinomio 2
modelo2 <- glm (Measurement ~ I (bio1^ 2 ) + bio7 + bio12 + I (bio15^ 2 ) + bio19,
data = data_final, family = "poisson" )
summary (modelo2)
Call:
glm(formula = Measurement ~ I(bio1^2) + bio7 + bio12 + I(bio15^2) +
bio19, family = "poisson", data = data_final)
Deviance Residuals:
Min 1Q Median 3Q Max
-12.0616 -1.7216 -1.3000 0.5815 5.8441
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 43.8725633 15.4543461 2.839 0.004528 **
I(bio1^2) -0.0001434 0.0001564 -0.917 0.358979
bio7 -0.3003849 0.0798584 -3.761 0.000169 ***
bio12 0.0076670 0.0027049 2.834 0.004590 **
I(bio15^2) 0.0008692 0.0007621 1.141 0.254019
bio19 -0.0224261 0.0092793 -2.417 0.015658 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 1169.74 on 38 degrees of freedom
Residual deviance: 310.28 on 33 degrees of freedom
(13 observations deleted due to missingness)
AIC: 378.72
Number of Fisher Scoring iterations: 6
par (mfrow = c (2 ,2 ))
plot (modelo2)
GAM
library (mgcv)
modelo3 <- gam (Measurement ~ s (bio1, k = 4 ) + s (bio7, k = 5 ) +
bio12 + bio15 + bio19 + bio1 + bio7,
data = data_final, family = "poisson" )
summary (modelo3)
Family: poisson
Link function: log
Formula:
Measurement ~ s(bio1, k = 4) + s(bio7, k = 5) + bio12 + bio15 +
bio19 + bio1 + bio7
Parametric coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 0.0000 0.0000 NA NA
bio12 0.2318 1.2777 0.181 0.856
bio15 4.0208 5.0339 0.799 0.424
bio19 -0.6589 4.8372 -0.136 0.892
bio1 -4.5917 23.6431 -0.194 0.846
bio7 7.1399 46.0812 0.155 0.877
Approximate significance of smooth terms:
edf Ref.df Chi.sq p-value
s(bio1) 0.889 1.019 0.534 0.505
s(bio7) 3.963 3.966 2.168 0.667
Rank: 11/13
R-sq.(adj) = 0.495 Deviance explained = 76%
UBRE = 6.6983 Scale est. = 1 n = 39
par (mfrow = c (2 , 2 ))
gam.check (modelo3)
Method: UBRE Optimizer: outer newton
full convergence after 11 iterations.
Gradient range [2.235383e-06,9.044007e-06]
(score 6.698331 & scale 1).
Hessian positive definite, eigenvalue range [9.541386e-06,0.0005525887].
Model rank = 11 / 13
Basis dimension (k) checking results. Low p-value (k-index<1) may
indicate that k is too low, especially if edf is close to k'.
k' edf k-index p-value
s(bio1) 3.000 0.889 0.99 0.49
s(bio7) 4.000 3.963 1.22 0.94
Mejor Modelo
Es mejor el modelo GAM (AIC más bajo). Con este modelo se realizan las predicciones de abundancia.
AIC (modelo1, modelo2, modelo3)
Predicciones
Nota: dado el bajo número de datos (51 registros) para la especie bajo análisis, no se logra construir un modelo que proporcione abundancias predichas correctamente. Cabe mencionar que es posible mejorar el análisis incluyendo más información de la especie o ajustando otros modelos.
prediccion <- predict (clima3, modelo2, type = "response" )
plot (prediccion, colNA = "black" )
prediccion_df <- prediccion %>%
as ("SpatialPixelsDataFrame" ) %>%
as.data.frame () %>%
rename (Abundancia = layer)
ggplot () +
geom_tile (data = prediccion_df, aes (x = x, y = y, fill = Abundancia)) +
geom_sf (data = mapa, alpha = 0 ) +
scale_fill_viridis_c () +
theme_bw ()
Predicciones futuras
Imágenes futuras: las mismas con las cuales se entrenó el modelo.
futuro <- raster:: getData ("CMIP5" , var = "bio" , res = 2.5 , rcp = 85 ,
model = "HD" , year = 70 ) %>%
crop (data_geminata)
futuro <- futuro[[c (1 , 7 , 12 , 15 , 19 )]]
names (futuro) <- c ("bio1" , "bio7" , "bio12" , "bio15" , "bio19" )
plot (futuro)
Mapa de predicción de abundancia:
prediccion_futura <- predict (futuro, modelo2, type = "response" )
prediccion_df_futuro <- prediccion_futura %>%
as ("SpatialPixelsDataFrame" ) %>%
as.data.frame () %>%
rename (Abundancia = layer)
ggplot () +
geom_tile (data = prediccion_df_futuro, aes (x = x, y = y, fill = Abundancia)) +
geom_sf (data = mapa, alpha = 0 ) +
scale_fill_viridis_c () +
theme_bw ()
Anexos
En el ejemplo de clase se trabaja con la especie Bombus affinis (abejorros), sin embargo, obtuve directamente la base de datos de PREDICTS database y voy a trabajar con el género de hormigas Solenopsis , también conocida como hormiga colorada o de fuego..
En este caso filtro información sólo para Colombia.
Se filtran datos sólo de abundancia de especies.
Mantengo sólo las siguientes variables:
Reference: referencia del estudio
Study_common_taxon: taxón
Site_name: nombre de sitio
Sampling_effort
Longitude
Latitude
Species
Measurement: medida de abundancia
Effort_corrected_measurement
# Base de datos total con más de 3 millones de registros
datos_total <- fread ("database.csv" , encoding = "UTF-8" )
# Filtro solenopsis para Colombia: 551 registros de abundancia
datos_colombia <- datos_total %>%
filter (Country == "Colombia" ) %>%
filter (Genus == "Solenopsis" ) %>%
filter (Diversity_metric_type == "Abundance" ) %>%
select (Reference, Study_common_taxon, Site_name, Sampling_effort,
Longitude, Latitude, Species, Measurement,
Effort_corrected_measurement)
# Exportando datos de solenopsis
write.csv (datos_colombia, file = "solenopsis.csv" , row.names = FALSE ,
fileEncoding = "UTF-8" )
LS0tDQp0aXRsZTogIlNpc3RlbWFzIGRlIEluZm9ybWFjacOzbiBHZW9ncsOhZmljYSBjb24gUiINCnN1YnRpdGxlOiAiTW9kZWxvcyBjb24gUmFzdGVyIg0KYXV0aG9yOiAiRWRpbWVyIERhdmlkIEphcmFtaWxsbyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrDQotLS0NCg0KYGBge3J9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmFsaWduID0gImNlbnRlciIsDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSA5KQ0KYGBgDQoNCg0KLSBOb3RhcyB0b21hZGFzIGRlbCBjdXJzbyBbIlNpc3RlbWFzIGRlIEluZm9ybWFjacOzbiBHZW9ncsOhZmljYSBlbiBSIChTSUcgZW4gUikiIGNvbiBEZXJlayBDb3Jjb3Jhbi5dKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9OWUtekRRUEdoQm8mYWJfY2hhbm5lbD1EZXJla0NvcmNvcmFuKQ0KDQo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkLzllLXpEUVBHaEJvIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPg0KDQojIEJpYmxpb3RlY2FzDQoNCmBgYHtyfQ0KbGlicmFyeShyd29ybGR4dHJhKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShyYXN0ZXIpDQpgYGANCg0KIyBEYXRvcyBTb2xlbm9wc2lzDQoNCjxjZW50ZXI+DQo8aW1nIHNyYyA9ICJpbWcvaG9ybWlnYS5QTkciIC8+DQo8L2NlbnRlcj4NCg0KLSBWZXIgYW5leG9zIHBhcmEgb2J0ZW5lciBjw7NkaWdvIGRlIGxhIGJhc2UgZGUgZGF0b3MgcXVlIHNlIGNhcmdhIGEgY29udGludWFjacOzbi4NCi0gRXN0b3MgZGF0b3MgZXN0w6FuIGVuIFtlc3RlIGRpcmVjdG9yaW8gZGUgR2l0aHViLl0oaHR0cHM6Ly9naXRodWIuY29tL0VkaW1lci9TcGF0aWFsLURhdGEtU2NpZW5jZS9ibG9iL21haW4vU0lHX1IvZGF0YS9zb2xlbm9wc2lzLmNzdikNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpkYXRvcyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0VkaW1lci9TcGF0aWFsLURhdGEtU2NpZW5jZS9tYWluL1NJR19SL2RhdGEvc29sZW5vcHNpcy5jc3YiKQ0KZGF0b3MNCmBgYA0KDQojIyBUcmFuc2Zvcm1hY2nDs24gYSBzZg0KDQotIEVuIGVsIGFyZ3VtZW50byBgY29vcmRzYCBzZSBpbmNvcnBvcmEgdW4gdmVjdG9yIGNvbiBsYSBwb3NpY2nDs24gZGUgbGFzIGNvbHVtbmFzIGxvbmdpdHVkIHkgbGF0aXR1ZCwgcmVzcGVjdGl2YW1lbnRlLg0KLSBFbCBzaXN0ZW1hIGRlIGNvb3JkZW5hZGFzIGVsZWdpZG8gZW4gZWwgc2lndWllbnRlIGPDs2RpZ28gZXMgZWwgbcOhcyBzZW5jaWxsbyBkZSB0b2Rvcywgc2luIGVtYmFyZ28sIHNlIHB1ZWRlIGNhbWJpYXIgKFt2ZXIgY2xhc2UgMDFdKGh0dHBzOi8vcnB1YnMuY29tL0VkaW1lci82NzM0MzMpKS4NCg0KYGBge3J9DQojIERhdG9zIGEgc2YNCmRhdG9zX3NmIDwtIGRhdG9zICU+JSANCiAgc3RfYXNfc2YoY29vcmRzID0gYyg1LCA2KSwgY3JzID0gIitwcm9qPWxvbmdsYXQgK2VsbHBzPVdHUzg0ICtkYXR1bT1XR1M4NCArbm9fZGVmcyArdG93Z3M4ND0wLDAsMCIpDQpgYGANCg0KIyMgw4FyZWEgZGUgZXN0dWRpbyB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIMOBcmVhIGVuIENvbG9tYmlhDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KY29sb21iaWEgPC0gcmFzdGVyOjpnZXREYXRhKG5hbWUgPSAiR0FETSIsIGNvdW50cnkgPSAiQ09MIiwgbGV2ZWwgPSAwKQ0KY29sb21iaWFfc2YgPC0gc3RfYXNfc2YoY29sb21iaWEpDQpjb2xvbWJpYV9zZiAlPiUgDQogIGdncGxvdCgpICsgDQogIGdlb21fc2YoKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdG9zX3NmKQ0KYGBgDQoNCiMjIyDDgXJlYSBlc3BlY8OtZmljYQ0KDQotIEVsIHNpZ3VpZW50ZSBtYXBhIGNvcnJlc3BvbmRlIGEgdW5hIHJlZ2nDs24gZXNwZWPDrWZpY2EgZGUgQ29sb21iaWEuDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KIyBNYXBhIGRlbCBtdW5kbw0KZGF0YSgiY291bnRyaWVzSGlnaCIpDQptYXBhIDwtIGNvdW50cmllc0hpZ2ggJT4lIA0KICBzdF9hc19zZigpICU+JQ0KICBzdF9jcm9wKGRhdG9zX3NmKQ0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBtYXBhKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdG9zX3NmKQ0KYGBgDQoNCiMjIE90cm9zIG1hcGFzDQoNCi0gKipHcsOhZmljbyBjb24gY29sb3IgcG9yIGVzcGVjaWUgeSB0YW1hw7FvIHBvciBhYnVuZGFuY2lhOioqIGZpbHRybyBsb3MgTkEgcGFyYSBxdWUgbm8gYXBhcmV6Y2FuIGVuIGxhIGxleWVuZGEuDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBtYXBhKSArDQogIGdlb21fc2YoZGF0YSA9IGRhdG9zX3NmICU+JSBmaWx0ZXIoIWlzLm5hKFNwZWNpZXMpKSwNCiAgICAgICAgICBhZXMoY29sb3IgPSBTcGVjaWVzLCBzaXplID0gTWVhc3VyZW1lbnQpKQ0KYGBgDQoNCi0gKipHcsOhZmljbyBwb3IgZXNwZWNpZToqKiBmaWx0cm8gbG9zIE5BIHBhcmEgcXVlIG5vIGFwYXJlemNhbiBlbiBsYSBsZXllbmRhLg0KDQpgYGB7cn0NCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gbWFwYSkgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRvc19zZiAlPiUgZmlsdGVyKCFpcy5uYShTcGVjaWVzKSksDQogICAgICAgICAgYWVzKGNvbG9yID0gU3BlY2llcywgc2l6ZSA9IE1lYXN1cmVtZW50KSkgKw0KICBmYWNldF93cmFwKH5TcGVjaWVzKQ0KYGBgDQoNCiMjIFJlc3VtZW4gZGVzY3JpcHRpdm8NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpkYXRvcyAlPiUgDQogIGdyb3VwX2J5KFNwZWNpZXMpICU+JSANCiAgc3VtbWFyaXNlKFRvdGFsID0gbigpLA0KICAgICAgICAgICAgQWJ1bmRhbmNpYVByb21lZGlvID0gbWVhbihNZWFzdXJlbWVudCksDQogICAgICAgICAgICBNw6F4aW1vQWJ1bmRhbmNpYSA9IG1heChNZWFzdXJlbWVudCksDQogICAgICAgICAgICBNw61uaW1vQWJ1bmRhbmNpYSA9IG1pbihNZWFzdXJlbWVudCkpDQpgYGANCg0KIyBNb2RlbGFjacOzbg0KDQojIyBFc3BlY2llIEdlbWluYXRhDQoNCi0gUGFyYSBsb3MgZWplbXBsb3Mgc2lndWllbnRlcyBzw7NsbyB1c28gbGEgZXNwZWNpZSAqR2VtaW5hdGEqLiBTZSBvYnNlcnZhIG1heW9yIGFidW5kYW5jaWEgZW4gZWwgbm9ydGUgZGUgQ29sb21iaWEuDQoNCmBgYHtyfQ0KZGF0YV9nZW1pbmF0YSA8LSBkYXRvc19zZiAlPiUgZmlsdGVyKFNwZWNpZXMgPT0gImdlbWluYXRhIikNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gbWFwYSkgKw0KICBnZW9tX3NmKGRhdGEgPSBkYXRhX2dlbWluYXRhLCBhZXMoc2l6ZSA9IE1lYXN1cmVtZW50KSkNCmBgYA0KDQojIyBEYXRvcyBhbWJpZW50YWxlcw0KDQotIFtBcXXDrV0oaHR0cHM6Ly93d3cud29ybGRjbGltLm9yZy9kYXRhL2Jpb2NsaW0uaHRtbCkgcHVlZGUgZW5jb250cmFyIGluZm9ybWFjacOzbiBhY2VyY2EgZGUgbGFzIHZhcmlhYmxlcyBiaW9jbGltw6F0aWNhcyBxdWUgc2UgZGVzY2FyZ2FuIGNvbiBlbCBzaWd1aWVudGUgY8OzZGlnby4NCi0gU2UgcmVhbGl6YSBlbCBjb3J0ZSBkZWwgw6FyZWEgZGUgaW50ZXLDqXMgY29uIGxhIGZ1bmNpw7NuIGNyb3AoKSBkZWwgcGFxdWV0ZSByYXN0ZXIuDQogIC0gQ3VhbmRvIHV0aWxpemFtb3MgZm9ybWF0byBzaGFwZSB1c2Ftb3MgbGEgZnVuY2nDs24gc3RfY3JvcCB5IGNvbiBmb3JtYXRvcyByYXN0ZXIgdXRpbGl6YW1vcyBsYSBmdW5jacOzbiBjcm9wKCkuDQoNCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD0xMH0NCmNsaW1hIDwtIGdldERhdGEoIndvcmxkY2xpbSIsIHZhciA9ICJiaW8iLCByZXMgPSAyLjUpDQpjbGltYTIgPC0gY2xpbWEgJT4lIGNyb3AoZGF0YV9nZW1pbmF0YSkNCnBsb3QoY2xpbWEyKQ0KYGBgDQoNCi0gKipOw7ptZXJvIGRlIGNhcGFzIG8gdmFyaWFibGVzIGJpb2NsaW3DoXRpY2FzOioqDQoNCmBgYHtyfQ0KbmxheWVycyhjbGltYTIpDQpgYGANCg0KLSBQYXJhIGVsIGVqZW1wbG8gc8OzbG8gdXNvIGxhcyBjYXBhcyAxLCA3LCAxMiwgMTUgeSAxOS4gTGEgZWxlY2Npw7NuIGxhIGhhZ28gZGUgZm9ybWEgYWxlYXRvcmlhIHBhcmEgZWwgZWplbXBsby4NCg0KYGBge3J9DQpjbGltYTMgPC0gY2xpbWEyW1tjKDEsIDcsIDEyLCAxNSwgMTkpXV0NCmBgYA0KDQoNCiMjIEV4dHJhY2Npw7NuIGRlIGRhdG9zDQoNCi0gKipWYXJpYWJsZXMgYmlvY2xpbcOhdGljYXM6KiogYWxndW5vcyBwdW50b3MgcXVlZGFuIGNvbiB2YWxvcmVzIE5BIHBhcmEgbGFzIHZhcmlhYmxlcyBiaW9jbGltw6F0aWNhcy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjbGltYV9kYXRhIDwtIGV4dHJhY3QoY2xpbWEzLCBkYXRhX2dlbWluYXRhKSAlPiUgDQogIGFzLmRhdGEuZnJhbWUoKQ0KY2xpbWFfZGF0YQ0KYGBgDQoNCi0gKipVbmllbmRvIGRhdG9zIGRlIGFidW5kYW5jaWEgY29uIHZhcmlhYmxlcyBiaW9jbGltw6F0aWNhczoqKg0KDQpgYGB7cn0NCmRhdGFfZmluYWwgPC0gZGF0YV9nZW1pbmF0YSAlPiUgDQogIGJpbmRfY29scyhjbGltYV9kYXRhKQ0KZGF0YV9maW5hbA0KYGBgDQoNCiMjIFJlbGFjaW9uZXMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCi0gKipOb3RhOioqIHBhcmEgZXN0YSBlc3BlY2llIGVuIHBhcnRpY3VsYXIgZWwgbsO6bWVybyBkZSBkYXRvcyBlcyBiYWpvLg0KDQojIyMgTGluZWFsZXMNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpkYXRhX2ZpbmFsICU+JSANCiAgYXMuZGF0YS5mcmFtZSgpICU+JSANCiAgZHBseXI6OnNlbGVjdChiaW8xOmJpbzE5LCBNZWFzdXJlbWVudCkgJT4lIA0KICBnYXRoZXIoa2V5ID0gImtleSIsIHZhbHVlID0gInZhbHVlIiwgLU1lYXN1cmVtZW50KSAgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeSA9IE1lYXN1cmVtZW50KSkgKw0KICBmYWNldF93cmFwKH5rZXksIHNjYWxlcyA9ICJmcmVlIiwgbmNvbCA9IDMpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIiwgc2UgPSBGQUxTRSkgKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyMjIExpbmVhbGVzIChMb2dhcml0bW9zIGRlIGFidW5kYW5jaWEpDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KZGF0YV9maW5hbCAlPiUgDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoYmlvMTpiaW8xOSwgTWVhc3VyZW1lbnQpICU+JSANCiAgZ2F0aGVyKGtleSA9ICJrZXkiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1NZWFzdXJlbWVudCkgICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIHkgPSBNZWFzdXJlbWVudCkpICsNCiAgZmFjZXRfd3JhcCh+a2V5LCBzY2FsZXMgPSAiZnJlZSIsIG5jb2wgPSAzKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpICsNCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMjIE1vZGVsb3Mgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCi0gU2UgcHJ1ZWJhbiB0cmVzIG1vZGVsb3MgeSBiYXNhZG8gZW4gZWwgY3JpdGVyaW8gZGUgaW5mb3JtYWNpw7NuIGRlIEFrYWlrZSBzZSBlbGlnZSBlbCBtZWpvci4NCiAgLSBFbCBwcmltZXJvIG1vZGVsbyBlcyB1bmEgcmVncmVzacOzbiBwb2lzc29uLCBlcyBkZWNpciwgdW4gbW9kZWxvIGxpbmVhbCBnZW5lcmFsaXphZG8gY29uIGRpc3RyaWJ1Y2nDs24gZGUgZXJyb3JlcyBQb2lzc29uLg0KICAtIEVsIHNlZ3VuZG8gZXMgdW4gbW9kZWxvIGRlIHJlZ3Jlc2nDs24gcG9pc3NvbiBjb24gdW4gcG9saW5vbWlvIGRlIHNlZ3VuZG8gZ3JhZG8gcGFyYSBiaW8xIHkgYmlvMTUuDQogIC0gRWwgdGVyY2VybyBlcyB1biBtb2RlbG8gYWRpdGl2byBnZW5lcmFsaXphZG8gKEdBTSkuDQoNCiMjIyBHTE0gUG9pc3Nvbg0KDQpgYGB7cn0NCm1vZGVsbzEgPC0gZ2xtKE1lYXN1cmVtZW50IH4gYmlvMSArIGJpbzcgKyBiaW8xMiArIGJpbzE1ICsgYmlvMTksDQogICAgICAgICAgICAgICBkYXRhID0gZGF0YV9maW5hbCwgZmFtaWx5ID0gInBvaXNzb24iKQ0Kc3VtbWFyeShtb2RlbG8xKQ0KYGBgDQoNCi0gKipSZXNpZHVhbGVzOioqDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChtb2RlbG8xKQ0KYGBgDQoNCiMjIyBHTE0gUG9pc3NvbiArIFBvbGlub21pbyAyDQoNCmBgYHtyfQ0KbW9kZWxvMiA8LSBnbG0oTWVhc3VyZW1lbnQgfiBJKGJpbzFeMikgKyBiaW83ICsgYmlvMTIgKyBJKGJpbzE1XjIpICsgYmlvMTksDQogICAgICAgICAgICAgICBkYXRhID0gZGF0YV9maW5hbCwgZmFtaWx5ID0gInBvaXNzb24iKQ0Kc3VtbWFyeShtb2RlbG8yKQ0KYGBgDQoNCi0gKipSZXNpZHVhbGVzOioqDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChtb2RlbG8yKQ0KYGBgDQoNCiMjIyBHQU0NCg0KYGBge3J9DQpsaWJyYXJ5KG1nY3YpDQptb2RlbG8zIDwtIGdhbShNZWFzdXJlbWVudCB+IHMoYmlvMSwgayA9IDQpICsgcyhiaW83LCBrID0gNSkgKyANCiAgICAgICAgICAgICAgICAgYmlvMTIgKyBiaW8xNSArIGJpbzE5ICsgYmlvMSArIGJpbzcsDQogICAgICAgICAgICAgICBkYXRhID0gZGF0YV9maW5hbCwgZmFtaWx5ID0gInBvaXNzb24iKQ0Kc3VtbWFyeShtb2RlbG8zKQ0KYGBgDQoNCi0gKipSZXNpZHVhbGVzOioqDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygyLCAyKSkNCmdhbS5jaGVjayhtb2RlbG8zKQ0KYGBgDQojIyMgTWVqb3IgTW9kZWxvDQoNCi0gRXMgbWVqb3IgZWwgbW9kZWxvIEdBTSAoQUlDIG3DoXMgYmFqbykuIENvbiBlc3RlIG1vZGVsbyBzZSByZWFsaXphbiBsYXMgcHJlZGljY2lvbmVzIGRlIGFidW5kYW5jaWEuDQoNCmBgYHtyfQ0KQUlDKG1vZGVsbzEsIG1vZGVsbzIsIG1vZGVsbzMpDQpgYGANCg0KIyMgUHJlZGljY2lvbmVzDQoNCi0gKipOb3RhOioqIGRhZG8gZWwgYmFqbyBuw7ptZXJvIGRlIGRhdG9zICg1MSByZWdpc3Ryb3MpIHBhcmEgbGEgZXNwZWNpZSBiYWpvIGFuw6FsaXNpcywgbm8gc2UgbG9ncmEgY29uc3RydWlyIHVuIG1vZGVsbyBxdWUgcHJvcG9yY2lvbmUgYWJ1bmRhbmNpYXMgcHJlZGljaGFzIGNvcnJlY3RhbWVudGUuIENhYmUgbWVuY2lvbmFyIHF1ZSBlcyBwb3NpYmxlIG1lam9yYXIgZWwgYW7DoWxpc2lzIGluY2x1eWVuZG8gbcOhcyBpbmZvcm1hY2nDs24gZGUgbGEgZXNwZWNpZSBvIGFqdXN0YW5kbyBvdHJvcyBtb2RlbG9zLg0KDQpgYGB7cn0NCnByZWRpY2Npb24gPC0gcHJlZGljdChjbGltYTMsIG1vZGVsbzIsIHR5cGUgPSAicmVzcG9uc2UiKQ0KcGxvdChwcmVkaWNjaW9uLCBjb2xOQSA9ICJibGFjayIpDQpgYGANCg0KLSAqKk1hcGEgY29uIGdncGxvdDI6KioNCg0KYGBge3J9DQpwcmVkaWNjaW9uX2RmIDwtIHByZWRpY2Npb24gJT4lIA0KICBhcygiU3BhdGlhbFBpeGVsc0RhdGFGcmFtZSIpICU+JSANCiAgYXMuZGF0YS5mcmFtZSgpICU+JSANCiAgcmVuYW1lKEFidW5kYW5jaWEgPSBsYXllcikNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3RpbGUoZGF0YSA9IHByZWRpY2Npb25fZGYsIGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSBBYnVuZGFuY2lhKSkgKw0KICBnZW9tX3NmKGRhdGEgPSBtYXBhLCBhbHBoYSA9IDApICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyBQcmVkaWNjaW9uZXMgZnV0dXJhcw0KDQotICoqSW3DoWdlbmVzIGZ1dHVyYXM6KiogbGFzIG1pc21hcyBjb24gbGFzIGN1YWxlcyBzZSBlbnRyZW7DsyBlbCBtb2RlbG8uDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KZnV0dXJvIDwtIHJhc3Rlcjo6Z2V0RGF0YSgiQ01JUDUiLCB2YXIgPSAiYmlvIiwgcmVzID0gMi41LCByY3AgPSA4NSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiSEQiLCB5ZWFyID0gNzApICU+JSANCiAgY3JvcChkYXRhX2dlbWluYXRhKQ0KDQpmdXR1cm8gPC0gZnV0dXJvW1tjKDEsIDcsIDEyLCAxNSwgMTkpXV0NCm5hbWVzKGZ1dHVybykgPC0gYygiYmlvMSIsICJiaW83IiwgImJpbzEyIiwgImJpbzE1IiwgImJpbzE5IikNCnBsb3QoZnV0dXJvKQ0KYGBgDQoNCi0gKipNYXBhIGRlIHByZWRpY2Npw7NuIGRlIGFidW5kYW5jaWE6KioNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpwcmVkaWNjaW9uX2Z1dHVyYSA8LSBwcmVkaWN0KGZ1dHVybywgbW9kZWxvMiwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCnByZWRpY2Npb25fZGZfZnV0dXJvIDwtIHByZWRpY2Npb25fZnV0dXJhICU+JSANCiAgYXMoIlNwYXRpYWxQaXhlbHNEYXRhRnJhbWUiKSAlPiUgDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgDQogIHJlbmFtZShBYnVuZGFuY2lhID0gbGF5ZXIpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV90aWxlKGRhdGEgPSBwcmVkaWNjaW9uX2RmX2Z1dHVybywgYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IEFidW5kYW5jaWEpKSArDQogIGdlb21fc2YoZGF0YSA9IG1hcGEsIGFscGhhID0gMCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCg0KDQojIEFuZXhvcw0KDQotIEVuIGVsIGVqZW1wbG8gZGUgY2xhc2Ugc2UgdHJhYmFqYSBjb24gbGEgZXNwZWNpZSBCb21idXMgYWZmaW5pcyAoYWJlam9ycm9zKSwgc2luIGVtYmFyZ28sIG9idHV2ZSBkaXJlY3RhbWVudGUgbGEgYmFzZSBkZSBkYXRvcyBkZSBbUFJFRElDVFMgZGF0YWJhc2VdKGh0dHBzOi8vZGF0YS5uaG0uYWMudWsvZGF0YXNldC90aGUtMjAxNi1yZWxlYXNlLW9mLXRoZS1wcmVkaWN0cy1kYXRhYmFzZSkgeSB2b3kgYSB0cmFiYWphciBjb24gZWwgZ8OpbmVybyBkZSBob3JtaWdhcyBbKlNvbGVub3BzaXMqLF0oaHR0cHM6Ly9lcy53aWtpcGVkaWEub3JnL3dpa2kvU29sZW5vcHNpcykgdGFtYmnDqW4gY29ub2NpZGEgY29tbyBob3JtaWdhIGNvbG9yYWRhIG8gZGUgZnVlZ28uLg0KLSAgRW4gZXN0ZSBjYXNvIGZpbHRybyBpbmZvcm1hY2nDs24gc8OzbG8gcGFyYSBDb2xvbWJpYS4NCi0gU2UgZmlsdHJhbiBkYXRvcyBzw7NsbyBkZSBhYnVuZGFuY2lhIGRlIGVzcGVjaWVzLg0KLSBNYW50ZW5nbyBzw7NsbyBsYXMgc2lndWllbnRlcyB2YXJpYWJsZXM6DQogIC0gKipSZWZlcmVuY2U6KiogcmVmZXJlbmNpYSBkZWwgZXN0dWRpbw0KICAtICoqU3R1ZHlfY29tbW9uX3RheG9uOioqIHRheMOzbg0KICAtICoqU2l0ZV9uYW1lOioqIG5vbWJyZSBkZSBzaXRpbw0KICAtIFNhbXBsaW5nX2VmZm9ydA0KICAtIExvbmdpdHVkZQ0KICAtIExhdGl0dWRlDQogIC0gU3BlY2llcw0KICAtICoqTWVhc3VyZW1lbnQ6KiogbWVkaWRhIGRlIGFidW5kYW5jaWENCiAgLSBFZmZvcnRfY29ycmVjdGVkX21lYXN1cmVtZW50DQoNCmBgYHtyfQ0KIyBCYXNlIGRlIGRhdG9zIHRvdGFsIGNvbiBtw6FzIGRlIDMgbWlsbG9uZXMgZGUgcmVnaXN0cm9zDQpkYXRvc190b3RhbCA8LSBmcmVhZCgiZGF0YWJhc2UuY3N2IiwgZW5jb2RpbmcgPSAiVVRGLTgiKQ0KIyBGaWx0cm8gc29sZW5vcHNpcyBwYXJhIENvbG9tYmlhOiA1NTEgcmVnaXN0cm9zIGRlIGFidW5kYW5jaWENCmRhdG9zX2NvbG9tYmlhIDwtIGRhdG9zX3RvdGFsICU+JSANCiAgZmlsdGVyKENvdW50cnkgPT0gIkNvbG9tYmlhIikgJT4lIA0KICBmaWx0ZXIoR2VudXMgPT0gIlNvbGVub3BzaXMiKSAlPiUgIA0KICBmaWx0ZXIoRGl2ZXJzaXR5X21ldHJpY190eXBlID09ICJBYnVuZGFuY2UiKSAlPiUgDQogIHNlbGVjdChSZWZlcmVuY2UsIFN0dWR5X2NvbW1vbl90YXhvbiwgU2l0ZV9uYW1lLCBTYW1wbGluZ19lZmZvcnQsDQogICAgICAgICBMb25naXR1ZGUsIExhdGl0dWRlLCBTcGVjaWVzLCBNZWFzdXJlbWVudCwgDQogICAgICAgICBFZmZvcnRfY29ycmVjdGVkX21lYXN1cmVtZW50KSANCiMgRXhwb3J0YW5kbyBkYXRvcyBkZSBzb2xlbm9wc2lzDQp3cml0ZS5jc3YoZGF0b3NfY29sb21iaWEsIGZpbGUgPSAic29sZW5vcHNpcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICBmaWxlRW5jb2RpbmcgPSAiVVRGLTgiKQ0KYGBgDQoNCg0K