Curso Introducción a R para RRHH

Bienvenidas y bienvenidos al curso Introducción a R para RRHH 👩‍💻. R es un lenguaje open source, es decir que es abierto, gratuito, y que además la comunidad de usuarias y usuarios contribuye a su mantenimiento.

Antes de empezar

Si quieren compartir imágenes del curso en redes sociales lo pueden hacer, tanto si les gustó como si no les gustó.

Los hashtags que pueden usar son

  • #R4HR

  • #RStats_ES

  • #data4hr

  • #BetterWithData

  • #PeopleAnalytics

  • … y el hashtag que quieran

También pueden arrobar a Data 4HR y unirse a los grupos

🕹️ Discord

🤝 Slack R4HR

Administración del curso

Algunas cosas que tienen que saber:

  • El link de Zoom va a ser siempre el mismo. 🛰️
  • No hace falta que tengan la cámara encendida. 🕵️
  • Las clases las grabo y las comparto de manera privada por Youtube. 🎥
  • Pueden usar documentos compartidos para tomar notas. 📖
  • Súmense al grupo de Slack ya que todas las consultas las hacemos por ahí. 🤜🤛
  • Pueden interrumpir las veces que quieran. ✋
  • También pueden poner su pregunta en el chat poniendo #P antes de la consulta.

R

R es un lenguaje de código abierto, que nació para el análisis estadístico de datos.

Su popularidad se debe gracias a la enorme comunidad global que permanentemente actualiza paquetes y genera contenido. Su principal base de usuarios no vienen del mundo de la programación sino personas de distintas disciplinas, lo que hace que su sintaxis sea más simple. Aparte de los desarrollos de machine learning que se hacen en todo el mundo, te podés encontrar con:

  • La BBC y The Economist construyendo paquetes y temas para los gráficos.
  • Airbnb usándolo para sus investigaciones, modelos y reportes.
  • Yo lo uso para los informes de nuestros proyectos…

Ventajas de R

Aprender R para RRHH tiene inagotables ventajas:

  • Hacer análisis predictivos (Clase 3 de este curso!)

  • Reproducir el trabajo

  • Más capacidad de procesamiento que Excel

  • Automatizar el trabajo

  • Analizar todo tipo de datos

    • Tablas
    • Bases de Datos, Sistemas de Gestión
    • Texto libre
    • Imágenes
  • Enviar mails con reportes automáticos

  • Escribir libros, blogs, CV

  • La comunidad de R

La comunidad de R

R cuenta con una red de soporte muy amplia, sólida y solidaria, especialmente en Latinoamérica. Prácticamente en todos los países del continente hay RUG (R Users Group) para el público en general, y toda una red de R-Ladies que promueven la diversidad de mujeres y de minorías, para generar mejores oportunidades laborales.

Pueden explorar todos los RUG https://benubah.github.io/r-community-explorer/rladies.html

R4HR Club de R para RRHH

En esta misma línea, desde Mayo de 2020 empezamos a desarrollar una comunidad específica para el mundo de RRHH, donde nos reunimos el primer sábado de cada mes de 10:00 a 11:30 hs. (hora de Argentina) para aprender a programar en este lenguaje.

Revisamos paquetes, exploramos formas de hacer análisis predictivos, y el plan es poder desarrollar una red similar a los RUGs y R-Ladies.

Desde agosto 2021 somos una comunidad reconocida y sponsoreada por R Consortium una organización dedicada a promover y a contribuir con recursos para la adopción y el desarrollo de R.

Todo el contenido generado por los usuarios de R4HR se puede utilizar gratuitamente citando la fuente

Licencia Creative Commons
Esta obra está bajo una Licencia Creative Commons Atribución 4.0 Internacional.

Aprender a programar

Al principio programar es un acto de fe. Vemos que pasan cosas, que escribo algo y una cosa aparece en la pantalla pero no sabemos por qué, cómo, y por qué no me sale de nuevo.

En particular R, y sus paquetes, permiten expresar lo que quiero programar mayormente de una forma similar a la que lo haría si tuviera que dar las instrucciones verbalmente a alguien. Y a diferencia de otros lenguajes como html por ejemplo, la sintaxis del código (las instrucciones) es mucho más simple (entren a cualquier página web y aprieten F12).

También les va a pasar con este curso que muchas cosas les va a resultar más sencillas de hacer en Excel. O que tal vez buscando en internet encontraste otra forma de hacer las cosas, y está bien que así sea.

Lo único que van a necesitar para programar es:

  • Paciencia
  • Constancia
  • Curiosidad

Y con el tiempo, y la práctica, un día tenés una epifanía y todo lo que volcas en un código cobra sentido.

Crear proyectos en RStudio

Trabajar con proyectos en RStudio hace que todo el trabajo sea más sencillo. Los proyectos crean una carpeta en nuestra PC en donde se almacenarán los archivos, tablas, scripts, y hace que todo sea más organizado.

Para crear un proyecto tenés podés entrar en:

  • File
  • New project

Y luego poner el nombre de la carpeta.

Se abre una ventana, hacemos click en New Project, luego en Ok y finalmente en la tercera ventana podemos darle un nombre al proyecto, y asignar la carpeta en la que queremos guardarlo con el botón Browse.

Objetos

Por definición, R es un lenguaje orientado a objetos. Esto significa que a una tabla, un valor, una variable, etc. le voy a poner un nombre, y esa cosa que ahora tiene nombre se llama objeto.

Esto nos simplifica un montón la vida. La asignación la hacemos con el símbolo <- que simula una flecha hacia la izquierda. También se puede hacer con el símbolo = pero como muchas veces lo utilizamos para configurar un parámetro de una función usamos “la flechita”.

Valores

# Esto es un comentario

objeto_1 <- 2021
objeto_2 <- 1979 # Poner año de nacimiento o ingreso al trabajo

# Para ver el resultado del objeto uso las teclas Ctrl + Enter
# También puedo usar el ícono  "Run"
objeto_1
## [1] 2021
# Podemos hacer operaciones con objetos
objeto_1 - objeto_2
## [1] 42

Vectores

Uno de los objetos más utilizados son los vectores que son conjuntos de valores, numéricos, de texto, o combinados. Para crear un vector necesitamos usar la función c() que significa combine.

Los vectores pueden tener texto o número.

# Vector de Texto
nombre <- c("Gustavo", "Charly", "Zeta") # Las variables de texto van siempre con comillas.

# Ver el contenido
nombre
## [1] "Gustavo" "Charly"  "Zeta"
# Seleccionar un elemento del vector 
nombre[1]
## [1] "Gustavo"
# Vector numérico
anio_nacimiento <- c(1959, 1963, 1958)

Los vectores son muy utilizados para filtrar elementos, cambiar nombres de tablas, etc..

Data frames

Los data frames son tables que contienen filas (observaciones) y columnas (variables).

Podemos crear un data frame usando vectores que tengan la misma cantidad de elementos usando la función data.frame(). Dentro de la función podemos poner los nombres

# Crear el data frame soda_stereo usando los vectores nombre y anio_nacimiento
soda.stereo <- data.frame(nombre, anio_nacimiento)
  
# Seleccionar elementos de un vector usando la sintaxis: nombre_dataframe[fila, columna]
soda.stereo[1,2]
## [1] 1959

Normalmente los data frames los cargamos de algún archivo. Por ejemplo, carguemos el archivo rotacion.csv usando la función read.csv.

# Crear un objeto llamado rotacion leyendo el archivo rotacion.csv
rotacion <- read.csv("rotacion.csv",
                     sep = ";")

# Ver el contenido del data frame
rotacion
##    Periodo Movimientos Cantidades
## 1     2010    Ingresos          9
## 2     2010     Egresos          2
## 3     2011    Ingresos         84
## 4     2011     Egresos         14
## 5     2012    Ingresos         44
## 6     2012     Egresos         17
## 7     2013    Ingresos         44
## 8     2013     Egresos         15
## 9     2014    Ingresos         60
## 10    2014     Egresos         14
## 11    2015    Ingresos         36
## 12    2015     Egresos         27
## 13    2016    Ingresos         14
## 14    2016     Egresos         14

Notas sobre el nombre de los objetos

Si bien a los objetos podemos nombrarlos de cualquier manera, hay buenas prácticas recomendadas. Por ejemplo:

  • Usar nombres cortos, y descriptivos
  • Los nombres tienen que empezar con una letra.
  • Los símbolos válidos que se pueden usar son el guión bajo (_) o el punto (.).
  • Se pueden usar mayúsculas, pero es más cómodo que siempre usar minúsculas.
  • Recuerden que R es case sensitive, o sea que hay que prestar atención a mayúsculas y mínusculas
# Nombres válidos de objetos
## Mejores opciones
respuestas_clima

respuestas.clima

# Esto funciona pero es menos recomendable
RespuestasClima

Por otro lado:

  • Los objetos no pueden comenzar con números.
  • No se pueden usar acentos, ñ, u otros caracteres especiales.
  • No se recomiendan usar letras o nombres de funciones.

Paquetes

Los paquetes o librerías son extensiones desarrolladas por la comunidad o por empresas que facilitan el uso de R y expanden sus capacidades. En este encuentro vamos a usar los siguientes paquetes:

  • readxl: Desarrollado por Hadley Wickham and Jennifer Bryan. Link a la web.
  • openxlsx: Desarrollado por Philipp Schauberger and Alexander Walker. Link a la web.
  • tidyverse: Desarrollado por Hadley Wickham y muchos más. Link a la web.

Este último paquete, tidyverse es una colección de paquetes que permiten realizar muchas tareas de exploración, limpieza y transformación de datos.

Para utilizar un paquete, lo primero que tenemos que hacer es instalarlos. Eso lo hacemos con la función install.packages() y dentro del paréntesis tenemos que poner el nombre del paquete. Tengan en cuenta que:

  • R es un lenguaje case sensitive o sea que hay que prestar atención a mayúsculas y minúsculas.

  • Para instalar los paquetes hay que usar comillas

Este es un paso que hacemos una sola vez por computadora.

Para correr el código se tienen que parar en la línea de código que quieren usar y apretar las teclas Ctrl + Enter o bien el triángulo verde (como si fuera un ícono de “Play” ▶️)

# Instalar los paquetes readxl y tidyverse
install.packages("tidyverse")
install.packages("readxl")

Esto lo que hace es instalar paquetes desde CRAN, que es un repositorio donde se publican los paquetes, asegurando un estándar de calidad y de documentación que hace que trabajar con cualquier paquete de CRAN sea seguro.

Para usar las funciones de los paquetes que instalamos, ahora tenemos que cargarlos. Esto lo que hace es de alguna manera “activar” el paquete y que podamos empezar sus funciones.

Para cargar un paquete tenemos que usar la función library(). Recuerden prestar atención a las mayúsculas y minúsculas. Ahora no son necesarias las comillas.

# Cargar los paquetes readxl y tidyverse
library(tidyverse)
library(readxl)

Una de las formas en las que nos damos cuenta que el paquete está instalado es cuando empezamos a escribir su nombre y nos aparece el nombre del paquete para autocompletar. Esta es una de las ventajas de trabajar en RStudio.

Recuerden:

La instalación de los paquetes se hace una sola vez por computadora.

La carga de los paquetese se hace cada vez que se abre un script.

Instalar y cargar introR4hr

Para este curso desarrollamos un paquete, introR4hr que consiste en una serie de tutoriales interactivos que iremos compartiendo a lo largo del curso.

introR4hr no es un paquete publicado en CRAN por lo cual hay que descargar la versión de desarrollo desde el repositorio de GitHub y seguir los siguientes pasos:

# Instalar los paquetes remotes y learnr
install.packages("remotes")
install.packages("learnr")

# Instalar el paquete introR4hr desde GitHub
remotes::install_github("chechoid/introR4hr")

La notación remotes::install_github() reemplaza parcialmente a la función library() ya que no carga el paquete (ahorrando memoria) y sólo utiliza la función que está invocando.

