Visão rápida

  • O ggplot2 foi criado por Hadley Wickham, baseado no livro Grammar of Graphics, de Leland Wilkinson.
  • A ideia é pensar em um “gráfico” como uma estrutura racional, composta por camadas.

  • Não precisa ter todas as camadas, mas três são obrigatórias:
    • data: o conjunto de dados deve ser uma tibble (ou, pelo menos, um dataframe);
    • aesthetics: mapeamentos estéticos, como selecionamento das variáveis em questão;
    • geometries: são as formas do gráfico (pontos, boxplot, histograma, etc)
  • Não veremos todos os gráficos, apenas alguns poucos;
  • Apenas tangenciaremos o pacote !!

Queira comunicar, não “lacrar”!

Antes de começar

  • Vamos carregar a biblioteca do tidyverse, não apenas a do ggplot.
  • Isso se deve ao fato de sempre precisarmos manipular os dados antes de plotar algum gráfico.

O código abaixo carrega o pacote:

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ---------------------------------- tidyverse 1.3.1 --
v ggplot2 3.3.5     v purrr   0.3.4
v tibble  3.1.3     v dplyr   1.0.7
v tidyr   1.1.3     v stringr 1.4.0
v readr   2.0.0     v forcats 0.5.1
-- Conflicts ------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
  • Depois disso, usaremos a função read_csv() para acessar o conjunto de dados.
  • Usaremos o conjunto de dados milhas_por_galao.csv, bem como o conjunto de dados notas_disciplinas.csv, ambos disponíveis em nosso repositório: Dados.csv.
    • O primeiro conjunto de dados, nomearemos por dados_mpg e o segundo por dados_notas
# Lendo o conjunto `milhas_por_galao.csv`
dados_mpg <- read_csv("dados/milhas_por_galao.csv")
Rows: 234 Columns: 11
-- Column specification ---------------------------------------------------
Delimiter: ","
chr (6): fabricante, modelo, transmissao, tracao, combustivel, classe
dbl (5): cilindrada, ano, cilindros, cidade, rodovia

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Lendo o conjunto de dados `notas_disciplina.csv`
dados_notas <- read_csv("dados/notas_disciplina.csv")
Rows: 20 Columns: 12
-- Column specification ---------------------------------------------------
Delimiter: ","
chr  (1): Curso
dbl (11): 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
  • O dataset dados_mpg ficaria assim:
dados_mpg
  • Já o dataset dados_notas, assim:
    • veja que esse conjunto de dados não está na forma tidy.
dados_notas

Sintax do ggplot

ggplot(dados, mapping = aes(x = ..., y = ..., outras = ...)) +
  geom_funcao(opções)

Ou, usando o pipe:

dados %>% 
  ggplot(mapping = aes(x = ..., y = ..., outras = ...)) +
  geom_funcao(opções)
  • Note que usamos o sinal “+” para adicionar as camadas do ggplot.
  • Para ficar mais explícito, às vezes usaremos assim:
dados %>% 
  ggplot() +
  aes(x = ..., y = ..., outras = ...) +
  geom_funcao(opções)

1. Gráficos de Pontos

  • Também são chamados de “gráfico de dispersão” (scatter plot)
  • Relaciona variáveis numéricas.
  • Vamos relacionar a quantidade de cilindradas com a quantidade de milhas por galão em rodovia.
    • ou seja, vamos relacionar as seguintes variáveis numéricas em nosso dataset: rodovia e cilindrada.
    • estamos usando o dataset dados_mpg.
  • Nesse exemplo, vamos usar as camadas, passo a passo (mesmo não precisando)

1º Camada: Data (dados)

ggplot(data = dados_mpg)

  • Observe que se colocarmos apenas a camada dos “dados”, não aparece (quase) nada
    • carrega-se apenas o conjunto de dados

2º Camada: Aesthetics (estética)

  • Acrescentando a camada estética geral (aes()), já aparecem os valores do eixo \(x\) e os valores do eixo \(y\).
    • também é costume colocar essa camada dentro da camada ggplot(), assim: ggplot(data = ..., aes(x = ..., y = ...)).
ggplot(data = dados_mpg) +
  aes(x = rodovia, y = cilindrada)

3º Camada: Geometries (geometria)

  • Vamos agora acrescentar a camada da geometria dos dados
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point()

  • Veja com com essas três camadas, já temos um conjunto de informações básicas sobre o que desejamos comunicar.
    • as outras camadas, deixam o gráfico mais “bonito”.
  • Também podemos acrescentar argumentos convenientes na camada Geometries, como size (que altera o tamanho dos pontos)
  • Apenas por simplicidade, vamos omitir o nome “date”
ggplot(dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2)

4º Camada: Facets (facetas)

  • Ao acrescentar a camada “facets”, particularizamos alguma característica desejável
    • no exemplo, estamos facetando pelo ano, ou seja, separando os dados por ano.
    • usaremos a função facet_grid()
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2) +
  facet_grid(ano~.)

  • Veja que usamos o argumento ano~.
    • isso faz com que a variável ano fique na “horizontal”
    • Se mudarmos a posição desse argumento, ou seja, .~ano, fica assim:
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2) +
  facet_grid(.~ano)

  • Ainda podemos mudar a cor dos pontos, de acordo com a classe dos carros.
    • para isso usaremos o argumento color = classe
    • devemos acrescentar o argumento color dentro de um aes() (local), pois relaciona à mudanças na variável do dataset (ou seja, ele mapeará as características estéticas em cada parte da variável);
    • se a mudança é apenas numérica, não precisa do aes()
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, aes(color = classe)) +
  facet_grid(.~ano)

  • Se não tivéssemos acrescentado o aes() ficaria assim (com erro):
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, color = classe) +
  facet_grid(.~ano)

5º Camada: Statistics (estatísticas)

  • Pode ser a média, mediana, uma regra de regressão, etc.
  • para esse exemplo, usaremos uma curva que melhor se ajusta nos conjuntos de pontos.
    • para isso, usaremos a função geom_smooth()
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, aes(color = classe)) +
  facet_grid(.~ano) +
  geom_smooth()
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

  • Se quisermos uma reta que melhor se adeque aos pontos, devemos acrescentar o argumento `method = lm:
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, aes(color = classe)) +
  facet_grid(.~ano) +
  geom_smooth(method = lm)
`geom_smooth()` using formula 'y ~ x'

  • Caso seja conveniente retirarmos o “sombreamento” (intervalo de confiança) da curva (inclusive a reta), usamos o argumento se = FALSE.
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, aes(color = classe)) +
  facet_grid(.~ano) +
  geom_smooth(method = lm, se = FALSE)
`geom_smooth()` using formula 'y ~ x'

  • E, se quisermos mudar a cor dessa reta de regressão, podemos fazer dentro dessa camada:
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, aes(color = classe)) +
  facet_grid(.~ano) +
  geom_smooth(method = lm, se = FALSE, color = "red")
`geom_smooth()` using formula 'y ~ x'

6º Camada: Coordinates (coordenadas)

  • Podemos usar coordenadas cartesianas ou polares;
    • no nosso exemplo, estamos usando coordenadas polares.
  • Também é possível delimitar os eixos coordenados.
    • em nosso caso, usaremos as funções xlim e ylim dentro da função coord_cartesian()
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, aes(color = classe)) +
  facet_grid(.~ano) +
  geom_smooth(method = lm, se = FALSE, color = "red") +
  coord_cartesian(xlim = c(0, 8), ylim = c(10, 45))
`geom_smooth()` using formula 'y ~ x'

7º Camada: Theme

  • Podemos colocar um tema que melhor se adeque ao aspecto de nosso texto
    • no nosso caso, vamos usar o tema theme_bw
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, aes(color = classe)) +
  facet_grid(.~ano) +
  geom_smooth(method = lm, se = FALSE, color = "red") +
  theme_bw()
`geom_smooth()` using formula 'y ~ x'

  • Também podemos modificar o título e os nomes dos eixos com a função labs()
ggplot(data = dados_mpg) +
  aes(x = cilindrada, y = rodovia) +
  geom_point(size = 2, aes(color = classe)) +
  facet_grid(.~ano) +
  geom_smooth(method = lm, se = FALSE, color = "red") +
  theme_bw() +
  labs(
    title = "Correlação entre variáveis numéricas",
    subtitle = "Cilindrada vs Milhas por Galão",
    x = "Cilindrada",
    y = "Milhas por Galão de Combustível"
  )
`geom_smooth()` using formula 'y ~ x'

2. Gráficos de linha

  • Usaremos para esse gráfico o dataset notas_disciplina.csv.
  • Precisamos deixá-lo na forma `tidy``, pivotando as colunas (já vimos como faz isso)
    • também renomearemos uma das variáveis
    • chamaremos esses dados modificados por notas_tidy
