Os 10 Mandamentos do R
  1. 1º - Não entrarás em pânico!
  2. 2º - Utilizarás o R para tuas análises bem como para a manipulação de teus dados(não mais o Excel);
  3. 3º - Nunca digitarás o código no console;
  4. 4º - Salvarás seus scripts e não se preocuparás com o .RData;
  5. 5º - Sempre concatenará e jamais esquecerás dos parênteses das funções;
  6. 6º - Conferirás o diretório de trabalho e os dados após a importação, antes de enlouqueceres;
  7. 7º - Usarás o help antes de perguntares e não culparás o R por teus erros;
  8. 8º - Não esmorecerás após as primeiras noites em claRo e jamais amaldiçoarás o R por tuas faltas de vírgulas;
  9. 9º - Lembrarás das aspas dos caracteres;
  10. 10º- Não cobiçarás e aperfeiçoarás os códigos alheios e nunca ocultarás o código e a autoria original
#install.packages("xlsx)
library(xlsx)
getwd()
[1] "C:/Users/Usuario/R_dados"
setwd("C:/Users/Usuario/R_dados")

Texto

x=read.csv(file.choose(),header = TRUE,sep=",")
x
x=read.csv("Credit.csv",header=TRUE,sep=",")
x

Excel

dados=read.xlsx("Credit.xlsx",sheetIndex=1)
dados
dados

Formação Cientista de Dados Limpeza e Tratamento de Dados

#importa dados, string vazio como NA, string como fatores...
dados=read.csv("Churn.csv",sep=";",na.strings = "",stringsAsFactors = T)
head(dados)
summary(dados)
       X0               X1         X2              X3            X4             X4.1              X6                 X7       
 Min.   :   1.0   Min.   :376.0   PR:257   F        :  2   Min.   :-20.0   Min.   : 0.000   Min.   :       0   Min.   :1.000  
 1st Qu.: 251.5   1st Qu.:580.0   RP:  1   Fem      :  1   1st Qu.: 32.0   1st Qu.: 2.000   1st Qu.:       0   1st Qu.:1.000  
 Median : 501.0   Median :653.0   RS:478   Feminino :461   Median : 37.0   Median : 5.000   Median : 8958835   Median :1.000  
 Mean   : 500.9   Mean   :648.6   SC:258   M        :  6   Mean   : 38.9   Mean   : 5.069   Mean   : 7164928   Mean   :1.527  
 3rd Qu.: 750.5   3rd Qu.:721.0   SP:  4   Masculino:521   3rd Qu.: 44.0   3rd Qu.: 8.000   3rd Qu.:12586844   3rd Qu.:2.000  
 Max.   :1000.0   Max.   :850.0   TD:  1   NA's     :  8   Max.   :140.0   Max.   :10.000   Max.   :21177431   Max.   :4.000  
                                                                                                                              
       X8               X9              X10                 X11        
 Min.   :0.0000   Min.   :0.0000   Min.   :9.677e+03   Min.   :0.0000  
 1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:3.029e+06   1st Qu.:0.0000  
 Median :1.0000   Median :1.0000   Median :8.703e+06   Median :0.0000  
 Mean   :0.7027   Mean   :0.5095   Mean   :3.529e+07   Mean   :0.2032  
 3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1.405e+07   3rd Qu.:0.0000  
 Max.   :1.0000   Max.   :1.0000   Max.   :1.193e+10   Max.   :1.0000  
                                   NA's   :7                           
#nomeando de forma correta os dados
colnames(dados)=c("Id","Score","Estado","Genero","Idade","Patrimonio","Saldo","Produtos","TemCartCredito","Ativo","Salario","Saiu")
head(dados)

Explorar Dados, colunas categóricas

#Estados
counts=table(dados$Estado)
barplot(counts,main="Estados",xlab="Estados")

#Gênero
counts=table(dados$Genero)
barplot(counts,main="Gêneros",xlab="Gêneros")

Explorar, colunas numéricas

#Score
summary(dados$Score)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  376.0   580.0   653.0   648.6   721.0   850.0 
boxplot(dados$Score)

hist(dados$Score)

#Idade
summary(dados$Idade)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  -20.0    32.0    37.0    38.9    44.0   140.0 
boxplot(dados$Idade)

hist(dados$Idade)

summary(dados$Saldo)
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
       0        0  8958835  7164928 12586844 21177431 
boxplot(dados$Saldo)

hist(dados$Saldo)

#dados faltantes - NAs
dados[!complete.cases(dados),]
summary(dados$Salario)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max.      NA's 
9.677e+03 3.029e+06 8.703e+06 3.529e+07 1.405e+07 1.193e+10         7 
median(dados$salario,na.rm=T)
NULL
dados[is.na(dados$Salario),]$Salario = median(dados$Salario,na.rm = T)
dados[!complete.cases(dados$Salario),]
NA

Falta de padronização em gênero

#ver resultado
summary(dados$Genero)
 Feminino Masculino 
      464       535 

Idades fora do domínio

summary(dados$Idade)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  -20.0    32.0    37.0    38.9    44.0   140.0 
#fora do domínio, numérico - ver idades anormais
dados[dados$Idade<0 | dados$Idade>110,]$Idade
[1] -10 -20 140
#não temos idade NAs
dados[is.na(dados$Idade),]
#preencher com a mediana
median(dados$Idade)
[1] 37
dados[dados$Idade<0 | dados$Idade>110,]$Idade=median(dados$Idade)

#buscamos novamente idades anormais

dados[dados$Idade<0 | dados$Idade>110,]

summary(dados$Idade)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0    32.0    37.0    38.9    44.0    82.0 

Dados duplicados

#buscar duplicados pelo ID
x=dados[duplicated(dados$Id),]
x
#verificamos que o ID 81 está duplicado
#vamos excluir pelo ID não pelo indice
dados=dados[-c(82),]
dados=dados[!dados$Id %in% c(x$Id),]
#buscamos a linha que estava duplicada
dados[dados$Id==x$Id,]

#verificamos novemente os dados duplicados
x =  dados[duplicated(dados$Id),]
x
NA

Estado fora do domínio

#fora do dominio - categorico
unique(dados$Estado)
[1] RS SC PR RP SP TD
Levels: PR RP RS SC SP TD
summary(dados$Estado)
 PR  RP  RS  SC  SP  TD 
257   1 476 258   4   1 
#preencher com a moda, RS
dados[!dados$Estado %in% c("RS","SC","PR"),]$Estado = "RS"
summary(dados$Estado)
 PR  RP  RS  SC  SP  TD 
257   0 482 258   0   0 
#removemos fatores não usados
dados$Estado =   factor(dados$Estado)
#visualizar novamente
summary(dados$Estado)
 PR  RS  SC 
257 482 258 

OUTLIERS XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

#outliers, criando um parametro com desvio padrão
desv = sd(dados$Salario, na.rm = T)
desv
[1] 529250582
dados[dados$Salario >= 2 *desv  , ]$Salario
[1] 11934688000 11563829000  1640178900  1119811900
#outra forma, resultado semelhante, mas sem os NAs
boxplot(dados$Salario)

