Escalas

Previamente comentamos que el mapeo de una variable en un elemento geométrico, por ejemplo cuando le asignamos distintos colores a los puntos que representan cada continente, usa una escala para definir, en este caso, que color le corresponde a cada elemento.

También cambiamos la apariencia del relleno (o fill) de barras y la forma de los puntos (o shape). Para esto, {ggplot2} siempre usa una escala que podemos modificar de acuerdo a nuestro gusto y teniendo en cuenta cómo queremos comunicar nuestros resultados.

Por supuesto, modificar una escala implica sumar una nueva capa al gráfico sumando una nueva función. Todas las funciones de escala comienzan con scale (de escala en inglés), el tipo de apariencia que queremos modificar (color, fill, shape, etc) y en muchos casos un nombre o una característica de esa escala.

Para mostrar como funciona, vamos a utilizar la base de datos de países y con suerte al final de este documento tendremos un gráfico listo para publicar.

paises <- readr::read_csv("datos/paises.csv")
## Parsed with column specification:
## cols(
##   pais = col_character(),
##   continente = col_character(),
##   anio = col_double(),
##   esperanza_de_vida = col_double(),
##   poblacion = col_double(),
##   pib_per_capita = col_double()
## )
paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point()

Escala de colores y otras

Lo primero que podemos hacer es cambiar el color de los puntos de a cuerdo al continente.

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente))

Pero esta escala de colores que usa {ggplot2} por defecto no es de las mejores, de hecho las personas que tienen daltonísmo muy posiblemente no logren diferenciar todos los puntos. Una escala o paleta de colores muy usada es viridis que fue creada justamente para resolver este y otros problemas. También existe otra gran familia de paletas de colores llamada ColorBrewer.

Vamos a probar la paleta “Dark2” de ColorBrewer, esta paleta es qualitativa y es justo lo que necesitamos para identificar elementos en categorías como los continentes. Cómo estamos modificando el color, la función a usar será scale_color_brewer():

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente)) +
  scale_color_brewer(type = "qual", palette = "Dark2")

Nuestro gráfico va quedando mejor y podemos aprovechar la capacidad de {ggplot2} de mapear variables a los elementos del gráfico y mostrar la población de cada país.

Desafío

A modo de prueba, cambia la paleta de colores actual por la de Viridis. Para eso tenés que usar scale_color_viridis_d(). La “d” del final viene de discrete y se usa para variables discretas o categorías, mientas que si los datos son continuos, se usa “c”.

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente, size = poblacion)) +
  scale_color_brewer(type = "qual", palette = "Dark2")

Esto nos da más información, pero al mismo tiempo vemos que los puntos se superponen. Vamos a arreglar eso agregando transparencia y de paso modificar el tamaño de los puntos con la escala correspondiente scale_size() y sacar la legenda con guide = NULL.

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente, size = poblacion), alpha = 0.5) +
  scale_color_brewer(type = "qual", palette = "Dark2") +
  scale_size_area(max_size = 15, guide = NULL)

Escalas de ejes

Tal vez notaste que el comportamiento entre la esperanza de vida y el PBI no es lineal, de hecho el PBI varía mucho mientras que la esperanza de vida va apenas entre 40 y 80 y algo. Eso puede esta indicando que el PBI tiene un comportamiento logarítmico y si bien podríamos transformar los datos antes de hacer el gráfico, también podemos elegir una escala logarítmica para el eje del gráfico correspondiente.

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente, size = poblacion), alpha = 0.5) +
  scale_color_brewer(type = "qual", palette = "Dark2") +
  scale_size_area(max_size = 15, guide = NULL) +
  scale_x_log10()

En este caso las escalas que modifican los ejes justamente comienzan con scala_x_ o scale_y_ según sea el caso. Y por supuesto hay una variedad importante de escalas con muchas opciones.

Elementos de texto

Ya sumamos 3 escalas, pero el gráfico ya se ve muy bien. ¿Cómo hacemos si queremos identificar países individuales? Por ahora es difícil, pero podríamos agregar etiquetas de texto con el nombre de cada país al lado de cada punto usando geom_text(), y en este caso la apariencia está dada por label o etiqueta:

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente, size = poblacion), alpha = 0.5) +
  scale_color_brewer(type = "qual", palette = "Dark2") +
  scale_size_area(max_size = 15, guide = NULL) +
  scale_x_log10() +
  geom_text(aes(label = pais)) 

