Objetivos da Aula

Ao final desta aula, o aluno será capaz de:

  1. Compreender a “gramática dos gráficos” implementada nos pacotes ggplot2 e highcharter.
  2. Criar diferentes tipos de visualizações estáticas para dados macroeconômicos, como gráficos de linha, dispersão e barras.
  3. Utilizar o pacote plotly para transformar gráficos estáticos do ggplot2 em visualizações interativas.
  4. Aplicar essas técnicas para explorar e comunicar insights de séries temporais econômicas.

1. Introdução e Preparação do Ambiente

Nesta aula, exploraremos o poder da visualização de dados para a análise macroeconômica. Utilizaremos o ggplot2, um dos pacotes mais populares e versáteis para criação de gráficos em R, baseado na “Gramática dos Gráficos”. Em seguida, elevaremos nossas análises a um novo patamar com o plotly, que nos permite criar gráficos interativos com apenas uma linha de código adicional.

1.1. Carregando os Pacotes Necessários

Primeiramente, vamos instalar (caso não tenha) e carregar os pacotes que utilizaremos. O ggplot2 para os gráficos estáticos, o plotly para a interatividade e o dplyr para manipulação de dados.

# Se os pacotes não estiverem instalados, execute os comandos abaixo:
# install.packages("ggplot2")
# install.packages("plotly")
# install.packages("dplyr")
# install.packages("tidyr")
# install.packages("lubridate")
# install.packages("highcharter")
# install.packages("quantmod")
# install.packages("scales")


# Carregar os pacotes na sessão
library(ggplot2)
library(plotly)
library(dplyr)
library(tidyr)
library(lubridate) 
library(highcharter) 
library(quantmod)
library(scales)

1.2. O Conjunto de Dados: economics

Para nossos exemplos, utilizaremos o conjunto de dados economics, que está convenientemente incluído no pacote ggplot2. Ele contém séries temporais mensais da economia dos EUA. Vamos inspecionar suas primeiras linhas e estrutura.

# Carregar o dataset 'economics'
data("economics")

# Visualizar as primeiras linhas do dataset
head(economics)
## # A tibble: 6 × 6
##   date         pce    pop psavert uempmed unemploy
##   <date>     <dbl>  <dbl>   <dbl>   <dbl>    <dbl>
## 1 1967-07-01  507. 198712    12.6     4.5     2944
## 2 1967-08-01  510. 198911    12.6     4.7     2945
## 3 1967-09-01  516. 199113    11.9     4.6     2958
## 4 1967-10-01  512. 199311    12.9     4.9     3143
## 5 1967-11-01  517. 199498    12.8     4.7     3066
## 6 1967-12-01  525. 199657    11.8     4.8     3018
# Visualizar a estrutura do dataset
str(economics)
## spc_tbl_ [574 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ date    : Date[1:574], format: "1967-07-01" "1967-08-01" ...
##  $ pce     : num [1:574] 507 510 516 512 517 ...
##  $ pop     : num [1:574] 198712 198911 199113 199311 199498 ...
##  $ psavert : num [1:574] 12.6 12.6 11.9 12.9 12.8 11.8 11.7 12.3 11.7 12.3 ...
##  $ uempmed : num [1:574] 4.5 4.7 4.6 4.9 4.7 4.8 5.1 4.5 4.1 4.6 ...
##  $ unemploy: num [1:574] 2944 2945 2958 3143 3066 ...

As variáveis principais são:

  • date: Data da observação.
  • pce: Despesas de consumo pessoal (em bilhões de dólares).
  • pop: População total (em milhares).
  • psavert: Taxa de poupança pessoal.
  • uempmed: Duração mediana do desemprego (em semanas).http://127.0.0.1:9989/graphics/6f9ca73b-260e-46ce-98fd-8d9ef407c9a9.png
  • unemploy: Número de desempregados (em milhares).

2. Tipos de Visualização com ggplot2

A sintaxe do ggplot2 é construída em camadas. A base é a função ggplot(), onde especificamos o data e o aes (estética), que mapeia variáveis aos eixos (x, y), cores, tamanhos, etc. Em seguida, adicionamos “geoms” (geom_...), que definem o tipo de gráfico.

2.1. Gráfico de Linha (Séries Temporais)

O gráfico de linha é fundamental para a macroeconomia, permitindo-nos visualizar a evolução de variáveis ao longo do tempo. Vamos plotar o número de desempregados (unemploy) ao longo dos anos.

Versão Estática com ggplot2

# Criar o gráfico de linha estático 
g_linha <- ggplot(data = economics, mapping = aes(x = date, y = unemploy)) +   
  geom_line(color = "darkblue") + # Adiciona a camada de linha 
  labs( title = "Número de Desempregados nos EUA (1967-2015)", 
        subtitle = "Dados mensais", 
        x = "Ano", 
        y = "Número de Desempregados (em milhares)", 
        caption = "Fonte: Pacote 'ggplot2' (dataset 'economics')" ) +
  theme_minimal() # Aplica um tema visual limpo 

# Exibir o gráfico estático 
print(g_linha)

Explicação do Código:

  • ggplot(data = economics, aes(x = date, y = unemploy)): Inicia o gráfico, define o dataset e mapeia a data para o eixo X e o número de desempregados para o eixo Y.
  • geom_line(): Adiciona a camada que desenha a linha conectando os pontos de dados.
  • labs(): Personaliza os rótulos do gráfico (título, eixos, etc.).
  • theme_minimal(): Aplica um tema estético para um visual mais limpo.

Versão Interativa com plotly

Agora, com uma única função, ggplotly(), transformamos nosso gráfico estático em uma ferramenta de exploração interativa.

# Converter o gráfico ggplot em um objeto plotly interativo
ggplotly(g_linha)

Análise Interativa: Passe o mouse sobre a linha para ver os valores exatos de cada ponto no tempo. Você também pode usar as ferramentas no canto superior direito para dar zoom em períodos específicos, o que é extremamente útil para analisar recessões ou períodos de expansão.


2.2. Gráfico de Dispersão (Relação entre Variáveis)

Gráficos de dispersão são excelentes para investigar a relação entre duas variáveis quantitativas. Vamos explorar a relação entre a população (pop) e as despesas de consumo pessoal (pce).

Versão Estática com ggplot2

g_dispersao <- ggplot(data = economics, aes(x = pop, y = pce)) +
  geom_point(
    aes(
      color = psavert,
      text = paste0(
        "<b>População:</b> ",
        number(pop, big.mark = ".", decimal.mark = ",", accuracy = 1), " mil<br>",
        "<b>PCE:</b> ",
        number(pce, big.mark = ".", decimal.mark = ",", accuracy = 0.01), " bi USD<br>",
        "<b>Taxa de poupança:</b> ",
        number(psavert, big.mark = ".", decimal.mark = ",", accuracy = 0.1), " %"
      )
    ),
    alpha = 0.6
  ) +
  geom_smooth(method = "lm", color = "red", se = FALSE) +
  scale_color_gradient(low = "#FEB712", high = "#02023C") +
  labs(
    title = "Relação entre População e Consumo nos EUA",
    x = "População (em milhares)",
    y = "Despesas de Consumo Pessoal (em bilhões de USD)",
    color = "Taxa de Poupança (%)",
    caption = "Fonte: Pacote 'ggplot2' (dataset 'economics')"
  ) +
  theme_bw()

# Exibir o gráfico estático
print(g_dispersao)

Explicação do Código:

  • geom_point(aes(color = psavert), alpha = 0.6): Adiciona a camada de pontos. Mapeamos a cor dos pontos à variável psavert (taxa de poupança) e aplicamos transparência (alpha) para melhor visualização de pontos sobrepostos.
  • geom_smooth(method = "lm", ...): Adiciona uma camada com uma linha de ajuste (regressão linear, lm). se = FALSE remove a banda de erro padrão.
  • scale_color_gradient(...): Controla o gradiente de cores usado para a taxa de poupança.

Versão Interativa com plotly

# Converter para um gráfico interativo
ggplotly(g_dispersao, tooltip = "text") 

Análise Interativa: Ao passar o mouse, você pode ver simultaneamente os valores da população, do consumo e da taxa de poupança para cada ponto de dados (mês). Isso enriquece a análise da relação entre as variáveis.


