Taller de programación en R

Sobre los notebooks

Los notebook son cuadernos de ejercicios que permiten mezclar código con texto y visualizarlo correctamente en un navegador web.

Para ejecutar un fragmento de código dentro de R Studio hay que presionar el botón Run de la parte superior del fragmento de código o colocar el cursor dentro de ese espacio y presionar Ctrl+Shift+Enter. Intenta ejecutar la siguiente instrucción.

plot(cars);

Para agregar un nuevo fragmento de código, presina el bóton de Insert Chunk en la barra de herramientas o la combinación de teclas Ctrl+Alt+I.

Como en este tipo de archivos se comprende de etiquetas Markdown, para insertar títulos se utilizan los #

Repaso

Realiza los siguientes ejercicios e inserta un bloque de código para cada respuesta.

Ejercicio 1

Guarda 2022 en la variable costo

Ejercicio 2

Guarda el entero 07 en la variable mes

Ejercicio 3

Guarda e imprime “¿A cuánto el kilo de pera?”

Ejercicio 4

Escribe en un archivo un vector con un la siguiente información:
[35, 26.25, 17, 45.59, 100, 12 + 2i]

Ejercicio 5

Lee el archivo que acabas de crear y asigna los siguientes nombres a las columnas:
Jupiter, Saturno, Urano, Neptuno, Kasiopea, Pluton

Estructuras de datos

Vectores

El objeto vector crea una variable de tipo vector que sólo puede contener datos de la misma clase. Algunos métodos para crear vectores son los siguientes:

  1. Explísita
explisito <- c(1,2,34);
explisito;
  1. Serie numérica
  • Base
serienum <- 1:5;
serienum;
  • Con la función sequence
serienum2 <- sequence(5)

serienum3 <- sequence(4:8)

serienum4 <- sequence(c(10,5))

serienum2;serienum3;serienum4;
  • Con la función seq
serienum5 <- seq(from=4, to=12, by=3)
serienum5;
  1. Repetición de elementos
repeticion <- rep(0, 4);
repeticion2 <- vector("numeric", 5)
repeticion3 <- vector("logical", 4)
repeticion; repeticion2; repeticion3;
  1. Generando patrones por niveles con la función gl
niveles1 <- gl(3,5)
niveles2 <- gl(3,5, length = 30)
niveles3 <- gl(2,6,labels = c("Macho", "Hembra"))
niveles4 <- gl(3,4, labels = c("blanco", "negro", "rojo"), length = 36)
niveles1; niveles2; niveles3; niveles4;

Matrices

El objeto matriz es un vector con el atributo de dimensión (dim). Una matriz se puede crear con el comando matrix:

matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL);

El parámetro byrow indica el orden de llenado del data frame por la variable data. Si es falso, será horizontal y si es verdadero, será vertical. El parámetro dimnames permite asignar nombres a las filas y columnas.

matrix(data = 5, nr=2, nc=5);

matrix(data = 1:6, nr=2, nc=3);

matrix(data = 1:6, nr=2, nc=3, byrow = TRUE);

matrix(data=gl(2,3,labels = c("macho", "hembra"), length = 16), nr=4, nc=4, byrow = FALSE, dimnames = list(letters[1:4], letters[16:19]))

Otra forma de crear una matriz es asignando el valor de la variable dim a un vector

v = 1:15
v
dim(v)
dim(v) = c(5, 3)
v;

Ejercicio

Crea una matriz con las relaciones mes-cumpleaños de los alumnos de la clase

matrix(NA, nrow = 3, ncol=3);
calendario = matrix(data=0, nrow = 12, ncol = 12)
View(calendario)
meses = c("enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre")
colnames(calendario) = meses
nombres = c("Ale", "Vic", "Gaby")
# rownames(calendario) = nombres #Esto da un error porque no tiene el mismo tamaño
nombresVacios = rep("Un nombre", 9)
nombres = c(nombres, nombresVacios)
rownames(calendario) = nombres;
View(calendario)
Vicmails <- c(0,1,12,6,8,6,2,1,1,11,10, 134)
calendario["Ale", "julio"] = 1
calendario["Vic",] = Vicmails

Ejercicio 2

alejandro = vector("numeric", 11)
nombres <- c("Vic", "Gaby", "Eli", "Ivan", "Diana", "Orlando", "Juan Manuel", "Juan Carlos", "Melina", "Noemi", "José Luis" )
names(alejandro) <- nombres
alejandro <- c(1, 2, 0, 0, 3, 6, 1, 10, 3, 0, 1)
alejandro

Extra tip: Con la función data.entry podemos editar de manera gráfica los datos de un objeto

