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.
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:
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:
b1 & b2
[1] FALSE
b1 | b2
[1] TRUE
!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.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.
2*v1 - v4
[1] 3 6 9 12 15 18
v5/2
[1] 0.5 1.5 2.5 3.5 4.5
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.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
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.3 Oparaciones binarias con data frames
Las siguientes son las principales operaciones que se pueden ejecutar con data frames.
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
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
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.
LS0tDQp0aXRsZTogIk9iamV0b3MgQsOhc2ljb3MgZW4gUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNClVuIG9iamV0bywgZGVudHJvIGRlbCBjb250ZXh0byBkZSBjb21wdXRhY2nDs24geSBwcm9ncmFtYWNpw7NuLCBlcyB1biBlbGVtZW50byBkZW50cm8gZGUgbGEgbWVtb3JpYSBlbiB1c28gY29uIGVsIGN1YWwgc2UgcHVlZGUgaW50ZXJhY3R1YXIgbWVkaWFudGUgaGVycmFtaWVudGFzIGNvbW8gUi4NCg0KIyMgMS4gT3BlcmFkb3JlcyBtYXRlbcOhdGljb3MNCg0KVG9kYSBoZXJyYW1pZW50YSBxdWUgdHJhYmFqZSBjb24gZGF0b3MgdGllbmUgaW5jb3Jwb3JhZG8gdW4gZXNxdWVtYSBkZSBvcGVyYWNpb25lcyBiw6FzaWNhcyAobyB0YW1iacOpbiBsbGFtYWRhcyBiaW5hcmlhcykgcXVlIHBlcm1pdGVuIHN1IHVzbyBjb21vIGNhbGN1bGFkb3JhLg0KDQojIyMgMS4xIFN1bWEgeSBSZXN0YQ0KYGBge3J9DQo0ICsgNg0KMyAtIDINCjI1ICsgMTEgLSA4DQpgYGANCiMjIyAxLjIgTXVsdGlwbGljYWNpw7NuIHkgRGl2aXNpw7NuDQpgYGB7cn0NCjMgKiA1DQo2IC8gMg0KYGBgDQojIyMgMS4zIFBvdGVuY2lhcw0KYGBge3J9DQozIF4gMiANCjMgKiogMiANCjQgKiogKDEvMykNCmBgYA0KTm90ZW1vcyBxdWUgc2UgcHVlZGUgZXNjcmliaXIgcG90ZW5jaWFzIGRlIGRvcyBmb3JtYXM6DQoNCiogTWVkaWFudGUgZWwgdXNvIGRlIF4sIG8gYmllbiwNCiogTWVkaWFudGUgZWwgdXNvIGRlICoqLg0KDQojIyMgMS40IE3Ds2R1bG9zDQpgYGB7cn0NCjggJSUgMw0KNSAlJSAyDQpgYGANCkxvcyBtw7NkdWxvcyBjb3JyZXNwb25kZW4gYWwgcmVzaWR1byBkZSBsYSBkaXZpc2nDs24sIHkgc29uIG11eSDDunRpbGVzIHBhcmEgZGV0ZWN0YXIgc2kgdW4gdmFsb3IgZXMgcGFyIG8gaW1wYXIsIG8gdGFtYmnDqW4sIHBhcmEgZ2VuZXJhciBpdGVyYWNpb25lcyBjb21wbGVqYXMuDQoNCiMjIDIuIFZhcmlhYmxlcw0KDQpBc8OtIGNvbW8gZW4gbWF0ZW3DoXRpY2FzLCBsYXMgdmFyaWFibGVzIHNvbiByZXByZXNlbnRhY2lvbmVzIGFsZ2VicmFpY2FzIHF1ZSB0aWVuZW4gbGEgY2FwYWNpZGFkIGRlIGd1YXJkYXIgdW4gdmFsb3IuIFZhbGUgc2XDsWFsYXIgcXVlIGVsIHVzbyBkZSB2YXJpYWJsZXMsIGVzIGVsIHByaW1lciAoeSBtdXkgaW1wb3J0YW50ZSkgcGFzbyBwYXJhIGxhIGdlbmVyYWxpemFjacOzbiBkZSB1biBwcm9ncmFtYSBjb21wdXRhY2lvbmFsLg0KDQojIyMgMi4xIFZhcmlhYmxlcyBudW3DqXJpY2FzIGRlY2ltYWxlcw0KYGBge3J9DQpuMSA9IDQNCm4yIDwtIC04LjY1DQpgYGANCg0KTm90ZW1vcywgcXVlIHBhcmEgYXNpZ25hciB1biB2YWxvciBhIHVuYSB2YXJpYWJsZSBzZSBwdWVkZSB1c2FyIGRlIGZvcm1hIGluZGlzdGludGEgdW4gc2ltYm9sbyA9IG8gdW5hICJmbGVjaGEiIDwtLg0KDQpQYXJhIGltcHJpbWlyIGVsIHZhbG9yIGRlIHVuYSB2YXJpYWJsZSBzZSBwdWVkZW4gb3B0YXIgcG9yIGxhcyBzaWd1aWVudGVzIG9wY2lvbmVzOg0KDQoqIEltcHJlc2lvbiBzaW1wbGUNCmBgYHtyfQ0KbjENCmBgYA0KKiBJbXByZXNpw7NuIG1lZGlhbnRlIGZ1bmNpw7NuIChyZWNvbWVuZGFkYSkNCmBgYHtyfQ0KcHJpbnQobjIpDQpgYGANCg0KIyMjIDIuMiBWYXJpYWJsZXMgbnVtw6lyaWNhcyBlbnRlcmFzDQpgYGB7cn0NCmUxID0gNEwNCnByaW50KGUxKQ0KYGBgDQpOb3RlbW9zIHF1ZSBwYXJhIGVzcGVjaWZpY2FyIHF1ZSBsYSB2YXJpYWJsZSBlcyBkZSB0aXBvIGVudGVybyBkZWJlbW9zIGluY29ycG9yYXIgdW5hIGxldHJhIEwgZGVzcHXDqXMgZGVsIG7Dum1lcm8uIA0KDQpQYXJhIGNvbm9jZXIgeSB2ZXJpZmljYXIgbGEgY2xhc2UgbyB0aXBvIGRlIHZhcmlhYmxlIHBvZGVtb3MgcmVhbGl6YXIgbG8gc2lndWllbnRlOg0KYGBge3J9DQpjbGFzcyhuMSkNCmNsYXNzKGUxKQ0KYGBgDQpMYSBjbGFzZSAqbnVtZXJpYyogZGUgUiBlcyBlcXVpdmFsZW50ZSBhIGxvIHF1ZSBub3NvdHJvcyBoZW1vcyByZWZlcmlkbyBjb21vIGRlY2ltYWwuDQoNCkVuIGVzdGUgcHVudG8sIHZlcmlmaXF1ZW1vcyBlbCDDoXJlYSBkZSBvYmpldG9zIHkgbm90ZW1vcyBxdWUgdG9kYXMgbGFzIHZhcmlhYmxlcyBxdWUgZXN0YW1vcyBjcmVhbmRvIHNlIGVzdMOhbiBndWFyZGFuZG8gYWxsw60uDQoNCiMjIyAyLjMgVmFyaWFibGVzIGRlIHRleHRvDQpgYGB7cn0NCnQxID0gIkhvbGEiDQp0MiA8LSAiTWkgbm9tYnJlIGVzIGp1YW4iDQpgYGANCg0KSW1wcmltYW1vcyBhaG9yYSBlc3RhcyB2YXJpYWJsZXM6DQpgYGB7cn0NCnByaW50KHQxKQ0KcHJpbnQodDIpDQpgYGANClkgdmVyaWZpcXVlbW9zIHN1IGNsYXNlOg0KYGBge3J9DQpjbGFzcyh0MSkNCmNsYXNzKHQyKQ0KYGBgDQojIyMgMi40IFZhcmlhYmxlcyBkZSB0aXBvIGZhY3Rvcg0KDQpFbiBlc3RlIHB1bnRvIGVzIGNvbnZlbmllbnRlIGRpc3Rpbmd1aXIgbG9zIGZhY3RvcmVzIGVuIGRvcyBncmFuZGVzIGdydXBvczoNCg0KKiBMb3MgZmFjdG9yZXMgYmluYXJpb3MgKG8gYnVsZWFub3MpIHF1ZSBzb2xhbWVudGUgcHVlZGVuIHRvbWFyIHZhbG9yZXMgZW4gdW4gcmFuZ28gZGUgMiBlbGVtZW50b3MuDQoqIExvcyBkZW3DoXMgZmFjdG9yZXMgcXVlIHB1ZWRlbiB0b21hciB2YWxvcmVzIGVuIHVuIHJhbmdvIGRlIG3DoXMgZGUgMiBlbGVtZW50b3MuDQoNCiMjIyMgMi40LjEgRmFjdG9yZXMgYnVsZWFub3MNCmBgYHtyfQ0KYjEgPSBUUlVFDQpiMiA8LSBGQUxTRQ0KYGBgDQoNClZlYW1vcyBsYSBjbGFzZSBkZSBlc3RhcyB2YXJpYWJsZXM6DQpgYGB7cn0NCmNsYXNzKGIxKQ0KY2xhc3MoYjIpDQpgYGANCk5vdGVtb3MgcXVlIGN1YW5kbyB1biBmYWN0b3IgZXMgZGUgdGlwbyBWRVJEQURFUk8vRkFMU08gc2UgbGUgc3VlbGUgbGxhbWFyIGZhY3RvciBsw7NnaWNvLg0KDQpJbnRlbnRlbW9zIGFob3JhIGVzdGEgZm9ybWEgZGUgZ2VuZXJhciBmYWN0b3JlcyBidWxlYW5vczoNCmBgYHtyfQ0KYjMgPC0gZmFjdG9yKDEsIGxldmVscyA9IDE6MikNCmBgYA0KDQpJbXByaW1hbW9zIGVsIGZhY3RvcjoNCmBgYHtyfQ0KcHJpbnQoYjMpDQpgYGANCkxvIHF1ZSBhY2FiYW1vcyBkZSBoYWNlciBlcyBkZWNpcmxlIGEgUiBxdWUgbnVlc3RyYSB2YXJpYWJsZSBiMyBlcyB1biBmYWN0b3IgcXVlIHRpZW5lIHVuIHZhbG9yIGRlIDEsIHkgcXVlIGVsIHBvc2libGUgcmFuZ28gZGVsIGZhY3RvciBlcyAxIG8gMi4gUmV2aXNlIGxhIGFzaWduYWNpw7NuIGRlIGIzIHkgY29uZmlybWUgZXN0ZSBlbnVuY2lhZG8uDQoNClZlcmlmaXF1ZW1vcyBhaG9yYSBsYSBjbGFzZSBkZSBlc3RhIHZhcmlhYmxlDQpgYGB7cn0NCmNsYXNzKGIzKQ0KYGBgDQojIyMjIDIuNC4yIE90cm9zIGZhY3RvcmVzDQpgYGB7cn0NCmNvbG9yZXNfc2VtYWZvcm8gPSBmYWN0b3IoImFtYXJpbGxvIiwgbGV2ZWxzID0gYygidmVyZGUiLCAiYW1hcmlsbG8iLCAicm9qbyIpKQ0KYGBgDQoNClJldmlzZSBlc3RhIGFzaWduYWNpw7NuIGRlIHZhcmlhYmxlIGUgaW50ZW50ZSBjb21wcmVuZGVybGEsIHNpIGxlIGN1ZXN0YSBubyBzZSBwcmVvY3VwZSwgZXMgbm9ybWFsIHB1ZXMgYcO6biBubyBoZW1vcyB2aXN0byB0b2RvcyBsb3Mgb2JqZXRvcyBiw6FzaWNvcyBxdWUgcG9zZWUgUi4NCg0KSW1wcmltYW1vcyB5IHZlYW1vcyBsYSBjbGFzZSBkZSBlc3RhIHZhcmlhYmxlDQpgYGB7cn0NCnByaW50KGNvbG9yZXNfc2VtYWZvcm8pDQpjbGFzcyhjb2xvcmVzX3NlbWFmb3JvKQ0KYGBgDQojIyMgMi41IE9wZXJhY2lvbmVzIGJpbmFyaWFzIGNvbiB2YXJpYWJsZXMNCg0KQWwgaWd1YWwgcXVlIGNvbiBuw7ptZXJvcyBlcyBwb3NpYmxlIGVqZWN1dGFyIG9wZXJhY2lvbmVzIG1hdGVtw6F0aWNhcyBjb24gdmFyaWFibGVzLCBzaWVtcHJlIHkgY3VhbmRvIGVzdGFzIHRlbmdhbiBzZW50aWRvLg0KYGBge3J9DQoobjEgKyBuMikqZTENCm4xKioyDQpgYGANClBvciBlamVtcGxvLCBtaXJlIHF1ZSBlc3RhIG9wZXJhY2nDs24gdmEgYSBnZW5lcmFyIHVuIGVycm9yIHB1ZXN0byBxdWUgY2FyZWNlIGRlIHNlbnRpZG8NCmBgYHtyfQ0KdDEgKyA1DQpgYGANCkNvbm9jaWVuZG8geWEgbG8gcXVlIHNvbiBsYXMgdmFyaWFibGVzIGzDs2dpY2FzIChWRVJEQURFUk8vRkFMU08pLCBwb2RlbW9zIHRhbWJpw6luIHZlciBsYXMgb3BlcmFjaW9uZXMgbWF0ZW3DoXRpY2FzIGzDs2dpY2FzOg0KDQoqIE9wZXJhY2nDs24gQU5EDQpgYGB7cn0NCmIxICYgYjINCmBgYA0KKiBPcGVyYWNpw7NuIE9SDQpgYGB7cn0NCmIxIHwgYjINCmBgYA0KKiBPcGVyYWNpw7NuIE5PVA0KYGBge3J9DQohYjENCmBgYA0KDQpBc8OtIGNvbW8gbGFzIG9wZXJhY2lvbmVzIGRlIGNvbnRyYXN0ZSBsw7NnaWNvLCBjb21vIGxhcyBzaWd1aWVudGVzOg0KYGBge3J9DQpuMSA+IDMNCm4yIDw9IDANCmUxID09IDMNCm4xIDwgbjINCihuMiA+IDIpICYgKG4yIDwgNCkNCmBgYA0KTsOzdGVtb3MgcXVlIGVsIHVzbyBkZWwgPT0gbm8gZXMgdW5hIGFzaWduYWNpw7NuIHNpbm8gdW4gY29uc3RyYXN0ZSBsw7NnaWNvLiBFbiBlc3RvIGRlYmVtb3MgdGVuZXIgbXVjaG8gY3VpZGFkbyBjdWFuZG8gcHJvZ3JhbWVtb3MgY29zYXMgbcOhcyBjb21wbGVqYXMuDQoNCiMjIDMuIFZlY3RvcmVzDQoNCkxvcyB2ZWN0b3JlcyBzb24gY29uanVudG9zIGRlIHVubyBvIG3DoXMgdmFsb3JlcyBkZWwgbWlzbW8gdGlwbyBvIGNsYXNlLiBFbiBSIGNvbnN0aXR1eWVuIGVsIGVsZW1lbnRvIGZ1bmRhbWVudGFsIGRlIG90cm9zIG9iamV0b3MgY29tbyBsYXMgbWF0cmljZXMsIG8gbG9zIGRhdGEgZnJhbWVzLg0KDQojIyMgMy4xIENvbnN0cnVjY2nDs24gZXN0w6FuZGFyDQoNCkxvcyB2ZWN0b3JlcyBzZSBwdWVkZW4gY29uc3RydWlyIGRlIHZhcmlhcyBmb3JtYXMsIHBlcm8gbGEgc2lndWllbnRlIGVzIGxhIG3DoXMgY29tw7puOg0KYGBge3J9DQp2MSA8LSBjKDIsNCw2LDgsMTAsMTIpDQp2MiA8LSBjKCJqIiwidSIsImEiLCJuIikNCnYzIDwtIGMoVFJVRSxGQUxTRSxUUlVFKQ0KYGBgDQoNCkltcHJpbWFtb3MgZXN0b3Mgb2JqZXRvcyB5IHZlYW1vcyBzdSBjbGFzZToNCmBgYHtyfQ0KcHJpbnQodjEpDQpjbGFzcyh2MSkNCnByaW50KHYyKQ0KY2xhc3ModjIpDQpwcmludCh2MykNCmNsYXNzKHYzKQ0KYGBgDQpFcyBpbXBvcnRhbnRlIG9ic2VydmFyIHF1ZSBsb3MgdmVjdG9yZXMsIHNpIGJpZW4gcG9zZWVuIHZhcmlvcyBlbGVtZW50b3MsIG1hbnRpZW5lbiBsYSBjbGFzZSBkZSBjYWRhIHVubyBkZSBlbGxvcy4NCg0KIyMjIDMuMiBPdHJhcyBmb3JtYXMgZGUgY29uc3RydWNjacOzbg0KDQoqIFJhbmdvcyBvIGludGVydmFsb3M6DQpgYGB7cn0NCnY0IDwtIDE6Ng0KcHJpbnQodjQpDQpgYGANCiogU2VjdWVuY2lhczoNCmBgYHtyfQ0KdjUgPC0gc2VxKGZyb20gPSAxLCB0byA9IDEwLCBieSA9IDIpDQpwcmludCh2NSkNCmBgYA0KKiBSZXBldGljaW9uZXM6DQpgYGB7cn0NCnY2IDwtIHJlcCgiYSIsdGltZXMgPSA1KQ0KcHJpbnQodjYpDQpgYGANCiMjIyAzLjMgT3BlcmFjaW9uZXMgYmluYXJpYXMgY29uIHZlY3RvcmVzDQoNClNlIHB1ZWRlbiBlamVjdXRhciB0YW50byBvcGVyYWNpb25lcyBtYXRlbcOhdGljYXMgY29tbyBsw7NnaWNhcyBlbiB2ZWN0b3JlcywgZGUgZm9ybWEgZXF1aXZhbGVudGUgYSBsbyBoZWNobyBlbiB2YXJpYWJsZXMuDQoNCiogT3BlcmFjaW9uZXMgbWF0ZW3DoXRpY2FzDQpgYGB7cn0NCjIqdjEgLSB2NA0KdjUvMg0KYGBgDQoqIE9wZXJhY2lvbmVzIGzDs2dpY2FzDQpgYGB7cn0NCnY0ID4gMw0KdjYgPT0gImIiDQpgYGANCkFkaWNpb25hbCBhIGVzdG8sIHNlIHB1ZWRlbiBpbmNsdWlyIGNvbW8gb3BlcmFjaW9uZXMgYmluYXJpYXMgY29uIHZlY3RvcmVzIGEgbGFzIHNpZ3VpZW50ZXMgZXN0YWTDrXN0aWNhcyBiw6FzaWNhczoNCg0KKiBUYW1hw7FvIG8gbm9ybWEgZGVsIHZlY3RvcjoNCmBgYHtyfQ0KbGVuZ3RoKHYxKQ0KYGBgDQoqIE1lZGlhLCBtZWRpYW5hLCBtw61uaW1vIHkgbcOheGltbyBkZWwgdmVjdG9yIChzw7NsbyBwYXJhIHZlY3RvcmVzIG51bcOpcmljb3MpOg0KYGBge3J9DQptZWFuKHY0KQ0KbWVkaWFuKHY0KQ0KbWluKHY0KQ0KbWF4KHY0KQ0KYGBgDQoqIERlc3ZpYWNpw7NuIGVzdMOhbmRhciwgdmFyaWFuemEsIHkgcmFuZ28gSVFSIGRlbCB2ZWN0b3IgKHPDs2xvIHBhcmEgdmVjdG9yZXMgbnVtw6lyaWNvcyk6DQpgYGB7cn0NCnZhcih2MSkNCnNkKHYxKQ0KSVFSKHYxKQ0KYGBgDQoqIFN1bWEgZGUgdG9kb3MgbG9zIGVsZW1lbnRvcyBkZWwgdmVjdG9yIChzw7NsbyBwYXJhIHZlY3RvcmVzIG51bcOpcmljb3MpOg0KYGBge3J9DQpzdW0odjUpDQpgYGANCiogQ29ycmVsYWNpw7NuIGVudHJlIGRvcyB2ZWN0b3JlcyAoc8OzbG8gcGFyYSB2ZWN0b3JlcyBudW3DqXJpY29zKToNCmBgYHtyfQ0KY29yKHYxLHY0KQ0KYGBgDQoqIENvbmNhdGVuYWNpw7NuIGRlIHZlY3RvcmVzOg0KYGBge3J9DQpwYXN0ZSh2NiwgY29sbGFwc2UgPSAiLCIpDQpwYXN0ZSh2MiwgY29sbGFwc2UgPSAiIikNCmBgYA0KIyMjIDMuNCBJbmRleGFjaW9uIGRlIHZlY3RvcmVzDQoNCkVuIG11Y2hhcyBvY2FzaW9uZXMgZXMgaW1wb3J0YW50ZSB1YmljYXIgZWxlbWVudG9zIGVzcGVjw61maWNvcyBkZSB1biB2ZWN0b3IuIEVuIGVzdGUgc2VudGlkbyB5IGVuIHTDqXJtaW5vcyBnZW5lcmFsZXMsIGRpcmVtb3MgKmluZGV4YXIqIHVuIHZlY3RvciBwYXJhIGVsIHByb2Nlc28gZGUgaWRlbnRpZmljYXIgdW4gZWxlbWVudG8gZW4gY29uY3JldG8gZGVsIG1pc21vLg0KDQpgYGB7cn0NCnYxWzFdDQp2Mls0XQ0KdjVbM10NCmBgYA0KQWRpY2lvbmFsbWVudGUsIGVzIHBvc2libGUgaW5kZXhhciB2ZWN0b3JlcyB1dGlsaXphbmRvIG9wZXJhY2lvbmVzIGJpbmFyaWFzIGzDs2dpY2FzIHkgZXN0YWTDrXN0aWNhcyBxdWUgeWEgaGVtb3MgdmlzdG8sIGFzw60gcG9yIGVqZW1wbG86DQpgYGB7cn0NCnYxW2xlbmd0aCh2MSldDQp2Mlt2MiA9PSAidSJdDQp2NFt2NCA+IDNdDQpgYGANClRhbWJpw6luIGVzIHBvc2libGUgaW5kZXhhciwgdXNhbmRvIG90cm8gdmVjdG9yIHBhcmEgb2J0ZW5lciBtw6FzIGRlIHVuIGVsZW1lbnRvOg0KYGBge3J9DQp2NVtjKDEsNCldDQpgYGANCk8gaW5kZXhhciBwYXJhIG9idGVuZXIgdW4gdmVjdG9yIFNJTiB1biBlbGVtZW50byBlbiBlc3BlY8OtZmljbyB1dGlsaXphbmRvIGVsIHNpZ25vICItIjoNCmBgYHtyfQ0KdjJbLTNdDQpgYGANCkPDs21vIHZlcmVtb3MgbcOhcyBhZGVsYW50ZSwgbGEgaW5kZXhhY2nDs24gZXMgdW5hIGNhcmFjdGVyw61zdGljYSBpbXBvcnRhbnRlIHRhbWJpw6luIGVuIG1hdHJpY2VzIHkgZW4gZGF0YSBmcmFtZXMuIExvIGFudGVyaW9yIGRlYmlkbyBhIHF1ZSwgY29tbyBzZSBpbmRpY8OzLCBlbCB2ZWN0b3IgZXMgc3UgZWxlbWVudG8gY29uc3RpdHV0aXZvIHByaW5jaXBhbC4NCg0KIyMgNC4gTWF0cmljZXMNCg0KVW5hIG1hdHJpeiBlcyB1biBvcmRlbmFtaWVudG8gdmVjdG9yaWFsIGRlIGVsZW1lbnRvcyBlbiBuIGZpbGFzIHkgbSBjb2x1bW5hcy4gVW5hIGNhcmFjdGVyw61zdGljYSBpbXBvcnRhbnRlIGRlIGxhcyBtYXRyaWNlcywgcmFkaWNhIGVuIHF1ZSBkZW50cm8gZGUgZWxsYXMgc29sYW1lbnRlIHB1ZWRlbiBleGlzdGlyIGVsZW1lbnRvcyBkZSB1bmEgbWlzbWEgY2xhc2UuDQoNCiMjIyA0LjEgQ29uc3RydWNjacOzbiBlc3TDoW5kYXINCg0KUGFyYSBlbXBlemFyLCBkZWZpbmFtb3MgdW4gdmVjdG9yIGNvbiAxNiBkZSBlbGVtZW50b3MgbnVtw6lyaWNvcy4NCg0KYGBge3J9DQp2NyA8LSBjKDUsNyw0LDAsMSwtNiwtMSwyLDAsOCwxLDksMCwtNSwtOSwxKQ0KYGBgDQoNCkFob3JhLCBjb25zdHJ1eWFtb3MgdW5hIG1hdHJpeiBjdWFkcmFkYToNCmBgYHtyfQ0KbiA8LSBzcXJ0KGxlbmd0aCh2NykpDQptIDwtIG4NCm0xIDwtIG1hdHJpeChkYXRhID0gdjcsDQogICAgICAgICAgICAgbnJvdyA9IG4sDQogICAgICAgICAgICAgbmNvbCA9IG0pDQpgYGANClBhcmEgZGVmaW5pciB1bmEgbWF0cml6LCBuw7N0ZXNlIHF1ZSBzZSBkZWJlbiBlc3RhYmxlY2VyIDMgcGFyYW1ldHJvczogdW5vcyBkYXRvcyBxdWUgY29ycmVzcG9uZGVuIGEgdW4gdmVjdG9yLCB1biBuw7ptZXJvIGRlIGNvbHVtbmFzLCB5IHVuIG7Dum1lcm8gZGUgZmlsYXMuDQoNCkltcHJpbWFtb3MgbGEgbWF0cml6IHBhcmEgdmVybGEgZW4gZGV0YWxsZQ0KYGBge3J9DQpwcmludChtMSkNCmBgYA0KRXMgaW1wb3J0YW50ZSBzZcOxYWxhciBxdWUgbGEgbWF0cml6IHZhIGxsZW7DoW5kb3NlIHBvciBjb2x1bW5hcy4NCg0KQ29uc3RydXlhbW9zIGFob3JhIHVuYSBtYXRyaXogYSBwYXJ0aXIgZGVsIG1pc21vIHZlY3RvciwgcGVybyBlbiBlc3RhIG9jYXNpw7NuIG5vIGxhIGhhZ2Ftb3MgY3VhZHJhZGEuDQpgYGB7cn0NCm0yIDwtIG1hdHJpeChkYXRhID0gdjcsDQogICAgICAgICAgICAgbnJvdyA9IDgsDQogICAgICAgICAgICAgbmNvbCA9IDIpDQpwcmludChtMikNCmBgYA0KDQpWZXJpZmlxdWVtb3MgbGEgY2xhc2UgZGUgZXN0b3Mgb2JqZXRvcyBjcmVhZG9zOg0KYGBge3J9DQpjbGFzcyhtMSkNCmBgYA0KYGBge3J9DQpjbGFzcyhtMikNCmBgYA0KDQojIyMgNC4yIE90cmFzIGZvcm1hcyBkZSBjb25zdHJ1Y2Npw7NuDQoNCiogQW5leGFyIHZlY3RvcmVzIHBvciBjb2x1bW5hczoNCmBgYHtyfQ0KdjggPC0gYygiYSIsImIiLCJjIikNCnY5IDwtIGMoImQiLCJlIiwiZiIpDQptMyA8LSBjYmluZCh2OCx2OSkNCnByaW50KG0zKQ0KYGBgDQoqIEFuZXhhciB2ZWN0b3JlcyBwb3IgZmlsYXM6DQpgYGB7cn0NCm00IDwtIHJiaW5kKHY4LHY5KQ0KcHJpbnQobTQpDQpgYGANCipHZW5lcmFyIG1hdHJpY2VzIGRlIHVuIHNvbG8gdmFsb3INCmBgYHtyfQ0KbTUgPC0gbWF0cml4KGRhdGEgPSA1LA0KICAgICAgICAgICAgIG5yb3cgPSA0LA0KICAgICAgICAgICAgIG5jb2wgPSA0KQ0KcHJpbnQobTUpDQpgYGANCg0KIyMjIDQuMyBPcGVyYWNpb25lcyBiaW5hcmlhcyBjb24gbWF0cmljZXMNCg0KSWd1YWwgcXVlIGVuIHZlY3RvcmVzLCBlcyBwb3NpYmxlIGVqZWN1dGFyIG9wZXJhY2lvbmVzIG1hdGVtw6F0aWNhcywgbMOzZ2ljYXMgeSBlc3RhZMOtc3RpY2FzLiBTaW4gZW1iYXJnbyBzZSBkZWJlIHNlciBjdWlkYWRvc28gcG9yIHVuIGxhZG8gcGFyYSBkaWZlcmVuY2lhciBlbnRyZSBvcGVyYWNpb25lcyBtYXRlbcOhdGljYXMgcHJvcGlhcyBkZWwgw6FsZ2VicmEgbGluZWFsLCB5IGFxdWVsbGFzIGRlbCBtYW5lam8gZGUgZGF0b3M7IHkgcG9yIG90cm8sIGVuIHJldmlzYXIgc2llbXByZSBsYXMgZGltZW5zaW9uZXMgKGZpbGFzIHkgY29sdW1uYXMpIGRlIG1vZG8gcXVlIG5vIHNlIGdlbmVyZW4gZXJyb3Jlcy4NCg0KKiBPcGVyYWNpb25lcyBtYXRlbcOhdGljYXMgKG1hbmVqbyBkZSBkYXRvcykNCmBgYHtyfQ0KLTUuMiptMSArIG01DQpgYGANClNpIHNlIG11bHRpcGxpY2FuIG1hdHJpY2VzIGRlIGRpZmVyZW50ZXMgZGltZW5zaW9uZXMgc2UgZ2VuZXJhIGVycm9yOg0KYGBge3J9DQptMSAqIG0yDQpgYGANCiogT3BlcmFjaW9uZXMgbWF0ZW3DoXRpY2FzICjDoWxnZWJyYSBsaW5lYWwpDQoNClNlIHB1ZWRlbiBnZW5lcmFyIG11bHRpcGxpY2FjaW9uZXMgYmFqbyBlc3RlIGNvbnRleHRvIHNpZW1wcmUgcXVlIGVsIG7Dum1lcm8gZGUgY29sdW1uYXMgZGUgbGEgcHJpbWVyYSBtYXRyaXogc2VhIGlndWFsIGFsIG7Dum1lcm8gZGUgZmlsYXMgZGUgbGEgc2VndW5kYS4NCmBgYHtyfQ0KbTYgPC0gY2JpbmQoYygzLDIsMSksYyg0LDAsMSksYyg1LDQsNikpDQptNyA8LSByYmluZChjKDUsNSksYygxLDQpLGMoMCwtMSkpDQptNiAlKiUgbTcNCmBgYA0KQWRpY2lvbmFsbWVudGUsIHBvZGVtb3MgY2FsY3VsYXIgbGFzIHRyYW5zcHVlc3RhcywgaW52ZXJzYXMsIGRldGVybWluYW50ZXMgeSB2YWxvcmVzIHkgdmVjdG9yZXMgcHJvcGlvcyBkZSBtYXRyaWNlcyBjdWFkcmFkYXMuDQpgYGB7cn0NCnQobTYpDQpgYGANCmBgYHtyfQ0Kc29sdmUobTYpDQpgYGANCmBgYHtyfQ0KZGV0KG02KQ0KYGBgDQpgYGB7cn0NCmVpZ2VuKG02KQ0KYGBgDQoqIE9wZXJhY2lvbmVzIGVzdGFkw61zdGljYXMNCg0KU2UgcHVlZGUgb2J0ZW5lciBlbCB2YWxvciBtZWRpbyBkZSBsYXMgY29sdW1uYXMgeSBsYXMgZmlsYXMNCmBgYHtyfQ0KY29sTWVhbnMobTEpDQpyb3dNZWFucyhtMSkNCmBgYA0KQXPDrSB0YW1iacOpbiBwb2RlbW9zIGNhbGN1bGFyIGxhcyBkaW1lbnNpb25lcw0KYGBge3J9DQpwcmludChtNCkNCmRpbShtNCkNCm5jb2wobTQpDQpucm93KG00KQ0KYGBgDQojIyMgNC40IEluZGV4YWNpw7NuIGRlIG1hdHJpY2VzDQoNCkRlIGZvcm1hIHNpbWlsYXIgYSB1biB2ZWN0b3IsIHNlIHB1ZWRlIGluZGV4YXIgdW5hIG1hdHJpeiBjb24gbGEgcGFydGljdWxhcmlkYWQgcXVlIGRlYmVtb3MgZXNwZWNpZmljYXIgeWEgbm8gc29sYW1lbnRlIHVuYSBwb3NpY2nDs24sIHNpbm8gZG9zIChmaWxhcyB5IGNvbHVtbmFzKS4NCmBgYHtyfQ0KbTNbMywyXQ0KYGBgDQpTaSBxdWVyZW1vcyBpbmRleGFyIHRvZGEgdW5hIGZpbGEsIHNlIGxvIHB1ZWRlIGhhY2VyIGRlIGxhIHNpZ3VpZW50ZSBmb3JtYToNCmBgYHtyfQ0KbTFbMixdDQpgYGANClkgcGFyYSBjb2x1bW5hcywgbGEgc2ludGF4aXMgZXMgc2ltaWxhcjoNCmBgYHtyfQ0KbTJbLDJdDQpgYGANCkFsIGlndWFsIHF1ZSBlbiBsbyB2aXN0byBlbiB2ZWN0b3JlcywgZXMgcG9zaWJsZSBhcGxpY2FyIG9wZXJhY2lvbmVzIGJpbmFyaWFzLCBwYXJhIGluZGV4YXIgdW5hIG1hdHJpei4NCmBgYHtyfQ0KbTZbMSxuY29sKG02KV0NCmBgYA0KIyMgNS4gRGF0YSBGcmFtZXMNCg0KTG9zIGRhdGEgZnJhbWVzIG8gdGFibGFzIGRlIGRhdG9zLCBzZSBwdWVkZW4gZW50ZW5kZXIgY29tbyB1biBhcnJlZ2xvIGRlIGVsZW1lbnRvcyBzaW1pbGFyIGEgbGEgbWF0cmljZXMsIHBlcm8gY29uIGxhIHByaW5jaXBhbCBkaWZlcmVuY2lhIHF1ZSBjYWRhIGNvbHVtbmEgcHVlZGUgdGVuZXIgdW5hIGNsYXNlIGRpZmVyZW50ZS4NCg0KRW4gbGFzIMOhcmVhcyBkZSBpbnRlbGlnZW5jaWEgZGUgbmVnb2Npb3MgeSBkZSBhbmFsw610aWNhLCBsb3MgZGF0YSBmcmFtZXMgc29uIGVsIG9iamV0byBtw6FzIGltcG9ydGFudGUgeSBmcmVjdWVudGUgY29uIGVsIGN1YWwgc2UgdHJhYmFqYS4NCg0KIyMjIDUuMSBDb25zdHJ1Y2Npw7NuIGVzdMOhbmRhcg0KYGBge3J9DQpkZjEgPC0gZGF0YS5mcmFtZShudW0gPSBjKDEsMiwzKSwNCiAgICAgICAgICAgICAgICAgIHR4dCA9IGMoImEiLCJiIiwiYyIpLA0KICAgICAgICAgICAgICAgICAgaW50ID0gcmVwKDRMLDMpLA0KICAgICAgICAgICAgICAgICAgYm9vID0gYyhUUlVFLFRSVUUsRkFMU0UpKQ0KYGBgDQoNCkltcHJpbWFtb3MgZWwgZGF0YSBmcmFtZSBwYXJhIHZpc3VhbGl6YXJsbw0KYGBge3J9DQpwcmludChkZjEpDQpgYGANCkxvcyBkYXRhIGZyYW1lcyBwdWVkZW4gc2VyIHZpc3VhbGl6YWRvcyBhZGVtw6FzIGRlIGxhIHNpZ3VpZW50ZSBmb3JtYSwgZW4gY29uc2lkZXJhY2nDs24gYSBxdWUgZ2VuZXJhbG1lbnRlIGNvbnRpZW5lbiBtdWNob3MgZGF0b3MsIHkgc3UgdmlzdWFsaXphY2nDs24gZW4gbGEgY29uc29sYSBwdWVkZSBzZXIgbGltaXRhZGEgbyBkaWZpY3VsdG9zYS4NCmBgYHtyfQ0KVmlldyhkZjEpDQpgYGANClBhcmEgZXZpZGVuY2lhciBlc3RvLCB0b21lbW9zIHVuIGRhdGEgZnJhbWUgaW50ZWdyYWRvIGVuIFIgcXVlIHNlIGxsYW1hICppcmlzKg0KYGBge3J9DQpwcmludChpcmlzKQ0KVmlldyhpcmlzKQ0KYGBgDQoNClZlcmlmaXF1ZW1vcyBsYSBjbGFzZSBkZSBlc3RvcyBvYmpldG9zOg0KYGBge3J9DQpjbGFzcyhkZjEpDQpgYGANCg0KDQojIyMgNS4yIE90cmFzIGZvcm1hcyBkZSBjb25zdHJ1Y2Npw7NuDQoNCk90cmEgZm9ybWEgZGUgY29uc3RydWlyIGRhdGEgZnJhbWVzIGVzIGEgcGFydGlyIGRlIHVuYSBtYXRyaXogcHJldmlhbWVudGUgY3JlYWRhDQpgYGB7cn0NCm04IDwtIGNiaW5kKHNlcShmcm9tID0gMiwgdG8gPSAxMDAsIGJ5ID0gMiksDQogICAgICAgICAgICBzZXEoZnJvbSA9IDEsIHRvID0gMjAuOCwgYnkgPSAwLjQpLA0KICAgICAgICAgICAgLTI0OjI1KQ0KZGYyIDwtIGRhdGEuZnJhbWUobTgpDQpWaWV3KGRmMikNCmBgYA0KDQojIyMgNS4zIE9wYXJhY2lvbmVzIGJpbmFyaWFzIGNvbiBkYXRhIGZyYW1lcw0KDQpMYXMgc2lndWllbnRlcyBzb24gbGFzIHByaW5jaXBhbGVzIG9wZXJhY2lvbmVzIHF1ZSBzZSBwdWVkZW4gZWplY3V0YXIgY29uIGRhdGEgZnJhbWVzLg0KDQoqIFJlc3VtZW4gZXN0YWTDrXN0aWNvDQpgYGB7cn0NCnN1bW1hcnkoZGYxKQ0KYGBgDQoqIEVzdHJ1Y3R1cmENCmBgYHtyfQ0Kc3RyKGRmMSkNCmBgYA0KKiBEaW1lbnNpb25lcyAoaWd1YWwgcXVlIHVuYSBtYXRyaXopDQpgYGB7cn0NCmRpbShkZjEpDQpuY29sKGRmMSkNCm5yb3coZGYxKQ0KYGBgDQoqIE5vbWJyZXMgZGUgY29sdW1uYXMNCmBgYHtyfQ0KbmFtZXMoZGYxKQ0KYGBgDQojIyMgNS40IEluZGV4YWNpw7NuIGRlIGRhdGEgZnJhbWVzDQoNClVuIGRhdGEgZnJhbWUgcHVlZGUgaW5kZXhhcnNlIGRlIGxhIG1pc21hIGZvcm1hIHF1ZSB1bmEgbWF0cml6Lg0KYGBge3J9DQpkZjJbMTAsM10NCmBgYA0KU2luIGVtYmFyZ28sIGFsIG1vbWVudG8gZGUgZWplY3V0YXIgYW7DoWxpc2lzIGRlIGRhdG9zIGVzdGEgZm9ybWEgZGUgaW5kZXhhciBubyBzdWVsZSBzZXIgbXV5IGVmaWNpZW50ZS4gUG9yIGxvIHF1ZSB0YW1iacOpbiBlcyBwb3NpYmxlIGluZGV4YXIgZXNwZWNpZmljYW5kbyBub21icmVzIGRlIGNvbHVtbmFzOg0KYGBge3J9DQppcmlzJFNlcGFsLkxlbmd0aFsxMjNdDQpgYGANCk8gdGFtYmnDqW4gZGUgZXN0YSBmb3JtYToNCmBgYHtyfQ0KZGYxWzMsInR4dCJdDQpgYGANCkN1YW5kbyBlbXBlY2Vtb3MgYSB1dGlsaXphciBlbCBwYXF1ZXRlICoqdGlkeXZlcnNlKiogdmFtb3MgYSBwcm9mdW5kaXphciBtYXMgZW4gbGEgY29uc3RydWNjacOzbiB5IG1hbmlwdWxhY2nDs24gZGUgZGF0YSBmcmFtZXMgeSBzdXMgb2JqZXRvcyBoZXJtYW5vczogbG9zIHRpYmJsZXMuDQoNCiMjIDYuIExpc3Rhcw0KDQpDb21vIHB1bnRvcyBleHRyYXMgcGFyYSBzdSBldmFsdWFjacOzbiBmaW5hbCwgc2UgZ2FuYXLDoSA1IHB1bnRvcyBlbCBwcmltZXIgZXN0dWRpYW50ZSBxdWUgbWUgcmVtaXRhIHBvciBjb3JyZW8gdW4gc2NyaXB0IGRlIFIgZW4gZWwgY3VhbCwgc2UgZGV0YWxsZSB1bmEgZGVzY3JpcGNpw7NuIGRlIGxvcyBvYmpldG9zIGRlbm9taW5hZG9zIExJU1RBUy4NCg0KRW4gY29uY3JldG8sIGVuIGVsIHNjcmlwdCBkZWJlIGVzcGVjaWZpY2Fyc2U6DQoNCjEuIFF1w6kgc29uDQoyLiBTdXMgZm9ybWFzIGRlIGNvbnN0cnVjY2nDs24NCjMuIFN1cyBvcGVyYWNpb25lcyBiaW5hcmlhcw0KNC4gU3UgaW5kZXhhY2nDs24NCg==