Un objeto, dentro del contexto de computación y programación, es un elemento dentro de la memoria en uso con el cual se puede interactuar mediante herramientas como R.

1. Operadores matemáticos

Toda herramienta que trabaje con datos tiene incorporado un esquema de operaciones básicas (o también llamadas binarias) que permiten su uso como calculadora.

1.1 Suma y Resta

4 + 6
[1] 10
3 - 2
[1] 1
25 + 11 - 8
[1] 28

1.2 Multiplicación y División

3 * 5
[1] 15
6 / 2
[1] 3

1.3 Potencias

3 ^ 2 
[1] 9
3 ** 2 
[1] 9
4 ** (1/3)
[1] 1.587401

Notemos que se puede escribir potencias de dos formas:

  • Mediante el uso de ^, o bien,
  • Mediante el uso de **.

1.4 Módulos

8 %% 3
[1] 2
5 %% 2
[1] 1

Los módulos corresponden al residuo de la división, y son muy útiles para detectar si un valor es par o impar, o también, para generar iteraciones complejas.

2. Variables

Así como en matemáticas, las variables son representaciones algebraicas que tienen la capacidad de guardar un valor. Vale señalar que el uso de variables, es el primer (y muy importante) paso para la generalización de un programa computacional.

2.1 Variables numéricas decimales

n1 = 4
n2 <- -8.65

Notemos, que para asignar un valor a una variable se puede usar de forma indistinta un simbolo = o una “flecha” <-.

Para imprimir el valor de una variable se pueden optar por las siguientes opciones:

  • Impresion simple
n1
[1] 4
  • Impresión mediante función (recomendada)
print(n2)
[1] -8.65

2.2 Variables numéricas enteras

e1 = 4L
print(e1)
[1] 4

Notemos que para especificar que la variable es de tipo entero debemos incorporar una letra L después del número.

Para conocer y verificar la clase o tipo de variable podemos realizar lo siguiente:

class(n1)
[1] "numeric"
class(e1)
[1] "integer"

La clase numeric de R es equivalente a lo que nosotros hemos referido como decimal.

En este punto, verifiquemos el área de objetos y notemos que todas las variables que estamos creando se están guardando allí.

2.3 Variables de texto

t1 = "Hola"
t2 <- "Mi nombre es juan"

Imprimamos ahora estas variables:

print(t1)
[1] "Hola"
print(t2)
[1] "Mi nombre es juan"

Y verifiquemos su clase:

class(t1)
[1] "character"
class(t2)
[1] "character"

2.4 Variables de tipo factor

En este punto es conveniente distinguir los factores en dos grandes grupos:

  • Los factores binarios (o buleanos) que solamente pueden tomar valores en un rango de 2 elementos.
  • Los demás factores que pueden tomar valores en un rango de más de 2 elementos.

2.4.1 Factores buleanos

b1 = TRUE
b2 <- FALSE

Veamos la clase de estas variables:

class(b1)
[1] "logical"
class(b2)
[1] "logical"

Notemos que cuando un factor es de tipo VERDADERO/FALSO se le suele llamar factor lógico.

Intentemos ahora esta forma de generar factores buleanos:

b3 <- factor(1, levels = 1:2)

Imprimamos el factor:

print(b3)
[1] 1
Levels: 1 2

Lo que acabamos de hacer es decirle a R que nuestra variable b3 es un factor que tiene un valor de 1, y que el posible rango del factor es 1 o 2. Revise la asignación de b3 y confirme este enunciado.

Verifiquemos ahora la clase de esta variable

class(b3)
[1] "factor"

2.4.2 Otros factores

colores_semaforo = factor("amarillo", levels = c("verde", "amarillo", "rojo"))

Revise esta asignación de variable e intente comprenderla, si le cuesta no se preocupe, es normal pues aún no hemos visto todos los objetos básicos que posee R.

Imprimamos y veamos la clase de esta variable

print(colores_semaforo)
[1] amarillo
Levels: verde amarillo rojo
class(colores_semaforo)
[1] "factor"

2.5 Operaciones binarias con variables

Al igual que con números es posible ejecutar operaciones matemáticas con variables, siempre y cuando estas tengan sentido.

(n1 + n2)*e1
[1] -18.6
n1**2
[1] 16

Por ejemplo, mire que esta operación va a generar un error puesto que carece de sentido

t1 + 5
Error in t1 + 5 : non-numeric argument to binary operator

Conociendo ya lo que son las variables lógicas (VERDADERO/FALSO), podemos también ver las operaciones matemáticas lógicas:

  • Operación AND
b1 & b2
[1] FALSE
  • Operación OR
b1 | b2
[1] TRUE
  • Operación NOT
!b1
[1] FALSE

Así como las operaciones de contraste lógico, como las siguientes:

n1 > 3
[1] TRUE
n2 <= 0
[1] TRUE
e1 == 3
[1] FALSE
n1 < n2
[1] FALSE
(n2 > 2) & (n2 < 4)
[1] FALSE

Nótemos que el uso del == no es una asignación sino un constraste lógico. En esto debemos tener mucho cuidado cuando programemos cosas más complejas.

3. Vectores

Los vectores son conjuntos de uno o más valores del mismo tipo o clase. En R constituyen el elemento fundamental de otros objetos como las matrices, o los data frames.

3.1 Construcción estándar

Los vectores se pueden construir de varias formas, pero la siguiente es la más común:

v1 <- c(2,4,6,8,10,12)
v2 <- c("j","u","a","n")
v3 <- c(TRUE,FALSE,TRUE)

Imprimamos estos objetos y veamos su clase:

print(v1)
[1]  2  4  6  8 10 12
class(v1)
[1] "numeric"
print(v2)
[1] "j" "u" "a" "n"
class(v2)
[1] "character"
print(v3)
[1]  TRUE FALSE  TRUE
class(v3)
[1] "logical"

Es importante observar que los vectores, si bien poseen varios elementos, mantienen la clase de cada uno de ellos.

3.2 Otras formas de construcción

  • Rangos o intervalos:
v4 <- 1:6
print(v4)
[1] 1 2 3 4 5 6
  • Secuencias:
v5 <- seq(from = 1, to = 10, by = 2)
print(v5)
[1] 1 3 5 7 9
  • Repeticiones:
v6 <- rep("a",times = 5)
print(v6)
[1] "a" "a" "a" "a" "a"

3.3 Operaciones binarias con vectores

Se pueden ejecutar tanto operaciones matemáticas como lógicas en vectores, de forma equivalente a lo hecho en variables.

  • Operaciones matemáticas
2*v1 - v4
[1]  3  6  9 12 15 18
v5/2
[1] 0.5 1.5 2.5 3.5 4.5
  • Operaciones lógicas
v4 > 3
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE
v6 == "b"
[1] FALSE FALSE FALSE FALSE FALSE

Adicional a esto, se pueden incluir como operaciones binarias con vectores a las siguientes estadísticas básicas:

  • Tamaño o norma del vector:
length(v1)
[1] 6
  • Media, mediana, mínimo y máximo del vector (sólo para vectores numéricos):
mean(v4)
[1] 3.5
median(v4)
[1] 3.5
min(v4)
[1] 1
max(v4)
[1] 6
  • Desviación estándar, varianza, y rango IQR del vector (sólo para vectores numéricos):
var(v1)
[1] 14
sd(v1)
[1] 3.741657
IQR(v1)
[1] 5
  • Suma de todos los elementos del vector (sólo para vectores numéricos):
sum(v5)
[1] 25
  • Correlación entre dos vectores (sólo para vectores numéricos):
