El objetivo de este tema es proporcionar una introducción a R para personas que no han tenido contacto previo con el programa. Se presentan algunos de los rudimentos del lenguaje de programación R. Conocer en detalle el lenguaje R es complicado, hay muchas cosas, pero conociendo unas cuantas de ellas puede ser suficiente para hacer análisis de datos complejos.

Se presentan conceptos básicos y, por tanto necesarios, como distintos tipos y estructuras de datos, asignación, funciones, operadores lógicos, subsetting etc… PERO, recuerda que en el curso vamos a enfatizar otra forma de trabajar con R. En el curso priorizamos trabajar con R usando principalmente los paquetes asociados al tidyverse; es decir, vamos a usar R à la tidyverse. Entonces, ¿por qué un tutorial sobre R-base? La razón es sencilla, los paquetes del tidyverse no son otro lenguaje, necesitan a R para funcionar, no tienen sentido fuera de él, así que cuanto mas sepas de R, de R-base, mejor. Al menos hay un mínimo de elementos de R-base que se necesita entender. En el tutorial intentaré centrarme en lo que es más necesario.

Este tutorial/tema no lo vamos a ver en clase en detalle. En clase empezaremos directamente a trabajar con ejemplos y casos con datos reales y con data.frames. ¿Entonces para qué este tema/tutorial? Pues para que sirva un poco de referencia, poco a poco iremos viendo trocitos de este tema; además tenéis en Internet excelentes recursos para aprender a programar con R.

En las respuestas a este tweet puedes ver distintas opiniones sobre si es mejor comenzar a aprender R con el tidyverse o con R-base:

Supongo que ya tenéis instalado R y RStudio en vuestro ordenador, así que intenta correr/ejecutar en tu ordenador las instrucciones de R que vayan apareciendo en el tutorial.

1. Ideas básicas

Operaciones matemáticas básicas

R puede utilizarse como calculadora.

# operaciones básicas con R
2 + 2    
2 - 2    
2 * 2    
2 / 2 

# Potenciación (se puede hacer con el operador ^ o con **)
3^2    
3**2

# división entera y módulo
11 %/% 5    # división entera 
11 %% 5     # módulo (resto de la división entera)


¿Hay que poner espacios entre los elementos de una expresión?

Como ves, no importa si pones o no espacios entre los elementos de una expresión. De todas formas casi siempre es mejor separar los diferentes elementos de una linea de código: usar espacios hace el código más legible. Para convencerte de que uses espacios: ¿que crees que se se lee mejor, esto, oquizascreesqueseleemejorestootro?


Orden de precedencia de las operaciones

Lógicamente se pueden combinar varias operaciones. Ten cuidado siempre con el orden de precedencia que da R a las operaciones. El orden es el siguiente: paréntesis, exponentes, multiplicación, división, suma y resta. Ante la duda usa los paréntesis.

6 + 2 * 5   
#> [1] 16
(6 + 2) * 5
#> [1] 40


expresiones de varias lineas

De momento es mejor no hacerlo, pero puedes repartir tus expresiones en varias lineas:

2  + 2 + 2 + 2 + 2 +
10 + 
30
#> [1] 50

Más adelante, por ejemplo cuando hagamos gráficos, sí que lo haremos para ganar en claridad en el código. LLegará.


Asignación (“creación de objetos”)

En la sección anterior hicimos que R hiciera unos cálculos. Muy bien, pero lo normal en un análisis con datos es que, para alcanzar un resultado final, tengamos que hacer cálculos intermedios e ir combinándolos para llegar al resultado final. Los cálculos que hemos hecho con R en la sección anterior se han perdido; dicho de otra manera, ahora mismo no podemos recuperarlos.

Para poder recuperar o reusar algún calculo intermedio hecho con R, tenemos que “asignar” un nombre a nuestros cálculos. Repito, para poder recuperar/reusar un valor, este ha de tener asociado/asignado un nombre.

Dicho de otra manera, para poder reusar un resultado o valor intermedio, tenemos que crear un objeto que contendrá el valor que hemos calculado, pero para ello tenemos que poner o asignar un nombre a ese objeto1.

En R, para asignar un nombre a un valor, y así crear un objeto, se usa el operador <-2. Veámoslo:

aa <- 2 + 2

Fíjate que parece que R no ha hecho nada ya que no nos muestra el resultado de la operación en la consola. Ahora lo que ha hecho es realizar la operación obteniéndose un resultado o valor de 4 (dos mas dos son 4), pero en lugar de mostrarnos/devolvernos el valor, lo que ha hecho es “almacenar” el valor 4 en el objeto aa.

En vuestra cabeza una expresión de asignación, por ejemplo aa <- 44, debe sonar como aa toma el valor 44.

Podemos ver el objeto aaen el panel superior-derecha de RStudio, concretamente en la pestaña “Environment”. Ahora el entorno de R, R, tiene un objeto almacenado en su memoria, un objeto que se llama “aa” y cuyo valor es 4.

Veámoslo: si ahora ejecutamos aa, R nos devolverá el valor 4. Podemos pensar que aa es un objeto, donde se almacena el valor 4.

aa
#> [1] 4

Ahora podemos usar el objeto aa para continuar o hacer más cálculos. Por ejemplo:

aa + 1
#> [1] 5

El nombre aa está asociado al valor 4, así que cuando usemos aa en una nueva instrucción, R usará el valor 4. Evidentemente podemos cambiar el valor del objeto aa. Por ejemplo:

aa <- 10

Ahora, si llamamos a aa nos devuelve el valor 10.

aa
#> [1] 10

¿Qué hacen las siguientes lineas de código? Puedes pensarlo, pero lo mejor es que ejecutes el código en tu ordenador.

aa <- 2 + 2
bb <- 5 + aa
cc <- 1 + aa * 2
dd <- bb

Fijaros que ahora en el entorno o environment de R tendremos 4 objetos: aa, bb, cc, dd.


Global environment (primera referencia al)

Cada vez que asignas un nombre a un valor, R crea un nuevo objeto en el Global environment. Estos objetos existirán hasta que cierres tu sesión en R, después desaparecerán, a no ser que guardes los objetos en disco en un fichero. Podemos ver los objetos que hay en el Global environment pinchando en la pestaña “Environment” del panel superior-izquierdo de RStudio. También podemos hacerlo con la función ls(). Ejecuta las siguientes instrucciones y observa que ocurre en el Global environment.

ls()              #- muestra los objetos en el Global env. Hace lo mismo que objects()
#> [1] "aa" "bb" "cc" "dd"

rm(cc)            #- borra/elimina el objeto cc del Global env.
rm(list = ls())   #- borra todos los objetos del Global env.


Recuerda que en R, la forma genérica de una operación de asignación, o de “creación de objetos”, es: nombre_del_objeto <- valor, por ejemplo aa <- 2 + 2, y que para poder recuperar/reusar un valor ha de tener asociado/asignado un nombre.


¿Puedo usar el nombre que quiera para mis objetos?

No del todo. Para que un nombre sea sintácticamente válido en R sólo puede contener caracteres alfanuméricos (letras y números), puntos (.) y guiones bajos (_).

Además:

  • un nombre NO puede empezar por un número

  • un nombre NO puede empezar por _

  • si el nombre empieza por . , el punto NO puede ir seguido de un número

# nombres válidos
pepe <- 4 
pepe_2 <- 4
pepe_2.3 <- 22
.hola <- 7

# nombres no válidos
4pepe <- 33
_hola <- 66
.2xx  <- 88

Lo normal es usar nombres simples y, si puede ser, explicativos de la naturaleza del valor almacenado en el objeto.

R distingue entre mayúsculas y minúsculas así que puede existir un objeto con nombre Ariadna y otro con nombre ariadna.

Como en todos los lenguajes de programación, R tiene una serie de nombres reservados, ya asignados, y que por tanto no se pueden usar para nombrar a los objetos que nosotros creemos. Lógicamente están prohibidos porque R los usa internamente. Estos nombres prohibidos son:

if  else repeat  while  for in next break
function
TRUE FALSE 
NULL Inf NaN 
NA NA_integer_ NA_real_ NA_complex_ NA_character_


Tipos de datos

Ya sabemos que R nos va a ayudar a manejar/analizar datos. Bien, pero ¿qué tipos de datos entiende R?

Los principales tipos son 3: numéricos, de texto y lógicos; en realidad 4 porque R diferencia los datos númericos en doubles e integers3 Cada dato o valor es de un tipo. El tipo de un valor determina la forma de almacenamiento y las operaciones que se pueden hacer con él.

R puede manejar, tiene, 5 tipos de datos:

  • Caracteres (texto): Pepe, hola, Madrid

  • Doubles: 2,35 , 77,00 , -5,6

  • Enteros: 1, -44

  • Lógicos: TRUE, FALSE (En operaciones aritméticas toman valores 1 y 0)

  • Complejos: 3+2i

Ademas de estos 5, existe un sexto tipo: raw, que no vamos a utilizar.

Por ejemplo:

aa <- "La calle en la que vivo es"    #- texto
bb <- 4L                              #- número entero
cc <- 4.3                             #- número decimal
dd <- TRUE                            #- logical

Normalmente sabremos de que tipo(texto, entero, …) es cada objeto, pero conviene saber con certeza de que tipo es cada objeto. Por ejemplo, los objetos creados en el chunk anterior, ¿de que tipo son? Nos lo imaginamos, pero para confirmarlo podemos usar la función typeof()

typeof(aa)
#> [1] "character"
typeof(bb)
#> [1] "integer"
typeof(cc)
#> [1] "double"
typeof(dd)
#> [1] "logical"


En realidad los 3 tipos fundamentales de datos/valores que usaremos son:

  • “numeric”: Numéricos (números enteros y reales)

  • “character”: Texto (secuencias o cadenas de caracteres)

  • “logical”: Lógicos o booleanos (TRUE o FALSE, que en operaciones aritméticas toman valores 1 y 0)

Además, hay 4 valores especiales: NULL, NA, NaN, e infinito

Los NA’s serán más adelante importantes y en cierta forma hay que tener cuidado con ellos, siempre habrá que chequear si nuestros datos contienen NA’s. NA representa una característica que existe pero de la cual no sabemos su valor, pero ese valor existe. Por ejemplo, imagina que el siguiente vector representa la altura en centímetros de un grupo de personas : c(182, 187, 159, NA, 166). El vector tiene 5 elementos, pero el cuarto elemento es un NA, lo que indica que no sabemos la altura del cuarto individuo. Imagina que queremos calcular la altura media, utilizaremos la función mean(). Fíjate que ocurre:

aa <- c(182, 178, 172, NA, 168)
mean(aa)
#> [1] NA
mean(aa, na.rm = TRUE)
#> [1] 175

Como ves, cuando se hace un calculo estadístico con un objeto que contiene NA’s, el resultado es NA; es decir, los NA’s se propagan4. Si quieres que esto no ocurra cuando haces cálculos estadísticos, has utilizar dentro de las funciones la opción na.rm = TRUE que lo que hace es excluir los NA’s antes de hacer los cálculos.


Operaciones con números

Con datos o variables numéricas ya hemos visto que se pueden hacer operaciones aritméticas (suma, resta etc…), pero con números se pueden hacer también comparaciones.

Por ejemplo:

2 < 4    # MENOR que: esta expresión chequea si 2 es MENOR que cuatro. Como es cierto, nos devuelve un TRUE
#> [1] TRUE
2 > 4    # MAYOR que: esta expresión chequea si 2 es MAYOR que cuatro. Como es cierto, nos devuelve un TRUE
#> [1] FALSE
5 >= 7   # MAYOR o IGUAL que.
#> [1] FALSE
8 <= 8   # MENOR o IGUAL que.
#> [1] TRUE
9 == 7   # IGUAL a: como 9 no es igual a 7, nos devolverá un FALSE
#> [1] FALSE
2 == 2   # IGUAL a: como 2 es igual a 2, nos devuelve TRUE
#> [1] TRUE
2 != 4   # NO IGUAL: como 2 no es igual a 4 nos devuelve TRUE
#> [1] TRUE
5 != 5   # NO IGUAL: como 5 es igual a 5, nos devuelve FALSE 
#> [1] FALSE

Fíjate que estas expresiones de comparación al ser ejecutadas devuelven un “boolean”: el resultado de la comparación solo puede ser TRUE o FALSE si la afirmación es cierta o, por el contrario no es cierta. Por ejemplo si ejecutas 5 < 2 el resultado sera FALSE porque 5 no es menor que 2.

Que ¿para qué necesitamos estos operadores? Para muchas cosas, por ejemplo para ver que valores de una variable están por encima de la media.

Fíjate que el operador para chequear la igualdad de dos valores no es =, que es lo que habitualmente usamos en matemáticas para expresar igualdad. En R la igualdad se expresa/chequea con el doble igual: ==.

Recuerda que el signo = se puede usar para hacer una asignación, aunque en R es más habitual usar el = dentro de las funciones para asignar un valor al argumento de una función, y se suele usar <- como símbolo de asignación.


Operaciones con texto

Hay muchas, pero por ejemplo, no podemos sumar 2 strings o 2 cadenas de texto, pero si que se pueden pegar o concatenar. Por ejemplo con la función paste().

aa <- "mi nombre es"
bb <- "pedro"
paste(aa, bb)
#> [1] "mi nombre es pedro"
paste(aa, bb, sep = " ... ")
#> [1] "mi nombre es ... pedro"
# Prueba tú mismo que hace la función paste0()

Veamos algunas otras “operaciones” que se pueden hacer con variables de texto:

toupper(aa)
#> [1] "MI NOMBRE ES"
tolower(aa)
#> [1] "mi nombre es"
stringr::str_to_sentence(aa)   #- stringr es un pkg del tidyverse
#> [1] "Mi nombre es"

nchar(bb)                      #- nchar() nos devuelve el número de caracteres de un string
#> [1] 5
substring(bb, 2, nchar(bb))    #- substring() extrae caracteres de un string
#> [1] "edro"

Si al final necesitamos manipular texto, tendremos que aprender un poco de regular expressions y, si puede ser, utilizaremos el paquete stringr


Operaciones lógicas

A veces tendremos que hacer operaciones lógicas, por ejemplo, tendremos que seleccionar a los individuos que tengan más renta que la media y que además estén solteros. En R tenemos los siguientes operadores lógicos:

  • &: es el operador lógico AND. Devuelve TRUE si se cumplen todas las condiciones.

  • |: es el operador lógico OR. Solo devuelve FALSE si NO se cumple ninguna de las condiciones.

  • !: es el operador lógico NOT. Cambia el sentido de una afirmación lógica. No confundir con el operador relacional o de comparación !=

  • xor(): es el operador lógico Exclusive OR. Devuelve TRUE si se cumple una y solo una de las 2 condiciones.

Hay alguno mas (all(), any(), && y ||) pero suficiente con estos tres primeros.

(4 > 3) & (3 > 2)     #- Y: como se cumplen las dos condiciones nos devuelve TRUE
#> [1] TRUE
(1 == 2) | (2 >3)       #- O: Como no se cumple ninguna de las 2 condiciones nos devuelve FALSE
#> [1] FALSE
!(4 > 3)              #- NOT: 4 es mayor que 3 es TRUE, pero el ! delante de esa condición la niega y pasa a FALSE
#> [1] FALSE
!!(4 > 3)             #- si niegas dos veces, vuelves al principio: TRUE
#> [1] TRUE

xor(10 < 1, 10 >1 ) #- se cumple 1 de las dos condiciones, entonces TRUE
#> [1] TRUE
xor(10 > 1, 10 >1 ) #- se cumplen las 2 condiciones, entonces FALSE
#> [1] FALSE
xor(10 < 1, 10 <1 ) #- no se cumple ninguna de las 2 condiciones: FALSE
#> [1] FALSE

Funciones

Entender qué es una función y cómo “funciona una función” es muuuuy importante!!!

R y sus paquetes tienen muchísimas funciones que nos permiten, por ejemplo, calcular logaritmos, o raíces cuadradas, o calcular desviaciones típicas etc etc… Además, también podemos crear nuestras propias funciones. Seguramente crearemos algunas en el curso, pero antes, es MUY IMPORTANTE entender qué es y cómo “funciona”, cómo se usa, una función de R.

Repito, es muy importante entender qué es y cómo usar una función. De hecho, todo lo que ocurre/pasa en R es porque has llamado a una función. Si algo ocurre en R es porque has hecho una llamada a una función, una “function call”.

Una función no es más un trozo de código R, unas instrucciones de R, a las que les hemos puesto un nombre; entonces, cuando invoquemos ese nombre, esa función, se ejecutarán esas lineas de código.

Empecemos a usar alguna función sencilla (el objetivo es entender como “funciona” una función):

Una función muy sencilla es sqrt(). Como igual imagináis, la función sqrt() sirve para calcular la raíz cuadrada de un número. Alguien, ha escrito por nosotros unas lineas de código que sirven para hacer raíces cuadradas y ha asignado esas lineas de código al nombre sqrt. sqrt es el nombre de la función sqrt()

Como alguien se ha preocupado de construir una función para hacer raíces cuadradas, nosotros podemos usar esa función. Si por ejemplo queremos calcular la raíz cuadrada de 9, sólo tendremos que teclear en R sqrt(9).

sqrt(9)
#> [1] 3

Perfecto, está bien que sepamos como hacer raíces cuadradas en R, PERO lo importante es empezar a entender la estructura de las funciones.

La forma genérica de utilizar una función es algo como: nombre_de_la_funcion(argumento_1, argumento_2, ....). En el caso de la función sqrt() su nombre es sqrt y esta función solo admite un argumento. Un vector de números5.

sqrt() solo admite un argumento, pero otras funciones pueden tener más de un argumento y quizás los argumentos no sean numéricos sino por ejemplo textuales. Buff!! es fácil, pero explicarlo por escrito, sin ver al que va a recibir la explicación, es complicado.

sqrt(9)
sqrt(9, 4)   # no funciona porque sqrt() solo admite un argumento 
sqrt("9")    # no funciona, solo hemos puesto un argumento pero es textual, y ha de ser numérico

Es decir, para usar una función tenemos que saber cuantos argumentos admite y de que tipo han de ser esos argumentos. Generalmente esto es obvio, pero habrá veces que tengamos que usar una función que no sepamos como funciona, entonces tendrás que buscar su ayuda.

Para obtener ayuda sobre una función podemos usar la función help(). Si tecleas en R help(nombre_de_la_funcion) se abrirá una ventana de ayuda en la pestaña “Help” del panel abajo-derecha. Probémoslo con la función sqrt()

help(sqrt) 

Al leer la ayuda de la función te darás cuenta que hace un rato hicimos un poco de trampas, ya que explicamos el funcionamiento de las funciones de una forma un poco simplificada. Vamos a hacerlo ahora mejor.

Yo dije que la estructura de una función es nombre_de_la_funcion(argumento_1, argumento_2, ....) cuando en realidad es : nombre_de_la_funcion(argumento_1 = valor_1, argumento_2 = valor_2, ....).

Vamos a explicarlo con la función sqrt(). El nombre de la función sqrt() es sqrt; y ¿cuales son sus argumentos? Si miramos más detenidamente la ayuda, veremos que sí, que sólo tiene un argumento x , que además debe ser numérico.

Bien, pero ¿cual es el valor del argumento? Intenta descubrirlo con el chunk de código siguiente:

sqrt(x = 9)
sqrt(x = 4)

La sintaxis correcta de sqrt() es efectivamente nombre_de_la_funcion(argumento_1 = valor_1, argumento_2 = valor_2, ....), en su caso, como solo tiene un argumento: sqrt(argumento = valor).

El nombre del argumento es x, y el valor del argumento es el que nosotros queramos, siempre que sea numérico. La sintaxis correcta-correcta es sqrt(x = 9). 9 es el valor que le damos al argumento x de la función sqrt().

Obviamente podemos cambiar el valor de la función, sino la función no nos sería muy útil. Entonces, ¿por qué funcionan las 2 lineas de código siguientes?

sqrt(9)
#> [1] 3
sqrt(4)
#> [1] 2

Pues porque a R no le importa que no pongas el nombre del argumento, puedes poner solamente su valor. Hablaremos de esto un poco más en clase.

A ver si consigues diferenciar, en una función, entre el nombre de un argumento y su valor

x <- 25
sqrt(9)
#> [1] 3
sqrt(x)
#> [1] 5
sqrt(x = x)
#> [1] 5

Sí, cuesta un poco, poquet a poquet, “bird by bird”.


Otra vez más. Por favor, calculad con R el logaritmo de 1000 en base 10. Para ello tenéis que usar la función log(), y cómo no sabéis cómo se usa, no sabéis cómo es su sintaxis, no sabéis cuales son sus argumentos, os tocará mirar la ayuda de la función.

help(log)
log(x = 1000, base = 10)

Al principio mirar la ayuda interna de R para las funciones abruma, pero muchas veces con mirar los ejemplos (que están siempre al final) o la sección de “Arguments” es suficiente. En el caso de log() vemos que admite dos argumentos, “x” y “base”. La ayuda nos dice que la sintaxis de la función es:

  • log(x, base = exp(1))

  • “x” ha de ser numérico (o complejo)

  • “base” ha de ser un número positivo (o complejo)

Fíjate que la sintaxis de la función no es log(x, base), sino log(x, base = exp(1)); esto indica que el argumento “base” tiene un valor asignado por defecto; es decir, si no especificamos el valor de “base”, “base” será exp(1); es decir, el numero e y estaremos tomando logaritmos naturales.

log(1000)  #- como no especificamos el valor del argumento "base", R lo fija por defecto base = exp(1) y toma logaritmos naturales
#> [1] 6.907755

log(1000, base = 10) #- ahora si estamos calculando logaritmo en base 10
#> [1] 3


Es más seguro poner los nombres de los argumentos, pero si no los pones, o solo los pones parcialmente, también funciona

log(1000, bas = 10)   #- funciona, pero ...
#> [1] 3
log(1000, 10)         #- esto sí es muy habitual verlo
#> [1] 3

Eso sí, si no pones el nombre de los argumentos, has de tener en cuenta el orden de estos.

log(10, 1000)
#> [1] 0.3333333

Poco a poco iremos hablando más de funciones. Cuando usemos una nueva generalmente tendremos que consultar la ayuda para ver su sintaxis y sus argumentos.

Saber buscar ayuda es una de las habilidades más importantes a la hora de aprender a programar. Este post da buenos consejos sobre cómo buscar ayuda sobre R. En el siguiente apartado desarrollaré un poco la idea de cómo usar la ayuda interna de R sobre sus funciones.


Ayuda de las funciones

Hay varias formas de pedir a R la ayuda interna de una función:

  1. usando la función help(), o sea, tecleando en R help(nombre_de_la_funcion); por ejemplo help(log)

  2. tecleando en R ?nombre_de_la_funcion; por ejemplo ?(log)

  3. la forma más cómoda, y la que suelo usar, consiste en situar el cursor en el nombre de la función y pulsar la tecla F1

Entender y saber leer la ayuda de las funciones es muy útil, así que os recomiendo, para más adelante (de momento tenemos otras cosas que aprender), la lectura de este post. Explican bien como está estructurada la ayuda de las funciones. Abajo tienes un ejemplo con la función mean().


Como ejemplo puedes probar a leer la ayuda de la función seq(). Mirando la ayuda intenta adivinar que harán las siguientes expresiones R. Tienes que entender porque la cuarta y quinta linea no devuelven el mismo resultado.

seq(from = 0, to = 10)
#>  [1]  0  1  2  3  4  5  6  7  8  9 10
seq(0, 10)
#>  [1]  0  1  2  3  4  5  6  7  8  9 10
seq(10, 0)
#>  [1] 10  9  8  7  6  5  4  3  2  1  0
seq(0, 10, 3)
#> [1] 0 3 6 9
seq(0, 10, length.out = 3)
#> [1]  0  5 10


No solo tenemos la ayuda oficial, en R también hay funciones que nos pueden ayudar a entender como funciona otra función; por ejemplo la función args(), que como su nombre insinúa, nos permite ver los argumentos de una función. Por ejemplo:

args(sqrt)       #- sqrt() solo tiene un argumento, x
args(log)        #- log() tiene 2 argumentos: x y base. Además base tiene un valor por defecto: exp(1)
args(args)       #- args() solo tiene un argumento, llamado name.


Ciclo vital de las funciones

Otra idea que conviene conocer, es que las funciones tienen un ciclo vital o “life cycle”: hay funciones que acaban de ser creadas y aún son experimentales. Hay funciones que pueden estar siendo cuestionadas y otras que pasan de experimentales a estables. Este post muestra los diferentes estados de una función. El estado actual de una función se suele conocer por su distintivo o “badge”. Por ejemplo, en el siguiente tweet se explica la diferencia entre una función cuyo badge es deprecated de otra función cuyo badge es superseded:


Ya vale de funciones, pero hay algo que a algunos os rondará por la cabeza.

Cuando hemos visto la ayuda de la función sqrt(), sólo tenía un argumento x, y este argumento tenía que ser, según la ayuda, un vector (o array6) de números.

PERO nosotros no introdujimos un vector en sqrt(), sino que introdujimos un sólo número. Sí, es cierto, introdujimos un sólo número, pero R no entiende lo que es un escalar, para él un número aislado es un vector, para R el número 4 es un vector con un sólo elemento, el número 4.

Fíjate que el valor que introduzcamos en sqrt() tiene que cumplir dos requisitos, ser un vector y que sus valores sean numéricos. Estas dos ideas hay que tenerlas claras para trabajar con R:

  • Tipos de datos: sqrt() sólo admite datos numéricos, pero ¿qué otros tipos de datos podemos utilizar en R? Lo hemos visto en una sección anterior (texto, lógicos,…) . Por ejemplo, si ejecutamos sqrt("99"), R nos devolverá un error, porque "99" no es numérico es texto.

  • Estructuras de datos: El vector es una de las estructuras de datos que “entiende R”, una de las estructuras en las que puede almacenar datos y luego volver a encontrarlos, pero hay más (matrices, listas, data frames)

En la próxima sección se presentan las diferentes estructuras de datos que tiene R para almacenar datos, pero en el curso vamos a centrarnos principalmente en una estructura de datos, los datos tabulares, que en R se almacenan en una estructura llamada data frame.

En clase ya hemos trabajado con data.frames, es la estructura de datos a la que estamos más acostumbrados, una tabla de datos con filas y columnas. Son las típicas tablas de Excel, con las variables en columnas y las observaciones en filas. Es muy fácil e intuitivo porque estamos acostumbrados a ellas, es lo que estamos acostumbrados a trabajar en ciencias sociales.

Igual de intuitivo resulta el hecho de que hay variables numéricas, como por ejemplo el número de páginas de un libro, y variables cuyos valores son texto; por ejemplo el título o el autor del libro.


Estructuras de datos en R

R es un lenguaje/programa/entorno para hacer estadística por lo que generalmente trabajaremos con datos. Pero los datos tienen que estar almacenados en objetos. Igual que en matemáticas podemos almacenar datos en determinadas estructuras como vectores o matrices, en R ocurre lo mismo, los datos se almacenan en objetos. PERO hay distintos tipos de objetos o estructuras de datos7.

Para almacenar conjuntos de valores o datos, R tiene definidas determinadas estructuras de datos. ¿Cuántas? ¿Cuáles? ¿Cómo se llaman?

Pues hay varias formas de contarlo, para Hadley Wickham sólo hay una estructura de datos, los vectores. Sólo que estos vectores pueden tener diversas propiedades. Hay vectores atómicos y vectores recursivos. Seguro que tiene razón, pero aunque sea un poco menos preciso, es mucho más útil explicarlo, al menos al principio, de otra forma.

Podemos pensar que las principales estructuras de datos en R son:

  • vectores

  • matrices

  • arrays (matrices multidimensionales)

  • listas

  • data.frames

Las que más utilizaremos en el curso son los data.frames, y en segundo lugar las listas. Sí, pero en realidad estas 2 estructuras (data.frames y listas) son en realidad agrupaciones de vectores; es decir, la estructura básica, la más importante en R son los vectores. Son los más importantes porque el resto de estructuras se construyen a partir de grupos de vectores o añadiendo alguna propiedad adicional, o atributo, a un vector.


2. Vectores

La estructura de datos fundamental en R es el vector8. En R, un vector es una estructura de datos que sirve para almacenar un conjunto ordenado de valores o elementos. Un vector puede contener cualquier número de elementos; sin embargo, todos los elementos deben ser del mismo tipo. Por ejemplo, un vector no puede contener números y texto.

Esta estructura para almacenar datos en R es muy fácil de entender porque son los típicos vectores que conocemos de matemáticas.

Vamos a crear nuestro primer vector en R; para ello usamos la función c():

aa <- c(3, 22, 6)
aa
#> [1]  3 22  6

Como vemos con la función c() hemos concatenado 3 números para construir un vector, el vector aa.

is.vector(aa)
#> [1] TRUE

Una de las características que tiene esta estructura de datos es que los elementos de un vector tienen que ser todos del mismo tipo. En nuestro caso el vector aa tiene 3 elementos, todos ellos de tipo numérico, concretamente doubles. Veámoslo con typeof()

typeof(aa)
#> [1] "double"

Creemos ahora un vector con datos textuales o caracteres:

aa <- c("Hola", "número", "9")
is.vector(aa)
#> [1] TRUE
typeof(aa)
#> [1] "character"

Ahora un vector de booleanos:

aa <- c(FALSE, TRUE, FALSE)
is.vector(aa)
#> [1] TRUE
typeof(aa)
#> [1] "logical"

Como R tiene 4 tipos de datos fundamentales (integer, double, character y lógico)9 se pueden crear con ellos 4 tipos de vectores; a estos tipos fundamentales de vectores se les suele llamar como “atomic vectors”.


Atomic vectors vs. Augmented vectors

Este tópico no es fácil de entender, seguramente, igual empezáis a entenderlo a mitad de curso, pero si que conviene tener al menos una ligera idea sobre ello, por eso lo introduzco mínimamente.

Como R tiene 4 tipos fundamentales de datos, con ellos podemos construir 4 tipos de vectores fundamentales o “atomic vectors”. El tipo de datos, or modes, define cómo se van a almacenar internamente los valores. El tipo de los vectores podemos obtenerlo con la función typeof().

Para ver de que tipo (fundamental) es un vector, disponemos de la función typeof()

typeof(4)
#> [1] "double"
typeof(4L)
#> [1] "integer"
typeof("4")
#> [1] "character"
typeof(TRUE)
#> [1] "logical"

PERO resulta que sobre la base de estos 4 tipos de vectores fundamentales se han construido otros tipos de vectores, que como son derivados de los fundamentales, se les suele llamar (derived or) augmented vectors10.

Voy a explicarlo un poco, pero antes hay que saber que los objetos, por ejemplo los vectores, en R pueden tener “atributos”. Podemos pensar que los atributos son una forma de poder almacenar información adicional (metadatos) en un objeto de R. OK, estos atributos son los que permiten crean nuevos tipos de vectores (augmented vectors).

Los augmented vectors o vectores derivados también se almacenarán internamente como numéricos o character o logical, PERO al contener información adicional, o atributos, hace que este tipo especial de vectores puedan ser identificados y tratados de forma especial por ciertas funciones de R. Es decir, los augmented vectors son como los atomic vectors (vectores fundamentales), se construyen a partir de ellos, PERO tienen información adicional (metadatos) almacenadas en sus atributos.

Se puede pensar que los atributos son una “named list of vectors” que se adjunta a un objeto. Puedes ver los atributos de un objeto con la función attributes().

Entre los atributos que podemos definir, hay uno especial llamado class, que permite definir la object’s class y que es el atributo que va a hacer que algunos vectores se comporten de manera distinta ante algunas funciones; por ejemplo que se impriman o grafiquen de forma un poco especial. Podemos ver la clase (class) de un objeto con la función class().

Entre los vectores aumentados, Hay 4 tipos importantes que conviene conocer: los factores, los factores ordenados, las fechas (date) y las date-time (o posixct).

Creado por Ildiko Czeller y Graham Parsons

Creado por Ildiko Czeller y Graham Parsons

Para tener un ejemplo que ayude a entender lo anterior uasaré el vector iris$Species. El vector iris$Species es en realidad un vector de integers pero, como ademas de los valores de los enteros, tiene metadatos almacenados en sus atributos, su class es factor. Sí, complicado entenderlo a la primera, PERO lo que si que se os tiene que quedar es que a veces usaremos factores. Lo importante será saber usar los factores correctamente y no tanto saber qué es un factor11. Lo veremos!!

typeof(iris$Species)
#> [1] "integer"
attributes(iris$Species)
#> $levels
#> [1] "setosa"     "versicolor" "virginica" 
#> 
#> $class
#> [1] "factor"
class(iris$Species)
#> [1] "factor"

Coerción

Ya dijimos que, en R, los elementos de un vector tienen que ser todos del mismo tipo; por eso decimos que los vectores son una estructura de datos homogénea.

Bien, pero ¿qué ocurre si en un vector hay elementos de distinto tipo?

por cómo están definidos internamente los vectores, sólo pueden contener elementos del mismo tipo, pero si por error introducimos valores de distinto tipo, R convertirá todos sus valores a un único tipo. Forzará a que sus elementos sean del mismo tipo. Esta forma de proceder en R, consistente en transformar todos los elementos de un vector a un mismo tipo se llama coerción.

¿A que tipo los convierte? Pues al menos flexible. Veámoslo con varios ejemplos:

aa <- c(4, 6,  "Hola")

En la expresión de arriba hemos puesto en el vector aa tres elementos, 2 números y un string. No puede ser, estamos violando las reglas internas de R para los vectores: los vectores sólo pueden tener elementos del mismo tipo.

Entonces, ¿por qué R no nos devuelve un mensaje de error? Porque lo que hace es convertir los valores de aa a un único tipo, ¿a cual? veámoslo con typeof().

aa <- c(4, 6, "Hola")
is.vector(aa)
#> [1] TRUE
typeof(aa)
#> [1] "character"
aa               #- ¿qué ha pasado?
#> [1] "4"    "6"    "Hola"

Fíjate en los resultados. ha convertido los 2 números a caracteres. El primer elemento de aa no es el número cuatro sino el string 4. ¿Veis que está entre comillas? Lo ha convertido en el carácter "4".


aa <- c(TRUE, 4L, 4, "Hola")
typeof(aa)
#> [1] "character"
aa <- c(TRUE, 4L, 4)
typeof(aa)
#> [1] "double"
aa <- c(TRUE, 4L)
typeof(aa)
#> [1] "integer"

Fíjate que con el chunk de arriba puedes inferir las reglas de conversión que usa R. character > double > integer > logical. Si en el vector hay un valor de texto, todos los valores se convertirán al tipo character.

Los valores de tipo carácter son los menos flexibles, piensa que no hay ninguna forma de convertir “Hola” en un número o en boolean. Al contrario sí se puede: el número 9 siempre se puede convertir al carácter “9”. Los booleans (TRUE y FALSE) se pueden transformar tanto en texto como en numérico: TRUE pasará a ser 1 y FALSE = 0.


Coerción explícita

A veces tendremos un vector de un tipo, por ejemplo numérico, pero nos gustaría convertirlo a por ejemplo character. ¿Podemos hacerlo? Sí.

aa <- c(1:4)
aa
#> [1] 1 2 3 4
aa <- as.character(aa)
aa
#> [1] "1" "2" "3" "4"

Hay toda una familia de unciones para hacer estas conversiones, si es que son posibles, ya que, por ejemplo, no podremos pasar un vector de strings a numérico.

as.character()           
as.logical()    
as.numeric()   
y algunas más como as.data.frame() ....


Propiedades de los vectores

Todo vector tiene dos propiedades claves:

  • su tipo, que podemos ver con typeof()

  • su longitud, orden o número de elementos, que podemos ver con length()

aa <- c(1:10)
typeof(aa)
#> [1] "integer"
length(aa)
#> [1] 10
attributes(aa)
#> NULL

Ademas, los vectores pueden tener más propiedades o atributos. Estas propiedades son adicionales, no son necesarias y generalmente se usan para construir tipos especiales de vectores. Se definen los atributos con la función attr() y con attributes() se pueden ver los atributos que tiene un determinado vector.

aa <- c(1:3)
attributes(aa)
#> NULL
attr(aa, "atributo_1") <- "This is a vector"
attributes(aa)
#> $atributo_1
#> [1] "This is a vector"
attr(aa, "atributo_2") <- c("primero", "segundo", "tercero")
aa
#> [1] 1 2 3
#> attr(,"atributo_1")
#> [1] "This is a vector"
#> attr(,"atributo_2")
#> [1] "primero" "segundo" "tercero"

Un atributo que a veces se usa es poner nombres a los elementos de un vector, se puede hacer directamente al crear el vector o después con setNames() o con set_names()

aa <- c(a = 1, b = 2, c = 4)
aa <- c(1,2,4)
aa <- setNames(aa, letters[1:3])       
aa                                      
#> a b c 
#> 1 2 4
aa <- c(1,2,4)
aa <- rlang::set_names(aa, c("a", "b", "c"))
aa                                      
#> a b c 
#> 1 2 4


Subsetting con vectores

En R podemos agrupar valores en vectores. Perfecto, pero habrá veces que necesitemos manipular los vectores. Por ejemplo necesitaremos acceder a determinados elementos de un vector, quizás a los elementos que sean mayores que 5, o a los que empiecen con la letra A, etc etc… Este tipo de operaciones sobre los vectores se les conoce genéricamente como subsetting; porque lo que se hace es crear subconjuntos de un vector. Veámoslo.

Hay varios tipos de subsetting, pero con vectores los principales son 3:

  1. Positive Index: podemos seleccionar por posición. Por ejemplo a los 2 primeros elementos o los 2 primeros y los 2 últimos. Veámoslo:
aa <- c(10:1)
aa[c(1:2)]        #- primer y segundo elemento del vector
#> [1] 10  9
aa[c(1,2,9,10)]   #- dos primeros y 2 últimos
#> [1] 10  9  2  1
aa[c(1,1,10,10)]  #- si repites el indice se repite el elemento del vector
#> [1] 10 10  1  1
  1. Negative index: eliminamos elementos del vector.

Por ejemplo eliminamos los 2 primeros elementos del vector y los 2 últimos:

aa <- c(10:1)
aa[- c(1,2, 9:10)]
#> [1] 8 7 6 5 4 3
  1. Logical index: se seleccionan aquellas posiciones marcadas con TRUE.

Esta forma de hacer subsetting será muy útil. De momento no lo apreciaremos, pero enseguida veremos su utilidad cuando hagamos subsetting lógico con funciones de comparación. Como ejemplo:

aa <- 1:10
aa <- aa[aa >= 7]
aa
#> [1]  7  8  9 10

Si no acabas de entenderlo, corre las siguientes lineas de código:

aa <- 1:10
aa <= 4
aa[aa <= 4]
aa <- aa[aa <= 4]

Modificando elementos de un vector

aa <- c(1:10)
aa[4] <- 88             #- el cuarto elemento de aa tomará el valor 88
aa <- c(aa, 111, 112)   #- añadimos 2 elementos al vector aa

Los vectores se pueden concatenar

aa <- c(1:5)
bb <- c(100:105)
cc <- c(aa, bb)

Creación de vectores mediante …

  • secuencias
  • repeticiones
  • números aleatorios

Secuencias

Ya sabemos que podemos generar secuencias de números con el operador :. Por ejemplo:

1:10
#>  [1]  1  2  3  4  5  6  7  8  9 10

Pero la función seq() nos ofrecen más posibilidades:

seq(1, 10, 2.5)
#> [1] 1.0 3.5 6.0 8.5
seq(from = 1, to = 10, by = 2.5)
#> [1] 1.0 3.5 6.0 8.5

Repeticiones

la función rep() permite generar vectores repitiendo los elementos de un vector o lista

aa <- 1:3
rep(aa, 2)
#> [1] 1 2 3 1 2 3
rep(aa, each = 2)     
#> [1] 1 1 2 2 3 3
rep(aa, c(2,2,4))
#> [1] 1 1 2 2 3 3 3 3
rep(aa, each = 2, len = 10)   
#>  [1] 1 1 2 2 3 3 1 1 2 2
rep(aa, each = 2, times = 3)  
#>  [1] 1 1 2 2 3 3 1 1 2 2 3 3 1 1 2 2 3 3

generando números aleatorios


Operaciones con vectores

Todos sabemos que los vectores se pueden sumar, multiplicar, trasponer, … En realidad en el curso no vamos a utilizar álgebra matricial o vectorial, pero si que vamos a manipular, a hacer operaciones con vectores, aunque generalmente lo haremos usando funciones.

No obstante, conviene saber algunas cosas sobre operaciones con vectores en R. Por ejemplo podemos sumar vectores:

aa <- 1:10
bb <- 1:10
aa + bb
#>  [1]  2  4  6  8 10 12 14 16 18 20

Recycling

Si intentamos hacer una operación con dos vectores pero estos no tienen el mismo orden, entonces R, automáticamente reciclará el vector más corto hasta que alcance la longitud del más grande y así poder hacer la operación. Este comportamiento se conoce como recycling. Por ejemplo:

Un ejemplo:

aa <- 1:10
aa + 1
#>  [1]  2  3  4  5  6  7  8  9 10 11

Como es que hemos podido sumar aa+1, si son vectores de distinto orden. Pues porque R siempre intentará hacer la operación y si no se puede, lo que hace es reciclar, aumentar el vector más pequeño, ¿Cómo? Repitiendo los elementos del vector más pequeño hasta que alcance la longitud del vector más grande.

Otro ejemplo de reciclado:

aa <- 1:6
bb <- 1:2
aa + bb
#> [1] 2 4 4 6 6 8

funciones vectorizadas

La mayoría de funciones, y todos los operadores de R están vectorizados; es decir, si una función recibe como input un vector, se aplicará la función a cada uno de los elementos del vector.

Por ejemplo, la función sqrt() tiene un sólo argumento, admite como valor de su único argumento un vector de números12. Se aplicará la función a cada uno de los elementos del vector:

aa <- c(4, 9, 25)
sqrt(aa)
#> [1] 2 3 5

La vectorización de las funciones hace que R sea un lenguaje en la que no es muy frecuente el uso de for loops o bucles, ya que en realidad la vectorización implica que se está ejecutando un bucle interno, pues se aplica la función a cada uno de los elementos del vector.

No siempre, pero en general, en R se sustituyen los bucles for tanto por la vectorización como por el uso de una familia de funciones *apply o más recientemente con las funciones del paquete purrr.

Operaciones de comparación

Los operadores de comparación también están vectorizados. Si comparamos 2 vectores, las comparaciones se efectúan elemento a elemento

aa <- 1:3
bb <- 3:1

aa == bb
#> [1] FALSE  TRUE FALSE
aa >= bb
#> [1] FALSE  TRUE  TRUE
aa != bb
#> [1]  TRUE FALSE  TRUE

En las operaciones de comparación también aplica el recycling:

aa <- 1:3
bb <- 2

aa == bb
#> [1] FALSE  TRUE FALSE
aa >= bb
#> [1] FALSE  TRUE  TRUE
aa != bb
#> [1]  TRUE FALSE  TRUE

Operaciones de conjuntos

aa <- 1:4
bb <- c(1:2, 99)

union(aa, bb)            
#> [1]  1  2  3  4 99
intersect(aa, bb)
#> [1] 1 2

setdiff(aa, bb)       #- Elementos de aa que no están en bb
#> [1] 3 4
setequal(aa,bb)       #- chequea si dos vectores son iguales
#> [1] FALSE
setequal(1:4, 1:4)    #- chequea si dos vectores son iguales
#> [1] TRUE

Funciones útiles con vectores

Hay muchísimas funciones que se pueden utilizar con vectores, pero algunas que merece la pena conocer son13 :


  • length(): nos da el orden o número de elementos de un vector. Ya la vimos.
aa <- 1:4
length(aa)
#> [1] 4


  • sum(): devuelve la suma de los valores de un vector.
aa <- 1:4
sum(aa)
#> [1] 10

¿Qué pasaría si el vector fue de tipo texto o character?


  • cumsum(): devuelve un vector del mismo orden con la suma acumulada (hasta ese valor).
aa <- 1:4
cumsum(aa)
#> [1]  1  3  6 10


  • mean(): devuelve el valor de la media de los valores del vector.
aa <- 1:4
mean(aa)
#> [1] 2.5


  • order(): devuelve un vector con la posición que ocuparían los valores originales de un vector si se ordenasen de menor a mayor
aa <- c(2.1, 4.2, 3.3, 5.4)
order(aa)
#> [1] 1 3 2 4


  • sort(): devuelve un vector con los valores del vector original ordenados de menor a mayor
aa <- c(2.1, 4.2, 3.3, 5.4)
sort(aa)
#> [1] 2.1 3.3 4.2 5.4


  • El siguiente chunk utiliza order() y subsetting para lograr el mismo resultado que con sort()
x <- c(2.1, 4.2, 3.3, 5.4)
x[order(x)]
#> [1] 2.1 3.3 4.2 5.4


3. Matrices

Otra estructura para almacenar valores en R son las matrices. No las usaremos mucho, en realidad nada, pero conviene conocerlas un poco. R si que usa las matrices internamente por ejemplo para hacer análisis de regresión.

Al igual que los vectores, una matriz sólo puede contener elementos del mismo tipo, por eso decimos que es una estructura de datos de tipo homogénea.

(aa <- matrix(1:6, ncol = 2, nrow = 3)) #- creamos una matriz 3x2
#>      [,1] [,2]
#> [1,]    1    4
#> [2,]    2    5
#> [3,]    3    6

Las matrices también se pueden crear concatenando vectores:

aa <- c(1:3)
bb <- c(11:13)
cc <- cbind(aa, bb)  #- agrupamos los vectores por columnas con cbind()
dd <- rbind(aa, bb)  #- agrupamos los vectores por filas con rbinb()

Subsetting con matrices

Es muy parecido al subsetting en vectores, el operador que usaremos es el mismo, [ ], pero claro las matrices tienen dos dimensiones, usaremos [ , ]. Veámoslo:

aa <- matrix(1:9, nrow = 3, ncol = 3) #- creamos una matriz 3x3

bb <- aa[2, 3]          #- seleccionamos el elemento (2,3) de la matriz

bb <- aa[ , 2]          #- seleccionamos la segunda columna (devuelve un vector)
bb <- aa[ , c(2,3)]     #- seleccionamos la segunda y tercera columna
bb <- aa[c(2,3), ]      #- seleccionamos la segunda y tercera FILAS

bb <- aa[-2, ]          #- eliminamos la segunda fila
bb <- aa[-2, -c(1,2)]   #- eliminamos la segunda fila y la primera y segunda Columnas

Nombrar las filas y columnas

Al igual que los vectores, las matrices admiten atributos, entre ellos dar nombre a las filas y columnas

aa <- matrix(1:6, nrow = 2, ncol = 3) #- creamos una matriz 2x3
aa
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

colnames(aa) <- c("col_1", "col_2", "col_3")    
rownames(aa) <- paste("fila", 1:2, sep = "_")    
aa
#>        col_1 col_2 col_3
#> fila_1     1     3     5
#> fila_2     2     4     6

Funciones para matrices

Hay muchas, por ejemplo:

aa <- matrix(1:6, nrow = 3, ncol = 2)  #- matriz 3x2
dim(aa)  #- devuelve un vector con las dimensiones de la matriz
t(aa)    #- transpone la matriz

Por supuesto se pueden multiplicar matrices, calcular inversas etc etc…


4. Arrays

Otra estructura para almacenar valores en R son los arrays. Son matrices multidimensionales. No las usaremos mucho, en realidad nada. También es una estructura de datos homogénea; es decir, sus elementos tienen que ser todos del mismo tipo

(aa <- array(1:12, c(2, 3, 2)) )
#> , , 1
#> 
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
#> 
#> , , 2
#> 
#>      [,1] [,2] [,3]
#> [1,]    7    9   11
#> [2,]    8   10   12


Podéis imaginar que si tuviésemos que hacer subsetting de un array, tendríamos que utilizar [ , , ]. Por ejemplo

aa[1, 2 , 1]
#> [1] 3


5. Factores

Podemos pensar que los factores son otro tipo de estructura para almacenar datos, aunque en realidad son vectores, pero vectores de un tipo especial. Veamos qué es un factor, para qué se utilizan, cómo puedo crearlos, etc…

Pues ya he dicho que los factores en realidad SON VECTORES, pero tienen un atributo que les hace comportarse de manera un poco especial.

Los creadores de R, crearon este tipo especial de vector llamado factor para almacenar información sobre variables categóricas; por ejemplo el color de ojos, o los días de la semana, los meses del año, el género etc…

En principio los factores sirven para representar variables categóricas: las categorías o valores que puede tomar la variable deberían ser fijas y conocidas. Por ejemplo, los días de la semana, que es una variable categórica con 7 categorías o grupos.

Mi opinión personal, puede que un poco extrema, es que los factores suelen dar problemas al manipular los datos, mejor evitarlos. Son útiles, sin embargo, al hacer gráficos (permiten ordenar las categorías para mostrarlas de la forma más conveniente) y cuando se trabaja con modelos estadísticos, ya que los factores se usan para representar variables categóricas, y las variables categóricas se incorporan, generalmente, en los modelos estadísticos mediante variables dummy. Si tienes tus variables categóricas almacenadas en factores las funciones relacionadas con los modelos, por ejemplo lm() tratarán la información de forma adecuada y generarán las dummies de forma automática en el modelo. Es muy fácil cometer errores al recodificar variables cuando codificas las variables categóricas como numéricas, así que mejor tenerlas como factores y que lo haga R por nosotros. Por ejemplo:



En mi opinión, otra vez un poco extrema, el tidyverse comenzó como una lucha contra string.as.factor = TRUE14. Afortunadamente, la próxima versión de R, la versión 4.0, tendrá la opción string.as.factor fijada a FALSE por defecto. El anuncio oficial se hizo en el post al que apunta este tweet:


Ya dije que los factores se usan para representar variables categóricas y que pueden ser útiles cuando trabajas con gráficos, tablas y/o modelos estadísticos pero también son un poco raros/especiales15.

Si tienes que usar factores, es mejor que lo hagas con el package forcats. No lo he usado, pero el paquete questionr tiene un adding para recodificar variables, entre ellos factores, en R.

Recodificar variables es una tarea muy común en análisis de datos y, a veces, suele ser fuente de errores. Por ejemplo, en este post nos cuentan un error en la recodificación de una variable que llevo a que un artículo tuviera que ser retirado. No se si el error de codificación llevó a errores en la vida real, pero el artículo analizaba tratamientos en pacientes hospitalizados.

Creación de factores

Por ejemplo, podemos crear un vector de tipo character con 5 observaciones

aa <- c("lunes", "martes", "jueves", "lunes", "martes")
aa
#> [1] "lunes"  "martes" "jueves" "lunes"  "martes"

De momento aa es un vector de texto. Si quisiéramos convertirlo a factor podríamos hacer lo siguiente:

aa_factor <- as.factor(aa)
is.factor(aa_factor)
#> [1] TRUE

Sí, efectivamente, ahora aa_factor es un vector, sí, pero es un vector especial, es un un factor. Vamos a imprimirlo para ver si ha cambiado algo.

aa_factor
#> [1] lunes  martes jueves lunes  martes
#> Levels: jueves lunes martes

Sí ahora al imprimir aa_factor, como es un factor, R nos ofrece más información. Un factor es un vector con atributos (con más información); en concreto un factor necesariamente tiene un atributo que son los levels, que nos indica cuantos grupos o categorías tiene el factor y cómo se llaman las categorías. En nuestro caso aa_factor tiene 3 levels o categorías.

levels(aa_factor)
#> [1] "jueves" "lunes"  "martes"

Vuelvo a repetirlo. aa_factor es un factor, OK. Como es un factor, resulta que es un vector con un atributo especial, los levels (los grupos o categorías). En nuestro caso, aa_factor es un vector con 5 observaciones y 3 grupos/categorias/levels. No es tan complicado.

En realidad, los factores sí son un poco más raros de lo que he explicado. Por razones históricas, se trataba de ahorrar memoria en el ordenador, los levels (o niveles )se utilizan para mostrar la información, PERO internamente la información se almacena con un vector de enteros y eso a veces genera (o generaba) cosas digamos extrañas. No importa mucho si no lo entiendes del todo. Fíjate, los factores se muestran como texto pero en realidad internamente son enteros:

aa_factor
#> [1] lunes  martes jueves lunes  martes
#> Levels: jueves lunes martes
typeof(aa_factor)     #- internamente lo almacena como enteros !!
#> [1] "integer"


El orden de los levels

El orden de los levels es importante, principalmente al hacer gráficos y tablas. Por defecto, R ordena los levels de forma alfabética y esto no siempre es adecuado. Veamos cual es el orden de los niveles en aa_factor

levels(aa_factor)
#> [1] "jueves" "lunes"  "martes"

Como por defecto los niveles se ordenan alfabéticamente, en nuestro aa_factor los

PERO hay funciones para reordenar los levels, por ejemplo:

relevel(aa_factor, "martes")
#> [1] lunes  martes jueves lunes  martes
#> Levels: martes jueves lunes

También podemos crear el factor directamente:

aa_factor_2 <- factor(aa, levels = c("lunes", "martes", "jueves"))
aa_factor_2
#> [1] lunes  martes jueves lunes  martes
#> Levels: lunes martes jueves

Aquí el orden de los levels lo hemos elegido nosotros. No hemos usado el orden alfabético sino el orden habitual en los días de la semana.

Ya dije que un factor es un vector con atributos. Veámoslo:

aa <- 1:10
attributes(aa)
#> NULL

aa_factor_2 <- factor(aa, levels = c("lunes", "martes", "jueves"))
attributes(aa_factor_2)
#> $levels
#> [1] "lunes"  "martes" "jueves"
#> 
#> $class
#> [1] "factor"

El objeto aa_factor_2 es un factor de R, aunque en el fondo no es más que un vector con atributos: los “levels” y “class”

Más cosas sobre factores

Durante el curso trabajaremos con variables categóricas y, por tanto, usaremos factores. Para ello utilizaremos el paquete forcats. Si lo necesitáis seguro que encontráis tutoriales o cursos en la web, pero aquí tenéis uno de ellos; aunque quizás sea mejor acudir a la web de forcats.

Un buen resumen visual de qué son los factores es el siguiente mapa conceptual de este repo:

Creado por Ildiko Czeller y Graham Parsons

Creado por Ildiko Czeller y Graham Parsons


6. Listas

Esta estructura de datos, aunque en realidad se construye por la agrupación de vectores, sí que se comporta de forma diferente a los vectores16. Las listas son una estructura de datos heterogénea: sí que pueden contener elementos de distinto tipo.

La mejor forma de entender como funciona una lista es pensar que es como una cajonera. En cada cajón podemos poner diferentes cosas, encada cajón podemos poner objetos de diferentes tipos.

Para definir una lista se hace uso de la función list(). Creemos nuestra primera lista:

# defino 3 vectores y una matriz
vec_numerico <- c(1:8)
vec_character <- c("Pedro", "Rebeca", "Susana")
vec_logic <- c(TRUE, FALSE, TRUE)
matriz <- matrix(1:6, nrow = 2, ncol = 3)

# creo una lista con cuatro elementos
my_list <- list(vec_numerico, vec_character, vec_logic, matriz)
my_list
#> [[1]]
#> [1] 1 2 3 4 5 6 7 8
#> 
#> [[2]]
#> [1] "Pedro"  "Rebeca" "Susana"
#> 
#> [[3]]
#> [1]  TRUE FALSE  TRUE
#> 
#> [[4]]
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

Podemos crear la misma lista pero añadiendo nombres a los distintos elementos. Puede sernos útil a la hora de hacer subsetting en una lista.

my_list <- list(slot_1 = vec_numerico, slot_2 = vec_character, slot_3 = vec_logic, slot_4 =matriz)
my_list
#> $slot_1
#> [1] 1 2 3 4 5 6 7 8
#> 
#> $slot_2
#> [1] "Pedro"  "Rebeca" "Susana"
#> 
#> $slot_3
#> [1]  TRUE FALSE  TRUE
#> 
#> $slot_4
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

Las listas no tienen dimensiones tienen length()

length(my_list) #- nuestra lista tiene 4 slots, 4 elementos
#> [1] 4

Las listas son sumamente flexibles porque un elemento de una lista puede ser otra lista.

my_list_2 <- list(primer_slot = c(44:47), segundo_slot = my_list)
my_list_2
#> $primer_slot
#> [1] 44 45 46 47
#> 
#> $segundo_slot
#> $segundo_slot$slot_1
#> [1] 1 2 3 4 5 6 7 8
#> 
#> $segundo_slot$slot_2
#> [1] "Pedro"  "Rebeca" "Susana"
#> 
#> $segundo_slot$slot_3
#> [1]  TRUE FALSE  TRUE
#> 
#> $segundo_slot$slot_4
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

Subsetting en listas

Como las listas son más complejas, tenemos más posibilidades para hacer subsetting.

Tendremos 3 operadores para hacer subsetting: [, [[ y $.

  • Empecemos con [. Si hacemos subsetting con [ el objeto devuelto siempre será otra lista.
my_list[2]  #- seleccionamos el segundo elemento de la lista. Nos devuelve una lista con un solo slot
#> $slot_2
#> [1] "Pedro"  "Rebeca" "Susana"
my_list[c(2,3)]  #- seleccionamos el segundo y tercer elemento de la lista. Nos devuelve una lista con dos slots
#> $slot_2
#> [1] "Pedro"  "Rebeca" "Susana"
#> 
#> $slot_3
#> [1]  TRUE FALSE  TRUE
  • con [ también se puede seleccionar por nombre
my_list["slot_2"]  #- seleccionamos el segundo elemento de la lista. Nos devuelve una lista con un solo slot
#> $slot_2
#> [1] "Pedro"  "Rebeca" "Susana"
my_list[c("slot_2", "slot_3")]  #- seleccionamos el segundo y tercer elemento de la lista. Nos devuelve una lista con dos slots
#> $slot_2
#> [1] "Pedro"  "Rebeca" "Susana"
#> 
#> $slot_3
#> [1]  TRUE FALSE  TRUE
  • subsetting listas con [[. NO devuelve una lista, nos devuelve el objeto que hay en el slot seleccionado
my_list[[2]]  #- seleccionamos el segundo elemento de la lista. Nos devuelve el elemento que hay en el segundo slot; es decir un vector, vec_character
#> [1] "Pedro"  "Rebeca" "Susana"
  • subsetting, por nombre, con $. No devuelve una lista, sino un elemento de la lista
my_list$slot_4  #- seleccionamos el cuarto elemento de la lista, cuyo nombre es `slot_4`; es decir, nos devuelve una matriz.
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
  • incluso se puede usar [[ para hacer subsetting por nombre, si es que los elementos de la lista tienen nombre17
my_list[["slot_4"]]  #- seleccionamos el cuarto elemento de la lista, cuyo nombre es `slot_4`; es decir, nos devuelve una matriz.
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

Las listas se usan para muchas cosas; por ejemplo, para almacenar los resultados de la estimación de un modelo , etc…

Las listas son sumamente flexibles porque un elemento de una lista puede ser otra lista.

my_list_2 <- list(primer_slot = c(44:47), segundo_slot = my_list)
my_list_2
#> $primer_slot
#> [1] 44 45 46 47
#> 
#> $segundo_slot
#> $segundo_slot$slot_1
#> [1] 1 2 3 4 5 6 7 8
#> 
#> $segundo_slot$slot_2
#> [1] "Pedro"  "Rebeca" "Susana"
#> 
#> $segundo_slot$slot_3
#> [1]  TRUE FALSE  TRUE
#> 
#> $segundo_slot$slot_4
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

¿Cómo podemos seleccionar la matriz que hay en my_list_2? Pues la matriz es uno de los cuatro elementos que hay en el segundo slot de my_list_2. Con my_lis_2[[2]] seleccionamos el segundo_slot, que es una lista (my_list), y en el cuarto slot de my_list esta la matriz. Así que podríamos hacerlo de varias maneras:

my_list_2[[2]][[4]]
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
my_list_2$segundo_slot$slot_4
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

Para terminar con las listas, aquí se explica visualmente la diferencia entre hacer subsetting con [ y hacerlo con [[. Además se critica un poco a XML, un lenguaje de marcado que muchas veces se utiliza para almacenar e intercambiar datos.


7. DATA FRAMES

A data frame is really a list where each element of the list is a single column. In other words, a data frame is a vector of columns

Los data.frames son otra estructura de R para almacenar datos. Es la estructura que más utilizaremos. Se utiliza para almacenar datos tabulares, tablas de datos, que son el formato de datos al que más acostumbrados estamos, son tablas como las que vemos en Excel etc …

En realidad un data.frame es una lista, pero una lista especial. Al ser una lista puede almacenar elementos de distinto tipo (numéricos, carácter etc…). ¿Por qué es una lista especial? Pues porque podemos pensar que es una lista restringida, restringida a que sus elementos/slots tienen que tener la misma longitud.

Como en cada slot de la lista hay un vector y todos los vectores tienen la misma longitud al final tenemos una tabla de datos. Esos vectores con la misma longitud se almacenan por columnas, así que la longitud de los vectores son el número de filas de la tabla. Habitualmente los data.frames se utilizan para almacenar datos tabulares, donde cada columna es una variable.

Otra propiedad de los data.frames es que los vectores o columnas que lo forman tienen que tener un nombre. Como habitualmente en cada columna del data.frame se almacenan los valores de una variable, el nombre de la columnas se corresponderá con el nombre de la variable.

Generalmente no crearemos data.frames sino que los importaremos de repositorios de datos, por ejemplo de Eurostat o del INE, pero hoy vamos a crear uno con la función data.frame()

Además, en general, en lugar de usar data.frames usaremos tibbles o data.frames tuneados à la tidyverse. Lo veremos.

aa <- c(4, 8, 6, 3)
bb <- c("Juan", "Paz", "Adrian", "Marquitos")
cc <- c(FALSE, TRUE, TRUE, FALSE)
df <- data.frame(aa, bb, cc)
df
aa bb cc
4 Juan FALSE
8 Paz TRUE
6 Adrian TRUE
3 Marquitos FALSE

En nuestro df tenemos 3 variables, cada una de ellas con 4 observaciones.

Podemos ponerle otros nombres a las columnas, nombres que tengan sentido y que nos recuerden las variables que contienen:

df <- data.frame(Nota = aa, Nombre = bb, Aprobado = cc)
names(df)  #- con names() podemos ver los nombres de las variables del df
#> [1] "Nota"     "Nombre"   "Aprobado"

Podemos cambiar los nombres de las columnas con names() o de más formas, como con rlang::set_names() o con magrittr::set_colnames() como se muestra aquí.

(names(df) <- c("Grade", "Name", "Pass"))
#> [1] "Grade" "Name"  "Pass"

Subsetting en data.frames

La verdad es que cuando tengamos que hacer subsetting en un data.frame, lo haremos à la tidyverse, pero conviene al menos conocer lo básico de como hacer subsetting con R-base.

El subsetting en data.frame à la R-base es muy una mezcla entre el subsetting para matrices y para listas.

  1. Subsetting como si fuera una matriz. Con [.
df_s <- df[,1]       #- seleccionamos la primera columna. devuelve un vector !!!
df_s <- df[,c(2,3)]  #- seleccionamos la segunda y tercera columna. devuelve un df
df_s <- df[1, ]      #- seleccionamos primera fila de todas las variables. devuelve un df. ¿xq no devuelve un vector? Preguntad si no lo sabéis
df_s <- df[c(1,4), ]  #- seleccionamos primera y cuarta fila. devuelve un df
df_s <- df[2, 3]      #- seleccionamos segunda observación de la tercera variable. Devuelve un vector.
  1. Subsetting como si fuera una lista. De hecho un df es una clase especial de lista. Podemos usar 3 operadores: [, [[ y $.
  • subsetting con [ como si fuera una lista
df_s <- df[3]        #- devuelve un df. Good!!
df_s <- df[c(1,2)]

#- también se puede hacer por nombre
df_s <- df["Name"]                #- devuelve un df
df_s <- df[c("Name", "Grade")]   
  • subsetting con [[ como si fuera una lista
df_s <- df[[2]]   #- Extraemos la segunda columna. Devuelve un vector, concretamente un factor. Ahhhh!!!!!
  • subsetting con $. Como si fuera una lista (por nombre de los elementos, en este caso los elementos de la lista son las columnas o variables del df)
df_s <- df$Name   #- Extraemos la columna con nombre "Name". Devuelve un vector, concretamente un factor. Ahhhh!!!!!
df_s <- df$Grade  #- Extraemos la columna con nombre "Grade". Devuelve un vector numérico

Hay más formas de hacer subsetting con data.frames, por ejemplo con subset(), pero ya he dicho que el manejo de tablas o data.frames lo haremos principalmente con funciones del tidyverse, principalmente funciones del paquete dplyr. Lo veremos en otro tutorial.

  • con [ podemos, al igual que en los vectores y matrices, hacer subsetting lógico:
df_s <- df[df$Grade >= 5, ]   #- selecciono filas que cumplan que el valor de la columna grade sea >= a 5
df_s <- df[!df$Grade >= 5, ]  #- los que NO han sacado igual o mas  de 5
df_s <- df[df$Grade == 8 | df$Grade < 5, ]  #- los que han sacado exactamente 8 ó menos de 5

Funciones útiles

Hay muchas, por ejemplo:

names(df)    #- devuelve un vector con los nombre de las columnas/variables del df
#> [1] "Grade" "Name"  "Pass"
nrow(df)     #- devuelve el nº de filas
#> [1] 4
ncol(df)     #- nº de columnas
#> [1] 3
length(df)   #- la longitud de las columnas/vectores que componen el df; osea devuelve otra vez el nº de filas
#> [1] 3

Podemos añadir columnas a nuestro df:

aa <- 101:104
df$new_v <- aa

También así:

df_new <- cbind(df, aa)

Con la función as.data.frame(), podemos convertir un vector o una matriz, e incluso algunas listas, en data.frames.

aa <- 1:5
df_new <- as.data.frame(aa)  #- df con una sola columna

Un resumen del dataframe con summary()

summary(df)
#>      Grade          Name              Pass             new_v      
#>  Min.   :3.00   Length:4           Mode :logical   Min.   :101.0  
#>  1st Qu.:3.75   Class :character   FALSE:2         1st Qu.:101.8  
#>  Median :5.00   Mode  :character   TRUE :2         Median :102.5  
#>  Mean   :5.25                                      Mean   :102.5  
#>  3rd Qu.:6.50                                      3rd Qu.:103.2  
#>  Max.   :8.00                                      Max.   :104.0

Y muchas más.

8. Paquetes (packages)

Ya sabemos que R tiene muchos paquetes, ya hemos utilizado alguno. ¿Que qué son los paquetes? Pues son colecciones/porciones de código R que podemos instalar en nuestro ordenador y después cargar en nuestra sesión. ¿Qué ganamos con ello? Los paquetes nos proveen de nuevas funciones o nuevos datos; es decir incrementa la funcionalidad de R: R podrá hacer más cosas. Es como instalar una nueva app en tu teléfono.

Imagina que quieres usar R para analizar datos de Eurostat. En ese caso podemos ir directamente a la web de Eurostat, pero es mucho más cómodo instalarnos el paquete eurostat que contiene una serie de funciones que nos facilitan el uso de datos de Eurostat en R; o a lo mejor quieres analizar datos relacionados con la pandemia covid-19. Podrías tratar de recopilar datos por ti mismo pero existen unos cuantos paquetes que ya lo han hecho, por ejemplo, para datos españoles, el paquete escovid19data.

Para poder usar un paquete ya sabéis que hay que hacer 2 cosas:

  1. Instalar el package en tu ordenador. Sólo hace falta hacerlo una vez. Generalmente con install.packages("my_pkg")

  2. Cargar el paquete en memoria. Cada vez que vayas a usar un paquete tendrás que abrirlo, tendrás que hacer accesible el paquete a la memoria de R con library(“my_pkg”).


Instalación de paquetes

Ya sabes que hay un repositorio oficial de paquetes, CRAN; pero hay muchos otros repositorios, uno de los más famosos y utilizados es Github.

Para instalar un paquete de CRAN tienes que usar install.packages().

Para instalar paquetes de otros repositorios se pueden utilizar varios paquetes, pero yo últimamente estoy usando el paquete remotes. El paquete remotes permite instalar paquetes alojados en GitHub, GitLab, Bitbucket, etc…

Por ejemplo para instalar un paquete de Github se utiliza: remotes::install_github("usuario/package"). Por ejemplo, para instalar el paquete emo del usuario hadley se haría lo siguiente: remotes::install_github("hadley/emo").


Obtener información sobre un pkg

Instalar un paquete es sencillo, pero para usarlo, generalmente, tendrás que acceder a la documentación del paquete. Los paquetes tienen mucha información sobre como usar sus funciones, sólo hay que saber buscarla.

Veamos un ejemplo concreto. Supongamos que queremos usar el paquete eurostat. Para conocer que funcionalidades tiene este paquete y cómo usar sus funciones tenemos varias alternativas:

  1. Si el paquete está en CRAN, pues ir a su página web en CRAN. El paquete. eursotat sí está alojado en CRAN, concretamente aquí, y hacía el final de esa página está la documentación. Todos los packages en CRAN tienen un Reference Manual: un pdf con documentación extensa de cada una de las funciones del pkg. Habitualmente los packages también tienen unos documentos llamados vignettes que explican de forma más genérica para qué sirve y cómo se usa el paquete.

  2. Además de en CRAN, es muy habitual que los paquetes estén alojados en otros repositorios, generalmente en Github. eurostat está alojado en Github aquí

  3. Cada vez es más frecuente que los paquetes tengan su propia página web donde ese explica detalladamente las funcionalidades del paquete. Si un paquete tiene página web, visitarla suele ser la mejor opción para aprender como usarlo. Por ejemplo, la página web del pkg eurostat está aquí.

Información interna sobre un pkg

Además de poder buscar ayuda sobre un package en la web, se puede acceder a la documentación de un paquete , tanto al reference manual como a las vignettes, directamente desde R/RStudio. Tienes que hacer lo siguiente:

#- abrimos en RStudio la ayuda del pkg eurostat
help(package = eurostat)

Además, recordad que ya sabemos que cada función tiene su propia ayuda interna. Puedes acceder a ella con help(nombre_de_la_funcion) o situando el cursor en el nombre de la función y pulsar la tecla F1


Obtener un listado de las funciones de un pkg

Hay varias formas, pero esta funciona:

library(tidyverse)
aa <- as.data.frame(ls("package:readr", all = TRUE))


¿Cómo saber a que package pertenece una función?

A veces es conveniente saber a que package pertenece una función. Podemos hacerlo con:

#- usamos la función"find" para encontrar en que package está la función "mean"
find("mean")



9. Más cosas a conocer

En esta sección presentaré algunas ideas y tópicos que creo pueden seros de utilidad.

Algunas funciones útiles

  • %in%: retorna un vector de booleans (TRUE or FALSE) para cada uno de los valores del vector a la izquierda dependiendo de si el valor se encuentra o no en el vector de la derecha.
?`%in%`  #- para ver la ayuda del operador
1:10 %in% c(1,3,5,9)
#>  [1]  TRUE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE
  • str(): muestra la estructura de un objeto
str(iris)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...



Directorio de trabajo

Cuando trabajamos con R es importante conocer la noción de directorio de trabajo. Es la carpeta donde R busca por defecto cuando le pidas que abra unos datos o donde los grabará.Muchas veces tendremos que importar o exportar datos así que tenemos que conocer cual es nuestro directorio de trabajo y cómo podemos cambiarlo.

  • Para conocer el directorio de trabajo actual usa la función getwd()

  • Si quisiéramos cambiar el directorio de trabajo podemos hacerlo con la función setwd()

Nosotros trabajaremos con Rprojects y el paquete here así que generalmente no necesitaremos estas funciones pero conviene conocerlas.

Cuando trabajas con Rprojects, el directorio de trabajo es la propia carpeta del proyecto. La ventaja de esto es que en lugar de rutas absolutas podemos usar rutas relativas y esto facilita la reproducibilidad de nuestros análisis.

Además de trabajar con proyectos usaremos, al meno yo, la función here() del paquete here. La razón es que quiero poder knitear los .Rmd desde cualquier carpeta del proyecto.


NA’s y valores especiales

Recordáis que en una sección anterior dijimos que los principales tipos de datos son 3: numéricos, de texto y lógicos, pero que existían unos valores especiales: NULL, NA, NaN e Inf. Veámoslos un poco:

  • NULL: representa el objeto vacío en R. No se propaga.
c()
#> NULL
sum(2, 2, NULL)
#> [1] 4
mean(c(2 ,4, NULL))
#> [1] 3
  • NA: Un valor no disponible. Sí se propaga, así que cuando tengamos que calcular medias etc… con vectores que contengan NAs hay que tener cuidado, habrá que usar la opción na.rm = TRUE.
sum(2, 2, NA)
#> [1] NA
mean(c(2 ,4, NA))
#> [1] NA
mean(c(2, 4, NA), na.rm = TRUE)
#> [1] 3
  • NaN: Not a number. Se propaga
0/0
#> [1] NaN
sum(2, 2, NaN)
#> [1] NaN
mean(c(2 ,4, 0/0))
#> [1] NaN
  • Infinito:
1/0
#> [1] Inf
-1/0
#> [1] -Inf
sum(2, 2, Inf)
#> [1] Inf

Secuencias

Muchas veces hay que crear secuencias de números. Para ello disponemos de las funciones :, seq() y rep(). Por ejemplo:

  • con : el incremento siempre es unitario. Devuelve enteros.
1:3
#> [1] 1 2 3
c(1:4)
#> [1] 1 2 3 4
-2:3
#> [1] -2 -1  0  1  2  3
4:-1
#> [1]  4  3  2  1  0 -1
  • seq()
seq(from = 0, to = 5, by = 1)
#> [1] 0 1 2 3 4 5
seq(-5, 5, 2)
#> [1] -5 -3 -1  1  3  5

seq(0, 1, length.out = 3)
#> [1] 0.0 0.5 1.0
seq(0, 1, length.out = 5)
#> [1] 0.00 0.25 0.50 0.75 1.00
seq(0, 1, 5)
#> [1] 0
  • rep()
rep(2, times= 3)
#> [1] 2 2 2
rep(1:3, 2)
#> [1] 1 2 3 1 2 3
rep(1:3, each = 2)       # not the same.
#> [1] 1 1 2 2 3 3
rep(1:3, times = 3, each = 2)
#>  [1] 1 1 2 2 3 3 1 1 2 2 3 3 1 1 2 2 3 3
rep(1:2, length.out = 9)
#> [1] 1 2 1 2 1 2 1 2 1


10. Algunos chunks

  1. Eliminar todos los objetos del Global Environment EXCEPTO los que tú decidas.

Para eliminar un objeto de la memoria puedes usar la función rm(). Para quitar un objeto solo has de hacer rm(objeto), pero si quieres eliminar todos los objetos excepto los que necesites:

aa <- 1
bb <- 2
cc <- 3
dd <- 4
quiero_dejar_estos <- c("cc", "dd")                #- fíjate que los nombres de los objetos están entre comillas
rm(list = ls()[!(ls() %in% quiero_dejar_estos)])   #- remueve todo excepto quiero_dejar_estos
  1. Función para comparar los elementos de dos vectores.

Inspirados por este tweet vamos a crear nuestra primera función:

my_f_compare <- function(x, y){
  list(
  "Valores de X q. no están en Y:" = setdiff(x, y),
  "Valores de Y q. no están en X:" = setdiff(y, x),
  "Valores comunes en X e Y:" = intersect(x, y),
  "La unión de X e Y sería:" = union(x,y), 
  "Si juntamos X e Y obtenemos:" = c(x, y) )
}

Trata de entenderlo Paul

XX <- c(1:4)
YY <- c(3:6)
aa <- my_f_compare(XX, YY)
names(aa)
#> [1] "Valores de X q. no están en Y:" "Valores de Y q. no están en X:"
#> [3] "Valores comunes en X e Y:"      "La unión de X e Y sería:"      
#> [5] "Si juntamos X e Y obtenemos:"
aa
#> $`Valores de X q. no están en Y:`
#> [1] 1 2
#> 
#> $`Valores de Y q. no están en X:`
#> [1] 5 6
#> 
#> $`Valores comunes en X e Y:`
#> [1] 3 4
#> 
#> $`La unión de X e Y sería:`
#> [1] 1 2 3 4 5 6
#> 
#> $`Si juntamos X e Y obtenemos:`
#> [1] 1 2 3 4 3 4 5 6


11. R 4.1.0

Con la llegada de R 4.1.0 se produjo un cambio importante en la comunidad R: apareció la pipe nativa >|18. Una comparación exhaustiva de la pipe nativa con la pipe de magrittr puede encontrarse aquí

La pipe original ( %>% ), la de Stefan Bache en el paquete magrittr, es un operador fundamental para el tidyverse que ha cambiado la forma de escribir código en R. ¿Sustituirá la pipe nativa ( |> ) a la original ( %>% ), ¿lo veremos?

mtcars |> group_by(cyl) |> summarise(mpg = mean(mpg))

Una ventaja evidente del pipe nativa es que es nativa, se puede usar sin tener que instalar ni cargar ningún paquete, menos dependencias!!; además, solo está compuesta de 2 caracteres frente a los 3 de la original.

Una desventaja de la nativa es que no puede usar . como placeholder para llevar el objeto de la izquierda a, por ejemplo, el segundo o tercer argumento de la función de la derecha. Por ejemplo, no se puede hacer esto con la pipe nativa:

2 %>% head(iris, n = .)

La forma de solucionarlo es usar funciones anónimas (un poco feo!!):

2 |> (function(x) head(iris, n = x)

Lo anterior mejora un poco (la verdad es que bien poco) si usamos la nueva sintaxis (shorthand) para funciones anónimas o lambda functions19:

#- estas dos lineas producen idénticos resultados
function(x) x + 1 
\(x) x + 1
2 |> (\(x) head(iris, x = n))()

Aunque en este tweet se dice que si puedes llevar el objeto de la izquierda a un argumento distinto del primero, si los anteriores argumentos tienen puesto el nombre, ya que “The pipe pipes into the first unnamed argument, I guess.”

Biblio


  1. De momento no estoy empleando la terminología correcta↩︎

  2. También se puede utilizar el operador = pero, en general, la comunidad R usa <-.↩︎

  3. En realidad son 6 porque R tiene también complex y raw types↩︎

  4. Sin embargo, si elevas un NA a cero, el resultado sera 1, porque todo número elevado a cero es 1. De la misma manera si ejecutas NA | TRUE devolverá TRUE↩︎

  5. R no entiende el concepto de escalar. Entre sus estructuras de datos no están los escalares. para R el número 9 es en realidad un vector con un solo elemento con valor 9↩︎

  6. Un array es muy similar a matrix, sólo que una matriz solo puede tener 2 dimensiones, filas y columnas, mientras que un array puede tener más de 2 dimensiones; es decir, las array son matrices mutidimensionales↩︎

  7. Aquí puedes consultar la especificación oficial del lenguaje R: https://cran.r-project.org/doc/manuals/r-release/R-lang.html↩︎

  8. De hecho en R no existen los escalares: para R un núnico numero es un vector de 1 elemento↩︎

  9. En realidad 6 si incluimos complex y raw types↩︎

  10. Augmented porque se construyen, a partir los vectores fundamentales, añadiendo información extra en los atributos del vector original o fundamental!! Si cuesta un poco entenderlo!! Con tener una ligera idea es suficiente↩︎

  11. Lo importante a saber de un factor es qué se puede hacer con él, lo veremos!!, y no tanto saber que un factor es realidad un vector de integers pero que como tiene un atributo que dice que su class es factor entonces se comporta de forma especial ante algunas funciones.↩︎

  12. de hecho cuando hacíamos sqrt(9) también introducíamos un vector porque, para R, 9 es un vector de un sólo elemento↩︎

  13. Esta sección se me ocurrió introducirla tras leer el libro https://smac-group.github.io/ds/data.html#useful-functions-with-vectors↩︎

  14. Esta historia os la contaré en clase↩︎

  15. Se usan para representar variables categóricas, pero internamente la información se almacena en un vector de enteros, PERO tienen también un vector de caracteres o levels que se utiliza para mostrar la información. Buff, lo explicaré en clase↩︎

  16. Aunque en realidad las listas se construye por la agrupación recursiva de vectores.↩︎

  17. Hadley nos avisa que esta forma de hacer subsetting por nombre es mejor porque $ hace partial matching.↩︎

  18. Como se dice aquí, la versión 4.0 ya vino con un cambio histórico: se acabo con stringAsFactors = TRUE. Además el post sugiere una solución para la falta de placeholder en la pipe nativa↩︎

  19. Aquí un hilo de Twitter sobre las lambda functions↩︎

LS0tCnRpdGxlOiAiSW5pY2lhY2nDs24gYSBSLWJhc2UiCmF1dGhvcjogIlBlZHJvIEouIFDDqXJleiAocGVkcm8uai5wZXJlekB1di5lcykuIFVuaXZlcnNpdGF0IGRlIFZhbMOobmNpYSA8YnI+IDxicj4gV2ViIGRlbCBjdXJzbzogPGh0dHBzOi8vcGVyZXpwNDQuZ2l0aHViLmlvL2ludHJvLWRzLTIyLTIzLXdlYi8+IgpkYXRlOiAiQWdvc3RvIGRlIDIwMTkgKGFjdHVhbGl6YWRvIGVsIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVknKWApIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNzczogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgInN0eWxlc19wanAuY3NzIikKICAgIHRoZW1lOiBwYXBlcgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBpbmNsdWRlczoKICAgICAgYWZ0ZXJfYm9keTogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgImZvb3Rlci5odG1sIikgCiAgICAgIGluX2hlYWRlcjogCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZ29vZ2xlLWFuYWx5dGljcy5odG1sIikgCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZmF2aWNvbi1zb2wuaHRtbCIpCiAgICBkZl9wcmludDoga2FibGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7ciBjaHVuay1zZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICNyZXN1bHRzID0gImhvbGQiLAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBGQUxTRSwgY2FjaGUucGF0aCA9ICIvY2FjaGVzLyIsIGNvbW1lbnQgPSAiIz4iLAogICAgICAgICAgICAgICAgICAgICAgI2ZpZy53aWR0aCA9IDcsICNmaWcuaGVpZ2h0PSA3LAogICAgICAgICAgICAgICAgICAgICAgI291dC53aWR0aCA9IDcsIG91dC5oZWlnaHQgPSA3LAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCAgZmlnLnNob3cgPSAiaG9sZCIsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYXNwID0gNy85LCBvdXQud2lkdGggPSAiNjAlIiwgZmlnLmFsaWduID0gImNlbnRlciIpCgojLSBwYXJhIG1lam9yYXIgbG9zIGdyw6FmaWNvcywgYnVlbm8gZW4gcmVhbGlkYWQgcGFyYSBxdWUgc2UgdmVhbiBpZ3VhbCBlbiBkaXN0aW50b3MgU08KIy0gaHR0cHM6Ly93d3cuanVtcGluZ3JpdmVycy5jb20vYmxvZy9yLWtuaXRyLW1hcmtkb3duLXBuZy1wZGYtZ3JhcGhpY3MvCmtuaXRyOjpvcHRzX2NodW5rJHNldChkZXYgPSAicG5nIiwgZGV2LmFyZ3MgPSBsaXN0KHR5cGUgPSAiY2Fpcm8tcG5nIikpCmBgYAoKYGBge3Igb3B0aW9ucy1zZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgIy0gcGFyYSBxdWl0YXIgbGEgbm90YWNpw7NuIGNpZW50w61maWNhCm9wdGlvbnMoInlhbWwuZXZhbC5leHByIiA9IFRSVUUpICMtIGh0dHBzOi8vZ2l0aHViLmNvbS92aWtpbmcvci15YW1sL2lzc3Vlcy80NyAgKGxvIHB1c2UgeCBlbCBwYiBjb24gZWwgd2FybmluZykgRW4gcmVhbGlkYWQgY3JlbyBxdWUgbWVqb3Igc2Vyw61hIHBvbmVybG8gZW4gUlByb2ZpbGUKYGBgCgpgYGB7ciBrbGlwcHksIGVjaG8gPSBGQUxTRX0Ka2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCJ0b3AiLCAicmlnaHQiKSkgIy0gcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInJsZXN1ci9rbGlwcHkiKQpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCkVsIG9iamV0aXZvIGRlIGVzdGUgdGVtYSBlcyBwcm9wb3JjaW9uYXIgdW5hIGludHJvZHVjY2nDs24gYSBSIHBhcmEgcGVyc29uYXMgcXVlIG5vIGhhbiB0ZW5pZG8gY29udGFjdG8gcHJldmlvIGNvbiBlbCBwcm9ncmFtYS4gU2UgcHJlc2VudGFuIGFsZ3Vub3MgZGUgbG9zIHJ1ZGltZW50b3MgZGVsIGxlbmd1YWplIGRlIHByb2dyYW1hY2nDs24gUi4gQ29ub2NlciBlbiBkZXRhbGxlIGVsIGxlbmd1YWplIFIgZXMgY29tcGxpY2FkbywgaGF5IG11Y2hhcyBjb3NhcywgcGVybyBjb25vY2llbmRvIHVuYXMgY3VhbnRhcyBkZSBlbGxhcyBwdWVkZSBzZXIgc3VmaWNpZW50ZSBwYXJhIGhhY2VyIGFuw6FsaXNpcyBkZSBkYXRvcyBjb21wbGVqb3MuCgoKU2UgcHJlc2VudGFuIGNvbmNlcHRvcyBiw6FzaWNvcyB5LCBwb3IgdGFudG8gbmVjZXNhcmlvcywgY29tbyBkaXN0aW50b3MgdGlwb3MgeSBlc3RydWN0dXJhcyBkZSBkYXRvcywgYXNpZ25hY2nDs24sIGZ1bmNpb25lcywgb3BlcmFkb3JlcyBsw7NnaWNvcywgc3Vic2V0dGluZyBldGMuLi4gUEVSTywgcmVjdWVyZGEgcXVlIGVuIGVsIGN1cnNvIHZhbW9zIGEgZW5mYXRpemFyIG90cmEgZm9ybWEgZGUgdHJhYmFqYXIgY29uIFIuIEVuIGVsIGN1cnNvIHByaW9yaXphbW9zIHRyYWJhamFyIGNvbiBSIHVzYW5kbyBwcmluY2lwYWxtZW50ZSBsb3MgcGFxdWV0ZXMgYXNvY2lhZG9zIGFsIHRpZHl2ZXJzZTsgZXMgZGVjaXIsIHZhbW9zIGEgdXNhciBSIMOgIGxhIHRpZHl2ZXJzZS4gRW50b25jZXMsIMK/cG9yIHF1w6kgdW4gdHV0b3JpYWwgc29icmUgUi1iYXNlPyBMYSByYXrDs24gZXMgc2VuY2lsbGEsIGxvcyBwYXF1ZXRlcyBkZWwgdGlkeXZlcnNlIG5vIHNvbiBvdHJvIGxlbmd1YWplLCBuZWNlc2l0YW4gYSBSIHBhcmEgZnVuY2lvbmFyLCBubyB0aWVuZW4gc2VudGlkbyBmdWVyYSBkZSDDqWwsIGFzw60gcXVlIGN1YW50byBtYXMgc2VwYXMgZGUgUiwgZGUgUi1iYXNlLCBtZWpvci4gQWwgbWVub3MgaGF5IHVuIG3DrW5pbW8gZGUgZWxlbWVudG9zIGRlIFItYmFzZSBxdWUgc2UgbmVjZXNpdGEgZW50ZW5kZXIuIEVuIGVsIHR1dG9yaWFsIGludGVudGFyw6kgY2VudHJhcm1lIGVuIGxvIHF1ZSBlcyBtw6FzIG5lY2VzYXJpby4gCgoKRXN0ZSB0dXRvcmlhbC90ZW1hIG5vIGxvIHZhbW9zIGEgdmVyIGVuIGNsYXNlIGVuIGRldGFsbGUuIEVuIGNsYXNlIGVtcGV6YXJlbW9zIGRpcmVjdGFtZW50ZSBhIHRyYWJhamFyIGNvbiBlamVtcGxvcyB5IGNhc29zIGNvbiBkYXRvcyByZWFsZXMgeSBjb24gYGRhdGEuZnJhbWVzYC4gwr9FbnRvbmNlcyBwYXJhIHF1w6kgZXN0ZSB0ZW1hL3R1dG9yaWFsPyBQdWVzIHBhcmEgcXVlIHNpcnZhIHVuIHBvY28gZGUgcmVmZXJlbmNpYSwgcG9jbyBhIHBvY28gaXJlbW9zIHZpZW5kbyB0cm9jaXRvcyBkZSBlc3RlIHRlbWE7IGFkZW3DoXMgdGVuw6lpcyBlbiBJbnRlcm5ldCBleGNlbGVudGVzIHJlY3Vyc29zIHBhcmEgYXByZW5kZXIgYSBwcm9ncmFtYXIgY29uIFIuIAoKCkVuIGxhcyByZXNwdWVzdGFzIGEgZXN0ZSB0d2VldCBwdWVkZXMgdmVyIGRpc3RpbnRhcyBvcGluaW9uZXMgc29icmUgc2kgZXMgbWVqb3IgY29tZW56YXIgYSBhcHJlbmRlciBSIGNvbiBlbCB0aWR5dmVyc2UgbyBjb24gUi1iYXNlOgoKYGBge3IsIGVjaG8gPSBGQUxTRX0KdHdlZXRybWQ6OnR3ZWV0X2VtYmVkKCJodHRwczovL3R3aXR0ZXIuY29tL2thaWphX2JlYW4vc3RhdHVzLzEyMTcyOTMzOTY3MDYwNTQxNDUiLCB0aGVtZSA9ICJsaWdodCIsIGFsaWduID0gImNlbnRlciIsIGRudCA9IFRSVUUsIHBsYWluID0gVFJVRSwgbWF4d2lkdGggPSA0MDApCmBgYAoKCgoKU3Vwb25nbyBxdWUgeWEgdGVuw6lpcyBpbnN0YWxhZG8gUiB5IFJTdHVkaW8gZW4gdnVlc3RybyBvcmRlbmFkb3IsIGFzw60gcXVlIGludGVudGEgY29ycmVyL2VqZWN1dGFyIGVuIHR1IG9yZGVuYWRvciBsYXMgaW5zdHJ1Y2Npb25lcyBkZSBSIHF1ZSB2YXlhbiBhcGFyZWNpZW5kbyBlbiBlbCB0dXRvcmlhbC4KCiMgMS4gSWRlYXMgYsOhc2ljYXMKCgojIyBPcGVyYWNpb25lcyBtYXRlbcOhdGljYXMgYsOhc2ljYXMKClIgcHVlZGUgdXRpbGl6YXJzZSBjb21vIGNhbGN1bGFkb3JhLiAKCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KIyBvcGVyYWNpb25lcyBiw6FzaWNhcyBjb24gUgoyICsgMiAgICAKMiAtIDIgICAgCjIgKiAyICAgIAoyIC8gMiAKCiMgUG90ZW5jaWFjacOzbiAoc2UgcHVlZGUgaGFjZXIgY29uIGVsIG9wZXJhZG9yIF4gbyBjb24gKiopCjNeMiAgICAKMyoqMgoKIyBkaXZpc2nDs24gZW50ZXJhIHkgbcOzZHVsbwoxMSAlLyUgNSAgICAjIGRpdmlzacOzbiBlbnRlcmEgCjExICUlIDUgICAgICMgbcOzZHVsbyAocmVzdG8gZGUgbGEgZGl2aXNpw7NuIGVudGVyYSkKYGBgCgo8YnI+CgojIyMjIMK/SGF5IHF1ZSBwb25lciBlc3BhY2lvcyBlbnRyZSBsb3MgZWxlbWVudG9zIGRlIHVuYSBleHByZXNpw7NuPwoKQ29tbyB2ZXMsIG5vIGltcG9ydGEgc2kgcG9uZXMgbyBubyBlc3BhY2lvcyBlbnRyZSBsb3MgZWxlbWVudG9zIGRlIHVuYSBleHByZXNpw7NuLiBEZSB0b2RhcyBmb3JtYXMgY2FzaSBzaWVtcHJlIGVzIG1lam9yIHNlcGFyYXIgbG9zIGRpZmVyZW50ZXMgZWxlbWVudG9zIGRlIHVuYSBsaW5lYSBkZSBjw7NkaWdvOiB1c2FyIGVzcGFjaW9zIGhhY2UgZWwgY8OzZGlnbyBtw6FzIGxlZ2libGUuIFBhcmEgY29udmVuY2VydGUgZGUgcXVlIHVzZXMgZXNwYWNpb3M6ICDCv3F1ZSBjcmVlcyBxdWUgc2Ugc2UgbGVlIG1lam9yLCBlc3RvLCAqb3F1aXphc2NyZWVzcXVlc2VsZWVtZWpvcmVzdG9vdHJvKj8KCjxicj4KCiMjIyMgT3JkZW4gZGUgcHJlY2VkZW5jaWEgZGUgbGFzIG9wZXJhY2lvbmVzCgpMw7NnaWNhbWVudGUgc2UgcHVlZGVuIGNvbWJpbmFyIHZhcmlhcyBvcGVyYWNpb25lcy4gVGVuIGN1aWRhZG8gc2llbXByZSBjb24gZWwgb3JkZW4gZGUgcHJlY2VkZW5jaWEgcXVlIGRhIFIgYSBsYXMgb3BlcmFjaW9uZXMuIEVsIG9yZGVuIGVzIGVsIHNpZ3VpZW50ZTogcGFyw6ludGVzaXMsIGV4cG9uZW50ZXMsIG11bHRpcGxpY2FjacOzbiwgZGl2aXNpw7NuLCBzdW1hIHkgcmVzdGEuICoqQW50ZSBsYSBkdWRhIHVzYSBsb3MgcGFyw6ludGVzaXMqKi4gCgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0KNiArIDIgKiA1ICAgCig2ICsgMikgKiA1CmBgYAoKPGJyPgoKIyMjIyBleHByZXNpb25lcyBkZSB2YXJpYXMgbGluZWFzCgpEZSBtb21lbnRvIGVzIG1lam9yIG5vIGhhY2VybG8sIHBlcm8gcHVlZGVzIHJlcGFydGlyIHR1cyBleHByZXNpb25lcyBlbiB2YXJpYXMgbGluZWFzOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0KMiAgKyAyICsgMiArIDIgKyAyICsKMTAgKyAKMzAKYGBgCgpNw6FzIGFkZWxhbnRlLCBwb3IgZWplbXBsbyBjdWFuZG8gaGFnYW1vcyBncsOhZmljb3MsIHPDrSBxdWUgbG8gaGFyZW1vcyBwYXJhIGdhbmFyIGVuIGNsYXJpZGFkIGVuIGVsIGPDs2RpZ28uIExMZWdhcsOhLgoKPGJyPgoKCiMjIEFzaWduYWNpw7NuICgiY3JlYWNpw7NuIGRlIG9iamV0b3MiKQoKRW4gbGEgc2VjY2nDs24gYW50ZXJpb3IgaGljaW1vcyBxdWUgUiBoaWNpZXJhIHVub3MgY8OhbGN1bG9zLiBNdXkgYmllbiwgcGVybyBsbyBub3JtYWwgZW4gdW4gYW7DoWxpc2lzIGNvbiBkYXRvcyBlcyBxdWUsIHBhcmEgYWxjYW56YXIgdW4gcmVzdWx0YWRvIGZpbmFsLCB0ZW5nYW1vcyBxdWUgaGFjZXIgY8OhbGN1bG9zIGludGVybWVkaW9zIGUgaXIgY29tYmluw6FuZG9sb3MgcGFyYSBsbGVnYXIgYWwgcmVzdWx0YWRvIGZpbmFsLiBMb3MgY8OhbGN1bG9zIHF1ZSBoZW1vcyAgaGVjaG8gY29uIFIgZW4gbGEgc2VjY2nDs24gYW50ZXJpb3Igc2UgaGFuIHBlcmRpZG87IGRpY2hvIGRlIG90cmEgbWFuZXJhLCBhaG9yYSBtaXNtbyBubyBwb2RlbW9zIHJlY3VwZXJhcmxvcy4KClBhcmEgcG9kZXIgcmVjdXBlcmFyIG8gcmV1c2FyIGFsZ8O6biBjYWxjdWxvIGludGVybWVkaW8gaGVjaG8gY29uIFIsIHRlbmVtb3MgcXVlICJhc2lnbmFyIiB1biBub21icmUgYSBudWVzdHJvcyBjw6FsY3Vsb3MuIFJlcGl0bywgcGFyYSBwb2RlciByZWN1cGVyYXIvcmV1c2FyIHVuIHZhbG9yLCBlc3RlIGhhIGRlIHRlbmVyIGFzb2NpYWRvL2FzaWduYWRvIHVuIG5vbWJyZS4gCgpEaWNobyBkZSBvdHJhIG1hbmVyYSwgcGFyYSBwb2RlciByZXVzYXIgdW4gcmVzdWx0YWRvIG8gdmFsb3IgaW50ZXJtZWRpbywgdGVuZW1vcyBxdWUgY3JlYXIgdW4gb2JqZXRvIHF1ZSBjb250ZW5kcsOhIGVsIHZhbG9yIHF1ZSBoZW1vcyBjYWxjdWxhZG8sIHBlcm8gcGFyYSBlbGxvIHRlbmVtb3MgcXVlIHBvbmVyIG8gYXNpZ25hciB1biBub21icmUgYSBlc2Ugb2JqZXRvXltEZSBtb21lbnRvIG5vIGVzdG95IGVtcGxlYW5kbyBsYSB0ZXJtaW5vbG9nw61hIGNvcnJlY3RhXS4KCgpFbiBSLCBwYXJhICoqYXNpZ25hciB1biBub21icmUgYSB1biB2YWxvcioqLCB5IGFzw60gY3JlYXIgdW4gb2JqZXRvLCBzZSB1c2EgZWwgb3BlcmFkb3IgKipgPC1gKipeW1RhbWJpw6luIHNlIHB1ZWRlIHV0aWxpemFyIGVsIG9wZXJhZG9yIGA9YCBwZXJvLCBlbiBnZW5lcmFsLCBsYSBjb211bmlkYWQgUiB1c2EgKipgPC1gKiouXS4gVmXDoW1vc2xvOgoKCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFfQphYSA8LSAyICsgMgpgYGAKCgpGw61qYXRlIHF1ZSBwYXJlY2UgcXVlIFIgbm8gaGEgaGVjaG8gbmFkYSB5YSBxdWUgbm8gbm9zIG11ZXN0cmEgZWwgcmVzdWx0YWRvIGRlIGxhIG9wZXJhY2nDs24gZW4gbGEgY29uc29sYS4gQWhvcmEgbG8gcXVlIGhhIGhlY2hvIGVzIHJlYWxpemFyIGxhIG9wZXJhY2nDs24gb2J0ZW5pw6luZG9zZSB1biByZXN1bHRhZG8gbyB2YWxvciBkZSBgNGAgKGRvcyBtYXMgZG9zIHNvbiA0KSwgcGVybyBlbiBsdWdhciBkZSBtb3N0cmFybm9zL2Rldm9sdmVybm9zIGVsIHZhbG9yLCBsbyBxdWUgaGEgaGVjaG8gZXMgImFsbWFjZW5hciIgZWwgdmFsb3IgNCBlbiBlbCBvYmpldG8gYGFhYC4gCgpFbiB2dWVzdHJhIGNhYmV6YSB1bmEgZXhwcmVzacOzbiBkZSBhc2lnbmFjacOzbiwgcG9yIGVqZW1wbG8gYGFhIDwtIDQ0YCwgZGViZSBzb25hciBjb21vICBgYWEgdG9tYSBlbCB2YWxvciA0NGAuCgoKUG9kZW1vcyB2ZXIgZWwgb2JqZXRvIGBhYSBgZW4gZWwgcGFuZWwgc3VwZXJpb3ItZGVyZWNoYSBkZSBSU3R1ZGlvLCBjb25jcmV0YW1lbnRlIGVuIGxhIHBlc3Rhw7FhICJFbnZpcm9ubWVudCIuIEFob3JhIGVsIGVudG9ybm8gZGUgUiwgUiwgdGllbmUgdW4gb2JqZXRvIGFsbWFjZW5hZG8gZW4gc3UgbWVtb3JpYSwgdW4gb2JqZXRvIHF1ZSBzZSBsbGFtYSAiYWEiIHkgY3V5byB2YWxvciBlcyA0LgoKVmXDoW1vc2xvOiBzaSBhaG9yYSBlamVjdXRhbW9zIGBhYWAsIFIgbm9zIGRldm9sdmVyw6EgZWwgdmFsb3IgNC4gUG9kZW1vcyBwZW5zYXIgcXVlIGBhYWAgZXMgdW4gb2JqZXRvLCBkb25kZSBzZSBhbG1hY2VuYSBlbCB2YWxvciA0LgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0KYWEKYGBgCgpBaG9yYSBwb2RlbW9zIHVzYXIgZWwgb2JqZXRvIGBhYWAgcGFyYSBjb250aW51YXIgbyBoYWNlciBtw6FzIGPDoWxjdWxvcy4gUG9yIGVqZW1wbG86CgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0KYWEgKyAxCmBgYAoKRWwgbm9tYnJlIGBhYWAgZXN0w6EgYXNvY2lhZG8gYWwgdmFsb3IgYDRgLCBhc8OtIHF1ZSBjdWFuZG8gdXNlbW9zIGBhYWAgZW4gdW5hIG51ZXZhIGluc3RydWNjacOzbiwgUgp1c2Fyw6EgZWwgdmFsb3IgNC4gRXZpZGVudGVtZW50ZSBwb2RlbW9zIGNhbWJpYXIgZWwgdmFsb3IgZGVsIG9iamV0byBgYWFgLiBQb3IgZWplbXBsbzoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUV9CmFhIDwtIDEwCmBgYAoKQWhvcmEsIHNpIGxsYW1hbW9zIGEgYGFhYCBub3MgZGV2dWVsdmUgZWwgdmFsb3IgMTAuCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFfQphYQpgYGAKCsK/UXXDqSBoYWNlbiBsYXMgc2lndWllbnRlcyBsaW5lYXMgZGUgY8OzZGlnbz8gUHVlZGVzIHBlbnNhcmxvLCBwZXJvIGxvIG1lam9yIGVzIHF1ZSBlamVjdXRlcyBlbCBjw7NkaWdvIGVuIHR1IG9yZGVuYWRvci4KCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFLCByZXN1bHRzID0gImhpZGUifQphYSA8LSAyICsgMgpiYiA8LSA1ICsgYWEKY2MgPC0gMSArIGFhICogMgpkZCA8LSBiYgpgYGAKCkZpamFyb3MgcXVlIGFob3JhIGVuIGVsIGVudG9ybm8gbyAqZW52aXJvbm1lbnQqIGRlIFIgdGVuZHJlbW9zIDQgb2JqZXRvczogYWEsIGJiLCBjYywgZGQuCgo8YnI+CgojIyMjIEdsb2JhbCBlbnZpcm9ubWVudCAocHJpbWVyYSByZWZlcmVuY2lhIGFsKQoKQ2FkYSB2ZXogcXVlIGFzaWduYXMgdW4gbm9tYnJlIGEgdW4gdmFsb3IsIFIgY3JlYSB1biBudWV2byBvYmpldG8gZW4gZWwgYEdsb2JhbCBlbnZpcm9ubWVudGAuIEVzdG9zIG9iamV0b3MgZXhpc3RpcsOhbiBoYXN0YSBxdWUgY2llcnJlcyB0dSBzZXNpw7NuIGVuIFIsIGRlc3B1w6lzIGRlc2FwYXJlY2Vyw6FuLCBhIG5vIHNlciBxdWUgZ3VhcmRlcyBsb3Mgb2JqZXRvcyBlbiBkaXNjbyBlbiB1biBmaWNoZXJvLiBQb2RlbW9zIHZlciBsb3Mgb2JqZXRvcyBxdWUgaGF5IGVuIGVsIEdsb2JhbCBlbnZpcm9ubWVudCBwaW5jaGFuZG8gZW4gbGEgcGVzdGHDsWEgIkVudmlyb25tZW50IiBkZWwgcGFuZWwgc3VwZXJpb3ItaXpxdWllcmRvIGRlIFJTdHVkaW8uIFRhbWJpw6luIHBvZGVtb3MgaGFjZXJsbyBjb24gbGEgZnVuY2nDs24gYGxzKClgLiBFamVjdXRhIGxhcyBzaWd1aWVudGVzIGluc3RydWNjaW9uZXMgeSBvYnNlcnZhIHF1ZSBvY3VycmUgZW4gZWwgR2xvYmFsIGVudmlyb25tZW50LgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUV9CmxzKCkgICAgICAgICAgICAgICMtIG11ZXN0cmEgbG9zIG9iamV0b3MgZW4gZWwgR2xvYmFsIGVudi4gSGFjZSBsbyBtaXNtbyBxdWUgb2JqZWN0cygpCgpybShjYykgICAgICAgICAgICAjLSBib3JyYS9lbGltaW5hIGVsIG9iamV0byBjYyBkZWwgR2xvYmFsIGVudi4Kcm0obGlzdCA9IGxzKCkpICAgIy0gYm9ycmEgdG9kb3MgbG9zIG9iamV0b3MgZGVsIEdsb2JhbCBlbnYuCmBgYAoKPGJyPgoKClJlY3VlcmRhIHF1ZSBlbiBSLCBsYSBmb3JtYSBnZW7DqXJpY2EgZGUgdW5hIG9wZXJhY2nDs24gZGUgYXNpZ25hY2nDs24sIG8gZGUgImNyZWFjacOzbiBkZSBvYmpldG9zIiwgZXM6IGBub21icmVfZGVsX29iamV0byA8LSB2YWxvcmAsIHBvciBlamVtcGxvIGBhYSA8LSAyICsgMmAsIHkgcXVlIHBhcmEgcG9kZXIgcmVjdXBlcmFyL3JldXNhciB1biB2YWxvciBoYSBkZSB0ZW5lciBhc29jaWFkby9hc2lnbmFkbyB1biBub21icmUuCgo8YnI+CgojIyMjIMK/UHVlZG8gdXNhciBlbCBub21icmUgcXVlIHF1aWVyYSBwYXJhIG1pcyBvYmpldG9zPwoKTm8gZGVsIHRvZG8uIFBhcmEgcXVlIHVuIG5vbWJyZSBzZWEgc2ludMOhY3RpY2FtZW50ZSB2w6FsaWRvIGVuIFIgc8OzbG8gcHVlZGUgY29udGVuZXIgY2FyYWN0ZXJlcyBhbGZhbnVtw6lyaWNvcyAobGV0cmFzIHkgbsO6bWVyb3MpLCBwdW50b3MgKCoqYC5gKiopIHkgZ3Vpb25lcyBiYWpvcyAoKipgX2AqKikuIAoKQWRlbcOhczoKCiAgLSB1biBub21icmUgTk8gcHVlZGUgZW1wZXphciBwb3IgdW4gbsO6bWVybwoKICAtIHVuIG5vbWJyZSBOTyBwdWVkZSBlbXBlemFyIHBvciAqKmBfYCoqCiAgCiAgLSBzaSBlbCBub21icmUgZW1waWV6YSBwb3IgYC5gICwgZWwgcHVudG8gTk8gcHVlZGUgaXIgc2VndWlkbyBkZSB1biBuw7ptZXJvCiAgCgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMgbm9tYnJlcyB2w6FsaWRvcwpwZXBlIDwtIDQgCnBlcGVfMiA8LSA0CnBlcGVfMi4zIDwtIDIyCi5ob2xhIDwtIDcKCiMgbm9tYnJlcyBubyB2w6FsaWRvcwo0cGVwZSA8LSAzMwpfaG9sYSA8LSA2NgouMnh4ICA8LSA4OApgYGAKCkxvIG5vcm1hbCBlcyB1c2FyIG5vbWJyZXMgc2ltcGxlcyB5LCBzaSBwdWVkZSBzZXIsIGV4cGxpY2F0aXZvcyBkZSBsYSBuYXR1cmFsZXphIGRlbCB2YWxvciBhbG1hY2VuYWRvIGVuIGVsIG9iamV0by4KCgpSIGRpc3Rpbmd1ZSBlbnRyZSBtYXnDunNjdWxhcyB5IG1pbsO6c2N1bGFzIGFzw60gcXVlIHB1ZWRlIGV4aXN0aXIgdW4gb2JqZXRvIGNvbiBub21icmUgYEFyaWFkbmFgIHkgb3RybyBjb24gbm9tYnJlIGBhcmlhZG5hYC4KCkNvbW8gZW4gdG9kb3MgbG9zIGxlbmd1YWplcyBkZSBwcm9ncmFtYWNpw7NuLCBSIHRpZW5lIHVuYSBzZXJpZSBkZSBub21icmVzIHJlc2VydmFkb3MsIHlhIGFzaWduYWRvcywgeSBxdWUgcG9yIHRhbnRvIG5vIHNlIHB1ZWRlbiB1c2FyIHBhcmEgbm9tYnJhciBhIGxvcyBvYmpldG9zIHF1ZSBub3NvdHJvcyBjcmVlbW9zLiBMw7NnaWNhbWVudGUgZXN0w6FuIHByb2hpYmlkb3MgcG9ycXVlIFIgbG9zIHVzYSBpbnRlcm5hbWVudGUuIEVzdG9zIG5vbWJyZXMgcHJvaGliaWRvcyBzb246CgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KaWYgIGVsc2UgcmVwZWF0ICB3aGlsZSAgZm9yIGluIG5leHQgYnJlYWsKZnVuY3Rpb24KVFJVRSBGQUxTRSAKTlVMTCBJbmYgTmFOIApOQSBOQV9pbnRlZ2VyXyBOQV9yZWFsXyBOQV9jb21wbGV4XyBOQV9jaGFyYWN0ZXJfCmBgYAoKPGJyPgoKIyMgVGlwb3MgZGUgZGF0b3MKCgpZYSBzYWJlbW9zIHF1ZSBSIG5vcyB2YSBhIGF5dWRhciBhIG1hbmVqYXIvYW5hbGl6YXIgZGF0b3MuIEJpZW4sIHBlcm8gwr9xdcOpIHRpcG9zIGRlIGRhdG9zIGVudGllbmRlIFI/IAoKTG9zIHByaW5jaXBhbGVzIHRpcG9zIHNvbiAzOiBudW3DqXJpY29zLCBkZSB0ZXh0byB5IGzDs2dpY29zOyBlbiByZWFsaWRhZCA0IHBvcnF1ZSBSIGRpZmVyZW5jaWEgbG9zIGRhdG9zIG7Dum1lcmljb3MgZW4gZG91YmxlcyBlIGludGVnZXJzXltFbiByZWFsaWRhZCBzb24gNiBwb3JxdWUgUiB0aWVuZSB0YW1iacOpbiBjb21wbGV4IHkgcmF3IHR5cGVzXQpDYWRhIGRhdG8gbyB2YWxvciBlcyBkZSB1biB0aXBvLiBFbCB0aXBvIGRlIHVuIHZhbG9yIGRldGVybWluYSBsYSBmb3JtYSBkZSBhbG1hY2VuYW1pZW50byB5IGxhcyBvcGVyYWNpb25lcyBxdWUgc2UgcHVlZGVuIGhhY2VyIGNvbiDDqWwuIAoKClIgcHVlZGUgbWFuZWphciwgdGllbmUsIDUgdGlwb3MgZGUgZGF0b3M6CgogIC0gQ2FyYWN0ZXJlcyAodGV4dG8pOiBQZXBlLCBob2xhLCBNYWRyaWQgIAogICAgCiAgLSBEb3VibGVzOiAyLDM1ICwgNzcsMDAgLCAtNSw2ICAgIAogICAgCiAgLSBFbnRlcm9zOiAxLCAtNDQgICAgCiAgICAKICAtIEzDs2dpY29zOiBUUlVFLCBGQUxTRSAoRW4gb3BlcmFjaW9uZXMgYXJpdG3DqXRpY2FzIHRvbWFuIHZhbG9yZXMgMSB5IDApICAgIAogICAgCiAgLSBDb21wbGVqb3M6IDMrMmkgICAgCiAgICAKQWRlbWFzIGRlIGVzdG9zIDUsIGV4aXN0ZSB1biBzZXh0byB0aXBvOiBgcmF3YCwgcXVlIG5vIHZhbW9zIGEgdXRpbGl6YXIuCgoKUG9yIGVqZW1wbG86CgpgYGB7cn0KYWEgPC0gIkxhIGNhbGxlIGVuIGxhIHF1ZSB2aXZvIGVzIiAgICAjLSB0ZXh0bwpiYiA8LSA0TCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMtIG7Dum1lcm8gZW50ZXJvCmNjIDwtIDQuMyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIy0gbsO6bWVybyBkZWNpbWFsCmRkIDwtIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgIy0gbG9naWNhbApgYGAKCgpOb3JtYWxtZW50ZSBzYWJyZW1vcyBkZSBxdWUgdGlwbyh0ZXh0bywgZW50ZXJvLCAuLi4pIGVzIGNhZGEgb2JqZXRvLCBwZXJvIGNvbnZpZW5lIHNhYmVyIGNvbiBjZXJ0ZXphIGRlIHF1ZSB0aXBvIGVzIGNhZGEgb2JqZXRvLiBQb3IgZWplbXBsbywgbG9zIG9iamV0b3MgY3JlYWRvcyBlbiBlbCBjaHVuayBhbnRlcmlvciwgwr9kZSBxdWUgdGlwbyBzb24/IE5vcyBsbyBpbWFnaW5hbW9zLCBwZXJvIHBhcmEgY29uZmlybWFybG8gcG9kZW1vcyB1c2FyIGxhIGZ1bmNpw7NuIGB0eXBlb2YoKWAKCgpgYGB7cn0KdHlwZW9mKGFhKQp0eXBlb2YoYmIpCnR5cGVvZihjYykKdHlwZW9mKGRkKQpgYGAKCjxicj4KCkVuIHJlYWxpZGFkIGxvcyAzIHRpcG9zIGZ1bmRhbWVudGFsZXMgZGUgZGF0b3MvdmFsb3JlcyBxdWUgdXNhcmVtb3Mgc29uOgoKICAgLSAgIm51bWVyaWMiOiBOdW3DqXJpY29zIChuw7ptZXJvcyBlbnRlcm9zIHkgcmVhbGVzKSAgCiAgIAogICAtICAiY2hhcmFjdGVyIjogVGV4dG8gKHNlY3VlbmNpYXMgbyBjYWRlbmFzIGRlIGNhcmFjdGVyZXMpICAKICAgCiAgIC0gICJsb2dpY2FsIjogTMOzZ2ljb3MgbyBib29sZWFub3MgKFRSVUUgbyBGQUxTRSwgcXVlIGVuIG9wZXJhY2lvbmVzIGFyaXRtw6l0aWNhcyB0b21hbiB2YWxvcmVzIDEgeSAwKSAgCgoKQWRlbcOhcywgaGF5IDQgdmFsb3JlcyBlc3BlY2lhbGVzOiBgTlVMTGAsIGBOQWAsIGBOYU5gLCBlIGluZmluaXRvCgpMb3MgYE5BYCdzIHNlcsOhbiBtw6FzIGFkZWxhbnRlIGltcG9ydGFudGVzIHkgZW4gY2llcnRhIGZvcm1hIGhheSBxdWUgdGVuZXIgY3VpZGFkbyBjb24gZWxsb3MsIHNpZW1wcmUgaGFicsOhIHF1ZSBjaGVxdWVhciBzaSBudWVzdHJvcyBkYXRvcyBjb250aWVuZW4gYE5BYCdzLiBOQSByZXByZXNlbnRhIHVuYSBjYXJhY3RlcsOtc3RpY2EgcXVlIGV4aXN0ZSBwZXJvIGRlIGxhIGN1YWwgbm8gc2FiZW1vcyBzdSB2YWxvciwgcGVybyBlc2UgdmFsb3IgZXhpc3RlLiBQb3IgZWplbXBsbywgaW1hZ2luYSBxdWUgZWwgc2lndWllbnRlIHZlY3RvciByZXByZXNlbnRhIGxhIGFsdHVyYSBlbiBjZW50w61tZXRyb3MgZGUgdW4gZ3J1cG8gZGUgcGVyc29uYXMgOiBgYygxODIsIDE4NywgMTU5LCBOQSwgMTY2KWAuIEVsIHZlY3RvciB0aWVuZSA1IGVsZW1lbnRvcywgcGVybyBlbCBjdWFydG8gZWxlbWVudG8gZXMgdW4gTkEsIGxvIHF1ZSBpbmRpY2EgcXVlIG5vIHNhYmVtb3MgbGEgYWx0dXJhIGRlbCBjdWFydG8gaW5kaXZpZHVvLiBJbWFnaW5hIHF1ZSBxdWVyZW1vcyBjYWxjdWxhciBsYSBhbHR1cmEgbWVkaWEsIHV0aWxpemFyZW1vcyBsYSBmdW5jacOzbiBgbWVhbigpYC4gRsOtamF0ZSBxdWUgb2N1cnJlOgoKYGBge3J9CmFhIDwtIGMoMTgyLCAxNzgsIDE3MiwgTkEsIDE2OCkKbWVhbihhYSkKbWVhbihhYSwgbmEucm0gPSBUUlVFKQpgYGAKCkNvbW8gdmVzLCBjdWFuZG8gc2UgaGFjZSB1biBjYWxjdWxvIGVzdGFkw61zdGljbyBjb24gdW4gb2JqZXRvIHF1ZSBjb250aWVuZSBOQSdzLCBlbCByZXN1bHRhZG8gZXMgTkE7IGVzIGRlY2lyLCBsb3MgTkEncyBzZSBwcm9wYWdhbl5bU2luIGVtYmFyZ28sIHNpIGVsZXZhcyB1biBOQSBhIGNlcm8sIGVsIHJlc3VsdGFkbyBzZXJhIDEsIHBvcnF1ZSB0b2RvIG7Dum1lcm8gZWxldmFkbyBhIGNlcm8gZXMgMS4gRGUgbGEgbWlzbWEgbWFuZXJhIHNpIGVqZWN1dGFzIGBOQSB8IFRSVUVgIGRldm9sdmVyw6EgVFJVRV0uIFNpIHF1aWVyZXMgcXVlIGVzdG8gbm8gb2N1cnJhIGN1YW5kbyBoYWNlcyBjw6FsY3Vsb3MgZXN0YWTDrXN0aWNvcywgaGFzIHV0aWxpemFyIGRlbnRybyBkZSBsYXMgZnVuY2lvbmVzIGxhIG9wY2nDs24gYG5hLnJtID0gVFJVRWAgcXVlIGxvIHF1ZSBoYWNlIGVzIGV4Y2x1aXIgbG9zIE5BJ3MgYW50ZXMgZGUgaGFjZXIgbG9zIGPDoWxjdWxvcy4KCgo8YnI+CgojIyBPcGVyYWNpb25lcyBjb24gbsO6bWVyb3MKCkNvbiBkYXRvcyBvICoqdmFyaWFibGVzIG51bcOpcmljYXMqKiB5YSBoZW1vcyB2aXN0byBxdWUgc2UgcHVlZGVuIGhhY2VyIG9wZXJhY2lvbmVzIGFyaXRtw6l0aWNhcyAoc3VtYSwgcmVzdGEgZXRjLi4uKSwgcGVybyBjb24gbsO6bWVyb3Mgc2UgcHVlZGVuIGhhY2VyIHRhbWJpw6luICoqY29tcGFyYWNpb25lcyoqLgoKClBvciBlamVtcGxvOgoKYGBge3J9CjIgPCA0ICAgICMgTUVOT1IgcXVlOiBlc3RhIGV4cHJlc2nDs24gY2hlcXVlYSBzaSAyIGVzIE1FTk9SIHF1ZSBjdWF0cm8uIENvbW8gZXMgY2llcnRvLCBub3MgZGV2dWVsdmUgdW4gVFJVRQoyID4gNCAgICAjIE1BWU9SIHF1ZTogZXN0YSBleHByZXNpw7NuIGNoZXF1ZWEgc2kgMiBlcyBNQVlPUiBxdWUgY3VhdHJvLiBDb21vIGVzIGNpZXJ0bywgbm9zIGRldnVlbHZlIHVuIFRSVUUKNSA+PSA3ICAgIyBNQVlPUiBvIElHVUFMIHF1ZS4KOCA8PSA4ICAgIyBNRU5PUiBvIElHVUFMIHF1ZS4KOSA9PSA3ICAgIyBJR1VBTCBhOiBjb21vIDkgbm8gZXMgaWd1YWwgYSA3LCBub3MgZGV2b2x2ZXLDoSB1biBGQUxTRQoyID09IDIgICAjIElHVUFMIGE6IGNvbW8gMiBlcyBpZ3VhbCBhIDIsIG5vcyBkZXZ1ZWx2ZSBUUlVFCjIgIT0gNCAgICMgTk8gSUdVQUw6IGNvbW8gMiBubyBlcyBpZ3VhbCBhIDQgbm9zIGRldnVlbHZlIFRSVUUKNSAhPSA1ICAgIyBOTyBJR1VBTDogY29tbyA1IGVzIGlndWFsIGEgNSwgbm9zIGRldnVlbHZlIEZBTFNFIApgYGAKCkbDrWphdGUgcXVlIGVzdGFzIGV4cHJlc2lvbmVzIGRlIGNvbXBhcmFjacOzbiBhbCBzZXIgZWplY3V0YWRhcyBkZXZ1ZWx2ZW4gdW4gImJvb2xlYW4iOiBlbCByZXN1bHRhZG8gZGUgbGEgY29tcGFyYWNpw7NuIHNvbG8gcHVlZGUgc2VyIFRSVUUgbyBGQUxTRSBzaSBsYSBhZmlybWFjacOzbiBlcyBjaWVydGEgbywgcG9yIGVsIGNvbnRyYXJpbyBubyBlcyBjaWVydGEuIFBvciBlamVtcGxvIHNpIGVqZWN1dGFzIGA1IDwgMmAgZWwgcmVzdWx0YWRvIHNlcmEgYEZBTFNFYCBwb3JxdWUgNSBubyBlcyBtZW5vciBxdWUgMi4KCgpRdWUgwr9wYXJhIHF1w6kgbmVjZXNpdGFtb3MgZXN0b3Mgb3BlcmFkb3Jlcz8gUGFyYSBtdWNoYXMgY29zYXMsIHBvciBlamVtcGxvIHBhcmEgdmVyIHF1ZSB2YWxvcmVzIGRlIHVuYSB2YXJpYWJsZSBlc3TDoW4gcG9yIGVuY2ltYSBkZSBsYSBtZWRpYS4gCgpGw61qYXRlIHF1ZSBlbCBvcGVyYWRvciBwYXJhIGNoZXF1ZWFyIGxhIGlndWFsZGFkIGRlIGRvcyB2YWxvcmVzIG5vIGVzIGA9YCwgcXVlIGVzIGxvIHF1ZSBoYWJpdHVhbG1lbnRlIHVzYW1vcyBlbiBtYXRlbcOhdGljYXMgcGFyYSBleHByZXNhciBpZ3VhbGRhZC4gRW4gUiBsYSBpZ3VhbGRhZCBzZSBleHByZXNhL2NoZXF1ZWEgY29uIGVsIGRvYmxlIGlndWFsOiAgYD09YC4gCgoKUmVjdWVyZGEgcXVlIGVsIHNpZ25vIGA9YCBzZSBwdWVkZSB1c2FyIHBhcmEgaGFjZXIgdW5hIGFzaWduYWNpw7NuLCBhdW5xdWUgZW4gUiBlcyBtw6FzIGhhYml0dWFsIHVzYXIgZWwgYD1gIGRlbnRybyBkZSBsYXMgZnVuY2lvbmVzIHBhcmEgYXNpZ25hciB1biB2YWxvciBhbCBhcmd1bWVudG8gZGUgdW5hIGZ1bmNpw7NuLCB5IHNlIHN1ZWxlIHVzYXIgYDwtYCBjb21vIHPDrW1ib2xvIGRlIGFzaWduYWNpw7NuLgoKPGJyPgoKIyMgT3BlcmFjaW9uZXMgY29uIHRleHRvCgpIYXkgbXVjaGFzLCBwZXJvIHBvciBlamVtcGxvLCBubyBwb2RlbW9zIHN1bWFyIDIgIHN0cmluZ3MgbyAyIGNhZGVuYXMgZGUgdGV4dG8sIHBlcm8gc2kgcXVlIHNlIHB1ZWRlbiBwZWdhciBvIGNvbmNhdGVuYXIuIFBvciBlamVtcGxvIGNvbiBsYSBmdW5jacOzbiBgcGFzdGUoKWAuCgoKYGBge3J9CmFhIDwtICJtaSBub21icmUgZXMiCmJiIDwtICJwZWRybyIKcGFzdGUoYWEsIGJiKQpwYXN0ZShhYSwgYmIsIHNlcCA9ICIgLi4uICIpCiMgUHJ1ZWJhIHTDuiBtaXNtbyBxdWUgaGFjZSBsYSBmdW5jacOzbiBwYXN0ZTAoKQpgYGAKCgpWZWFtb3MgYWxndW5hcyBvdHJhcyAib3BlcmFjaW9uZXMiIHF1ZSBzZSBwdWVkZW4gaGFjZXIgY29uIHZhcmlhYmxlcyBkZSB0ZXh0bzoKCgpgYGB7cn0KdG91cHBlcihhYSkKdG9sb3dlcihhYSkKc3RyaW5ncjo6c3RyX3RvX3NlbnRlbmNlKGFhKSAgICMtIHN0cmluZ3IgZXMgdW4gcGtnIGRlbCB0aWR5dmVyc2UKCm5jaGFyKGJiKSAgICAgICAgICAgICAgICAgICAgICAjLSBuY2hhcigpIG5vcyBkZXZ1ZWx2ZSBlbCBuw7ptZXJvIGRlIGNhcmFjdGVyZXMgZGUgdW4gc3RyaW5nCnN1YnN0cmluZyhiYiwgMiwgbmNoYXIoYmIpKSAgICAjLSBzdWJzdHJpbmcoKSBleHRyYWUgY2FyYWN0ZXJlcyBkZSB1biBzdHJpbmcKYGBgCgoKU2kgYWwgZmluYWwgbmVjZXNpdGFtb3MgbWFuaXB1bGFyIHRleHRvLCB0ZW5kcmVtb3MgcXVlIGFwcmVuZGVyIHVuIHBvY28gZGUgYHJlZ3VsYXIgZXhwcmVzc2lvbnNgIHksIHNpIHB1ZWRlIHNlciwgdXRpbGl6YXJlbW9zIGVsIHBhcXVldGUgW2BzdHJpbmdyYF0oaHR0cHM6Ly9zdHJpbmdyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvcmVndWxhci1leHByZXNzaW9ucy5odG1sKQoKPGJyPgoKCgojIyBPcGVyYWNpb25lcyBsw7NnaWNhcwoKCkEgdmVjZXMgdGVuZHJlbW9zIHF1ZSBoYWNlciBvcGVyYWNpb25lcyBsw7NnaWNhcywgcG9yIGVqZW1wbG8sIHRlbmRyZW1vcyBxdWUgc2VsZWNjaW9uYXIgYSBsb3MgaW5kaXZpZHVvcyBxdWUgdGVuZ2FuIG3DoXMgcmVudGEgcXVlIGxhIG1lZGlhIHkgcXVlIGFkZW3DoXMgZXN0w6luIHNvbHRlcm9zLiBFbiBSIHRlbmVtb3MgbG9zIHNpZ3VpZW50ZXMgb3BlcmFkb3JlcyBsw7NnaWNvczoKCi0gYCZgOiBlcyBlbCBvcGVyYWRvciBsw7NnaWNvICoqQU5EKiouIERldnVlbHZlIFRSVUUgc2kgc2UgY3VtcGxlbiB0b2RhcyBsYXMgY29uZGljaW9uZXMuCgotIGB8YDogZXMgZWwgb3BlcmFkb3IgbMOzZ2ljbyAqKk9SKiouIFNvbG8gZGV2dWVsdmUgRkFMU0Ugc2kgTk8gc2UgY3VtcGxlIG5pbmd1bmEgZGUgbGFzIGNvbmRpY2lvbmVzLgoKLSBgIWA6IGVzIGVsIG9wZXJhZG9yIGzDs2dpY28gKipOT1QqKi4gQ2FtYmlhIGVsIHNlbnRpZG8gZGUgdW5hIGFmaXJtYWNpw7NuIGzDs2dpY2EuIE5vIGNvbmZ1bmRpciBjb24gZWwgb3BlcmFkb3IgcmVsYWNpb25hbCBvIGRlIGNvbXBhcmFjacOzbiBgIT1gCgotIGB4b3IoKWA6IGVzIGVsIG9wZXJhZG9yIGzDs2dpY28gKipFeGNsdXNpdmUgT1IqKi4gRGV2dWVsdmUgVFJVRSBzaSBzZSBjdW1wbGUgdW5hIHkgc29sbyB1bmEgZGUgbGFzIDIgY29uZGljaW9uZXMuCgoKSGF5IGFsZ3VubyBtYXMgKGBhbGwoKWAsIGBhbnkoKWAsIGAmJmAgeSBgfHxgKSBwZXJvIHN1ZmljaWVudGUgY29uIGVzdG9zIHRyZXMgcHJpbWVyb3MuCgoKYGBge3J9Cig0ID4gMykgJiAoMyA+IDIpICAgICAjLSBZOiBjb21vIHNlIGN1bXBsZW4gbGFzIGRvcyBjb25kaWNpb25lcyBub3MgZGV2dWVsdmUgVFJVRQooMSA9PSAyKSB8ICgyID4zKSAgICAgICAjLSBPOiBDb21vIG5vIHNlIGN1bXBsZSBuaW5ndW5hIGRlIGxhcyAyIGNvbmRpY2lvbmVzIG5vcyBkZXZ1ZWx2ZSBGQUxTRQohKDQgPiAzKSAgICAgICAgICAgICAgIy0gTk9UOiA0IGVzIG1heW9yIHF1ZSAzIGVzIFRSVUUsIHBlcm8gZWwgISBkZWxhbnRlIGRlIGVzYSBjb25kaWNpw7NuIGxhIG5pZWdhIHkgcGFzYSBhIEZBTFNFCiEhKDQgPiAzKSAgICAgICAgICAgICAjLSBzaSBuaWVnYXMgZG9zIHZlY2VzLCB2dWVsdmVzIGFsIHByaW5jaXBpbzogVFJVRQoKeG9yKDEwIDwgMSwgMTAgPjEgKSAjLSBzZSBjdW1wbGUgMSBkZSBsYXMgZG9zIGNvbmRpY2lvbmVzLCBlbnRvbmNlcyBUUlVFCnhvcigxMCA+IDEsIDEwID4xICkgIy0gc2UgY3VtcGxlbiBsYXMgMiBjb25kaWNpb25lcywgZW50b25jZXMgRkFMU0UKeG9yKDEwIDwgMSwgMTAgPDEgKSAjLSBubyBzZSBjdW1wbGUgbmluZ3VuYSBkZSBsYXMgMiBjb25kaWNpb25lczogRkFMU0UKYGBgCgoKCgojIyBGdW5jaW9uZXMKCkVudGVuZGVyIHF1w6kgZXMgdW5hIGZ1bmNpw7NuIHkgY8OzbW8gImZ1bmNpb25hIHVuYSBmdW5jacOzbiIgZXMgbXV1dXV5IGltcG9ydGFudGUhISEKClIgeSBzdXMgcGFxdWV0ZXMgdGllbmVuIG11Y2jDrXNpbWFzIGZ1bmNpb25lcyBxdWUgbm9zIHBlcm1pdGVuLCBwb3IgZWplbXBsbywgY2FsY3VsYXIgbG9nYXJpdG1vcywgbyByYcOtY2VzIGN1YWRyYWRhcywgbyBjYWxjdWxhciBkZXN2aWFjaW9uZXMgdMOtcGljYXMgZXRjIGV0Yy4uLiBBZGVtw6FzLCB0YW1iacOpbiBwb2RlbW9zIGNyZWFyIG51ZXN0cmFzIHByb3BpYXMgZnVuY2lvbmVzLiBTZWd1cmFtZW50ZSBjcmVhcmVtb3MgYWxndW5hcyBlbiBlbCBjdXJzbywgcGVybyBhbnRlcywgZXMgTVVZIElNUE9SVEFOVEUgZW50ZW5kZXIgcXXDqSBlcyB5IGPDs21vICJmdW5jaW9uYSIsIGPDs21vIHNlIHVzYSwgdW5hIGZ1bmNpw7NuIGRlIFIuCgpSZXBpdG8sIGVzIG11eSBpbXBvcnRhbnRlIGVudGVuZGVyIHF1w6kgZXMgeSBjw7NtbyB1c2FyIHVuYSBmdW5jacOzbi4gRGUgaGVjaG8sIHRvZG8gbG8gcXVlIG9jdXJyZS9wYXNhIGVuIFIgZXMgcG9ycXVlIGhhcyBsbGFtYWRvIGEgdW5hIGZ1bmNpw7NuLiBTaSBhbGdvIG9jdXJyZSBlbiBSIGVzIHBvcnF1ZSBoYXMgaGVjaG8gdW5hIGxsYW1hZGEgYSB1bmEgZnVuY2nDs24sIHVuYSAiZnVuY3Rpb24gY2FsbCIuCgpVbmEgZnVuY2nDs24gbm8gZXMgbcOhcyB1biB0cm96byBkZSBjw7NkaWdvIFIsIHVuYXMgaW5zdHJ1Y2Npb25lcyBkZSBSLCBhIGxhcyBxdWUgbGVzIGhlbW9zIHB1ZXN0byB1biBub21icmU7IGVudG9uY2VzLCBjdWFuZG8gaW52b3F1ZW1vcyBlc2Ugbm9tYnJlLCBlc2EgZnVuY2nDs24sIHNlIGVqZWN1dGFyw6FuIGVzYXMgbGluZWFzIGRlIGPDs2RpZ28uCgpFbXBlY2Vtb3MgYSB1c2FyIGFsZ3VuYSBmdW5jacOzbiBzZW5jaWxsYSAoZWwgb2JqZXRpdm8gZXMgZW50ZW5kZXIgY29tbyAiZnVuY2lvbmEiIHVuYSBmdW5jacOzbik6CgpVbmEgZnVuY2nDs24gbXV5IHNlbmNpbGxhIGVzIGBzcXJ0KClgLiBDb21vIGlndWFsIGltYWdpbsOhaXMsIGxhIGZ1bmNpw7NuIGBzcXJ0KClgIHNpcnZlIHBhcmEgY2FsY3VsYXIgbGEgcmHDrXogY3VhZHJhZGEgZGUgdW4gbsO6bWVyby4gQWxndWllbiwgaGEgZXNjcml0byBwb3Igbm9zb3Ryb3MgdW5hcyBsaW5lYXMgZGUgY8OzZGlnbyBxdWUgc2lydmVuIHBhcmEgaGFjZXIgcmHDrWNlcyBjdWFkcmFkYXMgeSBoYSBhc2lnbmFkbyBlc2FzIGxpbmVhcyBkZSBjw7NkaWdvIGFsIG5vbWJyZSBgc3FydGAuIHNxcnQgZXMgZWwgbm9tYnJlIGRlIGxhIGZ1bmNpw7NuIGBzcXJ0KClgCgoKQ29tbyBhbGd1aWVuIHNlIGhhIHByZW9jdXBhZG8gZGUgY29uc3RydWlyIHVuYSBmdW5jacOzbiBwYXJhIGhhY2VyIHJhw61jZXMgY3VhZHJhZGFzLCBub3NvdHJvcyBwb2RlbW9zIHVzYXIgZXNhIGZ1bmNpw7NuLiBTaSBwb3IgZWplbXBsbyBxdWVyZW1vcyBjYWxjdWxhciBsYSByYcOteiBjdWFkcmFkYSBkZSA5LCBzw7NsbyB0ZW5kcmVtb3MgcXVlIHRlY2xlYXIgZW4gUiBgc3FydCg5KWAuCgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gVFJVRX0Kc3FydCg5KQpgYGAKCgpQZXJmZWN0bywgZXN0w6EgYmllbiBxdWUgc2VwYW1vcyBjb21vIGhhY2VyIHJhw61jZXMgY3VhZHJhZGFzIGVuIFIsIFBFUk8gKipsbyBpbXBvcnRhbnRlIGVzIGVtcGV6YXIgYSBlbnRlbmRlciBsYSBlc3RydWN0dXJhIGRlIGxhcyBmdW5jaW9uZXMqKi4gCgoKTGEgZm9ybWEgZ2Vuw6lyaWNhIGRlIHV0aWxpemFyIHVuYSBmdW5jacOzbiBlcyBhbGdvIGNvbW86IGBub21icmVfZGVfbGFfZnVuY2lvbihhcmd1bWVudG9fMSwgYXJndW1lbnRvXzIsIC4uLi4pYC4gRW4gZWwgY2FzbyBkZSBsYSBmdW5jacOzbiBgc3FydCgpYCBzdSBub21icmUgZXMgYHNxcnRgIHkgZXN0YSBmdW5jacOzbiBzb2xvIGFkbWl0ZSB1biBhcmd1bWVudG8uIFVuIHZlY3RvciBkZSBuw7ptZXJvc15bUiBubyBlbnRpZW5kZSBlbCBjb25jZXB0byBkZSBlc2NhbGFyLiBFbnRyZSBzdXMgZXN0cnVjdHVyYXMgZGUgZGF0b3Mgbm8gZXN0w6FuIGxvcyBlc2NhbGFyZXMuIHBhcmEgUiBlbCBuw7ptZXJvIDkgZXMgZW4gcmVhbGlkYWQgdW4gdmVjdG9yIGNvbiB1biBzb2xvIGVsZW1lbnRvIGNvbiB2YWxvciA5XS4gCgpgc3FydCgpYCBzb2xvIGFkbWl0ZSB1biBhcmd1bWVudG8sIHBlcm8gb3RyYXMgZnVuY2lvbmVzIHB1ZWRlbiB0ZW5lciBtw6FzIGRlIHVuIGFyZ3VtZW50byB5IHF1aXrDoXMgbG9zIGFyZ3VtZW50b3Mgbm8gc2VhbiBudW3DqXJpY29zIHNpbm8gcG9yIGVqZW1wbG8gdGV4dHVhbGVzLiBCdWZmISEgZXMgZsOhY2lsLCBwZXJvIGV4cGxpY2FybG8gcG9yIGVzY3JpdG8sIHNpbiB2ZXIgYWwgcXVlIHZhIGEgcmVjaWJpciBsYSBleHBsaWNhY2nDs24sIGVzIGNvbXBsaWNhZG8uCgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CnNxcnQoOSkKc3FydCg5LCA0KSAgICMgbm8gZnVuY2lvbmEgcG9ycXVlIHNxcnQoKSBzb2xvIGFkbWl0ZSB1biBhcmd1bWVudG8gCnNxcnQoIjkiKSAgICAjIG5vIGZ1bmNpb25hLCBzb2xvIGhlbW9zIHB1ZXN0byB1biBhcmd1bWVudG8gcGVybyBlcyB0ZXh0dWFsLCB5IGhhIGRlIHNlciBudW3DqXJpY28KYGBgCgpFcyBkZWNpciwgcGFyYSB1c2FyIHVuYSBmdW5jacOzbiB0ZW5lbW9zIHF1ZSBzYWJlciBjdWFudG9zIGFyZ3VtZW50b3MgYWRtaXRlIHkgZGUgcXVlIHRpcG8gaGFuIGRlIHNlciBlc29zIGFyZ3VtZW50b3MuIEdlbmVyYWxtZW50ZSBlc3RvIGVzIG9idmlvLCBwZXJvIGhhYnLDoSB2ZWNlcyBxdWUgdGVuZ2Ftb3MgcXVlIHVzYXIgdW5hIGZ1bmNpw7NuIHF1ZSBubyBzZXBhbW9zIGNvbW8gZnVuY2lvbmEsIGVudG9uY2VzIHRlbmRyw6FzIHF1ZSBidXNjYXIgc3UgYXl1ZGEuICAKClBhcmEgb2J0ZW5lciBheXVkYSBzb2JyZSB1bmEgZnVuY2nDs24gcG9kZW1vcyB1c2FyIGxhIGZ1bmNpw7NuIGBoZWxwKClgLiBTaSB0ZWNsZWFzIGVuIFIgYGhlbHAobm9tYnJlX2RlX2xhX2Z1bmNpb24pYCBzZSBhYnJpcsOhIHVuYSB2ZW50YW5hIGRlIGF5dWRhIGVuIGxhIHBlc3Rhw7FhICJIZWxwIiBkZWwgcGFuZWwgYWJham8tZGVyZWNoYS4gUHJvYsOpbW9zbG8gY29uIGxhIGZ1bmNpw7NuIGBzcXJ0KClgCgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CmhlbHAoc3FydCkgCmBgYAoKQWwgbGVlciBsYSBheXVkYSBkZSBsYSBmdW5jacOzbiB0ZSBkYXLDoXMgY3VlbnRhIHF1ZSBoYWNlIHVuIHJhdG8gaGljaW1vcyB1biBwb2NvIGRlIHRyYW1wYXMsIHlhIHF1ZSBleHBsaWNhbW9zIGVsIGZ1bmNpb25hbWllbnRvIGRlIGxhcyBmdW5jaW9uZXMgZGUgdW5hIGZvcm1hIHVuIHBvY28gc2ltcGxpZmljYWRhLiBWYW1vcyBhIGhhY2VybG8gYWhvcmEgbWVqb3IuCgpZbyBkaWplIHF1ZSBsYSBlc3RydWN0dXJhIGRlIHVuYSBmdW5jacOzbiBlcyBgbm9tYnJlX2RlX2xhX2Z1bmNpb24oYXJndW1lbnRvXzEsIGFyZ3VtZW50b18yLCAuLi4uKWAgICBjdWFuZG8gZW4gcmVhbGlkYWQgZXMgOiAqKmBub21icmVfZGVfbGFfZnVuY2lvbihhcmd1bWVudG9fMSA9IHZhbG9yXzEsIGFyZ3VtZW50b18yID0gdmFsb3JfMiwgLi4uLilgKiouIAoKVmFtb3MgYSBleHBsaWNhcmxvIGNvbiBsYSBmdW5jacOzbiBgc3FydCgpYC4gRWwgbm9tYnJlIGRlIGxhIGZ1bmNpw7NuIGBzcXJ0KClgIGVzIGBzcXJ0YDsgeSDCv2N1YWxlcyBzb24gc3VzIGFyZ3VtZW50b3M/IFNpIG1pcmFtb3MgbcOhcyBkZXRlbmlkYW1lbnRlIGxhIGF5dWRhLCB2ZXJlbW9zIHF1ZSBzw60sIHF1ZSBzw7NsbyB0aWVuZSB1biBhcmd1bWVudG8gYHhgICwgcXVlIGFkZW3DoXMgZGViZSBzZXIgbnVtw6lyaWNvLiAKCkJpZW4sIHBlcm8gwr9jdWFsIGVzIGVsIHZhbG9yIGRlbCBhcmd1bWVudG8/IEludGVudGEgZGVzY3VicmlybG8gY29uIGVsIGNodW5rIGRlIGPDs2RpZ28gc2lndWllbnRlOgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQpzcXJ0KHggPSA5KQpzcXJ0KHggPSA0KQpgYGAKCkxhIHNpbnRheGlzIGNvcnJlY3RhIGRlIGBzcXJ0KClgIGVzIGVmZWN0aXZhbWVudGUgYG5vbWJyZV9kZV9sYV9mdW5jaW9uKGFyZ3VtZW50b18xID0gdmFsb3JfMSwgYXJndW1lbnRvXzIgPSB2YWxvcl8yLCAuLi4uKWAsIGVuIHN1IGNhc28sIGNvbW8gc29sbyB0aWVuZSB1biBhcmd1bWVudG86IGBzcXJ0KGFyZ3VtZW50byA9IHZhbG9yKWAuIAoKRWwgbm9tYnJlIGRlbCBhcmd1bWVudG8gZXMgYHhgLCB5IGVsIHZhbG9yIGRlbCBhcmd1bWVudG8gZXMgZWwgcXVlIG5vc290cm9zIHF1ZXJhbW9zLCBzaWVtcHJlIHF1ZSBzZWEgbnVtw6lyaWNvLiBMYSBzaW50YXhpcyBjb3JyZWN0YS1jb3JyZWN0YSBlcyBgc3FydCh4ID0gOSlgLiA5IGVzIGVsIHZhbG9yIHF1ZSBsZSBkYW1vcyBhbCBhcmd1bWVudG8gYHhgIGRlIGxhIGZ1bmNpw7NuIGBzcXJ0KClgLgoKT2J2aWFtZW50ZSBwb2RlbW9zIGNhbWJpYXIgZWwgdmFsb3IgZGUgbGEgZnVuY2nDs24sIHNpbm8gbGEgZnVuY2nDs24gbm8gbm9zIHNlcsOtYSBtdXkgw7p0aWwuIEVudG9uY2VzLCDCv3BvciBxdcOpIGZ1bmNpb25hbiBsYXMgMiBsaW5lYXMgZGUgY8OzZGlnbyBzaWd1aWVudGVzPwoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IFRSVUV9CnNxcnQoOSkKc3FydCg0KQpgYGAKClB1ZXMgcG9ycXVlIGEgUiBubyBsZSBpbXBvcnRhIHF1ZSBubyBwb25nYXMgZWwgbm9tYnJlIGRlbCBhcmd1bWVudG8sIHB1ZWRlcyBwb25lciBzb2xhbWVudGUgc3UgdmFsb3IuIEhhYmxhcmVtb3MgZGUgZXN0byB1biBwb2NvIG3DoXMgZW4gY2xhc2UuIAoKQSB2ZXIgc2kgY29uc2lndWVzIGRpZmVyZW5jaWFyLCBlbiB1bmEgZnVuY2nDs24sIGVudHJlIGVsIG5vbWJyZSBkZSB1biBhcmd1bWVudG8geSBzdSB2YWxvcgoKYGBge3J9CnggPC0gMjUKc3FydCg5KQpzcXJ0KHgpCnNxcnQoeCA9IHgpCmBgYAoKU8OtLCBjdWVzdGEgdW4gcG9jbywgcG9xdWV0IGEgcG9xdWV0LCBbImJpcmQgYnkgYmlyZCJdKGh0dHBzOi8vd3d3LmJvb2tkZXBvc2l0b3J5LmNvbS9CaXJkLUJ5LUJpcmQtQW5uZS1MYW1vdHQvOTc4MDM4NTQ4MDAxNykuCgo8YnI+CgpPdHJhIHZleiBtw6FzLiBQb3IgZmF2b3IsIGNhbGN1bGFkIGNvbiBSIGVsIGxvZ2FyaXRtbyBkZSAxMDAwIGVuIGJhc2UgMTAuIFBhcmEgZWxsbyB0ZW7DqWlzIHF1ZSB1c2FyIGxhIGZ1bmNpw7NuIGBsb2coKWAsIHkgY8OzbW8gbm8gc2Fiw6lpcyBjw7NtbyBzZSB1c2EsIG5vIHNhYsOpaXMgY8OzbW8gZXMgc3Ugc2ludGF4aXMsIG5vIHNhYsOpaXMgY3VhbGVzIHNvbiBzdXMgYXJndW1lbnRvcywgb3MgdG9jYXLDoSBtaXJhciBsYSBheXVkYSBkZSBsYSBmdW5jacOzbi4KCgpgYGB7ciwgZWNobyA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KaGVscChsb2cpCmxvZyh4ID0gMTAwMCwgYmFzZSA9IDEwKQpgYGAKCkFsIHByaW5jaXBpbyBtaXJhciBsYSBheXVkYSBpbnRlcm5hIGRlIFIgcGFyYSBsYXMgZnVuY2lvbmVzIGFicnVtYSwgcGVybyBtdWNoYXMgdmVjZXMgY29uIG1pcmFyIGxvcyBlamVtcGxvcyAocXVlIGVzdMOhbiBzaWVtcHJlIGFsIGZpbmFsKSBvIGxhIHNlY2Npw7NuIGRlICJBcmd1bWVudHMiIGVzIHN1ZmljaWVudGUuIEVuIGVsIGNhc28gZGUgYGxvZygpYCB2ZW1vcyBxdWUgYWRtaXRlIGRvcyBhcmd1bWVudG9zLCAieCIgeSAiYmFzZSIuIExhIGF5dWRhIG5vcyBkaWNlIHF1ZSBsYSBzaW50YXhpcyBkZSBsYSBmdW5jacOzbiBlczogCgogIC0gbG9nKHgsIGJhc2UgPSBleHAoMSkpICAKICAKICAtICJ4IiBoYSBkZSBzZXIgbnVtw6lyaWNvIChvIGNvbXBsZWpvKSAgCiAgCiAgLSAiYmFzZSIgaGEgZGUgc2VyIHVuIG7Dum1lcm8gcG9zaXRpdm8gKG8gY29tcGxlam8pCiAgCkbDrWphdGUgcXVlIGxhIHNpbnRheGlzIGRlIGxhIGZ1bmNpw7NuIG5vIGVzIGBsb2coeCwgYmFzZSlgLCBzaW5vIGBsb2coeCwgYmFzZSA9IGV4cCgxKSlgOyBlc3RvIGluZGljYSBxdWUgZWwgYXJndW1lbnRvICJiYXNlIiB0aWVuZSB1biB2YWxvciBhc2lnbmFkbyBwb3IgZGVmZWN0bzsgZXMgZGVjaXIsIHNpIG5vIGVzcGVjaWZpY2Ftb3MgZWwgdmFsb3IgZGUgImJhc2UiLCAgImJhc2UiIHNlcsOhIGV4cCgxKTsgZXMgZGVjaXIsIGVsIG51bWVybyBlIHkgZXN0YXJlbW9zIHRvbWFuZG8gbG9nYXJpdG1vcyBuYXR1cmFsZXMuCgoKYGBge3IsIGVjaG8gPSBUUlVFfQpsb2coMTAwMCkgICMtIGNvbW8gbm8gZXNwZWNpZmljYW1vcyBlbCB2YWxvciBkZWwgYXJndW1lbnRvICJiYXNlIiwgUiBsbyBmaWphIHBvciBkZWZlY3RvIGJhc2UgPSBleHAoMSkgeSB0b21hIGxvZ2FyaXRtb3MgbmF0dXJhbGVzCgpsb2coMTAwMCwgYmFzZSA9IDEwKSAjLSBhaG9yYSBzaSBlc3RhbW9zIGNhbGN1bGFuZG8gbG9nYXJpdG1vIGVuIGJhc2UgMTAKYGBgCgo8YnI+CgpFcyBtw6FzIHNlZ3VybyBwb25lciBsb3Mgbm9tYnJlcyBkZSBsb3MgYXJndW1lbnRvcywgcGVybyBzaSBubyBsb3MgcG9uZXMsIG8gc29sbyBsb3MgcG9uZXMgcGFyY2lhbG1lbnRlLCB0YW1iacOpbiBmdW5jaW9uYQoKYGBge3IsIGVjaG8gPSBUUlVFfQpsb2coMTAwMCwgYmFzID0gMTApICAgIy0gZnVuY2lvbmEsIHBlcm8gLi4uCmxvZygxMDAwLCAxMCkgICAgICAgICAjLSBlc3RvIHPDrSBlcyBtdXkgaGFiaXR1YWwgdmVybG8KYGBgCgpFc28gc8OtLCBzaSBubyBwb25lcyBlbCBub21icmUgZGUgbG9zIGFyZ3VtZW50b3MsIGhhcyBkZSB0ZW5lciBlbiBjdWVudGEgZWwgb3JkZW4gZGUgZXN0b3MuCgpgYGB7ciwgZWNobyA9IFRSVUV9CmxvZygxMCwgMTAwMCkKYGBgCgoKUG9jbyBhIHBvY28gaXJlbW9zIGhhYmxhbmRvIG3DoXMgZGUgZnVuY2lvbmVzLiBDdWFuZG8gdXNlbW9zIHVuYSBudWV2YSBnZW5lcmFsbWVudGUgdGVuZHJlbW9zIHF1ZSBjb25zdWx0YXIgbGEgKipheXVkYSoqIHBhcmEgdmVyIHN1IHNpbnRheGlzIHkgc3VzIGFyZ3VtZW50b3MuCgpTYWJlciBidXNjYXIgYXl1ZGEgZXMgdW5hIGRlIGxhcyBoYWJpbGlkYWRlcyBtw6FzIGltcG9ydGFudGVzIGEgbGEgaG9yYSBkZSBhcHJlbmRlciBhIHByb2dyYW1hci4gW0VzdGUgcG9zdF0oaHR0cHM6Ly9zY3R5bmVyLmdpdGh1Yi5pby9yaGVscC5odG1sKSBkYSBidWVub3MgY29uc2Vqb3Mgc29icmUgY8OzbW8gYnVzY2FyIGF5dWRhIHNvYnJlIFIuIEVuIGVsIHNpZ3VpZW50ZSBhcGFydGFkbyBkZXNhcnJvbGxhcsOpIHVuIHBvY28gbGEgaWRlYSBkZSBjw7NtbyB1c2FyIGxhIGF5dWRhIGludGVybmEgZGUgUiBzb2JyZSBzdXMgZnVuY2lvbmVzLgoKPGJyPgoKIyMjIEF5dWRhIGRlIGxhcyBmdW5jaW9uZXMKCkhheSB2YXJpYXMgZm9ybWFzIGRlIHBlZGlyIGEgUiBsYSBheXVkYSBpbnRlcm5hIGRlIHVuYSBmdW5jacOzbjoKCjEpIHVzYW5kbyBsYSBmdW5jacOzbiBgaGVscCgpYCwgbyBzZWEsIHRlY2xlYW5kbyBlbiBSIGBoZWxwKG5vbWJyZV9kZV9sYV9mdW5jaW9uKWA7IHBvciBlamVtcGxvIGBoZWxwKGxvZylgCgoyKSB0ZWNsZWFuZG8gZW4gUiBgP25vbWJyZV9kZV9sYV9mdW5jaW9uYDsgcG9yIGVqZW1wbG8gYD8obG9nKWAKCjMpIGxhIGZvcm1hIG3DoXMgY8OzbW9kYSwgeSBsYSBxdWUgc3VlbG8gdXNhciwgY29uc2lzdGUgZW4gc2l0dWFyIGVsIGN1cnNvciBlbiBlbCBub21icmUgZGUgbGEgZnVuY2nDs24geSBwdWxzYXIgbGEgdGVjbGEgPGtiZD5GMTwva2JkPgogIAoKRW50ZW5kZXIgeSBzYWJlciBsZWVyIGxhIGF5dWRhIGRlIGxhcyBmdW5jaW9uZXMgZXMgbXV5IMO6dGlsLCBhc8OtIHF1ZSBvcyByZWNvbWllbmRvLCBwYXJhIG3DoXMgYWRlbGFudGUgKGRlIG1vbWVudG8gdGVuZW1vcyBvdHJhcyBjb3NhcyBxdWUgYXByZW5kZXIpLCBsYSBsZWN0dXJhIGRlICBbZXN0ZSBwb3N0XShodHRwOi8vc29jdml6LmNvL2FwcGVuZGl4Lmh0bWwjYS1saXR0bGUtbW9yZS1hYm91dC1yKS4gRXhwbGljYW4gYmllbiBjb21vIGVzdMOhIGVzdHJ1Y3R1cmFkYSBsYSBheXVkYSBkZSBsYXMgZnVuY2lvbmVzLiBBYmFqbyB0aWVuZXMgdW4gZWplbXBsbyBjb24gbGEgZnVuY2nDs24gYG1lYW4oKWAuCgoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIGV2YWwgPSBUUlVFLCBmaWcuY2FwID0gIkPDs21vIGxlZXIgbGFzIGF5dWRhcyBkZSBSICAoaHR0cDovL3NvY3Zpei5jby9hcHBlbmRpeC5odG1sI2EtbGl0dGxlLW1vcmUtYWJvdXQtcikiLCBmaWcuYXNwID0gNC8yLCBvdXQud2lkdGggPSAiOTAlIiwgZmlnLmFsaWduID0gImNlbnRlciJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltYWdlbmVzIiwgInR0XzAzX2ltZ18wMV9heXVkYS1mdW5jaW9uZXMuanBnIikpCmBgYAoKPGJyPgoKCkNvbW8gZWplbXBsbyBwdWVkZXMgcHJvYmFyIGEgbGVlciBsYSBheXVkYSBkZSBsYSBmdW5jacOzbiBgc2VxKClgLiBNaXJhbmRvIGxhIGF5dWRhIGludGVudGEgYWRpdmluYXIgcXVlIGhhcsOhbiBsYXMgc2lndWllbnRlcyBleHByZXNpb25lcyBSLiBUaWVuZXMgcXVlIGVudGVuZGVyIHBvcnF1ZSBsYSBjdWFydGEgeSBxdWludGEgbGluZWEgbm8gZGV2dWVsdmVuIGVsIG1pc21vIHJlc3VsdGFkby4KCgoKYGBge3J9CnNlcShmcm9tID0gMCwgdG8gPSAxMCkKc2VxKDAsIDEwKQpzZXEoMTAsIDApCnNlcSgwLCAxMCwgMykKc2VxKDAsIDEwLCBsZW5ndGgub3V0ID0gMykKYGBgCgo8YnI+CgpObyBzb2xvIHRlbmVtb3MgbGEgYXl1ZGEgb2ZpY2lhbCwgZW4gUiB0YW1iacOpbiBoYXkgZnVuY2lvbmVzIHF1ZSBub3MgcHVlZGVuIGF5dWRhciBhIGVudGVuZGVyIGNvbW8gZnVuY2lvbmEgb3RyYSBmdW5jacOzbjsgcG9yIGVqZW1wbG8gbGEgZnVuY2nDs24gYGFyZ3MoKWAsIHF1ZSBjb21vIHN1IG5vbWJyZSBpbnNpbsO6YSwgbm9zIHBlcm1pdGUgdmVyIGxvcyBhcmd1bWVudG9zIGRlIHVuYSBmdW5jacOzbi4gUG9yIGVqZW1wbG86CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KYXJncyhzcXJ0KSAgICAgICAjLSBzcXJ0KCkgc29sbyB0aWVuZSB1biBhcmd1bWVudG8sIHgKYXJncyhsb2cpICAgICAgICAjLSBsb2coKSB0aWVuZSAyIGFyZ3VtZW50b3M6IHggeSBiYXNlLiBBZGVtw6FzIGJhc2UgdGllbmUgdW4gdmFsb3IgcG9yIGRlZmVjdG86IGV4cCgxKQphcmdzKGFyZ3MpICAgICAgICMtIGFyZ3MoKSBzb2xvIHRpZW5lIHVuIGFyZ3VtZW50bywgbGxhbWFkbyBuYW1lLgpgYGAKCjxicj4KCiMjIyBDaWNsbyB2aXRhbCBkZSBsYXMgZnVuY2lvbmVzCgpPdHJhIGlkZWEgcXVlIGNvbnZpZW5lIGNvbm9jZXIsIGVzIHF1ZSBsYXMgZnVuY2lvbmVzIHRpZW5lbiB1biBjaWNsbyB2aXRhbCBvICJsaWZlIGN5Y2xlIjogaGF5IGZ1bmNpb25lcyBxdWUgYWNhYmFuIGRlIHNlciBjcmVhZGFzIHkgYcO6biBzb24gZXhwZXJpbWVudGFsZXMuIEhheSBmdW5jaW9uZXMgcXVlIHB1ZWRlbiBlc3RhciBzaWVuZG8gY3Vlc3Rpb25hZGFzIHkgb3RyYXMgcXVlIHBhc2FuIGRlIGV4cGVyaW1lbnRhbGVzIGEgZXN0YWJsZXMuIFtFc3RlIHBvc3RdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvbGlmZWN5Y2xlLykgbXVlc3RyYSBsb3MgZGlmZXJlbnRlcyBlc3RhZG9zIGRlIHVuYSBmdW5jacOzbi4gRWwgZXN0YWRvIGFjdHVhbCBkZSB1bmEgZnVuY2nDs24gc2Ugc3VlbGUgY29ub2NlciBwb3Igc3UgZGlzdGludGl2byBvICIqKmJhZGdlKioiLiBQb3IgZWplbXBsbywgZW4gZWwgc2lndWllbnRlIHR3ZWV0IHNlIGV4cGxpY2EgbGEgZGlmZXJlbmNpYSBlbnRyZSB1bmEgZnVuY2nDs24gY3V5byBiYWRnZSBlcyBgZGVwcmVjYXRlZGAgZGUgb3RyYSBmdW5jacOzbiBjdXlvIGJhZGdlIGVzIGBzdXBlcnNlZGVkYDoKCjxibG9ja3F1b3RlIGNsYXNzPSJ0d2l0dGVyLXR3ZWV0Ij48cCBsYW5nPSJlbiIgZGlyPSJsdHIiPldoeSA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL2hhZGxleXdpY2toYW0/cmVmX3NyYz10d3NyYyU1RXRmdyI+QGhhZGxleXdpY2toYW08L2E+IGFuZCB0ZWFtIHN3aXRjaGVkIGZyb20gY2FsbGluZyBmdW5jdGlvbnMg4oCccmV0aXJlZOKAnSB0byDigJxzdXBlcnNlZGVk4oCdIC0g4oCcV2hlbiBwZW9wbGUgaGVhcmQgYSBmdW5jdGlvbiB3YXMgcmV0aXJlZCB0aGV5IHRob3VnaHQgd2Ugd2VyZSBnb2luZyB0byB0YWtlIGl0IG91dCBiYWNrIGFuZCBzaG9vdCBpdCBpbiB0aGUgaGVhZC7igJ0gPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9oYXNodGFnL3JzdHVkaW9jb25mP3NyYz1oYXNoJmFtcDtyZWZfc3JjPXR3c3JjJTVFdGZ3Ij4jcnN0dWRpb2NvbmY8L2E+IDxhIGhyZWY9Imh0dHBzOi8vdC5jby80NjJoYk9Ta1J2Ij5waWMudHdpdHRlci5jb20vNDYyaGJPU2tSdjwvYT48L3A+Jm1kYXNoOyBFbWlseSBSb2JpbnNvbiAoQHJvYmluc29uX2VzKSA8YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL3JvYmluc29uX2VzL3N0YXR1cy8xMjIyOTcyNDY5NjUwMTUzNDcyP3JlZl9zcmM9dHdzcmMlNUV0ZnciPkphbnVhcnkgMzAsIDIwMjA8L2E+PC9ibG9ja3F1b3RlPiA8c2NyaXB0IGFzeW5jIHNyYz0iaHR0cHM6Ly9wbGF0Zm9ybS50d2l0dGVyLmNvbS93aWRnZXRzLmpzIiBjaGFyc2V0PSJ1dGYtOCI+PC9zY3JpcHQ+IAoKCjxicj4KCllhIHZhbGUgZGUgZnVuY2lvbmVzLCBwZXJvIGhheSBhbGdvIHF1ZSBhIGFsZ3Vub3Mgb3Mgcm9uZGFyw6EgcG9yIGxhIGNhYmV6YS4gCgpDdWFuZG8gaGVtb3MgdmlzdG8gbGEgYXl1ZGEgZGUgbGEgZnVuY2nDs24gYHNxcnQoKWAsIHPDs2xvIHRlbsOtYSB1biBhcmd1bWVudG8gYHhgLCB5IGVzdGUgYXJndW1lbnRvIHRlbsOtYSBxdWUgc2VyLCBzZWfDum4gbGEgYXl1ZGEsIHVuIHZlY3RvciAobyBhcnJheV5bVW4gYXJyYXkgZXMgbXV5IHNpbWlsYXIgYSBtYXRyaXgsIHPDs2xvIHF1ZSB1bmEgbWF0cml6IHNvbG8gcHVlZGUgdGVuZXIgMiBkaW1lbnNpb25lcywgZmlsYXMgeSBjb2x1bW5hcywgbWllbnRyYXMgcXVlIHVuIGFycmF5IHB1ZWRlIHRlbmVyIG3DoXMgZGUgMiBkaW1lbnNpb25lczsgZXMgZGVjaXIsIGxhcyBhcnJheSBzb24gbWF0cmljZXMgbXV0aWRpbWVuc2lvbmFsZXNdKSBkZSBuw7ptZXJvcy4KCioqUEVSTyoqIG5vc290cm9zIG5vIGludHJvZHVqaW1vcyB1biB2ZWN0b3IgZW4gYHNxcnQoKWAsIHNpbm8gcXVlIGludHJvZHVqaW1vcyB1biBzw7NsbyBuw7ptZXJvLiBTw60sIGVzIGNpZXJ0bywgaW50cm9kdWppbW9zIHVuIHPDs2xvIG7Dum1lcm8sIHBlcm8gUiBubyBlbnRpZW5kZSBsbyBxdWUgZXMgdW4gZXNjYWxhciwgcGFyYSDDqWwgdW4gbsO6bWVybyBhaXNsYWRvIGVzIHVuIHZlY3RvciwgcGFyYSBSIGVsIG7Dum1lcm8gNCBlcyB1biB2ZWN0b3IgY29uIHVuIHPDs2xvIGVsZW1lbnRvLCBlbCBuw7ptZXJvIDQuCgpGw61qYXRlIHF1ZSBlbCB2YWxvciBxdWUgaW50cm9kdXpjYW1vcyBlbiBgc3FydCgpYCB0aWVuZSBxdWUgY3VtcGxpciBkb3MgcmVxdWlzaXRvcywgc2VyIHVuIHZlY3RvciB5IHF1ZSBzdXMgdmFsb3JlcyBzZWFuIG51bcOpcmljb3MuIEVzdGFzIGRvcyBpZGVhcyBoYXkgcXVlIHRlbmVybGFzIGNsYXJhcyBwYXJhIHRyYWJhamFyIGNvbiBSOgoKICAtIFRpcG9zIGRlIGRhdG9zOiBgc3FydCgpYCBzw7NsbyBhZG1pdGUgZGF0b3MgbnVtw6lyaWNvcywgcGVybyDCv3F1w6kgb3Ryb3MgdGlwb3MgZGUgZGF0b3MgcG9kZW1vcyB1dGlsaXphciBlbiBSPyBMbyBoZW1vcyB2aXN0byBlbiB1bmEgc2VjY2nDs24gYW50ZXJpb3IgKHRleHRvLCBsw7NnaWNvcywuLi4pIC4gUG9yIGVqZW1wbG8sIHNpIGVqZWN1dGFtb3MgYHNxcnQoIjk5IilgLCBSIG5vcyBkZXZvbHZlcsOhIHVuIGVycm9yLCBwb3JxdWUgYCI5OSJgIG5vIGVzIG51bcOpcmljbyBlcyB0ZXh0by4KICAgIAogIC0gRXN0cnVjdHVyYXMgZGUgZGF0b3M6IEVsIHZlY3RvciBlcyB1bmEgZGUgbGFzIGVzdHJ1Y3R1cmFzIGRlIGRhdG9zIHF1ZSAiZW50aWVuZGUgUiIsIHVuYSBkZSBsYXMgZXN0cnVjdHVyYXMgZW4gbGFzIHF1ZSBwdWVkZSBhbG1hY2VuYXIgZGF0b3MgeSBsdWVnbyB2b2x2ZXIgYSBlbmNvbnRyYXJsb3MsIHBlcm8gaGF5IG3DoXMgKG1hdHJpY2VzLCBsaXN0YXMsIGRhdGEgZnJhbWVzKQogICAgCkVuIGxhICBwcsOzeGltYSBzZWNjacOzbiBzZSBwcmVzZW50YW4gbGFzIGRpZmVyZW50ZXMgZXN0cnVjdHVyYXMgZGUgZGF0b3MgcXVlIHRpZW5lIFIgcGFyYSBhbG1hY2VuYXIgZGF0b3MsIHBlcm8gZW4gZWwgY3Vyc28gdmFtb3MgYSBjZW50cmFybm9zIHByaW5jaXBhbG1lbnRlIGVuIHVuYSBlc3RydWN0dXJhIGRlIGRhdG9zLCBsb3MgZGF0b3MgdGFidWxhcmVzLCBxdWUgZW4gUiBzZSBhbG1hY2VuYW4gZW4gdW5hIGVzdHJ1Y3R1cmEgbGxhbWFkYSBgZGF0YSBmcmFtZWAuIAoKRW4gY2xhc2UgeWEgaGVtb3MgdHJhYmFqYWRvIGNvbiBkYXRhLmZyYW1lcywgZXMgbGEgZXN0cnVjdHVyYSBkZSBkYXRvcyBhIGxhIHF1ZSBlc3RhbW9zIG3DoXMgYWNvc3R1bWJyYWRvcywgdW5hIHRhYmxhIGRlIGRhdG9zIGNvbiBmaWxhcyB5IGNvbHVtbmFzLiBTb24gbGFzIHTDrXBpY2FzIHRhYmxhcyBkZSBFeGNlbCwgY29uIGxhcyB2YXJpYWJsZXMgZW4gY29sdW1uYXMgeSBsYXMgb2JzZXJ2YWNpb25lcyBlbiBmaWxhcy4gRXMgbXV5IGbDoWNpbCBlIGludHVpdGl2byBwb3JxdWUgZXN0YW1vcyBhY29zdHVtYnJhZG9zIGEgZWxsYXMsIGVzIGxvIHF1ZSBlc3RhbW9zIGFjb3N0dW1icmFkb3MgYSB0cmFiYWphciBlbiBjaWVuY2lhcyBzb2NpYWxlcy4gCgpJZ3VhbCBkZSBpbnR1aXRpdm8gcmVzdWx0YSBlbCBoZWNobyBkZSBxdWUgaGF5IHZhcmlhYmxlcyBudW3DqXJpY2FzLCBjb21vIHBvciBlamVtcGxvIGVsIG7Dum1lcm8gZGUgcMOhZ2luYXMgZGUgdW4gbGlicm8sIHkgdmFyaWFibGVzIGN1eW9zIHZhbG9yZXMgc29uIHRleHRvOyBwb3IgZWplbXBsbyBlbCB0w610dWxvIG8gZWwgYXV0b3IgZGVsIGxpYnJvLgoKPGJyPgoKIyMgRXN0cnVjdHVyYXMgZGUgZGF0b3MgZW4gUgoKClIgZXMgdW4gbGVuZ3VhamUvcHJvZ3JhbWEvZW50b3JubyBwYXJhIGhhY2VyIGVzdGFkw61zdGljYSBwb3IgbG8gcXVlIGdlbmVyYWxtZW50ZSB0cmFiYWphcmVtb3MgY29uIGRhdG9zLiBQZXJvIGxvcyBkYXRvcyB0aWVuZW4gcXVlIGVzdGFyIGFsbWFjZW5hZG9zIGVuIG9iamV0b3MuIElndWFsIHF1ZSBlbiBtYXRlbcOhdGljYXMgcG9kZW1vcyBhbG1hY2VuYXIgZGF0b3MgZW4gZGV0ZXJtaW5hZGFzIGVzdHJ1Y3R1cmFzIGNvbW8gdmVjdG9yZXMgbyBtYXRyaWNlcywgZW4gUiBvY3VycmUgbG8gbWlzbW8sIGxvcyBkYXRvcyBzZSBhbG1hY2VuYW4gZW4gb2JqZXRvcy4gUEVSTyBoYXkgZGlzdGludG9zIHRpcG9zIGRlIG9iamV0b3MgbyBlc3RydWN0dXJhcyBkZSBkYXRvc15bQXF1w60gcHVlZGVzIGNvbnN1bHRhciBsYSBlc3BlY2lmaWNhY2nDs24gb2ZpY2lhbCBkZWwgbGVuZ3VhamUgUjogaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvZG9jL21hbnVhbHMvci1yZWxlYXNlL1ItbGFuZy5odG1sXS4KClBhcmEgYWxtYWNlbmFyIGNvbmp1bnRvcyBkZSB2YWxvcmVzIG8gZGF0b3MsIFIgdGllbmUgZGVmaW5pZGFzIGRldGVybWluYWRhcyBlc3RydWN0dXJhcyBkZSBkYXRvcy4gwr9DdcOhbnRhcz8gwr9DdcOhbGVzPyDCv0PDs21vIHNlIGxsYW1hbj8KClB1ZXMgaGF5IHZhcmlhcyBmb3JtYXMgZGUgY29udGFybG8sIHBhcmEgSGFkbGV5IFdpY2toYW0gc8OzbG8gaGF5IHVuYSBlc3RydWN0dXJhIGRlIGRhdG9zLCBsb3MgdmVjdG9yZXMuIFPDs2xvIHF1ZSBlc3RvcyB2ZWN0b3JlcyBwdWVkZW4gdGVuZXIgZGl2ZXJzYXMgcHJvcGllZGFkZXMuIEhheSB2ZWN0b3JlcyBhdMOzbWljb3MgeSB2ZWN0b3JlcyByZWN1cnNpdm9zLiBTZWd1cm8gcXVlIHRpZW5lIHJhesOzbiwgcGVybyBhdW5xdWUgc2VhIHVuIHBvY28gbWVub3MgcHJlY2lzbywgZXMgbXVjaG8gbcOhcyDDunRpbCBleHBsaWNhcmxvLCBhbCBtZW5vcyBhbCBwcmluY2lwaW8sIGRlIG90cmEgZm9ybWEuCgoKClBvZGVtb3MgcGVuc2FyIHF1ZSBsYXMgcHJpbmNpcGFsZXMgZXN0cnVjdHVyYXMgZGUgZGF0b3MgZW4gUiBzb246CgotIHZlY3RvcmVzCgotIG1hdHJpY2VzCgotIGFycmF5cyAobWF0cmljZXMgbXVsdGlkaW1lbnNpb25hbGVzKQoKLSBsaXN0YXMKCi0gZGF0YS5mcmFtZXMKCgpMYXMgcXVlIG3DoXMgdXRpbGl6YXJlbW9zIGVuIGVsIGN1cnNvIHNvbiBsb3MgZGF0YS5mcmFtZXMsIHkgZW4gc2VndW5kbyBsdWdhciBsYXMgbGlzdGFzLiBTw60sIHBlcm8gZW4gcmVhbGlkYWQgZXN0YXMgMiBlc3RydWN0dXJhcyAoZGF0YS5mcmFtZXMgeSBsaXN0YXMpIHNvbiBlbiByZWFsaWRhZCBhZ3J1cGFjaW9uZXMgZGUgdmVjdG9yZXM7IGVzIGRlY2lyLCBsYSBlc3RydWN0dXJhIGLDoXNpY2EsIGxhIG3DoXMgaW1wb3J0YW50ZSBlbiBSIHNvbiBsb3MgdmVjdG9yZXMuIFNvbiBsb3MgbcOhcyBpbXBvcnRhbnRlcyBwb3JxdWUgZWwgcmVzdG8gZGUgZXN0cnVjdHVyYXMgc2UgY29uc3RydXllbiBhIHBhcnRpciBkZSBncnVwb3MgZGUgdmVjdG9yZXMgbyBhw7FhZGllbmRvIGFsZ3VuYSBwcm9waWVkYWQgYWRpY2lvbmFsLCBvIGF0cmlidXRvLCBhIHVuIHZlY3Rvci4KCgo8YnI+CgoKIyAyLiBWZWN0b3JlcwoKTGEgKiplc3RydWN0dXJhIGRlIGRhdG9zIGZ1bmRhbWVudGFsIGVuIFIgZXMgZWwgdmVjdG9yKipeW0RlIGhlY2hvIGVuIFIgbm8gZXhpc3RlbiBsb3MgZXNjYWxhcmVzOiBwYXJhIFIgdW4gbsO6bmljbyBudW1lcm8gZXMgdW4gdmVjdG9yIGRlIDEgZWxlbWVudG9dLiBFbiBSLCB1biB2ZWN0b3IgZXMgdW5hIGVzdHJ1Y3R1cmEgZGUgZGF0b3MgcXVlIHNpcnZlIHBhcmEgYWxtYWNlbmFyIHVuIGNvbmp1bnRvIG9yZGVuYWRvIGRlIHZhbG9yZXMgbyBlbGVtZW50b3MuIFVuIHZlY3RvciBwdWVkZSBjb250ZW5lciBjdWFscXVpZXIgbsO6bWVybyBkZSBlbGVtZW50b3M7IHNpbiBlbWJhcmdvLCAqKnRvZG9zIGxvcyBlbGVtZW50b3MgZGViZW4gc2VyIGRlbCBtaXNtbyB0aXBvKiouIFBvciBlamVtcGxvLCB1biB2ZWN0b3Igbm8gcHVlZGUgY29udGVuZXIgbsO6bWVyb3MgeSB0ZXh0by4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CiMtIFZlcnNpb24gMyBvZiBTLCBhbmQgdGhlcmVmb3JlIFIsIHJldGFpbmVkIHZlY3RvcnMgYXMgdGhlIGNvcmUgZGF0YSBzdHJ1Y3R1cmUuIEFuIGV4dGVuc2libGUgZmFjaWxpdHkgZm9yIGRlZmluaW5nIGdlbmVyYWwgb2JqZWN0IHN0cnVjdHVyZSB3YXMgYnVpbHQgb24gdGhpcyB0aHJvdWdoIHR3byBmZWF0dXJlcy4gVmVjdG9ycyBjb3VsZCBiZSBvZiB0eXBlICJsaXN0Iiwgd2l0aCBlbGVtZW50cyBiZWluZyBhcmJpdHJhcnkgb2JqZWN0czsgYW5kIGFueSB2ZWN0b3IgY291bGQgaGF2ZSBhIG5hbWVkIGxpc3Qgb2YgYXR0cmlidXRlcyB0byBzcGVjaWZ5IGFkZGl0aW9uYWwgaW5mb3JtYXRpb24uCgojLSAgVmVjdG9ycyB3aXRoIGF0dHJpYnV0ZXMgc3VwcG9ydGVkIGFuIGV4dGVuc2libGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3BlY2lhbGl6ZWQgc3RydWN0dXJlIHRvIHNpbXBsZSBvYmplY3RzLCBhdCBmaXJzdCBpbXBsaWNpdGx5IGFuZCBsYXRlciBleHBsaWNpdGx5LiBPYmplY3RzIGVzc2VudGlhbCB0byBkYXRhIHNjaWVuY2UsIHN1Y2ggYXMgbWF0cmljZXMgYW5kIG11bHRpLXdheSBhcnJheXMsIGNvdWxkIGJlIGNvbnNpZGVyZWQgYnVpbHQtaW4gd2l0aG91dCByZXF1aXJpbmcgYSBwcmltaXRpdmUgaW1wbGVtZW50YXRpb24uIEEgZ2VuZXJhbCBhcnJheSBpcyBhIHZlY3RvciB0aGF0IGhhcyBhbiBhdHRyaWJ1dGUgbmFtZWQgImRpbSIgY29udGFpbmluZyBhbiBpbnRlZ2VyIHZlY3RvciBvZiB0aGUgZGltZW5zaW9ucy4gVGh1cyBhcnJheXMgYXV0b21hdGljYWxseSBjYW4gaGF2ZSBhbnkgdHlwZSBvZiBkYXRhIGFuZCBjYW4gYWxsb3cgZm9yIG1pc3NpbmcgdmFsdWVzLiBTZXBhcmF0aW5nIHRoZSBkYXRhIGZyb20gdGhlIGF0dHJpYnV0ZXMgaXMgaGVscGZ1bCBmb3IgZGF0YSBhbmFseXNpcywgc2VwYXJhdGluZyB0aGUgbG9naWMgdGhhdCBkZXBlbmRzIG9uIHRoZSBzdHJ1Y3R1cmUgZnJvbSB0aGUgY29tcHV0YXRpb25zIG9uIHRoZSBzcGVjaWZpYyB0eXBlIG9mIGRhdGEuCmBgYAoKCgpFc3RhIGVzdHJ1Y3R1cmEgcGFyYSBhbG1hY2VuYXIgZGF0b3MgZW4gUiBlcyBtdXkgZsOhY2lsIGRlIGVudGVuZGVyIHBvcnF1ZSBzb24gbG9zIHTDrXBpY29zIHZlY3RvcmVzIHF1ZSBjb25vY2Vtb3MgZGUgbWF0ZW3DoXRpY2FzLgoKClZhbW9zIGEgY3JlYXIgbnVlc3RybyBwcmltZXIgdmVjdG9yIGVuIFI7IHBhcmEgZWxsbyB1c2Ftb3MgbGEgZnVuY2nDs24gYGMoKWA6CgoKYGBge3J9CmFhIDwtIGMoMywgMjIsIDYpCmFhCmBgYAoKQ29tbyB2ZW1vcyBjb24gbGEgZnVuY2nDs24gYGMoKWAgaGVtb3MgY29uY2F0ZW5hZG8gMyBuw7ptZXJvcyBwYXJhIGNvbnN0cnVpciB1biB2ZWN0b3IsIGVsIHZlY3RvciBhYS4KCgpgYGB7cn0KaXMudmVjdG9yKGFhKQpgYGAKClVuYSBkZSBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBxdWUgdGllbmUgZXN0YSBlc3RydWN0dXJhIGRlIGRhdG9zIGVzIHF1ZSBsb3MgZWxlbWVudG9zIGRlIHVuIHZlY3RvciB0aWVuZW4gcXVlIHNlciB0b2RvcyBkZWwgbWlzbW8gdGlwby4gRW4gbnVlc3RybyBjYXNvIGVsIHZlY3RvciBgYWFgIHRpZW5lIDMgZWxlbWVudG9zLCB0b2RvcyBlbGxvcyBkZSB0aXBvIG51bcOpcmljbywgY29uY3JldGFtZW50ZSBkb3VibGVzLiBWZcOhbW9zbG8gY29uIGB0eXBlb2YoKWAKCgpgYGB7cn0KdHlwZW9mKGFhKQpgYGAKCgpDcmVlbW9zIGFob3JhIHVuIHZlY3RvciBjb24gZGF0b3MgdGV4dHVhbGVzIG8gY2FyYWN0ZXJlczoKCgpgYGB7cn0KYWEgPC0gYygiSG9sYSIsICJuw7ptZXJvIiwgIjkiKQppcy52ZWN0b3IoYWEpCnR5cGVvZihhYSkKYGBgCgpBaG9yYSB1biB2ZWN0b3IgZGUgYm9vbGVhbm9zOgoKCmBgYHtyfQphYSA8LSBjKEZBTFNFLCBUUlVFLCBGQUxTRSkKaXMudmVjdG9yKGFhKQp0eXBlb2YoYWEpCmBgYAoKQ29tbyBSIHRpZW5lIDQgdGlwb3MgZGUgZGF0b3MgZnVuZGFtZW50YWxlcyAoaW50ZWdlciwgZG91YmxlLCBjaGFyYWN0ZXIgeSBsw7NnaWNvKV5bRW4gcmVhbGlkYWQgNiBzaSBpbmNsdWltb3MgY29tcGxleCB5IHJhdyB0eXBlc10gc2UgcHVlZGVuIGNyZWFyIGNvbiBlbGxvcyA0IHRpcG9zIGRlIHZlY3RvcmVzOyBhIGVzdG9zIHRpcG9zIGZ1bmRhbWVudGFsZXMgZGUgdmVjdG9yZXMgc2UgbGVzIHN1ZWxlIGxsYW1hciBjb21vICJhdG9taWMgdmVjdG9ycyIuCgo8YnI+CgojIyBBdG9taWMgdmVjdG9ycyB2cy4gQXVnbWVudGVkIHZlY3RvcnMKCkVzdGUgdMOzcGljbyBubyBlcyBmw6FjaWwgZGUgZW50ZW5kZXIsIHNlZ3VyYW1lbnRlLCBpZ3VhbCBlbXBlesOhaXMgYSBlbnRlbmRlcmxvIGEgbWl0YWQgZGUgY3Vyc28sIHBlcm8gc2kgcXVlIGNvbnZpZW5lIHRlbmVyIGFsIG1lbm9zIHVuYSBsaWdlcmEgaWRlYSBzb2JyZSBlbGxvLCBwb3IgZXNvIGxvIGludHJvZHV6Y28gbcOtbmltYW1lbnRlLgoKQ29tbyBSIHRpZW5lIDQgdGlwb3MgZnVuZGFtZW50YWxlcyBkZSBkYXRvcywgY29uIGVsbG9zIHBvZGVtb3MgY29uc3RydWlyIDQgdGlwb3MgZGUgdmVjdG9yZXMgZnVuZGFtZW50YWxlcyBvICJhdG9taWMgdmVjdG9ycyIuIEVsIHRpcG8gZGUgZGF0b3MsIG9yIG1vZGVzLCBkZWZpbmUgY8OzbW8gc2UgdmFuIGEgYWxtYWNlbmFyIGludGVybmFtZW50ZSBsb3MgdmFsb3Jlcy4gRWwgdGlwbyBkZSBsb3MgdmVjdG9yZXMgcG9kZW1vcyBvYnRlbmVybG8gY29uIGxhIGZ1bmNpw7NuIGB0eXBlb2YoKWAuIAoKClBhcmEgdmVyIGRlIHF1ZSB0aXBvIChmdW5kYW1lbnRhbCkgZXMgdW4gdmVjdG9yLCBkaXNwb25lbW9zIGRlIGxhIGZ1bmNpw7NuIGB0eXBlb2YoKWAKCmBgYHtyfQp0eXBlb2YoNCkKdHlwZW9mKDRMKQp0eXBlb2YoIjQiKQp0eXBlb2YoVFJVRSkKYGBgCgoKKipQRVJPKiogcmVzdWx0YSBxdWUgc29icmUgbGEgYmFzZSBkZSBlc3RvcyA0IHRpcG9zIGRlIHZlY3RvcmVzIGZ1bmRhbWVudGFsZXMgc2UgaGFuIGNvbnN0cnVpZG8gb3Ryb3MgdGlwb3MgZGUgdmVjdG9yZXMsIHF1ZSBjb21vIHNvbiBkZXJpdmFkb3MgZGUgbG9zIGZ1bmRhbWVudGFsZXMsIHNlIGxlcyBzdWVsZSBsbGFtYXIgKGRlcml2ZWQgb3IpICoqYXVnbWVudGVkIHZlY3RvcnMqKl5bQXVnbWVudGVkIHBvcnF1ZSBzZSBjb25zdHJ1eWVuLCBhIHBhcnRpciBsb3MgdmVjdG9yZXMgZnVuZGFtZW50YWxlcywgYcOxYWRpZW5kbyBpbmZvcm1hY2nDs24gZXh0cmEgZW4gbG9zIGF0cmlidXRvcyBkZWwgdmVjdG9yIG9yaWdpbmFsIG8gZnVuZGFtZW50YWwhISBTaSBjdWVzdGEgdW4gcG9jbyBlbnRlbmRlcmxvISEgQ29uIHRlbmVyIHVuYSBsaWdlcmEgaWRlYSBlcyBzdWZpY2llbnRlXS4KCgpWb3kgYSBleHBsaWNhcmxvIHVuIHBvY28sIHBlcm8gYW50ZXMgaGF5IHF1ZSBzYWJlciBxdWUgbG9zIG9iamV0b3MsIHBvciBlamVtcGxvIGxvcyB2ZWN0b3JlcywgZW4gUiBwdWVkZW4gdGVuZXIgImF0cmlidXRvcyIuIFBvZGVtb3MgcGVuc2FyIHF1ZSBsb3MgYXRyaWJ1dG9zIHNvbiB1bmEgZm9ybWEgZGUgcG9kZXIgYWxtYWNlbmFyIGluZm9ybWFjacOzbiBhZGljaW9uYWwgKG1ldGFkYXRvcykgZW4gdW4gb2JqZXRvIGRlIFIuIE9LLCBlc3RvcyBhdHJpYnV0b3Mgc29uIGxvcyBxdWUgcGVybWl0ZW4gY3JlYW4gbnVldm9zIHRpcG9zIGRlIHZlY3RvcmVzIChhdWdtZW50ZWQgdmVjdG9ycykuCgpMb3MgYXVnbWVudGVkIHZlY3RvcnMgbyB2ZWN0b3JlcyBkZXJpdmFkb3MgdGFtYmnDqW4gc2UgYWxtYWNlbmFyw6FuIGludGVybmFtZW50ZSBjb21vIG51bcOpcmljb3MgbyBjaGFyYWN0ZXIgbyBsb2dpY2FsLCAqKlBFUk8qKiBhbCBjb250ZW5lciBpbmZvcm1hY2nDs24gYWRpY2lvbmFsLCBvIGF0cmlidXRvcywgaGFjZSBxdWUgZXN0ZSB0aXBvIGVzcGVjaWFsIGRlIHZlY3RvcmVzIHB1ZWRhbiBzZXIgaWRlbnRpZmljYWRvcyB5IHRyYXRhZG9zIGRlIGZvcm1hIGVzcGVjaWFsIHBvciBjaWVydGFzIGZ1bmNpb25lcyBkZSBSLiBFcyBkZWNpciwgbG9zIGF1Z21lbnRlZCB2ZWN0b3JzIHNvbiBjb21vIGxvcyBhdG9taWMgdmVjdG9ycyAodmVjdG9yZXMgZnVuZGFtZW50YWxlcyksIHNlIGNvbnN0cnV5ZW4gYSBwYXJ0aXIgZGUgZWxsb3MsIFBFUk8gdGllbmVuIGluZm9ybWFjacOzbiBhZGljaW9uYWwgKG1ldGFkYXRvcykgYWxtYWNlbmFkYXMgZW4gc3VzIGF0cmlidXRvcy4KClNlIHB1ZWRlIHBlbnNhciBxdWUgbG9zIGF0cmlidXRvcyBzb24gdW5hICJuYW1lZCBsaXN0IG9mIHZlY3RvcnMiIHF1ZSBzZSBhZGp1bnRhIGEgdW4gb2JqZXRvLiBQdWVkZXMgdmVyIGxvcyBhdHJpYnV0b3MgZGUgdW4gb2JqZXRvIGNvbiBsYSBmdW5jacOzbiBgYXR0cmlidXRlcygpYC4KCkVudHJlIGxvcyBhdHJpYnV0b3MgcXVlIHBvZGVtb3MgZGVmaW5pciwgaGF5IHVubyBlc3BlY2lhbCBsbGFtYWRvIGNsYXNzLCBxdWUgcGVybWl0ZSBkZWZpbmlyIGxhIGBvYmplY3TigJlzIGNsYXNzYCB5IHF1ZSBlcyBlbCBhdHJpYnV0byBxdWUgdmEgYSBoYWNlciBxdWUgYWxndW5vcyB2ZWN0b3JlcyBzZSBjb21wb3J0ZW4gZGUgbWFuZXJhIGRpc3RpbnRhIGFudGUgYWxndW5hcyBmdW5jaW9uZXM7IHBvciBlamVtcGxvIHF1ZSBzZSBpbXByaW1hbiBvIGdyYWZpcXVlbiBkZSBmb3JtYSB1biBwb2NvIGVzcGVjaWFsLiBQb2RlbW9zIHZlciBsYSBjbGFzZSAoY2xhc3MpIGRlIHVuIG9iamV0byBjb24gbGEgZnVuY2nDs24gYGNsYXNzKClgLgoKCkVudHJlIGxvcyB2ZWN0b3JlcyBhdW1lbnRhZG9zLCBIYXkgNCB0aXBvcyBpbXBvcnRhbnRlcyBxdWUgY29udmllbmUgY29ub2NlcjogbG9zIGZhY3RvcmVzLCBsb3MgZmFjdG9yZXMgb3JkZW5hZG9zLCBsYXMgZmVjaGFzIChkYXRlKSB5IGxhcyBkYXRlLXRpbWUgKG8gcG9zaXhjdCkuICAKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBldmFsID0gVFJVRSwgZmlnLmNhcCA9ICJDcmVhZG8gcG9yIElsZGlrbyBDemVsbGVyIHkgR3JhaGFtIFBhcnNvbnMiLCBmaWcuYXNwID0gNC8yLCBvdXQud2lkdGggPSAiOTAlIiwgZmlnLmFsaWduID0gImNlbnRlciJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0L2JhYTE5ZDBlYmY5Yjk3OTQ5YTdhZDI1OWIyOWExYzRhZTAzMWM4ZTIvOGU5YjgvZGlhZ3JhbXMvdmVjdG9ycy9zdW1tYXJ5LXRyZWUtczMtMS5wbmciKQpgYGAKClBhcmEgdGVuZXIgdW4gZWplbXBsbyBxdWUgYXl1ZGUgYSBlbnRlbmRlciBsbyBhbnRlcmlvciB1YXNhcsOpIGVsIHZlY3RvciBgaXJpcyRTcGVjaWVzYC4gRWwgIHZlY3RvciBgaXJpcyRTcGVjaWVzYCBlcyBlbiByZWFsaWRhZCB1biB2ZWN0b3IgZGUgaW50ZWdlcnMgcGVybywgY29tbyBhZGVtYXMgZGUgbG9zIHZhbG9yZXMgZGUgbG9zIGVudGVyb3MsIHRpZW5lIG1ldGFkYXRvcyBhbG1hY2VuYWRvcyBlbiBzdXMgYXRyaWJ1dG9zLCBzdSBjbGFzcyBlcyBmYWN0b3IuICBTw60sIGNvbXBsaWNhZG8gZW50ZW5kZXJsbyBhIGxhIHByaW1lcmEsIFBFUk8gbG8gcXVlIHNpIHF1ZSBzZSBvcyB0aWVuZSBxdWUgcXVlZGFyIGVzIHF1ZSBhIHZlY2VzIHVzYXJlbW9zIGZhY3RvcmVzLiBMbyBpbXBvcnRhbnRlIHNlcsOhIHNhYmVyIHVzYXIgbG9zIGZhY3RvcmVzIGNvcnJlY3RhbWVudGUgeSBubyB0YW50byBzYWJlciBxdcOpIGVzIHVuIGZhY3Rvcl5bTG8gaW1wb3J0YW50ZSBhIHNhYmVyIGRlIHVuIGZhY3RvciBlcyBxdcOpIHNlIHB1ZWRlIGhhY2VyIGNvbiDDqWwsIGxvIHZlcmVtb3MhISwgeSBubyB0YW50byBzYWJlciBxdWUgdW4gZmFjdG9yIGVzIHJlYWxpZGFkIHVuIHZlY3RvciBkZSBpbnRlZ2VycyBwZXJvIHF1ZSBjb21vIHRpZW5lIHVuIGF0cmlidXRvIHF1ZSBkaWNlIHF1ZSBzdSBjbGFzcyBlcyBmYWN0b3IgZW50b25jZXMgc2UgY29tcG9ydGEgZGUgZm9ybWEgZXNwZWNpYWwgYW50ZSBhbGd1bmFzIGZ1bmNpb25lcy5dLiBMbyB2ZXJlbW9zISEKCgpgYGB7cn0KdHlwZW9mKGlyaXMkU3BlY2llcykKYXR0cmlidXRlcyhpcmlzJFNwZWNpZXMpCmNsYXNzKGlyaXMkU3BlY2llcykKYGBgCgoKCiMjIENvZXJjacOzbgoKWWEgZGlqaW1vcyBxdWUsIGVuIFIsIGxvcyBlbGVtZW50b3MgZGUgdW4gdmVjdG9yIHRpZW5lbiBxdWUgc2VyIHRvZG9zIGRlbCBtaXNtbyB0aXBvOyBwb3IgZXNvIGRlY2ltb3MgcXVlIGxvcyB2ZWN0b3JlcyBzb24gdW5hIGVzdHJ1Y3R1cmEgZGUgZGF0b3MgaG9tb2fDqW5lYS4KCkJpZW4sIHBlcm8gwr9xdcOpIG9jdXJyZSBzaSBlbiB1biB2ZWN0b3IgaGF5IGVsZW1lbnRvcyBkZSBkaXN0aW50byB0aXBvPwoKcG9yIGPDs21vIGVzdMOhbiBkZWZpbmlkb3MgaW50ZXJuYW1lbnRlIGxvcyB2ZWN0b3Jlcywgc8OzbG8gcHVlZGVuIGNvbnRlbmVyIGVsZW1lbnRvcyBkZWwgbWlzbW8gdGlwbywgcGVybyBzaSBwb3IgZXJyb3IgaW50cm9kdWNpbW9zIHZhbG9yZXMgZGUgZGlzdGludG8gdGlwbywgUiBjb252ZXJ0aXLDoSB0b2RvcyBzdXMgdmFsb3JlcyBhIHVuIMO6bmljbyB0aXBvLiBGb3J6YXLDoSBhIHF1ZSBzdXMgZWxlbWVudG9zIHNlYW4gZGVsIG1pc21vIHRpcG8uIEVzdGEgZm9ybWEgZGUgcHJvY2VkZXIgZW4gUiwgY29uc2lzdGVudGUgZW4gdHJhbnNmb3JtYXIgdG9kb3MgbG9zIGVsZW1lbnRvcyBkZSB1biB2ZWN0b3IgYSB1biBtaXNtbyB0aXBvIHNlIGxsYW1hICoqY29lcmNpw7NuKiouCgoKwr9BIHF1ZSB0aXBvIGxvcyBjb252aWVydGU/IFB1ZXMgYWwgbWVub3MgZmxleGlibGUuIFZlw6Ftb3NsbyBjb24gdmFyaW9zIGVqZW1wbG9zOgoKCmBgYHtyfQphYSA8LSBjKDQsIDYsICAiSG9sYSIpCmBgYAoKRW4gbGEgZXhwcmVzacOzbiBkZSBhcnJpYmEgaGVtb3MgcHVlc3RvIGVuIGVsIHZlY3RvciBgYWFgIHRyZXMgZWxlbWVudG9zLCAyIG7Dum1lcm9zIHkgdW4gc3RyaW5nLiBObyBwdWVkZSBzZXIsIGVzdGFtb3MgdmlvbGFuZG8gbGFzIHJlZ2xhcyBpbnRlcm5hcyBkZSBSIHBhcmEgbG9zIHZlY3RvcmVzOiBsb3MgdmVjdG9yZXMgc8OzbG8gcHVlZGVuIHRlbmVyIGVsZW1lbnRvcyBkZWwgbWlzbW8gdGlwby4KCkVudG9uY2VzLCDCv3BvciBxdcOpIFIgbm8gbm9zIGRldnVlbHZlIHVuIG1lbnNhamUgZGUgZXJyb3I/IFBvcnF1ZSBsbyBxdWUgaGFjZSBlcyBjb252ZXJ0aXIgbG9zIHZhbG9yZXMgZGUgYGFhYCBhIHVuIMO6bmljbyB0aXBvLCDCv2EgY3VhbD8gdmXDoW1vc2xvIGNvbiBgdHlwZW9mKClgLgoKCmBgYHtyfQphYSA8LSBjKDQsIDYsICJIb2xhIikKaXMudmVjdG9yKGFhKQp0eXBlb2YoYWEpCmFhICAgICAgICAgICAgICAgIy0gwr9xdcOpIGhhIHBhc2Fkbz8KYGBgCgpGw61qYXRlIGVuIGxvcyByZXN1bHRhZG9zLiBoYSBjb252ZXJ0aWRvIGxvcyAyIG7Dum1lcm9zIGEgY2FyYWN0ZXJlcy4gRWwgcHJpbWVyIGVsZW1lbnRvIGRlIGBhYWAgbm8gZXMgZWwgbsO6bWVybyBjdWF0cm8gc2lubyBlbCBzdHJpbmcgNC4gwr9WZWlzIHF1ZSBlc3TDoSBlbnRyZSBjb21pbGxhcz8gTG8gaGEgY29udmVydGlkbyBlbiBlbCBjYXLDoWN0ZXIgYCI0ImAuCgo8YnI+CgpgYGB7cn0KYWEgPC0gYyhUUlVFLCA0TCwgNCwgIkhvbGEiKQp0eXBlb2YoYWEpCmFhIDwtIGMoVFJVRSwgNEwsIDQpCnR5cGVvZihhYSkKYWEgPC0gYyhUUlVFLCA0TCkKdHlwZW9mKGFhKQpgYGAKCkbDrWphdGUgcXVlIGNvbiBlbCBjaHVuayBkZSBhcnJpYmEgcHVlZGVzIGluZmVyaXIgbGFzIHJlZ2xhcyBkZSBjb252ZXJzacOzbiBxdWUgdXNhIFIuIGBjaGFyYWN0ZXIgPiBkb3VibGUgPiBpbnRlZ2VyID4gbG9naWNhbGAuIFNpIGVuIGVsIHZlY3RvciBoYXkgdW4gdmFsb3IgZGUgdGV4dG8sIHRvZG9zIGxvcyB2YWxvcmVzIHNlIGNvbnZlcnRpcsOhbiBhbCB0aXBvIGNoYXJhY3Rlci4gCgpMb3MgdmFsb3JlcyBkZSB0aXBvIGNhcsOhY3RlciBzb24gbG9zIG1lbm9zIGZsZXhpYmxlcywgcGllbnNhIHF1ZSBubyBoYXkgbmluZ3VuYSBmb3JtYSBkZSBjb252ZXJ0aXIgIkhvbGEiIGVuIHVuIG7Dum1lcm8gbyBlbiBib29sZWFuLiBBbCBjb250cmFyaW8gc8OtIHNlIHB1ZWRlOiBlbCBuw7ptZXJvIDkgc2llbXByZSBzZSBwdWVkZSBjb252ZXJ0aXIgYWwgY2Fyw6FjdGVyICI5Ii4gTG9zIGJvb2xlYW5zIChUUlVFIHkgRkFMU0UpIHNlIHB1ZWRlbiB0cmFuc2Zvcm1hciB0YW50byBlbiB0ZXh0byBjb21vIGVuIG51bcOpcmljbzogVFJVRSBwYXNhcsOhIGEgc2VyIDEgeSBGQUxTRSA9IDAuCgo8YnI+CgojIyMgQ29lcmNpw7NuIGV4cGzDrWNpdGEKCkEgdmVjZXMgdGVuZHJlbW9zIHVuIHZlY3RvciBkZSB1biB0aXBvLCBwb3IgZWplbXBsbyBudW3DqXJpY28sIHBlcm8gbm9zIGd1c3RhcsOtYSBjb252ZXJ0aXJsbyBhIHBvciBlamVtcGxvIGNoYXJhY3Rlci4gwr9Qb2RlbW9zIGhhY2VybG8/IFPDrS4KCgpgYGB7cn0KYWEgPC0gYygxOjQpCmFhCmFhIDwtIGFzLmNoYXJhY3RlcihhYSkKYWEKYGBgCgpIYXkgdG9kYSB1bmEgZmFtaWxpYSBkZSB1bmNpb25lcyBwYXJhIGhhY2VyIGVzdGFzIGNvbnZlcnNpb25lcywgc2kgZXMgcXVlIHNvbiBwb3NpYmxlcywgeWEgcXVlLCBwb3IgZWplbXBsbywgbm8gcG9kcmVtb3MgcGFzYXIgdW4gdmVjdG9yIGRlIHN0cmluZ3MgYSBudW3DqXJpY28uCgpgYGByCmFzLmNoYXJhY3RlcigpICAgICAgICAgICAKYXMubG9naWNhbCgpICAgIAphcy5udW1lcmljKCkgICAKeSBhbGd1bmFzIG3DoXMgY29tbyBhcy5kYXRhLmZyYW1lKCkgLi4uLgpgYGAKCjxicj4KCgojIyBQcm9waWVkYWRlcyBkZSBsb3MgdmVjdG9yZXMKClRvZG8gdmVjdG9yIHRpZW5lIGRvcyBwcm9waWVkYWRlcyBjbGF2ZXM6CgogICAtIHN1IHRpcG8sIHF1ZSBwb2RlbW9zIHZlciBjb24gYHR5cGVvZigpYCAgCiAgIAogICAtIHN1IGxvbmdpdHVkLCBvcmRlbiBvIG7Dum1lcm8gZGUgZWxlbWVudG9zLCBxdWUgcG9kZW1vcyB2ZXIgY29uIGBsZW5ndGgoKWAgIAogICAKICAgCmBgYHtyfQphYSA8LSBjKDE6MTApCnR5cGVvZihhYSkKbGVuZ3RoKGFhKQphdHRyaWJ1dGVzKGFhKQpgYGAKICAgCiAKQWRlbWFzLCBsb3MgdmVjdG9yZXMgcHVlZGVuIHRlbmVyIG3DoXMgcHJvcGllZGFkZXMgbyBbYXRyaWJ1dG9zXShodHRwOi8vYWR2LXIuaGFkLmNvLm56L0RhdGEtc3RydWN0dXJlcy5odG1sI2F0dHJpYnV0ZXMpLiBFc3RhcyBwcm9waWVkYWRlcyBzb24gYWRpY2lvbmFsZXMsIG5vIHNvbiBuZWNlc2FyaWFzIHkgZ2VuZXJhbG1lbnRlIHNlIHVzYW4gcGFyYSBjb25zdHJ1aXIgdGlwb3MgZXNwZWNpYWxlcyBkZSB2ZWN0b3Jlcy4gU2UgZGVmaW5lbiBsb3MgYXRyaWJ1dG9zIGNvbiBsYSBmdW5jacOzbiBgYXR0cigpYCB5IGNvbiBgYXR0cmlidXRlcygpYCBzZSBwdWVkZW4gdmVyIGxvcyBhdHJpYnV0b3MgcXVlIHRpZW5lIHVuIGRldGVybWluYWRvIHZlY3Rvci4KCgpgYGB7cn0KYWEgPC0gYygxOjMpCmF0dHJpYnV0ZXMoYWEpCmF0dHIoYWEsICJhdHJpYnV0b18xIikgPC0gIlRoaXMgaXMgYSB2ZWN0b3IiCmF0dHJpYnV0ZXMoYWEpCmF0dHIoYWEsICJhdHJpYnV0b18yIikgPC0gYygicHJpbWVybyIsICJzZWd1bmRvIiwgInRlcmNlcm8iKQphYQpgYGAKClVuIGF0cmlidXRvIHF1ZSBhIHZlY2VzIHNlIHVzYSBlcyBwb25lciBub21icmVzIGEgbG9zIGVsZW1lbnRvcyBkZSB1biB2ZWN0b3IsIHNlIHB1ZWRlIGhhY2VyIGRpcmVjdGFtZW50ZSBhbCBjcmVhciBlbCB2ZWN0b3IgbyBkZXNwdcOpcyBjb24gYHNldE5hbWVzKClgIG8gY29uIGBzZXRfbmFtZXMoKWAKCmBgYHtyfQphYSA8LSBjKGEgPSAxLCBiID0gMiwgYyA9IDQpCmBgYAoKCgpgYGB7cn0KYWEgPC0gYygxLDIsNCkKYWEgPC0gc2V0TmFtZXMoYWEsIGxldHRlcnNbMTozXSkgICAgICAgCmFhICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKYGBgCgoKYGBge3J9CmFhIDwtIGMoMSwyLDQpCmFhIDwtIHJsYW5nOjpzZXRfbmFtZXMoYWEsIGMoImEiLCAiYiIsICJjIikpCmFhICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKYGBgCgoKPGJyPgoKIyMgU3Vic2V0dGluZyBjb24gdmVjdG9yZXMKCkVuIFIgcG9kZW1vcyBhZ3J1cGFyIHZhbG9yZXMgZW4gdmVjdG9yZXMuIFBlcmZlY3RvLCBwZXJvIGhhYnLDoSB2ZWNlcyBxdWUgbmVjZXNpdGVtb3MgbWFuaXB1bGFyIGxvcyB2ZWN0b3Jlcy4gUG9yIGVqZW1wbG8gbmVjZXNpdGFyZW1vcyBhY2NlZGVyIGEgZGV0ZXJtaW5hZG9zIGVsZW1lbnRvcyBkZSB1biB2ZWN0b3IsIHF1aXrDoXMgYSBsb3MgZWxlbWVudG9zIHF1ZSBzZWFuIG1heW9yZXMgcXVlIDUsIG8gYSBsb3MgcXVlIGVtcGllY2VuIGNvbiBsYSBsZXRyYSBBLCBldGMgZXRjLi4uIEVzdGUgdGlwbyBkZSBvcGVyYWNpb25lcyBzb2JyZSBsb3MgdmVjdG9yZXMgc2UgbGVzIGNvbm9jZSBnZW7DqXJpY2FtZW50ZSBjb21vICoqc3Vic2V0dGluZyoqOyBwb3JxdWUgbG8gcXVlIHNlIGhhY2UgZXMgY3JlYXIgc3ViY29uanVudG9zIGRlIHVuIHZlY3Rvci4gVmXDoW1vc2xvLgoKSGF5IHZhcmlvcyB0aXBvcyBkZSBzdWJzZXR0aW5nLCBwZXJvIGNvbiB2ZWN0b3JlcyBsb3MgcHJpbmNpcGFsZXMgc29uIDM6CgoKMSkgKipQb3NpdGl2ZSBJbmRleCoqOiBwb2RlbW9zIHNlbGVjY2lvbmFyIHBvciBwb3NpY2nDs24uIFBvciBlamVtcGxvIGEgbG9zIDIgcHJpbWVyb3MgZWxlbWVudG9zIG8gbG9zIDIgcHJpbWVyb3MgeSBsb3MgMiDDumx0aW1vcy4gVmXDoW1vc2xvOgoKCmBgYHtyfQphYSA8LSBjKDEwOjEpCmFhW2MoMToyKV0gICAgICAgICMtIHByaW1lciB5IHNlZ3VuZG8gZWxlbWVudG8gZGVsIHZlY3RvcgphYVtjKDEsMiw5LDEwKV0gICAjLSBkb3MgcHJpbWVyb3MgeSAyIMO6bHRpbW9zCmFhW2MoMSwxLDEwLDEwKV0gICMtIHNpIHJlcGl0ZXMgZWwgaW5kaWNlIHNlIHJlcGl0ZSBlbCBlbGVtZW50byBkZWwgdmVjdG9yCmBgYAoKMikgKipOZWdhdGl2ZSBpbmRleCoqOiBlbGltaW5hbW9zIGVsZW1lbnRvcyBkZWwgdmVjdG9yLiAKClBvciBlamVtcGxvIGVsaW1pbmFtb3MgbG9zIDIgcHJpbWVyb3MgZWxlbWVudG9zIGRlbCB2ZWN0b3IgeSBsb3MgMiDDumx0aW1vczoKCgpgYGB7cn0KYWEgPC0gYygxMDoxKQphYVstIGMoMSwyLCA5OjEwKV0KYGBgCgozKSAqKkxvZ2ljYWwgaW5kZXgqKjogc2Ugc2VsZWNjaW9uYW4gYXF1ZWxsYXMgcG9zaWNpb25lcyBtYXJjYWRhcyBjb24gVFJVRS4KCkVzdGEgZm9ybWEgZGUgaGFjZXIgc3Vic2V0dGluZyBzZXLDoSBtdXkgw7p0aWwuIERlIG1vbWVudG8gbm8gbG8gYXByZWNpYXJlbW9zLCBwZXJvIGVuc2VndWlkYSB2ZXJlbW9zIHN1IHV0aWxpZGFkIGN1YW5kbyBoYWdhbW9zIHN1YnNldHRpbmcgbMOzZ2ljbyBjb24gZnVuY2lvbmVzIGRlIGNvbXBhcmFjacOzbi4gQ29tbyBlamVtcGxvOgoKYGBge3J9CmFhIDwtIDE6MTAKYWEgPC0gYWFbYWEgPj0gN10KYWEKYGBgCgpTaSBubyBhY2FiYXMgZGUgZW50ZW5kZXJsbywgY29ycmUgbGFzIHNpZ3VpZW50ZXMgbGluZWFzIGRlIGPDs2RpZ286CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQphYSA8LSAxOjEwCmFhIDw9IDQKYWFbYWEgPD0gNF0KYWEgPC0gYWFbYWEgPD0gNF0KYGBgCgojIyBNb2RpZmljYW5kbyBlbGVtZW50b3MgZGUgdW4gdmVjdG9yCgoKYGBge3J9CmFhIDwtIGMoMToxMCkKYWFbNF0gPC0gODggICAgICAgICAgICAgIy0gZWwgY3VhcnRvIGVsZW1lbnRvIGRlIGFhIHRvbWFyw6EgZWwgdmFsb3IgODgKYWEgPC0gYyhhYSwgMTExLCAxMTIpICAgIy0gYcOxYWRpbW9zIDIgZWxlbWVudG9zIGFsIHZlY3RvciBhYQpgYGAKCgpMb3MgdmVjdG9yZXMgc2UgcHVlZGVuIGNvbmNhdGVuYXIKCgpgYGB7cn0KYWEgPC0gYygxOjUpCmJiIDwtIGMoMTAwOjEwNSkKY2MgPC0gYyhhYSwgYmIpCmBgYAoKCiMjIENyZWFjacOzbiBkZSB2ZWN0b3JlcyBtZWRpYW50ZSAuLi4KCi0gc2VjdWVuY2lhcyAgCi0gcmVwZXRpY2lvbmVzICAgCi0gbsO6bWVyb3MgYWxlYXRvcmlvcwoKCiMjIyBTZWN1ZW5jaWFzCgpZYSBzYWJlbW9zIHF1ZSBwb2RlbW9zIGdlbmVyYXIgc2VjdWVuY2lhcyBkZSBuw7ptZXJvcyBjb24gZWwgb3BlcmFkb3IgYDpgLiBQb3IgZWplbXBsbzogCgpgYGB7cn0KMToxMApgYGAKClBlcm8gbGEgZnVuY2nDs24gYHNlcSgpYCBub3Mgb2ZyZWNlbiBtw6FzIHBvc2liaWxpZGFkZXM6CgoKYGBge3J9CnNlcSgxLCAxMCwgMi41KQpzZXEoZnJvbSA9IDEsIHRvID0gMTAsIGJ5ID0gMi41KQpgYGAKCgojIyMgUmVwZXRpY2lvbmVzCgoKbGEgZnVuY2nDs24gYHJlcCgpYCBwZXJtaXRlIGdlbmVyYXIgdmVjdG9yZXMgcmVwaXRpZW5kbyBsb3MgZWxlbWVudG9zIGRlIHVuIHZlY3RvciBvIGxpc3RhCgpgYGB7cn0KYWEgPC0gMTozCnJlcChhYSwgMikKcmVwKGFhLCBlYWNoID0gMikgICAgIApyZXAoYWEsIGMoMiwyLDQpKQpyZXAoYWEsIGVhY2ggPSAyLCBsZW4gPSAxMCkgICAKcmVwKGFhLCBlYWNoID0gMiwgdGltZXMgPSAzKSAgCmBgYAoKIyMjIGdlbmVyYW5kbyBuw7ptZXJvcyBhbGVhdG9yaW9zCgo8YnI+CgoKCiMjIE9wZXJhY2lvbmVzIGNvbiB2ZWN0b3JlcwoKClRvZG9zIHNhYmVtb3MgcXVlIGxvcyB2ZWN0b3JlcyBzZSBwdWVkZW4gc3VtYXIsIG11bHRpcGxpY2FyLCB0cmFzcG9uZXIsIC4uLiBFbiByZWFsaWRhZCBlbiBlbCBjdXJzbyBubyB2YW1vcyBhIHV0aWxpemFyIMOhbGdlYnJhIG1hdHJpY2lhbCBvIHZlY3RvcmlhbCwgcGVybyBzaSBxdWUgdmFtb3MgYSBtYW5pcHVsYXIsIGEgaGFjZXIgb3BlcmFjaW9uZXMgY29uIHZlY3RvcmVzLCBhdW5xdWUgZ2VuZXJhbG1lbnRlIGxvIGhhcmVtb3MgdXNhbmRvIGZ1bmNpb25lcy4KCgoKTm8gb2JzdGFudGUsIGNvbnZpZW5lIHNhYmVyIGFsZ3VuYXMgY29zYXMgc29icmUgb3BlcmFjaW9uZXMgY29uIHZlY3RvcmVzIGVuIFIuIFBvciBlamVtcGxvIHBvZGVtb3Mgc3VtYXIgdmVjdG9yZXM6CgpgYGB7cn0KYWEgPC0gMToxMApiYiA8LSAxOjEwCmFhICsgYmIKYGBgCgoKIyMjIFJlY3ljbGluZwoKU2kgaW50ZW50YW1vcyBoYWNlciB1bmEgb3BlcmFjacOzbiBjb24gZG9zIHZlY3RvcmVzIHBlcm8gZXN0b3Mgbm8gdGllbmVuIGVsIG1pc21vIG9yZGVuLCBlbnRvbmNlcyBSLCBhdXRvbcOhdGljYW1lbnRlIHJlY2ljbGFyw6EgZWwgdmVjdG9yIG3DoXMgY29ydG8gaGFzdGEgcXVlIGFsY2FuY2UgbGEgbG9uZ2l0dWQgZGVsIG3DoXMgZ3JhbmRlIHkgYXPDrSBwb2RlciBoYWNlciBsYSBvcGVyYWNpw7NuLiBFc3RlIGNvbXBvcnRhbWllbnRvIHNlIGNvbm9jZSBjb21vIHJlY3ljbGluZy4gUG9yIGVqZW1wbG86CgpVbiBlamVtcGxvOgoKYGBge3J9CmFhIDwtIDE6MTAKYWEgKyAxCmBgYAoKQ29tbyBlcyBxdWUgaGVtb3MgcG9kaWRvIHN1bWFyIGBhYSsxYCwgc2kgc29uIHZlY3RvcmVzIGRlIGRpc3RpbnRvIG9yZGVuLiBQdWVzIHBvcnF1ZSBSIHNpZW1wcmUgaW50ZW50YXLDoSBoYWNlciBsYSBvcGVyYWNpw7NuIHkgc2kgbm8gc2UgcHVlZGUsIGxvIHF1ZSBoYWNlIGVzICoqcmVjaWNsYXIqKiwgYXVtZW50YXIgZWwgdmVjdG9yIG3DoXMgcGVxdWXDsW8sIMK/Q8OzbW8/IFJlcGl0aWVuZG8gbG9zIGVsZW1lbnRvcyBkZWwgdmVjdG9yIG3DoXMgcGVxdWXDsW8gaGFzdGEgcXVlIGFsY2FuY2UgbGEgbG9uZ2l0dWQgZGVsIHZlY3RvciBtw6FzIGdyYW5kZS4KCk90cm8gZWplbXBsbyBkZSByZWNpY2xhZG86CgpgYGB7cn0KYWEgPC0gMTo2CmJiIDwtIDE6MgphYSArIGJiCmBgYAoKIyMjIGZ1bmNpb25lcyB2ZWN0b3JpemFkYXMKCkxhIG1heW9yw61hIGRlIGZ1bmNpb25lcywgeSB0b2RvcyBsb3Mgb3BlcmFkb3JlcyBkZSBSIGVzdMOhbiB2ZWN0b3JpemFkb3M7IGVzIGRlY2lyLCBzaSB1bmEgZnVuY2nDs24gcmVjaWJlIGNvbW8gaW5wdXQgdW4gdmVjdG9yLCBzZSBhcGxpY2Fyw6EgbGEgZnVuY2nDs24gYSBjYWRhIHVubyBkZSBsb3MgZWxlbWVudG9zIGRlbCB2ZWN0b3IuCgpQb3IgZWplbXBsbywgbGEgZnVuY2nDs24gYHNxcnQoKWAgdGllbmUgdW4gc8OzbG8gYXJndW1lbnRvLCBhZG1pdGUgY29tbyB2YWxvciBkZSBzdSDDum5pY28gYXJndW1lbnRvIHVuIHZlY3RvciBkZSBuw7ptZXJvc15bZGUgaGVjaG8gY3VhbmRvIGhhY8OtYW1vcyBgc3FydCg5KWAgdGFtYmnDqW4gaW50cm9kdWPDrWFtb3MgdW4gdmVjdG9yIHBvcnF1ZSwgcGFyYSBSLCA5IGVzIHVuIHZlY3RvciBkZSB1biBzw7NsbyBlbGVtZW50b10uIFNlIGFwbGljYXLDoSBsYSBmdW5jacOzbiBhIGNhZGEgdW5vIGRlIGxvcyBlbGVtZW50b3MgZGVsIHZlY3RvcjoKCmBgYHtyfQphYSA8LSBjKDQsIDksIDI1KQpzcXJ0KGFhKQpgYGAKCkxhIHZlY3Rvcml6YWNpw7NuIGRlIGxhcyBmdW5jaW9uZXMgaGFjZSBxdWUgUiBzZWEgdW4gbGVuZ3VhamUgZW4gbGEgcXVlIG5vIGVzIG11eSBmcmVjdWVudGUgZWwgdXNvIGRlIGZvciBsb29wcyBvIGJ1Y2xlcywgeWEgcXVlIGVuIHJlYWxpZGFkIGxhIHZlY3Rvcml6YWNpw7NuIGltcGxpY2EgcXVlIHNlIGVzdMOhIGVqZWN1dGFuZG8gdW4gYnVjbGUgaW50ZXJubywgcHVlcyBzZSBhcGxpY2EgbGEgZnVuY2nDs24gYSBjYWRhIHVubyBkZSBsb3MgZWxlbWVudG9zIGRlbCB2ZWN0b3IuIAoKTm8gc2llbXByZSwgcGVybyBlbiBnZW5lcmFsLCBlbiBSIHNlIHN1c3RpdHV5ZW4gbG9zIGJ1Y2xlcyBgZm9yYCB0YW50byBwb3IgbGEgdmVjdG9yaXphY2nDs24gY29tbyBwb3IgZWwgdXNvIGRlIHVuYSBmYW1pbGlhIGRlIGZ1bmNpb25lcyBgKmFwcGx5YCBvIG3DoXMgcmVjaWVudGVtZW50ZSBjb24gbGFzIGZ1bmNpb25lcyBkZWwgcGFxdWV0ZSBgcHVycnJgLgoKIyMgT3BlcmFjaW9uZXMgZGUgY29tcGFyYWNpw7NuCgpMb3Mgb3BlcmFkb3JlcyBkZSBjb21wYXJhY2nDs24gdGFtYmnDqW4gZXN0w6FuIHZlY3Rvcml6YWRvcy4gU2kgY29tcGFyYW1vcyAyIHZlY3RvcmVzLCBsYXMgY29tcGFyYWNpb25lcyBzZSBlZmVjdMO6YW4gZWxlbWVudG8gYSBlbGVtZW50bwoKCmBgYHtyfQphYSA8LSAxOjMKYmIgPC0gMzoxCgphYSA9PSBiYgphYSA+PSBiYgphYSAhPSBiYgpgYGAKCkVuIGxhcyBvcGVyYWNpb25lcyBkZSBjb21wYXJhY2nDs24gdGFtYmnDqW4gYXBsaWNhIGVsIHJlY3ljbGluZzoKCmBgYHtyfQphYSA8LSAxOjMKYmIgPC0gMgoKYWEgPT0gYmIKYWEgPj0gYmIKYWEgIT0gYmIKYGBgCgoKIyMgT3BlcmFjaW9uZXMgZGUgY29uanVudG9zCgoKYGBge3J9CmFhIDwtIDE6NApiYiA8LSBjKDE6MiwgOTkpCgp1bmlvbihhYSwgYmIpICAgICAgICAgICAgCmludGVyc2VjdChhYSwgYmIpCgpzZXRkaWZmKGFhLCBiYikgICAgICAgIy0gRWxlbWVudG9zIGRlIGFhIHF1ZSBubyBlc3TDoW4gZW4gYmIKc2V0ZXF1YWwoYWEsYmIpICAgICAgICMtIGNoZXF1ZWEgc2kgZG9zIHZlY3RvcmVzIHNvbiBpZ3VhbGVzCnNldGVxdWFsKDE6NCwgMTo0KSAgICAjLSBjaGVxdWVhIHNpIGRvcyB2ZWN0b3JlcyBzb24gaWd1YWxlcwpgYGAKCgoKCgojIyBGdW5jaW9uZXMgw7p0aWxlcyBjb24gdmVjdG9yZXMKCkhheSBtdWNow61zaW1hcyBmdW5jaW9uZXMgcXVlIHNlIHB1ZWRlbiB1dGlsaXphciBjb24gdmVjdG9yZXMsIHBlcm8gYWxndW5hcyBxdWUgbWVyZWNlIGxhIHBlbmEgY29ub2NlciBzb25eW0VzdGEgc2VjY2nDs24gc2UgbWUgb2N1cnJpw7MgaW50cm9kdWNpcmxhIHRyYXMgbGVlciBlbCBsaWJybyA8aHR0cHM6Ly9zbWFjLWdyb3VwLmdpdGh1Yi5pby9kcy9kYXRhLmh0bWwjdXNlZnVsLWZ1bmN0aW9ucy13aXRoLXZlY3RvcnM+XSA6IAoKPGJyPgoKLSBgbGVuZ3RoKClgOiBub3MgZGEgZWwgb3JkZW4gbyBuw7ptZXJvIGRlIGVsZW1lbnRvcyBkZSB1biB2ZWN0b3IuIFlhIGxhIHZpbW9zLgoKCmBgYHtyfQphYSA8LSAxOjQKbGVuZ3RoKGFhKQpgYGAKCjxicj4KCi0gYHN1bSgpYDogZGV2dWVsdmUgbGEgc3VtYSBkZSBsb3MgdmFsb3JlcyBkZSB1biB2ZWN0b3IuCgoKYGBge3J9CmFhIDwtIDE6NApzdW0oYWEpCmBgYAoKwr9RdcOpIHBhc2Fyw61hIHNpIGVsIHZlY3RvciBmdWUgZGUgdGlwbyB0ZXh0byBvIGNoYXJhY3Rlcj8KIAo8YnI+CgoKLSBgY3Vtc3VtKClgOiBkZXZ1ZWx2ZSB1biB2ZWN0b3IgZGVsIG1pc21vIG9yZGVuIGNvbiBsYSBzdW1hIGFjdW11bGFkYSAoaGFzdGEgZXNlIHZhbG9yKS4KCgpgYGB7cn0KYWEgPC0gMTo0CmN1bXN1bShhYSkKYGBgCgo8YnI+CgotIGBtZWFuKClgOiBkZXZ1ZWx2ZSBlbCB2YWxvciBkZSBsYSBtZWRpYSBkZSBsb3MgdmFsb3JlcyBkZWwgdmVjdG9yLgoKYGBge3J9CmFhIDwtIDE6NAptZWFuKGFhKQpgYGAKCjxicj4KCgotIGBvcmRlcigpYDogZGV2dWVsdmUgdW4gdmVjdG9yIGNvbiBsYSBwb3NpY2nDs24gcXVlIG9jdXBhcsOtYW4gbG9zIHZhbG9yZXMgb3JpZ2luYWxlcyBkZSB1biB2ZWN0b3Igc2kgc2Ugb3JkZW5hc2VuIGRlIG1lbm9yIGEgbWF5b3IKCmBgYHtyfQphYSA8LSBjKDIuMSwgNC4yLCAzLjMsIDUuNCkKb3JkZXIoYWEpCmBgYAoKPGJyPgoKLSBgc29ydCgpYDogZGV2dWVsdmUgdW4gdmVjdG9yIGNvbiBsb3MgdmFsb3JlcyBkZWwgdmVjdG9yIG9yaWdpbmFsIG9yZGVuYWRvcyBkZSBtZW5vciBhIG1heW9yCgpgYGB7cn0KYWEgPC0gYygyLjEsIDQuMiwgMy4zLCA1LjQpCnNvcnQoYWEpCmBgYAoKPGJyPgoKLSBFbCBzaWd1aWVudGUgY2h1bmsgdXRpbGl6YSBgb3JkZXIoKWAgeSBzdWJzZXR0aW5nIHBhcmEgbG9ncmFyIGVsIG1pc21vIHJlc3VsdGFkbyBxdWUgY29uIGBzb3J0KClgCgpgYGB7cn0KeCA8LSBjKDIuMSwgNC4yLCAzLjMsIDUuNCkKeFtvcmRlcih4KV0KYGBgCgoKCjxicj4KCgoKIyAzLiBNYXRyaWNlcwoKCk90cmEgZXN0cnVjdHVyYSBwYXJhIGFsbWFjZW5hciB2YWxvcmVzIGVuIFIgc29uIGxhcyBtYXRyaWNlcy4gTm8gbGFzIHVzYXJlbW9zIG11Y2hvLCBlbiByZWFsaWRhZCBuYWRhLCBwZXJvIGNvbnZpZW5lIGNvbm9jZXJsYXMgdW4gcG9jby4gUiBzaSBxdWUgdXNhIGxhcyBtYXRyaWNlcyBpbnRlcm5hbWVudGUgcG9yIGVqZW1wbG8gcGFyYSBoYWNlciBhbsOhbGlzaXMgZGUgcmVncmVzacOzbi4KCkFsIGlndWFsIHF1ZSBsb3MgdmVjdG9yZXMsIHVuYSBtYXRyaXogKipzw7NsbyBwdWVkZSBjb250ZW5lciBlbGVtZW50b3MgZGVsIG1pc21vIHRpcG8qKiwgcG9yIGVzbyBkZWNpbW9zIHF1ZSBlcyB1bmEgZXN0cnVjdHVyYSBkZSBkYXRvcyBkZSB0aXBvIGhvbW9nw6luZWEuCgoKYGBge3J9CihhYSA8LSBtYXRyaXgoMTo2LCBuY29sID0gMiwgbnJvdyA9IDMpKSAjLSBjcmVhbW9zIHVuYSBtYXRyaXogM3gyCmBgYAoKCkxhcyBtYXRyaWNlcyB0YW1iacOpbiBzZSBwdWVkZW4gY3JlYXIgKipjb25jYXRlbmFuZG8gdmVjdG9yZXMqKjoKCmBgYHtyfQphYSA8LSBjKDE6MykKYmIgPC0gYygxMToxMykKY2MgPC0gY2JpbmQoYWEsIGJiKSAgIy0gYWdydXBhbW9zIGxvcyB2ZWN0b3JlcyBwb3IgY29sdW1uYXMgY29uIGNiaW5kKCkKZGQgPC0gcmJpbmQoYWEsIGJiKSAgIy0gYWdydXBhbW9zIGxvcyB2ZWN0b3JlcyBwb3IgZmlsYXMgY29uIHJiaW5iKCkKYGBgCgoKCiMjIFN1YnNldHRpbmcgY29uIG1hdHJpY2VzCgpFcyBtdXkgcGFyZWNpZG8gYWwgc3Vic2V0dGluZyBlbiB2ZWN0b3JlcywgZWwgb3BlcmFkb3IgcXVlIHVzYXJlbW9zIGVzIGVsIG1pc21vLCBgWyBdYCwgcGVybyBjbGFybyBsYXMgbWF0cmljZXMgdGllbmVuIGRvcyBkaW1lbnNpb25lcywgdXNhcmVtb3MgYFsgLCBdYC4gVmXDoW1vc2xvOgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmFhIDwtIG1hdHJpeCgxOjksIG5yb3cgPSAzLCBuY29sID0gMykgIy0gY3JlYW1vcyB1bmEgbWF0cml6IDN4MwoKYmIgPC0gYWFbMiwgM10gICAgICAgICAgIy0gc2VsZWNjaW9uYW1vcyBlbCBlbGVtZW50byAoMiwzKSBkZSBsYSBtYXRyaXoKCmJiIDwtIGFhWyAsIDJdICAgICAgICAgICMtIHNlbGVjY2lvbmFtb3MgbGEgc2VndW5kYSBjb2x1bW5hIChkZXZ1ZWx2ZSB1biB2ZWN0b3IpCmJiIDwtIGFhWyAsIGMoMiwzKV0gICAgICMtIHNlbGVjY2lvbmFtb3MgbGEgc2VndW5kYSB5IHRlcmNlcmEgY29sdW1uYQpiYiA8LSBhYVtjKDIsMyksIF0gICAgICAjLSBzZWxlY2Npb25hbW9zIGxhIHNlZ3VuZGEgeSB0ZXJjZXJhIEZJTEFTCgpiYiA8LSBhYVstMiwgXSAgICAgICAgICAjLSBlbGltaW5hbW9zIGxhIHNlZ3VuZGEgZmlsYQpiYiA8LSBhYVstMiwgLWMoMSwyKV0gICAjLSBlbGltaW5hbW9zIGxhIHNlZ3VuZGEgZmlsYSB5IGxhIHByaW1lcmEgeSBzZWd1bmRhIENvbHVtbmFzCmBgYAoKCiMjIE5vbWJyYXIgbGFzIGZpbGFzIHkgY29sdW1uYXMKCkFsIGlndWFsIHF1ZSBsb3MgdmVjdG9yZXMsIGxhcyBtYXRyaWNlcyBhZG1pdGVuIGF0cmlidXRvcywgZW50cmUgZWxsb3MgZGFyIG5vbWJyZSBhIGxhcyBmaWxhcyB5IGNvbHVtbmFzCgoKYGBge3J9CmFhIDwtIG1hdHJpeCgxOjYsIG5yb3cgPSAyLCBuY29sID0gMykgIy0gY3JlYW1vcyB1bmEgbWF0cml6IDJ4MwphYQoKY29sbmFtZXMoYWEpIDwtIGMoImNvbF8xIiwgImNvbF8yIiwgImNvbF8zIikgICAgCnJvd25hbWVzKGFhKSA8LSBwYXN0ZSgiZmlsYSIsIDE6Miwgc2VwID0gIl8iKSAgICAKYWEKYGBgCgoKCiMjIEZ1bmNpb25lcyBwYXJhIG1hdHJpY2VzCgoKSGF5IG11Y2hhcywgcG9yIGVqZW1wbG86CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KYWEgPC0gbWF0cml4KDE6NiwgbnJvdyA9IDMsIG5jb2wgPSAyKSAgIy0gbWF0cml6IDN4MgpkaW0oYWEpICAjLSBkZXZ1ZWx2ZSB1biB2ZWN0b3IgY29uIGxhcyBkaW1lbnNpb25lcyBkZSBsYSBtYXRyaXoKdChhYSkgICAgIy0gdHJhbnNwb25lIGxhIG1hdHJpegpgYGAKClBvciBzdXB1ZXN0byBzZSBwdWVkZW4gbXVsdGlwbGljYXIgbWF0cmljZXMsIGNhbGN1bGFyIGludmVyc2FzIGV0YyBldGMuLi4KCgo8YnI+CgoKIyA0LiAgQXJyYXlzCgoKT3RyYSBlc3RydWN0dXJhIHBhcmEgYWxtYWNlbmFyIHZhbG9yZXMgZW4gUiBzb24gbG9zIGFycmF5cy4gU29uIG1hdHJpY2VzIG11bHRpZGltZW5zaW9uYWxlcy4gTm8gbGFzIHVzYXJlbW9zIG11Y2hvLCBlbiByZWFsaWRhZCBuYWRhLiBUYW1iacOpbiBlcyB1bmEgZXN0cnVjdHVyYSBkZSBkYXRvcyBob21vZ8OpbmVhOyBlcyBkZWNpciwgc3VzIGVsZW1lbnRvcyB0aWVuZW4gcXVlIHNlciB0b2RvcyBkZWwgbWlzbW8gdGlwbwoKCmBgYHtyfQooYWEgPC0gYXJyYXkoMToxMiwgYygyLCAzLCAyKSkgKQpgYGAKCjxicj4KClBvZMOpaXMgaW1hZ2luYXIgcXVlIHNpIHR1dmnDqXNlbW9zIHF1ZSBoYWNlciBzdWJzZXR0aW5nIGRlIHVuIGFycmF5LCB0ZW5kcsOtYW1vcyBxdWUgdXRpbGl6YXIgYFsgLCAsIF1gLiBQb3IgZWplbXBsbwoKYGBge3J9CmFhWzEsIDIgLCAxXQpgYGAKCgoKPGJyPgoKCiMgNS4gRmFjdG9yZXMKClBvZGVtb3MgcGVuc2FyIHF1ZSBsb3MgZmFjdG9yZXMgc29uIG90cm8gdGlwbyBkZSBlc3RydWN0dXJhIHBhcmEgYWxtYWNlbmFyIGRhdG9zLCBhdW5xdWUgZW4gcmVhbGlkYWQgc29uIHZlY3RvcmVzLCBwZXJvIHZlY3RvcmVzIGRlIHVuIHRpcG8gZXNwZWNpYWwuIFZlYW1vcyBxdcOpIGVzIHVuIGZhY3RvciwgcGFyYSBxdcOpIHNlIHV0aWxpemFuLCBjw7NtbyBwdWVkbyBjcmVhcmxvcywgZXRjLi4uCgpQdWVzIHlhIGhlIGRpY2hvIHF1ZSBsb3MgZmFjdG9yZXMgZW4gcmVhbGlkYWQgKipTT04gVkVDVE9SRVMqKiwgcGVybyB0aWVuZW4gdW4gYXRyaWJ1dG8gcXVlIGxlcyBoYWNlIGNvbXBvcnRhcnNlIGRlIG1hbmVyYSB1biBwb2NvIGVzcGVjaWFsLgoKTG9zIGNyZWFkb3JlcyBkZSBSLCBjcmVhcm9uIGVzdGUgdGlwbyBlc3BlY2lhbCBkZSB2ZWN0b3IgbGxhbWFkbyAqKmZhY3RvcioqIHBhcmEgYWxtYWNlbmFyIGluZm9ybWFjacOzbiBzb2JyZSAqKnZhcmlhYmxlcyBjYXRlZ8OzcmljYXMqKjsgcG9yIGVqZW1wbG8gZWwgY29sb3IgZGUgb2pvcywgbyBsb3MgZMOtYXMgZGUgbGEgc2VtYW5hLCBsb3MgbWVzZXMgZGVsIGHDsW8sIGVsIGfDqW5lcm8gZXRjLi4uCgpFbiBwcmluY2lwaW8gbG9zIGZhY3RvcmVzIHNpcnZlbiBwYXJhIHJlcHJlc2VudGFyIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXM6IGxhcyBjYXRlZ29yw61hcyBvIHZhbG9yZXMgcXVlIHB1ZWRlIHRvbWFyIGxhIHZhcmlhYmxlIGRlYmVyw61hbiBzZXIgZmlqYXMgeSBjb25vY2lkYXMuIFBvciBlamVtcGxvLCBsb3MgZMOtYXMgZGUgbGEgc2VtYW5hLCBxdWUgZXMgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhIGNvbiA3IGNhdGVnb3LDrWFzIG8gZ3J1cG9zLgoKTWkgb3BpbmnDs24gcGVyc29uYWwsIHB1ZWRlIHF1ZSB1biBwb2NvIGV4dHJlbWEsIGVzIHF1ZSBsb3MgZmFjdG9yZXMgc3VlbGVuIGRhciBwcm9ibGVtYXMgYWwgbWFuaXB1bGFyIGxvcyBkYXRvcywgbWVqb3IgZXZpdGFybG9zLiBTb24gw7p0aWxlcywgc2luIGVtYmFyZ28sIGFsIGhhY2VyIGdyw6FmaWNvcyAocGVybWl0ZW4gb3JkZW5hciBsYXMgY2F0ZWdvcsOtYXMgcGFyYSBtb3N0cmFybGFzIGRlIGxhIGZvcm1hIG3DoXMgY29udmVuaWVudGUpIHkgY3VhbmRvIHNlIHRyYWJhamEgY29uIG1vZGVsb3MgZXN0YWTDrXN0aWNvcywgeWEgcXVlIGxvcyBmYWN0b3JlcyBzZSB1c2FuIHBhcmEgcmVwcmVzZW50YXIgdmFyaWFibGVzIGNhdGVnw7NyaWNhcywgeSBsYXMgdmFyaWFibGVzIGNhdGVnw7NyaWNhcyBzZSBpbmNvcnBvcmFuLCBnZW5lcmFsbWVudGUsIGVuIGxvcyBtb2RlbG9zIGVzdGFkw61zdGljb3MgbWVkaWFudGUgdmFyaWFibGVzIGR1bW15LiBTaSB0aWVuZXMgdHVzIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgYWxtYWNlbmFkYXMgZW4gZmFjdG9yZXMgbGFzIGZ1bmNpb25lcyByZWxhY2lvbmFkYXMgY29uIGxvcyBtb2RlbG9zLCBwb3IgZWplbXBsbyBgbG0oKWAgdHJhdGFyw6FuIGxhIGluZm9ybWFjacOzbiBkZSBmb3JtYSBhZGVjdWFkYSB5IGdlbmVyYXLDoW4gbGFzIGR1bW1pZXMgZGUgZm9ybWEgYXV0b23DoXRpY2EgZW4gZWwgbW9kZWxvLiBFcyBtdXkgZsOhY2lsIGNvbWV0ZXIgZXJyb3JlcyBhbCByZWNvZGlmaWNhciB2YXJpYWJsZXMgY3VhbmRvIGNvZGlmaWNhcyBsYXMgdmFyaWFibGVzIGNhdGVnw7NyaWNhcyBjb21vIG51bcOpcmljYXMsIGFzw60gcXVlIG1lam9yIHRlbmVybGFzIGNvbW8gZmFjdG9yZXMgeSBxdWUgbG8gaGFnYSBSIHBvciBub3NvdHJvcy4gUG9yIGVqZW1wbG86CgoKYGBge3IsIGVjaG8gPSBGQUxTRX0KdHdlZXRybWQ6OnR3ZWV0X2VtYmVkKCJodHRwczovL3R3aXR0ZXIuY29tL2V3ZW5oYXJyaXNvbi9zdGF0dXMvMTE4MzQyNDUxMDE1MTg3MjUxMiIsIHRoZW1lID0gImxpZ2h0IiwgYWxpZ24gPSAiY2VudGVyIiwgZG50ID0gVFJVRSwgcGxhaW4gPSBUUlVFLCBtYXh3aWR0aCA9IDQwMCkKYGBgCgoKPGJyPjxicj4KCkVuIG1pIG9waW5pw7NuLCBvdHJhIHZleiB1biBwb2NvIGV4dHJlbWEsIGVsIHRpZHl2ZXJzZSBjb21lbnrDsyBjb21vIHVuYSBsdWNoYSBjb250cmEgYHN0cmluZy5hcy5mYWN0b3IgPSBUUlVFYF5bRXN0YSBoaXN0b3JpYSBvcyBsYSBjb250YXLDqSBlbiBjbGFzZV0uIEFmb3J0dW5hZGFtZW50ZSwgbGEgcHLDs3hpbWEgdmVyc2nDs24gZGUgUiwgbGEgdmVyc2nDs24gNC4wLCB0ZW5kcsOhIGxhIG9wY2nDs24gYHN0cmluZy5hcy5mYWN0b3JgIGZpamFkYSBhIEZBTFNFIHBvciBkZWZlY3RvLiBFbCBhbnVuY2lvIG9maWNpYWwgc2UgaGl6byBlbiBlbCBwb3N0IGFsIHF1ZSBhcHVudGEgZXN0ZSB0d2VldDoKCgpgYGB7ciwgZWNobyA9IEZBTFNFfQp0d2VldHJtZDo6dHdlZXRfZW1iZWQoImh0dHBzOi8vdHdpdHRlci5jb20vX1JfRm91bmRhdGlvbi9zdGF0dXMvMTIyOTM4NjM0Mzk5NTgzODQ2NSIsIHRoZW1lID0gImxpZ2h0IiwgYWxpZ24gPSAiY2VudGVyIiwgZG50ID0gVFJVRSwgcGxhaW4gPSBUUlVFLCBtYXh3aWR0aCA9IDQwMCkKYGBgCgoKPGJyPgoKWWEgZGlqZSBxdWUgbG9zIGZhY3RvcmVzIHNlIHVzYW4gcGFyYSByZXByZXNlbnRhciB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIHkgcXVlIHB1ZWRlbiBzZXIgw7p0aWxlcyBjdWFuZG8gdHJhYmFqYXMgY29uIGdyw6FmaWNvcywgdGFibGFzIHkvbyBtb2RlbG9zIGVzdGFkw61zdGljb3MgcGVybyB0YW1iacOpbiBzb24gdW4gcG9jbyByYXJvcy9lc3BlY2lhbGVzXltTZSB1c2FuIHBhcmEgcmVwcmVzZW50YXIgdmFyaWFibGVzIGNhdGVnw7NyaWNhcywgcGVybyBpbnRlcm5hbWVudGUgbGEgaW5mb3JtYWNpw7NuIHNlIGFsbWFjZW5hIGVuIHVuIHZlY3RvciBkZSBlbnRlcm9zLCBQRVJPIHRpZW5lbiB0YW1iacOpbiB1biB2ZWN0b3IgZGUgY2FyYWN0ZXJlcyBvIGBsZXZlbHNgIHF1ZSBzZSB1dGlsaXphIHBhcmEgbW9zdHJhciBsYSBpbmZvcm1hY2nDs24uIEJ1ZmYsIGxvIGV4cGxpY2Fyw6kgZW4gY2xhc2VdLgoKU2kgdGllbmVzIHF1ZSB1c2FyIGZhY3RvcmVzLCBlcyBtZWpvciBxdWUgbG8gaGFnYXMgY29uIGVsIHBhY2thZ2UgW2Bmb3JjYXRzYF0oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcvKS4gTm8gbG8gaGUgdXNhZG8sIHBlcm8gZWwgcGFxdWV0ZSBbYHF1ZXN0aW9ucmBdKGh0dHBzOi8vanViYS5naXRodWIuaW8vcXVlc3Rpb25yL2FydGljbGVzL3JlY29kaW5nX2FkZGlucy5odG1sKSB0aWVuZSB1biBhZGRpbmcgcGFyYSByZWNvZGlmaWNhciB2YXJpYWJsZXMsIGVudHJlIGVsbG9zIGZhY3RvcmVzLCBlbiBSLgoKUmVjb2RpZmljYXIgdmFyaWFibGVzIGVzIHVuYSB0YXJlYSBtdXkgY29tw7puIGVuIGFuw6FsaXNpcyBkZSBkYXRvcyB5LCBhIHZlY2VzLCBzdWVsZSBzZXIgZnVlbnRlIGRlIGVycm9yZXMuIFBvciBlamVtcGxvLCBlbiBbZXN0ZSBwb3N0XShodHRwczovL3d3dy5kYXRhc3VyZy5uZXQvMjAxOS8xMC8xNS9qYW1hLXJldHJhY3Rpb24tYWZ0ZXItbWlzY29kaW5nLW5ldy1maW5hbGZpdC1mdW5jdGlvbi10by1jaGVjay1yZWNvZGluZy8pIG5vcyBjdWVudGFuIHVuIGVycm9yIGVuIGxhIHJlY29kaWZpY2FjacOzbiBkZSB1bmEgdmFyaWFibGUgcXVlIGxsZXZvIGEgcXVlIHVuIGFydMOtY3VsbyB0dXZpZXJhIHF1ZSBzZXIgcmV0aXJhZG8uIE5vIHNlIHNpIGVsIGVycm9yIGRlIGNvZGlmaWNhY2nDs24gbGxldsOzIGEgZXJyb3JlcyBlbiBsYSB2aWRhIHJlYWwsIHBlcm8gZWwgYXJ0w61jdWxvIGFuYWxpemFiYSB0cmF0YW1pZW50b3MgZW4gcGFjaWVudGVzIGhvc3BpdGFsaXphZG9zLgoKCiMjIyBDcmVhY2nDs24gZGUgZmFjdG9yZXMKClBvciBlamVtcGxvLCBwb2RlbW9zIGNyZWFyIHVuIHZlY3RvciBkZSB0aXBvIGNoYXJhY3RlciBjb24gNSBvYnNlcnZhY2lvbmVzCgpgYGB7cn0KYWEgPC0gYygibHVuZXMiLCAibWFydGVzIiwgImp1ZXZlcyIsICJsdW5lcyIsICJtYXJ0ZXMiKQphYQpgYGAKCkRlIG1vbWVudG8gYGFhYCBlcyB1biB2ZWN0b3IgZGUgdGV4dG8uIFNpIHF1aXNpw6lyYW1vcyBjb252ZXJ0aXJsbyBhIGZhY3RvciBwb2Ryw61hbW9zIGhhY2VyIGxvIHNpZ3VpZW50ZToKCmBgYHtyfQphYV9mYWN0b3IgPC0gYXMuZmFjdG9yKGFhKQppcy5mYWN0b3IoYWFfZmFjdG9yKQpgYGAKClPDrSwgZWZlY3RpdmFtZW50ZSwgYWhvcmEgYGFhX2ZhY3RvcmAgZXMgdW4gdmVjdG9yLCBzw60sIHBlcm8gZXMgdW4gdmVjdG9yIGVzcGVjaWFsLCBlcyB1biB1biBmYWN0b3IuIFZhbW9zIGEgaW1wcmltaXJsbyBwYXJhIHZlciBzaSBoYSBjYW1iaWFkbyBhbGdvLgoKYGBge3J9CmFhX2ZhY3RvcgpgYGAKClPDrSBhaG9yYSBhbCBpbXByaW1pciBgYWFfZmFjdG9yYCwgY29tbyBlcyB1biBmYWN0b3IsIFIgbm9zIG9mcmVjZSBtw6FzIGluZm9ybWFjacOzbi4gVW4gZmFjdG9yIGVzIHVuIHZlY3RvciBjb24gYXRyaWJ1dG9zIChjb24gbcOhcyBpbmZvcm1hY2nDs24pOyBlbiBjb25jcmV0byB1biBmYWN0b3IgbmVjZXNhcmlhbWVudGUgdGllbmUgdW4gYXRyaWJ1dG8gcXVlIHNvbiBsb3MgYGxldmVsc2AsIHF1ZSBub3MgaW5kaWNhIGN1YW50b3MgZ3J1cG9zIG8gY2F0ZWdvcsOtYXMgdGllbmUgZWwgZmFjdG9yIHkgY8OzbW8gc2UgbGxhbWFuIGxhcyBjYXRlZ29yw61hcy4gRW4gbnVlc3RybyBjYXNvIGBhYV9mYWN0b3JgIHRpZW5lIDMgbGV2ZWxzIG8gY2F0ZWdvcsOtYXMuCgpgYGB7cn0KbGV2ZWxzKGFhX2ZhY3RvcikKYGBgCgpWdWVsdm8gYSByZXBldGlybG8uIGBhYV9mYWN0b3JgIGVzIHVuIGZhY3RvciwgT0suIENvbW8gZXMgdW4gZmFjdG9yLCByZXN1bHRhIHF1ZSBlcyB1biB2ZWN0b3IgY29uIHVuIGF0cmlidXRvIGVzcGVjaWFsLCBsb3MgYGxldmVsc2AgKGxvcyBncnVwb3MgbyBjYXRlZ29yw61hcykuIEVuIG51ZXN0cm8gY2FzbywgYGFhX2ZhY3RvcmAgZXMgdW4gdmVjdG9yIGNvbiA1IG9ic2VydmFjaW9uZXMgeSAzIGdydXBvcy9jYXRlZ29yaWFzL2xldmVscy4gTm8gZXMgdGFuIGNvbXBsaWNhZG8uCgpFbiByZWFsaWRhZCwgbG9zIGZhY3RvcmVzICBzw60gc29uIHVuIHBvY28gbcOhcyByYXJvcyBkZSBsbyBxdWUgaGUgZXhwbGljYWRvLiBQb3IgcmF6b25lcyBoaXN0w7NyaWNhcywgc2UgdHJhdGFiYSBkZSBhaG9ycmFyIG1lbW9yaWEgZW4gZWwgb3JkZW5hZG9yLCBsb3MgYGxldmVsc2AgKG8gbml2ZWxlcyApc2UgdXRpbGl6YW4gcGFyYSBtb3N0cmFyIGxhIGluZm9ybWFjacOzbiwgUEVSTyBpbnRlcm5hbWVudGUgbGEgaW5mb3JtYWNpw7NuIHNlIGFsbWFjZW5hIGNvbiB1biB2ZWN0b3IgZGUgZW50ZXJvcyB5IGVzbyBhIHZlY2VzIGdlbmVyYSAobyBnZW5lcmFiYSkgY29zYXMgZGlnYW1vcyBleHRyYcOxYXMuIE5vIGltcG9ydGEgbXVjaG8gc2kgbm8gbG8gZW50aWVuZGVzIGRlbCB0b2RvLiBGw61qYXRlLCBsb3MgZmFjdG9yZXMgc2UgbXVlc3RyYW4gY29tbyB0ZXh0byBwZXJvIGVuIHJlYWxpZGFkIGludGVybmFtZW50ZSBzb24gZW50ZXJvczoKCmBgYHtyfQphYV9mYWN0b3IKdHlwZW9mKGFhX2ZhY3RvcikgICAgICMtIGludGVybmFtZW50ZSBsbyBhbG1hY2VuYSBjb21vIGVudGVyb3MgISEKYGBgCgoKPGJyPgoKIyMjIEVsIG9yZGVuIGRlIGxvcyBgbGV2ZWxzYAoKRWwgb3JkZW4gZGUgbG9zIGBsZXZlbHNgIGVzIGltcG9ydGFudGUsIHByaW5jaXBhbG1lbnRlIGFsIGhhY2VyIGdyw6FmaWNvcyB5IHRhYmxhcy4gUG9yIGRlZmVjdG8sIFIgb3JkZW5hIGxvcyBsZXZlbHMgZGUgZm9ybWEgYWxmYWLDqXRpY2EgeSBlc3RvIG5vIHNpZW1wcmUgZXMgYWRlY3VhZG8uIFZlYW1vcyBjdWFsIGVzIGVsIG9yZGVuIGRlIGxvcyBuaXZlbGVzIGVuIGBhYV9mYWN0b3JgCgpgYGB7cn0KbGV2ZWxzKGFhX2ZhY3RvcikKYGBgCgpDb21vIHBvciBkZWZlY3RvIGxvcyBuaXZlbGVzIHNlIG9yZGVuYW4gYWxmYWLDqXRpY2FtZW50ZSwgZW4gbnVlc3RybyBgYWFfZmFjdG9yYCBsb3MKClBFUk8gaGF5IGZ1bmNpb25lcyBwYXJhIHJlb3JkZW5hciBsb3MgYGxldmVsc2AsIHBvciBlamVtcGxvOgoKCmBgYHtyfQpyZWxldmVsKGFhX2ZhY3RvciwgIm1hcnRlcyIpCmBgYAoKVGFtYmnDqW4gcG9kZW1vcyBjcmVhciBlbCBmYWN0b3IgZGlyZWN0YW1lbnRlOgoKCmBgYHtyfQphYV9mYWN0b3JfMiA8LSBmYWN0b3IoYWEsIGxldmVscyA9IGMoImx1bmVzIiwgIm1hcnRlcyIsICJqdWV2ZXMiKSkKYWFfZmFjdG9yXzIKYGBgCgpBcXXDrSBlbCBvcmRlbiBkZSBsb3MgbGV2ZWxzIGxvIGhlbW9zIGVsZWdpZG8gbm9zb3Ryb3MuIE5vIGhlbW9zIHVzYWRvIGVsIG9yZGVuIGFsZmFiw6l0aWNvIHNpbm8gZWwgb3JkZW4gaGFiaXR1YWwgZW4gbG9zIGTDrWFzIGRlIGxhIHNlbWFuYS4KCgpZYSBkaWplIHF1ZSB1biBmYWN0b3IgZXMgdW4gdmVjdG9yIGNvbiBhdHJpYnV0b3MuIFZlw6Ftb3NsbzoKCmBgYHtyfQphYSA8LSAxOjEwCmF0dHJpYnV0ZXMoYWEpCgphYV9mYWN0b3JfMiA8LSBmYWN0b3IoYWEsIGxldmVscyA9IGMoImx1bmVzIiwgIm1hcnRlcyIsICJqdWV2ZXMiKSkKYXR0cmlidXRlcyhhYV9mYWN0b3JfMikKYGBgCgoKRWwgb2JqZXRvIGBhYV9mYWN0b3JfMmAgZXMgdW4gZmFjdG9yIGRlIFIsIGF1bnF1ZSBlbiBlbCBmb25kbyBubyBlcyBtw6FzIHF1ZSB1biB2ZWN0b3IgY29uIGF0cmlidXRvczogbG9zICJsZXZlbHMiIHkgImNsYXNzIgoKIyMjIE3DoXMgY29zYXMgc29icmUgZmFjdG9yZXMKCkR1cmFudGUgZWwgY3Vyc28gdHJhYmFqYXJlbW9zIGNvbiB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIHksIHBvciB0YW50bywgdXNhcmVtb3MgZmFjdG9yZXMuIFBhcmEgZWxsbyB1dGlsaXphcmVtb3MgZWwgcGFxdWV0ZSBgZm9yY2F0c2AuIFNpIGxvIG5lY2VzaXTDoWlzIHNlZ3VybyBxdWUgZW5jb250csOhaXMgdHV0b3JpYWxlcyBvIGN1cnNvcyBlbiBsYSB3ZWIsIHBlcm8gW2FxdcOtXShodHRwczovL3JzdGF0aXN0aWNzYmxvZy5jb20vci1iZWdpbm5lcnMvd29ya2luZy13aXRoLWZhY3RvcnMtaW4tci10dXRvcmlhbC1mb3JjYXRzLXBhY2thZ2UvP3V0bV9zb3VyY2U9cnNzJnV0bV9tZWRpdW09cnNzJnV0bV9jYW1wYWlnbj13b3JraW5nLXdpdGgtZmFjdG9ycy1pbi1yLXR1dG9yaWFsLWZvcmNhdHMtcGFja2FnZSkgdGVuw6lpcyB1bm8gZGUgZWxsb3M7IGF1bnF1ZSBxdWl6w6FzIHNlYSBtZWpvciBhY3VkaXIgYSBsYSBbd2ViIGRlIGBmb3JjYXRzYF0oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcvKS4KCgpVbiBidWVuIHJlc3VtZW4gdmlzdWFsIGRlIHF1w6kgc29uIGxvcyBmYWN0b3JlcyBlcyBlbCBzaWd1aWVudGUgbWFwYSBjb25jZXB0dWFsIGRlIFtlc3RlIHJlcG9dKGh0dHBzOi8vZ2l0aHViLmNvbS9zcGNhbmVsb24vY29uY2VwdC1tYXBzKToKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBldmFsID0gVFJVRSwgZmlnLmNhcCA9ICJDcmVhZG8gcG9yIElsZGlrbyBDemVsbGVyIHkgR3JhaGFtIFBhcnNvbnMiLCBmaWcuYXNwID0gNC8yLCBvdXQud2lkdGggPSAiOTAlIiwgZmlnLmFsaWduID0gImNlbnRlciJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3BjYW5lbG9uL2NvbmNlcHQtbWFwcy9tYXN0ZXIvZW4vZmFjdG9ycy5zdmciKQpgYGAKCgoKPGJyPgoKIyA2LiBMaXN0YXMKCkVzdGEgZXN0cnVjdHVyYSBkZSBkYXRvcywgYXVucXVlIGVuIHJlYWxpZGFkIHNlIGNvbnN0cnV5ZSBwb3IgbGEgYWdydXBhY2nDs24gZGUgdmVjdG9yZXMsIHPDrSBxdWUgc2UgY29tcG9ydGEgZGUgZm9ybWEgZGlmZXJlbnRlIGEgbG9zIHZlY3RvcmVzXltBdW5xdWUgZW4gcmVhbGlkYWQgbGFzIGxpc3RhcyBzZSBjb25zdHJ1eWUgcG9yIGxhIGFncnVwYWNpw7NuIHJlY3Vyc2l2YSBkZSB2ZWN0b3Jlcy5dLiBMYXMgbGlzdGFzIHNvbiB1bmEgZXN0cnVjdHVyYSBkZSBkYXRvcyBoZXRlcm9nw6luZWE6IHPDrSBxdWUgcHVlZGVuIGNvbnRlbmVyIGVsZW1lbnRvcyBkZSBkaXN0aW50byB0aXBvLgoKTGEgbWVqb3IgZm9ybWEgZGUgZW50ZW5kZXIgY29tbyBmdW5jaW9uYSB1bmEgbGlzdGEgZXMgcGVuc2FyIHF1ZSBlcyBjb21vIHVuYSBjYWpvbmVyYS4gRW4gY2FkYSBjYWrDs24gcG9kZW1vcyBwb25lciBkaWZlcmVudGVzIGNvc2FzLCBlbmNhZGEgY2Fqw7NuIHBvZGVtb3MgcG9uZXIgb2JqZXRvcyBkZSBkaWZlcmVudGVzIHRpcG9zLgoKClBhcmEgZGVmaW5pciB1bmEgbGlzdGEgc2UgaGFjZSB1c28gZGUgbGEgZnVuY2nDs24gYGxpc3QoKWAuIENyZWVtb3MgbnVlc3RyYSBwcmltZXJhIGxpc3RhOgoKYGBge3J9CiMgZGVmaW5vIDMgdmVjdG9yZXMgeSB1bmEgbWF0cml6CnZlY19udW1lcmljbyA8LSBjKDE6OCkKdmVjX2NoYXJhY3RlciA8LSBjKCJQZWRybyIsICJSZWJlY2EiLCAiU3VzYW5hIikKdmVjX2xvZ2ljIDwtIGMoVFJVRSwgRkFMU0UsIFRSVUUpCm1hdHJpeiA8LSBtYXRyaXgoMTo2LCBucm93ID0gMiwgbmNvbCA9IDMpCgojIGNyZW8gdW5hIGxpc3RhIGNvbiBjdWF0cm8gZWxlbWVudG9zCm15X2xpc3QgPC0gbGlzdCh2ZWNfbnVtZXJpY28sIHZlY19jaGFyYWN0ZXIsIHZlY19sb2dpYywgbWF0cml6KQpteV9saXN0CmBgYAoKUG9kZW1vcyBjcmVhciBsYSBtaXNtYSBsaXN0YSBwZXJvIGHDsWFkaWVuZG8gbm9tYnJlcyBhIGxvcyBkaXN0aW50b3MgZWxlbWVudG9zLiBQdWVkZSBzZXJub3Mgw7p0aWwgYSBsYSBob3JhIGRlIGhhY2VyIHN1YnNldHRpbmcgZW4gdW5hIGxpc3RhLgoKYGBge3J9Cm15X2xpc3QgPC0gbGlzdChzbG90XzEgPSB2ZWNfbnVtZXJpY28sIHNsb3RfMiA9IHZlY19jaGFyYWN0ZXIsIHNsb3RfMyA9IHZlY19sb2dpYywgc2xvdF80ID1tYXRyaXopCm15X2xpc3QKYGBgCgpMYXMgbGlzdGFzIG5vIHRpZW5lbiBkaW1lbnNpb25lcyB0aWVuZW4gYGxlbmd0aCgpYAoKYGBge3J9Cmxlbmd0aChteV9saXN0KSAjLSBudWVzdHJhIGxpc3RhIHRpZW5lIDQgc2xvdHMsIDQgZWxlbWVudG9zCmBgYAoKCkxhcyBsaXN0YXMgc29uIHN1bWFtZW50ZSBmbGV4aWJsZXMgcG9ycXVlIHVuIGVsZW1lbnRvIGRlIHVuYSBsaXN0YSBwdWVkZSBzZXIgb3RyYSBsaXN0YS4KCmBgYHtyfQpteV9saXN0XzIgPC0gbGlzdChwcmltZXJfc2xvdCA9IGMoNDQ6NDcpLCBzZWd1bmRvX3Nsb3QgPSBteV9saXN0KQpteV9saXN0XzIKYGBgCgoKCiMjIFN1YnNldHRpbmcgZW4gbGlzdGFzCgpDb21vIGxhcyBsaXN0YXMgc29uIG3DoXMgY29tcGxlamFzLCB0ZW5lbW9zIG3DoXMgcG9zaWJpbGlkYWRlcyBwYXJhIGhhY2VyIHN1YnNldHRpbmcuCgpUZW5kcmVtb3MgMyBvcGVyYWRvcmVzIHBhcmEgaGFjZXIgc3Vic2V0dGluZzogYFtgLCBgW1tgIHkgYCRgLgoKLSBFbXBlY2Vtb3MgY29uIGBbYC4gU2kgaGFjZW1vcyBzdWJzZXR0aW5nIGNvbiBgW2AgZWwgb2JqZXRvIGRldnVlbHRvIHNpZW1wcmUgc2Vyw6Egb3RyYSBsaXN0YS4KCgpgYGB7cn0KbXlfbGlzdFsyXSAgIy0gc2VsZWNjaW9uYW1vcyBlbCBzZWd1bmRvIGVsZW1lbnRvIGRlIGxhIGxpc3RhLiBOb3MgZGV2dWVsdmUgdW5hIGxpc3RhIGNvbiB1biBzb2xvIHNsb3QKYGBgCgoKYGBge3J9Cm15X2xpc3RbYygyLDMpXSAgIy0gc2VsZWNjaW9uYW1vcyBlbCBzZWd1bmRvIHkgdGVyY2VyIGVsZW1lbnRvIGRlIGxhIGxpc3RhLiBOb3MgZGV2dWVsdmUgdW5hIGxpc3RhIGNvbiBkb3Mgc2xvdHMKYGBgCgotIGNvbiBgW2AgdGFtYmnDqW4gc2UgcHVlZGUgc2VsZWNjaW9uYXIgcG9yIG5vbWJyZQoKYGBge3J9Cm15X2xpc3RbInNsb3RfMiJdICAjLSBzZWxlY2Npb25hbW9zIGVsIHNlZ3VuZG8gZWxlbWVudG8gZGUgbGEgbGlzdGEuIE5vcyBkZXZ1ZWx2ZSB1bmEgbGlzdGEgY29uIHVuIHNvbG8gc2xvdApteV9saXN0W2MoInNsb3RfMiIsICJzbG90XzMiKV0gICMtIHNlbGVjY2lvbmFtb3MgZWwgc2VndW5kbyB5IHRlcmNlciBlbGVtZW50byBkZSBsYSBsaXN0YS4gTm9zIGRldnVlbHZlIHVuYSBsaXN0YSBjb24gZG9zIHNsb3RzCmBgYAoKCi0gc3Vic2V0dGluZyBsaXN0YXMgY29uIGBbW2AuICoqTk8qKiBkZXZ1ZWx2ZSB1bmEgbGlzdGEsIG5vcyBkZXZ1ZWx2ZSBlbCBvYmpldG8gcXVlIGhheSBlbiBlbCBzbG90IHNlbGVjY2lvbmFkbwoKCmBgYHtyfQpteV9saXN0W1syXV0gICMtIHNlbGVjY2lvbmFtb3MgZWwgc2VndW5kbyBlbGVtZW50byBkZSBsYSBsaXN0YS4gTm9zIGRldnVlbHZlIGVsIGVsZW1lbnRvIHF1ZSBoYXkgZW4gZWwgc2VndW5kbyBzbG90OyBlcyBkZWNpciB1biB2ZWN0b3IsIHZlY19jaGFyYWN0ZXIKYGBgCgoKLSBzdWJzZXR0aW5nLCBwb3Igbm9tYnJlLCBjb24gYCRgLiBObyBkZXZ1ZWx2ZSB1bmEgbGlzdGEsIHNpbm8gdW4gZWxlbWVudG8gZGUgbGEgbGlzdGEKCgpgYGB7cn0KbXlfbGlzdCRzbG90XzQgICMtIHNlbGVjY2lvbmFtb3MgZWwgY3VhcnRvIGVsZW1lbnRvIGRlIGxhIGxpc3RhLCBjdXlvIG5vbWJyZSBlcyBgc2xvdF80YDsgZXMgZGVjaXIsIG5vcyBkZXZ1ZWx2ZSB1bmEgbWF0cml6LgpgYGAKCgotIGluY2x1c28gc2UgcHVlZGUgdXNhciBgW1tgIHBhcmEgaGFjZXIgc3Vic2V0dGluZyBwb3Igbm9tYnJlLCBzaSBlcyBxdWUgbG9zIGVsZW1lbnRvcyBkZSBsYSBsaXN0YSB0aWVuZW4gbm9tYnJlXltIYWRsZXkgbm9zIGF2aXNhIHF1ZSBlc3RhIGZvcm1hIGRlIGhhY2VyIHN1YnNldHRpbmcgcG9yIG5vbWJyZSBlcyBtZWpvciBwb3JxdWUgYCRgIGhhY2UgcGFydGlhbCBtYXRjaGluZy5dCgpgYGB7cn0KbXlfbGlzdFtbInNsb3RfNCJdXSAgIy0gc2VsZWNjaW9uYW1vcyBlbCBjdWFydG8gZWxlbWVudG8gZGUgbGEgbGlzdGEsIGN1eW8gbm9tYnJlIGVzIGBzbG90XzRgOyBlcyBkZWNpciwgbm9zIGRldnVlbHZlIHVuYSBtYXRyaXouCmBgYAoKCgpMYXMgbGlzdGFzIHNlIHVzYW4gcGFyYSBtdWNoYXMgY29zYXM7IHBvciBlamVtcGxvLCBwYXJhIGFsbWFjZW5hciBsb3MgcmVzdWx0YWRvcyBkZSBsYSBlc3RpbWFjacOzbiBkZSB1biBtb2RlbG8gLCBldGMuLi4KCgoKTGFzIGxpc3RhcyBzb24gc3VtYW1lbnRlIGZsZXhpYmxlcyBwb3JxdWUgdW4gZWxlbWVudG8gZGUgdW5hIGxpc3RhIHB1ZWRlIHNlciBvdHJhIGxpc3RhLgoKYGBge3J9Cm15X2xpc3RfMiA8LSBsaXN0KHByaW1lcl9zbG90ID0gYyg0NDo0NyksIHNlZ3VuZG9fc2xvdCA9IG15X2xpc3QpCm15X2xpc3RfMgpgYGAKCsK/Q8OzbW8gcG9kZW1vcyBzZWxlY2Npb25hciBsYSBtYXRyaXogcXVlIGhheSBlbiBteV9saXN0XzI/ICBQdWVzIGxhIG1hdHJpeiBlcyB1bm8gZGUgbG9zIGN1YXRybyBlbGVtZW50b3MgcXVlIGhheSBlbiBlbCBzZWd1bmRvIHNsb3QgZGUgbXlfbGlzdF8yLiBDb24gYG15X2xpc18yW1syXV1gIHNlbGVjY2lvbmFtb3MgZWwgc2VndW5kb19zbG90LCBxdWUgZXMgdW5hIGxpc3RhIChteV9saXN0KSwgeSBlbiBlbCBjdWFydG8gc2xvdCBkZSBteV9saXN0IGVzdGEgbGEgbWF0cml6LiBBc8OtIHF1ZSBwb2Ryw61hbW9zIGhhY2VybG8gZGUgdmFyaWFzIG1hbmVyYXM6CgpgYGB7cn0KbXlfbGlzdF8yW1syXV1bWzRdXQpteV9saXN0XzIkc2VndW5kb19zbG90JHNsb3RfNApgYGAKClBhcmEgdGVybWluYXIgY29uIGxhcyBsaXN0YXMsIFthcXXDrV0oaHR0cHM6Ly93d3cuam9obmRjb29rLmNvbS9ibG9nLzIwMTUvMTIvMDIvci1saXN0cy1hbmQteG1sLykgc2UgZXhwbGljYSB2aXN1YWxtZW50ZSBsYSBkaWZlcmVuY2lhIGVudHJlIGhhY2VyIHN1YnNldHRpbmcgY29uIGBbYCB5IGhhY2VybG8gY29uIGBbW2AuIEFkZW3DoXMgc2UgY3JpdGljYSB1biBwb2NvIGEgWE1MLCB1biBsZW5ndWFqZSBkZSBtYXJjYWRvIHF1ZSBtdWNoYXMgdmVjZXMgc2UgdXRpbGl6YSBwYXJhIGFsbWFjZW5hciBlIGludGVyY2FtYmlhciBkYXRvcy4KCgo8YnI+CgojIDcuIERBVEEgRlJBTUVTCgoKPiBBIGRhdGEgZnJhbWUgaXMgcmVhbGx5IGEgbGlzdCB3aGVyZSBlYWNoIGVsZW1lbnQgb2YgdGhlIGxpc3QgaXMgYSBzaW5nbGUgY29sdW1uLiBJbiBvdGhlciB3b3JkcywgYSBkYXRhIGZyYW1lIGlzIGEgdmVjdG9yIG9mIGNvbHVtbnMKCgpMb3MgZGF0YS5mcmFtZXMgc29uIG90cmEgZXN0cnVjdHVyYSBkZSBSIHBhcmEgYWxtYWNlbmFyIGRhdG9zLiBFcyBsYSBlc3RydWN0dXJhIHF1ZSBtw6FzIHV0aWxpemFyZW1vcy4gU2UgdXRpbGl6YSBwYXJhIGFsbWFjZW5hciBkYXRvcyB0YWJ1bGFyZXMsIHRhYmxhcyBkZSBkYXRvcywgcXVlIHNvbiBlbCBmb3JtYXRvIGRlIGRhdG9zIGFsIHF1ZSBtw6FzIGFjb3N0dW1icmFkb3MgZXN0YW1vcywgc29uIHRhYmxhcyBjb21vIGxhcyBxdWUgdmVtb3MgZW4gRXhjZWwgZXRjIC4uLiAKCkVuIHJlYWxpZGFkIHVuIGRhdGEuZnJhbWUgKiplcyB1bmEgbGlzdGEqKiwgcGVybyB1bmEgbGlzdGEgZXNwZWNpYWwuIEFsIHNlciB1bmEgbGlzdGEgKipwdWVkZSBhbG1hY2VuYXIgZWxlbWVudG9zIGRlIGRpc3RpbnRvIHRpcG8qKiAobnVtw6lyaWNvcywgY2Fyw6FjdGVyIGV0Yy4uLikuIMK/UG9yIHF1w6kgZXMgdW5hIGxpc3RhIGVzcGVjaWFsPyBQdWVzIHBvcnF1ZSBwb2RlbW9zIHBlbnNhciBxdWUgZXMgdW5hIGxpc3RhIHJlc3RyaW5naWRhLCByZXN0cmluZ2lkYSBhIHF1ZSBzdXMgZWxlbWVudG9zL3Nsb3RzIHRpZW5lbiBxdWUgdGVuZXIgbGEgbWlzbWEgbG9uZ2l0dWQuIAoKQ29tbyBlbiBjYWRhIHNsb3QgZGUgbGEgbGlzdGEgaGF5IHVuIHZlY3RvciB5IHRvZG9zIGxvcyB2ZWN0b3JlcyB0aWVuZW4gbGEgbWlzbWEgbG9uZ2l0dWQgYWwgZmluYWwgdGVuZW1vcyB1bmEgdGFibGEgZGUgZGF0b3MuIEVzb3MgdmVjdG9yZXMgY29uIGxhIG1pc21hIGxvbmdpdHVkIHNlIGFsbWFjZW5hbiBwb3IgY29sdW1uYXMsIGFzw60gcXVlIGxhIGxvbmdpdHVkIGRlIGxvcyB2ZWN0b3JlcyBzb24gZWwgbsO6bWVybyBkZSBmaWxhcyBkZSBsYSB0YWJsYS4gSGFiaXR1YWxtZW50ZSBsb3MgZGF0YS5mcmFtZXMgc2UgdXRpbGl6YW4gcGFyYSBhbG1hY2VuYXIgZGF0b3MgdGFidWxhcmVzLCBkb25kZSBjYWRhIGNvbHVtbmEgZXMgdW5hIHZhcmlhYmxlLgoKT3RyYSBwcm9waWVkYWQgZGUgbG9zIGRhdGEuZnJhbWVzIGVzIHF1ZSBsb3MgdmVjdG9yZXMgbyBjb2x1bW5hcyBxdWUgbG8gZm9ybWFuIHRpZW5lbiBxdWUgdGVuZXIgdW4gbm9tYnJlLiBDb21vIGhhYml0dWFsbWVudGUgZW4gY2FkYSBjb2x1bW5hIGRlbCBkYXRhLmZyYW1lIHNlIGFsbWFjZW5hbiBsb3MgdmFsb3JlcyBkZSB1bmEgdmFyaWFibGUsIGVsIG5vbWJyZSBkZSBsYSBjb2x1bW5hcyBzZSBjb3JyZXNwb25kZXLDoSBjb24gZWwgbm9tYnJlIGRlIGxhIHZhcmlhYmxlLiAKCgpHZW5lcmFsbWVudGUgbm8gY3JlYXJlbW9zIGRhdGEuZnJhbWVzIHNpbm8gcXVlIGxvcyBpbXBvcnRhcmVtb3MgZGUgcmVwb3NpdG9yaW9zIGRlIGRhdG9zLCBwb3IgZWplbXBsbyBkZSBFdXJvc3RhdCBvIGRlbCBJTkUsIHBlcm8gaG95IHZhbW9zIGEgY3JlYXIgdW5vIGNvbiBsYSBmdW5jacOzbiBgZGF0YS5mcmFtZSgpYAoKQWRlbcOhcywgZW4gZ2VuZXJhbCwgZW4gbHVnYXIgZGUgdXNhciBkYXRhLmZyYW1lcyB1c2FyZW1vcyBbdGliYmxlc10oaHR0cHM6Ly90aWJibGUudGlkeXZlcnNlLm9yZy8pIG8gZGF0YS5mcmFtZXMgdHVuZWFkb3Mgw6AgbGEgdGlkeXZlcnNlLiBMbyB2ZXJlbW9zLgoKCmBgYHtyfQphYSA8LSBjKDQsIDgsIDYsIDMpCmJiIDwtIGMoIkp1YW4iLCAiUGF6IiwgIkFkcmlhbiIsICJNYXJxdWl0b3MiKQpjYyA8LSBjKEZBTFNFLCBUUlVFLCBUUlVFLCBGQUxTRSkKZGYgPC0gZGF0YS5mcmFtZShhYSwgYmIsIGNjKQpkZgpgYGAKCkVuIG51ZXN0cm8gYGRmYCB0ZW5lbW9zIDMgdmFyaWFibGVzLCBjYWRhIHVuYSBkZSBlbGxhcyBjb24gNCBvYnNlcnZhY2lvbmVzLgoKUG9kZW1vcyBwb25lcmxlIG90cm9zIG5vbWJyZXMgYSBsYXMgY29sdW1uYXMsIG5vbWJyZXMgcXVlIHRlbmdhbiBzZW50aWRvIHkgcXVlIG5vcyByZWN1ZXJkZW4gbGFzIHZhcmlhYmxlcyBxdWUgY29udGllbmVuOgoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoTm90YSA9IGFhLCBOb21icmUgPSBiYiwgQXByb2JhZG8gPSBjYykKbmFtZXMoZGYpICAjLSBjb24gbmFtZXMoKSBwb2RlbW9zIHZlciBsb3Mgbm9tYnJlcyBkZSBsYXMgdmFyaWFibGVzIGRlbCBkZgpgYGAKClBvZGVtb3MgY2FtYmlhciBsb3Mgbm9tYnJlcyBkZSBsYXMgY29sdW1uYXMgY29uIGBuYW1lcygpYCBvICBkZSBtw6FzIGZvcm1hcywgY29tbyBjb24gYHJsYW5nOjpzZXRfbmFtZXMoKWAgbyBjb24gYG1hZ3JpdHRyOjpzZXRfY29sbmFtZXMoKWAgY29tbyBzZSBtdWVzdHJhIFthcXXDrV0oaHR0cHM6Ly9naXRodWIuY29tL3NoYXJsYWdlbGZhbmQvdHdvZnVuY3Rpb25zbW9zdGRheXMvdHJlZS9tYXN0ZXIvMjAyMC8wMy8wNSNwdXJycnNldF9uYW1lcy0tLW5ldy10by1tZSkuCgoKYGBge3J9CihuYW1lcyhkZikgPC0gYygiR3JhZGUiLCAiTmFtZSIsICJQYXNzIikpCmBgYAoKCgojIyBTdWJzZXR0aW5nIGVuIGRhdGEuZnJhbWVzCgoKTGEgdmVyZGFkIGVzIHF1ZSBjdWFuZG8gdGVuZ2Ftb3MgcXVlIGhhY2VyIHN1YnNldHRpbmcgZW4gdW4gZGF0YS5mcmFtZSwgbG8gaGFyZW1vcyDDoCBsYSB0aWR5dmVyc2UsIHBlcm8gY29udmllbmUgYWwgbWVub3MgY29ub2NlciBsbyBiw6FzaWNvIGRlIGNvbW8gaGFjZXIgc3Vic2V0dGluZyBjb24gUi1iYXNlLgoKRWwgc3Vic2V0dGluZyBlbiBkYXRhLmZyYW1lIMOgIGxhIFItYmFzZSBlcyBtdXkgdW5hIG1lemNsYSBlbnRyZSBlbCBzdWJzZXR0aW5nIHBhcmEgbWF0cmljZXMgeSBwYXJhIGxpc3Rhcy4gCgoKMSkgU3Vic2V0dGluZyBjb21vIHNpIGZ1ZXJhIHVuYSBtYXRyaXouIENvbiAgYFtgLgoKYGBge3J9CmRmX3MgPC0gZGZbLDFdICAgICAgICMtIHNlbGVjY2lvbmFtb3MgbGEgcHJpbWVyYSBjb2x1bW5hLiBkZXZ1ZWx2ZSB1biB2ZWN0b3IgISEhCmRmX3MgPC0gZGZbLGMoMiwzKV0gICMtIHNlbGVjY2lvbmFtb3MgbGEgc2VndW5kYSB5IHRlcmNlcmEgY29sdW1uYS4gZGV2dWVsdmUgdW4gZGYKZGZfcyA8LSBkZlsxLCBdICAgICAgIy0gc2VsZWNjaW9uYW1vcyBwcmltZXJhIGZpbGEgZGUgdG9kYXMgbGFzIHZhcmlhYmxlcy4gZGV2dWVsdmUgdW4gZGYuIMK/eHEgbm8gZGV2dWVsdmUgdW4gdmVjdG9yPyBQcmVndW50YWQgc2kgbm8gbG8gc2Fiw6lpcwpkZl9zIDwtIGRmW2MoMSw0KSwgXSAgIy0gc2VsZWNjaW9uYW1vcyBwcmltZXJhIHkgY3VhcnRhIGZpbGEuIGRldnVlbHZlIHVuIGRmCmRmX3MgPC0gZGZbMiwgM10gICAgICAjLSBzZWxlY2Npb25hbW9zIHNlZ3VuZGEgb2JzZXJ2YWNpw7NuIGRlIGxhIHRlcmNlcmEgdmFyaWFibGUuIERldnVlbHZlIHVuIHZlY3Rvci4KYGBgCgoKMikgU3Vic2V0dGluZyBjb21vIHNpIGZ1ZXJhIHVuYSBsaXN0YS4gRGUgaGVjaG8gdW4gZGYgZXMgdW5hIGNsYXNlIGVzcGVjaWFsIGRlIGxpc3RhLiBQb2RlbW9zIHVzYXIgMyBvcGVyYWRvcmVzOiBgW2AsIGBbW2AgeSBgJGAuCgoKCi0gc3Vic2V0dGluZyBjb24gYFtgICBjb21vIHNpIGZ1ZXJhIHVuYSBsaXN0YSAKCmBgYHtyfQpkZl9zIDwtIGRmWzNdICAgICAgICAjLSBkZXZ1ZWx2ZSB1biBkZi4gR29vZCEhCmRmX3MgPC0gZGZbYygxLDIpXQoKIy0gdGFtYmnDqW4gc2UgcHVlZGUgaGFjZXIgcG9yIG5vbWJyZQpkZl9zIDwtIGRmWyJOYW1lIl0gICAgICAgICAgICAgICAgIy0gZGV2dWVsdmUgdW4gZGYKZGZfcyA8LSBkZltjKCJOYW1lIiwgIkdyYWRlIildICAgCmBgYAoKLSBzdWJzZXR0aW5nIGNvbiBgW1tgIGNvbW8gc2kgZnVlcmEgdW5hIGxpc3RhCgoKYGBge3J9CmRmX3MgPC0gZGZbWzJdXSAgICMtIEV4dHJhZW1vcyBsYSBzZWd1bmRhIGNvbHVtbmEuIERldnVlbHZlIHVuIHZlY3RvciwgY29uY3JldGFtZW50ZSB1biBmYWN0b3IuIEFoaGhoISEhISEKYGBgCgotIHN1YnNldHRpbmcgY29uIGAkYC4gQ29tbyBzaSBmdWVyYSB1bmEgbGlzdGEgKHBvciBub21icmUgZGUgbG9zIGVsZW1lbnRvcywgZW4gZXN0ZSBjYXNvIGxvcyBlbGVtZW50b3MgZGUgbGEgbGlzdGEgc29uIGxhcyBjb2x1bW5hcyBvIHZhcmlhYmxlcyBkZWwgZGYpCgoKYGBge3J9CmRmX3MgPC0gZGYkTmFtZSAgICMtIEV4dHJhZW1vcyBsYSBjb2x1bW5hIGNvbiBub21icmUgIk5hbWUiLiBEZXZ1ZWx2ZSB1biB2ZWN0b3IsIGNvbmNyZXRhbWVudGUgdW4gZmFjdG9yLiBBaGhoaCEhISEhCmRmX3MgPC0gZGYkR3JhZGUgICMtIEV4dHJhZW1vcyBsYSBjb2x1bW5hIGNvbiBub21icmUgIkdyYWRlIi4gRGV2dWVsdmUgdW4gdmVjdG9yIG51bcOpcmljbwpgYGAKCgoKCkhheSBtw6FzIGZvcm1hcyBkZSBoYWNlciBzdWJzZXR0aW5nIGNvbiBkYXRhLmZyYW1lcywgcG9yIGVqZW1wbG8gY29uIGBzdWJzZXQoKWAsIHBlcm8geWEgaGUgZGljaG8gcXVlIGVsIG1hbmVqbyBkZSB0YWJsYXMgbyBkYXRhLmZyYW1lcyBsbyBoYXJlbW9zIHByaW5jaXBhbG1lbnRlIGNvbiBmdW5jaW9uZXMgZGVsIHRpZHl2ZXJzZSwgcHJpbmNpcGFsbWVudGUgZnVuY2lvbmVzIGRlbCBwYXF1ZXRlIGBkcGx5cmAuIExvIHZlcmVtb3MgZW4gb3RybyB0dXRvcmlhbC4KCgoKCi0gY29uIGBbYCBwb2RlbW9zLCBhbCBpZ3VhbCBxdWUgZW4gbG9zIHZlY3RvcmVzIHkgbWF0cmljZXMsIGhhY2VyIHN1YnNldHRpbmcgbMOzZ2ljbzoKCgpgYGB7cn0KZGZfcyA8LSBkZltkZiRHcmFkZSA+PSA1LCBdICAgIy0gc2VsZWNjaW9ubyBmaWxhcyBxdWUgY3VtcGxhbiBxdWUgZWwgdmFsb3IgZGUgbGEgY29sdW1uYSBncmFkZSBzZWEgPj0gYSA1CmRmX3MgPC0gZGZbIWRmJEdyYWRlID49IDUsIF0gICMtIGxvcyBxdWUgTk8gaGFuIHNhY2FkbyBpZ3VhbCBvIG1hcyAgZGUgNQpkZl9zIDwtIGRmW2RmJEdyYWRlID09IDggfCBkZiRHcmFkZSA8IDUsIF0gICMtIGxvcyBxdWUgaGFuIHNhY2FkbyBleGFjdGFtZW50ZSA4IMOzIG1lbm9zIGRlIDUKYGBgCgoKCgoKIyMgRnVuY2lvbmVzIMO6dGlsZXMKCgpIYXkgbXVjaGFzLCBwb3IgZWplbXBsbzoKCgpgYGB7cn0KbmFtZXMoZGYpICAgICMtIGRldnVlbHZlIHVuIHZlY3RvciBjb24gbG9zIG5vbWJyZSBkZSBsYXMgY29sdW1uYXMvdmFyaWFibGVzIGRlbCBkZgpucm93KGRmKSAgICAgIy0gZGV2dWVsdmUgZWwgbsK6IGRlIGZpbGFzCm5jb2woZGYpICAgICAjLSBuwrogZGUgY29sdW1uYXMKbGVuZ3RoKGRmKSAgICMtIGxhIGxvbmdpdHVkIGRlIGxhcyBjb2x1bW5hcy92ZWN0b3JlcyBxdWUgY29tcG9uZW4gZWwgZGY7IG9zZWEgZGV2dWVsdmUgb3RyYSB2ZXogZWwgbsK6IGRlIGZpbGFzCmBgYAoKCgpQb2RlbW9zIGHDsWFkaXIgY29sdW1uYXMgYSBudWVzdHJvIGRmOgoKCmBgYHtyfQphYSA8LSAxMDE6MTA0CmRmJG5ld192IDwtIGFhCmBgYAoKVGFtYmnDqW4gYXPDrToKCgpgYGB7cn0KZGZfbmV3IDwtIGNiaW5kKGRmLCBhYSkKYGBgCgoKQ29uIGxhIGZ1bmNpw7NuIGBhcy5kYXRhLmZyYW1lKClgLCBwb2RlbW9zIGNvbnZlcnRpciB1biB2ZWN0b3IgbyB1bmEgbWF0cml6LCBlIGluY2x1c28gYWxndW5hcyBsaXN0YXMsIGVuIGRhdGEuZnJhbWVzLgoKCmBgYHtyfQphYSA8LSAxOjUKZGZfbmV3IDwtIGFzLmRhdGEuZnJhbWUoYWEpICAjLSBkZiBjb24gdW5hIHNvbGEgY29sdW1uYQpgYGAKCgpVbiByZXN1bWVuIGRlbCBkYXRhZnJhbWUgY29uIGBzdW1tYXJ5KClgCgpgYGB7cn0Kc3VtbWFyeShkZikKYGBgCgoKClkgbXVjaGFzIG3DoXMuCgoKCiMgOC4gUGFxdWV0ZXMgKHBhY2thZ2VzKQoKWWEgc2FiZW1vcyBxdWUgUiB0aWVuZSBtdWNob3MgcGFxdWV0ZXMsIHlhIGhlbW9zIHV0aWxpemFkbyBhbGd1bm8uIMK/UXVlIHF1w6kgc29uIGxvcyBwYXF1ZXRlcz8gUHVlcyBzb24gY29sZWNjaW9uZXMvcG9yY2lvbmVzIGRlIGPDs2RpZ28gUiBxdWUgcG9kZW1vcyBpbnN0YWxhciBlbiBudWVzdHJvIG9yZGVuYWRvciB5IGRlc3B1w6lzIGNhcmdhciBlbiBudWVzdHJhIHNlc2nDs24uIMK/UXXDqSBnYW5hbW9zIGNvbiBlbGxvPyBMb3MgcGFxdWV0ZXMgbm9zIHByb3ZlZW4gIGRlIG51ZXZhcyBmdW5jaW9uZXMgbyBudWV2b3MgZGF0b3M7IGVzIGRlY2lyIGluY3JlbWVudGEgbGEgZnVuY2lvbmFsaWRhZCBkZSBSOiBSIHBvZHLDoSBoYWNlciBtw6FzIGNvc2FzLiBFcyBjb21vIGluc3RhbGFyIHVuYSBudWV2YSBhcHAgZW4gdHUgdGVsw6lmb25vLgoKSW1hZ2luYSBxdWUgcXVpZXJlcyB1c2FyIFIgcGFyYSBhbmFsaXphciBkYXRvcyBkZSBFdXJvc3RhdC4gRW4gZXNlIGNhc28gcG9kZW1vcyBpciBkaXJlY3RhbWVudGUgYSBsYSB3ZWIgZGUgRXVyb3N0YXQsIHBlcm8gZXMgbXVjaG8gbcOhcyBjw7Ntb2RvIGluc3RhbGFybm9zIGVsIHBhcXVldGUgW2BldXJvc3RhdGBdKGh0dHBzOi8vcm9wZW5nb3YuZ2l0aHViLmlvL2V1cm9zdGF0LykgcXVlIGNvbnRpZW5lIHVuYSBzZXJpZSBkZSBmdW5jaW9uZXMgcXVlIG5vcyBmYWNpbGl0YW4gZWwgdXNvIGRlIGRhdG9zIGRlIEV1cm9zdGF0IGVuIFI7IG8gYSBsbyBtZWpvciBxdWllcmVzIGFuYWxpemFyIGRhdG9zIHJlbGFjaW9uYWRvcyBjb24gbGEgcGFuZGVtaWEgY292aWQtMTkuIFBvZHLDrWFzIHRyYXRhciBkZSByZWNvcGlsYXIgZGF0b3MgcG9yIHRpIG1pc21vIHBlcm8gZXhpc3RlbiB1bm9zIGN1YW50b3MgcGFxdWV0ZXMgcXVlIHlhIGxvIGhhbiBoZWNobywgcG9yIGVqZW1wbG8sIHBhcmEgZGF0b3MgZXNwYcOxb2xlcywgZWwgcGFxdWV0ZSBbYGVzY292aWQxOWRhdGFgXShodHRwczovL2dpdGh1Yi5jb20vbW9udGVyYTM0L2VzY292aWQxOWRhdGEpLgoKUGFyYSBwb2RlciB1c2FyIHVuIHBhcXVldGUgeWEgc2Fiw6lpcyBxdWUgaGF5IHF1ZSBoYWNlciAyIGNvc2FzOgoKICAxKSBJbnN0YWxhciBlbCBwYWNrYWdlIGVuIHR1IG9yZGVuYWRvci4gU8OzbG8gaGFjZSBmYWx0YSBoYWNlcmxvIHVuYSB2ZXouIEdlbmVyYWxtZW50ZSBjb24gYGluc3RhbGwucGFja2FnZXMoIm15X3BrZyIpYAogIAogIDIpIENhcmdhciBlbCBwYXF1ZXRlIGVuIG1lbW9yaWEuIENhZGEgdmV6IHF1ZSB2YXlhcyBhIHVzYXIgdW4gcGFxdWV0ZSB0ZW5kcsOhcyBxdWUgYWJyaXJsbywgdGVuZHLDoXMgcXVlIGhhY2VyIGFjY2VzaWJsZSBlbCBwYXF1ZXRlIGEgbGEgbWVtb3JpYSBkZSBSIGNvbiBsaWJyYXJ5KCJteV9wa2ciKS4KICAKPGJyPgoKIyMjIyBJbnN0YWxhY2nDs24gZGUgcGFxdWV0ZXMKCllhIHNhYmVzIHF1ZSBoYXkgdW4gcmVwb3NpdG9yaW8gb2ZpY2lhbCBkZSBwYXF1ZXRlcywgW0NSQU5dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLyk7IHBlcm8gaGF5IG11Y2hvcyBvdHJvcyByZXBvc2l0b3Jpb3MsIHVubyBkZSBsb3MgbcOhcyBmYW1vc29zIHkgdXRpbGl6YWRvcyBlcyBbR2l0aHViXShodHRwczovL2dpdGh1Yi5jb20vKS4KClBhcmEgaW5zdGFsYXIgdW4gcGFxdWV0ZSBkZSBDUkFOIHRpZW5lcyBxdWUgdXNhciBgaW5zdGFsbC5wYWNrYWdlcygpYC4KClBhcmEgaW5zdGFsYXIgcGFxdWV0ZXMgZGUgb3Ryb3MgcmVwb3NpdG9yaW9zIHNlIHB1ZWRlbiB1dGlsaXphciB2YXJpb3MgcGFxdWV0ZXMsIHBlcm8geW8gw7psdGltYW1lbnRlIGVzdG95IHVzYW5kbyBlbCBwYXF1ZXRlIFtgcmVtb3Rlc2BdKGh0dHBzOi8vZ2l0aHViLmNvbS9yLWxpYi9yZW1vdGVzI3JlYWRtZSkuIEVsIHBhcXVldGUgYHJlbW90ZXNgIHBlcm1pdGUgaW5zdGFsYXIgcGFxdWV0ZXMgYWxvamFkb3MgZW4gR2l0SHViLCBHaXRMYWIsIEJpdGJ1Y2tldCwgZXRjLi4uCgpQb3IgZWplbXBsbyBwYXJhIGluc3RhbGFyIHVuIHBhcXVldGUgZGUgR2l0aHViIHNlIHV0aWxpemE6IGByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigidXN1YXJpby9wYWNrYWdlIilgLiBQb3IgZWplbXBsbywgcGFyYSBpbnN0YWxhciBlbCBwYXF1ZXRlIFtgZW1vYF0oaHR0cHM6Ly9naXRodWIuY29tL2hhZGxleS9lbW8pIGRlbCB1c3VhcmlvIFtgaGFkbGV5YF0oaHR0cHM6Ly9naXRodWIuY29tL2hhZGxleSkgc2UgaGFyw61hIGxvIHNpZ3VpZW50ZTogYHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJoYWRsZXkvZW1vIilgLgoKPGJyPgoKCiMjIyMgT2J0ZW5lciBpbmZvcm1hY2nDs24gc29icmUgdW4gcGtnIAoKSW5zdGFsYXIgdW4gcGFxdWV0ZSBlcyBzZW5jaWxsbywgcGVybyBwYXJhIHVzYXJsbywgZ2VuZXJhbG1lbnRlLCB0ZW5kcsOhcyBxdWUgYWNjZWRlciBhIGxhIGRvY3VtZW50YWNpw7NuIGRlbCBwYXF1ZXRlLiBMb3MgcGFxdWV0ZXMgdGllbmVuIG11Y2hhIGluZm9ybWFjacOzbiBzb2JyZSBjb21vIHVzYXIgc3VzIGZ1bmNpb25lcywgc8OzbG8gaGF5IHF1ZSBzYWJlciBidXNjYXJsYS4KClZlYW1vcyB1biBlamVtcGxvIGNvbmNyZXRvLiBTdXBvbmdhbW9zIHF1ZSBxdWVyZW1vcyB1c2FyIGVsIHBhcXVldGUgW2BldXJvc3RhdGBdKGh0dHBzOi8vcm9wZW5nb3YuZ2l0aHViLmlvL2V1cm9zdGF0LykuIFBhcmEgY29ub2NlciBxdWUgZnVuY2lvbmFsaWRhZGVzIHRpZW5lIGVzdGUgcGFxdWV0ZSB5IGPDs21vIHVzYXIgc3VzIGZ1bmNpb25lcyB0ZW5lbW9zIHZhcmlhcyBhbHRlcm5hdGl2YXM6CgogIDEuIFNpIGVsIHBhcXVldGUgZXN0w6EgZW4gQ1JBTiwgcHVlcyBpciBhIHN1IHDDoWdpbmEgd2ViIGVuIENSQU4uIEVsIHBhcXVldGUuIGBldXJzb3RhdGAgc8OtIGVzdMOhIGFsb2phZG8gZW4gQ1JBTiwgY29uY3JldGFtZW50ZSBbYXF1w61dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9ldXJvc3RhdC9pbmRleC5odG1sKSwgeSBoYWPDrWEgZWwgZmluYWwgZGUgZXNhIHDDoWdpbmEgZXN0w6EgbGEgZG9jdW1lbnRhY2nDs24uIFRvZG9zIGxvcyBwYWNrYWdlcyBlbiBDUkFOIHRpZW5lbiB1biAqKlJlZmVyZW5jZSBNYW51YWwqKjogdW4gcGRmIGNvbiBkb2N1bWVudGFjacOzbiBleHRlbnNhIGRlIGNhZGEgdW5hIGRlIGxhcyBmdW5jaW9uZXMgZGVsIHBrZy4gSGFiaXR1YWxtZW50ZSBsb3MgcGFja2FnZXMgdGFtYmnDqW4gdGllbmVuIHVub3MgZG9jdW1lbnRvcyBsbGFtYWRvcyAqKnZpZ25ldHRlcyoqIHF1ZSBleHBsaWNhbiBkZSBmb3JtYSBtw6FzIGdlbsOpcmljYSBwYXJhIHF1w6kgc2lydmUgeSBjw7NtbyBzZSB1c2EgZWwgcGFxdWV0ZS4KICAKICAyLiBBZGVtw6FzIGRlIGVuIENSQU4sIGVzIG11eSBoYWJpdHVhbCBxdWUgbG9zIHBhcXVldGVzIGVzdMOpbiBhbG9qYWRvcyBlbiBvdHJvcyByZXBvc2l0b3Jpb3MsIGdlbmVyYWxtZW50ZSBlbiBHaXRodWIuIGBldXJvc3RhdGAgZXN0w6EgYWxvamFkbyBlbiBHaXRodWIgW2FxdcOtXShodHRwczovL2dpdGh1Yi5jb20vck9wZW5Hb3YvZXVyb3N0YXQpCiAgICAKICAzLiBDYWRhIHZleiBlcyBtw6FzIGZyZWN1ZW50ZSBxdWUgbG9zIHBhcXVldGVzIHRlbmdhbiBzdSBwcm9waWEgcMOhZ2luYSB3ZWIgZG9uZGUgZXNlIGV4cGxpY2EgZGV0YWxsYWRhbWVudGUgbGFzIGZ1bmNpb25hbGlkYWRlcyBkZWwgcGFxdWV0ZS4gU2kgdW4gcGFxdWV0ZSB0aWVuZSBww6FnaW5hIHdlYiwgdmlzaXRhcmxhIHN1ZWxlIHNlciBsYSBtZWpvciBvcGNpw7NuIHBhcmEgYXByZW5kZXIgY29tbyB1c2FybG8uIFBvciBlamVtcGxvLCBsYSBww6FnaW5hIHdlYiBkZWwgcGtnIGBldXJvc3RhdGAgZXN0w6EgW2FxdcOtXShodHRwczovL3JvcGVuZ292LmdpdGh1Yi5pby9ldXJvc3RhdC8pLgogICAgCiAgICAKIyMjIyBJbmZvcm1hY2nDs24gaW50ZXJuYSBzb2JyZSB1biBwa2cKCkFkZW3DoXMgZGUgcG9kZXIgYnVzY2FyIGF5dWRhIHNvYnJlIHVuIHBhY2thZ2UgZW4gbGEgd2ViLCBzZSBwdWVkZSBhY2NlZGVyIGEgbGEgZG9jdW1lbnRhY2nDs24gZGUgdW4gcGFxdWV0ZSAsIHRhbnRvIGFsIHJlZmVyZW5jZSBtYW51YWwgY29tbyBhIGxhcyB2aWduZXR0ZXMsIGRpcmVjdGFtZW50ZSBkZXNkZSBSL1JTdHVkaW8uIFRpZW5lcyBxdWUgaGFjZXIgbG8gc2lndWllbnRlOgoKCmBgYHtyLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQojLSBhYnJpbW9zIGVuIFJTdHVkaW8gbGEgYXl1ZGEgZGVsIHBrZyBldXJvc3RhdApoZWxwKHBhY2thZ2UgPSBldXJvc3RhdCkKYGBgIAogIAoqKkFkZW3DoXMqKiwgcmVjb3JkYWQgcXVlIHlhIHNhYmVtb3MgcXVlIGNhZGEgZnVuY2nDs24gdGllbmUgc3UgcHJvcGlhIGF5dWRhIGludGVybmEuIFB1ZWRlcyBhY2NlZGVyIGEgZWxsYSBjb24gYGhlbHAobm9tYnJlX2RlX2xhX2Z1bmNpb24pYCBvIHNpdHVhbmRvIGVsIGN1cnNvciBlbiBlbCBub21icmUgZGUgbGEgZnVuY2nDs24geSBwdWxzYXIgbGEgdGVjbGEgPGtiZD5GMTwva2JkPgoKPGJyPgoKIyMjIyBPYnRlbmVyIHVuIGxpc3RhZG8gZGUgbGFzIGZ1bmNpb25lcyBkZSB1biBwa2cKCkhheSB2YXJpYXMgZm9ybWFzLCBwZXJvIGVzdGEgZnVuY2lvbmE6CgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmFhIDwtIGFzLmRhdGEuZnJhbWUobHMoInBhY2thZ2U6cmVhZHIiLCBhbGwgPSBUUlVFKSkKYGBgCgo8YnI+CgojIyMjICDCv0PDs21vIHNhYmVyIGEgcXVlIHBhY2thZ2UgcGVydGVuZWNlIHVuYSBmdW5jacOzbj8KCkEgdmVjZXMgZXMgY29udmVuaWVudGUgc2FiZXIgYSBxdWUgcGFja2FnZSBwZXJ0ZW5lY2UgdW5hIGZ1bmNpw7NuLiBQb2RlbW9zIGhhY2VybG8gY29uOgoKYGBge3IsIGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0V9CiMtIHVzYW1vcyBsYSBmdW5jacOzbiJmaW5kIiBwYXJhIGVuY29udHJhciBlbiBxdWUgcGFja2FnZSBlc3TDoSBsYSBmdW5jacOzbiAibWVhbiIKZmluZCgibWVhbiIpCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCgojIDkuIE3DoXMgY29zYXMgYSBjb25vY2VyCgpFbiBlc3RhIHNlY2Npw7NuIHByZXNlbnRhcsOpIGFsZ3VuYXMgaWRlYXMgeSB0w7NwaWNvcyBxdWUgY3JlbyBwdWVkZW4gc2Vyb3MgZGUgdXRpbGlkYWQuCgoKIyMgQWxndW5hcyBmdW5jaW9uZXMgw7p0aWxlcwoKLSBgJWluJWA6IHJldG9ybmEgdW4gdmVjdG9yIGRlIGJvb2xlYW5zIChUUlVFIG9yIEZBTFNFKSBwYXJhIGNhZGEgdW5vIGRlIGxvcyB2YWxvcmVzIGRlbCB2ZWN0b3IgYSBsYSBpenF1aWVyZGEgZGVwZW5kaWVuZG8gZGUgc2kgZWwgdmFsb3Igc2UgZW5jdWVudHJhIG8gbm8gZW4gZWwgdmVjdG9yIGRlIGxhIGRlcmVjaGEuCgoKYGBge3J9Cj9gJWluJWAgICMtIHBhcmEgdmVyIGxhIGF5dWRhIGRlbCBvcGVyYWRvcgoxOjEwICVpbiUgYygxLDMsNSw5KQpgYGAKCgotIGBzdHIoKWA6IG11ZXN0cmEgbGEgZXN0cnVjdHVyYSBkZSB1biBvYmpldG8KCmBgYHtyfQpzdHIoaXJpcykKYGBgCgo8YnI+Cgo8YnI+CgojIyBEaXJlY3RvcmlvIGRlIHRyYWJham8KCkN1YW5kbyB0cmFiYWphbW9zIGNvbiBSIGVzIGltcG9ydGFudGUgY29ub2NlciBsYSBub2Npw7NuIGRlIGRpcmVjdG9yaW8gZGUgdHJhYmFqby4gRXMgbGEgY2FycGV0YSBkb25kZSBSIGJ1c2NhIHBvciBkZWZlY3RvIGN1YW5kbyBsZSBwaWRhcyBxdWUgYWJyYSB1bm9zIGRhdG9zIG8gZG9uZGUgbG9zIGdyYWJhcsOhLk11Y2hhcyB2ZWNlcyB0ZW5kcmVtb3MgcXVlIGltcG9ydGFyIG8gZXhwb3J0YXIgZGF0b3MgYXPDrSBxdWUgdGVuZW1vcyBxdWUgY29ub2NlciBjdWFsIGVzIG51ZXN0cm8gZGlyZWN0b3JpbyBkZSB0cmFiYWpvIHkgY8OzbW8gcG9kZW1vcyBjYW1iaWFybG8uCgoKLSBQYXJhIGNvbm9jZXIgZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvIGFjdHVhbCB1c2EgbGEgZnVuY2nDs24gYGdldHdkKClgCgotIFNpIHF1aXNpw6lyYW1vcyBjYW1iaWFyIGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBwb2RlbW9zIGhhY2VybG8gY29uIGxhIGZ1bmNpw7NuIGBzZXR3ZCgpYAoKCk5vc290cm9zIHRyYWJhamFyZW1vcyBjb24gUnByb2plY3RzIHkgZWwgcGFxdWV0ZSBgaGVyZWAgYXPDrSBxdWUgZ2VuZXJhbG1lbnRlIG5vIG5lY2VzaXRhcmVtb3MgZXN0YXMgZnVuY2lvbmVzIHBlcm8gY29udmllbmUgY29ub2Nlcmxhcy4KCkN1YW5kbyB0cmFiYWphcyBjb24gUnByb2plY3RzLCBlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8gZXMgbGEgcHJvcGlhIGNhcnBldGEgZGVsIHByb3llY3RvLiBMYSB2ZW50YWphIGRlIGVzdG8gZXMgcXVlIGVuIGx1Z2FyIGRlIHJ1dGFzIGFic29sdXRhcyBwb2RlbW9zIHVzYXIgcnV0YXMgcmVsYXRpdmFzIHkgZXN0byBmYWNpbGl0YSBsYSByZXByb2R1Y2liaWxpZGFkIGRlIG51ZXN0cm9zIGFuw6FsaXNpcy4KCgoKQWRlbcOhcyBkZSB0cmFiYWphciBjb24gcHJveWVjdG9zIHVzYXJlbW9zLCBhbCBtZW5vIHlvLCBsYSBmdW5jacOzbiBgaGVyZSgpYCBkZWwgcGFxdWV0ZSBgaGVyZWAuIExhIHJhesOzbiBlcyBxdWUgcXVpZXJvIHBvZGVyIGtuaXRlYXIgbG9zIC5SbWQgZGVzZGUgY3VhbHF1aWVyIGNhcnBldGEgZGVsIHByb3llY3RvLgoKPGJyPgoKCiMjIE5BJ3MgeSB2YWxvcmVzIGVzcGVjaWFsZXMKClJlY29yZMOhaXMgcXVlIGVuIHVuYSBzZWNjacOzbiBhbnRlcmlvciBkaWppbW9zIHF1ZSBsb3MgcHJpbmNpcGFsZXMgdGlwb3MgZGUgZGF0b3Mgc29uIDM6IG51bcOpcmljb3MsIGRlIHRleHRvIHkgbMOzZ2ljb3MsIHBlcm8gcXVlIGV4aXN0w61hbiB1bm9zIHZhbG9yZXMgZXNwZWNpYWxlczogYE5VTExgLCBgTkFgLCBgTmFOYCBlIGBJbmZgLiBWZcOhbW9zbG9zIHVuIHBvY286CgotIGBOVUxMYDogcmVwcmVzZW50YSBlbCBvYmpldG8gdmFjw61vIGVuIFIuIE5vIHNlIHByb3BhZ2EuCgpgYGB7cn0KYygpCnN1bSgyLCAyLCBOVUxMKQptZWFuKGMoMiAsNCwgTlVMTCkpCmBgYAoKCgotIGBOQWA6IFVuIHZhbG9yIG5vIGRpc3BvbmlibGUuIFPDrSBzZSBwcm9wYWdhLCBhc8OtIHF1ZSBjdWFuZG8gdGVuZ2Ftb3MgcXVlIGNhbGN1bGFyIG1lZGlhcyBldGMuLi4gY29uIHZlY3RvcmVzIHF1ZSBjb250ZW5nYW4gTkFzIGhheSBxdWUgdGVuZXIgY3VpZGFkbywgaGFicsOhIHF1ZSB1c2FyIGxhIG9wY2nDs24gYG5hLnJtID0gVFJVRWAuCgpgYGB7cn0Kc3VtKDIsIDIsIE5BKQptZWFuKGMoMiAsNCwgTkEpKQptZWFuKGMoMiwgNCwgTkEpLCBuYS5ybSA9IFRSVUUpCmBgYAoKCi0gYE5hTmA6IE5vdCBhIG51bWJlci4gU2UgcHJvcGFnYQoKYGBge3J9CjAvMApzdW0oMiwgMiwgTmFOKQptZWFuKGMoMiAsNCwgMC8wKSkKYGBgCgoKLSBgSW5maW5pdG9gOiAKCmBgYHtyfQoxLzAKLTEvMApzdW0oMiwgMiwgSW5mKQpgYGAKCgoKCiMjIFNlY3VlbmNpYXMKCk11Y2hhcyB2ZWNlcyBoYXkgcXVlIGNyZWFyIHNlY3VlbmNpYXMgIGRlIG7Dum1lcm9zLiBQYXJhIGVsbG8gZGlzcG9uZW1vcyBkZSBsYXMgZnVuY2lvbmVzIGA6YCwgYHNlcSgpYCB5IGByZXAoKWAuIFBvciBlamVtcGxvOgoKLSBjb24gYDpgIGVsIGluY3JlbWVudG8gc2llbXByZSBlcyB1bml0YXJpby4gRGV2dWVsdmUgZW50ZXJvcy4KCmBgYHtyfQoxOjMKYygxOjQpCi0yOjMKNDotMQpgYGAKCi0gYHNlcSgpYAoKYGBge3J9CnNlcShmcm9tID0gMCwgdG8gPSA1LCBieSA9IDEpCnNlcSgtNSwgNSwgMikKCnNlcSgwLCAxLCBsZW5ndGgub3V0ID0gMykKc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSA1KQpzZXEoMCwgMSwgNSkKYGBgCgotIGByZXAoKWAKCmBgYHtyfQpyZXAoMiwgdGltZXM9IDMpCnJlcCgxOjMsIDIpCnJlcCgxOjMsIGVhY2ggPSAyKSAgICAgICAjIG5vdCB0aGUgc2FtZS4KcmVwKDE6MywgdGltZXMgPSAzLCBlYWNoID0gMikKcmVwKDE6MiwgbGVuZ3RoLm91dCA9IDkpCgpgYGAKCgoKPGJyPgoKIyAxMC4gQWxndW5vcyBjaHVua3MKCgoxLiBFbGltaW5hciB0b2RvcyBsb3Mgb2JqZXRvcyBkZWwgR2xvYmFsIEVudmlyb25tZW50ICoqRVhDRVBUTyoqIGxvcyBxdWUgdMO6IGRlY2lkYXMuCgpQYXJhIGVsaW1pbmFyIHVuIG9iamV0byBkZSBsYSBtZW1vcmlhIHB1ZWRlcyB1c2FyIGxhIGZ1bmNpw7NuIGBybSgpYC4gUGFyYSBxdWl0YXIgdW4gb2JqZXRvIHNvbG8gaGFzIGRlIGhhY2VyIGBybShvYmpldG8pYCwgcGVybyBzaSBxdWllcmVzIGVsaW1pbmFyIHRvZG9zIGxvcyBvYmpldG9zIGV4Y2VwdG8gbG9zIHF1ZSBuZWNlc2l0ZXM6CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KYWEgPC0gMQpiYiA8LSAyCmNjIDwtIDMKZGQgPC0gNApxdWllcm9fZGVqYXJfZXN0b3MgPC0gYygiY2MiLCAiZGQiKSAgICAgICAgICAgICAgICAjLSBmw61qYXRlIHF1ZSBsb3Mgbm9tYnJlcyBkZSBsb3Mgb2JqZXRvcyBlc3TDoW4gZW50cmUgY29taWxsYXMKcm0obGlzdCA9IGxzKClbIShscygpICVpbiUgcXVpZXJvX2RlamFyX2VzdG9zKV0pICAgIy0gcmVtdWV2ZSB0b2RvIGV4Y2VwdG8gcXVpZXJvX2RlamFyX2VzdG9zCmBgYAoKCjIuIEZ1bmNpw7NuIHBhcmEgY29tcGFyYXIgbG9zIGVsZW1lbnRvcyBkZSBkb3MgdmVjdG9yZXMuCgpJbnNwaXJhZG9zIHBvciBbZXN0ZSB0d2VldF0oaHR0cHM6Ly90d2l0dGVyLmNvbS90eWx1UnAvc3RhdHVzLzExOTc2MzQ3NTU0MzAzNjcyMzUpIHZhbW9zIGEgY3JlYXIgbnVlc3RyYSBwcmltZXJhIGZ1bmNpw7NuOgoKYGBge3J9Cm15X2ZfY29tcGFyZSA8LSBmdW5jdGlvbih4LCB5KXsKICBsaXN0KAogICJWYWxvcmVzIGRlIFggcS4gbm8gZXN0w6FuIGVuIFk6IiA9IHNldGRpZmYoeCwgeSksCiAgIlZhbG9yZXMgZGUgWSBxLiBubyBlc3TDoW4gZW4gWDoiID0gc2V0ZGlmZih5LCB4KSwKICAiVmFsb3JlcyBjb211bmVzIGVuIFggZSBZOiIgPSBpbnRlcnNlY3QoeCwgeSksCiAgIkxhIHVuacOzbiBkZSBYIGUgWSBzZXLDrWE6IiA9IHVuaW9uKHgseSksIAogICJTaSBqdW50YW1vcyBYIGUgWSBvYnRlbmVtb3M6IiA9IGMoeCwgeSkgKQp9CmBgYAoKVHJhdGEgZGUgZW50ZW5kZXJsbyBQYXVsCgpgYGB7cn0KWFggPC0gYygxOjQpCllZIDwtIGMoMzo2KQphYSA8LSBteV9mX2NvbXBhcmUoWFgsIFlZKQpuYW1lcyhhYSkKYWEKYGBgCgoKPGJyPgoKIyAxMS4gUiA0LjEuMAoKQ29uIGxhIGxsZWdhZGEgZGUgUiA0LjEuMCBzZSBwcm9kdWpvIHVuIGNhbWJpbyBpbXBvcnRhbnRlIGVuIGxhIGNvbXVuaWRhZCBSOiBhcGFyZWNpw7MgbGEgKipwaXBlIG5hdGl2YSoqIGA+fGBeW0NvbW8gc2UgZGljZSBbYXF1w61dKGh0dHBzOi8vZWxpb2NhbXAuZ2l0aHViLmlvL2NvZGlnby1yL2VuLzIwMjEvMDUvci1waXBhLW5hdGl2YS8pLCBsYSB2ZXJzacOzbiA0LjAgeWEgdmlubyBjb24gdW4gY2FtYmlvIGhpc3TDs3JpY286IHNlIGFjYWJvIGNvbiBgc3RyaW5nQXNGYWN0b3JzID0gVFJVRWAuIEFkZW3DoXMgZWwgcG9zdCBzdWdpZXJlIHVuYSBzb2x1Y2nDs24gcGFyYSBsYSBmYWx0YSBkZSBwbGFjZWhvbGRlciBlbiBsYSBwaXBlIG5hdGl2YV0uIFVuYSBjb21wYXJhY2nDs24gZXhoYXVzdGl2YSBkZSBsYSBwaXBlIG5hdGl2YSBjb24gbGEgcGlwZSBkZSBtYWdyaXR0ciBwdWVkZSBlbmNvbnRyYXJzZSBbYXF1w61dKGh0dHBzOi8vbWljaGFlbGJhcnJvd21hbi5jby51ay9wb3N0L3RoZS1uZXctYmFzZS1waXBlLykKCkxhIHBpcGUgb3JpZ2luYWwgKCAlPiUgKSwgbGEgZGUgU3RlZmFuIEJhY2hlIGVuIGVsIHBhcXVldGUgbWFncml0dHIsIGVzIHVuIG9wZXJhZG9yIGZ1bmRhbWVudGFsIHBhcmEgZWwgdGlkeXZlcnNlIHF1ZSBoYSBjYW1iaWFkbyBsYSBmb3JtYSBkZSBlc2NyaWJpciBjw7NkaWdvIGVuIFIuIMK/U3VzdGl0dWlyw6EgbGEgcGlwZSBuYXRpdmEgKCB8PiApIGEgbGEgb3JpZ2luYWwgKCAlPiUgKSwgwr9sbyB2ZXJlbW9zPwoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbXRjYXJzIHw+IGdyb3VwX2J5KGN5bCkgfD4gc3VtbWFyaXNlKG1wZyA9IG1lYW4obXBnKSkKYGBgCgoKVW5hIHZlbnRhamEgZXZpZGVudGUgZGVsIHBpcGUgbmF0aXZhIGVzIHF1ZSBlcyBuYXRpdmEsIHNlIHB1ZWRlIHVzYXIgc2luIHRlbmVyIHF1ZSBpbnN0YWxhciBuaSBjYXJnYXIgbmluZ8O6biBwYXF1ZXRlLCBtZW5vcyBkZXBlbmRlbmNpYXMhITsgYWRlbcOhcywgc29sbyBlc3TDoSBjb21wdWVzdGEgZGUgMiBjYXJhY3RlcmVzIGZyZW50ZSBhIGxvcyAzIGRlIGxhIG9yaWdpbmFsLgoKClVuYSBkZXN2ZW50YWphIGRlIGxhIG5hdGl2YSBlcyBxdWUgbm8gcHVlZGUgdXNhciBgLmAgY29tbyBwbGFjZWhvbGRlciBwYXJhIGxsZXZhciBlbCBvYmpldG8gZGUgbGEgaXpxdWllcmRhIGEsIHBvciBlamVtcGxvLCBlbCBzZWd1bmRvIG8gdGVyY2VyIGFyZ3VtZW50byBkZSBsYSBmdW5jacOzbiBkZSBsYSBkZXJlY2hhLiBQb3IgZWplbXBsbywgbm8gc2UgcHVlZGUgaGFjZXIgZXN0byBjb24gbGEgcGlwZSBuYXRpdmE6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQoyICU+JSBoZWFkKGlyaXMsIG4gPSAuKQpgYGAKCkxhIGZvcm1hIGRlIHNvbHVjaW9uYXJsbyBlcyB1c2FyIGZ1bmNpb25lcyBhbsOzbmltYXMgKHVuIHBvY28gZmVvISEpOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KMiB8PiAoZnVuY3Rpb24oeCkgaGVhZChpcmlzLCBuID0geCkKYGBgCgpMbyBhbnRlcmlvciBtZWpvcmEgdW4gcG9jbyAobGEgdmVyZGFkIGVzIHF1ZSBiaWVuIHBvY28pIHNpIHVzYW1vcyBsYSBudWV2YSBzaW50YXhpcyAoc2hvcnRoYW5kKSBwYXJhIGZ1bmNpb25lcyBhbsOzbmltYXMgbyBsYW1iZGEgZnVuY3Rpb25zXltbQXF1w61dKGh0dHBzOi8vdHdpdHRlci5jb20vcmFiYWF0aC9zdGF0dXMvMTMzNTIyNjY5MTMwNDc3NTY4MSkgdW4gaGlsbyBkZSBUd2l0dGVyIHNvYnJlIGxhcyBsYW1iZGEgZnVuY3Rpb25zXToKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMtIGVzdGFzIGRvcyBsaW5lYXMgcHJvZHVjZW4gaWTDqW50aWNvcyByZXN1bHRhZG9zCmZ1bmN0aW9uKHgpIHggKyAxIApcKHgpIHggKyAxCmBgYAoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQoyIHw+IChcKHgpIGhlYWQoaXJpcywgeCA9IG4pKSgpCmBgYAoKQXVucXVlIGVuIFtlc3RlIHR3ZWV0XShodHRwczovL3R3aXR0ZXIuY29tL21hdHRoaWFzZ29tb2xrYS9zdGF0dXMvMTQwNDgyMTY2ODY5NTAwNzIzNykgc2UgZGljZSBxdWUgc2kgcHVlZGVzIGxsZXZhciBlbCBvYmpldG8gZGUgbGEgaXpxdWllcmRhIGEgdW4gYXJndW1lbnRvIGRpc3RpbnRvIGRlbCBwcmltZXJvLCBzaSBsb3MgYW50ZXJpb3JlcyBhcmd1bWVudG9zIHRpZW5lbiBwdWVzdG8gZWwgbm9tYnJlLCB5YSBxdWUgIlRoZSBwaXBlIHBpcGVzIGludG8gdGhlIGZpcnN0IHVubmFtZWQgYXJndW1lbnQsIEkgZ3Vlc3MuIgoKCgojIEJpYmxpbwoKLSBbQmFzZSBSIENoZWF0IFNoZWV0XShodHRwczovL3JzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE2LzEwL3ItY2hlYXQtc2hlZXQtMy5wZGYpICAgIChodHRwczovL2dpdGh1Yi5jb20vc2FnaGlyYi9HZXR0aW5nLVN0YXJ0ZWQtaW4tUikuIFVuYSBidWVuYSBjaHVsZXRhIGRlIDIgcMOhZ2luYXMgY29uIGxhcyBwcmluY2lwYWxlcyBpZGVhcyB5IGZ1bmNpb25lcyBwYXJhIGluaWNpYXJzZSBjb24gUi4gSGF5IG11Y2hhcyBtw6FzLCBwb3IgZWplbXBsbywgW2VzdGFdKChodHRwczovL2dpdGh1Yi5jb20vc2FnaGlyYi9HZXR0aW5nLVN0YXJ0ZWQtaW4tUikpIG8gW2VzdGFdKGh0dHA6Ly9kYXRhc2NpZW5jZWZyZWUuY29tL2Jhc2ljUi5wZGYpLgoKCi0gW2FScmdoOiBhIG5ld2NvbWVy4oCZcyAoYW5ncnkpIGd1aWRlIHRvIFJdKGh0dHA6Ly9hcnJnaC50aW0tc21pdGgudXMvKTogdW4gcGl0b25pc3RhIChwcm9ncmFtYWRvciBxdWUgdXNhIFBoeXRvbikgcXVlIHNlIHF1ZWphIHkgZGVzY3JpYmUgbGFzIGNvc2FzIGRlIFIgcXVlIMOpbCB2ZSByYXJhcyAoZ290Y2hhcyk7IGF1bnF1ZSB2YSBhZG1pdGllbmRvLCBwb2NvIGEgcG9jbywgIHF1ZSBsZSB2YSBlbXBlemFuZG8gYSBndXN0YXIgUi4KCi0gW2ZvcmNhdHMgZm9yIGZhY3RvcnNdKGh0dHBzOi8vY3JhaWcucmJpbmQuaW8vcG9zdC8yMDIwLTA4LTI5LWFzZ3ItMi00LWZhY3RvcnMvKS4gVW4gYXJ0w61jdWxvIHNlbmNpbGxvIHkgYWN0dWFsIHNvYnJlIGZhY3RvcmVzLg==