Este documento ofrece algunos de los conceptos más fundamentales de la R, otorgándo una experiencia práctica tanto en la exploración y el resumen de datos.

Cada parte esta posee una descripción de la tarea y los comandos R utilizados.

Carga de datos

Utilizaremos el set de datos de “esoph”, que esta relacionado con un estudio de caso y control realizado en Francia que busca asociar el cáncer esofágico con el consumo de alcohol. Este set de datos forma parte estándar de R, por tanto se carga directamente. En caso de cargar desde un dirección de internet. utilice dataset_url <- “http://direccion_url/esoph_cancer.txt” download.file (dataset_url, “esoph_cancer.txt”) y establezca si directorio de trabajo

#dataset_url <- "http://direccion web/esoph_cancer.txt"
#download.file (dataset_url, "esoph_cancer.txt")
#getwd ()                           # get working directory
#setwd ("path/to/your/directory")   # set working directory

Revisando el contenido y tipo de datos. Los datos se dividen en 5 columnas: agep (edad promedio), alcgp ( consumo de alcohol), tobgp (consumo de tabaco), ncases (numero de casos) y ncontrols (numero de controles):

head(esoph) # muestra las primeras líneas dela tabla
tail(esoph) # muestra las últimas líneas de la tabla.
class (esoph)      # find the type of data structure
[1] "data.frame"
colnames (esoph)   # find the names of the columns
[1] "agegp"      "alcgp"      "tobgp"      "ncases"     "ncontrols" 
[6] "cat_ncases"

Exploración de datos

dim (esoph) # find the dimension of the data.frame esoph
[1] 88  6

También podemos inferir estos valores indirectamente observando la longitud de cualquier columna, por ejemplo la columna agegp, y la longitud de cualquier fila, por ejemplo, la fila número 3:

length (esoph$agegp) # find the length of column ‘agegp', or esoph[,1]
[1] 88
length (esoph[3,])   # find the length of row 3
[1] 6

Otro comando muy útil para dar salida a todos estos datos, además de los datos internos str UCTURA del objeto:

str (esoph)
'data.frame':   88 obs. of  6 variables:
 $ agegp     : Ord.factor w/ 6 levels "25-34"<"35-44"<..: 1 1 1 1 1 1 1 1 1 1 ...
 $ alcgp     : Ord.factor w/ 4 levels "0-39g/day"<"40-79"<..: 1 1 1 1 2 2 2 2 3 3 ...
 $ tobgp     : Ord.factor w/ 4 levels "0-9g/day"<"10-19"<..: 1 2 3 4 1 2 3 4 1 2 ...
 $ ncases    : num  0 0 0 0 0 0 0 0 0 0 ...
 $ ncontrols : num  40 10 6 5 27 7 4 7 2 1 ...
 $ cat_ncases: Factor w/ 3 levels "rare","med","freq": 1 1 1 1 1 1 1 1 1 1 ...

Resumen de estadísticas

Estadística descriptiva

Estadística descriptiva, estas medidas permiten la descripción cuantitativa de los datos, resumiendo efectivamente las muestras de datos. Las medidas más utilizadas se dividen en 2 grandes grupos: 1. Medidas de tendencia central : media (o promedio), moda y mediana; 2. Medidas de variabilidad o dispersión : desviación estándar, varianza, máxima y mínima.

summary (esoph)
   agegp          alcgp         tobgp        ncases      
 25-34:15   0-39g/day:23   0-9g/day:24   Min.   : 0.000  
 35-44:15   40-79    :23   10-19   :24   1st Qu.: 0.000  
 45-54:16   80-119   :21   20-29   :20   Median : 1.000  
 55-64:16   120+     :21   30+     :20   Mean   : 2.273  
 65-74:15                                3rd Qu.: 4.000  
 75+  :11                                Max.   :17.000  
   ncontrols     cat_ncases
 Min.   : 1.00   rare:79   
 1st Qu.: 3.00   med : 8   
 Median : 6.00   freq: 1   
 Mean   :11.08             
 3rd Qu.:14.00             
 Max.   :60.00             

Como podemos ver, las variables categóricas se contaron por grupo: 6 diferentes grupos de edad , 4 grupos de alcohol y 4 grupos de tabaco , cada uno debidamente identificado. Las variables numéricas se describieron y resumieron mostrando sus valores mínimos, máximos, medios y medianos (equivalentes al segundo cuartil), así como los valores del primer y tercer cuartil.

Estas medidas también se pueden calcular individualmente, para cada variable numérica de interés:

mean (esoph$ncases)       # mean/average of the nr of cases
[1] 2.272727
median (esoph$ncontrols)  # median of all controls
[1] 6
mean (esoph$ncases[1:15]) # mean nr of cases in ages 25-34 (rows 1 to 15)
[1] 0.06666667
median (esoph[16:30,4])   # median nr of cases (column 4) in ages 35-44 (rows 16 to 30)
[1] 0
min (esoph$ncases)        # minimum nr of cases observed
[1] 0
max (esoph[,5])           # maximum nr of controls (column 5) observed
[1] 60

¿Cómo podemos seleccionar datos que no son contiguos? Imagine, por ejemplo, que queremos calcular el número medio de casos de cáncer observados en personas que fuman más de 30 gramos de tabaco por día. Estas filas no se agrupan contiguamente … Para estos casos, podemos usar la función subconjunto () y pedirle a R que “calcule la media del subconjunto del número de casos, donde el grupo de tabaco es igual a 30+”. (Al igual que con cualquier lenguaje de programación, aprender a leer el código ayuda a comprenderlo).

# mean/average of the nr of cases, in smokers’ "30+" group
mean (subset(esoph$ncases, esoph$tobgp == "30+"))
[1] 1.55

¿Qué sucede si queremos seleccionar todos los datos para el grupo de tabaco etiquetado 30+? ¿Cómo podemos hacer eso? Usando la función which () , R puede seleccionar solo los datos pertenecientes a un grupo particular:

# all data concerning tobacco group "30+"
tobgp30 <- esoph[which(esoph[,"tobgp"] == "30+"),]

¿Y cómo podemos obtener, a partir de tobgp30_data, solo los valores donde el número de casos es mayor que cero y sumarlos todos? En este caso, podemos usar which () para seleccionar y sum () para agregar los valores seleccionados:

# all data from tobacco group "30+" where the nr of cases is higher than zero
tobgp30_noZero <- tobgp30[which(tobgp30[, "ncases"] > 0 ),]
sum (tobgp30_noZero$ncases) # sum all cases of cancer
[1] 31

Finalmente, para completar la exploración y el resumen de datos, debemos calcular la desviación estándar y la varianza (que es el cuadrado de la desviación estándar).

sd (esoph$ncases)  # standard deviation of the total nr of cases
[1] 2.753169
var (esoph$ncases) # variance of the number of cases
[1] 7.579937

O si queremos verificar los resultados …

# var of the nr of cases is the square of its standard deviation
sd (esoph$ncases)^2
[1] 7.579937
# sd of the nr of cases is the square root of its variance
sqrt (var (esoph$ncases))
[1] 2.753169

Como se mencionó brevemente antes, este conjunto de datos presenta 2 tipos de variables: variables numéricas continuas (el número de casos y el número de controles) y variables categóricas discretas (el grupo de edad, el grupo de fumadores de tabaco y el grupo de consumo de alcohol). A veces es difícil “categorizar” variables continuas, es decir, agruparlas en intervalos específicos de interés y nombrar estos grupos (también llamados niveles ). En consecuencia, imagine que estábamos interesados en clasificar el número de casos de cáncer según su ocurrencia: frecuente , intermedia y rara . Este tipo de recodificación variableen factores se logra fácilmente utilizando la función cut () .

# factorize the nr of cases in 3 levels, equally spaced, and add the new variable,
 # as the new column cat_ncases, to our dataset
esoph$cat_ncases <- cut (esoph$ncases,3,labels=c("rare","med","freq"))
summary (esoph)
   agegp          alcgp         tobgp        ncases      
 25-34:15   0-39g/day:23   0-9g/day:24   Min.   : 0.000  
 35-44:15   40-79    :23   10-19   :24   1st Qu.: 0.000  
 45-54:16   80-119   :21   20-29   :20   Median : 1.000  
 55-64:16   120+     :21   30+     :20   Mean   : 2.273  
 65-74:15                                3rd Qu.: 4.000  
 75+  :11                                Max.   :17.000  
   ncontrols     cat_ncases
 Min.   : 1.00   rare:79   
 1st Qu.: 3.00   med : 8   
 Median : 6.00   freq: 1   
 Mean   :11.08             
 3rd Qu.:14.00             
 Max.   :60.00             

Visualización

Los gráficos R se crean utilizando una serie de comandos de trazado de alto y bajo nivel . Los comandos de alto nivel crean nuevas tramas, y los de bajo nivel agregan información a una trama existente (la que está actualmente abierta por el dispositivo gráfico). ### Diagramas de dispersión

plot (esoph$ncases, esoph$ncontrols)

# put labels on axis, main title, change point type and color
plot(esoph$ncases, esoph$ncontrols, xlab="Cases", ylab="Controls",
     main="Cases vs Controls", pch=15, col="red")

Histogramas y diagramas

La función hist () muestra la frecuencia (número de ocurrencias) de cada observación; y la función boxplot () muestra la distribución de las ocurrencias en cada categoría (agegp, alcgp y tobg).

# basic histogram, with labels, title and orange bars
hist(esoph$ncases, xlab="Nr of Cases", main="Esoph data", col="orange")

boxplot (esoph$ncases ~ esoph$agegp,main="Esoph dataset",
         border="gray",lwd=1,col=rainbow(5))

Gráficos lineales

Estos son gráficos continuos (generalmente de distribuciones estadísticas conocidas, como Gaussian (dnorm), gamma, beta, etc.).

# multiple normal distribution curves, different mean and sd
curve(dnorm, from=-3, to=5, lwd=2, col="red")
curve(dnorm(x, mean=2), lwd=2, col="blue", add=TRUE)
curve(dnorm(x, mean=-1), lwd=2, col="green", add=TRUE)
curve(dnorm (x,mean=0,sd=1.5), lwd=2, lty=2, col="red", add=TRUE)
# add a vertical line at the mean of the distribution
lines(c(0,0), c(0,dnorm(0)), lty=1, col="red")
# add free text to the plot, in coordinates x=4, y=0.2
text(4,0.2,"Gaussian distribution")
# add extra axis, on top (side 3), from -3 to 5, with tick-marks from -3 to 5,
 # and colored violet 
axis(3, -3:5, seq(-3,5), col.axis = "violet")

gráficos

Para crear una página con varios gráficos ubicados en paneles uno al lado del otro, debemos usar la función par () con uno de los siguientes parámetros: par (mfrow = c (r, c)) o par (mfcol = c (r , c)) . mfrow agrega imágenes por línea, de derecha a izquierda, y mfcol agrega por columna, de arriba a abajo.

# set the graphical display parameters to 3 rows and 2 columns
par(mfrow=c(3,2)) # mfrow adds plots per row, from left to right
# draw boxplots for cases and controls, per each group 
boxplot (esoph$ncases ~ esoph$agegp, xlab="agegp",border="gray", lwd=1,
         col=rainbow(5))
boxplot (esoph$ncontrols ~ esoph$agegp,xlab="agegp", border="gray", lwd=1,
         col=rainbow(5))
boxplot (esoph$ncases ~ esoph$alcgp,border="gray",xlab="alcgp", lwd=1,
         col=rainbow(4))
boxplot (esoph$ncontrols ~ esoph$alcgp,border="gray", xlab="alcgp", lwd=1,
         col=rainbow(4))
boxplot (esoph$ncases ~ esoph$tobgp,border="gray",xlab="tobgp", lwd=1,
         col=rainbow(4))
boxplot (esoph$ncontrols ~ esoph$tobgp,border="gray", xlab="tobgp", lwd=1,
         col=rainbow(4))
# add a title outside of the plotting area
title("Boxplots of Cases (left) and Controls (right)",outer=TRUE, line=-2, cex.main=2)

Una vez finalizados los gráficos del panel, debemos revertir los parámetros gráficos a sus valores predeterminados, para que podamos volver a trazar un gráfico por página.

# reset the graphical display parameters to 1 row and 1 column
par (mfrow=c(1,1))

Exportar

RStudio permite la visualización de las parcelas antes de exportarlas / guardarlas en un archivo de imagen (es decir , basado en mapas de bits , como .jpeg y .png que se pixelan al hacer zoom), o como .pdf (que es un formato vectorial que se puede hacer zoom y estirado hasta el infinito, sin perder calidad de imagen).

# Create a single pdf of figures, with one graph on each page
pdf ("esoph_boxplots.pdf", width=7, height=5) # Start graphics device
# draw boxplots for cases and controls, per each group 
boxplot (esoph$ncases ~ esoph$agegp, main="Cases per Age group",xlab="agegp",
         border="gray", lwd=1, col=rainbow(5))
boxplot (esoph$ncontrols ~ esoph$agegp, xlab="agegp",main="Controls per Age group",
         border="gray", lwd=1, col=rainbow(5))
boxplot (esoph$ncases ~ esoph$alcgp,border="gray",xlab="alcgp",
         main="Cases per Alcohol group",lwd=1, col=rainbow(4))
boxplot (esoph$ncontrols ~ esoph$alcgp,border="gray", xlab="alcgp",
         main="Controls per Alcohol group", lwd=1, col=rainbow(4))
boxplot (esoph$ncases ~ esoph$tobgp,border="gray", xlab="tobgp",
         main="Cases per Tobacco group", lwd=1, col=rainbow(4))
boxplot (esoph$ncontrols ~ esoph$tobgp,border="gray", xlab="tobgp",
         main="Controls per Tobacco group", lwd=1, col=rainbow(4))
dev.off () # close graphics device (stop writing to file)
null device 
          1 
