# install.packages("FactoMineR", "gglot2", "readxl")
# install.packages("devtools")
# devtools::install_github("kassambara/factoextra")
rm(list=ls(all=TRUE))
library("ggplot2")
library("FactoMineR")
library("factoextra")
library("readxl")
library("gplots")
library("corrplot")
library("graphics")
library("foreign")
library("readxl")

Tabela de contingência

# entrada de dados já tabulados 
# o comum é usar o comando table para cruzar os dados
tab <- matrix(data = c(15,27,50,43,25,37,12,8,8,13,9,10), nrow = 3, ncol = 4, byrow = T)
rownames(tab) <- c("menos de 2000", "2000 a 5000", "5000 ou mais")
colnames(tab) <- c("0", "1", "2", ">2")
tab
               0  1  2 >2
menos de 2000 15 27 50 43
2000 a 5000   25 37 12  8
5000 ou mais   8 13  9 10
# 1. convert the data as a table
dt <- as.table(as.matrix(tab))
# 2. Graph
balloonplot(t(dt), main ="Tabela de contingência - Renda x nº de Filhos", xlab ="", ylab="",
            label = T, show.margins = T)

As células com os resíduos padronizados absolutos mais elevados contribuem mais para a pontuação total do Qui-quadrado.

chisq <- chisq.test(tab)
chisq

    Pearson's Chi-squared test

data:  tab
X-squared = 41.391, df = 6, p-value = 2.425e-07
# Observed counts
chisq$observed
               0  1  2 >2
menos de 2000 15 27 50 43
2000 a 5000   25 37 12  8
5000 ou mais   8 13  9 10
# Expected counts 
round(chisq$expected,2)
                  0     1     2    >2
menos de 2000 25.21 40.45 37.30 32.04
2000 a 5000   15.32 24.57 22.65 19.46
5000 ou mais   7.47 11.98 11.05  9.49
# residuals padronizados 
round(chisq$residuals, 2)
                  0     1     2    >2
menos de 2000 -2.03 -2.11  2.08  1.94
2000 a 5000    2.47  2.51 -2.24 -2.60
5000 ou mais   0.19  0.29 -0.62  0.16
corrplot(chisq$residuals, is.cor = FALSE)

A inércia total (\(\phi^2\)) é a quantidade de informação contida na tabela de dados

phi2 <- as.numeric(chisq$statistic/sum(tab))
phi2
[1] 0.161053

Análise de correspondência

Análise de correspondência (ANACOR) é necessária para a tabela de contingência grande.

Aplica-se para visualizar pontos da linha e pontos da coluna em um espaço dimensional reduzido.

ANACOR é um método de redução dimensional aplicado a uma tabela de contingência.

A informação retida por cada dimensão é chamada autovalor.

A informação total (ou inércia) contida nos dados é chamada phi (\(\phi^2\)) e pode ser calculado da seguinte forma:

\(\phi^2 = \frac{\chi^2}{grand.total}\)

A análise de correspondência é usada para representar graficamente a tabela de distâncias entre variáveis de linha ou entre variáveis de coluna.

A abordagem ANACOR inclui as seguintes etapas:

1.Calcular os resíduos padronizados

Os resíduos padronizados (S) são:

\(S = \frac{o - e}{\sqrt{e}}\)

De fato, S é apenas a raíz quadrada dos termos que compõem a estatística \(\chi^2\).

2. Calcule a decomposição do valor singular (SVD) dos resíduos padronizados.

\(M = \frac{1}{sqrt(grand.total)} \times S\)

SVD significa que queremos encontrar matrizes ortogonais U e V, em conjunto com uma matriz diagonal \(\Delta\), tal que:

\(M = U \Delta V^T\)

O autovalor de um determinado eixo é:

\(\lambda = \delta^2\)

\(\delta\) É o valor singular

As coordenadas das variáveis de linha em um determinado eixo são:

\(row.coord = \frac{U * \delta }{\sqrt{row.mass}}\)

As coordenadas das colunas são

\(col.coord = \frac{V * \delta }{\sqrt{col.mass}}\)

cálculo SVD

### Grand total
n <- sum(tab)
n
[1] 257
### Standardized residuals
residuals <- chisq$residuals/sqrt(n)
residuals
                        0          1           2          >2
menos de 2000 -0.12688453 -0.1318950  0.12976393  0.12074460
2000 a 5000    0.15437047  0.1564536 -0.13962533 -0.16207938
5000 ou mais   0.01207689  0.0182992 -0.03847852  0.01024036
### Number of dimensions
nb.axes <- min(nrow(residuals)-1, ncol(residuals)-1)
nb.axes
[1] 2
### Singular value decomposition
res.svd <- svd(residuals, nu = nb.axes, nv = nb.axes)
res.svd
$d
[1] 3.994798e-01 3.832625e-02 4.351618e-17

$u
            [,1]       [,2]
[1,]  0.63728969  0.2618588
[2,] -0.76722779  0.3038015
[3,] -0.07227289 -0.9160429

$v
           [,1]        [,2]
[1,] -0.5010825  0.06807878
[2,] -0.5142022 -0.09836301
[3,]  0.4821335  0.69950511
[4,]  0.5020554 -0.70454425
### singular value
sv <- res.svd$d[1:nb.axes] 
sv
[1] 0.39947979 0.03832625
u <-res.svd$u
u
            [,1]       [,2]
[1,]  0.63728969  0.2618588
[2,] -0.76722779  0.3038015
[3,] -0.07227289 -0.9160429
v <- res.svd$v
v
           [,1]        [,2]