boxplot(dados$Salario, outline = F)

x = boxplot(dados$Salario)$out

x
[1] 11934688000 11563829000  1640178900  1119811900
#atualizamos todos para mediana
median(dados$Salario)
[1] 8703250
dados[dados$Salario >= 2 *desv  , ]$Salario = median(dados$Salario)
#checamos se sairam os outliers
dados[dados$Salario >= 2 *desv  , ]$Salario
numeric(0)
summary(dados$Salario)
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
    9677  3073095  8703250  8834769 13909373 19972539 

OUTLIERS XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

GRÁFICOS BÁSICOS

HISTOGRAMAS: Uma excelente forma de visualizar a distribuição dos dados, ou seja em quais intervalos existe maior ou menor ocorrência ou frequência de dados. No histograma na sua forma tradicional serve para mostrar apenas uma variável numérica de cada vez. Um histograma é uma espécie de gráfico de barras que demonstra uma distribuição de frequências. No histograma, a base de cada uma das barras representa uma classe e a altura representa a quantidade ou frequência absoluta com que o valor de cada classe ocorre.

trees
hist( trees$Height)

hist( trees$Height,  main="Árvores", ylab="Frequência",xlab="Altura", col="blue")

hist( trees$Height,  main="Árvores", ylab="Frequência",xlab="Altura", col="blue",
      density=20, breaks=20    )

Densidade: Ao invés de ver a distribuição em forma de barra, passamos a ver através de uma linha mais suave. O gráfico de densidade mostra como os dados numéricos, agrupados em intervalos, são distribuídos entre os eixos X e Y. Para visualizar a densidade, isto é, visualizar em que medida os marcadores se sobrepõem totalmente, a configuração Por cor é usada.

densidade = density(trees$Height)
plot(densidade)

Densidade sobre o Histograma

hist(trees$Height, main=NULL, xlab=NULL, ylab = NULL)
par(new=TRUE)
plot(densidade)

DISPERSÃO: Ele é muito utilizado quando estamos comparando variáveis contínuas. Gráfico de Dispersão são utilizados para pontuar dados em um eixo vertical e horizontal com a intenção de exibir quanto uma variável é afetada por outra. Cada linha na tabela de dados é representada por um marcador cuja posição depende dos seus valores nas colunas determinados nos eixos X e Y.

plot(trees$Girth, trees$Volume)

plot(trees$Girth, trees$Volume, main="Árvores")

plot(trees$Girth, trees$Volume, ylab="Cirunferência", xlab="Volume", col="blue", main="Árvores")

#pch muda o elementog gráfico
plot(trees$Girth, trees$Volume, ylab="Cirunferência", xlab="Volume", col="blue", main="Árvores", pch=21)

Muda o tipo

plot(trees$Girth, trees$Volume, ylab="Cirunferência", xlab="Volume", col="blue", main="Árovres", pch=20, type="l")

Tremulação, diminui sobre posição dos pontos do gráfico

plot(jitter(trees$Girth), trees$Volume, ylab="Cirunferência", xlab="Volume", col="blue", main="Árvores")

Legenda com dimensão categórica: Este gráfico eu vou poder usar o IDC e Imput - acredito que seja uma ideia interessante.

CO2
plot(CO2$conc, CO2$uptake,pch=20, col= CO2$Treatment)
#"bottom", "bottomleft", "left", "topleft", "top", "topright", "right" and "center"
legend("bottomright",legend=c("nonchilled","chilled"),cex=1, fill=c("black","red")) 

NOVOS DADOS: Quando eu chamo a função plot com um conjunto de dados com mais de uma variável numérica o resultado será uma matriz de gráficos de dispersão.

plot(trees)

DIVISÃO DE TELA


#Executar com crtl+shift+enter
split.screen(figs=c(2,2))
[1] 1 2 3 4
screen(1)
plot(trees$Girth, trees$Volume)
screen(2)
plot(trees$Girth, trees$Height)
screen(3)
plot(trees$Height, trees$Volume)
screen(4)
hist(trees$Volume)
close.screen(all=TRUE)

GRÁFICOS BÁSICOS II XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Continuação

BOXPLOT: ou diagrama de caixa é uma ferramenta gráfica que permite visualizar a distribuição e valores discrepantes (outliers) dos dados, fornecendo assim um meio complementar para desenvolver uma perspectiva sobre o caráter dos dados.

boxplot(trees$Volume, main= "Árvores", xlab = "Volume")

boxplot(trees$Volume, main= "Árvores", xlab = "Volume", col="blue", horizontal=TRUE ) 

boxplot(trees$Volume, main="Árvores", xlab = "Volume", col="blue",outline=F )

#notch
boxplot( trees$Height, main="Árvores", xlab = "Altura", col="blue",  notch=TRUE )

#dados do gráfico
boxplot.stats(trees$Height)
$stats
[1] 63 72 76 80 87

$n
[1] 31

$conf
[1] 73.72979 78.27021

$out
numeric(0)
#lê apenas uma informação
boxplot.stats(trees$Height)$stats
[1] 63 72 76 80 87

VÁRIOS GRÁFICOS

boxplot(trees)

Agregação

InsectSprays
spray = aggregate(. ~ spray, data=InsectSprays,sum)
spray 

Gráfico de Barras - novos dados

#Executar com ctrl+shift+enter
barplot(spray$count,col= gray.colors(6), xlab="Spray", ylab="Total", names.arg=spray$spray)

#box()

Gráfico de Setor - Pizza

Pizza com legenda

pie(spray$count,labels=NA,  main="Spray",col=c(1:6))
legend("bottomright",legend=spray$spray ,cex=1, fill=c(1:6))

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX TABELAS

install.packages ("stargazer")
library(stargazer)

Formato Latex (muito ulizado em trabalhos científicos)

stargazer(iris)

% Table created by stargazer v.5.2.3 by Marek Hlavac, Social Policy Institute. E-mail: marek.hlavac at gmail.com
% Date and time: seg, ago 01, 2022 - 15:07:17
\begin{table}[!htbp] \centering 
  \caption{} 
  \label{} 
\begin{tabular}{@{\extracolsep{5pt}}lccccc} 
\\[-1.8ex]\hline 
\hline \\[-1.8ex] 
Statistic & \multicolumn{1}{c}{N} & \multicolumn{1}{c}{Mean} & \multicolumn{1}{c}{St. Dev.} & \multicolumn{1}{c}{Min} & \multicolumn{1}{c}{Max} \\ 
\hline \\[-1.8ex] 
Sepal.Length & 150 & 5.843 & 0.828 & 4.300 & 7.900 \\ 
Sepal.Width & 150 & 3.057 & 0.436 & 2.000 & 4.400 \\ 
Petal.Length & 150 & 3.758 & 1.765 & 1.000 & 6.900 \\ 
Petal.Width & 150 & 1.199 & 0.762 & 0.100 & 2.500 \\ 
\hline \\[-1.8ex] 
\end{tabular} 
\end{table} 

Formato Html

stargazer(iris, type="html")

