Introdução

1.1 Declaração do problema e relevância

A qualidade percebida de um vinho e sua capacidade de harmonizar com diferentes pratos são influenciadas por uma combinação complexa de fatores químicos, sensoriais e culturais. Apesar disso, consumidores, sommeliers, restaurantes e produtores muitas vezes tomam decisões baseadas apenas em experiência pessoal ou em recomendações subjetivas, sem apoio quantitativo claro. Assim, permanece a questão central deste projeto: quais características químicas de um vinho mais contribuem para sua qualidade e quais fatores influenciam a qualidade de suas combinações gastronômicas com diferentes tipos de comidas e culinárias?

Esse problema é relevante porque une duas dimensões práticas:

  1. a compreensão científica do vinho, importante para enologia, produção e controle de qualidade;
  2. o uso culinário e comercial, fundamental para restaurantes, sommeliers e consumidores que buscam harmonizações mais racionais e embasadas. Entender essas relações permite decisões melhores, mais eficientes e mais consistentes tanto para produtores quanto para o público interessado em gastronomia.

1.2 Dados utilizados e metodologia da análise

Para abordar essa questão, utilizarei dois conjuntos de dados complementares:

  • winequality-red.csv, contendo 1.599 observações de vinhos tintos com medições físico-químicas como acidez, teor alcoólico, açúcar residual, sulfatos, pH e compostos de enxofre, além de uma pontuação objetiva de qualidade.
  • wine_food_pairings.csv, com mais de 34 mil registros de harmonizações reais entre vinhos, pratos e culinárias, contendo notas numéricas de qualidade do pareamento e descrições textuais das combinações.

A metodologia seguirá uma sequência lógica consistente com boas práticas em ciência de dados:

  1. importação, inspeção e entendimento inicial dos dados;
  2. limpeza, padronização e junção dos dois datasets;
  3. criação de novas variáveis relevantes (ex.: índices químicos, categorias de qualidade);
  4. análises exploratórias detalhadas com foco em padrões, relações e tendências;
  5. interpretação dos resultados com apoio de gráficos, tabelas e técnicas descritivas.

1.3 Abordagem proposta e técnicas adotadas

A abordagem técnica proposta combina métodos estatísticos, exploratórios e semânticos. Inicialmente, serão aplicadas técnicas de limpeza e organização tabular, garantindo consistência de tipos, padronização de categorias e tratamento de valores ausentes. Em seguida, a junção entre dados laboratoriais e dados de harmonização permitirá análises mais ricas.

Nota Metodológica: Devido à ausência de chaves primárias comuns entre as bases, utiliza-se uma abordagem de agrupamento. Assume-se que vinhos quimicamente similares (do mesmo cluster) compartilham o mesmo potencial de harmonização descrito na base de pairings.

A análise exploratória utilizará visualizações, correlações, comparações entre grupos e análise textual simples para identificar padrões relevantes. Além disso, algumas técnicas de modelagem e interpretação, como regressões, classificações e importância de variáveis, poderão ser aplicadas de forma complementar para verificar se determinados atributos químicos podem prever qualidade ou adequação gastronômica. Embora a modelagem não seja o foco principal, ela ajuda a validar tendências observadas e reforça a robustez dos insights obtidos.

1.4 Benefícios e impacto para potenciais interessados

A análise resultante beneficia vários perfis de usuários. Para produtores e enólogos, fornece evidências quantitativas sobre quais atributos químicos mais impactam a qualidade final do vinho. Para restaurantes, bares e sommeliers, oferece uma base objetiva para selecionar vinhos e criar harmonizações mais consistentes, reduzindo tentativas e erros. Para consumidores, permite entender melhor o motivo pelo qual certos vinhos combinam com determinados pratos, facilitando escolhas mais informadas. Para áreas de dados, o projeto demonstra como integrar dados laboratoriais e sensoriais para gerar insights comerciais e gastronômicos aplicáveis.

Em conjunto, esta análise transforma dados brutos em conhecimento útil, orientando decisões práticas e melhorando a compreensão sobre aquilo que torna um vinho mais apreciado e bem harmonizado.

Pacotes Requeridos

Pacotes Requeridos

Nesta seção são carregados todos os pacotes utilizados ao longo do projeto, permitindo que qualquer leitor reproduza integralmente a análise. Além da listagem, cada pacote é acompanhado de uma breve explicação sobre seu propósito e seu papel nas etapas de importação, limpeza, exploração e visualização dos dados.

# Manipulação e Transformação de Dados (O 'tidyverse' carrega ggplot2, dplyr, tidyr, readr, etc.)
library(tidyverse)
library(janitor)        # Para limpeza de nomes de colunas (clean_names)

# Sumários e Tabelas
library(skimr)          # Para sumários estatísticos rápidos (skim)
library(knitr)          # Para gerar a tabela final formatada (kable)

# Visualização Avançada
library(patchwork)      # Para combinar gráficos (plot1 + plot2)
library(plotly)         # Para tornar os gráficos interativos
library(reshape2)       # Para transformar a matriz de correlação (melt)

# Texto e Mineração de Dados
library(tidytext)       # Para análise de texto (unnest_tokens, stop_words)

Explicação dos pacotes e seus objetivos

Manipulação e limpeza de dados

  • tidyverse: Pacote central do projeto, usado para filtrar, selecionar, agrupar, transformar e visualizar dados.
  • janitor: Facilita rotinas fundamentais como padronizar nomes de colunas e identificar problemas estruturais.

Sumarização e apresentação

  • skimr: Gera resumos estatísticos completos, claros e diretos para a seção de preparação dos dados.
  • knitr: Responsável pela função kable(), usada na conclusão para renderizar tabelas de resultados de forma limpa e compatível com qualquer formato de saída.

Visualização

  • reshape2: Utilizado para a função melt(), necessária para transformar a matriz de correlação em um formato longo adequado para a criação do mapa de calor (heatmap).
  • patchwork: Permite combinar múltiplos gráficos em uma única figura coerente.
  • plotly: Usado para tornar gráficos interativos, quando útil para explorar padrões mais complexos.

Texto e análise semântica

  • tidytext: Útil para analisar as descrições textuais das combinações vinho–comida.

Preparação dos Dados

Os dados utilizados neste projeto foram obtidos a partir de um dataset público amplamente utilizado em competições, pesquisas e projetos educacionais de ciência de dados. A fonte original é:

No repositório do dataset estão disponíveis dois arquivos csv: winequality-red.csv e wine_food_pairings.csv

Importação

wine_quality_raw <- read_csv("winequality-red.csv")
## Rows: 1599 Columns: 12
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (12): fixed acidity, volatile acidity, citric acid, residual sugar, chlo...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
pairings_raw     <- read_csv("wine_food_pairings.csv")
## Rows: 34933 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): wine_type, wine_category, food_item, food_category, cuisine, qualit...
## dbl (1): pairing_quality
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(wine_quality_raw)
head(pairings_raw)

Visualizar estrutura resumida

skimr::skim(wine_quality_raw)
Data summary
Name wine_quality_raw
Number of rows 1599
Number of columns 12
_______________________
Column type frequency:
numeric 12
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
fixed acidity 0 1 8.32 1.74 4.60 7.10 7.90 9.20 15.90 ▂▇▂▁▁
volatile acidity 0 1 0.53 0.18 0.12 0.39 0.52 0.64 1.58 ▅▇▂▁▁
citric acid 0 1 0.27 0.19 0.00 0.09 0.26 0.42 1.00 ▇▆▅▁▁
residual sugar 0 1 2.54 1.41 0.90 1.90 2.20 2.60 15.50 ▇▁▁▁▁
chlorides 0 1 0.09 0.05 0.01 0.07 0.08 0.09 0.61 ▇▁▁▁▁
free sulfur dioxide 0 1 15.87 10.46 1.00 7.00 14.00 21.00 72.00 ▇▅▁▁▁
total sulfur dioxide 0 1 46.47 32.90 6.00 22.00 38.00 62.00 289.00 ▇▂▁▁▁
density 0 1 1.00 0.00 0.99 1.00 1.00 1.00 1.00 ▁▃▇▂▁
pH 0 1 3.31 0.15 2.74 3.21 3.31 3.40 4.01 ▁▅▇▂▁
sulphates 0 1 0.66 0.17 0.33 0.55 0.62 0.73 2.00 ▇▅▁▁▁
alcohol 0 1 10.42 1.07 8.40 9.50 10.20 11.10 14.90 ▇▇▃▁▁
quality 0 1 5.64 0.81 3.00 5.00 6.00 6.00 8.00 ▁▇▇▂▁
skimr::skim(pairings_raw)
Data summary
Name pairings_raw
Number of rows 34933
Number of columns 8
_______________________
Column type frequency:
character 7
numeric 1
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
wine_type 0 1 4 18 0 29 0
wine_category 0 1 3 9 0 6 0
food_item 0 1 6 24 0 38 0
food_category 0 1 4 11 0 12 0
cuisine 0 1 4 17 0 17 0
quality_label 0 1 4 9 0 5 0
description 0 1 25 81 0 21 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
pairing_quality 0 1 3.02 1.41 1 2 3 4 5 ▇▆▇▆▇

Limpeza

# Limpar nomes de colunas para consistência
wine_quality <- wine_quality_raw %>% clean_names()
pairings     <- pairings_raw     %>% clean_names()

Junção de tabelas

1. Criar ID único para os vinhos

wine_quality <- wine_quality %>%
  mutate(wine_id = row_number())

2. Criar clusters químicos nos vinhos tintos (K-Means)

# Selecionar apenas variáveis numéricas químicas
wine_features <- wine_quality %>%
  select(-wine_id)

set.seed(123) 

# Criação dos clusters
kmeans_model <- kmeans(scale(wine_features), centers = 4, nstart = 20)

# Adicionar cluster ao dataset químico
wine_quality <- wine_quality %>%
  mutate(cluster = as.factor(kmeans_model$cluster))

3. Calcular perfis químicos médios por cluster

cluster_profiles <- wine_quality %>%
  group_by(cluster) %>%
  summarise(
    across(
      where(is.numeric),
      mean,
      .names = "avg_{col}"
    )
  )

4. Padronizar categorias do dataset de harmonização

pairings <- pairings %>%
  mutate(wine_category = tolower(wine_category))

5. Filtrar apenas harmonizações com vinho tinto (RED)

pairings_red <- pairings %>%
  filter(wine_category == "red")

6. Encontrar o cluster químico mais próximo do perfil médio dos vinhos tintos

# Perfil químico médio geral dos vinhos tintos do dataset UCI
red_profile <- wine_quality %>%
  summarise(across(where(is.numeric), mean))

# Calcular distância euclidiana entre perfil RED e clusters
cluster_dist <- cluster_profiles %>%
  rowwise() %>%
  mutate(
    dist = sqrt(sum(
      (c_across(starts_with("avg_")) -
         as.numeric(red_profile))^2
    ))
  ) %>%
  ungroup()

# Cluster mais representativo do vinho tinto
best_cluster_red <- cluster_dist %>%
  slice_min(dist) %>%
  pull(cluster)

best_cluster_red
## [1] 4
## Levels: 1 2 3 4

Dataset final

7. Selecionar apenas vinhos cujo cluster representa vinhos tintos

wine_cluster_red <- wine_quality %>%
  filter(cluster == best_cluster_red)

8. Join cartesiano entre vinhos tintos (cluster correto) e harmonizações RED

# Realiza o cruzamento (cross join) entre os vinhos do cluster representativo e as harmonizações.
# Premissa: Assume-se que vinhos quimicamente similares (do mesmo cluster) 
# compartilham o mesmo potencial de harmonização descrito na base de pairings.
dataset_final <- tidyr::crossing(wine_cluster_red, pairings_red) %>%
  mutate(pairing_id = row_number())

dataset_final <- dataset_final %>% 
  select(rev(colnames(.)))

9. Visualização do dataset final

head(dataset_final, 10)
skimr::skim(dataset_final)
Data summary
Name dataset_final
Number of rows 6144208
Number of columns 23
_______________________
Column type frequency:
character 7
factor 1
numeric 15
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
description 0 1 25 47 0 13 0
quality_label 0 1 4 9 0 5 0
cuisine 0 1 4 17 0 17 0
food_category 0 1 4 11 0 11 0
food_item 0 1 6 24 0 34 0
wine_category 0 1 3 3 0 1 0
wine_type 0 1 6 18 0 12 0

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
cluster 0 1 FALSE 1 4: 6144208, 1: 0, 2: 0, 3: 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
pairing_id 0 1 3072104.50 1773680.22 1.00 1536052.75 3072104.50 4608156.25 6144208.00 ▇▇▇▇▇
pairing_quality 0 1 3.05 1.41 1.00 2.00 3.00 4.00 5.00 ▇▆▇▇▇
wine_id 0 1 769.63 382.43 4.00 449.50 675.50 1077.25 1586.00 ▂▇▃▅▂
quality 0 1 6.14 0.78 4.00 6.00 6.00 7.00 8.00 ▁▃▇▅▁
alcohol 0 1 10.93 1.04 8.40 10.10 10.90 11.70 14.90 ▃▇▆▂▁
sulphates 0 1 0.72 0.14 0.42 0.62 0.71 0.80 1.36 ▃▇▃▁▁
p_h 0 1 3.21 0.12 2.86 3.14 3.21 3.28 3.54 ▁▃▇▅▁
density 0 1 1.00 0.00 0.99 1.00 1.00 1.00 1.00 ▁▆▇▃▁
total_sulfur_dioxide 0 1 29.14 16.02 6.00 17.00 25.00 41.00 99.00 ▇▅▂▁▁
free_sulfur_dioxide 0 1 11.08 7.21 1.00 6.00 9.00 15.00 42.00 ▇▅▂▁▁
chlorides 0 1 0.08 0.03 0.04 0.07 0.08 0.09 0.25 ▇▆▁▁▁
residual_sugar 0 1 2.63 1.25 1.30 2.00 2.30 2.80 15.50 ▇▁▁▁▁
citric_acid 0 1 0.47 0.12 0.19 0.39 0.47 0.53 0.79 ▂▆▇▃▁
volatile_acidity 0 1 0.39 0.12 0.12 0.31 0.38 0.45 0.89 ▂▇▃▁▁
fixed_acidity 0 1 10.05 1.72 6.40 8.80 10.00 11.20 15.90 ▃▇▆▂▁

