Introdução ao RMarkdown

O RMarkdown é uma ferramenta que transforma suas análises de dados em documentos de alta qualidade. Pense nele como um caderno de laboratório digital. Em vez de escrever seu código em um lugar, seus gráficos em outro e seu texto explicativo em um terceiro, o RMarkdown permite que você faça tudo isso em um único arquivo.

A filosofia é a da pesquisa reprodutível. Se um colega (ou você mesmo, daqui a seis meses) quiser entender ou replicar sua análise, basta ter este único arquivo .Rmd. Ao executá-lo (“tricotar”, ou knit), o resultado será exatamente o mesmo, garantindo transparência e consistência.

O destino final pode ser um relatório em PDF, uma apresentação de slides, um site ou, como focaremos hoje, uma página web interativa via RPubs.

Anatomia de um Arquivo .Rmd

Um arquivo RMarkdown (.Rmd) tem três componentes principais:

Vamos ver um exemplo de um chunk básico que carrega as bibliotecas.

# Este é um chunk especial de 'setup'.
# A opção 'include=FALSE' significa que este código será executado, 
# mas nem o código nem seu resultado aparecerão no documento final.
# Ela não foi aqui utilizada para que possam vizualizar as bibliotecas.
# É ótimo para carregar pacotes e definir opções globais.
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(tidyverse)
library(plotly)
library(lubridate)
library(leaflet)
library(highcharter)
library(DT)
library(wooldridge)

Cabeçalho YAML utilizado nesse arquivo


title: “Criando e Publicando Análises com RMarkdown no RPubs”
author: “Bruno Melo de Oliveira Santos”
date: “2025-08-18”
output:
html_document:
toc: false
toc_float: false
theme: lumen
highlight: tango

Como Formatar o Texto no RMarkdown

A sintaxe Markdown é projetada para ser fácil de ler e escrever. Aqui estão os elementos de formatação mais comuns que você usará para construir a narrativa do seu documento.

Cabeçalhos de Seção

Para criar hierarquia no seu documento, use o #.

Um #:

Título Principal (Nível 1)

Dois #:

Subtítulo (Nível 2)

Três #:

Um Nível Mais Abaixo (Nível 3)

Ênfase no Texto

Você pode deixar o texto em itálico adicionando um asterísco (*) de cada lado, ou em negrito adicionando dois asteríscos de cada lado (**).

Este texto ficará em itálico.

Este texto ficará em negrito.

Listas

Listas Não Ordenadas:

Use *, + ou -, endentando para sub-itens.

  • Primeiro item
  • Segundo item
    • Um sub-item aninhado
  • Terceiro item

Listas Ordenadas:

Use números seguidos de um ponto.

  1. Primeiro passo
  2. Segundo passo
  3. Terceiro passo

Como Fazer Gráficos Interativos Básicos com Plotly

Análise de Dados: Salários e Educação

Para nossos exemplos, usaremos o conjunto de dados wage1 do pacote wooldridge. Este dataset contém informações sobre salários e outras variáveis para 526 trabalhadores.

Vamos carregar os dados e dar uma olhada em sua estrutura.

# Carrega o dataset "wage1" para o nosso ambiente
data("wage1")

# Visualiza as primeiras linhas e a estrutura dos dados
head(wage1)
##   wage educ exper tenure nonwhite female married numdep smsa northcen south
## 1 3.10   11     2      0        0      1       0      2    1        0     0
## 2 3.24   12    22      2        0      1       1      3    1        0     0
## 3 3.00   11     2      0        0      0       0      2    0        0     0
## 4 6.00    8    44     28        0      0       1      0    1        0     0
## 5 5.30   12     7      2        0      0       1      1    0        0     0
## 6 8.75   16     9      8        0      0       1      0    1        0     0
##   west construc ndurman trcommpu trade services profserv profocc clerocc
## 1    1        0       0        0     0        0        0       0       0
## 2    1        0       0        0     0        1        0       0       0
## 3    1        0       0        0     1        0        0       0       0
## 4    1        0       0        0     0        0        0       0       1
## 5    1        0       0        0     0        0        0       0       0
## 6    1        0       0        0     0        0        1       1       0
##   servocc    lwage expersq tenursq
## 1       0 1.131402       4       0
## 2       1 1.175573     484       4
## 3       0 1.098612       4       0
## 4       0 1.791759    1936     784
## 5       0 1.667707      49       4
## 6       0 2.169054      81      64
glimpse(wage1)
## Rows: 526
## Columns: 24
## $ wage     <dbl> 3.10, 3.24, 3.00, 6.00, 5.30, 8.75, 11.25, 5.00, 3.60, 18.18,…
## $ educ     <int> 11, 12, 11, 8, 12, 16, 18, 12, 12, 17, 16, 13, 12, 12, 12, 16…
## $ exper    <int> 2, 22, 2, 44, 7, 9, 15, 5, 26, 22, 8, 3, 15, 18, 31, 14, 10, …
## $ tenure   <int> 0, 2, 0, 28, 2, 8, 7, 3, 4, 21, 2, 0, 0, 3, 15, 0, 0, 10, 0, …
## $ nonwhite <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ female   <int> 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1…
## $ married  <int> 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0…
## $ numdep   <int> 2, 3, 2, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, 0, 1, 1, 0, 0, 3, 0, 0…
## $ smsa     <int> 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ northcen <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ south    <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ west     <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ construc <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ ndurman  <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ trcommpu <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ trade    <int> 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ services <int> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ profserv <int> 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1…
## $ profocc  <int> 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1…
## $ clerocc  <int> 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0…
## $ servocc  <int> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0…
## $ lwage    <dbl> 1.1314021, 1.1755733, 1.0986123, 1.7917595, 1.6677068, 2.1690…
## $ expersq  <int> 4, 484, 4, 1936, 49, 81, 225, 25, 676, 484, 64, 9, 225, 324, …
## $ tenursq  <int> 0, 4, 0, 784, 4, 64, 49, 9, 16, 441, 4, 0, 0, 9, 225, 0, 0, 1…

O dataset contém variáveis interessantes como:

  • wage: salário por hora em dólares.

  • educ: anos de educação.

  • exper: anos de experiência no mercado de trabalho.

  • female: variável binária (1 se for mulher, 0 se for homem).

  • married: variável binária (1 se for casado(a), 0 se não).

A maneira mais fácil e poderosa de criar gráficos interativos a partir de uma sintaxe que muitos já conhecem é usar a função ggplotly() do pacote plotly. Ela transforma um gráfico estático criado com ggplot2 em uma visualização interativa em HTML.

Gráfico de Dispersão Interativo (Scatter Plot)

Vamos investigar a relação entre anos de educação (educ) e o salário por hora (wage). Para deixar a visualização mais rica, vamos colorir os pontos de acordo com o gênero (female).

Interatividade: Passe o mouse sobre um ponto para ver os valores exatos de educação, salário e gênero. Você também pode usar as ferramentas no canto superior direito do gráfico para dar zoom, mover a área de visualização e salvar o gráfico como uma imagem.

# Criamos o objeto ggplot primeiro
p_dispersao <- ggplot(wage1, aes(x = educ, y = wage,
                                # A transformação ocorre aqui, apenas para o gráfico:
                                color = factor(female,
                                               levels = c(0, 1),
                                               labels = c("Homem", "Mulher")),
                                # A estética 'text' personaliza o que aparece no tooltip
                                text = paste("Educação:", educ, "anos<br>",
                                             "Salário/h: $", round(wage, 2)))) +
  geom_point(alpha = 0.7) +
  # Mapeamos as cores para as novas etiquetas ("Homem", "Mulher")
  scale_color_manual(name = "Gênero", values = c("Homem" = "#02023C", "Mulher" = "#FEB712")) +
  labs(title = "Relação entre Educação e Salário por Gênero",
       x = "Anos de Educação",
       y = "Salário por Hora (USD)") +
  theme_minimal()

# Agora, transformamos o objeto ggplot em um gráfico interativo
ggplotly(p_dispersao, tooltip = "text")

Explicação do código:

1. Criação do Gráfico Estático com ggplot2

O primeiro bloco de código cria um objeto de gráfico chamado p_dispersao.

  • p_dispersao <-: Atribui o resultado do código à direita a uma variável chamada p_dispersao.

  • ggplot(data = wage1, ...): Esta é a função principal que inicializa o gráfico.

    • data = wage1: Especifica que o conjunto de dados a ser usado é o wage1.

    • aes(x = educ, y = wage, ...): A função aes() (de aesthetics ou “estética”) mapeia as variáveis do conjunto de dados a propriedades visuais do gráfico.

      • x = educ: Mapeia a variável educ (educação) para o eixo X.

      • y = wage: Mapeia a variável wage (salário) para o eixo Y.

      • color = factor(...): Define que a cor dos pontos será determinada pela variável female.

        • factor(female, levels = c(0, 1), labels = c("Homem", "Mulher")): Esta parte é crucial. Ela transforma a variável female (que provavelmente contém valores numéricos como 0 e 1) em uma variável categórica (um “fator” em R). O valor 0 é rotulado como "Homem" e o 1 como "Mulher". Essa transformação é feita apenas para a visualização no gráfico.
      • text = paste(...): Cria um texto personalizado para cada ponto de dados. Esse texto será usado mais tarde pela função ggplotly para criar as caixas de informação (tooltips) interativas.

        • paste("Educação:", educ, "anos<br>", "Salário/h: $", round(wage, 2)): Concatena (junta) vários textos e valores para formar uma única string. Por exemplo: “Educação: 16 anos<br>Salário/h: $ 25.50”.

        • <br>: É uma tag HTML para quebra de linha, que fará o texto aparecer em duas linhas na caixa de informação.

        • round(wage, 2): Arredonda o valor do salário para duas casas decimais.

  • + geom_point(alpha = 0.7): Adiciona uma camada de pontos ao gráfico, criando o gráfico de dispersão.

    • alpha = 0.7: Define a transparência dos pontos em 70%. Isso é útil para visualizar áreas com alta concentração de pontos sobrepostos.
  • + scale_color_manual(...): Permite personalizar manualmente as cores usadas no mapeamento de color.

    • name = "Gênero": Define o título da legenda das cores como “Gênero”.

    • values = c("Homem" = "#02023C", "Mulher" = "#FEB712"): Atribui cores específicas (em código hexadecimal) para cada um dos rótulos que criamos. A cor azul escuro (#02023C) será usada para “Homem” e a cor amarela (#FEB712) para “Mulher”.

  • + labs(...): Adiciona ou modifica os rótulos do gráfico.

    • title = "...": Define o título principal do gráfico.

    • x = "...": Define o rótulo do eixo X.

    • y = "...": Define o rótulo do eixo Y.

  • + theme_minimal(): Aplica um tema visual “limpo” ao gráfico, com fundo branco e linhas de grade suaves, sem muitos elementos visuais de fundo.

2. Transformação em Gráfico Interativo com plotly

O segundo bloco de código pega o gráfico estático criado anteriormente e o torna interativo.

  • ggplotly(p_dispersao, tooltip = "text"):

    • ggplotly(): É a função da biblioteca plotly que converte um objeto ggplot em um gráfico interativo (HTML).

    • p_dispersao: O primeiro argumento é o objeto do gráfico ggplot que queremos converter.

    • tooltip = "text": Este é o argumento chave. Ele diz à função ggplotly para usar o mapeamento estético text que definimos dentro de aes() como o conteúdo da caixa de informação que aparece quando o usuário passa o mouse sobre um ponto no gráfico.

Gráfico de Barras Interativo (Bar Plot)

Qual é o salário médio por gênero? Um gráfico de barras é ideal para responder a essa pergunta. Primeiro, precisamos preparar os dados, calculando a média de salário para homens e mulheres.

Interatividade: Passe o mouse sobre as barras para ver o salário médio exato para cada grupo.

# 1. Preparar os dados: agrupar por gênero e calcular o salário médio
salario_medio_genero <- wage1 %>%
  mutate(genero_label = factor(female, labels = c("Homem", "Mulher"))) %>%
  group_by(genero_label) %>%
  summarise(salario_medio = mean(wage))

# 2. Criar o gráfico de barras com ggplot
p_barras <- ggplot(salario_medio_genero, aes(x = genero_label, y = salario_medio, fill = genero_label,
                                             text = paste("Salário Médio: $", round(salario_medio, 2)))) +
  geom_col() + # geom_col é usado quando a altura da barra já é um valor no dataset
  scale_fill_manual(values = c("Homem" = "#02023C", "Mulher" = "#FEB712")) +
  labs(title = "Salário Médio por Hora por Gênero",
       x = "Gênero",
       y = "Salário Médio por Hora (USD)") +
  theme_minimal() +
  theme(legend.position = "none") # Remove a legenda, pois a informação já está no eixo x

# 3. Transformar em interativo
ggplotly(p_barras, tooltip = "text")

Explicação do código:

1. Preparação e Sumarização dos Dados com dplyr

Esta seção usa o operador “pipe” (%>%) da biblioteca dplyr para criar um novo dataframe resumido chamado salario_medio_genero. O pipe pega o resultado do comando à sua esquerda e o usa como o primeiro argumento do comando à sua direita, tornando o código mais legível.

  • salario_medio_genero <- wage1 %>% ...: Inicia a sequência de operações com o dataframe wage1 e atribui o resultado final à variável salario_medio_genero.

  • mutate(genero_label = ...): A função mutate cria uma nova coluna no dataframe.

    • genero_label = factor(female, labels = c("Homem", "Mulher")): Cria uma coluna chamada genero_label. Assim como no exemplo anterior, ela converte a variável numérica female (com valores 0 e 1) em uma variável categórica com os rótulos “Homem” e “Mulher”.
  • group_by(genero_label): Agrupa o dataframe pela nova coluna genero_label. A partir deste ponto, qualquer operação subsequente será realizada separadamente para cada grupo (para todos os “Homem” e para todas as “Mulher”).

  • summarise(salario_medio = mean(wage)): A função summarise (ou summarize) colapsa os dados agrupados em uma única linha por grupo, criando colunas de resumo.

    • salario_medio = mean(wage): Cria uma nova coluna chamada salario_medio que contém a média (mean) da coluna wage (salário) para cada um dos grupos definidos anteriormente.

O resultado final é um pequeno dataframe com duas linhas e duas colunas: genero_label e salario_medio.

2. Criação do Gráfico de Barras Estático com ggplot2

Este bloco usa o dataframe recém-criado para gerar um gráfico de barras.

  • p_barras <- ggplot(...): Inicia a criação do objeto de gráfico e o atribui à variável p_barras.

  • ggplot(salario_medio_genero, aes(x = genero_label, ...)): Usa o dataframe salario_medio_genero como fonte.

    • aes(x = genero_label, y = salario_medio, fill = genero_label, text = ...): Mapeia as variáveis às estéticas do gráfico.

      • x = genero_label: As categorias no eixo X serão “Homem” e “Mulher”.

      • y = salario_medio: A altura das barras será determinada pelo valor na coluna salario_medio.

      • fill = genero_label: A cor de preenchimento (fill) de cada barra será determinada pela categoria de gênero.

      • text = paste(...): Novamente, cria o texto personalizado para o tooltip interativo. Por exemplo: “Salário Médio: $ 7.09”.

  • + geom_col(): Adiciona a camada de barras ao gráfico. É importante notar que se usa geom_col() (e não geom_bar()) porque a altura das barras (y) já é um valor explícito no dataframe. geom_bar() seria usado se quiséssemos que o próprio ggplot contasse o número de ocorrências de cada categoria.

  • + scale_fill_manual(...): Personaliza as cores de preenchimento das barras.

    • values = c(...): Atribui as mesmas cores do exemplo anterior (azul escuro e amarelo) às categorias “Homem” e “Mulher”.
  • + labs(...): Define os títulos e rótulos do gráfico.

  • + theme_minimal(): Aplica o tema visual minimalista.

  • + theme(legend.position = "none"): Modifica o tema para remover a legenda de cores. Isso é feito porque a informação de cor já está representada de forma clara no eixo X, tornando a legenda redundante.

3. Transformação em Gráfico Interativo com plotly

Esta linha final converte o gráfico estático em uma visualização interativa.

  • ggplotly(p_barras, tooltip = "text"):

    • ggplotly(): Converte o objeto ggplot (p_barras) em um gráfico interativo.

    • tooltip = "text": Instrui o plotly a usar a string de texto que criamos na estética text para exibir uma caixa de informação personalizada quando o usuário passar o mouse sobre as barras do gráfico.

Gráfico de Barras Empilhadas Interativo (Stacked Bar Plot)

Agora, vamos analisar a composição do estado civil (married) dentro de cada gênero. Um gráfico de barras empilhadas nos mostrará a contagem de pessoas casadas e não casadas para homens e mulheres.

Interatividade: Passe o mouse sobre cada segmento da barra para ver a contagem exata daquele grupo específico (ex: mulheres casadas, homens não casados).

# --- 1. Opções de Cores Personalizadas (Fácil de Modificar) ---
# Crie um vetor nomeado para as cores. Mudar as cores aqui é simples e direto.
# O nome ("Não Casado(a)") deve corresponder exatamente à etiqueta do fator.
cores_estado_civil <- c(
  "Não Casado(a)" = "#02023C", # Uma cor para os não casados
  "Casado(a)"     = "#FEB712"  # Uma cor para os casados
)


# --- 2. Criação do Gráfico ggplot ---
p_empilhado <- ggplot(wage1, aes(
    x = factor(female, labels = c("Homem", "Mulher")),
    fill = factor(married, labels = c("Não Casado(a)", "Casado(a)")),
    
    # --- 3. Adicionando um "Bom Tooltip" ---
    # A estética 'text' define o conteúdo do tooltip. Usamos paste() para juntar o texto.
    # <br> é uma tag HTML para criar uma nova linha no tooltip.
    text = paste("Gênero:", factor(female, labels = c("Homem", "Mulher")),
                 "<br>Estado Civil:", factor(married, labels = c("Não Casado(a)", "Casado(a)")))
  )) +
  geom_bar(position = "stack") +
  
  # Usando as cores que definimos anteriormente
  scale_fill_manual(values = cores_estado_civil) +
  
  labs(
    title = "Distribuição do Estado Civil por Gênero",
    x = "Gênero",
    y = "Contagem de Indivíduos",
    fill = "Estado Civil" # Controla o título da legenda
  ) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5)) # Centraliza o título


# --- 4. Transformar em Interativo com o Tooltip Personalizado ---
# ggplotly vai usar a estética 'text' que definimos para criar o tooltip.
# Ele é inteligente o suficiente para adicionar a contagem automaticamente.
ggplotly(p_empilhado, tooltip = c("text", "y"))

Explicação do gráfico:

1. Criação do Gráfico Estático com ggplot2

Construção do gráfico visual usando a biblioteca ggplot2.

  • cores_estado_civil <- c(...): Cria um “vetor nomeado” em R chamado cores_estado_civil. Pense nisso como um pequeno dicionário ou uma lista de pares chave-valor.

    • "Não Casado(a)" = "#02023C": Associa a string (o rótulo) "Não Casado(a)" ao código de cor hexadecimal para azul escuro.

    • "Casado(a)" = "#FEB712": Associa o rótulo "Casado(a)" ao código de cor para amarelo/laranja.

  • p_empilhado <- ggplot(wage1, aes(...)): Inicia o gráfico usando o dataframe wage1 e o atribui ao objeto p_empilhado.

  • aes(...): Define o mapeamento das variáveis do dataframe para as propriedades visuais (“estéticas”) do gráfico.

    • x = factor(female, labels = c("Homem", "Mulher")): Mapeia o eixo X à variável female, transformando-a em um fator com os rótulos “Homem” e “Mulher”. Isso criará duas barras principais no gráfico.

    • fill = factor(married, labels = c("Não Casado(a)", "Casado(a)")): Esta é a estética chave para o empilhamento. Ela determina que a cor de preenchimento (fill) das barras será baseada na variável married (estado civil). O ggplot irá dividir cada barra do eixo X em segmentos coloridos, um para cada estado civil.

    • text = paste(...): Cria a string de texto personalizada para o tooltip interativo. A função paste() junta os textos e variáveis. Por exemplo, para um ponto de dados de uma mulher casada, o texto seria: "Gênero: Mulher<br>Estado Civil: Casado(a)". A tag <br> garante uma quebra de linha no tooltip.

  • + geom_bar(position = "stack"): Adiciona a camada de geometria que desenha as barras.

    • geom_bar(): Como estamos fornecendo apenas uma variável x (e uma fill), esta função automaticamente conta o número de observações para cada combinação de x e fill. O resultado dessa contagem é mapeado para a altura no eixo Y.

    • position = "stack": Especifica que os segmentos definidos pela estética fill devem ser empilhados uns sobre os outros.

  • + scale_fill_manual(values = cores_estado_civil): Personaliza as cores de preenchimento. Ele instrui o ggplot a usar as cores que definimos no nosso vetor cores_estado_civil. A correspondência é feita pelos nomes (“Casado(a)”, “Não Casado(a)”).

  • + labs(...): Configura os textos de rótulos e títulos do gráfico.

    • fill = "Estado Civil": Um detalhe importante aqui é que fill dentro de labs() define o título da legenda associada à estética de preenchimento.
  • + theme_minimal(): Aplica o tema visual limpo.

  • + theme(plot.title = element_text(hjust = 0.5)): Faz um ajuste fino no tema.

    • plot.title: Seleciona o elemento do título do gráfico para modificação.

    • element_text(hjust = 0.5): hjust significa “justificação horizontal”. Um valor de 0.5 (ou seja, 50%) centraliza o texto. 0 alinha à esquerda e 1 à direita.