Pero nos olvidamos que tenemos casi 200 países, es razonable agregarle etiquetas a todos. Pero podríamos querer resaltar algunos, tal vez los de un continente en particular o los que cumplen con la condición de tener las mayores poblaciones del mundo. Para eso vamos a generarnos una nueva tabla con los países que queremos resaltar y de paso usarla dentro de geom_text().

mucha_poblacion <- paises %>% 
  filter(anio == 2007) %>% 
  filter(poblacion > 1000000000) # Países con más de un billón de habitantes!

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente, size = poblacion), alpha = 0.5) +
  scale_color_brewer(type = "qual", palette = "Dark2") +
  scale_size_area(max_size = 15, guide = NULL) +
  scale_x_log10() +
  geom_text(aes(label = pais), 
            data = mucha_poblacion)  # Esta capa usa la tabla mucha_población!

Del código anterior surge algo muy importante: es posible generar capas en un gráfico usando una data.frame distinto al que usamos para graficar las capas anteriores. Esto es útil principalmente para definir etiquetas o resaltar determinadas observaciones.

Y el truco está en que ambos data.frames tienen las variables pib_per_capita y esperanza_de_vida y entonces {ggplot2} puede identificar en que parte del gráfico (en que valores de x y en que valores de y) colocar cada elemento.

Veamos ahora una (de varias) maneras agregar o modificar elementos de texto en el gráfico. Vamos a usar una nueva función (y una nueva capa!), labs():

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente, size = poblacion), alpha = 0.5) +
  scale_color_brewer(type = "qual", palette = "Dark2") +
  scale_size_area(max_size = 15, guide = NULL) +
  scale_x_log10() +
  geom_text(aes(label = pais), data = mucha_poblacion) +
  labs(title = "Paises del mundo",
       subtitle = "Año 2007",
       caption = "El tamaño de cada circulo representa la población.",
       x = "PBI per capita",
       y = "Esperanza de vida",
       color = "")

Agregamos un título, un subtítulo, el epígrafe de la figura (caption) para las aclaraciones y cambiamos el nombre de los ejes para que se vean mejor. Pero ademas eliminamos el nombre de la leyenda porque era un poco redundante.

Temas

Nos queda una última cosa por hacer, cambiar la apariencia global del gráfico. {ggplot2} tiene muchos temas disponibles y para todos los gustos. Pero además hay otros paquetes que extienden las posibilidades, por ejemplo {ggthemes}.

Por defecto {ggplot2} usa theme_grey(), probemos theme_light():

paises %>% 
  filter(anio == 2007) %>% 
  ggplot(aes(pib_per_capita, esperanza_de_vida)) +
  geom_point(aes(color = continente, size = poblacion), alpha = 0.5) +
  scale_color_brewer(type = "qual", palette = "Dark2") +
  scale_size_area(max_size = 15, guide = NULL) +
  scale_x_log10() +
  geom_text(aes(label = pais), data = mucha_poblacion) +
  labs(title = "Paises del mundo",
       subtitle = "Año 2007",
       caption = "El tamaño de cada circulo representa la población.",
       x = "PBI per capita",
       y = "Esperanza de vida",
       color = "") +
  theme_light()

Ahora es tu turno. Elegí un tema que te guste y probalo. Además, si se te ocurre algún título mejor modificalo!

Junto con las funciones theme_...(), hay una función llamada theme() que permite cambiar la apariencia de cualquier elemento del gráfico. Tiene casi infinitas opciones y si algún momento te desvelas intentando cambiar esa línea o ese borde, seguro que theme() tiene alguna opción para hacer eso.

