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.
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 ggplot2
no 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ón 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
equivalentes, 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
:
Los gráficos se inician con la función ggplot()
.
Generalmente aquí se especifican los datos (data.frame) que queremos
utilizar
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 …)
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 verdad 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 utilizar. 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 detalle.
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:
- 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
- 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()
- 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án 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:
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 140. 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:
centrarnos en una región especifica del gráfico
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:
- 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
- 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 . 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:
- 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)
- 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 funciones
position_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)
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))
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 paquetes 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.
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=