¿Qué se realizará?

A lo largo de este archivo se podrá visualizar el proceso de limpieza de datos que se realizó en las bases de datos proporcionadas por FORM para solucionar las problemáticas de Pronostico de ventas y Rotación del personal. Este proceso de limpieza fue realizado tanto en R como en Excel dependiendo de la acción a realizar. De este proceso se busca obtener bases homologadas e incluso unificadas si es requerido según la situación problema.

Librerias

library(dplyr)     # Manipulación de datos: filtros, transformaciones, agrupaciones y más.
library(ggplot2)   # Visualización de datos: gráficos y diagramas elegantes y personalizables.
library(xts)       # Manejo y manipulación de series temporales en R.
library(dygraphs)  # Visualización interactiva de series temporales.
library(tseries)   # Análisis de series temporales y pruebas estadísticas.
library(forecast)  # Pronóstico de series temporales: modelos ARIMA, ETS, etc.
library(zoo)       # Manipulación de series temporales irregulares o regulares.
library(corrplot)  # Visualización de matrices de correlación.
library(ggpubr)    # Mejoras en la visualización de gráficos 'ggplot2' para publicaciones.
library(kableExtra)# Mejora de tablas en formato 'knitr' y 'kable' para reportes.
library(openxlsx)  # Lectura y escritura de archivos Excel sin necesidad de Java.
library(lubridate)
library(tidyr)
library(stringr)
library(writexl)

Datos

setwd("C:\\Users\\Silva\\Documents\\Concentración\\Form_E3")
RH_A = read.csv("Bases\\RH_ACTIVOS.csv")
RH_B = readxl::read_xlsx("Bases\\Datos_FORM_RH_FJ2024.xlsx")
ventas1 = readxl::read_xlsx("Bases\\form_bajas.xlsx", sheet = "Ventas_mensuales")
ventas2_2021 = readxl::read_xlsx("Bases\\Datos_FORM_Ventas_FJ2024.xlsx", sheet = "2021")
ventas2_2022 = readxl::read_xlsx("Bases\\Datos_FORM_Ventas_FJ2024.xlsx", sheet = "2022")
ventas2_2023 = readxl::read_xlsx("Bases\\Datos_FORM_Ventas_FJ2024.xlsx", sheet = "2023")

Sobre los datos
Todas las bases utilizadas en este proceso fueron proporcionadas por Form y previo a su utilización fueron seleccionas ciertas variables que el equipo consideró importantes y que no impactaran en problemas de seguridad de información de datos personales.

Ventas

Ventas carton, retornable, servicios y total 2020-2022

Cambiar formato de datos en columna “Fecha”

ventas1$Fecha <- as.Date(ventas1$Fecha, format = "%d/%m/%Y")

Identificación de NAs

NAdetecter= data.frame(colSums(is.na(ventas1)))
colnames(NAdetecter) <- c("NA's")
NAdetecter %>%
  kbl() %>%
  add_header_above(c("Cantidad de NA´s Ventas 1" = 2))%>%
  kable_styling()
Cantidad de NA´s Ventas 1
NA’s
Fecha 0
Carton 15
Retornable 15
Servicios 24
Total 3

Manejo de NAs

# Extraer Mes y Año a columnas nuevas 
ventas1 <- ventas1 %>%
  mutate(Mes = format(Fecha, "%m"),
         Año = format(Fecha, "%Y"))

La forma en la que imputaremos los datos faltantes será con el promedio de las ventas del mismo mes de los demás años. POr ejemplo, si Enero 2020 es un NA, lo imputaremos con el promedio de enero 2021 y enero 2022. En caso de no haber datos suficientes para sacar el promedio, lo haremos con el promedio de las demás cantidades de ese año y categoria. Los datos pueden no ser muy precisos, pero consideramos que es la forma más acertada.

# Carton

# Calcular el promedio de Carton por mes
promedios_carton <- ventas1 %>%
  group_by(Mes) %>%
  summarise(promedio_Carton = mean(Carton, na.rm = TRUE))

# Unir los promedios con los datos originales
ventas1 <- ventas1 %>%
  left_join(promedios_carton, by = "Mes")

# Imputar los valores faltantes en Carton con los promedios
ventas1$Carton <- ifelse(is.na(ventas1$Carton), ventas1$promedio_Carton, ventas1$Carton)
# Repetir el proceso para Retornable 

promedios_retornable <- ventas1 %>%
  group_by(Mes) %>%
  summarise(promedio_Retornable = mean(Retornable, na.rm = TRUE))

ventas1 <- ventas1 %>%
  left_join(promedios_retornable, by = "Mes")

ventas1$Retornable <- ifelse(is.na(ventas1$Retornable), ventas1$promedio_Retornable, ventas1$Retornable)
# Servicios

promedios_servicios <- ventas1 %>%
  group_by(Mes) %>%
  summarise(promedio_Servicios = mean(Servicios, na.rm = TRUE))

ventas1 <- ventas1 %>%
  left_join(promedios_servicios, by = "Mes")

ventas1$Servicios <- ifelse(is.na(ventas1$Servicios), ventas1$promedio_Servicios, ventas1$Servicios)
# Total

promedios_total <- ventas1 %>%
  group_by(Mes) %>%
  summarise(promedio_Total = mean(Total, na.rm = TRUE))

ventas1 <- ventas1 %>%
  left_join(promedios_total, by = "Mes")

ventas1$Total <- ifelse(is.na(ventas1$Total), ventas1$promedio_Total, ventas1$Total)
# Eliminar las columnas de promedios
ventas1 <- ventas1 %>%
  select(-promedio_Carton, -promedio_Retornable, -promedio_Servicios, -promedio_Total)

Aún quedan NaNs en la columna de servicios en los meses donde no hay datos en ningún año.

sum(is.nan(ventas1$Servicios))
## [1] 6

Imputaremos estos datos con el promedio de Servicios del año al que pertenece ese NaN.

# Calcular el promedio anual de Servicios
promedios_servicios <- ventas1 %>%
  group_by(Año) %>%
  summarise(promedio_Servicios = mean(Servicios, na.rm = TRUE))

# Unir los promedios anuales con los datos originales
ventas1 <- ventas1 %>%
  left_join(promedios_servicios, by = "Año")

# Reemplazar los valores NaN en Servicios con los promedios anuales
ventas1 <- ventas1 %>%
  mutate(Servicios = ifelse(is.nan(Servicios), promedio_Servicios, Servicios))

# Eliminar la columna de promedios
ventas1 <- ventas1 %>%
  select(-promedio_Servicios)

Estructura final de Ventas 1

La estructura de esta base de datos consta en 5 variables principales las cuales son Fecha (por mes de 2020 - 2022), Carton, Servicios, Retornable y Total. Agregamos 2 columnas, una extrayendo el mes y la otra el año, esto para poder también analizar el efecto que tiene cada mes y cada año que va transcurriendo. Usaremos esta base de datos para predecir las ventas de los siguientes años, pero debido a que los últimos datos que tenemos son de 2022, nuestras predicciones pueden no ser muy precisas al llegar a 2024.

Ventas por categoría de productos

# Unimos las tres pestañas para tener una base de datos completa con los años 2021, 2022 y 2023. 
ventas2_actualizado <- rbind(ventas2_2021, ventas2_2022,ventas2_2023)

Identificación de NAs

NAdetecter= data.frame(colSums(is.na(ventas2_actualizado)))
colnames(NAdetecter) <- c("NA's")
NAdetecter %>%
  kbl() %>%
  add_header_above(c("Cantidad de NA´s Ventas 2" = 2))%>%
  kable_styling()
Cantidad de NA´s Ventas 2
NA’s
Folio de Factura 0
Fecha 0
No. OC Cliente 26
Ref. cliente 30
Cliente 0
Producto 0
Cantidad 0
Categoría de producto 0
Estado 0
moda <- names(sort(table(ventas2_actualizado$`No. OC Cliente`), decreasing = TRUE))[1]
moda1 <- names(sort(table(ventas2_actualizado$`Ref. cliente`), decreasing = TRUE))[1]
# Imputar los valores faltantes en 'No OC Cliente' con la moda 
ventas2_actualizado[is.na(ventas2_actualizado)] <- moda

# Imputar los valores faltantes en 'Ref Cliente' con la moda
ventas2_actualizado[is.na(ventas2_actualizado)] <- moda1
# Verificamos que ya no haya NA's
NAdetecter= data.frame(colSums(is.na(ventas2_actualizado)))
colnames(NAdetecter) <- c("NA's")
NAdetecter %>%
  kbl() %>%
  add_header_above(c("Cantidad de NA´s Ventas 2" = 2))%>%
  kable_styling()
Cantidad de NA´s Ventas 2
NA’s
Folio de Factura 0
Fecha 0
No. OC Cliente 0
Ref. cliente 0
Cliente 0
Producto 0
Cantidad 0
Categoría de producto 0
Estado 0
# Quitamos los espacios en los nombres de las variables y reemplazamos con guión bajo

nombres_variables <- names(ventas2_actualizado)
nombres_variables_nuevos <- gsub(" ", "_", nombres_variables)
names(ventas2_actualizado) <- nombres_variables_nuevos
#Agregamos una columna con el año de la venta (ya que unimos las tres pestañas con cada año)
ventas2_actualizado$Fecha <- as.Date(ventas2_actualizado$Fecha)

ventas2_actualizado$Año <- year(ventas2_actualizado$Fecha)
# Cambiamos los nombres de los clientes a MAYUSCULAS
ventas2_actualizado$Cliente <- toupper(ventas2_actualizado$Cliente)
# Juntamos clientes con nombres iguales
ventas2_actualizado$Cliente <- gsub("YANFENG SEATING MEXICO SA DE CV", "YANFENG SEATING MEXICO", ventas2_actualizado$Cliente)
# Verificamos que ya no haya mismos clientes con nombre diferente
frecuencia_clientes <- ventas2_actualizado %>%
  count(Cliente)

frecuencia_clientes
## # A tibble: 35 × 2
##    Cliente                                         n
##    <chr>                                       <int>
##  1 AGP WORLDWIDE OPERATIONS GMBH                   1
##  2 AISLANTES Y EMPAQUES                            1
##  3 ANTOLIN INTERIORS MEXICO                        5
##  4 APTIV SERVICES US, LLC                         54
##  5 AVANZAR INTERIOR PRODUCTS DE MEXICO            10
##  6 DENSO MEXICO                                 1698
##  7 DRAEXLMAIER COMPONENTS AUTOMOTIVE DE MEXICO    16
##  8 EFP OPERATIONS MEXICANA                        85
##  9 ELRINGKLINGER TEXAS LLC                        39
## 10 ESTAPACK S.A.P.I  DE C.V                        1
## # ℹ 25 more rows
# Agrupar por producto (Es muy dificil identificar si hay productos iguales que tengan nombre diferente ya que la cantidad es muy grande y los nombre muy largos)
frecuencia_producto <- ventas2_actualizado %>%
  count(Producto)

frecuencia_producto
## # A tibble: 799 × 2
##    Producto                                                                    n
##    <chr>                                                                   <int>
##  1 "[#01651-BURBUJA GRANDE EN ROLLO] 61010001 #01651-BURBUJA GRANDE EN RO…     1
##  2 "[- 430969 FS 30 99 0000 00 000 LISTONES TRIANGULAR - CORNER BOARD 48\…     1
##  3 "[- 431208 FS 30 99 0000 00 000 LISTONES TRIANGULAR - CORNER BOARD 45\…     1
##  4 "[00010 817904 AS 30 99 0000 00 000 INSERTO] 18891. Coupe. Charola Arm…   118
##  5 "[00010 Caja individual para toyota] 19559 Caja individual para toyota"     2
##  6 "[00010] Thermoformed Tray for 252.181-00"                                  1
##  7 "[00020] Thermoformed Tray for 252.541-00"                                  1
##  8 "[00200 882195] AS  30 99 0000 00 000 CARTÓN (EMPAQUE). MEVEREST. EMPA…     6
##  9 "[00690 565289] FS 30 99 0000 00 000 INSERTO A"                            11
## 10 "[00700  565528] FS 30 99 0000 00 000 INSERTO B"                           11
## # ℹ 789 more rows
# Agrugar las observaciones en la columna de 'Estado' solamente para verificar cuales son las categorías (Estaría bien preguntar a FORM las razones de los pedidos cancelados)

frecuencia_estado <- ventas2_actualizado %>%
  count(Estado)

frecuencia_estado
## # A tibble: 2 × 2
##   Estado     n
##   <chr>  <int>
## 1 cancel   143
## 2 posted 16563

Estructura final Ventas 2

Esta base tiene una gran cantidad de datos tanto categóricos como numéricos, las modificaciones se hicieron más que nada en los nombres de los clientes la cual es una variable importante al analizar sus pedidos en FORM, y en donde tuvimos que asegurarnos que no se tomara el mismo cliente como diferente por estar mal escrito.

RH

Contexto

Para la problemática de rotación de personal inicialmente se decidió mantener únicamente variables que fueran relevantes para el equipo y no involucraran problemas hacia la seguridad de la información, de igual forma se buscó generar variables claves con los datos proporcionados, como su edad y duración en la empresa.

Las variables explicativas con las que buscamos quedarnos son:

  • Genero: Sexo del colaborador (MASCULINO, FEMENINO).
  • Fecha_Alta: Fecha en que se dio de alta al colaborador.
  • Fecha_Baja: Fecha en que se dio de baja al colaborador.
  • Puesto: Cargo o posición que ocupa el colaborador.
  • SD: Salario Diario del colaborador.
  • Lugar_Nacimiento: Ciudad o localidad donde nació el colaborador.
  • Municipio: Municipio de residencia del colaborador.
  • Estado: Estado o entidad federativa de residencia del colaborador.
  • CP: Código Postal del lugar de residencia del colaborador.
  • Estado_Civil: Estado civil del colaborador (MATRIMONIO, SOLTERIA, etc.).
  • dias_trabajados: Número de días trabajados por el colaborador.
  • diferencias_meses: Número de meses trabajados por el colaborador.
  • Edad: Edad del colaborador.

Activos

Excel

Tiempos y Edad

Para las variables referentes al tiempo trabajado y a la edad se pensó en sacar la diferencia de días entre ciertas fechas clave(Nacimiento, Alta, Baja). La edad fue sacada hasta el día que se trabajó dentro de la organización.

La variable de edad consideramos que era de suma importancia agregarla a la base de datos, debido a temas generacionales que llegaba a mencionar el socio formador y de ciertos comportamientos que ya habían sido considerados, en cuanto al tiempo de trabajo fue considerado importante para poder considerar diferentes variables independientes en el futuro.

¿Fecha de baja en activos?
Puede sonar ilogico que los activos en la organización tengan una fecha de salida, pero era necesario que tuvieran alguna para poder hacer analisis posteriores con los días trabajos, es por eso que se tomo como refencia el día en el cual se trató la variable.

Tipo y Activo

La variable de Activo es la variable dependiente “predeterminada” del caso, pero consideramos que no era suficiente, al tener una problemática muy punzante en la rotación del personal decidimos enfocarnos principalmente en el proceso de selección

Los modelos de IA manejan un concepto de Garbage in, garbage out donde los resultados que obtengas dependerán en gran medida de la calidad de datos que ingreses, decidimos manejar la misma ideología en la rotación del personal. Se busca definir a los prospectos con probabilidades que cumplan cierto tiempo en la organización, de esta manera se puede ir mejorando las medidas de duración y disminuyendo poco a poco la rotación.

¿Qué son Tipo y Activo?
Las variables “Tipo” y “Activo” son nuestras variables dependientes, Activo significando que siguen trabajando en la empresa y Tipo es creada apartir de una condicional sobre “diferencia_meses” donde aquellos que hayan o han durado minimo 2 meses en la empresa serán marcados como “1”.

R

Homologar lugares de nacimiento

# LUGAR DE NACIMIENTO
RH_A$Lugar_Nacimiento <- gsub('.*NUEVO LEON.*', 'NUEVO LEON', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*SAN NICOLAS DE LOS GARZA*', 'NUEVO LEON', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*N.L.*', 'NUEVO LEON', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*MONTERREY*', 'NUEVO LEON', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*MTY*', 'NUEVO LEON', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*TAMAULIPAS.*', 'TAMAULIPAS', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*COAHUILA.*', 'COAHUILA', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*VERACRUZ*', 'VERACRUZ', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*SAN LUIS POTOSI*', 'SAN LUIS POTOSI', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*CHIAPAS*', 'CHIAPAS', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*ZACATECAS*', 'ZACATECAS', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*OAXACA*', 'OAXACA', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*HIDALGO*', 'HIDALGO', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*DURANGO*', 'DURANGO', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*GUANAJUATO*', 'GUANAJUATO', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*PUEBLA*', 'PUEBLA', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*TABASCO*', 'TABASCO', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*CHIHUAHUA*', 'CHIHUAHUA', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*MEXICO*', 'CDMX', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*DISTRITO FEDERAL*', 'CDMX', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*QUINTANA ROO*', 'QUINTANA ROO', RH_A$Lugar_Nacimiento)
RH_A$Lugar_Nacimiento <- gsub('.*JALISCO*', 'JALISCO', RH_A$Lugar_Nacimiento)

RH_A$Lugar_Nacimiento[RH_A$Lugar_Nacimiento == ""] = RH_A$Estado[RH_A$Lugar_Nacimiento == ""]


# ESTADO
RH_A$Estado[which(RH_A$Estado == 'NUEVO LEÓN')] <- 'NUEVO LEON'

En ambas bases se busco homologar un poco el lugar de nacimiento del personal, dejando de considera las ciudades o municipios donde nacieron y concentrándonos únicamente en el estado, disminuyendo la cantidad de niveles al momento de hacer la variable factor y buscando que la variable pueda tener algo de significancia en los modelos.

Identificación y Remplazo de NA´s

RH_A <- subset(RH_A, select = -c(X, X.1, X.2, X.3))
NAdetecter= data.frame(colSums(is.na(RH_A)))
colnames(NAdetecter) <- c("NA's")
NAdetecter %>%
  kbl() %>%
  add_header_above(c("Cantidad de NA´s RH Activos" = 2))%>%
  kable_styling()
Cantidad de NA´s RH Activos
NA’s
Fecha_Nacimiento 0
Genero 0
Fecha_Alta 0
Fecha_Baja 0
Puesto 0
SD 0
Lugar_Nacimiento 0
Municipio 0
Estado 0
CP 0
Estado_Civil 0
dias_trabajados 0
diferencias_meses 0
Edad 0
Tipo 0
Activo 0

Al terminar con los procesos de limpieza la base de Activos terminó sin más valores faltantes que tratar y se encuentra lista para unir con la base de Bajas.

Bajas

Excel en Bajas
En el caso de la base de datos de bajas solo se utilizó la herramienta Excel para hacer le primer subset de variables enfocado en la seguridad de datos personales de los colaboradores de FORM

R

Homologar lugares de nacimiento

# MAYUSCULAS
RH_B <- mutate_if(RH_B, is.character, toupper) # Poner todos los caracteres en mayusculas

Debido a la forma en la que fueron guardando la información proporcionada se encontraron diferentes formatos de respuesta ej.(respuesta, Respuesta, RESPUESTA), por lo que se tuvo que homologar en todas las columnas para que al momento de convertir las variables a factores manejen los mismo niveles, se decidió convertir todo a mayúsculas.

Identificación NA´s

# Hacer un subset de variables iguales
RH_B <- subset(RH_B, select = -c(Banco, Causa_Baja, Observaciones_baja))
NAdetecter= data.frame(colSums(is.na(RH_B)))
colnames(NAdetecter) <- c("NA's")
NAdetecter %>%
  kbl() %>%
  add_header_above(c("Cantidad de NA´s en RH Bajas" = 2))%>%
  kable_styling()
Cantidad de NA´s en RH Bajas
NA’s
Fecha_Nacimiento 0
Género 0
Fecha_Alta 0
Fecha_Baja 141
Puesto 2
SD 19
Lugar_Nacimiento 17
Municipio 0
Estado 0
CP 5
Estado_Civil 0

¿Nulos en CP?
Se puede observar que se cuentan con 5 valores nulos en el código postal, estos valores no fueron tratados debido a lo poco problable que sea implementado un analisis espacial donde fueran necesarios, de serlos se trataría con la moda dependiendo de su municipio.

Homologar lugares de nacimiento

# LUGAR DE NACIMIENTO
RH_B$Lugar_Nacimiento <- gsub('.*NUEVO LEON.*', 'NUEVO LEON', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*SAN NICOLAS DE LOS GARZA*', 'NUEVO LEON', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*N.L.*', 'NUEVO LEON', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*MONTERREY*', 'NUEVO LEON', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*MTY*', 'NUEVO LEON', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*TAMAULIPAS.*', 'TAMAULIPAS', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*COAHUILA.*', 'COAHUILA', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*VERACRUZ*', 'VERACRUZ', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*SAN LUIS POTOSI*', 'SAN LUIS POTOSI', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*CHIAPAS*', 'CHIAPAS', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*ZACATECAS*', 'ZACATECAS', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*OAXACA*', 'OAXACA', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*HIDALGO*', 'HIDALGO', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*DURANGO*', 'DURANGO', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*GUANAJUATO*', 'GUANAJUATO', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*PUEBLA*', 'PUEBLA', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*TABASCO*', 'TABASCO', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*CHIHUAHUA*', 'CHIHUAHUA', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*MEXICO*', 'CDMX', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*DISTRITO FEDERAL*', 'CDMX', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*QUINTANA ROO*', 'QUINTANA ROO', RH_B$Lugar_Nacimiento)
RH_B$Lugar_Nacimiento <- gsub('.*JALISCO*', 'JALISCO', RH_B$Lugar_Nacimiento)

#RH_B$Lugar_Nacimiento[RH_B$Lugar_Nacimiento == ""] = RH_B$Estado[RH_B$Lugar_Nacimiento == ""]

# ESTADO
RH_B$Estado[which(RH_B$Estado == 'NUEVO LEÓN')] <- 'NUEVO LEON'

En este tratamiento se repitió lo realizado en la base de Activos.

Variables de tiempo y edad

datos_filtrados <- RH_B[!is.na(RH_B$Fecha_Alta) & !is.na(RH_B$Fecha_Baja),]

# Calcular la diferencia en días y almacenarla en una nueva columna
datos_filtrados$diferencia_dias <- as.numeric(difftime(datos_filtrados$Fecha_Baja, datos_filtrados$Fecha_Alta, units = "days"))

medianas_dias = round(median(datos_filtrados$diferencia_dias))
RH_B$Fecha_Baja[is.na(RH_B$Fecha_Baja)] <- RH_B$Fecha_Alta[is.na(RH_B$Fecha_Baja)] + medianas_dias
options(scipen = 999)
RH_B$dias_trabajados <- trunc(as.numeric(difftime(RH_B$Fecha_Baja, RH_B$Fecha_Alta, units = "days")))
RH_B$diferencias_meses <- round(as.numeric(RH_B$dias_trabajados/30))
RH_B$Edad = trunc(as.numeric(difftime(RH_B$Fecha_Baja, RH_B$Fecha_Nacimiento, units = "days")) / 365.25)

Tratamiento de nulos en fechas
Para el tratamiento de los nulos en las fechas de bajas se decidió por imputar valores debido a la importancia de la variable y la cantidad de cosas que nos permitía hacer teniendola completa, es por eso que se saco la mediana de dias trabajados contando unicamente los registros con ambas fechas (Alta y Baja) y se valor de días fue sumado a la fecha de alta de que aquellos con baja faltante.

Manejo de NA´s

# NA´s en variable de Genero
RH_B$Puesto <- ifelse(is.na(RH_B$Puesto), "SERVICIO AL CLIENTE", RH_B$Puesto )

Tipo y Activo

RH_B$Tipo <- ifelse(RH_B$diferencias_meses <= 2, "0", "1")
RH_B$Tipo = as.factor(RH_B$Tipo)
RH_B$Activo = 0

Homologar niveles (varios)

# Preparar formato de fechas
RH_B$Fecha_Nacimiento <- as.Date(RH_B$Fecha_Nacimiento, format = "%d-%m-%Y")

# Igualar el formato de "Estado_Civil" en ambas bases 
RH_B$Estado_Civil[RH_B$Estado_Civil == "SOLTERA"] <- "SOLTERIA"
RH_B$Estado_Civil[RH_B$Estado_Civil == "CASADA"] <- "MATRIMONIO"
RH_B$Estado_Civil[RH_B$Estado_Civil == "DIVORCIADA"] <- "DIVORCIO"

names(RH_B)[names(RH_B) == "Género"] <- "Genero" # Renombrar la columna de "Género" para que cuadren ambas 

Unificación

# Unir bases de datos
RH <- rbind(RH_B, RH_A)

# Definir los formatos a utiliazr por la variables
RH$Genero = as.factor(RH$Genero)
RH$Puesto = as.factor(RH$Puesto)
RH$Lugar_Nacimiento = as.factor(RH$Lugar_Nacimiento)
RH$Municipio = as.factor(RH$Municipio)
RH$Estado = as.factor(RH$Estado)
RH$Estado_Civil = as.factor(RH$Estado_Civil)
RH$CP = as.character(RH$CP)
RH$SD = as.numeric(RH$SD)
RH$Tipo = as.factor(RH$Tipo)
RH$Activo = as.factor(RH$Activo)

# Obtener una visualización de las primeras 5 filas
head(RH)
## # A tibble: 6 × 16
##   Fecha_Nacimiento Genero   Fecha_Alta          Fecha_Baja          Puesto    SD
##   <date>           <fct>    <dttm>              <dttm>              <fct>  <dbl>
## 1 1985-08-18       FEMENINO 2017-02-20 00:00:00 2023-10-02 00:00:00 COSTU…  153.
## 2 1969-06-27       MASCULI… 2017-12-01 00:00:00 2023-01-05 00:00:00 GESTOR  177.
## 3 1989-06-21       MASCULI… 2018-03-23 00:00:00 2023-10-31 00:00:00 CHOFER  177.
## 4 1997-11-20       FEMENINO 2018-09-06 00:00:00 2018-09-06 00:00:22 LIDER   144.
## 5 1984-08-19       FEMENINO 2019-05-02 00:00:00 2024-02-20 00:00:00 AYUDA…  144.
## 6 1990-06-24       MASCULI… 2019-07-30 00:00:00 2023-01-18 00:00:00 RESID…  177.
## # ℹ 10 more variables: Lugar_Nacimiento <fct>, Municipio <fct>, Estado <fct>,
## #   CP <chr>, Estado_Civil <fct>, dias_trabajados <dbl>,
## #   diferencias_meses <dbl>, Edad <dbl>, Tipo <fct>, Activo <fct>

Extraccíon de base

#write.xlsx(RH, "BASE_RH_FORM_PERSONA.xlsx")
#write_xlsx(ventas1, "ventas1_limpio.xlsx")
#write_xlsx(ventas2, "ventas2_limpio.xlsx")
LS0tDQp0aXRsZTogIkxpbXBpZXphIGRlIEJhc2VzIGRlIERhdG9zIg0KYXV0aG9yOiAiRXF1aXBvIDMiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQohW10oQzpcXFVzZXJzXFxTaWx2YVxcRG9jdW1lbnRzXFxDb25jZW50cmFjacOzblxcRm9ybV9FM1xcSW1hZ2VuZXNfYXBveW9cXEJhbm5lci5qcGcpDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCiMjIyDCv1F1w6kgc2UgcmVhbGl6YXLDoT8NCkEgbG8gbGFyZ28gZGUgZXN0ZSBhcmNoaXZvIHNlIHBvZHLDoSB2aXN1YWxpemFyIGVsIHByb2Nlc28gZGUgbGltcGllemEgZGUgZGF0b3MgcXVlIHNlIHJlYWxpesOzIGVuIGxhcyBiYXNlcyBkZSBkYXRvcyBwcm9wb3JjaW9uYWRhcyBwb3IgKkZPUk0qIHBhcmEgc29sdWNpb25hciBsYXMgcHJvYmxlbcOhdGljYXMgZGUgKipQcm9ub3N0aWNvIGRlIHZlbnRhcyoqIHkgKipSb3RhY2nDs24gZGVsIHBlcnNvbmFsKiouIEVzdGUgcHJvY2VzbyBkZSBsaW1waWV6YSBmdWUgcmVhbGl6YWRvIHRhbnRvIGVuICpSKiBjb21vIGVuICpFeGNlbCogZGVwZW5kaWVuZG8gZGUgbGEgYWNjacOzbiBhIHJlYWxpemFyLiBEZSBlc3RlIHByb2Nlc28gc2UgYnVzY2Egb2J0ZW5lciBiYXNlcyBob21vbG9nYWRhcyBlIGluY2x1c28gdW5pZmljYWRhcyBzaSBlcyByZXF1ZXJpZG8gc2Vnw7puIGxhIHNpdHVhY2nDs24gcHJvYmxlbWEuDQoNCiMjIExpYnJlcmlhcyANCmBgYHtyfQ0KbGlicmFyeShkcGx5cikgICAgICMgTWFuaXB1bGFjacOzbiBkZSBkYXRvczogZmlsdHJvcywgdHJhbnNmb3JtYWNpb25lcywgYWdydXBhY2lvbmVzIHkgbcOhcy4NCmxpYnJhcnkoZ2dwbG90MikgICAjIFZpc3VhbGl6YWNpw7NuIGRlIGRhdG9zOiBncsOhZmljb3MgeSBkaWFncmFtYXMgZWxlZ2FudGVzIHkgcGVyc29uYWxpemFibGVzLg0KbGlicmFyeSh4dHMpICAgICAgICMgTWFuZWpvIHkgbWFuaXB1bGFjacOzbiBkZSBzZXJpZXMgdGVtcG9yYWxlcyBlbiBSLg0KbGlicmFyeShkeWdyYXBocykgICMgVmlzdWFsaXphY2nDs24gaW50ZXJhY3RpdmEgZGUgc2VyaWVzIHRlbXBvcmFsZXMuDQpsaWJyYXJ5KHRzZXJpZXMpICAgIyBBbsOhbGlzaXMgZGUgc2VyaWVzIHRlbXBvcmFsZXMgeSBwcnVlYmFzIGVzdGFkw61zdGljYXMuDQpsaWJyYXJ5KGZvcmVjYXN0KSAgIyBQcm9uw7NzdGljbyBkZSBzZXJpZXMgdGVtcG9yYWxlczogbW9kZWxvcyBBUklNQSwgRVRTLCBldGMuDQpsaWJyYXJ5KHpvbykgICAgICAgIyBNYW5pcHVsYWNpw7NuIGRlIHNlcmllcyB0ZW1wb3JhbGVzIGlycmVndWxhcmVzIG8gcmVndWxhcmVzLg0KbGlicmFyeShjb3JycGxvdCkgICMgVmlzdWFsaXphY2nDs24gZGUgbWF0cmljZXMgZGUgY29ycmVsYWNpw7NuLg0KbGlicmFyeShnZ3B1YnIpICAgICMgTWVqb3JhcyBlbiBsYSB2aXN1YWxpemFjacOzbiBkZSBncsOhZmljb3MgJ2dncGxvdDInIHBhcmEgcHVibGljYWNpb25lcy4NCmxpYnJhcnkoa2FibGVFeHRyYSkjIE1lam9yYSBkZSB0YWJsYXMgZW4gZm9ybWF0byAna25pdHInIHkgJ2thYmxlJyBwYXJhIHJlcG9ydGVzLg0KbGlicmFyeShvcGVueGxzeCkgICMgTGVjdHVyYSB5IGVzY3JpdHVyYSBkZSBhcmNoaXZvcyBFeGNlbCBzaW4gbmVjZXNpZGFkIGRlIEphdmEuDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHdyaXRleGwpDQpgYGANCg0KIyMgRGF0b3MgDQpgYGB7cn0NCnNldHdkKCJDOlxcVXNlcnNcXFNpbHZhXFxEb2N1bWVudHNcXENvbmNlbnRyYWNpw7NuXFxGb3JtX0UzIikNClJIX0EgPSByZWFkLmNzdigiQmFzZXNcXFJIX0FDVElWT1MuY3N2IikNClJIX0IgPSByZWFkeGw6OnJlYWRfeGxzeCgiQmFzZXNcXERhdG9zX0ZPUk1fUkhfRkoyMDI0Lnhsc3giKQ0KdmVudGFzMSA9IHJlYWR4bDo6cmVhZF94bHN4KCJCYXNlc1xcZm9ybV9iYWphcy54bHN4Iiwgc2hlZXQgPSAiVmVudGFzX21lbnN1YWxlcyIpDQp2ZW50YXMyXzIwMjEgPSByZWFkeGw6OnJlYWRfeGxzeCgiQmFzZXNcXERhdG9zX0ZPUk1fVmVudGFzX0ZKMjAyNC54bHN4Iiwgc2hlZXQgPSAiMjAyMSIpDQp2ZW50YXMyXzIwMjIgPSByZWFkeGw6OnJlYWRfeGxzeCgiQmFzZXNcXERhdG9zX0ZPUk1fVmVudGFzX0ZKMjAyNC54bHN4Iiwgc2hlZXQgPSAiMjAyMiIpDQp2ZW50YXMyXzIwMjMgPSByZWFkeGw6OnJlYWRfeGxzeCgiQmFzZXNcXERhdG9zX0ZPUk1fVmVudGFzX0ZKMjAyNC54bHN4Iiwgc2hlZXQgPSAiMjAyMyIpDQpgYGANCg0KPioqU29icmUgbG9zIGRhdG9zKiogIA0KVG9kYXMgbGFzIGJhc2VzIHV0aWxpemFkYXMgZW4gZXN0ZSBwcm9jZXNvIGZ1ZXJvbiBwcm9wb3JjaW9uYWRhcyBwb3IgKkZvcm0qIHkgcHJldmlvIGEgc3UgdXRpbGl6YWNpw7NuIGZ1ZXJvbiBzZWxlY2Npb25hcyBjaWVydGFzIHZhcmlhYmxlcyBxdWUgZWwgZXF1aXBvIGNvbnNpZGVyw7MgaW1wb3J0YW50ZXMgeSBxdWUgbm8gaW1wYWN0YXJhbiBlbiBwcm9ibGVtYXMgZGUgc2VndXJpZGFkIGRlIGluZm9ybWFjacOzbiBkZSBkYXRvcyBwZXJzb25hbGVzLg0KDQojIFZlbnRhcw0KIVtdKEM6XFxVc2Vyc1xcU2lsdmFcXERvY3VtZW50c1xcQ29uY2VudHJhY2nDs25cXEZvcm1fRTNcXEltYWdlbmVzX2Fwb3lvXFxwcm9kZC5qcGcpDQoNCiMjIFZlbnRhcyBjYXJ0b24sIHJldG9ybmFibGUsIHNlcnZpY2lvcyB5IHRvdGFsIDIwMjAtMjAyMg0KDQoNCiMjIyMgQ2FtYmlhciBmb3JtYXRvIGRlIGRhdG9zIGVuIGNvbHVtbmEgIkZlY2hhIg0KDQpgYGB7cn0NCnZlbnRhczEkRmVjaGEgPC0gYXMuRGF0ZSh2ZW50YXMxJEZlY2hhLCBmb3JtYXQgPSAiJWQvJW0vJVkiKQ0KYGBgDQoNCiMjIyMgSWRlbnRpZmljYWNpw7NuIGRlIE5Bcw0KYGBge3J9DQpOQWRldGVjdGVyPSBkYXRhLmZyYW1lKGNvbFN1bXMoaXMubmEodmVudGFzMSkpKQ0KY29sbmFtZXMoTkFkZXRlY3RlcikgPC0gYygiTkEncyIpDQpOQWRldGVjdGVyICU+JQ0KICBrYmwoKSAlPiUNCiAgYWRkX2hlYWRlcl9hYm92ZShjKCJDYW50aWRhZCBkZSBOQcK0cyBWZW50YXMgMSIgPSAyKSklPiUNCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KIyMjIyBNYW5lam8gZGUgTkFzDQpgYGB7cn0NCiMgRXh0cmFlciBNZXMgeSBBw7FvIGEgY29sdW1uYXMgbnVldmFzIA0KdmVudGFzMSA8LSB2ZW50YXMxICU+JQ0KICBtdXRhdGUoTWVzID0gZm9ybWF0KEZlY2hhLCAiJW0iKSwNCiAgICAgICAgIEHDsW8gPSBmb3JtYXQoRmVjaGEsICIlWSIpKQ0KYGBgDQoNCkxhIGZvcm1hIGVuIGxhIHF1ZSBpbXB1dGFyZW1vcyBsb3MgZGF0b3MgZmFsdGFudGVzIHNlcsOhIGNvbiBlbCBwcm9tZWRpbyBkZSBsYXMgdmVudGFzIGRlbCBtaXNtbyBtZXMgZGUgbG9zIGRlbcOhcyBhw7Fvcy4gUE9yIGVqZW1wbG8sIHNpIEVuZXJvIDIwMjAgZXMgdW4gTkEsIGxvIGltcHV0YXJlbW9zIGNvbiBlbCBwcm9tZWRpbyBkZSBlbmVybyAyMDIxIHkgZW5lcm8gMjAyMi4gRW4gY2FzbyBkZSBubyBoYWJlciBkYXRvcyBzdWZpY2llbnRlcyBwYXJhIHNhY2FyIGVsIHByb21lZGlvLCBsbyBoYXJlbW9zIGNvbiBlbCBwcm9tZWRpbyBkZSBsYXMgZGVtw6FzIGNhbnRpZGFkZXMgZGUgZXNlIGHDsW8geSBjYXRlZ29yaWEuIExvcyBkYXRvcyBwdWVkZW4gbm8gc2VyIG11eSBwcmVjaXNvcywgcGVybyBjb25zaWRlcmFtb3MgcXVlIGVzIGxhIGZvcm1hIG3DoXMgYWNlcnRhZGEuIA0KDQpgYGB7cn0NCiMgQ2FydG9uDQoNCiMgQ2FsY3VsYXIgZWwgcHJvbWVkaW8gZGUgQ2FydG9uIHBvciBtZXMNCnByb21lZGlvc19jYXJ0b24gPC0gdmVudGFzMSAlPiUNCiAgZ3JvdXBfYnkoTWVzKSAlPiUNCiAgc3VtbWFyaXNlKHByb21lZGlvX0NhcnRvbiA9IG1lYW4oQ2FydG9uLCBuYS5ybSA9IFRSVUUpKQ0KDQojIFVuaXIgbG9zIHByb21lZGlvcyBjb24gbG9zIGRhdG9zIG9yaWdpbmFsZXMNCnZlbnRhczEgPC0gdmVudGFzMSAlPiUNCiAgbGVmdF9qb2luKHByb21lZGlvc19jYXJ0b24sIGJ5ID0gIk1lcyIpDQoNCiMgSW1wdXRhciBsb3MgdmFsb3JlcyBmYWx0YW50ZXMgZW4gQ2FydG9uIGNvbiBsb3MgcHJvbWVkaW9zDQp2ZW50YXMxJENhcnRvbiA8LSBpZmVsc2UoaXMubmEodmVudGFzMSRDYXJ0b24pLCB2ZW50YXMxJHByb21lZGlvX0NhcnRvbiwgdmVudGFzMSRDYXJ0b24pDQpgYGANCg0KDQpgYGB7cn0NCiMgUmVwZXRpciBlbCBwcm9jZXNvIHBhcmEgUmV0b3JuYWJsZSANCg0KcHJvbWVkaW9zX3JldG9ybmFibGUgPC0gdmVudGFzMSAlPiUNCiAgZ3JvdXBfYnkoTWVzKSAlPiUNCiAgc3VtbWFyaXNlKHByb21lZGlvX1JldG9ybmFibGUgPSBtZWFuKFJldG9ybmFibGUsIG5hLnJtID0gVFJVRSkpDQoNCnZlbnRhczEgPC0gdmVudGFzMSAlPiUNCiAgbGVmdF9qb2luKHByb21lZGlvc19yZXRvcm5hYmxlLCBieSA9ICJNZXMiKQ0KDQp2ZW50YXMxJFJldG9ybmFibGUgPC0gaWZlbHNlKGlzLm5hKHZlbnRhczEkUmV0b3JuYWJsZSksIHZlbnRhczEkcHJvbWVkaW9fUmV0b3JuYWJsZSwgdmVudGFzMSRSZXRvcm5hYmxlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTZXJ2aWNpb3MNCg0KcHJvbWVkaW9zX3NlcnZpY2lvcyA8LSB2ZW50YXMxICU+JQ0KICBncm91cF9ieShNZXMpICU+JQ0KICBzdW1tYXJpc2UocHJvbWVkaW9fU2VydmljaW9zID0gbWVhbihTZXJ2aWNpb3MsIG5hLnJtID0gVFJVRSkpDQoNCnZlbnRhczEgPC0gdmVudGFzMSAlPiUNCiAgbGVmdF9qb2luKHByb21lZGlvc19zZXJ2aWNpb3MsIGJ5ID0gIk1lcyIpDQoNCnZlbnRhczEkU2VydmljaW9zIDwtIGlmZWxzZShpcy5uYSh2ZW50YXMxJFNlcnZpY2lvcyksIHZlbnRhczEkcHJvbWVkaW9fU2VydmljaW9zLCB2ZW50YXMxJFNlcnZpY2lvcykNCmBgYA0KDQpgYGB7cn0NCiMgVG90YWwNCg0KcHJvbWVkaW9zX3RvdGFsIDwtIHZlbnRhczEgJT4lDQogIGdyb3VwX2J5KE1lcykgJT4lDQogIHN1bW1hcmlzZShwcm9tZWRpb19Ub3RhbCA9IG1lYW4oVG90YWwsIG5hLnJtID0gVFJVRSkpDQoNCnZlbnRhczEgPC0gdmVudGFzMSAlPiUNCiAgbGVmdF9qb2luKHByb21lZGlvc190b3RhbCwgYnkgPSAiTWVzIikNCg0KdmVudGFzMSRUb3RhbCA8LSBpZmVsc2UoaXMubmEodmVudGFzMSRUb3RhbCksIHZlbnRhczEkcHJvbWVkaW9fVG90YWwsIHZlbnRhczEkVG90YWwpDQpgYGANCg0KYGBge3J9DQojIEVsaW1pbmFyIGxhcyBjb2x1bW5hcyBkZSBwcm9tZWRpb3MNCnZlbnRhczEgPC0gdmVudGFzMSAlPiUNCiAgc2VsZWN0KC1wcm9tZWRpb19DYXJ0b24sIC1wcm9tZWRpb19SZXRvcm5hYmxlLCAtcHJvbWVkaW9fU2VydmljaW9zLCAtcHJvbWVkaW9fVG90YWwpDQpgYGANCg0KQcO6biBxdWVkYW4gTmFOcyBlbiBsYSBjb2x1bW5hIGRlIHNlcnZpY2lvcyBlbiBsb3MgbWVzZXMgZG9uZGUgbm8gaGF5IGRhdG9zIGVuIG5pbmfDum4gYcOxby4NCg0KYGBge3J9DQpzdW0oaXMubmFuKHZlbnRhczEkU2VydmljaW9zKSkNCmBgYA0KSW1wdXRhcmVtb3MgZXN0b3MgZGF0b3MgY29uIGVsIHByb21lZGlvIGRlIFNlcnZpY2lvcyBkZWwgYcOxbyBhbCBxdWUgcGVydGVuZWNlIGVzZSBOYU4uDQoNCmBgYHtyfQ0KIyBDYWxjdWxhciBlbCBwcm9tZWRpbyBhbnVhbCBkZSBTZXJ2aWNpb3MNCnByb21lZGlvc19zZXJ2aWNpb3MgPC0gdmVudGFzMSAlPiUNCiAgZ3JvdXBfYnkoQcOxbykgJT4lDQogIHN1bW1hcmlzZShwcm9tZWRpb19TZXJ2aWNpb3MgPSBtZWFuKFNlcnZpY2lvcywgbmEucm0gPSBUUlVFKSkNCg0KIyBVbmlyIGxvcyBwcm9tZWRpb3MgYW51YWxlcyBjb24gbG9zIGRhdG9zIG9yaWdpbmFsZXMNCnZlbnRhczEgPC0gdmVudGFzMSAlPiUNCiAgbGVmdF9qb2luKHByb21lZGlvc19zZXJ2aWNpb3MsIGJ5ID0gIkHDsW8iKQ0KDQojIFJlZW1wbGF6YXIgbG9zIHZhbG9yZXMgTmFOIGVuIFNlcnZpY2lvcyBjb24gbG9zIHByb21lZGlvcyBhbnVhbGVzDQp2ZW50YXMxIDwtIHZlbnRhczEgJT4lDQogIG11dGF0ZShTZXJ2aWNpb3MgPSBpZmVsc2UoaXMubmFuKFNlcnZpY2lvcyksIHByb21lZGlvX1NlcnZpY2lvcywgU2VydmljaW9zKSkNCg0KIyBFbGltaW5hciBsYSBjb2x1bW5hIGRlIHByb21lZGlvcw0KdmVudGFzMSA8LSB2ZW50YXMxICU+JQ0KICBzZWxlY3QoLXByb21lZGlvX1NlcnZpY2lvcykNCmBgYA0KDQoNCiMjIyMgRXN0cnVjdHVyYSBmaW5hbCBkZSBWZW50YXMgMQ0KDQpMYSBlc3RydWN0dXJhIGRlIGVzdGEgYmFzZSBkZSBkYXRvcyBjb25zdGEgZW4gNSB2YXJpYWJsZXMgcHJpbmNpcGFsZXMgbGFzIGN1YWxlcyBzb24gRmVjaGEgKHBvciBtZXMgZGUgMjAyMCAtIDIwMjIpLCBDYXJ0b24sIFNlcnZpY2lvcywgUmV0b3JuYWJsZSB5IFRvdGFsLiBBZ3JlZ2Ftb3MgMiBjb2x1bW5hcywgdW5hIGV4dHJheWVuZG8gZWwgbWVzIHkgbGEgb3RyYSBlbCBhw7FvLCBlc3RvIHBhcmEgcG9kZXIgdGFtYmnDqW4gYW5hbGl6YXIgZWwgZWZlY3RvIHF1ZSB0aWVuZSBjYWRhIG1lcyB5IGNhZGEgYcOxbyBxdWUgdmEgdHJhbnNjdXJyaWVuZG8uIFVzYXJlbW9zIGVzdGEgYmFzZSBkZSBkYXRvcyBwYXJhIHByZWRlY2lyIGxhcyB2ZW50YXMgZGUgbG9zIHNpZ3VpZW50ZXMgYcOxb3MsIHBlcm8gZGViaWRvIGEgcXVlIGxvcyDDumx0aW1vcyBkYXRvcyBxdWUgdGVuZW1vcyBzb24gZGUgMjAyMiwgbnVlc3RyYXMgcHJlZGljY2lvbmVzIHB1ZWRlbiBubyBzZXIgbXV5IHByZWNpc2FzIGFsIGxsZWdhciBhIDIwMjQuIA0KDQojIyBWZW50YXMgcG9yIGNhdGVnb3LDrWEgZGUgcHJvZHVjdG9zDQoNCmBgYHtyfQ0KIyBVbmltb3MgbGFzIHRyZXMgcGVzdGHDsWFzIHBhcmEgdGVuZXIgdW5hIGJhc2UgZGUgZGF0b3MgY29tcGxldGEgY29uIGxvcyBhw7FvcyAyMDIxLCAyMDIyIHkgMjAyMy4gDQp2ZW50YXMyX2FjdHVhbGl6YWRvIDwtIHJiaW5kKHZlbnRhczJfMjAyMSwgdmVudGFzMl8yMDIyLHZlbnRhczJfMjAyMykNCmBgYA0KDQojIyBJZGVudGlmaWNhY2nDs24gZGUgTkFzICANCmBgYHtyfQ0KTkFkZXRlY3Rlcj0gZGF0YS5mcmFtZShjb2xTdW1zKGlzLm5hKHZlbnRhczJfYWN0dWFsaXphZG8pKSkNCmNvbG5hbWVzKE5BZGV0ZWN0ZXIpIDwtIGMoIk5BJ3MiKQ0KTkFkZXRlY3RlciAlPiUNCiAga2JsKCkgJT4lDQogIGFkZF9oZWFkZXJfYWJvdmUoYygiQ2FudGlkYWQgZGUgTkHCtHMgVmVudGFzIDIiID0gMikpJT4lDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCmBgYHtyfQ0KbW9kYSA8LSBuYW1lcyhzb3J0KHRhYmxlKHZlbnRhczJfYWN0dWFsaXphZG8kYE5vLiBPQyBDbGllbnRlYCksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMV0NCm1vZGExIDwtIG5hbWVzKHNvcnQodGFibGUodmVudGFzMl9hY3R1YWxpemFkbyRgUmVmLiBjbGllbnRlYCksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMV0NCmBgYA0KDQpgYGB7cn0NCiMgSW1wdXRhciBsb3MgdmFsb3JlcyBmYWx0YW50ZXMgZW4gJ05vIE9DIENsaWVudGUnIGNvbiBsYSBtb2RhIA0KdmVudGFzMl9hY3R1YWxpemFkb1tpcy5uYSh2ZW50YXMyX2FjdHVhbGl6YWRvKV0gPC0gbW9kYQ0KDQojIEltcHV0YXIgbG9zIHZhbG9yZXMgZmFsdGFudGVzIGVuICdSZWYgQ2xpZW50ZScgY29uIGxhIG1vZGENCnZlbnRhczJfYWN0dWFsaXphZG9baXMubmEodmVudGFzMl9hY3R1YWxpemFkbyldIDwtIG1vZGExDQpgYGANCg0KYGBge3J9DQojIFZlcmlmaWNhbW9zIHF1ZSB5YSBubyBoYXlhIE5BJ3MNCk5BZGV0ZWN0ZXI9IGRhdGEuZnJhbWUoY29sU3Vtcyhpcy5uYSh2ZW50YXMyX2FjdHVhbGl6YWRvKSkpDQpjb2xuYW1lcyhOQWRldGVjdGVyKSA8LSBjKCJOQSdzIikNCk5BZGV0ZWN0ZXIgJT4lDQogIGtibCgpICU+JQ0KICBhZGRfaGVhZGVyX2Fib3ZlKGMoIkNhbnRpZGFkIGRlIE5BwrRzIFZlbnRhcyAyIiA9IDIpKSU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpgYGB7cn0NCiMgUXVpdGFtb3MgbG9zIGVzcGFjaW9zIGVuIGxvcyBub21icmVzIGRlIGxhcyB2YXJpYWJsZXMgeSByZWVtcGxhemFtb3MgY29uIGd1acOzbiBiYWpvDQoNCm5vbWJyZXNfdmFyaWFibGVzIDwtIG5hbWVzKHZlbnRhczJfYWN0dWFsaXphZG8pDQpub21icmVzX3ZhcmlhYmxlc19udWV2b3MgPC0gZ3N1YigiICIsICJfIiwgbm9tYnJlc192YXJpYWJsZXMpDQpuYW1lcyh2ZW50YXMyX2FjdHVhbGl6YWRvKSA8LSBub21icmVzX3ZhcmlhYmxlc19udWV2b3MNCmBgYA0KDQpgYGB7cn0NCiNBZ3JlZ2Ftb3MgdW5hIGNvbHVtbmEgY29uIGVsIGHDsW8gZGUgbGEgdmVudGEgKHlhIHF1ZSB1bmltb3MgbGFzIHRyZXMgcGVzdGHDsWFzIGNvbiBjYWRhIGHDsW8pDQp2ZW50YXMyX2FjdHVhbGl6YWRvJEZlY2hhIDwtIGFzLkRhdGUodmVudGFzMl9hY3R1YWxpemFkbyRGZWNoYSkNCg0KdmVudGFzMl9hY3R1YWxpemFkbyRBw7FvIDwtIHllYXIodmVudGFzMl9hY3R1YWxpemFkbyRGZWNoYSkNCmBgYA0KDQpgYGB7cn0NCiMgQ2FtYmlhbW9zIGxvcyBub21icmVzIGRlIGxvcyBjbGllbnRlcyBhIE1BWVVTQ1VMQVMNCnZlbnRhczJfYWN0dWFsaXphZG8kQ2xpZW50ZSA8LSB0b3VwcGVyKHZlbnRhczJfYWN0dWFsaXphZG8kQ2xpZW50ZSkNCmBgYA0KDQpgYGB7cn0NCiMgSnVudGFtb3MgY2xpZW50ZXMgY29uIG5vbWJyZXMgaWd1YWxlcw0KdmVudGFzMl9hY3R1YWxpemFkbyRDbGllbnRlIDwtIGdzdWIoIllBTkZFTkcgU0VBVElORyBNRVhJQ08gU0EgREUgQ1YiLCAiWUFORkVORyBTRUFUSU5HIE1FWElDTyIsIHZlbnRhczJfYWN0dWFsaXphZG8kQ2xpZW50ZSkNCmBgYA0KDQpgYGB7cn0NCiMgVmVyaWZpY2Ftb3MgcXVlIHlhIG5vIGhheWEgbWlzbW9zIGNsaWVudGVzIGNvbiBub21icmUgZGlmZXJlbnRlDQpmcmVjdWVuY2lhX2NsaWVudGVzIDwtIHZlbnRhczJfYWN0dWFsaXphZG8gJT4lDQogIGNvdW50KENsaWVudGUpDQoNCmZyZWN1ZW5jaWFfY2xpZW50ZXMNCmBgYA0KDQpgYGB7cn0NCiMgQWdydXBhciBwb3IgcHJvZHVjdG8gKEVzIG11eSBkaWZpY2lsIGlkZW50aWZpY2FyIHNpIGhheSBwcm9kdWN0b3MgaWd1YWxlcyBxdWUgdGVuZ2FuIG5vbWJyZSBkaWZlcmVudGUgeWEgcXVlIGxhIGNhbnRpZGFkIGVzIG11eSBncmFuZGUgeSBsb3Mgbm9tYnJlIG11eSBsYXJnb3MpDQpmcmVjdWVuY2lhX3Byb2R1Y3RvIDwtIHZlbnRhczJfYWN0dWFsaXphZG8gJT4lDQogIGNvdW50KFByb2R1Y3RvKQ0KDQpmcmVjdWVuY2lhX3Byb2R1Y3RvDQpgYGANCg0KYGBge3J9DQojIEFncnVnYXIgbGFzIG9ic2VydmFjaW9uZXMgZW4gbGEgY29sdW1uYSBkZSAnRXN0YWRvJyBzb2xhbWVudGUgcGFyYSB2ZXJpZmljYXIgY3VhbGVzIHNvbiBsYXMgY2F0ZWdvcsOtYXMgKEVzdGFyw61hIGJpZW4gcHJlZ3VudGFyIGEgRk9STSBsYXMgcmF6b25lcyBkZSBsb3MgcGVkaWRvcyBjYW5jZWxhZG9zKQ0KDQpmcmVjdWVuY2lhX2VzdGFkbyA8LSB2ZW50YXMyX2FjdHVhbGl6YWRvICU+JQ0KICBjb3VudChFc3RhZG8pDQoNCmZyZWN1ZW5jaWFfZXN0YWRvDQpgYGANCg0KIyMjIyBFc3RydWN0dXJhIGZpbmFsIFZlbnRhcyAyDQoNCkVzdGEgYmFzZSB0aWVuZSB1bmEgZ3JhbiBjYW50aWRhZCBkZSBkYXRvcyB0YW50byBjYXRlZ8Ozcmljb3MgY29tbyBudW3DqXJpY29zLCBsYXMgbW9kaWZpY2FjaW9uZXMgc2UgaGljaWVyb24gbcOhcyBxdWUgbmFkYSBlbiBsb3Mgbm9tYnJlcyBkZSBsb3MgY2xpZW50ZXMgbGEgY3VhbCBlcyB1bmEgdmFyaWFibGUgaW1wb3J0YW50ZSBhbCBhbmFsaXphciBzdXMgcGVkaWRvcyBlbiBGT1JNLCB5IGVuIGRvbmRlIHR1dmltb3MgcXVlIGFzZWd1cmFybm9zIHF1ZSBubyBzZSB0b21hcmEgZWwgbWlzbW8gY2xpZW50ZSBjb21vIGRpZmVyZW50ZSBwb3IgZXN0YXIgbWFsIGVzY3JpdG8uIA0KDQoNCiMgUkgNCiFbXShDOlxcVXNlcnNcXFNpbHZhXFxEb2N1bWVudHNcXENvbmNlbnRyYWNpw7NuXFxGb3JtX0UzXFxJbWFnZW5lc19hcG95b1xccm90YWNpb24yLmpwZWcpDQoNCiMjIyMgQ29udGV4dG8gIA0KUGFyYSBsYSBwcm9ibGVtw6F0aWNhIGRlIHJvdGFjacOzbiBkZSBwZXJzb25hbCBpbmljaWFsbWVudGUgc2UgZGVjaWRpw7MgKiptYW50ZW5lciDDum5pY2FtZW50ZSB2YXJpYWJsZXMgcXVlIGZ1ZXJhbiByZWxldmFudGVzKiogcGFyYSBlbCBlcXVpcG8geSBubyBpbnZvbHVjcmFyYW4gcHJvYmxlbWFzIGhhY2lhIGxhIHNlZ3VyaWRhZCBkZSBsYSBpbmZvcm1hY2nDs24sIGRlIGlndWFsIGZvcm1hIHNlIGJ1c2PDsyAqKmdlbmVyYXIgdmFyaWFibGVzKiogY2xhdmVzIGNvbiBsb3MgZGF0b3MgcHJvcG9yY2lvbmFkb3MsIGNvbW8gc3UgKiplZGFkIHkgZHVyYWNpw7NuIGVuIGxhIGVtcHJlc2EqKi4NCg0KTGFzIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMgY29uIGxhcyBxdWUgYnVzY2Ftb3MgcXVlZGFybm9zIHNvbjogIA0KDQoqIEdlbmVybzogU2V4byBkZWwgY29sYWJvcmFkb3IgKE1BU0NVTElOTywgRkVNRU5JTk8pLg0KKiBGZWNoYV9BbHRhOiBGZWNoYSBlbiBxdWUgc2UgZGlvIGRlIGFsdGEgYWwgY29sYWJvcmFkb3IuDQoqIEZlY2hhX0JhamE6IEZlY2hhIGVuIHF1ZSBzZSBkaW8gZGUgYmFqYSBhbCBjb2xhYm9yYWRvci4NCiogUHVlc3RvOiBDYXJnbyBvIHBvc2ljacOzbiBxdWUgb2N1cGEgZWwgY29sYWJvcmFkb3IuDQoqIFNEOiBTYWxhcmlvIERpYXJpbyBkZWwgY29sYWJvcmFkb3IuDQoqIEx1Z2FyX05hY2ltaWVudG86IENpdWRhZCBvIGxvY2FsaWRhZCBkb25kZSBuYWNpw7MgZWwgY29sYWJvcmFkb3IuDQoqIE11bmljaXBpbzogTXVuaWNpcGlvIGRlIHJlc2lkZW5jaWEgZGVsIGNvbGFib3JhZG9yLg0KKiBFc3RhZG86IEVzdGFkbyBvIGVudGlkYWQgZmVkZXJhdGl2YSBkZSByZXNpZGVuY2lhIGRlbCBjb2xhYm9yYWRvci4NCiogQ1A6IEPDs2RpZ28gUG9zdGFsIGRlbCBsdWdhciBkZSByZXNpZGVuY2lhIGRlbCBjb2xhYm9yYWRvci4NCiogRXN0YWRvX0NpdmlsOiBFc3RhZG8gY2l2aWwgZGVsIGNvbGFib3JhZG9yIChNQVRSSU1PTklPLCBTT0xURVJJQSwgZXRjLikuDQoqIGRpYXNfdHJhYmFqYWRvczogTsO6bWVybyBkZSBkw61hcyB0cmFiYWphZG9zIHBvciBlbCBjb2xhYm9yYWRvci4NCiogZGlmZXJlbmNpYXNfbWVzZXM6IE7Dum1lcm8gZGUgbWVzZXMgdHJhYmFqYWRvcyBwb3IgZWwgY29sYWJvcmFkb3IuDQoqIEVkYWQ6IEVkYWQgZGVsIGNvbGFib3JhZG9yLg0KDQojIyBBY3Rpdm9zDQojIyMgRXhjZWwNCiMjIyMgVGllbXBvcyB5IEVkYWQNCg0KYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0nODAlJywgZmlnLmFsaWduPSdjZW50ZXInfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIkltYWdlbmVzX2Fwb3lvXFxUaWVtcG9FZGFkMi5wbmciKQ0KYGBgDQoNClBhcmEgbGFzIHZhcmlhYmxlcyByZWZlcmVudGVzIGFsIHRpZW1wbyB0cmFiYWphZG8geSBhIGxhIGVkYWQgc2UgcGVuc8OzIGVuIHNhY2FyIGxhIGRpZmVyZW5jaWEgZGUgZMOtYXMgZW50cmUgY2llcnRhcyBmZWNoYXMgY2xhdmUoTmFjaW1pZW50bywgQWx0YSwgQmFqYSkuIExhIGVkYWQgZnVlIHNhY2FkYSBoYXN0YSBlbCBkw61hIHF1ZSBzZSB0cmFiYWrDsyBkZW50cm8gZGUgbGEgb3JnYW5pemFjacOzbi4NCg0KTGEgdmFyaWFibGUgZGUgZWRhZCBjb25zaWRlcmFtb3MgcXVlIGVyYSBkZSBzdW1hIGltcG9ydGFuY2lhIGFncmVnYXJsYSBhIGxhIGJhc2UgZGUgZGF0b3MsIGRlYmlkbyBhIHRlbWFzICoqZ2VuZXJhY2lvbmFsZXMqKiBxdWUgbGxlZ2FiYSBhIG1lbmNpb25hciBlbCBzb2NpbyBmb3JtYWRvciB5IGRlIGNpZXJ0b3MgY29tcG9ydGFtaWVudG9zIHF1ZSB5YSBoYWLDrWFuIHNpZG8gY29uc2lkZXJhZG9zLCBlbiBjdWFudG8gYWwgdGllbXBvIGRlIHRyYWJham8gZnVlIGNvbnNpZGVyYWRvIGltcG9ydGFudGUgcGFyYSBwb2RlciBjb25zaWRlcmFyICoqZGlmZXJlbnRlcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMqKiBlbiBlbCBmdXR1cm8uDQogIA0KICANCj4gKirCv0ZlY2hhIGRlIGJhamEgZW4gYWN0aXZvcz8qKiAgDQpQdWVkZSBzb25hciBpbG9naWNvIHF1ZSBsb3MgYWN0aXZvcyBlbiBsYSBvcmdhbml6YWNpw7NuIHRlbmdhbiB1bmEgZmVjaGEgZGUgc2FsaWRhLCBwZXJvIGVyYSBuZWNlc2FyaW8gcXVlIHR1dmllcmFuIGFsZ3VuYSBwYXJhIHBvZGVyIGhhY2VyIGFuYWxpc2lzIHBvc3RlcmlvcmVzIGNvbiBsb3MgZMOtYXMgdHJhYmFqb3MsIGVzIHBvciBlc28gcXVlIHNlIHRvbW8gY29tbyByZWZlbmNpYSBlbCBkw61hIGVuIGVsIGN1YWwgc2UgdHJhdMOzIGxhIHZhcmlhYmxlLiANCg0KIyMjIyBUaXBvIHkgQWN0aXZvDQpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSc4MCUnLCBmaWcuYWxpZ249J2NlbnRlcid9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiSW1hZ2VuZXNfYXBveW9cXFRpcG9BY3Rpdm8yLnBuZyIpDQpgYGANCg0KTGEgdmFyaWFibGUgZGUgKipBY3Rpdm8qKiBlcyBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSAicHJlZGV0ZXJtaW5hZGEiIGRlbCBjYXNvLCBwZXJvIGNvbnNpZGVyYW1vcyBxdWUgbm8gZXJhIHN1ZmljaWVudGUsIGFsIHRlbmVyIHVuYSBwcm9ibGVtw6F0aWNhIG11eSBwdW56YW50ZSBlbiBsYSByb3RhY2nDs24gZGVsIHBlcnNvbmFsIGRlY2lkaW1vcyBlbmZvY2Fybm9zIHByaW5jaXBhbG1lbnRlIGVuIGVsIHByb2Nlc28gZGUgKipzZWxlY2Npw7NuKioNCg0KIExvcyBtb2RlbG9zIGRlIElBIG1hbmVqYW4gdW4gY29uY2VwdG8gZGUgKkdhcmJhZ2UgaW4sIGdhcmJhZ2Ugb3V0KiBkb25kZSBsb3MgcmVzdWx0YWRvcyBxdWUgb2J0ZW5nYXMgZGVwZW5kZXLDoW4gZW4gZ3JhbiBtZWRpZGEgZGUgbGEgY2FsaWRhZCBkZSBkYXRvcyBxdWUgaW5ncmVzZXMsIGRlY2lkaW1vcyBtYW5lamFyIGxhICoqbWlzbWEgaWRlb2xvZ8OtYSBlbiBsYSByb3RhY2nDs24gZGVsIHBlcnNvbmFsKiouIFNlIGJ1c2NhIGRlZmluaXIgYSBsb3MgcHJvc3BlY3RvcyBjb24gcHJvYmFiaWxpZGFkZXMgcXVlIGN1bXBsYW4gY2llcnRvIHRpZW1wbyBlbiBsYSBvcmdhbml6YWNpw7NuLCBkZSBlc3RhIG1hbmVyYSBzZSBwdWVkZSBpciAqKm1lam9yYW5kbyBsYXMgbWVkaWRhcyBkZSBkdXJhY2nDs24geSBkaXNtaW51eWVuZG8gcG9jbyBhIHBvY28gbGEgcm90YWNpw7NuKiouIA0KICAgDQogICAgDQo+ICoqwr9RdcOpIHNvbiBUaXBvIHkgQWN0aXZvPyoqICANCkxhcyB2YXJpYWJsZXMgIlRpcG8iIHkgIkFjdGl2byIgc29uIG51ZXN0cmFzIHZhcmlhYmxlcyBkZXBlbmRpZW50ZXMsICpBY3Rpdm8qIHNpZ25pZmljYW5kbyBxdWUgc2lndWVuIHRyYWJhamFuZG8gZW4gbGEgZW1wcmVzYSB5ICpUaXBvKiBlcyBjcmVhZGEgYXBhcnRpciBkZSB1bmEgY29uZGljaW9uYWwgc29icmUgImRpZmVyZW5jaWFfbWVzZXMiIGRvbmRlIGFxdWVsbG9zIHF1ZSBoYXlhbiBvIGhhbiBkdXJhZG8gbWluaW1vIDIgbWVzZXMgZW4gbGEgZW1wcmVzYSBzZXLDoW4gbWFyY2Fkb3MgY29tbyAiMSIuDQoNCiMjIyBSDQojIyMjIEhvbW9sb2dhciBsdWdhcmVzIGRlIG5hY2ltaWVudG8NCmBgYHtyfQ0KIyBMVUdBUiBERSBOQUNJTUlFTlRPDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipOVUVWTyBMRU9OLionLCAnTlVFVk8gTEVPTicsIFJIX0EkTHVnYXJfTmFjaW1pZW50bykNClJIX0EkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKlNBTiBOSUNPTEFTIERFIExPUyBHQVJaQSonLCAnTlVFVk8gTEVPTicsIFJIX0EkTHVnYXJfTmFjaW1pZW50bykNClJIX0EkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKk4uTC4qJywgJ05VRVZPIExFT04nLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipNT05URVJSRVkqJywgJ05VRVZPIExFT04nLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipNVFkqJywgJ05VRVZPIExFT04nLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipUQU1BVUxJUEFTLionLCAnVEFNQVVMSVBBUycsIFJIX0EkTHVnYXJfTmFjaW1pZW50bykNClJIX0EkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKkNPQUhVSUxBLionLCAnQ09BSFVJTEEnLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipWRVJBQ1JVWionLCAnVkVSQUNSVVonLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipTQU4gTFVJUyBQT1RPU0kqJywgJ1NBTiBMVUlTIFBPVE9TSScsIFJIX0EkTHVnYXJfTmFjaW1pZW50bykNClJIX0EkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKkNISUFQQVMqJywgJ0NISUFQQVMnLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipaQUNBVEVDQVMqJywgJ1pBQ0FURUNBUycsIFJIX0EkTHVnYXJfTmFjaW1pZW50bykNClJIX0EkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKk9BWEFDQSonLCAnT0FYQUNBJywgUkhfQSRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQSRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qSElEQUxHTyonLCAnSElEQUxHTycsIFJIX0EkTHVnYXJfTmFjaW1pZW50bykNClJIX0EkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKkRVUkFOR08qJywgJ0RVUkFOR08nLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipHVUFOQUpVQVRPKicsICdHVUFOQUpVQVRPJywgUkhfQSRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQSRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qUFVFQkxBKicsICdQVUVCTEEnLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipUQUJBU0NPKicsICdUQUJBU0NPJywgUkhfQSRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQSRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qQ0hJSFVBSFVBKicsICdDSElIVUFIVUEnLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipNRVhJQ08qJywgJ0NETVgnLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipESVNUUklUTyBGRURFUkFMKicsICdDRE1YJywgUkhfQSRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQSRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qUVVJTlRBTkEgUk9PKicsICdRVUlOVEFOQSBST08nLCBSSF9BJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9BJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipKQUxJU0NPKicsICdKQUxJU0NPJywgUkhfQSRMdWdhcl9OYWNpbWllbnRvKQ0KDQpSSF9BJEx1Z2FyX05hY2ltaWVudG9bUkhfQSRMdWdhcl9OYWNpbWllbnRvID09ICIiXSA9IFJIX0EkRXN0YWRvW1JIX0EkTHVnYXJfTmFjaW1pZW50byA9PSAiIl0NCg0KDQojIEVTVEFETw0KUkhfQSRFc3RhZG9bd2hpY2goUkhfQSRFc3RhZG8gPT0gJ05VRVZPIExFw5NOJyldIDwtICdOVUVWTyBMRU9OJw0KYGBgDQoNCkVuIGFtYmFzIGJhc2VzIHNlIGJ1c2NvIGhvbW9sb2dhciB1biBwb2NvIGVsIGx1Z2FyIGRlIG5hY2ltaWVudG8gZGVsIHBlcnNvbmFsLCBkZWphbmRvIGRlIGNvbnNpZGVyYSBsYXMgY2l1ZGFkZXMgbyBtdW5pY2lwaW9zIGRvbmRlIG5hY2llcm9uIHkgKipjb25jZW50csOhbmRvbm9zIMO6bmljYW1lbnRlIGVuIGVsIGVzdGFkbyoqLCBkaXNtaW51eWVuZG8gbGEgY2FudGlkYWQgZGUgbml2ZWxlcyBhbCBtb21lbnRvIGRlIGhhY2VyIGxhIHZhcmlhYmxlIGZhY3RvciB5IGJ1c2NhbmRvIHF1ZSBsYSB2YXJpYWJsZSBwdWVkYSB0ZW5lciBhbGdvIGRlIHNpZ25pZmljYW5jaWEgZW4gbG9zIG1vZGVsb3MuIA0KDQojIyMjIElkZW50aWZpY2FjacOzbiB5IFJlbXBsYXpvIGRlIE5BwrRzIA0KYGBge3J9DQpSSF9BIDwtIHN1YnNldChSSF9BLCBzZWxlY3QgPSAtYyhYLCBYLjEsIFguMiwgWC4zKSkNCk5BZGV0ZWN0ZXI9IGRhdGEuZnJhbWUoY29sU3Vtcyhpcy5uYShSSF9BKSkpDQpjb2xuYW1lcyhOQWRldGVjdGVyKSA8LSBjKCJOQSdzIikNCk5BZGV0ZWN0ZXIgJT4lDQogIGtibCgpICU+JQ0KICBhZGRfaGVhZGVyX2Fib3ZlKGMoIkNhbnRpZGFkIGRlIE5BwrRzIFJIIEFjdGl2b3MiID0gMikpJT4lDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpSSF9BJEZlY2hhX05hY2ltaWVudG8gPC0gYXMuRGF0ZShSSF9BJEZlY2hhX05hY2ltaWVudG8sIGZvcm1hdCA9ICIlZC0lbS0lWSIpDQpgYGANCg0KDQpBbCB0ZXJtaW5hciBjb24gbG9zIHByb2Nlc29zIGRlIGxpbXBpZXphIGxhIGJhc2UgZGUgKipBY3Rpdm9zKiogdGVybWluw7Mgc2luIG3DoXMgdmFsb3JlcyBmYWx0YW50ZXMgcXVlIHRyYXRhciB5IHNlIGVuY3VlbnRyYSBsaXN0YSBwYXJhIHVuaXIgY29uIGxhIGJhc2UgZGUgQmFqYXMuICANCg0KIyMgQmFqYXMNCj4gKipFeGNlbCBlbiBCYWphcyoqICANCkVuIGVsIGNhc28gZGUgbGEgYmFzZSBkZSBkYXRvcyBkZSBiYWphcyBzb2xvIHNlIHV0aWxpesOzIGxhIGhlcnJhbWllbnRhICpFeGNlbCogcGFyYSBoYWNlciBsZSBwcmltZXIgc3Vic2V0IGRlIHZhcmlhYmxlcyBlbmZvY2FkbyBlbiBsYSBzZWd1cmlkYWQgZGUgZGF0b3MgcGVyc29uYWxlcyBkZSBsb3MgY29sYWJvcmFkb3JlcyBkZSAqRk9STSoNCg0KIyMjIFINCiMjIyMgSG9tb2xvZ2FyIGx1Z2FyZXMgZGUgbmFjaW1pZW50bw0KYGBge3J9DQojIE1BWVVTQ1VMQVMNClJIX0IgPC0gbXV0YXRlX2lmKFJIX0IsIGlzLmNoYXJhY3RlciwgdG91cHBlcikgIyBQb25lciB0b2RvcyBsb3MgY2FyYWN0ZXJlcyBlbiBtYXl1c2N1bGFzDQpgYGANCg0KRGViaWRvIGEgbGEgZm9ybWEgZW4gbGEgcXVlIGZ1ZXJvbiBndWFyZGFuZG8gbGEgaW5mb3JtYWNpw7NuIHByb3BvcmNpb25hZGEgc2UgZW5jb250cmFyb24gZGlmZXJlbnRlcyBmb3JtYXRvcyBkZSByZXNwdWVzdGEgZWouKHJlc3B1ZXN0YSwgUmVzcHVlc3RhLCBSRVNQVUVTVEEpLCBwb3IgbG8gcXVlIHNlIHR1dm8gcXVlIGhvbW9sb2dhciBlbiB0b2RhcyBsYXMgY29sdW1uYXMgcGFyYSBxdWUgYWwgbW9tZW50byBkZSBjb252ZXJ0aXIgbGFzIHZhcmlhYmxlcyBhIGZhY3RvcmVzIG1hbmVqZW4gbG9zIG1pc21vIG5pdmVsZXMsIHNlIGRlY2lkacOzIGNvbnZlcnRpciB0b2RvIGEgbWF5w7pzY3VsYXMuICANCg0KIyMjIyBJZGVudGlmaWNhY2nDs24gTkHCtHMgDQpgYGB7cn0NCiMgSGFjZXIgdW4gc3Vic2V0IGRlIHZhcmlhYmxlcyBpZ3VhbGVzDQpSSF9CIDwtIHN1YnNldChSSF9CLCBzZWxlY3QgPSAtYyhCYW5jbywgQ2F1c2FfQmFqYSwgT2JzZXJ2YWNpb25lc19iYWphKSkNCk5BZGV0ZWN0ZXI9IGRhdGEuZnJhbWUoY29sU3Vtcyhpcy5uYShSSF9CKSkpDQpjb2xuYW1lcyhOQWRldGVjdGVyKSA8LSBjKCJOQSdzIikNCk5BZGV0ZWN0ZXIgJT4lDQogIGtibCgpICU+JQ0KICBhZGRfaGVhZGVyX2Fib3ZlKGMoIkNhbnRpZGFkIGRlIE5BwrRzIGVuIFJIIEJhamFzIiA9IDIpKSU+JQ0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQo+ICoqwr9OdWxvcyBlbiBDUD8qKiAgDQpTZSBwdWVkZSBvYnNlcnZhciBxdWUgc2UgY3VlbnRhbiBjb24gNSB2YWxvcmVzIG51bG9zIGVuIGVsIGPDs2RpZ28gcG9zdGFsLCBlc3RvcyB2YWxvcmVzIG5vIGZ1ZXJvbiB0cmF0YWRvcyBkZWJpZG8gYSBsbyBwb2NvIHByb2JsYWJsZSBxdWUgc2VhIGltcGxlbWVudGFkbyB1biBhbmFsaXNpcyBlc3BhY2lhbCBkb25kZSBmdWVyYW4gbmVjZXNhcmlvcywgZGUgc2VybG9zIHNlIHRyYXRhcsOtYSBjb24gbGEgbW9kYSBkZXBlbmRpZW5kbyBkZSBzdSBtdW5pY2lwaW8uDQoNCiMjIyMgSG9tb2xvZ2FyIGx1Z2FyZXMgZGUgbmFjaW1pZW50bw0KYGBge3J9DQojIExVR0FSIERFIE5BQ0lNSUVOVE8NClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKk5VRVZPIExFT04uKicsICdOVUVWTyBMRU9OJywgUkhfQiRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQiRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qU0FOIE5JQ09MQVMgREUgTE9TIEdBUlpBKicsICdOVUVWTyBMRU9OJywgUkhfQiRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQiRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qTi5MLionLCAnTlVFVk8gTEVPTicsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKk1PTlRFUlJFWSonLCAnTlVFVk8gTEVPTicsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKk1UWSonLCAnTlVFVk8gTEVPTicsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKlRBTUFVTElQQVMuKicsICdUQU1BVUxJUEFTJywgUkhfQiRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQiRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qQ09BSFVJTEEuKicsICdDT0FIVUlMQScsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKlZFUkFDUlVaKicsICdWRVJBQ1JVWicsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKlNBTiBMVUlTIFBPVE9TSSonLCAnU0FOIExVSVMgUE9UT1NJJywgUkhfQiRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQiRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qQ0hJQVBBUyonLCAnQ0hJQVBBUycsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKlpBQ0FURUNBUyonLCAnWkFDQVRFQ0FTJywgUkhfQiRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQiRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qT0FYQUNBKicsICdPQVhBQ0EnLCBSSF9CJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9CJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipISURBTEdPKicsICdISURBTEdPJywgUkhfQiRMdWdhcl9OYWNpbWllbnRvKQ0KUkhfQiRMdWdhcl9OYWNpbWllbnRvIDwtIGdzdWIoJy4qRFVSQU5HTyonLCAnRFVSQU5HTycsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKkdVQU5BSlVBVE8qJywgJ0dVQU5BSlVBVE8nLCBSSF9CJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9CJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipQVUVCTEEqJywgJ1BVRUJMQScsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKlRBQkFTQ08qJywgJ1RBQkFTQ08nLCBSSF9CJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9CJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipDSElIVUFIVUEqJywgJ0NISUhVQUhVQScsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKk1FWElDTyonLCAnQ0RNWCcsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKkRJU1RSSVRPIEZFREVSQUwqJywgJ0NETVgnLCBSSF9CJEx1Z2FyX05hY2ltaWVudG8pDQpSSF9CJEx1Z2FyX05hY2ltaWVudG8gPC0gZ3N1YignLipRVUlOVEFOQSBST08qJywgJ1FVSU5UQU5BIFJPTycsIFJIX0IkTHVnYXJfTmFjaW1pZW50bykNClJIX0IkTHVnYXJfTmFjaW1pZW50byA8LSBnc3ViKCcuKkpBTElTQ08qJywgJ0pBTElTQ08nLCBSSF9CJEx1Z2FyX05hY2ltaWVudG8pDQoNCiNSSF9CJEx1Z2FyX05hY2ltaWVudG9bUkhfQiRMdWdhcl9OYWNpbWllbnRvID09ICIiXSA9IFJIX0IkRXN0YWRvW1JIX0IkTHVnYXJfTmFjaW1pZW50byA9PSAiIl0NCg0KIyBFU1RBRE8NClJIX0IkRXN0YWRvW3doaWNoKFJIX0IkRXN0YWRvID09ICdOVUVWTyBMRcOTTicpXSA8LSAnTlVFVk8gTEVPTicNCmBgYA0KKkVuIGVzdGUgdHJhdGFtaWVudG8gc2UgcmVwaXRpw7MgbG8gcmVhbGl6YWRvIGVuIGxhIGJhc2UgZGUgKioqQWN0aXZvcyoqKi4qICANCg0KIyMjIyBWYXJpYWJsZXMgZGUgdGllbXBvIHkgZWRhZA0KYGBge3J9DQpkYXRvc19maWx0cmFkb3MgPC0gUkhfQlshaXMubmEoUkhfQiRGZWNoYV9BbHRhKSAmICFpcy5uYShSSF9CJEZlY2hhX0JhamEpLF0NCg0KIyBDYWxjdWxhciBsYSBkaWZlcmVuY2lhIGVuIGTDrWFzIHkgYWxtYWNlbmFybGEgZW4gdW5hIG51ZXZhIGNvbHVtbmENCmRhdG9zX2ZpbHRyYWRvcyRkaWZlcmVuY2lhX2RpYXMgPC0gYXMubnVtZXJpYyhkaWZmdGltZShkYXRvc19maWx0cmFkb3MkRmVjaGFfQmFqYSwgZGF0b3NfZmlsdHJhZG9zJEZlY2hhX0FsdGEsIHVuaXRzID0gImRheXMiKSkNCg0KbWVkaWFuYXNfZGlhcyA9IHJvdW5kKG1lZGlhbihkYXRvc19maWx0cmFkb3MkZGlmZXJlbmNpYV9kaWFzKSkNClJIX0IkRmVjaGFfQmFqYVtpcy5uYShSSF9CJEZlY2hhX0JhamEpXSA8LSBSSF9CJEZlY2hhX0FsdGFbaXMubmEoUkhfQiRGZWNoYV9CYWphKV0gKyBtZWRpYW5hc19kaWFzDQoNCmBgYA0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KUkhfQiRkaWFzX3RyYWJhamFkb3MgPC0gdHJ1bmMoYXMubnVtZXJpYyhkaWZmdGltZShSSF9CJEZlY2hhX0JhamEsIFJIX0IkRmVjaGFfQWx0YSwgdW5pdHMgPSAiZGF5cyIpKSkNClJIX0IkZGlmZXJlbmNpYXNfbWVzZXMgPC0gcm91bmQoYXMubnVtZXJpYyhSSF9CJGRpYXNfdHJhYmFqYWRvcy8zMCkpDQpSSF9CJEVkYWQgPSB0cnVuYyhhcy5udW1lcmljKGRpZmZ0aW1lKFJIX0IkRmVjaGFfQmFqYSwgUkhfQiRGZWNoYV9OYWNpbWllbnRvLCB1bml0cyA9ICJkYXlzIikpIC8gMzY1LjI1KQ0KYGBgDQoNCj4gKipUcmF0YW1pZW50byBkZSBudWxvcyBlbiBmZWNoYXMqKiAgDQpQYXJhIGVsIHRyYXRhbWllbnRvIGRlIGxvcyBudWxvcyBlbiBsYXMgZmVjaGFzIGRlIGJhamFzIHNlIGRlY2lkacOzIHBvciBpbXB1dGFyIHZhbG9yZXMgZGViaWRvIGEgbGEgaW1wb3J0YW5jaWEgZGUgbGEgdmFyaWFibGUgeSBsYSBjYW50aWRhZCBkZSBjb3NhcyBxdWUgbm9zIHBlcm1pdMOtYSBoYWNlciB0ZW5pZW5kb2xhIGNvbXBsZXRhLCBlcyBwb3IgZXNvIHF1ZSBzZSBzYWNvIGxhIG1lZGlhbmEgZGUgZGlhcyB0cmFiYWphZG9zIGNvbnRhbmRvIHVuaWNhbWVudGUgbG9zIHJlZ2lzdHJvcyBjb24gYW1iYXMgZmVjaGFzIChBbHRhIHkgQmFqYSkgeSBzZSB2YWxvciBkZSBkw61hcyBmdWUgc3VtYWRvIGEgbGEgZmVjaGEgZGUgYWx0YSBkZSBxdWUgYXF1ZWxsb3MgY29uIGJhamEgZmFsdGFudGUuIA0KDQojIyMjIE1hbmVqbyBkZSBOQcK0cyANCmBgYHtyfQ0KIyBOQcK0cyBlbiB2YXJpYWJsZSBkZSBHZW5lcm8NClJIX0IkUHVlc3RvIDwtIGlmZWxzZShpcy5uYShSSF9CJFB1ZXN0byksICJTRVJWSUNJTyBBTCBDTElFTlRFIiwgUkhfQiRQdWVzdG8gKQ0KYGBgDQoNCiMjIyMgVGlwbyB5IEFjdGl2bw0KYGBge3J9DQpSSF9CJFRpcG8gPC0gaWZlbHNlKFJIX0IkZGlmZXJlbmNpYXNfbWVzZXMgPD0gMiwgIjAiLCAiMSIpDQpSSF9CJFRpcG8gPSBhcy5mYWN0b3IoUkhfQiRUaXBvKQ0KUkhfQiRBY3Rpdm8gPSAwDQpgYGANCg0KIyMjIyBIb21vbG9nYXIgbml2ZWxlcyAodmFyaW9zKQ0KYGBge3J9DQojIFByZXBhcmFyIGZvcm1hdG8gZGUgZmVjaGFzDQpSSF9CJEZlY2hhX05hY2ltaWVudG8gPC0gYXMuRGF0ZShSSF9CJEZlY2hhX05hY2ltaWVudG8sIGZvcm1hdCA9ICIlZC0lbS0lWSIpDQoNCiMgSWd1YWxhciBlbCBmb3JtYXRvIGRlICJFc3RhZG9fQ2l2aWwiIGVuIGFtYmFzIGJhc2VzIA0KUkhfQiRFc3RhZG9fQ2l2aWxbUkhfQiRFc3RhZG9fQ2l2aWwgPT0gIlNPTFRFUkEiXSA8LSAiU09MVEVSSUEiDQpSSF9CJEVzdGFkb19DaXZpbFtSSF9CJEVzdGFkb19DaXZpbCA9PSAiQ0FTQURBIl0gPC0gIk1BVFJJTU9OSU8iDQpSSF9CJEVzdGFkb19DaXZpbFtSSF9CJEVzdGFkb19DaXZpbCA9PSAiRElWT1JDSUFEQSJdIDwtICJESVZPUkNJTyINCg0KbmFtZXMoUkhfQilbbmFtZXMoUkhfQikgPT0gIkfDqW5lcm8iXSA8LSAiR2VuZXJvIiAjIFJlbm9tYnJhciBsYSBjb2x1bW5hIGRlICJHw6luZXJvIiBwYXJhIHF1ZSBjdWFkcmVuIGFtYmFzIA0KYGBgDQoNCiMjIFVuaWZpY2FjacOzbg0KYGBge3J9DQojIFVuaXIgYmFzZXMgZGUgZGF0b3MNClJIIDwtIHJiaW5kKFJIX0IsIFJIX0EpDQoNCiMgRGVmaW5pciBsb3MgZm9ybWF0b3MgYSB1dGlsaWF6ciBwb3IgbGEgdmFyaWFibGVzDQpSSCRHZW5lcm8gPSBhcy5mYWN0b3IoUkgkR2VuZXJvKQ0KUkgkUHVlc3RvID0gYXMuZmFjdG9yKFJIJFB1ZXN0bykNClJIJEx1Z2FyX05hY2ltaWVudG8gPSBhcy5mYWN0b3IoUkgkTHVnYXJfTmFjaW1pZW50bykNClJIJE11bmljaXBpbyA9IGFzLmZhY3RvcihSSCRNdW5pY2lwaW8pDQpSSCRFc3RhZG8gPSBhcy5mYWN0b3IoUkgkRXN0YWRvKQ0KUkgkRXN0YWRvX0NpdmlsID0gYXMuZmFjdG9yKFJIJEVzdGFkb19DaXZpbCkNClJIJENQID0gYXMuY2hhcmFjdGVyKFJIJENQKQ0KUkgkU0QgPSBhcy5udW1lcmljKFJIJFNEKQ0KUkgkVGlwbyA9IGFzLmZhY3RvcihSSCRUaXBvKQ0KUkgkQWN0aXZvID0gYXMuZmFjdG9yKFJIJEFjdGl2bykNCg0KIyBPYnRlbmVyIHVuYSB2aXN1YWxpemFjacOzbiBkZSBsYXMgcHJpbWVyYXMgNSBmaWxhcw0KaGVhZChSSCkNCmBgYA0KDQojIyMgRXh0cmFjY8Otb24gZGUgYmFzZQ0KYGBge3J9DQojd3JpdGUueGxzeChSSCwgIkJBU0VfUkhfRk9STV9QRVJTT05BLnhsc3giKQ0KI3dyaXRlX3hsc3godmVudGFzMSwgInZlbnRhczFfbGltcGlvLnhsc3giKQ0KI3dyaXRlX3hsc3godmVudGFzMiwgInZlbnRhczJfbGltcGlvLnhsc3giKQ0KYGBgDQoNCg0KDQoNCg==