Resumen

Si quieres saber si tus datos tienen una distribución normal usa métodos gráficos y aplica pruebas estadísticas de normalidad:

Métodos gráficos * Histogramas: para ver la forma de la distribución

datos = rnorm(100)
hist(datos)

qqnorm(datos)
qqline(datos, col="red", lw=2)

Pruebas de normalidad * Hipótesis nula = datos tienen una distribución normal * Con p-value < 0.05 se rechaza hipótesi nula * Shapiro-Wilk * para tamaño de muestra menor a 5 mil

shapiro.test(datos)

    Shapiro-Wilk normality test

data:  datos
W = 0.99048, p-value = 0.7037
set.seed(100)
binom_data = rbinom(15, 5, .6)
shapiro.test(binom_data)

    Shapiro-Wilk normality test

data:  binom_data
W = 0.88164, p-value = 0.0502
pois_data = rpois(15, 5)
shapiro.test(pois_data)

    Shapiro-Wilk normality test

data:  pois_data
W = 0.9416, p-value = 0.4029

Antes de empezar

Algunos ejemplos necesitan las siguientes librerías. - car - nortest - qualityTools

Si aún no las tienes, instálalas.

install.packages("car")
Installing package into ‘/home/nekrum/R/x86_64-pc-linux-gnu-library/3.6’
(as ‘lib’ is unspecified)
probando la URL 'https://cloud.r-project.org/src/contrib/car_3.0-10.tar.gz'
Content type 'application/x-gzip' length 501293 bytes (489 KB)
==================================================
downloaded 489 KB

* installing *source* package ‘car’ ...
** package ‘car’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (car)

The downloaded source packages are in
    ‘/tmp/RtmpP5YTs3/downloaded_packages’
install.packages("qualityTools")
Installing package into ‘/home/nekrum/R/x86_64-pc-linux-gnu-library/3.6’
(as ‘lib’ is unspecified)
probando la URL 'https://cloud.r-project.org/src/contrib/qualityTools_1.55.tar.gz'
Content type 'application/x-gzip' length 1386009 bytes (1.3 MB)
==================================================
downloaded 1.3 MB

* installing *source* package ‘qualityTools’ ...
** package ‘qualityTools’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** inst
** byte-compile and prepare package for lazy loading
Creating a generic function for ‘nrow’ from package ‘base’ in package ‘qualityTools’
Creating a generic function for ‘ncol’ from package ‘base’ in package ‘qualityTools’
in method for ‘compPlot’ with signature ‘x="gageRR"’: no definition for class “gageRR”
in method for ‘averagePlot’ with signature ‘x="gageRR"’: no definition for class “gageRR”
in method for ‘effectPlot’ with signature ‘object="taguchiDesign"’: no definition for class “taguchiDesign”
in method for ‘errorPlot’ with signature ‘x="gageRR"’: no definition for class “gageRR”
Creating a new generic function for ‘sigma’ in package ‘qualityTools’
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (qualityTools)

The downloaded source packages are in
    ‘/tmp/RtmpP5YTs3/downloaded_packages’

Antes de analizar los datos con pruebas estadísticas paramétricas que asumen una distribución normal, cómo pruebas T o ANOVAs, debemos asegurarnos que nuestros datos tienen una distribución normal. Para ello podemos usar una combinación entre pruebas estadísticas de normalidad y métodos gráficos como histogramas y qq-plots.

Pruebas estadísticas de normalidad

Las pruebas de normalidad son pruebas estadísticas que ponen a prueba la hipótesis nula (h0) de que los datos tienen una distribución normal. Esto quiere decir que si la prueba arroja un valor de p < 0.05 (o el valor de significancia que decidas), se rachaza la h0 y concluimos que los datos NO están distribuidos de manera normal.

Algunas pruebas son:

Por ahora, sólo se hablará de Shapiro-Wilk y Kolmogorov-Smirnov.

Shapiro-Wilk

En el ejemplo de abajo, la función rnorm saca n datos aleatorios de una distribución normal con media = m y desviación estándar = s. Después, a esos datos se les aplica la prueba Shapiro-Wilk para probar si tienen una distribución normal o no.

Recuerda: Generalmente no sabemos la distribución de los datos a priori. En este ejemplo sabemos que los datos vienen de una distribución normal entonces esperamos que la prueba Shapiro-Wilk NO rechace h0. Es decir, p-value tiene que ser mayor a 0.05.


n = 30 # num de datos
m = 5  # media
s = 2  # desv estándar

# Datos aleatorios de una distribución normal
set.seed(123) # para reproducibilidad
norm_data = rnorm(n, m, s)

# prueba de normalidad; h0: norm_data está distribuida de manera normal
shapiro.test(norm_data) 

    Shapiro-Wilk normality test

data:  norm_data
W = 0.97894, p-value = 0.7966

Shapiro-Wilk arroja dos valores:

Los resultados de shapirto.test se pueden guardar en una variable para acceder a ellos posteriormente.

shapiro = shapiro.test(norm_data)
shapiro$statistic # shapiro$stat también funciona
        W 
0.9789351 
shapiro$p.value
[1] 0.7965839

Reto: si sacamos 1000 muestras diferentes con las mismas características que la anterior (tamaño, media, desv. estándar), sólo el 5% de las veces shapiro-wilk rechazará h0. Haz un código para comprobar eso.

n = 
m = 
s = 
  
repeticiones = 1000

Ahora probemos a Shapiro-Wilk con datos que sabemos que no vienen de una distribución normal.

En el ejemplo de abajo generamos datos aleatorios de una distribución poisson con una lambda = l. En las distribuciones tipo poisson, lambda es como si fuera la media y es el único parámetro que se necesita.

Como sabemos que los datos NO vienen de una distribución normal esperamos que shapiro-wilk rechace la h0.

n = 30 # núm de datos
l = 5  # lambda

# datos aleatorios de una distribución poisson
set.seed(50)
pois_data = rpois(n, l)

shapiro.test(pois_data)

    Shapiro-Wilk normality test

data:  pois_data
W = 0.9038, p-value = 0.01041

Problemas de Shapiro-Wilk

Como cualquier prueba estadística, Shapiro-Wilk sólo nos dice qué tan probable es que nuestra muestra tenga una distribución normal. Pero habrá ocasiones en que la vida arroje muestras que parecen normales y que Shapiro-Wilk falla en rechazar h0. Esto generalmente ocurre con un tamaño de muestra pequeña.

En el caso de abajo, los datos vienen de una distribución poisson y Shapiro-Wilk NO rechaza h0.

set.seed(100)

n = 15 # número de datos
lambda = 5 # lambda es como la media para distribuciones poisson
pois_data = rpois(n, lambda)
shapiro.test(pois_data)

    Shapiro-Wilk normality test

data:  pois_data
W = 0.95387, p-value = 0.5873

Genera una muestra con distribución poisson. Ve aumentando el tamaño de la n y observa cómo va cambiando el valor de p cuando usas Shapiro-Wilk.

set.seed(100) # No cambies el seed para que la única variable sea el tamaño de la muestra.
n = 
lambda = 5 # no cambies la lambda para que la única variable sea el tamaño de la muestra


pois_data = rpois(n, lambda)

Reto: haz el ejercicio de arriba pero usa un loop. Luego haz una gráfica de cómo va cambiando la p en función de n.

Otro problema es el tamaño de la muestra. Las pruebas de normalidad comparan datos contra distribuciones teóricas. Pero en la realidad nada se distribuye perfectamente normal. Si tenemos una muestra muy grande, las pruebas estadísticas tienen mayor poder estadístico y pueden detectar pequeñas desviaciones de las distribuciones teóricas. Ésta es la razón por la que no se recomienda que la prueba Shapiro-Wilk se utilice con más de 5 mil datos. De hecho, la función de R para Shapiro-Wilk lanza un error si la usas con más de 5 mil datos.

Genera una muestra aleatoria de una distribución normal con menos de 5 mil datos y usa la prueba de Shapiro-Wilk

Ahora, trata de usar Shapiro-Wilk con una muestra de más de 5 mil datos

Kolmogorov-Smirnov n > 5 mil

La prueba de Kolmogorov-Smirnov se puede usar para un tamaño de muestra grande.

Las desventajas son que:

En el ejemplo de abajo se genrea una muestra con tamaño mayor a 5 mil y se usa Kolmogorov-Smirnov (ks.test) para probar que tenga una distribución normal. Observa que el segundo argumento de ks.test es pnorm, que en R hace referencia a la distribución de probabilidad acumulativa de una curva normal, y le siguen la media y la desviación estándar de los datos.


n = 5001 # tamaño de la muestra
m = 5 # media
s = 2 # desv. estándar
set.seed(5)
datos_masivos = rnorm(n, m , s)
ks.test(datos_masivos, "pnorm", mean(datos_masivos), sd(datos_masivos))

    One-sample Kolmogorov-Smirnov test

data:  datos_masivos
D = 0.01008, p-value = 0.6898
alternative hypothesis: two-sided

Los resultados que arroja ks.test son parecidos a los de Shapiro-Wilk, sólo que en lugar de obtener el estadístico W, se obtiene el estadístico D. La p-value se interpreta igual (rechaza h0 con p-value < 0.05 ).

Kolmogorov-Smirnov se puede usar para probar si los datos vienen de otras distribuciones además de la normal. En el código de abajo se generan datos a partir de una distribución gamma y se usa Kolomogorov-Smirnov para probar la h0 de que los datos tengan una distribución gamma. Observa que se tiene que específicar la distribución acumulativa y los parámetros que le corresponden a la distribución de comparación.

n = 5001
shape = 10
scale = 10

set.seed(10)
gamma_data = rgamma(n, shape, scale)

ks.test(gamma_data, "pgamma", shape, scale)

    One-sample Kolmogorov-Smirnov test

data:  gamma_data
D = 0.013391, p-value = 0.3312
alternative hypothesis: two-sided

Ejercicio: interpreta los resultados que arrojó Kolmogorov-Smirnov en el ejemplo anterior

Ejercicio: Usa ks.test para probar si “gama_data” tiene una distribución normal.

Pruebas de normalidad gráficas: QQ plot

En la sección anterior vimos que en ocasiones las pruebas de normalidad fallan en detectar que los datos no tienen una distribución normal. También vimos que si aumentamos la cantidad de datos aumenta su poder estadístico y son capaces de cometer errores tipo 1. Esto último se está volviendo un problema porque cada vez tenemos mayor capacidad de procesar y almacenar datos masivos. Debido a esto es recomendable recurrir a los QQ-plots, o plots cuantil-cuantil.

Los QQ-plots es una herramienta visual para ver si los datos tiene nuna distribución normal o no. De manera muy simplificada, los qq-plots toman cuantiles de los datos y los grafican en función de los cuantiles de una distribución normal teórica. Si los datos siguen una distribución normal entonces los datos siguen una línea recta.

Los ejemplos de abajo toman los datos antes generados para ver en los QQ-plots cómo se ajustan a una distribución normal. Observa cómo los datos generados aleatoriamente de una distribución normal tienden a seguir la recta. Sin embargo, los datos poisson no y tienen un patrón de escalera característico de una distribución discreta.

Datos normales

qqnorm(norm_data)
qqline(norm_data, col="red", lw=2)

Datos poisson

qqnorm(pois_data)
qqline(pois_data, col="red", lw=2)

Haz un qqplot para “gamma_data”

QQ-plots para distribuciones diferentes a la normal.

La ventaja de los qq-plots es que se pueden usar para ver si los datos siguen otras distribuciones a parte de la normal.

La interpretación es la misma. Si los datos siguen la línea recta, los datos vienen de esa distribución.

Las librerías car o qualityTools tienen funciones qqPlot para poder hacer gráficas cuantil-cuantil con otras distribuciones

En el ejemplo de abajo, se usaron los mismos datos generados a partir de una distribución Poisson (pois_data) para hacer un qq-plot contra una distribución poisson y una gamma. Los qqplots de abajo añaden unas líneas punteadas, que es el rango de error que tienen los datos para alejarse de la línea recta.

En este caso es necesario cargar la librería car o qualityTools.

library(car)
Loading required package: carData
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
qqPlot(pois_data, "pois", lambda = 5)
[1] 4 3

qqPlot(gamma_data, "gamma", shape = 10, scale = 10)
[1]  211 1557

LS0tCnRpdGxlOiAiUHJ1ZWJhcyBkZSBOb3JtYWxpZGFkIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBSZXN1bWVuCgpTaSBxdWllcmVzIHNhYmVyIHNpIHR1cyBkYXRvcyB0aWVuZW4gdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsIHVzYSBtw6l0b2RvcyBncsOhZmljb3MgeSBhcGxpY2EgcHJ1ZWJhcyBlc3RhZMOtc3RpY2FzIGRlIG5vcm1hbGlkYWQ6CgoqKk3DqXRvZG9zIGdyw6FmaWNvcyoqCiAgKiBIaXN0b2dyYW1hczogcGFyYSB2ZXIgbGEgZm9ybWEgZGUgbGEgZGlzdHJpYnVjacOzbgpgYGB7cn0KZGF0b3MgPSBybm9ybSgxMDApCmhpc3QoZGF0b3MpCmBgYAogICogUVEtcGxvdDogcGFyYSB2ZXIgY8OzbW8gbG9zIGN1YW50aWxlcyBkZSBsb3MgZGF0b3Mgc2UgYWp1c3RhbiBhIGxvcyBjdWFudGlsZXMgZGUgbGEgZGlzdHJpYnVjacOzbiBub3JtYWwgdGXDs3JpY2EuIFNpIGxvcyBkYXRvcyB0aWVuZW4gdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLCBsb3MgcHVudG9zIHRpZW5lbiBxdWUgc2VndWlyIGxhIGzDrW5lYSByZWN0YS4KYGBge3J9CnFxbm9ybShkYXRvcykKcXFsaW5lKGRhdG9zLCBjb2w9InJlZCIsIGx3PTIpCmBgYAogIAoqKlBydWViYXMgZGUgbm9ybWFsaWRhZCoqCiAgKiBIaXDDs3Rlc2lzIG51bGEgPSBkYXRvcyB0aWVuZW4gdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsCiAgKiBDb24gKnAtdmFsdWUgPCAwLjA1KiBzZSByZWNoYXphIGhpcMOzdGVzaSBudWxhCiAgKiBTaGFwaXJvLVdpbGsKICAgICogcGFyYSB0YW1hw7FvIGRlIG11ZXN0cmEgbWVub3IgYSA1IG1pbApgYGB7cn0Kc2hhcGlyby50ZXN0KGRhdG9zKQpgYGAKICAqIEtvbG1vZ29yb3YtU21pcm5vdjogcGFyYSB0YW1hw7FvIGRlIG11ZXN0cmEgZ3JhbmRlCiAgICAqIHNlIHRpZW5lIHF1ZSBlc3BlY2lmaWNhciBsYSBkaXN0cmlidWNpw7NuCiAgICAqIFNpcnZlIHBhcmEgY29tcGFyYXIgY29udHJhIGRpc3RyaWJ1Y2lvbmVzIGNvbnRpbnVhcwpgYGB7cn0KZGF0b3MgPSBybm9ybSgxMDAwMCkKbWVkaWFfZGVfZGF0b3MgPSBtZWFuKGRhdG9zKQpkZXN2X2VzdGFuZGFyX2RlX2RhdG9zID0gc2QoZGF0b3MpCmtzLnRlc3QoZGF0b3MsICJwbm9ybSIsIG1lZGlhX2RlX2RhdG9zLCBkZXN2X2VzdGFuZGFyX2RlX2RhdG9zKQpgYGAKICAKCiMjIEFudGVzIGRlIGVtcGV6YXIKCkFsZ3Vub3MgZWplbXBsb3MgbmVjZXNpdGFuIGxhcyBzaWd1aWVudGVzIGxpYnJlcsOtYXMuIAotIGNhcgotIG5vcnRlc3QKLSBxdWFsaXR5VG9vbHMKClNpIGHDum4gbm8gbGFzIHRpZW5lcywgaW5zdMOhbGFsYXMuCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygiY2FyIikKaW5zdGFsbC5wYWNrYWdlcygicXVhbGl0eVRvb2xzIikKYGBgCgpBbnRlcyBkZSBhbmFsaXphciBsb3MgZGF0b3MgY29uICoqcHJ1ZWJhcyBlc3RhZMOtc3RpY2FzIHBhcmFtw6l0cmljYXMqKiBxdWUgYXN1bWVuIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCwgY8OzbW8gcHJ1ZWJhcyBUIG8gQU5PVkFzLCBkZWJlbW9zICoqYXNlZ3VyYXJub3MgcXVlIG51ZXN0cm9zIGRhdG9zIHRpZW5lbiB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwqKi4gUGFyYSBlbGxvIHBvZGVtb3MgdXNhciB1bmEgKipjb21iaW5hY2nDs24gZW50cmUgcHJ1ZWJhcyBlc3RhZMOtc3RpY2FzIGRlIG5vcm1hbGlkYWQgeSBtw6l0b2RvcyBncsOhZmljb3MgY29tbyBoaXN0b2dyYW1hcyB5IHFxLXBsb3RzKiouCgojIyBQcnVlYmFzIGVzdGFkw61zdGljYXMgZGUgbm9ybWFsaWRhZAoKTGFzIHBydWViYXMgZGUgbm9ybWFsaWRhZCBzb24gKipwcnVlYmFzIGVzdGFkw61zdGljYXMqKiBxdWUgcG9uZW4gYSBwcnVlYmEgbGEgKipoaXDDs3Rlc2lzIG51bGEgKGgwKSBkZSBxdWUgbG9zIGRhdG9zIHRpZW5lbiB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwqKi4gRXN0byBxdWllcmUgZGVjaXIgcXVlIHNpIGxhIHBydWViYSBhcnJvamEgdW4gKip2YWxvciBkZSBwIDwgMC4wNSoqIChvIGVsIHZhbG9yIGRlIHNpZ25pZmljYW5jaWEgcXVlIGRlY2lkYXMpLCBzZSByYWNoYXphIGxhIGgwIHkgY29uY2x1aW1vcyBxdWUgKipsb3MgZGF0b3MgTk8gZXN0w6FuIGRpc3RyaWJ1aWRvcyBkZSBtYW5lcmEgbm9ybWFsKiouCgpBbGd1bmFzIHBydWViYXMgc29uOgoKKiBTaGFwaXJvLVdpbGs6IHVzYXIgc8OzbG8gcGFyYSB1biB0YW1hw7FvIGRlIG11ZXN0cmEgbWVub3IgYSA1IG1pbAoqIEtvbG1vZ29yb3YtU21pcm5vdjogc2UgcHVlZGUgdXNhciBwYXJhIHRhbWHDsW9zIGRlIG11ZXN0cmFzIGdyYW5kZXMgeSBwYXJhIG90cmFzIGRpc3RyaWJ1Y2lvbmVzIGFwYXJ0ZSBkZSBsYSBub3JtYWwuCiogQW5kZXJzb24tRGFybGluZzogTW9kaWZpY2FjacOzbiBkZSBLb2xtb2dvcm92LVNtaXJub3YgcGFyYSB0YW1hw7FvcyBkZSBtdWVzdHJhcyBwZXF1ZcOxYXMuCgpQb3IgYWhvcmEsIHPDs2xvIHNlIGhhYmxhcsOhIGRlIFNoYXBpcm8tV2lsayB5IEtvbG1vZ29yb3YtU21pcm5vdi4KCiMjIFNoYXBpcm8tV2lsawoKRW4gZWwgZWplbXBsbyBkZSBhYmFqbywgbGEgZnVuY2nDs24gKnJub3JtKiBzYWNhICpuKiBkYXRvcyBhbGVhdG9yaW9zIGRlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCBjb24gKm1lZGlhID0gbSogeSAqZGVzdmlhY2nDs24gZXN0w6FuZGFyID0gcyouIERlc3B1w6lzLCBhIGVzb3MgZGF0b3Mgc2UgbGVzIGFwbGljYSBsYSBwcnVlYmEgKlNoYXBpcm8tV2lsayogcGFyYSBwcm9iYXIgc2kgdGllbmVuIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCBvIG5vLgoKKipSZWN1ZXJkYToqKiBHZW5lcmFsbWVudGUgbm8gc2FiZW1vcyBsYSBkaXN0cmlidWNpw7NuIGRlIGxvcyBkYXRvcyAqYSBwcmlvcmkqLiBFbiBlc3RlIGVqZW1wbG8gc2FiZW1vcyBxdWUgbG9zIGRhdG9zIHZpZW5lbiBkZSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwgZW50b25jZXMgZXNwZXJhbW9zIHF1ZSBsYSBwcnVlYmEgU2hhcGlyby1XaWxrIE5PIHJlY2hhY2UgaDAuIEVzIGRlY2lyLCAqcC12YWx1ZSogdGllbmUgcXVlIHNlciBtYXlvciBhIDAuMDUuCgpgYGB7cn0KCm4gPSAzMCAjIG51bSBkZSBkYXRvcwptID0gNSAgIyBtZWRpYQpzID0gMiAgIyBkZXN2IGVzdMOhbmRhcgoKIyBEYXRvcyBhbGVhdG9yaW9zIGRlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbApzZXQuc2VlZCgxMjMpICMgcGFyYSByZXByb2R1Y2liaWxpZGFkCm5vcm1fZGF0YSA9IHJub3JtKG4sIG0sIHMpCgojIHBydWViYSBkZSBub3JtYWxpZGFkOyBoMDogbm9ybV9kYXRhIGVzdMOhIGRpc3RyaWJ1aWRhIGRlIG1hbmVyYSBub3JtYWwKc2hhcGlyby50ZXN0KG5vcm1fZGF0YSkgCgpgYGAKClNoYXBpcm8tV2lsayBhcnJvamEgZG9zIHZhbG9yZXM6CgotICpXKiBlcyBlbCB2YWxvciBlc3RhZMOtc3RpY28gcXVlIGFycm9qYSBsYSBwcnVlYmEgZGVzcHXDqXMgZGUgYXBsaWNhciBsYSBmw7NybXVsYSBkZSBTaGFwaXJvLVdpbGsgYSBsb3MgZGF0b3MuCi0gKnAtdmFsdWUqIHRvbWEgdmFsb3JlcyBlbnRyZSAwIHkgMS4gU2lydmUgcGFyYSBjdWFudGlmaWNhciBxdcOpIHRhbiBjb25maWFkb3MgZXN0YW1vcyBkZSBxdWUgbGEgZGlzdHJpYnVjacOzbiBkZSBsb3MgZGF0b3MgdGllbmVuIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbC4gTWllbnRyYXMgbcOhcyBjZXJjYW5vIGVzdMOpIGRlIDEsIGxvcyBkYXRvcyBzZSBhc2VtZWphbiBtw6FzIGEgbGEgbm9ybWFsLiBVbiBwLXZhbHVlIDwgLjA1IG5vcyBzaXJ2ZSBwYXJhIGRlZmluaXIgZWwgdW1icmFsIGVuIGVsIHF1ZSBwb2RlbW9zIGRlY2lyIHF1ZSBsb3MgZGF0b3Mgc29uIHN1ZmljaWVudGVtZW50ZSBkaWZlcmVudGVzIGRlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCBwYXJhIHJlY2hhemFyIGgwLgoKTG9zIHJlc3VsdGFkb3MgZGUgc2hhcGlydG8udGVzdCBzZSBwdWVkZW4gZ3VhcmRhciBlbiB1bmEgdmFyaWFibGUgcGFyYSBhY2NlZGVyIGEgZWxsb3MgcG9zdGVyaW9ybWVudGUuCmBgYHtyfQpzaGFwaXJvID0gc2hhcGlyby50ZXN0KG5vcm1fZGF0YSkKc2hhcGlybyRzdGF0aXN0aWMgIyBzaGFwaXJvJHN0YXQgdGFtYmnDqW4gZnVuY2lvbmEKc2hhcGlybyRwLnZhbHVlCmBgYAoKCipSZXRvOiBzaSBzYWNhbW9zIDEwMDAgbXVlc3RyYXMgZGlmZXJlbnRlcyBjb24gbGFzIG1pc21hcyBjYXJhY3RlcsOtc3RpY2FzIHF1ZSBsYSBhbnRlcmlvciAodGFtYcOxbywgbWVkaWEsIGRlc3YuIGVzdMOhbmRhciksIHPDs2xvIGVsIDUlIGRlIGxhcyB2ZWNlcyBzaGFwaXJvLXdpbGsgcmVjaGF6YXLDoSBoMC4gSGF6IHVuIGPDs2RpZ28gcGFyYSBjb21wcm9iYXIgZXNvLioKYGBge3J9Cm4gPSAKbSA9IApzID0gCiAgCnJlcGV0aWNpb25lcyA9IDEwMDAKCmBgYAoKQWhvcmEgcHJvYmVtb3MgYSAqU2hhcGlyby1XaWxrKiBjb24gZGF0b3MgcXVlIHNhYmVtb3MgcXVlIG5vIHZpZW5lbiBkZSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuCgpFbiBlbCBlamVtcGxvIGRlIGFiYWpvIGdlbmVyYW1vcyBkYXRvcyBhbGVhdG9yaW9zIGRlIHVuYSBkaXN0cmlidWNpw7NuIHBvaXNzb24gY29uIHVuYSAqbGFtYmRhID0gbCouIEVuIGxhcyBkaXN0cmlidWNpb25lcyB0aXBvIHBvaXNzb24sICpsYW1iZGEqIGVzIGNvbW8gc2kgZnVlcmEgbGEgbWVkaWEgeSBlcyBlbCDDum5pY28gcGFyw6FtZXRybyBxdWUgc2UgbmVjZXNpdGEuCgpDb21vIHNhYmVtb3MgcXVlICoqbG9zIGRhdG9zIE5PIHZpZW5lbiBkZSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwgZXNwZXJhbW9zIHF1ZSBzaGFwaXJvLXdpbGsgcmVjaGFjZSBsYSBoMCoqLgoKYGBge3J9Cm4gPSAzMCAjIG7Dum0gZGUgZGF0b3MKbCA9IDUgICMgbGFtYmRhCgojIGRhdG9zIGFsZWF0b3Jpb3MgZGUgdW5hIGRpc3RyaWJ1Y2nDs24gcG9pc3NvbgpzZXQuc2VlZCg1MCkKcG9pc19kYXRhID0gcnBvaXMobiwgbCkKCnNoYXBpcm8udGVzdChwb2lzX2RhdGEpCmBgYAoKIyMgUHJvYmxlbWFzIGRlIFNoYXBpcm8tV2lsawoKQ29tbyBjdWFscXVpZXIgcHJ1ZWJhIGVzdGFkw61zdGljYSwgU2hhcGlyby1XaWxrIHPDs2xvIG5vcyBkaWNlIHF1w6kgdGFuIHByb2JhYmxlIGVzIHF1ZSBudWVzdHJhIG11ZXN0cmEgdGVuZ2EgdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLiBQZXJvIGhhYnLDoSBvY2FzaW9uZXMgZW4gcXVlIGxhIHZpZGEgYXJyb2plIG11ZXN0cmFzIHF1ZSBwYXJlY2VuIG5vcm1hbGVzIHkgcXVlICoqU2hhcGlyby1XaWxrIGZhbGxhIGVuIHJlY2hhemFyIGgwLiBFc3RvIGdlbmVyYWxtZW50ZSBvY3VycmUgY29uIHVuIHRhbWHDsW8gZGUgbXVlc3RyYSBwZXF1ZcOxYSoqLgoKRW4gZWwgY2FzbyBkZSBhYmFqbywgbG9zIGRhdG9zIHZpZW5lbiBkZSB1bmEgZGlzdHJpYnVjacOzbiBwb2lzc29uIHkgU2hhcGlyby1XaWxrICoqTk8gcmVjaGF6YSBoMCoqLgoKYGBge3J9CnNldC5zZWVkKDEwMCkKCm4gPSAxNSAjIG7Dum1lcm8gZGUgZGF0b3MKbGFtYmRhID0gNSAjIGxhbWJkYSBlcyBjb21vIGxhIG1lZGlhIHBhcmEgZGlzdHJpYnVjaW9uZXMgcG9pc3Nvbgpwb2lzX2RhdGEgPSBycG9pcyhuLCBsYW1iZGEpCnNoYXBpcm8udGVzdChwb2lzX2RhdGEpCmBgYAoKKkdlbmVyYSB1bmEgbXVlc3RyYSBjb24gZGlzdHJpYnVjacOzbiBwb2lzc29uLiBWZSBhdW1lbnRhbmRvIGVsIHRhbWHDsW8gZGUgbGEgbiB5IG9ic2VydmEgY8OzbW8gdmEgY2FtYmlhbmRvIGVsIHZhbG9yIGRlIHAgY3VhbmRvIHVzYXMgU2hhcGlyby1XaWxrLioKYGBge3J9CnNldC5zZWVkKDEwMCkgIyBObyBjYW1iaWVzIGVsIHNlZWQgcGFyYSBxdWUgbGEgw7puaWNhIHZhcmlhYmxlIHNlYSBlbCB0YW1hw7FvIGRlIGxhIG11ZXN0cmEuCm4gPSAKbGFtYmRhID0gNSAjIG5vIGNhbWJpZXMgbGEgbGFtYmRhIHBhcmEgcXVlIGxhIMO6bmljYSB2YXJpYWJsZSBzZWEgZWwgdGFtYcOxbyBkZSBsYSBtdWVzdHJhCgoKcG9pc19kYXRhID0gcnBvaXMobiwgbGFtYmRhKQoKYGBgCgoqUmV0bzogaGF6IGVsIGVqZXJjaWNpbyBkZSBhcnJpYmEgcGVybyB1c2EgdW4gbG9vcC4gTHVlZ28gaGF6IHVuYSBncsOhZmljYSBkZSBjw7NtbyB2YSBjYW1iaWFuZG8gbGEgcCBlbiBmdW5jacOzbiBkZSBuLioKCmBgYHtyfQoKYGBgCgpPdHJvICoqcHJvYmxlbWEgZXMgZWwgdGFtYcOxbyBkZSBsYSBtdWVzdHJhKiouIExhcyBwcnVlYmFzIGRlIG5vcm1hbGlkYWQgY29tcGFyYW4gZGF0b3MgY29udHJhIGRpc3RyaWJ1Y2lvbmVzIHRlw7NyaWNhcy4gUGVybyBlbiBsYSByZWFsaWRhZCAqKm5hZGEgc2UgZGlzdHJpYnV5ZSBwZXJmZWN0YW1lbnRlIG5vcm1hbCoqLiBTaSB0ZW5lbW9zIHVuYSAqKm11ZXN0cmEgbXV5IGdyYW5kZSoqLCBsYXMgcHJ1ZWJhcyBlc3RhZMOtc3RpY2FzIHRpZW5lbiAqKm1heW9yIHBvZGVyIGVzdGFkw61zdGljbyoqIHkgcHVlZGVuIGRldGVjdGFyIHBlcXVlw7FhcyBkZXN2aWFjaW9uZXMgZGUgbGFzIGRpc3RyaWJ1Y2lvbmVzIHRlw7NyaWNhcy4gw4lzdGEgZXMgbGEgcmF6w7NuIHBvciBsYSBxdWUgKipubyBzZSByZWNvbWllbmRhIHF1ZSBsYSBwcnVlYmEgU2hhcGlyby1XaWxrIHNlIHV0aWxpY2UgY29uIG3DoXMgZGUgNSBtaWwgZGF0b3MqKi4gRGUgaGVjaG8sIGxhIGZ1bmNpw7NuIGRlIFIgcGFyYSBTaGFwaXJvLVdpbGsgbGFuemEgdW4gZXJyb3Igc2kgbGEgdXNhcyBjb24gbcOhcyBkZSA1IG1pbCBkYXRvcy4KCipHZW5lcmEgdW5hIG11ZXN0cmEgYWxlYXRvcmlhIGRlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCBjb24gbWVub3MgZGUgNSBtaWwgZGF0b3MgeSB1c2EgbGEgcHJ1ZWJhIGRlIFNoYXBpcm8tV2lsayoKYGBge3J9CgpgYGAKCipBaG9yYSwgdHJhdGEgZGUgdXNhciBTaGFwaXJvLVdpbGsgY29uIHVuYSBtdWVzdHJhIGRlIG3DoXMgZGUgNSBtaWwgZGF0b3MqCmBgYHtyfQoKYGBgCgojIyBLb2xtb2dvcm92LVNtaXJub3YgKm4gPiA1IG1pbCoKCkxhIHBydWViYSBkZSBLb2xtb2dvcm92LVNtaXJub3Ygc2UgcHVlZGUgdXNhciBwYXJhIHVuIHRhbWHDsW8gZGUgbXVlc3RyYSBncmFuZGUuIAoKTGFzIGRlc3ZlbnRhamFzIHNvbiBxdWU6CgogLSBzw7NsbyBzaXJ2ZSBwYXJhIGRpc3RyaWJ1Y2lvbmVzIGNvbnTDrW51YXMKIC0gaGF5IHF1ZSBlc3BlY8OtZmljYXIgY29udHJhIHF1w6kgZGlzdHJpYnVjacOzbiBxdWVyZW1vcyBjb21wYXJhcmFyCgpFbiBlbCBlamVtcGxvIGRlIGFiYWpvIHNlIGdlbnJlYSB1bmEgbXVlc3RyYSBjb24gdGFtYcOxbyBtYXlvciBhIDUgbWlsIHkgc2UgdXNhIEtvbG1vZ29yb3YtU21pcm5vdiAoKmtzLnRlc3QqKSBwYXJhIHByb2JhciBxdWUgdGVuZ2EgdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLiBPYnNlcnZhIHF1ZSBlbCBzZWd1bmRvIGFyZ3VtZW50byBkZSAqa3MudGVzdCogZXMgKnBub3JtKiwgcXVlIGVuIFIgaGFjZSByZWZlcmVuY2lhIGEgbGEgKmRpc3RyaWJ1Y2nDs24gZGUgcHJvYmFiaWxpZGFkIGFjdW11bGF0aXZhIGRlIHVuYSBjdXJ2YSBub3JtYWwqLCB5IGxlIHNpZ3VlbiBsYSBtZWRpYSB5IGxhIGRlc3ZpYWNpw7NuIGVzdMOhbmRhciBkZSBsb3MgZGF0b3MuCmBgYHtyfQoKbiA9IDUwMDEgIyB0YW1hw7FvIGRlIGxhIG11ZXN0cmEKbSA9IDUgIyBtZWRpYQpzID0gMiAjIGRlc3YuIGVzdMOhbmRhcgpzZXQuc2VlZCg1KQpkYXRvc19tYXNpdm9zID0gcm5vcm0obiwgbSAsIHMpCmtzLnRlc3QoZGF0b3NfbWFzaXZvcywgInBub3JtIiwgbWVhbihkYXRvc19tYXNpdm9zKSwgc2QoZGF0b3NfbWFzaXZvcykpCgpgYGAKCkxvcyByZXN1bHRhZG9zIHF1ZSBhcnJvamEga3MudGVzdCBzb24gcGFyZWNpZG9zIGEgbG9zIGRlIFNoYXBpcm8tV2lsaywgc8OzbG8gcXVlIGVuIGx1Z2FyIGRlIG9idGVuZXIgZWwgZXN0YWTDrXN0aWNvICpXKiwgc2Ugb2J0aWVuZSBlbCBlc3RhZMOtc3RpY28gKkQqLiBMYSAqcC12YWx1ZSogc2UgaW50ZXJwcmV0YSBpZ3VhbCAocmVjaGF6YSBoMCBjb24gcC12YWx1ZSA8IDAuMDUgKS4KCktvbG1vZ29yb3YtU21pcm5vdiBzZSBwdWVkZSB1c2FyIHBhcmEgcHJvYmFyIHNpIGxvcyBkYXRvcyB2aWVuZW4gZGUgb3RyYXMgZGlzdHJpYnVjaW9uZXMgYWRlbcOhcyBkZSBsYSBub3JtYWwuIEVuIGVsIGPDs2RpZ28gZGUgYWJham8gc2UgZ2VuZXJhbiAqKmRhdG9zIGEgcGFydGlyIGRlIHVuYSBkaXN0cmlidWNpw7NuIGdhbW1hKiogeSBzZSB1c2EgS29sb21vZ29yb3YtU21pcm5vdiBwYXJhIHByb2JhciBsYSBoMCBkZSBxdWUgbG9zIGRhdG9zIHRlbmdhbiB1bmEgZGlzdHJpYnVjacOzbiBnYW1tYS4gT2JzZXJ2YSBxdWUgc2UgdGllbmUgcXVlICoqZXNwZWPDrWZpY2FyIGxhIGRpc3RyaWJ1Y2nDs24gYWN1bXVsYXRpdmEgeSBsb3MgcGFyw6FtZXRyb3MgcXVlIGxlIGNvcnJlc3BvbmRlbiBhIGxhIGRpc3RyaWJ1Y2nDs24gZGUgY29tcGFyYWNpw7NuKiouCgpgYGB7cn0KbiA9IDUwMDEKc2hhcGUgPSAxMApzY2FsZSA9IDEwCgpzZXQuc2VlZCgxMCkKZ2FtbWFfZGF0YSA9IHJnYW1tYShuLCBzaGFwZSwgc2NhbGUpCgprcy50ZXN0KGdhbW1hX2RhdGEsICJwZ2FtbWEiLCBzaGFwZSwgc2NhbGUpCmBgYAoKKkVqZXJjaWNpbzogaW50ZXJwcmV0YSBsb3MgcmVzdWx0YWRvcyBxdWUgYXJyb2rDsyBLb2xtb2dvcm92LVNtaXJub3YgZW4gZWwgZWplbXBsbyBhbnRlcmlvcioKCgoqRWplcmNpY2lvOiBVc2Ega3MudGVzdCBwYXJhIHByb2JhciBzaSAiZ2FtYV9kYXRhIiB0aWVuZSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuKgpgYGB7cn0KCmBgYAoKCiMjIFBydWViYXMgZGUgbm9ybWFsaWRhZCBncsOhZmljYXM6IFFRIHBsb3QKCkVuIGxhIHNlY2Npw7NuIGFudGVyaW9yIHZpbW9zIHF1ZSBlbiBvY2FzaW9uZXMgbGFzIHBydWViYXMgZGUgbm9ybWFsaWRhZCBmYWxsYW4gZW4gZGV0ZWN0YXIgcXVlIGxvcyBkYXRvcyBubyB0aWVuZW4gdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLiBUYW1iacOpbiB2aW1vcyBxdWUgc2kgYXVtZW50YW1vcyBsYSBjYW50aWRhZCBkZSBkYXRvcyBhdW1lbnRhIHN1IHBvZGVyIGVzdGFkw61zdGljbyB5IHNvbiBjYXBhY2VzIGRlIGNvbWV0ZXIgZXJyb3JlcyB0aXBvIDEuIEVzdG8gw7psdGltbyBzZSBlc3TDoSB2b2x2aWVuZG8gdW4gcHJvYmxlbWEgcG9ycXVlIGNhZGEgdmV6IHRlbmVtb3MgbWF5b3IgY2FwYWNpZGFkIGRlIHByb2Nlc2FyIHkgYWxtYWNlbmFyIGRhdG9zIG1hc2l2b3MuIERlYmlkbyBhIGVzdG8gZXMgcmVjb21lbmRhYmxlIHJlY3VycmlyIGEgbG9zIFFRLXBsb3RzLCBvIHBsb3RzIGN1YW50aWwtY3VhbnRpbC4KCkxvcyBRUS1wbG90cyBlcyB1bmEgaGVycmFtaWVudGEgdmlzdWFsIHBhcmEgdmVyIHNpIGxvcyBkYXRvcyB0aWVuZSBudW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsIG8gbm8uIERlIG1hbmVyYSBtdXkgc2ltcGxpZmljYWRhLCBsb3MgcXEtcGxvdHMgdG9tYW4gY3VhbnRpbGVzIGRlIGxvcyBkYXRvcyB5IGxvcyBncmFmaWNhbiBlbiBmdW5jacOzbiBkZSBsb3MgY3VhbnRpbGVzIGRlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCB0ZcOzcmljYS4gU2kgbG9zIGRhdG9zIHNpZ3VlbiB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwgZW50b25jZXMgbG9zIGRhdG9zIHNpZ3VlbiB1bmEgbMOtbmVhIHJlY3RhLgoKTG9zIGVqZW1wbG9zIGRlIGFiYWpvIHRvbWFuIGxvcyBkYXRvcyBhbnRlcyBnZW5lcmFkb3MgcGFyYSB2ZXIgZW4gbG9zIFFRLXBsb3RzIGPDs21vIHNlIGFqdXN0YW4gYSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuIE9ic2VydmEgY8OzbW8gbG9zIGRhdG9zIGdlbmVyYWRvcyBhbGVhdG9yaWFtZW50ZSBkZSB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwgdGllbmRlbiBhIHNlZ3VpciBsYSByZWN0YS4gU2luIGVtYmFyZ28sIGxvcyBkYXRvcyBwb2lzc29uIG5vIHkgdGllbmVuIHVuIHBhdHLDs24gZGUgKmVzY2FsZXJhKiBjYXJhY3RlcsOtc3RpY28gZGUgdW5hIGRpc3RyaWJ1Y2nDs24gZGlzY3JldGEuCgojIyMgRGF0b3Mgbm9ybWFsZXMKYGBge3J9CnFxbm9ybShub3JtX2RhdGEpCnFxbGluZShub3JtX2RhdGEsIGNvbD0icmVkIiwgbHc9MikKYGBgCgojIyMgRGF0b3MgcG9pc3NvbgpgYGB7cn0KcXFub3JtKHBvaXNfZGF0YSkKcXFsaW5lKHBvaXNfZGF0YSwgY29sPSJyZWQiLCBsdz0yKQpgYGAKCipIYXogdW4gcXFwbG90IHBhcmEgImdhbW1hX2RhdGEiKgpgYGB7cn0KCmBgYAoKIyMgUVEtcGxvdHMgcGFyYSBkaXN0cmlidWNpb25lcyBkaWZlcmVudGVzIGEgbGEgbm9ybWFsLgoKTGEgdmVudGFqYSBkZSBsb3MgcXEtcGxvdHMgZXMgcXVlIHNlIHB1ZWRlbiB1c2FyIHBhcmEgdmVyIHNpIGxvcyBkYXRvcyBzaWd1ZW4gb3RyYXMgZGlzdHJpYnVjaW9uZXMgYSBwYXJ0ZSBkZSBsYSBub3JtYWwuIAoKTGEgaW50ZXJwcmV0YWNpw7NuIGVzIGxhIG1pc21hLiBTaSBsb3MgZGF0b3Mgc2lndWVuIGxhIGzDrW5lYSByZWN0YSwgbG9zIGRhdG9zIHZpZW5lbiBkZSBlc2EgZGlzdHJpYnVjacOzbi4KCkxhcyBsaWJyZXLDrWFzICpjYXIqIG8gKnF1YWxpdHlUb29scyogdGllbmVuIGZ1bmNpb25lcyAqcXFQbG90KiBwYXJhIHBvZGVyIGhhY2VyIGdyw6FmaWNhcyBjdWFudGlsLWN1YW50aWwgY29uIG90cmFzIGRpc3RyaWJ1Y2lvbmVzCgpFbiBlbCBlamVtcGxvIGRlIGFiYWpvLCBzZSB1c2Fyb24gbG9zIG1pc21vcyBkYXRvcyBnZW5lcmFkb3MgYSBwYXJ0aXIgZGUgdW5hIGRpc3RyaWJ1Y2nDs24gKlBvaXNzb24qIChwb2lzX2RhdGEpIHBhcmEgaGFjZXIgdW4gcXEtcGxvdCBjb250cmEgdW5hIGRpc3RyaWJ1Y2nDs24gcG9pc3NvbiB5IHVuYSBnYW1tYS4gTG9zIHFxcGxvdHMgZGUgYWJham8gYcOxYWRlbiB1bmFzIGzDrW5lYXMgcHVudGVhZGFzLCBxdWUgZXMgZWwgcmFuZ28gZGUgZXJyb3IgcXVlIHRpZW5lbiBsb3MgZGF0b3MgcGFyYSBhbGVqYXJzZSBkZSBsYSBsw61uZWEgcmVjdGEuCgpFbiBlc3RlIGNhc28gZXMgbmVjZXNhcmlvIGNhcmdhciBsYSBsaWJyZXLDrWEgKmNhciogbyAqcXVhbGl0eVRvb2xzKi4KCmBgYHtyfQpsaWJyYXJ5KGNhcikKcXFQbG90KHBvaXNfZGF0YSwgInBvaXMiLCBsYW1iZGEgPSA1KQpgYGAKCmBgYHtyfQpxcVBsb3QoZ2FtbWFfZGF0YSwgImdhbW1hIiwgc2hhcGUgPSAxMCwgc2NhbGUgPSAxMCkKYGBgCgo=