[1,] -0.5010825  0.06807878
[2,] -0.5142022 -0.09836301
[3,]  0.4821335  0.69950511
[4,]  0.5020554 -0.70454425
# inércia total
sum(sv^2)
[1] 0.161053
### Eigenvalues
eig <- sv^2
### Variances in percentage
variance <- eig*100/sum(eig)
### Cumulative variances
cumvar <- cumsum(variance)
eig<- data.frame(eig = eig, variance = variance,
                     cumvariance = cumvar)
head(eig)
barplot(eig[, 2], names.arg=1:nrow(eig), 
       main = "Variances",
       xlab = "Dimensions",
       ylab = "Percentage of variances",
       col ="steelblue")
### Add connected line segments to the plot
lines(x = 1:nrow(eig), eig[, 2], 
      type="b", pch=19, col = "red")

Quantas dimensões reter ?

1. O número máximo de eixos na ANACOR é:

\(nb.axes = min(r-1, c-1)\)

r e c são respectivamente o número de linhas e colunas na tabela.

Coordenadas principais das linhas (Row coordinates)

### row sum
row.sum <- apply(tab, 1, sum)
row.sum
menos de 2000   2000 a 5000  5000 ou mais 
          135            82            40 
### row mass
row.mass <- row.sum/n
row.mass
menos de 2000   2000 a 5000  5000 ou mais 
    0.5252918     0.3190661     0.1556420 
### row coord = sv * u /sqrt(row.mass)
cc <- t(apply(u, 1, '*', sv)) ### each row X sv
row.coord <- apply(cc, 2, '/', sqrt(row.mass))
rownames(row.coord) <- rownames(tab)
colnames(row.coord) <- paste0("Dim.", 1:nb.axes)
round(row.coord,3)
               Dim.1  Dim.2
menos de 2000  0.351  0.014
2000 a 5000   -0.543  0.021
5000 ou mais  -0.073 -0.089
### plot
plot(row.coord, pch=19, col = "blue")
text(row.coord, labels =rownames(row.coord), pos = 3, col ="blue")
abline(v=0, h=0, lty = 2)

Coordenadas principais das colunas (Column coordinates)

### Coordinates of columns
col.sum <- apply(tab, 2, sum)
col.mass <- col.sum/n
### coordinates sv * v /sqrt(col.mass)
cc <- t(apply(v, 1, '*', sv))
col.coord <- apply(cc, 2, '/', sqrt(col.mass))
rownames(col.coord) <- colnames(tab)
colnames(col.coord) <- paste0("Dim", 1:nb.axes)
head(col.coord)
         Dim1         Dim2
0  -0.4631802  0.006037457
1  -0.3752753 -0.006887306
2   0.3664372  0.051006395
>2  0.4116690 -0.055425073
### plot
plot(col.coord, pch=17, col = "red")
text(col.coord, labels =rownames(col.coord), pos = 3, col ="red")
abline(v=0, h=0, lty = 2)

Biplot de linhas e colunas para ver a associação

xlim <- range(c(row.coord[,1], col.coord[,1]))*1.1
ylim <- range(c(row.coord[,2], col.coord[,2]))*1.1
### Plot of rows
plot(row.coord, pch=19, col = "blue", xlim = xlim, ylim = ylim)
text(row.coord, labels =rownames(row.coord), pos = 3, col ="blue")
### plot off columns
points(col.coord, pch=17, col = "red")
text(col.coord, labels =rownames(col.coord), pos = 3, col ="red")
abline(v=0, h=0, lty = 2)

Diagnóstico

Lembre-se que, a inércia total contida nos dados é:

\(\phi^2 = \frac{\chi^2}{n} = 0.3176\)

Nosso gráfico bidimensional captura 100% da inércia total da tabela.

Contribuição de linhas e colunas

As contribuições de uma linha / coluna para a definição de um eixo principal são:

\(row.contrib = \frac{row.mass * row.coord^2}{eigenvalue}\)

\(col.contrib = \frac{col.mass * col.coord^2}{eigenvalue}\)

Contribuição de linhas em %

### contrib <- row.mass * row.coord^2/eigenvalue
cc <- apply(row.coord^2, 2, "*", row.mass)
row.contrib <- t(apply(cc, 1, "/", eig[1:nb.axes,1])) *100
round(row.contrib, 2)
              Dim.1 Dim.2
menos de 2000 40.61  6.86
2000 a 5000   58.86  9.23
5000 ou mais   0.52 83.91
corrplot(row.contrib, is.cor = FALSE)

Contribuição das colunas em %

### contrib <- col.mass * col.coord^2/eigenvalue
cc <- apply(col.coord^2, 2, "*", col.mass)
col.contrib <- t(apply(cc, 1, "/", eig[1:nb.axes,1])) *100
round(col.contrib, 2)
    Dim1  Dim2
0  25.11  0.46
1  26.44  0.97
2  23.25 48.93
>2 25.21 49.64
corrplot(col.contrib, is.cor = FALSE)

Qualidade da representação

A qualidade da representação é chamada COS2.

A qualidade da representação de uma linha em um eixo é:

\(row.cos2 = \frac{row.coord^2}{d^2}\)

Lembre-se de que a distância entre cada perfil de linha e o perfil de linha médio é:

\(d^2(row_i, average.profile) = \sum{\frac{(row.profile_i - average.profile)^2}{average.profile}}\)

row.profile <- tab/row.sum
head(round(row.profile, 3))
                  0     1     2    >2
menos de 2000 0.111 0.200 0.370 0.319
2000 a 5000   0.305 0.451 0.146 0.098
5000 ou mais  0.200 0.325 0.225 0.250
average.profile <- col.sum/n
head(round(average.profile, 3))
    0     1     2    >2 
0.187 0.300 0.276 0.237 

O código R abaixo calcula a distância do perfil médio para todas as variáveis de linha

