Practica No. 1. - R básico

En esta practica se estudiaran los primeros conceptos acerca del manejo del entorno de R, la creación y manipulación de los objetos en el ambiente de trabajo además de otros relacionados con el acceso a información desde fuentes de datos internas y externas.

Entorno de R

El entorno de R, se puede entender como el la creación y acceso a objetos desde la consola, elemento principal en la edición de código R, así se muestran a continuación las características mas importantes de este elemento.

Para edición de código cualquier herramienta de texto disponible es suficiente aún así el manejo de herramientas como RStudio puede facilitar la implementación del código en R.

# Calcula 3 + 4
3 + 4

# Calcula 6 + 12
6 + 12

Un primer vistazo al funcionamiento de la herramienta R, es bajo la perspectiva de la realización de cálculos aritméticos. A continuación se muestran algunas de las operaciones básicas que se pueden realizar con simples llamados a funciones genéricas y utilitarias.

# Una suma
5 + 5 
[1] 10
# Una resta
5 - 5 
[1] 0
# Una multiplicación 
3 * 5
[1] 15
# Una división
(5 + 5)/2 
[1] 5
# Una exponenciación 
2^5
[1] 32
# Modulo por división 
28 %% 6
[1] 4
# Raiz cuadrada 
2^16
[1] 65536

Números en R

En R se pueden almacenar números acorde al tipo de datos, es importante para los casos en donde el resultado de un calculo es precisamente un número

3/0; -3/0; 
[1] Inf
[1] -Inf
exp(-Inf); 0/Inf
[1] 0
[1] 0
(0:3)^Inf; 0/0
[1]   0   1 Inf Inf
[1] NaN
Inf - Inf; Inf/Inf
[1] NaN
[1] NaN
is.finite(10)
[1] TRUE
is.finite(Inf)
[1] FALSE

Funciones misceláneas

Para concluir este apartado en el que R puede ser visto como una herramienta de cálculos se muestran a continuación algunas funciones de uso genérico.

# Raíz cuadrada
sqrt(4)
[1] 2
# Exponencial 
exp(1); exp(2)
[1] 2.718282
[1] 7.389056
# Funciones trigonométricas 
cos(pi); tan(pi/4); sin(pi)
[1] -1
[1] 1
[1] 1.224647e-16
# Funciones para redondear 
round(sin(pi),10); round(1.34562, 2)
[1] 0
[1] 1.35
floor(sin(pi)); floor(1.34562)
[1] 0
[1] 1
ceiling(sin(pi)); ceiling(1.34562)
[1] 1
[1] 2

Un concepto básico del análisis de datos en Estadística es el de variable, en R las variables son objetos vistos desde el tipo de datos que se le asigna, entonces es importante conocer los tipos de operadores de asignación como se muestra a continuación

# Asignando un valor 45 a "x" e "y"
x <- 45
y = 45
45 -> z
# Imprimiendo de manera automatica el valor de "x" 
x; y; z
[1] 45
[1] 45
[1] 45

Los nombres de variables siguen una serie de restricciones en R debido a que en este entorno se distinguen mayúsculas, minúsculas y palabras reservadas entre otros.

x <- 2; X <- 2 
un.numero.grande <- 126597264
x_1o <- 36
%un.numero.grande <- 12457
Error: unexpected input in "%un.numero.grande <- 12457"

Los tipos de datos en R, como en cualquier otro lenguaje de programación cobran vital importancia, debido a esto el conocer cada uno de los tipos de datos se puede entender como el proceso almacenamiento interno de datos en R.

# Declarando distintos tipos de variables
my_numeric <- 42
my_character <- "forty-two"
my_logical <- FALSE 
# Investigar el tipo de datos 
class(my_numeric)
[1] "numeric"
class(my_character)
[1] "character"
class(my_logical)
[1] "logical"

Objetos en R

Todo en R puede ser visto como un objeto, esto implica a su vez que tiene nombre, contenido y atributos.

x <- rnorm(10); names(x) <- paste("Pos", 1:10)
x
       Pos 1        Pos 2        Pos 3        Pos 4        Pos 5 
-0.245003490 -1.377579727 -0.022812200  1.008184695 -1.119920494 
       Pos 6        Pos 7        Pos 8        Pos 9       Pos 10 
-0.007174784 -0.276150953 -1.230713078  0.739840763 -0.391343039 
mode(x); length(x); class(x)
[1] "numeric"
[1] 10
[1] "numeric"

Cuando el objeto adquiere más atributos y dimensiones es fácil observar estas a través de las funciones genéricas dim() y attributes()