cor(v1,v4)
[1] 1
  • Concatenación de vectores:
paste(v6, collapse = ",")
[1] "a,a,a,a,a"
paste(v2, collapse = "")
[1] "juan"

3.4 Indexacion de vectores

En muchas ocasiones es importante ubicar elementos específicos de un vector. En este sentido y en términos generales, diremos indexar un vector para el proceso de identificar un elemento en concreto del mismo.

v1[1]
[1] 2
v2[4]
[1] "n"
v5[3]
[1] 5

Adicionalmente, es posible indexar vectores utilizando operaciones binarias lógicas y estadísticas que ya hemos visto, así por ejemplo:

v1[length(v1)]
[1] 12
v2[v2 == "u"]
[1] "u"
v4[v4 > 3]
[1] 4 5 6

También es posible indexar, usando otro vector para obtener más de un elemento:

v5[c(1,4)]
[1] 1 7

O indexar para obtener un vector SIN un elemento en específico utilizando el signo “-”:

v2[-3]
[1] "j" "u" "n"

Cómo veremos más adelante, la indexación es una característica importante también en matrices y en data frames. Lo anterior debido a que, como se indicó, el vector es su elemento constitutivo principal.

4. Matrices

Una matriz es un ordenamiento vectorial de elementos en n filas y m columnas. Una característica importante de las matrices, radica en que dentro de ellas solamente pueden existir elementos de una misma clase.

4.1 Construcción estándar

Para empezar, definamos un vector con 16 de elementos numéricos.

v7 <- c(5,7,4,0,1,-6,-1,2,0,8,1,9,0,-5,-9,1)

Ahora, construyamos una matriz cuadrada:

n <- sqrt(length(v7))
m <- n
m1 <- matrix(data = v7,
             nrow = n,
             ncol = m)

Para definir una matriz, nótese que se deben establecer 3 parametros: unos datos que corresponden a un vector, un número de columnas, y un número de filas.

Imprimamos la matriz para verla en detalle

print(m1)
     [,1] [,2] [,3] [,4]
[1,]    5    1    0    0
[2,]    7   -6    8   -5
[3,]    4   -1    1   -9
[4,]    0    2    9    1

Es importante señalar que la matriz va llenándose por columnas.

Construyamos ahora una matriz a partir del mismo vector, pero en esta ocasión no la hagamos cuadrada.

m2 <- matrix(data = v7,
             nrow = 8,
             ncol = 2)
print(m2)
     [,1] [,2]
[1,]    5    0
[2,]    7    8
[3,]    4    1
[4,]    0    9
[5,]    1    0
[6,]   -6   -5
[7,]   -1   -9
[8,]    2    1

Verifiquemos la clase de estos objetos creados:

class(m1)
[1] "matrix" "array" 
class(m2)
[1] "matrix" "array" 

4.2 Otras formas de construcción

  • Anexar vectores por columnas:
v8 <- c("a","b","c")
v9 <- c("d","e","f")
m3 <- cbind(v8,v9)
print(m3)
     v8  v9 
[1,] "a" "d"
[2,] "b" "e"
[3,] "c" "f"
  • Anexar vectores por filas:
m4 <- rbind(v8,v9)
print(m4)
   [,1] [,2] [,3]
v8 "a"  "b"  "c" 
v9 "d"  "e"  "f" 

*Generar matrices de un solo valor

print(m5)
     [,1] [,2] [,3] [,4]
[1,]    5    5    5    5
[2,]    5    5    5    5
[3,]    5    5    5    5
[4,]    5    5    5    5

4.3 Operaciones binarias con matrices

Igual que en vectores, es posible ejecutar operaciones matemáticas, lógicas y estadísticas. Sin embargo se debe ser cuidadoso por un lado para diferenciar entre operaciones matemáticas propias del álgebra lineal, y aquellas del manejo de datos; y por otro, en revisar siempre las dimensiones (filas y columnas) de modo que no se generen errores.

  • Operaciones matemáticas (manejo de datos)
-5.2*m1 + m5
      [,1] [,2]  [,3] [,4]
[1,] -21.0 -0.2   5.0  5.0
[2,] -31.4 36.2 -36.6 31.0
[3,] -15.8 10.2  -0.2 51.8
[4,]   5.0 -5.4 -41.8 -0.2

Si se multiplican matrices de diferentes dimensiones se genera error:

m1 * m2
Error in m1 * m2 : non-conformable arrays
  • Operaciones matemáticas (álgebra lineal)

Se pueden generar multiplicaciones bajo este contexto siempre que el número de columnas de la primera matriz sea igual al número de filas de la segunda.

m6 <- cbind(c(3,2,1),c(4,0,1),c(5,4,6))
m7 <- rbind(c(5,5),c(1,4),c(0,-1))
m6 %*% m7
     [,1] [,2]
[1,]   19   26
[2,]   10    6
[3,]    6    3

Adicionalmente, podemos calcular las transpuestas, inversas, determinantes y valores y vectores propios de matrices cuadradas.

t(m6)
     [,1] [,2] [,3]
[1,]    3    2    1
[2,]    4    0    1
[3,]    5    4    6
solve(m6)
            [,1]        [,2]        [,3]
[1,]  0.11764706  0.55882353 -0.47058824
[2,]  0.23529412 -0.38235294  0.05882353
[3,] -0.05882353 -0.02941176  0.23529412
det(m6)
[1] -34
eigen(m6)
eigen() decomposition
$values
[1]  8.398959  2.334835 -1.733793

$vectors
           [,1]       [,2]        [,3]
[1,] -0.7643881 -0.9168930  0.63419426
[2,] -0.4164422 -0.2431649 -0.77296551
[3,] -0.4922262  0.3165090  0.01794349
  • Operaciones estadísticas

Se puede obtener el valor medio de las columnas y las filas

colMeans(m1)
[1]  4.00 -1.00  4.50 -3.25
rowMeans(m1)
[1]  1.50  1.00 -1.25  3.00

Así también podemos calcular las dimensiones

print(m4)
   [,1] [,2] [,3]
v8 "a"  "b"  "c" 
v9 "d"  "e"  "f" 
dim(m4)
[1] 2 3
ncol(m4)
[1] 3
nrow(m4)
[1] 2

4.4 Indexación de matrices

De forma similar a un vector, se puede indexar una matriz con la particularidad que debemos especificar ya no solamente una posición, sino dos (filas y columnas).

m3[3,2]
 v9 
"f" 

Si queremos indexar toda una fila, se lo puede hacer de la siguiente forma:

m1[2,]
[1]  7 -6  8 -5

Y para columnas, la sintaxis es similar:

m2[,2]
[1]  0  8  1  9  0 -5 -9  1

Al igual que en lo visto en vectores, es posible aplicar operaciones binarias, para indexar una matriz.

m6[1,ncol(m6)]
[1] 5

5. Data Frames

Los data frames o tablas de datos, se pueden entender como un arreglo de elementos similar a la matrices, pero con la principal diferencia que cada columna puede tener una clase diferente.

En las áreas de inteligencia de negocios y de analítica, los data frames son el objeto más importante y frecuente con el cual se trabaja.

5.1 Construcción estándar

df1 <- data.frame(num = c(1,2,3),
                  txt = c("a","b","c"),
                  int = rep(4L,3),
                  boo = c(TRUE,TRUE,FALSE))

Imprimamos el data frame para visualizarlo

print(df1)

Los data frames pueden ser visualizados además de la siguiente forma, en consideración a que generalmente contienen muchos datos, y su visualización en la consola puede ser limitada o dificultosa.

