¿Qué es R?

R es un lenguaje de programación (Derivado del lenguaje S),así como tambien es el nombre del software que usa este lenguaje para la computación estadística.

Comenzando a escribir

Esta es una de las tareas encargadas para el curso de Geomática básica, el objetivo es tener una introducción a R y aprender algunas de sus funciones para el análisis de datos ;Este documento ha sido realizado por Angie Alejandra Juyo Buitrago

Paquetes en R

Los paquetes en R nos proporcionan funciones adicionales y pueden instalarse de la siguiente manera

#install.packages()

Una vez instalado ,cuando necesitemos emplear sus funciones, debemos cargarlo:

#library(packages-name)

En este caso utilizaremos el paquete dplyr

#install.packages("dplyr")
#library(dplyr)

Directorio de trabajo

Es la carpeta en nuestro equipo donde se encuentran los archivos con los que trabajaremos en R

Para conocer donde está nuestro directorio de trabajo emplearemos la funcion

getwd()
[1] "C:/Users/lmontanap/Desktop"

Para cambiar el directorio de trabajo, usamos la funci?n setwd()

setwd("C:/Users/lmontanap/Desktop/GB-2")
The working directory was changed to C:/Users/lmontanap/Desktop/GB-2 inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.

Anáisis de datos (Registros NBN Gateaway 2000-2016) en R

Importar y verificar datos Podemos importar nuestros datos en RStudio , dando click en el botón “Import Dataset”.También podemos usar el comando read.csv()con la ruta de acceso al documento que se desee importar

read.csv("C:/Users/lmontanap/Desktop/GB-2/edidiv.csv")

¿Cómo saber si se han cometido errores en los codigos?

Se recomienda ejecutar el código y verificar la salida en la consola,para tener una vista de todos los datos que se han importado.Aunque ,existen comandos que nos permiten tener una vista rápida

head(edidiv)  # Muestra las primeras filas de toda la tabla que fue importada
tail(edidiv) # Muestra las ultimas filas 

Una vez importados podemos usar el comando str(object.name) para mostrar la estructura de los datos , es decir,nos indica que tipo de variables son

str(edidiv)
'data.frame':   25684 obs. of  5 variables:
 $ organisationName: Factor w/ 28 levels "BATS & The Millennium Link",..: 14 14 14 8 8 28 28 28 28 28 ...
 $ gridReference   : Factor w/ 1938 levels "NT200701","NT200712",..: 1314 569 569 1412 1412 1671 1671 1671 1671 1671 ...
 $ year            : int  2000 2000 2000 2000 2000 2001 2001 2001 2001 2001 ...
 $ taxonName       : Factor w/ 1275 levels "Acarospora fuscata",..: 1126 1126 1127 192 193 1202 365 977 472 947 ...
 $ taxonGroup      : Factor w/ 11 levels "Beetle","Bird",..: 2 2 2 2 2 2 2 2 2 2 ...

Cuando deseemos acceder solo a una columna ,debemos agregar el nombre del objeto (El archivo que importamos) unido a la variable mediante el simbolo $

Vimos como el comando str() nos muestra la estructura de nuestros datos , si queremos conocer el tipo de variable de una columna en específico usaremos :

class(edidiv$taxonGroup)
[1] "factor"

Como nuestra variable es de carácter, se debe convertir a una variable de factor (categorica) mediante el siguiente comando

edidiv$taxonGroup <- as.factor(edidiv$taxonGroup)
class(edidiv$taxonGroup)
[1] "factor"

Existen otras instrucciones que tambien pueden ser útiles,por ejemplo si queremos saber el número de filas o columnas usaremos dim()

dim(edidiv)
[1] 25684     5

El comando summary ()nos permite ver un resumen de los datos importados

