1. Introducción

Los gráficos e infografías son, cada vez más, una parte importante de la mayoría de textos escritos, ya sean estos un informe técnico, un artículo de periódico o un TFG. En el caso de la ciencia de datos, como puede verse abajo en la infografía, el análisis y la exploración de los datos es un proceso iterativo en el que la visualización y la generación de gráficos ocupa un lugar destacado.

R for Data Science (http://r4ds.had.co.nz/)

R for Data Science (http://r4ds.had.co.nz/)

Uno de los instrumentos y tareas fundamentales de un científico de datos es la capacidad de realizar visualizaciones de datos apropiadas y convincentes. El análisis gráfico no solo ayuda en la exploración y comprensión de los datos, sino que es fundamental a la hora de mostrar las posibles relaciones entre variables, descubrir relaciones o patrones ocultos, y descartar o sugerir nuevas preguntas sobre los datos.

El entorno R tiene diversos sistemas para visualizar datos, los dos más utilizados son el sistema gráfico de R-base y el ecosistema asociado al paquete ggplot2. Por diversas razones, en el curso usaremos el entorno ggplot2 para hacer nuestros gráficos; de hecho, en la actualidad ggplot2, dada su rapidez en la iteración entre gráficos, versatilidad y la cuidada estética que tienen sus gráficos, se ha convertido, al menos por el momento, en el sistema estándar para hacer gráficos en R. Por ejemplo, ¿cómo creéis que hace la BBC sus gráficos?, evidentemente con R y ggplot2. Puedes ver uno de sus repositorios aquí y su cookbook aquí.

Con ggplot2 es sencillo hacer gráficos con calidad para ser publicados o mostrados, además de que, dada su sintaxis modular, hace sencillo el reutilizar los gráficos durante el proceso de análisis. El paquete ggplot2 fue inicialmente desarrollado por Hadley Wickham, pero actualmente el ecosistema ggplot es el resultado de toda una comunidad de usuarios que contribuye a enriquecer el sistema gráfico con sus extensiones y paquetes auxiliares.

En palabras de Hadley en su libro sobre ggplot2:

ggplot2 is an R package for producing statistical, or data, graphics, but it is unlike most other graphics packages because it has a deep underlying grammar. This grammar, based on the Grammar of Graphics (Wilkinson 2005), is made up of a set of independent components that can be composed in many different ways. This makes ggplot2 very powerful because you are not limited to a set of pre-specified graphics, but you can create new graphics that are precisely tailored for your problem. This may sound overwhelming, but because there is a simple set of core principles and very few special cases, ggplot2 is also easy to learn (although it may take a little time to forget your preconceptions from other graphics tools).

Sí, con ggplot2 es “fácil” hacer rápidamente gráficos de calidad, PERO dominar todos los detalles del paquete sí que es complicado, pero no nos hace falta conocerlo todo. Además, hay que tener en cuenta que ggplot2 es un paquete/entorno en constante evolución. Actualmente está en la versión 3.2.1. Bueno, en marzo de 2020 apareció la versión 3.3.0.

Para entender esta idea de la constante evolución y el papel que tiene la comunidad de usuarios en el desarrollo de R y sus paquetes puedes leer este tweet y las respuestas a él. En el tweet sólo se anuncia una pequeña mejora en como ggplot2 gestiona los títulos de los gráficos pero genera reacciones en la comunidad de usuarios.

La redes sociales pueden hacerse eco de la evolución de un paquete, pero donde habitualmente se produce la discusión/colaboración entre usuarios es en plataformas como Github. Por ejemplo, puedes ver como se gestó está pequeña mejora aquí. Fue la issue 3252 de ggplot2. Otro ejemplo, justo cuando estaba escribiendo este párrafo, leí este otro tweet anunciando otra mejora en ggplot2.

La página web de ggplot2 puedes encontrarla aquí. En ella puedes encontrar documentos de ayuda y la referencia oficial. Para darte cuenta de todo lo que se puede hacer con el ecosistema ggplot visita esta página donde podrás ver los 79 “paquetes auxiliares” o extensiones a ggplot2.

Para hacer “buenos” gráficos con ggplot2no sólo es necesario entender la sintaxis y los pormenores del paquete, sino que quizás se necesite algo más. Por ejemplo, algo de experiencia y cierta capacidad visual y estética; incluso hay quien dice que hacer buenos gráficos es un arte. Para intentar mejorar vuestros gráficos o evitar ciertos errores, aquí tenéis algunas reglas/consejos, y aquí un curso completo sobre visualización1 con bookdown incluido.

Entenderás muy bien que hacer buenos gráficos exige conocimiento y mucho trabajo al ver el video de este post, donde se ven las versiones previas para que al final saliese esta preciosura de gŕafico:

Otro ejemplo del making-of de un gráfico puedes verlo aquí.

Otros dos libros sobre visualización con el código de los ejemplos hechos con ggplot2, aquí y aquí. Finalmente, algunos consejos de la BBC sobre visualización.

Como también se aprende los errores, aquí tienes un articulo de The Economist donde muestran errores que ellos mismos han cometido haciendo gráficos. Además pueden descargarse los datos.


2. Ideas básicas sobre ggplot2

Ya se dijo que ggplot2 es un paquete R desarrollado por Hadley Wickham, aunque actualmente es el resultado de la colaboración de múltiples desarrolladores. ggplot2 implementa en R The Grammar of Graphics de L. Wilkinson, un sistema coherente para describir y construir gráficos. El énfasis de ggplot2 está en la exploración rápida de datos, especialmente de datos de alta dimensionalidad. Con ggplot2 es sencillo ir transformando el gráfico mientras se van analizando los datos.

Para empezar a entender la “filosofía” de ggplot2, os planteo una pregunta medio retórica: ¿qué vemos en el gráfico de abajo?

Pues sí, es un gráfico de puntos y nos ayuda a ver las relaciones que existen entre 3 variables. Estamos habituados a ello, pero vamos a pensar en el gráfico desde la óptica de “The Grammar of Graphics” implementada en el paquete ggplot2.

En nuestro gráfico se representan por medio de puntos, en el espacio X-Y, y mediante los distintos colores de los puntos, las observaciones de 3 variables. Bien, nada muy novedoso, todos los sistemas gráficos hacen este tipo de gráficos. Los gráficos de ggplot2 se realizan mediante la superposición de elementos/capas. Podemos pensar que el gráfico que hemos visto es una capa. ¿Cómo creamos este gráfico o capa en ggplot2?

Pues, una de las principales ideas para entender ggplot2 es que cada capa de un gráfico tiene 3 componentes o elementos principales:

  • los datos que se van a representar (sencillo, para hacer un gráfico hacen falta datos). Para ello utilizaremos generalmente la función ggplot()

  • un conjunto de propiedades estéticas asociadas a alguna variable del conjunto de datos. Por ejemplo, la variable Sepal.Lenght está asociada al eje X, ala posición en el eje X. Por su parte, el color de los puntos (otra característica visual o estética) está asociado a los valores de la variable Species. Es decir, usando la terminología de ggplot2, las distintas variables están asociadas o mapeadas a determinadas características estéticas. El mapeo de variables con estéticas se hará con la función aes() de aesthetics. (esto ya no es tan estándar, se explica en breve)

  • el elemento geométrico que se va a representar. En nuestro caso el elemento geométrico que se utiliza para representar los valores de las variables son los puntos, pero podrían haber sido las lineas o las barras … Para especificar el elemento geométrico que vamos a usar en nuestro gráfico se utiliza la familia de funciones geom_xx(); por ejemplo geom_point() si queremos puntos, geom_line() si queremos que las relaciones entre las variables se representen/visualicen como lineas.

Así en abstracto puede ser complicado entender del todo que quiere decir todo esto. Vamos a verlo con ejemplos concretos. De momento, para explicar las principales características de ggplot2 utilizaré un conjunto de datos famoso, pero odiado por algunos, por haber sido utilizado en numerosos ejemplos y cursos: el iris dataset.

El conjunto de datos iris contiene datos sobre 150 flores, en concreto sobre 150 lirios. iris tiene 5 variables, 4 de ellas miden la longitud y el ancho del pétalo y sépalo de los 150 lirios. Estas 4 primeras variables son cuantitativas y continuas; mientras que la quinta variable es categórica, indicando la clase o variedad de los lirios, ya que en los datos hay 3 especies distintas de lirios (setosa, versicolor y virginica). Con estos datos, con 3 de sus variables, se ha creado el gráfico que ves más arriba.


PRIMER GRÁFICO: Para comenzar nuestras andanzas con ggplot2 intentaremos replicar con código R el gráfico de arriba; aunque al principio sólo utilizaremos 2 variables: haremos un gráfico de puntos de la longitud del sépalo frente a la longitud del pétalo.

Hacer un gráfico con ggplot2 requiere de varias etapas,

  • la primera de ellas consiste en usar la función ggplot() para inicializar el gráfico.

  • en segundo lugar, tendremos que especificar que conjunto de datos usaremos en el gráfico.

  • en tercer lugar tendremos que especificar que variables irán asociadas a determinados elementos visuales o estéticos del gráfico

  • por último, en cuarto lugar, tendremos que especificar que tipo de gráfico o geometría usaremos para visualizar las observaciones.

Veámoslo más detenidamente.

En ggplot2, para hacer un gráfico se empieza SIEMPRE llamando a la función ggplot(). Si tecleamos ggplot() en la consola o en un script, parece que no ocurre nada, pero tras la llamada a la función ggplot(), R ha creado un objeto, un contenedor para nuestro futuro gráfico. Aún no vemos el gráfico, faltan cosas, pero ya lo hemos inicializado. Si quieres ver el objeto/contenedor que hemos creado con la llamada a ggplot() tienes que asignarle un nombre, así podrás verlo en la pestaña “Environment” de RStudio.

Para verlo tienes que hacer:

my_grafico <- ggplot()

my_grafico, es un objeto R, concretamente una lista con 9 elementos, que tendremos que ir “llenando” para hacer nuestro gráfico.

Generalmente, dentro de la función ggplot() se suele especificar el conjunto de datos que vas a utilizar para hacer el gráfico. A diferencia de los gráficos de R-base, ggplot2 no permite graficar vectores: los datos que se suministran han de ser SIEMPRE data.frames o similares.

ggplot(data = iris)
ggplot(iris)

Ya está, con cualquiera de las 2 instrucciones de arriba, son equivalentes2, ya hemos inicializado el gráfico y le hemos dicho que datos vamos a usar.

Dijimos que para comenzar haríamos un gráfico de puntos de la variable Sepal.Length frente a Petal.Length; así que tenemos que decirle a ggplot2 que variables de iris queremos visualizar y con que propiedades estéticas queremos asociar cada variable. Para ello utilizaremos la función aes() dentro de ggplot(). Lo hacemos con la siguiente expresión:

ggplot(iris, aes(x = Sepal.Length,  y = Petal.Length))

Aún no vemos el gráfico, pero con aes() le hemos dicho a R/ggplot2 que queremos asociar/mapear la variable Sepal.Length al eje x, y la variable Petal.Length al eje y. Fíjate que, en la pestaña de gráficos de RStudio, ya vemos los ejes del gráfico; además, fíjate que ggplot2 ya ha calculado para nosotros el rango de las variables para ajustar los ejes.

Al igual que antes, podemos omitir los nombres de las opciones de la función aes(). Esta función puede tener muchos argumentos (veremos algunos), pero los 2 primeros son siempre x (el eje x) y después y (el eje y o vertical de mi gráfico); es decir podríamos hacer lo siguiente que es más rápido de teclear.

ggplot(iris, aes(Sepal.Length, Petal.Length))

Con esta instrucción le estamos diciendo a ggplot2 que vamos a hacer un gráfico con los datos del data.frame iris y que vamos a asociar/conectar/mapear la variable Sepal.Length con el eje x, y la variable Petal.Length con el eje y del gráfico. Perfecto, pero entonces ¿por qué no vemos el gráfico? La razón estriba en que no le hemos dicho a ggplot2 qué tipo de gráfico queremos (de puntos, de lineas etc…). El tipo de gráfico se explicita con una familia de funciones: geom_xx() o geometrías. Hay muchas geometrías o tipos de gráficos que podemos usar. Ya lo veremos!!! Nosotros queremos hacer un gráfico de puntos, así que, de la familia de geometrías tenemos que usar geom_point(). Veámoslo.

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()

Como vemos los puntos del gráfico se visualizan en color negro, con un tamaño, una transparencia y una forma determinadas. Obviamente todo esto se puede cambiar dentro de geom_point() con las opciones adecuadas. Por ejemplo:

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(color = "red", size = 2, alpha = 0.2)

Ya casi está. No es tan complicado. Ya hemos visto las ideas fundamentales de la visualización con ggplot2:

  1. Los gráficos se inician con la función ggplot(). Generalmente aquí se especifican los datos (data.frame) que queremos utilizar

  2. La función aes() sirve para asociar/mappear variables con atributos/características estéticas del gráfico. De hecho el nombre del función aes() viene de aesthetics. Las aesthetics más importantes de un gráfico suelen ser los ejes x e y, por eso se ponen siempre al principio de aes(); es decir, son los 2 primeros argumentos de aes(). En nuestro ejemplo hemos asociado la variable Sepal.Length con el eje x, y la variable Petal.Length con el eje y. Veremos más aesthetics, como por ejemplo el color o el tamaño (de los puntos … o de las lineas o …)

  3. Con geom_**() elegimos el tipo o geometría de gráfico. Hay muchos tipos de gráficos, así que habrán muchos geoms. Por ejemplo: geom_point(), geom_line(), geom_ …

Estas 3 ideas son las principales para entender ggplot2. Después hay muchas más opciones y elementos que serán muy importantes para conseguir un buen gráfico, pero en cierta forma son secundarias; por ejemplo, los títulos, los ejes, las escalas, el tema etc… lo iremos viendo poco a poco.


Afiancemos las ideas principales de la visualización con ggplot2. ¿Piensa que hará la siguiente linea de código?

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_line()

La primera parte de la instrucción es igual a la anterior: queremos un gráfico con el data.frame iris y queremos asociar la variable Sepal.Length con el eje x, con la propiedad aesthetic eje x y Petal.Length con el eje y; pero le hemos pedido un gráfico de lineas (geom_line()). En este caso hacer un gráfico de lineas no tiene mucho sentido, pero si se lo pedimos a R, este nos hace caso y nos lo muestra.


Ya dije que una de las características importantes de ggplot2 es que funciona por capas que se van superponiendo; para ir añadiendo capas a nuestro gráfico tenemos que usar el símbolo +. Por ejemplo si quisiéramos ver los puntos y las lineas ¿Cómo lo hacemos?

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_line()


Tampoco son muy útiles la lineas en este gráfico.

Otra geometría o geom_() que se usa mucho es geom_smooth(). Probémosla:

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() +  geom_smooth()

Del gráfico puede inferirse que hay una relación, no lineal, pero sí directa o positiva entre la longitud del sépalo y del pétalo; pero también se aprecia que hay al menos dos grupos distintos de lirios. Hay un grupo de observaciones cuyo pétalo parece ser claramente menor que el del resto de lirios. Veamos si esto se debe o esta asociado al tipo de lirio, recuerda que hay 3 tipos de lirios, asociados a la variable iris$Species. Para verlo en nuestro gráfico lo que vamos a hacer es asociar/mapear la variable Species con la aesthetics color:

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() 

Pues parece que sí, que la especie de lirios “setosa” es más pequeña, al menos en longitud, del sépalo, pero sobre todo del pétalo.

La variable Species podríamos haberla asociado a la estética tamaño (size), o a la estética forma (shape) pero no sería tan útil ni quedaría tan bonito el gráfico. Fíjate que incluso R nos avisa de que asociar la propiedad o aesthetic tamaño con una variable categórica como Species no es muy recomendable.

ggplot(iris, aes(Sepal.Length, Petal.Length, size = Species)) + geom_point() 
#> Warning: Using size for a discrete variable is not advised.

También podemos asociar la variable Species a la estética “forma”(shape). Lo hacemos en la expresión de más abajo. Como veis, ahora las diferencias entre especies de lirios no se aprecian tan bien como cuando usábamos color = Species

ggplot(iris, aes(Sepal.Length, Petal.Length, shape = Species)) + geom_point() 

Os va a costar hacer gráficos, normal!!!, pero espero que la idea principal ya la tengáis. Lo que pasa es que no os he contado todo, en realidad es un poco más complejo y versátil. Lo medio explico en el siguiente apartado.


Más ideas sobre ggplot2

Ya tenéis las ideas principales para hacer gráficos con ggplot2, pero no os lo he contado todo, tampoco lo voy a hacer ahora, pero si contaré las cosas de una forma diferente para que tengáis más flexibilidad/versatilidad a la hora de hacer gráficos. Si queréis saber toda la verdad3 tendréis que ir al libro de Hadley, concretamente aquí.

La forma que os he contado de hacer gráficos ggplot2 es la que veréis habitualmente, yo también hago mis gráficos así, solo que para entender mejor el funcionamiento, la sintaxis de ggplot2, os lo tengo que contar otra vez de una forma un poco diferente o ampliada.

Un gráfico de ggplot2 se inicia llamando a la función ggplot() eso es cierto y también es verdad que generalmente dentro de ggplot() se indica el data.frame que vas a utilizar y con aes() que variables vas a usar y con que elementos visuales o estéticos quieres asociar cada una de la variables que vas a utilizar4. Correcto, pero …. en realidad un gráfico ggplot2 se hace por capas, cada capa se especifica con una función de la familia geom_xx(), así que en realidad los datos y las aes() se “deberían” especificar dentro de la función geom_xx()`.

Parece un poco de lío pero en cuanto lo entiendas es muy fácil y te puede dar más flexibilidad a la hora de hacer tus gráficos. Empecemos: ¿recuerdas que hacen las lineas/expresiones de abajo?

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() 
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_line()

Podemos pensar que las funciones que hacen la representación gráfica realmente son las geom_xx(); es ahí donde deberíamos especificar los datos y variables/estéticas que queremos usar, pero si no las especificamos en la función geom(), entonces, ggplot2 mirará a ver si existen, si están especificados, dentro de ggplot().

Ahora que ya sabemos el funcionamiento básico de ggplot2, veamos algunos detalles mediante algunos ejemplos. Hasta ahora hemos especificado el data.frame que queremos graficar con ggplot(data = my_df) o con ggplot(my_df) y las variables que queremos ver, y a que propiedad estética queremos asociarla, con la función aes() dentro de ggplot(). Si lo hacemos así, todos los geoms_xx() que utilicemos compartirán el conjunto de datos y las variables/estéticas a mappear y mostrar; pero a veces, en gráficos más complejos podemos querer hacer que cada geom_xx() muestre datos y/o variables distintas.

Entender que cada geom_xx() puede estar asociado a distintos data.frames y/o variables es importante para tener más versatilidad con ggplot2.Por ejemplo, las siguientes tres expresiones hacen el mismo gráfico. Se suele utilizar la primera expresión, pero la segunda y tercera expresiones son “más flexibles”, aunque es verdad que si sólo se utiliza un geom_xx() no ganamos nada por usar la segunda o tercera expresión, pero no será el caso si en nuestro gráfico necesitamos usar varios geom_xx()

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()

ggplot(iris) + geom_point(aes(Sepal.Length, Petal.Length))

ggplot() + geom_point(data = iris, aes(Sepal.Length, Petal.Length))

Fíjate, como detalle, pero importante, que si utilizas la tercera expresión; es decir, si especificas los datos dentro de la función geom_xx(), es necesario poner el nombre del argumento ; es decir, debes poner data = iris, no puedes poner solo iris. Yo me olvido siempre de este detalle5.

En este caso (como el gráfico solo tiene una capa, como sólo usamos un geom(_xx)) no ganamos nada por usar la segunda o tercera expresión; PERO, cuando usemos varios geom_xx() esto nos dará muchas posibilidades para nuestro gráfico.

Intenta descubrir las diferencias y funcionamiento de las 3 siguientes instrucciones. Recuerda que puedes correr las instrucciones en R para ver que hacen exactamente.

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(aes(color = Species)) + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth(aes(color = Species))

Veámoslas una a una:

  1. Los datos y las 3 variables/estéticas dentro de ggplot()
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth()

En este caso los 2 geoms comparten el conjunto de datos (iris) y las variables/estéticas a graficar


  1. Los datos y 2 variables/estéticas dentro de ggplot(), pero una tercera variable (Species), asociada a la estética color, aparece solamente en geom_point()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(aes(color = Species)) + geom_smooth()


  1. Los datos y 2 variables/estéticas dentro de ggplot(), pero una tercera variable (Species), asociada a la estética color, aparece solamente en geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth(aes(color = Species))

Espero, seguro!!, que te has dado cuenta de que si especificas el data.frame y las variables/estéticas dentro de ggplot() esto afectará a todos los geoms del gráfico; pero lo que se especifique dentro de un geom_xx() solo afecta a esa geometría.


Otro ejemplo para entenderlo, ¿por qué no funciona la siguiente expresión?

ggplot(iris) + geom_point(aes(Sepal.Length, Petal.Length)) + geom_smooth(aes(color = Species))

Pues porque para poder representar la linea suavizada se utiliza geom_smooth(), y geom_smooth() necesita como mínimo tener variables asociadas a las estéticas x e y. Como veis, dentro de geom_smooth() solo hemos especificado la estética “color” y tampoco hemos especificado en la función ggplot() que variables se asocian con x e y. Por lo tanto, geom_smooth() no puede hacer su trabajo, le faltan los “datos” de x e y para calcular/obtener la linea suavizada.

Vamos con otros ejemplos. La siguientes expresiones tampoco funcionarán6 si intentáis correrlas en vuestro ordenador. ¿Por qué?

ggplot() + geom_point(data = iris, aes(Sepal.Length, Petal.Length)) + geom_line(aes(Sepal.Length, Petal.Length))

ggplot(aes(Sepal.Length, Petal.Length)) + geom_point(data = iris) + geom_line()

ggplot() + geom_point(data = iris, aes(Sepal.Length, Petal.Length)) + geom_line()


Otro ejemplo: hagamos algo más marciano/complicado. Supón que quieres hacer un gráfico diferenciando los puntos por color para las tres especies de lirios, pero quieres que solo se vea la linea suavizada para las dos especies más grandes (virginica y versicolor). Los lirios más pequeños son los de la clase setosa. Igual se puede hacer de otra forma pero la que me viene a la cabeza es hacer lo siguiente:

Primero, crear un dataset que sólo contenga a los lirios grandes, los de las especies virginica y versicolor.

iris2 <- iris %>% filter(Species != "setosa") #- me quedo con los lirios que no son de clase "setosa"

Para después hacer el gráfico con cualquiera de las 2 expresiones siguientes. Prefiero la segunda porque hay que teclear/escribir menos, pero puede que sea más didáctica la primera.

ggplot() + geom_point(data = iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_smooth(data = iris2, aes(Sepal.Length, Petal.Length) ) 

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point(aes(color = Species)) + geom_smooth(data = iris2)


Otro ejemplo más: ¿y si quisiéramos que las 2 especies grandes se representen con el mismo color? Hay varias soluciones, una de las más marcianas es la que propongo abajo. Es una solución rara, pero creo que os ayudará a entender ggplot2

Primero voy a crear un nuevo data.frame sólo con las observaciones de los lirios pequeños, los de la clase setosa.

iris_setosa <- iris %>% filter(Species == "setosa") #- me quedo con los lirios pequeños, los de clase "setosa"
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_point(data = iris_setosa, aes(color = Species)) + geom_smooth(data = iris2,aes(Sepal.Length, Petal.Length) )

Otra solución, quizás más lógica, consiste en primero agrupar las 2 especies de lirios grandes (versicolor y virginica) en una sola clase.

iris_solo_2_clases <- iris %>% mutate(Species_2 = ifelse(Species %in% c("versicolor", "virginica"), "versi_virgi", "setosa"))

Para después hacer el gráfico. Además el gráfico lo podemos hacer al menos de 2 maneras, la segunda mucho mejor, la primera expresión es un poco enrevesada:

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_point(data = iris_setosa, aes(color = Species)) + geom_smooth(data = iris_solo_2_clases,aes(Sepal.Length, Petal.Length, color = Species_2) )

ggplot(iris_solo_2_clases, aes(Sepal.Length, Petal.Length, color = Species_2)) + geom_point() + geom_smooth()

Como veis, en ggplot2 hay varias maneras de hacer el mismo gráfico. Esto al principio puede abrumar/molestar, pero muestra la flexibilidad de la sintaxis.

Para ir acabando con la “filosofía”/sintaxis/gramática de ggplot2 intenta imaginar que gráficos hacen las 6 expresiones de más abajo.

Si no puedes, recuerda que siempre puedes ejecutar las ordenes en el ordenador. Fíjate sobre todo en la tercera expresión

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth()
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point(color = "purple") + geom_smooth()

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point() + geom_smooth(color = "brown")
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_smooth(aes(color = Species))
ggplot(iris) + geom_point(aes(Sepal.Length, Petal.Length, color = Species) ) + geom_smooth(aes(Sepal.Length, Petal.Length, color = Species))


En la tercera expresión se especifica color = Species dentro de aes() en ggplot(), así que, de momento, todos los geoms del gráfico deberían diferenciar por especies de lirios usando el color, PERO, después se vuelve a usar el argumento color dentro de geom_point(), pero fíjate que no va dentro de de aes(), va fuera. Concretamente hacemos lo siguiente: geom_point(color = "purple"); es decir, para la capa de puntos, y solo para la capa de puntos que se crea con geom_point(), estamos asociando la estética color, no a una variable, sino a un color fijo. Sin embargo, para la otra capa del gráfico, la que resulta de usar geom_smooth() sigue siendo valido que la estética color está asociada a la variable Species.

Un detalle: imagina que en un gráfico en el que has fijado 3 estéticas dentro de ggplot(aes()). En principio las 3 estéticas afectarán a todos los geom_xx() que utilices en tu gráfico, pero si quisieras que, por ejemplo, la estética color no afectase a un geom concreto, podrías hacer lo siguiente: geom_xx(aes(color = NULL)).

Como puedes imaginar, aún tenemos que ver más elementos de ggplot2. Como mínimo los títulos y leyendas, los ejes, el tema, coordenadas, etc… vamos a ello!!


3. Elementos de un ggplot

Ya hemos presentado los principales elementos de los gráficos hechos con ggplot2, los que tienen que ver con la representación de las variables. Pero es evidente que un gráfico tiene muchos más elementos, y lógicamente hay que conocerlos un poco para poder ajustar los gráficos a nuestras necesidades y mejorar la calidad de nuestros gráficos.

Ejemplos de otros elementos son: títulos del gráfico y de los ejes, “theme” del gráfico, small multiples o faceting, anotaciones etc…

En está sección iremos más rápido. Se presentarán solamente algunos ejemplos, conceptos y/o aclaraciones. Si necesitas profundizar más en estos elementos, puedes acudir a la referencia oficial de ggplot2 o al bookdown de ggplot2.

Ya dijimos que los gráficos ggplot se componen de capas o layers. Para nosotros, hasta ahora, una capa estaba compuesta de 3 elementos:

  • un conjunto de datos

  • un conjunto de variables mapeadas con aes() a propiedades estéticas

  • una geometría, con geom_xx()

Es evidente que en todos los geoms no se pueden especificar todas las características estéticas. Por ejemplo si usas geom_point no podrás especificar la anchura o el tipo de las lineas, porque no estás usando lineas sino puntos. Para ver que estéticas admite cada geom tendrás que mirar la ayuda de cada geom. Al final de este post tienen un gráfico interactivo con el que se puede ver fácilmente que características estéticas admite cada geom. Por ejemplo geom_bar(), que sirve para hacer gráficos de barras, no admite mappear variables al eje Y, ya que en el eje Y se visualizan/mapean las frecuencias absolutas o relativas de la variable que se representa en el eje X.


Esto es lo básico que hay que saber, pero en realidad, una capa necesita de dos elementos más: una stat (o transformación estadística) y una posición. Estos dos últimos elementos son necesarios pero la verdad es que podríamos seguir haciendo gráficos con ggplot2 sin conocerlos. ¿Por qué? Pues porque si en una capa no los especificamos, lo hace ggplot2 por nosotros. Lo ha estado haciendo hasta ahora en todos los gráficos que llevamos hechos. Pero claro, saber como utilizar estos elementos nos dará más flexibilidad a la hora de hacer ggplots.

Generalmente las capas se van añadiendo con la familia de funciones geom_xx(), PERO también se pueden añadir capas con otra familia de funciones stat_xx().

Aparte de estos cinco elementos (datos, aes(), geom, stat y posición) los gráficos ggplot pueden tener más elementos. Veámoslos uno a uno.


3.1 Títulos del gráfico

Es evidente que un gráfico para ser efectivo y mostrar su mensaje con claridad debe tener un título y/o subtítulo ilustrativo y debe mostrar información relevante sobre que variables se grafican los ejes X e Y. Este tipo de elementos pueden modificarse de varias maneras, pero nos centraremos en la función labs().

Fíjate que con la función labs(), de labels, podemos cambiar los títulos del gráfico, de los ejes y también de las leyendas.

En los títulos (tanto del gráfico, como de los ejes y leyendas) también se pueden cambiar otras características; por ejemplo, cambiar el tamaño, la fuente o el color, pero eso será tarea de otra función de ggplot2: del grupo de funciones theme_(). Pero el tema o theme de los gráficos lo veremos en el siguiente apartado.

Tomemos el siguiente gráfico como referencia y sobre él iremos añadiendo elementos:

p <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point()
p

Con la función labs() podemos añadirle un título, subtitulo, pie de gráfico o caption. También podemos cambiar el título de los ejes X e Y, así como también el titulo de la leyenda para color, o para otras estéticas que utilicemos en el gráfico.

Es suficiente con ver un ejemplo:

p + labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo",
       subtitle = "(diferenciando por especie de lirio)",
       caption = "Datos provenientes del Iris dataset",
       x = "Longitud del sépalo",
       y = "Longitud del pétalo",
       color = "Especie de lirio")

Si quisieras eliminar completamente los títulos del eje X podrías hacerlo en el anterior chunk fijando x = NULL dentro de la función labs().

En lugar de usar la función labs(), también podemos utilizar las funciones auxiliares xlab() e ylab()

p + labs(color = NULL, x = NULL)  #- borra el título de la leyenda y del eje X
p + xlab(NULL) + ylab(NULL)       #- elimina títulos de los ejes X e Y

3.2 Themes

Themes control the display of all non-data elements of the plot. You can override all settings with a complete theme like theme_bw(), or choose to tweak individual settings by using theme() and the element_ functions. Use theme_set() to modify the active theme, affecting all future plots.

Para cambiar detalles de la apariencia del gráfico como el tamaño, fuentes y color de los títulos, pero también de los puntos, las lineas, el fondo del gráfico, la apariencia de las grid-lines, el lugar para las leyendas, etc… etc… contamos con las “funciones de tema”; todas ellas comienzan con theme_()

En general con las funciones theme_() podemos cambiar/ajustar cualquier elemento del gráfico, con la excepción de la propia representación de los datos (ya sabemos que esto se hacen con las funciones geom_()). Estos elementos afectan a la apariencia y detalles del gráfico, pero no a la relación entre variables que se muestra realmente en el gráfico.

Para empezar a entender que hacen las funciones relacionadas con el theme, señalar que ggplot2 incorpora un conjunto de “temas” que podemos utilizar para cambiar la apariencia del gráfico a nuestro gusto. Puedes verlos todos aquí. El tema que usa por defecto ggplot2 es theme_gray(). Veamos a los themes en acción:

p + theme_gray()   #- tema por defecto
p + theme_light()
p + theme_dark()
p + theme_classic()
p + theme_minimal()
p + theme_void()

El paquete de R ggthemes incorpora una amplia lista de temas adicionales, algunos de ellos tratan de replicar el estilo de corporaciones famosas como The Economist o Stata. En el tutorial no se ven bien los gráficos porque los he hecho pequeñitos, pero prueba a hacerlos tú mismo y verás que son good-looking. Veamos algunos:

library(ggthemes)
p + theme_economist()   
p + theme_fivethirtyeight()
p + theme_stata()
p + theme_solarized()

También podemos definir un tema propio para que el gráfico se ajuste los más posible a nuestras preferencias.

# define custom theme
my_theme <- theme(axis.text.x = 
                  element_text(colour = "grey20", size = 12, angle = 90, hjust = 0.5, vjust = 0.5) ,                   axis.text.y = element_text(colour = "grey20", size = 12) ,
                  text = element_text(size = 16))
p + my_theme

Se puede fijar el tema/theme de los gráficos con la función theme_set(). Por ejemplo:

theme_set(theme_minimal())  #- un tema concreto
theme_set(theme_minimal() + 
    theme(axis.text.x=element_blank(), axis.ticks.x=element_blank())) #- un tema modificando algunas opciones, que ele eje x no muestre ticks ni escalas

Si quieres volver al theme por defecto:

theme_set(theme_gray())

Ejemplos de algunos elementos cuya apariencia que se pueden cambiar con theme()

p + theme(legend.position = "none")            #- que no aparezca leyenda
p + theme(legend.position = "bottom")          #- leyenda abajo
p + theme(legend.direction = "horizontal")     #- leyenda horizontal!!
p + theme(legend.title = element_text(size = 22))     #- título de la leyenda a 22
p + theme(legend.key.size = unit(2.4, "cm"))          #- tamaño de los cuadros de la leyenda


p + theme(text = element_text(size = 20, face = "bold"))         #- cambiar el tamaño de todos los elementos de texto
p + theme(text = element_text(face = "bold"))                    #- pone en negrita todos los elementos de texto

p + theme(axis.text.x = element_text(colour = "pink", size = 12, angle = 90, hjust = 0.5, vjust = 0.5)) # apariencia de la escala del eje x

p + theme(axis.title.y = element_text(size=25, angle = 45)) #- tamaño y angulo del texto del eje Y

p + theme(plot.subtitle = element_text(hjust = 3))   #- posición horizontal del subtitulo (si lo tuviese)
  
p + theme(plot.caption = element_text(hjust = 3))    #- posición vertical del pie de gráfico (si lo tuviese)

p + theme(panel.background = element_rect(fill = "green", colour = "pink", linetype = "longdash", size = 3.5))
p + theme(panel.background = element_blank())
p + theme(panel.background = NULL)


p + theme(plot.background = element_rect(fill = "pink", colour = "purple", linetype = "dotted", size = 7))

Si quieres ver todos las características que controla y que por tanto puedes modificar con theme(), usa la ayuda de la función theme() o ejecuta en R args(theme). Aunque casi mejor verlas en esta infografía de Henry Wang o en este post de Isabella Benabaye con las opciones que suele cambiar ella.

En general, si quieres cambiar algún elemento de un ggplot, has de hacer theme(elemento = element_text()). Si quieres eliminar por completo algún elemento del gráfico, por ejemplo las grid-lines del gráfico, harías theme(panel.grid = element_blank()).

Evidentemente todo esto es imposible de aprender, sólo tienes que saber que cualquier elemento del gráfico se puede cambiar y tienes que saber buscar e interpretar la ayuda.

Colores

Se poco de colores, pero claro si quieres cambiar la apariencia de los gráficos, has de saber los colores de R, así que:

  • Aquí tienes una guía para elegir color. Si solo quieres ver la lista de nombres de los colores en R ejecuta: aa <- as.data.frame(colours())

  • Si sabes el nombre del color que quieres, aquí, podrás buscarlo y ver su color y su codificación en RGB y Hex.

  • También es interesante el paquete paleteer que agrupa un conjunto amplio de paletas de colores para usar en R.


XKCD theme

Por último, no sé si conocéis el webcomic XKCD. Pues en R también hay un paquete y un theme para hacer gráficos al estilo XKCD. Es el paquete xkcd cuyo autor es Emilio Torres-Manzanera de la Universidad de Oviedo. Aquí podéis ver algunos gráficos hechos con este estilo en R.

Intenté hacer un gráfico con su theme, pero desafortunadamente no me salió; pero justo al día siguiente vi este tweet que hace algo parecido con datos de los Simpsons y su código sí me ha funcionado, además simula/construye el estilo XKCD desde cero.

Además, después vi que Evangelyne Reinolds hizo esta maravilla. Lo haremos en clase?! En este post puedes encontrar el código para reproducir una de las historias o viñetas de XKCD.


3.3 Small multiples o Facetting

El sistema gráfico de ggplot2 incorpora una técnica especial llamada “faceting” que permite dividir un gráfico en múltiples gráficos. Cada uno de esos múltiples gráficos se realiza sólo para las observaciones de una de los valores de una variable categórica (o factor) incluido en el conjunto de datos. Es más fácil hacerlo que explicarlo/escribirlo.

Por ejemplo, en iris tenemos la variable Species, que es categórica. Lo que se hace con el “facetting” es dividir el dataset en grupos y hacer el mismo gráfico para cada uno de los grupos. Los grupos se van a definir en función de los valores de la variable Species. Recuerda que hay tres tipos o especies de lirios.

Para hacer un “facetting graph” podemos usar las funciones facet_wrap() y facet_grid().

Por ejemplo, con la función facet_grid() puedes elegir entre hacer los small multiples por filas o por columnas. Empecemos haciendo un facetting por columnas. Además, con facet_grid() se pueden usar varias sintaxis, pero la que aparece en la cheatsheet actual de ggplot2 y, por tanto, la recomendada es las que ves en la segunda linea:

#p + facet_grid( . ~ Species)                # old sintaxis
p + facet_grid(cols = vars(Species))         # gráficos x columnas, separando por valores de 'Species'

Ahora por filas:

p + facet_grid(rows = vars(Species))         # gráficos x filas

También podemos utilizar la función facet_wrap(). Esta función reparte los small multiples en una rejilla con forma de matriz.

p + facet_wrap(vars(Species), nrow = 2, ncol = 2)        # graf x filas y columnas

Si en el dataset hubiesen dos variables categóricas podríamos hacer que una de ellas sirviese para llenar las filas y la otra las columnas.

Como iris sólo tiene una variable categórica (Species) vamos a discretizar una de las variables continuas. Por ejemplo la anchura del pétalo, crearemos una nueva variable dividiendo las observaciones de Petal.Width en 2 categorías, por encima y por debajo de la media de su media. Además lo vamos a hacer con R-base y con dplyr::ntile(). Seguro que hay mejores formas, por ejemplo

Con dplyr::ntile()

iris <- iris %>% mutate(new_variable = ntile(Petal.Width, 2)) 

Con R-base y la función cut():

iris <- iris
iris$new_variable <- cut(iris$Petal.Width, 
                   breaks = c(-Inf, mean(iris$Petal.Width), Inf), 
                   labels = c("debajo-media", "arriba-media"))

Ahora ya tenemos dos variable discreta y podemos hacer que facet_grid() utilice una variable para llenar filas y otra para columnas. Se puede especificar de dos maneras

ggplot(iris) + geom_point( aes(Sepal.Length, Petal.Length, color = Species)) +
facet_grid(rows = vars(new_variable), cols = vars(Species))        # graf x filas y columnas
ggplot(iris) + geom_point( aes(Sepal.Length, Petal.Length, color = Species)) +
facet_grid(new_variable ~ Species)     

Como vemos, los lirios de la clase setosa siempre tiene el ancho de su pétalo por debajo de la media, y los virginica siempre están por encima de la media.

Ejes de los small multiples

Podemos ajustar las escalas de los ejes para que sean comunes para cada small multiple (la opción por defecto) o dejar que las escalas de cada gráfico varíen en función del rango de los datos representados:

p + facet_grid(rows = vars(Species))    #- escalas comunes
p + facet_grid(rows = vars(Species), scales = "free")   #- las escalas de cada small pueden variar
p + facet_grid(rows = vars(Species), scales = "free_y") #- solo dejamos libre/variar la escala del eje y

Solo muestro el resultado de la segunda expresión:

Un truquito: el argumento margin en ggplot2::facet_grid() añade margenes a los small multiples facilitando la visualización. Además añade un nuevo small con todas las observaciones.

Puedes probarlo tú mismo corriendo lo siguiente:

p + facet_grid(rows = vars(Species),  margins = TRUE)   

Otro truco, esta vez avanzado: Un gist para poner labels a los smalls multiples.


3.4 Anotaciones

Annotations are a special type of layer that don’t inherit global settings from the plot. They are used to add fixed reference data to plots.

Las anotaciones en los gráficos permiten resaltar algún fenómeno u observación de interés, y son importantes a la hora de contar historias (storytelling) con los gráficos y visualizaciones.

En el entorno ggplot podemos hacer anotaciones en nuestro gráficos de varias maneras, por ejemplo con annotate(). Aunque conceptualmente, como señala Hadley, las anotaciones son metadatos, desde el punto de vista práctico se usan los mismas funciones o geoms para manipularlos.

También existen algunas funciones auxiliares en ggplot2 y en paquetes específicos para hacer anotaciones en gráficos ggplot. Por ejemplo, cuando se hacen anotaciones en un gráfico de puntos es fácil que las anotaciones caigan unas encima de otras, el paquete ggrepel permite aliviar este problema.

Utilicemos la función annotate(). Por ejemplo, el siguiente chunk hace algunas anotaciones sin mucho sentido pero fáciles de entender:

Por ejemplo, el siguiente chunk usa annotate() para hacer algunas anotaciones sin mucho sentido, pero fáciles de entender:

p + annotate(geom = "text", x = 6, y = 2, label = "Una anotación", size = 5) +
    annotate("rect", xmin = 6, xmax = 7,ymin = -Inf, ymax = Inf, alpha = 0.2, fill = "pink") + 
    annotate("segment", x = 5, xend = 7, y = 6, yend = 8, colour = "blue") 


Anotaciones de texto en las observaciones

Agregar texto a un gráfico es una de las formas más comunes de anotación. Por ejemplo, para señalizar e identificar observaciones anómalas. Sin embargo, como señala Hadley, añadir texto no es fácil por la forma en la que R maneja las fuentes.

La función principal para el etiquetado de gráficos es geom_text(). Por ejemplo:

p + geom_text(aes(label = Species))

También podíamos haber añadido el valor de la longitud del pétalo.

p + geom_text(aes(label = Petal.Length))

Hemos añadido a cada observación un texto, proveniente de alguna de las variables de iris. Este gráfico no es muy útil, pero la técnica sí. Imagina que queremos marcar los lirios 45 y 1407. Podemos hacer lo siguiente:

#- seleccionamos los lirios más grandes de cada especie 
iris_max <- iris %>% group_by(Species) %>% slice_max(Petal.Length, n = 1)

p + 
 geom_text(data = iris_max, aes(label = Species), color = "black", size = 3)

Podemos ajustar la posición y tamaño del texto, etc.. Por ejemplo, podemos cambiar la alineación de las anotaciones con con hjust(“left”, “center”, “right”, “inward”, “outward”) y vjust (“bottom”, “middle”, “top”, “inward”, “outward”).


Lineas

Podemos añadir lineas:

p + geom_vline(xintercept = 6)
p + geom_hline(yintercept = 5, size = 1.7, colour = "purple", linetype = "dashed")
p + geom_abline(intercept = 0.7, slope = 0.4, size = 1.9, colour = "steelblue")


3.5 Cambiando los límites de los ejes

Si quieres modificar el recorrido de los ejes, los “límites” de los ejes, puedes usar lims(). Para los ejes X e Y hay dos funciones auxiliares: xlim() e ylim().

p + lims(color = c("setosa"), x = c(NA,6), y = c(1,8))

p + xlim(c (4, 6)) + ylim(c(NA, 5)) 

Se puede hasta dar la vuelta a los ejes

p + xlim(c (7, 3)) + ylim(c(NA, 5)) 

Los límites o dominio del gráfico suelen obtenerse automáticamente de los datos, pero, otra vez according to Hadley, hay dos razones por las que podemos estar interesados en cambiar los límites del gráfico:

  1. centrarnos en una región especifica del gráfico

  2. aumentar los límites para que varios gráficos ajusten sus escalas.


Por ejemplo, si después de hacer un gráfico quieres centrarte sólo en una parte; es decir, hacer un zoom sobre una parte del gráfico, tenemos 2 alternativas:

  1. Borrar los puntos que caen fuera de los limites de lo que quieras que se visualice (si en una escala continua solo quieres usar un límite pon NA):
p + xlim(c(4, 5)) + ylim(c(NA, 5)) #- cuidado, se pueden borrar observaciones

Con este enfoque tienes que tener cuidado, ya que si por ejemplo después utilizar alguna transformación estadística como por ejemplo geom_smooth(), las observaciones eliminadas al ajustar los límites no entrarán en el cálculo estadístico.

p + geom_smooth(color = "purple")
p + geom_smooth(color = "purple") + xlim(c(4, 5.7)) + ylim(c(1.5, 5))   # deletes points

  1. Cambiar los límites de los ejes X e Y haciendo un zoom en la región de interés pero sin eliminar puntos. Esto lo conseguimos con coord_cartesian().
p + geom_smooth(color = "purple")
p + geom_smooth(color = "purple") + coord_cartesian(xlim = c(4, 5.7), ylim = c(1.5, 5))


3.6 Escalas

Scales control the details of how data values are translated to visual properties. Override the default scales to tweak details like the axis labels or legend keys, or to use a completely different translation from data to aesthetic. labs() and lims() are convenient helpers for the most common adjustments to the labels and limits.

Las escalas permiten leer/interpretar un gráfico; permiten interpretar los elementos geométricos (por ejemplo en nuestro gráfico, los puntos) en función de los valores originales de las observaciones. Las escalas son un elemento más de los gráficos ggplot y se producen/controlan con la familia de funciones scale_xx()

En ggplot2 las escalas o guías se producen automáticamente, no vemos que hagamos nada, pero under the hood se están fijando con la familia de funciones scale_xx() que son las que controlan como se mapean los valores de las variables con las propiedades estéticas de nuestro gráfico (por ejemplo el eje X) de forma que podamos interpretar la posición de los distintos puntos mirando las escalas. Las escalas también construyen los elementos que permiten leer/interpretar los gráficos: los ejes y las leyendas.

Como ggplot2 hace el mapeo y genera las escalas y leyendas automáticamente, en la práctica podemos hacer gráficos sin saber como funcionan y, por tanto, sin saber manipular este elemento de un gráfico ggplot. Pero si aprendemos a manipular las escalas, esto nos dará más flexibilidad a la hora de utilizar ggplot2.

En muchos tipos de datos es importante pararse a pensar cual es la mejor escala para representar las variables. Quizás sea conveniente cambiar la escala de un eje para distribuir mejor las observaciones en el espacio, o para interpretar mejor las variaciones entre observaciones; por ejemplo la escala logarítmica o en porcentajes son a veces más apropiadas que las escalas originales.

En realidad, para cada par variable/estética representada en un gráfico ggplot es necesaria una escala, y tendría que fijarse con una de las funciones de la familia scale_xx(). Realmente cuando hacemos este gráfico, en el que asociamos 3 variables a 3 propiedades estéticas con aes(), se necesitaría especificar las escalas de las 3 variables:

p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) +  geom_point(aes (color = Species)) 

Bien, pero entonces ¿por qué no lo hacemos?, ¿por qué no especificamos las escalas? Pues porque lo hace ggplot2 por nosotros. En realidad cuando ejecutamos la expresión anterior, realmente se está haciendo lo siguiente:

ggplot(iris, aes(Sepal.Length, Petal.Length)) +   geom_point(aes (color = Species)) +
  scale_x_continuous() + 
  scale_y_continuous() + 
  scale_color_discrete()

Es decir, ggplot2 asigna a cada variable una escala, a las variables continuas les asigna una escala continua y a las categóricas una escala discreta. PERO, si queremos, si lo consideramos apropiado podríamos cambiar la escala. Por ejemplo:

p + scale_y_reverse() + scale_colour_grey()
p + scale_x_sqrt() + scale_y_log10()
p + scale_x_continuous(trans = "log")
p + scale_color_brewer(palette = "Dark2")

Solo represento la primera transformación, en la que se da la vuelta a la escala del eje Y y el color, asociado a la variable Species, pasa a escala de grises:


Labels de los ejes

También se pueden modificar lo que vemos en los escalas. Antes vamos a modificar los títulos de los ejes y leyendas.

p <- p + labs(x = "Eje X", y = "Eje Y", color = "Leyenda\n para el color")

De momento la escala del eje X varia aproximadamente de 3 a 8. Los títulos de la escala del eje X sólo muestra los valores 5, 6, 7 y 8. Vamos a modificar “el texto”, los números que se ven y que sirven de guía para la escala del eje X:

p + scale_x_continuous(breaks = seq(3, 10, 0.5), limits = c(3, 10)) 

En el eje Y se ven los números 2, 4 y 6. Cambiemos los labels de su escala (no del eje Y, sino de la escala del eje Y):

p + scale_y_continuous(breaks = seq(0, 12, 0.25),  label = scales::dollar) 

También se pueden modificar los de la leyenda para el color:

p + scale_color_manual(values = c("purple", "pink", "red2"), name = "Especies\n de lirios")

Si hubiese usado una variable discreta asociada a la estética “shape”, tendríamos que usar scale_shape_discrete(), si la variable asociada a shape fuese continua: scale_shape_continuous()

¿Y si hubiésemos usado una variable continua para la estética “fill”? Pues scale_fill_continuous()


Familia de funciones de escala

• scale_continuous()
• scale
_discrete()
• scale_ordinal()
• scale
_manual()
• scale_{color/fill}brewer()
• scale
{color/fill}distiller()
• scale
{color/fill}_gradient()


3.7 Stats (transformaciones estadísticas)

Puede leerse en la web de ggplot2, concretamente aquí lo siguiente:

A layer combines data, aesthetic mapping, a geom (geometric object), a stat (statistical transformation), and a position adjustment. Typically, you will create layers using a geom_ function

PERO

A handful of layers are more easily specified with a stat_ function, drawing attention to the statistical transformation rather than the visual appearance. The computed variables can be mapped using stat().

Algunos gráficos, como los gráficos de puntos, no requieren del uso de transformaciones estadísticas de las observaciones, pero otros gráficos como rectas o curvas de predicción o como los boxplots o diagramas de caja, sí que las necesitan 8. Podemos usar transformaciones estadísticas en gráficos ggplot con la familia de funciones stat_xx()

Por ejemplo, cuando se hace un diagrama de caja o boxplot, no se representan las observaciones originales, sino que se muestran 5 estadísticos resumen de la distribución de los datos; es decir, se utiliza una transformación estadística. Cuando usábamos geom_smoth() tampoco representábamos con él los datos originales, sino una transformación estadística de estos. Concretamente la transformación estadística que utiliza geom_smoth() es genéricamente un “smoother”, calcula mediante una rolling-windows la media de y, condicionada a x.

Cada función geom_xx() que utilicemos, en realidad necesita de un stat_xx(), entonces ¿por qué nunca lo hemos usado/especificado nosotros? Con ggplot2 la razón es casi siempre la misma: porque under the hood ggplot2 hace muchas cosas por nosotros. En concreto, cada vez que usamos un geom_xx() en realidad ggplot2 está fijando una transformación estadística por defecto por nosotros. Ggplot es un sistema muy completo, pero aún así, una vez lo entiendes, hacer gráficos con el es relativamente fácil y rápido porque muchas de sus opciones opciones no hace falta especificarlas directamente.

Por ejemplo, ¿cual es la transformación estadística que se usa por defecto en geom_point()? Ninguna, bueno, en realidad usa stat = "identity", pero como lo especifica ggplot2 automáticamente, nosotros no nos damos cuenta.

Cuando hacíamos este gráfico:

ggplot(iris, aes(Petal.Length, Sepal.Length)) + geom_point() 

En realidad estábamos haciendo

ggplot(iris, aes(Petal.Length, Sepal.Length)) + geom_point(stat = "identity")

¿Qué transformaciones estadísticas podemos hacer cuando usemos geom_point()? Por ejemplo, geom_point(stat = "unique") sólo representaría las observaciones únicas o NO repetidas. En este caso creo que iris no tiene observaciones repetidas, así si ejecutáis la instrucción de abajo se seguirán visualizando los 150 lirios.

ggplot(iris, aes(Petal.Length, Sepal.Length)) + geom_point(stat = "unique") #- dejaría solo observaciones no repetidas

Podemos consultar las opciones por defecto completas de geom_point() aquí

Cada geom_xx() tiene un default statistic, pero podemos cambiarlo y especificar otra stat para adaptarlo a nuestras necesidades. Por ejemplo, the default statistic for geom_bar() is stat_bin() pero podemos usar otras stat_xx. Otra vez parece un trabalenguas, pero cuando lo entiendes es relativamente sencillo.

Por ejemplo, en nuestro gráfico de puntos, podemos usar otras transformaciones estadísticas, una de las que más sentido tiene es calcular medias móviles con un método de alisado (smoother)

p <- ggplot(iris, aes(Petal.Length, Sepal.Length, color = Species)) 

p + geom_point(stat = "identity")

p + geom_point(stat = "smooth", method = "auto")

Aunque en este caso es bastante más fácil hacerlo con:

p + geom_point() + geom_smooth()
p + geom_point() + stat_smooth()

p + geom_point() + stat_smooth(method = "lm",   se = FALSE,  size = 1)
p + geom_point() + geom_smooth(method = "lm",   se = FALSE,  size = 1)

p + geom_point() + geom_smooth(method = "lm", col = "#C42126",  se = FALSE,  size = 1)


Algunas transformaciones estadísticas útiles y en qué geoms están disponibles:

- stat_bin(): geom_bar(), geom_freqpoly(), geom_histogram()  
- stat_bin2d(): geom_bin2d()  
- stat_bindot(): geom_dotplot()  
- stat_binhex(): geom_hex()   
- stat_boxplot(): geom_boxplot()    
- stat_contour(): geom_contour()  
- stat_quantile(): geom_quantile()   
- stat_smooth(): geom_smooth()  
- stat_sum(): geom_count()   

Es raro que tengamos que usar estas funciones stats_xx() directamente, pero si quieres ver que hacen exactamente conviene consultar la documentación para ver qué hace y cómo se aplica exactamente cada transformación estadística a los datos.

Hay otras funciones stat_xx() que no se pueden utilizar con las funciones geom_xx():

- stat_ecdf(): compute a empirical cumulative distribution plot.  
- stat_function(): compute y values from a function of x values.  
- stat_summary(): summarise y values at distinct x values.  
- stat_summary2d(), stat_summary_hex(): summarise binned values.  
- stat_qq(): perform calculations for a quantile-quantile plot.  
- stat_spoke(): convert angle and radius to position.  
- stat_unique(): remove duplicated rows.  


Algunos ejemplos

Veamos algunos ejemplos útiles:

  1. En un diagrama de caja mostrar la media:

Queremos que la media se represente como un punto, entonces usamos geom_point() pero no queremos representar los valores originales, sino la media, así que dentro de geom usamos la opción stat = summary

ggplot(iris, aes(Species, Sepal.Length)) + 
  geom_boxplot() + 
  geom_point(stat = "summary", fun.y = "mean", colour = "red", size = 4)

Se conseguiría los mismo usando directamente la función stat_summary() con la opción geom = “point”. Sí, en ggplot2 las cosas se pueden hacer de varias maneras!!

ggplot(iris, aes(Species, Sepal.Length)) + 
  geom_boxplot() + 
  stat_summary(geom = "point", fun.y = "mean", colour = "red", size = 4)
  1. Otro ejemplo: el default stat de geom_histogram es stat = “bin”, mostrandonos el número de observaciones en cada bin. Si queremos que nos muestre frecuencias relatívas al grupo o bin más numeroso:
ggplot(iris, aes(Sepal.Length)) + geom_histogram() 

ggplot(iris, aes(Sepal.Length)) + geom_histogram(aes(y = stat(count / max(count))))


Bonus: Con stat_function() podemos dibujar curvas de densidad:

df <- tibble(x = c(-20, 20))
  
ggplot(df, aes(x = x)) +
stat_function(fun = dnorm, args = list(mean = 0, sd = 5), color = "black") +
stat_function(fun = dnorm, args = list(mean = 0, sd = 1), color = "red") +
stat_function(fun = dnorm, args = list(mean = 0, sd = 3), color = "blue")


3.8 Position adjustments

All layers have a position adjustment that resolves overlapping geoms. Override the default by using the position argument to the geom_ or stat_ function.

Los ajustes de posición afectan a la posición de los elementos de una capa. Los gráficos en los que más se utilizan los ajustes de posición son los gráficos de barras. Su posición por defecto es position = “stack”. Se pueden cambiar con el argumento geom_bar(position = “xxxx”), aunque si usas las funcionesposition_xx()` tienes más flexibilidad:

ggplot(iris , aes(Species)) + geom_bar()
ggplot(mtcars, aes(cyl)) +  geom_bar()

Para poder visualizar gráficos de barras con 2 variables,tenemos que usar otro dataset: mtcars

ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar() #- pos
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "fill")
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "dodge")
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = position_dodge2(preserve = "single"))


Por ejemplo, podemos modificar la posición por defecto de nuestro gráfico de puntos con el iris dataset usando dos enfoques:

ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_point(position = "jitter", color = "pink") 
ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() + geom_jitter( color = "pink") 

geom_jitter(), o alternativamente geom_point(position = "jitter") cambia la posición original de los datos añadiendo un poco de ruido, haciendo que se desplacen un poco. Esta técnica se usa muchos cuando hay muchos datos similares (overplotting).


3.9 Coordenadas

The coordinate system determines how the x and y aesthetics combine to position elements in the plot. The default coordinate system is Cartesian (coord_cartesian()), which can be tweaked with coord_map(), coord_fixed(), coord_flip(), and coord_trans(), or completely replaced with coord_polar()

Por ejemplo: coord_fixed(). En nuestro gráfico, tanto la longitud del pétalo como del sépalo se miden en las mismas unidades, así que su ratio implícito es 1 a 1. Cambiemos el ratio de las coordenadas con coord_fixed()

p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point() 

p + coord_fixed(ratio = 1/3)
p + coord_fixed(ratio = 3/1)


4. Combinando gráficos

La técnica del faceting es una fantástica herramienta para dividir un gráfico en varios, en principio en función de una variable categórica, si queremos usar una variable continua primero habría que discretizarla. PERO, a veces lo que interesa es crear una figura compuesta de varios gráficos diferentes. Esto no es un elemento más de ggplot2 es una operación que hacemos sobre un grupo de varios gráficos.

Podemos hacerlo con varios paquetes:

  • con el paquete gridExtra y su función grid.arrange()
library(gridExtra)
p1 <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point()
p2 <- ggplot(iris)+ aes(Species, Sepal.Length) + geom_boxplot()

grid.arrange(p1, p2, ncol = 2, widths = c(6.5, 3.5))

Además de los argumentos ncol, nrow y widths, con gridExtra se pueden hacer composiciones más complejas

library(patchwork)
p1 + p2 + plot_layout(ncol = 2)

  • con el paquete cowplot. Este paquete también se puede utilizar para hacer anotaciones o para incorporar imágenes a nuestros gráficos
library(cowplot)
ggdraw(p1) + draw_image(here::here("./imagenes/Captura.JPG"), 
               x = 1, y = 1, hjust = 1, vjust = 1, width = 0.33, height = 0.42)


5. Exportando gráficos

La mayoría de las veces, cuando creamos un gráfico lo vemos inmediatamente, pero también podemos asignarle un nombre y manipularlo más adelante.

p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()
p + geom_line()

Una vez que tienes el gráfico guardado como un objeto en el R environment puedes hacer varias cosas con él:

  1. verlo en la pantalla con print() o llamándolo con “su nombre”.

  2. guardarlo en un fichero en diferentes formatos. Piensa que estamos guardando el gráfico, la imagen, la representación del gráfico, no el objeto R.

    • Podemos usar la “Export Tab” en el Plot Pane de Rstudio. Lo grabará en baja resolución. También podemos cambiar las dimensiones (anchura y altura) del gráfico. Si vamos a utilizar el gráfico en Word es conveniente guardar el gráfico como Metafile.

    • Como alternativa podemos usar la función ggsave() que, ademas, nos permite cambiar el tamaño y la resolución del gráfico con los argumentos width, height y dpi. Nota: Los parámetros width and height también determinan el tamaño de la fuente del gráfico guardado.

ggsave("./graf_out/my_grafico_chulo.png", p, width = 15, height = 10)
ggsave("filename.png", plot = my_plot, width = 8, height = 6, units = "in", dpi = "retina")

# Tb funciona para figuras compuestas de varios gráficos
grafico_combinado <- grid.arrange(p1, p2, ncol = 2, widths = c(6, 4))
ggsave("fig_output/my_combo_plot.png", grafico_combinado, width = 10, dpi = 300)
  • Finalmente, podemos guardar una copia completa del gráfico, no de su representación visual, sino del objeto R con saveRDS(), para luego leerlo con readRDS(). Aunque, ya que nos ponemos, es mejor guardar el script que genera el objeto. Sólo tendría sentido si fuese muy costoso, por ejemplo en términos de tiempo, reproducir el gráfico.
saveRDS(p, "plot.rds")
my_valioso_grafico <- readRDS("plot.rds")



6. Tipos de gráficos

En está sección presentaremos algunos ejemplos de algunos de los gráficos más utilizados en el análisis de datos. Puedes ver listados más completos en:


6.1 Histogramas

Se utilizan para mostrar la distribución de UNA variable continua, por ejemplo la longitud del sépalo (Sepal.Length).

Para hacer histogramas en ggplot2 se utiliza geom_histogram y, a veces, geom_freqpoly(). Los dos geoms trabajan de la misma manera, dividen la variable x en intervalos y cuentan las observaciones en cada intervalo, para mostrarlas en el eje Y. La diferencia entre ellos es que geom_histogram utiliza barras para mostrar el número de observaciones o frecuencia absoluta, mientras que geom_freqpoly() usa lineas.

ggplot(iris, aes(Sepal.Length)) + geom_histogram() 

Como ves, lo que hace un histograma es dividir el eje X en intervalos o “bins” y mostrar en el eje Y el número de observaciones de x que caen en cada “bin”. Además, ggplot2 nos avisa de que por defecto se divide el eje X en 30 intervalos, pero que podemos cambiarlo con bins = my_n_de_bins.

p <- ggplot(iris, aes(Sepal.Length))
p + geom_histogram(bins = 40) + xlab("40 intervalos")
p + geom_histogram(bins = 4) + xlab("Sólo 4 intervalos")

Otra opción interesante es elegir la anchura del intervalo con la opción binwidth.

p <- ggplot(iris, aes(Sepal.Length))
p + geom_histogram(binwidth = 0.1) + xlab("He elegido la anchura = 0.1")
p + geom_histogram(binwidth = 1.1) + xlab("Esta vez la anchura = 1.1")

Si en el eje Y queremos frecuencias relativas o porcentajes en lugar de counts, podemos hacerlo con:

ggplot(iris, aes(Sepal.Length)) + 
  geom_histogram(aes(y = stat(count) / sum(count)), bins = 10) +
  scale_y_continuous(labels = scales::percent)

Se puede mejorar bastante la apariencia del histograma jugando con los colores y opciones:

ggplot(iris, aes(Sepal.Length)) + geom_histogram(bins = 10, color = "black", fill = "tomato")

Si asociamos/mapeamos la variable Species a alguna estética, por ejemplo a la estética “fill” o relleno de las barras con aes(fill = Species), se visualizará qué parte de cada intervalo pertenece a cada clase de lirio.

ggplot(iris, aes(Sepal.Length)) + geom_histogram(bins = 10, aes(fill = Species), color = "black")

Evidentemente, también podemos visualizar como cambia la distribución de la anchura del sépalo entre los 3 tipos de lirios si hacemos un small multiple:

p <- ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_histogram(bins = 10, color = "black")
p + facet_grid(cols = vars(Species))

Para mejorar la visualización podemos poner en el fondo el histograma para todos los datos. Lo aprendí aquí, aunque ahora mismo hay un paquete para implementar esta técnica, es el paquete gghighlight.

iris_backgroung <- iris %>% select(-Species)
ggplot(iris, aes(x = Sepal.Length)) +
  geom_histogram(data = iris_backgroung, fill = "grey", bins = 15) +
  geom_histogram(aes(fill = Species), bins = 15) +
  facet_grid(cols = vars(Species))


geom_density()

Una alternativa a los histogramas son los gráficos de densidad con geom_density(). Pero, según Hadley:

they are harder to interpret since the underlying computations are more complex. They also make assumptions that are not true for all data, namely that the underlying distribution is continuous, unbounded, and smooth.

Como dice Hadley, geom_density() estima la función de densidad, por lo que la estimación depende de una serie de paramétros como adjust. Hacemos uso de xlim(3, 9) para expandir loslímites del eje X.

ggplot(iris, aes(Sepal.Length)) + 
  geom_density(color = "red",   size = 1.2) +  
  geom_density(color = "blue",  size = 1.2, adjust = 3) +
  geom_density(color = "black", size = 1.2, adjust = 0.5) +  xlim(2, 10)


Podemos asociar la variable Species a la estética fill con aes(fill = Species) para obtener una estimación de la densidad para cada tipo de lirio.

ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_density(position = "stack", alpha = 0.5)


Hay muchas otras posibilidades. Por ejemplo:

ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_density(position = "fill", alpha = 0.5) #- position = "fill"

ggplot(iris, aes(Sepal.Length, stat(count), fill = Species)) + geom_density(position = "stack", alpha = 0.5) #- stat(count)

Muchas veces se suelen hacer los histogramas superponiendo la f. de densidad normal o una estimación de la densidad de x con geom_density() o con geom_line(stat="density"). Para que el gráfico se vea mejor, ajustaremos el eje X con xlim():

#- calculamos media y desviación tipica de Sepal.Length para luego usarlas para construir la curva normal
media <- mean(iris$Sepal.Length, na.rm = TRUE)     #- media de la longitud del sépalo
desviacion <- sd(iris$Sepal.Length, na.rm = TRUE)  #- desviación 

#- hacemos el histograma
p <- ggplot(iris, aes(Sepal.Length)) +
      geom_histogram(aes(y=..density..),  color="black", fill = "steelblue", alpha = 0.2)

#- le añadimos la densiidad estimada y la normal
p + geom_density( color="purple", size = 1) +
    stat_function(fun = dnorm, colour = "red", size = 1, args = list(mean = media, sd = desviacion))  + 
    xlim(c(min(iris$Sepal.Length)-1, 9))

Si necesitas saber más cosas sobre los histogramas puedes acudir aquí o aquí.

Joy Division plots

Hace poco apareció el paquete ggridges y se pusieron de moda estos tipos de gráficos:

library(ggridges)
ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(aes(fill = Species), alpha = 0.5)

Como se parecen a la mítica portada del primer disco de Joy Division, algunos los conocen como Joy Division plots. Abajo os pongo un ejemplo sacado de la vignette del paquete ggridges.

library(viridis)
ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = `Month`, fill = ..x..)) +
  geom_density_ridges_gradient(scale = 3, rel_min_height = 0.01) +
  scale_fill_viridis(name = "Temp. [F]", option = "C") +
  labs(title = 'Temperatures in Lincoln NE in 2016')


6.2 Scatter plot

Scatter plot, gráfico de puntos o gráfico X-Y. Se utiliza para mostrar la relación entre DOS variables continuas. Lo tenemos más que visto, ya que es el tipo de gráfico que hemos utilizado durante todo el tutorial.

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + 
     geom_point() +
     labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo",
       subtitle = "(diferenciando por especie de lirio)",
       caption = "Datos provenientes del Iris dataset",
       x = "Longitud del sépalo",
       y = "Longitud del pétalo",
       color = "Especie de lirio")

Overplotting

Los gráficos de puntos se usan habitualmente para mostrar la relación entre dos variables continuas. En conjuntos de datos con muchas observaciones pueden tener un problema de “overplotting”. Esta situación ocurre cuando unos puntos se dibujan encima de otros, de forma que no se aprecia bien la relación entre las variables

Hay varias formas de tratar este problema. Veámoslo con un ejemplo sacado de la web de ggplot2:

set.seed(1234)
df <- data.frame(x = rnorm(2000), y = rnorm(2000))   #- creamos un conjuto de datos con 2000 observaciones y 2 v.

p <- ggplot(df, aes(x, y)) + xlab(NULL) + ylab(NULL)

p + geom_point()
p + geom_point(shape = 1) # Hollow circles
p + geom_point(shape = ".") # Pixel sized

#- For larger datasets with more overplotting, you can use alpha blending (transparency) t
p + geom_point(alpha = 1 / 10)


De forma alternativa, podemos lidiar con el overplotting utilizando otro enfoque que consiste en pensar en el overplotting como un problema de densidad en 2 dimensiones y utilizar un hex-plot. En un hex-plot se representan regiones (generalmente hexagonales) coloreadas en función del número de observaciones que caen en cada región, de forma que es equivalente a un histograma pero en 2d.

p + geom_bin2d()
p + geom_bin2d(bins = 10)
p + geom_hex()
p + geom_hex() + scale_fill_gradient2(low = "#132B43", high = "#56B1F7")  

En este post utilizan hex-plots para mostrar las zonas desde donde tiran a canasta los jugadores NBA. En este otro post utilizan una combinación de geom_point(alpha = .2, pch = 15) + geom_density_2d() para analizar las puntuaciones de videojuegos. Además hace un uso avanzado de las escalas, al menos para mi, para que el gráfico sea fácil de visualizar.

Acaba de aparecer un nuevo geom: geom_pointdensity(). Como dicen en su repo de Github: “A cross between a scatter plot and a 2D density plot”.

#devtools::install_github("LKremer/ggpointdensity")
library(viridis)
library(ggpointdensity)
p +  geom_pointdensity(adjust = 7) +
  scale_color_viridis()

Si quieres saber más cosas sobre cómo hacer scatterplots con ggplot2 puedes hacerlo por ejemplo, aquí o aquí.


6.3 Boxplot (gráficos de caja)

Para visualizar una variable cuantitativa se suelen usar los histogramas, pero si lo que quieres es visualizar como varían los valores de una variable continua en función de los valores de una variable categórica (Species) se suelen usan boxplots o diagramas de caja. Son útiles para comparar diferentes grupos y para identificar outliers.

Para hacer un boxplot o diagrama de caja se utiliza geom_boxplot(). El gráfico muestra 5 estadísticos: la mediana con una linea gruesa, el primer y tercer cuartil de los datos con los limites de la caja y el máximo y el mínimo (los limites de la linea vertical). Adicionalmente, si existen outliers, estos también se representarán.

ggplot(iris, aes(x = Species,  y = Sepal.Length)) + geom_boxplot() 

Se pueden cambiar algunas opciones del gráfico

p <- ggplot(iris, aes(x = Species,  y = Sepal.Length)) 
p + geom_boxplot(aes(fill = Species), outlier.colour = "purple")

A veces, para mejorar la visualización, conviene rotar los ejes. Podemos hacerlo con coord_flip()

p <- ggplot(iris, aes(x = Species,  y = Sepal.Length)) + geom_boxplot(aes(fill = Species)) 
p + coord_flip()

Para mejorar la visualización podemos incluir las observaciones originales con geom_point(), pero habría mucho overplotting, así que mejor con geom_jitter()

p + geom_jitter(width = 0.15, alpha = 1/4, color = "tomato")

Podemos añadir con stats() algún estadístico más con stats_xx(), por ejemplo la media con stats_summary:

p + stat_summary(fun.y = "mean", geom = "point", color = "purple", size = 2.5)

Los boxplots resumen la distribución de una variable cuantitativa con sólo cinco números, proporcionando un resumen útil de los datos, pero ocultan la forma de la distribución; por ejemplo, si la distribución fuese bimodal, no lo apreciaríamos. Además, aunque usemos geom_jitter() para superponer los valores originales, a veces es difícil a simple vista inferir la distribución de estos. Por ello hay varias técnicas/geoms útiles que ayudan a resolver el problema, por ejemplo geom_violin(). Una alternativa al boxplot son los gráficos de violín donde se estima y muestra la forma de la distribución de las observaciones:

p <- ggplot(iris, aes(x = Species,  y = Sepal.Length)) 
p + geom_violin(aes(fill = Species), alpha = 0.6)
p + geom_violin(aes(fill = Species), alpha = 0.6) + geom_jitter(width = 0.15, alpha = 1/4)


En el ejemplo que hemos usado hemos tenido suerte y los tres grupos estaban ordenados, pero otras veces no tendremos tanta suerte, por ejemplo si en lugar de graficar la longitud de sépalo queremos visualizar la anchura (Sepal.Width):

ggplot(iris, aes(x = Species,  y = Sepal.Width)) + geom_boxplot()

¿Cómo ordenamos los grupos? Por ejemplo podemos ordenarlos de menor a mayor en función de su anchura media del sépalo. Para ello usamos stats::reorder()

ggplot(iris, aes(x = reorder(Species, Sepal.Width, mean),  y = Sepal.Width)) + geom_boxplot() +
  xlab("De menor a mayor anchura del sépalo")

6.4 Gráficos de barras

Los gráficos de barras se utilizan para representar una variable categórica, como por ejemplo Species, o variables cuantitativas discretas. Se representan barras verticales proporcionales a los valores de la variable en cada categoría o valor. Para crear gráficos de barras con ggplot2 se usa geom_bar().

Para hacer nuestro primer gráfico de barras no vamos a utilizar iris porque en la única variable categórica (Species) resulta que hay 50 lirios en cada tipo de especie. Usaremos el data.frame mpg del paquete ggplot2 que contiene 234 observaciones sobre distintos modelos de coches y sus características. Algunos de los ejemplos están sacados de la web de ggplot2.

p <- ggplot(mpg, aes(class))
p + geom_bar()
p + geom_bar(fill = "steelblue") + coord_flip()

Como vemos, la variable class es categórica, concretamente tiene 7 grupos o categorías de coches. Las barras verticales son proporcionales al número de vehículos en cada categoría. Con coord_flip() las categorías pasan a representarse en el eje Y, haciendo que, generalmente, se visualicen mejor los nombres de las categorías.


Si en lugar de tener una tabla de datos, tenemos ya una tabla de frecuencias, tendremos que especificar en aes() que la variable con las frecuencias se mapee/asocie al eje Y: aes(x = variable, y = frecuencias). Además tendrás que usar geom_bar(stat = "identity") o directamente usar geom_col() que ya usa por defecto stat_identity(). Veámoslo con un ejemplo:

df <- mpg %>% group_by(class) %>% count
p <- ggplot(df, aes(x = class, y = n))
p + geom_bar(stat = "identity",  fill = "steelblue") 
# p + geom_col(fill = "steelblue")                          #- hace exactamente el mismo plot


Distintas posiciones para las barras

Para esta subsección utilizaremos el data.frame mtcars

ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar() #- pos
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "fill")
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "dodge")
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = position_dodge2(preserve = "single"))


Reordenando las categorías

A veces es importante reordenar las barras para mejorar la visualización. Para ello tendremos que usar factores. Por ejemplo, volviendo al mpg dataset:

df <- mpg
df <- df %>% mutate(class = forcats::as_factor(class)) #- convertimos la v. class a factor con la f. as_factor()
df <- df %>% mutate(class = forcats::fct_infreq(class)) #- fct_infreq() los niveles del factor según su frecuencia de mayor a menor
p <- ggplot(df, aes(fct_rev(class))) #- fct_rev() ordena los levels de menor a mayor
p + geom_bar(fill = "steelblue") + coord_flip()


Si queremos que en las barras se visualice el número de observaciones o frecuencia absoluta de cada categoría:

p + geom_bar(fill = "steelblue") + coord_flip() +
    geom_text(stat='count', aes(label = ..count.. ), hjust = -0.15, size = 3.25) 


Porcentajes en lugar de counts

Si queremos que las barras representen porcentajes en lugar de numero de casos en cada categoría:

p <- ggplot(df, aes(fct_rev(class))) #- fct_rev() ordena los levels de menor a mayor
p + geom_bar(aes( y = (..count..)/sum(..count..)), fill = "steelblue")  + coord_flip()

En realidad esto mismo se pude hacer de varias formas. En este post nos muestran 5 formas de hacerlo.


Si quieres saber más sobre como hacer gráficos de barras con ggplot2, puedes hacerlo aquí o aquí.


6.5 Gráficos de lineas

Los gráficos de lineas se usan principalmente para mostrar la evolución de variables en el tiempo. Generalmente la variable que se muestra en el eje X es el tiempo. En Economía estos gráficos puede que sean los más usados y habitualmente nos referimos a ellos como gráficos temporales.

Podemos simular gráficos de tiempo con geom_line() y geom_path(). Estos 2 geoms grafican lineas entre dos observaciones de una variable. Por ejemplo:

ggplot(economics, aes(date, uempmed)) + geom_line()

Empecemos usando el conjunto de datos gapminder para mostrar las observaciones de la variable lifeExp para España:

library(gapminder)
gapminder %>% filter(country == "Spain") %>% 
ggplot(aes(x = year, y = lifeExp)) + geom_line() + geom_point()

Visualicemos ahora 4 series de tiempo, la esperanza de vida en cuatro países europeos. Para poder distinguir las lineas de cada país podemos asociar la variable country a la característica estética color: aes(color = country)

gapminder %>% filter(country %in% c("Spain", "France", "Norway", "Belgium")) %>% 
ggplot(aes(x = year, y = lifeExp, color = country)) + geom_line() + geom_point()

Una tontería pero que puede ser de utilidad para saber cuanta diferencia hay al final del plot. Lo aprendí aquí

df <- gapminder %>% filter(country %in% c("Spain", "France", "Norway", "Belgium")) 
lifeExp_ends <- df %>% group_by(country) %>% top_n(1, year) %>% pull(lifeExp) #- vector con los valores últimos de lifeExp
ggplot(df, aes(x = year, y = lifeExp, color = country)) + geom_line() + 
     scale_y_continuous(sec.axis = sec_axis(~ ., breaks = lifeExp_ends)) +   #- sec_axis() especifica un eje secundario
     scale_x_continuous(expand = c(0, 0))


El siguiente ejemplo está sacado de este libro aún en construcción. Utiliza el paquete tidyquant para descargar las cotizaciones de las 4 principales empresas tecnológicas, las GAFA. tidyquant tiene muchas funciones interesantes, puedes verlas corriendo tq_transmute_fun_options().

library(tidyquant)
stocks <- c("GOOGL","AMZN","FB","AAPL") #- seleccionamos a las GAFAs
df <- tq_get(stocks, from = as.Date("2013-01-01"), to = as.Date("2013-12-31"))

ggplot(df, aes(date, y = close, color = fct_reorder2(symbol, date, close))) +
  geom_line() + xlab("") + ylab("") +
  theme(legend.title = element_blank())

Reescalamos para que las series comiencen todas en 100

df <- df %>% group_by(symbol) %>% mutate(rescaled_close = 100*close / close[1])

ggplot(df, aes(date, y = rescaled_close, color = fct_reorder2(symbol, date, rescaled_close))) +
  geom_line() + xlab("") + ylab("") +
  theme(legend.title = element_blank())


Otro paquete muy útil para analizar y descargar datos financieros es quantmod. El ejemplo siguiente lo he sacado de este fantastico libro.

library(quantmod)
today <- Sys.Date()
three_months_ago <- seq(today, length = 2, by = "-3 months")[2]
getSymbols("AAPL", from = three_months_ago, to = today)
#> [1] "AAPL"
candleChart(AAPL, theme = 'white', type = 'candles')


Si quieres saber más sobre gráficos de lineas puedes ir aquí.

Un poco sobre datos temporales

Para trabajar con datos temporales, R tiene distintos paquetes y estructuras, pero para manipular fechas y datos temporales en el tidyverse tenemos el paquete lubridate. Para una introducción a fechas y tiempo en R aquí.

En general, para realizar análisis estadísticos en R con series de tiempo se necesita que los datos estén en matrices, pero nosotros en el curso estamos trabajando con el tidyverse, y la la estructura de datos usada son los dataframes o tibbles, esto era un problema, pero recientemente, el paquete tsibble ha extendido el tidyverse y las tibbles a los datos temporales, creando una nueva estructura de datos: las “tsibbles”.

Apoyándose en esta nueva estructura de datos, las “tsibbles”, el paquete feast proporciona las herramientas y funciones necesarias para trabajar con series temporales en un entorno tidy. feast es un acrónimo de Feature Extraction And Statistics for Time Series. Para una introducción a feast puedes ir aquí o aquí.

Asimismo, el paquete fable también utiliza “tsibbles” y proporciona herramientas para la predicción de series temporales, incluyendo modelos ARIMA en el entorno tidyverse. Para una introducción a fable puedes ir aquí.

Aquí puedes ver una conferencia en la que se explica las principales ideas de esta nueva forma de trabajar con series temporales en el tidyverse


7. Más detalles/cosas

7.1 geom_smooth()

Ya hemos usado geom_smooth(). Un argumento importante de geom_smooth() es method con el que se selecciona el método con el que se obtiene la smooth curve. El método por defecto es method = "loess". Puedes controlar el nivel de smoothing con el parametro “span”, que va de 0 to 1 (mayor suavizado).

Otras opciones para geom_smooth():

  • method = “lm” fits a linear model, giving the line of best fit.

  • method = “rlm” works like lm(), but uses a robust fitting algorithm so that outliers don’t affect the fit as much. It’s part of the MASS package, so remember to load that first.

  • stat_smooth(se = FALSE) , indica si se representan intervalos de confianza para la linea suavizada.

Además de los métodos implementados, podemos elegir nuestro propio método, ya sea usando el argumento “formula” o definiendo nuestro propio método de alisado como nos cuentan aquí

p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) + 
     geom_point() 
p + geom_smooth(method = "lm", formula = y ~ poly(x,4))

Podemos, obviamente, comparar dos métodos de alisado. Para poner nombre a los diferentes métodos se puede hacer lo siguiente:

library(tidyverse)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length)) + 
     geom_point()
p + geom_smooth(aes(color = "loess") , method = "loess", se = FALSE) + 
    geom_smooth(aes(color = "lm")    , method = "lm"   , se = FALSE)


7.2 Áreas bajo la curva

Para un profesor de estadística/econometría es importante saber hacer gráficos como estos:

library(tidyverse) ggplot(xx, aes(x)) + geom_function(fun = function(x) {x**3} , colour = “red”)

ggplot(xx, aes(x)) + geom_function(fun = ~ .x**3 , colour = “red”)

xx <- data.frame(x = c(-2, 2))

ggplot(xx, aes(x)) + geom_function(fun = function(x) {x**3} ,  colour = "red")

ggplot(xx, aes(x)) + geom_function(fun = ~ .x**3 ,  colour = "red")

ggplot(xx, aes(x)) + stat_function(fun = function(x) { x**3 }, geom = "line")

ggplot(xx, aes(x)) + geom_function(fun = dnorm)  

ggplot(xx, aes(x = x))+
  geom_function(fun = dnorm, xlim = c(-4, 0)) +
  stat_function(fun = dnorm, geom = "area", fill = "steelblue", xlim = c(0, 4)) +
  xlim(-5, 5)

Lo aprendí aquí.

Inspirado por Fran Morillas, otro ejemplo:

nn <- 300 ; esperanza <- 10; sd <- 6
datos <- data.frame(normal = rnorm(nn, esperanza, sd))

ggplot(datos, aes(normal)) +
    geom_histogram(aes(y = after_stat(density)), bins = 30) +
    geom_density(color = "blue") +
    geom_function(fun = dnorm, args = list(esperanza, sd), colour = "red")

Aunque, la verdad, hoy en dia hay paquetes en R para todo. Por ejemplo el paquete nhstplot

library(nhstplot)
plotttest(t = 1.96, df = 1000 , tails = "one")


7.3 Labelling las observaciones con ggrepel

El gráfico de abajo se basa en este post de Julia Silge. Utiliza el paquete `ggrepel para poder ver a quien pertenece la observación en un gráfico de puntos.

library(ggrepel)
df <- gapminder::gapminder %>% filter(year == "2007")  %>% filter(continent == "Europe")
ggplot(df, aes(gdpPercap, lifeExp, label = country)) + geom_point() +
     labs(title = "Gráfico 1: Esperanza de vida frente a PIB per cápita" ,
       caption = "Datos provenientes de gapminder",
       y = "lifeExp",
       x = "gdpPercap") + geom_smooth() +
        geom_label_repel() 


7.4 GGally package

Hay muchos paquetes que proporcionan formas rápidas de hacer gráficos como por ejemplo este del paquete GGally:

library(GGally)
ggpairs(iris)
ggpairs(iris %>% select(1:4) %>% na.omit(), progress = FALSE, lower = list(combo = wrap("facethist", bins=6)))


7.5 Un poco más de anotaciones

Para que un gráfico sea efectivo y nos haga ver algún hecho o característica de los datos, muchas veces es preciso hacer anotaciones en el gráfico para señalar o resaltar ciertos aspectos de este. En esta subsección presento algún ejemplo de uso de las anotaciones en gráficos ggplot.

  • En primer lugar un ejemplo de Hadley. Hadley nos dice que no hay nada novedoso excepto el uso de -Inf and Inf como posiciones para referirse a los límites del gráfico, the top and bottom (or left and right) limits of the plot.
presidential <- subset(presidential, start > economics$date[1])

p <- ggplot(economics) +  geom_line(aes(date, unemploy)) + 
  scale_fill_manual(values = c("blue", "red")) +
  xlab("date") + 
  ylab("unemployment")

#- comienzan las anotaciones  
p + geom_rect(aes(xmin = start, xmax = end, fill = party), 
              ymin = -Inf, ymax = Inf, alpha = 0.2, data = presidential) + 
    geom_vline(aes(xintercept = as.numeric(start)), 
               data = presidential, colour = "grey50", alpha = 0.5) + 
    geom_text(aes(x = start, y = 2500, label = name), 
               data = presidential, size = 3, vjust = 0, hjust = 0, nudge_x = 50) 

Una forma común de anotación consiste en marcar o subrayar un conjunto de puntos. Por ejemplo marcar los coches de la marca Subaru en el siguiente gráfico. Se puede hacer de la siguiente manera:

p <- ggplot(mpg, aes(displ, hwy)) +
  geom_point(data = filter(mpg, manufacturer == "subaru"), colour = "orange", size = 3) +
  geom_point() 
p

Como ves, lo que se ha hecho es superponer una capa con solo las observaciones de Subaru y graficarlas con un punto más grande de lo habitual y con un color llamativo. El problema es que se ve que sean las observaciones de Subaru. Esto se puede resolver de varias maneras usando annotate():

p + annotate(geom = "point", x = 5.5, y = 40, colour = "orange", size = 3) + 
    annotate(geom = "point", x = 5.5, y = 40) + 
    annotate(geom = "text", x = 5.6, y = 40, label = "subaru", hjust = "left")

O de esta otra forma:

p + annotate(geom = "curve", x = 4, y = 35, xend = 2.65, yend = 27, 
              curvature = .3, arrow = arrow(length = unit(2, "mm"))) +
   annotate(geom = "text", x = 4.1, y = 35, label = "subaru", hjust = "left")

El paquete ggforce es una extensión a ggplot2 que puede servir para muchas cosas, entre ellas hacer anotaciones o marcas en gráficos ggplot. Por ejemplo:

ggplot(iris, aes(Sepal.Length, Petal.Length)) +
  geom_point(aes(color = Species)) + 
  ggforce::geom_mark_ellipse(aes(label = Species, group = Species))

Con ggforce podemos hasta simular un zoom:

ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) +
  geom_point() +
  ggforce::facet_zoom(x = Species == "versicolor")


El paquete/extensión ggforce es pretty awesome. En este post puede verlo en acción haciendo mapas.


Otro enfoque para hacer anotaciones, bueno, en realidad centrase en un grupo de observaciones es utilizar el paquete gghighlight:

df <- gapminder::gapminder %>% filter(continent == "Europe")
ggplot(df, aes(year, lifeExp, group = country)) + 
  geom_line() + 
  geom_point() + 
  gghighlight::gghighlight(country %in% c("Spain", "Portugal"))

ggplot(iris, aes(Sepal.Length, Petal.Length, color = as.factor(Species))) +
  geom_point() + 
  gghighlight::gghighlight() + 
  facet_wrap(vars(Species))


8. Asistentes para ggplot2

Ahora mismo existen 2 asistentes para crear gráficos ggplot a través de interfaces gráficas: ggThemeAssist y esquisse

  • El paquete ggThemeAssist facilita mediante un adding de RStudio la edición de los detalles de un gráfico; es decir, puedes comenzar haciendo un gráfico básico en RStudio, para después abrir la interfaz de ggThemeAssist para modificar con el asistente visual todos los elementos estéticos de gráfico como títulos, leyendas, colores etc… etc…

    Para ello, primero has de instalar el paquete con install.packages("ggThemeAssist"), después cargarlo con library(ggThemeAssist). Una vez has cargado el paquete en memoria has de seleccionar con el cursor el código que genera el ggplot que quieres modificar/tunear. Una vez tienes marcado con el ratón el código del gráfico, has de seguir, en RStudio, esta ruta de menús: Tools > Addings > Browse Addings ..., para seleccionar finalmente el adding llamado “ggplot Theme Asistant”.

    Se abrirá un interfaz donde podrás modificar la mayoría de elementos del gráfico. Cuando hayas dejado el gráfico a tu gusto pinchas en DONE y te devolverá el código que reproduce el gráfico tal y como lo elegiste en el interfaz.

    Podéis ver un ejemplo en https://github.com/calligross/ggthemeassist


  • El paquete esquisse permite crear gráficos ggplot desde cero con una interfaz gráfica.

    Para usarlo tienes que instalarlo con install.packages("esquisse") para luego abrirlo siguiendo esta ruta de menús en RStudio: Tools > Addings > Browse Addings ... para elegir el adding con nombre: “esquisse - ggplot builder”.


9. Gráficos interactivos

Los gráficos interactivos, como su nombre indica, permite al usuario interactuar con el gráfico, abriéndose posibilidades como centrarse en parte del gráfico (zooming), highlighting, o mostrar información adicional al pinchar en algún elemento del gráfico, etc …

En general, JavaScript (JS) es el lenguaje utilizado para hacer gráficos interactivos con librerías como D3, Chart, Plotly, Vis Highcharts, …

Recientemente, el paquete de R htmlwidgets ha facilitado el uso de las librerías de JS en R. Actualmente, paquetes de R, como leaflet, DT, dygraphs, networkD3 y muchos otros, utilizan el framework propuesto por htmlwidgets para hacer disponibles los gráficos interactivos de JS en R.

Para daros cuenta de lo fácil que es hacer un gráfico interactivo con R usaremos el paquete plotly que hace posible usar la librería plotly.js en R. Con plotly se pueden hacer muchos tipos de gráficos, pero por ejemplo, permite con una sola linea convertir un gráfico ggplot en interactivo:

library(plotly)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +  geom_point() + geom_smooth()
ggplotly(p)

Aquí puedes encontraar un bookdown sobre plotly.

p1 <- p + facet_grid(cols = vars(Species)) 
ggplotly(p1)
p <- ggplot(mpg, aes(class)) +  geom_bar(fill = "steelblue") + coord_flip()
ggplotly(p)

Aquí puedes ver un galería de ejemplos de gráficos interactivos hechos con R. Aquí un post, ya de 2015, para iniciarse un poco en estos temas.

Hay muchos paquetes que permiten hacer gráficos interactivos en R, por ejemplo leaflet, que permite hacer mapas interactivos muy fácilmente.

library(leaflet)
m <- leaflet()
m <- addTiles(m)
m <- addMarkers(m, lng = 174.768, lat =-36.852, popup = "The birthplace of R")
m

Todos estos paquetes9 que permiten hacer gráficos interactivos a través de htmlwidgets se pueden consultar en: http://gallery.htmlwidgets.org/. Dos paquetes que no están en la gallery: Apexcharts y TSplotly.

En Interactive web-based data visualization with R, plotly, and shiny explican más detalladamente como hacer gráficos interactivos en R. Tendré que releer su sección Saving and embedding HTML para mostrar en el tutorial alguno de los gráficos dinámicos que he hecho, pero ahora tengo prisa, la próxima clase es pronto y empezamos sí o sí ggplot2.

Hadley también está escribiendo un bookdown sobre Shiny: Mastering Shiny. En palabras de Hadley: Shiny is a framework for creating web applications using R code. Para ver que significa esto puedes ver la siguiente galería con ejemplos de aplicaciones shiny.

gganimate

gganimate es un paquete que no hace gráficos dinámicos, pero permite animar gráficos mediante la creación creación de secuencias de gráficos. Mejor que explicarlo visita su sección de ejemplos en su wiki: https://github.com/thomasp85/gganimate/wiki. Por ejemplo este ejemplo o este ejemplo de jurgol. En este post explican como usarlo.

Un ejemplo con los datos de gapminder

library(tidyverse)
library(gapminder)
library(gganimate)
gapmider_europe <- gapminder %>% filter(continent == "Europe")
ggplot(gapmider_europe, aes(gdpPercap, lifeExp, size = pop, colour = country)) +
  geom_point(alpha = 0.7, show.legend = FALSE) +
  scale_colour_manual(values = country_colors) +
  scale_size(range = c(2, 12)) +
  scale_x_log10() +
  facet_wrap(~continent) +
  # Here comes the gganimate specific bits
  labs(title = 'Year: {frame_time}', x = 'GDP per capita', y = 'life expectancy') +
  transition_time(year) +
  ease_aes('linear') 

Aquí tienes un ejemplo para hacerlo para los cinco continentes.


Biblio/ejemplos/recursos

Son recursos que he visto o utilizado mientras escribía esta notas, y no quiero olvidarlos:

Por último 3 repos de Github asociados al proyecto Tidytuesday. Absolutamente impresionantes!!!

Mas biblio



  1. El repo donde se aloja el código para recrear el libro está aquí↩︎

  2. Tienes que entender porque son equivalentes. Busca la ayuda de la función y verás que el primer argumento de la función es data, así que si, como ocurre en la segunda expresión, no ponemos el nombre del argumento de la función, R tomará iris como el valor del primer argumento. Parece un trabalenguas pero es importante↩︎

  3. Si alguno de vosotros me pregunta por la verdad adoptaré semblante hierático y sólo diré lo siguiente: geom_point() is a short-hand for layer(geom = “point”, stat = “identity”, position = “identity”) ó apelaré a la canción de Albert Pla, según me de.↩︎

  4. En aes() no sólo puedes poner variables, sino transformaciones de ellas, por ejemplo aes(x = v1 ^ 2, y = v1 / v2). También se pueden mappear variables con constantes aes(x = 1, colour = “loquesea”)↩︎

  5. En este caso sí quiero que me preguntéis por este detalle. Es algo que ya deberías saber/intuir pero …↩︎

  6. En realidad la tercera expresión sí correrá, pero sólo mostrará los puntos, no las lineas.↩︎

  7. O marcar/anotar los lirios 2 lirios más grandes o el lirio mediano. ¿Lo hacéis?↩︎

  8. Por ejemplo, piensa que lo que se visualiza con geom_smoth() no son los datos originales sino, por ejemplo, la linea de regresión si usamos geom_smoth(method = "lm")↩︎

  9. En el momento de escribir estas notas eran 107 paquetes↩︎

LS0tCnRpdGxlOiAiVmlzdWFsaXphY2nDs246IGdyw6FmaWNvcyBjb24gZ2dwbG90MiIKYXV0aG9yOiAiUGVkcm8gSi4gUMOpcmV6IChwZWRyby5qLnBlcmV6QHV2LmVzKS4gVW5pdmVyc2l0YXQgZGUgVmFsw6huY2lhIDxicj4gPGJyPiBXZWIgZGVsIGN1cnNvOiA8aHR0cHM6Ly9wZXJlenA0NC5naXRodWIuaW8vaW50cm8tZHMtMjItMjMtd2ViLz4iCmRhdGU6ICJPY3R1YnJlIGRlIDIwMTkgKGFjdHVhbGl6YWRvIGVsIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVknKWApIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNzczogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgInN0eWxlc19wanAuY3NzIikKICAgIHRoZW1lOiBwYXBlcgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBpbmNsdWRlczoKICAgICAgYWZ0ZXJfYm9keTogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgImZvb3Rlci5odG1sIikgCiAgICAgIGluX2hlYWRlcjogCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZ29vZ2xlLWFuYWx5dGljcy5odG1sIikgCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZmF2aWNvbi1zb2wuaHRtbCIpCiAgICBkZl9wcmludDoga2FibGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7ciBjaHVuay1zZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICNyZXN1bHRzID0gImhvbGQiLAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBGQUxTRSwgY2FjaGUucGF0aCA9ICIvY2FjaGVzLyIsIGNvbW1lbnQgPSAiIz4iLAogICAgICAgICAgICAgICAgICAgICAgI2ZpZy53aWR0aCA9IDcsICNmaWcuaGVpZ2h0PSA3LAogICAgICAgICAgICAgICAgICAgICAgI291dC53aWR0aCA9IDcsIG91dC5oZWlnaHQgPSA3LAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCAgZmlnLnNob3cgPSAiaG9sZCIsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYXNwID0gNy85LCBvdXQud2lkdGggPSAiNjAlIiwgZmlnLmFsaWduID0gImNlbnRlciIpCgojLSBwYXJhIG1lam9yYXIgbG9zIGdyw6FmaWNvcywgYnVlbm8gZW4gcmVhbGlkYWQgcGFyYSBxdWUgc2UgdmVhbiBpZ3VhbCBlbiBkaXN0aW50b3MgU08KIy0gaHR0cHM6Ly93d3cuanVtcGluZ3JpdmVycy5jb20vYmxvZy9yLWtuaXRyLW1hcmtkb3duLXBuZy1wZGYtZ3JhcGhpY3MvCmtuaXRyOjpvcHRzX2NodW5rJHNldChkZXYgPSAicG5nIiwgZGV2LmFyZ3MgPSBsaXN0KHR5cGUgPSAiY2Fpcm8tcG5nIikpCmBgYAoKYGBge3Igb3B0aW9ucy1zZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQpvcHRpb25zKHNjaXBlbiA9IDk5OSkgIy0gcGFyYSBxdWl0YXIgbGEgbm90YWNpw7NuIGNpZW50w61maWNhCm9wdGlvbnMoInlhbWwuZXZhbC5leHByIiA9IFRSVUUpICMtIGh0dHBzOi8vZ2l0aHViLmNvbS92aWtpbmcvci15YW1sL2lzc3Vlcy80NyAgKGxvIHB1c2UgeCBlbCBwYiBjb24gZWwgd2FybmluZykgRW4gcmVhbGlkYWQgY3JlbyBxdWUgbWVqb3Igc2Vyw61hIHBvbmVybG8gZW4gUlByb2ZpbGUKYGBgCgoKYGBge3Iga2xpcHB5LCBlY2hvID0gRkFMU0V9CmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygidG9wIiwgInJpZ2h0IikpICMtIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJybGVzdXIva2xpcHB5IikKYGBgCgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeShrbml0cikKbGlicmFyeShoZXJlKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigidGhvbWFzcDg1L3BhdGNod29yayIpCmxpYnJhcnkocGF0Y2h3b3JrKQpgYGAKCi0tLS0tLS0tLS0tLS0KCjxicj4KCiMjIDEuIEludHJvZHVjY2nDs24KCkxvcyBncsOhZmljb3MgZSBpbmZvZ3JhZsOtYXMgc29uLCBjYWRhIHZleiBtw6FzLCB1bmEgcGFydGUgaW1wb3J0YW50ZSBkZSBsYSBtYXlvcsOtYSBkZSB0ZXh0b3MgZXNjcml0b3MsIHlhIHNlYW4gZXN0b3MgIHVuIGluZm9ybWUgdMOpY25pY28sIHVuIGFydMOtY3VsbyBkZSBwZXJpw7NkaWNvIG8gdW4gVEZHLiBFbiBlbCBjYXNvIGRlIGxhIGNpZW5jaWEgZGUgZGF0b3MsIGNvbW8gcHVlZGUgdmVyc2UgYWJham8gZW4gbGEgaW5mb2dyYWbDrWEsIGVsIGFuw6FsaXNpcyB5IGxhIGV4cGxvcmFjacOzbiBkZSBsb3MgZGF0b3MgZXMgdW4gcHJvY2VzbyBpdGVyYXRpdm8gZW4gZWwgcXVlIGxhIHZpc3VhbGl6YWNpw7NuIHkgbGEgZ2VuZXJhY2nDs24gZGUgZ3LDoWZpY29zIG9jdXBhIHVuIGx1Z2FyIGRlc3RhY2Fkby4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI3NSUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgZmlnLmNhcCA9ICJSIGZvciBEYXRhIFNjaWVuY2UgKGh0dHA6Ly9yNGRzLmhhZC5jby5uei8pIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1hZ2VuZXMiLCAidHRfMDZfaW1nXzAxX2V4cGxvcmUtdmlzdWFsaWNlLnBuZyIpICkgCmBgYAoKCgpVbm8gZGUgbG9zIGluc3RydW1lbnRvcyB5IHRhcmVhcyBmdW5kYW1lbnRhbGVzIGRlIHVuIGNpZW50w61maWNvIGRlIGRhdG9zIGVzIGxhIGNhcGFjaWRhZCBkZSByZWFsaXphciB2aXN1YWxpemFjaW9uZXMgZGUgZGF0b3MgYXByb3BpYWRhcyB5IGNvbnZpbmNlbnRlcy4gRWwgYW7DoWxpc2lzIGdyw6FmaWNvIG5vIHNvbG8gYXl1ZGEgZW4gbGEgZXhwbG9yYWNpw7NuIHkgY29tcHJlbnNpw7NuIGRlIGxvcyBkYXRvcywgc2lubyBxdWUgZXMgZnVuZGFtZW50YWwgYSBsYSBob3JhIGRlIG1vc3RyYXIgbGFzIHBvc2libGVzIHJlbGFjaW9uZXMgZW50cmUgdmFyaWFibGVzLCBkZXNjdWJyaXIgcmVsYWNpb25lcyBvIHBhdHJvbmVzIG9jdWx0b3MsIHkgZGVzY2FydGFyIG8gc3VnZXJpciBudWV2YXMgcHJlZ3VudGFzIHNvYnJlIGxvcyBkYXRvcy4gCgoKCkVsIGVudG9ybm8gUiB0aWVuZSBkaXZlcnNvcyBzaXN0ZW1hcyBwYXJhIHZpc3VhbGl6YXIgZGF0b3MsIGxvcyBkb3MgbcOhcyB1dGlsaXphZG9zIHNvbiBlbCBzaXN0ZW1hIGdyw6FmaWNvIGRlIFItYmFzZSB5IGVsIGVjb3Npc3RlbWEgYXNvY2lhZG8gYWwgcGFxdWV0ZSBgZ2dwbG90MmAuIFBvciBkaXZlcnNhcyByYXpvbmVzLCBlbiBlbCBjdXJzbyB1c2FyZW1vcyBlbCBlbnRvcm5vIGBnZ3Bsb3QyYCBwYXJhIGhhY2VyIG51ZXN0cm9zIGdyw6FmaWNvczsgZGUgaGVjaG8sIGVuIGxhIGFjdHVhbGlkYWQgYGdncGxvdDJgLCBkYWRhIHN1IHJhcGlkZXogZW4gbGEgaXRlcmFjacOzbiBlbnRyZSBncsOhZmljb3MsIHZlcnNhdGlsaWRhZCB5IGxhIGN1aWRhZGEgZXN0w6l0aWNhIHF1ZSB0aWVuZW4gc3VzIGdyw6FmaWNvcywgc2UgaGEgY29udmVydGlkbywgYWwgbWVub3MgcG9yIGVsIG1vbWVudG8sIGVuIGVsIHNpc3RlbWEgZXN0w6FuZGFyIHBhcmEgaGFjZXIgZ3LDoWZpY29zIGVuIFIuIFBvciBlamVtcGxvLCBbwr9jw7NtbyBjcmXDqWlzIHF1ZSBoYWNlIGxhIEJCQyBzdXMgZ3LDoWZpY29zP10oaHR0cHM6Ly9tZWRpdW0uY29tL2JiYy12aXN1YWwtYW5kLWRhdGEtam91cm5hbGlzbS9ob3ctdGhlLWJiYy12aXN1YWwtYW5kLWRhdGEtam91cm5hbGlzbS10ZWFtLXdvcmtzLXdpdGgtZ3JhcGhpY3MtaW4tci1lZDBiMzU2OTM1MzUpe3RhcmdldD0iX2JsYW5rIn0sIGV2aWRlbnRlbWVudGUgY29uIFIgeSBgZ2dwbG90MmAuIFB1ZWRlcyB2ZXIgdW5vIGRlIHN1cyByZXBvc2l0b3Jpb3MgW2FxdcOtXShodHRwczovL2dpdGh1Yi5jb20vYmJjL2JicGxvdCl7dGFyZ2V0PSJfYmxhbmsifSB5IHN1IGNvb2tib29rIFthcXXDrV0oaHR0cDovL2JpdC5seS9iYmNnZ3Bsb3QyKXt0YXJnZXQ9Il9ibGFuayJ9LiAKCgpDb24gYGdncGxvdDJgIGVzIHNlbmNpbGxvIGhhY2VyIGdyw6FmaWNvcyBjb24gY2FsaWRhZCBwYXJhIHNlciBwdWJsaWNhZG9zIG8gbW9zdHJhZG9zLCBhZGVtw6FzIGRlIHF1ZSwgZGFkYSBzdSBzaW50YXhpcyBtb2R1bGFyLCBoYWNlIHNlbmNpbGxvIGVsIHJldXRpbGl6YXIgbG9zIGdyw6FmaWNvcyBkdXJhbnRlIGVsIHByb2Nlc28gZGUgYW7DoWxpc2lzLiBFbCBwYXF1ZXRlIGBnZ3Bsb3QyYCBmdWUgaW5pY2lhbG1lbnRlIGRlc2Fycm9sbGFkbyBwb3IgW0hhZGxleSBXaWNraGFtXShodHRwOi8vaGFkbGV5Lm56Lyl7dGFyZ2V0PSJfYmxhbmsifSwgcGVybyBhY3R1YWxtZW50ZSBlbCBlY29zaXN0ZW1hIGdncGxvdCBlcyBlbCByZXN1bHRhZG8gZGUgdG9kYSB1bmEgY29tdW5pZGFkIGRlIHVzdWFyaW9zIHF1ZSBjb250cmlidXllIGEgZW5yaXF1ZWNlciBlbCBzaXN0ZW1hIGdyw6FmaWNvIGNvbiBzdXMgZXh0ZW5zaW9uZXMgeSBwYXF1ZXRlcyBhdXhpbGlhcmVzLgoKCkVuIHBhbGFicmFzIGRlIEhhZGxleSBlbiBzdSBbbGlicm8gc29icmUgZ2dwbG90Ml0oaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnLyl7dGFyZ2V0PSJfYmxhbmsifToKCj4gZ2dwbG90MiBpcyBhbiBSIHBhY2thZ2UgZm9yIHByb2R1Y2luZyBzdGF0aXN0aWNhbCwgb3IgZGF0YSwgZ3JhcGhpY3MsIGJ1dCBpdCBpcyB1bmxpa2UgbW9zdCBvdGhlciBncmFwaGljcyBwYWNrYWdlcyBiZWNhdXNlIGl0IGhhcyBhIGRlZXAgdW5kZXJseWluZyBncmFtbWFyLiBUaGlzIGdyYW1tYXIsIGJhc2VkIG9uIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzIChXaWxraW5zb24gMjAwNSksIGlzIG1hZGUgdXAgb2YgYSBzZXQgb2YgaW5kZXBlbmRlbnQgY29tcG9uZW50cyB0aGF0IGNhbiBiZSBjb21wb3NlZCBpbiBtYW55IGRpZmZlcmVudCB3YXlzLiBUaGlzIG1ha2VzIGdncGxvdDIgdmVyeSBwb3dlcmZ1bCBiZWNhdXNlIHlvdSBhcmUgbm90IGxpbWl0ZWQgdG8gYSBzZXQgb2YgcHJlLXNwZWNpZmllZCBncmFwaGljcywgYnV0IHlvdSBjYW4gY3JlYXRlIG5ldyBncmFwaGljcyB0aGF0IGFyZSBwcmVjaXNlbHkgdGFpbG9yZWQgZm9yIHlvdXIgcHJvYmxlbS4gVGhpcyBtYXkgc291bmQgb3ZlcndoZWxtaW5nLCBidXQgKipiZWNhdXNlIHRoZXJlIGlzIGEgc2ltcGxlIHNldCBvZiBjb3JlIHByaW5jaXBsZXMgYW5kIHZlcnkgZmV3IHNwZWNpYWwgY2FzZXMsIGdncGxvdDIgaXMgYWxzbyBlYXN5IHRvIGxlYXJuKiogKGFsdGhvdWdoIGl0IG1heSB0YWtlIGEgbGl0dGxlIHRpbWUgdG8gZm9yZ2V0IHlvdXIgcHJlY29uY2VwdGlvbnMgZnJvbSBvdGhlciBncmFwaGljcyB0b29scykuCgoKU8OtLCBjb24gYGdncGxvdDJgIGVzICJmw6FjaWwiIGhhY2VyIHLDoXBpZGFtZW50ZSBncsOhZmljb3MgZGUgY2FsaWRhZCwgUEVSTyBkb21pbmFyIHRvZG9zIGxvcyBkZXRhbGxlcyBkZWwgcGFxdWV0ZSBzw60gcXVlIGVzIGNvbXBsaWNhZG8sIHBlcm8gbm8gbm9zIGhhY2UgZmFsdGEgY29ub2NlcmxvIHRvZG8uIEFkZW3DoXMsIGhheSBxdWUgdGVuZXIgZW4gY3VlbnRhIHF1ZSBgZ2dwbG90MmAgZXMgdW4gcGFxdWV0ZS9lbnRvcm5vIGVuIGNvbnN0YW50ZSBldm9sdWNpw7NuLiBBY3R1YWxtZW50ZSBlc3TDoSBlbiBsYSB2ZXJzacOzbiAzLjIuMS4gQnVlbm8sIGVuIG1hcnpvIGRlIDIwMjAgYXBhcmVjacOzIGxhIHZlcnNpw7NuIDMuMy4wLiAKCgpQYXJhIGVudGVuZGVyIGVzdGEgaWRlYSBkZSBsYSBjb25zdGFudGUgZXZvbHVjacOzbiB5IGVsIHBhcGVsIHF1ZSB0aWVuZSBsYSBjb211bmlkYWQgZGUgdXN1YXJpb3MgZW4gZWwgZGVzYXJyb2xsbyBkZSBSIHkgc3VzIHBhcXVldGVzIHB1ZWRlcyBsZWVyIFtlc3RlIHR3ZWV0XShodHRwczovL3R3aXR0ZXIuY29tL0NsYXVzV2lsa2Uvc3RhdHVzLzExNjYzNTYyMTA3ODM4NzA5NzYpe3RhcmdldD0iX2JsYW5rIn0geSBsYXMgcmVzcHVlc3RhcyBhIMOpbC4gRW4gZWwgdHdlZXQgc8OzbG8gc2UgYW51bmNpYSB1bmEgcGVxdWXDsWEgbWVqb3JhIGVuIGNvbW8gYGdncGxvdDJgIGdlc3Rpb25hIGxvcyB0w610dWxvcyBkZSBsb3MgZ3LDoWZpY29zIHBlcm8gZ2VuZXJhIHJlYWNjaW9uZXMgZW4gbGEgY29tdW5pZGFkIGRlIHVzdWFyaW9zLiAKCkxhIHJlZGVzIHNvY2lhbGVzIHB1ZWRlbiBoYWNlcnNlIGVjbyBkZSBsYSBldm9sdWNpw7NuIGRlIHVuIHBhcXVldGUsIHBlcm8gZG9uZGUgaGFiaXR1YWxtZW50ZSBzZSBwcm9kdWNlIGxhIGRpc2N1c2nDs24vY29sYWJvcmFjacOzbiBlbnRyZSB1c3VhcmlvcyBlcyBlbiBwbGF0YWZvcm1hcyBjb21vIFtHaXRodWJdKGh0dHBzOi8vZ2l0aHViLmNvbS8pe3RhcmdldD0iX2JsYW5rIn0uIFBvciBlamVtcGxvLCBwdWVkZXMgdmVyIGNvbW8gc2UgZ2VzdMOzIGVzdMOhIHBlcXVlw7FhICBtZWpvcmEgW2FxdcOtXShodHRwczovL2dpdGh1Yi5jb20vdGlkeXZlcnNlL2dncGxvdDIvaXNzdWVzLzMyNTIpe3RhcmdldD0iX2JsYW5rIn0uIEZ1ZSBsYSBpc3N1ZSAzMjUyIGRlIGBnZ3Bsb3QyYC4gT3RybyBlamVtcGxvLCBqdXN0byBjdWFuZG8gZXN0YWJhIGVzY3JpYmllbmRvIGVzdGUgcMOhcnJhZm8sIGxlw60gW2VzdGUgb3RybyB0d2VldF0oaHR0cHM6Ly90d2l0dGVyLmNvbS90aG9tYXNwODUvc3RhdHVzLzExNjY2MDk1Mjk3NzExMDIyMDgpe3RhcmdldD0iX2JsYW5rIn0gYW51bmNpYW5kbyBvdHJhIG1lam9yYSBlbiBgZ2dwbG90MmAuCgoKTGEgcMOhZ2luYSB3ZWIgZGUgYGdncGxvdDJgIHB1ZWRlcyBlbmNvbnRyYXJsYSBbYXF1w61dKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifS4gRW4gZWxsYSBwdWVkZXMgZW5jb250cmFyIGRvY3VtZW50b3MgZGUgYXl1ZGEgeSBsYSBbcmVmZXJlbmNpYSBvZmljaWFsXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4gUGFyYSBkYXJ0ZSBjdWVudGEgZGUgdG9kbyBsbyBxdWUgc2UgcHVlZGUgaGFjZXIgY29uIGVsIGVjb3Npc3RlbWEgZ2dwbG90ICB2aXNpdGEgW2VzdGEgcMOhZ2luYV0oaHR0cHM6Ly9leHRzLmdncGxvdDIudGlkeXZlcnNlLm9yZy9nYWxsZXJ5Lyl7dGFyZ2V0PSJfYmxhbmsifSBkb25kZSBwb2Ryw6FzIHZlciBsb3MgNzkgInBhcXVldGVzIGF1eGlsaWFyZXMiIG8gZXh0ZW5zaW9uZXMgYSBgZ2dwbG90MmAuCgoKUGFyYSBoYWNlciAiYnVlbm9zIiBncsOhZmljb3MgY29uIGBnZ3Bsb3QyIGBubyBzw7NsbyBlcyBuZWNlc2FyaW8gZW50ZW5kZXIgbGEgc2ludGF4aXMgeSBsb3MgcG9ybWVub3JlcyBkZWwgcGFxdWV0ZSwgc2lubyBxdWUgcXVpesOhcyBzZSBuZWNlc2l0ZSBhbGdvIG3DoXMuIFBvciBlamVtcGxvLCBhbGdvIGRlIGV4cGVyaWVuY2lhIHkgY2llcnRhIGNhcGFjaWRhZCB2aXN1YWwgeSBlc3TDqXRpY2E7IGluY2x1c28gaGF5IHF1aWVuIGRpY2UgcXVlIGhhY2VyIGJ1ZW5vcyBncsOhZmljb3MgZXMgdW4gYXJ0ZS4gUGFyYSBpbnRlbnRhciBtZWpvcmFyIHZ1ZXN0cm9zIGdyw6FmaWNvcyBvIGV2aXRhciBjaWVydG9zIGVycm9yZXMsIFthcXXDrV0oaHR0cHM6Ly9yb2JqaHluZG1hbi5jb20vaHluZHNpZ2h0L2dyYXBoaWNzLyl7dGFyZ2V0PSJfYmxhbmsifSB0ZW7DqWlzIGFsZ3VuYXMgcmVnbGFzL2NvbnNlam9zLCB5IFthcXXDrV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vdGVhY2hpbmctYW5kLWxlYXJuaW5nLW1hdGVyaWFscy1mb3ItZGF0YS12aXN1YWxpemF0aW9uLyl7dGFyZ2V0PSJfYmxhbmsifSB1biBjdXJzbyBjb21wbGV0byBzb2JyZSB2aXN1YWxpemFjacOzbl5bRWwgcmVwbyBkb25kZSBzZSBhbG9qYSBlbCBjw7NkaWdvIHBhcmEgcmVjcmVhciBlbCBsaWJybyBlc3TDoSBbYXF1w61dKGh0dHBzOi8vZ2l0aHViLmNvbS9jbGF1c3dpbGtlL2RhdGF2aXopXSBjb24gW2Jvb2tkb3duIGluY2x1aWRvXShodHRwczovL3NlcmlhbG1lbnRvci5jb20vZGF0YXZpei8pe3RhcmdldD0iX2JsYW5rIn0uCgpFbnRlbmRlcsOhcyBtdXkgYmllbiBxdWUgaGFjZXIgYnVlbm9zIGdyw6FmaWNvcyBleGlnZSBjb25vY2ltaWVudG8geSBtdWNobyB0cmFiYWpvIGFsIHZlciBlbCB2aWRlbyBkZSBbZXN0ZSBwb3N0XShodHRwczovL2thcmFtYW4uaXMvYmxvZy8yMDIwLzA3L21ha2luZy1vZi8pLCBkb25kZSBzZSB2ZW4gbGFzIHZlcnNpb25lcyBwcmV2aWFzIHBhcmEgcXVlIGFsIGZpbmFsIHNhbGllc2UgZXN0YSBwcmVjaW9zdXJhIGRlIGfFlWFmaWNvOgoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy5hc3AgPSA3Lzl9CnR3ZWV0cm1kOjp0d2VldF9lbWJlZCgiaHR0cHM6Ly90d2l0dGVyLmNvbS9nZW9rYXJhbWFuaXMvc3RhdHVzLzEyNDc1ODYzOTU4NzY3NDExMjAiLCB0aGVtZSA9ICJsaWdodCIsIGFsaWduID0gImNlbnRlciIsIG1heHdpZHRoID0gNDAwKQpgYGAKCk90cm8gZWplbXBsbyBkZWwgbWFraW5nLW9mIGRlIHVuIGdyw6FmaWNvIHB1ZWRlcyB2ZXJsbyBbYXF1w61dKGh0dHBzOi8vdHdpdHRlci5jb20vTWFpYVBlbGxldGllci9zdGF0dXMvMTI4NzkyMjg1NTkyNjIxNDY1NykuCgoKT3Ryb3MgZG9zIGxpYnJvcyBzb2JyZSB2aXN1YWxpemFjacOzbiBjb24gZWwgY8OzZGlnbyBkZSBsb3MgZWplbXBsb3MgaGVjaG9zIGNvbiBgZ2dwbG90MmAsIFthcXXDrV0oaHR0cHM6Ly9zb2N2aXouY28vaW5kZXguaHRtbCNwcmVmYWNlKSB5IFthcXXDrV0oaHR0cHM6Ly9zb2N2aXouY28vbG9va2F0ZGF0YS5odG1sI2xvb2thdGRhdGEpe3RhcmdldD0iX2JsYW5rIn0uIEZpbmFsbWVudGUsIGFsZ3Vub3MgY29uc2Vqb3MgZGUgbGEgW0JCQ10oaHR0cHM6Ly9iYmMuZ2l0aHViLmlvL3Jjb29rYm9vay8pe3RhcmdldD0iX2JsYW5rIn0gc29icmUgdmlzdWFsaXphY2nDs24uCgpDb21vIHRhbWJpw6luIHNlIGFwcmVuZGUgbG9zIGVycm9yZXMsIFthcXXDrV0oaHR0cHM6Ly9tZWRpdW0uZWNvbm9taXN0LmNvbS9taXN0YWtlcy13ZXZlLWRyYXduLWEtZmV3LThjZGQ4YTQyZDM2OCl7dGFyZ2V0PSJfYmxhbmsifSB0aWVuZXMgdW4gYXJ0aWN1bG8gZGUgVGhlIEVjb25vbWlzdCBkb25kZSBtdWVzdHJhbiBlcnJvcmVzIHF1ZSBlbGxvcyBtaXNtb3MgaGFuIGNvbWV0aWRvIGhhY2llbmRvIGdyw6FmaWNvcy4gQWRlbcOhcyBwdWVkZW4gZGVzY2FyZ2Fyc2UgbG9zIGRhdG9zLgoKPGJyPgoKIyMgMi4gSWRlYXMgYsOhc2ljYXMgc29icmUgZ2dwbG90MgoKWWEgc2UgZGlqbyBxdWUgYGdncGxvdDJgIGVzIHVuIHBhcXVldGUgUiBkZXNhcnJvbGxhZG8gcG9yIEhhZGxleSBXaWNraGFtLCBhdW5xdWUgYWN0dWFsbWVudGUgZXMgZWwgcmVzdWx0YWRvIGRlIGxhIGNvbGFib3JhY2nDs24gZGUgbcO6bHRpcGxlcyBkZXNhcnJvbGxhZG9yZXMuIGBnZ3Bsb3QyYCBpbXBsZW1lbnRhIGVuIFIgW1RoZSBHcmFtbWFyIG9mIEdyYXBoaWNzXShodHRwczovL3d3dy5zcHJpbmdlci5jb20vZ3AvYm9vay85NzgwMzg3MjQ1NDQ3KSBkZSBMLiBXaWxraW5zb24sIHVuIHNpc3RlbWEgY29oZXJlbnRlIHBhcmEgZGVzY3JpYmlyIHkgY29uc3RydWlyIGdyw6FmaWNvcy4gRWwgw6luZmFzaXMgZGUgZ2dwbG90MiBlc3TDoSBlbiBsYSBleHBsb3JhY2nDs24gcsOhcGlkYSBkZSBkYXRvcywgZXNwZWNpYWxtZW50ZSBkZSBkYXRvcyBkZSBhbHRhIGRpbWVuc2lvbmFsaWRhZC4gQ29uIGBnZ3Bsb3QyYCBlcyBzZW5jaWxsbyBpciB0cmFuc2Zvcm1hbmRvIGVsIGdyw6FmaWNvIG1pZW50cmFzIHNlIHZhbiBhbmFsaXphbmRvIGxvcyBkYXRvcy4gCgoKClBhcmEgZW1wZXphciBhIGVudGVuZGVyIGxhICJmaWxvc29mw61hIiBkZSBgZ2dwbG90MmAsIG9zIHBsYW50ZW8gdW5hIHByZWd1bnRhIG1lZGlvIHJldMOzcmljYTogwr9xdcOpIHZlbW9zIGVuIGVsIGdyw6FmaWNvIGRlIGFiYWpvPwoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI2MCUifQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fcG9pbnQoKQpgYGAKCgpQdWVzIHPDrSwgZXMgdW4gZ3LDoWZpY28gZGUgcHVudG9zIHkgbm9zIGF5dWRhIGEgdmVyIGxhcyByZWxhY2lvbmVzIHF1ZSBleGlzdGVuIGVudHJlIDMgdmFyaWFibGVzLiBFc3RhbW9zIGhhYml0dWFkb3MgYSBlbGxvLCBwZXJvIHZhbW9zIGEgcGVuc2FyIGVuIGVsIGdyw6FmaWNvIGRlc2RlIGxhIMOzcHRpY2EgZGUgIlRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzIiBpbXBsZW1lbnRhZGEgZW4gZWwgcGFxdWV0ZSBgZ2dwbG90MmAuIAoKRW4gbnVlc3RybyBncsOhZmljbyBzZSByZXByZXNlbnRhbiBwb3IgbWVkaW8gZGUgcHVudG9zLCBlbiBlbCBlc3BhY2lvIFgtWSwgeSBtZWRpYW50ZSBsb3MgZGlzdGludG9zIGNvbG9yZXMgZGUgbG9zIHB1bnRvcywgbGFzIG9ic2VydmFjaW9uZXMgZGUgMyB2YXJpYWJsZXMuIEJpZW4sIG5hZGEgbXV5IG5vdmVkb3NvLCB0b2RvcyBsb3Mgc2lzdGVtYXMgZ3LDoWZpY29zIGhhY2VuIGVzdGUgdGlwbyBkZSBncsOhZmljb3MuIExvcyBncsOhZmljb3MgZGUgYGdncGxvdDJgIHNlIHJlYWxpemFuIG1lZGlhbnRlIGxhIHN1cGVycG9zaWNpw7NuIGRlIGVsZW1lbnRvcy9jYXBhcy4gUG9kZW1vcyBwZW5zYXIgcXVlIGVsIGdyw6FmaWNvIHF1ZSBoZW1vcyB2aXN0byBlcyB1bmEgY2FwYS4gwr9Dw7NtbyBjcmVhbW9zIGVzdGUgZ3LDoWZpY28gbyBjYXBhIGVuIGBnZ3Bsb3QyYD8KClB1ZXMsIHVuYSBkZSBsYXMgcHJpbmNpcGFsZXMgaWRlYXMgcGFyYSBlbnRlbmRlciBgZ2dwbG90MmAgZXMgcXVlIGNhZGEgY2FwYSBkZSB1biBncsOhZmljbyB0aWVuZSAzIGNvbXBvbmVudGVzIG8gZWxlbWVudG9zIHByaW5jaXBhbGVzOgoKCiAgLSAqKmxvcyBkYXRvcyoqIHF1ZSBzZSB2YW4gYSByZXByZXNlbnRhciAgKHNlbmNpbGxvLCBwYXJhIGhhY2VyIHVuIGdyw6FmaWNvIGhhY2VuIGZhbHRhIGRhdG9zKS4gUGFyYSBlbGxvIHV0aWxpemFyZW1vcyBnZW5lcmFsbWVudGUgbGEgZnVuY2nDs24gYGdncGxvdCgpYAogIAogIC0gKip1biBjb25qdW50byBkZSBwcm9waWVkYWRlcyBlc3TDqXRpY2FzIGFzb2NpYWRhcyBhIGFsZ3VuYSB2YXJpYWJsZSBkZWwgY29uanVudG8gZGUgZGF0b3MqKi4gUG9yIGVqZW1wbG8sIGxhIHZhcmlhYmxlIFNlcGFsLkxlbmdodCBlc3TDoSBhc29jaWFkYSBhbCBlamUgWCwgYWxhIHBvc2ljacOzbiBlbiBlbCBlamUgWC4gUG9yIHN1IHBhcnRlLCBlbCBjb2xvciBkZSBsb3MgcHVudG9zIChvdHJhIGNhcmFjdGVyw61zdGljYSB2aXN1YWwgbyBlc3TDqXRpY2EpIGVzdMOhIGFzb2NpYWRvIGEgbG9zIHZhbG9yZXMgZGUgbGEgdmFyaWFibGUgU3BlY2llcy4gRXMgZGVjaXIsIHVzYW5kbyBsYSB0ZXJtaW5vbG9nw61hIGRlIGBnZ3Bsb3QyYCwgbGFzIGRpc3RpbnRhcyB2YXJpYWJsZXMgZXN0w6FuIGFzb2NpYWRhcyBvIG1hcGVhZGFzIGEgZGV0ZXJtaW5hZGFzIGNhcmFjdGVyw61zdGljYXMgZXN0w6l0aWNhcy4gRWwgbWFwZW8gZGUgdmFyaWFibGVzIGNvbiBlc3TDqXRpY2FzIHNlIGhhcsOhIGNvbiBsYSBmdW5jacOzbiBgYWVzKClgICBkZSAqKmFlcyoqdGhldGljcy4gKGVzdG8geWEgbm8gZXMgdGFuIGVzdMOhbmRhciwgc2UgZXhwbGljYSBlbiBicmV2ZSkKICAKICAtICoqZWwgZWxlbWVudG8gZ2VvbcOpdHJpY28gcXVlIHNlIHZhIGEgcmVwcmVzZW50YXIqKi4gRW4gbnVlc3RybyBjYXNvIGVsIGVsZW1lbnRvIGdlb23DqXRyaWNvIHF1ZSBzZSB1dGlsaXphIHBhcmEgcmVwcmVzZW50YXIgbG9zIHZhbG9yZXMgZGUgbGFzIHZhcmlhYmxlcyBzb24gbG9zIHB1bnRvcywgcGVybyBwb2Ryw61hbiBoYWJlciBzaWRvIGxhcyBsaW5lYXMgbyBsYXMgYmFycmFzIC4uLiBQYXJhIGVzcGVjaWZpY2FyIGVsIGVsZW1lbnRvIGdlb23DqXRyaWNvIHF1ZSB2YW1vcyBhIHVzYXIgZW4gbnVlc3RybyBncsOhZmljbyBzZSB1dGlsaXphIGxhIGZhbWlsaWEgZGUgZnVuY2lvbmVzIGBnZW9tX3h4KClgOyBwb3IgZWplbXBsbyBgZ2VvbV9wb2ludCgpYCBzaSBxdWVyZW1vcyBwdW50b3MsIGBnZW9tX2xpbmUoKWAgc2kgcXVlcmVtb3MgcXVlIGxhcyByZWxhY2lvbmVzIGVudHJlIGxhcyB2YXJpYWJsZXMgc2UgcmVwcmVzZW50ZW4vdmlzdWFsaWNlbiBjb21vIGxpbmVhcy4KICAKQXPDrSBlbiBhYnN0cmFjdG8gcHVlZGUgc2VyIGNvbXBsaWNhZG8gZW50ZW5kZXIgZGVsIHRvZG8gcXVlIHF1aWVyZSBkZWNpciB0b2RvIGVzdG8uIFZhbW9zIGEgdmVybG8gY29uIGVqZW1wbG9zIGNvbmNyZXRvcy4gRGUgbW9tZW50bywgcGFyYSBleHBsaWNhciBsYXMgcHJpbmNpcGFsZXMgY2FyYWN0ZXLDrXN0aWNhcyBkZSBgZ2dwbG90MmAgdXRpbGl6YXLDqSB1biBjb25qdW50byBkZSBkYXRvcyBmYW1vc28sIHBlcm8gIG9kaWFkbyBwb3IgYWxndW5vcywgcG9yIGhhYmVyIHNpZG8gdXRpbGl6YWRvIGVuIG51bWVyb3NvcyBlamVtcGxvcyB5IGN1cnNvczogZWwgW2lyaXMgZGF0YXNldF0oaHR0cHM6Ly9lcy53aWtpcGVkaWEub3JnL3dpa2kvQ29uanVudG9fZGVfZGF0b3NfZmxvcl9pcmlzKS4KCkVsIGNvbmp1bnRvIGRlIGRhdG9zIGBpcmlzYCBjb250aWVuZSBkYXRvcyBzb2JyZSAxNTAgZmxvcmVzLCBlbiBjb25jcmV0byBzb2JyZSAxNTAgbGlyaW9zLiBgaXJpc2AgdGllbmUgNSB2YXJpYWJsZXMsIDQgZGUgZWxsYXMgbWlkZW4gbGEgbG9uZ2l0dWQgeSBlbCBhbmNobyBkZWwgcMOpdGFsbyB5IHPDqXBhbG8gZGUgbG9zIDE1MCBsaXJpb3MuIEVzdGFzIDQgcHJpbWVyYXMgdmFyaWFibGVzIHNvbiBjdWFudGl0YXRpdmFzIHkgY29udGludWFzOyBtaWVudHJhcyBxdWUgbGEgcXVpbnRhIHZhcmlhYmxlIGVzIGNhdGVnw7NyaWNhLCBpbmRpY2FuZG8gbGEgY2xhc2UgbyB2YXJpZWRhZCBkZSBsb3MgbGlyaW9zLCB5YSBxdWUgZW4gbG9zIGRhdG9zIGhheSAzIGVzcGVjaWVzIGRpc3RpbnRhcyBkZSBsaXJpb3MgKHNldG9zYSwgdmVyc2ljb2xvciB5IHZpcmdpbmljYSkuIENvbiBlc3RvcyBkYXRvcywgY29uIDMgZGUgc3VzIHZhcmlhYmxlcywgc2UgaGEgY3JlYWRvIGVsIGdyw6FmaWNvIHF1ZSB2ZXMgbcOhcyBhcnJpYmEuCgo8YnI+CgoqKlBSSU1FUiBHUsOBRklDTyoqOiBQYXJhIGNvbWVuemFyIG51ZXN0cmFzIGFuZGFuemFzIGNvbiBgZ2dwbG90MmAgaW50ZW50YXJlbW9zIHJlcGxpY2FyIGNvbiBjw7NkaWdvIFIgZWwgZ3LDoWZpY28gZGUgYXJyaWJhOyBhdW5xdWUgYWwgcHJpbmNpcGlvIHPDs2xvIHV0aWxpemFyZW1vcyAyIHZhcmlhYmxlczogaGFyZW1vcyB1biBncsOhZmljbyBkZSBwdW50b3MgZGUgbGEgbG9uZ2l0dWQgZGVsIHPDqXBhbG8gZnJlbnRlIGEgbGEgbG9uZ2l0dWQgZGVsIHDDqXRhbG8uCgpIYWNlciB1biBncsOhZmljbyBjb24gYGdncGxvdDJgIHJlcXVpZXJlIGRlIHZhcmlhcyBldGFwYXMsIAoKICAtIGxhIHByaW1lcmEgZGUgZWxsYXMgY29uc2lzdGUgZW4gdXNhciBsYSBmdW5jacOzbiBgZ2dwbG90KClgIHBhcmEgaW5pY2lhbGl6YXIgZWwgZ3LDoWZpY28uIAogIAogIC0gZW4gc2VndW5kbyBsdWdhciwgdGVuZHJlbW9zIHF1ZSBlc3BlY2lmaWNhciBxdWUgY29uanVudG8gZGUgZGF0b3MgdXNhcmVtb3MgZW4gZWwgZ3LDoWZpY28uIAogIAogIC0gZW4gdGVyY2VyIGx1Z2FyIHRlbmRyZW1vcyBxdWUgZXNwZWNpZmljYXIgcXVlIHZhcmlhYmxlcyBpcsOhbiBhc29jaWFkYXMgYSBkZXRlcm1pbmFkb3MgZWxlbWVudG9zIHZpc3VhbGVzIG8gZXN0w6l0aWNvcyBkZWwgZ3LDoWZpY28gCiAgCiAgLSBwb3Igw7psdGltbywgZW4gY3VhcnRvIGx1Z2FyLCB0ZW5kcmVtb3MgcXVlIGVzcGVjaWZpY2FyIHF1ZSB0aXBvIGRlIGdyw6FmaWNvIG8gZ2VvbWV0csOtYSB1c2FyZW1vcyBwYXJhIHZpc3VhbGl6YXIgbGFzIG9ic2VydmFjaW9uZXMuCiAgCiAgClZlw6Ftb3NsbyBtw6FzIGRldGVuaWRhbWVudGUuCgpFbiBgZ2dwbG90MmAsIHBhcmEgaGFjZXIgdW4gZ3LDoWZpY28gKipzZSBlbXBpZXphIFNJRU1QUkUgbGxhbWFuZG8gYSBsYSBmdW5jacOzbiBgZ2dwbG90KClgKiouIFNpIHRlY2xlYW1vcyBgZ2dwbG90KClgIGVuIGxhIGNvbnNvbGEgbyBlbiB1biBzY3JpcHQsIHBhcmVjZSBxdWUgbm8gb2N1cnJlIG5hZGEsIHBlcm8gdHJhcyBsYSBsbGFtYWRhIGEgbGEgZnVuY2nDs24gYGdncGxvdCgpYCwgUiBoYSBjcmVhZG8gdW4gb2JqZXRvLCB1biBjb250ZW5lZG9yIHBhcmEgbnVlc3RybyBmdXR1cm8gZ3LDoWZpY28uIEHDum4gbm8gdmVtb3MgZWwgZ3LDoWZpY28sIGZhbHRhbiBjb3NhcywgcGVybyB5YSBsbyBoZW1vcyBpbmljaWFsaXphZG8uIFNpIHF1aWVyZXMgdmVyIGVsIG9iamV0by9jb250ZW5lZG9yIHF1ZSBoZW1vcyBjcmVhZG8gY29uIGxhIGxsYW1hZGEgYSBgZ2dwbG90KClgIHRpZW5lcyBxdWUgYXNpZ25hcmxlIHVuIG5vbWJyZSwgYXPDrSBwb2Ryw6FzIHZlcmxvIGVuIGxhIHBlc3Rhw7FhICJFbnZpcm9ubWVudCIgZGUgUlN0dWRpby4gCgoKUGFyYSB2ZXJsbyB0aWVuZXMgcXVlIGhhY2VyOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbXlfZ3JhZmljbyA8LSBnZ3Bsb3QoKQpgYGAKCmBteV9ncmFmaWNvYCwgZXMgdW4gb2JqZXRvIFIsIGNvbmNyZXRhbWVudGUgdW5hIGxpc3RhIGNvbiA5IGVsZW1lbnRvcywgcXVlIHRlbmRyZW1vcyBxdWUgaXIgImxsZW5hbmRvIiBwYXJhIGhhY2VyIG51ZXN0cm8gZ3LDoWZpY28uIAoKCkdlbmVyYWxtZW50ZSwgZGVudHJvIGRlIGxhIGZ1bmNpw7NuIGBnZ3Bsb3QoKWAgc2Ugc3VlbGUgZXNwZWNpZmljYXIgZWwgY29uanVudG8gZGUgZGF0b3MgcXVlIHZhcyBhIHV0aWxpemFyIHBhcmEgaGFjZXIgZWwgZ3LDoWZpY28uIEEgZGlmZXJlbmNpYSBkZSBsb3MgZ3LDoWZpY29zIGRlIFItYmFzZSwgYGdncGxvdDJgIG5vIHBlcm1pdGUgZ3JhZmljYXIgdmVjdG9yZXM6ICoqbG9zIGRhdG9zIHF1ZSBzZSBzdW1pbmlzdHJhbiBoYW4gZGUgc2VyIFNJRU1QUkUgZGF0YS5mcmFtZXMqKiBvIHNpbWlsYXJlcy4KCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGlyaXMpCmdncGxvdChpcmlzKQpgYGAKCgoKWWEgZXN0w6EsIGNvbiBjdWFscXVpZXJhIGRlIGxhcyAyIGluc3RydWNjaW9uZXMgZGUgYXJyaWJhLCBzb24gZXF1aXZhbGVudGVzXltUaWVuZXMgcXVlIGVudGVuZGVyIHBvcnF1ZSBzb24gZXF1aXZhbGVudGVzLiBCdXNjYSBsYSBheXVkYSBkZSBsYSBmdW5jacOzbiB5IHZlcsOhcyBxdWUgZWwgcHJpbWVyIGFyZ3VtZW50byBkZSBsYSBmdW5jacOzbiBlcyBkYXRhLCBhc8OtIHF1ZSBzaSwgY29tbyBvY3VycmUgZW4gbGEgc2VndW5kYSBleHByZXNpw7NuLCBubyBwb25lbW9zIGVsIG5vbWJyZSBkZWwgYXJndW1lbnRvIGRlIGxhIGZ1bmNpw7NuLCBSIHRvbWFyw6EgaXJpcyBjb21vIGVsIHZhbG9yIGRlbCBwcmltZXIgYXJndW1lbnRvLiBQYXJlY2UgdW4gdHJhYmFsZW5ndWFzIHBlcm8gZXMgaW1wb3J0YW50ZV0sIHlhIGhlbW9zIGluaWNpYWxpemFkbyBlbCBncsOhZmljbyB5IGxlIGhlbW9zIGRpY2hvIHF1ZSBkYXRvcyB2YW1vcyBhIHVzYXIuIAoKRGlqaW1vcyBxdWUgcGFyYSBjb21lbnphciBoYXLDrWFtb3MgdW4gZ3LDoWZpY28gZGUgcHVudG9zIGRlIGxhIHZhcmlhYmxlIGBTZXBhbC5MZW5ndGhgIGZyZW50ZSBhIGBQZXRhbC5MZW5ndGhgOyBhc8OtIHF1ZSAqKnRlbmVtb3MgcXVlIGRlY2lybGUgYSBgZ2dwbG90MmAgcXVlIHZhcmlhYmxlcyBkZSBgaXJpc2AgcXVlcmVtb3MgdmlzdWFsaXphciB5IGNvbiBxdWUgcHJvcGllZGFkZXMgZXN0w6l0aWNhcyBxdWVyZW1vcyBhc29jaWFyIGNhZGEgdmFyaWFibGUqKi4gUGFyYSBlbGxvIHV0aWxpemFyZW1vcyBsYSBmdW5jacOzbiBgYWVzKClgIGRlbnRybyBkZSBgZ2dwbG90KClgLiBMbyBoYWNlbW9zIGNvbiBsYSBzaWd1aWVudGUgZXhwcmVzacOzbjoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsICB5ID0gUGV0YWwuTGVuZ3RoKSkKYGBgCgpBw7puIG5vIHZlbW9zIGVsIGdyw6FmaWNvLCBwZXJvIGNvbiBgYWVzKClgIGxlIGhlbW9zIGRpY2hvIGEgUi9nZ3Bsb3QyIHF1ZSBxdWVyZW1vcyBhc29jaWFyL21hcGVhciBsYSB2YXJpYWJsZSBTZXBhbC5MZW5ndGggYWwgZWplIHgsIHkgbGEgdmFyaWFibGUgUGV0YWwuTGVuZ3RoIGFsIGVqZSB5LiBGw61qYXRlIHF1ZSwgZW4gbGEgcGVzdGHDsWEgZGUgZ3LDoWZpY29zIGRlIFJTdHVkaW8sIHlhIHZlbW9zIGxvcyBlamVzIGRlbCBncsOhZmljbzsgYWRlbcOhcywgZsOtamF0ZSBxdWUgYGdncGxvdDJgIHlhIGhhIGNhbGN1bGFkbyBwYXJhIG5vc290cm9zIGVsIHJhbmdvIGRlIGxhcyB2YXJpYWJsZXMgcGFyYSBhanVzdGFyIGxvcyBlamVzLgoKCkFsIGlndWFsIHF1ZSBhbnRlcywgcG9kZW1vcyBvbWl0aXIgbG9zIG5vbWJyZXMgZGUgbGFzIG9wY2lvbmVzIGRlIGxhIGZ1bmNpw7NuIGBhZXMoKWAuIEVzdGEgZnVuY2nDs24gcHVlZGUgdGVuZXIgbXVjaG9zIGFyZ3VtZW50b3MgKHZlcmVtb3MgYWxndW5vcyksIHBlcm8gbG9zIDIgcHJpbWVyb3Mgc29uIHNpZW1wcmUgeCAoZWwgZWplIHgpIHkgZGVzcHXDqXMgeSAoZWwgZWplIHkgbyB2ZXJ0aWNhbCBkZSBtaSBncsOhZmljbyk7IGVzIGRlY2lyIHBvZHLDrWFtb3MgaGFjZXIgbG8gc2lndWllbnRlIHF1ZSBlcyBtw6FzIHLDoXBpZG8gZGUgdGVjbGVhci4KCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkKYGBgCgpDb24gZXN0YSBpbnN0cnVjY2nDs24gbGUgZXN0YW1vcyBkaWNpZW5kbyBhIGBnZ3Bsb3QyYCBxdWUgdmFtb3MgYSBoYWNlciB1biBncsOhZmljbyBjb24gbG9zIGRhdG9zIGRlbCBkYXRhLmZyYW1lIGBpcmlzYCB5IHF1ZSB2YW1vcyBhIGFzb2NpYXIvY29uZWN0YXIvbWFwZWFyIGxhIHZhcmlhYmxlIGBTZXBhbC5MZW5ndGhgIGNvbiBlbCBlamUgeCwgeSBsYSB2YXJpYWJsZSBgUGV0YWwuTGVuZ3RoYCBjb24gZWwgZWplIHkgZGVsIGdyw6FmaWNvLiBQZXJmZWN0bywgcGVybyBlbnRvbmNlcyDCv3BvciBxdcOpIG5vIHZlbW9zIGVsIGdyw6FmaWNvPyBMYSByYXrDs24gZXN0cmliYSBlbiBxdWUgbm8gbGUgaGVtb3MgZGljaG8gYSBgZ2dwbG90MmAgcXXDqSB0aXBvIGRlIGdyw6FmaWNvIHF1ZXJlbW9zIChkZSBwdW50b3MsIGRlIGxpbmVhcyBldGMuLi4pLiBFbCB0aXBvIGRlIGdyw6FmaWNvIHNlIGV4cGxpY2l0YSBjb24gdW5hIGZhbWlsaWEgZGUgZnVuY2lvbmVzOiBgZ2VvbV94eCgpYCBvICoqZ2VvbSoqZXRyw61hcy4gSGF5IG11Y2hhcyBnZW9tZXRyw61hcyBvIHRpcG9zIGRlIGdyw6FmaWNvcyBxdWUgcG9kZW1vcyB1c2FyLiBZYSBsbyB2ZXJlbW9zISEhIE5vc290cm9zIHF1ZXJlbW9zIGhhY2VyIHVuIGdyw6FmaWNvIGRlIHB1bnRvcywgYXPDrSBxdWUsIGRlIGxhIGZhbWlsaWEgZGUgKipnZW9tKipldHLDrWFzIHRlbmVtb3MgcXVlICB1c2FyIGBnZW9tX3BvaW50KClgLiBWZcOhbW9zbG8uCgoKYGBge3IsIG91dC53aWR0aCA9ICI1MCUifQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpDb21vIHZlbW9zIGxvcyBwdW50b3MgZGVsIGdyw6FmaWNvIHNlIHZpc3VhbGl6YW4gZW4gY29sb3IgbmVncm8sIGNvbiB1biB0YW1hw7FvLCB1bmEgdHJhbnNwYXJlbmNpYSB5IHVuYSBmb3JtYSBkZXRlcm1pbmFkYXMuIE9idmlhbWVudGUgdG9kbyBlc3RvIHNlIHB1ZWRlIGNhbWJpYXIgZGVudHJvIGRlIGBnZW9tX3BvaW50KClgIGNvbiBsYXMgb3BjaW9uZXMgYWRlY3VhZGFzLiBQb3IgZWplbXBsbzoKCgpgYGB7ciwgb3V0LndpZHRoID0gIjUwJSJ9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoY29sb3IgPSAicmVkIiwgc2l6ZSA9IDIsIGFscGhhID0gMC4yKQpgYGAKCgoKWWEgY2FzaSBlc3TDoS4gTm8gZXMgdGFuIGNvbXBsaWNhZG8uIFlhIGhlbW9zIHZpc3RvIGxhcyBpZGVhcyBmdW5kYW1lbnRhbGVzIGRlIGxhIHZpc3VhbGl6YWNpw7NuIGNvbiBgZ2dwbG90MmA6CgoxLiBMb3MgZ3LDoWZpY29zIHNlIGluaWNpYW4gY29uIGxhIGZ1bmNpw7NuIGBnZ3Bsb3QoKWAuIEdlbmVyYWxtZW50ZSBhcXXDrSBzZSBlc3BlY2lmaWNhbiBsb3MgZGF0b3MgKGRhdGEuZnJhbWUpIHF1ZSBxdWVyZW1vcyB1dGlsaXphciAgCgoyLiBMYSBmdW5jacOzbiBgYWVzKClgIHNpcnZlIHBhcmEgYXNvY2lhci9tYXBwZWFyIHZhcmlhYmxlcyBjb24gYXRyaWJ1dG9zL2NhcmFjdGVyw61zdGljYXMgZXN0w6l0aWNhcyBkZWwgZ3LDoWZpY28uIERlIGhlY2hvIGVsIG5vbWJyZSBkZWwgZnVuY2nDs24gYGFlcygpYCB2aWVuZSBkZSAqKmFlcyoqdGhldGljcy4gTGFzICoqYWVzKip0aGV0aWNzIG3DoXMgaW1wb3J0YW50ZXMgZGUgdW4gZ3LDoWZpY28gc3VlbGVuIHNlciBsb3MgZWplcyB4IGUgeSwgcG9yIGVzbyBzZSBwb25lbiBzaWVtcHJlIGFsIHByaW5jaXBpbyBkZSBgYWVzKClgOyBlcyBkZWNpciwgc29uIGxvcyAyIHByaW1lcm9zIGFyZ3VtZW50b3MgZGUgYGFlcygpYC4gRW4gbnVlc3RybyBlamVtcGxvIGhlbW9zIGFzb2NpYWRvIGxhIHZhcmlhYmxlIGBTZXBhbC5MZW5ndGhgIGNvbiBlbCBlamUgeCwgeSBsYSB2YXJpYWJsZSBgUGV0YWwuTGVuZ3RoYCBjb24gZWwgZWplIHkuIFZlcmVtb3MgbcOhcyAqKmFlcyoqdGhldGljcywgY29tbyBwb3IgZWplbXBsbyBlbCBjb2xvciBvIGVsIHRhbWHDsW8gKGRlIGxvcyBwdW50b3MgLi4uIG8gZGUgbGFzIGxpbmVhcyBvIC4uLikKCjMuIENvbiBgZ2VvbV8qKigpYCBlbGVnaW1vcyBlbCB0aXBvIG8gKipnZW9tKipldHLDrWEgZGUgZ3LDoWZpY28uIEhheSBtdWNob3MgdGlwb3MgZGUgZ3LDoWZpY29zLCBhc8OtIHF1ZSBoYWJyw6FuIG11Y2hvcyAqKmdlb21zKiouIFBvciBlamVtcGxvOiBnZW9tX3BvaW50KCksIGdlb21fbGluZSgpLCBnZW9tXyAuLi4KCgpFc3RhcyAzIGlkZWFzIHNvbiBsYXMgcHJpbmNpcGFsZXMgcGFyYSBlbnRlbmRlciBnZ3Bsb3QyLiBEZXNwdcOpcyBoYXkgbXVjaGFzIG3DoXMgb3BjaW9uZXMgeSBlbGVtZW50b3MgcXVlIHNlcsOhbiBtdXkgaW1wb3J0YW50ZXMgcGFyYSBjb25zZWd1aXIgdW4gYnVlbiBncsOhZmljbywgcGVybyBlbiBjaWVydGEgZm9ybWEgc29uIHNlY3VuZGFyaWFzOyBwb3IgZWplbXBsbywgbG9zIHTDrXR1bG9zLCBsb3MgZWplcywgbGFzIGVzY2FsYXMsIGVsIHRlbWEgZXRjLi4uIGxvIGlyZW1vcyB2aWVuZG8gcG9jbyBhIHBvY28uCgo8YnI+CgpBZmlhbmNlbW9zIGxhcyBpZGVhcyBwcmluY2lwYWxlcyBkZSBsYSB2aXN1YWxpemFjacOzbiBjb24gYGdncGxvdDJgLiDCv1BpZW5zYSBxdWUgaGFyw6EgbGEgc2lndWllbnRlIGxpbmVhIGRlIGPDs2RpZ28/CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9saW5lKCkKYGBgCgpMYSBwcmltZXJhIHBhcnRlIGRlIGxhIGluc3RydWNjacOzbiBlcyBpZ3VhbCBhIGxhIGFudGVyaW9yOiBxdWVyZW1vcyB1biBncsOhZmljbyBjb24gZWwgZGF0YS5mcmFtZSBpcmlzIHkgcXVlcmVtb3MgYXNvY2lhciBsYSB2YXJpYWJsZSBTZXBhbC5MZW5ndGggY29uIGVsIGVqZSB4LCBjb24gbGEgcHJvcGllZGFkICoqYWVzKip0aGV0aWMgZWplIHggeSBQZXRhbC5MZW5ndGggY29uIGVsIGVqZSB5OyBwZXJvIGxlIGhlbW9zIHBlZGlkbyB1biBncsOhZmljbyBkZSBsaW5lYXMgKGdlb21fbGluZSgpKS4gRW4gZXN0ZSBjYXNvIGhhY2VyIHVuIGdyw6FmaWNvIGRlIGxpbmVhcyBubyB0aWVuZSBtdWNobyBzZW50aWRvLCBwZXJvIHNpIHNlIGxvIHBlZGltb3MgYSBSLCBlc3RlIG5vcyBoYWNlIGNhc28geSBub3MgbG8gbXVlc3RyYS4KCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiNDklIn0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9saW5lKCkKYGBgCgo8YnI+CgpZYSBkaWplIHF1ZSB1bmEgZGUgbGFzIGNhcmFjdGVyw61zdGljYXMgaW1wb3J0YW50ZXMgZGUgYGdncGxvdDJgICBlcyBxdWUgZnVuY2lvbmEgcG9yIGNhcGFzIHF1ZSBzZSB2YW4gc3VwZXJwb25pZW5kbzsgcGFyYSBpciBhw7FhZGllbmRvIGNhcGFzIGEgbnVlc3RybyBncsOhZmljbyB0ZW5lbW9zIHF1ZSB1c2FyIGVsIHPDrW1ib2xvICoqYCtgKiouIFBvciBlamVtcGxvIHNpIHF1aXNpw6lyYW1vcyB2ZXIgbG9zIHB1bnRvcyB5IGxhcyBsaW5lYXMgwr9Dw7NtbyBsbyBoYWNlbW9zPwoKCmBgYHtyLCBvdXQud2lkdGggPSAiNDklIn0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKCkKYGBgCgo8YnI+CgpUYW1wb2NvIHNvbiBtdXkgw7p0aWxlcyBsYSBsaW5lYXMgZW4gZXN0ZSBncsOhZmljby4KCgoKT3RyYSBnZW9tZXRyw61hIG8gKipnZW9tXygpKiogcXVlIHNlIHVzYSBtdWNobyBlcyBgZ2VvbV9zbW9vdGgoKWAuIFByb2LDqW1vc2xhOgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyAgZ2VvbV9zbW9vdGgoKQpgYGAKCkRlbCBncsOhZmljbyBwdWVkZSBpbmZlcmlyc2UgcXVlIGhheSB1bmEgcmVsYWNpw7NuLCBubyBsaW5lYWwsIHBlcm8gc8OtIGRpcmVjdGEgbyBwb3NpdGl2YSBlbnRyZSBsYSBsb25naXR1ZCBkZWwgc8OpcGFsbyB5IGRlbCBww6l0YWxvOyBwZXJvIHRhbWJpw6luIHNlIGFwcmVjaWEgcXVlIGhheSBhbCBtZW5vcyBkb3MgZ3J1cG9zIGRpc3RpbnRvcyBkZSBsaXJpb3MuIEhheSB1biBncnVwbyBkZSBvYnNlcnZhY2lvbmVzIGN1eW8gcMOpdGFsbyBwYXJlY2Ugc2VyIGNsYXJhbWVudGUgbWVub3IgcXVlIGVsIGRlbCByZXN0byBkZSBsaXJpb3MuIFZlYW1vcyBzaSBlc3RvIHNlIGRlYmUgbyBlc3RhIGFzb2NpYWRvIGFsIHRpcG8gZGUgbGlyaW8sIHJlY3VlcmRhIHF1ZSBoYXkgMyB0aXBvcyBkZSBsaXJpb3MsIGFzb2NpYWRvcyBhIGxhIHZhcmlhYmxlIGBpcmlzJFNwZWNpZXNgLiBQYXJhIHZlcmxvIGVuIG51ZXN0cm8gZ3LDoWZpY28gbG8gcXVlIHZhbW9zIGEgaGFjZXIgZXMgYXNvY2lhci9tYXBlYXIgbGEgdmFyaWFibGUgYFNwZWNpZXNgIGNvbiBsYSAqKmFlcyoqdGhldGljcyBjb2xvcjoKCgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpIApgYGAKClB1ZXMgcGFyZWNlIHF1ZSBzw60sIHF1ZSBsYSBlc3BlY2llIGRlIGxpcmlvcyAic2V0b3NhIiBlcyBtw6FzIHBlcXVlw7FhLCBhbCBtZW5vcyBlbiBsb25naXR1ZCwgZGVsIHPDqXBhbG8sIHBlcm8gc29icmUgdG9kbyBkZWwgcMOpdGFsby4KCkxhIHZhcmlhYmxlIGBTcGVjaWVzYCBwb2Ryw61hbW9zIGhhYmVybGEgYXNvY2lhZG8gYSBsYSBlc3TDqXRpY2EgdGFtYcOxbyAoc2l6ZSksIG8gYSBsYSBlc3TDqXRpY2EgZm9ybWEgKHNoYXBlKSBwZXJvIG5vIHNlcsOtYSB0YW4gw7p0aWwgbmkgcXVlZGFyw61hIHRhbiBib25pdG8gZWwgZ3LDoWZpY28uIEbDrWphdGUgcXVlIGluY2x1c28gUiBub3MgYXZpc2EgZGUgcXVlIGFzb2NpYXIgbGEgcHJvcGllZGFkIG8gKiphZXMqKnRoZXRpYyB0YW1hw7FvIGNvbiB1bmEgdmFyaWFibGUgY2F0ZWfDs3JpY2EgY29tbyBTcGVjaWVzIG5vIGVzIG11eSByZWNvbWVuZGFibGUuCgoKYGBge3IsIHdhcm5pbmcgPSBUUlVFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBzaXplID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpIApgYGAKClRhbWJpw6luIHBvZGVtb3MgYXNvY2lhciBsYSB2YXJpYWJsZSBgU3BlY2llc2AgYSBsYSBlc3TDqXRpY2EgImZvcm1hIihzaGFwZSkuIExvIGhhY2Vtb3MgZW4gbGEgZXhwcmVzacOzbiBkZSBtw6FzIGFiYWpvLiBDb21vIHZlaXMsIGFob3JhIGxhcyBkaWZlcmVuY2lhcyBlbnRyZSBlc3BlY2llcyBkZSBsaXJpb3Mgbm8gc2UgYXByZWNpYW4gdGFuIGJpZW4gY29tbyBjdWFuZG8gdXPDoWJhbW9zIGBjb2xvciA9IFNwZWNpZXNgCgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIHNoYXBlID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpIApgYGAKCgpPcyB2YSBhIGNvc3RhciBoYWNlciBncsOhZmljb3MsIG5vcm1hbCEhISwgcGVybyBlc3Blcm8gcXVlIGxhIGlkZWEgcHJpbmNpcGFsIHlhIGxhIHRlbmfDoWlzLiBMbyBxdWUgcGFzYSBlcyBxdWUgbm8gb3MgaGUgY29udGFkbyB0b2RvLCBlbiByZWFsaWRhZCBlcyB1biBwb2NvIG3DoXMgY29tcGxlam8geSB2ZXJzw6F0aWwuIExvIG1lZGlvIGV4cGxpY28gZW4gZWwgc2lndWllbnRlIGFwYXJ0YWRvLgoKCjxicj4KCiMjIyAgTcOhcyBpZGVhcyBzb2JyZSBnZ3Bsb3QyCgpZYSB0ZW7DqWlzIGxhcyBpZGVhcyBwcmluY2lwYWxlcyBwYXJhIGhhY2VyIGdyw6FmaWNvcyBjb24gYGdncGxvdDJgLCBwZXJvIG5vIG9zIGxvIGhlIGNvbnRhZG8gdG9kbywgdGFtcG9jbyBsbyB2b3kgYSBoYWNlciBhaG9yYSwgcGVybyBzaSBjb250YXLDqSBsYXMgY29zYXMgZGUgdW5hIGZvcm1hIGRpZmVyZW50ZSBwYXJhIHF1ZSB0ZW5nw6FpcyBtw6FzIGZsZXhpYmlsaWRhZC92ZXJzYXRpbGlkYWQgYSBsYSBob3JhIGRlIGhhY2VyIGdyw6FmaWNvcy4gU2kgcXVlcsOpaXMgc2FiZXIgdG9kYSBsYSB2ZXJkYWReW1NpIGFsZ3VubyBkZSB2b3NvdHJvcyBtZSBwcmVndW50YSBwb3IgbGEgdmVyZGFkIGFkb3B0YXLDqSBzZW1ibGFudGUgaGllcsOhdGljbyB5IHPDs2xvIGRpcsOpIGxvIHNpZ3VpZW50ZTogZ2VvbV9wb2ludCgpIGlzIGEgc2hvcnQtaGFuZCBmb3IgbGF5ZXIoZ2VvbSA9ICJwb2ludCIsIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpIMOzIGFwZWxhcsOpIGEgbGEgY2FuY2nDs24gZGUgQWxiZXJ0IFBsYSwgc2Vnw7puIG1lIGRlLl0gdGVuZHLDqWlzIHF1ZSBpciBhbCBsaWJybyBkZSBIYWRsZXksIGNvbmNyZXRhbWVudGUgW2FxdcOtXShodHRwczovL2dncGxvdDItYm9vay5vcmcvbGF5ZXJzLmh0bWwpLgoKTGEgZm9ybWEgcXVlIG9zIGhlIGNvbnRhZG8gIGRlIGhhY2VyIGdyw6FmaWNvcyBgZ2dwbG90MmAgZXMgbGEgcXVlIHZlcsOpaXMgaGFiaXR1YWxtZW50ZSwgeW8gdGFtYmnDqW4gaGFnbyBtaXMgZ3LDoWZpY29zIGFzw60sIHNvbG8gcXVlIHBhcmEgZW50ZW5kZXIgbWVqb3IgZWwgZnVuY2lvbmFtaWVudG8sIGxhIHNpbnRheGlzIGRlIGBnZ3Bsb3QyYCwgb3MgbG8gdGVuZ28gcXVlIGNvbnRhciBvdHJhIHZleiBkZSB1bmEgZm9ybWEgdW4gcG9jbyBkaWZlcmVudGUgbyBhbXBsaWFkYS4gCgpVbiBncsOhZmljbyBkZSBnZ3Bsb3QyIHNlIGluaWNpYSBsbGFtYW5kbyBhIGxhIGZ1bmNpw7NuIGBnZ3Bsb3QoKWAgZXNvIGVzIGNpZXJ0byB5IHRhbWJpw6luIGVzIHZlcmRhZCBxdWUgZ2VuZXJhbG1lbnRlIGRlbnRybyBkZSBgZ2dwbG90KClgIHNlIGluZGljYSBlbCBkYXRhLmZyYW1lIHF1ZSB2YXMgYSB1dGlsaXphciB5IGNvbiBgYWVzKClgIHF1ZSB2YXJpYWJsZXMgdmFzIGEgdXNhciB5IGNvbiBxdWUgZWxlbWVudG9zIHZpc3VhbGVzIG8gZXN0w6l0aWNvcyBxdWllcmVzIGFzb2NpYXIgY2FkYSB1bmEgZGUgbGEgdmFyaWFibGVzIHF1ZSB2YXMgYSB1dGlsaXphcl5bRW4gYGFlcygpYCBubyBzw7NsbyBwdWVkZXMgcG9uZXIgdmFyaWFibGVzLCBzaW5vIHRyYW5zZm9ybWFjaW9uZXMgZGUgZWxsYXMsIHBvciBlamVtcGxvIGFlcyh4ID0gdjEgXiAyLCB5ID0gdjEgLyB2MikuIFRhbWJpw6luIHNlIHB1ZWRlbiBtYXBwZWFyIHZhcmlhYmxlcyBjb24gY29uc3RhbnRlcyBhZXMoeCA9IDEsIGNvbG91ciA9ICJsb3F1ZXNlYSIpXS4gQ29ycmVjdG8sIHBlcm8gLi4uLiBlbiByZWFsaWRhZCB1biBncsOhZmljbyBnZ3Bsb3QyIHNlIGhhY2UgcG9yIGNhcGFzLCBjYWRhIGNhcGEgc2UgZXNwZWNpZmljYSBjb24gdW5hIGZ1bmNpw7NuIGRlIGxhIGZhbWlsaWEgYGdlb21feHgoKWAsIGFzw60gcXVlIGVuIHJlYWxpZGFkIGxvcyBkYXRvcyB5IGxhcyBgYWVzKClgIHNlICJkZWJlcsOtYW4iIGVzcGVjaWZpY2FyIGRlbnRybyBkZSBsYSBmdW5jacOzbiBnZW9tX3h4KClgLiAKClBhcmVjZSB1biBwb2NvIGRlIGzDrW8gcGVybyBlbiBjdWFudG8gbG8gZW50aWVuZGFzIGVzIG11eSBmw6FjaWwgeSB0ZSBwdWVkZSBkYXIgbcOhcyBmbGV4aWJpbGlkYWQgYSBsYSBob3JhIGRlIGhhY2VyIHR1cyBncsOhZmljb3MuIEVtcGVjZW1vczogwr9yZWN1ZXJkYXMgcXVlIGhhY2VuIGxhcyBsaW5lYXMvZXhwcmVzaW9uZXMgZGUgYWJham8/CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpIApnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKQpgYGAKClBvZGVtb3MgcGVuc2FyIHF1ZSBsYXMgZnVuY2lvbmVzIHF1ZSBoYWNlbiBsYSByZXByZXNlbnRhY2nDs24gZ3LDoWZpY2EgcmVhbG1lbnRlIHNvbiBsYXMgYGdlb21feHgoKWA7IGVzIGFow60gZG9uZGUgZGViZXLDrWFtb3MgZXNwZWNpZmljYXIgbG9zIGRhdG9zIHkgdmFyaWFibGVzL2VzdMOpdGljYXMgcXVlIHF1ZXJlbW9zIHVzYXIsIHBlcm8gc2kgbm8gbGFzIGVzcGVjaWZpY2Ftb3MgZW4gbGEgZnVuY2nDs24gYGdlb20oKWAsIGVudG9uY2VzLCBnZ3Bsb3QyIG1pcmFyw6EgYSB2ZXIgc2kgZXhpc3Rlbiwgc2kgZXN0w6FuIGVzcGVjaWZpY2Fkb3MsIGRlbnRybyBkZSBgZ2dwbG90KClgLgoKCkFob3JhIHF1ZSB5YSBzYWJlbW9zIGVsIGZ1bmNpb25hbWllbnRvIGLDoXNpY28gZGUgYGdncGxvdDJgLCB2ZWFtb3MgYWxndW5vcyBkZXRhbGxlcyBtZWRpYW50ZSBhbGd1bm9zIGVqZW1wbG9zLiBIYXN0YSBhaG9yYSBoZW1vcyBlc3BlY2lmaWNhZG8gZWwgZGF0YS5mcmFtZSBxdWUgcXVlcmVtb3MgZ3JhZmljYXIgY29uIGBnZ3Bsb3QoZGF0YSA9IG15X2RmKWAgbyBjb24gYGdncGxvdChteV9kZilgIHkgbGFzIHZhcmlhYmxlcyBxdWUgcXVlcmVtb3MgdmVyLCB5IGEgcXVlIHByb3BpZWRhZCBlc3TDqXRpY2EgcXVlcmVtb3MgYXNvY2lhcmxhLCBjb24gbGEgZnVuY2nDs24gYGFlcygpYCAqKmRlbnRybyBkZSBgZ2dwbG90KClgKiouIFNpIGxvIGhhY2Vtb3MgYXPDrSwgdG9kb3MgbG9zIGBnZW9tc194eCgpYCBxdWUgdXRpbGljZW1vcyBjb21wYXJ0aXLDoW4gZWwgY29uanVudG8gZGUgZGF0b3MgeSBsYXMgdmFyaWFibGVzL2VzdMOpdGljYXMgYSBtYXBwZWFyIHkgbW9zdHJhcjsgcGVybyBhIHZlY2VzLCBlbiBncsOhZmljb3MgbcOhcyBjb21wbGVqb3MgcG9kZW1vcyBxdWVyZXIgaGFjZXIgcXVlIGNhZGEgYGdlb21feHgoKWAgbXVlc3RyZSBkYXRvcyB5L28gdmFyaWFibGVzIGRpc3RpbnRhcy4gCgpFbnRlbmRlciBxdWUgY2FkYSBgZ2VvbV94eCgpYCBwdWVkZSBlc3RhciBhc29jaWFkbyBhIGRpc3RpbnRvcyBkYXRhLmZyYW1lcyB5L28gdmFyaWFibGVzIGVzIGltcG9ydGFudGUgcGFyYSB0ZW5lciBtw6FzIHZlcnNhdGlsaWRhZCBjb24gYGdncGxvdDJgLlBvciBlamVtcGxvLCBsYXMgc2lndWllbnRlcyB0cmVzIGV4cHJlc2lvbmVzIGhhY2VuIGVsIG1pc21vIGdyw6FmaWNvLiBTZSBzdWVsZSB1dGlsaXphciBsYSBwcmltZXJhIGV4cHJlc2nDs24sIHBlcm8gbGEgc2VndW5kYSB5IHRlcmNlcmEgZXhwcmVzaW9uZXMgc29uICJtw6FzIGZsZXhpYmxlcyIsIGF1bnF1ZSBlcyB2ZXJkYWQgcXVlIHNpIHPDs2xvIHNlIHV0aWxpemEgdW4gYGdlb21feHgoKWAgbm8gZ2FuYW1vcyBuYWRhIHBvciB1c2FyIGxhIHNlZ3VuZGEgbyB0ZXJjZXJhIGV4cHJlc2nDs24sIHBlcm8gbm8gc2Vyw6EgZWwgY2FzbyBzaSBlbiBudWVzdHJvIGdyw6FmaWNvIG5lY2VzaXRhbW9zIHVzYXIgdmFyaW9zIGBnZW9tX3h4KClgCgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKQoKZ2dwbG90KGlyaXMpICsgZ2VvbV9wb2ludChhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKQoKZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGEgPSBpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKQpgYGAKCkbDrWphdGUsIGNvbW8gZGV0YWxsZSwgcGVybyBpbXBvcnRhbnRlLCBxdWUgc2kgdXRpbGl6YXMgbGEgdGVyY2VyYSBleHByZXNpw7NuOyBlcyBkZWNpciwgc2kgZXNwZWNpZmljYXMgbG9zIGRhdG9zIGRlbnRybyBkZSBsYSBmdW5jacOzbiBgZ2VvbV94eCgpYCwgZXMgbmVjZXNhcmlvIHBvbmVyIGVsIG5vbWJyZSBkZWwgYXJndW1lbnRvIDsgZXMgZGVjaXIsIGRlYmVzIHBvbmVyIGBkYXRhID0gaXJpc2AsIG5vIHB1ZWRlcyBwb25lciBzb2xvIGBpcmlzYC4gWW8gbWUgb2x2aWRvIHNpZW1wcmUgZGUgZXN0ZSBkZXRhbGxlXltFbiBlc3RlIGNhc28gc8OtIHF1aWVybyBxdWUgbWUgcHJlZ3VudMOpaXMgcG9yIGVzdGUgZGV0YWxsZS4gRXMgYWxnbyBxdWUgeWEgZGViZXLDrWFzIHNhYmVyL2ludHVpciBwZXJvIC4uLl0uCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkKYGBgCgoKCkVuIGVzdGUgY2FzbyAoY29tbyBlbCBncsOhZmljbyBzb2xvIHRpZW5lIHVuYSBjYXBhLCBjb21vIHPDs2xvIHVzYW1vcyB1biBgZ2VvbShfeHgpYCkgbm8gZ2FuYW1vcyBuYWRhIHBvciB1c2FyIGxhIHNlZ3VuZGEgbyB0ZXJjZXJhIGV4cHJlc2nDs247IFBFUk8sIGN1YW5kbyB1c2Vtb3MgdmFyaW9zIGBnZW9tX3h4KClgIGVzdG8gbm9zIGRhcsOhIG11Y2hhcyBwb3NpYmlsaWRhZGVzIHBhcmEgbnVlc3RybyBncsOhZmljby4gCgpJbnRlbnRhIGRlc2N1YnJpciBsYXMgZGlmZXJlbmNpYXMgeSBmdW5jaW9uYW1pZW50byBkZSBsYXMgMyBzaWd1aWVudGVzIGluc3RydWNjaW9uZXMuIFJlY3VlcmRhIHF1ZSBwdWVkZXMgY29ycmVyIGxhcyBpbnN0cnVjY2lvbmVzIGVuIFIgcGFyYSB2ZXIgcXVlIGhhY2VuIGV4YWN0YW1lbnRlLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9zbW9vdGgoKQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBTcGVjaWVzKSkKYGBgCgoKVmXDoW1vc2xhcyB1bmEgYSB1bmE6CgoxLiBMb3MgZGF0b3MgeSBsYXMgMyB2YXJpYWJsZXMvZXN0w6l0aWNhcyBkZW50cm8gZGUgZ2dwbG90KCkKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKYGBgCgpFbiBlc3RlIGNhc28gbG9zIDIgZ2VvbXMgY29tcGFydGVuIGVsIGNvbmp1bnRvIGRlIGRhdG9zIChpcmlzKSB5IGxhcyB2YXJpYWJsZXMvZXN0w6l0aWNhcyBhIGdyYWZpY2FyCgo8YnI+CgoyLiBMb3MgZGF0b3MgeSAyIHZhcmlhYmxlcy9lc3TDqXRpY2FzIGRlbnRybyBkZSBnZ3Bsb3QoKSwgcGVybyB1bmEgdGVyY2VyYSB2YXJpYWJsZSAoU3BlY2llcyksIGFzb2NpYWRhIGEgbGEgZXN0w6l0aWNhIGNvbG9yLCBhcGFyZWNlIHNvbGFtZW50ZSBlbiBgZ2VvbV9wb2ludCgpYAoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fc21vb3RoKCkKYGBgCgo8YnI+CgozLiBMb3MgZGF0b3MgeSAyIHZhcmlhYmxlcy9lc3TDqXRpY2FzIGRlbnRybyBkZSBnZ3Bsb3QoKSwgcGVybyB1bmEgdGVyY2VyYSB2YXJpYWJsZSAoU3BlY2llcyksIGFzb2NpYWRhIGEgbGEgZXN0w6l0aWNhIGNvbG9yLCBhcGFyZWNlIHNvbGFtZW50ZSBlbiBgZ2VvbV9zbW9vdGgoKWAKCgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IFNwZWNpZXMpKQpgYGAKCkVzcGVybywgc2VndXJvISEsIHF1ZSB0ZSBoYXMgZGFkbyBjdWVudGEgZGUgcXVlIHNpIGVzcGVjaWZpY2FzIGVsIGRhdGEuZnJhbWUgeSBsYXMgdmFyaWFibGVzL2VzdMOpdGljYXMgZGVudHJvIGRlIGBnZ3Bsb3QoKWAgZXN0byBhZmVjdGFyw6EgYSB0b2RvcyBsb3MgZ2VvbXMgZGVsIGdyw6FmaWNvOyBwZXJvIGxvIHF1ZSBzZSBlc3BlY2lmaXF1ZSBkZW50cm8gZGUgdW4gYGdlb21feHgoKWAgc29sbyBhZmVjdGEgYSBlc2EgZ2VvbWV0csOtYS4KCjxicj4KCioqT3RybyBlamVtcGxvKiogcGFyYSBlbnRlbmRlcmxvLCDCv3BvciBxdcOpIG5vIGZ1bmNpb25hIGxhIHNpZ3VpZW50ZSBleHByZXNpw7NuPwoKYGBge3IsIGV2YWwgPSBGQUxTRSwgbWVzc2FnZSA9IFRSVUV9CmdncGxvdChpcmlzKSArIGdlb21fcG9pbnQoYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBTcGVjaWVzKSkKYGBgCgpQdWVzIHBvcnF1ZSBwYXJhIHBvZGVyIHJlcHJlc2VudGFyIGxhIGxpbmVhIHN1YXZpemFkYSBzZSB1dGlsaXphIGBnZW9tX3Ntb290aCgpYCwgeSBgZ2VvbV9zbW9vdGgoKWAgbmVjZXNpdGEgY29tbyBtw61uaW1vIHRlbmVyIHZhcmlhYmxlcyBhc29jaWFkYXMgYSBsYXMgZXN0w6l0aWNhcyB4IGUgeS4gQ29tbyB2ZWlzLCBkZW50cm8gZGUgYGdlb21fc21vb3RoKClgIHNvbG8gaGVtb3MgZXNwZWNpZmljYWRvIGxhIGVzdMOpdGljYSAiY29sb3IiIHkgdGFtcG9jbyBoZW1vcyBlc3BlY2lmaWNhZG8gZW4gbGEgZnVuY2nDs24gYGdncGxvdCgpYCBxdWUgdmFyaWFibGVzIHNlIGFzb2NpYW4gY29uIHggZSB5LiBQb3IgbG8gdGFudG8sIGBnZW9tX3Ntb290aCgpYCBubyBwdWVkZSBoYWNlciBzdSB0cmFiYWpvLCBsZSBmYWx0YW4gbG9zICJkYXRvcyIgZGUgeCBlIHkgcGFyYSBjYWxjdWxhci9vYnRlbmVyIGxhIGxpbmVhIHN1YXZpemFkYS4gCgpWYW1vcyBjb24gKipvdHJvcyBlamVtcGxvcyoqLiBMYSBzaWd1aWVudGVzIGV4cHJlc2lvbmVzIHRhbXBvY28gZnVuY2lvbmFyw6FuXltFbiByZWFsaWRhZCBsYSB0ZXJjZXJhIGV4cHJlc2nDs24gc8OtIGNvcnJlcsOhLCBwZXJvIHPDs2xvIG1vc3RyYXLDoSBsb3MgcHVudG9zLCBubyBsYXMgbGluZWFzLl0gc2kgaW50ZW50w6FpcyBjb3JyZXJsYXMgZW4gdnVlc3RybyBvcmRlbmFkb3IuIMK/UG9yIHF1w6k/CgoKCmBgYHtyLCBldmFsID0gRkFMU0UsIG1lc3NhZ2UgPSBUUlVFfQpnZ3Bsb3QoKSArIGdlb21fcG9pbnQoZGF0YSA9IGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9saW5lKGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpCgpnZ3Bsb3QoYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KGRhdGEgPSBpcmlzKSArIGdlb21fbGluZSgpCgpnZ3Bsb3QoKSArIGdlb21fcG9pbnQoZGF0YSA9IGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9saW5lKCkKYGBgCgo8YnI+CgoqKk90cm8gZWplbXBsbzoqKiBoYWdhbW9zIGFsZ28gbcOhcyBtYXJjaWFuby9jb21wbGljYWRvLiBTdXDDs24gcXVlIHF1aWVyZXMgaGFjZXIgdW4gZ3LDoWZpY28gZGlmZXJlbmNpYW5kbyBsb3MgcHVudG9zIHBvciBjb2xvciBwYXJhIGxhcyB0cmVzIGVzcGVjaWVzIGRlIGxpcmlvcywgcGVybyBxdWllcmVzIHF1ZSBzb2xvIHNlIHZlYSBsYSBsaW5lYSBzdWF2aXphZGEgcGFyYSBsYXMgZG9zIGVzcGVjaWVzIG3DoXMgZ3JhbmRlcyAodmlyZ2luaWNhIHkgdmVyc2ljb2xvcikuIExvcyBsaXJpb3MgbcOhcyBwZXF1ZcOxb3Mgc29uIGxvcyBkZSBsYSBjbGFzZSBzZXRvc2EuIElndWFsIHNlIHB1ZWRlIGhhY2VyIGRlIG90cmEgZm9ybWEgcGVybyBsYSBxdWUgbWUgdmllbmUgYSBsYSBjYWJlemEgZXMgaGFjZXIgbG8gc2lndWllbnRlOgoKClByaW1lcm8sIGNyZWFyIHVuIGRhdGFzZXQgcXVlIHPDs2xvIGNvbnRlbmdhIGEgbG9zIGxpcmlvcyBncmFuZGVzLCBsb3MgZGUgbGFzIGVzcGVjaWVzIHZpcmdpbmljYSB5IHZlcnNpY29sb3IuCgpgYGB7cn0KaXJpczIgPC0gaXJpcyAlPiUgZmlsdGVyKFNwZWNpZXMgIT0gInNldG9zYSIpICMtIG1lIHF1ZWRvIGNvbiBsb3MgbGlyaW9zIHF1ZSBubyBzb24gZGUgY2xhc2UgInNldG9zYSIKYGBgCgpQYXJhIGRlc3B1w6lzIGhhY2VyIGVsIGdyw6FmaWNvIGNvbiBjdWFscXVpZXJhIGRlIGxhcyAyIGV4cHJlc2lvbmVzIHNpZ3VpZW50ZXMuIFByZWZpZXJvIGxhIHNlZ3VuZGEgcG9ycXVlIGhheSBxdWUgdGVjbGVhci9lc2NyaWJpciBtZW5vcywgcGVybyBwdWVkZSBxdWUgc2VhIG3DoXMgZGlkw6FjdGljYSBsYSBwcmltZXJhLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KCkgKyBnZW9tX3BvaW50KGRhdGEgPSBpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9zbW9vdGgoZGF0YSA9IGlyaXMyLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpICkgCgpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fc21vb3RoKGRhdGEgPSBpcmlzMikKYGBgCgo8YnI+CgoqKk90cm8gZWplbXBsbyoqIG3DoXM6IMK/eSBzaSBxdWlzacOpcmFtb3MgcXVlIGxhcyAyIGVzcGVjaWVzIGdyYW5kZXMgc2UgcmVwcmVzZW50ZW4gY29uIGVsIG1pc21vIGNvbG9yPyBIYXkgdmFyaWFzIHNvbHVjaW9uZXMsIHVuYSBkZSBsYXMgbcOhcyBtYXJjaWFuYXMgZXMgbGEgcXVlIHByb3BvbmdvIGFiYWpvLiBFcyB1bmEgc29sdWNpw7NuIHJhcmEsIHBlcm8gY3JlbyBxdWUgb3MgYXl1ZGFyw6EgYSBlbnRlbmRlciBgZ2dwbG90MmAKClByaW1lcm8gdm95IGEgY3JlYXIgdW4gbnVldm8gZGF0YS5mcmFtZSBzw7NsbyBjb24gbGFzIG9ic2VydmFjaW9uZXMgZGUgbG9zIGxpcmlvcyBwZXF1ZcOxb3MsIGxvcyBkZSBsYSBjbGFzZSBzZXRvc2EuCgpgYGB7cn0KaXJpc19zZXRvc2EgPC0gaXJpcyAlPiUgZmlsdGVyKFNwZWNpZXMgPT0gInNldG9zYSIpICMtIG1lIHF1ZWRvIGNvbiBsb3MgbGlyaW9zIHBlcXVlw7FvcywgbG9zIGRlIGNsYXNlICJzZXRvc2EiCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3BvaW50KGRhdGEgPSBpcmlzX3NldG9zYSwgYWVzKGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9zbW9vdGgoZGF0YSA9IGlyaXMyLGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkgKQpgYGAKCk90cmEgc29sdWNpw7NuLCBxdWl6w6FzIG3DoXMgbMOzZ2ljYSwgY29uc2lzdGUgZW4gcHJpbWVybyBhZ3J1cGFyIGxhcyAyIGVzcGVjaWVzIGRlIGxpcmlvcyBncmFuZGVzICh2ZXJzaWNvbG9yIHkgdmlyZ2luaWNhKSBlbiB1bmEgc29sYSBjbGFzZS4KCgpgYGB7cn0KaXJpc19zb2xvXzJfY2xhc2VzIDwtIGlyaXMgJT4lIG11dGF0ZShTcGVjaWVzXzIgPSBpZmVsc2UoU3BlY2llcyAlaW4lIGMoInZlcnNpY29sb3IiLCAidmlyZ2luaWNhIiksICJ2ZXJzaV92aXJnaSIsICJzZXRvc2EiKSkKYGBgCgpQYXJhIGRlc3B1w6lzIGhhY2VyIGVsIGdyw6FmaWNvLiBBZGVtw6FzIGVsIGdyw6FmaWNvIGxvIHBvZGVtb3MgaGFjZXIgYWwgbWVub3MgZGUgMiBtYW5lcmFzLCBsYSBzZWd1bmRhIG11Y2hvIG1lam9yLCBsYSBwcmltZXJhIGV4cHJlc2nDs24gZXMgdW4gcG9jbyBlbnJldmVzYWRhOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9wb2ludChkYXRhID0gaXJpc19zZXRvc2EsIGFlcyhjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fc21vb3RoKGRhdGEgPSBpcmlzX3NvbG9fMl9jbGFzZXMsYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXNfMikgKQoKZ2dwbG90KGlyaXNfc29sb18yX2NsYXNlcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXNfMikpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoaXJpc19zb2xvXzJfY2xhc2VzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llc18yKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmBgYAoKCkNvbW8gdmVpcywgZW4gYGdncGxvdDJgIGhheSB2YXJpYXMgbWFuZXJhcyBkZSBoYWNlciBlbCBtaXNtbyBncsOhZmljby4gRXN0byBhbCBwcmluY2lwaW8gcHVlZGUgYWJydW1hci9tb2xlc3RhciwgcGVybyBtdWVzdHJhIGxhIGZsZXhpYmlsaWRhZCBkZSBsYSBzaW50YXhpcy4KCgpQYXJhIGlyIGFjYWJhbmRvIGNvbiBsYSAiZmlsb3NvZsOtYSIvc2ludGF4aXMvZ3JhbcOhdGljYSBkZSBgZ2dwbG90MmAgaW50ZW50YSBpbWFnaW5hciBxdWUgZ3LDoWZpY29zIGhhY2VuIGxhcyA2IGV4cHJlc2lvbmVzIGRlIG3DoXMgYWJham8uCgpTaSBubyBwdWVkZXMsIHJlY3VlcmRhIHF1ZSBzaWVtcHJlIHB1ZWRlcyBlamVjdXRhciBsYXMgb3JkZW5lcyBlbiBlbCBvcmRlbmFkb3IuIEbDrWphdGUgc29icmUgdG9kbyBlbiBsYSB0ZXJjZXJhIGV4cHJlc2nDs24KCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludChjb2xvciA9ICJwdXJwbGUiKSArIGdlb21fc21vb3RoKCkKCmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoY29sb3IgPSAiYnJvd24iKQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBTcGVjaWVzKSkKZ2dwbG90KGlyaXMpICsgZ2VvbV9wb2ludChhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykgKSArIGdlb21fc21vb3RoKGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkKYGBgCgo8YnI+CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiMTAwJSJ9CnAxIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKcDIgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCnAzIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludChjb2xvciA9ICJwdXJwbGUiKSArIGdlb21fc21vb3RoKCkKcDQgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChjb2xvciA9ICJicm93biIpCnA1IDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IFNwZWNpZXMpKQpwNiA8LSBnZ3Bsb3QoaXJpcykgKyBnZW9tX3BvaW50KGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSApICsgZ2VvbV9zbW9vdGgoYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKQoKcDEgKyBwMiArIHAzICsgcDQgKyBwNSArIHA2ICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKCgpFbiBsYSB0ZXJjZXJhIGV4cHJlc2nDs24gc2UgZXNwZWNpZmljYSBgY29sb3IgPSBTcGVjaWVzYCBkZW50cm8gZGUgYGFlcygpYCBlbiBgZ2dwbG90KClgLCBhc8OtIHF1ZSwgZGUgbW9tZW50bywgdG9kb3MgbG9zIGdlb21zIGRlbCBncsOhZmljbyBkZWJlcsOtYW4gZGlmZXJlbmNpYXIgcG9yIGVzcGVjaWVzIGRlIGxpcmlvcyB1c2FuZG8gZWwgY29sb3IsIFBFUk8sIGRlc3B1w6lzIHNlIHZ1ZWx2ZSBhIHVzYXIgZWwgYXJndW1lbnRvIGBjb2xvcmAgZGVudHJvIGRlIGBnZW9tX3BvaW50KClgLCBwZXJvIGbDrWphdGUgcXVlIG5vIHZhIGRlbnRybyBkZSBkZSBgYWVzKClgLCB2YSBmdWVyYS4gQ29uY3JldGFtZW50ZSBoYWNlbW9zIGxvIHNpZ3VpZW50ZTogYGdlb21fcG9pbnQoY29sb3IgPSAicHVycGxlIilgOyBlcyBkZWNpciwgcGFyYSBsYSBjYXBhIGRlIHB1bnRvcywgeSBzb2xvIHBhcmEgbGEgY2FwYSBkZSBwdW50b3MgcXVlIHNlIGNyZWEgY29uIGBnZW9tX3BvaW50KClgLCBlc3RhbW9zIGFzb2NpYW5kbyBsYSBlc3TDqXRpY2EgY29sb3IsIG5vIGEgdW5hIHZhcmlhYmxlLCBzaW5vIGEgdW4gY29sb3IgZmlqby4gU2luIGVtYmFyZ28sIHBhcmEgbGEgb3RyYSBjYXBhIGRlbCBncsOhZmljbywgbGEgcXVlIHJlc3VsdGEgZGUgdXNhciBgZ2VvbV9zbW9vdGgoKWAgc2lndWUgc2llbmRvIHZhbGlkbyBxdWUgbGEgZXN0w6l0aWNhIGNvbG9yIGVzdMOhIGFzb2NpYWRhIGEgbGEgdmFyaWFibGUgU3BlY2llcy4gCgoKVW4gZGV0YWxsZTogaW1hZ2luYSBxdWUgZW4gdW4gZ3LDoWZpY28gZW4gZWwgcXVlIGhhcyBmaWphZG8gMyBlc3TDqXRpY2FzIGRlbnRybyBkZSBgZ2dwbG90KGFlcygpKWAuIEVuIHByaW5jaXBpbyBsYXMgMyBlc3TDqXRpY2FzIGFmZWN0YXLDoW4gYSB0b2RvcyBsb3MgYGdlb21feHgoKWAgcXVlIHV0aWxpY2VzIGVuIHR1IGdyw6FmaWNvLCBwZXJvIHNpIHF1aXNpZXJhcyBxdWUsIHBvciBlamVtcGxvLCBsYSBlc3TDqXRpY2EgY29sb3Igbm8gYWZlY3Rhc2UgYSB1biBnZW9tIGNvbmNyZXRvLCBwb2Ryw61hcyBoYWNlciBsbyBzaWd1aWVudGU6IGBnZW9tX3h4KGFlcyhjb2xvciA9IE5VTEwpKWAuICAgCgoKQ29tbyBwdWVkZXMgaW1hZ2luYXIsIGHDum4gdGVuZW1vcyBxdWUgdmVyIG3DoXMgZWxlbWVudG9zIGRlIGBnZ3Bsb3QyYC4gQ29tbyBtw61uaW1vIGxvcyB0w610dWxvcyB5IGxleWVuZGFzLCBsb3MgZWplcywgZWwgdGVtYSwgY29vcmRlbmFkYXMsIGV0Yy4uLiB2YW1vcyBhIGVsbG8hIQoKPGJyPgoKCiMjIDMuIEVsZW1lbnRvcyBkZSB1biBnZ3Bsb3QKCgpZYSBoZW1vcyBwcmVzZW50YWRvIGxvcyBwcmluY2lwYWxlcyBlbGVtZW50b3MgZGUgbG9zIGdyw6FmaWNvcyBoZWNob3MgY29uIGBnZ3Bsb3QyYCwgbG9zIHF1ZSB0aWVuZW4gcXVlIHZlciBjb24gbGEgcmVwcmVzZW50YWNpw7NuIGRlIGxhcyB2YXJpYWJsZXMuIFBlcm8gZXMgZXZpZGVudGUgcXVlIHVuIGdyw6FmaWNvIHRpZW5lIG11Y2hvcyBtw6FzIGVsZW1lbnRvcywgeSBsw7NnaWNhbWVudGUgaGF5IHF1ZSBjb25vY2VybG9zIHVuIHBvY28gIHBhcmEgcG9kZXIgYWp1c3RhciBsb3MgZ3LDoWZpY29zIGEgbnVlc3RyYXMgbmVjZXNpZGFkZXMgeSBtZWpvcmFyIGxhIGNhbGlkYWQgZGUgbnVlc3Ryb3MgZ3LDoWZpY29zLiAKCkVqZW1wbG9zIGRlIG90cm9zIGVsZW1lbnRvcyBzb246IHTDrXR1bG9zIGRlbCBncsOhZmljbyB5IGRlIGxvcyBlamVzLCAidGhlbWUiIGRlbCBncsOhZmljbywgc21hbGwgbXVsdGlwbGVzIG8gZmFjZXRpbmcsIGFub3RhY2lvbmVzIGV0Yy4uLgoKRW4gZXN0w6Egc2VjY2nDs24gaXJlbW9zIG3DoXMgcsOhcGlkby4gU2UgcHJlc2VudGFyw6FuIHNvbGFtZW50ZSBhbGd1bm9zIGVqZW1wbG9zLCBjb25jZXB0b3MgeS9vIGFjbGFyYWNpb25lcy4gU2kgbmVjZXNpdGFzIHByb2Z1bmRpemFyIG3DoXMgZW4gZXN0b3MgZWxlbWVudG9zLCBwdWVkZXMgYWN1ZGlyIGEgbGEgW3JlZmVyZW5jaWEgb2ZpY2lhbF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2luZGV4Lmh0bWwjc2VjdGlvbi1sYXllci1nZW9tcykgZGUgYGdncGxvdDJgIG8gYWwgW2Jvb2tkb3duIGRlIGdncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy8pLgoKCllhIGRpamltb3MgcXVlIGxvcyBncsOhZmljb3MgZ2dwbG90IHNlIGNvbXBvbmVuIGRlIGNhcGFzIG8gbGF5ZXJzLiBQYXJhIG5vc290cm9zLCBoYXN0YSBhaG9yYSwgdW5hIGNhcGEgZXN0YWJhIGNvbXB1ZXN0YSBkZSAzIGVsZW1lbnRvczoKCiAgLSB1biBjb25qdW50byBkZSBkYXRvcyAgCiAgCiAgLSB1biBjb25qdW50byBkZSB2YXJpYWJsZXMgbWFwZWFkYXMgY29uIGBhZXMoKWAgYSBwcm9waWVkYWRlcyBlc3TDqXRpY2FzICAKICAKICAtIHVuYSBnZW9tZXRyw61hLCBjb24gYGdlb21feHgoKWAgCiAgCkVzIGV2aWRlbnRlIHF1ZSBlbiB0b2RvcyBsb3MgZ2VvbXMgbm8gc2UgcHVlZGVuIGVzcGVjaWZpY2FyIHRvZGFzIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGVzdMOpdGljYXMuIFBvciBlamVtcGxvIHNpIHVzYXMgYGdlb21fcG9pbnRgIG5vIHBvZHLDoXMgZXNwZWNpZmljYXIgbGEgYW5jaHVyYSBvIGVsIHRpcG8gZGUgbGFzIGxpbmVhcywgcG9ycXVlIG5vIGVzdMOhcyB1c2FuZG8gbGluZWFzIHNpbm8gcHVudG9zLiBQYXJhIHZlciBxdWUgZXN0w6l0aWNhcyBhZG1pdGUgY2FkYSBnZW9tIHRlbmRyw6FzIHF1ZSBtaXJhciBsYSBheXVkYSBkZSBjYWRhIGdlb20uIEFsIGZpbmFsIGRlIFtlc3RlIHBvc3RdKGh0dHBzOi8vd3d3LnlpaGFud3UuY2EvcG9zdC9nZW9tcy1hbmQtYWVzdGhldGljLXBhcmFtZXRlcnMvKSB0aWVuZW4gdW4gZ3LDoWZpY28gaW50ZXJhY3Rpdm8gY29uIGVsIHF1ZSBzZSBwdWVkZSAgdmVyIGbDoWNpbG1lbnRlIHF1ZSBjYXJhY3RlcsOtc3RpY2FzIGVzdMOpdGljYXMgYWRtaXRlIGNhZGEgZ2VvbS4gUG9yIGVqZW1wbG8gYGdlb21fYmFyKClgLCBxdWUgc2lydmUgcGFyYSBoYWNlciBncsOhZmljb3MgZGUgYmFycmFzLCBubyBhZG1pdGUgbWFwcGVhciB2YXJpYWJsZXMgYWwgZWplIFksIHlhIHF1ZSBlbiBlbCBlamUgWSBzZSB2aXN1YWxpemFuL21hcGVhbiBsYXMgZnJlY3VlbmNpYXMgYWJzb2x1dGFzIG8gcmVsYXRpdmFzIGRlIGxhIHZhcmlhYmxlIHF1ZSBzZSByZXByZXNlbnRhIGVuIGVsIGVqZSBYLgoKPGJyPgoKRXN0byBlcyBsbyBiw6FzaWNvIHF1ZSBoYXkgcXVlIHNhYmVyLCBwZXJvIGVuIHJlYWxpZGFkLCB1bmEgY2FwYSBuZWNlc2l0YSBkZSBkb3MgZWxlbWVudG9zIG3DoXM6IHVuYSAqKnN0YXQqKiAobyB0cmFuc2Zvcm1hY2nDs24gZXN0YWTDrXN0aWNhKSB5IHVuYSAqKnBvc2ljacOzbioqLiAgRXN0b3MgZG9zIMO6bHRpbW9zIGVsZW1lbnRvcyBzb24gbmVjZXNhcmlvcyBwZXJvIGxhIHZlcmRhZCBlcyBxdWUgcG9kcsOtYW1vcyBzZWd1aXIgaGFjaWVuZG8gZ3LDoWZpY29zIGNvbiBgZ2dwbG90MmAgc2luIGNvbm9jZXJsb3MuIMK/UG9yIHF1w6k/IFB1ZXMgcG9ycXVlIHNpIGVuIHVuYSBjYXBhIG5vIGxvcyBlc3BlY2lmaWNhbW9zLCBsbyBoYWNlIGBnZ3Bsb3QyYCBwb3Igbm9zb3Ryb3MuIExvIGhhIGVzdGFkbyBoYWNpZW5kbyBoYXN0YSBhaG9yYSBlbiB0b2RvcyBsb3MgZ3LDoWZpY29zIHF1ZSBsbGV2YW1vcyBoZWNob3MuIFBlcm8gY2xhcm8sIHNhYmVyIGNvbW8gdXRpbGl6YXIgZXN0b3MgZWxlbWVudG9zIG5vcyBkYXLDoSBtw6FzIGZsZXhpYmlsaWRhZCBhIGxhIGhvcmEgZGUgaGFjZXIgZ2dwbG90cy4KCgpHZW5lcmFsbWVudGUgbGFzIGNhcGFzIHNlIHZhbiBhw7FhZGllbmRvIGNvbiBsYSBmYW1pbGlhIGRlIGZ1bmNpb25lcyBgZ2VvbV94eCgpYCwgKipQRVJPKiogdGFtYmnDqW4gc2UgcHVlZGVuIGHDsWFkaXIgY2FwYXMgY29uIG90cmEgZmFtaWxpYSBkZSBmdW5jaW9uZXMgYHN0YXRfeHgoKWAuIAoKCkFwYXJ0ZSBkZSBlc3RvcyBjaW5jbyBlbGVtZW50b3MgKGRhdG9zLCBhZXMoKSwgZ2VvbSwgc3RhdCB5IHBvc2ljacOzbikgbG9zIGdyw6FmaWNvcyBnZ3Bsb3QgcHVlZGVuIHRlbmVyIG3DoXMgZWxlbWVudG9zLiBWZcOhbW9zbG9zIHVubyBhIHVuby4KCjxicj4KCiMjIyAzLjEgVMOtdHVsb3MgZGVsIGdyw6FmaWNvCgpFcyBldmlkZW50ZSBxdWUgdW4gZ3LDoWZpY28gcGFyYSBzZXIgZWZlY3Rpdm8geSBtb3N0cmFyIHN1IG1lbnNhamUgY29uIGNsYXJpZGFkIGRlYmUgdGVuZXIgdW4gdMOtdHVsbyB5L28gc3VidMOtdHVsbyBpbHVzdHJhdGl2byB5IGRlYmUgbW9zdHJhciBpbmZvcm1hY2nDs24gcmVsZXZhbnRlIHNvYnJlIHF1ZSB2YXJpYWJsZXMgc2UgZ3JhZmljYW4gbG9zIGVqZXMgWCBlIFkuIEVzdGUgdGlwbyBkZSBlbGVtZW50b3MgcHVlZGVuIG1vZGlmaWNhcnNlIGRlIHZhcmlhcyBtYW5lcmFzLCBwZXJvIG5vcyBjZW50cmFyZW1vcyBlbiBsYSBmdW5jacOzbiBgbGFicygpYC4gCgpGw61qYXRlIHF1ZSBjb24gbGEgZnVuY2nDs24gYGxhYnMoKWAsIGRlIGxhYmVscywgcG9kZW1vcyBjYW1iaWFyIGxvcyB0w610dWxvcyBkZWwgZ3LDoWZpY28sIGRlIGxvcyBlamVzIHkgdGFtYmnDqW4gZGUgbGFzIGxleWVuZGFzLiAKCkVuIGxvcyB0w610dWxvcyAodGFudG8gZGVsIGdyw6FmaWNvLCBjb21vIGRlIGxvcyBlamVzIHkgbGV5ZW5kYXMpIHRhbWJpw6luIHNlIHB1ZWRlbiBjYW1iaWFyIG90cmFzIGNhcmFjdGVyw61zdGljYXM7IHBvciBlamVtcGxvLCBjYW1iaWFyIGVsIHRhbWHDsW8sIGxhIGZ1ZW50ZSBvIGVsIGNvbG9yLCBwZXJvIGVzbyBzZXLDoSB0YXJlYSBkZSBvdHJhIGZ1bmNpw7NuIGRlIGBnZ3Bsb3QyYDogZGVsIGdydXBvIGRlIGZ1bmNpb25lcyBgdGhlbWVfKClgLiBQZXJvIGVsIHRlbWEgbyB0aGVtZSBkZSBsb3MgZ3LDoWZpY29zIGxvIHZlcmVtb3MgZW4gZWwgc2lndWllbnRlIGFwYXJ0YWRvLgoKClRvbWVtb3MgZWwgc2lndWllbnRlIGdyw6FmaWNvIGNvbW8gcmVmZXJlbmNpYSB5IHNvYnJlIMOpbCBpcmVtb3MgYcOxYWRpZW5kbyBlbGVtZW50b3M6CgpgYGB7cn0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKSArIGdlb21fcG9pbnQoKQpwCmBgYAoKQ29uIGxhIGZ1bmNpw7NuIGBsYWJzKClgIHBvZGVtb3MgYcOxYWRpcmxlIHVuIHTDrXR1bG8sIHN1YnRpdHVsbywgcGllIGRlIGdyw6FmaWNvIG8gKmNhcHRpb24qLiBUYW1iacOpbiBwb2RlbW9zIGNhbWJpYXIgZWwgdMOtdHVsbyBkZSBsb3MgZWplcyBYIGUgWSwgYXPDrSBjb21vIHRhbWJpw6luIGVsIHRpdHVsbyBkZSBsYSBsZXllbmRhIHBhcmEgYGNvbG9yYCwgbyBwYXJhIG90cmFzIGVzdMOpdGljYXMgcXVlIHV0aWxpY2Vtb3MgZW4gZWwgZ3LDoWZpY28uCgpFcyBzdWZpY2llbnRlIGNvbiB2ZXIgdW4gZWplbXBsbzoKCgpgYGB7cn0KcCArIGxhYnModGl0bGUgPSAiR3LDoWZpY28gMTogTG9uZ2l0dWQgZGVsIHPDqXBhbG8gZnJlbnRlIGFsIHDDqXRhbG8iLAogICAgICAgc3VidGl0bGUgPSAiKGRpZmVyZW5jaWFuZG8gcG9yIGVzcGVjaWUgZGUgbGlyaW8pIiwKICAgICAgIGNhcHRpb24gPSAiRGF0b3MgcHJvdmVuaWVudGVzIGRlbCBJcmlzIGRhdGFzZXQiLAogICAgICAgeCA9ICJMb25naXR1ZCBkZWwgc8OpcGFsbyIsCiAgICAgICB5ID0gIkxvbmdpdHVkIGRlbCBww6l0YWxvIiwKICAgICAgIGNvbG9yID0gIkVzcGVjaWUgZGUgbGlyaW8iKQpgYGAKCgpTaSBxdWlzaWVyYXMgZWxpbWluYXIgY29tcGxldGFtZW50ZSBsb3MgKip0w610dWxvcyoqICBkZWwgZWplIFggcG9kcsOtYXMgaGFjZXJsbyBlbiBlbCBhbnRlcmlvciBjaHVuayBmaWphbmRvIGB4ID0gTlVMTGAgZGVudHJvIGRlIGxhIGZ1bmNpw7NuIGBsYWJzKClgLgoKCkVuIGx1Z2FyIGRlIHVzYXIgbGEgZnVuY2nDs24gYGxhYnMoKWAsIHRhbWJpw6luIHBvZGVtb3MgdXRpbGl6YXIgbGFzIGZ1bmNpb25lcyBhdXhpbGlhcmVzIGB4bGFiKClgIGUgYHlsYWIoKWAKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgbGFicyhjb2xvciA9IE5VTEwsIHggPSBOVUxMKSAgIy0gYm9ycmEgZWwgdMOtdHVsbyBkZSBsYSBsZXllbmRhIHkgZGVsIGVqZSBYCnAgKyB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSAgICAgICAjLSBlbGltaW5hIHTDrXR1bG9zIGRlIGxvcyBlamVzIFggZSBZCmBgYAoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI5MCUiLCAgZmlnLmFzcCA9IDMvOX0KcDEgPC0gcCArIGxhYnMoY29sb3IgPSBOVUxMLCB4ID0gTlVMTCkgICMtIGJvcnJhIGVsIHTDrXR1bG8gZGUgbGEgbGV5ZW5kYSB5IGRlbCBlamUgWApwMiA8LSBwICsgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgICAgICAgIy0gZWxpbWluYSB0w610dWxvcyBkZSBsb3MgZWplcyBYIGUgWQoKcDEgKyBwMiArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCgoKCiMjIyAzLjIgVGhlbWVzCgo+IFRoZW1lcyBjb250cm9sIHRoZSBkaXNwbGF5IG9mIGFsbCBub24tZGF0YSBlbGVtZW50cyBvZiB0aGUgcGxvdC4gWW91IGNhbiBvdmVycmlkZSBhbGwgc2V0dGluZ3Mgd2l0aCBhIGNvbXBsZXRlIHRoZW1lIGxpa2UgdGhlbWVfYncoKSwgb3IgY2hvb3NlIHRvIHR3ZWFrIGluZGl2aWR1YWwgc2V0dGluZ3MgYnkgdXNpbmcgdGhlbWUoKSBhbmQgdGhlIGVsZW1lbnRfIGZ1bmN0aW9ucy4gVXNlIHRoZW1lX3NldCgpIHRvIG1vZGlmeSB0aGUgYWN0aXZlIHRoZW1lLCBhZmZlY3RpbmcgYWxsIGZ1dHVyZSBwbG90cy4KClBhcmEgY2FtYmlhciBkZXRhbGxlcyBkZSBsYSBhcGFyaWVuY2lhIGRlbCBncsOhZmljbyBjb21vIGVsIHRhbWHDsW8sIGZ1ZW50ZXMgeSBjb2xvciBkZSBsb3MgdMOtdHVsb3MsIHBlcm8gdGFtYmnDqW4gZGUgbG9zIHB1bnRvcywgbGFzIGxpbmVhcywgZWwgZm9uZG8gZGVsIGdyw6FmaWNvLCBsYSBhcGFyaWVuY2lhIGRlIGxhcyBncmlkLWxpbmVzLCBlbCBsdWdhciBwYXJhIGxhcyBsZXllbmRhcywgZXRjLi4uIGV0Yy4uLiBjb250YW1vcyBjb24gbGFzICJmdW5jaW9uZXMgZGUgdGVtYSI7IHRvZGFzIGVsbGFzIGNvbWllbnphbiBjb24gYHRoZW1lXygpYAoKRW4gZ2VuZXJhbCBjb24gbGFzIGZ1bmNpb25lcyBgdGhlbWVfKClgIHBvZGVtb3MgY2FtYmlhci9hanVzdGFyIGN1YWxxdWllciBlbGVtZW50byBkZWwgZ3LDoWZpY28sIGNvbiBsYSBleGNlcGNpw7NuIGRlIGxhIHByb3BpYSByZXByZXNlbnRhY2nDs24gZGUgbG9zIGRhdG9zICh5YSBzYWJlbW9zIHF1ZSBlc3RvIHNlIGhhY2VuIGNvbiBsYXMgZnVuY2lvbmVzIGBnZW9tXygpYCkuIEVzdG9zIGVsZW1lbnRvcyBhZmVjdGFuIGEgbGEgYXBhcmllbmNpYSB5IGRldGFsbGVzIGRlbCBncsOhZmljbywgcGVybyBubyBhIGxhIHJlbGFjacOzbiBlbnRyZSB2YXJpYWJsZXMgcXVlIHNlIG11ZXN0cmEgcmVhbG1lbnRlIGVuIGVsIGdyw6FmaWNvLgoKClBhcmEgZW1wZXphciBhIGVudGVuZGVyIHF1ZSBoYWNlbiBsYXMgZnVuY2lvbmVzIHJlbGFjaW9uYWRhcyBjb24gZWwgdGhlbWUsIHNlw7FhbGFyIHF1ZSBgZ2dwbG90MmAgaW5jb3Jwb3JhIHVuIGNvbmp1bnRvIGRlICJ0ZW1hcyIgcXVlIHBvZGVtb3MgdXRpbGl6YXIgcGFyYSBjYW1iaWFyIGxhIGFwYXJpZW5jaWEgZGVsIGdyw6FmaWNvIGEgbnVlc3RybyBndXN0by4gUHVlZGVzIHZlcmxvcyB0b2RvcyBbYXF1w61dKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZ3RoZW1lLmh0bWwpLiBFbCB0ZW1hIHF1ZSB1c2EgcG9yIGRlZmVjdG8gYGdncGxvdDJgIGVzIGB0aGVtZV9ncmF5KClgLiBWZWFtb3MgYSBsb3MgdGhlbWVzIGVuIGFjY2nDs246CgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyB0aGVtZV9ncmF5KCkgICAjLSB0ZW1hIHBvciBkZWZlY3RvCnAgKyB0aGVtZV9saWdodCgpCnAgKyB0aGVtZV9kYXJrKCkKcCArIHRoZW1lX2NsYXNzaWMoKQpwICsgdGhlbWVfbWluaW1hbCgpCnAgKyB0aGVtZV92b2lkKCkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjkwJSIsICBmaWcuYXNwID0gNS85fQpwMSA8LSBwICsgdGhlbWVfZ3JheSgpICAgIy0gdGVtYSBwb3IgZGVmZWN0bwpwMiA8LSBwICsgdGhlbWVfbGlnaHQoKQpwMyA8LSBwICsgdGhlbWVfZGFyaygpCnA0IDwtIHAgKyB0aGVtZV9jbGFzc2ljKCkKcDUgPC0gcCArIHRoZW1lX21pbmltYWwoKQpwNiA8LSBwICsgdGhlbWVfdm9pZCgpCgpwMSArIHAyICsgcDMgKyBwNCArIHA1ICsgcDYgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKCkVsIHBhcXVldGUgZGUgUiBbZ2d0aGVtZXNdKGh0dHBzOi8vanJub2xkLmdpdGh1Yi5pby9nZ3RoZW1lcy9yZWZlcmVuY2UvaW5kZXguaHRtbCkgaW5jb3Jwb3JhIHVuYSBhbXBsaWEgbGlzdGEgZGUgdGVtYXMgYWRpY2lvbmFsZXMsIGFsZ3Vub3MgZGUgZWxsb3MgdHJhdGFuIGRlIHJlcGxpY2FyIGVsIGVzdGlsbyBkZSBjb3Jwb3JhY2lvbmVzIGZhbW9zYXMgY29tbyBUaGUgRWNvbm9taXN0IG8gU3RhdGEuIEVuIGVsIHR1dG9yaWFsIG5vIHNlIHZlbiBiaWVuIGxvcyBncsOhZmljb3MgcG9ycXVlIGxvcyBoZSBoZWNobyBwZXF1ZcOxaXRvcywgcGVybyBwcnVlYmEgYSBoYWNlcmxvcyB0w7ogbWlzbW8geSB2ZXLDoXMgcXVlIHNvbiBnb29kLWxvb2tpbmcuIFZlYW1vcyBhbGd1bm9zOgoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGdndGhlbWVzKQpwICsgdGhlbWVfZWNvbm9taXN0KCkgICAKcCArIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpCnAgKyB0aGVtZV9zdGF0YSgpCnAgKyB0aGVtZV9zb2xhcml6ZWQoKQpgYGAKCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjg1JSIsICBmaWcuYXNwID0gNS85fQpsaWJyYXJ5KGdndGhlbWVzKQpwMSA8LSBwICsgdGhlbWVfZWNvbm9taXN0KCkgCnAyIDwtIHAgKyB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKQpwMyA8LSBwICsgdGhlbWVfc3RhdGEoKSAgCnA0IDwtIHAgKyB0aGVtZV9zb2xhcml6ZWQoKQoKcDEgKyAgcDIgKyBwbG90X2xheW91dChuY29sID0gMikKcDMgKyAgcDQgKyBwbG90X2xheW91dChuY29sID0gMikKCgpgYGAKCgoKClRhbWJpw6luIHBvZGVtb3MgZGVmaW5pciB1biB0ZW1hIHByb3BpbyBwYXJhIHF1ZSBlbCBncsOhZmljbyBzZSBhanVzdGUgbG9zIG3DoXMgcG9zaWJsZSBhIG51ZXN0cmFzIHByZWZlcmVuY2lhcy4KCgpgYGB7ciwgb3V0LndpZHRoID0gIjUwJSIsICBmaWcuYXNwID0gNC85fQojIGRlZmluZSBjdXN0b20gdGhlbWUKbXlfdGhlbWUgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSAKICAgICAgICAgICAgICAgICAgZWxlbWVudF90ZXh0KGNvbG91ciA9ICJncmV5MjAiLCBzaXplID0gMTIsIGFuZ2xlID0gOTAsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSkgLCAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiZ3JleTIwIiwgc2l6ZSA9IDEyKSAsCiAgICAgICAgICAgICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkKcCArIG15X3RoZW1lCmBgYAoKClNlIHB1ZWRlIGZpamFyIGVsIHRlbWEvdGhlbWUgZGUgbG9zIGdyw6FmaWNvcyBjb24gbGEgZnVuY2nDs24gYHRoZW1lX3NldCgpYC4gUG9yIGVqZW1wbG86CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKSAgIy0gdW4gdGVtYSBjb25jcmV0bwp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpICsgCiAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpKSAjLSB1biB0ZW1hIG1vZGlmaWNhbmRvIGFsZ3VuYXMgb3BjaW9uZXMsIHF1ZSBlbGUgZWplIHggbm8gbXVlc3RyZSB0aWNrcyBuaSBlc2NhbGFzCmBgYAoKU2kgcXVpZXJlcyB2b2x2ZXIgYWwgdGhlbWUgcG9yIGRlZmVjdG86CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KdGhlbWVfc2V0KHRoZW1lX2dyYXkoKSkKYGBgCgoKRWplbXBsb3MgZGUgYWxndW5vcyBlbGVtZW50b3MgY3V5YSBhcGFyaWVuY2lhIHF1ZSBzZSBwdWVkZW4gY2FtYmlhciBjb24gYHRoZW1lKClgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgICAgICAgICAgICMtIHF1ZSBubyBhcGFyZXpjYSBsZXllbmRhCnAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgICAgICAgICAgIy0gbGV5ZW5kYSBhYmFqbwpwICsgdGhlbWUobGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgICAgICMtIGxleWVuZGEgaG9yaXpvbnRhbCEhCnAgKyB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSkgICAgICMtIHTDrXR1bG8gZGUgbGEgbGV5ZW5kYSBhIDIyCnAgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDIuNCwgImNtIikpICAgICAgICAgICMtIHRhbWHDsW8gZGUgbG9zIGN1YWRyb3MgZGUgbGEgbGV5ZW5kYQoKCnAgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIikpICAgICAgICAgIy0gY2FtYmlhciBlbCB0YW1hw7FvIGRlIHRvZG9zIGxvcyBlbGVtZW50b3MgZGUgdGV4dG8KcCArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpICAgICAgICAgICAgICAgICAgICAjLSBwb25lIGVuIG5lZ3JpdGEgdG9kb3MgbG9zIGVsZW1lbnRvcyBkZSB0ZXh0bwoKcCArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJwaW5rIiwgc2l6ZSA9IDEyLCBhbmdsZSA9IDkwLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUpKSAjIGFwYXJpZW5jaWEgZGUgbGEgZXNjYWxhIGRlbCBlamUgeAoKcCArIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTI1LCBhbmdsZSA9IDQ1KSkgIy0gdGFtYcOxbyB5IGFuZ3VsbyBkZWwgdGV4dG8gZGVsIGVqZSBZCgpwICsgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDMpKSAgICMtIHBvc2ljacOzbiBob3Jpem9udGFsIGRlbCBzdWJ0aXR1bG8gKHNpIGxvIHR1dmllc2UpCiAgCnAgKyB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAzKSkgICAgIy0gcG9zaWNpw7NuIHZlcnRpY2FsIGRlbCBwaWUgZGUgZ3LDoWZpY28gKHNpIGxvIHR1dmllc2UpCgpwICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZWVuIiwgY29sb3VyID0gInBpbmsiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIsIHNpemUgPSAzLjUpKQpwICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKcCArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBOVUxMKQoKCnAgKyB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJwaW5rIiwgY29sb3VyID0gInB1cnBsZSIsIGxpbmV0eXBlID0gImRvdHRlZCIsIHNpemUgPSA3KSkKYGBgCgoKU2kgcXVpZXJlcyB2ZXIgdG9kb3MgbGFzIGNhcmFjdGVyw61zdGljYXMgcXVlIGNvbnRyb2xhIHkgcXVlIHBvciB0YW50byBwdWVkZXMgbW9kaWZpY2FyIGNvbiBgdGhlbWUoKWAsIHVzYSBsYSBheXVkYSBkZSBsYSBmdW5jacOzbiBgdGhlbWUoKWAgbyBlamVjdXRhIGVuIFIgYGFyZ3ModGhlbWUpYC4gQXVucXVlIGNhc2kgbWVqb3IgdmVybGFzIGVuIFtlc3RhIGluZm9ncmFmw61hXShodHRwczovL2hlbnJ5d2FuZy5ubC9nZ3Bsb3QyLXRoZW1lLWVsZW1lbnRzLWRlbW9uc3RyYXRpb24vKSBkZSBIZW5yeSBXYW5nIG8gZW4gW2VzdGUgcG9zdF0oaHR0cHM6Ly9pc2FiZWxsYS1iLmNvbS9ibG9nL2dncGxvdDItdGhlbWUtZWxlbWVudHMtcmVmZXJlbmNlLykgZGUgSXNhYmVsbGEgQmVuYWJheWUgY29uIGxhcyBvcGNpb25lcyBxdWUgc3VlbGUgY2FtYmlhciBlbGxhLgoKCgpFbiBnZW5lcmFsLCBzaSBxdWllcmVzIGNhbWJpYXIgYWxnw7puIGVsZW1lbnRvIGRlIHVuIGdncGxvdCwgaGFzIGRlIGhhY2VyIGB0aGVtZShlbGVtZW50byA9IGVsZW1lbnRfdGV4dCgpKWAuIFNpIHF1aWVyZXMgZWxpbWluYXIgcG9yIGNvbXBsZXRvIGFsZ8O6biBlbGVtZW50byBkZWwgZ3LDoWZpY28sIHBvciBlamVtcGxvIGxhcyBncmlkLWxpbmVzIGRlbCBncsOhZmljbywgaGFyw61hcyBgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSlgLgoKCkV2aWRlbnRlbWVudGUgdG9kbyBlc3RvIGVzIGltcG9zaWJsZSBkZSBhcHJlbmRlciwgc8OzbG8gdGllbmVzIHF1ZSBzYWJlciBxdWUgY3VhbHF1aWVyIGVsZW1lbnRvIGRlbCBncsOhZmljbyBzZSBwdWVkZSBjYW1iaWFyIHkgdGllbmVzIHF1ZSBzYWJlciBidXNjYXIgZSBpbnRlcnByZXRhciBsYSBheXVkYS4KCiMjIyMgQ29sb3JlcwoKClNlIHBvY28gZGUgY29sb3JlcywgcGVybyBjbGFybyBzaSBxdWllcmVzIGNhbWJpYXIgbGEgYXBhcmllbmNpYSBkZSBsb3MgZ3LDoWZpY29zLCBoYXMgZGUgc2FiZXIgbG9zIGNvbG9yZXMgZGUgUiwgYXPDrSBxdWU6CgotIFtBcXXDrV0oaHR0cDovL3NhcGUuaW5mLnVzaS5jaC9xdWljay1yZWZlcmVuY2UvZ2dwbG90Mi9jb2xvdXIpIHRpZW5lcyB1bmEgZ3XDrWEgcGFyYSBlbGVnaXIgY29sb3IuIFNpIHNvbG8gcXVpZXJlcyB2ZXIgbGEgbGlzdGEgZGUgbm9tYnJlcyBkZSBsb3MgY29sb3JlcyBlbiBSIGVqZWN1dGE6IGBhYSA8LSBhcy5kYXRhLmZyYW1lKGNvbG91cnMoKSlgCgotIFNpIHNhYmVzIGVsIG5vbWJyZSBkZWwgY29sb3IgcXVlIHF1aWVyZXMsIFthcXXDrV0oaHR0cHM6Ly9wa2cuZ2Fycmlja2FkZW5idWllLmNvbS9yLWNvbG9ycy1jc3MvKSwgIHBvZHLDoXMgYnVzY2FybG8geSB2ZXIgc3UgY29sb3IgeSBzdSBjb2RpZmljYWNpw7NuIGVuIFJHQiB5IEhleC4KCgotIFRhbWJpw6luIGVzIGludGVyZXNhbnRlIGVsIHBhcXVldGUgW3BhbGV0ZWVyXShodHRwczovL2dpdGh1Yi5jb20vRW1pbEh2aXRmZWxkdC9wYWxldHRlZXIpIHF1ZSBhZ3J1cGEgdW4gY29uanVudG8gYW1wbGlvIGRlIHBhbGV0YXMgZGUgY29sb3JlcyBwYXJhIHVzYXIgZW4gUi4gCgo8YnI+CgojIyMjIFhLQ0QgdGhlbWUKClBvciDDumx0aW1vLCBubyBzw6kgc2kgY29ub2PDqWlzIGVsIFt3ZWJjb21pYyBYS0NEXShodHRwczovL3hrY2QuY29tLykuIFB1ZXMgZW4gUiB0YW1iacOpbiBoYXkgdW4gcGFxdWV0ZSB5IHVuIHRoZW1lIHBhcmEgaGFjZXIgZ3LDoWZpY29zIGFsIGVzdGlsbyBYS0NELiBFcyBlbCBbcGFxdWV0ZSB4a2NkXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMveGtjZC9pbmRleC5odG1sKSBjdXlvIGF1dG9yIGVzIEVtaWxpbyBUb3JyZXMtTWFuemFuZXJhIGRlIGxhIFVuaXZlcnNpZGFkIGRlIE92aWVkby4gW0FxdcOtXShodHRwOi8veGtjZC5yLWZvcmdlLnItcHJvamVjdC5vcmcvKSBwb2TDqWlzIHZlciBhbGd1bm9zIGdyw6FmaWNvcyBoZWNob3MgY29uIGVzdGUgZXN0aWxvIGVuIFIuIAoKSW50ZW50w6kgaGFjZXIgdW4gZ3LDoWZpY28gY29uIHN1IHRoZW1lLCBwZXJvIGRlc2Fmb3J0dW5hZGFtZW50ZSBubyBtZSBzYWxpw7M7IHBlcm8ganVzdG8gYWwgZMOtYSBzaWd1aWVudGUgdmkgW2VzdGUgdHdlZXRdKGh0dHBzOi8vdHdpdHRlci5jb20vX0dpbF9IZW5yaXF1ZXMvc3RhdHVzLzExNjY0NDAyNjI3NzM1Njc0ODg/cz0wOSkgcXVlIGhhY2UgYWxnbyBwYXJlY2lkbyBjb24gZGF0b3MgZGUgbG9zIFNpbXBzb25zIHkgW3N1IGPDs2RpZ29dKGh0dHBzOi8vZ2l0aHViLmNvbS9HaWxIZW5yaXF1ZXMvVGlkeVR1ZXNkYXlzL2Jsb2IvbWFzdGVyLzIwMTktMDgtMjclMjBTaW1wc29ucyUyMGd1ZXN0JTIwc3RhcnMvc2ltcHNvbnMuUikgc8OtIG1lIGhhIGZ1bmNpb25hZG8sIGFkZW3DoXMgc2ltdWxhL2NvbnN0cnV5ZSBlbCBlc3RpbG8gWEtDRCBkZXNkZSBjZXJvLiAKCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmFzcCA9IDcvOX0KdHdlZXRybWQ6OnR3ZWV0X2VtYmVkKCJodHRwczovL3R3aXR0ZXIuY29tL19HaWxfSGVucmlxdWVzL3N0YXR1cy8xMTY2MzczODQ0MDQwMzM1MzYwIiwgdGhlbWUgPSAibGlnaHQiLCBhbGlnbiA9ICJjZW50ZXIiLCBtYXh3aWR0aCA9IDQwMCkKYGBgCgoKCkFkZW3DoXMsIGRlc3B1w6lzIHZpIHF1ZSBFdmFuZ2VseW5lIFJlaW5vbGRzIGhpem8gW2VzdGEgbWFyYXZpbGxhXShodHRwczovL2V2YW1hZXJleS5naXRodWIuaW8vdGlkeXR1ZXNkYXlfd2Fsa190aHJvdWdoL3NpbXBzb25zLmh0bWwjMSkuIExvIGhhcmVtb3MgZW4gY2xhc2U/ISBFbiBbZXN0ZSBwb3N0XShodHRwczovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vMjAxOC8wOS9jdXJ2ZS1maXR0aW5nLmh0bWwpIHB1ZWRlcyBlbmNvbnRyYXIgZWwgY8OzZGlnbyBwYXJhIHJlcHJvZHVjaXIgdW5hIGRlIGxhcyBoaXN0b3JpYXMgbyB2acOxZXRhcyBkZSBYS0NELgogCgo8YnI+CgoKIyMjIDMuMyBTbWFsbCBtdWx0aXBsZXMgbyBGYWNldHRpbmcKCgpFbCBzaXN0ZW1hIGdyw6FmaWNvIGRlIGBnZ3Bsb3QyYCBpbmNvcnBvcmEgdW5hIHTDqWNuaWNhIGVzcGVjaWFsIGxsYW1hZGEgImZhY2V0aW5nIiBxdWUgcGVybWl0ZSBkaXZpZGlyIHVuIGdyw6FmaWNvIGVuIG3Dumx0aXBsZXMgZ3LDoWZpY29zLiBDYWRhIHVubyBkZSBlc29zIG3Dumx0aXBsZXMgZ3LDoWZpY29zIHNlIHJlYWxpemEgc8OzbG8gcGFyYSBsYXMgb2JzZXJ2YWNpb25lcyBkZSB1bmEgZGUgbG9zIHZhbG9yZXMgZGUgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhIChvIGZhY3RvcikgaW5jbHVpZG8gZW4gZWwgY29uanVudG8gZGUgZGF0b3MuIEVzIG3DoXMgZsOhY2lsIGhhY2VybG8gcXVlIGV4cGxpY2FybG8vZXNjcmliaXJsby4KClBvciBlamVtcGxvLCBlbiBgaXJpc2AgdGVuZW1vcyBsYSB2YXJpYWJsZSBgU3BlY2llc2AsIHF1ZSBlcyBjYXRlZ8OzcmljYS4gTG8gcXVlIHNlIGhhY2UgY29uIGVsICJmYWNldHRpbmciIGVzIGRpdmlkaXIgZWwgZGF0YXNldCBlbiBncnVwb3MgeSBoYWNlciBlbCBtaXNtbyBncsOhZmljbyBwYXJhIGNhZGEgdW5vIGRlIGxvcyBncnVwb3MuIExvcyBncnVwb3Mgc2UgdmFuIGEgZGVmaW5pciBlbiBmdW5jacOzbiBkZSBsb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBTcGVjaWVzLiBSZWN1ZXJkYSBxdWUgaGF5IHRyZXMgdGlwb3MgbyBlc3BlY2llcyBkZSBsaXJpb3MuCgpQYXJhIGhhY2VyIHVuICJmYWNldHRpbmcgZ3JhcGgiIHBvZGVtb3MgdXNhciBsYXMgZnVuY2lvbmVzIGBmYWNldF93cmFwKClgIHkgYGZhY2V0X2dyaWQoKWAuIAoKUG9yIGVqZW1wbG8sIGNvbiBsYSBmdW5jacOzbiBgZmFjZXRfZ3JpZCgpYCBwdWVkZXMgZWxlZ2lyIGVudHJlIGhhY2VyIGxvcyBzbWFsbCBtdWx0aXBsZXMgcG9yIGZpbGFzIG8gcG9yIGNvbHVtbmFzLiBFbXBlY2Vtb3MgaGFjaWVuZG8gdW4gZmFjZXR0aW5nICoqcG9yIGNvbHVtbmFzKiouIEFkZW3DoXMsIGNvbiBgZmFjZXRfZ3JpZCgpYCBzZSBwdWVkZW4gdXNhciB2YXJpYXMgc2ludGF4aXMsIHBlcm8gbGEgcXVlIGFwYXJlY2UgZW4gbGEgW2NoZWF0c2hlZXQgYWN0dWFsIGRlIGdncGxvdDJdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pIHksIHBvciB0YW50bywgbGEgcmVjb21lbmRhZGEgZXMgbGFzIHF1ZSB2ZXMgZW4gbGEgc2VndW5kYSBsaW5lYTogCgoKYGBge3IsIG91dC53aWR0aCA9ICIxMDAlIiwgZmlnLmFzcCA9IDQvOX0KI3AgKyBmYWNldF9ncmlkKCAuIH4gU3BlY2llcykgICAgICAgICAgICAgICAgIyBvbGQgc2ludGF4aXMKcCArIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoU3BlY2llcykpICAgICAgICAgIyBncsOhZmljb3MgeCBjb2x1bW5hcywgc2VwYXJhbmRvIHBvciB2YWxvcmVzIGRlICdTcGVjaWVzJwpgYGAKCgpBaG9yYSBwb3IgZmlsYXM6CgoKYGBge3IsIG91dC53aWR0aCA9ICI1MCUiLCBmaWcuYXNwID0gOS85fQpwICsgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTcGVjaWVzKSkgICAgICAgICAjIGdyw6FmaWNvcyB4IGZpbGFzCmBgYAoKClRhbWJpw6luIHBvZGVtb3MgdXRpbGl6YXIgbGEgZnVuY2nDs24gKipgZmFjZXRfd3JhcCgpYCoqLiBFc3RhIGZ1bmNpw7NuIHJlcGFydGUgbG9zIHNtYWxsIG11bHRpcGxlcyBlbiB1bmEgcmVqaWxsYSBjb24gZm9ybWEgZGUgbWF0cml6LiAKCmBgYHtyLCBvdXQud2lkdGggPSAiOTAlIiwgZmlnLmFzcCA9IDQvOX0KcCArIGZhY2V0X3dyYXAodmFycyhTcGVjaWVzKSwgbnJvdyA9IDIsIG5jb2wgPSAyKSAgICAgICAgIyBncmFmIHggZmlsYXMgeSBjb2x1bW5hcwpgYGAKCgpTaSBlbiBlbCBkYXRhc2V0IGh1Ymllc2VuIGRvcyB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIHBvZHLDrWFtb3MgaGFjZXIgcXVlIHVuYSBkZSBlbGxhcyBzaXJ2aWVzZSBwYXJhIGxsZW5hciBsYXMgZmlsYXMgeSBsYSBvdHJhIGxhcyBjb2x1bW5hcy4gCgoKQ29tbyBgaXJpc2Agc8OzbG8gdGllbmUgdW5hIHZhcmlhYmxlIGNhdGVnw7NyaWNhIChTcGVjaWVzKSB2YW1vcyBhIGRpc2NyZXRpemFyIHVuYSBkZSBsYXMgdmFyaWFibGVzIGNvbnRpbnVhcy4gUG9yIGVqZW1wbG8gbGEgYW5jaHVyYSBkZWwgcMOpdGFsbywgY3JlYXJlbW9zIHVuYSBudWV2YSB2YXJpYWJsZSBkaXZpZGllbmRvIGxhcyBvYnNlcnZhY2lvbmVzIGRlIGBQZXRhbC5XaWR0aGAgZW4gMiBjYXRlZ29yw61hcywgcG9yIGVuY2ltYSB5IHBvciBkZWJham8gZGUgbGEgbWVkaWEgZGUgc3UgbWVkaWEuIEFkZW3DoXMgbG8gdmFtb3MgIGEgaGFjZXIgY29uIFItYmFzZSB5IGNvbiBgZHBseXI6Om50aWxlKClgLiBTZWd1cm8gcXVlIGhheSBtZWpvcmVzIGZvcm1hcywgW3BvciBlamVtcGxvXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvYXJ1bGVzL3ZlcnNpb25zLzEuNi0zL3RvcGljcy9kaXNjcmV0aXplKQoKCkNvbiBgZHBseXI6Om50aWxlKClgCgpgYGB7cn0KaXJpcyA8LSBpcmlzICU+JSBtdXRhdGUobmV3X3ZhcmlhYmxlID0gbnRpbGUoUGV0YWwuV2lkdGgsIDIpKSAKYGBgCgpDb24gUi1iYXNlIHkgbGEgZnVuY2nDs24gYGN1dCgpYDoKCmBgYHtyfQppcmlzIDwtIGlyaXMKaXJpcyRuZXdfdmFyaWFibGUgPC0gY3V0KGlyaXMkUGV0YWwuV2lkdGgsIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygtSW5mLCBtZWFuKGlyaXMkUGV0YWwuV2lkdGgpLCBJbmYpLCAKICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImRlYmFqby1tZWRpYSIsICJhcnJpYmEtbWVkaWEiKSkKYGBgCgoKCkFob3JhIHlhIHRlbmVtb3MgZG9zIHZhcmlhYmxlIGRpc2NyZXRhIHkgcG9kZW1vcyBoYWNlciBxdWUgYGZhY2V0X2dyaWQoKWAgdXRpbGljZSB1bmEgdmFyaWFibGUgcGFyYSBsbGVuYXIgZmlsYXMgeSBvdHJhIHBhcmEgY29sdW1uYXMuIFNlIHB1ZWRlIGVzcGVjaWZpY2FyIGRlIGRvcyBtYW5lcmFzCgoKYGBge3IsIG91dC53aWR0aCA9ICI5MCUiLCBmaWcuYXNwID0gNC85LCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzKSArIGdlb21fcG9pbnQoIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKwpmYWNldF9ncmlkKHJvd3MgPSB2YXJzKG5ld192YXJpYWJsZSksIGNvbHMgPSB2YXJzKFNwZWNpZXMpKSAgICAgICAgIyBncmFmIHggZmlsYXMgeSBjb2x1bW5hcwpgYGAKCgpgYGB7ciwgb3V0LndpZHRoID0gIjkwJSIsIGZpZy5hc3AgPSA0Lzl9CmdncGxvdChpcmlzKSArIGdlb21fcG9pbnQoIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKwpmYWNldF9ncmlkKG5ld192YXJpYWJsZSB+IFNwZWNpZXMpICAgICAKYGBgCgoKQ29tbyB2ZW1vcywgbG9zIGxpcmlvcyBkZSBsYSBjbGFzZSBzZXRvc2Egc2llbXByZSB0aWVuZSBlbCBhbmNobyBkZSBzdSBww6l0YWxvIHBvciBkZWJham8gZGUgbGEgbWVkaWEsIHkgbG9zIHZpcmdpbmljYSBzaWVtcHJlIGVzdMOhbiBwb3IgZW5jaW1hIGRlIGxhIG1lZGlhLgoKCiMjIyMgRWplcyBkZSBsb3Mgc21hbGwgbXVsdGlwbGVzCgoKUG9kZW1vcyBhanVzdGFyIGxhcyBlc2NhbGFzIGRlIGxvcyBlamVzIHBhcmEgcXVlIHNlYW4gY29tdW5lcyBwYXJhIGNhZGEgc21hbGwgbXVsdGlwbGUgKGxhIG9wY2nDs24gcG9yIGRlZmVjdG8pIG8gZGVqYXIgcXVlIGxhcyBlc2NhbGFzIGRlIGNhZGEgZ3LDoWZpY28gdmFyw61lbiBlbiBmdW5jacOzbiBkZWwgcmFuZ28gZGUgbG9zIGRhdG9zIHJlcHJlc2VudGFkb3M6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTcGVjaWVzKSkgICAgIy0gZXNjYWxhcyBjb211bmVzCnAgKyBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFNwZWNpZXMpLCBzY2FsZXMgPSAiZnJlZSIpICAgIy0gbGFzIGVzY2FsYXMgZGUgY2FkYSBzbWFsbCBwdWVkZW4gdmFyaWFyCnAgKyBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFNwZWNpZXMpLCBzY2FsZXMgPSAiZnJlZV95IikgIy0gc29sbyBkZWphbW9zIGxpYnJlL3ZhcmlhciBsYSBlc2NhbGEgZGVsIGVqZSB5CmBgYAoKClNvbG8gbXVlc3RybyBlbCByZXN1bHRhZG8gZGUgbGEgc2VndW5kYSBleHByZXNpw7NuOgoKCmBgYHtyLCBvdXQud2lkdGggPSAiOTAlIiwgZmlnLmFzcCA9IDQvOSwgZWNobyA9IEZBTFNFfQpwICsgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTcGVjaWVzKSwgc2NhbGVzID0gImZyZWUiKSAgICMtIGxhcyBlc2NhbGFzIGRlIGNhZGEgc21hbGwgcHVlZGVuIHZhcmlhcgpgYGAKCgoqKlVuIHRydXF1aXRvOioqIGVsIGFyZ3VtZW50byBgbWFyZ2luYCBlbiBgZ2dwbG90Mjo6ZmFjZXRfZ3JpZCgpYCBhw7FhZGUgbWFyZ2VuZXMgYSBsb3Mgc21hbGwgbXVsdGlwbGVzIGZhY2lsaXRhbmRvIGxhIHZpc3VhbGl6YWNpw7NuLiBBZGVtw6FzIGHDsWFkZSB1biBudWV2byBzbWFsbCBjb24gdG9kYXMgbGFzIG9ic2VydmFjaW9uZXMuCgoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy5hc3AgPSA3Lzl9CnR3ZWV0cm1kOjp0d2VldF9lbWJlZCgiaHR0cHM6Ly90d2l0dGVyLmNvbS9XZUFyZVJMYWRpZXMvc3RhdHVzLzExMjMxMDkwOTQ0MjI5MjEyMTYiLCB0aGVtZSA9ICJsaWdodCIsIGFsaWduID0gImNlbnRlciIsIG1heHdpZHRoID0gNDAwKQpgYGAKCgoKUHVlZGVzIHByb2JhcmxvIHTDuiBtaXNtbyBjb3JyaWVuZG8gbG8gc2lndWllbnRlOgoKYGBge3IsIG91dC53aWR0aCA9ICI5MCUiLCBmaWcuYXNwID0gNC85fQpwICsgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTcGVjaWVzKSwgIG1hcmdpbnMgPSBUUlVFKSAgIApgYGAKCgpPdHJvIHRydWNvLCBlc3RhIHZleiBhdmFuemFkbzogVW4gW2dpc3RdKGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3BhZHBhZHBhZHBhZC9kYzFmNDUyMGU0Zjk1MzBiMmM3MGZlZDBlNGE0MjVlOSkgcGFyYSBwb25lciBsYWJlbHMgYSBsb3Mgc21hbGxzIG11bHRpcGxlcy4KCjxicj4KCgojIyMgMy40IEFub3RhY2lvbmVzCgo+IEFubm90YXRpb25zIGFyZSBhIHNwZWNpYWwgdHlwZSBvZiBsYXllciB0aGF0IGRvbuKAmXQgaW5oZXJpdCBnbG9iYWwgc2V0dGluZ3MgZnJvbSB0aGUgcGxvdC4gVGhleSBhcmUgdXNlZCB0byBhZGQgZml4ZWQgcmVmZXJlbmNlIGRhdGEgdG8gcGxvdHMuCgpMYXMgYW5vdGFjaW9uZXMgZW4gbG9zIGdyw6FmaWNvcyBwZXJtaXRlbiByZXNhbHRhciBhbGfDum4gZmVuw7NtZW5vIHUgb2JzZXJ2YWNpw7NuICBkZSBpbnRlcsOpcywgeSBzb24gaW1wb3J0YW50ZXMgYSBsYSBob3JhIGRlIGNvbnRhciBoaXN0b3JpYXMgKHN0b3J5dGVsbGluZykgY29uIGxvcyBncsOhZmljb3MgeSB2aXN1YWxpemFjaW9uZXMuIAoKRW4gZWwgZW50b3JubyBgZ2dwbG90YCBwb2RlbW9zIGhhY2VyIGFub3RhY2lvbmVzIGVuIG51ZXN0cm8gZ3LDoWZpY29zIGRlIHZhcmlhcyBtYW5lcmFzLCBwb3IgZWplbXBsbyBjb24gYGFubm90YXRlKClgLiBBdW5xdWUgY29uY2VwdHVhbG1lbnRlLCBjb21vIHNlw7FhbGEgSGFkbGV5LCBsYXMgYW5vdGFjaW9uZXMgc29uIG1ldGFkYXRvcywgZGVzZGUgZWwgcHVudG8gZGUgdmlzdGEgcHLDoWN0aWNvIHNlIHVzYW4gbG9zIG1pc21hcyBmdW5jaW9uZXMgbyBnZW9tcyBwYXJhIG1hbmlwdWxhcmxvcy4gIAoKClRhbWJpw6luIGV4aXN0ZW4gYWxndW5hcyBmdW5jaW9uZXMgYXV4aWxpYXJlcyBlbiBnZ3Bsb3QyIHkgZW4gcGFxdWV0ZXMgZXNwZWPDrWZpY29zIHBhcmEgaGFjZXIgYW5vdGFjaW9uZXMgZW4gZ3LDoWZpY29zIGdncGxvdC4gUG9yIGVqZW1wbG8sIGN1YW5kbyBzZSBoYWNlbiBhbm90YWNpb25lcyBlbiB1biBncsOhZmljbyBkZSBwdW50b3MgZXMgZsOhY2lsIHF1ZSBsYXMgYW5vdGFjaW9uZXMgY2FpZ2FuIHVuYXMgZW5jaW1hIGRlIG90cmFzLCBlbCBwYXF1ZXRlIFtnZ3JlcGVsXShodHRwczovL2J1ZmYubHkvMkhYRXJ4TCkgcGVybWl0ZSBhbGl2aWFyIGVzdGUgcHJvYmxlbWEuCgpVdGlsaWNlbW9zIGxhIGZ1bmNpw7NuIGBhbm5vdGF0ZSgpYC4gUG9yIGVqZW1wbG8sIGVsIHNpZ3VpZW50ZSBjaHVuayBoYWNlIGFsZ3VuYXMgYW5vdGFjaW9uZXMgc2luIG11Y2hvIHNlbnRpZG8gcGVybyBmw6FjaWxlcyBkZSBlbnRlbmRlcjoKCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpwIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgZ2VvbV9wb2ludCgpCmBgYAoKUG9yIGVqZW1wbG8sIGVsIHNpZ3VpZW50ZSBjaHVuayB1c2EgYGFubm90YXRlKClgIHBhcmEgaGFjZXIgYWxndW5hcyBhbm90YWNpb25lcyBzaW4gbXVjaG8gc2VudGlkbywgcGVybyBmw6FjaWxlcyBkZSBlbnRlbmRlcjoKCmBgYHtyfQpwICsgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IDYsIHkgPSAyLCBsYWJlbCA9ICJVbmEgYW5vdGFjacOzbiIsIHNpemUgPSA1KSArCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW4gPSA2LCB4bWF4ID0gNyx5bWluID0gLUluZiwgeW1heCA9IEluZiwgYWxwaGEgPSAwLjIsIGZpbGwgPSAicGluayIpICsgCiAgICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSA1LCB4ZW5kID0gNywgeSA9IDYsIHllbmQgPSA4LCBjb2xvdXIgPSAiYmx1ZSIpIApgYGAKCjxicj4KCiMjIyMgQW5vdGFjaW9uZXMgZGUgdGV4dG8gZW4gbGFzIG9ic2VydmFjaW9uZXMKCkFncmVnYXIgdGV4dG8gYSB1biBncsOhZmljbyBlcyB1bmEgZGUgbGFzIGZvcm1hcyBtw6FzIGNvbXVuZXMgZGUgYW5vdGFjacOzbi4gUG9yIGVqZW1wbG8sIHBhcmEgc2XDsWFsaXphciBlIGlkZW50aWZpY2FyIG9ic2VydmFjaW9uZXMgYW7Ds21hbGFzLiBTaW4gZW1iYXJnbywgY29tbyBzZcOxYWxhIEhhZGxleSwgYcOxYWRpciB0ZXh0byBubyBlcyBmw6FjaWwgcG9yIGxhIGZvcm1hIGVuIGxhIHF1ZSBSIG1hbmVqYSBsYXMgZnVlbnRlcy4KCkxhIGZ1bmNpw7NuIHByaW5jaXBhbCBwYXJhIGVsIGV0aXF1ZXRhZG8gZGUgZ3LDoWZpY29zIGVzIGBnZW9tX3RleHQoKWAuIFBvciBlamVtcGxvOgoKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjYwJSJ9CnAgKyBnZW9tX3RleHQoYWVzKGxhYmVsID0gU3BlY2llcykpCmBgYAoKVGFtYmnDqW4gcG9kw61hbW9zIGhhYmVyIGHDsWFkaWRvIGVsIHZhbG9yIGRlIGxhIGxvbmdpdHVkIGRlbCBww6l0YWxvLiAKCgpgYGB7ciwgZXZhbCA9IFRSVUUsIG91dC53aWR0aCA9ICI2MCUifQpwICsgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IFBldGFsLkxlbmd0aCkpCmBgYAoKCgpIZW1vcyBhw7FhZGlkbyBhIGNhZGEgb2JzZXJ2YWNpw7NuIHVuIHRleHRvLCBwcm92ZW5pZW50ZSBkZSBhbGd1bmEgZGUgbGFzIHZhcmlhYmxlcyBkZSBgaXJpc2AuIEVzdGUgZ3LDoWZpY28gbm8gZXMgbXV5IMO6dGlsLCBwZXJvIGxhIHTDqWNuaWNhIHPDrS4gSW1hZ2luYSBxdWUgcXVlcmVtb3MgbWFyY2FyIGxvcyBsaXJpb3MgNDUgeSAxNDBeW08gbWFyY2FyL2Fub3RhciBsb3MgbGlyaW9zIDIgbGlyaW9zIG3DoXMgZ3JhbmRlcyBvIGVsIGxpcmlvIG1lZGlhbm8uIMK/TG8gaGFjw6lpcz9dLiBQb2RlbW9zIGhhY2VyIGxvIHNpZ3VpZW50ZToKCgpgYGB7ciwgZXZhbCA9IFRSVUUsIG91dC53aWR0aCA9ICI2MCUifQojLSBzZWxlY2Npb25hbW9zIGxvcyBsaXJpb3MgbcOhcyBncmFuZGVzIGRlIGNhZGEgZXNwZWNpZSAKaXJpc19tYXggPC0gaXJpcyAlPiUgZ3JvdXBfYnkoU3BlY2llcykgJT4lIHNsaWNlX21heChQZXRhbC5MZW5ndGgsIG4gPSAxKQoKcCArIAogZ2VvbV90ZXh0KGRhdGEgPSBpcmlzX21heCwgYWVzKGxhYmVsID0gU3BlY2llcyksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMpCmBgYAoKUG9kZW1vcyBhanVzdGFyIGxhIHBvc2ljacOzbiB5IHRhbWHDsW8gZGVsIHRleHRvLCBldGMuLiBQb3IgZWplbXBsbywgcG9kZW1vcyBjYW1iaWFyIGxhIGFsaW5lYWNpw7NuIGRlIGxhcyBhbm90YWNpb25lcyBjb24gY29uIGBoanVzdCjigJxsZWZ04oCdLCDigJxjZW50ZXLigJ0sIOKAnHJpZ2h04oCdLCDigJxpbndhcmTigJ0sIOKAnG91dHdhcmTigJ0pYCB5IGB2anVzdCAo4oCcYm90dG9t4oCdLCDigJxtaWRkbGXigJ0sIOKAnHRvcOKAnSwg4oCcaW53YXJk4oCdLCDigJxvdXR3YXJk4oCdKWAuCgo8YnI+CgoKIyMjIyBMaW5lYXMKClBvZGVtb3MgYcOxYWRpciBsaW5lYXM6IAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDYpCnAgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA1LCBzaXplID0gMS43LCBjb2xvdXIgPSAicHVycGxlIiwgbGluZXR5cGUgPSAiZGFzaGVkIikKcCArIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAuNywgc2xvcGUgPSAwLjQsIHNpemUgPSAxLjksIGNvbG91ciA9ICJzdGVlbGJsdWUiKQpgYGAKCjxicj4KCgojIyMgMy41IENhbWJpYW5kbyBsb3MgbMOtbWl0ZXMgZGUgbG9zIGVqZXMKCiAgU2kgcXVpZXJlcyBtb2RpZmljYXIgZWwgcmVjb3JyaWRvIGRlIGxvcyBlamVzLCBsb3MgImzDrW1pdGVzIiBkZSBsb3MgZWplcywgcHVlZGVzIHVzYXIgYGxpbXMoKWAuIFBhcmEgbG9zIGVqZXMgWCBlIFkgaGF5IGRvcyBmdW5jaW9uZXMgYXV4aWxpYXJlczogYHhsaW0oKWAgZSBgeWxpbSgpYC4gCgoKYGBge3IsIG91dC53aWR0aCA9ICI2MCUifQpwICsgbGltcyhjb2xvciA9IGMoInNldG9zYSIpLCB4ID0gYyhOQSw2KSwgeSA9IGMoMSw4KSkKYGBgCgoKCmBgYHtyLCBvdXQud2lkdGggPSAiNjAlIn0KcCArIHhsaW0oYyAoNCwgNikpICsgeWxpbShjKE5BLCA1KSkgCmBgYAoKU2UgcHVlZGUgaGFzdGEgZGFyIGxhIHZ1ZWx0YSBhIGxvcyBlamVzCgoKYGBge3IsIG91dC53aWR0aCA9ICI2MCUifQpwICsgeGxpbShjICg3LCAzKSkgKyB5bGltKGMoTkEsIDUpKSAKYGBgCgoKTG9zIGzDrW1pdGVzIG8gZG9taW5pbyBkZWwgZ3LDoWZpY28gc3VlbGVuIG9idGVuZXJzZSBhdXRvbcOhdGljYW1lbnRlIGRlIGxvcyBkYXRvcywgcGVybywgb3RyYSB2ZXogYWNjb3JkaW5nIHRvIEhhZGxleSwgaGF5IGRvcyByYXpvbmVzIHBvciBsYXMgcXVlIHBvZGVtb3MgZXN0YXIgaW50ZXJlc2Fkb3MgZW4gY2FtYmlhciBsb3MgbMOtbWl0ZXMgZGVsIGdyw6FmaWNvOiAKCiAgMSkgY2VudHJhcm5vcyBlbiB1bmEgcmVnacOzbiBlc3BlY2lmaWNhIGRlbCBncsOhZmljbyAKICAKICAyKSBhdW1lbnRhciBsb3MgbMOtbWl0ZXMgcGFyYSBxdWUgdmFyaW9zIGdyw6FmaWNvcyBhanVzdGVuIHN1cyBlc2NhbGFzLiAgIAoKPGJyPgoKUG9yIGVqZW1wbG8sIHNpIGRlc3B1w6lzIGRlIGhhY2VyIHVuIGdyw6FmaWNvIHF1aWVyZXMgY2VudHJhcnRlIHPDs2xvIGVuIHVuYSBwYXJ0ZTsgZXMgZGVjaXIsIGhhY2VyIHVuIHpvb20gc29icmUgdW5hIHBhcnRlIGRlbCBncsOhZmljbywgdGVuZW1vcyAyIGFsdGVybmF0aXZhczoKCjEuIEJvcnJhciBsb3MgcHVudG9zIHF1ZSBjYWVuIGZ1ZXJhIGRlIGxvcyBsaW1pdGVzIGRlIGxvIHF1ZSBxdWllcmFzIHF1ZSBzZSB2aXN1YWxpY2UgKHNpIGVuIHVuYSBlc2NhbGEgY29udGludWEgc29sbyBxdWllcmVzIHVzYXIgdW4gbMOtbWl0ZSBwb24gTkEpOgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyB4bGltKGMoNCwgNSkpICsgeWxpbShjKE5BLCA1KSkgIy0gY3VpZGFkbywgc2UgcHVlZGVuIGJvcnJhciBvYnNlcnZhY2lvbmVzCmBgYAoKQ29uIGVzdGUgZW5mb3F1ZSB0aWVuZXMgcXVlIHRlbmVyIGN1aWRhZG8sIHlhIHF1ZSAgc2kgcG9yIGVqZW1wbG8gZGVzcHXDqXMgdXRpbGl6YXIgYWxndW5hIHRyYW5zZm9ybWFjacOzbiBlc3RhZMOtc3RpY2EgY29tbyBwb3IgZWplbXBsbyBgZ2VvbV9zbW9vdGgoKWAsIGxhcyBvYnNlcnZhY2lvbmVzIGVsaW1pbmFkYXMgYWwgYWp1c3RhciBsb3MgbMOtbWl0ZXMgbm8gZW50cmFyw6FuIGVuIGVsIGPDoWxjdWxvIGVzdGFkw61zdGljby4gCgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyBnZW9tX3Ntb290aChjb2xvciA9ICJwdXJwbGUiKQpwICsgZ2VvbV9zbW9vdGgoY29sb3IgPSAicHVycGxlIikgKyB4bGltKGMoNCwgNS43KSkgKyB5bGltKGMoMS41LCA1KSkgICAjIGRlbGV0ZXMgcG9pbnRzCmBgYAoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy5hc3AgPSA1LzksIG91dC53aWR0aCA9ICI5MCUifQpwMSA8LSBwICsgZ2VvbV9zbW9vdGgoY29sb3IgPSAicHVycGxlIikKcDIgPC0gcCArIGdlb21fc21vb3RoKGNvbG9yID0gInB1cnBsZSIpICsgeGxpbShjKDQsIDUuNykpICsgeWxpbShjKDEuNSwgNSkpICAgIyBkZWxldGVzIHBvaW50cwoKCnAxICsgcDIgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKMi4gQ2FtYmlhciBsb3MgbMOtbWl0ZXMgZGUgbG9zIGVqZXMgWCBlIFkgaGFjaWVuZG8gdW4gem9vbSBlbiBsYSByZWdpw7NuIGRlIGludGVyw6lzIHBlcm8gc2luIGVsaW1pbmFyIHB1bnRvcy4gRXN0byBsbyBjb25zZWd1aW1vcyBjb24gYGNvb3JkX2NhcnRlc2lhbigpYC4KCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgKyBnZW9tX3Ntb290aChjb2xvciA9ICJwdXJwbGUiKQpwICsgZ2VvbV9zbW9vdGgoY29sb3IgPSAicHVycGxlIikgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoNCwgNS43KSwgeWxpbSA9IGMoMS41LCA1KSkKYGBgCgoKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYXNwID0gNS85LCBvdXQud2lkdGggPSAiOTAlIn0KcDEgPC0gcCArIGdlb21fc21vb3RoKGNvbG9yID0gInB1cnBsZSIpCnAyIDwtIHAgKyBnZW9tX3Ntb290aChjb2xvciA9ICJwdXJwbGUiKSArIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYyg0LCA1LjcpLCB5bGltID0gYygxLjUsIDUpKQoKCnAxICsgcDIgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKPGJyPgoKCiMjIyAzLjYgRXNjYWxhcwoKPiBTY2FsZXMgY29udHJvbCB0aGUgZGV0YWlscyBvZiBob3cgZGF0YSB2YWx1ZXMgYXJlIHRyYW5zbGF0ZWQgdG8gdmlzdWFsIHByb3BlcnRpZXMuIE92ZXJyaWRlIHRoZSBkZWZhdWx0IHNjYWxlcyB0byB0d2VhayBkZXRhaWxzIGxpa2UgdGhlIGF4aXMgbGFiZWxzIG9yIGxlZ2VuZCBrZXlzLCBvciB0byB1c2UgYSBjb21wbGV0ZWx5IGRpZmZlcmVudCB0cmFuc2xhdGlvbiBmcm9tIGRhdGEgdG8gYWVzdGhldGljLiBsYWJzKCkgYW5kIGxpbXMoKSBhcmUgY29udmVuaWVudCBoZWxwZXJzIGZvciB0aGUgbW9zdCBjb21tb24gYWRqdXN0bWVudHMgdG8gdGhlIGxhYmVscyBhbmQgbGltaXRzLgoKTGFzIGVzY2FsYXMgcGVybWl0ZW4gbGVlci9pbnRlcnByZXRhciB1biBncsOhZmljbzsgcGVybWl0ZW4gaW50ZXJwcmV0YXIgbG9zIGVsZW1lbnRvcyBnZW9tw6l0cmljb3MgKHBvciBlamVtcGxvIGVuIG51ZXN0cm8gZ3LDoWZpY28sIGxvcyBwdW50b3MpIGVuIGZ1bmNpw7NuIGRlIGxvcyB2YWxvcmVzIG9yaWdpbmFsZXMgZGUgbGFzIG9ic2VydmFjaW9uZXMuIExhcyBlc2NhbGFzIHNvbiB1biBlbGVtZW50byBtw6FzIGRlIGxvcyBncsOhZmljb3MgZ2dwbG90IHkgc2UgcHJvZHVjZW4vY29udHJvbGFuIGNvbiBsYSBmYW1pbGlhIGRlIGZ1bmNpb25lcyBgc2NhbGVfeHgoKWAKCkVuIGBnZ3Bsb3QyYCBsYXMgZXNjYWxhcyBvIGd1w61hcyBzZSBwcm9kdWNlbiBhdXRvbcOhdGljYW1lbnRlLCBubyB2ZW1vcyBxdWUgaGFnYW1vcyBuYWRhLCBwZXJvIHVuZGVyIHRoZSBob29kIHNlIGVzdMOhbiBmaWphbmRvIGNvbiBsYSBmYW1pbGlhIGRlIGZ1bmNpb25lcyBgc2NhbGVfeHgoKWAgcXVlIHNvbiBsYXMgcXVlIGNvbnRyb2xhbiBjb21vIHNlIG1hcGVhbiBsb3MgdmFsb3JlcyBkZSBsYXMgdmFyaWFibGVzIGNvbiBsYXMgcHJvcGllZGFkZXMgZXN0w6l0aWNhcyBkZSBudWVzdHJvIGdyw6FmaWNvIChwb3IgZWplbXBsbyBlbCBlamUgWCkgZGUgZm9ybWEgcXVlIHBvZGFtb3MgaW50ZXJwcmV0YXIgbGEgcG9zaWNpw7NuIGRlIGxvcyBkaXN0aW50b3MgcHVudG9zIG1pcmFuZG8gbGFzIGVzY2FsYXMuIExhcyBlc2NhbGFzIHRhbWJpw6luIGNvbnN0cnV5ZW4gbG9zIGVsZW1lbnRvcyBxdWUgcGVybWl0ZW4gbGVlci9pbnRlcnByZXRhciBsb3MgZ3LDoWZpY29zOiBsb3MgZWplcyB5IGxhcyBsZXllbmRhcy4KCkNvbW8gYGdncGxvdDJgIGhhY2UgZWwgbWFwZW8geSBnZW5lcmEgbGFzIGVzY2FsYXMgeSBsZXllbmRhcyBhdXRvbcOhdGljYW1lbnRlLCBlbiBsYSBwcsOhY3RpY2EgcG9kZW1vcyBoYWNlciBncsOhZmljb3Mgc2luIHNhYmVyIGNvbW8gZnVuY2lvbmFuIHksIHBvciB0YW50bywgc2luIHNhYmVyIG1hbmlwdWxhciBlc3RlIGVsZW1lbnRvIGRlIHVuIGdyw6FmaWNvIGdncGxvdC4gUGVybyBzaSBhcHJlbmRlbW9zIGEgbWFuaXB1bGFyIGxhcyBlc2NhbGFzLCBlc3RvIG5vcyBkYXLDoSBtw6FzIGZsZXhpYmlsaWRhZCBhIGxhIGhvcmEgZGUgdXRpbGl6YXIgYGdncGxvdDJgLgoKRW4gbXVjaG9zIHRpcG9zIGRlIGRhdG9zIGVzIGltcG9ydGFudGUgcGFyYXJzZSBhIHBlbnNhciBjdWFsIGVzIGxhIG1lam9yIGVzY2FsYSBwYXJhIHJlcHJlc2VudGFyIGxhcyB2YXJpYWJsZXMuIFF1aXrDoXMgc2VhIGNvbnZlbmllbnRlIGNhbWJpYXIgbGEgZXNjYWxhIGRlIHVuIGVqZSBwYXJhIGRpc3RyaWJ1aXIgbWVqb3IgbGFzIG9ic2VydmFjaW9uZXMgZW4gZWwgZXNwYWNpbywgbyBwYXJhIGludGVycHJldGFyIG1lam9yIGxhcyB2YXJpYWNpb25lcyBlbnRyZSBvYnNlcnZhY2lvbmVzOyBwb3IgZWplbXBsbyBsYSBlc2NhbGEgbG9nYXLDrXRtaWNhIG8gZW4gcG9yY2VudGFqZXMgc29uIGEgdmVjZXMgbcOhcyBhcHJvcGlhZGFzIHF1ZSBsYXMgZXNjYWxhcyBvcmlnaW5hbGVzLgoKCkVuIHJlYWxpZGFkLCBwYXJhIGNhZGEgcGFyIHZhcmlhYmxlL2VzdMOpdGljYSByZXByZXNlbnRhZGEgZW4gdW4gZ3LDoWZpY28gZ2dwbG90IGVzIG5lY2VzYXJpYSB1bmEgZXNjYWxhLCB5IHRlbmRyw61hIHF1ZSBmaWphcnNlIGNvbiB1bmEgZGUgbGFzIGZ1bmNpb25lcyBkZSBsYSBmYW1pbGlhIGBzY2FsZV94eCgpYC4gUmVhbG1lbnRlIGN1YW5kbyBoYWNlbW9zIGVzdGUgZ3LDoWZpY28sIGVuIGVsIHF1ZSBhc29jaWFtb3MgMyB2YXJpYWJsZXMgYSAzIHByb3BpZWRhZGVzIGVzdMOpdGljYXMgY29uIGBhZXMoKWAsIHNlIG5lY2VzaXRhcsOtYSBlc3BlY2lmaWNhciBsYXMgZXNjYWxhcyBkZSBsYXMgMyB2YXJpYWJsZXM6CgoKYGBge3J9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgIGdlb21fcG9pbnQoYWVzIChjb2xvciA9IFNwZWNpZXMpKSAKYGBgCgpCaWVuLCBwZXJvIGVudG9uY2VzIMK/cG9yIHF1w6kgbm8gbG8gaGFjZW1vcz8sIMK/cG9yIHF1w6kgbm8gZXNwZWNpZmljYW1vcyBsYXMgZXNjYWxhcz8gUHVlcyBwb3JxdWUgbG8gaGFjZSBgZ2dwbG90MmAgcG9yIG5vc290cm9zLiBFbiByZWFsaWRhZCBjdWFuZG8gZWplY3V0YW1vcyBsYSBleHByZXNpw7NuIGFudGVyaW9yLCByZWFsbWVudGUgc2UgZXN0w6EgaGFjaWVuZG8gbG8gc2lndWllbnRlOgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArICAgZ2VvbV9wb2ludChhZXMgKGNvbG9yID0gU3BlY2llcykpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoKSArIAogIHNjYWxlX3lfY29udGludW91cygpICsgCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoKQpgYGAKCkVzIGRlY2lyLCBgZ2dwbG90MmAgYXNpZ25hIGEgY2FkYSB2YXJpYWJsZSB1bmEgZXNjYWxhLCBhIGxhcyB2YXJpYWJsZXMgY29udGludWFzIGxlcyBhc2lnbmEgdW5hIGVzY2FsYSBjb250aW51YSB5IGEgbGFzIGNhdGVnw7NyaWNhcyB1bmEgZXNjYWxhIGRpc2NyZXRhLiBQRVJPLCBzaSBxdWVyZW1vcywgc2kgbG8gY29uc2lkZXJhbW9zIGFwcm9waWFkbyBwb2Ryw61hbW9zIGNhbWJpYXIgbGEgZXNjYWxhLiBQb3IgZWplbXBsbzoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV9jb2xvdXJfZ3JleSgpCnAgKyBzY2FsZV94X3NxcnQoKSArIHNjYWxlX3lfbG9nMTAoKQpwICsgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZyIpCnAgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpCmBgYAoKU29sbyByZXByZXNlbnRvIGxhIHByaW1lcmEgdHJhbnNmb3JtYWNpw7NuLCBlbiBsYSBxdWUgc2UgZGEgbGEgdnVlbHRhIGEgbGEgZXNjYWxhIGRlbCBlamUgWSB5IGVsIGNvbG9yLCBhc29jaWFkbyBhIGxhIHZhcmlhYmxlIFNwZWNpZXMsIHBhc2EgYSBlc2NhbGEgZGUgZ3Jpc2VzOgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmFzcCA9IDUvOSwgb3V0LndpZHRoID0gIjYwJSJ9CnAgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX2NvbG91cl9ncmV5KCkKYGBgCgo8YnI+CgojIyMjIExhYmVscyBkZSBsb3MgZWplcwoKVGFtYmnDqW4gc2UgcHVlZGVuIG1vZGlmaWNhciBsbyBxdWUgdmVtb3MgZW4gbG9zIGVzY2FsYXMuIEFudGVzIHZhbW9zIGEgbW9kaWZpY2FyIGxvcyB0w610dWxvcyBkZSBsb3MgZWplcyB5IGxleWVuZGFzLgoKCmBgYHtyLCBldmFsID0gVFJVRX0KcCA8LSBwICsgbGFicyh4ID0gIkVqZSBYIiwgeSA9ICJFamUgWSIsIGNvbG9yID0gIkxleWVuZGFcbiBwYXJhIGVsIGNvbG9yIikKYGBgCgoKRGUgbW9tZW50byBsYSBlc2NhbGEgZGVsIGVqZSBYIHZhcmlhIGFwcm94aW1hZGFtZW50ZSBkZSAzIGEgOC4gTG9zIHTDrXR1bG9zIGRlIGxhIGVzY2FsYSBkZWwgZWplIFggc8OzbG8gbXVlc3RyYSBsb3MgdmFsb3JlcyA1LCA2LCA3IHkgOC4gVmFtb3MgYSBtb2RpZmljYXIgImVsIHRleHRvIiwgbG9zIG7Dum1lcm9zIHF1ZSBzZSB2ZW4geSBxdWUgc2lydmVuIGRlIGd1w61hIHBhcmEgbGEgZXNjYWxhIGRlbCBlamUgWDoKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjQ1JSJ9CnAgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDMsIDEwLCAwLjUpLCBsaW1pdHMgPSBjKDMsIDEwKSkgCmBgYAoKCkVuIGVsIGVqZSBZIHNlIHZlbiBsb3MgbsO6bWVyb3MgMiwgNCB5IDYuIENhbWJpZW1vcyBsb3MgbGFiZWxzIGRlIHN1IGVzY2FsYSAobm8gZGVsIGVqZSBZLCBzaW5vIGRlIGxhIGVzY2FsYSBkZWwgZWplIFkpOgoKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjQ1JSJ9CnAgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEyLCAwLjI1KSwgIGxhYmVsID0gc2NhbGVzOjpkb2xsYXIpIApgYGAKCgoKVGFtYmnDqW4gc2UgcHVlZGVuIG1vZGlmaWNhciBsb3MgZGUgbGEgbGV5ZW5kYSBwYXJhIGVsIGNvbG9yOgoKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjQ1JSJ9CnAgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicHVycGxlIiwgInBpbmsiLCAicmVkMiIpLCBuYW1lID0gIkVzcGVjaWVzXG4gZGUgbGlyaW9zIikKYGBgCgpTaSBodWJpZXNlIHVzYWRvIHVuYSB2YXJpYWJsZSBkaXNjcmV0YSBhc29jaWFkYSAgYSBsYSBlc3TDqXRpY2EgInNoYXBlIiwgdGVuZHLDrWFtb3MgcXVlIHVzYXIgYHNjYWxlX3NoYXBlX2Rpc2NyZXRlKClgLCBzaSBsYSB2YXJpYWJsZSBhc29jaWFkYSBhIHNoYXBlIGZ1ZXNlIGNvbnRpbnVhOiBgc2NhbGVfc2hhcGVfY29udGludW91cygpYAoKwr9ZIHNpIGh1YmnDqXNlbW9zIHVzYWRvIHVuYSB2YXJpYWJsZSBjb250aW51YSBwYXJhIGxhIGVzdMOpdGljYSAiZmlsbCI/IFB1ZXMgYHNjYWxlX2ZpbGxfY29udGludW91cygpYAoKPGJyPgoKIyMjIyBGYW1pbGlhIGRlIGZ1bmNpb25lcyBkZSBlc2NhbGEKCiAg4oCiIHNjYWxlXypfY29udGludW91cygpICAKICDigKIgc2NhbGVfKl9kaXNjcmV0ZSgpICAKICDigKIgc2NhbGVfKl9vcmRpbmFsKCkgIAogIOKAoiBzY2FsZV8qX21hbnVhbCgpICAKICDigKIgc2NhbGVfe2NvbG9yL2ZpbGx9X2JyZXdlcigpICAKICDigKIgc2NhbGVfe2NvbG9yL2ZpbGx9X2Rpc3RpbGxlcigpICAKICDigKIgc2NhbGVfe2NvbG9yL2ZpbGx9X2dyYWRpZW50KCkgIAoKCjxicj4KCiMjIyAzLjcgU3RhdHMgKHRyYW5zZm9ybWFjaW9uZXMgZXN0YWTDrXN0aWNhcykKCgpQdWVkZSBsZWVyc2UgZW4gbGEgd2ViIGRlIGBnZ3Bsb3QyYCwgY29uY3JldGFtZW50ZSBbYXF1w61dKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8pIGxvIHNpZ3VpZW50ZToKCj4gQSBsYXllciBjb21iaW5lcyBkYXRhLCBhZXN0aGV0aWMgbWFwcGluZywgYSBnZW9tIChnZW9tZXRyaWMgb2JqZWN0KSwgYSBzdGF0IChzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbiksIGFuZCBhIHBvc2l0aW9uIGFkanVzdG1lbnQuIFR5cGljYWxseSwgeW91IHdpbGwgY3JlYXRlIGxheWVycyB1c2luZyBhIGdlb21fIGZ1bmN0aW9uIAoKUEVSTwoKPiBBIGhhbmRmdWwgb2YgbGF5ZXJzIGFyZSBtb3JlIGVhc2lseSBzcGVjaWZpZWQgd2l0aCBhIHN0YXRfIGZ1bmN0aW9uLCBkcmF3aW5nIGF0dGVudGlvbiB0byB0aGUgc3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb24gcmF0aGVyIHRoYW4gdGhlIHZpc3VhbCBhcHBlYXJhbmNlLiBUaGUgY29tcHV0ZWQgdmFyaWFibGVzIGNhbiBiZSBtYXBwZWQgdXNpbmcgc3RhdCgpLgoKQWxndW5vcyBncsOhZmljb3MsIGNvbW8gbG9zIGdyw6FmaWNvcyBkZSBwdW50b3MsIG5vIHJlcXVpZXJlbiBkZWwgdXNvIGRlIHRyYW5zZm9ybWFjaW9uZXMgZXN0YWTDrXN0aWNhcyBkZSBsYXMgb2JzZXJ2YWNpb25lcywgcGVybyBvdHJvcyBncsOhZmljb3MgY29tbyByZWN0YXMgbyBjdXJ2YXMgZGUgcHJlZGljY2nDs24gbyBjb21vIGxvcyBib3hwbG90cyBvIGRpYWdyYW1hcyBkZSBjYWphLCBzw60gcXVlIGxhcyBuZWNlc2l0YW4gXltQb3IgZWplbXBsbywgcGllbnNhIHF1ZSBsbyBxdWUgc2UgdmlzdWFsaXphIGNvbiBgZ2VvbV9zbW90aCgpYCBubyBzb24gbG9zIGRhdG9zIG9yaWdpbmFsZXMgc2lubywgcG9yIGVqZW1wbG8sIGxhIGxpbmVhIGRlIHJlZ3Jlc2nDs24gc2kgdXNhbW9zIGBnZW9tX3Ntb3RoKG1ldGhvZCA9ICJsbSIpYF0uIFBvZGVtb3MgdXNhciB0cmFuc2Zvcm1hY2lvbmVzIGVzdGFkw61zdGljYXMgZW4gZ3LDoWZpY29zIGdncGxvdCBjb24gbGEgZmFtaWxpYSBkZSBmdW5jaW9uZXMgKipgc3RhdF94eCgpYCoqCgpQb3IgZWplbXBsbywgY3VhbmRvIHNlIGhhY2UgdW4gZGlhZ3JhbWEgZGUgY2FqYSBvIGJveHBsb3QsIG5vIHNlIHJlcHJlc2VudGFuIGxhcyBvYnNlcnZhY2lvbmVzIG9yaWdpbmFsZXMsIHNpbm8gcXVlIHNlIG11ZXN0cmFuIDUgZXN0YWTDrXN0aWNvcyByZXN1bWVuIGRlIGxhIGRpc3RyaWJ1Y2nDs24gZGUgbG9zIGRhdG9zOyBlcyBkZWNpciwgc2UgdXRpbGl6YSB1bmEgdHJhbnNmb3JtYWNpw7NuIGVzdGFkw61zdGljYS4gQ3VhbmRvIHVzw6FiYW1vcyBgZ2VvbV9zbW90aCgpYCB0YW1wb2NvIHJlcHJlc2VudMOhYmFtb3MgY29uIMOpbCBsb3MgZGF0b3Mgb3JpZ2luYWxlcywgc2lubyB1bmEgdHJhbnNmb3JtYWNpw7NuIGVzdGFkw61zdGljYSBkZSBlc3Rvcy4gQ29uY3JldGFtZW50ZSBsYSB0cmFuc2Zvcm1hY2nDs24gZXN0YWTDrXN0aWNhIHF1ZSB1dGlsaXphIGBnZW9tX3Ntb3RoKClgIGVzIGdlbsOpcmljYW1lbnRlIHVuICJzbW9vdGhlciIsIGNhbGN1bGEgbWVkaWFudGUgdW5hIHJvbGxpbmctd2luZG93cyBsYSBtZWRpYSBkZSB5LCBjb25kaWNpb25hZGEgYSB4LiAgCgpDYWRhIGZ1bmNpw7NuIGBnZW9tX3h4KClgIHF1ZSB1dGlsaWNlbW9zLCBlbiByZWFsaWRhZCBuZWNlc2l0YSBkZSB1biBzdGF0X3h4KCksIGVudG9uY2VzIMK/cG9yIHF1w6kgbnVuY2EgbG8gaGVtb3MgdXNhZG8vZXNwZWNpZmljYWRvIG5vc290cm9zPyBDb24gYGdncGxvdDJgIGxhIHJhesOzbiBlcyBjYXNpIHNpZW1wcmUgbGEgbWlzbWE6IHBvcnF1ZSB1bmRlciB0aGUgaG9vZCBgZ2dwbG90MmAgaGFjZSBtdWNoYXMgY29zYXMgcG9yIG5vc290cm9zLiBFbiBjb25jcmV0bywgY2FkYSB2ZXogcXVlIHVzYW1vcyB1biBgZ2VvbV94eCgpYCBlbiByZWFsaWRhZCBgZ2dwbG90MmAgZXN0w6EgZmlqYW5kbyB1bmEgdHJhbnNmb3JtYWNpw7NuIGVzdGFkw61zdGljYSBwb3IgZGVmZWN0byBwb3Igbm9zb3Ryb3MuIEdncGxvdCBlcyB1biBzaXN0ZW1hIG11eSBjb21wbGV0bywgcGVybyBhw7puIGFzw60sIHVuYSB2ZXogbG8gZW50aWVuZGVzLCBoYWNlciBncsOhZmljb3MgY29uIGVsIGVzIHJlbGF0aXZhbWVudGUgZsOhY2lsIHkgcsOhcGlkbyBwb3JxdWUgbXVjaGFzIGRlIHN1cyBvcGNpb25lcyBvcGNpb25lcyBubyBoYWNlIGZhbHRhIGVzcGVjaWZpY2FybGFzIGRpcmVjdGFtZW50ZS4KCgpQb3IgZWplbXBsbywgwr9jdWFsIGVzIGxhIHRyYW5zZm9ybWFjacOzbiBlc3RhZMOtc3RpY2EgcXVlIHNlIHVzYSBwb3IgZGVmZWN0byBlbiBnZW9tX3BvaW50KCk/IE5pbmd1bmEsIGJ1ZW5vLCBlbiByZWFsaWRhZCB1c2EgIGBzdGF0ID0gImlkZW50aXR5ImAsIHBlcm8gY29tbyBsbyBlc3BlY2lmaWNhIGdncGxvdDIgYXV0b23DoXRpY2FtZW50ZSwgbm9zb3Ryb3Mgbm8gbm9zIGRhbW9zIGN1ZW50YS4KCkN1YW5kbyBoYWPDrWFtb3MgZXN0ZSBncsOhZmljbzoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoUGV0YWwuTGVuZ3RoLCBTZXBhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSAKYGBgCgpFbiByZWFsaWRhZCBlc3TDoWJhbW9zIGhhY2llbmRvCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhQZXRhbC5MZW5ndGgsIFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludChzdGF0ID0gImlkZW50aXR5IikKYGBgCgrCv1F1w6kgdHJhbnNmb3JtYWNpb25lcyBlc3RhZMOtc3RpY2FzIHBvZGVtb3MgaGFjZXIgY3VhbmRvIHVzZW1vcyBgZ2VvbV9wb2ludCgpYD8gUG9yIGVqZW1wbG8sIGBnZW9tX3BvaW50KHN0YXQgPSAidW5pcXVlIilgIHPDs2xvIHJlcHJlc2VudGFyw61hIGxhcyBvYnNlcnZhY2lvbmVzIMO6bmljYXMgbyBOTyByZXBldGlkYXMuIEVuIGVzdGUgY2FzbyBjcmVvIHF1ZSBpcmlzIG5vIHRpZW5lIG9ic2VydmFjaW9uZXMgcmVwZXRpZGFzLCBhc8OtIHNpIGVqZWN1dMOhaXMgbGEgaW5zdHJ1Y2Npw7NuIGRlIGFiYWpvIHNlIHNlZ3VpcsOhbiB2aXN1YWxpemFuZG8gbG9zIDE1MCBsaXJpb3MuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFBldGFsLkxlbmd0aCwgU2VwYWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KHN0YXQgPSAidW5pcXVlIikgIy0gZGVqYXLDrWEgc29sbyBvYnNlcnZhY2lvbmVzIG5vIHJlcGV0aWRhcwpgYGAKCgpQb2RlbW9zIGNvbnN1bHRhciBsYXMgb3BjaW9uZXMgcG9yIGRlZmVjdG8gY29tcGxldGFzIGRlIGBnZW9tX3BvaW50KClgIFthcXXDrV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fcG9pbnQuaHRtbCkKCgoKQ2FkYSBgZ2VvbV94eCgpYCB0aWVuZSB1biBkZWZhdWx0IHN0YXRpc3RpYywgcGVybyBwb2RlbW9zIGNhbWJpYXJsbyB5IGVzcGVjaWZpY2FyIG90cmEgc3RhdCBwYXJhIGFkYXB0YXJsbyBhIG51ZXN0cmFzIG5lY2VzaWRhZGVzLiBQb3IgZWplbXBsbywgdGhlIGRlZmF1bHQgc3RhdGlzdGljIGZvciBgZ2VvbV9iYXIoKWAgaXMgYHN0YXRfYmluKClgIHBlcm8gcG9kZW1vcyB1c2FyIG90cmFzIHN0YXRfeHguIE90cmEgdmV6IHBhcmVjZSB1biB0cmFiYWxlbmd1YXMsIHBlcm8gY3VhbmRvIGxvIGVudGllbmRlcyBlcyByZWxhdGl2YW1lbnRlIHNlbmNpbGxvLiAKClBvciBlamVtcGxvLCBlbiBudWVzdHJvIGdyw6FmaWNvIGRlIHB1bnRvcywgcG9kZW1vcyB1c2FyIG90cmFzIHRyYW5zZm9ybWFjaW9uZXMgZXN0YWTDrXN0aWNhcywgdW5hIGRlIGxhcyBxdWUgbcOhcyBzZW50aWRvIHRpZW5lIGVzIGNhbGN1bGFyIG1lZGlhcyBtw7N2aWxlcyBjb24gdW4gbcOpdG9kbyBkZSBhbGlzYWRvIChzbW9vdGhlcikKCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFBldGFsLkxlbmd0aCwgU2VwYWwuTGVuZ3RoLCBjb2xvciA9IFNwZWNpZXMpKSAKCnAgKyBnZW9tX3BvaW50KHN0YXQgPSAiaWRlbnRpdHkiKQoKcCArIGdlb21fcG9pbnQoc3RhdCA9ICJzbW9vdGgiLCBtZXRob2QgPSAiYXV0byIpCmBgYAoKCkF1bnF1ZSBlbiBlc3RlIGNhc28gZXMgYmFzdGFudGUgbcOhcyBmw6FjaWwgaGFjZXJsbyBjb246CgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkKcCArIGdlb21fcG9pbnQoKSArIHN0YXRfc21vb3RoKCkKCnAgKyBnZW9tX3BvaW50KCkgKyBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCAgIHNlID0gRkFMU0UsICBzaXplID0gMSkKcCArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsICAgc2UgPSBGQUxTRSwgIHNpemUgPSAxKQoKcCArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbCA9ICIjQzQyMTI2IiwgIHNlID0gRkFMU0UsICBzaXplID0gMSkKYGBgCgo8YnI+CgpBbGd1bmFzIHRyYW5zZm9ybWFjaW9uZXMgZXN0YWTDrXN0aWNhcyDDunRpbGVzIHkgZW4gcXXDqSBnZW9tcyBlc3TDoW4gZGlzcG9uaWJsZXM6CgogICAgLSBzdGF0X2JpbigpOiBnZW9tX2JhcigpLCBnZW9tX2ZyZXFwb2x5KCksIGdlb21faGlzdG9ncmFtKCkgIAogICAgLSBzdGF0X2JpbjJkKCk6IGdlb21fYmluMmQoKSAgCiAgICAtIHN0YXRfYmluZG90KCk6IGdlb21fZG90cGxvdCgpICAKICAgIC0gc3RhdF9iaW5oZXgoKTogZ2VvbV9oZXgoKSAgIAogICAgLSBzdGF0X2JveHBsb3QoKTogZ2VvbV9ib3hwbG90KCkgICAgCiAgICAtIHN0YXRfY29udG91cigpOiBnZW9tX2NvbnRvdXIoKSAgCiAgICAtIHN0YXRfcXVhbnRpbGUoKTogZ2VvbV9xdWFudGlsZSgpICAgCiAgICAtIHN0YXRfc21vb3RoKCk6IGdlb21fc21vb3RoKCkgIAogICAgLSBzdGF0X3N1bSgpOiBnZW9tX2NvdW50KCkgICAKCkVzIHJhcm8gcXVlIHRlbmdhbW9zIHF1ZSB1c2FyIGVzdGFzIGZ1bmNpb25lcyBgc3RhdHNfeHgoKWAgZGlyZWN0YW1lbnRlLCBwZXJvIHNpIHF1aWVyZXMgdmVyIHF1ZSBoYWNlbiBleGFjdGFtZW50ZSBjb252aWVuZSBjb25zdWx0YXIgbGEgZG9jdW1lbnRhY2nDs24gcGFyYSB2ZXIgcXXDqSBoYWNlIHkgY8OzbW8gc2UgYXBsaWNhIGV4YWN0YW1lbnRlIGNhZGEgdHJhbnNmb3JtYWNpw7NuIGVzdGFkw61zdGljYSBhIGxvcyBkYXRvcy4KCkhheSBvdHJhcyBmdW5jaW9uZXMgYHN0YXRfeHgoKWAgcXVlIG5vIHNlIHB1ZWRlbiB1dGlsaXphciBjb24gbGFzIGZ1bmNpb25lcyBgZ2VvbV94eCgpYDoKCiAgICAtIHN0YXRfZWNkZigpOiBjb21wdXRlIGEgZW1waXJpY2FsIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIHBsb3QuICAKICAgIC0gc3RhdF9mdW5jdGlvbigpOiBjb21wdXRlIHkgdmFsdWVzIGZyb20gYSBmdW5jdGlvbiBvZiB4IHZhbHVlcy4gIAogICAgLSBzdGF0X3N1bW1hcnkoKTogc3VtbWFyaXNlIHkgdmFsdWVzIGF0IGRpc3RpbmN0IHggdmFsdWVzLiAgCiAgICAtIHN0YXRfc3VtbWFyeTJkKCksIHN0YXRfc3VtbWFyeV9oZXgoKTogc3VtbWFyaXNlIGJpbm5lZCB2YWx1ZXMuICAKICAgIC0gc3RhdF9xcSgpOiBwZXJmb3JtIGNhbGN1bGF0aW9ucyBmb3IgYSBxdWFudGlsZS1xdWFudGlsZSBwbG90LiAgCiAgICAtIHN0YXRfc3Bva2UoKTogY29udmVydCBhbmdsZSBhbmQgcmFkaXVzIHRvIHBvc2l0aW9uLiAgCiAgICAtIHN0YXRfdW5pcXVlKCk6IHJlbW92ZSBkdXBsaWNhdGVkIHJvd3MuICAKCjxicj4KCiMjIyMgQWxndW5vcyBlamVtcGxvcwoKVmVhbW9zIGFsZ3Vub3MgZWplbXBsb3Mgw7p0aWxlczoKCjEuIEVuIHVuIGRpYWdyYW1hIGRlIGNhamEgbW9zdHJhciBsYSBtZWRpYToKIApRdWVyZW1vcyBxdWUgbGEgbWVkaWEgc2UgcmVwcmVzZW50ZSBjb21vIHVuIHB1bnRvLCBlbnRvbmNlcyB1c2Ftb3MgYGdlb21fcG9pbnQoKWAgcGVybyBubyBxdWVyZW1vcyByZXByZXNlbnRhciBsb3MgdmFsb3JlcyBvcmlnaW5hbGVzLCBzaW5vIGxhIG1lZGlhLCBhc8OtIHF1ZSBkZW50cm8gZGUgZ2VvbSB1c2Ftb3MgbGEgb3BjacOzbiBgc3RhdCA9IHN1bW1hcnlgCiAKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU3BlY2llcywgU2VwYWwuTGVuZ3RoKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGdlb21fcG9pbnQoc3RhdCA9ICJzdW1tYXJ5IiwgZnVuLnkgPSAibWVhbiIsIGNvbG91ciA9ICJyZWQiLCBzaXplID0gNCkKYGBgCgpTZSBjb25zZWd1aXLDrWEgbG9zIG1pc21vIHVzYW5kbyBkaXJlY3RhbWVudGUgbGEgIGZ1bmNpw7NuIGBzdGF0X3N1bW1hcnkoKWAgY29uIGxhIG9wY2nDs24gZ2VvbSA9ICJwb2ludCIuIFPDrSwgZW4gYGdncGxvdDJgIGxhcyBjb3NhcyBzZSBwdWVkZW4gaGFjZXIgZGUgdmFyaWFzIG1hbmVyYXMhIQoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoU3BlY2llcywgU2VwYWwuTGVuZ3RoKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIHN0YXRfc3VtbWFyeShnZW9tID0gInBvaW50IiwgZnVuLnkgPSAibWVhbiIsIGNvbG91ciA9ICJyZWQiLCBzaXplID0gNCkKYGBgCgoyLiAqKk90cm8gZWplbXBsbzoqKiBlbCBkZWZhdWx0IHN0YXQgZGUgZ2VvbV9oaXN0b2dyYW0gZXMgc3RhdCA9ICJiaW4iLCBtb3N0cmFuZG9ub3MgZWwgbsO6bWVybyBkZSBvYnNlcnZhY2lvbmVzIGVuIGNhZGEgYmluLiBTaSBxdWVyZW1vcyBxdWUgbm9zIG11ZXN0cmUgZnJlY3VlbmNpYXMgcmVsYXTDrXZhcyBhbCBncnVwbyBvIGJpbiBtw6FzIG51bWVyb3NvOiAKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkgKyBnZW9tX2hpc3RvZ3JhbSgpIAoKZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgpKSArIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gc3RhdChjb3VudCAvIG1heChjb3VudCkpKSkKYGBgCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiOTAlIiwgIGZpZy5hc3AgPSA1Lzl9CnAxIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkgKyBnZW9tX2hpc3RvZ3JhbSgpIApwMiA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBzdGF0KGNvdW50IC8gbWF4KGNvdW50KSkpKQpwMSArIHAyICArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCgoKPGJyPgoKKipCb251czoqKiBDb24gYHN0YXRfZnVuY3Rpb24oKWAgcG9kZW1vcyBkaWJ1amFyIGN1cnZhcyBkZSBkZW5zaWRhZDoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmRmIDwtIHRpYmJsZSh4ID0gYygtMjAsIDIwKSkKICAKZ2dwbG90KGRmLCBhZXMoeCA9IHgpKSArCnN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW4gPSAwLCBzZCA9IDUpLCBjb2xvciA9ICJibGFjayIpICsKc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IDAsIHNkID0gMSksIGNvbG9yID0gInJlZCIpICsKc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IDAsIHNkID0gMyksIGNvbG9yID0gImJsdWUiKQpgYGAKCgo8YnI+CgojIyMgMy44IFBvc2l0aW9uIGFkanVzdG1lbnRzCgoKPiBBbGwgbGF5ZXJzIGhhdmUgYSBwb3NpdGlvbiBhZGp1c3RtZW50IHRoYXQgcmVzb2x2ZXMgb3ZlcmxhcHBpbmcgZ2VvbXMuIE92ZXJyaWRlIHRoZSBkZWZhdWx0IGJ5IHVzaW5nIHRoZSBwb3NpdGlvbiBhcmd1bWVudCB0byB0aGUgZ2VvbV8gb3Igc3RhdF8gZnVuY3Rpb24uCgoKTG9zIGFqdXN0ZXMgZGUgcG9zaWNpw7NuIGFmZWN0YW4gYSBsYSBwb3NpY2nDs24gZGUgbG9zIGVsZW1lbnRvcyBkZSB1bmEgY2FwYS4gTG9zIGdyw6FmaWNvcyBlbiBsb3MgcXVlIG3DoXMgc2UgdXRpbGl6YW4gbG9zIGFqdXN0ZXMgZGUgcG9zaWNpw7NuIHNvbiBsb3MgZ3LDoWZpY29zIGRlIGJhcnJhcy4gU3UgcG9zaWNpw7NuIHBvciBkZWZlY3RvIGVzIHBvc2l0aW9uID0gInN0YWNrIi4gU2UgcHVlZGVuIGNhbWJpYXIgY29uIGVsIGFyZ3VtZW50byBnZW9tX2Jhcihwb3NpdGlvbiA9ICJ4eHh4IilgICwgYXVucXVlIHNpIHVzYXMgbGFzIGZ1bmNpb25lcyBgcG9zaXRpb25feHgoKWAgdGllbmVzIG3DoXMgZmxleGliaWxpZGFkOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMgLCBhZXMoU3BlY2llcykpICsgZ2VvbV9iYXIoKQpnZ3Bsb3QobXRjYXJzLCBhZXMoY3lsKSkgKyAgZ2VvbV9iYXIoKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiOTAlIiwgIGZpZy5hc3AgPSA1Lzl9CnAxIDwtIGdncGxvdChpcmlzICwgYWVzKFNwZWNpZXMpKSArIGdlb21fYmFyKCkgICMtIHRlbWEgcG9yIGRlZmVjdG8KcDIgPC0gZ2dwbG90KG10Y2FycywgYWVzKGN5bCkpICsgIGdlb21fYmFyKCkKCnAxICsgcDIgICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKUGFyYSBwb2RlciB2aXN1YWxpemFyIGdyw6FmaWNvcyBkZSBiYXJyYXMgY29uIDIgdmFyaWFibGVzLHRlbmVtb3MgcXVlIHVzYXIgb3RybyBkYXRhc2V0OiBgbXRjYXJzYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIoKSAjLSBwb3MKZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpCmdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikKZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZTIocHJlc2VydmUgPSAic2luZ2xlIikpCmBgYAoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICIxMDAlIiwgIGZpZy5hc3AgPSA1Lzl9CnAxIDwtIGdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKCkgIy0gcG9zCnAyIDwtIGdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKQpwMyA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpCnA0IDwtIGdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UocHJlc2VydmUgPSAic2luZ2xlIikpCgpwMSArIHAyICsgcDMgKyBwNCArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCjxicj4KClBvciBlamVtcGxvLCBwb2RlbW9zIG1vZGlmaWNhciBsYSBwb3NpY2nDs24gcG9yIGRlZmVjdG8gZGUgbnVlc3RybyBncsOhZmljbyBkZSBwdW50b3MgY29uIGVsIGlyaXMgZGF0YXNldCB1c2FuZG8gZG9zIGVuZm9xdWVzOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiLCBjb2xvciA9ICJwaW5rIikgCmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSArIGdlb21faml0dGVyKCBjb2xvciA9ICJwaW5rIikgCmBgYAoKCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIsIGNvbG9yID0gInBpbmsiKSAKYGBgCgpgZ2VvbV9qaXR0ZXIoKWAsIG8gYWx0ZXJuYXRpdmFtZW50ZSBgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiKWAgY2FtYmlhIGxhIHBvc2ljacOzbiBvcmlnaW5hbCBkZSBsb3MgZGF0b3MgYcOxYWRpZW5kbyB1biBwb2NvIGRlIHJ1aWRvLCBoYWNpZW5kbyBxdWUgc2UgZGVzcGxhY2VuIHVuIHBvY28uIEVzdGEgdMOpY25pY2Egc2UgdXNhIG11Y2hvcyBjdWFuZG8gaGF5IG11Y2hvcyBkYXRvcyBzaW1pbGFyZXMgKG92ZXJwbG90dGluZykuCgoKPGJyPgoKIyMjIDMuOSBDb29yZGVuYWRhcwoKPiBUaGUgY29vcmRpbmF0ZSBzeXN0ZW0gZGV0ZXJtaW5lcyBob3cgdGhlIHggYW5kIHkgYWVzdGhldGljcyBjb21iaW5lIHRvIHBvc2l0aW9uIGVsZW1lbnRzIGluIHRoZSBwbG90LiBUaGUgZGVmYXVsdCBjb29yZGluYXRlIHN5c3RlbSBpcyBDYXJ0ZXNpYW4gKGNvb3JkX2NhcnRlc2lhbigpKSwgd2hpY2ggY2FuIGJlIHR3ZWFrZWQgd2l0aCBjb29yZF9tYXAoKSwgY29vcmRfZml4ZWQoKSwgY29vcmRfZmxpcCgpLCBhbmQgY29vcmRfdHJhbnMoKSwgb3IgY29tcGxldGVseSByZXBsYWNlZCB3aXRoIGNvb3JkX3BvbGFyKCkKCgpQb3IgZWplbXBsbzogYGNvb3JkX2ZpeGVkKClgLiBFbiBudWVzdHJvIGdyw6FmaWNvLCB0YW50byBsYSBsb25naXR1ZCBkZWwgcMOpdGFsbyBjb21vIGRlbCBzw6lwYWxvIHNlIG1pZGVuIGVuIGxhcyBtaXNtYXMgdW5pZGFkZXMsIGFzw60gcXVlIHN1IHJhdGlvIGltcGzDrWNpdG8gZXMgMSBhIDEuIENhbWJpZW1vcyBlbCByYXRpbyBkZSBsYXMgY29vcmRlbmFkYXMgY29uIGBjb29yZF9maXhlZCgpYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkgCgpwICsgY29vcmRfZml4ZWQocmF0aW8gPSAxLzMpCnAgKyBjb29yZF9maXhlZChyYXRpbyA9IDMvMSkKYGBgCgoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICIxMDAlIiwgIGZpZy5hc3AgPSA1Lzl9CnAxIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKSAKcDIgPC0gcDEgKyBjb29yZF9maXhlZChyYXRpbyA9IDEvMykKcDMgPC0gcDEgKyBjb29yZF9maXhlZChyYXRpbyA9IDMvMSkKCnAyICsgcDMgKyBwbG90X2xheW91dChucm93ID0gMSkKI2h0dHBzOi8vd3d3LnN0YXR3b3J4LmNvbS9kZS9ibG9nL2Nvb3JkaW5hdGUtc3lzdGVtcy1pbi1nZ3Bsb3QyLWVhc2lseS1vdmVybG9va2VkLWFuZC1yYXRoZXItdW5kZXJyYXRlZC8KYGBgCgoKCgo8YnI+CgoKIyMgNC4gQ29tYmluYW5kbyBncsOhZmljb3MKCgpMYSB0w6ljbmljYSBkZWwgZmFjZXRpbmcgZXMgdW5hIGZhbnTDoXN0aWNhIGhlcnJhbWllbnRhIHBhcmEgZGl2aWRpciB1biBncsOhZmljbyBlbiB2YXJpb3MsIGVuIHByaW5jaXBpbyBlbiBmdW5jacOzbiBkZSB1bmEgdmFyaWFibGUgY2F0ZWfDs3JpY2EsIHNpIHF1ZXJlbW9zIHVzYXIgdW5hIHZhcmlhYmxlIGNvbnRpbnVhIHByaW1lcm8gaGFicsOtYSBxdWUgZGlzY3JldGl6YXJsYS4gKipQRVJPKiosIGEgdmVjZXMgbG8gcXVlIGludGVyZXNhIGVzIGNyZWFyIHVuYSBmaWd1cmEgY29tcHVlc3RhIGRlIHZhcmlvcyBncsOhZmljb3MgZGlmZXJlbnRlcy4gRXN0byBubyBlcyB1biBlbGVtZW50byBtw6FzIGRlIGdncGxvdDIgZXMgdW5hIG9wZXJhY2nDs24gcXVlIGhhY2Vtb3Mgc29icmUgdW4gZ3J1cG8gZGUgdmFyaW9zIGdyw6FmaWNvcy4KCgpQb2RlbW9zIGhhY2VybG8gY29uIHZhcmlvcyBwYXF1ZXRlczoKCi0gY29uIGVsIHBhcXVldGUgYGdyaWRFeHRyYWAgeSBzdSBmdW5jacOzbiBgZ3JpZC5hcnJhbmdlKClgCgpgYGB7ciwgZXZhbCA9IFRSVUUsIG91dC53aWR0aCA9ICI4MCUifQpsaWJyYXJ5KGdyaWRFeHRyYSkKcDEgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyBnZW9tX3BvaW50KCkKcDIgPC0gZ2dwbG90KGlyaXMpKyBhZXMoU3BlY2llcywgU2VwYWwuTGVuZ3RoKSArIGdlb21fYm94cGxvdCgpCgpncmlkLmFycmFuZ2UocDEsIHAyLCBuY29sID0gMiwgd2lkdGhzID0gYyg2LjUsIDMuNSkpCmBgYAoKQWRlbcOhcyBkZSBsb3MgYXJndW1lbnRvcyBuY29sLCBucm93IHkgd2lkdGhzLCBjb24gIGBncmlkRXh0cmFgIHNlIHB1ZWRlbiBoYWNlciBjb21wb3NpY2lvbmVzIFttw6FzIGNvbXBsZWphc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dyaWRFeHRyYS92aWduZXR0ZXMvYXJyYW5nZUdyb2IuaHRtbCkKCgotIGNvbiBlbCBwYXF1ZXRlIFtgcGF0Y2h3b3JrYF0oaHR0cHM6Ly9naXRodWIuY29tL3Rob21hc3A4NS9wYXRjaHdvcmspIAoKCgpgYGB7ciwgZXZhbCA9IFRSVUUsIG91dC53aWR0aCA9ICI4MCUifQpsaWJyYXJ5KHBhdGNod29yaykKcDEgKyBwMiArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCi0gY29uIGVsIHBhcXVldGUgW2Bjb3dwbG90YF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Nvd3Bsb3QvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKS4gRXN0ZSBwYXF1ZXRlIHRhbWJpw6luIHNlIHB1ZWRlIHV0aWxpemFyIHBhcmEgaGFjZXIgYW5vdGFjaW9uZXMgbyBwYXJhIGluY29ycG9yYXIgaW3DoWdlbmVzIGEgbnVlc3Ryb3MgZ3LDoWZpY29zCgoKYGBge3J9CmxpYnJhcnkoY293cGxvdCkKZ2dkcmF3KHAxKSArIGRyYXdfaW1hZ2UoaGVyZTo6aGVyZSgiLi9pbWFnZW5lcy9DYXB0dXJhLkpQRyIpLCAKICAgICAgICAgICAgICAgeCA9IDEsIHkgPSAxLCBoanVzdCA9IDEsIHZqdXN0ID0gMSwgd2lkdGggPSAwLjMzLCBoZWlnaHQgPSAwLjQyKQpgYGAKCgo8YnI+CgoKIyMgNS4gRXhwb3J0YW5kbyBncsOhZmljb3MKCkxhIG1heW9yw61hIGRlIGxhcyB2ZWNlcywgY3VhbmRvIGNyZWFtb3MgdW4gZ3LDoWZpY28gbG8gdmVtb3MgaW5tZWRpYXRhbWVudGUsIHBlcm8gdGFtYmnDqW4gcG9kZW1vcyBhc2lnbmFybGUgdW4gbm9tYnJlIHkgbWFuaXB1bGFybG8gbcOhcyBhZGVsYW50ZS4gCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArIGdlb21fcG9pbnQoKQpwICsgZ2VvbV9saW5lKCkKYGBgCgoKVW5hIHZleiBxdWUgdGllbmVzIGVsIGdyw6FmaWNvIGd1YXJkYWRvIGNvbW8gdW4gb2JqZXRvIGVuIGVsIFIgZW52aXJvbm1lbnQgcHVlZGVzIGhhY2VyIHZhcmlhcyBjb3NhcyBjb24gw6lsOgoKMS4gdmVybG8gZW4gbGEgcGFudGFsbGEgY29uIGBwcmludCgpYCBvIGxsYW3DoW5kb2xvIGNvbiAic3Ugbm9tYnJlIi4KCjIuIGd1YXJkYXJsbyBlbiB1biBmaWNoZXJvIGVuIGRpZmVyZW50ZXMgZm9ybWF0b3MuIFBpZW5zYSBxdWUgZXN0YW1vcyBndWFyZGFuZG8gZWwgZ3LDoWZpY28sIGxhIGltYWdlbiwgbGEgcmVwcmVzZW50YWNpw7NuIGRlbCBncsOhZmljbywgbm8gZWwgb2JqZXRvIFIuCgogICAgLSBQb2RlbW9zIHVzYXIgbGEgIkV4cG9ydCBUYWIiIGVuIGVsIFBsb3QgUGFuZSBkZSBSc3R1ZGlvLiBMbyBncmFiYXLDoSBlbiBiYWphIHJlc29sdWNpw7NuLiAgVGFtYmnDqW4gcG9kZW1vcyBjYW1iaWFyIGxhcyBkaW1lbnNpb25lcyAoYW5jaHVyYSB5IGFsdHVyYSkgZGVsIGdyw6FmaWNvLiBTaSB2YW1vcyBhIHV0aWxpemFyIGVsIGdyw6FmaWNvIGVuIFdvcmQgZXMgY29udmVuaWVudGUgZ3VhcmRhciBlbCBncsOhZmljbyBjb21vIE1ldGFmaWxlLgogICAgCiAgICAtIENvbW8gYWx0ZXJuYXRpdmEgcG9kZW1vcyB1c2FyIGxhIGZ1bmNpw7NuIGBnZ3NhdmUoKWAgcXVlLCBhZGVtYXMsIG5vcyBwZXJtaXRlIGNhbWJpYXIgZWwgdGFtYcOxbyB5IGxhIHJlc29sdWNpw7NuIGRlbCBncsOhZmljbyBjb24gbG9zIGFyZ3VtZW50b3Mgd2lkdGgsIGhlaWdodCB5IGRwaS4gTm90YTogTG9zIHBhcsOhbWV0cm9zIHdpZHRoIGFuZCBoZWlnaHQgdGFtYmnDqW4gZGV0ZXJtaW5hbiBlbCB0YW1hw7FvIGRlIGxhIGZ1ZW50ZSBkZWwgZ3LDoWZpY28gZ3VhcmRhZG8uCgogICAgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3NhdmUoIi4vZ3JhZl9vdXQvbXlfZ3JhZmljb19jaHVsby5wbmciLCBwLCB3aWR0aCA9IDE1LCBoZWlnaHQgPSAxMCkKZ2dzYXZlKCJmaWxlbmFtZS5wbmciLCBwbG90ID0gbXlfcGxvdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIsIGRwaSA9ICJyZXRpbmEiKQoKIyBUYiBmdW5jaW9uYSBwYXJhIGZpZ3VyYXMgY29tcHVlc3RhcyBkZSB2YXJpb3MgZ3LDoWZpY29zCmdyYWZpY29fY29tYmluYWRvIDwtIGdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2wgPSAyLCB3aWR0aHMgPSBjKDYsIDQpKQpnZ3NhdmUoImZpZ19vdXRwdXQvbXlfY29tYm9fcGxvdC5wbmciLCBncmFmaWNvX2NvbWJpbmFkbywgd2lkdGggPSAxMCwgZHBpID0gMzAwKQpgYGAKCgotIEZpbmFsbWVudGUsIHBvZGVtb3MgZ3VhcmRhciB1bmEgY29waWEgY29tcGxldGEgZGVsIGdyw6FmaWNvLCBubyBkZSBzdSByZXByZXNlbnRhY2nDs24gdmlzdWFsLCBzaW5vIGRlbCBvYmpldG8gUiBjb24gYHNhdmVSRFMoKWAsIHBhcmEgbHVlZ28gbGVlcmxvIGNvbiBgcmVhZFJEUygpYC4gQXVucXVlLCB5YSBxdWUgbm9zIHBvbmVtb3MsIGVzIG1lam9yIGd1YXJkYXIgZWwgc2NyaXB0IHF1ZSBnZW5lcmEgZWwgb2JqZXRvLiBTw7NsbyB0ZW5kcsOtYSBzZW50aWRvIHNpICBmdWVzZSBtdXkgY29zdG9zbywgcG9yIGVqZW1wbG8gZW4gdMOpcm1pbm9zIGRlIHRpZW1wbywgcmVwcm9kdWNpciBlbCBncsOhZmljby4KCmBgYHtyLCBldmFsID0gRkFMU0V9CnNhdmVSRFMocCwgInBsb3QucmRzIikKbXlfdmFsaW9zb19ncmFmaWNvIDwtIHJlYWRSRFMoInBsb3QucmRzIikKYGBgCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIDYuIFRpcG9zIGRlIGdyw6FmaWNvcwoKRW4gZXN0w6Egc2VjY2nDs24gcHJlc2VudGFyZW1vcyBhbGd1bm9zIGVqZW1wbG9zIGRlIGFsZ3Vub3MgZGUgbG9zIGdyw6FmaWNvcyBtw6FzIHV0aWxpemFkb3MgZW4gZWwgYW7DoWxpc2lzIGRlIGRhdG9zLiBQdWVkZXMgdmVyIGxpc3RhZG9zIG3DoXMgY29tcGxldG9zIGVuOgoKICAtIFtSLWdyYXBoIGdhbGxlcnldKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vKTogSW1wcmVzaW9uYW50ZSEhCgoKICAtIFtUb3AgNTAgZ2dwbG90MiBWaXN1YWxpemF0aW9ucyAoV2l0aCBGdWxsIFIgQ29kZSksIGRlIHItc3RhdGlzdGljcy5jb10oaHR0cDovL3Itc3RhdGlzdGljcy5jby9Ub3A1MC1HZ3Bsb3QyLVZpc3VhbGl6YXRpb25zLU1hc3Rlckxpc3QtUi1Db2RlLmh0bWwpOiBtw6FzIGLDoXNpY28gcGVybyBtdXkgZGlkw6FjdGljby4KCiAgCiAgLSBbRXh0ZW5zaW9uZXMgZ2dwbG90Ml0oaHR0cHM6Ly9leHRzLmdncGxvdDIudGlkeXZlcnNlLm9yZy8pOiBJbXByZXNpb25hbnRlISEKCiAgLSBbUi1ncmFwaCBnYWxsZXJ5IC0gZ2dwbG90MiBwYWNrYWdlXShodHRwOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS9nZ3Bsb3QyLXBhY2thZ2UuaHRtbCk6IG90cmEgdmV6LCBpbXByZXNpb25hbnRlISEKCgo8YnI+CgojIyMgNi4xIEhpc3RvZ3JhbWFzCgpTZSB1dGlsaXphbiBwYXJhIG1vc3RyYXIgbGEgKipkaXN0cmlidWNpw7NuIGRlIFVOQSB2YXJpYWJsZSBjb250aW51YSoqLCBwb3IgZWplbXBsbyBsYSBsb25naXR1ZCBkZWwgc8OpcGFsbyAoU2VwYWwuTGVuZ3RoKS4KClBhcmEgaGFjZXIgaGlzdG9ncmFtYXMgZW4gZ2dwbG90MiBzZSB1dGlsaXphIGBnZW9tX2hpc3RvZ3JhbWAgeSwgYSB2ZWNlcywgYGdlb21fZnJlcXBvbHkoKWAuIExvcyBkb3MgZ2VvbXMgdHJhYmFqYW4gZGUgbGEgbWlzbWEgbWFuZXJhLCBkaXZpZGVuIGxhIHZhcmlhYmxlIHggZW4gaW50ZXJ2YWxvcyB5IGN1ZW50YW4gbGFzIG9ic2VydmFjaW9uZXMgZW4gY2FkYSBpbnRlcnZhbG8sIHBhcmEgbW9zdHJhcmxhcyBlbiBlbCBlamUgWS4gTGEgZGlmZXJlbmNpYSBlbnRyZSBlbGxvcyBlcyBxdWUgYGdlb21faGlzdG9ncmFtYCB1dGlsaXphIGJhcnJhcyBwYXJhIG1vc3RyYXIgZWwgbsO6bWVybyBkZSBvYnNlcnZhY2lvbmVzIG8gZnJlY3VlbmNpYSBhYnNvbHV0YSwgbWllbnRyYXMgcXVlICBgZ2VvbV9mcmVxcG9seSgpYCB1c2EgbGluZWFzLiAKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oKSAKYGBgCgpDb21vIHZlcywgbG8gcXVlIGhhY2UgdW4gaGlzdG9ncmFtYSBlcyBkaXZpZGlyIGVsIGVqZSBYIGVuIGludGVydmFsb3MgbyAiYmlucyIgeSBtb3N0cmFyIGVuIGVsIGVqZSBZIGVsIG7Dum1lcm8gZGUgb2JzZXJ2YWNpb25lcyBkZSB4IHF1ZSBjYWVuIGVuIGNhZGEgImJpbiIuIEFkZW3DoXMsIGdncGxvdDIgbm9zIGF2aXNhIGRlIHF1ZSBwb3IgZGVmZWN0byBzZSBkaXZpZGUgZWwgZWplIFggZW4gMzAgaW50ZXJ2YWxvcywgcGVybyBxdWUgcG9kZW1vcyBjYW1iaWFybG8gY29uIGBiaW5zID0gbXlfbl9kZV9iaW5zYC4gCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpCnAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDApICsgeGxhYigiNDAgaW50ZXJ2YWxvcyIpCnAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNCkgKyB4bGFiKCJTw7NsbyA0IGludGVydmFsb3MiKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiOTUlIn0KcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpCnAxIDwtIHAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDApICsgeGxhYigiNDAgaW50ZXJ2YWxvcyIpCnAyIDwtIHAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNCkgKyB4bGFiKCJTw7NsbyA0IGludGVydmFsb3MiKQpwMSArIHAyICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKCk90cmEgb3BjacOzbiBpbnRlcmVzYW50ZSBlcyBlbGVnaXIgbGEgYW5jaHVyYSBkZWwgaW50ZXJ2YWxvIGNvbiBsYSBvcGNpw7NuIGBiaW53aWR0aGAuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkKcCArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xKSArIHhsYWIoIkhlIGVsZWdpZG8gbGEgYW5jaHVyYSA9IDAuMSIpCnAgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEuMSkgKyB4bGFiKCJFc3RhIHZleiBsYSBhbmNodXJhID0gMS4xIikKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjk1JSJ9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgpKQpwMSA8LSBwICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEpICsgeGxhYigiSGUgZWxlZ2lkbyBsYSBhbmNodXJhID0gMC4xIikKcDIgPC0gcCArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMS4xKSArIHhsYWIoIkVzdGEgdmV6IGxhIGFuY2h1cmEgPSAxLjEiKQpwMSArIHAyICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKClNpIGVuIGVsIGVqZSBZIHF1ZXJlbW9zIGZyZWN1ZW5jaWFzIHJlbGF0aXZhcyBvIHBvcmNlbnRhamVzIGVuIGx1Z2FyIGRlIGNvdW50cywgcG9kZW1vcyBoYWNlcmxvIGNvbjoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBzdGF0KGNvdW50KSAvIHN1bShjb3VudCkpLCBiaW5zID0gMTApICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KQpgYGAKCgpTZSBwdWVkZSBtZWpvcmFyIGJhc3RhbnRlIGxhIGFwYXJpZW5jaWEgZGVsIGhpc3RvZ3JhbWEganVnYW5kbyBjb24gbG9zIGNvbG9yZXMgeSBvcGNpb25lczoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidG9tYXRvIikKYGBgCgoKClNpIGFzb2NpYW1vcy9tYXBlYW1vcyBsYSB2YXJpYWJsZSBTcGVjaWVzIGEgYWxndW5hIGVzdMOpdGljYSwgcG9yIGVqZW1wbG8gYSBsYSBlc3TDqXRpY2EgImZpbGwiIG8gcmVsbGVubyBkZSBsYXMgYmFycmFzIGNvbiBgYWVzKGZpbGwgPSBTcGVjaWVzKWAsIHNlIHZpc3VhbGl6YXLDoSBxdcOpIHBhcnRlIGRlIGNhZGEgaW50ZXJ2YWxvIHBlcnRlbmVjZSBhIGNhZGEgY2xhc2UgZGUgbGlyaW8uCgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwLCBhZXMoZmlsbCA9IFNwZWNpZXMpLCBjb2xvciA9ICJibGFjayIpCmBgYAoKCkV2aWRlbnRlbWVudGUsIHRhbWJpw6luIHBvZGVtb3MgdmlzdWFsaXphciBjb21vIGNhbWJpYSBsYSBkaXN0cmlidWNpw7NuIGRlIGxhIGFuY2h1cmEgZGVsIHPDqXBhbG8gZW50cmUgbG9zIDMgdGlwb3MgZGUgbGlyaW9zIHNpIGhhY2Vtb3MgdW4gc21hbGwgbXVsdGlwbGU6CgoKYGBge3J9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIGZpbGwgPSBTcGVjaWVzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAsIGNvbG9yID0gImJsYWNrIikKcCArIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoU3BlY2llcykpCmBgYAoKUGFyYSBtZWpvcmFyIGxhIHZpc3VhbGl6YWNpw7NuIHBvZGVtb3MgcG9uZXIgZW4gZWwgZm9uZG8gZWwgaGlzdG9ncmFtYSBwYXJhIHRvZG9zIGxvcyBkYXRvcy4gTG8gYXByZW5kw60gW2FxdcOtXShodHRwczovL2Ryc2ltb25qLnN2YnRsZS5jb20vcGxvdHRpbmctYmFja2dyb3VuZC1kYXRhLWZvci1ncm91cHMtd2l0aC1nZ3Bsb3QyKSwgYXVucXVlIGFob3JhIG1pc21vIGhheSB1biBwYXF1ZXRlIHBhcmEgaW1wbGVtZW50YXIgZXN0YSB0w6ljbmljYSwgZXMgZWwgcGFxdWV0ZSBbYGdnaGlnaGxpZ2h0YF0oaHR0cHM6Ly95dXRhbm5paGlsYXRpb24uZ2l0aHViLmlvL2dnaGlnaGxpZ2h0L2luZGV4Lmh0bWwpLgoKCmBgYHtyfQppcmlzX2JhY2tncm91bmcgPC0gaXJpcyAlPiUgc2VsZWN0KC1TcGVjaWVzKQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IGlyaXNfYmFja2dyb3VuZywgZmlsbCA9ICJncmV5IiwgYmlucyA9IDE1KSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSBTcGVjaWVzKSwgYmlucyA9IDE1KSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhTcGVjaWVzKSkKYGBgCgo8YnI+CgojIyMjIGdlb21fZGVuc2l0eSgpCgpVbmEgYWx0ZXJuYXRpdmEgYSBsb3MgaGlzdG9ncmFtYXMgc29uIGxvcyBncsOhZmljb3MgZGUgZGVuc2lkYWQgY29uIGBnZW9tX2RlbnNpdHkoKWAuIFBlcm8sIHNlZ8O6biBIYWRsZXk6IAoKPnRoZXkgYXJlIGhhcmRlciB0byBpbnRlcnByZXQgc2luY2UgdGhlIHVuZGVybHlpbmcgY29tcHV0YXRpb25zIGFyZSBtb3JlIGNvbXBsZXguIFRoZXkgYWxzbyBtYWtlIGFzc3VtcHRpb25zIHRoYXQgYXJlIG5vdCB0cnVlIGZvciBhbGwgZGF0YSwgbmFtZWx5IHRoYXQgdGhlIHVuZGVybHlpbmcgZGlzdHJpYnV0aW9uIGlzIGNvbnRpbnVvdXMsIHVuYm91bmRlZCwgYW5kIHNtb290aC4KCkNvbW8gZGljZSBIYWRsZXksIGBnZW9tX2RlbnNpdHkoKWAgZXN0aW1hIGxhIGZ1bmNpw7NuIGRlIGRlbnNpZGFkLCBwb3IgbG8gcXVlIGxhIGVzdGltYWNpw7NuIGRlcGVuZGUgZGUgdW5hIHNlcmllIGRlIHBhcmFtw6l0cm9zIGNvbW8gYGFkanVzdGAuIEhhY2Vtb3MgdXNvIGRlIGB4bGltKDMsIDkpYCBwYXJhIGV4cGFuZGlyIGxvc2zDrW1pdGVzIGRlbCBlamUgWC4KCgpgYGB7ciwgb3V0LndpZHRoID0gIjg1JSJ9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkgKyAKICBnZW9tX2RlbnNpdHkoY29sb3IgPSAicmVkIiwgICBzaXplID0gMS4yKSArICAKICBnZW9tX2RlbnNpdHkoY29sb3IgPSAiYmx1ZSIsICBzaXplID0gMS4yLCBhZGp1c3QgPSAzKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEuMiwgYWRqdXN0ID0gMC41KSArICB4bGltKDIsIDEwKQpgYGAKCjxicj4KClBvZGVtb3MgYXNvY2lhciBsYSB2YXJpYWJsZSBTcGVjaWVzIGEgbGEgZXN0w6l0aWNhIGZpbGwgY29uIGBhZXMoZmlsbCA9IFNwZWNpZXMpYCBwYXJhIG9idGVuZXIgdW5hIGVzdGltYWNpw7NuIGRlIGxhIGRlbnNpZGFkIHBhcmEgY2FkYSB0aXBvIGRlIGxpcmlvLgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgZmlsbCA9IFNwZWNpZXMpKSArIGdlb21fZGVuc2l0eShwb3NpdGlvbiA9ICJzdGFjayIsIGFscGhhID0gMC41KQpgYGAKCjxicj4KCkhheSBtdWNoYXMgb3RyYXMgcG9zaWJpbGlkYWRlcy4gUG9yIGVqZW1wbG86CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgZmlsbCA9IFNwZWNpZXMpKSArIGdlb21fZGVuc2l0eShwb3NpdGlvbiA9ICJmaWxsIiwgYWxwaGEgPSAwLjUpICMtIHBvc2l0aW9uID0gImZpbGwiCgpnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgc3RhdChjb3VudCksIGZpbGwgPSBTcGVjaWVzKSkgKyBnZW9tX2RlbnNpdHkocG9zaXRpb24gPSAic3RhY2siLCBhbHBoYSA9IDAuNSkgIy0gc3RhdChjb3VudCkKYGBgCgoKTXVjaGFzIHZlY2VzIHNlIHN1ZWxlbiBoYWNlciBsb3MgaGlzdG9ncmFtYXMgc3VwZXJwb25pZW5kbyBsYSBmLiBkZSBkZW5zaWRhZCBub3JtYWwgbyB1bmEgZXN0aW1hY2nDs24gZGUgbGEgZGVuc2lkYWQgZGUgeCBjb24gYGdlb21fZGVuc2l0eSgpYCBvIGNvbiBgZ2VvbV9saW5lKHN0YXQ9ImRlbnNpdHkiKWAuIFBhcmEgcXVlIGVsIGdyw6FmaWNvIHNlIHZlYSBtZWpvciwgYWp1c3RhcmVtb3MgZWwgZWplIFggY29uIGAgeGxpbSgpYDoKCgpgYGB7cn0KIy0gY2FsY3VsYW1vcyBtZWRpYSB5IGRlc3ZpYWNpw7NuIHRpcGljYSBkZSBTZXBhbC5MZW5ndGggcGFyYSBsdWVnbyB1c2FybGFzIHBhcmEgY29uc3RydWlyIGxhIGN1cnZhIG5vcm1hbAptZWRpYSA8LSBtZWFuKGlyaXMkU2VwYWwuTGVuZ3RoLCBuYS5ybSA9IFRSVUUpICAgICAjLSBtZWRpYSBkZSBsYSBsb25naXR1ZCBkZWwgc8OpcGFsbwpkZXN2aWFjaW9uIDwtIHNkKGlyaXMkU2VwYWwuTGVuZ3RoLCBuYS5ybSA9IFRSVUUpICAjLSBkZXN2aWFjacOzbiAKCiMtIGhhY2Vtb3MgZWwgaGlzdG9ncmFtYQpwIDwtIGdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoKSkgKwogICAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksICBjb2xvcj0iYmxhY2siLCBmaWxsID0gInN0ZWVsYmx1ZSIsIGFscGhhID0gMC4yKQoKIy0gbGUgYcOxYWRpbW9zIGxhIGRlbnNpaWRhZCBlc3RpbWFkYSB5IGxhIG5vcm1hbApwICsgZ2VvbV9kZW5zaXR5KCBjb2xvcj0icHVycGxlIiwgc2l6ZSA9IDEpICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGNvbG91ciA9ICJyZWQiLCBzaXplID0gMSwgYXJncyA9IGxpc3QobWVhbiA9IG1lZGlhLCBzZCA9IGRlc3ZpYWNpb24pKSAgKyAKICAgIHhsaW0oYyhtaW4oaXJpcyRTZXBhbC5MZW5ndGgpLTEsIDkpKQoKYGBgCgoKU2kgbmVjZXNpdGFzIHNhYmVyIG3DoXMgY29zYXMgc29icmUgbG9zIGhpc3RvZ3JhbWFzIHB1ZWRlcyBhY3VkaXIgW2FxdcOtXShodHRwczovL2VkYXYuaW5mby9oaXN0by5odG1sKSBvIFthcXXDrV0oaHR0cDovL3QtcmVkYWN0eWwuaW8vYmxvZy8yMDE2LzAyL2NyZWF0aW5nLXBsb3RzLWluLXItdXNpbmctZ2dwbG90Mi1wYXJ0LTctaGlzdG9ncmFtcy5odG1sKS4KCgoKIyMjIyBKb3kgRGl2aXNpb24gcGxvdHMKCkhhY2UgcG9jbyBhcGFyZWNpw7MgZWwgcGFxdWV0ZSBbZ2dyaWRnZXNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ3JpZGdlcy92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwpIHkgc2UgcHVzaWVyb24gZGUgbW9kYSBlc3RvcyB0aXBvcyBkZSBncsOhZmljb3M6CgoKCmBgYHtyfQpsaWJyYXJ5KGdncmlkZ2VzKQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTcGVjaWVzKSkgKyBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyhmaWxsID0gU3BlY2llcyksIGFscGhhID0gMC41KQpgYGAKCkNvbW8gc2UgcGFyZWNlbiBhIGxhIFttw610aWNhIHBvcnRhZGFdKGh0dHBzOi8vY29kaWdvZXNwYWd1ZXRpLmNvbS9ub3RpY2lhcy9jaWVuY2lhL2hpc3RvcmlhLXNlY3JldGEtdW5rbm93bi1wbGVhc3VyZXMvKSBkZWwgcHJpbWVyIGRpc2NvIGRlIEpveSBEaXZpc2lvbiwgYWxndW5vcyBsb3MgY29ub2NlbiBjb21vIEpveSBEaXZpc2lvbiBwbG90cy4gQWJham8gb3MgcG9uZ28gdW4gZWplbXBsbyBzYWNhZG8gZGUgbGEgW3ZpZ25ldHRlIGRlbCBwYXF1ZXRlIGdncmlkZ2VzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dyaWRnZXMvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKS4KCgpgYGB7cn0KbGlicmFyeSh2aXJpZGlzKQpnZ3Bsb3QobGluY29sbl93ZWF0aGVyLCBhZXMoeCA9IGBNZWFuIFRlbXBlcmF0dXJlIFtGXWAsIHkgPSBgTW9udGhgLCBmaWxsID0gLi54Li4pKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudChzY2FsZSA9IDMsIHJlbF9taW5faGVpZ2h0ID0gMC4wMSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lID0gIlRlbXAuIFtGXSIsIG9wdGlvbiA9ICJDIikgKwogIGxhYnModGl0bGUgPSAnVGVtcGVyYXR1cmVzIGluIExpbmNvbG4gTkUgaW4gMjAxNicpCmBgYAoKCjxicj4KCiMjIyA2LjIgU2NhdHRlciBwbG90CgpTY2F0dGVyIHBsb3QsIGdyw6FmaWNvIGRlIHB1bnRvcyBvIGdyw6FmaWNvIFgtWS4gU2UgdXRpbGl6YSBwYXJhIG1vc3RyYXIgbGEgKipyZWxhY2nDs24gZW50cmUgRE9TIHZhcmlhYmxlcyBjb250aW51YXMqKi4gTG8gdGVuZW1vcyBtw6FzIHF1ZSB2aXN0bywgeWEgcXVlIGVzIGVsIHRpcG8gZGUgZ3LDoWZpY28gcXVlIGhlbW9zIHV0aWxpemFkbyBkdXJhbnRlIHRvZG8gZWwgdHV0b3JpYWwuIAoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcykpICsgCiAgICAgZ2VvbV9wb2ludCgpICsKICAgICBsYWJzKHRpdGxlID0gIkdyw6FmaWNvIDE6IExvbmdpdHVkIGRlbCBzw6lwYWxvIGZyZW50ZSBhbCBww6l0YWxvIiwKICAgICAgIHN1YnRpdGxlID0gIihkaWZlcmVuY2lhbmRvIHBvciBlc3BlY2llIGRlIGxpcmlvKSIsCiAgICAgICBjYXB0aW9uID0gIkRhdG9zIHByb3ZlbmllbnRlcyBkZWwgSXJpcyBkYXRhc2V0IiwKICAgICAgIHggPSAiTG9uZ2l0dWQgZGVsIHPDqXBhbG8iLAogICAgICAgeSA9ICJMb25naXR1ZCBkZWwgcMOpdGFsbyIsCiAgICAgICBjb2xvciA9ICJFc3BlY2llIGRlIGxpcmlvIikKYGBgCgoKCiMjIyMgT3ZlcnBsb3R0aW5nCgpMb3MgZ3LDoWZpY29zIGRlIHB1bnRvcyBzZSB1c2FuIGhhYml0dWFsbWVudGUgcGFyYSBtb3N0cmFyIGxhIHJlbGFjacOzbiBlbnRyZSBkb3MgdmFyaWFibGVzIGNvbnRpbnVhcy4gRW4gY29uanVudG9zIGRlIGRhdG9zIGNvbiBtdWNoYXMgb2JzZXJ2YWNpb25lcyBwdWVkZW4gdGVuZXIgdW4gcHJvYmxlbWEgZGUgIm92ZXJwbG90dGluZyIuIEVzdGEgc2l0dWFjacOzbiBvY3VycmUgY3VhbmRvIHVub3MgcHVudG9zIHNlICBkaWJ1amFuIGVuY2ltYSBkZSBvdHJvcywgZGUgZm9ybWEgcXVlIG5vIHNlIGFwcmVjaWEgYmllbiBsYSByZWxhY2nDs24gZW50cmUgbGFzIHZhcmlhYmxlcwoKSGF5IHZhcmlhcyBmb3JtYXMgZGUgdHJhdGFyIGVzdGUgcHJvYmxlbWEuIFZlw6Ftb3NsbyBjb24gdW4gZWplbXBsbyBzYWNhZG8gZGUgbGEgd2ViIGRlIGdncGxvdDI6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpzZXQuc2VlZCgxMjM0KQpkZiA8LSBkYXRhLmZyYW1lKHggPSBybm9ybSgyMDAwKSwgeSA9IHJub3JtKDIwMDApKSAgICMtIGNyZWFtb3MgdW4gY29uanV0byBkZSBkYXRvcyBjb24gMjAwMCBvYnNlcnZhY2lvbmVzIHkgMiB2LgoKcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKyB4bGFiKE5VTEwpICsgeWxhYihOVUxMKQoKcCArIGdlb21fcG9pbnQoKQpwICsgZ2VvbV9wb2ludChzaGFwZSA9IDEpICMgSG9sbG93IGNpcmNsZXMKcCArIGdlb21fcG9pbnQoc2hhcGUgPSAiLiIpICMgUGl4ZWwgc2l6ZWQKCiMtIEZvciBsYXJnZXIgZGF0YXNldHMgd2l0aCBtb3JlIG92ZXJwbG90dGluZywgeW91IGNhbiB1c2UgYWxwaGEgYmxlbmRpbmcgKHRyYW5zcGFyZW5jeSkgdApwICsgZ2VvbV9wb2ludChhbHBoYSA9IDEgLyAxMCkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjg1JSJ9CnNldC5zZWVkKDEyMzQpCmRmIDwtIGRhdGEuZnJhbWUoeCA9IHJub3JtKDIwMDApLCB5ID0gcm5vcm0oMjAwMCkpICAgIy0gY3JlYW1vcyB1biBjb25qdXRvIGRlIGRhdG9zIGNvbiAyMDAwIG9ic2VydmFjaW9uZXMgeSAyIHYuCgpwIDwtIGdncGxvdChkZiwgYWVzKHgsIHkpKSArIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpCgpwMSA8LSBwICsgZ2VvbV9wb2ludCgpCnAyIDwtIHAgKyBnZW9tX3BvaW50KHNoYXBlID0gMSkgIyBIb2xsb3cgY2lyY2xlcwpwMyA8LSBwICsgZ2VvbV9wb2ludChzaGFwZSA9ICIuIikgIyBQaXhlbCBzaXplZAoKIy0gRm9yIGxhcmdlciBkYXRhc2V0cyB3aXRoIG1vcmUgb3ZlcnBsb3R0aW5nLCB5b3UgY2FuIHVzZSBhbHBoYSBibGVuZGluZyAodHJhbnNwYXJlbmN5KSB0CnA0IDwtIHAgKyBnZW9tX3BvaW50KGFscGhhID0gMSAvIDEwKQoKcDEgKyBwMiArIHAzICsgcDQgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKPGJyPgoKCkRlIGZvcm1hIGFsdGVybmF0aXZhLCBwb2RlbW9zIGxpZGlhciBjb24gZWwgb3ZlcnBsb3R0aW5nIHV0aWxpemFuZG8gb3RybyBlbmZvcXVlIHF1ZSBjb25zaXN0ZSBlbiBwZW5zYXIgZW4gZWwgb3ZlcnBsb3R0aW5nIGNvbW8gdW4gcHJvYmxlbWEgZGUgZGVuc2lkYWQgZW4gMiBkaW1lbnNpb25lcyB5IHV0aWxpemFyIHVuIGhleC1wbG90LiBFbiB1biBoZXgtcGxvdCBzZSByZXByZXNlbnRhbiByZWdpb25lcyAoZ2VuZXJhbG1lbnRlIGhleGFnb25hbGVzKSBjb2xvcmVhZGFzIGVuIGZ1bmNpw7NuIGRlbCBuw7ptZXJvIGRlIG9ic2VydmFjaW9uZXMgcXVlIGNhZW4gZW4gY2FkYSByZWdpw7NuLCBkZSBmb3JtYSBxdWUgZXMgZXF1aXZhbGVudGUgYSB1biBoaXN0b2dyYW1hIHBlcm8gZW4gMmQuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwICsgZ2VvbV9iaW4yZCgpCnAgKyBnZW9tX2JpbjJkKGJpbnMgPSAxMCkKcCArIGdlb21faGV4KCkKcCArIGdlb21faGV4KCkgKyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiIzEzMkI0MyIsIGhpZ2ggPSAiIzU2QjFGNyIpICAKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjk1JSJ9CnAxIDwtIHAgKyBnZW9tX2JpbjJkKCkKcDIgPC0gcCArIGdlb21fYmluMmQoYmlucyA9IDEwKQpwMyA8LSBwICsgZ2VvbV9oZXgoKQpwNCA8LSBwICsgZ2VvbV9oZXgoKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICIjMTMyQjQzIiwgaGlnaCA9ICIjNTZCMUY3IikgIApwMSArIHAyICsgcDMgKyBwNCArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCgoKIAoKRW4gW2VzdGUgcG9zdF0oaHR0cHM6Ly9jdXIuYXQvbnVoWWtMMD9tPWVtYWlsJnNpZD05SXByVEN0KSB1dGlsaXphbiBoZXgtcGxvdHMgcGFyYSBtb3N0cmFyIGxhcyB6b25hcyBkZXNkZSBkb25kZSB0aXJhbiBhIGNhbmFzdGEgbG9zIGp1Z2Fkb3JlcyBOQkEuIEVuIFtlc3RlIG90cm8gcG9zdF0oaHR0cHM6Ly9yY3Jhc3RpbmF0ZS5yYmluZC5pby9wb3N0L3RoZXJlLWlzLWEtZ2FtZS1pLXBsYXktYW5hbHl6aW5nLW1ldGFjcml0aWMtc2NvcmVzLWZvci12aWRlby1nYW1lcy8pIHV0aWxpemFuIHVuYSBjb21iaW5hY2nDs24gZGUgYGdlb21fcG9pbnQoYWxwaGEgPSAuMiwgcGNoID0gMTUpICsgZ2VvbV9kZW5zaXR5XzJkKClgIHBhcmEgYW5hbGl6YXIgbGFzIHB1bnR1YWNpb25lcyBkZSB2aWRlb2p1ZWdvcy4gQWRlbcOhcyBoYWNlIHVuIHVzbyBhdmFuemFkbyBkZSBsYXMgZXNjYWxhcywgYWwgbWVub3MgcGFyYSBtaSwgcGFyYSBxdWUgZWwgZ3LDoWZpY28gc2VhIGbDoWNpbCBkZSB2aXN1YWxpemFyLgoKQWNhYmEgZGUgYXBhcmVjZXIgdW4gbnVldm8gZ2VvbTogW2BnZW9tX3BvaW50ZGVuc2l0eSgpYF0oaHR0cHM6Ly9naXRodWIuY29tL0xLcmVtZXIvZ2dwb2ludGRlbnNpdHkpLiBDb21vIGRpY2VuIGVuIHN1IHJlcG8gZGUgR2l0aHViOiAiQSBjcm9zcyBiZXR3ZWVuIGEgc2NhdHRlciBwbG90IGFuZCBhIDJEIGRlbnNpdHkgcGxvdCIuCgoKYGBge3J9CiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIkxLcmVtZXIvZ2dwb2ludGRlbnNpdHkiKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dwb2ludGRlbnNpdHkpCnAgKyAgZ2VvbV9wb2ludGRlbnNpdHkoYWRqdXN0ID0gNykgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXMoKQoKYGBgCgoKU2kgcXVpZXJlcyBzYWJlciBtw6FzIGNvc2FzIHNvYnJlIGPDs21vIGhhY2VyIHNjYXR0ZXJwbG90cyBjb24gYGdncGxvdDJgIHB1ZWRlcyBoYWNlcmxvIHBvciBlamVtcGxvLCBbYXF1w61dKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzL1NjYXR0ZXJwbG90c18oZ2dwbG90MikvKSBvIFthcXXDrV0oaHR0cHM6Ly9taWNoYWVsdG90aC5tZS9hLWRldGFpbGVkLWd1aWRlLXRvLXRoZS1nZ3Bsb3Qtc2NhdHRlci1wbG90LWluLXIuaHRtbCkuCgo8YnI+CgojIyMgNi4zIEJveHBsb3QgKGdyw6FmaWNvcyBkZSBjYWphKQoKUGFyYSB2aXN1YWxpemFyIHVuYSB2YXJpYWJsZSBjdWFudGl0YXRpdmEgc2Ugc3VlbGVuIHVzYXIgbG9zIGhpc3RvZ3JhbWFzLCBwZXJvIHNpIGxvIHF1ZSBxdWllcmVzIGVzIHZpc3VhbGl6YXIgY29tbyB2YXLDrWFuIGxvcyB2YWxvcmVzIGRlIHVuYSB2YXJpYWJsZSBjb250aW51YSBlbiBmdW5jacOzbiBkZSBsb3MgdmFsb3JlcyBkZSB1bmEgdmFyaWFibGUgY2F0ZWfDs3JpY2EgKFNwZWNpZXMpIHNlIHN1ZWxlbiB1c2FuIGJveHBsb3RzIG8gZGlhZ3JhbWFzIGRlIGNhamEuIFNvbiDDunRpbGVzIHBhcmEgY29tcGFyYXIgZGlmZXJlbnRlcyBncnVwb3MgeSBwYXJhIGlkZW50aWZpY2FyIG91dGxpZXJzLgoKClBhcmEgaGFjZXIgdW4gYm94cGxvdCBvIGRpYWdyYW1hIGRlIGNhamEgc2UgdXRpbGl6YSBgZ2VvbV9ib3hwbG90KClgLiBFbCBncsOhZmljbyBtdWVzdHJhIDUgZXN0YWTDrXN0aWNvczogbGEgbWVkaWFuYSBjb24gdW5hIGxpbmVhIGdydWVzYSwgZWwgcHJpbWVyIHkgdGVyY2VyIGN1YXJ0aWwgZGUgbG9zIGRhdG9zIGNvbiBsb3MgbGltaXRlcyBkZSBsYSBjYWphIHkgZWwgbcOheGltbyB5IGVsIG3DrW5pbW8gKGxvcyBsaW1pdGVzIGRlIGxhIGxpbmVhIHZlcnRpY2FsKS4gQWRpY2lvbmFsbWVudGUsIHNpIGV4aXN0ZW4gb3V0bGllcnMsIGVzdG9zIHRhbWJpw6luIHNlIHJlcHJlc2VudGFyw6FuLgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTcGVjaWVzLCAgeSA9IFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9ib3hwbG90KCkgCmBgYAoKU2UgcHVlZGVuIGNhbWJpYXIgYWxndW5hcyBvcGNpb25lcyBkZWwgZ3LDoWZpY28KCmBgYHtyfQpwIDwtIGdncGxvdChpcmlzLCBhZXMoeCA9IFNwZWNpZXMsICB5ID0gU2VwYWwuTGVuZ3RoKSkgCnAgKyBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBTcGVjaWVzKSwgb3V0bGllci5jb2xvdXIgPSAicHVycGxlIikKYGBgCgoKQSB2ZWNlcywgcGFyYSBtZWpvcmFyIGxhIHZpc3VhbGl6YWNpw7NuLCBjb252aWVuZSByb3RhciBsb3MgZWplcy4gUG9kZW1vcyBoYWNlcmxvIGNvbiBgY29vcmRfZmxpcCgpYAoKYGBge3J9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyh4ID0gU3BlY2llcywgIHkgPSBTZXBhbC5MZW5ndGgpKSArIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IFNwZWNpZXMpKSAKcCArIGNvb3JkX2ZsaXAoKQpgYGAKCgpQYXJhIG1lam9yYXIgbGEgdmlzdWFsaXphY2nDs24gcG9kZW1vcyBpbmNsdWlyIGxhcyBvYnNlcnZhY2lvbmVzIG9yaWdpbmFsZXMgY29uIGBnZW9tX3BvaW50KClgLCBwZXJvIGhhYnLDrWEgbXVjaG8gb3ZlcnBsb3R0aW5nLCBhc8OtIHF1ZSBtZWpvciBjb24gYGdlb21faml0dGVyKClgCgoKYGBge3J9CnAgKyBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMTUsIGFscGhhID0gMS80LCBjb2xvciA9ICJ0b21hdG8iKQpgYGAKClBvZGVtb3MgYcOxYWRpciBjb24gYHN0YXRzKClgIGFsZ8O6biBlc3RhZMOtc3RpY28gbcOhcyBjb24gYHN0YXRzX3h4KClgLCBwb3IgZWplbXBsbyBsYSBtZWRpYSBjb24gYHN0YXRzX3N1bW1hcnlgOgoKCmBgYHtyfQpwICsgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gIm1lYW4iLCBnZW9tID0gInBvaW50IiwgY29sb3IgPSAicHVycGxlIiwgc2l6ZSA9IDIuNSkKYGBgCgpMb3MgYm94cGxvdHMgcmVzdW1lbiBsYSBkaXN0cmlidWNpw7NuIGRlIHVuYSB2YXJpYWJsZSBjdWFudGl0YXRpdmEgY29uIHPDs2xvIGNpbmNvIG7Dum1lcm9zLCBwcm9wb3JjaW9uYW5kbyB1biByZXN1bWVuIMO6dGlsIGRlIGxvcyBkYXRvcywgcGVybyBvY3VsdGFuIGxhIGZvcm1hIGRlIGxhIGRpc3RyaWJ1Y2nDs247IHBvciBlamVtcGxvLCBzaSBsYSBkaXN0cmlidWNpw7NuIGZ1ZXNlIGJpbW9kYWwsIG5vIGxvIGFwcmVjaWFyw61hbW9zLiBBZGVtw6FzLCBhdW5xdWUgdXNlbW9zIGBnZW9tX2ppdHRlcigpYCBwYXJhIHN1cGVycG9uZXIgbG9zIHZhbG9yZXMgb3JpZ2luYWxlcywgYSB2ZWNlcyAgZXMgZGlmw61jaWwgYSBzaW1wbGUgdmlzdGEgaW5mZXJpciBsYSBkaXN0cmlidWNpw7NuIGRlIGVzdG9zLiBQb3IgZWxsbyBoYXkgdmFyaWFzIHTDqWNuaWNhcy9nZW9tcyDDunRpbGVzIHF1ZSBheXVkYW4gYSByZXNvbHZlciBlbCBwcm9ibGVtYSwgcG9yIGVqZW1wbG8gYGdlb21fdmlvbGluKClgLiBVbmEgYWx0ZXJuYXRpdmEgYWwgYm94cGxvdCBzb24gbG9zIGdyw6FmaWNvcyBkZSB2aW9sw61uIGRvbmRlIHNlIGVzdGltYSB5IG11ZXN0cmEgbGEgZm9ybWEgZGUgbGEgZGlzdHJpYnVjacOzbiBkZSBsYXMgb2JzZXJ2YWNpb25lczoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyh4ID0gU3BlY2llcywgIHkgPSBTZXBhbC5MZW5ndGgpKSAKcCArIGdlb21fdmlvbGluKGFlcyhmaWxsID0gU3BlY2llcyksIGFscGhhID0gMC42KQpwICsgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSBTcGVjaWVzKSwgYWxwaGEgPSAwLjYpICsgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjE1LCBhbHBoYSA9IDEvNCkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjgwJSJ9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyh4ID0gU3BlY2llcywgIHkgPSBTZXBhbC5MZW5ndGgpKSAKcDEgPC0gcCArIGdlb21fdmlvbGluKGFlcyhmaWxsID0gU3BlY2llcyksIGFscGhhID0gMC42KQpwMiA8LSBwICsgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSBTcGVjaWVzKSwgYWxwaGEgPSAwLjYpICsgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjE1LCBhbHBoYSA9IDEvNCkKcDEgKyBwMiArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCjxicj4KCkVuIGVsIGVqZW1wbG8gcXVlIGhlbW9zIHVzYWRvIGhlbW9zIHRlbmlkbyBzdWVydGUgeSBsb3MgdHJlcyBncnVwb3MgZXN0YWJhbiBvcmRlbmFkb3MsIHBlcm8gb3RyYXMgdmVjZXMgbm8gdGVuZHJlbW9zIHRhbnRhIHN1ZXJ0ZSwgcG9yIGVqZW1wbG8gc2kgZW4gbHVnYXIgZGUgZ3JhZmljYXIgbGEgbG9uZ2l0dWQgZGUgc8OpcGFsbyBxdWVyZW1vcyB2aXN1YWxpemFyIGxhIGFuY2h1cmEgKFNlcGFsLldpZHRoKToKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTcGVjaWVzLCAgeSA9IFNlcGFsLldpZHRoKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCsK/Q8OzbW8gb3JkZW5hbW9zIGxvcyBncnVwb3M/IFBvciBlamVtcGxvIHBvZGVtb3Mgb3JkZW5hcmxvcyBkZSBtZW5vciBhIG1heW9yIGVuIGZ1bmNpw7NuIGRlIHN1IGFuY2h1cmEgbWVkaWEgZGVsIHPDqXBhbG8uIFBhcmEgZWxsbyB1c2Ftb3MgYHN0YXRzOjpyZW9yZGVyKClgCgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoeCA9IHJlb3JkZXIoU3BlY2llcywgU2VwYWwuV2lkdGgsIG1lYW4pLCAgeSA9IFNlcGFsLldpZHRoKSkgKyBnZW9tX2JveHBsb3QoKSArCiAgeGxhYigiRGUgbWVub3IgYSBtYXlvciBhbmNodXJhIGRlbCBzw6lwYWxvIikKYGBgCgoKCgoKICAKIyMjIDYuNCBHcsOhZmljb3MgZGUgYmFycmFzCgpMb3MgZ3LDoWZpY29zIGRlIGJhcnJhcyBzZSB1dGlsaXphbiBwYXJhIHJlcHJlc2VudGFyIHVuYSB2YXJpYWJsZSBjYXRlZ8OzcmljYSwgY29tbyBwb3IgZWplbXBsbyBTcGVjaWVzLCBvIHZhcmlhYmxlcyBjdWFudGl0YXRpdmFzIGRpc2NyZXRhcy4gU2UgcmVwcmVzZW50YW4gYmFycmFzIHZlcnRpY2FsZXMgcHJvcG9yY2lvbmFsZXMgYSBsb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBlbiBjYWRhIGNhdGVnb3LDrWEgbyB2YWxvci4gUGFyYSBjcmVhciBncsOhZmljb3MgZGUgYmFycmFzIGNvbiBnZ3Bsb3QyIHNlIHVzYSBgZ2VvbV9iYXIoKWAuCgpQYXJhIGhhY2VyIG51ZXN0cm8gcHJpbWVyIGdyw6FmaWNvIGRlIGJhcnJhcyBubyB2YW1vcyBhIHV0aWxpemFyIGBpcmlzYCBwb3JxdWUgZW4gbGEgw7puaWNhIHZhcmlhYmxlIGNhdGVnw7NyaWNhIChTcGVjaWVzKSByZXN1bHRhIHF1ZSBoYXkgNTAgbGlyaW9zIGVuIGNhZGEgdGlwbyBkZSBlc3BlY2llLiBVc2FyZW1vcyBlbCBkYXRhLmZyYW1lIGBtcGdgIGRlbCBwYXF1ZXRlIGdncGxvdDIgcXVlIGNvbnRpZW5lIDIzNCBvYnNlcnZhY2lvbmVzIHNvYnJlIGRpc3RpbnRvcyBtb2RlbG9zIGRlIGNvY2hlcyB5IHN1cyBjYXJhY3RlcsOtc3RpY2FzLiBBbGd1bm9zIGRlIGxvcyBlamVtcGxvcyBlc3TDoW4gc2FjYWRvcyBkZSBsYSBbd2ViIGRlIGdncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZW9tX2Jhci5odG1sKS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CnAgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkKcCArIGdlb21fYmFyKCkKcCArIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKyBjb29yZF9mbGlwKCkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjk1JSIsIGZpZy5hc3AgPSA2Lzl9CnAgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkKcDEgPC0gcCArIGdlb21fYmFyKCkKcDIgPC0gcCArIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKyBjb29yZF9mbGlwKCkKcDEgKyBwMiArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCgpDb21vIHZlbW9zLCBsYSB2YXJpYWJsZSBgY2xhc3NgIGVzIGNhdGVnw7NyaWNhLCBjb25jcmV0YW1lbnRlIHRpZW5lIDcgZ3J1cG9zIG8gY2F0ZWdvcsOtYXMgZGUgY29jaGVzLiBMYXMgYmFycmFzIHZlcnRpY2FsZXMgc29uIHByb3BvcmNpb25hbGVzIGFsIG7Dum1lcm8gZGUgdmVow61jdWxvcyBlbiBjYWRhIGNhdGVnb3LDrWEuIENvbiBgY29vcmRfZmxpcCgpYCBsYXMgY2F0ZWdvcsOtYXMgcGFzYW4gYSByZXByZXNlbnRhcnNlIGVuIGVsIGVqZSBZLCBoYWNpZW5kbyBxdWUsIGdlbmVyYWxtZW50ZSwgc2UgdmlzdWFsaWNlbiBtZWpvciBsb3Mgbm9tYnJlcyBkZSBsYXMgY2F0ZWdvcsOtYXMuCgoKCjxicj4KClNpIGVuIGx1Z2FyIGRlIHRlbmVyIHVuYSB0YWJsYSBkZSBkYXRvcywgdGVuZW1vcyB5YSB1bmEgKip0YWJsYSBkZSBmcmVjdWVuY2lhcyoqLCB0ZW5kcmVtb3MgcXVlIGVzcGVjaWZpY2FyIGVuIGBhZXMoKWAgcXVlIGxhIHZhcmlhYmxlIGNvbiBsYXMgZnJlY3VlbmNpYXMgc2UgbWFwZWUvYXNvY2llIGFsIGVqZSBZOiBgYWVzKHggPSB2YXJpYWJsZSwgeSA9IGZyZWN1ZW5jaWFzKWAuIEFkZW3DoXMgdGVuZHLDoXMgcXVlIHVzYXIgYGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKWAgbyBkaXJlY3RhbWVudGUgdXNhciBgZ2VvbV9jb2woKWAgcXVlIHlhIHVzYSBwb3IgZGVmZWN0byBzdGF0X2lkZW50aXR5KCkuIFZlw6Ftb3NsbyBjb24gdW4gZWplbXBsbzoKCgpgYGB7cn0KZGYgPC0gbXBnICU+JSBncm91cF9ieShjbGFzcykgJT4lIGNvdW50CnAgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGNsYXNzLCB5ID0gbikpCnAgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgIGZpbGwgPSAic3RlZWxibHVlIikgCiMgcCArIGdlb21fY29sKGZpbGwgPSAic3RlZWxibHVlIikgICAgICAgICAgICAgICAgICAgICAgICAgICMtIGhhY2UgZXhhY3RhbWVudGUgZWwgbWlzbW8gcGxvdApgYGAKCjxicj4KCiMjIyMgRGlzdGludGFzIHBvc2ljaW9uZXMgcGFyYSBsYXMgYmFycmFzCgpQYXJhIGVzdGEgc3Vic2VjY2nDs24gdXRpbGl6YXJlbW9zIGVsIGRhdGEuZnJhbWUgYG10Y2Fyc2AKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKCkgIy0gcG9zCmdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKQpnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpCmdncGxvdChtdGNhcnMsIGFlcyhmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3Rvcih2cykpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UyKHByZXNlcnZlID0gInNpbmdsZSIpKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiMTAwJSIsICBmaWcuYXNwID0gNS85fQpwMSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2JhcigpICMtIHBvcwpwMiA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikKcDMgPC0gZ2dwbG90KG10Y2FycywgYWVzKGZhY3RvcihjeWwpLCBmaWxsID0gZmFjdG9yKHZzKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQpwNCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoZmFjdG9yKGN5bCksIGZpbGwgPSBmYWN0b3IodnMpKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHByZXNlcnZlID0gInNpbmdsZSIpKQoKcDEgKyBwMiArIHAzICsgcDQgKyBwbG90X2xheW91dChuY29sID0gMikKYGBgCgoKPGJyPgoKIyMjIyBSZW9yZGVuYW5kbyBsYXMgY2F0ZWdvcsOtYXMKCkEgdmVjZXMgZXMgaW1wb3J0YW50ZSByZW9yZGVuYXIgbGFzIGJhcnJhcyBwYXJhIG1lam9yYXIgbGEgdmlzdWFsaXphY2nDs24uIFBhcmEgZWxsbyB0ZW5kcmVtb3MgcXVlIHVzYXIgKipmYWN0b3JlcyoqLiBQb3IgZWplbXBsbywgdm9sdmllbmRvIGFsIGBtcGdgIGRhdGFzZXQ6IAoKCmBgYHtyfQpkZiA8LSBtcGcKZGYgPC0gZGYgJT4lIG11dGF0ZShjbGFzcyA9IGZvcmNhdHM6OmFzX2ZhY3RvcihjbGFzcykpICMtIGNvbnZlcnRpbW9zIGxhIHYuIGNsYXNzIGEgZmFjdG9yIGNvbiBsYSBmLiBhc19mYWN0b3IoKQpkZiA8LSBkZiAlPiUgbXV0YXRlKGNsYXNzID0gZm9yY2F0czo6ZmN0X2luZnJlcShjbGFzcykpICMtIGZjdF9pbmZyZXEoKSBsb3Mgbml2ZWxlcyBkZWwgZmFjdG9yIHNlZ8O6biBzdSBmcmVjdWVuY2lhIGRlIG1heW9yIGEgbWVub3IKcCA8LSBnZ3Bsb3QoZGYsIGFlcyhmY3RfcmV2KGNsYXNzKSkpICMtIGZjdF9yZXYoKSBvcmRlbmEgbG9zIGxldmVscyBkZSBtZW5vciBhIG1heW9yCnAgKyBnZW9tX2JhcihmaWxsID0gInN0ZWVsYmx1ZSIpICsgY29vcmRfZmxpcCgpCmBgYAoKPGJyPgoKU2kgcXVlcmVtb3MgcXVlIGVuIGxhcyBiYXJyYXMgc2UgdmlzdWFsaWNlIGVsIG7Dum1lcm8gZGUgb2JzZXJ2YWNpb25lcyBvIGZyZWN1ZW5jaWEgYWJzb2x1dGEgZGUgY2FkYSBjYXRlZ29yw61hOgoKCmBgYHtyfQpwICsgZ2VvbV9iYXIoZmlsbCA9ICJzdGVlbGJsdWUiKSArIGNvb3JkX2ZsaXAoKSArCiAgICBnZW9tX3RleHQoc3RhdD0nY291bnQnLCBhZXMobGFiZWwgPSAuLmNvdW50Li4gKSwgaGp1c3QgPSAtMC4xNSwgc2l6ZSA9IDMuMjUpIApgYGAKCgoKPGJyPgoKIyMjIyBQb3JjZW50YWplcyBlbiBsdWdhciBkZSBjb3VudHMKIApTaSBxdWVyZW1vcyBxdWUgbGFzIGJhcnJhcyByZXByZXNlbnRlbiBwb3JjZW50YWplcyBlbiBsdWdhciBkZSBudW1lcm8gZGUgY2Fzb3MgZW4gY2FkYSBjYXRlZ29yw61hOgoKCmBgYHtyfQpwIDwtIGdncGxvdChkZiwgYWVzKGZjdF9yZXYoY2xhc3MpKSkgIy0gZmN0X3JldigpIG9yZGVuYSBsb3MgbGV2ZWxzIGRlIG1lbm9yIGEgbWF5b3IKcCArIGdlb21fYmFyKGFlcyggeSA9ICguLmNvdW50Li4pL3N1bSguLmNvdW50Li4pKSwgZmlsbCA9ICJzdGVlbGJsdWUiKSAgKyBjb29yZF9mbGlwKCkKYGBgCgoKRW4gcmVhbGlkYWQgZXN0byBtaXNtbyBzZSBwdWRlIGhhY2VyIGRlIHZhcmlhcyBmb3JtYXMuIEVuIFtlc3RlIHBvc3RdKGh0dHBzOi8vc2ViYXN0aWFuc2F1ZXIuZ2l0aHViLmlvL3BlcmNlbnRhZ2VfcGxvdF9nZ3Bsb3QyX1YyLykgbm9zIG11ZXN0cmFuIDUgZm9ybWFzIGRlIGhhY2VybG8uCgoKPGJyPgogU2kgcXVpZXJlcyBzYWJlciBtw6FzIHNvYnJlIGNvbW8gaGFjZXIgZ3LDoWZpY29zIGRlIGJhcnJhcyBjb24gZ2dwbG90MiwgcHVlZGVzIGhhY2VybG8gW2FxdcOtXSgKaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC93aWtpL2dncGxvdDItYmFycGxvdHMtcXVpY2stc3RhcnQtZ3VpZGUtci1zb2Z0d2FyZS1hbmQtZGF0YS12aXN1YWxpemF0aW9uCikgbyBbYXF1w61dKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vNDgtZ3JvdXBlZC1iYXJwbG90LXdpdGgtZ2dwbG90Mi5odG1sKS4KCgo8YnI+CgoKIyMjIDYuNSBHcsOhZmljb3MgZGUgbGluZWFzCgpMb3MgZ3LDoWZpY29zIGRlIGxpbmVhcyBzZSB1c2FuIHByaW5jaXBhbG1lbnRlIHBhcmEgbW9zdHJhciBsYSBldm9sdWNpw7NuIGRlIHZhcmlhYmxlcyBlbiBlbCB0aWVtcG8uIEdlbmVyYWxtZW50ZSBsYSB2YXJpYWJsZSBxdWUgc2UgbXVlc3RyYSBlbiBlbCBlamUgWCBlcyBlbCB0aWVtcG8uIEVuIEVjb25vbcOtYSBlc3RvcyBncsOhZmljb3MgcHVlZGUgcXVlIHNlYW4gbG9zIG3DoXMgdXNhZG9zIHkgaGFiaXR1YWxtZW50ZSBub3MgcmVmZXJpbW9zIGEgZWxsb3MgY29tbyBncsOhZmljb3MgdGVtcG9yYWxlcy4KCgpQb2RlbW9zIHNpbXVsYXIgZ3LDoWZpY29zIGRlIHRpZW1wbyBjb24gYGdlb21fbGluZSgpYCB5IGBnZW9tX3BhdGgoKWAuIEVzdG9zIDIgZ2VvbXMgZ3JhZmljYW4gbGluZWFzIGVudHJlIGRvcyBvYnNlcnZhY2lvbmVzIGRlIHVuYSB2YXJpYWJsZS4gUG9yIGVqZW1wbG86CgoKYGBge3J9CmdncGxvdChlY29ub21pY3MsIGFlcyhkYXRlLCB1ZW1wbWVkKSkgKyBnZW9tX2xpbmUoKQpgYGAKCgpFbXBlY2Vtb3MgdXNhbmRvIGVsIGNvbmp1bnRvIGRlIGRhdG9zIGBnYXBtaW5kZXJgIHBhcmEgbW9zdHJhciBsYXMgb2JzZXJ2YWNpb25lcyBkZSBsYSB2YXJpYWJsZSBsaWZlRXhwIHBhcmEgRXNwYcOxYToKCmBgYHtyfQpsaWJyYXJ5KGdhcG1pbmRlcikKZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSA9PSAiU3BhaW4iKSAlPiUgCmdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBsaWZlRXhwKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoKQpgYGAKCgpWaXN1YWxpY2Vtb3MgYWhvcmEgNCBzZXJpZXMgZGUgdGllbXBvLCBsYSBlc3BlcmFuemEgZGUgdmlkYSBlbiBjdWF0cm8gcGHDrXNlcyBldXJvcGVvcy4gUGFyYSBwb2RlciBkaXN0aW5ndWlyIGxhcyBsaW5lYXMgZGUgY2FkYSBwYcOtcyBwb2RlbW9zIGFzb2NpYXIgbGEgdmFyaWFibGUgY291bnRyeSBhIGxhIGNhcmFjdGVyw61zdGljYSBlc3TDqXRpY2EgY29sb3I6IGBhZXMoY29sb3IgPSBjb3VudHJ5KWAKCmBgYHtyfQpnYXBtaW5kZXIgJT4lIGZpbHRlcihjb3VudHJ5ICVpbiUgYygiU3BhaW4iLCAiRnJhbmNlIiwgIk5vcndheSIsICJCZWxnaXVtIikpICU+JSAKZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHAsIGNvbG9yID0gY291bnRyeSkpICsgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkKYGBgCgpVbmEgdG9udGVyw61hIHBlcm8gcXVlIHB1ZWRlIHNlciBkZSB1dGlsaWRhZCBwYXJhIHNhYmVyIGN1YW50YSBkaWZlcmVuY2lhIGhheSBhbCBmaW5hbCBkZWwgcGxvdC4gTG8gYXByZW5kw60gW2FxdcOtXShodHRwczovL2Ryc2ltb25qLnN2YnRsZS5jb20vbGFiZWwtbGluZS1lbmRzLWluLXRpbWUtc2VyaWVzLXdpdGgtZ2dwbG90MikKCgpgYGB7cn0KZGYgPC0gZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIlNwYWluIiwgIkZyYW5jZSIsICJOb3J3YXkiLCAiQmVsZ2l1bSIpKSAKbGlmZUV4cF9lbmRzIDwtIGRmICU+JSBncm91cF9ieShjb3VudHJ5KSAlPiUgdG9wX24oMSwgeWVhcikgJT4lIHB1bGwobGlmZUV4cCkgIy0gdmVjdG9yIGNvbiBsb3MgdmFsb3JlcyDDumx0aW1vcyBkZSBsaWZlRXhwCmdncGxvdChkZiwgYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCwgY29sb3IgPSBjb3VudHJ5KSkgKyBnZW9tX2xpbmUoKSArIAogICAgIHNjYWxlX3lfY29udGludW91cyhzZWMuYXhpcyA9IHNlY19heGlzKH4gLiwgYnJlYWtzID0gbGlmZUV4cF9lbmRzKSkgKyAgICMtIHNlY19heGlzKCkgZXNwZWNpZmljYSB1biBlamUgc2VjdW5kYXJpbwogICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKQpgYGAgCgo8YnI+CgpFbCBzaWd1aWVudGUgZWplbXBsbyBlc3TDoSBzYWNhZG8gZGUgW2VzdGUgbGlicm9dKGh0dHBzOi8vZWRhdi5pbmZvL3RpbWVzZXJpZXNiYXNpYy5odG1sKSBhw7puIGVuIGNvbnN0cnVjY2nDs24uIFV0aWxpemEgZWwgcGFxdWV0ZSBbdGlkeXF1YW50XShodHRwczovL2dpdGh1Yi5jb20vYnVzaW5lc3Mtc2NpZW5jZS90aWR5cXVhbnQpIHBhcmEgZGVzY2FyZ2FyIGxhcyBjb3RpemFjaW9uZXMgZGUgbGFzIDQgcHJpbmNpcGFsZXMgZW1wcmVzYXMgdGVjbm9sw7NnaWNhcywgbGFzIEdBRkEuIGB0aWR5cXVhbnRgIHRpZW5lIG11Y2hhcyBmdW5jaW9uZXMgaW50ZXJlc2FudGVzLCBwdWVkZXMgdmVybGFzIGNvcnJpZW5kbyBgdHFfdHJhbnNtdXRlX2Z1bl9vcHRpb25zKClgLgoKYGBge3J9CmxpYnJhcnkodGlkeXF1YW50KQpzdG9ja3MgPC0gYygiR09PR0wiLCJBTVpOIiwiRkIiLCJBQVBMIikgIy0gc2VsZWNjaW9uYW1vcyBhIGxhcyBHQUZBcwpkZiA8LSB0cV9nZXQoc3RvY2tzLCBmcm9tID0gYXMuRGF0ZSgiMjAxMy0wMS0wMSIpLCB0byA9IGFzLkRhdGUoIjIwMTMtMTItMzEiKSkKCmdncGxvdChkZiwgYWVzKGRhdGUsIHkgPSBjbG9zZSwgY29sb3IgPSBmY3RfcmVvcmRlcjIoc3ltYm9sLCBkYXRlLCBjbG9zZSkpKSArCiAgZ2VvbV9saW5lKCkgKyB4bGFiKCIiKSArIHlsYWIoIiIpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKUmVlc2NhbGFtb3MgcGFyYSBxdWUgbGFzIHNlcmllcyBjb21pZW5jZW4gdG9kYXMgZW4gMTAwCgpgYGB7cn0KZGYgPC0gZGYgJT4lIGdyb3VwX2J5KHN5bWJvbCkgJT4lIG11dGF0ZShyZXNjYWxlZF9jbG9zZSA9IDEwMCpjbG9zZSAvIGNsb3NlWzFdKQoKZ2dwbG90KGRmLCBhZXMoZGF0ZSwgeSA9IHJlc2NhbGVkX2Nsb3NlLCBjb2xvciA9IGZjdF9yZW9yZGVyMihzeW1ib2wsIGRhdGUsIHJlc2NhbGVkX2Nsb3NlKSkpICsKICBnZW9tX2xpbmUoKSArIHhsYWIoIiIpICsgeWxhYigiIikgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgo8YnI+CgpPdHJvIHBhcXVldGUgbXV5IMO6dGlsIHBhcmEgYW5hbGl6YXIgeSBkZXNjYXJnYXIgZGF0b3MgZmluYW5jaWVyb3MgZXMgW2BxdWFudG1vZGBdKGh0dHA6Ly93d3cucXVhbnRtb2QuY29tLykuIEVsIGVqZW1wbG8gc2lndWllbnRlIGxvIGhlIHNhY2FkbyBkZSBbZXN0ZSBmYW50YXN0aWNvIGxpYnJvXShodHRwczovL3NtYWMtZ3JvdXAuZ2l0aHViLmlvL2RzL2RhdGEuaHRtbCNleGFtcGxlLWFwcGxlLXN0b2NrLXByaWNlKS4KCgpgYGB7cn0KbGlicmFyeShxdWFudG1vZCkKdG9kYXkgPC0gU3lzLkRhdGUoKQp0aHJlZV9tb250aHNfYWdvIDwtIHNlcSh0b2RheSwgbGVuZ3RoID0gMiwgYnkgPSAiLTMgbW9udGhzIilbMl0KZ2V0U3ltYm9scygiQUFQTCIsIGZyb20gPSB0aHJlZV9tb250aHNfYWdvLCB0byA9IHRvZGF5KQpjYW5kbGVDaGFydChBQVBMLCB0aGVtZSA9ICd3aGl0ZScsIHR5cGUgPSAnY2FuZGxlcycpCmBgYAoKPGJyPgoKU2kgcXVpZXJlcyBzYWJlciBtw6FzIHNvYnJlIGdyw6FmaWNvcyBkZSBsaW5lYXMgcHVlZGVzIGlyIFthcXXDrV0oaHR0cHM6Ly9taWNoYWVsdG90aC5tZS9hLWRldGFpbGVkLWd1aWRlLXRvLXBsb3R0aW5nLWxpbmUtZ3JhcGhzLWluLXItdXNpbmctZ2dwbG90LWdlb21fbGluZS5odG1sKS4KCgojIyMjIyBVbiBwb2NvIHNvYnJlIGRhdG9zIHRlbXBvcmFsZXMKClBhcmEgdHJhYmFqYXIgY29uIGRhdG9zIHRlbXBvcmFsZXMsIFIgdGllbmUgZGlzdGludG9zIHBhcXVldGVzIHkgZXN0cnVjdHVyYXMsIHBlcm8gcGFyYSBtYW5pcHVsYXIgZmVjaGFzIHkgIGRhdG9zIHRlbXBvcmFsZXMgZW4gZWwgdGlkeXZlcnNlIHRlbmVtb3MgZWwgcGFxdWV0ZSBbYGx1YnJpZGF0ZWBdKGh0dHBzOi8vbHVicmlkYXRlLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2x1YnJpZGF0ZS1wYWNrYWdlLmh0bWwpLiBQYXJhIHVuYSBpbnRyb2R1Y2Npw7NuIGEgZmVjaGFzIHkgdGllbXBvIGVuIFIgW2FxdcOtXShodHRwczovL2RlcmVrc29uZGVyZWdnZXIuZ2l0aHViLmlvLzU3MEwvMTQtZGF0ZXMtYW5kLXRpbWVzLmh0bWwpLgoKCkVuIGdlbmVyYWwsIHBhcmEgcmVhbGl6YXIgYW7DoWxpc2lzIGVzdGFkw61zdGljb3MgZW4gUiBjb24gc2VyaWVzIGRlIHRpZW1wbyBzZSBuZWNlc2l0YSBxdWUgbG9zIGRhdG9zIGVzdMOpbiBlbiBtYXRyaWNlcywgcGVybyBub3NvdHJvcyBlbiBlbCBjdXJzbyBlc3RhbW9zIHRyYWJhamFuZG8gY29uIGVsIHRpZHl2ZXJzZSwgeSBsYSBsYSBlc3RydWN0dXJhIGRlIGRhdG9zIHVzYWRhIHNvbiBsb3MgZGF0YWZyYW1lcyBvIHRpYmJsZXMsIGVzdG8gZXJhIHVuIHByb2JsZW1hLCBwZXJvIHJlY2llbnRlbWVudGUsIGVsIHBhcXVldGUgW2B0c2liYmxlYF0oaHR0cHM6Ly90c2liYmxlLnRpZHl2ZXJ0cy5vcmcvKSBoYSBleHRlbmRpZG8gZWwgdGlkeXZlcnNlIHkgbGFzIHRpYmJsZXMgYSBsb3MgZGF0b3MgdGVtcG9yYWxlcywgY3JlYW5kbyB1bmEgbnVldmEgZXN0cnVjdHVyYSBkZSBkYXRvczogbGFzICJ0c2liYmxlcyIuIAoKQXBvecOhbmRvc2UgZW4gZXN0YSBudWV2YSBlc3RydWN0dXJhIGRlIGRhdG9zLCBsYXMgInRzaWJibGVzIiwgZWwgcGFxdWV0ZSBbYGZlYXN0YF0oaHR0cDovL2ZlYXN0cy50aWR5dmVydHMub3JnLykgcHJvcG9yY2lvbmEgbGFzIGhlcnJhbWllbnRhcyB5IGZ1bmNpb25lcyBuZWNlc2FyaWFzIHBhcmEgdHJhYmFqYXIgY29uIHNlcmllcyB0ZW1wb3JhbGVzIGVuIHVuIGVudG9ybm8gdGlkeS4gYGZlYXN0YCBlcyB1biBhY3LDs25pbW8gZGUgRmVhdHVyZSBFeHRyYWN0aW9uIEFuZCBTdGF0aXN0aWNzIGZvciBUaW1lIFNlcmllcy4gUGFyYSB1bmEgaW50cm9kdWNjacOzbiBhIGBmZWFzdGAgcHVlZGVzIGlyIFthcXXDrV0oaHR0cHM6Ly9ibG9nLm1pdGNoZWxsb2hhcmF3aWxkLmNvbS9ibG9nL2ZlYXN0cy8pIG8gW2FxdcOtXShodHRwczovL3JvYmpoeW5kbWFuLmNvbS9oeW5kc2lnaHQvZmVhc3RzLykuIAoKQXNpbWlzbW8sIGVsIHBhcXVldGUgW2BmYWJsZWBdKGh0dHA6Ly9mYWJsZS50aWR5dmVydHMub3JnLykgdGFtYmnDqW4gdXRpbGl6YSAidHNpYmJsZXMiIHkgcHJvcG9yY2lvbmEgIGhlcnJhbWllbnRhcyBwYXJhIGxhIHByZWRpY2Npw7NuIGRlIHNlcmllcyB0ZW1wb3JhbGVzLCBpbmNsdXllbmRvIG1vZGVsb3MgQVJJTUEgZW4gZWwgZW50b3JubyB0aWR5dmVyc2UuIFBhcmEgdW5hIGludHJvZHVjY2nDs24gYSBgZmFibGVgIHB1ZWRlcyBpciBbYXF1w61dKGh0dHBzOi8vYmxvZy5taXRjaGVsbG9oYXJhd2lsZC5jb20vYmxvZy9mYWJsZS8pLgoKW0FxdcOtXShodHRwczovL3Jlc291cmNlcy5yc3R1ZGlvLmNvbS9yc3R1ZGlvLWNvbmYtMjAxOS9tZWx0LXRoZS1jbG9jay10aWR5LXRpbWUtc2VyaWVzLWFuYWx5c2lzKSBwdWVkZXMgdmVyIHVuYSBjb25mZXJlbmNpYSBlbiBsYSBxdWUgc2UgZXhwbGljYSBsYXMgcHJpbmNpcGFsZXMgaWRlYXMgZGUgZXN0YSBudWV2YSBmb3JtYSBkZSB0cmFiYWphciBjb24gc2VyaWVzIHRlbXBvcmFsZXMgZW4gZWwgdGlkeXZlcnNlIAoKPGJyPgoKCiMjIDcuIE3DoXMgZGV0YWxsZXMvY29zYXMKCgojIyMgNy4xIGdlb21fc21vb3RoKCkKCllhIGhlbW9zIHVzYWRvIGBnZW9tX3Ntb290aCgpYC4gVW4gYXJndW1lbnRvIGltcG9ydGFudGUgZGUgYGdlb21fc21vb3RoKClgIGVzIGBtZXRob2RgIGNvbiBlbCBxdWUgc2Ugc2VsZWNjaW9uYSBlbCBtw6l0b2RvICBjb24gZWwgcXVlIHNlIG9idGllbmUgbGEgc21vb3RoIGN1cnZlLiBFbCBtw6l0b2RvIHBvciBkZWZlY3RvIGVzIGBtZXRob2QgPSAibG9lc3MiYC4gUHVlZGVzIGNvbnRyb2xhciBlbCBuaXZlbCBkZSBzbW9vdGhpbmcgY29uIGVsIHBhcmFtZXRybyAic3BhbiIsIHF1ZSB2YSBkZSAwICB0byAxIChtYXlvciBzdWF2aXphZG8pLgoKT3RyYXMgb3BjaW9uZXMgcGFyYSBnZW9tX3Ntb290aCgpOgoKLSBtZXRob2QgPSAibG0iIGZpdHMgYSBsaW5lYXIgbW9kZWwsIGdpdmluZyB0aGUgbGluZSBvZiBiZXN0IGZpdC4gCgotIG1ldGhvZCA9ICJybG0iIHdvcmtzIGxpa2UgbG0oKSwgYnV0IHVzZXMgYSByb2J1c3QgZml0dGluZyBhbGdvcml0aG0gc28gdGhhdCBvdXRsaWVycyBkb27igJl0IGFmZmVjdCB0aGUgZml0IGFzIG11Y2guIEl04oCZcyBwYXJ0IG9mIHRoZSBNQVNTIHBhY2thZ2UsIHNvIHJlbWVtYmVyIHRvIGxvYWQgdGhhdCBmaXJzdC4gCgotIHN0YXRfc21vb3RoKHNlID0gRkFMU0UpICwgaW5kaWNhIHNpIHNlIHJlcHJlc2VudGFuIGludGVydmFsb3MgZGUgY29uZmlhbnphIHBhcmEgbGEgbGluZWEgc3Vhdml6YWRhLgoKQWRlbcOhcyBkZSBsb3MgbcOpdG9kb3MgaW1wbGVtZW50YWRvcywgcG9kZW1vcyBlbGVnaXIgbnVlc3RybyBwcm9waW8gbcOpdG9kbywgeWEgc2VhIHVzYW5kbyBlbCBhcmd1bWVudG8gImZvcm11bGEiIG8gZGVmaW5pZW5kbyBudWVzdHJvIHByb3BpbyBtw6l0b2RvIGRlIGFsaXNhZG8gY29tbyBub3MgY3VlbnRhbiBbYXF1w61dKGh0dHBzOi8vZWxpb2NhbXAuZ2l0aHViLmlvL2NvZGlnby1yLzIwMTgvMDYvdHUtcHJvcGlvLWdlb20tc21vb3RoLykgCgoKYGBge3J9CnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgCiAgICAgZ2VvbV9wb2ludCgpIApwICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiBwb2x5KHgsNCkpCmBgYAoKClBvZGVtb3MsIG9idmlhbWVudGUsIGNvbXBhcmFyIGRvcyBtw6l0b2RvcyBkZSBhbGlzYWRvLiBQYXJhIHBvbmVyIG5vbWJyZSBhIGxvcyBkaWZlcmVudGVzIG3DqXRvZG9zIHNlIHB1ZWRlIGhhY2VyIGxvIHNpZ3VpZW50ZTogCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCkpICsgCiAgICAgZ2VvbV9wb2ludCgpCnAgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSAibG9lc3MiKSAsIG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UpICsgCiAgICBnZW9tX3Ntb290aChhZXMoY29sb3IgPSAibG0iKSAgICAsIG1ldGhvZCA9ICJsbSIgICAsIHNlID0gRkFMU0UpCmBgYAoKCjxicj4KCiMjIyA3LjIgw4FyZWFzIGJham8gbGEgY3VydmEKClBhcmEgdW4gcHJvZmVzb3IgZGUgZXN0YWTDrXN0aWNhL2Vjb25vbWV0csOtYSBlcyBpbXBvcnRhbnRlIHNhYmVyIGhhY2VyIGdyw6FmaWNvcyBjb21vIGVzdG9zOgoKbGlicmFyeSh0aWR5dmVyc2UpCmdncGxvdCh4eCwgYWVzKHgpKSArCiAgICBnZW9tX2Z1bmN0aW9uKGZ1biA9IGZ1bmN0aW9uKHgpIHt4KiozfSAsICBjb2xvdXIgPSAicmVkIikKCmdncGxvdCh4eCwgYWVzKHgpKSArCiAgICBnZW9tX2Z1bmN0aW9uKGZ1biA9IH4gLngqKjMgLCAgY29sb3VyID0gInJlZCIpCgoKCmBgYHtyLCBldmFsID0gRkFMU0V9Cnh4IDwtIGRhdGEuZnJhbWUoeCA9IGMoLTIsIDIpKQoKZ2dwbG90KHh4LCBhZXMoeCkpICsgZ2VvbV9mdW5jdGlvbihmdW4gPSBmdW5jdGlvbih4KSB7eCoqM30gLCAgY29sb3VyID0gInJlZCIpCgpnZ3Bsb3QoeHgsIGFlcyh4KSkgKyBnZW9tX2Z1bmN0aW9uKGZ1biA9IH4gLngqKjMgLCAgY29sb3VyID0gInJlZCIpCgpnZ3Bsb3QoeHgsIGFlcyh4KSkgKyBzdGF0X2Z1bmN0aW9uKGZ1biA9IGZ1bmN0aW9uKHgpIHsgeCoqMyB9LCBnZW9tID0gImxpbmUiKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiNDUlIn0KeHggPC0gZGF0YS5mcmFtZSh4ID0gYygtMiwgMikpCmdncGxvdCh4eCwgYWVzKHgpKSArIGdlb21fZnVuY3Rpb24oZnVuID0gZnVuY3Rpb24oeCkge3gqKjN9ICwgIGNvbG91ciA9ICJyZWQiKQpgYGAKCmBgYHtyLCBldmFsID0gVFJVRSwgb3V0LndpZHRoID0gIjQ1JSJ9CmdncGxvdCh4eCwgYWVzKHgpKSArIGdlb21fZnVuY3Rpb24oZnVuID0gZG5vcm0pICAKYGBgCgpgYGB7ciwgZXZhbCA9IFRSVUUsIG91dC53aWR0aCA9ICI0NSUifQpnZ3Bsb3QoeHgsIGFlcyh4ID0geCkpKwogIGdlb21fZnVuY3Rpb24oZnVuID0gZG5vcm0sIHhsaW0gPSBjKC00LCAwKSkgKwogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGdlb20gPSAiYXJlYSIsIGZpbGwgPSAic3RlZWxibHVlIiwgeGxpbSA9IGMoMCwgNCkpICsKICB4bGltKC01LCA1KQpgYGAKCkxvIGFwcmVuZMOtIFthcXXDrV0oaHR0cHM6Ly9jaHJpc3RpYW5idXJraGFydC5kZS9ibG9nL2FyZWFfdW5kZXJfdGhlX2N1cnZlLykuCgpJbnNwaXJhZG8gcG9yIEZyYW4gTW9yaWxsYXMsIG90cm8gZWplbXBsbzoKCmBgYHtyLCAsIG91dC53aWR0aCA9ICI1MCUifQpubiA8LSAzMDAgOyBlc3BlcmFuemEgPC0gMTA7IHNkIDwtIDYKZGF0b3MgPC0gZGF0YS5mcmFtZShub3JtYWwgPSBybm9ybShubiwgZXNwZXJhbnphLCBzZCkpCgpnZ3Bsb3QoZGF0b3MsIGFlcyhub3JtYWwpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IGFmdGVyX3N0YXQoZGVuc2l0eSkpLCBiaW5zID0gMzApICsKICAgIGdlb21fZGVuc2l0eShjb2xvciA9ICJibHVlIikgKwogICAgZ2VvbV9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QoZXNwZXJhbnphLCBzZCksIGNvbG91ciA9ICJyZWQiKQpgYGAKCgpBdW5xdWUsIGxhIHZlcmRhZCwgaG95IGVuIGRpYSBoYXkgcGFxdWV0ZXMgZW4gUiBwYXJhIHRvZG8uIFBvciBlamVtcGxvIGVsIHBhcXVldGUgW2BuaHN0cGxvdGBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9uaHN0cGxvdC92aWduZXR0ZXMvbmhzdHBsb3QuaHRtbCkKCgpgYGB7ciwgb3V0LndpZHRoID0gIjUwJSJ9CmxpYnJhcnkobmhzdHBsb3QpCnBsb3R0dGVzdCh0ID0gMS45NiwgZGYgPSAxMDAwICwgdGFpbHMgPSAib25lIikKYGBgCgoKPGJyPgoKCiMjIyA3LjMgTGFiZWxsaW5nIGxhcyBvYnNlcnZhY2lvbmVzIGNvbiBnZ3JlcGVsCgpFbCBncsOhZmljbyBkZSBhYmFqbyBzZSBiYXNhIGVuIFtlc3RlIHBvc3RdKGh0dHBzOi8vanVsaWFzaWxnZS5jb20vYmxvZy9sdWJyaWRhdGUtbG9uZG9uLXN0YWdlLykgZGUgSnVsaWEgU2lsZ2UuIFV0aWxpemEgZWwgcGFxdWV0ZSBbYGdncmVwZWxdKGh0dHBzOi8vZ2l0aHViLmNvbS9zbG93a293L2dncmVwZWwpIHBhcmEgcG9kZXIgdmVyIGEgcXVpZW4gcGVydGVuZWNlIGxhIG9ic2VydmFjacOzbiBlbiB1biBncsOhZmljbyBkZSBwdW50b3MuCgpgYGB7cn0KbGlicmFyeShnZ3JlcGVsKQpkZiA8LSBnYXBtaW5kZXI6OmdhcG1pbmRlciAlPiUgZmlsdGVyKHllYXIgPT0gIjIwMDciKSAgJT4lIGZpbHRlcihjb250aW5lbnQgPT0gIkV1cm9wZSIpCmdncGxvdChkZiwgYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCwgbGFiZWwgPSBjb3VudHJ5KSkgKyBnZW9tX3BvaW50KCkgKwogICAgIGxhYnModGl0bGUgPSAiR3LDoWZpY28gMTogRXNwZXJhbnphIGRlIHZpZGEgZnJlbnRlIGEgUElCIHBlciBjw6FwaXRhIiAsCiAgICAgICBjYXB0aW9uID0gIkRhdG9zIHByb3ZlbmllbnRlcyBkZSBnYXBtaW5kZXIiLAogICAgICAgeSA9ICJsaWZlRXhwIiwKICAgICAgIHggPSAiZ2RwUGVyY2FwIikgKyBnZW9tX3Ntb290aCgpICsKICAgICAgICBnZW9tX2xhYmVsX3JlcGVsKCkgCmBgYAoKCgo8YnI+CgojIyMgNy40IGBHR2FsbHlgIHBhY2thZ2UKCkhheSBtdWNob3MgcGFxdWV0ZXMgcXVlIHByb3BvcmNpb25hbiBmb3JtYXMgcsOhcGlkYXMgZGUgaGFjZXIgZ3LDoWZpY29zIGNvbW8gcG9yIGVqZW1wbG8gZXN0ZSBkZWwgcGFxdWV0ZSBbYEdHYWxseWBdKGh0dHBzOi8vZ2dvYmkuZ2l0aHViLmlvL2dnYWxseS9pbmRleC5odG1sKToKCmBgYHtyfQpsaWJyYXJ5KEdHYWxseSkKZ2dwYWlycyhpcmlzKQpnZ3BhaXJzKGlyaXMgJT4lIHNlbGVjdCgxOjQpICU+JSBuYS5vbWl0KCksIHByb2dyZXNzID0gRkFMU0UsIGxvd2VyID0gbGlzdChjb21ibyA9IHdyYXAoImZhY2V0aGlzdCIsIGJpbnM9NikpKQpgYGAKCjxicj4KCgojIyMgNy41IFVuIHBvY28gbcOhcyBkZSBhbm90YWNpb25lcyAKClBhcmEgcXVlIHVuIGdyw6FmaWNvIHNlYSBlZmVjdGl2byB5IG5vcyBoYWdhIHZlciBhbGfDum4gaGVjaG8gbyBjYXJhY3RlcsOtc3RpY2EgZGUgbG9zIGRhdG9zLCBtdWNoYXMgdmVjZXMgZXMgcHJlY2lzbyBoYWNlciBhbm90YWNpb25lcyBlbiBlbCBncsOhZmljbyBwYXJhIHNlw7FhbGFyIG8gcmVzYWx0YXIgY2llcnRvcyBhc3BlY3RvcyBkZSBlc3RlLiBFbiBlc3RhIHN1YnNlY2Npw7NuIHByZXNlbnRvIGFsZ8O6biBlamVtcGxvIGRlIHVzbyBkZSBsYXMgYW5vdGFjaW9uZXMgZW4gZ3LDoWZpY29zIGdncGxvdC4KCgotIEVuIHByaW1lciBsdWdhciB1biBlamVtcGxvIGRlIEhhZGxleS4gSGFkbGV5IG5vcyBkaWNlIHF1ZSBubyBoYXkgbmFkYSBub3ZlZG9zbyBleGNlcHRvIGVsIHVzbyBkZSAtSW5mIGFuZCBJbmYgY29tbyBwb3NpY2lvbmVzIHBhcmEgcmVmZXJpcnNlIGEgbG9zIGzDrW1pdGVzIGRlbCBncsOhZmljbywgdGhlIHRvcCBhbmQgYm90dG9tIChvciBsZWZ0IGFuZCByaWdodCkgbGltaXRzIG9mIHRoZSBwbG90LgoKYGBge3J9CnByZXNpZGVudGlhbCA8LSBzdWJzZXQocHJlc2lkZW50aWFsLCBzdGFydCA+IGVjb25vbWljcyRkYXRlWzFdKQoKcCA8LSBnZ3Bsb3QoZWNvbm9taWNzKSArICBnZW9tX2xpbmUoYWVzKGRhdGUsIHVuZW1wbG95KSkgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpKSArCiAgeGxhYigiZGF0ZSIpICsgCiAgeWxhYigidW5lbXBsb3ltZW50IikKCiMtIGNvbWllbnphbiBsYXMgYW5vdGFjaW9uZXMgIApwICsgZ2VvbV9yZWN0KGFlcyh4bWluID0gc3RhcnQsIHhtYXggPSBlbmQsIGZpbGwgPSBwYXJ0eSksIAogICAgICAgICAgICAgIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mLCBhbHBoYSA9IDAuMiwgZGF0YSA9IHByZXNpZGVudGlhbCkgKyAKICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBhcy5udW1lcmljKHN0YXJ0KSksIAogICAgICAgICAgICAgICBkYXRhID0gcHJlc2lkZW50aWFsLCBjb2xvdXIgPSAiZ3JleTUwIiwgYWxwaGEgPSAwLjUpICsgCiAgICBnZW9tX3RleHQoYWVzKHggPSBzdGFydCwgeSA9IDI1MDAsIGxhYmVsID0gbmFtZSksIAogICAgICAgICAgICAgICBkYXRhID0gcHJlc2lkZW50aWFsLCBzaXplID0gMywgdmp1c3QgPSAwLCBoanVzdCA9IDAsIG51ZGdlX3ggPSA1MCkgCmBgYAoKClVuYSBmb3JtYSBjb23Dum4gZGUgYW5vdGFjacOzbiBjb25zaXN0ZSBlbiBtYXJjYXIgbyBzdWJyYXlhciB1biBjb25qdW50byBkZSBwdW50b3MuIFBvciBlamVtcGxvIG1hcmNhciBsb3MgY29jaGVzIGRlIGxhIG1hcmNhIFN1YmFydSBlbiBlbCBzaWd1aWVudGUgZ3LDoWZpY28uIFNlIHB1ZWRlIGhhY2VyIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6IAoKCmBgYHtyfQpwIDwtIGdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGZpbHRlcihtcGcsIG1hbnVmYWN0dXJlciA9PSAic3ViYXJ1IiksIGNvbG91ciA9ICJvcmFuZ2UiLCBzaXplID0gMykgKwogIGdlb21fcG9pbnQoKSAKcApgYGAKCgpDb21vIHZlcywgbG8gcXVlIHNlIGhhIGhlY2hvIGVzIHN1cGVycG9uZXIgdW5hIGNhcGEgY29uIHNvbG8gbGFzIG9ic2VydmFjaW9uZXMgZGUgU3ViYXJ1IHkgZ3JhZmljYXJsYXMgY29uIHVuIHB1bnRvIG3DoXMgZ3JhbmRlIGRlIGxvIGhhYml0dWFsIHkgY29uIHVuIGNvbG9yIGxsYW1hdGl2by4gRWwgcHJvYmxlbWEgZXMgcXVlIHNlIHZlIHF1ZSBzZWFuIGxhcyBvYnNlcnZhY2lvbmVzIGRlIFN1YmFydS4gRXN0byBzZSBwdWVkZSByZXNvbHZlciBkZSB2YXJpYXMgbWFuZXJhcyB1c2FuZG8gYW5ub3RhdGUoKToKCgpgYGB7cn0KcCArIGFubm90YXRlKGdlb20gPSAicG9pbnQiLCB4ID0gNS41LCB5ID0gNDAsIGNvbG91ciA9ICJvcmFuZ2UiLCBzaXplID0gMykgKyAKICAgIGFubm90YXRlKGdlb20gPSAicG9pbnQiLCB4ID0gNS41LCB5ID0gNDApICsgCiAgICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gNS42LCB5ID0gNDAsIGxhYmVsID0gInN1YmFydSIsIGhqdXN0ID0gImxlZnQiKQpgYGAKCk8gZGUgZXN0YSBvdHJhIGZvcm1hOgoKYGBge3J9CnAgKyBhbm5vdGF0ZShnZW9tID0gImN1cnZlIiwgeCA9IDQsIHkgPSAzNSwgeGVuZCA9IDIuNjUsIHllbmQgPSAyNywgCiAgICAgICAgICAgICAgY3VydmF0dXJlID0gLjMsIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgyLCAibW0iKSkpICsKICAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IDQuMSwgeSA9IDM1LCBsYWJlbCA9ICJzdWJhcnUiLCBoanVzdCA9ICJsZWZ0IikKYGBgCgoKRWwgcGFxdWV0ZSBbYGdnZm9yY2VgXShodHRwczovL2dnZm9yY2UuZGF0YS1pbWFnaW5pc3QuY29tLykgZXMgdW5hIGV4dGVuc2nDs24gYSBnZ3Bsb3QyIHF1ZSBwdWVkZSBzZXJ2aXIgcGFyYSBtdWNoYXMgY29zYXMsIGVudHJlIGVsbGFzIGhhY2VyIGFub3RhY2lvbmVzIG8gbWFyY2FzIGVuIGdyw6FmaWNvcyBnZ3Bsb3QuIFBvciBlamVtcGxvOgoKYGBge3J9CmdncGxvdChpcmlzLCBhZXMoU2VwYWwuTGVuZ3RoLCBQZXRhbC5MZW5ndGgpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTcGVjaWVzKSkgKyAKICBnZ2ZvcmNlOjpnZW9tX21hcmtfZWxsaXBzZShhZXMobGFiZWwgPSBTcGVjaWVzLCBncm91cCA9IFNwZWNpZXMpKQpgYGAKCgpDb24gYGdnZm9yY2VgIHBvZGVtb3MgaGFzdGEgc2ltdWxhciB1biB6b29tOgoKCmBgYHtyfQpnZ3Bsb3QoaXJpcywgYWVzKFBldGFsLkxlbmd0aCwgUGV0YWwuV2lkdGgsIGNvbG91ciA9IFNwZWNpZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZ2ZvcmNlOjpmYWNldF96b29tKHggPSBTcGVjaWVzID09ICJ2ZXJzaWNvbG9yIikKYGBgCgo8YnI+CgpFbCBwYXF1ZXRlL2V4dGVuc2nDs24gYGdnZm9yY2VgIGVzIHByZXR0eSBhd2Vzb21lLiBFbiBbZXN0ZSBwb3N0XShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE5LzA5LzE5L2ludHJvLXRvLWdnZm9yY2UpIHB1ZWRlIHZlcmxvIGVuIGFjY2nDs24gaGFjaWVuZG8gbWFwYXMuCgo8YnI+CgpPdHJvIGVuZm9xdWUgcGFyYSBoYWNlciBhbm90YWNpb25lcywgYnVlbm8sIGVuIHJlYWxpZGFkIGNlbnRyYXNlIGVuIHVuIGdydXBvIGRlIG9ic2VydmFjaW9uZXMgZXMgdXRpbGl6YXIgZWwgcGFxdWV0ZSBbYGdnaGlnaGxpZ2h0YF0oaHR0cHM6Ly9naXRodWIuY29tL3l1dGFubmloaWxhdGlvbi9nZ2hpZ2hsaWdodCk6CgoKYGBge3J9CmRmIDwtIGdhcG1pbmRlcjo6Z2FwbWluZGVyICU+JSBmaWx0ZXIoY29udGluZW50ID09ICJFdXJvcGUiKQpnZ3Bsb3QoZGYsIGFlcyh5ZWFyLCBsaWZlRXhwLCBncm91cCA9IGNvdW50cnkpKSArIAogIGdlb21fbGluZSgpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2doaWdobGlnaHQ6OmdnaGlnaGxpZ2h0KGNvdW50cnkgJWluJSBjKCJTcGFpbiIsICJQb3J0dWdhbCIpKQpgYGAKCgpgYGB7cn0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBhcy5mYWN0b3IoU3BlY2llcykpKSArCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2doaWdobGlnaHQ6OmdnaGlnaGxpZ2h0KCkgKyAKICBmYWNldF93cmFwKHZhcnMoU3BlY2llcykpCmBgYAoKCgoKCjxicj4KCgojIyA4LiBBc2lzdGVudGVzIHBhcmEgZ2dwbG90MgoKQWhvcmEgbWlzbW8gZXhpc3RlbiAyIGFzaXN0ZW50ZXMgcGFyYSBjcmVhciBncsOhZmljb3MgZ2dwbG90IGEgdHJhdsOpcyBkZSBpbnRlcmZhY2VzIGdyw6FmaWNhczogW2BnZ1RoZW1lQXNzaXN0YF0oaHR0cHM6Ly9naXRodWIuY29tL2NhbGxpZ3Jvc3MvZ2d0aGVtZWFzc2lzdCkgeSBbYGVzcXVpc3NlYF0oaHR0cHM6Ly9kcmVhbXJzLmdpdGh1Yi5pby9lc3F1aXNzZS9pbmRleC5odG1sKSAKCi0gRWwgcGFxdWV0ZSBbYGdnVGhlbWVBc3Npc3RgXShodHRwczovL2dpdGh1Yi5jb20vY2FsbGlncm9zcy9nZ3RoZW1lYXNzaXN0KSBmYWNpbGl0YSBtZWRpYW50ZSB1biBhZGRpbmcgZGUgUlN0dWRpbyBsYSBlZGljacOzbiBkZSBsb3MgZGV0YWxsZXMgZGUgdW4gZ3LDoWZpY287IGVzIGRlY2lyLCBwdWVkZXMgY29tZW56YXIgaGFjaWVuZG8gdW4gZ3LDoWZpY28gYsOhc2ljbyBlbiBSU3R1ZGlvLCBwYXJhIGRlc3B1w6lzIGFicmlyIGxhIGludGVyZmF6IGRlIGBnZ1RoZW1lQXNzaXN0YCBwYXJhIG1vZGlmaWNhciBjb24gZWwgYXNpc3RlbnRlIHZpc3VhbCB0b2RvcyBsb3MgZWxlbWVudG9zIGVzdMOpdGljb3MgZGUgZ3LDoWZpY28gY29tbyB0w610dWxvcywgbGV5ZW5kYXMsIGNvbG9yZXMgZXRjLi4uIGV0Yy4uLgoKICBQYXJhIGVsbG8sIHByaW1lcm8gaGFzIGRlIGluc3RhbGFyIGVsIHBhcXVldGUgY29uIGBpbnN0YWxsLnBhY2thZ2VzKCJnZ1RoZW1lQXNzaXN0IilgLCBkZXNwdcOpcyBjYXJnYXJsbyBjb24gYGxpYnJhcnkoZ2dUaGVtZUFzc2lzdClgLiBVbmEgdmV6IGhhcyBjYXJnYWRvIGVsIHBhcXVldGUgZW4gbWVtb3JpYSBoYXMgZGUgc2VsZWNjaW9uYXIgY29uIGVsIGN1cnNvciBlbCBjw7NkaWdvIHF1ZSBnZW5lcmEgZWwgZ2dwbG90IHF1ZSBxdWllcmVzIG1vZGlmaWNhci90dW5lYXIuIFVuYSB2ZXogdGllbmVzIG1hcmNhZG8gY29uIGVsIHJhdMOzbiBlbCBjw7NkaWdvIGRlbCBncsOhZmljbywgaGFzIGRlIHNlZ3VpciwgZW4gUlN0dWRpbywgZXN0YSBydXRhIGRlIG1lbsO6czogYFRvb2xzID4gQWRkaW5ncyA+IEJyb3dzZSBBZGRpbmdzIC4uLmAsIHBhcmEgc2VsZWNjaW9uYXIgZmluYWxtZW50ZSBlbCBhZGRpbmcgbGxhbWFkbyAiZ2dwbG90IFRoZW1lIEFzaXN0YW50Ii4KCiAgU2UgYWJyaXLDoSB1biBpbnRlcmZheiBkb25kZSBwb2Ryw6FzIG1vZGlmaWNhciBsYSBtYXlvcsOtYSBkZSBlbGVtZW50b3MgZGVsIGdyw6FmaWNvLiBDdWFuZG8gaGF5YXMgZGVqYWRvIGVsIGdyw6FmaWNvIGEgdHUgZ3VzdG8gcGluY2hhcyBlbiBgRE9ORWAgeSB0ZSBkZXZvbHZlcsOhIGVsIGPDs2RpZ28gcXVlIHJlcHJvZHVjZSBlbCBncsOhZmljbyB0YWwgeSBjb21vIGxvIGVsZWdpc3RlIGVuIGVsIGludGVyZmF6LgoKICBQb2TDqWlzIHZlciB1biBlamVtcGxvIGVuIDxodHRwczovL2dpdGh1Yi5jb20vY2FsbGlncm9zcy9nZ3RoZW1lYXNzaXN0PgoKCjxicj4KCi0gRWwgcGFxdWV0ZSBbYGVzcXVpc3NlYF0oaHR0cHM6Ly9kcmVhbXJzLmdpdGh1Yi5pby9lc3F1aXNzZS9pbmRleC5odG1sKSBwZXJtaXRlIGNyZWFyIGdyw6FmaWNvcyBnZ3Bsb3QgZGVzZGUgY2VybyBjb24gdW5hIGludGVyZmF6IGdyw6FmaWNhLgoKICBQYXJhIHVzYXJsbyB0aWVuZXMgcXVlIGluc3RhbGFybG8gY29uIGBpbnN0YWxsLnBhY2thZ2VzKCJlc3F1aXNzZSIpYCBwYXJhIGx1ZWdvIGFicmlybG8gc2lndWllbmRvIGVzdGEgcnV0YSBkZSBtZW7DunMgZW4gUlN0dWRpbzogYFRvb2xzID4gQWRkaW5ncyA+IEJyb3dzZSBBZGRpbmdzIC4uLmAgcGFyYSBlbGVnaXIgZWwgYWRkaW5nIGNvbiBub21icmU6ICJlc3F1aXNzZSAtIGdncGxvdCBidWlsZGVyIi4KCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmFzcCA9IDcvOX0KdHdlZXRybWQ6OnR3ZWV0X2VtYmVkKCJodHRwczovL3R3aXR0ZXIuY29tL1dlQXJlUkxhZGllcy9zdGF0dXMvMTEzOTU5MTc2MzQzMjIyMjcyMyIsIHRoZW1lID0gImxpZ2h0IiwgYWxpZ24gPSAiY2VudGVyIiwgbWF4d2lkdGggPSA0MDApCmBgYAoKCjxicj4KCiMjIDkuIEdyw6FmaWNvcyBpbnRlcmFjdGl2b3MKCkxvcyBncsOhZmljb3MgaW50ZXJhY3Rpdm9zLCBjb21vIHN1IG5vbWJyZSBpbmRpY2EsIHBlcm1pdGUgYWwgdXN1YXJpbyBpbnRlcmFjdHVhciBjb24gZWwgZ3LDoWZpY28sIGFicmnDqW5kb3NlIHBvc2liaWxpZGFkZXMgY29tbyBjZW50cmFyc2UgZW4gcGFydGUgZGVsIGdyw6FmaWNvICh6b29taW5nKSwgaGlnaGxpZ2h0aW5nLCBvIG1vc3RyYXIgaW5mb3JtYWNpw7NuIGFkaWNpb25hbCBhbCBwaW5jaGFyIGVuIGFsZ8O6biBlbGVtZW50byBkZWwgZ3LDoWZpY28sIGV0YyAuLi4KCkVuIGdlbmVyYWwsIEphdmFTY3JpcHQgKEpTKSBlcyBlbCBsZW5ndWFqZSB1dGlsaXphZG8gcGFyYSBoYWNlciBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGNvbiBsaWJyZXLDrWFzIGNvbW8gRDMsIENoYXJ0LCBQbG90bHksIFZpcyBIaWdoY2hhcnRzLCAuLi4KClJlY2llbnRlbWVudGUsIGVsIHBhcXVldGUgZGUgUiBbYGh0bWx3aWRnZXRzYF0oaHR0cDovL3d3dy5odG1sd2lkZ2V0cy5vcmcvKSBoYSBmYWNpbGl0YWRvIGVsIHVzbyBkZSBsYXMgbGlicmVyw61hcyBkZSBKUyBlbiBSLiBBY3R1YWxtZW50ZSwgcGFxdWV0ZXMgZGUgUiwgY29tbyBsZWFmbGV0LCBEVCwgZHlncmFwaHMsIG5ldHdvcmtEMyB5IG11Y2hvcyBvdHJvcywgdXRpbGl6YW4gZWwgZnJhbWV3b3JrIHByb3B1ZXN0byBwb3IgaHRtbHdpZGdldHMgcGFyYSBoYWNlciBkaXNwb25pYmxlcyBsb3MgZ3LDoWZpY29zIGludGVyYWN0aXZvcyBkZSBKUyBlbiBSLgoKClBhcmEgZGFyb3MgY3VlbnRhIGRlIGxvIGbDoWNpbCBxdWUgZXMgaGFjZXIgdW4gZ3LDoWZpY28gaW50ZXJhY3Rpdm8gY29uIFIgdXNhcmVtb3MgZWwgcGFxdWV0ZSBbYHBsb3RseWBdKGh0dHBzOi8vcGxvdC5seS9yLykgcXVlIGhhY2UgcG9zaWJsZSB1c2FyIGxhIGxpYnJlcsOtYSBwbG90bHkuanMgZW4gUi4gQ29uIGBwbG90bHlgIHNlIHB1ZWRlbiBoYWNlciBtdWNob3MgdGlwb3MgZGUgZ3LDoWZpY29zLCBwZXJvIHBvciBlamVtcGxvLCBwZXJtaXRlIGNvbiB1bmEgc29sYSBsaW5lYSBjb252ZXJ0aXIgdW4gZ3LDoWZpY28gZ2dwbG90IGVuIGludGVyYWN0aXZvOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShwbG90bHkpCnAgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFBldGFsLkxlbmd0aCwgY29sb3IgPSBTcGVjaWVzKSkgKyAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKQpnZ3Bsb3RseShwKQpgYGAKCltBcXXDrV0oaHR0cHM6Ly9wbG90bHktci5jb20vcHJlZmFjZS5odG1sKSBwdWVkZXMgZW5jb250cmFhciB1biBib29rZG93biBzb2JyZSBwbG90bHkuCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcDEgPC0gcCArIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoU3BlY2llcykpIApnZ3Bsb3RseShwMSkKYGBgCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCA8LSBnZ3Bsb3QobXBnLCBhZXMoY2xhc3MpKSArICBnZW9tX2JhcihmaWxsID0gInN0ZWVsYmx1ZSIpICsgY29vcmRfZmxpcCgpCmdncGxvdGx5KHApCmBgYAoKCltBcXXDrV0oaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS9pbnRlcmFjdGl2ZS1jaGFydHMuaHRtbCkgcHVlZGVzIHZlciB1biBnYWxlcsOtYSBkZSBlamVtcGxvcyBkZSBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGhlY2hvcyBjb24gUi4gW0FxdcOtXShodHRwczovL21vZGVybmRhdGEucGxvdC5seS9pbnRlcmFjdGl2ZS1yLXZpc3VhbGl6YXRpb25zLXdpdGgtZDMtZ2dwbG90Mi1yc3R1ZGlvLykgdW4gcG9zdCwgeWEgZGUgMjAxNSwgcGFyYSBpbmljaWFyc2UgdW4gcG9jbyBlbiBlc3RvcyB0ZW1hcy4gCgoKSGF5IG11Y2hvcyBwYXF1ZXRlcyBxdWUgcGVybWl0ZW4gaGFjZXIgZ3LDoWZpY29zIGludGVyYWN0aXZvcyBlbiBSLCBwb3IgZWplbXBsbyBbYGxlYWZsZXRgXShodHRwOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pLCBxdWUgcGVybWl0ZSBoYWNlciBtYXBhcyBpbnRlcmFjdGl2b3MgbXV5IGbDoWNpbG1lbnRlLgoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGxlYWZsZXQpCm0gPC0gbGVhZmxldCgpCm0gPC0gYWRkVGlsZXMobSkKbSA8LSBhZGRNYXJrZXJzKG0sIGxuZyA9IDE3NC43NjgsIGxhdCA9LTM2Ljg1MiwgcG9wdXAgPSAiVGhlIGJpcnRocGxhY2Ugb2YgUiIpCm0KYGBgCgoKVG9kb3MgZXN0b3MgcGFxdWV0ZXNeW0VuIGVsIG1vbWVudG8gZGUgZXNjcmliaXIgZXN0YXMgbm90YXMgZXJhbiAxMDcgcGFxdWV0ZXNdIHF1ZSBwZXJtaXRlbiBoYWNlciBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGEgdHJhdsOpcyBkZSBgaHRtbHdpZGdldHNgIHNlIHB1ZWRlbiBjb25zdWx0YXIgZW46IDxodHRwOi8vZ2FsbGVyeS5odG1sd2lkZ2V0cy5vcmcvPi4gRG9zIHBhcXVldGVzIHF1ZSBubyBlc3TDoW4gZW4gbGEgZ2FsbGVyeTogW2BBcGV4Y2hhcnRzYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2FwZXhjaGFydGVyL3ZpZ25ldHRlcy9zdGFydGluZy13aXRoLWFwZXhjaGFydHMuaHRtbCkgeSBbYFRTcGxvdGx5YF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1RTcGxvdGx5L3ZpZ25ldHRlcy9UU3Bsb3RseS5odG1sKS4KCgpFbiBbSW50ZXJhY3RpdmUgd2ViLWJhc2VkIGRhdGEgdmlzdWFsaXphdGlvbiB3aXRoIFIsIHBsb3RseSwgYW5kIHNoaW55XShodHRwczovL3Bsb3RseS1yLmNvbS8pIGV4cGxpY2FuIG3DoXMgZGV0YWxsYWRhbWVudGUgY29tbyBoYWNlciBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGVuIFIuIFRlbmRyw6kgcXVlIHJlbGVlciBzdSBzZWNjacOzbiBbU2F2aW5nIGFuZCBlbWJlZGRpbmcgSFRNTF0oaHR0cHM6Ly9wbG90bHktci5jb20vc2F2aW5nLmh0bWwpIHBhcmEgbW9zdHJhciBlbiBlbCB0dXRvcmlhbCBhbGd1bm8gZGUgbG9zIGdyw6FmaWNvcyBkaW7DoW1pY29zIHF1ZSBoZSBoZWNobywgcGVybyBhaG9yYSB0ZW5nbyBwcmlzYSwgbGEgcHLDs3hpbWEgY2xhc2UgZXMgcHJvbnRvIHkgZW1wZXphbW9zIHPDrSBvIHPDrSBnZ3Bsb3QyLgoKSGFkbGV5IHRhbWJpw6luIGVzdMOhIGVzY3JpYmllbmRvIHVuIGJvb2tkb3duIHNvYnJlIFNoaW55OiBbTWFzdGVyaW5nIFNoaW55XShodHRwczovL21hc3RlcmluZy1zaGlueS5vcmcvKS4gRW4gcGFsYWJyYXMgZGUgSGFkbGV5OiBTaGlueSBpcyBhIGZyYW1ld29yayBmb3IgY3JlYXRpbmcgd2ViIGFwcGxpY2F0aW9ucyB1c2luZyBSIGNvZGUuIFBhcmEgdmVyIHF1ZSBzaWduaWZpY2EgZXN0byBwdWVkZXMgdmVyIGxhIHNpZ3VpZW50ZSBbZ2FsZXLDrWEgY29uIGVqZW1wbG9zIGRlIGFwbGljYWNpb25lcyBzaGlueV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5LykuCgoKIyMjIGdnYW5pbWF0ZSAKCltgZ2dhbmltYXRlYF0oaHR0cHM6Ly9nZ2FuaW1hdGUuY29tLykgZXMgdW4gcGFxdWV0ZSBxdWUgbm8gaGFjZSBncsOhZmljb3MgZGluw6FtaWNvcywgcGVybyBwZXJtaXRlIGFuaW1hciBncsOhZmljb3MgbWVkaWFudGUgbGEgY3JlYWNpw7NuIGNyZWFjacOzbiBkZSBzZWN1ZW5jaWFzIGRlIGdyw6FmaWNvcy4gTWVqb3IgcXVlIGV4cGxpY2FybG8gdmlzaXRhIHN1IHNlY2Npw7NuIGRlIGVqZW1wbG9zIGVuIHN1IHdpa2k6IDxodHRwczovL2dpdGh1Yi5jb20vdGhvbWFzcDg1L2dnYW5pbWF0ZS93aWtpPi4gUG9yIGVqZW1wbG8gW2VzdGUgZWplbXBsb10oaHR0cHM6Ly9naXRodWIuY29tL3Rob21hc3A4NS9nZ2FuaW1hdGUvd2lraS9UcmFja2luZy1vZi1odXJyaWNhbmVzLWFuZC10eXBob29ucykgIG8gIFtlc3RlIGVqZW1wbG9dKGh0dHBzOi8vZ2l0aHViLmNvbS90aG9tYXNwODUvZ2dhbmltYXRlL3dpa2kvV29ybGQtQ3VwLUdvYWwtQW5pbWF0aW9uKSBkZSBqdXJnb2wuIEVuIFtlc3RlIHBvc3RdKGh0dHBzOi8vd3d3LmRhdGFub3ZpYS5jb20vZW4vYmxvZy9nZ2FuaW1hdGUtaG93LXRvLWNyZWF0ZS1wbG90cy13aXRoLWJlYXV0aWZ1bC1hbmltYXRpb24taW4tci8pIGV4cGxpY2FuIGNvbW8gdXNhcmxvLgoKClVuIGVqZW1wbG8gY29uIGxvcyBkYXRvcyBkZSBgZ2FwbWluZGVyYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2FwbWluZGVyKQpsaWJyYXJ5KGdnYW5pbWF0ZSkKZ2FwbWlkZXJfZXVyb3BlIDwtIGdhcG1pbmRlciAlPiUgZmlsdGVyKGNvbnRpbmVudCA9PSAiRXVyb3BlIikKZ2dwbG90KGdhcG1pZGVyX2V1cm9wZSwgYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCwgc2l6ZSA9IHBvcCwgY29sb3VyID0gY291bnRyeSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb3VudHJ5X2NvbG9ycykgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDEyKSkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArCiAgIyBIZXJlIGNvbWVzIHRoZSBnZ2FuaW1hdGUgc3BlY2lmaWMgYml0cwogIGxhYnModGl0bGUgPSAnWWVhcjoge2ZyYW1lX3RpbWV9JywgeCA9ICdHRFAgcGVyIGNhcGl0YScsIHkgPSAnbGlmZSBleHBlY3RhbmN5JykgKwogIHRyYW5zaXRpb25fdGltZSh5ZWFyKSArCiAgZWFzZV9hZXMoJ2xpbmVhcicpIApgYGAKCltBcXXDrV0oaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdGhvbWFzcDg1LzA1MTY5YWQ0NGRkY2M4ZWQ1NmRhNmZmN2JmN2ZiZTM2KSB0aWVuZXMgdW4gZWplbXBsbyBwYXJhIGhhY2VybG8gcGFyYSBsb3MgY2luY28gY29udGluZW50ZXMuIAoKPGJyPgoKCgojIyBCaWJsaW8vZWplbXBsb3MvcmVjdXJzb3MKCgpTb24gcmVjdXJzb3MgcXVlIGhlIHZpc3RvIG8gdXRpbGl6YWRvIG1pZW50cmFzIGVzY3JpYsOtYSBlc3RhIG5vdGFzLCB5IG5vIHF1aWVybyBvbHZpZGFybG9zOgoKLSBbQm9va2Rvd24gb2ZpY2lhbCBzb2JyZSBnZ3Bsb3QyXShodHRwczovL2dncGxvdDItYm9vay5vcmcvKQoKLSBbV2ViaW5hciBkZSBUaG9tYXMgTC4gUGVkZXJzZW4gc29icmUgZ2dwbG90Ml0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1oMjlnMjF6MGE2OCZmZWF0dXJlPXlvdXR1LmJlKQoKLSBbRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggUiBkZSBSLiBDYWJhY29mZl0oaHR0cHM6Ly9ya2FiYWNvZmYuZ2l0aHViLmlvL2RhdGF2aXMvaW5kZXguaHRtbCkuCgotIFtDw6lkcmljIFNjaGVyZXJdKGh0dHBzOi8vZ2l0aHViLmNvbS9aM3R0L1RpZHlUdWVzZGF5KTogdW5hIHNlcmllIGRlIHZpc3VhbGl6YWNpb25lcyBpbmNyZcOtYmxlcyBhc29jaWFkYXMgYWwgcHJveWVjdG8gdGlkeSBUdWVzZGF5LgoKLSBbZGF0YS10by12aXpdKGh0dHBzOi8vd3d3LmRhdGEtdG8tdml6LmNvbS8pOiB1bmEgcGxhbnRpbGxhIHF1ZSB0ZSBwdWVkZSBheXVkYXIgYSBkZWNpZGlyIHF1w6kgZ3LDoWZpY28gdXNhci4KCgotIFtyLXN0YXRpc3RpY3MuY29dKCBodHRwOi8vci1zdGF0aXN0aWNzLmNvL2dncGxvdDItVHV0b3JpYWwtV2l0aC1SLmh0bWwpOiB0dXRvcmlhbCBgZ2dwbG90MmAuCgoKLSBbQSBnZ3Bsb3QyIFR1dG9yaWFsIGZvciBCZWF1dGlmdWwgUGxvdHRpbmcgaW4gUl0oaHR0cHM6Ly9jZWRyaWNzY2hlcmVyLm5ldGxpZnkuY29tLzIwMTkvMDgvMDUvYS1nZ3Bsb3QyLXR1dG9yaWFsLWZvci1iZWF1dGlmdWwtcGxvdHRpbmctaW4tci8pOiB0dXRvcmlhbCBgZ2dwbG90MmAuCgotIFtUaGUgRXZvbHV0aW9uIG9mIGEgZ2dwbG90IChFcC4gMSldKGh0dHBzOi8vY2Vkcmljc2NoZXJlci5uZXRsaWZ5LmNvbS8yMDE5LzA1LzE3L3RoZS1ldm9sdXRpb24tb2YtYS1nZ3Bsb3QtZXAuLTEvKS4gVW4gcG9zdCBtdXkgZGlkw6FjdGljbyBkb25kZSBzZSB2YSB0cmFuc2Zvcm1hbmRvIHVuIGdyw6FmaWNvIGhhc3RhIGhhY2VybG8gbXV5IGNodWxvLgoKCi0gW0VEQVYgaW5mb10oaHR0cHM6Ly9lZGF2LmluZm8vYm94Lmh0bWwpLiBCb29rZG93biBhw7puIGVuIGNvbnN0cnVjY2nDs24gY29uIGJ1ZW5vcyBlamVtcGxvcy4KCgotIFtUb23igJlzIENvb2tib29rIGZvciBCZXR0ZXIgVml6XShodHRwczovL2p0aG9tYXNtb2NrLmdpdGh1Yi5pby9uZmxfcGxvdHRpbmdfY29va2Jvb2svI2xpY2Vuc2UpLiBVbiBidWVuIHR1dG9yaWFsIGNvbiBjb25zZWpvcyB5IGRlbcOhcyBzb2JyZSBncsOhZmljb3MgZ2dwbG90LiBQb3IgZWplbXBsbyBtaXJhIGxvcyAiVXNlZnVsIGNvZGUgY2h1bmtzIi4KCgotIFtnZ3Bsb3QyIFF1aWNrIFJlZmVyZW5jZTogZ2VvbV0oaHR0cDovL3NhcGUuaW5mLnVzaS5jaC9xdWljay1yZWZlcmVuY2UvZ2dwbG90Mi9nZW9tKS4gVW5hIGNoZWF0c2hlZXQgcGFyYSB2ZXIgbG9zIGdlb21zX3h4KCkgZGlzcG9uaWJsZXMKCi0gW0VtaSBUYW5ha2EgLSAxXShodHRwczovL2VtaXRhbmFrYS5vcmcvd29ya3Nob3BVVG9reW8yMDE4L2RheTEtc2Vzc2lvbjAyLWRhdGF2aXMuaHRtbCMxKS4gU29uIHVuYXMgdHJhbnBhcmVuY2lhcyBkZSBFbWkgVGFuYWthIGRvbmRlIGhhY2UgdW4gcmVwYXNvIGZhbnTDoXN0aWNvIGEgbGEgZ2dwbG90MiBncmFtbWFyLiBFbWkgVGFuYWthIGVzIGxvIG1lam9yIHBhcmEgY29ub2NlciBsYSB2ZXJkYWQgc29icmUgZ2dwbG90MiBlbiBwb2NvIHRpZW1wbzsgcG9yIGVqZW1wbG8gbGEgc2xpZGUgIzYuCgotIFtEYXRhIFZpc3VhbGl6YXRpb24gaW4gdGhlIFRpZHl2ZXJzZV0oaHR0cHM6Ly9hbGlzb24ubmV0bGlmeS5jb20vdW8tdGlkeS1iYWtlb2ZmLyMxKS4gT3RyYXMgZmFudMOhc3RpY2FzIHRyYW5zcGFyZW5jaWFzIHNvYnJlIGdncGxvdDIsIGVzdGEgdmV6IGRlIEFsaXNvbiBIaWxsLgoKCi0gW1ByZWd1bnRhcyBzb2JyZSBnZ3Bsb3QyIGVuIFN0YWNrb3ZlcmZsb3ddKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zL3RhZ2dlZC9nZ3Bsb3QyKS4gU2VndXJvcyBxdWUgZW5jdWVudHJhcyB0dSBwcmVndW50YSB5IGFsZ3VpZW4gbGEgaGEgcmVzcG9uZGlkbyEhCgoKLSBbRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggZ2dwbG90Ml0oaHR0cHM6Ly9jZW5nZWwuZ2l0aHViLmlvL1ItZGF0YS12aXovZGF0YS12aXN1YWxpemF0aW9uLXdpdGgtZ2dwbG90Mi5odG1sKS4gVW4gYm9va2Rvd24gc29icmUgZ2dwbG90Mi4gTm8gbG8gaGUgbGXDrWRvIHBlcm8gdGllbmUgYnVlbmEgcGludGEgcGFyYSBpbmljaWFyc2UgY29uIGdncGxvdC4KCgotIFtwcmFjdGljYWxnZ10oaHR0cHM6Ly93aWxrZWxhYi5vcmcvcHJhY3RpY2FsZ2cvKS4gVW4gcGFxdWV0ZSBjb24gZWplbXBsb3MgZGUgNiB2aXN1YWxpemFjaW9uZXMgZXhwbGljYWRhcyBwYXNvIGEgcGFzby4KCgotIFt2aXotcHViXShodHRwczovL2J1ZmYubHkvMm56TVpQWCkuIFVuIHJlcG8gZGUgR2l0aHViIGNvbiBncsOhZmljb3MgeSB2aXN1YWxpemFjaW9uZXMsIG9idmlhbWVudGUgY29uIGVsIGPDs2RpZ28uCgoKCi0gW1RoZSBnZ3Bsb3QgZmxpcGJvb2tdKGh0dHBzOi8vZXZhbWFlcmV5LmdpdGh1Yi5pby9nZ3Bsb3RfZmxpcGJvb2svZ2dwbG90X2ZsaXBib29rX3hhcmluZ2FuLmh0bWwjMSkuIE90cmFzIHRyYW5zcGFyZW5jaWFzIHNvYnJlIGdncGxvdDIsIGRlIEdpbmEgUmV5bm9sZHMuCgoKLSBbQXJ0aXN0aWMgY29kaW5nIGZvciB0aGUgdXNlUl0oaHR0cHM6Ly93d3cud2lsbGlhbXJjaGFzZS5jb20vcG9zdC9hcnRpc3RpYy1jb2RpbmctZm9yLXRoZS11c2VyLTEyLW1vbnRocy1vZi1hcnQtanVuZS8pLiBVbiBwb3N0IGRlIFdpbGwgQ2hhc2UgcGFyYSBpbnRyb2R1Y2lyc2UgZW4gZWwgbXVuZG8gZGUgaGFjZXIgYVJ0ZSBjb24gUi4gCgoKLSBbWW91IGNhbiByZXBsaWNhdGUgYWxtb3N0IGFueSBwbG90IHdpdGggUl0oaHR0cHM6Ly9zaW1wbHlzdGF0aXN0aWNzLm9yZy8yMDE5LzA4LzI4L3lvdS1jYW4tcmVwbGljYXRlLWFsbW9zdC1hbnktcGxvdC13aXRoLWdncGxvdDIvKS4gSW1wcmVzaW9uYW50ZSBwb3N0IGRlIFJhZmFlbCBJcml6YXJyeSBkb25kZSByZXBsaWNhIGNvbiBSIDUgdmlzdWFsaXphY2lvbmVzLgoKLSBbRGF0YSwgbW92aWVzIGFuZCBnZ3Bsb3QyXShodHRwOi8vc21hcnRlcnBvbGFuZC5wbC9pbmRleC5waHAvMjAxOC8xMi9kYXRhLW1vdmllcy1hbmQtZ2dwbG90Mi8pLiBVbm9zIHBvc3RlcnMgaW5jcmXDrWJsZXMgaGVjaG9zIGNvbiBSLgoKLSBMYSByZXZpc3RhIFsqKkJ1enpmZWRkKipdKGh0dHBzOi8vd3d3LmJ1enpmZWVkbmV3cy5jb20vKSBtYW50aWVuZSB1bmEgc2VyaWUgZGUgW3JlcG9zIGVuIEdpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL0J1enpGZWVkTmV3cy8pIGNvbiBlbCBjw7NkaWdvIHkgZGF0b3MgcGFyYSByZXBsaWNhciBzdXMgYW7DoWxpc2lzLiBFbiBjb25jcmV0bywgZW4gW2VzdGUgcmVwb10oaHR0cHM6Ly9naXRodWIuY29tL0J1enpGZWVkTmV3cy9ldmVyeXRoaW5nKSBoYXkgdW4gbGlzdGFkbyBkZSBhbsOhbGlzaXMuIENvbW8gZWplbXBsbywgW2VzdGUgZ3LDoWZpY29dKGh0dHBzOi8vdHdpdHRlci5jb20vcGFsZGhvdXMvc3RhdHVzLzExOTAwMjI2NjQ4MDg3ODM4NzIvcGhvdG8vMSksIGFwYXJlY2UgZW4gW2VzdGUgYXJ0w61jdWxvXShodHRwczovL3d3dy5idXp6ZmVlZG5ld3MuY29tL2FydGljbGUvbGFtdm8vc29jaWFsLXNlbnRpbmVsLXNjaG9vbC1vZmZpY2lhbHMtc2hvb3RpbmdzLWZsYWctc29jaWFsLW1lZGlhKSwgeSBlbCBjw7NkaWdvIHBhcmEgcmVwbGljYXJsb3MgZXN0w6EgW2FxdcOtXShodHRwczovL2J1enpmZWVkbmV3cy5naXRodWIuaW8vMjAxOS0xMC1zb2NpYWwtc2VudGluZWwvKS4KCgpQb3Igw7psdGltbyAzIHJlcG9zIGRlIEdpdGh1YiBhc29jaWFkb3MgYWwgcHJveWVjdG8gW1RpZHl0dWVzZGF5XShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5KS4gQWJzb2x1dGFtZW50ZSBpbXByZXNpb25hbnRlcyEhIQoKCi0gPGh0dHBzOi8vZ2l0aHViLmNvbS9na2FyYW1hbmlzL3RpZHl0dWVzZGF5L3RyZWUvbWFzdGVyL3dlZWstMzg+CgotIDxodHRwczovL2dpdGh1Yi5jb20vc3ByZW45ZXIvdGlkeXR1ZXNkYXk+CgotIDxodHRwczovL2dpdGh1Yi5jb20vb3Rob21hbnRlZ2F6emEvY29kZS10aWR5dHVlc2RheT4KCgoKIyMgTWFzIGJpYmxpbwoKLSBbQSBHcmFtbWFyIG9mIEdyYXBoaWNzIGZvciBQeXRob25dKGh0dHBzOi8vcGxvdG5pbmUucmVhZHRoZWRvY3MuaW8vZW4vc3RhYmxlL2luZGV4Lmh0bWwpLiBgUGxvdG5pbmVgIGlzIGFuIGltcGxlbWVudGF0aW9uIG9mIGEgZ3JhbW1hciBvZiBncmFwaGljcyBpbiBQeXRob24sIGl0IGlzIGJhc2VkIG9uIGBnZ3Bsb3QyYC4gCgotIFtQeXRob24gUGxvdHRpbmcgZm9yIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXNdKGh0dHBzOi8vcHl0aG9ucGxvdC5jb20vKS4gVW4gcmVjb3JyaWRvIHBvciBsb3Mgc2lzdGVtYXMgZ3LDoWZpY29zIGRlIFBoeXRvbi4KCi0gW2dncGxvdDJ0b3JdKGh0dHBzOi8vZ2dwbG90MnR1dG9yLmNvbS8pLiBVbiBzaXRpbyB3ZWIgY29uIGJ1ZW5vcyB0dXRvcmlhbGVzIHNvYnJlIGVqZW1wbG9zIGRlIGdyw6FmaWNvcyBnZ3Bsb3QuIFZhbiBtZWpvcmFuZG8gZWwgZ3LDoWZpY28gcG9jbyBhIHBvY28uIFBvciBlamVtcGxvIFtlc3RlIHR1dG9yaWFsIHNvYnJlIHBvd2VybGlmdGluZ10oaHR0cHM6Ly9nZ3Bsb3QydHV0b3IuY29tL3Bvd2VybGlmdGluZy9zcXVhdHMvKQoKLSBVbiBbZWplbXBsbyBkZSBkb3RwbG90XShodHRwczovL2lrYXNobml0c2t5LmdpdGh1Yi5pby8yMDE5L2RvdHBsb3QvKSBJbHlhIEthc2huaXRza3kgZGUgIEFxdcOtIGVzdMOhIGVsIFtnaXN0XShodHRwczovL2dpc3QuZ2l0aHViLmNvbS9pa2FzaG5pdHNreS8yZjNlMmIyYWY2ZjUwOTExYmI3NzViYmNlNmViMGZiOCkuCgoKLSBbUiBHcmFwaGljcyBDb29rYm9va10oaHR0cHM6Ly9yLWdyYXBoaWNzLm9yZy8pCgotLS0tLS0tLS0tLS0tLS0tLS0KCmBgYHtyLCBldmFsID0gRkFMU0UsIGVjaG8gPSBGQUxTRX0KIy0gbnVldmFzIGNvc2FzIGRlIGdncGxvdDIKcCA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgUGV0YWwuTGVuZ3RoKSkgKyBnZW9tX2xpbmUoKQphYSA8LSBnZ3Bsb3RfYnVpbGQocCkgIy0gZnVuY2lvbiBudWV2YQoKIy0gZ2FyZmljb3MgdXBzZXQgeSB2ZW5uOiBodHRwczovL3d3dy5saXR0bGVtaXNzZGF0YS5jb20vYmxvZy9zZXQtYW5hbHlzaXMKIy0gaHR0cHM6Ly9kcW4ud2Vic2l0ZS9wb3N0L2ludGVyYWN0aXZlLW1la2tvLWNoYXJ0cy1pbi1yLwoKYGBgCgo=