Líbrerías

library(data.table)
library(dplyr)
library(plyr)
library(ggplot2)
library(naniar)
library(Hmisc)         
library(psych)
library(tidyverse)
library(janitor)
library(knitr)
library(pollster)
library(epiDisplay)
library(descr)
library(tidyr)
library(textclean)
library(lubridate)

Importar bases de datos

setwd("C:\\Users\\javaw\\OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey\\7mo Semestre\\Reto")
merma<-read.csv("FORM - Merma.csv")
scrap<-read.csv("FORM - Scrap.csv")
carton<-read.csv("FORM - Produccion Carton Completa.csv")
perf<-read.csv("Del Perf.csv")
plan<-read.csv("DP_1.csv")
bajas<-read.csv("RH.csv")
colaboradores<-read.csv("RH2.csv")

Limpieza, transformación y organización de bases de datos

Para crear una base de datos final para cada una de las áreas de la empresa FORM hay que considerar lo siguiente:

1.- Cambiar nombres de variables.
2.- Mantener variables relevantes para el análisis.
3.- Hay que limpiar las bases de datos.(Na’s, tipo de variables, errores ortograficos, entre otros).Además de homogeneizar variables.

Cambiar nombres de variables

Merma

merma<-clean_names(merma)
merma<-dplyr::rename(merma, fecha=i_fecha)
colnames(merma)
## [1] "fecha" "mes"   "kilos"

Scrap

scrap<-clean_names(scrap)
scrap<-scrap %>% dplyr::rename(referencia=i_referencia,
                               ubi_origen=ubicaci_a3n_de_origen,
                               ubi_desecho=ubicaci_a3n_de_desecho)
colnames(scrap)
## [1] "referencia"       "fecha"            "producto"         "cantidad"        
## [5] "unidad_de_medida" "ubi_origen"       "ubi_desecho"      "estado"

Producción de carton

carton<-clean_names(carton)
carton<-carton %>% dplyr::rename(lam_procesadas=laminas_procesadas,
                                 fin_sep_up=fin_inicio_de_sep_up,
                                 inicio_proceso=inicio_de_proceso,
                                 fin_proceso=fin_de_proceso,
                                 fecha=i_fecha)
colnames(carton)
##  [1] "fecha"             "cliente"           "id_form"          
##  [4] "producto"          "piezas_prog"       "tmo_min"          
##  [7] "hr_fin"            "estacion_arranque" "lam_procesadas"   
## [10] "inicio_sep_up"     "fin_sep_up"        "inicio_proceso"   
## [13] "fin_proceso"       "tiempo_calidad"    "tiempo_materiales"
## [16] "x"

Delivery Performance

perf<-clean_names(perf)
colnames(perf)
## [1] "cliente"        "vueltas"        "fecha"          "plan_arrival"  
## [5] "real_arrival"   "real_departure" "diference"

Delivery Plan

plan<-clean_names(plan)
plan<-plan %>% dplyr::rename(cliente=i_cliente_planta,
                                 jun_21=junio,
                                 jul_21=julio,
                                 ago_21=agosto,
                                 sep_21=septiembre,
                                 oct_21=octubre,
                                 nov_21=noviembre,
                                 dic_21=diciembre,
                                 oct_22=octubre_22)
colnames(plan)
##  [1] "cliente"     "proyecto"    "id_odoo"     "item"        "jun_21"     
##  [6] "jul_21"      "ago_21"      "sep_21"      "oct_21"      "nov_21"     
## [11] "dic_21"      "ene_22"      "feb_22"      "mar_22"      "abr_22"     
## [16] "may_22"      "jun_22"      "jul_22"      "ago_22"      "sep_22"     
## [21] "oct_22"      "nov_22"      "dic_22"      "ene_23"      "feb_23"     
## [26] "mar_23"      "total_meses"

Bajas

bajas<-clean_names(bajas)
bajas<-bajas %>% dplyr::rename(apellidos=i_apellidos,
                                 fecha_nac=fecha_de_nacimiento,
                                 fecha_alta=fecha_de_alta,
                                 mot_baja=motivo_de_baja,
                                 no_ss=no_seguro_social,
                                 sal_diario_imss=salario_diario_imss,
                                 credito_infonavit=na_credito_infonavit,
                                 lugar_nac=lugar_de_nacimiento,
                                 num_interno=numero_interno,
                                 CP=codigo_postal,
                                 permanencia=x)
colnames(bajas)
##  [1] "apellidos"             "nombre"                "fecha_nac"            
##  [4] "genero"                "rfc"                   "fecha_alta"           
##  [7] "mot_baja"              "permanencia"           "baja"                 
## [10] "puesto"                "departamento"          "no_ss"                
## [13] "sal_diario_imss"       "factor_cred_infonavit" "credito_infonavit"    
## [16] "lugar_nac"             "curp"                  "calle"                
## [19] "num_interno"           "colonia"               "CP"                   
## [22] "municipio"             "estado"                "estado_civil"         
## [25] "tarjeta_cuenta"

Colaboradores

colaboradores<-clean_names(colaboradores)
colaboradores<-colaboradores %>% dplyr::rename(no_empleado=i_no_de_empleado,
                                 fecha_nac=fecha_de_nacimiento,
                                 fecha_alta=fecha_de_alta,
                                 "4to_mes"=x4to_mes,
                                 no_ss=no_seguro_social,
                                 sal_diario_imss=salario_diario_imss,
                                 credito_infonavit=na_credito_infonavit,
                                 lugar_nac=lugar_de_nacimiento,
                                 num_interno=numero_interno,
                                 CP=codigo_postal)
colnames(bajas)
##  [1] "apellidos"             "nombre"                "fecha_nac"            
##  [4] "genero"                "rfc"                   "fecha_alta"           
##  [7] "mot_baja"              "permanencia"           "baja"                 
## [10] "puesto"                "departamento"          "no_ss"                
## [13] "sal_diario_imss"       "factor_cred_infonavit" "credito_infonavit"    
## [16] "lugar_nac"             "curp"                  "calle"                
## [19] "num_interno"           "colonia"               "CP"                   
## [22] "municipio"             "estado"                "estado_civil"         
## [25] "tarjeta_cuenta"

Mantener variables relevantes para el análisis.

Merma

En el caso de la base de datos de merma hemos optado por mantener las tres variables, puesto que consideramos que todas son relevantes para el análisis que vamos a realizar.

Scrap

Para la base de datos de scrap hemos optado por eliminar las variables de unidad de medida, estado y ubicación de desecho, ya que todos los registros tienen el mismo valor en estas variables, es decir todos tienen la misma unidad, la misma ubicación y el mismo estado.

scrap<-dplyr::select(scrap,-c(ubi_desecho, estado, unidad_de_medida))
colnames(scrap)
## [1] "referencia" "fecha"      "producto"   "cantidad"   "ubi_origen"

Producción de cartón

Para producción de cartón hemos optado por eliminar las columnas de id_form, producto, tiempo_materiales y estación de arranque, ya que consideramos que con el resto de columnas podemos realizar otros calculos, principalmente hacer mediciones de tiempo.

carton<-dplyr::select(carton,-c(id_form, producto, tiempo_materiales, x, estacion_arranque))
colnames(carton)
##  [1] "fecha"          "cliente"        "piezas_prog"    "tmo_min"       
##  [5] "hr_fin"         "lam_procesadas" "inicio_sep_up"  "fin_sep_up"    
##  [9] "inicio_proceso" "fin_proceso"    "tiempo_calidad"

Delivery Performance

Para delivery performance hemos optado por mantener todas las columnas.

Delivery Plan

Para delivery plan por fines de estructura y análisis hemos optado por transformar la base de datos y las columnas de cada mes convertirlas en datos de una sola columna denominada como Mes y los valores pasarlos a otra columna llamada Unidades.

plan<-pivot_longer(plan, cols=5:26, names_to = "mes", values_to = "unidades")

Ya que contamos con la base de datos transformada hemos optado por eliminar la columna de total meses ya que podemos calcular el total de todos los meses por productos posteriormente, y la de id_odoo.

plan<-dplyr::select(plan,-c(total_meses,id_odoo))

Bajas

Para bajas hemos optado por hacer una transformación a la base de datos y unir los apellidos con el nombre, además de eliminar las columnas de RFC, número de seguro social, factor de crédito infonavit, crédito infonavit, CURP, lugar de nacimiento, calle, numero interno, y tarjeta de cuenta.

bajas<-mutate(bajas,nombre_completo = paste(bajas$nombre, bajas$apellidos, sep=" "))
bajas<-dplyr::select(bajas,-c(rfc, no_ss,factor_cred_infonavit,credito_infonavit,curp,tarjeta_cuenta,apellidos,nombre,lugar_nac,calle,num_interno))
bajas<-bajas[,c(15,1,2,3,4,5,6,7,8,9,10,11,12,13,14)]
colnames(bajas)
##  [1] "nombre_completo" "fecha_nac"       "genero"          "fecha_alta"     
##  [5] "mot_baja"        "permanencia"     "baja"            "puesto"         
##  [9] "departamento"    "sal_diario_imss" "colonia"         "CP"             
## [13] "municipio"       "estado"          "estado_civil"

Colaboradores

Para colaboradotes también optamos por unir apellidos con el nombre, además de eliminar las columnas de RFC, número de seguro social, factor de crédito infonavit, crédito infonavit, CURP, lugar de nacimiento, calle, numero interno, y tarjeta de cuenta.

colaboradores<-mutate(colaboradores,nombre_completo = paste(colaboradores$nombre, colaboradores$apellidos, sep=" "))
colaboradores<-dplyr::select(colaboradores,-c(rfc, no_ss,factor_cred_infonavit,credito_infonavit,curp,tarjeta_cuenta,apellidos,nombre,baja,calle,num_interno,lugar_nac,"4to_mes",primer_mes,no_empleado))
colaboradores<-colaboradores[,c(12,1,2,3,4,5,6,7,8,9,10,11)]

Limpieza

El siguiente paso a realizar es la limpieza de las bases de datos (Na’s, tipo de variables, errores ortograficos, entre otros).

Merma

Para merma eliminaremos las filas que contienen el total de los meses.

merma<-merma[- grep("Total", merma$mes),]

Así mismo, hay que corroborar que las columnas estén en el formato correcto.

str(merma)
## 'data.frame':    50 obs. of  3 variables:
##  $ fecha: chr  "11/01/2022" "11/01/2022" "22/01/2022" "22/01/2022" ...
##  $ mes  : chr  "ENERO" "ENERO" "ENERO" "ENERO" ...
##  $ kilos: chr  "5080" "3810" "2990" "2680" ...

Al observar el tipo de variable, debemos de cambiar fecha a formato de fecha y kilos como entero.

merma$kilos<-as.integer(merma$kilos)
merma$fecha<-as.POSIXct(merma$fecha, format ="%d/%m/%Y")
str(merma)
## 'data.frame':    50 obs. of  3 variables:
##  $ fecha: POSIXct, format: "2022-01-11" "2022-01-11" ...
##  $ mes  : chr  "ENERO" "ENERO" "ENERO" "ENERO" ...
##  $ kilos: int  5080 3810 2990 2680 3650 4380 3870 3590 3410 3930 ...

Scrap

Para scrap eliminamos la primer fila que dice agosto.

scrap<-scrap[- grep("agosto", scrap$referencia),]
head(scrap,5)
##   referencia      fecha
## 2   SP/08731 31/08/2022
## 3   SP/08730 31/08/2022
## 4   SP/08729 31/08/2022
## 5   SP/08728 31/08/2022
## 6   SP/08727 31/08/2022
##                                                                      producto
## 2          [BACKFRAME 60% CUELLO ARMADO] 18805. 60% Backframe. Cuello Armado.
## 3                                      [N61506747 CAJA] N61506747. Kit. Caja.
## 4                            [N61506729 SEPARADOR] N61506729. Kit. Separador.
## 5      [341332 DIVISOR - U611 & U625] 341332. U611. U625. Divisor Troquelado.
## 6 [DIVISOR ZIGZAG VW CHATTANOOGA] Chattanooga. St1 y St3. Zig Zag Troquelado.
##   cantidad                ubi_origen
## 2        2 SAB/Calidad/Entrega de PT
## 3        1 SAB/Calidad/Entrega de PT
## 4        1 SAB/Calidad/Entrega de PT
## 5       31        SAB/Pre-Production
## 6        1        SAB/Pre-Production

Así mismo, hay que corroborar que las columnas estén en el formato correcto.

str(scrap)
## 'data.frame':    250 obs. of  5 variables:
##  $ referencia: chr  "SP/08731" "SP/08730" "SP/08729" "SP/08728" ...
##  $ fecha     : chr  "31/08/2022" "31/08/2022" "31/08/2022" "31/08/2022" ...
##  $ producto  : chr  "[BACKFRAME 60% CUELLO ARMADO] 18805. 60% Backframe. Cuello Armado." "[N61506747 CAJA] N61506747. Kit. Caja." "[N61506729 SEPARADOR] N61506729. Kit. Separador." "[341332 DIVISOR - U611 & U625] 341332. U611. U625. Divisor Troquelado." ...
##  $ cantidad  : num  2 1 1 31 1 1 1 9 2 1 ...
##  $ ubi_origen: chr  "SAB/Calidad/Entrega de PT" "SAB/Calidad/Entrega de PT" "SAB/Calidad/Entrega de PT" "SAB/Pre-Production" ...

Al observar el tipo de variable, debemos de cambiar cantidad a entero y fecha con formato de fecha.

scrap$cantidad<-as.integer(scrap$cantidad)
scrap$fecha<-as.POSIXct(scrap$fecha, format ="%d/%m/%Y")
str(scrap)
## 'data.frame':    250 obs. of  5 variables:
##  $ referencia: chr  "SP/08731" "SP/08730" "SP/08729" "SP/08728" ...
##  $ fecha     : POSIXct, format: "2022-08-31" "2022-08-31" ...
##  $ producto  : chr  "[BACKFRAME 60% CUELLO ARMADO] 18805. 60% Backframe. Cuello Armado." "[N61506747 CAJA] N61506747. Kit. Caja." "[N61506729 SEPARADOR] N61506729. Kit. Separador." "[341332 DIVISOR - U611 & U625] 341332. U611. U625. Divisor Troquelado." ...
##  $ cantidad  : int  2 1 1 31 1 1 1 9 2 1 ...
##  $ ubi_origen: chr  "SAB/Calidad/Entrega de PT" "SAB/Calidad/Entrega de PT" "SAB/Calidad/Entrega de PT" "SAB/Pre-Production" ...

Producción de cartón

Primero hay que corroborar que las columnas estén en el formato correcto.

str(carton)
## 'data.frame':    5300 obs. of  11 variables:
##  $ fecha         : chr  "15/07/22" "15/07/22" "15/07/22" "15/07/22" ...
##  $ cliente       : chr  "STABILUS 1" "STABILUS 1" "STABILUS 1" "STABILUS 1" ...
##  $ piezas_prog   : chr  "200" "100" "216" "100" ...
##  $ tmo_min       : chr  "20" "15" "20" "10" ...
##  $ hr_fin        : chr  "9:20" "9:35" "9:55" "10:05" ...
##  $ lam_procesadas: chr  "402" "134" "110" "100" ...
##  $ inicio_sep_up : chr  "9:05" "10:05" "9:40" "11.20" ...
##  $ fin_sep_up    : chr  "9.10" "10:16" "9:43" "11:26" ...
##  $ inicio_proceso: chr  "9:12" "10.17" "9:45" "11:30" ...
##  $ fin_proceso   : chr  "10:04" "11:05" "9.57" "11:49" ...
##  $ tiempo_calidad: chr  "1" "1" "1" "1" ...

Al observar el tipo de variable, debemos de cambiar la fecha con formato de fecha, piezas programadas y laminas procesadas a entero, y las columnas que son de tiempo al formato de hora también.

carton$piezas_prog <-as.integer(carton$piezas_prog)
carton$tmo_min <-as.integer(carton$tmo_min)

carton$hr_fin<-as.POSIXct(carton$hr_fin,format="%H:%M")
carton$hr_fin <- as.ITime(carton$hr_fin)

carton$inicio_sep_up<-as.POSIXct(carton$inicio_sep_up,format="%H:%M")
carton$inicio_sep_up <- as.ITime(carton$inicio_sep_up)

carton$fin_sep_up<-as.POSIXct(carton$fin_sep_up,format="%H:%M")
carton$fin_sep_up <- as.ITime(carton$fin_sep_up)

carton$inicio_proceso<-as.POSIXct(carton$inicio_proceso,format="%H:%M")
carton$inicio_proceso <- as.ITime(carton$inicio_proceso)

carton$fin_proceso<-as.POSIXct(carton$fin_proceso,format="%H:%M")
carton$fin_proceso <- as.ITime(carton$fin_proceso)

str(carton)
## 'data.frame':    5300 obs. of  11 variables:
##  $ fecha         : chr  "15/07/22" "15/07/22" "15/07/22" "15/07/22" ...
##  $ cliente       : chr  "STABILUS 1" "STABILUS 1" "STABILUS 1" "STABILUS 1" ...
##  $ piezas_prog   : int  200 100 216 100 20 200 100 12 32 500 ...
##  $ tmo_min       : int  20 15 20 10 10 20 10 10 10 60 ...
##  $ hr_fin        : 'ITime' int  09:20:00 09:35:00 09:55:00 10:05:00 10:15:00 10:35:00 10:45:00 10:55:00 11:05:00 10:00:00 ...
##  $ lam_procesadas: chr  "402" "134" "110" "100" ...
##  $ inicio_sep_up : 'ITime' int  09:05:00 10:05:00 09:40:00 NA 12:00:00 12:32:00 NA NA 02:44:00 09:00:00 ...
##  $ fin_sep_up    : 'ITime' int  NA 10:16:00 09:43:00 11:26:00 12:05:00 12:34:00 02:16:00 02:29:00 02:47:00 09:11:00 ...
##  $ inicio_proceso: 'ITime' int  09:12:00 NA 09:45:00 11:30:00 12:15:00 12:47:00 NA 02:31:00 NA 09:13:00 ...
##  $ fin_proceso   : 'ITime' int  10:04:00 11:05:00 NA 11:49:00 12:31:00 02:00:00 NA 03:00:00 02:12:00 10:59:00 ...
##  $ tiempo_calidad: chr  "1" "1" "1" "1" ...

En esta base de datos hay otros elementos a considerar por esto revisaremos cada columna y los registros que la conforman.

Fecha
Con la fecha podemos observar que no hay NA’s, además que nuestros registros son 59 fechas, que abarcan de mediados de julio hasta mediados de septiembre. Y hay que cambair el formato a formato de fecha.

carton$fecha <- as.Date(carton$fecha, format ="%d/%m/%y")
str(carton$fecha)
##  Date[1:5300], format: "2022-07-15" "2022-07-15" "2022-07-15" "2022-07-15" "2022-07-15" ...
unique(carton$fecha)
##  [1] "2022-07-15" "2022-07-16" "2022-07-18" "2022-07-19" "2022-07-20"
##  [6] "2022-07-21" "2022-07-22" "2022-07-23" "2022-07-25" "2022-07-26"
## [11] "2022-07-27" "2022-07-28" "2022-07-29" "2022-07-30" "2022-08-01"
## [16] "2022-08-02" "2022-08-03" "2022-08-04" "2022-08-05" "2022-08-06"
## [21] "2022-08-08" "2022-08-09" "2022-08-10" "2022-08-11" "2022-08-12"
## [26] "2022-08-13" "2022-08-15" "2022-08-16" "2022-08-17" "2022-08-18"
## [31] "2022-08-19" "2022-08-20" "2022-08-22" "2022-08-23" "2022-08-24"
## [36] "2022-08-25" "2022-08-26" "2022-08-27" "2022-08-29" "2022-08-30"
## [41] "2022-08-31" "2022-09-01" "2022-09-02" "2022-09-03" "2022-09-05"
## [46] "2022-09-06" "2022-09-07" "2022-09-08" "2022-09-09" "2022-09-10"
## [51] "2022-09-12" "2022-09-13" "2022-09-14" "2022-09-15" "2022-09-16"
## [56] "2022-09-17" "2022-09-19" "2022-09-20" "2022-09-21"

Cliente
Con los clientes podemos observar que hay clientes con NA, otros con espacios vacíos, y otros que son el mismo pero con error ortográfico, como es el caso de STABILUS 3 y STABILUS 3. El formato de caracter está correcto por lo que no hay que hacer modificaciones en eso.

unique(carton$cliente)
##  [1] "STABILUS 1"           "YANFENG"              "TRMX"                
##  [4] "MERIDIAN LIGHTWEIGHT" "STABILUS 3"           "STABILUS 3."         
##  [7] "HANON SYSTEMS"        "VARROC"               "VL-017-13939"        
## [10] "DENSO"                ""                     "VL-017-14086"        
## [13] "HELLA"                NA

Por lo tanto, hay que eliminar los registros con clientes con NA y con espacios en blanco, además de cambiar aquellos que tienen de cliente STABILUS 3. a STABILUS 3.

carton <- filter(carton, !is.na(carton$cliente))
carton <- filter(carton,cliente!="")
carton$cliente <- replace(carton$cliente,carton$cliente=="STABILUS 3.","STABILUS 3")
unique(carton$cliente)
##  [1] "STABILUS 1"           "YANFENG"              "TRMX"                
##  [4] "MERIDIAN LIGHTWEIGHT" "STABILUS 3"           "HANON SYSTEMS"       
##  [7] "VARROC"               "VL-017-13939"         "DENSO"               
## [10] "VL-017-14086"         "HELLA"

Inicio de proceso y fin de proceso
Previo a analizar el resto de las columnas primero habremos de eliminar los registros que estén como NA’s en alguna de estas dos columnas, puesto que esos registros no sirven para el análisis más adelante. Así mismo, hay que eliminar los registros que esten como 00:00 también.

carton <- filter(carton, !is.na(carton$inicio_proceso) & !is.na(carton$fin_proceso))
carton <- filter(carton,!inicio_proceso==00:00:00 & !fin_proceso==00:00:00)
sum(is.na(carton$inicio_proceso))
## [1] 0
sum(is.na(carton$fin_proceso))
## [1] 0

Inicio de sep up y fin de sep up
Para estas columnas también eliminaremos los registros con NA o con 00:00 como tiempo.

carton <- filter(carton, !is.na(carton$inicio_sep_up) & !is.na(carton$fin_sep_up))
carton <- filter(carton,!inicio_sep_up==00:00:00 & !fin_sep_up==00:00:00)
sum(is.na(carton$inicio_sep_up))
## [1] 0
sum(is.na(carton$fin_sep_up))
## [1] 0

Piezas Programadas
Hay que eliminar los registros con NA de igual manera. El formato debe ser de entero, por lo que hay que hacer este cambio.

carton <- filter(carton, !is.na(carton$piezas_prog))
carton$piezas_prog <- as.integer(carton$piezas_prog)
sum(is.na(carton$piezas_prog))
## [1] 0

Laminas procesadas
Para laminas procesadas si bien no tiene NA sus registros no son consistentes y uniformes en su formato, algunos son numeros enteros, otros estan divididos por un “/”, otros son alfanúmericos, y otros son 0 o registros vacíos. Eliminaremos aquellos menores a 1, los vaciós y mantendremos el primer número antes del “/”, y los registros alfanúmericos aquellos que solo tienen una letra, eliminaremos la letra, pero aquellos que contengan signos como “=”, o más caracteres serán eliminados. También el formato debe ser de entero por lo que hay que hacer esta modificación.

carton <- carton %>% separate(lam_procesadas, c("lam_procesadas", "eliminar"), sep="/")
carton <- dplyr::select(carton,-eliminar)
carton <- carton[- grep("=", carton$lam_procesadas),]
carton <- carton[- grep("A 4 B4", carton$lam_procesadas),]
carton$lam_procesadas <- str_replace(carton$lam_procesadas, "[aeiouLAM=NbBsS]", "")
carton$lam_procesadas <- as.integer(carton$lam_procesadas)
carton <- filter(carton,carton$lam_procesadas>0)
sum(is.na(carton$lam_procesadas))
## [1] 0
unique(carton$lam_procesadas)
##   [1]   51  402  501  152    3  202  216  407  212  421  454  350  104  204  203
##  [16]    2   24   15   20   31   53   27  405  403  303  102  503   45   63   56
##  [31]   57  404   37   46   78  101    9    6  232  406  302  103  210   76   26
##  [46]   11  601   28   40   61  398  190  400   86   18   74  287   22   10   38
##  [61]   34  135   82   30    8   23   25   97   13  243  148  205  300   67   41
##  [76]  242    5   29   50  592  253  206  379  200   35   21  245  124   17  226
##  [91]  201  121  218  109   12    4   47   70  110   88  323  324   33  184   73
## [106]   49   84  100  228   91  500  310  839   48  255 1262   36   19 1240   32
## [121]   14  234  219  427 1229  502  105   42   39  415  175  412  231  180    7
## [136]  221  126   16  208  145   52   81  602   90  802  134   79   77  439  163
## [151]  789  600  111   58  328  609  347  161  399   64  384  118  378  370  452
## [166]  752  375  171   44  215  391  376  339  252  772  207  139  197  766  390
## [181]  227  773  194  112  209  688  146  358  408  123  286   60  122   62  410
## [196]  130  322  437  229  187  380  199  120  162  354  247  137  240  236  304
## [211]  136  143 1022  519  608  584  298  154  306  211   65  414  505  396  140
## [226]   66  386  174  165  313   43  114  178  356  196  301  377   54  106  164
## [241]  125  401  274  435  235  108  388   80  127  181  244  220  241  151  117
## [256]   98  382  248  617  385  305   71  141  344   93  409  222  465  250   68
## [271]  315  185  416  312    1  107  133  195  198  128

Tmo_min y hr_final
Estas columnas permaneceran igual sin ajustes en específico. Únicamente el formato de tmo_min lo dejaremos en entero.

carton$tmo_min <- as.integer(carton$tmo_min)

Tiempo de calidad
Para tiempo de calidad podemos observar que hay registros que no son enteros, otors que tienen segundos y otros que están vacíos. Los que no son enteros los dejaremos vacíos, los vacíos van a permanecer y los que tienen segundos van a ser redondeados al entero más cercano.

carton$tiempo_calidad <- str_replace(carton$tiempo_calidad, "9:47", "9")
carton <- subset(carton, tiempo_calidad!="|")
carton$tiempo_calidad <- as.integer(carton$tiempo_calidad)
unique(carton$tiempo_calidad)
##  [1]  1  2  3  4  9  0  5 10 22  8 11 17 NA  7 25 21  6

Delivery Performance

Para Delivery Performance hay que verificar que las columnas estén en el formato correcto además de identificar valores como Na, valores atípicos, entre otros.

De igual manera iremos revisando columna por columna.

Cliente
Como podemos observar en cliente, solo son 4 clientes, no hay errores ortográficos y el formato de la columna es el correcto.

unique(perf$cliente)
## [1] "PRINTEL " "MAHLE"    "MAGNA"    "VARROC"
str(perf$cliente)
##  chr [1:2392] "PRINTEL " "MAHLE" "MAHLE" "MAHLE" "MAGNA" "VARROC" "VARROC" ...
sum(is.na(perf$cliente))
## [1] 0

Vueltas
De igual manera en vueltas, solo hay hasta 3 vueltas, y no hay errores ortográficos o valores atípicos.

unique(perf$vueltas)
## [1] 1 2 3
str(perf$vueltas)
##  int [1:2392] 1 1 2 3 1 1 2 3 1 1 ...
sum(is.na(perf$vueltas))
## [1] 0

Fecha
La columna de fecha está como caracter, por lo que hay que hacer el cambio de formato a fecha. Así mismo, tampoco hay valores faltantes.

perf$fecha <-as.Date(perf$fecha, format ="%d/%m/%Y")
str(perf$fecha)
##  Date[1:2392], format: "2021-07-22" "2021-07-22" "2021-07-22" "2021-07-22" "2021-07-22" ...
sum(is.na(perf$fecha))
## [1] 0

Real Arrival y Real Departure
Para estas columnas podemos encontrar que hay valores con 0. Por lo tanto vamos a eliminar los registros que tengan 0 en estas columnas.

unique(perf$real_arrival)
##  [1]  0.00  8.00  9.00 20.00 16.30 16.10 16.00 16.50 18.20 17.00 16.20 17.20
## [13] 16.35 16.05 20.18 16.15 20.05  9.30  9.20  9.15  9.10 15.30 21.00 18.15
## [25] 16.25 16.40 15.36  9.45 15.40  9.06  9.03  9.27 17.35  9.05 15.00 15.10
## [37] 15.20 15.16 15.45  9.35 22.08  9.08 21.05 19.15 20.40 20.15 23.20 11.00
## [49] 23.00 13.00 23.50 14.00 23.15  7.45 13.40 23.30 10.40 22.40 11.37 21.15
## [61]  9.26  6.00  8.05 10.42 20.10  7.30 21.10  8.52 20.30  7.34  7.35  9.12
## [73] 10.05  9.17 19.48 20.20 10.00  8.10 19.20  9.11  8.45  9.50 18.00  9.40
## [85] 18.35 18.40 18.05 18.10 18.50 19.10  9.25 18.30 17.30
unique(perf$real_departure)
##   [1]  0.00  8.55 10.00 21.10 18.10  8.50 10.10 21.25 17.20 21.00 16.50 17.00
##  [13] 21.15  8.51  9.00 21.05 18.50  9.45 17.05 17.15 19.55 16.55  9.55 18.20
##  [25] 10.15 17.25 10.05 18.00 17.40 17.30  9.05 17.45  9.50 17.50  9.40 17.35
##  [37] 20.55 10.30 21.20  9.10 10.45 11.05 22.58 10.25  9.15 21.45  9.35 20.50
##  [49] 17.58 19.40 17.55 18.25 10.40 10.06 18.31 10.27 19.00 18.30 18.35 10.20
##  [61] 19.30 16.45 15.55 16.00 16.10 16.35 21.50 16.40 16.30  9.20  9.25 10.35
##  [73] 22.55 17.38 22.10 20.00 10.50 11.35 23.00 22.30 21.40 24.15 12.00 23.50
##  [85] 18.08 13.45 24.50 18.40 24.35 14.40 11.15  8.05 12.40 22.50 11.00 12.05
##  [97] 24.45 11.55 22.15 10.12 22.00 21.55 21.35 20.45 21.30  9.30  8.40 20.15
## [109]  8.30  9.58  8.34  8.20 20.40  8.35  8.15 11.30 11.20 19.20  6.40 18.45
## [121] 10.55 19.50 20.10 10.24  7.40 19.45 13.10
perf <- filter(perf, perf$real_arrival>0 & perf$real_departure>0)
unique(perf$real_arrival)
##  [1]  8.00  9.00 20.00 16.30 16.10 16.00 16.50 18.20 17.00 16.20 17.20 16.35
## [13] 16.05 20.18 16.15 20.05  9.30  9.20  9.15  9.10 15.30 21.00 18.15 16.25
## [25] 16.40 15.36  9.45 15.40  9.06  9.03  9.27 17.35  9.05 15.00 15.10 15.20
## [37] 15.16 15.45  9.35 22.08  9.08 21.05 19.15 20.40 20.15 23.20 11.00 23.00
## [49] 13.00 23.50 14.00 23.15  7.45 13.40 23.30 10.40 22.40 11.37 21.15  9.26
## [61]  6.00  8.05 10.42 20.10  7.30 21.10  8.52 20.30  7.34  7.35  9.12 10.05
## [73]  9.17 19.48 20.20 10.00  8.10 19.20  9.11  8.45  9.50 18.00  9.40 18.35
## [85] 18.40 18.05 18.10 18.50 19.10  9.25 18.30 17.30
unique(perf$real_departure)
##   [1]  8.55 10.00 21.10 18.10  8.50 10.10 21.25 17.20 21.00 16.50 17.00 21.15
##  [13]  8.51  9.00 21.05 18.50  9.45 17.05 17.15 19.55 16.55  9.55 18.20 10.15
##  [25] 17.25 10.05 18.00 17.40 17.30  9.05 17.45  9.50 17.50  9.40 17.35 20.55
##  [37] 10.30 21.20  9.10 10.45 11.05 22.58 10.25  9.15 21.45  9.35 20.50 17.58
##  [49] 19.40 17.55 18.25 10.40 10.06 18.31 10.27 19.00 18.30 18.35 10.20 19.30
##  [61] 16.45 15.55 16.00 16.10 16.35 21.50 16.40 16.30  9.20  9.25 10.35 22.55
##  [73] 17.38 22.10 20.00 10.50 11.35 23.00 22.30 21.40 24.15 12.00 23.50 18.08
##  [85] 13.45 24.50 18.40 24.35 14.40 11.15  8.05 12.40 22.50 11.00 12.05 24.45
##  [97] 11.55 22.15 10.12 22.00 21.55 21.35 20.45 21.30  9.30  8.40 20.15  8.30
## [109]  9.58  8.34  8.20 20.40  8.35  8.15 11.30 11.20 19.20  6.40 18.45 10.55
## [121] 19.50 20.10 10.24  7.40 19.45 13.10

Al hacerlo, los registros de los clientes de VARROC y MAGNA se eliminaron de la base de datos.

Plan Arrival Para Plan Arrival es posible observar que hay registros con 0, estos serán remplazados con el promedio o la mediana de plan arrival por cliente por vuelta, esto con el propósito que sea lo más preciso posible.

unique(perf$plan_arrival)
## [1]  8  9 20 16  0
sum(is.na(perf$plan_arrival))
## [1] 0

Al observar el valor de skewness, podemos observar que el valor es menor a 1 por lo que su distribución nos sugiere que es mejor remplazar con el promedio. Además podemos observar que la mediana es 0 debido a que muchos valores tienen 0 como plan arrival.

describe(perf$plan_arrival)
##    vars   n  mean   sd median trimmed  mad min max range skew kurtosis   se
## X1    1 800 12.86 5.27      9   12.59 1.48   0  20    20 0.42    -1.64 0.19

Para remplazar 0 con el promedio vamos a calcular el promedio por cliente por vuelta.

aggregate(plan_arrival ~ cliente + vueltas, data = perf, mean)
##    cliente vueltas plan_arrival
## 1    MAHLE       1      8.00000
## 2 PRINTEL        1     16.00000
## 3    MAHLE       2      9.00000
## 4    MAHLE       3     19.91736

Como queremos remplazar el 0 unicamente en el cliente MAHLE y en la vuelta 3 debemos hacer lo siguiente.

perf <- perf %>% mutate(plan_arrival=ifelse(plan_arrival == 0 | cliente == "MAHLE"| vueltas == 3,20,plan_arrival))
unique(perf$plan_arrival)
## [1] 20 16

Difference
Esta columna se supone es la diferencia que hay entre real arrival y real departure, sin embargo, podemos observar que no todos los registros tienen el calculo correcto, e inclusive algunas ni si quiera tienen el calculo por lo que es necesario modificar esta columna.

perf <- mutate(perf, difference=real_departure-real_arrival)
perf <- dplyr::select(perf,-diference)
head(perf,5)
##    cliente vueltas      fecha plan_arrival real_arrival real_departure
## 1    MAHLE       1 2021-07-22           20          8.0           8.55
## 2    MAHLE       2 2021-07-22           20          9.0          10.00
## 3    MAHLE       3 2021-07-22           20         20.0          21.10
## 4 PRINTEL        1 2021-07-25           16         16.3          18.10
## 5    MAHLE       1 2021-07-25           20          8.0           8.50
##   difference
## 1       0.55
## 2       1.00
## 3       1.10
## 4       1.80
## 5       0.50

Delivery Plan

Para Delivery Plan también iremos revisando columna por columna.

Cliente Planta
Para Cliente podemos observar que no hay errores ortográficos por lo que no haremos cambios en esta columna.

unique(plan$cliente)
##  [1] "STB3"            "STB4"            "STB5"            "STB6"           
##  [5] "STB7"            "STB8"            "STB9"            "STB 1"          
##  [9] "YF RAMOS"        "INOAC POLYTEC"   "MERIDIAN"        "YANFENG sm"     
## [13] "YFTO"            "YFCF"            "YF QRO"          "TRMX"           
## [17] "DENSO"           "SEGROVE"         "HANON"           "ANTOLIN TOLUCA" 
## [21] "ANTOLIN ARTEAGA" "HELLA"           "UFI"             "ISRI"           
## [25] "ABC QUERETARO"   "VARROC"

Proyecto
De igual manera para proyecto no haremos cambios a los registros, pues no encontramos errores de inconsistencia.

unique(plan$proyecto)

Item
De igaul manera con item no haremos modificaciones.

unique(plan$item)

Mes
Para la columna de mes no encontramos errores ortográficos por lo que no haremos modificaciones.

