Integrantes:

Luis Angel Elizondo Gallegos A01198186

Avril Lobato Delgado A00833113

Genaro RodrĆ­guez A00833172

Enrique Pablos A00835037

Diego PerƩz A01275561

LibrerĆ­as

knitr::opts_chunk$set(
    echo = TRUE,
    message = FALSE,
    warning = FALSE
)
library(ggplot2)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(readxl)
library(tidyr)
library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(purrr)
library(plotly)
## Warning: package 'plotly' was built under R version 4.3.3
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
library(forecast)
## Warning: package 'forecast' was built under R version 4.3.3
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(readxl)
library(DataExplorer)
library(dplyr)
library(ggplot2)
library(tm)
## Warning: package 'tm' was built under R version 4.3.3
## Loading required package: NLP
## 
## Attaching package: 'NLP'
## The following object is masked from 'package:ggplot2':
## 
##     annotate
library(wordcloud)
## Warning: package 'wordcloud' was built under R version 4.3.3
## Loading required package: RColorBrewer
library(cluster)
library(factoextra) 
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
library(purrr)
library(pROC)
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
library(rpart)
library(rpart.plot)
library(e1071)
## Warning: package 'e1071' was built under R version 4.3.3
library(ggpubr)
## 
## Attaching package: 'ggpubr'
## The following object is masked from 'package:forecast':
## 
##     gghistogram
library(dlookr)
## Registered S3 methods overwritten by 'dlookr':
##   method          from  
##   plot.transform  scales
##   print.transform scales
## 
## Attaching package: 'dlookr'
## The following objects are masked from 'package:e1071':
## 
##     kurtosis, skewness
## The following object is masked from 'package:tidyr':
## 
##     extract
## The following object is masked from 'package:base':
## 
##     transform
library(zoo)
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(caret)
## Warning: package 'caret' was built under R version 4.3.3
## Loading required package: lattice
## 
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
## 
##     lift
library(stats)
library(tseries)
## Warning: package 'tseries' was built under R version 4.3.3
library(readr)
library(vars)
## Warning: package 'vars' was built under R version 4.3.3
## Loading required package: MASS
## 
## Attaching package: 'MASS'
## The following object is masked from 'package:plotly':
## 
##     select
## The following object is masked from 'package:dplyr':
## 
##     select
## Loading required package: strucchange
## Warning: package 'strucchange' was built under R version 4.3.3
## Loading required package: sandwich
## Loading required package: urca
## Warning: package 'urca' was built under R version 4.3.3
## Loading required package: lmtest
## 
## Attaching package: 'vars'
## The following object is masked from 'package:dlookr':
## 
##     normality
library(syuzhet)
## Warning: package 'syuzhet' was built under R version 4.3.3
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(plotly)
library(scales)
## 
## Attaching package: 'scales'
## The following object is masked from 'package:syuzhet':
## 
##     rescale
## The following object is masked from 'package:readr':
## 
##     col_factor
## The following object is masked from 'package:purrr':
## 
##     discard
library(readxl)
library(randomForest)
## randomForest 4.7-1.1
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## The following object is masked from 'package:gridExtra':
## 
##     combine
## The following object is masked from 'package:dplyr':
## 
##     combine
## The following object is masked from 'package:ggplot2':
## 
##     margin

Limpieza de Bases de Datos

Descripción de la estructura general de los datos previo a la limpieza

La base para la situación problema de Recursos Humanos en Form estÔ conformada por 29 variables para un total de 553 empleados registrados, entre las cuÔles se presenta información personal del empleado como su nombre, fecha de nacimiento, género y lugar de residencia, así como datos laborales que incluyen fecha de inicio en Form, departamento, puesto, salario, entre otros datos del tipo fiscal. Esta fue creada a partir de la unión previa de la base de empleados actuales de Form hasta el 2024, así como la base de Bajas Form 2024.

En consideración de esto, la base resultante presenta información tanto de empleados que continĆŗan sus actividades laborales en la empresa (empleado con mayor antigüedad en 2010), como de aquellos que han abandonado sus puestos desde 2022. Para estos Ćŗltimos se consideran datos relacionados con la baja, como la fecha, motivo y detalles de Ć©sta. Asimismo, es necesario mencionar que mediante la unión de ambas bases, fue posible determinar la variable dependiente a analizar, la cual corresponde a ā€œRenunciaā€, una variable previamente creada para diferenciar a los empleados de cada previa, es decir, aquellos que SĆ­ renuncian y los que No.

# Leer y observar el archivo
rh_alt <- read_excel("form_rh_datos.xlsx")
str(rh_alt)
## tibble [553 Ɨ 29] (S3: tbl_df/tbl/data.frame)
##  $ No_Empleado        : num [1:553] 10 12 13 19 20 29 35 37 42 44 ...
##  $ Apellidos          : chr [1:553] "Luna Lopez" "Suarez Romo" "Cruz Ramos" "Garcia Perales" ...
##  $ Nombre             : chr [1:553] "Yolanda Judith" "Julio Cesar" "Victor Abel" "Yuliana Mireya" ...
##  $ Fecha_Nacimiento   : POSIXct[1:553], format: "1985-08-18" "1969-06-27" ...
##  $ Genero             : chr [1:553] "Femenino" "Masculino" "Masculino" "Femenino" ...
##  $ RFC                : chr [1:553] "LULY8508183C7" "SURJ690627TK7" "CURV890621SC2" "GAPY840819V87" ...
##  $ Fecha_Alta         : POSIXct[1:553], format: "2017-02-20" "2017-12-01" ...
##  $ Primer_mes         : POSIXct[1:553], format: "2017-03-22" "2017-12-31" ...
##  $ Cuarto_mes         : POSIXct[1:553], format: "2017-06-20" "2018-03-31" ...
##  $ Baja               : POSIXct[1:553], format: "2023-10-02" "2023-01-05" ...
##  $ Puesto             : chr [1:553] "Costurera" "Gestor" "Chofer" "Ayudante General" ...
##  $ Departamento       : chr [1:553] "Costura" "Embarques" "Embarques" "Produccion Cartón MC" ...
##  $ No_SeguroSocial    : chr [1:553] "43108518077" "43956909626" "43058956681" "43008429805" ...
##  $ Salario_diario     : num [1:553] 153 177 177 144 177 ...
##  $ Fact_Infonavit     : chr [1:553] NA NA NA NA ...
##  $ No_CreditoInfonavit: chr [1:553] NA NA NA NA ...
##  $ Lugar_nacimiento   : chr [1:553] NA NA NA NA ...
##  $ CURP               : chr [1:553] "LULY850818MNLNPL02" "SURJ690627HNLRML04" "CURV890621HVZRMC05" "GAPY840819MNLRRL00" ...
##  $ Calle              : chr [1:553] "De Los Carniceros" "Mar Rojo" "Altamura" "Santa Rosa" ...
##  $ No_Interno         : chr [1:553] NA NA "216" NA ...
##  $ Colonia            : chr [1:553] "C. H. Blas Chumacero C.T.M" "Loma Linda" "Lomas De San Martin" "El Rosario" ...
##  $ Municipio          : chr [1:553] "San Nicolas De Los G" "Monterrey" "Pesqueria" "Apodaca" ...
##  $ Estado             : chr [1:553] "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ Codigo_Postal      : num [1:553] 66473 64120 66673 66647 25115 ...
##  $ Estado_Civil       : chr [1:553] "Casado" "Casado" "Casado" "Unión libre" ...
##  $ Tarjeta_Cuenta     : chr [1:553] "BANORTE" "BANORTE" "BANORTE" "BANORTE" ...
##  $ Renuncia           : chr [1:553] "Si" "Si" "Si" "Si" ...
##  $ MotivoRenuncia     : chr [1:553] "Personal" "Interna" "No le agradaba horario de entrada/salida" NA ...
##  $ DetallesRenuncia   : chr [1:553] "DejarÔ de trabajar para atender a sus hijos en casa" "Se realizó cambio de empresa (Form - Poligrama)" NA NA ...
summary(rh_alt)
##   No_Empleado     Apellidos            Nombre         
##  Min.   :  1.0   Length:553         Length:553        
##  1st Qu.:202.0   Class :character   Class :character  
##  Median :350.0   Mode  :character   Mode  :character  
##  Mean   :380.3                                        
##  3rd Qu.:501.0                                        
##  Max.   :851.0                                        
##                                                       
##  Fecha_Nacimiento                     Genero              RFC           
##  Min.   :1955-09-10 00:00:00.000   Length:553         Length:553        
##  1st Qu.:1983-08-14 00:00:00.000   Class :character   Class :character  
##  Median :1994-05-06 00:00:00.000   Mode  :character   Mode  :character  
##  Mean   :1991-05-20 09:17:15.080                                        
##  3rd Qu.:2000-10-30 00:00:00.000                                        
##  Max.   :2022-10-31 00:00:00.000                                        
##                                                                         
##    Fecha_Alta                       Primer_mes                    
##  Min.   :2010-07-01 00:00:00.00   Min.   :2010-07-31 00:00:00.00  
##  1st Qu.:2022-12-23 00:00:00.00   1st Qu.:2023-01-22 00:00:00.00  
##  Median :2023-05-24 00:00:00.00   Median :2023-06-23 00:00:00.00  
##  Mean   :2023-02-17 05:30:42.30   Mean   :2023-03-19 08:35:35.25  
##  3rd Qu.:2023-08-25 00:00:00.00   3rd Qu.:2023-09-24 00:00:00.00  
##  Max.   :2024-05-07 00:00:00.00   Max.   :2024-06-06 00:00:00.00  
##                                                                   
##    Cuarto_mes                          Baja                       
##  Min.   :2010-10-29 00:00:00.00   Min.   :2022-01-25 00:00:00.00  
##  1st Qu.:2023-04-24 00:00:00.00   1st Qu.:2023-03-02 00:00:00.00  
##  Median :2023-09-23 00:00:00.00   Median :2023-06-07 00:00:00.00  
##  Mean   :2023-06-19 01:38:57.06   Mean   :2023-05-24 11:12:43.64  
##  3rd Qu.:2023-12-25 00:00:00.00   3rd Qu.:2023-08-14 06:00:00.00  
##  Max.   :2024-09-06 00:00:00.00   Max.   :2024-02-22 00:00:00.00  
##                                   NA's   :157                     
##     Puesto          Departamento       No_SeguroSocial    Salario_diario     
##  Length:553         Length:553         Length:553         Min.   :1.440e+02  
##  Class :character   Class :character   Class :character   1st Qu.:2.180e+02  
##  Mode  :character   Mode  :character   Mode  :character   Median :2.180e+02  
##                                                           Mean   :2.751e+06  
##                                                           3rd Qu.:2.180e+02  
##                                                           Max.   :1.517e+09  
##                                                                              
##  Fact_Infonavit     No_CreditoInfonavit Lugar_nacimiento       CURP          
##  Length:553         Length:553          Length:553         Length:553        
##  Class :character   Class :character    Class :character   Class :character  
##  Mode  :character   Mode  :character    Mode  :character   Mode  :character  
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##     Calle            No_Interno          Colonia           Municipio        
##  Length:553         Length:553         Length:553         Length:553        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     Estado          Codigo_Postal   Estado_Civil       Tarjeta_Cuenta    
##  Length:553         Min.   : 9999   Length:553         Length:553        
##  Class :character   1st Qu.:66646   Class :character   Class :character  
##  Mode  :character   Median :66646   Mode  :character   Mode  :character  
##                     Mean   :65125                                        
##                     3rd Qu.:66670                                        
##                     Max.   :99999                                        
##                                                                          
##    Renuncia         MotivoRenuncia     DetallesRenuncia  
##  Length:553         Length:553         Length:553        
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
##                                                          
## 

Tras haber comprendido la estructura y contenido de la base de datos, se llevó a cabo una serie de medidas para la limpieza y ajuste para su adecuado procesamiento. Estos se muestran a continuación:

Limpieza General

Renombrar columnas

Se optó por modificar el nombre de las columnas del dataframe con la finalidad de facilitar el manejo y entendimiento de las columnas durante la manipulación de los datos, considerando aspectos como el evitar los espacios en blanco y el uso de símbolos para cada uno de los nombres. Los nombres definitivos para cada columna puede apreciarse a continuación:

# Renombrar columnas
colnames(rh_alt) <- c('No_Empleado','Apellidos', 'Nombre','Fecha_Nacimiento','Genero', 'RFC',
                       'Fecha_Alta','Primer_mes','Cuarto_mes','Baja','Puesto','Departamento',
                       'No_SeguroSocial','Salario_diario', 'Fact_Infonavit','No_CreditoInfonavit',
                       'Lugar_nacimiento','CURP', 'Calle', 'No_Interno','Colonia','Municipio',
                       'Estado','Codigo_Postal','Estado_Civil', 'Tarjeta', 'Renuncia', 
                       'MotivoRenuncia', 'DetalleRenuncia')

Variables con valores nulos y su manejo

Primeramente se identificaron nulos sobre las variables listadas a continuación:

  • Baja (Fecha baja)

  • Departamento

  • Fact_Infonavit

  • No_CreditoInfonavit

  • MotivoRenuncia

  • DetalleRenuncia

  • Tarjeta

  • Lugar de Nacimiento

Para atender esta situación fueron empleadas dos medidas:

  • Reemplazar valores nulos: Esta medida consistió en cambiar los valores nulos por una nueva clase. Para el primer caso, la columna Departamento, se definió como Ɓrea Desconocida, con la intención de no perder los datos para estos empleados, sino que en caso de destacar como variable en los modelos, tener la capacidad de analizarla mediante su variable complementaria ā€œPuestoā€. Por otro lado, las variables relacionadas con el comportamiento de renuncia, incluyendo MotivoRenuncia y DetalleRenuncia, para los empleados actualmente activos, al no haber renunciado no es posible contar con valores para esta columna, por lo que se crea la clase ā€œNo Aplicaā€.

  • Insertar NAs: Debido a la naturaleza del resto de variables consideradas, una falta en sus registros no puede imputarse directamente. Ante esto, directamente fue insertado el valor ā€œNaā€. Esta medida no tendrĆ” un efecto perjudicial sobre los niveles de las variables, pues los casos en los que aplica consideran información muy amplia y especĆ­fica, por lo que al no ser de utilidad, serĆ”n descartadas en el proceso de selección de variables para la especificación de los modelos mĆ”s adelante.

# Identificar variables con valores nulos
missing_values <- sapply(rh_alt, function(x) sum(is.na(x)))
missing_values
##         No_Empleado           Apellidos              Nombre    Fecha_Nacimiento 
##                   0                   0                   0                   0 
##              Genero                 RFC          Fecha_Alta          Primer_mes 
##                   0                   0                   0                   0 
##          Cuarto_mes                Baja              Puesto        Departamento 
##                   0                 157                   0                 369 
##     No_SeguroSocial      Salario_diario      Fact_Infonavit No_CreditoInfonavit 
##                   0                   0                  46                  35 
##    Lugar_nacimiento                CURP               Calle          No_Interno 
##                  31                   0                   0                  17 
##             Colonia           Municipio              Estado       Codigo_Postal 
##                   0                   0                   0                   0 
##        Estado_Civil             Tarjeta            Renuncia      MotivoRenuncia 
##                   0                  50                   0                 481 
##     DetalleRenuncia 
##                 516
# Imputar/Reemplazar valores nulos

## Departamento
rh_alt <- rh_alt %>%
  mutate(Departamento = ifelse(is.na(Departamento), "Area No Definida", Departamento))

## MotivoRenuncia 
rh_alt$MotivoRenuncia[rh_alt$MotivoRenuncia == "N/A"] <- "No aplica"
rh_alt$MotivoRenuncia <- ifelse(is.na(rh_alt$MotivoRenuncia), "No aplica", rh_alt$MotivoRenuncia)

## DetalleRenuncia
rh_alt$DetalleRenuncia[rh_alt$DetalleRenuncia == "N/A"] <- "No aplica"
rh_alt$DetalleRenuncia <- ifelse(is.na(rh_alt$DetalleRenuncia), "No aplica", rh_alt$DetalleRenuncia)



# Insertar Nas 

## Fact_Infonavit
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "CUOTA FIJA EN VSM"] <- "Na"
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "CUOTA FIJA"] <- "Na"
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == " "] <- "Na"
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "N/A"] <- "Na"
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "Mensual"] <- "Na"
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "Pesos"] <- "Na"
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == NULL] <- "Na"
rh_alt$Fact_Infonavit <- ifelse(is.na(rh_alt$Fact_Infonavit), "Na", rh_alt$Fact_Infonavit)

## No_Interno
rh_alt$No_Interno[rh_alt$No_Interno == "N/A"] <- "Na"
rh_alt$No_Interno <- ifelse(is.na(rh_alt$No_Interno), "Na", rh_alt$No_Interno)

## No_CreditoInfonavit
rh_alt$No_CreditoInfonavit[rh_alt$No_CreditoInfonavit == " "] <- "Na"
rh_alt$No_CreditoInfonavit[rh_alt$No_CreditoInfonavit == "N/A"] <- "Na"
rh_alt$No_CreditoInfonavit[rh_alt$No_CreditoInfonavit == NULL] <- "Na"
rh_alt$No_CreditoInfonavit[rh_alt$No_CreditoInfonavit == "N/A"] <- "Na"
rh_alt$No_CreditoInfonavit <- ifelse(is.na(rh_alt$No_CreditoInfonavit), "Na", rh_alt$No_CreditoInfonavit)

## Lugar de Nacimiento
rh_alt$Lugar_nacimiento[rh_alt$Lugar_nacimiento == "N/A"] <- "Na"
rh_alt$Lugar_nacimiento <- ifelse(is.na(rh_alt$Lugar_nacimiento), "Na", rh_alt$Lugar_nacimiento)

Reemplazo de valores erróneos y estandarización

Con la finalidad de establecer una escritura estandarizada, se definieron funciones que permitirÔn disponer de valores sin acentos y con letras minúsculas (para algunos casos), guiando a un mejor procesamiento de los datos en su lectura para la creación de modelos.

# Definir funciones para estandarizar columnas a minĆŗsculas sin acentos
convertir_acentos <- function(x) {
  x <- chartr("Ć”Ć©Ć­Ć³ĆŗĆ¼Ć±ĆĆ‰ĆĆ“ĆšĆœĆ‘", "aeiouunAEIOUUN", x)
  x
}

convertir_acentos1 <- function(x) {
  x <- tolower(iconv(x, from = "latin1", to = "UTF-8"))
  x <- chartr("Ć”Ć©Ć­Ć³ĆŗĆ¼Ć±ĆĆ‰ĆĆ“ĆšĆœĆ‘", "aeiouunAEIOUUN", x)
  x
}


convertir_acentos2 <- function(x) {
  x <- tolower(iconv(x, from = "latin1", to = "UTF-8"))
  x <- chartr("Ć”Ć©Ć­Ć³ĆŗĆ¼Ć±ĆĆ‰ĆĆ“ĆšĆœĆ‘", "aeiouunAEIOUUN", x)
  x
}

convertir_acentos3 <- function(x) {
  x <- tolower(iconv(x, from = "latin1", to = "UTF-8"))
  x <- chartr("Ć”Ć©Ć­Ć³ĆŗĆ¼Ć±ĆĆ‰ĆĆ“ĆšĆœĆ‘", "aeiouunAEIOUUN", x)
  x
}

# Pasar nombres y apellidos a minĆŗsculas
rh_alt <- rh_alt %>%
  mutate(Nombre = tolower(iconv(Nombre, from = "latin1", to = "UTF-8")),
         Apellidos = tolower(iconv(Apellidos, from = "latin1", to = "UTF-8")))

# Aplicar las funciones
rh_alt$Nombre          <- convertir_acentos(rh_alt$Nombre)
rh_alt$Apellidos       <- convertir_acentos(rh_alt$Apellidos)
rh_alt$Puesto          <- convertir_acentos1(rh_alt$Puesto)
rh_alt$MotivoRenuncia  <- convertir_acentos2(rh_alt$MotivoRenuncia)
rh_alt$DetalleRenuncia <- convertir_acentos2(rh_alt$DetalleRenuncia)
rh_alt$Departamento    <- convertir_acentos3(rh_alt$Departamento)

Homologación de variables categóricas

El proceso de homologación considera el definir una única forma de escribir los nombres de las clases o niveles en una variable al detectar que esta ha sido escrita de múltiples formas diferentes entre sí. Debido a esto, es necesario recodificar cada una de las variaciones y así disponer de una cantidad real de niveles para las variables categóricas.

Columna Estado Civil

# Estandarizar variables categóricas como Estado_Civil, Puesto, Departamento, etc.
rh_alt$Estado_Civil <- recode(rh_alt$Estado_Civil, 
                               "Soltera" = "Solteria", "Soltero" = "Solteria", 
                               "Casada" = "Matrimonio", "casado" = "Matrimonio", 
                               "CASADA" = "Matrimonio", "CASADO" = "Matrimonio", 
                               "Casado" = "Matrimonio", "Divorciada" = "Divorcio", 
                               "Separada" = "Divorcio", "Divorciado" = "Divorcio", 
                               "Unión Libre" = "Union Libre", "Unión libre" = "Union Libre", 
                               "Unión libre" = "Union Libre", "Viudo" = "Viudez", 
                               "Viuda" = "Viudez")

Columnas Departamento | Puesto

Adicional a la recodificación de la variable Departamento, se creó la columna ā€œOtrosā€, esta considera: Cajas, Celdas, Mantenimiento y Rotativa. Esta etiqueta fue creada debido al bajo volumen de empleados dentro de los departamentos previamente mencionados, asĆ­ como una similitud entre las tareas y nivel organizacional.

rh_alt$Puesto[rh_alt$Puesto == "ayudante de embarques"]           <- "Ayudante de embarques"
rh_alt$Puesto[rh_alt$Puesto == "ayud. de embarques"]              <- "Ayudante de embarques"
rh_alt$Puesto[rh_alt$Puesto == "auxiliar de embarques"]           <- "Ayudante de embarques"
rh_alt$Puesto[rh_alt$Puesto == "ayudante de soldador"]            <- "Ayudante de soldador"
rh_alt$Puesto[rh_alt$Puesto == "ayu. de soldador"]                <- "Ayudante de soldador"
rh_alt$Puesto[rh_alt$Puesto == "ay. general"]                     <- "Ayudante general"
rh_alt$Puesto[rh_alt$Puesto == "Ayudante general"]                <- "Ayudante general"
rh_alt$Puesto[rh_alt$Puesto == "ayu. de pintor"]                  <- "Ayudante de pintor"
rh_alt$Puesto[rh_alt$Puesto == "Ayudante De Pintor"]              <- "Ayudante de pintor"
rh_alt$Puesto[rh_alt$Puesto == "ayudante general-cedis"]          <- "Ayudante general"
rh_alt$Puesto[rh_alt$Puesto == "ayudante general cedis"]          <- "Ayudante general"
rh_alt$Puesto[rh_alt$Puesto == "ayudante general"]                <- "Ayudante general"
rh_alt$Puesto[rh_alt$Puesto == "inspectora de calidad"]           <- "Calidad"
rh_alt$Puesto[rh_alt$Puesto == "calidad"]                         <- "Calidad"
rh_alt$Puesto[rh_alt$Puesto == "inspector de calidad"]            <- "Calidad"
rh_alt$Puesto[rh_alt$Puesto == "Inspectora De Calidad"]           <- "Calidad"
rh_alt$Puesto[rh_alt$Puesto == "Materialista"]                    <- "Materiales"
rh_alt$Puesto[rh_alt$Puesto == "Operador Sierra"]                 <- "Operador"
rh_alt$Puesto[rh_alt$Puesto == "Op. Flexo-Ranuradora-Refiladora"] <- "Operador"
rh_alt$Puesto[rh_alt$Puesto == "op. flexo-ranuradora-refiladora"] <- "Operador"
rh_alt$Puesto[rh_alt$Puesto == "materialista"]                    <- "Materiales"
rh_alt$Puesto[rh_alt$Puesto == "materiales"]                      <- "Materiales"
rh_alt$Puesto[rh_alt$Puesto == "costurera"]                       <- "Costura"
rh_alt$Puesto[rh_alt$Puesto == "costurero"]                       <- "Costura"
rh_alt$Puesto[rh_alt$Puesto == "chofer gestor"]                   <- "Chofer"
rh_alt$Puesto[rh_alt$Puesto == "Chofer CEDIS"]                    <- "Chofer"
rh_alt$Puesto[rh_alt$Puesto == "chofer"]                          <- "Chofer"
rh_alt$Puesto[rh_alt$Puesto == "gestor"]                          <- "Gestor"
rh_alt$Puesto[rh_alt$Puesto == "residente"]                       <- "Residente"
rh_alt$Puesto[rh_alt$Puesto == "pintor"]                          <- "Pintor"
rh_alt$Puesto[rh_alt$Puesto == "limpieza"]                        <- "Limpieza"
rh_alt$Puesto[rh_alt$Puesto == "enfermera"]                       <- "Enfermera"
rh_alt$Puesto[rh_alt$Puesto == "soldador"]                        <- "Soldador"
rh_alt$Puesto[rh_alt$Puesto == "mozo"]                            <- "Ayudante general"
rh_alt$Puesto[rh_alt$Puesto == "almacenista"]                     <- "Almacenista"
rh_alt$Puesto[rh_alt$Puesto == "guardia de seguridad"]            <- "Guardia"
rh_alt$Puesto[rh_alt$Puesto == "supervisor de maquina"]           <- "Supervisor"
rh_alt$Puesto[rh_alt$Puesto == "supervisor de pegado"]            <- "Supervisor"
rh_alt$Puesto[rh_alt$Puesto == "supervisora"]                     <- "Supervisor"
rh_alt$Puesto[rh_alt$Puesto == "supervisor de maquinas"]          <- "Supervisor"
rh_alt$Puesto[rh_alt$Puesto == "marcadora"]                       <- "Marcadora"
rh_alt$Puesto[rh_alt$Puesto == "chofer cedis"]                    <- "Chofer"
rh_alt$Puesto[rh_alt$Puesto == "residente stabilus"]              <- "Residente"
rh_alt$Puesto[rh_alt$Puesto == "residente yfcf"]                  <- "Residente"
rh_alt$Puesto[rh_alt$Puesto == "ayudante de mantenimiento"]       <- "Ayudante de mantenimiento"
rh_alt$Puesto[rh_alt$Puesto == "montacarguista"]                  <- "Montacarguista"


 # Recodificación columna Departamento
rh_alt$Departamento[rh_alt$Departamento == "Cedis"]                    <- "CEDIS"
rh_alt$Departamento[rh_alt$Departamento == "cedis"]                    <- "CEDIS"
rh_alt$Departamento[rh_alt$Departamento == "calidad"]                  <- "Calidad"
rh_alt$Departamento[rh_alt$Departamento == "cajas"]                    <- "Otros"
rh_alt$Departamento[rh_alt$Departamento == "cortadoras"]               <- "Cortadoras"
rh_alt$Departamento[rh_alt$Departamento == "costura"]                  <- "Costura"
rh_alt$Departamento[rh_alt$Departamento == "celdas"]                   <- "Otros"
rh_alt$Departamento[rh_alt$Departamento == "embarques"]                <- "Embarques"
rh_alt$Departamento[rh_alt$Departamento == "Ehs"]                      <- "EHS"
rh_alt$Departamento[rh_alt$Departamento == "ehs"]                      <- "EHS"
rh_alt$Departamento[rh_alt$Departamento == "mantenimieto"]             <- "Otros"
rh_alt$Departamento[rh_alt$Departamento == "mantenimiento"]            <- "Otros"
rh_alt$Departamento[rh_alt$Departamento == "materiales"]               <- "Materiales"
rh_alt$Departamento[rh_alt$Departamento == "paileria"]                 <- "Paileria y Pintura"
rh_alt$Departamento[rh_alt$Departamento == "paileria y pintura"]       <- "Paileria y Pintura"
rh_alt$Departamento[rh_alt$Departamento == "rotativa"]                 <- "Otros"
rh_alt$Departamento[rh_alt$Departamento == "stabilus"]                 <- "Stabilus"
rh_alt$Departamento[rh_alt$Departamento == "Produccion Carton MC"]     <- "Produccion Carton MC"
rh_alt$Departamento[rh_alt$Departamento == "produccion carton mc"]     <- "Produccion Carton MC"
rh_alt$Departamento[rh_alt$Departamento == "Produccion Cartón MC"]     <- "Produccion Carton MC"
rh_alt$Departamento[rh_alt$Departamento == "Produccion Cart\xf3n MC"]  <- "Produccion Carton MC"
rh_alt$Departamento[rh_alt$Departamento == "produccion carton mdl"]    <- "Produccion Carton MDL"
rh_alt$Departamento[rh_alt$Departamento == "Produccion Cart\xf3n MDL"] <- "Produccion Carton MDL"
rh_alt$Departamento[rh_alt$Departamento == "Produccion Carton MDL"]    <- "Produccion Carton MDL"
rh_alt$Departamento[rh_alt$Departamento == "Produccion Cartón Mdl"]    <- "Produccion Carton MDL"
rh_alt$Departamento[rh_alt$Departamento == "Producción Retorn"]        <- "Produccion Retornable"
rh_alt$Departamento[rh_alt$Departamento == "produccion retornable"]    <- "Produccion Retornable"
rh_alt$Departamento[rh_alt$Departamento == "produccion cartã³n mdl"]   <- "Produccion Carton MDL"
rh_alt$Departamento[rh_alt$Departamento == "produccion cartã³n mc"]    <- "Produccion Carton MC"
rh_alt$Departamento[rh_alt$Departamento == "producciã³n retornable"]   <- "Produccion Retornable"
rh_alt$Departamento[rh_alt$Departamento == "producciã³n cartã³n mc"]   <- "Produccion Carton MC"

Columnas Municipio | Estado

 # Recodificación columna Municipio
rh_alt$Municipio[rh_alt$Municipio == "Ramoz Arizpe"]         <- "Ramos Arizpe"
rh_alt$Municipio[rh_alt$Municipio == "RAMOS ARIZPE"]         <- "Ramos Arizpe"
rh_alt$Municipio[rh_alt$Municipio == "PESQUERIA"]            <- "Pesqueria"
rh_alt$Municipio[rh_alt$Municipio == "MONTERREY"]            <- "Monterrey"
rh_alt$Municipio[rh_alt$Municipio == "JUAREZ"]               <- "Juarez"
rh_alt$Municipio[rh_alt$Municipio == "GUADALUPE"]            <- "Guadalupe"
rh_alt$Municipio[rh_alt$Municipio == "CAƑADA BLANCA"]        <- "CaƱada Blanca"
rh_alt$Municipio[rh_alt$Municipio == "APODACA"]              <- "Apodaca"
rh_alt$Municipio[rh_alt$Municipio == "SAN NICOLAS DE LOS G"] <- "San Nicolas de los Garza"
rh_alt$Municipio[rh_alt$Municipio == "San Nicolas De Los G"] <- "San Nicolas de los Garza"
rh_alt$Municipio[rh_alt$Municipio == "San Nicolas"]          <- "San Nicolas de los Garza"
rh_alt$Municipio[rh_alt$Municipio == "Cienega de Flores"]    <- "Cienega de Flores"
rh_alt$Municipio[rh_alt$Municipio == "Cienega De Flores"]    <- "Cienega de Flores"
rh_alt$Municipio[rh_alt$Municipio == "CiƩnega de Flores"]    <- "Cienega de Flores"
rh_alt$Municipio[rh_alt$Municipio == "SALTILLO"]             <- "Saltillo"

# Recodificación columna Estado
rh_alt$Estado[rh_alt$Estado == "Nuevo León"]    <- "Nuevo Leon"
rh_alt$Estado[rh_alt$Estado == "Nuevo Le\xf3n"] <- "Nuevo Leon"
rh_alt$Estado[rh_alt$Estado == "COAHUILA"]      <- "Coahuila"

Columna Fact_Infonavit

 # Recodificación columna Fact_Infonavit
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "CUOTA FIJA EN VSM 56.118"] <- 56.11
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "30.7344 VSM"]              <- 30.73
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "CF EN VSM 21.310"]         <- 21.31
rh_alt$Fact_Infonavit[rh_alt$Fact_Infonavit == "FD 13.2544"]               <- 13.25

SĆ­mbolos y mayĆŗsuculas

Generación de nuevas variables

Una vez corregidas las variables disponibles en la base de datos, es necesario definir nuevas posibles variables independientes que permitan aportar mayor explicabilidad a la variable de interés (Renuncia). Ante esto, se utilizaron las variables de fechas, las cuales pasaron por una transformación a columna tipo fecha, para definir dos nuevas variables:

  • Edad: La edad de los empleados puede ser una variable de impacto en su criterio para continuar o no trabajando en una empresa, considerando las posibles implicaciones fĆ­sicas y sociales de contar una menor o mayor edad. La columna fue creada tomando en cuenta el escenario de empleados de baja y los que continĆŗan laborando. Esto se determino en caso de tener o no valores nulos en la columna de fecha de baja, siendo esta la restada a su fecha de nacimiento para los empleados para conocer la edad a la que deciden renunciar. Por otro lado, para los empleados que deciden continuar, su edad es calculada restando la fecha al dĆ­a actual a su fecha de nacimiento, siendo esta considerada como una posible edad para que los empleados opten por continuar en la empresa.

  • NombreMes: El Mes en el que iniciaron actividades laborales en Form puede ser un factor decisivo para que un empleado opte por permanecer o no en la empresa, considerando la primera impresión de las actividades del dĆ­a a dĆ­a que deberĆ” estar realizando. Esta variable fue creada al extraer el mes de la columna Fecha Alta.

# Convertir fechas en formato de fecha
rh_alt$Fecha_Nacimiento<-as.Date(rh_alt$Fecha_Nacimiento,format="%m/%d/%Y") 
rh_alt$Fecha_Alta      <-as.Date(rh_alt$Fecha_Alta,format="%m/%d/%Y") 
rh_alt$Primer_mes      <-as.Date(rh_alt$Primer_mes,format="%m/%d/%Y") 
rh_alt$Cuarto_mes      <-as.Date(rh_alt$Cuarto_mes,format="%m/%d/%Y") 
rh_alt$Baja            <-as.Date(rh_alt$Baja,format="%m/%d/%Y") 
rh_alt <- rh_alt %>%
  mutate(Fecha_Hoy = Sys.Date())
rh_alt$Fecha_Hoy       <-as.Date(rh_alt$Fecha_Hoy,format="%m/%d/%Y") 

# Crear columna edad
rh_alt <- rh_alt %>%
  mutate(
    Edad = ifelse(is.na(Baja), 
                  floor(as.numeric(difftime(Fecha_Hoy, Fecha_Nacimiento, units = "days")) / 365),
                  floor(as.numeric(difftime(Baja, Fecha_Nacimiento, units = "days")) / 365))
  )

# Crear columna NombreMes
rh_alt$NombreMes <- format(rh_alt$Fecha_Alta, "%B")

Manejo de Outliers

Dentro de las columnas disponibles originalmente en la base de datos, se detectaron outliers en una de ellas, siendo el Salario Diario con una cantidad considerablemente alta a diferencia del resto de salarios. Asimismo, tras crear la columna Edad se identificaron cantidades muy bajas, siendo imposible contar con empleados que no cumplen con la mayorĆ­a de edad laborando en la empresa.

Tras considerar ambos casos, se realizó una imputación de la mediana, como una alternativa mÔs representativa ante el sesgo que pudiera implicar el usar la media de los datos. Para el caso de edad se realiza la imputación al contar con una edad menor a la mayoría de edad. Por otro lado, en el salario diario se imputa al considerar altas cantidades que superan los $5000 MXN diarios.

# Reemplazar outliers
## Variable Edad
mediana_edad          <- median(rh_alt$Edad, na.rm = TRUE)
rh_alt$Edad           <- ifelse(rh_alt$Edad < 17, mediana_edad, rh_alt$Edad)

## Variable Salario
mediana_salario       <- median(rh_alt$Salario_diario, na.rm = TRUE)
rh_alt$Salario_diario <- ifelse(rh_alt$Salario_diario > 5000, mediana_salario, rh_alt$Salario_diario)

Conversión de variables y eliminación de duplicados

Finalmente, el último tratamiento de los datos contempla la conversión de variables categóricas a factores, alineando algunas de las variables al procesamiento necesario para la creación de modelos de aprendizaje automÔtico.

Junto a esto, la revisión final consiste en usar variables que contienen datos puntuales y específicos para cada persona, como su nombre y apellido (previamente estandarizados en escritura) y su RFC, para detectar empleados registrados múltiples veces. Este proceso revela que no existen estos casos en la base de datos resultante.

# Convertir variables categóricas a factores
rh_alt$Genero        <-as.factor(rh_alt$Genero)
rh_alt$Puesto        <-as.factor(rh_alt$Puesto)
rh_alt$Departamento  <-as.factor(rh_alt$Departamento)
rh_alt$Municipio     <-as.factor(rh_alt$Municipio)
rh_alt$Estado        <-as.factor(rh_alt$Estado)
rh_alt$Estado_Civil  <-as.factor(rh_alt$Estado_Civil)
rh_alt$Salario_diario<-as.numeric(rh_alt$Salario_diario)
rh_alt$Renuncia      <-as.factor(rh_alt$Renuncia)
rh_alt$NombreMes     <-as.factor(rh_alt$NombreMes)

