1 Introdução

library(corrplot)
library(ggcorrplot)
library(ggplot2)
library(GGally)
library(dplyr)
library(factoextra)
library(viridis)
library(knitr)
library(pacman)
library(rstatix)
library(e1071)
library(RColorBrewer)
library(ggmap)
library(maps)
library(ggrepel)
library(MASS)
library(magrittr)
library(ggpubr)
library(Rtsne)
library(bigmds)
library(tictoc)
pacman::p_load(knitr, captioner, bundesligR, stringr)

A concentração média anual de dióxido de enxofre (\(SO_2\)), em microgramas por metro cúbico, é uma medida da poluição do ar em cidades. A questão de interesse é:

Quais ou como os aspectos do clima e da ecologia humana medidos por outras variáveis influenciam a poluição do ar?

A fim de responder esse questionamento, serão realizadas diversas análises no dataset usairpollution.

2 Análise Descritiva

A Análise Descritiva é a fase inicial do processo de estudo dos dados coletados, em que são utilizados métodos de Estatística Descritiva para organizar, resumir, descrever e comparar aspectos importantes de um conjunto de variáveis. A identificação de anomalias e e dados dispersos também faz parte desse tipo de análise.

2.1 Características básicas

table_nums <- captioner::captioner(prefix = "Tabela")
tab.1_cap <- table_nums(name = "tab_1", 
                         caption = "Características básicas do dataset usairpollution")
tab.2_cap <- table_nums(name = "tab_2", 
                         caption = "Tipos e descrições das variáveis do dataset usairpollution")
tab.3_cap <- table_nums(name = "tab_3", 
                         caption = "Amostra de 6 observações de usairpollution")
tab.4_cap <- table_nums(name = "tab_4", 
                         caption = "Estatísticas resumo para as variáveis quantitativas de usairpollution")
tab.5_cap <- table_nums(name = "tab_5", 
                         caption = "Coeficiente de assimetria para as variáveis quantitativas de usairpollution")

tab.6_cap <- table_nums(name = "tab_6", 
                         caption = "Outliers para a variável SO2")
tab.7_cap <- table_nums(name = "tab_7", 
                         caption = "Outliers para a variável temp")
tab.8_cap <- table_nums(name = "tab_8", 
                         caption = "Outliers para a variável manu")
tab.9_cap <- table_nums(name = "tab_9", 
                         caption = "Outliers para a variável popul")
tab.10_cap <- table_nums(name = "tab_10", 
                         caption = "Outliers para a variável precip")
tab.11_cap <- table_nums(name = "tab_11", 
                         caption = "Outliers para a variável predays")
tab.12_cap <- table_nums(name = "tab_12", 
                         caption = "Vetores de carga")
tab.13_cap <- table_nums(name = "tab_13", 
                         caption = "Resumo PCA")

A Tabela 1 contém algumas características básicas do dataset, quais sejam:

  • Existência de NAs;
  • Total de observações;
  • Total de variáveis;
  • Existência de linhas duplicadas.

De acordo com a Tabela 1:

  • O dataset possui 41 observações e 8 variáveis;
  • Não possui NAs e nem linhas duplicadas.

Tabela 1: Características básicas do dataset usairpollution

usairpollution <- read.csv2("E:/Multivariada/Atividade 01/usairpollution.csv", header= TRUE)
basic1_charact <- data.frame(NAs = sum(is.na(usairpollution)), Observações = nrow(usairpollution), Variáveis = ncol(usairpollution), Duplicidades = anyDuplicated.data.frame(usairpollution))
colnames(usairpollution)[1] <- "City"                            

knitr::kable(basic1_charact)
NAs Observações Variáveis Duplicidades
0 41 8 0

Em seguida, a Tabela 2 contém os tipos e descrições das 8 variáveis de usairpollution.

De acordo com a Tabela 2, o dataset possui:

  • 4 variáveis quantitativas discretas (SO2,manu,popul e predays);
  • 3 variáveis quantitativas contínuas (temp, wind, precip);
  • 1 variável qualitativa nominal (City).

Tabela 2: Tipos e descrições das variáveis do dataset usairpollution

tipos <- NULL
for(i in 1:ncol(usairpollution)){
 tipos<- rbind(tipos,(typeof(usairpollution[,i])))
}

basic2_charact <- data.frame(Variável = colnames(usairpollution),
                            Tipo = tipos, 
                            Descrição = c("Cidade dos E.U.A","Teor de $SO_2$ (dióxido de enxofre) do ar em microgramas por metro cúbico","Temperatura média anual em Fahrenheit","Nº de empresas manufatureiras que empregam 20 ou mais trabalhadores","Tamanho da população em milhares (Censo de 1970)", "Velocidade média anual do vento em milhas por hora", "Precipitação média anual em polegadas", "Nº médio de dias com precipitação por ano"))

knitr::kable(basic2_charact)
Variável Tipo Descrição
City character Cidade dos E.U.A
SO2 integer Teor de \(SO_2\) (dióxido de enxofre) do ar em microgramas por metro cúbico
temp double Temperatura média anual em Fahrenheit
manu integer Nº de empresas manufatureiras que empregam 20 ou mais trabalhadores
popul integer Tamanho da população em milhares (Censo de 1970)
wind double Velocidade média anual do vento em milhas por hora
precip double Precipitação média anual em polegadas
predays integer Nº médio de dias com precipitação por ano

Por fim, ao retirarmos uma pequena amostra do dataset, podemos observar que cada linha traz informação sobre uma cidade.

Tabela 3: Amostra de 6 observações de usairpollution

knitr::kable(head(usairpollution))
City SO2 temp manu popul wind precip predays
Albany 46 47.6 44 116 8.8 33.36 135
Albuquerque 11 56.8 46 244 8.9 7.77 58
Atlanta 24 61.5 368 497 9.1 48.34 115
Baltimore 47 55.0 625 905 9.6 41.31 111
Buffalo 11 47.1 391 463 12.4 36.11 166
Charleston 31 55.2 35 71 6.5 40.75 148

2.2 Estatísticas resumo

A Tabela 4 contém algumas estatísticas resumo para as variáveis quantitativas do dataset usairpollution.

De acordo com a Tabela 4, para a variável de interesse teor de dióxido de enxofre em microgramas por metro cúbico (\(SO_2/m^3\)), temos que:

  • Os teores mínimo e máximo observados são 8µg/m³ e 110µg/m³;
  • De acordo com o primeiro quartil, 25% das cidades do conjunto de dados possuem teor \(SO_2\) igual ou inferior a 13µg/m³;
  • De acordo com a mediana, 50% das cidades do conjunto de dados possuem teor de \(SO_2\) igual ou acima de 26µg/m³;
  • De acordo com o terceiro quartil, 25% das cidades do conjunto de dados possuem teor \(SO_2\) igual ou acima de 35µg/m³;
  • De acordo com o intervalo interquartil, 50% das cidades possui teor de \(SO_2\) entre 13µg/m³ e 35µg/m³.
  • O teor médio de \(SO_2/m^3\) é 30.049µg/m³;
  • O desvio padrão do teor de \(SO_2/m^3\) é 23.472µg/m³.

Tabela 4: Estatísticas resumo para as variáveis quantitativas de usairpollution

knitr::kable(get_summary_stats(usairpollution[,-1])[,-c(2,8,9,12,13)])
variable min max median q1 q3 mean sd
manu 35.00 3344.0 347.00 181.00 462.00 463.098 563.474
popul 71.00 3369.0 515.00 299.00 717.00 608.610 579.113
precip 7.05 59.8 38.74 30.96 43.11 36.769 11.772
predays 36.00 166.0 115.00 103.00 128.00 113.902 26.506
SO2 8.00 110.0 26.00 13.00 35.00 30.049 23.472
temp 43.50 75.5 54.60 50.60 59.30 55.763 7.228
wind 6.00 12.7 9.30 8.70 10.60 9.444 1.429

2.3 Assimetria e Densidades

A Tabela 5 contém os coeficientes de assimetria para as variáveis quantitativas do dataset.

A assimetria negativa indica que a média dos dados é menor que a mediana e, portanto, que a distribuição dos dados é assimétrica à esquerda. Já a assimetria positiva indica que a média dos dados é maior que a mediana e, portanto, que a distribuição dos dados é assimétrica à direita.

Tabela 5: Coeficiente de assimetria para as variáveis quantitativas de usairpollution

knitr::kable(apply(usairpollution[,-1],2,skewness),col.names = "Coeficiente de assimetria")
Coeficiente de assimetria
SO2 1.5841126
temp 0.8229757
manu 3.4846033
popul 2.9412580
wind 0.0026751
precip -0.6925181
predays -0.5500923

De acordo com a Tabela 5, temos que:

  • A distribuição de SO2 é assimétrica à direita;
  • A distribuição de temp é assimétrica à direita;
  • A distribuição de manu é assimétrica à direita;
  • A distribuição de popul é assimétrica à direita;
  • A distribuição de wind é assimétrica à direita;
  • A distribuição de precip é assimétrica à esquerda;
  • A distribuição de predays é assimétrica à esquerda.

Os gráficos abaixo são relativos às densidades dessas variáveis. Através deles é possível visualizar a informação contida nos coeficientes de assimetria.

par(mfrow=c(4,2))

ggplot(usairpollution, aes(x=SO2)) + 
  geom_density()+
scale_x_continuous(breaks = seq(0,120,by=10), limits= c(0,120))+
labs(title = "Densidade da variável SO2 \n",
      x = "\nTeor de SO2 em µg/m³ \n",
      y = "Densidade\n")+
  theme_classic()

ggplot(usairpollution, aes(x=temp)) + 
  geom_density()+
scale_x_continuous(breaks = seq(0,120,by=10), limits= c(0,120))+
labs(title = "Densidade da variável temp \n",
     x = "\nTemperatura média anual (°F)",
      y = "Densidade\n")+
  theme_classic()

ggplot(usairpollution, aes(x=manu)) + 
  geom_density()+
scale_x_continuous(breaks = seq(0,3400,by=200), limits= c(0,3400))+
labs(title = "Densidade da variável manu \n",
     x = "\n Nº de empresas manufatureiras que empregam 20 ou mais trabalhadores",
      y = "Densidade\n")+
  theme_classic()
  
ggplot(usairpollution, aes(x=popul)) + 
  geom_density()+
scale_x_continuous(breaks = seq(0,3400,by=200), limits= c(0,3400))+
labs(title = "Densidade da variável popul \n",
     x = "\n Tamanho da população (Censo de 1970) em milhares",
      y = "Densidade\n")+
  theme_classic()
    
ggplot(usairpollution, aes(x=wind)) + 
  geom_density()+
scale_x_continuous(breaks = seq(0,120,by=10), limits= c(0,120))+
labs(title = "Densidade da variável wind \n",
     x = "\n Velocidade média anual do vento em milhas por hora",
      y = "Densidade\n")+
  theme_classic()


ggplot(usairpollution, aes(x=precip)) + 
  geom_density()+
scale_x_continuous(breaks = seq(0,120,by=10), limits= c(0,120))+
labs(title = "Densidade da variável precip \n",
     x = "\n Precipitação média anual em polegadas",
      y = "Densidade\n")+
  theme_classic()

ggplot(usairpollution, aes(x=predays)) + 
  geom_density()+
scale_x_continuous(breaks = seq(0,200,by=10), limits= c(0,200))+
labs(title = "Densidade da variável predays \n",
     x = "\n Nº médio de dias com precipitação por ano",
      y = "Densidade\n")+
  theme_classic()

2.4 Boxplots

Os gráficos abaixo são relativos aos boxplots das variáveis quantitativas do dataset usairpollution. Através deles é possível visualizar a distribuição dos dados, assimetria, quartis e outliers.

par(mfrow=c(4,2))
cores <- brewer.pal(n = 7, name = "Set1")


a<-ggplot(data = usairpollution, aes (y = SO2))+
geom_boxplot(fill=cores[1], color="black") +
labs(title = "Distribuição da variável SO2 \n",
       y = " Teor de SO2 em µg/m³ \n")+
scale_y_continuous(breaks = seq(0,130,by=10), limits= c(0,130))+
scale_x_continuous(labels=NULL)+
theme_classic()
a

b<- ggplot(data = usairpollution, aes (y = temp))+
geom_boxplot(fill=cores[2], color="black") +
labs(title = "Distribuição da variável temp \n",
       y = "Temperatura média anual (°F) \n")+
scale_y_continuous(breaks = seq(0,130,by=10), limits= c(0,130))+
scale_x_continuous(labels=NULL)+
theme_classic()
b
c<- ggplot(data = usairpollution, aes (y = manu))+
geom_boxplot(fill=cores[3], color="black") +
labs(title = "Distribuição da variável manu \n",
       y = " Nº de empresas manufatureiras que empregam 20 ou mais trabalhadores \n")+
scale_y_continuous(breaks = seq(0,3400,by=200), limits= c(0,3400))+
scale_x_continuous(labels=NULL)+
theme_classic()
c

d<- ggplot(data = usairpollution, aes (y = popul))+
geom_boxplot(fill=cores[4], color="black") +
labs(title = "Distribuição da variável popul \n",
       y = " Tamanho da população (Censo de 1970) em milhares \n")+
scale_y_continuous(breaks = seq(0,3400,by=200), limits= c(0,3400))+
scale_x_continuous(labels=NULL)+
theme_classic()
d

e<-ggplot(data = usairpollution, aes (y = wind))+
geom_boxplot(fill=cores[5], color="black") +
labs(title = "Distribuição da variável wind \n",
       y = " Velocidade média anual do vento em milhas por hora \n")+
scale_y_continuous(breaks = seq(0,50,by=2), limits= c(0,50))+
scale_x_continuous(labels=NULL)+
theme_classic()
e


f<-ggplot(data = usairpollution, aes (y = precip))+
geom_boxplot(fill=cores[6], color="black") +
labs(title = "Distribuição da variável precip \n",
       y = " Precipitação média anual em polegadas \n")+
scale_y_continuous(breaks = seq(0,130,by=10), limits= c(0,130))+
scale_x_continuous(labels=NULL)+
theme_classic()
f

g<-ggplot(data = usairpollution, aes (y = predays))+
geom_boxplot(fill=cores[7], color="black") +
labs(title = "Distribuição da variável predays \n",
       y = " Nº médio de dias com precipitação por ano \n")+
scale_y_continuous(breaks = seq(0,180,by=10), limits= c(0,180))+
scale_x_continuous(labels=NULL)+
theme_classic()
g

2.5 Outliers

Para a variável SO2, os outliers correspondem às medições de dióxido de enxofre nas cidades de Chicago, Philadelphia e Providence.

Tabela 6: Outliers para a variável SO2

out<- ggplot_build(a)[["data"]][[1]][["outliers"]]
knitr::kable(usairpollution[which(usairpollution$SO2==out[[1]][1]| usairpollution$SO2==out[[1]][2] |usairpollution$SO2==out[[1]][3]),c(1,2)],row.names=FALSE)
City SO2
Chicago 110
Philadelphia 69
Providence 94

Para a variável temp, o outlier corresponde a temperatura média anual(°F) na cidade de Miami.

Tabela 7: Outliers para a variável temp

out<- ggplot_build(b)[["data"]][[1]][["outliers"]]
knitr::kable(usairpollution[which(usairpollution$temp==out[[1]][1]),c(1,3)],row.names=FALSE)
City temp
Miami 75.5

Para a variável manu, os outliers correspondem ao nº de empresas manufatureiras que empregam 20 ou mais trabalhadores nas cidades de Chicago, Cleveland, Detroit e Philadelphia.

Tabela 8: Outliers para a variável manu

out<- ggplot_build(c)[["data"]][[1]][["outliers"]]
knitr::kable(usairpollution[which(usairpollution$manu==out[[1]][1]|usairpollution$manu==out[[1]][2]|usairpollution$manu==out[[1]][3]|usairpollution$manu==out[[1]][4]),c(1,4)],row.names=FALSE)
City manu
Chicago 3344
Cleveland 1007
Detroit 1064
Philadelphia 1692

Para a variável popul, os outliers correspondem ao tamanho da população (milhare) nas cidades de Chicago, Detroit e Philadelphia.

Tabela 9: Outliers para a variável popul

out<- ggplot_build(d)[["data"]][[1]][["outliers"]]
knitr::kable(usairpollution[which(usairpollution$popul==out[[1]][1]|usairpollution$popul==out[[1]][2]|usairpollution$popul==out[[1]][3]),c(1,5)],row.names=FALSE)
City popul
Chicago 3369
Detroit 1513
Philadelphia 1950

A variável wind não possui outliers.

Para a variável precip, os outliers correspondem a precipitação média anual em polegadas nas cidades de Albuquerque e Phoenix.