Por eso luego usamos la siguiente sintaxis para cargar un tutorial interactivo.

learnr::run_tutorial("sesion0", "introR4hr")

Donde:

  • sesion0 es el nombre del tutorial
  • introR4hr es el nombre del paquete

Carga de datos

Como gran parte de los datos que vamos a usar en la vida son archivos de Excel vamos a usar la función read_excel() del paquete readxl para levantar dos archivos: el archivo maestro.xlsx que contiene un listado de empleados de una empresa, y el archivo salarios.xlsx que contiene el sueldo base de algunos de los empleados del listado.

# Cargar el archivo maestro.xlsx
maestro <- read_excel("maestro.xlsx")

# Cargar el archivo salarios.xlsx
salarios <- read_excel("salarios.xlsx")

Los archivos no necesariamente tienen que estar en una computadora. También se pueden cargar archivos desde internet, por ejemplo, desde repositorios de GitHub:

# Cargar un archivo desde GitHub
encuesta <- read.csv("https://raw.githubusercontent.com/r4hr/kiwi2020/main/rh_ar.csv",
                     sep = ";",
                     encoding = "UTF-8")

Otra alternativa para leer archivos .csv cuando el delimitador entre campos no es una coma (,) es usar la función read_delim() del paquete readr que viene en tidyverse.

encuesta <- read_delim("https://raw.githubusercontent.com/r4hr/kiwi2020/main/rh_ar.csv",
                       delim = ";") # Indicamos el delimitador de campos

Una función que nos permite ver el contenido de estos data frames es la función View().

Otra función interesante para explorar el contenido de un data frame es la función glimpse() del paquete dplyr que forma parte del paquete tidyverse.

# Explorar los dataframes maestro, salarios y encuesta
glimpse(maestro)
## Rows: 522
## Columns: 8
## $ ID           <dbl> 180, 242, 260, 277, 278, 304, 314, 315, 345, 362, 375, 38…
## $ ANTIGUEDAD   <dbl> 38, 37, 36, 36, 36, 35, 35, 35, 40, 33, 32, 32, 32, 32, 3…
## $ EDAD         <dbl> 63, 64, 61, 64, 59, 57, 54, 56, 59, 59, 55, 62, 52, 52, 6…
## $ ESTADO_CIVIL <chr> "C", "C", "C", "S", "C", "K", "C", "K", "C", "S", "S", "S…
## $ HIJOS        <dbl> 1, 2, 2, 4, 0, 1, 4, 3, 2, 1, 5, 0, 4, 2, 3, 2, 2, 0, 4, …
## $ AREA         <chr> "PRODUCCION 1", "PRODUCCION 1", "PRODUCCION 1", "PRODUCCI…
## $ ID_CAT       <chr> "5", "5", "5", "5", "4", "4", "4", "4", "5", "4", "5", "5…
## $ N_CATEG      <chr> "OP. CAT. 5", "OP. CAT. 5", "OP. CAT. 5", "OP. CAT. 5", "…
glimpse(salarios)
## Rows: 135
## Columns: 3
## $ ID     <dbl> 597, 789, 791, 804, 816, 820, 851, 864, 867, 872, 892, 898, 918…
## $ PUESTO <chr> "TEC. MANTENIMIENTO", "VOLANTE", "JEFE", "LIDER DE EQUIPO", "LI…
## $ SUELDO <dbl> 25129, 24474, 44344, 34235, 26401, 26682, 23851, 25811, 25877, …
glimpse(encuesta)
## Rows: 528
## Columns: 45
## $ Marca.temporal             <dttm> 2020-09-30 20:29:06, 2020-09-30 21:45:26, …
## $ genero                     <chr> "Femenino", "Masculino", "Femenino", "Femen…
## $ genero_diverso             <chr> "No", "Si", "No", "Si", "No", "No", "No", "…
## $ edad                       <dbl> 35, 33, 24, 32, 32, 26, 29, 30, 34, 41, 37,…
## $ discapacidad               <chr> "No tengo ninguna discapacidad", "No tengo …
## $ nivel_formacion            <chr> "Universitario completo", "Universitario en…
## $ carrera_grado              <chr> "RRHH / RRLL / RRTT", "RRHH / RRLL / RRTT",…
## $ tipo_universidad           <chr> "Universidad Privada", "Universidad Pública…
## $ pais                       <chr> "Argentina", "Argentina", "Argentina", "Arg…
## $ provincia                  <chr> "Buenos Aires", "Chaco", "Buenos Aires", "B…
## $ trabajo                    <chr> "Relación de Dependencia", "Relación de Dep…
## $ rubro                      <chr> "Comercio", "Otros", "Tecnología", "Hoteler…
## $ dotacion                   <dbl> 1433, 6000, 270, 700, 7500, 100, 700, 92, 5…
## $ origen_capital             <chr> "Nacional", "Multinacional", "Nacional", "N…
## $ dotacion_rh                <dbl> 10, 55, 7, 4, 25, 3, 9, 2, 10, 1, 3, 7, 25,…
## $ puesto                     <chr> "Analista", "HRBP", "Analista", "Gerente", …
## $ tipo_contratacion          <chr> "Full time", "Full time", "Full time", "Ful…
## $ funcion_rh                 <chr> "Administración de personal", "Relaciones l…
## $ personas_a_cargo           <dbl> 0, 8, 0, 3, 0, 0, 2, 1, 0, 0, 0, 0, 0, 9, 0…
## $ anios_en_empresa           <dbl> 0, 9, 1, 10, 2, 3, 5, 7, 2, 0, 1, 4, 0, 15,…
## $ anios_en_puesto            <dbl> 0, 3, 1, 3, 2, 3, 3, 4, 2, 0, 1, 0, 2, 5, 1…
## $ anios_experiencia          <dbl> 7, 8, 2, 10, 14, 5, 5, 4, 12, 10, 13, 9, 5,…
## $ sueldo_bruto               <dbl> 55700, 55000, 47000, 86747, 85500, 73600, 8…
## $ beneficios                 <chr> "Tarjeta de descuento", "Medicina prepaga /…
## $ bono                       <chr> "Menos de un sueldo", "No recibo bono", "No…
## $ ajuste                     <chr> "1 solo", "2 ajustes", "1 solo", "1 solo", …
## $ ajuste_porcentaje          <dbl> 20, 50, 25, 50, 10, 15, 17, 25, 30, 0, 46, …
## $ ajuste_mes                 <chr> "Marzo", "Septiembre", "Septiembre", "Enero…
## $ otros_proyectos            <chr> "No", "No", "No", "Si, en emprendimiento re…
## $ erp                        <chr> "Enlatado propio", "SAP / SuccessFactors", …
## $ nombre_area                <chr> "Recursos Humanos", "Recursos Humanos", "Re…
## $ mate                       <chr> "Si, está permitido", "Si, está permitido",…
## $ idioma_exigencia           <chr> "No", "No", "No", "No", "No", "No", "No", N…
## $ idioma_porcentaje          <dbl> 0.0, 0.0, 0.2, 0.0, 0.2, 0.0, 0.1, NA, 0.0,…
## $ contactos_linkedin         <dbl> 500, 100, 3000, 1474, 9240, 1000, 9800, NA,…
## $ satisfaccion               <dbl> 3, 5, 3, 5, 2, 3, 4, NA, 2, 5, 2, 3, 5, 5, …
## $ busqueda                   <chr> "No,  pero escucho ofertas", "No,  pero esc…
## $ beneficios_expectativa     <chr> "Vacaciones extendidas, almuerzo,medicina p…
## $ rh_una_palabra             <chr> "Servicio", "Indispensable", "Empatia", "Va…
## $ pregunta_bizarra           <chr> "Si pensaba tener hijos en los próximos mes…
## $ teletrabajo                <chr> "Voy rotando entre la oficina y el trabajo"…
## $ elementos                  <chr> "Computadora / Laptop", "Computadora / Lapt…
## $ valoracion_gestion_empresa <dbl> 3, 5, 3, 3, 2, 4, 4, NA, NA, NA, 4, 4, 5, 5…
## $ multiplicador              <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ sueldo_ft                  <dbl> 55700, 55000, 47000, 86747, 85500, 73600, 8…

Otra función que se suele usar mucho para explorar los datos se llama summary().

# Usar la función summary() con el objeto 'maestro' y 'salarios'
summary(maestro)
##        ID           ANTIGUEDAD          EDAD       ESTADO_CIVIL      
##  Min.   : 180.0   Min.   : 0.000   Min.   :21.00   Length:522        
##  1st Qu.: 886.5   1st Qu.: 3.000   1st Qu.:29.00   Class :character  
##  Median :1664.0   Median : 7.000   Median :35.00   Mode  :character  
##  Mean   :1873.6   Mean   : 8.839   Mean   :37.35                     
##  3rd Qu.:3087.8   3rd Qu.:10.000   3rd Qu.:44.00                     
##  Max.   :3222.0   Max.   :40.000   Max.   :65.00                     
##      HIJOS           AREA              ID_CAT            N_CATEG         
##  Min.   :0.000   Length:522         Length:522         Length:522        
##  1st Qu.:0.000   Class :character   Class :character   Class :character  
##  Median :1.000   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :1.588                                                           
##  3rd Qu.:2.000                                                           
##  Max.   :8.000
summary(salarios)
##        ID          PUESTO              SUELDO     
##  Min.   : 597   Length:135         Min.   :14776  
##  1st Qu.:1438   Class :character   1st Qu.:24402  
##  Median :1639   Mode  :character   Median :28366  
##  Mean   :1579                      Mean   :32913  
##  3rd Qu.:1738                      3rd Qu.:37094  
##  Max.   :3051                      Max.   :94128
summary(encuesta)
##  Marca.temporal                      genero          genero_diverso    
##  Min.   :2020-09-30 20:29:06.00   Length:528         Length:528        
##  1st Qu.:2020-10-02 09:29:50.75   Class :character   Class :character  
##  Median :2020-10-06 17:35:05.00   Mode  :character   Mode  :character  
##  Mean   :2020-10-10 15:54:17.05                                        
##  3rd Qu.:2020-10-17 14:38:04.50                                        
##  Max.   :2020-11-01 21:48:56.00                                        
##                                                                        
##       edad       discapacidad       nivel_formacion    carrera_grado     
##  Min.   :21.00   Length:528         Length:528         Length:528        
##  1st Qu.:28.00   Class :character   Class :character   Class :character  
##  Median :32.00   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :33.27                                                           
##  3rd Qu.:37.00                                                           
##  Max.   :63.00                                                           
##                                                                          
##  tipo_universidad       pais            provincia           trabajo         
##  Length:528         Length:528         Length:528         Length:528        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     rubro              dotacion      origen_capital      dotacion_rh     
##  Length:528         Min.   :     3   Length:528         Min.   :   0.00  
##  Class :character   1st Qu.:   100   Class :character   1st Qu.:   2.00  
##  Mode  :character   Median :   300   Mode  :character   Median :   5.00  
##                     Mean   :  2664                      Mean   :  40.37  
##                     3rd Qu.:  1000                      3rd Qu.:  11.00  
##                     Max.   :400000                      Max.   :5000.00  
##                                                                          
##     puesto          tipo_contratacion   funcion_rh        personas_a_cargo
##  Length:528         Length:528         Length:528         Min.   :  0.00  
##  Class :character   Class :character   Class :character   1st Qu.:  0.00  
##  Mode  :character   Mode  :character   Mode  :character   Median :  0.00  
##                                                           Mean   :  4.28  
##                                                           3rd Qu.:  1.00  
##                                                           Max.   :800.00  
##                                                                           
##  anios_en_empresa anios_en_puesto  anios_experiencia  sueldo_bruto   
##  Min.   : 0.000   Min.   : 0.000   Min.   : 0.000    Min.   : 29400  
##  1st Qu.: 1.000   1st Qu.: 1.000   1st Qu.: 4.000    1st Qu.: 54430  
##  Median : 3.000   Median : 2.000   Median : 7.000    Median : 72000  
##  Mean   : 3.955   Mean   : 2.798   Mean   : 8.078    Mean   : 80647  
##  3rd Qu.: 5.000   3rd Qu.: 4.000   3rd Qu.:11.000    3rd Qu.: 96763  
##  Max.   :25.000   Max.   :17.000   Max.   :35.000    Max.   :210000  
##                                                                      
##   beneficios            bono              ajuste          ajuste_porcentaje
##  Length:528         Length:528         Length:528         Min.   :   0.00  
##  Class :character   Class :character   Class :character   1st Qu.:   5.00  
##  Mode  :character   Mode  :character   Mode  :character   Median :  15.00  
##                                                           Mean   :  44.96  
##                                                           3rd Qu.:  25.00  
##                                                           Max.   :5000.00  
##                                                                            
##   ajuste_mes        otros_proyectos        erp            nombre_area       
##  Length:528         Length:528         Length:528         Length:528        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##      mate           idioma_exigencia   idioma_porcentaje contactos_linkedin
##  Length:528         Length:528         Min.   :0.0000    Min.   :    0     
##  Class :character   Class :character   1st Qu.:0.0000    1st Qu.:  250     
##  Mode  :character   Mode  :character   Median :0.0000    Median :  655     
##                                        Mean   :0.1066    Mean   : 2369     
##                                        3rd Qu.:0.1000    3rd Qu.: 2290     
##                                        Max.   :1.0000    Max.   :30000     
##                                        NA's   :101       NA's   :86        
##   satisfaccion     busqueda         beneficios_expectativa rh_una_palabra    
##  Min.   :1.000   Length:528         Length:528             Length:528        
##  1st Qu.:3.000   Class :character   Class :character       Class :character  
##  Median :3.000   Mode  :character   Mode  :character       Mode  :character  
##  Mean   :3.258                                                               
##  3rd Qu.:4.000                                                               
##  Max.   :5.000                                                               
##  NA's   :75                                                                  
##  pregunta_bizarra   teletrabajo         elementos        
##  Length:528         Length:528         Length:528        
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
##                                                          
##                                                          
##  valoracion_gestion_empresa multiplicador     sueldo_ft     
##  Min.   :1.000              Min.   :1.000   Min.   : 29400  
##  1st Qu.:2.000              1st Qu.:1.000   1st Qu.: 55000  
##  Median :3.000              Median :1.000   Median : 72000  
##  Mean   :3.072              Mean   :1.017   Mean   : 81335  
##  3rd Qu.:4.000              3rd Qu.:1.000   3rd Qu.: 97175  
##  Max.   :5.000              Max.   :1.500   Max.   :210000  
##  NA's   :127