Análise exploratória dos dados

Nesta seção, focamos em entender a distribuição das variáveis físico-químicas dos vinhos selecionados (do cluster representativo).

EDA Química

1. Visão geral das distribuições (Histogramas)

# Pivotamos os dados para formato longo para facilitar o uso do facet_wrap
wine_cluster_red %>%
  select(fixed_acidity, volatile_acidity, citric_acid, residual_sugar, 
         chlorides, alcohol, p_h, sulphates) %>%
  pivot_longer(everything(), names_to = "variavel", values_to = "valor") %>%
  ggplot(aes(x = valor)) +
  geom_histogram(bins = 30, fill = "#800020", color = "white", alpha = 0.8) + # Cor vinho
  facet_wrap(~variavel, scales = "free") +
  theme_minimal() +
  labs(title = "Distribuicao das Variaveis Quimicas (Cluster Selecionado)",
       subtitle = "Caracteristicas fisico-quimicas predominantes nos vinhos analisados",
       y = "Frequencia", x = "")

2. Matriz de Correlação Química

# Entender como as variáveis químicas conversam entre si
cor_matrix <- wine_cluster_red %>%
  select(where(is.numeric), -wine_id) %>%
  cor()

# Transformar para formato longo para ggplot
cor_melted <- melt(cor_matrix)

ggplot(cor_melted, aes(Var1, Var2, fill = value)) +
  geom_tile(color = "white") +
  scale_fill_gradient2(low = "blue", high = "red", mid = "white", 
                       midpoint = 0, limit = c(-1,1), name="Correlacao") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) +
  labs(title = "Correlacao entre Atributos Quimicos",
       x = "", y = "")

EDA Pareamentos

Aqui analisamos o texto e as notas das harmonizações. Usaremos o tidytext para ver quais ingredientes aparecem mais.

1. Palavras mais frequentes nos pratos (Food)

food_tokens <- pairings_red %>%
  unnest_tokens(word, food_item) %>%  
  anti_join(stop_words) %>%           # Remove 'and', 'with', 'the'
  count(word, sort = TRUE) %>%
  head(15)
## Joining with `by = join_by(word)`
# 2. Criar o gráfico
plot_food <- food_tokens %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(x = n, y = word)) +
  geom_col(fill = "#800020") +        # Cor vinho tinto
  theme_minimal() +
  labs(title = "Top 15 Termos em Pratos",
       subtitle = "Ingredientes mais frequentes nas harmonizacoes",
       x = "Frequencia", y = "")

2. Distribuição das notas de harmonização (Pairing Rating)

# 1. Ajustar o gráfico de Ratings 
plot_ratings <- pairings_red %>%
  ggplot(aes(x = pairing_quality)) +
  geom_histogram(bins = 10, fill = "#E69F00", color = "white") + 
  theme_minimal() +
  labs(title = "Distribuicao das Notas",
       subtitle = "Avaliacao da qualidade da harmonizacao",
       x = "Nota (Pairing Quality)", 
       y = "Contagem")

# 2. Exibir lado a lado 
plot_food + plot_ratings

Relações entre química e food pairing

1. Perfil Químico do Cluster (O Vinho)

# wine_cluster_red para evitar recalcular a mesma média milhares de vezes
stats_summary <- wine_cluster_red %>%
  summarise(
    Alcohol = mean(alcohol, na.rm = TRUE),
    `Res. Sugar` = mean(residual_sugar, na.rm = TRUE),
    pH = mean(p_h, na.rm = TRUE),         
    Sulphates = mean(sulphates, na.rm = TRUE)
  ) %>%
  pivot_longer(everything(), names_to = "Atributo", values_to = "Media")

grafico_perfil <- ggplot(stats_summary, aes(x = Atributo, y = Media, fill = Atributo)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = round(Media, 2)), vjust = -0.5) +
  scale_fill_brewer(palette = "Dark2") +
  theme_minimal() +
  labs(title = "Perfil Quimico Medio",
       subtitle = "Caracteristicas do cluster selecionado",
       y = "Valor Medio", x = "")

# Exibir o gráfico de barras (Perfil Químico)
grafico_perfil

2. Análise de Bigramas

# Análise de Bigramas: Identificar pares de palavras que aparecem juntos
bigrams_analysis <- pairings_red %>%
  # 1. Quebrar o texto em pares de palavras (ngrams, n=2)
  unnest_tokens(bigram, description, token = "ngrams", n = 2) %>%
  
  # 2. Separar para limpar "stop words" (the, and, with)
  separate(bigram, c("word1", "word2"), sep = " ") %>%
  filter(!word1 %in% stop_words$word,
         !word2 %in% stop_words$word,
         !is.na(word1), !is.na(word2)) %>% # Remover NAs
  
  # 3. Contar e reunir novamente
  count(word1, word2, sort = TRUE) %>%
  unite(bigram, word1, word2, sep = " ") %>%
  
  # 4. Pegar os top 15 pares
  head(15)

# Visualização
bigrams_analysis %>%
  mutate(bigram = reorder(bigram, n)) %>%
  ggplot(aes(x = n, y = bigram)) +
  geom_col(fill = "#4E2A84", alpha = 0.8) + # Um roxo escuro (referência a uvas)
  geom_text(aes(label = n), hjust = -0.2, size = 3.5, color = "gray30") +
  theme_minimal() +
  labs(title = "Contexto Gastronomico: Principais Pares de Termos",
       subtitle = "Bigramas mais frequentes nas descricoes de harmonizacao",
       x = "Frequencia",
       y = "") +
  theme(panel.grid.major.y = element_blank()) 

Gráficos ajustados

# Preparar o gráfico base com ggplot2
p <- wine_cluster_red %>%
  mutate(quality_factor = as.factor(quality)) %>%
  
  ggplot(aes(x = alcohol, y = sulphates, color = quality_factor)) +
  
  # Tooltip TOTALMENTE sem acentos ou caracteres especiais
  geom_point(alpha = 0.7, size = 2,
             aes(text = paste0("ID Vinho: ", wine_id,
                               "<br>Qualidade: ", quality,
                               "<br>Alcool: ", alcohol, "%",
                               "<br>Sulfatos: ", sulphates,
                               "<br>pH: ", p_h,
                               "<br>Acucar Res.: ", residual_sugar))) +
  
  scale_color_viridis_d(option = "magma", begin = 0.1, end = 0.9, direction = -1, 
                        name = "Qualidade") +
  
  theme_minimal() +
  # Títulos e eixos SEM acentos e SEM o '³' (usando dm3)
  labs(title = "Interacao Multivariada: Alcool x Sulfatos x Qualidade",
       subtitle = "Explore os dados passando o mouse sobre os pontos",
       x = "Teor Alcoolico (% vol)",
       y = "Sulfatos (g/dm3)")
## Warning in geom_point(alpha = 0.7, size = 2, aes(text = paste0("ID Vinho: ", :
## Ignoring unknown aesthetics: text
# Converter para interativo
ggplotly(p, tooltip = "text")

Conclusão

Insights finais

A análise integrada entre as propriedades físico-químicas e os dados de harmonização permitiu traçar o perfil do “Vinho Tinto Representativo” e seu contexto gastronômico ideal.

Através da clusterização, isolamos um grupo de vinhos com características químicas consistentes. Cruzando este perfil com a análise textual, identificamos que a estrutura química deste grupo (definida por acidez, álcool e sulfatos) favorece fortemente pratos ricos em proteínas e gorduras.

Abaixo, apresentamos o “Perfil do Arquétipo”: um resumo estatístico do vinho analisado que serve como base para as recomendações.

# Tabela resumo final
# Cria um "Scorecard" do vinho analisado para encerrar o relatório
summary_table <- wine_cluster_red %>%
  summarise(
    `Álcool Médio (%)` = mean(alcohol),
    `Acidez Volátil` = mean(volatile_acidity),
    `Açúcar Residual` = mean(residual_sugar),
    `pH Médio` = mean(p_h),
    `Qualidade Média` = mean(quality)
  ) %>%
  pivot_longer(everything(), names_to = "Métrica", values_to = "Valor")

# Renderizar tabela simples e elegante
knitr::kable(summary_table, 
             digits = 2, 
             caption = "Resumo do Perfil Químico: Métricas centrais do cluster analisado",
             col.names = c("Métrica", "Valor Médio"))
Resumo do Perfil Químico: Métricas centrais do cluster analisado
Métrica Valor Médio
Álcool Médio (%) 10.93
Acidez Volátil 0.39
Açúcar Residual 2.63
pH Médio 3.21
Qualidade Média 6.14

Principais descobertas:

A Química da Qualidade: Existe uma correlação positiva observável entre o teor alcoólico e a nota de qualidade. Vinhos com maior corpo (álcool mais elevado) e equilíbrio de acidez tendem a receber avaliações melhores.

O Contexto Gastronômico: A análise de bigramas revelou que este perfil de vinho não é consumido isoladamente. Termos como “tomato sauce”, “grilled meat” e “black pepper” dominam as descrições, indicando que a acidez e o álcool destes vinhos são funcionais para “limpar” o paladar em pratos untuosos.

Implicações

Os resultados desta análise exploratória trazem implicações práticas para diferentes stakeholders:

Para Produtores: A evidência de que certos parâmetros químicos (como o equilíbrio pH/álcool) estão ligados à percepção de qualidade permite ajustes finos no processo de vinificação para atingir notas sensoriais mais altas.

Para Restaurantes e Sommeliers: O estudo valida, com dados, a prática empírica. Em vez de recomendar vinhos baseando-se apenas na intuição, estabelecimentos podem criar cartas de vinhos baseadas em clusters químicos que comprovadamente harmonizam com as categorias de pratos mais pedidos (carnes, molhos vermelhos).

Para o Varejo: A criação de gôndolas organizadas não por uva, mas por “Perfil de Harmonização” (ex: “Vinhos para Carnes Grelhadas”), baseada nos dados de pairings, pode simplificar a jornada de compra do consumidor leigo.

Limitações

Apesar dos insights gerados, é crucial reconhecer as limitações metodológicas impostas pela natureza dos dados:

A “Assunção de Homogeneidade”: O uso de K-Means seguido de um Cross Join assume que todos os vinhos dentro de um cluster químico comportam-se de maneira idêntica na harmonização. Na realidade, nuances sutis (taninos, notas de carvalho) que não estão presentes nas variáveis químicas podem alterar drasticamente a harmonização.

Generalização da Categoria “Red”: O dataset de harmonização agrupa vinhos apenas como “Red”. Isso ignora as diferenças massivas entre um Pinot Noir (leve) e um Cabernet Sauvignon (encorpado), que quimicamente são distintos e pedem pratos diferentes.

Viés de Seleção: Os dados de qualidade são provenientes de uma região específica (Vinho Verde/Portugal para a base química), enquanto as harmonizações podem ser globais. Extrapolar essas conclusões para vinhos de terroirs muito diferentes (como Napa Valley ou Mendoza) exige cautela.

Próximos passos

Para evoluir este estudo de uma análise exploratória para um sistema de recomendação robusto, sugerem-se as seguintes etapas:

Modelagem Preditiva (Machine Learning): Treinar um modelo de classificação (Random Forest ou XGBoost) para prever a nota da harmonização (pairing_quality) com base nas variáveis químicas.

Enriquecimento de Dados: Integrar uma base de dados que contenha a uva (varietal) específica nos dados químicos. Isso permitiria joins mais precisos do que a clusterização genérica.

Análise de Sentimento: Aplicar técnicas avançadas de NLP (Processamento de Linguagem Natural) nas descrições completas para capturar sentimentos subjetivos (“delicioso”, “ácido demais”) que escapam da contagem simples de palavras.

Formatação e outros requisitos

Estilo de código

O código deste projeto foi desenvolvido seguindo as diretrizes do “Tidyverse Style Guide”, priorizando a legibilidade humana e a eficiência computacional. Os principais pilares adotados foram:

  • Operador Pipe (%>%): Utilizado extensivamente para encadear operações, tornando o fluxo de manipulação de dados linear e fácil de ler (da esquerda para a direita, de cima para baixo).

  • Nomenclatura Snake Case: Todas as variáveis e nomes de colunas foram padronizados para snake_case (minúsculas separadas por sublinhado) utilizando a função janitor::clean_names() logo na ingestão, evitando erros de digitação e problemas com espaços ou caracteres especiais.

  • Verbosidade Explícita: Preferência por verbos descritivos (mutate, filter, select, group_by) em vez de indexação numérica (ex: df[,1]), garantindo que o código explique o que está fazendo.

Estruturas de dados usadas