2.3. Gráfico de Barras (Comparações)

Gráficos de barras são úteis para comparar valores entre diferentes grupos ou categorias. Como nossos dados são séries temporais, podemos criar categorias, como “décadas”, para comparar uma métrica. Vamos comparar a duração mediana do desemprego por década.

Preparação dos Dados

Primeiro, precisamos criar uma variável decada em nosso dataframe.

# Usando dplyr para criar a variável 'década'
economics_decadas <- economics %>%
  mutate(decada = factor(floor(year(date) / 10) * 10)) %>% # Agrupa anos em décadas
  group_by(decada) %>% # Agrupa por década
  summarise(uempmed_media = mean(uempmed, na.rm = TRUE)) # Calcula a média da duração do desemprego

# Visualizar o resultado
head(economics_decadas)
## # A tibble: 6 × 2
##   decada uempmed_media
##   <fct>          <dbl>
## 1 1960            4.52
## 2 1970            6.29
## 3 1980            7.2 
## 4 1990            7.58
## 5 2000            9.26
## 6 2010           18.2

Versão Estática com ggplot2

# Criar o gráfico de barras estático
g_barras <- ggplot(data = economics_decadas, aes(x = decada, y = uempmed_media)) +
  geom_col(
    aes(
      fill = uempmed_media,
      text = paste0(
        "<b>Década:</b> ", decada,
        "<br><b>Duração mediana:</b> ",
        number(uempmed_media, accuracy = 0.1, decimal.mark = ","),
        " semanas"
      )
    ),
    show.legend = FALSE
  ) +
  scale_fill_gradient(low = "#FEB712", high = "#02023C") +
  labs(
    title = "Duração Mediana do Desemprego por Década nos EUA",
    x = "Década",
    y = "Duração Média (em semanas)",
    caption = "Fonte: Pacote 'ggplot2' (dataset 'economics')"
  ) +
  theme_light()

# Exibir o gráfico estático
print(g_barras)

Explicação do Código:

  • mutate() e group_by()/summarise(): Funções do dplyr para criar a variável decada e calcular a estatística agregada.
  • geom_col(): Cria as barras com alturas definidas pela variável uempmed_media.

Versão Interativa com plotly

# Converter para um gráfico interativo
ggplotly(g_barras, tooltip = "text")

Análise Interativa: A interatividade aqui permite que você passe o mouse sobre cada barra para confirmar o valor exato, o que pode ser útil em gráficos mais complexos ou com muitas categorias.

3. Preparação do Ambiente e Novos Dados

Agora, utilizaremos um novo conjunto de ferramentas e dados. O highcharter será nosso motor de visualização, e o quantmod nos permitirá buscar dados macroeconômicos atualizados diretamente do Federal Reserve Economic Data (FRED).

3.1. Obtendo Dados Macroeconômicos do FRED

Com o quantmod, podemos baixar séries de dados diretamente do FRED. Vamos buscar três indicadores chave da economia dos EUA:

  • GDP: Produto Interno Bruto (trimestral)
  • CPIAUCSL: Índice de Preços ao Consumidor (inflação, mensal)
  • UNRATE: Taxa de Desemprego (mensal)

O quantmod baixa os dados como objetos xts (eXtensible Time Series), que são ótimos para modelagem, mas vamos convertê-los para um data.frame para facilitar a manipulação com dplyr e o uso no highcharter.

# Definir os códigos das séries no FRED
fred_series <- c("GDP", "CPIAUCSL", "UNRATE")

# Baixar os dados a partir de 2000-01-01
getSymbols(fred_series, src = "FRED", from = "2000-01-01")
## [1] "GDP"      "CPIAUCSL" "UNRATE"
# Converter os objetos xts para um data.frame único e organizado
macro_data <- data.frame(
  date = index(UNRATE),
  # CORREÇÃO: Usar as.numeric() para remover nomes de colunas da matriz xts
  unemployment = as.numeric(coredata(UNRATE))
) %>%
  # Juntar os dados de inflação (também mensais)
  left_join(
    data.frame(date = index(CPIAUCSL), 
               # CORREÇÃO: Usar as.numeric()
               inflation_cpi = as.numeric(coredata(CPIAUCSL))),
    by = "date"
  ) %>%
  # Juntar os dados do PIB (trimestrais, o join vai repetir os valores)
  left_join(
    data.frame(date = index(GDP), 
               # CORREÇÃO: Usar as.numeric()
               gdp = as.numeric(coredata(GDP))),
    by = "date"
  ) %>%
  # A função fill agora encontrará a coluna 'gdp' corretamente
  fill(gdp, .direction = "down") %>%
  # Remover quaisquer linhas com NA restantes (geralmente no início)
  na.omit()

# Inspecionar o resultado
head(macro_data)
##         date unemployment inflation_cpi      gdp
## 1 2000-01-01          4.0         169.3 10002.18
## 2 2000-02-01          4.1         170.0 10002.18
## 3 2000-03-01          4.0         171.0 10002.18
## 4 2000-04-01          3.8         170.9 10247.72
## 5 2000-05-01          4.0         171.2 10247.72
## 6 2000-06-01          4.0         172.2 10247.72
tail(macro_data)
##           date unemployment inflation_cpi      gdp
## 303 2025-03-01          4.2       319.615 29962.05
## 304 2025-04-01          4.2       320.321 30353.90
## 305 2025-05-01          4.2       320.580 30353.90
## 306 2025-06-01          4.1       321.500 30353.90
## 307 2025-07-01          4.2       322.132 30353.90
## 308 2025-08-01          4.3       323.364 30353.90

4. O Gráfico de Séries Temporais: highchart(type = "stock")

Este é o tipo de gráfico mais poderoso do highcharter para análise de séries temporais. Ele vem com funcionalidades nativas como seletor de intervalo de datas, navegador de série, zoom e tooltips detalhados.

Vamos visualizar o PIB e a Taxa de Desemprego no mesmo gráfico. Como as escalas são muito diferentes (trilhões de dólares para o PIB e porcentagem para o desemprego), usaremos dois eixos Y.

highchart(type = "stock") %>%
  # Adiciona a série do PIB
  hc_add_series(
    data = macro_data,
    type = "line",
    hcaes(x = date, y = gdp),
    name = "PIB (US$ Bilhões)",
    yAxis = 0 # Define que esta série usará o primeiro eixo Y (índice 0)
  ) %>%
  # Adiciona a série da Taxa de Desemprego
  hc_add_series(
    data = macro_data,
    type = "line",
    hcaes(x = date, y = unemployment),
    name = "Desemprego (%)",
    yAxis = 1 # Define que esta série usará o segundo eixo Y (índice 1)
  ) %>%
  # Configuração dos múltiplos eixos Y
  hc_yAxis_multiples(
    # Configuração do primeiro eixo (PIB)
    list(
      title = list(text = "PIB (US$ Bilhões)"),
      opposite = FALSE # Posiciona o eixo à esquerda
    ),
    # Configuração do segundo eixo (Desemprego)
    list(
      title = list(text = "Taxa de Desemprego (%)"),
      opposite = TRUE # Posiciona o eixo à direita
    )
  ) %>%
  # Adiciona títulos e créditos
  hc_title(text = "Desempenho Macroeconômico dos EUA") %>%
  hc_subtitle(text = "PIB vs. Taxa de Desemprego") %>%
  hc_credits(enabled = TRUE, text = "Fonte: Federal Reserve Economic Data (FRED)") %>%
  # Configura o tooltip para mostrar informações de ambas as séries
  hc_tooltip(shared = TRUE, crosshairs = TRUE)

5. Outras Visualizações com highcharter

5.1. Gráfico de Dispersão (Curva de Phillips Simplificada)

Podemos investigar a relação entre inflação e desemprego (uma versão simplificada da Curva de Phillips). Primeiro, vamos calcular a variação anual da inflação.

# Calcular a variação anual do CPI (Inflação)
phillips_data <- macro_data %>%
  mutate(
    inflation_yoy = (inflation_cpi / lag(inflation_cpi, 12) - 1) * 100,
    ano = year(date)
  ) %>%
  filter(date >= "2001-01-01") %>% # Remove NAs do cálculo do lag
  na.omit()

Agora, criamos o gráfico de dispersão interativo.