# Eliminar duplicados
rh_alt <- distinct(rh_alt, Nombre, Apellidos, RFC, .keep_all = TRUE)
summary(rh_alt)
##   No_Empleado     Apellidos            Nombre          Fecha_Nacimiento    
##  Min.   :  1.0   Length:553         Length:553         Min.   :1955-09-10  
##  1st Qu.:202.0   Class :character   Class :character   1st Qu.:1983-08-14  
##  Median :350.0   Mode  :character   Mode  :character   Median :1994-05-06  
##  Mean   :380.3                                         Mean   :1991-05-20  
##  3rd Qu.:501.0                                         3rd Qu.:2000-10-30  
##  Max.   :851.0                                         Max.   :2022-10-31  
##                                                                            
##        Genero        RFC              Fecha_Alta           Primer_mes        
##  Femenino :306   Length:553         Min.   :2010-07-01   Min.   :2010-07-31  
##  Masculino:247   Class :character   1st Qu.:2022-12-23   1st Qu.:2023-01-22  
##                  Mode  :character   Median :2023-05-24   Median :2023-06-23  
##                                     Mean   :2023-02-17   Mean   :2023-03-19  
##                                     3rd Qu.:2023-08-25   3rd Qu.:2023-09-24  
##                                     Max.   :2024-05-07   Max.   :2024-06-06  
##                                                                              
##    Cuarto_mes              Baja                         Puesto   
##  Min.   :2010-10-29   Min.   :2022-01-25   Ayudante general:450  
##  1st Qu.:2023-04-24   1st Qu.:2023-03-02   Costura         : 15  
##  Median :2023-09-23   Median :2023-06-07   Soldador        : 12  
##  Mean   :2023-06-19   Mean   :2023-05-24   Chofer          : 10  
##  3rd Qu.:2023-12-25   3rd Qu.:2023-08-14   Residente       : 10  
##  Max.   :2024-09-06   Max.   :2024-02-22   Calidad         :  8  
##                       NA's   :157          (Other)         : 48  
##                 Departamento No_SeguroSocial    Salario_diario 
##  area no definida     :369   Length:553         Min.   :144.4  
##  Produccion Carton MC : 50   Class :character   1st Qu.:217.6  
##  Produccion Carton MDL: 37   Mode  :character   Median :217.6  
##  CEDIS                : 24                      Mean   :215.9  
##  Costura              : 15                      3rd Qu.:217.6  
##  Produccion Retornable: 14                      Max.   :337.1  
##  (Other)              : 44                                     
##  Fact_Infonavit     No_CreditoInfonavit Lugar_nacimiento       CURP          
##  Length:553         Length:553          Length:553         Length:553        
##  Class :character   Class :character    Class :character   Class :character  
##  Mode  :character   Mode  :character    Mode  :character   Mode  :character  
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##     Calle            No_Interno          Colonia                   Municipio  
##  Length:553         Length:553         Length:553         Apodaca       :370  
##  Class :character   Class :character   Class :character   Pesqueria     : 60  
##  Mode  :character   Mode  :character   Mode  :character   Juarez        : 50  
##                                                           Guadalupe     : 30  
##                                                           Ramos Arizpe  : 24  
##                                                           General Zuazua:  4  
##                                                           (Other)       : 15  
##         Estado    Codigo_Postal        Estado_Civil   Tarjeta          Renuncia
##  Coahuila  : 27   Min.   : 9999   Divorcio   :  8   Length:553         No:157  
##  Nuevo Leon:526   1st Qu.:66646   Matrimonio :155   Class :character   Si:396  
##                   Median :66646   Solteria   :258   Mode  :character           
##                   Mean   :65125   Union Libre:130                              
##                   3rd Qu.:66670   Viudez     :  2                              
##                   Max.   :99999                                                
##                                                                                
##  MotivoRenuncia     DetalleRenuncia      Fecha_Hoy               Edad      
##  Length:553         Length:553         Min.   :2024-06-02   Min.   :18.00  
##  Class :character   Class :character   1st Qu.:2024-06-02   1st Qu.:22.00  
##  Mode  :character   Mode  :character   Median :2024-06-02   Median :29.00  
##                                        Mean   :2024-06-02   Mean   :31.87  
##                                        3rd Qu.:2024-06-02   3rd Qu.:40.00  
##                                        Max.   :2024-06-02   Max.   :68.00  
##                                                                            
##    NombreMes  
##  julio  : 70  
##  agosto : 65  
##  febrero: 63  
##  abril  : 60  
##  enero  : 57  
##  marzo  : 55  
##  (Other):183

Estructura final de los datos

Durante el proceso de limpieza y anĆ”lisis de los datos del archivo ā€œform_rh_datos.xlsxā€, se identificaron y corrigieron mĆŗltiples inconsistencias y errores considerando desde valores atĆ­picos hasta ortografĆ­a incorrecta, los cuales podĆ­an afectar la calidad de los anĆ”lisis posteriores. Uno de los hallazgos mĆ”s relevantes fue la presencia de valores nulos en varias columnas clave, como ā€œFact_Infonavitā€ y ā€œFecha_Nacimientoā€. Estos valores se gestionaron mediante la imputación de datos faltantes y la eliminación de registros incompletos, lo que permitió mejorar la integridad y consistencia del conjunto de datos.

AdemĆ”s, se realizaron correcciones en las variables categóricas, como la estandarización de los nombres de empleados y departamentos para asegurar uniformidad en el anĆ”lisis. Por ejemplo, se corrigieron inconsistencias en la columna ā€œEstado_Civilā€, donde tĆ©rminos como ā€œSolteraā€ y ā€œCasadoā€ se unificaron bajo las categorĆ­as ā€œSolteriaā€ y ā€œMatrimonioā€, respectivamente. Este paso fue crucial para evitar duplicaciones y errores durante el anĆ”lisis de las tendencias demogrĆ”ficas y de empleo.

La creación de nuevas variables, como la ā€œEdadā€ de los empleados, y la corrección de valores atĆ­picos en ā€œSalario_diarioā€ y ā€œEdadā€, permitió obtener una visión mĆ”s precisa y adecuada de los datos. En resumen, el proceso de limpieza y estandarización no solo mejoró la calidad del conjunto de datos, sino que tambiĆ©n sentó las bases para anĆ”lisis mĆ”s robustos y confiables en futuros estudios. Este ejercicio subraya la importancia de la limpieza de datos en el campo de Business Intelligence, donde decisiones informadas y estrategias efectivas dependen de la precisión y confiabilidad de los datos analizados.

# Estructura final de los datos
head(rh_alt, 20)
## # A tibble: 20 Ɨ 32
##    No_Empleado Apellidos         Nombre Fecha_Nacimiento Genero RFC   Fecha_Alta
##          <dbl> <chr>             <chr>  <date>           <fct>  <chr> <date>    
##  1          10 luna lopez        yolan… 1985-08-18       Femen… LULY… 2017-02-20
##  2          12 suarez romo       julio… 1969-06-27       Mascu… SURJ… 2017-12-01
##  3          13 cruz ramos        victo… 1989-06-21       Mascu… CURV… 2018-03-23
##  4          19 garcia perales    yulia… 1984-08-19       Femen… GAPY… 2019-05-02
##  5          20 yabert alvarado   mario… 1990-06-24       Mascu… YAAM… 2019-07-30
##  6          29 lopez torres      martin 1978-02-27       Mascu… LOTM… 2020-08-20
##  7          35 celestino barboza jaime  1982-06-02       Mascu… CEBJ… 2021-02-16
##  8          37 hernandez felix   emili… 1993-06-22       Femen… HEFE… 2021-03-25
##  9          42 chapan marcial    ernes… 1969-11-11       Femen… CAME… 2021-05-27
## 10          44 arriaga reyes     erika… 2001-10-22       Femen… AIRE… 2021-08-12
## 11          45 gonzalez esquivel franc… 1982-01-08       Mascu… GOEF… 2021-09-15
## 12          48 garcia chapa      jesus… 2003-01-20       Mascu… GACJ… 2021-10-09
## 13          49 perez olarte      santi… 2001-07-25       Mascu… PEOS… 2021-11-04
## 14          56 garcia garcia     sofia  2002-01-11       Femen… GAGS… 2022-02-11
## 15          57 salas perales     irene… 1976-09-29       Femen… SAPI… 2022-02-16
## 16          59 hernandez tovar   palom… 2000-05-01       Femen… HETP… 2022-03-09
## 17          60 esquivel medina   cassa… 2002-11-22       Femen… EUMC… 2022-03-09
## 18          64 compean paz       ferna… 1997-01-22       Mascu… COPF… 2022-04-09
## 19          67 ortiz de la torre fermi… 1966-07-07       Femen… OITF… 2022-06-01
## 20          71 martinez ramirez  yudit… 1977-03-07       Femen… MARY… 2022-06-14
## # ℹ 25 more variables: Primer_mes <date>, Cuarto_mes <date>, Baja <date>,
## #   Puesto <fct>, Departamento <fct>, No_SeguroSocial <chr>,
## #   Salario_diario <dbl>, Fact_Infonavit <chr>, No_CreditoInfonavit <chr>,
## #   Lugar_nacimiento <chr>, CURP <chr>, Calle <chr>, No_Interno <chr>,
## #   Colonia <chr>, Municipio <fct>, Estado <fct>, Codigo_Postal <dbl>,
## #   Estado_Civil <fct>, Tarjeta <chr>, Renuncia <fct>, MotivoRenuncia <chr>,
## #   DetalleRenuncia <chr>, Fecha_Hoy <date>, Edad <dbl>, NombreMes <fct>
summary(rh_alt)
##   No_Empleado     Apellidos            Nombre          Fecha_Nacimiento    
##  Min.   :  1.0   Length:553         Length:553         Min.   :1955-09-10  
##  1st Qu.:202.0   Class :character   Class :character   1st Qu.:1983-08-14  
##  Median :350.0   Mode  :character   Mode  :character   Median :1994-05-06  
##  Mean   :380.3                                         Mean   :1991-05-20  
##  3rd Qu.:501.0                                         3rd Qu.:2000-10-30  
##  Max.   :851.0                                         Max.   :2022-10-31  
##                                                                            
##        Genero        RFC              Fecha_Alta           Primer_mes        
##  Femenino :306   Length:553         Min.   :2010-07-01   Min.   :2010-07-31  
##  Masculino:247   Class :character   1st Qu.:2022-12-23   1st Qu.:2023-01-22  
##                  Mode  :character   Median :2023-05-24   Median :2023-06-23  
##                                     Mean   :2023-02-17   Mean   :2023-03-19  
##                                     3rd Qu.:2023-08-25   3rd Qu.:2023-09-24  
##                                     Max.   :2024-05-07   Max.   :2024-06-06  
##                                                                              
##    Cuarto_mes              Baja                         Puesto   
##  Min.   :2010-10-29   Min.   :2022-01-25   Ayudante general:450  
##  1st Qu.:2023-04-24   1st Qu.:2023-03-02   Costura         : 15  
##  Median :2023-09-23   Median :2023-06-07   Soldador        : 12  
##  Mean   :2023-06-19   Mean   :2023-05-24   Chofer          : 10  
##  3rd Qu.:2023-12-25   3rd Qu.:2023-08-14   Residente       : 10  
##  Max.   :2024-09-06   Max.   :2024-02-22   Calidad         :  8  
##                       NA's   :157          (Other)         : 48  
##                 Departamento No_SeguroSocial    Salario_diario 
##  area no definida     :369   Length:553         Min.   :144.4  
##  Produccion Carton MC : 50   Class :character   1st Qu.:217.6  
##  Produccion Carton MDL: 37   Mode  :character   Median :217.6  
##  CEDIS                : 24                      Mean   :215.9  
##  Costura              : 15                      3rd Qu.:217.6  
##  Produccion Retornable: 14                      Max.   :337.1  
##  (Other)              : 44                                     
##  Fact_Infonavit     No_CreditoInfonavit Lugar_nacimiento       CURP          
##  Length:553         Length:553          Length:553         Length:553        
##  Class :character   Class :character    Class :character   Class :character  
##  Mode  :character   Mode  :character    Mode  :character   Mode  :character  
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##     Calle            No_Interno          Colonia                   Municipio  
##  Length:553         Length:553         Length:553         Apodaca       :370  
##  Class :character   Class :character   Class :character   Pesqueria     : 60  
##  Mode  :character   Mode  :character   Mode  :character   Juarez        : 50  
##                                                           Guadalupe     : 30  
##                                                           Ramos Arizpe  : 24  
##                                                           General Zuazua:  4  
##                                                           (Other)       : 15  
##         Estado    Codigo_Postal        Estado_Civil   Tarjeta          Renuncia
##  Coahuila  : 27   Min.   : 9999   Divorcio   :  8   Length:553         No:157  
##  Nuevo Leon:526   1st Qu.:66646   Matrimonio :155   Class :character   Si:396  
##                   Median :66646   Solteria   :258   Mode  :character           
##                   Mean   :65125   Union Libre:130                              
##                   3rd Qu.:66670   Viudez     :  2                              
##                   Max.   :99999                                                
##                                                                                
##  MotivoRenuncia     DetalleRenuncia      Fecha_Hoy               Edad      
##  Length:553         Length:553         Min.   :2024-06-02   Min.   :18.00  
##  Class :character   Class :character   1st Qu.:2024-06-02   1st Qu.:22.00  
##  Mode  :character   Mode  :character   Median :2024-06-02   Median :29.00  
##                                        Mean   :2024-06-02   Mean   :31.87  
##                                        3rd Qu.:2024-06-02   3rd Qu.:40.00  
##                                        Max.   :2024-06-02   Max.   :68.00  
##                                                                            
##    NombreMes  
##  julio  : 70  
##  agosto : 65  
##  febrero: 63  
##  abril  : 60  
##  enero  : 57  
##  marzo  : 55  
##  (Other):183

Modelos de Clasificación

Preparación de Datos para los Modelos

Partición del Conjunto de Datos

Inicialmente, para la creación de los modelos de aprendizaje automÔtico se dividieron los datos en dos conjuntos, entrenamiento y prueba, con una repartición 80-20, tomando en cuenta que el 80% de los datos se consideren como entrenamiento. Este tamaño de conjuntos se planteó considerando la baja cantidad de registros de empleados disponibles, siendo necesario conservar una alta cantidad de registros para un mejor entrenamiento por parte del modelo, conservando un 20% para su posterior evaluación.

Objetivo

Esta partición considera la variable de interĆ©s, previamente definida como Renuncia o No Renuncia, esto con el objetivo de construir modelos capaces principalmente de determinar las cualidades que conducen a que un empleado decida mantenerse en Form, optimizando asĆ­ la selección de prospectos durante el proceso de reclutamiento, y en algunos casos al diseƱo de estrategias para aumentar esfuerzos sobre los empleados con mayor tendencia a renunciar, guiando de esta manera a mejorar los niveles de retención en la empresa. En consideración de esto, la clase de positiva o de interĆ©s serĆ­a ā€œNoā€, es decir, aquellos empleados que no renuncian.

set.seed(123)

sample <- createDataPartition(y = rh_alt$Renuncia, p=0.8, list=F)
train  <- rh_alt[sample, ]
test   <- rh_alt[-sample, ]

Fórmula para los modelos

Primeramente, se descartan variables inadecuadas por su extensión de valores disponibles (Calle, No_Interno, Colonia), redundancia (Ubicación GeogrÔfica, Ej: Estado-Municipio), o información irrelevante (Datos Personales y fiscales: nombre, No_CreditoInfonavit, No_CreditoInfonavit, etc.). Posteriormente, tras evaluar múltiples variables en la especificación del modelo, las siguientes fueron seleccionadas como las mÔs adecuadas para predecir la Renuncia de los colaboradores.

formula_glm <- ifelse(Renuncia=="Si", 1,0) ~ Genero  + Departamento + Municipio + Estado_Civil +Salario_diario + Edad + NombreMes 
formula1 <- Renuncia ~ Genero + Departamento + Municipio + Estado_Civil + Salario_diario + Edad + NombreMes 

Creación de Modelos

Modelo 1 Regresión lineal

El primer modelo seleccionado es la regresión lineal, siendo un modelo inicial adecuado debido a su simplicidad y funcionamiento, permitiendo establecer una base para comprender el comportamiento de renuncia por los empleados en Form.

# Creación del Modelo
model_glm1 <- glm(formula_glm, family = "binomial", data = train)

Interpretación

Primeramente, es necesario destacar las variables que obtuvieron un mayor nivel de significancia al obtener un p-value menor a 0.05, es decir, las que realmente permiten la distinción de las categorías en la variable de interés. En conjunto con esto, también aquellas con un mayor nivel de importancia en el modelo, al tener nivel de impacto mayor para pertenecer a una clase u otra.

  • Salario Diario: Cuenta con un nivel de significancia bastante alto, al igual que con el mayor nivel de importancia en el modelo (6.88). Con un coeficiente de -5.083e-02, esta variable determina que los empleados que Renuncian cuentan con un salario mĆ”s bajo.

  • Departamento: La pertenencia a los departamentos de Produccion Carton MC, Costura, Produccion Retornable y Produccion Carton MDL representan una alta significancia, al igual que algunos de los niveles mĆ”s altos de importancia (5.74, 5.19, 4.84 y 4 respectivamente). Los coeficientes revelan que los empleados en estos departamentos son aquellos que tienen mayor abandono, particularmente siendo en mayor medida por el departamento de costura al tener el coeficiente mĆ”s bajo (-5.741e+00).

  • Mes: El ser contratados durante los meses de Febrero, Junio y Julio tiene un alto nivel de signifancia dentro del modelo, asĆ­ como los niveles mĆ”s altos niveles de importancia para los meses (4.71, 4.36 y 3.83 respectivamente). Al ser contratado durante el mes de junio, los empleados tienden a renunciar mĆ”s de acuerdo a un coeficiente alto de 5.829e+00, posiblemente debido a las altas temperaturas del verano. Igualmente, Febrero serĆ­a el segundo mes mĆ”s alto (considerando Ćŗnicamente los significativos), que tiene mayor tendencia a que los empleados renuncien, siendo los mĆ”s ideales los meses de mayo y enero como los meses significativos (p-value menor a 0.05) con el coeficiente mĆ”s bajo (menor tendencia a renunciar).

# Resumen del Modelo
summary(model_glm1)
## 
## Call:
## glm(formula = formula_glm, family = "binomial", data = train)
## 
## Coefficients:
##                                     Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                        1.106e+01  3.919e+00   2.823 0.004755 ** 
## GeneroMasculino                   -3.574e-01  3.547e-01  -1.008 0.313540    
## DepartamentoCalidad               -2.725e+00  1.447e+00  -1.883 0.059703 .  
## DepartamentoCEDIS                 -1.719e+01  2.084e+03  -0.008 0.993417    
## DepartamentoCortadoras             1.542e+01  3.956e+03   0.004 0.996891    
## DepartamentoCostura               -5.539e+00  1.103e+00  -5.024 5.05e-07 ***
## DepartamentoEHS                   -2.969e+00  1.107e+00  -2.681 0.007336 ** 
## DepartamentoEmbarques             -1.040e+00  1.002e+00  -1.038 0.299131    
## DepartamentoMateriales             1.434e+01  1.579e+03   0.009 0.992753    
## DepartamentoOtros                 -4.112e+00  1.375e+00  -2.991 0.002779 ** 
## DepartamentoPaileria y Pintura    -3.094e+00  1.451e+00  -2.132 0.033035 *  
## DepartamentoProduccion Carton MC  -3.792e+00  6.537e-01  -5.802 6.56e-09 ***
## DepartamentoProduccion Carton MDL -2.516e+00  6.207e-01  -4.053 5.05e-05 ***
## DepartamentoProduccion Retornable -4.073e+00  8.456e-01  -4.816 1.46e-06 ***
## DepartamentoStabilus               1.224e+01  2.407e+03   0.005 0.995942    
## MunicipioCienega de Flores         1.284e+01  3.956e+03   0.003 0.997409    
## MunicipioEscobedo                  1.412e+01  3.956e+03   0.004 0.997153    
## MunicipioGeneral Zuazua           -1.333e+00  2.022e+00  -0.659 0.509971    
## MunicipioGuadalupe                -1.686e+00  7.930e-01  -2.126 0.033518 *  
## MunicipioHuinala                  -1.596e+01  3.956e+03  -0.004 0.996781    
## MunicipioJuarez                    2.181e-02  6.223e-01   0.035 0.972042    
## MunicipioMonterrey                 3.566e-01  4.855e+00   0.073 0.941457    
## MunicipioPesqueria                -2.481e-01  5.669e-01  -0.438 0.661651    
## MunicipioRamos Arizpe              1.553e+01  2.084e+03   0.007 0.994055    
## MunicipioSalinas Victoria          3.337e+01  5.595e+03   0.006 0.995241    
## MunicipioSaltillo                  1.152e+01  2.084e+03   0.006 0.995590    
## MunicipioSan Nicolas de los Garza -1.969e+01  3.956e+03  -0.005 0.996028    
## Estado_CivilMatrimonio             2.340e+00  3.358e+00   0.697 0.485995    
## Estado_CivilSolteria               2.527e+00  3.360e+00   0.752 0.451960    
## Estado_CivilUnion Libre            2.328e+00  3.375e+00   0.690 0.490283    
## Estado_CivilViudez                -1.495e+01  3.956e+03  -0.004 0.996984    
## Salario_diario                    -5.285e-02  7.607e-03  -6.947 3.74e-12 ***
## Edad                              -3.337e-02  1.736e-02  -1.922 0.054592 .  
## NombreMesagosto                    2.172e+00  7.196e-01   3.018 0.002546 ** 
## NombreMesdiciembre                 2.391e+00  1.061e+00   2.253 0.024250 *  
## NombreMesenero                     1.670e+00  7.243e-01   2.305 0.021159 *  
## NombreMesfebrero                   3.511e+00  7.348e-01   4.778 1.77e-06 ***
## NombreMesjulio                     3.016e+00  7.795e-01   3.869 0.000109 ***
## NombreMesjunio                     5.940e+00  1.340e+00   4.431 9.37e-06 ***
## NombreMesmarzo                     1.036e+00  5.592e-01   1.853 0.063931 .  
## NombreMesmayo                      1.374e+00  6.453e-01   2.130 0.033193 *  
## NombreMesnoviembre                -8.987e-03  9.029e-01  -0.010 0.992058    
## NombreMesoctubre                   5.663e-01  9.007e-01   0.629 0.529523    
## NombreMesseptiembre                1.903e+00  8.398e-01   2.266 0.023421 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 529.02  on 442  degrees of freedom
## Residual deviance: 264.99  on 399  degrees of freedom
## AIC: 352.99
## 
## Number of Fisher Scoring iterations: 16
# Impacto de las Variables
Importancia <- varImp(model_glm1)
Importancia <- Importancia %>%
  arrange(desc(Overall))
Importancia
##                                       Overall
## Salario_diario                    6.946690215
## DepartamentoProduccion Carton MC  5.801755553
## DepartamentoCostura               5.024336935
## DepartamentoProduccion Retornable 4.816238280
## NombreMesfebrero                  4.778487359
## NombreMesjunio                    4.431239857
## DepartamentoProduccion Carton MDL 4.053355395
## NombreMesjulio                    3.869496152
## NombreMesagosto                   3.017772537
## DepartamentoOtros                 2.991192244
## DepartamentoEHS                   2.681191005
## NombreMesenero                    2.305127624
## NombreMesseptiembre               2.266499071
## NombreMesdiciembre                2.253141479
## DepartamentoPaileria y Pintura    2.131652580
## NombreMesmayo                     2.129738773
## MunicipioGuadalupe                2.125825491
## Edad                              1.922107625
## DepartamentoCalidad               1.882982290
## NombreMesmarzo                    1.852663783
## DepartamentoEmbarques             1.038298815
## GeneroMasculino                   1.007822503
## Estado_CivilSolteria              0.752151982
## Estado_CivilMatrimonio            0.696692277
## Estado_CivilUnion Libre           0.689858387
## MunicipioGeneral Zuazua           0.658882912
## NombreMesoctubre                  0.628733850
## MunicipioPesqueria                0.437635015
## MunicipioMonterrey                0.073439334
## MunicipioJuarez                   0.035047642
## NombreMesnoviembre                0.009953382
## DepartamentoMateriales            0.009083075
## DepartamentoCEDIS                 0.008250414
## MunicipioRamos Arizpe             0.007450765
## MunicipioSalinas Victoria         0.005964589
## MunicipioSaltillo                 0.005527697
## DepartamentoStabilus              0.005085925
## MunicipioSan Nicolas de los Garza 0.004977652
## MunicipioHuinala                  0.004034689
## DepartamentoCortadoras            0.003896589
## Estado_CivilViudez                0.003780021
## MunicipioEscobedo                 0.003568782
## MunicipioCienega de Flores        0.003246788

Predicción y Resultados

El modelo demuestra tener un desempeño inicial adecuado, siendo acertado en un 87.27% de los casos en que un colaborador tiende a renunciar. Asimismo, cuenta con un Kappa de 0.6793, teniendo por lo tanto, un mejor desempeño que una predicción aleatoria.

# Predicción
prediccion_glm <- predict(model_glm1, test, type = "response")

# Matriz de confuisión
confusion_glm <- confusionMatrix(as.factor(ifelse(prediccion_glm>0.5, "Si", "No")), 
                                 test$Renuncia, positive = "No")
print(confusion_glm)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Si
##         No 23  6
##         Si  8 73
##                                           
##                Accuracy : 0.8727          
##                  95% CI : (0.7957, 0.9286)
##     No Information Rate : 0.7182          
##     P-Value [Acc > NIR] : 8.935e-05       
##                                           
##                   Kappa : 0.6793          
##                                           
##  Mcnemar's Test P-Value : 0.7893          
##                                           
##             Sensitivity : 0.7419          
##             Specificity : 0.9241          
##          Pos Pred Value : 0.7931          
##          Neg Pred Value : 0.9012          
##              Prevalence : 0.2818          
##          Detection Rate : 0.2091          
##    Detection Prevalence : 0.2636          
##       Balanced Accuracy : 0.8330          
##                                           
##        'Positive' Class : No              
## 

Asimismo, en el modelo glm se obtiene un valor en AUC de 0.85, reafirmando que la predicción del modelo es mejor que una predicción aleatoria.

#  Curva ROC 
## Obtener las puntuaciones de probabilidad
prediccion_prob_glm <- predict(model_glm1, test, type = "response")

## Generar la curva ROC
roc_obj_glm <- roc(test$Renuncia, as.numeric(prediccion_prob_glm))

## Dibujar la curva ROC
plot.roc(roc_obj_glm, main = "Curva ROC", col = "blue")

## Calcular el AUC
auc_glm <- pROC::auc(roc_obj_glm)
auc_glm
## Area under the curve: 0.8591

Modelo 2 Ôrbol de decisión

El segundo modelo elegido es el Árbol de Decisión, el cual al ser un modelo basado en el establecimiento de reglas, un planteamiento comúnmente usado en problemas de clasificación, así como ser un modelo de fÔcil interpretación visual, fue seleccionado como una de las posibles alternativas de modelo para el entendimiento en el comportamiento de renuncia de los empleados de Form.

# Creación del Modelo
arbol <- rpart(formula=formula1, data = train, method = "class")

Interpretación

  • El primer camino (0% de probabilidad de renunciar con cobertura del 2% de empleados) indica que cuando el salario diario es menor a 229, el empleado trabaja en uno de los departamentos (Calidad, CEDIS, EHS, Embarques, Otros, PailerĆ­a y Pintura, Producción Cartón MDL) y el mes es enero o noviembre, la probabilidad de renuncia es muy baja, casi nula.

  • El segundo camino (11% de probabilidad de renunciar con cobertura del 20% de empleados) sugiere que cuando el salario diario es igual o mayor a 229, la probabilidad de renuncia aumenta ligeramente, pero todavĆ­a es relativamente baja.

  • El tercer camino (14% de probabilidad de renunciar con cobertura del 7% de empleados) muestra que si el salario diario es menor a 229, el empleado trabaja en los departamentos (Calidad, CEDIS, EHS, Embarques, Otros, PailerĆ­a y Pintura, Producción Cartón MDL) y el mes es agosto, diciembre, febrero, julio, octubre o septiembre, y la edad es mayor o igual a 47, la probabilidad de renuncia es mayor que en la primera regla, pero aĆŗn no es muy alta.

  • El cuarto camino (18% de probabilidad de renunciar con cobertura del 2% de empleados) muestra una mayor probabilidad de renuncia cuando se combinan caracterĆ­sticas como salario diario menor a 229, trabajo en los departamentos (Costura, Producción Cartón MC, Producción Retornable) y el mes es agosto, diciembre, enero, febrero, julio, noviembre, octubre o septiembre.

  • El quinto camino (86% de probabilidad de renunciar con cobertura del 8% de empleados) indica una alta probabilidad de renuncia cuando el salario diario es menor a 229 y el empleado trabaja en los departamentos de (Calidad, CEDIS, EHS, Embarques, Otros, PailerĆ­a y Pintura, Producción Cartón MDL) durante los meses agosto, diciembre, febrero, julio, octubre o septiembre, y la edad es menor a 47.

  • El sexto camino (89% de probabilidad de renunciar con cobertura del 8% de empleados) indica una alta probabilidad de renuncia cuando el salario diario es menor a 229 y el empleado trabaja en los departamentos de (Calidad, CEDIS, Costura, EHS, Embarques, Otros, PailerĆ­a y Pintura, Producción Cartón MC, Producción Cartón MDL, Producción Retornable) durante los meses abril, junio, marzo o mayo.

  • Finalmente, el sĆ©ptimo camino (100% de probabilidad de renuncia con cobertura del 56%, la mayorĆ­a de empleados) muestra la probabilidad mĆ”s alta de renuncia cuando el salario diario es menor a 229 y el empleado trabaja en departamentos (Ɓrea no definida, Cortadoras, Materiales, Stabilus).

El anÔlisis sobre las reglas del modelo de Árbol determina que los empleados con salarios diarios bajos (menos de 229) estÔn mÔs propensos a renunciar, particularmente si trabajan en departamentos específicos y en ciertos meses del año. Este riesgo de renuncia se intensifica en Ôreas como Calidad, CEDIS, EHS, Embarques, Otros, Pailería y Pintura, Producción Cartón MDL, Producción Cartón MC, y Producción Retornable, posiblemente debido a factores como mayores cargas de trabajo, ambientes laborales desafiantes u oportunidades de crecimiento limitadas. AdemÔs, la probabilidad de renuncia aumenta durante los meses de agosto, diciembre, febrero, julio, octubre y septiembre, pudiendo ser períodos de alta demanda laboral o factores estacionales que les generan mayor estrés.

# GrÔfica del Árbol de Decisión
rpart.plot(arbol, type = 5, box.palette = "GnRd", shadow.col = "gray")

# Reglas del Ɓrbol
rpart.rules(arbol, style = "tallw", cover = FALSE, clip.facs = TRUE, faclen = 0)
## Renuncia is 0.00 when
##                  Salario_diario < 229
##                  Calidad or CEDIS or EHS or Embarques or Otros or Paileria y Pintura or Produccion Carton MDL
##                  enero or noviembre
## 
## Renuncia is 0.11 when
##                  Salario_diario >= 229
## 
## Renuncia is 0.18 when
##                  Salario_diario < 229
##                  Costura or Produccion Carton MC or Produccion Retornable
##                  agosto or diciembre or enero or febrero or julio or noviembre or octubre or septiembre
## 
## Renuncia is 0.22 when
##                  Salario_diario < 229
##                  Calidad or CEDIS or EHS or Embarques or Otros or Paileria y Pintura or Produccion Carton MDL
##                  agosto or diciembre or febrero or julio or octubre or septiembre
##                  Edad >= 46
## 
## Renuncia is 0.89 when
##                  Salario_diario < 229
##                  Calidad or CEDIS or Costura or EHS or Embarques or Otros or Paileria y Pintura or Produccion Carton MC or Produccion Carton MDL or Produccion Retornable
##                  abril or junio or marzo or mayo
## 
## Renuncia is 0.90 when
##                  Salario_diario < 229
##                  Calidad or CEDIS or EHS or Embarques or Otros or Paileria y Pintura or Produccion Carton MDL
##                  agosto or diciembre or febrero or julio or octubre or septiembre
##                  Edad < 46
## 
## Renuncia is 1.00 when
##                  Salario_diario < 229
##                  area no definida or Cortadoras or Materiales or Stabilus

Predicción y Resultados

De acuerdo a los resultados de la matriz de confusión, el desempeño inicial del modelo muestra que se logra predecir acertadamente en un 87.27% de los casos. AdemÔs cuenta con un nivel de Kappa de 0.6856, siendo mejor a una predicción aleatoria.

# Predicción
prediccion_A<- predict(arbol, test, type="class")

# Matriz de confuisión
confusion_A <- confusionMatrix(prediccion_A, test$Renuncia,positive = "No")
print(confusion_A)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Si
##         No 24  7
##         Si  7 72
##                                           
##                Accuracy : 0.8727          
##                  95% CI : (0.7957, 0.9286)
##     No Information Rate : 0.7182          
##     P-Value [Acc > NIR] : 8.935e-05       
##                                           
##                   Kappa : 0.6856          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.7742          
##             Specificity : 0.9114          
##          Pos Pred Value : 0.7742          
##          Neg Pred Value : 0.9114          
##              Prevalence : 0.2818          
##          Detection Rate : 0.2182          
##    Detection Prevalence : 0.2818          
##       Balanced Accuracy : 0.8428          
##                                           
##        'Positive' Class : No              
## 

El AUC obtenido para el modelo de Ôrbol de decisión es de 0.92, reafirmando su desempeño sobre un modelo aleatoria.

#  Curva ROC 
## Obtener las puntuaciones de probabilidad
prediccion_prob_tree <- predict(arbol, test, type = "prob")
prediccion_prob_true_tree <- prediccion_prob_tree[ , "No"]

## Generar la curva ROC
roc_obj_tree <- roc(test$Renuncia, prediccion_prob_true_tree)

## Dibujar la curva ROC
plot.roc(roc_obj_tree, main="Curva ROC", col="blue")

## Calcular el AUC
auc_tree <- auc(roc_obj_tree)
auc_tree
## Area under the curve: 0.9249

Modelo 3 Naive Bayes

El tercer modelo elegido fue Naive Bayes, este modelo fue seleccionado como una alternativa debido a su enfoque en la probabilidad condicional bajo el supuesto de que cada una de las variables explicativas impacta de manera independiente a la variable interés (sin existe una interacción entre ellas). Este funcionamiento puede representar una forma estadística clara para diferenciar entre los empleados que renuncian y los que se mantienen en la empresa, manteniendo un buen nivel de interpretabilidad a través de visualizaciones de la probabilidad para cada una de las variables independientes.

modelo_nb <- naiveBayes(formula1, data = train)

Interpretación

Con la finalidad de interpretar adecuadamente el modelo de Naive Bayes, con su supuesto de independiente, se obtuvieron una serie de visualizaciones que permiten determinar la probabilidad condicional con la que un nivel o valor en las variables independientes determina que un empleado renuncie o no

GƩnero

Se observa que la probabilidad de renuncia es similar para las mujeres y los hombres, ya que el modelo Naive Bayes no encuentra una diferencia significativa en la probabilidad de renuncia entre los dos géneros, es decir, el género no representa una variable que tenga efecto sobre el si un empleado renuncia o no. Esto podría ocurrir debido a que los empleados dan mayor peso a otras variables y hay una distinción casi nula en el trato a los empleados segun su genero. No obstante, se observa que las mujeres tienen una probabilidad de renuncia ligeramente mayor que los hombres, por ende, se requiere generar un lugar de trabajo mÔs justo y equitativo para todos los empleados.

bp <- barplot(modelo_nb$tables$Genero, beside = TRUE, col = c("black", "lightgray"), legend.text = TRUE, main = "GƩnero",
              ylim = c(0, max(modelo_nb$tables$Genero) * 1.2))
text(x = bp, y = modelo_nb$tables$Genero, label = round(modelo_nb$tables$Genero,4), pos = 3)

Departamento