Tabela 10: Outliers para a variável precip

out<- ggplot_build(f)[["data"]][[1]][["outliers"]]
knitr::kable(usairpollution[which(usairpollution$precip==out[[1]][1]|usairpollution$precip==out[[1]][2]),c(1,7)],row.names=FALSE)
City precip
Albuquerque 7.77
Phoenix 7.05

Por fim, para a variável predays os outliers correspondem ao número médio de dias com precipitação por ano nas cidades de Albuquerque, Phoenix e San Francisco.

Tabela 11: Outliers para a variável predays

out<- ggplot_build(g)[["data"]][[1]][["outliers"]]
knitr::kable(usairpollution[which(usairpollution$predays==out[[1]][1]|usairpollution$predays==out[[1]][2] | usairpollution$predays==out[[1]][3]),c(1,8)],row.names=FALSE)
City predays
Albuquerque 58
Buffalo 166
Phoenix 36

2.6 Gráficos de dispersão

Foram feitos gráficos de dispersão entre a variável SO2 e as demais a fim de verificar possíveis relações, especialmente as lineares. Entretanto, ao observar os gráficos não foi possível identificar visualmente quaisquer relações.

par(mfrow=c(3,2))
cores2 <- brewer.pal(n = 6, name = "Dark2")
ggplot(usairpollution, aes(x=temp, y=SO2)) +
geom_point(color=cores2[1])+
labs(title = "Gráfico de dispersão entre as variáveis SO2 e temp",
      y = " Teor de SO2 em µg/m³ \n",
      x = "\nTemperatura média anual (°F)")+
theme_classic()

ggplot(usairpollution, aes(x=manu, y=SO2)) +
geom_point(color=cores2[2])+
labs(title = "Gráfico de dispersão entre as variáveis SO2 e manu",
      y = " Teor de SO2 em µg/m³ \n",
      x = "\n Nº de empresas manufatureiras que empregam 20 ou mais trabalhadores")+
theme_classic()

ggplot(usairpollution, aes(x=popul, y=SO2)) +
geom_point(color=cores2[3])+
labs(title = "Gráfico de dispersão entre as variáveis SO2 e popul",
      y = " Teor de SO2 em µg/m³ \n",
      x = "\n Tamanho da população (Censo de 1970) em milhares")+
theme_classic()

ggplot(usairpollution, aes(x=wind, y=SO2)) +
geom_point(color=cores2[4])+
labs(title = "Gráfico de dispersão entre as variáveis SO2 e wind",
      y = " Teor de SO2 em µg/m³ \n",
      x = "\n Velocidade média anual do vento em milhas por hora")+
theme_classic()

ggplot(usairpollution, aes(x=precip, y=SO2)) +
geom_point(color=cores2[5])+
labs(title = "Gráfico de dispersão entre as variáveis SO2 e precip",
      y = " Teor de SO2 em µg/m³ \n",
      x = "\n Precipitação média anual em polegadas")+
theme_classic()

ggplot(usairpollution, aes(x=predays, y=SO2)) +
geom_point(color=cores2[6])+
labs(title = "Gráfico de dispersão entre as variáveis SO2 e predays",
      y = " Teor de SO2 em µg/m³ \n",
      x = "\n Nº médio de dias com precipitação por ano")+
theme_classic()

2.7 Correlograma

O correlograma abaixo mostra os coeficientes de correlação de Pearson para todas as variáveis quantitativas do dataset.

corr <- cor(usairpollution[,-1]) #Matriz de correlação
#p.mat <- cor_pmat(usairpollution[,-1]) # Matriz de p-valores das correlações
ggcorrplot(corr,outline.color = "white",type = "lower",lab = TRUE)

  • A variável de interesse SO2 possui maior correlação com a variável manu, 0.64.

  • As variáveis manu e popul possuem correlação 0.96, o que significa que essas variáveis possuem uma relação linear positiva: quanto maior a população, maior o número de empresas manufatureiras.

2.8 Distribuição geográfica da variável SO2

Para produzir o mapa de calor para a variável teor de dióxido de enxofre (SO2), foi utilizado um dataset que contém populações de cidades dos E.U.A de 1790 a 2010 e suas respectivas latitudes e longitudes, aqui nomeado popul_1790.2010.

O primeiro passo da construção do mapa de calor consistiu em fazer um merge entre os datasets popul_1790.2010 e usairpollution pela variável chave City, a fim de obter as latitudes e longitudes. O resultado foi um dataset com 130 observações, visto que existem cidades de mesmo nome mas que pertencem a estados diferentes.

Com o objetivo de eliminar múltiplas entradas para a mesma cidade, foi feita uma comparação entre variáveis popul e X1970, ambas referentes à população dessas cidades de acordo com o Censo de 1970. Foram mantidas entradas únicas para cada cidade, de forma que o quadrado da diferença entre popul e X1970 fosse mínima.

popul_1790.2010 <- read.csv("E:/Multivariada/Atividade 01/1790-2010_MASTER.txt")
x <- merge(usairpollution,popul_1790.2010,by="City")
x$popul <- x$popul*1000

cities <- as.character(unique(x$City))
y <- NULL
for(i in 1:length(cities)){
  
  keep <- which(x$City==cities[i])[which.min((x$popul[which(x$City==cities[i])] - x$X1970[which(x$City==cities[i])])^2)]
  y <- rbind(y,x[keep,])
}

Com os dados das latitudes e longitudes em mãos foi possível construir o mapa abaixo, que contém os teores de \(SO_2\) para as 41 cidades do dataset usairpollution. Cada ponto representa uma cidade e, quanto maior o tamanho do ponto ou mais próximo de amarelo a sua cor, maior o teor de \(SO_2\) observado. As cidades com as maiores medições de \(SO_2\) são Chicago, Providence, Philadelphia, Cleveland e Pittsburgh.

ggplot(y, aes(LON, LAT,label=City)) + borders("state") + 
  geom_point(aes(size = SO2, fill=SO2),colour="black", shape=21) + 
  coord_quickmap() +
  geom_label_repel(aes(label=ifelse(SO2>60,as.character(City),'')), 
                   size = 2,
                   box.padding   = 0.35, 
                   point.padding = 0.5,
                   segment.color = 'grey50')+
  labs(title = "Distribuição geográfica da variável SO2\n",
      y = " Latitude\n",
      x = "\nLongitude")+
  theme(plot.title = element_text(hjust = 0.5))+
  scale_fill_viridis(option = "C")+
  theme_classic()

3 Análise de Componentes Principais (PCA)

As componentes principais são novas variáveis que são construídas como combinações lineares das variáveis iniciais.

Essas combinações são feitas de forma que essas novas variáveis não sejam correlacionadas e a maioria das informações contidas nas variáveis iniciais seja armazenada nos primeiros componentes.

Então, a ideia é que dados k-dimensionais forneçam k componentes principais. O método busca colocar o máximo de informações possíveis nas primeiras componentes, para que, se quisermos reduzir a dimensionalidade de um conjunto de dados, possamos focar a análise nos primeiros componentes sem sofrer uma grande penalidade em termos de perda de informação.

As variáveis do conjunto de dados devem ser padronizadas previamente à análise. No caso desse estudo, a variável de interesse, SO2, foi retirada da análise de componentes principais, pois não é nosso interesse agregá-la às demais.

3.1 Vetores de Carga

Os vetores de carga contêm os coeficientes da combinação linear que gerou cada componente principal para as variáveis utilizadas. A primeira componente, por exemplo, pode ser descrita como

\(0.32temp-0.61manu-0.57popul-0.35wind+0.04precip-0.24predays\)

Tabela 12: Vetores de carga

res.pca <- prcomp(usairpollution[,-c(1,2)], scale = TRUE, center = TRUE)
knitr::kable(res.pca$rotation)
PC1 PC2 PC3 PC4 PC5 PC6
temp 0.3296461 -0.1275974 0.6716861 -0.3064573 -0.5580564 0.1361878
manu -0.6115424 -0.1680577 0.2728863 0.1368408 0.1020421 0.7029705
popul -0.5778220 -0.2224533 0.3503741 0.0724813 -0.0780655 -0.6946413
wind -0.3538388 0.1307915 -0.2972533 -0.8694258 -0.1132669 0.0245250
precip 0.0408070 0.6228578 0.5045629 -0.1711483 0.5681834 -0.0606222
predays -0.2379159 0.7077653 -0.0930885 0.3113069 -0.5800039 0.0219606

3.2 Scree Plot

Podemos observar que a primeira componente explica cerca de 36.6% da variância total dos dados, a segunda 25% e a terceira, 23.2%.

fviz_eig(res.pca,addlabels = TRUE, xlab = "Dimensões", ylab = "% da variância explicada", main = "Scree Plot" )

Utilizando as 4 primeiras componentes principais, a proporção da variância explicada (PVE) acumulada é de 97.52%.

Tabela 13: Resumo PCA

summary(res.pca)
## Importance of components:
##                          PC1   PC2    PC3    PC4    PC5     PC6
## Standard deviation     1.482 1.225 1.1810 0.8719 0.3385 0.18560
## Proportion of Variance 0.366 0.250 0.2324 0.1267 0.0191 0.00574
## Cumulative Proportion  0.366 0.616 0.8485 0.9752 0.9943 1.00000

3.3 Qualidade da representação das variáveis (cos2)

A qualidade da representação de uma variável é medida pelo quadrado das cargas ou coordenadas, chamada de cos2.

O gráfico abaixo mostra o total de cos2 para as duas primeiras componentes, ou seja, o valor para cada variável corresponde a soma de cos2 para as duas primeiras componentes.

fviz_pca_var(res.pca, col.var = "cos2",
gradient.cols = viridis_pal()(12),
repel = TRUE # Avoid text overlapping
) + labs(title =" Qualidade da representação (cos2) - Variáveis")

As variáveis mais próximas da circunferência são predays, precip, manu e popul, ou seja sua representação no mapa de fatores é boa, então são mais importantes para as primeiras componentes. Já as variáveis wind e temp estão mais próximas da origem, o que significa não foram bem representadas pelas componentes 1 e 2. Podemos observar que as variáveis popul, manu, wind e predays são variáveis positivamente correlacionadas, pois estão do mesmo lado da circunferência.

3.4 Contribuição das variáveis nas componentes (contrib)

As variáveis que mais contribuem na construção da primeira e segunda componentes (Dim.1 e Dim.2) são as mais importantes para explicar a variabilidade no conjunto de dados. Essas variáveis são:

  • manu
  • popul
  • precip
  • predays
var <- get_pca_var(res.pca)

corrplot(var$contrib, is.corr=FALSE,tl.col = 'black',col.lim = c(0, 80))

O gráfico abaixo contém as contribuições das variáveis na primeira componente (%). A linha tracejada em vermelho representa a contribuição média esperada (%).

Se todas as variáveis contribuíssem de forma igual para a construção das componentes, o valor da contribuição média esperada (%) seria \(\frac{1}{6} \approx 16.67\%\).

Portanto, se uma determinada variável tem contribuição acima de \(16.67\%\), esta pode ser considerada importante na contribuição da componente.

Na primeira componente, as variáveis manu e popul ultrapassam a contribuição média esperada (%), logo, são importantes em sua construção.

fviz_contrib(res.pca, choice = "var", axes = 1) + labs(title ="Contribuição das variáveis na Primeira Componente", y = "Contribuição em %")

Já na componente 2, as variáveis predays e precip são importantes para sua construção.

fviz_contrib(res.pca, choice = "var", axes = 2) + labs(title ="Contribuição das variáveis na Segunda Componente", y = "Contribuição em %")

Se considerarmos as componentes 1 e 2 simultaneamente, as variáveis predays, manu e popul são as mais importantes.

fviz_contrib(res.pca, choice = "var", axes = 1:2) + labs(title ="Contribuição das variáveis na Primeira e Segunda Componentes", y = "Contribuição em %")

3.5 Qualidade da representação dos indivíduos (cos2)

O gráfico abaixo contém a qualidade da representação(cos2) dos indivíduos. Os individuos similares ficam mais próximos entre si. Quanto mais distante do centro estiver o indivíduo, melhor sua representação nas componentes 1 e 2: destacam-se as cidades de Philadelphia, Chicago, Phoenix e Albuquerque.

us <- data.frame(usairpollution, row.names = 1)
res.pca <- prcomp(us[,-2], scale = TRUE, center = TRUE)
ind = get_pca_ind(res.pca)
fviz_pca_ind(res.pca, repel = TRUE,
pointsize = "cos2",
pointshape = 21,
fill = "yellow"
) + theme_minimal() + labs(title =" Qualidade da representação (cos2) - Indivíduos")

O gráfico abaixo corrobora com o que foi observado anteriormente: as cidades melhor representadas nas componentes 1 e 2 são Chicago, San Francisco, Albuquerque, Philadelphia, Denver e Phoenix.

fviz_cos2(res.pca, choice = "ind", axes=1:2) + labs(title =" Qualidade da representação (cos2) - Indivíduos - Componentes 1 e 2", y = "Cos2 - Qualidade da representação")

3.6 Contribuição dos índividuos nas componentes (contrib)

O gráfico abaixo contém as contribuições dos indivíduos nas duas primeiras componente (%). A linha tracejada em vermelho representa a contribuição média esperada (%).

fviz_contrib(res.pca, choice = "ind", axes = 1:2) +labs(title =" Contribuição dos Indivíduos nas componentes 1 e 2", y = "Contrib - Contribuição (%)")

Se todas os indivíduos contribuíssem de forma igual para a construção das componentes, o valor da contribuição média esperada (%) seria \(\frac{1}{41} \approx 2.44\%\).

Portanto, se um determinad indivíduo tem contribuição acima de \(2.44\%\), este pode ser considerada importante na contribuição da componente.

Para as duas primeiras componente, os indivíduos Chicago, Phoenix, Philadelphia, Albuquerque, San Francisco, Denver, Cleveland, e Salt Lake City ultrapassam a contribuição média esperada (%), logo, são importantes em sua construção.

4 Escalonamento Multidimensional (MDS)

O MDS retorna uma solução ótima para representar os dados em um espaço de dimensão inferior, onde o número de dimensões k é pré-especificado pela pessoa fazendo a análise. Por exemplo, a escolha de \(k = 2\) otimiza a localização dos objetos para um gráfico bidimensional de dispersão.

O algoritmo MDS toma como dado de entrada a matriz de dissimilaridade, representando as distâncias entre pares de objetos. A ideia é encontrar uma representação dos dados em menor dimensão e que preserve ao máximo as distâncias entre pontos.