summary(edidiv)
                                             organisationName gridReference  
 Biological Records Centre                           :6744    NT2673 : 2741  
 RSPB                                                :5809    NT2773 : 2031  
 Butterfly Conservation                              :3000    NT2873 : 1247  
 Scottish Wildlife Trust                             :2070    NT2570 : 1001  
 Conchological Society of Great Britain &amp; Ireland:1998    NT27   :  888  
 The Wildlife Information Centre                     :1860    NT2871 :  767  
 (Other)                                             :4203    (Other):17009  
      year                      taxonName                taxonGroup  
 Min.   :2000   Maniola jurtina      : 1710   Butterfly       :9670  
 1st Qu.:2006   Aphantopus hyperantus: 1468   Bird            :7366  
 Median :2009   Turdus merula        : 1112   Flowering.Plants:2625  
 Mean   :2009   Lycaena phlaeas      :  972   Mollusc         :2226  
 3rd Qu.:2011   Aglais urticae       :  959   Hymenopteran    :1391  
 Max.   :2016   Aglais io            :  720   Mammal          : 960  
                (Other)              :18743   (Other)         :1446  

Summary (-$-) muestra el resumen de una variable en específico

summary(edidiv$taxonGroup)
          Beetle             Bird        Butterfly        Dragonfly 
             426             7366             9670              421 
Flowering.Plants           Fungus     Hymenopteran           Lichen 
            2625              334             1391              140 
       Liverwort           Mammal          Mollusc 
             125              960             2226 

Calcular la riqueza de especie Para saber cuantas especies tenemos de cada grupo taxonómico,debemos dividir edidiv (Nuestros datos) en varios objetos,es decir,cada uno con filas para cierto grupo

Beetle <- filter(edidiv, taxonGroup == "Beetle")
Bird <- filter(edidiv, taxonGroup == "Bird")
Butterfly <- filter(edidiv, taxonGroup == "Butterfly")
Flowering.Plants <- filter(edidiv, taxonGroup == "Flowering.Plants")
Fungus <- filter(edidiv, taxonGroup == "Fungus")
Hymenopteran <- filter(edidiv, taxonGroup == "Hymenopteran")
Lichen <- filter(edidiv, taxonGroup == "Lichen")
Liverwort <- filter(edidiv, taxonGroup == "Liverwort")
Mammal <- filter(edidiv, taxonGroup == "Mammal")
Mollusc <- filter(edidiv, taxonGroup == "Mollusc")

Una vez hayamos creado los objetos para cada taxón ,podemos calcular el número de especies diferentes para cada grupo

a <- length(unique(Beetle$taxonName))
b <- length(unique(Bird$taxonName))
c <- length(unique(Butterfly$taxonName))
d <- length(unique(Flowering.Plants$taxonName))
e <- length(unique(Fungus$taxonName))
f <- length(unique(Hymenopteran$taxonName))
g <- length(unique(Lichen$taxonName))
h <- length(unique(Liverwort$taxonName))
i <- length(unique(Mammal$taxonName))
j <- length(unique(Mollusc$taxonName))

Crear un vector y trazarlo

Una vez tenemos la riqueza de especies por cada taxón ,podemos combinar estos valores en un vector con la función c()

biodiv <- c(a,b,c,d,e,f,g,h,i,j,i)

names(biodiv) <- c("Beetle", "Bird", "Butterfly", "Dragonfly", "Flowering.Plants", "Fungus", "Hymenopteran", "Lichen", "Liverwort", "Mammal", "Mollusc")

Ahora,podemos visualizar la riqueza de especies con la función barplot()

barplot(biodiv)

Vemos un gráfico al ejectutar el código,pero ,aún falta arreglar algunos detalles (Agregarle nombres a los ejes,Modificar el rango de los ejes,agragar color,entre otros)

Para conocer cuales comandos o funciones emplear utilizaremos la función help()

help("barplot")
barplot(biodiv, xlab="Taxón", ylab="Número de especies",space = c(0.25,0.25,0.25,0.25,0.3,0.25,0.25,0.25,0.25,0.25) ,ylim=c(0,600), cex.names= 0.53, cex.axis=0.6, cex.lab=0.9, col = c("powderblue","lightblue","lightskyblue","lightsteelblue","powderblue","skyblue","grey","cadetblue","deepskyblue","steelblue","lightcyan"), main="Riqueza de especies")
longer object length is not a multiple of shorter object lengthlonger object length is not a multiple of shorter object length

Crear un marco de datos y trazarlo

Los marcos de datos son tablas de valores : tienen una esctructura bidimensional : Filas y columnas

Usaremos la función data.frame() para crearlo,pero primero crearemos una columna que contenga los nombres de todos los taxones

taxa <- c("Beetle", "Bird", "Butterfly","Dragonfly","Flowering.Plants", "Fungus", "Hymenopteran", "Lichen", "Liverwort", "Mammal", "Mollusc")

Para convertir el objeto en un factor

taxa_f <-factor(taxa)

Combinando todos los valores en el n?mero de especies en la columna llamada “richness”

richness <- c(a,b,c,d,e,f,g,h,i,j,i)

Para crear un marco de datos a partir de dos vectores

biodata <- data.frame(taxa_f, richness)

Para guardar el marco de datos en nuestro directorio de trabajo

write.csv(biodata, file ="biodata.csv")

Si queremos crear un diagrama de barras usando el marco de datos, debemos cambiar el código, pues los marcos de datos pueden contener múltiples variables.Así que debemos decirle a R exactamente cuál queremos que trace.(Como vimos antes, podemos especificar columnas de un marco de datos usando $)

barplot(biodata$richness, names.arg=c("Beetle","bird","Butterfly","Dragonfly","Flowering.plants","fungus","hymenopteran","lichen" , "Liverwort","Mammal","Mollusc"),xlab="Taxón", ylab="Numero de especies", ylim=c(0,600))