Los empleados del departamento ā€œĆrea no definidaā€ tienen una mayor probabilidad de considerar la renuncia, posiblemente debido a la falta de claridad en sus roles y responsabilidades, lo que genera incertidumbre y desmotivación. En contraste, aquellos que trabajan en los departamentos de Producción MC, MCL y Retornable, asĆ­ como en Costura y CEDIS, muestran una mayor probabilidad de permanecer en la empresa, probablemente porque estos roles estĆ”n mejor definidos y proporcionan una mayor estabilidad y satisfacción laboral. De hecho, la variación de probabilidades de renuncia entre los diferentes departamentos de FORM puede explicarse por diversos factores relacionados con el contexto de negocio de la empresa. Algunos de estos factores podrĆ­an ser:
* Condiciones de trabajo: Las condiciones de trabajo en algunos departamentos pueden ser mÔs duras o peligrosas que en otros, lo que podría conducir a una menor satisfacción laboral y una mayor probabilidad de renuncia.
* Salario y beneficios: El salario y los beneficios pueden ser mÔs bajos en algunos departamentos que en otros, lo que podría llevar a la frustración y la renuncia.
* Oportunidades de crecimiento: Las oportunidades de crecimiento pueden ser mƔs limitadas en algunos departamentos que en otros, lo que podrƭa desmotivar a los empleados y llevarlos a buscar oportunidades en otras empresas.
* Cultura del departamento: La cultura del departamento puede ser mÔs negativa o tóxica en algunos departamentos que en otros, lo que podría crear un ambiente de trabajo hostil y llevar a la renuncia.

bp <- barplot(modelo_nb$tables$Departamento, legend.text = TRUE, beside = TRUE, col = c("black", "lightgray"), main = "Departamento",
              ylim = c(0, max(modelo_nb$tables$Departamento) * 1.2), cex.names =  0.6)
text(x = bp, y = modelo_nb$tables$Departamento, label = round(modelo_nb$tables$Departamento,2), pos = 3)

Municipio

Los colaboradores del municipio de Apodaca tienen mayor probabilidad de considerar renunciar debido a posibles desafíos logísticos y una menor calidad de vida, lo que afecta negativamente su satisfacción laboral. En contraste, los colaboradores de Guadalupe tienen mayor probabilidad de permanecer en la empresa, posiblemente gracias a mejores condiciones de vida y servicios, lo que contribuye a una mayor satisfacción y compromiso con la empresa. Sin embargo, es importante destacar la variación de probabilidades de renuncia entre los diferentes municipios de FORM, lo cual puede explicarse por diversos factores relacionados con el contexto de negocio de la empresa. Algunos de estos factores podrían ser: * Costo de vida: El costo de vida puede ser mÔs alto en algunos municipios que en otros, lo que podría llevar a la insatisfacción de los empleados con su salario y beneficios. * Distancia al trabajo: La distancia al trabajo puede ser mÔs larga en algunos municipios que en otros, lo que podría generar dificultades en el transporte y un menor equilibrio entre la vida laboral y personal. * Oportunidades de empleo: Las oportunidades de empleo pueden ser mÔs limitadas en algunos municipios que en otros, lo que podría desmotivar a los empleados y llevarlos a buscar oportunidades en otras empresas. * Calidad de vida: La calidad de vida puede ser mÔs baja en algunos municipios que en otros, lo que podría afectar negativamente la satisfacción general de los empleados.

bp <-barplot(modelo_nb$tables$Municipio, legend.text = TRUE, beside = TRUE, col = c("black", "lightgray"), main = "Municipio",
              ylim = c(0, max(modelo_nb$tables$Municipio) * 1.2))
text(x = bp, y = modelo_nb$tables$Municipio, label = round(modelo_nb$tables$Municipio,2), pos = 3)

Estado Civil

Los colaboradores en matrimonio y los divorciados tienen una mayor probabilidad de permanecer en la empresa, probablemente debido a sus mayores responsabilidades familiares y financieras, que los hacen valorar mÔs la estabilidad laboral. Por otro lado, los colaboradores solteros y aquellos en unión libre presentan una mayor probabilidad de renunciar, posiblemente debido a su menor nivel de compromisos personales, lo que les otorga una mayor libertad para buscar nuevas oportunidades y cambios en su carrera profesional. La diferencia de probabilidades de renuncia entre los empleados de FORM según su estado civil puede explicarse por diversos factores relacionados con el contexto de negocio de la empresa. Algunos de estos factores podrían ser: * Estrés: Los empleados divorciados pueden experimentar mÔs estrés que los empleados casados o solteros, lo que podría afectar negativamente su salud mental y física y conducir a una menor satisfacción laboral y una mayor probabilidad de renuncia. * Dificultades financieras: Los empleados divorciados pueden tener mÔs dificultades financieras que los empleados casados o solteros, lo que podría llevarlos a buscar un trabajo con un salario mÔs alto o a renunciar para cuidar a sus hijos. * Falta de apoyo social: Los empleados divorciados pueden tener menos apoyo social que los empleados casados o solteros, lo que podría desmotivarlos y hacerlos mÔs propensos a renunciar.

bp <-barplot(modelo_nb$tables$Estado_Civil, legend.text = TRUE, beside = TRUE, col = c("black", "lightgray"), main = "Estado Civil",
              ylim = c(0, max(modelo_nb$tables$Estado_Civil) * 1.2))
text(x = bp, y = modelo_nb$tables$Estado_Civil, label = round(modelo_nb$tables$Estado_Civil,2), pos = 3)

Salario diario

Los empleados con un salario mƔs alto tienen una menor probabilidad de renunciar que los empleados con un salario mƔs bajo. Las probabilidades especƭficas son las siguientes: * Salario diario menor a 200: 61% * Salario diario entre 200 y 300: 48% * Salario diario entre 300 y 400: 35% * Salario diario mayor a 400: 22%

La relación inversa entre el salario y la probabilidad de renuncia es un fenómeno común en el mercado laboral. Existen diversas explicaciones para esta relación: * Satisfacción laboral: Los empleados con un salario mÔs alto suelen estar mÔs satisfechos con su trabajo que los empleados con un salario mÔs bajo. Esto se debe a que el salario puede satisfacer necesidades bÔsicas y proporcionar un mayor nivel de vida. * Motivación: Los empleados con un salario mÔs alto suelen estar mÔs motivados para trabajar duro y alcanzar sus objetivos. Esto se debe a que ven su trabajo como una forma de alcanzar sus metas financieras y personales. * Oportunidades alternativas: Los empleados con un salario mÔs alto suelen tener mÔs oportunidades de empleo en otras empresas. Esto significa que si no estÔn satisfechos con su trabajo actual, es mÔs probable que buscquen y/o encuentren otro trabajo mejor pagado.

## Tablas de Media y Desviación EstÔndar para Variables Continuas
bp <-barplot(modelo_nb$tables$Salario_diario, legend.text = TRUE, beside = TRUE, col = c("black", "lightgray"), names.arg = c("Media", "Desviación estÔndar"), main = "Salario_diario",
              ylim = c(0, max(modelo_nb$tables$Salario_diario) * 1.2))
text(x = bp, y = modelo_nb$tables$Salario_diario, label = round(modelo_nb$tables$Salario_diario,2), pos = 3)

Edad

Los colaboradores de mayor edad tienen una mayor probabilidad de permanecer dentro de la empresa, ya que suelen valorar mÔs la estabilidad laboral y las relaciones establecidas en su entorno de trabajo. Por otro lado, los colaboradores de menor edad tienden a considerar la renuncia con mayor frecuencia, posiblemente debido a la mayor libertad e independencia que caracteriza a los adultos jóvenes. Esta diferencia en la propensión a permanecer o renunciar puede estar influenciada por las distintas prioridades y expectativas que tienen los empleados en diferentes etapas de sus vidas profesionales. En resumen, los empleados mÔs jóvenes tienen una menor probabilidad de renunciar que los empleados mÔs mayores. Las probabilidades específicas son las siguientes: * Menor a 25: 38% * Entre 25 y 30: 45% * Entre 30 y 35: 52% * Entre 35 y 40: 59% * Mayor a 40: 65%

bp <-barplot(modelo_nb$tables$Edad, legend.text = TRUE, beside = TRUE, col = c("black", "lightgray"), main = "Edad", names.arg = c("Media", "Desviación estÔndar"),
              ylim = c(0, max(modelo_nb$tables$Edad) * 1.2))
text(x = bp, y = modelo_nb$tables$Edad, label = round(modelo_nb$tables$Edad,2), pos = 3)

Mes

La retención de empleados en la empresa FORM muestra una tendencia clara en función del mes de contratación. Los colaboradores contratados durante los meses de abril, febrero, marzo, mayo y octubre tienen una mayor probabilidad de permanecer en la empresa, con una retención particularmente alta en abril. En contraste, aquellos que inician su trabajo en los meses de agosto, junio, julio y septiembre presentan una mayor tendencia a considerar la renuncia. Esto sugiere que la planificación estratégica de las contrataciones en meses específicos puede influir significativamente en la estabilidad y la permanencia del personal. De hecho, la diferencia de probabilidades de renuncia entre los empleados de FORM según su mes de contratación puede explicarse por diversos factores como: * 1. Contrataciones estacionales: FORM podría realizar contrataciones estacionales para satisfacer una mayor demanda de productos o servicios durante los meses de verano, cuando las personas suelen tener mÔs tiempo libre y dinero para gastar. Estos empleados contratados estacionalmente podrían ser menos propensos a permanecer en la empresa después de la temporada alta, lo que explicaría la mayor probabilidad de renuncia en los meses de verano.
* 2. Falta de capacitación: FORM podría no proporcionar una capacitación adecuada a los empleados contratados durante los meses de verano, lo que podría generar dificultades en su trabajo y una menor satisfacción laboral. Esto podría llevar a una mayor probabilidad de renuncia en estos meses.
* 3. Expectativas no cumplidas: Los empleados contratados durante los meses de verano podrƭan tener expectativas poco realistas sobre su trabajo, como un mayor salario o mƔs oportunidades de crecimiento. Cuando estas expectativas no se cumplen, los empleados podrƭan sentirse frustrados y renunciar.
* 4. Factores externos: La mayor probabilidad de renuncia en los meses de verano también podría estar relacionada con factores externos, como el clima cÔlido o las vacaciones escolares. Estos factores podrían hacer que los empleados sean mÔs propensos a buscar otras oportunidades o a tomar un tiempo libre, lo que podría conducir a una mayor rotación de personal.

bp <- barplot(modelo_nb$tables$NombreMes, legend.text = TRUE, beside = TRUE, col = c("black", "lightgray"), main = "Mes de Contratación", cex.names = 0.8)
text(x = bp, y = modelo_nb$tables$NombreMes, label = round(modelo_nb$tables$NombreMes,2), pos = 3)

Predicción y Resultados

Gracias a la matriz de confusión es posible determinar que el modelo predice acertadamente en un 90.91% de los casos en que un colaborador decide renunciar. Junto a esto, se obtiene un valor de Kappa de 0.7754, mostrando que el modelo estÔ prediciendo considerablemente mejor a una clasificación aleatoria.

# Predicción
prediccion_nb <- predict(modelo_nb, test)

# Matriz de Confusión
confusion_nb <- confusionMatrix(prediccion_nb, test$Renuncia,positive = "No")
print(confusion_nb)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Si
##         No 26  5
##         Si  5 74
##                                           
##                Accuracy : 0.9091          
##                  95% CI : (0.8392, 0.9555)
##     No Information Rate : 0.7182          
##     P-Value [Acc > NIR] : 8.235e-07       
##                                           
##                   Kappa : 0.7754          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.8387          
##             Specificity : 0.9367          
##          Pos Pred Value : 0.8387          
##          Neg Pred Value : 0.9367          
##              Prevalence : 0.2818          
##          Detection Rate : 0.2364          
##    Detection Prevalence : 0.2818          
##       Balanced Accuracy : 0.8877          
##                                           
##        'Positive' Class : No              
## 

Se obtiene un valor de AUC del 0.8971, reafirmando que la predicción del modelo es mejor que una predicción aleatoria

#  Curva ROC 
## Obtener las puntuaciones de probabilidad
prediccion_prob_nb <- predict(modelo_nb, test, type = "raw")

## Seleccionar la columna que corresponde a la clase 'No'
prediccion_prob_true_nb <- prediccion_prob_nb[, "No"]

## Generar la curva ROC
roc_obj_nb <- roc(test$Renuncia, prediccion_prob_true_nb)

## Dibujar la curva ROC
plot.roc(roc_obj_nb, main="Curva ROC", col="blue")

## Calcular el AUC
auc_nb <- pROC::auc(roc_obj_nb)
auc_nb
## Area under the curve: 0.8971

Modelo 4 Random Forest

El modelo de Random Forest fue seleccionado con la intención de emplear un modelo complejo capaz de tener una alta capacidad predictiva a cambio de perder interpretabilidad, buscando comparar de esta manera el desempeño de modelos de mayor simplicidad con uno de alta complejidad. Los parÔmetros seleccionados fueron un estÔndar seleccionado para la configuración del random forest.

set.seed(123)

# Creación del modelo
rf_model <- randomForest(formula1, data = train, ntree=500, mtry=5, nodesize=5, importance=TRUE)

Interpretación

A pesar del tradeoff entre interpretabilidad y precisión, el modelo ofrece una visualización para comprobar el impacto que estÔ ejerciendo cada variable para determinar la pertenencia a una determinada clase (Renuncia o No renuncia). Sin embargo, esto no contempla una dirección clara del impacto ejercido. Considerando esto, se determina que las variables que tienen mayor influencia en el modelo corresponden a: Salario diario, departamento y el mes de contratación, siendo poco relevantes el género y el estado civil (de acuerdo al modelo).

varImpPlot(rf_model)

Predicción y Resultados

El modelo logra predecir acertadamente en un 90% de los casos en que un colaborador decide renunciar. El nivel de kappa es de 0.7402, superando a una clasificación aleatoria.

# Predicción
prediccion_rf <- predict(rf_model, test)

# Matriz de Confusión
confusion_rf <- confusionMatrix(prediccion_rf, test$Renuncia,positive = "No")
print(confusion_rf)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Si
##         No 23  3
##         Si  8 76
##                                          
##                Accuracy : 0.9            
##                  95% CI : (0.8281, 0.949)
##     No Information Rate : 0.7182         
##     P-Value [Acc > NIR] : 3.045e-06      
##                                          
##                   Kappa : 0.7402         
##                                          
##  Mcnemar's Test P-Value : 0.2278         
##                                          
##             Sensitivity : 0.7419         
##             Specificity : 0.9620         
##          Pos Pred Value : 0.8846         
##          Neg Pred Value : 0.9048         
##              Prevalence : 0.2818         
##          Detection Rate : 0.2091         
##    Detection Prevalence : 0.2364         
##       Balanced Accuracy : 0.8520         
##                                          
##        'Positive' Class : No             
## 

Tiene un valor de AUC de 0.96, reafirmando su superioridad sobre una predicción aleatoria.

# Curva ROC 
## Obtener las puntuaciones de probabilidad
prediccion_prob_rf <- predict(rf_model, test, type = "prob")

## Seleccionar la columna que corresponde a la clase 'No'
prediccion_prob_true_rf <- prediccion_prob_rf[, "No"]

## Generar la curva ROC
roc_obj_rf <- roc(test$Renuncia, prediccion_prob_true_rf)

## Dibujar la curva ROC
plot.roc(roc_obj_rf, main="Curva ROC", col="blue")

## Calcular el AUC
auc_rf <- pROC::auc(roc_obj_rf)
auc_rf
## Area under the curve: 0.9622

Comparación de modelos

# Obtener cada una de las mƩtricas
#ACCURACY
acc_RM<- confusion_glm$overall["Accuracy"]
acc_A<- confusion_A$overall["Accuracy"]
acc_NB<- confusion_nb$overall["Accuracy"]
acc_RF<- confusion_rf$overall["Accuracy"]

#BALANCED ACCURACY
B_acc_RM<- confusion_glm$byClass["Balanced Accuracy"]
B_acc_A<- confusion_A$byClass["Balanced Accuracy"]
B_acc_NB<- confusion_nb$byClass["Balanced Accuracy"]
B_acc_RF<- confusion_rf$byClass["Balanced Accuracy"]

#KAPPA
kappa_RM<- confusion_glm$overall["Kappa"]
kappa_A<- confusion_A$overall["Kappa"]
kappa_NB<- confusion_nb$overall["Kappa"]
kappa_RF<- confusion_rf$overall["Kappa"]

#SENSITIVITY
sens_RM<- confusion_glm$byClass["Sensitivity"]
sens_A<- confusion_A$byClass["Sensitivity"]
sens_NB<- confusion_nb$byClass["Sensitivity"]
sens_RF<- confusion_rf$byClass["Sensitivity"]

#SPECIFITY
spec_RM<- confusion_glm$byClass["Specificity"]
spec_A<- confusion_A$byClass["Specificity"]
spec_NB<- confusion_nb$byClass["Specificity"]
spec_RF<- confusion_rf$byClass["Specificity"]

#AUC
auc_RM<- auc_glm
auc_A<- auc_tree
auc_NB<- auc_nb
auc_rf<-auc_rf

# Creación de vectores para las métricas de cada modelo
metricas <- c("Accuracy", "Balanced Accuracy", "Kappa", "Sensitivity", "Specificity", "AUC")
glm_values <- c(acc_RM, B_acc_RM, kappa_RM, sens_RM, spec_RM, auc_RM)
arbol_values <- c(acc_A, B_acc_A, kappa_A, sens_A, spec_A, auc_A)
naiveBayes_values <- c(acc_NB, B_acc_NB, kappa_NB, sens_NB, spec_NB, auc_NB)
randomforest_values <- c(acc_RF, B_acc_RF, kappa_RF, sens_RF, spec_RF, auc_rf)

Tabla Comparativa de MƩtricas

Tras la construcción de distintos modelos para la situación problema de Form fue posible obtener las siguientes medidas de desempeño para cada uno de ellos: Accuracy, Balanced Accuracy, Kappa, Sensitivity, Specificity, AUC. En consideración de estos, se plantea la siguiente evaluación:

  • 1. Accuracy: Inicialmente, como una medida rĆ”pida sobre el desempeƱo general de los modelos se considera el Accuracy General, es decir, el porcentaje de aciertos del modelo sobre el total de predicciones. En este caso, los modelos tienen un desempeƱo bastante similar, estando en un rando entre el 87-90.9%. Sin embargo, el modelo con el mejor desempeƱo corresponde a Naive Bayes.

  • 2. Kappa: Como siguiente medida, es necesario evaluar el que cada uno de los modelos fuera capaz de realizar una predicción mejor a una clasificación aleatoria, es decir, tener un valor Kappa mayor a 0. En este caso, cada uno de los modelos construidos obtuvo altos valores para esta medida, superiores a 0.6, lo cual es un primer buen indicativo del modelo.

  • 3. Balanced Accuracy: Tras afirmar que los modelos no estĆ”n realizando predicciones aleatorias, se comprueba mediante el Balanced Accuracy la ponderación de las predicciones correctas de ambas clases, destacando nuevamente el modelo de Naive Bayes como aquel que mantiene el porcentaje mĆ”s alto con 88.7%.

  • 4. Sensitivity y Specificity: Mediante estas medidas se analizarĆ”n individualmente las predicciones correctas de cada clase. Siendo sensitivity el porcentaje de predicciones correctas para los empleados que no renuncian, y specificity, las predicciones correctas para los que sĆ­ renuncian. En este caso, Naive Bayes obtiene el valor mĆ”s alto de sensitivity con 83.87% seguido del Ɓrbol de Decisión con 77.4%. Por otro lado, el modelo de Random Forest obtiene el valor mĆ”s alto de specificity con 96.2%, seguido de Naive Bayes con 93.67%.

  • 5. AUC y Curva ROC: Finalmente, la Ćŗltima mĆ©trica a revisar para este caso es el Ɓrea bajo la Curva (AUC), obtenida de la curva ROC. La curva ROC muestra la distinción entre verdaderos y falsos positivos por parte del modelo, buscando que esta sea lo mĆ”s recta y grande posible, permitiendo alcanzar un Ć”rea lo mĆ”s cercana posible a 1. Para esta mĆ©trica el modelo de Random Forest es aquel que mejor se desempeƱo al hacer la distinción, con un AUC de 0.96 (muy cercano a 1: ajuste casi perfecto), seguido del Ɓrbol de Decisión con 0.92.

# Crear el dataframe comparativo
TablaComparacion <- data.frame(
  Metrica = metricas,  
  RegresiónLogistica = glm_values,  
  ArboldeDecision = arbol_values,  
  NaiveBayes = naiveBayes_values,
  RandomForest = randomforest_values
)
rownames(TablaComparacion) = NULL
TablaComparacion <- head(TablaComparacion, 6)

kable(TablaComparacion)
Metrica RegresiónLogistica ArboldeDecision NaiveBayes RandomForest
Accuracy 0.8727273 0.8727273 0.9090909 0.9000000
Balanced Accuracy 0.8329931 0.8427930 0.8877093 0.8519804
Kappa 0.6793003 0.6855860 0.7754185 0.7402319
Sensitivity 0.7419355 0.7741935 0.8387097 0.7419355
Specificity 0.9240506 0.9113924 0.9367089 0.9620253
AUC 0.8591262 0.9248673 0.8971009 0.9622295

Comparación de Curvas ROC

Las curvas ROC (Receiver Operating Characteristic) son una herramienta grÔfica que se utiliza para evaluar el rendimiento de un modelo de clasificación binaria, en este caso si el empleado de FORM decide seguir o no en la empresa. A continuación, en las curvas ROC que se muestran en la imagen, se estÔn comparando los cuatro modelos de clasificación: Regresión, Árbol de Decision, Naive Bayes y Random Forest.

  • 1. Curva ROC de Regresión: La curva tiene una baja especificidad, lo que significa que el modelo es propenso a clasificar erróneamente los negativos como positivos. Esto se puede observar en la parte inferior izquierda de la curva, donde la curva se acerca a la lĆ­nea diagonal.

  • 2. Curva ROC de Ɓrbol: Tiene una especificidad mĆ”s alta que la curva ROC de Regresión, lo que significa que el modelo es menos propenso a clasificar erróneamente los negativos como positivos. Sin embargo, la curva ROC de Ɓrbol tambiĆ©n tiene una sensibilidad mĆ”s baja, lo que significa que el modelo es menos propenso a clasificar correctamente los positivos. Esto se puede observar en la parte superior izquierda de la curva, donde la curva se aleja de la lĆ­nea diagonal.

  • 3. Curva ROC de Naive Bayes: Tiene una especificidad similar a la curva ROC de Ɓrbol, pero una sensibilidad mĆ”s alta. Esto significa que el modelo es mĆ”s propenso a clasificar correctamente los positivos, pero tambiĆ©n es mĆ”s propenso a clasificar erróneamente los negativos como positivos.

  • 4. Curva ROC de Random Forest: Tiene la especificidad mĆ”s alta de las cuatro curvas ROC. Esto significa que el modelo es menos propenso a clasificar erróneamente los negativos como positivos. La curva ROC de Random Forest tambiĆ©n tiene una sensibilidad alta, lo que significa que el modelo es mĆ”s propenso a clasificar correctamente los positivos.

#GRAFICAS ROC
par(mfrow=c(2, 2))

plot.roc(roc_obj_glm, main = "Curva ROC Regresión", col = "blue")

plot.roc(roc_obj_tree, main="Curva ROC Ɓrbol", col="blue")

plot.roc(roc_obj_nb, main="Curva ROC Naive Bayes", col="blue")

plot.roc(roc_obj_rf, main="Curva ROC Random Forest", col="blue")

Conclusiones

Con base en los anƔlisis previamente hechos de los modelos realizados, se determina que el modelo mƔs apropiado para analizar el abandono de los empleados en Form corresponde a Naive Bayes, debido a su capacidad de analizar cada factor de forma independiente, asƭ como su desempeƱo considerable en las mƩtricas. Accuracy:0. 91, Kappa: 0.78 y AUC: 0.90. Acorde a esto, se determina que la mayorƭa de la fuerza laboral de Form tiene las siguientes caracterƭsticas: Mujeres solteras de 31 o 32 aƱos, residentes de Apodaca, con un puesto de Ayudante General del cual perciben un salario promedio de $215.9.

Por otra parte, acorde al modelo Naive Bayes, las variables explicativas estiman que estiman el siguiente perfil para determinar la permanencia/retención de los empleados en Form: * Empleadas y empleados casados y/o divorciados de mayor edad que viven en el municipio de Guadalupe pertenecientes a los departamentos de Producción MC, MCL y Retornable que perciben un salario diario mayor donde su mes de contratación es mayormente a principios de año.

El perfil anterior se puede generar debido a que los empleados casados y divorciados suelen tener mayores responsabilidades familiares y financieras, lo que los hace valorar mÔs la estabilidad laboral. AdemÔs, al ser personas de mayor pueden tener menos disposición para cambiar de trabajo debido a estas responsabilidades y a que tienden a valorar mÔs la estabilidad, la seguridad y las relaciones establecidas en su lugar de trabajo. Asimismo, al ser residentes de Guadalupe podría implicar no solamente que por la ubicación de la empresa realizan menores tiempos de desplazamiento sino que también la oferta de trabajo en dicha ubicación es mejor que en su lugar de residencia. No obstante, se debe tomar en cuenta que los departamentos de dicho grupo de personas ya que, implica que tienen roles mÔs definidos y estables, con una clara trayectoria de desarrollo y una sensación de contribución tangible al producto final en comparación con otros departamentos lo cual se refuerza con la percepción de un salario diario dando a entender que los empleados sienten que estÔn bien compensados por su trabajo y por ende, son mÔs propensos a permanecer en la empresa.

Sin embargo, acorde al modelo Naive Bayes, las variables explicativas que estiman el siguiente perfil para determinar la renuncia y/o falta de seguimiento de los empleados en Form son: * Empleadas y empleados solteros y en unión libre de menor edad (adultos jóvenes) que viven en el municipio de Apodaca que perciben un salario diario menor donde su mes de contratación es mayormente a mediados de año.

El perfil anterior se puede generar debido a que los colaboradores solteros y en unión libre suelen tener menos responsabilidades familiares, lo que les otorga mayor libertad para buscar mejores oportunidades laborales. AdemÔs, estan mÔs dispuestos a cambiar de trabajo en busca de mejores condiciones o desafíos nuevos, mayormente si perciben un salario bajo ya que implica una fuente importante de insatisfacción labor. Esto se refuerza ya que, al ser adultos jóvenes suelen tener menos compromisos y pueden ser mÔs propensos a buscar nuevas oportunidades para avanzar en sus carreras. No obstante, se observa que dicho perfil abarca residentes de Apodaca lo que implica que al vivir en dicha zona buscan y/o conocen oportunidades laborales en las cuales perciban mayor valor que en FORM. Asimismo, el periodo de contratación tiene alta posibilidad de coincidir con los meses donde hay altas temperaturas afectando las condiciones laborales, tiempos de contratación de otras empresas en las cuales se puede percibir un mayor beneficio y/o con periodos de FORM donde hay una contratación mÔs impulsiva para cubrir necesidades temporales. En consecuencia, los empleados contratados en estos meses pueden sentir una menor integración y estabilidad en la empresa, lo que incrementa la probabilidad de renuncia.

Por lo tanto, las características que influyen en la permanencia o renuncia en la empresa FORM reflejan la importancia de la estabilidad, la claridad en el rol, la compensación adecuada y las condiciones personales de los empleados. Entender estos factores permite a la empresa implementar estrategias específicas para mejorar la retención, como: * Realizar un anÔlisis de mercado para asegurar que los salarios sean competitivos, ya que considerar aumentos salariales o bonificaciones justas para aumentar la satisfacción y reducir la intención de renunciar debido a la percepción de una compensación justa. * Implementar programas de capacitación y desarrollo de habilidades, así como planes de carrera claros para todos los empleados, lo que puede aumentar la lealtad y el compromiso de los empleados con la empresa. * Planificar las contrataciones estratégicamente para evitar los meses con mayor índice de renuncia (Agosto, Junio, Julio, Septiembre) y mejorar el proceso de onboarding para integrar a los empleados de manera efectiva; lo cual reducirÔ la rotación temprana de los nuevos empleados.

Muestra de Predicción con el Modelo Seleccionado

Según el modelo seleccionado, se estima que aproximadamente el 16% de los empleados actuales de la empresa FORM consideran no seguir siendo parte de la organización en un futuro próximo. AdemÔs, dentro de este grupo, se observa que tres de cada cinco empleados son mujeres. Dicho hallazgo sugiere la necesidad de desarrollar estrategias efectivas de retención que aborden las preocupaciones y necesidades específicas de este segmento de la fuerza laboral.

test_muestra            <- test
test_muestra$prediccion <- predict(modelo_nb, test)

# Filtrar solo los empleados que tienen "No" en la columna "Renuncia"
test_muestra_filtrado <- test_muestra %>%
  filter(Renuncia == "No")

# Mostrar Valor Real vs Predicción para Cada Empleado del Set de Muestra
test_muestra_filtrado %>%
  dplyr::select(Nombre, Apellidos, Renuncia, prediccion)