average.rp <- col.sum/n 
d2.row <- apply(row.profile, 1, 
                function(row.p, av.p){sum(((row.p - av.p)^2)/av.p)}, 
                average.rp)
head(round(d2.row,3))
menos de 2000   2000 a 5000  5000 ou mais 
        0.124         0.295         0.013 

O cos2 de linhas no mapa de fatores são:

row.cos2 <- apply(row.coord^2, 2, "/", d2.row)
round(row.cos2, 3)
              Dim.1 Dim.2
menos de 2000 0.998 0.002
2000 a 5000   0.999 0.001
5000 ou mais  0.403 0.597
corrplot(row.cos2, is.cor = FALSE)

Cos2 das colunas

\(col.cos2 = \frac{col.coord^2}{d^2}\)

col.profile <- t(tab)/col.sum
col.profile <- t(col.profile)
###head(round(col.profile, 3))
average.profile <- row.sum/n
###head(round(average.profile, 3))

O código R abaixo calcula a distância do perfil médio para todas as variáveis da coluna

d2.col <- apply(col.profile, 2, 
        function(col.p, av.p){sum(((col.p - av.p)^2)/av.p)}, 
        average.profile)
round(d2.col,3)
    0     1     2    >2 
0.215 0.141 0.137 0.173 

O cos2 das colunas no mapa de fatores são:

col.cos2 <- apply(col.coord^2, 2, "/", d2.col)
round(col.cos2, 3)
    Dim1  Dim2
0  1.000 0.000
1  1.000 0.000
2  0.981 0.019
>2 0.982 0.018
corrplot(col.cos2, is.cor = FALSE)

Pacotes no R para ANACOR

-FactoMineR -ade4 -ca

library(FactoMineR)
res.ca <- CA(tab, graph = F)
summary(res.ca)

Call:
CA(X = tab, graph = F) 

The chi square of independence between the two variables is equal to 41.39062 (p-value =  2.425291e-07 ).

Eigenvalues
                       Dim.1   Dim.2
Variance               0.160   0.001
% of var.             99.088   0.912
Cumulative % of var.  99.088 100.000

Rows
                Iner*1000    Dim.1    ctr   cos2    Dim.2    ctr   cos2  
menos de 2000 |    64.914 | -0.351 40.614  0.998 | -0.014  6.857  0.002 |
2000 a 5000   |    94.073 |  0.543 58.864  0.999 | -0.021  9.230  0.001 |
5000 ou mais  |     2.066 |  0.073  0.522  0.403 |  0.089 83.913  0.597 |

Columns
                Iner*1000    Dim.1    ctr   cos2    Dim.2    ctr   cos2  
0             |    40.076 |  0.463 25.108  1.000 | -0.006  0.463  0.000 |
1             |    42.209 |  0.375 26.440  1.000 |  0.007  0.968  0.000 |
2             |    37.815 | -0.366 23.245  0.981 | -0.051 48.931  0.019 |
>2            |    40.954 | -0.412 25.206  0.982 |  0.055 49.638  0.018 |
### eigenvalue
head(res.ca$eig)[, 1:2]
### barplot of percentage of variance
barplot(res.ca$eig[,2], names.arg = rownames(res.ca$eig))

### Plot row points
plot(res.ca, invisible ="col")

### Plot column points
plot(res.ca, invisible ="row")

### Biplot of rows and columns
plot(res.ca)

plot(res.ca)

ellipseCA(res.ca,ellipse="col")

ellipseCA(res.ca,ellipse="row")

ellipseCA(res.ca)