unique(plan$mes)
##  [1] "jun_21" "jul_21" "ago_21" "sep_21" "oct_21" "nov_21" "dic_21" "ene_22"
##  [9] "feb_22" "mar_22" "abr_22" "may_22" "jun_22" "jul_22" "ago_22" "sep_22"
## [17] "oct_22" "nov_22" "dic_22" "ene_23" "feb_23" "mar_23"

Unidades
Para unidades optamos por eliminar los registros con 0 unidades, ya que al convertir los meses en una sola columna, todos aquellos meses donde no había producción planeada tienen 0.También corroboramos que el formato de la columna sea el correcto, de entero.

plan <- filter(plan, unidades>0)
str(plan$unidades)
##  int [1:972] 140 530 200 150 230 500 200 900 1000 184 ...

Bajas

Para esta base de datos también iremos revisando columna por columna.

Nombre completo, fecha de nacimiento y fecha de alta
Estas columnas están correctas, solo hay que convertir a formato de fecha la fecha de alta y de nacimiento.

bajas$fecha_nac <- as.Date(bajas$fecha_nac, format ="%d/%m/%Y")
str(bajas$fecha_nac)
##  Date[1:238], format: "1990-06-24" "1986-07-11" "1999-01-29" "2001-06-11" "1993-01-28" ...
bajas$fecha_alta <- as.Date(bajas$fecha_alta, format ="%d/%m/%Y")
str(bajas$fecha_alta)
##  Date[1:238], format: "2020-03-09" "2021-11-09" "2021-11-10" "2021-11-10" "2021-11-18" ...

Género
En la columna de género solo hay un valor vacío, basados en su nombre completo vamos a asumir que su género es masculino. Así mismo, hay un registro con lo que parece ser un RFC en lugar del género, basado en el nombre de esa persona asumiremos que es femenino.

bajas$genero <- replace(bajas$genero,bajas$genero=="","MASCULINO") 
bajas$genero <- replace(bajas$genero,bajas$genero=="CAPJ000926597","FEMENINO")

Código Postal
Para el código postal hay que remplazar los registros que tengan el municipio en lugar del CP por su respectivo CP.

bajas2 <- bajas
bajas2 <- bajas2 %>% mutate(bajas2, CP2=CP)
bajas2 <- bajas2 %>% mutate(bajas2, municipio2=municipio)
bajas2 <- bajas2 %>% mutate(bajas2, estado2=estado)

#Remplazar el municipio por el CP en la columna de CP, tambien remplazar union libre por el CP
bajas2 <-  bajas2 %>% mutate(CP=ifelse(CP == "APODACA" | CP == "JUAREZ" | CP == "MONTERREY" | CP == "GUADALUPE" | CP == "RAMOS ARIZPE"| CP == "SAN PDRO COAH"| CP == "PESQUERIA"| CP == "CADEREYTA"| CP == "SAN NICOLAS DE LOS GARZA"| CP == "SALTILLO"| CP == "ZUAZUA"| CP == "CIENEGA DE FLORES"| CP == "NUEVO LEON", estado , CP))
bajas2 <-  bajas2 %>% mutate(CP=ifelse(CP == "UNION LIBRE", municipio , CP))
unique(bajas2$CP)
##  [1] "66438" "0"     "66646" "66640" "66649" "67254" "67114" "66645" ""     
## [10] "66644" "66360" "25297" "66648" "67256" "64750" "66670" "66600" "66643"
## [19] "67267" "64764" "67110" "66633" "25902" "27970" "66655" "66647" "66612"
## [28] "64220" "66650" "67486" "67130" "99999" "66673" "67117" "66473" "25019"
## [37] "66634" "67258" "66477" "67113" "67265" "25904" "65776" "67250" "66444"
## [46] "67176" "65580" "67262"

Municipio
En municipio hay que remplazar los registros que tienen el estado por su respectivo municipio. Además de estandarizar los registros, por ejemplo, cambiar “SAN NICOLAS DE LOS G” por “SAN NICOLAS DE LOS GARZA”.

#Remplazar el estado por el municipio
bajas2 <-  bajas2 %>% mutate(municipio=ifelse(municipio == "NUEVO LEON" | municipio == "Nuevo León" | municipio == "COAHUILA" | municipio == "Coahuila",CP2 , municipio))
bajas2 <-  bajas2 %>% mutate(municipio=ifelse(municipio == 66444 , colonia , municipio))

#Cambias SAN NICOLAS DE LOS G x SAN NICOLAS DE LOS GARZA
bajas2$municipio <- replace(bajas2$municipio,bajas2$municipio=="SAN NICOLAS DE LOS G","SAN NICOLAS DE LOS GARZA")

unique(bajas2$municipio)
##  [1] "SAN NICOLAS DE LOS GARZA" "PESQUERIA"               
##  [3] "APODACA"                  "JUAREZ"                  
##  [5] "GUADALUPE"                "RAMOS ARIZPE"            
##  [7] "MONTERREY"                "SAN PDRO COAH"           
##  [9] "CADEREYTA"                "SALTILLO"                
## [11] ""                         "ZUAZUA"                  
## [13] "CIENEGA DE FLORES"

Estado
En estado hay que remplazar los registros que tienen CP por el estado correcto. Así mismo, estandarizar los registros, por ejemplo, cambiar “Nuevo León” por “NUEVO LEON”.

#Remplazar el CP por el estado correcto en la columna de estado
bajas2 <-  bajas2 %>% mutate(estado=ifelse(municipio == "SAN NICOLAS DE LOS GARZA" | municipio == "PESQUERIA" | municipio == "APODACA" | municipio == "JUAREZ" | municipio == "GUADALUPE"| municipio == "RAMOS ARIZPE"| municipio == "CADEREYTA"| municipio == "ZUAZUA"| municipio == "CIENEGA DE FLORES"| municipio == "MONTERREY", "NUEVO LEON" , municipio))

bajas2$estado <- replace(bajas2$estado,bajas2$estado=="SAN PDRO COAH","COAHUILA")
bajas2$estado <- replace(bajas2$estado,bajas2$estado=="SALTILLO","COAHUILA")

unique(bajas2$estado)
## [1] "NUEVO LEON" "COAHUILA"   ""

Estado Civil
En estado civil hay que estandarizar los registros, por ejemplo, cambiar “UNIONLIBRE” por “UNION LIBRE”.

bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="UNIONLIBRE","UNION LIBRE")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="Unión libre","UNION LIBRE")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="Soltero","SOLTERO(A)")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="Casado","CASADO(A)")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="CASADA","CASADO(A)")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="SOLTERA","SOLTERO(A)")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="DIVORCIADA","DIVORCIADO(A)")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="CASADO","CASADO(A)")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="DIVORCIADO","DIVORCIADO(A)")
bajas2$estado_civil <- replace(bajas2$estado_civil,bajas2$estado_civil=="SOLTERO","SOLTERO(A)")
unique(bajas2$estado_civil)
## [1] "SOLTERO(A)"    "UNION LIBRE"   "CASADO(A)"     ""             
## [5] "DIVORCIADO(A)"

Puesto Para puesto vamos a unificar aquellos puestos que son similares y que tengan errores ortográficos. Por ejemplo, “AY. GENERAL (MATERIALES)” y “AYUDANTE DE EMBARQUES” pasarlos a “AYUDANTE GENERAL”.

bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="AY.GENERAL (MATERIALES)","AYUDANTE GENERAL")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="AYUDANTE DE EMBARQUES","AYUDANTE GENERAL")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="AY. GENERAL","AYUDANTE GENERAL")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="AYUD.EMBARQUES","AYUDANTE GENERAL")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="AYUDANTE DE SOLDADOR","AYUDANTE GENERAL")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="AYUDANTE DE MTTO","AYUDANTE GENERAL")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="AYUDANTE DE SOLDADOR","AYUDANTE GENERAL")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="AYUDANTE GENERAL DE EMBARQUES","AYUDANTE GENERAL")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="COSTURERA","COSTURERO(A)")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="COSTURERO","COSTURERO(A)")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="INSPECTOR DE CALIDAD","INSPECTOR(A) DE CALIDAD")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="INSPECTORA DE CALIDAD","INSPECTOR(A) DE CALIDAD")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="INSPECTOR CALIDAD","INSPECTOR(A) DE CALIDAD")
bajas2$puesto <- replace(bajas2$puesto,bajas2$puesto=="DISEÑO","DISEÑO")
bajas2$puesto <- toupper(bajas2$puesto)
unique(bajas2$puesto)
##  [1] "DISEÑO"                           "AYUDANTE GENERAL"                
##  [3] "COSTURERO(A)"                     "RESIDENTE YANFENG"               
##  [5] "MONTACARGUISTA"                   "SERVICIO AL CLIENTE"             
##  [7] "ANALISTA DE NOMINAS /AUX DE R.H." "AUXILIAR DE EMBARQUES"           
##  [9] "CHOFER"                           "INSPECTOR(A) DE CALIDAD"         
## [11] "MATERIALISTA"                     "SOLDADOR"                        
## [13] "FACTURACION"                      "JEFE DE SEGURIDAD E HIGIENE"     
## [15] "MARCADORA"                        "CORTADOR"                        
## [17] "LIMPIEZA"                         "RESIDENTE"                       
## [19] "GUARDIA DE SEGURIDAD"             "ENCARGADA DE CALIDAD"            
## [21] ""                                 "PRACTICANTE DE MTTO"

Departamento
Para departamento haremos lo mismo que en puesto.

bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="celdas","CELDAS")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Celdas","CELDAS")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Cedis","CEDIS")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Embarques","EMBARQUES")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Costura","COSTURA")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Paileria","PAILERIA")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Stabilus","STABILUS")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Troquel","TROQUEL")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Mantenimiento FF","MANTENIMIENTO")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="stabilus","STABILUS")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Produccion Cartón MC","PRODUCCION CARTON MC")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Produccion Cartón MDL","PRODUCCION CARTON MDL")
bajas2$departamento <- replace(bajas2$departamento,bajas2$departamento=="Producción Retorn","PRODUCCION RETORNO")
bajas2$departamento <- toupper(bajas2$departamento)
unique(bajas2$departamento)
##  [1] "ADMINISTRATIVO"        "PRODUCCION CARTON MC"  "STABILUS"             
##  [4] "CELDAS"                ""                      "MATERIALES"           
##  [7] "CEDIS"                 "PRODUCCION CARTON MDL" "PRODUCCION RETORNO"   
## [10] "TROQUEL"               "MANTENIMIENTO"         "AY.FLEXO"             
## [13] "COSTURA"               "MARCADORA"             "CAJAS"                
## [16] "LAMINADO"              "CALIDAD"               "EMBARQUES"            
## [19] "PAILERIA"              "EHS"                   "CORTADORAS"

Motivo de Baja
Para motivo de baja haremos algo diferente, al analizar la base de datos nos dimos cuenta que muchos de los empleados que tienen muy pocos días, por ejemplo de 0 días a 10, en lugar de tener como motivo de baja ABANDONO, tiene FALTAS. Para que el motivo de baja sea las faltas, debería de existir un periodo establecido sobre el cual se limiten las faltas. Por ejemplo, cada empleado solo tiene permitidas tanta cantidad de faltas al mes. Por lo tanto, aquellos empleados que tienen menos de 10 días de permanencia, cambiaremos su motivo de baja de FALTAS a ABANDONO.

#Remplazamos FALTA POR BAJAS por ABANDONO
bajas2 <-  bajas2 %>% mutate(mot_baja=ifelse(permanencia < 10, "ABANDONO" , mot_baja))

#Mantenemos los motivos de baja que no eran FALTA POR BAJAS y tenian permanencia menor de 10 días.
bajas2 <-  bajas2 %>% mutate(mot_baja=ifelse(nombre_completo == "JOANA NOHEMI COLUNGA ORTIZ", "RENUNCIA VOLUNTARIA" , mot_baja))
bajas2 <-  bajas2 %>% mutate(mot_baja=ifelse(nombre_completo == "ERNESTO GONZALEZ CASTILLO", "RENUNCIA VOLUNTARIA" , mot_baja))
bajas2 <-  bajas2 %>% mutate(mot_baja=ifelse(nombre_completo == "MOISES LAUREANI JIMENEZ", "RENUNCIA VOLUNTARIA" , mot_baja))
bajas2 <-  bajas2 %>% mutate(mot_baja=ifelse(nombre_completo == "ROCIO OCHOA GONZALEZ", "RENUNCIA VOLUNTARIA" , mot_baja))
bajas2 <-  bajas2 %>% mutate(mot_baja=ifelse(nombre_completo == "MARGARITA RIVAS GONZALEZ", "RENUNCIA VOLUNTARIA" , mot_baja))
bajas2 <-  bajas2 %>% mutate(mot_baja=ifelse(nombre_completo == "GLORIA FACUNDO MONTOYA", "RENUNCIA VOLUNTARIA" , mot_baja))

Salario Diario del IMSS
Para salario diario hay que asegurarnos que este en el formato correcto, además de remplazar los valores faltantes e identificar valores atípicos y definir si hay que remplazarlos.

filter(bajas2,is.na(bajas2$sal_diario_imss))
##                 nombre_completo  fecha_nac    genero fecha_alta
## 1     POLETT ADRIANA LOPEZ RUIZ 1988-08-24  FEMENINO 2022-04-04
## 2 ANGEL GUILLERMO  PALOMO LOPEZ       <NA> MASCULINO       <NA>
##              mot_baja permanencia       baja                      puesto
## 1 RENUNCIA VOLUNTARIA          18 22/04/2022 JEFE DE SEGURIDAD E HIGIENE
## 2                <NA>          NA                                       
##   departamento sal_diario_imss colonia CP municipio estado estado_civil CP2
## 1                           NA                                             
## 2                           NA                                             
##   municipio2 estado2
## 1                   
## 2
describe(bajas2$sal_diario_imss)
##    vars   n   mean    sd median trimmed mad    min max  range  skew kurtosis
## X1    1 236 177.96 23.26 180.68  179.14   0 144.45 500 355.55 11.05   152.94
##      se
## X1 1.51

Como podemos ver hay dos registros faltantes, para el de Ángel remplazaremos con la mediana, ya que su valor de skewness del salario diario es mayor a 1, indicandonos que es mejor usar la mediana que la media.

summary(bajas2$sal_diario_imss)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   144.4   180.7   180.7   178.0   180.7   500.0       2
bajas2 <-  bajas2 %>% mutate(sal_diario_imss=ifelse(nombre_completo == "ANGEL GUILLERMO  PALOMO LOPEZ", 180.7, sal_diario_imss))
bajas2 <-  bajas2 %>% mutate(sal_diario_imss=ifelse(nombre_completo == "POLETT ADRIANA LOPEZ RUIZ", 180.7, sal_diario_imss))
sum(is.na(bajas2$sal_diario_imss))
## [1] 0

Finalmente terminamos de limpiar la base de datos de aquellas columnas que tuvimos que agregar o renombrar.

bajas2 <- dplyr::select(bajas2, -c(CP2,municipio2,estado2))
bajas <- bajas2
colnames(bajas2)
##  [1] "nombre_completo" "fecha_nac"       "genero"          "fecha_alta"     
##  [5] "mot_baja"        "permanencia"     "baja"            "puesto"         
##  [9] "departamento"    "sal_diario_imss" "colonia"         "CP"             
## [13] "municipio"       "estado"          "estado_civil"

Colaboradores

Para colaboradores de igual manera revisaremos columna por columna.

Nombre Completo
A nombre completo no le haremos cambios en la base.

Fecha de nacimiento y fecha de alta
Para fecha de nacimiento y fecha de alta nos aseguramos que el formato este en formato de fecha.

colaboradores$fecha_alta <- as.Date(colaboradores$fecha_alta, format = "%d/%m/%Y")
colaboradores$fecha_nac <- as.Date(colaboradores$fecha_nac, format = "%d/%m/%Y")

Género
Para género identificamos que en la base de datos existen 9 registros totalmente vacíos, por lo que es necesario eliminar estas filas.

unique(colaboradores$genero)
## [1] "MASCULINO" "FEMENINO"
filter(colaboradores, colaboradores$genero=="")
##  [1] nombre_completo fecha_nac       genero          fecha_alta     
##  [5] puesto          departamento    sal_diario_imss colonia        
##  [9] municipio       estado          CP              estado_civil   
## <0 rows> (or 0-length row.names)
colaboradores <- colaboradores[-(114:122),]

Puesto
Para puesto hay que estandarizar los registros, específicamente aquellos que son el mismo puesto pero están escritos diferente.

colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="Supervisor de Máquin","SUPERVISOR(A)")
colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="Supervisor de pegado","SUPERVISOR(A)")
colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="SUPERVISORA","SUPERVISOR(A)")
colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="AY. GENERAL","AYUDANTE GENERAL")
colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="AYUDANTE DE MANTENIMIENTO","AYUDANTE GENERAL")
colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="CHOFER GESTOR","CHOFER")
colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="INSPECTOR DE CALIDAD","INSPECTOR(A) DE CALIDAD")
colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="INSPECTORA DE CALIDAD","INSPECTOR(A) DE CALIDAD")
colaboradores$puesto <- replace(colaboradores$puesto,colaboradores$puesto=="Mantenimiento FF","MANTENIMIENTO")
colaboradores$puesto <- toupper(colaboradores$puesto)
unique(colaboradores$puesto)
##  [1] "SUPERVISOR(A)"                   "EXTERNO"                        
##  [3] "CUSTOMER SERVICE INF"            "COSTURERA"                      
##  [5] "AYUDANTE GENERAL"                "GESTOR"                         
##  [7] "CHOFER"                          "LIDER"                          
##  [9] "MANTENIMIENTO"                   "RESIDENTE"                      
## [11] "LIMPIEZA"                        "INSPECTOR(A) DE CALIDAD"        
## [13] "PINTOR"                          "SOLDADOR"                       
## [15] "RECIBO"                          "ENFERMERA"                      
## [17] "OP. FLEXO-RANURADORA-REFILADORA" "GUARDIA DE SEGURIDAD"           
## [19] "MOZO"                            "OPERADOR SIERRA"                
## [21] "MONTACARGUISTA"

Departamento
Para departamento hay que extandarizar los registros que son el mismo pero escritos diferentes.

unique(colaboradores$departamento)
##  [1] "Produccion Cartón MDL" "Externo"                "Produccion Cartón MC" 
##  [4] "Costura"                "Cedis"                  "Producción Retorn"    
##  [7] "Costura T2"             "Embarques"              "EHS"                   
## [10] "Limpieza"               "Calidad"                "Paileria"              
## [13] "Materiales"             "COSTURA"                ""                      
## [16] "Rotativa"               "Stabilus"               "Troquel"               
## [19] "Celdas"                 "Ay.flexo"               "CEDIS"                 
## [22] "CORTADORAS"
colaboradores$departamento <- replace(colaboradores$departamento,colaboradores$departamento=="Produccion Cartón MDL","PRODUCCIÓN CARTON MDL")
colaboradores$departamento <- replace(colaboradores$departamento,colaboradores$departamento=="Produccion Cartón MC","PRODUCCION CARTON MC")
colaboradores$departamento <- replace(colaboradores$departamento,colaboradores$departamento=="Producción Retorn","PRODUCCION RETORNO")
colaboradores$departamento <- replace(colaboradores$departamento,colaboradores$departamento=="Costura T2","COSTURA")
colaboradores$departamento <- toupper(colaboradores$departamento)

Salario diario del IMSS
Para salario diario del IMSS hay que asegurarnos que el formato esté correcto. Así mismo, hay que corregir los valores atípicos.

options(scipen = 999)
colaboradores$sal_diario_imss <- as.numeric(colaboradores$sal_diario_imss)
unique(colaboradores$sal_diario_imss)
##  [1]        176.72        337.05    4413757.00        260.01        240.75
##  [6]        152.86        175.79        144.45        279.61        151.67
## [11]        208.65        240.71 1516728571.00        151.61        180.68
## [16]        181.68        184.68        185.68
str(colaboradores$sal_diario_imss)
##  num [1:113] 177 177 177 337 4413757 ...

Para corregir los valores atípicos primero hay que identificarlos. Para remplazar su salario, nos basaremos en puestos similares para asignarles uno similar.

filter(colaboradores, colaboradores$sal_diario_imss >  4413750)
##                    nombre_completo  fecha_nac    genero fecha_alta
## 1              YOLANDA LOPEZ RAMOS 1965-09-06  FEMENINO 2014-05-05
## 2 JAIME ERNESTO AGUILERA RODRIGUEZ 1969-02-21 MASCULINO 2020-08-08
##             puesto       departamento sal_diario_imss                colonia
## 1    SUPERVISOR(A)            COSTURA         4413757      RINCON DE GALICIA
## 2 AYUDANTE GENERAL PRODUCCION RETORNO      1516728571 LOS PUERTOS 2° SECTOR
##   municipio      estado    CP estado_civil
## 1   APODACA Nuevo León 66620      Soltero
## 2    JUAREZ Nuevo León 67267       Casado

Podemos observar que el puesto de Jaime Ernesto es Ayudante general y que está en el departamento de Producción Retorno. Al observar registros con el mismo puesto y departamento, podemos observar que tres colaboradores tienen un salario de 151.61, y el salario equivocado es de 1516728571.00, por lo que podríamos asumir ha sido un error de dedo y el salario correcto es 151.61, por lo que optamos por remplazarlo por este valor.

colaboradores$sal_diario_imss[colaboradores$nombre_completo == "JAIME ERNESTO AGUILERA RODRIGUEZ"] <- 151.61

El segundo salario equivocado es de 4413757.00. y de YOLANDA LOPEZ RAMOS. Ahora hay que identificar puestos similares en el mismo departamento.

filter(colaboradores, departamento == "COSTURA")
##                      nombre_completo  fecha_nac    genero fecha_alta
## 1                YOLANDA LOPEZ RAMOS 1965-09-06  FEMENINO 2014-05-05
## 2           ARACELY PERALTA MARTINEZ 1967-07-01  FEMENINO 2015-08-06
## 3          YOLANDA JUDITH LUNA LOPEZ 1985-08-18  FEMENINO 2017-02-20
## 4    MA DEL CARMEN GARCIA CARRIZALES 1978-02-03  FEMENINO 2020-04-07
## 5           ADELAIDA MENDOZA NAVARRO 1979-12-16  FEMENINO 2020-08-26
## 6         BLANCA OLIVIA BARRON RAMOS 1969-06-29  FEMENINO 2020-08-28
## 7 SOLEDAD ENCARNACION CARDENAS OLMOS 1972-01-22  FEMENINO 2021-04-24
## 8        JOSE ALFREDO VAZQUEZ CEPEDA 1963-08-28 MASCULINO 2021-08-12
## 9        MARCIANA NATIVIDAD MARTINEZ 1967-01-04  FEMENINO 2021-12-06
##             puesto departamento sal_diario_imss                    colonia
## 1    SUPERVISOR(A)      COSTURA      4413757.00          RINCON DE GALICIA
## 2        COSTURERA      COSTURA          260.01          ALAMOS DEL PARQUE
## 3        COSTURERA      COSTURA          152.86 C. H. BLAS CHUMACERO C.T.M
## 4        COSTURERA      COSTURA          240.71               PUEBLO NUEVO
## 5        COSTURERA      COSTURA          176.72               LOS CANDILES
## 6        COSTURERA      COSTURA          176.72               PUEBLO NUEVO
## 7 AYUDANTE GENERAL      COSTURA          151.61            PORTAL DE VALLE
## 8        COSTURERA      COSTURA          176.72        LA ALIANZA SECTOR C
## 9        COSTURERA      COSTURA          176.72                    LA JOYA
##              municipio      estado    CP estado_civil
## 1              APODACA Nuevo León 66620      Soltero
## 2              APODACA Nuevo León 66633       Casado
## 3 SAN NICOLAS DE LOS G Nuevo León 66473       Casado
## 4              APODACA Nuevo León 66646  Union Libre
## 5              APODACA Nuevo León 66647       CASADA
## 6              APODACA Nuevo León 66646       Casado
## 7              APODACA  NUEVO LEON 66643       CASADA
## 8            MONTERREY  NUEVO LEON 64103   DIVORCIADO
## 9            GUADALUPE  NUEVO LEON 67160       CASADA

En este caso, es el único registro con ese puesto en el departamento, por lo tanto identificaremos puestos similares pero en diversos departamentos.

filter(colaboradores, puesto == "SUPERVISOR(A)")
##             nombre_completo  fecha_nac    genero fecha_alta        puesto
## 1 NICOLAS MARTINEZ DE LOERA 1955-09-10 MASCULINO 2010-07-01 SUPERVISOR(A)
## 2    MARIANA DE LEON MORENO 1979-05-14  FEMENINO 2011-07-01 SUPERVISOR(A)
## 3     MARIA CAZARES MORALES 1990-05-01  FEMENINO 2013-01-30 SUPERVISOR(A)
## 4       YOLANDA LOPEZ RAMOS 1965-09-06  FEMENINO 2014-05-05 SUPERVISOR(A)
##            departamento sal_diario_imss           colonia            municipio
## 1 PRODUCCIÓN CARTON MDL          176.72    UNIDAD LABORAL SAN NICOLAS DE LOS G
## 2 PRODUCCIÓN CARTON MDL          176.72    SANTA TERESITA              APODACA
## 3  PRODUCCION CARTON MC          337.05      PUEBLO NUEVO              APODACA
## 4               COSTURA      4413757.00 RINCON DE GALICIA              APODACA
##        estado    CP estado_civil
## 1 Nuevo León 66440      Soltero
## 2 Nuevo León 66605      Soltero
## 3 Nuevo León 66649       Casado
## 4 Nuevo León 66620      Soltero

Hemos identificado solo un registro más con el mismo puesto, por lo que optaremos por asignar el mismo salario al registro con el salario elevado.

colaboradores$sal_diario_imss[colaboradores$nombre_completo == "YOLANDA LOPEZ RAMOS"] <- 337.05 
unique(colaboradores$sal_diario_imss)
##  [1] 176.72 337.05 260.01 240.75 152.86 175.79 144.45 279.61 151.67 208.65
## [11] 240.71 151.61 180.68 181.68 184.68 185.68

Municipio
En municipio hay que estandarizar los registros, por ejemplo, cambiar “SAN NICOLAS DE LOS G” por “SAN NICOLAS DE LOS GARZA”.

unique(colaboradores$municipio)
## [1] "SAN NICOLAS DE LOS G" "APODACA"              "SALTILLO"            
## [4] "PESQUERIA"            "MONTERREY"            "GUADALUPE"           
## [7] "JUAREZ"               "RAMOS ARIZPE"         "CAÑADA BLANCA"
colaboradores$municipio <- replace(colaboradores$municipio,colaboradores$municipio=="SAN NICOLAS DE LOS G","SAN NICOLAS DE LOS GARZA")
colaboradores$municipio <- replace(colaboradores$municipio,colaboradores$municipio=="CAÑADA BLANCA","GUADALUPE")

Estado
En estado hay que estandarizar los registros, por ejemplo, cambiar “Nuevo León” por “NUEVO LEON”.

colaboradores$estado <- replace(colaboradores$estado,colaboradores$estado=="Nuevo León","NUEVO LEON")
colaboradores$estado <- replace(colaboradores$estado,colaboradores$estado=="GUADALUPE N.L.","NUEVO LEON")
colaboradores$estado <- toupper(colaboradores$estado)

unique(colaboradores$estado)
## [1] "NUEVO LEON" "COAHUILA"

Estado Civil
En estado civil también hay que estandarizar los registros.

unique(colaboradores$estado_civil)
##  [1] "Soltero"      "Casado"       "Unión libre" "Union Libre"  "CASADA"      
##  [6] "UNION LIBRE"  "CASADO"       "SOLTERA"      "DIVORCIADO"   "SOLTERO"     
## [11] "DIVORCIADA"
colaboradores$estado_civil <- toupper(colaboradores$estado_civil)
colaboradores$estado_civil <- replace(colaboradores$estado_civil,colaboradores$estado_civil=="UNIóN LIBRE","UNION LIBRE")
colaboradores$estado_civil <- replace(colaboradores$estado_civil,colaboradores$estado_civil=="SOLTERO","SOLTERO(A)")
colaboradores$estado_civil <- replace(colaboradores$estado_civil,colaboradores$estado_civil=="CASADO","CASADO(A)")
colaboradores$estado_civil <- replace(colaboradores$estado_civil,colaboradores$estado_civil=="CASADA","CASADO(A)")
colaboradores$estado_civil <- replace(colaboradores$estado_civil,colaboradores$estado_civil=="SOLTERA","SOLTERO(A)")
colaboradores$estado_civil <- replace(colaboradores$estado_civil,colaboradores$estado_civil=="DIVORCIADA","DIVORCIADO(A)")
colaboradores$estado_civil <- replace(colaboradores$estado_civil,colaboradores$estado_civil=="DIVORCIADO","DIVORCIADO(A)")

Análisis Exploratorio de las Bases de Datos

Estadísticos Descriptivos

A continuación se presenta una tabla de estadísticos descriptivos para cada una de las bases de datos y sus respectivos comentarios.

Merma

En merma como fecha y mes son datos cualitativos no contienen información descriptiva, sin embargo la variable de kilos si. A continuación podemos identificar que tiene skewness negativo, cercano a -1.

ed_merma <- describe(merma)
ed_merma
##       vars  n    mean      sd median trimmed    mad min  max range  skew
## fecha    1 50     NaN      NA     NA     NaN     NA Inf -Inf  -Inf    NA
## mes*     2 50    4.60    2.60      4    4.53   2.97   1    9     8  0.21
## kilos    3 50 3708.52 1023.99   3925 3798.65 541.15 790 6140  5350 -0.94
##       kurtosis     se
## fecha       NA     NA
## mes*     -1.35   0.37
## kilos     1.65 144.81
hist(merma$kilos)

Scrap

En Scrap referencia, fecha producto y ubicación de origen son más que nada cualitativas por lo que no tenemos sus datos descriptivos, sin embargo en cantidad sí, a continuación pdoemos encontrar su media, mediana, desviación, estandar y también su distribución, pues tiene skewness positiva alta, indicandonos que en caso de datos faltantes seria mejor remplazar con la mediana que con la media.

ed_scrap <- describe(scrap)
ed_scrap
##             vars   n   mean    sd median trimmed   mad min  max range  skew
## referencia*    1 250 125.50 72.31  125.5  125.50 92.66   1  250   249  0.00
## fecha          2 250    NaN    NA     NA     NaN    NA Inf -Inf  -Inf    NA
## producto*      3 250  44.38 24.72   45.0   44.12 25.95   1   95    94  0.01
## cantidad       4 250   6.69 11.85    2.0    3.88  1.48   0   96    96  4.12
## ubi_origen*    5 250   2.48  0.85    3.0    2.60  0.00   1    3     2 -1.10
##             kurtosis   se
## referencia*    -1.21 4.57
## fecha             NA   NA
## producto*      -0.79 1.56
## cantidad       21.13 0.75
## ubi_origen*    -0.70 0.05
hist(scrap$cantidad)

Producción de Cartón

En producción de carton tenemos los datos descriptivos principalmente de pizas programas, laminas procesadas, tiempo de calidad y tmo_min. EN todas estas ademas de encontrar su media, mediana y desviación estandar podemos identificar su skewness positiva de nuevo indicando su distribución y como en caso de valores faltantes es mejor remplazar con la mediana.

ed_carton <- describe(carton)
ed_carton
##                vars    n     mean       sd  median  trimmed      mad  min   max
## fecha             1 1486      NaN       NA      NA      NaN       NA  Inf  -Inf
## cliente*          2 1486     6.03     2.39     6.0     5.95     1.48    1    11
## piezas_prog       3 1486   169.96   127.90   184.0   153.67   124.54    1  1500
## tmo_min           4 1477    22.39    13.78    20.0    20.03     7.41   10   120
## hr_fin            5 1469 32241.73 11925.66 36000.0 33834.65  6226.92 3600 46500
## lam_procesadas    6 1486   153.70   141.65   105.5   131.81   130.47    1  1262
## inicio_sep_up     7 1486 30172.53 12770.81 34200.0 31263.53 10007.55 2280 46560
## fin_sep_up        8 1486 30107.00 13005.13 34380.0 31125.18 10585.76 3600 69300
## inicio_proceso    9 1486 29999.27 13061.85 34350.0 31016.22 10585.76 2100 57840
## fin_proceso      10 1486 29503.16 13563.70 34800.0 30462.55 11653.24 2100 57360
## tiempo_calidad   11 1431     1.29     1.50     1.0     1.00     0.00    0    25
##                range  skew kurtosis     se
## fecha           -Inf    NA       NA     NA
## cliente*          10  0.34     0.48   0.06
## piezas_prog     1499  1.97     9.97   3.32
## tmo_min          110  2.68    11.01   0.36
## hr_fin         42900 -1.18     0.08 311.15
## lam_procesadas  1261  2.24     9.66   3.67
## inicio_sep_up  44280 -0.75    -0.85 331.29
## fin_sep_up     65700 -0.68    -0.91 337.37
## inicio_proceso 55740 -0.68    -0.97 338.84
## fin_proceso    55260 -0.59    -1.17 351.86
## tiempo_calidad    25  9.21   107.84   0.04
hist(carton$piezas_prog)

hist(carton$lam_procesadas)

hist(carton$tiempo_calidad)

hist(carton$tmo_min)

Delivery Performance

Para Delivery performance podemos encontrar a continuación sus datos descriptivos, sin embargo hay algunos que no tomamos mucho en consideración por ejemplo, la fecha, vuelta o cliente, y por otro lado tenemos plan arrival, real arrival y real departure que son horas en teoria y difference que es la diferencia en minutos.

ed_perf <- describe(perf)
ed_perf
##                vars   n  mean   sd median trimmed  mad    min  max range   skew
## cliente*          1 800  1.12 0.32   1.00    1.02 0.00   1.00  2.0  1.00   2.35
## vueltas           2 800  1.91 0.83   2.00    1.89 1.48   1.00  3.0  2.00   0.16
## fecha             3 800   NaN   NA     NA     NaN   NA    Inf -Inf  -Inf     NA
## plan_arrival      4 800 19.52 1.29  20.00   19.91 0.00  16.00 20.0  4.00  -2.35
## real_arrival      5 800 12.84 5.21   9.09   12.52 1.62   6.00 23.5 17.50   0.47
## real_departure    6 800 13.88 5.26  10.15   13.57 1.70   6.40 24.5 18.10   0.45
## difference        7 800  1.03 0.83   1.00    1.03 0.03 -14.35  4.4 18.75 -12.42
##                kurtosis   se
## cliente*           3.54 0.01
## vueltas           -1.53 0.03
## fecha                NA   NA
## plan_arrival       3.54 0.05
## real_arrival      -1.58 0.18
## real_departure    -1.61 0.19
## difference       220.30 0.03

Delivery Plan

Con delivery plan muchas de las variables son horas o cualitativas, sin embargo tenemos la variable de unidades, la cual, al igual que en bases anteriores tiene una distribución con skew positiva y alta, por lo que en caso de valores faltantes nos convendría mas remplazar con la mediana que con la media.

ed_plan <- describe(plan)
ed_plan
##           vars   n   mean      sd median trimmed    mad min   max range  skew
## cliente*     1 972  17.71    6.87   21.0   18.52   2.97   1    26    25 -1.15
## proyecto*    2 972  46.03   26.24   58.0   46.50  14.83   1   103   102 -0.47
## item*        3 972 100.68   54.76  108.0  101.17  59.30   1   201   200 -0.20
## mes*         4 972  10.05    5.66   10.0   10.01   5.93   1    20    19 -0.03
## unidades     5 972 548.07 2016.47   90.5  176.34 116.38   1 29379 29378  8.56
##           kurtosis    se
## cliente*     -0.19  0.22
## proyecto*    -0.49  0.84
## item*        -1.12  1.76
## mes*         -1.10  0.18
## unidades     89.82 64.68
hist(plan$unidades)

Bajas

En bajas muchas de las variables son cualitativas por lo que carecen de estadisticos descriptivos, sin embargo, tenemos otra como el salario diario, el cual tiene una distribución con skewness positivo también.