<table style="text-align:center"><tr><td colspan="6" style="border-bottom: 1px solid black"></td></tr><tr><td style="text-align:left">Statistic</td><td>N</td><td>Mean</td><td>St. Dev.</td><td>Min</td><td>Max</td></tr>
<tr><td colspan="6" style="border-bottom: 1px solid black"></td></tr><tr><td style="text-align:left">Sepal.Length</td><td>150</td><td>5.843</td><td>0.828</td><td>4.300</td><td>7.900</td></tr>
<tr><td style="text-align:left">Sepal.Width</td><td>150</td><td>3.057</td><td>0.436</td><td>2.000</td><td>4.400</td></tr>
<tr><td style="text-align:left">Petal.Length</td><td>150</td><td>3.758</td><td>1.765</td><td>1.000</td><td>6.900</td></tr>
<tr><td style="text-align:left">Petal.Width</td><td>150</td><td>1.199</td><td>0.762</td><td>0.100</td><td>2.500</td></tr>
<tr><td colspan="6" style="border-bottom: 1px solid black"></td></tr></table>

Formato Texto

stargazer(iris, type="text")

===========================================
Statistic     N  Mean  St. Dev.  Min   Max 
-------------------------------------------
Sepal.Length 150 5.843  0.828   4.300 7.900
Sepal.Width  150 3.057  0.436   2.000 4.400
Petal.Length 150 3.758  1.765   1.000 6.900
Petal.Width  150 1.199  0.762   0.100 2.500
-------------------------------------------

Salva em Disco

stargazer(women, out="women.tex", summary=FALSE)

% Table created by stargazer v.5.2.3 by Marek Hlavac, Social Policy Institute. E-mail: marek.hlavac at gmail.com
% Date and time: seg, ago 01, 2022 - 15:07:34
\begin{table}[!htbp] \centering 
  \caption{} 
  \label{} 
\begin{tabular}{@{\extracolsep{5pt}} ccc} 
\\[-1.8ex]\hline 
\hline \\[-1.8ex] 
 & height & weight \\ 
\hline \\[-1.8ex] 
1 & $58$ & $115$ \\ 
2 & $59$ & $117$ \\ 
3 & $60$ & $120$ \\ 
4 & $61$ & $123$ \\ 
5 & $62$ & $126$ \\ 
6 & $63$ & $129$ \\ 
7 & $64$ & $132$ \\ 
8 & $65$ & $135$ \\ 
9 & $66$ & $139$ \\ 
10 & $67$ & $142$ \\ 
11 & $68$ & $146$ \\ 
12 & $69$ & $150$ \\ 
13 & $70$ & $154$ \\ 
14 & $71$ & $159$ \\ 
15 & $72$ & $164$ \\ 
\hline \\[-1.8ex] 
\end{tabular} 
\end{table} 

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX PACOTE LATICE É um pacote para visualização de produção de gráficos que já vem no R mas precisa ser carregado

library(lattice)

Boxplot

bwplot(trees$Volume)

bwplot(trees$Volume, main="Árvores",xlab="Volume")

Histograma Aspecto é a proporção, nint número de quebras, type: percent, count, density

Histograma condicional

#Peso das galinhas de acordo com alimentação
chickwts
#histrograma
histogram(chickwts$weight)

#agregamos dados
aggregate(chickwts$weight, by=list(chickwts$feed), FUN=sum)
#histograma condicional
histogram( ~weight | feed, data=chickwts)

Gráfico de dispersão condicional CO2, seis plantas em dois locais, refrigeradas ou não durante a noite

#CO2, conc: concentração de co2, uptake: captação de co2
CO2
xyplot(CO2$conc ~  CO2$uptake)

#type é a origem 
xyplot(CO2$conc ~  CO2$uptake | CO2$Type)

#regrigerado ou não
xyplot(CO2$conc ~  CO2$uptake | CO2$Treatment)

Cancer de esofago agegp: idade, alcgp: alcool, tobgp: tabaco

esoph
dotplot(esoph$alcgp ~ esoph$ncontrols, data=esoph)

dotplot(esoph$alcgp ~ esoph$ncontrols | esoph$tobgp)

Matriz de dispersão

splom(~CO2[4:5] | CO2$Type, CO2)

Densidade condicional

densityplot(~CO2$conc | CO2$Treatment, plot.points=FALSE)

densityplot(CO2$conc)

densityplot(~CO2$conc | CO2$Treatment)

densityplot(~CO2$conc | CO2$Treatment, plot.points=FALSE)

Gráfico 3D Spray para repelir abelhas

OrchardSprays
cloud(decrease ~  rowpos * colpos, data=OrchardSprays)

cloud(decrease ~  rowpos * colpos, groups=treatment, data=OrchardSprays)

Level plot Circunferência, largura e volume de árvores

trees
levelplot(Girth ~ Height * Volume, data=trees)

