Limpieza, Transformación, y Organización de Bases de Datos

Librerias Utilizadas

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

merma <- read.csv("/Users/anita3/Desktop/FORM/FORM - Merma.csv")
scrap <- read.csv("/Users/anita3/Desktop/FORM/FORM - Scrap.csv")
carton <- read.csv("/Users/anita3/Desktop/FORM/FORM - Produccion Carton Completa.csv")
perf <- read.csv("/Users/anita3/Desktop/FORM/Del Perf.csv")
plan <- read.csv("/Users/anita3/Desktop/FORM/DP_1.csv")
bajas <- read.csv("/Users/anita3/Desktop/FORM/RH.csv")
colaboradores <- read.csv("/Users/anita3/Desktop/FORM/RH2.csv")

Técnicas de Limpieza

Merma

Cambiar nombre de variables

merma <- clean_names(merma)
colnames(merma)
## [1] "fecha" "mes"   "kilos"

Mantener variables relevantes para el análisis
Para la base de datos de merma se opto por mantener las tres variables, ya que todas las variables son relevantes para el analsis.

Eliminar filas que contienen el total de los meses

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

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" ...

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

Cambiar nombre de variables

scrap<-scrap %>% dplyr::rename(referencia=Referencia,
                               ubi_origen=Ubicación.de.origen,
                               ubi_desecho=Ubicación.de.desecho)
scrap<-clean_names(scrap)
colnames(scrap)
## [1] "referencia"       "fecha"            "producto"         "cantidad"        
## [5] "unidad_de_medida" "ubi_origen"       "ubi_desecho"      "estado"

Mantener variables relevantes para el análisis
Para la base de datos de scrap se opto 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 que 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"

Eliminar 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

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" ...

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 Carton

Cambiar nombre de variables

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=Fecha)
carton<-clean_names(carton)
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"

Mantener variables relevantes para el análisis
Para la base de datos de producción de cartón se ha optado por eliminar las columnas de id_form, producto, tiempo_materiales y estación de arranque; ya que con el resto de las columnas se puede realizar otros calculos como 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"

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" ...

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 (Producción de Carton) hay otros elementos a considerar, es por eso que se reviso cada columna y los registros que la conforman

Fecha
No hay valores faltantes en esta comluna, sin embargo hay que cambiar el formato de la variable a formato de fecha para que despues nos permita realizar operaciones y encontrar otros datos.

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´s, 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 el tipo de variable.

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

He optado por, 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
Se encontraron registros con NA´s, de opto por eliminar estos registros para tener un mejor analisis. 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´s 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
Se eliminaron los registros con NA´s. Otro cambio a realizar es convertir el formato a entero.

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. Se eliminaran aquellos registros 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
Cambiar el formato de tmo_min a 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

Cambiar nombre de variables

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

Mantener variables relevantes para el análisis
Para delivery performance se opto por mantener todas las columnas.

En esta base de datos (Delivery Performance) hay que verificar que las columnas estén en el formato correcto además de identificar valores como N/A, valores atípicos, entre otros. Se revisara columna por columna.

Cliente
Como podemos observar en cliente 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 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
En esta variable no hay valores faltantes. Sin embargo la columna de fecha está como caracter, por lo que hay que hacer el cambio de formato a fecha.

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. Así mismo que hay valores en formato de 24 horas y hay que pasarlos a formato de 12 horas.

perf <-  perf %>% mutate(real_arrival=ifelse(real_arrival > 12, real_arrival-12, real_arrival))
perf <-  perf %>% mutate(real_departure=ifelse(real_departure > 12, real_departure-12, real_departure))
perf <- filter(perf, perf$real_arrival>0 & perf$real_departure>0)
unique(perf$real_arrival)
##  [1]  8.00  9.00  4.30  4.10  4.00  4.50  6.20  5.00  4.20  5.20  4.35  4.05
## [13]  8.18  4.15  8.05  9.30  9.20  9.15  9.10  3.30  6.15  4.25  4.40  3.36
## [25]  9.45  3.40  9.06  9.03  9.27  5.35  9.05  3.00  3.10  3.20  3.16  3.45
## [37]  9.35 10.08  9.08  7.15  8.40  8.15 11.20 11.00  1.00 11.50  2.00 11.15
## [49]  7.45  1.40 11.30 10.40 10.40 11.37  9.15  9.26  6.00 10.42  8.10  7.30
## [61]  9.10  8.52  8.30  7.34  7.35  9.12 10.05  9.17  7.48  8.20 10.00  8.10
## [73]  7.20  9.11  8.45  9.50  9.40  6.35  6.40  6.05  6.10  6.50  7.10  9.25
## [85]  6.30  5.30
unique(perf$real_departure)
##   [1]  8.55 10.00  9.10  6.10  8.50 10.10  9.25  5.20  9.00  4.50  5.00  9.15
##  [13]  8.51  9.05  6.50  9.45  5.05  5.15  7.55  4.55  9.55  6.20 10.15  5.25
##  [25] 10.05  6.00  5.40  5.30  5.45  9.50  5.50  9.40  5.35 10.30  9.20  9.10
##  [37] 10.45 11.05 10.58 10.25  9.15  9.35  5.58  7.40  5.55  6.25 10.40 10.06
##  [49]  6.31 10.27  7.00  6.30  6.35 10.20  7.30  4.45  3.55  4.00  4.10  4.35
##  [61]  4.40  4.30 10.35 10.55  5.38 10.10  8.00 10.50 11.35 11.00  9.40 12.15
##  [73] 12.00 11.50  6.08  1.45 12.50  6.40 12.35  2.40 11.15  8.05  0.40  0.05
##  [85] 12.45 11.55 10.15 10.12  9.35  8.45  9.30  8.40  8.15  8.30  9.58  8.34
##  [97]  8.20  8.40  8.35  8.15 11.30 11.20  7.20  6.40  6.45  7.50  8.10 10.24
## [109]  7.40  7.45  1.10

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

Plan Arrival

Primeramente hay que pasar el formato de 24 horas a 12 horas. Para esto se remplazaron los valores de 20 y 16 a 8 y 4.

perf$plan_arrival <- replace(perf$plan_arrival,perf$plan_arrival==20,8)
perf$plan_arrival <- replace(perf$plan_arrival,perf$plan_arrival==16,4)
unique(perf$plan_arrival)
## [1] 8 9 4 0

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.

sum(is.na(perf$plan_arrival))
## [1] 0

Al observar el valor de skewness, podemos observar que el valor es mayor a 1, (aunque sea negativo su valor supera el 1) por lo que su distribución nos sugiere que es mejor remplazar con la mediana.

describe(perf$plan_arrival)
##    vars   n mean  sd median trimmed mad min max range  skew kurtosis   se
## X1    1 800 7.82 1.5      8    8.16   0   0   9     9 -1.98        3 0.05

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,8,plan_arrival))
unique(perf$plan_arrival)
## [1] 8 4

En 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 7.53 1.29      8    7.91   0   4   8     4 -2.35     3.54 0.05

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
## 2 PRINTEL        1            4
## 3    MAHLE       2            8
## 4    MAHLE       3            8

Como se quiere 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  4

Difference
Esta columna se supone es la diferencia que hay entre real arrival y real departure, sin embargo, como se puede observar 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$plan_arrival <- as.numeric(perf$plan_arrival)
perf$real_arrival <- as.numeric(perf$real_arrival)
perf$real_departure <- as.numeric(perf$real_departure)
perf <- mutate(perf, difference=real_departure-real_arrival)
perf <- dplyr::select(perf,-difference)
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          8.0           9.10
## 4 PRINTEL        1 2021-07-25            4          4.3           6.10
## 5    MAHLE       1 2021-07-25           20          8.0           8.50
##   diference
## 1      0.55
## 2      1.00
## 3      1.10
## 4      1.80
## 5      0.50

Delivery Plan

Cambiar nombre de variables

plan<-clean_names(plan)
plan<-plan %>% dplyr::rename(cliente=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"

Mantener variables relevantes para el análisis
Para delivery plan por fines de estructura y análisis se ha otado 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 se ha 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))
En esta base de datos (Delivery Plan) hay otros elementos a considerar por esto revisaremos cada columna y los registros que la conforman

Cliente Planta
No hay errores ortográficos por lo que no se haran 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 se encontramos errores de inconsistencia.

unique(plan$proyecto)

Item
De igaul manera para item no se realizaron modificaciones.

unique(plan$item)

Mes
Para la columna de mes no se encontraron errores ortográficos por lo que no se haran 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 se opto 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 se corraboro 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

Cambiar nombre de variables

bajas<-clean_names(bajas)
bajas<-bajas %>% dplyr::rename(apellidos=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=n_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"

Mantener variables relevantes para el análisis
Para la base de datos de bajas se opto 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"
En esta base de datos (Bajas) hay otros elementos a considerar por esto revisaremos cada columna y los registros que la conforman

Nombre completo, fecha de nacimiento y fecha de alta
Convertir a formato de fecha la fecha de alta y la fecha 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] "Nuevo León"               "SAN PDRO COAH"           
##  [9] "CADEREYTA"                "SALTILLO"                
## [11] ""                         "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" "Nuevo León" "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)" "Unión libre"

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 se realizara 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 CARTÓN MC"  "STABILUS"             
##  [4] "CELDAS"                ""                      "MATERIALES"           
##  [7] "CEDIS"                 "PRODUCCION CARTÓN MDL" "PRODUCCIÓN RETORN"    
## [10] "TROQUEL"               "MANTENIMIENTO"         "AY.FLEXO"             
## [13] "COSTURA"               "MARCADORA"             "CAJAS"                
## [16] "LAMINADO"              "CALIDAD"               "EMBARQUES"            
## [19] "PAILERIA"              "EHS"                   "CORTADORAS"

Motivo de Baja
Para motivo de baja se realizara 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

Cambiar nombre de variables

colaboradores<-clean_names(colaboradores)
colaboradores<-colaboradores %>% dplyr::rename(no_empleado=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=n_credito_infonavit,
                                 lugar_nac=lugar_de_nacimiento,
                                 num_interno=numero_interno,
                                 CP=codigo_postal)
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"

Mantener variables relevantes para el análisis
Para la base de datos de colaboradores también se opto 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)]
En esta base de datos (Colaboradores) hay otros elementos a considerar por esto revisaremos cada columna y los registros que la conforman

Nombre Completo
A nombre completo no se le haran cambios en la base de datos.

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 se identificaron 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 DE MÁQUIN"            "SUPERVISOR(A)"                  
##  [3] "EXTERNO"                         "CUSTOMER SERVICE INF"           
##  [5] "COSTURERA"                       "AYUDANTE GENERAL"               
##  [7] "GESTOR"                          "CHOFER"                         
##  [9] "LIDER"                           "MANTENIMIENTO"                  
## [11] "RESIDENTE"                       "LIMPIEZA"                       
## [13] "INSPECTOR(A) DE CALIDAD"         "PINTOR"                         
## [15] "SOLDADOR"                        "RECIBO"                         
## [17] "ENFERMERA"                       "OP. FLEXO-RANURADORA-REFILADORA"
## [19] "GUARDIA DE SEGURIDAD"            "MOZO"                           
## [21] "OPERADOR SIERRA"                 "MONTACARGUISTA"

Departamento
Para departamento hay que estandarizar 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 PRODUCCIÓN RETORN      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 MARIANA DE LEON MORENO 1979-05-14 FEMENINO 2011-07-01 SUPERVISOR(A)
## 2  MARIA CAZARES MORALES 1990-05-01 FEMENINO 2013-01-30 SUPERVISOR(A)
## 3    YOLANDA LOPEZ RAMOS 1965-09-06 FEMENINO 2014-05-05 SUPERVISOR(A)
##            departamento sal_diario_imss           colonia municipio     estado
## 1 PRODUCCION CARTÓN MDL          176.72    SANTA TERESITA   APODACA Nuevo León
## 2  PRODUCCION CARTÓN MC          337.05      PUEBLO NUEVO   APODACA Nuevo León
## 3               COSTURA      4413757.00 RINCON DE GALICIA   APODACA Nuevo León
##      CP estado_civil
## 1 66605      Soltero
## 2 66649       Casado
## 3 66620      Soltero

Se identifico 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 LEÓN" "COAHUILA"   "NUEVO LEON"

Estado Civil
Para 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)")

Variables y Registros por base de datos

La base de datos Merma tiene 3 variables y 50 registros.

describe(merma)
## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf
## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf
##       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
dim(merma)
## [1] 50  3

La base de datos Scrap tiene 5 variables y 250 registros.

describe(scrap)
## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf
## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf
##             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
dim(scrap)
## [1] 250   5

La base de datos Producción de Carton tiene 11 variables y 1486 registros.

describe(carton)
## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf
## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf
##                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
dim(carton)
## [1] 1486   11

La base de datos Delivery Performance tiene 7 variables y 800 registros.

describe(perf)
## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf
## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf
##                vars   n  mean   sd median trimmed  mad    min  max range   skew
## cliente*          1 800  1.12 0.32      1    1.02 0.00   1.00  2.0  1.00   2.35
## vueltas           2 800  1.91 0.83      2    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 18.10 5.18     20   19.62 0.00   4.00 20.0 16.00  -2.35
## real_arrival      5 800  7.82 1.65      8    8.12 0.30   1.00 11.5 10.50  -1.63
## real_departure    6 800  8.85 1.63      9    9.11 0.84   0.05 12.5 12.45  -1.72
## diference         7 800  1.02 0.84      1    1.02 0.04 -14.35  4.4 18.75 -12.19
##                kurtosis   se
## cliente*           3.54 0.01
## vueltas           -1.53 0.03
## fecha                NA   NA
## plan_arrival       3.54 0.18
## real_arrival       2.48 0.06
## real_departure     3.89 0.06
## diference        214.85 0.03
dim(perf)
## [1] 800   7

La base de datos Delivery Plan tiene 5 variables y 972 registros.