hchart(
  phillips_data,
  "scatter", # Tipo de gráfico
  hcaes(x = unemployment, y = inflation_yoy, group = ano)
) %>%
  hc_title(text = "Relação entre Desemprego e Inflação nos EUA (2001-Presente)") %>%
  hc_xAxis(title = list(text = "Taxa de Desemprego (%)")) %>%
  hc_yAxis(title = list(text = "Inflação Anual (%)")) %>%
  hc_tooltip(
    pointFormat = "<b>Ano: {point.ano}</b><br/>Desemprego: {point.x:.2f}%<br/>Inflação: {point.y:.2f}%"
  )

6. Conclusão e Comparativo

Quando usar ggplot2 + plotly vs. highcharter?

ggplot2 + plotly: É a combinação ideal quando você precisa de total controle sobre a gramática dos gráficos para construir visualizações complexas e personalizadas em camadas. É a escolha padrão para publicações acadêmicas e análises exploratórias detalhadas. A conversão para plotly adiciona interatividade a essa base sólida.

highcharter: É a melhor escolha quando o objetivo é produzir rapidamente gráficos de “qualidade de dashboard”, elegantes e com funcionalidades interativas avançadas (como o type = "stock"). Sua sintaxe baseada em pipe é muito fluida, e é excelente para relatórios executivos, aplicações web (Shiny) e exploração rápida de séries temporais financeiras e econômicas.

Ambos os conjuntos de ferramentas são extremamente poderosos. A escolha dependerá do seu objetivo final e preferência de sintaxe.