data.entry(calendario)
View(calendario);

Marco de datos o Data frame

Los marcos de datos se pueden crear de manera implícita con la función read.table() y de manera explísita con la función data.farame. Lo que pide como argumentos de entrada son vectores del mismo tamaño o con un tamaño que se pueda repetir una cantidad exacta de veces.

vcol <- c("azul", "rojo", "amarillo", "verde")
vcol1 <- 1:4

data.frame(vcol, vcol1); #Esta operación crea un data frame concatenando columnas

vcol2 <- "contenido"
data.frame(vcol1, vcol2); 

vcol3 <- c(1,0,2)
vcol4 <- sequence(1:5)

data.frame(vcol4, vcol3);
  • Para asignar los nombres al momento de la creación, se realiza de la siguiente forma:
data.frame("colores"=vcol, "estatus"=vcol2, "registro"=vector("logical", 4))
  • Para asignar los nombres a los renglones al momento de la creación, se realiza de la siguiente forma:
dfcolores <- data.frame("estatus"=vcol2, "registro"=vector("logical", 4), row.names = vcol)
dfcolores;
  • Para extraer sólo el elemento de registro del color verde podemos:
    • Dar la posición exacta del valor
dfcolores[4, 2]
  • Pedirlo por sus identificadores
dfcolores["verde",]$registro

Para agregar una nueva columna a un dataframe se puede agregar con la misma función de data.frame o direccionando a una nueva llave de columna

undataframe <- data.frame(c("uno", "dos"), c(0,0))
undataframe
nvodataframe <- data.frame(undataframe, c(T,F))
nvodataframe
nvodataframe["columna4"] <-  c(3i, 0i)
nvodataframe;

Para agregar registros a un data frame se agrega directamente en el último renglón del dataframe se asigna el vector al último renglón más uno. El vector debe ser del mismo tamaño que las columnas que tiene el dataframe base

nrow(nvodataframe) #Esta función nos dice el número del último renglón de un dataframe
nvodataframe[nrow(nvodataframe)+1, ] <- data.frame("tres", 0, T, 4+5i)
nvodataframe

# También podemos agregar con una lista, por ejemplo:
nvodataframe[nrow(nvodataframe)+1, ] <- list("cuatro", 0, F, 1+0i)
nvodataframe;

Listas

Las listas permiten agrupar elementos de distinta clase y formar de esta forma estructuras de datos complejas.

vnum <-  c(1:7)
vletras <-  c("alfa", "beta", "teta", "delta", "omega")
matriz <- matrix(1:12, ncol=4)
lista <-  list(vnum , vletras, matriz)
vnum; vletras; lista

Lectura de archivos

Ya hemos visto que podemos leer un archivo con el comando scan(). Este comano va a buscar un archivo en el directorio de trabajo que estemos. Para conocer este directorio de trabajo o establecer uno personalizado, los comandos son:

# Obtención del directorio de trabajo
getwd()
# Establecimiento de un directorio de trabajo
# setwd("El directorio que desees")

Además de scan, también podemos leer archivos con el comando read.table. Este comando nos permite leer el archivo y acomodarlo como un marco de datos o data frame. El grupo de funciones de read, también incluye: - read.csv - read.csv2 - read.delim - read.delim2

Read.table

La lectura con read.table abre de forma automática un marco de datos

directoriodetrabajo = getwd()
paste(directoriodetrabajo, "fb_posts.txt", sep = "/")
publicaciones.soloread <- read.table("/Volumes/Perita/UAM/TPP/fb_posts.txt")
publicaciones.separador <- read.table("fb_posts.txt", sep=" ")
View(publicaciones.soloread);View(publicaciones.separador);

Operaciones con vectores

Suma

De los vectores

a <- c(2,3,6,8)
b <- c(8,7,4,2)
a+b

Entre el contenido del vector

sum(b)

Resta

b-a

Producto

a*b
b*3
prod(a)

Raíz cuadrada

sqrt(b*3)

Funciones estadísticas

El máximo de un grupo de datos

max(b)

El mínimo de un grupo de datos

min(b)

El rango de los datos

range(a)

La media de un grupo

mean(a)

La mediana de un grupo

median(a)

Varianza

var(a)

Comparación

Se pueden comparar los elementos de un vector