Esto que estamos haciendo se llama Análisis Exploratorio de Datos, pueden ver la sesión que hicimos en R4HR Club de R para RRHH en YouTube.

Tipos de variables

En R nos vamos a encontrar con varios tipos de variables. Los más usuales son:

  • numéricas: son las variables que nos permiten hacer todo tipo de cálculos aritméticos y estadísticos. Se los suelen identificar como int a los números enteros (que no tienen decimales) o dbl cuando tienen decimales. A veces también se los encuentra como num.
  • character: son variables nominales, o sea que nombran cosas y no tienen jerarquías ni distancias entre los valores. Usualmente se identifican con las siglas chr
  • factor: las variables de tipo factor nos permiten crear variables ordinales, es decir que tengan un orden o jerarquía implícita. A este tipo de variables normalmente la identificamos como fct.

También existen las variables de tipo lógicas, también conocidas como booleanas, que sólo pueden asumir los valores TRUE o FALSE (o también T o F), y las variables de fechas y horas, que como siempre es un quilombo, tenemos toda una sesión dedicada a eso en R4HR Club de R para RRHH.

Volvamos a usar la función glimpse() para ver los tipos de variable que tienen el data frame salarios.

# Ver los tipos de variables de salarios
glimpse(salarios)
## Rows: 135
## Columns: 3
## $ ID     <dbl> 597, 789, 791, 804, 816, 820, 851, 864, 867, 872, 892, 898, 918…
## $ PUESTO <chr> "TEC. MANTENIMIENTO", "VOLANTE", "JEFE", "LIDER DE EQUIPO", "LI…
## $ SUELDO <dbl> 25129, 24474, 44344, 34235, 26401, 26682, 23851, 25811, 25877, …

Ahora realicemos un análisis exploratorio con la función summary().

# Usar la función summary() con salarios
summary(salarios)
##        ID          PUESTO              SUELDO     
##  Min.   : 597   Length:135         Min.   :14776  
##  1st Qu.:1438   Class :character   1st Qu.:24402  
##  Median :1639   Mode  :character   Median :28366  
##  Mean   :1579                      Mean   :32913  
##  3rd Qu.:1738                      3rd Qu.:37094  
##  Max.   :3051                      Max.   :94128

Con las variables numéricas podemos apreciar algunas medidas de resumen tales como:

  • Los valores mínimos y máximos.
  • El promedio (mean) y la mediana (median)
  • El 1° y el 3° cuartil.

Cuando la variable es de tipo chr solo podemos ver la cantidad de elementos que tiene esa columna.

Probemos lo siguiente:

# Convertimos la variable chr en fct
salarios$PUESTO <- as.factor(salarios$PUESTO)

Ahora volvamos a revisar el dataframe, primero con la función glimpse() y luego con la función summary().

# Usar la función glimpse() con el dataframe salarios
glimpse(salarios)
## Rows: 135
## Columns: 3
## $ ID     <dbl> 597, 789, 791, 804, 816, 820, 851, 864, 867, 872, 892, 898, 918…
## $ PUESTO <fct> TEC. MANTENIMIENTO, VOLANTE, JEFE, LIDER DE EQUIPO, LIDER DE EQ…
## $ SUELDO <dbl> 25129, 24474, 44344, 34235, 26401, 26682, 23851, 25811, 25877, …
# Y ahora usar la función summary()
summary(salarios)
##        ID                      PUESTO       SUELDO     
##  Min.   : 597   ANALISTA          :30   Min.   :14776  
##  1st Qu.:1438   LIDER DE EQUIPO   :30   1st Qu.:24402  
##  Median :1639   INGENIERO         :21   Median :28366  
##  Mean   :1579   TEC. MANTENIMIENTO:15   Mean   :32913  
##  3rd Qu.:1738   JEFE              :14   3rd Qu.:37094  
##  Max.   :3051   VOLANTE           :11   Max.   :94128  
##                 (Other)           :14

Ahora que la variable PUESTO es una variable de tipo factor podemos ver con la función summary() la cantidad de casos que tenemos por puesto.

Otra de las cosas que podemos hacer con las variables de tipo factor es ordenarlas. Vamos a crear una versión del data frame sólo con algunos valores para hacer más fácil el ejemplo.

# Creamos un nuevo data frame
salarios2 <- salarios %>% 
  filter(PUESTO %in% c("GERENTE", "JEFE", "ANALISTA", "TEC. MANTENIMIENTO"))

# Y ahora realicemos un gráfico de barras contando la cantidad de personas en cada puesto
ggplot(salarios2, aes(x = PUESTO)) + 
  geom_bar()

El gráfico anterior, desde el punto de vista técnico es correcto, porque nos muestra la cantidad de personas en cada una de las posiciones, pero el gráfico está desordenado lo cual no es ideal porque no muestra la jerarquía de las posiciones.

Con las variables de tipo factor podemos corregir eso.

# Modifiquemos el orden de la columna PUESTO
salarios2 <- salarios2 %>% 
  mutate(PUESTO = factor(PUESTO, 
                         levels = c("GERENTE", "JEFE", "TEC. MANTENIMIENTO", "ANALISTA")))

# Repitamos el gráfico anterior
ggplot(salarios2, aes(x = PUESTO)) +
  geom_bar()

Para saber más como trabajar con variables factor pueden ver este contenido de R4HR Club de R para RRHH.

Check out

Para seguir practicando con un contenido muy accesible y entretenido pueden buscar estos libros:

Además, pueden revisar y utilizar todo el contenido de R4HR Club de R para RRHH disponible en Google Drive y YouTube.

Recuerden hacer todas las consultas en el canal #auxilio en Slack

