Paquete spapstat

Caja de herramientas integral de código abierto para analizar patrones de puntos espaciales. Centrado principalmente en patrones de puntos bidimensionales, incluidos puntos marcados/multitipo, en cualquier región espacial. También admite patrones de puntos tridimensionales, patrones de puntos de espacio-tiempo en cualquier número de dimensiones, patrones de puntos en una red lineal y patrones de otros objetos geométricos. Admite datos covariables espaciales, como imágenes de píxeles. Documentación

library(spatstat)
library(maptools)

Datos

Datos de pinos suecos de Strand-Ripley

swedishpines
Planar point pattern: 71 points
window: rectangle = [0, 96] x [0, 100] units (one unit = 0.1 
metres)
class(swedishpines)
[1] "ppp"
summary(swedishpines)
Planar point pattern:  71 points
Average intensity 0.007395833 points per square unit (one unit 
= 0.1 metres)

Coordinates are integers
i.e. rounded to the nearest unit (one unit = 0.1 metres)

Window: rectangle = [0, 96] x [0, 100] units
Window area = 9600 square units
Unit of length: 0.1 metres
plot(swedishpines)

Más documentación

Para una introducción rápida a Spatstat, lea la viñeta del paquete Getting started with spatstat instalado con Spatstat. Para leer ese documento, puede:

vignette("datasets")

Generando coordenadas aleatorias

La función runiff obtiene números decimales aleatorios de 0 a 1. Esta función está incluida dentro del paquete de R llamado stats.

x <- runif(20)
y <- runif(20)

Creando patrón de puntos

ppp: Crea un patrón de puntos a partir de \((x, y)\) y la información de la ventana:

X <- ppp(x, y, c(0,1), c(0,1))
X <- ppp(x, y)
X <- ppp(x, y, window=owin(c(0,1),c(0,1)))
class(X)
[1] "ppp"
summary(X)
Planar point pattern:  20 points
Average intensity 20 points per square unit

Coordinates are given to 8 decimal places

Window: rectangle = [0, 1] x [0, 1] units
Window area = 1 square unit
plot(X)

Datos-vesicles

Convirtiendo DataFrame a PPP

Patrón de puntos de vesículas sinápticas observado en tejido cerebral de rata.

# Estableciendo nueva ubicacion
setwd("C:/Users/EQUIPO/Documents/EstadisticaEspacial/data/vesicles/") 
Error in setwd("C:/Users/EQUIPO/Documents/EstadisticaEspacial/data/vesicles/") : 
  cannot change working directory
class(df)
[1] "data.frame"
head(df)
         x        y
1 467.0168 776.0189
2 445.3418 827.4970
3 364.0606 911.4876
4 323.4200 914.1969
5 339.6762 957.5469
6 345.0950 873.5563
Y1 <- ppp(df$x, df$y,  c(22,587), c(11,1031))
class(Y1)
[1] "ppp"
plot(Y1)

summary(df)
       x               y         
 Min.   :277.4   Min.   : 55.33  
 1st Qu.:347.8   1st Qu.:440.06  
 Median :399.3   Median :637.84  
 Mean   :403.9   Mean   :597.57  
 3rd Qu.:461.6   3rd Qu.:762.47  
 Max.   :548.3   Max.   :957.55  

Datos-finpines

Editando un PPP

Datos de pinos filandenses.

class(df1)
[1] "data.frame"
head(df1)
          x         y diameter height
1 -1.993875 0.9297642        1    1.7
2 -1.019901 0.4120694        1    1.7
3 -4.914071 1.9854250        1    1.6
4 -4.469962 1.4523900        5    4.1
5 -4.303847 0.9148214        3    3.1
6 -3.814774 0.8108644        4    4.3
Y2 = as.ppp(df1, owin(c(-5,5), c(-8,2)))
plot(Y2)

edit(Y2)
Marked planar point pattern: 126 points
Mark variables: diameter, height 
window: rectangle = [-5, 5] x [-8, 2] units

** Removiendo punto fuera de la cuadrícula o ventana**

S = ppp(x=c(-0.2, runif(10)),y=c(0.3, runif(10)),
        window=square(1))
1 point was rejected as lying outside the specified window
plot(S)

# Agregando punto
as.ppp(S)
Planar point pattern: 10 points
window: rectangle = [0, 1] x [0, 1] units
plot(as.ppp(S))

Graficando y editando punto fuera de la cuadrícula o ventana

# Grafica los puntos rechazados por estar fuera de la ventana
# de observacion
plot(attr(S, "rejects"))

edit(attr(S, "rejects"))
Planar point pattern: 1 point
window: polygonal boundary
enclosing rectangle: [-0.3665029, 1] x [0, 1.1536944] units

Verificando existencia de puntos duplicados