LS0tDQp0aXRsZTogJ0Fuw6FsaXNlIGRlIENvcnJlc3BvbmTDqm5jaWEgLSBSZW5kYSB4IE7CuiBkZSBmaWxob3MgLSBBTkFDT1IgJw0KYXV0aG9yOiAiTGVvbmksIFIuIEMuIFByb2Zlc3NvciBEci4iDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoqKioNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEYpDQpybShsaXN0PWxzKGFsbD1UUlVFKSkNCmBgYA0KDQpgYGB7cn0NCiMgaW5zdGFsbC5wYWNrYWdlcygiRmFjdG9NaW5lUiIsICJnZ2xvdDIiLCAicmVhZHhsIikNCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQ0KIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImthc3NhbWJhcmEvZmFjdG9leHRyYSIpDQpybShsaXN0PWxzKGFsbD1UUlVFKSkNCmxpYnJhcnkoImdncGxvdDIiKQ0KbGlicmFyeSgiRmFjdG9NaW5lUiIpDQpsaWJyYXJ5KCJmYWN0b2V4dHJhIikNCmxpYnJhcnkoInJlYWR4bCIpDQpsaWJyYXJ5KCJncGxvdHMiKQ0KbGlicmFyeSgiY29ycnBsb3QiKQ0KbGlicmFyeSgiZ3JhcGhpY3MiKQ0KbGlicmFyeSgiZm9yZWlnbiIpDQpsaWJyYXJ5KCJyZWFkeGwiKQ0KYGBgDQoNCiMjIyBUYWJlbGEgZGUgY29udGluZ8OqbmNpYQ0KDQpgYGB7cn0NCiMgZW50cmFkYSBkZSBkYWRvcyBqw6EgdGFidWxhZG9zIA0KIyBvIGNvbXVtIMOpIHVzYXIgbyBjb21hbmRvIHRhYmxlIHBhcmEgY3J1emFyIG9zIGRhZG9zDQp0YWIgPC0gbWF0cml4KGRhdGEgPSBjKDE1LDI3LDUwLDQzLDI1LDM3LDEyLDgsOCwxMyw5LDEwKSwgbnJvdyA9IDMsIG5jb2wgPSA0LCBieXJvdyA9IFQpDQpyb3duYW1lcyh0YWIpIDwtIGMoIm1lbm9zIGRlIDIwMDAiLCAiMjAwMCBhIDUwMDAiLCAiNTAwMCBvdSBtYWlzIikNCmNvbG5hbWVzKHRhYikgPC0gYygiMCIsICIxIiwgIjIiLCAiPjIiKQ0KDQp0YWINCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0NCg0KIyAxLiBjb252ZXJ0IHRoZSBkYXRhIGFzIGEgdGFibGUNCmR0IDwtIGFzLnRhYmxlKGFzLm1hdHJpeCh0YWIpKQ0KIyAyLiBHcmFwaA0KYmFsbG9vbnBsb3QodChkdCksIG1haW4gPSJUYWJlbGEgZGUgY29udGluZ8OqbmNpYSAtIFJlbmRhIHggbsK6IGRlIEZpbGhvcyIsIHhsYWIgPSIiLCB5bGFiPSIiLA0KICAgICAgICAgICAgbGFiZWwgPSBULCBzaG93Lm1hcmdpbnMgPSBUKQ0KDQpgYGANCg0KIyMjIEFzIGPDqWx1bGFzIGNvbSBvcyByZXPDrWR1b3MgcGFkcm9uaXphZG9zIGFic29sdXRvcyBtYWlzIGVsZXZhZG9zIGNvbnRyaWJ1ZW0gbWFpcyBwYXJhIGEgcG9udHVhw6fDo28gdG90YWwgZG8gUXVpLXF1YWRyYWRvLg0KDQpgYGB7cn0NCmNoaXNxIDwtIGNoaXNxLnRlc3QodGFiKQ0KY2hpc3ENCiMgT2JzZXJ2ZWQgY291bnRzDQpjaGlzcSRvYnNlcnZlZA0KIyBFeHBlY3RlZCBjb3VudHMgDQpyb3VuZChjaGlzcSRleHBlY3RlZCwyKQ0KIyByZXNpZHVhbHMgcGFkcm9uaXphZG9zIA0Kcm91bmQoY2hpc3EkcmVzaWR1YWxzLCAyKQ0KDQpjb3JycGxvdChjaGlzcSRyZXNpZHVhbHMsIGlzLmNvciA9IEZBTFNFKQ0KDQpgYGANCg0KDQojIyMgQSBpbsOpcmNpYSB0b3RhbCAoJFxwaGleMiQpIMOpIGEgcXVhbnRpZGFkZSBkZSBpbmZvcm1hw6fDo28gY29udGlkYSBuYSB0YWJlbGEgZGUgZGFkb3MNCiANCmBgYHtyfQ0KcGhpMiA8LSBhcy5udW1lcmljKGNoaXNxJHN0YXRpc3RpYy9zdW0odGFiKSkNCnBoaTINCmBgYA0KDQoNCg0KIyMjIEFuw6FsaXNlIGRlIGNvcnJlc3BvbmTDqm5jaWENCiMjIyBBbsOhbGlzZSBkZSBjb3JyZXNwb25kw6puY2lhIChBTkFDT1IpIMOpIG5lY2Vzc8OhcmlhIHBhcmEgYSB0YWJlbGEgZGUgY29udGluZ8OqbmNpYSBncmFuZGUuDQojIyMgQXBsaWNhLXNlIHBhcmEgdmlzdWFsaXphciBwb250b3MgZGEgbGluaGEgZSBwb250b3MgZGEgY29sdW5hIGVtIHVtIGVzcGHDp28gZGltZW5zaW9uYWwgcmVkdXppZG8uDQojIyMgQU5BQ09SIMOpIHVtIG3DqXRvZG8gZGUgcmVkdcOnw6NvIGRpbWVuc2lvbmFsIGFwbGljYWRvIGEgdW1hIHRhYmVsYSBkZSBjb250aW5nw6puY2lhLiANCiMjIyBBIGluZm9ybWHDp8OjbyByZXRpZGEgcG9yIGNhZGEgZGltZW5zw6NvIMOpIGNoYW1hZGEgYXV0b3ZhbG9yLg0KIyMjIEEgaW5mb3JtYcOnw6NvIHRvdGFsIChvdSBpbsOpcmNpYSkgY29udGlkYSBub3MgZGFkb3Mgw6kgY2hhbWFkYSBwaGkgKCRccGhpXjIkKSBlIHBvZGUgc2VyIGNhbGN1bGFkbyBkYSBzZWd1aW50ZSBmb3JtYToNCg0KJFxwaGleMiA9IFxmcmFje1xjaGleMn17Z3JhbmQudG90YWx9JA0KDQojIyMgQSBhbsOhbGlzZSBkZSBjb3JyZXNwb25kw6puY2lhIMOpIHVzYWRhIHBhcmEgcmVwcmVzZW50YXIgZ3JhZmljYW1lbnRlIGEgdGFiZWxhIGRlIGRpc3TDom5jaWFzIGVudHJlIHZhcmnDoXZlaXMgZGUgbGluaGEgb3UgZW50cmUgdmFyacOhdmVpcyBkZSBjb2x1bmEuDQoNCiMjIyBBIGFib3JkYWdlbSBBTkFDT1IgaW5jbHVpIGFzIHNlZ3VpbnRlcyBldGFwYXM6DQoNCiMjIyAxLkNhbGN1bGFyIG9zIHJlc8OtZHVvcyBwYWRyb25pemFkb3MNCg0KIyMjIE9zIHJlc8OtZHVvcyBwYWRyb25pemFkb3MgKFMpIHPDo286DQoNCiRTID0gXGZyYWN7byAtIGV9e1xzcXJ0e2V9fSQNCg0KIyMjIERlIGZhdG8sIFMgw6kgYXBlbmFzIGEgcmHDrXogcXVhZHJhZGEgZG9zIHRlcm1vcyBxdWUgY29tcMO1ZW0gYSBlc3RhdMOtc3RpY2EgJFxjaGleMiQuDQoNCiMjIyAyLiBDYWxjdWxlIGEgZGVjb21wb3Npw6fDo28gZG8gdmFsb3Igc2luZ3VsYXIgKFNWRCkgZG9zIHJlc8OtZHVvcyBwYWRyb25pemFkb3MuDQoNCiRNID0gXGZyYWN7MX17c3FydChncmFuZC50b3RhbCl9IFx0aW1lcyBTJA0KDQojIyMgU1ZEIHNpZ25pZmljYSBxdWUgcXVlcmVtb3MgZW5jb250cmFyIG1hdHJpemVzIG9ydG9nb25haXMgVSBlIFYsIGVtIGNvbmp1bnRvIGNvbSB1bWEgbWF0cml6IGRpYWdvbmFsICRcRGVsdGEkLCB0YWwgcXVlOg0KDQokTSA9IFUgXERlbHRhIFZeVCQNCg0KLSBVIMOJIHVtYSBtYXRyaXogY29udGVuZG8gYXV0b3ZldG9yZXMgZGUgbGluaGFzDQotICRcRGVsdGEkIMOJIGEgbWF0cml6IGRpYWdvbmFsLiBPcyBuw7ptZXJvcyBuYSBkaWFnb25hbCBkYSBtYXRyaXogc8OjbyBjaGFtYWRvcyBkZSB2YWxvcmVzIHNpbmd1bGFyZXMgKFNWKS4gT3MgYXV0b3ZhbG9yZXMgc8OjbyBvIFNWIHF1YWRyYWRvLg0KLSBWIMOJIHVtYSBtYXRyaXogY29udGVuZG8gYXV0b3ZldG9yZXMgZGUgY29sdW5hDQoNCiMjIyBPIGF1dG92YWxvciBkZSB1bSBkZXRlcm1pbmFkbyBlaXhvIMOpOg0KDQokXGxhbWJkYSA9IFxkZWx0YV4yJA0KDQokXGRlbHRhJCDDiSBvIHZhbG9yIHNpbmd1bGFyDQoNCiMjIyBBcyBjb29yZGVuYWRhcyBkYXMgdmFyacOhdmVpcyBkZSBsaW5oYSBlbSB1bSBkZXRlcm1pbmFkbyBlaXhvIHPDo286DQoNCiRyb3cuY29vcmQgPSBcZnJhY3tVICogXGRlbHRhIH17XHNxcnR7cm93Lm1hc3N9fSQNCg0KIyMjIEFzIGNvb3JkZW5hZGFzIGRhcyBjb2x1bmFzIHPDo28NCg0KJGNvbC5jb29yZCA9IFxmcmFje1YgKiBcZGVsdGEgfXtcc3FydHtjb2wubWFzc319JA0KDQojIyMgY8OhbGN1bG8gU1ZEDQoNCmBgYHtyfQ0KIyMjIEdyYW5kIHRvdGFsDQpuIDwtIHN1bSh0YWIpDQpuDQojIyMgU3RhbmRhcmRpemVkIHJlc2lkdWFscw0KcmVzaWR1YWxzIDwtIGNoaXNxJHJlc2lkdWFscy9zcXJ0KG4pDQpyZXNpZHVhbHMNCiMjIyBOdW1iZXIgb2YgZGltZW5zaW9ucw0KbmIuYXhlcyA8LSBtaW4obnJvdyhyZXNpZHVhbHMpLTEsIG5jb2wocmVzaWR1YWxzKS0xKQ0KbmIuYXhlcw0KDQojIyMgU2luZ3VsYXIgdmFsdWUgZGVjb21wb3NpdGlvbg0KcmVzLnN2ZCA8LSBzdmQocmVzaWR1YWxzLCBudSA9IG5iLmF4ZXMsIG52ID0gbmIuYXhlcykNCnJlcy5zdmQNCg0KIyMjIHNpbmd1bGFyIHZhbHVlDQpzdiA8LSByZXMuc3ZkJGRbMTpuYi5heGVzXSANCnN2DQp1IDwtcmVzLnN2ZCR1DQp1DQp2IDwtIHJlcy5zdmQkdg0Kdg0KDQojIGluw6lyY2lhIHRvdGFsDQpzdW0oc3ZeMikNCmBgYA0KDQoNCg0KYGBge3J9DQojIyMgRWlnZW52YWx1ZXMNCmVpZyA8LSBzdl4yDQojIyMgVmFyaWFuY2VzIGluIHBlcmNlbnRhZ2UNCnZhcmlhbmNlIDwtIGVpZyoxMDAvc3VtKGVpZykNCiMjIyBDdW11bGF0aXZlIHZhcmlhbmNlcw0KY3VtdmFyIDwtIGN1bXN1bSh2YXJpYW5jZSkNCmVpZzwtIGRhdGEuZnJhbWUoZWlnID0gZWlnLCB2YXJpYW5jZSA9IHZhcmlhbmNlLA0KICAgICAgICAgICAgICAgICAgICAgY3VtdmFyaWFuY2UgPSBjdW12YXIpDQpoZWFkKGVpZykNCmBgYA0KDQoNCmBgYHtyfQ0KYmFycGxvdChlaWdbLCAyXSwgbmFtZXMuYXJnPTE6bnJvdyhlaWcpLCANCiAgICAgICBtYWluID0gIlZhcmlhbmNlcyIsDQogICAgICAgeGxhYiA9ICJEaW1lbnNpb25zIiwNCiAgICAgICB5bGFiID0gIlBlcmNlbnRhZ2Ugb2YgdmFyaWFuY2VzIiwNCiAgICAgICBjb2wgPSJzdGVlbGJsdWUiKQ0KIyMjIEFkZCBjb25uZWN0ZWQgbGluZSBzZWdtZW50cyB0byB0aGUgcGxvdA0KbGluZXMoeCA9IDE6bnJvdyhlaWcpLCBlaWdbLCAyXSwgDQogICAgICB0eXBlPSJiIiwgcGNoPTE5LCBjb2wgPSAicmVkIikNCmBgYA0KIyMjIFF1YW50YXMgZGltZW5zw7VlcyByZXRlciA/DQoNCiMjIyAxLiBPIG7Dum1lcm8gbcOheGltbyBkZSBlaXhvcyBuYSBBTkFDT1Igw6k6DQoNCiRuYi5heGVzID0gbWluKHItMSwgYy0xKSQNCg0KIyMjIHIgZSBjIHPDo28gcmVzcGVjdGl2YW1lbnRlIG8gbsO6bWVybyBkZSBsaW5oYXMgZSBjb2x1bmFzIG5hIHRhYmVsYS4NCg0KIyMjIENvb3JkZW5hZGFzIHByaW5jaXBhaXMgZGFzIGxpbmhhcyAoUm93IGNvb3JkaW5hdGVzKQ0KDQpgYGB7cn0NCiMjIyByb3cgc3VtDQpyb3cuc3VtIDwtIGFwcGx5KHRhYiwgMSwgc3VtKQ0Kcm93LnN1bQ0KDQojIyMgcm93IG1hc3MNCnJvdy5tYXNzIDwtIHJvdy5zdW0vbg0Kcm93Lm1hc3MNCg0KIyMjIHJvdyBjb29yZCA9IHN2ICogdSAvc3FydChyb3cubWFzcykNCmNjIDwtIHQoYXBwbHkodSwgMSwgJyonLCBzdikpICMjIyBlYWNoIHJvdyBYIHN2DQpyb3cuY29vcmQgPC0gYXBwbHkoY2MsIDIsICcvJywgc3FydChyb3cubWFzcykpDQpyb3duYW1lcyhyb3cuY29vcmQpIDwtIHJvd25hbWVzKHRhYikNCmNvbG5hbWVzKHJvdy5jb29yZCkgPC0gcGFzdGUwKCJEaW0uIiwgMTpuYi5heGVzKQ0Kcm91bmQocm93LmNvb3JkLDMpDQoNCg0KIyMjIHBsb3QNCnBsb3Qocm93LmNvb3JkLCBwY2g9MTksIGNvbCA9ICJibHVlIikNCnRleHQocm93LmNvb3JkLCBsYWJlbHMgPXJvd25hbWVzKHJvdy5jb29yZCksIHBvcyA9IDMsIGNvbCA9ImJsdWUiKQ0KYWJsaW5lKHY9MCwgaD0wLCBsdHkgPSAyKQ0KYGBgDQoNCiMjIyAgQ29vcmRlbmFkYXMgcHJpbmNpcGFpcyBkYXMgY29sdW5hcyAoQ29sdW1uIGNvb3JkaW5hdGVzKQ0KDQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQ0KIyMjIENvb3JkaW5hdGVzIG9mIGNvbHVtbnMNCmNvbC5zdW0gPC0gYXBwbHkodGFiLCAyLCBzdW0pDQpjb2wubWFzcyA8LSBjb2wuc3VtL24NCiMjIyBjb29yZGluYXRlcyBzdiAqIHYgL3NxcnQoY29sLm1hc3MpDQpjYyA8LSB0KGFwcGx5KHYsIDEsICcqJywgc3YpKQ0KY29sLmNvb3JkIDwtIGFwcGx5KGNjLCAyLCAnLycsIHNxcnQoY29sLm1hc3MpKQ0Kcm93bmFtZXMoY29sLmNvb3JkKSA8LSBjb2xuYW1lcyh0YWIpDQpjb2xuYW1lcyhjb2wuY29vcmQpIDwtIHBhc3RlMCgiRGltIiwgMTpuYi5heGVzKQ0KaGVhZChjb2wuY29vcmQpDQoNCiMjIyBwbG90DQpwbG90KGNvbC5jb29yZCwgcGNoPTE3LCBjb2wgPSAicmVkIikNCnRleHQoY29sLmNvb3JkLCBsYWJlbHMgPXJvd25hbWVzKGNvbC5jb29yZCksIHBvcyA9IDMsIGNvbCA9InJlZCIpDQphYmxpbmUodj0wLCBoPTAsIGx0eSA9IDIpDQpgYGANCg0KIyMjIEJpcGxvdCBkZSBsaW5oYXMgZSBjb2x1bmFzIHBhcmEgdmVyIGEgYXNzb2NpYcOnw6NvDQoNCmBgYHtyfQ0KeGxpbSA8LSByYW5nZShjKHJvdy5jb29yZFssMV0sIGNvbC5jb29yZFssMV0pKSoxLjENCnlsaW0gPC0gcmFuZ2UoYyhyb3cuY29vcmRbLDJdLCBjb2wuY29vcmRbLDJdKSkqMS4xDQojIyMgUGxvdCBvZiByb3dzDQpwbG90KHJvdy5jb29yZCwgcGNoPTE5LCBjb2wgPSAiYmx1ZSIsIHhsaW0gPSB4bGltLCB5bGltID0geWxpbSkNCnRleHQocm93LmNvb3JkLCBsYWJlbHMgPXJvd25hbWVzKHJvdy5jb29yZCksIHBvcyA9IDMsIGNvbCA9ImJsdWUiKQ0KIyMjIHBsb3Qgb2ZmIGNvbHVtbnMNCnBvaW50cyhjb2wuY29vcmQsIHBjaD0xNywgY29sID0gInJlZCIpDQp0ZXh0KGNvbC5jb29yZCwgbGFiZWxzID1yb3duYW1lcyhjb2wuY29vcmQpLCBwb3MgPSAzLCBjb2wgPSJyZWQiKQ0KYWJsaW5lKHY9MCwgaD0wLCBsdHkgPSAyKQ0KYGBgDQoNCiMjIyBEaWFnbsOzc3RpY28NCg0KIyMjTGVtYnJlLXNlIHF1ZSwgYSBpbsOpcmNpYSB0b3RhbCBjb250aWRhIG5vcyBkYWRvcyDDqToNCg0KJFxwaGleMiA9IFxmcmFje1xjaGleMn17bn0gPSAwLjMxNzYkDQoNCiMjI05vc3NvIGdyw6FmaWNvIGJpZGltZW5zaW9uYWwgY2FwdHVyYSAxMDAlIGRhIGluw6lyY2lhIHRvdGFsIGRhIHRhYmVsYS4NCg0KIyMjIENvbnRyaWJ1acOnw6NvIGRlIGxpbmhhcyBlIGNvbHVuYXMNCg0KIyMjQXMgY29udHJpYnVpw6fDtWVzIGRlIHVtYSBsaW5oYSAvIGNvbHVuYSBwYXJhIGEgZGVmaW5pw6fDo28gZGUgdW0gZWl4byBwcmluY2lwYWwgc8OjbzoNCg0KJHJvdy5jb250cmliID0gXGZyYWN7cm93Lm1hc3MgKiByb3cuY29vcmReMn17ZWlnZW52YWx1ZX0kDQoNCiRjb2wuY29udHJpYiA9IFxmcmFje2NvbC5tYXNzICogY29sLmNvb3JkXjJ9e2VpZ2VudmFsdWV9JA0KDQojIyNDb250cmlidWnDp8OjbyBkZSBsaW5oYXMgZW0gJQ0KDQpgYGB7cn0NCiMjIyBjb250cmliIDwtIHJvdy5tYXNzICogcm93LmNvb3JkXjIvZWlnZW52YWx1ZQ0KY2MgPC0gYXBwbHkocm93LmNvb3JkXjIsIDIsICIqIiwgcm93Lm1hc3MpDQpyb3cuY29udHJpYiA8LSB0KGFwcGx5KGNjLCAxLCAiLyIsIGVpZ1sxOm5iLmF4ZXMsMV0pKSAqMTAwDQpyb3VuZChyb3cuY29udHJpYiwgMikNCg0KY29ycnBsb3Qocm93LmNvbnRyaWIsIGlzLmNvciA9IEZBTFNFKQ0KYGBgDQoNCiMjI0NvbnRyaWJ1acOnw6NvIGRhcyBjb2x1bmFzIGVtICUNCg0KYGBge3J9DQojIyMgY29udHJpYiA8LSBjb2wubWFzcyAqIGNvbC5jb29yZF4yL2VpZ2VudmFsdWUNCmNjIDwtIGFwcGx5KGNvbC5jb29yZF4yLCAyLCAiKiIsIGNvbC5tYXNzKQ0KY29sLmNvbnRyaWIgPC0gdChhcHBseShjYywgMSwgIi8iLCBlaWdbMTpuYi5heGVzLDFdKSkgKjEwMA0Kcm91bmQoY29sLmNvbnRyaWIsIDIpDQoNCmNvcnJwbG90KGNvbC5jb250cmliLCBpcy5jb3IgPSBGQUxTRSkNCmBgYA0KDQojIyMgUXVhbGlkYWRlIGRhIHJlcHJlc2VudGHDp8Ojbw0KDQojIyNBIHF1YWxpZGFkZSBkYSByZXByZXNlbnRhw6fDo28gw6kgY2hhbWFkYSBDT1MyLg0KDQojIyNBIHF1YWxpZGFkZSBkYSByZXByZXNlbnRhw6fDo28gZGUgdW1hIGxpbmhhIGVtIHVtIGVpeG8gw6k6DQoNCiRyb3cuY29zMiA9IFxmcmFje3Jvdy5jb29yZF4yfXtkXjJ9JA0KDQotIFJvdy5jb29yZCDDqSBhIGNvb3JkZW5hZGEgZGEgbGluaGEgbm8gZWl4bw0KLSAkZF4yJCDDiSBhIGRpc3TDom5jaWEgYW8gcXVhZHJhZG8gZG8gcGVyZmlsIG3DqWRpbw0KDQojIyNMZW1icmUtc2UgZGUgcXVlIGEgZGlzdMOibmNpYSBlbnRyZSBjYWRhIHBlcmZpbCBkZSBsaW5oYSBlIG8gcGVyZmlsIGRlIGxpbmhhIG3DqWRpbyDDqToNCg0KJGReMihyb3dfaSwgYXZlcmFnZS5wcm9maWxlKSA9IFxzdW17XGZyYWN7KHJvdy5wcm9maWxlX2kgLSBhdmVyYWdlLnByb2ZpbGUpXjJ9e2F2ZXJhZ2UucHJvZmlsZX19JA0KDQpgYGB7cn0NCnJvdy5wcm9maWxlIDwtIHRhYi9yb3cuc3VtDQpoZWFkKHJvdW5kKHJvdy5wcm9maWxlLCAzKSkNCg0KYXZlcmFnZS5wcm9maWxlIDwtIGNvbC5zdW0vbg0KaGVhZChyb3VuZChhdmVyYWdlLnByb2ZpbGUsIDMpKQ0KYGBgDQoNCiMjI08gY8OzZGlnbyBSIGFiYWl4byBjYWxjdWxhIGEgZGlzdMOibmNpYSBkbyBwZXJmaWwgbcOpZGlvIHBhcmEgdG9kYXMgYXMgdmFyacOhdmVpcyBkZSBsaW5oYQ0KDQpgYGB7cn0NCmF2ZXJhZ2UucnAgPC0gY29sLnN1bS9uIA0KZDIucm93IDwtIGFwcGx5KHJvdy5wcm9maWxlLCAxLCANCiAgICAgICAgICAgICAgICBmdW5jdGlvbihyb3cucCwgYXYucCl7c3VtKCgocm93LnAgLSBhdi5wKV4yKS9hdi5wKX0sIA0KICAgICAgICAgICAgICAgIGF2ZXJhZ2UucnApDQpoZWFkKHJvdW5kKGQyLnJvdywzKSkNCmBgYA0KDQojIyNPIGNvczIgZGUgbGluaGFzIG5vIG1hcGEgZGUgZmF0b3JlcyBzw6NvOg0KDQpgYGB7cn0NCnJvdy5jb3MyIDwtIGFwcGx5KHJvdy5jb29yZF4yLCAyLCAiLyIsIGQyLnJvdykNCnJvdW5kKHJvdy5jb3MyLCAzKQ0KDQpjb3JycGxvdChyb3cuY29zMiwgaXMuY29yID0gRkFMU0UpDQpgYGANCg0KIyMjQ29zMiBkYXMgY29sdW5hcw0KDQokY29sLmNvczIgPSBcZnJhY3tjb2wuY29vcmReMn17ZF4yfSQNCg0KYGBge3J9DQpjb2wucHJvZmlsZSA8LSB0KHRhYikvY29sLnN1bQ0KY29sLnByb2ZpbGUgPC0gdChjb2wucHJvZmlsZSkNCiMjI2hlYWQocm91bmQoY29sLnByb2ZpbGUsIDMpKQ0KYXZlcmFnZS5wcm9maWxlIDwtIHJvdy5zdW0vbg0KIyMjaGVhZChyb3VuZChhdmVyYWdlLnByb2ZpbGUsIDMpKQ0KYGBgDQoNCiMjI08gY8OzZGlnbyBSIGFiYWl4byBjYWxjdWxhIGEgZGlzdMOibmNpYSBkbyBwZXJmaWwgbcOpZGlvIHBhcmEgdG9kYXMgYXMgdmFyacOhdmVpcyBkYSBjb2x1bmENCg0KYGBge3J9DQoNCmQyLmNvbCA8LSBhcHBseShjb2wucHJvZmlsZSwgMiwgDQogICAgICAgIGZ1bmN0aW9uKGNvbC5wLCBhdi5wKXtzdW0oKChjb2wucCAtIGF2LnApXjIpL2F2LnApfSwgDQogICAgICAgIGF2ZXJhZ2UucHJvZmlsZSkNCnJvdW5kKGQyLmNvbCwzKQ0KYGBgDQoNCiMjI08gY29zMiBkYXMgY29sdW5hcyBubyBtYXBhIGRlIGZhdG9yZXMgc8OjbzoNCg0KYGBge3J9DQpjb2wuY29zMiA8LSBhcHBseShjb2wuY29vcmReMiwgMiwgIi8iLCBkMi5jb2wpDQpyb3VuZChjb2wuY29zMiwgMykNCg0KY29ycnBsb3QoY29sLmNvczIsIGlzLmNvciA9IEZBTFNFKQ0KYGBgDQoNCg0KIyMjIFBhY290ZXMgbm8gUiBwYXJhIEFOQUNPUg0KDQotRmFjdG9NaW5lUg0KLWFkZTQNCi1jYQ0KDQpgYGB7cn0NCmxpYnJhcnkoRmFjdG9NaW5lUikNCnJlcy5jYSA8LSBDQSh0YWIsIGdyYXBoID0gRikNCg0Kc3VtbWFyeShyZXMuY2EpDQpgYGANCg0KDQpgYGB7cn0NCiMjIyBlaWdlbnZhbHVlDQpoZWFkKHJlcy5jYSRlaWcpWywgMToyXQ0KDQojIyMgYmFycGxvdCBvZiBwZXJjZW50YWdlIG9mIHZhcmlhbmNlDQpiYXJwbG90KHJlcy5jYSRlaWdbLDJdLCBuYW1lcy5hcmcgPSByb3duYW1lcyhyZXMuY2EkZWlnKSkNCmBgYA0KDQpgYGB7cn0NCiMjIyBQbG90IHJvdyBwb2ludHMNCnBsb3QocmVzLmNhLCBpbnZpc2libGUgPSJjb2wiKQ0KYGBgDQoNCmBgYHtyfQ0KIyMjIFBsb3QgY29sdW1uIHBvaW50cw0KcGxvdChyZXMuY2EsIGludmlzaWJsZSA9InJvdyIpDQpgYGANCg0KYGBge3J9DQojIyMgQmlwbG90IG9mIHJvd3MgYW5kIGNvbHVtbnMNCnBsb3QocmVzLmNhKQ0KcGxvdChyZXMuY2EpDQplbGxpcHNlQ0EocmVzLmNhLGVsbGlwc2U9ImNvbCIpDQplbGxpcHNlQ0EocmVzLmNhLGVsbGlwc2U9InJvdyIpDQplbGxpcHNlQ0EocmVzLmNhKQ0KYGBgDQo=