2. Transformação em Gráfico Interativo com plotly

A linha final dá vida ao gráfico estático.

  • ggplotly(p_empilhado, tooltip = c("text", "y")):

    • ggplotly(): Converte o objeto ggplot (p_empilhado) em uma visualização HTML interativa.

    • tooltip = c("text", "y"): Este argumento controla o que é exibido na caixa de informação (tooltip) ao passar o mouse. Ao fornecer um vetor com c("text", "y"), você está pedindo para o plotly mostrar duas informações:

      • "text": O conteúdo da sua estética text personalizada.

      • "y": O valor do eixo Y para aquele segmento da barra (neste caso, a contagem de indivíduos).

Como Fazer Gráficos Interativos Complexos com Highchart

Carregamento e Preparação dos Dados

Primeiro, carregamos e preparamos o dataset intdef para a visualização, criando uma coluna de data e as colunas necessárias para o sombreamento entre as séries.

# Carregar os dados
data("intdef")

# Preparar o dataframe para o highcharter
df_analise <- intdef %>%
  # Selecionar e renomear variáveis para maior clareza
  select(ano = year, juros = i3, inflacao = inf) %>%
  # Criar uma coluna de data formal
  mutate(data = as.Date(paste0(ano, "-01-01"))) %>%
  # Ordenar por data, uma boa prática para séries temporais
  arrange(data)

# Visualizar o dataframe transformado
head(df_analise)
##    ano juros inflacao       data
## 1 1948  1.04      8.1 1948-01-01
## 2 1949  1.10     -1.2 1949-01-01
## 3 1950  1.22      1.3 1950-01-01
## 4 1951  1.55      7.9 1951-01-01
## 5 1952  1.77      1.9 1952-01-01
## 6 1953  1.93      0.8 1953-01-01

Construção do Gráfico Interativo

Agora, vamos construir nosso gráfico. Além das séries de dados e dos marcadores de eventos, adicionaremos as faixas de recessão.

Dados para os Marcadores (Flags)

Criamos um dataframe para os eventos (choques do petróleo) que queremos destacar.

flags_data <- data.frame(
  data = as.Date(c("1973-10-01", "1979-01-01")),
  title = c("C1", "C2"),
  text = c("Primeiro Choque do Petróleo (1973)", "Segundo Choque do Petróleo (1979)")
)

Explicação do código:

  • flags_data <-: Inicia a atribuição. O resultado do comando à direita será armazenado em um novo objeto (uma variável) chamado flags_data.

  • data.frame(...): Esta é a função principal em R para criar um data.frame, que é a estrutura padrão para armazenar dados em formato de tabela (linhas e colunas), similar a uma planilha. Cada argumento dentro da função define uma coluna da tabela.

    • data = as.Date(c("1973-10-01", "1979-01-01")):

      • data = ...: Cria uma coluna chamada data.

      • c("1973-10-01", "1979-01-01"): Cria um vetor (uma lista) de strings de texto contendo duas datas.

      • as.Date(...): Esta é uma função crucial. Ela converte as strings de texto em um formato especial de Data que o R entende. Isso é fundamental para que bibliotecas de gráficos como ggplot2 ou plotly saibam posicionar corretamente esses pontos em um eixo de tempo.

    • title = c("C1", "C2"):

      • title = ...: Cria uma segunda coluna chamada title.

      • c("C1", "C2"): Cria um vetor de texto com rótulos curtos. Em um gráfico, estes são frequentemente usados como pequenos textos que aparecem diretamente sobre ou ao lado de um marcador de evento.

    • text = c("Primeiro Choque do Petróleo (1973)", "Segundo Choque do Petróleo (1979)"):

      • text = ...: Cria uma terceira coluna chamada text.

      • c(...): Cria um vetor de texto com descrições mais longas. Este texto é ideal para ser usado como o conteúdo da caixa de informação (tooltip) que aparece quando um usuário passa o mouse sobre o marcador do evento em um gráfico interativo.

Dados para as Faixas de Recessão (Plot Bands)

Para adicionar as faixas de recessão, precisamos de uma lista contendo as datas de início e fim de cada período. Utilizaremos as datas oficiais do National Bureau of Economic Research (NBER) para os EUA. O highcharter espera que as datas de início (from) e fim (to) estejam em formato de timestamp (milissegundos desde 1970-01-01), mas ele faz a conversão automaticamente a partir de objetos Date.

# Função para converter data para timestamp em milissegundos
date_to_timestamp <- function(date_string) {
  as.numeric(as.POSIXct(date_string)) * 1000
}

# Lista que define cada faixa de recessão com o formato de data corrigido
recession_bands <- list(
  list(from = date_to_timestamp("1948-11-01"), to = date_to_timestamp("1949-10-01"), color = "rgba(100, 100, 100, 0.2)", label = list(style = list(color = "gray"))),
  list(from = date_to_timestamp("1953-07-01"), to = date_to_timestamp("1954-05-01"), color = "rgba(100, 100, 100, 0.2)"),
  list(from = date_to_timestamp("1957-08-01"), to = date_to_timestamp("1958-04-01"), color = "rgba(100, 100, 100, 0.2)"),
  list(from = date_to_timestamp("1960-04-01"), to = date_to_timestamp("1961-02-01"), color = "rgba(100, 100, 100, 0.2)"),
  list(from = date_to_timestamp("1969-12-01"), to = date_to_timestamp("1970-11-01"), color = "rgba(100, 100, 100, 0.2)"),
  list(from = date_to_timestamp("1973-11-01"), to = date_to_timestamp("1975-03-01"), color = "rgba(100, 100, 100, 0.2)"),
  list(from = date_to_timestamp("1980-01-01"), to = date_to_timestamp("1980-07-01"), color = "rgba(100, 100, 100, 0.2)"),
  list(from = date_to_timestamp("1981-07-01"), to = date_to_timestamp("1982-11-01"), color = "rgba(100, 100, 100, 0.2)"),
  list(from = date_to_timestamp("1990-07-01"), to = date_to_timestamp("1991-03-01"), color = "rgba(100, 100, 100, 0.2)"),
  list(from = date_to_timestamp("2001-03-01"), to = date_to_timestamp("2001-11-01"), color = "rgba(100, 100, 100, 0.2)")
)

Explicação do código:

1. Função para conversão de datas.

  • date_to_timestamp <- function(date_string) { ... }: Define uma nova função chamada date_to_timestamp que aceita um argumento, date_string (uma data em formato de texto).

  • as.POSIXct(date_string): Esta é a primeira etapa da conversão. Ela pega a string de texto (ex: "1948-11-01") e a converte para um formato de data/hora que o R entende, chamado POSIXct. Internamente, este formato armazena a data como o número de segundos passados desde o início da “era Unix” (01 de janeiro de 1970).

  • as.numeric(...): Extrai apenas o valor numérico (o número de segundos) do objeto POSIXct.

  • * 1000: Multiplica o número de segundos por 1000. Isso converte o valor para milissegundos. Essa conversão é muito comum porque bibliotecas de gráficos interativos baseadas em JavaScript (como Highcharts, que é usada pelo pacote highcharter em R) exigem que os tempos sejam fornecidos em milissegundos.

Em resumo, a função pega uma data em texto e a transforma em um número que representa essa mesma data em milissegundos, pronta para ser usada por uma biblioteca de gráficos.

2. Estrutura de Dados: recession_bands

Esta parte do código cria o objeto principal, uma lista chamada recession_bands.

  • recession_bands <- list(...): Cria uma lista. Em R, uma lista é uma coleção que pode conter diversos tipos de objetos, inclusive outras listas. Isso a torna perfeita para armazenar configurações complexas.

  • list(from = ..., to = ..., color = ...): A lista recession_bands contém uma série de outras listas. Cada uma dessas listas internas representa uma única faixa de recessão e possui os seguintes elementos nomeados:

    • from: Define o início da faixa de recessão. O valor é obtido chamando a função date_to_timestamp() com a data de início.

    • to: Define o fim da faixa de recessão, também usando a função para converter a data.

    • color = "rgba(100, 100, 100, 0.2)": Define a cor da faixa sombreada. O formato rgba significa Vermelho (Red), Verde (Green), Azul (Blue) e Alpha (transparência). Neste caso, (100, 100, 100) é um tom de cinza, e 0.2 é o nível de alpha, que torna a faixa 80% transparente, permitindo que os dados do gráfico (como uma linha) sejam vistos através dela.

    • label = list(...): Um elemento opcional para configurar um rótulo de texto que pode ser exibido sobre a faixa.

Código do Gráfico Final

Juntamos todas as peças, incluindo a chamada à função hc_xAxis para adicionar as plotBands.

highchart(type = "stock") %>%
  # Adição das séries de dados (Juros e Inflação)
  hc_add_series(data = df_analise, type = "line", hcaes(x = data, y = juros), name = "Taxa de Juros (i3)", id = "juros", color = "#02023C") %>%
  hc_add_series(data = df_analise, type = "line", hcaes(x = data, y = inflacao), name = "Inflação (inf)", color = "#FEB712") %>%
  
  # A SÉRIE 'arearange' FOI REMOVIDA DESTA SEÇÃO
  
  # Adição dos marcadores de eventos (flags)
  hc_add_series(data = flags_data, type = "flags", hcaes(x = data, title = title, text = text), onSeries = "juros", name = "Eventos Históricos") %>%
  
  # Adição das Faixas de Recessão ao Eixo X
  hc_xAxis(
    plotBands = recession_bands
  ) %>%

  # Títulos e legendas
  hc_title(text = "Relação entre Taxa de Juros e Inflação nos EUA (1948-2003)") %>%
  hc_subtitle(text = "Períodos de recessão (NBER) indicados por faixas sombreadas") %>%
  hc_credits(enabled = TRUE, text = "Fonte: Wooldridge, NBER | Gráfico: Highcharter") %>%
  hc_legend(enabled = TRUE) %>% 
  
  #Disponibilização de download do gráfico e dos dados
  hc_exporting(enabled = TRUE) %>% 
  
  # Tooltip customizado
  hc_tooltip(
    shared = TRUE, 
    crosshairs = TRUE,
    valueDecimals = 2,
    pointFormat = '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y} %</b><br/>'
  )

Explicação do código:

  • highchart(type = "stock"):

    • Esta é a função que inicializa o objeto do gráfico.

    • type = "stock": Especifica que queremos criar um “gráfico de ações”. Este tipo de gráfico é otimizado para séries temporais e inclui automaticamente ferramentas úteis, como um navegador de intervalo de datas (o “navigator” na parte inferior) e botões de zoom predefinidos (1m, 3m, 1a, etc.).

  • hc_add_series(...):

    • Esta função é usada para adicionar camadas de dados (séries) ao gráfico. É chamada três vezes aqui:
    1. Primeira chamada (Juros):

      • data = df_analise, type = "line", hcaes(x = data, y = juros): Adiciona os dados do dataframe df_analise como uma linha (type = "line"), mapeando a coluna data para o eixo X e juros para o eixo Y.

      • name = "...": Define o nome da série que aparecerá na legenda e no tooltip.

      • id = "juros": Atribui um identificador único a esta série. Isso é crucial, pois permite que outras camadas (como as “flags”) se refiram a esta linha específica.

      • color = "...": Define a cor da linha.

    2. Segunda chamada (Inflação): Faz o mesmo que a anterior, mas para a coluna inflacao, com uma cor diferente.

    3. Terceira chamada (Flags/Eventos):

      • data = flags_data: Usa o dataframe flags_data que criamos anteriormente.

      • type = "flags": Especifica um tipo de série especial para marcar eventos no gráfico.

      • hcaes(x = data, title = title, text = text): Mapeia as colunas do flags_data para as propriedades das flags: x para a posição no tempo, title para o rótulo curto no gráfico (C1, C2) e text para a descrição longa no tooltip.

      • onSeries = "juros": Instrui o Highcharter a ancorar essas flags diretamente na linha que tem o id = "juros". Isso faz com que os marcadores apareçam sobre a linha de juros.

  • hc_xAxis(plotBands = recession_bands):

    • Esta função personaliza o eixo X.

    • plotBands = recession_bands: Este é o argumento chave que recebe a lista recession_bands que definimos no código anterior. O Highcharter usa essa lista para desenhar as faixas sombreadas no fundo do gráfico, indicando os períodos de recessão.

  • hc_title(...), hc_subtitle(...), hc_credits(...), hc_legend(...):

    • Este bloco de funções configura os elementos textuais e de metadados do gráfico: o título principal, o subtítulo, os créditos (fonte dos dados) no canto inferior e garante que a legenda esteja habilitada.
  • hc_exporting(enabled = TRUE):

    • Adiciona o menu “hambúrguer” (☰) no canto superior direito do gráfico, que permite ao usuário final baixar a visualização como uma imagem (PNG, JPEG, SVG) ou exportar os dados brutos (CSV, XLS).
  • hc_tooltip(...):

    • Personaliza a caixa de informações (tooltip) que aparece quando o mouse passa sobre o gráfico.

    • shared = TRUE: Faz com que uma única caixa de informações apareça para todas as séries em um ponto específico do tempo. Ao passar o mouse sobre uma data, você verá os valores de juros e inflação juntos.

    • crosshairs = TRUE: Ativa as linhas de mira (vertical e horizontal) que seguem o cursor, facilitando a leitura dos valores nos eixos.

    • valueDecimals = 2: Formata os números no tooltip para terem sempre duas casas decimais.

    • pointFormat = '...': Define um modelo (template) HTML para formatar cada linha dentro do tooltip compartilhado.

      • <span style="color:{point.color}">\u25CF</span>: Cria um marcador de círculo (\u25CF) que herda a cor da série ({point.color}).

      • {series.name}: Insere o nome da série (ex: “Taxa de Juros (i3)”).

      • <b>{point.y} %</b>: Insere o valor do ponto no eixo Y ({point.y}), em negrito (<b>), seguido de um sinal de porcentagem.

      • <br/>: Adiciona uma quebra de linha.

Mapas Interativos com leaflet

Quando você tem dados geográficos, o leaflet é a ferramenta ideal. Vamos criar um mapa simples que marca a localização de algumas capitais brasileiras.

# Criamos um dataframe com as coordenadas das cidades
capitais <- data.frame(
  nome = c("Belo Horizonte", "Rio de Janeiro", "São Paulo", "Salvador"),
  lat = c(-19.9245, -22.9068, -23.5505, -12.9777),
  lon = c(-43.9352, -43.1729, -46.6333, -38.5016),
  populacao = c("2.5 milhões", "6.7 milhões", "12.3 milhões", "2.9 milhões")
)

# Criando o mapa
leaflet(data = capitais) %>%
  addTiles() %>%  # Adiciona o mapa base (OpenStreetMap)
  addMarkers(
    lng = ~lon, 
    lat = ~lat, 
    popup = ~paste0("<strong>", nome, "</strong><br>População Aprox.: ", populacao)
  )

1. Criação do Dataframe capitais

Esta primeira parte do código simplesmente organiza os dados que serão exibidos no mapa em uma tabela.

  • capitais <- data.frame(...): Cria um objeto do tipo data.frame (a estrutura de tabela padrão do R) e o armazena na variável capitais.

  • Dentro do data.frame(), cada argumento cria uma coluna na tabela:

    • nome = c(...): Cria a coluna nome, contendo um vetor de texto com os nomes das capitais.

    • lat = c(...): Cria a coluna lat, contendo as coordenadas de latitude para cada cidade.

    • lon = c(...): Cria a coluna lon, contendo as coordenadas de longitude para cada cidade.

    • populacao = c(...): Cria a coluna populacao, com a população aproximada de cada cidade armazenada como texto.

O resultado é uma tabela organizada, onde cada linha representa uma cidade e suas respectivas propriedades (nome, coordenadas, etc.).

2. Construção do Mapa com leaflet

Esta seção usa o data.frame criado e o encadeamento de funções (através do pipe %>%) para construir o mapa camada por camada.

  • leaflet(data = capitais):

    • Esta é a função que inicializa o objeto do mapa.

    • data = capitais: Informa ao leaflet que o conjunto de dados padrão para as camadas seguintes será o dataframe capitais. Isso evita que precisemos especificar a fonte de dados em cada função subsequente.

  • %>% addTiles():

    • Adiciona a camada de “azulejos” (tiles), que é o mapa base visual. Por padrão, ele busca os mapas do OpenStreetMap, um projeto de mapeamento colaborativo e gratuito. Sem esta linha, você teria apenas um quadro em branco.
  • %>% addMarkers(...):

    • Adiciona os marcadores (alfinetes) ao mapa, um para cada linha do nosso dataframe.

    • lng = ~lon e lat = ~lat: Estes argumentos especificam de onde virão as coordenadas para os marcadores. O til (~) é uma forma de dizer ao leaflet para “procurar por uma coluna com este nome (lon ou lat) dentro do dataframe que foi fornecido na função leaflet()”.

    • popup = ~paste0(...): Este argumento define o conteúdo da janela pop-up que aparece quando um usuário clica em um marcador.

      • paste0(...): É uma função do R que junta (concatena) textos.

      • "<strong>", nome, "</strong>": Pega o valor da coluna nome e o envolve com as tags HTML <strong>, o que fará com que o nome da cidade apareça em negrito.

      • "<br>": Adiciona uma quebra de linha em HTML.

      • "População Aprox.: ", populacao: Junta um texto fixo com o valor da coluna populacao.

Publicando no RPubs

Agora que seu documento está pronto, com texto formatado e gráficos interativos, é hora de compartilhá-lo. O RPubs é uma plataforma gratuita e simples, perfeitamente integrada ao RStudio.

Passo 1: Instalar o Pacote rsconnect Se você nunca publicou antes, precisará instalar o pacote que gerencia a conexão. Você só precisa fazer isso uma vez.

# Execute este código no seu Console do R, não precisa estar em um chunk.
install.packages("rsconnect")  

Passo 2: “Tricotar” (Knit) seu Documento Clique no botão Knit (ou use o atalho Ctrl+Shift+K) no RStudio. Isso irá gerar o arquivo html final. Uma janela de visualização aparecerá com o seu relatório.

Passo 3: Publicar Na janela de visualização do seu relatório, no canto superior direito, você verá um botão Publish.

  1. Clique em Publish.

  2. Escolha a opção RPubs.

  3. Se for sua primeira vez, o RStudio pedirá para você se autenticar. Ele abrirá uma página no seu navegador para você criar ou logar em uma conta no RPubs. Siga as instruções.

  4. Após a autenticação, dê um nome e uma descrição para sua publicação e clique em Publish.

Pronto! O RStudio fará o upload do seu documento e de todos os arquivos necessários (como os gráficos interativos) para o servidor do RPubs. Ao final, ele abrirá o seu navegador na página da sua publicação recém-criada, com um link que você pode compartilhar com quem quiser.