## # A tibble: 31 Ɨ 4
##    Nombre        Apellidos             Renuncia prediccion
##    <chr>         <chr>                 <fct>    <fct>     
##  1 mariana       de leon moreno        No       Si        
##  2 maria         cazares morales       No       No        
##  3 jose antonio  riojas gutierrez      No       No        
##  4 blanca olivia barron ramos          No       No        
##  5 pedro ivan    marin arjona          No       No        
##  6 maria esther  baluis paulino        No       No        
##  7 adriana irene zapata garcia         No       No        
##  8 andres        sanchez de la rosa    No       No        
##  9 belem sarahi  moncada  zavala       No       Si        
## 10 anjuli rubi   bezanilla  villarreal No       Si        
## # ℹ 21 more rows
conteo_si <- table(test_muestra_filtrado$prediccion == "Si")
conteo_si
## 
## FALSE  TRUE 
##    26     5
LS0tDQp0aXRsZTogIkV2aWRlbmNpYSAyOiBQcmVwYXJhY2nDs24geSBtb2RlbGFkbyBkZSBsb3MgZGF0b3MiDQpzdWJ0aXRsZTogIkNEMzAwMkMgSW50ZWxpZ2VuY2lhIEFydGlmaWNpYWwgY29uIEltcGFjdG8gRW1wcmVzYXJpYWwiDQphdXRob3I6ICJFcXVpcG8gMSINCmRhdGU6ICIyMDI0LTA1LTIzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICB0aGVtZTogdW5pdGVkDQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIGNzczogc3R5bGVzLmNzcw0KLS0tDQoNCioqSW50ZWdyYW50ZXM6KioNCg0KTHVpcyBBbmdlbCBFbGl6b25kbyBHYWxsZWdvcyBBMDExOTgxODYNCiAgDQpBdnJpbCBMb2JhdG8gRGVsZ2FkbyBBMDA4MzMxMTMNCiAgDQpHZW5hcm8gUm9kcsOtZ3VleiBBMDA4MzMxNzINCiAgDQpFbnJpcXVlIFBhYmxvcyBBMDA4MzUwMzcNCiAgDQpEaWVnbyBQZXLDqXogQTAxMjc1NTYxDQoNCg0KIyBMaWJyZXLDrWFzDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gVFJVRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCgllY2hvID0gVFJVRSwNCgltZXNzYWdlID0gRkFMU0UsDQoJd2FybmluZyA9IEZBTFNFDQopDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHB1cnJyKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KERhdGFFeHBsb3JlcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRtKQ0KbGlicmFyeSh3b3JkY2xvdWQpDQpsaWJyYXJ5KGNsdXN0ZXIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpIA0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KHB1cnJyKQ0KbGlicmFyeShwUk9DKQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoZTEwNzEpDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkoZGxvb2tyKQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShzdGF0cykNCmxpYnJhcnkodHNlcmllcykNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHZhcnMpDQpsaWJyYXJ5KHN5dXpoZXQpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmBgYA0KDQojIExpbXBpZXphIGRlIEJhc2VzIGRlIERhdG9zDQojIyBEZXNjcmlwY2nDs24gZGUgbGEgZXN0cnVjdHVyYSBnZW5lcmFsIGRlIGxvcyBkYXRvcyBwcmV2aW8gYSBsYSBsaW1waWV6YQ0KDQpMYSBiYXNlIHBhcmEgbGEgKipzaXR1YWNpw7NuIHByb2JsZW1hIGRlIFJlY3Vyc29zIEh1bWFub3MgZW4gRm9ybSoqIGVzdMOhIGNvbmZvcm1hZGEgcG9yIDI5IHZhcmlhYmxlcyBwYXJhIHVuIHRvdGFsIGRlIDU1MyBlbXBsZWFkb3MgcmVnaXN0cmFkb3MsIGVudHJlIGxhcyBjdcOhbGVzIHNlIHByZXNlbnRhIGluZm9ybWFjacOzbiBwZXJzb25hbCBkZWwgZW1wbGVhZG8gY29tbyBzdSBub21icmUsIGZlY2hhIGRlIG5hY2ltaWVudG8sIGfDqW5lcm8geSBsdWdhciBkZSByZXNpZGVuY2lhLCBhc8OtIGNvbW8gZGF0b3MgbGFib3JhbGVzIHF1ZSBpbmNsdXllbiBmZWNoYSBkZSBpbmljaW8gZW4gRm9ybSwgZGVwYXJ0YW1lbnRvLCBwdWVzdG8sIHNhbGFyaW8sIGVudHJlIG90cm9zIGRhdG9zIGRlbCB0aXBvIGZpc2NhbC4gRXN0YSBmdWUgY3JlYWRhIGEgcGFydGlyIGRlIGxhIHVuacOzbiBwcmV2aWEgZGUgbGEgYmFzZSBkZSBlbXBsZWFkb3MgYWN0dWFsZXMgZGUgRm9ybSBoYXN0YSBlbCAyMDI0LCBhc8OtIGNvbW8gbGEgYmFzZSBkZSBCYWphcyBGb3JtIDIwMjQuDQoNCkVuIGNvbnNpZGVyYWNpw7NuIGRlIGVzdG8sIGxhIGJhc2UgcmVzdWx0YW50ZSBwcmVzZW50YSBpbmZvcm1hY2nDs24gdGFudG8gZGUgKiplbXBsZWFkb3MgcXVlIGNvbnRpbsO6YW4gc3VzIGFjdGl2aWRhZGVzIGxhYm9yYWxlcyoqIGVuIGxhIGVtcHJlc2EgKGVtcGxlYWRvIGNvbiBtYXlvciBhbnRpZ8O8ZWRhZCBlbiAyMDEwKSwgY29tbyBkZSAqKmFxdWVsbG9zIHF1ZSBoYW4gYWJhbmRvbmFkbyBzdXMgcHVlc3RvcyoqIGRlc2RlIDIwMjIuIFBhcmEgZXN0b3Mgw7psdGltb3Mgc2UgY29uc2lkZXJhbiBkYXRvcyByZWxhY2lvbmFkb3MgY29uIGxhIGJhamEsIGNvbW8gbGEgZmVjaGEsIG1vdGl2byB5IGRldGFsbGVzIGRlIMOpc3RhLiBBc2ltaXNtbywgZXMgbmVjZXNhcmlvIG1lbmNpb25hciBxdWUgbWVkaWFudGUgbGEgdW5pw7NuIGRlIGFtYmFzIGJhc2VzLCBmdWUgcG9zaWJsZSBkZXRlcm1pbmFyIGxhICoqdmFyaWFibGUgZGVwZW5kaWVudGUqKiBhIGFuYWxpemFyLCBsYSBjdWFsIGNvcnJlc3BvbmRlIGEgIlJlbnVuY2lhIiwgdW5hIHZhcmlhYmxlIHByZXZpYW1lbnRlIGNyZWFkYSBwYXJhIGRpZmVyZW5jaWFyIGEgbG9zIGVtcGxlYWRvcyBkZSBjYWRhIHByZXZpYSwgZXMgZGVjaXIsIGFxdWVsbG9zIHF1ZSBTw60gcmVudW5jaWFuIHkgbG9zIHF1ZSBOby4NCg0KDQpgYGB7cn0NCiMgTGVlciB5IG9ic2VydmFyIGVsIGFyY2hpdm8NCnJoX2FsdCA8LSByZWFkX2V4Y2VsKCJmb3JtX3JoX2RhdG9zLnhsc3giKQ0Kc3RyKHJoX2FsdCkNCnN1bW1hcnkocmhfYWx0KQ0KYGBgDQpUcmFzIGhhYmVyIGNvbXByZW5kaWRvIGxhIGVzdHJ1Y3R1cmEgeSBjb250ZW5pZG8gZGUgbGEgYmFzZSBkZSBkYXRvcywgc2UgbGxldsOzIGEgY2FibyB1bmEgc2VyaWUgZGUgbWVkaWRhcyBwYXJhIGxhIGxpbXBpZXphIHkgYWp1c3RlIHBhcmEgc3UgYWRlY3VhZG8gcHJvY2VzYW1pZW50by4gRXN0b3Mgc2UgbXVlc3RyYW4gYSBjb250aW51YWNpw7NuOg0KDQojIyBMaW1waWV6YSBHZW5lcmFsDQoNCiMjIyBSZW5vbWJyYXIgY29sdW1uYXMNCg0KU2Ugb3B0w7MgcG9yIG1vZGlmaWNhciBlbCBub21icmUgZGUgbGFzIGNvbHVtbmFzIGRlbCBkYXRhZnJhbWUgY29uIGxhIGZpbmFsaWRhZCBkZSBmYWNpbGl0YXIgZWwgbWFuZWpvIHkgZW50ZW5kaW1pZW50byBkZSBsYXMgY29sdW1uYXMgZHVyYW50ZSBsYSBtYW5pcHVsYWNpw7NuIGRlIGxvcyBkYXRvcywgY29uc2lkZXJhbmRvIGFzcGVjdG9zIGNvbW8gZWwgZXZpdGFyIGxvcyBlc3BhY2lvcyBlbiBibGFuY28geSBlbCB1c28gZGUgc8OtbWJvbG9zIHBhcmEgY2FkYSB1bm8gZGUgbG9zIG5vbWJyZXMuIExvcyBub21icmVzIGRlZmluaXRpdm9zIHBhcmEgY2FkYSBjb2x1bW5hIHB1ZWRlIGFwcmVjaWFyc2UgYSBjb250aW51YWNpw7NuOg0KDQpgYGB7cn0NCiMgUmVub21icmFyIGNvbHVtbmFzDQpjb2xuYW1lcyhyaF9hbHQpIDwtIGMoJ05vX0VtcGxlYWRvJywnQXBlbGxpZG9zJywgJ05vbWJyZScsJ0ZlY2hhX05hY2ltaWVudG8nLCdHZW5lcm8nLCAnUkZDJywNCiAgICAgICAgICAgICAgICAgICAgICAgJ0ZlY2hhX0FsdGEnLCdQcmltZXJfbWVzJywnQ3VhcnRvX21lcycsJ0JhamEnLCdQdWVzdG8nLCdEZXBhcnRhbWVudG8nLA0KICAgICAgICAgICAgICAgICAgICAgICAnTm9fU2VndXJvU29jaWFsJywnU2FsYXJpb19kaWFyaW8nLCAnRmFjdF9JbmZvbmF2aXQnLCdOb19DcmVkaXRvSW5mb25hdml0JywNCiAgICAgICAgICAgICAgICAgICAgICAgJ0x1Z2FyX25hY2ltaWVudG8nLCdDVVJQJywgJ0NhbGxlJywgJ05vX0ludGVybm8nLCdDb2xvbmlhJywnTXVuaWNpcGlvJywNCiAgICAgICAgICAgICAgICAgICAgICAgJ0VzdGFkbycsJ0NvZGlnb19Qb3N0YWwnLCdFc3RhZG9fQ2l2aWwnLCAnVGFyamV0YScsICdSZW51bmNpYScsIA0KICAgICAgICAgICAgICAgICAgICAgICAnTW90aXZvUmVudW5jaWEnLCAnRGV0YWxsZVJlbnVuY2lhJykNCmBgYA0KDQojIyMgVmFyaWFibGVzIGNvbiB2YWxvcmVzIG51bG9zIHkgc3UgbWFuZWpvDQoNClByaW1lcmFtZW50ZSBzZSBpZGVudGlmaWNhcm9uIG51bG9zIHNvYnJlIGxhcyB2YXJpYWJsZXMgbGlzdGFkYXMgYSBjb250aW51YWNpw7NuOg0KDQoqIEJhamEgKEZlY2hhIGJhamEpDQoNCiogRGVwYXJ0YW1lbnRvDQoNCiogRmFjdF9JbmZvbmF2aXQgDQoNCiogTm9fQ3JlZGl0b0luZm9uYXZpdA0KDQoqIE1vdGl2b1JlbnVuY2lhIA0KDQoqIERldGFsbGVSZW51bmNpYSANCg0KKiBUYXJqZXRhDQoNCiogTHVnYXIgZGUgTmFjaW1pZW50bw0KDQpQYXJhIGF0ZW5kZXIgZXN0YSBzaXR1YWNpw7NuIGZ1ZXJvbiBlbXBsZWFkYXMgZG9zIG1lZGlkYXM6DQoNCiogKipSZWVtcGxhemFyIHZhbG9yZXMgbnVsb3M6KiogRXN0YSBtZWRpZGEgY29uc2lzdGnDsyBlbiBjYW1iaWFyIGxvcyB2YWxvcmVzIG51bG9zIHBvciB1bmEgbnVldmEgY2xhc2UuIFBhcmEgZWwgcHJpbWVyIGNhc28sIGxhIGNvbHVtbmEgRGVwYXJ0YW1lbnRvLCBzZSBkZWZpbmnDsyBjb21vIMOBcmVhIERlc2Nvbm9jaWRhLCBjb24gbGEgaW50ZW5jacOzbiBkZSBubyBwZXJkZXIgbG9zIGRhdG9zIHBhcmEgZXN0b3MgZW1wbGVhZG9zLCBzaW5vIHF1ZSBlbiBjYXNvIGRlIGRlc3RhY2FyIGNvbW8gdmFyaWFibGUgZW4gbG9zIG1vZGVsb3MsIHRlbmVyIGxhIGNhcGFjaWRhZCBkZSBhbmFsaXphcmxhIG1lZGlhbnRlIHN1IHZhcmlhYmxlIGNvbXBsZW1lbnRhcmlhICJQdWVzdG8iLiBQb3Igb3RybyBsYWRvLCBsYXMgdmFyaWFibGVzIHJlbGFjaW9uYWRhcyBjb24gZWwgY29tcG9ydGFtaWVudG8gZGUgcmVudW5jaWEsIGluY2x1eWVuZG8gTW90aXZvUmVudW5jaWEgeSBEZXRhbGxlUmVudW5jaWEsIHBhcmEgbG9zIGVtcGxlYWRvcyBhY3R1YWxtZW50ZSBhY3Rpdm9zLCBhbCBubyBoYWJlciByZW51bmNpYWRvIG5vIGVzIHBvc2libGUgY29udGFyIGNvbiB2YWxvcmVzIHBhcmEgZXN0YSBjb2x1bW5hLCBwb3IgbG8gcXVlIHNlIGNyZWEgbGEgY2xhc2UgIk5vIEFwbGljYSIuDQoNCiogKipJbnNlcnRhciBOQXM6KiogRGViaWRvIGEgbGEgbmF0dXJhbGV6YSBkZWwgcmVzdG8gZGUgdmFyaWFibGVzIGNvbnNpZGVyYWRhcywgdW5hIGZhbHRhIGVuIHN1cyByZWdpc3Ryb3Mgbm8gcHVlZGUgaW1wdXRhcnNlIGRpcmVjdGFtZW50ZS4gQW50ZSBlc3RvLCBkaXJlY3RhbWVudGUgZnVlIGluc2VydGFkbyBlbCB2YWxvciAiTmEiLiBFc3RhIG1lZGlkYSBubyB0ZW5kcsOhIHVuIGVmZWN0byBwZXJqdWRpY2lhbCBzb2JyZSBsb3Mgbml2ZWxlcyBkZSBsYXMgdmFyaWFibGVzLCBwdWVzIGxvcyBjYXNvcyBlbiBsb3MgcXVlIGFwbGljYSBjb25zaWRlcmFuIGluZm9ybWFjacOzbiBtdXkgYW1wbGlhIHkgZXNwZWPDrWZpY2EsIHBvciBsbyBxdWUgYWwgbm8gc2VyIGRlIHV0aWxpZGFkLCBzZXLDoW4gZGVzY2FydGFkYXMgZW4gZWwgcHJvY2VzbyBkZSBzZWxlY2Npw7NuIGRlIHZhcmlhYmxlcyBwYXJhIGxhIGVzcGVjaWZpY2FjacOzbiBkZSBsb3MgbW9kZWxvcyBtw6FzIGFkZWxhbnRlLg0KDQpgYGB7cn0NCiMgSWRlbnRpZmljYXIgdmFyaWFibGVzIGNvbiB2YWxvcmVzIG51bG9zDQptaXNzaW5nX3ZhbHVlcyA8LSBzYXBwbHkocmhfYWx0LCBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQ0KbWlzc2luZ192YWx1ZXMNCg0KIyBJbXB1dGFyL1JlZW1wbGF6YXIgdmFsb3JlcyBudWxvcw0KDQojIyBEZXBhcnRhbWVudG8NCnJoX2FsdCA8LSByaF9hbHQgJT4lDQogIG11dGF0ZShEZXBhcnRhbWVudG8gPSBpZmVsc2UoaXMubmEoRGVwYXJ0YW1lbnRvKSwgIkFyZWEgTm8gRGVmaW5pZGEiLCBEZXBhcnRhbWVudG8pKQ0KDQojIyBNb3Rpdm9SZW51bmNpYSANCnJoX2FsdCRNb3Rpdm9SZW51bmNpYVtyaF9hbHQkTW90aXZvUmVudW5jaWEgPT0gIk4vQSJdIDwtICJObyBhcGxpY2EiDQpyaF9hbHQkTW90aXZvUmVudW5jaWEgPC0gaWZlbHNlKGlzLm5hKHJoX2FsdCRNb3Rpdm9SZW51bmNpYSksICJObyBhcGxpY2EiLCByaF9hbHQkTW90aXZvUmVudW5jaWEpDQoNCiMjIERldGFsbGVSZW51bmNpYQ0KcmhfYWx0JERldGFsbGVSZW51bmNpYVtyaF9hbHQkRGV0YWxsZVJlbnVuY2lhID09ICJOL0EiXSA8LSAiTm8gYXBsaWNhIg0KcmhfYWx0JERldGFsbGVSZW51bmNpYSA8LSBpZmVsc2UoaXMubmEocmhfYWx0JERldGFsbGVSZW51bmNpYSksICJObyBhcGxpY2EiLCByaF9hbHQkRGV0YWxsZVJlbnVuY2lhKQ0KDQoNCg0KIyBJbnNlcnRhciBOYXMgDQoNCiMjIEZhY3RfSW5mb25hdml0DQpyaF9hbHQkRmFjdF9JbmZvbmF2aXRbcmhfYWx0JEZhY3RfSW5mb25hdml0ID09ICJDVU9UQSBGSUpBIEVOIFZTTSJdIDwtICJOYSINCnJoX2FsdCRGYWN0X0luZm9uYXZpdFtyaF9hbHQkRmFjdF9JbmZvbmF2aXQgPT0gIkNVT1RBIEZJSkEiXSA8LSAiTmEiDQpyaF9hbHQkRmFjdF9JbmZvbmF2aXRbcmhfYWx0JEZhY3RfSW5mb25hdml0ID09ICIgIl0gPC0gIk5hIg0KcmhfYWx0JEZhY3RfSW5mb25hdml0W3JoX2FsdCRGYWN0X0luZm9uYXZpdCA9PSAiTi9BIl0gPC0gIk5hIg0KcmhfYWx0JEZhY3RfSW5mb25hdml0W3JoX2FsdCRGYWN0X0luZm9uYXZpdCA9PSAiTWVuc3VhbCJdIDwtICJOYSINCnJoX2FsdCRGYWN0X0luZm9uYXZpdFtyaF9hbHQkRmFjdF9JbmZvbmF2aXQgPT0gIlBlc29zIl0gPC0gIk5hIg0KcmhfYWx0JEZhY3RfSW5mb25hdml0W3JoX2FsdCRGYWN0X0luZm9uYXZpdCA9PSBOVUxMXSA8LSAiTmEiDQpyaF9hbHQkRmFjdF9JbmZvbmF2aXQgPC0gaWZlbHNlKGlzLm5hKHJoX2FsdCRGYWN0X0luZm9uYXZpdCksICJOYSIsIHJoX2FsdCRGYWN0X0luZm9uYXZpdCkNCg0KIyMgTm9fSW50ZXJubw0KcmhfYWx0JE5vX0ludGVybm9bcmhfYWx0JE5vX0ludGVybm8gPT0gIk4vQSJdIDwtICJOYSINCnJoX2FsdCROb19JbnRlcm5vIDwtIGlmZWxzZShpcy5uYShyaF9hbHQkTm9fSW50ZXJubyksICJOYSIsIHJoX2FsdCROb19JbnRlcm5vKQ0KDQojIyBOb19DcmVkaXRvSW5mb25hdml0DQpyaF9hbHQkTm9fQ3JlZGl0b0luZm9uYXZpdFtyaF9hbHQkTm9fQ3JlZGl0b0luZm9uYXZpdCA9PSAiICJdIDwtICJOYSINCnJoX2FsdCROb19DcmVkaXRvSW5mb25hdml0W3JoX2FsdCROb19DcmVkaXRvSW5mb25hdml0ID09ICJOL0EiXSA8LSAiTmEiDQpyaF9hbHQkTm9fQ3JlZGl0b0luZm9uYXZpdFtyaF9hbHQkTm9fQ3JlZGl0b0luZm9uYXZpdCA9PSBOVUxMXSA8LSAiTmEiDQpyaF9hbHQkTm9fQ3JlZGl0b0luZm9uYXZpdFtyaF9hbHQkTm9fQ3JlZGl0b0luZm9uYXZpdCA9PSAiTi9BIl0gPC0gIk5hIg0KcmhfYWx0JE5vX0NyZWRpdG9JbmZvbmF2aXQgPC0gaWZlbHNlKGlzLm5hKHJoX2FsdCROb19DcmVkaXRvSW5mb25hdml0KSwgIk5hIiwgcmhfYWx0JE5vX0NyZWRpdG9JbmZvbmF2aXQpDQoNCiMjIEx1Z2FyIGRlIE5hY2ltaWVudG8NCnJoX2FsdCRMdWdhcl9uYWNpbWllbnRvW3JoX2FsdCRMdWdhcl9uYWNpbWllbnRvID09ICJOL0EiXSA8LSAiTmEiDQpyaF9hbHQkTHVnYXJfbmFjaW1pZW50byA8LSBpZmVsc2UoaXMubmEocmhfYWx0JEx1Z2FyX25hY2ltaWVudG8pLCAiTmEiLCByaF9hbHQkTHVnYXJfbmFjaW1pZW50bykNCg0KYGBgDQoNCiMjIFJlZW1wbGF6byBkZSB2YWxvcmVzIGVycsOzbmVvcyB5IGVzdGFuZGFyaXphY2nDs24NCg0KQ29uIGxhIGZpbmFsaWRhZCBkZSBlc3RhYmxlY2VyIHVuYSBlc2NyaXR1cmEgZXN0YW5kYXJpemFkYSwgc2UgZGVmaW5pZXJvbiBmdW5jaW9uZXMgcXVlIHBlcm1pdGlyw6FuIGRpc3BvbmVyIGRlIHZhbG9yZXMgc2luIGFjZW50b3MgeSBjb24gbGV0cmFzIG1pbsO6c2N1bGFzIChwYXJhIGFsZ3Vub3MgY2Fzb3MpLCBndWlhbmRvIGEgdW4gbWVqb3IgcHJvY2VzYW1pZW50byBkZSBsb3MgZGF0b3MgZW4gc3UgbGVjdHVyYSBwYXJhIGxhIGNyZWFjacOzbiBkZSBtb2RlbG9zLg0KDQpgYGB7cn0NCiMgRGVmaW5pciBmdW5jaW9uZXMgcGFyYSBlc3RhbmRhcml6YXIgY29sdW1uYXMgYSBtaW7DunNjdWxhcyBzaW4gYWNlbnRvcw0KY29udmVydGlyX2FjZW50b3MgPC0gZnVuY3Rpb24oeCkgew0KICB4IDwtIGNoYXJ0cigiw6HDqcOtw7PDusO8w7HDgcOJw43Dk8Oaw5zDkSIsICJhZWlvdXVuQUVJT1VVTiIsIHgpDQogIHgNCn0NCg0KY29udmVydGlyX2FjZW50b3MxIDwtIGZ1bmN0aW9uKHgpIHsNCiAgeCA8LSB0b2xvd2VyKGljb252KHgsIGZyb20gPSAibGF0aW4xIiwgdG8gPSAiVVRGLTgiKSkNCiAgeCA8LSBjaGFydHIoIsOhw6nDrcOzw7rDvMOxw4HDicONw5PDmsOcw5EiLCAiYWVpb3V1bkFFSU9VVU4iLCB4KQ0KICB4DQp9DQoNCg0KY29udmVydGlyX2FjZW50b3MyIDwtIGZ1bmN0aW9uKHgpIHsNCiAgeCA8LSB0b2xvd2VyKGljb252KHgsIGZyb20gPSAibGF0aW4xIiwgdG8gPSAiVVRGLTgiKSkNCiAgeCA8LSBjaGFydHIoIsOhw6nDrcOzw7rDvMOxw4HDicONw5PDmsOcw5EiLCAiYWVpb3V1bkFFSU9VVU4iLCB4KQ0KICB4DQp9DQoNCmNvbnZlcnRpcl9hY2VudG9zMyA8LSBmdW5jdGlvbih4KSB7DQogIHggPC0gdG9sb3dlcihpY29udih4LCBmcm9tID0gImxhdGluMSIsIHRvID0gIlVURi04IikpDQogIHggPC0gY2hhcnRyKCLDocOpw63Ds8O6w7zDscOBw4nDjcOTw5rDnMORIiwgImFlaW91dW5BRUlPVVVOIiwgeCkNCiAgeA0KfQ0KDQojIFBhc2FyIG5vbWJyZXMgeSBhcGVsbGlkb3MgYSBtaW7DunNjdWxhcw0KcmhfYWx0IDwtIHJoX2FsdCAlPiUNCiAgbXV0YXRlKE5vbWJyZSA9IHRvbG93ZXIoaWNvbnYoTm9tYnJlLCBmcm9tID0gImxhdGluMSIsIHRvID0gIlVURi04IikpLA0KICAgICAgICAgQXBlbGxpZG9zID0gdG9sb3dlcihpY29udihBcGVsbGlkb3MsIGZyb20gPSAibGF0aW4xIiwgdG8gPSAiVVRGLTgiKSkpDQoNCiMgQXBsaWNhciBsYXMgZnVuY2lvbmVzDQpyaF9hbHQkTm9tYnJlICAgICAgICAgIDwtIGNvbnZlcnRpcl9hY2VudG9zKHJoX2FsdCROb21icmUpDQpyaF9hbHQkQXBlbGxpZG9zICAgICAgIDwtIGNvbnZlcnRpcl9hY2VudG9zKHJoX2FsdCRBcGVsbGlkb3MpDQpyaF9hbHQkUHVlc3RvICAgICAgICAgIDwtIGNvbnZlcnRpcl9hY2VudG9zMShyaF9hbHQkUHVlc3RvKQ0KcmhfYWx0JE1vdGl2b1JlbnVuY2lhICA8LSBjb252ZXJ0aXJfYWNlbnRvczIocmhfYWx0JE1vdGl2b1JlbnVuY2lhKQ0KcmhfYWx0JERldGFsbGVSZW51bmNpYSA8LSBjb252ZXJ0aXJfYWNlbnRvczIocmhfYWx0JERldGFsbGVSZW51bmNpYSkNCnJoX2FsdCREZXBhcnRhbWVudG8gICAgPC0gY29udmVydGlyX2FjZW50b3MzKHJoX2FsdCREZXBhcnRhbWVudG8pDQpgYGANCg0KDQoNCiMjIyBIb21vbG9nYWNpw7NuIGRlIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMNCg0KRWwgcHJvY2VzbyBkZSBob21vbG9nYWNpw7NuIGNvbnNpZGVyYSBlbCBkZWZpbmlyIHVuYSDDum5pY2EgZm9ybWEgZGUgZXNjcmliaXIgbG9zIG5vbWJyZXMgZGUgbGFzIGNsYXNlcyBvIG5pdmVsZXMgZW4gdW5hIHZhcmlhYmxlIGFsIGRldGVjdGFyIHF1ZSBlc3RhIGhhIHNpZG8gZXNjcml0YSBkZSBtw7psdGlwbGVzIGZvcm1hcyBkaWZlcmVudGVzIGVudHJlIHPDrS4gRGViaWRvIGEgZXN0bywgZXMgbmVjZXNhcmlvIHJlY29kaWZpY2FyIGNhZGEgdW5hIGRlIGxhcyB2YXJpYWNpb25lcyB5IGFzw60gZGlzcG9uZXIgZGUgdW5hIGNhbnRpZGFkIHJlYWwgZGUgbml2ZWxlcyBwYXJhIGxhcyB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzLg0KDQojIyMjIENvbHVtbmEgRXN0YWRvIENpdmlsDQpgYGB7cn0NCiMgRXN0YW5kYXJpemFyIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgY29tbyBFc3RhZG9fQ2l2aWwsIFB1ZXN0bywgRGVwYXJ0YW1lbnRvLCBldGMuDQpyaF9hbHQkRXN0YWRvX0NpdmlsIDwtIHJlY29kZShyaF9hbHQkRXN0YWRvX0NpdmlsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU29sdGVyYSIgPSAiU29sdGVyaWEiLCAiU29sdGVybyIgPSAiU29sdGVyaWEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2FzYWRhIiA9ICJNYXRyaW1vbmlvIiwgImNhc2FkbyIgPSAiTWF0cmltb25pbyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDQVNBREEiID0gIk1hdHJpbW9uaW8iLCAiQ0FTQURPIiA9ICJNYXRyaW1vbmlvIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNhc2FkbyIgPSAiTWF0cmltb25pbyIsICJEaXZvcmNpYWRhIiA9ICJEaXZvcmNpbyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZXBhcmFkYSIgPSAiRGl2b3JjaW8iLCAiRGl2b3JjaWFkbyIgPSAiRGl2b3JjaW8iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVW5pw7NuIExpYnJlIiA9ICJVbmlvbiBMaWJyZSIsICJVbmnDs24gbGlicmUiID0gIlVuaW9uIExpYnJlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVuacOzbiBsaWJyZSIgPSAiVW5pb24gTGlicmUiLCAiVml1ZG8iID0gIlZpdWRleiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJWaXVkYSIgPSAiVml1ZGV6IikNCg0KYGBgDQoNCg0KIyMjIyBDb2x1bW5hcyBEZXBhcnRhbWVudG8gfCBQdWVzdG8NCkFkaWNpb25hbCBhIGxhIHJlY29kaWZpY2FjacOzbiBkZSBsYSB2YXJpYWJsZSBEZXBhcnRhbWVudG8sIHNlIGNyZcOzIGxhIGNvbHVtbmEgIk90cm9zIiwgZXN0YSBjb25zaWRlcmE6IENhamFzLCBDZWxkYXMsIE1hbnRlbmltaWVudG8geSBSb3RhdGl2YS4gRXN0YSBldGlxdWV0YSBmdWUgY3JlYWRhIGRlYmlkbyBhbCBiYWpvIHZvbHVtZW4gZGUgZW1wbGVhZG9zIGRlbnRybyBkZSBsb3MgZGVwYXJ0YW1lbnRvcyBwcmV2aWFtZW50ZSBtZW5jaW9uYWRvcywgYXPDrSBjb21vIHVuYSBzaW1pbGl0dWQgZW50cmUgbGFzIHRhcmVhcyB5IG5pdmVsIG9yZ2FuaXphY2lvbmFsLg0KYGBge3J9DQoNCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiYXl1ZGFudGUgZGUgZW1iYXJxdWVzIl0gICAgICAgICAgIDwtICJBeXVkYW50ZSBkZSBlbWJhcnF1ZXMiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gImF5dWQuIGRlIGVtYmFycXVlcyJdICAgICAgICAgICAgICA8LSAiQXl1ZGFudGUgZGUgZW1iYXJxdWVzIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJhdXhpbGlhciBkZSBlbWJhcnF1ZXMiXSAgICAgICAgICAgPC0gIkF5dWRhbnRlIGRlIGVtYmFycXVlcyINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiYXl1ZGFudGUgZGUgc29sZGFkb3IiXSAgICAgICAgICAgIDwtICJBeXVkYW50ZSBkZSBzb2xkYWRvciINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiYXl1LiBkZSBzb2xkYWRvciJdICAgICAgICAgICAgICAgIDwtICJBeXVkYW50ZSBkZSBzb2xkYWRvciINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiYXkuIGdlbmVyYWwiXSAgICAgICAgICAgICAgICAgICAgIDwtICJBeXVkYW50ZSBnZW5lcmFsIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJBeXVkYW50ZSBnZW5lcmFsIl0gICAgICAgICAgICAgICAgPC0gIkF5dWRhbnRlIGdlbmVyYWwiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gImF5dS4gZGUgcGludG9yIl0gICAgICAgICAgICAgICAgICA8LSAiQXl1ZGFudGUgZGUgcGludG9yIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJBeXVkYW50ZSBEZSBQaW50b3IiXSAgICAgICAgICAgICAgPC0gIkF5dWRhbnRlIGRlIHBpbnRvciINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiYXl1ZGFudGUgZ2VuZXJhbC1jZWRpcyJdICAgICAgICAgIDwtICJBeXVkYW50ZSBnZW5lcmFsIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJheXVkYW50ZSBnZW5lcmFsIGNlZGlzIl0gICAgICAgICAgPC0gIkF5dWRhbnRlIGdlbmVyYWwiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gImF5dWRhbnRlIGdlbmVyYWwiXSAgICAgICAgICAgICAgICA8LSAiQXl1ZGFudGUgZ2VuZXJhbCINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiaW5zcGVjdG9yYSBkZSBjYWxpZGFkIl0gICAgICAgICAgIDwtICJDYWxpZGFkIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJjYWxpZGFkIl0gICAgICAgICAgICAgICAgICAgICAgICAgPC0gIkNhbGlkYWQiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gImluc3BlY3RvciBkZSBjYWxpZGFkIl0gICAgICAgICAgICA8LSAiQ2FsaWRhZCINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiSW5zcGVjdG9yYSBEZSBDYWxpZGFkIl0gICAgICAgICAgIDwtICJDYWxpZGFkIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJNYXRlcmlhbGlzdGEiXSAgICAgICAgICAgICAgICAgICAgPC0gIk1hdGVyaWFsZXMiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gIk9wZXJhZG9yIFNpZXJyYSJdICAgICAgICAgICAgICAgICA8LSAiT3BlcmFkb3IiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gIk9wLiBGbGV4by1SYW51cmFkb3JhLVJlZmlsYWRvcmEiXSA8LSAiT3BlcmFkb3IiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gIm9wLiBmbGV4by1yYW51cmFkb3JhLXJlZmlsYWRvcmEiXSA8LSAiT3BlcmFkb3IiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gIm1hdGVyaWFsaXN0YSJdICAgICAgICAgICAgICAgICAgICA8LSAiTWF0ZXJpYWxlcyINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAibWF0ZXJpYWxlcyJdICAgICAgICAgICAgICAgICAgICAgIDwtICJNYXRlcmlhbGVzIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJjb3N0dXJlcmEiXSAgICAgICAgICAgICAgICAgICAgICAgPC0gIkNvc3R1cmEiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gImNvc3R1cmVybyJdICAgICAgICAgICAgICAgICAgICAgICA8LSAiQ29zdHVyYSINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiY2hvZmVyIGdlc3RvciJdICAgICAgICAgICAgICAgICAgIDwtICJDaG9mZXIiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gIkNob2ZlciBDRURJUyJdICAgICAgICAgICAgICAgICAgICA8LSAiQ2hvZmVyIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJjaG9mZXIiXSAgICAgICAgICAgICAgICAgICAgICAgICAgPC0gIkNob2ZlciINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiZ2VzdG9yIl0gICAgICAgICAgICAgICAgICAgICAgICAgIDwtICJHZXN0b3IiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gInJlc2lkZW50ZSJdICAgICAgICAgICAgICAgICAgICAgICA8LSAiUmVzaWRlbnRlIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJwaW50b3IiXSAgICAgICAgICAgICAgICAgICAgICAgICAgPC0gIlBpbnRvciINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAibGltcGllemEiXSAgICAgICAgICAgICAgICAgICAgICAgIDwtICJMaW1waWV6YSINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiZW5mZXJtZXJhIl0gICAgICAgICAgICAgICAgICAgICAgIDwtICJFbmZlcm1lcmEiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gInNvbGRhZG9yIl0gICAgICAgICAgICAgICAgICAgICAgICA8LSAiU29sZGFkb3IiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gIm1vem8iXSAgICAgICAgICAgICAgICAgICAgICAgICAgICA8LSAiQXl1ZGFudGUgZ2VuZXJhbCINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiYWxtYWNlbmlzdGEiXSAgICAgICAgICAgICAgICAgICAgIDwtICJBbG1hY2VuaXN0YSINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAiZ3VhcmRpYSBkZSBzZWd1cmlkYWQiXSAgICAgICAgICAgIDwtICJHdWFyZGlhIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJzdXBlcnZpc29yIGRlIG1hcXVpbmEiXSAgICAgICAgICAgPC0gIlN1cGVydmlzb3IiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gInN1cGVydmlzb3IgZGUgcGVnYWRvIl0gICAgICAgICAgICA8LSAiU3VwZXJ2aXNvciINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAic3VwZXJ2aXNvcmEiXSAgICAgICAgICAgICAgICAgICAgIDwtICJTdXBlcnZpc29yIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJzdXBlcnZpc29yIGRlIG1hcXVpbmFzIl0gICAgICAgICAgPC0gIlN1cGVydmlzb3IiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gIm1hcmNhZG9yYSJdICAgICAgICAgICAgICAgICAgICAgICA8LSAiTWFyY2Fkb3JhIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJjaG9mZXIgY2VkaXMiXSAgICAgICAgICAgICAgICAgICAgPC0gIkNob2ZlciINCnJoX2FsdCRQdWVzdG9bcmhfYWx0JFB1ZXN0byA9PSAicmVzaWRlbnRlIHN0YWJpbHVzIl0gICAgICAgICAgICAgIDwtICJSZXNpZGVudGUiDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gInJlc2lkZW50ZSB5ZmNmIl0gICAgICAgICAgICAgICAgICA8LSAiUmVzaWRlbnRlIg0KcmhfYWx0JFB1ZXN0b1tyaF9hbHQkUHVlc3RvID09ICJheXVkYW50ZSBkZSBtYW50ZW5pbWllbnRvIl0gICAgICAgPC0gIkF5dWRhbnRlIGRlIG1hbnRlbmltaWVudG8iDQpyaF9hbHQkUHVlc3RvW3JoX2FsdCRQdWVzdG8gPT0gIm1vbnRhY2FyZ3Vpc3RhIl0gICAgICAgICAgICAgICAgICA8LSAiTW9udGFjYXJndWlzdGEiDQoNCg0KICMgUmVjb2RpZmljYWNpw7NuIGNvbHVtbmEgRGVwYXJ0YW1lbnRvDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gIkNlZGlzIl0gICAgICAgICAgICAgICAgICAgIDwtICJDRURJUyINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiY2VkaXMiXSAgICAgICAgICAgICAgICAgICAgPC0gIkNFRElTIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJjYWxpZGFkIl0gICAgICAgICAgICAgICAgICA8LSAiQ2FsaWRhZCINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiY2FqYXMiXSAgICAgICAgICAgICAgICAgICAgPC0gIk90cm9zIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJjb3J0YWRvcmFzIl0gICAgICAgICAgICAgICA8LSAiQ29ydGFkb3JhcyINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiY29zdHVyYSJdICAgICAgICAgICAgICAgICAgPC0gIkNvc3R1cmEiDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gImNlbGRhcyJdICAgICAgICAgICAgICAgICAgIDwtICJPdHJvcyINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiZW1iYXJxdWVzIl0gICAgICAgICAgICAgICAgPC0gIkVtYmFycXVlcyINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiRWhzIl0gICAgICAgICAgICAgICAgICAgICAgPC0gIkVIUyINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiZWhzIl0gICAgICAgICAgICAgICAgICAgICAgPC0gIkVIUyINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAibWFudGVuaW1pZXRvIl0gICAgICAgICAgICAgPC0gIk90cm9zIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJtYW50ZW5pbWllbnRvIl0gICAgICAgICAgICA8LSAiT3Ryb3MiDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gIm1hdGVyaWFsZXMiXSAgICAgICAgICAgICAgIDwtICJNYXRlcmlhbGVzIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJwYWlsZXJpYSJdICAgICAgICAgICAgICAgICA8LSAiUGFpbGVyaWEgeSBQaW50dXJhIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJwYWlsZXJpYSB5IHBpbnR1cmEiXSAgICAgICA8LSAiUGFpbGVyaWEgeSBQaW50dXJhIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJyb3RhdGl2YSJdICAgICAgICAgICAgICAgICA8LSAiT3Ryb3MiDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gInN0YWJpbHVzIl0gICAgICAgICAgICAgICAgIDwtICJTdGFiaWx1cyINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiUHJvZHVjY2lvbiBDYXJ0b24gTUMiXSAgICAgPC0gIlByb2R1Y2Npb24gQ2FydG9uIE1DIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJwcm9kdWNjaW9uIGNhcnRvbiBtYyJdICAgICA8LSAiUHJvZHVjY2lvbiBDYXJ0b24gTUMiDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gIlByb2R1Y2Npb24gQ2FydMOzbiBNQyJdICAgICA8LSAiUHJvZHVjY2lvbiBDYXJ0b24gTUMiDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gIlByb2R1Y2Npb24gQ2FydFx4ZjNuIE1DIl0gIDwtICJQcm9kdWNjaW9uIENhcnRvbiBNQyINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAicHJvZHVjY2lvbiBjYXJ0b24gbWRsIl0gICAgPC0gIlByb2R1Y2Npb24gQ2FydG9uIE1ETCINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiUHJvZHVjY2lvbiBDYXJ0XHhmM24gTURMIl0gPC0gIlByb2R1Y2Npb24gQ2FydG9uIE1ETCINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiUHJvZHVjY2lvbiBDYXJ0b24gTURMIl0gICAgPC0gIlByb2R1Y2Npb24gQ2FydG9uIE1ETCINCnJoX2FsdCREZXBhcnRhbWVudG9bcmhfYWx0JERlcGFydGFtZW50byA9PSAiUHJvZHVjY2lvbiBDYXJ0w7NuIE1kbCJdICAgIDwtICJQcm9kdWNjaW9uIENhcnRvbiBNREwiDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gIlByb2R1Y2Npw7NuIFJldG9ybiJdICAgICAgICA8LSAiUHJvZHVjY2lvbiBSZXRvcm5hYmxlIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJwcm9kdWNjaW9uIHJldG9ybmFibGUiXSAgICA8LSAiUHJvZHVjY2lvbiBSZXRvcm5hYmxlIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJwcm9kdWNjaW9uIGNhcnTDo8KzbiBtZGwiXSAgIDwtICJQcm9kdWNjaW9uIENhcnRvbiBNREwiDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gInByb2R1Y2Npb24gY2FydMOjwrNuIG1jIl0gICAgPC0gIlByb2R1Y2Npb24gQ2FydG9uIE1DIg0KcmhfYWx0JERlcGFydGFtZW50b1tyaF9hbHQkRGVwYXJ0YW1lbnRvID09ICJwcm9kdWNjacOjwrNuIHJldG9ybmFibGUiXSAgIDwtICJQcm9kdWNjaW9uIFJldG9ybmFibGUiDQpyaF9hbHQkRGVwYXJ0YW1lbnRvW3JoX2FsdCREZXBhcnRhbWVudG8gPT0gInByb2R1Y2Npw6PCs24gY2FydMOjwrNuIG1jIl0gICA8LSAiUHJvZHVjY2lvbiBDYXJ0b24gTUMiDQpgYGANCg0KIyMjIyBDb2x1bW5hcyBNdW5pY2lwaW8gfCBFc3RhZG8gDQpgYGB7cn0NCiAjIFJlY29kaWZpY2FjacOzbiBjb2x1bW5hIE11bmljaXBpbw0KcmhfYWx0JE11bmljaXBpb1tyaF9hbHQkTXVuaWNpcGlvID09ICJSYW1veiBBcml6cGUiXSAgICAgICAgIDwtICJSYW1vcyBBcml6cGUiDQpyaF9hbHQkTXVuaWNpcGlvW3JoX2FsdCRNdW5pY2lwaW8gPT0gIlJBTU9TIEFSSVpQRSJdICAgICAgICAgPC0gIlJhbW9zIEFyaXpwZSINCnJoX2FsdCRNdW5pY2lwaW9bcmhfYWx0JE11bmljaXBpbyA9PSAiUEVTUVVFUklBIl0gICAgICAgICAgICA8LSAiUGVzcXVlcmlhIg0KcmhfYWx0JE11bmljaXBpb1tyaF9hbHQkTXVuaWNpcGlvID09ICJNT05URVJSRVkiXSAgICAgICAgICAgIDwtICJNb250ZXJyZXkiDQpyaF9hbHQkTXVuaWNpcGlvW3JoX2FsdCRNdW5pY2lwaW8gPT0gIkpVQVJFWiJdICAgICAgICAgICAgICAgPC0gIkp1YXJleiINCnJoX2FsdCRNdW5pY2lwaW9bcmhfYWx0JE11bmljaXBpbyA9PSAiR1VBREFMVVBFIl0gICAgICAgICAgICA8LSAiR3VhZGFsdXBlIg0KcmhfYWx0JE11bmljaXBpb1tyaF9hbHQkTXVuaWNpcGlvID09ICJDQcORQURBIEJMQU5DQSJdICAgICAgICA8LSAiQ2HDsWFkYSBCbGFuY2EiDQpyaF9hbHQkTXVuaWNpcGlvW3JoX2FsdCRNdW5pY2lwaW8gPT0gIkFQT0RBQ0EiXSAgICAgICAgICAgICAgPC0gIkFwb2RhY2EiDQpyaF9hbHQkTXVuaWNpcGlvW3JoX2FsdCRNdW5pY2lwaW8gPT0gIlNBTiBOSUNPTEFTIERFIExPUyBHIl0gPC0gIlNhbiBOaWNvbGFzIGRlIGxvcyBHYXJ6YSINCnJoX2FsdCRNdW5pY2lwaW9bcmhfYWx0JE11bmljaXBpbyA9PSAiU2FuIE5pY29sYXMgRGUgTG9zIEciXSA8LSAiU2FuIE5pY29sYXMgZGUgbG9zIEdhcnphIg0KcmhfYWx0JE11bmljaXBpb1tyaF9hbHQkTXVuaWNpcGlvID09ICJTYW4gTmljb2xhcyJdICAgICAgICAgIDwtICJTYW4gTmljb2xhcyBkZSBsb3MgR2FyemEiDQpyaF9hbHQkTXVuaWNpcGlvW3JoX2FsdCRNdW5pY2lwaW8gPT0gIkNpZW5lZ2EgZGUgRmxvcmVzIl0gICAgPC0gIkNpZW5lZ2EgZGUgRmxvcmVzIg0KcmhfYWx0JE11bmljaXBpb1tyaF9hbHQkTXVuaWNpcGlvID09ICJDaWVuZWdhIERlIEZsb3JlcyJdICAgIDwtICJDaWVuZWdhIGRlIEZsb3JlcyINCnJoX2FsdCRNdW5pY2lwaW9bcmhfYWx0JE11bmljaXBpbyA9PSAiQ2nDqW5lZ2EgZGUgRmxvcmVzIl0gICAgPC0gIkNpZW5lZ2EgZGUgRmxvcmVzIg0KcmhfYWx0JE11bmljaXBpb1tyaF9hbHQkTXVuaWNpcGlvID09ICJTQUxUSUxMTyJdICAgICAgICAgICAgIDwtICJTYWx0aWxsbyINCg0KIyBSZWNvZGlmaWNhY2nDs24gY29sdW1uYSBFc3RhZG8NCnJoX2FsdCRFc3RhZG9bcmhfYWx0JEVzdGFkbyA9PSAiTnVldm8gTGXDs24iXSAgICA8LSAiTnVldm8gTGVvbiINCnJoX2FsdCRFc3RhZG9bcmhfYWx0JEVzdGFkbyA9PSAiTnVldm8gTGVceGYzbiJdIDwtICJOdWV2byBMZW9uIg0KcmhfYWx0JEVzdGFkb1tyaF9hbHQkRXN0YWRvID09ICJDT0FIVUlMQSJdICAgICAgPC0gIkNvYWh1aWxhIg0KDQpgYGANCg0KIyMjIyBDb2x1bW5hIEZhY3RfSW5mb25hdml0DQpgYGB7cn0NCg0KICMgUmVjb2RpZmljYWNpw7NuIGNvbHVtbmEgRmFjdF9JbmZvbmF2aXQNCnJoX2FsdCRGYWN0X0luZm9uYXZpdFtyaF9hbHQkRmFjdF9JbmZvbmF2aXQgPT0gIkNVT1RBIEZJSkEgRU4gVlNNIDU2LjExOCJdIDwtIDU2LjExDQpyaF9hbHQkRmFjdF9JbmZvbmF2aXRbcmhfYWx0JEZhY3RfSW5mb25hdml0ID09ICIzMC43MzQ0IFZTTSJdICAgICAgICAgICAgICA8LSAzMC43Mw0KcmhfYWx0JEZhY3RfSW5mb25hdml0W3JoX2FsdCRGYWN0X0luZm9uYXZpdCA9PSAiQ0YgRU4gVlNNIDIxLjMxMCJdICAgICAgICAgPC0gMjEuMzENCnJoX2FsdCRGYWN0X0luZm9uYXZpdFtyaF9hbHQkRmFjdF9JbmZvbmF2aXQgPT0gIkZEIDEzLjI1NDQiXSAgICAgICAgICAgICAgIDwtIDEzLjI1DQoNCmBgYA0KDQoNCiMjIyBTw61tYm9sb3MgeSBtYXnDunN1Y3VsYXMNCg0KDQojIyAqKkdlbmVyYWNpw7NuIGRlIG51ZXZhcyB2YXJpYWJsZXMqKg0KDQpVbmEgdmV6IGNvcnJlZ2lkYXMgbGFzIHZhcmlhYmxlcyBkaXNwb25pYmxlcyBlbiBsYSBiYXNlIGRlIGRhdG9zLCBlcyBuZWNlc2FyaW8gZGVmaW5pciBudWV2YXMgcG9zaWJsZXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIHF1ZSBwZXJtaXRhbiBhcG9ydGFyIG1heW9yIGV4cGxpY2FiaWxpZGFkIGEgbGEgdmFyaWFibGUgZGUgaW50ZXLDqXMgKFJlbnVuY2lhKS4gQW50ZSBlc3RvLCBzZSB1dGlsaXphcm9uIGxhcyB2YXJpYWJsZXMgZGUgZmVjaGFzLCBsYXMgY3VhbGVzIHBhc2Fyb24gcG9yIHVuYSB0cmFuc2Zvcm1hY2nDs24gYSBjb2x1bW5hIHRpcG8gZmVjaGEsIHBhcmEgZGVmaW5pciBkb3MgbnVldmFzIHZhcmlhYmxlczoNCg0KKiAqKkVkYWQ6KiogTGEgZWRhZCBkZSBsb3MgZW1wbGVhZG9zIHB1ZWRlIHNlciB1bmEgdmFyaWFibGUgZGUgaW1wYWN0byBlbiBzdSBjcml0ZXJpbyBwYXJhIGNvbnRpbnVhciBvIG5vIHRyYWJhamFuZG8gZW4gdW5hIGVtcHJlc2EsIGNvbnNpZGVyYW5kbyBsYXMgcG9zaWJsZXMgaW1wbGljYWNpb25lcyBmw61zaWNhcyB5IHNvY2lhbGVzIGRlIGNvbnRhciB1bmEgbWVub3IgbyBtYXlvciBlZGFkLiBMYSBjb2x1bW5hIGZ1ZSBjcmVhZGEgdG9tYW5kbyBlbiBjdWVudGEgZWwgZXNjZW5hcmlvIGRlIGVtcGxlYWRvcyBkZSBiYWphIHkgbG9zIHF1ZSBjb250aW7DumFuIGxhYm9yYW5kby4gRXN0byBzZSBkZXRlcm1pbm8gZW4gY2FzbyBkZSB0ZW5lciBvIG5vIHZhbG9yZXMgbnVsb3MgZW4gbGEgY29sdW1uYSBkZSBmZWNoYSBkZSBiYWphLCBzaWVuZG8gZXN0YSBsYSByZXN0YWRhIGEgc3UgZmVjaGEgZGUgbmFjaW1pZW50byBwYXJhIGxvcyBlbXBsZWFkb3MgcGFyYSBjb25vY2VyIGxhIGVkYWQgYSBsYSBxdWUgZGVjaWRlbiByZW51bmNpYXIuIFBvciBvdHJvIGxhZG8sIHBhcmEgbG9zIGVtcGxlYWRvcyBxdWUgZGVjaWRlbiBjb250aW51YXIsIHN1IGVkYWQgZXMgY2FsY3VsYWRhIHJlc3RhbmRvIGxhIGZlY2hhIGFsIGTDrWEgYWN0dWFsIGEgc3UgZmVjaGEgZGUgbmFjaW1pZW50bywgc2llbmRvIGVzdGEgY29uc2lkZXJhZGEgY29tbyB1bmEgcG9zaWJsZSBlZGFkIHBhcmEgcXVlIGxvcyBlbXBsZWFkb3Mgb3B0ZW4gcG9yIGNvbnRpbnVhciBlbiBsYSBlbXByZXNhLg0KDQoqICoqTm9tYnJlTWVzOioqIEVsIE1lcyBlbiBlbCBxdWUgaW5pY2lhcm9uIGFjdGl2aWRhZGVzIGxhYm9yYWxlcyBlbiBGb3JtIHB1ZWRlIHNlciB1biBmYWN0b3IgZGVjaXNpdm8gcGFyYSBxdWUgdW4gZW1wbGVhZG8gb3B0ZSBwb3IgcGVybWFuZWNlciBvIG5vIGVuIGxhIGVtcHJlc2EsIGNvbnNpZGVyYW5kbyBsYSBwcmltZXJhIGltcHJlc2nDs24gZGUgbGFzIGFjdGl2aWRhZGVzIGRlbCBkw61hIGEgZMOtYSBxdWUgZGViZXLDoSBlc3RhciByZWFsaXphbmRvLiBFc3RhIHZhcmlhYmxlIGZ1ZSBjcmVhZGEgYWwgZXh0cmFlciBlbCBtZXMgZGUgbGEgY29sdW1uYSBGZWNoYSBBbHRhLg0KDQoNCmBgYHtyfQ0KIyBDb252ZXJ0aXIgZmVjaGFzIGVuIGZvcm1hdG8gZGUgZmVjaGENCnJoX2FsdCRGZWNoYV9OYWNpbWllbnRvPC1hcy5EYXRlKHJoX2FsdCRGZWNoYV9OYWNpbWllbnRvLGZvcm1hdD0iJW0vJWQvJVkiKSANCnJoX2FsdCRGZWNoYV9BbHRhICAgICAgPC1hcy5EYXRlKHJoX2FsdCRGZWNoYV9BbHRhLGZvcm1hdD0iJW0vJWQvJVkiKSANCnJoX2FsdCRQcmltZXJfbWVzICAgICAgPC1hcy5EYXRlKHJoX2FsdCRQcmltZXJfbWVzLGZvcm1hdD0iJW0vJWQvJVkiKSANCnJoX2FsdCRDdWFydG9fbWVzICAgICAgPC1hcy5EYXRlKHJoX2FsdCRDdWFydG9fbWVzLGZvcm1hdD0iJW0vJWQvJVkiKSANCnJoX2FsdCRCYWphICAgICAgICAgICAgPC1hcy5EYXRlKHJoX2FsdCRCYWphLGZvcm1hdD0iJW0vJWQvJVkiKSANCnJoX2FsdCA8LSByaF9hbHQgJT4lDQogIG11dGF0ZShGZWNoYV9Ib3kgPSBTeXMuRGF0ZSgpKQ0KcmhfYWx0JEZlY2hhX0hveSAgICAgICA8LWFzLkRhdGUocmhfYWx0JEZlY2hhX0hveSxmb3JtYXQ9IiVtLyVkLyVZIikgDQoNCiMgQ3JlYXIgY29sdW1uYSBlZGFkDQpyaF9hbHQgPC0gcmhfYWx0ICU+JQ0KICBtdXRhdGUoDQogICAgRWRhZCA9IGlmZWxzZShpcy5uYShCYWphKSwgDQogICAgICAgICAgICAgICAgICBmbG9vcihhcy5udW1lcmljKGRpZmZ0aW1lKEZlY2hhX0hveSwgRmVjaGFfTmFjaW1pZW50bywgdW5pdHMgPSAiZGF5cyIpKSAvIDM2NSksDQogICAgICAgICAgICAgICAgICBmbG9vcihhcy5udW1lcmljKGRpZmZ0aW1lKEJhamEsIEZlY2hhX05hY2ltaWVudG8sIHVuaXRzID0gImRheXMiKSkgLyAzNjUpKQ0KICApDQoNCiMgQ3JlYXIgY29sdW1uYSBOb21icmVNZXMNCnJoX2FsdCROb21icmVNZXMgPC0gZm9ybWF0KHJoX2FsdCRGZWNoYV9BbHRhLCAiJUIiKQ0KYGBgDQoNCiMjICoqTWFuZWpvIGRlIE91dGxpZXJzKioNCg0KRGVudHJvIGRlIGxhcyBjb2x1bW5hcyBkaXNwb25pYmxlcyBvcmlnaW5hbG1lbnRlIGVuIGxhIGJhc2UgZGUgZGF0b3MsIHNlIGRldGVjdGFyb24gb3V0bGllcnMgZW4gdW5hIGRlIGVsbGFzLCBzaWVuZG8gZWwgKipTYWxhcmlvIERpYXJpbyoqIGNvbiB1bmEgY2FudGlkYWQgY29uc2lkZXJhYmxlbWVudGUgYWx0YSBhIGRpZmVyZW5jaWEgZGVsIHJlc3RvIGRlIHNhbGFyaW9zLiBBc2ltaXNtbywgdHJhcyBjcmVhciBsYSBjb2x1bW5hICoqRWRhZCoqIHNlIGlkZW50aWZpY2Fyb24gY2FudGlkYWRlcyBtdXkgYmFqYXMsIHNpZW5kbyBpbXBvc2libGUgY29udGFyIGNvbiBlbXBsZWFkb3MgcXVlIG5vIGN1bXBsZW4gY29uIGxhIG1heW9yw61hIGRlIGVkYWQgbGFib3JhbmRvIGVuIGxhIGVtcHJlc2EuIA0KDQpUcmFzIGNvbnNpZGVyYXIgYW1ib3MgY2Fzb3MsIHNlIHJlYWxpesOzIHVuYSAqKmltcHV0YWNpw7NuIGRlIGxhIG1lZGlhbmEqKiwgY29tbyB1bmEgYWx0ZXJuYXRpdmEgbcOhcyByZXByZXNlbnRhdGl2YSBhbnRlIGVsIHNlc2dvIHF1ZSBwdWRpZXJhIGltcGxpY2FyIGVsIHVzYXIgbGEgbWVkaWEgZGUgbG9zIGRhdG9zLiBQYXJhIGVsIGNhc28gZGUgZWRhZCBzZSByZWFsaXphIGxhIGltcHV0YWNpw7NuIGFsIGNvbnRhciBjb24gdW5hIGVkYWQgbWVub3IgYSBsYSBtYXlvcsOtYSBkZSBlZGFkLiBQb3Igb3RybyBsYWRvLCBlbiBlbCBzYWxhcmlvIGRpYXJpbyBzZSBpbXB1dGEgYWwgY29uc2lkZXJhciBhbHRhcyBjYW50aWRhZGVzIHF1ZSBzdXBlcmFuIGxvcyAkNTAwMCBNWE4gZGlhcmlvcy4NCg0KYGBge3J9DQojIFJlZW1wbGF6YXIgb3V0bGllcnMNCiMjIFZhcmlhYmxlIEVkYWQNCm1lZGlhbmFfZWRhZCAgICAgICAgICA8LSBtZWRpYW4ocmhfYWx0JEVkYWQsIG5hLnJtID0gVFJVRSkNCnJoX2FsdCRFZGFkICAgICAgICAgICA8LSBpZmVsc2UocmhfYWx0JEVkYWQgPCAxNywgbWVkaWFuYV9lZGFkLCByaF9hbHQkRWRhZCkNCg0KIyMgVmFyaWFibGUgU2FsYXJpbw0KbWVkaWFuYV9zYWxhcmlvICAgICAgIDwtIG1lZGlhbihyaF9hbHQkU2FsYXJpb19kaWFyaW8sIG5hLnJtID0gVFJVRSkNCnJoX2FsdCRTYWxhcmlvX2RpYXJpbyA8LSBpZmVsc2UocmhfYWx0JFNhbGFyaW9fZGlhcmlvID4gNTAwMCwgbWVkaWFuYV9zYWxhcmlvLCByaF9hbHQkU2FsYXJpb19kaWFyaW8pDQpgYGANCg0KIyMjICoqQ29udmVyc2nDs24gZGUgdmFyaWFibGVzIHkgZWxpbWluYWNpw7NuIGRlIGR1cGxpY2Fkb3MqKg0KDQpGaW5hbG1lbnRlLCBlbCDDumx0aW1vIHRyYXRhbWllbnRvIGRlIGxvcyBkYXRvcyBjb250ZW1wbGEgbGEgY29udmVyc2nDs24gZGUgdmFyaWFibGVzIGNhdGVnw7NyaWNhcyBhIGZhY3RvcmVzLCBhbGluZWFuZG8gYWxndW5hcyBkZSBsYXMgdmFyaWFibGVzIGFsIHByb2Nlc2FtaWVudG8gbmVjZXNhcmlvIHBhcmEgbGEgY3JlYWNpw7NuIGRlIG1vZGVsb3MgZGUgYXByZW5kaXphamUgYXV0b23DoXRpY28uDQoNCkp1bnRvIGEgZXN0bywgbGEgcmV2aXNpw7NuIGZpbmFsIGNvbnNpc3RlIGVuIHVzYXIgdmFyaWFibGVzIHF1ZSBjb250aWVuZW4gZGF0b3MgcHVudHVhbGVzIHkgZXNwZWPDrWZpY29zIHBhcmEgY2FkYSBwZXJzb25hLCBjb21vIHN1IG5vbWJyZSB5IGFwZWxsaWRvIChwcmV2aWFtZW50ZSBlc3RhbmRhcml6YWRvcyBlbiBlc2NyaXR1cmEpIHkgc3UgUkZDLCBwYXJhIGRldGVjdGFyIGVtcGxlYWRvcyByZWdpc3RyYWRvcyBtw7psdGlwbGVzIHZlY2VzLiBFc3RlIHByb2Nlc28gcmV2ZWxhIHF1ZSBubyBleGlzdGVuIGVzdG9zIGNhc29zIGVuIGxhIGJhc2UgZGUgZGF0b3MgcmVzdWx0YW50ZS4NCg0KYGBge3J9DQojIENvbnZlcnRpciB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIGEgZmFjdG9yZXMNCnJoX2FsdCRHZW5lcm8gICAgICAgIDwtYXMuZmFjdG9yKHJoX2FsdCRHZW5lcm8pDQpyaF9hbHQkUHVlc3RvICAgICAgICA8LWFzLmZhY3RvcihyaF9hbHQkUHVlc3RvKQ0KcmhfYWx0JERlcGFydGFtZW50byAgPC1hcy5mYWN0b3IocmhfYWx0JERlcGFydGFtZW50bykNCnJoX2FsdCRNdW5pY2lwaW8gICAgIDwtYXMuZmFjdG9yKHJoX2FsdCRNdW5pY2lwaW8pDQpyaF9hbHQkRXN0YWRvICAgICAgICA8LWFzLmZhY3RvcihyaF9hbHQkRXN0YWRvKQ0KcmhfYWx0JEVzdGFkb19DaXZpbCAgPC1hcy5mYWN0b3IocmhfYWx0JEVzdGFkb19DaXZpbCkNCnJoX2FsdCRTYWxhcmlvX2RpYXJpbzwtYXMubnVtZXJpYyhyaF9hbHQkU2FsYXJpb19kaWFyaW8pDQpyaF9hbHQkUmVudW5jaWEgICAgICA8LWFzLmZhY3RvcihyaF9hbHQkUmVudW5jaWEpDQpyaF9hbHQkTm9tYnJlTWVzICAgICA8LWFzLmZhY3RvcihyaF9hbHQkTm9tYnJlTWVzKQ0KDQojIEVsaW1pbmFyIGR1cGxpY2Fkb3MNCnJoX2FsdCA8LSBkaXN0aW5jdChyaF9hbHQsIE5vbWJyZSwgQXBlbGxpZG9zLCBSRkMsIC5rZWVwX2FsbCA9IFRSVUUpDQpzdW1tYXJ5KHJoX2FsdCkNCmBgYA0KDQojIyMgRXN0cnVjdHVyYSBmaW5hbCBkZSBsb3MgZGF0b3MNCg0KRHVyYW50ZSBlbCBwcm9jZXNvIGRlIGxpbXBpZXphIHkgYW7DoWxpc2lzIGRlIGxvcyBkYXRvcyBkZWwgYXJjaGl2byAiZm9ybV9yaF9kYXRvcy54bHN4Iiwgc2UgaWRlbnRpZmljYXJvbiB5IGNvcnJpZ2llcm9uIG3Dumx0aXBsZXMgaW5jb25zaXN0ZW5jaWFzIHkgZXJyb3JlcyBjb25zaWRlcmFuZG8gZGVzZGUgdmFsb3JlcyBhdMOtcGljb3MgaGFzdGEgb3J0b2dyYWbDrWEgaW5jb3JyZWN0YSwgbG9zIGN1YWxlcyBwb2TDrWFuIGFmZWN0YXIgbGEgY2FsaWRhZCBkZSBsb3MgYW7DoWxpc2lzIHBvc3RlcmlvcmVzLiBVbm8gZGUgbG9zIGhhbGxhemdvcyBtw6FzIHJlbGV2YW50ZXMgZnVlIGxhIHByZXNlbmNpYSBkZSB2YWxvcmVzIG51bG9zIGVuIHZhcmlhcyBjb2x1bW5hcyBjbGF2ZSwgY29tbyAiRmFjdF9JbmZvbmF2aXQiIHkgIkZlY2hhX05hY2ltaWVudG8iLiBFc3RvcyB2YWxvcmVzIHNlIGdlc3Rpb25hcm9uIG1lZGlhbnRlIGxhIGltcHV0YWNpw7NuIGRlIGRhdG9zIGZhbHRhbnRlcyB5IGxhIGVsaW1pbmFjacOzbiBkZSByZWdpc3Ryb3MgaW5jb21wbGV0b3MsIGxvIHF1ZSBwZXJtaXRpw7MgbWVqb3JhciBsYSBpbnRlZ3JpZGFkIHkgY29uc2lzdGVuY2lhIGRlbCBjb25qdW50byBkZSBkYXRvcy4NCg0KQWRlbcOhcywgc2UgcmVhbGl6YXJvbiBjb3JyZWNjaW9uZXMgZW4gbGFzIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMsIGNvbW8gbGEgZXN0YW5kYXJpemFjacOzbiBkZSBsb3Mgbm9tYnJlcyBkZSBlbXBsZWFkb3MgeSBkZXBhcnRhbWVudG9zIHBhcmEgYXNlZ3VyYXIgdW5pZm9ybWlkYWQgZW4gZWwgYW7DoWxpc2lzLiBQb3IgZWplbXBsbywgc2UgY29ycmlnaWVyb24gaW5jb25zaXN0ZW5jaWFzIGVuIGxhIGNvbHVtbmEgIkVzdGFkb19DaXZpbCIsIGRvbmRlIHTDqXJtaW5vcyBjb21vICJTb2x0ZXJhIiB5ICJDYXNhZG8iIHNlIHVuaWZpY2Fyb24gYmFqbyBsYXMgY2F0ZWdvcsOtYXMgIlNvbHRlcmlhIiB5ICJNYXRyaW1vbmlvIiwgcmVzcGVjdGl2YW1lbnRlLiBFc3RlIHBhc28gZnVlIGNydWNpYWwgcGFyYSBldml0YXIgZHVwbGljYWNpb25lcyB5IGVycm9yZXMgZHVyYW50ZSBlbCBhbsOhbGlzaXMgZGUgbGFzIHRlbmRlbmNpYXMgZGVtb2dyw6FmaWNhcyB5IGRlIGVtcGxlby4NCg0KTGEgY3JlYWNpw7NuIGRlIG51ZXZhcyB2YXJpYWJsZXMsIGNvbW8gbGEgIkVkYWQiIGRlIGxvcyBlbXBsZWFkb3MsIHkgbGEgY29ycmVjY2nDs24gZGUgdmFsb3JlcyBhdMOtcGljb3MgZW4gIlNhbGFyaW9fZGlhcmlvIiB5ICJFZGFkIiwgcGVybWl0acOzIG9idGVuZXIgdW5hIHZpc2nDs24gbcOhcyBwcmVjaXNhIHkgYWRlY3VhZGEgZGUgbG9zIGRhdG9zLiBFbiByZXN1bWVuLCBlbCBwcm9jZXNvIGRlIGxpbXBpZXphIHkgZXN0YW5kYXJpemFjacOzbiBubyBzb2xvIG1lam9yw7MgbGEgY2FsaWRhZCBkZWwgY29uanVudG8gZGUgZGF0b3MsIHNpbm8gcXVlIHRhbWJpw6luIHNlbnTDsyBsYXMgYmFzZXMgcGFyYSBhbsOhbGlzaXMgbcOhcyByb2J1c3RvcyB5IGNvbmZpYWJsZXMgZW4gZnV0dXJvcyBlc3R1ZGlvcy4gRXN0ZSBlamVyY2ljaW8gc3VicmF5YSBsYSBpbXBvcnRhbmNpYSBkZSBsYSBsaW1waWV6YSBkZSBkYXRvcyBlbiBlbCBjYW1wbyBkZSBCdXNpbmVzcyBJbnRlbGxpZ2VuY2UsIGRvbmRlIGRlY2lzaW9uZXMgaW5mb3JtYWRhcyB5IGVzdHJhdGVnaWFzIGVmZWN0aXZhcyBkZXBlbmRlbiBkZSBsYSBwcmVjaXNpw7NuIHkgY29uZmlhYmlsaWRhZCBkZSBsb3MgZGF0b3MgYW5hbGl6YWRvcy4NCg0KYGBge3J9DQojIEVzdHJ1Y3R1cmEgZmluYWwgZGUgbG9zIGRhdG9zDQpoZWFkKHJoX2FsdCwgMjApDQpzdW1tYXJ5KHJoX2FsdCkNCmBgYA0KDQoNCiMgTW9kZWxvcyBkZSBDbGFzaWZpY2FjacOzbg0KDQojIyBQcmVwYXJhY2nDs24gZGUgRGF0b3MgcGFyYSBsb3MgTW9kZWxvcw0KDQojIyMgUGFydGljacOzbiBkZWwgQ29uanVudG8gZGUgRGF0b3MNCkluaWNpYWxtZW50ZSwgcGFyYSBsYSBjcmVhY2nDs24gZGUgbG9zIG1vZGVsb3MgZGUgYXByZW5kaXphamUgYXV0b23DoXRpY28gc2UgZGl2aWRpZXJvbiBsb3MgZGF0b3MgZW4gZG9zIGNvbmp1bnRvcywgZW50cmVuYW1pZW50byB5IHBydWViYSwgY29uIHVuYSByZXBhcnRpY2nDs24gODAtMjAsIHRvbWFuZG8gZW4gY3VlbnRhIHF1ZSBlbCA4MCUgZGUgbG9zIGRhdG9zIHNlIGNvbnNpZGVyZW4gY29tbyBlbnRyZW5hbWllbnRvLiBFc3RlIHRhbWHDsW8gZGUgY29uanVudG9zIHNlIHBsYW50ZcOzIGNvbnNpZGVyYW5kbyBsYSBiYWphIGNhbnRpZGFkIGRlIHJlZ2lzdHJvcyBkZSBlbXBsZWFkb3MgZGlzcG9uaWJsZXMsIHNpZW5kbyBuZWNlc2FyaW8gY29uc2VydmFyIHVuYSBhbHRhIGNhbnRpZGFkIGRlIHJlZ2lzdHJvcyBwYXJhIHVuIG1lam9yIGVudHJlbmFtaWVudG8gcG9yIHBhcnRlIGRlbCBtb2RlbG8sIGNvbnNlcnZhbmRvIHVuIDIwJSBwYXJhIHN1IHBvc3RlcmlvciBldmFsdWFjacOzbi4NCg0KKipPYmpldGl2byoqDQoNCkVzdGEgcGFydGljacOzbiBjb25zaWRlcmEgbGEgdmFyaWFibGUgZGUgaW50ZXLDqXMsIHByZXZpYW1lbnRlIGRlZmluaWRhIGNvbW8gUmVudW5jaWEgbyBObyBSZW51bmNpYSwgZXN0byBjb24gZWwgb2JqZXRpdm8gZGUgY29uc3RydWlyIG1vZGVsb3MgY2FwYWNlcyBwcmluY2lwYWxtZW50ZSBkZSAqKmRldGVybWluYXIgbGFzIGN1YWxpZGFkZXMgcXVlIGNvbmR1Y2VuIGEgcXVlIHVuIGVtcGxlYWRvIGRlY2lkYSBtYW50ZW5lcnNlIGVuIEZvcm0qKiwgb3B0aW1pemFuZG8gYXPDrSBsYSBzZWxlY2Npw7NuIGRlIHByb3NwZWN0b3MgZHVyYW50ZSBlbCBwcm9jZXNvIGRlIHJlY2x1dGFtaWVudG8sIHkgZW4gYWxndW5vcyBjYXNvcyBhbCBkaXNlw7FvIGRlIGVzdHJhdGVnaWFzIHBhcmEgYXVtZW50YXIgZXNmdWVyem9zIHNvYnJlIGxvcyBlbXBsZWFkb3MgY29uIG1heW9yIHRlbmRlbmNpYSBhIHJlbnVuY2lhciwgZ3VpYW5kbyBkZSBlc3RhIG1hbmVyYSBhICoqbWVqb3JhciBsb3Mgbml2ZWxlcyBkZSByZXRlbmNpw7NuIGVuIGxhIGVtcHJlc2EqKi4gRW4gY29uc2lkZXJhY2nDs24gZGUgZXN0bywgbGEgY2xhc2UgZGUgcG9zaXRpdmEgbyBkZSBpbnRlcsOpcyBzZXLDrWEgIk5vIiwgZXMgZGVjaXIsIGFxdWVsbG9zIGVtcGxlYWRvcyBxdWUgbm8gcmVudW5jaWFuLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2V0LnNlZWQoMTIzKQ0KDQpzYW1wbGUgPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5ID0gcmhfYWx0JFJlbnVuY2lhLCBwPTAuOCwgbGlzdD1GKQ0KdHJhaW4gIDwtIHJoX2FsdFtzYW1wbGUsIF0NCnRlc3QgICA8LSByaF9hbHRbLXNhbXBsZSwgXQ0KYGBgDQoNCiMjIyBGw7NybXVsYSBwYXJhIGxvcyBtb2RlbG9zDQpQcmltZXJhbWVudGUsIHNlICoqZGVzY2FydGFuIHZhcmlhYmxlcyBpbmFkZWN1YWRhcyoqIHBvciBzdSBleHRlbnNpw7NuIGRlIHZhbG9yZXMgZGlzcG9uaWJsZXMgKENhbGxlLCBOb19JbnRlcm5vLCBDb2xvbmlhKSwgcmVkdW5kYW5jaWEgKFViaWNhY2nDs24gR2VvZ3LDoWZpY2EsIEVqOiBFc3RhZG8tTXVuaWNpcGlvKSwgbyBpbmZvcm1hY2nDs24gaXJyZWxldmFudGUgKERhdG9zIFBlcnNvbmFsZXMgeSBmaXNjYWxlczogbm9tYnJlLCBOb19DcmVkaXRvSW5mb25hdml0LCBOb19DcmVkaXRvSW5mb25hdml0LCBldGMuKS4gUG9zdGVyaW9ybWVudGUsIHRyYXMgKipldmFsdWFyIG3Dumx0aXBsZXMgdmFyaWFibGVzIGVuIGxhIGVzcGVjaWZpY2FjacOzbiBkZWwgbW9kZWxvKiosIGxhcyBzaWd1aWVudGVzIGZ1ZXJvbiBzZWxlY2Npb25hZGFzIGNvbW8gbGFzIG3DoXMgYWRlY3VhZGFzIHBhcmEgcHJlZGVjaXIgbGEgUmVudW5jaWEgZGUgbG9zIGNvbGFib3JhZG9yZXMuDQpgYGB7cn0NCmZvcm11bGFfZ2xtIDwtIGlmZWxzZShSZW51bmNpYT09IlNpIiwgMSwwKSB+IEdlbmVybyAgKyBEZXBhcnRhbWVudG8gKyBNdW5pY2lwaW8gKyBFc3RhZG9fQ2l2aWwgK1NhbGFyaW9fZGlhcmlvICsgRWRhZCArIE5vbWJyZU1lcyANCmZvcm11bGExIDwtIFJlbnVuY2lhIH4gR2VuZXJvICsgRGVwYXJ0YW1lbnRvICsgTXVuaWNpcGlvICsgRXN0YWRvX0NpdmlsICsgU2FsYXJpb19kaWFyaW8gKyBFZGFkICsgTm9tYnJlTWVzIA0KYGBgDQoNCiMjIENyZWFjacOzbiBkZSBNb2RlbG9zDQoNCiMjIyBNb2RlbG8gMSBSZWdyZXNpw7NuIGxpbmVhbA0KDQpFbCBwcmltZXIgbW9kZWxvIHNlbGVjY2lvbmFkbyBlcyBsYSByZWdyZXNpw7NuIGxpbmVhbCwgc2llbmRvIHVuIG1vZGVsbyBpbmljaWFsIGFkZWN1YWRvIGRlYmlkbyBhIHN1IHNpbXBsaWNpZGFkIHkgZnVuY2lvbmFtaWVudG8sIHBlcm1pdGllbmRvIGVzdGFibGVjZXIgdW5hIGJhc2UgcGFyYSBjb21wcmVuZGVyIGVsIGNvbXBvcnRhbWllbnRvIGRlIHJlbnVuY2lhIHBvciBsb3MgZW1wbGVhZG9zIGVuIEZvcm0uDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDcmVhY2nDs24gZGVsIE1vZGVsbw0KbW9kZWxfZ2xtMSA8LSBnbG0oZm9ybXVsYV9nbG0sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGRhdGEgPSB0cmFpbikNCmBgYA0KDQojIyMjIEludGVycHJldGFjacOzbg0KUHJpbWVyYW1lbnRlLCBlcyBuZWNlc2FyaW8gZGVzdGFjYXIgbGFzIHZhcmlhYmxlcyBxdWUgb2J0dXZpZXJvbiB1biBtYXlvciBuaXZlbCBkZSBzaWduaWZpY2FuY2lhIGFsIG9idGVuZXIgdW4gcC12YWx1ZSBtZW5vciBhIDAuMDUsIGVzIGRlY2lyLCBsYXMgcXVlIHJlYWxtZW50ZSBwZXJtaXRlbiBsYSBkaXN0aW5jacOzbiBkZSBsYXMgY2F0ZWdvcsOtYXMgZW4gbGEgdmFyaWFibGUgZGUgaW50ZXLDqXMuIEVuIGNvbmp1bnRvIGNvbiBlc3RvLCB0YW1iacOpbiBhcXVlbGxhcyBjb24gdW4gbWF5b3Igbml2ZWwgZGUgaW1wb3J0YW5jaWEgZW4gZWwgbW9kZWxvLCBhbCB0ZW5lciBuaXZlbCBkZSBpbXBhY3RvIG1heW9yIHBhcmEgcGVydGVuZWNlciBhIHVuYSBjbGFzZSB1IG90cmEuDQoNCiogKipTYWxhcmlvIERpYXJpbyoqOiBDdWVudGEgY29uIHVuIG5pdmVsIGRlIHNpZ25pZmljYW5jaWEgYmFzdGFudGUgYWx0bywgYWwgaWd1YWwgcXVlIGNvbiBlbCBtYXlvciBuaXZlbCBkZSBpbXBvcnRhbmNpYSBlbiBlbCBtb2RlbG8gKDYuODgpLiBDb24gdW4gY29lZmljaWVudGUgZGUgLTUuMDgzZS0wMiwgZXN0YSB2YXJpYWJsZSBkZXRlcm1pbmEgcXVlIGxvcyBlbXBsZWFkb3MgcXVlIFJlbnVuY2lhbiBjdWVudGFuIGNvbiB1biBzYWxhcmlvIG3DoXMgYmFqby4NCg0KKiAqKkRlcGFydGFtZW50byoqOiBMYSBwZXJ0ZW5lbmNpYSBhIGxvcyBkZXBhcnRhbWVudG9zIGRlIFByb2R1Y2Npb24gQ2FydG9uIE1DLCBDb3N0dXJhLCBQcm9kdWNjaW9uIFJldG9ybmFibGUgeSBQcm9kdWNjaW9uIENhcnRvbiBNREwgcmVwcmVzZW50YW4gdW5hIGFsdGEgc2lnbmlmaWNhbmNpYSwgYWwgaWd1YWwgcXVlIGFsZ3Vub3MgZGUgbG9zIG5pdmVsZXMgbcOhcyBhbHRvcyBkZSBpbXBvcnRhbmNpYSAoNS43NCwgNS4xOSwgNC44NCB5IDQgcmVzcGVjdGl2YW1lbnRlKS4gTG9zIGNvZWZpY2llbnRlcyByZXZlbGFuIHF1ZSBsb3MgZW1wbGVhZG9zIGVuIGVzdG9zIGRlcGFydGFtZW50b3Mgc29uIGFxdWVsbG9zIHF1ZSB0aWVuZW4gbWF5b3IgYWJhbmRvbm8sIHBhcnRpY3VsYXJtZW50ZSBzaWVuZG8gZW4gbWF5b3IgbWVkaWRhIHBvciBlbCBkZXBhcnRhbWVudG8gZGUgY29zdHVyYSBhbCB0ZW5lciBlbCBjb2VmaWNpZW50ZSBtw6FzIGJham8gKC01Ljc0MWUrMDApLg0KDQoqICoqTWVzKio6IEVsIHNlciBjb250cmF0YWRvcyBkdXJhbnRlIGxvcyBtZXNlcyBkZSBGZWJyZXJvLCBKdW5pbyB5IEp1bGlvIHRpZW5lIHVuIGFsdG8gbml2ZWwgZGUgc2lnbmlmYW5jaWEgZGVudHJvIGRlbCBtb2RlbG8sIGFzw60gY29tbyBsb3Mgbml2ZWxlcyBtw6FzIGFsdG9zIG5pdmVsZXMgZGUgaW1wb3J0YW5jaWEgcGFyYSBsb3MgbWVzZXMgKDQuNzEsIDQuMzYgeSAzLjgzIHJlc3BlY3RpdmFtZW50ZSkuIEFsIHNlciBjb250cmF0YWRvIGR1cmFudGUgZWwgbWVzIGRlIGp1bmlvLCBsb3MgZW1wbGVhZG9zIHRpZW5kZW4gYSByZW51bmNpYXIgbcOhcyBkZSBhY3VlcmRvIGEgdW4gY29lZmljaWVudGUgYWx0byBkZSA1LjgyOWUrMDAsIHBvc2libGVtZW50ZSBkZWJpZG8gYSBsYXMgYWx0YXMgdGVtcGVyYXR1cmFzIGRlbCB2ZXJhbm8uIElndWFsbWVudGUsIEZlYnJlcm8gc2Vyw61hIGVsIHNlZ3VuZG8gbWVzIG3DoXMgYWx0byAoY29uc2lkZXJhbmRvIMO6bmljYW1lbnRlIGxvcyBzaWduaWZpY2F0aXZvcyksIHF1ZSB0aWVuZSBtYXlvciB0ZW5kZW5jaWEgYSBxdWUgbG9zIGVtcGxlYWRvcyByZW51bmNpZW4sIHNpZW5kbyBsb3MgbcOhcyBpZGVhbGVzIGxvcyBtZXNlcyBkZSBtYXlvIHkgZW5lcm8gY29tbyBsb3MgbWVzZXMgc2lnbmlmaWNhdGl2b3MgKHAtdmFsdWUgbWVub3IgYSAwLjA1KSBjb24gZWwgY29lZmljaWVudGUgbcOhcyBiYWpvIChtZW5vciB0ZW5kZW5jaWEgYSByZW51bmNpYXIpLg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgUmVzdW1lbiBkZWwgTW9kZWxvDQpzdW1tYXJ5KG1vZGVsX2dsbTEpDQoNCiMgSW1wYWN0byBkZSBsYXMgVmFyaWFibGVzDQpJbXBvcnRhbmNpYSA8LSB2YXJJbXAobW9kZWxfZ2xtMSkNCkltcG9ydGFuY2lhIDwtIEltcG9ydGFuY2lhICU+JQ0KICBhcnJhbmdlKGRlc2MoT3ZlcmFsbCkpDQpJbXBvcnRhbmNpYQ0KYGBgDQoNCiMjIyMgUHJlZGljY2nDs24geSBSZXN1bHRhZG9zDQpFbCBtb2RlbG8gZGVtdWVzdHJhIHRlbmVyIHVuIGRlc2VtcGXDsW8gaW5pY2lhbCBhZGVjdWFkbywgc2llbmRvIGFjZXJ0YWRvIGVuIHVuIDg3LjI3JSBkZSBsb3MgY2Fzb3MgZW4gcXVlIHVuIGNvbGFib3JhZG9yIHRpZW5kZSBhIHJlbnVuY2lhci4gQXNpbWlzbW8sIGN1ZW50YSBjb24gdW4gS2FwcGEgZGUgMC42NzkzLCB0ZW5pZW5kbyBwb3IgbG8gdGFudG8sIHVuIG1lam9yIGRlc2VtcGXDsW8gcXVlIHVuYSBwcmVkaWNjacOzbiBhbGVhdG9yaWEuDQoNCmBgYHtyfQ0KIyBQcmVkaWNjacOzbg0KcHJlZGljY2lvbl9nbG0gPC0gcHJlZGljdChtb2RlbF9nbG0xLCB0ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyBNYXRyaXogZGUgY29uZnVpc2nDs24NCmNvbmZ1c2lvbl9nbG0gPC0gY29uZnVzaW9uTWF0cml4KGFzLmZhY3RvcihpZmVsc2UocHJlZGljY2lvbl9nbG0+MC41LCAiU2kiLCAiTm8iKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdCRSZW51bmNpYSwgcG9zaXRpdmUgPSAiTm8iKQ0KcHJpbnQoY29uZnVzaW9uX2dsbSkNCmBgYA0KDQpBc2ltaXNtbywgZW4gZWwgbW9kZWxvIGdsbSBzZSBvYnRpZW5lIHVuIHZhbG9yIGVuIEFVQyBkZSAwLjg1LCByZWFmaXJtYW5kbyBxdWUgbGEgcHJlZGljY2nDs24gZGVsIG1vZGVsbyBlcyBtZWpvciBxdWUgdW5hIHByZWRpY2Npw7NuIGFsZWF0b3JpYS4NCmBgYHtyfQ0KIyAgQ3VydmEgUk9DIA0KIyMgT2J0ZW5lciBsYXMgcHVudHVhY2lvbmVzIGRlIHByb2JhYmlsaWRhZA0KcHJlZGljY2lvbl9wcm9iX2dsbSA8LSBwcmVkaWN0KG1vZGVsX2dsbTEsIHRlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQojIyBHZW5lcmFyIGxhIGN1cnZhIFJPQw0Kcm9jX29ial9nbG0gPC0gcm9jKHRlc3QkUmVudW5jaWEsIGFzLm51bWVyaWMocHJlZGljY2lvbl9wcm9iX2dsbSkpDQoNCiMjIERpYnVqYXIgbGEgY3VydmEgUk9DDQpwbG90LnJvYyhyb2Nfb2JqX2dsbSwgbWFpbiA9ICJDdXJ2YSBST0MiLCBjb2wgPSAiYmx1ZSIpDQoNCiMjIENhbGN1bGFyIGVsIEFVQw0KYXVjX2dsbSA8LSBwUk9DOjphdWMocm9jX29ial9nbG0pDQphdWNfZ2xtDQpgYGANCg0KIyMjIE1vZGVsbyAyIMOhcmJvbCBkZSBkZWNpc2nDs24NCg0KRWwgc2VndW5kbyBtb2RlbG8gZWxlZ2lkbyBlcyBlbCDDgXJib2wgZGUgRGVjaXNpw7NuLCBlbCBjdWFsIGFsIHNlciB1biBtb2RlbG8gYmFzYWRvIGVuIGVsIGVzdGFibGVjaW1pZW50byBkZSByZWdsYXMsIHVuIHBsYW50ZWFtaWVudG8gY29tw7pubWVudGUgdXNhZG8gZW4gcHJvYmxlbWFzIGRlIGNsYXNpZmljYWNpw7NuLCBhc8OtIGNvbW8gc2VyIHVuIG1vZGVsbyBkZSBmw6FjaWwgaW50ZXJwcmV0YWNpw7NuIHZpc3VhbCwgZnVlIHNlbGVjY2lvbmFkbyBjb21vIHVuYSBkZSBsYXMgcG9zaWJsZXMgYWx0ZXJuYXRpdmFzIGRlIG1vZGVsbyBwYXJhIGVsIGVudGVuZGltaWVudG8gZW4gZWwgY29tcG9ydGFtaWVudG8gZGUgcmVudW5jaWEgZGUgbG9zIGVtcGxlYWRvcyBkZSBGb3JtLg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQ3JlYWNpw7NuIGRlbCBNb2RlbG8NCmFyYm9sIDwtIHJwYXJ0KGZvcm11bGE9Zm9ybXVsYTEsIGRhdGEgPSB0cmFpbiwgbWV0aG9kID0gImNsYXNzIikNCmBgYA0KDQoNCiMjIyMgSW50ZXJwcmV0YWNpw7NuDQoqIEVsIHByaW1lciBjYW1pbm8gKDAlIGRlIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYXIgY29uIGNvYmVydHVyYSBkZWwgMiUgZGUgZW1wbGVhZG9zKSBpbmRpY2EgcXVlIGN1YW5kbyBlbCBzYWxhcmlvIGRpYXJpbyBlcyBtZW5vciBhIDIyOSwgZWwgZW1wbGVhZG8gdHJhYmFqYSBlbiB1bm8gZGUgbG9zIGRlcGFydGFtZW50b3MgKENhbGlkYWQsIENFRElTLCBFSFMsIEVtYmFycXVlcywgT3Ryb3MsIFBhaWxlcsOtYSB5IFBpbnR1cmEsIFByb2R1Y2Npw7NuIENhcnTDs24gTURMKSB5IGVsIG1lcyBlcyBlbmVybyBvIG5vdmllbWJyZSwgbGEgcHJvYmFiaWxpZGFkIGRlIHJlbnVuY2lhIGVzIG11eSBiYWphLCBjYXNpIG51bGEuDQoNCiogRWwgc2VndW5kbyBjYW1pbm8gKDExJSBkZSBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWFyIGNvbiBjb2JlcnR1cmEgZGVsIDIwJSBkZSBlbXBsZWFkb3MpIHN1Z2llcmUgcXVlIGN1YW5kbyBlbCBzYWxhcmlvIGRpYXJpbyBlcyBpZ3VhbCBvIG1heW9yIGEgMjI5LCBsYSBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWEgYXVtZW50YSBsaWdlcmFtZW50ZSwgcGVybyB0b2RhdsOtYSBlcyByZWxhdGl2YW1lbnRlIGJhamEuDQoNCiogRWwgdGVyY2VyIGNhbWlubyAoMTQlIGRlIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYXIgY29uIGNvYmVydHVyYSBkZWwgNyUgZGUgZW1wbGVhZG9zKSBtdWVzdHJhIHF1ZSBzaSBlbCBzYWxhcmlvIGRpYXJpbyBlcyBtZW5vciBhIDIyOSwgZWwgZW1wbGVhZG8gdHJhYmFqYSBlbiBsb3MgZGVwYXJ0YW1lbnRvcyAoQ2FsaWRhZCwgQ0VESVMsIEVIUywgRW1iYXJxdWVzLCBPdHJvcywgUGFpbGVyw61hIHkgUGludHVyYSwgUHJvZHVjY2nDs24gQ2FydMOzbiBNREwpIHkgZWwgbWVzIGVzIGFnb3N0bywgZGljaWVtYnJlLCBmZWJyZXJvLCBqdWxpbywgb2N0dWJyZSBvIHNlcHRpZW1icmUsIHkgbGEgZWRhZCBlcyBtYXlvciBvIGlndWFsIGEgNDcsIGxhIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYSBlcyBtYXlvciBxdWUgZW4gbGEgcHJpbWVyYSByZWdsYSwgcGVybyBhw7puIG5vIGVzIG11eSBhbHRhLg0KDQoqIEVsIGN1YXJ0byBjYW1pbm8gKDE4JSBkZSBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWFyIGNvbiBjb2JlcnR1cmEgZGVsIDIlIGRlIGVtcGxlYWRvcykgbXVlc3RyYSB1bmEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHJlbnVuY2lhIGN1YW5kbyBzZSBjb21iaW5hbiBjYXJhY3RlcsOtc3RpY2FzIGNvbW8gc2FsYXJpbyBkaWFyaW8gbWVub3IgYSAyMjksIHRyYWJham8gZW4gbG9zIGRlcGFydGFtZW50b3MgKENvc3R1cmEsIFByb2R1Y2Npw7NuIENhcnTDs24gTUMsIFByb2R1Y2Npw7NuIFJldG9ybmFibGUpIHkgZWwgbWVzIGVzIGFnb3N0bywgZGljaWVtYnJlLCBlbmVybywgZmVicmVybywganVsaW8sIG5vdmllbWJyZSwgb2N0dWJyZSBvIHNlcHRpZW1icmUuDQoNCiogRWwgcXVpbnRvIGNhbWlubyAoODYlIGRlIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYXIgY29uIGNvYmVydHVyYSBkZWwgOCUgZGUgZW1wbGVhZG9zKSBpbmRpY2EgdW5hIGFsdGEgcHJvYmFiaWxpZGFkIGRlIHJlbnVuY2lhIGN1YW5kbyBlbCBzYWxhcmlvIGRpYXJpbyBlcyBtZW5vciBhIDIyOSB5IGVsIGVtcGxlYWRvIHRyYWJhamEgZW4gbG9zIGRlcGFydGFtZW50b3MgZGUgKENhbGlkYWQsIENFRElTLCBFSFMsIEVtYmFycXVlcywgT3Ryb3MsIFBhaWxlcsOtYSB5IFBpbnR1cmEsIFByb2R1Y2Npw7NuIENhcnTDs24gTURMKSBkdXJhbnRlIGxvcyBtZXNlcyBhZ29zdG8sIGRpY2llbWJyZSwgZmVicmVybywganVsaW8sIG9jdHVicmUgbyBzZXB0aWVtYnJlLCB5IGxhIGVkYWQgZXMgbWVub3IgYSA0Ny4NCg0KKiBFbCBzZXh0byBjYW1pbm8gKDg5JSBkZSBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWFyIGNvbiBjb2JlcnR1cmEgZGVsIDglIGRlIGVtcGxlYWRvcykgaW5kaWNhIHVuYSBhbHRhIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYSBjdWFuZG8gZWwgc2FsYXJpbyBkaWFyaW8gZXMgbWVub3IgYSAyMjkgeSBlbCBlbXBsZWFkbyB0cmFiYWphIGVuIGxvcyBkZXBhcnRhbWVudG9zIGRlIChDYWxpZGFkLCBDRURJUywgQ29zdHVyYSwgRUhTLCBFbWJhcnF1ZXMsIE90cm9zLCBQYWlsZXLDrWEgeSBQaW50dXJhLCBQcm9kdWNjacOzbiBDYXJ0w7NuIE1DLCBQcm9kdWNjacOzbiBDYXJ0w7NuIE1ETCwgUHJvZHVjY2nDs24gUmV0b3JuYWJsZSkgZHVyYW50ZSBsb3MgbWVzZXMgYWJyaWwsIGp1bmlvLCBtYXJ6byBvIG1heW8uDQoNCiogRmluYWxtZW50ZSwgZWwgc8OpcHRpbW8gY2FtaW5vICgxMDAlIGRlIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYSBjb24gY29iZXJ0dXJhIGRlbCA1NiUsIGxhIG1heW9yw61hIGRlIGVtcGxlYWRvcykgbXVlc3RyYSBsYSBwcm9iYWJpbGlkYWQgbcOhcyBhbHRhIGRlIHJlbnVuY2lhIGN1YW5kbyBlbCBzYWxhcmlvIGRpYXJpbyBlcyBtZW5vciBhIDIyOSB5IGVsIGVtcGxlYWRvIHRyYWJhamEgZW4gZGVwYXJ0YW1lbnRvcyAow4FyZWEgbm8gZGVmaW5pZGEsIENvcnRhZG9yYXMsIE1hdGVyaWFsZXMsIFN0YWJpbHVzKS4NCg0KRWwgYW7DoWxpc2lzIHNvYnJlIGxhcyByZWdsYXMgZGVsIG1vZGVsbyBkZSDDgXJib2wgZGV0ZXJtaW5hIHF1ZSBsb3MgZW1wbGVhZG9zIGNvbiBzYWxhcmlvcyBkaWFyaW9zIGJham9zIChtZW5vcyBkZSAyMjkpIGVzdMOhbiBtw6FzIHByb3BlbnNvcyBhIHJlbnVuY2lhciwgcGFydGljdWxhcm1lbnRlIHNpIHRyYWJhamFuIGVuIGRlcGFydGFtZW50b3MgZXNwZWPDrWZpY29zIHkgZW4gY2llcnRvcyBtZXNlcyBkZWwgYcOxby4gRXN0ZSByaWVzZ28gZGUgcmVudW5jaWEgc2UgaW50ZW5zaWZpY2EgZW4gw6FyZWFzIGNvbW8gQ2FsaWRhZCwgQ0VESVMsIEVIUywgRW1iYXJxdWVzLCBPdHJvcywgUGFpbGVyw61hIHkgUGludHVyYSwgUHJvZHVjY2nDs24gQ2FydMOzbiBNREwsIFByb2R1Y2Npw7NuIENhcnTDs24gTUMsIHkgUHJvZHVjY2nDs24gUmV0b3JuYWJsZSwgcG9zaWJsZW1lbnRlIGRlYmlkbyBhIGZhY3RvcmVzIGNvbW8gbWF5b3JlcyBjYXJnYXMgZGUgdHJhYmFqbywgYW1iaWVudGVzIGxhYm9yYWxlcyBkZXNhZmlhbnRlcyB1IG9wb3J0dW5pZGFkZXMgZGUgY3JlY2ltaWVudG8gbGltaXRhZGFzLiBBZGVtw6FzLCBsYSBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWEgYXVtZW50YSBkdXJhbnRlIGxvcyBtZXNlcyBkZSBhZ29zdG8sIGRpY2llbWJyZSwgZmVicmVybywganVsaW8sIG9jdHVicmUgeSBzZXB0aWVtYnJlLCBwdWRpZW5kbyBzZXIgcGVyw61vZG9zIGRlIGFsdGEgZGVtYW5kYSBsYWJvcmFsIG8gZmFjdG9yZXMgZXN0YWNpb25hbGVzIHF1ZSBsZXMgZ2VuZXJhbiBtYXlvciBlc3Ryw6lzLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBHcsOhZmljYSBkZWwgw4FyYm9sIGRlIERlY2lzacOzbg0KcnBhcnQucGxvdChhcmJvbCwgdHlwZSA9IDUsIGJveC5wYWxldHRlID0gIkduUmQiLCBzaGFkb3cuY29sID0gImdyYXkiKQ0KDQojIFJlZ2xhcyBkZWwgw4FyYm9sDQpycGFydC5ydWxlcyhhcmJvbCwgc3R5bGUgPSAidGFsbHciLCBjb3ZlciA9IEZBTFNFLCBjbGlwLmZhY3MgPSBUUlVFLCBmYWNsZW4gPSAwKQ0KYGBgDQoNCiMjIyMgUHJlZGljY2nDs24geSBSZXN1bHRhZG9zDQpEZSBhY3VlcmRvIGEgbG9zIHJlc3VsdGFkb3MgZGUgbGEgbWF0cml6IGRlIGNvbmZ1c2nDs24sIGVsIGRlc2VtcGXDsW8gaW5pY2lhbCBkZWwgbW9kZWxvIG11ZXN0cmEgcXVlIHNlIGxvZ3JhIHByZWRlY2lyIGFjZXJ0YWRhbWVudGUgZW4gdW4gODcuMjclIGRlIGxvcyBjYXNvcy4gQWRlbcOhcyBjdWVudGEgY29uIHVuIG5pdmVsIGRlIEthcHBhIGRlIDAuNjg1Niwgc2llbmRvIG1lam9yIGEgdW5hIHByZWRpY2Npw7NuIGFsZWF0b3JpYS4NCmBgYHtyfQ0KIyBQcmVkaWNjacOzbg0KcHJlZGljY2lvbl9BPC0gcHJlZGljdChhcmJvbCwgdGVzdCwgdHlwZT0iY2xhc3MiKQ0KDQojIE1hdHJpeiBkZSBjb25mdWlzacOzbg0KY29uZnVzaW9uX0EgPC0gY29uZnVzaW9uTWF0cml4KHByZWRpY2Npb25fQSwgdGVzdCRSZW51bmNpYSxwb3NpdGl2ZSA9ICJObyIpDQpwcmludChjb25mdXNpb25fQSkNCmBgYA0KDQpFbCBBVUMgb2J0ZW5pZG8gcGFyYSBlbCBtb2RlbG8gZGUgw6FyYm9sIGRlIGRlY2lzacOzbiBlcyBkZSAwLjkyLCByZWFmaXJtYW5kbyBzdSBkZXNlbXBlw7FvIHNvYnJlIHVuIG1vZGVsbyBhbGVhdG9yaWEuDQpgYGB7cn0NCiMgIEN1cnZhIFJPQyANCiMjIE9idGVuZXIgbGFzIHB1bnR1YWNpb25lcyBkZSBwcm9iYWJpbGlkYWQNCnByZWRpY2Npb25fcHJvYl90cmVlIDwtIHByZWRpY3QoYXJib2wsIHRlc3QsIHR5cGUgPSAicHJvYiIpDQpwcmVkaWNjaW9uX3Byb2JfdHJ1ZV90cmVlIDwtIHByZWRpY2Npb25fcHJvYl90cmVlWyAsICJObyJdDQoNCiMjIEdlbmVyYXIgbGEgY3VydmEgUk9DDQpyb2Nfb2JqX3RyZWUgPC0gcm9jKHRlc3QkUmVudW5jaWEsIHByZWRpY2Npb25fcHJvYl90cnVlX3RyZWUpDQoNCiMjIERpYnVqYXIgbGEgY3VydmEgUk9DDQpwbG90LnJvYyhyb2Nfb2JqX3RyZWUsIG1haW49IkN1cnZhIFJPQyIsIGNvbD0iYmx1ZSIpDQoNCiMjIENhbGN1bGFyIGVsIEFVQw0KYXVjX3RyZWUgPC0gYXVjKHJvY19vYmpfdHJlZSkNCmF1Y190cmVlDQpgYGANCg0KDQojIyMgTW9kZWxvIDMgTmFpdmUgQmF5ZXMNCg0KRWwgdGVyY2VyIG1vZGVsbyBlbGVnaWRvIGZ1ZSBOYWl2ZSBCYXllcywgZXN0ZSBtb2RlbG8gZnVlIHNlbGVjY2lvbmFkbyBjb21vIHVuYSBhbHRlcm5hdGl2YSBkZWJpZG8gYSBzdSBlbmZvcXVlIGVuIGxhIHByb2JhYmlsaWRhZCBjb25kaWNpb25hbCBiYWpvIGVsIHN1cHVlc3RvIGRlIHF1ZSBjYWRhIHVuYSBkZSBsYXMgdmFyaWFibGVzIGV4cGxpY2F0aXZhcyBpbXBhY3RhIGRlIG1hbmVyYSBpbmRlcGVuZGllbnRlIGEgbGEgdmFyaWFibGUgaW50ZXLDqXMgKHNpbiBleGlzdGUgdW5hIGludGVyYWNjacOzbiBlbnRyZSBlbGxhcykuIEVzdGUgZnVuY2lvbmFtaWVudG8gcHVlZGUgcmVwcmVzZW50YXIgdW5hIGZvcm1hIGVzdGFkw61zdGljYSBjbGFyYSBwYXJhIGRpZmVyZW5jaWFyIGVudHJlIGxvcyBlbXBsZWFkb3MgcXVlIHJlbnVuY2lhbiB5IGxvcyBxdWUgc2UgbWFudGllbmVuIGVuIGxhIGVtcHJlc2EsIG1hbnRlbmllbmRvIHVuIGJ1ZW4gbml2ZWwgZGUgaW50ZXJwcmV0YWJpbGlkYWQgYSB0cmF2w6lzIGRlIHZpc3VhbGl6YWNpb25lcyBkZSBsYSBwcm9iYWJpbGlkYWQgcGFyYSBjYWRhIHVuYSBkZSBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzLg0KDQpgYGB7cn0NCm1vZGVsb19uYiA8LSBuYWl2ZUJheWVzKGZvcm11bGExLCBkYXRhID0gdHJhaW4pDQpgYGANCg0KIyMjIyBJbnRlcnByZXRhY2nDs24NCkNvbiBsYSBmaW5hbGlkYWQgZGUgaW50ZXJwcmV0YXIgYWRlY3VhZGFtZW50ZSBlbCBtb2RlbG8gZGUgTmFpdmUgQmF5ZXMsIGNvbiBzdSBzdXB1ZXN0byBkZSBpbmRlcGVuZGllbnRlLCBzZSBvYnR1dmllcm9uIHVuYSBzZXJpZSBkZSB2aXN1YWxpemFjaW9uZXMgcXVlIHBlcm1pdGVuIGRldGVybWluYXIgbGEgcHJvYmFiaWxpZGFkIGNvbmRpY2lvbmFsIGNvbiBsYSBxdWUgdW4gbml2ZWwgbyB2YWxvciBlbiBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIGRldGVybWluYSBxdWUgdW4gZW1wbGVhZG8gcmVudW5jaWUgbyBubw0KDQoNCiMjIyMjIEfDqW5lcm8NClNlIG9ic2VydmEgcXVlIGxhIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYSBlcyBzaW1pbGFyIHBhcmEgbGFzIG11amVyZXMgeSBsb3MgaG9tYnJlcywgeWEgcXVlIGVsIG1vZGVsbyBOYWl2ZSBCYXllcyBubyBlbmN1ZW50cmEgdW5hIGRpZmVyZW5jaWEgc2lnbmlmaWNhdGl2YSBlbiBsYSBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWEgZW50cmUgbG9zIGRvcyBnw6luZXJvcywgZXMgZGVjaXIsIGVsIGfDqW5lcm8gbm8gcmVwcmVzZW50YSB1bmEgdmFyaWFibGUgcXVlIHRlbmdhIGVmZWN0byBzb2JyZSBlbCBzaSB1biBlbXBsZWFkbyByZW51bmNpYSBvIG5vLiBFc3RvIHBvZHLDrWEgb2N1cnJpciBkZWJpZG8gYSBxdWUgbG9zIGVtcGxlYWRvcyBkYW4gbWF5b3IgcGVzbyBhIG90cmFzIHZhcmlhYmxlcyB5IGhheSB1bmEgZGlzdGluY2nDs24gY2FzaSBudWxhIGVuIGVsIHRyYXRvIGEgbG9zIGVtcGxlYWRvcyBzZWd1biBzdSBnZW5lcm8uIE5vIG9ic3RhbnRlLCBzZSBvYnNlcnZhIHF1ZSBsYXMgbXVqZXJlcyB0aWVuZW4gdW5hIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYSBsaWdlcmFtZW50ZSBtYXlvciBxdWUgbG9zIGhvbWJyZXMsIHBvciBlbmRlLCBzZSByZXF1aWVyZSBnZW5lcmFyIHVuIGx1Z2FyIGRlIHRyYWJham8gbcOhcyBqdXN0byB5IGVxdWl0YXRpdm8gcGFyYSB0b2RvcyBsb3MgZW1wbGVhZG9zLg0KYGBge3J9DQpicCA8LSBiYXJwbG90KG1vZGVsb19uYiR0YWJsZXMkR2VuZXJvLCBiZXNpZGUgPSBUUlVFLCBjb2wgPSBjKCJibGFjayIsICJsaWdodGdyYXkiKSwgbGVnZW5kLnRleHQgPSBUUlVFLCBtYWluID0gIkfDqW5lcm8iLA0KICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgobW9kZWxvX25iJHRhYmxlcyRHZW5lcm8pICogMS4yKSkNCnRleHQoeCA9IGJwLCB5ID0gbW9kZWxvX25iJHRhYmxlcyRHZW5lcm8sIGxhYmVsID0gcm91bmQobW9kZWxvX25iJHRhYmxlcyRHZW5lcm8sNCksIHBvcyA9IDMpDQpgYGANCg0KDQojIyMjIyBEZXBhcnRhbWVudG8NCkxvcyBlbXBsZWFkb3MgZGVsIGRlcGFydGFtZW50byAiw4FyZWEgbm8gZGVmaW5pZGEiIHRpZW5lbiB1bmEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIGNvbnNpZGVyYXIgbGEgcmVudW5jaWEsIHBvc2libGVtZW50ZSBkZWJpZG8gYSBsYSBmYWx0YSBkZSBjbGFyaWRhZCBlbiBzdXMgcm9sZXMgeSByZXNwb25zYWJpbGlkYWRlcywgbG8gcXVlIGdlbmVyYSBpbmNlcnRpZHVtYnJlIHkgZGVzbW90aXZhY2nDs24uIEVuIGNvbnRyYXN0ZSwgYXF1ZWxsb3MgcXVlIHRyYWJhamFuIGVuIGxvcyBkZXBhcnRhbWVudG9zIGRlIFByb2R1Y2Npw7NuIE1DLCBNQ0wgeSBSZXRvcm5hYmxlLCBhc8OtIGNvbW8gZW4gQ29zdHVyYSB5IENFRElTLCBtdWVzdHJhbiB1bmEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHBlcm1hbmVjZXIgZW4gbGEgZW1wcmVzYSwgcHJvYmFibGVtZW50ZSBwb3JxdWUgZXN0b3Mgcm9sZXMgZXN0w6FuIG1lam9yIGRlZmluaWRvcyB5IHByb3BvcmNpb25hbiB1bmEgbWF5b3IgZXN0YWJpbGlkYWQgeSBzYXRpc2ZhY2Npw7NuIGxhYm9yYWwuIERlIGhlY2hvLCBsYSB2YXJpYWNpw7NuIGRlIHByb2JhYmlsaWRhZGVzIGRlIHJlbnVuY2lhIGVudHJlIGxvcyBkaWZlcmVudGVzIGRlcGFydGFtZW50b3MgZGUgRk9STSBwdWVkZSBleHBsaWNhcnNlIHBvciBkaXZlcnNvcyBmYWN0b3JlcyByZWxhY2lvbmFkb3MgY29uIGVsIGNvbnRleHRvIGRlIG5lZ29jaW8gZGUgbGEgZW1wcmVzYS4gQWxndW5vcyBkZSBlc3RvcyBmYWN0b3JlcyBwb2Ryw61hbiBzZXI6ICANCiogQ29uZGljaW9uZXMgZGUgdHJhYmFqbzogTGFzIGNvbmRpY2lvbmVzIGRlIHRyYWJham8gZW4gYWxndW5vcyBkZXBhcnRhbWVudG9zIHB1ZWRlbiBzZXIgbcOhcyBkdXJhcyBvIHBlbGlncm9zYXMgcXVlIGVuIG90cm9zLCBsbyBxdWUgcG9kcsOtYSBjb25kdWNpciBhIHVuYSBtZW5vciBzYXRpc2ZhY2Npw7NuIGxhYm9yYWwgeSB1bmEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHJlbnVuY2lhLiAgDQoqIFNhbGFyaW8geSBiZW5lZmljaW9zOiBFbCBzYWxhcmlvIHkgbG9zIGJlbmVmaWNpb3MgcHVlZGVuIHNlciBtw6FzIGJham9zIGVuIGFsZ3Vub3MgZGVwYXJ0YW1lbnRvcyBxdWUgZW4gb3Ryb3MsIGxvIHF1ZSBwb2Ryw61hIGxsZXZhciBhIGxhIGZydXN0cmFjacOzbiB5IGxhIHJlbnVuY2lhLiAgDQoqIE9wb3J0dW5pZGFkZXMgZGUgY3JlY2ltaWVudG86IExhcyBvcG9ydHVuaWRhZGVzIGRlIGNyZWNpbWllbnRvIHB1ZWRlbiBzZXIgbcOhcyBsaW1pdGFkYXMgZW4gYWxndW5vcyBkZXBhcnRhbWVudG9zIHF1ZSBlbiBvdHJvcywgbG8gcXVlIHBvZHLDrWEgZGVzbW90aXZhciBhIGxvcyBlbXBsZWFkb3MgeSBsbGV2YXJsb3MgYSBidXNjYXIgb3BvcnR1bmlkYWRlcyBlbiBvdHJhcyBlbXByZXNhcy4gIA0KKiBDdWx0dXJhIGRlbCBkZXBhcnRhbWVudG86IExhIGN1bHR1cmEgZGVsIGRlcGFydGFtZW50byBwdWVkZSBzZXIgbcOhcyBuZWdhdGl2YSBvIHTDs3hpY2EgZW4gYWxndW5vcyBkZXBhcnRhbWVudG9zIHF1ZSBlbiBvdHJvcywgbG8gcXVlIHBvZHLDrWEgY3JlYXIgdW4gYW1iaWVudGUgZGUgdHJhYmFqbyBob3N0aWwgeSBsbGV2YXIgYSBsYSByZW51bmNpYS4NCmBgYHtyfQ0KYnAgPC0gYmFycGxvdChtb2RlbG9fbmIkdGFibGVzJERlcGFydGFtZW50bywgbGVnZW5kLnRleHQgPSBUUlVFLCBiZXNpZGUgPSBUUlVFLCBjb2wgPSBjKCJibGFjayIsICJsaWdodGdyYXkiKSwgbWFpbiA9ICJEZXBhcnRhbWVudG8iLA0KICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgobW9kZWxvX25iJHRhYmxlcyREZXBhcnRhbWVudG8pICogMS4yKSwgY2V4Lm5hbWVzID0gIDAuNikNCnRleHQoeCA9IGJwLCB5ID0gbW9kZWxvX25iJHRhYmxlcyREZXBhcnRhbWVudG8sIGxhYmVsID0gcm91bmQobW9kZWxvX25iJHRhYmxlcyREZXBhcnRhbWVudG8sMiksIHBvcyA9IDMpDQpgYGANCg0KIyMjIyMgTXVuaWNpcGlvDQpMb3MgY29sYWJvcmFkb3JlcyBkZWwgbXVuaWNpcGlvIGRlIEFwb2RhY2EgdGllbmVuIG1heW9yIHByb2JhYmlsaWRhZCBkZSBjb25zaWRlcmFyIHJlbnVuY2lhciBkZWJpZG8gYSBwb3NpYmxlcyBkZXNhZsOtb3MgbG9nw61zdGljb3MgeSB1bmEgbWVub3IgY2FsaWRhZCBkZSB2aWRhLCBsbyBxdWUgYWZlY3RhIG5lZ2F0aXZhbWVudGUgc3Ugc2F0aXNmYWNjacOzbiBsYWJvcmFsLiBFbiBjb250cmFzdGUsIGxvcyBjb2xhYm9yYWRvcmVzIGRlIEd1YWRhbHVwZSB0aWVuZW4gbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHBlcm1hbmVjZXIgZW4gbGEgZW1wcmVzYSwgcG9zaWJsZW1lbnRlIGdyYWNpYXMgYSBtZWpvcmVzIGNvbmRpY2lvbmVzIGRlIHZpZGEgeSBzZXJ2aWNpb3MsIGxvIHF1ZSBjb250cmlidXllIGEgdW5hIG1heW9yIHNhdGlzZmFjY2nDs24geSBjb21wcm9taXNvIGNvbiBsYSBlbXByZXNhLiBTaW4gZW1iYXJnbywgZXMgaW1wb3J0YW50ZSBkZXN0YWNhciBsYSB2YXJpYWNpw7NuIGRlIHByb2JhYmlsaWRhZGVzIGRlIHJlbnVuY2lhIGVudHJlIGxvcyBkaWZlcmVudGVzIG11bmljaXBpb3MgZGUgRk9STSwgbG8gY3VhbCBwdWVkZSBleHBsaWNhcnNlIHBvciBkaXZlcnNvcyBmYWN0b3JlcyByZWxhY2lvbmFkb3MgY29uIGVsIGNvbnRleHRvIGRlIG5lZ29jaW8gZGUgbGEgZW1wcmVzYS4gQWxndW5vcyBkZSBlc3RvcyBmYWN0b3JlcyBwb2Ryw61hbiBzZXI6DQoqIENvc3RvIGRlIHZpZGE6IEVsIGNvc3RvIGRlIHZpZGEgcHVlZGUgc2VyIG3DoXMgYWx0byBlbiBhbGd1bm9zIG11bmljaXBpb3MgcXVlIGVuIG90cm9zLCBsbyBxdWUgcG9kcsOtYSBsbGV2YXIgYSBsYSBpbnNhdGlzZmFjY2nDs24gZGUgbG9zIGVtcGxlYWRvcyBjb24gc3Ugc2FsYXJpbyB5IGJlbmVmaWNpb3MuDQoqIERpc3RhbmNpYSBhbCB0cmFiYWpvOiBMYSBkaXN0YW5jaWEgYWwgdHJhYmFqbyBwdWVkZSBzZXIgbcOhcyBsYXJnYSBlbiBhbGd1bm9zIG11bmljaXBpb3MgcXVlIGVuIG90cm9zLCBsbyBxdWUgcG9kcsOtYSBnZW5lcmFyIGRpZmljdWx0YWRlcyBlbiBlbCB0cmFuc3BvcnRlIHkgdW4gbWVub3IgZXF1aWxpYnJpbyBlbnRyZSBsYSB2aWRhIGxhYm9yYWwgeSBwZXJzb25hbC4NCiogT3BvcnR1bmlkYWRlcyBkZSBlbXBsZW86IExhcyBvcG9ydHVuaWRhZGVzIGRlIGVtcGxlbyBwdWVkZW4gc2VyIG3DoXMgbGltaXRhZGFzIGVuIGFsZ3Vub3MgbXVuaWNpcGlvcyBxdWUgZW4gb3Ryb3MsIGxvIHF1ZSBwb2Ryw61hIGRlc21vdGl2YXIgYSBsb3MgZW1wbGVhZG9zIHkgbGxldmFybG9zIGEgYnVzY2FyIG9wb3J0dW5pZGFkZXMgZW4gb3RyYXMgZW1wcmVzYXMuDQoqIENhbGlkYWQgZGUgdmlkYTogTGEgY2FsaWRhZCBkZSB2aWRhIHB1ZWRlIHNlciBtw6FzIGJhamEgZW4gYWxndW5vcyBtdW5pY2lwaW9zIHF1ZSBlbiBvdHJvcywgbG8gcXVlIHBvZHLDrWEgYWZlY3RhciBuZWdhdGl2YW1lbnRlIGxhIHNhdGlzZmFjY2nDs24gZ2VuZXJhbCBkZSBsb3MgZW1wbGVhZG9zLg0KDQpgYGB7cn0NCmJwIDwtYmFycGxvdChtb2RlbG9fbmIkdGFibGVzJE11bmljaXBpbywgbGVnZW5kLnRleHQgPSBUUlVFLCBiZXNpZGUgPSBUUlVFLCBjb2wgPSBjKCJibGFjayIsICJsaWdodGdyYXkiKSwgbWFpbiA9ICJNdW5pY2lwaW8iLA0KICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgobW9kZWxvX25iJHRhYmxlcyRNdW5pY2lwaW8pICogMS4yKSkNCnRleHQoeCA9IGJwLCB5ID0gbW9kZWxvX25iJHRhYmxlcyRNdW5pY2lwaW8sIGxhYmVsID0gcm91bmQobW9kZWxvX25iJHRhYmxlcyRNdW5pY2lwaW8sMiksIHBvcyA9IDMpDQpgYGANCg0KIyMjIyMgRXN0YWRvIENpdmlsDQpMb3MgY29sYWJvcmFkb3JlcyBlbiBtYXRyaW1vbmlvIHkgbG9zIGRpdm9yY2lhZG9zIHRpZW5lbiB1bmEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHBlcm1hbmVjZXIgZW4gbGEgZW1wcmVzYSwgcHJvYmFibGVtZW50ZSBkZWJpZG8gYSBzdXMgbWF5b3JlcyByZXNwb25zYWJpbGlkYWRlcyBmYW1pbGlhcmVzIHkgZmluYW5jaWVyYXMsIHF1ZSBsb3MgaGFjZW4gdmFsb3JhciBtw6FzIGxhIGVzdGFiaWxpZGFkIGxhYm9yYWwuIFBvciBvdHJvIGxhZG8sIGxvcyBjb2xhYm9yYWRvcmVzIHNvbHRlcm9zIHkgYXF1ZWxsb3MgZW4gdW5pw7NuIGxpYnJlIHByZXNlbnRhbiB1bmEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHJlbnVuY2lhciwgcG9zaWJsZW1lbnRlIGRlYmlkbyBhIHN1IG1lbm9yIG5pdmVsIGRlIGNvbXByb21pc29zIHBlcnNvbmFsZXMsIGxvIHF1ZSBsZXMgb3RvcmdhIHVuYSBtYXlvciBsaWJlcnRhZCBwYXJhIGJ1c2NhciBudWV2YXMgb3BvcnR1bmlkYWRlcyB5IGNhbWJpb3MgZW4gc3UgY2FycmVyYSBwcm9mZXNpb25hbC4gTGEgZGlmZXJlbmNpYSBkZSBwcm9iYWJpbGlkYWRlcyBkZSByZW51bmNpYSBlbnRyZSBsb3MgZW1wbGVhZG9zIGRlIEZPUk0gc2Vnw7puIHN1IGVzdGFkbyBjaXZpbCBwdWVkZSBleHBsaWNhcnNlIHBvciBkaXZlcnNvcyBmYWN0b3JlcyByZWxhY2lvbmFkb3MgY29uIGVsIGNvbnRleHRvIGRlIG5lZ29jaW8gZGUgbGEgZW1wcmVzYS4gQWxndW5vcyBkZSBlc3RvcyBmYWN0b3JlcyBwb2Ryw61hbiBzZXI6DQoqIEVzdHLDqXM6IExvcyBlbXBsZWFkb3MgZGl2b3JjaWFkb3MgcHVlZGVuIGV4cGVyaW1lbnRhciBtw6FzIGVzdHLDqXMgcXVlIGxvcyBlbXBsZWFkb3MgY2FzYWRvcyBvIHNvbHRlcm9zLCBsbyBxdWUgcG9kcsOtYSBhZmVjdGFyIG5lZ2F0aXZhbWVudGUgc3Ugc2FsdWQgbWVudGFsIHkgZsOtc2ljYSB5IGNvbmR1Y2lyIGEgdW5hIG1lbm9yIHNhdGlzZmFjY2nDs24gbGFib3JhbCB5IHVuYSBtYXlvciBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWEuDQoqIERpZmljdWx0YWRlcyBmaW5hbmNpZXJhczogTG9zIGVtcGxlYWRvcyBkaXZvcmNpYWRvcyBwdWVkZW4gdGVuZXIgbcOhcyBkaWZpY3VsdGFkZXMgZmluYW5jaWVyYXMgcXVlIGxvcyBlbXBsZWFkb3MgY2FzYWRvcyBvIHNvbHRlcm9zLCBsbyBxdWUgcG9kcsOtYSBsbGV2YXJsb3MgYSBidXNjYXIgdW4gdHJhYmFqbyBjb24gdW4gc2FsYXJpbyBtw6FzIGFsdG8gbyBhIHJlbnVuY2lhciBwYXJhIGN1aWRhciBhIHN1cyBoaWpvcy4NCiogRmFsdGEgZGUgYXBveW8gc29jaWFsOiBMb3MgZW1wbGVhZG9zIGRpdm9yY2lhZG9zIHB1ZWRlbiB0ZW5lciBtZW5vcyBhcG95byBzb2NpYWwgcXVlIGxvcyBlbXBsZWFkb3MgY2FzYWRvcyBvIHNvbHRlcm9zLCBsbyBxdWUgcG9kcsOtYSBkZXNtb3RpdmFybG9zIHkgaGFjZXJsb3MgbcOhcyBwcm9wZW5zb3MgYSByZW51bmNpYXIuDQoNCmBgYHtyfQ0KYnAgPC1iYXJwbG90KG1vZGVsb19uYiR0YWJsZXMkRXN0YWRvX0NpdmlsLCBsZWdlbmQudGV4dCA9IFRSVUUsIGJlc2lkZSA9IFRSVUUsIGNvbCA9IGMoImJsYWNrIiwgImxpZ2h0Z3JheSIpLCBtYWluID0gIkVzdGFkbyBDaXZpbCIsDQogICAgICAgICAgICAgIHlsaW0gPSBjKDAsIG1heChtb2RlbG9fbmIkdGFibGVzJEVzdGFkb19DaXZpbCkgKiAxLjIpKQ0KdGV4dCh4ID0gYnAsIHkgPSBtb2RlbG9fbmIkdGFibGVzJEVzdGFkb19DaXZpbCwgbGFiZWwgPSByb3VuZChtb2RlbG9fbmIkdGFibGVzJEVzdGFkb19DaXZpbCwyKSwgcG9zID0gMykNCmBgYA0KDQoNCiMjIyMjIFNhbGFyaW8gZGlhcmlvDQpMb3MgZW1wbGVhZG9zIGNvbiB1biBzYWxhcmlvIG3DoXMgYWx0byB0aWVuZW4gdW5hIG1lbm9yIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYXIgcXVlIGxvcyBlbXBsZWFkb3MgY29uIHVuIHNhbGFyaW8gbcOhcyBiYWpvLiBMYXMgcHJvYmFiaWxpZGFkZXMgZXNwZWPDrWZpY2FzIHNvbiBsYXMgc2lndWllbnRlczoNCiogU2FsYXJpbyBkaWFyaW8gbWVub3IgYSAyMDA6IDYxJQ0KKiBTYWxhcmlvIGRpYXJpbyBlbnRyZSAyMDAgeSAzMDA6IDQ4JQ0KKiBTYWxhcmlvIGRpYXJpbyBlbnRyZSAzMDAgeSA0MDA6IDM1JQ0KKiBTYWxhcmlvIGRpYXJpbyBtYXlvciBhIDQwMDogMjIlDQoNCkxhIHJlbGFjacOzbiBpbnZlcnNhIGVudHJlIGVsIHNhbGFyaW8geSBsYSBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWEgZXMgdW4gZmVuw7NtZW5vIGNvbcO6biBlbiBlbCBtZXJjYWRvIGxhYm9yYWwuIEV4aXN0ZW4gZGl2ZXJzYXMgZXhwbGljYWNpb25lcyBwYXJhIGVzdGEgcmVsYWNpw7NuOg0KKiBTYXRpc2ZhY2Npw7NuIGxhYm9yYWw6IExvcyBlbXBsZWFkb3MgY29uIHVuIHNhbGFyaW8gbcOhcyBhbHRvIHN1ZWxlbiBlc3RhciBtw6FzIHNhdGlzZmVjaG9zIGNvbiBzdSB0cmFiYWpvIHF1ZSBsb3MgZW1wbGVhZG9zIGNvbiB1biBzYWxhcmlvIG3DoXMgYmFqby4gRXN0byBzZSBkZWJlIGEgcXVlIGVsIHNhbGFyaW8gcHVlZGUgc2F0aXNmYWNlciBuZWNlc2lkYWRlcyBiw6FzaWNhcyB5IHByb3BvcmNpb25hciB1biBtYXlvciBuaXZlbCBkZSB2aWRhLg0KKiBNb3RpdmFjacOzbjogTG9zIGVtcGxlYWRvcyBjb24gdW4gc2FsYXJpbyBtw6FzIGFsdG8gc3VlbGVuIGVzdGFyIG3DoXMgbW90aXZhZG9zIHBhcmEgdHJhYmFqYXIgZHVybyB5IGFsY2FuemFyIHN1cyBvYmpldGl2b3MuIEVzdG8gc2UgZGViZSBhIHF1ZSB2ZW4gc3UgdHJhYmFqbyBjb21vIHVuYSBmb3JtYSBkZSBhbGNhbnphciBzdXMgbWV0YXMgZmluYW5jaWVyYXMgeSBwZXJzb25hbGVzLg0KKiBPcG9ydHVuaWRhZGVzIGFsdGVybmF0aXZhczogTG9zIGVtcGxlYWRvcyBjb24gdW4gc2FsYXJpbyBtw6FzIGFsdG8gc3VlbGVuIHRlbmVyIG3DoXMgb3BvcnR1bmlkYWRlcyBkZSBlbXBsZW8gZW4gb3RyYXMgZW1wcmVzYXMuIEVzdG8gc2lnbmlmaWNhIHF1ZSBzaSBubyBlc3TDoW4gc2F0aXNmZWNob3MgY29uIHN1IHRyYWJham8gYWN0dWFsLCBlcyBtw6FzIHByb2JhYmxlIHF1ZSBidXNjcXVlbiB5L28gZW5jdWVudHJlbiBvdHJvIHRyYWJham8gbWVqb3IgcGFnYWRvLg0KYGBge3J9DQojIyBUYWJsYXMgZGUgTWVkaWEgeSBEZXN2aWFjacOzbiBFc3TDoW5kYXIgcGFyYSBWYXJpYWJsZXMgQ29udGludWFzDQpicCA8LWJhcnBsb3QobW9kZWxvX25iJHRhYmxlcyRTYWxhcmlvX2RpYXJpbywgbGVnZW5kLnRleHQgPSBUUlVFLCBiZXNpZGUgPSBUUlVFLCBjb2wgPSBjKCJibGFjayIsICJsaWdodGdyYXkiKSwgbmFtZXMuYXJnID0gYygiTWVkaWEiLCAiRGVzdmlhY2nDs24gZXN0w6FuZGFyIiksIG1haW4gPSAiU2FsYXJpb19kaWFyaW8iLA0KICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgobW9kZWxvX25iJHRhYmxlcyRTYWxhcmlvX2RpYXJpbykgKiAxLjIpKQ0KdGV4dCh4ID0gYnAsIHkgPSBtb2RlbG9fbmIkdGFibGVzJFNhbGFyaW9fZGlhcmlvLCBsYWJlbCA9IHJvdW5kKG1vZGVsb19uYiR0YWJsZXMkU2FsYXJpb19kaWFyaW8sMiksIHBvcyA9IDMpDQpgYGANCg0KDQojIyMjIyBFZGFkDQpMb3MgY29sYWJvcmFkb3JlcyBkZSBtYXlvciBlZGFkIHRpZW5lbiB1bmEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHBlcm1hbmVjZXIgZGVudHJvIGRlIGxhIGVtcHJlc2EsIHlhIHF1ZSBzdWVsZW4gdmFsb3JhciBtw6FzIGxhIGVzdGFiaWxpZGFkIGxhYm9yYWwgeSBsYXMgcmVsYWNpb25lcyBlc3RhYmxlY2lkYXMgZW4gc3UgZW50b3JubyBkZSB0cmFiYWpvLiBQb3Igb3RybyBsYWRvLCBsb3MgY29sYWJvcmFkb3JlcyBkZSBtZW5vciBlZGFkIHRpZW5kZW4gYSBjb25zaWRlcmFyIGxhIHJlbnVuY2lhIGNvbiBtYXlvciBmcmVjdWVuY2lhLCBwb3NpYmxlbWVudGUgZGViaWRvIGEgbGEgbWF5b3IgbGliZXJ0YWQgZSBpbmRlcGVuZGVuY2lhIHF1ZSBjYXJhY3Rlcml6YSBhIGxvcyBhZHVsdG9zIGrDs3ZlbmVzLiBFc3RhIGRpZmVyZW5jaWEgZW4gbGEgcHJvcGVuc2nDs24gYSBwZXJtYW5lY2VyIG8gcmVudW5jaWFyIHB1ZWRlIGVzdGFyIGluZmx1ZW5jaWFkYSBwb3IgbGFzIGRpc3RpbnRhcyBwcmlvcmlkYWRlcyB5IGV4cGVjdGF0aXZhcyBxdWUgdGllbmVuIGxvcyBlbXBsZWFkb3MgZW4gZGlmZXJlbnRlcyBldGFwYXMgZGUgc3VzIHZpZGFzIHByb2Zlc2lvbmFsZXMuIEVuIHJlc3VtZW4sIGxvcyBlbXBsZWFkb3MgbcOhcyBqw7N2ZW5lcyB0aWVuZW4gdW5hIG1lbm9yIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYXIgcXVlIGxvcyBlbXBsZWFkb3MgbcOhcyBtYXlvcmVzLiBMYXMgcHJvYmFiaWxpZGFkZXMgZXNwZWPDrWZpY2FzIHNvbiBsYXMgc2lndWllbnRlczoNCiogTWVub3IgYSAyNTogMzglDQoqIEVudHJlIDI1IHkgMzA6IDQ1JQ0KKiBFbnRyZSAzMCB5IDM1OiA1MiUNCiogRW50cmUgMzUgeSA0MDogNTklDQoqIE1heW9yIGEgNDA6IDY1JQ0KDQpgYGB7cn0NCmJwIDwtYmFycGxvdChtb2RlbG9fbmIkdGFibGVzJEVkYWQsIGxlZ2VuZC50ZXh0ID0gVFJVRSwgYmVzaWRlID0gVFJVRSwgY29sID0gYygiYmxhY2siLCAibGlnaHRncmF5IiksIG1haW4gPSAiRWRhZCIsIG5hbWVzLmFyZyA9IGMoIk1lZGlhIiwgIkRlc3ZpYWNpw7NuIGVzdMOhbmRhciIpLA0KICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgobW9kZWxvX25iJHRhYmxlcyRFZGFkKSAqIDEuMikpDQp0ZXh0KHggPSBicCwgeSA9IG1vZGVsb19uYiR0YWJsZXMkRWRhZCwgbGFiZWwgPSByb3VuZChtb2RlbG9fbmIkdGFibGVzJEVkYWQsMiksIHBvcyA9IDMpDQpgYGANCg0KDQojIyMjIyBNZXMNCkxhIHJldGVuY2nDs24gZGUgZW1wbGVhZG9zIGVuIGxhIGVtcHJlc2EgRk9STSBtdWVzdHJhIHVuYSB0ZW5kZW5jaWEgY2xhcmEgZW4gZnVuY2nDs24gZGVsIG1lcyBkZSBjb250cmF0YWNpw7NuLiBMb3MgY29sYWJvcmFkb3JlcyBjb250cmF0YWRvcyBkdXJhbnRlIGxvcyBtZXNlcyBkZSBhYnJpbCwgZmVicmVybywgbWFyem8sIG1heW8geSBvY3R1YnJlIHRpZW5lbiB1bmEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHBlcm1hbmVjZXIgZW4gbGEgZW1wcmVzYSwgY29uIHVuYSByZXRlbmNpw7NuIHBhcnRpY3VsYXJtZW50ZSBhbHRhIGVuIGFicmlsLiBFbiBjb250cmFzdGUsIGFxdWVsbG9zIHF1ZSBpbmljaWFuIHN1IHRyYWJham8gZW4gbG9zIG1lc2VzIGRlIGFnb3N0bywganVuaW8sIGp1bGlvIHkgc2VwdGllbWJyZSBwcmVzZW50YW4gdW5hIG1heW9yIHRlbmRlbmNpYSBhIGNvbnNpZGVyYXIgbGEgcmVudW5jaWEuIEVzdG8gc3VnaWVyZSBxdWUgbGEgcGxhbmlmaWNhY2nDs24gZXN0cmF0w6lnaWNhIGRlIGxhcyBjb250cmF0YWNpb25lcyBlbiBtZXNlcyBlc3BlY8OtZmljb3MgcHVlZGUgaW5mbHVpciBzaWduaWZpY2F0aXZhbWVudGUgZW4gbGEgZXN0YWJpbGlkYWQgeSBsYSBwZXJtYW5lbmNpYSBkZWwgcGVyc29uYWwuIERlIGhlY2hvLCBsYSBkaWZlcmVuY2lhIGRlIHByb2JhYmlsaWRhZGVzIGRlIHJlbnVuY2lhIGVudHJlIGxvcyBlbXBsZWFkb3MgZGUgRk9STSBzZWfDum4gc3UgbWVzIGRlIGNvbnRyYXRhY2nDs24gcHVlZGUgZXhwbGljYXJzZSBwb3IgZGl2ZXJzb3MgZmFjdG9yZXMgY29tbzoNCiogMS4gQ29udHJhdGFjaW9uZXMgZXN0YWNpb25hbGVzOiBGT1JNIHBvZHLDrWEgcmVhbGl6YXIgY29udHJhdGFjaW9uZXMgZXN0YWNpb25hbGVzIHBhcmEgc2F0aXNmYWNlciB1bmEgbWF5b3IgZGVtYW5kYSBkZSBwcm9kdWN0b3MgbyBzZXJ2aWNpb3MgZHVyYW50ZSBsb3MgbWVzZXMgZGUgdmVyYW5vLCBjdWFuZG8gbGFzIHBlcnNvbmFzIHN1ZWxlbiB0ZW5lciBtw6FzIHRpZW1wbyBsaWJyZSB5IGRpbmVybyBwYXJhIGdhc3Rhci4gRXN0b3MgZW1wbGVhZG9zIGNvbnRyYXRhZG9zIGVzdGFjaW9uYWxtZW50ZSBwb2Ryw61hbiBzZXIgbWVub3MgcHJvcGVuc29zIGEgcGVybWFuZWNlciBlbiBsYSBlbXByZXNhIGRlc3B1w6lzIGRlIGxhIHRlbXBvcmFkYSBhbHRhLCBsbyBxdWUgZXhwbGljYXLDrWEgbGEgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHJlbnVuY2lhIGVuIGxvcyBtZXNlcyBkZSB2ZXJhbm8uICANCiogMi4gRmFsdGEgZGUgY2FwYWNpdGFjacOzbjogRk9STSBwb2Ryw61hIG5vIHByb3BvcmNpb25hciB1bmEgY2FwYWNpdGFjacOzbiBhZGVjdWFkYSBhIGxvcyBlbXBsZWFkb3MgY29udHJhdGFkb3MgZHVyYW50ZSBsb3MgbWVzZXMgZGUgdmVyYW5vLCBsbyBxdWUgcG9kcsOtYSBnZW5lcmFyIGRpZmljdWx0YWRlcyBlbiBzdSB0cmFiYWpvIHkgdW5hIG1lbm9yIHNhdGlzZmFjY2nDs24gbGFib3JhbC4gRXN0byBwb2Ryw61hIGxsZXZhciBhIHVuYSBtYXlvciBwcm9iYWJpbGlkYWQgZGUgcmVudW5jaWEgZW4gZXN0b3MgbWVzZXMuICANCiogMy4gRXhwZWN0YXRpdmFzIG5vIGN1bXBsaWRhczogTG9zIGVtcGxlYWRvcyBjb250cmF0YWRvcyBkdXJhbnRlIGxvcyBtZXNlcyBkZSB2ZXJhbm8gcG9kcsOtYW4gdGVuZXIgZXhwZWN0YXRpdmFzIHBvY28gcmVhbGlzdGFzIHNvYnJlIHN1IHRyYWJham8sIGNvbW8gdW4gbWF5b3Igc2FsYXJpbyBvIG3DoXMgb3BvcnR1bmlkYWRlcyBkZSBjcmVjaW1pZW50by4gQ3VhbmRvIGVzdGFzIGV4cGVjdGF0aXZhcyBubyBzZSBjdW1wbGVuLCBsb3MgZW1wbGVhZG9zIHBvZHLDrWFuIHNlbnRpcnNlIGZydXN0cmFkb3MgeSByZW51bmNpYXIuICANCiogNC4gRmFjdG9yZXMgZXh0ZXJub3M6IExhIG1heW9yIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYSBlbiBsb3MgbWVzZXMgZGUgdmVyYW5vIHRhbWJpw6luIHBvZHLDrWEgZXN0YXIgcmVsYWNpb25hZGEgY29uIGZhY3RvcmVzIGV4dGVybm9zLCBjb21vIGVsIGNsaW1hIGPDoWxpZG8gbyBsYXMgdmFjYWNpb25lcyBlc2NvbGFyZXMuIEVzdG9zIGZhY3RvcmVzIHBvZHLDrWFuIGhhY2VyIHF1ZSBsb3MgZW1wbGVhZG9zIHNlYW4gbcOhcyBwcm9wZW5zb3MgYSBidXNjYXIgb3RyYXMgb3BvcnR1bmlkYWRlcyBvIGEgdG9tYXIgdW4gdGllbXBvIGxpYnJlLCBsbyBxdWUgcG9kcsOtYSBjb25kdWNpciBhIHVuYSBtYXlvciByb3RhY2nDs24gZGUgcGVyc29uYWwuDQoNCmBgYHtyfQ0KYnAgPC0gYmFycGxvdChtb2RlbG9fbmIkdGFibGVzJE5vbWJyZU1lcywgbGVnZW5kLnRleHQgPSBUUlVFLCBiZXNpZGUgPSBUUlVFLCBjb2wgPSBjKCJibGFjayIsICJsaWdodGdyYXkiKSwgbWFpbiA9ICJNZXMgZGUgQ29udHJhdGFjacOzbiIsIGNleC5uYW1lcyA9IDAuOCkNCnRleHQoeCA9IGJwLCB5ID0gbW9kZWxvX25iJHRhYmxlcyROb21icmVNZXMsIGxhYmVsID0gcm91bmQobW9kZWxvX25iJHRhYmxlcyROb21icmVNZXMsMiksIHBvcyA9IDMpDQpgYGANCg0KDQojIyMjIFByZWRpY2Npw7NuIHkgUmVzdWx0YWRvcw0KR3JhY2lhcyBhIGxhIG1hdHJpeiBkZSBjb25mdXNpw7NuIGVzIHBvc2libGUgZGV0ZXJtaW5hciBxdWUgZWwgbW9kZWxvIHByZWRpY2UgYWNlcnRhZGFtZW50ZSBlbiB1biA5MC45MSUgZGUgbG9zIGNhc29zIGVuIHF1ZSB1biBjb2xhYm9yYWRvciBkZWNpZGUgcmVudW5jaWFyLiBKdW50byBhIGVzdG8sIHNlIG9idGllbmUgdW4gdmFsb3IgZGUgS2FwcGEgZGUgMC43NzU0LCBtb3N0cmFuZG8gcXVlIGVsIG1vZGVsbyBlc3TDoSBwcmVkaWNpZW5kbyBjb25zaWRlcmFibGVtZW50ZSBtZWpvciBhIHVuYSBjbGFzaWZpY2FjacOzbiBhbGVhdG9yaWEuDQpgYGB7cn0NCiMgUHJlZGljY2nDs24NCnByZWRpY2Npb25fbmIgPC0gcHJlZGljdChtb2RlbG9fbmIsIHRlc3QpDQoNCiMgTWF0cml6IGRlIENvbmZ1c2nDs24NCmNvbmZ1c2lvbl9uYiA8LSBjb25mdXNpb25NYXRyaXgocHJlZGljY2lvbl9uYiwgdGVzdCRSZW51bmNpYSxwb3NpdGl2ZSA9ICJObyIpDQpwcmludChjb25mdXNpb25fbmIpDQpgYGANCg0KU2Ugb2J0aWVuZSB1biB2YWxvciBkZSBBVUMgZGVsIDAuODk3MSwgcmVhZmlybWFuZG8gcXVlIGxhIHByZWRpY2Npw7NuIGRlbCBtb2RlbG8gZXMgbWVqb3IgcXVlIHVuYSBwcmVkaWNjacOzbiBhbGVhdG9yaWENCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojICBDdXJ2YSBST0MgDQojIyBPYnRlbmVyIGxhcyBwdW50dWFjaW9uZXMgZGUgcHJvYmFiaWxpZGFkDQpwcmVkaWNjaW9uX3Byb2JfbmIgPC0gcHJlZGljdChtb2RlbG9fbmIsIHRlc3QsIHR5cGUgPSAicmF3IikNCg0KIyMgU2VsZWNjaW9uYXIgbGEgY29sdW1uYSBxdWUgY29ycmVzcG9uZGUgYSBsYSBjbGFzZSAnTm8nDQpwcmVkaWNjaW9uX3Byb2JfdHJ1ZV9uYiA8LSBwcmVkaWNjaW9uX3Byb2JfbmJbLCAiTm8iXQ0KDQojIyBHZW5lcmFyIGxhIGN1cnZhIFJPQw0Kcm9jX29ial9uYiA8LSByb2ModGVzdCRSZW51bmNpYSwgcHJlZGljY2lvbl9wcm9iX3RydWVfbmIpDQoNCiMjIERpYnVqYXIgbGEgY3VydmEgUk9DDQpwbG90LnJvYyhyb2Nfb2JqX25iLCBtYWluPSJDdXJ2YSBST0MiLCBjb2w9ImJsdWUiKQ0KDQojIyBDYWxjdWxhciBlbCBBVUMNCmF1Y19uYiA8LSBwUk9DOjphdWMocm9jX29ial9uYikNCmF1Y19uYg0KYGBgDQoNCiMjIyBNb2RlbG8gNCBSYW5kb20gRm9yZXN0DQoNCkVsIG1vZGVsbyBkZSBSYW5kb20gRm9yZXN0IGZ1ZSBzZWxlY2Npb25hZG8gY29uIGxhIGludGVuY2nDs24gZGUgZW1wbGVhciB1biBtb2RlbG8gY29tcGxlam8gY2FwYXogZGUgdGVuZXIgdW5hIGFsdGEgY2FwYWNpZGFkIHByZWRpY3RpdmEgYSBjYW1iaW8gZGUgcGVyZGVyIGludGVycHJldGFiaWxpZGFkLCBidXNjYW5kbyBjb21wYXJhciBkZSBlc3RhIG1hbmVyYSBlbCBkZXNlbXBlw7FvIGRlIG1vZGVsb3MgZGUgbWF5b3Igc2ltcGxpY2lkYWQgY29uIHVubyBkZSBhbHRhIGNvbXBsZWppZGFkLiBMb3MgcGFyw6FtZXRyb3Mgc2VsZWNjaW9uYWRvcyBmdWVyb24gdW4gZXN0w6FuZGFyIHNlbGVjY2lvbmFkbyBwYXJhIGxhIGNvbmZpZ3VyYWNpw7NuIGRlbCByYW5kb20gZm9yZXN0Lg0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCg0KIyBDcmVhY2nDs24gZGVsIG1vZGVsbw0KcmZfbW9kZWwgPC0gcmFuZG9tRm9yZXN0KGZvcm11bGExLCBkYXRhID0gdHJhaW4sIG50cmVlPTUwMCwgbXRyeT01LCBub2Rlc2l6ZT01LCBpbXBvcnRhbmNlPVRSVUUpDQpgYGANCg0KIyMjIyBJbnRlcnByZXRhY2nDs24NCg0KQSBwZXNhciBkZWwgdHJhZGVvZmYgZW50cmUgaW50ZXJwcmV0YWJpbGlkYWQgeSBwcmVjaXNpw7NuLCBlbCBtb2RlbG8gb2ZyZWNlIHVuYSB2aXN1YWxpemFjacOzbiBwYXJhIGNvbXByb2JhciBlbCBpbXBhY3RvIHF1ZSBlc3TDoSBlamVyY2llbmRvIGNhZGEgdmFyaWFibGUgcGFyYSBkZXRlcm1pbmFyIGxhIHBlcnRlbmVuY2lhIGEgdW5hIGRldGVybWluYWRhIGNsYXNlIChSZW51bmNpYSBvIE5vIHJlbnVuY2lhKS4gU2luIGVtYmFyZ28sIGVzdG8gbm8gY29udGVtcGxhIHVuYSBkaXJlY2Npw7NuIGNsYXJhIGRlbCBpbXBhY3RvIGVqZXJjaWRvLiBDb25zaWRlcmFuZG8gZXN0bywgc2UgZGV0ZXJtaW5hIHF1ZSBsYXMgdmFyaWFibGVzIHF1ZSB0aWVuZW4gbWF5b3IgaW5mbHVlbmNpYSBlbiBlbCBtb2RlbG8gY29ycmVzcG9uZGVuIGE6IFNhbGFyaW8gZGlhcmlvLCBkZXBhcnRhbWVudG8geSBlbCBtZXMgZGUgY29udHJhdGFjacOzbiwgc2llbmRvIHBvY28gcmVsZXZhbnRlcyBlbCBnw6luZXJvIHkgZWwgZXN0YWRvIGNpdmlsIChkZSBhY3VlcmRvIGFsIG1vZGVsbykuDQoNCmBgYHtyfQ0KdmFySW1wUGxvdChyZl9tb2RlbCkNCmBgYA0KDQoNCiMjIyMgUHJlZGljY2nDs24geSBSZXN1bHRhZG9zDQpFbCBtb2RlbG8gbG9ncmEgcHJlZGVjaXIgYWNlcnRhZGFtZW50ZSBlbiB1biA5MCUgZGUgbG9zIGNhc29zIGVuIHF1ZSB1biBjb2xhYm9yYWRvciBkZWNpZGUgcmVudW5jaWFyLiBFbCBuaXZlbCBkZSBrYXBwYSBlcyBkZSAwLjc0MDIsIHN1cGVyYW5kbyBhIHVuYSBjbGFzaWZpY2FjacOzbiBhbGVhdG9yaWEuDQpgYGB7cn0NCiMgUHJlZGljY2nDs24NCnByZWRpY2Npb25fcmYgPC0gcHJlZGljdChyZl9tb2RlbCwgdGVzdCkNCg0KIyBNYXRyaXogZGUgQ29uZnVzacOzbg0KY29uZnVzaW9uX3JmIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWNjaW9uX3JmLCB0ZXN0JFJlbnVuY2lhLHBvc2l0aXZlID0gIk5vIikNCnByaW50KGNvbmZ1c2lvbl9yZikNCmBgYA0KDQpUaWVuZSB1biB2YWxvciBkZSBBVUMgZGUgMC45NiwgcmVhZmlybWFuZG8gc3Ugc3VwZXJpb3JpZGFkIHNvYnJlIHVuYSBwcmVkaWNjacOzbiBhbGVhdG9yaWEuDQpgYGB7cn0NCiMgQ3VydmEgUk9DIA0KIyMgT2J0ZW5lciBsYXMgcHVudHVhY2lvbmVzIGRlIHByb2JhYmlsaWRhZA0KcHJlZGljY2lvbl9wcm9iX3JmIDwtIHByZWRpY3QocmZfbW9kZWwsIHRlc3QsIHR5cGUgPSAicHJvYiIpDQoNCiMjIFNlbGVjY2lvbmFyIGxhIGNvbHVtbmEgcXVlIGNvcnJlc3BvbmRlIGEgbGEgY2xhc2UgJ05vJw0KcHJlZGljY2lvbl9wcm9iX3RydWVfcmYgPC0gcHJlZGljY2lvbl9wcm9iX3JmWywgIk5vIl0NCg0KIyMgR2VuZXJhciBsYSBjdXJ2YSBST0MNCnJvY19vYmpfcmYgPC0gcm9jKHRlc3QkUmVudW5jaWEsIHByZWRpY2Npb25fcHJvYl90cnVlX3JmKQ0KDQojIyBEaWJ1amFyIGxhIGN1cnZhIFJPQw0KcGxvdC5yb2Mocm9jX29ial9yZiwgbWFpbj0iQ3VydmEgUk9DIiwgY29sPSJibHVlIikNCg0KIyMgQ2FsY3VsYXIgZWwgQVVDDQphdWNfcmYgPC0gcFJPQzo6YXVjKHJvY19vYmpfcmYpDQphdWNfcmYNCmBgYA0KDQojIyBDb21wYXJhY2nDs24gZGUgbW9kZWxvcw0KYGBge3J9DQojIE9idGVuZXIgY2FkYSB1bmEgZGUgbGFzIG3DqXRyaWNhcw0KI0FDQ1VSQUNZDQphY2NfUk08LSBjb25mdXNpb25fZ2xtJG92ZXJhbGxbIkFjY3VyYWN5Il0NCmFjY19BPC0gY29uZnVzaW9uX0Ekb3ZlcmFsbFsiQWNjdXJhY3kiXQ0KYWNjX05CPC0gY29uZnVzaW9uX25iJG92ZXJhbGxbIkFjY3VyYWN5Il0NCmFjY19SRjwtIGNvbmZ1c2lvbl9yZiRvdmVyYWxsWyJBY2N1cmFjeSJdDQoNCiNCQUxBTkNFRCBBQ0NVUkFDWQ0KQl9hY2NfUk08LSBjb25mdXNpb25fZ2xtJGJ5Q2xhc3NbIkJhbGFuY2VkIEFjY3VyYWN5Il0NCkJfYWNjX0E8LSBjb25mdXNpb25fQSRieUNsYXNzWyJCYWxhbmNlZCBBY2N1cmFjeSJdDQpCX2FjY19OQjwtIGNvbmZ1c2lvbl9uYiRieUNsYXNzWyJCYWxhbmNlZCBBY2N1cmFjeSJdDQpCX2FjY19SRjwtIGNvbmZ1c2lvbl9yZiRieUNsYXNzWyJCYWxhbmNlZCBBY2N1cmFjeSJdDQoNCiNLQVBQQQ0Ka2FwcGFfUk08LSBjb25mdXNpb25fZ2xtJG92ZXJhbGxbIkthcHBhIl0NCmthcHBhX0E8LSBjb25mdXNpb25fQSRvdmVyYWxsWyJLYXBwYSJdDQprYXBwYV9OQjwtIGNvbmZ1c2lvbl9uYiRvdmVyYWxsWyJLYXBwYSJdDQprYXBwYV9SRjwtIGNvbmZ1c2lvbl9yZiRvdmVyYWxsWyJLYXBwYSJdDQoNCiNTRU5TSVRJVklUWQ0Kc2Vuc19STTwtIGNvbmZ1c2lvbl9nbG0kYnlDbGFzc1siU2Vuc2l0aXZpdHkiXQ0Kc2Vuc19BPC0gY29uZnVzaW9uX0EkYnlDbGFzc1siU2Vuc2l0aXZpdHkiXQ0Kc2Vuc19OQjwtIGNvbmZ1c2lvbl9uYiRieUNsYXNzWyJTZW5zaXRpdml0eSJdDQpzZW5zX1JGPC0gY29uZnVzaW9uX3JmJGJ5Q2xhc3NbIlNlbnNpdGl2aXR5Il0NCg0KI1NQRUNJRklUWQ0Kc3BlY19STTwtIGNvbmZ1c2lvbl9nbG0kYnlDbGFzc1siU3BlY2lmaWNpdHkiXQ0Kc3BlY19BPC0gY29uZnVzaW9uX0EkYnlDbGFzc1siU3BlY2lmaWNpdHkiXQ0Kc3BlY19OQjwtIGNvbmZ1c2lvbl9uYiRieUNsYXNzWyJTcGVjaWZpY2l0eSJdDQpzcGVjX1JGPC0gY29uZnVzaW9uX3JmJGJ5Q2xhc3NbIlNwZWNpZmljaXR5Il0NCg0KI0FVQw0KYXVjX1JNPC0gYXVjX2dsbQ0KYXVjX0E8LSBhdWNfdHJlZQ0KYXVjX05CPC0gYXVjX25iDQphdWNfcmY8LWF1Y19yZg0KDQojIENyZWFjacOzbiBkZSB2ZWN0b3JlcyBwYXJhIGxhcyBtw6l0cmljYXMgZGUgY2FkYSBtb2RlbG8NCm1ldHJpY2FzIDwtIGMoIkFjY3VyYWN5IiwgIkJhbGFuY2VkIEFjY3VyYWN5IiwgIkthcHBhIiwgIlNlbnNpdGl2aXR5IiwgIlNwZWNpZmljaXR5IiwgIkFVQyIpDQpnbG1fdmFsdWVzIDwtIGMoYWNjX1JNLCBCX2FjY19STSwga2FwcGFfUk0sIHNlbnNfUk0sIHNwZWNfUk0sIGF1Y19STSkNCmFyYm9sX3ZhbHVlcyA8LSBjKGFjY19BLCBCX2FjY19BLCBrYXBwYV9BLCBzZW5zX0EsIHNwZWNfQSwgYXVjX0EpDQpuYWl2ZUJheWVzX3ZhbHVlcyA8LSBjKGFjY19OQiwgQl9hY2NfTkIsIGthcHBhX05CLCBzZW5zX05CLCBzcGVjX05CLCBhdWNfTkIpDQpyYW5kb21mb3Jlc3RfdmFsdWVzIDwtIGMoYWNjX1JGLCBCX2FjY19SRiwga2FwcGFfUkYsIHNlbnNfUkYsIHNwZWNfUkYsIGF1Y19yZikNCmBgYA0KDQojIyMgVGFibGEgQ29tcGFyYXRpdmEgZGUgTcOpdHJpY2FzDQoNClRyYXMgbGEgY29uc3RydWNjacOzbiBkZSBkaXN0aW50b3MgbW9kZWxvcyBwYXJhIGxhIHNpdHVhY2nDs24gcHJvYmxlbWEgZGUgRm9ybSBmdWUgcG9zaWJsZSBvYnRlbmVyIGxhcyBzaWd1aWVudGVzIG1lZGlkYXMgZGUgZGVzZW1wZcOxbyBwYXJhIGNhZGEgdW5vIGRlIGVsbG9zOiAqQWNjdXJhY3kqLCAqQmFsYW5jZWQgQWNjdXJhY3kqLCAqS2FwcGEqLCAqU2Vuc2l0aXZpdHkqLCAqU3BlY2lmaWNpdHkqLCAqQVVDKi4gRW4gY29uc2lkZXJhY2nDs24gZGUgZXN0b3MsIHNlIHBsYW50ZWEgbGEgc2lndWllbnRlIGV2YWx1YWNpw7NuOg0KDQoqICoqMS4gQWNjdXJhY3k6KiogSW5pY2lhbG1lbnRlLCBjb21vIHVuYSBtZWRpZGEgcsOhcGlkYSBzb2JyZSBlbCBkZXNlbXBlw7FvIGdlbmVyYWwgZGUgbG9zIG1vZGVsb3Mgc2UgY29uc2lkZXJhIGVsIEFjY3VyYWN5IEdlbmVyYWwsIGVzIGRlY2lyLCBlbCBwb3JjZW50YWplIGRlIGFjaWVydG9zIGRlbCBtb2RlbG8gc29icmUgZWwgdG90YWwgZGUgcHJlZGljY2lvbmVzLiBFbiBlc3RlIGNhc28sIGxvcyBtb2RlbG9zIHRpZW5lbiB1biBkZXNlbXBlw7FvIGJhc3RhbnRlIHNpbWlsYXIsIGVzdGFuZG8gZW4gdW4gcmFuZG8gZW50cmUgZWwgODctOTAuOSUuIFNpbiBlbWJhcmdvLCBlbCBtb2RlbG8gY29uIGVsIG1lam9yIGRlc2VtcGXDsW8gY29ycmVzcG9uZGUgYSBOYWl2ZSBCYXllcy4gDQoNCiogKioyLiBLYXBwYToqKiBDb21vIHNpZ3VpZW50ZSBtZWRpZGEsIGVzIG5lY2VzYXJpbyBldmFsdWFyIGVsIHF1ZSBjYWRhIHVubyBkZSBsb3MgbW9kZWxvcyBmdWVyYSBjYXBheiBkZSByZWFsaXphciB1bmEgcHJlZGljY2nDs24gbWVqb3IgYSB1bmEgY2xhc2lmaWNhY2nDs24gYWxlYXRvcmlhLCBlcyBkZWNpciwgdGVuZXIgdW4gdmFsb3IgS2FwcGEgbWF5b3IgYSAwLiBFbiBlc3RlIGNhc28sIGNhZGEgdW5vIGRlIGxvcyBtb2RlbG9zIGNvbnN0cnVpZG9zIG9idHV2byBhbHRvcyB2YWxvcmVzIHBhcmEgZXN0YSBtZWRpZGEsIHN1cGVyaW9yZXMgYSAwLjYsIGxvIGN1YWwgZXMgdW4gcHJpbWVyIGJ1ZW4gaW5kaWNhdGl2byBkZWwgbW9kZWxvLg0KDQoqICoqMy4gQmFsYW5jZWQgQWNjdXJhY3k6KiogVHJhcyBhZmlybWFyIHF1ZSBsb3MgbW9kZWxvcyBubyBlc3TDoW4gcmVhbGl6YW5kbyBwcmVkaWNjaW9uZXMgYWxlYXRvcmlhcywgc2UgY29tcHJ1ZWJhIG1lZGlhbnRlIGVsIEJhbGFuY2VkIEFjY3VyYWN5IGxhIHBvbmRlcmFjacOzbiBkZSBsYXMgcHJlZGljY2lvbmVzIGNvcnJlY3RhcyBkZSBhbWJhcyBjbGFzZXMsIGRlc3RhY2FuZG8gbnVldmFtZW50ZSBlbCBtb2RlbG8gZGUgTmFpdmUgQmF5ZXMgY29tbyBhcXVlbCBxdWUgbWFudGllbmUgZWwgcG9yY2VudGFqZSBtw6FzIGFsdG8gY29uIDg4LjclLg0KDQoqICoqNC4gU2Vuc2l0aXZpdHkgeSBTcGVjaWZpY2l0eToqKiBNZWRpYW50ZSBlc3RhcyBtZWRpZGFzIHNlIGFuYWxpemFyw6FuIGluZGl2aWR1YWxtZW50ZSBsYXMgcHJlZGljY2lvbmVzIGNvcnJlY3RhcyBkZSBjYWRhIGNsYXNlLiBTaWVuZG8gc2Vuc2l0aXZpdHkgZWwgcG9yY2VudGFqZSBkZSBwcmVkaWNjaW9uZXMgY29ycmVjdGFzIHBhcmEgbG9zIGVtcGxlYWRvcyBxdWUgbm8gcmVudW5jaWFuLCB5IHNwZWNpZmljaXR5LCBsYXMgcHJlZGljY2lvbmVzIGNvcnJlY3RhcyBwYXJhIGxvcyBxdWUgc8OtIHJlbnVuY2lhbi4gRW4gZXN0ZSBjYXNvLCBOYWl2ZSBCYXllcyBvYnRpZW5lIGVsIHZhbG9yIG3DoXMgYWx0byBkZSBzZW5zaXRpdml0eSBjb24gODMuODclIHNlZ3VpZG8gZGVsIMOBcmJvbCBkZSBEZWNpc2nDs24gY29uIDc3LjQlLiBQb3Igb3RybyBsYWRvLCBlbCBtb2RlbG8gZGUgUmFuZG9tIEZvcmVzdCBvYnRpZW5lIGVsIHZhbG9yIG3DoXMgYWx0byBkZSBzcGVjaWZpY2l0eSBjb24gOTYuMiUsIHNlZ3VpZG8gZGUgTmFpdmUgQmF5ZXMgY29uIDkzLjY3JS4NCg0KKiAqKjUuIEFVQyB5IEN1cnZhIFJPQzoqKiBGaW5hbG1lbnRlLCBsYSDDumx0aW1hIG3DqXRyaWNhIGEgcmV2aXNhciBwYXJhIGVzdGUgY2FzbyBlcyBlbCDDgXJlYSBiYWpvIGxhIEN1cnZhIChBVUMpLCBvYnRlbmlkYSBkZSBsYSBjdXJ2YSBST0MuIExhIGN1cnZhIFJPQyBtdWVzdHJhIGxhIGRpc3RpbmNpw7NuIGVudHJlIHZlcmRhZGVyb3MgeSBmYWxzb3MgcG9zaXRpdm9zIHBvciBwYXJ0ZSBkZWwgbW9kZWxvLCBidXNjYW5kbyBxdWUgZXN0YSBzZWEgbG8gbcOhcyByZWN0YSB5IGdyYW5kZSBwb3NpYmxlLCBwZXJtaXRpZW5kbyBhbGNhbnphciB1biDDoXJlYSBsbyBtw6FzIGNlcmNhbmEgcG9zaWJsZSBhIDEuIFBhcmEgZXN0YSBtw6l0cmljYSBlbCBtb2RlbG8gZGUgUmFuZG9tIEZvcmVzdCBlcyBhcXVlbCBxdWUgbWVqb3Igc2UgZGVzZW1wZcOxbyBhbCBoYWNlciBsYSBkaXN0aW5jacOzbiwgY29uIHVuIEFVQyBkZSAwLjk2IChtdXkgY2VyY2FubyBhIDE6IGFqdXN0ZSBjYXNpIHBlcmZlY3RvKSwgc2VndWlkbyBkZWwgw4FyYm9sIGRlIERlY2lzacOzbiBjb24gMC45Mi4NCg0KYGBge1J9DQojIENyZWFyIGVsIGRhdGFmcmFtZSBjb21wYXJhdGl2bw0KVGFibGFDb21wYXJhY2lvbiA8LSBkYXRhLmZyYW1lKA0KICBNZXRyaWNhID0gbWV0cmljYXMsICANCiAgUmVncmVzacOzbkxvZ2lzdGljYSA9IGdsbV92YWx1ZXMsICANCiAgQXJib2xkZURlY2lzaW9uID0gYXJib2xfdmFsdWVzLCAgDQogIE5haXZlQmF5ZXMgPSBuYWl2ZUJheWVzX3ZhbHVlcywNCiAgUmFuZG9tRm9yZXN0ID0gcmFuZG9tZm9yZXN0X3ZhbHVlcw0KKQ0Kcm93bmFtZXMoVGFibGFDb21wYXJhY2lvbikgPSBOVUxMDQpUYWJsYUNvbXBhcmFjaW9uIDwtIGhlYWQoVGFibGFDb21wYXJhY2lvbiwgNikNCg0Ka2FibGUoVGFibGFDb21wYXJhY2lvbikNCmBgYA0KDQojIyMgQ29tcGFyYWNpw7NuIGRlIEN1cnZhcyBST0MNCkxhcyBjdXJ2YXMgUk9DIChSZWNlaXZlciBPcGVyYXRpbmcgQ2hhcmFjdGVyaXN0aWMpIHNvbiB1bmEgaGVycmFtaWVudGEgZ3LDoWZpY2EgcXVlIHNlIHV0aWxpemEgcGFyYSBldmFsdWFyIGVsIHJlbmRpbWllbnRvIGRlIHVuIG1vZGVsbyBkZSBjbGFzaWZpY2FjacOzbiBiaW5hcmlhLCBlbiBlc3RlIGNhc28gc2kgZWwgZW1wbGVhZG8gZGUgRk9STSBkZWNpZGUgc2VndWlyIG8gbm8gZW4gbGEgZW1wcmVzYS4gQSBjb250aW51YWNpw7NuLCBlbiBsYXMgY3VydmFzIFJPQyBxdWUgc2UgbXVlc3RyYW4gZW4gbGEgaW1hZ2VuLCBzZSBlc3TDoW4gY29tcGFyYW5kbyBsb3MgY3VhdHJvIG1vZGVsb3MgZGUgY2xhc2lmaWNhY2nDs246IFJlZ3Jlc2nDs24sIMOBcmJvbCBkZSBEZWNpc2lvbiwgTmFpdmUgQmF5ZXMgeSBSYW5kb20gRm9yZXN0Lg0KDQoNCiogKioxLiBDdXJ2YSBST0MgZGUgUmVncmVzacOzbjoqKiBMYSBjdXJ2YSB0aWVuZSB1bmEgYmFqYSBlc3BlY2lmaWNpZGFkLCBsbyBxdWUgc2lnbmlmaWNhIHF1ZSBlbCBtb2RlbG8gZXMgcHJvcGVuc28gYSBjbGFzaWZpY2FyIGVycsOzbmVhbWVudGUgbG9zIG5lZ2F0aXZvcyBjb21vIHBvc2l0aXZvcy4gRXN0byBzZSBwdWVkZSBvYnNlcnZhciBlbiBsYSBwYXJ0ZSBpbmZlcmlvciBpenF1aWVyZGEgZGUgbGEgY3VydmEsIGRvbmRlIGxhIGN1cnZhIHNlIGFjZXJjYSBhIGxhIGzDrW5lYSBkaWFnb25hbC4NCg0KKiAqKjIuIEN1cnZhIFJPQyBkZSDDgXJib2w6KiogVGllbmUgdW5hIGVzcGVjaWZpY2lkYWQgbcOhcyBhbHRhIHF1ZSBsYSBjdXJ2YSBST0MgZGUgUmVncmVzacOzbiwgbG8gcXVlIHNpZ25pZmljYSBxdWUgZWwgbW9kZWxvIGVzIG1lbm9zIHByb3BlbnNvIGEgY2xhc2lmaWNhciBlcnLDs25lYW1lbnRlIGxvcyBuZWdhdGl2b3MgY29tbyBwb3NpdGl2b3MuIFNpbiBlbWJhcmdvLCBsYSBjdXJ2YSBST0MgZGUgw4FyYm9sIHRhbWJpw6luIHRpZW5lIHVuYSBzZW5zaWJpbGlkYWQgbcOhcyBiYWphLCBsbyBxdWUgc2lnbmlmaWNhIHF1ZSBlbCBtb2RlbG8gZXMgbWVub3MgcHJvcGVuc28gYSBjbGFzaWZpY2FyIGNvcnJlY3RhbWVudGUgbG9zIHBvc2l0aXZvcy4gRXN0byBzZSBwdWVkZSBvYnNlcnZhciBlbiBsYSBwYXJ0ZSBzdXBlcmlvciBpenF1aWVyZGEgZGUgbGEgY3VydmEsIGRvbmRlIGxhIGN1cnZhIHNlIGFsZWphIGRlIGxhIGzDrW5lYSBkaWFnb25hbC4NCg0KKiAqKjMuIEN1cnZhIFJPQyBkZSBOYWl2ZSBCYXllczoqKiBUaWVuZSB1bmEgZXNwZWNpZmljaWRhZCBzaW1pbGFyIGEgbGEgY3VydmEgUk9DIGRlIMOBcmJvbCwgcGVybyB1bmEgc2Vuc2liaWxpZGFkIG3DoXMgYWx0YS4gRXN0byBzaWduaWZpY2EgcXVlIGVsIG1vZGVsbyBlcyBtw6FzIHByb3BlbnNvIGEgY2xhc2lmaWNhciBjb3JyZWN0YW1lbnRlIGxvcyBwb3NpdGl2b3MsIHBlcm8gdGFtYmnDqW4gZXMgbcOhcyBwcm9wZW5zbyBhIGNsYXNpZmljYXIgZXJyw7NuZWFtZW50ZSBsb3MgbmVnYXRpdm9zIGNvbW8gcG9zaXRpdm9zLg0KDQoqICoqNC4gQ3VydmEgUk9DIGRlIFJhbmRvbSBGb3Jlc3Q6KiogVGllbmUgbGEgZXNwZWNpZmljaWRhZCBtw6FzIGFsdGEgZGUgbGFzIGN1YXRybyBjdXJ2YXMgUk9DLiBFc3RvIHNpZ25pZmljYSBxdWUgZWwgbW9kZWxvIGVzIG1lbm9zIHByb3BlbnNvIGEgY2xhc2lmaWNhciBlcnLDs25lYW1lbnRlIGxvcyBuZWdhdGl2b3MgY29tbyBwb3NpdGl2b3MuIExhIGN1cnZhIFJPQyBkZSBSYW5kb20gRm9yZXN0IHRhbWJpw6luIHRpZW5lIHVuYSBzZW5zaWJpbGlkYWQgYWx0YSwgbG8gcXVlIHNpZ25pZmljYSBxdWUgZWwgbW9kZWxvIGVzIG3DoXMgcHJvcGVuc28gYSBjbGFzaWZpY2FyIGNvcnJlY3RhbWVudGUgbG9zIHBvc2l0aXZvcy4NCg0KYGBge3J9DQojR1JBRklDQVMgUk9DDQpwYXIobWZyb3c9YygyLCAyKSkNCg0KcGxvdC5yb2Mocm9jX29ial9nbG0sIG1haW4gPSAiQ3VydmEgUk9DIFJlZ3Jlc2nDs24iLCBjb2wgPSAiYmx1ZSIpDQoNCnBsb3Qucm9jKHJvY19vYmpfdHJlZSwgbWFpbj0iQ3VydmEgUk9DIMOBcmJvbCIsIGNvbD0iYmx1ZSIpDQoNCnBsb3Qucm9jKHJvY19vYmpfbmIsIG1haW49IkN1cnZhIFJPQyBOYWl2ZSBCYXllcyIsIGNvbD0iYmx1ZSIpDQoNCnBsb3Qucm9jKHJvY19vYmpfcmYsIG1haW49IkN1cnZhIFJPQyBSYW5kb20gRm9yZXN0IiwgY29sPSJibHVlIikNCmBgYA0KDQoNCiMgQ29uY2x1c2lvbmVzDQoNCg0KQ29uIGJhc2UgZW4gbG9zIGFuw6FsaXNpcyBwcmV2aWFtZW50ZSBoZWNob3MgZGUgbG9zIG1vZGVsb3MgcmVhbGl6YWRvcywgc2UgZGV0ZXJtaW5hIHF1ZSBlbCBtb2RlbG8gbcOhcyBhcHJvcGlhZG8gcGFyYSBhbmFsaXphciBlbCBhYmFuZG9ubyBkZSBsb3MgZW1wbGVhZG9zIGVuIEZvcm0gY29ycmVzcG9uZGUgYSAqKk5haXZlIEJheWVzKiosIGRlYmlkbyBhIHN1IGNhcGFjaWRhZCBkZSBhbmFsaXphciBjYWRhIGZhY3RvciBkZSAqKmZvcm1hIGluZGVwZW5kaWVudGUqKiwgYXPDrSBjb21vIHN1IGRlc2VtcGXDsW8gY29uc2lkZXJhYmxlIGVuIGxhcyBtw6l0cmljYXMuIEFjY3VyYWN5OjAuIDkxLCBLYXBwYTogMC43OCB5IEFVQzogMC45MC4gQWNvcmRlIGEgZXN0bywgc2UgZGV0ZXJtaW5hIHF1ZSBsYSBtYXlvcsOtYSBkZSBsYSAqKmZ1ZXJ6YSBsYWJvcmFsKiogZGUgRm9ybSB0aWVuZSBsYXMgc2lndWllbnRlcyBjYXJhY3RlcsOtc3RpY2FzOiBNdWplcmVzIHNvbHRlcmFzIGRlIDMxIG8gMzIgYcOxb3MsIHJlc2lkZW50ZXMgZGUgQXBvZGFjYSwgY29uIHVuIHB1ZXN0byBkZSBBeXVkYW50ZSBHZW5lcmFsIGRlbCBjdWFsIHBlcmNpYmVuIHVuIHNhbGFyaW8gcHJvbWVkaW8gZGUgJDIxNS45LiANCg0KUG9yIG90cmEgcGFydGUsIGFjb3JkZSBhbCBtb2RlbG8gTmFpdmUgQmF5ZXMsIGxhcyB2YXJpYWJsZXMgZXhwbGljYXRpdmFzIGVzdGltYW4gcXVlIGVzdGltYW4gZWwgc2lndWllbnRlIHBlcmZpbCAqKnBhcmEgZGV0ZXJtaW5hciBsYSBwZXJtYW5lbmNpYS9yZXRlbmNpw7NuKiogZGUgbG9zIGVtcGxlYWRvcyBlbiBGb3JtOg0KKiBFbXBsZWFkYXMgeSBlbXBsZWFkb3MgY2FzYWRvcyB5L28gZGl2b3JjaWFkb3MgZGUgbWF5b3IgZWRhZCBxdWUgdml2ZW4gZW4gZWwgbXVuaWNpcGlvIGRlIEd1YWRhbHVwZSBwZXJ0ZW5lY2llbnRlcyBhIGxvcyBkZXBhcnRhbWVudG9zIGRlIFByb2R1Y2Npw7NuIE1DLCBNQ0wgeSBSZXRvcm5hYmxlIHF1ZSBwZXJjaWJlbiB1biBzYWxhcmlvIGRpYXJpbyBtYXlvciBkb25kZSBzdSBtZXMgZGUgY29udHJhdGFjacOzbiBlcyBtYXlvcm1lbnRlIGEgcHJpbmNpcGlvcyBkZSBhw7FvLg0KDQpFbCBwZXJmaWwgYW50ZXJpb3Igc2UgcHVlZGUgZ2VuZXJhciBkZWJpZG8gYSBxdWUgbG9zIGVtcGxlYWRvcyBjYXNhZG9zIHkgZGl2b3JjaWFkb3Mgc3VlbGVuIHRlbmVyIG1heW9yZXMgcmVzcG9uc2FiaWxpZGFkZXMgZmFtaWxpYXJlcyB5IGZpbmFuY2llcmFzLCBsbyBxdWUgbG9zIGhhY2UgdmFsb3JhciBtw6FzIGxhIGVzdGFiaWxpZGFkIGxhYm9yYWwuIEFkZW3DoXMsIGFsIHNlciBwZXJzb25hcyBkZSBtYXlvciBwdWVkZW4gdGVuZXIgbWVub3MgZGlzcG9zaWNpw7NuIHBhcmEgY2FtYmlhciBkZSB0cmFiYWpvIGRlYmlkbyBhIGVzdGFzIHJlc3BvbnNhYmlsaWRhZGVzIHkgYSBxdWUgdGllbmRlbiBhIHZhbG9yYXIgbcOhcyBsYSBlc3RhYmlsaWRhZCwgbGEgc2VndXJpZGFkIHkgbGFzIHJlbGFjaW9uZXMgZXN0YWJsZWNpZGFzIGVuIHN1IGx1Z2FyIGRlIHRyYWJham8uIEFzaW1pc21vLCBhbCBzZXIgcmVzaWRlbnRlcyBkZSBHdWFkYWx1cGUgcG9kcsOtYSBpbXBsaWNhciBubyBzb2xhbWVudGUgcXVlIHBvciBsYSB1YmljYWNpw7NuIGRlIGxhIGVtcHJlc2EgcmVhbGl6YW4gbWVub3JlcyB0aWVtcG9zIGRlIGRlc3BsYXphbWllbnRvIHNpbm8gcXVlIHRhbWJpw6luIGxhIG9mZXJ0YSBkZSB0cmFiYWpvIGVuIGRpY2hhIHViaWNhY2nDs24gZXMgbWVqb3IgcXVlIGVuIHN1IGx1Z2FyIGRlIHJlc2lkZW5jaWEuIE5vIG9ic3RhbnRlLCBzZSBkZWJlIHRvbWFyIGVuIGN1ZW50YSBxdWUgbG9zIGRlcGFydGFtZW50b3MgZGUgZGljaG8gZ3J1cG8gZGUgcGVyc29uYXMgeWEgcXVlLCBpbXBsaWNhIHF1ZSB0aWVuZW4gcm9sZXMgbcOhcyBkZWZpbmlkb3MgeSBlc3RhYmxlcywgY29uIHVuYSBjbGFyYSB0cmF5ZWN0b3JpYSBkZSBkZXNhcnJvbGxvIHkgdW5hIHNlbnNhY2nDs24gZGUgY29udHJpYnVjacOzbiB0YW5naWJsZSBhbCBwcm9kdWN0byBmaW5hbCBlbiBjb21wYXJhY2nDs24gY29uIG90cm9zIGRlcGFydGFtZW50b3MgbG8gY3VhbCBzZSByZWZ1ZXJ6YSBjb24gbGEgcGVyY2VwY2nDs24gZGUgdW4gc2FsYXJpbyBkaWFyaW8gZGFuZG8gYSBlbnRlbmRlciBxdWUgbG9zIGVtcGxlYWRvcyBzaWVudGVuIHF1ZSBlc3TDoW4gYmllbiBjb21wZW5zYWRvcyBwb3Igc3UgdHJhYmFqbyB5IHBvciBlbmRlLCBzb24gbcOhcyBwcm9wZW5zb3MgYSBwZXJtYW5lY2VyIGVuIGxhIGVtcHJlc2EuDQoNClNpbiBlbWJhcmdvLCBhY29yZGUgYWwgbW9kZWxvIE5haXZlIEJheWVzLCBsYXMgdmFyaWFibGVzIGV4cGxpY2F0aXZhcyBxdWUgZXN0aW1hbiBlbCBzaWd1aWVudGUgcGVyZmlsICoqcGFyYSBkZXRlcm1pbmFyIGxhIHJlbnVuY2lhIHkvbyBmYWx0YSBkZSBzZWd1aW1pZW50byoqIGRlIGxvcyBlbXBsZWFkb3MgZW4gRm9ybSBzb246DQoqIEVtcGxlYWRhcyB5IGVtcGxlYWRvcyBzb2x0ZXJvcyB5IGVuIHVuacOzbiBsaWJyZSBkZSBtZW5vciBlZGFkIChhZHVsdG9zIGrDs3ZlbmVzKSBxdWUgdml2ZW4gZW4gZWwgbXVuaWNpcGlvIGRlIEFwb2RhY2EgcXVlIHBlcmNpYmVuIHVuIHNhbGFyaW8gZGlhcmlvIG1lbm9yIGRvbmRlIHN1IG1lcyBkZSBjb250cmF0YWNpw7NuIGVzIG1heW9ybWVudGUgYSBtZWRpYWRvcyBkZSBhw7FvLg0KDQpFbCBwZXJmaWwgYW50ZXJpb3Igc2UgcHVlZGUgZ2VuZXJhciBkZWJpZG8gYSBxdWUgbG9zIGNvbGFib3JhZG9yZXMgc29sdGVyb3MgeSBlbiB1bmnDs24gbGlicmUgc3VlbGVuIHRlbmVyIG1lbm9zIHJlc3BvbnNhYmlsaWRhZGVzIGZhbWlsaWFyZXMsIGxvIHF1ZSBsZXMgb3RvcmdhIG1heW9yIGxpYmVydGFkIHBhcmEgYnVzY2FyIG1lam9yZXMgb3BvcnR1bmlkYWRlcyBsYWJvcmFsZXMuIEFkZW3DoXMsIGVzdGFuIG3DoXMgZGlzcHVlc3RvcyBhIGNhbWJpYXIgZGUgdHJhYmFqbyBlbiBidXNjYSBkZSBtZWpvcmVzIGNvbmRpY2lvbmVzIG8gZGVzYWbDrW9zIG51ZXZvcywgbWF5b3JtZW50ZSBzaSBwZXJjaWJlbiB1biBzYWxhcmlvIGJham8geWEgcXVlIGltcGxpY2EgdW5hIGZ1ZW50ZSBpbXBvcnRhbnRlIGRlIGluc2F0aXNmYWNjacOzbiBsYWJvci4gRXN0byBzZSByZWZ1ZXJ6YSB5YSBxdWUsIGFsIHNlciBhZHVsdG9zIGrDs3ZlbmVzIHN1ZWxlbiB0ZW5lciBtZW5vcyBjb21wcm9taXNvcyB5IHB1ZWRlbiBzZXIgbcOhcyBwcm9wZW5zb3MgYSBidXNjYXIgbnVldmFzIG9wb3J0dW5pZGFkZXMgcGFyYSBhdmFuemFyIGVuIHN1cyBjYXJyZXJhcy4gTm8gb2JzdGFudGUsIHNlIG9ic2VydmEgcXVlIGRpY2hvIHBlcmZpbCBhYmFyY2EgcmVzaWRlbnRlcyBkZSBBcG9kYWNhIGxvIHF1ZSBpbXBsaWNhIHF1ZSBhbCB2aXZpciBlbiBkaWNoYSB6b25hIGJ1c2NhbiB5L28gY29ub2NlbiBvcG9ydHVuaWRhZGVzIGxhYm9yYWxlcyBlbiBsYXMgY3VhbGVzIHBlcmNpYmFuIG1heW9yIHZhbG9yIHF1ZSBlbiBGT1JNLiBBc2ltaXNtbywgZWwgcGVyaW9kbyBkZSBjb250cmF0YWNpw7NuIHRpZW5lIGFsdGEgcG9zaWJpbGlkYWQgZGUgY29pbmNpZGlyIGNvbiBsb3MgbWVzZXMgZG9uZGUgaGF5IGFsdGFzIHRlbXBlcmF0dXJhcyBhZmVjdGFuZG8gbGFzIGNvbmRpY2lvbmVzIGxhYm9yYWxlcywgdGllbXBvcyBkZSBjb250cmF0YWNpw7NuIGRlIG90cmFzIGVtcHJlc2FzIGVuIGxhcyBjdWFsZXMgc2UgcHVlZGUgcGVyY2liaXIgdW4gbWF5b3IgYmVuZWZpY2lvIHkvbyBjb24gcGVyaW9kb3MgZGUgRk9STSBkb25kZSBoYXkgdW5hIGNvbnRyYXRhY2nDs24gbcOhcyBpbXB1bHNpdmEgcGFyYSBjdWJyaXIgbmVjZXNpZGFkZXMgdGVtcG9yYWxlcy4gRW4gY29uc2VjdWVuY2lhLCBsb3MgZW1wbGVhZG9zIGNvbnRyYXRhZG9zIGVuIGVzdG9zIG1lc2VzIHB1ZWRlbiBzZW50aXIgdW5hIG1lbm9yIGludGVncmFjacOzbiB5IGVzdGFiaWxpZGFkIGVuIGxhIGVtcHJlc2EsIGxvIHF1ZSBpbmNyZW1lbnRhIGxhIHByb2JhYmlsaWRhZCBkZSByZW51bmNpYS4NCg0KUG9yIGxvIHRhbnRvLCBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBxdWUgaW5mbHV5ZW4gZW4gbGEgcGVybWFuZW5jaWEgbyByZW51bmNpYSBlbiBsYSBlbXByZXNhIEZPUk0gcmVmbGVqYW4gbGEgaW1wb3J0YW5jaWEgZGUgbGEgZXN0YWJpbGlkYWQsIGxhIGNsYXJpZGFkIGVuIGVsIHJvbCwgbGEgY29tcGVuc2FjacOzbiBhZGVjdWFkYSB5IGxhcyBjb25kaWNpb25lcyBwZXJzb25hbGVzIGRlIGxvcyBlbXBsZWFkb3MuIEVudGVuZGVyIGVzdG9zIGZhY3RvcmVzIHBlcm1pdGUgYSBsYSBlbXByZXNhIGltcGxlbWVudGFyIGVzdHJhdGVnaWFzIGVzcGVjw61maWNhcyBwYXJhIG1lam9yYXIgbGEgcmV0ZW5jacOzbiwgY29tbzoNCiogUmVhbGl6YXIgdW4gYW7DoWxpc2lzIGRlIG1lcmNhZG8gcGFyYSBhc2VndXJhciBxdWUgbG9zIHNhbGFyaW9zIHNlYW4gY29tcGV0aXRpdm9zLCB5YSBxdWUgY29uc2lkZXJhciBhdW1lbnRvcyBzYWxhcmlhbGVzIG8gYm9uaWZpY2FjaW9uZXMganVzdGFzIHBhcmEgYXVtZW50YXIgbGEgc2F0aXNmYWNjacOzbiB5IHJlZHVjaXIgbGEgaW50ZW5jacOzbiBkZSByZW51bmNpYXIgZGViaWRvIGEgbGEgcGVyY2VwY2nDs24gZGUgdW5hIGNvbXBlbnNhY2nDs24ganVzdGEuDQoqIEltcGxlbWVudGFyIHByb2dyYW1hcyBkZSBjYXBhY2l0YWNpw7NuIHkgZGVzYXJyb2xsbyBkZSBoYWJpbGlkYWRlcywgYXPDrSBjb21vIHBsYW5lcyBkZSBjYXJyZXJhIGNsYXJvcyBwYXJhIHRvZG9zIGxvcyBlbXBsZWFkb3MsIGxvIHF1ZSBwdWVkZSBhdW1lbnRhciBsYSBsZWFsdGFkIHkgZWwgY29tcHJvbWlzbyBkZSBsb3MgZW1wbGVhZG9zIGNvbiBsYSBlbXByZXNhLg0KKiBQbGFuaWZpY2FyIGxhcyBjb250cmF0YWNpb25lcyBlc3RyYXTDqWdpY2FtZW50ZSBwYXJhIGV2aXRhciBsb3MgbWVzZXMgY29uIG1heW9yIMOtbmRpY2UgZGUgcmVudW5jaWEgKEFnb3N0bywgSnVuaW8sIEp1bGlvLCBTZXB0aWVtYnJlKSB5IG1lam9yYXIgZWwgcHJvY2VzbyBkZSBvbmJvYXJkaW5nIHBhcmEgaW50ZWdyYXIgYSBsb3MgZW1wbGVhZG9zIGRlIG1hbmVyYSBlZmVjdGl2YTsgbG8gY3VhbCByZWR1Y2lyw6EgbGEgcm90YWNpw7NuIHRlbXByYW5hIGRlIGxvcyBudWV2b3MgZW1wbGVhZG9zLg0KDQojIyBNdWVzdHJhIGRlIFByZWRpY2Npw7NuIGNvbiBlbCBNb2RlbG8gU2VsZWNjaW9uYWRvDQpTZWfDum4gZWwgbW9kZWxvIHNlbGVjY2lvbmFkbywgc2UgZXN0aW1hIHF1ZSBhcHJveGltYWRhbWVudGUgZWwgMTYlIGRlIGxvcyBlbXBsZWFkb3MgYWN0dWFsZXMgZGUgbGEgZW1wcmVzYSBGT1JNIGNvbnNpZGVyYW4gbm8gc2VndWlyIHNpZW5kbyBwYXJ0ZSBkZSBsYSBvcmdhbml6YWNpw7NuIGVuIHVuIGZ1dHVybyBwcsOzeGltby4gQWRlbcOhcywgZGVudHJvIGRlIGVzdGUgZ3J1cG8sIHNlIG9ic2VydmEgcXVlIHRyZXMgZGUgY2FkYSBjaW5jbyBlbXBsZWFkb3Mgc29uIG11amVyZXMuIERpY2hvIGhhbGxhemdvIHN1Z2llcmUgbGEgbmVjZXNpZGFkIGRlIGRlc2Fycm9sbGFyIGVzdHJhdGVnaWFzIGVmZWN0aXZhcyBkZSByZXRlbmNpw7NuIHF1ZSBhYm9yZGVuIGxhcyBwcmVvY3VwYWNpb25lcyB5IG5lY2VzaWRhZGVzIGVzcGVjw61maWNhcyBkZSBlc3RlIHNlZ21lbnRvIGRlIGxhIGZ1ZXJ6YSBsYWJvcmFsLg0KDQpgYGB7cn0NCnRlc3RfbXVlc3RyYSAgICAgICAgICAgIDwtIHRlc3QNCnRlc3RfbXVlc3RyYSRwcmVkaWNjaW9uIDwtIHByZWRpY3QobW9kZWxvX25iLCB0ZXN0KQ0KDQojIEZpbHRyYXIgc29sbyBsb3MgZW1wbGVhZG9zIHF1ZSB0aWVuZW4gIk5vIiBlbiBsYSBjb2x1bW5hICJSZW51bmNpYSINCnRlc3RfbXVlc3RyYV9maWx0cmFkbyA8LSB0ZXN0X211ZXN0cmEgJT4lDQogIGZpbHRlcihSZW51bmNpYSA9PSAiTm8iKQ0KDQojIE1vc3RyYXIgVmFsb3IgUmVhbCB2cyBQcmVkaWNjacOzbiBwYXJhIENhZGEgRW1wbGVhZG8gZGVsIFNldCBkZSBNdWVzdHJhDQp0ZXN0X211ZXN0cmFfZmlsdHJhZG8gJT4lDQogIGRwbHlyOjpzZWxlY3QoTm9tYnJlLCBBcGVsbGlkb3MsIFJlbnVuY2lhLCBwcmVkaWNjaW9uKQ0KDQpjb250ZW9fc2kgPC0gdGFibGUodGVzdF9tdWVzdHJhX2ZpbHRyYWRvJHByZWRpY2Npb24gPT0gIlNpIikNCmNvbnRlb19zaQ0KYGBgDQoNCg==