notas_tidy <- dados_notas %>% 
  pivot_longer(
    !Curso,
    names_to = "ano",
    values_to = "notas"
  ) %>% 
  rename(curso = Curso)

notas_tidy
  • Para criarmos o gráfico de linhas, vamos usar as médias do conjunto de dados ao longo dos anos.
  • Agora, vamos sumarizar as médias das turmas por ano:
    • chamaremos de p
p <- notas_tidy %>% 
  group_by(ano) %>% 
  summarise(
    media_notas = round(mean(notas), 1)
)

p
 p %>% 
  ggplot(aes(x = ano, y = media_notas, group = 1)) +
  geom_line(color = "#BF616A", size = 1)

  • Note que:
    • usamos o comando group = 1 no aes() geral.
    • escolhemos uma cor vermelha específica (com suporte em html)
  • O código completo, já com o tema, fica
 notas_tidy %>% 
  group_by(ano) %>% 
  summarise(
    media_notas = round(mean(notas), 1)
  ) %>% 
  ggplot(aes(x = ano, y = media_notas, group = 1)) +
  geom_line(color = "#BF616A", size = 1) +
  labs(
    title = "Gráfico de Linhas das Notas",
    x = "",
    y = "Média das Notas"
  ) +
  theme_minimal()

  • Veja que o “+” só é colocado nas camadas do ggolot.
    • um erro comum é trocar o + pelo %>% (pipe)

3. Gráfico de Colunas

  • Para o gráfico de colunas, vamos usar o dados_mpg e relacionarmos quantos carros usam os diferentes combustíveis (p = premium; r = regular; e = ethanol; d = diesel; g = gás).
dados_mpg %>% 
  count(combustivel)
  • Assim, usaremos a função geom_col() para criarmos o gráfico de colunas, relacionando o combustível com sua frequência absoluta n
dados_mpg %>% 
  count(combustivel) %>%
  ggplot(aes(x = combustivel, y = n)) +
  geom_col()

  • Se quisermos preencher cada coluna com uma cor de acordo com o tipo de combustível, devemos acrescentar no aes() um argumento para tal fato.
    • o argumento color vai cobrir a fronteira dos gráficos;
    • o argumento fill vai preencher o interior dos gráficos.
dados_mpg %>% 
  count(combustivel) %>%
  ggplot(aes(x = combustivel, y = n , color = combustivel)) +
  geom_col()

dados_mpg %>% 
  count(combustivel) %>%
  ggplot(aes(x = combustivel, y = n , fill = combustivel)) +
  geom_col()

  • Há um conjunto de cores (viridis) que ajuda na visualização de pessoas com dificuldade de visão ou daltonismo
    • particularmente, uso a variação d (default)
dados_mpg %>% 
  count(combustivel) %>%
  ggplot(aes(x = combustivel, y = n , fill = combustivel)) +
  geom_col() +
  scale_fill_viridis_d()

  • Uma maneira rápida de reordenarmos as colunas de forma crescente é usarmos a função fct_reorder(), do pacote forcats (incluso no tidyverse).
dados_mpg %>% 
  count(combustivel) %>% 
  ggplot(aes(x = fct_reorder(combustivel, n), y = n, fill = combustivel)) +
  geom_col() +
  scale_fill_viridis_d()

  • Para a ordenação de forma decrescente, podemos usar a função desc(), ou simplesmente colocar o sinal “-” antes da variável numérica que estamos ordenando.
dados_mpg %>% 
  count(combustivel) %>% 
  ggplot(aes(x = fct_reorder(combustivel, -n), y = n, fill = combustivel)) +
  geom_col() +
  scale_fill_viridis_d()

  • Melhorando o gráfico, temos:
dados_mpg %>% 
  count(combustivel) %>% 
  ggplot(aes(x = fct_reorder(combustivel, -n), y = n, fill = combustivel)) +
  geom_col() +
  scale_fill_viridis_d() +
  labs(
    title = "Comparando os tipos de combustíveis",
    subtitle = "(`p` = premium; `r` = regular; `e` = ethanol; `d` = diesel; `g` = gás)",
    x = "",
    y = ""
)

3.1 Gráfico de Barras

  • Mesma ideia do anterior
  • Geralmente usado quando o nome da variável é grande
  • o pacote scales será usado para exibir um dos eixos com notação de porcentagem.
    • esse pacote faz muitas outras coisas, mas só usaremos essa nesse exemplo.
    • se você não tiver instalado, faça-o com o comando install.packages("scales").
library(scales)

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
  • Usamos a função label_percent(), dentro da função scale_x_continuous(), no argumnento label.
    • também podemos delimitar o eixo \(x\) por aqui!
    • o termo accuracy diz quantas casas decimais desejamos, caso seja necessário.
dados_mpg %>% 
  count(classe) %>% 
  mutate(prop = n / sum(n)) %>% 
  ggplot() +
  aes(x = prop, y = fct_reorder(classe, prop), fill = classe) +
  geom_col() +
  scale_fill_viridis_d() +
  scale_x_continuous(
    labels = label_percent(accuracy = 1), 
    limits = c(0, .3)
  ) +
  labs(
    title = "Porcentagem das Classes dos Carros",
    x = "",
    y = ""
  ) +
  theme_minimal()

4. Histograma

  • Um histograma é uma representação gráfica da distribuição de uma variável numérica.
    • note que aceita apenas variáveis numéricas de entrada.
  • A variável é dividida em várias classes (intervalor)
    • o número de observações por classes é representado pela altura da barra.
  • Usaremos o dataset notas_tidy para exemplificação:
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_histogram(fill = "blue", color = "black") +
  labs(
    title = "Histograma das Notas",
    x = "Notas",
    y = ""
  ) +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

  • Podemos alterar o tamanho das classes usando o argumento binwidth
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_histogram(fill = "blue", color = "black", binwidth = 1) +
  labs(
    title = "Histograma das Notas",
    x = "Notas",
    y = ""
  ) +
  theme_minimal()

5. Gráfico de Densidade

  • Podemos interpretar como uma suavização do Histograma.
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_density(fill = "#404080")

  • Podemos comparar por curso
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_density(aes(fill = curso), alpha = 0.3) +
  scale_fill_viridis_d()

  • Ainda separando por “facetas” pelos cursos
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_density(aes(fill = curso), alpha = 0.3) +
  facet_grid(curso~.) +
  scale_fill_viridis_d()

  • Ou “facetando” ao longo dos anos
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_density(aes(fill = curso), alpha = 0.3) +
  facet_grid(ano~.) +
  scale_fill_viridis_d()

  • Este último, por conter muita informação, não ficou tão clara nossa comunicação.
    • Para contornar isso, podemos usar o pacote ggridges

5.1 Ridgeline (Joyplot)

  • Carregue (se preciso, instale-o antes) o pacote ggridges
# install.packages("ggridges")
library(ggridges)
  • Usaremos a função geom_density_ridges()
notas_tidy %>% 
  ggplot() +
  aes(x = notas, y = ano, fill = curso) +
  geom_density_ridges(alpha = 0.5) +
  scale_fill_viridis_d()
Picking joint bandwidth of 0.568

  • Podemos filtrar por um curso específico, por exemplo, Química.
notas_tidy %>% 
  filter(curso == "Qui") %>% 
  ggplot() +
  aes(x = notas, y = ano, fill = curso) +
  geom_density_ridges(alpha = 0.5) +
  scale_fill_viridis_d()
Picking joint bandwidth of 0.61

6. Boxplot

  • Já vimos muito sobre boxplot em nossa disciplina (inclusive artigos que explicam os pormenores)
  • Para exemplificar, continuaremos com o dataset notas_tidy.
    • inicialmente, vamos comparar, ao logo dos anos, os boxplots dos cursos
notas_tidy %>% 
  ggplot() +
  aes(x = ano, y= notas, fill = curso) +
  geom_boxplot() +
  scale_fill_viridis_d()

  • Agora, vamos comparar apenas as notas ao longo dos anos.
    • se quisermos omitir as legendas, usamos show.legend = FALSE
notas_tidy %>% 
  ggplot() +
  aes(x = ano, y= notas) +
  geom_boxplot(aes(fill = ano), show.legend = FALSE) +
  scale_fill_viridis_d()