a > median(a)
LS0tCnRpdGxlOiAiQ3VhZGVybm8gZGUgZWplcmNpY2lvcyB8IEludHJvZHVjY2nDs24geSBlc3RydWN0dXJhcyBkZSBkYXRvcyIKYXV0aG9yOiAiQWxlamFuZHJvIFJvc2FsZXMgTWFydMOtbmV6IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIFRhbGxlciBkZSBwcm9ncmFtYWNpw7NuIGVuIFIKCiMjIyMgVGFibGEgZGUgY29udGVuaWRvCgoxLiBbU29icmUgbG9zIG5vdGVib29rc10KMi4gW1JlcGFzb10KMy4gW0VzdHJ1Y3R1cmFzIGRlIGRhdG9zXQo0LiBbTGVjdHVyYSBkZSBhcmNoaXZvc10KNS4gW09wZXJhY2lvbmVzIGNvbiB2ZWN0b3Jlc10KCi0tLQoKIyMgU29icmUgbG9zIG5vdGVib29rcwoKTG9zIG5vdGVib29rIHNvbiBjdWFkZXJub3MgZGUgZWplcmNpY2lvcyBxdWUgcGVybWl0ZW4gbWV6Y2xhciBjw7NkaWdvIGNvbiB0ZXh0byB5IHZpc3VhbGl6YXJsbyBjb3JyZWN0YW1lbnRlIGVuIHVuIG5hdmVnYWRvciB3ZWIuCgpQYXJhIGVqZWN1dGFyIHVuIGZyYWdtZW50byBkZSBjw7NkaWdvIGRlbnRybyBkZSBSIFN0dWRpbyBoYXkgcXVlIHByZXNpb25hciBlbCBib3TDs24gKlJ1biogZGUgbGEgcGFydGUgc3VwZXJpb3IgZGVsIGZyYWdtZW50byBkZSBjw7NkaWdvIG8gY29sb2NhciBlbCBjdXJzb3IgZGVudHJvIGRlIGVzZSBlc3BhY2lvIHkgcHJlc2lvbmFyICpDdHJsK1NoaWZ0K0VudGVyKi4gSW50ZW50YSBlamVjdXRhciBsYSBzaWd1aWVudGUgaW5zdHJ1Y2Npw7NuLgoKYGBge3J9CnBsb3QoY2Fycyk7CmBgYAoKUGFyYSBhZ3JlZ2FyIHVuIG51ZXZvIGZyYWdtZW50byBkZSBjw7NkaWdvLCBwcmVzaW5hIGVsIGLDs3RvbiBkZSAqSW5zZXJ0IENodW5rKiBlbiBsYSBiYXJyYSBkZSBoZXJyYW1pZW50YXMgbyBsYSBjb21iaW5hY2nDs24gZGUgdGVjbGFzICpDdHJsK0FsdCtJKi4KCkNvbW8gZW4gZXN0ZSB0aXBvIGRlIGFyY2hpdm9zIHNlIGNvbXByZW5kZSBkZSBldGlxdWV0YXMgTWFya2Rvd24sIHBhcmEgaW5zZXJ0YXIgdMOtdHVsb3Mgc2UgdXRpbGl6YW4gbG9zIFwjCgoKIyMgUmVwYXNvIHsudGFic2V0fQoKUmVhbGl6YSBsb3Mgc2lndWllbnRlcyBlamVyY2ljaW9zIGUgaW5zZXJ0YSB1biBibG9xdWUgZGUgY8OzZGlnbyBwYXJhIGNhZGEgcmVzcHVlc3RhLgoKIyMjIEVqZXJjaWNpbyAxCgpHdWFyZGEgMjAyMiBlbiBsYSB2YXJpYWJsZSBjb3N0bwoKIyMjIEVqZXJjaWNpbyAyCgpHdWFyZGEgZWwgZW50ZXJvIDA3IGVuIGxhIHZhcmlhYmxlIG1lcwoKIyMjIEVqZXJjaWNpbyAzCgpHdWFyZGEgZSBpbXByaW1lICLCv0EgY3XDoW50byBlbCBraWxvIGRlIHBlcmE/IgoKIyMjIEVqZXJjaWNpbyA0CgpFc2NyaWJlIGVuIHVuIGFyY2hpdm8gdW4gdmVjdG9yIGNvbiB1biBsYSBzaWd1aWVudGUgaW5mb3JtYWNpw7NuOlwKWzM1LCAyNi4yNSwgMTcsIDQ1LjU5LCAxMDAsIDEyICsgMmldCgojIyMgRWplcmNpY2lvIDUKCkxlZSBlbCBhcmNoaXZvIHF1ZSBhY2FiYXMgZGUgY3JlYXIgeSBhc2lnbmEgbG9zIHNpZ3VpZW50ZXMgbm9tYnJlcyBhIGxhcyBjb2x1bW5hczpcCkp1cGl0ZXIsIFNhdHVybm8sIFVyYW5vLCBOZXB0dW5vLCBLYXNpb3BlYSwgUGx1dG9uCgojIyBFc3RydWN0dXJhcyBkZSBkYXRvcyB7LnRhYnNldH0KCiMjIyBWZWN0b3JlcwoKRWwgb2JqZXRvIHZlY3RvciBjcmVhIHVuYSB2YXJpYWJsZSBkZSB0aXBvIHZlY3RvciBxdWUgc8OzbG8gcHVlZGUgY29udGVuZXIgZGF0b3MgZGUgbGEgbWlzbWEgY2xhc2UuIEFsZ3Vub3MgbcOpdG9kb3MgcGFyYSBjcmVhciB2ZWN0b3JlcyBzb24gbG9zIHNpZ3VpZW50ZXM6CgoxLiAgRXhwbMOtc2l0YQoKYGBge3J9CmV4cGxpc2l0byA8LSBjKDEsMiwzNCk7CmV4cGxpc2l0bzsKYGBgCgoyLiAgU2VyaWUgbnVtw6lyaWNhCgotICAgQmFzZQoKYGBge3J9CnNlcmllbnVtIDwtIDE6NTsKc2VyaWVudW07CmBgYAoKLSAgIENvbiBsYSBmdW5jacOzbiBzZXF1ZW5jZQoKYGBge3J9CnNlcmllbnVtMiA8LSBzZXF1ZW5jZSg1KQoKc2VyaWVudW0zIDwtIHNlcXVlbmNlKDQ6OCkKCnNlcmllbnVtNCA8LSBzZXF1ZW5jZShjKDEwLDUpKQoKc2VyaWVudW0yO3NlcmllbnVtMztzZXJpZW51bTQ7CmBgYAotIENvbiBsYSBmdW5jacOzbiBzZXEKYGBge3J9CnNlcmllbnVtNSA8LSBzZXEoZnJvbT00LCB0bz0xMiwgYnk9MykKc2VyaWVudW01OwpgYGAKCgoKMy4gIFJlcGV0aWNpw7NuIGRlIGVsZW1lbnRvcwoKYGBge3J9CnJlcGV0aWNpb24gPC0gcmVwKDAsIDQpOwpyZXBldGljaW9uMiA8LSB2ZWN0b3IoIm51bWVyaWMiLCA1KQpyZXBldGljaW9uMyA8LSB2ZWN0b3IoImxvZ2ljYWwiLCA0KQpyZXBldGljaW9uOyByZXBldGljaW9uMjsgcmVwZXRpY2lvbjM7CmBgYAoKNC4gIEdlbmVyYW5kbyBwYXRyb25lcyBwb3Igbml2ZWxlcyBjb24gbGEgZnVuY2nDs24gZ2wKCmBgYHtyfQpuaXZlbGVzMSA8LSBnbCgzLDUpCm5pdmVsZXMyIDwtIGdsKDMsNSwgbGVuZ3RoID0gMzApCm5pdmVsZXMzIDwtIGdsKDIsNixsYWJlbHMgPSBjKCJNYWNobyIsICJIZW1icmEiKSkKbml2ZWxlczQgPC0gZ2woMyw0LCBsYWJlbHMgPSBjKCJibGFuY28iLCAibmVncm8iLCAicm9qbyIpLCBsZW5ndGggPSAzNikKbml2ZWxlczE7IG5pdmVsZXMyOyBuaXZlbGVzMzsgbml2ZWxlczQ7CmBgYAoKIyMjIE1hdHJpY2VzCgpFbCBvYmpldG8gbWF0cml6IGVzIHVuIHZlY3RvciBjb24gZWwgYXRyaWJ1dG8gZGUgZGltZW5zacOzbiAoZGltKS4gVW5hIG1hdHJpeiBzZSBwdWVkZSBjcmVhciBjb24gZWwgY29tYW5kbyBtYXRyaXg6CgpgYGB7cn0KbWF0cml4KGRhdGEgPSBOQSwgbnJvdyA9IDEsIG5jb2wgPSAxLCBieXJvdyA9IEZBTFNFLCBkaW1uYW1lcyA9IE5VTEwpOwpgYGAKCkVsIHBhcsOhbWV0cm8gKipieXJvdyoqIGluZGljYSBlbCBvcmRlbiBkZSBsbGVuYWRvIGRlbCBkYXRhIGZyYW1lIHBvciBsYSB2YXJpYWJsZSBkYXRhLiBTaSBlcyBmYWxzbywgc2Vyw6EgaG9yaXpvbnRhbCB5IHNpIGVzIHZlcmRhZGVybywgc2Vyw6EgdmVydGljYWwuIEVsIHBhcsOhbWV0cm8gKipkaW1uYW1lcyoqIHBlcm1pdGUgYXNpZ25hciBub21icmVzIGEgbGFzIGZpbGFzIHkgY29sdW1uYXMuCgpgYGB7cn0KbWF0cml4KGRhdGEgPSA1LCBucj0yLCBuYz01KTsKCm1hdHJpeChkYXRhID0gMTo2LCBucj0yLCBuYz0zKTsKCm1hdHJpeChkYXRhID0gMTo2LCBucj0yLCBuYz0zLCBieXJvdyA9IFRSVUUpOwoKbWF0cml4KGRhdGE9Z2woMiwzLGxhYmVscyA9IGMoIm1hY2hvIiwgImhlbWJyYSIpLCBsZW5ndGggPSAxNiksIG5yPTQsIG5jPTQsIGJ5cm93ID0gRkFMU0UsIGRpbW5hbWVzID0gbGlzdChsZXR0ZXJzWzE6NF0sIGxldHRlcnNbMTY6MTldKSkKYGBgCgpPdHJhIGZvcm1hIGRlIGNyZWFyIHVuYSBtYXRyaXogZXMgYXNpZ25hbmRvIGVsIHZhbG9yIGRlIGxhIHZhcmlhYmxlIGRpbSBhIHVuIHZlY3RvcgoKYGBge3J9CnYgPSAxOjE1CnYKZGltKHYpCmRpbSh2KSA9IGMoNSwgMykKdjsKYGBgCgojIyMjIEVqZXJjaWNpbwoKQ3JlYSB1bmEgbWF0cml6IGNvbiBsYXMgcmVsYWNpb25lcyBtZXMtY3VtcGxlYcOxb3MgZGUgbG9zIGFsdW1ub3MgZGUgbGEgY2xhc2UKCmBgYHtyfQptYXRyaXgoTkEsIG5yb3cgPSAzLCBuY29sPTMpOwpgYGAKCmBgYHtyfQpjYWxlbmRhcmlvID0gbWF0cml4KGRhdGE9MCwgbnJvdyA9IDEyLCBuY29sID0gMTIpClZpZXcoY2FsZW5kYXJpbykKbWVzZXMgPSBjKCJlbmVybyIsICJmZWJyZXJvIiwgIm1hcnpvIiwgImFicmlsIiwgIm1heW8iLCAianVuaW8iLCAianVsaW8iLCAiYWdvc3RvIiwgInNlcHRpZW1icmUiLCAib2N0dWJyZSIsICJub3ZpZW1icmUiLCAiZGljaWVtYnJlIikKY29sbmFtZXMoY2FsZW5kYXJpbykgPSBtZXNlcwpub21icmVzID0gYygiQWxlIiwgIlZpYyIsICJHYWJ5IikKIyByb3duYW1lcyhjYWxlbmRhcmlvKSA9IG5vbWJyZXMgI0VzdG8gZGEgdW4gZXJyb3IgcG9ycXVlIG5vIHRpZW5lIGVsIG1pc21vIHRhbWHDsW8Kbm9tYnJlc1ZhY2lvcyA9IHJlcCgiVW4gbm9tYnJlIiwgOSkKbm9tYnJlcyA9IGMobm9tYnJlcywgbm9tYnJlc1ZhY2lvcykKcm93bmFtZXMoY2FsZW5kYXJpbykgPSBub21icmVzOwpWaWV3KGNhbGVuZGFyaW8pClZpY21haWxzIDwtIGMoMCwxLDEyLDYsOCw2LDIsMSwxLDExLDEwLCAxMzQpCmNhbGVuZGFyaW9bIkFsZSIsICJqdWxpbyJdID0gMQpjYWxlbmRhcmlvWyJWaWMiLF0gPSBWaWNtYWlscwpgYGAKCiMjIyMgRWplcmNpY2lvIDIKYGBge3J9CmFsZWphbmRybyA9IHZlY3RvcigibnVtZXJpYyIsIDExKQpub21icmVzIDwtIGMoIlZpYyIsICJHYWJ5IiwgIkVsaSIsICJJdmFuIiwgIkRpYW5hIiwgIk9ybGFuZG8iLCAiSnVhbiBNYW51ZWwiLCAiSnVhbiBDYXJsb3MiLCAiTWVsaW5hIiwgIk5vZW1pIiwgIkpvc8OpIEx1aXMiICkKbmFtZXMoYWxlamFuZHJvKSA8LSBub21icmVzCmFsZWphbmRybyA8LSBjKDEsIDIsIDAsIDAsIDMsIDYsIDEsIDEwLCAzLCAwLCAxKQphbGVqYW5kcm8KYGBgCgoKPiBFeHRyYSB0aXA6IENvbiBsYSBmdW5jacOzbiBkYXRhLmVudHJ5IHBvZGVtb3MgZWRpdGFyIGRlIG1hbmVyYSBncsOhZmljYSBsb3MgZGF0b3MgZGUgdW4gb2JqZXRvCgpgYGB7cn0KZGF0YS5lbnRyeShjYWxlbmRhcmlvKQpWaWV3KGNhbGVuZGFyaW8pOwpgYGAKCgojIyMgTWFyY28gZGUgZGF0b3MgbyBEYXRhIGZyYW1lCgpMb3MgbWFyY29zIGRlIGRhdG9zIHNlIHB1ZWRlbiBjcmVhciBkZSBtYW5lcmEgaW1wbMOtY2l0YSBjb24gbGEgZnVuY2nDs24gcmVhZC50YWJsZSgpIHkgZGUgbWFuZXJhIGV4cGzDrXNpdGEgY29uIGxhIGZ1bmNpw7NuIGRhdGEuZmFyYW1lLiBMbyBxdWUgcGlkZSBjb21vIGFyZ3VtZW50b3MgZGUgZW50cmFkYSBzb24gdmVjdG9yZXMgZGVsIG1pc21vIHRhbWHDsW8gbyBjb24gdW4gdGFtYcOxbyBxdWUgc2UgcHVlZGEgcmVwZXRpciB1bmEgY2FudGlkYWQgZXhhY3RhIGRlIHZlY2VzLgpgYGB7cn0KdmNvbCA8LSBjKCJhenVsIiwgInJvam8iLCAiYW1hcmlsbG8iLCAidmVyZGUiKQp2Y29sMSA8LSAxOjQKCmRhdGEuZnJhbWUodmNvbCwgdmNvbDEpOyAjRXN0YSBvcGVyYWNpw7NuIGNyZWEgdW4gZGF0YSBmcmFtZSBjb25jYXRlbmFuZG8gY29sdW1uYXMKCnZjb2wyIDwtICJjb250ZW5pZG8iCmRhdGEuZnJhbWUodmNvbDEsIHZjb2wyKTsgCgp2Y29sMyA8LSBjKDEsMCwyKQp2Y29sNCA8LSBzZXF1ZW5jZSgxOjUpCgpkYXRhLmZyYW1lKHZjb2w0LCB2Y29sMyk7CmBgYAotIFBhcmEgYXNpZ25hciBsb3Mgbm9tYnJlcyBhbCBtb21lbnRvIGRlIGxhIGNyZWFjacOzbiwgc2UgcmVhbGl6YSBkZSBsYSBzaWd1aWVudGUgZm9ybWE6CgpgYGB7cn0KZGF0YS5mcmFtZSgiY29sb3JlcyI9dmNvbCwgImVzdGF0dXMiPXZjb2wyLCAicmVnaXN0cm8iPXZlY3RvcigibG9naWNhbCIsIDQpKQpgYGAKCi0gUGFyYSBhc2lnbmFyIGxvcyBub21icmVzIGEgbG9zIHJlbmdsb25lcyBhbCBtb21lbnRvIGRlIGxhIGNyZWFjacOzbiwgc2UgcmVhbGl6YSBkZSBsYSBzaWd1aWVudGUgZm9ybWE6CmBgYHtyfQpkZmNvbG9yZXMgPC0gZGF0YS5mcmFtZSgiZXN0YXR1cyI9dmNvbDIsICJyZWdpc3RybyI9dmVjdG9yKCJsb2dpY2FsIiwgNCksIHJvdy5uYW1lcyA9IHZjb2wpCmRmY29sb3JlczsKYGBgCgotIFBhcmEgZXh0cmFlciBzw7NsbyBlbCBlbGVtZW50byBkZSByZWdpc3RybyBkZWwgY29sb3IgdmVyZGUgcG9kZW1vczoKICAtIERhciBsYSBwb3NpY2nDs24gZXhhY3RhIGRlbCB2YWxvcgpgYGB7cn0KZGZjb2xvcmVzWzQsIDJdCmBgYAoKCiAgLSBQZWRpcmxvIHBvciBzdXMgaWRlbnRpZmljYWRvcmVzCmBgYHtyfQpkZmNvbG9yZXNbInZlcmRlIixdJHJlZ2lzdHJvCmBgYAoKClBhcmEgYWdyZWdhciB1bmEgbnVldmEgY29sdW1uYSBhIHVuIGRhdGFmcmFtZSBzZSBwdWVkZSBhZ3JlZ2FyIGNvbiBsYSBtaXNtYSBmdW5jacOzbiBkZSBkYXRhLmZyYW1lIG8gZGlyZWNjaW9uYW5kbyBhIHVuYSBudWV2YSBsbGF2ZSBkZSBjb2x1bW5hCmBgYHtyfQp1bmRhdGFmcmFtZSA8LSBkYXRhLmZyYW1lKGMoInVubyIsICJkb3MiKSwgYygwLDApKQp1bmRhdGFmcmFtZQpudm9kYXRhZnJhbWUgPC0gZGF0YS5mcmFtZSh1bmRhdGFmcmFtZSwgYyhULEYpKQpudm9kYXRhZnJhbWUKbnZvZGF0YWZyYW1lWyJjb2x1bW5hNCJdIDwtICBjKDNpLCAwaSkKbnZvZGF0YWZyYW1lOwpgYGAKCgpQYXJhIGFncmVnYXIgcmVnaXN0cm9zIGEgdW4gZGF0YSBmcmFtZSBzZSBhZ3JlZ2EgZGlyZWN0YW1lbnRlIGVuIGVsIMO6bHRpbW8gcmVuZ2zDs24gZGVsIGRhdGFmcmFtZSBzZSBhc2lnbmEgZWwgdmVjdG9yIGFsIMO6bHRpbW8gcmVuZ2zDs24gbcOhcyB1bm8uICoqRWwgdmVjdG9yIGRlYmUgc2VyIGRlbCBtaXNtbyB0YW1hw7FvIHF1ZSBsYXMgY29sdW1uYXMgcXVlIHRpZW5lIGVsIGRhdGFmcmFtZSBiYXNlKioKCmBgYHtyfQpucm93KG52b2RhdGFmcmFtZSkgI0VzdGEgZnVuY2nDs24gbm9zIGRpY2UgZWwgbsO6bWVybyBkZWwgw7psdGltbyByZW5nbMOzbiBkZSB1biBkYXRhZnJhbWUKbnZvZGF0YWZyYW1lW25yb3cobnZvZGF0YWZyYW1lKSsxLCBdIDwtIGRhdGEuZnJhbWUoInRyZXMiLCAwLCBULCA0KzVpKQpudm9kYXRhZnJhbWUKCiMgVGFtYmnDqW4gcG9kZW1vcyBhZ3JlZ2FyIGNvbiB1bmEgbGlzdGEsIHBvciBlamVtcGxvOgpudm9kYXRhZnJhbWVbbnJvdyhudm9kYXRhZnJhbWUpKzEsIF0gPC0gbGlzdCgiY3VhdHJvIiwgMCwgRiwgMSswaSkKbnZvZGF0YWZyYW1lOwpgYGAKCgojIyMgTGlzdGFzCgpMYXMgbGlzdGFzIHBlcm1pdGVuIGFncnVwYXIgZWxlbWVudG9zIGRlIGRpc3RpbnRhIGNsYXNlIHkgZm9ybWFyIGRlIGVzdGEgZm9ybWEgZXN0cnVjdHVyYXMgZGUgZGF0b3MgY29tcGxlamFzLgoKYGBge3J9CnZudW0gPC0gIGMoMTo3KQp2bGV0cmFzIDwtICBjKCJhbGZhIiwgImJldGEiLCAidGV0YSIsICJkZWx0YSIsICJvbWVnYSIpCm1hdHJpeiA8LSBtYXRyaXgoMToxMiwgbmNvbD00KQpsaXN0YSA8LSAgbGlzdCh2bnVtICwgdmxldHJhcywgbWF0cml6KQp2bnVtOyB2bGV0cmFzOyBsaXN0YQpgYGAKCgoKIyMgTGVjdHVyYSBkZSBhcmNoaXZvcwoKWWEgaGVtb3MgdmlzdG8gcXVlIHBvZGVtb3MgbGVlciB1biBhcmNoaXZvIGNvbiBlbCBjb21hbmRvIHNjYW4oKS4gRXN0ZSBjb21hbm8gdmEgYSBidXNjYXIgdW4gYXJjaGl2byBlbiBlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8gcXVlIGVzdGVtb3MuIFBhcmEgY29ub2NlciBlc3RlIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBvIGVzdGFibGVjZXIgdW5vIHBlcnNvbmFsaXphZG8sIGxvcyBjb21hbmRvcyBzb246CgpgYGB7cn0KIyBPYnRlbmNpw7NuIGRlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8KZ2V0d2QoKQojIEVzdGFibGVjaW1pZW50byBkZSB1biBkaXJlY3RvcmlvIGRlIHRyYWJham8KIyBzZXR3ZCgiRWwgZGlyZWN0b3JpbyBxdWUgZGVzZWVzIikKYGBgCgpBZGVtw6FzIGRlIHNjYW4sIHRhbWJpw6luIHBvZGVtb3MgbGVlciBhcmNoaXZvcyBjb24gZWwgY29tYW5kbyAqKnJlYWQudGFibGUqKi4gRXN0ZSBjb21hbmRvIG5vcyBwZXJtaXRlIGxlZXIgZWwgYXJjaGl2byB5IGFjb21vZGFybG8gY29tbyB1biBtYXJjbyBkZSBkYXRvcyBvIGRhdGEgZnJhbWUuIEVsIGdydXBvIGRlIGZ1bmNpb25lcyBkZSByZWFkLCB0YW1iacOpbiBpbmNsdXllOiAtIHJlYWQuY3N2IC0gcmVhZC5jc3YyIC0gcmVhZC5kZWxpbSAtIHJlYWQuZGVsaW0yCgojIyMgUmVhZC50YWJsZQpMYSBsZWN0dXJhIGNvbiByZWFkLnRhYmxlIGFicmUgZGUgZm9ybWEgYXV0b23DoXRpY2EgdW4gbWFyY28gZGUgZGF0b3MKYGBge3J9CmRpcmVjdG9yaW9kZXRyYWJham8gPSBnZXR3ZCgpCnBhc3RlKGRpcmVjdG9yaW9kZXRyYWJham8sICJmYl9wb3N0cy50eHQiLCBzZXAgPSAiLyIpCnB1YmxpY2FjaW9uZXMuc29sb3JlYWQgPC0gcmVhZC50YWJsZSgiL1ZvbHVtZXMvUGVyaXRhL1VBTS9UUFAvZmJfcG9zdHMudHh0IikKcHVibGljYWNpb25lcy5zZXBhcmFkb3IgPC0gcmVhZC50YWJsZSgiZmJfcG9zdHMudHh0Iiwgc2VwPSIgIikKVmlldyhwdWJsaWNhY2lvbmVzLnNvbG9yZWFkKTtWaWV3KHB1YmxpY2FjaW9uZXMuc2VwYXJhZG9yKTsKYGBgCgoKIyMgT3BlcmFjaW9uZXMgY29uIHZlY3RvcmVzIHsudGFic2V0fQoKIyMjIFN1bWEKRGUgbG9zIHZlY3RvcmVzCmBgYHtyfQphIDwtIGMoMiwzLDYsOCkKYiA8LSBjKDgsNyw0LDIpCmErYgpgYGAKCkVudHJlIGVsIGNvbnRlbmlkbyBkZWwgdmVjdG9yCmBgYHtyfQpzdW0oYikKYGBgCiMjIyBSZXN0YQpgYGB7cn0KYi1hCmBgYAoKIyMjIFByb2R1Y3RvCmBgYHtyfQphKmIKYiozCnByb2QoYSkKYGBgCgojIyMgUmHDrXogY3VhZHJhZGEKYGBge3J9CnNxcnQoYiozKQpgYGAKCgojIyMgRnVuY2lvbmVzIGVzdGFkw61zdGljYXMKCkVsIG3DoXhpbW8gZGUgdW4gZ3J1cG8gZGUgZGF0b3MKCmBgYHtyfQptYXgoYikKYGBgCgpFbCBtw61uaW1vIGRlIHVuIGdydXBvIGRlIGRhdG9zCgpgYGB7cn0KbWluKGIpCmBgYAoKRWwgcmFuZ28gZGUgbG9zIGRhdG9zCgpgYGB7cn0KcmFuZ2UoYSkKYGBgCgpMYSBtZWRpYSBkZSB1biBncnVwbwoKYGBge3J9Cm1lYW4oYSkKYGBgCgpMYSBtZWRpYW5hIGRlIHVuIGdydXBvCmBgYHtyfQptZWRpYW4oYSkKYGBgClZhcmlhbnphCmBgYHtyfQp2YXIoYSkKYGBgCgojIyMgQ29tcGFyYWNpw7NuClNlIHB1ZWRlbiBjb21wYXJhciBsb3MgZWxlbWVudG9zIGRlIHVuIHZlY3RvcgpgYGB7cn0KYSA+IG1lZGlhbihhKQpgYGAK