View(df1)

Para evidenciar esto, tomemos un data frame integrado en R que se llama iris

print(iris)
View(iris)

Verifiquemos la clase de estos objetos:

class(df1)
[1] "data.frame"

5.2 Otras formas de construcción

Otra forma de construir data frames es a partir de una matriz previamente creada

m8 <- cbind(seq(from = 2, to = 100, by = 2),
            seq(from = 1, to = 20.8, by = 0.4),
            -24:25)
df2 <- data.frame(m8)
View(df2)

5.3 Oparaciones binarias con data frames

Las siguientes son las principales operaciones que se pueden ejecutar con data frames.

  • Resumen estadístico
summary(df1)
      num          txt                 int       boo         
 Min.   :1.0   Length:3           Min.   :4   Mode :logical  
 1st Qu.:1.5   Class :character   1st Qu.:4   FALSE:1        
 Median :2.0   Mode  :character   Median :4   TRUE :2        
 Mean   :2.0                      Mean   :4                  
 3rd Qu.:2.5                      3rd Qu.:4                  
 Max.   :3.0                      Max.   :4                  
  • Estructura
str(df1)
'data.frame':   3 obs. of  4 variables:
 $ num: num  1 2 3
 $ txt: chr  "a" "b" "c"
 $ int: int  4 4 4
 $ boo: logi  TRUE TRUE FALSE
  • Dimensiones (igual que una matriz)
dim(df1)
[1] 3 4
ncol(df1)
[1] 4
nrow(df1)
[1] 3
  • Nombres de columnas
names(df1)
[1] "num" "txt" "int" "boo"

5.4 Indexación de data frames

Un data frame puede indexarse de la misma forma que una matriz.

df2[10,3]
[1] -15

Sin embargo, al momento de ejecutar análisis de datos esta forma de indexar no suele ser muy eficiente. Por lo que también es posible indexar especificando nombres de columnas:

iris$Sepal.Length[123]
[1] 7.7

O también de esta forma:

df1[3,"txt"]
[1] "c"

Cuando empecemos a utilizar el paquete tidyverse vamos a profundizar mas en la construcción y manipulación de data frames y sus objetos hermanos: los tibbles.

6. Listas

Como puntos extras para su evaluación final, se ganará 5 puntos el primer estudiante que me remita por correo un script de R en el cual, se detalle una descripción de los objetos denominados LISTAS.

En concreto, en el script debe especificarse:

  1. Qué son
  2. Sus formas de construcción
  3. Sus operaciones binarias
  4. Su indexación