data("mtcars")
dim(mtcars)
[1] 32 11
attributes(mtcars)
$names
 [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
[11] "carb"

$row.names
 [1] "Mazda RX4"           "Mazda RX4 Wag"       "Datsun 710"         
 [4] "Hornet 4 Drive"      "Hornet Sportabout"   "Valiant"            
 [7] "Duster 360"          "Merc 240D"           "Merc 230"           
[10] "Merc 280"            "Merc 280C"           "Merc 450SE"         
[13] "Merc 450SL"          "Merc 450SLC"         "Cadillac Fleetwood" 
[16] "Lincoln Continental" "Chrysler Imperial"   "Fiat 128"           
[19] "Honda Civic"         "Toyota Corolla"      "Toyota Corona"      
[22] "Dodge Challenger"    "AMC Javelin"         "Camaro Z28"         
[25] "Pontiac Firebird"    "Fiat X1-9"           "Porsche 914-2"      
[28] "Lotus Europa"        "Ford Pantera L"      "Ferrari Dino"       
[31] "Maserati Bora"       "Volvo 142E"         

$class
[1] "data.frame"

Vectores

El objeto más simple y de mayor uso para el calculo de cantidades es el vector a continuación veremos las múltiples formas de declarar y manipular un vector en R.

# Declarando vectores, "vector()" 
numeric_integer <- vector("integer"); numeric_integer
integer(0)
numeric_vector <- vector("numeric"); numeric_vector
numeric(0)
character_vector <- vector("character"); character_vector
character(0)
boolean_vector <- vector("logical"); boolean_vector
logical(0)
complex_vector <- vector("complex"); complex_vector
complex(0)

La forma mas común de declarar un vector en mediante la función concatenar c() como se muestra a continuación.

numeric_vector <- c(1, 10, 49)
character_vector <- c("a", "b", "c")
boolean_vector <- c(T, F, T)

Una ultima pero no única forma de crear vectores es a través de las funciones de ejecución de secuencias.

# Operador dos puntos 
vector_integer <- 1:10
vector_integer
 [1]  1  2  3  4  5  6  7  8  9 10
# Función "seq()"
seq(1, 20, 2)
 [1]  1  3  5  7  9 11 13 15 17 19
# Función "seq_along()"
seq_along(letters)
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
[24] 24 25 26

Aquí algunos ejemplos de manipulación de los atributos del objeto en este caso empezamos con los nombres.

poker_vector <- c(140, -50, 20, -120, 240)
roulette_vector <- c(-24, -50, 100, -350, 10)  
names(poker_vector) <- c("Monday", "Tuesday", "Wednesday",
                       "Thursday", "Friday")
names(roulette_vector) <- c("Monday", "Tuesday", "Wednesday", 
                          "Thursday", "Friday")

Otra forma de realizar el proceso anterior el creando el vector de nombres en primera instancia.

poker_vector <- c(140, -50, 20, -120, 240)
roulette_vector <- c(-24, -50, 100, -350, 10)
days_vector <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
 
names(poker_vector) <- days_vector   
names(roulette_vector) <- days_vector
poker_vector; roulette_vector
   Monday   Tuesday Wednesday  Thursday    Friday 
      140       -50        20      -120       240 
   Monday   Tuesday Wednesday  Thursday    Friday 
      -24       -50       100      -350        10 

Las operaciones sobre vectores se hacen de manera posicional.

poker_vector <- c(140, -50, 20, -120, 240)
roulette_vector <- c(-24, -50, 100, -350, 10)
total_roulette <- sum(roulette_vector)
total_poker <- sum(poker_vector)
total_week <- total_poker + total_roulette
total_poker
[1] 230
total_roulette
[1] -314
total_week
[1] -84

La indexación de vectores se realiza de manera convencional a través del operador [ de manera tal que siempre se obtenga un vector como resultado

poker_vector <- c(140, -50, 20, -120, 240)
roulette_vector <- c(-24, -50, 100, -350, 10)
###
days_vector <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
names(roulette_vector) <- days_vector
names(poker_vector) <- days_vector
ans <- total_poker > total_roulette
poker_wednesday <- poker_vector[3]
poker_midweek <- poker_vector[c(2:4)] 
average_midweek_gain <- mean(poker_vector[c(1:3)])
selection_vector <- poker_vector > 0

Matrices

Como pudimos observar en el capítulo anterior las matrices pueden definirse como un vector de dos dimensiones, así veamos como se declaran y manipulan en el entorno R.

## Con la función matrix() 
matrix(1:9, byrow = T, nrow = 3) 
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

Otra forma es mediante el uso de vectores declarados

new_hope <- c(460.998, 314.4)
empire_strikes <- c(290.475, 247.900)
return_jedi <- c(309.306, 165.8)
star_wars_matrix <- matrix(c(new_hope, empire_strikes, return_jedi), 
                           byrow = T, nrow = 3)
colnames(star_wars_matrix)<-c("US", "non-US")
rownames(star_wars_matrix)<-c("A New Hope", "The Empire Strikes Back", 
                              "Return of the Jedi")
star_wars_matrix
                             US non-US
A New Hope              460.998  314.4
The Empire Strikes Back 290.475  247.9
Return of the Jedi      309.306  165.8

Las primeras operaciones sobre matrices que se pueden realizar son los resúmenes teniendo en cuenta las dimensiones de la misma. Recuerde las funciones cbind() y rbind() también pueden crear matrices.

box_office_all <- c(461, 314.4, 290.5, 247.9, 309.3, 165.8)
movie_names <- c("A New Hope","The Empire Strikes Back",
                 "Return of the Jedi")
col_titles <- c("US","non-US")
star_wars_matrix <- matrix(box_office_all, nrow = 3, 
                           byrow = TRUE, dimnames = list(movie_names,
                                                         col_titles))
worldwide_vector <- rowSums(star_wars_matrix)
all_wars_matrix <- cbind(star_wars_matrix, worldwide_vector) 
all_wars_matrix
                           US non-US worldwide_vector
A New Hope              461.0  314.4            775.4
The Empire Strikes Back 290.5  247.9            538.4
Return of the Jedi      309.3  165.8            475.1

Como en el caso anterior se pueden añadir filas en vez de columnas, el objeto sigue siendo una matriz si la declaración tiene sentido

 
total_revenue_vector <- colSums(all_wars_matrix)
total_revenue_vector
              US           non-US worldwide_vector 
          1060.8            728.1           1788.9 

La indexación en matrices se hace de manera similar a los vectores teniendo en cuenta que en este caso el atributo dimensión juega un papel muy importante

box_office_all <- c(461, 314.4, 290.5, 247.9, 309.3, 165.8)
movie_names <- c("A New Hope","The Empire Strikes Back",
                 "Return of the Jedi")
col_titles <- c("US","non-US")
star_wars_matrix <- matrix(box_office_all, nrow = 3, 
                           byrow = TRUE, 
                           dimnames = list(movie_names, col_titles))
non_us_all  <- mean(star_wars_matrix[,2])  ; non_us_all
[1] 242.7
non_us_some <- mean(star_wars_matrix[1:2,2]); non_us_some
[1] 281.15

Factores

En R los factores son la base para el tratamiento de variables categóricas, su definición parte de una cadena de caracteres y su tratamiento depende de esta característica.

## Creación de factores con `factor()`
gender_vector <- c("Male", "Female", "Female", "Male", "Male")
factor_gender_vector <- factor(gender_vector); factor_gender_vector
[1] Male   Female Female Male   Male  
Levels: Female Male

Parte de la manipulación de los factores de la especificación de sus niveles como se muestra a continuación.

animals_vector <- c("Elephant", "Giraffe", "Donkey", "Horse")
temperature_vector <- c("High", "Low", "High","Low", "Medium")
factor_animals_vector <- factor(animals_vector)
factor_animals_vector
[1] Elephant Giraffe  Donkey   Horse   
Levels: Donkey Elephant Giraffe Horse
factor_temperature_vector <- factor(temperature_vector,
                                    order = TRUE, 
                                    levels = c("Low", "Medium", "High"))
factor_temperature_vector
[1] High   Low    High   Low    Medium
Levels: Low < Medium < High
summary(factor_temperature_vector)
   Low Medium   High 
     2      1      2 

La indexación se realiza de forma similar a la vista en casos anteriores, tenga en cuenta que cambiar la naturaleza de un factor en un código donde se analizan variables categóricas es de cuidado.

survey_vector <- c("M", "F", "F", "M", "M")
factor_survey_vector <- factor(survey_vector)
levels(factor_survey_vector) <- c("Female", "Male")
factor_survey_vector[1] 
[1] Male
Levels: Female Male
factor_survey_vector[2] 
[1] Female
Levels: Female Male

Otra forma de tener variables ordinales es teniendo en cuenta los parámetros ordered y levels de la función factor()

speed_vector <- c("Fast", "Slow", "Slow", "Fast", "Ultra-fast") 
factor_speed_vector <- factor(speed_vector, 
                              ordered = T, 
                              levels = c("Slow","Fast","Ultra-fast")) 
factor_speed_vector
[1] Fast       Slow       Slow       Fast       Ultra-fast
Levels: Slow < Fast < Ultra-fast
summary(factor_speed_vector) 
      Slow       Fast Ultra-fast 
         2          2          1 

Data Frames

El objeto tabular de mayor uso en R, es el data frame posee las características de una lista como veremos permite realizar muchas de las tareas más comunes en el análisis de datos.

data("mtcars")
head(mtcars)
tail(mtcars)

Siempre se hace necesario revisar la estructura del data frame observado debido a los distintos datos que puede traer la lectura del mismo, esto sin importar la fuente de los datos.

str(mtcars)
'data.frame':   32 obs. of  11 variables:
 $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
 $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
 $ disp: num  160 160 108 258 360 ...
 $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
 $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
 $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
 $ qsec: num  16.5 17 18.6 19.4 17 ...
 $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
 $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
 $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
 $ carb: num  4 4 1 1 2 1 4 2 2 4 ...

la forma de creación manual de estructuras tabulares es con la función data.frame() teniendo en cuenta las estructuras de los vectores fuentes

planets <- c("Mercury", "Venus", "Earth", "Mars", 
             "Jupiter", "Saturn", "Uranus", "Neptune");
type <- c("Terrestrial planet", "Terrestrial planet", 
          "Terrestrial planet", "Terrestrial planet", 
          "Gas giant", "Gas giant", "Gas giant", "Gas giant")
diameter <- c(0.382, 0.949, 1, 0.532, 11.209, 9.449, 4.007, 3.883); 
rotation <- c(58.64, -243.02, 1, 1.03, 0.41, 0.43, -0.72, 0.67);
rings <- c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE);
planets_df  <- data.frame(planets, type, diameter, rotation, rings)
planets_df

La indexación se realiza con el operador [ de forma similar a los vectores y a las matrices.

closest_planets_df <- planets_df[1:3,] 
furthest_planets_df <- planets_df[6:8,]
closest_planets_df
furthest_planets_df
furthest_planets_diameter <- planets_df[3:8, 3]

Se pueden hacer manipulaciones basadas en creación de nuevas variables

rings_vector <- planets_df[, "rings"]
planets_with_rings_df <- planets_df[rings_vector, ]
planets_with_rings_df

La tarea principal del analista de datos muchas veces se remite a la obtención de subconjuntos de datos de una lista o marco de datos.

small_planets_df  <- subset(planets_df, subset = (diameter < 1)) 
small_planets_df
small_planets_df[order(small_planets_df$diameter), ]

Listas

Las listas se muestran como el objeto de mayor impacto en cuanto a manipulación de datos se refiere, a continuación se muestran ejemplos de creación y manipulación de objetos tipo listas en R.

my_vector <- 1:10 
my_matrix <- matrix(1:9, ncol = 3)
my_df <- mtcars[1:10,]
# Se construye una lista con la función `list()` en distintos tipos de datos 
my_list <- list(vec = my_vector, mat = my_matrix, df =  my_df)
my_list
$vec
 [1]  1  2  3  4  5  6  7  8  9 10

$mat
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

$df
NA

La forma de indexar listas es con base al operador [[, así teniendo en cuenta la posición del nodo y el objeto que contiene se puede obtener el resultado deseado.

act <- c("Jack Nicholson", "Shelley Duvall", 
         "Danny Lloyd", "Scatman Crothers", "Barry Nelson")
mov <- c("The Shining")
rev <- data.frame(scores = c(4.5, 4, 5), 
                  sources = c("IMDb1","IMDb2", "IMDb3"), 
                  comments = c("Best Horror Film I Have Ever Seen", 
                              "A truly brilliant and scary film from Stanley Kubrick", 
                              "A masterpiece of psychological horror"))
shining_list <- list(moviename = "The Shining", 
                     actors = act, reviews = rev)
shining_list$actors
[1] "Jack Nicholson"   "Shelley Duvall"   "Danny Lloyd"     
[4] "Scatman Crothers" "Barry Nelson"    
shining_list[[2]][2]
[1] "Shelley Duvall"
shining_list_full <- c(shining_list, year = 1980)
str(shining_list_full)
List of 4
 $ moviename: chr "The Shining"
 $ actors   : chr [1:5] "Jack Nicholson" "Shelley Duvall" "Danny Lloyd" "Scatman Crothers" ...
 $ reviews  :'data.frame':  3 obs. of  3 variables:
  ..$ scores  : num [1:3] 4.5 4 5
  ..$ sources : Factor w/ 3 levels "IMDb1","IMDb2",..: 1 2 3
  ..$ comments: Factor w/ 3 levels "A masterpiece of psychological horror",..: 3 2 1
 $ year     : num 1980
LS0tCnRpdGxlOiAiUiAtIFByYWN0aWNhIGNhcMOtdHVsbyBJIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBQcmFjdGljYSBOby4gMS4gLSBSIGLDoXNpY28gCgpFbiBlc3RhIHByYWN0aWNhIHNlIGVzdHVkaWFyYW4gbG9zIHByaW1lcm9zIGNvbmNlcHRvcyBhY2VyY2EgZGVsIG1hbmVqbyBkZWwgZW50b3JubyBkZSBSLCBsYSBjcmVhY2nDs24geSBtYW5pcHVsYWNpw7NuIGRlIGxvcyBvYmpldG9zIGVuIGVsIGFtYmllbnRlIGRlIHRyYWJham8gYWRlbcOhcyBkZSBvdHJvcyByZWxhY2lvbmFkb3MgY29uIGVsIGFjY2VzbyBhIGluZm9ybWFjacOzbiBkZXNkZSBmdWVudGVzIGRlIGRhdG9zIGludGVybmFzIHkgZXh0ZXJuYXMuIAoKIyMgRW50b3JubyBkZSBSCgpFbCBlbnRvcm5vIGRlIFIsIHNlIHB1ZWRlIGVudGVuZGVyIGNvbW8gZWwgbGEgY3JlYWNpw7NuIHkgYWNjZXNvIGEgb2JqZXRvcyBkZXNkZSBsYSBjb25zb2xhLCBlbGVtZW50byBwcmluY2lwYWwgZW4gbGEgZWRpY2nDs24gZGUgY8OzZGlnbyBSLCBhc8OtIHNlIG11ZXN0cmFuIGEgY29udGludWFjacOzbiBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBtYXMgaW1wb3J0YW50ZXMgZGUgZXN0ZSBlbGVtZW50by4KClBhcmEgZWRpY2nDs24gZGUgY8OzZGlnbyBjdWFscXVpZXIgaGVycmFtaWVudGEgZGUgdGV4dG8gZGlzcG9uaWJsZSBlcyBzdWZpY2llbnRlIGHDum4gYXPDrSBlbCBtYW5lam8gZGUgaGVycmFtaWVudGFzIGNvbW8gW1JTdHVkaW9dKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tKSBwdWVkZSBmYWNpbGl0YXIgbGEgaW1wbGVtZW50YWNpw7NuIGRlbCBjw7NkaWdvIGVuIFIuCgpgYGB7cn0KIyBDYWxjdWxhIDMgKyA0CjMgKyA0CgojIENhbGN1bGEgNiArIDEyCjYgKyAxMgpgYGAKCgpVbiBwcmltZXIgdmlzdGF6byBhbCBmdW5jaW9uYW1pZW50byBkZSBsYSBoZXJyYW1pZW50YSBSLCBlcyBiYWpvIGxhIHBlcnNwZWN0aXZhIGRlIGxhIHJlYWxpemFjacOzbiBkZSBjw6FsY3Vsb3MgYXJpdG3DqXRpY29zLiBBIGNvbnRpbnVhY2nDs24gc2UgbXVlc3RyYW4gYWxndW5hcyBkZSBsYXMgb3BlcmFjaW9uZXMgYsOhc2ljYXMgcXVlIHNlIHB1ZWRlbiByZWFsaXphciBjb24gc2ltcGxlcyBsbGFtYWRvcyBhIGZ1bmNpb25lcyBnZW7DqXJpY2FzIHkgdXRpbGl0YXJpYXMuICAgCgpgYGB7cn0KIyBVbmEgc3VtYQo1ICsgNSAKCiMgVW5hIHJlc3RhCjUgLSA1IAoKIyBVbmEgbXVsdGlwbGljYWNpw7NuIAozICogNQoKIyBVbmEgZGl2aXNpw7NuCig1ICsgNSkvMiAKCiMgVW5hIGV4cG9uZW5jaWFjacOzbiAKMl41CgojIE1vZHVsbyBwb3IgZGl2aXNpw7NuIAoyOCAlJSA2CgojIFJhaXogY3VhZHJhZGEgCjJeMTYKYGBgCiMjIyBOw7ptZXJvcyBlbiBSCkVuIFIgc2UgcHVlZGVuIGFsbWFjZW5hciBuw7ptZXJvcyBhY29yZGUgYWwgdGlwbyBkZSBkYXRvcywgZXMgaW1wb3J0YW50ZSBwYXJhIGxvcyBjYXNvcyBlbiBkb25kZSBlbCByZXN1bHRhZG8gZGUgdW4gY2FsY3VsbyBlcyBwcmVjaXNhbWVudGUgdW4gbsO6bWVybyAKCgpgYGB7cn0KMy8wOyAtMy8wOyAKZXhwKC1JbmYpOyAwL0luZgooMDozKV5JbmY7IDAvMApJbmYgLSBJbmY7IEluZi9JbmYKaXMuZmluaXRlKDEwKQppcy5maW5pdGUoSW5mKQpgYGAKCiMjIyBGdW5jaW9uZXMgbWlzY2Vsw6FuZWFzIAoKUGFyYSBjb25jbHVpciBlc3RlIGFwYXJ0YWRvIGVuIGVsIHF1ZSBSIHB1ZWRlIHNlciB2aXN0byBjb21vIHVuYSBoZXJyYW1pZW50YSBkZSBjw6FsY3Vsb3Mgc2UgbXVlc3RyYW4gYSBjb250aW51YWNpw7NuIGFsZ3VuYXMgZnVuY2lvbmVzIGRlIHVzbyBnZW7DqXJpY28uIAoKYGBge3J9CiMgUmHDrXogY3VhZHJhZGEKc3FydCg0KQoKIyBFeHBvbmVuY2lhbCAKZXhwKDEpOyBleHAoMik7IGV4cChsb2coMikpCgojIExvZ2FyaXRtbyAKbG9nMTAoMTAwKTsgCmxvZygxMDAsMTApICMgZWwgc2VndW5kbyBuw7ptZXJvIGVzIGxhIGJhc2UKbG9nKDEwKTsgbG9nKDEwLGJhc2UgPSBleHAoMSkpCgoKIyBGdW5jaW9uZXMgdHJpZ29ub23DqXRyaWNhcyAKY29zKHBpKTsgdGFuKHBpLzQpOyBzaW4ocGkpCgojIEZ1bmNpb25lcyBwYXJhIHJlZG9uZGVhciAKcm91bmQoc2luKHBpKSwxMCk7IHJvdW5kKDEuMzQ1NjIsIDIpCmZsb29yKHNpbihwaSkpOyBmbG9vcigxLjM0NTYyKQpjZWlsaW5nKHNpbihwaSkpOyBjZWlsaW5nKDEuMzQ1NjIpCmBgYAoKCgpVbiBjb25jZXB0byBiw6FzaWNvIGRlbCBhbsOhbGlzaXMgZGUgZGF0b3MgZW4gX0VzdGFkw61zdGljYV8gIGVzIGVsIGRlIF8qKnZhcmlhYmxlKipfLCBlbiBSIGxhcyB2YXJpYWJsZXMgc29uIG9iamV0b3MgdmlzdG9zIGRlc2RlIGVsIHRpcG8gZGUgZGF0b3MgcXVlIHNlIGxlIGFzaWduYSwgZW50b25jZXMgZXMgaW1wb3J0YW50ZSBjb25vY2VyIGxvcyB0aXBvcyBkZSBvcGVyYWRvcmVzIGRlIGFzaWduYWNpw7NuIGNvbW8gc2UgbXVlc3RyYSBhIGNvbnRpbnVhY2nDs24gCgpgYGB7cn0KIyBBc2lnbmFuZG8gdW4gdmFsb3IgNDUgYSAieCIsICJ5IiwgInoiLCAidyIKeCA8LSA0NQp5ID0gNDUKNDUgLT4gego0NSA9IHcKIyBJbXByaW1pZW5kbyBkZSBtYW5lcmEgYXV0b21hdGljYSBlbCB2YWxvciBkZSAieCIgCng7IHk7IHo7IHcKYGBgCgpMb3Mgbm9tYnJlcyBkZSB2YXJpYWJsZXMgc2lndWVuIHVuYSBzZXJpZSBkZSByZXN0cmljY2lvbmVzIGVuIFIgZGViaWRvIGEgcXVlIGVuIGVzdGUgZW50b3JubyBzZSBkaXN0aW5ndWVuIG1hecO6c2N1bGFzLCBtaW7DunNjdWxhcyB5IHBhbGFicmFzIHJlc2VydmFkYXMgZW50cmUgb3Ryb3MuIAoKYGBge3J9CnggPC0gMjsgWCA8LSAyIAp1bi5udW1lcm8uZ3JhbmRlIDwtIDEyNjU5NzI2NAp4XzFvIDwtIDM2CgoldW4ubnVtZXJvLmdyYW5kZSA8LSAxMjQ1NwoKdW4ubnVtPWVyby5ncmFuZGUgPC0gMTI0NTcKCnVuX251bWVyb19ncmFuZGUgPC0gMTU0Njk3NwoKCmBgYAoKCkxvcyB0aXBvcyBkZSBkYXRvcyBlbiBSLCBjb21vIGVuIGN1YWxxdWllciBvdHJvIGxlbmd1YWplIGRlIHByb2dyYW1hY2nDs24gY29icmFuIHZpdGFsIGltcG9ydGFuY2lhLCBkZWJpZG8gYSBlc3RvIGVsIGNvbm9jZXIgY2FkYSB1bm8gZGUgbG9zIHRpcG9zIGRlIGRhdG9zIHNlIHB1ZWRlIGVudGVuZGVyIGNvbW8gZWwgcHJvY2VzbyBhbG1hY2VuYW1pZW50byBpbnRlcm5vIGRlIGRhdG9zIGVuIFIuCgoKYGBge3J9CiMgRGVjbGFyYW5kbyBkaXN0aW50b3MgdGlwb3MgZGUgdmFyaWFibGVzCm15X251bWVyaWMgPC0gNDIKbXlfY2hhcmFjdGVyIDwtICJmb3J0eS10d28iCm15X2xvZ2ljYWwgPC0gRkFMU0UgCgojIEludmVzdGlnYXIgZWwgdGlwbyBkZSBkYXRvcyAKY2xhc3MobXlfbnVtZXJpYykKY2xhc3MobXlfY2hhcmFjdGVyKQpjbGFzcyhteV9sb2dpY2FsKQpgYGAKCiMjIE9iamV0b3MgZW4gUiAKClRvZG8gZW4gUiBwdWVkZSBzZXIgdmlzdG8gY29tbyB1biBvYmpldG8sIGVzdG8gaW1wbGljYSBhIHN1IHZleiBxdWUgdGllbmUgbm9tYnJlLCBjb250ZW5pZG8geSBhdHJpYnV0b3MuIAoKYGBge3J9CnggPC0gcm5vcm0oMTApOyBuYW1lcyh4KSA8LSBwYXN0ZSgiUG9zIiwgMToxMCkKeAptb2RlKHgpOyBsZW5ndGgoeCk7IGNsYXNzKHgpCmBgYAoKQ3VhbmRvIGVsIG9iamV0byBhZHF1aWVyZSBtw6FzIGF0cmlidXRvcyB5IGRpbWVuc2lvbmVzIGVzIGbDoWNpbCBvYnNlcnZhciBlc3RhcyBhIHRyYXbDqXMgZGUgbGFzIGZ1bmNpb25lcyBnZW7DqXJpY2FzIGBkaW0oKWAgeSBgYXR0cmlidXRlcygpYAoKYGBge3J9CmRhdGEoIm10Y2FycyIpCmRpbShtdGNhcnMpCmF0dHJpYnV0ZXMobXRjYXJzKQpgYGAKCiMjIyBWZWN0b3JlcyAKCkVsIG9iamV0byBtw6FzIHNpbXBsZSB5IGRlIG1heW9yIHVzbyBwYXJhIGVsIGNhbGN1bG8gZGUgY2FudGlkYWRlcyBlcyBlbCB2ZWN0b3IgYSBjb250aW51YWNpw7NuIHZlcmVtb3MgbGFzIG3Dumx0aXBsZXMgZm9ybWFzIGRlIGRlY2xhcmFyIHkgbWFuaXB1bGFyIHVuIHZlY3RvciBlbiBSLgoKYGBge3J9CiMgRGVjbGFyYW5kbyB2ZWN0b3JlcywgInZlY3RvcigpIiAKCm51bWVyaWNfaW50ZWdlciA8LSB2ZWN0b3IoImludGVnZXIiKTsgbnVtZXJpY19pbnRlZ2VyCm51bWVyaWNfdmVjdG9yIDwtIHZlY3RvcigibnVtZXJpYyIpOyBudW1lcmljX3ZlY3RvcgpjaGFyYWN0ZXJfdmVjdG9yIDwtIHZlY3RvcigiY2hhcmFjdGVyIik7IGNoYXJhY3Rlcl92ZWN0b3IKYm9vbGVhbl92ZWN0b3IgPC0gdmVjdG9yKCJsb2dpY2FsIik7IGJvb2xlYW5fdmVjdG9yCmNvbXBsZXhfdmVjdG9yIDwtIHZlY3RvcigiY29tcGxleCIpOyBjb21wbGV4X3ZlY3RvcgoKYGBgCgpMYSBmb3JtYSBtYXMgY29tw7puIGRlIGRlY2xhcmFyIHVuIHZlY3RvciBlbiBtZWRpYW50ZSBsYSBmdW5jacOzbiBjb25jYXRlbmFyIGBjKClgIGNvbW8gc2UgbXVlc3RyYSBhIGNvbnRpbnVhY2nDs24uIAoKYGBge3J9Cm51bWVyaWNfdmVjdG9yIDwtIGMoMSwgMTAsIDQ5KQpjaGFyYWN0ZXJfdmVjdG9yIDwtIGMoImEiLCAiYiIsICJjIikKYm9vbGVhbl92ZWN0b3IgPC0gYyhULCBGLCBUKQpgYGAKClVuYSB1bHRpbWEgcGVybyBubyDDum5pY2EgZm9ybWEgZGUgY3JlYXIgdmVjdG9yZXMgZXMgYSB0cmF2w6lzIGRlIGxhcyBmdW5jaW9uZXMgZGUgZWplY3VjacOzbiBkZSBzZWN1ZW5jaWFzLgoKYGBge3J9CiMgT3BlcmFkb3IgZG9zIHB1bnRvcyAKdmVjdG9yX2ludGVnZXIgPC0gMToxMAp2ZWN0b3JfaW50ZWdlcgoKIyBGdW5jacOzbiAic2VxKCkiCnNlcSgxLCAyMCwgMikKCiMgRnVuY2nDs24gInNlcV9hbG9uZygpIgoKc2VxX2Fsb25nKGxldHRlcnMpCgpgYGAKCkFxdcOtIGFsZ3Vub3MgZWplbXBsb3MgZGUgbWFuaXB1bGFjacOzbiBkZSBsb3MgYXRyaWJ1dG9zIGRlbCBvYmpldG8gZW4gZXN0ZSBjYXNvIGVtcGV6YW1vcyBjb24gbG9zIG5vbWJyZXMuICAKCmBgYHtyfQpwb2tlcl92ZWN0b3IgPC0gYygxNDAsIC01MCwgMjAsIC0xMjAsIDI0MCkKcm91bGV0dGVfdmVjdG9yIDwtIGMoLTI0LCAtNTAsIDEwMCwgLTM1MCwgMTApICAKbmFtZXMocG9rZXJfdmVjdG9yKSA8LSBjKCJNb25kYXkiLCAiVHVlc2RheSIsICJXZWRuZXNkYXkiLAogICAgICAgICAgICAgICAgICAgICAgICJUaHVyc2RheSIsICJGcmlkYXkiKQpuYW1lcyhyb3VsZXR0ZV92ZWN0b3IpIDwtIGMoIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICJUaHVyc2RheSIsICJGcmlkYXkiKQpgYGAKCk90cmEgZm9ybWEgZGUgcmVhbGl6YXIgZWwgcHJvY2VzbyBhbnRlcmlvciBlbCBjcmVhbmRvIGVsIHZlY3RvciBkZSBub21icmVzIGVuIHByaW1lcmEgaW5zdGFuY2lhLgoKYGBge3J9CnBva2VyX3ZlY3RvciA8LSBjKDE0MCwgLTUwLCAyMCwgLTEyMCwgMjQwKQpyb3VsZXR0ZV92ZWN0b3IgPC0gYygtMjQsIC01MCwgMTAwLCAtMzUwLCAxMCkKCmRheXNfdmVjdG9yIDwtIGMoIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsICJUaHVyc2RheSIsICJGcmlkYXkiKQogCm5hbWVzKHBva2VyX3ZlY3RvcikgPC0gZGF5c192ZWN0b3IgICAKbmFtZXMocm91bGV0dGVfdmVjdG9yKSA8LSBkYXlzX3ZlY3Rvcgpwb2tlcl92ZWN0b3I7IHJvdWxldHRlX3ZlY3RvcgpgYGAKCkxhcyBvcGVyYWNpb25lcyBzb2JyZSB2ZWN0b3JlcyBzZSBoYWNlbiBkZSBtYW5lcmEgcG9zaWNpb25hbC4KCmBgYHtyfQpwb2tlcl92ZWN0b3IgPC0gYygxNDAsIC01MCwgMjAsIC0xMjAsIDI0MCkKcm91bGV0dGVfdmVjdG9yIDwtIGMoLTI0LCAtNTAsIDEwMCwgLTM1MCwgMTApCnRvdGFsX3JvdWxldHRlIDwtIHN1bShyb3VsZXR0ZV92ZWN0b3IpCnRvdGFsX3Bva2VyIDwtIHN1bShwb2tlcl92ZWN0b3IpCnRvdGFsX3dlZWsgPC0gdG90YWxfcG9rZXIgKyB0b3RhbF9yb3VsZXR0ZQoKdG90YWxfcG9rZXIKdG90YWxfcm91bGV0dGUKdG90YWxfd2VlawpgYGAKCkxhIGluZGV4YWNpw7NuIGRlIHZlY3RvcmVzIHNlIHJlYWxpemEgZGUgbWFuZXJhIGNvbnZlbmNpb25hbCBhIHRyYXbDqXMgZGVsIG9wZXJhZG9yIGBbYCBkZSBtYW5lcmEgdGFsIHF1ZSBzaWVtcHJlIHNlIG9idGVuZ2EgdW4gdmVjdG9yIGNvbW8gcmVzdWx0YWRvCgpgYGB7cn0KcG9rZXJfdmVjdG9yIDwtIGMoMTQwLCAtNTAsIDIwLCAtMTIwLCAyNDApCnJvdWxldHRlX3ZlY3RvciA8LSBjKC0yNCwgLTUwLCAxMDAsIC0zNTAsIDEwKQojIyMKZGF5c192ZWN0b3IgPC0gYygiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIpCm5hbWVzKHJvdWxldHRlX3ZlY3RvcikgPC0gZGF5c192ZWN0b3IKbmFtZXMocG9rZXJfdmVjdG9yKSA8LSBkYXlzX3ZlY3RvcgoKCmFucyA8LSB0b3RhbF9wb2tlciA+IHRvdGFsX3JvdWxldHRlCnBva2VyX3dlZG5lc2RheSA8LSBwb2tlcl92ZWN0b3JbM10KcG9rZXJfbWlkd2VlayA8LSBwb2tlcl92ZWN0b3JbYygyOjQpXSAKYXZlcmFnZV9taWR3ZWVrX2dhaW4gPC0gbWVhbihwb2tlcl92ZWN0b3JbYygxOjMpXSkKc2VsZWN0aW9uX3ZlY3RvciA8LSBwb2tlcl92ZWN0b3IgPiAwCnBva2VyX3dpbm5pbmdfZGF5cyA8LSBwb2tlcl92ZWN0b3Jbc2VsZWN0aW9uX3ZlY3Rvcl0gCmBgYAoKIyMjIE1hdHJpY2VzIAoKQ29tbyBwdWRpbW9zIG9ic2VydmFyIGVuIGVsIGNhcMOtdHVsbyBhbnRlcmlvciBsYXMgbWF0cmljZXMgcHVlZGVuIGRlZmluaXJzZSBjb21vIHVuIHZlY3RvciBkZSBkb3MgZGltZW5zaW9uZXMsIGFzw60gdmVhbW9zIGNvbW8gc2UgZGVjbGFyYW4geSBtYW5pcHVsYW4gZW4gZWwgZW50b3JubyBSLiAKCmBgYHtyfQojIyBDb24gbGEgZnVuY2nDs24gbWF0cml4KCkgCm1hdHJpeCgxOjksIGJ5cm93ID0gVCwgbnJvdyA9IDMpIApgYGAKCk90cmEgZm9ybWEgZXMgbWVkaWFudGUgZWwgdXNvIGRlIHZlY3RvcmVzIGRlY2xhcmFkb3MgCgpgYGB7cn0KbmV3X2hvcGUgPC0gYyg0NjAuOTk4LCAzMTQuNCkKZW1waXJlX3N0cmlrZXMgPC0gYygyOTAuNDc1LCAyNDcuOTAwKQpyZXR1cm5famVkaSA8LSBjKDMwOS4zMDYsIDE2NS44KQoKc3Rhcl93YXJzX21hdHJpeCA8LSBtYXRyaXgoYyhuZXdfaG9wZSwgZW1waXJlX3N0cmlrZXMsIHJldHVybl9qZWRpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5cm93ID0gVCwgbnJvdyA9IDMpCgpjb2xuYW1lcyhzdGFyX3dhcnNfbWF0cml4KSA8LSBjKCJVUyIsICJub24tVVMiKQpyb3duYW1lcyhzdGFyX3dhcnNfbWF0cml4KSA8LSBjKCJBIE5ldyBIb3BlIiwgIlRoZSBFbXBpcmUgU3RyaWtlcyBCYWNrIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZXR1cm4gb2YgdGhlIEplZGkiKQoKc3Rhcl93YXJzX21hdHJpeApgYGAKTGFzIHByaW1lcmFzIG9wZXJhY2lvbmVzIHNvYnJlIG1hdHJpY2VzIHF1ZSBzZSBwdWVkZW4gcmVhbGl6YXIgc29uIGxvcyByZXPDum1lbmVzIHRlbmllbmRvIGVuIGN1ZW50YSBsYXMgZGltZW5zaW9uZXMgZGUgbGEgbWlzbWEuIFJlY3VlcmRlIGxhcyBmdW5jaW9uZXMgYGNiaW5kKClgIHkgYHJiaW5kKClgIHRhbWJpw6luIHB1ZWRlbiBjcmVhciBtYXRyaWNlcy4KCmBgYHtyfQpib3hfb2ZmaWNlX2FsbCA8LSBjKDQ2MSwgMzE0LjQsIDI5MC41LCAyNDcuOSwgMzA5LjMsIDE2NS44KQptb3ZpZV9uYW1lcyA8LSBjKCJBIE5ldyBIb3BlIiwiVGhlIEVtcGlyZSBTdHJpa2VzIEJhY2siLAogICAgICAgICAgICAgICAgICJSZXR1cm4gb2YgdGhlIEplZGkiKQpjb2xfdGl0bGVzIDwtIGMoIlVTIiwibm9uLVVTIikKc3Rhcl93YXJzX21hdHJpeCA8LSBtYXRyaXgoYm94X29mZmljZV9hbGwsIG5yb3cgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgYnlyb3cgPSBUUlVFLCBkaW1uYW1lcyA9IGxpc3QobW92aWVfbmFtZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF90aXRsZXMpKQp3b3JsZHdpZGVfdmVjdG9yIDwtIHJvd1N1bXMoc3Rhcl93YXJzX21hdHJpeCkKYWxsX3dhcnNfbWF0cml4IDwtIGNiaW5kKHN0YXJfd2Fyc19tYXRyaXgsIHdvcmxkd2lkZV92ZWN0b3IpIAphbGxfd2Fyc19tYXRyaXgKYGBgCgpDb21vIGVuIGVsIGNhc28gYW50ZXJpb3Igc2UgcHVlZGVuIGHDsWFkaXIgZmlsYXMgZW4gdmV6IGRlIGNvbHVtbmFzLCBlbCBvYmpldG8gc2lndWUgc2llbmRvIHVuYSBtYXRyaXogc2kgbGEgZGVjbGFyYWNpw7NuIHRpZW5lIHNlbnRpZG8gCgpgYGB7cn0KIAp0b3RhbF9yZXZlbnVlX3ZlY3RvciA8LSBjb2xTdW1zKGFsbF93YXJzX21hdHJpeCkKdG90YWxfcmV2ZW51ZV92ZWN0b3IKYGBgCgpMYSBpbmRleGFjacOzbiBlbiBtYXRyaWNlcyBzZSBoYWNlIGRlIG1hbmVyYSBzaW1pbGFyIGEgbG9zIHZlY3RvcmVzIHRlbmllbmRvIGVuIGN1ZW50YSBxdWUgZW4gZXN0ZSBjYXNvIGVsIGF0cmlidXRvIGRpbWVuc2nDs24ganVlZ2EgdW4gcGFwZWwgbXV5IGltcG9ydGFudGUgCgpgYGB7cn0KYm94X29mZmljZV9hbGwgPC0gYyg0NjEsIDMxNC40LCAyOTAuNSwgMjQ3LjksIDMwOS4zLCAxNjUuOCkKbW92aWVfbmFtZXMgPC0gYygiQSBOZXcgSG9wZSIsIlRoZSBFbXBpcmUgU3RyaWtlcyBCYWNrIiwKICAgICAgICAgICAgICAgICAiUmV0dXJuIG9mIHRoZSBKZWRpIikKY29sX3RpdGxlcyA8LSBjKCJVUyIsIm5vbi1VUyIpCnN0YXJfd2Fyc19tYXRyaXggPC0gbWF0cml4KGJveF9vZmZpY2VfYWxsLCBucm93ID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5cm93ID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChtb3ZpZV9uYW1lcywgY29sX3RpdGxlcykpCm5vbl91c19hbGwgIDwtIG1lYW4oc3Rhcl93YXJzX21hdHJpeFssMl0pICA7IG5vbl91c19hbGwKbm9uX3VzX3NvbWUgPC0gbWVhbihzdGFyX3dhcnNfbWF0cml4WzE6MiwyXSk7IG5vbl91c19zb21lCmBgYAoKIyMjIEZhY3RvcmVzIAoKRW4gUiBsb3MgZmFjdG9yZXMgc29uIGxhIGJhc2UgcGFyYSBlbCB0cmF0YW1pZW50byBkZSB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzLCBzdSBkZWZpbmljacOzbiBwYXJ0ZSBkZSB1bmEgY2FkZW5hIGRlIGNhcmFjdGVyZXMgeSBzdSB0cmF0YW1pZW50byBkZXBlbmRlIGRlIGVzdGEgY2FyYWN0ZXLDrXN0aWNhLiAKCmBgYHtyfQojIyBDcmVhY2nDs24gZGUgZmFjdG9yZXMgY29uIGBmYWN0b3IoKWAKZ2VuZGVyX3ZlY3RvciA8LSBjKCJNYWxlIiwgIkZlbWFsZSIsICJGZW1hbGUiLCAiTWFsZSIsICJNYWxlIikKZmFjdG9yX2dlbmRlcl92ZWN0b3IgPC0gZmFjdG9yKGdlbmRlcl92ZWN0b3IpOyBmYWN0b3JfZ2VuZGVyX3ZlY3RvcgpgYGAKClBhcnRlIGRlIGxhIG1hbmlwdWxhY2nDs24gZGUgbG9zIGZhY3RvcmVzIGRlIGxhIGVzcGVjaWZpY2FjacOzbiBkZSBzdXMgbml2ZWxlcyBjb21vIHNlIG11ZXN0cmEgYSBjb250aW51YWNpw7NuLiAKCmBgYHtyfQphbmltYWxzX3ZlY3RvciA8LSBjKCJFbGVwaGFudCIsICJHaXJhZmZlIiwgIkRvbmtleSIsICJIb3JzZSIpCnRlbXBlcmF0dXJlX3ZlY3RvciA8LSBjKCJIaWdoIiwgIkxvdyIsICJIaWdoIiwiTG93IiwgIk1lZGl1bSIpCgpmYWN0b3JfYW5pbWFsc192ZWN0b3IgPC0gZmFjdG9yKGFuaW1hbHNfdmVjdG9yKQpmYWN0b3JfYW5pbWFsc192ZWN0b3IKZmFjdG9yX3RlbXBlcmF0dXJlX3ZlY3RvciA8LSBmYWN0b3IodGVtcGVyYXR1cmVfdmVjdG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giKSkKZmFjdG9yX3RlbXBlcmF0dXJlX3ZlY3RvcgpzdW1tYXJ5KGZhY3Rvcl90ZW1wZXJhdHVyZV92ZWN0b3IpCmBgYAoKTGEgaW5kZXhhY2nDs24gc2UgcmVhbGl6YSBkZSBmb3JtYSBzaW1pbGFyIGEgbGEgdmlzdGEgZW4gY2Fzb3MgYW50ZXJpb3JlcywgdGVuZ2EgZW4gY3VlbnRhIHF1ZSBjYW1iaWFyIGxhIG5hdHVyYWxlemEgZGUgdW4gZmFjdG9yIGVuIHVuIGPDs2RpZ28gZG9uZGUgc2UgYW5hbGl6YW4gdmFyaWFibGVzIGNhdGVnw7NyaWNhcyBlcyBkZSBjdWlkYWRvLiAKCmBgYHtyfQpzdXJ2ZXlfdmVjdG9yIDwtIGMoIk0iLCAiRiIsICJGIiwgIk0iLCAiTSIpCmZhY3Rvcl9zdXJ2ZXlfdmVjdG9yIDwtIGZhY3RvcihzdXJ2ZXlfdmVjdG9yKQpsZXZlbHMoZmFjdG9yX3N1cnZleV92ZWN0b3IpIDwtIGMoIkZlbWFsZSIsICJNYWxlIikKCmZhY3Rvcl9zdXJ2ZXlfdmVjdG9yWzFdIApmYWN0b3Jfc3VydmV5X3ZlY3RvclsyXSAKYGBgCk90cmEgZm9ybWEgZGUgdGVuZXIgdmFyaWFibGVzIG9yZGluYWxlcyBlcyB0ZW5pZW5kbyBlbiBjdWVudGEgbG9zIHBhcsOhbWV0cm9zIGBvcmRlcmVkYCB5IGBsZXZlbHNgIGRlIGxhIGZ1bmNpw7NuIGBmYWN0b3IoKWAKCmBgYHtyfQpzcGVlZF92ZWN0b3IgPC0gYygiRmFzdCIsICJTbG93IiwgIlNsb3ciLCAiRmFzdCIsICJVbHRyYS1mYXN0IikgCmZhY3Rvcl9zcGVlZF92ZWN0b3IgPC0gZmFjdG9yKHNwZWVkX3ZlY3RvciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiU2xvdyIsIkZhc3QiLCJVbHRyYS1mYXN0IikpIApmYWN0b3Jfc3BlZWRfdmVjdG9yCnN1bW1hcnkoZmFjdG9yX3NwZWVkX3ZlY3RvcikgCmBgYAoKIyMjIERhdGEgRnJhbWVzIAoKRWwgb2JqZXRvIHRhYnVsYXIgZGUgbWF5b3IgdXNvIGVuIFIsIGVzIGVsIF9kYXRhIGZyYW1lXyBwb3NlZSBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBkZSB1bmEgbGlzdGEgY29tbyB2ZXJlbW9zIHBlcm1pdGUgcmVhbGl6YXIgbXVjaGFzIGRlIGxhcyB0YXJlYXMgbcOhcyBjb211bmVzIGVuIGVsIGFuw6FsaXNpcyBkZSBkYXRvcy4gCgpgYGB7cn0KZGF0YSgibXRjYXJzIikKaGVhZChtdGNhcnMpCnRhaWwobXRjYXJzKQpgYGAKClNpZW1wcmUgc2UgaGFjZSBuZWNlc2FyaW8gcmV2aXNhciBsYSBlc3RydWN0dXJhIGRlbCBkYXRhIGZyYW1lIG9ic2VydmFkbyBkZWJpZG8gYSBsb3MgZGlzdGludG9zIGRhdG9zIHF1ZSBwdWVkZSB0cmFlciBsYSBsZWN0dXJhIGRlbCBtaXNtbywgZXN0byBzaW4gaW1wb3J0YXIgbGEgZnVlbnRlIGRlIGxvcyBkYXRvcy4KCmBgYHtyfQpzdHIobXRjYXJzKQpgYGAKCmxhIGZvcm1hIGRlIGNyZWFjacOzbiBtYW51YWwgZGUgZXN0cnVjdHVyYXMgdGFidWxhcmVzIGVzIGNvbiBsYSBmdW5jacOzbiBgZGF0YS5mcmFtZSgpYCB0ZW5pZW5kbyBlbiBjdWVudGEgbGFzIGVzdHJ1Y3R1cmFzIGRlIGxvcyB2ZWN0b3JlcyBmdWVudGVzIAoKYGBge3J9CnBsYW5ldHMgPC0gYygiTWVyY3VyeSIsICJWZW51cyIsICJFYXJ0aCIsICJNYXJzIiwgCiAgICAgICAgICAgICAiSnVwaXRlciIsICJTYXR1cm4iLCAiVXJhbnVzIiwgIk5lcHR1bmUiKTsKdHlwZSA8LSBjKCJUZXJyZXN0cmlhbCBwbGFuZXQiLCAiVGVycmVzdHJpYWwgcGxhbmV0IiwgCiAgICAgICAgICAiVGVycmVzdHJpYWwgcGxhbmV0IiwgIlRlcnJlc3RyaWFsIHBsYW5ldCIsIAogICAgICAgICAgIkdhcyBnaWFudCIsICJHYXMgZ2lhbnQiLCAiR2FzIGdpYW50IiwgIkdhcyBnaWFudCIpCmRpYW1ldGVyIDwtIGMoMC4zODIsIDAuOTQ5LCAxLCAwLjUzMiwgMTEuMjA5LCA5LjQ0OSwgNC4wMDcsIDMuODgzKTsgCnJvdGF0aW9uIDwtIGMoNTguNjQsIC0yNDMuMDIsIDEsIDEuMDMsIDAuNDEsIDAuNDMsIC0wLjcyLCAwLjY3KTsKcmluZ3MgPC0gYyhGQUxTRSwgRkFMU0UsIEZBTFNFLCBGQUxTRSwgVFJVRSwgVFJVRSwgVFJVRSwgVFJVRSk7CnBsYW5ldHNfZGYgIDwtIGRhdGEuZnJhbWUocGxhbmV0cywgdHlwZSwgZGlhbWV0ZXIsIHJvdGF0aW9uLCByaW5ncykKcGxhbmV0c19kZgoKYGBgCkxhIGluZGV4YWNpw7NuIHNlIHJlYWxpemEgY29uIGVsIG9wZXJhZG9yIGBbYCBkZSBmb3JtYSBzaW1pbGFyIGEgbG9zIHZlY3RvcmVzIHkgYSBsYXMgbWF0cmljZXMuIAoKYGBge3J9CmNsb3Nlc3RfcGxhbmV0c19kZiA8LSBwbGFuZXRzX2RmWzE6MyxdIApmdXJ0aGVzdF9wbGFuZXRzX2RmIDwtIHBsYW5ldHNfZGZbNjo4LF0KCmNsb3Nlc3RfcGxhbmV0c19kZgpmdXJ0aGVzdF9wbGFuZXRzX2RmCmZ1cnRoZXN0X3BsYW5ldHNfZGlhbWV0ZXIgPC0gcGxhbmV0c19kZlszOjgsIDNdCmBgYAoKU2UgcHVlZGVuIGhhY2VyIG1hbmlwdWxhY2lvbmVzIGJhc2FkYXMgZW4gY3JlYWNpw7NuIGRlIG51ZXZhcyB2YXJpYWJsZXMgCgpgYGB7cn0KcmluZ3NfdmVjdG9yIDwtIHBsYW5ldHNfZGZbLCAicmluZ3MiXQpwbGFuZXRzX3dpdGhfcmluZ3NfZGYgPC0gcGxhbmV0c19kZltyaW5nc192ZWN0b3IsIF0KcGxhbmV0c193aXRoX3JpbmdzX2RmCgpgYGAKTGEgdGFyZWEgcHJpbmNpcGFsIGRlbCBhbmFsaXN0YSBkZSBkYXRvcyBtdWNoYXMgdmVjZXMgc2UgcmVtaXRlIGEgbGEgb2J0ZW5jacOzbiBkZSBzdWJjb25qdW50b3MgZGUgZGF0b3MgZGUgdW5hIGxpc3RhIG8gbWFyY28gZGUgZGF0b3MuIAoKYGBge3J9CnNtYWxsX3BsYW5ldHNfZGYgIDwtIHN1YnNldChwbGFuZXRzX2RmLCBzdWJzZXQgPSAoZGlhbWV0ZXIgPCAxKSkgCnNtYWxsX3BsYW5ldHNfZGYKc21hbGxfcGxhbmV0c19kZltvcmRlcihzbWFsbF9wbGFuZXRzX2RmJGRpYW1ldGVyKSwgXQpgYGAKCiMjIyBMaXN0YXMgCgpMYXMgbGlzdGFzIHNlIG11ZXN0cmFuIGNvbW8gZWwgb2JqZXRvIGRlIG1heW9yIGltcGFjdG8gZW4gY3VhbnRvIGEgbWFuaXB1bGFjacOzbiBkZSBkYXRvcyBzZSByZWZpZXJlLCBhIGNvbnRpbnVhY2nDs24gc2UgbXVlc3RyYW4gZWplbXBsb3MgZGUgY3JlYWNpw7NuIHkgbWFuaXB1bGFjacOzbiBkZSBvYmpldG9zIHRpcG8gbGlzdGFzIGVuIFIuCmBgYHtyfQpteV92ZWN0b3IgPC0gMToxMCAKbXlfbWF0cml4IDwtIG1hdHJpeCgxOjksIG5jb2wgPSAzKQpteV9kZiA8LSBtdGNhcnNbMToxMCxdCgojIFNlIGNvbnN0cnV5ZSB1bmEgbGlzdGEgY29uIGxhIGZ1bmNpw7NuIGBsaXN0KClgIGVuIGRpc3RpbnRvcyB0aXBvcyBkZSBkYXRvcyAKbXlfbGlzdCA8LSBsaXN0KHZlYyA9IG15X3ZlY3RvciwgbWF0ID0gbXlfbWF0cml4LCBkZiA9ICBteV9kZikKbXlfbGlzdApgYGAKTGEgZm9ybWEgZGUgaW5kZXhhciBsaXN0YXMgZXMgY29uIGJhc2UgYWwgb3BlcmFkb3IgYFtbYCwgYXPDrSB0ZW5pZW5kbyBlbiBjdWVudGEgbGEgcG9zaWNpw7NuIGRlbCBub2RvIHkgZWwgb2JqZXRvIHF1ZSBjb250aWVuZSBzZSBwdWVkZSBvYnRlbmVyIGVsIHJlc3VsdGFkbyBkZXNlYWRvLgoKYGBge3J9CmFjdCA8LSBjKCJKYWNrIE5pY2hvbHNvbiIsICJTaGVsbGV5IER1dmFsbCIsIAogICAgICAgICAiRGFubnkgTGxveWQiLCAiU2NhdG1hbiBDcm90aGVycyIsICJCYXJyeSBOZWxzb24iKQptb3YgPC0gYygiVGhlIFNoaW5pbmciKQpyZXYgPC0gZGF0YS5mcmFtZShzY29yZXMgPSBjKDQuNSwgNCwgNSksIAogICAgICAgICAgICAgICAgICBzb3VyY2VzID0gYygiSU1EYjEiLCJJTURiMiIsICJJTURiMyIpLCAKICAgICAgICAgICAgICAgICAgY29tbWVudHMgPSBjKCJCZXN0IEhvcnJvciBGaWxtIEkgSGF2ZSBFdmVyIFNlZW4iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkEgdHJ1bHkgYnJpbGxpYW50IGFuZCBzY2FyeSBmaWxtIGZyb20gU3RhbmxleSBLdWJyaWNrIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBIG1hc3RlcnBpZWNlIG9mIHBzeWNob2xvZ2ljYWwgaG9ycm9yIikpCnNoaW5pbmdfbGlzdCA8LSBsaXN0KG1vdmllbmFtZSA9ICJUaGUgU2hpbmluZyIsIAogICAgICAgICAgICAgICAgICAgICBhY3RvcnMgPSBhY3QsIHJldmlld3MgPSByZXYpCgpzaGluaW5nX2xpc3QkYWN0b3JzCnNoaW5pbmdfbGlzdFtbMl1dWzJdCgpzaGluaW5nX2xpc3RfZnVsbCA8LSBjKHNoaW5pbmdfbGlzdCwgeWVhciA9IDE5ODApCnN0cihzaGluaW5nX2xpc3RfZnVsbCkKCmBgYAoK