LS0tDQp0aXRsZTogIlJzdHVkaW8iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KKirCv1F1w6kgZXMgUj8qKg0KDQpSIGVzIHVuIGxlbmd1YWplIGRlIHByb2dyYW1hY2nDs24gKERlcml2YWRvIGRlbCBsZW5ndWFqZSBTKSxhc8OtIGNvbW8gdGFtYmllbiBlcyBlbCBub21icmUgZGVsIHNvZnR3YXJlIHF1ZSB1c2EgZXN0ZSBsZW5ndWFqZSBwYXJhIGxhIGNvbXB1dGFjacOzbiBlc3RhZMOtc3RpY2EuDQoNCioqQ29tZW56YW5kbyBhIGVzY3JpYmlyKioNCg0KRXN0YSBlcyB1bmEgZGUgbGFzIHRhcmVhcyBlbmNhcmdhZGFzIHBhcmEgZWwgY3Vyc28gZGUgR2VvbcOhdGljYSBiw6FzaWNhLCBlbCBvYmpldGl2byBlcyB0ZW5lciB1bmEgaW50cm9kdWNjacOzbiBhIFIgeSBhcHJlbmRlciBhbGd1bmFzIGRlIHN1cyBmdW5jaW9uZXMgcGFyYSBlbCBhbsOhbGlzaXMgZGUgZGF0b3MgO0VzdGUgZG9jdW1lbnRvIGhhIHNpZG8gcmVhbGl6YWRvIHBvciBBbmdpZSBBbGVqYW5kcmEgSnV5byBCdWl0cmFnbyANCg0KKipQYXF1ZXRlcyBlbiBSKioNCg0KTG9zIHBhcXVldGVzIGVuIFIgbm9zIHByb3BvcmNpb25hbiBmdW5jaW9uZXMgYWRpY2lvbmFsZXMNCnkgcHVlZGVuIGluc3RhbGFyc2UgZGUgbGEgc2lndWllbnRlIG1hbmVyYSANCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygpDQpgYGANCg0KVW5hIHZleiBpbnN0YWxhZG8gLGN1YW5kbyBuZWNlc2l0ZW1vcyBlbXBsZWFyIHN1cyBmdW5jaW9uZXMsIGRlYmVtb3MgY2FyZ2FybG86DQoNCmBgYHtyfQ0KI2xpYnJhcnkocGFja2FnZXMtbmFtZSkNCmBgYA0KDQpFbiBlc3RlIGNhc28gdXRpbGl6YXJlbW9zIGVsIHBhcXVldGUgZHBseXINCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KI2xpYnJhcnkoZHBseXIpDQpgYGANCg0KKipEaXJlY3RvcmlvIGRlIHRyYWJham8qKg0KDQpFcyBsYSBjYXJwZXRhIGVuIG51ZXN0cm8gZXF1aXBvIGRvbmRlIHNlIGVuY3VlbnRyYW4gbG9zIGFyY2hpdm9zIGNvbiBsb3MgcXVlIHRyYWJhamFyZW1vcyBlbiBSDQoNClBhcmEgY29ub2NlciBkb25kZSBlc3TDoSBudWVzdHJvIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBlbXBsZWFyZW1vcyBsYSBmdW5jaW9uIA0KDQpgYGB7cn0NCmdldHdkKCkNCg0KYGBgDQoNClBhcmEgY2FtYmlhciBlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8sIHVzYW1vcyBsYSBmdW5jaT9uIHNldHdkKCkNCmBgYHtyfQ0Kc2V0d2QoIkM6L1VzZXJzL2xtb250YW5hcC9EZXNrdG9wL0dCLTIiKQ0KYGBgDQoNCioqQW7DoWlzaXMgZGUgZGF0b3MgKFJlZ2lzdHJvcyBOQk4gR2F0ZWF3YXkgMjAwMC0yMDE2KSBlbiBSKioNCg0KKipJbXBvcnRhciB5IHZlcmlmaWNhciBkYXRvcyoqDQpQb2RlbW9zIGltcG9ydGFyIG51ZXN0cm9zIGRhdG9zIGVuIFJTdHVkaW8gLCBkYW5kbyBjbGljayBlbiBlbCBib3TDs24gIkltcG9ydCBEYXRhc2V0Ii5UYW1iacOpbiBwb2RlbW9zIHVzYXIgZWwgY29tYW5kbyByZWFkLmNzdigpY29uIGxhIHJ1dGEgZGUgYWNjZXNvIGFsIGRvY3VtZW50byBxdWUgc2UgZGVzZWUgaW1wb3J0YXINCg0KYGBge3J9DQpyZWFkLmNzdigiQzovVXNlcnMvbG1vbnRhbmFwL0Rlc2t0b3AvR0ItMi9lZGlkaXYuY3N2IikNCmBgYA0KDQoqKsK/Q8OzbW8gc2FiZXIgc2kgc2UgaGFuIGNvbWV0aWRvIGVycm9yZXMgZW4gbG9zIGNvZGlnb3M/KioNCg0KU2UgcmVjb21pZW5kYSBlamVjdXRhciBlbCBjw7NkaWdvIHkgdmVyaWZpY2FyIGxhIHNhbGlkYSBlbiBsYSBjb25zb2xhLHBhcmEgdGVuZXIgdW5hIHZpc3RhIGRlIHRvZG9zIGxvcyBkYXRvcyBxdWUgc2UgaGFuIGltcG9ydGFkby5BdW5xdWUgLGV4aXN0ZW4gY29tYW5kb3MgcXVlIG5vcyBwZXJtaXRlbiB0ZW5lciB1bmEgdmlzdGEgcsOhcGlkYSANCg0KYGBge3J9DQpoZWFkKGVkaWRpdikgICMgTXVlc3RyYSBsYXMgcHJpbWVyYXMgZmlsYXMgZGUgdG9kYSBsYSB0YWJsYSBxdWUgZnVlIGltcG9ydGFkYQ0KYGBgDQoNCmBgYHtyfQ0KdGFpbChlZGlkaXYpICMgTXVlc3RyYSBsYXMgdWx0aW1hcyBmaWxhcyANCmBgYA0KIA0KVW5hIHZleiBpbXBvcnRhZG9zIHBvZGVtb3MgdXNhciBlbCBjb21hbmRvIHN0cihvYmplY3QubmFtZSkgcGFyYSBtb3N0cmFyIGxhIGVzdHJ1Y3R1cmEgZGUgbG9zIGRhdG9zICAsIGVzIGRlY2lyLG5vcyBpbmRpY2EgcXVlIHRpcG8gZGUgdmFyaWFibGVzIHNvbiANCg0KYGBge3J9DQpzdHIoZWRpZGl2KQ0KYGBgDQoNCkN1YW5kbyBkZXNlZW1vcyBhY2NlZGVyIHNvbG8gYSB1bmEgY29sdW1uYSAsZGViZW1vcyBhZ3JlZ2FyIGVsIG5vbWJyZSBkZWwgb2JqZXRvIChFbCBhcmNoaXZvIHF1ZSBpbXBvcnRhbW9zKSB1bmlkbyBhIGxhIHZhcmlhYmxlIG1lZGlhbnRlIGVsIHNpbWJvbG8gKiokKiogDQoNClZpbW9zIGNvbW8gZWwgY29tYW5kbyBzdHIoKSBub3MgbXVlc3RyYSBsYSBlc3RydWN0dXJhIGRlIG51ZXN0cm9zIGRhdG9zICwgc2kgcXVlcmVtb3MgY29ub2NlciBlbCB0aXBvIGRlIHZhcmlhYmxlIGRlIHVuYSBjb2x1bW5hIGVuIGVzcGVjw61maWNvIHVzYXJlbW9zIDoNCg0KYGBge3J9DQpjbGFzcyhlZGlkaXYkdGF4b25Hcm91cCkNCmBgYA0KDQpDb21vIG51ZXN0cmEgdmFyaWFibGUgZXMgZGUgY2Fyw6FjdGVyLCBzZSBkZWJlIGNvbnZlcnRpciBhIHVuYSB2YXJpYWJsZSBkZSBmYWN0b3IgKGNhdGVnb3JpY2EpIG1lZGlhbnRlIGVsIHNpZ3VpZW50ZSBjb21hbmRvDQoNCmBgYHtyfQ0KZWRpZGl2JHRheG9uR3JvdXAgPC0gYXMuZmFjdG9yKGVkaWRpdiR0YXhvbkdyb3VwKQ0KY2xhc3MoZWRpZGl2JHRheG9uR3JvdXApDQpgYGANCg0KRXhpc3RlbiBvdHJhcyBpbnN0cnVjY2lvbmVzIHF1ZSB0YW1iaWVuIHB1ZWRlbiBzZXIgw7p0aWxlcyxwb3IgZWplbXBsbyBzaSBxdWVyZW1vcyBzYWJlciBlbCBuw7ptZXJvIGRlIGZpbGFzIG8gY29sdW1uYXMgdXNhcmVtb3MgZGltKCkNCg0KYGBge3J9DQpkaW0oZWRpZGl2KQ0KYGBgDQoNCkVsIGNvbWFuZG8gc3VtbWFyeSAoKW5vcyBwZXJtaXRlIHZlciB1biByZXN1bWVuIGRlIGxvcyBkYXRvcyBpbXBvcnRhZG9zDQoNCmBgYHtyfQ0Kc3VtbWFyeShlZGlkaXYpDQpgYGANCg0KU3VtbWFyeSAoLSQtKSBtdWVzdHJhIGVsIHJlc3VtZW4gZGUgdW5hIHZhcmlhYmxlIGVuIGVzcGVjw61maWNvDQoNCmBgYHtyfQ0Kc3VtbWFyeShlZGlkaXYkdGF4b25Hcm91cCkNCmBgYA0KDQoNCioqQ2FsY3VsYXIgbGEgcmlxdWV6YSBkZSBlc3BlY2llKioNClBhcmEgc2FiZXIgY3VhbnRhcyBlc3BlY2llcyB0ZW5lbW9zIGRlIGNhZGEgZ3J1cG8gdGF4b27Ds21pY28sZGViZW1vcyBkaXZpZGlyIGVkaWRpdiAoTnVlc3Ryb3MgZGF0b3MpIGVuIHZhcmlvcyBvYmpldG9zLGVzIGRlY2lyLGNhZGEgdW5vIGNvbiBmaWxhcyBwYXJhIGNpZXJ0byBncnVwbyANCg0KYGBge3J9DQpCZWV0bGUgPC0gZmlsdGVyKGVkaWRpdiwgdGF4b25Hcm91cCA9PSAiQmVldGxlIikNCkJpcmQgPC0gZmlsdGVyKGVkaWRpdiwgdGF4b25Hcm91cCA9PSAiQmlyZCIpDQpCdXR0ZXJmbHkgPC0gZmlsdGVyKGVkaWRpdiwgdGF4b25Hcm91cCA9PSAiQnV0dGVyZmx5IikNCkZsb3dlcmluZy5QbGFudHMgPC0gZmlsdGVyKGVkaWRpdiwgdGF4b25Hcm91cCA9PSAiRmxvd2VyaW5nLlBsYW50cyIpDQpGdW5ndXMgPC0gZmlsdGVyKGVkaWRpdiwgdGF4b25Hcm91cCA9PSAiRnVuZ3VzIikNCkh5bWVub3B0ZXJhbiA8LSBmaWx0ZXIoZWRpZGl2LCB0YXhvbkdyb3VwID09ICJIeW1lbm9wdGVyYW4iKQ0KTGljaGVuIDwtIGZpbHRlcihlZGlkaXYsIHRheG9uR3JvdXAgPT0gIkxpY2hlbiIpDQpMaXZlcndvcnQgPC0gZmlsdGVyKGVkaWRpdiwgdGF4b25Hcm91cCA9PSAiTGl2ZXJ3b3J0IikNCk1hbW1hbCA8LSBmaWx0ZXIoZWRpZGl2LCB0YXhvbkdyb3VwID09ICJNYW1tYWwiKQ0KTW9sbHVzYyA8LSBmaWx0ZXIoZWRpZGl2LCB0YXhvbkdyb3VwID09ICJNb2xsdXNjIikNCmBgYA0KDQpVbmEgdmV6IGhheWFtb3MgY3JlYWRvIGxvcyBvYmpldG9zIHBhcmEgY2FkYSB0YXjDs24gLHBvZGVtb3MgY2FsY3VsYXIgZWwgbsO6bWVybyBkZSBlc3BlY2llcyBkaWZlcmVudGVzIHBhcmEgY2FkYSBncnVwbw0KYGBge3J9DQphIDwtIGxlbmd0aCh1bmlxdWUoQmVldGxlJHRheG9uTmFtZSkpDQpiIDwtIGxlbmd0aCh1bmlxdWUoQmlyZCR0YXhvbk5hbWUpKQ0KYyA8LSBsZW5ndGgodW5pcXVlKEJ1dHRlcmZseSR0YXhvbk5hbWUpKQ0KZCA8LSBsZW5ndGgodW5pcXVlKEZsb3dlcmluZy5QbGFudHMkdGF4b25OYW1lKSkNCmUgPC0gbGVuZ3RoKHVuaXF1ZShGdW5ndXMkdGF4b25OYW1lKSkNCmYgPC0gbGVuZ3RoKHVuaXF1ZShIeW1lbm9wdGVyYW4kdGF4b25OYW1lKSkNCmcgPC0gbGVuZ3RoKHVuaXF1ZShMaWNoZW4kdGF4b25OYW1lKSkNCmggPC0gbGVuZ3RoKHVuaXF1ZShMaXZlcndvcnQkdGF4b25OYW1lKSkNCmkgPC0gbGVuZ3RoKHVuaXF1ZShNYW1tYWwkdGF4b25OYW1lKSkNCmogPC0gbGVuZ3RoKHVuaXF1ZShNb2xsdXNjJHRheG9uTmFtZSkpDQpgYGANCg0KKipDcmVhciB1biB2ZWN0b3IgeSB0cmF6YXJsbyoqDQoNClVuYSB2ZXogdGVuZW1vcyBsYSByaXF1ZXphIGRlIGVzcGVjaWVzIHBvciBjYWRhIHRheMOzbiAscG9kZW1vcyBjb21iaW5hciBlc3RvcyB2YWxvcmVzIGVuIHVuIHZlY3RvciBjb24gbGEgZnVuY2nDs24gYygpDQoNCg0KYGBge3J9DQpiaW9kaXYgPC0gYyhhLGIsYyxkLGUsZixnLGgsaSxqLGkpDQoNCm5hbWVzKGJpb2RpdikgPC0gYygiQmVldGxlIiwgIkJpcmQiLCAiQnV0dGVyZmx5IiwgIkRyYWdvbmZseSIsICJGbG93ZXJpbmcuUGxhbnRzIiwgIkZ1bmd1cyIsICJIeW1lbm9wdGVyYW4iLCAiTGljaGVuIiwgIkxpdmVyd29ydCIsICJNYW1tYWwiLCAiTW9sbHVzYyIpDQpgYGANCg0KQWhvcmEscG9kZW1vcyB2aXN1YWxpemFyIGxhIHJpcXVlemEgZGUgZXNwZWNpZXMgY29uIGxhIGZ1bmNpw7NuIGJhcnBsb3QoKQ0KDQpgYGB7cn0NCmJhcnBsb3QoYmlvZGl2KQ0KYGBgDQoNClZlbW9zIHVuIGdyw6FmaWNvIGFsIGVqZWN0dXRhciBlbCBjw7NkaWdvLHBlcm8gLGHDum4gZmFsdGEgYXJyZWdsYXIgYWxndW5vcyBkZXRhbGxlcyAoQWdyZWdhcmxlIG5vbWJyZXMgYSBsb3MgZWplcyxNb2RpZmljYXIgZWwgcmFuZ28gZGUgbG9zIGVqZXMsYWdyYWdhciBjb2xvcixlbnRyZSBvdHJvcykNCg0KUGFyYSBjb25vY2VyIGN1YWxlcyBjb21hbmRvcyBvIGZ1bmNpb25lcyBlbXBsZWFyIHV0aWxpemFyZW1vcyBsYSBmdW5jacOzbiBoZWxwKCkNCg0KYGBge3J9DQpoZWxwKCJiYXJwbG90IikNCmBgYA0KDQpgYGB7cn0NCmJhcnBsb3QoYmlvZGl2LCB4bGFiPSJUYXjDs24iLCB5bGFiPSJOw7ptZXJvIGRlIGVzcGVjaWVzIixzcGFjZSA9IGMoMC4yNSwwLjI1LDAuMjUsMC4yNSwwLjMsMC4yNSwwLjI1LDAuMjUsMC4yNSwwLjI1KSAseWxpbT1jKDAsNjAwKSwgY2V4Lm5hbWVzPSAwLjUzLCBjZXguYXhpcz0wLjYsIGNleC5sYWI9MC45LCBjb2wgPSBjKCJwb3dkZXJibHVlIiwibGlnaHRibHVlIiwibGlnaHRza3libHVlIiwibGlnaHRzdGVlbGJsdWUiLCJwb3dkZXJibHVlIiwic2t5Ymx1ZSIsImdyZXkiLCJjYWRldGJsdWUiLCJkZWVwc2t5Ymx1ZSIsInN0ZWVsYmx1ZSIsImxpZ2h0Y3lhbiIpLCBtYWluPSJSaXF1ZXphIGRlIGVzcGVjaWVzIikNCmBgYA0KDQoNCioqQ3JlYXIgdW4gbWFyY28gZGUgZGF0b3MgeSB0cmF6YXJsbyoqDQoNCkxvcyBtYXJjb3MgZGUgZGF0b3Mgc29uIHRhYmxhcyBkZSB2YWxvcmVzIDogdGllbmVuIHVuYSBlc2N0cnVjdHVyYSBiaWRpbWVuc2lvbmFsIDogRmlsYXMgeSBjb2x1bW5hcw0KDQpVc2FyZW1vcyBsYSBmdW5jacOzbiBkYXRhLmZyYW1lKCkgcGFyYSBjcmVhcmxvLHBlcm8gcHJpbWVybyBjcmVhcmVtb3MgdW5hIGNvbHVtbmEgcXVlIGNvbnRlbmdhIGxvcyBub21icmVzIGRlIHRvZG9zIGxvcyB0YXhvbmVzDQoNCmBgYHtyfQ0KdGF4YSA8LSBjKCJCZWV0bGUiLCAiQmlyZCIsICJCdXR0ZXJmbHkiLCJEcmFnb25mbHkiLCJGbG93ZXJpbmcuUGxhbnRzIiwgIkZ1bmd1cyIsICJIeW1lbm9wdGVyYW4iLCAiTGljaGVuIiwgIkxpdmVyd29ydCIsICJNYW1tYWwiLCAiTW9sbHVzYyIpDQpgYGANCg0KUGFyYSBjb252ZXJ0aXIgZWwgb2JqZXRvIGVuIHVuIGZhY3RvciANCmBgYHtyfQ0KdGF4YV9mIDwtZmFjdG9yKHRheGEpDQpgYGANCkNvbWJpbmFuZG8gdG9kb3MgbG9zIHZhbG9yZXMgZW4gZWwgbj9tZXJvIGRlIGVzcGVjaWVzIGVuIGxhIGNvbHVtbmEgbGxhbWFkYSAicmljaG5lc3MiDQpgYGB7cn0NCnJpY2huZXNzIDwtIGMoYSxiLGMsZCxlLGYsZyxoLGksaixpKQ0KYGBgDQoNClBhcmEgY3JlYXIgdW4gbWFyY28gZGUgZGF0b3MgYSBwYXJ0aXIgZGUgZG9zIHZlY3RvcmVzIA0KDQpgYGB7cn0NCmJpb2RhdGEgPC0gZGF0YS5mcmFtZSh0YXhhX2YsIHJpY2huZXNzKQ0KYGBgDQpQYXJhIGd1YXJkYXIgZWwgbWFyY28gZGUgZGF0b3MgZW4gbnVlc3RybyBkaXJlY3RvcmlvIGRlIHRyYWJham8gDQoNCmBgYHtyfQ0Kd3JpdGUuY3N2KGJpb2RhdGEsIGZpbGUgPSJiaW9kYXRhLmNzdiIpDQpgYGANCg0KDQpTaSBxdWVyZW1vcyBjcmVhciB1biBkaWFncmFtYSBkZSBiYXJyYXMgdXNhbmRvIGVsIG1hcmNvIGRlIGRhdG9zLCBkZWJlbW9zIGNhbWJpYXIgZWwgY8OzZGlnbywgcHVlcyBsb3MgbWFyY29zIGRlIGRhdG9zIHB1ZWRlbiBjb250ZW5lciBtw7psdGlwbGVzIHZhcmlhYmxlcy5Bc8OtIHF1ZSBkZWJlbW9zIGRlY2lybGUgYSBSIGV4YWN0YW1lbnRlIGN1w6FsIHF1ZXJlbW9zIHF1ZSB0cmFjZS4oQ29tbyB2aW1vcyBhbnRlcywgcG9kZW1vcyBlc3BlY2lmaWNhciBjb2x1bW5hcyBkZSB1biBtYXJjbyBkZSBkYXRvcyB1c2FuZG8gJCkNCg0KDQpgYGB7cn0NCmJhcnBsb3QoYmlvZGF0YSRyaWNobmVzcywgbmFtZXMuYXJnPWMoIkJlZXRsZSIsImJpcmQiLCJCdXR0ZXJmbHkiLCJEcmFnb25mbHkiLCJGbG93ZXJpbmcucGxhbnRzIiwiZnVuZ3VzIiwiaHltZW5vcHRlcmFuIiwibGljaGVuIiAsICJMaXZlcndvcnQiLCJNYW1tYWwiLCJNb2xsdXNjIikseGxhYj0iVGF4w7NuIiwgeWxhYj0iTnVtZXJvIGRlIGVzcGVjaWVzIiwgeWxpbT1jKDAsNjAwKSkNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==