Simulación de Procesos Poisson no-homogéneos

Un Proceso puntual Poisson homogéneo son caracterizados por las siguientes propiedades: Homogeneidad e Independencia. Además, se le llama “Proceso Poisson” porque el número de puntos que caen en cualquier región sigue una distribución de Poisson. En muchas pruebas estadísticas, el CSR sirve como hipótesis nula.

  • La intensidad puede ser una constante
pp <- rpoispp(5)
pp
Planar point pattern: 9 points
window: rectangle = [0, 1] x [0, 1] units
plot(pp)

summary(pp)
Planar point pattern:  9 points
Average intensity 9 points per square unit

Coordinates are given to 8 decimal places

Window: rectangle = [0, 1] x [0, 1] units
Window area = 1 square unit
  • La intensidad puede ser una función (x,y) o una imagen pixelada
lambda = function(x,y) { 100 *(x^2+y)}
X = rpoispp(lambda, win = square(1))
X
Planar point pattern: 78 points
window: rectangle = [0, 1] x [0, 1] units
plot(X)

summary(X)
Planar point pattern:  78 points
Average intensity 78 points per square unit

Coordinates are given to 8 decimal places

Window: rectangle = [0, 1] x [0, 1] units
Window area = 1 square unit

Tarea 1

Consultar las clases de objetos que se pueden obtener en el paquete spatstat.

Los principales tipos de datos espaciales admitidos por Spatstat son:

Tarea 2

Leer sección 3.1, 3.2 posiblemente 3.3
Libro PDF

Tarea 3

¿Cuáles serían los argumentos a considerar en la función PPP cuando la ventana de observación es un polígono?

# Librerias
library(GWmodel)
library(ggplot2)

# Leyendo coordenadas de los bordes
borde = readShapeSpatial(file.choose()) # Borde.shp 

# Encontrando coordenadas
Borde = fortify(borde)

# Longitud y latitud
Borde$long = (Borde$long)/1000
Borde$lat = (Borde$lat)/1000
head(Borde)

# Leyendo coordenadas desde los datos
datos = read.csv(file.choose(), header=T) # Indicador.csv
datos$x = (datos$x)/1000
datos$y = (datos$y)/1000

La función rev() coloca las componentes de un vector en orden inverso a como han sido introducidas

# Matriz de coordenadas
Bor = cbind(Borde$long,Borde$lat)
colnames(Bor) = c("Long","Lat") # Matriz de coordenadas

# Reversa
lon = rev(Bor[,1])
lat = rev(Bor[,2])

# Poligono
p=list(x=lon, y=lat)
Z = owin(poly=p)
# Formamos objeto PPP
S = ppp(datos$x, datos$y, window=Z)
S
plot(S)

Tarea 4

Usando la función clickpoly crear una ventana de observación (ver sesión 3.9).

#-------------------------------------------------------------
# Dibuja manualmente ventana de graficacion
# clickpoly()
plot(S) # mapa de Medellín (ventana de observación)
prueba = clickpoly(add=TRUE) # crear otra ventana de observación 
class(prueba)