ed_bajas <- describe(bajas)
ed_bajas
##                  vars   n   mean     sd median trimmed   mad    min  max
## nombre_completo*    1 238 117.54  67.76 116.50  117.43 86.73   1.00  235
## fecha_nac           2 237    NaN     NA     NA     NaN    NA    Inf -Inf
## genero*             3 238   1.41   0.49   1.00    1.39  0.00   1.00    2
## fecha_alta          4 237    NaN     NA     NA     NaN    NA    Inf -Inf
## mot_baja*           5 213   2.50   1.28   2.00    2.46  1.48   1.00    5
## permanencia         6 213  79.71 224.98  19.00   31.18 19.27   0.00 1966
## baja*               7 238  46.84  28.59  49.00   47.07 35.58   1.00   96
## puesto*             8 238   6.03   4.93   4.00    4.66  0.00   1.00   22
## departamento*       9 238  10.11   8.11   9.00    9.97 11.86   1.00   21
## sal_diario_imss    10 238 177.99  23.16 180.68  179.16  0.00 144.45  500
## colonia*           11 238  58.43  27.09  73.00   60.30 25.20   1.00   99
## CP*                12 238  26.08   8.66  26.00   26.52  2.97   1.00   48
## municipio*         13 238   3.74   2.77   2.00    3.28  0.00   1.00   13
## estado*            14 238   2.97   0.20   3.00    3.00  0.00   1.00    3
## estado_civil*      15 238   3.66   1.16   4.00    3.72  1.48   1.00    5
##                    range  skew kurtosis    se
## nombre_completo*  234.00  0.01    -1.21  4.39
## fecha_nac           -Inf    NA       NA    NA
## genero*             1.00  0.36    -1.88  0.03
## fecha_alta          -Inf    NA       NA    NA
## mot_baja*           4.00  0.33    -1.39  0.09
## permanencia      1966.00  5.65    35.86 15.42
## baja*              95.00 -0.04    -1.24  1.85
## puesto*            21.00  2.33     4.09  0.32
## departamento*      20.00  0.08    -1.72  0.53
## sal_diario_imss   355.55 11.09   154.20  1.50
## colonia*           98.00 -0.63    -0.88  1.76
## CP*                47.00 -0.52     1.75  0.56
## municipio*         12.00  1.22     0.12  0.18
## estado*             2.00 -8.56    75.40  0.01
## estado_civil*       4.00 -0.55    -1.07  0.08
hist(bajas$sal_diario_imss)

Colaboradores

El caso de la base de datos de colaboradores es muy similar al de bajas.

ed_colaboradores <- describe(colaboradores)
ed_colaboradores
##                  vars   n     mean       sd   median  trimmed   mad      min
## nombre_completo*    1 113    57.00    32.76    57.00    57.00 41.51     1.00
## fecha_nac           2 112      NaN       NA       NA      NaN    NA      Inf
## genero*             3 113     1.46     0.50     1.00     1.45  0.00     1.00
## fecha_alta          4 112      NaN       NA       NA      NaN    NA      Inf
## puesto*             5 113     4.85     6.64     1.00     3.46  0.00     1.00
## departamento*       6 113     7.73     6.53     7.00     7.25  8.90     1.00
## sal_diario_imss     7 113   180.25    28.60   180.68   176.77  0.00   144.45
## colonia*            8 113    39.42    19.75    47.00    40.07 19.27     1.00
## municipio*          9 113     2.23     1.97     1.00     1.82  0.00     1.00
## estado*            10 113     1.92     0.27     2.00     2.00  0.00     1.00
## CP                 11 113 63364.74 11201.89 66646.00 66622.84  4.45 25016.00
## estado_civil*      12 113     2.37     1.17     3.00     2.34  1.48     1.00
##                       max   range  skew kurtosis      se
## nombre_completo*   113.00   112.0  0.00    -1.23    3.08
## fecha_nac            -Inf    -Inf    NA       NA      NA
## genero*              2.00     1.0  0.16    -1.99    0.05
## fecha_alta           -Inf    -Inf    NA       NA      NA
## puesto*             21.00    20.0  1.52     0.67    0.62
## departamento*       19.00    18.0  0.36    -1.47    0.61
## sal_diario_imss    337.05   192.6  3.45    15.39    2.69
## colonia*            73.00    72.0 -0.38    -0.99    1.86
## municipio*           8.00     7.0  1.42     0.74    0.19
## estado*              2.00     1.0 -3.06     7.45    0.03
## CP               67493.00 42477.0 -3.06     7.42 1053.79
## estado_civil*        4.00     3.0 -0.09    -1.59    0.11
hist(colaboradores$sal_diario_imss)

Bar Plots

Merma

Para merma, nos interesa saber el total de kilos que se producen por mes. Para gráficar el total de kilos de merma por mes primero es necesario obtener la cantidad total de kilos por mes, eso lo hacemos de la siguiente manera.

merma_mes<-aggregate(kilos ~ mes, data = merma, sum) %>% arrange(desc(kilos))
merma_mes
##          mes kilos
## 1     AGOSTO 32100
## 2       MAYO 23410
## 3    FEBRERO 22830
## 4      MARZO 22470
## 5      JULIO 19370
## 6      ABRIL 18820
## 7      JUNIO 18280
## 8      ENERO 14560
## 9 SEPTIEMBRE 13586

Una vez que ya tenemos el total por mes podemos graficarlo.

ggplot(merma_mes,aes(x= reorder(mes, -kilos),y=kilos, fill=mes))+
  geom_bar(stat="identity")+labs(x="Mes", y="Kilos")+
  theme_minimal()+
  labs(title="Kilos de merma por mes")+theme(axis.text.x = element_text(angle=65, hjust=1))

### Scrap Para Scrap hemos optado por visualizar un gráfico de barras de la cantidad y la ubicación de origen.

ggplot(scrap,aes(x= reorder(ubi_origen, -cantidad),y=cantidad, fill=ubi_origen))+
  geom_bar(stat="identity")+labs(x="Ubicación de origen", y="Cantidad")+
  theme_minimal()+
  labs(title="Cantidad por ubicación de origen")+theme(axis.text.x = element_text(angle=65, hjust=1))

Producción de Cartón

La base de datos de producción de cartón solo abarca de los meses de julio a septiembre del 2022, la base de datos de merma incluye más meses pero del mismo año. Hemos graficado la cantidad de piezas programadas por mes en producción de cartón para confirmar si el mes con mayor cantidad de piezas programadas también es el mes con mayor cantidad de kilos de merma.

carton_mes <- carton %>% mutate(mes=format(fecha, "%m"))

carton_mes2<-aggregate(piezas_prog ~ mes, data = carton_mes, sum) %>% arrange(desc(piezas_prog))

ggplot(carton_mes2,aes(x= reorder(mes, -piezas_prog),y=piezas_prog, fill=mes))+
  geom_bar(stat="identity")+labs(x="Mes", y="Piezas programadas")+
  theme_minimal()+
  labs(title="Piezas programadas por mes")+theme(axis.text.x = element_text(angle=65, hjust=1))

Al observar el gráfico de barras podemos confirmar que efectivamente el mes de agosto, el mes donde más piezas programadas para producción hubo también fue el mes con mayor cantidad de merma.

Delivery Performance

Para delivery performance optamos por graficar el retraso que hubo entre el tiempo planeado de llegada con el cliente y el tiempo real. Nos dimos cuenta que el formato de hora variaba de 24 horas a 12 horas por lo que hicimos el cambio para que este correcto. Como podemos ver MAHLE tiene un delay mayor al de PRINTEL.

class(perf$real_arrival)
perf2 <- perf
perf3 <-  perf2 %>% mutate(plan_arrival=ifelse(cliente == "MAHLE"| plan_arrival ==  20 | real_arrival < 15, 8, plan_arrival))

perf3 <- mutate(perf3, delay=real_arrival-plan_arrival) 
perf3 <- aggregate(delay~cliente, data=perf3, mean)
ggplot(perf3,aes(x= cliente,y=delay, fill=cliente))+
  geom_bar(stat="identity")+labs(x="Clientes", y="Delay en min")+
  theme_minimal()+
  labs(title="Delay por cliente")+theme(axis.text.x = element_text(angle=65, hjust=1))

Delivery Plan

Con Delivery Plan queremos evaluar si el mes donde mayor cantidad de piezas programadas había también era el mes donde mayor producción hubo.

plan_mes<-aggregate(unidades ~ mes, data = plan, sum) %>% arrange(desc(unidades))

ggplot(plan_mes,aes(x= reorder(mes, -unidades),y=unidades, fill=mes))+
  geom_bar(stat="identity")+labs(x="Mes", y="Unidades programadas")+
  theme_minimal()+
  labs(title="Unidades programadas por mes")+theme(axis.text.x = element_text(angle=65, hjust=1))

Al observar el gráfico de barras podemos identificar que el mes en el que más piezas programadas hubo no fue el mes con mayor producción y merma.

Bajas

En esta gráfica de barras se pude observar que el principal motivo de las bajas son las faltas seguidamente de renuncias voluntarias. Es importante mencionar que las personas fueron dadas de bajas a pesar de tener solo una falta, por lo que no se puede realizar un buen diagnóstico sobre las bajas por faltas.

mot_baja <- bajas %>% group_by(mot_baja, genero) %>% tally()
mot_baja
## # A tibble: 11 x 3
## # Groups:   mot_baja [6]
##    mot_baja            genero        n
##    <chr>               <chr>     <int>
##  1 ABANDONO            FEMENINO     30
##  2 ABANDONO            MASCULINO    25
##  3 BAJA POR FALTAS     FEMENINO     56
##  4 BAJA POR FALTAS     MASCULINO    24
##  5 JUBILACION          MASCULINO     1
##  6 RENUNCIA VOLUNTARIA FEMENINO     37
##  7 RENUNCIA VOLUNTARIA MASCULINO    33
##  8 TERMINO DE CONTRATO FEMENINO      6
##  9 TERMINO DE CONTRATO MASCULINO     1
## 10 <NA>                FEMENINO     11
## 11 <NA>                MASCULINO    14
ggplot(data=mot_baja, aes(x=mot_baja, y=n, fill=genero)) +
geom_bar(stat="identity", position=position_dodge())+theme(axis.text.x = element_text(angle=65, hjust=1))

Colaboradores

En esta gráfica de barras podemos observar el sueldo promedio por puesto y género. Una observación importante es en el puesto de supervisor/supervisora, ya que las mujeres tienen mayor salario que los hombres a pesar de realizar el mismo trabajo. Otro puesto en donde la situación es similar pero el salario no es de mucha diferencia es en el puesto de costurera/costurero.
Al observar esta gráfica podemos decir que las mujeres ganan más, ya que a pesar de que realicen el mismo trabajo las mujeres reciben un mayor sueldo que los hombres.

sueldo_genero_puesto<-aggregate(sal_diario_imss ~ puesto+genero, data = colaboradores, mean) 

ggplot(data=sueldo_genero_puesto, aes(x=puesto, y=sal_diario_imss, fill=genero)) +
geom_bar(stat="identity", position=position_dodge())+theme(axis.text.x = element_text(angle=65, hjust=1))

Dispersion Plots

Merma

En esta gráfica de boxplot podemos observar la dispersión de kilos de merma por mes. Agosto es el mes con mayor dispersión,por lo que podemos inferir que en este mes es donde hay mayor producción.

ggplot(merma, aes(x=mes, y=kilos)) + 
  geom_boxplot(color="red", fill="orange", alpha=0.2)

Scrap

En esta gráfica de boxplot podemos observar la desperation de cantidad de scrap por ubicación de origen. Como se puede observar dónde hay mayor dispersión de scrap es en pre-producción y esto se puede deber a que la empresa utiliza más materiales en esta área de trabajo.

ggplot(scrap, aes(x=ubi_origen, y=cantidad)) + 
  geom_boxplot(color="red", fill="orange", alpha=0.2)

Producción de Cartón

En esta gráfica podemos observar la dispersión de tiempo de calidad por cliente de producción de cartón. Existen diferentes motivos por lo cual se tome mayor tiempo de calidad a ciertos clientes esto puede ser por:
- cantidad de piezas
- cliente frecuente
- empaque complicado

ggplot(carton, aes(x=cliente, y=tiempo_calidad)) + 
  geom_boxplot(color="red", fill="orange", alpha=0.2)+theme(axis.text.x = element_text(angle=65, hjust=1))
## Warning: Removed 55 rows containing non-finite values (stat_boxplot).

Delivery Performance

Al graficar la dispersión de la diferencia entre real arrival y departure arrival vemos que hay mayor variacion en el cliente MAHLE que en PRINTEL.

ggplot(perf, aes(x=cliente, y=difference)) + 
  geom_boxplot(color="red", fill="orange", alpha=0.2)+theme(axis.text.x = element_text(angle=65, hjust=1))

Delivery Plan

El boxplot a continuación muestra la dispersión de las unidades programadas por cliente, como podemos ver Hella y TRMX tienen mayor cantidad de unidades programadas y es más dispersa su distribución.

ggplot(plan, aes(x=cliente, y=unidades)) + 
  geom_boxplot(color="red", fill="orange", alpha=0.2)+theme(axis.text.x = element_text(angle=65, hjust=1))

Bajas

El siguiente es un boxplot de la permanencia en la empresa en las bajas por genero, por lo general las mujeres duran más en la empresa.

ggplot(bajas, aes(x=genero, y=permanencia)) + 
  geom_boxplot(color="red", fill="orange", alpha=0.2)+theme(axis.text.x = element_text(angle=65, hjust=1))
## Warning: Removed 25 rows containing non-finite values (stat_boxplot).

Colaboradores

Este boxplot muestra la dispersión que hay en el salario diario que reciben los colaboradores por género, la dispersión en mayor en el género femenino, hay mujeres que ganan considerablemente más que los hombres.

ggplot(colaboradores, aes(x=genero, y=sal_diario_imss)) + 
  geom_boxplot(color="red", fill="orange", alpha=0.2)+theme(axis.text.x = element_text(angle=65, hjust=1))

Time Series Plots

Merma

En el siguiente plot podemos identificar la fecha en la que hubo mayor kilos de merma. A mediados de febrero hubo una gran cantidad de merma, específicamente el 18 de febrero. Podemos observar también que después de cada pico de merma, hay una corrección y la merma disminuye. Esto podría ser por diversos factores como ajustes en la producción, en los cortes, entre otros.

merma_fecha<-aggregate(kilos ~ fecha, data = merma, sum) 

ggplot(merma_fecha,aes(x=fecha,y=kilos))+
  geom_line()+
  labs(title="Kilos por fecha")+ theme(axis.text.x = element_text(angle=65, hjust=1))

Producción de Cartón

Para la producción de cartón vamos a graficar el tiempo en minutos que tomó el proceso. Para esto necesitamos restar el tiempo de inicio de proceso al de fin de proceso.

carton2<-mutate(carton, tiempo_proceso = fin_proceso-inicio_proceso)
carton2 <- filter(carton2, !tiempo_proceso < 0)

Asi mismo, vamos a sacar el promedio de tiempo que toma el proceso por cliente. De esta forma podremos saber cual cliente consume mas tiempo de produccion de Form en el mes de agosto.

prod_cliente<-aggregate(tiempo_proceso ~ cliente, data=carton2, mean)
prod_cliente
##                 cliente tiempo_proceso
## 1                 DENSO       00:20:08
## 2         HANON SYSTEMS       01:23:30
## 3                 HELLA       00:12:00
## 4  MERIDIAN LIGHTWEIGHT       00:18:56
## 5            STABILUS 1       00:20:34
## 6            STABILUS 3       00:19:00
## 7                  TRMX       00:17:11
## 8                VARROC       00:28:45
## 9          VL-017-13939       00:27:40
## 10         VL-017-14086       00:27:00
## 11              YANFENG       00:32:25
ggplot(prod_cliente,aes(x=cliente,y=tiempo_proceso))+
  geom_point()+
  labs(title="Tiempo promedio por cliente")+ theme(axis.text.x = element_text(angle=90, hjust=1))
## Don't know how to automatically pick scale for object of type ITime. Defaulting to continuous.

Como podemos ver Hanon Systems, Yen Fang y Varroc son los clientes que mayor tiempo toma su proceso de producción. Esto puede ser debido a la complejidad de sus productos o material, sin embargo, hay que prestar especial atención a la gran diferencia que hay entre Hanon Systems y el resto de clientes.

Delivery Plan

En Delivery Plan podemos observar la cantidad de unidades por mes por cliente. Podemos identificar que HELLA en la gran mayoría de las fechas es el cliente con mayor cantidad de unidades planeadas, rara vez es superado por TRMX.

plan_mesycliente <- aggregate(unidades ~ cliente+mes, data=plan, sum)

plan_mesycliente <- plan_mesycliente %>%  pivot_wider(names_from = cliente, values_from = unidades)

plan_mesycliente <- dplyr::select(plan_mesycliente, c(mes,"HELLA", "TRMX", "VARROC", "DENSO"))


ggplot(plan_mesycliente, aes(x=mes)) + 
  geom_point(aes(y = HELLA), color = "darkred") + 
  geom_point(aes(y = TRMX), color="steelblue", linetype="twodash")+
  geom_point(aes(y = VARROC), color = "darkgreen") + 
  geom_point(aes(y = DENSO), color="darkorange", linetype="twodash")+ theme(axis.text.x = element_text(angle=90, hjust=1))

Bajas

En estos gráficos de barras podemos identificar los meses donde más bajas hay y donde más ingresos hay, identificando patrones. En las bajas, la mayor cantidad ocurren al inicio del año y posteriormente a mediados pero casi nunca a finales de año. Esto podría deberse a que los colaboradores no quieren estar sin trabajo en fechas donde sus gastos pueden ser mayores como navidad, año nuevo, cierre e inicio de ciclos escolares, entre otros.

En el caso de los ingresos o altas, estas se dan más a mediados de año, muy probablemente FORM haga esto para cubrir las bajas que ocurrieron a inicios y mediados de año. De igual forma, las altas son muy bajas a finales de año, quizás podría deberse a que en estas fechas FORM está cerrando el año, preparando aguinaldos, entre otros costos que tiene por lo que prefiere no contratar nuevos empleados.

bajas$baja <- as.Date(bajas$baja, format ="%d/%m/%Y")
bajas_mes <- bajas %>% mutate(mes_baja=format(baja, "%m"))
bajas_mes <- bajas_mes %>% mutate(mes_alta=format(fecha_alta,"%m"))

bajas_por_mes <- bajas_mes %>% group_by(mes_baja) %>% tally()
altas_por_mes <- bajas_mes %>% group_by(mes_alta) %>% tally()

ggplot(bajas_por_mes,aes(x= reorder(mes_baja,-n),y=n, fill=mes_baja))+
  geom_bar(stat="identity")+labs(x="Mes", y="Bajas")+
  theme_minimal()+
  labs(title="Bajas por mes")+theme(axis.text.x = element_text(angle=65, hjust=1))

ggplot(altas_por_mes,aes(x= reorder(mes_alta,-n),y=n, fill=mes_alta))+
  geom_bar(stat="identity")+labs(x="Mes", y="Bajas")+
  theme_minimal()+
  labs(title="Altas por mes")+theme(axis.text.x = element_text(angle=65, hjust=1))

Colaboradores

Graficamos antiguedad por puesto y podemos identificar que los hombres tienen mayor antiguedad en form que las mujeres, además que los unicos puestos donde hay hombres y mujeres son en supervisor(a) y costurera(o). Así mismo que los puestos hay más para hombres que para muejeres.

colaboradores <- mutate(colaboradores, antiguedad=today()-fecha_alta)

antiguedad_puesto <- aggregate(antiguedad ~ puesto+genero, data=colaboradores, mean)

ggplot(data=antiguedad_puesto, aes(x=puesto, y=antiguedad, fill=genero)) +
geom_bar(stat="identity", position=position_dodge())+theme(axis.text.x = element_text(angle=65, hjust=1))
## Don't know how to automatically pick scale for object of type difftime. Defaulting to continuous.

Predicción del Desempeño

¿Qué nos dice esta base de datos?

Tenemos las ventas internacionales de autopartes por estado de la república mexicana. Nuestro proosito es encontrar el estado con mayor futuro y volumen de ventas internacionales de autopartes.

ventasi_autopartes <- read.csv("VentasAP.csv")
summary(ventasi_autopartes)
##       year       id_trimestre     trimestre       id_estado    
##  Min.   :2014   Min.   :20141   Min.   :1.000   Min.   : 1.00  
##  1st Qu.:2016   1st Qu.:20161   1st Qu.:1.000   1st Qu.:10.00  
##  Median :2018   Median :20181   Median :2.000   Median :17.00  
##  Mean   :2018   Mean   :20180   Mean   :2.399   Mean   :17.37  
##  3rd Qu.:2020   3rd Qu.:20201   3rd Qu.:3.000   3rd Qu.:26.00  
##  Max.   :2022   Max.   :20222   Max.   :4.000   Max.   :32.00  
##     estado          id_estado_and_id_trimestre   idnueva         
##  Length:690         Min.   : 120141            Length:690        
##  Class :character   1st Qu.:1020152            Class :character  
##  Mode  :character   Median :1720178            Mode  :character  
##                     Mean   :1756991                              
##                     3rd Qu.:2620172                              
##                     Max.   :3220222                              
##  ventas_autopartes_trimestre ventas_autopartes_anual      eci         
##  Min.   :     10839          Min.   :      4382      Min.   :-0.9374  
##  1st Qu.:  88046463          1st Qu.:  22559574      1st Qu.:-0.4630  
##  Median : 897579256          Median : 231613702      Median : 0.7088  
##  Mean   :1237565052          Mean   : 330709701      Mean   : 0.4391  
##  3rd Qu.:2151422249          3rd Qu.: 575938509      3rd Qu.: 0.9138  
##  Max.   :4205866546          Max.   :1467265578      Max.   : 1.7810  
##  poblacion_ocupada_ensambladora_year exportaciones_anual
##  Length:690                          Length:690         
##  Class :character                    Class :character   
##  Mode  :character                    Mode  :character   
##                                                         
##                                                         
##                                                         
##  exportaciones_trimestrales iedanual_porestado
##  Length:690                 Length:690        
##  Class :character           Class :character  
##  Mode  :character           Mode  :character  
##                                               
##                                               
## 

¿Que modificaciones le haremos a la base de datos?

En excel realizamos limpieza de datos con: nombres_de_variables, añadir cifrias anuales y unir bases de datos. Esto con la finalidad de realizar una predicción sobre los Estados de la República con el mayor número de ventas internacionales de autopartes en México.

En R, la limpieza que realizaremos será: eliminar registros de los trimestres 2, 3 y 4. Usamos como base el trimestre 1 para juntar las cifras anuales y no repetir registros.

Eliminamos las columnas que no necesitaremos y Cambiamos los tipos de variable.

#Filtrar por trimestre
vap_1 <- ventasi_autopartes
vap_1 <- filter(vap_1, trimestre == 1)
vap_1
#Eliminar columnas
vap_2 <- vap_1
vap_2 <- subset(vap_2,select = -c(id_trimestre, trimestre, id_estado, id_estado_and_id_trimestre, idnueva, ventas_autopartes_trimestre, exportaciones_trimestrales))
summary(vap_2)
##       year         estado          ventas_autopartes_anual      eci         
##  Min.   :2014   Length:186         Min.   :      7241      Min.   :-0.9374  
##  1st Qu.:2016   Class :character   1st Qu.:  23444726      1st Qu.:-0.4143  
##  Median :2018   Mode  :character   Median : 231380284      Median : 0.7088  
##  Mean   :2018                      Mean   : 330227591      Mean   : 0.4587  
##  3rd Qu.:2020                      3rd Qu.: 557443513      3rd Qu.: 0.9138  
##  Max.   :2022                      Max.   :1299710425      Max.   : 1.7810  
##  poblacion_ocupada_ensambladora_year exportaciones_anual iedanual_porestado
##  Length:186                          Length:186          Length:186        
##  Class :character                    Class :character    Class :character  
##  Mode  :character                    Mode  :character    Mode  :character  
##                                                                            
##                                                                            
## 
#Cambiar tipos de variable
vap_3 <- vap_2
vap_3$ventas_autopartes_anual <- as.numeric(vap_3$ventas_autopartes_anual)
vap_3$eci <- as.numeric(vap_3$eci)
vap_3$poblacion_ocupada_ensambladora_year <- as.numeric(vap_3$poblacion_ocupada_ensambladora_year)
## Warning: NAs introducidos por coerción
vap_3$exportaciones_anual <- as.numeric(vap_3$exportaciones_anual)
## Warning: NAs introducidos por coerción
vap_3$iedanual_porestado <- as.numeric(vap_3$iedanual_porestado)
## Warning: NAs introducidos por coerción
vap_3$year <- as.numeric(vap_3$year)
vap_3$estado <- as.character(vap_3$estado)
vap_3
vap_4 <- vap_3
vap_4 <- vap_4 %>%
  group_by(estado) %>%
  summarise(ventas = sum(ventas_autopartes_anual))
vap_4
vap_7 <- vap_3
top_5 <- vap_4 %>%
  filter(rank(desc(ventas))<=5)
top_5

Encontrar el top 5 de los estados con mayor numero de ventas internacionales de autopartes y enfocarnos en ellos.

#Eliminar registros de estados no relevantes
nueva <- vap_3
nueva <- filter(nueva, !estado=="Durango")
nueva <- filter(nueva, !estado=="Hidalgo")
nueva <- filter(nueva, !estado=="Jalisco")
nueva <- filter(nueva, !estado=="Estado de Mexico")
nueva <- filter(nueva, !estado=="Morelos")
nueva <- filter(nueva, !estado=="Hidalgo")
nueva <- filter(nueva, !estado=="Quintana Roo")
nueva <- filter(nueva, !estado=="Sonora")
nueva <- filter(nueva, !estado=="Zacatecas")
nueva <- filter(nueva, !estado=="Yucatan")
nueva <- filter(nueva, !estado=="Tlaxcala")
nueva <- filter(nueva, !estado=="Veracruz de Ignacio de la Llave")
nueva <- filter(nueva, !estado=="Colima")
nueva <- filter(nueva, !estado=="Baja California")
nueva <- filter(nueva, !estado=="Chihuaua")
nueva <- filter(nueva, !estado=="Puebla")
nueva <- filter(nueva, !estado=="Aguascalientes")
nueva <- filter(nueva, !estado=="San Luis Potosi")
nueva <- filter(nueva, !estado=="Tamaulipas")
nueva <- filter(nueva, !estado=="Chihuahua")
nueva <- filter(nueva, !estado=="Ciudad de Mexico")

unique(nueva$estado)

Eliminamos tambien a Ciudad de Mexico debido a que es un mercado fuera del que busca FORM.

top_4 <- nueva
top_4

Realizaremos un modelo de regresion lineal para los estados de Coahuila, Guanajuato, Nuevo Leon y Queretaro. Esto nos permitirá evaluar el rumbo de los estados con más ventas internacionales de autopartes.

model1<-lm(ventas_autopartes_anual~eci+poblacion_ocupada_ensambladora_year+exportaciones_anual+iedanual_porestado,data=top_4)     
summary(model1)
## 
## Call:
## lm(formula = ventas_autopartes_anual ~ eci + poblacion_ocupada_ensambladora_year + 
##     exportaciones_anual + iedanual_porestado, data = top_4)
## 
## Residuals:
##        Min         1Q     Median         3Q        Max 
## -200117593 -102191916   11993893   81461056  160068056 
## 
## Coefficients:
##                                             Estimate       Std. Error t value
## (Intercept)                         951429657.032634  92629981.493389  10.271
## eci                                 -94939113.056243  56316538.389027  -1.686
## poblacion_ocupada_ensambladora_year      -524.951185      3436.193733  -0.153
## exportaciones_anual                         0.003336         0.003346   0.997
## iedanual_porestado                         -0.127185         0.038684  -3.288
##                                            Pr(>|t|)    
## (Intercept)                         0.0000000000797 ***
## eci                                         0.10336    
## poblacion_ocupada_ensambladora_year         0.87971    
## exportaciones_anual                         0.32759    
## iedanual_porestado                          0.00281 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 114700000 on 27 degrees of freedom
##   (4 observations deleted due to missingness)
## Multiple R-squared:  0.4733, Adjusted R-squared:  0.3953 
## F-statistic: 6.065 on 4 and 27 DF,  p-value: 0.001288

Podemos identificar las variables a utilizar dentro del modelo.

ventas_autopartes_anual: es la variable dependiente que nos ayudará a responder la pregunta principal sobre las ventas por estado.

eci: variable exploratoria para comparar los estados con mayores niveles de ingresos, potencial de crecimiento económico, menor desigualdad de ingresos y menores emisiones.

poblacion_ocupada_ensambladora_year: variable exploratoria que se refiere a las personas dentro del estado que trabajan dierectamente dentro de la industria de autopartes en el respecivo año.

exportaciones_anual: variable exploratoria que se refiere a el número de exportaciones anuales del estado en todas las industrias.

iedanual_porestadol: variable explanatoria que se refiere a la inversion extranjera directa anual del estado, no necesariamente enfocada en la industria automotriz.

Modelo lineal por estado

model1coahuila<-lm(ventas_autopartes_anual~eci+poblacion_ocupada_ensambladora_year+exportaciones_anual+iedanual_porestado,data=coahuila) 
summary(model1coahuila)
## 
## Call:
## lm(formula = ventas_autopartes_anual ~ eci + poblacion_ocupada_ensambladora_year + 
##     exportaciones_anual + iedanual_porestado, data = coahuila)
## 
## Residuals:
##          1          2          3          4          5          6          7 
## -112098108  -21573011   68781706   17705699   66916376  -25757932  -54147456 
##          8 
##   60172727 
## 
## Coefficients: (1 not defined because of singularities)
##                                             Estimate       Std. Error t value
## (Intercept)                         480278614.114136 431472506.467605   1.113
## eci                                               NA               NA      NA
## poblacion_ocupada_ensambladora_year      8150.653336      5472.637197   1.489
## exportaciones_anual                         0.008748         0.010996   0.796
## iedanual_porestado                         -0.123249         0.099760  -1.235
##                                     Pr(>|t|)
## (Intercept)                            0.328
## eci                                       NA
## poblacion_ocupada_ensambladora_year    0.211
## exportaciones_anual                    0.471
## iedanual_porestado                     0.284
## 
## Residual standard error: 86270000 on 4 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.7555, Adjusted R-squared:  0.5722 
## F-statistic:  4.12 on 3 and 4 DF,  p-value: 0.1025

Coahuila: Adjusted R-squared: 0.5317

model1nuevoleon<-lm(ventas_autopartes_anual~eci+poblacion_ocupada_ensambladora_year+exportaciones_anual+iedanual_porestado,data=nuevoleon) 
summary(model1nuevoleon)
## 
## Call:
## lm(formula = ventas_autopartes_anual ~ eci + poblacion_ocupada_ensambladora_year + 
##     exportaciones_anual + iedanual_porestado, data = nuevoleon)
## 
## Residuals:
##         1         2         3         4         5         6         7         8 
## -27425247  45082342  13143113  34080604 -16783601 -23023827   1501825 -26575210 
## 
## Coefficients: (1 not defined because of singularities)
##                                             Estimate       Std. Error t value
## (Intercept)                         994959864.548289 157202606.167140   6.329
## eci                                               NA               NA      NA
## poblacion_ocupada_ensambladora_year      6949.747308      3018.173156   2.303
## exportaciones_anual                        -0.015302         0.004382  -3.492
## iedanual_porestado                         -0.042347         0.023390  -1.810
##                                     Pr(>|t|)   
## (Intercept)                          0.00319 **
## eci                                       NA   
## poblacion_ocupada_ensambladora_year  0.08270 . 
## exportaciones_anual                  0.02508 * 
## iedanual_porestado                   0.14447   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 37550000 on 4 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.7897, Adjusted R-squared:  0.6321 
## F-statistic: 5.008 on 3 and 4 DF,  p-value: 0.07683

Nuevo Leon: Adjusted R-squared: 0.8242

model1guanajuato<-lm(ventas_autopartes_anual~eci+poblacion_ocupada_ensambladora_year+exportaciones_anual+iedanual_porestado,data=guanajuato) 
summary(model1guanajuato)
## 
## Call:
## lm(formula = ventas_autopartes_anual ~ eci + poblacion_ocupada_ensambladora_year + 
##     exportaciones_anual + iedanual_porestado, data = guanajuato)
## 
## Residuals:
##         1         2         3         4         5         6         7         8 
## -41107653  16667588   6583736  55782925 -20566009 -50526839   9687603  23478649 
## 
## Coefficients: (1 not defined because of singularities)
##                                             Estimate       Std. Error t value
## (Intercept)                         320795370.506850 144282624.498742   2.223
## eci                                               NA               NA      NA
## poblacion_ocupada_ensambladora_year      3953.515522      2548.475105   1.551
## exportaciones_anual                         0.013471         0.006916   1.948
## iedanual_porestado                          0.049807         0.036810   1.353
##                                     Pr(>|t|)  
## (Intercept)                           0.0903 .
## eci                                       NA  
## poblacion_ocupada_ensambladora_year   0.1958  
## exportaciones_anual                   0.1233  
## iedanual_porestado                    0.2474  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 46750000 on 4 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.7318, Adjusted R-squared:  0.5307 
## F-statistic: 3.639 on 3 and 4 DF,  p-value: 0.1221

Guanajuato:Adjusted R-squared: 0.5904

model1queretaro<-lm(ventas_autopartes_anual~eci+poblacion_ocupada_ensambladora_year+exportaciones_anual+iedanual_porestado,data=queretaro) 
summary(model1queretaro)
## 
## Call:
## lm(formula = ventas_autopartes_anual ~ eci + poblacion_ocupada_ensambladora_year + 
##     exportaciones_anual + iedanual_porestado, data = queretaro)
## 
## Residuals:
##         1         2         3         4         5         6         7         8 
## -29436644   9131024  37147064 -44654010  -2383969 -15201637  -4223647  49621818 
## 
## Coefficients: (1 not defined because of singularities)
##                                            Estimate      Std. Error t value
## (Intercept)                         490168045.45645 201625052.05102   2.431
## eci                                              NA              NA      NA
## poblacion_ocupada_ensambladora_year     28450.78982     18772.95436   1.516
## exportaciones_anual                         0.01420         0.01763   0.805
## iedanual_porestado                         -0.06691         0.12921  -0.518
##                                     Pr(>|t|)  
## (Intercept)                           0.0719 .
## eci                                       NA  
## poblacion_ocupada_ensambladora_year   0.2042  
## exportaciones_anual                   0.4659  
## iedanual_porestado                    0.6319  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 41950000 on 4 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.7624, Adjusted R-squared:  0.5842 
## F-statistic: 4.279 on 3 and 4 DF,  p-value: 0.09704

Queretaro: Adjusted R-squared: 0.686

library(jtools)
## 
## Attaching package: 'jtools'
## The following object is masked from 'package:epiDisplay':
## 
##     summ
## The following object is masked from 'package:Hmisc':
## 
##     %nin%
effect_plot(model1nuevoleon,pred=ventas_autopartes_anual,interval=TRUE)
## Warning in predict.lm(model, newdata = pm, se.fit = interval, interval =
## int.type[1], : prediction from a rank-deficient fit may be misleading
## geom_path: Each group consists of only one observation. Do you need to adjust
## the group aesthetic?

library(tseries)   # time series analysis and computational finance 
library(forecast)  # provides methods and tools for displaying and analyzing univariate time series forecast
library(astsa)     # applied statistical time series analysis 
options(scipen =999)
plot(nuevoleon$year,nuevoleon$ventas_autopartes_anual, type="l",col="blue", lwd=1.5, xlab ="año",ylab ="Ventas en dolares", main = "Ventas Internacionales de Nuevo Leon en Autopartes ")
lines(nuevoleon$ventas_autopartes_anual,coahuila$ventas_autopartes_anual, col="red",lty=3)
legend("topleft", legend=c("Nuevo Leon", "Coahuila"),
       col=c("blue", "red"), lty = 1:2, cex=0.8) 

options(scipen =999)
colors <- c("coahuila"="green", "nuevoleon"="orange", "queretaro"="blue", "guanajuato"="red")
ggplot(data=coahuila, aes(x=year, y=ventas_autopartes_anual), color='purple') + 
geom_line() + 
geom_line(data=nuevoleon, aes(x=year, y=ventas_autopartes_anual), color='orange') + 
geom_line() + 
geom_line(data=queretaro, aes(x=year, y=ventas_autopartes_anual), color='blue')+
geom_line() + 
geom_line(data=guanajuato, aes(x=year, y=ventas_autopartes_anual), color='red')+
  scale_colour_manual("", 
                      breaks = c("Coahuila", "Nuevo Leon", "Queretaro", "Guanajuato"),
                      values = c("green", "orange", "blue", "red"))+
  labs(x="Year", y="Total Ventas Dolares")

Pronóstico del Desempeño de la Industria Automotriz y la empresa FORM

Moving Average