LS0tDQp0aXRsZTogIkF1bGEgMjogVmlzdWFsaXphw6fDo28gZGUgRGFkb3MgY29tIGdncGxvdDIsIHBsb3RseSBlIGhpZ2hjaGFydGVyIg0KYXV0aG9yOiAiQnJ1bm8gTWVsbyBkZSBPbGl2ZWlyYSBTYW50b3MiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdGhlbWU6IHVuaXRlZA0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIENvbmZpZ3VyYcOnw6NvIGluaWNpYWwgZG8gYW1iaWVudGUgUk1hcmtkb3duDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQ0KYGBgDQoNCiMgT2JqZXRpdm9zIGRhIEF1bGENCg0KQW8gZmluYWwgZGVzdGEgYXVsYSwgbyBhbHVubyBzZXLDoSBjYXBheiBkZToNCg0KMS4gIENvbXByZWVuZGVyIGEgImdyYW3DoXRpY2EgZG9zIGdyw6FmaWNvcyIgaW1wbGVtZW50YWRhIG5vcyBwYWNvdGVzIGBnZ3Bsb3QyYCBlIGBoaWdoY2hhcnRlcmAuDQoyLiAgQ3JpYXIgZGlmZXJlbnRlcyB0aXBvcyBkZSB2aXN1YWxpemHDp8O1ZXMgZXN0w6F0aWNhcyBwYXJhIGRhZG9zIG1hY3JvZWNvbsO0bWljb3MsIGNvbW8gZ3LDoWZpY29zIGRlIGxpbmhhLCBkaXNwZXJzw6NvIGUgYmFycmFzLg0KMy4gIFV0aWxpemFyIG8gcGFjb3RlIGBwbG90bHlgIHBhcmEgdHJhbnNmb3JtYXIgZ3LDoWZpY29zIGVzdMOhdGljb3MgZG8gYGdncGxvdDJgIGVtIHZpc3VhbGl6YcOnw7VlcyBpbnRlcmF0aXZhcy4NCjQuICBBcGxpY2FyIGVzc2FzIHTDqWNuaWNhcyBwYXJhIGV4cGxvcmFyIGUgY29tdW5pY2FyIGluc2lnaHRzIGRlIHPDqXJpZXMgdGVtcG9yYWlzIGVjb27DtG1pY2FzLg0KDQotLS0tLQ0KDQojIDFcLiBJbnRyb2R1w6fDo28gZSBQcmVwYXJhw6fDo28gZG8gQW1iaWVudGUNCg0KTmVzdGEgYXVsYSwgZXhwbG9yYXJlbW9zIG8gcG9kZXIgZGEgdmlzdWFsaXphw6fDo28gZGUgZGFkb3MgcGFyYSBhIGFuw6FsaXNlIG1hY3JvZWNvbsO0bWljYS4gVXRpbGl6YXJlbW9zIG8gYGdncGxvdDJgLCB1bSBkb3MgcGFjb3RlcyBtYWlzIHBvcHVsYXJlcyBlIHZlcnPDoXRlaXMgcGFyYSBjcmlhw6fDo28gZGUgZ3LDoWZpY29zIGVtIFIsIGJhc2VhZG8gbmEgIkdyYW3DoXRpY2EgZG9zIEdyw6FmaWNvcyIuIEVtIHNlZ3VpZGEsIGVsZXZhcmVtb3Mgbm9zc2FzIGFuw6FsaXNlcyBhIHVtIG5vdm8gcGF0YW1hciBjb20gbyBgcGxvdGx5YCwgcXVlIG5vcyBwZXJtaXRlIGNyaWFyIGdyw6FmaWNvcyBpbnRlcmF0aXZvcyBjb20gYXBlbmFzIHVtYSBsaW5oYSBkZSBjw7NkaWdvIGFkaWNpb25hbC4NCg0KIyMgMS4xLiBDYXJyZWdhbmRvIG9zIFBhY290ZXMgTmVjZXNzw6FyaW9zDQoNClByaW1laXJhbWVudGUsIHZhbW9zIGluc3RhbGFyIChjYXNvIG7Do28gdGVuaGEpIGUgY2FycmVnYXIgb3MgcGFjb3RlcyBxdWUgdXRpbGl6YXJlbW9zLiBPIGBnZ3Bsb3QyYCBwYXJhIG9zIGdyw6FmaWNvcyBlc3TDoXRpY29zLCBvIGBwbG90bHlgIHBhcmEgYSBpbnRlcmF0aXZpZGFkZSBlIG8gYGRwbHlyYCBwYXJhIG1hbmlwdWxhw6fDo28gZGUgZGFkb3MuDQoNCmBgYHtyIGNhcnJlZ2FyX3BhY290ZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIFNlIG9zIHBhY290ZXMgbsOjbyBlc3RpdmVyZW0gaW5zdGFsYWRvcywgZXhlY3V0ZSBvcyBjb21hbmRvcyBhYmFpeG86DQojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQojIGluc3RhbGwucGFja2FnZXMoInRpZHlyIikNCiMgaW5zdGFsbC5wYWNrYWdlcygibHVicmlkYXRlIikNCiMgaW5zdGFsbC5wYWNrYWdlcygiaGlnaGNoYXJ0ZXIiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJxdWFudG1vZCIpDQojIGluc3RhbGwucGFja2FnZXMoInNjYWxlcyIpDQoNCg0KIyBDYXJyZWdhciBvcyBwYWNvdGVzIG5hIHNlc3PDo28NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkgDQpsaWJyYXJ5KGhpZ2hjaGFydGVyKSANCmxpYnJhcnkocXVhbnRtb2QpDQpsaWJyYXJ5KHNjYWxlcykNCmBgYA0KDQojIyAxLjIuIE8gQ29uanVudG8gZGUgRGFkb3M6IGBlY29ub21pY3NgDQoNClBhcmEgbm9zc29zIGV4ZW1wbG9zLCB1dGlsaXphcmVtb3MgbyBjb25qdW50byBkZSBkYWRvcyBgZWNvbm9taWNzYCwgcXVlIGVzdMOhIGNvbnZlbmllbnRlbWVudGUgaW5jbHXDrWRvIG5vIHBhY290ZSBgZ2dwbG90MmAuIEVsZSBjb250w6ltIHPDqXJpZXMgdGVtcG9yYWlzIG1lbnNhaXMgZGEgZWNvbm9taWEgZG9zIEVVQS4gVmFtb3MgaW5zcGVjaW9uYXIgc3VhcyBwcmltZWlyYXMgbGluaGFzIGUgZXN0cnV0dXJhLg0KDQpgYGB7ciBjYXJyZWdhcl9kYWRvc30NCiMgQ2FycmVnYXIgbyBkYXRhc2V0ICdlY29ub21pY3MnDQpkYXRhKCJlY29ub21pY3MiKQ0KDQojIFZpc3VhbGl6YXIgYXMgcHJpbWVpcmFzIGxpbmhhcyBkbyBkYXRhc2V0DQpoZWFkKGVjb25vbWljcykNCg0KIyBWaXN1YWxpemFyIGEgZXN0cnV0dXJhIGRvIGRhdGFzZXQNCnN0cihlY29ub21pY3MpDQpgYGANCg0KQXMgdmFyacOhdmVpcyBwcmluY2lwYWlzIHPDo286DQoNCiAgKiBgZGF0ZWA6IERhdGEgZGEgb2JzZXJ2YcOnw6NvLg0KICAqIGBwY2VgOiBEZXNwZXNhcyBkZSBjb25zdW1vIHBlc3NvYWwgKGVtIGJpbGjDtWVzIGRlIGTDs2xhcmVzKS4NCiAgKiBgcG9wYDogUG9wdWxhw6fDo28gdG90YWwgKGVtIG1pbGhhcmVzKS4NCiAgKiBgcHNhdmVydGA6IFRheGEgZGUgcG91cGFuw6dhIHBlc3NvYWwuDQogICogYHVlbXBtZWRgOiBEdXJhw6fDo28gbWVkaWFuYSBkbyBkZXNlbXByZWdvIChlbSBzZW1hbmFzKS5odHRwOi8vMTI3LjAuMC4xOjk5ODkvZ3JhcGhpY3MvNmY5Y2E3M2ItMjYwZS00NmNlLTk4ZmQtOGQ5ZWY0MDdjOWE5LnBuZw0KICAqIGB1bmVtcGxveWA6IE7Dum1lcm8gZGUgZGVzZW1wcmVnYWRvcyAoZW0gbWlsaGFyZXMpLg0KDQotLS0tLQ0KDQojIDJcLiBUaXBvcyBkZSBWaXN1YWxpemHDp8OjbyBjb20gYGdncGxvdDJgDQoNCkEgc2ludGF4ZSBkbyBgZ2dwbG90MmAgw6kgY29uc3RydcOtZGEgZW0gY2FtYWRhcy4gQSBiYXNlIMOpIGEgZnVuw6fDo28gYGdncGxvdCgpYCwgb25kZSBlc3BlY2lmaWNhbW9zIG8gYGRhdGFgIGUgbyBgYWVzYCAoZXN0w6l0aWNhKSwgcXVlIG1hcGVpYSB2YXJpw6F2ZWlzIGFvcyBlaXhvcyAoeCwgeSksIGNvcmVzLCB0YW1hbmhvcywgZXRjLiBFbSBzZWd1aWRhLCBhZGljaW9uYW1vcyAiZ2VvbXMiIChgZ2VvbV8uLi5gKSwgcXVlIGRlZmluZW0gbyB0aXBvIGRlIGdyw6FmaWNvLg0KDQojIyAyLjEuIEdyw6FmaWNvIGRlIExpbmhhIChTw6lyaWVzIFRlbXBvcmFpcykNCg0KTyBncsOhZmljbyBkZSBsaW5oYSDDqSBmdW5kYW1lbnRhbCBwYXJhIGEgbWFjcm9lY29ub21pYSwgcGVybWl0aW5kby1ub3MgdmlzdWFsaXphciBhIGV2b2x1w6fDo28gZGUgdmFyacOhdmVpcyBhbyBsb25nbyBkbyB0ZW1wby4gVmFtb3MgcGxvdGFyIG8gbsO6bWVybyBkZSBkZXNlbXByZWdhZG9zIChgdW5lbXBsb3lgKSBhbyBsb25nbyBkb3MgYW5vcy4NCg0KIyMjIFZlcnPDo28gRXN0w6F0aWNhIGNvbSBgZ2dwbG90MmANCg0KYGBge3IgZ3JhZmljb19saW5oYV9nZ3Bsb3R9DQojIENyaWFyIG8gZ3LDoWZpY28gZGUgbGluaGEgZXN0w6F0aWNvIA0KZ19saW5oYSA8LSBnZ3Bsb3QoZGF0YSA9IGVjb25vbWljcywgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZSwgeSA9IHVuZW1wbG95KSkgKyAgIA0KICBnZW9tX2xpbmUoY29sb3IgPSAiZGFya2JsdWUiKSArICMgQWRpY2lvbmEgYSBjYW1hZGEgZGUgbGluaGEgDQogIGxhYnMoIHRpdGxlID0gIk7Dum1lcm8gZGUgRGVzZW1wcmVnYWRvcyBub3MgRVVBICgxOTY3LTIwMTUpIiwgDQogICAgICAgIHN1YnRpdGxlID0gIkRhZG9zIG1lbnNhaXMiLCANCiAgICAgICAgeCA9ICJBbm8iLCANCiAgICAgICAgeSA9ICJOw7ptZXJvIGRlIERlc2VtcHJlZ2Fkb3MgKGVtIG1pbGhhcmVzKSIsIA0KICAgICAgICBjYXB0aW9uID0gIkZvbnRlOiBQYWNvdGUgJ2dncGxvdDInIChkYXRhc2V0ICdlY29ub21pY3MnKSIgKSArDQogIHRoZW1lX21pbmltYWwoKSAjIEFwbGljYSB1bSB0ZW1hIHZpc3VhbCBsaW1wbyANCg0KIyBFeGliaXIgbyBncsOhZmljbyBlc3TDoXRpY28gDQpwcmludChnX2xpbmhhKQ0KYGBgDQoNCioqRXhwbGljYcOnw6NvIGRvIEPDs2RpZ286KioNCg0KICAtIGBnZ3Bsb3QoZGF0YSA9IGVjb25vbWljcywgYWVzKHggPSBkYXRlLCB5ID0gdW5lbXBsb3kpKWA6IEluaWNpYSBvIGdyw6FmaWNvLCBkZWZpbmUgbyBkYXRhc2V0IGUgbWFwZWlhIGEgZGF0YSBwYXJhIG8gZWl4byBYIGUgbyBuw7ptZXJvIGRlIGRlc2VtcHJlZ2Fkb3MgcGFyYSBvIGVpeG8gWS4NCiAgLSBgZ2VvbV9saW5lKClgOiBBZGljaW9uYSBhIGNhbWFkYSBxdWUgZGVzZW5oYSBhIGxpbmhhIGNvbmVjdGFuZG8gb3MgcG9udG9zIGRlIGRhZG9zLg0KICAtIGBsYWJzKClgOiBQZXJzb25hbGl6YSBvcyByw7N0dWxvcyBkbyBncsOhZmljbyAodMOtdHVsbywgZWl4b3MsIGV0Yy4pLg0KICAtIGB0aGVtZV9taW5pbWFsKClgOiBBcGxpY2EgdW0gdGVtYSBlc3TDqXRpY28gcGFyYSB1bSB2aXN1YWwgbWFpcyBsaW1wby4NCg0KIyMjIFZlcnPDo28gSW50ZXJhdGl2YSBjb20gYHBsb3RseWANCg0KQWdvcmEsIGNvbSB1bWEgw7puaWNhIGZ1bsOnw6NvLCBgZ2dwbG90bHkoKWAsIHRyYW5zZm9ybWFtb3Mgbm9zc28gZ3LDoWZpY28gZXN0w6F0aWNvIGVtIHVtYSBmZXJyYW1lbnRhIGRlIGV4cGxvcmHDp8OjbyBpbnRlcmF0aXZhLg0KDQpgYGB7ciBncmFmaWNvX2xpbmhhX3Bsb3RseX0NCiMgQ29udmVydGVyIG8gZ3LDoWZpY28gZ2dwbG90IGVtIHVtIG9iamV0byBwbG90bHkgaW50ZXJhdGl2bw0KZ2dwbG90bHkoZ19saW5oYSkNCmBgYA0KDQoqKkFuw6FsaXNlIEludGVyYXRpdmE6KiogUGFzc2UgbyBtb3VzZSBzb2JyZSBhIGxpbmhhIHBhcmEgdmVyIG9zIHZhbG9yZXMgZXhhdG9zIGRlIGNhZGEgcG9udG8gbm8gdGVtcG8uIFZvY8OqIHRhbWLDqW0gcG9kZSB1c2FyIGFzIGZlcnJhbWVudGFzIG5vIGNhbnRvIHN1cGVyaW9yIGRpcmVpdG8gcGFyYSBkYXIgem9vbSBlbSBwZXLDrW9kb3MgZXNwZWPDrWZpY29zLCBvIHF1ZSDDqSBleHRyZW1hbWVudGUgw7p0aWwgcGFyYSBhbmFsaXNhciByZWNlc3PDtWVzIG91IHBlcsOtb2RvcyBkZSBleHBhbnPDo28uDQoNCi0tLS0tDQoNCiMjIDIuMi4gR3LDoWZpY28gZGUgRGlzcGVyc8OjbyAoUmVsYcOnw6NvIGVudHJlIFZhcmnDoXZlaXMpDQoNCkdyw6FmaWNvcyBkZSBkaXNwZXJzw6NvIHPDo28gZXhjZWxlbnRlcyBwYXJhIGludmVzdGlnYXIgYSByZWxhw6fDo28gZW50cmUgZHVhcyB2YXJpw6F2ZWlzIHF1YW50aXRhdGl2YXMuIFZhbW9zIGV4cGxvcmFyIGEgcmVsYcOnw6NvIGVudHJlIGEgcG9wdWxhw6fDo28gKGBwb3BgKSBlIGFzIGRlc3Blc2FzIGRlIGNvbnN1bW8gcGVzc29hbCAoYHBjZWApLg0KDQojIyMgVmVyc8OjbyBFc3TDoXRpY2EgY29tIGBnZ3Bsb3QyYA0KDQpgYGB7ciBncmFmaWNvX2Rpc3BlcnNhb19nZ3Bsb3R9DQpnX2Rpc3BlcnNhbyA8LSBnZ3Bsb3QoZGF0YSA9IGVjb25vbWljcywgYWVzKHggPSBwb3AsIHkgPSBwY2UpKSArDQogIGdlb21fcG9pbnQoDQogICAgYWVzKA0KICAgICAgY29sb3IgPSBwc2F2ZXJ0LA0KICAgICAgdGV4dCA9IHBhc3RlMCgNCiAgICAgICAgIjxiPlBvcHVsYcOnw6NvOjwvYj4gIiwNCiAgICAgICAgbnVtYmVyKHBvcCwgYmlnLm1hcmsgPSAiLiIsIGRlY2ltYWwubWFyayA9ICIsIiwgYWNjdXJhY3kgPSAxKSwgIiBtaWw8YnI+IiwNCiAgICAgICAgIjxiPlBDRTo8L2I+ICIsDQogICAgICAgIG51bWJlcihwY2UsIGJpZy5tYXJrID0gIi4iLCBkZWNpbWFsLm1hcmsgPSAiLCIsIGFjY3VyYWN5ID0gMC4wMSksICIgYmkgVVNEPGJyPiIsDQogICAgICAgICI8Yj5UYXhhIGRlIHBvdXBhbsOnYTo8L2I+ICIsDQogICAgICAgIG51bWJlcihwc2F2ZXJ0LCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiLCBhY2N1cmFjeSA9IDAuMSksICIgJSINCiAgICAgICkNCiAgICApLA0KICAgIGFscGhhID0gMC42DQogICkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiLCBzZSA9IEZBTFNFKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICIjRkVCNzEyIiwgaGlnaCA9ICIjMDIwMjNDIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlJlbGHDp8OjbyBlbnRyZSBQb3B1bGHDp8OjbyBlIENvbnN1bW8gbm9zIEVVQSIsDQogICAgeCA9ICJQb3B1bGHDp8OjbyAoZW0gbWlsaGFyZXMpIiwNCiAgICB5ID0gIkRlc3Blc2FzIGRlIENvbnN1bW8gUGVzc29hbCAoZW0gYmlsaMO1ZXMgZGUgVVNEKSIsDQogICAgY29sb3IgPSAiVGF4YSBkZSBQb3VwYW7Dp2EgKCUpIiwNCiAgICBjYXB0aW9uID0gIkZvbnRlOiBQYWNvdGUgJ2dncGxvdDInIChkYXRhc2V0ICdlY29ub21pY3MnKSINCiAgKSArDQogIHRoZW1lX2J3KCkNCg0KIyBFeGliaXIgbyBncsOhZmljbyBlc3TDoXRpY28NCnByaW50KGdfZGlzcGVyc2FvKQ0KYGBgDQoNCioqRXhwbGljYcOnw6NvIGRvIEPDs2RpZ286KioNCg0KICAtIGBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHBzYXZlcnQpLCBhbHBoYSA9IDAuNilgOiBBZGljaW9uYSBhIGNhbWFkYSBkZSBwb250b3MuIE1hcGVhbW9zIGEgY29yIGRvcyBwb250b3Mgw6AgdmFyacOhdmVsIGBwc2F2ZXJ0YCAodGF4YSBkZSBwb3VwYW7Dp2EpIGUgYXBsaWNhbW9zIHRyYW5zcGFyw6puY2lhIChgYWxwaGFgKSBwYXJhIG1lbGhvciB2aXN1YWxpemHDp8OjbyBkZSBwb250b3Mgc29icmVwb3N0b3MuDQogIC0gYGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIC4uLilgOiBBZGljaW9uYSB1bWEgY2FtYWRhIGNvbSB1bWEgbGluaGEgZGUgYWp1c3RlIChyZWdyZXNzw6NvIGxpbmVhciwgYGxtYCkuIGBzZSA9IEZBTFNFYCByZW1vdmUgYSBiYW5kYSBkZSBlcnJvIHBhZHLDo28uDQogIC0gYHNjYWxlX2NvbG9yX2dyYWRpZW50KC4uLilgOiBDb250cm9sYSBvIGdyYWRpZW50ZSBkZSBjb3JlcyB1c2FkbyBwYXJhIGEgdGF4YSBkZSBwb3VwYW7Dp2EuDQoNCiMjIyBWZXJzw6NvIEludGVyYXRpdmEgY29tIGBwbG90bHlgDQoNCmBgYHtyIGdyYWZpY29fZGlzcGVyc2FvX3Bsb3RseX0NCiMgQ29udmVydGVyIHBhcmEgdW0gZ3LDoWZpY28gaW50ZXJhdGl2bw0KZ2dwbG90bHkoZ19kaXNwZXJzYW8sIHRvb2x0aXAgPSAidGV4dCIpIA0KYGBgDQoNCioqQW7DoWxpc2UgSW50ZXJhdGl2YToqKiBBbyBwYXNzYXIgbyBtb3VzZSwgdm9jw6ogcG9kZSB2ZXIgc2ltdWx0YW5lYW1lbnRlIG9zIHZhbG9yZXMgZGEgcG9wdWxhw6fDo28sIGRvIGNvbnN1bW8gZSBkYSB0YXhhIGRlIHBvdXBhbsOnYSBwYXJhIGNhZGEgcG9udG8gZGUgZGFkb3MgKG3DqnMpLiBJc3NvIGVucmlxdWVjZSBhIGFuw6FsaXNlIGRhIHJlbGHDp8OjbyBlbnRyZSBhcyB2YXJpw6F2ZWlzLg0KDQotLS0tLQ0KDQojIyAyLjMuIEdyw6FmaWNvIGRlIEJhcnJhcyAoQ29tcGFyYcOnw7VlcykNCg0KR3LDoWZpY29zIGRlIGJhcnJhcyBzw6NvIMO6dGVpcyBwYXJhIGNvbXBhcmFyIHZhbG9yZXMgZW50cmUgZGlmZXJlbnRlcyBncnVwb3Mgb3UgY2F0ZWdvcmlhcy4gQ29tbyBub3Nzb3MgZGFkb3Mgc8OjbyBzw6lyaWVzIHRlbXBvcmFpcywgcG9kZW1vcyBjcmlhciBjYXRlZ29yaWFzLCBjb21vICJkw6ljYWRhcyIsIHBhcmEgY29tcGFyYXIgdW1hIG3DqXRyaWNhLiBWYW1vcyBjb21wYXJhciBhIGR1cmHDp8OjbyBtZWRpYW5hIGRvIGRlc2VtcHJlZ28gcG9yIGTDqWNhZGEuDQoNCiMjIyBQcmVwYXJhw6fDo28gZG9zIERhZG9zDQoNClByaW1laXJvLCBwcmVjaXNhbW9zIGNyaWFyIHVtYSB2YXJpw6F2ZWwgYGRlY2FkYWAgZW0gbm9zc28gZGF0YWZyYW1lLg0KDQpgYGB7ciBwcmVwYXJhY2FvX2JhcnJhc30NCiMgVXNhbmRvIGRwbHlyIHBhcmEgY3JpYXIgYSB2YXJpw6F2ZWwgJ2TDqWNhZGEnDQplY29ub21pY3NfZGVjYWRhcyA8LSBlY29ub21pY3MgJT4lDQogIG11dGF0ZShkZWNhZGEgPSBmYWN0b3IoZmxvb3IoeWVhcihkYXRlKSAvIDEwKSAqIDEwKSkgJT4lICMgQWdydXBhIGFub3MgZW0gZMOpY2FkYXMNCiAgZ3JvdXBfYnkoZGVjYWRhKSAlPiUgIyBBZ3J1cGEgcG9yIGTDqWNhZGENCiAgc3VtbWFyaXNlKHVlbXBtZWRfbWVkaWEgPSBtZWFuKHVlbXBtZWQsIG5hLnJtID0gVFJVRSkpICMgQ2FsY3VsYSBhIG3DqWRpYSBkYSBkdXJhw6fDo28gZG8gZGVzZW1wcmVnbw0KDQojIFZpc3VhbGl6YXIgbyByZXN1bHRhZG8NCmhlYWQoZWNvbm9taWNzX2RlY2FkYXMpDQpgYGANCg0KIyMjIFZlcnPDo28gRXN0w6F0aWNhIGNvbSBgZ2dwbG90MmANCg0KYGBge3IgZ3JhZmljb19iYXJyYXNfZ2dwbG90fQ0KIyBDcmlhciBvIGdyw6FmaWNvIGRlIGJhcnJhcyBlc3TDoXRpY28NCmdfYmFycmFzIDwtIGdncGxvdChkYXRhID0gZWNvbm9taWNzX2RlY2FkYXMsIGFlcyh4ID0gZGVjYWRhLCB5ID0gdWVtcG1lZF9tZWRpYSkpICsNCiAgZ2VvbV9jb2woDQogICAgYWVzKA0KICAgICAgZmlsbCA9IHVlbXBtZWRfbWVkaWEsDQogICAgICB0ZXh0ID0gcGFzdGUwKA0KICAgICAgICAiPGI+RMOpY2FkYTo8L2I+ICIsIGRlY2FkYSwNCiAgICAgICAgIjxicj48Yj5EdXJhw6fDo28gbWVkaWFuYTo8L2I+ICIsDQogICAgICAgIG51bWJlcih1ZW1wbWVkX21lZGlhLCBhY2N1cmFjeSA9IDAuMSwgZGVjaW1hbC5tYXJrID0gIiwiKSwNCiAgICAgICAgIiBzZW1hbmFzIg0KICAgICAgKQ0KICAgICksDQogICAgc2hvdy5sZWdlbmQgPSBGQUxTRQ0KICApICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiI0ZFQjcxMiIsIGhpZ2ggPSAiIzAyMDIzQyIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEdXJhw6fDo28gTWVkaWFuYSBkbyBEZXNlbXByZWdvIHBvciBEw6ljYWRhIG5vcyBFVUEiLA0KICAgIHggPSAiRMOpY2FkYSIsDQogICAgeSA9ICJEdXJhw6fDo28gTcOpZGlhIChlbSBzZW1hbmFzKSIsDQogICAgY2FwdGlvbiA9ICJGb250ZTogUGFjb3RlICdnZ3Bsb3QyJyAoZGF0YXNldCAnZWNvbm9taWNzJykiDQogICkgKw0KICB0aGVtZV9saWdodCgpDQoNCiMgRXhpYmlyIG8gZ3LDoWZpY28gZXN0w6F0aWNvDQpwcmludChnX2JhcnJhcykNCmBgYA0KDQoqKkV4cGxpY2HDp8OjbyBkbyBDw7NkaWdvOioqDQoNCiAgLSBgbXV0YXRlKClgIGUgYGdyb3VwX2J5KClgL2BzdW1tYXJpc2UoKWA6IEZ1bsOnw7VlcyBkbyBgZHBseXJgIHBhcmEgY3JpYXIgYSB2YXJpw6F2ZWwgYGRlY2FkYWAgZSBjYWxjdWxhciBhIGVzdGF0w61zdGljYSBhZ3JlZ2FkYS4NCiAgLSBgZ2VvbV9jb2woKWA6IENyaWEgYXMgYmFycmFzIGNvbSBhbHR1cmFzIGRlZmluaWRhcyBwZWxhIHZhcmnDoXZlbCBgdWVtcG1lZF9tZWRpYWAuDQoNCiMjIyBWZXJzw6NvIEludGVyYXRpdmEgY29tIGBwbG90bHlgDQoNCmBgYHtyIGdyYWZpY29fYmFycmFzX3Bsb3RseX0NCiMgQ29udmVydGVyIHBhcmEgdW0gZ3LDoWZpY28gaW50ZXJhdGl2bw0KZ2dwbG90bHkoZ19iYXJyYXMsIHRvb2x0aXAgPSAidGV4dCIpDQpgYGANCg0KKipBbsOhbGlzZSBJbnRlcmF0aXZhOioqIEEgaW50ZXJhdGl2aWRhZGUgYXF1aSBwZXJtaXRlIHF1ZSB2b2PDqiBwYXNzZSBvIG1vdXNlIHNvYnJlIGNhZGEgYmFycmEgcGFyYSBjb25maXJtYXIgbyB2YWxvciBleGF0bywgbyBxdWUgcG9kZSBzZXIgw7p0aWwgZW0gZ3LDoWZpY29zIG1haXMgY29tcGxleG9zIG91IGNvbSBtdWl0YXMgY2F0ZWdvcmlhcy4NCg0KDQojIDNcLiBQcmVwYXJhw6fDo28gZG8gQW1iaWVudGUgZSBOb3ZvcyBEYWRvcw0KDQpBZ29yYSwgdXRpbGl6YXJlbW9zIHVtIG5vdm8gY29uanVudG8gZGUgZmVycmFtZW50YXMgZSBkYWRvcy4gTyBgaGlnaGNoYXJ0ZXJgIHNlcsOhIG5vc3NvIG1vdG9yIGRlIHZpc3VhbGl6YcOnw6NvLCBlIG8gYHF1YW50bW9kYCBub3MgcGVybWl0aXLDoSBidXNjYXIgZGFkb3MgbWFjcm9lY29uw7RtaWNvcyBhdHVhbGl6YWRvcyBkaXJldGFtZW50ZSBkbyBGZWRlcmFsIFJlc2VydmUgRWNvbm9taWMgRGF0YSAoRlJFRCkuDQoNCiMjIDMuMS4gT2J0ZW5kbyBEYWRvcyBNYWNyb2Vjb27DtG1pY29zIGRvIEZSRUQNCg0KQ29tIG8gYHF1YW50bW9kYCwgcG9kZW1vcyBiYWl4YXIgc8OpcmllcyBkZSBkYWRvcyBkaXJldGFtZW50ZSBkbyBGUkVELiBWYW1vcyBidXNjYXIgdHLDqnMgaW5kaWNhZG9yZXMgY2hhdmUgZGEgZWNvbm9taWEgZG9zIEVVQToNCg0KICAqIGBHRFBgOiBQcm9kdXRvIEludGVybm8gQnJ1dG8gKHRyaW1lc3RyYWwpDQogICogYENQSUFVQ1NMYDogw41uZGljZSBkZSBQcmXDp29zIGFvIENvbnN1bWlkb3IgKGluZmxhw6fDo28sIG1lbnNhbCkNCiAgKiBgVU5SQVRFYDogVGF4YSBkZSBEZXNlbXByZWdvIChtZW5zYWwpDQoNCk8gYHF1YW50bW9kYCBiYWl4YSBvcyBkYWRvcyBjb21vIG9iamV0b3MgYHh0c2AgKGVYdGVuc2libGUgVGltZSBTZXJpZXMpLCBxdWUgc8OjbyDDs3RpbW9zIHBhcmEgbW9kZWxhZ2VtLCBtYXMgdmFtb3MgY29udmVydMOqLWxvcyBwYXJhIHVtIGBkYXRhLmZyYW1lYCBwYXJhIGZhY2lsaXRhciBhIG1hbmlwdWxhw6fDo28gY29tIGBkcGx5cmAgZSBvIHVzbyBubyBgaGlnaGNoYXJ0ZXJgLg0KDQpgYGB7ciBvYnRlcl9kYWRvc30NCiMgRGVmaW5pciBvcyBjw7NkaWdvcyBkYXMgc8OpcmllcyBubyBGUkVEDQpmcmVkX3NlcmllcyA8LSBjKCJHRFAiLCAiQ1BJQVVDU0wiLCAiVU5SQVRFIikNCg0KIyBCYWl4YXIgb3MgZGFkb3MgYSBwYXJ0aXIgZGUgMjAwMC0wMS0wMQ0KZ2V0U3ltYm9scyhmcmVkX3Nlcmllcywgc3JjID0gIkZSRUQiLCBmcm9tID0gIjIwMDAtMDEtMDEiKQ0KDQojIENvbnZlcnRlciBvcyBvYmpldG9zIHh0cyBwYXJhIHVtIGRhdGEuZnJhbWUgw7puaWNvIGUgb3JnYW5pemFkbw0KbWFjcm9fZGF0YSA8LSBkYXRhLmZyYW1lKA0KICBkYXRlID0gaW5kZXgoVU5SQVRFKSwNCiAgIyBDT1JSRcOHw4NPOiBVc2FyIGFzLm51bWVyaWMoKSBwYXJhIHJlbW92ZXIgbm9tZXMgZGUgY29sdW5hcyBkYSBtYXRyaXogeHRzDQogIHVuZW1wbG95bWVudCA9IGFzLm51bWVyaWMoY29yZWRhdGEoVU5SQVRFKSkNCikgJT4lDQogICMgSnVudGFyIG9zIGRhZG9zIGRlIGluZmxhw6fDo28gKHRhbWLDqW0gbWVuc2FpcykNCiAgbGVmdF9qb2luKA0KICAgIGRhdGEuZnJhbWUoZGF0ZSA9IGluZGV4KENQSUFVQ1NMKSwgDQogICAgICAgICAgICAgICAjIENPUlJFw4fDg086IFVzYXIgYXMubnVtZXJpYygpDQogICAgICAgICAgICAgICBpbmZsYXRpb25fY3BpID0gYXMubnVtZXJpYyhjb3JlZGF0YShDUElBVUNTTCkpKSwNCiAgICBieSA9ICJkYXRlIg0KICApICU+JQ0KICAjIEp1bnRhciBvcyBkYWRvcyBkbyBQSUIgKHRyaW1lc3RyYWlzLCBvIGpvaW4gdmFpIHJlcGV0aXIgb3MgdmFsb3JlcykNCiAgbGVmdF9qb2luKA0KICAgIGRhdGEuZnJhbWUoZGF0ZSA9IGluZGV4KEdEUCksIA0KICAgICAgICAgICAgICAgIyBDT1JSRcOHw4NPOiBVc2FyIGFzLm51bWVyaWMoKQ0KICAgICAgICAgICAgICAgZ2RwID0gYXMubnVtZXJpYyhjb3JlZGF0YShHRFApKSksDQogICAgYnkgPSAiZGF0ZSINCiAgKSAlPiUNCiAgIyBBIGZ1bsOnw6NvIGZpbGwgYWdvcmEgZW5jb250cmFyw6EgYSBjb2x1bmEgJ2dkcCcgY29ycmV0YW1lbnRlDQogIGZpbGwoZ2RwLCAuZGlyZWN0aW9uID0gImRvd24iKSAlPiUNCiAgIyBSZW1vdmVyIHF1YWlzcXVlciBsaW5oYXMgY29tIE5BIHJlc3RhbnRlcyAoZ2VyYWxtZW50ZSBubyBpbsOtY2lvKQ0KICBuYS5vbWl0KCkNCg0KIyBJbnNwZWNpb25hciBvIHJlc3VsdGFkbw0KaGVhZChtYWNyb19kYXRhKQ0KdGFpbChtYWNyb19kYXRhKQ0KYGBgDQoNCi0tLS0tDQoNCiMgNFwuIE8gR3LDoWZpY28gZGUgU8OpcmllcyBUZW1wb3JhaXM6IGBoaWdoY2hhcnQodHlwZSA9ICJzdG9jayIpYA0KDQpFc3RlIMOpIG8gdGlwbyBkZSBncsOhZmljbyBtYWlzIHBvZGVyb3NvIGRvIGBoaWdoY2hhcnRlcmAgcGFyYSBhbsOhbGlzZSBkZSBzw6lyaWVzIHRlbXBvcmFpcy4gRWxlIHZlbSBjb20gZnVuY2lvbmFsaWRhZGVzIG5hdGl2YXMgY29tbyBzZWxldG9yIGRlIGludGVydmFsbyBkZSBkYXRhcywgbmF2ZWdhZG9yIGRlIHPDqXJpZSwgem9vbSBlIHRvb2x0aXBzIGRldGFsaGFkb3MuDQoNClZhbW9zIHZpc3VhbGl6YXIgbyBQSUIgZSBhIFRheGEgZGUgRGVzZW1wcmVnbyBubyBtZXNtbyBncsOhZmljby4gQ29tbyBhcyBlc2NhbGFzIHPDo28gbXVpdG8gZGlmZXJlbnRlcyAodHJpbGjDtWVzIGRlIGTDs2xhcmVzIHBhcmEgbyBQSUIgZSBwb3JjZW50YWdlbSBwYXJhIG8gZGVzZW1wcmVnbyksIHVzYXJlbW9zICoqZG9pcyBlaXhvcyBZKiouDQoNCmBgYHtyIGdyYWZpY29fc3RvY2t9DQpoaWdoY2hhcnQodHlwZSA9ICJzdG9jayIpICU+JQ0KICAjIEFkaWNpb25hIGEgc8OpcmllIGRvIFBJQg0KICBoY19hZGRfc2VyaWVzKA0KICAgIGRhdGEgPSBtYWNyb19kYXRhLA0KICAgIHR5cGUgPSAibGluZSIsDQogICAgaGNhZXMoeCA9IGRhdGUsIHkgPSBnZHApLA0KICAgIG5hbWUgPSAiUElCIChVUyQgQmlsaMO1ZXMpIiwNCiAgICB5QXhpcyA9IDAgIyBEZWZpbmUgcXVlIGVzdGEgc8OpcmllIHVzYXLDoSBvIHByaW1laXJvIGVpeG8gWSAow61uZGljZSAwKQ0KICApICU+JQ0KICAjIEFkaWNpb25hIGEgc8OpcmllIGRhIFRheGEgZGUgRGVzZW1wcmVnbw0KICBoY19hZGRfc2VyaWVzKA0KICAgIGRhdGEgPSBtYWNyb19kYXRhLA0KICAgIHR5cGUgPSAibGluZSIsDQogICAgaGNhZXMoeCA9IGRhdGUsIHkgPSB1bmVtcGxveW1lbnQpLA0KICAgIG5hbWUgPSAiRGVzZW1wcmVnbyAoJSkiLA0KICAgIHlBeGlzID0gMSAjIERlZmluZSBxdWUgZXN0YSBzw6lyaWUgdXNhcsOhIG8gc2VndW5kbyBlaXhvIFkgKMOtbmRpY2UgMSkNCiAgKSAlPiUNCiAgIyBDb25maWd1cmHDp8OjbyBkb3MgbcO6bHRpcGxvcyBlaXhvcyBZDQogIGhjX3lBeGlzX211bHRpcGxlcygNCiAgICAjIENvbmZpZ3VyYcOnw6NvIGRvIHByaW1laXJvIGVpeG8gKFBJQikNCiAgICBsaXN0KA0KICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiUElCIChVUyQgQmlsaMO1ZXMpIiksDQogICAgICBvcHBvc2l0ZSA9IEZBTFNFICMgUG9zaWNpb25hIG8gZWl4byDDoCBlc3F1ZXJkYQ0KICAgICksDQogICAgIyBDb25maWd1cmHDp8OjbyBkbyBzZWd1bmRvIGVpeG8gKERlc2VtcHJlZ28pDQogICAgbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIlRheGEgZGUgRGVzZW1wcmVnbyAoJSkiKSwNCiAgICAgIG9wcG9zaXRlID0gVFJVRSAjIFBvc2ljaW9uYSBvIGVpeG8gw6AgZGlyZWl0YQ0KICAgICkNCiAgKSAlPiUNCiAgIyBBZGljaW9uYSB0w610dWxvcyBlIGNyw6lkaXRvcw0KICBoY190aXRsZSh0ZXh0ID0gIkRlc2VtcGVuaG8gTWFjcm9lY29uw7RtaWNvIGRvcyBFVUEiKSAlPiUNCiAgaGNfc3VidGl0bGUodGV4dCA9ICJQSUIgdnMuIFRheGEgZGUgRGVzZW1wcmVnbyIpICU+JQ0KICBoY19jcmVkaXRzKGVuYWJsZWQgPSBUUlVFLCB0ZXh0ID0gIkZvbnRlOiBGZWRlcmFsIFJlc2VydmUgRWNvbm9taWMgRGF0YSAoRlJFRCkiKSAlPiUNCiAgIyBDb25maWd1cmEgbyB0b29sdGlwIHBhcmEgbW9zdHJhciBpbmZvcm1hw6fDtWVzIGRlIGFtYmFzIGFzIHPDqXJpZXMNCiAgaGNfdG9vbHRpcChzaGFyZWQgPSBUUlVFLCBjcm9zc2hhaXJzID0gVFJVRSkNCmBgYA0KDQotLS0tLQ0KDQojIDVcLiBPdXRyYXMgVmlzdWFsaXphw6fDtWVzIGNvbSBgaGlnaGNoYXJ0ZXJgDQoNCiMjIDUuMS4gR3LDoWZpY28gZGUgRGlzcGVyc8OjbyAoQ3VydmEgZGUgUGhpbGxpcHMgU2ltcGxpZmljYWRhKQ0KDQpQb2RlbW9zIGludmVzdGlnYXIgYSByZWxhw6fDo28gZW50cmUgaW5mbGHDp8OjbyBlIGRlc2VtcHJlZ28gKHVtYSB2ZXJzw6NvIHNpbXBsaWZpY2FkYSBkYSBDdXJ2YSBkZSBQaGlsbGlwcykuIFByaW1laXJvLCB2YW1vcyBjYWxjdWxhciBhIHZhcmlhw6fDo28gYW51YWwgZGEgaW5mbGHDp8Ojby4NCg0KYGBge3IgZGFkb3NfcGhpbGxpcHN9DQojIENhbGN1bGFyIGEgdmFyaWHDp8OjbyBhbnVhbCBkbyBDUEkgKEluZmxhw6fDo28pDQpwaGlsbGlwc19kYXRhIDwtIG1hY3JvX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBpbmZsYXRpb25feW95ID0gKGluZmxhdGlvbl9jcGkgLyBsYWcoaW5mbGF0aW9uX2NwaSwgMTIpIC0gMSkgKiAxMDAsDQogICAgYW5vID0geWVhcihkYXRlKQ0KICApICU+JQ0KICBmaWx0ZXIoZGF0ZSA+PSAiMjAwMS0wMS0wMSIpICU+JSAjIFJlbW92ZSBOQXMgZG8gY8OhbGN1bG8gZG8gbGFnDQogIG5hLm9taXQoKQ0KYGBgDQoNCkFnb3JhLCBjcmlhbW9zIG8gZ3LDoWZpY28gZGUgZGlzcGVyc8OjbyBpbnRlcmF0aXZvLg0KDQpgYGB7ciBncmFmaWNvX2Rpc3BlcnNhb19oY30NCmhjaGFydCgNCiAgcGhpbGxpcHNfZGF0YSwNCiAgInNjYXR0ZXIiLCAjIFRpcG8gZGUgZ3LDoWZpY28NCiAgaGNhZXMoeCA9IHVuZW1wbG95bWVudCwgeSA9IGluZmxhdGlvbl95b3ksIGdyb3VwID0gYW5vKQ0KKSAlPiUNCiAgaGNfdGl0bGUodGV4dCA9ICJSZWxhw6fDo28gZW50cmUgRGVzZW1wcmVnbyBlIEluZmxhw6fDo28gbm9zIEVVQSAoMjAwMS1QcmVzZW50ZSkiKSAlPiUNCiAgaGNfeEF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiVGF4YSBkZSBEZXNlbXByZWdvICglKSIpKSAlPiUNCiAgaGNfeUF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiSW5mbGHDp8OjbyBBbnVhbCAoJSkiKSkgJT4lDQogIGhjX3Rvb2x0aXAoDQogICAgcG9pbnRGb3JtYXQgPSAiPGI+QW5vOiB7cG9pbnQuYW5vfTwvYj48YnIvPkRlc2VtcHJlZ286IHtwb2ludC54Oi4yZn0lPGJyLz5JbmZsYcOnw6NvOiB7cG9pbnQueTouMmZ9JSINCiAgKQ0KYGBgDQoNCi0tLS0tDQoNCiMgNlwuIENvbmNsdXPDo28gZSBDb21wYXJhdGl2bw0KDQpRdWFuZG8gdXNhciBgZ2dwbG90MmAgKyBgcGxvdGx5YCB2cy4gYGhpZ2hjaGFydGVyYD8NCg0KYGdncGxvdDJgICsgYHBsb3RseWA6IMOJIGEgY29tYmluYcOnw6NvIGlkZWFsIHF1YW5kbyB2b2PDqiBwcmVjaXNhIGRlIHRvdGFsIGNvbnRyb2xlIHNvYnJlIGEgZ3JhbcOhdGljYSBkb3MgZ3LDoWZpY29zIHBhcmEgY29uc3RydWlyIHZpc3VhbGl6YcOnw7VlcyBjb21wbGV4YXMgZSBwZXJzb25hbGl6YWRhcyBlbSBjYW1hZGFzLiDDiSBhIGVzY29saGEgcGFkcsOjbyBwYXJhIHB1YmxpY2HDp8O1ZXMgYWNhZMOqbWljYXMgZSBhbsOhbGlzZXMgZXhwbG9yYXTDs3JpYXMgZGV0YWxoYWRhcy4gQSBjb252ZXJzw6NvIHBhcmEgYHBsb3RseWAgYWRpY2lvbmEgaW50ZXJhdGl2aWRhZGUgYSBlc3NhIGJhc2Ugc8OzbGlkYS4NCg0KYGhpZ2hjaGFydGVyYDogw4kgYSBtZWxob3IgZXNjb2xoYSBxdWFuZG8gbyBvYmpldGl2byDDqSBwcm9kdXppciByYXBpZGFtZW50ZSBncsOhZmljb3MgZGUgInF1YWxpZGFkZSBkZSBkYXNoYm9hcmQiLCBlbGVnYW50ZXMgZSBjb20gZnVuY2lvbmFsaWRhZGVzIGludGVyYXRpdmFzIGF2YW7Dp2FkYXMgKGNvbW8gbyBgdHlwZSA9ICJzdG9jayJgKS4gU3VhIHNpbnRheGUgYmFzZWFkYSBlbSBwaXBlIMOpIG11aXRvIGZsdWlkYSwgZSDDqSBleGNlbGVudGUgcGFyYSByZWxhdMOzcmlvcyBleGVjdXRpdm9zLCBhcGxpY2HDp8O1ZXMgd2ViIChTaGlueSkgZSBleHBsb3Jhw6fDo28gcsOhcGlkYSBkZSBzw6lyaWVzIHRlbXBvcmFpcyBmaW5hbmNlaXJhcyBlIGVjb27DtG1pY2FzLg0KDQpBbWJvcyBvcyBjb25qdW50b3MgZGUgZmVycmFtZW50YXMgc8OjbyBleHRyZW1hbWVudGUgcG9kZXJvc29zLiBBIGVzY29saGEgZGVwZW5kZXLDoSBkbyBzZXUgb2JqZXRpdm8gZmluYWwgZSBwcmVmZXLDqm5jaWEgZGUgc2ludGF4ZS4=