Extra: reta vertical e anotações

  • Considere a densidade das notas do curso:
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_density(fill = "lightblue") +
  labs(
    title = "Distribuição das Notas",
    subtitle = "(Tanto de Matemática, quanto de Química)",
    x = "Notas",
    y = "Distribuição"
  ) +
  theme_minimal()

  • Como representar, por exemplo, a média dessas notas com uma reta vertical?
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_density(fill = "lightblue", alpha = 0.6) +
  labs(
    title = "Distribuição das Notas",
    subtitle = "(Tanto de Matemática, quanto de Química)",
    x = "Notas",
    y = "Distribuição"
  ) +
  theme_minimal() +
  geom_vline(aes(xintercept = mean(notas)), color = "red") 

  • E se quisermos colocar o nome “Média” nessa reta?
notas_tidy %>% 
  ggplot() +
  aes(x = notas) +
  geom_density(fill = "lightblue", alpha = 0.6) +
  geom_vline(aes(xintercept = mean(notas)), color = "red") +
  labs(
    title = "Distribuição das Notas",
    subtitle = "(Tanto de Matemática, quanto de Química)",
    x = "Notas",
    y = "Distribuição"
  ) +
  theme_minimal() +
  annotate("text", x = 6, y = 0.05, label = "Média", color = "red")