O MDS será aplicado nas variáveis quantitativas do dataset usairpollution, com exceção da variável SO2. Previamente a utilização do algoritmo, os dados foram padronizados para a métrica [0,1]: \(X' = \frac{x-\min(x)}{\max(x)-min(x)}\). Foi utilizada a distância euclidiana para cálculo de dissimilaridades.

4.1 Escalonamento Multidimensional Clássico

O MDS Clássico preserva a métrica de distância original, entre pontos, da melhor forma possível. Isto é, as distâncias ajustadas no mapa MDS e as distâncias originais estão na mesma métrica. O MDS Clássico pertence à chamada categoria de escalonamento multidimensional métrico. É também conhecido como análise de coordenadas principais e é adequado para dados quantitativos.

Para demonstrar que é possível reconstruir a matriz de distância original utilizando as coordenadas geradas a partir do MDS, calcula-se a diferença máxima absoluta entre a matriz de distâncias original e a reconstruída.

O gráfico abaixo mostra as diferenças máximas observadas entre a matriz de distância original e a reconstruída a a partir das coordenadas MDS ao variar o valor de k entre 2 e 7.

As diferenças próximas de zero para \(k=\{5,6,7\}\) expressam que, a partir de \(k=5\) coordenadas, a matriz de distância original pode ser recuperada quase que totalmente.

P<- data.frame(usairpollution[,-2], row.names = 1)
P <- apply(P,2, function(x) (x-min(x))/(max(x)-min(x)))

valor <- NULL
for(i in 2:7){
  valor<-rbind(valor,c(i,max(abs(dist(P) - dist(cmdscale(dist(P),k=i, eig=TRUE)$points)))))
}

df<-data.frame(k = valor[,1], `dif_max` = valor[,2])

ggplot(data=df, aes(x=k, y=dif_max, group=1)) +
  geom_line()+
  geom_point()+
  theme_bw()+
  labs(title="Diferenças máximas absolutas entre matriz de distância original e matriz \nreconstruída a partir das coordenadas MDS, para k entre 2 e 7\n", y="Diferença máxima absoluta\n",x="\nk")+
  ylim(0,1.25)

Aplicando a função cmdscale (stats), com \(k=5\), abaixo temos o gráfico de dispersão das observações para as coordenadas 1 e 2. Pode ser observado que as cidades com teores de \(SO_2\) mais similares ficam mais agrupadas, enquanto que as que possuem teores mais discrepantes ficam mais afastadas.

mds <- P %>%
  dist() %>%          
  cmdscale(k=5, eig=TRUE) 


mds_label<- as.data.frame(mds$points)
colnames(mds_label)<- paste("Coord",1:5)
mds_label$City <- usairpollution[,1]
mds_label$SO2 <- usairpollution[,2]

ggplot(mds_label, aes(x=`Coord 1`, y = `Coord 2`)) +
  geom_point(aes(size=SO2), colour = "#32CD32")+
  labs(title= "Gráfico de dispersão - MDS Clássico com k=5", x= "Coord 1", y="Coord 2")+
  theme(plot.title = element_text(hjust = 0.5))+
  geom_label_repel(aes(label=City), 
                   size = 2,
                   box.padding   = 0.35, 
                   point.padding = 0.5,
                   segment.color = 'grey50')+
  theme_bw()

5 t-Distributed Stochastic Neighbor Embedding (t-SNE)

É uma técnica de visualização em 1D, 2D ou 3D de datasets de altas dimensões. No gráfico abaixo, podemos perceber que algumas cidades com valor de SO2 baixos ficaram concentradas na parte superior do gráfico. Comparando os resultados gráficos entre MDS e t-SNE, nota-se que as observações estão mais uniformemente distribuídas no t-SNE, enquanto que no MDS estão mais agrupadas.

tic("tsne")
toc(log = TRUE, quiet = TRUE)

tsne_fit <- Rtsne(P, perplexity=13)
tsne_coord <- data.frame(tsne_fit$Y)
tsne_coord$City <- usairpollution[,1]
tsne_coord$SO2 <- usairpollution[,2]

ggplot(data=tsne_coord, aes(x=X1, y=X2))+ 
  geom_point(aes(size=SO2), colour = "#32CD32") + 
  geom_label_repel(aes(label=City), 
                   size = 2,
                   box.padding   = 0.35, 
                   point.padding = 0.5,
                   segment.color = 'grey50')+ 
  labs(x="Coord 1\n",y="\nCoord 2", title="t-SNE para o dataset usairpollution") +
  theme_bw() 

6 Conclusão

O dataset usairpollution contém 8 variáveis e 41 observações relativas a cidades estadunidenses. A variável de interesse é SO2, o teor de dióxido de enxofre em microgramas por metro cúbico, uma medida de poluição.

Na Análise Descritiva, vimos estatísticas resumo, assimetria e densidades, boxplots, outliers, gráficos de dispersão, correlograma e distribuição geográfica para as 7 variáveis quantitativas: SO2, temp, manu, popul, wind, precip e predays.

Em seguida, na Análise de Componentes Principais, vimos que com 4 componentes conseguimos explicar cerca de 97.52% da variabilidade total do conjunto de dados.

No Escalonamento Multidimensional, vimos que a partir de \(k=5\) coordenadas, a matriz de distância original pode ser recuperada quase que totalmente.

E, por fim, vimos no t-SNE que podemos representar dados de altas dimensionalidades em 2 dimensões.

Como próximos passos, poderíamos, por exemplo, fazer uma regressão linear entre SO2 e as seis componentes principais calculadas ou então uma clusterização com os resultados do MDS.

Todas as análises realizadas abriram diversas possibilidades para explorar melhor a relação entre SO2 e as demais variáveis.

LS0tDQp0aXRsZTogIkFuw6FsaXNlIE11bHRpdmFyaWFkYSAtIEF0aXZpZGFkZSAwMSINCmF1dGhvcjogIlZpY3TDs3JpYSBWYXJnYXMiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUgDQogICAgdG9jOiB0cnVlICMgdGFibGUgb2YgY29udGVudCB0cnVlDQogICAgdG9jX2RlcHRoOiAzICAjIHVwdG8gdGhyZWUgZGVwdGhzIG9mIGhlYWRpbmdzIChzcGVjaWZpZWQgYnkgIywgIyMgYW5kICMjKQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgaWYgeW91IHdhbnQgbnVtYmVyIHNlY3Rpb25zIGF0IGVhY2ggdGFibGUgaGVhZGVyDQogICAgdGhlbWU6IHVuaXRlZCAgIyBtYW55IG9wdGlvbnMgZm9yIHRoZW1lLCB0aGlzIG9uZSBpcyBteSBmYXZvcml0ZS4NCiAgICBoaWdobGlnaHQ6IHRhbmdvICAjIHNwZWNpZmllcyB0aGUgc3ludGF4IGhpZ2hsaWdodGluZyBzdHlsZQ0KDQotLS0NCiMgSW50cm9kdcOnw6NvIA0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0gDQpsaWJyYXJ5KGNvcnJwbG90KQ0KbGlicmFyeShnZ2NvcnJwbG90KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShmYWN0b2V4dHJhKQ0KbGlicmFyeSh2aXJpZGlzKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkocGFjbWFuKQ0KbGlicmFyeShyc3RhdGl4KQ0KbGlicmFyeShlMTA3MSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShnZ21hcCkNCmxpYnJhcnkobWFwcykNCmxpYnJhcnkoZ2dyZXBlbCkNCmxpYnJhcnkoTUFTUykNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkoUnRzbmUpDQpsaWJyYXJ5KGJpZ21kcykNCmxpYnJhcnkodGljdG9jKQ0KcGFjbWFuOjpwX2xvYWQoa25pdHIsIGNhcHRpb25lciwgYnVuZGVzbGlnUiwgc3RyaW5ncikNCg0KDQoNCmBgYA0KDQpBIGNvbmNlbnRyYcOnw6NvIG3DqWRpYSBhbnVhbCBkZSBkacOzeGlkbyBkZSBlbnhvZnJlICgkU09fMiQpLCBlbSBtaWNyb2dyYW1hcyBwb3IgbWV0cm8gY8O6Ymljbywgw6kgdW1hIG1lZGlkYSBkYSBwb2x1acOnw6NvIGRvIGFyIGVtIGNpZGFkZXMuIEEgcXVlc3TDo28gZGUgaW50ZXJlc3NlIMOpOg0KDQo+IFF1YWlzIG91IGNvbW8gb3MgYXNwZWN0b3MgZG8gY2xpbWEgZSBkYSBlY29sb2dpYSBodW1hbmEgbWVkaWRvcyBwb3Igb3V0cmFzIHZhcmnDoXZlaXMgaW5mbHVlbmNpYW0gYSBwb2x1acOnw6NvIGRvIGFyPw0KDQpBIGZpbSBkZSByZXNwb25kZXIgZXNzZSBxdWVzdGlvbmFtZW50bywgc2Vyw6NvIHJlYWxpemFkYXMgZGl2ZXJzYXMgYW7DoWxpc2VzIG5vIGRhdGFzZXQgKnVzYWlycG9sbHV0aW9uKi4NCg0KIyBBbsOhbGlzZSBEZXNjcml0aXZhIA0KDQpBIEFuw6FsaXNlIERlc2NyaXRpdmEgw6kgYSBmYXNlIGluaWNpYWwgZG8gcHJvY2Vzc28gZGUgZXN0dWRvIGRvcyBkYWRvcyBjb2xldGFkb3MsIGVtIHF1ZSANCnPDo28gdXRpbGl6YWRvcyAgbcOpdG9kb3MgZGUgRXN0YXTDrXN0aWNhIERlc2NyaXRpdmEgcGFyYSBvcmdhbml6YXIsIHJlc3VtaXIsIGRlc2NyZXZlciBlIGNvbXBhcmFyIGFzcGVjdG9zIGltcG9ydGFudGVzIGRlIHVtIGNvbmp1bnRvIGRlIHZhcmnDoXZlaXMuIEEgaWRlbnRpZmljYcOnw6NvIGRlIGFub21hbGlhcyBlIGUgZGFkb3MgZGlzcGVyc29zIHRhbWLDqW0gZmF6IHBhcnRlIGRlc3NlIHRpcG8gZGUgYW7DoWxpc2UuDQoNCg0KIyMgQ2FyYWN0ZXLDrXN0aWNhcyBiw6FzaWNhcw0KYGBge3Igd2FybmluZz1GQUxTRX0NCnRhYmxlX251bXMgPC0gY2FwdGlvbmVyOjpjYXB0aW9uZXIocHJlZml4ID0gIlRhYmVsYSIpDQp0YWIuMV9jYXAgPC0gdGFibGVfbnVtcyhuYW1lID0gInRhYl8xIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJDYXJhY3RlcsOtc3RpY2FzIGLDoXNpY2FzIGRvIGRhdGFzZXQgdXNhaXJwb2xsdXRpb24iKQ0KdGFiLjJfY2FwIDwtIHRhYmxlX251bXMobmFtZSA9ICJ0YWJfMiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSAiVGlwb3MgZSBkZXNjcmnDp8O1ZXMgZGFzIHZhcmnDoXZlaXMgZG8gZGF0YXNldCB1c2FpcnBvbGx1dGlvbiIpDQp0YWIuM19jYXAgPC0gdGFibGVfbnVtcyhuYW1lID0gInRhYl8zIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJBbW9zdHJhIGRlIDYgb2JzZXJ2YcOnw7VlcyBkZSB1c2FpcnBvbGx1dGlvbiIpDQp0YWIuNF9jYXAgPC0gdGFibGVfbnVtcyhuYW1lID0gInRhYl80IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJFc3RhdMOtc3RpY2FzIHJlc3VtbyBwYXJhIGFzIHZhcmnDoXZlaXMgcXVhbnRpdGF0aXZhcyBkZSB1c2FpcnBvbGx1dGlvbiIpDQp0YWIuNV9jYXAgPC0gdGFibGVfbnVtcyhuYW1lID0gInRhYl81IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJDb2VmaWNpZW50ZSBkZSBhc3NpbWV0cmlhIHBhcmEgYXMgdmFyacOhdmVpcyBxdWFudGl0YXRpdmFzIGRlIHVzYWlycG9sbHV0aW9uIikNCg0KdGFiLjZfY2FwIDwtIHRhYmxlX251bXMobmFtZSA9ICJ0YWJfNiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSAiT3V0bGllcnMgcGFyYSBhIHZhcmnDoXZlbCBTTzIiKQ0KdGFiLjdfY2FwIDwtIHRhYmxlX251bXMobmFtZSA9ICJ0YWJfNyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSAiT3V0bGllcnMgcGFyYSBhIHZhcmnDoXZlbCB0ZW1wIikNCnRhYi44X2NhcCA8LSB0YWJsZV9udW1zKG5hbWUgPSAidGFiXzgiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBjYXB0aW9uID0gIk91dGxpZXJzIHBhcmEgYSB2YXJpw6F2ZWwgbWFudSIpDQp0YWIuOV9jYXAgPC0gdGFibGVfbnVtcyhuYW1lID0gInRhYl85IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJPdXRsaWVycyBwYXJhIGEgdmFyacOhdmVsIHBvcHVsIikNCnRhYi4xMF9jYXAgPC0gdGFibGVfbnVtcyhuYW1lID0gInRhYl8xMCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSAiT3V0bGllcnMgcGFyYSBhIHZhcmnDoXZlbCBwcmVjaXAiKQ0KdGFiLjExX2NhcCA8LSB0YWJsZV9udW1zKG5hbWUgPSAidGFiXzExIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJPdXRsaWVycyBwYXJhIGEgdmFyacOhdmVsIHByZWRheXMiKQ0KdGFiLjEyX2NhcCA8LSB0YWJsZV9udW1zKG5hbWUgPSAidGFiXzEyIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJWZXRvcmVzIGRlIGNhcmdhIikNCnRhYi4xM19jYXAgPC0gdGFibGVfbnVtcyhuYW1lID0gInRhYl8xMyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSAiUmVzdW1vIFBDQSIpDQoNCmBgYA0KDQoNCkEgVGFiZWxhIDEgY29udMOpbSBhbGd1bWFzIGNhcmFjdGVyw61zdGljYXMgYsOhc2ljYXMgZG8gZGF0YXNldCwgcXVhaXMgc2VqYW06IA0KDQoqIEV4aXN0w6puY2lhIGRlIE5BczsNCiogVG90YWwgZGUgb2JzZXJ2YcOnw7VlczsNCiogVG90YWwgZGUgdmFyacOhdmVpczsNCiogRXhpc3TDqm5jaWEgZGUgbGluaGFzIGR1cGxpY2FkYXMuDQoNCg0KRGUgYWNvcmRvIGNvbSBhIFRhYmVsYSAxOiANCg0KKiBPIGRhdGFzZXQgcG9zc3VpIDQxIG9ic2VydmHDp8O1ZXMgZSA4IHZhcmnDoXZlaXM7DQoqIE7Do28gcG9zc3VpIE5BcyBlIG5lbSBsaW5oYXMgZHVwbGljYWRhcy4NCg0KYHIgdGFibGVfbnVtcygndGFiXzEnKWANCmBgYHtyIH0NCg0KdXNhaXJwb2xsdXRpb24gPC0gcmVhZC5jc3YyKCJFOi9NdWx0aXZhcmlhZGEvQXRpdmlkYWRlIDAxL3VzYWlycG9sbHV0aW9uLmNzdiIsIGhlYWRlcj0gVFJVRSkNCmJhc2ljMV9jaGFyYWN0IDwtIGRhdGEuZnJhbWUoTkFzID0gc3VtKGlzLm5hKHVzYWlycG9sbHV0aW9uKSksIE9ic2VydmHDp8O1ZXMgPSBucm93KHVzYWlycG9sbHV0aW9uKSwgVmFyacOhdmVpcyA9IG5jb2wodXNhaXJwb2xsdXRpb24pLCBEdXBsaWNpZGFkZXMgPSBhbnlEdXBsaWNhdGVkLmRhdGEuZnJhbWUodXNhaXJwb2xsdXRpb24pKQ0KY29sbmFtZXModXNhaXJwb2xsdXRpb24pWzFdIDwtICJDaXR5IiAgICAgICAgICAgICAgICAgICAgICAgICAgICANCg0Ka25pdHI6OmthYmxlKGJhc2ljMV9jaGFyYWN0KQ0KYGBgDQoNCkVtIHNlZ3VpZGEsIGEgVGFiZWxhIDIgY29udMOpbSBvcyB0aXBvcyBlIGRlc2NyacOnw7VlcyBkYXMgOCB2YXJpw6F2ZWlzIGRlICp1c2FpcnBvbGx1dGlvbiouDQoNCkRlIGFjb3JkbyBjb20gYSBUYWJlbGEgMiwgbyBkYXRhc2V0IHBvc3N1aToNCg0KKiA0IHZhcmnDoXZlaXMgcXVhbnRpdGF0aXZhcyBkaXNjcmV0YXMgKFNPMixtYW51LHBvcHVsIGUgcHJlZGF5cyk7DQoqIDMgdmFyacOhdmVpcyBxdWFudGl0YXRpdmFzIGNvbnTDrW51YXMgKHRlbXAsIHdpbmQsIHByZWNpcCk7DQoqIDEgdmFyacOhdmVsIHF1YWxpdGF0aXZhIG5vbWluYWwgKENpdHkpLiANCg0KDQpgciB0YWJsZV9udW1zKCd0YWJfMicpYA0KYGBge3IgfSANCg0KdGlwb3MgPC0gTlVMTA0KZm9yKGkgaW4gMTpuY29sKHVzYWlycG9sbHV0aW9uKSl7DQogdGlwb3M8LSByYmluZCh0aXBvcywodHlwZW9mKHVzYWlycG9sbHV0aW9uWyxpXSkpKQ0KfQ0KDQpiYXNpYzJfY2hhcmFjdCA8LSBkYXRhLmZyYW1lKFZhcmnDoXZlbCA9IGNvbG5hbWVzKHVzYWlycG9sbHV0aW9uKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaXBvID0gdGlwb3MsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlc2NyacOnw6NvID0gYygiQ2lkYWRlIGRvcyBFLlUuQSIsIlRlb3IgZGUgJFNPXzIkIChkacOzeGlkbyBkZSBlbnhvZnJlKSBkbyBhciBlbSBtaWNyb2dyYW1hcyBwb3IgbWV0cm8gY8O6YmljbyIsIlRlbXBlcmF0dXJhIG3DqWRpYSBhbnVhbCBlbSBGYWhyZW5oZWl0IiwiTsK6IGRlIGVtcHJlc2FzIG1hbnVmYXR1cmVpcmFzIHF1ZSBlbXByZWdhbSAyMCBvdSBtYWlzIHRyYWJhbGhhZG9yZXMiLCJUYW1hbmhvIGRhIHBvcHVsYcOnw6NvIGVtIG1pbGhhcmVzIChDZW5zbyBkZSAxOTcwKSIsICJWZWxvY2lkYWRlIG3DqWRpYSBhbnVhbCBkbyB2ZW50byBlbSBtaWxoYXMgcG9yIGhvcmEiLCAiUHJlY2lwaXRhw6fDo28gbcOpZGlhIGFudWFsIGVtIHBvbGVnYWRhcyIsICJOwrogbcOpZGlvIGRlIGRpYXMgY29tIHByZWNpcGl0YcOnw6NvIHBvciBhbm8iKSkNCg0Ka25pdHI6OmthYmxlKGJhc2ljMl9jaGFyYWN0KQ0KDQpgYGANCg0KUG9yIGZpbSwgYW8gcmV0aXJhcm1vcyB1bWEgcGVxdWVuYSBhbW9zdHJhIGRvIGRhdGFzZXQsIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGNhZGEgbGluaGEgdHJheiBpbmZvcm1hw6fDo28gc29icmUgdW1hIGNpZGFkZS4NCg0KYHIgdGFibGVfbnVtcygndGFiXzMnKWANCmBgYHtyIH0NCmtuaXRyOjprYWJsZShoZWFkKHVzYWlycG9sbHV0aW9uKSkNCmBgYA0KDQoNCiMjIEVzdGF0w61zdGljYXMgcmVzdW1vIA0KDQpBIFRhYmVsYSA0IGNvbnTDqW0gYWxndW1hcyBlc3RhdMOtc3RpY2FzIHJlc3VtbyBwYXJhIGFzIHZhcmnDoXZlaXMgcXVhbnRpdGF0aXZhcyBkbyBkYXRhc2V0ICp1c2FpcnBvbGx1dGlvbiouDQoNCkRlIGFjb3JkbyBjb20gYSBUYWJlbGEgNCwgcGFyYSBhIHZhcmnDoXZlbCBkZSBpbnRlcmVzc2UgKip0ZW9yIGRlIGRpw7N4aWRvIGRlIGVueG9mcmUgZW0gbWljcm9ncmFtYXMgcG9yIG1ldHJvIGPDumJpY28qKiAoJFNPXzIvbV4zJCksIHRlbW9zIHF1ZToNCg0KKiBPcyB0ZW9yZXMgKiptw61uaW1vKiogZSAqKm3DoXhpbW8qKiBvYnNlcnZhZG9zIHPDo28gOMK1Zy9twrMgZSAxMTDCtWcvbcKzOyANCiogRGUgYWNvcmRvIGNvbSBvICoqcHJpbWVpcm8gcXVhcnRpbCoqLCAyNSUgZGFzIGNpZGFkZXMgZG8gY29uanVudG8gZGUgZGFkb3MgcG9zc3VlbSB0ZW9yICRTT18yJCBpZ3VhbCBvdSBpbmZlcmlvciBhIDEzwrVnL23CszsNCiogRGUgYWNvcmRvIGNvbSBhICoqbWVkaWFuYSoqLCA1MCUgZGFzIGNpZGFkZXMgZG8gY29uanVudG8gZGUgZGFkb3MgcG9zc3VlbSB0ZW9yIGRlICRTT18yJCBpZ3VhbCBvdSBhY2ltYSBkZSAyNsK1Zy9twrM7DQoqIERlIGFjb3JkbyBjb20gbyAqKnRlcmNlaXJvIHF1YXJ0aWwqKiwgMjUlIGRhcyBjaWRhZGVzIGRvIGNvbmp1bnRvIGRlIGRhZG9zIHBvc3N1ZW0gdGVvciAkU09fMiQgaWd1YWwgb3UgYWNpbWEgZGUgMzXCtWcvbcKzOw0KKiBEZSBhY29yZG8gY29tIG8gKippbnRlcnZhbG8gaW50ZXJxdWFydGlsKiosIDUwJSBkYXMgY2lkYWRlcyBwb3NzdWkgdGVvciBkZSAkU09fMiQgZW50cmUgMTPCtWcvbcKzIGUgMzXCtWcvbcKzLiANCiogTyAqKnRlb3IgbcOpZGlvKiogZGUgJFNPXzIvbV4zJCDDqSAzMC4wNDnCtWcvbcKzOw0KKiBPICoqZGVzdmlvIHBhZHLDo28qKiBkbyB0ZW9yIGRlICRTT18yL21eMyQgw6kgMjMuNDcywrVnL23Csy4NCg0KYHIgdGFibGVfbnVtcygndGFiXzQnKWANCmBgYHtyIH0NCmtuaXRyOjprYWJsZShnZXRfc3VtbWFyeV9zdGF0cyh1c2FpcnBvbGx1dGlvblssLTFdKVssLWMoMiw4LDksMTIsMTMpXSkNCmBgYA0KDQojIyBBc3NpbWV0cmlhIGUgRGVuc2lkYWRlcyANCg0KQSBUYWJlbGEgNSBjb250w6ltIG9zIGNvZWZpY2llbnRlcyBkZSBhc3NpbWV0cmlhIHBhcmEgYXMgdmFyacOhdmVpcyBxdWFudGl0YXRpdmFzIGRvIGRhdGFzZXQuDQoNCkEgYXNzaW1ldHJpYSBuZWdhdGl2YSBpbmRpY2EgcXVlIGEgbcOpZGlhIGRvcyBkYWRvcyDDqSBtZW5vciBxdWUgYSBtZWRpYW5hIGUsIHBvcnRhbnRvLCBxdWUgYSBkaXN0cmlidWnDp8OjbyBkb3MgZGFkb3Mgw6kgYXNzaW3DqXRyaWNhIMOgIGVzcXVlcmRhLiBKw6EgYSBhc3NpbWV0cmlhIHBvc2l0aXZhIGluZGljYSBxdWUgYSBtw6lkaWEgZG9zIGRhZG9zIMOpIG1haW9yIHF1ZSBhIG1lZGlhbmEgZSwgcG9ydGFudG8sIHF1ZSBhIGRpc3RyaWJ1acOnw6NvIGRvcyBkYWRvcyDDqSBhc3NpbcOpdHJpY2Egw6AgZGlyZWl0YS4gDQoNCmByIHRhYmxlX251bXMoJ3RhYl81JylgDQpgYGB7ciB9DQprbml0cjo6a2FibGUoYXBwbHkodXNhaXJwb2xsdXRpb25bLC0xXSwyLHNrZXduZXNzKSxjb2wubmFtZXMgPSAiQ29lZmljaWVudGUgZGUgYXNzaW1ldHJpYSIpDQpgYGANCkRlIGFjb3JkbyBjb20gYSBUYWJlbGEgNSwgdGVtb3MgcXVlOiANCg0KKiBBIGRpc3RyaWJ1acOnw6NvIGRlICoqU08yKiogw6kgYXNzaW3DqXRyaWNhIMOgICoqZGlyZWl0YSoqOw0KKiBBIGRpc3RyaWJ1acOnw6NvIGRlICoqdGVtcCoqIMOpIGFzc2ltw6l0cmljYSDDoCAqKmRpcmVpdGEqKjsNCiogQSBkaXN0cmlidWnDp8OjbyBkZSAqKm1hbnUqKiDDqSBhc3NpbcOpdHJpY2Egw6AgKipkaXJlaXRhKio7DQoqIEEgZGlzdHJpYnVpw6fDo28gZGUgKipwb3B1bCoqIMOpIGFzc2ltw6l0cmljYSDDoCAqKmRpcmVpdGEqKjsNCiogQSBkaXN0cmlidWnDp8OjbyBkZSAqKndpbmQqKiDDqSBhc3NpbcOpdHJpY2Egw6AgKipkaXJlaXRhKio7DQoqIEEgZGlzdHJpYnVpw6fDo28gZGUgKipwcmVjaXAqKiDDqSBhc3NpbcOpdHJpY2Egw6AgKiplc3F1ZXJkYSoqOyANCiogQSBkaXN0cmlidWnDp8OjbyBkZSAqKnByZWRheXMqKiDDqSBhc3NpbcOpdHJpY2Egw6AgKiplc3F1ZXJkYSoqLg0KDQpPcyBncsOhZmljb3MgYWJhaXhvIHPDo28gcmVsYXRpdm9zIMOgcyBkZW5zaWRhZGVzIGRlc3NhcyB2YXJpw6F2ZWlzLiBBdHJhdsOpcyBkZWxlcyDDqSBwb3Nzw612ZWwgdmlzdWFsaXphciBhIGluZm9ybWHDp8OjbyBjb250aWRhIG5vcyBjb2VmaWNpZW50ZXMgZGUgYXNzaW1ldHJpYS4gDQoNCg0KYGBge3IgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjUwJSIsIHdhcm5pbmc9RkFMU0V9DQoNCnBhcihtZnJvdz1jKDQsMikpDQoNCmdncGxvdCh1c2FpcnBvbGx1dGlvbiwgYWVzKHg9U08yKSkgKyANCiAgZ2VvbV9kZW5zaXR5KCkrDQpzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTIwLGJ5PTEwKSwgbGltaXRzPSBjKDAsMTIwKSkrDQpsYWJzKHRpdGxlID0gIkRlbnNpZGFkZSBkYSB2YXJpw6F2ZWwgU08yIFxuIiwNCiAgICAgIHggPSAiXG5UZW9yIGRlIFNPMiBlbSDCtWcvbcKzIFxuIiwNCiAgICAgIHkgPSAiRGVuc2lkYWRlXG4iKSsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmdncGxvdCh1c2FpcnBvbGx1dGlvbiwgYWVzKHg9dGVtcCkpICsgDQogIGdlb21fZGVuc2l0eSgpKw0Kc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEyMCxieT0xMCksIGxpbWl0cz0gYygwLDEyMCkpKw0KbGFicyh0aXRsZSA9ICJEZW5zaWRhZGUgZGEgdmFyacOhdmVsIHRlbXAgXG4iLA0KICAgICB4ID0gIlxuVGVtcGVyYXR1cmEgbcOpZGlhIGFudWFsICjCsEYpIiwNCiAgICAgIHkgPSAiRGVuc2lkYWRlXG4iKSsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmdncGxvdCh1c2FpcnBvbGx1dGlvbiwgYWVzKHg9bWFudSkpICsgDQogIGdlb21fZGVuc2l0eSgpKw0Kc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDM0MDAsYnk9MjAwKSwgbGltaXRzPSBjKDAsMzQwMCkpKw0KbGFicyh0aXRsZSA9ICJEZW5zaWRhZGUgZGEgdmFyacOhdmVsIG1hbnUgXG4iLA0KICAgICB4ID0gIlxuIE7CuiBkZSBlbXByZXNhcyBtYW51ZmF0dXJlaXJhcyBxdWUgZW1wcmVnYW0gMjAgb3UgbWFpcyB0cmFiYWxoYWRvcmVzIiwNCiAgICAgIHkgPSAiRGVuc2lkYWRlXG4iKSsNCiAgdGhlbWVfY2xhc3NpYygpDQogIA0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD1wb3B1bCkpICsgDQogIGdlb21fZGVuc2l0eSgpKw0Kc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDM0MDAsYnk9MjAwKSwgbGltaXRzPSBjKDAsMzQwMCkpKw0KbGFicyh0aXRsZSA9ICJEZW5zaWRhZGUgZGEgdmFyacOhdmVsIHBvcHVsIFxuIiwNCiAgICAgeCA9ICJcbiBUYW1hbmhvIGRhIHBvcHVsYcOnw6NvIChDZW5zbyBkZSAxOTcwKSBlbSBtaWxoYXJlcyIsDQogICAgICB5ID0gIkRlbnNpZGFkZVxuIikrDQogIHRoZW1lX2NsYXNzaWMoKQ0KICAgIA0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD13aW5kKSkgKyANCiAgZ2VvbV9kZW5zaXR5KCkrDQpzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTIwLGJ5PTEwKSwgbGltaXRzPSBjKDAsMTIwKSkrDQpsYWJzKHRpdGxlID0gIkRlbnNpZGFkZSBkYSB2YXJpw6F2ZWwgd2luZCBcbiIsDQogICAgIHggPSAiXG4gVmVsb2NpZGFkZSBtw6lkaWEgYW51YWwgZG8gdmVudG8gZW0gbWlsaGFzIHBvciBob3JhIiwNCiAgICAgIHkgPSAiRGVuc2lkYWRlXG4iKSsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCg0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD1wcmVjaXApKSArIA0KICBnZW9tX2RlbnNpdHkoKSsNCnNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsYnk9MTApLCBsaW1pdHM9IGMoMCwxMjApKSsNCmxhYnModGl0bGUgPSAiRGVuc2lkYWRlIGRhIHZhcmnDoXZlbCBwcmVjaXAgXG4iLA0KICAgICB4ID0gIlxuIFByZWNpcGl0YcOnw6NvIG3DqWRpYSBhbnVhbCBlbSBwb2xlZ2FkYXMiLA0KICAgICAgeSA9ICJEZW5zaWRhZGVcbiIpKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD1wcmVkYXlzKSkgKyANCiAgZ2VvbV9kZW5zaXR5KCkrDQpzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMjAwLGJ5PTEwKSwgbGltaXRzPSBjKDAsMjAwKSkrDQpsYWJzKHRpdGxlID0gIkRlbnNpZGFkZSBkYSB2YXJpw6F2ZWwgcHJlZGF5cyBcbiIsDQogICAgIHggPSAiXG4gTsK6IG3DqWRpbyBkZSBkaWFzIGNvbSBwcmVjaXBpdGHDp8OjbyBwb3IgYW5vIiwNCiAgICAgIHkgPSAiRGVuc2lkYWRlXG4iKSsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCg0KYGBgDQoNCg0KIyMgQm94cGxvdHMNCg0KT3MgZ3LDoWZpY29zIGFiYWl4byBzw6NvIHJlbGF0aXZvcyBhb3MgYm94cGxvdHMgZGFzIHZhcmnDoXZlaXMgcXVhbnRpdGF0aXZhcyBkbyBkYXRhc2V0ICp1c2FpcnBvbGx1dGlvbiouIEF0cmF2w6lzIGRlbGVzIMOpIHBvc3PDrXZlbCB2aXN1YWxpemFyIGEgZGlzdHJpYnVpw6fDo28gZG9zIGRhZG9zLCBhc3NpbWV0cmlhLCBxdWFydGlzIGUgb3V0bGllcnMuDQoNCg0KYGBge3IgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjUwJSIsIHdhcm5pbmc9RkFMU0V9DQoNCnBhcihtZnJvdz1jKDQsMikpDQpjb3JlcyA8LSBicmV3ZXIucGFsKG4gPSA3LCBuYW1lID0gIlNldDEiKQ0KDQoNCmE8LWdncGxvdChkYXRhID0gdXNhaXJwb2xsdXRpb24sIGFlcyAoeSA9IFNPMikpKw0KZ2VvbV9ib3hwbG90KGZpbGw9Y29yZXNbMV0sIGNvbG9yPSJibGFjayIpICsNCmxhYnModGl0bGUgPSAiRGlzdHJpYnVpw6fDo28gZGEgdmFyacOhdmVsIFNPMiBcbiIsDQogICAgICAgeSA9ICIgVGVvciBkZSBTTzIgZW0gwrVnL23CsyBcbiIpKw0Kc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEzMCxieT0xMCksIGxpbWl0cz0gYygwLDEzMCkpKw0Kc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1OVUxMKSsNCnRoZW1lX2NsYXNzaWMoKQ0KYQ0KDQpiPC0gZ2dwbG90KGRhdGEgPSB1c2FpcnBvbGx1dGlvbiwgYWVzICh5ID0gdGVtcCkpKw0KZ2VvbV9ib3hwbG90KGZpbGw9Y29yZXNbMl0sIGNvbG9yPSJibGFjayIpICsNCmxhYnModGl0bGUgPSAiRGlzdHJpYnVpw6fDo28gZGEgdmFyacOhdmVsIHRlbXAgXG4iLA0KICAgICAgIHkgPSAiVGVtcGVyYXR1cmEgbcOpZGlhIGFudWFsICjCsEYpIFxuIikrDQpzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTMwLGJ5PTEwKSwgbGltaXRzPSBjKDAsMTMwKSkrDQpzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPU5VTEwpKw0KdGhlbWVfY2xhc3NpYygpDQpiDQpjPC0gZ2dwbG90KGRhdGEgPSB1c2FpcnBvbGx1dGlvbiwgYWVzICh5ID0gbWFudSkpKw0KZ2VvbV9ib3hwbG90KGZpbGw9Y29yZXNbM10sIGNvbG9yPSJibGFjayIpICsNCmxhYnModGl0bGUgPSAiRGlzdHJpYnVpw6fDo28gZGEgdmFyacOhdmVsIG1hbnUgXG4iLA0KICAgICAgIHkgPSAiIE7CuiBkZSBlbXByZXNhcyBtYW51ZmF0dXJlaXJhcyBxdWUgZW1wcmVnYW0gMjAgb3UgbWFpcyB0cmFiYWxoYWRvcmVzIFxuIikrDQpzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMzQwMCxieT0yMDApLCBsaW1pdHM9IGMoMCwzNDAwKSkrDQpzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPU5VTEwpKw0KdGhlbWVfY2xhc3NpYygpDQpjDQoNCmQ8LSBnZ3Bsb3QoZGF0YSA9IHVzYWlycG9sbHV0aW9uLCBhZXMgKHkgPSBwb3B1bCkpKw0KZ2VvbV9ib3hwbG90KGZpbGw9Y29yZXNbNF0sIGNvbG9yPSJibGFjayIpICsNCmxhYnModGl0bGUgPSAiRGlzdHJpYnVpw6fDo28gZGEgdmFyacOhdmVsIHBvcHVsIFxuIiwNCiAgICAgICB5ID0gIiBUYW1hbmhvIGRhIHBvcHVsYcOnw6NvIChDZW5zbyBkZSAxOTcwKSBlbSBtaWxoYXJlcyBcbiIpKw0Kc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDM0MDAsYnk9MjAwKSwgbGltaXRzPSBjKDAsMzQwMCkpKw0Kc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1OVUxMKSsNCnRoZW1lX2NsYXNzaWMoKQ0KZA0KDQplPC1nZ3Bsb3QoZGF0YSA9IHVzYWlycG9sbHV0aW9uLCBhZXMgKHkgPSB3aW5kKSkrDQpnZW9tX2JveHBsb3QoZmlsbD1jb3Jlc1s1XSwgY29sb3I9ImJsYWNrIikgKw0KbGFicyh0aXRsZSA9ICJEaXN0cmlidWnDp8OjbyBkYSB2YXJpw6F2ZWwgd2luZCBcbiIsDQogICAgICAgeSA9ICIgVmVsb2NpZGFkZSBtw6lkaWEgYW51YWwgZG8gdmVudG8gZW0gbWlsaGFzIHBvciBob3JhIFxuIikrDQpzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsNTAsYnk9MiksIGxpbWl0cz0gYygwLDUwKSkrDQpzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPU5VTEwpKw0KdGhlbWVfY2xhc3NpYygpDQplDQoNCg0KZjwtZ2dwbG90KGRhdGEgPSB1c2FpcnBvbGx1dGlvbiwgYWVzICh5ID0gcHJlY2lwKSkrDQpnZW9tX2JveHBsb3QoZmlsbD1jb3Jlc1s2XSwgY29sb3I9ImJsYWNrIikgKw0KbGFicyh0aXRsZSA9ICJEaXN0cmlidWnDp8OjbyBkYSB2YXJpw6F2ZWwgcHJlY2lwIFxuIiwNCiAgICAgICB5ID0gIiBQcmVjaXBpdGHDp8OjbyBtw6lkaWEgYW51YWwgZW0gcG9sZWdhZGFzIFxuIikrDQpzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTMwLGJ5PTEwKSwgbGltaXRzPSBjKDAsMTMwKSkrDQpzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPU5VTEwpKw0KdGhlbWVfY2xhc3NpYygpDQpmDQoNCmc8LWdncGxvdChkYXRhID0gdXNhaXJwb2xsdXRpb24sIGFlcyAoeSA9IHByZWRheXMpKSsNCmdlb21fYm94cGxvdChmaWxsPWNvcmVzWzddLCBjb2xvcj0iYmxhY2siKSArDQpsYWJzKHRpdGxlID0gIkRpc3RyaWJ1acOnw6NvIGRhIHZhcmnDoXZlbCBwcmVkYXlzIFxuIiwNCiAgICAgICB5ID0gIiBOwrogbcOpZGlvIGRlIGRpYXMgY29tIHByZWNpcGl0YcOnw6NvIHBvciBhbm8gXG4iKSsNCnNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxODAsYnk9MTApLCBsaW1pdHM9IGMoMCwxODApKSsNCnNjYWxlX3hfY29udGludW91cyhsYWJlbHM9TlVMTCkrDQp0aGVtZV9jbGFzc2ljKCkNCmcNCmBgYA0KDQojIyBPdXRsaWVycyANCg0KUGFyYSBhIHZhcmnDoXZlbCAqU08yKiwgb3Mgb3V0bGllcnMgY29ycmVzcG9uZGVtIMOgcyBtZWRpw6fDtWVzIGRlIGRpw7N4aWRvIGRlIGVueG9mcmUgbmFzIGNpZGFkZXMgZGUgQ2hpY2FnbywgUGhpbGFkZWxwaGlhIGUgUHJvdmlkZW5jZS4gDQoNCmByIHRhYmxlX251bXMoJ3RhYl82JylgDQpgYGB7ciB9DQpvdXQ8LSBnZ3Bsb3RfYnVpbGQoYSlbWyJkYXRhIl1dW1sxXV1bWyJvdXRsaWVycyJdXQ0Ka25pdHI6OmthYmxlKHVzYWlycG9sbHV0aW9uW3doaWNoKHVzYWlycG9sbHV0aW9uJFNPMj09b3V0W1sxXV1bMV18IHVzYWlycG9sbHV0aW9uJFNPMj09b3V0W1sxXV1bMl0gfHVzYWlycG9sbHV0aW9uJFNPMj09b3V0W1sxXV1bM10pLGMoMSwyKV0scm93Lm5hbWVzPUZBTFNFKQ0KYGBgDQpQYXJhIGEgdmFyacOhdmVsICp0ZW1wKiwgbyBvdXRsaWVyIGNvcnJlc3BvbmRlIGEgdGVtcGVyYXR1cmEgbcOpZGlhIGFudWFsKMKwRikgbmEgY2lkYWRlIGRlIE1pYW1pLg0KDQpgciB0YWJsZV9udW1zKCd0YWJfNycpYA0KDQpgYGB7ciB9DQpvdXQ8LSBnZ3Bsb3RfYnVpbGQoYilbWyJkYXRhIl1dW1sxXV1bWyJvdXRsaWVycyJdXQ0Ka25pdHI6OmthYmxlKHVzYWlycG9sbHV0aW9uW3doaWNoKHVzYWlycG9sbHV0aW9uJHRlbXA9PW91dFtbMV1dWzFdKSxjKDEsMyldLHJvdy5uYW1lcz1GQUxTRSkNCmBgYA0KUGFyYSBhIHZhcmnDoXZlbCAqbWFudSosIG9zIG91dGxpZXJzIGNvcnJlc3BvbmRlbSBhbyBuwrogZGUgZW1wcmVzYXMgbWFudWZhdHVyZWlyYXMgcXVlIGVtcHJlZ2FtIDIwIG91IG1haXMgdHJhYmFsaGFkb3JlcyBuYXMgY2lkYWRlcyBkZSBDaGljYWdvLCBDbGV2ZWxhbmQsIERldHJvaXQgZSBQaGlsYWRlbHBoaWEuIA0KDQpgciB0YWJsZV9udW1zKCd0YWJfOCcpYA0KYGBge3IgfQ0Kb3V0PC0gZ2dwbG90X2J1aWxkKGMpW1siZGF0YSJdXVtbMV1dW1sib3V0bGllcnMiXV0NCmtuaXRyOjprYWJsZSh1c2FpcnBvbGx1dGlvblt3aGljaCh1c2FpcnBvbGx1dGlvbiRtYW51PT1vdXRbWzFdXVsxXXx1c2FpcnBvbGx1dGlvbiRtYW51PT1vdXRbWzFdXVsyXXx1c2FpcnBvbGx1dGlvbiRtYW51PT1vdXRbWzFdXVszXXx1c2FpcnBvbGx1dGlvbiRtYW51PT1vdXRbWzFdXVs0XSksYygxLDQpXSxyb3cubmFtZXM9RkFMU0UpDQpgYGANClBhcmEgYSB2YXJpw6F2ZWwgKnBvcHVsKiwgb3Mgb3V0bGllcnMgY29ycmVzcG9uZGVtIGFvIHRhbWFuaG8gZGEgcG9wdWxhw6fDo28gKG1pbGhhcmUpIG5hcyBjaWRhZGVzIGRlIENoaWNhZ28sIERldHJvaXQgZSBQaGlsYWRlbHBoaWEuIA0KDQoNCmByIHRhYmxlX251bXMoJ3RhYl85JylgDQpgYGB7ciB9DQpvdXQ8LSBnZ3Bsb3RfYnVpbGQoZClbWyJkYXRhIl1dW1sxXV1bWyJvdXRsaWVycyJdXQ0Ka25pdHI6OmthYmxlKHVzYWlycG9sbHV0aW9uW3doaWNoKHVzYWlycG9sbHV0aW9uJHBvcHVsPT1vdXRbWzFdXVsxXXx1c2FpcnBvbGx1dGlvbiRwb3B1bD09b3V0W1sxXV1bMl18dXNhaXJwb2xsdXRpb24kcG9wdWw9PW91dFtbMV1dWzNdKSxjKDEsNSldLHJvdy5uYW1lcz1GQUxTRSkNCmBgYA0KDQo+IEEgdmFyacOhdmVsICp3aW5kKiBuw6NvIHBvc3N1aSBvdXRsaWVycy4NCg0KUGFyYSBhIHZhcmnDoXZlbCAqcHJlY2lwKiwgb3Mgb3V0bGllcnMgY29ycmVzcG9uZGVtIGEgcHJlY2lwaXRhw6fDo28gbcOpZGlhIGFudWFsIGVtIHBvbGVnYWRhcyBuYXMgY2lkYWRlcyBkZSBBbGJ1cXVlcnF1ZSBlIFBob2VuaXguIA0KDQpgciB0YWJsZV9udW1zKCd0YWJfMTAnKWANCmBgYHtyIH0NCm91dDwtIGdncGxvdF9idWlsZChmKVtbImRhdGEiXV1bWzFdXVtbIm91dGxpZXJzIl1dDQprbml0cjo6a2FibGUodXNhaXJwb2xsdXRpb25bd2hpY2godXNhaXJwb2xsdXRpb24kcHJlY2lwPT1vdXRbWzFdXVsxXXx1c2FpcnBvbGx1dGlvbiRwcmVjaXA9PW91dFtbMV1dWzJdKSxjKDEsNyldLHJvdy5uYW1lcz1GQUxTRSkNCmBgYA0KUG9yIGZpbSwgcGFyYSBhIHZhcmnDoXZlbCAqcHJlZGF5cyogb3Mgb3V0bGllcnMgY29ycmVzcG9uZGVtIGFvIG7Dum1lcm8gbcOpZGlvIGRlIGRpYXMgY29tIHByZWNpcGl0YcOnw6NvIHBvciBhbm8gbmFzIGNpZGFkZXMgZGUgQWxidXF1ZXJxdWUsIFBob2VuaXggZSBTYW4gRnJhbmNpc2NvLiANCg0KYHIgdGFibGVfbnVtcygndGFiXzExJylgDQpgYGB7ciAsIHdhcm5pbmc9RkFMU0V9DQpvdXQ8LSBnZ3Bsb3RfYnVpbGQoZylbWyJkYXRhIl1dW1sxXV1bWyJvdXRsaWVycyJdXQ0Ka25pdHI6OmthYmxlKHVzYWlycG9sbHV0aW9uW3doaWNoKHVzYWlycG9sbHV0aW9uJHByZWRheXM9PW91dFtbMV1dWzFdfHVzYWlycG9sbHV0aW9uJHByZWRheXM9PW91dFtbMV1dWzJdIHwgdXNhaXJwb2xsdXRpb24kcHJlZGF5cz09b3V0W1sxXV1bM10pLGMoMSw4KV0scm93Lm5hbWVzPUZBTFNFKQ0KYGBgDQojIyBHcsOhZmljb3MgZGUgZGlzcGVyc8OjbyAgDQoNCkZvcmFtIGZlaXRvcyBncsOhZmljb3MgZGUgZGlzcGVyc8OjbyBlbnRyZSBhIHZhcmnDoXZlbCBTTzIgZSBhcyBkZW1haXMgYSBmaW0gZGUgdmVyaWZpY2FyIHBvc3PDrXZlaXMgcmVsYcOnw7VlcywgZXNwZWNpYWxtZW50ZSBhcyBsaW5lYXJlcy4gDQpFbnRyZXRhbnRvLCBhbyBvYnNlcnZhciBvcyBncsOhZmljb3MgbsOjbyBmb2kgcG9zc8OtdmVsIGlkZW50aWZpY2FyIHZpc3VhbG1lbnRlIHF1YWlzcXVlciByZWxhw6fDtWVzLiANCg0KYGBge3IgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjUwJSIgfQ0KDQpwYXIobWZyb3c9YygzLDIpKQ0KY29yZXMyIDwtIGJyZXdlci5wYWwobiA9IDYsIG5hbWUgPSAiRGFyazIiKQ0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD10ZW1wLCB5PVNPMikpICsNCmdlb21fcG9pbnQoY29sb3I9Y29yZXMyWzFdKSsNCmxhYnModGl0bGUgPSAiR3LDoWZpY28gZGUgZGlzcGVyc8OjbyBlbnRyZSBhcyB2YXJpw6F2ZWlzIFNPMiBlIHRlbXAiLA0KICAgICAgeSA9ICIgVGVvciBkZSBTTzIgZW0gwrVnL23CsyBcbiIsDQogICAgICB4ID0gIlxuVGVtcGVyYXR1cmEgbcOpZGlhIGFudWFsICjCsEYpIikrDQp0aGVtZV9jbGFzc2ljKCkNCg0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD1tYW51LCB5PVNPMikpICsNCmdlb21fcG9pbnQoY29sb3I9Y29yZXMyWzJdKSsNCmxhYnModGl0bGUgPSAiR3LDoWZpY28gZGUgZGlzcGVyc8OjbyBlbnRyZSBhcyB2YXJpw6F2ZWlzIFNPMiBlIG1hbnUiLA0KICAgICAgeSA9ICIgVGVvciBkZSBTTzIgZW0gwrVnL23CsyBcbiIsDQogICAgICB4ID0gIlxuIE7CuiBkZSBlbXByZXNhcyBtYW51ZmF0dXJlaXJhcyBxdWUgZW1wcmVnYW0gMjAgb3UgbWFpcyB0cmFiYWxoYWRvcmVzIikrDQp0aGVtZV9jbGFzc2ljKCkNCg0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD1wb3B1bCwgeT1TTzIpKSArDQpnZW9tX3BvaW50KGNvbG9yPWNvcmVzMlszXSkrDQpsYWJzKHRpdGxlID0gIkdyw6FmaWNvIGRlIGRpc3BlcnPDo28gZW50cmUgYXMgdmFyacOhdmVpcyBTTzIgZSBwb3B1bCIsDQogICAgICB5ID0gIiBUZW9yIGRlIFNPMiBlbSDCtWcvbcKzIFxuIiwNCiAgICAgIHggPSAiXG4gVGFtYW5obyBkYSBwb3B1bGHDp8OjbyAoQ2Vuc28gZGUgMTk3MCkgZW0gbWlsaGFyZXMiKSsNCnRoZW1lX2NsYXNzaWMoKQ0KDQpnZ3Bsb3QodXNhaXJwb2xsdXRpb24sIGFlcyh4PXdpbmQsIHk9U08yKSkgKw0KZ2VvbV9wb2ludChjb2xvcj1jb3JlczJbNF0pKw0KbGFicyh0aXRsZSA9ICJHcsOhZmljbyBkZSBkaXNwZXJzw6NvIGVudHJlIGFzIHZhcmnDoXZlaXMgU08yIGUgd2luZCIsDQogICAgICB5ID0gIiBUZW9yIGRlIFNPMiBlbSDCtWcvbcKzIFxuIiwNCiAgICAgIHggPSAiXG4gVmVsb2NpZGFkZSBtw6lkaWEgYW51YWwgZG8gdmVudG8gZW0gbWlsaGFzIHBvciBob3JhIikrDQp0aGVtZV9jbGFzc2ljKCkNCg0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD1wcmVjaXAsIHk9U08yKSkgKw0KZ2VvbV9wb2ludChjb2xvcj1jb3JlczJbNV0pKw0KbGFicyh0aXRsZSA9ICJHcsOhZmljbyBkZSBkaXNwZXJzw6NvIGVudHJlIGFzIHZhcmnDoXZlaXMgU08yIGUgcHJlY2lwIiwNCiAgICAgIHkgPSAiIFRlb3IgZGUgU08yIGVtIMK1Zy9twrMgXG4iLA0KICAgICAgeCA9ICJcbiBQcmVjaXBpdGHDp8OjbyBtw6lkaWEgYW51YWwgZW0gcG9sZWdhZGFzIikrDQp0aGVtZV9jbGFzc2ljKCkNCg0KZ2dwbG90KHVzYWlycG9sbHV0aW9uLCBhZXMoeD1wcmVkYXlzLCB5PVNPMikpICsNCmdlb21fcG9pbnQoY29sb3I9Y29yZXMyWzZdKSsNCmxhYnModGl0bGUgPSAiR3LDoWZpY28gZGUgZGlzcGVyc8OjbyBlbnRyZSBhcyB2YXJpw6F2ZWlzIFNPMiBlIHByZWRheXMiLA0KICAgICAgeSA9ICIgVGVvciBkZSBTTzIgZW0gwrVnL23CsyBcbiIsDQogICAgICB4ID0gIlxuIE7CuiBtw6lkaW8gZGUgZGlhcyBjb20gcHJlY2lwaXRhw6fDo28gcG9yIGFubyIpKw0KdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KIyMgQ29ycmVsb2dyYW1hDQoNCk8gY29ycmVsb2dyYW1hIGFiYWl4byBtb3N0cmEgb3MgY29lZmljaWVudGVzIGRlIGNvcnJlbGHDp8OjbyBkZSBQZWFyc29uIHBhcmEgdG9kYXMgYXMgdmFyacOhdmVpcyBxdWFudGl0YXRpdmFzIGRvIGRhdGFzZXQuDQoNCg0KYGBge3IgfQ0KY29yciA8LSBjb3IodXNhaXJwb2xsdXRpb25bLC0xXSkgI01hdHJpeiBkZSBjb3JyZWxhw6fDo28NCiNwLm1hdCA8LSBjb3JfcG1hdCh1c2FpcnBvbGx1dGlvblssLTFdKSAjIE1hdHJpeiBkZSBwLXZhbG9yZXMgZGFzIGNvcnJlbGHDp8O1ZXMNCmdnY29ycnBsb3QoY29ycixvdXRsaW5lLmNvbG9yID0gIndoaXRlIix0eXBlID0gImxvd2VyIixsYWIgPSBUUlVFKQ0KYGBgDQoNCiogQSB2YXJpw6F2ZWwgZGUgaW50ZXJlc3NlICpTTzIqIHBvc3N1aSBtYWlvciBjb3JyZWxhw6fDo28gY29tIGEgdmFyacOhdmVsIG1hbnUsIDAuNjQuDQoNCiogQXMgdmFyacOhdmVpcyAqbWFudSogZSAqcG9wdWwqIHBvc3N1ZW0gY29ycmVsYcOnw6NvIDAuOTYsIG8gcXVlIHNpZ25pZmljYSBxdWUgZXNzYXMgdmFyacOhdmVpcyBwb3NzdWVtIHVtYSByZWxhw6fDo28gbGluZWFyIHBvc2l0aXZhOiBxdWFudG8gIG1haW9yIGEgcG9wdWxhw6fDo28sIG1haW9yIG8gbsO6bWVybyBkZSBlbXByZXNhcyBtYW51ZmF0dXJlaXJhcy4NCg0KDQojIyBEaXN0cmlidWnDp8OjbyBnZW9ncsOhZmljYSBkYSB2YXJpw6F2ZWwgU08yDQoNClBhcmEgcHJvZHV6aXIgbyBtYXBhIGRlIGNhbG9yIHBhcmEgYSB2YXJpw6F2ZWwgdGVvciBkZSBkacOzeGlkbyBkZSBlbnhvZnJlIChTTzIpLCBmb2kgdXRpbGl6YWRvIHVtIFtkYXRhc2V0XShodHRwczovL2dpdGh1Yi5jb20vY2VzdGFzdGFuZm9yZC9oaXN0b3JpY2FsLXVzLWNpdHktcG9wdWxhdGlvbnMvYmxvYi9tYXN0ZXIvZGF0YS8xNzkwLTIwMTBfTUFTVEVSLmNzdikgcXVlIGNvbnTDqW0gcG9wdWxhw6fDtWVzIGRlIGNpZGFkZXMgZG9zIEUuVS5BIGRlIDE3OTAgYSAyMDEwIGUgc3VhcyByZXNwZWN0aXZhcyBsYXRpdHVkZXMgZSBsb25naXR1ZGVzLCBhcXVpIG5vbWVhZG8gKnBvcHVsXzE3OTAuMjAxMCouDQoNCk8gcHJpbWVpcm8gcGFzc28gZGEgY29uc3RydcOnw6NvIGRvIG1hcGEgZGUgY2Fsb3IgY29uc2lzdGl1IGVtIGZhemVyIHVtIG1lcmdlIGVudHJlIG9zIGRhdGFzZXRzICpwb3B1bF8xNzkwLjIwMTAqIGUgKnVzYWlycG9sbHV0aW9uKiBwZWxhIHZhcmnDoXZlbCBjaGF2ZSAqQ2l0eSosIGEgZmltIGRlIG9idGVyIGFzIGxhdGl0dWRlcyBlIGxvbmdpdHVkZXMuIE8gcmVzdWx0YWRvIGZvaSB1bSBkYXRhc2V0IGNvbSAxMzAgb2JzZXJ2YcOnw7VlcywgdmlzdG8gcXVlIGV4aXN0ZW0gY2lkYWRlcyBkZSBtZXNtbyBub21lIG1hcyBxdWUgcGVydGVuY2VtIGEgZXN0YWRvcyBkaWZlcmVudGVzLiANCg0KQ29tIG8gb2JqZXRpdm8gZGUgZWxpbWluYXIgbcO6bHRpcGxhcyBlbnRyYWRhcyBwYXJhIGEgbWVzbWEgY2lkYWRlLCBmb2kgZmVpdGEgdW1hIGNvbXBhcmHDp8OjbyBlbnRyZSB2YXJpw6F2ZWlzICpwb3B1bCogZSAqWDE5NzAqLCBhbWJhcyByZWZlcmVudGVzIMOgIHBvcHVsYcOnw6NvIGRlc3NhcyBjaWRhZGVzIGRlIGFjb3JkbyBjb20gbyBDZW5zbyBkZSAxOTcwLiBGb3JhbSBtYW50aWRhcyBlbnRyYWRhcyDDum5pY2FzIHBhcmEgY2FkYSBjaWRhZGUsIGRlIGZvcm1hIHF1ZSBvIHF1YWRyYWRvIGRhIGRpZmVyZW7Dp2EgZW50cmUgKnBvcHVsKiBlICpYMTk3MCogZm9zc2UgbcOtbmltYS4gDQoNCg0KYGBge3IgfQ0KcG9wdWxfMTc5MC4yMDEwIDwtIHJlYWQuY3N2KCJFOi9NdWx0aXZhcmlhZGEvQXRpdmlkYWRlIDAxLzE3OTAtMjAxMF9NQVNURVIudHh0IikNCnggPC0gbWVyZ2UodXNhaXJwb2xsdXRpb24scG9wdWxfMTc5MC4yMDEwLGJ5PSJDaXR5IikNCngkcG9wdWwgPC0geCRwb3B1bCoxMDAwDQoNCmNpdGllcyA8LSBhcy5jaGFyYWN0ZXIodW5pcXVlKHgkQ2l0eSkpDQp5IDwtIE5VTEwNCmZvcihpIGluIDE6bGVuZ3RoKGNpdGllcykpew0KICANCiAga2VlcCA8LSB3aGljaCh4JENpdHk9PWNpdGllc1tpXSlbd2hpY2gubWluKCh4JHBvcHVsW3doaWNoKHgkQ2l0eT09Y2l0aWVzW2ldKV0gLSB4JFgxOTcwW3doaWNoKHgkQ2l0eT09Y2l0aWVzW2ldKV0pXjIpXQ0KICB5IDwtIHJiaW5kKHkseFtrZWVwLF0pDQp9DQoNCmBgYA0KDQpDb20gb3MgZGFkb3MgZGFzIGxhdGl0dWRlcyBlIGxvbmdpdHVkZXMgZW0gbcOjb3MgZm9pIHBvc3PDrXZlbCBjb25zdHJ1aXIgbyBtYXBhIGFiYWl4bywgcXVlIGNvbnTDqW0gb3MgdGVvcmVzIGRlICRTT18yJCBwYXJhIGFzIDQxIGNpZGFkZXMgZG8gZGF0YXNldCAqdXNhaXJwb2xsdXRpb24qLiBDYWRhIHBvbnRvIHJlcHJlc2VudGEgdW1hIGNpZGFkZSBlLCBxdWFudG8gbWFpb3IgbyB0YW1hbmhvIGRvIHBvbnRvIG91IG1haXMgcHLDs3hpbW8gZGUgYW1hcmVsbyBhIHN1YSBjb3IsIG1haW9yIG8gdGVvciBkZSAkU09fMiQgb2JzZXJ2YWRvLiBBcyBjaWRhZGVzIGNvbSBhcyBtYWlvcmVzIG1lZGnDp8O1ZXMgZGUgJFNPXzIkIHPDo28gQ2hpY2FnbywgUHJvdmlkZW5jZSwgUGhpbGFkZWxwaGlhLCBDbGV2ZWxhbmQgZSBQaXR0c2J1cmdoLg0KDQoNCmBgYHtyIGZpZy5zaG93PSJob2xkIiwgd2FybmluZz1GQUxTRX0NCg0KDQpnZ3Bsb3QoeSwgYWVzKExPTiwgTEFULGxhYmVsPUNpdHkpKSArIGJvcmRlcnMoInN0YXRlIikgKyANCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IFNPMiwgZmlsbD1TTzIpLGNvbG91cj0iYmxhY2siLCBzaGFwZT0yMSkgKyANCiAgY29vcmRfcXVpY2ttYXAoKSArDQogIGdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsPWlmZWxzZShTTzI+NjAsYXMuY2hhcmFjdGVyKENpdHkpLCcnKSksIA0KICAgICAgICAgICAgICAgICAgIHNpemUgPSAyLA0KICAgICAgICAgICAgICAgICAgIGJveC5wYWRkaW5nICAgPSAwLjM1LCANCiAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC41LA0KICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuY29sb3IgPSAnZ3JleTUwJykrDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVpw6fDo28gZ2VvZ3LDoWZpY2EgZGEgdmFyacOhdmVsIFNPMlxuIiwNCiAgICAgIHkgPSAiIExhdGl0dWRlXG4iLA0KICAgICAgeCA9ICJcbkxvbmdpdHVkZSIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrDQogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAiQyIpKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQoNCg0KIyBBbsOhbGlzZSBkZSBDb21wb25lbnRlcyBQcmluY2lwYWlzIChQQ0EpDQoNCkFzIGNvbXBvbmVudGVzIHByaW5jaXBhaXMgc8OjbyBub3ZhcyB2YXJpw6F2ZWlzIHF1ZSBzw6NvIGNvbnN0cnXDrWRhcyBjb21vIGNvbWJpbmHDp8O1ZXMgbGluZWFyZXMgZGFzIHZhcmnDoXZlaXMgaW5pY2lhaXMuIA0KDQpFc3NhcyBjb21iaW5hw6fDtWVzIHPDo28gZmVpdGFzIGRlIGZvcm1hIHF1ZSBlc3NhcyBub3ZhcyB2YXJpw6F2ZWlzIG7Do28gc2VqYW0gY29ycmVsYWNpb25hZGFzIGUgYSBtYWlvcmlhIGRhcyBpbmZvcm1hw6fDtWVzIGNvbnRpZGFzIG5hcyB2YXJpw6F2ZWlzIGluaWNpYWlzIHNlamEgYXJtYXplbmFkYSBub3MgcHJpbWVpcm9zIGNvbXBvbmVudGVzLiANCg0KRW50w6NvLCBhIGlkZWlhIMOpIHF1ZSBkYWRvcyBrLWRpbWVuc2lvbmFpcyBmb3JuZcOnYW0gayBjb21wb25lbnRlcyBwcmluY2lwYWlzLg0KTyBtw6l0b2RvIGJ1c2NhIGNvbG9jYXIgbyBtw6F4aW1vIGRlIGluZm9ybWHDp8O1ZXMgcG9zc8OtdmVpcyBuYXMgcHJpbWVpcmFzIGNvbXBvbmVudGVzLCBwYXJhIHF1ZSwgc2UgcXVpc2VybW9zIHJlZHV6aXIgYSBkaW1lbnNpb25hbGlkYWRlIGRlIHVtIGNvbmp1bnRvIGRlIGRhZG9zLCBwb3NzYW1vcyBmb2NhciBhIGFuw6FsaXNlIG5vcyBwcmltZWlyb3MgY29tcG9uZW50ZXMgc2VtIHNvZnJlciB1bWEgZ3JhbmRlIHBlbmFsaWRhZGUgZW0gdGVybW9zIGRlIHBlcmRhIGRlIGluZm9ybWHDp8Ojby4NCg0KQXMgdmFyacOhdmVpcyBkbyBjb25qdW50byBkZSBkYWRvcyBkZXZlbSBzZXIgcGFkcm9uaXphZGFzIHByZXZpYW1lbnRlIMOgIGFuw6FsaXNlLg0KTm8gY2FzbyBkZXNzZSBlc3R1ZG8sIGEgdmFyacOhdmVsIGRlIGludGVyZXNzZSwgKlNPMiosIGZvaSByZXRpcmFkYSBkYSBhbsOhbGlzZSBkZSBjb21wb25lbnRlcyBwcmluY2lwYWlzLCBwb2lzIG7Do28gw6kgbm9zc28gaW50ZXJlc3NlIGFncmVnw6EtbGEgw6BzIGRlbWFpcy4NCg0KIyMgVmV0b3JlcyBkZSBDYXJnYQ0KDQpPcyB2ZXRvcmVzIGRlIGNhcmdhIGNvbnTDqm0gb3MgY29lZmljaWVudGVzIGRhIGNvbWJpbmHDp8OjbyBsaW5lYXIgcXVlIGdlcm91IGNhZGEgY29tcG9uZW50ZSBwcmluY2lwYWwgcGFyYSBhcyB2YXJpw6F2ZWlzIHV0aWxpemFkYXMuIA0KQSBwcmltZWlyYSBjb21wb25lbnRlLCBwb3IgZXhlbXBsbywgcG9kZSBzZXIgZGVzY3JpdGEgY29tbyANCg0KPiAkMC4zMnRlbXAtMC42MW1hbnUtMC41N3BvcHVsLTAuMzV3aW5kKzAuMDRwcmVjaXAtMC4yNHByZWRheXMkDQoNCmByIHRhYmxlX251bXMoJ3RhYl8xMicpYA0KYGBge3IgfQ0KDQoNCg0KcmVzLnBjYSA8LSBwcmNvbXAodXNhaXJwb2xsdXRpb25bLC1jKDEsMildLCBzY2FsZSA9IFRSVUUsIGNlbnRlciA9IFRSVUUpDQprbml0cjo6a2FibGUocmVzLnBjYSRyb3RhdGlvbikNCg0KYGBgDQoNCg0KDQojIyBTY3JlZSBQbG90IA0KDQpQb2RlbW9zIG9ic2VydmFyIHF1ZSBhIHByaW1laXJhIGNvbXBvbmVudGUgZXhwbGljYSBjZXJjYSBkZSAzNi42JSBkYSB2YXJpw6JuY2lhIHRvdGFsIGRvcyBkYWRvcywgYSBzZWd1bmRhIDI1JSBlIGEgdGVyY2VpcmEsIDIzLjIlLg0KDQoNCmBgYHtyIH0NCmZ2aXpfZWlnKHJlcy5wY2EsYWRkbGFiZWxzID0gVFJVRSwgeGxhYiA9ICJEaW1lbnPDtWVzIiwgeWxhYiA9ICIlIGRhIHZhcmnDom5jaWEgZXhwbGljYWRhIiwgbWFpbiA9ICJTY3JlZSBQbG90IiApDQpgYGANCg0KVXRpbGl6YW5kbyBhcyA0IHByaW1laXJhcyBjb21wb25lbnRlcyBwcmluY2lwYWlzLCBhIHByb3BvcsOnw6NvIGRhIHZhcmnDom5jaWEgZXhwbGljYWRhIChQVkUpIGFjdW11bGFkYSDDqSBkZSA5Ny41MiUuDQoNCmByIHRhYmxlX251bXMoJ3RhYl8xMycpYA0KYGBge3IgfQ0KDQpzdW1tYXJ5KHJlcy5wY2EpDQpgYGANCg0KIyMgUXVhbGlkYWRlIGRhIHJlcHJlc2VudGHDp8OjbyBkYXMgdmFyacOhdmVpcyAoY29zMikNCg0KQSBxdWFsaWRhZGUgZGEgcmVwcmVzZW50YcOnw6NvIGRlIHVtYSB2YXJpw6F2ZWwgw6kgbWVkaWRhIHBlbG8gcXVhZHJhZG8gZGFzIGNhcmdhcyBvdSBjb29yZGVuYWRhcywgY2hhbWFkYSBkZSBjb3MyLg0KDQpPIGdyw6FmaWNvIGFiYWl4byBtb3N0cmEgbyB0b3RhbCBkZSBjb3MyIHBhcmEgYXMgZHVhcyBwcmltZWlyYXMgY29tcG9uZW50ZXMsIG91IHNlamEsIG8gdmFsb3IgcGFyYSBjYWRhIHZhcmnDoXZlbCBjb3JyZXNwb25kZSBhIHNvbWEgZGUgY29zMiBwYXJhIGFzIGR1YXMgcHJpbWVpcmFzIGNvbXBvbmVudGVzLg0KDQoNCmBgYHtyIH0NCg0KZnZpel9wY2FfdmFyKHJlcy5wY2EsIGNvbC52YXIgPSAiY29zMiIsDQpncmFkaWVudC5jb2xzID0gdmlyaWRpc19wYWwoKSgxMiksDQpyZXBlbCA9IFRSVUUgIyBBdm9pZCB0ZXh0IG92ZXJsYXBwaW5nDQopICsgbGFicyh0aXRsZSA9IiBRdWFsaWRhZGUgZGEgcmVwcmVzZW50YcOnw6NvIChjb3MyKSAtIFZhcmnDoXZlaXMiKQ0KYGBgDQoNCkFzIHZhcmnDoXZlaXMgbWFpcyBwcsOzeGltYXMgZGEgY2lyY3VuZmVyw6puY2lhIHPDo28gKnByZWRheXMqLCAqcHJlY2lwKiwgKm1hbnUqIGUgKnBvcHVsKiwgb3Ugc2VqYSBzdWEgcmVwcmVzZW50YcOnw6NvIG5vIG1hcGEgZGUgZmF0b3JlcyDDqSBib2EsIGVudMOjbyBzw6NvIG1haXMgaW1wb3J0YW50ZXMgcGFyYSBhcyBwcmltZWlyYXMgY29tcG9uZW50ZXMuDQpKw6EgYXMgdmFyacOhdmVpcyAqd2luZCogZSAqdGVtcCogZXN0w6NvIG1haXMgcHLDs3hpbWFzIGRhIG9yaWdlbSwgbyBxdWUgc2lnbmlmaWNhIG7Do28gZm9yYW0gYmVtIHJlcHJlc2VudGFkYXMgcGVsYXMgY29tcG9uZW50ZXMgMSBlIDIuIA0KUG9kZW1vcyBvYnNlcnZhciBxdWUgYXMgdmFyacOhdmVpcyAqcG9wdWwqLCAqbWFudSosICp3aW5kKiBlICpwcmVkYXlzKiBzw6NvIHZhcmnDoXZlaXMgcG9zaXRpdmFtZW50ZSBjb3JyZWxhY2lvbmFkYXMsIHBvaXMgZXN0w6NvIGRvIG1lc21vIGxhZG8gZGEgY2lyY3VuZmVyw6puY2lhLg0KDQojIyBDb250cmlidWnDp8OjbyBkYXMgdmFyacOhdmVpcyBuYXMgY29tcG9uZW50ZXMgKGNvbnRyaWIpDQoNCkFzIHZhcmnDoXZlaXMgcXVlIG1haXMgY29udHJpYnVlbSBuYSBjb25zdHJ1w6fDo28gZGEgcHJpbWVpcmEgZSBzZWd1bmRhIGNvbXBvbmVudGVzIChEaW0uMSBlIERpbS4yKSBzw6NvIGFzIG1haXMgaW1wb3J0YW50ZXMgcGFyYSBleHBsaWNhciBhIHZhcmlhYmlsaWRhZGUgbm8gY29uanVudG8gZGUgZGFkb3MuIEVzc2FzIHZhcmnDoXZlaXMgc8OjbzoNCg0KLSBtYW51DQotIHBvcHVsDQotIHByZWNpcCANCi0gcHJlZGF5cw0KDQpgYGB7ciAgd2FybmluZz1GQUxTRX0NCg0KdmFyIDwtIGdldF9wY2FfdmFyKHJlcy5wY2EpDQoNCmNvcnJwbG90KHZhciRjb250cmliLCBpcy5jb3JyPUZBTFNFLHRsLmNvbCA9ICdibGFjaycsY29sLmxpbSA9IGMoMCwgODApKQ0KDQpgYGANCg0KDQpPIGdyw6FmaWNvIGFiYWl4byBjb250w6ltIGFzIGNvbnRyaWJ1acOnw7VlcyBkYXMgdmFyacOhdmVpcyBuYSBwcmltZWlyYSBjb21wb25lbnRlICglKS4gQSBsaW5oYSB0cmFjZWphZGEgZW0gdmVybWVsaG8gcmVwcmVzZW50YSBhIGNvbnRyaWJ1acOnw6NvIG3DqWRpYSBlc3BlcmFkYSAoJSkuIA0KDQpTZSB0b2RhcyBhcyB2YXJpw6F2ZWlzIGNvbnRyaWJ1w61zc2VtIGRlIGZvcm1hIGlndWFsIHBhcmEgYSBjb25zdHJ1w6fDo28gZGFzIGNvbXBvbmVudGVzLCBvIHZhbG9yIGRhIGNvbnRyaWJ1acOnw6NvIG3DqWRpYSBlc3BlcmFkYSAoJSkgc2VyaWEgJFxmcmFjezF9ezZ9IFxhcHByb3ggMTYuNjdcJSQuIA0KDQpQb3J0YW50bywgc2UgdW1hIGRldGVybWluYWRhIHZhcmnDoXZlbCB0ZW0gY29udHJpYnVpw6fDo28gYWNpbWEgZGUgJDE2LjY3XCUkLCBlc3RhIHBvZGUgc2VyIGNvbnNpZGVyYWRhIGltcG9ydGFudGUgbmEgY29udHJpYnVpw6fDo28gZGEgY29tcG9uZW50ZS4gDQoNCk5hIHByaW1laXJhIGNvbXBvbmVudGUsIGFzIHZhcmnDoXZlaXMgKm1hbnUqIGUgKnBvcHVsKiB1bHRyYXBhc3NhbSBhIGNvbnRyaWJ1acOnw6NvIG3DqWRpYSBlc3BlcmFkYSAoJSksIGxvZ28sIHPDo28gaW1wb3J0YW50ZXMgZW0gc3VhIGNvbnN0cnXDp8Ojby4NCg0KYGBge3IgfQ0KDQpmdml6X2NvbnRyaWIocmVzLnBjYSwgY2hvaWNlID0gInZhciIsIGF4ZXMgPSAxKSArIGxhYnModGl0bGUgPSJDb250cmlidWnDp8OjbyBkYXMgdmFyacOhdmVpcyBuYSBQcmltZWlyYSBDb21wb25lbnRlIiwgeSA9ICJDb250cmlidWnDp8OjbyBlbSAlIikNCg0KDQpgYGANCg0KSsOhIG5hIGNvbXBvbmVudGUgMiwgYXMgdmFyacOhdmVpcyAqcHJlZGF5cyogZSAqcHJlY2lwKiBzw6NvIGltcG9ydGFudGVzIHBhcmEgc3VhIGNvbnN0cnXDp8Ojby4NCg0KDQpgYGB7ciB9DQoNCmZ2aXpfY29udHJpYihyZXMucGNhLCBjaG9pY2UgPSAidmFyIiwgYXhlcyA9IDIpICsgbGFicyh0aXRsZSA9IkNvbnRyaWJ1acOnw6NvIGRhcyB2YXJpw6F2ZWlzIG5hIFNlZ3VuZGEgQ29tcG9uZW50ZSIsIHkgPSAiQ29udHJpYnVpw6fDo28gZW0gJSIpDQoNCg0KYGBgDQoNClNlIGNvbnNpZGVyYXJtb3MgYXMgY29tcG9uZW50ZXMgMSBlIDIgc2ltdWx0YW5lYW1lbnRlLCBhcyB2YXJpw6F2ZWlzICpwcmVkYXlzKiwgKm1hbnUqIGUgKnBvcHVsKiBzw6NvIGFzIG1haXMgaW1wb3J0YW50ZXMuICANCg0KDQpgYGB7ciB9DQoNCmZ2aXpfY29udHJpYihyZXMucGNhLCBjaG9pY2UgPSAidmFyIiwgYXhlcyA9IDE6MikgKyBsYWJzKHRpdGxlID0iQ29udHJpYnVpw6fDo28gZGFzIHZhcmnDoXZlaXMgbmEgUHJpbWVpcmEgZSBTZWd1bmRhIENvbXBvbmVudGVzIiwgeSA9ICJDb250cmlidWnDp8OjbyBlbSAlIikNCg0KDQpgYGANCg0KDQojIyBRdWFsaWRhZGUgZGEgcmVwcmVzZW50YcOnw6NvIGRvcyBpbmRpdsOtZHVvcyAoY29zMikNCg0KTyBncsOhZmljbyBhYmFpeG8gY29udMOpbSBhIHF1YWxpZGFkZSBkYSByZXByZXNlbnRhw6fDo28oY29zMikgZG9zIGluZGl2w61kdW9zLg0KT3MgaW5kaXZpZHVvcyBzaW1pbGFyZXMgZmljYW0gbWFpcyBwcsOzeGltb3MgZW50cmUgc2kuIA0KUXVhbnRvIG1haXMgZGlzdGFudGUgZG8gY2VudHJvIGVzdGl2ZXIgbyBpbmRpdsOtZHVvLCBtZWxob3Igc3VhIHJlcHJlc2VudGHDp8OjbyBuYXMgY29tcG9uZW50ZXMgMSBlIDI6IGRlc3RhY2FtLXNlIGFzIGNpZGFkZXMgZGUgUGhpbGFkZWxwaGlhLCBDaGljYWdvLCBQaG9lbml4IGUgQWxidXF1ZXJxdWUuDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCnVzIDwtIGRhdGEuZnJhbWUodXNhaXJwb2xsdXRpb24sIHJvdy5uYW1lcyA9IDEpDQpyZXMucGNhIDwtIHByY29tcCh1c1ssLTJdLCBzY2FsZSA9IFRSVUUsIGNlbnRlciA9IFRSVUUpDQppbmQgPSBnZXRfcGNhX2luZChyZXMucGNhKQ0KZnZpel9wY2FfaW5kKHJlcy5wY2EsIHJlcGVsID0gVFJVRSwNCnBvaW50c2l6ZSA9ICJjb3MyIiwNCnBvaW50c2hhcGUgPSAyMSwNCmZpbGwgPSAieWVsbG93Ig0KKSArIHRoZW1lX21pbmltYWwoKSArIGxhYnModGl0bGUgPSIgUXVhbGlkYWRlIGRhIHJlcHJlc2VudGHDp8OjbyAoY29zMikgLSBJbmRpdsOtZHVvcyIpDQoNCg0KYGBgDQoNCk8gZ3LDoWZpY28gYWJhaXhvIGNvcnJvYm9yYSBjb20gbyBxdWUgZm9pIG9ic2VydmFkbyBhbnRlcmlvcm1lbnRlOiBhcyBjaWRhZGVzIG1lbGhvciByZXByZXNlbnRhZGFzIG5hcyBjb21wb25lbnRlcyAxIGUgMiBzw6NvIENoaWNhZ28sIFNhbiBGcmFuY2lzY28sIEFsYnVxdWVycXVlLCBQaGlsYWRlbHBoaWEsIERlbnZlciBlIFBob2VuaXguDQoNCmBgYHtyIH0NCmZ2aXpfY29zMihyZXMucGNhLCBjaG9pY2UgPSAiaW5kIiwgYXhlcz0xOjIpICsgbGFicyh0aXRsZSA9IiBRdWFsaWRhZGUgZGEgcmVwcmVzZW50YcOnw6NvIChjb3MyKSAtIEluZGl2w61kdW9zIC0gQ29tcG9uZW50ZXMgMSBlIDIiLCB5ID0gIkNvczIgLSBRdWFsaWRhZGUgZGEgcmVwcmVzZW50YcOnw6NvIikNCg0KYGBgDQoNCiMjIENvbnRyaWJ1acOnw6NvIGRvcyDDrW5kaXZpZHVvcyBuYXMgY29tcG9uZW50ZXMgKGNvbnRyaWIpDQpPIGdyw6FmaWNvIGFiYWl4byBjb250w6ltIGFzIGNvbnRyaWJ1acOnw7VlcyBkb3MgaW5kaXbDrWR1b3MgbmFzIGR1YXMgcHJpbWVpcmFzIGNvbXBvbmVudGUgKCUpLiBBIGxpbmhhIHRyYWNlamFkYSBlbSB2ZXJtZWxobyByZXByZXNlbnRhIGEgY29udHJpYnVpw6fDo28gbcOpZGlhIGVzcGVyYWRhICglKS4gDQoNCmBgYHtyIH0NCmZ2aXpfY29udHJpYihyZXMucGNhLCBjaG9pY2UgPSAiaW5kIiwgYXhlcyA9IDE6MikgK2xhYnModGl0bGUgPSIgQ29udHJpYnVpw6fDo28gZG9zIEluZGl2w61kdW9zIG5hcyBjb21wb25lbnRlcyAxIGUgMiIsIHkgPSAiQ29udHJpYiAtIENvbnRyaWJ1acOnw6NvICglKSIpDQpgYGANCg0KDQoNClNlIHRvZGFzIG9zIGluZGl2w61kdW9zIGNvbnRyaWJ1w61zc2VtIGRlIGZvcm1hIGlndWFsIHBhcmEgYSBjb25zdHJ1w6fDo28gZGFzIGNvbXBvbmVudGVzLCBvIHZhbG9yIGRhIGNvbnRyaWJ1acOnw6NvIG3DqWRpYSBlc3BlcmFkYSAoJSkgc2VyaWEgJFxmcmFjezF9ezQxfSBcYXBwcm94IDIuNDRcJSQuIA0KDQpQb3J0YW50bywgc2UgdW0gZGV0ZXJtaW5hZCBpbmRpdsOtZHVvIHRlbSBjb250cmlidWnDp8OjbyBhY2ltYSBkZSAkMi40NFwlJCwgZXN0ZSBwb2RlIHNlciBjb25zaWRlcmFkYSBpbXBvcnRhbnRlIG5hIGNvbnRyaWJ1acOnw6NvIGRhIGNvbXBvbmVudGUuIA0KDQpQYXJhIGFzIGR1YXMgcHJpbWVpcmFzIGNvbXBvbmVudGUsIG9zIGluZGl2w61kdW9zIENoaWNhZ28sIFBob2VuaXgsIFBoaWxhZGVscGhpYSwgQWxidXF1ZXJxdWUsIFNhbiBGcmFuY2lzY28sIERlbnZlciwgQ2xldmVsYW5kLCBlIFNhbHQgTGFrZSBDaXR5IHVsdHJhcGFzc2FtIGEgY29udHJpYnVpw6fDo28gbcOpZGlhIGVzcGVyYWRhICglKSwgbG9nbywgc8OjbyBpbXBvcnRhbnRlcyBlbSBzdWEgY29uc3RydcOnw6NvLg0KDQoNCiMgRXNjYWxvbmFtZW50byBNdWx0aWRpbWVuc2lvbmFsIChNRFMpDQoNCk8gTURTIHJldG9ybmEgdW1hIHNvbHXDp8OjbyDDs3RpbWEgcGFyYSByZXByZXNlbnRhciBvcyBkYWRvcyBlbSB1bSBlc3Bhw6dvIGRlIGRpbWVuc8OjbyBpbmZlcmlvciwgb25kZSBvIG7Dum1lcm8gZGUgZGltZW5zw7VlcyBrIMOpIHByw6ktZXNwZWNpZmljYWRvIHBlbGEgcGVzc29hIGZhemVuZG8gYSBhbsOhbGlzZS4gUG9yIGV4ZW1wbG8sIGEgZXNjb2xoYSBkZSAkayA9IDIkIG90aW1pemEgYSBsb2NhbGl6YcOnw6NvIGRvcyBvYmpldG9zIHBhcmEgdW0gZ3LDoWZpY28gYmlkaW1lbnNpb25hbCBkZSBkaXNwZXJzw6NvLg0KDQpPIGFsZ29yaXRtbyBNRFMgdG9tYSBjb21vIGRhZG8gZGUgZW50cmFkYSBhIG1hdHJpeiBkZSBkaXNzaW1pbGFyaWRhZGUsIHJlcHJlc2VudGFuZG8gYXMgZGlzdMOibmNpYXMgZW50cmUgcGFyZXMgZGUgb2JqZXRvcy4gQSBpZGVpYSDDqSBlbmNvbnRyYXIgdW1hIHJlcHJlc2VudGHDp8OjbyBkb3MgZGFkb3MgZW0gbWVub3IgZGltZW5zw6NvIGUgcXVlIHByZXNlcnZlIGFvIG3DoXhpbW8gYXMgZGlzdMOibmNpYXMgZW50cmUgcG9udG9zLg0KDQpPIE1EUyBzZXLDoSBhcGxpY2FkbyBuYXMgdmFyacOhdmVpcyBxdWFudGl0YXRpdmFzIGRvIGRhdGFzZXQgKnVzYWlycG9sbHV0aW9uKiwgY29tIGV4Y2XDp8OjbyBkYSB2YXJpw6F2ZWwgU08yLiBQcmV2aWFtZW50ZSBhIHV0aWxpemHDp8OjbyBkbyBhbGdvcml0bW8sIG9zIGRhZG9zIGZvcmFtIHBhZHJvbml6YWRvcyBwYXJhIGEgbcOpdHJpY2EgWzAsMV06ICRYJyA9IFxmcmFje3gtXG1pbih4KX17XG1heCh4KS1taW4oeCl9JC4gRm9pIHV0aWxpemFkYSBhIGRpc3TDom5jaWEgZXVjbGlkaWFuYSBwYXJhIGPDoWxjdWxvIGRlIGRpc3NpbWlsYXJpZGFkZXMuIA0KDQoNCiMjIEVzY2Fsb25hbWVudG8gTXVsdGlkaW1lbnNpb25hbCBDbMOhc3NpY28gDQoNCk8gTURTIENsw6Fzc2ljbyBwcmVzZXJ2YSBhIG3DqXRyaWNhIGRlIGRpc3TDom5jaWEgb3JpZ2luYWwsIGVudHJlIHBvbnRvcywgZGEgbWVsaG9yIGZvcm1hIHBvc3PDrXZlbC4gSXN0byDDqSwgYXMgZGlzdMOibmNpYXMgYWp1c3RhZGFzIG5vIG1hcGEgTURTIGUgYXMgZGlzdMOibmNpYXMgb3JpZ2luYWlzIGVzdMOjbyBuYSBtZXNtYSBtw6l0cmljYS4gTyBNRFMgQ2zDoXNzaWNvIHBlcnRlbmNlIMOgIGNoYW1hZGEgY2F0ZWdvcmlhIGRlIGVzY2Fsb25hbWVudG8gbXVsdGlkaW1lbnNpb25hbCBtw6l0cmljby4gw4kgdGFtYsOpbSBjb25oZWNpZG8gY29tbyBhbsOhbGlzZSBkZSBjb29yZGVuYWRhcyBwcmluY2lwYWlzIGUgw6kgYWRlcXVhZG8gcGFyYSBkYWRvcyBxdWFudGl0YXRpdm9zLg0KDQpQYXJhIGRlbW9uc3RyYXIgcXVlIMOpIHBvc3PDrXZlbCByZWNvbnN0cnVpciBhIG1hdHJpeiBkZSBkaXN0w6JuY2lhIG9yaWdpbmFsIHV0aWxpemFuZG8gYXMgY29vcmRlbmFkYXMgZ2VyYWRhcyBhIHBhcnRpciBkbyBNRFMsIGNhbGN1bGEtc2UgYSBkaWZlcmVuw6dhIG3DoXhpbWEgYWJzb2x1dGEgZW50cmUgYSBtYXRyaXogZGUgZGlzdMOibmNpYXMgb3JpZ2luYWwgZSBhIHJlY29uc3RydcOtZGEuIA0KDQpPIGdyw6FmaWNvIGFiYWl4byBtb3N0cmEgYXMgZGlmZXJlbsOnYXMgbcOheGltYXMgb2JzZXJ2YWRhcyBlbnRyZSBhIG1hdHJpeiBkZSBkaXN0w6JuY2lhIG9yaWdpbmFsIGUgYSByZWNvbnN0cnXDrWRhIGEgYSBwYXJ0aXIgZGFzIGNvb3JkZW5hZGFzIE1EUyBhbyB2YXJpYXIgbyB2YWxvciBkZSBrIGVudHJlIDIgZSA3LiAgDQoNCkFzIGRpZmVyZW7Dp2FzIHByw7N4aW1hcyBkZSB6ZXJvIHBhcmEgJGs9XHs1LDYsN1x9JCBleHByZXNzYW0gcXVlLCBhIHBhcnRpciBkZSAkaz01JCBjb29yZGVuYWRhcywgYSBtYXRyaXogZGUgZGlzdMOibmNpYSBvcmlnaW5hbCBwb2RlIHNlciByZWN1cGVyYWRhIHF1YXNlIHF1ZSB0b3RhbG1lbnRlLiANCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KUDwtIGRhdGEuZnJhbWUodXNhaXJwb2xsdXRpb25bLC0yXSwgcm93Lm5hbWVzID0gMSkNClAgPC0gYXBwbHkoUCwyLCBmdW5jdGlvbih4KSAoeC1taW4oeCkpLyhtYXgoeCktbWluKHgpKSkNCg0KdmFsb3IgPC0gTlVMTA0KZm9yKGkgaW4gMjo3KXsNCiAgdmFsb3I8LXJiaW5kKHZhbG9yLGMoaSxtYXgoYWJzKGRpc3QoUCkgLSBkaXN0KGNtZHNjYWxlKGRpc3QoUCksaz1pLCBlaWc9VFJVRSkkcG9pbnRzKSkpKSkNCn0NCg0KZGY8LWRhdGEuZnJhbWUoayA9IHZhbG9yWywxXSwgYGRpZl9tYXhgID0gdmFsb3JbLDJdKQ0KDQpnZ3Bsb3QoZGF0YT1kZiwgYWVzKHg9aywgeT1kaWZfbWF4LCBncm91cD0xKSkgKw0KICBnZW9tX2xpbmUoKSsNCiAgZ2VvbV9wb2ludCgpKw0KICB0aGVtZV9idygpKw0KICBsYWJzKHRpdGxlPSJEaWZlcmVuw6dhcyBtw6F4aW1hcyBhYnNvbHV0YXMgZW50cmUgbWF0cml6IGRlIGRpc3TDom5jaWEgb3JpZ2luYWwgZSBtYXRyaXogXG5yZWNvbnN0cnXDrWRhIGEgcGFydGlyIGRhcyBjb29yZGVuYWRhcyBNRFMsIHBhcmEgayBlbnRyZSAyIGUgN1xuIiwgeT0iRGlmZXJlbsOnYSBtw6F4aW1hIGFic29sdXRhXG4iLHg9IlxuayIpKw0KICB5bGltKDAsMS4yNSkNCg0KYGBgDQoNCg0KQXBsaWNhbmRvIGEgZnVuw6fDo28gKmNtZHNjYWxlKiAoc3RhdHMpLCBjb20gJGs9NSQsIGFiYWl4byB0ZW1vcyBvIGdyw6FmaWNvIGRlIGRpc3BlcnPDo28gZGFzIG9ic2VydmHDp8O1ZXMgcGFyYSBhcyBjb29yZGVuYWRhcyAxIGUgMi4gUG9kZSBzZXIgb2JzZXJ2YWRvIHF1ZSBhcyBjaWRhZGVzIGNvbSB0ZW9yZXMgZGUgJFNPXzIkIG1haXMgc2ltaWxhcmVzIGZpY2FtIG1haXMgYWdydXBhZGFzLCBlbnF1YW50byBxdWUgYXMgcXVlIHBvc3N1ZW0gdGVvcmVzIG1haXMgZGlzY3JlcGFudGVzIGZpY2FtIG1haXMgYWZhc3RhZGFzLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KDQoNCm1kcyA8LSBQICU+JQ0KICBkaXN0KCkgJT4lICAgICAgICAgIA0KICBjbWRzY2FsZShrPTUsIGVpZz1UUlVFKSANCg0KDQptZHNfbGFiZWw8LSBhcy5kYXRhLmZyYW1lKG1kcyRwb2ludHMpDQpjb2xuYW1lcyhtZHNfbGFiZWwpPC0gcGFzdGUoIkNvb3JkIiwxOjUpDQptZHNfbGFiZWwkQ2l0eSA8LSB1c2FpcnBvbGx1dGlvblssMV0NCm1kc19sYWJlbCRTTzIgPC0gdXNhaXJwb2xsdXRpb25bLDJdDQoNCmdncGxvdChtZHNfbGFiZWwsIGFlcyh4PWBDb29yZCAxYCwgeSA9IGBDb29yZCAyYCkpICsNCiAgZ2VvbV9wb2ludChhZXMoc2l6ZT1TTzIpLCBjb2xvdXIgPSAiIzMyQ0QzMiIpKw0KICBsYWJzKHRpdGxlPSAiR3LDoWZpY28gZGUgZGlzcGVyc8OjbyAtIE1EUyBDbMOhc3NpY28gY29tIGs9NSIsIHg9ICJDb29yZCAxIiwgeT0iQ29vcmQgMiIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrDQogIGdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsPUNpdHkpLCANCiAgICAgICAgICAgICAgICAgICBzaXplID0gMiwNCiAgICAgICAgICAgICAgICAgICBib3gucGFkZGluZyAgID0gMC4zNSwgDQogICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuNSwNCiAgICAgICAgICAgICAgICAgICBzZWdtZW50LmNvbG9yID0gJ2dyZXk1MCcpKw0KICB0aGVtZV9idygpDQoNCg0KYGBgDQoNCg0KIyB0LURpc3RyaWJ1dGVkIFN0b2NoYXN0aWMgTmVpZ2hib3IgRW1iZWRkaW5nICh0LVNORSkNCg0Kw4kgdW1hIHTDqWNuaWNhIGRlIHZpc3VhbGl6YcOnw6NvIGVtIDFELCAyRCBvdSAzRCBkZSBkYXRhc2V0cyBkZSBhbHRhcyBkaW1lbnPDtWVzLg0KTm8gZ3LDoWZpY28gYWJhaXhvLCBwb2RlbW9zIHBlcmNlYmVyIHF1ZSBhbGd1bWFzIGNpZGFkZXMgY29tIHZhbG9yIGRlIFNPMiBiYWl4b3MgZmljYXJhbQ0KY29uY2VudHJhZGFzIG5hIHBhcnRlIHN1cGVyaW9yIGRvIGdyw6FmaWNvLiANCkNvbXBhcmFuZG8gb3MgcmVzdWx0YWRvcyBncsOhZmljb3MgZW50cmUgTURTIGUgdC1TTkUsIG5vdGEtc2UgcXVlIGFzIG9ic2VydmHDp8O1ZXMgZXN0w6NvIG1haXMgdW5pZm9ybWVtZW50ZSBkaXN0cmlidcOtZGFzIG5vIHQtU05FLCBlbnF1YW50byBxdWUgbm8gTURTIGVzdMOjbyBtYWlzIGFncnVwYWRhcy4gDQoNCg0KYGBge3J9DQoNCnRpYygidHNuZSIpDQp0b2MobG9nID0gVFJVRSwgcXVpZXQgPSBUUlVFKQ0KDQp0c25lX2ZpdCA8LSBSdHNuZShQLCBwZXJwbGV4aXR5PTEzKQ0KdHNuZV9jb29yZCA8LSBkYXRhLmZyYW1lKHRzbmVfZml0JFkpDQp0c25lX2Nvb3JkJENpdHkgPC0gdXNhaXJwb2xsdXRpb25bLDFdDQp0c25lX2Nvb3JkJFNPMiA8LSB1c2FpcnBvbGx1dGlvblssMl0NCg0KZ2dwbG90KGRhdGE9dHNuZV9jb29yZCwgYWVzKHg9WDEsIHk9WDIpKSsgDQogIGdlb21fcG9pbnQoYWVzKHNpemU9U08yKSwgY29sb3VyID0gIiMzMkNEMzIiKSArIA0KICBnZW9tX2xhYmVsX3JlcGVsKGFlcyhsYWJlbD1DaXR5KSwgDQogICAgICAgICAgICAgICAgICAgc2l6ZSA9IDIsDQogICAgICAgICAgICAgICAgICAgYm94LnBhZGRpbmcgICA9IDAuMzUsIA0KICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgc2VnbWVudC5jb2xvciA9ICdncmV5NTAnKSsgDQogIGxhYnMoeD0iQ29vcmQgMVxuIix5PSJcbkNvb3JkIDIiLCB0aXRsZT0idC1TTkUgcGFyYSBvIGRhdGFzZXQgdXNhaXJwb2xsdXRpb24iKSArDQogIHRoZW1lX2J3KCkgDQoNCmBgYA0KDQojIENvbmNsdXPDo28gDQpPIGRhdGFzZXQgKnVzYWlycG9sbHV0aW9uKiBjb250w6ltIDggdmFyacOhdmVpcyBlIDQxIG9ic2VydmHDp8O1ZXMgcmVsYXRpdmFzIGEgY2lkYWRlcyBlc3RhZHVuaWRlbnNlcy4gQSB2YXJpw6F2ZWwgZGUgaW50ZXJlc3NlIMOpICpTTzIqLCBvIHRlb3IgZGUgZGnDs3hpZG8gZGUgZW54b2ZyZSBlbSBtaWNyb2dyYW1hcyBwb3IgbWV0cm8gY8O6YmljbywgdW1hIG1lZGlkYSBkZSBwb2x1acOnw6NvLg0KDQpOYSBBbsOhbGlzZSBEZXNjcml0aXZhLCB2aW1vcyBlc3RhdMOtc3RpY2FzIHJlc3VtbywgYXNzaW1ldHJpYSBlIGRlbnNpZGFkZXMsIGJveHBsb3RzLCBvdXRsaWVycywgZ3LDoWZpY29zIGRlIGRpc3BlcnPDo28sIGNvcnJlbG9ncmFtYSBlIGRpc3RyaWJ1acOnw6NvIGdlb2dyw6FmaWNhIHBhcmEgYXMgNyB2YXJpw6F2ZWlzIHF1YW50aXRhdGl2YXM6ICpTTzIqLCAqdGVtcCosICptYW51KiwgKnBvcHVsKiwgKndpbmQqLCAqcHJlY2lwKiBlICpwcmVkYXlzKi4gDQoNCkVtIHNlZ3VpZGEsIG5hIEFuw6FsaXNlIGRlIENvbXBvbmVudGVzIFByaW5jaXBhaXMsIHZpbW9zIHF1ZSBjb20gNCBjb21wb25lbnRlcyBjb25zZWd1aW1vcyBleHBsaWNhciBjZXJjYSBkZSA5Ny41MiUgZGEgdmFyaWFiaWxpZGFkZSB0b3RhbCBkbyBjb25qdW50byBkZSBkYWRvcy4gDQoNCk5vIEVzY2Fsb25hbWVudG8gTXVsdGlkaW1lbnNpb25hbCwgdmltb3MgcXVlIGEgcGFydGlyIGRlICRrPTUkIGNvb3JkZW5hZGFzLCBhIG1hdHJpeiBkZSBkaXN0w6JuY2lhIG9yaWdpbmFsIHBvZGUgc2VyIHJlY3VwZXJhZGEgcXVhc2UgcXVlIHRvdGFsbWVudGUuIA0KDQpFLCBwb3IgZmltLCB2aW1vcyBubyB0LVNORSBxdWUgcG9kZW1vcyByZXByZXNlbnRhciBkYWRvcyBkZSBhbHRhcyBkaW1lbnNpb25hbGlkYWRlcyBlbSAyIGRpbWVuc8O1ZXMuIA0KDQpDb21vIHByw7N4aW1vcyBwYXNzb3MsIHBvZGVyw61hbW9zLCBwb3IgZXhlbXBsbywgZmF6ZXIgdW1hIHJlZ3Jlc3PDo28gbGluZWFyIGVudHJlIFNPMiBlIGFzIHNlaXMgY29tcG9uZW50ZXMgcHJpbmNpcGFpcyBjYWxjdWxhZGFzIG91IGVudMOjbyB1bWEgY2x1c3Rlcml6YcOnw6NvIGNvbSBvcyByZXN1bHRhZG9zIGRvIE1EUy4gDQoNClRvZGFzIGFzIGFuw6FsaXNlcyByZWFsaXphZGFzIGFicmlyYW0gZGl2ZXJzYXMgcG9zc2liaWxpZGFkZXMgcGFyYSBleHBsb3JhciBtZWxob3IgYSByZWxhw6fDo28gZW50cmUgU08yIGUgYXMgZGVtYWlzIHZhcmnDoXZlaXMuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K