LS0tDQp0aXRsZTogIkRhdGEgU2NpZW5jZSBwYXJhIENpZW50aXN0YSBkZSBEYWRvcyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpPcyAxMCBNYW5kYW1lbnRvcyBkbyBSDQo8b2w+DQo8bGk+McK6IC0gTsOjbyBlbnRyYXLDoXMgZW0gcMOibmljbyENCjxsaT4ywrogLSBVdGlsaXphcsOhcyBvIFIgcGFyYSB0dWFzIGFuw6FsaXNlcyBiZW0gY29tbyBwYXJhIGEgbWFuaXB1bGHDp8OjbyBkZSB0ZXVzIGRhZG9zKG7Do28gbWFpcyBvIEV4Y2VsKTsNCjxsaT4zwrogLSBOdW5jYSBkaWdpdGFyw6FzIG8gY8OzZGlnbyBubyBjb25zb2xlOw0KPGxpPjTCuiAtIFNhbHZhcsOhcyBzZXVzIHNjcmlwdHMgZSBuw6NvIHNlIHByZW9jdXBhcsOhcyBjb20gbyAuUkRhdGE7DQo8bGk+NcK6IC0gU2VtcHJlIGNvbmNhdGVuYXLDoSBlIGphbWFpcyBlc3F1ZWNlcsOhcyBkb3MgcGFyw6pudGVzZXMgZGFzIGZ1bsOnw7VlczsNCjxsaT42wrogLSBDb25mZXJpcsOhcyBvIGRpcmV0w7NyaW8gZGUgdHJhYmFsaG8gZSBvcyBkYWRvcyBhcMOzcyBhIGltcG9ydGHDp8OjbywgYW50ZXMgZGUgZW5sb3VxdWVjZXJlczsNCjxsaT43wrogLSBVc2Fyw6FzIG8gaGVscCBhbnRlcyBkZSBwZXJndW50YXJlcyBlIG7Do28gY3VscGFyw6FzIG8gUiBwb3IgdGV1cyBlcnJvczsNCjxsaT44wrogLSBOw6NvIGVzbW9yZWNlcsOhcyBhcMOzcyBhcyBwcmltZWlyYXMgbm9pdGVzIGVtIGNsYVJvIGUgamFtYWlzIGFtYWxkacOnb2Fyw6FzIG8gUiBwb3IgdHVhcyBmYWx0YXMgZGUgdsOtcmd1bGFzOw0KPGxpPjnCuiAtIExlbWJyYXLDoXMgZGFzIGFzcGFzIGRvcyBjYXJhY3RlcmVzOw0KPGxpPjEwwrotIE7Do28gY29iacOnYXLDoXMgZSBhcGVyZmVpw6dvYXLDoXMgb3MgY8OzZGlnb3MgYWxoZWlvcyBlIG51bmNhIG9jdWx0YXLDoXMgbyBjw7NkaWdvIGUgYSBhdXRvcmlhIG9yaWdpbmFsDQo8L29sPg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygieGxzeCkNCmxpYnJhcnkoeGxzeCkNCmBgYA0KDQpgYGB7cn0NCmdldHdkKCkNCnNldHdkKCJDOi9Vc2Vycy9Vc3VhcmlvL1JfZGFkb3MiKQ0KYGBgDQpUZXh0bw0KYGBge3J9DQp4PXJlYWQuY3N2KGZpbGUuY2hvb3NlKCksaGVhZGVyID0gVFJVRSxzZXA9IiwiKQ0KeA0KeD1yZWFkLmNzdigiQ3JlZGl0LmNzdiIsaGVhZGVyPVRSVUUsc2VwPSIsIikNCngNCg0KYGBgDQpFeGNlbA0KYGBge3J9DQpkYWRvcz1yZWFkLnhsc3goIkNyZWRpdC54bHN4IixzaGVldEluZGV4PTEpDQpkYWRvcw0KYGBgDQpGb3JtYcOnw6NvIENpZW50aXN0YSBkZSBEYWRvcw0KTGltcGV6YSBlIFRyYXRhbWVudG8gZGUgRGFkb3MNCmBgYHtyfQ0KI2ltcG9ydGEgZGFkb3MsIHN0cmluZyB2YXppbyBjb21vIE5BLCBzdHJpbmcgY29tbyBmYXRvcmVzLi4uDQpkYWRvcz1yZWFkLmNzdigiQ2h1cm4uY3N2IixzZXA9IjsiLG5hLnN0cmluZ3MgPSAiIixzdHJpbmdzQXNGYWN0b3JzID0gVCkNCmhlYWQoZGFkb3MpDQpzdW1tYXJ5KGRhZG9zKQ0KYGBgDQpgYGB7cn0NCiNub21lYW5kbyBkZSBmb3JtYSBjb3JyZXRhIG9zIGRhZG9zDQpjb2xuYW1lcyhkYWRvcyk9YygiSWQiLCJTY29yZSIsIkVzdGFkbyIsIkdlbmVybyIsIklkYWRlIiwiUGF0cmltb25pbyIsIlNhbGRvIiwiUHJvZHV0b3MiLCJUZW1DYXJ0Q3JlZGl0byIsIkF0aXZvIiwiU2FsYXJpbyIsIlNhaXUiKQ0KaGVhZChkYWRvcykNCmBgYA0KRXhwbG9yYXIgRGFkb3MsIGNvbHVuYXMgY2F0ZWfDs3JpY2FzDQoNCmBgYHtyfQ0KI0VzdGFkb3MNCmNvdW50cz10YWJsZShkYWRvcyRFc3RhZG8pDQpiYXJwbG90KGNvdW50cyxtYWluPSJFc3RhZG9zIix4bGFiPSJFc3RhZG9zIikNCmBgYA0KDQpgYGB7cn0NCiNHw6puZXJvDQpjb3VudHM9dGFibGUoZGFkb3MkR2VuZXJvKQ0KYmFycGxvdChjb3VudHMsbWFpbj0iR8OqbmVyb3MiLHhsYWI9IkfDqm5lcm9zIikNCmBgYA0KDQpFeHBsb3JhciwgY29sdW5hcyBudW3DqXJpY2FzDQpgYGB7cn0NCiNTY29yZQ0Kc3VtbWFyeShkYWRvcyRTY29yZSkNCmJveHBsb3QoZGFkb3MkU2NvcmUpDQpoaXN0KGRhZG9zJFNjb3JlKQ0KYGBgDQoNCmBgYHtyfQ0KI0lkYWRlDQpzdW1tYXJ5KGRhZG9zJElkYWRlKQ0KYm94cGxvdChkYWRvcyRJZGFkZSkNCmhpc3QoZGFkb3MkSWRhZGUpDQpgYGANCmBgYHtyfQ0Kc3VtbWFyeShkYWRvcyRTYWxkbykNCmJveHBsb3QoZGFkb3MkU2FsZG8pDQpoaXN0KGRhZG9zJFNhbGRvKQ0KYGBgDQoNCmBgYHtyfQ0KI2RhZG9zIGZhbHRhbnRlcyAtIE5Bcw0KZGFkb3NbIWNvbXBsZXRlLmNhc2VzKGRhZG9zKSxdDQpgYGANCg0KDQpgYGB7cn0NCnN1bW1hcnkoZGFkb3MkU2FsYXJpbykNCm1lZGlhbihkYWRvcyRzYWxhcmlvLG5hLnJtPVQpDQpkYWRvc1tpcy5uYShkYWRvcyRTYWxhcmlvKSxdJFNhbGFyaW8gPSBtZWRpYW4oZGFkb3MkU2FsYXJpbyxuYS5ybSA9IFQpDQpkYWRvc1shY29tcGxldGUuY2FzZXMoZGFkb3MkU2FsYXJpbyksXQ0KDQpgYGANCg0KRmFsdGEgZGUgcGFkcm9uaXphw6fDo28gZW0gZ8OqbmVybw0KYGBge3J9DQojdmVyIHZhbG9yZXMNCnVuaXF1ZShkYWRvcyRHZW5lcm8pDQpzdW1tYXJ5KGRhZG9zJEdlbmVybykNCg0KI3RyYW5mb3JtYXIgRiBlIEZlbSBlbSBGZW1pbmlubw0KIyAiIiAsIE0gZW0gTWFzY3VsaW5vIChtb2RhKQ0KDQpkYWRvc1tpcy5uYShkYWRvcyRHZW5lcm8pfCBkYWRvcyRHZW5lcm89PSJNIixdJEdlbmVybz0iTWFzY3VsaW5vIg0KZGFkb3NbZGFkb3MkR2VuZXJvID09IkYiIHwgZGFkb3MkR2VuZXJvPT0iRmVtIixdJEdlbmVybz0iRmVtaW5pbm8iDQojdmVyIHJlc3VsdGFkbw0Kc3VtbWFyeShkYWRvcyRHZW5lcm8pDQoNCiNyZW1vdmVyIGxldmVscyBuw6NvIHV0aWxpemFkb3MgKHF1ZXJvIHJlbW92ZXIgRiBGZW0gTSkNCmRhZG9zJEdlbmVybz1mYWN0b3IoZGFkb3MkR2VuZXJvKQ0KDQojdmVyIHJlc3VsdGFkbw0Kc3VtbWFyeShkYWRvcyRHZW5lcm8pDQpgYGANCg0KSWRhZGVzIGZvcmEgZG8gZG9tw61uaW8NCmBgYHtyfQ0Kc3VtbWFyeShkYWRvcyRJZGFkZSkNCg0KI2ZvcmEgZG8gZG9tw61uaW8sIG51bcOpcmljbyAtIHZlciBpZGFkZXMgYW5vcm1haXMNCmRhZG9zW2RhZG9zJElkYWRlPDAgfCBkYWRvcyRJZGFkZT4xMTAsXSRJZGFkZQ0KDQojbsOjbyB0ZW1vcyBpZGFkZSBOQXMNCmRhZG9zW2lzLm5hKGRhZG9zJElkYWRlKSxdDQojcHJlZW5jaGVyIGNvbSBhIG1lZGlhbmENCm1lZGlhbihkYWRvcyRJZGFkZSkNCg0KZGFkb3NbZGFkb3MkSWRhZGU8MCB8IGRhZG9zJElkYWRlPjExMCxdJElkYWRlPW1lZGlhbihkYWRvcyRJZGFkZSkNCg0KI2J1c2NhbW9zIG5vdmFtZW50ZSBpZGFkZXMgYW5vcm1haXMNCg0KZGFkb3NbZGFkb3MkSWRhZGU8MCB8IGRhZG9zJElkYWRlPjExMCxdDQoNCnN1bW1hcnkoZGFkb3MkSWRhZGUpDQoNCmBgYA0KDQpEYWRvcyBkdXBsaWNhZG9zDQpgYGB7cn0NCiNidXNjYXIgZHVwbGljYWRvcyBwZWxvIElEDQp4PWRhZG9zW2R1cGxpY2F0ZWQoZGFkb3MkSWQpLF0NCngNCiN2ZXJpZmljYW1vcyBxdWUgbyBJRCA4MSBlc3TDoSBkdXBsaWNhZG8NCiN2YW1vcyBleGNsdWlyIHBlbG8gSUQgbsOjbyBwZWxvIGluZGljZQ0KZGFkb3M9ZGFkb3NbLWMoODIpLF0NCmRhZG9zPWRhZG9zWyFkYWRvcyRJZCAlaW4lIGMoeCRJZCksXQ0KI2J1c2NhbW9zIGEgbGluaGEgcXVlIGVzdGF2YSBkdXBsaWNhZGENCmRhZG9zW2RhZG9zJElkPT14JElkLF0NCg0KI3ZlcmlmaWNhbW9zIG5vdmVtZW50ZSBvcyBkYWRvcyBkdXBsaWNhZG9zDQp4ID0gIGRhZG9zW2R1cGxpY2F0ZWQoZGFkb3MkSWQpLF0NCngNCg0KYGBgDQoNCkVzdGFkbyBmb3JhIGRvIGRvbcOtbmlvDQpgYGB7cn0NCiNmb3JhIGRvIGRvbWluaW8gLSBjYXRlZ29yaWNvDQp1bmlxdWUoZGFkb3MkRXN0YWRvKQ0Kc3VtbWFyeShkYWRvcyRFc3RhZG8pDQojcHJlZW5jaGVyIGNvbSBhIG1vZGEsIFJTDQpkYWRvc1shZGFkb3MkRXN0YWRvICVpbiUgYygiUlMiLCJTQyIsIlBSIiksXSRFc3RhZG8gPSAiUlMiDQpzdW1tYXJ5KGRhZG9zJEVzdGFkbykNCiNyZW1vdmVtb3MgZmF0b3JlcyBuw6NvIHVzYWRvcw0KZGFkb3MkRXN0YWRvID0gICBmYWN0b3IoZGFkb3MkRXN0YWRvKQ0KI3Zpc3VhbGl6YXIgbm92YW1lbnRlDQpzdW1tYXJ5KGRhZG9zJEVzdGFkbykNCg0KYGBgDQoNCk9VVExJRVJTIFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYDQpgYGB7cn0NCiNvdXRsaWVycywgY3JpYW5kbyB1bSBwYXJhbWV0cm8gY29tIGRlc3ZpbyBwYWRyw6NvDQpkZXN2ID0gc2QoZGFkb3MkU2FsYXJpbywgbmEucm0gPSBUKQ0KZGVzdg0KZGFkb3NbZGFkb3MkU2FsYXJpbyA+PSAyICpkZXN2ICAsIF0kU2FsYXJpbw0KI291dHJhIGZvcm1hLCByZXN1bHRhZG8gc2VtZWxoYW50ZSwgbWFzIHNlbSBvcyBOQXMNCmJveHBsb3QoZGFkb3MkU2FsYXJpbykNCmJveHBsb3QoZGFkb3MkU2FsYXJpbywgb3V0bGluZSA9IEYpDQp4ID0gYm94cGxvdChkYWRvcyRTYWxhcmlvKSRvdXQNCngNCiNhdHVhbGl6YW1vcyB0b2RvcyBwYXJhIG1lZGlhbmENCm1lZGlhbihkYWRvcyRTYWxhcmlvKQ0KZGFkb3NbZGFkb3MkU2FsYXJpbyA+PSAyICpkZXN2ICAsIF0kU2FsYXJpbyA9IG1lZGlhbihkYWRvcyRTYWxhcmlvKQ0KI2NoZWNhbW9zIHNlIHNhaXJhbSBvcyBvdXRsaWVycw0KZGFkb3NbZGFkb3MkU2FsYXJpbyA+PSAyICpkZXN2ICAsIF0kU2FsYXJpbw0Kc3VtbWFyeShkYWRvcyRTYWxhcmlvKQ0KYGBgDQpPVVRMSUVSUyBYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYDQoNCkdSw4FGSUNPUyBCw4FTSUNPUw0KDQpISVNUT0dSQU1BUzogVW1hIGV4Y2VsZW50ZSBmb3JtYSBkZSB2aXN1YWxpemFyIGEgZGlzdHJpYnVpw6fDo28gZG9zIGRhZG9zLCBvdSBzZWphIGVtIHF1YWlzIGludGVydmFsb3MgZXhpc3RlIG1haW9yIG91IG1lbm9yIG9jb3Jyw6puY2lhIG91IGZyZXF1w6puY2lhIGRlIGRhZG9zLiBObyBoaXN0b2dyYW1hIG5hIHN1YSBmb3JtYSB0cmFkaWNpb25hbCBzZXJ2ZSBwYXJhIG1vc3RyYXIgYXBlbmFzIHVtYSB2YXJpw6F2ZWwgbnVtw6lyaWNhIGRlIGNhZGEgdmV6LiBVbSBoaXN0b2dyYW1hIMOpIHVtYSBlc3DDqWNpZSBkZSBncsOhZmljbyBkZSBiYXJyYXMgcXVlIGRlbW9uc3RyYSB1bWEgZGlzdHJpYnVpw6fDo28gZGUgZnJlcXXDqm5jaWFzLiBObyBoaXN0b2dyYW1hLCBhIGJhc2UgZGUgY2FkYSB1bWEgZGFzIGJhcnJhcyByZXByZXNlbnRhIHVtYSBjbGFzc2UgZSBhIGFsdHVyYSByZXByZXNlbnRhIGEgcXVhbnRpZGFkZSBvdSBmcmVxdcOqbmNpYSBhYnNvbHV0YSBjb20gcXVlIG8gdmFsb3IgZGUgY2FkYSBjbGFzc2Ugb2NvcnJlLg0KYGBge3J9DQp0cmVlcw0KaGlzdCggdHJlZXMkSGVpZ2h0KQ0KaGlzdCggdHJlZXMkSGVpZ2h0LCAgbWFpbj0iw4Fydm9yZXMiLCB5bGFiPSJGcmVxdcOqbmNpYSIseGxhYj0iQWx0dXJhIiwgY29sPSJibHVlIikNCmhpc3QoIHRyZWVzJEhlaWdodCwgIG1haW49IsOBcnZvcmVzIiwgeWxhYj0iRnJlcXXDqm5jaWEiLHhsYWI9IkFsdHVyYSIsIGNvbD0iYmx1ZSIsDQogICAgICBkZW5zaXR5PTIwLCBicmVha3M9MjAgICAgKQ0KDQpgYGANCg0KRGVuc2lkYWRlOiBBbyBpbnbDqXMgZGUgdmVyIGEgZGlzdHJpYnVpw6fDo28gZW0gZm9ybWEgZGUgYmFycmEsIHBhc3NhbW9zIGEgdmVyIGF0cmF2w6lzIGRlIHVtYSBsaW5oYSBtYWlzIHN1YXZlLiBPIGdyw6FmaWNvIGRlIGRlbnNpZGFkZSBtb3N0cmEgY29tbyBvcyBkYWRvcyBudW3DqXJpY29zLCBhZ3J1cGFkb3MgZW0gaW50ZXJ2YWxvcywgc8OjbyBkaXN0cmlidcOtZG9zIGVudHJlIG9zIGVpeG9zIFggZSBZLiBQYXJhIHZpc3VhbGl6YXIgYSBkZW5zaWRhZGUsIGlzdG8gw6ksIHZpc3VhbGl6YXIgZW0gcXVlIG1lZGlkYSBvcyBtYXJjYWRvcmVzIHNlIHNvYnJlcMO1ZW0gdG90YWxtZW50ZSwgYSBjb25maWd1cmHDp8OjbyBQb3IgY29yIMOpIHVzYWRhLg0KYGBge3J9DQpkZW5zaWRhZGUgPSBkZW5zaXR5KHRyZWVzJEhlaWdodCkNCnBsb3QoZGVuc2lkYWRlKQ0KYGBgDQoNCkRlbnNpZGFkZSBzb2JyZSBvIEhpc3RvZ3JhbWENCmBgYHtyfQ0KaGlzdCh0cmVlcyRIZWlnaHQsIG1haW49TlVMTCwgeGxhYj1OVUxMLCB5bGFiID0gTlVMTCkNCnBhcihuZXc9VFJVRSkNCnBsb3QoZGVuc2lkYWRlKQ0KDQpgYGANCg0KRElTUEVSU8ODTzogRWxlIMOpIG11aXRvIHV0aWxpemFkbyBxdWFuZG8gZXN0YW1vcyBjb21wYXJhbmRvIHZhcmnDoXZlaXMgY29udMOtbnVhcy4gR3LDoWZpY28gZGUgRGlzcGVyc8OjbyBzw6NvIHV0aWxpemFkb3MgcGFyYSBwb250dWFyIGRhZG9zIGVtIHVtIGVpeG8gdmVydGljYWwgZSBob3Jpem9udGFsIGNvbSBhIGludGVuw6fDo28gZGUgZXhpYmlyIHF1YW50byB1bWEgdmFyacOhdmVsIMOpIGFmZXRhZGEgcG9yIG91dHJhLiBDYWRhIGxpbmhhIG5hIHRhYmVsYSBkZSBkYWRvcyDDqSByZXByZXNlbnRhZGEgcG9yIHVtIG1hcmNhZG9yIGN1amEgcG9zacOnw6NvIGRlcGVuZGUgZG9zIHNldXMgdmFsb3JlcyBuYXMgY29sdW5hcyBkZXRlcm1pbmFkb3Mgbm9zIGVpeG9zIFggZSBZLg0KYGBge3J9DQpwbG90KHRyZWVzJEdpcnRoLCB0cmVlcyRWb2x1bWUpDQpwbG90KHRyZWVzJEdpcnRoLCB0cmVlcyRWb2x1bWUsIG1haW49IsOBcnZvcmVzIikNCnBsb3QodHJlZXMkR2lydGgsIHRyZWVzJFZvbHVtZSwgeWxhYj0iQ2lydW5mZXLDqm5jaWEiLCB4bGFiPSJWb2x1bWUiLCBjb2w9ImJsdWUiLCBtYWluPSLDgXJ2b3JlcyIpDQojcGNoIG11ZGEgbyBlbGVtZW50b2cgZ3LDoWZpY28NCnBsb3QodHJlZXMkR2lydGgsIHRyZWVzJFZvbHVtZSwgeWxhYj0iQ2lydW5mZXLDqm5jaWEiLCB4bGFiPSJWb2x1bWUiLCBjb2w9ImJsdWUiLCBtYWluPSLDgXJ2b3JlcyIsIHBjaD0yMSkNCmBgYA0KTXVkYSBvIHRpcG8NCmBgYHtyfQ0KcGxvdCh0cmVlcyRHaXJ0aCwgdHJlZXMkVm9sdW1lLCB5bGFiPSJDaXJ1bmZlcsOqbmNpYSIsIHhsYWI9IlZvbHVtZSIsIGNvbD0iYmx1ZSIsIG1haW49IsOBcm92cmVzIiwgcGNoPTIwLCB0eXBlPSJsIikNCmBgYA0KDQpUcmVtdWxhw6fDo28sIGRpbWludWkgc29icmUgcG9zacOnw6NvIGRvcyBwb250b3MgZG8gZ3LDoWZpY28NCmBgYHtyfQ0KcGxvdChqaXR0ZXIodHJlZXMkR2lydGgpLCB0cmVlcyRWb2x1bWUsIHlsYWI9IkNpcnVuZmVyw6puY2lhIiwgeGxhYj0iVm9sdW1lIiwgY29sPSJibHVlIiwgbWFpbj0iw4Fydm9yZXMiKQ0KYGBgDQoNCkxlZ2VuZGEgY29tIGRpbWVuc8OjbyBjYXRlZ8OzcmljYTogRXN0ZSBncsOhZmljbyBldSB2b3UgcG9kZXIgdXNhciBvIElEQyBlIEltcHV0IC0gYWNyZWRpdG8gcXVlIHNlamEgdW1hIGlkZWlhIGludGVyZXNzYW50ZS4NCmBgYHtyfQ0KQ08yDQpwbG90KENPMiRjb25jLCBDTzIkdXB0YWtlLHBjaD0yMCwgY29sPSBDTzIkVHJlYXRtZW50KQ0KIyJib3R0b20iLCAiYm90dG9tbGVmdCIsICJsZWZ0IiwgInRvcGxlZnQiLCAidG9wIiwgInRvcHJpZ2h0IiwgInJpZ2h0IiBhbmQgImNlbnRlciINCmxlZ2VuZCgiYm90dG9tcmlnaHQiLGxlZ2VuZD1jKCJub25jaGlsbGVkIiwiY2hpbGxlZCIpLGNleD0xLCBmaWxsPWMoImJsYWNrIiwicmVkIikpIA0KYGBgDQoNCk5PVk9TIERBRE9TOiBRdWFuZG8gZXUgY2hhbW8gYSBmdW7Dp8OjbyBwbG90IGNvbSB1bSBjb25qdW50byBkZSBkYWRvcyBjb20gbWFpcyBkZSB1bWEgdmFyacOhdmVsIG51bcOpcmljYSBvIHJlc3VsdGFkbyBzZXLDoSB1bWEgbWF0cml6IGRlIGdyw6FmaWNvcyBkZSBkaXNwZXJzw6NvLg0KYGBge3J9DQpwbG90KHRyZWVzKQ0KYGBgDQoNCkRJVklTw4NPIERFIFRFTEENCg0KDQpgYGB7cn0NCg0KI0V4ZWN1dGFyIGNvbSBjcnRsK3NoaWZ0K2VudGVyDQpzcGxpdC5zY3JlZW4oZmlncz1jKDIsMikpDQpzY3JlZW4oMSkNCnBsb3QodHJlZXMkR2lydGgsIHRyZWVzJFZvbHVtZSkNCnNjcmVlbigyKQ0KcGxvdCh0cmVlcyRHaXJ0aCwgdHJlZXMkSGVpZ2h0KQ0Kc2NyZWVuKDMpDQpwbG90KHRyZWVzJEhlaWdodCwgdHJlZXMkVm9sdW1lKQ0Kc2NyZWVuKDQpDQpoaXN0KHRyZWVzJFZvbHVtZSkNCmNsb3NlLnNjcmVlbihhbGw9VFJVRSkNCg0KYGBgDQoNCkdSw4FGSUNPUyBCw4FTSUNPUyBJSSBYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWA0KDQpDb250aW51YcOnw6NvDQoNCkJPWFBMT1Q6IG91IGRpYWdyYW1hIGRlIGNhaXhhIMOpIHVtYSBmZXJyYW1lbnRhIGdyw6FmaWNhIHF1ZSBwZXJtaXRlIHZpc3VhbGl6YXIgYSBkaXN0cmlidWnDp8OjbyBlIHZhbG9yZXMgZGlzY3JlcGFudGVzIChvdXRsaWVycykgZG9zIGRhZG9zLCBmb3JuZWNlbmRvIGFzc2ltIHVtIG1laW8gY29tcGxlbWVudGFyIHBhcmEgZGVzZW52b2x2ZXIgdW1hIHBlcnNwZWN0aXZhIHNvYnJlIG8gY2Fyw6F0ZXIgZG9zIGRhZG9zLg0KYGBge3J9DQpib3hwbG90KHRyZWVzJFZvbHVtZSwgbWFpbj0gIsOBcnZvcmVzIiwgeGxhYiA9ICJWb2x1bWUiKQ0KYm94cGxvdCh0cmVlcyRWb2x1bWUsIG1haW49ICLDgXJ2b3JlcyIsIHhsYWIgPSAiVm9sdW1lIiwgY29sPSJibHVlIiwgaG9yaXpvbnRhbD1UUlVFICkgDQpib3hwbG90KHRyZWVzJFZvbHVtZSwgbWFpbj0iw4Fydm9yZXMiLCB4bGFiID0gIlZvbHVtZSIsIGNvbD0iYmx1ZSIsb3V0bGluZT1GICkNCmBgYA0KDQpgYGB7cn0NCiNub3RjaA0KYm94cGxvdCggdHJlZXMkSGVpZ2h0LCBtYWluPSLDgXJ2b3JlcyIsIHhsYWIgPSAiQWx0dXJhIiwgY29sPSJibHVlIiwgIG5vdGNoPVRSVUUgKQ0KI2RhZG9zIGRvIGdyw6FmaWNvDQpib3hwbG90LnN0YXRzKHRyZWVzJEhlaWdodCkNCiNsw6ogYXBlbmFzIHVtYSBpbmZvcm1hw6fDo28NCmJveHBsb3Quc3RhdHModHJlZXMkSGVpZ2h0KSRzdGF0cw0KYGBgDQoNClbDgVJJT1MgR1LDgUZJQ09TDQoNCmBgYHtyfQ0KYm94cGxvdCh0cmVlcykNCmBgYA0KQWdyZWdhw6fDo28NCmBgYHtyfQ0KSW5zZWN0U3ByYXlzDQpzcHJheSA9IGFnZ3JlZ2F0ZSguIH4gc3ByYXksIGRhdGE9SW5zZWN0U3ByYXlzLHN1bSkNCnNwcmF5IA0KYGBgDQpHcsOhZmljbyBkZSBCYXJyYXMgLSBub3ZvcyBkYWRvcyANCmBgYHtyfQ0KI0V4ZWN1dGFyIGNvbSBjdHJsK3NoaWZ0K2VudGVyDQpiYXJwbG90KHNwcmF5JGNvdW50LGNvbD0gZ3JheS5jb2xvcnMoNiksIHhsYWI9IlNwcmF5IiwgeWxhYj0iVG90YWwiLCBuYW1lcy5hcmc9c3ByYXkkc3ByYXkpDQpgYGANCkdyw6FmaWNvIGRlIFNldG9yIC0gUGl6emENCmBgYHtyfQ0KcGllKHNwcmF5JGNvdW50LCBsYWJlbHM9IHNwcmF5JHNwcmF5LCBtYWluPSJTcHJheSIsY29sPWMoMTo2KSkNCmBgYA0KUGl6emEgY29tIGxlZ2VuZGENCmBgYHtyfQ0KcGllKHNwcmF5JGNvdW50LGxhYmVscz1OQSwgIG1haW49IlNwcmF5Iixjb2w9YygxOjYpKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsbGVnZW5kPXNwcmF5JHNwcmF5ICxjZXg9MSwgZmlsbD1jKDE6NikpDQpgYGANCg0KWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYDQpUQUJFTEFTDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMgKCJzdGFyZ2F6ZXIiKQ0KbGlicmFyeShzdGFyZ2F6ZXIpDQpgYGANCg0KRm9ybWF0byBMYXRleCAobXVpdG8gdWxpemFkbyBlbSB0cmFiYWxob3MgY2llbnTDrWZpY29zKQ0KYGBge3J9DQpzdGFyZ2F6ZXIoaXJpcykNCg0KI3BhcmEgZmF6ZXIgdW1hIGJ1c2NhIG9uIGxpbmUgKHF1aWNrbGF0ZXguY29tKSAvIGVzdMOhIHRyYW5zZm9ybWHDp8OjbyBwZXJtaXRlIHZpc3VhbGl6YXIgb3MgZGFkb3MgZSBvcyBncsOhZmljb3MgY2FzbyBzZSBxdWVpcmEgZmF6ZXIgdW1hIGFwcmVzZW50YcOnw6NvLg0KYGBgDQoNCkZvcm1hdG8gSHRtbA0KYGBge3J9DQpzdGFyZ2F6ZXIoaXJpcywgdHlwZT0iaHRtbCIpDQoNCiNwYXJhIHZpc3VhbGl6YXIgc2VsZWNpb25lIGEgZmVycmFtZW50YSBvbmxpbmUgZW0gaHR0cHM6Ly93d3cub25saW5laHRtbGVkaXRvci5uZXQvDQpgYGANCg0KRm9ybWF0byBUZXh0bw0KYGBge3J9DQpzdGFyZ2F6ZXIoaXJpcywgdHlwZT0idGV4dCIpDQpgYGANCg0KU2FsdmEgZW0gRGlzY28NCmBgYHtyfQ0Kc3RhcmdhemVyKHdvbWVuLCBvdXQ9IndvbWVuLnRleCIsIHN1bW1hcnk9RkFMU0UpDQoNCiNCdXNjYSBvbmxpbmUgKHF1aWNrbGF0ZXguY29tKQ0KYGBgDQpYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWA0KUEFDT1RFIExBVElDRQ0Kw4kgdW0gcGFjb3RlIHBhcmEgdmlzdWFsaXphw6fDo28gZGUgcHJvZHXDp8OjbyBkZSBncsOhZmljb3MgcXVlIGrDoSB2ZW0gbm8gUiBtYXMgcHJlY2lzYSBzZXIgY2FycmVnYWRvDQoNCmBgYHtyfQ0KbGlicmFyeShsYXR0aWNlKQ0KYGBgDQoNCkJveHBsb3QNCmBgYHtyfQ0KYndwbG90KHRyZWVzJFZvbHVtZSkNCmJ3cGxvdCh0cmVlcyRWb2x1bWUsIG1haW49IsOBcnZvcmVzIix4bGFiPSJWb2x1bWUiKQ0KYGBgDQoNCkhpc3RvZ3JhbWENCkFzcGVjdG8gw6kgYSBwcm9wb3LDp8OjbywgbmludCBuw7ptZXJvIGRlIHF1ZWJyYXMsIHR5cGU6IHBlcmNlbnQsIGNvdW50LCBkZW5zaXR5DQpgYGB7cn0NCmhpc3RvZ3JhbSh0cmVlcyRWb2x1bWUsIG1haW49IsOBcnZvcmVzIix4bGFiPSJWb2x1bWUiLGFzcGVjdD0xLCB0eXBlID0gInBlcmNlbnQiLCBuaW50PTUgKQ0KYGBgDQoNCg0KSGlzdG9ncmFtYSBjb25kaWNpb25hbA0KYGBge3J9DQojUGVzbyBkYXMgZ2FsaW5oYXMgZGUgYWNvcmRvIGNvbSBhbGltZW50YcOnw6NvDQpjaGlja3d0cw0KI2hpc3Ryb2dyYW1hDQpoaXN0b2dyYW0oY2hpY2t3dHMkd2VpZ2h0KQ0KI2FncmVnYW1vcyBkYWRvcw0KYWdncmVnYXRlKGNoaWNrd3RzJHdlaWdodCwgYnk9bGlzdChjaGlja3d0cyRmZWVkKSwgRlVOPXN1bSkNCiNoaXN0b2dyYW1hIGNvbmRpY2lvbmFsDQpoaXN0b2dyYW0oIH53ZWlnaHQgfCBmZWVkLCBkYXRhPWNoaWNrd3RzKQ0KYGBgDQoNCg0KR3LDoWZpY28gZGUgZGlzcGVyc8OjbyBjb25kaWNpb25hbA0KQ08yLCBzZWlzIHBsYW50YXMgZW0gZG9pcyBsb2NhaXMsIHJlZnJpZ2VyYWRhcyBvdSBuw6NvIGR1cmFudGUgYSBub2l0ZQ0KYGBge3J9DQojQ08yLCBjb25jOiBjb25jZW50cmHDp8OjbyBkZSBjbzIsIHVwdGFrZTogY2FwdGHDp8OjbyBkZSBjbzINCkNPMg0KeHlwbG90KENPMiRjb25jIH4gIENPMiR1cHRha2UpDQojdHlwZSDDqSBhIG9yaWdlbSANCnh5cGxvdChDTzIkY29uYyB+ICBDTzIkdXB0YWtlIHwgQ08yJFR5cGUpDQojcmVncmlnZXJhZG8gb3UgbsOjbw0KeHlwbG90KENPMiRjb25jIH4gIENPMiR1cHRha2UgfCBDTzIkVHJlYXRtZW50KQ0KYGBgDQoNCkNhbmNlciBkZSBlc29mYWdvDQphZ2VncDogaWRhZGUsIGFsY2dwOiBhbGNvb2wsIHRvYmdwOiB0YWJhY28NCmBgYHtyfQ0KZXNvcGgNCmRvdHBsb3QoZXNvcGgkYWxjZ3AgfiBlc29waCRuY29udHJvbHMsIGRhdGE9ZXNvcGgpDQpkb3RwbG90KGVzb3BoJGFsY2dwIH4gZXNvcGgkbmNvbnRyb2xzIHwgZXNvcGgkdG9iZ3ApDQpgYGANCg0KTWF0cml6IGRlIGRpc3BlcnPDo28NCmBgYHtyfQ0Kc3Bsb20ofkNPMls0OjVdIHwgQ08yJFR5cGUsIENPMikNCmBgYA0KDQoNCkRlbnNpZGFkZSBjb25kaWNpb25hbA0KYGBge3J9DQpkZW5zaXR5cGxvdCh+Q08yJGNvbmMgfCBDTzIkVHJlYXRtZW50LCBwbG90LnBvaW50cz1GQUxTRSkNCmRlbnNpdHlwbG90KENPMiRjb25jKQ0KZGVuc2l0eXBsb3QofkNPMiRjb25jIHwgQ08yJFRyZWF0bWVudCkNCmRlbnNpdHlwbG90KH5DTzIkY29uYyB8IENPMiRUcmVhdG1lbnQsIHBsb3QucG9pbnRzPUZBTFNFKQ0KYGBgDQoNCg0KR3LDoWZpY28gM0QNClNwcmF5IHBhcmEgcmVwZWxpciBhYmVsaGFzDQpgYGB7cn0NCk9yY2hhcmRTcHJheXMNCmNsb3VkKGRlY3JlYXNlIH4gIHJvd3BvcyAqIGNvbHBvcywgZGF0YT1PcmNoYXJkU3ByYXlzKQ0KY2xvdWQoZGVjcmVhc2UgfiAgcm93cG9zICogY29scG9zLCBncm91cHM9dHJlYXRtZW50LCBkYXRhPU9yY2hhcmRTcHJheXMpDQpgYGANCg0KTGV2ZWwgcGxvdA0KQ2lyY3VuZmVyw6puY2lhLCBsYXJndXJhIGUgdm9sdW1lIGRlIMOhcnZvcmVzDQpgYGB7cn0NCnRyZWVzDQpsZXZlbHBsb3QoR2lydGggfiBIZWlnaHQgKiBWb2x1bWUsIGRhdGE9dHJlZXMpDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=