LS0tCnRpdGxlOiAiQWxnbyAobXVpdG8gcsOhcGlkbykgc29icmUgbyBnZ3Bsb3QyIgphdXRob3I6ICLDjWNhcm8gVmlkYWwgRnJlaXJlIgpkYXRlOiAiRXN0YXQgKENGUC9VRlJCKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGhpZ2hsaWdodDogdGFuZ28KICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCmBgYHtyIHNldHVwLCBlY2hvPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgbWVzc2FnZSA9IEZBTFNFLAogIHdhcm5pbmcgPSBGQUxTRSwKICBjb2xsYXBzZSA9IFRSVUUKKQpgYGAKCgotLS0KCiMjIFZpc8OjbyByw6FwaWRhCgotIE8gYGdncGxvdDJgIGZvaSBjcmlhZG8gcG9yIEhhZGxleSBXaWNraGFtLCBiYXNlYWRvIG5vIGxpdnJvICpHcmFtbWFyIG9mIEdyYXBoaWNzKiwgZGUgTGVsYW5kIFdpbGtpbnNvbi4KLSBBIGlkZWlhIMOpIHBlbnNhciBlbSB1bSAiZ3LDoWZpY28iIGNvbW8gdW1hIGVzdHJ1dHVyYSByYWNpb25hbCwgY29tcG9zdGEgcG9yICoqY2FtYWRhcyoqLgoKYGBge3IgY2FtYWRhcywgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltZy9ncmFtbWFyLW9mLWdyYXBoaWNzLnBuZyIpCmBgYAoKLSBOw6NvIHByZWNpc2EgdGVyIHRvZGFzIGFzIGNhbWFkYXMsIG1hcyB0csOqcyBzw6NvICoqb2JyaWdhdMOzcmlhcyoqOgogICsgKipkYXRhKio6IG8gY29uanVudG8gZGUgZGFkb3MgZGV2ZSBzZXIgdW1hIGB0aWJibGVgIChvdSwgcGVsbyBtZW5vcywgdW0gYGRhdGFmcmFtZWApOwogICsgKiphZXN0aGV0aWNzKio6IG1hcGVhbWVudG9zIGVzdMOpdGljb3MsIGNvbW8gc2VsZWNpb25hbWVudG8gZGFzIHZhcmnDoXZlaXMgZW0gcXVlc3TDo287CiAgKyAqKmdlb21ldHJpZXMqKjogc8OjbyBhcyBmb3JtYXMgZG8gZ3LDoWZpY28gKHBvbnRvcywgYm94cGxvdCwgaGlzdG9ncmFtYSwgZXRjKQoKLSBOw6NvIHZlcmVtb3MgdG9kb3Mgb3MgZ3LDoWZpY29zLCBhcGVuYXMgYWxndW5zIHBvdWNvczsKLSBBcGVuYXMgdGFuZ2VuY2lhcmVtb3MgbyBwYWNvdGUgISEKCiMjIFF1ZWlyYSBjb211bmljYXIsIG7Do28gImxhY3JhciIhCgohW10oaHR0cHM6Ly9zdGF0NTQ1LmNvbS9pbWcvbGVzcy1pcy1tb3JlLWRhcmtob3JzZS1hbmFseXRpY3MuZ2lmKQoKIyMgQW50ZXMgZGUgY29tZcOnYXIKCi0gVmFtb3MgY2FycmVnYXIgYSBiaWJsaW90ZWNhIGRvIGB0aWR5dmVyc2VgLCBuw6NvIGFwZW5hcyBhIGRvIGBnZ3Bsb3RgLgotIElzc28gc2UgZGV2ZSBhbyBmYXRvIGRlIHNlbXByZSBwcmVjaXNhcm1vcyBtYW5pcHVsYXIgb3MgZGFkb3MgYW50ZXMgZGUgcGxvdGFyIGFsZ3VtIGdyw6FmaWNvLgoKTyBjw7NkaWdvIGFiYWl4byBjYXJyZWdhIG8gcGFjb3RlOgoKYGBge3IgdGlkeXZlcnNlfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgotIERlcG9pcyBkaXNzbywgdXNhcmVtb3MgYSBmdW7Dp8OjbyBgcmVhZF9jc3YoKWAgcGFyYSBhY2Vzc2FyIG8gY29uanVudG8gZGUgZGFkb3MuCi0gVXNhcmVtb3MgbyBjb25qdW50byBkZSBkYWRvcyBgbWlsaGFzX3Bvcl9nYWxhby5jc3ZgLCBiZW0gY29tbyBvIGNvbmp1bnRvIGRlIGRhZG9zIGBub3Rhc19kaXNjaXBsaW5hcy5jc3ZgLCBhbWJvcyBkaXNwb27DrXZlaXMgZW0gbm9zc28gcmVwb3NpdMOzcmlvOiBbRGFkb3MuY3N2XShodHRwczovL2dpdGh1Yi5jb20vaWNhcm8tZnJlaXJlL2RhZG9zX2NzdikuCiAgKyBPIHByaW1laXJvIGNvbmp1bnRvIGRlIGRhZG9zLCBub21lYXJlbW9zIHBvciBgZGFkb3NfbXBnYCBlIG8gc2VndW5kbyBwb3IgYGRhZG9zX25vdGFzYAoKYGBge3IgbGVuZG8tZGFkb3N9CiMgTGVuZG8gbyBjb25qdW50byBgbWlsaGFzX3Bvcl9nYWxhby5jc3ZgCmRhZG9zX21wZyA8LSByZWFkX2NzdigiZGFkb3MvbWlsaGFzX3Bvcl9nYWxhby5jc3YiKQoKIyBMZW5kbyBvIGNvbmp1bnRvIGRlIGRhZG9zIGBub3Rhc19kaXNjaXBsaW5hLmNzdmAKZGFkb3Nfbm90YXMgPC0gcmVhZF9jc3YoImRhZG9zL25vdGFzX2Rpc2NpcGxpbmEuY3N2IikKYGBgCgotIE8gKmRhdGFzZXQqIGBkYWRvc19tcGdgIGZpY2FyaWEgYXNzaW06CgpgYGB7cn0KZGFkb3NfbXBnCmBgYAoKLSBKw6EgbyAqZGF0YXNldCogYGRhZG9zX25vdGFzYCwgYXNzaW06CiAgKyB2ZWphIHF1ZSBlc3NlIGNvbmp1bnRvIGRlIGRhZG9zIG7Do28gZXN0w6EgbmEgZm9ybWEgYHRpZHlgLiAKICAKYGBge3J9CmRhZG9zX25vdGFzCmBgYAoKIyMgU2ludGF4IGRvIGdncGxvdAoKYGBge3IsIGV2YWw9RkFMU0V9CmdncGxvdChkYWRvcywgbWFwcGluZyA9IGFlcyh4ID0gLi4uLCB5ID0gLi4uLCBvdXRyYXMgPSAuLi4pKSArCiAgZ2VvbV9mdW5jYW8ob3DDp8O1ZXMpCmBgYAoKT3UsIHVzYW5kbyBvICpwaXBlKjoKCmBgYHtyLCBldmFsPUZBTFNFfQpkYWRvcyAlPiUgCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IC4uLiwgeSA9IC4uLiwgb3V0cmFzID0gLi4uKSkgKwogIGdlb21fZnVuY2FvKG9ww6fDtWVzKQpgYGAKCi0gTm90ZSBxdWUgdXNhbW9zIG8gc2luYWwgImArYCIgcGFyYSAqYWRpY2lvbmFyKiBhcyBjYW1hZGFzIGRvIGBnZ3Bsb3RgLgotIFBhcmEgZmljYXIgbWFpcyBleHBsw61jaXRvLCDDoHMgdmV6ZXMgdXNhcmVtb3MgYXNzaW06CgpgYGB7ciwgZXZhbD1GQUxTRX0KZGFkb3MgJT4lIAogIGdncGxvdCgpICsKICBhZXMoeCA9IC4uLiwgeSA9IC4uLiwgb3V0cmFzID0gLi4uKSArCiAgZ2VvbV9mdW5jYW8ob3DDp8O1ZXMpCmBgYAoKIyMgMS4gR3LDoWZpY29zIGRlIFBvbnRvcwoKLSBUYW1iw6ltIHPDo28gY2hhbWFkb3MgZGUgImdyw6FmaWNvIGRlIGRpc3BlcnPDo28iICgqc2NhdHRlciBwbG90KikKLSBSZWxhY2lvbmEgdmFyacOhdmVpcyBudW3DqXJpY2FzLgotIFZhbW9zIHJlbGFjaW9uYXIgYSBxdWFudGlkYWRlIGRlIGNpbGluZHJhZGFzIGNvbSBhIHF1YW50aWRhZGUgZGUgbWlsaGFzIHBvciBnYWzDo28gZW0gcm9kb3ZpYS4KICArIG91IHNlamEsIHZhbW9zIHJlbGFjaW9uYXIgYXMgc2VndWludGVzIHZhcmnDoXZlaXMgbnVtw6lyaWNhcyBlbSBub3NzbyAqZGF0YXNldCo6IGByb2RvdmlhYCBlIGBjaWxpbmRyYWRhYC4KICArIGVzdGFtb3MgdXNhbmRvIG8gKmRhdGFzZXQqIGBkYWRvc19tcGdgLgotIE5lc3NlIGV4ZW1wbG8sIHZhbW9zIHVzYXIgYXMgY2FtYWRhcywgcGFzc28gYSBwYXNzbyAobWVzbW8gbsOjbyBwcmVjaXNhbmRvKQoKIyMjIDHCuiBDYW1hZGE6ICpEYXRhKiAoZGFkb3MpCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYWRvc19tcGcpCmBgYAoKLSBPYnNlcnZlIHF1ZSBzZSBjb2xvY2FybW9zIGFwZW5hcyBhIGNhbWFkYSBkb3MgImRhZG9zIiwgbsOjbyBhcGFyZWNlIChxdWFzZSkgbmFkYQogICsgY2FycmVnYS1zZSBhcGVuYXMgbyBjb25qdW50byBkZSBkYWRvcwoKIyMjIDLCuiBDYW1hZGE6ICpBZXN0aGV0aWNzKiAoZXN0w6l0aWNhKQoKLSBBY3Jlc2NlbnRhbmRvIGEgY2FtYWRhIGVzdMOpdGljYSBnZXJhbCAoYGFlcygpYCksIGrDoSBhcGFyZWNlbSBvcyB2YWxvcmVzIGRvIGVpeG8gJHgkIGUgb3MgdmFsb3JlcyBkbyBlaXhvICR5JC4KICArIHRhbWLDqW0gw6kgY29zdHVtZSBjb2xvY2FyIGVzc2EgY2FtYWRhIGRlbnRybyBkYSBjYW1hZGEgYGdncGxvdCgpYCwgYXNzaW06IGBnZ3Bsb3QoZGF0YSA9IC4uLiwgYWVzKHggPSAuLi4sIHkgPSAuLi4pKWAuCiAgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRhZG9zX21wZykgKwogIGFlcyh4ID0gcm9kb3ZpYSwgeSA9IGNpbGluZHJhZGEpCmBgYAojIyMgM8K6IENhbWFkYTogKkdlb21ldHJpZXMqIChnZW9tZXRyaWEpCgotIFZhbW9zIGFnb3JhIGFjcmVzY2VudGFyIGEgY2FtYWRhIGRhIGdlb21ldHJpYSBkb3MgZGFkb3MKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRhZG9zX21wZykgKwogIGFlcyh4ID0gY2lsaW5kcmFkYSwgeSA9IHJvZG92aWEpICsKICBnZW9tX3BvaW50KCkKYGBgCi0gVmVqYSBjb20gY29tIGVzc2FzIHRyw6pzIGNhbWFkYXMsIGrDoSB0ZW1vcyB1bSBjb25qdW50byBkZSBpbmZvcm1hw6fDtWVzIGLDoXNpY2FzIHNvYnJlIG8gcXVlIGRlc2VqYW1vcyBjb211bmljYXIuCiAgKyBhcyBvdXRyYXMgY2FtYWRhcywgZGVpeGFtIG8gZ3LDoWZpY28gbWFpcyAiYm9uaXRvIi4KLSBUYW1iw6ltIHBvZGVtb3MgYWNyZXNjZW50YXIgYXJndW1lbnRvcyBjb252ZW5pZW50ZXMgbmEgY2FtYWRhIGBHZW9tZXRyaWVzYCwgY29tbyBgc2l6ZWAgKHF1ZSBhbHRlcmEgbyB0YW1hbmhvIGRvcyBwb250b3MpCi0gQXBlbmFzIHBvciBzaW1wbGljaWRhZGUsIHZhbW9zIG9taXRpciBvIG5vbWUgImRhdGUiCgpgYGB7cn0KZ2dwbG90KGRhZG9zX21wZykgKwogIGFlcyh4ID0gY2lsaW5kcmFkYSwgeSA9IHJvZG92aWEpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKQpgYGAKCiMjIyA0wrogQ2FtYWRhOiAqRmFjZXRzKiAoZmFjZXRhcykKCi0gQW8gYWNyZXNjZW50YXIgYSBjYW1hZGEgImZhY2V0cyIsIHBhcnRpY3VsYXJpemFtb3MgYWxndW1hIGNhcmFjdGVyw61zdGljYSBkZXNlasOhdmVsCiAgKyBubyBleGVtcGxvLCBlc3RhbW9zIGZhY2V0YW5kbyBwZWxvIGFubywgb3Ugc2VqYSwgc2VwYXJhbmRvIG9zIGRhZG9zIHBvciBhbm8uCiAgKyB1c2FyZW1vcyBhIGZ1bsOnw6NvIGBmYWNldF9ncmlkKClgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYWRvc19tcGcpICsKICBhZXMoeCA9IGNpbGluZHJhZGEsIHkgPSByb2RvdmlhKSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIGZhY2V0X2dyaWQoYW5vfi4pCmBgYAotIFZlamEgcXVlICB1c2Ftb3MgbyBhcmd1bWVudG8gYGFub34uYAogICsgaXNzbyBmYXogY29tIHF1ZSBhIHZhcmnDoXZlbCBgYW5vYCBmaXF1ZSBuYSAiaG9yaXpvbnRhbCIKICArIFNlIG11ZGFybW9zIGEgcG9zacOnw6NvIGRlc3NlIGFyZ3VtZW50bywgb3Ugc2VqYSwgYC5+YW5vYCwgZmljYSBhc3NpbToKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRhZG9zX21wZykgKwogIGFlcyh4ID0gY2lsaW5kcmFkYSwgeSA9IHJvZG92aWEpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgZmFjZXRfZ3JpZCgufmFubykKYGBgCi0gQWluZGEgcG9kZW1vcyBtdWRhciBhIGNvciBkb3MgcG9udG9zLCBkZSBhY29yZG8gY29tIGEgY2xhc3NlIGRvcyBjYXJyb3MuCiAgKyBwYXJhIGlzc28gdXNhcmVtb3MgbyBhcmd1bWVudG8gYGNvbG9yID0gY2xhc3NlYAogICsgZGV2ZW1vcyBhY3Jlc2NlbnRhciBvIGFyZ3VtZW50byBgY29sb3JgIGRlbnRybyBkZSB1bSBgYWVzKClgIChsb2NhbCksIHBvaXMgcmVsYWNpb25hIMOgIG11ZGFuw6dhcyBuYSAqKnZhcmnDoXZlbCoqIGRvIGRhdGFzZXQgKG91IHNlamEsIGVsZSBtYXBlYXLDoSBhcyBjYXJhY3RlcsOtc3RpY2FzIGVzdMOpdGljYXMgZW0gY2FkYSBwYXJ0ZSBkYSB2YXJpw6F2ZWwpOwogICsgc2UgYSBtdWRhbsOnYSDDqSBhcGVuYXMgbnVtw6lyaWNhLCBuw6NvIHByZWNpc2EgZG8gYGFlcygpYAogIApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYWRvc19tcGcpICsKICBhZXMoeCA9IGNpbGluZHJhZGEsIHkgPSByb2RvdmlhKSArCiAgZ2VvbV9wb2ludChzaXplID0gMiwgYWVzKGNvbG9yID0gY2xhc3NlKSkgKwogIGZhY2V0X2dyaWQoLn5hbm8pCmBgYAotIFNlIG7Do28gdGl2w6lzc2Vtb3MgYWNyZXNjZW50YWRvIG8gYGFlcygpYCBmaWNhcmlhIGFzc2ltIChjb20gZXJybyk6CgpgYGB7ciwgZXZhbD1GQUxTRX0KZ2dwbG90KGRhdGEgPSBkYWRvc19tcGcpICsKICBhZXMoeCA9IGNpbGluZHJhZGEsIHkgPSByb2RvdmlhKSArCiAgZ2VvbV9wb2ludChzaXplID0gMiwgY29sb3IgPSBjbGFzc2UpICsKICBmYWNldF9ncmlkKC5+YW5vKQpgYGAKCiMjIyA1wrogQ2FtYWRhOiAqU3RhdGlzdGljcyogKGVzdGF0w61zdGljYXMpCgotIFBvZGUgc2VyIGEgbcOpZGlhLCBtZWRpYW5hLCB1bWEgcmVncmEgZGUgcmVncmVzc8OjbywgZXRjLgotIHBhcmEgZXNzZSBleGVtcGxvLCB1c2FyZW1vcyB1bWEgY3VydmEgcXVlIG1lbGhvciBzZSBhanVzdGEgbm9zIGNvbmp1bnRvcyBkZSBwb250b3MuCiAgKyBwYXJhIGlzc28sIHVzYXJlbW9zIGEgZnVuw6fDo28gYGdlb21fc21vb3RoKClgCiAgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRhZG9zX21wZykgKwogIGFlcyh4ID0gY2lsaW5kcmFkYSwgeSA9IHJvZG92aWEpICsKICBnZW9tX3BvaW50KHNpemUgPSAyLCBhZXMoY29sb3IgPSBjbGFzc2UpKSArCiAgZmFjZXRfZ3JpZCgufmFubykgKwogIGdlb21fc21vb3RoKCkKYGBgCgotIFNlIHF1aXNlcm1vcyB1bWEgcmV0YSBxdWUgbWVsaG9yIHNlIGFkZXF1ZSBhb3MgcG9udG9zLCBkZXZlbW9zIGFjcmVzY2VudGFyIG8gYXJndW1lbnRvIGBgbWV0aG9kID0gbG1gOgoKYGBge3J9CmdncGxvdChkYXRhID0gZGFkb3NfbXBnKSArCiAgYWVzKHggPSBjaWxpbmRyYWRhLCB5ID0gcm9kb3ZpYSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFlcyhjb2xvciA9IGNsYXNzZSkpICsKICBmYWNldF9ncmlkKC5+YW5vKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pCmBgYAoKLSBDYXNvIHNlamEgY29udmVuaWVudGUgcmV0aXJhcm1vcyBvICJzb21icmVhbWVudG8iIChpbnRlcnZhbG8gZGUgY29uZmlhbsOnYSkgZGEgY3VydmEgKGluY2x1c2l2ZSBhIHJldGEpLCB1c2Ftb3MgbyBhcmd1bWVudG8gYHNlID0gRkFMU0VgLgoKYGBge3J9CmdncGxvdChkYXRhID0gZGFkb3NfbXBnKSArCiAgYWVzKHggPSBjaWxpbmRyYWRhLCB5ID0gcm9kb3ZpYSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFlcyhjb2xvciA9IGNsYXNzZSkpICsKICBmYWNldF9ncmlkKC5+YW5vKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIHNlID0gRkFMU0UpCmBgYAoKLSBFLCBzZSBxdWlzZXJtb3MgbXVkYXIgYSBjb3IgZGVzc2EgcmV0YSBkZSByZWdyZXNzw6NvLCBwb2RlbW9zIGZhemVyIGRlbnRybyBkZXNzYSBjYW1hZGE6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYWRvc19tcGcpICsKICBhZXMoeCA9IGNpbGluZHJhZGEsIHkgPSByb2RvdmlhKSArCiAgZ2VvbV9wb2ludChzaXplID0gMiwgYWVzKGNvbG9yID0gY2xhc3NlKSkgKwogIGZhY2V0X2dyaWQoLn5hbm8pICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikKYGBgCgojIyMgNsK6IENhbWFkYTogKkNvb3JkaW5hdGVzKiAoY29vcmRlbmFkYXMpCgotIFBvZGVtb3MgdXNhciBjb29yZGVuYWRhcyBjYXJ0ZXNpYW5hcyBvdSBwb2xhcmVzOwogICsgbm8gbm9zc28gZXhlbXBsbywgZXN0YW1vcyB1c2FuZG8gY29vcmRlbmFkYXMgcG9sYXJlcy4KLSBUYW1iw6ltIMOpIHBvc3PDrXZlbCBkZWxpbWl0YXIgb3MgZWl4b3MgY29vcmRlbmFkb3MuCiAgKyBlbSBub3NzbyBjYXNvLCB1c2FyZW1vcyBhcyBmdW7Dp8O1ZXMgYHhsaW1gIGUgYHlsaW1gIGRlbnRybyBkYSBmdW7Dp8OjbyBgY29vcmRfY2FydGVzaWFuKClgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYWRvc19tcGcpICsKICBhZXMoeCA9IGNpbGluZHJhZGEsIHkgPSByb2RvdmlhKSArCiAgZ2VvbV9wb2ludChzaXplID0gMiwgYWVzKGNvbG9yID0gY2xhc3NlKSkgKwogIGZhY2V0X2dyaWQoLn5hbm8pICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCA4KSwgeWxpbSA9IGMoMTAsIDQ1KSkKYGBgCgojIyMgN8K6IENhbWFkYTogKlRoZW1lKgoKLSBQb2RlbW9zIGNvbG9jYXIgdW0gdGVtYSBxdWUgbWVsaG9yIHNlIGFkZXF1ZSBhbyBhc3BlY3RvIGRlIG5vc3NvIHRleHRvCiAgKyBubyBub3NzbyBjYXNvLCB2YW1vcyB1c2FyIG8gdGVtYSBgdGhlbWVfYndgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRhZG9zX21wZykgKwogIGFlcyh4ID0gY2lsaW5kcmFkYSwgeSA9IHJvZG92aWEpICsKICBnZW9tX3BvaW50KHNpemUgPSAyLCBhZXMoY29sb3IgPSBjbGFzc2UpKSArCiAgZmFjZXRfZ3JpZCgufmFubykgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJyZWQiKSArCiAgdGhlbWVfYncoKQpgYGAKCi0gVGFtYsOpbSBwb2RlbW9zIG1vZGlmaWNhciBvIHTDrXR1bG8gZSBvcyBub21lcyBkb3MgZWl4b3MgY29tIGEgZnVuw6fDo28gYGxhYnMoKWAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRhZG9zX21wZykgKwogIGFlcyh4ID0gY2lsaW5kcmFkYSwgeSA9IHJvZG92aWEpICsKICBnZW9tX3BvaW50KHNpemUgPSAyLCBhZXMoY29sb3IgPSBjbGFzc2UpKSArCiAgZmFjZXRfZ3JpZCgufmFubykgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJyZWQiKSArCiAgdGhlbWVfYncoKSArCiAgbGFicygKICAgIHRpdGxlID0gIkNvcnJlbGHDp8OjbyBlbnRyZSB2YXJpw6F2ZWlzIG51bcOpcmljYXMiLAogICAgc3VidGl0bGUgPSAiQ2lsaW5kcmFkYSB2cyBNaWxoYXMgcG9yIEdhbMOjbyIsCiAgICB4ID0gIkNpbGluZHJhZGEiLAogICAgeSA9ICJNaWxoYXMgcG9yIEdhbMOjbyBkZSBDb21idXN0w612ZWwiCiAgKQpgYGAKCiMjIDIuIEdyw6FmaWNvcyBkZSBsaW5oYQoKLSBVc2FyZW1vcyBwYXJhIGVzc2UgZ3LDoWZpY28gbyAqZGF0YXNldCogYG5vdGFzX2Rpc2NpcGxpbmEuY3N2YC4KLSBQcmVjaXNhbW9zIGRlaXjDoS1sbyBuYSBmb3JtYSBgdGlkeWBgLCBwaXZvdGFuZG8gYXMgY29sdW5hcyAoasOhIHZpbW9zIGNvbW8gZmF6IGlzc28pCiAgKyB0YW1iw6ltIHJlbm9tZWFyZW1vcyB1bWEgZGFzIHZhcmnDoXZlaXMKICArIGNoYW1hcmVtb3MgZXNzZXMgZGFkb3MgbW9kaWZpY2Fkb3MgcG9yIGBub3Rhc190aWR5YAoKYGBge3J9Cm5vdGFzX3RpZHkgPC0gZGFkb3Nfbm90YXMgJT4lIAogIHBpdm90X2xvbmdlcigKICAgICFDdXJzbywKICAgIG5hbWVzX3RvID0gImFubyIsCiAgICB2YWx1ZXNfdG8gPSAibm90YXMiCiAgKSAlPiUgCiAgcmVuYW1lKGN1cnNvID0gQ3Vyc28pCgpub3Rhc190aWR5CmBgYAoKLSBQYXJhIGNyaWFybW9zIG8gZ3LDoWZpY28gZGUgbGluaGFzLCB2YW1vcyB1c2FyIGFzIG3DqWRpYXMgZG8gY29uanVudG8gZGUgZGFkb3MgYW8gbG9uZ28gZG9zIGFub3MuCi0gQWdvcmEsIHZhbW9zIHN1bWFyaXphciBhcyBtw6lkaWFzIGRhcyB0dXJtYXMgcG9yIGFubzoKICArIGNoYW1hcmVtb3MgZGUgYHBgCgpgYGB7cn0KcCA8LSBub3Rhc190aWR5ICU+JSAKICBncm91cF9ieShhbm8pICU+JSAKICBzdW1tYXJpc2UoCiAgICBtZWRpYV9ub3RhcyA9IHJvdW5kKG1lYW4obm90YXMpLCAxKQopCgpwCmBgYAoKYGBge3J9CiBwICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBhbm8sIHkgPSBtZWRpYV9ub3RhcywgZ3JvdXAgPSAxKSkgKwogIGdlb21fbGluZShjb2xvciA9ICIjQkY2MTZBIiwgc2l6ZSA9IDEpCmBgYAotIE5vdGUgcXVlOgogICsgdXNhbW9zIG8gY29tYW5kbyBgZ3JvdXAgPSAxYCBubyBgYWVzKClgIGdlcmFsLgogICsgZXNjb2xoZW1vcyB1bWEgY29yIHZlcm1lbGhhIGVzcGVjw61maWNhIChjb20gc3Vwb3J0ZSBlbSBodG1sKQotIE8gY8OzZGlnbyBjb21wbGV0bywgasOhIGNvbSBvIHRlbWEsIGZpY2EKCmBgYHtyfQogbm90YXNfdGlkeSAlPiUgCiAgZ3JvdXBfYnkoYW5vKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgbWVkaWFfbm90YXMgPSByb3VuZChtZWFuKG5vdGFzKSwgMSkKICApICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBhbm8sIHkgPSBtZWRpYV9ub3RhcywgZ3JvdXAgPSAxKSkgKwogIGdlb21fbGluZShjb2xvciA9ICIjQkY2MTZBIiwgc2l6ZSA9IDEpICsKICBsYWJzKAogICAgdGl0bGUgPSAiR3LDoWZpY28gZGUgTGluaGFzIGRhcyBOb3RhcyIsCiAgICB4ID0gIiIsCiAgICB5ID0gIk3DqWRpYSBkYXMgTm90YXMiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAotIFZlamEgcXVlIG8gImArYCIgc8OzIMOpIGNvbG9jYWRvIG5hcyAqKmNhbWFkYXMqKiBkbyBnZ29sb3QuCiAgKyB1bSBlcnJvIGNvbXVtIMOpIHRyb2NhciBvIGArYCBwZWxvIGAgJT4lIGAgKHBpcGUpCgojIyAzLiBHcsOhZmljbyBkZSBDb2x1bmFzCgotIFBhcmEgbyBncsOhZmljbyBkZSBjb2x1bmFzLCB2YW1vcyB1c2FyIG8gYGRhZG9zX21wZ2AgZSByZWxhY2lvbmFybW9zIHF1YW50b3MgY2Fycm9zIHVzYW0gb3MgZGlmZXJlbnRlcyBjb21idXN0w612ZWlzIChgcGAgPSAqcHJlbWl1bSo7IGByYCA9ICpyZWd1bGFyKjsgYGVgID0gKmV0aGFub2wqOyBgZGAgPSAqZGllc2VsKjsgYGdgID0gKmfDoXMqKS4KCmBgYHtyfQpkYWRvc19tcGcgJT4lIAogIGNvdW50KGNvbWJ1c3RpdmVsKQpgYGAKCi0gQXNzaW0sIHVzYXJlbW9zIGEgZnVuw6fDo28gYGdlb21fY29sKClgIHBhcmEgY3JpYXJtb3MgbyBncsOhZmljbyBkZSBjb2x1bmFzLCByZWxhY2lvbmFuZG8gbyBjb21idXN0w612ZWwgY29tIHN1YSBmcmVxdcOqbmNpYSBhYnNvbHV0YSBgbmAKCmBgYHtyfQpkYWRvc19tcGcgJT4lIAogIGNvdW50KGNvbWJ1c3RpdmVsKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb21idXN0aXZlbCwgeSA9IG4pKSArCiAgZ2VvbV9jb2woKQpgYGAKLSBTZSBxdWlzZXJtb3MgcHJlZW5jaGVyIGNhZGEgY29sdW5hIGNvbSB1bWEgY29yIGRlIGFjb3JkbyBjb20gbyB0aXBvIGRlIGNvbWJ1c3TDrXZlbCwgZGV2ZW1vcyBhY3Jlc2NlbnRhciBubyBgYWVzKClgIHVtIGFyZ3VtZW50byBwYXJhIHRhbCBmYXRvLgogICsgbyBhcmd1bWVudG8gYGNvbG9yYCB2YWkgY29icmlyIGEgZnJvbnRlaXJhIGRvcyBncsOhZmljb3M7CiAgKyBvIGFyZ3VtZW50byBgZmlsbGAgdmFpIHByZWVuY2hlciBvIGludGVyaW9yIGRvcyBncsOhZmljb3MuCgpgYGB7cn0KZGFkb3NfbXBnICU+JSAKICBjb3VudChjb21idXN0aXZlbCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gY29tYnVzdGl2ZWwsIHkgPSBuICwgY29sb3IgPSBjb21idXN0aXZlbCkpICsKICBnZW9tX2NvbCgpCmBgYAoKYGBge3J9CmRhZG9zX21wZyAlPiUgCiAgY291bnQoY29tYnVzdGl2ZWwpICU+JQogIGdncGxvdChhZXMoeCA9IGNvbWJ1c3RpdmVsLCB5ID0gbiAsIGZpbGwgPSBjb21idXN0aXZlbCkpICsKICBnZW9tX2NvbCgpCmBgYAotIEjDoSB1bSBjb25qdW50byBkZSBjb3JlcyAoW3ZpcmlkaXNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy92aXJpZGlzL3ZpZ25ldHRlcy9pbnRyby10by12aXJpZGlzLmh0bWwpKSBxdWUgYWp1ZGEgbmEgdmlzdWFsaXphw6fDo28gZGUgcGVzc29hcyBjb20gZGlmaWN1bGRhZGUgZGUgdmlzw6NvIG91IGRhbHRvbmlzbW8KICArIHBhcnRpY3VsYXJtZW50ZSwgdXNvIGEgdmFyaWHDp8OjbyBgZGAgKCpkZWZhdWx0KikKCmBgYHtyfQpkYWRvc19tcGcgJT4lIAogIGNvdW50KGNvbWJ1c3RpdmVsKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBjb21idXN0aXZlbCwgeSA9IG4gLCBmaWxsID0gY29tYnVzdGl2ZWwpKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKQpgYGAKLSBVbWEgbWFuZWlyYSByw6FwaWRhIGRlIHJlb3JkZW5hcm1vcyBhcyBjb2x1bmFzIGRlIGZvcm1hIGNyZXNjZW50ZSDDqSB1c2FybW9zIGEgZnVuw6fDo28gYGZjdF9yZW9yZGVyKClgLCBkbyBwYWNvdGUgW2Bmb3JjYXRzYF0oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcvKSAoaW5jbHVzbyBubyBgdGlkeXZlcnNlYCkuCgpgYGB7cn0KZGFkb3NfbXBnICU+JSAKICBjb3VudChjb21idXN0aXZlbCkgJT4lIAogIGdncGxvdChhZXMoeCA9IGZjdF9yZW9yZGVyKGNvbWJ1c3RpdmVsLCBuKSwgeSA9IG4sIGZpbGwgPSBjb21idXN0aXZlbCkpICsKICBnZW9tX2NvbCgpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpCmBgYAotIFBhcmEgYSBvcmRlbmHDp8OjbyBkZSBmb3JtYSBkZWNyZXNjZW50ZSwgcG9kZW1vcyB1c2FyIGEgZnVuw6fDo28gYGRlc2MoKWAsIG91IHNpbXBsZXNtZW50ZSBjb2xvY2FyIG8gc2luYWwgImAtYCIgYW50ZXMgZGEgdmFyacOhdmVsIG51bcOpcmljYSBxdWUgZXN0YW1vcyBvcmRlbmFuZG8uCgpgYGB7cn0KZGFkb3NfbXBnICU+JSAKICBjb3VudChjb21idXN0aXZlbCkgJT4lIAogIGdncGxvdChhZXMoeCA9IGZjdF9yZW9yZGVyKGNvbWJ1c3RpdmVsLCAtbiksIHkgPSBuLCBmaWxsID0gY29tYnVzdGl2ZWwpKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKQpgYGAKLSBNZWxob3JhbmRvIG8gZ3LDoWZpY28sIHRlbW9zOgoKYGBge3J9CmRhZG9zX21wZyAlPiUgCiAgY291bnQoY29tYnVzdGl2ZWwpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBmY3RfcmVvcmRlcihjb21idXN0aXZlbCwgLW4pLCB5ID0gbiwgZmlsbCA9IGNvbWJ1c3RpdmVsKSkgKwogIGdlb21fY29sKCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJDb21wYXJhbmRvIG9zIHRpcG9zIGRlIGNvbWJ1c3TDrXZlaXMiLAogICAgc3VidGl0bGUgPSAiKGBwYCA9IHByZW1pdW07IGByYCA9IHJlZ3VsYXI7IGBlYCA9IGV0aGFub2w7IGBkYCA9IGRpZXNlbDsgYGdgID0gZ8OhcykiLAogICAgeCA9ICIiLAogICAgeSA9ICIiCikKYGBgCgoKIyMjIDMuMSBHcsOhZmljbyBkZSBCYXJyYXMKCi0gTWVzbWEgaWRlaWEgZG8gYW50ZXJpb3IKLSBHZXJhbG1lbnRlIHVzYWRvIHF1YW5kbyBvIG5vbWUgZGEgdmFyacOhdmVsIMOpIGdyYW5kZQotIG8gcGFjb3RlIGBzY2FsZXNgIHNlcsOhIHVzYWRvIHBhcmEgZXhpYmlyIHVtIGRvcyBlaXhvcyBjb20gbm90YcOnw6NvIGRlIHBvcmNlbnRhZ2VtLgogICsgZXNzZSBwYWNvdGUgZmF6IG11aXRhcyBvdXRyYXMgY29pc2FzLCBtYXMgc8OzIHVzYXJlbW9zIGVzc2EgbmVzc2UgZXhlbXBsby4KICArIHNlIHZvY8OqIG7Do28gdGl2ZXIgaW5zdGFsYWRvLCBmYcOnYS1vIGNvbSBvIGNvbWFuZG8gYGluc3RhbGwucGFja2FnZXMoInNjYWxlcyIpYC4KCmBgYHtyfQpsaWJyYXJ5KHNjYWxlcykKYGBgCgotIFVzYW1vcyBhIGZ1bsOnw6NvIGBsYWJlbF9wZXJjZW50KClgLCBkZW50cm8gZGEgZnVuw6fDo28gYHNjYWxlX3hfY29udGludW91cygpYCwgbm8gYXJndW1uZW50byBgbGFiZWxgLgogICsgdGFtYsOpbSBwb2RlbW9zIGRlbGltaXRhciBvIGVpeG8gJHgkIHBvciBhcXVpIQogICsgbyB0ZXJtbyBgYWNjdXJhY3lgIGRpeiBxdWFudGFzIGNhc2FzIGRlY2ltYWlzIGRlc2VqYW1vcywgY2FzbyBzZWphIG5lY2Vzc8OhcmlvLgoKYGBge3J9CmRhZG9zX21wZyAlPiUgCiAgY291bnQoY2xhc3NlKSAlPiUgCiAgbXV0YXRlKHByb3AgPSBuIC8gc3VtKG4pKSAlPiUgCiAgZ2dwbG90KCkgKwogIGFlcyh4ID0gcHJvcCwgeSA9IGZjdF9yZW9yZGVyKGNsYXNzZSwgcHJvcCksIGZpbGwgPSBjbGFzc2UpICsKICBnZW9tX2NvbCgpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBsYWJlbHMgPSBsYWJlbF9wZXJjZW50KGFjY3VyYWN5ID0gMSksIAogICAgbGltaXRzID0gYygwLCAuMykKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiUG9yY2VudGFnZW0gZGFzIENsYXNzZXMgZG9zIENhcnJvcyIsCiAgICB4ID0gIiIsCiAgICB5ID0gIiIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyA0LiBIaXN0b2dyYW1hCgotIFVtIGhpc3RvZ3JhbWEgw6kgdW1hIHJlcHJlc2VudGHDp8OjbyBncsOhZmljYSBkYSBkaXN0cmlidWnDp8OjbyBkZSB1bWEgdmFyacOhdmVsIG51bcOpcmljYS4gCiAgKyBub3RlIHF1ZSBhY2VpdGEgYXBlbmFzIHZhcmnDoXZlaXMgbnVtw6lyaWNhcyBkZSBlbnRyYWRhLiAKLSBBIHZhcmnDoXZlbCDDqSBkaXZpZGlkYSBlbSB2w6FyaWFzIGNsYXNzZXMgKGludGVydmFsb3IpCiAgKyBvIG7Dum1lcm8gZGUgb2JzZXJ2YcOnw7VlcyBwb3IgY2xhc3NlcyDDqSByZXByZXNlbnRhZG8gcGVsYSBhbHR1cmEgZGEgYmFycmEuCi0gVXNhcmVtb3MgbyAqZGF0YXNldCogYG5vdGFzX3RpZHlgIHBhcmEgZXhlbXBsaWZpY2HDp8OjbzoKCmBgYHtyfQpub3Rhc190aWR5ICU+JSAKICBnZ3Bsb3QoKSArCiAgYWVzKHggPSBub3RhcykgKwogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJIaXN0b2dyYW1hIGRhcyBOb3RhcyIsCiAgICB4ID0gIk5vdGFzIiwKICAgIHkgPSAiIgogICkgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCi0gUG9kZW1vcyBhbHRlcmFyIG8gdGFtYW5obyBkYXMgY2xhc3NlcyB1c2FuZG8gbyBhcmd1bWVudG8gYGJpbndpZHRoYAoKYGBge3J9Cm5vdGFzX3RpZHkgJT4lIAogIGdncGxvdCgpICsKICBhZXMoeCA9IG5vdGFzKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJibHVlIiwgY29sb3IgPSAiYmxhY2siLCBiaW53aWR0aCA9IDEpICsKICBsYWJzKAogICAgdGl0bGUgPSAiSGlzdG9ncmFtYSBkYXMgTm90YXMiLAogICAgeCA9ICJOb3RhcyIsCiAgICB5ID0gIiIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyA1LiBHcsOhZmljbyBkZSBEZW5zaWRhZGUKCi0gUG9kZW1vcyBpbnRlcnByZXRhciBjb21vIHVtYSBgc3Vhdml6YcOnw6NvYCBkbyBIaXN0b2dyYW1hLgoKYGBge3J9Cm5vdGFzX3RpZHkgJT4lIAogIGdncGxvdCgpICsKICBhZXMoeCA9IG5vdGFzKSArCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiIzQwNDA4MCIpCmBgYAotIFBvZGVtb3MgY29tcGFyYXIgcG9yIGN1cnNvCgpgYGB7cn0Kbm90YXNfdGlkeSAlPiUgCiAgZ2dwbG90KCkgKwogIGFlcyh4ID0gbm90YXMpICsKICBnZW9tX2RlbnNpdHkoYWVzKGZpbGwgPSBjdXJzbyksIGFscGhhID0gMC4zKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKQpgYGAKLSBBaW5kYSBzZXBhcmFuZG8gcG9yICJmYWNldGFzIiBwZWxvcyBjdXJzb3MKCmBgYHtyfQpub3Rhc190aWR5ICU+JSAKICBnZ3Bsb3QoKSArCiAgYWVzKHggPSBub3RhcykgKwogIGdlb21fZGVuc2l0eShhZXMoZmlsbCA9IGN1cnNvKSwgYWxwaGEgPSAwLjMpICsKICBmYWNldF9ncmlkKGN1cnNvfi4pICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpCmBgYAotIE91ICJmYWNldGFuZG8iIGFvIGxvbmdvIGRvcyBhbm9zCgpgYGB7cn0Kbm90YXNfdGlkeSAlPiUgCiAgZ2dwbG90KCkgKwogIGFlcyh4ID0gbm90YXMpICsKICBnZW9tX2RlbnNpdHkoYWVzKGZpbGwgPSBjdXJzbyksIGFscGhhID0gMC4zKSArCiAgZmFjZXRfZ3JpZChhbm9+LikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkKYGBgCi0gRXN0ZSDDumx0aW1vLCBwb3IgY29udGVyIG11aXRhIGluZm9ybWHDp8OjbywgbsOjbyBmaWNvdSB0w6NvIGNsYXJhIG5vc3NhIGNvbXVuaWNhw6fDo28uCiAgKyBQYXJhIGNvbnRvcm5hciBpc3NvLCBwb2RlbW9zIHVzYXIgbyBwYWNvdGUgW2BnZ3JpZGdlc2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ3JpZGdlcy92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwpCgojIyMgNS4xIFJpZGdlbGluZSAoSm95cGxvdCkKCi0gQ2FycmVndWUgKHNlIHByZWNpc28sIGluc3RhbGUtbyBhbnRlcykgbyBwYWNvdGUgYGdncmlkZ2VzYAoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygiZ2dyaWRnZXMiKQpsaWJyYXJ5KGdncmlkZ2VzKQpgYGAKCi0gVXNhcmVtb3MgYSBmdW7Dp8OjbyBgZ2VvbV9kZW5zaXR5X3JpZGdlcygpYAoKYGBge3J9Cm5vdGFzX3RpZHkgJT4lIAogIGdncGxvdCgpICsKICBhZXMoeCA9IG5vdGFzLCB5ID0gYW5vLCBmaWxsID0gY3Vyc28pICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC41KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKQpgYGAKCi0gUG9kZW1vcyBmaWx0cmFyIHBvciB1bSBjdXJzbyBlc3BlY8OtZmljbywgcG9yIGV4ZW1wbG8sIFF1w61taWNhLgoKYGBge3J9Cm5vdGFzX3RpZHkgJT4lIAogIGZpbHRlcihjdXJzbyA9PSAiUXVpIikgJT4lIAogIGdncGxvdCgpICsKICBhZXMoeCA9IG5vdGFzLCB5ID0gYW5vLCBmaWxsID0gY3Vyc28pICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC41KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKQpgYGAKCiMjIDYuIEJveHBsb3QKCi0gSsOhIHZpbW9zIG11aXRvIHNvYnJlIGJveHBsb3QgZW0gbm9zc2EgZGlzY2lwbGluYSAoaW5jbHVzaXZlIGFydGlnb3MgcXVlIGV4cGxpY2FtIG9zIHBvcm1lbm9yZXMpCi0gUGFyYSBleGVtcGxpZmljYXIsIGNvbnRpbnVhcmVtb3MgY29tIG8gKmRhdGFzZXQqIGBub3Rhc190aWR5YC4KICArIGluaWNpYWxtZW50ZSwgdmFtb3MgY29tcGFyYXIsIGFvIGxvZ28gZG9zIGFub3MsIG9zIGJveHBsb3RzIGRvcyBjdXJzb3MKICAKYGBge3J9Cm5vdGFzX3RpZHkgJT4lIAogIGdncGxvdCgpICsKICBhZXMoeCA9IGFubywgeT0gbm90YXMsIGZpbGwgPSBjdXJzbykgKwogIGdlb21fYm94cGxvdCgpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpCmBgYAotIEFnb3JhLCB2YW1vcyBjb21wYXJhciBhcGVuYXMgYXMgbm90YXMgYW8gbG9uZ28gZG9zIGFub3MuCiAgKyBzZSBxdWlzZXJtb3Mgb21pdGlyIGFzIGxlZ2VuZGFzLCB1c2Ftb3MgYHNob3cubGVnZW5kID0gRkFMU0VgCgpgYGB7cn0Kbm90YXNfdGlkeSAlPiUgCiAgZ2dwbG90KCkgKwogIGFlcyh4ID0gYW5vLCB5PSBub3RhcykgKwogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IGFubyksIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpCmBgYAojIyBFeHRyYTogcmV0YSB2ZXJ0aWNhbCBlIGFub3Rhw6fDtWVzCgotIENvbnNpZGVyZSBhIGRlbnNpZGFkZSBkYXMgbm90YXMgZG8gY3Vyc286CgpgYGB7cn0Kbm90YXNfdGlkeSAlPiUgCiAgZ2dwbG90KCkgKwogIGFlcyh4ID0gbm90YXMpICsKICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJsaWdodGJsdWUiKSArCiAgbGFicygKICAgIHRpdGxlID0gIkRpc3RyaWJ1acOnw6NvIGRhcyBOb3RhcyIsCiAgICBzdWJ0aXRsZSA9ICIoVGFudG8gZGUgTWF0ZW3DoXRpY2EsIHF1YW50byBkZSBRdcOtbWljYSkiLAogICAgeCA9ICJOb3RhcyIsCiAgICB5ID0gIkRpc3RyaWJ1acOnw6NvIgogICkgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKLSBDb21vIHJlcHJlc2VudGFyLCBwb3IgZXhlbXBsbywgYSBtw6lkaWEgZGVzc2FzIG5vdGFzIGNvbSB1bWEgcmV0YSB2ZXJ0aWNhbD8KCmBgYHtyfQpub3Rhc190aWR5ICU+JSAKICBnZ3Bsb3QoKSArCiAgYWVzKHggPSBub3RhcykgKwogIGdlb21fZGVuc2l0eShmaWxsID0gImxpZ2h0Ymx1ZSIsIGFscGhhID0gMC42KSArCiAgbGFicygKICAgIHRpdGxlID0gIkRpc3RyaWJ1acOnw6NvIGRhcyBOb3RhcyIsCiAgICBzdWJ0aXRsZSA9ICIoVGFudG8gZGUgTWF0ZW3DoXRpY2EsIHF1YW50byBkZSBRdcOtbWljYSkiLAogICAgeCA9ICJOb3RhcyIsCiAgICB5ID0gIkRpc3RyaWJ1acOnw6NvIgogICkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4obm90YXMpKSwgY29sb3IgPSAicmVkIikgCmBgYAotIEUgc2UgcXVpc2VybW9zIGNvbG9jYXIgbyBub21lICJNw6lkaWEiIG5lc3NhIHJldGE/CgpgYGB7cn0Kbm90YXNfdGlkeSAlPiUgCiAgZ2dwbG90KCkgKwogIGFlcyh4ID0gbm90YXMpICsKICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG5vdGFzKSksIGNvbG9yID0gInJlZCIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnVpw6fDo28gZGFzIE5vdGFzIiwKICAgIHN1YnRpdGxlID0gIihUYW50byBkZSBNYXRlbcOhdGljYSwgcXVhbnRvIGRlIFF1w61taWNhKSIsCiAgICB4ID0gIk5vdGFzIiwKICAgIHkgPSAiRGlzdHJpYnVpw6fDo28iCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSA2LCB5ID0gMC4wNSwgbGFiZWwgPSAiTcOpZGlhIiwgY29sb3IgPSAicmVkIikKYGBgCgo=