LS0tCnRpdGxlOiAiRXN0YWTDrXN0aWNhIHkgZ3JhZmljYWNpw7NuIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpFc3RlIGRvY3VtZW50byBvZnJlY2UgYWxndW5vcyBkZSBsb3MgY29uY2VwdG9zIG3DoXMgZnVuZGFtZW50YWxlcyBkZSBsYSBSLCBvdG9yZ8OhbmRvIHVuYSBleHBlcmllbmNpYSBwcsOhY3RpY2EgdGFudG8gZW4gbGEgZXhwbG9yYWNpw7NuIHkgZWwgcmVzdW1lbiBkZSBkYXRvcy4KCkNhZGEgcGFydGUgZXN0YSBwb3NlZSB1bmEgZGVzY3JpcGNpw7NuIGRlIGxhIHRhcmVhIHkgbG9zIGNvbWFuZG9zIFIgdXRpbGl6YWRvcy4KCiMjIENhcmdhIGRlIGRhdG9zClV0aWxpemFyZW1vcyBlbCBzZXQgZGUgZGF0b3MgZGUgImVzb3BoIiwgcXVlIGVzdGEgcmVsYWNpb25hZG8gY29uIHVuIGVzdHVkaW8gZGUgY2FzbyB5IGNvbnRyb2wgcmVhbGl6YWRvIGVuIEZyYW5jaWEgcXVlIGJ1c2NhIGFzb2NpYXIgZWwgY8OhbmNlciBlc29mw6FnaWNvIGNvbiBlbCBjb25zdW1vIGRlIGFsY29ob2wuCkVzdGUgc2V0IGRlIGRhdG9zIGZvcm1hIHBhcnRlICBlc3TDoW5kYXIgZGUgUiwgcG9yIHRhbnRvIHNlIGNhcmdhIGRpcmVjdGFtZW50ZS4KRW4gY2FzbyBkZSBjYXJnYXIgZGVzZGUgdW4gZGlyZWNjacOzbiBkZSBpbnRlcm5ldC4gdXRpbGljZSAKZGF0YXNldF91cmwgPC0gImh0dHA6Ly9kaXJlY2Npb25fdXJsL2Vzb3BoX2NhbmNlci50eHQiCmRvd25sb2FkLmZpbGUgKGRhdGFzZXRfdXJsLCAiZXNvcGhfY2FuY2VyLnR4dCIpCnkgZXN0YWJsZXpjYSBzaSBkaXJlY3RvcmlvIGRlIHRyYWJham8KYGBge3J9CiNkYXRhc2V0X3VybCA8LSAiaHR0cDovL2RpcmVjY2lvbiB3ZWIvZXNvcGhfY2FuY2VyLnR4dCIKI2Rvd25sb2FkLmZpbGUgKGRhdGFzZXRfdXJsLCAiZXNvcGhfY2FuY2VyLnR4dCIpCiNnZXR3ZCAoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ2V0IHdvcmtpbmcgZGlyZWN0b3J5CiNzZXR3ZCAoInBhdGgvdG8veW91ci9kaXJlY3RvcnkiKSAgICMgc2V0IHdvcmtpbmcgZGlyZWN0b3J5CmBgYAoKUmV2aXNhbmRvIGVsIGNvbnRlbmlkbyB5IHRpcG8gZGUgZGF0b3MuCkxvcyBkYXRvcyBzZSBkaXZpZGVuIGVuIDUgY29sdW1uYXM6IGFnZXAgKGVkYWQgcHJvbWVkaW8pLCBhbGNncCAoIGNvbnN1bW8gZGUgYWxjb2hvbCksIHRvYmdwIChjb25zdW1vIGRlIHRhYmFjbyksIG5jYXNlcyAobnVtZXJvIGRlIGNhc29zKSB5IG5jb250cm9scyAobnVtZXJvIGRlIGNvbnRyb2xlcyk6CmBgYHtyfQpoZWFkKGVzb3BoKSAjIG11ZXN0cmEgbGFzIHByaW1lcmFzIGzDrW5lYXMgZGVsYSB0YWJsYQpgYGAKCmBgYHtyfQp0YWlsKGVzb3BoKSAjIG11ZXN0cmEgbGFzIMO6bHRpbWFzIGzDrW5lYXMgZGUgbGEgdGFibGEuCmBgYAoKYGBge3J9CmNsYXNzIChlc29waCkgICAgICAjIGZpbmQgdGhlIHR5cGUgb2YgZGF0YSBzdHJ1Y3R1cmUKYGBgCgpgYGB7cn0KY29sbmFtZXMgKGVzb3BoKSAgICMgZmluZCB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMKYGBgCgojIyBFeHBsb3JhY2nDs24gZGUgZGF0b3MKYGBge3J9CmRpbSAoZXNvcGgpICMgZmluZCB0aGUgZGltZW5zaW9uIG9mIHRoZSBkYXRhLmZyYW1lIGVzb3BoCmBgYAoKVGFtYmnDqW4gcG9kZW1vcyBpbmZlcmlyIGVzdG9zIHZhbG9yZXMgaW5kaXJlY3RhbWVudGUgb2JzZXJ2YW5kbyBsYSBsb25naXR1ZCBkZSBjdWFscXVpZXIgY29sdW1uYSwgcG9yIGVqZW1wbG8gbGEgY29sdW1uYSBhZ2VncCwgeSBsYSBsb25naXR1ZCBkZSBjdWFscXVpZXIgZmlsYSwgcG9yIGVqZW1wbG8sIGxhIGZpbGEgbsO6bWVybyAzOgpgYGB7cn0KbGVuZ3RoIChlc29waCRhZ2VncCkgIyBmaW5kIHRoZSBsZW5ndGggb2YgY29sdW1uIOKAmGFnZWdwJywgb3IgZXNvcGhbLDFdCmxlbmd0aCAoZXNvcGhbMyxdKSAgICMgZmluZCB0aGUgbGVuZ3RoIG9mIHJvdyAzCmBgYApPdHJvIGNvbWFuZG8gbXV5IMO6dGlsIHBhcmEgZGFyIHNhbGlkYSBhIHRvZG9zIGVzdG9zIGRhdG9zLCBhZGVtw6FzIGRlIGxvcyBkYXRvcyBpbnRlcm5vcyBzdHIgVUNUVVJBIGRlbCBvYmpldG86CmBgYHtyfQpzdHIgKGVzb3BoKQpgYGAKIyMgUmVzdW1lbiBkZSBlc3RhZMOtc3RpY2FzCiMjIyBFc3RhZMOtc3RpY2EgZGVzY3JpcHRpdmEKRXN0YWTDrXN0aWNhIGRlc2NyaXB0aXZhLCBlc3RhcyBtZWRpZGFzIHBlcm1pdGVuIGxhIGRlc2NyaXBjacOzbiBjdWFudGl0YXRpdmEgZGUgbG9zIGRhdG9zLCByZXN1bWllbmRvIGVmZWN0aXZhbWVudGUgbGFzIG11ZXN0cmFzIGRlIGRhdG9zLiBMYXMgbWVkaWRhcyBtw6FzIHV0aWxpemFkYXMgc2UgZGl2aWRlbiBlbiAyIGdyYW5kZXMgZ3J1cG9zOgoxLiBNZWRpZGFzIGRlIHRlbmRlbmNpYSBjZW50cmFsIDogbWVkaWEgKG8gcHJvbWVkaW8pLCBtb2RhIHkgbWVkaWFuYTsKMi4gTWVkaWRhcyBkZSB2YXJpYWJpbGlkYWQgbyBkaXNwZXJzacOzbiA6IGRlc3ZpYWNpw7NuIGVzdMOhbmRhciwgdmFyaWFuemEsIG3DoXhpbWEgeSBtw61uaW1hLgpgYGB7cn0Kc3VtbWFyeSAoZXNvcGgpCmBgYApDb21vIHBvZGVtb3MgdmVyLCBsYXMgdmFyaWFibGVzIGNhdGVnw7NyaWNhcyBzZSBjb250YXJvbiBwb3IgZ3J1cG86IDYgZGlmZXJlbnRlcyBncnVwb3MgZGUgZWRhZCAsIDQgZ3J1cG9zIGRlIGFsY29ob2wgeSA0IGdydXBvcyBkZSB0YWJhY28gLCBjYWRhIHVubyBkZWJpZGFtZW50ZSBpZGVudGlmaWNhZG8uIApMYXMgdmFyaWFibGVzIG51bcOpcmljYXMgc2UgZGVzY3JpYmllcm9uIHkgcmVzdW1pZXJvbiBtb3N0cmFuZG8gc3VzIHZhbG9yZXMgbcOtbmltb3MsIG3DoXhpbW9zLCBtZWRpb3MgeSBtZWRpYW5vcyAoZXF1aXZhbGVudGVzIGFsIHNlZ3VuZG8gY3VhcnRpbCksIGFzw60gY29tbyBsb3MgdmFsb3JlcyBkZWwgcHJpbWVyIHkgdGVyY2VyIGN1YXJ0aWwuCgpFc3RhcyBtZWRpZGFzIHRhbWJpw6luIHNlIHB1ZWRlbiBjYWxjdWxhciBpbmRpdmlkdWFsbWVudGUsIHBhcmEgY2FkYSB2YXJpYWJsZSBudW3DqXJpY2EgZGUgaW50ZXLDqXM6CmBgYHtyfQptZWFuIChlc29waCRuY2FzZXMpICAgICAgICMgbWVhbi9hdmVyYWdlIG9mIHRoZSBuciBvZiBjYXNlcwptZWRpYW4gKGVzb3BoJG5jb250cm9scykgICMgbWVkaWFuIG9mIGFsbCBjb250cm9scwptZWFuIChlc29waCRuY2FzZXNbMToxNV0pICMgbWVhbiBuciBvZiBjYXNlcyBpbiBhZ2VzIDI1LTM0IChyb3dzIDEgdG8gMTUpCm1lZGlhbiAoZXNvcGhbMTY6MzAsNF0pICAgIyBtZWRpYW4gbnIgb2YgY2FzZXMgKGNvbHVtbiA0KSBpbiBhZ2VzIDM1LTQ0IChyb3dzIDE2IHRvIDMwKQptaW4gKGVzb3BoJG5jYXNlcykgICAgICAgICMgbWluaW11bSBuciBvZiBjYXNlcyBvYnNlcnZlZAptYXggKGVzb3BoWyw1XSkgICAgICAgICAgICMgbWF4aW11bSBuciBvZiBjb250cm9scyAoY29sdW1uIDUpIG9ic2VydmVkCmBgYArCv0PDs21vIHBvZGVtb3Mgc2VsZWNjaW9uYXIgZGF0b3MgcXVlIG5vIHNvbiBjb250aWd1b3M/IEltYWdpbmUsIHBvciBlamVtcGxvLCBxdWUgcXVlcmVtb3MgY2FsY3VsYXIgZWwgbsO6bWVybyBtZWRpbyBkZSBjYXNvcyBkZSBjw6FuY2VyIG9ic2VydmFkb3MgZW4gcGVyc29uYXMgcXVlIGZ1bWFuIG3DoXMgZGUgMzAgZ3JhbW9zIGRlIHRhYmFjbyBwb3IgZMOtYS4gRXN0YXMgZmlsYXMgbm8gc2UgYWdydXBhbiBjb250aWd1YW1lbnRlIC4uLiBQYXJhIGVzdG9zIGNhc29zLCBwb2RlbW9zIHVzYXIgbGEgZnVuY2nDs24gc3ViY29uanVudG8gKCkgeSBwZWRpcmxlIGEgUiBxdWUgImNhbGN1bGUgbGEgbWVkaWEgZGVsIHN1YmNvbmp1bnRvIGRlbCBuw7ptZXJvIGRlIGNhc29zLCBkb25kZSBlbCBncnVwbyBkZSB0YWJhY28gZXMgaWd1YWwgYSAzMCsiLiAoQWwgaWd1YWwgcXVlIGNvbiBjdWFscXVpZXIgbGVuZ3VhamUgZGUgcHJvZ3JhbWFjacOzbiwgYXByZW5kZXIgYSBsZWVyIGVsIGPDs2RpZ28gYXl1ZGEgYSBjb21wcmVuZGVybG8pLgpgYGB7cn0KIyBtZWFuL2F2ZXJhZ2Ugb2YgdGhlIG5yIG9mIGNhc2VzLCBpbiBzbW9rZXJz4oCZICIzMCsiIGdyb3VwCm1lYW4gKHN1YnNldChlc29waCRuY2FzZXMsIGVzb3BoJHRvYmdwID09ICIzMCsiKSkKYGBgCsK/UXXDqSBzdWNlZGUgc2kgcXVlcmVtb3Mgc2VsZWNjaW9uYXIgdG9kb3MgbG9zIGRhdG9zIHBhcmEgZWwgZ3J1cG8gZGUgdGFiYWNvIGV0aXF1ZXRhZG8gMzArPyDCv0PDs21vIHBvZGVtb3MgaGFjZXIgZXNvPyBVc2FuZG8gbGEgZnVuY2nDs24gd2hpY2ggKCkgLCBSIHB1ZWRlIHNlbGVjY2lvbmFyIHNvbG8gbG9zIGRhdG9zIHBlcnRlbmVjaWVudGVzIGEgdW4gZ3J1cG8gcGFydGljdWxhcjoKYGBge3J9CiMgYWxsIGRhdGEgY29uY2VybmluZyB0b2JhY2NvIGdyb3VwICIzMCsiCnRvYmdwMzAgPC0gZXNvcGhbd2hpY2goZXNvcGhbLCJ0b2JncCJdID09ICIzMCsiKSxdCmBgYArCv1kgY8OzbW8gcG9kZW1vcyBvYnRlbmVyLCBhIHBhcnRpciBkZSB0b2JncDMwX2RhdGEsIHNvbG8gbG9zIHZhbG9yZXMgZG9uZGUgZWwgbsO6bWVybyBkZSBjYXNvcyBlcyBtYXlvciBxdWUgY2VybyB5IHN1bWFybG9zIHRvZG9zPyBFbiBlc3RlIGNhc28sIHBvZGVtb3MgdXNhciB3aGljaCAoKSBwYXJhIHNlbGVjY2lvbmFyIHkgc3VtICgpIHBhcmEgYWdyZWdhciBsb3MgdmFsb3JlcyBzZWxlY2Npb25hZG9zOgpgYGB7cn0KIyBhbGwgZGF0YSBmcm9tIHRvYmFjY28gZ3JvdXAgIjMwKyIgd2hlcmUgdGhlIG5yIG9mIGNhc2VzIGlzIGhpZ2hlciB0aGFuIHplcm8KdG9iZ3AzMF9ub1plcm8gPC0gdG9iZ3AzMFt3aGljaCh0b2JncDMwWywgIm5jYXNlcyJdID4gMCApLF0Kc3VtICh0b2JncDMwX25vWmVybyRuY2FzZXMpICMgc3VtIGFsbCBjYXNlcyBvZiBjYW5jZXIKYGBgCkZpbmFsbWVudGUsIHBhcmEgY29tcGxldGFyIGxhIGV4cGxvcmFjacOzbiB5IGVsIHJlc3VtZW4gZGUgZGF0b3MsIGRlYmVtb3MgY2FsY3VsYXIgbGEgZGVzdmlhY2nDs24gZXN0w6FuZGFyIHkgbGEgdmFyaWFuemEgKHF1ZSBlcyBlbCBjdWFkcmFkbyBkZSBsYSBkZXN2aWFjacOzbiBlc3TDoW5kYXIpLgpgYGB7cn0Kc2QgKGVzb3BoJG5jYXNlcykgICMgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSB0b3RhbCBuciBvZiBjYXNlcwp2YXIgKGVzb3BoJG5jYXNlcykgIyB2YXJpYW5jZSBvZiB0aGUgbnVtYmVyIG9mIGNhc2VzCmBgYApPIHNpIHF1ZXJlbW9zIHZlcmlmaWNhciBsb3MgcmVzdWx0YWRvcyAuLi4KYGBge3J9CiMgdmFyIG9mIHRoZSBuciBvZiBjYXNlcyBpcyB0aGUgc3F1YXJlIG9mIGl0cyBzdGFuZGFyZCBkZXZpYXRpb24Kc2QgKGVzb3BoJG5jYXNlcyleMgojIHNkIG9mIHRoZSBuciBvZiBjYXNlcyBpcyB0aGUgc3F1YXJlIHJvb3Qgb2YgaXRzIHZhcmlhbmNlCnNxcnQgKHZhciAoZXNvcGgkbmNhc2VzKSkKYGBgCkNvbW8gc2UgbWVuY2lvbsOzIGJyZXZlbWVudGUgYW50ZXMsIGVzdGUgY29uanVudG8gZGUgZGF0b3MgcHJlc2VudGEgMiB0aXBvcyBkZSB2YXJpYWJsZXM6IHZhcmlhYmxlcyBudW3DqXJpY2FzIGNvbnRpbnVhcyAoZWwgbsO6bWVybyBkZSBjYXNvcyB5IGVsIG7Dum1lcm8gZGUgY29udHJvbGVzKSB5IHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgZGlzY3JldGFzIChlbCBncnVwbyBkZSBlZGFkLCBlbCBncnVwbyBkZSBmdW1hZG9yZXMgZGUgdGFiYWNvIHkgZWwgZ3J1cG8gZGUgY29uc3VtbyBkZSBhbGNvaG9sKS4gQSB2ZWNlcyBlcyBkaWbDrWNpbCAiY2F0ZWdvcml6YXIiIHZhcmlhYmxlcyBjb250aW51YXMsIGVzIGRlY2lyLCBhZ3J1cGFybGFzIGVuIGludGVydmFsb3MgZXNwZWPDrWZpY29zIGRlIGludGVyw6lzIHkgbm9tYnJhciBlc3RvcyBncnVwb3MgKHRhbWJpw6luIGxsYW1hZG9zIG5pdmVsZXMgKS4KRW4gY29uc2VjdWVuY2lhLCBpbWFnaW5lIHF1ZSBlc3TDoWJhbW9zIGludGVyZXNhZG9zIAsLZW4gY2xhc2lmaWNhciBlbCBuw7ptZXJvIGRlIGNhc29zIGRlIGPDoW5jZXIgc2Vnw7puIHN1IG9jdXJyZW5jaWE6IGZyZWN1ZW50ZSAsIGludGVybWVkaWEgeSByYXJhIC4gRXN0ZSB0aXBvIGRlIHJlY29kaWZpY2FjacOzbiB2YXJpYWJsZWVuIGZhY3RvcmVzIHNlIGxvZ3JhIGbDoWNpbG1lbnRlIHV0aWxpemFuZG8gbGEgZnVuY2nDs24gY3V0ICgpIC4KYGBge3J9CiMgZmFjdG9yaXplIHRoZSBuciBvZiBjYXNlcyBpbiAzIGxldmVscywgZXF1YWxseSBzcGFjZWQsIGFuZCBhZGQgdGhlIG5ldyB2YXJpYWJsZSwKICMgYXMgdGhlIG5ldyBjb2x1bW4gY2F0X25jYXNlcywgdG8gb3VyIGRhdGFzZXQKZXNvcGgkY2F0X25jYXNlcyA8LSBjdXQgKGVzb3BoJG5jYXNlcywzLGxhYmVscz1jKCJyYXJlIiwibWVkIiwiZnJlcSIpKQpzdW1tYXJ5IChlc29waCkKYGBgCiMjIFZpc3VhbGl6YWNpw7NuCkxvcyBncsOhZmljb3MgUiBzZSBjcmVhbiB1dGlsaXphbmRvIHVuYSBzZXJpZSBkZSBjb21hbmRvcyBkZSB0cmF6YWRvIGRlIGFsdG8geSBiYWpvIG5pdmVsIC4gTG9zIGNvbWFuZG9zIGRlIGFsdG8gbml2ZWwgY3JlYW4gbnVldmFzIHRyYW1hcywgeSBsb3MgZGUgYmFqbyBuaXZlbCBhZ3JlZ2FuIGluZm9ybWFjacOzbiBhIHVuYSB0cmFtYSBleGlzdGVudGUgKGxhIHF1ZSBlc3TDoSBhY3R1YWxtZW50ZSBhYmllcnRhIHBvciBlbCBkaXNwb3NpdGl2byBncsOhZmljbykuCiMjIyBEaWFncmFtYXMgZGUgZGlzcGVyc2nDs24KYGBge3J9CnBsb3QgKGVzb3BoJG5jYXNlcywgZXNvcGgkbmNvbnRyb2xzKQpgYGAKYGBge3J9CiMgcHV0IGxhYmVscyBvbiBheGlzLCBtYWluIHRpdGxlLCBjaGFuZ2UgcG9pbnQgdHlwZSBhbmQgY29sb3IKcGxvdChlc29waCRuY2FzZXMsIGVzb3BoJG5jb250cm9scywgeGxhYj0iQ2FzZXMiLCB5bGFiPSJDb250cm9scyIsCiAgICAgbWFpbj0iQ2FzZXMgdnMgQ29udHJvbHMiLCBwY2g9MTUsIGNvbD0icmVkIikKYGBgCiMjIyBIaXN0b2dyYW1hcyB5IGRpYWdyYW1hcwpMYSBmdW5jacOzbiBoaXN0ICgpIG11ZXN0cmEgbGEgZnJlY3VlbmNpYSAobsO6bWVybyBkZSBvY3VycmVuY2lhcykgZGUgY2FkYSBvYnNlcnZhY2nDs247IHkgbGEgZnVuY2nDs24gYm94cGxvdCAoKSBtdWVzdHJhIGxhIGRpc3RyaWJ1Y2nDs24gZGUgbGFzIG9jdXJyZW5jaWFzIGVuIGNhZGEgY2F0ZWdvcsOtYSAoYWdlZ3AsIGFsY2dwIHkgdG9iZykuCmBgYHtyfQojIGJhc2ljIGhpc3RvZ3JhbSwgd2l0aCBsYWJlbHMsIHRpdGxlIGFuZCBvcmFuZ2UgYmFycwpoaXN0KGVzb3BoJG5jYXNlcywgeGxhYj0iTnIgb2YgQ2FzZXMiLCBtYWluPSJFc29waCBkYXRhIiwgY29sPSJvcmFuZ2UiKQpgYGAKYGBge3J9CmJveHBsb3QgKGVzb3BoJG5jYXNlcyB+IGVzb3BoJGFnZWdwLG1haW49IkVzb3BoIGRhdGFzZXQiLAogICAgICAgICBib3JkZXI9ImdyYXkiLGx3ZD0xLGNvbD1yYWluYm93KDUpKQpgYGAKIyMjIEdyw6FmaWNvcyBsaW5lYWxlcwpFc3RvcyBzb24gZ3LDoWZpY29zIGNvbnRpbnVvcyAoZ2VuZXJhbG1lbnRlIGRlIGRpc3RyaWJ1Y2lvbmVzIGVzdGFkw61zdGljYXMgY29ub2NpZGFzLCBjb21vIEdhdXNzaWFuIChkbm9ybSksIGdhbW1hLCBiZXRhLCBldGMuKS4gCmBgYHtyfQojIG11bHRpcGxlIG5vcm1hbCBkaXN0cmlidXRpb24gY3VydmVzLCBkaWZmZXJlbnQgbWVhbiBhbmQgc2QKY3VydmUoZG5vcm0sIGZyb209LTMsIHRvPTUsIGx3ZD0yLCBjb2w9InJlZCIpCmN1cnZlKGRub3JtKHgsIG1lYW49MiksIGx3ZD0yLCBjb2w9ImJsdWUiLCBhZGQ9VFJVRSkKY3VydmUoZG5vcm0oeCwgbWVhbj0tMSksIGx3ZD0yLCBjb2w9ImdyZWVuIiwgYWRkPVRSVUUpCmN1cnZlKGRub3JtICh4LG1lYW49MCxzZD0xLjUpLCBsd2Q9MiwgbHR5PTIsIGNvbD0icmVkIiwgYWRkPVRSVUUpCiMgYWRkIGEgdmVydGljYWwgbGluZSBhdCB0aGUgbWVhbiBvZiB0aGUgZGlzdHJpYnV0aW9uCmxpbmVzKGMoMCwwKSwgYygwLGRub3JtKDApKSwgbHR5PTEsIGNvbD0icmVkIikKIyBhZGQgZnJlZSB0ZXh0IHRvIHRoZSBwbG90LCBpbiBjb29yZGluYXRlcyB4PTQsIHk9MC4yCnRleHQoNCwwLjIsIkdhdXNzaWFuIGRpc3RyaWJ1dGlvbiIpCiMgYWRkIGV4dHJhIGF4aXMsIG9uIHRvcCAoc2lkZSAzKSwgZnJvbSAtMyB0byA1LCB3aXRoIHRpY2stbWFya3MgZnJvbSAtMyB0byA1LAogIyBhbmQgY29sb3JlZCB2aW9sZXQgCmF4aXMoMywgLTM6NSwgc2VxKC0zLDUpLCBjb2wuYXhpcyA9ICJ2aW9sZXQiKQpgYGAKCiMjIyBncsOhZmljb3MKUGFyYSBjcmVhciB1bmEgcMOhZ2luYSBjb24gdmFyaW9zIGdyw6FmaWNvcyB1YmljYWRvcyBlbiBwYW5lbGVzIHVubyBhbCBsYWRvIGRlbCBvdHJvLCBkZWJlbW9zIHVzYXIgbGEgZnVuY2nDs24gcGFyICgpIGNvbiB1bm8gZGUgbG9zIHNpZ3VpZW50ZXMgcGFyw6FtZXRyb3M6IHBhciAobWZyb3cgPSBjIChyLCBjKSkgbyBwYXIgKG1mY29sID0gYyAociAsIGMpKSAuIG1mcm93IGFncmVnYSBpbcOhZ2VuZXMgcG9yIGzDrW5lYSwgZGUgZGVyZWNoYSBhIGl6cXVpZXJkYSwgeSBtZmNvbCBhZ3JlZ2EgcG9yIGNvbHVtbmEsIGRlIGFycmliYSBhIGFiYWpvLgpgYGB7cn0KIyBzZXQgdGhlIGdyYXBoaWNhbCBkaXNwbGF5IHBhcmFtZXRlcnMgdG8gMyByb3dzIGFuZCAyIGNvbHVtbnMKcGFyKG1mcm93PWMoMywyKSkgIyBtZnJvdyBhZGRzIHBsb3RzIHBlciByb3csIGZyb20gbGVmdCB0byByaWdodAojIGRyYXcgYm94cGxvdHMgZm9yIGNhc2VzIGFuZCBjb250cm9scywgcGVyIGVhY2ggZ3JvdXAgCmJveHBsb3QgKGVzb3BoJG5jYXNlcyB+IGVzb3BoJGFnZWdwLCB4bGFiPSJhZ2VncCIsYm9yZGVyPSJncmF5IiwgbHdkPTEsCiAgICAgICAgIGNvbD1yYWluYm93KDUpKQpib3hwbG90IChlc29waCRuY29udHJvbHMgfiBlc29waCRhZ2VncCx4bGFiPSJhZ2VncCIsIGJvcmRlcj0iZ3JheSIsIGx3ZD0xLAogICAgICAgICBjb2w9cmFpbmJvdyg1KSkKYm94cGxvdCAoZXNvcGgkbmNhc2VzIH4gZXNvcGgkYWxjZ3AsYm9yZGVyPSJncmF5Iix4bGFiPSJhbGNncCIsIGx3ZD0xLAogICAgICAgICBjb2w9cmFpbmJvdyg0KSkKYm94cGxvdCAoZXNvcGgkbmNvbnRyb2xzIH4gZXNvcGgkYWxjZ3AsYm9yZGVyPSJncmF5IiwgeGxhYj0iYWxjZ3AiLCBsd2Q9MSwKICAgICAgICAgY29sPXJhaW5ib3coNCkpCmJveHBsb3QgKGVzb3BoJG5jYXNlcyB+IGVzb3BoJHRvYmdwLGJvcmRlcj0iZ3JheSIseGxhYj0idG9iZ3AiLCBsd2Q9MSwKICAgICAgICAgY29sPXJhaW5ib3coNCkpCmJveHBsb3QgKGVzb3BoJG5jb250cm9scyB+IGVzb3BoJHRvYmdwLGJvcmRlcj0iZ3JheSIsIHhsYWI9InRvYmdwIiwgbHdkPTEsCiAgICAgICAgIGNvbD1yYWluYm93KDQpKQojIGFkZCBhIHRpdGxlIG91dHNpZGUgb2YgdGhlIHBsb3R0aW5nIGFyZWEKdGl0bGUoIkJveHBsb3RzIG9mIENhc2VzIChsZWZ0KSBhbmQgQ29udHJvbHMgKHJpZ2h0KSIsb3V0ZXI9VFJVRSwgbGluZT0tMiwgY2V4Lm1haW49MikKYGBgClVuYSB2ZXogZmluYWxpemFkb3MgbG9zIGdyw6FmaWNvcyBkZWwgcGFuZWwsIGRlYmVtb3MgcmV2ZXJ0aXIgbG9zIHBhcsOhbWV0cm9zIGdyw6FmaWNvcyBhIHN1cyB2YWxvcmVzIHByZWRldGVybWluYWRvcywgcGFyYSBxdWUgcG9kYW1vcyB2b2x2ZXIgYSB0cmF6YXIgdW4gZ3LDoWZpY28gcG9yIHDDoWdpbmEuCmBgYHtyfQojIHJlc2V0IHRoZSBncmFwaGljYWwgZGlzcGxheSBwYXJhbWV0ZXJzIHRvIDEgcm93IGFuZCAxIGNvbHVtbgpwYXIgKG1mcm93PWMoMSwxKSkKYGBgCiMjIyBFeHBvcnRhcgpSU3R1ZGlvIHBlcm1pdGUgbGEgdmlzdWFsaXphY2nDs24gZGUgbGFzIHBhcmNlbGFzIGFudGVzIGRlIGV4cG9ydGFybGFzIC8gZ3VhcmRhcmxhcyBlbiB1biBhcmNoaXZvIGRlIGltYWdlbiAoZXMgZGVjaXIgLCBiYXNhZG8gZW4gbWFwYXMgZGUgYml0cyAsIGNvbW8gLmpwZWcgeSAucG5nIHF1ZSBzZSBwaXhlbGFuIGFsIGhhY2VyIHpvb20pLCBvIGNvbW8gLnBkZiAocXVlIGVzIHVuIGZvcm1hdG8gdmVjdG9yaWFsIHF1ZSBzZSBwdWVkZSBoYWNlciB6b29tIHkgZXN0aXJhZG8gaGFzdGEgZWwgaW5maW5pdG8sIHNpbiBwZXJkZXIgY2FsaWRhZCBkZSBpbWFnZW4pLiAKYGBge3J9CiMgQ3JlYXRlIGEgc2luZ2xlIHBkZiBvZiBmaWd1cmVzLCB3aXRoIG9uZSBncmFwaCBvbiBlYWNoIHBhZ2UKcGRmICgiZXNvcGhfYm94cGxvdHMucGRmIiwgd2lkdGg9NywgaGVpZ2h0PTUpICMgU3RhcnQgZ3JhcGhpY3MgZGV2aWNlCiMgZHJhdyBib3hwbG90cyBmb3IgY2FzZXMgYW5kIGNvbnRyb2xzLCBwZXIgZWFjaCBncm91cCAKYm94cGxvdCAoZXNvcGgkbmNhc2VzIH4gZXNvcGgkYWdlZ3AsIG1haW49IkNhc2VzIHBlciBBZ2UgZ3JvdXAiLHhsYWI9ImFnZWdwIiwKICAgICAgICAgYm9yZGVyPSJncmF5IiwgbHdkPTEsIGNvbD1yYWluYm93KDUpKQpib3hwbG90IChlc29waCRuY29udHJvbHMgfiBlc29waCRhZ2VncCwgeGxhYj0iYWdlZ3AiLG1haW49IkNvbnRyb2xzIHBlciBBZ2UgZ3JvdXAiLAogICAgICAgICBib3JkZXI9ImdyYXkiLCBsd2Q9MSwgY29sPXJhaW5ib3coNSkpCmJveHBsb3QgKGVzb3BoJG5jYXNlcyB+IGVzb3BoJGFsY2dwLGJvcmRlcj0iZ3JheSIseGxhYj0iYWxjZ3AiLAogICAgICAgICBtYWluPSJDYXNlcyBwZXIgQWxjb2hvbCBncm91cCIsbHdkPTEsIGNvbD1yYWluYm93KDQpKQpib3hwbG90IChlc29waCRuY29udHJvbHMgfiBlc29waCRhbGNncCxib3JkZXI9ImdyYXkiLCB4bGFiPSJhbGNncCIsCiAgICAgICAgIG1haW49IkNvbnRyb2xzIHBlciBBbGNvaG9sIGdyb3VwIiwgbHdkPTEsIGNvbD1yYWluYm93KDQpKQpib3hwbG90IChlc29waCRuY2FzZXMgfiBlc29waCR0b2JncCxib3JkZXI9ImdyYXkiLCB4bGFiPSJ0b2JncCIsCiAgICAgICAgIG1haW49IkNhc2VzIHBlciBUb2JhY2NvIGdyb3VwIiwgbHdkPTEsIGNvbD1yYWluYm93KDQpKQpib3hwbG90IChlc29waCRuY29udHJvbHMgfiBlc29waCR0b2JncCxib3JkZXI9ImdyYXkiLCB4bGFiPSJ0b2JncCIsCiAgICAgICAgIG1haW49IkNvbnRyb2xzIHBlciBUb2JhY2NvIGdyb3VwIiwgbHdkPTEsIGNvbD1yYWluYm93KDQpKQpkZXYub2ZmICgpICMgY2xvc2UgZ3JhcGhpY3MgZGV2aWNlIChzdG9wIHdyaXRpbmcgdG8gZmlsZSkKYGBgCg==