LS0tDQp0aXRsZTogIk9iamV0b3MgQsOhc2ljb3MgZW4gUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNClVuIG9iamV0bywgZGVudHJvIGRlbCBjb250ZXh0byBkZSBjb21wdXRhY2nDs24geSBwcm9ncmFtYWNpw7NuLCBlcyB1biBlbGVtZW50byBkZW50cm8gZGUgbGEgbWVtb3JpYSBlbiB1c28gY29uIGVsIGN1YWwgc2UgcHVlZGUgaW50ZXJhY3R1YXIgbWVkaWFudGUgaGVycmFtaWVudGFzIGNvbW8gUi4NCg0KIyMgMS4gT3BlcmFkb3JlcyBtYXRlbcOhdGljb3MNCg0KVG9kYSBoZXJyYW1pZW50YSBxdWUgdHJhYmFqZSBjb24gZGF0b3MgdGllbmUgaW5jb3Jwb3JhZG8gdW4gZXNxdWVtYSBkZSBvcGVyYWNpb25lcyBiw6FzaWNhcyAobyB0YW1iacOpbiBsbGFtYWRhcyBiaW5hcmlhcykgcXVlIHBlcm1pdGVuIHN1IHVzbyBjb21vIGNhbGN1bGFkb3JhLg0KDQojIyMgMS4xIFN1bWEgeSBSZXN0YQ0KYGBge3J9DQo0ICsgNg0KMyAtIDINCjI1ICsgMTEgLSA4DQpgYGANCiMjIyAxLjIgTXVsdGlwbGljYWNpw7NuIHkgRGl2aXNpw7NuDQpgYGB7cn0NCjMgKiA1DQo2IC8gMg0KYGBgDQojIyMgMS4zIFBvdGVuY2lhcw0KYGBge3J9DQozIF4gMiANCjMgKiogMiANCjQgKiogKDEvMykNCmBgYA0KTm90ZW1vcyBxdWUgc2UgcHVlZGUgZXNjcmliaXIgcG90ZW5jaWFzIGRlIGRvcyBmb3JtYXM6DQoNCiogTWVkaWFudGUgZWwgdXNvIGRlIF4sIG8gYmllbiwNCiogTWVkaWFudGUgZWwgdXNvIGRlICoqLg0KDQojIyMgMS40IE3Ds2R1bG9zDQpgYGB7cn0NCjggJSUgMw0KNSAlJSAyDQpgYGANCkxvcyBtw7NkdWxvcyBjb3JyZXNwb25kZW4gYWwgcmVzaWR1byBkZSBsYSBkaXZpc2nDs24sIHkgc29uIG11eSDDunRpbGVzIHBhcmEgZGV0ZWN0YXIgc2kgdW4gdmFsb3IgZXMgcGFyIG8gaW1wYXIsIG8gdGFtYmnDqW4sIHBhcmEgZ2VuZXJhciBpdGVyYWNpb25lcyBjb21wbGVqYXMuDQoNCiMjIDIuIFZhcmlhYmxlcw0KDQpBc8OtIGNvbW8gZW4gbWF0ZW3DoXRpY2FzLCBsYXMgdmFyaWFibGVzIHNvbiByZXByZXNlbnRhY2lvbmVzIGFsZ2VicmFpY2FzIHF1ZSB0aWVuZW4gbGEgY2FwYWNpZGFkIGRlIGd1YXJkYXIgdW4gdmFsb3IuIFZhbGUgc2XDsWFsYXIgcXVlIGVsIHVzbyBkZSB2YXJpYWJsZXMsIGVzIGVsIHByaW1lciAoeSBtdXkgaW1wb3J0YW50ZSkgcGFzbyBwYXJhIGxhIGdlbmVyYWxpemFjacOzbiBkZSB1biBwcm9ncmFtYSBjb21wdXRhY2lvbmFsLg0KDQojIyMgMi4xIFZhcmlhYmxlcyBudW3DqXJpY2FzIGRlY2ltYWxlcw0KYGBge3J9DQpuMSA9IDQNCm4yIDwtIC04LjY1DQpgYGANCg0KTm90ZW1vcywgcXVlIHBhcmEgYXNpZ25hciB1biB2YWxvciBhIHVuYSB2YXJpYWJsZSBzZSBwdWVkZSB1c2FyIGRlIGZvcm1hIGluZGlzdGludGEgdW4gc2ltYm9sbyA9IG8gdW5hICJmbGVjaGEiIDwtLg0KDQpQYXJhIGltcHJpbWlyIGVsIHZhbG9yIGRlIHVuYSB2YXJpYWJsZSBzZSBwdWVkZW4gb3B0YXIgcG9yIGxhcyBzaWd1aWVudGVzIG9wY2lvbmVzOg0KDQoqIEltcHJlc2lvbiBzaW1wbGUNCmBgYHtyfQ0KbjENCmBgYA0KKiBJbXByZXNpw7NuIG1lZGlhbnRlIGZ1bmNpw7NuIChyZWNvbWVuZGFkYSkNCmBgYHtyfQ0KcHJpbnQobjIpDQpgYGANCg0KIyMjIDIuMiBWYXJpYWJsZXMgbnVtw6lyaWNhcyBlbnRlcmFzDQpgYGB7cn0NCmUxID0gNEwNCnByaW50KGUxKQ0KYGBgDQpOb3RlbW9zIHF1ZSBwYXJhIGVzcGVjaWZpY2FyIHF1ZSBsYSB2YXJpYWJsZSBlcyBkZSB0aXBvIGVudGVybyBkZWJlbW9zIGluY29ycG9yYXIgdW5hIGxldHJhIEwgZGVzcHXDqXMgZGVsIG7Dum1lcm8uIA0KDQpQYXJhIGNvbm9jZXIgeSB2ZXJpZmljYXIgbGEgY2xhc2UgbyB0aXBvIGRlIHZhcmlhYmxlIHBvZGVtb3MgcmVhbGl6YXIgbG8gc2lndWllbnRlOg0KYGBge3J9DQpjbGFzcyhuMSkNCmNsYXNzKGUxKQ0KYGBgDQpMYSBjbGFzZSAqbnVtZXJpYyogZGUgUiBlcyBlcXVpdmFsZW50ZSBhIGxvIHF1ZSBub3NvdHJvcyBoZW1vcyByZWZlcmlkbyBjb21vIGRlY2ltYWwuDQoNCkVuIGVzdGUgcHVudG8sIHZlcmlmaXF1ZW1vcyBlbCDDoXJlYSBkZSBvYmpldG9zIHkgbm90ZW1vcyBxdWUgdG9kYXMgbGFzIHZhcmlhYmxlcyBxdWUgZXN0YW1vcyBjcmVhbmRvIHNlIGVzdMOhbiBndWFyZGFuZG8gYWxsw60uDQoNCiMjIyAyLjMgVmFyaWFibGVzIGRlIHRleHRvDQpgYGB7cn0NCnQxID0gIkhvbGEiDQp0MiA8LSAiTWkgbm9tYnJlIGVzIGp1YW4iDQpgYGANCg0KSW1wcmltYW1vcyBhaG9yYSBlc3RhcyB2YXJpYWJsZXM6DQpgYGB7cn0NCnByaW50KHQxKQ0KcHJpbnQodDIpDQpgYGANClkgdmVyaWZpcXVlbW9zIHN1IGNsYXNlOg0KYGBge3J9DQpjbGFzcyh0MSkNCmNsYXNzKHQyKQ0KYGBgDQojIyMgMi40IFZhcmlhYmxlcyBkZSB0aXBvIGZhY3Rvcg0KDQpFbiBlc3RlIHB1bnRvIGVzIGNvbnZlbmllbnRlIGRpc3Rpbmd1aXIgbG9zIGZhY3RvcmVzIGVuIGRvcyBncmFuZGVzIGdydXBvczoNCg0KKiBMb3MgZmFjdG9yZXMgYmluYXJpb3MgKG8gYnVsZWFub3MpIHF1ZSBzb2xhbWVudGUgcHVlZGVuIHRvbWFyIHZhbG9yZXMgZW4gdW4gcmFuZ28gZGUgMiBlbGVtZW50b3MuDQoqIExvcyBkZW3DoXMgZmFjdG9yZXMgcXVlIHB1ZWRlbiB0b21hciB2YWxvcmVzIGVuIHVuIHJhbmdvIGRlIG3DoXMgZGUgMiBlbGVtZW50b3MuDQoNCiMjIyMgMi40LjEgRmFjdG9yZXMgYnVsZWFub3MNCmBgYHtyfQ0KYjEgPSBUUlVFDQpiMiA8LSBGQUxTRQ0KYGBgDQoNClZlYW1vcyBsYSBjbGFzZSBkZSBlc3RhcyB2YXJpYWJsZXM6DQpgYGB7cn0NCmNsYXNzKGIxKQ0KY2xhc3MoYjIpDQpgYGANCk5vdGVtb3MgcXVlIGN1YW5kbyB1biBmYWN0b3IgZXMgZGUgdGlwbyBWRVJEQURFUk8vRkFMU08gc2UgbGUgc3VlbGUgbGxhbWFyIGZhY3RvciBsw7NnaWNvLg0KDQpJbnRlbnRlbW9zIGFob3JhIGVzdGEgZm9ybWEgZGUgZ2VuZXJhciBmYWN0b3JlcyBidWxlYW5vczoNCmBgYHtyfQ0KYjMgPC0gZmFjdG9yKDEsIGxldmVscyA9IDE6MikNCmBgYA0KDQpJbXByaW1hbW9zIGVsIGZhY3RvcjoNCmBgYHtyfQ0KcHJpbnQoYjMpDQpgYGANCkxvIHF1ZSBhY2FiYW1vcyBkZSBoYWNlciBlcyBkZWNpcmxlIGEgUiBxdWUgbnVlc3RyYSB2YXJpYWJsZSBiMyBlcyB1biBmYWN0b3IgcXVlIHRpZW5lIHVuIHZhbG9yIGRlIDEsIHkgcXVlIGVsIHBvc2libGUgcmFuZ28gZGVsIGZhY3RvciBlcyAxIG8gMi4gUmV2aXNlIGxhIGFzaWduYWNpw7NuIGRlIGIzIHkgY29uZmlybWUgZXN0ZSBlbnVuY2lhZG8uDQoNClZlcmlmaXF1ZW1vcyBhaG9yYSBsYSBjbGFzZSBkZSBlc3RhIHZhcmlhYmxlDQpgYGB7cn0NCmNsYXNzKGIzKQ0KYGBgDQojIyMjIDIuNC4yIE90cm9zIGZhY3RvcmVzDQpgYGB7cn0NCmNvbG9yZXNfc2VtYWZvcm8gPSBmYWN0b3IoImFtYXJpbGxvIiwgbGV2ZWxzID0gYygidmVyZGUiLCAiYW1hcmlsbG8iLCAicm9qbyIpKQ0KYGBgDQoNClJldmlzZSBlc3RhIGFzaWduYWNpw7NuIGRlIHZhcmlhYmxlIGUgaW50ZW50ZSBjb21wcmVuZGVybGEsIHNpIGxlIGN1ZXN0YSBubyBzZSBwcmVvY3VwZSwgZXMgbm9ybWFsIHB1ZXMgYcO6biBubyBoZW1vcyB2aXN0byB0b2RvcyBsb3Mgb2JqZXRvcyBiw6FzaWNvcyBxdWUgcG9zZWUgUi4NCg0KSW1wcmltYW1vcyB5IHZlYW1vcyBsYSBjbGFzZSBkZSBlc3RhIHZhcmlhYmxlDQpgYGB7cn0NCnByaW50KGNvbG9yZXNfc2VtYWZvcm8pDQpjbGFzcyhjb2xvcmVzX3NlbWFmb3JvKQ0KYGBgDQojIyMgMi41IE9wZXJhY2lvbmVzIGJpbmFyaWFzIGNvbiB2YXJpYWJsZXMNCg0KQWwgaWd1YWwgcXVlIGNvbiBuw7ptZXJvcyBlcyBwb3NpYmxlIGVqZWN1dGFyIG9wZXJhY2lvbmVzIG1hdGVtw6F0aWNhcyBjb24gdmFyaWFibGVzLCBzaWVtcHJlIHkgY3VhbmRvIGVzdGFzIHRlbmdhbiBzZW50aWRvLg0KYGBge3J9DQoobjEgKyBuMikqZTENCm4xKioyDQpgYGANClBvciBlamVtcGxvLCBtaXJlIHF1ZSBlc3RhIG9wZXJhY2nDs24gdmEgYSBnZW5lcmFyIHVuIGVycm9yIHB1ZXN0byBxdWUgY2FyZWNlIGRlIHNlbnRpZG8NCmBgYHtyfQ0KdDEgKyA1DQpgYGANCkNvbm9jaWVuZG8geWEgbG8gcXVlIHNvbiBsYXMgdmFyaWFibGVzIGzDs2dpY2FzIChWRVJEQURFUk8vRkFMU08pLCBwb2RlbW9zIHRhbWJpw6luIHZlciBsYXMgb3BlcmFjaW9uZXMgbWF0ZW3DoXRpY2FzIGzDs2dpY2FzOg0KDQoqIE9wZXJhY2nDs24gQU5EDQpgYGB7cn0NCmIxICYgYjINCmBgYA0KKiBPcGVyYWNpw7NuIE9SDQpgYGB7cn0NCmIxIHwgYjINCmBgYA0KKiBPcGVyYWNpw7NuIE5PVA0KYGBge3J9DQohYjENCmBgYA0KDQpBc8OtIGNvbW8gbGFzIG9wZXJhY2lvbmVzIGRlIGNvbnRyYXN0ZSBsw7NnaWNvLCBjb21vIGxhcyBzaWd1aWVudGVzOg0KYGBge3J9DQpuMSA+IDMNCm4yIDw9IDANCmUxID09IDMNCm4xIDwgbjINCihuMiA+IDIpICYgKG4yIDwgNCkNCmBgYA0KTsOzdGVtb3MgcXVlIGVsIHVzbyBkZWwgPT0gbm8gZXMgdW5hIGFzaWduYWNpw7NuIHNpbm8gdW4gY29uc3RyYXN0ZSBsw7NnaWNvLiBFbiBlc3RvIGRlYmVtb3MgdGVuZXIgbXVjaG8gY3VpZGFkbyBjdWFuZG8gcHJvZ3JhbWVtb3MgY29zYXMgbcOhcyBjb21wbGVqYXMuDQoNCiMjIDMuIFZlY3RvcmVzDQoNCkxvcyB2ZWN0b3JlcyBzb24gY29uanVudG9zIGRlIHVubyBvIG3DoXMgdmFsb3JlcyBkZWwgbWlzbW8gdGlwbyBvIGNsYXNlLiBFbiBSIGNvbnN0aXR1eWVuIGVsIGVsZW1lbnRvIGZ1bmRhbWVudGFsIGRlIG90cm9zIG9iamV0b3MgY29tbyBsYXMgbWF0cmljZXMsIG8gbG9zIGRhdGEgZnJhbWVzLg0KDQojIyMgMy4xIENvbnN0cnVjY2nDs24gZXN0w6FuZGFyDQoNCkxvcyB2ZWN0b3JlcyBzZSBwdWVkZW4gY29uc3RydWlyIGRlIHZhcmlhcyBmb3JtYXMsIHBlcm8gbGEgc2lndWllbnRlIGVzIGxhIG3DoXMgY29tw7puOg0KYGBge3J9DQp2MSA8LSBjKDIsNCw2LDgsMTAsMTIpDQp2MiA8LSBjKCJqIiwidSIsImEiLCJuIikNCnYzIDwtIGMoVFJVRSxGQUxTRSxUUlVFKQ0KYGBgDQoNCkltcHJpbWFtb3MgZXN0b3Mgb2JqZXRvcyB5IHZlYW1vcyBzdSBjbGFzZToNCmBgYHtyfQ0KcHJpbnQodjEpDQpjbGFzcyh2MSkNCnByaW50KHYyKQ0KY2xhc3ModjIpDQpwcmludCh2MykNCmNsYXNzKHYzKQ0KYGBgDQpFcyBpbXBvcnRhbnRlIG9ic2VydmFyIHF1ZSBsb3MgdmVjdG9yZXMsIHNpIGJpZW4gcG9zZWVuIHZhcmlvcyBlbGVtZW50b3MsIG1hbnRpZW5lbiBsYSBjbGFzZSBkZSBjYWRhIHVubyBkZSBlbGxvcy4NCg0KIyMjIDMuMiBPdHJhcyBmb3JtYXMgZGUgY29uc3RydWNjacOzbg0KDQoqIFJhbmdvcyBvIGludGVydmFsb3M6DQpgYGB7cn0NCnY0IDwtIDE6Ng0KcHJpbnQodjQpDQpgYGANCiogU2VjdWVuY2lhczoNCmBgYHtyfQ0KdjUgPC0gc2VxKGZyb20gPSAxLCB0byA9IDEwLCBieSA9IDIpDQpwcmludCh2NSkNCmBgYA0KKiBSZXBldGljaW9uZXM6DQpgYGB7cn0NCnY2IDwtIHJlcCgiYSIsdGltZXMgPSA1KQ0KcHJpbnQodjYpDQpgYGANCiMjIyAzLjMgT3BlcmFjaW9uZXMgYmluYXJpYXMgY29uIHZlY3RvcmVzDQoNClNlIHB1ZWRlbiBlamVjdXRhciB0YW50byBvcGVyYWNpb25lcyBtYXRlbcOhdGljYXMgY29tbyBsw7NnaWNhcyBlbiB2ZWN0b3JlcywgZGUgZm9ybWEgZXF1aXZhbGVudGUgYSBsbyBoZWNobyBlbiB2YXJpYWJsZXMuDQoNCiogT3BlcmFjaW9uZXMgbWF0ZW3DoXRpY2FzDQpgYGB7cn0NCjIqdjEgLSB2NA0KdjUvMg0KYGBgDQoqIE9wZXJhY2lvbmVzIGzDs2dpY2FzDQpgYGB7cn0NCnY0ID4gMw0KdjYgPT0gImIiDQpgYGANCkFkaWNpb25hbCBhIGVzdG8sIHNlIHB1ZWRlbiBpbmNsdWlyIGNvbW8gb3BlcmFjaW9uZXMgYmluYXJpYXMgY29uIHZlY3RvcmVzIGEgbGFzIHNpZ3VpZW50ZXMgZXN0YWTDrXN0aWNhcyBiw6FzaWNhczoNCg0KKiBUYW1hw7FvIG8gbm9ybWEgZGVsIHZlY3RvcjoNCmBgYHtyfQ0KbGVuZ3RoKHYxKQ0KYGBgDQoqIE1lZGlhLCBtZWRpYW5hLCBtw61uaW1vIHkgbcOheGltbyBkZWwgdmVjdG9yIChzw7NsbyBwYXJhIHZlY3RvcmVzIG51bcOpcmljb3MpOg0KYGBge3J9DQptZWFuKHY0KQ0KbWVkaWFuKHY0KQ0KbWluKHY0KQ0KbWF4KHY0KQ0KYGBgDQoqIERlc3ZpYWNpw7NuIGVzdMOhbmRhciwgdmFyaWFuemEsIHkgcmFuZ28gSVFSIGRlbCB2ZWN0b3IgKHPDs2xvIHBhcmEgdmVjdG9yZXMgbnVtw6lyaWNvcyk6DQpgYGB7cn0NCnZhcih2MSkNCnNkKHYxKQ0KSVFSKHYxKQ0KYGBgDQoqIFN1bWEgZGUgdG9kb3MgbG9zIGVsZW1lbnRvcyBkZWwgdmVjdG9yIChzw7NsbyBwYXJhIHZlY3RvcmVzIG51bcOpcmljb3MpOg0KYGBge3J9DQpzdW0odjUpDQpgYGANCiogQ29ycmVsYWNpw7NuIGVudHJlIGRvcyB2ZWN0b3JlcyAoc8OzbG8gcGFyYSB2ZWN0b3JlcyBudW3DqXJpY29zKToNCmBgYHtyfQ0KY29yKHYxLHY0KQ0KYGBgDQoqIENvbmNhdGVuYWNpw7NuIGRlIHZlY3RvcmVzOg0KYGBge3J9DQpwYXN0ZSh2NiwgY29sbGFwc2UgPSAiLCIpDQpwYXN0ZSh2MiwgY29sbGFwc2UgPSAiIikNCmBgYA0KIyMjIDMuNCBJbmRleGFjaW9uIGRlIHZlY3RvcmVzDQoNCkVuIG11Y2hhcyBvY2FzaW9uZXMgZXMgaW1wb3J0YW50ZSB1YmljYXIgZWxlbWVudG9zIGVzcGVjw61maWNvcyBkZSB1biB2ZWN0b3IuIEVuIGVzdGUgc2VudGlkbyB5IGVuIHTDqXJtaW5vcyBnZW5lcmFsZXMsIGRpcmVtb3MgKmluZGV4YXIqIHVuIHZlY3RvciBwYXJhIGVsIHByb2Nlc28gZGUgaWRlbnRpZmljYXIgdW4gZWxlbWVudG8gZW4gY29uY3JldG8gZGVsIG1pc21vLg0KDQpgYGB7cn0NCnYxWzFdDQp2Mls0XQ0KdjVbM10NCmBgYA0KQWRpY2lvbmFsbWVudGUsIGVzIHBvc2libGUgaW5kZXhhciB2ZWN0b3JlcyB1dGlsaXphbmRvIG9wZXJhY2lvbmVzIGJpbmFyaWFzIGzDs2dpY2FzIHkgZXN0YWTDrXN0aWNhcyBxdWUgeWEgaGVtb3MgdmlzdG8sIGFzw60gcG9yIGVqZW1wbG86DQpgYGB7cn0NCnYxW2xlbmd0aCh2MSldDQp2Mlt2MiA9PSAidSJdDQp2NFt2NCA+IDNdDQpgYGANClRhbWJpw6luIGVzIHBvc2libGUgaW5kZXhhciwgdXNhbmRvIG90cm8gdmVjdG9yIHBhcmEgb2J0ZW5lciBtw6FzIGRlIHVuIGVsZW1lbnRvOg0KYGBge3J9DQp2NVtjKDEsNCldDQpgYGANCk8gaW5kZXhhciBwYXJhIG9idGVuZXIgdW4gdmVjdG9yIFNJTiB1biBlbGVtZW50byBlbiBlc3BlY8OtZmljbyB1dGlsaXphbmRvIGVsIHNpZ25vICItIjoNCmBgYHtyfQ0KdjJbLTNdDQpgYGANCkPDs21vIHZlcmVtb3MgbcOhcyBhZGVsYW50ZSwgbGEgaW5kZXhhY2nDs24gZXMgdW5hIGNhcmFjdGVyw61zdGljYSBpbXBvcnRhbnRlIHRhbWJpw6luIGVuIG1hdHJpY2VzIHkgZW4gZGF0YSBmcmFtZXMuIExvIGFudGVyaW9yIGRlYmlkbyBhIHF1ZSwgY29tbyBzZSBpbmRpY8OzLCBlbCB2ZWN0b3IgZXMgc3UgZWxlbWVudG8gY29uc3RpdHV0aXZvIHByaW5jaXBhbC4NCg0KIyMgNC4gTWF0cmljZXMNCg0KVW5hIG1hdHJpeiBlcyB1biBvcmRlbmFtaWVudG8gdmVjdG9yaWFsIGRlIGVsZW1lbnRvcyBlbiBuIGZpbGFzIHkgbSBjb2x1bW5hcy4gVW5hIGNhcmFjdGVyw61zdGljYSBpbXBvcnRhbnRlIGRlIGxhcyBtYXRyaWNlcywgcmFkaWNhIGVuIHF1ZSBkZW50cm8gZGUgZWxsYXMgc29sYW1lbnRlIHB1ZWRlbiBleGlzdGlyIGVsZW1lbnRvcyBkZSB1bmEgbWlzbWEgY2xhc2UuDQoNCiMjIyA0LjEgQ29uc3RydWNjacOzbiBlc3TDoW5kYXINCg0KUGFyYSBlbXBlemFyLCBkZWZpbmFtb3MgdW4gdmVjdG9yIGNvbiAxNiBkZSBlbGVtZW50b3MgbnVtw6lyaWNvcy4NCg0KYGBge3J9DQp2NyA8LSBjKDUsNyw0LDAsMSwtNiwtMSwyLDAsOCwxLDksMCwtNSwtOSwxKQ0KYGBgDQoNCkFob3JhLCBjb25zdHJ1eWFtb3MgdW5hIG1hdHJpeiBjdWFkcmFkYToNCmBgYHtyfQ0KbiA8LSBzcXJ0KGxlbmd0aCh2NykpDQptIDwtIG4NCm0xIDwtIG1hdHJpeChkYXRhID0gdjcsDQogICAgICAgICAgICAgbnJvdyA9IG4sDQogICAgICAgICAgICAgbmNvbCA9IG0pDQpgYGANClBhcmEgZGVmaW5pciB1bmEgbWF0cml6LCBuw7N0ZXNlIHF1ZSBzZSBkZWJlbiBlc3RhYmxlY2VyIDMgcGFyYW1ldHJvczogdW5vcyBkYXRvcyBxdWUgY29ycmVzcG9uZGVuIGEgdW4gdmVjdG9yLCB1biBuw7ptZXJvIGRlIGNvbHVtbmFzLCB5IHVuIG7Dum1lcm8gZGUgZmlsYXMuDQoNCkltcHJpbWFtb3MgbGEgbWF0cml6IHBhcmEgdmVybGEgZW4gZGV0YWxsZQ0KYGBge3J9DQpwcmludChtMSkNCmBgYA0KRXMgaW1wb3J0YW50ZSBzZcOxYWxhciBxdWUgbGEgbWF0cml6IHZhIGxsZW7DoW5kb3NlIHBvciBjb2x1bW5hcy4NCg0KQ29uc3RydXlhbW9zIGFob3JhIHVuYSBtYXRyaXogYSBwYXJ0aXIgZGVsIG1pc21vIHZlY3RvciwgcGVybyBlbiBlc3RhIG9jYXNpw7NuIG5vIGxhIGhhZ2Ftb3MgY3VhZHJhZGEuDQpgYGB7cn0NCm0yIDwtIG1hdHJpeChkYXRhID0gdjcsDQogICAgICAgICAgICAgbnJvdyA9IDgsDQogICAgICAgICAgICAgbmNvbCA9IDIpDQpwcmludChtMikNCmBgYA0KDQpWZXJpZmlxdWVtb3MgbGEgY2xhc2UgZGUgZXN0b3Mgb2JqZXRvcyBjcmVhZG9zOg0KYGBge3J9DQpjbGFzcyhtMSkNCmBgYA0KYGBge3J9DQpjbGFzcyhtMikNCmBgYA0KDQojIyMgNC4yIE90cmFzIGZvcm1hcyBkZSBjb25zdHJ1Y2Npw7NuDQoNCiogQW5leGFyIHZlY3RvcmVzIHBvciBjb2x1bW5hczoNCmBgYHtyfQ0KdjggPC0gYygiYSIsImIiLCJjIikNCnY5IDwtIGMoImQiLCJlIiwiZiIpDQptMyA8LSBjYmluZCh2OCx2OSkNCnByaW50KG0zKQ0KYGBgDQoqIEFuZXhhciB2ZWN0b3JlcyBwb3IgZmlsYXM6DQpgYGB7cn0NCm00IDwtIHJiaW5kKHY4LHY5KQ0KcHJpbnQobTQpDQpgYGANCipHZW5lcmFyIG1hdHJpY2VzIGRlIHVuIHNvbG8gdmFsb3INCmBgYHtyfQ0KbTUgPC0gbWF0cml4KGRhdGEgPSA1LA0KICAgICAgICAgICAgIG5yb3cgPSA0LA0KICAgICAgICAgICAgIG5jb2wgPSA0KQ0KcHJpbnQobTUpDQpgYGANCg0KIyMjIDQuMyBPcGVyYWNpb25lcyBiaW5hcmlhcyBjb24gbWF0cmljZXMNCg0KSWd1YWwgcXVlIGVuIHZlY3RvcmVzLCBlcyBwb3NpYmxlIGVqZWN1dGFyIG9wZXJhY2lvbmVzIG1hdGVtw6F0aWNhcywgbMOzZ2ljYXMgeSBlc3RhZMOtc3RpY2FzLiBTaW4gZW1iYXJnbyBzZSBkZWJlIHNlciBjdWlkYWRvc28gcG9yIHVuIGxhZG8gcGFyYSBkaWZlcmVuY2lhciBlbnRyZSBvcGVyYWNpb25lcyBtYXRlbcOhdGljYXMgcHJvcGlhcyBkZWwgw6FsZ2VicmEgbGluZWFsLCB5IGFxdWVsbGFzIGRlbCBtYW5lam8gZGUgZGF0b3M7IHkgcG9yIG90cm8sIGVuIHJldmlzYXIgc2llbXByZSBsYXMgZGltZW5zaW9uZXMgKGZpbGFzIHkgY29sdW1uYXMpIGRlIG1vZG8gcXVlIG5vIHNlIGdlbmVyZW4gZXJyb3Jlcy4NCg0KKiBPcGVyYWNpb25lcyBtYXRlbcOhdGljYXMgKG1hbmVqbyBkZSBkYXRvcykNCmBgYHtyfQ0KLTUuMiptMSArIG01DQpgYGANClNpIHNlIG11bHRpcGxpY2FuIG1hdHJpY2VzIGRlIGRpZmVyZW50ZXMgZGltZW5zaW9uZXMgc2UgZ2VuZXJhIGVycm9yOg0KYGBge3J9DQptMSAqIG0yDQpgYGANCiogT3BlcmFjaW9uZXMgbWF0ZW3DoXRpY2FzICjDoWxnZWJyYSBsaW5lYWwpDQoNClNlIHB1ZWRlbiBnZW5lcmFyIG11bHRpcGxpY2FjaW9uZXMgYmFqbyBlc3RlIGNvbnRleHRvIHNpZW1wcmUgcXVlIGVsIG7Dum1lcm8gZGUgY29sdW1uYXMgZGUgbGEgcHJpbWVyYSBtYXRyaXogc2VhIGlndWFsIGFsIG7Dum1lcm8gZGUgZmlsYXMgZGUgbGEgc2VndW5kYS4NCmBgYHtyfQ0KbTYgPC0gY2JpbmQoYygzLDIsMSksYyg0LDAsMSksYyg1LDQsNikpDQptNyA8LSByYmluZChjKDUsNSksYygxLDQpLGMoMCwtMSkpDQptNiAlKiUgbTcNCmBgYA0KQWRpY2lvbmFsbWVudGUsIHBvZGVtb3MgY2FsY3VsYXIgbGFzIHRyYW5zcHVlc3RhcywgaW52ZXJzYXMsIGRldGVybWluYW50ZXMgeSB2YWxvcmVzIHkgdmVjdG9yZXMgcHJvcGlvcyBkZSBtYXRyaWNlcyBjdWFkcmFkYXMuDQpgYGB7cn0NCnQobTYpDQpgYGANCmBgYHtyfQ0Kc29sdmUobTYpDQpgYGANCmBgYHtyfQ0KZGV0KG02KQ0KYGBgDQpgYGB7cn0NCmVpZ2VuKG02KQ0KYGBgDQoqIE9wZXJhY2lvbmVzIGVzdGFkw61zdGljYXMNCg0KU2UgcHVlZGUgb2J0ZW5lciBlbCB2YWxvciBtZWRpbyBkZSBsYXMgY29sdW1uYXMgeSBsYXMgZmlsYXMNCmBgYHtyfQ0KY29sTWVhbnMobTEpDQpyb3dNZWFucyhtMSkNCmBgYA0KQXPDrSB0YW1iacOpbiBwb2RlbW9zIGNhbGN1bGFyIGxhcyBkaW1lbnNpb25lcw0KYGBge3J9DQpwcmludChtNCkNCmRpbShtNCkNCm5jb2wobTQpDQpucm93KG00KQ0KYGBgDQojIyMgNC40IEluZGV4YWNpw7NuIGRlIG1hdHJpY2VzDQoNCkRlIGZvcm1hIHNpbWlsYXIgYSB1biB2ZWN0b3IsIHNlIHB1ZWRlIGluZGV4YXIgdW5hIG1hdHJpeiBjb24gbGEgcGFydGljdWxhcmlkYWQgcXVlIGRlYmVtb3MgZXNwZWNpZmljYXIgeWEgbm8gc29sYW1lbnRlIHVuYSBwb3NpY2nDs24sIHNpbm8gZG9zIChmaWxhcyB5IGNvbHVtbmFzKS4NCmBgYHtyfQ0KbTNbMywyXQ0KYGBgDQpTaSBxdWVyZW1vcyBpbmRleGFyIHRvZGEgdW5hIGZpbGEsIHNlIGxvIHB1ZWRlIGhhY2VyIGRlIGxhIHNpZ3VpZW50ZSBmb3JtYToNCmBgYHtyfQ0KbTFbMixdDQpgYGANClkgcGFyYSBjb2x1bW5hcywgbGEgc2ludGF4aXMgZXMgc2ltaWxhcjoNCmBgYHtyfQ0KbTJbLDJdDQpgYGANCkFsIGlndWFsIHF1ZSBlbiBsbyB2aXN0byBlbiB2ZWN0b3JlcywgZXMgcG9zaWJsZSBhcGxpY2FyIG9wZXJhY2lvbmVzIGJpbmFyaWFzLCBwYXJhIGluZGV4YXIgdW5hIG1hdHJpei4NCmBgYHtyfQ0KbTZbMSxuY29sKG02KV0NCmBgYA0KIyMgNS4gRGF0YSBGcmFtZXMNCg0KTG9zIGRhdGEgZnJhbWVzIG8gdGFibGFzIGRlIGRhdG9zLCBzZSBwdWVkZW4gZW50ZW5kZXIgY29tbyB1biBhcnJlZ2xvIGRlIGVsZW1lbnRvcyBzaW1pbGFyIGEgbGEgbWF0cmljZXMsIHBlcm8gY29uIGxhIHByaW5jaXBhbCBkaWZlcmVuY2lhIHF1ZSBjYWRhIGNvbHVtbmEgcHVlZGUgdGVuZXIgdW5hIGNsYXNlIGRpZmVyZW50ZS4NCg0KRW4gbGFzIMOhcmVhcyBkZSBpbnRlbGlnZW5jaWEgZGUgbmVnb2Npb3MgeSBkZSBhbmFsw610aWNhLCBsb3MgZGF0YSBmcmFtZXMgc29uIGVsIG9iamV0byBtw6FzIGltcG9ydGFudGUgeSBmcmVjdWVudGUgY29uIGVsIGN1YWwgc2UgdHJhYmFqYS4NCg0KIyMjIDUuMSBDb25zdHJ1Y2Npw7NuIGVzdMOhbmRhcg0KYGBge3J9DQpkZjEgPC0gZGF0YS5mcmFtZShudW0gPSBjKDEsMiwzKSwNCiAgICAgICAgICAgICAgICAgIHR4dCA9IGMoImEiLCJiIiwiYyIpLA0KICAgICAgICAgICAgICAgICAgaW50ID0gcmVwKDRMLDMpLA0KICAgICAgICAgICAgICAgICAgYm9vID0gYyhUUlVFLFRSVUUsRkFMU0UpKQ0KYGBgDQoNCkltcHJpbWFtb3MgZWwgZGF0YSBmcmFtZSBwYXJhIHZpc3VhbGl6YXJsbw0KYGBge3J9DQpwcmludChkZjEpDQpgYGANCkxvcyBkYXRhIGZyYW1lcyBwdWVkZW4gc2VyIHZpc3VhbGl6YWRvcyBhZGVtw6FzIGRlIGxhIHNpZ3VpZW50ZSBmb3JtYSwgZW4gY29uc2lkZXJhY2nDs24gYSBxdWUgZ2VuZXJhbG1lbnRlIGNvbnRpZW5lbiBtdWNob3MgZGF0b3MsIHkgc3UgdmlzdWFsaXphY2nDs24gZW4gbGEgY29uc29sYSBwdWVkZSBzZXIgbGltaXRhZGEgbyBkaWZpY3VsdG9zYS4NCmBgYHtyfQ0KVmlldyhkZjEpDQpgYGANClBhcmEgZXZpZGVuY2lhciBlc3RvLCB0b21lbW9zIHVuIGRhdGEgZnJhbWUgaW50ZWdyYWRvIGVuIFIgcXVlIHNlIGxsYW1hICppcmlzKg0KYGBge3J9DQpwcmludChpcmlzKQ0KVmlldyhpcmlzKQ0KYGBgDQoNClZlcmlmaXF1ZW1vcyBsYSBjbGFzZSBkZSBlc3RvcyBvYmpldG9zOg0KYGBge3J9DQpjbGFzcyhkZjEpDQpgYGANCg0KDQojIyMgNS4yIE90cmFzIGZvcm1hcyBkZSBjb25zdHJ1Y2Npw7NuDQoNCk90cmEgZm9ybWEgZGUgY29uc3RydWlyIGRhdGEgZnJhbWVzIGVzIGEgcGFydGlyIGRlIHVuYSBtYXRyaXogcHJldmlhbWVudGUgY3JlYWRhDQpgYGB7cn0NCm04IDwtIGNiaW5kKHNlcShmcm9tID0gMiwgdG8gPSAxMDAsIGJ5ID0gMiksDQogICAgICAgICAgICBzZXEoZnJvbSA9IDEsIHRvID0gMjAuOCwgYnkgPSAwLjQpLA0KICAgICAgICAgICAgLTI0OjI1KQ0KZGYyIDwtIGRhdGEuZnJhbWUobTgpDQpWaWV3KGRmMikNCmBgYA0KDQojIyMgNS4zIE9wYXJhY2lvbmVzIGJpbmFyaWFzIGNvbiBkYXRhIGZyYW1lcw0KDQpMYXMgc2lndWllbnRlcyBzb24gbGFzIHByaW5jaXBhbGVzIG9wZXJhY2lvbmVzIHF1ZSBzZSBwdWVkZW4gZWplY3V0YXIgY29uIGRhdGEgZnJhbWVzLg0KDQoqIFJlc3VtZW4gZXN0YWTDrXN0aWNvDQpgYGB7cn0NCnN1bW1hcnkoZGYxKQ0KYGBgDQoqIEVzdHJ1Y3R1cmENCmBgYHtyfQ0Kc3RyKGRmMSkNCmBgYA0KKiBEaW1lbnNpb25lcyAoaWd1YWwgcXVlIHVuYSBtYXRyaXopDQpgYGB7cn0NCmRpbShkZjEpDQpuY29sKGRmMSkNCm5yb3coZGYxKQ0KYGBgDQoqIE5vbWJyZXMgZGUgY29sdW1uYXMNCmBgYHtyfQ0KbmFtZXMoZGYxKQ0KYGBgDQojIyMgNS40IEluZGV4YWNpw7NuIGRlIGRhdGEgZnJhbWVzDQoNClVuIGRhdGEgZnJhbWUgcHVlZGUgaW5kZXhhcnNlIGRlIGxhIG1pc21hIGZvcm1hIHF1ZSB1bmEgbWF0cml6Lg0KYGBge3J9DQpkZjJbMTAsM10NCmBgYA0KU2luIGVtYmFyZ28sIGFsIG1vbWVudG8gZGUgZWplY3V0YXIgYW7DoWxpc2lzIGRlIGRhdG9zIGVzdGEgZm9ybWEgZGUgaW5kZXhhciBubyBzdWVsZSBzZXIgbXV5IGVmaWNpZW50ZS4gUG9yIGxvIHF1ZSB0YW1iacOpbiBlcyBwb3NpYmxlIGluZGV4YXIgZXNwZWNpZmljYW5kbyBub21icmVzIGRlIGNvbHVtbmFzOg0KYGBge3J9DQppcmlzJFNlcGFsLkxlbmd0aFsxMjNdDQpgYGANCk8gdGFtYmnDqW4gZGUgZXN0YSBmb3JtYToNCmBgYHtyfQ0KZGYxWzMsInR4dCJdDQpgYGANCkN1YW5kbyBlbXBlY2Vtb3MgYSB1dGlsaXphciBlbCBwYXF1ZXRlICoqdGlkeXZlcnNlKiogdmFtb3MgYSBwcm9mdW5kaXphciBtYXMgZW4gbGEgY29uc3RydWNjacOzbiB5IG1hbmlwdWxhY2nDs24gZGUgZGF0YSBmcmFtZXMgeSBzdXMgb2JqZXRvcyBoZXJtYW5vczogbG9zIHRpYmJsZXMuDQoNCiMjIDYuIExpc3Rhcw0KDQpDb21vIHB1bnRvcyBleHRyYXMgcGFyYSBzdSBldmFsdWFjacOzbiBmaW5hbCwgc2UgZ2FuYXLDoSA1IHB1bnRvcyBlbCBwcmltZXIgZXN0dWRpYW50ZSBxdWUgbWUgcmVtaXRhIHBvciBjb3JyZW8gdW4gc2NyaXB0IGRlIFIgZW4gZWwgY3VhbCwgc2UgZGV0YWxsZSB1bmEgZGVzY3JpcGNpw7NuIGRlIGxvcyBvYmpldG9zIGRlbm9taW5hZG9zIExJU1RBUy4NCg0KRW4gY29uY3JldG8sIGVuIGVsIHNjcmlwdCBkZWJlIGVzcGVjaWZpY2Fyc2U6DQoNCjEuIFF1w6kgc29uDQoyLiBTdXMgZm9ybWFzIGRlIGNvbnN0cnVjY2nDs24NCjMuIFN1cyBvcGVyYWNpb25lcyBiaW5hcmlhcw0KNC4gU3UgaW5kZXhhY2nDs24NCg==