LS0tCnRpdGxlOiAiQXBhcmllbmNpYSBkZSBncsOhZmljb3MiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiBmYWxzZQogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKYGBgCgojIyBFc2NhbGFzCgpbUHJldmlhbWVudGVdKDA2LWdyYWZpY29zLUkuaHRtbCkgY29tZW50YW1vcyBxdWUgZWwgKm1hcGVvKiBkZSB1bmEgdmFyaWFibGUgZW4gdW4gZWxlbWVudG8gZ2VvbcOpdHJpY28sIHBvciBlamVtcGxvIGN1YW5kbyBsZSBhc2lnbmFtb3MgZGlzdGludG9zIGNvbG9yZXMgYSBsb3MgcHVudG9zIHF1ZSByZXByZXNlbnRhbiBjYWRhIGNvbnRpbmVudGUsIHVzYSB1bmEgKiplc2NhbGEqKiBwYXJhIGRlZmluaXIsIGVuIGVzdGUgY2FzbywgcXVlIGNvbG9yIGxlIGNvcnJlc3BvbmRlIGEgY2FkYSBlbGVtZW50by4gCgpUYW1iacOpbiBjYW1iaWFtb3MgbGEgYXBhcmllbmNpYSBkZWwgcmVsbGVubyAobyBgZmlsbGApIGRlIGJhcnJhcyB5IGxhIGZvcm1hIGRlIGxvcyBwdW50b3MgKG8gYHNoYXBlYCkuIFBhcmEgZXN0bywge2dncGxvdDJ9IHNpZW1wcmUgdXNhIHVuYSBlc2NhbGEgcXVlIHBvZGVtb3MgbW9kaWZpY2FyIGRlIGFjdWVyZG8gYSBudWVzdHJvIGd1c3RvIHkgdGVuaWVuZG8gZW4gY3VlbnRhIGPDs21vIHF1ZXJlbW9zIGNvbXVuaWNhciBudWVzdHJvcyByZXN1bHRhZG9zLiAKClBvciBzdXB1ZXN0bywgbW9kaWZpY2FyIHVuYSBlc2NhbGEgaW1wbGljYSBzdW1hciB1bmEgbnVldmEgY2FwYSBhbCBncsOhZmljbyBzdW1hbmRvIHVuYSBudWV2YSBmdW5jacOzbi4gVG9kYXMgbGFzIGZ1bmNpb25lcyBkZSBlc2NhbGEgY29taWVuemFuIGNvbiBgc2NhbGVgIChkZSBlc2NhbGEgZW4gaW5nbMOpcyksIGVsIHRpcG8gZGUgYXBhcmllbmNpYSBxdWUgcXVlcmVtb3MgbW9kaWZpY2FyIChgY29sb3JgLCBgZmlsbGAsIGBzaGFwZWAsIGV0YykgeSBlbiBtdWNob3MgY2Fzb3MgdW4gbm9tYnJlIG8gdW5hIGNhcmFjdGVyw61zdGljYSBkZSBlc2EgZXNjYWxhLiAKClBhcmEgbW9zdHJhciBjb21vIGZ1bmNpb25hLCB2YW1vcyBhIHV0aWxpemFyIGxhIGJhc2UgZGUgZGF0b3MgZGUgcGHDrXNlcyB5IGNvbiBzdWVydGUgYWwgZmluYWwgZGUgZXN0ZSBkb2N1bWVudG8gdGVuZHJlbW9zIHVuIGdyw6FmaWNvIGxpc3RvIHBhcmEgcHVibGljYXIuCgpgYGB7cn0KcGFpc2VzIDwtIHJlYWRyOjpyZWFkX2NzdigiZGF0b3MvcGFpc2VzLmNzdiIpCgpwYWlzZXMgJT4lIAogIGZpbHRlcihhbmlvID09IDIwMDcpICU+JSAKICBnZ3Bsb3QoYWVzKHBpYl9wZXJfY2FwaXRhLCBlc3BlcmFuemFfZGVfdmlkYSkpICsKICBnZW9tX3BvaW50KCkKCmBgYAoKIyMjIEVzY2FsYSBkZSBjb2xvcmVzIHkgb3RyYXMKCkxvIHByaW1lcm8gcXVlIHBvZGVtb3MgaGFjZXIgZXMgY2FtYmlhciBlbCBjb2xvciBkZSBsb3MgcHVudG9zIGRlIGEgY3VlcmRvIGFsIGNvbnRpbmVudGUuCgpgYGB7cn0KcGFpc2VzICU+JSAKICBmaWx0ZXIoYW5pbyA9PSAyMDA3KSAlPiUgCiAgZ2dwbG90KGFlcyhwaWJfcGVyX2NhcGl0YSwgZXNwZXJhbnphX2RlX3ZpZGEpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjb250aW5lbnRlKSkKYGBgCgpQZXJvIGVzdGEgZXNjYWxhIGRlIGNvbG9yZXMgcXVlIHVzYSB7Z2dwbG90Mn0gcG9yIGRlZmVjdG8gbm8gZXMgZGUgbGFzIG1lam9yZXMsIGRlIGhlY2hvIGxhcyBwZXJzb25hcyBxdWUgdGllbmVuIGRhbHRvbsOtc21vIG11eSBwb3NpYmxlbWVudGUgbm8gbG9ncmVuIGRpZmVyZW5jaWFyIHRvZG9zIGxvcyBwdW50b3MuIFVuYSBlc2NhbGEgbyBwYWxldGEgZGUgY29sb3JlcyBtdXkgdXNhZGEgZXMgWyoqdmlyaWRpcyoqXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdmlyaWRpcy92aWduZXR0ZXMvaW50cm8tdG8tdmlyaWRpcy5odG1sKSBxdWUgZnVlIGNyZWFkYSBqdXN0YW1lbnRlIHBhcmEgcmVzb2x2ZXIgZXN0ZSB5IG90cm9zIHByb2JsZW1hcy4gVGFtYmnDqW4gZXhpc3RlIG90cmEgZ3JhbiBmYW1pbGlhIGRlIHBhbGV0YXMgZGUgY29sb3JlcyBsbGFtYWRhIFsqKkNvbG9yQnJld2VyKipdKGh0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy8pLgoKVmFtb3MgYSBwcm9iYXIgbGEgcGFsZXRhICJEYXJrMiIgZGUgQ29sb3JCcmV3ZXIsIGVzdGEgcGFsZXRhIGVzICpxdWFsaXRhdGl2YSogeSBlcyBqdXN0byBsbyBxdWUgbmVjZXNpdGFtb3MgcGFyYSBpZGVudGlmaWNhciBlbGVtZW50b3MgZW4gY2F0ZWdvcsOtYXMgY29tbyBsb3MgY29udGluZW50ZXMuIEPDs21vIGVzdGFtb3MgbW9kaWZpY2FuZG8gZWwgKmNvbG9yKiwgbGEgZnVuY2nDs24gYSB1c2FyIHNlcsOhIGBzY2FsZV9jb2xvcl9icmV3ZXIoKWA6CgpgYGB7cn0KcGFpc2VzICU+JSAKICBmaWx0ZXIoYW5pbyA9PSAyMDA3KSAlPiUgCiAgZ2dwbG90KGFlcyhwaWJfcGVyX2NhcGl0YSwgZXNwZXJhbnphX2RlX3ZpZGEpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjb250aW5lbnRlKSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIkRhcmsyIikKYGBgCgpOdWVzdHJvIGdyw6FmaWNvIHZhIHF1ZWRhbmRvIG1lam9yIHkgcG9kZW1vcyBhcHJvdmVjaGFyIGxhIGNhcGFjaWRhZCBkZSB7Z2dwbG90Mn0gZGUgKm1hcGVhciogdmFyaWFibGVzIGEgbG9zIGVsZW1lbnRvcyBkZWwgZ3LDoWZpY28geSBtb3N0cmFyIGxhIHBvYmxhY2nDs24gZGUgY2FkYSBwYcOtcy4KCjo6OiB7LmFsZXJ0IC5hbGVydC1pbmZvfQoqKkRlc2Fmw61vKioKCkEgbW9kbyBkZSBwcnVlYmEsIGNhbWJpYSBsYSBwYWxldGEgZGUgY29sb3JlcyBhY3R1YWwgcG9yIGxhIGRlIFZpcmlkaXMuIFBhcmEgZXNvIHRlbsOpcyBxdWUgdXNhciBgc2NhbGVfY29sb3JfdmlyaWRpc19kKClgLiBMYSAiZCIgZGVsIGZpbmFsIHZpZW5lIGRlICpkaXNjcmV0ZSogeSBzZSB1c2EgcGFyYSB2YXJpYWJsZXMgZGlzY3JldGFzIG8gY2F0ZWdvcsOtYXMsIG1pZW50YXMgcXVlIHNpIGxvcyBkYXRvcyBzb24gY29udGludW9zLCBzZSB1c2EgImMiLgo6OjoKCmBgYHtyfQpwYWlzZXMgJT4lIAogIGZpbHRlcihhbmlvID09IDIwMDcpICU+JSAKICBnZ3Bsb3QoYWVzKHBpYl9wZXJfY2FwaXRhLCBlc3BlcmFuemFfZGVfdmlkYSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNvbnRpbmVudGUsIHNpemUgPSBwb2JsYWNpb24pKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiKQpgYGAKCkVzdG8gbm9zIGRhIG3DoXMgaW5mb3JtYWNpw7NuLCBwZXJvIGFsIG1pc21vIHRpZW1wbyB2ZW1vcyBxdWUgbG9zIHB1bnRvcyBzZSBzdXBlcnBvbmVuLiBWYW1vcyBhIGFycmVnbGFyIGVzbyBhZ3JlZ2FuZG8gdHJhbnNwYXJlbmNpYSB5IGRlIHBhc28gbW9kaWZpY2FyIGVsIHRhbWHDsW8gZGUgbG9zIHB1bnRvcyBjb24gbGEgZXNjYWxhIGNvcnJlc3BvbmRpZW50ZSBgc2NhbGVfc2l6ZSgpYCB5IHNhY2FyIGxhIGxlZ2VuZGEgY29uIGBndWlkZSA9IE5VTExgLgoKYGBge3J9CnBhaXNlcyAlPiUgCiAgZmlsdGVyKGFuaW8gPT0gMjAwNykgJT4lIAogIGdncGxvdChhZXMocGliX3Blcl9jYXBpdGEsIGVzcGVyYW56YV9kZV92aWRhKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY29udGluZW50ZSwgc2l6ZSA9IHBvYmxhY2lvbiksIGFscGhhID0gMC41KSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTUsIGd1aWRlID0gTlVMTCkKYGBgCgojIyMgRXNjYWxhcyBkZSBlamVzCgpUYWwgdmV6IG5vdGFzdGUgcXVlIGVsIGNvbXBvcnRhbWllbnRvIGVudHJlIGxhIGVzcGVyYW56YSBkZSB2aWRhIHkgZWwgUEJJIG5vIGVzIGxpbmVhbCwgZGUgaGVjaG8gZWwgUEJJIHZhcsOtYSBtdWNobyBtaWVudHJhcyBxdWUgbGEgZXNwZXJhbnphIGRlIHZpZGEgdmEgYXBlbmFzIGVudHJlIDQwIHkgODAgeSBhbGdvLiBFc28gcHVlZGUgZXN0YSBpbmRpY2FuZG8gcXVlIGVsIFBCSSB0aWVuZSB1biBjb21wb3J0YW1pZW50byAqbG9nYXLDrXRtaWNvKiB5IHNpIGJpZW4gcG9kcsOtYW1vcyB0cmFuc2Zvcm1hciBsb3MgZGF0b3MgYW50ZXMgZGUgaGFjZXIgZWwgZ3LDoWZpY28sIHRhbWJpw6luIHBvZGVtb3MgZWxlZ2lyIHVuYSBlc2NhbGEgbG9nYXLDrXRtaWNhIHBhcmEgZWwgZWplIGRlbCBncsOhZmljbyBjb3JyZXNwb25kaWVudGUuIAoKYGBge3J9CnBhaXNlcyAlPiUgCiAgZmlsdGVyKGFuaW8gPT0gMjAwNykgJT4lIAogIGdncGxvdChhZXMocGliX3Blcl9jYXBpdGEsIGVzcGVyYW56YV9kZV92aWRhKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY29udGluZW50ZSwgc2l6ZSA9IHBvYmxhY2lvbiksIGFscGhhID0gMC41KSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTUsIGd1aWRlID0gTlVMTCkgKwogIHNjYWxlX3hfbG9nMTAoKQpgYGAKCkVuIGVzdGUgY2FzbyBsYXMgZXNjYWxhcyBxdWUgbW9kaWZpY2FuIGxvcyBlamVzIGp1c3RhbWVudGUgY29taWVuemFuIGNvbiBgc2NhbGFfeF9gIG8gYHNjYWxlX3lfYCBzZWfDum4gc2VhIGVsIGNhc28uIFkgcG9yIHN1cHVlc3RvIGhheSB1bmEgdmFyaWVkYWQgaW1wb3J0YW50ZSBkZSBlc2NhbGFzIGNvbiBtdWNoYXMgb3BjaW9uZXMuIAoKIyMgRWxlbWVudG9zIGRlIHRleHRvCgpZYSBzdW1hbW9zIDMgZXNjYWxhcywgcGVybyBlbCBncsOhZmljbyB5YSBzZSB2ZSBtdXkgYmllbi4gwr9Dw7NtbyBoYWNlbW9zIHNpIHF1ZXJlbW9zIGlkZW50aWZpY2FyIHBhw61zZXMgaW5kaXZpZHVhbGVzPyBQb3IgYWhvcmEgZXMgZGlmw61jaWwsIHBlcm8gcG9kcsOtYW1vcyBhZ3JlZ2FyIGV0aXF1ZXRhcyBkZSB0ZXh0byBjb24gZWwgbm9tYnJlIGRlIGNhZGEgcGHDrXMgYWwgbGFkbyBkZSBjYWRhIHB1bnRvIHVzYW5kbyBgZ2VvbV90ZXh0KClgLCB5IGVuIGVzdGUgY2FzbyBsYSBhcGFyaWVuY2lhIGVzdMOhIGRhZGEgcG9yIGBsYWJlbGAgbyBldGlxdWV0YToKCmBgYHtyfQpwYWlzZXMgJT4lIAogIGZpbHRlcihhbmlvID09IDIwMDcpICU+JSAKICBnZ3Bsb3QoYWVzKHBpYl9wZXJfY2FwaXRhLCBlc3BlcmFuemFfZGVfdmlkYSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNvbnRpbmVudGUsIHNpemUgPSBwb2JsYWNpb24pLCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIkRhcmsyIikgKwogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDE1LCBndWlkZSA9IE5VTEwpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYWlzKSkgCmBgYAoKUGVybyBub3Mgb2x2aWRhbW9zIHF1ZSB0ZW5lbW9zIGNhc2kgMjAwIHBhw61zZXMsIGVzIHJhem9uYWJsZSBhZ3JlZ2FybGUgZXRpcXVldGFzIGEgdG9kb3MuIFBlcm8gcG9kcsOtYW1vcyBxdWVyZXIgcmVzYWx0YXIgYWxndW5vcywgdGFsIHZleiBsb3MgZGUgdW4gY29udGluZW50ZSBlbiBwYXJ0aWN1bGFyIG8gbG9zIHF1ZSBjdW1wbGVuIGNvbiBsYSBjb25kaWNpw7NuIGRlIHRlbmVyIGxhcyBtYXlvcmVzIHBvYmxhY2lvbmVzIGRlbCBtdW5kby4gUGFyYSBlc28gdmFtb3MgYSBnZW5lcmFybm9zIHVuYSBudWV2YSB0YWJsYSBjb24gbG9zIHBhw61zZXMgcXVlIHF1ZXJlbW9zIHJlc2FsdGFyIHkgZGUgcGFzbyB1c2FybGEgZGVudHJvIGRlIGBnZW9tX3RleHQoKWAuCgpgYGB7cn0KbXVjaGFfcG9ibGFjaW9uIDwtIHBhaXNlcyAlPiUgCiAgZmlsdGVyKGFuaW8gPT0gMjAwNykgJT4lIAogIGZpbHRlcihwb2JsYWNpb24gPiAxMDAwMDAwMDAwKSAjIFBhw61zZXMgY29uIG3DoXMgZGUgdW4gYmlsbMOzbiBkZSBoYWJpdGFudGVzIQoKcGFpc2VzICU+JSAKICBmaWx0ZXIoYW5pbyA9PSAyMDA3KSAlPiUgCiAgZ2dwbG90KGFlcyhwaWJfcGVyX2NhcGl0YSwgZXNwZXJhbnphX2RlX3ZpZGEpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjb250aW5lbnRlLCBzaXplID0gcG9ibGFjaW9uKSwgYWxwaGEgPSAwLjUpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIpICsKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSAxNSwgZ3VpZGUgPSBOVUxMKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFpcyksIAogICAgICAgICAgICBkYXRhID0gbXVjaGFfcG9ibGFjaW9uKSAgIyBFc3RhIGNhcGEgdXNhIGxhIHRhYmxhIG11Y2hhX3BvYmxhY2nDs24hCmBgYAoKOjo6IHsuYWxlcnQgLmFsZXJ0LXN1Y2Nlc3N9CgpEZWwgY8OzZGlnbyBhbnRlcmlvciBzdXJnZSBhbGdvIG11eSBpbXBvcnRhbnRlOiBlcyBwb3NpYmxlIGdlbmVyYXIgY2FwYXMgZW4gdW4gZ3LDoWZpY28gdXNhbmRvIHVuYSBkYXRhLmZyYW1lICpkaXN0aW50byogYWwgcXVlIHVzYW1vcyBwYXJhIGdyYWZpY2FyIGxhcyBjYXBhcyBhbnRlcmlvcmVzLiBFc3RvIGVzIMO6dGlsIHByaW5jaXBhbG1lbnRlIHBhcmEgZGVmaW5pciBldGlxdWV0YXMgbyByZXNhbHRhciBkZXRlcm1pbmFkYXMgb2JzZXJ2YWNpb25lcy4gCgpZIGVsIHRydWNvIGVzdMOhIGVuIHF1ZSBhbWJvcyBkYXRhLmZyYW1lcyB0aWVuZW4gbGFzIHZhcmlhYmxlcyBgcGliX3Blcl9jYXBpdGFgIHkgYGVzcGVyYW56YV9kZV92aWRhYCB5IGVudG9uY2VzIHtnZ3Bsb3QyfSBwdWVkZSBpZGVudGlmaWNhciBlbiBxdWUgcGFydGUgZGVsIGdyw6FmaWNvIChlbiBxdWUgdmFsb3JlcyBkZSB4IHkgZW4gcXVlIHZhbG9yZXMgZGUgeSkgY29sb2NhciBjYWRhIGVsZW1lbnRvLgo6OjoKClZlYW1vcyBhaG9yYSB1bmEgKGRlIHZhcmlhcykgbWFuZXJhcyBhZ3JlZ2FyIG8gbW9kaWZpY2FyIGVsZW1lbnRvcyBkZSB0ZXh0byBlbiBlbCBncsOhZmljby4gVmFtb3MgYSB1c2FyIHVuYSBudWV2YSBmdW5jacOzbiAoeSB1bmEgbnVldmEgY2FwYSEpLCBgbGFicygpYDoKCmBgYHtyfQpwYWlzZXMgJT4lIAogIGZpbHRlcihhbmlvID09IDIwMDcpICU+JSAKICBnZ3Bsb3QoYWVzKHBpYl9wZXJfY2FwaXRhLCBlc3BlcmFuemFfZGVfdmlkYSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNvbnRpbmVudGUsIHNpemUgPSBwb2JsYWNpb24pLCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIkRhcmsyIikgKwogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDE1LCBndWlkZSA9IE5VTEwpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYWlzKSwgZGF0YSA9IG11Y2hhX3BvYmxhY2lvbikgKwogIGxhYnModGl0bGUgPSAiUGFpc2VzIGRlbCBtdW5kbyIsCiAgICAgICBzdWJ0aXRsZSA9ICJBw7FvIDIwMDciLAogICAgICAgY2FwdGlvbiA9ICJFbCB0YW1hw7FvIGRlIGNhZGEgY2lyY3VsbyByZXByZXNlbnRhIGxhIHBvYmxhY2nDs24uIiwKICAgICAgIHggPSAiUEJJIHBlciBjYXBpdGEiLAogICAgICAgeSA9ICJFc3BlcmFuemEgZGUgdmlkYSIsCiAgICAgICBjb2xvciA9ICIiKQpgYGAKCkFncmVnYW1vcyB1biB0w610dWxvLCB1biBzdWJ0w610dWxvLCBlbCBlcMOtZ3JhZmUgZGUgbGEgZmlndXJhICgqY2FwdGlvbiopIHBhcmEgbGFzIGFjbGFyYWNpb25lcyB5IGNhbWJpYW1vcyBlbCBub21icmUgZGUgbG9zIGVqZXMgcGFyYSBxdWUgc2UgdmVhbiBtZWpvci4gUGVybyBhZGVtYXMgZWxpbWluYW1vcyBlbCBub21icmUgZGUgbGEgbGV5ZW5kYSBwb3JxdWUgZXJhIHVuIHBvY28gcmVkdW5kYW50ZS4gCgojIyBUZW1hcwoKTm9zIHF1ZWRhIHVuYSDDumx0aW1hIGNvc2EgcG9yIGhhY2VyLCBjYW1iaWFyIGxhIGFwYXJpZW5jaWEgZ2xvYmFsIGRlbCBncsOhZmljby4ge2dncGxvdDJ9IHRpZW5lIG11Y2hvcyAqdGVtYXMqIGRpc3BvbmlibGVzIHkgcGFyYSB0b2RvcyBsb3MgZ3VzdG9zLiBQZXJvIGFkZW3DoXMgaGF5IG90cm9zIHBhcXVldGVzIHF1ZSBleHRpZW5kZW4gbGFzIHBvc2liaWxpZGFkZXMsIHBvciBlamVtcGxvIFt7Z2d0aGVtZXN9XShodHRwczovL2dpdGh1Yi5jb20vanJub2xkL2dndGhlbWVzKS4KClBvciBkZWZlY3RvIHtnZ3Bsb3QyfSB1c2EgYHRoZW1lX2dyZXkoKWAsIHByb2JlbW9zIGB0aGVtZV9saWdodCgpYDoKCmBgYHtyfQpwYWlzZXMgJT4lIAogIGZpbHRlcihhbmlvID09IDIwMDcpICU+JSAKICBnZ3Bsb3QoYWVzKHBpYl9wZXJfY2FwaXRhLCBlc3BlcmFuemFfZGVfdmlkYSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNvbnRpbmVudGUsIHNpemUgPSBwb2JsYWNpb24pLCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIkRhcmsyIikgKwogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDE1LCBndWlkZSA9IE5VTEwpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYWlzKSwgZGF0YSA9IG11Y2hhX3BvYmxhY2lvbikgKwogIGxhYnModGl0bGUgPSAiUGFpc2VzIGRlbCBtdW5kbyIsCiAgICAgICBzdWJ0aXRsZSA9ICJBw7FvIDIwMDciLAogICAgICAgY2FwdGlvbiA9ICJFbCB0YW1hw7FvIGRlIGNhZGEgY2lyY3VsbyByZXByZXNlbnRhIGxhIHBvYmxhY2nDs24uIiwKICAgICAgIHggPSAiUEJJIHBlciBjYXBpdGEiLAogICAgICAgeSA9ICJFc3BlcmFuemEgZGUgdmlkYSIsCiAgICAgICBjb2xvciA9ICIiKSArCiAgdGhlbWVfbGlnaHQoKQpgYGAKCjo6OiB7LmFsZXJ0IC5hbGVydC1pbmZvfQoKQWhvcmEgZXMgdHUgdHVybm8uIEVsZWfDrSB1biBbdGVtYSBxdWUgdGUgZ3VzdGVdKGh0dHBzOi8vZXMucjRkcy5oYWRsZXkubnovaW1hZ2VzL3Zpc3VhbGl6YXRpb24tdGhlbWVzLnBuZyl7LmFsZXJ0LWxpbmt9IHkgcHJvYmFsby4gQWRlbcOhcywgc2kgc2UgdGUgb2N1cnJlIGFsZ8O6biB0w610dWxvIG1lam9yIG1vZGlmaWNhbG8hIAo6OjoKCjo6OiB7LmFsZXJ0IC5hbGVydC1zdWNjZXNzfQoKSnVudG8gY29uIGxhcyBmdW5jaW9uZXMgYHRoZW1lXy4uLigpYCwgaGF5IHVuYSBmdW5jacOzbiBsbGFtYWRhIGB0aGVtZSgpYCBxdWUgcGVybWl0ZSBjYW1iaWFyIGxhIGFwYXJpZW5jaWEgZGUgY3VhbHF1aWVyIGVsZW1lbnRvIGRlbCBncsOhZmljby4gVGllbmUgY2FzaSBpbmZpbml0YXMgb3BjaW9uZXMgeSBzaSBhbGfDum4gbW9tZW50byB0ZSBkZXN2ZWxhcyBpbnRlbnRhbmRvIGNhbWJpYXIgZXNhIGzDrW5lYSBvIGVzZSBib3JkZSwgc2VndXJvIHF1ZSBgdGhlbWUoKWAgdGllbmUgYWxndW5hIG9wY2nDs24gcGFyYSBoYWNlciBlc28uCgo6OjoKCgo8ZGl2IGNsYXNzPSJidG4tZ3JvdXAiIHJvbGU9Imdyb3VwIiBhcmlhLWxhYmVsPSJOYXZlZ2FjacOzbiI+CiAgPGEgaHJlZj0gIjExLXRhYmxhcy5odG1sIiBjbGFzcyA9ICJidG4gYnRuLXByaW1hcnkiPkFudGVyaW9yPC9hPgogIDxhIGhyZWY9ICJob2phLWRlLXJ1dGEuaHRtbCIgY2xhc3MgPSAiYnRuIGJ0bi1wcmltYXJ5Ij5Ib2phIGRlIHJ1dGE8L2E+CjwvZGl2Pg==