summary(model1nuevoleon<-arma(nuevoleon$ventas_autopartes_anual,order=c(0,1)))
## 
## Call:
## arma(x = nuevoleon$ventas_autopartes_anual, order = c(0, 1))
## 
## Model:
## ARMA(0,1)
## 
## Residuals:
##        Min         1Q     Median         3Q        Max 
## -155651952  -37299724   19338817   44510100   79154505 
## 
## Coefficient(s):
##                 Estimate     Std. Error   t value            Pr(>|t|)    
## ma1               0.7301         0.3604     2.026              0.0428 *  
## intercept 600553057.7170     37703.4591 15928.328 <0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Fit:
## sigma^2 estimated as 5473146058018973,  Conditional Sum-of-Squares = 38381860832053096,  AIC = 355.69
nuevoleon_forecast<-forecast(model1nuevoleon$fitted,h=5,level=c(95))
## Warning in ets(object, lambda = lambda, biasadj = biasadj,
## allow.multiplicative.trend = allow.multiplicative.trend, : Missing values
## encountered. Using longest contiguous portion of time series
nuevoleon_forecast
##    Point Forecast     Lo 95     Hi 95
## 10      591345747 488822579 693868916
## 11      591345747 488822579 693868916
## 12      591345747 488822578 693868917
## 13      591345747 488822578 693868917
## 14      591345747 488822577 693868918

Identificación y Descripción de los Principales Resultados

Merma

Realizando un análisis de la base de datos merma nos dimos cuenta que los meses con mayor producción de merma son los meses de agosto con 32,500 kilos de merma y mayo con 22,700 kilos de merma, en base a esto podemos inferir que en estos meses es donde la empresa FORM tiene mayor producción. De igual manera se identificaron los meses donde la empresa producía menos merma, los cuales fueron los meses de enero con 14,800 kilos de merma y septiembre con 12,400 kilos de merma, por lo que también podemos inferir que en estos meses FORM no tiene mucha producción.

Scrap

Analizando los resultados de la base de datos Scrap, el cual se refiere a todos los desechos y/o residuos derivados del proceso industrial, se observa que SAB/Pre-Production es el lugar donde más cantidad existe con un valor total de 1500 toneladas.

Producción de cartón

Las gráficas de dispersión nos indican que con el cliente YANFENG es la vez que más se ha llevado más tiempo de calidad, podemos analizar el tiempo que se llevó a cabo con un empaque y si la producción del mismo ¿es rentable para la empresa? o si lo mejor es no aceptar producir este tipo de producto.

Por otro lado con las series de tiempo, observamos que con Hanon Systems es con quien más se ha tomado tiempo en el proceso. Podemos preguntarnos si es conveniente llevar procesos de producción tan largos con un cliente y evaluar su rentabilidad.

Delivery Performance

En Delivery Performance hemos podido identificar que MAHLE tiene mayor delay que PRINTEL, por lo que podría decirnos que algo sucede con MAHLE que ocasiona que se retrase más que otros clientes. Habría que tomar medidas correctivas.

Delivery Plan

Al analizar los resultados, se obtuvo que el mes con más piezas programadas fue Julio de 2022, seguido de Septiembre y Mayo. Además se puede observar una caída significativa de piezas programadas entre el mes de julio a septiembre, ya que en julio hay más de 72,000 piezas programadas y para septiembre ese valor decreció con 10 mil unidades menos.

Bajas

El principal motivo de las bajas fue por renuncias, este es un dato muy interesante sin embargo no se puede llegar a hacer una buena interpretación ya que muchos de los ex empleados fueron dados de baja solo por faltar un día. El segundo motivo de las bajas fue por renuncia voluntaria y muchas de estas personas no renunciaron sin complir el año en la empresa. Es importante que la empresa recuerde bien la información sobre las bajas y sus motivos para de esta manera generar una buena estrategia en disminuir la rotación de empleados, ya que, la mayoría de los empleados solo dura un año o menos en la empresa.

Colaboradores

La persona que más salario tienen son la supervisora mujeres y los de mantenimiento, si FORM necesita un recorte en gastos se fijaría primeramente en estas personas.

Ventas Internacionales de Autopartes por Estado (Datos externos)

El estado de Coahuila se mantiene como el líder en ventas de autopartes y en el 2022 tuvo un despunte en las ventas.
- Querétaro y Guanajuato siguen manteniendo un crecimiento en el volúmen de ventas.
- Nuevo León se está recuperando del declive que tuvo en 2018.
- En general la industria en México está creciendo.

Reflexión Final

¿Qué es Business Analytics?

Es el proceso de recolección de datos mediante diferentes modelos predictivos y estadísticos con el fin de analizar la situación actual de la empresa para predecir evento, tendencias y comportamientos a futuro que ayuden a determinar la toma de decisiones, reduciendo los riesgos de equivocaciones futuras.

¿Describir brevemente 3 objetivos deL uso de la herramienta de Business Analytics?

  • Diseñar y ejecutar proyectos de análisis de datos.
  • Responder a necesidades empresariales utilizando el análisis de datos y la inteligencia artificial.
  • Comunicar de forma eficiente los resultados obtenidos.
  • Obtener conocimientos tecnológicos en el ámbito de los datos.
  • Obtener conocimientos analíticos aplicados al ámbito de los datos (estadística, matemáticas y modelización matemática, entre otros)
  • Obtener conocimientos en diferentes ámbitos empresariales (economía, finanzas, marketing, operaciones, salud, industria, etc.)

¿Cuál es la relación entre Business Analytics y Business Intelligence?

Con Business Intelligence se puede optimizar, evaluar y coordinar las actividades internas de una empresa, mientras que con Business Analytics e ponen en marcha soluciones empresariales con las que satisfacer las necesidades de un negocio.

Se puede decir que el Business Intelligence se preocupa por el qué y el cómo, y el Business Analytics sí se pregunta por qué pasan las cosas para poder predecir futuros comportamientos o tendencias.