describe(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.04   26.24   58.0   46.51  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
dim(plan)
## [1] 972   5

La base de datos Bajas tiene 15 variables y 238 registros.

describe(bajas)
## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf

## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf
## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf

## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf
##                  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.82   2.74   2.00    3.41  0.00   1.00   12
## estado*            14 238   3.00   0.27   3.00    3.00  0.00   1.00    4
## estado_civil*      15 238   3.67   1.17   4.00    3.72  1.48   1.00    6
##                    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*         11.00  1.06    -0.37  0.18
## estado*             3.00 -2.45    30.65  0.02
## estado_civil*       5.00 -0.53    -1.04  0.08
dim(bajas)
## [1] 238  15

La base de datos Colaboradores tiene 12 variables y 113 registros.

describe(colaboradores)
## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf

## Warning in FUN(newX[, i], ...): no non-missing arguments to min; returning Inf
## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf

## Warning in FUN(newX[, i], ...): no non-missing arguments to max; returning -Inf
##                  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.88     6.71     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.58     2.39     1.00     2.14  0.00     1.00
## estado*            10 113     2.17     0.55     2.00     2.19  0.00     1.00
## CP                 11 113 63364.74 11201.89 66646.00 66622.84  4.45 25016.00
## estado_civil*      12 113     2.38     1.19     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*             22.00    21.0  1.53     0.74    0.63
## 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*           9.00     8.0  1.19     0.02    0.22
## estado*              3.00     2.0  0.08    -0.06    0.05
## CP               67493.00 42477.0 -3.06     7.42 1053.79
## estado_civil*        5.00     4.0 -0.03    -1.48    0.11
dim(colaboradores)
## [1] 113  12

Clasificación de Variables y Escala de Medicion

Merma

Variable<-c("Fecha","Mes","Kilos")

Tipo<-c("Qualitative (nominal)", "Qualitative (nominal)", "Quantitative (discrete)")

Escala <- c("Fecha (y/m/d)","N/A", "Kilogramos")

table<-data.frame(Variable,Tipo,Escala)
knitr::kable(table)
Variable Tipo Escala
Fecha Qualitative (nominal) Fecha (y/m/d)
Mes Qualitative (nominal) N/A
Kilos Quantitative (discrete) Kilogramos

Scrap

Variable2<-c("Referencia","Fecha","Producto","Cantidad", "Ubicación de origen")

Tipo2<-c("Qualitative (nominal)", "Qualitative (nominal)", "Qualitative (nominal)","Quantitative (discrete)", "Qualitative (nominal)")

Escala2 <- c("N/A","Fecha (y/m/d)", "N/A","Unidades","N/A")

table2<-data.frame(Variable2,Tipo2,Escala2)
knitr::kable(table2)
Variable2 Tipo2 Escala2
Referencia Qualitative (nominal) N/A
Fecha Qualitative (nominal) Fecha (y/m/d)
Producto Qualitative (nominal) N/A
Cantidad Quantitative (discrete) Unidades
Ubicación de origen Qualitative (nominal) N/A

Producción de Carton

Variable3<-c("Fecha","Cliente","Piezas programadas", "Tiempo minutos", "Hora fin", "Laminas procesadas", "Inicio sep up", "Fin inicio sep up", "Inicio de proceso", "Fin de proceso", "Tiempo calidad")

Tipo3<-c("Qualitative (nominal)","Qualitative (nominal)","Quantitative (discrete)", "Quantitative (discrete)",  "Quantitative (discrete)", "Quantitative (discrete)", "Quantitative (discrete)", "Quantitative (discrete)", "Quantitative (discrete)", "Quantitative (discrete)","Quantitative (discrete)" )

Escala3 <- c("Fecha (y/m/d)","N/A","Piezas (unidades) ","Tiempo (M)","Tiempo (H:M)","Laminas (unidades)","Tiempo (H:M)","Tiempo (H:M)","Tiempo (H:M)","Tiempo (H:M)","Tiempo (M)")

table3<-data.frame(Variable3,Tipo3,Escala3)
knitr::kable(table3)
Variable3 Tipo3 Escala3
Fecha Qualitative (nominal) Fecha (y/m/d)
Cliente Qualitative (nominal) N/A
Piezas programadas Quantitative (discrete) Piezas (unidades)
Tiempo minutos Quantitative (discrete) Tiempo (M)
Hora fin Quantitative (discrete) Tiempo (H:M)
Laminas procesadas Quantitative (discrete) Laminas (unidades)
Inicio sep up Quantitative (discrete) Tiempo (H:M)
Fin inicio sep up Quantitative (discrete) Tiempo (H:M)
Inicio de proceso Quantitative (discrete) Tiempo (H:M)
Fin de proceso Quantitative (discrete) Tiempo (H:M)
Tiempo calidad Quantitative (discrete) Tiempo (M)

Delivery Performance

Variable4<-c("Cliente","Vuelta","Fecha", "Plan Arrival", "Real Arrival", "Real Departure", "Difference")

Tipo4<-c("Qualitative (nominal)","Qualitative (nominal)","Quantitative (discrete)", "Quantitative (discrete)",  "Quantitative (continuous)", "Quantitative (continuous)", "Quantitative (continuous)")

Escala4 <- c("N/A","Vueltas","Fecha (y/m/d)","Tiempo (H)","Tiempo (H:M)","Tiempo (H:M)","Tiempo (H:M)")

table4<-data.frame(Variable4,Tipo4,Escala4)
knitr::kable(table4)
Variable4 Tipo4 Escala4
Cliente Qualitative (nominal) N/A
Vuelta Qualitative (nominal) Vueltas
Fecha Quantitative (discrete) Fecha (y/m/d)
Plan Arrival Quantitative (discrete) Tiempo (H)
Real Arrival Quantitative (continuous) Tiempo (H:M)
Real Departure Quantitative (continuous) Tiempo (H:M)
Difference Quantitative (continuous) Tiempo (H:M)

Delivery Plan

Variable5<-c("Cliente","Proyecto", "Item", "Mes","Unidades")

Tipo5<-c("Cualitative (nominal)", "Cualitative (nominal)", "Cualitative (nominal)", "Quantiative (discrete)", "Quantiative (discrete)")

Escala5 <- c("N/A","N/A","N/A","Tiempo (Y/M)","Unidades")

table5<-data.frame(Variable5,Tipo5,Escala5)
knitr::kable(table)
Variable Tipo Escala
Fecha Qualitative (nominal) Fecha (y/m/d)
Mes Qualitative (nominal) N/A
Kilos Quantitative (discrete) Kilogramos

Bajas

Variable6<-c("Nombre Completo","Fecha Nacimiento", "Genero", "Fecha Alta","Motivo Renuncia","Permanencia","Fecha de Baja","Puesto","Departamento","Salario Diario IMSS","Colonia","CP","Municipio","Estado","Estado Civil")

Tipo6<-c("Cualitative (nominal)","Quantiative (discrete)", "Cualitative (nominal)", "Quantiative (discrete)","Cualitative (nominal)","Quantiative (discrete)","Quantiative (discrete)","Cualitative (nominal)","Cualitative (nominal)","Quantiative (continuous)","Cualitative (nominal)","Cualitative (nominal)","Cualitative (nominal)","Cualitative (nominal)","Cualitative (nominal)")

Escala6<-c("N/A","Fecha (Y/M/D)", "N/A", "Fecha (Y/M/D)","N/A","Dias","Fecha (Y/M/D)","N/A","N/A","Pesos Mexicanos","N/A","N/A","N/A","N/A","N/A")

table6<-data.frame(Variable6,Tipo6,Escala6)
knitr::kable(table)
Variable Tipo Escala
Fecha Qualitative (nominal) Fecha (y/m/d)
Mes Qualitative (nominal) N/A
Kilos Quantitative (discrete) Kilogramos

Colaboradores

Variable7<-c("Nombre Completo","Fecha Nacimiento", "Genero", "Fecha Alta","Puesto","Departamento","Salario Diario IMSS","Colonia","Municipio","Estado","CP","Estado Civil")

Tipo7<-c("Cualitative (nominal)","Quantiative (discrete)", "Cualitative (nominal)", "Quantiative (discrete)","Cualitative (nominal)","Cualitative (nominal)","Quantiative (continuous)","Cualitative (nominal)","Cualitative (nominal)","Cualitative (nominal)","Cualitative (nominal)","Cualitative (nominal)")

Escala7<-c("N/A","Fecha (Y/M/D)", "N/A", "Fecha (Y/M/D)","N/A","N/A","Pesos Mexicanos","N/A","N/A","N/A","CP","N/A")

table7<-data.frame(Variable7,Tipo7,Escala7)
knitr::kable(table)
Variable Tipo Escala
Fecha Qualitative (nominal) Fecha (y/m/d)
Mes Qualitative (nominal) N/A
Kilos Quantitative (discrete) Kilogramos

Análisis Estadístico Descriptivo

mes <- merma %>% group_by(mes) %>% tally()
mes
## # A tibble: 9 × 2
##   mes            n
##   <chr>      <int>
## 1 ABRIL          5
## 2 AGOSTO        11
## 3 ENERO          4
## 4 FEBRERO        6
## 5 JULIO          5
## 6 JUNIO          4
## 7 MARZO          6
## 8 MAYO           5
## 9 SEPTIEMBRE     4

Agosto es el mes en donde la empresa produce mayor merma, con esto podemos inferir que agosto es el mes con mayor producción en la empresa. De la misma manera, podemos inferir que en los meses de enero, junio y septiembre es donde la empresa tiene menor demanda, ya que son los meses en donde la empresa produce menos merma.

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

ubicacion_origen <- scrap %>% group_by(ubi_origen) %>% tally()
ubicacion_origen
## # A tibble: 3 × 2
##   ubi_origen                    n
##   <chr>                     <int>
## 1 SAB/Calidad/Entrega de PT    58
## 2 SAB/Post-Production          13
## 3 SAB/Pre-Production          179

Esta tabla nos muestra los desechos y/o residuos derivados del proceso en las diferentes areas de trabajo. Como podemos observar en pre-producción es el lugar donde mas se generan estos residuos.

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

cliente <- carton %>% group_by(cliente) %>% tally()
cliente
## # A tibble: 11 × 2
##    cliente                  n
##    <chr>                <int>
##  1 DENSO                   92
##  2 HANON SYSTEMS            8
##  3 HELLA                   45
##  4 MERIDIAN LIGHTWEIGHT    47
##  5 STABILUS 1             549
##  6 STABILUS 3             244
##  7 TRMX                   180
##  8 VARROC                 154
##  9 VL-017-13939             3
## 10 VL-017-14086             1
## 11 YANFENG                163

Los clientes con mayor demanda son:
- Stabilus 1
- Stabilus 3
- TRMX
- VARROC
- DENSO

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

Propuesta

Hacer que la empresa FORM utilice la metodologia de Business Analytics, ayudara a la empresa a analizar la situación actual en la que se encuntran y por ende predecir eventos, tendencias y comportamientos a futuro que ayuden a determinar la toma de decisiones, reduciendo los riesgos de equivocaciones futuras.

La segunda propuesta va encaminada a indicadores de calidad, los cual permite a la emprsa visualizar y medir los cambios y progresos que está haciendo hacia el logro de un resultado específico. En este caso buscamos reducir la merma por lo que algunos de los indicadores que pueden utilizar son:

  • Defectos por unidad
    Este indicador nos da el número de defectos que hay en una muestra sobre el número de unidades que se tomaron en la muestra.
    DPU = # numero de defectos / tamaño de muestra

  • Defectos por oportunidad
    Es el número de defectos que hay en una muestra sobre la cantidad de oportunidades, tomándose las oportunidades como los posibles defectos que puede tener cada unidad.
    DPO = # numero de defectos / # de oportunidades

  • Defecto por millon de opotunidades
    En este se toma el número de defectos en una muestra dividido entre el número total de oportunidades y multiplicado por 1,000,000, esto ayuda a estandarizar el nivel de oportunidad y además permite comparar con otros processo.
    DPO = (# numero de defectos / # de oportunidades) x 1,000,000

Esto nos ayudara a evalar la rentabilidad de los procesos que se llevan a cabo.

Base de Datos Externa

Importar Base de Datos

reg <- read.csv("/Users/anita3/Downloads/registrations.csv")
car_prod <- read.csv("/Users/anita3/Downloads/car_production.csv")

Graficos Cualitativos y Cuantitativos

ggplot(data=car_prod, aes(x=year, y=us_car_production), color='orange') + 
geom_line() + 
    labs(x="Año", y="Produccion")

En esta grafica podemos observar que la producción de los carros en EUA disminuye, esto puede ser a casusa de la insuficiencia de semiconductores en el mercado, la cual es una pieza escencial para su producción. Ademas de la escasez de chips que tienen las empresas tecnológicas que fabrican desde electrodomésticos, computadores y celulares hasta consolas de videojuegos.

ggplot(data=car_prod, aes(x=year, y=retail_price_gasoline), color='red') + 
geom_line() + 
  labs(x="Año", y="Precio de Gasolina")

Una de las causas del incremento de gasolina se debe a la fuerza que ha tomado el dólar frente a otras monedas, por lo que productores prefieren recibir más divisas en otros países.

ggplot(data=car_prod, aes(x=year, y=poblacion), color='red') + 
geom_line() + 
  labs(x="Año", y="Población Desocupada")

Esta grafica corresponde a la población desocupada, es decir, las personas cesantes que son desocupados que han tenido un trabajo previamente como las que buscan trabajo por primera vez, con respecto a la población económicamente activa.

Tabla de frecuencia

No se puede realizar la tabla de freciencia debido a que la naturaleza de los datos no nos lo permite. (No se cuentan con datos repetitivos)

Graficos de dispersión

ggplot(data=car_prod, aes(x=year, y=poblacion), color='orange') + 
geom_point() + 
  labs(x="Año", y="Poblacion")

ggplot(data=car_prod, aes(x=year, y=retail_price_gasoline), color='orange') + 
geom_point() + 
  labs(x="Año", y="Precio de Gasolina")

Exportar Bases de Datos Limpias

write.csv(bajas,file = "bajas_limpia.csv", row.names = FALSE)
write.csv(colaboradores,file = "colaboradores_limpia.csv", row.names = FALSE)
write.csv(perf, file= "del_perf_limpia.csv", row.names = FALSE)
write.csv(plan,file = "plan_limpia.csv", row.names = FALSE)
write.csv(merma,file = "merma_limpia.csv", row.names = FALSE)
write.csv(scrap,file = "scrap_limpia.csv", row.names = FALSE)
write.csv(carton,file = "carton_limpia.csv", row.names = FALSE)
LS0tCnRpdGxlOiAiU0VDQ0lPTiAxIgphdXRob3I6ICJBbmEgUGF0cmljaWEgQXBvbnRlIC0gQTAxMjgzOTI4IgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogI0ZGN0YyNCI+ICoqTGltcGllemEsIFRyYW5zZm9ybWFjacOzbiwgeSBPcmdhbml6YWNpw7NuIGRlIEJhc2VzIGRlIERhdG9zKiogPC9zcGFuPgoKKipMaWJyZXJpYXMgVXRpbGl6YWRhcyoqCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShkcGx5cikKbGlicmFyeShwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobmFuaWFyKQpsaWJyYXJ5KEhtaXNjKSAgICAgICAgIApsaWJyYXJ5KHBzeWNoKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHBvbGxzdGVyKQpsaWJyYXJ5KGVwaURpc3BsYXkpCmxpYnJhcnkoZGVzY3IpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodGV4dGNsZWFuKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKYGBgCgoqKkltcG9ydGFyIEJhc2VzIGRlIERhdG9zKioKYGBge3J9Cm1lcm1hIDwtIHJlYWQuY3N2KCIvVXNlcnMvYW5pdGEzL0Rlc2t0b3AvRk9STS9GT1JNIC0gTWVybWEuY3N2IikKc2NyYXAgPC0gcmVhZC5jc3YoIi9Vc2Vycy9hbml0YTMvRGVza3RvcC9GT1JNL0ZPUk0gLSBTY3JhcC5jc3YiKQpjYXJ0b24gPC0gcmVhZC5jc3YoIi9Vc2Vycy9hbml0YTMvRGVza3RvcC9GT1JNL0ZPUk0gLSBQcm9kdWNjaW9uIENhcnRvbiBDb21wbGV0YS5jc3YiKQpwZXJmIDwtIHJlYWQuY3N2KCIvVXNlcnMvYW5pdGEzL0Rlc2t0b3AvRk9STS9EZWwgUGVyZi5jc3YiKQpwbGFuIDwtIHJlYWQuY3N2KCIvVXNlcnMvYW5pdGEzL0Rlc2t0b3AvRk9STS9EUF8xLmNzdiIpCmJhamFzIDwtIHJlYWQuY3N2KCIvVXNlcnMvYW5pdGEzL0Rlc2t0b3AvRk9STS9SSC5jc3YiKQpjb2xhYm9yYWRvcmVzIDwtIHJlYWQuY3N2KCIvVXNlcnMvYW5pdGEzL0Rlc2t0b3AvRk9STS9SSDIuY3N2IikKYGBgCgojIyBUw6ljbmljYXMgZGUgTGltcGllemEKCiMjIyBNZXJtYQoKKipDYW1iaWFyIG5vbWJyZSBkZSB2YXJpYWJsZXMqKgpgYGB7cn0KbWVybWEgPC0gY2xlYW5fbmFtZXMobWVybWEpCmNvbG5hbWVzKG1lcm1hKQpgYGAKCioqTWFudGVuZXIgdmFyaWFibGVzIHJlbGV2YW50ZXMgcGFyYSBlbCBhbsOhbGlzaXMqKiAgClBhcmEgbGEgYmFzZSBkZSBkYXRvcyBkZSAqKm1lcm1hKiogc2Ugb3B0byBwb3IgbWFudGVuZXIgbGFzIHRyZXMgdmFyaWFibGVzLCB5YSBxdWUgdG9kYXMgbGFzIHZhcmlhYmxlcyBzb24gcmVsZXZhbnRlcyBwYXJhIGVsIGFuYWxzaXMuCgoqKkVsaW1pbmFyIGZpbGFzIHF1ZSBjb250aWVuZW4gZWwgdG90YWwgZGUgbG9zIG1lc2VzKioKYGBge3J9Cm1lcm1hPC1tZXJtYVstIGdyZXAoIlRvdGFsIiwgbWVybWEkbWVzKSxdCmBgYAoKKipDb3Jyb2JvcmFyIHF1ZSBsYXMgY29sdW1uYXMgZXN0w6luIGVuIGVsIGZvcm1hdG8gY29ycmVjdG8qKgpgYGB7cn0Kc3RyKG1lcm1hKQpgYGAKCioqQ2FtYmlhciBmZWNoYSBhIGZvcm1hdG8gZGUgZmVjaGEgeSBraWxvcyBjb21vIGVudGVybyoqCmBgYHtyfQptZXJtYSRraWxvczwtYXMuaW50ZWdlcihtZXJtYSRraWxvcykKbWVybWEkZmVjaGE8LWFzLlBPU0lYY3QobWVybWEkZmVjaGEsIGZvcm1hdCA9IiVkLyVtLyVZIikKc3RyKG1lcm1hKQpgYGAKCgojIyMgU2NyYXAKCioqQ2FtYmlhciBub21icmUgZGUgdmFyaWFibGVzKioKYGBge3J9CnNjcmFwPC1zY3JhcCAlPiUgZHBseXI6OnJlbmFtZShyZWZlcmVuY2lhPVJlZmVyZW5jaWEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1Ymlfb3JpZ2VuPVViaWNhY2nDs24uZGUub3JpZ2VuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdWJpX2Rlc2VjaG89VWJpY2FjacOzbi5kZS5kZXNlY2hvKQpzY3JhcDwtY2xlYW5fbmFtZXMoc2NyYXApCmNvbG5hbWVzKHNjcmFwKQpgYGAKCioqTWFudGVuZXIgdmFyaWFibGVzIHJlbGV2YW50ZXMgcGFyYSBlbCBhbsOhbGlzaXMqKiAgClBhcmEgbGEgYmFzZSBkZSBkYXRvcyBkZSAqKnNjcmFwKiogc2Ugb3B0byBwb3IgZWxpbWluYXIgbGFzIHZhcmlhYmxlcyBkZSB1bmlkYWQgZGUgbWVkaWRhLCBlc3RhZG8geSB1YmljYWNpw7NuIGRlIGRlc2VjaG8sIHlhIHF1ZSB0b2RvcyBsb3MgcmVnaXN0cm9zIHRpZW5lbiBlbCBtaXNtbyB2YWxvciBlbiBlc3RhcyB2YXJpYWJsZXMsIGVzIGRlY2lyIHF1ZSB0b2RvcyB0aWVuZW4gbGEgbWlzbWEgdW5pZGFkLCBsYSBtaXNtYSB1YmljYWNpw7NuIHkgZWwgbWlzbW8gZXN0YWRvLgoKYGBge3J9CnNjcmFwPC1kcGx5cjo6c2VsZWN0KHNjcmFwLC1jKHViaV9kZXNlY2hvLCBlc3RhZG8sIHVuaWRhZF9kZV9tZWRpZGEpKQpjb2xuYW1lcyhzY3JhcCkKYGBgCgoqKkVsaW1pbmFyIGxhIHByaW1lciBmaWxhIHF1ZSBkaWNlIGFnb3N0byoqCmBgYHtyfQpzY3JhcDwtc2NyYXBbLSBncmVwKCJhZ29zdG8iLCBzY3JhcCRyZWZlcmVuY2lhKSxdCmhlYWQoc2NyYXAsNSkKYGBgCgoqKkNvcnJvYm9yYXIgcXVlIGxhcyBjb2x1bW5hcyBlc3TDqW4gZW4gZWwgZm9ybWF0byBjb3JyZWN0byoqCmBgYHtyfQpzdHIoc2NyYXApCmBgYAoKKipDYW1iaWFyIGNhbnRpZGFkIGEgZW50ZXJvIHkgZmVjaGEgY29uIGZvcm1hdG8gZGUgZmVjaGEqKgpgYGB7cn0Kc2NyYXAkY2FudGlkYWQ8LWFzLmludGVnZXIoc2NyYXAkY2FudGlkYWQpCnNjcmFwJGZlY2hhPC1hcy5QT1NJWGN0KHNjcmFwJGZlY2hhLCBmb3JtYXQgPSIlZC8lbS8lWSIpCnN0cihzY3JhcCkKYGBgCgoKIyMjIFByb2R1Y2Npw7NuIGRlIENhcnRvbgoKKipDYW1iaWFyIG5vbWJyZSBkZSB2YXJpYWJsZXMqKgpgYGB7cn0KY2FydG9uPC1jYXJ0b24gJT4lIGRwbHlyOjpyZW5hbWUobGFtX3Byb2Nlc2FkYXM9TGFtaW5hcy5wcm9jZXNhZGFzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaW5fc2VwX3VwPUZJTi5JTklDSU8uREUuU0VQLlVQLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmljaW9fcHJvY2Vzbz1JTklDSU8uZGUuUFJPQ0VTTywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluX3Byb2Nlc289RklOLmRlLlBST0NFU08sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlY2hhPUZlY2hhKQpjYXJ0b248LWNsZWFuX25hbWVzKGNhcnRvbikKY29sbmFtZXMoY2FydG9uKQpgYGAKCioqTWFudGVuZXIgdmFyaWFibGVzIHJlbGV2YW50ZXMgcGFyYSBlbCBhbsOhbGlzaXMqKiAgClBhcmEgbGEgYmFzZSBkZSBkYXRvcyBkZSAqKnByb2R1Y2Npw7NuIGRlIGNhcnTDs24qKiBzZSBoYSBvcHRhZG8gcG9yIGVsaW1pbmFyIGxhcyBjb2x1bW5hcyBkZSBpZF9mb3JtLCBwcm9kdWN0bywgdGllbXBvX21hdGVyaWFsZXMgeSBlc3RhY2nDs24gZGUgYXJyYW5xdWU7IHlhIHF1ZSBjb24gZWwgcmVzdG8gZGUgbGFzIGNvbHVtbmFzIHNlIHB1ZWRlIHJlYWxpemFyIG90cm9zIGNhbGN1bG9zIGNvbW8gbWVkaWNpb25lcyBkZSB0aWVtcG8uCgpgYGB7cn0KY2FydG9uPC1kcGx5cjo6c2VsZWN0KGNhcnRvbiwtYyhpZF9mb3JtLCBwcm9kdWN0bywgdGllbXBvX21hdGVyaWFsZXMsIHgsIGVzdGFjaW9uX2FycmFucXVlKSkKY29sbmFtZXMoY2FydG9uKQpgYGAKCioqQ29ycm9ib3JhciBxdWUgbGFzIGNvbHVtbmFzIGVzdMOpbiBlbiBlbCBmb3JtYXRvIGNvcnJlY3RvKioKYGBge3J9CnN0cihjYXJ0b24pCmBgYAoKKipDYW1iaWFyIGxhIGZlY2hhIGNvbiBmb3JtYXRvIGRlIGZlY2hhLCBwaWV6YXMgcHJvZ3JhbWFkYXMgeSBsYW1pbmFzIHByb2Nlc2FkYXMgYSBlbnRlcm8sIHkgbGFzIGNvbHVtbmFzIHF1ZSBzb24gZGUgdGllbXBvIGFsIGZvcm1hdG8gZGUgaG9yYSB0YW1iacOpbioqCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmNhcnRvbiRwaWV6YXNfcHJvZyA8LWFzLmludGVnZXIoY2FydG9uJHBpZXphc19wcm9nKQpjYXJ0b24kdG1vX21pbiA8LWFzLmludGVnZXIoY2FydG9uJHRtb19taW4pCgpjYXJ0b24kaHJfZmluPC1hcy5QT1NJWGN0KGNhcnRvbiRocl9maW4sZm9ybWF0PSIlSDolTSIpCmNhcnRvbiRocl9maW4gPC0gYXMuSVRpbWUoY2FydG9uJGhyX2ZpbikKCmNhcnRvbiRpbmljaW9fc2VwX3VwPC1hcy5QT1NJWGN0KGNhcnRvbiRpbmljaW9fc2VwX3VwLGZvcm1hdD0iJUg6JU0iKQpjYXJ0b24kaW5pY2lvX3NlcF91cCA8LSBhcy5JVGltZShjYXJ0b24kaW5pY2lvX3NlcF91cCkKCmNhcnRvbiRmaW5fc2VwX3VwPC1hcy5QT1NJWGN0KGNhcnRvbiRmaW5fc2VwX3VwLGZvcm1hdD0iJUg6JU0iKQpjYXJ0b24kZmluX3NlcF91cCA8LSBhcy5JVGltZShjYXJ0b24kZmluX3NlcF91cCkKCmNhcnRvbiRpbmljaW9fcHJvY2VzbzwtYXMuUE9TSVhjdChjYXJ0b24kaW5pY2lvX3Byb2Nlc28sZm9ybWF0PSIlSDolTSIpCmNhcnRvbiRpbmljaW9fcHJvY2VzbyA8LSBhcy5JVGltZShjYXJ0b24kaW5pY2lvX3Byb2Nlc28pCgpjYXJ0b24kZmluX3Byb2Nlc288LWFzLlBPU0lYY3QoY2FydG9uJGZpbl9wcm9jZXNvLGZvcm1hdD0iJUg6JU0iKQpjYXJ0b24kZmluX3Byb2Nlc28gPC0gYXMuSVRpbWUoY2FydG9uJGZpbl9wcm9jZXNvKQoKc3RyKGNhcnRvbikKYGBgCgo8Y2VudGVyPiAKPHNwYW4gc3R5bGU9ImNvbG9yOiAjRkZBNTRGIj4gKipFbiBlc3RhIGJhc2UgZGUgZGF0b3MgKFByb2R1Y2Npw7NuIGRlIENhcnRvbikgaGF5IG90cm9zIGVsZW1lbnRvcyBhIGNvbnNpZGVyYXIsIGVzIHBvciBlc28gcXVlIHNlIHJldmlzbyBjYWRhIGNvbHVtbmEgeSBsb3MgcmVnaXN0cm9zIHF1ZSBsYSBjb25mb3JtYW4qKiA8L3NwYW4+CjwvY2VudGVyPiAKCioqRmVjaGEqKiAgCk5vIGhheSB2YWxvcmVzIGZhbHRhbnRlcyBlbiBlc3RhIGNvbWx1bmEsIHNpbiBlbWJhcmdvIGhheSBxdWUgY2FtYmlhciBlbCBmb3JtYXRvIGRlIGxhIHZhcmlhYmxlIGEgZm9ybWF0byBkZSBmZWNoYSBwYXJhIHF1ZSBkZXNwdWVzIG5vcyBwZXJtaXRhIHJlYWxpemFyIG9wZXJhY2lvbmVzIHkgZW5jb250cmFyIG90cm9zIGRhdG9zLgpgYGB7cn0KY2FydG9uJGZlY2hhIDwtIGFzLkRhdGUoY2FydG9uJGZlY2hhLCBmb3JtYXQgPSIlZC8lbS8leSIpCnN0cihjYXJ0b24kZmVjaGEpCnVuaXF1ZShjYXJ0b24kZmVjaGEpCmBgYAoKKipDbGllbnRlKiogICAKQ29uIGxvcyBjbGllbnRlcyBwb2RlbW9zIG9ic2VydmFyIHF1ZSBoYXkgY2xpZW50ZXMgY29uIE5BwrRzLCBvdHJvcyBjb24gZXNwYWNpb3MgdmFjw61vcywgeSBvdHJvcyBxdWUgc29uIGVsIG1pc21vIHBlcm8gY29uIGVycm9yIG9ydG9ncsOhZmljbywgY29tbyBlcyBlbCBjYXNvIGRlIFNUQUJJTFVTIDMgeSBTVEFCSUxVUyAzLiBFbCBmb3JtYXRvIGRlIGNhcmFjdGVyIGVzdMOhIGNvcnJlY3RvIHBvciBsbyBxdWUgbm8gaGF5IHF1ZSBoYWNlciBtb2RpZmljYWNpb25lcyBlbiBlbCB0aXBvIGRlIHZhcmlhYmxlLgpgYGB7cn0KdW5pcXVlKGNhcnRvbiRjbGllbnRlKQpgYGAKCkhlIG9wdGFkbyBwb3IsIGVsaW1pbmFyIGxvcyByZWdpc3Ryb3MgY29uIGNsaWVudGVzIGNvbiBOQSB5IGNvbiBlc3BhY2lvcyBlbiBibGFuY28sIGFkZW3DoXMgZGUgY2FtYmlhciBhcXVlbGxvcyBxdWUgdGllbmVuIGRlIGNsaWVudGUgU1RBQklMVVMgMy4gYSBTVEFCSUxVUyAzLgpgYGB7cn0KY2FydG9uIDwtIGZpbHRlcihjYXJ0b24sICFpcy5uYShjYXJ0b24kY2xpZW50ZSkpCmNhcnRvbiA8LSBmaWx0ZXIoY2FydG9uLGNsaWVudGUhPSIiKQpjYXJ0b24kY2xpZW50ZSA8LSByZXBsYWNlKGNhcnRvbiRjbGllbnRlLGNhcnRvbiRjbGllbnRlPT0iU1RBQklMVVMgMy4iLCJTVEFCSUxVUyAzIikKdW5pcXVlKGNhcnRvbiRjbGllbnRlKQpgYGAKCioqSW5pY2lvIGRlIHByb2Nlc28geSBmaW4gZGUgcHJvY2VzbyoqICAgIApTZSBlbmNvbnRyYXJvbiByZWdpc3Ryb3MgY29uIE5BwrRzLCBkZSBvcHRvIHBvciBlbGltaW5hciBlc3RvcyByZWdpc3Ryb3MgcGFyYSB0ZW5lciB1biBtZWpvciBhbmFsaXNpcy4gQXPDrSBtaXNtbywgaGF5IHF1ZSBlbGltaW5hciBsb3MgcmVnaXN0cm9zIHF1ZSBlc3RlbiBjb21vIDAwOjAwIHRhbWJpw6luLgpgYGB7cn0KY2FydG9uIDwtIGZpbHRlcihjYXJ0b24sICFpcy5uYShjYXJ0b24kaW5pY2lvX3Byb2Nlc28pICYgIWlzLm5hKGNhcnRvbiRmaW5fcHJvY2VzbykpCmNhcnRvbiA8LSBmaWx0ZXIoY2FydG9uLCFpbmljaW9fcHJvY2Vzbz09MDA6MDA6MDAgJiAhZmluX3Byb2Nlc289PTAwOjAwOjAwKQpzdW0oaXMubmEoY2FydG9uJGluaWNpb19wcm9jZXNvKSkKc3VtKGlzLm5hKGNhcnRvbiRmaW5fcHJvY2VzbykpCmBgYAoKKipJbmljaW8gZGUgc2VwIHVwIHkgZmluIGRlIHNlcCB1cCoqICAgClBhcmEgZXN0YXMgY29sdW1uYXMgdGFtYmnDqW4gZWxpbWluYXJlbW9zIGxvcyByZWdpc3Ryb3MgY29uIE5BwrRzIG8gY29uIDAwOjAwIGNvbW8gdGllbXBvLiAgCmBgYHtyfQpjYXJ0b24gPC0gZmlsdGVyKGNhcnRvbiwgIWlzLm5hKGNhcnRvbiRpbmljaW9fc2VwX3VwKSAmICFpcy5uYShjYXJ0b24kZmluX3NlcF91cCkpCmNhcnRvbiA8LSBmaWx0ZXIoY2FydG9uLCFpbmljaW9fc2VwX3VwPT0wMDowMDowMCAmICFmaW5fc2VwX3VwPT0wMDowMDowMCkKc3VtKGlzLm5hKGNhcnRvbiRpbmljaW9fc2VwX3VwKSkKc3VtKGlzLm5hKGNhcnRvbiRmaW5fc2VwX3VwKSkKYGBgCgoqKlBpZXphcyBQcm9ncmFtYWRhcyoqICAgIApTZSBlbGltaW5hcm9uIGxvcyByZWdpc3Ryb3MgY29uIE5BwrRzLiBPdHJvIGNhbWJpbyBhIHJlYWxpemFyIGVzIGNvbnZlcnRpciBlbCBmb3JtYXRvIGEgZW50ZXJvLgpgYGB7cn0KY2FydG9uIDwtIGZpbHRlcihjYXJ0b24sICFpcy5uYShjYXJ0b24kcGllemFzX3Byb2cpKQpjYXJ0b24kcGllemFzX3Byb2cgPC0gYXMuaW50ZWdlcihjYXJ0b24kcGllemFzX3Byb2cpCnN1bShpcy5uYShjYXJ0b24kcGllemFzX3Byb2cpKQpgYGAKCioqTGFtaW5hcyBwcm9jZXNhZGFzKiogICAKUGFyYSBsYW1pbmFzIHByb2Nlc2FkYXMgc2kgYmllbiBubyB0aWVuZSBOQSBzdXMgcmVnaXN0cm9zIG5vIHNvbiBjb25zaXN0ZW50ZXMgeSB1bmlmb3JtZXMgZW4gc3UgZm9ybWF0bywgYWxndW5vcyBzb24gbnVtZXJvcyBlbnRlcm9zLCBvdHJvcyBlc3RhbiBkaXZpZGlkb3MgcG9yIHVuICIvIiwgb3Ryb3Mgc29uIGFsZmFuw7ptZXJpY29zLCB5IG90cm9zIHNvbiAwIG8gcmVnaXN0cm9zIHZhY8Otb3MuIFNlIGVsaW1pbmFyYW4gYXF1ZWxsb3MgcmVnaXN0cm9zIG1lbm9yZXMgYSAxLCBsb3MgdmFjacOzcyB5IG1hbnRlbmRyZW1vcyBlbCBwcmltZXIgbsO6bWVybyBhbnRlcyBkZWwgIi8iLCB5IGxvcyByZWdpc3Ryb3MgYWxmYW7Dum1lcmljb3MgYXF1ZWxsb3MgcXVlIHNvbG8gdGllbmVuIHVuYSBsZXRyYSwgZWxpbWluYXJlbW9zIGxhIGxldHJhLCBwZXJvIGFxdWVsbG9zIHF1ZSBjb250ZW5nYW4gc2lnbm9zIGNvbW8gIj0iLCBvIG3DoXMgY2FyYWN0ZXJlcyBzZXLDoW4gZWxpbWluYWRvcy4gVGFtYmnDqW4gZWwgZm9ybWF0byBkZWJlIHNlciBkZSBlbnRlcm8gcG9yIGxvIHF1ZSBoYXkgcXVlIGhhY2VyIGVzdGEgbW9kaWZpY2FjacOzbi4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2FydG9uIDwtIGNhcnRvbiAlPiUgc2VwYXJhdGUobGFtX3Byb2Nlc2FkYXMsIGMoImxhbV9wcm9jZXNhZGFzIiwgImVsaW1pbmFyIiksIHNlcD0iLyIpCmNhcnRvbiA8LSBkcGx5cjo6c2VsZWN0KGNhcnRvbiwtZWxpbWluYXIpCmNhcnRvbiA8LSBjYXJ0b25bLSBncmVwKCI9IiwgY2FydG9uJGxhbV9wcm9jZXNhZGFzKSxdCmNhcnRvbiA8LSBjYXJ0b25bLSBncmVwKCJBIDQgQjQiLCBjYXJ0b24kbGFtX3Byb2Nlc2FkYXMpLF0KY2FydG9uJGxhbV9wcm9jZXNhZGFzIDwtIHN0cl9yZXBsYWNlKGNhcnRvbiRsYW1fcHJvY2VzYWRhcywgIlthZWlvdUxBTT1OYkJzU10iLCAiIikKY2FydG9uJGxhbV9wcm9jZXNhZGFzIDwtIGFzLmludGVnZXIoY2FydG9uJGxhbV9wcm9jZXNhZGFzKQpjYXJ0b24gPC0gZmlsdGVyKGNhcnRvbixjYXJ0b24kbGFtX3Byb2Nlc2FkYXM+MCkKc3VtKGlzLm5hKGNhcnRvbiRsYW1fcHJvY2VzYWRhcykpCnVuaXF1ZShjYXJ0b24kbGFtX3Byb2Nlc2FkYXMpCmBgYAoKKipUbW9fbWluIHkgaHJfZmluYWwqKiAgIApDYW1iaWFyIGVsIGZvcm1hdG8gZGUgdG1vX21pbiBhIGVudGVyby4gIApgYGB7cn0KY2FydG9uJHRtb19taW4gPC0gYXMuaW50ZWdlcihjYXJ0b24kdG1vX21pbikKYGBgCgoqKlRpZW1wbyBkZSBjYWxpZGFkKiogICAKUGFyYSB0aWVtcG8gZGUgY2FsaWRhZCBwb2RlbW9zIG9ic2VydmFyIHF1ZSBoYXkgcmVnaXN0cm9zIHF1ZSBubyBzb24gZW50ZXJvcywgb3RvcnMgcXVlIHRpZW5lbiBzZWd1bmRvcyB5IG90cm9zIHF1ZSBlc3TDoW4gdmFjw61vcy4gTG9zIHF1ZSBubyBzb24gZW50ZXJvcyBsb3MgZGVqYXJlbW9zIHZhY8Otb3MsIGxvcyB2YWPDrW9zIHZhbiBhIHBlcm1hbmVjZXIgeSBsb3MgcXVlIHRpZW5lbiBzZWd1bmRvcyB2YW4gYSBzZXIgcmVkb25kZWFkb3MgYWwgZW50ZXJvIG3DoXMgY2VyY2Fuby4gIApgYGB7cn0KY2FydG9uJHRpZW1wb19jYWxpZGFkIDwtIHN0cl9yZXBsYWNlKGNhcnRvbiR0aWVtcG9fY2FsaWRhZCwgIjk6NDciLCAiOSIpCmNhcnRvbiA8LSBzdWJzZXQoY2FydG9uLCB0aWVtcG9fY2FsaWRhZCE9InwiKQpjYXJ0b24kdGllbXBvX2NhbGlkYWQgPC0gYXMuaW50ZWdlcihjYXJ0b24kdGllbXBvX2NhbGlkYWQpCnVuaXF1ZShjYXJ0b24kdGllbXBvX2NhbGlkYWQpCmBgYAoKCiMjIyBEZWxpdmVyeSBQZXJmb3JtYW5jZQoKKipDYW1iaWFyIG5vbWJyZSBkZSB2YXJpYWJsZXMqKgpgYGB7cn0KcGVyZjwtY2xlYW5fbmFtZXMocGVyZikKY29sbmFtZXMocGVyZikKYGBgCgoqKk1hbnRlbmVyIHZhcmlhYmxlcyByZWxldmFudGVzIHBhcmEgZWwgYW7DoWxpc2lzKiogIApQYXJhICoqZGVsaXZlcnkgcGVyZm9ybWFuY2UqKiBzZSBvcHRvIHBvciBtYW50ZW5lciB0b2RhcyBsYXMgY29sdW1uYXMuICAKCjxjZW50ZXI+IAo8c3BhbiBzdHlsZT0iY29sb3I6ICNGRkE1NEYiPiAqKkVuIGVzdGEgYmFzZSBkZSBkYXRvcyAoRGVsaXZlcnkgUGVyZm9ybWFuY2UpIGhheSBxdWUgdmVyaWZpY2FyIHF1ZSBsYXMgY29sdW1uYXMgZXN0w6luIGVuIGVsIGZvcm1hdG8gY29ycmVjdG8gYWRlbcOhcyBkZSBpZGVudGlmaWNhciB2YWxvcmVzIGNvbW8gTi9BLCB2YWxvcmVzIGF0w61waWNvcywgZW50cmUgb3Ryb3MuIFNlIHJldmlzYXJhIGNvbHVtbmEgcG9yIGNvbHVtbmEuKiogPC9zcGFuPgo8L2NlbnRlcj4gCgoqKkNsaWVudGUqKiAgCkNvbW8gcG9kZW1vcyBvYnNlcnZhciBlbiBjbGllbnRlIG5vIGhheSBlcnJvcmVzIG9ydG9ncsOhZmljb3MgeSBlbCBmb3JtYXRvIGRlIGxhIGNvbHVtbmEgZXMgZWwgY29ycmVjdG8uCmBgYHtyfQp1bmlxdWUocGVyZiRjbGllbnRlKQpzdHIocGVyZiRjbGllbnRlKQpzdW0oaXMubmEocGVyZiRjbGllbnRlKSkKYGBgCgoqKlZ1ZWx0YXMqKiAgCkRlIGlndWFsIG1hbmVyYSBlbiB2dWVsdGFzIG5vIGhheSBlcnJvcmVzIG9ydG9ncsOhZmljb3MgbyB2YWxvcmVzIGF0w61waWNvcy4KYGBge3J9CnVuaXF1ZShwZXJmJHZ1ZWx0YXMpCnN0cihwZXJmJHZ1ZWx0YXMpCnN1bShpcy5uYShwZXJmJHZ1ZWx0YXMpKQpgYGAKCioqRmVjaGEqKiAgCkVuIGVzdGEgdmFyaWFibGUgbm8gaGF5IHZhbG9yZXMgZmFsdGFudGVzLiBTaW4gZW1iYXJnbyBsYSBjb2x1bW5hIGRlIGZlY2hhIGVzdMOhIGNvbW8gY2FyYWN0ZXIsIHBvciBsbyBxdWUgaGF5IHF1ZSBoYWNlciBlbCBjYW1iaW8gZGUgZm9ybWF0byBhIGZlY2hhLiAKYGBge3J9CnBlcmYkZmVjaGEgPC1hcy5EYXRlKHBlcmYkZmVjaGEsIGZvcm1hdCA9IiVkLyVtLyVZIikKc3RyKHBlcmYkZmVjaGEpCnN1bShpcy5uYShwZXJmJGZlY2hhKSkKYGBgCgoqKlJlYWwgQXJyaXZhbCB5IFJlYWwgRGVwYXJ0dXJlKiogICAKUGFyYSBlc3RhcyBjb2x1bW5hcyBwb2RlbW9zIGVuY29udHJhciBxdWUgaGF5IHZhbG9yZXMgY29uIDAuIFBvciBsbyB0YW50byB2YW1vcyBhIGVsaW1pbmFyIGxvcyByZWdpc3Ryb3MgcXVlIHRlbmdhbiAwIGVuIGVzdGFzIGNvbHVtbmFzLiBBc8OtIG1pc21vIHF1ZSBoYXkgdmFsb3JlcyBlbiBmb3JtYXRvIGRlIDI0IGhvcmFzIHkgaGF5IHF1ZSBwYXNhcmxvcyBhIGZvcm1hdG8gZGUgMTIgaG9yYXMuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwZXJmIDwtICBwZXJmICU+JSBtdXRhdGUocmVhbF9hcnJpdmFsPWlmZWxzZShyZWFsX2Fycml2YWwgPiAxMiwgcmVhbF9hcnJpdmFsLTEyLCByZWFsX2Fycml2YWwpKQpwZXJmIDwtICBwZXJmICU+JSBtdXRhdGUocmVhbF9kZXBhcnR1cmU9aWZlbHNlKHJlYWxfZGVwYXJ0dXJlID4gMTIsIHJlYWxfZGVwYXJ0dXJlLTEyLCByZWFsX2RlcGFydHVyZSkpCnBlcmYgPC0gZmlsdGVyKHBlcmYsIHBlcmYkcmVhbF9hcnJpdmFsPjAgJiBwZXJmJHJlYWxfZGVwYXJ0dXJlPjApCnVuaXF1ZShwZXJmJHJlYWxfYXJyaXZhbCkKdW5pcXVlKHBlcmYkcmVhbF9kZXBhcnR1cmUpCmBgYApBbCBoYWNlcmxvLCBsb3MgcmVnaXN0cm9zIGRlIGxvcyBjbGllbnRlcyBkZSBWQVJST0MgeSBNQUdOQSBzZSBlbGltaW5hcm9uIGRlIGxhIGJhc2UgZGUgZGF0b3MuICAKCioqUGxhbiBBcnJpdmFsKiogICAKClByaW1lcmFtZW50ZSBoYXkgcXVlIHBhc2FyIGVsIGZvcm1hdG8gZGUgMjQgaG9yYXMgYSAxMiBob3Jhcy4gUGFyYSBlc3RvIHNlIHJlbXBsYXphcm9uIGxvcyB2YWxvcmVzIGRlIDIwIHkgMTYgYSA4IHkgNC4KYGBge3J9CnBlcmYkcGxhbl9hcnJpdmFsIDwtIHJlcGxhY2UocGVyZiRwbGFuX2Fycml2YWwscGVyZiRwbGFuX2Fycml2YWw9PTIwLDgpCnBlcmYkcGxhbl9hcnJpdmFsIDwtIHJlcGxhY2UocGVyZiRwbGFuX2Fycml2YWwscGVyZiRwbGFuX2Fycml2YWw9PTE2LDQpCnVuaXF1ZShwZXJmJHBsYW5fYXJyaXZhbCkKYGBgCgpQYXJhICoqUGxhbiBBcnJpdmFsKiogZXMgcG9zaWJsZSBvYnNlcnZhciBxdWUgaGF5IHJlZ2lzdHJvcyBjb24gMCwgZXN0b3Mgc2Vyw6FuICoqcmVtcGxhemFkb3MgY29uIGVsIHByb21lZGlvIG8gbGEgbWVkaWFuYSoqIGRlIHBsYW4gYXJyaXZhbCBwb3IgY2xpZW50ZSBwb3IgdnVlbHRhLCBlc3RvIGNvbiBlbCBwcm9ww7NzaXRvIHF1ZSBzZWEgbG8gbcOhcyBwcmVjaXNvIHBvc2libGUuCmBgYHtyfQpzdW0oaXMubmEocGVyZiRwbGFuX2Fycml2YWwpKQpgYGAKCkFsIG9ic2VydmFyIGVsIHZhbG9yIGRlIHNrZXduZXNzLCBwb2RlbW9zIG9ic2VydmFyIHF1ZSBlbCB2YWxvciBlcyBtYXlvciBhIDEsIChhdW5xdWUgc2VhIG5lZ2F0aXZvIHN1IHZhbG9yIHN1cGVyYSBlbCAxKSBwb3IgbG8gcXVlIHN1IGRpc3RyaWJ1Y2nDs24gbm9zIHN1Z2llcmUgcXVlIGVzIG1lam9yIHJlbXBsYXphciBjb24gbGEgbWVkaWFuYS4KYGBge3J9CmRlc2NyaWJlKHBlcmYkcGxhbl9hcnJpdmFsKQpgYGAKCkNvbW8gcXVlcmVtb3MgcmVtcGxhemFyIGVsIDAgdW5pY2FtZW50ZSBlbiBlbCBjbGllbnRlIE1BSExFIHkgZW4gbGEgdnVlbHRhIDMgZGViZW1vcyBoYWNlciBsbyBzaWd1aWVudGUuCmBgYHtyfQpwZXJmIDwtIHBlcmYgJT4lIG11dGF0ZShwbGFuX2Fycml2YWw9aWZlbHNlKHBsYW5fYXJyaXZhbCA9PSAwIHwgY2xpZW50ZSA9PSAiTUFITEUifCB2dWVsdGFzID09IDMsOCxwbGFuX2Fycml2YWwpKQp1bmlxdWUocGVyZiRwbGFuX2Fycml2YWwpCmBgYAoKRW4gZWwgdmFsb3IgZGUgc2tld25lc3MsIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGVsIHZhbG9yIGVzIG1lbm9yIGEgMSBwb3IgbG8gcXVlIHN1IGRpc3RyaWJ1Y2nDs24gbm9zIHN1Z2llcmUgcXVlIGVzIG1lam9yIHJlbXBsYXphciBjb24gZWwgcHJvbWVkaW8uIEFkZW3DoXMgcG9kZW1vcyBvYnNlcnZhciBxdWUgbGEgbWVkaWFuYSBlcyAwIGRlYmlkbyBhIHF1ZSBtdWNob3MgdmFsb3JlcyB0aWVuZW4gMCBjb21vIHBsYW4gYXJyaXZhbC4KYGBge3J9CmRlc2NyaWJlKHBlcmYkcGxhbl9hcnJpdmFsKQpgYGAKClBhcmEgKipyZW1wbGF6YXIgMCBjb24gZWwgcHJvbWVkaW8qKiB2YW1vcyBhIGNhbGN1bGFyIGVsIHByb21lZGlvIHBvciBjbGllbnRlIHBvciB2dWVsdGEuCmBgYHtyfQphZ2dyZWdhdGUocGxhbl9hcnJpdmFsIH4gY2xpZW50ZSArIHZ1ZWx0YXMsIGRhdGEgPSBwZXJmLCBtZWFuKQpgYGAKCkNvbW8gc2UgcXVpZXJlIHJlbXBsYXphciBlbCAwIHVuaWNhbWVudGUgZW4gZWwgY2xpZW50ZSBNQUhMRSB5IGVuIGxhIHZ1ZWx0YSAzIGRlYmVtb3MgaGFjZXIgbG8gc2lndWllbnRlLgpgYGB7cn0KcGVyZiA8LSBwZXJmICU+JSBtdXRhdGUocGxhbl9hcnJpdmFsPWlmZWxzZShwbGFuX2Fycml2YWwgPT0gMCB8IGNsaWVudGUgPT0gIk1BSExFInwgdnVlbHRhcyA9PSAzLDIwLHBsYW5fYXJyaXZhbCkpCnVuaXF1ZShwZXJmJHBsYW5fYXJyaXZhbCkKYGBgCgoqKkRpZmZlcmVuY2UqKiAgCkVzdGEgY29sdW1uYSBzZSBzdXBvbmUgZXMgbGEgZGlmZXJlbmNpYSBxdWUgaGF5IGVudHJlIHJlYWwgYXJyaXZhbCB5IHJlYWwgZGVwYXJ0dXJlLCBzaW4gZW1iYXJnbywgY29tbyBzZSBwdWVkZSBvYnNlcnZhciBubyB0b2RvcyBsb3MgcmVnaXN0cm9zIHRpZW5lbiBlbCBjYWxjdWxvIGNvcnJlY3RvLCBlIGluY2x1c2l2ZSBhbGd1bmFzIG5pIHNpIHF1aWVyYSB0aWVuZW4gZWwgY2FsY3VsbyBwb3IgbG8gcXVlIGVzIG5lY2VzYXJpbyBtb2RpZmljYXIgZXN0YSBjb2x1bW5hLgpgYGB7cn0KcGVyZiRwbGFuX2Fycml2YWwgPC0gYXMubnVtZXJpYyhwZXJmJHBsYW5fYXJyaXZhbCkKcGVyZiRyZWFsX2Fycml2YWwgPC0gYXMubnVtZXJpYyhwZXJmJHJlYWxfYXJyaXZhbCkKcGVyZiRyZWFsX2RlcGFydHVyZSA8LSBhcy5udW1lcmljKHBlcmYkcmVhbF9kZXBhcnR1cmUpCnBlcmYgPC0gbXV0YXRlKHBlcmYsIGRpZmZlcmVuY2U9cmVhbF9kZXBhcnR1cmUtcmVhbF9hcnJpdmFsKQpwZXJmIDwtIGRwbHlyOjpzZWxlY3QocGVyZiwtZGlmZmVyZW5jZSkKaGVhZChwZXJmLDUpCmBgYAoKIyMjIERlbGl2ZXJ5IFBsYW4KCioqQ2FtYmlhciBub21icmUgZGUgdmFyaWFibGVzKioKYGBge3J9CnBsYW48LWNsZWFuX25hbWVzKHBsYW4pCnBsYW48LXBsYW4gJT4lIGRwbHlyOjpyZW5hbWUoY2xpZW50ZT1jbGllbnRlX3BsYW50YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganVuXzIxPWp1bmlvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdWxfMjE9anVsaW8sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnb18yMT1hZ29zdG8sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcF8yMT1zZXB0aWVtYnJlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvY3RfMjE9b2N0dWJyZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm92XzIxPW5vdmllbWJyZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGljXzIxPWRpY2llbWJyZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2N0XzIyPW9jdHVicmVfMjIpCmNvbG5hbWVzKHBsYW4pCmBgYAoKKipNYW50ZW5lciB2YXJpYWJsZXMgcmVsZXZhbnRlcyBwYXJhIGVsIGFuw6FsaXNpcyoqICAKUGFyYSAqKmRlbGl2ZXJ5IHBsYW4qKiBwb3IgZmluZXMgZGUgZXN0cnVjdHVyYSB5IGFuw6FsaXNpcyBzZSBoYSBvdGFkbyBwb3IgdHJhbnNmb3JtYXIgbGEgYmFzZSBkZSBkYXRvcyB5IGxhcyBjb2x1bW5hcyBkZSBjYWRhIG1lcyBjb252ZXJ0aXJsYXMgZW4gZGF0b3MgZGUgdW5hIHNvbGEgY29sdW1uYSBkZW5vbWluYWRhIGNvbW8gTWVzIHkgbG9zIHZhbG9yZXMgcGFzYXJsb3MgYSBvdHJhIGNvbHVtbmEgbGxhbWFkYSBVbmlkYWRlcy4KYGBge3J9CnBsYW48LXBpdm90X2xvbmdlcihwbGFuLCBjb2xzPTU6MjYsIG5hbWVzX3RvID0gIm1lcyIsIHZhbHVlc190byA9ICJ1bmlkYWRlcyIpCmBgYAoKWWEgcXVlIGNvbnRhbW9zIGNvbiBsYSBiYXNlIGRlIGRhdG9zIHRyYW5zZm9ybWFkYSBzZSBoYSBvcHRhZG8gcG9yIGVsaW1pbmFyIGxhIGNvbHVtbmEgZGUgdG90YWwgbWVzZXMgeWEgcXVlIHBvZGVtb3MgY2FsY3VsYXIgZWwgdG90YWwgZGUgdG9kb3MgbG9zIG1lc2VzIHBvciBwcm9kdWN0b3MgcG9zdGVyaW9ybWVudGUsIHkgbGEgZGUgaWRfb2Rvby4KYGBge3J9CnBsYW48LWRwbHlyOjpzZWxlY3QocGxhbiwtYyh0b3RhbF9tZXNlcyxpZF9vZG9vKSkKYGBgCgo8Y2VudGVyPiAKPHNwYW4gc3R5bGU9ImNvbG9yOiAjRkZBNTRGIj4gKipFbiBlc3RhIGJhc2UgZGUgZGF0b3MgKERlbGl2ZXJ5IFBsYW4pIGhheSBvdHJvcyBlbGVtZW50b3MgYSBjb25zaWRlcmFyIHBvciBlc3RvIHJldmlzYXJlbW9zIGNhZGEgY29sdW1uYSB5IGxvcyByZWdpc3Ryb3MgcXVlIGxhIGNvbmZvcm1hbioqIDwvc3Bhbj4KPC9jZW50ZXI+IAoKKipDbGllbnRlIFBsYW50YSoqICAKTm8gaGF5IGVycm9yZXMgb3J0b2dyw6FmaWNvcyBwb3IgbG8gcXVlIG5vIHNlIGhhcmFuIGNhbWJpb3MgZW4gZXN0YSBjb2x1bW5hLiAgCmBgYHtyfQp1bmlxdWUocGxhbiRjbGllbnRlKQpgYGAKCioqUHJveWVjdG8qKiAgCkRlIGlndWFsIG1hbmVyYSBwYXJhIHByb3llY3RvIG5vIHNlIGVuY29udHJhbW9zIGVycm9yZXMgZGUgaW5jb25zaXN0ZW5jaWEuICAKYGBge3IgcmVzdWx0cz0naGlkZSd9CnVuaXF1ZShwbGFuJHByb3llY3RvKQpgYGAKCioqSXRlbSoqICAKRGUgaWdhdWwgbWFuZXJhIHBhcmEgaXRlbSBubyBzZSByZWFsaXphcm9uIG1vZGlmaWNhY2lvbmVzLiAgCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQp1bmlxdWUocGxhbiRpdGVtKQpgYGAKCioqTWVzKiogIApQYXJhIGxhIGNvbHVtbmEgZGUgbWVzIG5vIHNlIGVuY29udHJhcm9uICBlcnJvcmVzIG9ydG9ncsOhZmljb3MgcG9yIGxvIHF1ZSBubyBzZSBoYXJhbiBtb2RpZmljYWNpb25lcy4KYGBge3J9CnVuaXF1ZShwbGFuJG1lcykKYGBgCgoqKlVuaWRhZGVzKiogIApQYXJhIHVuaWRhZGVzIHNlIG9wdG8gcG9yIGVsaW1pbmFyIGxvcyByZWdpc3Ryb3MgY29uIDAgdW5pZGFkZXMsIHlhIHF1ZSBhbCBjb252ZXJ0aXIgbG9zIG1lc2VzIGVuIHVuYSBzb2xhIGNvbHVtbmEsIHRvZG9zIGFxdWVsbG9zIG1lc2VzIGRvbmRlIG5vIGhhYsOtYSBwcm9kdWNjacOzbiBwbGFuZWFkYSB0aWVuZW4gMC4gVGFtYmnDqW4gc2UgY29ycmFib3JvIHF1ZSBlbCBmb3JtYXRvIGRlIGxhIGNvbHVtbmEgc2VhIGVsIGNvcnJlY3RvLCBkZSBlbnRlcm8uCmBgYHtyfQpwbGFuIDwtIGZpbHRlcihwbGFuLCB1bmlkYWRlcz4wKQpzdHIocGxhbiR1bmlkYWRlcykKYGBgCgoKIyMjIEJhamFzCgoqKkNhbWJpYXIgbm9tYnJlIGRlIHZhcmlhYmxlcyoqCmBgYHtyfQpiYWphczwtY2xlYW5fbmFtZXMoYmFqYXMpCmJhamFzPC1iYWphcyAlPiUgZHBseXI6OnJlbmFtZShhcGVsbGlkb3M9YXBlbGxpZG9zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWNoYV9uYWM9ZmVjaGFfZGVfbmFjaW1pZW50bywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVjaGFfYWx0YT1mZWNoYV9kZV9hbHRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb3RfYmFqYT1tb3Rpdm9fZGVfYmFqYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9fc3M9bm9fc2VndXJvX3NvY2lhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsX2RpYXJpb19pbXNzPXNhbGFyaW9fZGlhcmlvX2ltc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNyZWRpdG9faW5mb25hdml0PW5fY3JlZGl0b19pbmZvbmF2aXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGx1Z2FyX25hYz1sdWdhcl9kZV9uYWNpbWllbnRvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1faW50ZXJubz1udW1lcm9faW50ZXJubywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ1A9Y29kaWdvX3Bvc3RhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVybWFuZW5jaWE9eCkKY29sbmFtZXMoYmFqYXMpCmBgYAoKKipNYW50ZW5lciB2YXJpYWJsZXMgcmVsZXZhbnRlcyBwYXJhIGVsIGFuw6FsaXNpcyoqICAKUGFyYSBsYSBiYXNlIGRlIGRhdG9zIGRlICoqYmFqYXMqKiBzZSBvcHRvIHBvciBoYWNlciB1bmEgdHJhbnNmb3JtYWNpw7NuIGEgbGEgYmFzZSBkZSBkYXRvcyB5IHVuaXIgbG9zIGFwZWxsaWRvcyBjb24gZWwgbm9tYnJlLCBhZGVtw6FzIGRlIGVsaW1pbmFyIGxhcyBjb2x1bW5hcyBkZSBSRkMsIG7Dum1lcm8gZGUgc2VndXJvIHNvY2lhbCwgZmFjdG9yIGRlIGNyw6lkaXRvIGluZm9uYXZpdCwgY3LDqWRpdG8gaW5mb25hdml0LCBDVVJQLCBsdWdhciBkZSBuYWNpbWllbnRvLCBjYWxsZSwgbnVtZXJvIGludGVybm8sIHkgdGFyamV0YSBkZSBjdWVudGEuCmBgYHtyfQoKYmFqYXM8LW11dGF0ZShiYWphcyxub21icmVfY29tcGxldG8gPSBwYXN0ZShiYWphcyRub21icmUsIGJhamFzJGFwZWxsaWRvcywgc2VwPSIgIikpCmJhamFzPC1kcGx5cjo6c2VsZWN0KGJhamFzLC1jKHJmYywgbm9fc3MsZmFjdG9yX2NyZWRfaW5mb25hdml0LGNyZWRpdG9faW5mb25hdml0LGN1cnAsdGFyamV0YV9jdWVudGEsYXBlbGxpZG9zLG5vbWJyZSxsdWdhcl9uYWMsY2FsbGUsbnVtX2ludGVybm8pKQpiYWphczwtYmFqYXNbLGMoMTUsMSwyLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQpXQpjb2xuYW1lcyhiYWphcykKYGBgCgo8Y2VudGVyPiAKPHNwYW4gc3R5bGU9ImNvbG9yOiAjRkZBNTRGIj4gKipFbiBlc3RhIGJhc2UgZGUgZGF0b3MgKEJhamFzKSBoYXkgb3Ryb3MgZWxlbWVudG9zIGEgY29uc2lkZXJhciBwb3IgZXN0byByZXZpc2FyZW1vcyBjYWRhIGNvbHVtbmEgeSBsb3MgcmVnaXN0cm9zIHF1ZSBsYSBjb25mb3JtYW4qKiA8L3NwYW4+CjwvY2VudGVyPiAKCioqTm9tYnJlIGNvbXBsZXRvLCBmZWNoYSBkZSBuYWNpbWllbnRvIHkgZmVjaGEgZGUgYWx0YSoqICAKQ29udmVydGlyIGEgZm9ybWF0byBkZSBmZWNoYSBsYSBmZWNoYSBkZSBhbHRhIHkgbGEgZmVjaGEgZGUgbmFjaW1pZW50by4KYGBge3J9CmJhamFzJGZlY2hhX25hYyA8LSBhcy5EYXRlKGJhamFzJGZlY2hhX25hYywgZm9ybWF0ID0iJWQvJW0vJVkiKQpzdHIoYmFqYXMkZmVjaGFfbmFjKQpiYWphcyRmZWNoYV9hbHRhIDwtIGFzLkRhdGUoYmFqYXMkZmVjaGFfYWx0YSwgZm9ybWF0ID0iJWQvJW0vJVkiKQpzdHIoYmFqYXMkZmVjaGFfYWx0YSkKYGBgCgoqKkfDqW5lcm8qKiAgCkVuIGxhIGNvbHVtbmEgZGUgZ8OpbmVybyBzb2xvIGhheSB1biB2YWxvciB2YWPDrW8sIGJhc2Fkb3MgZW4gc3Ugbm9tYnJlIGNvbXBsZXRvIHZhbW9zIGEgYXN1bWlyIHF1ZSBzdSBnw6luZXJvIGVzIG1hc2N1bGluby4gQXPDrSBtaXNtbywgaGF5IHVuIHJlZ2lzdHJvIGNvbiBsbyBxdWUgcGFyZWNlIHNlciB1biBSRkMgZW4gbHVnYXIgZGVsIGfDqW5lcm8sIGJhc2FkbyBlbiBlbCBub21icmUgZGUgZXNhIHBlcnNvbmEgYXN1bWlyZW1vcyBxdWUgZXMgZmVtZW5pbm8uCmBgYHtyfQpiYWphcyRnZW5lcm8gPC0gcmVwbGFjZShiYWphcyRnZW5lcm8sYmFqYXMkZ2VuZXJvPT0iIiwiTUFTQ1VMSU5PIikgCmJhamFzJGdlbmVybyA8LSByZXBsYWNlKGJhamFzJGdlbmVybyxiYWphcyRnZW5lcm89PSJDQVBKMDAwOTI2NTk3IiwiRkVNRU5JTk8iKQpgYGAKCioqQ8OzZGlnbyBQb3N0YWwqKiAgClBhcmEgZWwgY8OzZGlnbyBwb3N0YWwgaGF5IHF1ZSByZW1wbGF6YXIgbG9zIHJlZ2lzdHJvcyBxdWUgdGVuZ2FuIGVsIG11bmljaXBpbyBlbiBsdWdhciBkZWwgQ1AgcG9yIHN1IHJlc3BlY3Rpdm8gQ1AuCmBgYHtyfQpiYWphczIgPC0gYmFqYXMKYmFqYXMyIDwtIGJhamFzMiAlPiUgbXV0YXRlKGJhamFzMiwgQ1AyPUNQKQpiYWphczIgPC0gYmFqYXMyICU+JSBtdXRhdGUoYmFqYXMyLCBtdW5pY2lwaW8yPW11bmljaXBpbykKYmFqYXMyIDwtIGJhamFzMiAlPiUgbXV0YXRlKGJhamFzMiwgZXN0YWRvMj1lc3RhZG8pCgojUmVtcGxhemFyIGVsIG11bmljaXBpbyBwb3IgZWwgQ1AgZW4gbGEgY29sdW1uYSBkZSBDUCwgdGFtYmllbiByZW1wbGF6YXIgdW5pb24gbGlicmUgcG9yIGVsIENQCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUoQ1A9aWZlbHNlKENQID09ICJBUE9EQUNBIiB8IENQID09ICJKVUFSRVoiIHwgQ1AgPT0gIk1PTlRFUlJFWSIgfCBDUCA9PSAiR1VBREFMVVBFIiB8IENQID09ICJSQU1PUyBBUklaUEUifCBDUCA9PSAiU0FOIFBEUk8gQ09BSCJ8IENQID09ICJQRVNRVUVSSUEifCBDUCA9PSAiQ0FERVJFWVRBInwgQ1AgPT0gIlNBTiBOSUNPTEFTIERFIExPUyBHQVJaQSJ8IENQID09ICJTQUxUSUxMTyJ8IENQID09ICJaVUFaVUEifCBDUCA9PSAiQ0lFTkVHQSBERSBGTE9SRVMifCBDUCA9PSAiTlVFVk8gTEVPTiIsIGVzdGFkbyAsIENQKSkKYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShDUD1pZmVsc2UoQ1AgPT0gIlVOSU9OIExJQlJFIiwgbXVuaWNpcGlvICwgQ1ApKQp1bmlxdWUoYmFqYXMyJENQKQoKYGBgCgoqKk11bmljaXBpbyoqICAKRW4gbXVuaWNpcGlvIGhheSBxdWUgcmVtcGxhemFyIGxvcyByZWdpc3Ryb3MgcXVlIHRpZW5lbiBlbCBlc3RhZG8gcG9yIHN1IHJlc3BlY3Rpdm8gbXVuaWNpcGlvLiBBZGVtw6FzIGRlIGVzdGFuZGFyaXphciBsb3MgcmVnaXN0cm9zLCBwb3IgZWplbXBsbywgY2FtYmlhciAiU0FOIE5JQ09MQVMgREUgTE9TIEciIHBvciAiU0FOIE5JQ09MQVMgREUgTE9TIEdBUlpBIi4KYGBge3J9CiNSZW1wbGF6YXIgZWwgZXN0YWRvIHBvciBlbCBtdW5pY2lwaW8KYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShtdW5pY2lwaW89aWZlbHNlKG11bmljaXBpbyA9PSAiTlVFVk8gTEVPTiIgfCBtdW5pY2lwaW8gPT0gIk51ZXZvIExlw4PCs24iIHwgbXVuaWNpcGlvID09ICJDT0FIVUlMQSIgfCBtdW5pY2lwaW8gPT0gIkNvYWh1aWxhIixDUDIgLCBtdW5pY2lwaW8pKQpiYWphczIgPC0gIGJhamFzMiAlPiUgbXV0YXRlKG11bmljaXBpbz1pZmVsc2UobXVuaWNpcGlvID09IDY2NDQ0ICwgY29sb25pYSAsIG11bmljaXBpbykpCgojQ2FtYmlhcyBTQU4gTklDT0xBUyBERSBMT1MgRyB4IFNBTiBOSUNPTEFTIERFIExPUyBHQVJaQQpiYWphczIkbXVuaWNpcGlvIDwtIHJlcGxhY2UoYmFqYXMyJG11bmljaXBpbyxiYWphczIkbXVuaWNpcGlvPT0iU0FOIE5JQ09MQVMgREUgTE9TIEciLCJTQU4gTklDT0xBUyBERSBMT1MgR0FSWkEiKQoKdW5pcXVlKGJhamFzMiRtdW5pY2lwaW8pCmBgYAoKKipFc3RhZG8qKiAgCkVuIGVzdGFkbyBoYXkgcXVlIHJlbXBsYXphciBsb3MgcmVnaXN0cm9zIHF1ZSB0aWVuZW4gQ1AgcG9yIGVsIGVzdGFkbyBjb3JyZWN0by4gQXPDrSBtaXNtbywgKiplc3RhbmRhcml6YXIgbG9zIHJlZ2lzdHJvcyoqLCAgcG9yIGVqZW1wbG8sIGNhbWJpYXIgIk51ZXZvIExlw4PCs24iIHBvciAiTlVFVk8gTEVPTiIuCmBgYHtyfQojUmVtcGxhemFyIGVsIENQIHBvciBlbCBlc3RhZG8gY29ycmVjdG8gZW4gbGEgY29sdW1uYSBkZSBlc3RhZG8KYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShlc3RhZG89aWZlbHNlKG11bmljaXBpbyA9PSAiU0FOIE5JQ09MQVMgREUgTE9TIEdBUlpBIiB8IG11bmljaXBpbyA9PSAiUEVTUVVFUklBIiB8IG11bmljaXBpbyA9PSAiQVBPREFDQSIgfCBtdW5pY2lwaW8gPT0gIkpVQVJFWiIgfCBtdW5pY2lwaW8gPT0gIkdVQURBTFVQRSJ8IG11bmljaXBpbyA9PSAiUkFNT1MgQVJJWlBFInwgbXVuaWNpcGlvID09ICJDQURFUkVZVEEifCBtdW5pY2lwaW8gPT0gIlpVQVpVQSJ8IG11bmljaXBpbyA9PSAiQ0lFTkVHQSBERSBGTE9SRVMifCBtdW5pY2lwaW8gPT0gIk1PTlRFUlJFWSIsICJOVUVWTyBMRU9OIiAsIG11bmljaXBpbykpCgpiYWphczIkZXN0YWRvIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkbyxiYWphczIkZXN0YWRvPT0iU0FOIFBEUk8gQ09BSCIsIkNPQUhVSUxBIikKYmFqYXMyJGVzdGFkbyA8LSByZXBsYWNlKGJhamFzMiRlc3RhZG8sYmFqYXMyJGVzdGFkbz09IlNBTFRJTExPIiwiQ09BSFVJTEEiKQoKdW5pcXVlKGJhamFzMiRlc3RhZG8pCmBgYAoKCioqRXN0YWRvIENpdmlsKiogIApFbiBlc3RhZG8gY2l2aWwgaGF5IHF1ZSAqKmVzdGFuZGFyaXphciBsb3MgcmVnaXN0cm9zKiosIHBvciBlamVtcGxvLCBjYW1iaWFyICJVTklPTkxJQlJFIiBwb3IgIlVOSU9OIExJQlJFIi4KYGBge3J9CmJhamFzMiRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShiYWphczIkZXN0YWRvX2NpdmlsLGJhamFzMiRlc3RhZG9fY2l2aWw9PSJVTklPTkxJQlJFIiwiVU5JT04gTElCUkUiKQpiYWphczIkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkb19jaXZpbCxiYWphczIkZXN0YWRvX2NpdmlsPT0iVW5pw4PCs24gbGlicmUiLCJVTklPTiBMSUJSRSIpCmJhamFzMiRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShiYWphczIkZXN0YWRvX2NpdmlsLGJhamFzMiRlc3RhZG9fY2l2aWw9PSJTb2x0ZXJvIiwiU09MVEVSTyhBKSIpCmJhamFzMiRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShiYWphczIkZXN0YWRvX2NpdmlsLGJhamFzMiRlc3RhZG9fY2l2aWw9PSJDYXNhZG8iLCJDQVNBRE8oQSkiKQpiYWphczIkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoYmFqYXMyJGVzdGFkb19jaXZpbCxiYWphczIkZXN0YWRvX2NpdmlsPT0iQ0FTQURBIiwiQ0FTQURPKEEpIikKYmFqYXMyJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGJhamFzMiRlc3RhZG9fY2l2aWwsYmFqYXMyJGVzdGFkb19jaXZpbD09IlNPTFRFUkEiLCJTT0xURVJPKEEpIikKYmFqYXMyJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGJhamFzMiRlc3RhZG9fY2l2aWwsYmFqYXMyJGVzdGFkb19jaXZpbD09IkRJVk9SQ0lBREEiLCJESVZPUkNJQURPKEEpIikKYmFqYXMyJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGJhamFzMiRlc3RhZG9fY2l2aWwsYmFqYXMyJGVzdGFkb19jaXZpbD09IkNBU0FETyIsIkNBU0FETyhBKSIpCmJhamFzMiRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShiYWphczIkZXN0YWRvX2NpdmlsLGJhamFzMiRlc3RhZG9fY2l2aWw9PSJESVZPUkNJQURPIiwiRElWT1JDSUFETyhBKSIpCmJhamFzMiRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShiYWphczIkZXN0YWRvX2NpdmlsLGJhamFzMiRlc3RhZG9fY2l2aWw9PSJTT0xURVJPIiwiU09MVEVSTyhBKSIpCnVuaXF1ZShiYWphczIkZXN0YWRvX2NpdmlsKQoKYGBgCgoqKlB1ZXN0byoqICAKUGFyYSBwdWVzdG8gdmFtb3MgYSB1bmlmaWNhciBhcXVlbGxvcyBwdWVzdG9zIHF1ZSBzb24gc2ltaWxhcmVzIHkgcXVlIHRlbmdhbiBlcnJvcmVzIG9ydG9ncsOhZmljb3MuClBvciBlamVtcGxvLCAiQVkuIEdFTkVSQUwgKE1BVEVSSUFMRVMpIiB5ICJBWVVEQU5URSBERSBFTUJBUlFVRVMiIHBhc2FybG9zIGEgIkFZVURBTlRFIEdFTkVSQUwiLgpgYGB7cn0KYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkFZLkdFTkVSQUwgKE1BVEVSSUFMRVMpIiwiQVlVREFOVEUgR0VORVJBTCIpCmJhamFzMiRwdWVzdG8gPC0gcmVwbGFjZShiYWphczIkcHVlc3RvLGJhamFzMiRwdWVzdG89PSJBWVVEQU5URSBERSBFTUJBUlFVRVMiLCJBWVVEQU5URSBHRU5FUkFMIikKYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkFZLiBHRU5FUkFMIiwiQVlVREFOVEUgR0VORVJBTCIpCmJhamFzMiRwdWVzdG8gPC0gcmVwbGFjZShiYWphczIkcHVlc3RvLGJhamFzMiRwdWVzdG89PSJBWVVELkVNQkFSUVVFUyIsIkFZVURBTlRFIEdFTkVSQUwiKQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iQVlVREFOVEUgREUgU09MREFET1IiLCJBWVVEQU5URSBHRU5FUkFMIikKYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkFZVURBTlRFIERFIE1UVE8iLCJBWVVEQU5URSBHRU5FUkFMIikKYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkFZVURBTlRFIERFIFNPTERBRE9SIiwiQVlVREFOVEUgR0VORVJBTCIpCmJhamFzMiRwdWVzdG8gPC0gcmVwbGFjZShiYWphczIkcHVlc3RvLGJhamFzMiRwdWVzdG89PSJBWVVEQU5URSBHRU5FUkFMIERFIEVNQkFSUVVFUyIsIkFZVURBTlRFIEdFTkVSQUwiKQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iQ09TVFVSRVJBIiwiQ09TVFVSRVJPKEEpIikKYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IkNPU1RVUkVSTyIsIkNPU1RVUkVSTyhBKSIpCmJhamFzMiRwdWVzdG8gPC0gcmVwbGFjZShiYWphczIkcHVlc3RvLGJhamFzMiRwdWVzdG89PSJJTlNQRUNUT1IgREUgQ0FMSURBRCIsIklOU1BFQ1RPUihBKSBERSBDQUxJREFEIikKYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IklOU1BFQ1RPUkEgREUgQ0FMSURBRCIsIklOU1BFQ1RPUihBKSBERSBDQUxJREFEIikKYmFqYXMyJHB1ZXN0byA8LSByZXBsYWNlKGJhamFzMiRwdWVzdG8sYmFqYXMyJHB1ZXN0bz09IklOU1BFQ1RPUiBDQUxJREFEIiwiSU5TUEVDVE9SKEEpIERFIENBTElEQUQiKQpiYWphczIkcHVlc3RvIDwtIHJlcGxhY2UoYmFqYXMyJHB1ZXN0byxiYWphczIkcHVlc3RvPT0iRElTRcOD4oCYTyIsIkRJU0XDkU8iKQpiYWphczIkcHVlc3RvIDwtIHRvdXBwZXIoYmFqYXMyJHB1ZXN0bykKdW5pcXVlKGJhamFzMiRwdWVzdG8pCmBgYAoKKipEZXBhcnRhbWVudG8qKiAgClBhcmEgZGVwYXJ0YW1lbnRvIHNlIHJlYWxpemFyYSBsbyBtaXNtbyBxdWUgZW4gcHVlc3RvLgpgYGB7cn0KCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJjZWxkYXMiLCJDRUxEQVMiKQpiYWphczIkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoYmFqYXMyJGRlcGFydGFtZW50byxiYWphczIkZGVwYXJ0YW1lbnRvPT0iQ2VsZGFzIiwiQ0VMREFTIikKYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09IkNlZGlzIiwiQ0VESVMiKQpiYWphczIkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoYmFqYXMyJGRlcGFydGFtZW50byxiYWphczIkZGVwYXJ0YW1lbnRvPT0iRW1iYXJxdWVzIiwiRU1CQVJRVUVTIikKYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09IkNvc3R1cmEiLCJDT1NUVVJBIikKYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09IlBhaWxlcmlhIiwiUEFJTEVSSUEiKQpiYWphczIkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoYmFqYXMyJGRlcGFydGFtZW50byxiYWphczIkZGVwYXJ0YW1lbnRvPT0iU3RhYmlsdXMiLCJTVEFCSUxVUyIpCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJUcm9xdWVsIiwiVFJPUVVFTCIpCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJNYW50ZW5pbWllbnRvIEZGIiwiTUFOVEVOSU1JRU5UTyIpCmJhamFzMiRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShiYWphczIkZGVwYXJ0YW1lbnRvLGJhamFzMiRkZXBhcnRhbWVudG89PSJzdGFiaWx1cyIsIlNUQUJJTFVTIikKYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09IlByb2R1Y2Npb24gQ2FydMODwrNuIE1DIiwiUFJPRFVDQ0lPTiBDQVJUT04gTUMiKQpiYWphczIkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoYmFqYXMyJGRlcGFydGFtZW50byxiYWphczIkZGVwYXJ0YW1lbnRvPT0iUHJvZHVjY2lvbiBDYXJ0w4PCs24gTURMIiwiUFJPRFVDQ0lPTiBDQVJUT04gTURMIikKYmFqYXMyJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGJhamFzMiRkZXBhcnRhbWVudG8sYmFqYXMyJGRlcGFydGFtZW50bz09IlByb2R1Y2Npw4PCs24gUmV0b3JuIiwiUFJPRFVDQ0lPTiBSRVRPUk5PIikKYmFqYXMyJGRlcGFydGFtZW50byA8LSB0b3VwcGVyKGJhamFzMiRkZXBhcnRhbWVudG8pCnVuaXF1ZShiYWphczIkZGVwYXJ0YW1lbnRvKQpgYGAKCioqTW90aXZvIGRlIEJhamEqKiAgIApQYXJhIG1vdGl2byBkZSBiYWphIHNlIHJlYWxpemFyYSBhbGdvIGRpZmVyZW50ZS4gQWwgYW5hbGl6YXIgbGEgYmFzZSBkZSBkYXRvcyBub3MgZGltb3MgY3VlbnRhIHF1ZSBtdWNob3MgZGUgbG9zIGVtcGxlYWRvcyBxdWUgdGllbmVuIG11eSBwb2NvcyBkw61hcywgcG9yIGVqZW1wbG8gZGUgMCBkw61hcyBhIDEwLCBlbiBsdWdhciBkZSB0ZW5lciBjb21vIG1vdGl2byBkZSBiYWphIEFCQU5ET05PLCB0aWVuZSBGQUxUQVMuIFBhcmEgcXVlIGVsIG1vdGl2byBkZSBiYWphIHNlYSBsYXMgZmFsdGFzLCBkZWJlcsOtYSBkZSBleGlzdGlyIHVuIHBlcmlvZG8gZXN0YWJsZWNpZG8gc29icmUgZWwgY3VhbCBzZSBsaW1pdGVuIGxhcyBmYWx0YXMuIFBvciBlamVtcGxvLCBjYWRhIGVtcGxlYWRvIHNvbG8gdGllbmUgcGVybWl0aWRhcyB0YW50YSBjYW50aWRhZCBkZSBmYWx0YXMgYWwgbWVzLiBQb3IgbG8gdGFudG8sIGFxdWVsbG9zIGVtcGxlYWRvcyBxdWUgdGllbmVuIG1lbm9zIGRlIDEwIGTDrWFzIGRlIHBlcm1hbmVuY2lhLCBjYW1iaWFyZW1vcyBzdSBtb3Rpdm8gZGUgYmFqYSBkZSBGQUxUQVMgYSBBQkFORE9OTy4KYGBge3J9CiNSZW1wbGF6YW1vcyBGQUxUQSBQT1IgQkFKQVMgcG9yIEFCQU5ET05PCmJhamFzMiA8LSAgYmFqYXMyICU+JSBtdXRhdGUobW90X2JhamE9aWZlbHNlKHBlcm1hbmVuY2lhIDwgMTAsICJBQkFORE9OTyIgLCBtb3RfYmFqYSkpCgojTWFudGVuZW1vcyBsb3MgbW90aXZvcyBkZSBiYWphIHF1ZSBubyBlcmFuIEZBTFRBIFBPUiBCQUpBUyB5IHRlbmlhbiBwZXJtYW5lbmNpYSBtZW5vciBkZSAxMCBkw61hcy4KYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShtb3RfYmFqYT1pZmVsc2Uobm9tYnJlX2NvbXBsZXRvID09ICJKT0FOQSBOT0hFTUkgQ09MVU5HQSBPUlRJWiIsICJSRU5VTkNJQSBWT0xVTlRBUklBIiAsIG1vdF9iYWphKSkKYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShtb3RfYmFqYT1pZmVsc2Uobm9tYnJlX2NvbXBsZXRvID09ICJFUk5FU1RPIEdPTlpBTEVaIENBU1RJTExPIiwgIlJFTlVOQ0lBIFZPTFVOVEFSSUEiICwgbW90X2JhamEpKQpiYWphczIgPC0gIGJhamFzMiAlPiUgbXV0YXRlKG1vdF9iYWphPWlmZWxzZShub21icmVfY29tcGxldG8gPT0gIk1PSVNFUyBMQVVSRUFOSSBKSU1FTkVaIiwgIlJFTlVOQ0lBIFZPTFVOVEFSSUEiICwgbW90X2JhamEpKQpiYWphczIgPC0gIGJhamFzMiAlPiUgbXV0YXRlKG1vdF9iYWphPWlmZWxzZShub21icmVfY29tcGxldG8gPT0gIlJPQ0lPIE9DSE9BIEdPTlpBTEVaIiwgIlJFTlVOQ0lBIFZPTFVOVEFSSUEiICwgbW90X2JhamEpKQpiYWphczIgPC0gIGJhamFzMiAlPiUgbXV0YXRlKG1vdF9iYWphPWlmZWxzZShub21icmVfY29tcGxldG8gPT0gIk1BUkdBUklUQSBSSVZBUyBHT05aQUxFWiIsICJSRU5VTkNJQSBWT0xVTlRBUklBIiAsIG1vdF9iYWphKSkKYmFqYXMyIDwtICBiYWphczIgJT4lIG11dGF0ZShtb3RfYmFqYT1pZmVsc2Uobm9tYnJlX2NvbXBsZXRvID09ICJHTE9SSUEgRkFDVU5ETyBNT05UT1lBIiwgIlJFTlVOQ0lBIFZPTFVOVEFSSUEiICwgbW90X2JhamEpKQpgYGAKCioqU2FsYXJpbyBEaWFyaW8gZGVsIElNU1MqKiAgClBhcmEgc2FsYXJpbyBkaWFyaW8gaGF5IHF1ZSBhc2VndXJhcm5vcyBxdWUgZXN0ZSBlbiBlbCBmb3JtYXRvIGNvcnJlY3RvLCBhZGVtw6FzIGRlIHJlbXBsYXphciBsb3MgdmFsb3JlcyBmYWx0YW50ZXMgZSBpZGVudGlmaWNhciB2YWxvcmVzIGF0w61waWNvcyB5IGRlZmluaXIgc2kgaGF5IHF1ZSByZW1wbGF6YXJsb3MuICAKYGBge3J9CmZpbHRlcihiYWphczIsaXMubmEoYmFqYXMyJHNhbF9kaWFyaW9faW1zcykpCmRlc2NyaWJlKGJhamFzMiRzYWxfZGlhcmlvX2ltc3MpCmBgYAoKQ29tbyBwb2RlbW9zIHZlciBoYXkgZG9zIHJlZ2lzdHJvcyBmYWx0YW50ZXMsIHBhcmEgZWwgZGUgIMOBbmdlbCByZW1wbGF6YXJlbW9zIGNvbiBsYSBtZWRpYW5hLCB5YSBxdWUgc3UgdmFsb3IgZGUgc2tld25lc3MgZGVsIHNhbGFyaW8gZGlhcmlvIGVzIG1heW9yIGEgMSwgaW5kaWNhbmRvbm9zIHF1ZSBlcyBtZWpvciB1c2FyIGxhIG1lZGlhbmEgcXVlIGxhIG1lZGlhLgpgYGB7cn0Kc3VtbWFyeShiYWphczIkc2FsX2RpYXJpb19pbXNzKQpgYGAKCmBgYHtyfQpiYWphczIgPC0gIGJhamFzMiAlPiUgbXV0YXRlKHNhbF9kaWFyaW9faW1zcz1pZmVsc2Uobm9tYnJlX2NvbXBsZXRvID09ICJBTkdFTCBHVUlMTEVSTU8gIFBBTE9NTyBMT1BFWiIsIDE4MC43LCBzYWxfZGlhcmlvX2ltc3MpKQpiYWphczIgPC0gIGJhamFzMiAlPiUgbXV0YXRlKHNhbF9kaWFyaW9faW1zcz1pZmVsc2Uobm9tYnJlX2NvbXBsZXRvID09ICJQT0xFVFQgQURSSUFOQSBMT1BFWiBSVUlaIiwgMTgwLjcsIHNhbF9kaWFyaW9faW1zcykpCnN1bShpcy5uYShiYWphczIkc2FsX2RpYXJpb19pbXNzKSkKYGBgCgpGaW5hbG1lbnRlIHRlcm1pbmFtb3MgZGUgbGltcGlhciBsYSBiYXNlIGRlIGRhdG9zIGRlIGFxdWVsbGFzIGNvbHVtbmFzIHF1ZSB0dXZpbW9zIHF1ZSBhZ3JlZ2FyIG8gcmVub21icmFyLgpgYGB7cn0KYmFqYXMyIDwtIGRwbHlyOjpzZWxlY3QoYmFqYXMyLCAtYyhDUDIsbXVuaWNpcGlvMixlc3RhZG8yKSkKYmFqYXMgPC0gYmFqYXMyCmNvbG5hbWVzKGJhamFzMikKYGBgCgoKIyMjIENvbGFib3JhZG9yZXMKCioqQ2FtYmlhciBub21icmUgZGUgdmFyaWFibGVzKioKYGBge3J9CmNvbGFib3JhZG9yZXM8LWNsZWFuX25hbWVzKGNvbGFib3JhZG9yZXMpCmNvbGFib3JhZG9yZXM8LWNvbGFib3JhZG9yZXMgJT4lIGRwbHlyOjpyZW5hbWUobm9fZW1wbGVhZG89bm9fZGVfZW1wbGVhZG8sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlY2hhX25hYz1mZWNoYV9kZV9uYWNpbWllbnRvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWNoYV9hbHRhPWZlY2hhX2RlX2FsdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0dG9fbWVzIj14NHRvX21lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9fc3M9bm9fc2VndXJvX3NvY2lhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FsX2RpYXJpb19pbXNzPXNhbGFyaW9fZGlhcmlvX2ltc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNyZWRpdG9faW5mb25hdml0PW5fY3JlZGl0b19pbmZvbmF2aXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGx1Z2FyX25hYz1sdWdhcl9kZV9uYWNpbWllbnRvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1faW50ZXJubz1udW1lcm9faW50ZXJubywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ1A9Y29kaWdvX3Bvc3RhbCkKY29sbmFtZXMoYmFqYXMpCmBgYAoKKipNYW50ZW5lciB2YXJpYWJsZXMgcmVsZXZhbnRlcyBwYXJhIGVsIGFuw6FsaXNpcyoqICAKUGFyYSBsYSBiYXNlIGRlIGRhdG9zIGRlICoqY29sYWJvcmFkb3JlcyoqIHRhbWJpw6luIHNlIG9wdG8gcG9yIHVuaXIgYXBlbGxpZG9zIGNvbiBlbCBub21icmUsIGFkZW3DoXMgZGUgZWxpbWluYXIgbGFzIGNvbHVtbmFzIGRlIFJGQywgbsO6bWVybyBkZSBzZWd1cm8gc29jaWFsLCBmYWN0b3IgZGUgY3LDqWRpdG8gaW5mb25hdml0LCBjcsOpZGl0byBpbmZvbmF2aXQsIENVUlAsIGx1Z2FyIGRlIG5hY2ltaWVudG8sIGNhbGxlLCBudW1lcm8gaW50ZXJubywgeSB0YXJqZXRhIGRlIGN1ZW50YS4KYGBge3J9CmNvbGFib3JhZG9yZXM8LW11dGF0ZShjb2xhYm9yYWRvcmVzLG5vbWJyZV9jb21wbGV0byA9IHBhc3RlKGNvbGFib3JhZG9yZXMkbm9tYnJlLCBjb2xhYm9yYWRvcmVzJGFwZWxsaWRvcywgc2VwPSIgIikpCmNvbGFib3JhZG9yZXM8LWRwbHlyOjpzZWxlY3QoY29sYWJvcmFkb3JlcywtYyhyZmMsIG5vX3NzLGZhY3Rvcl9jcmVkX2luZm9uYXZpdCxjcmVkaXRvX2luZm9uYXZpdCxjdXJwLHRhcmpldGFfY3VlbnRhLGFwZWxsaWRvcyxub21icmUsYmFqYSxjYWxsZSxudW1faW50ZXJubyxsdWdhcl9uYWMsIjR0b19tZXMiLHByaW1lcl9tZXMsbm9fZW1wbGVhZG8pKQpjb2xhYm9yYWRvcmVzPC1jb2xhYm9yYWRvcmVzWyxjKDEyLDEsMiwzLDQsNSw2LDcsOCw5LDEwLDExKV0KYGBgCgo8Y2VudGVyPiAKPHNwYW4gc3R5bGU9ImNvbG9yOiAjRkZBNTRGIj4gKipFbiBlc3RhIGJhc2UgZGUgZGF0b3MgKENvbGFib3JhZG9yZXMpIGhheSBvdHJvcyBlbGVtZW50b3MgYSBjb25zaWRlcmFyIHBvciBlc3RvIHJldmlzYXJlbW9zIGNhZGEgY29sdW1uYSB5IGxvcyByZWdpc3Ryb3MgcXVlIGxhIGNvbmZvcm1hbioqIDwvc3Bhbj4KPC9jZW50ZXI+IAoKKipOb21icmUgQ29tcGxldG8qKiAgIApBIG5vbWJyZSBjb21wbGV0byBubyBzZSBsZSBoYXJhbiBjYW1iaW9zIGVuIGxhIGJhc2UgZGUgZGF0b3MuCgoqKkZlY2hhIGRlIG5hY2ltaWVudG8geSBmZWNoYSBkZSBhbHRhKiogICAKUGFyYSBmZWNoYSBkZSBuYWNpbWllbnRvIHkgZmVjaGEgZGUgYWx0YSBub3MgYXNlZ3VyYW1vcyBxdWUgZWwgZm9ybWF0byBlc3RlIGVuIGZvcm1hdG8gZGUgZmVjaGEuCmBgYHtyfQpjb2xhYm9yYWRvcmVzJGZlY2hhX2FsdGEgPC0gYXMuRGF0ZShjb2xhYm9yYWRvcmVzJGZlY2hhX2FsdGEsIGZvcm1hdCA9ICIlZC8lbS8lWSIpCmNvbGFib3JhZG9yZXMkZmVjaGFfbmFjIDwtIGFzLkRhdGUoY29sYWJvcmFkb3JlcyRmZWNoYV9uYWMsIGZvcm1hdCA9ICIlZC8lbS8lWSIpCgpgYGAKCioqR8OpbmVybyoqICAgClBhcmEgZ8OpbmVybyBzZSBpZGVudGlmaWNhcm9uIDkgcmVnaXN0cm9zIHRvdGFsbWVudGUgdmFjw61vcywgcG9yIGxvIHF1ZSBlcyBuZWNlc2FyaW8gZWxpbWluYXIgZXN0YXMgZmlsYXMuCmBgYHtyfQp1bmlxdWUoY29sYWJvcmFkb3JlcyRnZW5lcm8pCmZpbHRlcihjb2xhYm9yYWRvcmVzLCBjb2xhYm9yYWRvcmVzJGdlbmVybz09IiIpCmBgYApgYGB7cn0KY29sYWJvcmFkb3JlcyA8LSBjb2xhYm9yYWRvcmVzWy0oMTE0OjEyMiksXQpgYGAKCioqUHVlc3RvKiogICAKUGFyYSBwdWVzdG8gaGF5IHF1ZSAqKmVzdGFuZGFyaXphciBsb3MgcmVnaXN0cm9zKiosIGVzcGVjw61maWNhbWVudGUgYXF1ZWxsb3MgcXVlIHNvbiBlbCBtaXNtbyBwdWVzdG8gcGVybyBlc3TDoW4gZXNjcml0b3MgZGlmZXJlbnRlLgpgYGB7cn0KCmNvbGFib3JhZG9yZXMkcHVlc3RvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRwdWVzdG8sY29sYWJvcmFkb3JlcyRwdWVzdG89PSJTdXBlcnZpc29yIGRlIE3Dg8KhcXVpbiIsIlNVUEVSVklTT1IoQSkiKQpjb2xhYm9yYWRvcmVzJHB1ZXN0byA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkcHVlc3RvLGNvbGFib3JhZG9yZXMkcHVlc3RvPT0iU3VwZXJ2aXNvciBkZSBwZWdhZG8iLCJTVVBFUlZJU09SKEEpIikKY29sYWJvcmFkb3JlcyRwdWVzdG8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJHB1ZXN0byxjb2xhYm9yYWRvcmVzJHB1ZXN0bz09IlNVUEVSVklTT1JBIiwiU1VQRVJWSVNPUihBKSIpCmNvbGFib3JhZG9yZXMkcHVlc3RvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRwdWVzdG8sY29sYWJvcmFkb3JlcyRwdWVzdG89PSJBWS4gR0VORVJBTCIsIkFZVURBTlRFIEdFTkVSQUwiKQpjb2xhYm9yYWRvcmVzJHB1ZXN0byA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkcHVlc3RvLGNvbGFib3JhZG9yZXMkcHVlc3RvPT0iQVlVREFOVEUgREUgTUFOVEVOSU1JRU5UTyIsIkFZVURBTlRFIEdFTkVSQUwiKQpjb2xhYm9yYWRvcmVzJHB1ZXN0byA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkcHVlc3RvLGNvbGFib3JhZG9yZXMkcHVlc3RvPT0iQ0hPRkVSIEdFU1RPUiIsIkNIT0ZFUiIpCmNvbGFib3JhZG9yZXMkcHVlc3RvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRwdWVzdG8sY29sYWJvcmFkb3JlcyRwdWVzdG89PSJJTlNQRUNUT1IgREUgQ0FMSURBRCIsIklOU1BFQ1RPUihBKSBERSBDQUxJREFEIikKY29sYWJvcmFkb3JlcyRwdWVzdG8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJHB1ZXN0byxjb2xhYm9yYWRvcmVzJHB1ZXN0bz09IklOU1BFQ1RPUkEgREUgQ0FMSURBRCIsIklOU1BFQ1RPUihBKSBERSBDQUxJREFEIikKY29sYWJvcmFkb3JlcyRwdWVzdG8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJHB1ZXN0byxjb2xhYm9yYWRvcmVzJHB1ZXN0bz09Ik1hbnRlbmltaWVudG8gRkYiLCJNQU5URU5JTUlFTlRPIikKY29sYWJvcmFkb3JlcyRwdWVzdG8gPC0gdG91cHBlcihjb2xhYm9yYWRvcmVzJHB1ZXN0bykKdW5pcXVlKGNvbGFib3JhZG9yZXMkcHVlc3RvKQpgYGAKCioqRGVwYXJ0YW1lbnRvKiogICAKUGFyYSBkZXBhcnRhbWVudG8gaGF5IHF1ZSAqKmVzdGFuZGFyaXphciBsb3MgcmVnaXN0cm9zKiogcXVlIHNvbiBlbCBtaXNtbyBwZXJvIGVzY3JpdG9zIGRpZmVyZW50ZXMuCmBgYHtyfQp1bmlxdWUoY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG8pCmNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG8sY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG89PSJQcm9kdWNjaW9uIENhcnTDg8KzbiBNREwiLCJQUk9EVUNDScOTTiBDQVJUT04gTURMIikKY29sYWJvcmFkb3JlcyRkZXBhcnRhbWVudG8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJGRlcGFydGFtZW50byxjb2xhYm9yYWRvcmVzJGRlcGFydGFtZW50bz09IlByb2R1Y2Npb24gQ2FydMODwrNuIE1DIiwiUFJPRFVDQ0lPTiBDQVJUT04gTUMiKQpjb2xhYm9yYWRvcmVzJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvLGNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvPT0iUHJvZHVjY2nDg8KzbiBSZXRvcm4iLCJQUk9EVUNDSU9OIFJFVE9STk8iKQpjb2xhYm9yYWRvcmVzJGRlcGFydGFtZW50byA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvLGNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvPT0iQ29zdHVyYSBUMiIsIkNPU1RVUkEiKQpjb2xhYm9yYWRvcmVzJGRlcGFydGFtZW50byA8LSB0b3VwcGVyKGNvbGFib3JhZG9yZXMkZGVwYXJ0YW1lbnRvKQpgYGAKCioqU2FsYXJpbyBkaWFyaW8gZGVsIElNU1MqKiAgIApQYXJhIHNhbGFyaW8gZGlhcmlvIGRlbCBJTVNTIGhheSBxdWUgYXNlZ3VyYXJub3MgcXVlIGVsIGZvcm1hdG8gZXN0w6kgY29ycmVjdG8uIEFzw60gbWlzbW8sIGhheSBxdWUgY29ycmVnaXIgbG9zICoqdmFsb3JlcyBhdMOtcGljb3MqKi4KYGBge3J9Cm9wdGlvbnMoc2NpcGVuID0gOTk5KQpjb2xhYm9yYWRvcmVzJHNhbF9kaWFyaW9faW1zcyA8LSBhcy5udW1lcmljKGNvbGFib3JhZG9yZXMkc2FsX2RpYXJpb19pbXNzKQp1bmlxdWUoY29sYWJvcmFkb3JlcyRzYWxfZGlhcmlvX2ltc3MpCnN0cihjb2xhYm9yYWRvcmVzJHNhbF9kaWFyaW9faW1zcykKYGBgCgpQYXJhIGNvcnJlZ2lyIGxvcyB2YWxvcmVzIGF0w61waWNvcyBwcmltZXJvIGhheSBxdWUgaWRlbnRpZmljYXJsb3MuIFBhcmEgcmVtcGxhemFyIHN1IHNhbGFyaW8sIG5vcyBiYXNhcmVtb3MgZW4gcHVlc3RvcyBzaW1pbGFyZXMgcGFyYSBhc2lnbmFybGVzIHVubyBzaW1pbGFyLgpgYGB7cn0KZmlsdGVyKGNvbGFib3JhZG9yZXMsIGNvbGFib3JhZG9yZXMkc2FsX2RpYXJpb19pbXNzID4gIDQ0MTM3NTApCmBgYAoKUG9kZW1vcyBvYnNlcnZhciBxdWUgZWwgcHVlc3RvIGRlICoqSmFpbWUgRXJuZXN0byoqIGVzICoqQXl1ZGFudGUgZ2VuZXJhbCoqIHkgcXVlIGVzdMOhIGVuIGVsIGRlcGFydGFtZW50byBkZSAqKlByb2R1Y2Npw7NuIFJldG9ybm8qKi4gQWwgb2JzZXJ2YXIgcmVnaXN0cm9zIGNvbiBlbCBtaXNtbyBwdWVzdG8geSBkZXBhcnRhbWVudG8sIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIHRyZXMgY29sYWJvcmFkb3JlcyB0aWVuZW4gdW4gc2FsYXJpbyBkZSAqKjE1MS42MSoqLCB5IGVsIHNhbGFyaW8gZXF1aXZvY2FkbyBlcyBkZSAqKjE1MTY3Mjg1NzEuMDAqKiwgcG9yIGxvIHF1ZSBwb2Ryw61hbW9zIGFzdW1pciBoYSBzaWRvIHVuIGVycm9yIGRlIGRlZG8geSBlbCBzYWxhcmlvIGNvcnJlY3RvIGVzICoqMTUxLjYxKiosIHBvciBsbyBxdWUgb3B0YW1vcyBwb3IgcmVtcGxhemFybG8gcG9yIGVzdGUgdmFsb3IuCgpgYGB7cn0KY29sYWJvcmFkb3JlcyRzYWxfZGlhcmlvX2ltc3NbY29sYWJvcmFkb3JlcyRub21icmVfY29tcGxldG8gPT0gIkpBSU1FIEVSTkVTVE8gQUdVSUxFUkEgUk9EUklHVUVaIl0gPC0gMTUxLjYxCmBgYAoKRWwgc2VndW5kbyBzYWxhcmlvIGVxdWl2b2NhZG8gZXMgZGUgKio0NDEzNzU3LjAwKiouIHkgZGUgKipZT0xBTkRBIExPUEVaIFJBTU9TKiouIEFob3JhIGhheSBxdWUgaWRlbnRpZmljYXIgcHVlc3RvcyBzaW1pbGFyZXMgZW4gZWwgbWlzbW8gZGVwYXJ0YW1lbnRvLgpgYGB7cn0KZmlsdGVyKGNvbGFib3JhZG9yZXMsIGRlcGFydGFtZW50byA9PSAiQ09TVFVSQSIpCmBgYAoKRW4gZXN0ZSBjYXNvLCBlcyBlbCDDum5pY28gcmVnaXN0cm8gY29uIGVzZSBwdWVzdG8gZW4gZWwgZGVwYXJ0YW1lbnRvLCBwb3IgbG8gdGFudG8gaWRlbnRpZmljYXJlbW9zIHB1ZXN0b3Mgc2ltaWxhcmVzIHBlcm8gZW4gZGl2ZXJzb3MgZGVwYXJ0YW1lbnRvcy4KYGBge3J9CmZpbHRlcihjb2xhYm9yYWRvcmVzLCBwdWVzdG8gPT0gIlNVUEVSVklTT1IoQSkiKQpgYGAKClNlIGlkZW50aWZpY28gc29sbyB1biByZWdpc3RybyBtw6FzIGNvbiBlbCBtaXNtbyBwdWVzdG8sIHBvciBsbyBxdWUgb3B0YXJlbW9zIHBvciBhc2lnbmFyIGVsIG1pc21vIHNhbGFyaW8gYWwgcmVnaXN0cm8gY29uIGVsIHNhbGFyaW8gZWxldmFkby4KYGBge3J9CmNvbGFib3JhZG9yZXMkc2FsX2RpYXJpb19pbXNzW2NvbGFib3JhZG9yZXMkbm9tYnJlX2NvbXBsZXRvID09ICJZT0xBTkRBIExPUEVaIFJBTU9TIl0gPC0gMzM3LjA1CQp1bmlxdWUoY29sYWJvcmFkb3JlcyRzYWxfZGlhcmlvX2ltc3MpCmBgYAoKKipNdW5pY2lwaW8qKiAgIApFbiBtdW5pY2lwaW8gaGF5IHF1ZSAqKmVzdGFuZGFyaXphciBsb3MgcmVnaXN0cm9zKiosICBwb3IgZWplbXBsbywgY2FtYmlhciAiU0FOIE5JQ09MQVMgREUgTE9TIEciIHBvciAiU0FOIE5JQ09MQVMgREUgTE9TIEdBUlpBIi4KYGBge3J9CnVuaXF1ZShjb2xhYm9yYWRvcmVzJG11bmljaXBpbykKY29sYWJvcmFkb3JlcyRtdW5pY2lwaW8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJG11bmljaXBpbyxjb2xhYm9yYWRvcmVzJG11bmljaXBpbz09IlNBTiBOSUNPTEFTIERFIExPUyBHIiwiU0FOIE5JQ09MQVMgREUgTE9TIEdBUlpBIikKY29sYWJvcmFkb3JlcyRtdW5pY2lwaW8gPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJG11bmljaXBpbyxjb2xhYm9yYWRvcmVzJG11bmljaXBpbz09IkNBw4PigJhBREEgQkxBTkNBIiwiR1VBREFMVVBFIikKYGBgCgoqKkVzdGFkbyoqICAgCkVuIGVzdGFkbyBoYXkgcXVlICoqZXN0YW5kYXJpemFyIGxvcyByZWdpc3Ryb3MqKiwgIHBvciBlamVtcGxvLCBjYW1iaWFyICJOdWV2byBMZcODwrNuIiBwb3IgIk5VRVZPIExFT04iLgpgYGB7cn0KCmNvbGFib3JhZG9yZXMkZXN0YWRvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRlc3RhZG8sY29sYWJvcmFkb3JlcyRlc3RhZG89PSJOdWV2byBMZcODwrNuIiwiTlVFVk8gTEVPTiIpCmNvbGFib3JhZG9yZXMkZXN0YWRvIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRlc3RhZG8sY29sYWJvcmFkb3JlcyRlc3RhZG89PSJHVUFEQUxVUEUgTi5MLiIsIk5VRVZPIExFT04iKQpjb2xhYm9yYWRvcmVzJGVzdGFkbyA8LSB0b3VwcGVyKGNvbGFib3JhZG9yZXMkZXN0YWRvKQoKdW5pcXVlKGNvbGFib3JhZG9yZXMkZXN0YWRvKQpgYGAKCioqRXN0YWRvIENpdmlsKiogICAKUGFyYSBlc3RhZG8gY2l2aWwgdGFtYmnDqW4gaGF5IHF1ZSAqKmVzdGFuZGFyaXphciBsb3MgcmVnaXN0cm9zKiouCmBgYHtyfQp1bmlxdWUoY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwpCmNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsIDwtIHRvdXBwZXIoY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwpCmNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwsY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWw9PSJVTknDg8KzTiBMSUJSRSIsIlVOSU9OIExJQlJFIikKY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCxjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbD09IlNPTFRFUk8iLCJTT0xURVJPKEEpIikKY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwgPC0gcmVwbGFjZShjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCxjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbD09IkNBU0FETyIsIkNBU0FETyhBKSIpCmNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsIDwtIHJlcGxhY2UoY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWwsY29sYWJvcmFkb3JlcyRlc3RhZG9fY2l2aWw9PSJDQVNBREEiLCJDQVNBRE8oQSkiKQpjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsLGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsPT0iU09MVEVSQSIsIlNPTFRFUk8oQSkiKQpjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsLGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsPT0iRElWT1JDSUFEQSIsIkRJVk9SQ0lBRE8oQSkiKQpjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbCA8LSByZXBsYWNlKGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsLGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsPT0iRElWT1JDSUFETyIsIkRJVk9SQ0lBRE8oQSkiKQpgYGAKCgojIyBWYXJpYWJsZXMgeSBSZWdpc3Ryb3MgcG9yIGJhc2UgZGUgZGF0b3MKCkxhIGJhc2UgZGUgZGF0b3MgKipNZXJtYSoqIHRpZW5lIDMgdmFyaWFibGVzIHkgNTAgcmVnaXN0cm9zLgpgYGB7cn0KZGVzY3JpYmUobWVybWEpCmRpbShtZXJtYSkKYGBgCgpMYSBiYXNlIGRlIGRhdG9zICoqU2NyYXAqKiB0aWVuZSA1IHZhcmlhYmxlcyB5IDI1MCByZWdpc3Ryb3MuCmBgYHtyfQpkZXNjcmliZShzY3JhcCkKZGltKHNjcmFwKQpgYGAKCkxhIGJhc2UgZGUgZGF0b3MgKipQcm9kdWNjacOzbiBkZSBDYXJ0b24qKiB0aWVuZSAxMSB2YXJpYWJsZXMgeSAxNDg2IHJlZ2lzdHJvcy4KYGBge3J9CmRlc2NyaWJlKGNhcnRvbikKZGltKGNhcnRvbikKYGBgCgpMYSBiYXNlIGRlIGRhdG9zICoqRGVsaXZlcnkgUGVyZm9ybWFuY2UqKiB0aWVuZSA3IHZhcmlhYmxlcyB5IDgwMCByZWdpc3Ryb3MuCmBgYHtyfQpkZXNjcmliZShwZXJmKQpkaW0ocGVyZikKYGBgCgpMYSBiYXNlIGRlIGRhdG9zICoqRGVsaXZlcnkgUGxhbioqIHRpZW5lIDUgdmFyaWFibGVzIHkgOTcyIHJlZ2lzdHJvcy4KYGBge3J9CmRlc2NyaWJlKHBsYW4pCmRpbShwbGFuKQpgYGAKCkxhIGJhc2UgZGUgZGF0b3MgKipCYWphcyoqIHRpZW5lIDE1IHZhcmlhYmxlcyB5IDIzOCByZWdpc3Ryb3MuCmBgYHtyfQpkZXNjcmliZShiYWphcykKZGltKGJhamFzKQpgYGAKCkxhIGJhc2UgZGUgZGF0b3MgKipDb2xhYm9yYWRvcmVzKiogdGllbmUgMTIgdmFyaWFibGVzIHkgMTEzIHJlZ2lzdHJvcy4KYGBge3J9CmRlc2NyaWJlKGNvbGFib3JhZG9yZXMpCmRpbShjb2xhYm9yYWRvcmVzKQpgYGAKCgojIyBDbGFzaWZpY2FjacOzbiBkZSBWYXJpYWJsZXMgeSBFc2NhbGEgZGUgTWVkaWNpb24KCioqTWVybWEqKgpgYGB7cn0KVmFyaWFibGU8LWMoIkZlY2hhIiwiTWVzIiwiS2lsb3MiKQoKVGlwbzwtYygiUXVhbGl0YXRpdmUgKG5vbWluYWwpIiwgIlF1YWxpdGF0aXZlIChub21pbmFsKSIsICJRdWFudGl0YXRpdmUgKGRpc2NyZXRlKSIpCgpFc2NhbGEgPC0gYygiRmVjaGEgKHkvbS9kKSIsIk4vQSIsICJLaWxvZ3JhbW9zIikKCnRhYmxlPC1kYXRhLmZyYW1lKFZhcmlhYmxlLFRpcG8sRXNjYWxhKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKKipTY3JhcCoqCmBgYHtyfQpWYXJpYWJsZTI8LWMoIlJlZmVyZW5jaWEiLCJGZWNoYSIsIlByb2R1Y3RvIiwiQ2FudGlkYWQiLCAiVWJpY2FjacOzbiBkZSBvcmlnZW4iKQoKVGlwbzI8LWMoIlF1YWxpdGF0aXZlIChub21pbmFsKSIsICJRdWFsaXRhdGl2ZSAobm9taW5hbCkiLCAiUXVhbGl0YXRpdmUgKG5vbWluYWwpIiwiUXVhbnRpdGF0aXZlIChkaXNjcmV0ZSkiLCAiUXVhbGl0YXRpdmUgKG5vbWluYWwpIikKCkVzY2FsYTIgPC0gYygiTi9BIiwiRmVjaGEgKHkvbS9kKSIsICJOL0EiLCJVbmlkYWRlcyIsIk4vQSIpCgp0YWJsZTI8LWRhdGEuZnJhbWUoVmFyaWFibGUyLFRpcG8yLEVzY2FsYTIpCmtuaXRyOjprYWJsZSh0YWJsZTIpCmBgYAoKKipQcm9kdWNjacOzbiBkZSBDYXJ0b24qKgpgYGB7cn0KVmFyaWFibGUzPC1jKCJGZWNoYSIsIkNsaWVudGUiLCJQaWV6YXMgcHJvZ3JhbWFkYXMiLCAiVGllbXBvIG1pbnV0b3MiLCAiSG9yYSBmaW4iLCAiTGFtaW5hcyBwcm9jZXNhZGFzIiwgIkluaWNpbyBzZXAgdXAiLCAiRmluIGluaWNpbyBzZXAgdXAiLCAiSW5pY2lvIGRlIHByb2Nlc28iLCAiRmluIGRlIHByb2Nlc28iLCAiVGllbXBvIGNhbGlkYWQiKQoKVGlwbzM8LWMoIlF1YWxpdGF0aXZlIChub21pbmFsKSIsIlF1YWxpdGF0aXZlIChub21pbmFsKSIsIlF1YW50aXRhdGl2ZSAoZGlzY3JldGUpIiwgIlF1YW50aXRhdGl2ZSAoZGlzY3JldGUpIiwgICJRdWFudGl0YXRpdmUgKGRpc2NyZXRlKSIsICJRdWFudGl0YXRpdmUgKGRpc2NyZXRlKSIsICJRdWFudGl0YXRpdmUgKGRpc2NyZXRlKSIsICJRdWFudGl0YXRpdmUgKGRpc2NyZXRlKSIsICJRdWFudGl0YXRpdmUgKGRpc2NyZXRlKSIsICJRdWFudGl0YXRpdmUgKGRpc2NyZXRlKSIsIlF1YW50aXRhdGl2ZSAoZGlzY3JldGUpIiApCgpFc2NhbGEzIDwtIGMoIkZlY2hhICh5L20vZCkiLCJOL0EiLCJQaWV6YXMgKHVuaWRhZGVzKSAiLCJUaWVtcG8gKE0pIiwiVGllbXBvIChIOk0pIiwiTGFtaW5hcyAodW5pZGFkZXMpIiwiVGllbXBvIChIOk0pIiwiVGllbXBvIChIOk0pIiwiVGllbXBvIChIOk0pIiwiVGllbXBvIChIOk0pIiwiVGllbXBvIChNKSIpCgp0YWJsZTM8LWRhdGEuZnJhbWUoVmFyaWFibGUzLFRpcG8zLEVzY2FsYTMpCmtuaXRyOjprYWJsZSh0YWJsZTMpCmBgYAoKKipEZWxpdmVyeSBQZXJmb3JtYW5jZSoqCmBgYHtyfQpWYXJpYWJsZTQ8LWMoIkNsaWVudGUiLCJWdWVsdGEiLCJGZWNoYSIsICJQbGFuIEFycml2YWwiLCAiUmVhbCBBcnJpdmFsIiwgIlJlYWwgRGVwYXJ0dXJlIiwgIkRpZmZlcmVuY2UiKQoKVGlwbzQ8LWMoIlF1YWxpdGF0aXZlIChub21pbmFsKSIsIlF1YWxpdGF0aXZlIChub21pbmFsKSIsIlF1YW50aXRhdGl2ZSAoZGlzY3JldGUpIiwgIlF1YW50aXRhdGl2ZSAoZGlzY3JldGUpIiwgICJRdWFudGl0YXRpdmUgKGNvbnRpbnVvdXMpIiwgIlF1YW50aXRhdGl2ZSAoY29udGludW91cykiLCAiUXVhbnRpdGF0aXZlIChjb250aW51b3VzKSIpCgpFc2NhbGE0IDwtIGMoIk4vQSIsIlZ1ZWx0YXMiLCJGZWNoYSAoeS9tL2QpIiwiVGllbXBvIChIKSIsIlRpZW1wbyAoSDpNKSIsIlRpZW1wbyAoSDpNKSIsIlRpZW1wbyAoSDpNKSIpCgp0YWJsZTQ8LWRhdGEuZnJhbWUoVmFyaWFibGU0LFRpcG80LEVzY2FsYTQpCmtuaXRyOjprYWJsZSh0YWJsZTQpCmBgYAoKKipEZWxpdmVyeSBQbGFuKioKYGBge3J9ClZhcmlhYmxlNTwtYygiQ2xpZW50ZSIsIlByb3llY3RvIiwgIkl0ZW0iLCAiTWVzIiwiVW5pZGFkZXMiKQoKVGlwbzU8LWMoIkN1YWxpdGF0aXZlIChub21pbmFsKSIsICJDdWFsaXRhdGl2ZSAobm9taW5hbCkiLCAiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwgIlF1YW50aWF0aXZlIChkaXNjcmV0ZSkiLCAiUXVhbnRpYXRpdmUgKGRpc2NyZXRlKSIpCgpFc2NhbGE1IDwtIGMoIk4vQSIsIk4vQSIsIk4vQSIsIlRpZW1wbyAoWS9NKSIsIlVuaWRhZGVzIikKCnRhYmxlNTwtZGF0YS5mcmFtZShWYXJpYWJsZTUsVGlwbzUsRXNjYWxhNSkKa25pdHI6OmthYmxlKHRhYmxlKQpgYGAKCioqQmFqYXMqKgpgYGB7cn0KVmFyaWFibGU2PC1jKCJOb21icmUgQ29tcGxldG8iLCJGZWNoYSBOYWNpbWllbnRvIiwgIkdlbmVybyIsICJGZWNoYSBBbHRhIiwiTW90aXZvIFJlbnVuY2lhIiwiUGVybWFuZW5jaWEiLCJGZWNoYSBkZSBCYWphIiwiUHVlc3RvIiwiRGVwYXJ0YW1lbnRvIiwiU2FsYXJpbyBEaWFyaW8gSU1TUyIsIkNvbG9uaWEiLCJDUCIsIk11bmljaXBpbyIsIkVzdGFkbyIsIkVzdGFkbyBDaXZpbCIpCgpUaXBvNjwtYygiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwiUXVhbnRpYXRpdmUgKGRpc2NyZXRlKSIsICJDdWFsaXRhdGl2ZSAobm9taW5hbCkiLCAiUXVhbnRpYXRpdmUgKGRpc2NyZXRlKSIsIkN1YWxpdGF0aXZlIChub21pbmFsKSIsIlF1YW50aWF0aXZlIChkaXNjcmV0ZSkiLCJRdWFudGlhdGl2ZSAoZGlzY3JldGUpIiwiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwiUXVhbnRpYXRpdmUgKGNvbnRpbnVvdXMpIiwiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIikKCkVzY2FsYTY8LWMoIk4vQSIsIkZlY2hhIChZL00vRCkiLCAiTi9BIiwgIkZlY2hhIChZL00vRCkiLCJOL0EiLCJEaWFzIiwiRmVjaGEgKFkvTS9EKSIsIk4vQSIsIk4vQSIsIlBlc29zIE1leGljYW5vcyIsIk4vQSIsIk4vQSIsIk4vQSIsIk4vQSIsIk4vQSIpCgp0YWJsZTY8LWRhdGEuZnJhbWUoVmFyaWFibGU2LFRpcG82LEVzY2FsYTYpCmtuaXRyOjprYWJsZSh0YWJsZSkKYGBgCgoqKkNvbGFib3JhZG9yZXMqKgpgYGB7cn0KVmFyaWFibGU3PC1jKCJOb21icmUgQ29tcGxldG8iLCJGZWNoYSBOYWNpbWllbnRvIiwgIkdlbmVybyIsICJGZWNoYSBBbHRhIiwiUHVlc3RvIiwiRGVwYXJ0YW1lbnRvIiwiU2FsYXJpbyBEaWFyaW8gSU1TUyIsIkNvbG9uaWEiLCJNdW5pY2lwaW8iLCJFc3RhZG8iLCJDUCIsIkVzdGFkbyBDaXZpbCIpCgpUaXBvNzwtYygiQ3VhbGl0YXRpdmUgKG5vbWluYWwpIiwiUXVhbnRpYXRpdmUgKGRpc2NyZXRlKSIsICJDdWFsaXRhdGl2ZSAobm9taW5hbCkiLCAiUXVhbnRpYXRpdmUgKGRpc2NyZXRlKSIsIkN1YWxpdGF0aXZlIChub21pbmFsKSIsIkN1YWxpdGF0aXZlIChub21pbmFsKSIsIlF1YW50aWF0aXZlIChjb250aW51b3VzKSIsIkN1YWxpdGF0aXZlIChub21pbmFsKSIsIkN1YWxpdGF0aXZlIChub21pbmFsKSIsIkN1YWxpdGF0aXZlIChub21pbmFsKSIsIkN1YWxpdGF0aXZlIChub21pbmFsKSIsIkN1YWxpdGF0aXZlIChub21pbmFsKSIpCgpFc2NhbGE3PC1jKCJOL0EiLCJGZWNoYSAoWS9NL0QpIiwgIk4vQSIsICJGZWNoYSAoWS9NL0QpIiwiTi9BIiwiTi9BIiwiUGVzb3MgTWV4aWNhbm9zIiwiTi9BIiwiTi9BIiwiTi9BIiwiQ1AiLCJOL0EiKQoKdGFibGU3PC1kYXRhLmZyYW1lKFZhcmlhYmxlNyxUaXBvNyxFc2NhbGE3KQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKCiMjIEFuw6FsaXNpcyBFc3RhZMOtc3RpY28gRGVzY3JpcHRpdm8KCmBgYHtyfQptZXMgPC0gbWVybWEgJT4lIGdyb3VwX2J5KG1lcykgJT4lIHRhbGx5KCkKbWVzCmBgYApBZ29zdG8gZXMgZWwgbWVzIGVuIGRvbmRlIGxhIGVtcHJlc2EgcHJvZHVjZSBtYXlvciBtZXJtYSwgY29uIGVzdG8gcG9kZW1vcyBpbmZlcmlyIHF1ZSBhZ29zdG8gZXMgZWwgbWVzIGNvbiBtYXlvciBwcm9kdWNjacOzbiBlbiBsYSBlbXByZXNhLiBEZSBsYSBtaXNtYSBtYW5lcmEsIHBvZGVtb3MgaW5mZXJpciBxdWUgZW4gbG9zIG1lc2VzIGRlIGVuZXJvLCBqdW5pbyB5IHNlcHRpZW1icmUgZXMgZG9uZGUgbGEgZW1wcmVzYSB0aWVuZSBtZW5vciBkZW1hbmRhLCB5YSBxdWUgc29uIGxvcyBtZXNlcyBlbiBkb25kZSBsYSBlbXByZXNhIHByb2R1Y2UgbWVub3MgbWVybWEuCgpgYGB7cn0KZ2dwbG90KG1lcm1hLCBhZXMoeCA9IG1lcywgeSA9IGtpbG9zKSkgKwogIGdlb21fYm94cGxvdChjb2xvcj0icmVkIixmaWxsPSJvcmFuZ2UiLGFscGhhPTAuMikKYGBgCgoKYGBge3J9CnViaWNhY2lvbl9vcmlnZW4gPC0gc2NyYXAgJT4lIGdyb3VwX2J5KHViaV9vcmlnZW4pICU+JSB0YWxseSgpCnViaWNhY2lvbl9vcmlnZW4KYGBgCkVzdGEgdGFibGEgbm9zIG11ZXN0cmEgbG9zIGRlc2VjaG9zIHkvbyByZXNpZHVvcyBkZXJpdmFkb3MgZGVsIHByb2Nlc28gZW4gbGFzIGRpZmVyZW50ZXMgYXJlYXMgZGUgdHJhYmFqby4gQ29tbyBwb2RlbW9zIG9ic2VydmFyIGVuIHByZS1wcm9kdWNjacOzbiBlcyBlbCBsdWdhciBkb25kZSBtYXMgc2UgZ2VuZXJhbiBlc3RvcyByZXNpZHVvcy4KCmBgYHtyfQpnZ3Bsb3Qoc2NyYXAsIGFlcyh4ID0gdWJpX29yaWdlbiwgeSA9IGNhbnRpZGFkKSkgKwogIGdlb21fYm94cGxvdChjb2xvcj0icmVkIixmaWxsPSJvcmFuZ2UiLGFscGhhPTAuMikKYGBgCgpgYGB7cn0KY2xpZW50ZSA8LSBjYXJ0b24gJT4lIGdyb3VwX2J5KGNsaWVudGUpICU+JSB0YWxseSgpCmNsaWVudGUKYGBgCkxvcyBjbGllbnRlcyBjb24gbWF5b3IgZGVtYW5kYSBzb246ICAKLSBTdGFiaWx1cyAxICAKLSBTdGFiaWx1cyAzICAKLSBUUk1YICAKLSBWQVJST0MgIAotIERFTlNPICAKCmBgYHtyfQpnZ3Bsb3QoY2FydG9uLCBhZXMoeCA9IGNsaWVudGUsIHkgPSBwaWV6YXNfcHJvZykpICsKICBnZW9tX2JveHBsb3QoY29sb3I9InJlZCIsZmlsbD0ib3JhbmdlIixhbHBoYT0wLjIpK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkKYGBgCgojIyMgUHJvcHVlc3RhCgpIYWNlciBxdWUgbGEgZW1wcmVzYSBGT1JNIHV0aWxpY2UgbGEgbWV0b2RvbG9naWEgZGUgKipCdXNpbmVzcyBBbmFseXRpY3MqKiwgYXl1ZGFyYSBhIGxhIGVtcHJlc2EgYSBhbmFsaXphciBsYSBzaXR1YWNpw7NuIGFjdHVhbCBlbiBsYSBxdWUgc2UgZW5jdW50cmFuIHkgcG9yIGVuZGUgcHJlZGVjaXIgZXZlbnRvcywgdGVuZGVuY2lhcyB5IGNvbXBvcnRhbWllbnRvcyBhIGZ1dHVybyBxdWUgYXl1ZGVuIGEgZGV0ZXJtaW5hciBsYSB0b21hIGRlIGRlY2lzaW9uZXMsIHJlZHVjaWVuZG8gbG9zIHJpZXNnb3MgZGUgZXF1aXZvY2FjaW9uZXMgZnV0dXJhcy4gIAogIApMYSBzZWd1bmRhIHByb3B1ZXN0YSB2YSBlbmNhbWluYWRhIGEgaW5kaWNhZG9yZXMgZGUgY2FsaWRhZCwgbG9zIGN1YWwgcGVybWl0ZSBhIGxhIGVtcHJzYSB2aXN1YWxpemFyIHkgbWVkaXIgbG9zIGNhbWJpb3MgeSBwcm9ncmVzb3MgcXVlIGVzdMOhIGhhY2llbmRvIGhhY2lhIGVsIGxvZ3JvIGRlIHVuIHJlc3VsdGFkbyBlc3BlY8OtZmljby4gRW4gZXN0ZSBjYXNvIGJ1c2NhbW9zIHJlZHVjaXIgbGEgbWVybWEgcG9yIGxvIHF1ZSBhbGd1bm9zIGRlIGxvcyBpbmRpY2Fkb3JlcyBxdWUgcHVlZGVuIHV0aWxpemFyIHNvbjogIAogIAotICoqRGVmZWN0b3MgcG9yIHVuaWRhZCoqICAKRXN0ZSBpbmRpY2Fkb3Igbm9zIGRhIGVsIG51zIFtZXJvIGRlIGRlZmVjdG9zIHF1ZSBoYXkgZW4gdW5hIG11ZXN0cmEgc29icmUgZWwgbnXMgW1lcm8gZGUgdW5pZGFkZXMgcXVlIHNlIHRvbWFyb24gZW4gbGEgbXVlc3RyYS4gIAoqRFBVID0gIyBudW1lcm8gZGUgZGVmZWN0b3MgLyB0YW1hw7FvIGRlIG11ZXN0cmEqCgotICoqRGVmZWN0b3MgcG9yIG9wb3J0dW5pZGFkKiogIApFcyBlbCBudcyBbWVybyBkZSBkZWZlY3RvcyBxdWUgaGF5IGVuIHVuYSBtdWVzdHJhIHNvYnJlIGxhIGNhbnRpZGFkIGRlIG9wb3J0dW5pZGFkZXMsIHRvbWHMgW5kb3NlIGxhcyBvcG9ydHVuaWRhZGVzIGNvbW8gbG9zIHBvc2libGVzIGRlZmVjdG9zIHF1ZSBwdWVkZSB0ZW5lciBjYWRhIHVuaWRhZC4gICAKKkRQTyA9ICMgbnVtZXJvIGRlIGRlZmVjdG9zIC8gIyBkZSBvcG9ydHVuaWRhZGVzKiAgCiAgCi0gKipEZWZlY3RvIHBvciBtaWxsb24gZGUgb3BvdHVuaWRhZGVzKiogIApFbiBlc3RlIHNlIHRvbWEgZWwgbnXMgW1lcm8gZGUgZGVmZWN0b3MgZW4gdW5hIG11ZXN0cmEgZGl2aWRpZG8gZW50cmUgZWwgbnXMgW1lcm8gdG90YWwgZGUgb3BvcnR1bmlkYWRlcyB5IG11bHRpcGxpY2FkbyBwb3IgMSwwMDAsMDAwLCBlc3RvIGF5dWRhIGEgZXN0YW5kYXJpemFyIGVsIG5pdmVsIGRlIG9wb3J0dW5pZGFkIHkgYWRlbWHMgXMgcGVybWl0ZSBjb21wYXJhciBjb24gb3Ryb3MgcHJvY2Vzc28uICAKKkRQTyA9ICgjIG51bWVybyBkZSBkZWZlY3RvcyAvICMgZGUgb3BvcnR1bmlkYWRlcykgeCAxLDAwMCwwMDAqICAKICAKRXN0byBub3MgYXl1ZGFyYSAgYSBldmFsYXIgbGEgcmVudGFiaWxpZGFkIGRlIGxvcyBwcm9jZXNvcyBxdWUgc2UgbGxldmFuIGEgY2Fiby4gIAoKCiMjIEJhc2UgZGUgRGF0b3MgRXh0ZXJuYQoKKipJbXBvcnRhciBCYXNlIGRlIERhdG9zKioKYGBge3J9CnJlZyA8LSByZWFkLmNzdigiL1VzZXJzL2FuaXRhMy9Eb3dubG9hZHMvcmVnaXN0cmF0aW9ucy5jc3YiKQpjYXJfcHJvZCA8LSByZWFkLmNzdigiL1VzZXJzL2FuaXRhMy9Eb3dubG9hZHMvY2FyX3Byb2R1Y3Rpb24uY3N2IikKYGBgCgojIyMgR3JhZmljb3MgQ3VhbGl0YXRpdm9zIHkgQ3VhbnRpdGF0aXZvcwoKYGBge3J9CmdncGxvdChkYXRhPWNhcl9wcm9kLCBhZXMoeD15ZWFyLCB5PXVzX2Nhcl9wcm9kdWN0aW9uKSwgY29sb3I9J29yYW5nZScpICsgCmdlb21fbGluZSgpICsgCiAgICBsYWJzKHg9IkHDsW8iLCB5PSJQcm9kdWNjaW9uIikKYGBgCkVuIGVzdGEgZ3JhZmljYSBwb2RlbW9zIG9ic2VydmFyIHF1ZSBsYSBwcm9kdWNjacOzbiBkZSBsb3MgY2Fycm9zIGVuIEVVQSBkaXNtaW51eWUsIGVzdG8gcHVlZGUgc2VyIGEgY2FzdXNhIGRlIGxhIGluc3VmaWNpZW5jaWEgZGUgc2VtaWNvbmR1Y3RvcmVzIGVuIGVsIG1lcmNhZG8sIGxhIGN1YWwgZXMgdW5hIHBpZXphIGVzY2VuY2lhbCBwYXJhIHN1IHByb2R1Y2Npw7NuLiBBZGVtYXMgZGUgbGEgZXNjYXNleiBkZSBjaGlwcyBxdWUgdGllbmVuIGxhcyBlbXByZXNhcyB0ZWNub2zDs2dpY2FzIHF1ZSBmYWJyaWNhbiBkZXNkZSBlbGVjdHJvZG9tw6lzdGljb3MsIGNvbXB1dGFkb3JlcyB5IGNlbHVsYXJlcyBoYXN0YSBjb25zb2xhcyBkZSB2aWRlb2p1ZWdvcy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1jYXJfcHJvZCwgYWVzKHg9eWVhciwgeT1yZXRhaWxfcHJpY2VfZ2Fzb2xpbmUpLCBjb2xvcj0ncmVkJykgKyAKZ2VvbV9saW5lKCkgKyAKICBsYWJzKHg9IkHDsW8iLCB5PSJQcmVjaW8gZGUgR2Fzb2xpbmEiKQpgYGAKVW5hIGRlIGxhcyBjYXVzYXMgZGVsIGluY3JlbWVudG8gZGUgZ2Fzb2xpbmEgc2UgZGViZSBhIGxhIGZ1ZXJ6YSBxdWUgaGEgdG9tYWRvIGVsIGTDs2xhciBmcmVudGUgYSBvdHJhcyBtb25lZGFzLCBwb3IgbG8gcXVlIHByb2R1Y3RvcmVzIHByZWZpZXJlbiByZWNpYmlyIG3DoXMgZGl2aXNhcyBlbiBvdHJvcyBwYcOtc2VzLgoKYGBge3J9CmdncGxvdChkYXRhPWNhcl9wcm9kLCBhZXMoeD15ZWFyLCB5PXBvYmxhY2lvbiksIGNvbG9yPSdyZWQnKSArIApnZW9tX2xpbmUoKSArIAogIGxhYnMoeD0iQcOxbyIsIHk9IlBvYmxhY2nDs24gRGVzb2N1cGFkYSIpCmBgYApFc3RhIGdyYWZpY2EgY29ycmVzcG9uZGUgYSBsYSBwb2JsYWNpw7NuIGRlc29jdXBhZGEsIGVzIGRlY2lyLCBsYXMgcGVyc29uYXMgY2VzYW50ZXMgcXVlIHNvbiBkZXNvY3VwYWRvcyBxdWUgaGFuIHRlbmlkbyB1biB0cmFiYWpvIHByZXZpYW1lbnRlIGNvbW8gbGFzIHF1ZSBidXNjYW4gdHJhYmFqbyBwb3IgcHJpbWVyYSB2ZXosIGNvbiByZXNwZWN0byBhIGxhIHBvYmxhY2nDs24gZWNvbsOzbWljYW1lbnRlIGFjdGl2YS4gCiAgCiMjIyBUYWJsYSBkZSBmcmVjdWVuY2lhCk5vIHNlIHB1ZWRlIHJlYWxpemFyIGxhIHRhYmxhIGRlIGZyZWNpZW5jaWEgZGViaWRvIGEgcXVlIGxhIG5hdHVyYWxlemEgZGUgbG9zIGRhdG9zIG5vIG5vcyBsbyBwZXJtaXRlLiAoTm8gc2UgY3VlbnRhbiBjb24gZGF0b3MgcmVwZXRpdGl2b3MpCgojIyMgR3JhZmljb3MgZGUgZGlzcGVyc2nDs24KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1jYXJfcHJvZCwgYWVzKHg9eWVhciwgeT1wb2JsYWNpb24pLCBjb2xvcj0nb3JhbmdlJykgKyAKZ2VvbV9wb2ludCgpICsgCiAgbGFicyh4PSJBw7FvIiwgeT0iUG9ibGFjaW9uIikKYGBgCgoKYGBge3J9CmdncGxvdChkYXRhPWNhcl9wcm9kLCBhZXMoeD15ZWFyLCB5PXJldGFpbF9wcmljZV9nYXNvbGluZSksIGNvbG9yPSdvcmFuZ2UnKSArIApnZW9tX3BvaW50KCkgKyAKICBsYWJzKHg9IkHDsW8iLCB5PSJQcmVjaW8gZGUgR2Fzb2xpbmEiKQpgYGAKCiMjIEV4cG9ydGFyIEJhc2VzIGRlIERhdG9zIExpbXBpYXMKCmBgYHtyfQp3cml0ZS5jc3YoYmFqYXMsZmlsZSA9ICJiYWphc19saW1waWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihjb2xhYm9yYWRvcmVzLGZpbGUgPSAiY29sYWJvcmFkb3Jlc19saW1waWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihwZXJmLCBmaWxlPSAiZGVsX3BlcmZfbGltcGlhLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YocGxhbixmaWxlID0gInBsYW5fbGltcGlhLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YobWVybWEsZmlsZSA9ICJtZXJtYV9saW1waWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihzY3JhcCxmaWxlID0gInNjcmFwX2xpbXBpYS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KGNhcnRvbixmaWxlID0gImNhcnRvbl9saW1waWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAo=