¿Qué es R?
- Básicamente, R es un “dialecto” de un lenguaje de los años ’70: S-Language
- S fue creado en los Bell Labs
- R creado en 1991 por Ross Ithaka y Robert Gentelman
- 1993: R se anuncia por primera vez
- 2000: se lanza la primera versión R 1.0
- 2017: la versión más actual es la R 3.4.0
Características de R
- Sintaxis similar a S (en su momento permitía hacer fácil el pasaje)
- Corre en casi cualquier SO/plataforma (incluso en PS3)
- Hay actualizaciones muy frecuentes (dos o tres por año)
- Funcionalidad modular: hay un conjunto de funciones básicas al cual se le van agregando diferentes paquetes con funcionalidades especificas
- Muy buenas capacidades gráficas: mucho mejores y más sofisticadas que cualquier software de procesamiento estadístico (extremo
ggplot2
)
- Lo mejor de todo: la comunidad. Cada estadístico que se le ocurre un algoritmo nuevo lo programa en R
- Lo segundo mejor: GRATIS
Filosofía Free software
- Libertad de correr el soft con cualquier propósito (grado 0)
- Libertad de estudiar cómo funciona el programa y adapatarlo a las necesidaes (grado 1). Requisito: disponer del código fuente
- Libertad de redistribuir copias (grado 2)
- Libertad de mejorar el software y lanzar las mejoras al público (grado 3). Mismo requisito que grado 1.
Desventajas de R
- En general, los datos se cargan en memoria RAM (es más o menos lo mismo que casi cualquier otro soft). Algunos paquetes comenzaron a mejorar esta cuestión.
- La implementación es… espontánea. Si a nadie le interesa generar tu algoritmo… te toca a vos.
Instalación de R y RStudio por primera vez
- Ir al sitio de R
- Elegir el repositorio del cual se desea descargar
- Descargar la última versión estable (actual R 3.4.0)
- Luego, es útil instalar alguna GUI para que el trabajo con el código sea más amigable.
- RStudio es una de las más usadas.
- Descargar la última versión del sitio.
Ayuda en R
*En general, para obtener ayuda hay dos formas
help(mean)
?mean
Uso de comandos
- En usamos básicamente instrucciones que tipeamos o bien en la consola o bien en un script.
- Los resultados van a aparecer en la consola
sqrt(81)
[1] 9
sqrt()
es una función, es decir, una secuencia de código lista para usar. + A su vez, tiene un parmétro o argumento, los valores que una función espera que el usuario defina
- Las funciones llevan un nombre y van entre paréntesis
Uso de comandos
- Ahora, ¿qué pasa si ingreso esto?
x <- sqrt(81)
- No se observa ningún resultado porque usamos un operador (una función)
<-
- En el código anterior se crea una variable (un objeto) y se le asigna el valor de
sqrt(81)
- Cuando pedimos…
x
[1] 9
Uso de comandos
- Trabajar con variables nos permite almacenar valores para usarlos después
- Hacen nuestro código más fácil de leer y compartir con otros
Importando Datos en R
- Varias funciones importantes:
read.table()
y read.csv()
para leer datos tabulares
readLines()
para leer líneas en un archivo de texto
read.spss()
en el paquete foreign que unifica muchas las funciones de importación exportación
Importando Datos en R: read.table()
- De las más usadas. El formato es similar al resto
file
: string con el nombre y la ruta del archivo a importar
header
: logical indicado si el archivo tiene cabeceras
sep
: string que indica cómo se separan las columnas
colClasses
: character vector que indica la clase de cada columna (“character”,“integer”,etc.)
nrows
: cantidad de filas en el dataset
skip
: cantidad de filas que hay que saltear contando desde el principio en el dataset
stringAsFactors
: logical que indica si al importar el archivo deben codificarse los strings como factors
Importando Datos en R: read.table()
- Si el archivo no es muy grande puede usarse directamente:
- R va a realizar algunas tareas automáticamente:
- saltear las líneas que empiezan con #
- calcular cuántas filas hay y cuánta memoria necesita reservar
- identificar la clase de cada columna (si uno lo define con los argumentos resulta más eficiente)
read.csv()
es idéntico a read.table()
con la diferencia de que el sperador es la coma.
Importando Datos “grandes” en R: read.table()
- Si se enfrentan con un dataset grandes pueden
- consultar la documentación de la función
- Setear el argumento
colClasses
: suele ser mucho más rápido dado que R no tiene que realizar la tarea de detectar los tipos de cada columna.
- Setar
nrows
esto ayuda a usar menos memoria… no a que sea necesariamente más rápido.
Importando Datos “grandes” en R: read.table()
- Además de todo esto es importante hacer una estimación gruesa de la memoria que va a requerir cargar el dataset.
- Si la memoria requerida es más de la dispnible en el el sistema…
- Cálculo grueso: supongamos un dataframe con 2.000.000 de filas y 200 columnas todos datos numéricos
- 2.000.000 x 200 x 8 bytes/numerics
- 2.400.000.000 bytes
- 2.400.000.000 bytes / 2^20 bytes/MB
- 2.288,8 MB
- 2.24 GB
Objetos en R
- Básicamente… donde R guarda los datos.
- Hay muchos tipos de objetos en R.
- Muchos paquetes “generan” sus propios objetos
- No obstante, la mayoría de las tareas pueden resolverse con algunos pocos tipos de objetos
- En R hay cinco tipos atómicos de objetos
character
numeric
integer
complex
logical
Objetos en R: Vectores
- El objeto más básico es un vector
- Un vector solo puede constener objetos de la misma clase
- Excepción: listas, tipo especial de vector.
- Pueden crearse vectores vacíos con la función
vector()
Generado vectores
- El operador
:
sirve para generar secuencias de enteros
- También puede usarse la función
seq()
para secuencias más complejas
x<-1:10
x
[1] 1 2 3 4 5 6 7 8 9 10
Generado vectores
- La función
c()
sirve para crear vectores de objetos
x<-c(1.2,2.3,3.8) #numérico
x<-c(TRUE, FALSE, TRUE) #lógico
x<-c("a","b","c") #character
x<-9:20 #integer
- También puede usarse la función
vector()
x<-vector("numeric",length=10)
x
[1] 0 0 0 0 0 0 0 0 0 0
Mezclando vectores
- Cuando se mezclan diferentes objetos en un vector R hace uso de la “coerción”: se fuerza a los objetos a transformarse en una misma clase
y<-c(1.2,"a") #character
x<-c(FALSE, 2) #numérico
x<-c(FALSE, "a") #character
Coerción explícita
- Puede aplicarse de forma explícita la coercion; se usan las funciones
as.[class]
- Esta coercion puede aplicarse a otros objetos, no solo a vectores
x<-0:6
as.character(x)
[1] "0" "1" "2" "3" "4" "5" "6"
as.logical(x)
[1] FALSE TRUE TRUE TRUE TRUE TRUE TRUE
as.character(x)
[1] "0" "1" "2" "3" "4" "5" "6"
Objetos en R: Factors
- Se usan para representar datos categóricos (nominales u ordinales). Son algo así como un vector de integers con una etiqueta asociada (algo parecido a SPSS).
- Algunas funciones como
lm()
y glm()
hacen un uso especial de los factores
- Suele ser mejor usar factors que integers porque son “autodescriptivos”: “Ocupado”, “Desocupado”, “Inactivo” es mejor que 1,2,3.
Objetos en R: Factors
x<-factor(c("Ocup","Ocup","Desoc","Ocup"
,"Inact"))
x
[1] Ocup Ocup Desoc Ocup Inact
Levels: Desoc Inact Ocup
Objetos en R: Factors
- Se puede generar factors ordenados con el argumento
levels
.
x<-factor(c("A","A","M","B"
,"B"),
levels=c("B","M","A"))
x
[1] A A M B B
Levels: B M A
table(x)
x
B M A
2 1 2
Objetos en R: Data Frames
- Son lo más parecido a lo que entendemos en ciencias sociales por base de datos. Se usan para almacenar datos tabulares.
- Para R son un tipo especial de list, en la cual cada elemento de la lista tiene la misma longitud (
length()
)
- Cada elemento de la list puede ser pensado por una columna y el largo de cada elemento de la list es el nro. de files (
nrow()
)
- A diferencia de las matrix cada data frame puede almancenar elementos (columnas) de diferentes tipos.
- Tienen un atributo especial:
row.names
- Cuando veamos impo y expo de datos, veremos que cuando uno llama a la función
read.csv()
o read.table()
o similares lo que devuelven son un data frame
- Pueden ser transformados en matrices usando
data.matrix()
Objetos en R: Data Frames
x<-data.frame(ident=1:3, tratamiento=c("D","B","B"))
x
nrow(x)
[1] 3
ncol(x)
[1] 2
row.names(x)
[1] "1" "2" "3"
Objetos en R: Listas
- Las listas son una clase especial de vector: pueden contener elementos de cualqueir tipo en su interior.
- Son objetos muy importantes en R: la mayoría de los resultados de los modelos que se aplican en R devuelven como output un objeto que es una lista.
- Otro ejemplo: al importar datos de un GIS (.shp, .geojson, geodatabase, etc.) R los interpreta como una lista.
Objetos en R: Listas
x<-list(1,"a",TRUE, 4.6)
x
[[1]]
[1] 1
[[2]]
[1] "a"
[[3]]
[1] TRUE
[[4]]
[1] 4.6
Subsetting/Slicing en R
- Ahora, ¿cómo extraemos partes de los objetos? ¿Cómo aplicamos transformaciones a un subconjunto de un determinado objeto?
- A ambas operaciones se las llama slicing o subsetting, respectivamente
- Cada objeto tiene alguna forma de subsetting o slicing. Hay varios operadores
[
devuelve siempre un objeto de la misma clase que el original
[[
se usa para extraer datos de un dataframe o una lista. No necesariamente devuelve siempre un objeto de la misma clase que el original
$
se usa para extraer elementos de un dataframe o una lista por nombre.
Subsetting/Slicing en R: Vectores
x<-1:10
x[1:4]
[1] 1 2 3 4
x[4:1]
[1] 4 3 2 1
x[c(1,5,8)]
[1] 1 5 8
x[-c(1,5,8)]
[1] 2 3 4 6 7 9 10
Subsetting/Slicing en R: Data Frames
x<-1:4
y<-c("Z","Z","E","F")
z<-c(TRUE,FALSE,FALSE,FALSE)
df<-as.data.frame(cbind(x,y,z))
df[,2]
[1] Z Z E F
Levels: E F Z
df$x
[1] 1 2 3 4
Levels: 1 2 3 4
df$z[1]
[1] TRUE
Levels: FALSE TRUE
Subsetting/Slicing en R: Listas
x<-list(foo = 1:4, bar = 0.6)
x[1]
$foo
[1] 1 2 3 4
x[[1]]
[1] 1 2 3 4
x$bar
[1] 0.6
x['bar']
$bar
[1] 0.6
x[['bar']]
[1] 0.6
Subsetting/Slicing en R: Listas
x<-list(foo = 1:4, bar = 0.6, baz = 'hola')
x[c(1,3)]
$foo
[1] 1 2 3 4
$baz
[1] "hola"
Subsetting/Slicing en R: Listas
- El operador
[[]]
puede usarse con índices calculados. $
solo puede usarse con nombres
x<-list(foo = 1:4, bar = 0.6, baz = 'hola')
name<-'bar' # Indice computado
x[[name]]
[1] 0.6
x$name
NULL
x$foo
[1] 1 2 3 4
Subsetting/Slicing en R: Listas con elementos anidados
- El operador
[[]]
puede tomar cualquier secuencia de enteros
x<-list(a = list(10,12,14), b = c(3.14, 2.81))
x[[c(1,3)]]
[1] 14
x[[1]][[3]]
[1] 14
x[[c(2,1)]]
[1] 3.14
Subsetting/Slicing en R: Indexado Lógico
- En R es posible utlizar un vector de booleanos (
TRUE
-FALSE
) como insumo para hacer subsetting/slicing. Tiene que tener la misma longitud que el objeto que vamos a subsetear
- La idea es que los miembros del objeto original que están en la “posición”
TRUE
del vector lógico son extraidos para el slice, mientras que los que están en la “posición” FALSE
, no.
x<-c("aa", "bb", "cc", "dd", "ee")
index<-c(FALSE, TRUE, FALSE, TRUE, FALSE)
x[index]
[1] "bb" "dd"
Subsetting/Slicing en R: Indexado Lógico
- Esto se aplica a cualquier objeto. Es particularmente útil para trabar con data frames, por ejemplo, para quedarnos con casos que cumplen determinada condición. Es similar a hacer un filtro en SPSS o STATA.
Valores NA: eliminarlos
- Puede ser pensado como un caso de indexado lógico:
- Relativamente fácil:
is.na()
devuelve un vector de lógicals
x<-c(1,2,NA,4,NA)
no<-is.na(x)
x1<-x[!no]
x2<-x[!is.na(x)]
x1
[1] 1 2 4
x2
[1] 1 2 4
Valores NA: eliminarlos
- Si hay varias columnas con
NA´s
, entonces puede usarse complete.cases()
x<-c(1,2,NA,4)
y<-c(NA,"a",NA,"n")
m<-cbind(x,y)
m1<-m[complete.cases(m),]
m1
x y
[1,] "2" "a"
[2,] "4" "n"
Funciones en R
- Las funciones se crean con el argumento
function()
y se almacenan como un objeto en R.
fun<-function(argumentos){
#[se hace algún cómputo]
}
- Las funciones pueden pasarse como argumento a otras funciones
- Pueden anidarse: puede definirse una función dentro de una función
- El valor que devuelve una función es la última expresión en el cuerpo de la función
Funciones en R
- Las funciones tienen un input (argumentos) y devuelven un output (valor)
- Argumentos formales: los que están incluidos en la definición de la función
- La función
formal()
devuelve los argumentos formales de una función
- No todas las funciones en R usan argumentos formales
- Los argumentos pueden estar “missing” o tener valores asignados por defecto
Funciones en R: matching de argumentos
- Los argumentos pueden ser matcheados por nombre o por posición. Las siguientes son expresiones equivalentes:
data<-rnorm(100)
sd(data)
[1] 0.9719429
sd(x=data)
[1] 0.9719429
sd(x=data,na.rm=TRUE)
[1] 0.9719429
sd(na.rm=TRUE,x=data)
[1] 0.9719429
sd(na.rm=TRUE, data)
[1] 0.9719429
Funciones en R: matching de argumentos
- Si bien no es recomendable, se pueden “mezclar” matching de argumentos por nombre y por posición. Cuando un argumento se llama por nombre se lo saca de la lista y el resto de los argumentos se llaman por la posición.
args(lm)
function (formula, data, subset, weights, na.action, method = "qr",
model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE,
contrasts = NULL, offset, ...)
NULL
- Por ende, las siguientes expresiones son equivalentes
lm(data=datos, y~x, model=FALSE,1:100)
lm(y~x,data=datos, 1:100, model=FALSE)
Funciones en R: definiendo una función
squared<-function(x,b,y=NULL,...){
return(x**2)
}
squared(2)
[1] 4
- Pueden usarse argumentos que son seteados por defecto en
NULL
- La evaluación de los argumentos es “lazy”, es decir que solamente se evalúan los argumentos a medida que son necesarios.
- En la función anterior el argumento b no se evalúa, entonces llamando
squared(2)
no arroja error porque el 2 se matchea en función a la posición
Funciones en R: definiendo una función
prt<-function(a,b){
print(a)
print(b)
}
prt(2)
[1] 2
Error in print(b) : argument "b" is missing, with no default
- Aquí si arroja un error: b debe ser evaluado luego de
print(a)
Funciones en R: definiendo una función
- Puede agregarse un argumento
"..."
- Se usa para pasar a la función argumentos de otras funciones y no tener que copiar toda la lista de argumentos
- También es útil cuando el número de argumentos de la función no puede ser conocido de antemano.
- Un punto a tener en cuenta: todos los argumentos luego del
"..."
deben ser nombrados explícitamente y no pueden ser matcheados parcialmente.
LS0tDQp0aXRsZTogIkludHJvZHVjY2nDs24gYSBsYSBDaWVuY2lhIGRlIERhdG9zIHBhcmEgQ2llbnTDrWZpY29zIFNvY2lhbGVzIg0Kc3VidGl0bGU6ICJVbmlkYWQgMS4gUHJvZ3JhbWFjacOzbiBFc3RhZMOtc3RpY2EgZW4gUjogSW50cm9kdWNjacOzbiBhIFIgYmFzZSINCmF1dGhvcjogIkRyLiBHZXJtYW4gUm9zYXRpIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyDCv1F1w6kgZXMgUj8NCg0KKiBCw6FzaWNhbWVudGUsIFIgZXMgdW4gImRpYWxlY3RvIiBkZSB1biBsZW5ndWFqZSBkZSBsb3MgYcOxb3MgJzcwOiBTLUxhbmd1YWdlDQoqIFMgZnVlIGNyZWFkbyBlbiBsb3MgKkJlbGwgTGFicyogDQoqIFIgY3JlYWRvIGVuIDE5OTEgcG9yIFJvc3MgSXRoYWthIHkgUm9iZXJ0IEdlbnRlbG1hbg0KKiAxOTkzOiBSIHNlIGFudW5jaWEgcG9yIHByaW1lcmEgdmV6DQoqIDIwMDA6IHNlIGxhbnphIGxhIHByaW1lcmEgdmVyc2nDs24gUiAxLjANCiogMjAxNzogbGEgdmVyc2nDs24gbcOhcyBhY3R1YWwgZXMgbGEgUiAzLjQuMA0KDQoNCiMgQ2FyYWN0ZXLDrXN0aWNhcyBkZSBSDQoqIFNpbnRheGlzIHNpbWlsYXIgYSBTIChlbiBzdSBtb21lbnRvIHBlcm1pdMOtYSBoYWNlciBmw6FjaWwgZWwgcGFzYWplKQ0KKiBDb3JyZSBlbiBjYXNpIGN1YWxxdWllciBTTy9wbGF0YWZvcm1hIChpbmNsdXNvIGVuIFBTMykNCiogSGF5IGFjdHVhbGl6YWNpb25lcyBtdXkgZnJlY3VlbnRlcyAoZG9zIG8gdHJlcyBwb3IgYcOxbykNCiogRnVuY2lvbmFsaWRhZCBtb2R1bGFyOiBoYXkgdW4gY29uanVudG8gZGUgZnVuY2lvbmVzIGLDoXNpY2FzIGFsIGN1YWwgc2UgbGUgdmFuIGFncmVnYW5kbyBkaWZlcmVudGVzIHBhcXVldGVzIGNvbiBmdW5jaW9uYWxpZGFkZXMgZXNwZWNpZmljYXMNCiogTXV5IGJ1ZW5hcyBjYXBhY2lkYWRlcyBncsOhZmljYXM6IG11Y2hvIG1lam9yZXMgeSBtw6FzIHNvZmlzdGljYWRhcyBxdWUgY3VhbHF1aWVyIHNvZnR3YXJlIGRlIHByb2Nlc2FtaWVudG8gZXN0YWTDrXN0aWNvIChleHRyZW1vIGBnZ3Bsb3QyYCkgDQoqIExvIG1lam9yIGRlIHRvZG86IGxhIGNvbXVuaWRhZC4gQ2FkYSBlc3RhZMOtc3RpY28gcXVlIHNlIGxlIG9jdXJyZSB1biBhbGdvcml0bW8gbnVldm8gbG8gcHJvZ3JhbWEgZW4gUg0KKiBMbyBzZWd1bmRvIG1lam9yOiAqX0dSQVRJU18qDQoNCiMgRmlsb3NvZsOtYSBfRnJlZSBzb2Z0d2FyZV8NCiogTGliZXJ0YWQgZGUgY29ycmVyIGVsIHNvZnQgY29uIGN1YWxxdWllciBwcm9ww7NzaXRvIChncmFkbyAwKQ0KKiBMaWJlcnRhZCBkZSBlc3R1ZGlhciBjw7NtbyBmdW5jaW9uYSBlbCBwcm9ncmFtYSB5IGFkYXBhdGFybG8gYSBsYXMgbmVjZXNpZGFlcyAoZ3JhZG8gMSkuIFJlcXVpc2l0bzogZGlzcG9uZXIgZGVsIGPDs2RpZ28gZnVlbnRlDQoqIExpYmVydGFkIGRlIHJlZGlzdHJpYnVpciBjb3BpYXMgKGdyYWRvIDIpDQoqIExpYmVydGFkIGRlIG1lam9yYXIgZWwgc29mdHdhcmUgeSBsYW56YXIgbGFzIG1lam9yYXMgYWwgcMO6YmxpY28gKGdyYWRvIDMpLiBNaXNtbyByZXF1aXNpdG8gcXVlIGdyYWRvIDEuIA0KDQojIERlc3ZlbnRhamFzIGRlIFINCiogRW4gZ2VuZXJhbCwgbG9zIGRhdG9zIHNlIGNhcmdhbiBlbiBtZW1vcmlhIFJBTSAoZXMgbcOhcyBvIG1lbm9zIGxvIG1pc21vIHF1ZSBjYXNpIGN1YWxxdWllciBvdHJvIHNvZnQpLiBBbGd1bm9zIHBhcXVldGVzIGNvbWVuemFyb24gYSBtZWpvcmFyIGVzdGEgY3Vlc3Rpw7NuLg0KKiBMYSBpbXBsZW1lbnRhY2nDs24gZXMuLi4gZXNwb250w6FuZWEuIFNpIGEgbmFkaWUgbGUgaW50ZXJlc2EgZ2VuZXJhciB0dSBhbGdvcml0bW8uLi4gdGUgdG9jYSBhIHZvcy4NCg0KIyBJbnN0YWxhY2nDs24gZGUgUiB5IFJTdHVkaW8gcG9yIHByaW1lcmEgdmV6DQoqIElyIGFsIFtzaXRpbyBkZSBSXShodHRwczovL3d3dy5yLXByb2plY3Qub3JnLykNCiogRWxlZ2lyIGVsIHJlcG9zaXRvcmlvIGRlbCBjdWFsIHNlIGRlc2VhIGRlc2Nhcmdhcg0KKiBEZXNjYXJnYXIgbGEgw7psdGltYSB2ZXJzacOzbiBlc3RhYmxlIChhY3R1YWwgUiAzLjQuMCkNCiogTHVlZ28sIGVzIMO6dGlsIGluc3RhbGFyIGFsZ3VuYSBHVUkgcGFyYSBxdWUgZWwgdHJhYmFqbyBjb24gZWwgY8OzZGlnbyBzZWEgbcOhcyBhbWlnYWJsZS4gDQoqIFJTdHVkaW8gZXMgdW5hIGRlIGxhcyBtw6FzIHVzYWRhcy4NCiogRGVzY2FyZ2FyIGxhIMO6bHRpbWEgdmVyc2nDs24gW2RlbCBzaXRpb10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vKS4NCg0KDQojIEF5dWRhIGVuIFINCipFbiBnZW5lcmFsLCBwYXJhIG9idGVuZXIgYXl1ZGEgaGF5IGRvcyBmb3JtYXMNCg0KYGBge3J9DQpoZWxwKG1lYW4pDQo/bWVhbg0KYGBgDQoNCiMgRG9zIGZvcm1hcyBkZSB0cmFiYWphcg0KMS4gTMOtbmVhIGRlIGNvbWFuZG8NCjIuIFNjcmlwdCAoc2ltaWxhciBhIHVuYSBfc2ludGF4XyBkZSBTUFNTIG8gdW4gXy5kbyBmaWxlXyBkZSBTVEFUQSkNCiogU29uIGLDoXNpY2FtZW50ZSBsbyBtaXNtby4gU29sYW1lbnRlIHF1ZSBlbiBsYSBwcmltZXJhIHNlIHZhIGluZ3Jlc2FuZG8gIGNhZGEgY29tYW5kbyBkZSB1bmEgdmV6Lg0KDQoNCiMgRG9zIGZvcm1hcyBkZSB0cmFiYWphcg0KIVtFc3F1ZW1hIGRlIFJTdHVkaW9dKC4uL1JtYXJrZG93bnMvaW1ncy9JbnRlcmZhel9SU3R1ZGlvLmpwZykNCg0KDQojIFVzbyBkZSBjb21hbmRvcw0KKiBFbiB1c2Ftb3MgYsOhc2ljYW1lbnRlIGluc3RydWNjaW9uZXMgcXVlIHRpcGVhbW9zIG8gYmllbiBlbiBsYSBjb25zb2xhIG8gYmllbiBlbiB1biBzY3JpcHQuDQoqIExvcyByZXN1bHRhZG9zIHZhbiBhIGFwYXJlY2VyIGVuIGxhIGNvbnNvbGENCg0KYGBge3J9DQpzcXJ0KDgxKQ0KYGBgDQoqIGBzcXJ0KClgIGVzIHVuYSBmdW5jacOzbiwgZXMgZGVjaXIsIHVuYSBzZWN1ZW5jaWEgZGUgY8OzZGlnbyBsaXN0YSBwYXJhIHVzYXIuDQogICAgICAgICsgQSBzdSB2ZXosIHRpZW5lIHVuICpwYXJtw6l0cm8qIG8gKmFyZ3VtZW50byosIGxvcyB2YWxvcmVzIHF1ZSB1bmEgZnVuY2nDs24gZXNwZXJhIHF1ZSBlbCB1c3VhcmlvIGRlZmluYQ0KKiBMYXMgZnVuY2lvbmVzIGxsZXZhbiB1biBub21icmUgeSB2YW4gZW50cmUgcGFyw6ludGVzaXMNCg0KIyBVc28gZGUgY29tYW5kb3MNCiogQWhvcmEsIMK/cXXDqSBwYXNhIHNpIGluZ3Jlc28gZXN0bz8NCg0KYGBge3J9DQp4IDwtIHNxcnQoODEpDQpgYGANCg0KKiBObyBzZSBvYnNlcnZhIG5pbmfDum4gcmVzdWx0YWRvIHBvcnF1ZSB1c2Ftb3MgdW4gb3BlcmFkb3IgKHVuYSBmdW5jacOzbikgYDwtYA0KKiBFbiBlbCBjw7NkaWdvIGFudGVyaW9yIHNlIGNyZWEgdW5hIHZhcmlhYmxlICh1biBvYmpldG8pIHkgc2UgbGUgYXNpZ25hIGVsIHZhbG9yIGRlIGBzcXJ0KDgxKWANCiogQ3VhbmRvIHBlZGltb3MuLi4gDQoNCmBgYHtyfQ0KeA0KYGBgDQoNCiMgVXNvIGRlIGNvbWFuZG9zDQoqIFRyYWJhamFyIGNvbiB2YXJpYWJsZXMgbm9zIHBlcm1pdGUgYWxtYWNlbmFyIHZhbG9yZXMgcGFyYSB1c2FybG9zIGRlc3B1w6lzDQoqIEhhY2VuIG51ZXN0cm8gY8OzZGlnbyBtw6FzIGbDoWNpbCBkZSBsZWVyIHkgY29tcGFydGlyIGNvbiBvdHJvcw0KDQoNCiMgSW1wb3J0YW5kbyBEYXRvcyBlbiBSDQoqIFZhcmlhcyBmdW5jaW9uZXMgaW1wb3J0YW50ZXM6IA0KCSsgYHJlYWQudGFibGUoKWAgeSBgcmVhZC5jc3YoKWAgcGFyYSBsZWVyIGRhdG9zIHRhYnVsYXJlcw0KCSsgYHJlYWRMaW5lcygpYCBwYXJhIGxlZXIgbMOtbmVhcyBlbiB1biBhcmNoaXZvIGRlIHRleHRvDQoJKyBgcmVhZC5zcHNzKClgIGVuIGVsIHBhcXVldGUgKmZvcmVpZ24qIHF1ZSB1bmlmaWNhIG11Y2hhcyBsYXMgZnVuY2lvbmVzIGRlIGltcG9ydGFjacOzbiBleHBvcnRhY2nDs24NCg0KIyBJbXBvcnRhbmRvIERhdG9zIGVuIFI6IGByZWFkLnRhYmxlKClgDQoqIERlIGxhcyBtw6FzIHVzYWRhcy4gRWwgZm9ybWF0byBlcyBzaW1pbGFyIGFsIHJlc3RvDQoJKyBgZmlsZWA6IHN0cmluZyBjb24gZWwgbm9tYnJlIHkgbGEgcnV0YSBkZWwgYXJjaGl2byBhIGltcG9ydGFyDQoJKyBgaGVhZGVyYDogbG9naWNhbCBpbmRpY2FkbyBzaSBlbCBhcmNoaXZvIHRpZW5lIGNhYmVjZXJhcw0KCSsgYHNlcGA6IHN0cmluZyBxdWUgaW5kaWNhIGPDs21vIHNlIHNlcGFyYW4gbGFzIGNvbHVtbmFzDQoJKyBgY29sQ2xhc3Nlc2A6IGNoYXJhY3RlciB2ZWN0b3IgcXVlIGluZGljYSBsYSBjbGFzZSBkZSBjYWRhIGNvbHVtbmEgKCJjaGFyYWN0ZXIiLCJpbnRlZ2VyIixldGMuKQ0KCSsgYG5yb3dzYDogY2FudGlkYWQgZGUgZmlsYXMgZW4gZWwgZGF0YXNldA0KCSsgYHNraXBgOiBjYW50aWRhZCBkZSBmaWxhcyBxdWUgaGF5IHF1ZSBzYWx0ZWFyIGNvbnRhbmRvIGRlc2RlIGVsIHByaW5jaXBpbyBlbiBlbCBkYXRhc2V0DQoJKyBgc3RyaW5nQXNGYWN0b3JzYDogbG9naWNhbCBxdWUgaW5kaWNhIHNpIGFsIGltcG9ydGFyIGVsIGFyY2hpdm8gZGViZW4gY29kaWZpY2Fyc2UgbG9zIHN0cmluZ3MgY29tbyBmYWN0b3JzDQoNCiMgSW1wb3J0YW5kbyBEYXRvcyBlbiBSOiBgcmVhZC50YWJsZSgpYA0KKiBTaSBlbCBhcmNoaXZvIG5vIGVzIG11eSBncmFuZGUgcHVlZGUgdXNhcnNlIGRpcmVjdGFtZW50ZToNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmRhdGE8LXJlYWQudGFibGUoInBydWViYS50eHQiKQ0KYGBgDQoNCiogUiB2YSBhIHJlYWxpemFyIGFsZ3VuYXMgdGFyZWFzIGF1dG9tw6F0aWNhbWVudGU6DQoJKyBzYWx0ZWFyIGxhcyBsw61uZWFzIHF1ZSBlbXBpZXphbiBjb24gIw0KCSsgY2FsY3VsYXIgY3XDoW50YXMgZmlsYXMgaGF5IHkgY3XDoW50YSBtZW1vcmlhIG5lY2VzaXRhIHJlc2VydmFyDQoJKyBpZGVudGlmaWNhciBsYSBjbGFzZSBkZSBjYWRhIGNvbHVtbmEgKHNpIHVubyBsbyBkZWZpbmUgY29uIGxvcyBhcmd1bWVudG9zIHJlc3VsdGEgbcOhcyBlZmljaWVudGUpDQoJKyBgcmVhZC5jc3YoKWAgZXMgaWTDqW50aWNvIGEgYHJlYWQudGFibGUoKWAgY29uIGxhIGRpZmVyZW5jaWEgZGUgcXVlIGVsIHNwZXJhZG9yIGVzIGxhIGNvbWEuDQoNCg0KIyBJbXBvcnRhbmRvIERhdG9zICJncmFuZGVzIiBlbiBSOiBgcmVhZC50YWJsZSgpYA0KKiBTaSBzZSBlbmZyZW50YW4gY29uIHVuIGRhdGFzZXQgZ3JhbmRlcyBwdWVkZW4NCgkrIGNvbnN1bHRhciBsYSBkb2N1bWVudGFjacOzbiBkZSBsYSBmdW5jacOzbg0KCSsgU2V0ZWFyIGVsIGFyZ3VtZW50byBgY29sQ2xhc3Nlc2A6IHN1ZWxlIHNlciBtdWNobyBtw6FzIHLDoXBpZG8gZGFkbyBxdWUgUiBubyB0aWVuZSBxdWUgcmVhbGl6YXIgbGEgdGFyZWEgZGUgZGV0ZWN0YXIgbG9zIHRpcG9zIGRlIGNhZGEgY29sdW1uYS4gDQoJKyBTZXRhciBgbnJvd3NgIGVzdG8gYXl1ZGEgYSB1c2FyIG1lbm9zIG1lbW9yaWEuLi4gbm8gYSBxdWUgc2VhIG5lY2VzYXJpYW1lbnRlIG3DoXMgcsOhcGlkby4NCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmluaXQ8LXJlYWQudGFibGUoInBydWViYS50eHQiLG5yb3dzPTEwMCkNCmNsYXNzZXM8LXNhcHBseShpbml0LGNsYXNzKQ0KdG9kb3M8LXJlYWQudGFibGUoInBydWViYS50eHQiDQoJCSAgLGNvbENsYXNzZXMgPSBjbGFzc2VzKQ0KYGBgDQoNCiMgSW1wb3J0YW5kbyBEYXRvcyAiZ3JhbmRlcyIgZW4gUjogYHJlYWQudGFibGUoKWANCiogQWRlbcOhcyBkZSB0b2RvIGVzdG8gZXMgaW1wb3J0YW50ZSBoYWNlciB1bmEgZXN0aW1hY2nDs24gZ3J1ZXNhIGRlIGxhIG1lbW9yaWEgcXVlIHZhIGEgcmVxdWVyaXIgY2FyZ2FyIGVsIGRhdGFzZXQuDQoqIFNpIGxhIG1lbW9yaWEgcmVxdWVyaWRhIGVzIG3DoXMgZGUgbGEgZGlzcG5pYmxlIGVuIGVsIGVsIHNpc3RlbWEuLi4gDQoqIEPDoWxjdWxvIGdydWVzbzogc3Vwb25nYW1vcyB1biBkYXRhZnJhbWUgY29uIDIuMDAwLjAwMCBkZSBmaWxhcyB5IDIwMCBjb2x1bW5hcyB0b2RvcyBkYXRvcyBudW3DqXJpY29zDQoJKyAyLjAwMC4wMDAgeCAyMDAgeCA4IGJ5dGVzL251bWVyaWNzDQoJKyAyLjQwMC4wMDAuMDAwIGJ5dGVzDQoJKyAyLjQwMC4wMDAuMDAwIGJ5dGVzIC8gMl4yMCBieXRlcy9NQg0KCSsgMi4yODgsOCBNQg0KCSsgMi4yNCBHQg0KDQoNCiMgT2JqZXRvcyBlbiBSDQoqIELDoXNpY2FtZW50ZS4uLiBkb25kZSBSIGd1YXJkYSBsb3MgZGF0b3MuDQoqIEhheSBtdWNob3MgdGlwb3MgZGUgb2JqZXRvcyBlbiBSLg0KKiBNdWNob3MgcGFxdWV0ZXMgImdlbmVyYW4iIHN1cyBwcm9waW9zIG9iamV0b3MNCiogTm8gb2JzdGFudGUsIGxhIG1heW9yw61hIGRlIGxhcyB0YXJlYXMgcHVlZGVuIHJlc29sdmVyc2UgY29uIGFsZ3Vub3MgcG9jb3MgdGlwb3MgZGUgb2JqZXRvcyANCiogRW4gUiBoYXkgY2luY28gdGlwb3MgYXTDs21pY29zIGRlIG9iamV0b3MNCgkrIGBjaGFyYWN0ZXJgDQoJKyBgbnVtZXJpY2ANCgkrIGBpbnRlZ2VyYA0KCSsgYGNvbXBsZXhgDQoJKyBgbG9naWNhbGANCg0KIyBPYmpldG9zIGVuIFI6IFZlY3RvcmVzDQoqIEVsIG9iamV0byBtw6FzIGLDoXNpY28gZXMgdW4gdmVjdG9yDQoqIFVuIHZlY3RvciBzb2xvIHB1ZWRlIGNvbnN0ZW5lciBvYmpldG9zIGRlIGxhIG1pc21hIGNsYXNlDQoqIEV4Y2VwY2nDs246IGxpc3RhcywgdGlwbyBlc3BlY2lhbCBkZSB2ZWN0b3IuDQoqIFB1ZWRlbiBjcmVhcnNlIHZlY3RvcmVzIHZhY8Otb3MgY29uIGxhIGZ1bmNpw7NuIGB2ZWN0b3IoKWANCg0KIyBHZW5lcmFkbyB2ZWN0b3Jlcw0KLSBFbCBvcGVyYWRvciBgOmAgc2lydmUgcGFyYSBnZW5lcmFyIHNlY3VlbmNpYXMgZGUgZW50ZXJvcw0KLSBUYW1iacOpbiBwdWVkZSB1c2Fyc2UgbGEgZnVuY2nDs24gYHNlcSgpYCBwYXJhIHNlY3VlbmNpYXMgbcOhcyBjb21wbGVqYXMNCg0KYGBge3J9DQp4PC0xOjEwIA0KeCAgICAgICAgDQpgYGANCg0KIyBHZW5lcmFkbyB2ZWN0b3Jlcw0KKiBMYSBmdW5jacOzbiBgYygpYCBzaXJ2ZSBwYXJhIGNyZWFyIHZlY3RvcmVzIGRlIG9iamV0b3MNCg0KYGBge3J9DQp4PC1jKDEuMiwyLjMsMy44KSAgICAgICAjbnVtw6lyaWNvDQp4PC1jKFRSVUUsIEZBTFNFLCBUUlVFKSAjbMOzZ2ljbw0KeDwtYygiYSIsImIiLCJjIikgICAgICAgI2NoYXJhY3Rlcg0KeDwtOToyMCAgICAgICAgICAgICAgICAgI2ludGVnZXINCmBgYA0KDQoqIFRhbWJpw6luIHB1ZWRlIHVzYXJzZSBsYSBmdW5jacOzbiBgdmVjdG9yKClgIA0KDQpgYGB7cn0NCng8LXZlY3RvcigibnVtZXJpYyIsbGVuZ3RoPTEwKQ0KeA0KYGBgDQoNCiMgTWV6Y2xhbmRvIHZlY3RvcmVzDQoqIEN1YW5kbyBzZSBtZXpjbGFuIGRpZmVyZW50ZXMgb2JqZXRvcyBlbiB1biB2ZWN0b3IgUiBoYWNlIHVzbyBkZSBsYSAiY29lcmNpw7NuIjogc2UgZnVlcnphIGEgbG9zIG9iamV0b3MgYSB0cmFuc2Zvcm1hcnNlIGVuIHVuYSBtaXNtYSBjbGFzZQ0KDQpgYGB7cn0NCnk8LWMoMS4yLCJhIikgICAgICAgICAgICNjaGFyYWN0ZXINCng8LWMoRkFMU0UsIDIpICAgICAgICAgICNudW3DqXJpY28NCng8LWMoRkFMU0UsICJhIikgICAgICAgICNjaGFyYWN0ZXINCmBgYA0KDQojIENvZXJjacOzbiBleHBsw61jaXRhDQoqIFB1ZWRlIGFwbGljYXJzZSBkZSBmb3JtYSBleHBsw61jaXRhIGxhIGNvZXJjaW9uOyBzZSB1c2FuIGxhcyBmdW5jaW9uZXMgYGFzLltjbGFzc11gDQoqIEVzdGEgY29lcmNpb24gcHVlZGUgYXBsaWNhcnNlIGEgb3Ryb3Mgb2JqZXRvcywgbm8gc29sbyBhIHZlY3RvcmVzDQoNCmBgYHtyfQ0KeDwtMDo2DQphcy5jaGFyYWN0ZXIoeCkNCmFzLmxvZ2ljYWwoeCkNCmFzLmNoYXJhY3Rlcih4KQ0KYGBgDQoNCiMgT2JqZXRvcyBlbiBSOiBGYWN0b3JzDQoqIFNlIHVzYW4gcGFyYSByZXByZXNlbnRhciBkYXRvcyBjYXRlZ8Ozcmljb3MgKG5vbWluYWxlcyB1IG9yZGluYWxlcykuIFNvbiBhbGdvIGFzw60gY29tbyB1biB2ZWN0b3IgZGUgaW50ZWdlcnMgY29uIHVuYSBldGlxdWV0YSBhc29jaWFkYSAoYWxnbyBwYXJlY2lkbyBhIFNQU1MpLg0KKiBBbGd1bmFzIGZ1bmNpb25lcyBjb21vIGBsbSgpYCB5IGBnbG0oKWAgaGFjZW4gdW4gdXNvIGVzcGVjaWFsIGRlIGxvcyBmYWN0b3Jlcw0KKiBTdWVsZSBzZXIgbWVqb3IgdXNhciBmYWN0b3JzIHF1ZSBpbnRlZ2VycyBwb3JxdWUgc29uICJhdXRvZGVzY3JpcHRpdm9zIjogIk9jdXBhZG8iLCAiRGVzb2N1cGFkbyIsICJJbmFjdGl2byIgZXMgbWVqb3IgcXVlIDEsMiwzLg0KDQojIE9iamV0b3MgZW4gUjogRmFjdG9ycw0KDQpgYGB7cn0NCng8LWZhY3RvcihjKCJPY3VwIiwiT2N1cCIsIkRlc29jIiwiT2N1cCINCgkgICAgLCJJbmFjdCIpKQ0KeA0KYGBgDQoNCiMgT2JqZXRvcyBlbiBSOiBGYWN0b3JzDQoqIFNlIHB1ZWRlIGdlbmVyYXIgZmFjdG9ycyBvcmRlbmFkb3MgY29uIGVsIGFyZ3VtZW50byBgbGV2ZWxzYC4gDQoNCmBgYHtyfQ0KeDwtZmFjdG9yKGMoIkEiLCJBIiwiTSIsIkIiDQoJICAgICwiQiIpLA0KCSAgbGV2ZWxzPWMoIkIiLCJNIiwiQSIpKQ0KeA0KdGFibGUoeCkNCmBgYA0KDQojIE9iamV0b3MgZW4gUjogRGF0YSBGcmFtZXMNCiogU29uIGxvIG3DoXMgcGFyZWNpZG8gYSBsbyBxdWUgZW50ZW5kZW1vcyBlbiBjaWVuY2lhcyBzb2NpYWxlcyBwb3IgYmFzZSBkZSBkYXRvcy4gU2UgdXNhbiBwYXJhIGFsbWFjZW5hciBkYXRvcyB0YWJ1bGFyZXMuDQoqIFBhcmEgUiBzb24gdW4gdGlwbyBlc3BlY2lhbCBkZSBsaXN0LCBlbiBsYSBjdWFsIGNhZGEgZWxlbWVudG8gZGUgbGEgbGlzdGEgdGllbmUgbGEgbWlzbWEgbG9uZ2l0dWQgKGBsZW5ndGgoKWApDQoqIENhZGEgZWxlbWVudG8gZGUgbGEgbGlzdCBwdWVkZSBzZXIgcGVuc2FkbyBwb3IgdW5hIGNvbHVtbmEgeSBlbCBsYXJnbyBkZSBjYWRhIGVsZW1lbnRvIGRlIGxhIGxpc3QgZXMgZWwgbnJvLiBkZSBmaWxlcyAoYG5yb3coKWApDQoqIEEgZGlmZXJlbmNpYSBkZSBsYXMgbWF0cml4IGNhZGEgZGF0YSBmcmFtZSBwdWVkZSBhbG1hbmNlbmFyIGVsZW1lbnRvcyAoY29sdW1uYXMpIGRlIGRpZmVyZW50ZXMgdGlwb3MuDQoqIFRpZW5lbiB1biBhdHJpYnV0byBlc3BlY2lhbDogYHJvdy5uYW1lc2ANCiogQ3VhbmRvIHZlYW1vcyBpbXBvIHkgZXhwbyBkZSBkYXRvcywgdmVyZW1vcyBxdWUgY3VhbmRvIHVubyBsbGFtYSBhIGxhIGZ1bmNpw7NuIGByZWFkLmNzdigpYCBvIGByZWFkLnRhYmxlKClgIG8gc2ltaWxhcmVzIGxvIHF1ZSBkZXZ1ZWx2ZW4gc29uIHVuIGRhdGEgZnJhbWUNCiogUHVlZGVuIHNlciB0cmFuc2Zvcm1hZG9zIGVuIG1hdHJpY2VzIHVzYW5kbyBgZGF0YS5tYXRyaXgoKWANCg0KIyBPYmpldG9zIGVuIFI6IERhdGEgRnJhbWVzDQoNCmBgYHtyfQ0KeDwtZGF0YS5mcmFtZShpZGVudD0xOjMsIHRyYXRhbWllbnRvPWMoIkQiLCJCIiwiQiIpKQ0KeA0KbnJvdyh4KQ0KbmNvbCh4KQ0Kcm93Lm5hbWVzKHgpDQpgYGANCg0KIyBPYmpldG9zIGVuIFI6IExpc3Rhcw0KKiBMYXMgbGlzdGFzIHNvbiB1bmEgY2xhc2UgZXNwZWNpYWwgZGUgdmVjdG9yOiBwdWVkZW4gY29udGVuZXIgZWxlbWVudG9zIGRlIGN1YWxxdWVpciB0aXBvIGVuIHN1IGludGVyaW9yLg0KKiBTb24gb2JqZXRvcyBtdXkgaW1wb3J0YW50ZXMgZW4gUjogbGEgbWF5b3LDrWEgZGUgbG9zIHJlc3VsdGFkb3MgZGUgbG9zIG1vZGVsb3MgcXVlIHNlIGFwbGljYW4gZW4gUiBkZXZ1ZWx2ZW4gY29tbyBvdXRwdXQgdW4gb2JqZXRvIHF1ZSBlcyB1bmEgbGlzdGEuDQoqIE90cm8gZWplbXBsbzogYWwgaW1wb3J0YXIgZGF0b3MgZGUgdW4gR0lTICguc2hwLCAuZ2VvanNvbiwgZ2VvZGF0YWJhc2UsIGV0Yy4pIFIgbG9zIGludGVycHJldGEgY29tbyB1bmEgbGlzdGEuDQoNCiMgT2JqZXRvcyBlbiBSOiBMaXN0YXMNCg0KYGBge3J9DQp4PC1saXN0KDEsImEiLFRSVUUsIDQuNikNCngNCmBgYA0KDQojIFN1YnNldHRpbmcvU2xpY2luZyBlbiBSDQoqIEFob3JhLCDCv2PDs21vIGV4dHJhZW1vcyBwYXJ0ZXMgZGUgbG9zIG9iamV0b3M/IMK/Q8OzbW8gYXBsaWNhbW9zIHRyYW5zZm9ybWFjaW9uZXMgYSB1biBzdWJjb25qdW50byBkZSB1biBkZXRlcm1pbmFkbyBvYmpldG8/DQoqIEEgYW1iYXMgb3BlcmFjaW9uZXMgc2UgbGFzIGxsYW1hICAqc2xpY2luZyogbyAqc3Vic2V0dGluZyosIHJlc3BlY3RpdmFtZW50ZQ0KKiBDYWRhIG9iamV0byB0aWVuZSBhbGd1bmEgZm9ybWEgZGUgc3Vic2V0dGluZyBvIHNsaWNpbmcuIEhheSB2YXJpb3Mgb3BlcmFkb3Jlcw0KKiBgW2AgZGV2dWVsdmUgc2llbXByZSB1biBvYmpldG8gZGUgbGEgbWlzbWEgY2xhc2UgcXVlIGVsIG9yaWdpbmFsDQoqIGBbW2Agc2UgdXNhIHBhcmEgZXh0cmFlciBkYXRvcyBkZSB1biBkYXRhZnJhbWUgbyB1bmEgbGlzdGEuIE5vIG5lY2VzYXJpYW1lbnRlIGRldnVlbHZlIHNpZW1wcmUgdW4gb2JqZXRvIGRlIGxhIG1pc21hIGNsYXNlIHF1ZSBlbCBvcmlnaW5hbA0KKiBgJGAgc2UgdXNhIHBhcmEgZXh0cmFlciBlbGVtZW50b3MgZGUgdW4gZGF0YWZyYW1lIG8gdW5hIGxpc3RhIHBvciBub21icmUuDQoNCiMgU3Vic2V0dGluZy9TbGljaW5nIGVuIFI6IFZlY3RvcmVzDQoqIE9yZGVuDQoNCmBgYHtyfQ0KeDwtMToxMCANCnhbMTo0XQ0KeFs0OjFdDQp4W2MoMSw1LDgpXQ0KeFstYygxLDUsOCldDQpgYGANCg0KIyBTdWJzZXR0aW5nL1NsaWNpbmcgZW4gUjogRGF0YSBGcmFtZXMNCg0KYGBge3J9DQp4PC0xOjQNCnk8LWMoIloiLCJaIiwiRSIsIkYiKQ0KejwtYyhUUlVFLEZBTFNFLEZBTFNFLEZBTFNFKQ0KZGY8LWFzLmRhdGEuZnJhbWUoY2JpbmQoeCx5LHopKQ0KZGZbLDJdDQpkZiR4DQpkZiR6WzFdDQpgYGANCg0KIyBTdWJzZXR0aW5nL1NsaWNpbmcgZW4gUjogTGlzdGFzDQoNCmBgYHtyfQ0KeDwtbGlzdChmb28gPSAxOjQsIGJhciA9IDAuNikNCnhbMV0NCnhbWzFdXQ0KeCRiYXINCnhbJ2JhciddDQp4W1snYmFyJ11dDQpgYGANCg0KIyBTdWJzZXR0aW5nL1NsaWNpbmcgZW4gUjogTGlzdGFzDQoNCmBgYHtyfQ0KeDwtbGlzdChmb28gPSAxOjQsIGJhciA9IDAuNiwgYmF6ID0gJ2hvbGEnKQ0KeFtjKDEsMyldDQpgYGANCg0KIyBTdWJzZXR0aW5nL1NsaWNpbmcgZW4gUjogTGlzdGFzDQoqIEVsIG9wZXJhZG9yIGBbW11dYCBwdWVkZSB1c2Fyc2UgY29uIMOtbmRpY2VzIGNhbGN1bGFkb3MuIGAkYCBzb2xvIHB1ZWRlIHVzYXJzZSBjb24gbm9tYnJlcw0KDQpgYGB7cn0NCng8LWxpc3QoZm9vID0gMTo0LCBiYXIgPSAwLjYsIGJheiA9ICdob2xhJykNCm5hbWU8LSdiYXInICMgSW5kaWNlIGNvbXB1dGFkbw0KeFtbbmFtZV1dDQp4JG5hbWUNCngkZm9vDQpgYGANCg0KIyBTdWJzZXR0aW5nL1NsaWNpbmcgZW4gUjogTGlzdGFzIGNvbiBlbGVtZW50b3MgYW5pZGFkb3MNCiogRWwgb3BlcmFkb3IgYFtbXV1gIHB1ZWRlIHRvbWFyIGN1YWxxdWllciBzZWN1ZW5jaWEgZGUgZW50ZXJvcyANCg0KYGBge3J9DQp4PC1saXN0KGEgPSBsaXN0KDEwLDEyLDE0KSwgYiA9IGMoMy4xNCwgMi44MSkpDQp4W1tjKDEsMyldXQ0KeFtbMV1dW1szXV0NCnhbW2MoMiwxKV1dDQpgYGANCg0KIyBTdWJzZXR0aW5nL1NsaWNpbmcgZW4gUjogSW5kZXhhZG8gTMOzZ2ljbyANCiogRW4gUiBlcyBwb3NpYmxlIHV0bGl6YXIgdW4gdmVjdG9yIGRlIGJvb2xlYW5vcyAoYFRSVUVgLWBGQUxTRWApIGNvbW8gaW5zdW1vIHBhcmEgaGFjZXIgc3Vic2V0dGluZy9zbGljaW5nLiBUaWVuZSBxdWUgdGVuZXIgbGEgbWlzbWEgbG9uZ2l0dWQgcXVlIGVsIG9iamV0byBxdWUgdmFtb3MgYSBzdWJzZXRlYXINCiogTGEgaWRlYSBlcyBxdWUgbG9zIG1pZW1icm9zIGRlbCBvYmpldG8gb3JpZ2luYWwgcXVlIGVzdMOhbiBlbiBsYSAicG9zaWNpw7NuIiBgVFJVRWAgZGVsIHZlY3RvciBsw7NnaWNvIHNvbiBleHRyYWlkb3MgcGFyYSBlbCBzbGljZSwgbWllbnRyYXMgcXVlIGxvcyBxdWUgZXN0w6FuIGVuIGxhICJwb3NpY2nDs24iIGBGQUxTRWAsIG5vLiANCg0KYGBge3J9DQp4PC1jKCJhYSIsICJiYiIsICJjYyIsICJkZCIsICJlZSIpDQppbmRleDwtYyhGQUxTRSwgVFJVRSwgRkFMU0UsIFRSVUUsIEZBTFNFKQ0KeFtpbmRleF0NCmBgYA0KDQojIFN1YnNldHRpbmcvU2xpY2luZyBlbiBSOiBJbmRleGFkbyBMw7NnaWNvIA0KKiBFc3RvIHNlIGFwbGljYSBhIGN1YWxxdWllciBvYmpldG8uIEVzIHBhcnRpY3VsYXJtZW50ZSDDunRpbCBwYXJhIHRyYWJhciBjb24gZGF0YSBmcmFtZXMsIHBvciBlamVtcGxvLCBwYXJhIHF1ZWRhcm5vcyBjb24gY2Fzb3MgcXVlIGN1bXBsZW4gZGV0ZXJtaW5hZGEgY29uZGljacOzbi4gRXMgc2ltaWxhciBhIGhhY2VyIHVuIGZpbHRybyBlbiBTUFNTIG8gU1RBVEEuDQoNCg0KIyBWYWxvcmVzIE5BOiBlbGltaW5hcmxvcw0KKiBQdWVkZSBzZXIgcGVuc2FkbyBjb21vIHVuIGNhc28gZGUgaW5kZXhhZG8gbMOzZ2ljbzoNCiogUmVsYXRpdmFtZW50ZSBmw6FjaWw6IGBpcy5uYSgpYCBkZXZ1ZWx2ZSB1biB2ZWN0b3IgZGUgbMOzZ2ljYWxzIA0KDQpgYGB7cn0NCng8LWMoMSwyLE5BLDQsTkEpDQpubzwtaXMubmEoeCkNCngxPC14WyFub10NCngyPC14WyFpcy5uYSh4KV0NCngxDQp4Mg0KYGBgDQoNCiMgVmFsb3JlcyBOQTogZWxpbWluYXJsb3MNCiogU2kgaGF5IHZhcmlhcyBjb2x1bW5hcyBjb24gYE5BwrRzYCwgZW50b25jZXMgcHVlZGUgdXNhcnNlIGBjb21wbGV0ZS5jYXNlcygpYA0KDQpgYGB7cn0NCng8LWMoMSwyLE5BLDQpDQp5PC1jKE5BLCJhIixOQSwibiIpDQptPC1jYmluZCh4LHkpDQptMTwtbVtjb21wbGV0ZS5jYXNlcyhtKSxdDQptMQ0KYGBgDQoNCiMgRnVuY2lvbmVzIGVuIFINCiogTGFzIGZ1bmNpb25lcyBzZSBjcmVhbiBjb24gZWwgYXJndW1lbnRvIGBmdW5jdGlvbigpYCB5IHNlIGFsbWFjZW5hbiBjb21vIHVuIG9iamV0byBlbiBSLiANCg0KYGBge3J9DQpmdW48LWZ1bmN0aW9uKGFyZ3VtZW50b3Mpew0KCSNbc2UgaGFjZSBhbGfDum4gY8OzbXB1dG9dDQp9DQpgYGANCg0KKiBMYXMgZnVuY2lvbmVzIHB1ZWRlbiBwYXNhcnNlIGNvbW8gYXJndW1lbnRvIGEgb3RyYXMgZnVuY2lvbmVzDQoqIFB1ZWRlbiBhbmlkYXJzZTogcHVlZGUgZGVmaW5pcnNlIHVuYSBmdW5jacOzbiBkZW50cm8gZGUgdW5hIGZ1bmNpw7NuDQoqIEVsIHZhbG9yIHF1ZSBkZXZ1ZWx2ZSB1bmEgZnVuY2nDs24gZXMgbGEgw7psdGltYSBleHByZXNpw7NuIGVuIGVsIGN1ZXJwbyBkZSBsYSBmdW5jacOzbg0KDQojIEZ1bmNpb25lcyBlbiBSDQoqIExhcyBmdW5jaW9uZXMgdGllbmVuIHVuIGlucHV0IChhcmd1bWVudG9zKSB5IGRldnVlbHZlbiB1biBvdXRwdXQgKHZhbG9yKQ0KCSsgQXJndW1lbnRvcyBmb3JtYWxlczogbG9zIHF1ZSBlc3TDoW4gaW5jbHVpZG9zIGVuIGxhIGRlZmluaWNpw7NuIGRlIGxhIGZ1bmNpw7NuDQoJKyBMYSBmdW5jacOzbiBgZm9ybWFsKClgIGRldnVlbHZlIGxvcyBhcmd1bWVudG9zIGZvcm1hbGVzIGRlIHVuYSBmdW5jacOzbg0KCSsgTm8gdG9kYXMgbGFzIGZ1bmNpb25lcyBlbiBSIHVzYW4gYXJndW1lbnRvcyBmb3JtYWxlcw0KCSsgTG9zIGFyZ3VtZW50b3MgcHVlZGVuIGVzdGFyICJtaXNzaW5nIiBvIHRlbmVyIHZhbG9yZXMgYXNpZ25hZG9zIHBvciBkZWZlY3RvDQoJDQojIEZ1bmNpb25lcyBlbiBSOiBtYXRjaGluZyBkZSBhcmd1bWVudG9zDQoqIExvcyBhcmd1bWVudG9zIHB1ZWRlbiBzZXIgbWF0Y2hlYWRvcyBwb3Igbm9tYnJlIG8gcG9yIHBvc2ljacOzbi4gTGFzIHNpZ3VpZW50ZXMgc29uIGV4cHJlc2lvbmVzIGVxdWl2YWxlbnRlczoNCg0KYGBge3J9DQpkYXRhPC1ybm9ybSgxMDApDQpzZChkYXRhKQ0Kc2QoeD1kYXRhKQ0Kc2QoeD1kYXRhLG5hLnJtPVRSVUUpDQpzZChuYS5ybT1UUlVFLHg9ZGF0YSkNCnNkKG5hLnJtPVRSVUUsIGRhdGEpDQpgYGANCg0KIyBGdW5jaW9uZXMgZW4gUjogbWF0Y2hpbmcgZGUgYXJndW1lbnRvcw0KKiBTaSBiaWVuIG5vIGVzIHJlY29tZW5kYWJsZSwgc2UgcHVlZGVuICJtZXpjbGFyIiBtYXRjaGluZyBkZSBhcmd1bWVudG9zIHBvciBub21icmUgeSBwb3IgcG9zaWNpw7NuLiBDdWFuZG8gdW4gYXJndW1lbnRvIHNlIGxsYW1hIHBvciBub21icmUgc2UgbG8gc2FjYSBkZSBsYSBsaXN0YSB5IGVsIHJlc3RvIGRlIGxvcyBhcmd1bWVudG9zIHNlIGxsYW1hbiBwb3IgbGEgcG9zaWNpw7NuLiANCg0KYGBge3J9DQphcmdzKGxtKQ0KYGBgDQoNCiogUG9yIGVuZGUsIGxhcyBzaWd1aWVudGVzIGV4cHJlc2lvbmVzIHNvbiBlcXVpdmFsZW50ZXMNCg0KYGBge3J9DQpsbShkYXRhPWRhdG9zLCB5fngsIG1vZGVsPUZBTFNFLDE6MTAwKQ0KbG0oeX54LGRhdGE9ZGF0b3MsIDE6MTAwLCBtb2RlbD1GQUxTRSkNCmBgYA0KDQojIEZ1bmNpb25lcyBlbiBSOiBkZWZpbmllbmRvIHVuYSBmdW5jacOzbg0KDQpgYGB7cn0NCnNxdWFyZWQ8LWZ1bmN0aW9uKHgsYix5PU5VTEwsLi4uKXsNCglyZXR1cm4oeCoqMikNCn0NCnNxdWFyZWQoMikNCmBgYA0KDQoqIFB1ZWRlbiB1c2Fyc2UgYXJndW1lbnRvcyBxdWUgc29uIHNldGVhZG9zIHBvciBkZWZlY3RvIGVuIGBOVUxMYA0KKiBMYSBldmFsdWFjacOzbiBkZSBsb3MgYXJndW1lbnRvcyBlcyAibGF6eSIsIGVzIGRlY2lyIHF1ZSBzb2xhbWVudGUgc2UgZXZhbMO6YW4gbG9zIGFyZ3VtZW50b3MgYSBtZWRpZGEgcXVlIHNvbiBuZWNlc2FyaW9zLg0KCSsgRW4gbGEgZnVuY2nDs24gYW50ZXJpb3IgZWwgYXJndW1lbnRvIGIgbm8gc2UgCWV2YWzDumEsIGVudG9uY2VzIGxsYW1hbmRvIGBzcXVhcmVkKDIpYCBubyBhcnJvamEgCQllcnJvciBwb3JxdWUgZWwgMiBzZSBtYXRjaGVhIGVuIGZ1bmNpw7NuCWEgbGEgcG9zaWNpw7NuDQoNCiMgRnVuY2lvbmVzIGVuIFI6IGRlZmluaWVuZG8gdW5hIGZ1bmNpw7NuDQoNCmBgYHtyfQ0KcHJ0PC1mdW5jdGlvbihhLGIpew0KCXByaW50KGEpDQoJcHJpbnQoYikNCn0NCnBydCgyKQ0KYGBgDQoqIEFxdcOtIHNpIGFycm9qYSB1biBlcnJvcjogYiBkZWJlIHNlciBldmFsdWFkbyBsdWVnbyBkZSBgcHJpbnQoYSlgDQoNCiMgRnVuY2lvbmVzIGVuIFI6IGRlZmluaWVuZG8gdW5hIGZ1bmNpw7NuDQoqIFB1ZWRlIGFncmVnYXJzZSB1biBhcmd1bWVudG8gYCIuLi4iYA0KKiBTZSB1c2EgcGFyYSBwYXNhciBhIGxhIGZ1bmNpw7NuIGFyZ3VtZW50b3MgZGUgb3RyYXMgZnVuY2lvbmVzIHkgbm8gdGVuZXIgcXVlIGNvcGlhciB0b2RhIGxhIGxpc3RhIGRlIGFyZ3VtZW50b3MNCiogVGFtYmnDqW4gZXMgw7p0aWwgY3VhbmRvIGVsIG7Dum1lcm8gZGUgYXJndW1lbnRvcyBkZSBsYSBmdW5jacOzbiBubyBwdWVkZSBzZXIgY29ub2NpZG8gZGUgYW50ZW1hbm8uDQoqIFVuIHB1bnRvIGEgdGVuZXIgZW4gY3VlbnRhOiB0b2RvcyBsb3MgYXJndW1lbnRvcyBsdWVnbyBkZWwgYCIuLi4iYCBkZWJlbiBzZXIgbm9tYnJhZG9zIGV4cGzDrWNpdGFtZW50ZSB5IG5vIHB1ZWRlbiBzZXIgbWF0Y2hlYWRvcyBwYXJjaWFsbWVudGUuDQo=