LS0tDQp0aXRsZTogIkNyaWFuZG8gZSBQdWJsaWNhbmRvIEFuw6FsaXNlcyBjb20gUk1hcmtkb3duIG5vIFJQdWJzIg0KYXV0aG9yOiAiQnJ1bm8gTWVsbyBkZSBPbGl2ZWlyYSBTYW50b3MiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBmYWxzZQ0KICAgIHRvY19mbG9hdDogZmFsc2UNCiAgICB0aGVtZTogbHVtZW4NCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZSAgDQotLS0NCg0KIyBJbnRyb2R1w6fDo28gYW8gUk1hcmtkb3duDQoNCk8gUk1hcmtkb3duIMOpIHVtYSBmZXJyYW1lbnRhIHF1ZSB0cmFuc2Zvcm1hIHN1YXMgYW7DoWxpc2VzIGRlIGRhZG9zIGVtIGRvY3VtZW50b3MgZGUgYWx0YSBxdWFsaWRhZGUuIFBlbnNlIG5lbGUgY29tbyB1bSBjYWRlcm5vIGRlIGxhYm9yYXTDs3JpbyBkaWdpdGFsLiBFbSB2ZXogZGUgZXNjcmV2ZXIgc2V1IGPDs2RpZ28gZW0gdW0gbHVnYXIsIHNldXMgZ3LDoWZpY29zIGVtIG91dHJvIGUgc2V1IHRleHRvIGV4cGxpY2F0aXZvIGVtIHVtIHRlcmNlaXJvLCBvIFJNYXJrZG93biBwZXJtaXRlIHF1ZSB2b2PDqiBmYcOnYSB0dWRvIGlzc28gZW0gdW0gw7puaWNvIGFycXVpdm8uDQoNCkEgZmlsb3NvZmlhIMOpIGEgZGEgKipwZXNxdWlzYSByZXByb2R1dMOtdmVsKiouIFNlIHVtIGNvbGVnYSAob3Ugdm9jw6ogbWVzbW8sIGRhcXVpIGEgc2VpcyBtZXNlcykgcXVpc2VyIGVudGVuZGVyIG91IHJlcGxpY2FyIHN1YSBhbsOhbGlzZSwgYmFzdGEgdGVyIGVzdGUgw7puaWNvIGFycXVpdm8gYC5SbWRgLiBBbyBleGVjdXTDoS1sbyAoInRyaWNvdGFyIiwgb3UgKmtuaXQqKSwgbyByZXN1bHRhZG8gc2Vyw6EgZXhhdGFtZW50ZSBvIG1lc21vLCBnYXJhbnRpbmRvIHRyYW5zcGFyw6puY2lhIGUgY29uc2lzdMOqbmNpYS4NCg0KTyBkZXN0aW5vIGZpbmFsIHBvZGUgc2VyIHVtIHJlbGF0w7NyaW8gZW0gUERGLCB1bWEgYXByZXNlbnRhw6fDo28gZGUgc2xpZGVzLCB1bSBzaXRlIG91LCBjb21vIGZvY2FyZW1vcyBob2plLCB1bWEgcMOhZ2luYSB3ZWIgaW50ZXJhdGl2YSB2aWEgUlB1YnMuDQoNCiMgQW5hdG9taWEgZGUgdW0gQXJxdWl2byBgLlJtZGANCg0KVW0gYXJxdWl2byBSTWFya2Rvd24gKGAuUm1kYCkgdGVtIHRyw6pzIGNvbXBvbmVudGVzIHByaW5jaXBhaXM6DQoNCi0gICAqKkNhYmXDp2FsaG8gWUFNTCoqOiBVbSBibG9jbyBkZSB0ZXh0byBubyB0b3BvIGRvIGFycXVpdm8sIGNlcmNhZG8gcG9yIGAtLS1gLCBxdWUgZGVmaW5lIG1ldGFkYWRvcyBlIG9ww6fDtWVzIGdsb2JhaXMgcGFyYSBvIHNldSBkb2N1bWVudG8uIMOJIGFxdWkgcXVlIHZvY8OqIGRlZmluZSBvIHTDrXR1bG8sIGF1dG9yLCBkYXRhIGUgbyBmb3JtYXRvIGRlIHNhw61kYS4NCi0gICAqKk5hcnJhdGl2YSBlbSBNYXJrZG93bioqOiBPIHRleHRvIGNvbXVtIHF1ZSB2b2PDqiBlc2NyZXZlIHBhcmEgZXhwbGljYXIgc3VhIGFuw6FsaXNlLCBpbnRyb2R1emlyIHNlw6fDtWVzIGUgY29uY2x1aXIgc3VhcyBpZGVpYXMuIEVsZSB1c2EgdW1hIHNpbnRheGUgc2ltcGxlcyBjaGFtYWRhIE1hcmtkb3duIHBhcmEgZm9ybWF0YcOnw6NvLg0KLSAgICoqKkNvZGUgQ2h1bmtzKioqICoqKEJsb2NvcyBkZSBDw7NkaWdvKSoqOiBCbG9jb3MgZGUgY8OzZGlnbyBSIChvdSBvdXRyYXMgbGluZ3VhZ2VucyksIGNlcmNhZG9zIHBvciBgYGBgIGBgYHtyfSBgYGBgIGUgYGBgYCBgYGAgYGBgYC4gw4kgYXF1aSBxdWUgYSBtw6FnaWNhIGFjb250ZWNlOiB2b2PDqiBjYXJyZWdhIGRhZG9zLCBmYXogbWFuaXB1bGHDp8O1ZXMsIGNyaWEgdGFiZWxhcyBlIGdlcmEgZ3LDoWZpY29zLg0KDQpWYW1vcyB2ZXIgdW0gZXhlbXBsbyBkZSB1bSAqY2h1bmsqIGLDoXNpY28gcXVlIGNhcnJlZ2EgYXMgYmlibGlvdGVjYXMuDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRX0NCiMgRXN0ZSDDqSB1bSBjaHVuayBlc3BlY2lhbCBkZSAnc2V0dXAnLg0KIyBBIG9ww6fDo28gJ2luY2x1ZGU9RkFMU0UnIHNpZ25pZmljYSBxdWUgZXN0ZSBjw7NkaWdvIHNlcsOhIGV4ZWN1dGFkbywgDQojIG1hcyBuZW0gbyBjw7NkaWdvIG5lbSBzZXUgcmVzdWx0YWRvIGFwYXJlY2Vyw6NvIG5vIGRvY3VtZW50byBmaW5hbC4NCiMgRWxhIG7Do28gZm9pIGFxdWkgdXRpbGl6YWRhIHBhcmEgcXVlIHBvc3NhbSB2aXp1YWxpemFyIGFzIGJpYmxpb3RlY2FzLg0KIyDDiSDDs3RpbW8gcGFyYSBjYXJyZWdhciBwYWNvdGVzIGUgZGVmaW5pciBvcMOnw7VlcyBnbG9iYWlzLg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeSh3b29sZHJpZGdlKQ0KYGBgDQoNCiMgQ2FiZcOnYWxobyBZQU1MIHV0aWxpemFkbyBuZXNzZSBhcnF1aXZvDQoNCi0tLVwNCnRpdGxlOiAiQ3JpYW5kbyBlIFB1YmxpY2FuZG8gQW7DoWxpc2VzIGNvbSBSTWFya2Rvd24gbm8gUlB1YnMiXA0KYXV0aG9yOiAiQnJ1bm8gTWVsbyBkZSBPbGl2ZWlyYSBTYW50b3MiXA0KZGF0ZTogImByIFN5cy5EYXRlKClgIlwNCm91dHB1dDpcDQpodG1sX2RvY3VtZW50OlwNCnRvYzogZmFsc2VcDQp0b2NfZmxvYXQ6IGZhbHNlXA0KdGhlbWU6IGx1bWVuXA0KaGlnaGxpZ2h0OiB0YW5nb1wNCi0tLQ0KDQojIENvbW8gRm9ybWF0YXIgbyBUZXh0byBubyBSTWFya2Rvd24NCg0KQSBzaW50YXhlIE1hcmtkb3duIMOpIHByb2pldGFkYSBwYXJhIHNlciBmw6FjaWwgZGUgbGVyIGUgZXNjcmV2ZXIuIEFxdWkgZXN0w6NvIG9zIGVsZW1lbnRvcyBkZSBmb3JtYXRhw6fDo28gbWFpcyBjb211bnMgcXVlIHZvY8OqIHVzYXLDoSBwYXJhIGNvbnN0cnVpciBhIG5hcnJhdGl2YSBkbyBzZXUgZG9jdW1lbnRvLg0KDQojIyBDYWJlw6dhbGhvcyBkZSBTZcOnw6NvDQoNClBhcmEgY3JpYXIgaGllcmFycXVpYSBubyBzZXUgZG9jdW1lbnRvLCB1c2UgbyAjLg0KDQoqKlVtICM6KioNCg0KIyBUw610dWxvIFByaW5jaXBhbCAoTsOtdmVsIDEpDQoNCioqRG9pcyAjOioqDQoNCiMjIFN1YnTDrXR1bG8gKE7DrXZlbCAyKQ0KDQoqKlRyw6pzICM6KioNCg0KIyMjIFVtIE7DrXZlbCBNYWlzIEFiYWl4byAoTsOtdmVsIDMpDQoNCiMjIMOKbmZhc2Ugbm8gVGV4dG8NCg0KVm9jw6ogcG9kZSBkZWl4YXIgbyB0ZXh0byBlbSBpdMOhbGljbyBhZGljaW9uYW5kbyB1bSBhc3RlcsOtc2NvIChcKikgZGUgY2FkYSBsYWRvLCBvdSBlbSBuZWdyaXRvIGFkaWNpb25hbmRvIGRvaXMgYXN0ZXLDrXNjb3MgZGUgY2FkYSBsYWRvIChcKlwqKS4NCg0KKkVzdGUgdGV4dG8gZmljYXLDoSBlbSBpdMOhbGljby4qDQoNCioqRXN0ZSB0ZXh0byBmaWNhcsOhIGVtIG5lZ3JpdG8uKioNCg0KIyMgTGlzdGFzDQoNCiMjIyBMaXN0YXMgTsOjbyBPcmRlbmFkYXM6DQoNClVzZSBcKiwgKyBvdSAtLCBlbmRlbnRhbmRvIHBhcmEgc3ViLWl0ZW5zLg0KDQotICAgUHJpbWVpcm8gaXRlbQ0KLSAgIFNlZ3VuZG8gaXRlbQ0KICAgIC0gICBVbSBzdWItaXRlbSBhbmluaGFkbw0KLSAgIFRlcmNlaXJvIGl0ZW0NCg0KIyMjIExpc3RhcyBPcmRlbmFkYXM6DQoNClVzZSBuw7ptZXJvcyBzZWd1aWRvcyBkZSB1bSBwb250by4NCg0KMS4gIFByaW1laXJvIHBhc3NvDQoyLiAgU2VndW5kbyBwYXNzbw0KMy4gIFRlcmNlaXJvIHBhc3NvDQoNCiMgTGlua3MgZSBJbWFnZW5zDQoNCioqTGlua3M6KiogYFtQb3NpdF0oaHR0cHM6Ly9wb3NpdC5jby8pYA0KDQpbUG9zaXRdKGh0dHBzOi8vcG9zaXQuY28vKQ0KDQoqKkltYWdlbnM6KiogYCFbVGV4dG8gYWx0ZXJuYXRpdm8gcGFyYSBhIGltYWdlbV0oY2FtaW5oby9wYXJhL2ltYWdlbS5qcGcpYA0KDQohW1RleHRvIGFsdGVybmF0aXZvIHBhcmEgYSBpbWFnZW1dKGNhbWluaG8vcGFyYS9pbWFnZW0uanBnKQ0KDQoqKk9iczoqKiBBIGltYWdlbSBwcmVjaXNhIGVzdGFyIG5hIG1lc21hIHBhc3RhIGRvIHNldSBhcnF1aXZvIC5SbWQgb3Ugdm9jw6ogZGV2ZSBmb3JuZWNlciBvIGNhbWluaG8gY29tcGxldG8uDQoNCioqQ2FpeGluaGEqKiBQYXJhIGNvbG9jYXIgbyB0ZXh0byBkZW50cm8gZGUgdW1hIGNhaXhhLCB1dGlsaXplIHVtIChcYCkgZGUgY2FkYSBsYWRvLg0KDQpgQXVsYV9SUHVicy5SbWRgDQoNCioqQ2l0YcOnw7VlcyAoQmxvY2txdW90ZXMpKiogUGFyYSBkZXN0YWNhciB1bSB0ZXh0byBvdSB1bWEgY2l0YcOnw6NvLCB1c2UgXD4uDQoNCj4gIkEgYXJ0ZSBkZSBwcm9ncmFtYXIgY29uc2lzdGUgbmEgYXJ0ZSBkZSBvcmdhbml6YXIgZSBkb21pbmFyIGEgY29tcGxleGlkYWRlLiIgLSBFZHNnZXIgRGlqa3N0cmENCg0KKipFcXVhw6fDtWVzKiogQXMgZXF1YcOnw7VlcyBkZXZlcsOjbyBzZXIgZGlnaXRhZGFzIG5vIGZvcm1hdG8gTGF0ZXggdXRpbGl6YW5kbyBkb2lzIGNpZnLDtWVzIGRlIGNhZGEgbGFkbw0KDQpgJCRyXF97dH0gPSB7XHBoaX0qezB9ICsgeypccGhpfXsxfXJcX3t0LTF9ICsge1xwaGl9KnsyfXIqe3QtMn0gKyBhXF97dH0kJGANCg0KJCQNCnJfe3R9ID0ge1xwaGl9X3swfSArIHtccGhpfV97MX1yX3t0LTF9ICsge1xwaGl9X3syfXJfe3QtMn0gKyBhX3t0fQ0KJCQNCg0KIyBDb21vIEZhemVyIEdyw6FmaWNvcyBJbnRlcmF0aXZvcyBCw6FzaWNvcyBjb20gUGxvdGx5DQoNCiMjIEFuw6FsaXNlIGRlIERhZG9zOiBTYWzDoXJpb3MgZSBFZHVjYcOnw6NvDQoNClBhcmEgbm9zc29zIGV4ZW1wbG9zLCB1c2FyZW1vcyBvIGNvbmp1bnRvIGRlIGRhZG9zIGB3YWdlMWAgZG8gcGFjb3RlIGB3b29sZHJpZGdlYC4gRXN0ZSBkYXRhc2V0IGNvbnTDqW0gaW5mb3JtYcOnw7VlcyBzb2JyZSBzYWzDoXJpb3MgZSBvdXRyYXMgdmFyacOhdmVpcyBwYXJhIDUyNiB0cmFiYWxoYWRvcmVzLg0KDQpWYW1vcyBjYXJyZWdhciBvcyBkYWRvcyBlIGRhciB1bWEgb2xoYWRhIGVtIHN1YSBlc3RydXR1cmEuDQoNCmBgYHtyIGNhcnJlZ2FyLWRhZG9zfQ0KIyBDYXJyZWdhIG8gZGF0YXNldCAid2FnZTEiIHBhcmEgbyBub3NzbyBhbWJpZW50ZQ0KZGF0YSgid2FnZTEiKQ0KDQojIFZpc3VhbGl6YSBhcyBwcmltZWlyYXMgbGluaGFzIGUgYSBlc3RydXR1cmEgZG9zIGRhZG9zDQpoZWFkKHdhZ2UxKQ0KZ2xpbXBzZSh3YWdlMSkNCmBgYA0KDQpPIGRhdGFzZXQgY29udMOpbSB2YXJpw6F2ZWlzIGludGVyZXNzYW50ZXMgY29tbzoNCg0KLSAgIGB3YWdlYDogc2Fsw6FyaW8gcG9yIGhvcmEgZW0gZMOzbGFyZXMuDQoNCi0gICBgZWR1Y2A6IGFub3MgZGUgZWR1Y2HDp8Ojby4NCg0KLSAgIGBleHBlcmA6IGFub3MgZGUgZXhwZXJpw6puY2lhIG5vIG1lcmNhZG8gZGUgdHJhYmFsaG8uDQoNCi0gICBgZmVtYWxlYDogdmFyacOhdmVsIGJpbsOhcmlhICgxIHNlIGZvciBtdWxoZXIsIDAgc2UgZm9yIGhvbWVtKS4NCg0KLSAgIGBtYXJyaWVkYDogdmFyacOhdmVsIGJpbsOhcmlhICgxIHNlIGZvciBjYXNhZG8oYSksIDAgc2UgbsOjbykuDQoNCkEgbWFuZWlyYSBtYWlzIGbDoWNpbCBlIHBvZGVyb3NhIGRlIGNyaWFyIGdyw6FmaWNvcyBpbnRlcmF0aXZvcyBhIHBhcnRpciBkZSB1bWEgc2ludGF4ZSBxdWUgbXVpdG9zIGrDoSBjb25oZWNlbSDDqSB1c2FyIGEgZnVuw6fDo28gYGdncGxvdGx5KClgIGRvIHBhY290ZSBgcGxvdGx5YC4gRWxhIHRyYW5zZm9ybWEgdW0gZ3LDoWZpY28gZXN0w6F0aWNvIGNyaWFkbyBjb20gYGdncGxvdDJgIGVtIHVtYSB2aXN1YWxpemHDp8OjbyBpbnRlcmF0aXZhIGVtIEhUTUwuDQoNCiMjIyBHcsOhZmljbyBkZSBEaXNwZXJzw6NvIEludGVyYXRpdm8gKFNjYXR0ZXIgUGxvdCkNCg0KVmFtb3MgaW52ZXN0aWdhciBhIHJlbGHDp8OjbyBlbnRyZSBhbm9zIGRlIGVkdWNhw6fDo28gKGBlZHVjYCkgZSBvIHNhbMOhcmlvIHBvciBob3JhIChgd2FnZWApLiBQYXJhIGRlaXhhciBhIHZpc3VhbGl6YcOnw6NvIG1haXMgcmljYSwgdmFtb3MgY29sb3JpciBvcyBwb250b3MgZGUgYWNvcmRvIGNvbSBvIGfDqm5lcm8gKGBmZW1hbGVgKS4NCg0KKipJbnRlcmF0aXZpZGFkZSoqOiBQYXNzZSBvIG1vdXNlIHNvYnJlIHVtIHBvbnRvIHBhcmEgdmVyIG9zIHZhbG9yZXMgZXhhdG9zIGRlIGVkdWNhw6fDo28sIHNhbMOhcmlvIGUgZ8OqbmVyby4gVm9jw6ogdGFtYsOpbSBwb2RlIHVzYXIgYXMgZmVycmFtZW50YXMgbm8gY2FudG8gc3VwZXJpb3IgZGlyZWl0byBkbyBncsOhZmljbyBwYXJhIGRhciB6b29tLCBtb3ZlciBhIMOhcmVhIGRlIHZpc3VhbGl6YcOnw6NvIGUgc2FsdmFyIG8gZ3LDoWZpY28gY29tbyB1bWEgaW1hZ2VtLg0KDQpgYGB7ciBncmFmaWNvLWRpc3BlcnNhb30NCiMgQ3JpYW1vcyBvIG9iamV0byBnZ3Bsb3QgcHJpbWVpcm8NCnBfZGlzcGVyc2FvIDwtIGdncGxvdCh3YWdlMSwgYWVzKHggPSBlZHVjLCB5ID0gd2FnZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBIHRyYW5zZm9ybWHDp8OjbyBvY29ycmUgYXF1aSwgYXBlbmFzIHBhcmEgbyBncsOhZmljbzoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoZmVtYWxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKDAsIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJIb21lbSIsICJNdWxoZXIiKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQSBlc3TDqXRpY2EgJ3RleHQnIHBlcnNvbmFsaXphIG8gcXVlIGFwYXJlY2Ugbm8gdG9vbHRpcA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUoIkVkdWNhw6fDo286IiwgZWR1YywgImFub3M8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTYWzDoXJpby9oOiAkIiwgcm91bmQod2FnZSwgMikpKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC43KSArDQogICMgTWFwZWFtb3MgYXMgY29yZXMgcGFyYSBhcyBub3ZhcyBldGlxdWV0YXMgKCJIb21lbSIsICJNdWxoZXIiKQ0KICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJHw6puZXJvIiwgdmFsdWVzID0gYygiSG9tZW0iID0gIiMwMjAyM0MiLCAiTXVsaGVyIiA9ICIjRkVCNzEyIikpICsNCiAgbGFicyh0aXRsZSA9ICJSZWxhw6fDo28gZW50cmUgRWR1Y2HDp8OjbyBlIFNhbMOhcmlvIHBvciBHw6puZXJvIiwNCiAgICAgICB4ID0gIkFub3MgZGUgRWR1Y2HDp8OjbyIsDQogICAgICAgeSA9ICJTYWzDoXJpbyBwb3IgSG9yYSAoVVNEKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiMgQWdvcmEsIHRyYW5zZm9ybWFtb3MgbyBvYmpldG8gZ2dwbG90IGVtIHVtIGdyw6FmaWNvIGludGVyYXRpdm8NCmdncGxvdGx5KHBfZGlzcGVyc2FvLCB0b29sdGlwID0gInRleHQiKQ0KYGBgDQoNCkV4cGxpY2HDp8OjbyBkbyBjw7NkaWdvOg0KDQoqKjEuIENyaWHDp8OjbyBkbyBHcsOhZmljbyBFc3TDoXRpY28gY29tIGBnZ3Bsb3QyYCoqDQoNCk8gcHJpbWVpcm8gYmxvY28gZGUgY8OzZGlnbyBjcmlhIHVtIG9iamV0byBkZSBncsOhZmljbyBjaGFtYWRvIGBwX2Rpc3BlcnNhb2AuDQoNCi0gICBgcF9kaXNwZXJzYW8gPC1gOiBBdHJpYnVpIG8gcmVzdWx0YWRvIGRvIGPDs2RpZ28gw6AgZGlyZWl0YSBhIHVtYSB2YXJpw6F2ZWwgY2hhbWFkYSBgcF9kaXNwZXJzYW9gLg0KDQotICAgYGdncGxvdChkYXRhID0gd2FnZTEsIC4uLilgOiBFc3RhIMOpIGEgZnVuw6fDo28gcHJpbmNpcGFsIHF1ZSBpbmljaWFsaXphIG8gZ3LDoWZpY28uDQoNCiAgICAtICAgYGRhdGEgPSB3YWdlMWA6IEVzcGVjaWZpY2EgcXVlIG8gY29uanVudG8gZGUgZGFkb3MgYSBzZXIgdXNhZG8gw6kgbyBgd2FnZTFgLg0KDQogICAgLSAgIGBhZXMoeCA9IGVkdWMsIHkgPSB3YWdlLCAuLi4pYDogQSBmdW7Dp8OjbyBgYWVzKClgIChkZSAqYWVzdGhldGljcyogb3UgImVzdMOpdGljYSIpIG1hcGVpYSBhcyB2YXJpw6F2ZWlzIGRvIGNvbmp1bnRvIGRlIGRhZG9zIGEgcHJvcHJpZWRhZGVzIHZpc3VhaXMgZG8gZ3LDoWZpY28uDQoNCiAgICAgICAgLSAgIGB4ID0gZWR1Y2A6IE1hcGVpYSBhIHZhcmnDoXZlbCBgZWR1Y2AgKGVkdWNhw6fDo28pIHBhcmEgbyBlaXhvIFguDQoNCiAgICAgICAgLSAgIGB5ID0gd2FnZWA6IE1hcGVpYSBhIHZhcmnDoXZlbCBgd2FnZWAgKHNhbMOhcmlvKSBwYXJhIG8gZWl4byBZLg0KDQogICAgICAgIC0gICBgY29sb3IgPSBmYWN0b3IoLi4uKWA6IERlZmluZSBxdWUgYSBjb3IgZG9zIHBvbnRvcyBzZXLDoSBkZXRlcm1pbmFkYSBwZWxhIHZhcmnDoXZlbCBgZmVtYWxlYC4NCg0KICAgICAgICAgICAgLSAgIGBmYWN0b3IoZmVtYWxlLCBsZXZlbHMgPSBjKDAsIDEpLCBsYWJlbHMgPSBjKCJIb21lbSIsICJNdWxoZXIiKSlgOiBFc3RhIHBhcnRlIMOpIGNydWNpYWwuIEVsYSB0cmFuc2Zvcm1hIGEgdmFyacOhdmVsIGBmZW1hbGVgIChxdWUgcHJvdmF2ZWxtZW50ZSBjb250w6ltIHZhbG9yZXMgbnVtw6lyaWNvcyBjb21vIDAgZSAxKSBlbSB1bWEgdmFyacOhdmVsIGNhdGVnw7NyaWNhICh1bSAiZmF0b3IiIGVtIFIpLiBPIHZhbG9yIGAwYCDDqSByb3R1bGFkbyBjb21vIGAiSG9tZW0iYCBlIG8gYDFgIGNvbW8gYCJNdWxoZXIiYC4gRXNzYSB0cmFuc2Zvcm1hw6fDo28gw6kgZmVpdGEgYXBlbmFzIHBhcmEgYSB2aXN1YWxpemHDp8OjbyBubyBncsOhZmljby4NCg0KICAgICAgICAtICAgYHRleHQgPSBwYXN0ZSguLi4pYDogQ3JpYSB1bSB0ZXh0byBwZXJzb25hbGl6YWRvIHBhcmEgY2FkYSBwb250byBkZSBkYWRvcy4gRXNzZSB0ZXh0byBzZXLDoSB1c2FkbyBtYWlzIHRhcmRlIHBlbGEgZnVuw6fDo28gYGdncGxvdGx5YCBwYXJhIGNyaWFyIGFzIGNhaXhhcyBkZSBpbmZvcm1hw6fDo28gKHRvb2x0aXBzKSBpbnRlcmF0aXZhcy4NCg0KICAgICAgICAgICAgLSAgIGBwYXN0ZSgiRWR1Y2HDp8OjbzoiLCBlZHVjLCAiYW5vczxicj4iLCAiU2Fsw6FyaW8vaDogJCIsIHJvdW5kKHdhZ2UsIDIpKWA6IENvbmNhdGVuYSAoanVudGEpIHbDoXJpb3MgdGV4dG9zIGUgdmFsb3JlcyBwYXJhIGZvcm1hciB1bWEgw7puaWNhIHN0cmluZy4gUG9yIGV4ZW1wbG86ICJFZHVjYcOnw6NvOiAxNiBhbm9zXDxiclw+U2Fsw6FyaW8vaDogXCQgMjUuNTAiLg0KDQogICAgICAgICAgICAtICAgYDxicj5gOiDDiSB1bWEgdGFnIEhUTUwgcGFyYSBxdWVicmEgZGUgbGluaGEsIHF1ZSBmYXLDoSBvIHRleHRvIGFwYXJlY2VyIGVtIGR1YXMgbGluaGFzIG5hIGNhaXhhIGRlIGluZm9ybWHDp8Ojby4NCg0KICAgICAgICAgICAgLSAgIGByb3VuZCh3YWdlLCAyKWA6IEFycmVkb25kYSBvIHZhbG9yIGRvIHNhbMOhcmlvIHBhcmEgZHVhcyBjYXNhcyBkZWNpbWFpcy4NCg0KLSAgIGArIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcpYDogQWRpY2lvbmEgdW1hIGNhbWFkYSBkZSBwb250b3MgYW8gZ3LDoWZpY28sIGNyaWFuZG8gbyBncsOhZmljbyBkZSBkaXNwZXJzw6NvLg0KDQogICAgLSAgIGBhbHBoYSA9IDAuN2A6IERlZmluZSBhIHRyYW5zcGFyw6puY2lhIGRvcyBwb250b3MgZW0gNzAlLiBJc3NvIMOpIMO6dGlsIHBhcmEgdmlzdWFsaXphciDDoXJlYXMgY29tIGFsdGEgY29uY2VudHJhw6fDo28gZGUgcG9udG9zIHNvYnJlcG9zdG9zLg0KDQotICAgYCsgc2NhbGVfY29sb3JfbWFudWFsKC4uLilgOiBQZXJtaXRlIHBlcnNvbmFsaXphciBtYW51YWxtZW50ZSBhcyBjb3JlcyB1c2FkYXMgbm8gbWFwZWFtZW50byBkZSBgY29sb3JgLg0KDQogICAgLSAgIGBuYW1lID0gIkfDqm5lcm8iYDogRGVmaW5lIG8gdMOtdHVsbyBkYSBsZWdlbmRhIGRhcyBjb3JlcyBjb21vICJHw6puZXJvIi4NCg0KICAgIC0gICBgdmFsdWVzID0gYygiSG9tZW0iID0gIiMwMjAyM0MiLCAiTXVsaGVyIiA9ICIjRkVCNzEyIilgOiBBdHJpYnVpIGNvcmVzIGVzcGVjw61maWNhcyAoZW0gY8OzZGlnbyBoZXhhZGVjaW1hbCkgcGFyYSBjYWRhIHVtIGRvcyByw7N0dWxvcyBxdWUgY3JpYW1vcy4gQSBjb3IgYXp1bCBlc2N1cm8gKGAjMDIwMjNDYCkgc2Vyw6EgdXNhZGEgcGFyYSAiSG9tZW0iIGUgYSBjb3IgYW1hcmVsYSAoYCNGRUI3MTJgKSBwYXJhICJNdWxoZXIiLg0KDQotICAgYCsgbGFicyguLi4pYDogQWRpY2lvbmEgb3UgbW9kaWZpY2Egb3MgcsOzdHVsb3MgZG8gZ3LDoWZpY28uDQoNCiAgICAtICAgYHRpdGxlID0gIi4uLiJgOiBEZWZpbmUgbyB0w610dWxvIHByaW5jaXBhbCBkbyBncsOhZmljby4NCg0KICAgIC0gICBgeCA9ICIuLi4iYDogRGVmaW5lIG8gcsOzdHVsbyBkbyBlaXhvIFguDQoNCiAgICAtICAgYHkgPSAiLi4uImA6IERlZmluZSBvIHLDs3R1bG8gZG8gZWl4byBZLg0KDQotICAgYCsgdGhlbWVfbWluaW1hbCgpYDogQXBsaWNhIHVtIHRlbWEgdmlzdWFsICJsaW1wbyIgYW8gZ3LDoWZpY28sIGNvbSBmdW5kbyBicmFuY28gZSBsaW5oYXMgZGUgZ3JhZGUgc3VhdmVzLCBzZW0gbXVpdG9zIGVsZW1lbnRvcyB2aXN1YWlzIGRlIGZ1bmRvLg0KDQoqKjIuIFRyYW5zZm9ybWHDp8OjbyBlbSBHcsOhZmljbyBJbnRlcmF0aXZvIGNvbSBgcGxvdGx5YCoqDQoNCk8gc2VndW5kbyBibG9jbyBkZSBjw7NkaWdvIHBlZ2EgbyBncsOhZmljbyBlc3TDoXRpY28gY3JpYWRvIGFudGVyaW9ybWVudGUgZSBvIHRvcm5hIGludGVyYXRpdm8uDQoNCi0gICBgZ2dwbG90bHkocF9kaXNwZXJzYW8sIHRvb2x0aXAgPSAidGV4dCIpYDoNCg0KICAgIC0gICBgZ2dwbG90bHkoKWA6IMOJIGEgZnVuw6fDo28gZGEgYmlibGlvdGVjYSBgcGxvdGx5YCBxdWUgY29udmVydGUgdW0gb2JqZXRvIGBnZ3Bsb3RgIGVtIHVtIGdyw6FmaWNvIGludGVyYXRpdm8gKEhUTUwpLg0KDQogICAgLSAgIGBwX2Rpc3BlcnNhb2A6IE8gcHJpbWVpcm8gYXJndW1lbnRvIMOpIG8gb2JqZXRvIGRvIGdyw6FmaWNvIGBnZ3Bsb3RgIHF1ZSBxdWVyZW1vcyBjb252ZXJ0ZXIuDQoNCiAgICAtICAgYHRvb2x0aXAgPSAidGV4dCJgOiBFc3RlIMOpIG8gYXJndW1lbnRvIGNoYXZlLiBFbGUgZGl6IMOgIGZ1bsOnw6NvIGBnZ3Bsb3RseWAgcGFyYSB1c2FyIG8gbWFwZWFtZW50byBlc3TDqXRpY28gYHRleHRgIHF1ZSBkZWZpbmltb3MgZGVudHJvIGRlIGBhZXMoKWAgY29tbyBvIGNvbnRlw7pkbyBkYSBjYWl4YSBkZSBpbmZvcm1hw6fDo28gcXVlIGFwYXJlY2UgcXVhbmRvIG8gdXN1w6FyaW8gcGFzc2EgbyBtb3VzZSBzb2JyZSB1bSBwb250byBubyBncsOhZmljby4NCg0KIyMjIEdyw6FmaWNvIGRlIEJhcnJhcyBJbnRlcmF0aXZvIChCYXIgUGxvdCkNCg0KUXVhbCDDqSBvIHNhbMOhcmlvIG3DqWRpbyBwb3IgZ8OqbmVybz8gVW0gZ3LDoWZpY28gZGUgYmFycmFzIMOpIGlkZWFsIHBhcmEgcmVzcG9uZGVyIGEgZXNzYSBwZXJndW50YS4gUHJpbWVpcm8sIHByZWNpc2Ftb3MgcHJlcGFyYXIgb3MgZGFkb3MsIGNhbGN1bGFuZG8gYSBtw6lkaWEgZGUgc2Fsw6FyaW8gcGFyYSBob21lbnMgZSBtdWxoZXJlcy4NCg0KKipJbnRlcmF0aXZpZGFkZSoqOiBQYXNzZSBvIG1vdXNlIHNvYnJlIGFzIGJhcnJhcyBwYXJhIHZlciBvIHNhbMOhcmlvIG3DqWRpbyBleGF0byBwYXJhIGNhZGEgZ3J1cG8uDQoNCmBgYHtyIGdyYWZpY28tYmFycmFzfQ0KIyAxLiBQcmVwYXJhciBvcyBkYWRvczogYWdydXBhciBwb3IgZ8OqbmVybyBlIGNhbGN1bGFyIG8gc2Fsw6FyaW8gbcOpZGlvDQpzYWxhcmlvX21lZGlvX2dlbmVybyA8LSB3YWdlMSAlPiUNCiAgbXV0YXRlKGdlbmVyb19sYWJlbCA9IGZhY3RvcihmZW1hbGUsIGxhYmVscyA9IGMoIkhvbWVtIiwgIk11bGhlciIpKSkgJT4lDQogIGdyb3VwX2J5KGdlbmVyb19sYWJlbCkgJT4lDQogIHN1bW1hcmlzZShzYWxhcmlvX21lZGlvID0gbWVhbih3YWdlKSkNCg0KIyAyLiBDcmlhciBvIGdyw6FmaWNvIGRlIGJhcnJhcyBjb20gZ2dwbG90DQpwX2JhcnJhcyA8LSBnZ3Bsb3Qoc2FsYXJpb19tZWRpb19nZW5lcm8sIGFlcyh4ID0gZ2VuZXJvX2xhYmVsLCB5ID0gc2FsYXJpb19tZWRpbywgZmlsbCA9IGdlbmVyb19sYWJlbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZSgiU2Fsw6FyaW8gTcOpZGlvOiAkIiwgcm91bmQoc2FsYXJpb19tZWRpbywgMikpKSkgKw0KICBnZW9tX2NvbCgpICsgIyBnZW9tX2NvbCDDqSB1c2FkbyBxdWFuZG8gYSBhbHR1cmEgZGEgYmFycmEgasOhIMOpIHVtIHZhbG9yIG5vIGRhdGFzZXQNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiSG9tZW0iID0gIiMwMjAyM0MiLCAiTXVsaGVyIiA9ICIjRkVCNzEyIikpICsNCiAgbGFicyh0aXRsZSA9ICJTYWzDoXJpbyBNw6lkaW8gcG9yIEhvcmEgcG9yIEfDqm5lcm8iLA0KICAgICAgIHggPSAiR8OqbmVybyIsDQogICAgICAgeSA9ICJTYWzDoXJpbyBNw6lkaW8gcG9yIEhvcmEgKFVTRCkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgIyBSZW1vdmUgYSBsZWdlbmRhLCBwb2lzIGEgaW5mb3JtYcOnw6NvIGrDoSBlc3TDoSBubyBlaXhvIHgNCg0KIyAzLiBUcmFuc2Zvcm1hciBlbSBpbnRlcmF0aXZvDQpnZ3Bsb3RseShwX2JhcnJhcywgdG9vbHRpcCA9ICJ0ZXh0IikNCmBgYA0KDQpFeHBsaWNhw6fDo28gZG8gY8OzZGlnbzoNCg0KKioxLiBQcmVwYXJhw6fDo28gZSBTdW1hcml6YcOnw6NvIGRvcyBEYWRvcyBjb20gYGRwbHlyYCoqDQoNCkVzdGEgc2XDp8OjbyB1c2EgbyBvcGVyYWRvciAicGlwZSIgKGAlPiVgKSBkYSBiaWJsaW90ZWNhIGBkcGx5cmAgcGFyYSBjcmlhciB1bSBub3ZvIGRhdGFmcmFtZSByZXN1bWlkbyBjaGFtYWRvIGBzYWxhcmlvX21lZGlvX2dlbmVyb2AuIE8gcGlwZSBwZWdhIG8gcmVzdWx0YWRvIGRvIGNvbWFuZG8gw6Agc3VhIGVzcXVlcmRhIGUgbyB1c2EgY29tbyBvIHByaW1laXJvIGFyZ3VtZW50byBkbyBjb21hbmRvIMOgIHN1YSBkaXJlaXRhLCB0b3JuYW5kbyBvIGPDs2RpZ28gbWFpcyBsZWfDrXZlbC4NCg0KLSAgIGBzYWxhcmlvX21lZGlvX2dlbmVybyA8LSB3YWdlMSAlPiUgLi4uYDogSW5pY2lhIGEgc2VxdcOqbmNpYSBkZSBvcGVyYcOnw7VlcyBjb20gbyBkYXRhZnJhbWUgYHdhZ2UxYCBlIGF0cmlidWkgbyByZXN1bHRhZG8gZmluYWwgw6AgdmFyacOhdmVsIGBzYWxhcmlvX21lZGlvX2dlbmVyb2AuDQoNCi0gICBgbXV0YXRlKGdlbmVyb19sYWJlbCA9IC4uLilgOiBBIGZ1bsOnw6NvIGBtdXRhdGVgIGNyaWEgdW1hIG5vdmEgY29sdW5hIG5vIGRhdGFmcmFtZS4NCg0KICAgIC0gICBgZ2VuZXJvX2xhYmVsID0gZmFjdG9yKGZlbWFsZSwgbGFiZWxzID0gYygiSG9tZW0iLCAiTXVsaGVyIikpYDogQ3JpYSB1bWEgY29sdW5hIGNoYW1hZGEgYGdlbmVyb19sYWJlbGAuIEFzc2ltIGNvbW8gbm8gZXhlbXBsbyBhbnRlcmlvciwgZWxhIGNvbnZlcnRlIGEgdmFyacOhdmVsIG51bcOpcmljYSBgZmVtYWxlYCAoY29tIHZhbG9yZXMgMCBlIDEpIGVtIHVtYSB2YXJpw6F2ZWwgY2F0ZWfDs3JpY2EgY29tIG9zIHLDs3R1bG9zICJIb21lbSIgZSAiTXVsaGVyIi4NCg0KLSAgIGBncm91cF9ieShnZW5lcm9fbGFiZWwpYDogQWdydXBhIG8gZGF0YWZyYW1lIHBlbGEgbm92YSBjb2x1bmEgYGdlbmVyb19sYWJlbGAuIEEgcGFydGlyIGRlc3RlIHBvbnRvLCBxdWFscXVlciBvcGVyYcOnw6NvIHN1YnNlcXVlbnRlIHNlcsOhIHJlYWxpemFkYSBzZXBhcmFkYW1lbnRlIHBhcmEgY2FkYSBncnVwbyAocGFyYSB0b2RvcyBvcyAiSG9tZW0iIGUgcGFyYSB0b2RhcyBhcyAiTXVsaGVyIikuDQoNCi0gICBgc3VtbWFyaXNlKHNhbGFyaW9fbWVkaW8gPSBtZWFuKHdhZ2UpKWA6IEEgZnVuw6fDo28gYHN1bW1hcmlzZWAgKG91IGBzdW1tYXJpemVgKSBjb2xhcHNhIG9zIGRhZG9zIGFncnVwYWRvcyBlbSB1bWEgw7puaWNhIGxpbmhhIHBvciBncnVwbywgY3JpYW5kbyBjb2x1bmFzIGRlIHJlc3Vtby4NCg0KICAgIC0gICBgc2FsYXJpb19tZWRpbyA9IG1lYW4od2FnZSlgOiBDcmlhIHVtYSBub3ZhIGNvbHVuYSBjaGFtYWRhIGBzYWxhcmlvX21lZGlvYCBxdWUgY29udMOpbSBhIG3DqWRpYSAoYG1lYW5gKSBkYSBjb2x1bmEgYHdhZ2VgIChzYWzDoXJpbykgcGFyYSBjYWRhIHVtIGRvcyBncnVwb3MgZGVmaW5pZG9zIGFudGVyaW9ybWVudGUuDQoNCk8gcmVzdWx0YWRvIGZpbmFsIMOpIHVtIHBlcXVlbm8gZGF0YWZyYW1lIGNvbSBkdWFzIGxpbmhhcyBlIGR1YXMgY29sdW5hczogYGdlbmVyb19sYWJlbGAgZSBgc2FsYXJpb19tZWRpb2AuDQoNCioqMi4gQ3JpYcOnw6NvIGRvIEdyw6FmaWNvIGRlIEJhcnJhcyBFc3TDoXRpY28gY29tIGBnZ3Bsb3QyYCoqDQoNCkVzdGUgYmxvY28gdXNhIG8gZGF0YWZyYW1lIHJlY8OpbS1jcmlhZG8gcGFyYSBnZXJhciB1bSBncsOhZmljbyBkZSBiYXJyYXMuDQoNCi0gICBgcF9iYXJyYXMgPC0gZ2dwbG90KC4uLilgOiBJbmljaWEgYSBjcmlhw6fDo28gZG8gb2JqZXRvIGRlIGdyw6FmaWNvIGUgbyBhdHJpYnVpIMOgIHZhcmnDoXZlbCBgcF9iYXJyYXNgLg0KDQotICAgYGdncGxvdChzYWxhcmlvX21lZGlvX2dlbmVybywgYWVzKHggPSBnZW5lcm9fbGFiZWwsIC4uLikpYDogVXNhIG8gZGF0YWZyYW1lIGBzYWxhcmlvX21lZGlvX2dlbmVyb2AgY29tbyBmb250ZS4NCg0KICAgIC0gICBgYWVzKHggPSBnZW5lcm9fbGFiZWwsIHkgPSBzYWxhcmlvX21lZGlvLCBmaWxsID0gZ2VuZXJvX2xhYmVsLCB0ZXh0ID0gLi4uKWA6IE1hcGVpYSBhcyB2YXJpw6F2ZWlzIMOgcyBlc3TDqXRpY2FzIGRvIGdyw6FmaWNvLg0KDQogICAgICAgIC0gICBgeCA9IGdlbmVyb19sYWJlbGA6IEFzIGNhdGVnb3JpYXMgbm8gZWl4byBYIHNlcsOjbyAiSG9tZW0iIGUgIk11bGhlciIuDQoNCiAgICAgICAgLSAgIGB5ID0gc2FsYXJpb19tZWRpb2A6IEEgYWx0dXJhIGRhcyBiYXJyYXMgc2Vyw6EgZGV0ZXJtaW5hZGEgcGVsbyB2YWxvciBuYSBjb2x1bmEgYHNhbGFyaW9fbWVkaW9gLg0KDQogICAgICAgIC0gICBgZmlsbCA9IGdlbmVyb19sYWJlbGA6IEEgY29yIGRlIHByZWVuY2hpbWVudG8gKGBmaWxsYCkgZGUgY2FkYSBiYXJyYSBzZXLDoSBkZXRlcm1pbmFkYSBwZWxhIGNhdGVnb3JpYSBkZSBnw6puZXJvLg0KDQogICAgICAgIC0gICBgdGV4dCA9IHBhc3RlKC4uLilgOiBOb3ZhbWVudGUsIGNyaWEgbyB0ZXh0byBwZXJzb25hbGl6YWRvIHBhcmEgbyB0b29sdGlwIGludGVyYXRpdm8uIFBvciBleGVtcGxvOiAiU2Fsw6FyaW8gTcOpZGlvOiBcJCA3LjA5Ii4NCg0KLSAgIGArIGdlb21fY29sKClgOiBBZGljaW9uYSBhIGNhbWFkYSBkZSBiYXJyYXMgYW8gZ3LDoWZpY28uIMOJIGltcG9ydGFudGUgbm90YXIgcXVlIHNlIHVzYSBgZ2VvbV9jb2woKWAgKGUgbsOjbyBgZ2VvbV9iYXIoKWApIHBvcnF1ZSBhIGFsdHVyYSBkYXMgYmFycmFzIChgeWApIGrDoSDDqSB1bSB2YWxvciBleHBsw61jaXRvIG5vIGRhdGFmcmFtZS4gYGdlb21fYmFyKClgIHNlcmlhIHVzYWRvIHNlIHF1aXPDqXNzZW1vcyBxdWUgbyBwcsOzcHJpbyBnZ3Bsb3QgY29udGFzc2UgbyBuw7ptZXJvIGRlIG9jb3Jyw6puY2lhcyBkZSBjYWRhIGNhdGVnb3JpYS4NCg0KLSAgIGArIHNjYWxlX2ZpbGxfbWFudWFsKC4uLilgOiBQZXJzb25hbGl6YSBhcyBjb3JlcyBkZSBwcmVlbmNoaW1lbnRvIGRhcyBiYXJyYXMuDQoNCiAgICAtICAgYHZhbHVlcyA9IGMoLi4uKWA6IEF0cmlidWkgYXMgbWVzbWFzIGNvcmVzIGRvIGV4ZW1wbG8gYW50ZXJpb3IgKGF6dWwgZXNjdXJvIGUgYW1hcmVsbykgw6BzIGNhdGVnb3JpYXMgIkhvbWVtIiBlICJNdWxoZXIiLg0KDQotICAgYCsgbGFicyguLi4pYDogRGVmaW5lIG9zIHTDrXR1bG9zIGUgcsOzdHVsb3MgZG8gZ3LDoWZpY28uDQoNCi0gICBgKyB0aGVtZV9taW5pbWFsKClgOiBBcGxpY2EgbyB0ZW1hIHZpc3VhbCBtaW5pbWFsaXN0YS4NCg0KLSAgIGArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIilgOiBNb2RpZmljYSBvIHRlbWEgcGFyYSByZW1vdmVyIGEgbGVnZW5kYSBkZSBjb3Jlcy4gSXNzbyDDqSBmZWl0byBwb3JxdWUgYSBpbmZvcm1hw6fDo28gZGUgY29yIGrDoSBlc3TDoSByZXByZXNlbnRhZGEgZGUgZm9ybWEgY2xhcmEgbm8gZWl4byBYLCB0b3JuYW5kbyBhIGxlZ2VuZGEgcmVkdW5kYW50ZS4NCg0KKiozLiBUcmFuc2Zvcm1hw6fDo28gZW0gR3LDoWZpY28gSW50ZXJhdGl2byBjb20gYHBsb3RseWAqKg0KDQpFc3RhIGxpbmhhIGZpbmFsIGNvbnZlcnRlIG8gZ3LDoWZpY28gZXN0w6F0aWNvIGVtIHVtYSB2aXN1YWxpemHDp8OjbyBpbnRlcmF0aXZhLg0KDQotICAgYGdncGxvdGx5KHBfYmFycmFzLCB0b29sdGlwID0gInRleHQiKWA6DQoNCiAgICAtICAgYGdncGxvdGx5KClgOiBDb252ZXJ0ZSBvIG9iamV0byBgZ2dwbG90YCAoYHBfYmFycmFzYCkgZW0gdW0gZ3LDoWZpY28gaW50ZXJhdGl2by4NCg0KICAgIC0gICBgdG9vbHRpcCA9ICJ0ZXh0ImA6IEluc3RydWkgbyBgcGxvdGx5YCBhIHVzYXIgYSBzdHJpbmcgZGUgdGV4dG8gcXVlIGNyaWFtb3MgbmEgZXN0w6l0aWNhIGB0ZXh0YCBwYXJhIGV4aWJpciB1bWEgY2FpeGEgZGUgaW5mb3JtYcOnw6NvIHBlcnNvbmFsaXphZGEgcXVhbmRvIG8gdXN1w6FyaW8gcGFzc2FyIG8gbW91c2Ugc29icmUgYXMgYmFycmFzIGRvIGdyw6FmaWNvLg0KDQojIyMgR3LDoWZpY28gZGUgQmFycmFzIEVtcGlsaGFkYXMgSW50ZXJhdGl2byAoU3RhY2tlZCBCYXIgUGxvdCkNCg0KQWdvcmEsIHZhbW9zIGFuYWxpc2FyIGEgY29tcG9zacOnw6NvIGRvIGVzdGFkbyBjaXZpbCAoYG1hcnJpZWRgKSBkZW50cm8gZGUgY2FkYSBnw6puZXJvLiBVbSBncsOhZmljbyBkZSBiYXJyYXMgZW1waWxoYWRhcyBub3MgbW9zdHJhcsOhIGEgY29udGFnZW0gZGUgcGVzc29hcyBjYXNhZGFzIGUgbsOjbyBjYXNhZGFzIHBhcmEgaG9tZW5zIGUgbXVsaGVyZXMuDQoNCioqSW50ZXJhdGl2aWRhZGUqKjogUGFzc2UgbyBtb3VzZSBzb2JyZSBjYWRhIHNlZ21lbnRvIGRhIGJhcnJhIHBhcmEgdmVyIGEgY29udGFnZW0gZXhhdGEgZGFxdWVsZSBncnVwbyBlc3BlY8OtZmljbyAoZXg6IG11bGhlcmVzIGNhc2FkYXMsIGhvbWVucyBuw6NvIGNhc2Fkb3MpLg0KDQpgYGB7ciBncmFmaWNvLWVtcGlsaGFkb30NCiMgLS0tIDEuIE9ww6fDtWVzIGRlIENvcmVzIFBlcnNvbmFsaXphZGFzIChGw6FjaWwgZGUgTW9kaWZpY2FyKSAtLS0NCiMgQ3JpZSB1bSB2ZXRvciBub21lYWRvIHBhcmEgYXMgY29yZXMuIE11ZGFyIGFzIGNvcmVzIGFxdWkgw6kgc2ltcGxlcyBlIGRpcmV0by4NCiMgTyBub21lICgiTsOjbyBDYXNhZG8oYSkiKSBkZXZlIGNvcnJlc3BvbmRlciBleGF0YW1lbnRlIMOgIGV0aXF1ZXRhIGRvIGZhdG9yLg0KY29yZXNfZXN0YWRvX2NpdmlsIDwtIGMoDQogICJOw6NvIENhc2FkbyhhKSIgPSAiIzAyMDIzQyIsICMgVW1hIGNvciBwYXJhIG9zIG7Do28gY2FzYWRvcw0KICAiQ2FzYWRvKGEpIiAgICAgPSAiI0ZFQjcxMiIgICMgVW1hIGNvciBwYXJhIG9zIGNhc2Fkb3MNCikNCg0KDQojIC0tLSAyLiBDcmlhw6fDo28gZG8gR3LDoWZpY28gZ2dwbG90IC0tLQ0KcF9lbXBpbGhhZG8gPC0gZ2dwbG90KHdhZ2UxLCBhZXMoDQogICAgeCA9IGZhY3RvcihmZW1hbGUsIGxhYmVscyA9IGMoIkhvbWVtIiwgIk11bGhlciIpKSwNCiAgICBmaWxsID0gZmFjdG9yKG1hcnJpZWQsIGxhYmVscyA9IGMoIk7Do28gQ2FzYWRvKGEpIiwgIkNhc2FkbyhhKSIpKSwNCiAgICANCiAgICAjIC0tLSAzLiBBZGljaW9uYW5kbyB1bSAiQm9tIFRvb2x0aXAiIC0tLQ0KICAgICMgQSBlc3TDqXRpY2EgJ3RleHQnIGRlZmluZSBvIGNvbnRlw7pkbyBkbyB0b29sdGlwLiBVc2Ftb3MgcGFzdGUoKSBwYXJhIGp1bnRhciBvIHRleHRvLg0KICAgICMgPGJyPiDDqSB1bWEgdGFnIEhUTUwgcGFyYSBjcmlhciB1bWEgbm92YSBsaW5oYSBubyB0b29sdGlwLg0KICAgIHRleHQgPSBwYXN0ZSgiR8OqbmVybzoiLCBmYWN0b3IoZmVtYWxlLCBsYWJlbHMgPSBjKCJIb21lbSIsICJNdWxoZXIiKSksDQogICAgICAgICAgICAgICAgICI8YnI+RXN0YWRvIENpdmlsOiIsIGZhY3RvcihtYXJyaWVkLCBsYWJlbHMgPSBjKCJOw6NvIENhc2FkbyhhKSIsICJDYXNhZG8oYSkiKSkpDQogICkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siKSArDQogIA0KICAjIFVzYW5kbyBhcyBjb3JlcyBxdWUgZGVmaW5pbW9zIGFudGVyaW9ybWVudGUNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29yZXNfZXN0YWRvX2NpdmlsKSArDQogIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIkRpc3RyaWJ1acOnw6NvIGRvIEVzdGFkbyBDaXZpbCBwb3IgR8OqbmVybyIsDQogICAgeCA9ICJHw6puZXJvIiwNCiAgICB5ID0gIkNvbnRhZ2VtIGRlIEluZGl2w61kdW9zIiwNCiAgICBmaWxsID0gIkVzdGFkbyBDaXZpbCIgIyBDb250cm9sYSBvIHTDrXR1bG8gZGEgbGVnZW5kYQ0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICMgQ2VudHJhbGl6YSBvIHTDrXR1bG8NCg0KDQojIC0tLSA0LiBUcmFuc2Zvcm1hciBlbSBJbnRlcmF0aXZvIGNvbSBvIFRvb2x0aXAgUGVyc29uYWxpemFkbyAtLS0NCiMgZ2dwbG90bHkgdmFpIHVzYXIgYSBlc3TDqXRpY2EgJ3RleHQnIHF1ZSBkZWZpbmltb3MgcGFyYSBjcmlhciBvIHRvb2x0aXAuDQojIEVsZSDDqSBpbnRlbGlnZW50ZSBvIHN1ZmljaWVudGUgcGFyYSBhZGljaW9uYXIgYSBjb250YWdlbSBhdXRvbWF0aWNhbWVudGUuDQpnZ3Bsb3RseShwX2VtcGlsaGFkbywgdG9vbHRpcCA9IGMoInRleHQiLCAieSIpKQ0KYGBgDQoNCkV4cGxpY2HDp8OjbyBkbyBncsOhZmljbzoNCg0KKioxLiBDcmlhw6fDo28gZG8gR3LDoWZpY28gRXN0w6F0aWNvIGNvbSBgZ2dwbG90MmAqKg0KDQpDb25zdHJ1w6fDo28gZG8gZ3LDoWZpY28gdmlzdWFsIHVzYW5kbyBhIGJpYmxpb3RlY2EgYGdncGxvdDJgLg0KDQotICAgYGNvcmVzX2VzdGFkb19jaXZpbCA8LSBjKC4uLilgOiBDcmlhIHVtICJ2ZXRvciBub21lYWRvIiBlbSBSIGNoYW1hZG8gYGNvcmVzX2VzdGFkb19jaXZpbGAuIFBlbnNlIG5pc3NvIGNvbW8gdW0gcGVxdWVubyBkaWNpb27DoXJpbyBvdSB1bWEgbGlzdGEgZGUgcGFyZXMgY2hhdmUtdmFsb3IuDQoNCiAgICAtICAgYCJOw6NvIENhc2FkbyhhKSIgPSAiIzAyMDIzQyJgOiBBc3NvY2lhIGEgc3RyaW5nIChvIHLDs3R1bG8pIGAiTsOjbyBDYXNhZG8oYSkiYCBhbyBjw7NkaWdvIGRlIGNvciBoZXhhZGVjaW1hbCBwYXJhIGF6dWwgZXNjdXJvLg0KDQogICAgLSAgIGAiQ2FzYWRvKGEpIiA9ICIjRkVCNzEyImA6IEFzc29jaWEgbyByw7N0dWxvIGAiQ2FzYWRvKGEpImAgYW8gY8OzZGlnbyBkZSBjb3IgcGFyYSBhbWFyZWxvL2xhcmFuamEuDQoNCjwhLS0gLS0+DQoNCi0gICBgcF9lbXBpbGhhZG8gPC0gZ2dwbG90KHdhZ2UxLCBhZXMoLi4uKSlgOiBJbmljaWEgbyBncsOhZmljbyB1c2FuZG8gbyBkYXRhZnJhbWUgYHdhZ2UxYCBlIG8gYXRyaWJ1aSBhbyBvYmpldG8gYHBfZW1waWxoYWRvYC4NCg0KLSAgIGBhZXMoLi4uKWA6IERlZmluZSBvIG1hcGVhbWVudG8gZGFzIHZhcmnDoXZlaXMgZG8gZGF0YWZyYW1lIHBhcmEgYXMgcHJvcHJpZWRhZGVzIHZpc3VhaXMgKCJlc3TDqXRpY2FzIikgZG8gZ3LDoWZpY28uDQoNCiAgICAtICAgYHggPSBmYWN0b3IoZmVtYWxlLCBsYWJlbHMgPSBjKCJIb21lbSIsICJNdWxoZXIiKSlgOiBNYXBlaWEgbyBlaXhvIFggw6AgdmFyacOhdmVsIGBmZW1hbGVgLCB0cmFuc2Zvcm1hbmRvLWEgZW0gdW0gZmF0b3IgY29tIG9zIHLDs3R1bG9zICJIb21lbSIgZSAiTXVsaGVyIi4gSXNzbyBjcmlhcsOhIGR1YXMgYmFycmFzIHByaW5jaXBhaXMgbm8gZ3LDoWZpY28uDQoNCiAgICAtICAgYGZpbGwgPSBmYWN0b3IobWFycmllZCwgbGFiZWxzID0gYygiTsOjbyBDYXNhZG8oYSkiLCAiQ2FzYWRvKGEpIikpYDogRXN0YSDDqSBhIGVzdMOpdGljYSBjaGF2ZSBwYXJhIG8gZW1waWxoYW1lbnRvLiBFbGEgZGV0ZXJtaW5hIHF1ZSBhIGNvciBkZSAqKnByZWVuY2hpbWVudG8qKiAoYGZpbGxgKSBkYXMgYmFycmFzIHNlcsOhIGJhc2VhZGEgbmEgdmFyacOhdmVsIGBtYXJyaWVkYCAoZXN0YWRvIGNpdmlsKS4gTyBgZ2dwbG90YCBpcsOhIGRpdmlkaXIgY2FkYSBiYXJyYSBkbyBlaXhvIFggZW0gc2VnbWVudG9zIGNvbG9yaWRvcywgdW0gcGFyYSBjYWRhIGVzdGFkbyBjaXZpbC4NCg0KICAgIC0gICBgdGV4dCA9IHBhc3RlKC4uLilgOiBDcmlhIGEgc3RyaW5nIGRlIHRleHRvIHBlcnNvbmFsaXphZGEgcGFyYSBvIHRvb2x0aXAgaW50ZXJhdGl2by4gQSBmdW7Dp8OjbyBgcGFzdGUoKWAganVudGEgb3MgdGV4dG9zIGUgdmFyacOhdmVpcy4gUG9yIGV4ZW1wbG8sIHBhcmEgdW0gcG9udG8gZGUgZGFkb3MgZGUgdW1hIG11bGhlciBjYXNhZGEsIG8gdGV4dG8gc2VyaWE6IGAiR8OqbmVybzogTXVsaGVyPGJyPkVzdGFkbyBDaXZpbDogQ2FzYWRvKGEpImAuIEEgdGFnIGA8YnI+YCBnYXJhbnRlIHVtYSBxdWVicmEgZGUgbGluaGEgbm8gdG9vbHRpcC4NCg0KLSAgIGArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIilgOiBBZGljaW9uYSBhIGNhbWFkYSBkZSBnZW9tZXRyaWEgcXVlIGRlc2VuaGEgYXMgYmFycmFzLg0KDQogICAgLSAgIGBnZW9tX2JhcigpYDogQ29tbyBlc3RhbW9zIGZvcm5lY2VuZG8gYXBlbmFzIHVtYSB2YXJpw6F2ZWwgYHhgIChlIHVtYSBgZmlsbGApLCBlc3RhIGZ1bsOnw6NvIGF1dG9tYXRpY2FtZW50ZSBjb250YSBvIG7Dum1lcm8gZGUgb2JzZXJ2YcOnw7VlcyBwYXJhIGNhZGEgY29tYmluYcOnw6NvIGRlIGB4YCBlIGBmaWxsYC4gTyByZXN1bHRhZG8gZGVzc2EgY29udGFnZW0gw6kgbWFwZWFkbyBwYXJhIGEgYWx0dXJhIG5vIGVpeG8gWS4NCg0KICAgIC0gICBgcG9zaXRpb24gPSAic3RhY2siYDogRXNwZWNpZmljYSBxdWUgb3Mgc2VnbWVudG9zIGRlZmluaWRvcyBwZWxhIGVzdMOpdGljYSBgZmlsbGAgZGV2ZW0gc2VyIGVtcGlsaGFkb3MgdW5zIHNvYnJlIG9zIG91dHJvcy4NCg0KLSAgIGArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvcmVzX2VzdGFkb19jaXZpbClgOiBQZXJzb25hbGl6YSBhcyBjb3JlcyBkZSBwcmVlbmNoaW1lbnRvLiBFbGUgaW5zdHJ1aSBvIGBnZ3Bsb3RgIGEgdXNhciBhcyBjb3JlcyBxdWUgZGVmaW5pbW9zIG5vIG5vc3NvIHZldG9yIGBjb3Jlc19lc3RhZG9fY2l2aWxgLiBBIGNvcnJlc3BvbmTDqm5jaWEgw6kgZmVpdGEgcGVsb3Mgbm9tZXMgKCJDYXNhZG8oYSkiLCAiTsOjbyBDYXNhZG8oYSkiKS4NCg0KLSAgIGArIGxhYnMoLi4uKWA6IENvbmZpZ3VyYSBvcyB0ZXh0b3MgZGUgcsOzdHVsb3MgZSB0w610dWxvcyBkbyBncsOhZmljby4NCg0KICAgIC0gICBgZmlsbCA9ICJFc3RhZG8gQ2l2aWwiYDogVW0gZGV0YWxoZSBpbXBvcnRhbnRlIGFxdWkgw6kgcXVlIGBmaWxsYCBkZW50cm8gZGUgYGxhYnMoKWAgZGVmaW5lIG8gdMOtdHVsbyBkYSAqKmxlZ2VuZGEqKiBhc3NvY2lhZGEgw6AgZXN0w6l0aWNhIGRlIHByZWVuY2hpbWVudG8uDQoNCi0gICBgKyB0aGVtZV9taW5pbWFsKClgOiBBcGxpY2EgbyB0ZW1hIHZpc3VhbCBsaW1wby4NCg0KLSAgIGArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKWA6IEZheiB1bSBhanVzdGUgZmlubyBubyB0ZW1hLg0KDQogICAgLSAgIGBwbG90LnRpdGxlYDogU2VsZWNpb25hIG8gZWxlbWVudG8gZG8gdMOtdHVsbyBkbyBncsOhZmljbyBwYXJhIG1vZGlmaWNhw6fDo28uDQoNCiAgICAtICAgYGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSlgOiBgaGp1c3RgIHNpZ25pZmljYSAianVzdGlmaWNhw6fDo28gaG9yaXpvbnRhbCIuIFVtIHZhbG9yIGRlIGAwLjVgIChvdSBzZWphLCA1MCUpIGNlbnRyYWxpemEgbyB0ZXh0by4gYDBgIGFsaW5oYSDDoCBlc3F1ZXJkYSBlIGAxYCDDoCBkaXJlaXRhLg0KDQoqKjIuIFRyYW5zZm9ybWHDp8OjbyBlbSBHcsOhZmljbyBJbnRlcmF0aXZvIGNvbSBgcGxvdGx5YCoqDQoNCkEgbGluaGEgZmluYWwgZMOhIHZpZGEgYW8gZ3LDoWZpY28gZXN0w6F0aWNvLg0KDQotICAgYGdncGxvdGx5KHBfZW1waWxoYWRvLCB0b29sdGlwID0gYygidGV4dCIsICJ5IikpYDoNCg0KICAgIC0gICBgZ2dwbG90bHkoKWA6IENvbnZlcnRlIG8gb2JqZXRvIGBnZ3Bsb3RgIChgcF9lbXBpbGhhZG9gKSBlbSB1bWEgdmlzdWFsaXphw6fDo28gSFRNTCBpbnRlcmF0aXZhLg0KDQogICAgLSAgIGB0b29sdGlwID0gYygidGV4dCIsICJ5IilgOiBFc3RlIGFyZ3VtZW50byBjb250cm9sYSBvIHF1ZSDDqSBleGliaWRvIG5hIGNhaXhhIGRlIGluZm9ybWHDp8OjbyAodG9vbHRpcCkgYW8gcGFzc2FyIG8gbW91c2UuIEFvIGZvcm5lY2VyIHVtIHZldG9yIGNvbSBgYygidGV4dCIsICJ5IilgLCB2b2PDqiBlc3TDoSBwZWRpbmRvIHBhcmEgbyBgcGxvdGx5YCBtb3N0cmFyIGR1YXMgaW5mb3JtYcOnw7VlczoNCg0KICAgICAgICAtICAgYCJ0ZXh0ImA6IE8gY29udGXDumRvIGRhIHN1YSBlc3TDqXRpY2EgYHRleHRgIHBlcnNvbmFsaXphZGEuDQoNCiAgICAgICAgLSAgIGAieSJgOiBPIHZhbG9yIGRvIGVpeG8gWSBwYXJhIGFxdWVsZSBzZWdtZW50byBkYSBiYXJyYSAobmVzdGUgY2FzbywgYSBjb250YWdlbSBkZSBpbmRpdsOtZHVvcykuDQoNCiMgQ29tbyBGYXplciBHcsOhZmljb3MgSW50ZXJhdGl2b3MgQ29tcGxleG9zIGNvbSBIaWdoY2hhcnQNCg0KIyMgQ2FycmVnYW1lbnRvIGUgUHJlcGFyYcOnw6NvIGRvcyBEYWRvcw0KDQpQcmltZWlybywgY2FycmVnYW1vcyBlIHByZXBhcmFtb3MgbyBkYXRhc2V0IGBpbnRkZWZgIHBhcmEgYSB2aXN1YWxpemHDp8OjbywgY3JpYW5kbyB1bWEgY29sdW5hIGRlIGRhdGEgZSBhcyBjb2x1bmFzIG5lY2Vzc8OhcmlhcyBwYXJhIG8gc29tYnJlYW1lbnRvIGVudHJlIGFzIHPDqXJpZXMuDQoNCmBgYHtyIGRhdGEtcHJlcGFyYXRpb259DQojIENhcnJlZ2FyIG9zIGRhZG9zDQpkYXRhKCJpbnRkZWYiKQ0KDQojIFByZXBhcmFyIG8gZGF0YWZyYW1lIHBhcmEgbyBoaWdoY2hhcnRlcg0KZGZfYW5hbGlzZSA8LSBpbnRkZWYgJT4lDQogICMgU2VsZWNpb25hciBlIHJlbm9tZWFyIHZhcmnDoXZlaXMgcGFyYSBtYWlvciBjbGFyZXphDQogIHNlbGVjdChhbm8gPSB5ZWFyLCBqdXJvcyA9IGkzLCBpbmZsYWNhbyA9IGluZikgJT4lDQogICMgQ3JpYXIgdW1hIGNvbHVuYSBkZSBkYXRhIGZvcm1hbA0KICBtdXRhdGUoZGF0YSA9IGFzLkRhdGUocGFzdGUwKGFubywgIi0wMS0wMSIpKSkgJT4lDQogICMgT3JkZW5hciBwb3IgZGF0YSwgdW1hIGJvYSBwcsOhdGljYSBwYXJhIHPDqXJpZXMgdGVtcG9yYWlzDQogIGFycmFuZ2UoZGF0YSkNCg0KIyBWaXN1YWxpemFyIG8gZGF0YWZyYW1lIHRyYW5zZm9ybWFkbw0KaGVhZChkZl9hbmFsaXNlKQ0KYGBgDQoNCiMjIENvbnN0cnXDp8OjbyBkbyBHcsOhZmljbyBJbnRlcmF0aXZvDQoNCkFnb3JhLCB2YW1vcyBjb25zdHJ1aXIgbm9zc28gZ3LDoWZpY28uIEFsw6ltIGRhcyBzw6lyaWVzIGRlIGRhZG9zIGUgZG9zIG1hcmNhZG9yZXMgZGUgZXZlbnRvcywgYWRpY2lvbmFyZW1vcyBhcyBmYWl4YXMgZGUgcmVjZXNzw6NvLg0KDQojIyMgRGFkb3MgcGFyYSBvcyBNYXJjYWRvcmVzIChGbGFncykNCg0KQ3JpYW1vcyB1bSBkYXRhZnJhbWUgcGFyYSBvcyBldmVudG9zIChjaG9xdWVzIGRvIHBldHLDs2xlbykgcXVlIHF1ZXJlbW9zIGRlc3RhY2FyLg0KDQpgYGB7ciBmbGFncy1kYXRhfQ0KZmxhZ3NfZGF0YSA8LSBkYXRhLmZyYW1lKA0KICBkYXRhID0gYXMuRGF0ZShjKCIxOTczLTEwLTAxIiwgIjE5NzktMDEtMDEiKSksDQogIHRpdGxlID0gYygiQzEiLCAiQzIiKSwNCiAgdGV4dCA9IGMoIlByaW1laXJvIENob3F1ZSBkbyBQZXRyw7NsZW8gKDE5NzMpIiwgIlNlZ3VuZG8gQ2hvcXVlIGRvIFBldHLDs2xlbyAoMTk3OSkiKQ0KKQ0KYGBgDQoNCkV4cGxpY2HDp8OjbyBkbyBjw7NkaWdvOg0KDQotICAgYGZsYWdzX2RhdGEgPC1gOiBJbmljaWEgYSBhdHJpYnVpw6fDo28uIE8gcmVzdWx0YWRvIGRvIGNvbWFuZG8gw6AgZGlyZWl0YSBzZXLDoSBhcm1hemVuYWRvIGVtIHVtIG5vdm8gb2JqZXRvICh1bWEgdmFyacOhdmVsKSBjaGFtYWRvIGBmbGFnc19kYXRhYC4NCg0KLSAgIGBkYXRhLmZyYW1lKC4uLilgOiBFc3RhIMOpIGEgZnVuw6fDo28gcHJpbmNpcGFsIGVtIFIgcGFyYSBjcmlhciB1bSBgZGF0YS5mcmFtZWAsIHF1ZSDDqSBhIGVzdHJ1dHVyYSBwYWRyw6NvIHBhcmEgYXJtYXplbmFyIGRhZG9zIGVtIGZvcm1hdG8gZGUgdGFiZWxhIChsaW5oYXMgZSBjb2x1bmFzKSwgc2ltaWxhciBhIHVtYSBwbGFuaWxoYS4gQ2FkYSBhcmd1bWVudG8gZGVudHJvIGRhIGZ1bsOnw6NvIGRlZmluZSB1bWEgY29sdW5hIGRhIHRhYmVsYS4NCg0KICAgIC0gICBgZGF0YSA9IGFzLkRhdGUoYygiMTk3My0xMC0wMSIsICIxOTc5LTAxLTAxIikpYDoNCg0KICAgICAgICAtICAgYGRhdGEgPSAuLi5gOiBDcmlhIHVtYSBjb2x1bmEgY2hhbWFkYSBgZGF0YWAuDQoNCiAgICAgICAgLSAgIGBjKCIxOTczLTEwLTAxIiwgIjE5NzktMDEtMDEiKWA6IENyaWEgdW0gdmV0b3IgKHVtYSBsaXN0YSkgZGUgc3RyaW5ncyBkZSB0ZXh0byBjb250ZW5kbyBkdWFzIGRhdGFzLg0KDQogICAgICAgIC0gICBgYXMuRGF0ZSguLi4pYDogRXN0YSDDqSB1bWEgZnVuw6fDo28gY3J1Y2lhbC4gRWxhIGNvbnZlcnRlIGFzIHN0cmluZ3MgZGUgdGV4dG8gZW0gdW0gZm9ybWF0byBlc3BlY2lhbCBkZSAqKkRhdGEqKiBxdWUgbyBSIGVudGVuZGUuIElzc28gw6kgZnVuZGFtZW50YWwgcGFyYSBxdWUgYmlibGlvdGVjYXMgZGUgZ3LDoWZpY29zIGNvbW8gYGdncGxvdDJgIG91IGBwbG90bHlgIHNhaWJhbSBwb3NpY2lvbmFyIGNvcnJldGFtZW50ZSBlc3NlcyBwb250b3MgZW0gdW0gZWl4byBkZSB0ZW1wby4NCg0KICAgIC0gICBgdGl0bGUgPSBjKCJDMSIsICJDMiIpYDoNCg0KICAgICAgICAtICAgYHRpdGxlID0gLi4uYDogQ3JpYSB1bWEgc2VndW5kYSBjb2x1bmEgY2hhbWFkYSBgdGl0bGVgLg0KDQogICAgICAgIC0gICBgYygiQzEiLCAiQzIiKWA6IENyaWEgdW0gdmV0b3IgZGUgdGV4dG8gY29tIHLDs3R1bG9zIGN1cnRvcy4gRW0gdW0gZ3LDoWZpY28sIGVzdGVzIHPDo28gZnJlcXVlbnRlbWVudGUgdXNhZG9zIGNvbW8gcGVxdWVub3MgdGV4dG9zIHF1ZSBhcGFyZWNlbSBkaXJldGFtZW50ZSBzb2JyZSBvdSBhbyBsYWRvIGRlIHVtIG1hcmNhZG9yIGRlIGV2ZW50by4NCg0KICAgIC0gICBgdGV4dCA9IGMoIlByaW1laXJvIENob3F1ZSBkbyBQZXRyw7NsZW8gKDE5NzMpIiwgIlNlZ3VuZG8gQ2hvcXVlIGRvIFBldHLDs2xlbyAoMTk3OSkiKWA6DQoNCiAgICAgICAgLSAgIGB0ZXh0ID0gLi4uYDogQ3JpYSB1bWEgdGVyY2VpcmEgY29sdW5hIGNoYW1hZGEgYHRleHRgLg0KDQogICAgICAgIC0gICBgYyguLi4pYDogQ3JpYSB1bSB2ZXRvciBkZSB0ZXh0byBjb20gZGVzY3Jpw6fDtWVzIG1haXMgbG9uZ2FzLiBFc3RlIHRleHRvIMOpIGlkZWFsIHBhcmEgc2VyIHVzYWRvIGNvbW8gbyBjb250ZcO6ZG8gZGEgY2FpeGEgZGUgaW5mb3JtYcOnw6NvICh0b29sdGlwKSBxdWUgYXBhcmVjZSBxdWFuZG8gdW0gdXN1w6FyaW8gcGFzc2EgbyBtb3VzZSBzb2JyZSBvIG1hcmNhZG9yIGRvIGV2ZW50byBlbSB1bSBncsOhZmljbyBpbnRlcmF0aXZvLg0KDQojIyMgRGFkb3MgcGFyYSBhcyBGYWl4YXMgZGUgUmVjZXNzw6NvIChQbG90IEJhbmRzKQ0KDQpQYXJhIGFkaWNpb25hciBhcyBmYWl4YXMgZGUgcmVjZXNzw6NvLCBwcmVjaXNhbW9zIGRlIHVtYSBsaXN0YSBjb250ZW5kbyBhcyBkYXRhcyBkZSBpbsOtY2lvIGUgZmltIGRlIGNhZGEgcGVyw61vZG8uIFV0aWxpemFyZW1vcyBhcyBkYXRhcyBvZmljaWFpcyBkbyAqTmF0aW9uYWwgQnVyZWF1IG9mIEVjb25vbWljIFJlc2VhcmNoIChOQkVSKSogcGFyYSBvcyBFVUEuIE8gYGhpZ2hjaGFydGVyYCBlc3BlcmEgcXVlIGFzIGRhdGFzIGRlIGluw61jaW8gKGBmcm9tYCkgZSBmaW0gKGB0b2ApIGVzdGVqYW0gZW0gZm9ybWF0byBkZSB0aW1lc3RhbXAgKG1pbGlzc2VndW5kb3MgZGVzZGUgMTk3MC0wMS0wMSksIG1hcyBlbGUgZmF6IGEgY29udmVyc8OjbyBhdXRvbWF0aWNhbWVudGUgYSBwYXJ0aXIgZGUgb2JqZXRvcyBgRGF0ZWAuDQoNCmBgYHtyIHJlY2Vzc2lvbi1kYXRhfQ0KIyBGdW7Dp8OjbyBwYXJhIGNvbnZlcnRlciBkYXRhIHBhcmEgdGltZXN0YW1wIGVtIG1pbGlzc2VndW5kb3MNCmRhdGVfdG9fdGltZXN0YW1wIDwtIGZ1bmN0aW9uKGRhdGVfc3RyaW5nKSB7DQogIGFzLm51bWVyaWMoYXMuUE9TSVhjdChkYXRlX3N0cmluZykpICogMTAwMA0KfQ0KDQojIExpc3RhIHF1ZSBkZWZpbmUgY2FkYSBmYWl4YSBkZSByZWNlc3PDo28gY29tIG8gZm9ybWF0byBkZSBkYXRhIGNvcnJpZ2lkbw0KcmVjZXNzaW9uX2JhbmRzIDwtIGxpc3QoDQogIGxpc3QoZnJvbSA9IGRhdGVfdG9fdGltZXN0YW1wKCIxOTQ4LTExLTAxIiksIHRvID0gZGF0ZV90b190aW1lc3RhbXAoIjE5NDktMTAtMDEiKSwgY29sb3IgPSAicmdiYSgxMDAsIDEwMCwgMTAwLCAwLjIpIiwgbGFiZWwgPSBsaXN0KHN0eWxlID0gbGlzdChjb2xvciA9ICJncmF5IikpKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjE5NTMtMDctMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMTk1NC0wNS0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjE5NTctMDgtMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMTk1OC0wNC0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjE5NjAtMDQtMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMTk2MS0wMi0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjE5NjktMTItMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMTk3MC0xMS0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjE5NzMtMTEtMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMTk3NS0wMy0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjE5ODAtMDEtMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMTk4MC0wNy0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjE5ODEtMDctMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMTk4Mi0xMS0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjE5OTAtMDctMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMTk5MS0wMy0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKSwNCiAgbGlzdChmcm9tID0gZGF0ZV90b190aW1lc3RhbXAoIjIwMDEtMDMtMDEiKSwgdG8gPSBkYXRlX3RvX3RpbWVzdGFtcCgiMjAwMS0xMS0wMSIpLCBjb2xvciA9ICJyZ2JhKDEwMCwgMTAwLCAxMDAsIDAuMikiKQ0KKQ0KYGBgDQoNCkV4cGxpY2HDp8OjbyBkbyBjw7NkaWdvOg0KDQoqKjEuIEZ1bsOnw6NvIHBhcmEgY29udmVyc8OjbyBkZSBkYXRhcy4qKg0KDQotICAgKipgZGF0ZV90b190aW1lc3RhbXAgPC0gZnVuY3Rpb24oZGF0ZV9zdHJpbmcpIHsgLi4uIH1gKio6IERlZmluZSB1bWEgbm92YSBmdW7Dp8OjbyBjaGFtYWRhIGBkYXRlX3RvX3RpbWVzdGFtcGAgcXVlIGFjZWl0YSB1bSBhcmd1bWVudG8sIGBkYXRlX3N0cmluZ2AgKHVtYSBkYXRhIGVtIGZvcm1hdG8gZGUgdGV4dG8pLg0KDQotICAgKipgYXMuUE9TSVhjdChkYXRlX3N0cmluZylgKio6IEVzdGEgw6kgYSBwcmltZWlyYSBldGFwYSBkYSBjb252ZXJzw6NvLiBFbGEgcGVnYSBhIHN0cmluZyBkZSB0ZXh0byAoZXg6IGAiMTk0OC0xMS0wMSJgKSBlIGEgY29udmVydGUgcGFyYSB1bSBmb3JtYXRvIGRlIGRhdGEvaG9yYSBxdWUgbyBSIGVudGVuZGUsIGNoYW1hZG8gYFBPU0lYY3RgLiBJbnRlcm5hbWVudGUsIGVzdGUgZm9ybWF0byBhcm1hemVuYSBhIGRhdGEgY29tbyBvICoqbsO6bWVybyBkZSBzZWd1bmRvcyoqIHBhc3NhZG9zIGRlc2RlIG8gaW7DrWNpbyBkYSAiZXJhIFVuaXgiICgwMSBkZSBqYW5laXJvIGRlIDE5NzApLg0KDQotICAgKipgYXMubnVtZXJpYyguLi4pYCoqOiBFeHRyYWkgYXBlbmFzIG8gdmFsb3IgbnVtw6lyaWNvIChvIG7Dum1lcm8gZGUgc2VndW5kb3MpIGRvIG9iamV0byBgUE9TSVhjdGAuDQoNCi0gICAqKmAqIDEwMDBgKio6IE11bHRpcGxpY2EgbyBuw7ptZXJvIGRlIHNlZ3VuZG9zIHBvciAxMDAwLiBJc3NvIGNvbnZlcnRlIG8gdmFsb3IgcGFyYSAqKm1pbGlzc2VndW5kb3MqKi4gRXNzYSBjb252ZXJzw6NvIMOpIG11aXRvIGNvbXVtIHBvcnF1ZSBiaWJsaW90ZWNhcyBkZSBncsOhZmljb3MgaW50ZXJhdGl2b3MgYmFzZWFkYXMgZW0gSmF2YVNjcmlwdCAoY29tbyBIaWdoY2hhcnRzLCBxdWUgw6kgdXNhZGEgcGVsbyBwYWNvdGUgYGhpZ2hjaGFydGVyYCBlbSBSKSBleGlnZW0gcXVlIG9zIHRlbXBvcyBzZWphbSBmb3JuZWNpZG9zIGVtIG1pbGlzc2VndW5kb3MuDQoNCkVtIHJlc3VtbywgYSBmdW7Dp8OjbyBwZWdhIHVtYSBkYXRhIGVtIHRleHRvIGUgYSB0cmFuc2Zvcm1hIGVtIHVtIG7Dum1lcm8gcXVlIHJlcHJlc2VudGEgZXNzYSBtZXNtYSBkYXRhIGVtIG1pbGlzc2VndW5kb3MsIHByb250YSBwYXJhIHNlciB1c2FkYSBwb3IgdW1hIGJpYmxpb3RlY2EgZGUgZ3LDoWZpY29zLg0KDQoqKjIuIEVzdHJ1dHVyYSBkZSBEYWRvczogYHJlY2Vzc2lvbl9iYW5kc2AqKg0KDQpFc3RhIHBhcnRlIGRvIGPDs2RpZ28gY3JpYSBvIG9iamV0byBwcmluY2lwYWwsIHVtYSBsaXN0YSBjaGFtYWRhIGByZWNlc3Npb25fYmFuZHNgLg0KDQotICAgKipgcmVjZXNzaW9uX2JhbmRzIDwtIGxpc3QoLi4uKWAqKjogQ3JpYSB1bWEgbGlzdGEuIEVtIFIsIHVtYSBsaXN0YSDDqSB1bWEgY29sZcOnw6NvIHF1ZSBwb2RlIGNvbnRlciBkaXZlcnNvcyB0aXBvcyBkZSBvYmpldG9zLCBpbmNsdXNpdmUgb3V0cmFzIGxpc3Rhcy4gSXNzbyBhIHRvcm5hIHBlcmZlaXRhIHBhcmEgYXJtYXplbmFyIGNvbmZpZ3VyYcOnw7VlcyBjb21wbGV4YXMuDQoNCi0gICAqKmBsaXN0KGZyb20gPSAuLi4sIHRvID0gLi4uLCBjb2xvciA9IC4uLilgKio6IEEgbGlzdGEgYHJlY2Vzc2lvbl9iYW5kc2AgY29udMOpbSB1bWEgc8OpcmllIGRlIG91dHJhcyBsaXN0YXMuIENhZGEgdW1hIGRlc3NhcyBsaXN0YXMgaW50ZXJuYXMgcmVwcmVzZW50YSAqKnVtYSDDum5pY2EgZmFpeGEgZGUgcmVjZXNzw6NvKiogZSBwb3NzdWkgb3Mgc2VndWludGVzIGVsZW1lbnRvcyBub21lYWRvczoNCg0KICAgIC0gICAqKmBmcm9tYCoqOiBEZWZpbmUgbyAqKmluw61jaW8qKiBkYSBmYWl4YSBkZSByZWNlc3PDo28uIE8gdmFsb3Igw6kgb2J0aWRvIGNoYW1hbmRvIGEgZnVuw6fDo28gYGRhdGVfdG9fdGltZXN0YW1wKClgIGNvbSBhIGRhdGEgZGUgaW7DrWNpby4NCg0KICAgIC0gICAqKmB0b2AqKjogRGVmaW5lIG8gKipmaW0qKiBkYSBmYWl4YSBkZSByZWNlc3PDo28sIHRhbWLDqW0gdXNhbmRvIGEgZnVuw6fDo28gcGFyYSBjb252ZXJ0ZXIgYSBkYXRhLg0KDQogICAgLSAgICoqYGNvbG9yID0gInJnYmEoMTAwLCAxMDAsIDEwMCwgMC4yKSJgKio6IERlZmluZSBhIGNvciBkYSBmYWl4YSBzb21icmVhZGEuIE8gZm9ybWF0byBgcmdiYWAgc2lnbmlmaWNhIFZlcm1lbGhvIChSZWQpLCBWZXJkZSAoR3JlZW4pLCBBenVsIChCbHVlKSBlIEFscGhhICh0cmFuc3BhcsOqbmNpYSkuIE5lc3RlIGNhc28sIGAoMTAwLCAxMDAsIDEwMClgIMOpIHVtIHRvbSBkZSBjaW56YSwgZSBgMC4yYCDDqSBvIG7DrXZlbCBkZSBhbHBoYSwgcXVlIHRvcm5hIGEgZmFpeGEgODAlIHRyYW5zcGFyZW50ZSwgcGVybWl0aW5kbyBxdWUgb3MgZGFkb3MgZG8gZ3LDoWZpY28gKGNvbW8gdW1hIGxpbmhhKSBzZWphbSB2aXN0b3MgYXRyYXbDqXMgZGVsYS4NCg0KICAgIC0gICAqKmBsYWJlbCA9IGxpc3QoLi4uKWAqKjogVW0gZWxlbWVudG8gb3BjaW9uYWwgcGFyYSBjb25maWd1cmFyIHVtIHLDs3R1bG8gZGUgdGV4dG8gcXVlIHBvZGUgc2VyIGV4aWJpZG8gc29icmUgYSBmYWl4YS4NCg0KIyMjIEPDs2RpZ28gZG8gR3LDoWZpY28gRmluYWwNCg0KSnVudGFtb3MgdG9kYXMgYXMgcGXDp2FzLCBpbmNsdWluZG8gYSBjaGFtYWRhIMOgIGZ1bsOnw6NvIGBoY194QXhpc2AgcGFyYSBhZGljaW9uYXIgYXMgYHBsb3RCYW5kc2AuDQoNCmBgYHtyIGludGVyYWN0aXZlLWNoYXJ0LXVwZGF0ZWQsIGZpZy5oZWlnaHQ9Nn0NCmhpZ2hjaGFydCh0eXBlID0gInN0b2NrIikgJT4lDQogICMgQWRpw6fDo28gZGFzIHPDqXJpZXMgZGUgZGFkb3MgKEp1cm9zIGUgSW5mbGHDp8OjbykNCiAgaGNfYWRkX3NlcmllcyhkYXRhID0gZGZfYW5hbGlzZSwgdHlwZSA9ICJsaW5lIiwgaGNhZXMoeCA9IGRhdGEsIHkgPSBqdXJvcyksIG5hbWUgPSAiVGF4YSBkZSBKdXJvcyAoaTMpIiwgaWQgPSAianVyb3MiLCBjb2xvciA9ICIjMDIwMjNDIikgJT4lDQogIGhjX2FkZF9zZXJpZXMoZGF0YSA9IGRmX2FuYWxpc2UsIHR5cGUgPSAibGluZSIsIGhjYWVzKHggPSBkYXRhLCB5ID0gaW5mbGFjYW8pLCBuYW1lID0gIkluZmxhw6fDo28gKGluZikiLCBjb2xvciA9ICIjRkVCNzEyIikgJT4lDQogIA0KICAjIEEgU8OJUklFICdhcmVhcmFuZ2UnIEZPSSBSRU1PVklEQSBERVNUQSBTRcOHw4NPDQogIA0KICAjIEFkacOnw6NvIGRvcyBtYXJjYWRvcmVzIGRlIGV2ZW50b3MgKGZsYWdzKQ0KICBoY19hZGRfc2VyaWVzKGRhdGEgPSBmbGFnc19kYXRhLCB0eXBlID0gImZsYWdzIiwgaGNhZXMoeCA9IGRhdGEsIHRpdGxlID0gdGl0bGUsIHRleHQgPSB0ZXh0KSwgb25TZXJpZXMgPSAianVyb3MiLCBuYW1lID0gIkV2ZW50b3MgSGlzdMOzcmljb3MiKSAlPiUNCiAgDQogICMgQWRpw6fDo28gZGFzIEZhaXhhcyBkZSBSZWNlc3PDo28gYW8gRWl4byBYDQogIGhjX3hBeGlzKA0KICAgIHBsb3RCYW5kcyA9IHJlY2Vzc2lvbl9iYW5kcw0KICApICU+JQ0KDQogICMgVMOtdHVsb3MgZSBsZWdlbmRhcw0KICBoY190aXRsZSh0ZXh0ID0gIlJlbGHDp8OjbyBlbnRyZSBUYXhhIGRlIEp1cm9zIGUgSW5mbGHDp8OjbyBub3MgRVVBICgxOTQ4LTIwMDMpIikgJT4lDQogIGhjX3N1YnRpdGxlKHRleHQgPSAiUGVyw61vZG9zIGRlIHJlY2Vzc8OjbyAoTkJFUikgaW5kaWNhZG9zIHBvciBmYWl4YXMgc29tYnJlYWRhcyIpICU+JQ0KICBoY19jcmVkaXRzKGVuYWJsZWQgPSBUUlVFLCB0ZXh0ID0gIkZvbnRlOiBXb29sZHJpZGdlLCBOQkVSIHwgR3LDoWZpY286IEhpZ2hjaGFydGVyIikgJT4lDQogIGhjX2xlZ2VuZChlbmFibGVkID0gVFJVRSkgJT4lIA0KICANCiAgI0Rpc3BvbmliaWxpemHDp8OjbyBkZSBkb3dubG9hZCBkbyBncsOhZmljbyBlIGRvcyBkYWRvcw0KICBoY19leHBvcnRpbmcoZW5hYmxlZCA9IFRSVUUpICU+JSANCiAgDQogICMgVG9vbHRpcCBjdXN0b21pemFkbw0KICBoY190b29sdGlwKA0KICAgIHNoYXJlZCA9IFRSVUUsIA0KICAgIGNyb3NzaGFpcnMgPSBUUlVFLA0KICAgIHZhbHVlRGVjaW1hbHMgPSAyLA0KICAgIHBvaW50Rm9ybWF0ID0gJzxzcGFuIHN0eWxlPSJjb2xvcjp7cG9pbnQuY29sb3J9Ij5cdTI1Q0Y8L3NwYW4+IHtzZXJpZXMubmFtZX06IDxiPntwb2ludC55fSAlPC9iPjxici8+Jw0KICApDQpgYGANCg0KRXhwbGljYcOnw6NvIGRvIGPDs2RpZ286DQoNCi0gICAqKmBoaWdoY2hhcnQodHlwZSA9ICJzdG9jayIpYCoqOg0KDQogICAgLSAgIEVzdGEgw6kgYSBmdW7Dp8OjbyBxdWUgaW5pY2lhbGl6YSBvIG9iamV0byBkbyBncsOhZmljby4NCg0KICAgIC0gICBgdHlwZSA9ICJzdG9jayJgOiBFc3BlY2lmaWNhIHF1ZSBxdWVyZW1vcyBjcmlhciB1bSAiZ3LDoWZpY28gZGUgYcOnw7VlcyIuIEVzdGUgdGlwbyBkZSBncsOhZmljbyDDqSBvdGltaXphZG8gcGFyYSBzw6lyaWVzIHRlbXBvcmFpcyBlIGluY2x1aSBhdXRvbWF0aWNhbWVudGUgZmVycmFtZW50YXMgw7p0ZWlzLCBjb21vIHVtIG5hdmVnYWRvciBkZSBpbnRlcnZhbG8gZGUgZGF0YXMgKG8gIm5hdmlnYXRvciIgbmEgcGFydGUgaW5mZXJpb3IpIGUgYm90w7VlcyBkZSB6b29tIHByZWRlZmluaWRvcyAoMW0sIDNtLCAxYSwgZXRjLikuDQoNCi0gICAqKmBoY19hZGRfc2VyaWVzKC4uLilgKio6DQoNCiAgICAtICAgRXN0YSBmdW7Dp8OjbyDDqSB1c2FkYSBwYXJhIGFkaWNpb25hciBjYW1hZGFzIGRlIGRhZG9zIChzw6lyaWVzKSBhbyBncsOhZmljby4gw4kgY2hhbWFkYSB0csOqcyB2ZXplcyBhcXVpOg0KDQogICAgMS4gICoqUHJpbWVpcmEgY2hhbWFkYSAoSnVyb3MpKio6DQoNCiAgICAgICAgLSAgIGBkYXRhID0gZGZfYW5hbGlzZSwgdHlwZSA9ICJsaW5lIiwgaGNhZXMoeCA9IGRhdGEsIHkgPSBqdXJvcylgOiBBZGljaW9uYSBvcyBkYWRvcyBkbyBkYXRhZnJhbWUgYGRmX2FuYWxpc2VgIGNvbW8gdW1hIGxpbmhhIChgdHlwZSA9ICJsaW5lImApLCBtYXBlYW5kbyBhIGNvbHVuYSBgZGF0YWAgcGFyYSBvIGVpeG8gWCBlIGBqdXJvc2AgcGFyYSBvIGVpeG8gWS4NCg0KICAgICAgICAtICAgYG5hbWUgPSAiLi4uImA6IERlZmluZSBvIG5vbWUgZGEgc8OpcmllIHF1ZSBhcGFyZWNlcsOhIG5hIGxlZ2VuZGEgZSBubyB0b29sdGlwLg0KDQogICAgICAgIC0gICBgaWQgPSAianVyb3MiYDogKipBdHJpYnVpIHVtIGlkZW50aWZpY2Fkb3Igw7puaWNvIGEgZXN0YSBzw6lyaWUqKi4gSXNzbyDDqSBjcnVjaWFsLCBwb2lzIHBlcm1pdGUgcXVlIG91dHJhcyBjYW1hZGFzIChjb21vIGFzICJmbGFncyIpIHNlIHJlZmlyYW0gYSBlc3RhIGxpbmhhIGVzcGVjw61maWNhLg0KDQogICAgICAgIC0gICBgY29sb3IgPSAiLi4uImA6IERlZmluZSBhIGNvciBkYSBsaW5oYS4NCg0KICAgIDIuICAqKlNlZ3VuZGEgY2hhbWFkYSAoSW5mbGHDp8OjbykqKjogRmF6IG8gbWVzbW8gcXVlIGEgYW50ZXJpb3IsIG1hcyBwYXJhIGEgY29sdW5hIGBpbmZsYWNhb2AsIGNvbSB1bWEgY29yIGRpZmVyZW50ZS4NCg0KICAgIDMuICAqKlRlcmNlaXJhIGNoYW1hZGEgKEZsYWdzL0V2ZW50b3MpKio6DQoNCiAgICAgICAgLSAgIGBkYXRhID0gZmxhZ3NfZGF0YWA6IFVzYSBvIGRhdGFmcmFtZSBgZmxhZ3NfZGF0YWAgcXVlIGNyaWFtb3MgYW50ZXJpb3JtZW50ZS4NCg0KICAgICAgICAtICAgYHR5cGUgPSAiZmxhZ3MiYDogRXNwZWNpZmljYSB1bSB0aXBvIGRlIHPDqXJpZSBlc3BlY2lhbCBwYXJhIG1hcmNhciBldmVudG9zIG5vIGdyw6FmaWNvLg0KDQogICAgICAgIC0gICBgaGNhZXMoeCA9IGRhdGEsIHRpdGxlID0gdGl0bGUsIHRleHQgPSB0ZXh0KWA6IE1hcGVpYSBhcyBjb2x1bmFzIGRvIGBmbGFnc19kYXRhYCBwYXJhIGFzIHByb3ByaWVkYWRlcyBkYXMgZmxhZ3M6IGB4YCBwYXJhIGEgcG9zacOnw6NvIG5vIHRlbXBvLCBgdGl0bGVgIHBhcmEgbyByw7N0dWxvIGN1cnRvIG5vIGdyw6FmaWNvIChgQzFgLCBgQzJgKSBlIGB0ZXh0YCBwYXJhIGEgZGVzY3Jpw6fDo28gbG9uZ2Egbm8gdG9vbHRpcC4NCg0KICAgICAgICAtICAgYG9uU2VyaWVzID0gImp1cm9zImA6ICoqSW5zdHJ1aSBvIEhpZ2hjaGFydGVyIGEgYW5jb3JhciBlc3NhcyBmbGFncyBkaXJldGFtZW50ZSBuYSBsaW5oYSBxdWUgdGVtIG8gYGlkID0gImp1cm9zImAqKi4gSXNzbyBmYXogY29tIHF1ZSBvcyBtYXJjYWRvcmVzIGFwYXJlw6dhbSBzb2JyZSBhIGxpbmhhIGRlIGp1cm9zLg0KDQotICAgKipgaGNfeEF4aXMocGxvdEJhbmRzID0gcmVjZXNzaW9uX2JhbmRzKWAqKjoNCg0KICAgIC0gICBFc3RhIGZ1bsOnw6NvIHBlcnNvbmFsaXphIG8gZWl4byBYLg0KDQogICAgLSAgIGBwbG90QmFuZHMgPSByZWNlc3Npb25fYmFuZHNgOiBFc3RlIMOpIG8gYXJndW1lbnRvIGNoYXZlIHF1ZSByZWNlYmUgYSBsaXN0YSBgcmVjZXNzaW9uX2JhbmRzYCBxdWUgZGVmaW5pbW9zIG5vIGPDs2RpZ28gYW50ZXJpb3IuIE8gSGlnaGNoYXJ0ZXIgdXNhIGVzc2EgbGlzdGEgcGFyYSBkZXNlbmhhciBhcyBmYWl4YXMgc29tYnJlYWRhcyBubyBmdW5kbyBkbyBncsOhZmljbywgaW5kaWNhbmRvIG9zIHBlcsOtb2RvcyBkZSByZWNlc3PDo28uDQoNCi0gICAqKmBoY190aXRsZSguLi4pYCwgYGhjX3N1YnRpdGxlKC4uLilgLCBgaGNfY3JlZGl0cyguLi4pYCwgYGhjX2xlZ2VuZCguLi4pYCoqOg0KDQogICAgLSAgIEVzdGUgYmxvY28gZGUgZnVuw6fDtWVzIGNvbmZpZ3VyYSBvcyBlbGVtZW50b3MgdGV4dHVhaXMgZSBkZSBtZXRhZGFkb3MgZG8gZ3LDoWZpY286IG8gdMOtdHVsbyBwcmluY2lwYWwsIG8gc3VidMOtdHVsbywgb3MgY3LDqWRpdG9zIChmb250ZSBkb3MgZGFkb3MpIG5vIGNhbnRvIGluZmVyaW9yIGUgZ2FyYW50ZSBxdWUgYSBsZWdlbmRhIGVzdGVqYSBoYWJpbGl0YWRhLg0KDQotICAgKipgaGNfZXhwb3J0aW5nKGVuYWJsZWQgPSBUUlVFKWAqKjoNCg0KICAgIC0gICBBZGljaW9uYSBvIG1lbnUgImhhbWLDunJndWVyIiAo4piwKSBubyBjYW50byBzdXBlcmlvciBkaXJlaXRvIGRvIGdyw6FmaWNvLCBxdWUgcGVybWl0ZSBhbyB1c3XDoXJpbyBmaW5hbCBiYWl4YXIgYSB2aXN1YWxpemHDp8OjbyBjb21vIHVtYSBpbWFnZW0gKFBORywgSlBFRywgU1ZHKSBvdSBleHBvcnRhciBvcyBkYWRvcyBicnV0b3MgKENTViwgWExTKS4NCg0KLSAgICoqYGhjX3Rvb2x0aXAoLi4uKWAqKjoNCg0KICAgIC0gICBQZXJzb25hbGl6YSBhIGNhaXhhIGRlIGluZm9ybWHDp8O1ZXMgKHRvb2x0aXApIHF1ZSBhcGFyZWNlIHF1YW5kbyBvIG1vdXNlIHBhc3NhIHNvYnJlIG8gZ3LDoWZpY28uDQoNCiAgICAtICAgYHNoYXJlZCA9IFRSVUVgOiBGYXogY29tIHF1ZSB1bWEgw7puaWNhIGNhaXhhIGRlIGluZm9ybWHDp8O1ZXMgYXBhcmXDp2EgcGFyYSB0b2RhcyBhcyBzw6lyaWVzIGVtIHVtIHBvbnRvIGVzcGVjw61maWNvIGRvIHRlbXBvLiBBbyBwYXNzYXIgbyBtb3VzZSBzb2JyZSB1bWEgZGF0YSwgdm9jw6ogdmVyw6Egb3MgdmFsb3JlcyBkZSBqdXJvcyBlIGluZmxhw6fDo28ganVudG9zLg0KDQogICAgLSAgIGBjcm9zc2hhaXJzID0gVFJVRWA6IEF0aXZhIGFzIGxpbmhhcyBkZSBtaXJhICh2ZXJ0aWNhbCBlIGhvcml6b250YWwpIHF1ZSBzZWd1ZW0gbyBjdXJzb3IsIGZhY2lsaXRhbmRvIGEgbGVpdHVyYSBkb3MgdmFsb3JlcyBub3MgZWl4b3MuDQoNCiAgICAtICAgYHZhbHVlRGVjaW1hbHMgPSAyYDogRm9ybWF0YSBvcyBuw7ptZXJvcyBubyB0b29sdGlwIHBhcmEgdGVyZW0gc2VtcHJlIGR1YXMgY2FzYXMgZGVjaW1haXMuDQoNCiAgICAtICAgYHBvaW50Rm9ybWF0ID0gJy4uLidgOiBEZWZpbmUgdW0gbW9kZWxvICh0ZW1wbGF0ZSkgSFRNTCBwYXJhIGZvcm1hdGFyIGNhZGEgbGluaGEgZGVudHJvIGRvIHRvb2x0aXAgY29tcGFydGlsaGFkby4NCg0KICAgICAgICAtICAgYDxzcGFuIHN0eWxlPSJjb2xvcjp7cG9pbnQuY29sb3J9Ij5cdTI1Q0Y8L3NwYW4+YDogQ3JpYSB1bSBtYXJjYWRvciBkZSBjw61yY3VsbyAoYFx1MjVDRmApIHF1ZSBoZXJkYSBhIGNvciBkYSBzw6lyaWUgKGB7cG9pbnQuY29sb3J9YCkuDQoNCiAgICAgICAgLSAgIGB7c2VyaWVzLm5hbWV9YDogSW5zZXJlIG8gbm9tZSBkYSBzw6lyaWUgKGV4OiAiVGF4YSBkZSBKdXJvcyAoaTMpIikuDQoNCiAgICAgICAgLSAgIGA8Yj57cG9pbnQueX0gJTwvYj5gOiBJbnNlcmUgbyB2YWxvciBkbyBwb250byBubyBlaXhvIFkgKGB7cG9pbnQueX1gKSwgZW0gbmVncml0byAoYDxiPmApLCBzZWd1aWRvIGRlIHVtIHNpbmFsIGRlIHBvcmNlbnRhZ2VtLg0KDQogICAgICAgIC0gICBgPGJyLz5gOiBBZGljaW9uYSB1bWEgcXVlYnJhIGRlIGxpbmhhLg0KDQojIyBNYXBhcyBJbnRlcmF0aXZvcyBjb20gbGVhZmxldA0KDQpRdWFuZG8gdm9jw6ogdGVtIGRhZG9zIGdlb2dyw6FmaWNvcywgbyBsZWFmbGV0IMOpIGEgZmVycmFtZW50YSBpZGVhbC4gVmFtb3MgY3JpYXIgdW0gbWFwYSBzaW1wbGVzIHF1ZSBtYXJjYSBhIGxvY2FsaXphw6fDo28gZGUgYWxndW1hcyBjYXBpdGFpcyBicmFzaWxlaXJhcy4NCg0KYGBge3J9DQojIENyaWFtb3MgdW0gZGF0YWZyYW1lIGNvbSBhcyBjb29yZGVuYWRhcyBkYXMgY2lkYWRlcw0KY2FwaXRhaXMgPC0gZGF0YS5mcmFtZSgNCiAgbm9tZSA9IGMoIkJlbG8gSG9yaXpvbnRlIiwgIlJpbyBkZSBKYW5laXJvIiwgIlPDo28gUGF1bG8iLCAiU2FsdmFkb3IiKSwNCiAgbGF0ID0gYygtMTkuOTI0NSwgLTIyLjkwNjgsIC0yMy41NTA1LCAtMTIuOTc3NyksDQogIGxvbiA9IGMoLTQzLjkzNTIsIC00My4xNzI5LCAtNDYuNjMzMywgLTM4LjUwMTYpLA0KICBwb3B1bGFjYW8gPSBjKCIyLjUgbWlsaMO1ZXMiLCAiNi43IG1pbGjDtWVzIiwgIjEyLjMgbWlsaMO1ZXMiLCAiMi45IG1pbGjDtWVzIikNCikNCg0KIyBDcmlhbmRvIG8gbWFwYQ0KbGVhZmxldChkYXRhID0gY2FwaXRhaXMpICU+JQ0KICBhZGRUaWxlcygpICU+JSAgIyBBZGljaW9uYSBvIG1hcGEgYmFzZSAoT3BlblN0cmVldE1hcCkNCiAgYWRkTWFya2VycygNCiAgICBsbmcgPSB+bG9uLCANCiAgICBsYXQgPSB+bGF0LCANCiAgICBwb3B1cCA9IH5wYXN0ZTAoIjxzdHJvbmc+Iiwgbm9tZSwgIjwvc3Ryb25nPjxicj5Qb3B1bGHDp8OjbyBBcHJveC46ICIsIHBvcHVsYWNhbykNCiAgKQ0KYGBgDQoNCioqMS4gQ3JpYcOnw6NvIGRvIERhdGFmcmFtZSBgY2FwaXRhaXNgKioNCg0KRXN0YSBwcmltZWlyYSBwYXJ0ZSBkbyBjw7NkaWdvIHNpbXBsZXNtZW50ZSBvcmdhbml6YSBvcyBkYWRvcyBxdWUgc2Vyw6NvIGV4aWJpZG9zIG5vIG1hcGEgZW0gdW1hIHRhYmVsYS4NCg0KLSAgICoqYGNhcGl0YWlzIDwtIGRhdGEuZnJhbWUoLi4uKWAqKjogQ3JpYSB1bSBvYmpldG8gZG8gdGlwbyBgZGF0YS5mcmFtZWAgKGEgZXN0cnV0dXJhIGRlIHRhYmVsYSBwYWRyw6NvIGRvIFIpIGUgbyBhcm1hemVuYSBuYSB2YXJpw6F2ZWwgYGNhcGl0YWlzYC4NCg0KLSAgICoqRGVudHJvIGRvIGBkYXRhLmZyYW1lKClgKiosIGNhZGEgYXJndW1lbnRvIGNyaWEgdW1hIGNvbHVuYSBuYSB0YWJlbGE6DQoNCiAgICAtICAgKipgbm9tZSA9IGMoLi4uKWAqKjogQ3JpYSBhIGNvbHVuYSBgbm9tZWAsIGNvbnRlbmRvIHVtIHZldG9yIGRlIHRleHRvIGNvbSBvcyBub21lcyBkYXMgY2FwaXRhaXMuDQoNCiAgICAtICAgKipgbGF0ID0gYyguLi4pYCoqOiBDcmlhIGEgY29sdW5hIGBsYXRgLCBjb250ZW5kbyBhcyBjb29yZGVuYWRhcyBkZSAqKmxhdGl0dWRlKiogcGFyYSBjYWRhIGNpZGFkZS4NCg0KICAgIC0gICAqKmBsb24gPSBjKC4uLilgKio6IENyaWEgYSBjb2x1bmEgYGxvbmAsIGNvbnRlbmRvIGFzIGNvb3JkZW5hZGFzIGRlICoqbG9uZ2l0dWRlKiogcGFyYSBjYWRhIGNpZGFkZS4NCg0KICAgIC0gICAqKmBwb3B1bGFjYW8gPSBjKC4uLilgKio6IENyaWEgYSBjb2x1bmEgYHBvcHVsYWNhb2AsIGNvbSBhIHBvcHVsYcOnw6NvIGFwcm94aW1hZGEgZGUgY2FkYSBjaWRhZGUgYXJtYXplbmFkYSBjb21vIHRleHRvLg0KDQpPIHJlc3VsdGFkbyDDqSB1bWEgdGFiZWxhIG9yZ2FuaXphZGEsIG9uZGUgY2FkYSBsaW5oYSByZXByZXNlbnRhIHVtYSBjaWRhZGUgZSBzdWFzIHJlc3BlY3RpdmFzIHByb3ByaWVkYWRlcyAobm9tZSwgY29vcmRlbmFkYXMsIGV0Yy4pLg0KDQoqKjIuIENvbnN0cnXDp8OjbyBkbyBNYXBhIGNvbSBgbGVhZmxldGAqKg0KDQpFc3RhIHNlw6fDo28gdXNhIG8gYGRhdGEuZnJhbWVgIGNyaWFkbyBlIG8gZW5jYWRlYW1lbnRvIGRlIGZ1bsOnw7VlcyAoYXRyYXbDqXMgZG8gcGlwZSBgJT4lYCkgcGFyYSBjb25zdHJ1aXIgbyBtYXBhIGNhbWFkYSBwb3IgY2FtYWRhLg0KDQotICAgKipgbGVhZmxldChkYXRhID0gY2FwaXRhaXMpYCoqOg0KDQogICAgLSAgIEVzdGEgw6kgYSBmdW7Dp8OjbyBxdWUgaW5pY2lhbGl6YSBvIG9iamV0byBkbyBtYXBhLg0KDQogICAgLSAgIGBkYXRhID0gY2FwaXRhaXNgOiBJbmZvcm1hIGFvIGBsZWFmbGV0YCBxdWUgbyBjb25qdW50byBkZSBkYWRvcyBwYWRyw6NvIHBhcmEgYXMgY2FtYWRhcyBzZWd1aW50ZXMgc2Vyw6EgbyBkYXRhZnJhbWUgYGNhcGl0YWlzYC4gSXNzbyBldml0YSBxdWUgcHJlY2lzZW1vcyBlc3BlY2lmaWNhciBhIGZvbnRlIGRlIGRhZG9zIGVtIGNhZGEgZnVuw6fDo28gc3Vic2VxdWVudGUuDQoNCi0gICAqKmAlPiUgYWRkVGlsZXMoKWAqKjoNCg0KICAgIC0gICBBZGljaW9uYSBhIGNhbWFkYSBkZSAiYXp1bGVqb3MiICh0aWxlcyksIHF1ZSDDqSBvICoqbWFwYSBiYXNlIHZpc3VhbCoqLiBQb3IgcGFkcsOjbywgZWxlIGJ1c2NhIG9zIG1hcGFzIGRvIFtPcGVuU3RyZWV0TWFwXShodHRwczovL3d3dy5vcGVuc3RyZWV0bWFwLm9yZy8pLCB1bSBwcm9qZXRvIGRlIG1hcGVhbWVudG8gY29sYWJvcmF0aXZvIGUgZ3JhdHVpdG8uIFNlbSBlc3RhIGxpbmhhLCB2b2PDqiB0ZXJpYSBhcGVuYXMgdW0gcXVhZHJvIGVtIGJyYW5jby4NCg0KLSAgICoqYCU+JSBhZGRNYXJrZXJzKC4uLilgKio6DQoNCiAgICAtICAgQWRpY2lvbmEgb3MgbWFyY2Fkb3JlcyAoYWxmaW5ldGVzKSBhbyBtYXBhLCB1bSBwYXJhIGNhZGEgbGluaGEgZG8gbm9zc28gZGF0YWZyYW1lLg0KDQogICAgLSAgICoqYGxuZyA9IH5sb25gKiogZSAqKmBsYXQgPSB+bGF0YCoqOiBFc3RlcyBhcmd1bWVudG9zIGVzcGVjaWZpY2FtIGRlIG9uZGUgdmlyw6NvIGFzIGNvb3JkZW5hZGFzIHBhcmEgb3MgbWFyY2Fkb3Jlcy4gTyB0aWwgKGB+YCkgw6kgdW1hIGZvcm1hIGRlIGRpemVyIGFvIGBsZWFmbGV0YCBwYXJhICJwcm9jdXJhciBwb3IgdW1hIGNvbHVuYSBjb20gZXN0ZSBub21lIChgbG9uYCBvdSBgbGF0YCkgZGVudHJvIGRvIGRhdGFmcmFtZSBxdWUgZm9pIGZvcm5lY2lkbyBuYSBmdW7Dp8OjbyBgbGVhZmxldCgpYCIuDQoNCiAgICAtICAgKipgcG9wdXAgPSB+cGFzdGUwKC4uLilgKio6IEVzdGUgYXJndW1lbnRvIGRlZmluZSBvIGNvbnRlw7pkbyBkYSBqYW5lbGEgcG9wLXVwIHF1ZSBhcGFyZWNlIHF1YW5kbyB1bSB1c3XDoXJpbyBjbGljYSBlbSB1bSBtYXJjYWRvci4NCg0KICAgICAgICAtICAgYHBhc3RlMCguLi4pYDogw4kgdW1hIGZ1bsOnw6NvIGRvIFIgcXVlIGp1bnRhIChjb25jYXRlbmEpIHRleHRvcy4NCg0KICAgICAgICAtICAgYCI8c3Ryb25nPiIsIG5vbWUsICI8L3N0cm9uZz4iYDogUGVnYSBvIHZhbG9yIGRhIGNvbHVuYSBgbm9tZWAgZSBvIGVudm9sdmUgY29tIGFzIHRhZ3MgSFRNTCBgPHN0cm9uZz5gLCBvIHF1ZSBmYXLDoSBjb20gcXVlIG8gbm9tZSBkYSBjaWRhZGUgYXBhcmXDp2EgZW0gKipuZWdyaXRvKiouDQoNCiAgICAgICAgLSAgIGAiPGJyPiJgOiBBZGljaW9uYSB1bWEgcXVlYnJhIGRlIGxpbmhhIGVtIEhUTUwuDQoNCiAgICAgICAgLSAgIGAiUG9wdWxhw6fDo28gQXByb3guOiAiLCBwb3B1bGFjYW9gOiBKdW50YSB1bSB0ZXh0byBmaXhvIGNvbSBvIHZhbG9yIGRhIGNvbHVuYSBgcG9wdWxhY2FvYC4NCg0KIyBQdWJsaWNhbmRvIG5vIFJQdWJzDQoNCkFnb3JhIHF1ZSBzZXUgZG9jdW1lbnRvIGVzdMOhIHByb250bywgY29tIHRleHRvIGZvcm1hdGFkbyBlIGdyw6FmaWNvcyBpbnRlcmF0aXZvcywgw6kgaG9yYSBkZSBjb21wYXJ0aWxow6EtbG8uIE8gUlB1YnMgw6kgdW1hIHBsYXRhZm9ybWEgZ3JhdHVpdGEgZSBzaW1wbGVzLCBwZXJmZWl0YW1lbnRlIGludGVncmFkYSBhbyBSU3R1ZGlvLg0KDQoqKlBhc3NvIDE6IEluc3RhbGFyIG8gUGFjb3RlIGByc2Nvbm5lY3RgKiogU2Ugdm9jw6ogbnVuY2EgcHVibGljb3UgYW50ZXMsIHByZWNpc2Fyw6EgaW5zdGFsYXIgbyBwYWNvdGUgcXVlIGdlcmVuY2lhIGEgY29uZXjDo28uIFZvY8OqIHPDsyBwcmVjaXNhIGZhemVyIGlzc28gdW1hIHZlei4NCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojIEV4ZWN1dGUgZXN0ZSBjw7NkaWdvIG5vIHNldSBDb25zb2xlIGRvIFIsIG7Do28gcHJlY2lzYSBlc3RhciBlbSB1bSBjaHVuay4NCmluc3RhbGwucGFja2FnZXMoInJzY29ubmVjdCIpICANCmBgYA0KDQoqKlBhc3NvIDI6ICJUcmljb3RhciIgKCpLbml0Kikgc2V1IERvY3VtZW50byoqIENsaXF1ZSBubyBib3TDo28gKipLbml0KiogKG91IHVzZSBvIGF0YWxobyBgQ3RybCtTaGlmdCtLYCkgbm8gUlN0dWRpby4gSXNzbyBpcsOhIGdlcmFyIG8gYXJxdWl2byBgaHRtbGAgZmluYWwuIFVtYSBqYW5lbGEgZGUgdmlzdWFsaXphw6fDo28gYXBhcmVjZXLDoSBjb20gbyBzZXUgcmVsYXTDs3Jpby4NCg0KKipQYXNzbyAzOiBQdWJsaWNhcioqIE5hIGphbmVsYSBkZSB2aXN1YWxpemHDp8OjbyBkbyBzZXUgcmVsYXTDs3Jpbywgbm8gY2FudG8gc3VwZXJpb3IgZGlyZWl0bywgdm9jw6ogdmVyw6EgdW0gYm90w6NvICoqUHVibGlzaCoqLg0KDQoxLiAgQ2xpcXVlIGVtICoqUHVibGlzaCoqLg0KDQoyLiAgRXNjb2xoYSBhIG9ww6fDo28gKipSUHVicyoqLg0KDQozLiAgU2UgZm9yIHN1YSBwcmltZWlyYSB2ZXosIG8gUlN0dWRpbyBwZWRpcsOhIHBhcmEgdm9jw6ogc2UgYXV0ZW50aWNhci4gRWxlIGFicmlyw6EgdW1hIHDDoWdpbmEgbm8gc2V1IG5hdmVnYWRvciBwYXJhIHZvY8OqIGNyaWFyIG91IGxvZ2FyIGVtIHVtYSBjb250YSBubyBSUHVicy4gU2lnYSBhcyBpbnN0cnXDp8O1ZXMuDQoNCjQuICBBcMOzcyBhIGF1dGVudGljYcOnw6NvLCBkw6ogdW0gbm9tZSBlIHVtYSBkZXNjcmnDp8OjbyBwYXJhIHN1YSBwdWJsaWNhw6fDo28gZSBjbGlxdWUgZW0gKipQdWJsaXNoKiouDQoNClByb250byEgTyBSU3R1ZGlvIGZhcsOhIG8gdXBsb2FkIGRvIHNldSBkb2N1bWVudG8gZSBkZSB0b2RvcyBvcyBhcnF1aXZvcyBuZWNlc3PDoXJpb3MgKGNvbW8gb3MgZ3LDoWZpY29zIGludGVyYXRpdm9zKSBwYXJhIG8gc2Vydmlkb3IgZG8gUlB1YnMuIEFvIGZpbmFsLCBlbGUgYWJyaXLDoSBvIHNldSBuYXZlZ2Fkb3IgbmEgcMOhZ2luYSBkYSBzdWEgcHVibGljYcOnw6NvIHJlY8OpbS1jcmlhZGEsIGNvbSB1bSBsaW5rIHF1ZSB2b2PDqiBwb2RlIGNvbXBhcnRpbGhhciBjb20gcXVlbSBxdWlzZXIuDQo=