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
objeto.
En R, para asignar un nombre a un valor, y así crear
un objeto, se usa el operador <-
.
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 aa
en 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
integers
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 propagan. 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úmeros.
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:
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:
usando la función help()
, o sea, tecleando en R
help(nombre_de_la_funcion)
; por ejemplo
help(log)
tecleando en R ?nombre_de_la_funcion
; por ejemplo
?(log)
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 array) 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 datos.
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:
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
vector. 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) 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 vectors.
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).
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
factor. 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:
- 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
- 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
- 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)
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úmeros. 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 son :
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 = TRUE
. 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/especiales.
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:
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
vectores. 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 nombre
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
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.
- 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.
- 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:
Instalar el package en tu ordenador. Sólo hace falta hacerlo una
vez. Generalmente con install.packages("my_pkg")
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 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.
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
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(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(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
- 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
- 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
>|
. 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 functions:
#- 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.”
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==