LS0tDQp0aXRsZTogIkVudHJlZ2FibGUgUjYiDQphdXRob3I6IDxjZW50ZXI+IEVxdWlwbyA1PC9jZW50ZXI+IA0KZGF0ZTogPGNlbnRlcj5gciBTeXMuRGF0ZSgpYDwvY2VudGVyPiANCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCjxjZW50ZXI+DQo8aW1nIHNyYz0iQzovL1VzZXJzLy9qYXZhdy8vT25lRHJpdmUgLSBJbnN0aXR1dG8gVGVjbm9sb2dpY28geSBkZSBFc3R1ZGlvcyBTdXBlcmlvcmVzIGRlIE1vbnRlcnJleS8vTG9nbyBJVEVTTSBzbWFsbC5wbmciPg0KPC9jZW50ZXI+IA0KDQoNCmBgYHtjc3MsIGVjaG89RkFMU0V9DQpoMSB7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDEsIGgyLCBoMywgaDQgew0KICBjb2xvcjogI0Y5NzQwNTsNCn0NCmBgYA0KDQoqKkzDrWJyZXLDrWFzKioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGRhdGEudGFibGUpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShuYW5pYXIpDQpsaWJyYXJ5KEhtaXNjKSAgICAgICAgIA0KbGlicmFyeShwc3ljaCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkocG9sbHN0ZXIpDQpsaWJyYXJ5KGVwaURpc3BsYXkpDQpsaWJyYXJ5KGRlc2NyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkodGV4dGNsZWFuKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpgYGANCg0KKipJbXBvcnRhciBiYXNlcyBkZSBkYXRvcyoqDQpgYGB7cn0NCnNldHdkKCJDOlxcVXNlcnNcXGphdmF3XFxPbmVEcml2ZSAtIEluc3RpdHV0byBUZWNub2xvZ2ljbyB5IGRlIEVzdHVkaW9zIFN1cGVyaW9yZXMgZGUgTW9udGVycmV5XFw3bW8gU2VtZXN0cmVcXFJldG8iKQ0KbWVybWE8LXJlYWQuY3N2KCJGT1JNIC0gTWVybWEuY3N2IikNCnNjcmFwPC1yZWFkLmNzdigiRk9STSAtIFNjcmFwLmNzdiIpDQpjYXJ0b248LXJlYWQuY3N2KCJGT1JNIC0gUHJvZHVjY2lvbiBDYXJ0b24gQ29tcGxldGEuY3N2IikNCnBlcmY8LXJlYWQuY3N2KCJEZWwgUGVyZi5jc3YiKQ0KcGxhbjwtcmVhZC5jc3YoIkRQXzEuY3N2IikNCmJhamFzPC1yZWFkLmNzdigiUkguY3N2IikNCmNvbGFib3JhZG9yZXM8LXJlYWQuY3N2KCJSSDIuY3N2IikNCmBgYA0KDQojIExpbXBpZXphLCB0cmFuc2Zvcm1hY2nDs24geSBvcmdhbml6YWNpw7NuIGRlIGJhc2VzIGRlIGRhdG9zICANCg0KUGFyYSBjcmVhciB1bmEgYmFzZSBkZSBkYXRvcyBmaW5hbCBwYXJhIGNhZGEgdW5hIGRlIGxhcyDDoXJlYXMgZGUgbGEgZW1wcmVzYSBGT1JNIGhheSBxdWUgY29uc2lkZXJhciBsbyBzaWd1aWVudGU6ICANCg0KMS4tIENhbWJpYXIgbm9tYnJlcyBkZSB2YXJpYWJsZXMuICAgIA0KMi4tIE1hbnRlbmVyIHZhcmlhYmxlcyByZWxldmFudGVzIHBhcmEgZWwgYW7DoWxpc2lzLiAgDQozLi0gSGF5IHF1ZSBsaW1waWFyIGxhcyBiYXNlcyBkZSBkYXRvcy4oTmEncywgdGlwbyBkZSB2YXJpYWJsZXMsIGVycm9yZXMgb3J0b2dyYWZpY29zLCBlbnRyZSBvdHJvcykuQWRlbcOhcyBkZSBob21vZ2VuZWl6YXIgdmFyaWFibGVzLiAgDQoNCiMjIENhbWJpYXIgbm9tYnJlcyBkZSB2YXJpYWJsZXMNCiMjIyBNZXJtYQ0KYGBge3J9DQptZXJtYTwtY2xlYW5fbmFtZXMobWVybWEpDQptZXJtYTwtZHBseXI6OnJlbmFtZShtZXJtYSwgZmVjaGE9aV9mZWNoYSkNCmNvbG5hbWVzKG1lcm1hKQ0KYGBgDQoNCiMjIyBTY3JhcA0KYGBge3J9DQpzY3JhcDwtY2xlYW5fbmFtZXMoc2NyYXApDQpzY3JhcDwtc2NyYXAgJT4lIGRwbHlyOjpyZW5hbWUocmVmZXJlbmNpYT1pX3JlZmVyZW5jaWEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdWJpX29yaWdlbj11YmljYWNpX2Ezbl9kZV9vcmlnZW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdWJpX2Rlc2VjaG89dWJpY2FjaV9hM25fZGVfZGVzZWNobykNCmNvbG5hbWVzKHNjcmFwKQ0KYGBgDQoNCiMjIyBQcm9kdWNjacOzbiBkZSBjYXJ0b24gIA0KDQpgYGB7cn0NCmNhcnRvbjwtY2xlYW5fbmFtZXMoY2FydG9uKQ0KY2FydG9uPC1jYXJ0b24gJT4lIGRwbHlyOjpyZW5hbWUobGFtX3Byb2Nlc2FkYXM9bGFtaW5hc19wcm9jZXNhZGFzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluX3NlcF91cD1maW5faW5pY2lvX2RlX3NlcF91cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluaWNpb19wcm9jZXNvPWluaWNpb19kZV9wcm9jZXNvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluX3Byb2Nlc289ZmluX2RlX3Byb2Nlc28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWNoYT1pX2ZlY2hhKQ0KY29sbmFtZXMoY2FydG9uKQ0KYGBgDQoNCiMjIyBEZWxpdmVyeSBQZXJmb3JtYW5jZQ0KYGBge3J9DQpwZXJmPC1jbGVhbl9uYW1lcyhwZXJmKQ0KY29sbmFtZXMocGVyZikNCmBgYA0KDQojIyMgIERlbGl2ZXJ5IFBsYW4NCmBgYHtyfQ0KcGxhbjwtY2xlYW5fbmFtZXMocGxhbikNCnBsYW48LXBsYW4gJT4lIGRwbHlyOjpyZW5hbWUoY2xpZW50ZT1pX2NsaWVudGVfcGxhbnRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganVuXzIxPWp1bmlvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganVsXzIxPWp1bGlvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdvXzIxPWFnb3N0bywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcF8yMT1zZXB0aWVtYnJlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2N0XzIxPW9jdHVicmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3ZfMjE9bm92aWVtYnJlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljXzIxPWRpY2llbWJyZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9jdF8yMj1vY3R1YnJlXzIyKQ0KY29sbmFtZXMocGxhbikNCmBgYA0KDQojIyMgQmFqYXMNCmBgYHtyfQ0KYmFqYXM8LWNsZWFuX25hbWVzKGJhamFzKQ0KYmFqYXM8LWJhamFzICU+JSBkcGx5cjo6cmVuYW1lKGFwZWxsaWRvcz1pX2FwZWxsaWRvcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlY2hhX25hYz1mZWNoYV9kZV9uYWNpbWllbnRvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVjaGFfYWx0YT1mZWNoYV9kZV9hbHRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW90X2JhamE9bW90aXZvX2RlX2JhamEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub19zcz1ub19zZWd1cm9fc29jaWFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsX2RpYXJpb19pbXNzPXNhbGFyaW9fZGlhcmlvX2ltc3MsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcmVkaXRvX2luZm9uYXZpdD1uYV9jcmVkaXRvX2luZm9uYXZpdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGx1Z2FyX25hYz1sdWdhcl9kZV9uYWNpbWllbnRvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2ludGVybm89bnVtZXJvX2ludGVybm8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDUD1jb2RpZ29fcG9zdGFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVybWFuZW5jaWE9eCkNCmNvbG5hbWVzKGJhamFzKQ0KYGBgDQoNCiMjIyBDb2xhYm9yYWRvcmVzDQpgYGB7cn0NCmNvbGFib3JhZG9yZXM8LWNsZWFuX25hbWVzKGNvbGFib3JhZG9yZXMpDQpjb2xhYm9yYWRvcmVzPC1jb2xhYm9yYWRvcmVzICU+JSBkcGx5cjo6cmVuYW1lKG5vX2VtcGxlYWRvPWlfbm9fZGVfZW1wbGVhZG8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWNoYV9uYWM9ZmVjaGFfZGVfbmFjaW1pZW50bywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlY2hhX2FsdGE9ZmVjaGFfZGVfYWx0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0dG9fbWVzIj14NHRvX21lcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vX3NzPW5vX3NlZ3Vyb19zb2NpYWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWxfZGlhcmlvX2ltc3M9c2FsYXJpb19kaWFyaW9faW1zcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNyZWRpdG9faW5mb25hdml0PW5hX2NyZWRpdG9faW5mb25hdml0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbHVnYXJfbmFjPWx1Z2FyX2RlX25hY2ltaWVudG8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1faW50ZXJubz1udW1lcm9faW50ZXJubywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENQPWNvZGlnb19wb3N0YWwpDQpjb2xuYW1lcyhiYWphcykNCmBgYA0KDQojIyBNYW50ZW5lciB2YXJpYWJsZXMgcmVsZXZhbnRlcyBwYXJhIGVsIGFuw6FsaXNpcy4gIA0KIyMjIE1lcm1hICANCkVuIGVsIGNhc28gZGUgbGEgYmFzZSBkZSBkYXRvcyBkZSBtZXJtYSBoZW1vcyBvcHRhZG8gcG9yIG1hbnRlbmVyIGxhcyB0cmVzIHZhcmlhYmxlcywgcHVlc3RvIHF1ZSBjb25zaWRlcmFtb3MgcXVlIHRvZGFzIHNvbiByZWxldmFudGVzIHBhcmEgZWwgYW7DoWxpc2lzIHF1ZSB2YW1vcyBhIHJlYWxpemFyLiAgDQoNCiMjIyBTY3JhcCAgDQpQYXJhIGxhIGJhc2UgZGUgZGF0b3MgZGUgc2NyYXAgaGVtb3Mgb3B0YWRvIHBvciBlbGltaW5hciBsYXMgdmFyaWFibGVzIGRlIHVuaWRhZCBkZSBtZWRpZGEsIGVzdGFkbyB5IHViaWNhY2nDs24gZGUgZGVzZWNobywgeWEgcXVlIHRvZG9zIGxvcyByZWdpc3Ryb3MgdGllbmVuIGVsIG1pc21vIHZhbG9yIGVuIGVzdGFzIHZhcmlhYmxlcywgZXMgZGVjaXIgdG9kb3MgdGllbmVuIGxhIG1pc21hIHVuaWRhZCwgbGEgbWlzbWEgdWJpY2FjacOzbiB5IGVsIG1pc21vIGVzdGFkby4NCg0KYGBge3J9DQpzY3JhcDwtZHBseXI6OnNlbGVjdChzY3JhcCwtYyh1YmlfZGVzZWNobywgZXN0YWRvLCB1bmlkYWRfZGVfbWVkaWRhKSkNCmNvbG5hbWVzKHNjcmFwKQ0KYGBgDQoNCiMjIyBQcm9kdWNjacOzbiBkZSBjYXJ0w7NuICANClBhcmEgcHJvZHVjY2nDs24gZGUgY2FydMOzbiBoZW1vcyBvcHRhZG8gcG9yIGVsaW1pbmFyIGxhcyBjb2x1bW5hcyBkZSBpZF9mb3JtLCBwcm9kdWN0bywgdGllbXBvX21hdGVyaWFsZXMgeSBlc3RhY2nDs24gZGUgYXJyYW5xdWUsIHlhIHF1ZSBjb25zaWRlcmFtb3MgcXVlIGNvbiBlbCByZXN0byBkZSBjb2x1bW5hcyBwb2RlbW9zIHJlYWxpemFyIG90cm9zIGNhbGN1bG9zLCBwcmluY2lwYWxtZW50ZSBoYWNlciBtZWRpY2lvbmVzIGRlIHRpZW1wby4NCg0KYGBge3J9DQpjYXJ0b248LWRwbHlyOjpzZWxlY3QoY2FydG9uLC1jKGlkX2Zvcm0sIHByb2R1Y3RvLCB0aWVtcG9fbWF0ZXJpYWxlcywgeCwgZXN0YWNpb25fYXJyYW5xdWUpKQ0KY29sbmFtZXMoY2FydG9uKQ0KYGBgDQoNCiMjIyBEZWxpdmVyeSBQZXJmb3JtYW5jZSAgDQpQYXJhIGRlbGl2ZXJ5IHBlcmZvcm1hbmNlIGhlbW9zIG9wdGFkbyBwb3IgbWFudGVuZXIgdG9kYXMgbGFzIGNvbHVtbmFzLg0KDQojIyMgRGVsaXZlcnkgUGxhbiAgDQpQYXJhIGRlbGl2ZXJ5IHBsYW4gcG9yIGZpbmVzIGRlIGVzdHJ1Y3R1cmEgeSBhbsOhbGlzaXMgaGVtb3Mgb3B0YWRvIHBvciB0cmFuc2Zvcm1hciBsYSBiYXNlIGRlIGRhdG9zIHkgbGFzIGNvbHVtbmFzIGRlIGNhZGEgbWVzIGNvbnZlcnRpcmxhcyBlbiBkYXRvcyBkZSB1bmEgc29sYSBjb2x1bW5hIGRlbm9taW5hZGEgY29tbyBNZXMgeSBsb3MgdmFsb3JlcyBwYXNhcmxvcyBhIG90cmEgY29sdW1uYSBsbGFtYWRhIFVuaWRhZGVzLg0KYGBge3J9DQpwbGFuPC1waXZvdF9sb25nZXIocGxhbiwgY29scz01OjI2LCBuYW1lc190byA9ICJtZXMiLCB2YWx1ZXNfdG8gPSAidW5pZGFkZXMiKQ0KYGBgDQoNCllhIHF1ZSBjb250YW1vcyBjb24gbGEgYmFzZSBkZSBkYXRvcyB0cmFuc2Zvcm1hZGEgaGVtb3Mgb3B0YWRvIHBvciBlbGltaW5hciBsYSBjb2x1bW5hIGRlIHRvdGFsIG1lc2VzIHlhIHF1ZSBwb2RlbW9zIGNhbGN1bGFyIGVsIHRvdGFsIGRlIHRvZG9zIGxvcyBtZXNlcyBwb3IgcHJvZHVjdG9zIHBvc3Rlcmlvcm1lbnRlLCB5IGxhIGRlIGlkX29kb28uDQpgYGB7cn0NCnBsYW48LWRwbHlyOjpzZWxlY3QocGxhbiwtYyh0b3RhbF9tZXNlcyxpZF9vZG9vKSkNCmBgYA0KDQojIyMgQmFqYXMgIA0KUGFyYSBiYWphcyBoZW1vcyBvcHRhZG8gcG9yIGhhY2VyIHVuYSB0cmFuc2Zvcm1hY2nDs24gYSBsYSBiYXNlIGRlIGRhdG9zIHkgdW5pciBsb3MgYXBlbGxpZG9zIGNvbiBlbCBub21icmUsIGFkZW3DoXMgZGUgZWxpbWluYXIgbGFzIGNvbHVtbmFzIGRlIFJGQywgbsO6bWVybyBkZSBzZWd1cm8gc29jaWFsLCBmYWN0b3IgZGUgY3LDqWRpdG8gaW5mb25hdml0LCBjcsOpZGl0byBpbmZvbmF2aXQsIENVUlAsIGx1Z2FyIGRlIG5hY2ltaWVudG8sIGNhbGxlLCBudW1lcm8gaW50ZXJubywgeSB0YXJqZXRhIGRlIGN1ZW50YS4NCmBgYHtyfQ0KDQpiYWphczwtbXV0YXRlKGJhamFzLG5vbWJyZV9jb21wbGV0byA9IHBhc3RlKGJhamFzJG5vbWJyZSwgYmFqYXMkYXBlbGxpZG9zLCBzZXA9IiAiKSkNCmJhamFzPC1kcGx5cjo6c2VsZWN0KGJhamFzLC1jKHJmYywgbm9fc3MsZmFjdG9yX2NyZWRfaW5mb25hdml0LGNyZWRpdG9faW5mb25hdml0LGN1cnAsdGFyamV0YV9jdWVudGEsYXBlbGxpZG9zLG5vbWJyZSxsdWdhcl9uYWMsY2FsbGUsbnVtX2ludGVybm8pKQ0KYmFqYXM8LWJhamFzWyxjKDE1LDEsMiwzLDQsNSw2LDcsOCw5LDEwLDExLDEyLDEzLDE0KV0NCmNvbG5hbWVzKGJhamFzKQ0KYGBgDQoNCiMjIyBDb2xhYm9yYWRvcmVzICANClBhcmEgY29sYWJvcmFkb3RlcyB0YW1iacOpbiBvcHRhbW9zIHBvciB1bmlyIGFwZWxsaWRvcyBjb24gZWwgbm9tYnJlLCBhZGVtw6FzIGRlIGVsaW1pbmFyIGxhcyBjb2x1bW5hcyBkZSBSRkMsIG7Dum1lcm8gZGUgc2VndXJvIHNvY2lhbCwgZmFjdG9yIGRlIGNyw6lkaXRvIGluZm9uYXZpdCwgY3LDqWRpdG8gaW5mb25hdml0LCBDVVJQLCBsdWdhciBkZSBuYWNpbWllbnRvLCBjYWxsZSwgbnVtZXJvIGludGVybm8sIHkgdGFyamV0YSBkZSBjdWVudGEuDQpgYGB7cn0NCmNvbGFib3JhZG9yZXM8LW11dGF0ZShjb2xhYm9yYWRvcmVzLG5vbWJyZV9jb21wbGV0byA9IHBhc3RlKGNvbGFib3JhZG9yZXMkbm9tYnJlLCBjb2xhYm9yYWRvcmVzJGFwZWxsaWRvcywgc2VwPSIgIikpDQpjb2xhYm9yYWRvcmVzPC1kcGx5cjo6c2VsZWN0KGNvbGFib3JhZG9yZXMsLWMocmZjLCBub19zcyxmYWN0b3JfY3JlZF9pbmZvbmF2aXQsY3JlZGl0b19pbmZvbmF2aXQsY3VycCx0YXJqZXRhX2N1ZW50YSxhcGVsbGlkb3Msbm9tYnJlLGJhamEsY2FsbGUsbnVtX2ludGVybm8sbHVnYXJfbmFjLCI0dG9fbWVzIixwcmltZXJfbWVzLG5vX2VtcGxlYWRvKSkNCmNvbGFib3JhZG9yZXM8LWNvbGFib3JhZG9yZXNbLGMoMTIsMSwyLDMsNCw1LDYsNyw4LDksMTAsMTEpXQ0KYGBgDQoNCiMjIExpbXBpZXphICANCkVsIHNpZ3VpZW50ZSBwYXNvIGEgcmVhbGl6YXIgZXMgbGEgbGltcGllemEgZGUgbGFzIGJhc2VzIGRlIGRhdG9zIChOYSdzLCB0aXBvIGRlIHZhcmlhYmxlcywgZXJyb3JlcyBvcnRvZ3JhZmljb3MsIGVudHJlIG90cm9zKS4NCg0KIyMjIE1lcm1hICANClBhcmEgbWVybWEgZWxpbWluYXJlbW9zIGxhcyBmaWxhcyBxdWUgY29udGllbmVuIGVsIHRvdGFsIGRlIGxvcyBtZXNlcy4NCmBgYHtyfQ0KbWVybWE8LW1lcm1hWy0gZ3JlcCgiVG90YWwiLCBtZXJtYSRtZXMpLF0NCmBgYA0KDQpBc8OtIG1pc21vLCBoYXkgcXVlIGNvcnJvYm9yYXIgcXVlIGxhcyBjb2x1bW5hcyBlc3TDqW4gZW4gZWwgZm9ybWF0byBjb3JyZWN0by4NCmBgYHtyfQ0Kc3RyKG1lcm1hKQ0KYGBgDQpBbCBvYnNlcnZhciBlbCB0aXBvIGRlIHZhcmlhYmxlLCBkZWJlbW9zIGRlIGNhbWJpYXIgZmVjaGEgYSBmb3JtYXRvIGRlIGZlY2hhIHkga2lsb3MgY29tbyBlbnRlcm8uDQpgYGB7cn0NCm1lcm1hJGtpbG9zPC1hcy5pbnRlZ2VyKG1lcm1hJGtpbG9zKQ0KbWVybWEkZmVjaGE8LWFzLlBPU0lYY3QobWVybWEkZmVjaGEsIGZvcm1hdCA9IiVkLyVtLyVZIikNCnN0cihtZXJtYSkNCmBgYA0KDQojIyMgU2NyYXAgIA0KUGFyYSBzY3JhcCBlbGltaW5hbW9zIGxhIHByaW1lciBmaWxhIHF1ZSBkaWNlIGFnb3N0by4NCmBgYHtyfQ0Kc2NyYXA8LXNjcmFwWy0gZ3JlcCgiYWdvc3RvIiwgc2NyYXAkcmVmZXJlbmNpYSksXQ0KaGVhZChzY3JhcCw1KQ0KYGBgDQoNCkFzw60gbWlzbW8sIGhheSBxdWUgY29ycm9ib3JhciBxdWUgbGFzIGNvbHVtbmFzIGVzdMOpbiBlbiBlbCBmb3JtYXRvIGNvcnJlY3RvLg0KYGBge3J9DQpzdHIoc2NyYXApDQpgYGANCg0KQWwgb2JzZXJ2YXIgZWwgdGlwbyBkZSB2YXJpYWJsZSwgZGViZW1vcyBkZSBjYW1iaWFyIGNhbnRpZGFkIGEgZW50ZXJvIHkgZmVjaGEgY29uIGZvcm1hdG8gZGUgZmVjaGEuDQpgYGB7cn0NCnNjcmFwJGNhbnRpZGFkPC1hcy5pbnRlZ2VyKHNjcmFwJGNhbnRpZGFkKQ0Kc2NyYXAkZmVjaGE8LWFzLlBPU0lYY3Qoc2NyYXAkZmVjaGEsIGZvcm1hdCA9IiVkLyVtLyVZIikNCnN0cihzY3JhcCkNCmBgYA0KDQojIyMgUHJvZHVjY2nDs24gZGUgY2FydMOzbiAgDQpQcmltZXJvIGhheSBxdWUgY29ycm9ib3JhciBxdWUgbGFzIGNvbHVtbmFzIGVzdMOpbiBlbiBlbCBmb3JtYXRvIGNvcnJlY3RvLg0KYGBge3J9DQpzdHIoY2FydG9uKQ0KYGBgDQoNCkFsIG9ic2VydmFyIGVsIHRpcG8gZGUgdmFyaWFibGUsIGRlYmVtb3MgZGUgY2FtYmlhciBsYSBmZWNoYSBjb24gZm9ybWF0byBkZSBmZWNoYSwgcGllemFzIHByb2dyYW1hZGFzIHkgbGFtaW5hcyBwcm9jZXNhZGFzIGEgZW50ZXJvLCB5IGxhcyBjb2x1bW5hcyBxdWUgc29uIGRlIHRpZW1wbyBhbCBmb3JtYXRvIGRlIGhvcmEgdGFtYmnDqW4uDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2FydG9uJHBpZXphc19wcm9nIDwtYXMuaW50ZWdlcihjYXJ0b24kcGllemFzX3Byb2cpDQpjYXJ0b24kdG1vX21pbiA8LWFzLmludGVnZXIoY2FydG9uJHRtb19taW4pDQoNCmNhcnRvbiRocl9maW48LWFzLlBPU0lYY3QoY2FydG9uJGhyX2Zpbixmb3JtYXQ9IiVIOiVNIikNCmNhcnRvbiRocl9maW4gPC0gYXMuSVRpbWUoY2FydG9uJGhyX2ZpbikNCg0KY2FydG9uJGluaWNpb19zZXBfdXA8LWFzLlBPU0lYY3QoY2FydG9uJGluaWNpb19zZXBfdXAsZm9ybWF0PSIlSDolTSIpDQpjYXJ0b24kaW5pY2lvX3NlcF91cCA8LSBhcy5JVGltZShjYXJ0b24kaW5pY2lvX3NlcF91cCkNCg0KY2FydG9uJGZpbl9zZXBfdXA8LWFzLlBPU0lYY3QoY2FydG9uJGZpbl9zZXBfdXAsZm9ybWF0PSIlSDolTSIpDQpjYXJ0b24kZmluX3NlcF91cCA8LSBhcy5JVGltZShjYXJ0b24kZmluX3NlcF91cCkNCg0KY2FydG9uJGluaWNpb19wcm9jZXNvPC1hcy5QT1NJWGN0KGNhcnRvbiRpbmljaW9fcHJvY2Vzbyxmb3JtYXQ9IiVIOiVNIikNCmNhcnRvbiRpbmljaW9fcHJvY2VzbyA8LSBhcy5JVGltZShjYXJ0b24kaW5pY2lvX3Byb2Nlc28pDQoNCmNhcnRvbiRmaW5fcHJvY2VzbzwtYXMuUE9TSVhjdChjYXJ0b24kZmluX3Byb2Nlc28sZm9ybWF0PSIlSDolTSIpDQpjYXJ0b24kZmluX3Byb2Nlc28gPC0gYXMuSVRpbWUoY2FydG9uJGZpbl9wcm9jZXNvKQ0KDQpzdHIoY2FydG9uKQ0KYGBgDQoNCkVuIGVzdGEgYmFzZSBkZSBkYXRvcyBoYXkgb3Ryb3MgZWxlbWVudG9zIGEgY29uc2lkZXJhciBwb3IgZXN0byByZXZpc2FyZW1vcyBjYWRhIGNvbHVtbmEgeSBsb3MgcmVnaXN0cm9zIHF1ZSBsYSBjb25mb3JtYW4uICANCg0KKipGZWNoYSoqICANCkNvbiBsYSBmZWNoYSBwb2RlbW9zIG9ic2VydmFyIHF1ZSBubyBoYXkgTkEncywgYWRlbcOhcyBxdWUgbnVlc3Ryb3MgcmVnaXN0cm9zIHNvbiA1OSBmZWNoYXMsIHF1ZSBhYmFyY2FuIGRlIG1lZGlhZG9zIGRlIGp1bGlvIGhhc3RhIG1lZGlhZG9zIGRlIHNlcHRpZW1icmUuIFkgaGF5IHF1ZSBjYW1iYWlyIGVsIGZvcm1hdG8gYSBmb3JtYXRvIGRlIGZlY2hhLg0KYGBge3J9DQpjYXJ0b24kZmVjaGEgPC0gYXMuRGF0ZShjYXJ0b24kZmVjaGEsIGZvcm1hdCA9IiVkLyVtLyV5IikNCnN0cihjYXJ0b24kZmVjaGEpDQp1bmlxdWUoY2FydG9uJGZlY2hhKQ0KYGBgDQoNCioqQ2xpZW50ZSoqICANCkNvbiBsb3MgY2xpZW50ZXMgcG9kZW1vcyBvYnNlcnZhciBxdWUgaGF5IGNsaWVudGVzIGNvbiBOQSwgb3Ryb3MgY29uIGVzcGFjaW9zIHZhY8Otb3MsIHkgb3Ryb3MgcXVlIHNvbiBlbCBtaXNtbyBwZXJvIGNvbiBlcnJvciBvcnRvZ3LDoWZpY28sIGNvbW8gZXMgZWwgY2FzbyBkZSBTVEFCSUxVUyAzIHkgU1RBQklMVVMgMy4gRWwgZm9ybWF0byBkZSBjYXJhY3RlciBlc3TDoSBjb3JyZWN0byBwb3IgbG8gcXVlIG5vIGhheSBxdWUgaGFjZXIgbW9kaWZpY2FjaW9uZXMgZW4gZXNvLg0KYGBge3J9DQp1bmlxdWUoY2FydG9uJGNsaWVudGUpDQpgYGANCg0KUG9yIGxvIHRhbnRvLCBoYXkgcXVlIGVsaW1pbmFyIGxvcyByZWdpc3Ryb3MgY29uIGNsaWVudGVzIGNvbiBOQSB5IGNvbiBlc3BhY2lvcyBlbiBibGFuY28sIGFkZW3DoXMgZGUgY2FtYmlhciBhcXVlbGxvcyBxdWUgdGllbmVuIGRlIGNsaWVudGUgU1RBQklMVVMgMy4gYSBTVEFCSUxVUyAzLg0KDQpgYGB7cn0NCmNhcnRvbiA8LSBmaWx0ZXIoY2FydG9uLCAhaXMubmEoY2FydG9uJGNsaWVudGUpKQ0KY2FydG9uIDwtIGZpbHRlcihjYXJ0b24sY2xpZW50ZSE9IiIpDQpjYXJ0b24kY2xpZW50ZSA8LSByZXBsYWNlKGNhcnRvbiRjbGllbnRlLGNhcnRvbiRjbGllbnRlPT0iU1RBQklMVVMgMy4iLCJTVEFCSUxVUyAzIikNCnVuaXF1ZShjYXJ0b24kY2xpZW50ZSkNCmBgYA0KDQoqKkluaWNpbyBkZSBwcm9jZXNvIHkgZmluIGRlIHByb2Nlc28qKiAgDQpQcmV2aW8gYSBhbmFsaXphciBlbCByZXN0byBkZSBsYXMgY29sdW1uYXMgcHJpbWVybyBoYWJyZW1vcyBkZSBlbGltaW5hciBsb3MgcmVnaXN0cm9zIHF1ZSBlc3TDqW4gY29tbyBOQSdzIGVuIGFsZ3VuYSBkZSBlc3RhcyBkb3MgY29sdW1uYXMsIHB1ZXN0byBxdWUgZXNvcyByZWdpc3Ryb3Mgbm8gc2lydmVuIHBhcmEgZWwgYW7DoWxpc2lzIG3DoXMgYWRlbGFudGUuIEFzw60gbWlzbW8sIGhheSBxdWUgZWxpbWluYXIgbG9zIHJlZ2lzdHJvcyBxdWUgZXN0ZW4gY29tbyAwMDowMCB0YW1iacOpbi4NCmBgYHtyfQ0KY2FydG9uIDwtIGZpbHRlcihjYXJ0b24sICFpcy5uYShjYXJ0b24kaW5pY2lvX3Byb2Nlc28pICYgIWlzLm5hKGNhcnRvbiRmaW5fcHJvY2VzbykpDQpjYXJ0b24gPC0gZmlsdGVyKGNhcnRvbiwhaW5pY2lvX3Byb2Nlc289PTAwOjAwOjAwICYgIWZpbl9wcm9jZXNvPT0wMDowMDowMCkNCnN1bShpcy5uYShjYXJ0b24kaW5pY2lvX3Byb2Nlc28pKQ0Kc3VtKGlzLm5hKGNhcnRvbiRmaW5fcHJvY2VzbykpDQpgYGANCg0KKipJbmljaW8gZGUgc2VwIHVwIHkgZmluIGRlIHNlcCB1cCoqICANClBhcmEgZXN0YXMgY29sdW1uYXMgdGFtYmnDqW4gZWxpbWluYXJlbW9zIGxvcyByZWdpc3Ryb3MgY29uIE5BIG8gY29uIDAwOjAwIGNvbW8gdGllbXBvLiAgDQpgYGB7cn0NCmNhcnRvbiA8LSBmaWx0ZXIoY2FydG9uLCAhaXMubmEoY2FydG9uJGluaWNpb19zZXBfdXApICYgIWlzLm5hKGNhcnRvbiRmaW5fc2VwX3VwKSkNCmNhcnRvbiA8LSBmaWx0ZXIoY2FydG9uLCFpbmljaW9fc2VwX3VwPT0wMDowMDowMCAmICFmaW5fc2VwX3VwPT0wMDowMDowMCkNCnN1bShpcy5uYShjYXJ0b24kaW5pY2lvX3NlcF91cCkpDQpzdW0oaXMubmEoY2FydG9uJGZpbl9zZXBfdXApKQ0KYGBgDQoNCioqUGllemFzIFByb2dyYW1hZGFzKiogIA0KSGF5IHF1ZSBlbGltaW5hciBsb3MgcmVnaXN0cm9zIGNvbiBOQSBkZSBpZ3VhbCBtYW5lcmEuIEVsIGZvcm1hdG8gZGViZSBzZXIgZGUgZW50ZXJvLCBwb3IgbG8gcXVlIGhheSBxdWUgaGFjZXIgZXN0ZSBjYW1iaW8uDQpgYGB7cn0NCmNhcnRvbiA8LSBmaWx0ZXIoY2FydG9uLCAhaXMubmEoY2FydG9uJHBpZXphc19wcm9nKSkNCmNhcnRvbiRwaWV6YXNfcHJvZyA8LSBhcy5pbnRlZ2VyKGNhcnRvbiRwaWV6YXNfcHJvZykNCnN1bShpcy5uYShjYXJ0b24kcGllemFzX3Byb2cpKQ0KYGBgDQoNCioqTGFtaW5hcyBwcm9jZXNhZGFzKiogIA0KUGFyYSBsYW1pbmFzIHByb2Nlc2FkYXMgc2kgYmllbiBubyB0aWVuZSBOQSBzdXMgcmVnaXN0cm9zIG5vIHNvbiBjb25zaXN0ZW50ZXMgeSB1bmlmb3JtZXMgZW4gc3UgZm9ybWF0bywgYWxndW5vcyBzb24gbnVtZXJvcyBlbnRlcm9zLCBvdHJvcyBlc3RhbiBkaXZpZGlkb3MgcG9yIHVuICIvIiwgb3Ryb3Mgc29uIGFsZmFuw7ptZXJpY29zLCB5IG90cm9zIHNvbiAwIG8gcmVnaXN0cm9zIHZhY8Otb3MuIEVsaW1pbmFyZW1vcyBhcXVlbGxvcyBtZW5vcmVzIGEgMSwgbG9zIHZhY2nDs3MgeSBtYW50ZW5kcmVtb3MgZWwgcHJpbWVyIG7Dum1lcm8gYW50ZXMgZGVsICIvIiwgeSBsb3MgcmVnaXN0cm9zIGFsZmFuw7ptZXJpY29zIGFxdWVsbG9zIHF1ZSBzb2xvIHRpZW5lbiB1bmEgbGV0cmEsIGVsaW1pbmFyZW1vcyBsYSBsZXRyYSwgcGVybyBhcXVlbGxvcyBxdWUgY29udGVuZ2FuIHNpZ25vcyBjb21vICI9IiwgbyBtw6FzIGNhcmFjdGVyZXMgc2Vyw6FuIGVsaW1pbmFkb3MuIFRhbWJpw6luIGVsIGZvcm1hdG8gZGViZSBzZXIgZGUgZW50ZXJvIHBvciBsbyBxdWUgaGF5IHF1ZSBoYWNlciBlc3RhIG1vZGlmaWNhY2nDs24uDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2FydG9uIDwtIGNhcnRvbiAlPiUgc2VwYXJhdGUobGFtX3Byb2Nlc2FkYXMsIGMoImxhbV9wcm9jZXNhZGFzIiwgImVsaW1pbmFyIiksIHNlcD0iLyIpDQpjYXJ0b24gPC0gZHBseXI6OnNlbGVjdChjYXJ0b24sLWVsaW1pbmFyKQ0KY2FydG9uIDwtIGNhcnRvblstIGdyZXAoIj0iLCBjYXJ0b24kbGFtX3Byb2Nlc2FkYXMpLF0NCmNhcnRvbiA8LSBjYXJ0b25bLSBncmVwKCJBIDQgQjQiLCBjYXJ0b24kbGFtX3Byb2Nlc2FkYXMpLF0NCmNhcnRvbiRsYW1fcHJvY2VzYWRhcyA8LSBzdHJfcmVwbGFjZShjYXJ0b24kbGFtX3Byb2Nlc2FkYXMsICJbYWVpb3VMQU09TmJCc1NdIiwgIiIpDQpjYXJ0b24kbGFtX3Byb2Nlc2FkYXMgPC0gYXMuaW50ZWdlcihjYXJ0b24kbGFtX3Byb2Nlc2FkYXMpDQpjYXJ0b24gPC0gZmlsdGVyKGNhcnRvbixjYXJ0b24kbGFtX3Byb2Nlc2FkYXM+MCkNCnN1bShpcy5uYShjYXJ0b24kbGFtX3Byb2Nlc2FkYXMpKQ0KdW5pcXVlKGNhcnRvbiRsYW1fcHJvY2VzYWRhcykNCmBgYA0KDQoqKlRtb19taW4geSBocl9maW5hbCoqICANCkVzdGFzIGNvbHVtbmFzIHBlcm1hbmVjZXJhbiBpZ3VhbCBzaW4gYWp1c3RlcyBlbiBlc3BlY8OtZmljby4gw5puaWNhbWVudGUgZWwgZm9ybWF0byBkZSB0bW9fbWluIGxvIGRlamFyZW1vcyBlbiBlbnRlcm8uICANCmBgYHtyfQ0KY2FydG9uJHRtb19taW4gPC0gYXMuaW50ZWdlcihjYXJ0b24kdG1vX21pbikNCmBgYA0KDQoqKlRpZW1wbyBkZSBjYWxpZGFkKiogIA0KUGFyYSB0aWVtcG8gZGUgY2FsaWRhZCBwb2RlbW9zIG9ic2VydmFyIHF1ZSBoYXkgcmVnaXN0cm9zIHF1ZSBubyBzb24gZW50ZXJvcywgb3RvcnMgcXVlIHRpZW5lbiBzZWd1bmRvcyB5IG90cm9zIHF1ZSBlc3TDoW4gdmFjw61vcy4gTG9zIHF1ZSBubyBzb24gZW50ZXJvcyBsb3MgZGVqYXJlbW9zIHZhY8Otb3MsIGxvcyB2YWPDrW9zIHZhbiBhIHBlcm1hbmVjZXIgeSBsb3MgcXVlIHRpZW5lbiBzZWd1bmRvcyB2YW4gYSBzZXIgcmVkb25kZWFkb3MgYWwgZW50ZXJvIG3DoXMgY2VyY2Fuby4gIA0KYGBge3J9DQpjYXJ0b24kdGllbXBvX2NhbGlkYWQgPC0gc3RyX3JlcGxhY2UoY2FydG9uJHRpZW1wb19jYWxpZGFkLCAiOTo0NyIsICI5IikNCmNhcnRvbiA8LSBzdWJzZXQoY2FydG9uLCB0aWVtcG9fY2FsaWRhZCE9InwiKQ0KY2FydG9uJHRpZW1wb19jYWxpZGFkIDwtIGFzLmludGVnZXIoY2FydG9uJHRpZW1wb19jYWxpZGFkKQ0KdW5pcXVlKGNhcnRvbiR0aWVtcG9fY2FsaWRhZCkNCmBgYA0KDQojIyMgRGVsaXZlcnkgUGVyZm9ybWFuY2UgIA0KUGFyYSBEZWxpdmVyeSBQZXJmb3JtYW5jZSBoYXkgcXVlIHZlcmlmaWNhciBxdWUgbGFzIGNvbHVtbmFzIGVzdMOpbiBlbiBlbCBmb3JtYXRvIGNvcnJlY3RvIGFkZW3DoXMgZGUgaWRlbnRpZmljYXIgdmFsb3JlcyBjb21vIE5hLCB2YWxvcmVzIGF0w61waWNvcywgZW50cmUgb3Ryb3MuDQoNCkRlIGlndWFsIG1hbmVyYSBpcmVtb3MgcmV2aXNhbmRvIGNvbHVtbmEgcG9yIGNvbHVtbmEuDQoNCioqQ2xpZW50ZSoqICANCkNvbW8gcG9kZW1vcyBvYnNlcnZhciBlbiBjbGllbnRlLCBzb2xvIHNvbiA0IGNsaWVudGVzLCBubyBoYXkgZXJyb3JlcyBvcnRvZ3LDoWZpY29zIHkgZWwgZm9ybWF0byBkZSBsYSBjb2x1bW5hIGVzIGVsIGNvcnJlY3RvLg0KYGBge3J9DQp1bmlxdWUocGVyZiRjbGllbnRlKQ0Kc3RyKHBlcmYkY2xpZW50ZSkNCnN1bShpcy5uYShwZXJmJGNsaWVudGUpKQ0KYGBgDQoNCioqVnVlbHRhcyoqICANCkRlIGlndWFsIG1hbmVyYSBlbiB2dWVsdGFzLCBzb2xvIGhheSBoYXN0YSAzIHZ1ZWx0YXMsIHkgbm8gaGF5IGVycm9yZXMgb3J0b2dyw6FmaWNvcyBvIHZhbG9yZXMgYXTDrXBpY29zLg0KYGBge3J9DQp1bmlxdWUocGVyZiR2dWVsdGFzKQ0Kc3RyKHBlcmYkdnVlbHRhcykNCnN1bShpcy5uYShwZXJmJHZ1ZWx0YXMpKQ0KYGBgDQoqKkZlY2hhKiogIA0KTGEgY29sdW1uYSBkZSBmZWNoYSBlc3TDoSBjb21vIGNhcmFjdGVyLCBwb3IgbG8gcXVlIGhheSBxdWUgaGFjZXIgZWwgY2FtYmlvIGRlIGZvcm1hdG8gYSBmZWNoYS4gQXPDrSBtaXNtbywgdGFtcG9jbyBoYXkgdmFsb3JlcyBmYWx0YW50ZXMuDQpgYGB7cn0NCnBlcmYkZmVjaGEgPC1hcy5EYXRlKHBlcmYkZmVjaGEsIGZvcm1hdCA9IiVkLyVtLyVZIikNCnN0cihwZXJmJGZlY2hhKQ0Kc3VtKGlzLm5hKHBlcmYkZmVjaGEpKQ0KYGBgDQoNCioqUmVhbCBBcnJpdmFsIHkgUmVhbCBEZXBhcnR1cmUqKiAgDQpQYXJhIGVzdGFzIGNvbHVtbmFzIHBvZGVtb3MgZW5jb250cmFyIHF1ZSBoYXkgdmFsb3JlcyBjb24gMC4gUG9yIGxvIHRhbnRvIHZhbW9zIGEgZWxpbWluYXIgbG9zIHJlZ2lzdHJvcyBxdWUgdGVuZ2FuIDAgZW4gZXN0YXMgY29sdW1uYXMuDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdW5pcXVlKHBlcmYkcmVhbF9hcnJpdmFsKQ0KdW5pcXVlKHBlcmYkcmVhbF9kZXBhcnR1cmUpDQpwZXJmIDwtIGZpbHRlcihwZXJmLCBwZXJmJHJlYWxfYXJyaXZhbD4wICYgcGVyZiRyZWFsX2RlcGFydHVyZT4wKQ0KdW5pcXVlKHBlcmYkcmVhbF9hcnJpdmFsKQ0KdW5pcXVlKHBlcmYkcmVhbF9kZXBhcnR1cmUpDQpgYGANCg0KQWwgaGFjZXJsbywgbG9zIHJlZ2lzdHJvcyBkZSBsb3MgY2xpZW50ZXMgZGUgVkFSUk9DIHkgTUFHTkEgc2UgZWxpbWluYXJvbiBkZSBsYSBiYXNlIGRlIGRhdG9zLiAgDQoNCioqUGxhbiBBcnJpdmFsKiogDQpQYXJhIFBsYW4gQXJyaXZhbCBlcyBwb3NpYmxlIG9ic2VydmFyIHF1ZSBoYXkgcmVnaXN0cm9zIGNvbiAwLCBlc3RvcyBzZXLDoW4gcmVtcGxhemFkb3MgY29uIGVsIHByb21lZGlvIG8gbGEgbWVkaWFuYSBkZSBwbGFuIGFycml2YWwgcG9yIGNsaWVudGUgcG9yIHZ1ZWx0YSwgZXN0byBjb24gZWwgcHJvcMOzc2l0byBxdWUgc2VhIGxvIG3DoXMgcHJlY2lzbyBwb3NpYmxlLg0KYGBge3J9DQp1bmlxdWUocGVyZiRwbGFuX2Fycml2YWwpDQpzdW0oaXMubmEocGVyZiRwbGFuX2Fycml2YWwpKQ0KYGBgDQoNCkFsIG9ic2VydmFyIGVsIHZhbG9yIGRlIHNrZXduZXNzLCBwb2RlbW9zIG9ic2VydmFyIHF1ZSBlbCB2YWxvciBlcyBtZW5vciBhIDEgcG9yIGxvIHF1ZSBzdSBkaXN0cmlidWNpw7NuIG5vcyBzdWdpZXJlIHF1ZSBlcyBtZWpvciByZW1wbGF6YXIgY29uIGVsIHByb21lZGlvLiBBZGVtw6FzIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGxhIG1lZGlhbmEgZXMgMCBkZWJpZG8gYSBxdWUgbXVjaG9zIHZhbG9yZXMgdGllbmVuIDAgY29tbyBwbGFuIGFycml2YWwuDQpgYGB7cn0NCmRlc2NyaWJlKHBlcmYkcGxhbl9hcnJpdmFsKQ0KYGBgDQoNClBhcmEgcmVtcGxhemFyIDAgY29uIGVsIHByb21lZGlvIHZhbW9zIGEgY2FsY3VsYXIgZWwgcHJvbWVkaW8gcG9yIGNsaWVudGUgcG9yIHZ1ZWx0YS4NCmBgYHtyfQ0KYWdncmVnYXRlKHBsYW5fYXJyaXZhbCB+IGNsaWVudGUgKyB2dWVsdGFzLCBkYXRhID0gcGVyZiwgbWVhbikNCmBgYA0KDQpDb21vIHF1ZXJlbW9zIHJlbXBsYXphciBlbCAwIHVuaWNhbWVudGUgZW4gZWwgY2xpZW50ZSBNQUhMRSB5IGVuIGxhIHZ1ZWx0YSAzIGRlYmVtb3MgaGFjZXIgbG8gc2lndWllbnRlLg0KYGBge3J9DQpwZXJmIDwtIHBlcmYgJT4lIG11dGF0ZShwbGFuX2Fycml2YWw9aWZlbHNlKHBsYW5fYXJyaXZhbCA9PSAwIHwgY2xpZW50ZSA9PSAiTUFITEUifCB2dWVsdGFzID09IDMsMjAscGxhbl9hcnJpdmFsKSkNCnVuaXF1ZShwZXJmJHBsYW5fYXJyaXZhbCkNCmBgYA0KKipEaWZmZXJlbmNlKiogIA0KRXN0YSBjb2x1bW5hIHNlIHN1cG9uZSBlcyBsYSBkaWZlcmVuY2lhIHF1ZSBoYXkgZW50cmUgcmVhbCBhcnJpdmFsIHkgcmVhbCBkZXBhcnR1cmUsIHNpbiBlbWJhcmdvLCBwb2RlbW9zIG9ic2VydmFyIHF1ZSBubyB0b2RvcyBsb3MgcmVnaXN0cm9zIHRpZW5lbiBlbCBjYWxjdWxvIGNvcnJlY3RvLCBlIGluY2x1c2l2ZSBhbGd1bmFzIG5pIHNpIHF1aWVyYSB0aWVuZW4gZWwgY2FsY3VsbyBwb3IgbG8gcXVlIGVzIG5lY2VzYXJpbyBtb2RpZmljYXIgZXN0YSBjb2x1bW5hLg0KYGBge3J9DQpwZXJmIDwtIG11dGF0ZShwZXJmLCBkaWZmZXJlbmNlPXJlYWxfZGVwYXJ0dXJlLXJlYWxfYXJyaXZhbCkNCnBlcmYgPC0gZHBseXI6OnNlbGVjdChwZXJmLC1kaWZlcmVuY2UpDQpoZWFkKHBlcmYsNSkNCmBgYA0KDQojIyMgRGVsaXZlcnkgUGxhbiAgDQpQYXJhIERlbGl2ZXJ5IFBsYW4gdGFtYmnDqW4gaXJlbW9zIHJldmlzYW5kbyBjb2x1bW5hIHBvciBjb2x1bW5hLiAgDQoNCioqQ2xpZW50ZSBQbGFudGEqKiAgDQpQYXJhIENsaWVudGUgcG9kZW1vcyBvYnNlcnZhciBxdWUgbm8gaGF5IGVycm9yZXMgb3J0b2dyw6FmaWNvcyBwb3IgbG8gcXVlIG5vIGhhcmVtb3MgY2FtYmlvcyBlbiBlc3RhIGNvbHVtbmEuICANCmBgYHtyfQ0KdW5pcXVlKHBsYW4kY2xpZW50ZSkNCmBgYA0KKipQcm95ZWN0byoqICANCkRlIGlndWFsIG1hbmVyYSBwYXJhIHByb3llY3RvIG5vIGhhcmVtb3MgY2FtYmlvcyBhIGxvcyByZWdpc3Ryb3MsIHB1ZXMgbm8gZW5jb250cmFtb3MgZXJyb3JlcyBkZSBpbmNvbnNpc3RlbmNpYS4gIA0KYGBge3IgcmVzdWx0cz0naGlkZSd9DQp1bmlxdWUocGxhbiRwcm95ZWN0bykNCmBgYA0KDQoqKkl0ZW0qKiAgDQpEZSBpZ2F1bCBtYW5lcmEgY29uIGl0ZW0gbm8gaGFyZW1vcyBtb2RpZmljYWNpb25lcy4gIA0KYGBge3IgcmVzdWx0cz0naGlkZSd9DQp1bmlxdWUocGxhbiRpdGVtKQ0KYGBgDQoNCioqTWVzKiogIA0KUGFyYSBsYSBjb2x1bW5hIGRlIG1lcyBubyBlbmNvbnRyYW1vcyBlcnJvcmVzIG9ydG9ncsOhZmljb3MgcG9yIGxvIHF1ZSBubyBoYXJlbW9zIG1vZGlmaWNhY2lvbmVzLg0KYGBge3J9DQp1bmlxdWUocGxhbiRtZXMpDQpgYGANCg0KKipVbmlkYWRlcyoqICANClBhcmEgdW5pZGFkZXMgb3B0YW1vcyBwb3IgZWxpbWluYXIgbG9zIHJlZ2lzdHJvcyBjb24gMCB1bmlkYWRlcywgeWEgcXVlIGFsIGNvbnZlcnRpciBsb3MgbWVzZXMgZW4gdW5hIHNvbGEgY29sdW1uYSwgdG9kb3MgYXF1ZWxsb3MgbWVzZXMgZG9uZGUgbm8gaGFiw61hIHByb2R1Y2Npw7NuIHBsYW5lYWRhIHRpZW5lbiAwLlRhbWJpw6luIGNvcnJvYm9yYW1vcyBxdWUgZWwgZm9ybWF0byBkZSBsYSBjb2x1bW5hIHNlYSBlbCBjb3JyZWN0bywgZGUgZW50ZXJvLg0KYGBge3J9DQpwbGFuIDwtIGZpbHRlcihwbGFuLCB1bmlkYWRlcz4wKQ0Kc3RyKHBsYW4kdW5pZGFkZXMpDQpgYGANCg0KIyMjIEJhamFzICANClBhcmEgZXN0YSBiYXNlIGRlIGRhdG9zIHRhbWJpw6luIGlyZW1vcyByZXZpc2FuZG8gY29sdW1uYSBwb3IgY29sdW1uYS4gIA0KDQoqKk5vbWJyZSBjb21wbGV0bywgZmVjaGEgZGUgbmFjaW1pZW50byB5IGZlY2hhIGRlIGFsdGEqKiAgDQpFc3RhcyBjb2x1bW5hcyBlc3TDoW4gY29ycmVjdGFzLCBzb2xvIGhheSBxdWUgY29udmVydGlyIGEgZm9ybWF0byBkZSBmZWNoYSBsYSBmZWNoYSBkZSBhbHRhIHkgZGUgbmFjaW1pZW50by4NCmBgYHtyfQ0KYmFqYXMkZmVjaGFfbmFjIDwtIGFzLkRhdGUoYmFqYXMkZmVjaGFfbmFjLCBmb3JtYXQgPSIlZC8lbS8lWSIpDQpzdHIoYmFqYXMkZmVjaGFfbmFjKQ0KYmFqYXMkZmVjaGFfYWx0YSA8LSBhcy5EYXRlKGJhamFzJGZlY2hhX2FsdGEsIGZvcm1hdCA9IiVkLyVtLyVZIikNCnN0cihiYWphcyRmZWNoYV9hbHRhKQ0KYGBgDQoNCioqR8OpbmVybyoqICANCkVuIGxhIGNvbHVtbmEgZGUgZ8OpbmVybyBzb2xvIGhheSB1biB2YWxvciB2YWPDrW8sIGJhc2Fkb3MgZW4gc3Ugbm9tYnJlIGNvbXBsZXRvIHZhbW9zIGEgYXN1bWlyIHF1ZSBzdSBnw6luZXJvIGVzIG1hc2N1bGluby4gQXPDrSBtaXNtbywgaGF5IHVuIHJlZ2lzdHJvIGNvbiBsbyBxdWUgcGFyZWNlIHNlciB1biBSRkMgZW4gbHVnYXIgZGVsIGfDqW5lcm8sIGJhc2FkbyBlbiBlbCBub21icmUgZGUgZXNhIHBlcnNvbmEgYXN1bWlyZW1vcyBxdWUgZXMgZmVtZW5pbm8uDQpgYGB7cn0NCmJhamFzJGdlbmVybyA8LSByZXBsYWNlKGJhamFzJGdlbmVybyxiYWphcyRnZW5lcm89PSIiLCJNQVNDVUxJTk8iKSANCmJhamFzJGdlbmVybyA8LSByZXBsYWNlKGJhamFzJGdlbmVybyxiYWphcyRnZW5lcm89PSJDQVBKMDAwOTI2NTk3IiwiRkVNRU5JTk8iKQ0KYGBgDQoNCioqQ8OzZGlnbyBQb3N0YWwqKiAgDQpQYXJhIGVsIGPDs2RpZ28gcG9zdGFsIGhheSBxdWUgcmVtcGxhemFyIGxvcyByZWdpc3Ryb3MgcXVlIHRlbmdhbiBlbCBtdW5pY2lwaW8gZW4gbHVnYXIgZGVsIENQIHBvciBzdSByZXNwZWN0aXZvIENQLg0KYGBge3J9DQpiYWphczIgPC0gYmFqYXMNCmJhamFzMiA8LSBiYWphczIgJT4lIG11dGF0ZShiYWphczIsIENQMj1DUCkNCmJhamFzMiA8LSBiYWphczIgJT4lIG11dGF0ZShiYWphczIsIG11bmljaXBpbzI9bXVuaWNpcGlvKQ0KYmFqYXMyIDwtIGJhamFzMiAlPiUgbXV0YXRlKGJhamFzMiwgZXN0YWRvMj1lc3RhZG8pDQoNCiNSZW1wbGF6YXIgZWwgbXVuaWNpcGlvIHBvciBlbCBDUCBlbiBsYSBjb2x1bW5hIGRlIENQLCB0YW1iaWVuIHJlbXBsYXphciB1bmlvbiBsaWJyZSBwb3IgZWwgQ1ANCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUoQ1A9aWZlbHNlKENQID09ICJBUE9EQUNBIiB8IENQID09ICJKVUFSRVoiIHwgQ1AgPT0gIk1PTlRFUlJFWSIgfCBDUCA9PSAiR1VBREFMVVBFIiB8IENQID09ICJSQU1PUyBBUklaUEUifCBDUCA9PSAiU0FOIFBEUk8gQ09BSCJ8IENQID09ICJQRVNRVUVSSUEifCBDUCA9PSAiQ0FERVJFWVRBInwgQ1AgPT0gIlNBTiBOSUNPTEFTIERFIExPUyBHQVJaQSJ8IENQID09ICJTQUxUSUxMTyJ8IENQID09ICJaVUFaVUEifCBDUCA9PSAiQ0lFTkVHQSBERSBGTE9SRVMifCBDUCA9PSAiTlVFVk8gTEVPTiIsIGVzdGFkbyAsIENQKSkNCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUoQ1A9aWZlbHNlKENQID09ICJVTklPTiBMSUJSRSIsIG11bmljaXBpbyAsIENQKSkNCnVuaXF1ZShiYWphczIkQ1ApDQoNCmBgYA0KDQoqKk11bmljaXBpbyoqICANCkVuIG11bmljaXBpbyBoYXkgcXVlIHJlbXBsYXphciBsb3MgcmVnaXN0cm9zIHF1ZSB0aWVuZW4gZWwgZXN0YWRvIHBvciBzdSByZXNwZWN0aXZvIG11bmljaXBpby4gQWRlbcOhcyBkZSBlc3RhbmRhcml6YXIgbG9zIHJlZ2lzdHJvcywgcG9yIGVqZW1wbG8sIGNhbWJpYXIgIlNBTiBOSUNPTEFTIERFIExPUyBHIiBwb3IgIlNBTiBOSUNPTEFTIERFIExPUyBHQVJaQSIuDQpgYGB7cn0NCiNSZW1wbGF6YXIgZWwgZXN0YWRvIHBvciBlbCBtdW5pY2lwaW8NCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUobXVuaWNpcGlvPWlmZWxzZShtdW5pY2lwaW8gPT0gIk5VRVZPIExFT04iIHwgbXVuaWNpcGlvID09ICJOdWV2byBMZcODwrNuIiB8IG11bmljaXBpbyA9PSAiQ09BSFVJTEEiIHwgbXVuaWNpcGlvID09ICJDb2FodWlsYSIsQ1AyICwgbXVuaWNpcGlvKSkNCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUobXVuaWNpcGlvPWlmZWxzZShtdW5pY2lwaW8gPT0gNjY0NDQgLCBjb2xvbmlhICwgbXVuaWNpcGlvKSkNCg0KI0NhbWJpYXMgU0FOIE5JQ09MQVMgREUgTE9TIEcgeCBTQU4gTklDT0xBUyBERSBMT1MgR0FSWkENCmJhamFzMiRtdW5pY2lwaW8gPC0gcmVwbGFjZShiYWphczIkbXVuaWNpcGlvLGJhamFzMiRtdW5pY2lwaW89PSJTQU4gTklDT0xBUyBERSBMT1MgRyIsIlNBTiBOSUNPTEFTIERFIExPUyBHQVJaQSIpDQoNCnVuaXF1ZShiYWphczIkbXVuaWNpcGlvKQ0KYGBgDQoNCioqRXN0YWRvKiogIA0KRW4gZXN0YWRvIGhheSBxdWUgcmVtcGxhemFyIGxvcyByZWdpc3Ryb3MgcXVlIHRpZW5lbiBDUCBwb3IgZWwgZXN0YWRvIGNvcnJlY3RvLiBBc8OtIG1pc21vLCBlc3RhbmRhcml6YXIgbG9zIHJlZ2lzdHJvcywgIHBvciBlamVtcGxvLCBjYW1iaWFyICJOdWV2byBMZcODwrNuIiBwb3IgIk5VRVZPIExFT04iLg0KYGBge3J9DQojUmVtcGxhemFyIGVsIENQIHBvciBlbCBlc3RhZG8gY29ycmVjdG8gZW4gbGEgY29sdW1uYSBkZSBlc3RhZG8NCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUoZXN0YWRvPWlmZWxzZShtdW5pY2lwaW8gPT0gIlNBTiBOSUNPTEFTIERFIExPUyBHQVJaQSIgfCBtdW5pY2lwaW8gPT0gIlBFU1FVRVJJQSIgfCBtdW5pY2lwaW8gPT0gIkFQT0RBQ0EiIHwgbXVuaWNpcGlvID09ICJKVUFSRVoiIHwgbXVuaWNpcGlvID09ICJHVUFEQUxVUEUifCBtdW5pY2lwaW8gPT0gIlJBTU9TIEFSSVpQRSJ8IG11bmljaXBpbyA9PSAiQ0FERVJFWVRBInwgbXVuaWNpcGlvID09ICJaVUFaVUEifCBtdW5pY2lwaW8gPT0gIkNJRU5FR0EgREUgRkxPUkVTInwgbXVuaWNpcGlvID09ICJNT05URVJSRVkiLCAiTlVFVk8gTEVPTiIgLCBtdW5pY2lwaW8pKQ0KDQpiYWphczIkZXN0YWRvIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkbyxiYWphczIkZXN0YWRvPT0iU0FOIFBEUk8gQ09BSCIsIkNPQUhVSUxBIikNCmJhamFzMiRlc3RhZG8gPC0gcmVwbGFjZShiYWphczIkZXN0YWRvLGJhamFzMiRlc3RhZG89PSJTQUxUSUxMTyIsIkNPQUhVSUxBIikNCg0KdW5pcXVlKGJhamFzMiRlc3RhZG8pDQpgYGANCg0KDQoqKkVzdGFkbyBDaXZpbCoqICANCkVuIGVzdGFkbyBjaXZpbCBoYXkgcXVlIGVzdGFuZGFyaXphciBsb3MgcmVnaXN0cm9zLCAgIHBvciBlamVtcGxvLCBjYW1iaWFyICJVTklPTkxJQlJFIiBwb3IgIlVOSU9OIExJQlJFIi4NCmBgYHtyfQ0KYmFqYXMyJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGJhamFzMiRlc3RhZG9fY2l2aWwsYmFqYXMyJGVzdGFkb19jaXZpbD09IlVOSU9OTElCUkUiLCJVTklPTiBMSUJSRSIpDQpiYWphczIkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkb19jaXZpbCxiYWphczIkZXN0YWRvX2NpdmlsPT0iVW5pw4PCs24gbGlicmUiLCJVTklPTiBMSUJSRSIpDQpiYWphczIkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkb19jaXZpbCxiYWphczIkZXN0YWRvX2NpdmlsPT0iU29sdGVybyIsIlNPTFRFUk8oQSkiKQ0KYmFqYXMyJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGJhamFzMiRlc3RhZG9fY2l2aWwsYmFqYXMyJGVzdGFkb19jaXZpbD09IkNhc2FkbyIsIkNBU0FETyhBKSIpDQpiYWphczIkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkb19jaXZpbCxiYWphczIkZXN0YWRvX2NpdmlsPT0iQ0FTQURBIiwiQ0FTQURPKEEpIikNCmJhamFzMiRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShiYWphczIkZXN0YWRvX2NpdmlsLGJhamFzMiRlc3RhZG9fY2l2aWw9PSJTT0xURVJBIiwiU09MVEVSTyhBKSIpDQpiYWphczIkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkb19jaXZpbCxiYWphczIkZXN0YWRvX2NpdmlsPT0iRElWT1JDSUFEQSIsIkRJVk9SQ0lBRE8oQSkiKQ0KYmFqYXMyJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGJhamFzMiRlc3RhZG9fY2l2aWwsYmFqYXMyJGVzdGFkb19jaXZpbD09IkNBU0FETyIsIkNBU0FETyhBKSIpDQpiYWphczIkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkb19jaXZpbCxiYWphczIkZXN0YWRvX2NpdmlsPT0iRElWT1JDSUFETyIsIkRJVk9SQ0lBRE8oQSkiKQ0KYmFqYXMyJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGJhamFzMiRlc3RhZG9fY2l2aWwsYmFqYXMyJGVzdGFkb19jaXZpbD09IlNPTFRFUk8iLCJTT0xURVJPKEEpIikNCnVuaXF1ZShiYWphczIkZXN0YWRvX2NpdmlsKQ0KDQpgYGANCg0KKipQdWVzdG8qKg0KUGFyYSBwdWVzdG8gdmFtb3MgYSB1bmlmaWNhciBhcXVlbGxvcyBwdWVzdG9zIHF1ZSBzb24gc2ltaWxhcmVzIHkgcXVlIHRlbmdhbiBlcnJvcmVzIG9ydG9ncsOhZmljb3MuICBQb3IgZWplbXBsbywgIkFZLiBHRU5FUkFMIChNQVRFUklBTEVTKSIgeSAiQVlVREFOVEUgREUgRU1CQVJRVUVTIiBwYXNhcmxvcyBhICJBWVVEQU5URSBHRU5FUkFMIi4NCmBgYHtyfQ0KYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkFZLkdFTkVSQUwgKE1BVEVSSUFMRVMpIiwiQVlVREFOVEUgR0VORVJBTCIpDQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iQVlVREFOVEUgREUgRU1CQVJRVUVTIiwiQVlVREFOVEUgR0VORVJBTCIpDQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iQVkuIEdFTkVSQUwiLCJBWVVEQU5URSBHRU5FUkFMIikNCmJhamFzMiRwdWVzdG8gPC0gcmVwbGFjZShiYWphczIkcHVlc3RvLGJhamFzMiRwdWVzdG89PSJBWVVELkVNQkFSUVVFUyIsIkFZVURBTlRFIEdFTkVSQUwiKQ0KYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkFZVURBTlRFIERFIFNPTERBRE9SIiwiQVlVREFOVEUgR0VORVJBTCIpDQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iQVlVREFOVEUgREUgTVRUTyIsIkFZVURBTlRFIEdFTkVSQUwiKQ0KYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkFZVURBTlRFIERFIFNPTERBRE9SIiwiQVlVREFOVEUgR0VORVJBTCIpDQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iQVlVREFOVEUgR0VORVJBTCBERSBFTUJBUlFVRVMiLCJBWVVEQU5URSBHRU5FUkFMIikNCmJhamFzMiRwdWVzdG8gPC0gcmVwbGFjZShiYWphczIkcHVlc3RvLGJhamFzMiRwdWVzdG89PSJDT1NUVVJFUkEiLCJDT1NUVVJFUk8oQSkiKQ0KYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkNPU1RVUkVSTyIsIkNPU1RVUkVSTyhBKSIpDQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iSU5TUEVDVE9SIERFIENBTElEQUQiLCJJTlNQRUNUT1IoQSkgREUgQ0FMSURBRCIpDQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iSU5TUEVDVE9SQSBERSBDQUxJREFEIiwiSU5TUEVDVE9SKEEpIERFIENBTElEQUQiKQ0KYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IklOU1BFQ1RPUiBDQUxJREFEIiwiSU5TUEVDVE9SKEEpIERFIENBTElEQUQiKQ0KYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkRJU0XDg+KAmE8iLCJESVNFw5FPIikNCmJhamFzMiRwdWVzdG8gPC0gdG91cHBlcihiYWphczIkcHVlc3RvKQ0KdW5pcXVlKGJhamFzMiRwdWVzdG8pDQpgYGANCg0KKipEZXBhcnRhbWVudG8qKiAgDQpQYXJhIGRlcGFydGFtZW50byBoYXJlbW9zIGxvIG1pc21vIHF1ZSBlbiBwdWVzdG8uDQpgYGB7cn0NCg0KYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09ImNlbGRhcyIsIkNFTERBUyIpDQpiYWphczIkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoYmFqYXMyJGRlcGFydGFtZW50byxiYWphczIkZGVwYXJ0YW1lbnRvPT0iQ2VsZGFzIiwiQ0VMREFTIikNCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJDZWRpcyIsIkNFRElTIikNCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJFbWJhcnF1ZXMiLCJFTUJBUlFVRVMiKQ0KYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09IkNvc3R1cmEiLCJDT1NUVVJBIikNCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJQYWlsZXJpYSIsIlBBSUxFUklBIikNCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJTdGFiaWx1cyIsIlNUQUJJTFVTIikNCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJUcm9xdWVsIiwiVFJPUVVFTCIpDQpiYWphczIkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoYmFqYXMyJGRlcGFydGFtZW50byxiYWphczIkZGVwYXJ0YW1lbnRvPT0iTWFudGVuaW1pZW50byBGRiIsIk1BTlRFTklNSUVOVE8iKQ0KYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09InN0YWJpbHVzIiwiU1RBQklMVVMiKQ0KYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09IlByb2R1Y2Npb24gQ2FydMODwrNuIE1DIiwiUFJPRFVDQ0lPTiBDQVJUT04gTUMiKQ0KYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09IlByb2R1Y2Npb24gQ2FydMODwrNuIE1ETCIsIlBST0RVQ0NJT04gQ0FSVE9OIE1ETCIpDQpiYWphczIkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoYmFqYXMyJGRlcGFydGFtZW50byxiYWphczIkZGVwYXJ0YW1lbnRvPT0iUHJvZHVjY2nDg8KzbiBSZXRvcm4iLCJQUk9EVUNDSU9OIFJFVE9STk8iKQ0KYmFqYXMyJGRlcGFydGFtZW50byA8LSB0b3VwcGVyKGJhamFzMiRkZXBhcnRhbWVudG8pDQp1bmlxdWUoYmFqYXMyJGRlcGFydGFtZW50bykNCmBgYA0KDQoqKk1vdGl2byBkZSBCYWphKiogIA0KUGFyYSBtb3Rpdm8gZGUgYmFqYSBoYXJlbW9zIGFsZ28gZGlmZXJlbnRlLCBhbCBhbmFsaXphciBsYSBiYXNlIGRlIGRhdG9zIG5vcyBkaW1vcyBjdWVudGEgcXVlIG11Y2hvcyBkZSBsb3MgZW1wbGVhZG9zIHF1ZSB0aWVuZW4gbXV5IHBvY29zIGTDrWFzLCBwb3IgZWplbXBsbyBkZSAwIGTDrWFzIGEgMTAsIGVuIGx1Z2FyIGRlIHRlbmVyIGNvbW8gbW90aXZvIGRlIGJhamEgQUJBTkRPTk8sIHRpZW5lIEZBTFRBUy4gUGFyYSBxdWUgZWwgbW90aXZvIGRlIGJhamEgc2VhIGxhcyBmYWx0YXMsIGRlYmVyw61hIGRlIGV4aXN0aXIgdW4gcGVyaW9kbyBlc3RhYmxlY2lkbyBzb2JyZSBlbCBjdWFsIHNlIGxpbWl0ZW4gbGFzIGZhbHRhcy4gUG9yIGVqZW1wbG8sIGNhZGEgZW1wbGVhZG8gc29sbyB0aWVuZSBwZXJtaXRpZGFzIHRhbnRhIGNhbnRpZGFkIGRlIGZhbHRhcyBhbCBtZXMuIFBvciBsbyB0YW50bywgYXF1ZWxsb3MgZW1wbGVhZG9zIHF1ZSB0aWVuZW4gbWVub3MgZGUgMTAgZMOtYXMgZGUgcGVybWFuZW5jaWEsIGNhbWJpYXJlbW9zIHN1IG1vdGl2byBkZSBiYWphIGRlIEZBTFRBUyBhIEFCQU5ET05PLg0KYGBge3J9DQojUmVtcGxhemFtb3MgRkFMVEEgUE9SIEJBSkFTIHBvciBBQkFORE9OTw0KYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShtb3RfYmFqYT1pZmVsc2UocGVybWFuZW5jaWEgPCAxMCwgIkFCQU5ET05PIiAsIG1vdF9iYWphKSkNCg0KI01hbnRlbmVtb3MgbG9zIG1vdGl2b3MgZGUgYmFqYSBxdWUgbm8gZXJhbiBGQUxUQSBQT1IgQkFKQVMgeSB0ZW5pYW4gcGVybWFuZW5jaWEgbWVub3IgZGUgMTAgZMOtYXMuDQpiYWphczIgPC0gIGJhamFzMiAlPiUgbXV0YXRlKG1vdF9iYWphPWlmZWxzZShub21icmVfY29tcGxldG8gPT0gIkpPQU5BIE5PSEVNSSBDT0xVTkdBIE9SVElaIiwgIlJFTlVOQ0lBIFZPTFVOVEFSSUEiICwgbW90X2JhamEpKQ0KYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShtb3RfYmFqYT1pZmVsc2Uobm9tYnJlX2NvbXBsZXRvID09ICJFUk5FU1RPIEdPTlpBTEVaIENBU1RJTExPIiwgIlJFTlVOQ0lBIFZPTFVOVEFSSUEiICwgbW90X2JhamEpKQ0KYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShtb3RfYmFqYT1pZmVsc2Uobm9tYnJlX2NvbXBsZXRvID09ICJNT0lTRVMgTEFVUkVBTkkgSklNRU5FWiIsICJSRU5VTkNJQSBWT0xVTlRBUklBIiAsIG1vdF9iYWphKSkNCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUobW90X2JhamE9aWZlbHNlKG5vbWJyZV9jb21wbGV0byA9PSAiUk9DSU8gT0NIT0EgR09OWkFMRVoiLCAiUkVOVU5DSUEgVk9MVU5UQVJJQSIgLCBtb3RfYmFqYSkpDQpiYWphczIgPC0gIGJhamFzMiAlPiUgbXV0YXRlKG1vdF9iYWphPWlmZWxzZShub21icmVfY29tcGxldG8gPT0gIk1BUkdBUklUQSBSSVZBUyBHT05aQUxFWiIsICJSRU5VTkNJQSBWT0xVTlRBUklBIiAsIG1vdF9iYWphKSkNCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUobW90X2JhamE9aWZlbHNlKG5vbWJyZV9jb21wbGV0byA9PSAiR0xPUklBIEZBQ1VORE8gTU9OVE9ZQSIsICJSRU5VTkNJQSBWT0xVTlRBUklBIiAsIG1vdF9iYWphKSkNCmBgYA0KDQoNCioqU2FsYXJpbyBEaWFyaW8gZGVsIElNU1MqKiAgDQpQYXJhIHNhbGFyaW8gZGlhcmlvIGhheSBxdWUgYXNlZ3VyYXJub3MgcXVlIGVzdGUgZW4gZWwgZm9ybWF0byBjb3JyZWN0bywgYWRlbcOhcyBkZSByZW1wbGF6YXIgbG9zIHZhbG9yZXMgZmFsdGFudGVzIGUgaWRlbnRpZmljYXIgdmFsb3JlcyBhdMOtcGljb3MgeSBkZWZpbmlyIHNpIGhheSBxdWUgcmVtcGxhemFybG9zLiAgDQpgYGB7cn0NCmZpbHRlcihiYWphczIsaXMubmEoYmFqYXMyJHNhbF9kaWFyaW9faW1zcykpDQpkZXNjcmliZShiYWphczIkc2FsX2RpYXJpb19pbXNzKQ0KYGBgDQoNCkNvbW8gcG9kZW1vcyB2ZXIgaGF5IGRvcyByZWdpc3Ryb3MgZmFsdGFudGVzLCBwYXJhIGVsIGRlICDDgW5nZWwgcmVtcGxhemFyZW1vcyBjb24gbGEgbWVkaWFuYSwgeWEgcXVlIHN1IHZhbG9yIGRlIHNrZXduZXNzIGRlbCBzYWxhcmlvIGRpYXJpbyBlcyBtYXlvciBhIDEsIGluZGljYW5kb25vcyBxdWUgZXMgbWVqb3IgdXNhciBsYSBtZWRpYW5hIHF1ZSBsYSBtZWRpYS4NCmBgYHtyfQ0Kc3VtbWFyeShiYWphczIkc2FsX2RpYXJpb19pbXNzKQ0KYGBgDQoNCmBgYHtyfQ0KYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShzYWxfZGlhcmlvX2ltc3M9aWZlbHNlKG5vbWJyZV9jb21wbGV0byA9PSAiQU5HRUwgR1VJTExFUk1PICBQQUxPTU8gTE9QRVoiLCAxODAuNywgc2FsX2RpYXJpb19pbXNzKSkNCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUoc2FsX2RpYXJpb19pbXNzPWlmZWxzZShub21icmVfY29tcGxldG8gPT0gIlBPTEVUVCBBRFJJQU5BIExPUEVaIFJVSVoiLCAxODAuNywgc2FsX2RpYXJpb19pbXNzKSkNCnN1bShpcy5uYShiYWphczIkc2FsX2RpYXJpb19pbXNzKSkNCmBgYA0KRmluYWxtZW50ZSB0ZXJtaW5hbW9zIGRlIGxpbXBpYXIgbGEgYmFzZSBkZSBkYXRvcyBkZSBhcXVlbGxhcyBjb2x1bW5hcyBxdWUgdHV2aW1vcyBxdWUgYWdyZWdhciBvIHJlbm9tYnJhci4NCmBgYHtyfQ0KYmFqYXMyIDwtIGRwbHlyOjpzZWxlY3QoYmFqYXMyLCAtYyhDUDIsbXVuaWNpcGlvMixlc3RhZG8yKSkNCmJhamFzIDwtIGJhamFzMg0KY29sbmFtZXMoYmFqYXMyKQ0KYGBgDQoNCiMjIyBDb2xhYm9yYWRvcmVzICANClBhcmEgY29sYWJvcmFkb3JlcyBkZSBpZ3VhbCBtYW5lcmEgcmV2aXNhcmVtb3MgY29sdW1uYSBwb3IgY29sdW1uYS4NCg0KKipOb21icmUgQ29tcGxldG8qKiAgDQpBIG5vbWJyZSBjb21wbGV0byBubyBsZSBoYXJlbW9zIGNhbWJpb3MgZW4gbGEgYmFzZS4NCg0KKipGZWNoYSBkZSBuYWNpbWllbnRvIHkgZmVjaGEgZGUgYWx0YSoqICANClBhcmEgZmVjaGEgZGUgbmFjaW1pZW50byB5IGZlY2hhIGRlIGFsdGEgbm9zIGFzZWd1cmFtb3MgcXVlIGVsIGZvcm1hdG8gZXN0ZSBlbiBmb3JtYXRvIGRlIGZlY2hhLg0KYGBge3J9DQpjb2xhYm9yYWRvcmVzJGZlY2hhX2FsdGEgPC0gYXMuRGF0ZShjb2xhYm9yYWRvcmVzJGZlY2hhX2FsdGEsIGZvcm1hdCA9ICIlZC8lbS8lWSIpDQpjb2xhYm9yYWRvcmVzJGZlY2hhX25hYyA8LSBhcy5EYXRlKGNvbGFib3JhZG9yZXMkZmVjaGFfbmFjLCBmb3JtYXQgPSAiJWQvJW0vJVkiKQ0KDQpgYGANCg0KKipHw6luZXJvKiogIA0KUGFyYSBnw6luZXJvIGlkZW50aWZpY2Ftb3MgcXVlIGVuIGxhIGJhc2UgZGUgZGF0b3MgZXhpc3RlbiA5IHJlZ2lzdHJvcyB0b3RhbG1lbnRlIHZhY8Otb3MsIHBvciBsbyBxdWUgZXMgbmVjZXNhcmlvIGVsaW1pbmFyIGVzdGFzIGZpbGFzLg0KYGBge3J9DQp1bmlxdWUoY29sYWJvcmFkb3JlcyRnZW5lcm8pDQpmaWx0ZXIoY29sYWJvcmFkb3JlcywgY29sYWJvcmFkb3JlcyRnZW5lcm89PSIiKQ0KYGBgDQpgYGB7cn0NCmNvbGFib3JhZG9yZXMgPC0gY29sYWJvcmFkb3Jlc1stKDExNDoxMjIpLF0NCmBgYA0KDQoqKlB1ZXN0byoqICANClBhcmEgcHVlc3RvIGhheSBxdWUgZXN0YW5kYXJpemFyIGxvcyByZWdpc3Ryb3MsIGVzcGVjw61maWNhbWVudGUgYXF1ZWxsb3MgcXVlIHNvbiBlbCBtaXNtbyBwdWVzdG8gcGVybyBlc3TDoW4gZXNjcml0b3MgZGlmZXJlbnRlLg0KYGBge3J9DQoNCmNvbGFib3JhZG9yZXMkcHVlc3RvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRwdWVzdG8sY29sYWJvcmFkb3JlcyRwdWVzdG89PSJTdXBlcnZpc29yIGRlIE3Dg8KhcXVpbiIsIlNVUEVSVklTT1IoQSkiKQ0KY29sYWJvcmFkb3JlcyRwdWVzdG8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJHB1ZXN0byxjb2xhYm9yYWRvcmVzJHB1ZXN0bz09IlN1cGVydmlzb3IgZGUgcGVnYWRvIiwiU1VQRVJWSVNPUihBKSIpDQpjb2xhYm9yYWRvcmVzJHB1ZXN0byA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkcHVlc3RvLGNvbGFib3JhZG9yZXMkcHVlc3RvPT0iU1VQRVJWSVNPUkEiLCJTVVBFUlZJU09SKEEpIikNCmNvbGFib3JhZG9yZXMkcHVlc3RvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRwdWVzdG8sY29sYWJvcmFkb3JlcyRwdWVzdG89PSJBWS4gR0VORVJBTCIsIkFZVURBTlRFIEdFTkVSQUwiKQ0KY29sYWJvcmFkb3JlcyRwdWVzdG8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJHB1ZXN0byxjb2xhYm9yYWRvcmVzJHB1ZXN0bz09IkFZVURBTlRFIERFIE1BTlRFTklNSUVOVE8iLCJBWVVEQU5URSBHRU5FUkFMIikNCmNvbGFib3JhZG9yZXMkcHVlc3RvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRwdWVzdG8sY29sYWJvcmFkb3JlcyRwdWVzdG89PSJDSE9GRVIgR0VTVE9SIiwiQ0hPRkVSIikNCmNvbGFib3JhZG9yZXMkcHVlc3RvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRwdWVzdG8sY29sYWJvcmFkb3JlcyRwdWVzdG89PSJJTlNQRUNUT1IgREUgQ0FMSURBRCIsIklOU1BFQ1RPUihBKSBERSBDQUxJREFEIikNCmNvbGFib3JhZG9yZXMkcHVlc3RvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRwdWVzdG8sY29sYWJvcmFkb3JlcyRwdWVzdG89PSJJTlNQRUNUT1JBIERFIENBTElEQUQiLCJJTlNQRUNUT1IoQSkgREUgQ0FMSURBRCIpDQpjb2xhYm9yYWRvcmVzJHB1ZXN0byA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkcHVlc3RvLGNvbGFib3JhZG9yZXMkcHVlc3RvPT0iTWFudGVuaW1pZW50byBGRiIsIk1BTlRFTklNSUVOVE8iKQ0KY29sYWJvcmFkb3JlcyRwdWVzdG8gPC0gdG91cHBlcihjb2xhYm9yYWRvcmVzJHB1ZXN0bykNCnVuaXF1ZShjb2xhYm9yYWRvcmVzJHB1ZXN0bykNCmBgYA0KDQoqKkRlcGFydGFtZW50byoqICANClBhcmEgZGVwYXJ0YW1lbnRvIGhheSBxdWUgZXh0YW5kYXJpemFyIGxvcyByZWdpc3Ryb3MgcXVlIHNvbiBlbCBtaXNtbyBwZXJvIGVzY3JpdG9zIGRpZmVyZW50ZXMuDQpgYGB7cn0NCnVuaXF1ZShjb2xhYm9yYWRvcmVzJGRlcGFydGFtZW50bykNCmNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG8sY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG89PSJQcm9kdWNjaW9uIENhcnTDg8KzbiBNREwiLCJQUk9EVUNDScOTTiBDQVJUT04gTURMIikNCmNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG8sY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG89PSJQcm9kdWNjaW9uIENhcnTDg8KzbiBNQyIsIlBST0RVQ0NJT04gQ0FSVE9OIE1DIikNCmNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG8sY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG89PSJQcm9kdWNjacODwrNuIFJldG9ybiIsIlBST0RVQ0NJT04gUkVUT1JOTyIpDQpjb2xhYm9yYWRvcmVzJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvLGNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvPT0iQ29zdHVyYSBUMiIsIkNPU1RVUkEiKQ0KY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG8gPC0gdG91cHBlcihjb2xhYm9yYWRvcmVzJGRlcGFydGFtZW50bykNCmBgYA0KDQoqKlNhbGFyaW8gZGlhcmlvIGRlbCBJTVNTKiogIA0KUGFyYSBzYWxhcmlvIGRpYXJpbyBkZWwgSU1TUyBoYXkgcXVlIGFzZWd1cmFybm9zIHF1ZSBlbCBmb3JtYXRvIGVzdMOpIGNvcnJlY3RvLiBBc8OtIG1pc21vLCBoYXkgcXVlIGNvcnJlZ2lyIGxvcyB2YWxvcmVzIGF0w61waWNvcy4NCmBgYHtyfQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpDQpjb2xhYm9yYWRvcmVzJHNhbF9kaWFyaW9faW1zcyA8LSBhcy5udW1lcmljKGNvbGFib3JhZG9yZXMkc2FsX2RpYXJpb19pbXNzKQ0KdW5pcXVlKGNvbGFib3JhZG9yZXMkc2FsX2RpYXJpb19pbXNzKQ0Kc3RyKGNvbGFib3JhZG9yZXMkc2FsX2RpYXJpb19pbXNzKQ0KYGBgDQpQYXJhIGNvcnJlZ2lyIGxvcyB2YWxvcmVzIGF0w61waWNvcyBwcmltZXJvIGhheSBxdWUgaWRlbnRpZmljYXJsb3MuIFBhcmEgcmVtcGxhemFyIHN1IHNhbGFyaW8sIG5vcyBiYXNhcmVtb3MgZW4gcHVlc3RvcyBzaW1pbGFyZXMgcGFyYSBhc2lnbmFybGVzIHVubyBzaW1pbGFyLg0KYGBge3J9DQpmaWx0ZXIoY29sYWJvcmFkb3JlcywgY29sYWJvcmFkb3JlcyRzYWxfZGlhcmlvX2ltc3MgPiAgNDQxMzc1MCkNCmBgYA0KDQpQb2RlbW9zIG9ic2VydmFyIHF1ZSBlbCBwdWVzdG8gZGUgKipKYWltZSBFcm5lc3RvKiogZXMgKipBeXVkYW50ZSBnZW5lcmFsKiogeSBxdWUgZXN0w6EgZW4gZWwgZGVwYXJ0YW1lbnRvIGRlICoqUHJvZHVjY2nDs24gUmV0b3JubyoqLiBBbCBvYnNlcnZhciByZWdpc3Ryb3MgY29uIGVsIG1pc21vIHB1ZXN0byB5IGRlcGFydGFtZW50bywgcG9kZW1vcyBvYnNlcnZhciBxdWUgdHJlcyBjb2xhYm9yYWRvcmVzIHRpZW5lbiB1biBzYWxhcmlvIGRlICoqMTUxLjYxKiosIHkgZWwgc2FsYXJpbyBlcXVpdm9jYWRvIGVzIGRlICoqMTUxNjcyODU3MS4wMCoqLCBwb3IgbG8gcXVlIHBvZHLDrWFtb3MgYXN1bWlyIGhhIHNpZG8gdW4gZXJyb3IgZGUgZGVkbyB5IGVsIHNhbGFyaW8gY29ycmVjdG8gZXMgKioxNTEuNjEqKiwgcG9yIGxvIHF1ZSBvcHRhbW9zIHBvciByZW1wbGF6YXJsbyBwb3IgZXN0ZSB2YWxvci4NCg0KYGBge3J9DQpjb2xhYm9yYWRvcmVzJHNhbF9kaWFyaW9faW1zc1tjb2xhYm9yYWRvcmVzJG5vbWJyZV9jb21wbGV0byA9PSAiSkFJTUUgRVJORVNUTyBBR1VJTEVSQSBST0RSSUdVRVoiXSA8LSAxNTEuNjENCmBgYA0KDQoNCkVsIHNlZ3VuZG8gc2FsYXJpbyBlcXVpdm9jYWRvIGVzIGRlICoqNDQxMzc1Ny4wMCoqLiB5IGRlICoqWU9MQU5EQSBMT1BFWiBSQU1PUyoqLiBBaG9yYSBoYXkgcXVlIGlkZW50aWZpY2FyIHB1ZXN0b3Mgc2ltaWxhcmVzIGVuIGVsIG1pc21vIGRlcGFydGFtZW50by4NCmBgYHtyfQ0KZmlsdGVyKGNvbGFib3JhZG9yZXMsIGRlcGFydGFtZW50byA9PSAiQ09TVFVSQSIpDQpgYGANCg0KRW4gZXN0ZSBjYXNvLCBlcyBlbCDDum5pY28gcmVnaXN0cm8gY29uIGVzZSBwdWVzdG8gZW4gZWwgZGVwYXJ0YW1lbnRvLCBwb3IgbG8gdGFudG8gaWRlbnRpZmljYXJlbW9zIHB1ZXN0b3Mgc2ltaWxhcmVzIHBlcm8gZW4gZGl2ZXJzb3MgZGVwYXJ0YW1lbnRvcy4NCmBgYHtyfQ0KZmlsdGVyKGNvbGFib3JhZG9yZXMsIHB1ZXN0byA9PSAiU1VQRVJWSVNPUihBKSIpDQpgYGANCg0KSGVtb3MgaWRlbnRpZmljYWRvIHNvbG8gdW4gcmVnaXN0cm8gbcOhcyBjb24gZWwgbWlzbW8gcHVlc3RvLCBwb3IgbG8gcXVlIG9wdGFyZW1vcyBwb3IgYXNpZ25hciBlbCBtaXNtbyBzYWxhcmlvIGFsIHJlZ2lzdHJvIGNvbiBlbCBzYWxhcmlvIGVsZXZhZG8uDQpgYGB7cn0NCmNvbGFib3JhZG9yZXMkc2FsX2RpYXJpb19pbXNzW2NvbGFib3JhZG9yZXMkbm9tYnJlX2NvbXBsZXRvID09ICJZT0xBTkRBIExPUEVaIFJBTU9TIl0gPC0gMzM3LjA1CQ0KdW5pcXVlKGNvbGFib3JhZG9yZXMkc2FsX2RpYXJpb19pbXNzKQ0KYGBgDQoNCioqTXVuaWNpcGlvKiogIA0KRW4gbXVuaWNpcGlvIGhheSBxdWUgZXN0YW5kYXJpemFyIGxvcyByZWdpc3Ryb3MsICBwb3IgZWplbXBsbywgY2FtYmlhciAiU0FOIE5JQ09MQVMgREUgTE9TIEciIHBvciAiU0FOIE5JQ09MQVMgREUgTE9TIEdBUlpBIi4NCmBgYHtyfQ0KdW5pcXVlKGNvbGFib3JhZG9yZXMkbXVuaWNpcGlvKQ0KY29sYWJvcmFkb3JlcyRtdW5pY2lwaW8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJG11bmljaXBpbyxjb2xhYm9yYWRvcmVzJG11bmljaXBpbz09IlNBTiBOSUNPTEFTIERFIExPUyBHIiwiU0FOIE5JQ09MQVMgREUgTE9TIEdBUlpBIikNCmNvbGFib3JhZG9yZXMkbXVuaWNpcGlvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRtdW5pY2lwaW8sY29sYWJvcmFkb3JlcyRtdW5pY2lwaW89PSJDQcOD4oCYQURBIEJMQU5DQSIsIkdVQURBTFVQRSIpDQpgYGANCg0KKipFc3RhZG8qKiAgDQpFbiBlc3RhZG8gaGF5IHF1ZSBlc3RhbmRhcml6YXIgbG9zIHJlZ2lzdHJvcywgIHBvciBlamVtcGxvLCBjYW1iaWFyICJOdWV2byBMZcODwrNuIiBwb3IgIk5VRVZPIExFT04iLg0KYGBge3J9DQoNCmNvbGFib3JhZG9yZXMkZXN0YWRvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRlc3RhZG8sY29sYWJvcmFkb3JlcyRlc3RhZG89PSJOdWV2byBMZcODwrNuIiwiTlVFVk8gTEVPTiIpDQpjb2xhYm9yYWRvcmVzJGVzdGFkbyA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkZXN0YWRvLGNvbGFib3JhZG9yZXMkZXN0YWRvPT0iR1VBREFMVVBFIE4uTC4iLCJOVUVWTyBMRU9OIikNCmNvbGFib3JhZG9yZXMkZXN0YWRvIDwtIHRvdXBwZXIoY29sYWJvcmFkb3JlcyRlc3RhZG8pDQoNCnVuaXF1ZShjb2xhYm9yYWRvcmVzJGVzdGFkbykNCmBgYA0KDQoqKkVzdGFkbyBDaXZpbCoqICANCkVuIGVzdGFkbyBjaXZpbCB0YW1iacOpbiBoYXkgcXVlIGVzdGFuZGFyaXphciBsb3MgcmVnaXN0cm9zLg0KYGBge3J9DQp1bmlxdWUoY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwpDQpjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCA8LSB0b3VwcGVyKGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsKQ0KY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCxjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbD09IlVOScODwrNOIExJQlJFIiwiVU5JT04gTElCUkUiKQ0KY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCxjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbD09IlNPTFRFUk8iLCJTT0xURVJPKEEpIikNCmNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwsY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWw9PSJDQVNBRE8iLCJDQVNBRE8oQSkiKQ0KY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCxjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbD09IkNBU0FEQSIsIkNBU0FETyhBKSIpDQpjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsLGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsPT0iU09MVEVSQSIsIlNPTFRFUk8oQSkiKQ0KY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCxjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbD09IkRJVk9SQ0lBREEiLCJESVZPUkNJQURPKEEpIikNCmNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwsY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWw9PSJESVZPUkNJQURPIiwiRElWT1JDSUFETyhBKSIpDQpgYGANCiMgQW7DoWxpc2lzIEV4cGxvcmF0b3JpbyBkZSBsYXMgQmFzZXMgZGUgRGF0b3MgIA0KDQojIyBFc3RhZMOtc3RpY29zIERlc2NyaXB0aXZvcyAgDQpBIGNvbnRpbnVhY2nDs24gc2UgcHJlc2VudGEgdW5hIHRhYmxhIGRlIGVzdGFkw61zdGljb3MgZGVzY3JpcHRpdm9zIHBhcmEgY2FkYSB1bmEgZGUgbGFzIGJhc2VzIGRlIGRhdG9zIHkgc3VzIHJlc3BlY3Rpdm9zIGNvbWVudGFyaW9zLiAgDQoNCiMjIyBNZXJtYQ0KRW4gbWVybWEgY29tbyBmZWNoYSB5IG1lcyBzb24gZGF0b3MgY3VhbGl0YXRpdm9zIG5vIGNvbnRpZW5lbiBpbmZvcm1hY2nDs24gZGVzY3JpcHRpdmEsIHNpbiBlbWJhcmdvIGxhIHZhcmlhYmxlIGRlIGtpbG9zIHNpLiBBIGNvbnRpbnVhY2nDs24gcG9kZW1vcyBpZGVudGlmaWNhciBxdWUgdGllbmUgc2tld25lc3MgbmVnYXRpdm8sIGNlcmNhbm8gYSAtMS4gDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZWRfbWVybWEgPC0gZGVzY3JpYmUobWVybWEpDQplZF9tZXJtYQ0KaGlzdChtZXJtYSRraWxvcykNCmBgYA0KDQojIyMgU2NyYXANCkVuIFNjcmFwIHJlZmVyZW5jaWEsIGZlY2hhIHByb2R1Y3RvIHkgdWJpY2FjacOzbiBkZSBvcmlnZW4gc29uIG3DoXMgcXVlIG5hZGEgY3VhbGl0YXRpdmFzIHBvciBsbyBxdWUgbm8gdGVuZW1vcyBzdXMgZGF0b3MgZGVzY3JpcHRpdm9zLCBzaW4gZW1iYXJnbyBlbiBjYW50aWRhZCBzw60sIGEgY29udGludWFjacOzbiBwZG9lbW9zIGVuY29udHJhciBzdSBtZWRpYSwgbWVkaWFuYSwgZGVzdmlhY2nDs24sIGVzdGFuZGFyIHkgdGFtYmnDqW4gc3UgZGlzdHJpYnVjacOzbiwgcHVlcyB0aWVuZSBza2V3bmVzcyBwb3NpdGl2YSBhbHRhLCBpbmRpY2FuZG9ub3MgcXVlIGVuIGNhc28gZGUgZGF0b3MgZmFsdGFudGVzIHNlcmlhIG1lam9yIHJlbXBsYXphciBjb24gbGEgbWVkaWFuYSBxdWUgY29uIGxhIG1lZGlhLg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmVkX3NjcmFwIDwtIGRlc2NyaWJlKHNjcmFwKQ0KZWRfc2NyYXANCmhpc3Qoc2NyYXAkY2FudGlkYWQpDQpgYGANCg0KIyMjIFByb2R1Y2Npw7NuIGRlIENhcnTDs24NCkVuIHByb2R1Y2Npw7NuIGRlIGNhcnRvbiB0ZW5lbW9zIGxvcyBkYXRvcyBkZXNjcmlwdGl2b3MgcHJpbmNpcGFsbWVudGUgZGUgcGl6YXMgcHJvZ3JhbWFzLCBsYW1pbmFzIHByb2Nlc2FkYXMsIHRpZW1wbyBkZSBjYWxpZGFkIHkgdG1vX21pbi4gRU4gdG9kYXMgZXN0YXMgYWRlbWFzIGRlIGVuY29udHJhciBzdSBtZWRpYSwgbWVkaWFuYSB5IGRlc3ZpYWNpw7NuIGVzdGFuZGFyIHBvZGVtb3MgaWRlbnRpZmljYXIgc3Ugc2tld25lc3MgcG9zaXRpdmEgZGUgbnVldm8gaW5kaWNhbmRvIHN1IGRpc3RyaWJ1Y2nDs24geSBjb21vIGVuIGNhc28gZGUgdmFsb3JlcyBmYWx0YW50ZXMgZXMgbWVqb3IgcmVtcGxhemFyIGNvbiBsYSBtZWRpYW5hLg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmVkX2NhcnRvbiA8LSBkZXNjcmliZShjYXJ0b24pDQplZF9jYXJ0b24NCmhpc3QoY2FydG9uJHBpZXphc19wcm9nKQ0KaGlzdChjYXJ0b24kbGFtX3Byb2Nlc2FkYXMpDQpoaXN0KGNhcnRvbiR0aWVtcG9fY2FsaWRhZCkNCmhpc3QoY2FydG9uJHRtb19taW4pDQpgYGANCg0KIyMjIERlbGl2ZXJ5IFBlcmZvcm1hbmNlDQpQYXJhIERlbGl2ZXJ5IHBlcmZvcm1hbmNlIHBvZGVtb3MgZW5jb250cmFyIGEgY29udGludWFjacOzbiBzdXMgZGF0b3MgZGVzY3JpcHRpdm9zLCBzaW4gZW1iYXJnbyBoYXkgYWxndW5vcyBxdWUgbm8gdG9tYW1vcyBtdWNobyBlbiBjb25zaWRlcmFjacOzbiBwb3IgZWplbXBsbywgbGEgZmVjaGEsIHZ1ZWx0YSBvIGNsaWVudGUsIHkgcG9yIG90cm8gbGFkbyB0ZW5lbW9zIHBsYW4gYXJyaXZhbCwgcmVhbCBhcnJpdmFsIHkgcmVhbCBkZXBhcnR1cmUgcXVlIHNvbiBob3JhcyBlbiB0ZW9yaWEgeSBkaWZmZXJlbmNlIHF1ZSBlcyBsYSBkaWZlcmVuY2lhIGVuIG1pbnV0b3MuDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZWRfcGVyZiA8LSBkZXNjcmliZShwZXJmKQ0KZWRfcGVyZg0KYGBgDQoNCiMjIyBEZWxpdmVyeSBQbGFuDQpDb24gZGVsaXZlcnkgcGxhbiBtdWNoYXMgZGUgbGFzIHZhcmlhYmxlcyBzb24gaG9yYXMgbyBjdWFsaXRhdGl2YXMsIHNpbiBlbWJhcmdvIHRlbmVtb3MgbGEgdmFyaWFibGUgZGUgdW5pZGFkZXMsIGxhIGN1YWwsIGFsIGlndWFsIHF1ZSBlbiBiYXNlcyBhbnRlcmlvcmVzIHRpZW5lIHVuYSBkaXN0cmlidWNpw7NuIGNvbiBza2V3IHBvc2l0aXZhIHkgYWx0YSwgcG9yIGxvIHF1ZSBlbiBjYXNvIGRlIHZhbG9yZXMgZmFsdGFudGVzIG5vcyBjb252ZW5kcsOtYSBtYXMgcmVtcGxhemFyIGNvbiBsYSBtZWRpYW5hIHF1ZSBjb24gbGEgbWVkaWEuDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZWRfcGxhbiA8LSBkZXNjcmliZShwbGFuKQ0KZWRfcGxhbg0KaGlzdChwbGFuJHVuaWRhZGVzKQ0KYGBgDQoNCiMjIyBCYWphcw0KRW4gYmFqYXMgbXVjaGFzIGRlIGxhcyB2YXJpYWJsZXMgc29uIGN1YWxpdGF0aXZhcyBwb3IgbG8gcXVlIGNhcmVjZW4gZGUgZXN0YWRpc3RpY29zIGRlc2NyaXB0aXZvcywgc2luIGVtYmFyZ28sIHRlbmVtb3Mgb3RyYSBjb21vIGVsIHNhbGFyaW8gZGlhcmlvLCBlbCBjdWFsIHRpZW5lIHVuYSBkaXN0cmlidWNpw7NuIGNvbiBza2V3bmVzcyBwb3NpdGl2byB0YW1iacOpbi4NCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQplZF9iYWphcyA8LSBkZXNjcmliZShiYWphcykNCmVkX2JhamFzDQpoaXN0KGJhamFzJHNhbF9kaWFyaW9faW1zcykNCmBgYA0KDQojIyMgQ29sYWJvcmFkb3Jlcw0KRWwgY2FzbyBkZSBsYSBiYXNlIGRlIGRhdG9zIGRlIGNvbGFib3JhZG9yZXMgZXMgbXV5IHNpbWlsYXIgYWwgZGUgYmFqYXMuDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZWRfY29sYWJvcmFkb3JlcyA8LSBkZXNjcmliZShjb2xhYm9yYWRvcmVzKQ0KZWRfY29sYWJvcmFkb3Jlcw0KaGlzdChjb2xhYm9yYWRvcmVzJHNhbF9kaWFyaW9faW1zcykNCmBgYA0KDQoNCiMjIEJhciBQbG90cw0KDQojIyMgTWVybWENClBhcmEgbWVybWEsIG5vcyBpbnRlcmVzYSBzYWJlciBlbCB0b3RhbCBkZSBraWxvcyBxdWUgc2UgcHJvZHVjZW4gcG9yIG1lcy4gDQpQYXJhIGdyw6FmaWNhciBlbCB0b3RhbCBkZSBraWxvcyBkZSBtZXJtYSBwb3IgbWVzIHByaW1lcm8gZXMgbmVjZXNhcmlvIG9idGVuZXIgbGEgY2FudGlkYWQgdG90YWwgZGUga2lsb3MgcG9yIG1lcywgZXNvIGxvIGhhY2Vtb3MgZGUgbGEgc2lndWllbnRlIG1hbmVyYS4NCmBgYHtyfQ0KbWVybWFfbWVzPC1hZ2dyZWdhdGUoa2lsb3MgfiBtZXMsIGRhdGEgPSBtZXJtYSwgc3VtKSAlPiUgYXJyYW5nZShkZXNjKGtpbG9zKSkNCm1lcm1hX21lcw0KYGBgDQoNClVuYSB2ZXogcXVlIHlhIHRlbmVtb3MgZWwgdG90YWwgcG9yIG1lcyBwb2RlbW9zIGdyYWZpY2FybG8uDQpgYGB7cn0NCmdncGxvdChtZXJtYV9tZXMsYWVzKHg9IHJlb3JkZXIobWVzLCAta2lsb3MpLHk9a2lsb3MsIGZpbGw9bWVzKSkrDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikrbGFicyh4PSJNZXMiLCB5PSJLaWxvcyIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIGxhYnModGl0bGU9IktpbG9zIGRlIG1lcm1hIHBvciBtZXMiKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgaGp1c3Q9MSkpDQpgYGANCiMjIyBTY3JhcA0KUGFyYSBTY3JhcCBoZW1vcyBvcHRhZG8gcG9yIHZpc3VhbGl6YXIgdW4gZ3LDoWZpY28gZGUgYmFycmFzIGRlIGxhIGNhbnRpZGFkIHkgbGEgdWJpY2FjacOzbiBkZSBvcmlnZW4uDQpgYGB7cn0NCmdncGxvdChzY3JhcCxhZXMoeD0gcmVvcmRlcih1Ymlfb3JpZ2VuLCAtY2FudGlkYWQpLHk9Y2FudGlkYWQsIGZpbGw9dWJpX29yaWdlbikpKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpK2xhYnMoeD0iVWJpY2FjacOzbiBkZSBvcmlnZW4iLCB5PSJDYW50aWRhZCIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIGxhYnModGl0bGU9IkNhbnRpZGFkIHBvciB1YmljYWNpw7NuIGRlIG9yaWdlbiIpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCBoanVzdD0xKSkNCmBgYA0KDQoNCiMjIyBQcm9kdWNjacOzbiBkZSBDYXJ0w7NuDQpMYSBiYXNlIGRlIGRhdG9zIGRlIHByb2R1Y2Npw7NuIGRlIGNhcnTDs24gc29sbyBhYmFyY2EgZGUgbG9zIG1lc2VzIGRlIGp1bGlvIGEgc2VwdGllbWJyZSBkZWwgMjAyMiwgbGEgYmFzZSBkZSBkYXRvcyBkZSBtZXJtYSBpbmNsdXllIG3DoXMgbWVzZXMgcGVybyBkZWwgbWlzbW8gYcOxby4gSGVtb3MgZ3JhZmljYWRvIGxhIGNhbnRpZGFkIGRlIHBpZXphcyBwcm9ncmFtYWRhcyBwb3IgbWVzIGVuIHByb2R1Y2Npw7NuIGRlIGNhcnTDs24gcGFyYSBjb25maXJtYXIgc2kgZWwgbWVzIGNvbiBtYXlvciBjYW50aWRhZCBkZSBwaWV6YXMgcHJvZ3JhbWFkYXMgdGFtYmnDqW4gZXMgZWwgbWVzIGNvbiBtYXlvciBjYW50aWRhZCBkZSBraWxvcyBkZSBtZXJtYS4NCmBgYHtyfQ0KY2FydG9uX21lcyA8LSBjYXJ0b24gJT4lIG11dGF0ZShtZXM9Zm9ybWF0KGZlY2hhLCAiJW0iKSkNCg0KY2FydG9uX21lczI8LWFnZ3JlZ2F0ZShwaWV6YXNfcHJvZyB+IG1lcywgZGF0YSA9IGNhcnRvbl9tZXMsIHN1bSkgJT4lIGFycmFuZ2UoZGVzYyhwaWV6YXNfcHJvZykpDQoNCmdncGxvdChjYXJ0b25fbWVzMixhZXMoeD0gcmVvcmRlcihtZXMsIC1waWV6YXNfcHJvZykseT1waWV6YXNfcHJvZywgZmlsbD1tZXMpKSsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKStsYWJzKHg9Ik1lcyIsIHk9IlBpZXphcyBwcm9ncmFtYWRhcyIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIGxhYnModGl0bGU9IlBpZXphcyBwcm9ncmFtYWRhcyBwb3IgbWVzIikrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIGhqdXN0PTEpKQ0KDQpgYGANCkFsIG9ic2VydmFyIGVsIGdyw6FmaWNvIGRlIGJhcnJhcyBwb2RlbW9zIGNvbmZpcm1hciBxdWUgZWZlY3RpdmFtZW50ZSBlbCBtZXMgZGUgYWdvc3RvLCBlbCBtZXMgZG9uZGUgbcOhcyBwaWV6YXMgcHJvZ3JhbWFkYXMgcGFyYSBwcm9kdWNjacOzbiBodWJvIHRhbWJpw6luIGZ1ZSBlbCBtZXMgY29uIG1heW9yIGNhbnRpZGFkIGRlIG1lcm1hLg0KDQojIyMgRGVsaXZlcnkgUGVyZm9ybWFuY2UNClBhcmEgZGVsaXZlcnkgcGVyZm9ybWFuY2Ugb3B0YW1vcyBwb3IgZ3JhZmljYXIgZWwgcmV0cmFzbyBxdWUgaHVibyBlbnRyZSBlbCB0aWVtcG8gcGxhbmVhZG8gZGUgbGxlZ2FkYSBjb24gZWwgY2xpZW50ZSB5IGVsIHRpZW1wbyByZWFsLiBOb3MgZGltb3MgY3VlbnRhIHF1ZSBlbCBmb3JtYXRvIGRlIGhvcmEgdmFyaWFiYSBkZSAyNCBob3JhcyBhIDEyIGhvcmFzIHBvciBsbyBxdWUgaGljaW1vcyBlbCBjYW1iaW8gcGFyYSBxdWUgZXN0ZSBjb3JyZWN0by4gQ29tbyBwb2RlbW9zIHZlciBNQUhMRSB0aWVuZSB1biBkZWxheSBtYXlvciBhbCBkZSBQUklOVEVMLg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KY2xhc3MocGVyZiRyZWFsX2Fycml2YWwpDQpwZXJmMiA8LSBwZXJmDQpwZXJmMyA8LSAgcGVyZjIgJT4lIG11dGF0ZShwbGFuX2Fycml2YWw9aWZlbHNlKGNsaWVudGUgPT0gIk1BSExFInwgcGxhbl9hcnJpdmFsID09ICAyMCB8IHJlYWxfYXJyaXZhbCA8IDE1LCA4LCBwbGFuX2Fycml2YWwpKQ0KDQpwZXJmMyA8LSBtdXRhdGUocGVyZjMsIGRlbGF5PXJlYWxfYXJyaXZhbC1wbGFuX2Fycml2YWwpIA0KcGVyZjMgPC0gYWdncmVnYXRlKGRlbGF5fmNsaWVudGUsIGRhdGE9cGVyZjMsIG1lYW4pDQpnZ3Bsb3QocGVyZjMsYWVzKHg9IGNsaWVudGUseT1kZWxheSwgZmlsbD1jbGllbnRlKSkrDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikrbGFicyh4PSJDbGllbnRlcyIsIHk9IkRlbGF5IGVuIG1pbiIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIGxhYnModGl0bGU9IkRlbGF5IHBvciBjbGllbnRlIikrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIGhqdXN0PTEpKQ0KYGBgDQoNCiMjIyBEZWxpdmVyeSBQbGFuDQpDb24gRGVsaXZlcnkgUGxhbiBxdWVyZW1vcyBldmFsdWFyIHNpIGVsIG1lcyBkb25kZSBtYXlvciBjYW50aWRhZCBkZSBwaWV6YXMgcHJvZ3JhbWFkYXMgaGFiw61hIHRhbWJpw6luIGVyYSBlbCBtZXMgZG9uZGUgbWF5b3IgcHJvZHVjY2nDs24gaHViby4NCmBgYHtyfQ0KcGxhbl9tZXM8LWFnZ3JlZ2F0ZSh1bmlkYWRlcyB+IG1lcywgZGF0YSA9IHBsYW4sIHN1bSkgJT4lIGFycmFuZ2UoZGVzYyh1bmlkYWRlcykpDQoNCmdncGxvdChwbGFuX21lcyxhZXMoeD0gcmVvcmRlcihtZXMsIC11bmlkYWRlcykseT11bmlkYWRlcywgZmlsbD1tZXMpKSsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKStsYWJzKHg9Ik1lcyIsIHk9IlVuaWRhZGVzIHByb2dyYW1hZGFzIikrDQogIHRoZW1lX21pbmltYWwoKSsNCiAgbGFicyh0aXRsZT0iVW5pZGFkZXMgcHJvZ3JhbWFkYXMgcG9yIG1lcyIpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCBoanVzdD0xKSkNCmBgYA0KQWwgb2JzZXJ2YXIgZWwgZ3LDoWZpY28gZGUgYmFycmFzIHBvZGVtb3MgaWRlbnRpZmljYXIgcXVlIGVsIG1lcyBlbiBlbCBxdWUgbcOhcyBwaWV6YXMgcHJvZ3JhbWFkYXMgaHVibyBubyBmdWUgZWwgbWVzIGNvbiBtYXlvciBwcm9kdWNjacOzbiB5IG1lcm1hLg0KDQojIyMgQmFqYXMgIA0KRW4gZXN0YSBncsOhZmljYSBkZSBiYXJyYXMgc2UgcHVkZSBvYnNlcnZhciBxdWUgZWwgcHJpbmNpcGFsIG1vdGl2byBkZSBsYXMgYmFqYXMgc29uIGxhcyBmYWx0YXMgc2VndWlkYW1lbnRlIGRlIHJlbnVuY2lhcyB2b2x1bnRhcmlhcy4gRXMgaW1wb3J0YW50ZSBtZW5jaW9uYXIgcXVlIGxhcyBwZXJzb25hcyBmdWVyb24gZGFkYXMgZGUgYmFqYXMgYSBwZXNhciBkZSB0ZW5lciBzb2xvIHVuYSBmYWx0YSwgcG9yIGxvIHF1ZSBubyBzZSBwdWVkZSByZWFsaXphciB1biBidWVuIGRpYWduw7NzdGljbyBzb2JyZSBsYXMgYmFqYXMgcG9yIGZhbHRhcy4NCmBgYHtyfQ0KbW90X2JhamEgPC0gYmFqYXMgJT4lIGdyb3VwX2J5KG1vdF9iYWphLCBnZW5lcm8pICU+JSB0YWxseSgpDQptb3RfYmFqYQ0KDQpnZ3Bsb3QoZGF0YT1tb3RfYmFqYSwgYWVzKHg9bW90X2JhamEsIHk9biwgZmlsbD1nZW5lcm8pKSArDQpnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCBoanVzdD0xKSkNCmBgYA0KDQojIyMgQ29sYWJvcmFkb3Jlcw0KRW4gZXN0YSBncsOhZmljYSBkZSBiYXJyYXMgcG9kZW1vcyBvYnNlcnZhciBlbCBzdWVsZG8gcHJvbWVkaW8gcG9yIHB1ZXN0byB5IGfDqW5lcm8uIFVuYSBvYnNlcnZhY2nDs24gaW1wb3J0YW50ZSBlcyBlbiBlbCBwdWVzdG8gZGUgc3VwZXJ2aXNvci9zdXBlcnZpc29yYSwgeWEgcXVlIGxhcyBtdWplcmVzIHRpZW5lbiBtYXlvciBzYWxhcmlvIHF1ZSBsb3MgaG9tYnJlcyBhIHBlc2FyIGRlIHJlYWxpemFyIGVsIG1pc21vIHRyYWJham8uIE90cm8gcHVlc3RvIGVuIGRvbmRlIGxhIHNpdHVhY2nDs24gZXMgc2ltaWxhciBwZXJvIGVsIHNhbGFyaW8gbm8gZXMgZGUgbXVjaGEgZGlmZXJlbmNpYSBlcyBlbiBlbCBwdWVzdG8gZGUgY29zdHVyZXJhL2Nvc3R1cmVyby4gICANCkFsIG9ic2VydmFyIGVzdGEgZ3LDoWZpY2EgcG9kZW1vcyBkZWNpciBxdWUgbGFzIG11amVyZXMgZ2FuYW4gbcOhcywgeWEgcXVlIGEgcGVzYXIgZGUgcXVlIHJlYWxpY2VuIGVsIG1pc21vIHRyYWJham8gbGFzIG11amVyZXMgcmVjaWJlbiB1biBtYXlvciBzdWVsZG8gcXVlIGxvcyBob21icmVzLg0KYGBge3J9DQpzdWVsZG9fZ2VuZXJvX3B1ZXN0bzwtYWdncmVnYXRlKHNhbF9kaWFyaW9faW1zcyB+IHB1ZXN0bytnZW5lcm8sIGRhdGEgPSBjb2xhYm9yYWRvcmVzLCBtZWFuKSANCg0KZ2dwbG90KGRhdGE9c3VlbGRvX2dlbmVyb19wdWVzdG8sIGFlcyh4PXB1ZXN0bywgeT1zYWxfZGlhcmlvX2ltc3MsIGZpbGw9Z2VuZXJvKSkgKw0KZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgpKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgaGp1c3Q9MSkpDQpgYGANCg0KDQojIyBEaXNwZXJzaW9uIFBsb3RzDQoNCiMjIyBNZXJtYQ0KRW4gZXN0YSBncsOhZmljYSBkZSBib3hwbG90IHBvZGVtb3Mgb2JzZXJ2YXIgbGEgZGlzcGVyc2nDs24gZGUga2lsb3MgZGUgbWVybWEgcG9yIG1lcy4gQWdvc3RvIGVzIGVsIG1lcyBjb24gbWF5b3IgZGlzcGVyc2nDs24scG9yIGxvIHF1ZSBwb2RlbW9zIGluZmVyaXIgcXVlIGVuIGVzdGUgbWVzIGVzIGRvbmRlIGhheSBtYXlvciBwcm9kdWNjacOzbi4NCmBgYHtyfQ0KZ2dwbG90KG1lcm1hLCBhZXMoeD1tZXMsIHk9a2lsb3MpKSArIA0KICBnZW9tX2JveHBsb3QoY29sb3I9InJlZCIsIGZpbGw9Im9yYW5nZSIsIGFscGhhPTAuMikNCmBgYA0KDQojIyMgU2NyYXANCkVuIGVzdGEgZ3LDoWZpY2EgZGUgYm94cGxvdCBwb2RlbW9zIG9ic2VydmFyIGxhIGRlc3BlcmF0aW9uIGRlIGNhbnRpZGFkIGRlIHNjcmFwIHBvciB1YmljYWNpw7NuIGRlIG9yaWdlbi4gQ29tbyBzZSBwdWVkZSBvYnNlcnZhciBkw7NuZGUgaGF5IG1heW9yIGRpc3BlcnNpw7NuIGRlIHNjcmFwIGVzIGVuIHByZS1wcm9kdWNjacOzbiB5IGVzdG8gc2UgcHVlZGUgZGViZXIgYSBxdWUgbGEgZW1wcmVzYSB1dGlsaXphIG3DoXMgbWF0ZXJpYWxlcyBlbiBlc3RhIMOhcmVhIGRlIHRyYWJham8uDQpgYGB7cn0NCmdncGxvdChzY3JhcCwgYWVzKHg9dWJpX29yaWdlbiwgeT1jYW50aWRhZCkpICsgDQogIGdlb21fYm94cGxvdChjb2xvcj0icmVkIiwgZmlsbD0ib3JhbmdlIiwgYWxwaGE9MC4yKQ0KYGBgDQoNCiMjIyBQcm9kdWNjacOzbiBkZSBDYXJ0w7NuDQpFbiBlc3RhIGdyw6FmaWNhIHBvZGVtb3Mgb2JzZXJ2YXIgbGEgZGlzcGVyc2nDs24gZGUgdGllbXBvIGRlIGNhbGlkYWQgcG9yIGNsaWVudGUgZGUgcHJvZHVjY2nDs24gZGUgY2FydMOzbi4gRXhpc3RlbiBkaWZlcmVudGVzIG1vdGl2b3MgcG9yIGxvIGN1YWwgc2UgdG9tZSBtYXlvciB0aWVtcG8gZGUgY2FsaWRhZCBhIGNpZXJ0b3MgY2xpZW50ZXMgZXN0byBwdWVkZSBzZXIgcG9yOiAgIA0KLSBjYW50aWRhZCBkZSBwaWV6YXMgICANCi0gY2xpZW50ZSBmcmVjdWVudGUgICANCi0gZW1wYXF1ZSBjb21wbGljYWRvICANCmBgYHtyfQ0KZ2dwbG90KGNhcnRvbiwgYWVzKHg9Y2xpZW50ZSwgeT10aWVtcG9fY2FsaWRhZCkpICsgDQogIGdlb21fYm94cGxvdChjb2xvcj0icmVkIiwgZmlsbD0ib3JhbmdlIiwgYWxwaGE9MC4yKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgaGp1c3Q9MSkpDQpgYGANCg0KIyMjIERlbGl2ZXJ5IFBlcmZvcm1hbmNlDQpBbCBncmFmaWNhciBsYSBkaXNwZXJzacOzbiBkZSBsYSBkaWZlcmVuY2lhIGVudHJlIHJlYWwgYXJyaXZhbCB5IGRlcGFydHVyZSBhcnJpdmFsIHZlbW9zIHF1ZSBoYXkgbWF5b3IgdmFyaWFjaW9uIGVuIGVsIGNsaWVudGUgTUFITEUgcXVlIGVuIFBSSU5URUwuDQpgYGB7cn0NCmdncGxvdChwZXJmLCBhZXMoeD1jbGllbnRlLCB5PWRpZmZlcmVuY2UpKSArIA0KICBnZW9tX2JveHBsb3QoY29sb3I9InJlZCIsIGZpbGw9Im9yYW5nZSIsIGFscGhhPTAuMikrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIGhqdXN0PTEpKQ0KYGBgDQoNCiMjIyBEZWxpdmVyeSBQbGFuDQpFbCBib3hwbG90IGEgY29udGludWFjacOzbiBtdWVzdHJhIGxhIGRpc3BlcnNpw7NuIGRlIGxhcyB1bmlkYWRlcyBwcm9ncmFtYWRhcyBwb3IgY2xpZW50ZSwgY29tbyBwb2RlbW9zIHZlciBIZWxsYSB5IFRSTVggdGllbmVuIG1heW9yIGNhbnRpZGFkIGRlIHVuaWRhZGVzIHByb2dyYW1hZGFzIHkgZXMgbcOhcyBkaXNwZXJzYSBzdSBkaXN0cmlidWNpw7NuLg0KYGBge3J9DQpnZ3Bsb3QocGxhbiwgYWVzKHg9Y2xpZW50ZSwgeT11bmlkYWRlcykpICsgDQogIGdlb21fYm94cGxvdChjb2xvcj0icmVkIiwgZmlsbD0ib3JhbmdlIiwgYWxwaGE9MC4yKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgaGp1c3Q9MSkpDQpgYGANCg0KIyMjIEJhamFzDQpFbCBzaWd1aWVudGUgZXMgdW4gYm94cGxvdCBkZSBsYSBwZXJtYW5lbmNpYSBlbiBsYSBlbXByZXNhIGVuIGxhcyBiYWphcyBwb3IgZ2VuZXJvLCBwb3IgbG8gZ2VuZXJhbCBsYXMgbXVqZXJlcyBkdXJhbiBtw6FzIGVuIGxhIGVtcHJlc2EuDQpgYGB7cn0NCmdncGxvdChiYWphcywgYWVzKHg9Z2VuZXJvLCB5PXBlcm1hbmVuY2lhKSkgKyANCiAgZ2VvbV9ib3hwbG90KGNvbG9yPSJyZWQiLCBmaWxsPSJvcmFuZ2UiLCBhbHBoYT0wLjIpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCBoanVzdD0xKSkNCmBgYA0KDQojIyMgQ29sYWJvcmFkb3Jlcw0KRXN0ZSBib3hwbG90IG11ZXN0cmEgbGEgZGlzcGVyc2nDs24gcXVlIGhheSBlbiBlbCBzYWxhcmlvIGRpYXJpbyBxdWUgcmVjaWJlbiBsb3MgY29sYWJvcmFkb3JlcyBwb3IgZ8OpbmVybywgbGEgZGlzcGVyc2nDs24gZW4gbWF5b3IgZW4gZWwgZ8OpbmVybyBmZW1lbmlubywgaGF5IG11amVyZXMgcXVlIGdhbmFuIGNvbnNpZGVyYWJsZW1lbnRlIG3DoXMgcXVlIGxvcyBob21icmVzLg0KYGBge3J9DQpnZ3Bsb3QoY29sYWJvcmFkb3JlcywgYWVzKHg9Z2VuZXJvLCB5PXNhbF9kaWFyaW9faW1zcykpICsgDQogIGdlb21fYm94cGxvdChjb2xvcj0icmVkIiwgZmlsbD0ib3JhbmdlIiwgYWxwaGE9MC4yKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgaGp1c3Q9MSkpDQpgYGANCg0KIyMgVGltZSBTZXJpZXMgUGxvdHMgDQoNCiMjIyBNZXJtYQ0KRW4gZWwgc2lndWllbnRlIHBsb3QgcG9kZW1vcyBpZGVudGlmaWNhciBsYSBmZWNoYSBlbiBsYSBxdWUgaHVibyBtYXlvciBraWxvcyBkZSBtZXJtYS4gQSBtZWRpYWRvcyBkZSBmZWJyZXJvIGh1Ym8gdW5hIGdyYW4gY2FudGlkYWQgZGUgbWVybWEsIGVzcGVjw61maWNhbWVudGUgZWwgMTggZGUgZmVicmVyby4gUG9kZW1vcyBvYnNlcnZhciB0YW1iacOpbiBxdWUgZGVzcHXDqXMgZGUgY2FkYSBwaWNvIGRlIG1lcm1hLCBoYXkgdW5hIGNvcnJlY2Npw7NuIHkgbGEgbWVybWEgZGlzbWludXllLiBFc3RvIHBvZHLDrWEgc2VyIHBvciBkaXZlcnNvcyBmYWN0b3JlcyBjb21vIGFqdXN0ZXMgZW4gbGEgcHJvZHVjY2nDs24sIGVuIGxvcyBjb3J0ZXMsIGVudHJlIG90cm9zLg0KYGBge3J9DQptZXJtYV9mZWNoYTwtYWdncmVnYXRlKGtpbG9zIH4gZmVjaGEsIGRhdGEgPSBtZXJtYSwgc3VtKSANCg0KZ2dwbG90KG1lcm1hX2ZlY2hhLGFlcyh4PWZlY2hhLHk9a2lsb3MpKSsNCiAgZ2VvbV9saW5lKCkrDQogIGxhYnModGl0bGU9IktpbG9zIHBvciBmZWNoYSIpKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgaGp1c3Q9MSkpDQpgYGANCg0KIyMjIFByb2R1Y2Npw7NuIGRlIENhcnTDs24NClBhcmEgbGEgcHJvZHVjY2nDs24gZGUgY2FydMOzbiB2YW1vcyBhIGdyYWZpY2FyIGVsIHRpZW1wbyBlbiBtaW51dG9zIHF1ZSB0b23DsyBlbCBwcm9jZXNvLiBQYXJhIGVzdG8gbmVjZXNpdGFtb3MgcmVzdGFyIGVsIHRpZW1wbyBkZSBpbmljaW8gZGUgcHJvY2VzbyBhbCBkZSBmaW4gZGUgcHJvY2Vzby4NCmBgYHtyfQ0KY2FydG9uMjwtbXV0YXRlKGNhcnRvbiwgdGllbXBvX3Byb2Nlc28gPSBmaW5fcHJvY2Vzby1pbmljaW9fcHJvY2VzbykNCmNhcnRvbjIgPC0gZmlsdGVyKGNhcnRvbjIsICF0aWVtcG9fcHJvY2VzbyA8IDApDQpgYGANCg0KQXNpIG1pc21vLCB2YW1vcyBhIHNhY2FyIGVsIHByb21lZGlvIGRlIHRpZW1wbyBxdWUgdG9tYSBlbCBwcm9jZXNvIHBvciBjbGllbnRlLiBEZSBlc3RhIGZvcm1hIHBvZHJlbW9zIHNhYmVyIGN1YWwgY2xpZW50ZSBjb25zdW1lIG1hcyB0aWVtcG8gZGUgcHJvZHVjY2lvbiBkZSBGb3JtIGVuIGVsIG1lcyBkZSBhZ29zdG8uDQpgYGB7cn0NCnByb2RfY2xpZW50ZTwtYWdncmVnYXRlKHRpZW1wb19wcm9jZXNvIH4gY2xpZW50ZSwgZGF0YT1jYXJ0b24yLCBtZWFuKQ0KcHJvZF9jbGllbnRlDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QocHJvZF9jbGllbnRlLGFlcyh4PWNsaWVudGUseT10aWVtcG9fcHJvY2VzbykpKw0KICBnZW9tX3BvaW50KCkrDQogIGxhYnModGl0bGU9IlRpZW1wbyBwcm9tZWRpbyBwb3IgY2xpZW50ZSIpKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpDQpgYGANCkNvbW8gcG9kZW1vcyB2ZXIgSGFub24gU3lzdGVtcywgWWVuIEZhbmcgeSBWYXJyb2Mgc29uIGxvcyBjbGllbnRlcyBxdWUgbWF5b3IgdGllbXBvIHRvbWEgc3UgcHJvY2VzbyBkZSBwcm9kdWNjacOzbi4gRXN0byBwdWVkZSBzZXIgZGViaWRvIGEgbGEgY29tcGxlamlkYWQgZGUgc3VzIHByb2R1Y3RvcyBvIG1hdGVyaWFsLCBzaW4gZW1iYXJnbywgaGF5IHF1ZSBwcmVzdGFyIGVzcGVjaWFsIGF0ZW5jacOzbiBhIGxhIGdyYW4gZGlmZXJlbmNpYSBxdWUgaGF5IGVudHJlIEhhbm9uIFN5c3RlbXMgeSBlbCByZXN0byBkZSBjbGllbnRlcy4NCg0KIyMjIERlbGl2ZXJ5IFBsYW4NCkVuIERlbGl2ZXJ5IFBsYW4gcG9kZW1vcyBvYnNlcnZhciBsYSBjYW50aWRhZCBkZSB1bmlkYWRlcyBwb3IgbWVzIHBvciBjbGllbnRlLiBQb2RlbW9zIGlkZW50aWZpY2FyIHF1ZSBIRUxMQSBlbiBsYSBncmFuIG1heW9yw61hIGRlIGxhcyBmZWNoYXMgZXMgZWwgY2xpZW50ZSBjb24gbWF5b3IgY2FudGlkYWQgZGUgdW5pZGFkZXMgcGxhbmVhZGFzLCByYXJhIHZleiBlcyBzdXBlcmFkbyBwb3IgVFJNWC4NCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwbGFuX21lc3ljbGllbnRlIDwtIGFnZ3JlZ2F0ZSh1bmlkYWRlcyB+IGNsaWVudGUrbWVzLCBkYXRhPXBsYW4sIHN1bSkNCg0KcGxhbl9tZXN5Y2xpZW50ZSA8LSBwbGFuX21lc3ljbGllbnRlICU+JSAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGNsaWVudGUsIHZhbHVlc19mcm9tID0gdW5pZGFkZXMpDQoNCnBsYW5fbWVzeWNsaWVudGUgPC0gZHBseXI6OnNlbGVjdChwbGFuX21lc3ljbGllbnRlLCBjKG1lcywiSEVMTEEiLCAiVFJNWCIsICJWQVJST0MiLCAiREVOU08iKSkNCg0KDQpnZ3Bsb3QocGxhbl9tZXN5Y2xpZW50ZSwgYWVzKHg9bWVzKSkgKyANCiAgZ2VvbV9wb2ludChhZXMoeSA9IEhFTExBKSwgY29sb3IgPSAiZGFya3JlZCIpICsgDQogIGdlb21fcG9pbnQoYWVzKHkgPSBUUk1YKSwgY29sb3I9InN0ZWVsYmx1ZSIsIGxpbmV0eXBlPSJ0d29kYXNoIikrDQogIGdlb21fcG9pbnQoYWVzKHkgPSBWQVJST0MpLCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArIA0KICBnZW9tX3BvaW50KGFlcyh5ID0gREVOU08pLCBjb2xvcj0iZGFya29yYW5nZSIsIGxpbmV0eXBlPSJ0d29kYXNoIikrIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkNCg0KYGBgDQoNCiMjIyBCYWphcw0KRW4gZXN0b3MgZ3LDoWZpY29zIGRlIGJhcnJhcyBwb2RlbW9zIGlkZW50aWZpY2FyIGxvcyBtZXNlcyBkb25kZSBtw6FzIGJhamFzIGhheSB5IGRvbmRlIG3DoXMgaW5ncmVzb3MgaGF5LCBpZGVudGlmaWNhbmRvIHBhdHJvbmVzLiBFbiBsYXMgYmFqYXMsIGxhIG1heW9yIGNhbnRpZGFkIG9jdXJyZW4gYWwgaW5pY2lvIGRlbCBhw7FvIHkgcG9zdGVyaW9ybWVudGUgYSBtZWRpYWRvcyBwZXJvIGNhc2kgbnVuY2EgYSBmaW5hbGVzIGRlIGHDsW8uIEVzdG8gcG9kcsOtYSBkZWJlcnNlIGEgcXVlIGxvcyBjb2xhYm9yYWRvcmVzIG5vIHF1aWVyZW4gZXN0YXIgc2luIHRyYWJham8gZW4gZmVjaGFzIGRvbmRlIHN1cyBnYXN0b3MgcHVlZGVuIHNlciBtYXlvcmVzIGNvbW8gbmF2aWRhZCwgYcOxbyBudWV2bywgY2llcnJlIGUgaW5pY2lvIGRlIGNpY2xvcyBlc2NvbGFyZXMsIGVudHJlIG90cm9zLg0KDQpFbiBlbCBjYXNvIGRlIGxvcyBpbmdyZXNvcyBvIGFsdGFzLCBlc3RhcyBzZSBkYW4gbcOhcyBhIG1lZGlhZG9zIGRlIGHDsW8sIG11eSBwcm9iYWJsZW1lbnRlIEZPUk0gaGFnYSBlc3RvIHBhcmEgY3VicmlyIGxhcyBiYWphcyBxdWUgb2N1cnJpZXJvbiBhIGluaWNpb3MgeSBtZWRpYWRvcyBkZSBhw7FvLiBEZSBpZ3VhbCBmb3JtYSwgbGFzIGFsdGFzIHNvbiBtdXkgYmFqYXMgYSBmaW5hbGVzIGRlIGHDsW8sIHF1aXrDoXMgcG9kcsOtYSBkZWJlcnNlIGEgcXVlIGVuIGVzdGFzIGZlY2hhcyBGT1JNIGVzdMOhIGNlcnJhbmRvIGVsIGHDsW8sIHByZXBhcmFuZG8gYWd1aW5hbGRvcywgZW50cmUgb3Ryb3MgY29zdG9zIHF1ZSB0aWVuZSBwb3IgbG8gcXVlIHByZWZpZXJlIG5vIGNvbnRyYXRhciBudWV2b3MgZW1wbGVhZG9zLg0KYGBge3J9DQpiYWphcyRiYWphIDwtIGFzLkRhdGUoYmFqYXMkYmFqYSwgZm9ybWF0ID0iJWQvJW0vJVkiKQ0KYmFqYXNfbWVzIDwtIGJhamFzICU+JSBtdXRhdGUobWVzX2JhamE9Zm9ybWF0KGJhamEsICIlbSIpKQ0KYmFqYXNfbWVzIDwtIGJhamFzX21lcyAlPiUgbXV0YXRlKG1lc19hbHRhPWZvcm1hdChmZWNoYV9hbHRhLCIlbSIpKQ0KDQpiYWphc19wb3JfbWVzIDwtIGJhamFzX21lcyAlPiUgZ3JvdXBfYnkobWVzX2JhamEpICU+JSB0YWxseSgpDQphbHRhc19wb3JfbWVzIDwtIGJhamFzX21lcyAlPiUgZ3JvdXBfYnkobWVzX2FsdGEpICU+JSB0YWxseSgpDQoNCmdncGxvdChiYWphc19wb3JfbWVzLGFlcyh4PSByZW9yZGVyKG1lc19iYWphLC1uKSx5PW4sIGZpbGw9bWVzX2JhamEpKSsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKStsYWJzKHg9Ik1lcyIsIHk9IkJhamFzIikrDQogIHRoZW1lX21pbmltYWwoKSsNCiAgbGFicyh0aXRsZT0iQmFqYXMgcG9yIG1lcyIpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCBoanVzdD0xKSkNCg0KZ2dwbG90KGFsdGFzX3Bvcl9tZXMsYWVzKHg9IHJlb3JkZXIobWVzX2FsdGEsLW4pLHk9biwgZmlsbD1tZXNfYWx0YSkpKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpK2xhYnMoeD0iTWVzIiwgeT0iQmFqYXMiKSsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICBsYWJzKHRpdGxlPSJBbHRhcyBwb3IgbWVzIikrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIGhqdXN0PTEpKQ0KYGBgDQoNCg0KIyMjIENvbGFib3JhZG9yZXMNCkdyYWZpY2Ftb3MgYW50aWd1ZWRhZCBwb3IgcHVlc3RvIHkgcG9kZW1vcyBpZGVudGlmaWNhciBxdWUgbG9zIGhvbWJyZXMgdGllbmVuIG1heW9yIGFudGlndWVkYWQgZW4gZm9ybSBxdWUgbGFzIG11amVyZXMsIGFkZW3DoXMgcXVlIGxvcyB1bmljb3MgcHVlc3RvcyBkb25kZSBoYXkgaG9tYnJlcyB5IG11amVyZXMgc29uIGVuIHN1cGVydmlzb3IoYSkgeSBjb3N0dXJlcmEobykuIEFzw60gbWlzbW8gcXVlIGxvcyBwdWVzdG9zIGhheSBtw6FzIHBhcmEgaG9tYnJlcyBxdWUgcGFyYSBtdWVqZXJlcy4NCmBgYHtyfQ0KY29sYWJvcmFkb3JlcyA8LSBtdXRhdGUoY29sYWJvcmFkb3JlcywgYW50aWd1ZWRhZD10b2RheSgpLWZlY2hhX2FsdGEpDQoNCmFudGlndWVkYWRfcHVlc3RvIDwtIGFnZ3JlZ2F0ZShhbnRpZ3VlZGFkIH4gcHVlc3RvK2dlbmVybywgZGF0YT1jb2xhYm9yYWRvcmVzLCBtZWFuKQ0KDQpnZ3Bsb3QoZGF0YT1hbnRpZ3VlZGFkX3B1ZXN0bywgYWVzKHg9cHVlc3RvLCB5PWFudGlndWVkYWQsIGZpbGw9Z2VuZXJvKSkgKw0KZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgpKSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgaGp1c3Q9MSkpDQpgYGANCg0KDQojIFByZWRpY2Npw7NuIGRlbCBEZXNlbXBlw7FvDQoNCiMjIMK/UXXDqSBub3MgZGljZSBlc3RhIGJhc2UgZGUgZGF0b3M/IA0KVGVuZW1vcyBsYXMgdmVudGFzIGludGVybmFjaW9uYWxlcyBkZSBhdXRvcGFydGVzIHBvciBlc3RhZG8gZGUgbGEgcmVww7pibGljYSBtZXhpY2FuYS4gTnVlc3RybyBwcm9vc2l0byBlcyBlbmNvbnRyYXIgZWwgZXN0YWRvIGNvbiBtYXlvciBmdXR1cm8geSB2b2x1bWVuIGRlIHZlbnRhcyBpbnRlcm5hY2lvbmFsZXMgZGUgYXV0b3BhcnRlcy4NCg0KYGBge3J9DQp2ZW50YXNpX2F1dG9wYXJ0ZXMgPC0gcmVhZC5jc3YoIlZlbnRhc0FQLmNzdiIpDQpzdW1tYXJ5KHZlbnRhc2lfYXV0b3BhcnRlcykNCmBgYA0KDQojIyDCv1F1ZSBtb2RpZmljYWNpb25lcyBsZSBoYXJlbW9zIGEgbGEgYmFzZSBkZSBkYXRvcz8NCkVuIGV4Y2VsIHJlYWxpemFtb3MgbGltcGllemEgZGUgZGF0b3MgY29uOiBub21icmVzX2RlX3ZhcmlhYmxlcywgYcOxYWRpciBjaWZyaWFzIGFudWFsZXMgeSB1bmlyIGJhc2VzIGRlIGRhdG9zLiBFc3RvIGNvbiBsYSBmaW5hbGlkYWQgZGUgcmVhbGl6YXIgdW5hIHByZWRpY2Npw7NuIHNvYnJlIGxvcyAqKkVzdGFkb3MgZGUgbGEgUmVww7pibGljYSBjb24gZWwgbWF5b3IgbsO6bWVybyBkZSB2ZW50YXMgaW50ZXJuYWNpb25hbGVzIGRlIGF1dG9wYXJ0ZXMgZW4gTcOpeGljbyoqLiANCg0KDQpFbiBSLCBsYSBsaW1waWV6YSBxdWUgcmVhbGl6YXJlbW9zIHNlcsOhOiBlbGltaW5hciByZWdpc3Ryb3MgZGUgbG9zIHRyaW1lc3RyZXMgMiwgMyB5IDQuIFVzYW1vcyBjb21vIGJhc2UgZWwgdHJpbWVzdHJlIDEgcGFyYSBqdW50YXIgbGFzIGNpZnJhcyBhbnVhbGVzIHkgbm8gcmVwZXRpciByZWdpc3Ryb3MuIA0KDQojIyBFbGltaW5hbW9zIGxhcyBjb2x1bW5hcyBxdWUgbm8gbmVjZXNpdGFyZW1vcyB5IENhbWJpYW1vcyBsb3MgdGlwb3MgZGUgdmFyaWFibGUuIA0KDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCiNGaWx0cmFyIHBvciB0cmltZXN0cmUNCnZhcF8xIDwtIHZlbnRhc2lfYXV0b3BhcnRlcw0KdmFwXzEgPC0gZmlsdGVyKHZhcF8xLCB0cmltZXN0cmUgPT0gMSkNCnZhcF8xDQpgYGANCg0KDQpgYGB7cn0NCiNFbGltaW5hciBjb2x1bW5hcw0KdmFwXzIgPC0gdmFwXzENCnZhcF8yIDwtIHN1YnNldCh2YXBfMixzZWxlY3QgPSAtYyhpZF90cmltZXN0cmUsIHRyaW1lc3RyZSwgaWRfZXN0YWRvLCBpZF9lc3RhZG9fYW5kX2lkX3RyaW1lc3RyZSwgaWRudWV2YSwgdmVudGFzX2F1dG9wYXJ0ZXNfdHJpbWVzdHJlLCBleHBvcnRhY2lvbmVzX3RyaW1lc3RyYWxlcykpDQpzdW1tYXJ5KHZhcF8yKQ0KYGBgDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCiNDYW1iaWFyIHRpcG9zIGRlIHZhcmlhYmxlDQp2YXBfMyA8LSB2YXBfMg0KdmFwXzMkdmVudGFzX2F1dG9wYXJ0ZXNfYW51YWwgPC0gYXMubnVtZXJpYyh2YXBfMyR2ZW50YXNfYXV0b3BhcnRlc19hbnVhbCkNCnZhcF8zJGVjaSA8LSBhcy5udW1lcmljKHZhcF8zJGVjaSkNCnZhcF8zJHBvYmxhY2lvbl9vY3VwYWRhX2Vuc2FtYmxhZG9yYV95ZWFyIDwtIGFzLm51bWVyaWModmFwXzMkcG9ibGFjaW9uX29jdXBhZGFfZW5zYW1ibGFkb3JhX3llYXIpDQp2YXBfMyRleHBvcnRhY2lvbmVzX2FudWFsIDwtIGFzLm51bWVyaWModmFwXzMkZXhwb3J0YWNpb25lc19hbnVhbCkNCnZhcF8zJGllZGFudWFsX3BvcmVzdGFkbyA8LSBhcy5udW1lcmljKHZhcF8zJGllZGFudWFsX3BvcmVzdGFkbykNCnZhcF8zJHllYXIgPC0gYXMubnVtZXJpYyh2YXBfMyR5ZWFyKQ0KdmFwXzMkZXN0YWRvIDwtIGFzLmNoYXJhY3Rlcih2YXBfMyRlc3RhZG8pDQp2YXBfMw0KYGBgDQoNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KdmFwXzQgPC0gdmFwXzMNCnZhcF80IDwtIHZhcF80ICU+JQ0KICBncm91cF9ieShlc3RhZG8pICU+JQ0KICBzdW1tYXJpc2UodmVudGFzID0gc3VtKHZlbnRhc19hdXRvcGFydGVzX2FudWFsKSkNCnZhcF80DQpgYGANCg0KYGBge3J9DQp2YXBfNyA8LSB2YXBfMw0KYGBgDQoNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KdG9wXzUgPC0gdmFwXzQgJT4lDQogIGZpbHRlcihyYW5rKGRlc2ModmVudGFzKSk8PTUpDQp0b3BfNQ0KYGBgDQoNCkVuY29udHJhciBlbCB0b3AgNSBkZSBsb3MgZXN0YWRvcyBjb24gbWF5b3IgbnVtZXJvIGRlIHZlbnRhcyBpbnRlcm5hY2lvbmFsZXMgZGUgYXV0b3BhcnRlcyB5IGVuZm9jYXJub3MgZW4gZWxsb3MuDQoNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KI0VsaW1pbmFyIHJlZ2lzdHJvcyBkZSBlc3RhZG9zIG5vIHJlbGV2YW50ZXMNCm51ZXZhIDwtIHZhcF8zDQpudWV2YSA8LSBmaWx0ZXIobnVldmEsICFlc3RhZG89PSJEdXJhbmdvIikNCm51ZXZhIDwtIGZpbHRlcihudWV2YSwgIWVzdGFkbz09IkhpZGFsZ28iKQ0KbnVldmEgPC0gZmlsdGVyKG51ZXZhLCAhZXN0YWRvPT0iSmFsaXNjbyIpDQpudWV2YSA8LSBmaWx0ZXIobnVldmEsICFlc3RhZG89PSJFc3RhZG8gZGUgTWV4aWNvIikNCm51ZXZhIDwtIGZpbHRlcihudWV2YSwgIWVzdGFkbz09Ik1vcmVsb3MiKQ0KbnVldmEgPC0gZmlsdGVyKG51ZXZhLCAhZXN0YWRvPT0iSGlkYWxnbyIpDQpudWV2YSA8LSBmaWx0ZXIobnVldmEsICFlc3RhZG89PSJRdWludGFuYSBSb28iKQ0KbnVldmEgPC0gZmlsdGVyKG51ZXZhLCAhZXN0YWRvPT0iU29ub3JhIikNCm51ZXZhIDwtIGZpbHRlcihudWV2YSwgIWVzdGFkbz09IlphY2F0ZWNhcyIpDQpudWV2YSA8LSBmaWx0ZXIobnVldmEsICFlc3RhZG89PSJZdWNhdGFuIikNCm51ZXZhIDwtIGZpbHRlcihudWV2YSwgIWVzdGFkbz09IlRsYXhjYWxhIikNCm51ZXZhIDwtIGZpbHRlcihudWV2YSwgIWVzdGFkbz09IlZlcmFjcnV6IGRlIElnbmFjaW8gZGUgbGEgTGxhdmUiKQ0KbnVldmEgPC0gZmlsdGVyKG51ZXZhLCAhZXN0YWRvPT0iQ29saW1hIikNCm51ZXZhIDwtIGZpbHRlcihudWV2YSwgIWVzdGFkbz09IkJhamEgQ2FsaWZvcm5pYSIpDQpudWV2YSA8LSBmaWx0ZXIobnVldmEsICFlc3RhZG89PSJDaGlodWF1YSIpDQpudWV2YSA8LSBmaWx0ZXIobnVldmEsICFlc3RhZG89PSJQdWVibGEiKQ0KbnVldmEgPC0gZmlsdGVyKG51ZXZhLCAhZXN0YWRvPT0iQWd1YXNjYWxpZW50ZXMiKQ0KbnVldmEgPC0gZmlsdGVyKG51ZXZhLCAhZXN0YWRvPT0iU2FuIEx1aXMgUG90b3NpIikNCm51ZXZhIDwtIGZpbHRlcihudWV2YSwgIWVzdGFkbz09IlRhbWF1bGlwYXMiKQ0KbnVldmEgPC0gZmlsdGVyKG51ZXZhLCAhZXN0YWRvPT0iQ2hpaHVhaHVhIikNCm51ZXZhIDwtIGZpbHRlcihudWV2YSwgIWVzdGFkbz09IkNpdWRhZCBkZSBNZXhpY28iKQ0KDQp1bmlxdWUobnVldmEkZXN0YWRvKQ0KYGBgDQoNCkVsaW1pbmFtb3MgdGFtYmllbiBhIENpdWRhZCBkZSBNZXhpY28gZGViaWRvIGEgcXVlIGVzIHVuIG1lcmNhZG8gZnVlcmEgZGVsIHF1ZSBidXNjYSBGT1JNLg0KDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCnRvcF80IDwtIG51ZXZhDQp0b3BfNA0KYGBgDQpSZWFsaXphcmVtb3MgdW4gbW9kZWxvIGRlIHJlZ3Jlc2lvbiBsaW5lYWwgcGFyYSBsb3MgZXN0YWRvcyBkZSAqKkNvYWh1aWxhKiosICoqR3VhbmFqdWF0byoqLCAqKk51ZXZvIExlb24qKiB5ICoqUXVlcmV0YXJvKiouIEVzdG8gbm9zIHBlcm1pdGlyw6EgZXZhbHVhciBlbCBydW1ibyBkZSBsb3MgZXN0YWRvcyBjb24gbcOhcyB2ZW50YXMgaW50ZXJuYWNpb25hbGVzIGRlIGF1dG9wYXJ0ZXMuIA0KDQpgYGB7cn0NCm1vZGVsMTwtbG0odmVudGFzX2F1dG9wYXJ0ZXNfYW51YWx+ZWNpK3BvYmxhY2lvbl9vY3VwYWRhX2Vuc2FtYmxhZG9yYV95ZWFyK2V4cG9ydGFjaW9uZXNfYW51YWwraWVkYW51YWxfcG9yZXN0YWRvLGRhdGE9dG9wXzQpICAgICANCnN1bW1hcnkobW9kZWwxKQ0KYGBgDQpQb2RlbW9zIGlkZW50aWZpY2FyIGxhcyB2YXJpYWJsZXMgYSB1dGlsaXphciBkZW50cm8gZGVsIG1vZGVsby4gICANCg0KKip2ZW50YXNfYXV0b3BhcnRlc19hbnVhbCoqOiBlcyBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSBxdWUgbm9zIGF5dWRhcsOhIGEgcmVzcG9uZGVyIGxhIHByZWd1bnRhIHByaW5jaXBhbCBzb2JyZSBsYXMgdmVudGFzIHBvciBlc3RhZG8uICAgDQoNCioqZWNpKio6IHZhcmlhYmxlIGV4cGxvcmF0b3JpYSBwYXJhIGNvbXBhcmFyIGxvcyBlc3RhZG9zIGNvbiBtYXlvcmVzIG5pdmVsZXMgZGUgaW5ncmVzb3MsIHBvdGVuY2lhbCBkZSBjcmVjaW1pZW50byBlY29uw7NtaWNvLCBtZW5vciBkZXNpZ3VhbGRhZCBkZSBpbmdyZXNvcyB5IG1lbm9yZXMgZW1pc2lvbmVzLiAgDQoNCioqcG9ibGFjaW9uX29jdXBhZGFfZW5zYW1ibGFkb3JhX3llYXIqKjogdmFyaWFibGUgZXhwbG9yYXRvcmlhIHF1ZSBzZSByZWZpZXJlIGEgbGFzIHBlcnNvbmFzIGRlbnRybyBkZWwgZXN0YWRvIHF1ZSB0cmFiYWphbiBkaWVyZWN0YW1lbnRlIGRlbnRybyBkZSBsYSBpbmR1c3RyaWEgZGUgYXV0b3BhcnRlcyBlbiBlbCByZXNwZWNpdm8gYcOxby4gICANCg0KKipleHBvcnRhY2lvbmVzX2FudWFsKio6IHZhcmlhYmxlIGV4cGxvcmF0b3JpYSBxdWUgc2UgcmVmaWVyZSBhIGVsIG7Dum1lcm8gZGUgZXhwb3J0YWNpb25lcyBhbnVhbGVzIGRlbCBlc3RhZG8gZW4gdG9kYXMgbGFzIGluZHVzdHJpYXMuICAgDQoNCioqaWVkYW51YWxfcG9yZXN0YWRvbCoqOiB2YXJpYWJsZSBleHBsYW5hdG9yaWEgcXVlIHNlIHJlZmllcmUgYSBsYSBpbnZlcnNpb24gZXh0cmFuamVyYSBkaXJlY3RhIGFudWFsIGRlbCBlc3RhZG8sIG5vIG5lY2VzYXJpYW1lbnRlIGVuZm9jYWRhIGVuIGxhIGluZHVzdHJpYSBhdXRvbW90cml6LiANCg0KDQojIyBNb2RlbG8gbGluZWFsIHBvciBlc3RhZG8gDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KY29haHVpbGEgPC0gZmlsdGVyKHRvcF80LCBlc3RhZG8gPT0gIkNvYWh1aWxhIGRlIFphcmFnb3phIikNCmNvYWh1aWxhIDwtIHN1YnNldChjb2FodWlsYSwgc2VsZWN0ID0gLWMoZXN0YWRvKSkNCmNvYWh1aWxhDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCm51ZXZvbGVvbiA8LSBmaWx0ZXIodG9wXzQsIGVzdGFkbyA9PSAiTnVldm8gTGVvbiIpDQpudWV2b2xlb248LSBzdWJzZXQobnVldm9sZW9uLCBzZWxlY3QgPSAtYyhlc3RhZG8pKQ0KbnVldm9sZW9uDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmd1YW5hanVhdG8gPC0gZmlsdGVyKHRvcF80LCBlc3RhZG8gPT0gIkd1YW5hanVhdG8iKQ0KZ3VhbmFqdWF0bzwtIHN1YnNldChndWFuYWp1YXRvLCBzZWxlY3QgPSAtYyhlc3RhZG8pKQ0KZ3VhbmFqdWF0bw0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpxdWVyZXRhcm8gPC0gZmlsdGVyKHRvcF80LCBlc3RhZG8gPT0gIlF1ZXJldGFybyIpDQpxdWVyZXRhcm88LSBzdWJzZXQocXVlcmV0YXJvLCBzZWxlY3QgPSAtYyhlc3RhZG8pKQ0KcXVlcmV0YXJvDQpgYGANCg0KYGBge3J9DQptb2RlbDFjb2FodWlsYTwtbG0odmVudGFzX2F1dG9wYXJ0ZXNfYW51YWx+ZWNpK3BvYmxhY2lvbl9vY3VwYWRhX2Vuc2FtYmxhZG9yYV95ZWFyK2V4cG9ydGFjaW9uZXNfYW51YWwraWVkYW51YWxfcG9yZXN0YWRvLGRhdGE9Y29haHVpbGEpIA0Kc3VtbWFyeShtb2RlbDFjb2FodWlsYSkNCmBgYA0KQ29haHVpbGE6IEFkanVzdGVkIFItc3F1YXJlZDogIDAuNTMxNw0KDQoNCmBgYHtyfQ0KbW9kZWwxbnVldm9sZW9uPC1sbSh2ZW50YXNfYXV0b3BhcnRlc19hbnVhbH5lY2krcG9ibGFjaW9uX29jdXBhZGFfZW5zYW1ibGFkb3JhX3llYXIrZXhwb3J0YWNpb25lc19hbnVhbCtpZWRhbnVhbF9wb3Jlc3RhZG8sZGF0YT1udWV2b2xlb24pIA0Kc3VtbWFyeShtb2RlbDFudWV2b2xlb24pDQpgYGANCk51ZXZvIExlb246IEFkanVzdGVkIFItc3F1YXJlZDogIDAuODI0MiANCg0KYGBge3J9DQptb2RlbDFndWFuYWp1YXRvPC1sbSh2ZW50YXNfYXV0b3BhcnRlc19hbnVhbH5lY2krcG9ibGFjaW9uX29jdXBhZGFfZW5zYW1ibGFkb3JhX3llYXIrZXhwb3J0YWNpb25lc19hbnVhbCtpZWRhbnVhbF9wb3Jlc3RhZG8sZGF0YT1ndWFuYWp1YXRvKSANCnN1bW1hcnkobW9kZWwxZ3VhbmFqdWF0bykNCmBgYA0KR3VhbmFqdWF0bzpBZGp1c3RlZCBSLXNxdWFyZWQ6ICAwLjU5MDQNCg0KYGBge3J9DQptb2RlbDFxdWVyZXRhcm88LWxtKHZlbnRhc19hdXRvcGFydGVzX2FudWFsfmVjaStwb2JsYWNpb25fb2N1cGFkYV9lbnNhbWJsYWRvcmFfeWVhcitleHBvcnRhY2lvbmVzX2FudWFsK2llZGFudWFsX3BvcmVzdGFkbyxkYXRhPXF1ZXJldGFybykgDQpzdW1tYXJ5KG1vZGVsMXF1ZXJldGFybykNCmBgYA0KUXVlcmV0YXJvOiBBZGp1c3RlZCBSLXNxdWFyZWQ6ICAwLjY4NiANCg0KYGBge3J9DQpsaWJyYXJ5KGp0b29scykNCmVmZmVjdF9wbG90KG1vZGVsMW51ZXZvbGVvbixwcmVkPXZlbnRhc19hdXRvcGFydGVzX2FudWFsLGludGVydmFsPVRSVUUpDQpgYGANCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0c2VyaWVzKSAgICMgdGltZSBzZXJpZXMgYW5hbHlzaXMgYW5kIGNvbXB1dGF0aW9uYWwgZmluYW5jZSANCmxpYnJhcnkoZm9yZWNhc3QpICAjIHByb3ZpZGVzIG1ldGhvZHMgYW5kIHRvb2xzIGZvciBkaXNwbGF5aW5nIGFuZCBhbmFseXppbmcgdW5pdmFyaWF0ZSB0aW1lIHNlcmllcyBmb3JlY2FzdA0KbGlicmFyeShhc3RzYSkgICAgICMgYXBwbGllZCBzdGF0aXN0aWNhbCB0aW1lIHNlcmllcyBhbmFseXNpcyANCmBgYA0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuID05OTkpDQpwbG90KG51ZXZvbGVvbiR5ZWFyLG51ZXZvbGVvbiR2ZW50YXNfYXV0b3BhcnRlc19hbnVhbCwgdHlwZT0ibCIsY29sPSJibHVlIiwgbHdkPTEuNSwgeGxhYiA9ImHDsW8iLHlsYWIgPSJWZW50YXMgZW4gZG9sYXJlcyIsIG1haW4gPSAiVmVudGFzIEludGVybmFjaW9uYWxlcyBkZSBOdWV2byBMZW9uIGVuIEF1dG9wYXJ0ZXMgIikNCmxpbmVzKG51ZXZvbGVvbiR2ZW50YXNfYXV0b3BhcnRlc19hbnVhbCxjb2FodWlsYSR2ZW50YXNfYXV0b3BhcnRlc19hbnVhbCwgY29sPSJyZWQiLGx0eT0zKQ0KbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kPWMoIk51ZXZvIExlb24iLCAiQ29haHVpbGEiKSwNCiAgICAgICBjb2w9YygiYmx1ZSIsICJyZWQiKSwgbHR5ID0gMToyLCBjZXg9MC44KSANCmBgYA0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuID05OTkpDQpjb2xvcnMgPC0gYygiY29haHVpbGEiPSJncmVlbiIsICJudWV2b2xlb24iPSJvcmFuZ2UiLCAicXVlcmV0YXJvIj0iYmx1ZSIsICJndWFuYWp1YXRvIj0icmVkIikNCmdncGxvdChkYXRhPWNvYWh1aWxhLCBhZXMoeD15ZWFyLCB5PXZlbnRhc19hdXRvcGFydGVzX2FudWFsKSwgY29sb3I9J3B1cnBsZScpICsgDQpnZW9tX2xpbmUoKSArIA0KZ2VvbV9saW5lKGRhdGE9bnVldm9sZW9uLCBhZXMoeD15ZWFyLCB5PXZlbnRhc19hdXRvcGFydGVzX2FudWFsKSwgY29sb3I9J29yYW5nZScpICsgDQpnZW9tX2xpbmUoKSArIA0KZ2VvbV9saW5lKGRhdGE9cXVlcmV0YXJvLCBhZXMoeD15ZWFyLCB5PXZlbnRhc19hdXRvcGFydGVzX2FudWFsKSwgY29sb3I9J2JsdWUnKSsNCmdlb21fbGluZSgpICsgDQpnZW9tX2xpbmUoZGF0YT1ndWFuYWp1YXRvLCBhZXMoeD15ZWFyLCB5PXZlbnRhc19hdXRvcGFydGVzX2FudWFsKSwgY29sb3I9J3JlZCcpKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKCIiLCANCiAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJDb2FodWlsYSIsICJOdWV2byBMZW9uIiwgIlF1ZXJldGFybyIsICJHdWFuYWp1YXRvIiksDQogICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiZ3JlZW4iLCAib3JhbmdlIiwgImJsdWUiLCAicmVkIikpKw0KICBsYWJzKHg9IlllYXIiLCB5PSJUb3RhbCBWZW50YXMgRG9sYXJlcyIpDQpgYGANCg0KDQojIFByb27Ds3N0aWNvIGRlbCBEZXNlbXBlw7FvIGRlIGxhIEluZHVzdHJpYSBBdXRvbW90cml6IHkgbGEgZW1wcmVzYSBGT1JNDQoNCiMjIE1vdmluZyBBdmVyYWdlDQpgYGB7cn0NCg0Kc3VtbWFyeShtb2RlbDFudWV2b2xlb248LWFybWEobnVldm9sZW9uJHZlbnRhc19hdXRvcGFydGVzX2FudWFsLG9yZGVyPWMoMCwxKSkpDQpudWV2b2xlb25fZm9yZWNhc3Q8LWZvcmVjYXN0KG1vZGVsMW51ZXZvbGVvbiRmaXR0ZWQsaD01LGxldmVsPWMoOTUpKQ0KbnVldm9sZW9uX2ZvcmVjYXN0DQpgYGANCg0KIyBJZGVudGlmaWNhY2nDs24geSBEZXNjcmlwY2nDs24gZGUgbG9zIFByaW5jaXBhbGVzIFJlc3VsdGFkb3MNCiMjIE1lcm1hIA0KUmVhbGl6YW5kbyB1biBhbsOhbGlzaXMgZGUgbGEgYmFzZSBkZSBkYXRvcyBtZXJtYSBub3MgZGltb3MgY3VlbnRhIHF1ZSBsb3MgbWVzZXMgY29uIG1heW9yIHByb2R1Y2Npw7NuIGRlIG1lcm1hIHNvbiBsb3MgbWVzZXMgZGUgYWdvc3RvIGNvbiAzMiw1MDAga2lsb3MgZGUgbWVybWEgeSBtYXlvIGNvbiAyMiw3MDAga2lsb3MgZGUgbWVybWEsIGVuIGJhc2UgYSBlc3RvIHBvZGVtb3MgaW5mZXJpciBxdWUgZW4gZXN0b3MgbWVzZXMgZXMgZG9uZGUgbGEgZW1wcmVzYSBGT1JNIHRpZW5lIG1heW9yIHByb2R1Y2Npw7NuLiBEZSBpZ3VhbCBtYW5lcmEgc2UgaWRlbnRpZmljYXJvbiBsb3MgbWVzZXMgZG9uZGUgbGEgZW1wcmVzYSBwcm9kdWPDrWEgbWVub3MgbWVybWEsIGxvcyBjdWFsZXMgZnVlcm9uIGxvcyBtZXNlcyBkZSBlbmVybyBjb24gMTQsODAwIGtpbG9zIGRlIG1lcm1hIHkgc2VwdGllbWJyZSBjb24gMTIsNDAwIGtpbG9zIGRlIG1lcm1hLCBwb3IgbG8gcXVlIHRhbWJpw6luIHBvZGVtb3MgaW5mZXJpciBxdWUgZW4gZXN0b3MgbWVzZXMgRk9STSBubyB0aWVuZSBtdWNoYSBwcm9kdWNjacOzbi4NCg0KIyMgU2NyYXANCkFuYWxpemFuZG8gbG9zIHJlc3VsdGFkb3MgZGUgbGEgYmFzZSBkZSBkYXRvcyBTY3JhcCwgZWwgY3VhbCBzZSByZWZpZXJlIGEgdG9kb3MgbG9zIGRlc2VjaG9zIHkvbyByZXNpZHVvcyBkZXJpdmFkb3MgZGVsIHByb2Nlc28gaW5kdXN0cmlhbCwgc2Ugb2JzZXJ2YSBxdWUgU0FCL1ByZS1Qcm9kdWN0aW9uIGVzIGVsIGx1Z2FyIGRvbmRlIG3DoXMgY2FudGlkYWQgZXhpc3RlIGNvbiB1biB2YWxvciB0b3RhbCBkZSAxNTAwIHRvbmVsYWRhcy4gDQoNCiMjIFByb2R1Y2Npw7NuIGRlIGNhcnTDs24NCkxhcyBncsOhZmljYXMgZGUgZGlzcGVyc2nDs24gbm9zIGluZGljYW4gcXVlIGNvbiBlbCBjbGllbnRlIFlBTkZFTkcgZXMgbGEgdmV6IHF1ZSBtw6FzIHNlIGhhIGxsZXZhZG8gbcOhcyB0aWVtcG8gZGUgY2FsaWRhZCwgcG9kZW1vcyBhbmFsaXphciBlbCB0aWVtcG8gcXVlIHNlIGxsZXbDsyBhIGNhYm8gY29uIHVuIGVtcGFxdWUgeSBzaSBsYSBwcm9kdWNjacOzbiBkZWwgbWlzbW8gwr9lcyByZW50YWJsZSBwYXJhIGxhIGVtcHJlc2E/IG8gc2kgbG8gbWVqb3IgZXMgbm8gYWNlcHRhciBwcm9kdWNpciBlc3RlIHRpcG8gZGUgcHJvZHVjdG8uIA0KDQpQb3Igb3RybyBsYWRvIGNvbiBsYXMgc2VyaWVzIGRlIHRpZW1wbywgb2JzZXJ2YW1vcyBxdWUgY29uIEhhbm9uIFN5c3RlbXMgZXMgY29uIHF1aWVuIG3DoXMgc2UgaGEgdG9tYWRvIHRpZW1wbyBlbiBlbCBwcm9jZXNvLiBQb2RlbW9zIHByZWd1bnRhcm5vcyBzaSBlcyBjb252ZW5pZW50ZSBsbGV2YXIgcHJvY2Vzb3MgZGUgcHJvZHVjY2nDs24gdGFuIGxhcmdvcyBjb24gdW4gY2xpZW50ZSB5IGV2YWx1YXIgc3UgcmVudGFiaWxpZGFkLiANCg0KIyMgRGVsaXZlcnkgUGVyZm9ybWFuY2UNCkVuIERlbGl2ZXJ5IFBlcmZvcm1hbmNlIGhlbW9zIHBvZGlkbyBpZGVudGlmaWNhciBxdWUgTUFITEUgdGllbmUgbWF5b3IgZGVsYXkgcXVlIFBSSU5URUwsIHBvciBsbyBxdWUgcG9kcsOtYSBkZWNpcm5vcyBxdWUgYWxnbyBzdWNlZGUgY29uIE1BSExFIHF1ZSBvY2FzaW9uYSBxdWUgc2UgcmV0cmFzZSBtw6FzIHF1ZSBvdHJvcyBjbGllbnRlcy4gSGFicsOtYSBxdWUgdG9tYXIgbWVkaWRhcyBjb3JyZWN0aXZhcy4NCg0KIyMgRGVsaXZlcnkgUGxhbg0KQWwgYW5hbGl6YXIgbG9zIHJlc3VsdGFkb3MsIHNlIG9idHV2byBxdWUgZWwgbWVzIGNvbiBtw6FzIHBpZXphcyBwcm9ncmFtYWRhcyBmdWUgSnVsaW8gZGUgMjAyMiwgc2VndWlkbyBkZSBTZXB0aWVtYnJlIHkgTWF5by4gQWRlbcOhcyBzZSBwdWVkZSBvYnNlcnZhciB1bmEgY2HDrWRhIHNpZ25pZmljYXRpdmEgZGUgcGllemFzIHByb2dyYW1hZGFzIGVudHJlIGVsIG1lcyBkZSBqdWxpbyBhIHNlcHRpZW1icmUsIHlhIHF1ZSBlbiBqdWxpbyBoYXkgbcOhcyBkZSA3MiwwMDAgcGllemFzIHByb2dyYW1hZGFzIHkgcGFyYSBzZXB0aWVtYnJlIGVzZSB2YWxvciBkZWNyZWNpw7MgY29uIDEwIG1pbCB1bmlkYWRlcyBtZW5vcy4gDQoNCiMjIEJhamFzDQpFbCBwcmluY2lwYWwgbW90aXZvIGRlIGxhcyBiYWphcyBmdWUgcG9yIHJlbnVuY2lhcywgZXN0ZSBlcyB1biBkYXRvIG11eSBpbnRlcmVzYW50ZSBzaW4gZW1iYXJnbyBubyBzZSBwdWVkZSBsbGVnYXIgYSBoYWNlciB1bmEgYnVlbmEgaW50ZXJwcmV0YWNpw7NuIHlhIHF1ZSBtdWNob3MgZGUgbG9zIGV4IGVtcGxlYWRvcyBmdWVyb24gZGFkb3MgZGUgYmFqYSBzb2xvIHBvciBmYWx0YXIgdW4gZMOtYS4gIEVsIHNlZ3VuZG8gbW90aXZvIGRlIGxhcyBiYWphcyBmdWUgcG9yIHJlbnVuY2lhIHZvbHVudGFyaWEgeSBtdWNoYXMgZGUgZXN0YXMgcGVyc29uYXMgbm8gcmVudW5jaWFyb24gc2luIGNvbXBsaXIgZWwgYcOxbyBlbiBsYSBlbXByZXNhLg0KRXMgaW1wb3J0YW50ZSBxdWUgbGEgZW1wcmVzYSByZWN1ZXJkZSBiaWVuIGxhIGluZm9ybWFjacOzbiBzb2JyZSBsYXMgYmFqYXMgeSBzdXMgbW90aXZvcyBwYXJhIGRlIGVzdGEgbWFuZXJhIGdlbmVyYXIgdW5hIGJ1ZW5hIGVzdHJhdGVnaWEgZW4gZGlzbWludWlyIGxhIHJvdGFjacOzbiBkZSBlbXBsZWFkb3MsIHlhIHF1ZSwgbGEgbWF5b3LDrWEgZGUgbG9zIGVtcGxlYWRvcyBzb2xvIGR1cmEgdW4gYcOxbyBvIG1lbm9zIGVuIGxhIGVtcHJlc2EuDQoNCiMjIENvbGFib3JhZG9yZXMNCkxhIHBlcnNvbmEgcXVlIG3DoXMgc2FsYXJpbyB0aWVuZW4gc29uIGxhIHN1cGVydmlzb3JhIG11amVyZXMgeSBsb3MgZGUgbWFudGVuaW1pZW50bywgc2kgRk9STSBuZWNlc2l0YSB1biByZWNvcnRlIGVuIGdhc3RvcyBzZSBmaWphcsOtYSBwcmltZXJhbWVudGUgZW4gZXN0YXMgcGVyc29uYXMuIA0KDQojIyBWZW50YXMgSW50ZXJuYWNpb25hbGVzIGRlIEF1dG9wYXJ0ZXMgcG9yIEVzdGFkbyAoRGF0b3MgZXh0ZXJub3MpDQpFbCBlc3RhZG8gZGUgQ29haHVpbGEgc2UgbWFudGllbmUgY29tbyBlbCBsw61kZXIgZW4gdmVudGFzIGRlIGF1dG9wYXJ0ZXMgeSBlbiBlbCAyMDIyIHR1dm8gdW4gZGVzcHVudGUgZW4gbGFzIHZlbnRhcy4gIA0KLSBRdWVyw6l0YXJvIHkgR3VhbmFqdWF0byBzaWd1ZW4gbWFudGVuaWVuZG8gdW4gY3JlY2ltaWVudG8gZW4gZWwgdm9sw7ptZW4gZGUgdmVudGFzLiAgDQotIE51ZXZvIExlw7NuIHNlIGVzdMOhIHJlY3VwZXJhbmRvIGRlbCBkZWNsaXZlIHF1ZSB0dXZvIGVuIDIwMTguICANCi0gRW4gZ2VuZXJhbCBsYSBpbmR1c3RyaWEgZW4gTcOpeGljbyBlc3TDoSBjcmVjaWVuZG8uDQoNCg0KIyBSZWZsZXhpw7NuIEZpbmFsDQoNCiMjIMK/UXXDqSBlcyBCdXNpbmVzcyBBbmFseXRpY3M/ICAgDQpFcyBlbCBwcm9jZXNvIGRlIHJlY29sZWNjacOzbiBkZSBkYXRvcyBtZWRpYW50ZSBkaWZlcmVudGVzIG1vZGVsb3MgcHJlZGljdGl2b3MgeSBlc3RhZMOtc3RpY29zIGNvbiBlbCBmaW4gZGUgYW5hbGl6YXIgbGEgc2l0dWFjacOzbiBhY3R1YWwgZGUgbGEgZW1wcmVzYSBwYXJhIHByZWRlY2lyIGV2ZW50bywgdGVuZGVuY2lhcyB5IGNvbXBvcnRhbWllbnRvcyBhIGZ1dHVybyBxdWUgYXl1ZGVuIGEgZGV0ZXJtaW5hciBsYSB0b21hIGRlIGRlY2lzaW9uZXMsIHJlZHVjaWVuZG8gbG9zIHJpZXNnb3MgZGUgZXF1aXZvY2FjaW9uZXMgZnV0dXJhcy4NCiANCiMjIMK/RGVzY3JpYmlyIGJyZXZlbWVudGUgMyBvYmpldGl2b3MgZGVMIHVzbyBkZSBsYSBoZXJyYW1pZW50YSBkZSBCdXNpbmVzcyBBbmFseXRpY3M/IA0KLSBEaXNlw7FhciB5IGVqZWN1dGFyIHByb3llY3RvcyBkZSBhbsOhbGlzaXMgZGUgZGF0b3MuICANCi0gUmVzcG9uZGVyIGEgbmVjZXNpZGFkZXMgZW1wcmVzYXJpYWxlcyB1dGlsaXphbmRvIGVsIGFuw6FsaXNpcyBkZSBkYXRvcyB5IGxhIGludGVsaWdlbmNpYSBhcnRpZmljaWFsLiAgDQotIENvbXVuaWNhciBkZSBmb3JtYSBlZmljaWVudGUgbG9zIHJlc3VsdGFkb3Mgb2J0ZW5pZG9zLiAgDQotIE9idGVuZXIgY29ub2NpbWllbnRvcyB0ZWNub2zDs2dpY29zIGVuIGVsIMOhbWJpdG8gZGUgbG9zIGRhdG9zLiAgDQotIE9idGVuZXIgY29ub2NpbWllbnRvcyBhbmFsw610aWNvcyBhcGxpY2Fkb3MgYWwgw6FtYml0byBkZSBsb3MgZGF0b3MgKGVzdGFkw61zdGljYSwgbWF0ZW3DoXRpY2FzIHkgbW9kZWxpemFjacOzbiBtYXRlbcOhdGljYSwgZW50cmUgb3Ryb3MpICANCi0gT2J0ZW5lciBjb25vY2ltaWVudG9zIGVuIGRpZmVyZW50ZXMgw6FtYml0b3MgZW1wcmVzYXJpYWxlcyAoZWNvbm9tw61hLCBmaW5hbnphcywgbWFya2V0aW5nLCBvcGVyYWNpb25lcywgc2FsdWQsIGluZHVzdHJpYSwgZXRjLikNCg0KIyMgwr9DdcOhbCBlcyBsYSByZWxhY2nDs24gZW50cmUgQnVzaW5lc3MgQW5hbHl0aWNzIHkgQnVzaW5lc3MgSW50ZWxsaWdlbmNlPyANCkNvbiBCdXNpbmVzcyBJbnRlbGxpZ2VuY2Ugc2UgcHVlZGUgb3B0aW1pemFyLCBldmFsdWFyIHkgY29vcmRpbmFyIGxhcyBhY3RpdmlkYWRlcyBpbnRlcm5hcyBkZSB1bmEgZW1wcmVzYSwgbWllbnRyYXMgcXVlIGNvbiBCdXNpbmVzcyBBbmFseXRpY3MgZSBwb25lbiBlbiBtYXJjaGEgc29sdWNpb25lcyBlbXByZXNhcmlhbGVzIGNvbiBsYXMgcXVlIHNhdGlzZmFjZXIgbGFzIG5lY2VzaWRhZGVzIGRlIHVuIG5lZ29jaW8uDQoNClNlIHB1ZWRlIGRlY2lyIHF1ZSBlbCBCdXNpbmVzcyBJbnRlbGxpZ2VuY2Ugc2UgcHJlb2N1cGEgcG9yIGVsIHF1w6kgeSBlbCBjw7NtbywgeSBlbCBCdXNpbmVzcyBBbmFseXRpY3Mgc8OtIHNlIHByZWd1bnRhIHBvciBxdcOpIHBhc2FuIGxhcyBjb3NhcyBwYXJhIHBvZGVyIHByZWRlY2lyIGZ1dHVyb3MgY29tcG9ydGFtaWVudG9zIG8gdGVuZGVuY2lhcy4NCg==