# Convierte a objeto poligono espacial
library(rgdal)
plot(prueba)
pruebaSp = as(prueba, "SpatialPolygons")
pruebaSp1 = as(pruebaSp, "SpatialPolygonsDataFrame")
class(pruebaSp)
writePolyShape(pruebaSp1,'pruebaSp.shp') # guardar 
LS0tDQp0aXRsZTogIioqUHJvY2Vzb3MgZGUgcGF0cm9uZXMgcHVudHVhbGVzKioiDQphdXRob3I6ICJFaWxpbiBMdW5hIE0uIg0KZGF0ZTogIjAyL01heW8vMjAyMSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjICoqUGFxdWV0ZSBzcGFwc3RhdCoqDQoNCkNhamEgZGUgaGVycmFtaWVudGFzIGludGVncmFsIGRlIGPDs2RpZ28gYWJpZXJ0byBwYXJhIGFuYWxpemFyIHBhdHJvbmVzIGRlIHB1bnRvcyBlc3BhY2lhbGVzLiBDZW50cmFkbyBwcmluY2lwYWxtZW50ZSBlbiBwYXRyb25lcyBkZSBwdW50b3MgYmlkaW1lbnNpb25hbGVzLCBpbmNsdWlkb3MgcHVudG9zIG1hcmNhZG9zL211bHRpdGlwbywgZW4gY3VhbHF1aWVyIHJlZ2nDs24gZXNwYWNpYWwuIFRhbWJpw6luIGFkbWl0ZSBwYXRyb25lcyBkZSBwdW50b3MgdHJpZGltZW5zaW9uYWxlcywgcGF0cm9uZXMgZGUgcHVudG9zIGRlIGVzcGFjaW8tdGllbXBvIGVuIGN1YWxxdWllciBuw7ptZXJvIGRlIGRpbWVuc2lvbmVzLCBwYXRyb25lcyBkZSBwdW50b3MgZW4gdW5hIHJlZCBsaW5lYWwgeSBwYXRyb25lcyBkZSBvdHJvcyBvYmpldG9zIGdlb23DqXRyaWNvcy4gQWRtaXRlIGRhdG9zIGNvdmFyaWFibGVzIGVzcGFjaWFsZXMsIGNvbW8gaW3DoWdlbmVzIGRlIHDDrXhlbGVzLiBbRG9jdW1lbnRhY2nDs25dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zcGF0c3RhdC9zcGF0c3RhdC5wZGYpDQoNCmBgYHtyfQ0KbGlicmFyeShzcGF0c3RhdCkNCmxpYnJhcnkobWFwdG9vbHMpDQpgYGANCg0KDQojIyAqKkRhdG9zKioNCg0KRGF0b3MgZGUgcGlub3Mgc3VlY29zIGRlIFN0cmFuZC1SaXBsZXkNCmBgYHtyfQ0Kc3dlZGlzaHBpbmVzDQpjbGFzcyhzd2VkaXNocGluZXMpDQpzdW1tYXJ5KHN3ZWRpc2hwaW5lcykNCnBsb3Qoc3dlZGlzaHBpbmVzKQ0KYGBgDQoNCiMjICoqTcOhcyBkb2N1bWVudGFjacOzbioqICANClBhcmEgdW5hIGludHJvZHVjY2nDs24gcsOhcGlkYSBhIFNwYXRzdGF0LCBsZWEgbGEgdmnDsWV0YSBkZWwgcGFxdWV0ZSAqR2V0dGluZyBzdGFydGVkIHdpdGggc3BhdHN0YXQqIGluc3RhbGFkbyBjb24gU3BhdHN0YXQuIFBhcmEgbGVlciBlc2UgZG9jdW1lbnRvLCBwdWVkZToNCg0KYGBge3J9DQp2aWduZXR0ZSgiZGF0YXNldHMiKQ0KYGBgDQoNCiMjICoqR2VuZXJhbmRvIGNvb3JkZW5hZGFzIGFsZWF0b3JpYXMqKg0KDQpMYSBmdW5jacOzbiBgcnVuaWZmYCBvYnRpZW5lIG7Dum1lcm9zIGRlY2ltYWxlcyBhbGVhdG9yaW9zIGRlIDAgYSAxLiBFc3RhIGZ1bmNpw7NuIGVzdMOhIGluY2x1aWRhIGRlbnRybyBkZWwgcGFxdWV0ZSBkZSBSIGxsYW1hZG8gc3RhdHMuDQoNCmBgYHtyfQ0KeCA8LSBydW5pZigyMCkNCnkgPC0gcnVuaWYoMjApDQpgYGANCg0KIyMgKipDcmVhbmRvIHBhdHLDs24gZGUgcHVudG9zKioNCg0KYHBwcGA6IENyZWEgdW4gcGF0csOzbiBkZSBwdW50b3MgYSBwYXJ0aXIgZGUgJCh4LCB5KSQgeSBsYSBpbmZvcm1hY2nDs24gZGUgbGEgdmVudGFuYTogIA0KDQoqIGBwcHAgKHgsIHksIHhsaW0sIHlsaW0pYCBwYXJhIHZlbnRhbmEgcmVjdGFuZ3VsYXINCiogYHBwcCAoeCwgeSwgcG9seSlgIHBhcmEgdmVudGFuYSBwb2xpZ29uYWwNCiogYHBwcCAoeCwgeSwgbcOhc2NhcmEpYCBwYXJhIGxhIHZlbnRhbmEgZGUgaW1hZ2VuIGJpbmFyaWENCg0KYGBge3J9DQpYIDwtIHBwcCh4LCB5LCBjKDAsMSksIGMoMCwxKSkNClggPC0gcHBwKHgsIHkpDQpYIDwtIHBwcCh4LCB5LCB3aW5kb3c9b3dpbihjKDAsMSksYygwLDEpKSkNCmBgYA0KDQpgYGB7cn0NCmNsYXNzKFgpDQpzdW1tYXJ5KFgpDQpwbG90KFgpDQpgYGANCg0KIyMgKipEYXRvcy12ZXNpY2xlcyoqDQojIyMgKipDb252aXJ0aWVuZG8gRGF0YUZyYW1lIGEgUFBQKioNCg0KUGF0csOzbiBkZSBwdW50b3MgZGUgdmVzw61jdWxhcyBzaW7DoXB0aWNhcyBvYnNlcnZhZG8gZW4gdGVqaWRvIGNlcmVicmFsIGRlIHJhdGEuDQoNCmBgYHtyfQ0KIyBFc3RhYmxlY2llbmRvIG51ZXZhIHViaWNhY2lvbg0Kc2V0d2QoIkM6L1VzZXJzL0VRVUlQTy9Eb2N1bWVudHMvRXN0YWRpc3RpY2FFc3BhY2lhbC9kYXRhL3Zlc2ljbGVzLyIpIA0KY29weUV4YW1wbGVGaWxlcygidmVzaWNsZXMiKQ0KZGYgPSByZWFkLnRhYmxlKCJ2ZXNpY2xlcy50eHQiLCBoZWFkZXI9VFJVRSkNCmBgYA0KDQpgYGB7ciBwYWdlZC5wcmludD1GQUxTRX0NCmNsYXNzKGRmKQ0KaGVhZChkZikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBDb252aXJ0aWVuZG8gYSBwYXRyb25lcyBkZSBwdW50b3MgZXNwYWNpYWxlcw0KWTEgPC0gcHBwKGRmJHgsIGRmJHksICBjKDIyLDU4NyksIGMoMTEsMTAzMSkpDQpjbGFzcyhZMSkNCnBsb3QoWTEpDQpzdW1tYXJ5KGRmKQ0KYGBgDQojIyAqKkRhdG9zLWZpbnBpbmVzKioNCiMjIyAqKkVkaXRhbmRvIHVuIFBQUCoqDQoNCkRhdG9zIGRlIHBpbm9zIGZpbGFuZGVuc2VzLg0KDQpgYGB7cn0NCmNvcHlFeGFtcGxlRmlsZXMoJ2ZpbnBpbmVzJykNCmRmMSA9IHJlYWQudGFibGUoImZpbnBpbmVzLnR4dCIsIGhlYWRlcj1UUlVFKQ0KYGBgDQoNCmBgYHtyIHBhZ2VkLnByaW50PUZBTFNFfQ0KY2xhc3MoZGYxKQ0KaGVhZChkZjEpDQpZMiA9IGFzLnBwcChkZjEsIG93aW4oYygtNSw1KSwgYygtOCwyKSkpDQpwbG90KFkyKQ0KZWRpdChZMikNCmBgYA0KDQojIyAqKiBSZW1vdmllbmRvIHB1bnRvIGZ1ZXJhIGRlIGxhIGN1YWRyw61jdWxhIG8gdmVudGFuYSoqDQoNCmBgYHtyfQ0KUyA9IHBwcCh4PWMoLTAuMiwgcnVuaWYoMTApKSx5PWMoMC4zLCBydW5pZigxMCkpLA0KICAgICAgICB3aW5kb3c9c3F1YXJlKDEpKQ0KcGxvdChTKQ0KDQojIFJlbW92aWVuZG8gcHVudG8NCmFzLnBwcChTKQ0KcGxvdChhcy5wcHAoUykpDQpgYGANCg0KIyMjICoqR3JhZmljYW5kbyB5IGVkaXRhbmRvIHB1bnRvIGZ1ZXJhIGRlIGxhIGN1YWRyw61jdWxhIG8gdmVudGFuYSoqDQoNCmBgYHtyfQ0KIyBHcmFmaWNhIGxvcyBwdW50b3MgcmVjaGF6YWRvcyBwb3IgZXN0YXIgZnVlcmEgZGUgbGEgdmVudGFuYQ0KIyBkZSBvYnNlcnZhY2lvbg0KcGxvdChhdHRyKFMsICJyZWplY3RzIikpDQplZGl0KGF0dHIoUywgInJlamVjdHMiKSkNCmBgYA0KDQojIyMgKipWZXJpZmljYW5kbyBleGlzdGVuY2lhIGRlIHB1bnRvcyBkdXBsaWNhZG9zKioNCg0KYGBge3J9DQojIFZlcmlmaWNhIHNpIGhheSBleGlzdGVuY2lhIGRlIHB1bnRvcyBkdXBsaWNhZG9zDQpkdXBsaWNhdGVkKFMpDQp0YWJsZShkdXBsaWNhdGVkKFMpKQ0KDQptdWx0aXBsaWNpdHkoUykNCnRhYmxlKG11bHRpcGxpY2l0eShTKSkNCmBgYA0KDQojIyMgKipTaW11bGFjacOzbiBkZSBQcm9jZXNvcyBQb2lzc29uIG5vLWhvbW9nw6luZW9zKioNCg0KVW4gUHJvY2VzbyBwdW50dWFsIFBvaXNzb24gaG9tb2fDqW5lbyBzb24gY2FyYWN0ZXJpemFkb3MgcG9yIGxhcyBzaWd1aWVudGVzIHByb3BpZWRhZGVzOiBIb21vZ2VuZWlkYWQgZSBJbmRlcGVuZGVuY2lhLiBBZGVtw6FzLCBzZSBsZSBsbGFtYSDigJxQcm9jZXNvIFBvaXNzb27igJ0gcG9ycXVlIGVsIG7Dum1lcm8gZGUgcHVudG9zIHF1ZSBjYWVuIGVuIGN1YWxxdWllciByZWdpw7NuIHNpZ3VlIHVuYSBkaXN0cmlidWNpw7NuIGRlIFBvaXNzb24uIEVuIG11Y2hhcyBwcnVlYmFzIGVzdGFkw61zdGljYXMsIGVsIENTUiBzaXJ2ZSBjb21vIGhpcMOzdGVzaXMgbnVsYS4NCg0KKiBMYSBpbnRlbnNpZGFkIHB1ZWRlIHNlciB1bmEgY29uc3RhbnRlDQoNCmBgYHtyfQ0KcHAgPC0gcnBvaXNwcCg1KQ0KcHANCnBsb3QocHApDQpzdW1tYXJ5KHBwKQ0KYGBgDQoNCiogTGEgaW50ZW5zaWRhZCBwdWVkZSBzZXIgdW5hIGZ1bmNpw7NuICh4LHkpIG8gdW5hIGltYWdlbiBwaXhlbGFkYQ0KYGBge3J9DQpsYW1iZGEgPSBmdW5jdGlvbih4LHkpIHsxMDAgKih4XjIreSl9DQpYID0gcnBvaXNwcChsYW1iZGEsIHdpbiA9IHNxdWFyZSgxKSkNClgNCnBsb3QoWCkNCnN1bW1hcnkoWCkNCmBgYA0KDQoNCg0KDQoNCg0KDQojICoqVGFyZWEgMSoqDQoqKkNvbnN1bHRhciBsYXMgY2xhc2VzIGRlIG9iamV0b3MgcXVlIHNlIHB1ZWRlbiBvYnRlbmVyIGVuIGVsIHBhcXVldGUgc3BhdHN0YXQuKioNCg0KTG9zIHByaW5jaXBhbGVzIHRpcG9zIGRlIGRhdG9zIGVzcGFjaWFsZXMgYWRtaXRpZG9zIHBvciBTcGF0c3RhdCBzb246DQoNCisgYHBwcGA6IHBhdHLDs24gZGUgcHVudG9zIHBwcCAgDQogICoqUGFyYSBjcmVhciB1biBwYXRyw7NuIGRlIHB1bnRvczoqKiBDcmVhIHVuIHBhdHLDs24gZGUgcHVudG9zIGEgcGFydGlyIGRlICh4LCB5KSB5IGxhIGluZm9ybWFjacOzbiBkZSBsYSB2ZW50YW5hLg0KICANCisgYG93aW5gOiB2ZW50YW5hIHByb3BpYSAocmVnacOzbiBlc3BhY2lhbCkgIA0KICAqKlBhcmEgY3JlYXIgdW5hIHZlbnRhbmE6KiogVW4gb2JqZXRvIGRlIGNsYXNlICJvd2luIiBkZXNjcmliZSB1bmEgcmVnacOzbiBlc3BhY2lhbCAodW5hIHZlbnRhbmEgZGUgb2JzZXJ2YWNpw7NuKS4NCiAgDQorIGBpbWA6IGltYWdlbiBkZSBww614ZWxlcyAgDQogICoqSW3DoWdlbmVzIGRlIHDDrXhlbGVzOioqIHVuIG9iamV0byBkZSBsYSBjbGFzZSAiaW0iIHJlcHJlc2VudGEgdW5hIGltYWdlbiBkZSBww614ZWxlcy4gDQogIA0KKyBgcHNwYDpwYXRyw7NuIGRlIHNlZ21lbnRvIGRlIGzDrW5lYSAgDQogICoqUGF0cm9uZXMgZGUgc2VnbWVudG8gZGUgbMOtbmVhKiogVW4gb2JqZXRvIGRlIGxhIGNsYXNlICJwc3AiIHJlcHJlc2VudGEgdW4gcGF0csOzbiBkZSBzZWdtZW50b3MgZGUgbMOtbmVhIHJlY3RhLg0KDQorIGB0ZXNzYDogdGVzZWxhY2nDs24gIA0KICAqKlRlc2VsYWNpb25lczoqKiBVbiBvYmpldG8gZGUgbGEgY2xhc2UgInRlc3MiIHJlcHJlc2VudGEgdW5hIHRlc2VsYWNpw7NuLg0KICANCisgYHBwM2A6IHBhdHLDs24gZGUgcHVudG9zIHRyaWRpbWVuc2lvbmFsICANCiAgKipQYXRyb25lcyBkZSBwdW50b3MgdHJpZGltZW5zaW9uYWxlcyoqIFVuIG9iamV0byBkZSBjbGFzZSAicHAzIiByZXByZXNlbnRhIHVuIHBhdHLDs24gZGUgcHVudG9zIHRyaWRpbWVuc2lvbmFsIGVuIHVuYSBjYWphIHJlY3Rhbmd1bGFyLiBMYSBjYWphIGVzdMOhIHJlcHJlc2VudGFkYSBwb3IgdW4gb2JqZXRvIGRlIGNsYXNlICJjYWphMyIuDQogIA0KKyBgcHB4YDogcGF0csOzbiBkZSBwdW50b3MgZW4gY3VhbHF1aWVyIG7Dum1lcm8gZGUgZGltZW5zaW9uZXMuICANCiAgKipQYXRyb25lcyBkZSBwdW50b3MgZGUgZXNwYWNpby10aWVtcG8gbXVsdGlkaW1lbnNpb25hbGVzKioNClVuIG9iamV0byBkZSBjbGFzZSAicHB4IiByZXByZXNlbnRhIHVuIHBhdHLDs24gZGUgcHVudG9zIGVuIHVuIGVzcGFjaW8geSAvIG8gdGllbXBvIG11bHRpZGltZW5zaW9uYWwuICANCg0KKyBgbHBwYDogcGF0csOzbiBkZSBwdW50b3MgZW4gdW5hIHJlZCBsaW5lYWwgIA0KICAqKlBhdHJvbmVzIGRlIHB1bnRvcyBlbiB1bmEgcmVkIGxpbmVhbDoqKiB1biBvYmpldG8gZGUgY2xhc2UgImxwcCIgcmVwcmVzZW50YSB1biBwYXRyw7NuIGRlIHB1bnRvcyBlbiB1bmEgcmVkIGxpbmVhbCAocG9yIGVqZW1wbG8sIGFjY2lkZW50ZXMgZGUgdHLDoWZpY28gZW4gdW5hIHJlZCBkZSBjYXJyZXRlcmFzKS4NCiAgDQojICoqVGFyZWEgMioqDQoqKkxlZXIgc2VjY2nDs24gMy4xLCAzLjIgcG9zaWJsZW1lbnRlIDMuMyoqICANCltMaWJybyBQREZdKGh0dHBzOi8vYXphaWV6bm90ZXNibG9nLmZpbGVzLndvcmRwcmVzcy5jb20vMjAxNy8wOC9zcGF0aWFsLXBvaW50LXBhdHRlcm5zX2NoYXBtYW4taGFsbC1jcmMtMjAxNi5wZGYpDQoNCiogKiozLjEgRU5DVUVTVEFTIFkgRVhQRVJJTUVOVE9TKiogIA0KKiozLjEuMSBEaXNlw7FvIGRlIHVuIGV4cGVyaW1lbnRvIG8gZW5jdWVzdGEqKiAgDQogICogRWwgY29uc2VqbyBtw6FzIGltcG9ydGFudGUgc29icmUgZWwgZGlzZcOxbyBkZSB1biBleHBlcmltZW50byBlcyBwbGFuaWZpY2FyIHRvZG8gZWwgZXhwZXJpbWVudG8sIGluY2x1aWRvIGVsIGFuw6FsaXNpcyBkZSBkYXRvcyAgDQoNCiAgKiozLjEuMiBRdcOpIHNlIG5lY2VzaXRhIHJlZ2lzdHJhcioqICANCiAgICAqIEFkZW3DoXMgZGUgbGFzIGNvb3JkZW5hZGFzIGRlIGxvcyBwdW50b3MgZW4gc8OtLCBlcyBuZWNlc2FyaW8gcmVnaXN0cmFyIG90cm9zIGVsZW1lbnRvcywgY29tbyBsYSB2ZW50YW5hIGRlIG9ic2VydmFjacOzbiwgY292YXJpYWJsZXMgZXNwYWNpbGFlcy4gIA0KICAgICogRXMgdW5hIGJ1ZW5hIHByw6FjdGljYSByZWdpc3RyYXIgbGEgaG9yYSBhIGxhIHF1ZSBzZSByZWFsaXrDsyBjYWRhIG9ic2VydmFjacOzbiB5IGJ1c2NhciBjdWFscXVpZXIgdGVuZGVuY2lhIGFwYXJlbnRlIGEgbG8gbGFyZ28gZGVsIHRpZW1wbyAgDQogICAgDQogICoqMy4xLjMgUmllc2dvcyB5IGJ1ZW5hcyBwcsOhY3RpY2FzKiogICAgDQogICAgKiBFbCBtYXlvciByaWVzZ28gYWwgcmVnaXN0cmFyIG9ic2VydmFjaW9uZXMgZXMgcXVlIHNlIG9taXRhIG8gc2UgcGllcmRhIGluZm9ybWFjacOzbiBpbXBvcnRhbnRlLiAgDQogICAgKiDCv1F1w6kgaW5mb3JtYWNpw7NuIHJlc3VsdGFyw6EgcmV0cm9zcGVjdGl2YW1lbnRlIHJlbGV2YW50ZSBwYXJhIG51ZXN0cm8gYW7DoWxpc2lzPyAgIA0KICAgICogTm8gY29uZnVuZGEgZXN0YXMgZG9zIGlkZWFzOiAiZmFsdGFudGUiIG8gImlub2JzZXJ2YWJsZSIgKE5BKSBlcyB1biBjb25jZXB0byBjb21wbGV0YW1lbnRlIGRpZmVyZW50ZSBjb25jZXB0byBkZSAiYXVzZW50ZSIgKDApLiAgIA0KICAgICogUmVnaXN0cmUgbGEgc2VjdWVuY2lhIGRlIG9wZXJhY2lvbmVzIGFwbGljYWRhcyBhIGxvcyBkYXRvcywgZXhwbGlxdWUgbG9zIG5vbWJyZXMgZGUgbGFzIHZhcmlhYmxlcywgaW5kaXF1ZSBsYXMgdW5pZGFkZXMgZW4gZWwgcXVlIHNlIGV4cHJlc2FuLCBldGMuICANCiAgDQoqICoqMy4yIE1BTkVKTyBERSBEQVRPUyoqIA0KDQogICoqMy4yLjEgRm9ybWF0b3MgZGUgYXJjaGl2byBkZSBkYXRvcyoqICANCiAgKiBmb3JtYXRvIGRlIHRhYmxhOiBsb3MgZGF0b3Mgc2Ugb3JnYW5pemFuIGVuIGZpbGFzIHkgY29sdW1uYSAgDQogICogVmFsb3JlcyBzZXBhcmFkb3MgcG9yIGNvbWFzIChjc3YpOiBlbCBzb2Z0d2FyZSBkZSBob2phIGRlIGPDoWxjdWxvLiAgDQogICogc2hhcGVmaWxlczogVW4gc2hhcGVmaWxlIGVzIHVuIGZvcm1hdG8gZGUgYXJjaGl2byBwb3B1bGFyIHBhcmEgY29tcGFydGlyIGRhdG9zIHZlY3RvcmlhbGVzIGVudHJlIHBhcXVldGVzIGRlIHNvZnR3YXJlIGRlbCBzaXN0ZW1hIGRlIGluZm9ybWFjacOzbiBnZW9ncsOhZmljYSAoR0lTKS4gICANCiAgDQogICoqMy4yLjIgTGVjdHVyYSBkZSBkYXRvcyBlbiBSKiogIA0KICAqIExvcyBkYXRvcyBlbiB1biBhcmNoaXZvIGRlIHRleHRvIGVuIGZvcm1hdG8gZGUgdGFibGEgc2UgcHVlZGVuIGxlZXIgZW4gUiB1c2FuZG8gZWwgY29tYW5kbyByZWFkLnRhYmxlLiAgDQogICogTG9zIGFyY2hpdm9zIGRlIGRhdG9zIG9yaWdpbmFsZXMgcGFyYSBlbCBjb25qdW50byBkZSBkYXRvcyBkZSB2ZXPDrWN1bGFzIHNlIGluc3RhbGFuIGVuIFNwYXRzdGF0IGNvbW8gZWplbXBsbyBwcsOhY3RpY28uICANCiAgDQoqICoqMy4zLiBJTkdSRVNBUyBEQVRPUyBERSBQQVRST05FUyBERSBQVU5UT1MgRU4gU3BhdHN0YXQqKiAgDQogICogMy4zLjEgQ3JlYXIgdW4gb2JqZXRvICJwcHAiICANCiAgKiAzLjMuMiBNYXJjYXMgIA0KICAgIE1hcmNhcyBjYXRlZ8OzcmljYXMsIG1hcmNhcyBtdWx0aXZhcmlhZGFzICANCiAgKiAzLjMuMyBVbmlkYWRlcyAgDQogIA0KIyAqKlRhcmVhIDMqKg0KKirCv0N1w6FsZXMgc2Vyw61hbiBsb3MgYXJndW1lbnRvcyBhIGNvbnNpZGVyYXIgZW4gbGEgZnVuY2nDs24gUFBQIGN1YW5kbyBsYSB2ZW50YW5hIGRlIG9ic2VydmFjacOzbiBlcyB1biBwb2zDrWdvbm8/KioNCg0KDQpgYGB7cn0NCiMgTGlicmVyaWFzDQpsaWJyYXJ5KEdXbW9kZWwpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgTGV5ZW5kbyBjb29yZGVuYWRhcyBkZSBsb3MgYm9yZGVzDQpib3JkZSA9IHJlYWRTaGFwZVNwYXRpYWwoZmlsZS5jaG9vc2UoKSkgIyBCb3JkZS5zaHAgDQoNCiMgRW5jb250cmFuZG8gY29vcmRlbmFkYXMNCkJvcmRlID0gZm9ydGlmeShib3JkZSkNCg0KIyBMb25naXR1ZCB5IGxhdGl0dWQNCkJvcmRlJGxvbmcgPSAoQm9yZGUkbG9uZykvMTAwMA0KQm9yZGUkbGF0ID0gKEJvcmRlJGxhdCkvMTAwMA0KaGVhZChCb3JkZSkNCg0KIyBMZXllbmRvIGNvb3JkZW5hZGFzIGRlc2RlIGxvcyBkYXRvcw0KZGF0b3MgPSByZWFkLmNzdihmaWxlLmNob29zZSgpLCBoZWFkZXI9VCkgIyBJbmRpY2Fkb3IuY3N2DQpkYXRvcyR4ID0gKGRhdG9zJHgpLzEwMDANCmRhdG9zJHkgPSAoZGF0b3MkeSkvMTAwMA0KYGBgDQoNCkxhIGZ1bmNpw7NuIHJldigpIGNvbG9jYSBsYXMgY29tcG9uZW50ZXMgZGUgdW4gdmVjdG9yIGVuIG9yZGVuIGludmVyc28gYSBjb21vIGhhbiBzaWRvIGludHJvZHVjaWRhcyANCg0KYGBge3IgcGFnZWQucHJpbnQ9RkFMU0V9DQojIE1hdHJpeiBkZSBjb29yZGVuYWRhcw0KQm9yID0gY2JpbmQoQm9yZGUkbG9uZyxCb3JkZSRsYXQpDQpjb2xuYW1lcyhCb3IpID0gYygiTG9uZyIsIkxhdCIpICMgTWF0cml6IGRlIGNvb3JkZW5hZGFzDQoNCiMgUmV2ZXJzYQ0KbG9uID0gcmV2KEJvclssMV0pDQpsYXQgPSByZXYoQm9yWywyXSkNCg0KIyBQb2xpZ29ubw0KcD1saXN0KHg9bG9uLCB5PWxhdCkNClogPSBvd2luKHBvbHk9cCkNCmBgYA0KDQpgYGB7cn0NCiMgRm9ybWFtb3Mgb2JqZXRvIFBQUA0KUyA9IHBwcChkYXRvcyR4LCBkYXRvcyR5LCB3aW5kb3c9WikNClMNCmBgYA0KDQpgYGB7cn0NCnBsb3QoUykNCmBgYA0KDQojICoqVGFyZWEgNCoqDQoNCioqVXNhbmRvIGxhIGZ1bmNpw7NuIGNsaWNrcG9seSBjcmVhciB1bmEgdmVudGFuYSBkZSBvYnNlcnZhY2nDs24gKHZlciBzZXNpw7NuIDMuOSkuKioNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBEaWJ1amEgbWFudWFsbWVudGUgdmVudGFuYSBkZSBncmFmaWNhY2lvbg0KIyBjbGlja3BvbHkoKQ0KcGxvdChTKSAjIG1hcGEgZGUgTWVkZWxsw61uICh2ZW50YW5hIGRlIG9ic2VydmFjacOzbikNCnBydWViYSA9IGNsaWNrcG9seShhZGQ9VFJVRSkgIyBjcmVhciBvdHJhIHZlbnRhbmEgZGUgb2JzZXJ2YWNpw7NuIA0KY2xhc3MocHJ1ZWJhKQ0KYGBgDQoNCiFbXShDOlxVc2Vyc1xFUVVJUE9cRG9jdW1lbnRzXEdpdEh1YlxFc3RhZGlzdGljYUVzcGFjaWFsXGltYWdlc1xtZWRlbGxpbi5wbmcpDQoNCmBgYHtyfQ0KIyBDb252aWVydGUgYSBvYmpldG8gcG9saWdvbm8gZXNwYWNpYWwNCmxpYnJhcnkocmdkYWwpDQpwbG90KHBydWViYSkNCnBydWViYVNwID0gYXMocHJ1ZWJhLCAiU3BhdGlhbFBvbHlnb25zIikNCnBydWViYVNwMSA9IGFzKHBydWViYVNwLCAiU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lIikNCmNsYXNzKHBydWViYVNwKQ0Kd3JpdGVQb2x5U2hhcGUocHJ1ZWJhU3AxLCdwcnVlYmFTcC5zaHAnKSAjIGd1YXJkYXIgDQpgYGANCg0KDQoNCg0KDQoNCg0KDQo=