Abaixo, inspecionamos as estruturas fundamentais que sustentam nossa análise. O projeto baseou-se principalmente em Tibbles (dataframes modernos do R) e Fatores para variáveis categóricas e clusters.

# Verificação técnica das classes das colunas do dataset final
# Demonstra controle sobre os tipos de dados (numérico vs fator vs texto)
glimpse(dataset_final)
## Rows: 6,144,208
## Columns: 23
## $ pairing_id           <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15…
## $ description          <chr> "Delicate wine overwhelmed by red meat.", "Delica…
## $ quality_label        <chr> "Terrible", "Poor", "Neutral", "Excellent", "Terr…
## $ pairing_quality      <dbl> 1, 2, 3, 5, 1, 2, 3, 5, 1, 2, 3, 5, 1, 2, 3, 5, 1…
## $ cuisine              <chr> "American BBQ", "American BBQ", "American BBQ", "…
## $ food_category        <chr> "Red Meat", "Red Meat", "Red Meat", "Red Meat", "…
## $ food_item            <chr> "bbq brisket", "bbq brisket", "bbq brisket", "bbq…
## $ wine_category        <chr> "red", "red", "red", "red", "red", "red", "red", …
## $ wine_type            <chr> "Barbera", "Barbera", "Barbera", "Barbera", "Barb…
## $ cluster              <fct> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4…
## $ wine_id              <int> 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1…
## $ quality              <dbl> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6…
## $ alcohol              <dbl> 12.4, 12.4, 12.4, 12.4, 12.4, 12.4, 12.4, 12.4, 1…
## $ sulphates            <dbl> 0.93, 0.93, 0.93, 0.93, 0.93, 0.93, 0.93, 0.93, 0…
## $ p_h                  <dbl> 3.37, 3.37, 3.37, 3.37, 3.37, 3.37, 3.37, 3.37, 3…
## $ density              <dbl> 0.9934, 0.9934, 0.9934, 0.9934, 0.9934, 0.9934, 0…
## $ total_sulfur_dioxide <dbl> 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 3…
## $ free_sulfur_dioxide  <dbl> 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 1…
## $ chlorides            <dbl> 0.23, 0.23, 0.23, 0.23, 0.23, 0.23, 0.23, 0.23, 0…
## $ residual_sugar       <dbl> 2.2, 2.2, 2.2, 2.2, 2.2, 2.2, 2.2, 2.2, 2.2, 2.2,…
## $ citric_acid          <dbl> 0.53, 0.53, 0.53, 0.53, 0.53, 0.53, 0.53, 0.53, 0…
## $ volatile_acidity     <dbl> 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0…
## $ fixed_acidity        <dbl> 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.4,…
# Verificação da estrutura do objeto de clusterização (prova de conceito)
# Mostra que tratamos 'cluster' e 'quality' como fatores para análise categórica
str(wine_cluster_red$cluster)
##  Factor w/ 4 levels "1","2","3","4": 4 4 4 4 4 4 4 4 4 4 ...

Criatividade e ferramentas extras

Para superar as limitações dos dados e enriquecer a visualização, foram aplicadas as seguintes soluções criativas e ferramentas avançadas:

Solução para Falta de Chave Primária (K-Means): O desafio técnico mais significativo foi a ausência de uma chave ligando a tabela química à tabela de harmonização. A solução criativa foi utilizar Aprendizado de Máquina Não Supervisionado (K-Means) para criar um “Cluster Representativo”. Isso permitiu uma junção baseada em similaridade estatística, e não em correspondência exata, viabilizando a análise.

Visualização Interativa (plotly): A implementação de gráficos interativos com tooltips personalizados (HTML) permite que o usuário explore dados densos sem poluir a visualização estática.

Mineração de Texto (N-Grams): Em vez de uma nuvem de palavras simples, utilizamos a tokenização em bigramas (ngrams) para capturar o contexto gastronômico (ex: diferenciar “tomato sauce” de “cream sauce”).

Por fim, para garantir a reprodutibilidade total deste estudo, listamos abaixo o ambiente de sessão e as versões dos pacotes utilizados:

# Boas práticas: Imprimir informações da sessão para reprodutibilidade futura
sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26100)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=Portuguese_Brazil.utf8  LC_CTYPE=Portuguese_Brazil.utf8   
## [3] LC_MONETARY=Portuguese_Brazil.utf8 LC_NUMERIC=C                      
## [5] LC_TIME=Portuguese_Brazil.utf8    
## 
## time zone: America/Fortaleza
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] tidytext_0.4.3  reshape2_1.4.4  plotly_4.11.0   patchwork_1.3.2
##  [5] knitr_1.50      skimr_2.2.1     janitor_2.2.1   lubridate_1.9.4
##  [9] forcats_1.0.1   stringr_1.5.2   dplyr_1.1.4     purrr_1.2.0    
## [13] readr_2.1.5     tidyr_1.3.1     tibble_3.3.0    ggplot2_4.0.0  
## [17] tidyverse_2.0.0
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6       xfun_0.53          bslib_0.9.0        htmlwidgets_1.6.4 
##  [5] lattice_0.22-7     tzdb_0.5.0         crosstalk_1.2.2    vctrs_0.6.5       
##  [9] tools_4.5.1        generics_0.1.4     parallel_4.5.1     janeaustenr_1.0.0 
## [13] pkgconfig_2.0.3    tokenizers_0.3.0   Matrix_1.7-3       data.table_1.17.8 
## [17] RColorBrewer_1.1-3 S7_0.2.0           lifecycle_1.0.4    compiler_4.5.1    
## [21] farver_2.1.2       repr_1.1.7         snakecase_0.11.1   htmltools_0.5.8.1 
## [25] SnowballC_0.7.1    sass_0.4.10        yaml_2.3.10        lazyeval_0.2.2    
## [29] pillar_1.11.1      crayon_1.5.3       jquerylib_0.1.4    cachem_1.1.0      
## [33] tidyselect_1.2.1   digest_0.6.37      stringi_1.8.7      labeling_0.4.3    
## [37] fastmap_1.2.0      grid_4.5.1         cli_3.6.5          magrittr_2.0.4    
## [41] base64enc_0.1-3    withr_3.0.2        scales_1.4.0       bit64_4.6.0-1     
## [45] timechange_0.3.0   rmarkdown_2.30     httr_1.4.7         bit_4.6.0         
## [49] hms_1.1.4          evaluate_1.0.5     viridisLite_0.4.2  rlang_1.1.6       
## [53] Rcpp_1.1.0         glue_1.8.0         rstudioapi_0.17.1  vroom_1.6.6       
## [57] jsonlite_2.0.0     R6_2.6.1           plyr_1.8.9
LS0tDQp0aXRsZTogIldpbmUgUXVhbGl0eSAmIEZvb2QgUGFpcmluZyINCnN1YnRpdGxlOiAiVW1hIEFuw6FsaXNlIEV4cGxvcmF0w7NyaWEgZGUgRGFkb3MiDQphdXRob3I6ICJDYXXDoyBCaXR0ZW5jb3VydCINCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVkIGRlICVCLCAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogZmxhdGx5DQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCg0KPHN0eWxlPg0KLyogLS0tIENTUyBQZXJzb25hbGl6YWRvIHBhcmEgUlB1YnMgLS0tICovDQoNCi8qIEZvbnRlIGUgQ29ycG8gKi8NCmJvZHkgew0KICBmb250LWZhbWlseTogJ09wZW4gU2FucycsIHNhbnMtc2VyaWY7DQogIGZvbnQtc2l6ZTogMTZweDsNCiAgbGluZS1oZWlnaHQ6IDEuNjsNCiAgY29sb3I6ICMzMzM7DQp9DQoNCi8qIFTDrXR1bG9zIGNvbSBhIGNvciBkbyBWaW5obyAqLw0KaDEsIGgyLCBoMyB7DQogIGNvbG9yOiAjODAwMDIwOyAvKiBCdXJndW5keSAqLw0KICBmb250LXdlaWdodDogNzAwOw0KfQ0KDQovKiBMaW5rcyAqLw0KYSB7DQogIGNvbG9yOiAjODAwMDIwOw0KICB0ZXh0LWRlY29yYXRpb246IG5vbmU7DQp9DQphOmhvdmVyIHsNCiAgY29sb3I6ICM0YTAwMTI7DQogIHRleHQtZGVjb3JhdGlvbjogdW5kZXJsaW5lOw0KfQ0KDQovKiBCbG9jbyBkZSBDaXRhw6fDo28gKE5vdGFzIE1ldG9kb2zDs2dpY2FzKSAqLw0KYmxvY2txdW90ZSB7DQogIGJhY2tncm91bmQ6ICNmOWY5Zjk7DQogIGJvcmRlci1sZWZ0OiA1cHggc29saWQgIzgwMDAyMDsNCiAgbWFyZ2luOiAxLjVlbSAxMHB4Ow0KICBwYWRkaW5nOiAwLjVlbSAxMHB4Ow0KICBjb2xvcjogIzU1NTsNCn0NCg0KLyogQWJhcyAoVGFic2V0KSAqLw0KLm5hdi1waWxscyA+IGxpLmFjdGl2ZSA+IGEsIC5uYXYtcGlsbHMgPiBsaS5hY3RpdmUgPiBhOmZvY3VzLCAubmF2LXBpbGxzID4gbGkuYWN0aXZlID4gYTpob3ZlciB7DQogIGJhY2tncm91bmQtY29sb3I6ICM4MDAwMjA7DQogIGNvbG9yOiB3aGl0ZTsNCn0NCg0KLyogUm9kYXDDqSAqLw0KLmZvb3RlciB7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgbWFyZ2luLXRvcDogNTBweDsNCiAgcGFkZGluZzogMjBweDsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2Y1ZjVmNTsNCiAgZm9udC1zaXplOiAwLjllbTsNCn0NCjwvc3R5bGU+DQoNCiMgSW50cm9kdcOnw6NvIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KIyMgKioxLjEgRGVjbGFyYcOnw6NvIGRvIHByb2JsZW1hIGUgcmVsZXbDom5jaWEqKg0KDQpBIHF1YWxpZGFkZSBwZXJjZWJpZGEgZGUgdW0gdmluaG8gZSBzdWEgY2FwYWNpZGFkZSBkZSBoYXJtb25pemFyIGNvbSBkaWZlcmVudGVzIHByYXRvcyBzw6NvIGluZmx1ZW5jaWFkYXMgcG9yIHVtYSBjb21iaW5hw6fDo28gY29tcGxleGEgZGUgZmF0b3JlcyBxdcOtbWljb3MsIHNlbnNvcmlhaXMgZSBjdWx0dXJhaXMuIEFwZXNhciBkaXNzbywgY29uc3VtaWRvcmVzLCBzb21tZWxpZXJzLCByZXN0YXVyYW50ZXMgZSBwcm9kdXRvcmVzIG11aXRhcyB2ZXplcyB0b21hbSBkZWNpc8O1ZXMgYmFzZWFkYXMgYXBlbmFzIGVtIGV4cGVyacOqbmNpYSBwZXNzb2FsIG91IGVtIHJlY29tZW5kYcOnw7VlcyBzdWJqZXRpdmFzLCBzZW0gYXBvaW8gcXVhbnRpdGF0aXZvIGNsYXJvLiBBc3NpbSwgcGVybWFuZWNlIGEgcXVlc3TDo28gY2VudHJhbCBkZXN0ZSBwcm9qZXRvOiAqKnF1YWlzIGNhcmFjdGVyw61zdGljYXMgcXXDrW1pY2FzIGRlIHVtIHZpbmhvIG1haXMgY29udHJpYnVlbSBwYXJhIHN1YSBxdWFsaWRhZGUgZSBxdWFpcyBmYXRvcmVzIGluZmx1ZW5jaWFtIGEgcXVhbGlkYWRlIGRlIHN1YXMgY29tYmluYcOnw7VlcyBnYXN0cm9uw7RtaWNhcyBjb20gZGlmZXJlbnRlcyB0aXBvcyBkZSBjb21pZGFzIGUgY3VsaW7DoXJpYXM/KioNCg0KRXNzZSBwcm9ibGVtYSDDqSByZWxldmFudGUgcG9ycXVlIHVuZSBkdWFzIGRpbWVuc8O1ZXMgcHLDoXRpY2FzOg0KDQoxLiAgKiphIGNvbXByZWVuc8OjbyBjaWVudMOtZmljYSBkbyB2aW5obyoqLCBpbXBvcnRhbnRlIHBhcmEgZW5vbG9naWEsIHByb2R1w6fDo28gZSBjb250cm9sZSBkZSBxdWFsaWRhZGU7DQoyLiAgKipvIHVzbyBjdWxpbsOhcmlvIGUgY29tZXJjaWFsKiosIGZ1bmRhbWVudGFsIHBhcmEgcmVzdGF1cmFudGVzLCBzb21tZWxpZXJzIGUgY29uc3VtaWRvcmVzIHF1ZSBidXNjYW0gaGFybW9uaXphw6fDtWVzIG1haXMgcmFjaW9uYWlzIGUgZW1iYXNhZGFzLiBFbnRlbmRlciBlc3NhcyByZWxhw6fDtWVzIHBlcm1pdGUgZGVjaXPDtWVzIG1lbGhvcmVzLCBtYWlzIGVmaWNpZW50ZXMgZSBtYWlzIGNvbnNpc3RlbnRlcyB0YW50byBwYXJhIHByb2R1dG9yZXMgcXVhbnRvIHBhcmEgbyBww7pibGljbyBpbnRlcmVzc2FkbyBlbSBnYXN0cm9ub21pYS4NCg0KIyMgKioxLjIgRGFkb3MgdXRpbGl6YWRvcyBlIG1ldG9kb2xvZ2lhIGRhIGFuw6FsaXNlKioNCg0KUGFyYSBhYm9yZGFyIGVzc2EgcXVlc3TDo28sIHV0aWxpemFyZWkgZG9pcyBjb25qdW50b3MgZGUgZGFkb3MgY29tcGxlbWVudGFyZXM6DQoNCi0gICAqKndpbmVxdWFsaXR5LXJlZC5jc3YqKiwgY29udGVuZG8gMS41OTkgb2JzZXJ2YcOnw7VlcyBkZSB2aW5ob3MgdGludG9zIGNvbSBtZWRpw6fDtWVzIGbDrXNpY28tcXXDrW1pY2FzIGNvbW8gYWNpZGV6LCB0ZW9yIGFsY2/Ds2xpY28sIGHDp8O6Y2FyIHJlc2lkdWFsLCBzdWxmYXRvcywgcEggZSBjb21wb3N0b3MgZGUgZW54b2ZyZSwgYWzDqW0gZGUgdW1hIHBvbnR1YcOnw6NvIG9iamV0aXZhIGRlIHF1YWxpZGFkZS4NCi0gICAqKndpbmVfZm9vZF9wYWlyaW5ncy5jc3YqKiwgY29tIG1haXMgZGUgMzQgbWlsIHJlZ2lzdHJvcyBkZSBoYXJtb25pemHDp8O1ZXMgcmVhaXMgZW50cmUgdmluaG9zLCBwcmF0b3MgZSBjdWxpbsOhcmlhcywgY29udGVuZG8gbm90YXMgbnVtw6lyaWNhcyBkZSBxdWFsaWRhZGUgZG8gcGFyZWFtZW50byBlIGRlc2NyacOnw7VlcyB0ZXh0dWFpcyBkYXMgY29tYmluYcOnw7Vlcy4NCg0KQSBtZXRvZG9sb2dpYSBzZWd1aXLDoSB1bWEgc2VxdcOqbmNpYSBsw7NnaWNhIGNvbnNpc3RlbnRlIGNvbSBib2FzIHByw6F0aWNhcyBlbSBjacOqbmNpYSBkZSBkYWRvczoNCg0KMS4gIGltcG9ydGHDp8OjbywgaW5zcGXDp8OjbyBlIGVudGVuZGltZW50byBpbmljaWFsIGRvcyBkYWRvczsNCjIuICBsaW1wZXphLCBwYWRyb25pemHDp8OjbyBlIGp1bsOnw6NvIGRvcyBkb2lzIGRhdGFzZXRzOw0KMy4gIGNyaWHDp8OjbyBkZSBub3ZhcyB2YXJpw6F2ZWlzIHJlbGV2YW50ZXMgKGV4Ljogw61uZGljZXMgcXXDrW1pY29zLCBjYXRlZ29yaWFzIGRlIHF1YWxpZGFkZSk7DQo0LiAgYW7DoWxpc2VzIGV4cGxvcmF0w7NyaWFzIGRldGFsaGFkYXMgY29tIGZvY28gZW0gcGFkcsO1ZXMsIHJlbGHDp8O1ZXMgZSB0ZW5kw6puY2lhczsNCjUuICBpbnRlcnByZXRhw6fDo28gZG9zIHJlc3VsdGFkb3MgY29tIGFwb2lvIGRlIGdyw6FmaWNvcywgdGFiZWxhcyBlIHTDqWNuaWNhcyBkZXNjcml0aXZhcy4NCg0KIyMgKioxLjMgQWJvcmRhZ2VtIHByb3Bvc3RhIGUgdMOpY25pY2FzIGFkb3RhZGFzKioNCg0KQSBhYm9yZGFnZW0gdMOpY25pY2EgcHJvcG9zdGEgY29tYmluYSBtw6l0b2RvcyBlc3RhdMOtc3RpY29zLCBleHBsb3JhdMOzcmlvcyBlIHNlbcOibnRpY29zLiBJbmljaWFsbWVudGUsIHNlcsOjbyBhcGxpY2FkYXMgdMOpY25pY2FzIGRlICoqbGltcGV6YSBlIG9yZ2FuaXphw6fDo28gdGFidWxhcioqLCBnYXJhbnRpbmRvIGNvbnNpc3TDqm5jaWEgZGUgdGlwb3MsIHBhZHJvbml6YcOnw6NvIGRlIGNhdGVnb3JpYXMgZSB0cmF0YW1lbnRvIGRlIHZhbG9yZXMgYXVzZW50ZXMuIEVtIHNlZ3VpZGEsIGEganVuw6fDo28gZW50cmUgZGFkb3MgbGFib3JhdG9yaWFpcyBlIGRhZG9zIGRlIGhhcm1vbml6YcOnw6NvIHBlcm1pdGlyw6EgYW7DoWxpc2VzIG1haXMgcmljYXMuDQoNCj4gKipOb3RhIE1ldG9kb2zDs2dpY2E6KiogRGV2aWRvIMOgIGF1c8OqbmNpYSBkZSBjaGF2ZXMgcHJpbcOhcmlhcyBjb211bnMgZW50cmUgYXMgYmFzZXMsIHV0aWxpemEtc2UgdW1hIGFib3JkYWdlbSBkZSBhZ3J1cGFtZW50by4gKipBc3N1bWUtc2UgcXVlIHZpbmhvcyBxdWltaWNhbWVudGUgc2ltaWxhcmVzIChkbyBtZXNtbyBjbHVzdGVyKSBjb21wYXJ0aWxoYW0gbyBtZXNtbyBwb3RlbmNpYWwgZGUgaGFybW9uaXphw6fDo28gZGVzY3JpdG8gbmEgYmFzZSBkZSBwYWlyaW5ncy4qKg0KDQpBIGFuw6FsaXNlIGV4cGxvcmF0w7NyaWEgdXRpbGl6YXLDoSB2aXN1YWxpemHDp8O1ZXMsIGNvcnJlbGHDp8O1ZXMsIGNvbXBhcmHDp8O1ZXMgZW50cmUgZ3J1cG9zIGUgYW7DoWxpc2UgdGV4dHVhbCBzaW1wbGVzIHBhcmEgaWRlbnRpZmljYXIgcGFkcsO1ZXMgcmVsZXZhbnRlcy4gQWzDqW0gZGlzc28sIGFsZ3VtYXMgdMOpY25pY2FzIGRlIG1vZGVsYWdlbSBlIGludGVycHJldGHDp8OjbywgY29tbyByZWdyZXNzw7VlcywgY2xhc3NpZmljYcOnw7VlcyBlIGltcG9ydMOibmNpYSBkZSB2YXJpw6F2ZWlzLCBwb2RlcsOjbyBzZXIgYXBsaWNhZGFzIGRlIGZvcm1hIGNvbXBsZW1lbnRhciBwYXJhIHZlcmlmaWNhciBzZSBkZXRlcm1pbmFkb3MgYXRyaWJ1dG9zIHF1w61taWNvcyBwb2RlbSBwcmV2ZXIgcXVhbGlkYWRlIG91IGFkZXF1YcOnw6NvIGdhc3Ryb27DtG1pY2EuIEVtYm9yYSBhIG1vZGVsYWdlbSBuw6NvIHNlamEgbyBmb2NvIHByaW5jaXBhbCwgZWxhIGFqdWRhIGEgdmFsaWRhciB0ZW5kw6puY2lhcyBvYnNlcnZhZGFzIGUgcmVmb3LDp2EgYSByb2J1c3RleiBkb3MgaW5zaWdodHMgb2J0aWRvcy4NCg0KIyMgKioxLjQgQmVuZWbDrWNpb3MgZSBpbXBhY3RvIHBhcmEgcG90ZW5jaWFpcyBpbnRlcmVzc2Fkb3MqKg0KDQpBIGFuw6FsaXNlIHJlc3VsdGFudGUgYmVuZWZpY2lhIHbDoXJpb3MgcGVyZmlzIGRlIHVzdcOhcmlvcy4gUGFyYSAqKnByb2R1dG9yZXMgZSBlbsOzbG9nb3MqKiwgZm9ybmVjZSBldmlkw6puY2lhcyBxdWFudGl0YXRpdmFzIHNvYnJlIHF1YWlzIGF0cmlidXRvcyBxdcOtbWljb3MgbWFpcyBpbXBhY3RhbSBhIHF1YWxpZGFkZSBmaW5hbCBkbyB2aW5oby4gUGFyYSAqKnJlc3RhdXJhbnRlcywgYmFyZXMgZSBzb21tZWxpZXJzKiosIG9mZXJlY2UgdW1hIGJhc2Ugb2JqZXRpdmEgcGFyYSBzZWxlY2lvbmFyIHZpbmhvcyBlIGNyaWFyIGhhcm1vbml6YcOnw7VlcyBtYWlzIGNvbnNpc3RlbnRlcywgcmVkdXppbmRvIHRlbnRhdGl2YXMgZSBlcnJvcy4gUGFyYSAqKmNvbnN1bWlkb3JlcyoqLCBwZXJtaXRlIGVudGVuZGVyIG1lbGhvciBvIG1vdGl2byBwZWxvIHF1YWwgY2VydG9zIHZpbmhvcyBjb21iaW5hbSBjb20gZGV0ZXJtaW5hZG9zIHByYXRvcywgZmFjaWxpdGFuZG8gZXNjb2xoYXMgbWFpcyBpbmZvcm1hZGFzLiBQYXJhICoqw6FyZWFzIGRlIGRhZG9zKiosIG8gcHJvamV0byBkZW1vbnN0cmEgY29tbyBpbnRlZ3JhciBkYWRvcyBsYWJvcmF0b3JpYWlzIGUgc2Vuc29yaWFpcyBwYXJhIGdlcmFyIGluc2lnaHRzIGNvbWVyY2lhaXMgZSBnYXN0cm9uw7RtaWNvcyBhcGxpY8OhdmVpcy4NCg0KRW0gY29uanVudG8sIGVzdGEgYW7DoWxpc2UgdHJhbnNmb3JtYSBkYWRvcyBicnV0b3MgZW0gY29uaGVjaW1lbnRvIMO6dGlsLCBvcmllbnRhbmRvIGRlY2lzw7VlcyBwcsOhdGljYXMgZSBtZWxob3JhbmRvIGEgY29tcHJlZW5zw6NvIHNvYnJlIGFxdWlsbyBxdWUgdG9ybmEgdW0gdmluaG8gbWFpcyBhcHJlY2lhZG8gZSBiZW0gaGFybW9uaXphZG8uDQoNCiMgUGFjb3RlcyBSZXF1ZXJpZG9zIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KIyMgKipQYWNvdGVzIFJlcXVlcmlkb3MqKg0KDQpOZXN0YSBzZcOnw6NvIHPDo28gY2FycmVnYWRvcyB0b2RvcyBvcyBwYWNvdGVzIHV0aWxpemFkb3MgYW8gbG9uZ28gZG8gcHJvamV0bywgcGVybWl0aW5kbyBxdWUgcXVhbHF1ZXIgbGVpdG9yIHJlcHJvZHV6YSBpbnRlZ3JhbG1lbnRlIGEgYW7DoWxpc2UuIEFsw6ltIGRhIGxpc3RhZ2VtLCBjYWRhIHBhY290ZSDDqSBhY29tcGFuaGFkbyBkZSB1bWEgYnJldmUgZXhwbGljYcOnw6NvIHNvYnJlIHNldSBwcm9ww7NzaXRvIGUgc2V1IHBhcGVsIG5hcyBldGFwYXMgZGUgaW1wb3J0YcOnw6NvLCBsaW1wZXphLCBleHBsb3Jhw6fDo28gZSB2aXN1YWxpemHDp8OjbyBkb3MgZGFkb3MuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBNYW5pcHVsYcOnw6NvIGUgVHJhbnNmb3JtYcOnw6NvIGRlIERhZG9zIChPICd0aWR5dmVyc2UnIGNhcnJlZ2EgZ2dwbG90MiwgZHBseXIsIHRpZHlyLCByZWFkciwgZXRjLikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShqYW5pdG9yKSAgICAgICAgIyBQYXJhIGxpbXBlemEgZGUgbm9tZXMgZGUgY29sdW5hcyAoY2xlYW5fbmFtZXMpDQoNCiMgU3Vtw6FyaW9zIGUgVGFiZWxhcw0KbGlicmFyeShza2ltcikgICAgICAgICAgIyBQYXJhIHN1bcOhcmlvcyBlc3RhdMOtc3RpY29zIHLDoXBpZG9zIChza2ltKQ0KbGlicmFyeShrbml0cikgICAgICAgICAgIyBQYXJhIGdlcmFyIGEgdGFiZWxhIGZpbmFsIGZvcm1hdGFkYSAoa2FibGUpDQoNCiMgVmlzdWFsaXphw6fDo28gQXZhbsOnYWRhDQpsaWJyYXJ5KHBhdGNod29yaykgICAgICAjIFBhcmEgY29tYmluYXIgZ3LDoWZpY29zIChwbG90MSArIHBsb3QyKQ0KbGlicmFyeShwbG90bHkpICAgICAgICAgIyBQYXJhIHRvcm5hciBvcyBncsOhZmljb3MgaW50ZXJhdGl2b3MNCmxpYnJhcnkocmVzaGFwZTIpICAgICAgICMgUGFyYSB0cmFuc2Zvcm1hciBhIG1hdHJpeiBkZSBjb3JyZWxhw6fDo28gKG1lbHQpDQoNCiMgVGV4dG8gZSBNaW5lcmHDp8OjbyBkZSBEYWRvcw0KbGlicmFyeSh0aWR5dGV4dCkgICAgICAgIyBQYXJhIGFuw6FsaXNlIGRlIHRleHRvICh1bm5lc3RfdG9rZW5zLCBzdG9wX3dvcmRzKQ0KYGBgDQoNCiMjICoqRXhwbGljYcOnw6NvIGRvcyBwYWNvdGVzIGUgc2V1cyBvYmpldGl2b3MqKg0KDQojIyMgKipNYW5pcHVsYcOnw6NvIGUgbGltcGV6YSBkZSBkYWRvcyoqDQoNCi0gICAqKnRpZHl2ZXJzZSoqOiBQYWNvdGUgY2VudHJhbCBkbyBwcm9qZXRvLCB1c2FkbyBwYXJhICpmaWx0cmFyLCBzZWxlY2lvbmFyLCBhZ3J1cGFyLCB0cmFuc2Zvcm1hciBlIHZpc3VhbGl6YXIqIGRhZG9zLg0KLSAgICoqamFuaXRvcioqOiBGYWNpbGl0YSByb3RpbmFzIGZ1bmRhbWVudGFpcyBjb21vIHBhZHJvbml6YXIgbm9tZXMgZGUgY29sdW5hcyBlIGlkZW50aWZpY2FyIHByb2JsZW1hcyBlc3RydXR1cmFpcy4NCg0KIyMjICoqU3VtYXJpemHDp8OjbyBlIGFwcmVzZW50YcOnw6NvKioNCg0KLSAgICoqc2tpbXIqKjogR2VyYSByZXN1bW9zIGVzdGF0w61zdGljb3MgY29tcGxldG9zLCBjbGFyb3MgZSBkaXJldG9zIHBhcmEgYSBzZcOnw6NvIGRlIHByZXBhcmHDp8OjbyBkb3MgZGFkb3MuDQotICAgKiprbml0cioqOiBSZXNwb25zw6F2ZWwgcGVsYSBmdW7Dp8OjbyBrYWJsZSgpLCB1c2FkYSBuYSBjb25jbHVzw6NvIHBhcmEgcmVuZGVyaXphciB0YWJlbGFzIGRlIHJlc3VsdGFkb3MgZGUgZm9ybWEgbGltcGEgZSBjb21wYXTDrXZlbCBjb20gcXVhbHF1ZXIgZm9ybWF0byBkZSBzYcOtZGEuDQoNCiMjIyAqKlZpc3VhbGl6YcOnw6NvKioNCg0KLSAgICoqcmVzaGFwZTIqKjogVXRpbGl6YWRvIHBhcmEgYSBmdW7Dp8OjbyBtZWx0KCksIG5lY2Vzc8OhcmlhIHBhcmEgdHJhbnNmb3JtYXIgYSBtYXRyaXogZGUgY29ycmVsYcOnw6NvIGVtIHVtIGZvcm1hdG8gbG9uZ28gYWRlcXVhZG8gcGFyYSBhIGNyaWHDp8OjbyBkbyBtYXBhIGRlIGNhbG9yIChoZWF0bWFwKS4NCi0gICAqKnBhdGNod29yayoqOiBQZXJtaXRlIGNvbWJpbmFyIG3Dumx0aXBsb3MgZ3LDoWZpY29zIGVtIHVtYSDDum5pY2EgZmlndXJhIGNvZXJlbnRlLg0KLSAgICoqcGxvdGx5Kio6IFVzYWRvIHBhcmEgdG9ybmFyIGdyw6FmaWNvcyBpbnRlcmF0aXZvcywgcXVhbmRvIMO6dGlsIHBhcmEgZXhwbG9yYXIgcGFkcsO1ZXMgbWFpcyBjb21wbGV4b3MuDQoNCiMjIyAqKlRleHRvIGUgYW7DoWxpc2Ugc2Vtw6JudGljYSoqDQoNCi0gICAqKnRpZHl0ZXh0Kio6IMOadGlsIHBhcmEgYW5hbGlzYXIgYXMgZGVzY3Jpw6fDtWVzIHRleHR1YWlzIGRhcyBjb21iaW5hw6fDtWVzIHZpbmhv4oCTY29taWRhLg0KDQojIFByZXBhcmHDp8OjbyBkb3MgRGFkb3Mgey50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQpPcyBkYWRvcyB1dGlsaXphZG9zIG5lc3RlIHByb2pldG8gZm9yYW0gb2J0aWRvcyBhIHBhcnRpciBkZSB1bSBkYXRhc2V0IHDDumJsaWNvIGFtcGxhbWVudGUgdXRpbGl6YWRvIGVtIGNvbXBldGnDp8O1ZXMsIHBlc3F1aXNhcyBlIHByb2pldG9zIGVkdWNhY2lvbmFpcyBkZSBjacOqbmNpYSBkZSBkYWRvcy4gQSBmb250ZSBvcmlnaW5hbCDDqTogDQoNCi0gICBodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL3JlaGFuNDk3L3dpbmUtZGF0YXNldA0KDQpObyByZXBvc2l0w7NyaW8gZG8gZGF0YXNldCBlc3TDo28gZGlzcG9uw612ZWlzIGRvaXMgYXJxdWl2b3MgY3N2OiB3aW5lcXVhbGl0eS1yZWQuY3N2IGUgd2luZV9mb29kX3BhaXJpbmdzLmNzdg0KDQojIyBJbXBvcnRhw6fDo28NCmBgYHtyfQ0Kd2luZV9xdWFsaXR5X3JhdyA8LSByZWFkX2Nzdigid2luZXF1YWxpdHktcmVkLmNzdiIpDQpwYWlyaW5nc19yYXcgICAgIDwtIHJlYWRfY3N2KCJ3aW5lX2Zvb2RfcGFpcmluZ3MuY3N2IikNCg0KaGVhZCh3aW5lX3F1YWxpdHlfcmF3KQ0KaGVhZChwYWlyaW5nc19yYXcpDQpgYGANCg0KIyMgVmlzdWFsaXphciBlc3RydXR1cmEgcmVzdW1pZGENCmBgYHtyfQ0Kc2tpbXI6OnNraW0od2luZV9xdWFsaXR5X3JhdykNCnNraW1yOjpza2ltKHBhaXJpbmdzX3JhdykNCmBgYA0KDQojIyBMaW1wZXphDQpgYGB7cn0NCiMgTGltcGFyIG5vbWVzIGRlIGNvbHVuYXMgcGFyYSBjb25zaXN0w6puY2lhDQp3aW5lX3F1YWxpdHkgPC0gd2luZV9xdWFsaXR5X3JhdyAlPiUgY2xlYW5fbmFtZXMoKQ0KcGFpcmluZ3MgICAgIDwtIHBhaXJpbmdzX3JhdyAgICAgJT4lIGNsZWFuX25hbWVzKCkNCmBgYA0KDQojIyBKdW7Dp8OjbyBkZSB0YWJlbGFzDQoNCiMjIyAxLiBDcmlhciBJRCDDum5pY28gcGFyYSBvcyB2aW5ob3MNCmBgYHtyfQ0Kd2luZV9xdWFsaXR5IDwtIHdpbmVfcXVhbGl0eSAlPiUNCiAgbXV0YXRlKHdpbmVfaWQgPSByb3dfbnVtYmVyKCkpDQpgYGANCg0KIyMjIDIuIENyaWFyIGNsdXN0ZXJzIHF1w61taWNvcyBub3MgdmluaG9zIHRpbnRvcyAoSy1NZWFucykNCmBgYHtyfQ0KIyBTZWxlY2lvbmFyIGFwZW5hcyB2YXJpw6F2ZWlzIG51bcOpcmljYXMgcXXDrW1pY2FzDQp3aW5lX2ZlYXR1cmVzIDwtIHdpbmVfcXVhbGl0eSAlPiUNCiAgc2VsZWN0KC13aW5lX2lkKQ0KDQpzZXQuc2VlZCgxMjMpIA0KDQojIENyaWHDp8OjbyBkb3MgY2x1c3RlcnMNCmttZWFuc19tb2RlbCA8LSBrbWVhbnMoc2NhbGUod2luZV9mZWF0dXJlcyksIGNlbnRlcnMgPSA0LCBuc3RhcnQgPSAyMCkNCg0KIyBBZGljaW9uYXIgY2x1c3RlciBhbyBkYXRhc2V0IHF1w61taWNvDQp3aW5lX3F1YWxpdHkgPC0gd2luZV9xdWFsaXR5ICU+JQ0KICBtdXRhdGUoY2x1c3RlciA9IGFzLmZhY3RvcihrbWVhbnNfbW9kZWwkY2x1c3RlcikpDQpgYGANCg0KIyMjIDMuIENhbGN1bGFyIHBlcmZpcyBxdcOtbWljb3MgbcOpZGlvcyBwb3IgY2x1c3Rlcg0KYGBge3J9DQpjbHVzdGVyX3Byb2ZpbGVzIDwtIHdpbmVfcXVhbGl0eSAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBhY3Jvc3MoDQogICAgICB3aGVyZShpcy5udW1lcmljKSwNCiAgICAgIG1lYW4sDQogICAgICAubmFtZXMgPSAiYXZnX3tjb2x9Ig0KICAgICkNCiAgKQ0KYGBgIA0KDQojIyMgNC4gUGFkcm9uaXphciBjYXRlZ29yaWFzIGRvIGRhdGFzZXQgZGUgaGFybW9uaXphw6fDo28NCmBgYHtyfQ0KcGFpcmluZ3MgPC0gcGFpcmluZ3MgJT4lDQogIG11dGF0ZSh3aW5lX2NhdGVnb3J5ID0gdG9sb3dlcih3aW5lX2NhdGVnb3J5KSkNCmBgYA0KDQojIyMgNS4gRmlsdHJhciBhcGVuYXMgaGFybW9uaXphw6fDtWVzIGNvbSB2aW5obyB0aW50byAoUkVEKQ0KYGBge3J9DQpwYWlyaW5nc19yZWQgPC0gcGFpcmluZ3MgJT4lDQogIGZpbHRlcih3aW5lX2NhdGVnb3J5ID09ICJyZWQiKQ0KYGBgDQoNCiMjIyA2LiBFbmNvbnRyYXIgbyBjbHVzdGVyIHF1w61taWNvIG1haXMgcHLDs3hpbW8gZG8gcGVyZmlsIG3DqWRpbyBkb3MgdmluaG9zIHRpbnRvcw0KYGBge3J9DQojIFBlcmZpbCBxdcOtbWljbyBtw6lkaW8gZ2VyYWwgZG9zIHZpbmhvcyB0aW50b3MgZG8gZGF0YXNldCBVQ0kNCnJlZF9wcm9maWxlIDwtIHdpbmVfcXVhbGl0eSAlPiUNCiAgc3VtbWFyaXNlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgbWVhbikpDQoNCiMgQ2FsY3VsYXIgZGlzdMOibmNpYSBldWNsaWRpYW5hIGVudHJlIHBlcmZpbCBSRUQgZSBjbHVzdGVycw0KY2x1c3Rlcl9kaXN0IDwtIGNsdXN0ZXJfcHJvZmlsZXMgJT4lDQogIHJvd3dpc2UoKSAlPiUNCiAgbXV0YXRlKA0KICAgIGRpc3QgPSBzcXJ0KHN1bSgNCiAgICAgIChjX2Fjcm9zcyhzdGFydHNfd2l0aCgiYXZnXyIpKSAtDQogICAgICAgICBhcy5udW1lcmljKHJlZF9wcm9maWxlKSleMg0KICAgICkpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojIENsdXN0ZXIgbWFpcyByZXByZXNlbnRhdGl2byBkbyB2aW5obyB0aW50bw0KYmVzdF9jbHVzdGVyX3JlZCA8LSBjbHVzdGVyX2Rpc3QgJT4lDQogIHNsaWNlX21pbihkaXN0KSAlPiUNCiAgcHVsbChjbHVzdGVyKQ0KDQpiZXN0X2NsdXN0ZXJfcmVkDQpgYGANCg0KIyMgRGF0YXNldCBmaW5hbA0KDQojIyMgNy4gU2VsZWNpb25hciBhcGVuYXMgdmluaG9zIGN1am8gY2x1c3RlciByZXByZXNlbnRhIHZpbmhvcyB0aW50b3MNCmBgYHtyfQ0Kd2luZV9jbHVzdGVyX3JlZCA8LSB3aW5lX3F1YWxpdHkgJT4lDQogIGZpbHRlcihjbHVzdGVyID09IGJlc3RfY2x1c3Rlcl9yZWQpDQpgYGANCg0KIyMjIDguIEpvaW4gY2FydGVzaWFubyBlbnRyZSB2aW5ob3MgdGludG9zIChjbHVzdGVyIGNvcnJldG8pIGUgaGFybW9uaXphw6fDtWVzIFJFRA0KYGBge3J9DQojIFJlYWxpemEgbyBjcnV6YW1lbnRvIChjcm9zcyBqb2luKSBlbnRyZSBvcyB2aW5ob3MgZG8gY2x1c3RlciByZXByZXNlbnRhdGl2byBlIGFzIGhhcm1vbml6YcOnw7Vlcy4NCiMgUHJlbWlzc2E6IEFzc3VtZS1zZSBxdWUgdmluaG9zIHF1aW1pY2FtZW50ZSBzaW1pbGFyZXMgKGRvIG1lc21vIGNsdXN0ZXIpIA0KIyBjb21wYXJ0aWxoYW0gbyBtZXNtbyBwb3RlbmNpYWwgZGUgaGFybW9uaXphw6fDo28gZGVzY3JpdG8gbmEgYmFzZSBkZSBwYWlyaW5ncy4NCmRhdGFzZXRfZmluYWwgPC0gdGlkeXI6OmNyb3NzaW5nKHdpbmVfY2x1c3Rlcl9yZWQsIHBhaXJpbmdzX3JlZCkgJT4lDQogIG11dGF0ZShwYWlyaW5nX2lkID0gcm93X251bWJlcigpKQ0KDQpkYXRhc2V0X2ZpbmFsIDwtIGRhdGFzZXRfZmluYWwgJT4lIA0KICBzZWxlY3QocmV2KGNvbG5hbWVzKC4pKSkNCmBgYA0KDQojIyMgOS4gVmlzdWFsaXphw6fDo28gZG8gZGF0YXNldCBmaW5hbA0KYGBge3J9DQpoZWFkKGRhdGFzZXRfZmluYWwsIDEwKQ0KDQpza2ltcjo6c2tpbShkYXRhc2V0X2ZpbmFsKQ0KYGBgDQoNCiMgQW7DoWxpc2UgZXhwbG9yYXTDs3JpYSBkb3MgZGFkb3Mgey50YWJzZXQgLnRhYnNldC1mYWRlfSANCg0KTmVzdGEgc2XDp8OjbywgZm9jYW1vcyBlbSBlbnRlbmRlciBhIGRpc3RyaWJ1acOnw6NvIGRhcyB2YXJpw6F2ZWlzIGbDrXNpY28tcXXDrW1pY2FzIGRvcyB2aW5ob3Mgc2VsZWNpb25hZG9zIChkbyBjbHVzdGVyIHJlcHJlc2VudGF0aXZvKS4NCg0KIyMgRURBIFF1w61taWNhDQoNCiMjIyAxLiBWaXPDo28gZ2VyYWwgZGFzIGRpc3RyaWJ1acOnw7VlcyAoSGlzdG9ncmFtYXMpDQpgYGB7cn0NCiMgUGl2b3RhbW9zIG9zIGRhZG9zIHBhcmEgZm9ybWF0byBsb25nbyBwYXJhIGZhY2lsaXRhciBvIHVzbyBkbyBmYWNldF93cmFwDQp3aW5lX2NsdXN0ZXJfcmVkICU+JQ0KICBzZWxlY3QoZml4ZWRfYWNpZGl0eSwgdm9sYXRpbGVfYWNpZGl0eSwgY2l0cmljX2FjaWQsIHJlc2lkdWFsX3N1Z2FyLCANCiAgICAgICAgIGNobG9yaWRlcywgYWxjb2hvbCwgcF9oLCBzdWxwaGF0ZXMpICU+JQ0KICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLCBuYW1lc190byA9ICJ2YXJpYXZlbCIsIHZhbHVlc190byA9ICJ2YWxvciIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB2YWxvcikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBmaWxsID0gIiM4MDAwMjAiLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC44KSArICMgQ29yIHZpbmhvDQogIGZhY2V0X3dyYXAofnZhcmlhdmVsLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidWljYW8gZGFzIFZhcmlhdmVpcyBRdWltaWNhcyAoQ2x1c3RlciBTZWxlY2lvbmFkbykiLA0KICAgICAgIHN1YnRpdGxlID0gIkNhcmFjdGVyaXN0aWNhcyBmaXNpY28tcXVpbWljYXMgcHJlZG9taW5hbnRlcyBub3MgdmluaG9zIGFuYWxpc2Fkb3MiLA0KICAgICAgIHkgPSAiRnJlcXVlbmNpYSIsIHggPSAiIikNCmBgYA0KDQojIyMgMi4gTWF0cml6IGRlIENvcnJlbGHDp8OjbyBRdcOtbWljYQ0KYGBge3J9DQojIEVudGVuZGVyIGNvbW8gYXMgdmFyacOhdmVpcyBxdcOtbWljYXMgY29udmVyc2FtIGVudHJlIHNpDQpjb3JfbWF0cml4IDwtIHdpbmVfY2x1c3Rlcl9yZWQgJT4lDQogIHNlbGVjdCh3aGVyZShpcy5udW1lcmljKSwgLXdpbmVfaWQpICU+JQ0KICBjb3IoKQ0KDQojIFRyYW5zZm9ybWFyIHBhcmEgZm9ybWF0byBsb25nbyBwYXJhIGdncGxvdA0KY29yX21lbHRlZCA8LSBtZWx0KGNvcl9tYXRyaXgpDQoNCmdncGxvdChjb3JfbWVsdGVkLCBhZXMoVmFyMSwgVmFyMiwgZmlsbCA9IHZhbHVlKSkgKw0KICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiLCBtaWQgPSAid2hpdGUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSAwLCBsaW1pdCA9IGMoLTEsMSksIG5hbWU9IkNvcnJlbGFjYW8iKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSkgKw0KICBsYWJzKHRpdGxlID0gIkNvcnJlbGFjYW8gZW50cmUgQXRyaWJ1dG9zIFF1aW1pY29zIiwNCiAgICAgICB4ID0gIiIsIHkgPSAiIikNCmBgYA0KDQojIyBFREEgUGFyZWFtZW50b3MNCg0KQXF1aSBhbmFsaXNhbW9zIG8gdGV4dG8gZSBhcyBub3RhcyBkYXMgaGFybW9uaXphw6fDtWVzLiBVc2FyZW1vcyBvIHRpZHl0ZXh0IHBhcmEgdmVyIHF1YWlzIGluZ3JlZGllbnRlcyBhcGFyZWNlbSBtYWlzLg0KDQojIyMgMS4gUGFsYXZyYXMgbWFpcyBmcmVxdWVudGVzIG5vcyBwcmF0b3MgKEZvb2QpDQpgYGB7cn0NCmZvb2RfdG9rZW5zIDwtIHBhaXJpbmdzX3JlZCAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBmb29kX2l0ZW0pICU+JSAgDQogIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUgICAgICAgICAgICMgUmVtb3ZlICdhbmQnLCAnd2l0aCcsICd0aGUnDQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUNCiAgaGVhZCgxNSkNCg0KIyAyLiBDcmlhciBvIGdyw6FmaWNvDQpwbG90X2Zvb2QgPC0gZm9vZF90b2tlbnMgJT4lDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSB3b3JkKSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiM4MDAwMjAiKSArICAgICAgICAjIENvciB2aW5obyB0aW50bw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCAxNSBUZXJtb3MgZW0gUHJhdG9zIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJJbmdyZWRpZW50ZXMgbWFpcyBmcmVxdWVudGVzIG5hcyBoYXJtb25pemFjb2VzIiwNCiAgICAgICB4ID0gIkZyZXF1ZW5jaWEiLCB5ID0gIiIpDQpgYGANCg0KIyMjIDIuIERpc3RyaWJ1acOnw6NvIGRhcyBub3RhcyBkZSBoYXJtb25pemHDp8OjbyAoUGFpcmluZyBSYXRpbmcpDQpgYGB7cn0NCiMgMS4gQWp1c3RhciBvIGdyw6FmaWNvIGRlIFJhdGluZ3MgDQpwbG90X3JhdGluZ3MgPC0gcGFpcmluZ3NfcmVkICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwYWlyaW5nX3F1YWxpdHkpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMCwgZmlsbCA9ICIjRTY5RjAwIiwgY29sb3IgPSAid2hpdGUiKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1aWNhbyBkYXMgTm90YXMiLA0KICAgICAgIHN1YnRpdGxlID0gIkF2YWxpYWNhbyBkYSBxdWFsaWRhZGUgZGEgaGFybW9uaXphY2FvIiwNCiAgICAgICB4ID0gIk5vdGEgKFBhaXJpbmcgUXVhbGl0eSkiLCANCiAgICAgICB5ID0gIkNvbnRhZ2VtIikNCg0KIyAyLiBFeGliaXIgbGFkbyBhIGxhZG8gDQpwbG90X2Zvb2QgKyBwbG90X3JhdGluZ3MNCmBgYA0KDQojIyBSZWxhw6fDtWVzIGVudHJlIHF1w61taWNhIGUgZm9vZCBwYWlyaW5nDQoNCiMjIyAxLiBQZXJmaWwgUXXDrW1pY28gZG8gQ2x1c3RlciAoTyBWaW5obykNCmBgYHtyfQ0KIyB3aW5lX2NsdXN0ZXJfcmVkIHBhcmEgZXZpdGFyIHJlY2FsY3VsYXIgYSBtZXNtYSBtw6lkaWEgbWlsaGFyZXMgZGUgdmV6ZXMNCnN0YXRzX3N1bW1hcnkgPC0gd2luZV9jbHVzdGVyX3JlZCAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIEFsY29ob2wgPSBtZWFuKGFsY29ob2wsIG5hLnJtID0gVFJVRSksDQogICAgYFJlcy4gU3VnYXJgID0gbWVhbihyZXNpZHVhbF9zdWdhciwgbmEucm0gPSBUUlVFKSwNCiAgICBwSCA9IG1lYW4ocF9oLCBuYS5ybSA9IFRSVUUpLCAgICAgICAgIA0KICAgIFN1bHBoYXRlcyA9IG1lYW4oc3VscGhhdGVzLCBuYS5ybSA9IFRSVUUpDQogICkgJT4lDQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIkF0cmlidXRvIiwgdmFsdWVzX3RvID0gIk1lZGlhIikNCg0KZ3JhZmljb19wZXJmaWwgPC0gZ2dwbG90KHN0YXRzX3N1bW1hcnksIGFlcyh4ID0gQXRyaWJ1dG8sIHkgPSBNZWRpYSwgZmlsbCA9IEF0cmlidXRvKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChNZWRpYSwgMikpLCB2anVzdCA9IC0wLjUpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJQZXJmaWwgUXVpbWljbyBNZWRpbyIsDQogICAgICAgc3VidGl0bGUgPSAiQ2FyYWN0ZXJpc3RpY2FzIGRvIGNsdXN0ZXIgc2VsZWNpb25hZG8iLA0KICAgICAgIHkgPSAiVmFsb3IgTWVkaW8iLCB4ID0gIiIpDQoNCiMgRXhpYmlyIG8gZ3LDoWZpY28gZGUgYmFycmFzIChQZXJmaWwgUXXDrW1pY28pDQpncmFmaWNvX3BlcmZpbA0KYGBgDQoNCiMjIyAyLiBBbsOhbGlzZSBkZSBCaWdyYW1hcw0KYGBge3J9DQojIEFuw6FsaXNlIGRlIEJpZ3JhbWFzOiBJZGVudGlmaWNhciBwYXJlcyBkZSBwYWxhdnJhcyBxdWUgYXBhcmVjZW0ganVudG9zDQpiaWdyYW1zX2FuYWx5c2lzIDwtIHBhaXJpbmdzX3JlZCAlPiUNCiAgIyAxLiBRdWVicmFyIG8gdGV4dG8gZW0gcGFyZXMgZGUgcGFsYXZyYXMgKG5ncmFtcywgbj0yKQ0KICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgZGVzY3JpcHRpb24sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUNCiAgDQogICMgMi4gU2VwYXJhciBwYXJhIGxpbXBhciAic3RvcCB3b3JkcyIgKHRoZSwgYW5kLCB3aXRoKQ0KICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lDQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhaXMubmEod29yZDEpLCAhaXMubmEod29yZDIpKSAlPiUgIyBSZW1vdmVyIE5Bcw0KICANCiAgIyAzLiBDb250YXIgZSByZXVuaXIgbm92YW1lbnRlDQogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQ0KICB1bml0ZShiaWdyYW0sIHdvcmQxLCB3b3JkMiwgc2VwID0gIiAiKSAlPiUNCiAgDQogICMgNC4gUGVnYXIgb3MgdG9wIDE1IHBhcmVzDQogIGhlYWQoMTUpDQoNCiMgVmlzdWFsaXphw6fDo28NCmJpZ3JhbXNfYW5hbHlzaXMgJT4lDQogIG11dGF0ZShiaWdyYW0gPSByZW9yZGVyKGJpZ3JhbSwgbikpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBuLCB5ID0gYmlncmFtKSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiM0RTJBODQiLCBhbHBoYSA9IDAuOCkgKyAjIFVtIHJveG8gZXNjdXJvIChyZWZlcsOqbmNpYSBhIHV2YXMpDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgaGp1c3QgPSAtMC4yLCBzaXplID0gMy41LCBjb2xvciA9ICJncmF5MzAiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiQ29udGV4dG8gR2FzdHJvbm9taWNvOiBQcmluY2lwYWlzIFBhcmVzIGRlIFRlcm1vcyIsDQogICAgICAgc3VidGl0bGUgPSAiQmlncmFtYXMgbWFpcyBmcmVxdWVudGVzIG5hcyBkZXNjcmljb2VzIGRlIGhhcm1vbml6YWNhbyIsDQogICAgICAgeCA9ICJGcmVxdWVuY2lhIiwNCiAgICAgICB5ID0gIiIpICsNCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKSANCmBgYA0KDQojIyBHcsOhZmljb3MgYWp1c3RhZG9zDQpgYGB7cn0NCiMgUHJlcGFyYXIgbyBncsOhZmljbyBiYXNlIGNvbSBnZ3Bsb3QyDQpwIDwtIHdpbmVfY2x1c3Rlcl9yZWQgJT4lDQogIG11dGF0ZShxdWFsaXR5X2ZhY3RvciA9IGFzLmZhY3RvcihxdWFsaXR5KSkgJT4lDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBhbGNvaG9sLCB5ID0gc3VscGhhdGVzLCBjb2xvciA9IHF1YWxpdHlfZmFjdG9yKSkgKw0KICANCiAgIyBUb29sdGlwIFRPVEFMTUVOVEUgc2VtIGFjZW50b3Mgb3UgY2FyYWN0ZXJlcyBlc3BlY2lhaXMNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgc2l6ZSA9IDIsDQogICAgICAgICAgICAgYWVzKHRleHQgPSBwYXN0ZTAoIklEIFZpbmhvOiAiLCB3aW5lX2lkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+UXVhbGlkYWRlOiAiLCBxdWFsaXR5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+QWxjb29sOiAiLCBhbGNvaG9sLCAiJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5TdWxmYXRvczogIiwgc3VscGhhdGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+cEg6ICIsIHBfaCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPkFjdWNhciBSZXMuOiAiLCByZXNpZHVhbF9zdWdhcikpKSArDQogIA0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gIm1hZ21hIiwgYmVnaW4gPSAwLjEsIGVuZCA9IDAuOSwgZGlyZWN0aW9uID0gLTEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJRdWFsaWRhZGUiKSArDQogIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICAjIFTDrXR1bG9zIGUgZWl4b3MgU0VNIGFjZW50b3MgZSBTRU0gbyAnwrMnICh1c2FuZG8gZG0zKQ0KICBsYWJzKHRpdGxlID0gIkludGVyYWNhbyBNdWx0aXZhcmlhZGE6IEFsY29vbCB4IFN1bGZhdG9zIHggUXVhbGlkYWRlIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJFeHBsb3JlIG9zIGRhZG9zIHBhc3NhbmRvIG8gbW91c2Ugc29icmUgb3MgcG9udG9zIiwNCiAgICAgICB4ID0gIlRlb3IgQWxjb29saWNvICglIHZvbCkiLA0KICAgICAgIHkgPSAiU3VsZmF0b3MgKGcvZG0zKSIpDQoNCiMgQ29udmVydGVyIHBhcmEgaW50ZXJhdGl2bw0KZ2dwbG90bHkocCwgdG9vbHRpcCA9ICJ0ZXh0IikNCmBgYA0KDQojIENvbmNsdXPDo28gey50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQojIyBJbnNpZ2h0cyBmaW5haXMNCg0KQSBhbsOhbGlzZSBpbnRlZ3JhZGEgZW50cmUgYXMgcHJvcHJpZWRhZGVzIGbDrXNpY28tcXXDrW1pY2FzIGUgb3MgZGFkb3MgZGUgaGFybW9uaXphw6fDo28gcGVybWl0aXUgdHJhw6dhciBvIHBlcmZpbCBkbyAiVmluaG8gVGludG8gUmVwcmVzZW50YXRpdm8iIGUgc2V1IGNvbnRleHRvIGdhc3Ryb27DtG1pY28gaWRlYWwuDQoNCkF0cmF2w6lzIGRhIGNsdXN0ZXJpemHDp8OjbywgaXNvbGFtb3MgdW0gZ3J1cG8gZGUgdmluaG9zIGNvbSBjYXJhY3RlcsOtc3RpY2FzIHF1w61taWNhcyBjb25zaXN0ZW50ZXMuIENydXphbmRvIGVzdGUgcGVyZmlsIGNvbSBhIGFuw6FsaXNlIHRleHR1YWwsIGlkZW50aWZpY2Ftb3MgcXVlIGEgZXN0cnV0dXJhIHF1w61taWNhIGRlc3RlIGdydXBvIChkZWZpbmlkYSBwb3IgYWNpZGV6LCDDoWxjb29sIGUgc3VsZmF0b3MpIGZhdm9yZWNlIGZvcnRlbWVudGUgcHJhdG9zIHJpY29zIGVtIHByb3Rlw61uYXMgZSBnb3JkdXJhcy4NCg0KQWJhaXhvLCBhcHJlc2VudGFtb3MgbyAqKiJQZXJmaWwgZG8gQXJxdcOpdGlwbyIqKjogdW0gcmVzdW1vIGVzdGF0w61zdGljbyBkbyB2aW5obyBhbmFsaXNhZG8gcXVlIHNlcnZlIGNvbW8gYmFzZSBwYXJhIGFzIHJlY29tZW5kYcOnw7Vlcy4NCg0KYGBge3J9DQojIFRhYmVsYSByZXN1bW8gZmluYWwNCiMgQ3JpYSB1bSAiU2NvcmVjYXJkIiBkbyB2aW5obyBhbmFsaXNhZG8gcGFyYSBlbmNlcnJhciBvIHJlbGF0w7NyaW8NCnN1bW1hcnlfdGFibGUgPC0gd2luZV9jbHVzdGVyX3JlZCAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGDDgWxjb29sIE3DqWRpbyAoJSlgID0gbWVhbihhbGNvaG9sKSwNCiAgICBgQWNpZGV6IFZvbMOhdGlsYCA9IG1lYW4odm9sYXRpbGVfYWNpZGl0eSksDQogICAgYEHDp8O6Y2FyIFJlc2lkdWFsYCA9IG1lYW4ocmVzaWR1YWxfc3VnYXIpLA0KICAgIGBwSCBNw6lkaW9gID0gbWVhbihwX2gpLA0KICAgIGBRdWFsaWRhZGUgTcOpZGlhYCA9IG1lYW4ocXVhbGl0eSkNCiAgKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiTcOpdHJpY2EiLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KDQojIFJlbmRlcml6YXIgdGFiZWxhIHNpbXBsZXMgZSBlbGVnYW50ZQ0Ka25pdHI6OmthYmxlKHN1bW1hcnlfdGFibGUsIA0KICAgICAgICAgICAgIGRpZ2l0cyA9IDIsIA0KICAgICAgICAgICAgIGNhcHRpb24gPSAiUmVzdW1vIGRvIFBlcmZpbCBRdcOtbWljbzogTcOpdHJpY2FzIGNlbnRyYWlzIGRvIGNsdXN0ZXIgYW5hbGlzYWRvIiwNCiAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCJNw6l0cmljYSIsICJWYWxvciBNw6lkaW8iKSkNCmBgYA0KDQojIyMgUHJpbmNpcGFpcyBkZXNjb2JlcnRhczoNCg0KQSBRdcOtbWljYSBkYSBRdWFsaWRhZGU6IEV4aXN0ZSB1bWEgY29ycmVsYcOnw6NvIHBvc2l0aXZhIG9ic2VydsOhdmVsIGVudHJlIG8gdGVvciBhbGNvw7NsaWNvIGUgYSBub3RhIGRlIHF1YWxpZGFkZS4gVmluaG9zIGNvbSBtYWlvciBjb3JwbyAow6FsY29vbCBtYWlzIGVsZXZhZG8pIGUgZXF1aWzDrWJyaW8gZGUgYWNpZGV6IHRlbmRlbSBhIHJlY2ViZXIgYXZhbGlhw6fDtWVzIG1lbGhvcmVzLg0KDQpPIENvbnRleHRvIEdhc3Ryb27DtG1pY286IEEgYW7DoWxpc2UgZGUgYmlncmFtYXMgcmV2ZWxvdSBxdWUgZXN0ZSBwZXJmaWwgZGUgdmluaG8gbsOjbyDDqSBjb25zdW1pZG8gaXNvbGFkYW1lbnRlLiBUZXJtb3MgY29tbyAidG9tYXRvIHNhdWNlIiwgImdyaWxsZWQgbWVhdCIgZSAiYmxhY2sgcGVwcGVyIiBkb21pbmFtIGFzIGRlc2NyacOnw7VlcywgaW5kaWNhbmRvIHF1ZSBhIGFjaWRleiBlIG8gw6FsY29vbCBkZXN0ZXMgdmluaG9zIHPDo28gZnVuY2lvbmFpcyBwYXJhICJsaW1wYXIiIG8gcGFsYWRhciBlbSBwcmF0b3MgdW50dW9zb3MuDQoNCiMjIEltcGxpY2HDp8O1ZXMNCg0KT3MgcmVzdWx0YWRvcyBkZXN0YSBhbsOhbGlzZSBleHBsb3JhdMOzcmlhIHRyYXplbSBpbXBsaWNhw6fDtWVzIHByw6F0aWNhcyBwYXJhIGRpZmVyZW50ZXMgc3Rha2Vob2xkZXJzOg0KDQoqKlBhcmEgUHJvZHV0b3JlcyoqOiBBIGV2aWTDqm5jaWEgZGUgcXVlIGNlcnRvcyBwYXLDom1ldHJvcyBxdcOtbWljb3MgKGNvbW8gbyBlcXVpbMOtYnJpbyBwSC/DoWxjb29sKSBlc3TDo28gbGlnYWRvcyDDoCBwZXJjZXDDp8OjbyBkZSBxdWFsaWRhZGUgcGVybWl0ZSBhanVzdGVzIGZpbm9zIG5vIHByb2Nlc3NvIGRlIHZpbmlmaWNhw6fDo28gcGFyYSBhdGluZ2lyIG5vdGFzIHNlbnNvcmlhaXMgbWFpcyBhbHRhcy4NCg0KKipQYXJhIFJlc3RhdXJhbnRlcyBlIFNvbW1lbGllcnMqKjogTyBlc3R1ZG8gdmFsaWRhLCBjb20gZGFkb3MsIGEgcHLDoXRpY2EgZW1ww61yaWNhLiBFbSB2ZXogZGUgcmVjb21lbmRhciB2aW5ob3MgYmFzZWFuZG8tc2UgYXBlbmFzIG5hIGludHVpw6fDo28sIGVzdGFiZWxlY2ltZW50b3MgcG9kZW0gY3JpYXIgY2FydGFzIGRlIHZpbmhvcyBiYXNlYWRhcyBlbSBjbHVzdGVycyBxdcOtbWljb3MgcXVlIGNvbXByb3ZhZGFtZW50ZSBoYXJtb25pemFtIGNvbSBhcyBjYXRlZ29yaWFzIGRlIHByYXRvcyBtYWlzIHBlZGlkb3MgKGNhcm5lcywgbW9saG9zIHZlcm1lbGhvcykuDQoNCioqUGFyYSBvIFZhcmVqbyoqOiBBIGNyaWHDp8OjbyBkZSBnw7RuZG9sYXMgb3JnYW5pemFkYXMgbsOjbyBwb3IgdXZhLCBtYXMgcG9yICJQZXJmaWwgZGUgSGFybW9uaXphw6fDo28iIChleDogIlZpbmhvcyBwYXJhIENhcm5lcyBHcmVsaGFkYXMiKSwgYmFzZWFkYSBub3MgZGFkb3MgZGUgcGFpcmluZ3MsIHBvZGUgc2ltcGxpZmljYXIgYSBqb3JuYWRhIGRlIGNvbXByYSBkbyBjb25zdW1pZG9yIGxlaWdvLg0KDQojIyBMaW1pdGHDp8O1ZXMNCg0KQXBlc2FyIGRvcyBpbnNpZ2h0cyBnZXJhZG9zLCDDqSBjcnVjaWFsIHJlY29uaGVjZXIgYXMgbGltaXRhw6fDtWVzIG1ldG9kb2zDs2dpY2FzIGltcG9zdGFzIHBlbGEgbmF0dXJlemEgZG9zIGRhZG9zOg0KDQoqKkEgIkFzc3Vuw6fDo28gZGUgSG9tb2dlbmVpZGFkZSIqKjogTyB1c28gZGUgSy1NZWFucyBzZWd1aWRvIGRlIHVtIENyb3NzIEpvaW4gYXNzdW1lIHF1ZSB0b2RvcyBvcyB2aW5ob3MgZGVudHJvIGRlIHVtIGNsdXN0ZXIgcXXDrW1pY28gY29tcG9ydGFtLXNlIGRlIG1hbmVpcmEgaWTDqm50aWNhIG5hIGhhcm1vbml6YcOnw6NvLiBOYSByZWFsaWRhZGUsIG51YW5jZXMgc3V0aXMgKHRhbmlub3MsIG5vdGFzIGRlIGNhcnZhbGhvKSBxdWUgbsOjbyBlc3TDo28gcHJlc2VudGVzIG5hcyB2YXJpw6F2ZWlzIHF1w61taWNhcyBwb2RlbSBhbHRlcmFyIGRyYXN0aWNhbWVudGUgYSBoYXJtb25pemHDp8Ojby4NCg0KKipHZW5lcmFsaXphw6fDo28gZGEgQ2F0ZWdvcmlhICJSZWQiKio6IE8gZGF0YXNldCBkZSBoYXJtb25pemHDp8OjbyBhZ3J1cGEgdmluaG9zIGFwZW5hcyBjb21vICJSZWQiLiBJc3NvIGlnbm9yYSBhcyBkaWZlcmVuw6dhcyBtYXNzaXZhcyBlbnRyZSB1bSBQaW5vdCBOb2lyIChsZXZlKSBlIHVtIENhYmVybmV0IFNhdXZpZ25vbiAoZW5jb3JwYWRvKSwgcXVlIHF1aW1pY2FtZW50ZSBzw6NvIGRpc3RpbnRvcyBlIHBlZGVtIHByYXRvcyBkaWZlcmVudGVzLg0KDQoqKlZpw6lzIGRlIFNlbGXDp8OjbyoqOiBPcyBkYWRvcyBkZSBxdWFsaWRhZGUgc8OjbyBwcm92ZW5pZW50ZXMgZGUgdW1hIHJlZ2nDo28gZXNwZWPDrWZpY2EgKFZpbmhvIFZlcmRlL1BvcnR1Z2FsIHBhcmEgYSBiYXNlIHF1w61taWNhKSwgZW5xdWFudG8gYXMgaGFybW9uaXphw6fDtWVzIHBvZGVtIHNlciBnbG9iYWlzLiBFeHRyYXBvbGFyIGVzc2FzIGNvbmNsdXPDtWVzIHBhcmEgdmluaG9zIGRlIHRlcnJvaXJzIG11aXRvIGRpZmVyZW50ZXMgKGNvbW8gTmFwYSBWYWxsZXkgb3UgTWVuZG96YSkgZXhpZ2UgY2F1dGVsYS4NCg0KIyMgUHLDs3hpbW9zIHBhc3Nvcw0KDQpQYXJhIGV2b2x1aXIgZXN0ZSBlc3R1ZG8gZGUgdW1hIGFuw6FsaXNlIGV4cGxvcmF0w7NyaWEgcGFyYSB1bSBzaXN0ZW1hIGRlIHJlY29tZW5kYcOnw6NvIHJvYnVzdG8sIHN1Z2VyZW0tc2UgYXMgc2VndWludGVzIGV0YXBhczoNCg0KKipNb2RlbGFnZW0gUHJlZGl0aXZhIChNYWNoaW5lIExlYXJuaW5nKSoqOiBUcmVpbmFyIHVtIG1vZGVsbyBkZSBjbGFzc2lmaWNhw6fDo28gKFJhbmRvbSBGb3Jlc3Qgb3UgWEdCb29zdCkgcGFyYSBwcmV2ZXIgYSBub3RhIGRhIGhhcm1vbml6YcOnw6NvIChwYWlyaW5nX3F1YWxpdHkpIGNvbSBiYXNlIG5hcyB2YXJpw6F2ZWlzIHF1w61taWNhcy4NCg0KKipFbnJpcXVlY2ltZW50byBkZSBEYWRvcyoqOiBJbnRlZ3JhciB1bWEgYmFzZSBkZSBkYWRvcyBxdWUgY29udGVuaGEgYSB1dmEgKHZhcmlldGFsKSBlc3BlY8OtZmljYSBub3MgZGFkb3MgcXXDrW1pY29zLiBJc3NvIHBlcm1pdGlyaWEgam9pbnMgbWFpcyBwcmVjaXNvcyBkbyBxdWUgYSBjbHVzdGVyaXphw6fDo28gZ2Vuw6lyaWNhLg0KDQoqKkFuw6FsaXNlIGRlIFNlbnRpbWVudG8qKjogQXBsaWNhciB0w6ljbmljYXMgYXZhbsOnYWRhcyBkZSBOTFAgKFByb2Nlc3NhbWVudG8gZGUgTGluZ3VhZ2VtIE5hdHVyYWwpIG5hcyBkZXNjcmnDp8O1ZXMgY29tcGxldGFzIHBhcmEgY2FwdHVyYXIgc2VudGltZW50b3Mgc3ViamV0aXZvcyAoImRlbGljaW9zbyIsICLDoWNpZG8gZGVtYWlzIikgcXVlIGVzY2FwYW0gZGEgY29udGFnZW0gc2ltcGxlcyBkZSBwYWxhdnJhcy4NCg0KIyBGb3JtYXRhw6fDo28gZSBvdXRyb3MgcmVxdWlzaXRvcyANCg0KIyMgRXN0aWxvIGRlIGPDs2RpZ28gey50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQpPIGPDs2RpZ28gZGVzdGUgcHJvamV0byBmb2kgZGVzZW52b2x2aWRvIHNlZ3VpbmRvIGFzIGRpcmV0cml6ZXMgZG8gKioiVGlkeXZlcnNlIFN0eWxlIEd1aWRlIioqLCBwcmlvcml6YW5kbyBhIGxlZ2liaWxpZGFkZSBodW1hbmEgZSBhIGVmaWNpw6puY2lhIGNvbXB1dGFjaW9uYWwuIE9zIHByaW5jaXBhaXMgcGlsYXJlcyBhZG90YWRvcyBmb3JhbToNCg0KKiAqKk9wZXJhZG9yIFBpcGUgKGAlPiVgKToqKiBVdGlsaXphZG8gZXh0ZW5zaXZhbWVudGUgcGFyYSBlbmNhZGVhciBvcGVyYcOnw7VlcywgdG9ybmFuZG8gbyBmbHV4byBkZSBtYW5pcHVsYcOnw6NvIGRlIGRhZG9zIGxpbmVhciBlIGbDoWNpbCBkZSBsZXIgKGRhIGVzcXVlcmRhIHBhcmEgYSBkaXJlaXRhLCBkZSBjaW1hIHBhcmEgYmFpeG8pLg0KDQoqICoqTm9tZW5jbGF0dXJhIFNuYWtlIENhc2U6KiogVG9kYXMgYXMgdmFyacOhdmVpcyBlIG5vbWVzIGRlIGNvbHVuYXMgZm9yYW0gcGFkcm9uaXphZG9zIHBhcmEgYHNuYWtlX2Nhc2VgIChtaW7DunNjdWxhcyBzZXBhcmFkYXMgcG9yIHN1YmxpbmhhZG8pIHV0aWxpemFuZG8gYSBmdW7Dp8OjbyBgamFuaXRvcjo6Y2xlYW5fbmFtZXMoKWAgbG9nbyBuYSBpbmdlc3TDo28sIGV2aXRhbmRvIGVycm9zIGRlIGRpZ2l0YcOnw6NvIGUgcHJvYmxlbWFzIGNvbSBlc3Bhw6dvcyBvdSBjYXJhY3RlcmVzIGVzcGVjaWFpcy4NCg0KKiAqKlZlcmJvc2lkYWRlIEV4cGzDrWNpdGE6KiogUHJlZmVyw6puY2lhIHBvciB2ZXJib3MgZGVzY3JpdGl2b3MgKGBtdXRhdGVgLCBgZmlsdGVyYCwgYHNlbGVjdGAsIGBncm91cF9ieWApIGVtIHZleiBkZSBpbmRleGHDp8OjbyBudW3DqXJpY2EgKGV4OiBgZGZbLDFdYCksIGdhcmFudGluZG8gcXVlIG8gY8OzZGlnbyBleHBsaXF1ZSAqbyBxdWUqIGVzdMOhIGZhemVuZG8uDQoNCiMjIEVzdHJ1dHVyYXMgZGUgZGFkb3MgdXNhZGFzDQoNCkFiYWl4bywgaW5zcGVjaW9uYW1vcyBhcyBlc3RydXR1cmFzIGZ1bmRhbWVudGFpcyBxdWUgc3VzdGVudGFtIG5vc3NhIGFuw6FsaXNlLiBPIHByb2pldG8gYmFzZW91LXNlIHByaW5jaXBhbG1lbnRlIGVtICoqVGliYmxlcyoqIChkYXRhZnJhbWVzIG1vZGVybm9zIGRvIFIpIGUgKipGYXRvcmVzKiogcGFyYSB2YXJpw6F2ZWlzIGNhdGVnw7NyaWNhcyBlIGNsdXN0ZXJzLg0KDQpgYGB7cn0NCiMgVmVyaWZpY2HDp8OjbyB0w6ljbmljYSBkYXMgY2xhc3NlcyBkYXMgY29sdW5hcyBkbyBkYXRhc2V0IGZpbmFsDQojIERlbW9uc3RyYSBjb250cm9sZSBzb2JyZSBvcyB0aXBvcyBkZSBkYWRvcyAobnVtw6lyaWNvIHZzIGZhdG9yIHZzIHRleHRvKQ0KZ2xpbXBzZShkYXRhc2V0X2ZpbmFsKQ0KDQojIFZlcmlmaWNhw6fDo28gZGEgZXN0cnV0dXJhIGRvIG9iamV0byBkZSBjbHVzdGVyaXphw6fDo28gKHByb3ZhIGRlIGNvbmNlaXRvKQ0KIyBNb3N0cmEgcXVlIHRyYXRhbW9zICdjbHVzdGVyJyBlICdxdWFsaXR5JyBjb21vIGZhdG9yZXMgcGFyYSBhbsOhbGlzZSBjYXRlZ8OzcmljYQ0Kc3RyKHdpbmVfY2x1c3Rlcl9yZWQkY2x1c3RlcikNCmBgYA0KDQojIyBDcmlhdGl2aWRhZGUgZSBmZXJyYW1lbnRhcyBleHRyYXMNCg0KUGFyYSBzdXBlcmFyIGFzIGxpbWl0YcOnw7VlcyBkb3MgZGFkb3MgZSBlbnJpcXVlY2VyIGEgdmlzdWFsaXphw6fDo28sIGZvcmFtIGFwbGljYWRhcyBhcyBzZWd1aW50ZXMgc29sdcOnw7VlcyBjcmlhdGl2YXMgZSBmZXJyYW1lbnRhcyBhdmFuw6dhZGFzOg0KDQoqKlNvbHXDp8OjbyBwYXJhIEZhbHRhIGRlIENoYXZlIFByaW3DoXJpYSAoSy1NZWFucykqKjogTyBkZXNhZmlvIHTDqWNuaWNvIG1haXMgc2lnbmlmaWNhdGl2byBmb2kgYSBhdXPDqm5jaWEgZGUgdW1hIGNoYXZlIGxpZ2FuZG8gYSB0YWJlbGEgcXXDrW1pY2Egw6AgdGFiZWxhIGRlIGhhcm1vbml6YcOnw6NvLiBBIHNvbHXDp8OjbyBjcmlhdGl2YSBmb2kgdXRpbGl6YXIgQXByZW5kaXphZG8gZGUgTcOhcXVpbmEgTsOjbyBTdXBlcnZpc2lvbmFkbyAoSy1NZWFucykgcGFyYSBjcmlhciB1bSAiQ2x1c3RlciBSZXByZXNlbnRhdGl2byIuIElzc28gcGVybWl0aXUgdW1hIGp1bsOnw6NvIGJhc2VhZGEgZW0gc2ltaWxhcmlkYWRlIGVzdGF0w61zdGljYSwgZSBuw6NvIGVtIGNvcnJlc3BvbmTDqm5jaWEgZXhhdGEsIHZpYWJpbGl6YW5kbyBhIGFuw6FsaXNlLg0KDQoqKlZpc3VhbGl6YcOnw6NvIEludGVyYXRpdmEgKHBsb3RseSkqKjogQSBpbXBsZW1lbnRhw6fDo28gZGUgZ3LDoWZpY29zIGludGVyYXRpdm9zIGNvbSB0b29sdGlwcyBwZXJzb25hbGl6YWRvcyAoSFRNTCkgcGVybWl0ZSBxdWUgbyB1c3XDoXJpbyBleHBsb3JlIGRhZG9zIGRlbnNvcyBzZW0gcG9sdWlyIGEgdmlzdWFsaXphw6fDo28gZXN0w6F0aWNhLg0KDQoqKk1pbmVyYcOnw6NvIGRlIFRleHRvIChOLUdyYW1zKSoqOiBFbSB2ZXogZGUgdW1hIG51dmVtIGRlIHBhbGF2cmFzIHNpbXBsZXMsIHV0aWxpemFtb3MgYSB0b2tlbml6YcOnw6NvIGVtIGJpZ3JhbWFzIChuZ3JhbXMpIHBhcmEgY2FwdHVyYXIgbyBjb250ZXh0byBnYXN0cm9uw7RtaWNvIChleDogZGlmZXJlbmNpYXIgInRvbWF0byBzYXVjZSIgZGUgImNyZWFtIHNhdWNlIikuDQoNClBvciBmaW0sIHBhcmEgZ2FyYW50aXIgYSByZXByb2R1dGliaWxpZGFkZSB0b3RhbCBkZXN0ZSBlc3R1ZG8sIGxpc3RhbW9zIGFiYWl4byBvIGFtYmllbnRlIGRlIHNlc3PDo28gZSBhcyB2ZXJzw7VlcyBkb3MgcGFjb3RlcyB1dGlsaXphZG9zOg0KDQpgYGB7cn0NCiMgQm9hcyBwcsOhdGljYXM6IEltcHJpbWlyIGluZm9ybWHDp8O1ZXMgZGEgc2Vzc8OjbyBwYXJhIHJlcHJvZHV0aWJpbGlkYWRlIGZ1dHVyYQ0Kc2Vzc2lvbkluZm8oKQ0KYGBgDQo8ZGl2IGNsYXNzPSJmb290ZXIiPiA8cD5EZXNlbnZvbHZpZG8gcG9yIDxiPkNhdcOjIEJpdHRlbmNvdXJ0PC9iPiB8IFdpbmUgUXVhbGl0eSAmIEZvb2QgUGFpcmluZyBQcm9qZWN0PC9wPiA8L2Rpdj4NCg==