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.
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.
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)
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)
| 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))
| 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 |
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)])
| 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 |
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")
| 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()







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







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)
| 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)
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)
| 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)
| 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)
| 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)
| Albuquerque |
58 |
| Buffalo |
166 |
| Phoenix |
36 |
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()






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.
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()

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.
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)
| 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 |
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
## 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
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.
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 %")

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")

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.
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.
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()

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()

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