LS0tDQp0aXRsZTogIkNsYXNlIDAgLSBJbnRyb2R1Y2Npw7NuIGEgUiB5IFJTdHVkaW8iDQphdXRob3I6ICJTZXJnaW8gR2FyY2lhIE1vcmEgfCBEYXRhIDRIUiINCmRhdGU6ICIxMS8xMC8yMDIyIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogbHVtZW4NCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy5yZXRpbmEgPSAzLCBvdXQud2lkdGggPSAiODAlIikNCmBgYA0KDQojIEN1cnNvIEludHJvZHVjY2nDs24gYSBSIHBhcmEgUlJISA0KDQpCaWVudmVuaWRhcyB5IGJpZW52ZW5pZG9zIGFsIGN1cnNvICoqSW50cm9kdWNjacOzbiBhIFIgcGFyYSBSUkhIKiog8J+RqeKAjfCfkrsuIFINCmVzIHVuIGxlbmd1YWplIG9wZW4gc291cmNlLCBlcyBkZWNpciBxdWUgZXMgYWJpZXJ0bywgZ3JhdHVpdG8sIHkgcXVlDQphZGVtw6FzIGxhIGNvbXVuaWRhZCBkZSB1c3VhcmlhcyB5IHVzdWFyaW9zIGNvbnRyaWJ1eWUgYSBzdQ0KbWFudGVuaW1pZW50by4NCg0KIyMgQW50ZXMgZGUgZW1wZXphcg0KDQpTaSBxdWllcmVuIGNvbXBhcnRpciBpbcOhZ2VuZXMgZGVsIGN1cnNvIGVuIHJlZGVzIHNvY2lhbGVzIGxvIHB1ZWRlbg0KaGFjZXIsIHRhbnRvIHNpIGxlcyBndXN0w7MgY29tbyBzaSBubyBsZXMgZ3VzdMOzLg0KDQpMb3MgaGFzaHRhZ3MgcXVlIHB1ZWRlbiB1c2FyIHNvbg0KDQotICAgKiojUjRIUioqDQoNCi0gICAqKiNSU3RhdHNfRVMqKg0KDQotICAgKiojZGF0YTRocioqDQoNCi0gICAqKiNCZXR0ZXJXaXRoRGF0YSoqDQoNCi0gICAqKiNQZW9wbGVBbmFseXRpY3MqKg0KDQotICAgLi4uIHkgZWwgaGFzaHRhZyBxdWUgcXVpZXJhbg0KDQpUYW1iacOpbiBwdWVkZW4gYXJyb2JhciBhIERhdGEgNEhSIHkgdW5pcnNlIGEgbG9zIGdydXBvcw0KDQrwn5W577iPIFtEaXNjb3JkXShodHRwczovL2Rpc2NvcmQuZ2cvM3hoTkg2a1JhZCkNCg0K8J+knSBbU2xhY2sNClI0SFJdKGh0dHBzOi8vam9pbi5zbGFjay5jb20vdC9yNGhyL3NoYXJlZF9pbnZpdGUvenQtb2NyaXl4NWUtbnFYdXdXZUR5T0ttMklDVUpqaHU2ZykNCg0KIVtdKGh0dHBzOi8vbWVkaWEuZ2lwaHkuY29tL21lZGlhL2wwSXljSTBycmVGTlF3bVNRL2dpcGh5LmdpZikNCg0KIyMgQWRtaW5pc3RyYWNpw7NuIGRlbCBjdXJzbw0KDQpBbGd1bmFzIGNvc2FzIHF1ZSB0aWVuZW4gcXVlIHNhYmVyOg0KDQotICAgRWwgbGluayBkZSBab29tIHZhIGEgc2VyIHNpZW1wcmUgZWwgbWlzbW8uIPCfm7DvuI8NCi0gICBObyBoYWNlIGZhbHRhIHF1ZSB0ZW5nYW4gbGEgY8OhbWFyYSBlbmNlbmRpZGEuIPCflbXvuI8NCi0gICBMYXMgY2xhc2VzIGxhcyBncmFibyB5IGxhcyBjb21wYXJ0byBkZSBtYW5lcmEgcHJpdmFkYSBwb3IgWW91dHViZS4g8J+OpQ0KLSAgIFB1ZWRlbiB1c2FyIGRvY3VtZW50b3MgY29tcGFydGlkb3MgcGFyYSB0b21hciBub3Rhcy4g8J+Tlg0KLSAgIFPDum1lbnNlIGFsIGdydXBvIGRlDQogICAgW1NsYWNrXShodHRwczovL2pvaW4uc2xhY2suY29tL3QvcjRoci9zaGFyZWRfaW52aXRlL3p0LW9jcml5eDVlLW5xWHV3V2VEeU9LbTJJQ1VKamh1NmcpDQogICAgeWEgcXVlIHRvZGFzIGxhcyBjb25zdWx0YXMgbGFzIGhhY2Vtb3MgcG9yIGFow60uIPCfpJzwn6SbDQotICAgUHVlZGVuIGludGVycnVtcGlyIGxhcyB2ZWNlcyBxdWUgcXVpZXJhbi4g4pyLDQotICAgVGFtYmnDqW4gcHVlZGVuIHBvbmVyIHN1IHByZWd1bnRhIGVuIGVsIGNoYXQgcG9uaWVuZG8gKiojUCoqIGFudGVzIGRlDQogICAgbGEgY29uc3VsdGEuDQoNCiMgUg0KDQpSIGVzIHVuIGxlbmd1YWplIGRlIGPDs2RpZ28gYWJpZXJ0bywgcXVlIG5hY2nDsyBwYXJhIGVsIGFuw6FsaXNpcw0KZXN0YWTDrXN0aWNvIGRlIGRhdG9zLg0KDQpTdSBwb3B1bGFyaWRhZCBzZSBkZWJlIGdyYWNpYXMgYSBsYSBlbm9ybWUgY29tdW5pZGFkIGdsb2JhbCBxdWUNCnBlcm1hbmVudGVtZW50ZSBhY3R1YWxpemEgcGFxdWV0ZXMgeSBnZW5lcmEgY29udGVuaWRvLiBTdSBwcmluY2lwYWwgYmFzZQ0KZGUgdXN1YXJpb3Mgbm8gdmllbmVuIGRlbCBtdW5kbyBkZSBsYSBwcm9ncmFtYWNpw7NuIHNpbm8gcGVyc29uYXMgZGUNCmRpc3RpbnRhcyBkaXNjaXBsaW5hcywgbG8gcXVlIGhhY2UgcXVlIHN1IHNpbnRheGlzIHNlYSBtw6FzIHNpbXBsZS4NCkFwYXJ0ZSBkZSBsb3MgZGVzYXJyb2xsb3MgZGUgbWFjaGluZSBsZWFybmluZyBxdWUgc2UgaGFjZW4gZW4gdG9kbyBlbA0KbXVuZG8sIHRlIHBvZMOpcyBlbmNvbnRyYXIgY29uOg0KDQotICAgTGEgW0JCQ10oaHR0cHM6Ly9naXRodWIuY29tL2JiYy9iYnBsb3QpIHkgW1RoZQ0KICAgIEVjb25vbWlzdF0oaHR0cDovL3JzdHVkaW8tcHVicy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS8yODQzMjlfYzdlNjYwNjM2ZmVjNGE0MmEwOWVlZDk2OGRjNDdmMzIuaHRtbCkNCiAgICBjb25zdHJ1eWVuZG8gcGFxdWV0ZXMgeSAqdGVtYXMqIHBhcmEgbG9zIGdyw6FmaWNvcy4NCi0gICBbQWlyYm5iXShodHRwczovL3BlZXJqLmNvbS9wcmVwcmludHMvMzE4Mi5wZGYpIHVzw6FuZG9sbyBwYXJhIHN1cw0KICAgIGludmVzdGlnYWNpb25lcywgbW9kZWxvcyB5IHJlcG9ydGVzLg0KLSAgIFlvIGxvIHVzbyBwYXJhIGxvcyBpbmZvcm1lcyBkZSBudWVzdHJvcyBwcm95ZWN0b3MuLi4NCg0KIyMgVmVudGFqYXMgZGUgUg0KDQpBcHJlbmRlciBSIHBhcmEgUlJISCB0aWVuZSBpbmFnb3RhYmxlcyB2ZW50YWphczoNCg0KLSAgIEhhY2VyIGFuw6FsaXNpcyBwcmVkaWN0aXZvcyAoQ2xhc2UgMyBkZSBlc3RlIGN1cnNvISkNCg0KLSAgIFJlcHJvZHVjaXIgZWwgdHJhYmFqbw0KDQotICAgTcOhcyBjYXBhY2lkYWQgZGUgcHJvY2VzYW1pZW50byBxdWUgRXhjZWwNCg0KLSAgIEF1dG9tYXRpemFyIGVsIHRyYWJham8NCg0KLSAgIEFuYWxpemFyICoqdG9kbyoqIHRpcG8gZGUgZGF0b3MNCg0KICAgIC0gICBUYWJsYXMNCiAgICAtICAgQmFzZXMgZGUgRGF0b3MsIFNpc3RlbWFzIGRlIEdlc3Rpw7NuDQogICAgLSAgIFRleHRvIGxpYnJlDQogICAgLSAgIEltw6FnZW5lcw0KDQotICAgRW52aWFyIG1haWxzIGNvbiByZXBvcnRlcyBhdXRvbcOhdGljb3MNCg0KLSAgIEVzY3JpYmlyIGxpYnJvcywgYmxvZ3MsIENWDQoNCi0gICBMYSBjb211bmlkYWQgZGUgUg0KDQojIyBMYSBjb211bmlkYWQgZGUgUg0KDQpSIGN1ZW50YSBjb24gdW5hIHJlZCBkZSBzb3BvcnRlIG11eSBhbXBsaWEsIHPDs2xpZGEgeSBzb2xpZGFyaWEsDQplc3BlY2lhbG1lbnRlIGVuIExhdGlub2Ftw6lyaWNhLiBQcsOhY3RpY2FtZW50ZSBlbiB0b2RvcyBsb3MgcGHDrXNlcyBkZWwNCmNvbnRpbmVudGUgaGF5IFJVRyAoUiBVc2VycyBHcm91cCkgcGFyYSBlbCBww7pibGljbyBlbiBnZW5lcmFsLCB5IHRvZGENCnVuYSByZWQgZGUgUi1MYWRpZXMgcXVlIHByb211ZXZlbiBsYSBkaXZlcnNpZGFkIGRlIG11amVyZXMgeSBkZQ0KbWlub3LDrWFzLCBwYXJhIGdlbmVyYXIgbWVqb3JlcyBvcG9ydHVuaWRhZGVzIGxhYm9yYWxlcy4NCg0KUHVlZGVuIGV4cGxvcmFyIHRvZG9zIGxvcyBSVUcNCjxodHRwczovL2JlbnViYWguZ2l0aHViLmlvL3ItY29tbXVuaXR5LWV4cGxvcmVyL3JsYWRpZXMuaHRtbD4NCg0KIyMgUjRIUiBDbHViIGRlIFIgcGFyYSBSUkhIDQoNCiFbXShBcmNoaXZvcy9DYXRiZXJ0LnBuZykNCg0KRW4gZXN0YSBtaXNtYSBsw61uZWEsIGRlc2RlIE1heW8gZGUgMjAyMCBlbXBlemFtb3MgYSBkZXNhcnJvbGxhciB1bmENCmNvbXVuaWRhZCBlc3BlY8OtZmljYSBwYXJhIGVsIG11bmRvIGRlIFJSSEgsIGRvbmRlIG5vcyByZXVuaW1vcyBlbCBwcmltZXINCnPDoWJhZG8gZGUgY2FkYSBtZXMgZGUgMTA6MDAgYSAxMTozMCBocy4gKGhvcmEgZGUgQXJnZW50aW5hKSBwYXJhDQphcHJlbmRlciBhIHByb2dyYW1hciBlbiBlc3RlIGxlbmd1YWplLg0KDQpSZXZpc2Ftb3MgcGFxdWV0ZXMsIGV4cGxvcmFtb3MgZm9ybWFzIGRlIGhhY2VyIGFuw6FsaXNpcyBwcmVkaWN0aXZvcywgeQ0KZWwgcGxhbiBlcyBwb2RlciBkZXNhcnJvbGxhciB1bmEgcmVkIHNpbWlsYXIgYSBsb3MgUlVHcyB5IFItTGFkaWVzLg0KDQpEZXNkZSBhZ29zdG8gMjAyMSBzb21vcyB1bmEgY29tdW5pZGFkIHJlY29ub2NpZGEgeSBzcG9uc29yZWFkYSBwb3IgW1INCkNvbnNvcnRpdW1dKGh0dHBzOi8vd3d3LnItY29uc29ydGl1bS5vcmcvKSB1bmEgb3JnYW5pemFjacOzbiBkZWRpY2FkYSBhDQpwcm9tb3ZlciB5IGEgY29udHJpYnVpciBjb24gcmVjdXJzb3MgcGFyYSBsYSBhZG9wY2nDs24geSBlbCBkZXNhcnJvbGxvIGRlDQpSLg0KDQo+ICoqVG9kbyBlbCBjb250ZW5pZG8gZ2VuZXJhZG8gcG9yIGxvcyB1c3VhcmlvcyBkZSBSNEhSIHNlIHB1ZWRlDQo+IHV0aWxpemFyIGdyYXR1aXRhbWVudGUgY2l0YW5kbyBsYSBmdWVudGUqKg0KDQo8YSByZWw9ImxpY2Vuc2UiIGhyZWY9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8iPjxpbWcgc3JjPSJodHRwczovL2kuY3JlYXRpdmVjb21tb25zLm9yZy9sL2J5LzQuMC84OHgzMS5wbmciIGFsdD0iTGljZW5jaWEgQ3JlYXRpdmUgQ29tbW9ucyIgc3R5bGU9ImJvcmRlci13aWR0aDowIi8+PC9hPjxiciAvPkVzdGENCm9icmEgZXN0w6EgYmFqbyB1bmENCjxhIHJlbD0ibGljZW5zZSIgaHJlZj0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyI+TGljZW5jaWENCkNyZWF0aXZlIENvbW1vbnMgQXRyaWJ1Y2nDs24gNC4wIEludGVybmFjaW9uYWw8L2E+Lg0KDQojIyBBcHJlbmRlciBhIHByb2dyYW1hcg0KDQpBbCBwcmluY2lwaW8gcHJvZ3JhbWFyIGVzIHVuIGFjdG8gZGUgZmUuIFZlbW9zIHF1ZSBwYXNhbiBjb3NhcywgcXVlDQplc2NyaWJvIGFsZ28geSB1bmEgY29zYSBhcGFyZWNlIGVuIGxhIHBhbnRhbGxhIHBlcm8gbm8gc2FiZW1vcyBwb3IgcXXDqSwNCmPDs21vLCB5IHBvciBxdcOpIG5vIG1lIHNhbGUgZGUgbnVldm8uDQoNCkVuIHBhcnRpY3VsYXIgUiwgeSBzdXMgcGFxdWV0ZXMsIHBlcm1pdGVuIGV4cHJlc2FyIGxvIHF1ZSBxdWllcm8NCnByb2dyYW1hciBtYXlvcm1lbnRlIGRlIHVuYSBmb3JtYSBzaW1pbGFyIGEgbGEgcXVlIGxvIGhhcsOtYSBzaSB0dXZpZXJhDQpxdWUgZGFyIGxhcyBpbnN0cnVjY2lvbmVzIHZlcmJhbG1lbnRlIGEgYWxndWllbi4gWSBhIGRpZmVyZW5jaWEgZGUgb3Ryb3MNCmxlbmd1YWplcyBjb21vIGh0bWwgcG9yIGVqZW1wbG8sIGxhIHNpbnRheGlzIGRlbCBjw7NkaWdvIChsYXMNCmluc3RydWNjaW9uZXMpIGVzIG11Y2hvIG3DoXMgc2ltcGxlIChlbnRyZW4gYSBjdWFscXVpZXIgcMOhZ2luYSB3ZWIgeQ0KYXByaWV0ZW4gRjEyKS4NCg0KVGFtYmnDqW4gbGVzIHZhIGEgcGFzYXIgY29uIGVzdGUgY3Vyc28gcXVlIG11Y2hhcyBjb3NhcyBsZXMgdmEgYSByZXN1bHRhcg0KbcOhcyBzZW5jaWxsYXMgZGUgaGFjZXIgZW4gRXhjZWwuIE8gcXVlIHRhbCB2ZXogYnVzY2FuZG8gZW4gaW50ZXJuZXQNCmVuY29udHJhc3RlIG90cmEgZm9ybWEgZGUgaGFjZXIgbGFzIGNvc2FzLCB5IGVzdMOhIGJpZW4gcXVlIGFzw60gc2VhLg0KDQpMbyDDum5pY28gcXVlIHZhbiBhIG5lY2VzaXRhciBwYXJhIHByb2dyYW1hciBlczoNCg0KLSAgIFBhY2llbmNpYQ0KLSAgIENvbnN0YW5jaWENCi0gICBDdXJpb3NpZGFkDQoNClkgY29uIGVsIHRpZW1wbywgeSBsYSBwcsOhY3RpY2EsIHVuIGTDrWEgdGVuw6lzIHVuYSBlcGlmYW7DrWEgeSB0b2RvIGxvIHF1ZQ0Kdm9sY2FzIGVuIHVuIGPDs2RpZ28gY29icmEgc2VudGlkby4NCg0KIyBDcmVhciBwcm95ZWN0b3MgZW4gUlN0dWRpbw0KDQpUcmFiYWphciBjb24gcHJveWVjdG9zIGVuIFJTdHVkaW8gaGFjZSBxdWUgdG9kbyBlbCB0cmFiYWpvIHNlYSBtw6FzDQpzZW5jaWxsby4gTG9zIHByb3llY3RvcyBjcmVhbiB1bmEgY2FycGV0YSBlbiBudWVzdHJhIFBDIGVuIGRvbmRlIHNlDQphbG1hY2VuYXLDoW4gbG9zIGFyY2hpdm9zLCB0YWJsYXMsIHNjcmlwdHMsIHkgaGFjZSBxdWUgdG9kbyBzZWEgbcOhcw0Kb3JnYW5pemFkby4NCg0KUGFyYSBjcmVhciB1biBwcm95ZWN0byB0ZW7DqXMgcG9kw6lzIGVudHJhciBlbjoNCg0KLSAgICpGaWxlKg0KLSAgICpOZXcgcHJvamVjdCoNCg0KWSBsdWVnbyBwb25lciBlbCBub21icmUgZGUgbGEgY2FycGV0YS4NCg0KIVtdKEFyY2hpdm9zL25ld19wcm9qZWN0LnBuZyl7d2lkdGg9IjMzNyJ9DQoNClNlIGFicmUgdW5hIHZlbnRhbmEsIGhhY2Vtb3MgY2xpY2sgZW4gKk5ldyBQcm9qZWN0KiwgbHVlZ28gZW4gKk9rKiB5DQpmaW5hbG1lbnRlIGVuIGxhIHRlcmNlcmEgdmVudGFuYSBwb2RlbW9zIGRhcmxlIHVuIG5vbWJyZSBhbCBwcm95ZWN0bywgeQ0KYXNpZ25hciBsYSBjYXJwZXRhIGVuIGxhIHF1ZSBxdWVyZW1vcyBndWFyZGFybG8gY29uIGVsIGJvdMOzbiAqQnJvd3NlKi4NCg0KIVtdKEFyY2hpdm9zL25ld19wcm9qZWN0by5wbmcpe3dpZHRoPSIzMzcifQ0KDQojIyBPYmpldG9zDQoNClBvciBkZWZpbmljacOzbiwgUiBlcyB1biBsZW5ndWFqZSAqb3JpZW50YWRvIGEgb2JqZXRvcyouIEVzdG8gc2lnbmlmaWNhDQpxdWUgYSB1bmEgdGFibGEsIHVuIHZhbG9yLCB1bmEgdmFyaWFibGUsIGV0Yy4gbGUgdm95IGEgcG9uZXIgdW4gbm9tYnJlLA0KeSBlc2EgY29zYSBxdWUgYWhvcmEgdGllbmUgbm9tYnJlIHNlIGxsYW1hICoqb2JqZXRvKiouDQoNCkVzdG8gbm9zIHNpbXBsaWZpY2EgdW4gbW9udMOzbiBsYSB2aWRhLiBMYSBhc2lnbmFjacOzbiBsYSBoYWNlbW9zIGNvbiBlbA0Kc8OtbWJvbG8gYDwtYCBxdWUgc2ltdWxhIHVuYSBmbGVjaGEgaGFjaWEgbGEgaXpxdWllcmRhLiBUYW1iacOpbiBzZSBwdWVkZQ0KaGFjZXIgY29uIGVsIHPDrW1ib2xvIGA9YCBwZXJvIGNvbW8gbXVjaGFzIHZlY2VzIGxvIHV0aWxpemFtb3MgcGFyYQ0KY29uZmlndXJhciB1biBwYXLDoW1ldHJvIGRlIHVuYSBmdW5jacOzbiB1c2Ftb3MgKiJsYSBmbGVjaGl0YSIqLg0KDQojIyMgVmFsb3Jlcw0KDQpgYGB7ciBvYmpldG9zfQ0KIyBFc3RvIGVzIHVuIGNvbWVudGFyaW8NCg0Kb2JqZXRvXzEgPC0gMjAyMQ0Kb2JqZXRvXzIgPC0gMTk3OSAjIFBvbmVyIGHDsW8gZGUgbmFjaW1pZW50byBvIGluZ3Jlc28gYWwgdHJhYmFqbw0KDQojIFBhcmEgdmVyIGVsIHJlc3VsdGFkbyBkZWwgb2JqZXRvIHVzbyBsYXMgdGVjbGFzIEN0cmwgKyBFbnRlcg0KIyBUYW1iacOpbiBwdWVkbyB1c2FyIGVsIMOtY29ubyAgIlJ1biINCm9iamV0b18xDQoNCiMgUG9kZW1vcyBoYWNlciBvcGVyYWNpb25lcyBjb24gb2JqZXRvcw0Kb2JqZXRvXzEgLSBvYmpldG9fMg0KYGBgDQoNCiMjIyBWZWN0b3Jlcw0KDQpVbm8gZGUgbG9zIG9iamV0b3MgbcOhcyB1dGlsaXphZG9zIHNvbiBsb3MgKip2ZWN0b3JlcyoqIHF1ZSBzb24gY29uanVudG9zDQpkZSB2YWxvcmVzLCBudW3DqXJpY29zLCBkZSB0ZXh0bywgbyBjb21iaW5hZG9zLiBQYXJhIGNyZWFyIHVuIHZlY3Rvcg0KbmVjZXNpdGFtb3MgdXNhciBsYSBmdW5jacOzbiBgYygpYCBxdWUgc2lnbmlmaWNhICpjb21iaW5lKi4NCg0KTG9zIHZlY3RvcmVzIHB1ZWRlbiB0ZW5lciB0ZXh0byBvIG7Dum1lcm8uDQoNCmBgYHtyIHZlY3RvcmVzfQ0KIyBWZWN0b3IgZGUgVGV4dG8NCm5vbWJyZSA8LSBjKCJHdXN0YXZvIiwgIkNoYXJseSIsICJaZXRhIikgIyBMYXMgdmFyaWFibGVzIGRlIHRleHRvIHZhbiBzaWVtcHJlIGNvbiBjb21pbGxhcy4NCg0KIyBWZXIgZWwgY29udGVuaWRvDQpub21icmUNCg0KIyBTZWxlY2Npb25hciB1biBlbGVtZW50byBkZWwgdmVjdG9yIA0Kbm9tYnJlWzFdDQoNCiMgVmVjdG9yIG51bcOpcmljbw0KYW5pb19uYWNpbWllbnRvIDwtIGMoMTk1OSwgMTk2MywgMTk1OCkNCg0KYGBgDQoNCkxvcyB2ZWN0b3JlcyBzb24gbXV5IHV0aWxpemFkb3MgcGFyYSBmaWx0cmFyIGVsZW1lbnRvcywgY2FtYmlhciBub21icmVzDQpkZSB0YWJsYXMsIGV0Yy4uDQoNCiMjIyBEYXRhIGZyYW1lcw0KDQpMb3MgKipkYXRhIGZyYW1lcyoqIHNvbiB0YWJsZXMgcXVlIGNvbnRpZW5lbiBmaWxhcyAob2JzZXJ2YWNpb25lcykgeQ0KY29sdW1uYXMgKHZhcmlhYmxlcykuDQoNClBvZGVtb3MgY3JlYXIgdW4gZGF0YSBmcmFtZSB1c2FuZG8gdmVjdG9yZXMgcXVlICoqdGVuZ2FuIGxhIG1pc21hDQpjYW50aWRhZCBkZSBlbGVtZW50b3MqKiB1c2FuZG8gbGEgZnVuY2nDs24gYGRhdGEuZnJhbWUoKWAuIERlbnRybyBkZSBsYQ0KZnVuY2nDs24gcG9kZW1vcyBwb25lciBsb3Mgbm9tYnJlcw0KDQpgYGB7ciBkYXRhZnJhbWVzfQ0KIyBDcmVhciBlbCBkYXRhIGZyYW1lIHNvZGFfc3RlcmVvIHVzYW5kbyBsb3MgdmVjdG9yZXMgbm9tYnJlIHkgYW5pb19uYWNpbWllbnRvDQpzb2RhLnN0ZXJlbyA8LSBkYXRhLmZyYW1lKG5vbWJyZSwgYW5pb19uYWNpbWllbnRvKQ0KICANCiMgU2VsZWNjaW9uYXIgZWxlbWVudG9zIGRlIHVuIHZlY3RvciB1c2FuZG8gbGEgc2ludGF4aXM6IG5vbWJyZV9kYXRhZnJhbWVbZmlsYSwgY29sdW1uYV0NCnNvZGEuc3RlcmVvWzEsMl0NCg0KYGBgDQoNCk5vcm1hbG1lbnRlIGxvcyBkYXRhIGZyYW1lcyBsb3MgY2FyZ2Ftb3MgZGUgYWxnw7puIGFyY2hpdm8uIFBvciBlamVtcGxvLA0KY2FyZ3VlbW9zIGVsIGFyY2hpdm8gYHJvdGFjaW9uLmNzdmAgdXNhbmRvIGxhIGZ1bmNpw7NuIGByZWFkLmNzdmAuDQoNCmBgYHtyIGNhcmdhMX0NCiMgQ3JlYXIgdW4gb2JqZXRvIGxsYW1hZG8gcm90YWNpb24gbGV5ZW5kbyBlbCBhcmNoaXZvIHJvdGFjaW9uLmNzdg0Kcm90YWNpb24gPC0gcmVhZC5jc3YoInJvdGFjaW9uLmNzdiIsDQogICAgICAgICAgICAgICAgICAgICBzZXAgPSAiOyIpDQoNCiMgVmVyIGVsIGNvbnRlbmlkbyBkZWwgZGF0YSBmcmFtZQ0Kcm90YWNpb24NCmBgYA0KDQojIyMgTm90YXMgc29icmUgZWwgbm9tYnJlIGRlIGxvcyBvYmpldG9zDQoNClNpIGJpZW4gYSBsb3Mgb2JqZXRvcyBwb2RlbW9zIG5vbWJyYXJsb3MgZGUgY3VhbHF1aWVyIG1hbmVyYSwgaGF5IGJ1ZW5hcw0KcHLDoWN0aWNhcyByZWNvbWVuZGFkYXMuIFBvciBlamVtcGxvOg0KDQotICAgVXNhciBub21icmVzIGNvcnRvcywgeSBkZXNjcmlwdGl2b3MNCi0gICBMb3Mgbm9tYnJlcyB0aWVuZW4gcXVlIGVtcGV6YXIgY29uIHVuYSBsZXRyYS4NCi0gICBMb3Mgc8OtbWJvbG9zIHbDoWxpZG9zIHF1ZSBzZSBwdWVkZW4gdXNhciBzb24gZWwgZ3Vpw7NuIGJham8gKGBfYCkgbyBlbA0KICAgIHB1bnRvIChgLmApLg0KLSAgIFNlIHB1ZWRlbiB1c2FyIG1hecO6c2N1bGFzLCBwZXJvIGVzIG3DoXMgY8OzbW9kbyBxdWUgc2llbXByZSB1c2FyDQogICAgbWluw7pzY3VsYXMuDQotICAgUmVjdWVyZGVuIHF1ZSBSIGVzICpjYXNlIHNlbnNpdGl2ZSosIG8gc2VhIHF1ZSBoYXkgcXVlIHByZXN0YXINCiAgICBhdGVuY2nDs24gYSBtYXnDunNjdWxhcyB5IG3DrW51c2N1bGFzDQoNCmBgYHtyIG5vbWJyZXMsIGV2YWw9RkFMU0V9DQojIE5vbWJyZXMgdsOhbGlkb3MgZGUgb2JqZXRvcw0KIyMgTWVqb3JlcyBvcGNpb25lcw0KcmVzcHVlc3Rhc19jbGltYQ0KDQpyZXNwdWVzdGFzLmNsaW1hDQoNCiMgRXN0byBmdW5jaW9uYSBwZXJvIGVzIG1lbm9zIHJlY29tZW5kYWJsZQ0KUmVzcHVlc3Rhc0NsaW1hDQpgYGANCg0KUG9yIG90cm8gbGFkbzoNCg0KLSAgIExvcyBvYmpldG9zIG5vIHB1ZWRlbiBjb21lbnphciBjb24gbsO6bWVyb3MuDQotICAgTm8gc2UgcHVlZGVuIHVzYXIgYWNlbnRvcywgw7EsIHUgb3Ryb3MgY2FyYWN0ZXJlcyBlc3BlY2lhbGVzLg0KLSAgIE5vIHNlIHJlY29taWVuZGFuIHVzYXIgbGV0cmFzIG8gbm9tYnJlcyBkZSBmdW5jaW9uZXMuDQoNCiMjIFBhcXVldGVzDQoNCkxvcyBwYXF1ZXRlcyBvIGxpYnJlcsOtYXMgc29uIGV4dGVuc2lvbmVzIGRlc2Fycm9sbGFkYXMgcG9yIGxhIGNvbXVuaWRhZA0KbyBwb3IgZW1wcmVzYXMgcXVlIGZhY2lsaXRhbiBlbCB1c28gZGUgUiB5IGV4cGFuZGVuIHN1cyBjYXBhY2lkYWRlcy4gRW4NCmVzdGUgZW5jdWVudHJvIHZhbW9zIGEgdXNhciBsb3Mgc2lndWllbnRlcyBwYXF1ZXRlczoNCg0KLSAgIGByZWFkeGxgOiBEZXNhcnJvbGxhZG8gcG9yIEhhZGxleSBXaWNraGFtIGFuZCBKZW5uaWZlciBCcnlhbi4gW0xpbmsNCiAgICBhIGxhIHdlYl0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1yZWFkeGwpLg0KLSAgIGBvcGVueGxzeGA6IERlc2Fycm9sbGFkbyBwb3IgUGhpbGlwcCBTY2hhdWJlcmdlciBhbmQgQWxleGFuZGVyDQogICAgV2Fsa2VyLiBbTGluayBhIGxhDQogICAgd2ViXShodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPW9wZW54bHN4KS4NCi0gICBgdGlkeXZlcnNlYDogRGVzYXJyb2xsYWRvIHBvciBIYWRsZXkgV2lja2hhbSB5IG11Y2hvcyBtw6FzLiBbTGluayBhDQogICAgbGEgd2ViXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykuDQoNCkVzdGUgw7psdGltbyBwYXF1ZXRlLCBgdGlkeXZlcnNlYCBlcyB1bmEgY29sZWNjacOzbiBkZSBwYXF1ZXRlcyBxdWUNCnBlcm1pdGVuIHJlYWxpemFyIG11Y2hhcyB0YXJlYXMgZGUgZXhwbG9yYWNpw7NuLCBsaW1waWV6YSB5DQp0cmFuc2Zvcm1hY2nDs24gZGUgZGF0b3MuDQoNClBhcmEgdXRpbGl6YXIgdW4gcGFxdWV0ZSwgbG8gcHJpbWVybyBxdWUgdGVuZW1vcyBxdWUgaGFjZXIgZXMNCmluc3RhbGFybG9zLiBFc28gbG8gaGFjZW1vcyBjb24gbGEgZnVuY2nDs24gYGluc3RhbGwucGFja2FnZXMoKWAgeSBkZW50cm8NCmRlbCBwYXLDqW50ZXNpcyB0ZW5lbW9zIHF1ZSBwb25lciBlbCBub21icmUgZGVsIHBhcXVldGUuICoqVGVuZ2FuIGVuDQpjdWVudGEgcXVlOioqDQoNCi0gICBSIGVzIHVuIGxlbmd1YWplICpjYXNlIHNlbnNpdGl2ZSogbyBzZWEgcXVlIGhheSBxdWUgcHJlc3RhciBhdGVuY2nDs24NCiAgICBhIG1hecO6c2N1bGFzIHkgbWluw7pzY3VsYXMuDQoNCi0gICBQYXJhIGluc3RhbGFyIGxvcyBwYXF1ZXRlcyBoYXkgcXVlIHVzYXIgY29taWxsYXMNCg0KRXN0ZSBlcyB1biBwYXNvIHF1ZSBoYWNlbW9zIHVuYSBzb2xhIHZleiBwb3IgY29tcHV0YWRvcmEuDQoNClBhcmEgY29ycmVyIGVsIGPDs2RpZ28gc2UgdGllbmVuIHF1ZSBwYXJhciBlbiBsYSBsw61uZWEgZGUgY8OzZGlnbyBxdWUNCnF1aWVyZW4gdXNhciB5IGFwcmV0YXIgbGFzIHRlY2xhcyBgQ3RybGAgKyBgRW50ZXJgIG8gYmllbiBlbCB0cmnDoW5ndWxvDQp2ZXJkZSAoY29tbyBzaSBmdWVyYSB1biDDrWNvbm8gZGUgKiJQbGF5Iiog4pa277iPKQ0KDQpgYGB7ciBwYXF1ZXRlcywgZXZhbCA9IEZBTFNFfQ0KIyBJbnN0YWxhciBsb3MgcGFxdWV0ZXMgcmVhZHhsIHkgdGlkeXZlcnNlDQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikNCmBgYA0KDQpFc3RvIGxvIHF1ZSBoYWNlIGVzIGluc3RhbGFyIHBhcXVldGVzIGRlc2RlDQpbQ1JBTl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvKSwgcXVlIGVzIHVuIHJlcG9zaXRvcmlvIGRvbmRlIHNlDQpwdWJsaWNhbiBsb3MgcGFxdWV0ZXMsIGFzZWd1cmFuZG8gdW4gZXN0w6FuZGFyIGRlIGNhbGlkYWQgeSBkZQ0KZG9jdW1lbnRhY2nDs24gcXVlIGhhY2UgcXVlIHRyYWJhamFyIGNvbiBjdWFscXVpZXIgcGFxdWV0ZSBkZSBDUkFOIHNlYQ0Kc2VndXJvLg0KDQpQYXJhIHVzYXIgbGFzIGZ1bmNpb25lcyBkZSBsb3MgcGFxdWV0ZXMgcXVlIGluc3RhbGFtb3MsIGFob3JhIHRlbmVtb3MNCnF1ZSAqKmNhcmdhcmxvcy4qKiBFc3RvIGxvIHF1ZSBoYWNlIGVzIGRlIGFsZ3VuYSBtYW5lcmEgKiJhY3RpdmFyIiogZWwNCnBhcXVldGUgeSBxdWUgcG9kYW1vcyBlbXBlemFyIHN1cyBmdW5jaW9uZXMuDQoNClBhcmEgY2FyZ2FyIHVuIHBhcXVldGUgdGVuZW1vcyBxdWUgdXNhciBsYSBmdW5jacOzbiBgbGlicmFyeSgpYC4NClJlY3VlcmRlbiBwcmVzdGFyIGF0ZW5jacOzbiBhIGxhcyBtYXnDunNjdWxhcyB5IG1pbsO6c2N1bGFzLiBBaG9yYSBubyBzb24NCm5lY2VzYXJpYXMgbGFzIGNvbWlsbGFzLg0KDQpgYGB7ciBjYXJnYS1wYXF1ZXRlc30NCiMgQ2FyZ2FyIGxvcyBwYXF1ZXRlcyByZWFkeGwgeSB0aWR5dmVyc2UNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkeGwpDQpgYGANCg0KVW5hIGRlIGxhcyBmb3JtYXMgZW4gbGFzIHF1ZSBub3MgZGFtb3MgY3VlbnRhIHF1ZSBlbCBwYXF1ZXRlIGVzdMOhDQppbnN0YWxhZG8gZXMgY3VhbmRvIGVtcGV6YW1vcyBhIGVzY3JpYmlyIHN1IG5vbWJyZSB5IG5vcyBhcGFyZWNlIGVsDQpub21icmUgZGVsIHBhcXVldGUgcGFyYSBhdXRvY29tcGxldGFyLiBFc3RhIGVzIHVuYSBkZSBsYXMgdmVudGFqYXMgZGUNCnRyYWJhamFyIGVuIFJTdHVkaW8uDQoNCiFbXShBcmNoaXZvcy9wYXF1ZXRlX2FjdGl2by5wbmcpDQoNCj4gUmVjdWVyZGVuOg0KPg0KPiBMYSBpbnN0YWxhY2nDs24gZGUgbG9zIHBhcXVldGVzIHNlIGhhY2UgKip1bmEgc29sYSB2ZXoqKiBwb3INCj4gY29tcHV0YWRvcmEuDQo+DQo+IExhIGNhcmdhIGRlIGxvcyBwYXF1ZXRlc2Ugc2UgaGFjZSAqKmNhZGEgdmV6IHF1ZSBzZSBhYnJlKiogdW4gc2NyaXB0Lg0KDQojIyMgSW5zdGFsYXIgeSBjYXJnYXIgKippbnRyb1I0aHIqKg0KDQpQYXJhIGVzdGUgY3Vyc28gZGVzYXJyb2xsYW1vcyB1biBwYXF1ZXRlLCBgaW50cm9SNGhyYCBxdWUgY29uc2lzdGUgZW4NCnVuYSBzZXJpZSBkZSB0dXRvcmlhbGVzIGludGVyYWN0aXZvcyBxdWUgaXJlbW9zIGNvbXBhcnRpZW5kbyBhIGxvIGxhcmdvDQpkZWwgY3Vyc28uDQoNCmBpbnRyb1I0aHJgIG5vIGVzIHVuIHBhcXVldGUgcHVibGljYWRvIGVuDQpbQ1JBTl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvKSBwb3IgbG8gY3VhbCBoYXkgcXVlIGRlc2NhcmdhciBsYQ0KdmVyc2nDs24gZGUgZGVzYXJyb2xsbyBkZXNkZSBlbCBbcmVwb3NpdG9yaW8gZGUNCkdpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2NoZWNob2lkL2ludHJvUjRocikgeSBzZWd1aXIgbG9zIHNpZ3VpZW50ZXMNCnBhc29zOg0KDQpgYGB7ciBpbnRyb3I0aHIxLCBldmFsPUZBTFNFfQ0KIyBJbnN0YWxhciBsb3MgcGFxdWV0ZXMgcmVtb3RlcyB5IGxlYXJucg0KaW5zdGFsbC5wYWNrYWdlcygicmVtb3RlcyIpDQppbnN0YWxsLnBhY2thZ2VzKCJsZWFybnIiKQ0KDQojIEluc3RhbGFyIGVsIHBhcXVldGUgaW50cm9SNGhyIGRlc2RlIEdpdEh1Yg0KcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImNoZWNob2lkL2ludHJvUjRociIpDQpgYGANCg0KTGEgbm90YWNpw7NuIGByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigpYCByZWVtcGxhemEgcGFyY2lhbG1lbnRlIGEgbGENCmZ1bmNpw7NuIGBsaWJyYXJ5KClgIHlhIHF1ZSBubyBjYXJnYSBlbCBwYXF1ZXRlIChhaG9ycmFuZG8gbWVtb3JpYSkgeQ0Kc8OzbG8gdXRpbGl6YSBsYSBmdW5jacOzbiBxdWUgZXN0w6EgaW52b2NhbmRvLg0KDQpQb3IgZXNvIGx1ZWdvIHVzYW1vcyBsYSBzaWd1aWVudGUgc2ludGF4aXMgcGFyYSBjYXJnYXIgdW4gdHV0b3JpYWwNCmludGVyYWN0aXZvLg0KDQpgYGB7ciBpbnRyb3I0aHIyLCBldmFsPUZBTFNFfQ0KbGVhcm5yOjpydW5fdHV0b3JpYWwoInNlc2lvbjAiLCAiaW50cm9SNGhyIikNCmBgYA0KDQpEb25kZToNCg0KLSAgIGBzZXNpb24wYCBlcyBlbCBub21icmUgZGVsIHR1dG9yaWFsDQotICAgYGludHJvUjRocmAgZXMgZWwgbm9tYnJlIGRlbCBwYXF1ZXRlDQoNCiMgQ2FyZ2EgZGUgZGF0b3MNCg0KQ29tbyBncmFuIHBhcnRlIGRlIGxvcyBkYXRvcyBxdWUgdmFtb3MgYSB1c2FyIGVuIGxhIHZpZGEgc29uIGFyY2hpdm9zIGRlDQpFeGNlbCB2YW1vcyBhIHVzYXIgbGEgZnVuY2nDs24gYHJlYWRfZXhjZWwoKWAgZGVsIHBhcXVldGUgYHJlYWR4bGAgcGFyYQ0KbGV2YW50YXIgZG9zIGFyY2hpdm9zOiBlbCBhcmNoaXZvIGBtYWVzdHJvLnhsc3hgIHF1ZSBjb250aWVuZSB1biBsaXN0YWRvDQpkZSBlbXBsZWFkb3MgZGUgdW5hIGVtcHJlc2EsIHkgZWwgYXJjaGl2byBgc2FsYXJpb3MueGxzeGAgcXVlIGNvbnRpZW5lDQplbCBzdWVsZG8gYmFzZSBkZSBhbGd1bm9zIGRlIGxvcyBlbXBsZWFkb3MgZGVsIGxpc3RhZG8uDQoNCmBgYHtyIGNhcmdhLWV4Y2VsfQ0KIyBDYXJnYXIgZWwgYXJjaGl2byBtYWVzdHJvLnhsc3gNCm1hZXN0cm8gPC0gcmVhZF9leGNlbCgibWFlc3Ryby54bHN4IikNCg0KIyBDYXJnYXIgZWwgYXJjaGl2byBzYWxhcmlvcy54bHN4DQpzYWxhcmlvcyA8LSByZWFkX2V4Y2VsKCJzYWxhcmlvcy54bHN4IikNCmBgYA0KDQpMb3MgYXJjaGl2b3Mgbm8gbmVjZXNhcmlhbWVudGUgdGllbmVuIHF1ZSBlc3RhciBlbiB1bmEgY29tcHV0YWRvcmEuDQpUYW1iacOpbiBzZSBwdWVkZW4gY2FyZ2FyIGFyY2hpdm9zIGRlc2RlIGludGVybmV0LCBwb3IgZWplbXBsbywgZGVzZGUNCltyZXBvc2l0b3Jpb3MgZGUNCkdpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL3I0aHIva2l3aTIwMjAvYmxvYi9tYWluL3JoX2FyLmNzdik6DQoNCmBgYHtyIGNhcmdhLWNzdn0NCiMgQ2FyZ2FyIHVuIGFyY2hpdm8gZGVzZGUgR2l0SHViDQplbmN1ZXN0YSA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3I0aHIva2l3aTIwMjAvbWFpbi9yaF9hci5jc3YiLA0KICAgICAgICAgICAgICAgICAgICAgc2VwID0gIjsiLA0KICAgICAgICAgICAgICAgICAgICAgZW5jb2RpbmcgPSAiVVRGLTgiKQ0KYGBgDQoNCk90cmEgYWx0ZXJuYXRpdmEgcGFyYSBsZWVyIGFyY2hpdm9zIGAuY3N2YCBjdWFuZG8gZWwgZGVsaW1pdGFkb3IgZW50cmUNCmNhbXBvcyBubyBlcyB1bmEgY29tYSAoYCxgKSBlcyB1c2FyIGxhIGZ1bmNpw7NuIGByZWFkX2RlbGltKClgIGRlbA0KcGFxdWV0ZSBgcmVhZHJgIHF1ZSB2aWVuZSBlbiBgdGlkeXZlcnNlYC4NCg0KYGBge3IgY2FyZ2EtY3N2Mn0NCmVuY3Vlc3RhIDwtIHJlYWRfZGVsaW0oImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yNGhyL2tpd2kyMDIwL21haW4vcmhfYXIuY3N2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgZGVsaW0gPSAiOyIpICMgSW5kaWNhbW9zIGVsIGRlbGltaXRhZG9yIGRlIGNhbXBvcw0KYGBgDQoNClVuYSBmdW5jacOzbiBxdWUgbm9zIHBlcm1pdGUgdmVyIGVsIGNvbnRlbmlkbyBkZSBlc3RvcyAqZGF0YSBmcmFtZXMqIGVzDQpsYSBmdW5jacOzbiBgVmlldygpYC4NCg0KYGBge3IgZWRhMSwgZWNobz1GQUxTRX0NCiMgVmVyIGVsIGNvbnRlbmlkbyBkZSBsb3MgZGF0YSBmcmFtZXMgbWFlc3Rybywgc2FsYXJpb3MgeSBlbmN1ZXN0YQ0KVmlldyhtYWVzdHJvKQ0KVmlldyhzYWxhcmlvcykNClZpZXcoZW5jdWVzdGEpDQpgYGANCg0KT3RyYSBmdW5jacOzbiBpbnRlcmVzYW50ZSBwYXJhIGV4cGxvcmFyIGVsIGNvbnRlbmlkbyBkZSB1biBkYXRhIGZyYW1lIGVzDQpsYSBmdW5jacOzbiBgZ2xpbXBzZSgpYCBkZWwgcGFxdWV0ZSBgZHBseXJgIHF1ZSBmb3JtYSBwYXJ0ZSBkZWwgcGFxdWV0ZQ0KYHRpZHl2ZXJzZWAuDQoNCmBgYHtyIGVkYTJ9DQojIEV4cGxvcmFyIGxvcyBkYXRhZnJhbWVzIG1hZXN0cm8sIHNhbGFyaW9zIHkgZW5jdWVzdGENCmdsaW1wc2UobWFlc3RybykNCmdsaW1wc2Uoc2FsYXJpb3MpDQpnbGltcHNlKGVuY3Vlc3RhKQ0KYGBgDQoNCk90cmEgZnVuY2nDs24gcXVlIHNlIHN1ZWxlIHVzYXIgbXVjaG8gcGFyYSBleHBsb3JhciBsb3MgZGF0b3Mgc2UgbGxhbWENCmBzdW1tYXJ5KClgLg0KDQpgYGB7ciBlZGEzfQ0KIyBVc2FyIGxhIGZ1bmNpw7NuIHN1bW1hcnkoKSBjb24gZWwgb2JqZXRvICdtYWVzdHJvJyB5ICdzYWxhcmlvcycNCnN1bW1hcnkobWFlc3RybykNCnN1bW1hcnkoc2FsYXJpb3MpDQpzdW1tYXJ5KGVuY3Vlc3RhKQ0KYGBgDQoNCkVzdG8gcXVlIGVzdGFtb3MgaGFjaWVuZG8gc2UgbGxhbWEgKipBbsOhbGlzaXMgRXhwbG9yYXRvcmlvIGRlIERhdG9zKiosDQpwdWVkZW4gdmVyIGxhIHNlc2nDs24gcXVlIGhpY2ltb3MgZW4gUjRIUiBDbHViIGRlIFIgcGFyYSBSUkhIIGVuDQpbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9RVFdFbklvdlBfNCkuDQoNCiMgVGlwb3MgZGUgdmFyaWFibGVzDQoNCkVuIFIgbm9zIHZhbW9zIGEgZW5jb250cmFyIGNvbiB2YXJpb3MgdGlwb3MgZGUgdmFyaWFibGVzLiBMb3MgbcOhcw0KdXN1YWxlcyBzb246DQoNCi0gICAqKm51bcOpcmljYXMqKjogc29uIGxhcyB2YXJpYWJsZXMgcXVlIG5vcyBwZXJtaXRlbiBoYWNlciB0b2RvIHRpcG8gZGUNCiAgICBjw6FsY3Vsb3MgYXJpdG3DqXRpY29zIHkgZXN0YWTDrXN0aWNvcy4gU2UgbG9zIHN1ZWxlbiBpZGVudGlmaWNhciBjb21vDQogICAgYGludGAgYSBsb3MgbsO6bWVyb3MgZW50ZXJvcyAocXVlIG5vIHRpZW5lbiBkZWNpbWFsZXMpIG8gYGRibGAgY3VhbmRvDQogICAgdGllbmVuIGRlY2ltYWxlcy4gQSB2ZWNlcyB0YW1iacOpbiBzZSBsb3MgZW5jdWVudHJhIGNvbW8gYG51bWAuDQotICAgKipjaGFyYWN0ZXIqKjogc29uIHZhcmlhYmxlcyBub21pbmFsZXMsIG8gc2VhIHF1ZSBub21icmFuIGNvc2FzIHkgbm8NCiAgICB0aWVuZW4gamVyYXJxdcOtYXMgbmkgZGlzdGFuY2lhcyBlbnRyZSBsb3MgdmFsb3Jlcy4gVXN1YWxtZW50ZSBzZQ0KICAgIGlkZW50aWZpY2FuIGNvbiBsYXMgc2lnbGFzIGBjaHJgDQotICAgKipmYWN0b3IqKjogbGFzIHZhcmlhYmxlcyBkZSB0aXBvIGZhY3RvciBub3MgcGVybWl0ZW4gY3JlYXINCiAgICB2YXJpYWJsZXMgb3JkaW5hbGVzLCBlcyBkZWNpciBxdWUgdGVuZ2FuIHVuIG9yZGVuIG8gamVyYXJxdcOtYQ0KICAgIGltcGzDrWNpdGEuIEEgZXN0ZSB0aXBvIGRlIHZhcmlhYmxlcyBub3JtYWxtZW50ZSBsYSBpZGVudGlmaWNhbW9zDQogICAgY29tbyBgZmN0YC4NCg0KVGFtYmnDqW4gZXhpc3RlbiBsYXMgdmFyaWFibGVzIGRlIHRpcG8gKmzDs2dpY2FzKiwgdGFtYmnDqW4gY29ub2NpZGFzIGNvbW8NCmJvb2xlYW5hcywgcXVlIHPDs2xvIHB1ZWRlbiBhc3VtaXIgbG9zIHZhbG9yZXMgYFRSVUVgIG8gYEZBTFNFYCAobw0KdGFtYmnDqW4gYFRgIG8gYEZgKSwgeSBsYXMgdmFyaWFibGVzIGRlIGZlY2hhcyB5IGhvcmFzLCBxdWUgY29tbyBzaWVtcHJlDQplcyB1biBxdWlsb21ibywgdGVuZW1vcyBbdG9kYSB1bmEgc2VzacOzbiBkZWRpY2FkYSBhIGVzbyBlbiBSNEhSIENsdWIgZGUNClIgcGFyYQ0KUlJISF0oaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2RyaXZlL2ZvbGRlcnMvMTc3NWg4elV2eC1USlJiLWJuTUxTNTVPaVIySEdhd25pP3VzcD1zaGFyaW5nKS4NCg0KVm9sdmFtb3MgYSB1c2FyIGxhIGZ1bmNpw7NuIGBnbGltcHNlKClgIHBhcmEgdmVyIGxvcyB0aXBvcyBkZSB2YXJpYWJsZQ0KcXVlIHRpZW5lbiBlbCBkYXRhIGZyYW1lIGBzYWxhcmlvc2AuDQoNCmBgYHtyIHZhcmlhYmxlc30NCiMgVmVyIGxvcyB0aXBvcyBkZSB2YXJpYWJsZXMgZGUgc2FsYXJpb3MNCmdsaW1wc2Uoc2FsYXJpb3MpDQpgYGANCg0KQWhvcmEgcmVhbGljZW1vcyB1biBhbsOhbGlzaXMgZXhwbG9yYXRvcmlvIGNvbiBsYSBmdW5jacOzbiBgc3VtbWFyeSgpYC4NCg0KYGBge3IgdmFyczJ9DQojIFVzYXIgbGEgZnVuY2nDs24gc3VtbWFyeSgpIGNvbiBzYWxhcmlvcw0Kc3VtbWFyeShzYWxhcmlvcykNCmBgYA0KDQpDb24gbGFzIHZhcmlhYmxlcyBudW3DqXJpY2FzIHBvZGVtb3MgYXByZWNpYXIgYWxndW5hcyBtZWRpZGFzIGRlIHJlc3VtZW4NCnRhbGVzIGNvbW86DQoNCi0gICBMb3MgdmFsb3JlcyBtw61uaW1vcyB5IG3DoXhpbW9zLg0KLSAgIEVsIHByb21lZGlvIChtZWFuKSB5IGxhIG1lZGlhbmEgKG1lZGlhbikNCi0gICBFbCAxwrAgeSBlbCAzwrAgY3VhcnRpbC4NCg0KQ3VhbmRvIGxhIHZhcmlhYmxlIGVzIGRlIHRpcG8gYGNocmAgc29sbyBwb2RlbW9zIHZlciBsYSBjYW50aWRhZCBkZQ0KZWxlbWVudG9zIHF1ZSB0aWVuZSBlc2EgY29sdW1uYS4NCg0KUHJvYmVtb3MgbG8gc2lndWllbnRlOg0KDQpgYGB7ciB2YXJzM30NCiMgQ29udmVydGltb3MgbGEgdmFyaWFibGUgY2hyIGVuIGZjdA0Kc2FsYXJpb3MkUFVFU1RPIDwtIGFzLmZhY3RvcihzYWxhcmlvcyRQVUVTVE8pDQpgYGANCg0KQWhvcmEgdm9sdmFtb3MgYSByZXZpc2FyIGVsIGRhdGFmcmFtZSwgcHJpbWVybyBjb24gbGEgZnVuY2nDs24NCmBnbGltcHNlKClgIHkgbHVlZ28gY29uIGxhIGZ1bmNpw7NuIGBzdW1tYXJ5KClgLg0KDQpgYGB7ciB2YXJzNH0NCiMgVXNhciBsYSBmdW5jacOzbiBnbGltcHNlKCkgY29uIGVsIGRhdGFmcmFtZSBzYWxhcmlvcw0KZ2xpbXBzZShzYWxhcmlvcykNCg0KIyBZIGFob3JhIHVzYXIgbGEgZnVuY2nDs24gc3VtbWFyeSgpDQpzdW1tYXJ5KHNhbGFyaW9zKQ0KYGBgDQoNCkFob3JhIHF1ZSBsYSB2YXJpYWJsZSBgUFVFU1RPYCBlcyB1bmEgdmFyaWFibGUgZGUgdGlwbyBgZmFjdG9yYCBwb2RlbW9zDQp2ZXIgY29uIGxhIGZ1bmNpw7NuIGBzdW1tYXJ5KClgIGxhIGNhbnRpZGFkIGRlIGNhc29zIHF1ZSB0ZW5lbW9zIHBvcg0KcHVlc3RvLg0KDQpPdHJhIGRlIGxhcyBjb3NhcyBxdWUgcG9kZW1vcyBoYWNlciBjb24gbGFzIHZhcmlhYmxlcyBkZSB0aXBvIGBmYWN0b3JgDQplcyBvcmRlbmFybGFzLiBWYW1vcyBhIGNyZWFyIHVuYSB2ZXJzacOzbiBkZWwgZGF0YSBmcmFtZSBzw7NsbyBjb24gYWxndW5vcw0KdmFsb3JlcyBwYXJhIGhhY2VyIG3DoXMgZsOhY2lsIGVsIGVqZW1wbG8uDQoNCmBgYHtyIHZhcnM1fQ0KIyBDcmVhbW9zIHVuIG51ZXZvIGRhdGEgZnJhbWUNCnNhbGFyaW9zMiA8LSBzYWxhcmlvcyAlPiUgDQogIGZpbHRlcihQVUVTVE8gJWluJSBjKCJHRVJFTlRFIiwgIkpFRkUiLCAiQU5BTElTVEEiLCAiVEVDLiBNQU5URU5JTUlFTlRPIikpDQoNCiMgWSBhaG9yYSByZWFsaWNlbW9zIHVuIGdyw6FmaWNvIGRlIGJhcnJhcyBjb250YW5kbyBsYSBjYW50aWRhZCBkZSBwZXJzb25hcyBlbiBjYWRhIHB1ZXN0bw0KZ2dwbG90KHNhbGFyaW9zMiwgYWVzKHggPSBQVUVTVE8pKSArIA0KICBnZW9tX2JhcigpDQpgYGANCg0KRWwgZ3LDoWZpY28gYW50ZXJpb3IsIGRlc2RlIGVsIHB1bnRvIGRlIHZpc3RhIHTDqWNuaWNvIGVzIGNvcnJlY3RvLCBwb3JxdWUNCm5vcyBtdWVzdHJhIGxhIGNhbnRpZGFkIGRlIHBlcnNvbmFzIGVuIGNhZGEgdW5hIGRlIGxhcyBwb3NpY2lvbmVzLCBwZXJvDQplbCBncsOhZmljbyBlc3TDoSBkZXNvcmRlbmFkbyBsbyBjdWFsIG5vIGVzIGlkZWFsIHBvcnF1ZSBubyBtdWVzdHJhIGxhDQpqZXJhcnF1w61hIGRlIGxhcyBwb3NpY2lvbmVzLg0KDQpDb24gbGFzIHZhcmlhYmxlcyBkZSB0aXBvIGZhY3RvciBwb2RlbW9zIGNvcnJlZ2lyIGVzby4NCg0KYGBge3IgdmFyczZ9DQojIE1vZGlmaXF1ZW1vcyBlbCBvcmRlbiBkZSBsYSBjb2x1bW5hIFBVRVNUTw0Kc2FsYXJpb3MyIDwtIHNhbGFyaW9zMiAlPiUgDQogIG11dGF0ZShQVUVTVE8gPSBmYWN0b3IoUFVFU1RPLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJHRVJFTlRFIiwgIkpFRkUiLCAiVEVDLiBNQU5URU5JTUlFTlRPIiwgIkFOQUxJU1RBIikpKQ0KDQojIFJlcGl0YW1vcyBlbCBncsOhZmljbyBhbnRlcmlvcg0KZ2dwbG90KHNhbGFyaW9zMiwgYWVzKHggPSBQVUVTVE8pKSArDQogIGdlb21fYmFyKCkNCmBgYA0KDQpQYXJhIHNhYmVyIG3DoXMgY29tbyB0cmFiYWphciBjb24gdmFyaWFibGVzIGBmYWN0b3JgIHB1ZWRlbiB2ZXIgW2VzdGUNCmNvbnRlbmlkbyBkZSBSNEhSIENsdWIgZGUgUiBwYXJhIFJSSEhdKGh0dHBzOi8veW91dHUuYmUvazNpZFZnRmluMncpLg0KDQojIyBDaGVjayBvdXQNCg0KUGFyYSBzZWd1aXIgcHJhY3RpY2FuZG8gY29uIHVuIGNvbnRlbmlkbyBtdXkgYWNjZXNpYmxlIHkgZW50cmV0ZW5pZG8NCnB1ZWRlbiBidXNjYXIgZXN0b3MgbGlicm9zOg0KDQotICAgW0NpZW5jaWEgZGUgRGF0b3MgcGFyYSBHZW50ZQ0KICAgIFNvY2lhYmxlXShodHRwczovL2JpdHNhbmRicmlja3MuZ2l0aHViLmlvL2NpZW5jaWFfZGVfZGF0b3NfZ2VudGVfc29jaWFibGUvKQ0KICAgIGRlIEFudG9uaW8gVmF6cXVleiBCcnVzdC4NCi0gICBbWWFScnIhIFRoZSBQaXJhdGUncyBHdWlkZSB0bw0KICAgIFJdKGh0dHBzOi8vYm9va2Rvd24ub3JnL25kcGhpbGxpcHMvWWFScnIvKSAoZW4gaW5nbMOpcykgZGUNCiAgICBOYXRoYW5pZWwgRC4gUGhpbGxpcHMuDQoNCkFkZW3DoXMsIHB1ZWRlbiByZXZpc2FyIHkgdXRpbGl6YXIgdG9kbyBlbCBjb250ZW5pZG8gZGUgUjRIUiBDbHViIGRlIFINCnBhcmEgUlJISCBkaXNwb25pYmxlIGVuIFtHb29nbGUNCkRyaXZlXShodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZHJpdmUvZm9sZGVycy8xUWNrM3pfdDZYTFJYYjJ2Yk4tMDA5MzFEZ2RKWjB5c2U/dXNwPXNoYXJpbmcpDQp5DQpbWW91VHViZV0oaHR0cHM6Ly95b3V0dWJlLmNvbS9wbGF5bGlzdD9saXN0PVBMWnVWeXRVSnJ4UWxjcXU2bC1QM291NHZWMm1SSlUyS2EpLg0KDQpSZWN1ZXJkZW4gaGFjZXIgdG9kYXMgbGFzIGNvbnN1bHRhcyBlbiBlbCBjYW5hbCAqKiNhdXhpbGlvKiogZW4NCltTbGFja10oaHR0cHM6Ly9qb2luLnNsYWNrLmNvbS90L3I0aHIvc2hhcmVkX2ludml0ZS96dC1vY3JpeXg1ZS1ucVh1d1dlRHlPS20ySUNVSmpodTZnKQ0K