Introducción

La empresa Form, fundada en el 2011, es una empresa familiar mexicana enfocada en la manufactura de empaques para la industria automotriz. Esta PyMe, liderada por Felipe Flores García, ha logrado crecer durante estos 11 años, y emplear a 130 personas. Form se dedica a desarrollar soluciones de empaque que generen valor agregado en la cadena de suministro de sus clientes, considerando la protección y las necesidades productivas, operativas y de almacenaje. También, consideran de suma importancia tener al ser humano como el centro de todos sus procesos. Debido a que la industria automotriz trabaja y crece constantemente, las empresas no se pueden permitir dejar pasar cualquier oportunidad para demostrar su eficiencia al ser productivos y rentables. Form ofrece esta oportunidad, la oportunidad de generar ahorros, ser más eficientes, productivos y rentables mediante el uso adecuado y efectivo del empaque, desde su inventario y su espacio mínimo en almacén, hasta su gran calidad de diseño y manipulación.

Con poco más de 100 años, la industria automotríz se ha posicionado como una de las ramas de mayor importancia tanto para el cliente final como para aquellos involucrados en que se mantenga a la vaguardia. Comparado con un dispositivo como la computadora o el teléfono celular, un auto puede parecer primitivo pero el ingenio que hay detras de cada parte y cada movimiento es interminable. Sobre todo en las últimas décadas, México se ha posicionado como clave para la manufactura como para la innovación por lo que esta rama de la economía no puede pasar desapercibida. Con ayuda de las herramientas para almacenar datos, se ha vuelto clave para el crecimiento poder interpretar estos datos de la manera apropiada que es el enfoque al que se espera llegar.

Índice

Sección 1

  • Limpieza de bases de datos

  • Comprensión de las bases de datos

  • Análisis estadístico descriptivo

  • Propuestas

  • Bases de datos externas

Sección 2

  • Análisis exploratorio de las bases de datos

  • Predicción del desempeño de la industria automotriz

Sección 3

  • K-Means Clustering

Sección 4

  • Identificación de Resultados Relevantes y Sugerencias

  • Definiciones

  • Key Performance Indicators (KPIs)

  • Referencias

Limpieza de bases de datos

FORM- Recursos Humanos (Colaboradores y Bajas).

Empezamos con “colaboradores”.

Se importa la base de datos y se entienden las variables.

colab<-read.csv("/Users/elenavela/Downloads/colabbb.csv")
summary(colab)
##  No..De.Empleado   APELLIDOS            NOMBRE          AÑO.DE.NACIMIENTO
##  Min.   :  1.00   Length:111         Length:111         Min.   :1955     
##  1st Qu.: 25.25   Class :character   Class :character   1st Qu.:1978     
##  Median : 49.50   Mode  :character   Mode  :character   Median :1989     
##  Mean   : 57.79                                         Mean   :1987     
##  3rd Qu.: 84.00                                         3rd Qu.:1997     
##  Max.   :148.00                                         Max.   :2022     
##  NA's   :21                                             NA's   :3        
##     GENERO              RFC            FECHA.DE.ALTA    Primer.mes  
##  Length:111         Length:111         Min.   :2010   Min.   :2010  
##  Class :character   Class :character   1st Qu.:2020   1st Qu.:2021  
##  Mode  :character   Mode  :character   Median :2022   Median :2022  
##                                        Mean   :2021   Mean   :2021  
##                                        3rd Qu.:2022   3rd Qu.:2022  
##                                        Max.   :2022   Max.   :2022  
##                                        NA's   :1      NA's   :3     
##     X4to.mes         BAJA       PUESTO          DEPARTAMENTO      
##  Min.   :1905   Min.   :3    Length:111         Length:111        
##  1st Qu.:2021   1st Qu.:3    Class :character   Class :character  
##  Median :2022   Median :3    Mode  :character   Mode  :character  
##  Mean   :2020   Mean   :3                                         
##  3rd Qu.:2022   3rd Qu.:3                                         
##  Max.   :2022   Max.   :3                                         
##  NA's   :3      NA's   :98                                        
##  NO.SEGURO.SOCIAL   SALARIO.DIARIO.IMSS FACTOR.CRED.INFONAVIT
##  Length:111         Min.   :144.4       Length:111           
##  Class :character   1st Qu.:176.7       Class :character     
##  Mode  :character   Median :180.7       Mode  :character     
##                     Mean   :179.1                            
##                     3rd Qu.:180.7                            
##                     Max.   :337.1                            
##                                                              
##  N...CREDITO.INFONAVIT LUGAR.DE.NACIMIENTO     CURP          
##  Length:111            Length:111          Length:111        
##  Class :character      Class :character    Class :character  
##  Mode  :character      Mode  :character    Mode  :character  
##                                                              
##                                                              
##                                                              
##                                                              
##     CALLE           NUMERO.INTERNO       COLONIA           MUNICIPIO        
##  Length:111         Length:111         Length:111         Length:111        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     ESTADO          CODIGO.POSTAL   ESTADO.CIVIL       TARJETA....CUENTA 
##  Length:111         Min.   :25016   Length:111         Length:111        
##  Class :character   1st Qu.:66642   Class :character   Class :character  
##  Mode  :character   Median :66646   Mode  :character   Mode  :character  
##                     Mean   :63300                                        
##                     3rd Qu.:66649                                        
##                     Max.   :67493                                        
## 

Técnica 3, ERRORES TIPOGRÁFICOS Y ERRORES SIMILARES
Primeramente, se notó que en el excel algunas fechas estaban con formato día/mes/año y otros mes/día/año. Por lo que para tener la información más real y precisa posible, se extrajeron solo los AÑOS para el análisis.

Técnica 1, REMOVER DATOS IRRELEVANTES
Existen muchas variables en la base de datos, y muchas de estas contienen datos personales de los colaboradores, por lo que es información muy sensible y que no aporta mucho insight al análisis. Por lo tanto, se ha escogido la técnica de remover datos irrelevantes a la información personal de los empleados que no sirvan mucho para el análisis, como lo es su nombre, apellido, RFC, CURP. Sin embargo, otros datos personales se han mantenido para hacer un análisis de diversidad e inclusión (género, año de nacimiento, estado civil).

Se eliminan las columnas que no aportan mucho al análisis de datos.

colab2<-colab
colab2<-subset(colab2,select=-c(No..De.Empleado,TARJETA....CUENTA,CODIGO.POSTAL,COLONIA,NUMERO.INTERNO,CALLE,CURP,FACTOR.CRED.INFONAVIT,NO.SEGURO.SOCIAL,RFC,APELLIDOS,NOMBRE,N...CREDITO.INFONAVIT,X4to.mes,Primer.mes))
summary(colab2)
##  AÑO.DE.NACIMIENTO    GENERO          FECHA.DE.ALTA       BAJA   
##  Min.   :1955      Length:111         Min.   :2010   Min.   :3   
##  1st Qu.:1978      Class :character   1st Qu.:2020   1st Qu.:3   
##  Median :1989      Mode  :character   Median :2022   Median :3   
##  Mean   :1987                         Mean   :2021   Mean   :3   
##  3rd Qu.:1997                         3rd Qu.:2022   3rd Qu.:3   
##  Max.   :2022                         Max.   :2022   Max.   :3   
##  NA's   :3                            NA's   :1      NA's   :98  
##     PUESTO          DEPARTAMENTO       SALARIO.DIARIO.IMSS LUGAR.DE.NACIMIENTO
##  Length:111         Length:111         Min.   :144.4       Length:111         
##  Class :character   Class :character   1st Qu.:176.7       Class :character   
##  Mode  :character   Mode  :character   Median :180.7       Mode  :character   
##                                        Mean   :179.1                          
##                                        3rd Qu.:180.7                          
##                                        Max.   :337.1                          
##                                                                               
##   MUNICIPIO            ESTADO          ESTADO.CIVIL      
##  Length:111         Length:111         Length:111        
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
##                                                          
## 

Técnica 5, VALORES FALTANTES
Con un rápido vistazo de la base de datos, se podía observar que faltaban ciertos valores en las diferentes variables. Si faltaban muchos datos en una sola variable, se optó por eliminar la columna, mientras que en otros casos donde haya pocos por variables sólo se eliminaron los registros.

¿Cuántos NA tengo por variable?

sapply(colab2,function(x) sum(is.na(x)))
##   AÑO.DE.NACIMIENTO              GENERO       FECHA.DE.ALTA                BAJA 
##                   3                   0                   1                  98 
##              PUESTO        DEPARTAMENTO SALARIO.DIARIO.IMSS LUGAR.DE.NACIMIENTO 
##                   0                   0                   0                   0 
##           MUNICIPIO              ESTADO        ESTADO.CIVIL 
##                   0                   0                   0

Se observan muchos NAs en la columna “BAJA”, por lo que se elimina en su totalidad esta variable (columna).

colab3<-colab2
colab3<-subset(colab2,select=-c(BAJA))
summary(colab3)
##  AÑO.DE.NACIMIENTO    GENERO          FECHA.DE.ALTA     PUESTO         
##  Min.   :1955      Length:111         Min.   :2010   Length:111        
##  1st Qu.:1978      Class :character   1st Qu.:2020   Class :character  
##  Median :1989      Mode  :character   Median :2022   Mode  :character  
##  Mean   :1987                         Mean   :2021                     
##  3rd Qu.:1997                         3rd Qu.:2022                     
##  Max.   :2022                         Max.   :2022                     
##  NA's   :3                            NA's   :1                        
##  DEPARTAMENTO       SALARIO.DIARIO.IMSS LUGAR.DE.NACIMIENTO  MUNICIPIO        
##  Length:111         Min.   :144.4       Length:111          Length:111        
##  Class :character   1st Qu.:176.7       Class :character    Class :character  
##  Mode  :character   Median :180.7       Mode  :character    Mode  :character  
##                     Mean   :179.1                                             
##                     3rd Qu.:180.7                                             
##                     Max.   :337.1                                             
##                                                                               
##     ESTADO          ESTADO.CIVIL      
##  Length:111         Length:111        
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
##                                       
## 

Se vuelve a ver lo de NAs

¿Cuántos NA tengo por variable?

sapply(colab3,function(x) sum(is.na(x)))
##   AÑO.DE.NACIMIENTO              GENERO       FECHA.DE.ALTA              PUESTO 
##                   3                   0                   1                   0 
##        DEPARTAMENTO SALARIO.DIARIO.IMSS LUGAR.DE.NACIMIENTO           MUNICIPIO 
##                   0                   0                   0                   0 
##              ESTADO        ESTADO.CIVIL 
##                   0                   0

Eliminar renglones de NAs

colab4<-colab3
colab4<-na.omit(colab4)      
summary(colab4)
##  AÑO.DE.NACIMIENTO    GENERO          FECHA.DE.ALTA     PUESTO         
##  Min.   :1955      Length:107         Min.   :2010   Length:107        
##  1st Qu.:1978      Class :character   1st Qu.:2020   Class :character  
##  Median :1989      Mode  :character   Median :2022   Mode  :character  
##  Mean   :1987                         Mean   :2021                     
##  3rd Qu.:1997                         3rd Qu.:2022                     
##  Max.   :2022                         Max.   :2022                     
##  DEPARTAMENTO       SALARIO.DIARIO.IMSS LUGAR.DE.NACIMIENTO  MUNICIPIO        
##  Length:107         Min.   :144.4       Length:107          Length:107        
##  Class :character   1st Qu.:176.7       Class :character    Class :character  
##  Mode  :character   Median :180.7       Mode  :character    Mode  :character  
##                     Mean   :179.1                                             
##                     3rd Qu.:180.7                                             
##                     Max.   :337.1                                             
##     ESTADO          ESTADO.CIVIL      
##  Length:107         Length:107        
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
## 
sapply(colab4,function(x) sum(is.na(x)))
##   AÑO.DE.NACIMIENTO              GENERO       FECHA.DE.ALTA              PUESTO 
##                   0                   0                   0                   0 
##        DEPARTAMENTO SALARIO.DIARIO.IMSS LUGAR.DE.NACIMIENTO           MUNICIPIO 
##                   0                   0                   0                   0 
##              ESTADO        ESTADO.CIVIL 
##                   0                   0

Eliminar error de fecha de nacimiento (existen datos que muestran que alguien nació en el 2022). Se mantienen solo a los colaboradores que nacieron antes del 2004, los mayores de edad.

colab5<-colab4
colab5<-colab5[colab5$AÑO.DE.NACIMIENTO<2004,]
summary(colab5)
##  AÑO.DE.NACIMIENTO    GENERO          FECHA.DE.ALTA     PUESTO         
##  Min.   :1955      Length:105         Min.   :2010   Length:105        
##  1st Qu.:1978      Class :character   1st Qu.:2020   Class :character  
##  Median :1989      Mode  :character   Median :2022   Mode  :character  
##  Mean   :1987                         Mean   :2021                     
##  3rd Qu.:1996                         3rd Qu.:2022                     
##  Max.   :2003                         Max.   :2022                     
##  DEPARTAMENTO       SALARIO.DIARIO.IMSS LUGAR.DE.NACIMIENTO  MUNICIPIO        
##  Length:105         Min.   :144.4       Length:105          Length:105        
##  Class :character   1st Qu.:176.7       Class :character    Class :character  
##  Mode  :character   Median :180.7       Mode  :character    Mode  :character  
##                     Mean   :179.1                                             
##                     3rd Qu.:180.7                                             
##                     Max.   :337.1                                             
##     ESTADO          ESTADO.CIVIL      
##  Length:105         Length:105        
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
## 

Se importa (de nuevo) la base de datos, debido a que se hicieron cambios en el excel: se calculó la edad, en vez de tener el año de nacimiento.

colab6<-read.csv("/Users/elenavela/Desktop/wal/rlimpia.csv")
summary(colab6)
##       EDAD          GENERO          FECHA.DE.ALTA    Primer.mes  
##  Min.   :19.00   Length:104         Min.   :2010   Min.   :2010  
##  1st Qu.:25.75   Class :character   1st Qu.:2021   1st Qu.:2020  
##  Median :33.00   Mode  :character   Median :2022   Median :2022  
##  Mean   :35.48                      Mean   :2021   Mean   :2021  
##  3rd Qu.:44.25                      3rd Qu.:2022   3rd Qu.:2022  
##  Max.   :67.00                      Max.   :2022   Max.   :2022  
##     X4to.mes       PUESTO          DEPARTAMENTO       SALARIO.DIARIO.IMSS
##  Min.   :2010   Length:104         Length:104         Min.   :144.4      
##  1st Qu.:2021   Class :character   Class :character   1st Qu.:176.7      
##  Median :2022   Mode  :character   Mode  :character   Median :180.7      
##  Mean   :2021                                         Mean   :179.3      
##  3rd Qu.:2022                                         3rd Qu.:180.7      
##  Max.   :2022                                         Max.   :337.1      
##  LUGAR.DE.NACIMIENTO  MUNICIPIO            ESTADO          ESTADO.CIVIL      
##  Length:104          Length:104         Length:104         Length:104        
##  Class :character    Class :character   Class :character   Class :character  
##  Mode  :character    Mode  :character   Mode  :character   Mode  :character  
##                                                                              
##                                                                              
##                                                                              
##    Empleado        
##  Length:104        
##  Class :character  
##  Mode  :character  
##                    
##                    
## 

Se vuelve a analizar, se eliminan columnas (o variables) que no se consideran relevantes para el análisis de datos.

colab7<-colab6
colab7<-subset(colab6,select=-c(Primer.mes,X4to.mes,DEPARTAMENTO,Empleado))

Se cambian los nombres de columnas con el fin de facilitar el análisis que será llevado a continuación.

str(colab7)
## 'data.frame':    104 obs. of  9 variables:
##  $ EDAD               : int  32 38 38 37 38 60 56 46 59 43 ...
##  $ GENERO             : chr  "FEMENINO" "MASCULINO" "FEMENINO" "MASCULINO" ...
##  $ FECHA.DE.ALTA      : int  2013 2018 2015 2016 2020 2020 2022 2022 2022 2022 ...
##  $ PUESTO             : chr  "SUPERVISORA" "MANTENIMIENTO" "COSTURERA" "AYUDANTE GENERAL" ...
##  $ SALARIO.DIARIO.IMSS: num  337 280 260 241 241 ...
##  $ LUGAR.DE.NACIMIENTO: chr  "" "" "" "" ...
##  $ MUNICIPIO          : chr  "APODACA" "APODACA" "APODACA" "APODACA" ...
##  $ ESTADO             : chr  "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" ...
##  $ ESTADO.CIVIL       : chr  "Casado" "Soltero" "Casado" "Casado" ...
names (colab7) = c("edad", "genero", "alta", "puesto", "salario_diario", "lugar.nacim.","mpio","estado","civil")
names (colab7)
## [1] "edad"           "genero"         "alta"           "puesto"        
## [5] "salario_diario" "lugar.nacim."   "mpio"           "estado"        
## [9] "civil"
str(colab7)
## 'data.frame':    104 obs. of  9 variables:
##  $ edad          : int  32 38 38 37 38 60 56 46 59 43 ...
##  $ genero        : chr  "FEMENINO" "MASCULINO" "FEMENINO" "MASCULINO" ...
##  $ alta          : int  2013 2018 2015 2016 2020 2020 2022 2022 2022 2022 ...
##  $ puesto        : chr  "SUPERVISORA" "MANTENIMIENTO" "COSTURERA" "AYUDANTE GENERAL" ...
##  $ salario_diario: num  337 280 260 241 241 ...
##  $ lugar.nacim.  : chr  "" "" "" "" ...
##  $ mpio          : chr  "APODACA" "APODACA" "APODACA" "APODACA" ...
##  $ estado        : chr  "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" ...
##  $ civil         : chr  "Casado" "Soltero" "Casado" "Casado" ...

EXPORTAR BASE DE DATOS

colab_bd <- colab7
write.csv(colab_bd, file ="colab_FORM_limpia.csv", row.names = FALSE)

Seguimos con “bajas”

bajas<-read.csv("/Users/elenavela/Downloads/bajas.csv")
summary(bajas)
##  NOMBRE.COMPLETO         EDAD          GENERO          FECHA.DE.ALTA     
##  Length:233         Min.   :18.00   Length:233         Length:233        
##  Class :character   1st Qu.:23.00   Class :character   Class :character  
##  Mode  :character   Median :29.00   Mode  :character   Mode  :character  
##                     Mean   :30.77                                        
##                     3rd Qu.:37.00                                        
##                     Max.   :61.00                                        
##                                                                          
##  MOTIVO.DE.BAJA     DÍAS.DE.DURACIÓN      BAJA              PUESTO         
##  Length:233         Min.   :   0.00   Length:233         Length:233        
##  Class :character   1st Qu.:   9.00   Class :character   Class :character  
##  Mode  :character   Median :  20.50   Mode  :character   Mode  :character  
##                     Mean   :  78.84                                        
##                     3rd Qu.:  48.25                                        
##                     Max.   :1966.00                                        
##                     NA's   :13                                             
##  SALARIO.DIARIO.IMSS    ESTADO          ESTADO.CIVIL      
##  Min.   :144.4       Length:233         Length:233        
##  1st Qu.:180.7       Class :character   Class :character  
##  Median :180.7       Mode  :character   Mode  :character  
##  Mean   :177.9                                            
##  3rd Qu.:180.7                                            
##  Max.   :500.0                                            
## 

Técnica 4, CONVERTIR TIPO DE DATOS

Después de notar que ciertas variables cuantitativas no son leídas como tal, es necesario conventirlas de carácter a fecha o a entero, como corresponda.

Convertir de carácter a fecha

bajas2<-bajas
bajas2$FECHA.DE.ALTA<-as.Date(bajas2$FECHA.DE.ALTA,format="%d/%m/%Y")  
str(bajas2)
## 'data.frame':    233 obs. of  11 variables:
##  $ NOMBRE.COMPLETO    : chr  "MARIO VALDEZ ORTIZ" "ISABEL BARRIOS MENDEZ" "MARIA ELIZABETH GOMEZ HERNANDEZ" "ALONDRA ABIGAIL ESCARCIA GOMEZ" ...
##  $ EDAD               : int  32 36 23 21 29 46 29 31 50 19 ...
##  $ GENERO             : chr  "MASCULINO" "FEMENINO" "FEMENINO" "FEMENINO" ...
##  $ FECHA.DE.ALTA      : Date, format: "2020-03-09" "2021-11-09" ...
##  $ MOTIVO.DE.BAJA     : chr  "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" ...
##  $ DÍAS.DE.DURACIÓN   : int  628 60 59 59 51 37 37 31 18 224 ...
##  $ BAJA               : chr  "27/11/21" "08/01/22" "08/01/22" "08/01/22" ...
##  $ PUESTO             : chr  "DISEÑO" "AYUDANTE GENERAL" "AYUDANTE GENERAL" "AYUDANTE GENERAL" ...
##  $ SALARIO.DIARIO.IMSS: num  500 152 152 152 152 ...
##  $ ESTADO             : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ ESTADO.CIVIL       : chr  "Soltero" "Unión libre" "Matrimonio" "Soltero" ...

Convertir de carácter a entero

bajas3<-bajas2
bajas3$EDAD<-as.integer(bajas3$EDAD)
str(bajas3)
## 'data.frame':    233 obs. of  11 variables:
##  $ NOMBRE.COMPLETO    : chr  "MARIO VALDEZ ORTIZ" "ISABEL BARRIOS MENDEZ" "MARIA ELIZABETH GOMEZ HERNANDEZ" "ALONDRA ABIGAIL ESCARCIA GOMEZ" ...
##  $ EDAD               : int  32 36 23 21 29 46 29 31 50 19 ...
##  $ GENERO             : chr  "MASCULINO" "FEMENINO" "FEMENINO" "FEMENINO" ...
##  $ FECHA.DE.ALTA      : Date, format: "2020-03-09" "2021-11-09" ...
##  $ MOTIVO.DE.BAJA     : chr  "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" ...
##  $ DÍAS.DE.DURACIÓN   : int  628 60 59 59 51 37 37 31 18 224 ...
##  $ BAJA               : chr  "27/11/21" "08/01/22" "08/01/22" "08/01/22" ...
##  $ PUESTO             : chr  "DISEÑO" "AYUDANTE GENERAL" "AYUDANTE GENERAL" "AYUDANTE GENERAL" ...
##  $ SALARIO.DIARIO.IMSS: num  500 152 152 152 152 ...
##  $ ESTADO             : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ ESTADO.CIVIL       : chr  "Soltero" "Unión libre" "Matrimonio" "Soltero" ...

Técnica 1, REMOVER DATOS IRRELEVANTES

Se nota que existe una columna que no aporta mucho al análisis, la fecha de baja, debido a que ya se tienen los días de duración de la persona. Se opta por remover este dato irrelevante.

Eliminar columnas

bajas4<-bajas3
bajas4<-subset(bajas4,select=-c(BAJA))
summary(bajas4)
##  NOMBRE.COMPLETO         EDAD          GENERO          FECHA.DE.ALTA       
##  Length:233         Min.   :18.00   Length:233         Min.   :0016-10-12  
##  Class :character   1st Qu.:23.00   Class :character   1st Qu.:0022-02-18  
##  Mode  :character   Median :29.00   Mode  :character   Median :0022-05-18  
##                     Mean   :30.77                      Mean   :0339-09-15  
##                     3rd Qu.:37.00                      3rd Qu.:0022-07-19  
##                     Max.   :61.00                      Max.   :2022-07-11  
##                                                                            
##  MOTIVO.DE.BAJA     DÍAS.DE.DURACIÓN     PUESTO          SALARIO.DIARIO.IMSS
##  Length:233         Min.   :   0.00   Length:233         Min.   :144.4      
##  Class :character   1st Qu.:   9.00   Class :character   1st Qu.:180.7      
##  Mode  :character   Median :  20.50   Mode  :character   Median :180.7      
##                     Mean   :  78.84                      Mean   :177.9      
##                     3rd Qu.:  48.25                      3rd Qu.:180.7      
##                     Max.   :1966.00                      Max.   :500.0      
##                     NA's   :13                                              
##     ESTADO          ESTADO.CIVIL      
##  Length:233         Length:233        
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
##                                       
## 

Técnica 5, VALORES FALTANTES

Para obtener un análisis más real, se identifican los valorees faltantes y se toma una decisión dependiendo de los resultados.

¿Cuántos NA tengo por variable?

sapply(bajas4,function(x) sum(is.na(x)))
##     NOMBRE.COMPLETO                EDAD              GENERO       FECHA.DE.ALTA 
##                   0                   0                   0                   0 
##      MOTIVO.DE.BAJA    DÍAS.DE.DURACIÓN              PUESTO SALARIO.DIARIO.IMSS 
##                   0                  13                   0                   0 
##              ESTADO        ESTADO.CIVIL 
##                   0                   0

Debido a que 13 es un número importante en esta base de datos, se ha decidido NO borrar los registros; se opta por reemplazar NAs por su mediana.

bajas5<-bajas4
bajas5$DÍAS.DE.DURACIÓN[is.na(bajas5$DÍAS.DE.DURACIÓN)]<- median(bajas5$DÍAS.DE.DURACIÓN, na.rm=TRUE)

¿Cuántos NA tengo por variable?

sapply(bajas5,function(x) sum(is.na(x)))
##     NOMBRE.COMPLETO                EDAD              GENERO       FECHA.DE.ALTA 
##                   0                   0                   0                   0 
##      MOTIVO.DE.BAJA    DÍAS.DE.DURACIÓN              PUESTO SALARIO.DIARIO.IMSS 
##                   0                   0                   0                   0 
##              ESTADO        ESTADO.CIVIL 
##                   0                   0

Queda limpio.

Se cambian los nombres de las columnas por nombres más adecuados para el análisis:

str(bajas5)
## 'data.frame':    233 obs. of  10 variables:
##  $ NOMBRE.COMPLETO    : chr  "MARIO VALDEZ ORTIZ" "ISABEL BARRIOS MENDEZ" "MARIA ELIZABETH GOMEZ HERNANDEZ" "ALONDRA ABIGAIL ESCARCIA GOMEZ" ...
##  $ EDAD               : int  32 36 23 21 29 46 29 31 50 19 ...
##  $ GENERO             : chr  "MASCULINO" "FEMENINO" "FEMENINO" "FEMENINO" ...
##  $ FECHA.DE.ALTA      : Date, format: "2020-03-09" "2021-11-09" ...
##  $ MOTIVO.DE.BAJA     : chr  "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" ...
##  $ DÍAS.DE.DURACIÓN   : num  628 60 59 59 51 37 37 31 18 224 ...
##  $ PUESTO             : chr  "DISEÑO" "AYUDANTE GENERAL" "AYUDANTE GENERAL" "AYUDANTE GENERAL" ...
##  $ SALARIO.DIARIO.IMSS: num  500 152 152 152 152 ...
##  $ ESTADO             : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ ESTADO.CIVIL       : chr  "Soltero" "Unión libre" "Matrimonio" "Soltero" ...
names (bajas5) = c("nombre", "edad", "genero", "alta", "motivo_baja", "duracion", "puesto","salario_diario","estado","civil")
names (bajas5)
##  [1] "nombre"         "edad"           "genero"         "alta"          
##  [5] "motivo_baja"    "duracion"       "puesto"         "salario_diario"
##  [9] "estado"         "civil"
str(bajas5)
## 'data.frame':    233 obs. of  10 variables:
##  $ nombre        : chr  "MARIO VALDEZ ORTIZ" "ISABEL BARRIOS MENDEZ" "MARIA ELIZABETH GOMEZ HERNANDEZ" "ALONDRA ABIGAIL ESCARCIA GOMEZ" ...
##  $ edad          : int  32 36 23 21 29 46 29 31 50 19 ...
##  $ genero        : chr  "MASCULINO" "FEMENINO" "FEMENINO" "FEMENINO" ...
##  $ alta          : Date, format: "2020-03-09" "2021-11-09" ...
##  $ motivo_baja   : chr  "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" ...
##  $ duracion      : num  628 60 59 59 51 37 37 31 18 224 ...
##  $ puesto        : chr  "DISEÑO" "AYUDANTE GENERAL" "AYUDANTE GENERAL" "AYUDANTE GENERAL" ...
##  $ salario_diario: num  500 152 152 152 152 ...
##  $ estado        : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ civil         : chr  "Soltero" "Unión libre" "Matrimonio" "Soltero" ...

Se elimina también la variable de “nombre” al resultar poco interesante para el análisis.

bajas6<-bajas5
bajas6<-subset(bajas6,select=-c(nombre))
str(bajas6)
## 'data.frame':    233 obs. of  9 variables:
##  $ edad          : int  32 36 23 21 29 46 29 31 50 19 ...
##  $ genero        : chr  "MASCULINO" "FEMENINO" "FEMENINO" "FEMENINO" ...
##  $ alta          : Date, format: "2020-03-09" "2021-11-09" ...
##  $ motivo_baja   : chr  "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" ...
##  $ duracion      : num  628 60 59 59 51 37 37 31 18 224 ...
##  $ puesto        : chr  "DISEÑO" "AYUDANTE GENERAL" "AYUDANTE GENERAL" "AYUDANTE GENERAL" ...
##  $ salario_diario: num  500 152 152 152 152 ...
##  $ estado        : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ civil         : chr  "Soltero" "Unión libre" "Matrimonio" "Soltero" ...

Cambiar “FEMENINO” por “F”, y “MASCULINO” por “M”. Se ha optado por hacer esto para tener la información más limpia en las gráficas.

colab7$genero[colab7$genero == "FEMENINO"] <- "F"
colab7$genero[colab7$genero == "MASCULINO"] <- "M"
str(colab7)
## 'data.frame':    104 obs. of  9 variables:
##  $ edad          : int  32 38 38 37 38 60 56 46 59 43 ...
##  $ genero        : chr  "F" "M" "F" "M" ...
##  $ alta          : int  2013 2018 2015 2016 2020 2020 2022 2022 2022 2022 ...
##  $ puesto        : chr  "SUPERVISORA" "MANTENIMIENTO" "COSTURERA" "AYUDANTE GENERAL" ...
##  $ salario_diario: num  337 280 260 241 241 ...
##  $ lugar.nacim.  : chr  "" "" "" "" ...
##  $ mpio          : chr  "APODACA" "APODACA" "APODACA" "APODACA" ...
##  $ estado        : chr  "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" ...
##  $ civil         : chr  "Casado" "Soltero" "Casado" "Casado" ...
bajas6$genero[bajas6$genero == "FEMENINO"] <- "F"
bajas6$genero[bajas6$genero == "MASCULINO"] <- "M"
str(bajas6)
## 'data.frame':    233 obs. of  9 variables:
##  $ edad          : int  32 36 23 21 29 46 29 31 50 19 ...
##  $ genero        : chr  "M" "F" "F" "F" ...
##  $ alta          : Date, format: "2020-03-09" "2021-11-09" ...
##  $ motivo_baja   : chr  "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" ...
##  $ duracion      : num  628 60 59 59 51 37 37 31 18 224 ...
##  $ puesto        : chr  "DISEÑO" "AYUDANTE GENERAL" "AYUDANTE GENERAL" "AYUDANTE GENERAL" ...
##  $ salario_diario: num  500 152 152 152 152 ...
##  $ estado        : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ civil         : chr  "Soltero" "Unión libre" "Matrimonio" "Soltero" ...

EXPORTAR BASE DE DATOS

bajas_bd <- bajas6
write.csv(bajas_bd, file ="bajas_FORM_limpia.csv", row.names = FALSE)

FORM- Delivery plan

Se juntaron los datos y la limpia de la base de datos principal desde excel.

La fecha se encontraba en formato de EUA, se ha cambiado a México.

plan<-read.csv("/Users/elenavela/Downloads/DELIVERY PLAN bdf_Prueba (1).csv")
summary(plan)
##     ID_Fecha        Fecha             CLIENTE             Pedidos       
##  Min.   : 1.00   Length:228         Length:228         Min.   :    0.0  
##  1st Qu.: 3.75   Class :character   Class :character   1st Qu.:    0.0  
##  Median : 6.50   Mode  :character   Mode  :character   Median :    0.0  
##  Mean   : 6.50                                         Mean   : 1703.1  
##  3rd Qu.: 9.25                                         3rd Qu.:  233.8  
##  Max.   :12.00                                         Max.   :52779.0

Se cambian los nombres para tener un formato más limpio y volver el análisis mejor.

names (plan) = c("ID_fecha", "fecha", "cliente", "pedidos")
names (plan)
## [1] "ID_fecha" "fecha"    "cliente"  "pedidos"
str(plan)
## 'data.frame':    228 obs. of  4 variables:
##  $ ID_fecha: int  1 1 1 1 1 1 1 1 1 1 ...
##  $ fecha   : chr  "31/01/2022" "31/01/2022" "31/01/2022" "31/01/2022" ...
##  $ cliente : chr  "STB3" "STB 1" "YF RAMOS" "INOAC POLYTEC" ...
##  $ pedidos : int  481 0 227 0 400 328 393 0 8975 449 ...

Técnica 4, CONVERTIR TIPO DE DATOS

Se cambia el formato de la fecha= de carácter a fecha.

plan2<-plan
plan2$fecha<-as.Date(plan2$fecha,format="%d/%m/%Y")  
str(plan2)
## 'data.frame':    228 obs. of  4 variables:
##  $ ID_fecha: int  1 1 1 1 1 1 1 1 1 1 ...
##  $ fecha   : Date, format: "2022-01-31" "2022-01-31" ...
##  $ cliente : chr  "STB3" "STB 1" "YF RAMOS" "INOAC POLYTEC" ...
##  $ pedidos : int  481 0 227 0 400 328 393 0 8975 449 ...

Técnica 5, VALORES FALTANTES

¿Cuántos NA tengo por variable?

sapply(plan2,function(x) sum(is.na(x)))
## ID_fecha    fecha  cliente  pedidos 
##        0        0        0        0

Exportar base de datos

plan_final <- plan2
write.csv(plan_final, file ="deliveryplan_final2.csv", row.names = FALSE)

FORM- Delivery performance

Importar base de datos

perf<-read.csv("/Users/elenavela/Downloads/deliverperf.csv")
summary(perf)
##    Cliente          Transportista         Fecha            Plan.arrival   
##  Length:104         Length:104         Length:104         Min.   : 0.000  
##  Class :character   Class :character   Class :character   1st Qu.: 0.000  
##  Mode  :character   Mode  :character   Mode  :character   Median : 4.000  
##                                                           Mean   : 6.625  
##                                                           3rd Qu.:10.750  
##                                                           Max.   :20.000  
##   Real.arrival    Real.Departure     Diferencia   
##  Min.   : 0.000   Min.   : 0.000   Min.   :0.000  
##  1st Qu.: 0.000   1st Qu.: 0.000   1st Qu.:0.000  
##  Median : 0.000   Median : 0.000   Median :0.000  
##  Mean   : 3.831   Mean   : 4.145   Mean   :0.303  
##  3rd Qu.: 8.000   3rd Qu.: 8.662   3rd Qu.:0.520  
##  Max.   :20.400   Max.   :22.550   Max.   :3.300
perf2<-perf
str(perf2)
## 'data.frame':    104 obs. of  7 variables:
##  $ Cliente       : chr  "PRINTEL " "MAHLE" "MAHLE" "MAHLE" ...
##  $ Transportista : chr  "JUVENCIO" "DIONICIO " "DIONICIO " "DIONICIO " ...
##  $ Fecha         : chr  "jul-21" "jul-21" "jul-21" "jul-21" ...
##  $ Plan.arrival  : num  16 8 9 20 0 0 0 0 16 8 ...
##  $ Real.arrival  : num  0 8 9 20 0 0 0 0 16 8 ...
##  $ Real.Departure: num  0 8.55 10 21.1 0 0 0 0 17 8.51 ...
##  $ Diferencia    : num  0 0.55 1 1.1 0 0 0 0 1 0.51 ...

Técnica 5, VALORES FALTANTES

¿Cuántos NA tengo por variable?

sapply(perf2,function(x) sum(is.na(x)))
##        Cliente  Transportista          Fecha   Plan.arrival   Real.arrival 
##              0              0              0              0              0 
## Real.Departure     Diferencia 
##              0              0

Técnica 1, REMOVER DATOS IRRELEVANTES

Se cambian diferentes variables de carácter a entero

perf2$Real.arrival <- substr(perf2$Real.arrival, start = 1, stop = 2)
perf2$Real.arrival<- as.integer(perf2$Real.arrival)  
perf2$Real.Departure <- substr(perf2$Real.Departure, start = 1, stop = 2)
perf2$Real.Departure<- as.integer(perf2$Real.Departure)  
perf2$Plan.arrival <- substr(perf2$Plan.arrival, start = 1, stop = 2)
perf2$Plan.arrival<- as.integer(perf2$Plan.arrival)  
str(perf2)
## 'data.frame':    104 obs. of  7 variables:
##  $ Cliente       : chr  "PRINTEL " "MAHLE" "MAHLE" "MAHLE" ...
##  $ Transportista : chr  "JUVENCIO" "DIONICIO " "DIONICIO " "DIONICIO " ...
##  $ Fecha         : chr  "jul-21" "jul-21" "jul-21" "jul-21" ...
##  $ Plan.arrival  : int  16 8 9 20 0 0 0 0 16 8 ...
##  $ Real.arrival  : int  0 8 9 20 0 0 0 0 16 8 ...
##  $ Real.Departure: int  0 8 10 21 0 0 0 0 17 8 ...
##  $ Diferencia    : num  0 0.55 1 1.1 0 0 0 0 1 0.51 ...

Se eliminan columnas que no aportan mucho al análisis

perf3<-perf2
perf3<-subset(perf3,select=-c(Transportista,Plan.arrival,Real.arrival,Real.Departure))
summary(perf3)
##    Cliente             Fecha             Diferencia   
##  Length:104         Length:104         Min.   :0.000  
##  Class :character   Class :character   1st Qu.:0.000  
##  Mode  :character   Mode  :character   Median :0.000  
##                                        Mean   :0.303  
##                                        3rd Qu.:0.520  
##                                        Max.   :3.300

Se renombran las columnas

names(perf3)=c("cliente","fecha","dif")
str(perf3)
## 'data.frame':    104 obs. of  3 variables:
##  $ cliente: chr  "PRINTEL " "MAHLE" "MAHLE" "MAHLE" ...
##  $ fecha  : chr  "jul-21" "jul-21" "jul-21" "jul-21" ...
##  $ dif    : num  0 0.55 1 1.1 0 0 0 0 1 0.51 ...

Exportar base de datos

performance_final <- perf3
write.csv(performance_final, file ="deliveryperformance_finalya.csv", row.names = FALSE)

FORM- Producción

Para esta base de datos, se tiene la información tres meses (casi completos): julio, agosto, septiembre.

Importar base de datos AGOSTO. Único mes completo.

prod<-read.csv("/Users/elenavela/Downloads/produccion.csv")
summary(prod)
##    CLIENTE            ID.FORM            PRODUCTO         PIEZAS.PROG.      
##  Length:2568        Length:2568        Length:2568        Length:2568       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##   TMO..MIN.           HR..FIN          ESTACION.ARRANQUE  Laminas.procesadas
##  Length:2568        Length:2568        Length:2568        Length:2568       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##  INICIO.SEP.UP      FIN.INICIO.DE.SEP.UP INICIO.de.PROCESO  FIN.de.PROCESO    
##  Length:2568        Length:2568          Length:2568        Length:2568       
##  Class :character   Class :character     Class :character   Class :character  
##  Mode  :character   Mode  :character     Mode  :character   Mode  :character  
##  TIEMPO.CALIDAD    
##  Length:2568       
##  Class :character  
##  Mode  :character

Técnica 1, REMOVER DATOS IRRELEVANTES
No todas las variables nos interesan, o aportan mucho, al análisis que se pretende hacer. Se ha definido que las variables con las que se desea quedar son las siguientes: cliente, piezas programadas, tiempo mínimo, láminas procesadas y tiempo de calidad.

Se eliminan columnas

prod2<-prod
prod2<-subset(prod2,select=-c(ID.FORM,PRODUCTO,HR..FIN,ESTACION.ARRANQUE,INICIO.SEP.UP,FIN.INICIO.DE.SEP.UP,INICIO.de.PROCESO,FIN.de.PROCESO))
summary(prod2)
##    CLIENTE          PIEZAS.PROG.        TMO..MIN.         Laminas.procesadas
##  Length:2568        Length:2568        Length:2568        Length:2568       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##  TIEMPO.CALIDAD    
##  Length:2568       
##  Class :character  
##  Mode  :character

Técnica 4, CONVERTIR TIPO DE DATOS

Para poder determinar los valores faltantes, es necesario convertir los valores de carácter a enteros/hora/fecha.

Carácter a entero

prod3<-prod2
prod3$PIEZAS.PROG.<-as.integer(prod3$PIEZAS.PROG.)
str(prod3)
## 'data.frame':    2568 obs. of  5 variables:
##  $ CLIENTE           : chr  "VARROC" "VARROC" "VARROC" "DENSO" ...
##  $ PIEZAS.PROG.      : int  199 57 68 192 192 400 80 104 104 160 ...
##  $ TMO..MIN.         : chr  "15" "10" "10" "15" ...
##  $ Laminas.procesadas: chr  "201" "116" "69" "49" ...
##  $ TIEMPO.CALIDAD    : chr  "1" "1" "1" "1" ...
summary(prod3)
##    CLIENTE           PIEZAS.PROG.     TMO..MIN.         Laminas.procesadas
##  Length:2568        Min.   :   1.0   Length:2568        Length:2568       
##  Class :character   1st Qu.: 100.0   Class :character   Class :character  
##  Mode  :character   Median : 176.0   Mode  :character   Mode  :character  
##                     Mean   : 183.5                                        
##                     3rd Qu.: 201.0                                        
##                     Max.   :1280.0                                        
##                     NA's   :164                                           
##  TIEMPO.CALIDAD    
##  Length:2568       
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 

Carácter a hora

prod4<-prod3
prod4$TMO..MIN.<-as.integer(prod4$TMO..MIN.)
str(prod4)
## 'data.frame':    2568 obs. of  5 variables:
##  $ CLIENTE           : chr  "VARROC" "VARROC" "VARROC" "DENSO" ...
##  $ PIEZAS.PROG.      : int  199 57 68 192 192 400 80 104 104 160 ...
##  $ TMO..MIN.         : int  15 10 10 15 15 30 15 15 15 20 ...
##  $ Laminas.procesadas: chr  "201" "116" "69" "49" ...
##  $ TIEMPO.CALIDAD    : chr  "1" "1" "1" "1" ...

Carácter a entero

prod5<-prod4
prod5$Laminas.procesadas<-as.integer(prod5$Laminas.procesadas)
str(prod5)
## 'data.frame':    2568 obs. of  5 variables:
##  $ CLIENTE           : chr  "VARROC" "VARROC" "VARROC" "DENSO" ...
##  $ PIEZAS.PROG.      : int  199 57 68 192 192 400 80 104 104 160 ...
##  $ TMO..MIN.         : int  15 10 10 15 15 30 15 15 15 20 ...
##  $ Laminas.procesadas: int  201 116 69 49 49 801 41 53 53 55 ...
##  $ TIEMPO.CALIDAD    : chr  "1" "1" "1" "1" ...

Carácter a hora

prod6<-prod5
prod6$TIEMPO.CALIDAD<-as.integer(prod6$TIEMPO.CALIDAD)
str(prod6)
## 'data.frame':    2568 obs. of  5 variables:
##  $ CLIENTE           : chr  "VARROC" "VARROC" "VARROC" "DENSO" ...
##  $ PIEZAS.PROG.      : int  199 57 68 192 192 400 80 104 104 160 ...
##  $ TMO..MIN.         : int  15 10 10 15 15 30 15 15 15 20 ...
##  $ Laminas.procesadas: int  201 116 69 49 49 801 41 53 53 55 ...
##  $ TIEMPO.CALIDAD    : int  1 1 1 1 1 1 1 1 1 1 ...

Técnica 5, VALORES FALTANTES

Al haber cambiado ya los valores a su forma correspondiente, observamos que existe una gran cantidad de valores faltantes (aprox. la mitad). Se ha optado por cambiar por mediana o por promedio dependiendo del caso.

¿Cuántos NA tengo por variables?

sapply(prod6,function(x) sum(is.na(x)))
##            CLIENTE       PIEZAS.PROG.          TMO..MIN. Laminas.procesadas 
##                  0                164                706                649 
##     TIEMPO.CALIDAD 
##                608

Después de verificar cada variable, se ha optado por cambiar TODOS los valores faltantes de las 4 variables por la mediana de cada uno.

prod7<-prod6
prod7$PIEZAS.PROG.[is.na(prod7$PIEZAS.PROG.)]<- median(prod7$PIEZAS.PROG., na.rm=TRUE)


prod8<-prod7
prod8$TMO..MIN.[is.na(prod8$TMO..MIN.)]<- median(prod8$TMO..MIN., na.rm=TRUE)


prod9<-prod8
prod9$Laminas.procesadas[is.na(prod9$Laminas.procesadas)]<- median(prod9$Laminas.procesadas, na.rm=TRUE)

prod10<-prod9
prod10$TIEMPO.CALIDAD[is.na(prod10$TIEMPO.CALIDAD)]<- median(prod10$TIEMPO.CALIDAD, na.rm=TRUE)

¿Cuántos NA tengo por variables?

sapply(prod10,function(x) sum(is.na(x)))
##            CLIENTE       PIEZAS.PROG.          TMO..MIN. Laminas.procesadas 
##                  0                  0                  0                  0 
##     TIEMPO.CALIDAD 
##                  0
prod_agosto<-prod10
write.csv(prod_agosto, file ="prod_agosto.csv", row.names = FALSE)

Base de datos de producción de SEPTIEMBRE

prod_sept<-read.csv("/Users/elenavela/Downloads/produccion_sept.csv")
summary(prod_sept)
##    CLIENTE            ID.FORM            PRODUCTO         PIEZAS.PROG.      
##  Length:1617        Length:1617        Length:1617        Length:1617       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##    TMO..MIN.       HR..FIN          ESTACION.ARRANQUE  Laminas.procesadas
##  Min.   : 10.0   Length:1617        Length:1617        Length:1617       
##  1st Qu.: 15.0   Class :character   Class :character   Class :character  
##  Median : 20.0   Mode  :character   Mode  :character   Mode  :character  
##  Mean   : 22.9                                                           
##  3rd Qu.: 25.0                                                           
##  Max.   :120.0                                                           
##  NA's   :485                                                             
##  INICIO.SEP.UP      fin.de.set.up      INICIO.de.PROCESO  FIN.de.PROCESO    
##  Length:1617        Length:1617        Length:1617        Length:1617       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##  TIEMPO.CALIDAD           X         
##  Length:1617        Min.   : 0.000  
##  Class :character   1st Qu.: 0.000  
##  Mode  :character   Median : 0.000  
##                     Mean   : 1.214  
##                     3rd Qu.: 0.750  
##                     Max.   :10.000  
##                     NA's   :1603

Técnica 1, REMOVER DATOS IRRELEVANTES
No todas las variables nos interesan, o aportan mucho, al análisis que se pretende hacer. Se ha definido que las variables con las que se desea quedar son las siguientes: cliente, piezas programadas, tiempo mínimo, láminas procesadas y tiempo de calidad.

Se eliminan columnas

prod_s2<-prod_sept
prod_s2<-subset(prod_s2,select=-c(ID.FORM,PRODUCTO,HR..FIN,ESTACION.ARRANQUE,INICIO.SEP.UP,INICIO.de.PROCESO,FIN.de.PROCESO,X,fin.de.set.up))
summary(prod_s2)
##    CLIENTE          PIEZAS.PROG.         TMO..MIN.     Laminas.procesadas
##  Length:1617        Length:1617        Min.   : 10.0   Length:1617       
##  Class :character   Class :character   1st Qu.: 15.0   Class :character  
##  Mode  :character   Mode  :character   Median : 20.0   Mode  :character  
##                                        Mean   : 22.9                     
##                                        3rd Qu.: 25.0                     
##                                        Max.   :120.0                     
##                                        NA's   :485                       
##  TIEMPO.CALIDAD    
##  Length:1617       
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 

Técnica 4, CONVERTIR TIPO DE DATOS

Para poder determinar los valores faltantes, es necesario convertir los valores de carácter a enteros/hora/fecha.

Carácter a entero

prod_s3<-prod_s2
prod_s3$PIEZAS.PROG.<-as.integer(prod_s3$PIEZAS.PROG.)
str(prod_s3)
## 'data.frame':    1617 obs. of  5 variables:
##  $ CLIENTE           : chr  "DENSO" "DENSO" "VARROC" "VARROC" ...
##  $ PIEZAS.PROG.      : int  240 240 100 98 10 10 200 10 10 60 ...
##  $ TMO..MIN.         : int  10 10 15 10 10 10 15 10 10 10 ...
##  $ Laminas.procesadas: chr  "48" "48" "202" "101" ...
##  $ TIEMPO.CALIDAD    : chr  "1" "1" "1" "1" ...
summary(prod_s3)
##    CLIENTE           PIEZAS.PROG.    TMO..MIN.     Laminas.procesadas
##  Length:1617        Min.   :   1   Min.   : 10.0   Length:1617       
##  Class :character   1st Qu.: 100   1st Qu.: 15.0   Class :character  
##  Mode  :character   Median : 120   Median : 20.0   Mode  :character  
##                     Mean   : 186   Mean   : 22.9                     
##                     3rd Qu.: 200   3rd Qu.: 25.0                     
##                     Max.   :2000   Max.   :120.0                     
##                     NA's   :66     NA's   :485                       
##  TIEMPO.CALIDAD    
##  Length:1617       
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 

Carácter a hora

prod_s4<-prod_s3
prod_s4$TMO..MIN.<-as.integer(prod_s4$TMO..MIN.)
str(prod_s4)
## 'data.frame':    1617 obs. of  5 variables:
##  $ CLIENTE           : chr  "DENSO" "DENSO" "VARROC" "VARROC" ...
##  $ PIEZAS.PROG.      : int  240 240 100 98 10 10 200 10 10 60 ...
##  $ TMO..MIN.         : int  10 10 15 10 10 10 15 10 10 10 ...
##  $ Laminas.procesadas: chr  "48" "48" "202" "101" ...
##  $ TIEMPO.CALIDAD    : chr  "1" "1" "1" "1" ...

Carácter a entero

prod_s5<-prod_s4
prod_s5$Laminas.procesadas<-as.integer(prod_s5$Laminas.procesadas)
str(prod_s5)
## 'data.frame':    1617 obs. of  5 variables:
##  $ CLIENTE           : chr  "DENSO" "DENSO" "VARROC" "VARROC" ...
##  $ PIEZAS.PROG.      : int  240 240 100 98 10 10 200 10 10 60 ...
##  $ TMO..MIN.         : int  10 10 15 10 10 10 15 10 10 10 ...
##  $ Laminas.procesadas: int  48 48 202 101 22 11 202 5 NA 25 ...
##  $ TIEMPO.CALIDAD    : chr  "1" "1" "1" "1" ...

Carácter a hora

prod_s6<-prod_s5
prod_s6$TIEMPO.CALIDAD<-as.integer(prod_s6$TIEMPO.CALIDAD)
str(prod_s6)
## 'data.frame':    1617 obs. of  5 variables:
##  $ CLIENTE           : chr  "DENSO" "DENSO" "VARROC" "VARROC" ...
##  $ PIEZAS.PROG.      : int  240 240 100 98 10 10 200 10 10 60 ...
##  $ TMO..MIN.         : int  10 10 15 10 10 10 15 10 10 10 ...
##  $ Laminas.procesadas: int  48 48 202 101 22 11 202 5 NA 25 ...
##  $ TIEMPO.CALIDAD    : int  1 1 1 1 1 1 1 1 1 1 ...

Técnica 5, VALORES FALTANTES

Al haber cambiado ya los valores a su forma correspondiente, observamos que existe una gran cantidad de valores faltantes (aprox. la mitad). Se ha optado por cambiar por mediana o por promedio dependiendo del caso.

¿Cuántos NA tengo por variables?

sapply(prod_s6,function(x) sum(is.na(x)))
##            CLIENTE       PIEZAS.PROG.          TMO..MIN. Laminas.procesadas 
##                  1                 66                485                320 
##     TIEMPO.CALIDAD 
##                522

En este caso, también,se toma ene cuenta la mediana para los valores faltantes.

prod_s7<-prod_s6
prod_s7$PIEZAS.PROG.[is.na(prod_s7$PIEZAS.PROG.)]<- median(prod_s7$PIEZAS.PROG., na.rm=TRUE)


prod_s8<-prod_s7
prod_s8$TMO..MIN.[is.na(prod_s8$TMO..MIN.)]<- median(prod_s8$TMO..MIN., na.rm=TRUE)


prod_s9<-prod_s8
prod_s9$Laminas.procesadas[is.na(prod_s9$Laminas.procesadas)]<- median(prod_s9$Laminas.procesadas, na.rm=TRUE)

prod_s10<-prod_s9
prod_s10$TIEMPO.CALIDAD[is.na(prod_s10$TIEMPO.CALIDAD)]<- median(prod_s10$TIEMPO.CALIDAD, na.rm=TRUE)

Ya no se tienen NAs.

prod_s11<-prod_s10
prod_s11<-na.omit(prod_s11)      
summary(prod_s11)
##    CLIENTE           PIEZAS.PROG.      TMO..MIN.      Laminas.procesadas
##  Length:1616        Min.   :   1.0   Min.   : 10.00   Min.   :  0.00    
##  Class :character   1st Qu.: 100.0   1st Qu.: 15.00   1st Qu.:  0.00    
##  Mode  :character   Median : 120.0   Median : 20.00   Median : 41.00    
##                     Mean   : 183.4   Mean   : 22.04   Mean   : 78.84    
##                     3rd Qu.: 200.0   3rd Qu.: 25.00   3rd Qu.:102.00    
##                     Max.   :2000.0   Max.   :120.00   Max.   :803.00    
##  TIEMPO.CALIDAD   
##  Min.   : 0.0000  
##  1st Qu.: 1.0000  
##  Median : 1.0000  
##  Mean   : 0.8874  
##  3rd Qu.: 1.0000  
##  Max.   :11.0000
sapply(prod_s11,function(x) sum(is.na(x)))
##            CLIENTE       PIEZAS.PROG.          TMO..MIN. Laminas.procesadas 
##                  0                  0                  0                  0 
##     TIEMPO.CALIDAD 
##                  0

Se exporta el mes de septiembre

prod_septiembre<-prod_s11
write.csv(prod_septiembre, file ="prod_septiembre.csv", row.names = FALSE)

Se continua con la base de datos de JULIO

prod_jul<-read.csv("/Users/elenavela/Downloads/produccion_julio.csv")
summary(prod_jul)
##    CLIENTE            ID.FORM            PRODUCTO         PIEZAS.PROG.      
##  Length:1035        Length:1035        Length:1035        Length:1035       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##    TMO..MIN.        HR..FIN          ESTACION.ARRANQUE  Laminas.procesadas
##  Min.   : 10.00   Length:1035        Length:1035        Length:1035       
##  1st Qu.: 15.00   Class :character   Class :character   Class :character  
##  Median : 20.00   Mode  :character   Mode  :character   Mode  :character  
##  Mean   : 23.48                                                           
##  3rd Qu.: 25.00                                                           
##  Max.   :150.00                                                           
##  NA's   :300                                                              
##  INICIO.SEP.UP      FIN.INICIO.DE.SEP.UP INICIO.de.PROCESO  FIN.de.PROCESO    
##  Length:1035        Length:1035          Length:1035        Length:1035       
##  Class :character   Class :character     Class :character   Class :character  
##  Mode  :character   Mode  :character     Mode  :character   Mode  :character  
##                                                                               
##                                                                               
##                                                                               
##                                                                               
##  TIEMPO.CALIDAD   
##  Min.   : 0.0000  
##  1st Qu.: 0.0000  
##  Median : 1.0000  
##  Mean   : 0.9507  
##  3rd Qu.: 1.0000  
##  Max.   :10.0000  
##  NA's   :183

Técnica 1, REMOVER DATOS IRRELEVANTES
No todas las variables nos interesan, o aportan mucho, al análisis que se pretende hacer. Se ha definido que las variables con las que se desea quedar son las siguientes: cliente, piezas programadas, tiempo mínimo, láminas procesadas y tiempo de calidad.

Se eliminan columnas

prod_j2<-prod_jul
prod_j2<-subset(prod_j2,select=-c(ID.FORM,PRODUCTO,HR..FIN,ESTACION.ARRANQUE,INICIO.SEP.UP,INICIO.de.PROCESO,FIN.de.PROCESO,FIN.INICIO.DE.SEP.UP))
summary(prod_j2)
##    CLIENTE          PIEZAS.PROG.         TMO..MIN.      Laminas.procesadas
##  Length:1035        Length:1035        Min.   : 10.00   Length:1035       
##  Class :character   Class :character   1st Qu.: 15.00   Class :character  
##  Mode  :character   Mode  :character   Median : 20.00   Mode  :character  
##                                        Mean   : 23.48                     
##                                        3rd Qu.: 25.00                     
##                                        Max.   :150.00                     
##                                        NA's   :300                        
##  TIEMPO.CALIDAD   
##  Min.   : 0.0000  
##  1st Qu.: 0.0000  
##  Median : 1.0000  
##  Mean   : 0.9507  
##  3rd Qu.: 1.0000  
##  Max.   :10.0000  
##  NA's   :183

Técnica 4, CONVERTIR TIPO DE DATOS

Para poder determinar los valores faltantes, es necesario convertir los valores de carácter a enteros/hora/fecha.

Carácter a entero

prod_j3<-prod_j2
prod_j3$PIEZAS.PROG.<-as.integer(prod_j3$PIEZAS.PROG.)
str(prod_j3)
## 'data.frame':    1035 obs. of  5 variables:
##  $ 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 ...
##  $ Laminas.procesadas: chr  "402" "134" "110" "100" ...
##  $ TIEMPO.CALIDAD    : int  1 1 1 1 1 1 1 1 1 2 ...
summary(prod_j3)
##    CLIENTE           PIEZAS.PROG.      TMO..MIN.      Laminas.procesadas
##  Length:1035        Min.   :   1.0   Min.   : 10.00   Length:1035       
##  Class :character   1st Qu.:  84.0   1st Qu.: 15.00   Class :character  
##  Mode  :character   Median : 119.5   Median : 20.00   Mode  :character  
##                     Mean   : 178.5   Mean   : 23.48                     
##                     3rd Qu.: 200.0   3rd Qu.: 25.00                     
##                     Max.   :1000.0   Max.   :150.00                     
##                     NA's   :59       NA's   :300                        
##  TIEMPO.CALIDAD   
##  Min.   : 0.0000  
##  1st Qu.: 0.0000  
##  Median : 1.0000  
##  Mean   : 0.9507  
##  3rd Qu.: 1.0000  
##  Max.   :10.0000  
##  NA's   :183

Carácter a hora

prod_j4<-prod_j3
prod_j4$TMO..MIN.<-as.integer(prod_j4$TMO..MIN.)
str(prod_j4)
## 'data.frame':    1035 obs. of  5 variables:
##  $ 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 ...
##  $ Laminas.procesadas: chr  "402" "134" "110" "100" ...
##  $ TIEMPO.CALIDAD    : int  1 1 1 1 1 1 1 1 1 2 ...

Carácter a entero

prod_j5<-prod_j4
prod_j5$Laminas.procesadas<-as.integer(prod_j5$Laminas.procesadas)
str(prod_j5)
## 'data.frame':    1035 obs. of  5 variables:
##  $ 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 ...
##  $ Laminas.procesadas: int  402 134 110 100 51 402 22 13 33 NA ...
##  $ TIEMPO.CALIDAD    : int  1 1 1 1 1 1 1 1 1 2 ...

Carácter a hora

prod_j6<-prod_j5
prod_j6$TIEMPO.CALIDAD<-as.integer(prod_j6$TIEMPO.CALIDAD)
str(prod_j6)
## 'data.frame':    1035 obs. of  5 variables:
##  $ 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 ...
##  $ Laminas.procesadas: int  402 134 110 100 51 402 22 13 33 NA ...
##  $ TIEMPO.CALIDAD    : int  1 1 1 1 1 1 1 1 1 2 ...

Técnica 5, VALORES FALTANTES

Al haber cambiado ya los valores a su forma correspondiente, observamos que existe una gran cantidad de valores faltantes (aprox. la mitad). Se ha optado por cambiar por mediana o por promedio dependiendo del caso.

¿Cuántos NA tengo por variables?

sapply(prod_j6,function(x) sum(is.na(x)))
##            CLIENTE       PIEZAS.PROG.          TMO..MIN. Laminas.procesadas 
##                  0                 59                300                220 
##     TIEMPO.CALIDAD 
##                183

Se toma el valor de la mediana para cambiar los valores faltantes

prod_j7<-prod_j6
prod_j7$PIEZAS.PROG.[is.na(prod_j7$PIEZAS.PROG.)]<- median(prod_j7$PIEZAS.PROG., na.rm=TRUE)


prod_j8<-prod_s7
prod_j8$TMO..MIN.[is.na(prod_j8$TMO..MIN.)]<- median(prod_j8$TMO..MIN., na.rm=TRUE)


prod_j9<-prod_s8
prod_j9$Laminas.procesadas[is.na(prod_j9$Laminas.procesadas)]<- median(prod_j9$Laminas.procesadas, na.rm=TRUE)

prod_j10<-prod_j9
prod_j10$TIEMPO.CALIDAD[is.na(prod_j10$TIEMPO.CALIDAD)]<- median(prod_j10$TIEMPO.CALIDAD, na.rm=TRUE)

Ya no se tienen valores faltantes.

prod_j11<-prod_j10
prod_j11<-na.omit(prod_j11)      
summary(prod_j11)
##    CLIENTE           PIEZAS.PROG.      TMO..MIN.      Laminas.procesadas
##  Length:1616        Min.   :   1.0   Min.   : 10.00   Min.   :  0.00    
##  Class :character   1st Qu.: 100.0   1st Qu.: 15.00   1st Qu.:  0.00    
##  Mode  :character   Median : 120.0   Median : 20.00   Median : 41.00    
##                     Mean   : 183.4   Mean   : 22.04   Mean   : 78.84    
##                     3rd Qu.: 200.0   3rd Qu.: 25.00   3rd Qu.:102.00    
##                     Max.   :2000.0   Max.   :120.00   Max.   :803.00    
##  TIEMPO.CALIDAD   
##  Min.   : 0.0000  
##  1st Qu.: 1.0000  
##  Median : 1.0000  
##  Mean   : 0.8874  
##  3rd Qu.: 1.0000  
##  Max.   :11.0000
sapply(prod_j11,function(x) sum(is.na(x)))
##            CLIENTE       PIEZAS.PROG.          TMO..MIN. Laminas.procesadas 
##                  0                  0                  0                  0 
##     TIEMPO.CALIDAD 
##                  0

Se exporta el último mes: JULIO

prod_julio<-prod_j11
write.csv(prod_julio, file ="prod_julio.csv", row.names = FALSE)

JUNTAMOS LAS TRERS BASES DE DATOS (julio, agosto, septiembre).

produccion_f<-read.csv("/Users/elenavela/prod_julio.csv")
summary(produccion_f)
##    CLIENTE           PIEZAS.PROG.      TMO..MIN.      Laminas.procesadas
##  Length:1616        Min.   :   1.0   Min.   : 10.00   Min.   :  0.00    
##  Class :character   1st Qu.: 100.0   1st Qu.: 15.00   1st Qu.:  0.00    
##  Mode  :character   Median : 120.0   Median : 20.00   Median : 41.00    
##                     Mean   : 183.4   Mean   : 22.04   Mean   : 78.84    
##                     3rd Qu.: 200.0   3rd Qu.: 25.00   3rd Qu.:102.00    
##                     Max.   :2000.0   Max.   :120.00   Max.   :803.00    
##  TIEMPO.CALIDAD   
##  Min.   : 0.0000  
##  1st Qu.: 1.0000  
##  Median : 1.0000  
##  Mean   : 0.8874  
##  3rd Qu.: 1.0000  
##  Max.   :11.0000
sapply(produccion_f,function(x) sum(is.na(x)))
##            CLIENTE       PIEZAS.PROG.          TMO..MIN. Laminas.procesadas 
##                  0                  0                  0                  0 
##     TIEMPO.CALIDAD 
##                  0
str(produccion_f)
## 'data.frame':    1616 obs. of  5 variables:
##  $ CLIENTE           : chr  "DENSO" "DENSO" "VARROC" "VARROC" ...
##  $ PIEZAS.PROG.      : int  240 240 100 98 10 10 200 10 10 60 ...
##  $ TMO..MIN.         : int  10 10 15 10 10 10 15 10 10 10 ...
##  $ Laminas.procesadas: int  48 48 202 101 22 11 202 5 41 25 ...
##  $ TIEMPO.CALIDAD    : int  1 1 1 1 1 1 1 1 1 1 ...

Se cambia el nombre de las columnas

names (produccion_f) = c("client", "piezas_prog", "tmpo_min", "laminas_proc", "tiempo_calidad")
names (produccion_f)
## [1] "client"         "piezas_prog"    "tmpo_min"       "laminas_proc"  
## [5] "tiempo_calidad"

Se exporta la versión final

write.csv(produccion_f, file ="produccion_final.csv", row.names = FALSE)

FORM- Merma

merma<-read.csv("/Users/elenavela/Downloads/merma.csv")
summary(merma)
##     Fecha               Mes                Kilos     
##  Length:50          Length:50          Min.   : 790  
##  Class :character   Class :character   1st Qu.:3178  
##  Mode  :character   Mode  :character   Median :3925  
##                                        Mean   :3709  
##                                        3rd Qu.:4232  
##                                        Max.   :6140

Técnica 1, REMOVER DATOS IRRELEVANTES
Desde el excel se han eliminado las filas de “total mes”.

Técnica 4, CONVERTIR TIPO DE DATOS

Para poder determinar los valores faltantes, es necesario convertir los valores de carácter a enteros/hora/fecha.

Carácter a entero

merma2<-merma
merma2$Fecha<-as.Date(merma2$Fecha,format="%d/%m/%y")  
str(merma2)
## 'data.frame':    50 obs. of  3 variables:
##  $ Fecha: Date, 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 ...
summary(merma2)
##      Fecha                Mes                Kilos     
##  Min.   :2022-01-11   Length:50          Min.   : 790  
##  1st Qu.:2022-03-12   Class :character   1st Qu.:3178  
##  Median :2022-05-24   Mode  :character   Median :3925  
##  Mean   :2022-05-25                      Mean   :3709  
##  3rd Qu.:2022-08-10                      3rd Qu.:4232  
##  Max.   :2022-09-21                      Max.   :6140

¿Cuántos NA tengo por variable?

sapply(merma2,function(x) sum(is.na(x)))
## Fecha   Mes Kilos 
##     0     0     0

No existen valores faltantes por variables.

Exportar nueva base de datos (limpia)

merma_final <-merma2
write.csv(merma_final, file ="mermaa_final2.csv", row.names = FALSE)

FORM- Scrap

Importar base de datos

scrap<-read.csv("/Users/elenavela/Downloads/scrap.csv")

Técnica 1, REMOVER DATOS IRRELEVANTES
No todas las variables nos interesan, o aportan mucho, al análisis que se pretende hacer. Se ha definido que las variables con las que se desea quedar son las siguientes: fecha, cantidad, y ubicación de origen.

Se eliminan columnas

scrap2<-scrap
scrap2<-subset(scrap2,select=-c(Referencia,Hora,Producto,Unidad.de.medida,Ubicación.de.desecho,Estado))

summary(scrap2)
##     Fecha              Cantidad      Ubicación.de.origen
##  Length:250         Min.   : 0.000   Length:250         
##  Class :character   1st Qu.: 1.000   Class :character   
##  Mode  :character   Median : 2.000   Mode  :character   
##                     Mean   : 6.696                      
##                     3rd Qu.: 7.000                      
##                     Max.   :96.000

Técnica 4, CONVERTIR TIPO DE DATOS

Para poder determinar los valores faltantes, es necesario convertir los valores de carácter a enteros/hora/fecha.

Carácter a fecha

scrap3<-scrap2
scrap3$Fecha<-as.Date(scrap3$Fecha,format="%d/%m/%Y")  
str(scrap3)
## 'data.frame':    250 obs. of  3 variables:
##  $ Fecha              : Date, format: "2022-08-31" "2022-08-31" ...
##  $ Cantidad           : num  2 1 1 31 1 1 1 9 2 1 ...
##  $ Ubicación.de.origen: chr  "SAB/Calidad/Entrega de PT" "SAB/Calidad/Entrega de PT" "SAB/Calidad/Entrega de PT" "SAB/Pre-Production" ...
summary(scrap3)
##      Fecha               Cantidad      Ubicación.de.origen
##  Min.   :2022-08-01   Min.   : 0.000   Length:250         
##  1st Qu.:2022-08-11   1st Qu.: 1.000   Class :character   
##  Median :2022-08-19   Median : 2.000   Mode  :character   
##  Mean   :2022-08-17   Mean   : 6.696                      
##  3rd Qu.:2022-08-25   3rd Qu.: 7.000                      
##  Max.   :2022-08-31   Max.   :96.000

¿Cuántos NA tengo por variable?

sapply(scrap3,function(x) sum(is.na(x)))
##               Fecha            Cantidad Ubicación.de.origen 
##                   0                   0                   0

Se cambian los nombres de las columnas

scrap4<-scrap3
names (scrap4) = c("fecha", "cantidad", "ubi_origen")
names (scrap4)
## [1] "fecha"      "cantidad"   "ubi_origen"

Exportar nueva base de datos (limpia)

scrap_finalya <-scrap4
write.csv(scrap_finalya, file ="scrap_finalya2.csv", row.names = FALSE)

Reflexión

¿Cómo te permitió la aplicación de dichas técnicas una mejor comprensión de las bases de datos?

Estas técnicas resultan de utilidad al poder tener datos de calidad y lo más reales posibles. Debido a que al final se desea poder comprender las tendencias y lo que está pasando en la organización en diversos temas, como lo son: sus actuales y pasados colaboradores, su producción, su delivery performance y plan, su merma y su scrap. Por lo tanto, lo ideal es trabajar con los datos más reales y ciertos posibles.
Es por esto que al llevar a cabo esta transformación y limpieza, podremos llevar a cabo un buen análisis y de buen uso para nuestro socio formador: FORM.

Comprensión de las bases de datos

FORM- Recursos Humanos (Colaboradores y Bajas).

bd_colab<-read.csv("/Users/elenavela/colab_FORM_limpia.csv")
bd_bajas<-read.csv("/Users/elenavela/bajas_FORM_limpia.csv")

¿Cuántas variables y cuantos registros tiene la base de datos?

str(bd_colab)
## 'data.frame':    104 obs. of  9 variables:
##  $ edad          : int  32 38 38 37 38 60 56 46 59 43 ...
##  $ genero        : chr  "FEMENINO" "MASCULINO" "FEMENINO" "MASCULINO" ...
##  $ alta          : int  2013 2018 2015 2016 2020 2020 2022 2022 2022 2022 ...
##  $ puesto        : chr  "SUPERVISORA" "MANTENIMIENTO" "COSTURERA" "AYUDANTE GENERAL" ...
##  $ salario_diario: num  337 280 260 241 241 ...
##  $ lugar.nacim.  : chr  "" "" "" "" ...
##  $ mpio          : chr  "APODACA" "APODACA" "APODACA" "APODACA" ...
##  $ estado        : chr  "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" ...
##  $ civil         : chr  "Casado" "Soltero" "Casado" "Casado" ...
str(bd_bajas)
## 'data.frame':    233 obs. of  9 variables:
##  $ edad          : int  32 36 23 21 29 46 29 31 50 19 ...
##  $ genero        : chr  "M" "F" "F" "F" ...
##  $ alta          : chr  "2020-03-09" "2021-11-09" "2021-11-10" "2021-11-10" ...
##  $ motivo_baja   : chr  "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" ...
##  $ duracion      : num  628 60 59 59 51 37 37 31 18 224 ...
##  $ puesto        : chr  "DISEÑO" "AYUDANTE GENERAL" "AYUDANTE GENERAL" "AYUDANTE GENERAL" ...
##  $ salario_diario: num  500 152 152 152 152 ...
##  $ estado        : chr  "Nuevo León" "Nuevo León" "Nuevo León" "Nuevo León" ...
##  $ civil         : chr  "Soltero" "Unión libre" "Matrimonio" "Soltero" ...

Se obtienen 104 registros (u observaciones) en colaboradores y 9 variables que se consideran como relevantes.
Se obtienen 232 registros (u observaciones) en bajas y 9 variables que se consideran como relevantes.

Clasificación de cada variable en cualitativa, cuantitativa discreta, o cuantitativa continua.

De colaboradores

VariableColab Type
Edad Cuantitativa discreta
Género Cualitativa
Año de Alta Cuantitativa discreta
Puesto Cualitativa
Salario diario Cuantitativa continua
Lugar nacimiento Cualitativa
Municipio Cualitativa
Estado Cualitativa
Estado civil Cualitativa

De bajas

VariableBajas Type
Edad Cuantitativa discreta
Género Cualitativa
Año de Alta Cuantitativa discreta
Motivo Baja Cualitativa
Duración Cuantitativa discreta
Puesto Cualitativa
Salario diario Cuantitativa continua
Estado Cualitativa
Estado civil Cualitativa

Escala de medición de cada variable.

VariableColab Medicion
Edad Años
Género No aplica
Año de Alta Años
Puesto No aplica
Salario diario Pesos mexicanos
Lugar nacimiento No aplica
Municipio No aplica
Estado No aplica
Estado civil No aplica
VariableBajas Medicion
Edad Años
Género No aplica
Año de Alta Años
Motivo Baja No aplica
Duración Días
Puesto No aplica
Salario diario Pesos mexicanos
Estado No aplica
Estado civil No aplica

FORM- Delivery Plan.

bd_delplan<-read.csv("/Users/elenavela/deliveryplan_final2.csv")

¿Cuántas variables y cuantos registros tiene la base de datos?

str(bd_delplan)
## 'data.frame':    228 obs. of  4 variables:
##  $ ID_fecha: int  1 1 1 1 1 1 1 1 1 1 ...
##  $ fecha   : chr  "2022-01-31" "2022-01-31" "2022-01-31" "2022-01-31" ...
##  $ cliente : chr  "STB3" "STB 1" "YF RAMOS" "INOAC POLYTEC" ...
##  $ pedidos : int  481 0 227 0 400 328 393 0 8975 449 ...

Esta base de datos cuenta con 228 observaciones y 4 variables relevantes.

Clasificación de cada variable en cualitativa, cuantitativa discreta, o cuantitativa continua.

VariablePlan Type
ID_fecha Cuantitativa discreta
Fecha Cuantitativa discreta
Cliente Cualitativa
Pedidos Cuantitativa discreta

Escala de medición de cada variable.

VariablePlan Medicion
ID_fecha Número de mes
Fecha Día/Mes/Año
Cliente No aplica
Pedidos Número o cantidad de pedidos

FORM- Delivery Performance.

bd_delperf<-read.csv("/Users/elenavela/deliveryperformance_finalya.csv")

¿Cuántas variables y cuantos registros tiene la base de datos?

str(bd_delperf)
## 'data.frame':    104 obs. of  3 variables:
##  $ cliente: chr  "PRINTEL " "MAHLE" "MAHLE" "MAHLE" ...
##  $ fecha  : chr  "jul-21" "jul-21" "jul-21" "jul-21" ...
##  $ dif    : num  0 0.55 1 1.1 0 0 0 0 1 0.51 ...

Esta base de datos cuenta con 104 observaciones y 3 variables relevantes.

Clasificación de cada variable en cualitativa, cuantitativa discreta, o cuantitativa continua.

VariablePerf Type
Cliente Cualitativa
Fecha Cuantitativa discreta
Diferencia Cuantitativa continua

Escala de medición de cada variable.

VariablePerf Medicion
Cliente No aplica
Fecha Mes-Año
Diferencia Minutos

FORM- Producción

bd_prod<-read.csv("/Users/elenavela/produccion_final.csv")

¿Cuántas variables y cuantos registros tiene la base de datos?

str(bd_prod)
## 'data.frame':    1616 obs. of  5 variables:
##  $ client        : chr  "DENSO" "DENSO" "VARROC" "VARROC" ...
##  $ piezas_prog   : int  240 240 100 98 10 10 200 10 10 60 ...
##  $ tmpo_min      : int  10 10 15 10 10 10 15 10 10 10 ...
##  $ laminas_proc  : int  48 48 202 101 22 11 202 5 41 25 ...
##  $ tiempo_calidad: int  1 1 1 1 1 1 1 1 1 1 ...

Esta base de datos cuenta con 1,616 observaciones y 5 variables relevantes.

Clasificación de cada variable en cualitativa, cuantitativa discreta, o cuantitativa continua.

VariableProd Type
Cliente Cualitativa
Piezas Programadas Cuantitativa discreta
Tiempo mínimo Cuantitativa continua
Láminas procesadas Cuantitativa discreta
Tiempo de calidad Cuantitativa continua

Escala de medición de cada variable.

VariableProd Medicion
Cliente No aplica
Piezas Programadas Número o cantidad de piezas programadas
Tiempo mínimo Minutos
Láminas procesadas Número o cantidad de láminas procesadas
Tiempo de calidad Mniutos

FORM- Merma

bd_merma_f<-read.csv("/Users/elenavela/mermaa_final2.csv")

¿Cuántas variables y cuantos registros tiene la base de datos?

str(bd_merma_f)
## 'data.frame':    50 obs. of  3 variables:
##  $ Fecha: chr  "2022-01-11" "2022-01-11" "2022-01-22" "2022-01-22" ...
##  $ Mes  : chr  "ENERO" "ENERO" "ENERO" "ENERO" ...
##  $ Kilos: int  5080 3810 2990 2680 3650 4380 3870 3590 3410 3930 ...

Esta base de datos cuenta con 50 observaciones y 3 variables relevantes.

Clasificación de cada variable en cualitativa, cuantitativa discreta, o cuantitativa continua.

VariableMerma Type
Fecha Cuantitativa continua
Mes Cualitativa
Kilos Cuantitativa continua

Escala de medición de cada variable.

VariableMerma Medicion
Fecha Día/Mes/Año
Mes No aplica
Kilos Kilos/Kgs

FORM- Scrap

bd_scrapp<-scrap_finalya

¿Cuántas variables y cuantos registros tiene la base de datos?

str(bd_scrapp)
## 'data.frame':    250 obs. of  3 variables:
##  $ fecha     : Date, format: "2022-08-31" "2022-08-31" ...
##  $ 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" ...
summary(scrap3)
##      Fecha               Cantidad      Ubicación.de.origen
##  Min.   :2022-08-01   Min.   : 0.000   Length:250         
##  1st Qu.:2022-08-11   1st Qu.: 1.000   Class :character   
##  Median :2022-08-19   Median : 2.000   Mode  :character   
##  Mean   :2022-08-17   Mean   : 6.696                      
##  3rd Qu.:2022-08-25   3rd Qu.: 7.000                      
##  Max.   :2022-08-31   Max.   :96.000

Esta base de datos cuenta con 250 observaciones y 3 variables relevantes.

Clasificación de cada variable en cualitativa, cuantitativa discreta, o cuantitativa continua.

VariableScrap Type
Fecha Cuantitativa continua
Cantidad Cuantitativa discreta
Ubicación de origen Cualitativa

Escala de medición de cada variable.

VariableScrap Medicion
Fecha Día/Mes/Año
Cantidad Número o cantidad de merma
Ubicación de origen No aplica

Análisis estadístico descriptivo

Librerías requeridas

library(foreign)
library(dplyr)        # data manipulation 
library(forcats)      # to work with categorical variables
library(ggplot2)      # data visualization 
library(janitor)      # data exploration and cleaning 
#install.packages("psych")
library(corrplot)     # correlation plots
library(lmtest)       # diagnostic checks - linear regression analysis 
library(car)          # diagnostic checks - linear regression analysis
library(dplyr)
library(janitor)

FORM- Recursos Humanos (Colaboradores y Bajas).

COLABORADORES

Se generan las tablas de frecuencia de las variables CUALITATIVAS.

genero<-table(bd_colab$genero)
knitr::kable(genero)
Var1 Freq
FEMENINO 59
MASCULINO 45

Observamos que en la base de datos limpia de los colaboradores actuales, existen más mujeres que hombres.

puesto<-table(bd_colab$puesto)
knitr::kable(puesto)
Var1 Freq
AYUDANTE DE MANTENIMIENTO 1
Ayudante general 1
AYUDANTE GENERAL 61
CHOFER 3
CHOFER GESTOR 1
COSTURERA 10
CUSTOMER SERVICE INF 1
ENFERMERA 1
GESTOR 1
GUARDIA DE SEGURIDAD 1
INSPECTOR DE CALIDAD 2
LIDER 1
LIMPIEZA 1
MANTENIMIENTO 1
MONTACARGUISTA 1
MOZO 1
OP. FLEXO-RANURADORA-REFILADORA 1
OPERADOR SIERRA 1
PINTOR 1
RECIBO 1
RESIDENTE 4
SOLDADOR 5
Supervisor de M√°quin 1
Supervisor de pegado 1
SUPERVISORA 1

Lo que más observamos en la base de colaboradores actuales, es el puesto de “Ayudante General”, seguido por “Costureras”.

municipio<-table(bd_colab$mpio)
knitr::kable(municipio)
Var1 Freq
APODACA 67
CANADA BLANCA 1
GUADALUPE 5
JUAREZ 9
MONTERREY 3
PESQUERIA 9
RAMOS ARIZPE 3
SALTILLO 5
SAN NICOLAS DE LOS G 2

Observamos que la gran parte de los colaboradores habitan en Apodaca, Nuevo León.

estado<-table(bd_colab$estado)
knitr::kable(estado)
Var1 Freq
Coahuila 9
Nuevo Leon 95

Así como la gran parte vienen de Nuevo León, y muy poco son foráneos.

civil<-table(bd_colab$civil)
knitr::kable(civil)
Var1 Freq
Casado 39
Divorciado 3
Soltero 42
Union libre 20

Actualmente, existen más personas solteras, siguiendo de casadas, por personas ene unión libre, y muy pocos divorciados.

Tablas cruzadas

Género con estado.

cruzada2<-table(bd_colab$estado,bd_colab$genero)
knitr::kable(cruzada2)
FEMENINO MASCULINO
Coahuila 1 8
Nuevo Leon 58 37

Con esta tabla cruzada observamos que la gran mayoría de los foráneos son hombres.

Gráficos de datos cualitativos y cuantitativos

Datos cualitativos

barplot(prop.table(table(bd_colab$civil)),col=c("lightyellow","lightblue","pink","lightgreen"),main="Estado Civil de Colaboradores FORM", ylab ="Frecuencias",las=1)

Como fue descrito anteriormente, aquí podemos ver de manera gráfica que casi la mitad son o solteros o casados.

porcentajes <- as.numeric(round(((prop.table(table(bd_colab$genero)))*100),2))
etiquetas <- c("Mujeres", "Hombres")
etiquetas <- paste(etiquetas, porcentajes)
etiquetas <- paste(etiquetas, "%", sep = "")


pie(porcentajes,etiquetas,col=c("pink","lightblue"),main="Género", ylab ="Frecuencias",las=1)

En esta gráfica de pastel, podemos notar que más del 50% de los que trabajan en FORM actualmente son mujeres.

Datos cuantitativos

hist((bd_colab$salario_diario),col=c("darkred"),main="Salario Diario de Colaboradores de FORM",xlab="Salario en pesos mx")

En cuanto al salario diario que reciben los colaboradores de FORM, la gran mayoría no sobrepasa los 200 pesos, y la gran parte de aquellos que si no sobrepasan los 300; existe un caso que está muy por encima de todos, entre los 400 y 500 diarios.

hist((bd_colab$alta),col=c("lightblue"),main="Fecha de Alta de Colaboradores de FORM",xlab="Año")

Siguiendo con el año de alta de los actuales colaboradores, observamos que existe muy poca antigüedad, al haber ingresado la gran mayoría durante los últimos dos años (2020-2022). Igualmente, la persona que más tiempo lleva en FORM, tiene una antigüedad de 12 años, esto tomando en cuenta la limpieza de datos.

Gráficos de dispersión

plot(bd_colab$alta, bd_colab$salario_diario, main = "Fecha de ingreso con salario diario",
     xlab = "Fecha de ingreso", ylab = "Salario",col=("darkgreen"),
     pch = 19, frame = FALSE)

Aquí también observamos lo descrito anteriormente, la mayoría no gana más de $200. Sin embargo, en este caso, vemos que no hay persona que haya ingresado desde el 2019 que gane más de $250. Igualmente, notamos que la persona que más gana no es aquella con mayor antigüedad, sino alguien que entró en el 2013. Las personas que mayor antigüedad ganan lo mismo que muchas de recién ingreso.

plot(bd_colab$edad, bd_colab$salario_diario, main = "Edad",
     xlab = "Edad", ylab = "Salario",col=("darkgreen"),
     pch = 19, frame = FALSE)

En esta gráfica notamos que no existe discriminación de salario por la edad, puesto que la mayoría de las personas, desde los 18 hasta más de los 60, ganan menos de 200 pesos diarios. La persona que más gana parece tener entre 30 y 35 años.

boxplot(bd_colab$edad , vertical = TRUE,col=("darkblue"))

Mediante el boxplot conocemos que la media es apróximadamente 32 años de edad, y que existe una notoria dispersión. Igual vemos que el rango de edad va desde los 18 hasta más de los 60.

BAJAS

#install.packages("epiDisplay")
library(epiDisplay)
bd_bajas<-bd_bajas

Tabla de frecuencia y gráfica de datos CUALITATIVOS: Puesto

puesto<-table(bd_bajas$puesto)
knitr::kable(puesto)
Var1 Freq
ANALISTA DE NOMINAS /AUX DE R.H. 1
AUXILIAR DE EMBARQUES 3
AY. GENERAL 4
AY.GENERAL (MATERIALES) 1
AYUD.EMBARQUES 1
AYUDANTE DE EMBARQUES 3
AYUDANTE DE MTTO 1
AYUDANTE DE SOLDADOR 1
AYUDANTE GENERAL 171
AYUDANTE GENERAL DE EMBARQUES 1
CHOFER 1
CORTADOR 1
COSTURERA 10
COSTURERO 1
DISEÑO 1
ENCARGADA DE CALIDAD 1
FACTURACION 1
GUARDIA DE SEGURIDAD 2
INSPECTOR CALIDAD 1
INSPECTOR DE CALIDAD 2
INSPECTORA DE CALIDAD 1
LIMPIEZA 1
MARCADORA 1
MATERIALISTA 2
MONTACARGUISTA 5
PRACTICANTE DE MTTO 1
RESIDENTE 2
Residente Yanfeng 1
SERVICIO AL CLIENTE 1
SOLDADOR 10

En esta observamos que el puesto que más han dejado en FORM es el puesto de ayudante general, por mucho. Esto puede ser debido a que es un puesto muy común y utilizado (aún en los colaboradores actuales es el que más personas tiene).

Tabla de frecuencia y gráfica de datos CUALITATIVOS: Motivo de bajas

motivo<-table(bd_bajas$motivo_baja)
knitr::kable(motivo)
Var1 Freq
ABANDONO 1
BAJA POR FALTAS 139
JUBILACION 1
RENUNCIA VOLUNTARIA 84
TERMINO DE CONTRATO 8

En el presente análisis notamos cuales son las principales razones, y las que menos, por las que los empleados de Form salen. Principalmente vemos que es por la BAJA POR FALTAS, siguiendo por RENUNCIA VOLUNTARIA. Las que menos vemos son por JUBILACIÓN y ABANDONO.

porcentajes <- as.numeric(round(((prop.table(table(bd_bajas$motivo_baja)))*100),2))
etiquetas <- c("Abandono", "Baja por faltas","Jubilación","Renuncia voluntaria","Termino de contrato")
etiquetas <- paste(etiquetas, porcentajes)
etiquetas <- paste(etiquetas, "%", sep = "")

pie(porcentajes,etiquetas,col=c("orange","lightblue","green","pink","yellow"),main="Motivo de bajas", ylab ="Frecuencias",las=1)

Lo mismo que ha sido descrito en la tabla puede ser comprendido a partir de la gráfica superior; sin embargo, esta nos demuesta a través de porcentajes la proporción de cada razón.

civil<-table(bd_bajas$civil)
knitr::kable(civil)
Var1 Freq
1
Divorcio 3
Matrimonio 63
Soltero 107
Unión libre 59
tab1(bd_bajas$civil,sort.group = FALSE,graph=TRUE,col=c("orange","lightblue","lightgreen","lightyellow"),main="Distribución de pasados colaboradores de FORM, por estado civil")

## bd_bajas$civil : 
##             Frequency Percent Cum. percent
##                     1     0.4          0.4
## Divorcio            3     1.3          1.7
## Matrimonio         63    27.0         28.8
## Soltero           107    45.9         74.7
## Unión libre        59    25.3        100.0
##   Total           233   100.0        100.0

Comprendemos que la mayor cantidad de las personas que salen son solteras, y las personas que están en matrimonio o en unión libre tienen casi la misma tendencia a salir.

estado<-table(bd_bajas$estado)
knitr::kable(estado)
Var1 Freq
Coahuila 8
Nuevo León 225
tab1(bd_bajas$estado,sort.group = FALSE,graph=TRUE,col=c("orange","lightblue"),main="Distribución de pasados colaboradores de FORM, por estado")

## bd_bajas$estado : 
##            Frequency Percent Cum. percent
## Coahuila           8     3.4          3.4
## Nuevo León       225    96.6        100.0
##   Total          233   100.0        100.0

La gran parte de los que salen son de Nuevo León.

hist((bd_bajas$edad),col=c("orange"),main="Distribución de pasados colaboradores de FORM, por edad",xlab="Años",ylab="Frecuencia")

De las personas que salen de la empresa, la edad lleva un sesgo positivo, la mayoría de los que salen se concentran en las edades más jóvenes, aproximadamente entre los 20 y los 30.

hist((bd_bajas$duracion),col=c("lightyellow"),main="Distribución de pasados colaboradores de FORM, por días de duración",xlab="Días",ylab="Frecuencia")

En cuanto a los días que duran las personas que bajan, observemaos que muchos no duran ni siguiera el año, al estar la gran mayoría en menos de los 250 días dentro de Form. La antigüedad es poca.

hist((bd_bajas$salario_diario),col=c("darkblue"),main="Distribución de pasados colaboradores de FORM, por su salario diario",xlab="Pesos",ylab="Frecuencia")

El salario no ve muchas diferencias, al estar la gran parte entre los 150 y los 200 pesos diarios.

porcentajes <- as.numeric(round(((prop.table(table(bd_bajas$genero)))*100),2))
etiquetas <- c("Mujeres", "Hombres")
etiquetas <- paste(etiquetas, porcentajes)
etiquetas <- paste(etiquetas, "%", sep = "")


pie(porcentajes,etiquetas,col=c("pink","lightblue"),main="Género de pasados colaboradores de FORM", ylab ="Frecuencias",las=1)

Observamos que existe una mayor tendencia en que las personas que salgan sean mujeres.

plot(bd_bajas$edad,bd_bajas$duracion, main = "Días de duración y edad, de antiguos colaboradores", xlab="Edad", ylab="Duración",
     pch = 19, frame = FALSE,col=c("orange"))

En cuanto a la relación y la dispersión entre los días de duración y la edad, vemos que la edad no influye mucho en los días que duran, puesto que vemos casi la misma frecuencia de duración entre las diferentes edades. Existeen algunos casos que se salen de los “común” al haber durado mucho.

FORM- Delivery Plan.

Para esta base de datos no se generan tablas cruzadas por el tipo de variables que se tienen.

Gráficos cualitativos y cuantitativos

library(plyr)
## ------------------------------------------------------------------------------
## You have loaded plyr after dplyr - this is likely to cause problems.
## If you need functions from both plyr and dplyr, please load plyr first, then dplyr:
## library(plyr); library(dplyr)
## ------------------------------------------------------------------------------
## 
## Attaching package: 'plyr'
## The following objects are masked from 'package:dplyr':
## 
##     arrange, count, desc, failwith, id, mutate, rename, summarise,
##     summarize
boxplot(bd_delplan$pedidos, main ="Total Ordenes")

Notamos que en el total de ordenes, hay unas cuantas que sobrepasan los 50,000, mientras que muchas se quedan cerca del 0.

hist((bd_delplan$pedidos),col=c("pink"), main="Ordenes", las=1,xlab="Pedidos")

Lo descrito anteriormente puede verse en este histograma, al estar la mayoría de los pedidos entre 0 y 5,000 pedidos, con muy poco pasando los 10,000.

Gráficos de dispersión

library(tibble)
tibble(bd_delplan)
## # A tibble: 228 × 4
##    ID_fecha fecha      cliente       pedidos
##       <int> <chr>      <chr>           <int>
##  1        1 2022-01-31 STB3              481
##  2        1 2022-01-31 STB 1               0
##  3        1 2022-01-31 YF RAMOS          227
##  4        1 2022-01-31 INOAC POLYTEC       0
##  5        1 2022-01-31 MERIDIAN          400
##  6        1 2022-01-31 YANFENG sm        328
##  7        1 2022-01-31 YFTO              393
##  8        1 2022-01-31 YF QRO              0
##  9        1 2022-01-31 TRMX             8975
## 10        1 2022-01-31 DENSO             449
## # … with 218 more rows
plot(bd_delplan$ID_fecha, bd_delplan$pedidos, main = "Pedidos por fecha",
     xlab = "Mes", ylab = "Ordenes", col=("darkred"),
     pch = 19, frame = TRUE)

Los meses estando por número, podemos ver que el mes que recibió el pedido máss grande fue el octavo (agosto), y el que menos pedidos en total ha recibido es diciembre, siguiendo de noviembre.

FORM- Delivery Performance

Tabla de frecuencia
Se generan las tablas de frecuencias de las variables CUALITATIVAS.

cliente<-table(bd_delperf$cliente)
knitr::kable(cliente)
Var1 Freq
MAGNA 13
MAHLE 39
PRINTEL 13
VARROC 39

Aqui observamos que a los clientes que más se les entregó fue a Mahle y a Varroc.

Gráfica de dispersión

boxplot(bd_delperf$dif, vertical = TRUE,col=("darkred"))

Vemos que la mayoría de los datos se encuentran entre el 0.0 y 0.5 de diferencia (o delay performance), con uno en 1.5, otro en 2.0, y otro sobrepasando los 3.

FORM- Producción

#install.packages("epiDisplay")
library(epiDisplay)

Tabla de frecuencia y gráfica de datos CUANTITATIVOS: Tiempo de Calidad

tiempo_de_calidad<-table(bd_prod$tiempo_calidad)
knitr::kable(tiempo_de_calidad)
Var1 Freq
0 279
1 1310
2 9
3 3
4 3
5 3
6 4
8 2
9 1
10 1
11 1
tab1(bd_prod$tiempo_calidad,sort.group = FALSE,graph=TRUE,main="Distribución del tiempo de calidad")

## bd_prod$tiempo_calidad : 
##         Frequency Percent Cum. percent
## 0             279    17.3         17.3
## 1            1310    81.1         98.3
## 2               9     0.6         98.9
## 3               3     0.2         99.1
## 4               3     0.2         99.3
## 5               3     0.2         99.4
## 6               4     0.2         99.7
## 8               2     0.1         99.8
## 9               1     0.1         99.9
## 10              1     0.1         99.9
## 11              1     0.1        100.0
##   Total      1616   100.0        100.0

Primeramente, se analiza el tiempo de calidad. A partir de la tabla de frecuencia y de su gráfica, observamos que la gran parte de la producción analizada toma un valor de tiempo de calidad de un minuto, poco siendo inferior a 1, o 0.

Gráfica de datos CUANTITATIVOS: Tiempo Mínimo

hist((bd_prod$tmpo_min),col=c("orange"),main="Distribución del Tiempo Mínimo",xlab="Minutos")

Siguientemente, observamos el tiempo mínimo (por minutos) de la producción. La gran parte se encuentrra entre los 0 y los 30 minutos.

Gráfica de datos CUANTITATIVOS: Piezas Programadas

hist((bd_prod$piezas_prog),col=c("yellow"),main="Piezas Programadas",xlab="Unidades")

Continuando con las piezas programadas, vemos que la gran parte de las piezas programadas rondan entre 0 y 200 unidades.

Gráfica de datos CUANTITATIVOS: Láminas Procesadas

hist((bd_prod$laminas_proc),col=c("darkgreen"),main="Láminas Procesadas",xlab="Unidades")

Con las láminas procesadas, vemos que la mayor frecuencia es está entre el 0 y 100, con un importante número también visto entre 100 y 300.

Gráfica de dispersión: Piezas programadas

boxplot(bd_prod$piezas_prog,col=c("orange"))

Tal como lo vimos en el histograma, vemos que la mayor concentración de datos está entre el 100 y 200. No resulta demasiado disperso, sin embargo hay un dato que se sale de lo “normal”, puesto que lo encontramos arriba de los miles.

FORM- Merma

Tabla de frecuencia y gráfica de datos CUALITATIVOS: Mes

knitr::kable(table(bd_merma_f$Mes))
Var1 Freq
ABRIL 5
AGOSTO 11
ENERO 4
FEBRERO 6
JULIO 5
JUNIO 4
MARZO 6
MAYO 5
SEPTIEMBRE 4
tab1(bd_merma_f$Mes,sort.group = FALSE,graph=TRUE,main="Distribución de la merma por mes")

## bd_merma_f$Mes : 
##            Frequency Percent Cum. percent
## ABRIL              5      10           10
## AGOSTO            11      22           32
## ENERO              4       8           40
## FEBRERO            6      12           52
## JULIO              5      10           62
## JUNIO              4       8           70
## MARZO              6      12           82
## MAYO               5      10           92
## SEPTIEMBRE         4       8          100
##   Total           50     100          100

Viendo los desperdicios por kilos, por mes, vemos que agosto fue un mes que tuvo una gran cantidad de desperdicios, sobre todo al compararla con los otros meses.

Gráfica de datos CUANTITATIVOS: Kilos

hist((bd_merma_f$Kilos),col=c("lightgreen"),main="Cantidad de merma por kilos",xlab="Kilogramos")

Viendo la cantidad de kilos, observamos que la gran cantidad se encuentra entre los 3000 y 5000 kilos.

Gráfica de datos CUALITATIVOS: Mes

porcentajes <- as.numeric(round(((prop.table(table(bd_merma_f$Mes)))*100),2))
etiquetas <- c("Abril","Agosto","Enero","Febrero","Julio","Junio","Marzo","Mayo","Septiembre")
etiquetas <- paste(etiquetas, porcentajes)
etiquetas <- paste(etiquetas, "%", sep = "")


pie(porcentajes,etiquetas,col=c("pink","blue","green","yellow","orange","darkblue","darkgreen","red","lightyellow","lightblue","darkred","lightgreen"),main="Merma en los meses", ylab ="Frecuencias",las=1)

En esta diferente presentación de los meses, vemos los porcentajes de merma de cada mes. Aquí notamos como Agosto tiene casi una cuarta parte de todo la merma del año, y Junio, Septiembre, y Enero, la menor parte.

Gráfica de dispersión: Kilos

boxplot(bd_merma_f$Kilos,col=c("yellow"))

En este boxplot que describe la dispersión de los kilos por los meses, vemos que no hay muchos datos que se salgan de lo “normal”. Y que la gran parte de los datos se encuentran entre los 3,000, y más de los 4,000 kilos; la media siendo alrededor de los 4,000 kilos.

FORM- Scrap

Tabla de frecuencia y gráfica de datos CUALITATIVOS: Ubicación de origen

knitr::kable(table(scrap_finalya$ubi_origen))
Var1 Freq
SAB/Calidad/Entrega de PT 58
SAB/Post-Production 13
SAB/Pre-Production 179
tab1(scrap_finalya$ubi_origen,sort.group = FALSE,graph=TRUE,main="Ubicación de origen del Scrap")

## scrap_finalya$ubi_origen : 
##                           Frequency Percent Cum. percent
## SAB/Calidad/Entrega de PT        58    23.2         23.2
## SAB/Post-Production              13     5.2         28.4
## SAB/Pre-Production              179    71.6        100.0
##   Total                         250   100.0        100.0

En este caso, observamos que la gran parte del scrap se encuentra en pre-producción, siguiendo por la verificación de calidad y la entrega, y muy pocas están en post-producción.

Gráfica de datos CUANTITATIVOS: Cantidad

hist((scrap_finalya$cantidad),col=c("lightgreen"),main="Cantidad del Scrap",xlab="Unidades")

Observando la variable cantidad del scrap, vemos que la gran parte está entre las 0 y 10 unidades, sobrepasando solo muy pocas del total.

Gráfica de dispersión: Fecha y cantidad

plot(scrap_finalya$fecha,scrap_finalya$cantidad, main = "Fecha y cantidad del scrap", xlab="Fecha",ylab="Cantidad",
     pch = 19, frame = FALSE,col=c("orange"))

Continuando con un análisis de dispersión, comparamos la fecha y las unidades. Vemos que la mayoría del scrap de agosto fue trabajado en pocas unidades, con muy pocas siendo altas. Todo el mes presenta trabajo.

Propuestas

  1. Tomando en cuenta los datos observados en el análisis de RH, se ha podido notar que Form tiene un área de oportunidad, en cuanto a su rotación y sus bajas, debido a que estas son más altas de lo deseable; muchas de las bajas son debido a las faltas. Igualmente, se conoce que la mayoría de las personas que han ingresado son de recién ingreso (entre 2020 y 2022), por lo que se conoce que no hay mucha antigüedad entre los colaboradores de Form. Lo que se propone es implementar estrategias de crecimiento y desarrollo en los trabajadores, con la finalidad de incrementar el compromiso dentro de los colaboradores y que puedan ver un futuro dentro de la empresa.
  2. Igualmente, es notorio como las personas de poca antigüedad y mucha antigüedad ganan casi lo mismo. Con el fin de incentivar a que las personas pasen más tiempo en la empresa, crear un programa de compensaciones que motive a las personas a quedarse un lapso más largo. Así como “premiar” a aquellos que han durado más.
  3. Finalmente, para disminuir la diferencia de tiempo con el rendimiento de los envíos podemos tener un encargado más de Transporte para poder llegar a tiempo o aplicar tecnologías como Blockchain o RFID que nos pueda dar información sobre el tiempo real donde se encuentra, que registre el tiempo y buscar mejores rutas para tener una excelente logística.

Bases de datos externas

México: cartón

Importar base datos

bd_mex <- read.csv("/Users/elenavela/Downloads/ACTIVIDAD 2.2 (1).csv")

bd_mex1 <- bd_mex
bd_mex1<-subset(bd_mex1,select=-c(ID))
str(bd_mex1)
## 'data.frame':    1436 obs. of  5 variables:
##  $ Unidad._Económica           : chr  "AGI SHOREWOOD MEXICO S DE RL DE CV" "BIO PAPPEL" "CAJAS CON CARTON" "CAJAS CORRUGADAS DE AGUASCALIENTES" ...
##  $ Clase._de_actividad         : chr  "Fabricación de envases de cartón" "Fabricación de envases de cartón" "Fabricación de envases de cartón" "Fabricación de envases de cartón" ...
##  $ Descripcion.estrato.personal: chr  "101 a 250 personas" "0 a 5 personas" "0 a 5 personas" "11 a 30 personas" ...
##  $ Estado                      : chr  "AGUASCALIENTES" "AGUASCALIENTES" "AGUASCALIENTES" "AGUASCALIENTES" ...
##  $ Tipo_establecimiento        : chr  "Fijo" "Fijo" "Fijo" "Fijo" ...
bd_mex1 [duplicated(bd_mex1),]
##                                Unidad._Económica
## 37             SOLUCIONES EN EMPAQUE Y LOGISTICA
## 49      CELULOSA Y CORRUGADOS DE SONORA SA DE CV
## 63                 EMPAQUES PRECISOS DE MEXICALI
## 89                       SINIL INDUSTRY SA DE CV
## 119                    DANHIL DE MEXICO SA DE CV
## 172                                      DURABOX
## 211                                   BIO PAPPEL
## 251             FABRICACIÓN DE ENVASES DE CARTÓN
## 372                                     CORRUWAX
## 383                                  EMPAQUES 06
## 390                            ENVASES MICROONDA
## 481  PROCESOS ESPECIALIZADOS DE CARTON DE MEXICO
## 670                                   PRAKTICAJA
## 698                                   BIO PAPPEL
## 703                         BIO PAPPEL SAB DE CV
## 728           CAJAS Y EMPAQUES MODERNOS SA DE CV
## 736                                    CARTONERA
## 803               FABRICACION DE CAJAS DE CARTON
## 804               FABRICACION DE CAJAS DE CARTON
## 805               FABRICACION DE CAJAS DE CARTON
## 818                                  GRUPO TENSA
## 919                                    HVM GROUP
## 955                                    BRECEPACK
## 963                      CAJAS DE CARTON SULTANA
## 980                           CARTOLITO SA DE CV
## 1021                         EMPAQUES CONFIABLES
## 1047            FABRICACIÓN DE ENVASES DE CARTÓN
## 1052                                       FECSA
## 1068                         HEXAGONOS MEXICANOS
## 1091                       LITOGRAFICA ROBERTSON
## 1224                               CORRUEMPAQUES
## 1226                   CORRUGADOS ESPECIALIZADOS
## 1241                                         ESK
## 1281                              CAJAS MIL USOS
## 1286                           CARDBOARD & BOXES
## 1302              EMPAQUES Y ENVOLTURAS SA DE CV
## 1338    CELULOSA Y CORRUGADOS DE SONORA SA DE CV
## 1339    CELULOSA Y CORRUGADOS DE SONORA SA DE CV
## 1340    CELULOSA Y CORRUGADOS DE SONORA SA DE CV
## 1341    CELULOSA Y CORRUGADOS DE SONORA SA DE CV
## 1374                EMPAQUES RIO GRANDE SA DE CV
## 1403                         HEXAGONOS MEXICANOS
##                   Clase._de_actividad Descripcion.estrato.personal
## 37   Fabricación de envases de cartón             11 a 30 personas
## 49   Fabricación de envases de cartón               0 a 5 personas
## 63   Fabricación de envases de cartón               0 a 5 personas
## 89   Fabricación de envases de cartón           251 y más personas
## 119  Fabricación de envases de cartón             31 a 50 personas
## 172  Fabricación de envases de cartón           101 a 250 personas
## 211  Fabricación de envases de cartón             31 a 50 personas
## 251  Fabricación de envases de cartón               0 a 5 personas
## 372  Fabricación de envases de cartón               0 a 5 personas
## 383  Fabricación de envases de cartón              6 a 10 personas
## 390  Fabricación de envases de cartón               0 a 5 personas
## 481  Fabricación de envases de cartón             11 a 30 personas
## 670  Fabricación de envases de cartón             11 a 30 personas
## 698  Fabricación de envases de cartón             31 a 50 personas
## 703  Fabricación de envases de cartón           251 y más personas
## 728  Fabricación de envases de cartón           101 a 250 personas
## 736  Fabricación de envases de cartón               0 a 5 personas
## 803  Fabricación de envases de cartón               0 a 5 personas
## 804  Fabricación de envases de cartón               0 a 5 personas
## 805  Fabricación de envases de cartón               0 a 5 personas
## 818  Fabricación de envases de cartón               0 a 5 personas
## 919  Fabricación de envases de cartón               0 a 5 personas
## 955  Fabricación de envases de cartón             31 a 50 personas
## 963  Fabricación de envases de cartón            51 a 100 personas
## 980  Fabricación de envases de cartón             11 a 30 personas
## 1021 Fabricación de envases de cartón               0 a 5 personas
## 1047 Fabricación de envases de cartón               0 a 5 personas
## 1052 Fabricación de envases de cartón               0 a 5 personas
## 1068 Fabricación de envases de cartón             11 a 30 personas
## 1091 Fabricación de envases de cartón             11 a 30 personas
## 1224 Fabricación de envases de cartón             11 a 30 personas
## 1226 Fabricación de envases de cartón              6 a 10 personas
## 1241 Fabricación de envases de cartón             11 a 30 personas
## 1281 Fabricación de envases de cartón               0 a 5 personas
## 1286 Fabricación de envases de cartón               0 a 5 personas
## 1302 Fabricación de envases de cartón               0 a 5 personas
## 1338 Fabricación de envases de cartón               0 a 5 personas
## 1339 Fabricación de envases de cartón               0 a 5 personas
## 1340 Fabricación de envases de cartón               0 a 5 personas
## 1341 Fabricación de envases de cartón               0 a 5 personas
## 1374 Fabricación de envases de cartón            51 a 100 personas
## 1403 Fabricación de envases de cartón             31 a 50 personas
##                    Estado Tipo_establecimiento
## 37         AGUASCALIENTES                 Fijo
## 49        BAJA CALIFORNIA                 Fijo
## 63        BAJA CALIFORNIA                 Fijo
## 89        BAJA CALIFORNIA                 Fijo
## 119  COAHUILA DE ZARAGOZA                 Fijo
## 172             CHIHUAHUA                 Fijo
## 211      CIUDAD DE MÉXICO                 Fijo
## 251      CIUDAD DE MÉXICO                 Fijo
## 372            GUANAJUATO                 Fijo
## 383            GUANAJUATO                 Fijo
## 390            GUANAJUATO                 Fijo
## 481            GUANAJUATO                 Fijo
## 670               JALISCO                 Fijo
## 698                MÉXICO                 Fijo
## 703                MÉXICO                 Fijo
## 728                MÉXICO                 Fijo
## 736                MÉXICO                 Fijo
## 803                MÉXICO                 Fijo
## 804                MÉXICO                 Fijo
## 805                MÉXICO                 Fijo
## 818                MÉXICO                 Fijo
## 919   MICHOACÁN DE OCAMPO                 Fijo
## 955            NUEVO LEÓN                 Fijo
## 963            NUEVO LEÓN                 Fijo
## 980            NUEVO LEÓN                 Fijo
## 1021           NUEVO LEÓN                 Fijo
## 1047           NUEVO LEÓN                 Fijo
## 1052           NUEVO LEÓN                 Fijo
## 1068           NUEVO LEÓN                 Fijo
## 1091           NUEVO LEÓN                 Fijo
## 1224            QUERÉTARO                 Fijo
## 1226            QUERÉTARO                 Fijo
## 1241            QUERÉTARO                 Fijo
## 1281      SAN LUIS POTOSÍ                 Fijo
## 1286      SAN LUIS POTOSÍ                 Fijo
## 1302      SAN LUIS POTOSÍ                 Fijo
## 1338               SONORA                 Fijo
## 1339               SONORA                 Fijo
## 1340               SONORA                 Fijo
## 1341               SONORA                 Fijo
## 1374           TAMAULIPAS                 Fijo
## 1403             TLAXCALA                 Fijo
sum(duplicated(bd_mex1))
## [1] 42
bd_mex2 <- bd_mex1
library(dplyr)
bd2 <- distinct (bd_mex2)

Librerías requeridas

library(foreign)
library(dplyr)        # data manipulation 
library(forcats)      # to work with categorical variables
library(ggplot2)      # data visualization 
library(janitor)      # data exploration and cleaning 
#install.packages("psych")
library(corrplot)     # correlation plots
library(lmtest)       # diagnostic checks - linear regression analysis 
library(car)          # diagnostic checks - linear regression analysis
str(bd_mex2)
## 'data.frame':    1436 obs. of  5 variables:
##  $ Unidad._Económica           : chr  "AGI SHOREWOOD MEXICO S DE RL DE CV" "BIO PAPPEL" "CAJAS CON CARTON" "CAJAS CORRUGADAS DE AGUASCALIENTES" ...
##  $ Clase._de_actividad         : chr  "Fabricación de envases de cartón" "Fabricación de envases de cartón" "Fabricación de envases de cartón" "Fabricación de envases de cartón" ...
##  $ Descripcion.estrato.personal: chr  "101 a 250 personas" "0 a 5 personas" "0 a 5 personas" "11 a 30 personas" ...
##  $ Estado                      : chr  "AGUASCALIENTES" "AGUASCALIENTES" "AGUASCALIENTES" "AGUASCALIENTES" ...
##  $ Tipo_establecimiento        : chr  "Fijo" "Fijo" "Fijo" "Fijo" ...

Gráficos de datos cualitativos y cuantitativos

Datos cualitativos

pie(prop.table(table(bd_mex2$Tipo_establecimiento)),col=c("pink","blue"),main="Tipo de establecimiento", ylab ="Frecuencias",las=1)

Observamos que muy pocos establecimientos de producción de cartón manejan los semifijo.

México: Exportaciones

bd_mex_e<-read.csv("/Users/elenavela/Downloads/exports_mx.csv")
summary(bd_mex_e)
##    PROD_EST          COBERTURA              ANIO          ID_MES      
##  Length:70421       Length:70421       Min.   :2017   Min.   : 1.000  
##  Class :character   Class :character   1st Qu.:2019   1st Qu.: 3.000  
##  Mode  :character   Mode  :character   Median :2020   Median : 6.000  
##                                        Mean   :2020   Mean   : 6.359  
##                                        3rd Qu.:2021   3rd Qu.: 9.000  
##                                        Max.   :2022   Max.   :12.000  
##      MES               MARCA              MODELO              TIPO          
##  Length:70421       Length:70421       Length:70421       Length:70421      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##    SEGMENTO             PAIS           ID_PAIS_DESTINO    UNI_VEH       
##  Length:70421       Length:70421       Min.   :  4.0   Min.   :  -23.0  
##  Class :character   Class :character   1st Qu.: 46.0   1st Qu.:    0.0  
##  Mode  :character   Mode  :character   Median : 74.0   Median :    0.0  
##                                        Mean   :121.4   Mean   :  242.8  
##                                        3rd Qu.:172.0   3rd Qu.:    8.0  
##                                        Max.   :999.0   Max.   :26454.0

Se eliminan variables que no aporten mucho al análisis

bd_mex_e2<-bd_mex_e
bd_mex_e2<-subset(bd_mex_e2,select=-c(PROD_EST,COBERTURA,ID_MES,MODELO,TIPO,ID_PAIS_DESTINO))
summary(bd_mex_e2)
##       ANIO          MES               MARCA             SEGMENTO        
##  Min.   :2017   Length:70421       Length:70421       Length:70421      
##  1st Qu.:2019   Class :character   Class :character   Class :character  
##  Median :2020   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :2020                                                           
##  3rd Qu.:2021                                                           
##  Max.   :2022                                                           
##      PAIS              UNI_VEH       
##  Length:70421       Min.   :  -23.0  
##  Class :character   1st Qu.:    0.0  
##  Mode  :character   Median :    0.0  
##                     Mean   :  242.8  
##                     3rd Qu.:    8.0  
##                     Max.   :26454.0

¿Cuántos NA tengo por variable?

sapply(bd_mex_e2,function(x) sum(is.na(x)))
##     ANIO      MES    MARCA SEGMENTO     PAIS  UNI_VEH 
##        0        0        0        0        0        0

Gráficas

marca<-table(bd_mex_e2$MARCA)
knitr::kable(marca)
Var1 Freq
Audi 1459
BMW Group 854
Chrysler 8359
Fiat 1051
Ford Motor 1175
General Motors 14336
Honda 387
KIA 7150
Mazda 12688
Mercedes Benz_Prod_Expo 1557
Nissan 12390
Toyota 254
Volkswagen 8761
tab1(bd_mex_e2$MARCA,sort.group = FALSE,graph=TRUE,main="Distribución de Exportaciones por Marca")

## bd_mex_e2$MARCA : 
##                         Frequency Percent Cum. percent
## Audi                         1459     2.1          2.1
## BMW Group                     854     1.2          3.3
## Chrysler                     8359    11.9         15.2
## Fiat                         1051     1.5         16.6
## Ford Motor                   1175     1.7         18.3
## General Motors              14336    20.4         38.7
## Honda                         387     0.5         39.2
## KIA                          7150    10.2         49.4
## Mazda                       12688    18.0         67.4
## Mercedes Benz_Prod_Expo      1557     2.2         69.6
## Nissan                      12390    17.6         87.2
## Toyota                        254     0.4         87.6
## Volkswagen                   8761    12.4        100.0
##   Total                     70421   100.0        100.0

De las exportaciones de vehículos de México, observamos que aquellos que mayor participación tienen son General Motors (EUA), Mazda (Japón), Nissan (Japón), Chrysler (EUA), Volkswagen (Alemania). Y aquellos que menos son Toyota (Japón), Honda (Japón), y BMW (Alemania).

segmento<-table(bd_mex_e2$SEGMENTO)
knitr::kable(segmento)
Var1 Freq
Compactos 21213
De Lujo 1293
Minivans 472
Pick Ups 8596
Subcompactos 16844
SUV’s 22003
tab1(bd_mex_e2$SEGMENTO,sort.group = FALSE,graph=TRUE,main="Exportaciones por segmento")

## bd_mex_e2$SEGMENTO : 
##              Frequency Percent Cum. percent
## Compactos        21213    30.1         30.1
## De Lujo           1293     1.8         32.0
## Minivans           472     0.7         32.6
## Pick Ups          8596    12.2         44.8
## Subcompactos     16844    23.9         68.8
## SUV's            22003    31.2        100.0
##   Total          70421   100.0        100.0

LA VARIABLE SIN NOMBRE ES SUBCOMPACTOS
De México, se exportan más vehículos Compactos y SUVs. Y las que menos son las Minivaans y los vehículos De Lujo.

mes<-table(bd_mex_e2$MES)
knitr::kable(mes)
Var1 Freq
Abril 6109
Agosto 5090
Diciembre 5696
Enero 6052
Febrero 6115
Julio 6205
Junio 6027
Marzo 6281
Mayo 6196
Noviembre 5656
Octubre 5418
Septiembre 5576
tab1(bd_mex_e2$MES,sort.group = FALSE,graph=TRUE, main="Distribución de Exportaciones por Mes")

## bd_mex_e2$MES : 
##            Frequency Percent Cum. percent
## Abril           6109     8.7          8.7
## Agosto          5090     7.2         15.9
## Diciembre       5696     8.1         24.0
## Enero           6052     8.6         32.6
## Febrero         6115     8.7         41.3
## Julio           6205     8.8         50.1
## Junio           6027     8.6         58.6
## Marzo           6281     8.9         67.6
## Mayo            6196     8.8         76.4
## Noviembre       5656     8.0         84.4
## Octubre         5418     7.7         92.1
## Septiembre      5576     7.9        100.0
##   Total        70421   100.0        100.0

No existe mucha diferencia en las exportaciones por mes, sin embargo lo más notorio es que mayo es el mes que más presenta exportaciones, y el que menos es agosto.

hist((bd_mex_e2$UNI_VEH),col=c("lightyellow"),main="Unidades de vehiculos exportados",xlab="Unidades")

Las unidades de vehículos exportados, la maaayoría no super las 3,000 unidades.

año<-table(bd_mex_e2$ANIO)
knitr::kable(año)
Var1 Freq
2017 7173
2018 9358
2019 14449
2020 15017
2021 15941
2022 8483
tab1(bd_mex_e2$ANIO,sort.group = FALSE,graph=TRUE,main="Distribución de exportaciones por año")

## bd_mex_e2$ANIO : 
##         Frequency Percent Cum. percent
## 2017         7173    10.2         10.2
## 2018         9358    13.3         23.5
## 2019        14449    20.5         44.0
## 2020        15017    21.3         65.3
## 2021        15941    22.6         88.0
## 2022         8483    12.0        100.0
##   Total     70421   100.0        100.0

En esta comparativa de las exportaciones de México a través de los años, vemos que durante el 2017 fue la menor cantidad, aún menos que el 2022 (año sin completar). Aquel que ha presentado más exportaciones fue el año pasado, 2021.

United States: Exports

bd_eua<-read.csv("/Users/elenavela/Downloads/exports.csv")
summary(bd_eua)
##       Year           APEC               ASEAN               CAFTA          
##  Min.   :2008   Min.   :3.577e+10   Min.   :4.969e+08   Min.   :278169351  
##  1st Qu.:2011   1st Qu.:5.587e+10   1st Qu.:8.402e+08   1st Qu.:352131115  
##  Median :2014   Median :6.611e+10   Median :1.023e+09   Median :382792580  
##  Mean   :2014   Mean   :6.144e+10   Mean   :1.015e+09   Mean   :398381579  
##  3rd Qu.:2018   3rd Qu.:6.955e+10   3rd Qu.:1.232e+09   3rd Qu.:459128093  
##  Max.   :2021   Max.   :7.344e+10   Max.   :1.502e+09   Max.   :520314206  
##  EU.Without.UK            EU28           FTA.COUNTRIES      
##  Min.   :3.032e+09   Min.   :3.645e+09   Min.   :3.403e+10  
##  1st Qu.:4.321e+09   1st Qu.:5.390e+09   1st Qu.:5.040e+10  
##  Median :5.084e+09   Median :6.282e+09   Median :6.218e+10  
##  Mean   :5.072e+09   Mean   :6.330e+09   Mean   :5.700e+10  
##  3rd Qu.:5.807e+09   3rd Qu.:7.360e+09   3rd Qu.:6.459e+10  
##  Max.   :6.903e+09   Max.   :8.900e+09   Max.   :6.709e+10  
##       GCC               MERCOSUR             USMCA          
##  Min.   :6.503e+08   Min.   :1.122e+09   Min.   :3.177e+10  
##  1st Qu.:8.310e+08   1st Qu.:1.469e+09   1st Qu.:4.707e+10  
##  Median :9.483e+08   Median :1.645e+09   Median :5.753e+10  
##  Mean   :9.264e+08   Mean   :1.730e+09   Mean   :5.320e+10  
##  3rd Qu.:1.008e+09   3rd Qu.:1.967e+09   3rd Qu.:6.014e+10  
##  Max.   :1.188e+09   Max.   :2.555e+09   Max.   :6.305e+10  
##      WORLD          
##  Min.   :4.324e+10  
##  1st Qu.:6.725e+10  
##  Median :7.695e+10  
##  Mean   :7.329e+10  
##  3rd Qu.:8.196e+10  
##  Max.   :8.876e+10

GLOSARIO:

  • APEC= Asia-Pacific Economic Cooperation

  • ASEAN=Association of South-East Asian Nations

  • CAFTA=Tratado de Libre Comercio entre Centroamérica y los Estados Unidos

  • EU.Without.UK=Unión europea sin el Reino Unido

  • EU28=Unión europea con el Reino Unido

  • FTA.Countries=The United States has agreements in force with 20 countries: Australia, Bahrain, Canada, Chile, Colombia, Costa Rica, Dominican Republic, El Salvador, Guatemala, Honduras, Israel, Jordan, Mexico, Morocco, Nicaragua, Oman, Panama, Peru, Singapore, and South Korea.

  • GCC=Gulf Cooperation Council: a group of six countries in the Persian Gulf: Bahrain, Kuwait, Qatar, Oman, Saudi Arabia, and the United Arab Emirates

  • Mercosur=El Mercado Común del Sur (MERCOSUR) Argentina, Brasil, Paraguay y Uruguay.

  • USMCA=United States-Mexico-Canada Agreement

  • World= mundo.

No se hacen tabla de frecuencia debido ya que no existen variables cualitativas.

hist((bd_eua$EU.Without.UK),col=c("lightgreen"),main="Exports for EU (without UK)",xlab="Unidades")

De las unidades exportadas de Estados Unidos a la Unión Europea sin el Reino Unido, se tienen desde 3 mil millones hasta 7 mil millones, siendo el mayor rango de 5 mil millones a 6 mil millones. Esto tomando en cuenta los años desde el 2008 hasta el 2021.

hist((bd_eua$EU28),col=c("lightblue"),main="Exports for EU (with UK)",xlab="Unidades")

Tomando en cuenta a la Unión Europa con el Reino Unido, las unidades de vehículos exportados van desde los 3 mil millones hasta los 9 mil millones de unidades. Siendo la mayor frecuencia desde los 5 mil millones hasta los 6 mil millones. Sin embargo, en este caso, se tiene un importante número de unidades exportadas desde los 6 mil millones hasta lo 8 mil millones.

plot(bd_eua$Year,bd_eua$WORLD, main = "Año y cantidad de exportaciones", xlab="Año",ylab="Mundo",
     pch = 19, frame = FALSE,col=c("orange"))

Notamos como el año con mayores exportaciones de parte de EUA es el 2018, y el menor 2009 (posiblemente debido a la crisis). Del 2010 hasta el 2018 vemos un importante crecimiento de los 6 billones de vehículos exportados hasta los 9 billones.

boxplot(bd_eua$CAFTA,col=c("orange"))

En cuanto a los vehículos exportados hacia la zona de Centroamérica y los Estados Unidos, vemos que la mayoría de las exportaciones se concentran entre los 350 millones hasta los 450 millones, con un promedio de apróximadamente los 375 millones. Sin embargo, el rango va desde los 300 millones hasta los 550 millones de vehículos exportados a esta zona.

Análisis Exploratorio de las Bases de Datos

Estadísticos descriptivos

Colaboradores

# install.packages("psych")
library(psych)
#describe(bd_colab)

Variables <-c("Edad","Alta","Salario Diario")
Media <-c("35.45","2020.69","179.35")
Mediana <-c("33","2022","180.68")
Moda<-c("32","2022","180.68")
Desviacion_estandar <-c("12.20","2.34","25.17")
Rango<-c("48","12","192.6")

table<-data.frame(Variables,Media,Mediana,Moda,Desviacion_estandar,Rango)
knitr::kable(table)
Variables Media Mediana Moda Desviacion_estandar Rango
Edad 35.45 33 32 12.20 48
Alta 2020.69 2022 2022 2.34 12
Salario Diario 179.35 180.68 180.68 25.17 192.6

A partir de estos estadísticos descriptivos de los colaboradores ACTUALES de FORM, observamos que tenemos solo 3 variables cuantitativas, por lo tanto solo estas serán descritas.
1. Edad: La media o el promedio es de 35.45 años, la mediana es de 33 años, y la moda es 32 años. A partir de esto comprendemos que existen muchos colaboradores que son adultos en sus treintas (relativamente jóvenes). La desviación es de 12.20, por lo que comprendemos que la edad se desvía de la media aproximademento por 12 años; la mayoría de los datos están en este rango (+12,-12). Finalmente, con el máximo y el mínimo de las edades obtenemos el rango de edad: 48 años.
2. Año de alta: Observamos que la media es durante la última parte del 2020, la mediana y la moda es el 2022. Por otra parte, vemos que la desviación es de 2.34. Por lo tanto, podemos notar como es que la mayoría de las personas que están actualmente en FORM no tienen mucha antigüedad, ya que ingresaron durante los últimos (aprox) 4 años. Viendo que el rango es de 12 años, no se tiene mucha antigüedad de parte de la mayoría.
3. Salario Diario: En el salario diario observamos que la moda y la mediana es de $180.68, por lo que podemos concluir que es un número altamente repetido en los colaboradores actuales. Mientras que la mediana es un poco inferior, $179.39, con lo que podemos inferir que existen más personas por debajo de $180.68 que arriba de. En cuanto a la desviación, encontramos que es de 25.17 pesos, se asume que los salarios no están muy dispersos. La diferencia entre el menor y el mayor de $192.6.

Bajas

# install.packages("psych")
library(psych)
#describe(bd_bajas)

Variables <-c("Edad","Duracion","Salario Diario")
Media <-c("30.77","75.59","177.93")
Mediana <-c("29","20.50","180.68")
Moda<-c("21","20.5","180.68")
Desviacion_estandar <-c("9.69","215.50","23.41")
Rango<-c("43","1966","355.55")

table<-data.frame(Variables,Media,Mediana,Moda,Desviacion_estandar,Rango)
knitr::kable(table)
Variables Media Mediana Moda Desviacion_estandar Rango
Edad 30.77 29 21 9.69 43
Duracion 75.59 20.50 20.5 215.50 1966
Salario Diario 177.93 180.68 180.68 23.41 355.55

A partir de estos estadísticos descriptivos de los colaboradores PASADOS de FORM, observamos que tenemos solo 3 variables cuantitativas, por lo tanto solo estas serán descritas.
1. Edad: La media o el promedio es de 30.77 años, la mediana es de 29 años, y la moda es 21 años. A partir de esto comprendemos que existen muchos colaboradores que son adultos en sus veintes y treintas (jóvenes). La desviación es de 9.69, por lo que comprendemos que la edad se desvía de la media aproximademento por 10 años; la mayoría de los datos están en este rango de 20 a 40 aprox. Finalmente, con el máximo y el mínimo de las edades obtenemos el rango de edad: 43 años. Las edades de los que se han dado de baja son menores a los que siguen en la empresa.
2. Duración: Observamos que la media es de 75.59 días, la mediana y la moda es 20.5; por lo tanto asumimos que hay personas que duraron mucho más que la mayoría (debido a la diferencia entre la media y la mediana y moda). Vemos que la desviación es de 215.5 días, lo cual puede ser considerado mucho observando la mediana, moda y la media; es por esto que resulta interesante ver el rango, ya que la diferencia entre la persona que menos duró y salió, y la que más duró y salió es de 1966 (gran cantidad de días de diferencia).
3. Salario Diario: En el salario diario observamos que la moda y la mediana es de $180.68, por lo que podemos concluir que es un número altamente repetido en los colaboradores actuales. Mientras que la mediana es un poco inferior, $177.93, con lo que podemos inferir que existen más personas por debajo de $180.68 que arriba de. En cuanto a la desviación, encontramos que es de 23.41 pesos, se asume que los salarios no están muy dispersos. La diferencia entre el menor y el mayor de $355.55; hubieron personas que ganaban mucho más que el promedio.

Producción

# install.packages("psych")
library(psych)
#describe(bd_prod)

Variables <-c("Piezas programadas","Tiempo mínimo","Láminas procesadas","Tiempo calidad" )
Media <-c("183.40","22.04","78.84","0.89" )
Mediana <-c("120","20","41","1" )
Moda<-c("200","20","0","1")
Desviacion_estandar <-c("169.45","12.32","103.18","0.70" )
Rango<-c("1999","110","803","11")

table<-data.frame(Variables,Media,Mediana,Moda,Desviacion_estandar,Rango)
knitr::kable(table)
Variables Media Mediana Moda Desviacion_estandar Rango
Piezas programadas 183.40 120 200 169.45 1999
Tiempo mínimo 22.04 20 20 12.32 110
Láminas procesadas 78.84 41 0 103.18 803
Tiempo calidad 0.89 1 1 0.70 11

Viendo las variables cuantitativas de la producción de julio, agosto, y septiembre de FORM, podemos analizar lo siguiente:
1.Piezas programadas: En esta caso resulta interesante empezar con el rango: 1999. Por lo que asumimos que el valor mayor es de 2000 piezas programadas, y lo menor ha sido 0 en diferentes días. Vemos que la media es de 183.40 piezas, la mediana es de 120 piezas, y la moda de 200; asumimos que lo común es que en un día se programen no más de 200 piezas, y que solo en casos “espaciales” llegue a los miles. La desviación estandar no es muy alta, considerando el rango, esta es de 169.45 piezas programadas; lo “normal” es que las piezas e stén entre las 0 y las 400 piezas programadas.
2. Tiempo mínimo: Importante recordar que esta variable se mide en minutos. La media es de 22.05, la mediana y la moda de 20, por lo que asumimos que muchos datos se encuentran en los veinte (+/-) minutos de calidad de producción. La desviación estandar es de 12.32, lo cual no es mucha dispersión considerando que el rango es de 110 minutos. El tiempo mínimo ha variado dependiendo de la orden, hasta llegar a casi dos horas.
3. Láminas procesadas: La media de las láminas procesadas en estos meses es de 78.84, la mediana de 41, y la moda de 0. Con esto comprendemos que la mayoría de los días no se procesó ninguna láminas, pero que los otros días no seguían una tendencia clara. La desviación es de 103.18 láminas procesadas y el rango de 803 láminas.
4. Tiempo calidad: Importante recordar que esta variable se mide en minutos. La mayoría de los datos se encuentran entre el 0 y 1 minuto de calidad, de acuerdo al análisis previo, por lo tanto aquí también observamos que la media es de 0.89 (acercándose más a 1 minuto), la mediana es de 1 y la moda es de 1. Lo más común de ver en FORM en cuanto a tiempo de calidad de su producción es de un minuto. La desviación es poca (0.70), considerando que el rango es de 11 minutos. Los datos no están muy dispersos.

Scrap

# install.packages("psych")
library(psych)
#describe(scrap_finalya)

Variable <-c("Cantidad")
Media <-c("6.70")
Mediana <-c("2")
Moda<-c("1")
Desviacion_estandar <-c("11.85")
Rango<-c("96")

table<-data.frame(Variable,Media,Mediana,Moda,Desviacion_estandar,Rango)
knitr::kable(table)
Variable Media Mediana Moda Desviacion_estandar Rango
Cantidad 6.70 2 1 11.85 96

En esta caso solo encontramos una variable cuantitativa a analizar:
1. Cantidad de scrap: observamos que el promedio del scrap es de 6.70, la mediana es de 2 y la moda es de 1; comprendemos que la mayoría de los valores en este base de datos están entre 1 y 2. Aunque el rango es de 96, la desviación es de 11.85, por lo que se puede asumir que lo común es que la cantidad no pase de 20.

Merma

# install.packages("psych")
library(psych)
#describe(bd_merma_f)

Variable <-c("Kilos")
Media <-c("3,708.52")
Mediana <-c("3,925")
Moda<-c("3,810")
Desviacion_estandar <-c("1,023.99")
Rango<-c("5,350")

table<-data.frame(Variable,Media,Mediana,Moda,Desviacion_estandar,Rango)
knitr::kable(table)
Variable Media Mediana Moda Desviacion_estandar Rango
Kilos 3,708.52 3,925 3,810 1,023.99 5,350

En esta caso solo encontramos una variable cuantitativa a analizar:
1. Kilos de merma: la media es de 3,708.52 kgs, la mediana es de 3,925 kgs, y la moda es de 3,810 kgs; se asume que lo común es encontrar kilos de merma en los 3,000 por vez. El rango es de 5,350 kgs y la desviación es de 1,023.99 kgs; se considera que los datos están algo dispersos.

Delivery performance

# install.packages("psych")
library(psych)
describe(bd_delperf)
##          vars   n mean   sd median trimmed  mad min  max range  skew kurtosis
## cliente*    1 104 2.75 1.10    2.5    2.81 1.48   1  4.0   3.0 -0.07    -1.46
## fecha*      2 104 7.00 3.76    7.0    7.00 4.45   1 13.0  12.0  0.00    -1.25
## dif         3 104 0.30 0.56    0.0    0.20 0.00   0  3.3   3.3  2.27     6.81
##            se
## cliente* 0.11
## fecha*   0.37
## dif      0.06
Variable <-c("Diferencia (delay performance)")
Media <-c("0.30")
Mediana <-c("0.0")
Moda<-c("0.0")
Desviacion_estandar <-c("0.56")
Rango<-c("3.3")

table<-data.frame(Variable,Media,Mediana,Moda,Desviacion_estandar,Rango)
knitr::kable(table)
Variable Media Mediana Moda Desviacion_estandar Rango
Diferencia (delay performance) 0.30 0.0 0.0 0.56 3.3

En esta caso solo encontramos una variable cuantitativa a analizar:
1. Delay performance: observamos en esta variable como la media es de 0.3 minutos, la moda y la mediana es de 0. En esto encontramos que la normal es que no haya un delay en las entregas, y al observar el rango (3.3) vemos que aún lo más “radical” es muy poco.

Delivery Plan

# install.packages("psych")
library(psych)
#describe(bd_delplan)

Variables <-c("Pedidos")
Media <-c("1,703.14")
Mediana <-c("0")
Moda<-c("0")
Desviacion_estandar <-c("6,164.04")

table<-data.frame(Variables,Media,Mediana,Moda,Desviacion_estandar)
knitr::kable(table)
Variables Media Mediana Moda Desviacion_estandar
Pedidos 1,703.14 0 0 6,164.04

En esta caso solo encontramos una variable cuantitativa a analizar:
1. Pedidos: En los pedidos encontramos que lo común es tener 0 (debido a la moda y la mediana), pero que a partir de la gran dispersión de los datos la media (1,703.14 pedidos) crece. O se pueden tener muchos pedidos o ningún pedido mes con mes.

Bar Plots/Pie Charts

Producción

bd_prodd<-read.csv("/Users/elenavela/produccion_final2.csv")
pie((prop.table(table(bd_prodd$client))),col=c("lightgreen","lightyellow","lightblue","pink"),main="Clientes",las=1)

En la producción analizamos los principales clientes de FORM, en este mismo, a partir de los porcentajes, comprendemos que Stabilus 1 y Stabilus 3 consiste en el 51.1% de todos los pedidos hechos por los clientes. TRMX también hace un pedido importante a FORM, representando un 20.3% en los últimos tres meses. Hay tres: Hanon Systems, Hella, y Meridian Lightweight que no conforman más del 4%.

SCRAP

porcentajes <- as.numeric(round(((prop.table(table(scrap_finalya$ubi_origen)))*100),2))
etiquetas <- c("Calidad/Entrega de PT","Post-Producción","Pre-Producción")
etiquetas <- paste(etiquetas, porcentajes)
etiquetas <- paste(etiquetas, "%", sep = "")


pie(porcentajes,etiquetas,col=c("pink","lightblue","lightyellow"),main="Ubicación de origen: Scrap", ylab ="Frecuencias",las=1)

Como se vió anteriormente, notamos como el scrap se encuentra por lo general (casi tres de cuatro veces) en la pre-producción, y casi nunca en la post-producción.

Delivery Plan: Principales clientes

Con la siguiente función podremos identificar los principales clientes para generar un análisis con la información relevante, pues hay clientes con pocos o sin pedidos por lo que generar un diagnóstico respecto a delivery plan con esos datos no es necesario.

ggplot(bd_delplan, aes(x=reorder(cliente,pedidos), y=pedidos)) +
  geom_bar(stat="identity")+
  coord_flip()

Delivery Plan: Top 6 clientes y promedio

Como siguiente paso eliminaremos todos los clientes que no nos interesan para el análisis y dejaremos el top 6 de clientes para el ejercicio.

bd_delplan2 <- bd_delplan
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="ABC QUERETARO",]
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="ANTOLIN ARTEAGA",]  
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="ANTOLIN TOLUCA",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="ISRI",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="SEGROVE",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="STB 1",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="UFI",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="YF QRO",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="INOAC POLYTEC",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="HANON",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="MERIDIAN",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="YF RAMOS",] 
bd_delplan2<-bd_delplan2[bd_delplan2$cliente!="YANFENG sm",] 

Se cambian la fecha a un formato que permite ver mejor el análisis en las tablas (se sobreponían).

bd_delplan5<-bd_delplan2
bd_delplan5$fecha[bd_delplan5$fecha == "jan-2022"] <- "jan22"
bd_delplan5$fecha[bd_delplan5$fecha == "feb-2022"] <- "feb22"
bd_delplan5$fecha[bd_delplan5$fecha == "mar-2022"] <- "mar22"
bd_delplan5$fecha[bd_delplan5$fecha == "abr-2022"] <- "abr22"
bd_delplan5$fecha[bd_delplan5$fecha == "may-2022"] <- "may22"
bd_delplan5$fecha[bd_delplan5$fecha == "jun-2022"] <- "jun22"
bd_delplan5$fecha[bd_delplan5$fecha == "jul-2022"] <- "jul22"
bd_delplan5$fecha[bd_delplan5$fecha == "ag-2022"] <- "ag22"
bd_delplan5$fecha[bd_delplan5$fecha == "sep-2022"] <- "sep22"
bd_delplan5$fecha[bd_delplan5$fecha == "oct-2022"] <- "oct22"
bd_delplan5$fecha[bd_delplan5$fecha == "nov-2022"] <- "nov22"
bd_delplan5$fecha[bd_delplan5$fecha == "dic-2022"] <- "dic22"

gráfica con 3 variables

ggplot(bd_delplan5,aes(x=fecha, y=pedidos,fill=cliente))+
  geom_bar(stat="identity")+
  geom_hline(yintercept=33,linetype="dashed",color="black")+
  labs(x="Fecha",y="Número de pedidos", color="Legend")+
  ggtitle("Pedidos por fecha")

En esta gráfica que une tres variables del delivery plan, observamos la cantidad de pedidos, por cliente, por fecha. En esta vemos como en todos los meses (menos oct, nov y dic) Hella ha estado muy presente como cliente, por lo general superando los 10,000, hasta casi 70,000 pedidos. YFTO es el cliente, de este pool que tiene menos pedidos a través de los meses.

Recursos Humanos: Colaboradores actuales

#as.data.frame(bd_bajas)
#as.data.frame(bd_colab)
bd_colab2<-bd_colab %>% dplyr::select(genero,edad,salario_diario) %>%  dplyr::group_by(genero) %>%
  dplyr::summarise(across(everything(),mean,na.rm=TRUE)) %>% arrange(desc(edad))
ggplot(bd_colab2, aes(x=reorder(genero,edad), y=edad, fill=(salario_diario))) +
  geom_bar(stat="identity",col=c("black"))+
  coord_flip()+
  guides(fill=guide_legend(reverse=FALSE))

La primera gráfica de barras nos da a conocer sobre el salario diario de las personas de Form, segmentado por edad y por género. Lo que nos da a conocer es que en la empresa existe un mayor rango de edad entre las mujeres de Form, llegando a ser casi 40 lo mayor; por otra parte, los hombres presentan un menor rango, con un máximo de (aprox) 33. Igualmente, vemos que en promedio las mujeres ganan 60 centavos más que los hombres.

bd_colab3<-bd_colab
bd_colab3$genero[bd_colab$genero == "FEMENINO"] <- "F"
bd_colab3$genero[bd_colab$genero == "MASCULINO"] <- "M"
ggplot(bd_colab3, aes(x=genero, y=salario_diario, fill=genero)) + 
  geom_bar(stat="identity") + 
  facet_grid(~civil) + scale_fill_brewer(palette = "Set2")

Esta segunda gráfica descriptiva de los actuales colaboradores de Form, nos comunica, principalmente, el estado civil de los colaboradores por género, y su ganancia. Lo que podemos notar es que en general, las mujeres ganan más en cualquier estado civil en el que se encuentren, y en donde se puede ver una mayor variedad de la suma de los salarios es en las mujeres y los hombres que se encuentran en unión libre, ya que las mujeres en unión libre suman casi el doble que los hombres en unión libre.

Recursos Humanos: Bajas

bd_bajas2<-bd_bajas %>% dplyr::select(motivo_baja,edad,duracion) %>% group_by(motivo_baja) %>%
  dplyr::summarise(across(everything(),mean,na.rm=TRUE)) %>% arrange(desc(edad))
ggplot(bd_bajas2, aes(x=reorder(motivo_baja,edad), y=edad, fill=(duracion))) +
  geom_bar(stat="identity",col=c("black"))+
  coord_flip()+
  guides(fill=guide_legend(reverse=FALSE))

Viendo ahora los colaboradores que han sido dados de baja en la empresa, vemos que la gran parte de los que se han salido de Form tienen menos de 30 años. Pocos casos han sido de jubilación, llegando a más de 60 en rango. Igualmente, a partir de la gráfica vemos que las razones principales son por abandono y baja por faltas; durando menos de 500 días en su trabajo.

ggplot(bd_bajas, aes(x=genero, y=salario_diario, fill=genero)) + 
  geom_bar(stat="identity") + 
  facet_grid(~civil) + scale_fill_brewer(palette = "Set2")

Finalmente, observamos la última gráfica descriptiva de los colaboradores que han sido dados de baja de Form. Observamos que la mayor cantidad (se asume por la suma de salario diario) han sido solteros, y una gran cantidad de mujeres casadas. Por lo tanto, podemos asumir a partir de los datos que la mayoría de los que abandonan su trabajo en Form son personas solteras. Por otra parte, vemos que las mujeres en matriminio suman más ganancias que los hombres en matrimonio. Las mujeres, en todas los estados civiles, han ganado más; estto pudiera verse también debido a que más del 50% de las personas en esta base dee datos son mujeres.

Merma

ggplot(bd_merma_f, aes(x= Mes, y= Kilos)) + geom_bar(stat="identity", fill="red") + scale_fill_grey() + labs(title = "Kilos de merma", x = "Fecha")

En esta gráfica podemos observar los meses que superan el promedio o lo “normal” de los kilos de merma; en este caso vemos que el mes de agoosto destaca como aquel con más kilos, mientras que septiembre es el que demuestra teener menos. Comparamos lo más de 30,000 kilos en agosto con los casi 15,000, por lo que es casi el doble.

Scrap

hist(scrap_finalya$cantidad, main = "Cantidad de Material Reciclado", xlab = "Cantidad", ylab = "Frecuencia",col = "blue")

Este histograma nos muestra que entre 0 a 10 toneladas de “scrap” es lo que mas se frecuenta a reciclar el material. Se observan muy pocos casos que superan los 20.

Dispersion Plots

Scrap

plot(scrap_finalya$fecha, scrap_finalya$cantidad, main = "Cantidad de Scrap por fecha", xlab = "Fecha", ylab = "Cantidad")

Viendo el mes de agosto para el “scrap”, obserevamos que la mayoría de los días manejan menos de 20 unidades de material, sin embargo, existen algunos casos en que se sale de lo “normal” como vemos aproximadamente el día 24 de agosto, en donde la cantidad de scrap superó el número 80.

Delivery Plan

Pedidos

plot(bd_delplan5$ID_fecha, bd_delplan5$pedidos, main = "Pedidos por número de mes",
     xlab = "Mes", ylab = "Pedidos",col=c("orange"),
     pch = 1, frame = FALSE)

Teniendo los meses como números del año, vemos como la mayoría de los meses cuentan con pedidos de menos de 10,000; sin embargo, algunos meses presentan pedidos “extraordinarios”, como lo fue juno y agosto, que estuvieron entren 40,000 y 50,000.

boxplot(bd_delplan5$pedidos, main = "Pedidos",col=c("orange"))

Como primer boxplot vemos los pedidos y la gran dispersión que hay entre pedidos, pues detectamos una frecuencia en donde hay clientes que generan pedidos obviamente diferentes como para tener una mediana o una dispersión positiva. Esto mismo lo pudimos ver anteriormente, como la mayoría de los pedidos no superan los 10,000, y que existen casos “extraordinarios” que van muy por arriba de este número.

Clientes

Después de generar un boxplot de pedidos en general, realizamos un boxplot que nos muestra los pedidos por cliente

bd_delplan4 <- bd_delplan5
bd_delplan4$cliente<-as.factor(bd_delplan4$cliente)
ggplot(bd_delplan4, aes(x=cliente, y=pedidos)) + 
  geom_boxplot(color="red", fill="orange", alpha=0.2)

En el gráfico anterior podemos observar a los 6 clientes con mayor presencia en FORM en cuanto a Delivery Plan en donde el objetivo es ver la dispersión y la distribución entre cada uno y respecto a los pronósticos individuales.

El cliente HELLA es el cliente con una mayor distribución y dispersión. Varroc y TRMX tienen también una distribución mayor al tener la boxplot más grande, mientras que los demás; DENSO, STB3 y YFTO tienen la mayoría de sus pedidos en un mismo rango con pequeños datos fuera del boxplot presentando algunos warnings.

Se puede concluir, que HELLA es el cliente más fuerte en dicha base de datos para la empresa, pero que sus pedidos varçian de cantidad.

Time series plots

dp_data<-read.csv("/Users/elenavela/Downloads/student_dp_form.csv")
summary(dp_data)
##     fecha             cliente          delay_performance
##  Length:52          Length:52          Min.   : 0.00    
##  Class :character   Class :character   1st Qu.: 0.00    
##  Mode  :character   Mode  :character   Median : 0.00    
##                                        Mean   :16.07    
##                                        3rd Qu.:29.38    
##                                        Max.   :71.25
str(dp_data)
## 'data.frame':    52 obs. of  3 variables:
##  $ fecha            : chr  "7/31/2021" "7/31/2021" "7/31/2021" "7/31/2021" ...
##  $ cliente          : chr  "PRINTEL " "MAHLE" "MAGNA" "VARROC" ...
##  $ delay_performance: num  4.9 15.7 0 0 27.7 ...
dp_data$fecha<-as.Date(dp_data$fecha,format="%m/%d/%Y") 
str(dp_data)
## 'data.frame':    52 obs. of  3 variables:
##  $ fecha            : Date, format: "2021-07-31" "2021-07-31" ...
##  $ cliente          : chr  "PRINTEL " "MAHLE" "MAGNA" "VARROC" ...
##  $ delay_performance: num  4.9 15.7 0 0 27.7 ...

Delivery performance: distribución

ggplot(dp_data,aes(x=fecha, y=delay_performance,color=cliente))+
  geom_line()+
  labs(x="Fecha",y="Retraso en Minutos", color="Legend")+
  ggtitle("Retrasos en el desempeño, por tiempo")

A partir de esta gráfica, comparamos a los clientes, y como ha sido el retraso en minutos en cuantto a las entregas. Vemos como la que se mantiene constante es Varroc, puesto que no ha visto mucho diferencia ni “delay”. Sin embargo, en el caso de Mahle vemos retrasos duranett todo el año, pero que durante los últimos meses ha podido bajar. El caso de Printel, vemos como sus retrasos no exceden la media hora, pero que no han habido inidicios de retrasos desde abril.

dp_data<-dp_data[dp_data$cliente!="Magna",]
dp_data<-dp_data[dp_data$cliente!="Varroc",]

ggplot(dp_data,aes(x=fecha, y=delay_performance,fill=cliente))+
  geom_bar(stat="identity")+
  geom_hline(yintercept=33,linetype="dashed",color="black")+
  labs(x="Fecha",y="Retraso en minutos", color="Legend")+
  ggtitle("Retrasos en el desempeño por cliente")

A pesar de que esta disminuyendo el retraso con el cliente Mahle seguimos viendo que se encuentra por arriba del promedio, y que es mucho mayor que aquellos retrasos presentados con Printel.

Predicción del Desempeño de la Industria Automotriz

Estados Unidos

base_eua<-read.csv("/Users/elenavela/Downloads/us_motor_production_and_domestic_sales.csv")
summary(base_eua)
##       Year      Total_Production Production_Passenger_Cars
##  Min.   :2007   Min.   : 5710    Min.   :1924             
##  1st Qu.:2010   1st Qu.: 8709    1st Qu.:2745             
##  Median :2014   Median :10823    Median :3382             
##  Mean   :2014   Mean   :10077    Mean   :3326             
##  3rd Qu.:2017   3rd Qu.:11268    3rd Qu.:4061             
##  Max.   :2020   Max.   :12179    Max.   :4369             
##  Production_Commercial_Vehicles Domestic_Sales  Sales_Passenger_Cars
##  Min.   :3514                   Min.   : 7868   Min.   :2560        
##  1st Qu.:5820                   1st Qu.:10474   1st Qu.:3865        
##  Median :6891                   Median :12583   Median :4542        
##  Mean   :6751                   Mean   :11996   Mean   :4491        
##  3rd Qu.:8095                   3rd Qu.:13669   3rd Qu.:5184        
##  Max.   :8512                   Max.   :14128   Max.   :5610        
##  Sales_Commercial_Vehicles US_Unemployment US_Consumer_Confidence
##  Min.   : 4309             Min.   :3.680   Min.   :63.75         
##  1st Qu.: 6088             1st Qu.:4.685   1st Qu.:73.02         
##  Median : 7634             Median :5.980   Median :82.83         
##  Mean   : 7505             Mean   :6.430   Mean   :82.29         
##  3rd Qu.: 8964             3rd Qu.:8.088   3rd Qu.:92.67         
##  Max.   :10133             Max.   :9.610   Max.   :98.37         
##  US_Min_Hour_Wage
##  Min.   :5.500   
##  1st Qu.:7.250   
##  Median :7.250   
##  Mean   :7.025   
##  3rd Qu.:7.250   
##  Max.   :7.250

Se cambian los nombres de las columnas.

names (base_eua) = c("año", "prod_total", "prod_passenger", "prod_veh_comerciales", "ventas_domesticas", "ventas_passenger","ventas_comerciales","desempleo_usa","confianza_cons_usa","salario_hora_min_usa")
names (base_eua)
##  [1] "año"                  "prod_total"           "prod_passenger"      
##  [4] "prod_veh_comerciales" "ventas_domesticas"    "ventas_passenger"    
##  [7] "ventas_comerciales"   "desempleo_usa"        "confianza_cons_usa"  
## [10] "salario_hora_min_usa"
str(base_eua)
## 'data.frame':    14 obs. of  10 variables:
##  $ año                 : int  2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 ...
##  $ prod_total          : num  10752 8672 5710 7744 8662 ...
##  $ prod_passenger      : num  3867 3731 2196 2732 2978 ...
##  $ prod_veh_comerciales: num  6885 4941 3514 5012 5685 ...
##  $ ventas_domesticas   : num  12687 10108 7868 9020 10109 ...
##  $ ventas_passenger    : num  5197 4491 3558 3792 4146 ...
##  $ ventas_comerciales  : num  7490 5617 4309 5229 5963 ...
##  $ desempleo_usa       : num  4.62 5.8 9.28 9.61 8.93 8.08 7.36 6.16 5.28 4.88 ...
##  $ confianza_cons_usa  : num  85.6 63.8 66.3 71.8 67.3 ...
##  $ salario_hora_min_usa: num  5.5 6.2 6.9 7.25 7.25 7.25 7.25 7.25 7.25 7.25 ...

Primer modelo de regresión

En este caso, se escoge como variable dependiente las ventas de los carros passenger, por ello entiéndase los automóviles de uso cotidiano en Estados Unidos. Para esta variable dependiente, se han tomado las siguientes variables independientes, con el fin de notar su efecto en las ventas. Estas son:

1. Desempleo USA: este índice es calculado anualmente con la formula. (Unemployed ÷ Labor Force) x 100. Entre menor mejor. Se mide en porcentaje.

2. Confianza del consumidor de USA: índice que mide, a partir de una encuesta que tan optimistas o pesimistas se encuentran los consumidores sobre su situación financiera. Entre mayor, mejor; se mide por puntos.

3. Salario mínimo por hora: se mide en dólares. Está establecido a nivel federal.

4. Año: los años que se tienen de los datos, 2007-2020.

regresion1 <- lm (ventas_passenger ~ desempleo_usa + confianza_cons_usa + salario_hora_min_usa + año, data=base_eua)
summary (regresion1)
## 
## Call:
## lm(formula = ventas_passenger ~ desempleo_usa + confianza_cons_usa + 
##     salario_hora_min_usa + año, data = base_eua)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -850.29 -560.86   88.28  446.55  847.47 
## 
## Coefficients:
##                       Estimate Std. Error t value Pr(>|t|)   
## (Intercept)          646180.04  197959.50   3.264  0.00977 **
## desempleo_usa          -255.04     217.62  -1.172  0.27129   
## confianza_cons_usa       46.68      41.57   1.123  0.29062   
## salario_hora_min_usa   1394.06     697.08   2.000  0.07657 . 
## año                    -324.65     100.49  -3.231  0.01031 * 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 712.5 on 9 degrees of freedom
## Multiple R-squared:  0.5831, Adjusted R-squared:  0.3978 
## F-statistic: 3.147 on 4 and 9 DF,  p-value: 0.0707

Para que una variable tenga significancia en la regresión, se desea que p<0.05

Al correr la regresión las ventas de los autos tipo passenger, observamos que existe una variable con mayor significancia, la cual es “año”. Se ha decidido quitar la variable de “confianza_cons_usa” para ver la nueva significancia del modelo:

regresion1_2 <- lm (ventas_passenger ~ desempleo_usa + salario_hora_min_usa + año, data=base_eua)
summary (regresion1_2)
## 
## Call:
## lm(formula = ventas_passenger ~ desempleo_usa + salario_hora_min_usa + 
##     año, data = base_eua)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -971.5 -594.0   41.6  556.5  862.3 
## 
## Coefficients:
##                       Estimate Std. Error t value Pr(>|t|)  
## (Intercept)          558612.65  184300.29   3.031   0.0127 *
## desempleo_usa          -440.19     143.83  -3.060   0.0120 *
## salario_hora_min_usa   1554.95     691.01   2.250   0.0482 *
## año                    -279.22      93.18  -2.997   0.0134 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 721.8 on 10 degrees of freedom
## Multiple R-squared:  0.5247, Adjusted R-squared:  0.3821 
## F-statistic:  3.68 on 3 and 10 DF,  p-value: 0.05096

Ahora, vemos que las tres variables independientes si demuestran significancia pueso que la p es menor que 0.05. Se utilizará mejor este modelo para la predicción de ventas de autos tipo paassenger.

Para la tasa de desempleo se ha decidido tomar el promedio del 2007 al 2020, para poder predecir. Con el salario mínimo por hora, al ser establecido a nivel federal, se toma en cuenta la última cantidad: $7.25 dólares. Se quieren las predicciones para los siguientes cinco años, a partir del último año con datos (2020).

A partir de este modelo de regresión, se conoce lo siguiente a partir del estimate:
1. Al subir una unidad del índice de desempleo de EUA, se bajan 400.19 de las ventas de los vehículos passenger.
2. Al subir una unidad (dólar) del salario mínimo por hora de EUA, se incrementan las ventas por 1,554.95.
3. Con el pasar de cada año, se espera que las ventas de este tipo de auto disminuyan por 279.22.

datos_nuevos1 <- data.frame(desempleo_usa=6.43,salario_hora_min_usa=7.25,año=2021:2025)
predict(regresion1_2,datos_nuevos1)
##        1        2        3        4        5 
## 2746.480 2467.257 2188.035 1908.812 1629.589

¿Qué observamos?

primer <- plot(predict(regresion1_2,datos_nuevos1), type = "l", xlab = "Año",  ylab ="Ventas Passenger",  main="Predicción de Ventas")

En esta predicción de ventas de automóviles passenger, observamos que es hacia abajo. Es decir, tomando en cuenta una tasa de desempleo de 6.43 y el salario mínimo por hora de $7.25, se espera que las ventas de estos automóviles bajen en EUA del 2021 hasta 2025.

library(jtools)
## 
## Attaching package: 'jtools'
## The following object is masked from 'package:epiDisplay':
## 
##     summ

¿Cómo es el impacto de dichas variables explanatorias sobre la variable dependiente?

effect_plot(regresion1_2,pred=desempleo_usa,interval=TRUE)

Si tomamos en cuenta la tasa de desempleo de EUA, y su relación con las ventas de los automóviles passenger, observamos que esta es NEGATIVA. Debido a que al crecer la tasa de desempleo, bajan las ventas.

effect_plot(regresion1_2,pred=salario_hora_min_usa,interval=TRUE)

Sin embargo, se conoce que al subir el salario mínimo por hora, se espera que las ventas de los automóviles passenger incrementan. La relación

effect_plot(regresion1_2,pred=año,interval=TRUE)

Con el pasar de los años las ventas han decrecido, la relación es NEGATIV.

Segundo modelo de regresión

En este caso, se escoge como variable dependiente las ventas de los carros comerciales, por ello entiéndase los cualquier tipo de vehículo de motor utilizado para transportar mercancías o pasajeros en Estados Unidos. Para esta variable dependiente, se han tomado las siguientes variables independientes, con el fin de notar su efecto en las ventas. Estas son:
1. Desempleo USA: este índice es calculado anualmente con la formula. (Unemployed ÷ Labor Force) x 100. Entre menor mejor. Se mide en porcentaje.

2. Confianza del consumidor de USA: índice que mide, a partir de una encuesta que tan optimistas o pesimistas se encuentran los consumidores sobre su situación financiera. Entre mayor, mejor; se mide por puntos.

3. Salario mínimo por hora: se mide en dólares. Está establecido a nivel federal.

4. Año: los años que se tienen de los datos, 2007-2020.

regresion2 <- lm (ventas_comerciales ~ desempleo_usa + confianza_cons_usa + salario_hora_min_usa + año, data=base_eua)
summary (regresion2)
## 
## Call:
## lm(formula = ventas_comerciales ~ desempleo_usa + confianza_cons_usa + 
##     salario_hora_min_usa + año, data = base_eua)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -731.58  -87.12   57.56  160.03  513.82 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -588817.41   97672.75  -6.028 0.000196 ***
## desempleo_usa           -147.24     107.37  -1.371 0.203504    
## confianza_cons_usa        50.55      20.51   2.464 0.035896 *  
## salario_hora_min_usa    -839.15     343.94  -2.440 0.037374 *  
## año                      297.49      49.58   6.000 0.000203 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 351.6 on 9 degrees of freedom
## Multiple R-squared:  0.9732, Adjusted R-squared:  0.9613 
## F-statistic: 81.82 on 4 and 9 DF,  p-value: 4.516e-07

Aunque en la mayoría de las variables demuestran significancia en las ventas de autos comerciales, se ha decidido eliminar la variable dependendiente de “Índice de Desempleo de EUA”, debido a que su nivel de p no demuestra mucha significancia (0.20)

regresion2_2 <- lm (ventas_comerciales ~ confianza_cons_usa + salario_hora_min_usa + año, data=base_eua)
summary (regresion2_2)
## 
## Call:
## lm(formula = ventas_comerciales ~ confianza_cons_usa + salario_hora_min_usa + 
##     año, data = base_eua)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -803.8 -176.2   67.8  197.9  549.5 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -607695.96  100864.56  -6.025 0.000128 ***
## confianza_cons_usa        71.87      13.96   5.148 0.000433 ***
## salario_hora_min_usa   -1117.90     289.39  -3.863 0.003145 ** 
## año                      306.50      51.26   5.979 0.000136 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 366.7 on 10 degrees of freedom
## Multiple R-squared:  0.9676, Adjusted R-squared:  0.9579 
## F-statistic: 99.69 on 3 and 10 DF,  p-value: 9.47e-08

Viendo este nuevo modelo de regresión para las ventas de autos comerciales, vemos que ahora todas las variables independientes seleccionadas tienen una mayor significancia. Esto nos dice el modelo:
1. Al subir una unidad del índice de confianza del consumidor de EUA, incrementa 71.87 de las ventas de los vehículos commercial.
2. Al subir una unidad (dólar) del salario mínimo por hora de EUA, disminuyen las ventas por 1,117.9.
3. Con el pasar de cada año, se espera que las ventas de este tipo de auto aumentan por 306.5.

Para el índice de confianza del consumidor, se ha decidido tomar el promedio del 2007 al 2020, para poder predecir. Con el salario mínimo por hora, al ser establecido a nivel federal, se toma en cuenta la última cantidad: $7.25 dólares. Se quieren las predicciones para los siguientes cinco años, a partir del último año con datos (2020).

datos_nuevos2 <- data.frame(confianza_cons_usa=82.3,salario_hora_min_usa=7.25,año=2021:2025)
predict(regresion2_2,datos_nuevos2)
##         1         2         3         4         5 
##  9552.768  9859.269 10165.770 10472.271 10778.772

¿Qué observamos?

segundo <- plot(predict(regresion2_2,datos_nuevos2), type = "l", xlab = "Año",  ylab ="Ventas Comerciales",  main="Predicción de Ventas")

En esta predicción de ventas de automóviles comerciales, observamos que es hacia arriba. Es decir, tomando en cuenta un índice de confianza de 82.3 y el salario mínimo por hora de $7.25, se espera que las ventas de estos automóviles suban en EUA del 2021 hasta 2025. En el caso de las ventas de automóviles comerciales, notamos que el pasar de los años y el índice de confianza del consumidor son las variable que más impactan, siguiendo por el salario mínimo por hora.

effect_plot(regresion2_2,pred=confianza_cons_usa,interval=TRUE)

El índice de confianza del consumidor de EUA, y su relación con las ventas de los automóviles comerciales, observamos que esta es POSITIVA. Debido a que al crecer la confianza, incrementan las ventas. A comparación de la gráfica de ventas passenger con el nivel de confianza, observamos que en este caso la pendiente es más pronunciada, por lo que vemos un mayor impacto.

¿Cómo es el impacto de dichas variables explanatorias sobre la variable dependiente?

effect_plot(regresion2_2,pred=salario_hora_min_usa,interval=TRUE)

Igualmente, al subir el salario mínimo por hora, las ventas de los automóviles comerciales disminuyen. La relación es NEGATIVA. Este podría ser un tema interesante a investigar.

HIPÓTESIS, al subir el salario mínimo, las personas sienten menos necesidad de buscar nuevas opciones de generar dinero, de diferentes maneras.

effect_plot(regresion2_2,pred=año,interval=TRUE)

Con el pasar de los años las ventas demuestran incrementar, la relación es POSITIVA.

México

Se importa la base de datos de la industria automotriz en México.

base_mex<-read.csv("/Users/elenavela/Downloads/industria_automotriz_mx.csv")
summary(base_mex)
##       año       unidades_exportacion unidades_produccion unidades_ventas  
##  Min.   :2017   Min.   :2681806      Min.   :3028481     Min.   : 950063  
##  1st Qu.:2018   1st Qu.:2706980      1st Qu.:3040178     1st Qu.:1014735  
##  Median :2019   Median :3253859      Median :3811068     Median :1317931  
##  Mean   :2019   Mean   :3096421      Mean   :3546297     Mean   :1248952  
##  3rd Qu.:2020   3rd Qu.:3388305      3rd Qu.:3918603     3rd Qu.:1427086  
##  Max.   :2021   Max.   :3451157      Max.   :3933154     Max.   :1534943  
##  empleados_automotriz compensaciones_empleados  salario_min  
##  Min.   :784000       Min.   :506716           Min.   :2435  
##  1st Qu.:831000       1st Qu.:520113           1st Qu.:2688  
##  Median :840000       Median :527878           Median :3123  
##  Mean   :833200       Mean   :542087           Mean   :3261  
##  3rd Qu.:844000       3rd Qu.:567228           3rd Qu.:3748  
##  Max.   :867000       Max.   :588498           Max.   :4310
str(base_mex)
## 'data.frame':    5 obs. of  7 variables:
##  $ año                     : int  2017 2018 2019 2020 2021
##  $ unidades_exportacion    : int  3253859 3451157 3388305 2681806 2706980
##  $ unidades_produccion     : int  3933154 3918603 3811068 3040178 3028481
##  $ unidades_ventas         : int  1534943 1427086 1317931 950063 1014735
##  $ empleados_automotriz    : int  784000 844000 867000 840000 831000
##  $ compensaciones_empleados: int  506716 527878 520113 567228 588498
##  $ salario_min             : num  2435 2688 3123 3748 4310

Se cambiana los nombres a las variables.

names (base_mex) = c("año", "uds_export", "uds_prod", "uds_ventas", "empleados_auto", "compensaciones_empleados","salario_min")
names (base_mex)
## [1] "año"                      "uds_export"              
## [3] "uds_prod"                 "uds_ventas"              
## [5] "empleados_auto"           "compensaciones_empleados"
## [7] "salario_min"
str(base_mex)
## 'data.frame':    5 obs. of  7 variables:
##  $ año                     : int  2017 2018 2019 2020 2021
##  $ uds_export              : int  3253859 3451157 3388305 2681806 2706980
##  $ uds_prod                : int  3933154 3918603 3811068 3040178 3028481
##  $ uds_ventas              : int  1534943 1427086 1317931 950063 1014735
##  $ empleados_auto          : int  784000 844000 867000 840000 831000
##  $ compensaciones_empleados: int  506716 527878 520113 567228 588498
##  $ salario_min             : num  2435 2688 3123 3748 4310

Primer modelo de regresión

En este caso, se escoge como variable dependiente las unidades producidas en México.

regresion3 <- lm (uds_prod ~ año + empleados_auto, data=base_mex)
summary (regresion3)
## 
## Call:
## lm(formula = uds_prod ~ año + empleados_auto, data = base_mex)
## 
## Residuals:
##       1       2       3       4       5 
##   18339   -3743   81649 -225423  129178 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)  
## (Intercept)     6.401e+08  1.379e+08   4.641   0.0434 *
## año            -3.175e+05  6.899e+04  -4.603   0.0441 *
## empleados_auto  5.418e+00  3.572e+00   1.517   0.2686  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 193000 on 2 degrees of freedom
## Multiple R-squared:  0.9156, Adjusted R-squared:  0.8311 
## F-statistic: 10.84 on 2 and 2 DF,  p-value: 0.08443

Descripción de variable:
Dependiente: tenemos las unidades de autos pproducidas en México, se miden en unidades (carros). Independiente: los años y la cantidad de personass que trabajan en la industria automotriz.

Viendo el modelo de regresión actual, observamos que la variable independiente que tiene más impacto en las unidades producidas es el año (posiblemente por temas de momento). Se espera que al pasar los años, las unidades producidas bajen. Con la variable empleados de la industria, podríamos esperar que suban las unidades producidas al subir la cantidad de empleados (sin embargo, esta variable no es muy significativa).

La cantidad de empleados de la industria se encuentra entre los 780,000 y los 870,000, se escoge 825,000 para el análisis. Con los años próximos.

datos_nuevos3 <- data.frame(año=2022:2025,empleados_auto=825000)
predict(regresion3,datos_nuevos3)
##       1       2       3       4 
## 2549259 2231721 1914184 1596647

¿Qué observamos?

tercer <- plot(predict(regresion3,datos_nuevos3), type = "l", xlab = "Año",  ylab ="Unidades producidas",  main="Predicción de Unidades Producidas en México")

En esta predicción de unidades de automóviles producidas en México, observamos que es hacia abajo. Esto es, tomando en cuenta el pasar de los años, y un promedio de colaboradores de 825,000.

¿Cómo es el impacto de dichas variables explanatorias sobre la variable dependiente?

effect_plot(regresion3,pred=año,interval=TRUE)

La relación del pasar de los años es NEGATIVA, debido a que se espera que disminuya la cantidad de producción.

effect_plot(regresion3,pred=empleados_auto,interval=TRUE)

Vemos una relación POSITIVA, en este caso; al subir el número de empleados de la industria automotriz en México, incrementan las unidades producidas en México.

Segundo modelo de regresión

En este caso, se escoge como variable dependiente las unidades exportadas.

regresion4 <- lm (uds_export ~ año + uds_prod, data=base_mex)
summary (regresion4)
## 
## Call:
## lm(formula = uds_export ~ año + uds_prod, data = base_mex)
## 
## Residuals:
##      1      2      3      4      5 
## -44976  51491  -7169  39767 -39114 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)  
## (Intercept) -2.377e+08  9.430e+07  -2.520   0.1279  
## año          1.173e+05  4.646e+04   2.524   0.1276  
## uds_prod     1.129e+00  1.564e-01   7.223   0.0186 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 62600 on 2 degrees of freedom
## Multiple R-squared:  0.986,  Adjusted R-squared:  0.972 
## F-statistic: 70.38 on 2 and 2 DF,  p-value: 0.01401

Descripción de variable:
Dependiente: las unidades de carros que son exportados provenientes de México.
Independiente: los años tenemos las unidades de autos pproducidas en México, se miden en unidades (carros).

En este caso, el año no tiene tantta significancia como en el modelo pasado. Sin embargo, al querer ver las unidades de autos producidas en México, ahora como variable independiente, vemos que si tiene un buen nivel de significancia (p menor a 0.05). Obtenemos que al pasar los años, y al subir las unidades prooducidas en México, la cantidad de autos exportados incrementa.

Se utilizan los próximos 5 años, en el cuál podemos ver un crecimiento (si tomamos en cuenta las dos variables descritas).

datos_nuevos4 <- data.frame(año=2022:2026,uds_prod=3000000)
predict(regresion4,datos_nuevos4)
##       1       2       3       4       5 
## 2831191 2948457 3065724 3182990 3300256

¿Qué observamos?

cuarto <- plot(predict(regresion4,datos_nuevos4), type = "l", xlab = "Año",  ylab ="Unidades exportadas",  main="Predicción de Unidades Exportadas de México")

Notamos que al tener esa información de las variables independientes, podríamos esperar un crecimiento en las unidades exportadas de México.

¿Cómo es el impacto de dichas variables explanatorias sobre la variable dependiente?

effect_plot(regresion4,pred=año,interval=TRUE)

Vemos una relación POSITIVA, al pasar los años se espera que las exportaciones crezcan.

effect_plot(regresion4,pred=uds_prod,interval=TRUE)

Vemos una relación POSITIVA, al subir las unidades producidas en México se espera que las exportaciones crezcan.

Pronóstico Industria Automotriz: Estados Unidos

Misma base de datos previamente utilizada

summary(base_eua)
##       año         prod_total    prod_passenger prod_veh_comerciales
##  Min.   :2007   Min.   : 5710   Min.   :1924   Min.   :3514        
##  1st Qu.:2010   1st Qu.: 8709   1st Qu.:2745   1st Qu.:5820        
##  Median :2014   Median :10823   Median :3382   Median :6891        
##  Mean   :2014   Mean   :10077   Mean   :3326   Mean   :6751        
##  3rd Qu.:2017   3rd Qu.:11268   3rd Qu.:4061   3rd Qu.:8095        
##  Max.   :2020   Max.   :12179   Max.   :4369   Max.   :8512        
##  ventas_domesticas ventas_passenger ventas_comerciales desempleo_usa  
##  Min.   : 7868     Min.   :2560     Min.   : 4309      Min.   :3.680  
##  1st Qu.:10474     1st Qu.:3865     1st Qu.: 6088      1st Qu.:4.685  
##  Median :12583     Median :4542     Median : 7634      Median :5.980  
##  Mean   :11996     Mean   :4491     Mean   : 7505      Mean   :6.430  
##  3rd Qu.:13669     3rd Qu.:5184     3rd Qu.: 8964      3rd Qu.:8.088  
##  Max.   :14128     Max.   :5610     Max.   :10133      Max.   :9.610  
##  confianza_cons_usa salario_hora_min_usa
##  Min.   :63.75      Min.   :5.500       
##  1st Qu.:73.02      1st Qu.:7.250       
##  Median :82.83      Median :7.250       
##  Mean   :82.29      Mean   :7.025       
##  3rd Qu.:92.67      3rd Qu.:7.250       
##  Max.   :98.37      Max.   :7.250
library(foreign)
library(dplyr)        # data manipulation 
library(forcats)      # to work with categorical variables
library(ggplot2)      # data visualization 
library(janitor)      # data exploration and cleaning 
library(Hmisc)        # several useful functions for data analysis 
#install.packages("Hmisc")
#install.packages("naniar")
library(naniar) 
#install.packages("dlookr")
#install.packages("pollster")
library(dlookr) 
library(pollster)
#install.packages("descr")
library(descr)
library(data.table)
library(graphics)
library(epiDisplay)
library(tidyr)
library(psych)        # functions for multivariate analysis 
library(corrplot)     # correlation plots
library(jtools)       # presentation of regression analysis 
library(lmtest)       # diagnostic checks - linear regression analysis 
library(car)          # diagnostic checks - linear regression analysis
#install.packages("olsrr")
library(olsrr)        # diagnostic checks - linear regression analysis 
library(kableExtra)   # HTML table attributes
library(tseries)   # time series analysis and computational finance 
library(forecast)  # provides methods and tools for displaying and analyzing univariate time series forecast
#install.packages("astsa")
library(astsa)     # applied statistical time series analysis
library(plyr) 
plot(base_eua$año,base_eua$prod_veh_comerciales, type="l",col="blue", lwd=1.5, xlab ="Año",ylab ="Dólares", main = "Ventas Anuales de Vehículos en EUA")
lines(base_eua$año,base_eua$prod_veh_comerciales,col="red",lty=3)
legend("topleft", legend=c("Domestic Commercial Sales", "Production Commercial Vehicles"),
       col=c("blue", "red"), lty = 1:2, cex=0.8)

En el presente gráfico, vemos la tendencia a través de los años de las ventas de los vehículos passenger y los comerciales, al juntarlos obtenemos lo presentado. Interesantemente, en el 2009 hubo una diminución de ventas debido a la crisis que empezó en EUA, e imapctó todo el mundo. Después observamos como sigue creciendo en los añoos, hasta el 2019 (por COVID-19) donde disminuyeron. Se esperaría que ahora que la economía va mejorando, saliendo del problema de sanidad.

Pronóstico Moving Average

summary(ma_model<-arma(base_eua$prod_total,order=c(0,1)))
## 
## Call:
## arma(x = base_eua$prod_total, order = c(0, 1))
## 
## Model:
## ARMA(0,1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -3425.7 -1313.0   141.6  1100.1  1449.5 
## 
## Coefficient(s):
##            Estimate  Std. Error  t value Pr(>|t|)    
## ma1       6.756e-01   1.610e-01    4.195 2.73e-05 ***
## intercept 1.010e+04   6.096e+02   16.569  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Fit:
## sigma^2 estimated as 2094390,  Conditional Sum-of-Squares = 25229390,  AIC = 247.5
ma_model_forecast<-forecast(ma_model$fitted,h=3,level=c(95))
ma_model_forecast
##    Point Forecast    Lo 95    Hi 95
## 15       10455.67 8568.350 12342.99
## 16       10455.67 8438.084 12473.26
## 17       10455.67 8315.732 12595.61

Podemos ver que en la industria Automotriz de USA, específicamente en ventas de autos (passenger y comerciales), tenemos una tendencia continua y que nuestros rangos (por los intervalos de confianza) van disminuyendo, pero las predicciones se encuentran de la siguiente manera:
1. Primer año: desde los $8,568.35 hasta los $12,342.99 en ventas de carros (passenger y comerciales).
2. Segundo año: desde los $8,438.08 hasta los $12,473.26 en ventas de carros (passenger y comerciales).
3. Tercer año: desde los $8,315.73 hasta los $12,595.61 en ventas de carros (passenger y comerciales).

Pronóstico Industria Automotriz: México

summary(base_mex)
##       año         uds_export         uds_prod         uds_ventas     
##  Min.   :2017   Min.   :2681806   Min.   :3028481   Min.   : 950063  
##  1st Qu.:2018   1st Qu.:2706980   1st Qu.:3040178   1st Qu.:1014735  
##  Median :2019   Median :3253859   Median :3811068   Median :1317931  
##  Mean   :2019   Mean   :3096421   Mean   :3546297   Mean   :1248952  
##  3rd Qu.:2020   3rd Qu.:3388305   3rd Qu.:3918603   3rd Qu.:1427086  
##  Max.   :2021   Max.   :3451157   Max.   :3933154   Max.   :1534943  
##  empleados_auto   compensaciones_empleados  salario_min  
##  Min.   :784000   Min.   :506716           Min.   :2435  
##  1st Qu.:831000   1st Qu.:520113           1st Qu.:2688  
##  Median :840000   Median :527878           Median :3123  
##  Mean   :833200   Mean   :542087           Mean   :3261  
##  3rd Qu.:844000   3rd Qu.:567228           3rd Qu.:3748  
##  Max.   :867000   Max.   :588498           Max.   :4310
plot(base_mex$año,base_mex$uds_ventas, type="l",col="blue", lwd=1.5, xlab ="Año",ylab ="Unidades", main = "Ventas de Vehículos Anuales MX")
lines(base_mex$año,base_mex$uds_export,col="red",lty=3) 
legend("topleft", legend=c("Vehículos"),
       col=c("blue", "red"), lty = 1:2, cex=0.8)

En las presentes gráficas observamos que tantas unidades de carros que se venden, considerando México. Vemos como el año “estrella” de los últimos 5 años fue el 2018, y el que bajó considerablemente en ventas fue el 2020, probablemente debido al incremento del Covid-19.

Pronóstico Moving Average

ma_model<- base_mex
ma_model_forecast<-forecast(ma_model$uds_ventas,h=3,level=c(95))
ma_model_forecast
##   Point Forecast    Lo 95     Hi 95
## 6         887801 675739.5 1099862.5
## 7         779944 567882.5  992005.5
## 8         672087 460025.5  884148.5

Podemos ver que en la industria automotriz de México, específicamente en ventas, tenemos una tendencia continua y que nuestros rangos (por los intervalos de confianza) disminuyen, por lo que las predicciones se encuentran de la siguiente manera:
1. Primer año: desde las 675,739.5 unidades hasta las 1,099,862.5 uidades en ventas de carros en México.
2. Segundo año: desde las 567,882.5 unidades hasta las 992,005.5 unidades en ventas de carros en México.
3. Tercer año: desde las 460,025.5 hasta las 884,148.5 unidades en ventas de carros en México.

Pronóstico Scrap FORM

plot(scrap_finalya$fecha, scrap_finalya$cantidad, type="l",col="red", lwd=1.5, xlab ="Fecha",ylab ="Cantidad", main = "Cantidad de Scrap por Fecha")
lines(scrap_finalya$fecha,scrap_finalya$cantidad,col="red",lty=3)
legend("topleft", legend=c("Scrap"),
       col=c("red"), lty = 1:2, cex=0.8)

Aquí vemos como progresó la cantidad de scrap durante el mes de agosto, presentando ciertos picos en ciertos días específicos. Vemos que hubo uno importante alrededor del 11 o 12 de agosto, y otra cantidad de scrap aún más relevante por el 24 de agosto. Por ora parte, había otros días que no presentaron cantidad de scrap.

(ma_model<-arma(scrap_finalya$cantidad,order=c(0,1)))
## 
## Call:
## arma(x = scrap_finalya$cantidad, order = c(0, 1))
## 
## Coefficient(s):
##       ma1  intercept  
##   0.01931    6.71395
ma_model_forecast<-forecast(ma_model$fitted,h=3,level=c(95))
ma_model_forecast
##     Point Forecast    Lo 95    Hi 95
## 251       6.714443 6.264717 7.164169
## 252       6.714443 6.264717 7.164169
## 253       6.714443 6.264717 7.164169

Para las siguientes tres fechas se presenta una cantidad de scrap igual. Con una cantidad mínima de 6.26 y un máximo de 7.16.

Pronóstico Merma FORM

# Sumar el Total de KilosxMes
merma <- c(14560,22830,22470,18820,23410,18280,19370,32100,13586)

merma_st <- ts(data = merma, start = c(2022,1), frequency = 12)
merma_st
##        Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep
## 2022 14560 22830 22470 18820 23410 18280 19370 32100 13586
modelo <- auto.arima(merma_st)
modelo
## Series: merma_st 
## ARIMA(0,0,0) with non-zero mean 
## 
## Coefficients:
##            mean
##       20602.889
## s.e.   1736.893
## 
## sigma^2 = 30544665:  log likelihood = -89.8
## AIC=183.59   AICc=185.59   BIC=183.99
pronostico <- forecast(modelo, level=c(95), h=3)
pronostico
##          Point Forecast    Lo 95    Hi 95
## Oct 2022       20602.89 9770.711 31435.07
## Nov 2022       20602.89 9770.711 31435.07
## Dec 2022       20602.89 9770.711 31435.07
plot(pronostico)

Para Form al desarrollar el pronóstico de Merma vemos que tenemos que para los próximos periodos de octubre, noviembre y diciembre tendremos una cantidad constante de 20,602.89 kilos de merma y que nuestro intervalo de confianza cae entre 9770.711 a 31435.07.Interpretando la información vemos que a comparación del último mes de septiembre, los kilos crecerán.

K-Means Clustering

Descargar librerías

library(foreign)
library(dplyr)        # data manipulation 
library(ggplot2)      # data visualization 
#install.packages("psych")
library(psych)        # functions for multivariate analysis 
library(corrplot)     # correlation plots
library(jtools)       # presentation of regression analysis 
library(lmtest)       # diagnostic checks - linear regression analysis 
library(car)          # diagnostic checks - linear regression analysis
library(factoextra)   # provides functions to extract and visualize the output of exploratory multivariate data analyses
#install.packages("ggfortify")
library(ggfortify)    # data visualization tools for statistical analysis results

Se importa la base de datos de BAJAS ya limpia.

#file.choose()
bajas<-read.csv("/Users/elenavela/bajas_final.csv")
summary(bajas)
##     nombre               edad          duracion       salario_diario 
##  Length:232         Min.   :18.00   Min.   :   0.00   Min.   :144.4  
##  Class :character   1st Qu.:23.00   1st Qu.:   9.00   1st Qu.:180.7  
##  Mode  :character   Median :28.50   Median :  20.50   Median :180.7  
##                     Mean   :30.77   Mean   :  75.82   Mean   :177.9  
##                     3rd Qu.:37.00   3rd Qu.:  47.25   3rd Qu.:180.7  
##                     Max.   :61.00   Max.   :1966.00   Max.   :500.0  
##     estado            e.civil.            genero              alta          
##  Length:232         Length:232         Length:232         Length:232        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##  motivo_baja           puesto         
##  Length:232         Length:232        
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
## 

¿Cuántos NA tengo por variables?

sapply(bajas,function(x) sum(is.na(x)))
##         nombre           edad       duracion salario_diario         estado 
##              0              0              0              0              0 
##       e.civil.         genero           alta    motivo_baja         puesto 
##              0              0              0              0              0

1. Edad y duración - 4 Clusters

bajas_new<-bajas
bajas_new<-subset(bajas_new,select = -c(alta,estado,nombre))
summary(bajas_new)
##       edad          duracion       salario_diario    e.civil.        
##  Min.   :18.00   Min.   :   0.00   Min.   :144.4   Length:232        
##  1st Qu.:23.00   1st Qu.:   9.00   1st Qu.:180.7   Class :character  
##  Median :28.50   Median :  20.50   Median :180.7   Mode  :character  
##  Mean   :30.77   Mean   :  75.82   Mean   :177.9                     
##  3rd Qu.:37.00   3rd Qu.:  47.25   3rd Qu.:180.7                     
##  Max.   :61.00   Max.   :1966.00   Max.   :500.0                     
##     genero          motivo_baja           puesto         
##  Length:232         Length:232         Length:232        
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
## 

Se toman en cuenta la edad y la duración dentro de la empresa.

bajas_edaddur_norm<-scale(bajas_new[1:2]) 
fviz_nbclust(bajas_edaddur_norm, kmeans, method="wss")+ 
  geom_vline(xintercept=4, linetype=2)+         
  labs(subtitle = "Elbow method")  
## Registered S3 methods overwritten by 'broom':
##   method            from  
##   tidy.glht         jtools
##   tidy.summary.glht jtools

Se toman en cuenta, primeramente, solo 4 clusters.

edad_cluster1<-kmeans(bajas_edaddur_norm,4)
edad_cluster1
## K-means clustering with 4 clusters of sizes 44, 114, 70, 4
## 
## Cluster means:
##         edad    duracion
## 1  1.5981208 -0.08817072
## 2 -0.8117882 -0.11434758
## 3  0.2558812 -0.13781428
## 4  1.0787124  6.64053370
## 
## Clustering vector:
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
##   3   3   2   2   3   1   3   3   1   2   2   3   2   3   3   2   2   2   2   2 
##  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
##   3   4   2   3   1   2   3   2   2   2   3   2   2   2   3   2   2   2   3   2 
##  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
##   2   3   2   2   2   2   2   2   1   1   1   4   2   1   2   2   1   2   2   2 
##  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
##   2   2   3   2   2   1   2   1   2   2   2   2   3   2   3   2   3   2   2   1 
##  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
##   4   2   1   2   2   2   1   2   2   2   3   1   1   2   2   2   1   3   2   3 
## 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
##   2   2   1   3   2   2   3   4   3   2   3   3   2   3   3   3   1   1   1   2 
## 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
##   2   2   2   2   3   2   3   1   2   3   2   1   2   2   2   3   1   3   1   3 
## 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
##   3   1   1   1   2   3   3   3   2   2   2   3   1   1   2   3   2   3   1   1 
## 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 
##   1   1   3   1   2   2   3   2   2   1   3   1   2   1   3   3   1   3   3   2 
## 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 
##   2   2   3   1   3   3   2   2   3   3   3   3   3   2   3   2   3   3   2   3 
## 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
##   1   3   2   2   2   1   2   2   2   3   3   2   2   3   3   3   1   2   2   3 
## 221 222 223 224 225 226 227 228 229 230 231 232 
##   2   2   2   2   1   3   1   2   2   2   2   2 
## 
## Within cluster sum of squares by cluster:
## [1] 20.59876 23.88535 25.54502 15.68265
##  (between_SS / total_SS =  81.4 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"
fviz_cluster(edad_cluster1,data=bajas_edaddur_norm)

Al observar vemos cuatro clusters que toman en cuenta la edad y la duración:
1. El rojo (será llamado “Senior”) es un cluster que observamos que son personas de edad mediana-alta y que durán relativamente poco tiempo, entran dentro del 25% con menor duración.
2. El verde (“Beginner”) es el segmento más joven y que de las personas (en general) menos han durado. Se puede considerar que los jóvenes son los que menos duran en Form a partir de este cluster.
3. El azul (“Junior”) resulta el cluster intermedio de los de menor duración. Son personas en un rango de edad medio que también son parte del 25% inferior que más han durado.
4. El morado (“Expert”) es el cluster que más se aparta de los demás; son personas con un mayor rango de edad pero que se alejan de los más jóvenes, y que han durado mucho má tiempo. Se encuentran en el 50% superior del tiempo de duración.

bajas_new2<-bajas_new
bajas_new2$Clusters<-edad_cluster1$cluster
bajas_new3<-bajas_new2 %>% group_by(Clusters) %>% summarise(edad=max(edad)) %>% arrange(desc(edad))
bajas_new2$Cluster_Names<-factor(bajas_new2$Clusters,levels = c(1,2,3,4), 
                              labels=c("Senior", "Beginner", "Junior", "Expert"))
#install.packages("dplyr")
library(dplyr)
bajas_new4 <- bajas_new2 %>% group_by(Cluster_Names) %>% dplyr::summarise(edad_años=max(edad), 
duracion=mean(duracion),
Count=n())
clusters<-as.data.frame(bajas_new4)
clusters
##   Cluster_Names edad_años   duracion Count
## 1        Senior        57   56.78409    44
## 2      Beginner        28   51.13158   114
## 3        Junior        39   46.06429    70
## 4        Expert        61 1509.75000     4

A partir de la tabla, observamos las edades máximas encontradas en cada clúster. Conocemos que la persona con mayor edad en los expert tiene 61, en senior tiene 57, en junior tiene 39 y en beginner 27.

Igualmente, vemos la duración máxima que ha durado alguien de cada cluster, comparamos el máximo del mayor, expert, que han sido más de 1,500 días, con el máximo del menor, junior, que han sido 46 días.

ggplot(bajas_new4,aes(x=reorder(Cluster_Names,Count),y=Count,fill=Cluster_Names)) +
  geom_bar(stat="identity")

Existe una mayor cantidad de “beginners”. Muy poca gente ha sido “Expert”.

ggplot(bajas_new4, aes(x=Cluster_Names,y=edad_años,fill= Cluster_Names,label=round(edad_años,digits=2))) + 
  geom_col() + 
  geom_text()

Comparamos la edad máxima en cada clúster (descrito anteriormente).

ggplot(bajas_new4,aes(x=Cluster_Names,y=duracion,fill= Cluster_Names,label=round(duracion,digits=2))) + 
  geom_col() + 
  geom_text()

Comparamos los días máximos en cada clúster (descrito anteriormente).

2. Edad y duración - 5 Clusters

Debido a que una gráfica anterior indicaba la posibilidad de tener otro clúster que describa a otro grupo, hacemos lo mismo con 5 segmentos, en vez de 4.

bajas_edaddur_norm2<-scale(bajas_new[1:2]) 
fviz_nbclust(bajas_edaddur_norm2, kmeans, method="wss")+ 
  geom_vline(xintercept=4, linetype=2)+         
  labs(subtitle = "Elbow method")  

edad_cluster2<-kmeans(bajas_edaddur_norm,5)
edad_cluster2
## K-means clustering with 5 clusters of sizes 33, 83, 45, 67, 4
## 
## Cluster means:
##         edad    duracion
## 1  1.7735967 -0.02984451
## 2 -0.9501264 -0.17750350
## 3  0.6732203 -0.26412700
## 4 -0.2131039  0.01554089
## 5  1.0787124  6.64053370
## 
## Clustering vector:
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
##   4   3   2   2   4   1   4   4   1   2   2   3   2   4   4   2   2   2   2   2 
##  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
##   4   5   2   3   1   2   3   2   2   2   3   4   2   2   4   2   2   4   4   2 
##  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
##   4   4   2   2   2   2   2   2   1   1   3   5   4   1   2   2   1   2   2   2 
##  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
##   2   2   3   4   4   3   2   1   4   2   2   2   3   4   3   2   3   2   2   1 
##  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
##   5   4   1   4   2   4   3   2   2   2   4   3   1   2   4   4   1   4   4   4 
## 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
##   2   2   3   4   4   4   3   5   3   2   4   4   2   3   3   4   1   1   1   2 
## 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
##   4   2   2   2   4   2   3   1   4   3   2   1   2   4   4   4   1   4   1   3 
## 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
##   4   3   1   1   4   4   3   4   2   4   4   3   1   1   2   3   2   3   3   3 
## 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 
##   1   1   4   1   2   4   4   2   4   1   4   1   4   3   4   3   1   3   3   2 
## 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 
##   2   2   4   1   3   3   2   2   4   3   3   3   4   2   4   4   4   4   2   4 
## 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
##   1   3   2   2   2   1   4   2   2   3   4   2   2   3   3   3   1   4   4   4 
## 221 222 223 224 225 226 227 228 229 230 231 232 
##   2   2   2   4   3   3   3   2   2   2   2   2 
## 
## Within cluster sum of squares by cluster:
## [1] 15.975197  6.410735  3.762808 31.155779 15.682652
##  (between_SS / total_SS =  84.2 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"
fviz_cluster(edad_cluster2,data=bajas_edaddur_norm2)

Comparando esta gráfica de 5 segmentos contra la anteriormente vista de 4. En esta observamos que separa un segmento que deja un “espacio” que antes no existía; separa y crea un segmento que han durado MUY poco, y que son de mediana edad:
1. El rojo (llamado “Senior”): tiene de baja a mediana duración y un rango de edad alta.

  1. El verde izquierdo (llamado “Beginner”): son las personas más jóvenes y que también han durado poco tiempo. Se quedan aproximadamente por el 10% inferior.

  2. El verde derecho (llamado “Unmotivated”) son personas de mediana edad que han durado muy poco tiempo en la empresa, parte del 5% inferior de duración.

  3. El azul (llamado “Junior”): al hacer el nuevo clúster nos damos cuenta de que existe un grupo de personas de corta-mediana edad que duran por lo general una buena cantidad de tiempo (a comparación de otros), aún más poco que los Senior.

  4. El morado (llamado “Expert”): es el cluster que más se aparta de los demás; son personas con un mayor rango de edad pero que se alejan de los más jóvenes, y que han durado mucho má tiempo. Se encuentran en el 50% superior del tiempo de duración.

bajas_new6<-bajas_new
bajas_new6$Clusters<-edad_cluster2$cluster
bajas_new7<-bajas_new6 %>% group_by(Clusters) %>% summarise(edad=max(edad)) %>% arrange(desc(edad))
bajas_new6$Cluster_Names<-factor(bajas_new6$Clusters,levels = c(1,2,3,4,5), 
                              labels=c("Senior", "Beginner", "Unmotivated", "Junior","Expert"))

Análisis con variables cualitativas

ESTADO CIVIL

¿Qué concluímos?
1. La gran parte de los beginner y expert son solteros.
2. Los estados civiles de los junior y senior están más distribuidos que los demás clusters (sin contar divorcio).
**3. El único clúster que presenta divorcios es aquel llamado UNMOTIVATED.*

GÉNERO

¿Qué concluímos?
1. Como ya se había vista, en general más mujeres han sido dadas de baja.
2. Hay más hombres en el segmento de expert (75%).
3. Hay más mujeres en senior (75%) y en unmotivated (80%)
4. Los generos están muy equitativamente distribuidos en beginner y junior.

PUESTO
1. El puesto más repetido en TODOS los segmentos, es ayudante general, representando el 75% de todos los puestos.

library(ggplot2)
#install.packages("ggalluvial")
library(ggalluvial)
bajas_new8<-bajas_new2 %>% filter(Clusters==1 | Clusters==4) %>% arrange(Clusters)
ggplot(as.data.frame(bajas_new8),
       aes(y=salario_diario, axis1=genero, axis2=e.civil.)) +
  geom_alluvium(aes(fill=Cluster_Names), width = 1/12) +
  geom_stratum(width = 1/12, fill = "black", color = "grey") +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("Genero", "Estado Civil"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("Salario Diario de Pasados Empleados de Form por Género y Estado Civil")

Debido a que existen más personaas que son parte del cluster “SENIOR” que de “EXPERT”, notamos más el color rosa. Notamos que en estos dos clusters ninnguna persona es divorciada y que las personas que están en unión libre (en estos dos casos) pertenecen solo a la categoría de Senior. Los hombres expert están en su mayoría casados, y la única mujer expert se encuentra soltera.

bajas_new9<-bajas_new2 %>% filter(Clusters==2 | Clusters==3) %>% arrange(Clusters)
ggplot(as.data.frame(bajas_new9),
       aes(y=salario_diario, axis1=genero, axis2=e.civil.)) +
  geom_alluvium(aes(fill=Cluster_Names), width = 1/12) +
  geom_stratum(width = 1/12, fill = "black", color = "grey") +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("Genero", "Estado Civil"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("Salario Diario de Pasados Empleados de Form por Género y Estado Civil")

Ahora viendo los clusters de “BEGINNER” y “JUNIOR”, vemos como existen más mujeres beginner solteras que mujeres junior solteras; fenómeno que ocurre de la misma manera con los hombres. En este caso si vemos más frecuente la unión libre. Aquí mismo podemos observar que en general las mujeres tiene un salario superior a los hombres.

3. Duración y salario

Importar base de datos

rh_bajas <-read.csv("/Users/elenavela/bajas_final.csv") 

¿Cuántos NA tengo por variables?

sapply(rh_bajas,function(x) sum(is.na(x)))
##         nombre           edad       duracion salario_diario         estado 
##              0              0              0              0              0 
##       e.civil.         genero           alta    motivo_baja         puesto 
##              0              0              0              0              0

Base de datos con valores CUANTITATIVOS

rh_bajas2<-rh_bajas
rh_bajas2<-subset(rh_bajas2,select = -c(alta,motivo_baja,estado,nombre))
summary(rh_bajas2)
##       edad          duracion       salario_diario    e.civil.        
##  Min.   :18.00   Min.   :   0.00   Min.   :144.4   Length:232        
##  1st Qu.:23.00   1st Qu.:   9.00   1st Qu.:180.7   Class :character  
##  Median :28.50   Median :  20.50   Median :180.7   Mode  :character  
##  Mean   :30.77   Mean   :  75.82   Mean   :177.9                     
##  3rd Qu.:37.00   3rd Qu.:  47.25   3rd Qu.:180.7                     
##  Max.   :61.00   Max.   :1966.00   Max.   :500.0                     
##     genero             puesto         
##  Length:232         Length:232        
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
## 

Normalizando las variables

bajas_duracion_norm<-scale(rh_bajas2[2:3]) 

Elbow Plot

fviz_nbclust(bajas_duracion_norm, kmeans, method="wss")+ 
  geom_vline(xintercept=5, linetype=2)+         
  labs(subtitle = "Elbow method")  

Visualización información de clusters

duracion_cluster1<-kmeans(bajas_duracion_norm,5)
duracion_cluster1
## K-means clustering with 5 clusters of sizes 1, 190, 10, 27, 4
## 
## Cluster means:
##      duracion salario_diario
## 1  2.55713774     13.7308825
## 2 -0.23514390      0.1149688
## 3  1.49570950     -0.3716097
## 4  0.02225829     -1.1214525
## 5  6.64053370     -0.3949078
## 
## Clustering vector:
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
##   1   4   4   4   4   4   4   4   4   3   4   4   2   2   3   4   4   4   4   4 
##  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
##   4   5   4   4   2   2   2   2   2   2   2   2   2   2   2   2   4   3   2   2 
##  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
##   3   3   2   2   2   2   2   2   2   2   2   5   3   2   2   2   2   2   2   4 
##  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
##   2   2   2   2   2   2   2   3   3   4   2   2   2   2   2   2   2   2   2   2 
##  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
##   5   4   4   4   4   2   2   2   2   2   2   2   2   2   2   2   4   4   2   2 
## 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
##   2   2   2   2   2   2   2   5   2   2   2   2   2   2   2   2   2   2   2   2 
## 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
##   2   2   2   2   2   2   2   2   2   2   2   3   2   2   2   2   2   2   2   2 
## 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
##   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2 
## 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 
##   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2 
## 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 
##   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2 
## 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
##   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   3   2   2   2 
## 221 222 223 224 225 226 227 228 229 230 231 232 
##   2   2   2   2   2   2   2   2   2   2   2   2 
## 
## Within cluster sum of squares by cluster:
## [1] 0.000000 3.267978 6.410837 1.642234 7.852827
##  (between_SS / total_SS =  95.8 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Visualización clustering resultados

fviz_cluster(duracion_cluster1,data=bajas_duracion_norm)

Al observar vemos 5 clusters que toman en cuenta la duración y el salario:

  1. Por último tenemos el rojo (“Extra”) que tuvo duración corto plazo con un salario muy alto ($500 pesos).

  2. El azul (“Costosos”) es el cluster que nos cuesta a la empresa porque se invierte en capacitaciones para ellos y terminan saliendo de la empresa; son personas con un poco mas de duración pero no la suficiente (197 días) y su salario es de $151 por debajo promedio.

  3. El verde derecho (“Duraderos”) son los colaboradores que tienen una duración regular, pero que aun así no se encuentran satisfechos por lo que se salen de la empresa al rededor después de 1 año y medio (646 días) y su salario es de $169.

  4. Vemos nuestro verde izquierdo (será llamado “Rehenes”) es un cluster que observamos que son personas que solo entran y salen muy pronto de la empresa (141 días) y tienen un salario diario que es arriba del promedio, alrededor de $180 pesos.

  5. Como morado tenemos a los colaboradores que son leales a la empresa (“Fieles”) es el segmento donde ya tienen más de 4-5 años trabajando y cuentan con un salario de $168 pesos.

Nombrar a los clusters

rh_bajas3<-rh_bajas2
rh_bajas3$Clusters<-duracion_cluster1$cluster
rh_bajas4<-rh_bajas3 %>% group_by(Clusters) %>% summarise(duracion=max(duracion)) %>% arrange(desc(duracion))
rh_bajas3$Cluster_Names<-factor(rh_bajas3$Clusters,levels = c(1,2,3,4,5), 
                              labels=c("Extra", "Rehenes","Duraderos", "Costosos","Fieles"))

Engrupar los clusters por nombre de clusters y resumir columnas

rh_bajas5<- rh_bajas3 %>% group_by(Cluster_Names) %>% dplyr::summarise(duracion_dias=max(duracion), 
salario_diario=mean(salario_diario),
Count=n())

Formato tabla para información de los clusters

Clusters<-as.data.frame(rh_bajas5)
Clusters
##   Cluster_Names duracion_dias salario_diario Count
## 1         Extra           628       500.0000     1
## 2       Rehenes           141       180.6126   190
## 3     Duraderos           646       169.1990    10
## 4      Costosos           197       151.6100    27
## 5        Fieles          1966       168.6525     4

Gráfica con el numero de observaciones por nombres de clusters

ggplot(rh_bajas5,aes(x=reorder(Cluster_Names,Count),y=Count,fill=Cluster_Names)) +
  geom_bar(stat="identity")

ggplot(rh_bajas5, aes(x=Cluster_Names,y=duracion_dias,fill= Cluster_Names,label=round(duracion_dias,digits=2))) + 
  geom_col() + 
  geom_text()

ggplot(rh_bajas5,aes(x=Cluster_Names,y=salario_diario,fill= Cluster_Names,label=round(salario_diario,digits=2))) + 
  geom_col() + 
  geom_text()

Estás ultimas gráficas nos muestran lo que se describió anteriorimente sobre que duración y salario tiene cada cluster y para concluir se mostrara cuantos colaboradores tiene cada cluster:

Total 233 * “Rehenes” tiene el segmento con más colaboradores y tiene un total de 191. En porcentaje se puede decir que es el 81.97% total de bajas.

  • “Costosos” esta en segundo lugar teniendo 27 colaboradores (11.57%).

  • “Duraderos” tercer lugar con un total de 10 colaboradores (4.29%).

  • “Fieles” cuarto lugar con 4 colaboradores (1.71%).

  • Por último esta el “Extra” que fue solo un colaborador que se encuentra fuera de lo normal (0.43%).

Análisis con variables cualitativas

ESTADO CIVIL

  1. En los rehenes vemos que aproximadamente la mitad son solteros. Solo aquí encontramos a los divorciados.
  2. En cuanto a los costosos, vemos que la minoría está en matrimonio.
  3. De los duraderos más de la mitad son solteros.
  4. En los fieles, el 75% son solteros.
  5. Finalmente, el caso “extraordinario” visto en extra, es soltero.

GENERO

  1. Igualmente, vemos que la mayoría en general son mujeres.
  2. La persona que es parte del extra es un hombre.
  3. De los duraderos el 70% son mujeres. De los rehenes y los costosos también se tiene la mayoría de mujeres.
  4. En fieles, el 75% son hombres.

PUESTO
1. Las 27 personas de costosos, tuvieron el puesto de “Ayudante General”. De los 190 integrantes de rehenes, 144 tenían el mismo puesto.
2. La persona de extra pertenecía al puesto a diseño.

rh_bajas6<-rh_bajas3 %>% filter(Clusters==2 | Clusters==3) %>% arrange(Clusters)
ggplot(as.data.frame(rh_bajas6),
       aes(y=salario_diario, axis1=genero, axis2=e.civil.)) +
  geom_alluvium(aes(fill=Cluster_Names), width = 1/12) +
  geom_stratum(width = 1/12, fill = "black", color = "grey") +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("Genero", "Estado Civil"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("Salario Diario de Pasados Empleados de Form por Género y Estado Civil")

Debido a que no existen muchas personas que son parte del segmento duraderos, se ve mucha más presencia de parte del segmento de los rehenes. Aquí observamos que la mayoría de las personas en matrimonio que son rehenes son hombres.

rh_bajas7<-rh_bajas3 %>% filter(Clusters==1 | Clusters==4) %>% arrange(Clusters)
ggplot(as.data.frame(rh_bajas7),
       aes(y=salario_diario, axis1=genero, axis2=e.civil.)) +
  geom_alluvium(aes(fill=Cluster_Names), width = 1/12) +
  geom_stratum(width = 1/12, fill = "black", color = "grey") +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("Genero", "Estado Civil"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("Salario Diario de Pasados Empleados de Form por Género y Estado Civil")

Como fue mencionado anteriormente, la persona que es parte de extra es un hombre soltero. Por otra parte, vemos que de los costosos, hay más mujeres en unión libre que hombres. Así como hay más mujeres solteras que casadas.

rh_bajas8<-rh_bajas3 %>% filter(Clusters==4 | Clusters==5) %>% arrange(Clusters)
ggplot(as.data.frame(rh_bajas8),
       aes(y=salario_diario, axis1=genero, axis2=e.civil.)) +
  geom_alluvium(aes(fill=Cluster_Names), width = 1/12) +
  geom_stratum(width = 1/12, fill = "black", color = "grey") +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("Genero", "Estado Civil"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("Salario Diario de Pasados Empleados de Form por Género y Estado Civil")

De parte de los fieles, observamos que hay una pequeñaa cantidad de hombres que están casados, una pequeña cantidad de mujeres solteras, y una cantidad unn poco mayor de hombres solteros.

4. Edad y salario

Importar base de datos

bd_rh<-read.csv("/Users/elenavela/bajas_final.csv") 

Analizar la estructura de la base de datos

str(bd_rh) 
## 'data.frame':    232 obs. of  10 variables:
##  $ nombre        : chr  "MARIO VALDEZ ORTIZ" "ISABEL BARRIOS MENDEZ" "MARIA ELIZABETH GOMEZ HERNANDEZ" "ALONDRA ABIGAIL ESCARCIA GOMEZ" ...
##  $ edad          : int  32 36 23 21 29 46 29 31 50 19 ...
##  $ duracion      : num  628 60 59 59 51 37 37 31 18 224 ...
##  $ salario_diario: num  500 152 152 152 152 ...
##  $ estado        : chr  "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" "Nuevo Leon" ...
##  $ e.civil.      : chr  "Soltero" "Union libre" "Matrimonio" "Soltero" ...
##  $ genero        : chr  "MASCULINO" "FEMENINO" "FEMENINO" "FEMENINO" ...
##  $ alta          : chr  "09/03/20" "09/11/21" "10/11/21" "10/11/21" ...
##  $ motivo_baja   : chr  "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" "RENUNCIA VOLUNTARIA" ...
##  $ puesto        : chr  "DISENO" "AYUDANTE GENERAL" "AYUDANTE GENERAL" "AYUDANTE GENERAL" ...

Cuantos Na´s hay por variable

sapply(bd_rh,function(x) sum(is.na(x)))
##         nombre           edad       duracion salario_diario         estado 
##              0              0              0              0              0 
##       e.civil.         genero           alta    motivo_baja         puesto 
##              0              0              0              0              0

Dejamos únicamente las variables de interés

bdr_new <- bd_rh
bdr_new<-subset(bdr_new,select = -c(alta,motivo_baja,estado,nombre,duracion))
summary(bdr_new)
##       edad       salario_diario    e.civil.            genero         
##  Min.   :18.00   Min.   :144.4   Length:232         Length:232        
##  1st Qu.:23.00   1st Qu.:180.7   Class :character   Class :character  
##  Median :28.50   Median :180.7   Mode  :character   Mode  :character  
##  Mean   :30.77   Mean   :177.9                                        
##  3rd Qu.:37.00   3rd Qu.:180.7                                        
##  Max.   :61.00   Max.   :500.0                                        
##     puesto         
##  Length:232        
##  Class :character  
##  Mode  :character  
##                    
##                    
## 

Normalización de variables

bajas_edadsal <- scale(bdr_new[1:2]) 

Elbow plot

fviz_nbclust(bajas_edadsal, kmeans, method="wss")+ 
  geom_vline(xintercept=4, linetype=2)+         
  labs(subtitle = "Elbow method") 

Visualización de clusters

Se toman en cuenta, primeramente, solo 4 clusters.

edadsal_cluster1<-kmeans(bajas_edadsal,4)
edadsal_cluster1
## K-means clustering with 4 clusters of sizes 46, 110, 1, 75
## 
## Cluster means:
##         edad salario_diario
## 1  1.6393195    -0.05379675
## 2 -0.8328042    -0.06389667
## 3  0.1264636    13.73088247
## 4  0.2143107    -0.05636798
## 
## Clustering vector:
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
##   3   4   2   2   4   1   4   4   1   2   2   4   2   4   4   2   2   2   2   2 
##  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
##   4   1   2   4   1   2   4   2   2   2   4   2   2   2   4   2   2   4   4   2 
##  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
##   2   4   2   2   2   2   2   2   1   1   1   1   2   1   2   2   1   2   2   2 
##  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
##   2   2   4   2   2   1   2   1   2   2   2   2   4   2   4   2   4   2   2   1 
##  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
##   2   4   1   2   2   2   1   2   2   2   4   1   1   2   2   4   1   4   2   4 
## 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
##   2   2   1   4   2   2   4   2   4   2   4   4   2   4   4   4   1   1   1   2 
## 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
##   2   2   2   2   4   2   4   1   2   4   2   1   2   4   2   4   1   4   1   4 
## 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
##   4   1   1   1   2   4   4   4   2   2   2   4   1   1   2   4   2   4   1   1 
## 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 
##   1   1   4   1   2   2   4   2   2   1   4   1   2   1   4   4   1   4   4   2 
## 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 
##   2   2   4   1   4   4   2   2   4   4   4   4   4   2   4   4   4   4   2   4 
## 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
##   1   4   2   2   2   1   2   2   2   4   4   2   2   4   4   4   1   2   2   4 
## 221 222 223 224 225 226 227 228 229 230 231 232 
##   2   2   2   4   1   4   1   2   2   2   2   2 
## 
## Within cluster sum of squares by cluster:
## [1] 18.69230 28.61998  0.00000 21.95851
##  (between_SS / total_SS =  85.0 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"
fviz_cluster(edadsal_cluster1,data=bajas_edadsal)

Vemos que el número óptimo de klusters para las variables edad y salario diario son 4 según el método de Elbow.

Como dato disparado y posible warning vemos que hay un trabajador con un salario diario de 500 pesos lo que podría resultar como un error, sin embargo los otros 3 grupos varían bastante en edad más no en salario pues el rango es muy cercano.

  1. Rojo Es el cluster con el registro de personas con mayor edad y cuentan con el salario diario más alto 183.68.

  2. Verde Es el cluster con mayor registro de personas con edad jóven y salario de 182.68.

  3. Alto El dato es único en el conteo con un trabajador de 32 años que tiene un salario diario de 500 pesos y es soltero. Su punto en el mapeo es único (color rojo).

  4. Azul Es el cluster con el registro de personas con edad promedio y salario de 180.68.

Nombrar a los clusters

bdr_new2<-bdr_new
bdr_new2$Clusters <- edadsal_cluster1$cluster
bdr_new3<-bdr_new2 %>% group_by(Clusters) %>% dplyr::summarize(salario_diario=max(salario_diario)) %>% arrange(desc(salario_diario))
bdr_new2$Cluster_Names<-factor(bdr_new2$Cluster,levels = c(1,2,3,4), 
                                labels=c("Alto", "Bajo","Muy Alto", "Regular"))

Agrupar clusters

bdr_new4<- bdr_new2 %>% group_by(Cluster_Names) %>% dplyr::summarize(salario_diario=max(salario_diario), 
edad=mean(edad),
Count=n())

Tabla

clusters<-as.data.frame(bdr_new4)
clusters
##   Cluster_Names salario_diario     edad Count
## 1          Alto         183.68 46.69565    46
## 2          Bajo         182.68 22.68182   110
## 3      Muy Alto         500.00 32.00000     1
## 4       Regular         180.68 32.85333    75

Gráficas

ggplot(bdr_new4,aes(x= reorder(Cluster_Names,Count),y=Count,fill=Cluster_Names)) +
  geom_bar(stat="identity")

Aquí podemos observar que hay una mayor cantidad de personas en el clússter de menor edad y menor salario. Seguido por el nivel regular, alto y muy alto.

ggplot(bdr_new4, aes(x=Cluster_Names,y=edad,fill= Cluster_Names,label=round(edad,digits=2))) + 
  geom_col() + 
  geom_text()

La edad máxima del muy alto es de 32 años, de alto es de 46.7 años, de regular es 32.85 años y de bajo, los más jóvenes, 22.68.

ggplot(bdr_new4,aes(x=Cluster_Names,y=salario_diario,fill= Cluster_Names,label=round(salario_diario,digits=2))) + 
  geom_col() + 
  geom_text()

  1. En la segunda gráfica el clúster azul es el que tiene mayor frecuencia con un promedio de edad de 48 años.

  2. El cluster alto en la segunda grafica englobó en 22 años el count, sin embargo hay trabajadores con menor edad. Dicho cluster es el que tiene menos trabajadores dentro de Form.

  3. En cuanto al salario en la tercera gráfica, el dato más alto es el que anteriormente se había mencionado como un dato único ($500) y vemos el comportamiento que se generaba en el mapeo de clusters donde los demás tienen una diferencia mínima siendo los resultados 182.68, 180.68 y 183.68.

Análisis con variables cualitativas

ESTADO CIVIL

  1. Observamos que en este caso, el divorcio puede ser visto en alto y regular, los dos segmentos del medio.
  2. Fuera del divorcio en alto, podemos ver que se está bastante parejo de los solteros y los casados.
  3. La persona que pertenecía a muy alto es soltera.
  4. De bajo, vemos la mayoría son solteros.
  5. Finalmente, en regular, dejando de fuera los divorciados, encontramos que se está muy parejo el número de casados, solteros y en unión libre.

GENERO

1. La persona en muy alto es hombre.
2. En alto y en regular, se tiene a la mayoría mujeres.
3. Finalmente, en bajo se ve muy parejo en cuanto a los géneros.

PUESTO
1. En todos los segmentos, excepto muy alto, el puesto más común es “Ayudante General”.
2. En muy alto, el hombre estaba en diseño.

bdr_new5<-bdr_new2 %>% filter(Clusters==4 | Clusters==3) %>% arrange(Clusters)
ggplot(as.data.frame(bdr_new5),
       aes(y=salario_diario, axis1=genero, axis2=e.civil.)) +
  geom_alluvium(aes(fill=Cluster_Names), width = 1/12) +
  geom_stratum(width = 1/12, fill = "black", color = "grey") +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("Genero", "Estado Civil"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("Salario Diario de Pasados Empleados de Form por Género y Estado Civil")

Como vimos previamente, la persona perteneciente a muy alto es un hombre soltero.

bdr_new6<-bdr_new2 %>% filter(Clusters==2 | Clusters==1) %>% arrange(Clusters)
ggplot(as.data.frame(bdr_new6),
       aes(y=salario_diario, axis1=genero, axis2=e.civil.)) +
  geom_alluvium(aes(fill=Cluster_Names), width = 1/12) +
  geom_stratum(width = 1/12, fill = "black", color = "grey") +
  geom_label(stat = "stratum", aes(label = after_stat(stratum))) +
  scale_x_discrete(limits = c("Genero", "Estado Civil"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("Salario Diario de Pasados Empleados de Form por Género y Estado Civil")

Las personas que se han divorciado del segmento alto han sido mujeres. Existen más personas en unión libre del segmento de bajo; muchos hombres y mujeres de este mismo segmento son solteros y solteras.

Insights

  • Con la base de datos de bajas se realizó el clúster de edad y duración, en la cual se tomaron en cuenta las variables de la edad de los colaboradores y la duración que han tenido en la empresa. Se buscó el número óptimo de clúster y se tomaron en cuenta los primeros 4. En este podemos observar que las personas con menor edad (los más jóvenes) son los que menos duran por lo general; otro dato que observamos que las personas con mayor edad han durado más tiempo en Form y forman parte del superior 50%, y por último observamos que existe una mayor rotación de personal en los colaboradores jóvenes.
  • En el análisis que se realizó mediante gráficas, obtuvimos que en los clúster, que la persona con mayor edad en los expert tiene 61, en senior tiene 57, en junior tiene 39 y en beginner 27, también que en la empresa la mayoría de los colaboradores corresponden al clúster de beginner.
  • Siguiendo con el análisis de estas dos variables y 5 clusters, notamos que encontramos un nuevo cluster, al cuál hemos llamado: unmotivated. Las caracerísticas que encontramos es que son personas de mediana edad que han durado muy poco tiempo en la empresa, parte del 5% inferior de duración. Un insight relevante es que SOLO en este cluster se presentan los divorcios, y que la gran parte son mujeres. ¿Fenómeno cultural?
  • En cuanto al análisis de la duración de los colaboradores en la empresa y su salario encontramos estos puntos mas importantes; a) la mayoría de los empleados ganan arriba del salario diario promedio establecido por el gobierno, este oscila entre $150-$200 pesos diarios, b) los colaboradores que tienen un tiempo medio en la empresa aproximadamente de un año tiene un salario promedio de $169, mientras los que duran menos tiempo (alrededor de 141 días) tienen un salario promedio de alrededor de $180.
  • El clúster mas grande es el llamado “Rehenes” tiene un total de colaboradores de 191, y en porcentaje de bajas se puede decir que es el 81.97% total de bajas, estos tiene la característica de son las personas que entran y salen muy pronto y tienen un salario por arriba de todos los demás clúster de $180 pesos, por lo que, en este clúster se necesita aplicar una estrategia de fidelización de los colaboradores de otra forma que no sea con un aumento de salario, es decir, Form puede implementar estrategias con recompensas que sean a largo plazo.
  • Adicionalmente, en el análisis de duración y salario, encontramos a una persona que se salía de lo “normal”, esta persona forma su propio clúster (extra). Sus características son las siguientes: duró aprox 2 años, ganaba $500 pesos diarios en un puesto de Diseño; hombre y soltero.
  • Como se puede observar en el analisis que se realizó de edad y salario se pueden destacar los siguientes puntos:
  1. el cluster que tiene una mayor frecuencia es el de 48 años de edad,
  2. el cluster que mas chico es de 22 años o menos por lo que podemos decir que en Form, la mayoría son mayores de 23 años,
  3. en cuanto al salario tienen un promedio de 180.68, pero existe un dato único el cual ganaba 500 (sus características fueron mencionadas anteriormente).

En conclusión para Form podemos plantear una estrategia de estandarización de salarios de acuerdo al tiempo que llevan en la empresa y su edad, para poder así tener un mejor control de gastos de la empresa y aplicar nuevas a estrategias a cada uno de los cluster.

Identificación de Resultados Relevantes y Sugerencias

1) Meaningful Insights.

  1. En cuanto a lo más común de los actuales y pasados colaboradores de Form, observamos que hay (y hubieron) más mujeres que hombres. Igualmente, notamos que el puesto más repetido es el de “Ayudante General”; notorio ver como es el puesto que más presenta rotación. Las personas solteras y las personas casadas son aquellas que presentan una mayor presencia; sin embargo, comparando a décadas pasadas, se ve mucha gente que vive en unión libre. Hay más personas jóvenes (20-30).

  2. La mayoría de los colaboradores actuales ingresaron durante los últimos dos años, y la mayoría ganan entre $140 y $200 diarios. Sin embargo, comparando la antigüedad de los colaboradores, notamos que una persona que lleva 12 años en la empresa gana lo mismo que una persona que lleva menos de un año. Inferimos que no existe un plan de crecimiento laboral interno, y tampoco existe un plan de compensaciones y beneficios que incentiven a permanecer más tiempo en Form.

  3. Un insight positivo de Form, es que no presenta discriminación por género, por estado civil, ni por edad.

  4. Existe un problema con la cantidad de faltas, más del 50% han salido de la organización debido a este tema. Notamos que no tienen información cuantitativa en cuanto a las faltas; se desconoce la cantidad límite de faltas y por cuánto se exceden. La duración de los pasados colaboradores no llega ni al año (máximo) por lo general. Todos ganaban aproximadamente lo mismo.

  5. Viendo el plan de entrega, observamos que hay meses en donde se veían pedidos “fuera de lo común”; en este caso analizamos los mayores a 30,000. Estos meses son: marzo, abril, junio y agosto. El mes de agosto también demuestra tener una mayor cantidad de merma; en scrap, vemos un crecimiento de cantidad durante la segunda mitad del mes. Sin embargo, viendo las exportaciones de México, agosto es la que menos exportaciones presenta de todo el año.

  6. Un diferenciado de Form es la importancia que le da a los tiempos; vemos como trabajan por mantener el tiempo mínimo, tiempo de calidad, el delay performance lo más bajo posible.

  7. A partir de los clústers, notamos que los jóvenes son los que menos duran, aunque su salario no esté muy debajo de los demás. Comentando con Felipe, se mencionó como al momento de reclutar muchos no aceptan entrar por el tema de las faltas (3 faltas cada 30 días). Un insight que observamos es que es difícil crear un sentido de pertenencia o deseo de crear antigüedad dentro de la empresa; sin embargo, los datos demuestran que Form no “premia” la antigüedad.

  8. Encontramos un nuevo cluster, al cuál hemos llamado: unmotivated. Las características que encontramos es que son personas de mediana edad que han durado muy poco tiempo en la empresa, parte del 5% inferior de duración. Un insight relevante es que SOLO en este cluster se presentan los divorcios, y que la gran parte son mujeres. ¿Fenómeno cultural?. Adicionalmente, en el análisis de duración y salario, encontramos a una persona que se salía de lo “normal”, esta persona forma su propio clúster (extra). Sus características son las siguientes: duró aprox 2 años, ganaba $500 pesos diarios en un puesto de Diseño; hombre y soltero.

Insights no relacionados a FORM

  1. Se exporta una importante cantidad de autos japoneses desde México, algunas marcas siendo: Nissan y Mazda. Posible cliente potencial, igualmente, existe un nicho donde algunas marcas japonesas (Toyota y Nissan) que exportan muy poco, por lo que no puede ser muy “llamativo”, pero podría funcionar para adentrarse más a la cultura y el mercado. Las marcas de lujo no se exportan mucho de parte de México, como vemos algunas marcas de lujo como BMW, Audi y Mercedes (Alemania).

  2. En EUA, vemos que cuando sube el salario mínimo por hora, sube la venta de autos passenger. Pero cuando sube esta misma variable, vemos que baja la venta de autos comerciales. Resultaría interesante investigar más a fondo este fenómeno. Se espera que en los próximos años suba la venta de autos en EUA, pero que bajen en México.

2) Sugerencias.

  1. Capturar las fechas en formato mexicano: día/mes/año. Y específicar esto en el Excel.
  2. No poner mucha atención en lo “estético” que se pudiera ver el formato. Usar primer renglón UNICAMENTE para nombrar las variables. Como se pudo ver en Producción, que no permite analizar los datos.
  3. Incluir fechas de las faltas para analizar la concurrencia de ellas por persona, hacer clusters para ver las características de las personas que faltan mucho y poco.

  4. Evitar poner acentos en los datos.
  5. Homogenizar los datos: tener una opción drop-down para puesto, municipio, estado, nombre de cliente, etc., para no tener diferentes de maneras de escritura, y volverlo más fácil al momento de capturar los datos.
  6. Cuidar el registro de cada persona, para que se llenen todos los espacios.
  7. Aparte de reunir pedidos por día, juntarlo todo a final de mes.

Definiciones

1. Business Analytics: Business Analytics se podría traducir, de manera más cercana, como “análisis empresarial”. Su enfoque es en datos históricos y actuales, que mediante su procesamiento, estudio y clasificación las empresas, pueden comprender su desempeño actual y pasado. El principal objetivo de este análisis es poder identificar patrones o problemas, identificar los riesgos, y tomar medidas para disminuirlos o evitarlos. A mi manera de verlo, Business Analytics es una buena manera de cuidar el futuro de la organización, al pensar y comprender su pasado se puede evitar repetir malass decisiones o ciertos problemas. Una frase famosa de Cicerón dice que “los pueblos que olvidan su historia están condenados a repetirla”; algo así podría verse con las organizaciones, al entender y manejar bien su pasado mediante datos, se pueden tomar las mejores decisiones (bien fundamentadas) para el futuro desempeño y planificación de la organización.

2. Business Intelligence: Business Intelligence podría definirse como el conjunto de herramientas, tecnologías y estrategias que permite la transformación de datos en información, para que esta información puede convertirse en conocimiento. De acuerdo a IBM, estas herramientas permiten a los integrantes de una organización acceder a diferentes tipos de datos (históricos o actuales) con el fin de analizar los datos y obtener insights de acuerdo a lo que necesitan conocer.

3. Diferencias:

  • El Business Analytics comúnmente se identifica como un complemento de Business Intelligence, por lo que no puede ser considerado lo mismo.

  • Business Intelligence se enfoca más a la recolección y la centralización de los datos, facilita el trabajo y el acceso a los datos; Business Analytics se encarga de analizar esta información y usarla en modelos descriptivos, predictivos y explicativos, con el fin de apoyar a ella toma de decisiones de alguna organización.

  • Business Intelligence busca contestar el WHAT? y HOW? de las cosas; Business Analytics contesta al WHY? las cosas suceden en la empresa, para poder predecir comportamientos.

  • Business Intelligence se orienta al pasado; Business Analytics se orienta al futuro.

Key Performance Indicators (KPIs)

¿Qué es un KPI?
Un Key Performance Indicatrr (KPI), o bien un Indicador Clave de Rendimiento, es una métrica cuantitativa que pretende medir el progreso de una persona, equipo o empresa hacia cierto objetivo; lo que busca es ayudar a tomar mejores decisiones respecto al estado actual de una persona, proceso, estrategia. Un buen KPI ayuda a lograr objetivos estratégicos, informa sobre la planificación de recurso, es medible (cuantitativo), se puede hacer seguimiento, ayuda a acercar a objetivos concretos y específicos. La evaluación constante de los KPIs (o con un periodo de tiempo delimitado) es de vital importancia; recordando al consultor de negocios Peter Drucker: “Lo que no se puede medir no se puede gestionar”.

KPIs para FORM:
1. Índice de rotación de personal: debido al análisis realizado, observamos que la retención de personal es baja, sobretodo en jóvenes, al ver los pocos días de duración en Form. Por lo tanto, se recomienda a Form llevar el índice de rotación CADA MES.

La fórmula para obtener este índice es: número actual (del mes) de empleados que renunciaron o se fueron / ((empleados al inicio de mes+ empleados al final de mes)/2) x 100

Se busca que disminuya el índice de rotación a partir de los meses, puesto que podría significar que las estrategias diseñadas e implementadas están funcionando. Sin embargo, es importante que nunca llegue a ser 0, debido a que podría ser indicativo de falta de dinaamismo empresarial.

Se propone este índice para conocer con mayor exactitud que tanta gente se va y que tanta gente se queda mes con mes, y tratar de mejorar aspectos para mejorar la retención.

2. Índice de Ausentismo: un problema que pudimos ver en las personas que partieron de Form, es que la mayoría fue por exceso de falta. Por lo tanto, se desea saber realmente QUÉ tanto ausentismo existe. Debido a que fue mencionado como un problema actual y relevante, se quiere medirlo de manera QUINCENAL, para ver que tanto varía dentro de un mes, y finalmente, en el año. Se espera que a partir de este indicador se pueda llegar a tomar una decisión y realizar estrategias para disminuir el ausentismo.

La fórmula para medir el ausentismo= (Número Total de Horas Ausentismo / Número Total de Horas Planificadas) x 100

Como podemos ver, esto se mide con las horas. Se propone a Form tomar en cuenta las horas diarias, y en caso de faltar, tener en el excel ya lista la fórmula (1 x horas del día laboral). Igualmente, tomar en cuenta las veces que las personas salen antes en el día (que se van a medio día). En este caso, debido a ser el primero, no se toma en cuenta la falta justificada y la no justificada.

3. Nivel de servicio (entregas a tiempo): como se mencionó, el tiempo de entrega y sus retrasos (delay performance) es un tema que la empresa le da mucha importancia, puesto que para ellos es vital el tema de servicio oportuno. Por lo tanto, propongo el KPI de “Nivel de servicio”, el cuál pretende medir las entregas a tiempo. Si bien, Form ya posee datos que permiten ver el tiempo de entrega, este KPI lo haría más puntual.

Mide el porcentaje de pedidos que se entregan a tiempo frente a la fecha y hora de compromiso con el cliente. La fórmula es: (número de pedidos entregados tiempo/número total de pedidos entregados) × 100

Mediante estos tres KPI, dos enfocados en RH, y otro enfocado más al servicio al cliente y su la eficiencia.

Referencias

LS0tCnRpdGxlOiA8c3BhbiBzdHlsZT0iY29sb3I6b3JhbmdlIj4qKkV0YXBhIDLigJNFdmlkZW5jaWEqKgphdXRob3I6ICJFbGVuYVZlbGEgIChBMDEyODM1MzUpIgpkYXRlOiAiMjAyMi0xMC0xMCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgojIDxpbWcgc3JjPSIvVXNlcnMvZWxlbmF2ZWxhL1BpY3R1cmVzL2hlYWRlci5wbmciPiAgCgo8aW1nIHNyYz0gIi9Vc2Vycy9lbGVuYXZlbGEvRGVza3RvcC9DYXB0dXJhIGRlIFBhbnRhbGxhIDIwMjItMTAtMjAgYSBsYShzKSAyMi4yNS4zNC5wbmciPiAgIAoKCgojIyAqKkludHJvZHVjY2nDs24qKgoKTGEgZW1wcmVzYSBGb3JtLCBmdW5kYWRhIGVuIGVsIDIwMTEsIGVzIHVuYSBlbXByZXNhIGZhbWlsaWFyIG1leGljYW5hIGVuZm9jYWRhIGVuIGxhIG1hbnVmYWN0dXJhIGRlIGVtcGFxdWVzIHBhcmEgbGEgaW5kdXN0cmlhIGF1dG9tb3RyaXouIEVzdGEgUHlNZSwgbGlkZXJhZGEgcG9yIEZlbGlwZSBGbG9yZXMgR2FyY8OtYSwgaGEgbG9ncmFkbyBjcmVjZXIgZHVyYW50ZSBlc3RvcyAxMSBhw7FvcywgeSBlbXBsZWFyIGEgMTMwIHBlcnNvbmFzLiBGb3JtIHNlIGRlZGljYSBhIGRlc2Fycm9sbGFyIHNvbHVjaW9uZXMgZGUgZW1wYXF1ZSBxdWUgZ2VuZXJlbiB2YWxvciBhZ3JlZ2FkbyBlbiBsYSBjYWRlbmEgZGUgc3VtaW5pc3RybyBkZSBzdXMgY2xpZW50ZXMsIGNvbnNpZGVyYW5kbyBsYSBwcm90ZWNjacOzbiB5IGxhcyBuZWNlc2lkYWRlcyBwcm9kdWN0aXZhcywgb3BlcmF0aXZhcyB5IGRlIGFsbWFjZW5hamUuIFRhbWJpw6luLCBjb25zaWRlcmFuIGRlIHN1bWEgaW1wb3J0YW5jaWEgdGVuZXIgYWwgc2VyIGh1bWFubyBjb21vIGVsIGNlbnRybyBkZSB0b2RvcyBzdXMgcHJvY2Vzb3MuIApEZWJpZG8gYSBxdWUgbGEgaW5kdXN0cmlhIGF1dG9tb3RyaXogdHJhYmFqYSB5IGNyZWNlIGNvbnN0YW50ZW1lbnRlLCBsYXMgZW1wcmVzYXMgbm8gc2UgcHVlZGVuIHBlcm1pdGlyIGRlamFyIHBhc2FyIGN1YWxxdWllciBvcG9ydHVuaWRhZCBwYXJhIGRlbW9zdHJhciBzdSBlZmljaWVuY2lhIGFsIHNlciBwcm9kdWN0aXZvcyB5IHJlbnRhYmxlcy4gRm9ybSBvZnJlY2UgZXN0YSBvcG9ydHVuaWRhZCwgbGEgb3BvcnR1bmlkYWQgZGUgZ2VuZXJhciBhaG9ycm9zLCBzZXIgbcOhcyBlZmljaWVudGVzLCBwcm9kdWN0aXZvcyB5IHJlbnRhYmxlcyBtZWRpYW50ZSBlbCB1c28gYWRlY3VhZG8geSBlZmVjdGl2byBkZWwgZW1wYXF1ZSwgZGVzZGUgc3UgaW52ZW50YXJpbyB5IHN1IGVzcGFjaW8gbcOtbmltbyBlbiBhbG1hY8OpbiwgaGFzdGEgc3UgZ3JhbiBjYWxpZGFkIGRlIGRpc2XDsW8geSBtYW5pcHVsYWNpw7NuLiAgIAoKQ29uIHBvY28gbcOhcyBkZSAxMDAgYcOxb3MsIGxhIGluZHVzdHJpYSBhdXRvbW90csOteiBzZSBoYSBwb3NpY2lvbmFkbyBjb21vIHVuYSBkZSBsYXMgcmFtYXMgZGUgbWF5b3IgaW1wb3J0YW5jaWEgdGFudG8gcGFyYSBlbCBjbGllbnRlIGZpbmFsIGNvbW8gcGFyYSBhcXVlbGxvcyBpbnZvbHVjcmFkb3MgZW4gcXVlIHNlIG1hbnRlbmdhIGEgbGEgdmFndWFyZGlhLiBDb21wYXJhZG8gY29uIHVuIGRpc3Bvc2l0aXZvIGNvbW8gbGEgY29tcHV0YWRvcmEgbyBlbCB0ZWzDqWZvbm8gY2VsdWxhciwgdW4gYXV0byBwdWVkZSBwYXJlY2VyIHByaW1pdGl2byBwZXJvIGVsIGluZ2VuaW8gcXVlIGhheSBkZXRyYXMgZGUgY2FkYSBwYXJ0ZSB5IGNhZGEgbW92aW1pZW50byBlcyBpbnRlcm1pbmFibGUuIFNvYnJlIHRvZG8gZW4gbGFzIMO6bHRpbWFzIGTDqWNhZGFzLCBNw6l4aWNvIHNlIGhhIHBvc2ljaW9uYWRvIGNvbW8gY2xhdmUgcGFyYSBsYSBtYW51ZmFjdHVyYSBjb21vIHBhcmEgbGEgaW5ub3ZhY2nDs24gcG9yIGxvIHF1ZSBlc3RhIHJhbWEgZGUgbGEgZWNvbm9tw61hIG5vIHB1ZWRlIHBhc2FyIGRlc2FwZXJjaWJpZGEuIENvbiBheXVkYSBkZSBsYXMgaGVycmFtaWVudGFzIHBhcmEgYWxtYWNlbmFyIGRhdG9zLCBzZSBoYSB2dWVsdG8gY2xhdmUgcGFyYSBlbCBjcmVjaW1pZW50byBwb2RlciBpbnRlcnByZXRhciBlc3RvcyBkYXRvcyBkZSBsYSBtYW5lcmEgYXByb3BpYWRhIHF1ZSBlcyBlbCBlbmZvcXVlIGFsIHF1ZSBzZSBlc3BlcmEgbGxlZ2FyLiAgCgojIyAqKsONbmRpY2UqKiAgCgoqKlNlY2Npw7NuIDEqKiAgCgoqIExpbXBpZXphIGRlIGJhc2VzIGRlIGRhdG9zICAKCiogQ29tcHJlbnNpw7NuIGRlIGxhcyBiYXNlcyBkZSBkYXRvcyAgCgoqIEFuw6FsaXNpcyBlc3RhZMOtc3RpY28gZGVzY3JpcHRpdm8gIAoKKiBQcm9wdWVzdGFzICAKCiogQmFzZXMgZGUgZGF0b3MgZXh0ZXJuYXMgIAoKKipTZWNjacOzbiAyKiogIAoKKiBBbsOhbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGxhcyBiYXNlcyBkZSBkYXRvcyAgCgoqIFByZWRpY2Npw7NuIGRlbCBkZXNlbXBlw7FvIGRlIGxhIGluZHVzdHJpYSBhdXRvbW90cml6ICAKCgoqKlNlY2Npw7NuIDMqKiAgCgoqIEstTWVhbnMgQ2x1c3RlcmluZyAgCgoKKipTZWNjacOzbiA0KiogIAoKKiBJZGVudGlmaWNhY2nDs24gZGUgUmVzdWx0YWRvcyBSZWxldmFudGVzIHkgU3VnZXJlbmNpYXMgIAoKKiBEZWZpbmljaW9uZXMgIAoKKiBLZXkgUGVyZm9ybWFuY2UgSW5kaWNhdG9ycyAoS1BJcykgIAoKKiBSZWZlcmVuY2lhcwoKCgojIDxpbWcgc3JjPSIvVXNlcnMvZWxlbmF2ZWxhL0Rlc2t0b3AvQ2FwdHVyYSBkZSBQYW50YWxsYSAyMDIyLTEwLTEwIGEgbGEocykgMTQuMjQuMjAucG5nIj4gIAoKIyMgKipMaW1waWV6YSBkZSBiYXNlcyBkZSBkYXRvcyoqICAKCiMjIyAqKkZPUk0tIFJlY3Vyc29zIEh1bWFub3MgKENvbGFib3JhZG9yZXMgeSBCYWphcykuKiogIAoKCkVtcGV6YW1vcyBjb24gImNvbGFib3JhZG9yZXMiLiAgIAogIAogIApTZSBpbXBvcnRhIGxhIGJhc2UgZGUgZGF0b3MgeSBzZSBlbnRpZW5kZW4gbGFzIHZhcmlhYmxlcy4KCmBgYHtyfQpjb2xhYjwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvRG93bmxvYWRzL2NvbGFiYmIuY3N2IikKc3VtbWFyeShjb2xhYikKYGBgCgogIAoqKlTDqWNuaWNhIDMsICpFUlJPUkVTIFRJUE9HUsOBRklDT1MgWSBFUlJPUkVTIFNJTUlMQVJFUyoqKiAgClByaW1lcmFtZW50ZSwgc2Ugbm90w7MgcXVlIGVuIGVsIGV4Y2VsIGFsZ3VuYXMgZmVjaGFzIGVzdGFiYW4gY29uIGZvcm1hdG8gZMOtYS9tZXMvYcOxbyB5IG90cm9zIG1lcy9kw61hL2HDsW8uIFBvciBsbyBxdWUgcGFyYSB0ZW5lciBsYSBpbmZvcm1hY2nDs24gbcOhcyByZWFsIHkgcHJlY2lzYSBwb3NpYmxlLCBzZSBleHRyYWplcm9uIHNvbG8gbG9zICoqQcORT1MqKiBwYXJhIGVsIGFuw6FsaXNpcy4gICAgCgoKKipUw6ljbmljYSAxLCAqUkVNT1ZFUiBEQVRPUyBJUlJFTEVWQU5URVMqKiogICAKRXhpc3RlbiBtdWNoYXMgdmFyaWFibGVzIGVuIGxhIGJhc2UgZGUgZGF0b3MsIHkgbXVjaGFzIGRlIGVzdGFzIGNvbnRpZW5lbiBkYXRvcyBwZXJzb25hbGVzIGRlIGxvcyBjb2xhYm9yYWRvcmVzLCBwb3IgbG8gcXVlIGVzIGluZm9ybWFjacOzbiBtdXkgc2Vuc2libGUgeSBxdWUgbm8gYXBvcnRhIG11Y2hvICppbnNpZ2h0KiBhbCBhbsOhbGlzaXMuIFBvciBsbyB0YW50bywgc2UgaGEgZXNjb2dpZG8gbGEgdMOpY25pY2EgZGUgcmVtb3ZlciBkYXRvcyBpcnJlbGV2YW50ZXMgYSBsYSBpbmZvcm1hY2nDs24gcGVyc29uYWwgZGUgbG9zIGVtcGxlYWRvcyBxdWUgbm8gc2lydmFuIG11Y2hvIHBhcmEgZWwgYW7DoWxpc2lzLCBjb21vIGxvIGVzIHN1IG5vbWJyZSwgYXBlbGxpZG8sIFJGQywgQ1VSUC4gU2luIGVtYmFyZ28sIG90cm9zIGRhdG9zIHBlcnNvbmFsZXMgc2UgaGFuIG1hbnRlbmlkbyBwYXJhIGhhY2VyIHVuIGFuw6FsaXNpcyBkZSBkaXZlcnNpZGFkIGUgaW5jbHVzacOzbiAoZ8OpbmVybywgYcOxbyBkZSBuYWNpbWllbnRvLCBlc3RhZG8gY2l2aWwpLiAgICAgCgoKU2UgZWxpbWluYW4gbGFzIGNvbHVtbmFzIHF1ZSBubyBhcG9ydGFuIG11Y2hvIGFsIGFuw6FsaXNpcyBkZSBkYXRvcy4KYGBge3J9CmNvbGFiMjwtY29sYWIKY29sYWIyPC1zdWJzZXQoY29sYWIyLHNlbGVjdD0tYyhOby4uRGUuRW1wbGVhZG8sVEFSSkVUQS4uLi5DVUVOVEEsQ09ESUdPLlBPU1RBTCxDT0xPTklBLE5VTUVSTy5JTlRFUk5PLENBTExFLENVUlAsRkFDVE9SLkNSRUQuSU5GT05BVklULE5PLlNFR1VSTy5TT0NJQUwsUkZDLEFQRUxMSURPUyxOT01CUkUsTi4uLkNSRURJVE8uSU5GT05BVklULFg0dG8ubWVzLFByaW1lci5tZXMpKQpzdW1tYXJ5KGNvbGFiMikKYGBgCgoqKlTDqWNuaWNhIDUsICpWQUxPUkVTIEZBTFRBTlRFUyoqKiAgICAKQ29uIHVuIHLDoXBpZG8gdmlzdGF6byBkZSBsYSBiYXNlIGRlIGRhdG9zLCBzZSBwb2TDrWEgb2JzZXJ2YXIgcXVlIGZhbHRhYmFuIGNpZXJ0b3MgdmFsb3JlcyBlbiBsYXMgZGlmZXJlbnRlcyB2YXJpYWJsZXMuIFNpIGZhbHRhYmFuIG11Y2hvcyBkYXRvcyBlbiB1bmEgc29sYSB2YXJpYWJsZSwgc2Ugb3B0w7MgcG9yIGVsaW1pbmFyIGxhICpjb2x1bW5hKiwgbWllbnRyYXMgcXVlIGVuIG90cm9zIGNhc29zIGRvbmRlIGhheWEgcG9jb3MgcG9yIHZhcmlhYmxlcyBzw7NsbyBzZSBlbGltaW5hcm9uIGxvcyByZWdpc3Ryb3MuICAgIAogIAoKwr9DdcOhbnRvcyBOQSB0ZW5nbyBwb3IgdmFyaWFibGU/CmBgYHtyfQpzYXBwbHkoY29sYWIyLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAogIAogICAgCiAgICAKU2Ugb2JzZXJ2YW4gbXVjaG9zIE5BcyBlbiBsYSBjb2x1bW5hICJCQUpBIiwgcG9yIGxvIHF1ZSBzZSBlbGltaW5hIGVuIHN1IHRvdGFsaWRhZCBlc3RhIHZhcmlhYmxlIChjb2x1bW5hKS4KYGBge3J9CmNvbGFiMzwtY29sYWIyCmNvbGFiMzwtc3Vic2V0KGNvbGFiMixzZWxlY3Q9LWMoQkFKQSkpCnN1bW1hcnkoY29sYWIzKQpgYGAKCipTZSB2dWVsdmUgYSB2ZXIgbG8gZGUgTkFzKiAgCiAgCgrCv0N1w6FudG9zIE5BIHRlbmdvIHBvciB2YXJpYWJsZT8KYGBge3J9CnNhcHBseShjb2xhYjMsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCiAgCiAgICAKRWxpbWluYXIgcmVuZ2xvbmVzIGRlIE5BcwpgYGB7cn0KY29sYWI0PC1jb2xhYjMKY29sYWI0PC1uYS5vbWl0KGNvbGFiNCkgICAgICAKc3VtbWFyeShjb2xhYjQpCnNhcHBseShjb2xhYjQsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCiAgIAogICAgIApFbGltaW5hciBlcnJvciBkZSBmZWNoYSBkZSBuYWNpbWllbnRvIChleGlzdGVuIGRhdG9zIHF1ZSBtdWVzdHJhbiBxdWUgYWxndWllbiBuYWNpw7MgZW4gZWwgMjAyMikuIFNlIG1hbnRpZW5lbiBzb2xvIGEgbG9zIGNvbGFib3JhZG9yZXMgcXVlIG5hY2llcm9uIGFudGVzIGRlbCAyMDA0LCBsb3MgbWF5b3JlcyBkZSBlZGFkLgpgYGB7cn0KY29sYWI1PC1jb2xhYjQKY29sYWI1PC1jb2xhYjVbY29sYWI1JEHDkU8uREUuTkFDSU1JRU5UTzwyMDA0LF0Kc3VtbWFyeShjb2xhYjUpCmBgYAogIAogICAgClNlIGltcG9ydGEgKGRlIG51ZXZvKSBsYSBiYXNlIGRlIGRhdG9zLCBkZWJpZG8gYSBxdWUgc2UgaGljaWVyb24gY2FtYmlvcyBlbiBlbCBleGNlbDogc2UgY2FsY3Vsw7MgbGEgZWRhZCwgZW4gdmV6IGRlIHRlbmVyIGVsIGHDsW8gZGUgbmFjaW1pZW50by4gCmBgYHtyfQpjb2xhYjY8LXJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL0Rlc2t0b3Avd2FsL3JsaW1waWEuY3N2IikKc3VtbWFyeShjb2xhYjYpCmBgYAogIAogIApTZSB2dWVsdmUgYSBhbmFsaXphciwgc2UgZWxpbWluYW4gY29sdW1uYXMgKG8gdmFyaWFibGVzKSBxdWUgbm8gc2UgY29uc2lkZXJhbiByZWxldmFudGVzIHBhcmEgZWwgYW7DoWxpc2lzIGRlIGRhdG9zLgpgYGB7cn0KY29sYWI3PC1jb2xhYjYKY29sYWI3PC1zdWJzZXQoY29sYWI2LHNlbGVjdD0tYyhQcmltZXIubWVzLFg0dG8ubWVzLERFUEFSVEFNRU5UTyxFbXBsZWFkbykpCgpgYGAKICAKICAgICAKU2UgY2FtYmlhbiBsb3Mgbm9tYnJlcyBkZSBjb2x1bW5hcyBjb24gZWwgZmluIGRlIGZhY2lsaXRhciBlbCBhbsOhbGlzaXMgcXVlIHNlcsOhIGxsZXZhZG8gYSBjb250aW51YWNpw7NuLiAKYGBge3J9CnN0cihjb2xhYjcpCm5hbWVzIChjb2xhYjcpID0gYygiZWRhZCIsICJnZW5lcm8iLCAiYWx0YSIsICJwdWVzdG8iLCAic2FsYXJpb19kaWFyaW8iLCAibHVnYXIubmFjaW0uIiwibXBpbyIsImVzdGFkbyIsImNpdmlsIikKbmFtZXMgKGNvbGFiNykKc3RyKGNvbGFiNykKYGBgCiAgCipFWFBPUlRBUiBCQVNFIERFIERBVE9TKgpgYGB7cn0KY29sYWJfYmQgPC0gY29sYWI3CndyaXRlLmNzdihjb2xhYl9iZCwgZmlsZSA9ImNvbGFiX0ZPUk1fbGltcGlhLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCgoKU2VndWltb3MgY29uICJiYWphcyIKCgpgYGB7cn0KYmFqYXM8LXJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL0Rvd25sb2Fkcy9iYWphcy5jc3YiKQpzdW1tYXJ5KGJhamFzKQpgYGAKKipUw6ljbmljYSA0LCAqQ09OVkVSVElSIFRJUE8gREUgREFUT1MqKiogICAgIAoKRGVzcHXDqXMgZGUgbm90YXIgcXVlIGNpZXJ0YXMgdmFyaWFibGVzIGN1YW50aXRhdGl2YXMgbm8gc29uIGxlw61kYXMgY29tbyB0YWwsIGVzIG5lY2VzYXJpbyBjb252ZW50aXJsYXMgIGRlIGNhcsOhY3RlciBhIGZlY2hhIG8gYSBlbnRlcm8sIGNvbW8gY29ycmVzcG9uZGEuICAgCgogIApDb252ZXJ0aXIgZGUgY2Fyw6FjdGVyIGEgZmVjaGEKYGBge3J9CmJhamFzMjwtYmFqYXMKYmFqYXMyJEZFQ0hBLkRFLkFMVEE8LWFzLkRhdGUoYmFqYXMyJEZFQ0hBLkRFLkFMVEEsZm9ybWF0PSIlZC8lbS8lWSIpICAKc3RyKGJhamFzMikKYGBgCiAgCkNvbnZlcnRpciBkZSBjYXLDoWN0ZXIgYSBlbnRlcm8KYGBge3J9CmJhamFzMzwtYmFqYXMyCmJhamFzMyRFREFEPC1hcy5pbnRlZ2VyKGJhamFzMyRFREFEKQpzdHIoYmFqYXMzKQpgYGAKCioqVMOpY25pY2EgMSwgKlJFTU9WRVIgREFUT1MgSVJSRUxFVkFOVEVTKioqICAgIAoKU2Ugbm90YSBxdWUgZXhpc3RlIHVuYSBjb2x1bW5hIHF1ZSBubyBhcG9ydGEgbXVjaG8gYWwgYW7DoWxpc2lzLCBsYSAgZmVjaGEgZGUgYmFqYSwgZGViaWRvIGEgcXVlIHlhIHNlIHRpZW5lbiBsb3MgZMOtYXMgZGUgZHVyYWNpw7NuIGRlIGxhIHBlcnNvbmEuIFNlIG9wdGEgcG9yIHJlbW92ZXIgZXN0ZSBkYXRvIGlycmVsZXZhbnRlLiAgICAKICAKRWxpbWluYXIgY29sdW1uYXMKYGBge3J9CmJhamFzNDwtYmFqYXMzCmJhamFzNDwtc3Vic2V0KGJhamFzNCxzZWxlY3Q9LWMoQkFKQSkpCnN1bW1hcnkoYmFqYXM0KQpgYGAKCioqVMOpY25pY2EgNSwgKlZBTE9SRVMgRkFMVEFOVEVTKioqICAgIAoKUGFyYSBvYnRlbmVyIHVuIGFuw6FsaXNpcyBtw6FzIHJlYWwsIHNlIGlkZW50aWZpY2FuIGxvcyB2YWxvcmVlcyBmYWx0YW50ZXMgeSBzZSB0b21hIHVuYSBkZWNpc2nDs24gZGVwZW5kaWVuZG8gZGUgbG9zIHJlc3VsdGFkb3MuICAgIAoKCsK/Q3XDoW50b3MgTkEgdGVuZ28gcG9yIHZhcmlhYmxlPwpgYGB7cn0Kc2FwcGx5KGJhamFzNCxmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQpgYGAKICAgCiAgICAgCkRlYmlkbyBhIHF1ZSAxMyBlcyB1biBuw7ptZXJvIGltcG9ydGFudGUgZW4gZXN0YSBiYXNlIGRlIGRhdG9zLCBzZSBoYSBkZWNpZGlkbyBOTyBib3JyYXIgbG9zIHJlZ2lzdHJvczsgc2Ugb3B0YSBwb3IgcmVlbXBsYXphciBOQXMgcG9yIHN1IG1lZGlhbmEuCmBgYHtyfQpiYWphczU8LWJhamFzNApiYWphczUkRMONQVMuREUuRFVSQUNJw5NOW2lzLm5hKGJhamFzNSREw41BUy5ERS5EVVJBQ0nDk04pXTwtIG1lZGlhbihiYWphczUkRMONQVMuREUuRFVSQUNJw5NOLCBuYS5ybT1UUlVFKQpgYGAKCsK/Q3XDoW50b3MgTkEgdGVuZ28gcG9yIHZhcmlhYmxlPwpgYGB7cn0Kc2FwcGx5KGJhamFzNSxmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQpgYGAKClF1ZWRhIGxpbXBpby4gICAKICAgCgpTZSBjYW1iaWFuIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcyBwb3Igbm9tYnJlcyBtw6FzIGFkZWN1YWRvcyBwYXJhIGVsIGFuw6FsaXNpczoKYGBge3J9CnN0cihiYWphczUpCm5hbWVzIChiYWphczUpID0gYygibm9tYnJlIiwgImVkYWQiLCAiZ2VuZXJvIiwgImFsdGEiLCAibW90aXZvX2JhamEiLCAiZHVyYWNpb24iLCAicHVlc3RvIiwic2FsYXJpb19kaWFyaW8iLCJlc3RhZG8iLCJjaXZpbCIpCm5hbWVzIChiYWphczUpCnN0cihiYWphczUpCmBgYAogICAKU2UgZWxpbWluYSB0YW1iacOpbiBsYSB2YXJpYWJsZSBkZSAibm9tYnJlIiBhbCByZXN1bHRhciBwb2NvIGludGVyZXNhbnRlIHBhcmEgZWwgYW7DoWxpc2lzLiAgCmBgYHtyfQpiYWphczY8LWJhamFzNQpiYWphczY8LXN1YnNldChiYWphczYsc2VsZWN0PS1jKG5vbWJyZSkpCnN0cihiYWphczYpCmBgYAogIApDYW1iaWFyICJGRU1FTklOTyIgcG9yICJGIiwgIHkgIk1BU0NVTElOTyIgcG9yICJNIi4gU2UgaGEgb3B0YWRvIHBvciBoYWNlciBlc3RvIHBhcmEgdGVuZXIgbGEgaW5mb3JtYWNpw7NuIG3DoXMgbGltcGlhIGVuIGxhcyBncsOhZmljYXMuIApgYGB7cn0KY29sYWI3JGdlbmVyb1tjb2xhYjckZ2VuZXJvID09ICJGRU1FTklOTyJdIDwtICJGIgpjb2xhYjckZ2VuZXJvW2NvbGFiNyRnZW5lcm8gPT0gIk1BU0NVTElOTyJdIDwtICJNIgpzdHIoY29sYWI3KQpgYGAKCmBgYHtyfQpiYWphczYkZ2VuZXJvW2JhamFzNiRnZW5lcm8gPT0gIkZFTUVOSU5PIl0gPC0gIkYiCmJhamFzNiRnZW5lcm9bYmFqYXM2JGdlbmVybyA9PSAiTUFTQ1VMSU5PIl0gPC0gIk0iCnN0cihiYWphczYpCmBgYAogICAgCipFWFBPUlRBUiBCQVNFIERFIERBVE9TKgpgYGB7cn0KYmFqYXNfYmQgPC0gYmFqYXM2CndyaXRlLmNzdihiYWphc19iZCwgZmlsZSA9ImJhamFzX0ZPUk1fbGltcGlhLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCiAgIAojIyMgKipGT1JNLSBEZWxpdmVyeSBwbGFuKiogIAoKU2UganVudGFyb24gbG9zIGRhdG9zIHkgbGEgbGltcGlhIGRlIGxhIGJhc2UgZGUgZGF0b3MgcHJpbmNpcGFsIGRlc2RlIGV4Y2VsLiAgICAgIAoKTGEgZmVjaGEgc2UgZW5jb250cmFiYSBlbiBmb3JtYXRvIGRlIEVVQSwgc2UgaGEgY2FtYmlhZG8gYSBNw6l4aWNvLiAgCgpgYGB7cn0KcGxhbjwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvRG93bmxvYWRzL0RFTElWRVJZIFBMQU4gYmRmX1BydWViYSAoMSkuY3N2IikKc3VtbWFyeShwbGFuKQpgYGAKCiAgIApTZSBjYW1iaWFuIGxvcyBub21icmVzIHBhcmEgdGVuZXIgdW4gZm9ybWF0byBtw6FzIGxpbXBpbyB5IHZvbHZlciBlbCBhbsOhbGlzaXMgbWVqb3IuIApgYGB7cn0KbmFtZXMgKHBsYW4pID0gYygiSURfZmVjaGEiLCAiZmVjaGEiLCAiY2xpZW50ZSIsICJwZWRpZG9zIikKbmFtZXMgKHBsYW4pCnN0cihwbGFuKQpgYGAKCioqVMOpY25pY2EgNCwgKkNPTlZFUlRJUiBUSVBPIERFIERBVE9TKioqICAgICAgICAKClNlIGNhbWJpYSBlbCBmb3JtYXRvIGRlIGxhIGZlY2hhPSBkZSBjYXLDoWN0ZXIgYSBmZWNoYS4gICAKYGBge3J9CnBsYW4yPC1wbGFuCnBsYW4yJGZlY2hhPC1hcy5EYXRlKHBsYW4yJGZlY2hhLGZvcm1hdD0iJWQvJW0vJVkiKSAgCnN0cihwbGFuMikKYGBgCgoqKlTDqWNuaWNhIDUsICpWQUxPUkVTIEZBTFRBTlRFUyoqKiAgICAKCsK/Q3XDoW50b3MgTkEgdGVuZ28gcG9yIHZhcmlhYmxlPyAgIApgYGB7cn0Kc2FwcGx5KHBsYW4yLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAoKRXhwb3J0YXIgYmFzZSBkZSBkYXRvcyAgCmBgYHtyfQpwbGFuX2ZpbmFsIDwtIHBsYW4yCndyaXRlLmNzdihwbGFuX2ZpbmFsLCBmaWxlID0iZGVsaXZlcnlwbGFuX2ZpbmFsMi5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgoKCiMjIyAqKkZPUk0tIERlbGl2ZXJ5IHBlcmZvcm1hbmNlKiogIAogICAgIApJbXBvcnRhciBiYXNlIGRlIGRhdG9zCmBgYHtyfQpwZXJmPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9Eb3dubG9hZHMvZGVsaXZlcnBlcmYuY3N2IikKc3VtbWFyeShwZXJmKQpgYGAKCmBgYHtyfQpwZXJmMjwtcGVyZgpzdHIocGVyZjIpCmBgYAoKKipUw6ljbmljYSA1LCAqVkFMT1JFUyBGQUxUQU5URVMqKiogICAgCgrCv0N1w6FudG9zIE5BIHRlbmdvIHBvciB2YXJpYWJsZT8KYGBge3J9CnNhcHBseShwZXJmMixmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQpgYGAKCioqVMOpY25pY2EgMSwgKlJFTU9WRVIgREFUT1MgSVJSRUxFVkFOVEVTKioqICAgIAoKClNlIGNhbWJpYW4gZGlmZXJlbnRlcyB2YXJpYWJsZXMgZGUgY2Fyw6FjdGVyIGEgZW50ZXJvCmBgYHtyfQpwZXJmMiRSZWFsLmFycml2YWwgPC0gc3Vic3RyKHBlcmYyJFJlYWwuYXJyaXZhbCwgc3RhcnQgPSAxLCBzdG9wID0gMikKcGVyZjIkUmVhbC5hcnJpdmFsPC0gYXMuaW50ZWdlcihwZXJmMiRSZWFsLmFycml2YWwpICAKcGVyZjIkUmVhbC5EZXBhcnR1cmUgPC0gc3Vic3RyKHBlcmYyJFJlYWwuRGVwYXJ0dXJlLCBzdGFydCA9IDEsIHN0b3AgPSAyKQpwZXJmMiRSZWFsLkRlcGFydHVyZTwtIGFzLmludGVnZXIocGVyZjIkUmVhbC5EZXBhcnR1cmUpICAKcGVyZjIkUGxhbi5hcnJpdmFsIDwtIHN1YnN0cihwZXJmMiRQbGFuLmFycml2YWwsIHN0YXJ0ID0gMSwgc3RvcCA9IDIpCnBlcmYyJFBsYW4uYXJyaXZhbDwtIGFzLmludGVnZXIocGVyZjIkUGxhbi5hcnJpdmFsKSAgCnN0cihwZXJmMikKYGBgCgogIApTZSBlbGltaW5hbiBjb2x1bW5hcyBxdWUgbm8gYXBvcnRhbiBtdWNobyBhbCBhbsOhbGlzaXMKYGBge3J9CnBlcmYzPC1wZXJmMgpwZXJmMzwtc3Vic2V0KHBlcmYzLHNlbGVjdD0tYyhUcmFuc3BvcnRpc3RhLFBsYW4uYXJyaXZhbCxSZWFsLmFycml2YWwsUmVhbC5EZXBhcnR1cmUpKQpzdW1tYXJ5KHBlcmYzKQpgYGAKICAgIApTZSByZW5vbWJyYW4gbGFzIGNvbHVtbmFzCmBgYHtyfQpuYW1lcyhwZXJmMyk9YygiY2xpZW50ZSIsImZlY2hhIiwiZGlmIikKc3RyKHBlcmYzKQpgYGAKICAgIApFeHBvcnRhciBiYXNlIGRlIGRhdG9zCmBgYHtyfQpwZXJmb3JtYW5jZV9maW5hbCA8LSBwZXJmMwp3cml0ZS5jc3YocGVyZm9ybWFuY2VfZmluYWwsIGZpbGUgPSJkZWxpdmVyeXBlcmZvcm1hbmNlX2ZpbmFseWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKCgoKIyMjICoqRk9STS0gUHJvZHVjY2nDs24qKiAgCgpQYXJhIGVzdGEgYmFzZSBkZSBkYXRvcywgc2UgdGllbmUgbGEgaW5mb3JtYWNpw7NuIHRyZXMgbWVzZXMgKGNhc2kgY29tcGxldG9zKToganVsaW8sIGFnb3N0bywgc2VwdGllbWJyZS4gICAgCgpJbXBvcnRhciBiYXNlIGRlIGRhdG9zIEFHT1NUTy4gw5puaWNvIG1lcyBjb21wbGV0by4gIApgYGB7cn0KcHJvZDwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvRG93bmxvYWRzL3Byb2R1Y2Npb24uY3N2IikKc3VtbWFyeShwcm9kKQpgYGAKCioqVMOpY25pY2EgMSwgKlJFTU9WRVIgREFUT1MgSVJSRUxFVkFOVEVTKioqICAgIApObyB0b2RhcyBsYXMgdmFyaWFibGVzIG5vcyBpbnRlcmVzYW4sIG8gYXBvcnRhbiBtdWNobywgYWwgYW7DoWxpc2lzIHF1ZSBzZSBwcmV0ZW5kZSBoYWNlci4gU2UgaGEgZGVmaW5pZG8gcXVlIGxhcyB2YXJpYWJsZXMgY29uICBsYXMgcXVlIHNlIGRlc2VhIHF1ZWRhciBzb24gbGFzIHNpZ3VpZW50ZXM6IGNsaWVudGUsIHBpZXphcyBwcm9ncmFtYWRhcywgdGllbXBvIG3DrW5pbW8sIGzDoW1pbmFzIHByb2Nlc2FkYXMgeSB0aWVtcG8gZGUgY2FsaWRhZC4gICAgCgpTZSBlbGltaW5hbiBjb2x1bW5hcwpgYGB7cn0KcHJvZDI8LXByb2QKcHJvZDI8LXN1YnNldChwcm9kMixzZWxlY3Q9LWMoSUQuRk9STSxQUk9EVUNUTyxIUi4uRklOLEVTVEFDSU9OLkFSUkFOUVVFLElOSUNJTy5TRVAuVVAsRklOLklOSUNJTy5ERS5TRVAuVVAsSU5JQ0lPLmRlLlBST0NFU08sRklOLmRlLlBST0NFU08pKQpzdW1tYXJ5KHByb2QyKQpgYGAKCioqVMOpY25pY2EgNCwgKkNPTlZFUlRJUiBUSVBPIERFIERBVE9TKioqICAgICAgCgpQYXJhIHBvZGVyIGRldGVybWluYXIgbG9zIHZhbG9yZXMgZmFsdGFudGVzLCBlcyBuZWNlc2FyaW8gY29udmVydGlyIGxvcyB2YWxvcmVzIGRlIGNhcsOhY3RlciBhIGVudGVyb3MvaG9yYS9mZWNoYS4gICAgCgpDYXLDoWN0ZXIgYSBlbnRlcm8KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcHJvZDM8LXByb2QyCnByb2QzJFBJRVpBUy5QUk9HLjwtYXMuaW50ZWdlcihwcm9kMyRQSUVaQVMuUFJPRy4pCnN0cihwcm9kMykKCnN1bW1hcnkocHJvZDMpCmBgYAoKQ2Fyw6FjdGVyIGEgaG9yYQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcm9kNDwtcHJvZDMKcHJvZDQkVE1PLi5NSU4uPC1hcy5pbnRlZ2VyKHByb2Q0JFRNTy4uTUlOLikKc3RyKHByb2Q0KQpgYGAKCkNhcsOhY3RlciBhIGVudGVybwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcm9kNTwtcHJvZDQKcHJvZDUkTGFtaW5hcy5wcm9jZXNhZGFzPC1hcy5pbnRlZ2VyKHByb2Q1JExhbWluYXMucHJvY2VzYWRhcykKc3RyKHByb2Q1KQpgYGAKCkNhcsOhY3RlciBhIGhvcmEKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcHJvZDY8LXByb2Q1CnByb2Q2JFRJRU1QTy5DQUxJREFEPC1hcy5pbnRlZ2VyKHByb2Q2JFRJRU1QTy5DQUxJREFEKQpzdHIocHJvZDYpCmBgYAoKKipUw6ljbmljYSA1LCAqVkFMT1JFUyBGQUxUQU5URVMqKiogICAgIAoKQWwgaGFiZXIgY2FtYmlhZG8geWEgbG9zIHZhbG9yZXMgYSBzdSBmb3JtYSBjb3JyZXNwb25kaWVudGUsIG9ic2VydmFtb3MgcXVlIGV4aXN0ZSB1bmEgZ3JhbiBjYW50aWRhZCBkZSB2YWxvcmVzIGZhbHRhbnRlcyAoYXByb3guIGxhIG1pdGFkKS4gU2UgaGEgb3B0YWRvIHBvciBjYW1iaWFyIHBvciAqKm1lZGlhbmEqKiBvIHBvciAqKnByb21lZGlvKiogZGVwZW5kaWVuZG8gZGVsIGNhc28uICAgIAoKwr9DdcOhbnRvcyBOQSAgdGVuZ28gcG9yIHZhcmlhYmxlcz8KYGBge3J9CnNhcHBseShwcm9kNixmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQpgYGAKCkRlc3B1w6lzIGRlIHZlcmlmaWNhciBjYWRhIHZhcmlhYmxlLCAgc2UgaGEgb3B0YWRvIHBvciBjYW1iaWFyIFRPRE9TIGxvcyB2YWxvcmVzIGZhbHRhbnRlcyBkZSBsYXMgNCB2YXJpYWJsZXMgcG9yIGxhIG1lZGlhbmEgZGUgY2FkYSB1bm8uICAgICAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcHJvZDc8LXByb2Q2CnByb2Q3JFBJRVpBUy5QUk9HLltpcy5uYShwcm9kNyRQSUVaQVMuUFJPRy4pXTwtIG1lZGlhbihwcm9kNyRQSUVaQVMuUFJPRy4sIG5hLnJtPVRSVUUpCgoKcHJvZDg8LXByb2Q3CnByb2Q4JFRNTy4uTUlOLltpcy5uYShwcm9kOCRUTU8uLk1JTi4pXTwtIG1lZGlhbihwcm9kOCRUTU8uLk1JTi4sIG5hLnJtPVRSVUUpCgoKcHJvZDk8LXByb2Q4CnByb2Q5JExhbWluYXMucHJvY2VzYWRhc1tpcy5uYShwcm9kOSRMYW1pbmFzLnByb2Nlc2FkYXMpXTwtIG1lZGlhbihwcm9kOSRMYW1pbmFzLnByb2Nlc2FkYXMsIG5hLnJtPVRSVUUpCgpwcm9kMTA8LXByb2Q5CnByb2QxMCRUSUVNUE8uQ0FMSURBRFtpcy5uYShwcm9kMTAkVElFTVBPLkNBTElEQUQpXTwtIG1lZGlhbihwcm9kMTAkVElFTVBPLkNBTElEQUQsIG5hLnJtPVRSVUUpCmBgYAoKwr9DdcOhbnRvcyBOQSAgdGVuZ28gcG9yIHZhcmlhYmxlcz8gICAKYGBge3J9CnNhcHBseShwcm9kMTAsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCgoKYGBge3J9CnByb2RfYWdvc3RvPC1wcm9kMTAKd3JpdGUuY3N2KHByb2RfYWdvc3RvLCBmaWxlID0icHJvZF9hZ29zdG8uY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAogIApCYXNlIGRlIGRhdG9zIGRlIHByb2R1Y2Npw7NuIGRlIFNFUFRJRU1CUkUKYGBge3J9CnByb2Rfc2VwdDwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvRG93bmxvYWRzL3Byb2R1Y2Npb25fc2VwdC5jc3YiKQpzdW1tYXJ5KHByb2Rfc2VwdCkKYGBgCioqVMOpY25pY2EgMSwgKlJFTU9WRVIgREFUT1MgSVJSRUxFVkFOVEVTKioqICAgIApObyB0b2RhcyBsYXMgdmFyaWFibGVzIG5vcyBpbnRlcmVzYW4sIG8gYXBvcnRhbiBtdWNobywgYWwgYW7DoWxpc2lzIHF1ZSBzZSBwcmV0ZW5kZSBoYWNlci4gU2UgaGEgZGVmaW5pZG8gcXVlIGxhcyB2YXJpYWJsZXMgY29uICBsYXMgcXVlIHNlIGRlc2VhIHF1ZWRhciBzb24gbGFzIHNpZ3VpZW50ZXM6IGNsaWVudGUsIHBpZXphcyBwcm9ncmFtYWRhcywgdGllbXBvIG3DrW5pbW8sIGzDoW1pbmFzIHByb2Nlc2FkYXMgeSB0aWVtcG8gZGUgY2FsaWRhZC4gICAgCgpTZSBlbGltaW5hbiBjb2x1bW5hcwpgYGB7cn0KcHJvZF9zMjwtcHJvZF9zZXB0CnByb2RfczI8LXN1YnNldChwcm9kX3MyLHNlbGVjdD0tYyhJRC5GT1JNLFBST0RVQ1RPLEhSLi5GSU4sRVNUQUNJT04uQVJSQU5RVUUsSU5JQ0lPLlNFUC5VUCxJTklDSU8uZGUuUFJPQ0VTTyxGSU4uZGUuUFJPQ0VTTyxYLGZpbi5kZS5zZXQudXApKQpzdW1tYXJ5KHByb2RfczIpCmBgYAoKKipUw6ljbmljYSA0LCAqQ09OVkVSVElSIFRJUE8gREUgREFUT1MqKiogICAgIAoKUGFyYSBwb2RlciBkZXRlcm1pbmFyIGxvcyB2YWxvcmVzIGZhbHRhbnRlcywgZXMgbmVjZXNhcmlvIGNvbnZlcnRpciBsb3MgdmFsb3JlcyBkZSBjYXLDoWN0ZXIgYSBlbnRlcm9zL2hvcmEvZmVjaGEuICAKCkNhcsOhY3RlciBhIGVudGVybwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcm9kX3MzPC1wcm9kX3MyCnByb2RfczMkUElFWkFTLlBST0cuPC1hcy5pbnRlZ2VyKHByb2RfczMkUElFWkFTLlBST0cuKQpzdHIocHJvZF9zMykKCnN1bW1hcnkocHJvZF9zMykKYGBgCgpDYXLDoWN0ZXIgYSBob3JhCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnByb2RfczQ8LXByb2RfczMKcHJvZF9zNCRUTU8uLk1JTi48LWFzLmludGVnZXIocHJvZF9zNCRUTU8uLk1JTi4pCnN0cihwcm9kX3M0KQpgYGAKCkNhcsOhY3RlciBhIGVudGVybwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcm9kX3M1PC1wcm9kX3M0CnByb2RfczUkTGFtaW5hcy5wcm9jZXNhZGFzPC1hcy5pbnRlZ2VyKHByb2RfczUkTGFtaW5hcy5wcm9jZXNhZGFzKQpzdHIocHJvZF9zNSkKYGBgCgpDYXLDoWN0ZXIgYSBob3JhCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnByb2RfczY8LXByb2RfczUKcHJvZF9zNiRUSUVNUE8uQ0FMSURBRDwtYXMuaW50ZWdlcihwcm9kX3M2JFRJRU1QTy5DQUxJREFEKQpzdHIocHJvZF9zNikKYGBgCgoqKlTDqWNuaWNhIDUsICpWQUxPUkVTIEZBTFRBTlRFUyoqKiAgICAgCgpBbCBoYWJlciBjYW1iaWFkbyB5YSBsb3MgdmFsb3JlcyBhIHN1IGZvcm1hIGNvcnJlc3BvbmRpZW50ZSwgb2JzZXJ2YW1vcyBxdWUgZXhpc3RlIHVuYSBncmFuIGNhbnRpZGFkIGRlIHZhbG9yZXMgZmFsdGFudGVzIChhcHJveC4gbGEgbWl0YWQpLiBTZSBoYSBvcHRhZG8gcG9yIGNhbWJpYXIgcG9yICoqbWVkaWFuYSoqIG8gcG9yICoqcHJvbWVkaW8qKiBkZXBlbmRpZW5kbyBkZWwgY2Fzby4gICAgCgrCv0N1w6FudG9zIE5BICB0ZW5nbyBwb3IgdmFyaWFibGVzPwpgYGB7cn0Kc2FwcGx5KHByb2RfczYsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCgpFbiBlc3RlIGNhc28sIHRhbWJpw6luLHNlIHRvbWEgZW5lIGN1ZW50YSBsYSBtZWRpYW5hIHBhcmEgbG9zIHZhbG9yZXMgZmFsdGFudGVzLiAKYGBge3J9CnByb2Rfczc8LXByb2RfczYKcHJvZF9zNyRQSUVaQVMuUFJPRy5baXMubmEocHJvZF9zNyRQSUVaQVMuUFJPRy4pXTwtIG1lZGlhbihwcm9kX3M3JFBJRVpBUy5QUk9HLiwgbmEucm09VFJVRSkKCgpwcm9kX3M4PC1wcm9kX3M3CnByb2RfczgkVE1PLi5NSU4uW2lzLm5hKHByb2RfczgkVE1PLi5NSU4uKV08LSBtZWRpYW4ocHJvZF9zOCRUTU8uLk1JTi4sIG5hLnJtPVRSVUUpCgoKcHJvZF9zOTwtcHJvZF9zOApwcm9kX3M5JExhbWluYXMucHJvY2VzYWRhc1tpcy5uYShwcm9kX3M5JExhbWluYXMucHJvY2VzYWRhcyldPC0gbWVkaWFuKHByb2RfczkkTGFtaW5hcy5wcm9jZXNhZGFzLCBuYS5ybT1UUlVFKQoKcHJvZF9zMTA8LXByb2RfczkKcHJvZF9zMTAkVElFTVBPLkNBTElEQURbaXMubmEocHJvZF9zMTAkVElFTVBPLkNBTElEQUQpXTwtIG1lZGlhbihwcm9kX3MxMCRUSUVNUE8uQ0FMSURBRCwgbmEucm09VFJVRSkKYGBgCgpZYSBubyBzZSB0aWVuZW4gTkFzLgoKYGBge3J9CnByb2RfczExPC1wcm9kX3MxMApwcm9kX3MxMTwtbmEub21pdChwcm9kX3MxMSkgICAgICAKc3VtbWFyeShwcm9kX3MxMSkKc2FwcGx5KHByb2RfczExLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAoKU2UgZXhwb3J0YSBlbCBtZXMgZGUgc2VwdGllbWJyZQpgYGB7cn0KcHJvZF9zZXB0aWVtYnJlPC1wcm9kX3MxMQp3cml0ZS5jc3YocHJvZF9zZXB0aWVtYnJlLCBmaWxlID0icHJvZF9zZXB0aWVtYnJlLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKClNlIGNvbnRpbnVhIGNvbiBsYSBiYXNlIGRlIGRhdG9zIGRlIEpVTElPCgpgYGB7cn0KcHJvZF9qdWw8LXJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL0Rvd25sb2Fkcy9wcm9kdWNjaW9uX2p1bGlvLmNzdiIpCnN1bW1hcnkocHJvZF9qdWwpCmBgYAoKKipUw6ljbmljYSAxLCAqUkVNT1ZFUiBEQVRPUyBJUlJFTEVWQU5URVMqKiogICAgIApObyB0b2RhcyBsYXMgdmFyaWFibGVzIG5vcyBpbnRlcmVzYW4sIG8gYXBvcnRhbiBtdWNobywgYWwgYW7DoWxpc2lzIHF1ZSBzZSBwcmV0ZW5kZSBoYWNlci4gU2UgaGEgZGVmaW5pZG8gcXVlIGxhcyB2YXJpYWJsZXMgY29uICBsYXMgcXVlIHNlIGRlc2VhIHF1ZWRhciBzb24gbGFzIHNpZ3VpZW50ZXM6IGNsaWVudGUsIHBpZXphcyBwcm9ncmFtYWRhcywgdGllbXBvIG3DrW5pbW8sIGzDoW1pbmFzIHByb2Nlc2FkYXMgeSB0aWVtcG8gZGUgY2FsaWRhZC4gICAgCgpTZSBlbGltaW5hbiBjb2x1bW5hcwpgYGB7cn0KcHJvZF9qMjwtcHJvZF9qdWwKcHJvZF9qMjwtc3Vic2V0KHByb2RfajIsc2VsZWN0PS1jKElELkZPUk0sUFJPRFVDVE8sSFIuLkZJTixFU1RBQ0lPTi5BUlJBTlFVRSxJTklDSU8uU0VQLlVQLElOSUNJTy5kZS5QUk9DRVNPLEZJTi5kZS5QUk9DRVNPLEZJTi5JTklDSU8uREUuU0VQLlVQKSkKc3VtbWFyeShwcm9kX2oyKQpgYGAKKipUw6ljbmljYSA0LCAqQ09OVkVSVElSIFRJUE8gREUgREFUT1MqKiogICAgIAoKUGFyYSBwb2RlciBkZXRlcm1pbmFyIGxvcyB2YWxvcmVzIGZhbHRhbnRlcywgZXMgbmVjZXNhcmlvIGNvbnZlcnRpciBsb3MgdmFsb3JlcyBkZSBjYXLDoWN0ZXIgYSBlbnRlcm9zL2hvcmEvZmVjaGEuICAKCkNhcsOhY3RlciBhIGVudGVybwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcm9kX2ozPC1wcm9kX2oyCnByb2RfajMkUElFWkFTLlBST0cuPC1hcy5pbnRlZ2VyKHByb2RfajMkUElFWkFTLlBST0cuKQpzdHIocHJvZF9qMykKCnN1bW1hcnkocHJvZF9qMykKYGBgCgpDYXLDoWN0ZXIgYSBob3JhCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnByb2RfajQ8LXByb2RfajMKcHJvZF9qNCRUTU8uLk1JTi48LWFzLmludGVnZXIocHJvZF9qNCRUTU8uLk1JTi4pCnN0cihwcm9kX2o0KQpgYGAKCkNhcsOhY3RlciBhIGVudGVybwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcm9kX2o1PC1wcm9kX2o0CnByb2RfajUkTGFtaW5hcy5wcm9jZXNhZGFzPC1hcy5pbnRlZ2VyKHByb2RfajUkTGFtaW5hcy5wcm9jZXNhZGFzKQpzdHIocHJvZF9qNSkKYGBgCgoKQ2Fyw6FjdGVyIGEgaG9yYQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcm9kX2o2PC1wcm9kX2o1CnByb2RfajYkVElFTVBPLkNBTElEQUQ8LWFzLmludGVnZXIocHJvZF9qNiRUSUVNUE8uQ0FMSURBRCkKc3RyKHByb2RfajYpCmBgYAoKKipUw6ljbmljYSA1LCAqVkFMT1JFUyBGQUxUQU5URVMqKiogICAgIAoKQWwgaGFiZXIgY2FtYmlhZG8geWEgbG9zIHZhbG9yZXMgYSBzdSBmb3JtYSBjb3JyZXNwb25kaWVudGUsIG9ic2VydmFtb3MgcXVlIGV4aXN0ZSB1bmEgZ3JhbiBjYW50aWRhZCBkZSB2YWxvcmVzIGZhbHRhbnRlcyAoYXByb3guIGxhIG1pdGFkKS4gU2UgaGEgb3B0YWRvIHBvciBjYW1iaWFyIHBvciAqKm1lZGlhbmEqKiBvIHBvciAqKnByb21lZGlvKiogZGVwZW5kaWVuZG8gZGVsIGNhc28uICAgCgrCv0N1w6FudG9zIE5BICB0ZW5nbyBwb3IgdmFyaWFibGVzPwpgYGB7cn0Kc2FwcGx5KHByb2RfajYsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCgpTZSB0b21hIGVsIHZhbG9yIGRlIGxhIG1lZGlhbmEgcGFyYSBjYW1iaWFyIGxvcyB2YWxvcmVzIGZhbHRhbnRlcyAKYGBge3J9CnByb2Rfajc8LXByb2RfajYKcHJvZF9qNyRQSUVaQVMuUFJPRy5baXMubmEocHJvZF9qNyRQSUVaQVMuUFJPRy4pXTwtIG1lZGlhbihwcm9kX2o3JFBJRVpBUy5QUk9HLiwgbmEucm09VFJVRSkKCgpwcm9kX2o4PC1wcm9kX3M3CnByb2RfajgkVE1PLi5NSU4uW2lzLm5hKHByb2RfajgkVE1PLi5NSU4uKV08LSBtZWRpYW4ocHJvZF9qOCRUTU8uLk1JTi4sIG5hLnJtPVRSVUUpCgoKcHJvZF9qOTwtcHJvZF9zOApwcm9kX2o5JExhbWluYXMucHJvY2VzYWRhc1tpcy5uYShwcm9kX2o5JExhbWluYXMucHJvY2VzYWRhcyldPC0gbWVkaWFuKHByb2RfajkkTGFtaW5hcy5wcm9jZXNhZGFzLCBuYS5ybT1UUlVFKQoKcHJvZF9qMTA8LXByb2RfajkKcHJvZF9qMTAkVElFTVBPLkNBTElEQURbaXMubmEocHJvZF9qMTAkVElFTVBPLkNBTElEQUQpXTwtIG1lZGlhbihwcm9kX2oxMCRUSUVNUE8uQ0FMSURBRCwgbmEucm09VFJVRSkKYGBgCgpZYSBubyBzZSB0aWVuZW4gdmFsb3JlcyBmYWx0YW50ZXMuIApgYGB7cn0KcHJvZF9qMTE8LXByb2RfajEwCnByb2RfajExPC1uYS5vbWl0KHByb2RfajExKSAgICAgIApzdW1tYXJ5KHByb2RfajExKQpzYXBwbHkocHJvZF9qMTEsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCgpTZSBleHBvcnRhIGVsIMO6bHRpbW8gbWVzOiBKVUxJTwpgYGB7cn0KcHJvZF9qdWxpbzwtcHJvZF9qMTEKd3JpdGUuY3N2KHByb2RfanVsaW8sIGZpbGUgPSJwcm9kX2p1bGlvLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCkpVTlRBTU9TIExBUyBUUkVSUyBCQVNFUyBERSBEQVRPUyAoanVsaW8sIGFnb3N0bywgc2VwdGllbWJyZSkuCgpgYGB7cn0KcHJvZHVjY2lvbl9mPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9wcm9kX2p1bGlvLmNzdiIpCnN1bW1hcnkocHJvZHVjY2lvbl9mKQpzYXBwbHkocHJvZHVjY2lvbl9mLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCnN0cihwcm9kdWNjaW9uX2YpCmBgYAoKU2UgY2FtYmlhIGVsIG5vbWJyZSBkZSBsYXMgY29sdW1uYXMKYGBge3J9Cm5hbWVzIChwcm9kdWNjaW9uX2YpID0gYygiY2xpZW50IiwgInBpZXphc19wcm9nIiwgInRtcG9fbWluIiwgImxhbWluYXNfcHJvYyIsICJ0aWVtcG9fY2FsaWRhZCIpCm5hbWVzIChwcm9kdWNjaW9uX2YpCmBgYAoKU2UgZXhwb3J0YSBsYSB2ZXJzacOzbiBmaW5hbApgYGB7cn0Kd3JpdGUuY3N2KHByb2R1Y2Npb25fZiwgZmlsZSA9InByb2R1Y2Npb25fZmluYWwuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKCiMjIyAqKkZPUk0tIE1lcm1hKiogIAoKYGBge3J9Cm1lcm1hPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9Eb3dubG9hZHMvbWVybWEuY3N2IikKc3VtbWFyeShtZXJtYSkKYGBgCgoqKlTDqWNuaWNhIDEsICpSRU1PVkVSIERBVE9TIElSUkVMRVZBTlRFUyoqKiAgICAKRGVzZGUgZWwgZXhjZWwgc2UgaGFuIGVsaW1pbmFkbyBsYXMgZmlsYXMgZGUgInRvdGFsICptZXMqIi4gICAgICAKCgoqKlTDqWNuaWNhIDQsICpDT05WRVJUSVIgVElQTyBERSBEQVRPUyoqKiAgICAgCgpQYXJhIHBvZGVyIGRldGVybWluYXIgbG9zIHZhbG9yZXMgZmFsdGFudGVzLCBlcyBuZWNlc2FyaW8gY29udmVydGlyIGxvcyB2YWxvcmVzIGRlIGNhcsOhY3RlciBhIGVudGVyb3MvaG9yYS9mZWNoYS4gIAoKCkNhcsOhY3RlciBhIGVudGVybwpgYGB7cn0KbWVybWEyPC1tZXJtYQptZXJtYTIkRmVjaGE8LWFzLkRhdGUobWVybWEyJEZlY2hhLGZvcm1hdD0iJWQvJW0vJXkiKSAgCnN0cihtZXJtYTIpCnN1bW1hcnkobWVybWEyKQpgYGAKCsK/Q3XDoW50b3MgTkEgdGVuZ28gcG9yIHZhcmlhYmxlPwpgYGB7cn0Kc2FwcGx5KG1lcm1hMixmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQpgYGAKCk5vIGV4aXN0ZW4gdmFsb3JlcyBmYWx0YW50ZXMgcG9yIHZhcmlhYmxlcy4gICAgCgoqKkV4cG9ydGFyIG51ZXZhIGJhc2UgZGUgZGF0b3MgKGxpbXBpYSkqKiAgIAoKYGBge3J9Cm1lcm1hX2ZpbmFsIDwtbWVybWEyCndyaXRlLmNzdihtZXJtYV9maW5hbCwgZmlsZSA9Im1lcm1hYV9maW5hbDIuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKCiMjIyAqKkZPUk0tIFNjcmFwKiogIAoKSW1wb3J0YXIgYmFzZSBkZSBkYXRvcwpgYGB7cn0Kc2NyYXA8LXJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL0Rvd25sb2Fkcy9zY3JhcC5jc3YiKQpgYGAKCioqVMOpY25pY2EgMSwgKlJFTU9WRVIgREFUT1MgSVJSRUxFVkFOVEVTKioqICAgICAgCk5vIHRvZGFzIGxhcyB2YXJpYWJsZXMgbm9zIGludGVyZXNhbiwgbyBhcG9ydGFuIG11Y2hvLCBhbCBhbsOhbGlzaXMgcXVlIHNlIHByZXRlbmRlIGhhY2VyLiBTZSBoYSBkZWZpbmlkbyBxdWUgbGFzIHZhcmlhYmxlcyBjb24gIGxhcyBxdWUgc2UgZGVzZWEgcXVlZGFyIHNvbiBsYXMgc2lndWllbnRlczogZmVjaGEsIGNhbnRpZGFkLCB5ICB1YmljYWNpw7NuIGRlIG9yaWdlbi4gICAKClNlIGVsaW1pbmFuIGNvbHVtbmFzCmBgYHtyfQpzY3JhcDI8LXNjcmFwCnNjcmFwMjwtc3Vic2V0KHNjcmFwMixzZWxlY3Q9LWMoUmVmZXJlbmNpYSxIb3JhLFByb2R1Y3RvLFVuaWRhZC5kZS5tZWRpZGEsVWJpY2FjacOzbi5kZS5kZXNlY2hvLEVzdGFkbykpCgpzdW1tYXJ5KHNjcmFwMikKYGBgCgoqKlTDqWNuaWNhIDQsICpDT05WRVJUSVIgVElQTyBERSBEQVRPUyoqKiAgICAgCgpQYXJhIHBvZGVyIGRldGVybWluYXIgbG9zIHZhbG9yZXMgZmFsdGFudGVzLCBlcyBuZWNlc2FyaW8gY29udmVydGlyIGxvcyB2YWxvcmVzIGRlIGNhcsOhY3RlciBhIGVudGVyb3MvaG9yYS9mZWNoYS4gICAKCgpDYXLDoWN0ZXIgYSBmZWNoYQpgYGB7cn0Kc2NyYXAzPC1zY3JhcDIKc2NyYXAzJEZlY2hhPC1hcy5EYXRlKHNjcmFwMyRGZWNoYSxmb3JtYXQ9IiVkLyVtLyVZIikgIApzdHIoc2NyYXAzKQpzdW1tYXJ5KHNjcmFwMykKYGBgCgrCv0N1w6FudG9zIE5BIHRlbmdvIHBvciB2YXJpYWJsZT8KYGBge3J9CnNhcHBseShzY3JhcDMsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCgpTZSBjYW1iaWFuIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcwpgYGB7cn0Kc2NyYXA0PC1zY3JhcDMKbmFtZXMgKHNjcmFwNCkgPSBjKCJmZWNoYSIsICJjYW50aWRhZCIsICJ1Ymlfb3JpZ2VuIikKbmFtZXMgKHNjcmFwNCkKYGBgCgoKRXhwb3J0YXIgbnVldmEgYmFzZSBkZSBkYXRvcyAobGltcGlhKQoKYGBge3J9CnNjcmFwX2ZpbmFseWEgPC1zY3JhcDQKd3JpdGUuY3N2KHNjcmFwX2ZpbmFseWEsIGZpbGUgPSJzY3JhcF9maW5hbHlhMi5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgojIyMgKipSZWZsZXhpw7NuKioKCioqwr9Dw7NtbyB0ZSBwZXJtaXRpw7MgbGEgYXBsaWNhY2nDs24gZGUgZGljaGFzIHTDqWNuaWNhcyB1bmEgbWVqb3IgY29tcHJlbnNpw7NuIGRlIGxhcyBiYXNlcyBkZSBkYXRvcz8qKiAgICAKCiAgCkVzdGFzIHTDqWNuaWNhcyByZXN1bHRhbiBkZSB1dGlsaWRhZCBhbCBwb2RlciB0ZW5lciBkYXRvcyBkZSBjYWxpZGFkIHkgbG8gbcOhcyByZWFsZXMgcG9zaWJsZXMuIERlYmlkbyBhIHF1ZSBhbCBmaW5hbCBzZSBkZXNlYSBwb2RlciBjb21wcmVuZGVyIGxhcyB0ZW5kZW5jaWFzIHkgbG8gcXVlIGVzdMOhIHBhc2FuZG8gZW4gbGEgb3JnYW5pemFjacOzbiBlbiBkaXZlcnNvcyB0ZW1hcywgY29tbyBsbyBzb246IHN1cyBhY3R1YWxlcyB5IHBhc2Fkb3MgY29sYWJvcmFkb3Jlcywgc3UgcHJvZHVjY2nDs24sIHN1ICpkZWxpdmVyeSBwZXJmb3JtYW5jZSB5IHBsYW4qLCBzdSBtZXJtYSB5IHN1IHNjcmFwLiBQb3IgbG8gdGFudG8sIGxvIGlkZWFsIGVzIHRyYWJhamFyIGNvbiBsb3MgZGF0b3MgbcOhcyByZWFsZXMgeSBjaWVydG9zIHBvc2libGVzLiAgCkVzIHBvciBlc3RvIHF1ZSBhbCBsbGV2YXIgYSBjYWJvIGVzdGEgdHJhbnNmb3JtYWNpw7NuIHkgbGltcGllemEsIHBvZHJlbW9zIGxsZXZhciBhIGNhYm8gdW4gYnVlbiBhbsOhbGlzaXMgeSBkZSBidWVuIHVzbyBwYXJhIG51ZXN0cm8gc29jaW8gZm9ybWFkb3I6IEZPUk0uCgoKCgojIyAqKkNvbXByZW5zacOzbiBkZSBsYXMgYmFzZXMgZGUgZGF0b3MqKiAgCgojIyMgKipGT1JNLSBSZWN1cnNvcyBIdW1hbm9zIChDb2xhYm9yYWRvcmVzIHkgQmFqYXMpLioqICAKYGBge3J9CmJkX2NvbGFiPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9jb2xhYl9GT1JNX2xpbXBpYS5jc3YiKQpiZF9iYWphczwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvYmFqYXNfRk9STV9saW1waWEuY3N2IikKYGBgCgojIyMjIMK/Q3XDoW50YXMgdmFyaWFibGVzIHkgY3VhbnRvcyByZWdpc3Ryb3MgdGllbmUgbGEgYmFzZSBkZSBkYXRvcz8KYGBge3J9CnN0cihiZF9jb2xhYikKc3RyKGJkX2JhamFzKQpgYGAKU2Ugb2J0aWVuZW4gMTA0IHJlZ2lzdHJvcyAodSBvYnNlcnZhY2lvbmVzKSBlbiAqKmNvbGFib3JhZG9yZXMqKiB5IDkgdmFyaWFibGVzIHF1ZSBzZSBjb25zaWRlcmFuIGNvbW8gcmVsZXZhbnRlcy4gICAKU2Ugb2J0aWVuZW4gMjMyIHJlZ2lzdHJvcyAodSBvYnNlcnZhY2lvbmVzKSBlbiAqKmJhamFzKiogeSA5IHZhcmlhYmxlcyBxdWUgc2UgY29uc2lkZXJhbiBjb21vIHJlbGV2YW50ZXMuCgoKIyMjIyBDbGFzaWZpY2FjacOzbiBkZSBjYWRhIHZhcmlhYmxlIGVuIGN1YWxpdGF0aXZhLCBjdWFudGl0YXRpdmEgZGlzY3JldGEsIG8gY3VhbnRpdGF0aXZhIGNvbnRpbnVhLgoKRGUgKipjb2xhYm9yYWRvcmVzKioKYGBge3IgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gVFJVRX0KVmFyaWFibGVDb2xhYjwtYygiYEVkYWRgIiwiYEfDqW5lcm9gIiwiYEHDsW8gZGUgQWx0YWAiLCJgUHVlc3RvYCIsImBTYWxhcmlvIGRpYXJpb2AiLCJgTHVnYXIgbmFjaW1pZW50b2AiLCJgTXVuaWNpcGlvYCIsImBFc3RhZG9gIiwiYEVzdGFkbyBjaXZpbGAiKQpUeXBlPC1jKCJDdWFudGl0YXRpdmEgZGlzY3JldGEiLCAiQ3VhbGl0YXRpdmEiLCAiQ3VhbnRpdGF0aXZhIGRpc2NyZXRhIiwgIkN1YWxpdGF0aXZhIiwiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIiwgIkN1YWxpdGF0aXZhIiwiQ3VhbGl0YXRpdmEiLCJDdWFsaXRhdGl2YSIsIkN1YWxpdGF0aXZhIikKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVDb2xhYixUeXBlKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKRGUgKipiYWphcyoqCmBgYHtyIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9IFRSVUV9ClZhcmlhYmxlQmFqYXM8LWMoImBFZGFkYCIsImBHw6luZXJvYCIsImBBw7FvIGRlIEFsdGFgIiwiYE1vdGl2byBCYWphYCIsImBEdXJhY2nDs25gIiwiYFB1ZXN0b2AiLCJgU2FsYXJpbyBkaWFyaW9gIiwiYEVzdGFkb2AiLCJgRXN0YWRvIGNpdmlsYCIpClR5cGU8LWMoIkN1YW50aXRhdGl2YSBkaXNjcmV0YSIsICJDdWFsaXRhdGl2YSIsICJDdWFudGl0YXRpdmEgZGlzY3JldGEiLCAiQ3VhbGl0YXRpdmEiLCJDdWFudGl0YXRpdmEgZGlzY3JldGEiLCAiQ3VhbGl0YXRpdmEiLCJDdWFudGl0YXRpdmEgY29udGludWEiLCJDdWFsaXRhdGl2YSIsIkN1YWxpdGF0aXZhIikKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVCYWphcyxUeXBlKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKIyMjIyBFc2NhbGEgZGUgbWVkaWNpw7NuIGRlIGNhZGEgdmFyaWFibGUuCgpgYGB7ciBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSBUUlVFfQpWYXJpYWJsZUNvbGFiPC1jKCJgRWRhZGAiLCJgR8OpbmVyb2AiLCJgQcOxbyBkZSBBbHRhYCIsImBQdWVzdG9gIiwiYFNhbGFyaW8gZGlhcmlvYCIsImBMdWdhciBuYWNpbWllbnRvYCIsImBNdW5pY2lwaW9gIiwiYEVzdGFkb2AiLCJgRXN0YWRvIGNpdmlsYCIpCk1lZGljaW9uPC1jKCJBw7FvcyIsIk5vIGFwbGljYSIsIkHDsW9zIiwiTm8gYXBsaWNhIiwiUGVzb3MgbWV4aWNhbm9zIiwiTm8gYXBsaWNhIiwiTm8gYXBsaWNhIiwiTm8gYXBsaWNhIiwiTm8gYXBsaWNhIikKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVDb2xhYixNZWRpY2lvbikKa25pdHI6OmthYmxlKHRhYmxlKQpgYGAKCmBgYHtyIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9IFRSVUV9ClZhcmlhYmxlQmFqYXM8LWMoImBFZGFkYCIsImBHw6luZXJvYCIsImBBw7FvIGRlIEFsdGFgIiwiYE1vdGl2byBCYWphYCIsImBEdXJhY2nDs25gIiwiYFB1ZXN0b2AiLCJgU2FsYXJpbyBkaWFyaW9gIiwiYEVzdGFkb2AiLCJgRXN0YWRvIGNpdmlsYCIpCk1lZGljaW9uPC1jKCJBw7FvcyIsIk5vIGFwbGljYSIsIkHDsW9zIiwiTm8gYXBsaWNhIiwiRMOtYXMiLCJObyBhcGxpY2EiLCJQZXNvcyBtZXhpY2Fub3MiLCJObyAgYXBsaWNhIiwiTm8gYXBsaWNhIikKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVCYWphcyxNZWRpY2lvbikKa25pdHI6OmthYmxlKHRhYmxlKQpgYGAKCiMjIyAqKkZPUk0tIERlbGl2ZXJ5IFBsYW4uKiogIApgYGB7cn0KYmRfZGVscGxhbjwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvZGVsaXZlcnlwbGFuX2ZpbmFsMi5jc3YiKQpgYGAKCiMjIyMgwr9DdcOhbnRhcyB2YXJpYWJsZXMgeSBjdWFudG9zIHJlZ2lzdHJvcyB0aWVuZSBsYSBiYXNlIGRlIGRhdG9zPwpgYGB7cn0Kc3RyKGJkX2RlbHBsYW4pCmBgYAoKRXN0YSBiYXNlIGRlIGRhdG9zIGN1ZW50YSBjb24gMjI4IG9ic2VydmFjaW9uZXMgeSA0IHZhcmlhYmxlcyByZWxldmFudGVzLiAKCiMjIyMgQ2xhc2lmaWNhY2nDs24gZGUgY2FkYSB2YXJpYWJsZSBlbiBjdWFsaXRhdGl2YSwgY3VhbnRpdGF0aXZhIGRpc2NyZXRhLCBvIGN1YW50aXRhdGl2YSBjb250aW51YS4KCmBgYHtyIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9IFRSVUV9ClZhcmlhYmxlUGxhbjwtYygiYElEX2ZlY2hhYCIsImBGZWNoYWAiLCJgQ2xpZW50ZWAiLCJgUGVkaWRvc2AiKQpUeXBlPC1jKCJDdWFudGl0YXRpdmEgZGlzY3JldGEiLCAiQ3VhbnRpdGF0aXZhIGRpc2NyZXRhIiwgIkN1YWxpdGF0aXZhIiwgIkN1YW50aXRhdGl2YSBkaXNjcmV0YSIpCnRhYmxlPC1kYXRhLmZyYW1lKFZhcmlhYmxlUGxhbixUeXBlKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKIyMjIyBFc2NhbGEgZGUgbWVkaWNpw7NuIGRlIGNhZGEgdmFyaWFibGUuCgpgYGB7ciBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSBUUlVFfQpWYXJpYWJsZVBsYW48LWMoImBJRF9mZWNoYWAiLCJgRmVjaGFgIiwiYENsaWVudGVgIiwiYFBlZGlkb3NgIikKTWVkaWNpb248LWMoIk7Dum1lcm8gZGUgbWVzIiwiRMOtYS9NZXMvQcOxbyIsIk5vIGFwbGljYSIsIk7Dum1lcm8gbyBjYW50aWRhZCBkZSBwZWRpZG9zIikKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVQbGFuLE1lZGljaW9uKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKIyMjICoqRk9STS0gRGVsaXZlcnkgUGVyZm9ybWFuY2UuKiogIApgYGB7cn0KYmRfZGVscGVyZjwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvZGVsaXZlcnlwZXJmb3JtYW5jZV9maW5hbHlhLmNzdiIpCmBgYAoKIyMjIyDCv0N1w6FudGFzIHZhcmlhYmxlcyB5IGN1YW50b3MgcmVnaXN0cm9zIHRpZW5lIGxhIGJhc2UgZGUgZGF0b3M/CmBgYHtyfQpzdHIoYmRfZGVscGVyZikKYGBgCgpFc3RhIGJhc2UgZGUgZGF0b3MgY3VlbnRhIGNvbiAxMDQgb2JzZXJ2YWNpb25lcyB5IDMgdmFyaWFibGVzIHJlbGV2YW50ZXMuIAoKCgojIyMjIENsYXNpZmljYWNpw7NuIGRlIGNhZGEgdmFyaWFibGUgZW4gY3VhbGl0YXRpdmEsIGN1YW50aXRhdGl2YSBkaXNjcmV0YSwgbyBjdWFudGl0YXRpdmEgY29udGludWEuCgpgYGB7ciBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSBUUlVFfQpWYXJpYWJsZVBlcmY8LWMoImBDbGllbnRlYCIsImBGZWNoYWAiLCJgRGlmZXJlbmNpYWAiKQpUeXBlPC1jKCJDdWFsaXRhdGl2YSIsICJDdWFudGl0YXRpdmEgZGlzY3JldGEiLCAiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIikKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVQZXJmLFR5cGUpCmtuaXRyOjprYWJsZSh0YWJsZSkKYGBgCgojIyMjIEVzY2FsYSBkZSBtZWRpY2nDs24gZGUgY2FkYSB2YXJpYWJsZS4KCmBgYHtyIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9IFRSVUV9ClZhcmlhYmxlUGVyZjwtYygiYENsaWVudGVgIiwiYEZlY2hhYCIsImBEaWZlcmVuY2lhYCIpCk1lZGljaW9uPC1jKCJObyBhcGxpY2EiLCJNZXMtQcOxbyIsIk1pbnV0b3MiKQp0YWJsZTwtZGF0YS5mcmFtZShWYXJpYWJsZVBlcmYsTWVkaWNpb24pCmtuaXRyOjprYWJsZSh0YWJsZSkKYGBgCgojIyMgKipGT1JNLSBQcm9kdWNjacOzbioqICAKYGBge3J9CmJkX3Byb2Q8LXJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL3Byb2R1Y2Npb25fZmluYWwuY3N2IikKYGBgCgojIyMjIMK/Q3XDoW50YXMgdmFyaWFibGVzIHkgY3VhbnRvcyByZWdpc3Ryb3MgdGllbmUgbGEgYmFzZSBkZSBkYXRvcz8KYGBge3J9CnN0cihiZF9wcm9kKQpgYGAKCkVzdGEgYmFzZSBkZSBkYXRvcyBjdWVudGEgY29uIDEsNjE2IG9ic2VydmFjaW9uZXMgeSA1IHZhcmlhYmxlcyByZWxldmFudGVzLiAKCgoKIyMjIyBDbGFzaWZpY2FjacOzbiBkZSBjYWRhIHZhcmlhYmxlIGVuIGN1YWxpdGF0aXZhLCBjdWFudGl0YXRpdmEgZGlzY3JldGEsIG8gY3VhbnRpdGF0aXZhIGNvbnRpbnVhLgoKYGBge3IgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gVFJVRX0KVmFyaWFibGVQcm9kPC1jKCJgQ2xpZW50ZWAiLCJgUGllemFzIFByb2dyYW1hZGFzYCIsImBUaWVtcG8gbcOtbmltb2AiLCJgTMOhbWluYXMgcHJvY2VzYWRhc2AiLCJgVGllbXBvIGRlIGNhbGlkYWRgIikKVHlwZTwtYygiQ3VhbGl0YXRpdmEiLCAiQ3VhbnRpdGF0aXZhIGRpc2NyZXRhIiwgIkN1YW50aXRhdGl2YSBjb250aW51YSIsICJDdWFudGl0YXRpdmEgZGlzY3JldGEiLCAiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIikKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVQcm9kLFR5cGUpCmtuaXRyOjprYWJsZSh0YWJsZSkKYGBgCgojIyMjIEVzY2FsYSBkZSBtZWRpY2nDs24gZGUgY2FkYSB2YXJpYWJsZS4KCmBgYHtyIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9IFRSVUV9ClZhcmlhYmxlUHJvZDwtYygiYENsaWVudGVgIiwiYFBpZXphcyBQcm9ncmFtYWRhc2AiLCJgVGllbXBvIG3DrW5pbW9gIiwiYEzDoW1pbmFzIHByb2Nlc2FkYXNgIiwiYFRpZW1wbyBkZSBjYWxpZGFkYCIpCk1lZGljaW9uPC1jKCJObyBhcGxpY2EiLCJOw7ptZXJvIG8gY2FudGlkYWQgZGUgcGllemFzIHByb2dyYW1hZGFzIiwiTWludXRvcyIsIk7Dum1lcm8gbyBjYW50aWRhZCBkZSBsw6FtaW5hcyBwcm9jZXNhZGFzIiwiTW5pdXRvcyIpCnRhYmxlPC1kYXRhLmZyYW1lKFZhcmlhYmxlUHJvZCxNZWRpY2lvbikKa25pdHI6OmthYmxlKHRhYmxlKQpgYGAKCiMjIyAqKkZPUk0tIE1lcm1hKiogIApgYGB7cn0KYmRfbWVybWFfZjwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvbWVybWFhX2ZpbmFsMi5jc3YiKQpgYGAKCgojIyMjIMK/Q3XDoW50YXMgdmFyaWFibGVzIHkgY3VhbnRvcyByZWdpc3Ryb3MgdGllbmUgbGEgYmFzZSBkZSBkYXRvcz8KYGBge3J9CnN0cihiZF9tZXJtYV9mKQpgYGAKCkVzdGEgYmFzZSBkZSBkYXRvcyBjdWVudGEgY29uIDUwIG9ic2VydmFjaW9uZXMgeSAzIHZhcmlhYmxlcyByZWxldmFudGVzLiAKCgoKIyMjIyBDbGFzaWZpY2FjacOzbiBkZSBjYWRhIHZhcmlhYmxlIGVuIGN1YWxpdGF0aXZhLCBjdWFudGl0YXRpdmEgZGlzY3JldGEsIG8gY3VhbnRpdGF0aXZhIGNvbnRpbnVhLgoKYGBge3IgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gVFJVRX0KVmFyaWFibGVNZXJtYTwtYygiYEZlY2hhYCIsImBNZXNgIiwiYEtpbG9zYCIpClR5cGU8LWMoIkN1YW50aXRhdGl2YSBjb250aW51YSIsICJDdWFsaXRhdGl2YSIsICJDdWFudGl0YXRpdmEgY29udGludWEiKQp0YWJsZTwtZGF0YS5mcmFtZShWYXJpYWJsZU1lcm1hLFR5cGUpCmtuaXRyOjprYWJsZSh0YWJsZSkKYGBgCgojIyMjIEVzY2FsYSBkZSBtZWRpY2nDs24gZGUgY2FkYSB2YXJpYWJsZS4KCmBgYHtyIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9IFRSVUV9ClZhcmlhYmxlTWVybWE8LWMoImBGZWNoYWAiLCJgTWVzYCIsImBLaWxvc2AiKQpNZWRpY2lvbjwtYygiRMOtYS9NZXMvQcOxbyIsIk5vIGFwbGljYSIsIktpbG9zL0tncyIpCnRhYmxlPC1kYXRhLmZyYW1lKFZhcmlhYmxlTWVybWEsTWVkaWNpb24pCmtuaXRyOjprYWJsZSh0YWJsZSkKYGBgCgojIyMgKipGT1JNLSBTY3JhcCoqICAKYGBge3J9CmJkX3NjcmFwcDwtc2NyYXBfZmluYWx5YQpgYGAKCiMjIyMgwr9DdcOhbnRhcyB2YXJpYWJsZXMgeSBjdWFudG9zIHJlZ2lzdHJvcyB0aWVuZSBsYSBiYXNlIGRlIGRhdG9zPwpgYGB7cn0Kc3RyKGJkX3NjcmFwcCkKc3VtbWFyeShzY3JhcDMpCmBgYAoKRXN0YSBiYXNlIGRlIGRhdG9zIGN1ZW50YSBjb24gMjUwIG9ic2VydmFjaW9uZXMgeSAzIHZhcmlhYmxlcyByZWxldmFudGVzLiAKCgoKIyMjIyBDbGFzaWZpY2FjacOzbiBkZSBjYWRhIHZhcmlhYmxlIGVuIGN1YWxpdGF0aXZhLCBjdWFudGl0YXRpdmEgZGlzY3JldGEsIG8gY3VhbnRpdGF0aXZhIGNvbnRpbnVhLgoKYGBge3IgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gVFJVRX0KVmFyaWFibGVTY3JhcDwtYygiYEZlY2hhYCIsImBDYW50aWRhZGAiLCJgVWJpY2FjacOzbiBkZSBvcmlnZW5gIikKVHlwZTwtYygiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIiwgIkN1YW50aXRhdGl2YSBkaXNjcmV0YSIsICJDdWFsaXRhdGl2YSIpCnRhYmxlPC1kYXRhLmZyYW1lKFZhcmlhYmxlU2NyYXAsVHlwZSkKa25pdHI6OmthYmxlKHRhYmxlKQpgYGAKCiMjIyMgRXNjYWxhIGRlIG1lZGljacOzbiBkZSBjYWRhIHZhcmlhYmxlLgoKYGBge3IgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gVFJVRX0KVmFyaWFibGVTY3JhcDwtYygiYEZlY2hhYCIsImBDYW50aWRhZGAiLCJgVWJpY2FjacOzbiBkZSBvcmlnZW5gIikKTWVkaWNpb248LWMoIkTDrWEvTWVzL0HDsW8iLCJOw7ptZXJvIG8gY2FudGlkYWQgZGUgbWVybWEiLCJObyBhcGxpY2EiKQp0YWJsZTwtZGF0YS5mcmFtZShWYXJpYWJsZVNjcmFwLE1lZGljaW9uKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKIyMgKipBbsOhbGlzaXMgZXN0YWTDrXN0aWNvIGRlc2NyaXB0aXZvKiogIAoKTGlicmVyw61hcyByZXF1ZXJpZGFzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZm9yZWlnbikKbGlicmFyeShkcGx5cikgICAgICAgICMgZGF0YSBtYW5pcHVsYXRpb24gCmxpYnJhcnkoZm9yY2F0cykgICAgICAjIHRvIHdvcmsgd2l0aCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKbGlicmFyeShnZ3Bsb3QyKSAgICAgICMgZGF0YSB2aXN1YWxpemF0aW9uIApsaWJyYXJ5KGphbml0b3IpICAgICAgIyBkYXRhIGV4cGxvcmF0aW9uIGFuZCBjbGVhbmluZyAKI2luc3RhbGwucGFja2FnZXMoInBzeWNoIikKbGlicmFyeShjb3JycGxvdCkgICAgICMgY29ycmVsYXRpb24gcGxvdHMKbGlicmFyeShsbXRlc3QpICAgICAgICMgZGlhZ25vc3RpYyBjaGVja3MgLSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcyAKbGlicmFyeShjYXIpICAgICAgICAgICMgZGlhZ25vc3RpYyBjaGVja3MgLSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcwpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGphbml0b3IpCmBgYAoKIyMjICoqRk9STS0gUmVjdXJzb3MgSHVtYW5vcyAoQ29sYWJvcmFkb3JlcyB5IEJhamFzKS4qKiAgCgojIyMjIENPTEFCT1JBRE9SRVMKClNlIGdlbmVyYW4gbGFzICoqdGFibGFzIGRlIGZyZWN1ZW5jaWEqKiBkZSBsYXMgdmFyaWFibGVzICoqQ1VBTElUQVRJVkFTKiouCgpgYGB7cn0KZ2VuZXJvPC10YWJsZShiZF9jb2xhYiRnZW5lcm8pCmtuaXRyOjprYWJsZShnZW5lcm8pCmBgYAoKT2JzZXJ2YW1vcyBxdWUgZW4gbGEgYmFzZSBkZSBkYXRvcyAqbGltcGlhKiBkZSBsb3MgY29sYWJvcmFkb3JlcyBhY3R1YWxlcywgZXhpc3RlbiBtw6FzIG11amVyZXMgcXVlIGhvbWJyZXMuIAoKYGBge3J9CnB1ZXN0bzwtdGFibGUoYmRfY29sYWIkcHVlc3RvKQprbml0cjo6a2FibGUocHVlc3RvKQpgYGAKCkxvIHF1ZSBtw6FzIG9ic2VydmFtb3MgZW4gbGEgYmFzZSBkZSBjb2xhYm9yYWRvcmVzIGFjdHVhbGVzLCBlcyBlbCBwdWVzdG8gZGUgIkF5dWRhbnRlIEdlbmVyYWwiLCBzZWd1aWRvIHBvciAiQ29zdHVyZXJhcyIuCgpgYGB7cn0KbXVuaWNpcGlvPC10YWJsZShiZF9jb2xhYiRtcGlvKQprbml0cjo6a2FibGUobXVuaWNpcGlvKQpgYGAKCk9ic2VydmFtb3MgcXVlIGxhIGdyYW4gcGFydGUgZGUgbG9zIGNvbGFib3JhZG9yZXMgaGFiaXRhbiBlbiBBcG9kYWNhLCBOdWV2byBMZcOzbi4KCmBgYHtyfQplc3RhZG88LXRhYmxlKGJkX2NvbGFiJGVzdGFkbykKa25pdHI6OmthYmxlKGVzdGFkbykKYGBgCgpBc8OtIGNvbW8gbGEgZ3JhbiBwYXJ0ZSB2aWVuZW4gZGUgTnVldm8gTGXDs24sIHkgbXV5IHBvY28gc29uIGZvcsOhbmVvcy4gCgpgYGB7cn0KY2l2aWw8LXRhYmxlKGJkX2NvbGFiJGNpdmlsKQprbml0cjo6a2FibGUoY2l2aWwpCmBgYAoKQWN0dWFsbWVudGUsIGV4aXN0ZW4gbcOhcyBwZXJzb25hcyBzb2x0ZXJhcywgc2lndWllbmRvIGRlIGNhc2FkYXMsIHBvciBwZXJzb25hcyBlbmUgdW5pw7NuIGxpYnJlLCAgeSBtdXkgcG9jb3MgZGl2b3JjaWFkb3MuIAoKICAgIAogICAgICAKCgoqKlRhYmxhcyBjcnV6YWRhcyoqICAgICAKCkfDqW5lcm8gY29uIGVzdGFkby4KYGBge3J9CmNydXphZGEyPC10YWJsZShiZF9jb2xhYiRlc3RhZG8sYmRfY29sYWIkZ2VuZXJvKQprbml0cjo6a2FibGUoY3J1emFkYTIpCmBgYAoKQ29uIGVzdGEgdGFibGEgY3J1emFkYSBvYnNlcnZhbW9zIHF1ZSBsYSBncmFuIG1heW9yw61hIGRlIGxvcyBmb3LDoW5lb3Mgc29uIGhvbWJyZXMuICAgCgogIAoqKkdyw6FmaWNvcyBkZSBkYXRvcyBjdWFsaXRhdGl2b3MgeSBjdWFudGl0YXRpdm9zKiogIAoKRGF0b3MgY3VhbGl0YXRpdm9zCgpgYGB7cn0KYmFycGxvdChwcm9wLnRhYmxlKHRhYmxlKGJkX2NvbGFiJGNpdmlsKSksY29sPWMoImxpZ2h0eWVsbG93IiwibGlnaHRibHVlIiwicGluayIsImxpZ2h0Z3JlZW4iKSxtYWluPSJFc3RhZG8gQ2l2aWwgZGUgQ29sYWJvcmFkb3JlcyBGT1JNIiwgeWxhYiA9IkZyZWN1ZW5jaWFzIixsYXM9MSkKYGBgCgpDb21vIGZ1ZSBkZXNjcml0byBhbnRlcmlvcm1lbnRlLCBhcXXDrSBwb2RlbW9zIHZlciBkZSBtYW5lcmEgZ3LDoWZpY2EgcXVlIGNhc2kgbGEgbWl0YWQgc29uIG8gc29sdGVyb3MgbyBjYXNhZG9zLiAKCmBgYHtyfQpwb3JjZW50YWplcyA8LSBhcy5udW1lcmljKHJvdW5kKCgocHJvcC50YWJsZSh0YWJsZShiZF9jb2xhYiRnZW5lcm8pKSkqMTAwKSwyKSkKZXRpcXVldGFzIDwtIGMoIk11amVyZXMiLCAiSG9tYnJlcyIpCmV0aXF1ZXRhcyA8LSBwYXN0ZShldGlxdWV0YXMsIHBvcmNlbnRhamVzKQpldGlxdWV0YXMgPC0gcGFzdGUoZXRpcXVldGFzLCAiJSIsIHNlcCA9ICIiKQoKCnBpZShwb3JjZW50YWplcyxldGlxdWV0YXMsY29sPWMoInBpbmsiLCJsaWdodGJsdWUiKSxtYWluPSJHw6luZXJvIiwgeWxhYiA9IkZyZWN1ZW5jaWFzIixsYXM9MSkKYGBgCgpFbiBlc3RhIGdyw6FmaWNhIGRlIHBhc3RlbCwgcG9kZW1vcyBub3RhciBxdWUgbcOhcyBkZWwgNTAlIGRlIGxvcyBxdWUgdHJhYmFqYW4gZW4gRk9STSBhY3R1YWxtZW50ZSBzb24gbXVqZXJlcy4gIAogIAogIAogIAoqKkRhdG9zIGN1YW50aXRhdGl2b3MqKgoKYGBge3J9Cmhpc3QoKGJkX2NvbGFiJHNhbGFyaW9fZGlhcmlvKSxjb2w9YygiZGFya3JlZCIpLG1haW49IlNhbGFyaW8gRGlhcmlvIGRlIENvbGFib3JhZG9yZXMgZGUgRk9STSIseGxhYj0iU2FsYXJpbyBlbiBwZXNvcyBteCIpCmBgYAoKRW4gY3VhbnRvIGFsIHNhbGFyaW8gZGlhcmlvIHF1ZSByZWNpYmVuIGxvcyBjb2xhYm9yYWRvcmVzIGRlIEZPUk0sIGxhIGdyYW4gbWF5b3LDrWEgbm8gc29icmVwYXNhIGxvcyAyMDAgcGVzb3MsIHkgbGEgZ3JhbiBwYXJ0ZSBkZSBhcXVlbGxvcyBxdWUgc2kgbm8gIHNvYnJlcGFzYW4gbG9zIDMwMDsgZXhpc3RlIHVuIGNhc28gcXVlIGVzdMOhIG11eSBwb3IgZW5jaW1hIGRlIHRvZG9zLCBlbnRyZSBsb3MgNDAwIHkgNTAwIGRpYXJpb3MuIAoKCmBgYHtyfQpoaXN0KChiZF9jb2xhYiRhbHRhKSxjb2w9YygibGlnaHRibHVlIiksbWFpbj0iRmVjaGEgZGUgQWx0YSBkZSBDb2xhYm9yYWRvcmVzIGRlIEZPUk0iLHhsYWI9IkHDsW8iKQpgYGAKClNpZ3VpZW5kbyBjb24gZWwgYcOxbyBkZSBhbHRhIGRlIGxvcyBhY3R1YWxlcyBjb2xhYm9yYWRvcmVzLCBvYnNlcnZhbW9zIHF1ZSBleGlzdGUgbXV5IHBvY2EgYW50aWfDvGVkYWQsIGFsIGhhYmVyIGluZ3Jlc2FkbyBsYSBncmFuIG1heW9yw61hIGR1cmFudGUgbG9zIMO6bHRpbW9zIGRvcyBhw7FvcyAoMjAyMC0yMDIyKS4gSWd1YWxtZW50ZSwgbGEgcGVyc29uYSBxdWUgbcOhcyB0aWVtcG8gbGxldmEgZW4gRk9STSwgdGllbmUgdW5hIGFudGlnw7xlZGFkIGRlIDEyIGHDsW9zLCBlc3RvIHRvbWFuZG8gZW4gY3VlbnRhIGxhIGxpbXBpZXphIGRlIGRhdG9zLiAgCiAgCiAgCgoqKkdyw6FmaWNvcyBkZSBkaXNwZXJzacOzbioqICAKCgpgYGB7cn0KcGxvdChiZF9jb2xhYiRhbHRhLCBiZF9jb2xhYiRzYWxhcmlvX2RpYXJpbywgbWFpbiA9ICJGZWNoYSBkZSBpbmdyZXNvIGNvbiBzYWxhcmlvIGRpYXJpbyIsCiAgICAgeGxhYiA9ICJGZWNoYSBkZSBpbmdyZXNvIiwgeWxhYiA9ICJTYWxhcmlvIixjb2w9KCJkYXJrZ3JlZW4iKSwKICAgICBwY2ggPSAxOSwgZnJhbWUgPSBGQUxTRSkKYGBgCgpBcXXDrSB0YW1iacOpbiBvYnNlcnZhbW9zIGxvIGRlc2NyaXRvIGFudGVyaW9ybWVudGUsIGxhIG1heW9yw61hIG5vIGdhbmEgbcOhcyBkZSAkMjAwLiBTaW4gZW1iYXJnbywgZW4gZXN0ZSBjYXNvLCB2ZW1vcyBxdWUgbm8gaGF5IHBlcnNvbmEgcXVlIGhheWEgaW5ncmVzYWRvIGRlc2RlIGVsIDIwMTkgcXVlIGdhbmUgbcOhcyBkZSAkMjUwLiBJZ3VhbG1lbnRlLCBub3RhbW9zIHF1ZSBsYSBwZXJzb25hIHF1ZSBtw6FzIGdhbmEgbm8gZXMgYXF1ZWxsYSBjb24gbWF5b3IgYW50aWfDvGVkYWQsIHNpbm8gYWxndWllbiBxdWUgZW50csOzIGVuIGVsIDIwMTMuIExhcyBwZXJzb25hcyBxdWUgbWF5b3IgYW50aWfDvGVkYWQgZ2FuYW4gbG8gbWlzbW8gcXVlIG11Y2hhcyBkZSByZWNpw6luIGluZ3Jlc28uIAoKICAKICAgIApgYGB7cn0KcGxvdChiZF9jb2xhYiRlZGFkLCBiZF9jb2xhYiRzYWxhcmlvX2RpYXJpbywgbWFpbiA9ICJFZGFkIiwKICAgICB4bGFiID0gIkVkYWQiLCB5bGFiID0gIlNhbGFyaW8iLGNvbD0oImRhcmtncmVlbiIpLAogICAgIHBjaCA9IDE5LCBmcmFtZSA9IEZBTFNFKQpgYGAKCkVuIGVzdGEgZ3LDoWZpY2Egbm90YW1vcyBxdWUgbm8gZXhpc3RlIGRpc2NyaW1pbmFjacOzbiBkZSBzYWxhcmlvIHBvciBsYSBlZGFkLCBwdWVzdG8gcXVlIGxhIG1heW9yw61hIGRlIGxhcyBwZXJzb25hcywgZGVzZGUgbG9zIDE4IGhhc3RhIG3DoXMgZGUgbG9zIDYwLCBnYW5hbiBtZW5vcyBkZSAyMDAgcGVzb3MgZGlhcmlvcy4gTGEgcGVyc29uYSBxdWUgbcOhcyBnYW5hIHBhcmVjZSB0ZW5lciBlbnRyZSAzMCB5IDM1IGHDsW9zLiAKCiAgIAogICAgIApgYGB7cn0KYm94cGxvdChiZF9jb2xhYiRlZGFkICwgdmVydGljYWwgPSBUUlVFLGNvbD0oImRhcmtibHVlIikpCmBgYAoKTWVkaWFudGUgZWwgYm94cGxvdCBjb25vY2Vtb3MgcXVlIGxhIG1lZGlhIGVzIGFwcsOzeGltYWRhbWVudGUgMzIgYcOxb3MgZGUgZWRhZCwgeSBxdWUgZXhpc3RlIHVuYSBub3RvcmlhIGRpc3BlcnNpw7NuLiBJZ3VhbCB2ZW1vcyBxdWUgZWwgcmFuZ28gZGUgZWRhZCB2YSBkZXNkZSBsb3MgMTggaGFzdGEgbcOhcyBkZSBsb3MgNjAuICAgCiAgCiAgICAKICAgCioqQkFKQVMqKgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI2luc3RhbGwucGFja2FnZXMoImVwaURpc3BsYXkiKQpsaWJyYXJ5KGVwaURpc3BsYXkpCmJkX2JhamFzPC1iZF9iYWphcwpgYGAKCioqVGFibGEgZGUgZnJlY3VlbmNpYSB5IGdyw6FmaWNhIGRlIGRhdG9zIENVQUxJVEFUSVZPUzogKlB1ZXN0byoqKiAgCgpgYGB7cn0KcHVlc3RvPC10YWJsZShiZF9iYWphcyRwdWVzdG8pCmtuaXRyOjprYWJsZShwdWVzdG8pCmBgYAoKRW4gZXN0YSBvYnNlcnZhbW9zIHF1ZSBlbCBwdWVzdG8gcXVlIG3DoXMgaGFuIGRlamFkbyBlbiBGT1JNIGVzIGVsIHB1ZXN0byBkZSBheXVkYW50ZSBnZW5lcmFsLCBwb3IgbXVjaG8uIEVzdG8gcHVlZGUgc2VyIGRlYmlkbyBhIHF1ZSBlcyB1biBwdWVzdG8gbXV5IGNvbcO6biB5IHV0aWxpemFkbyAoYcO6biBlbiBsb3MgY29sYWJvcmFkb3JlcyBhY3R1YWxlcyBlcyBlbCBxdWUgbcOhcyBwZXJzb25hcyB0aWVuZSkuICAKICAKCiAgCioqVGFibGEgZGUgZnJlY3VlbmNpYSB5IGdyw6FmaWNhIGRlIGRhdG9zIENVQUxJVEFUSVZPUzogKk1vdGl2byBkZSBiYWphcyoqKiAgIAoKYGBge3J9Cm1vdGl2bzwtdGFibGUoYmRfYmFqYXMkbW90aXZvX2JhamEpCmtuaXRyOjprYWJsZShtb3Rpdm8pCmBgYAoKRW4gZWwgcHJlc2VudGUgYW7DoWxpc2lzIG5vdGFtb3MgY3VhbGVzIHNvbiBsYXMgcHJpbmNpcGFsZXMgcmF6b25lcywgeSBsYXMgcXVlIG1lbm9zLCBwb3IgbGFzIHF1ZSBsb3MgZW1wbGVhZG9zIGRlIEZvcm0gc2FsZW4uIFByaW5jaXBhbG1lbnRlIHZlbW9zIHF1ZSBlcyBwb3IgbGEgQkFKQSBQT1IgRkFMVEFTLCBzaWd1aWVuZG8gcG9yIFJFTlVOQ0lBIFZPTFVOVEFSSUEuIExhcyBxdWUgbWVub3MgdmVtb3Mgc29uIHBvciBKVUJJTEFDScOTTiB5IEFCQU5ET05PLiAgCiAgIAogICAgCgoKYGBge3J9CnBvcmNlbnRhamVzIDwtIGFzLm51bWVyaWMocm91bmQoKChwcm9wLnRhYmxlKHRhYmxlKGJkX2JhamFzJG1vdGl2b19iYWphKSkpKjEwMCksMikpCmV0aXF1ZXRhcyA8LSBjKCJBYmFuZG9ubyIsICJCYWphIHBvciBmYWx0YXMiLCJKdWJpbGFjacOzbiIsIlJlbnVuY2lhIHZvbHVudGFyaWEiLCJUZXJtaW5vIGRlIGNvbnRyYXRvIikKZXRpcXVldGFzIDwtIHBhc3RlKGV0aXF1ZXRhcywgcG9yY2VudGFqZXMpCmV0aXF1ZXRhcyA8LSBwYXN0ZShldGlxdWV0YXMsICIlIiwgc2VwID0gIiIpCgpwaWUocG9yY2VudGFqZXMsZXRpcXVldGFzLGNvbD1jKCJvcmFuZ2UiLCJsaWdodGJsdWUiLCJncmVlbiIsInBpbmsiLCJ5ZWxsb3ciKSxtYWluPSJNb3Rpdm8gZGUgYmFqYXMiLCB5bGFiID0iRnJlY3VlbmNpYXMiLGxhcz0xKQpgYGAKCkxvIG1pc21vIHF1ZSBoYSBzaWRvIGRlc2NyaXRvIGVuIGxhIHRhYmxhIHB1ZWRlIHNlciBjb21wcmVuZGlkbyBhIHBhcnRpciBkZSBsYSBncsOhZmljYSBzdXBlcmlvcjsgc2luIGVtYmFyZ28sIGVzdGEgbm9zIGRlbXVlc3RhIGEgdHJhdsOpcyBkZSBwb3JjZW50YWplcyBsYSBwcm9wb3JjacOzbiBkZSBjYWRhIHJhesOzbi4gICAKICAKCmBgYHtyfQpjaXZpbDwtdGFibGUoYmRfYmFqYXMkY2l2aWwpCmtuaXRyOjprYWJsZShjaXZpbCkKCnRhYjEoYmRfYmFqYXMkY2l2aWwsc29ydC5ncm91cCA9IEZBTFNFLGdyYXBoPVRSVUUsY29sPWMoIm9yYW5nZSIsImxpZ2h0Ymx1ZSIsImxpZ2h0Z3JlZW4iLCJsaWdodHllbGxvdyIpLG1haW49IkRpc3RyaWJ1Y2nDs24gZGUgcGFzYWRvcyBjb2xhYm9yYWRvcmVzIGRlIEZPUk0sIHBvciBlc3RhZG8gY2l2aWwiKQpgYGAKCkNvbXByZW5kZW1vcyBxdWUgbGEgbWF5b3IgY2FudGlkYWQgZGUgbGFzIHBlcnNvbmFzIHF1ZSBzYWxlbiBzb24gc29sdGVyYXMsIHkgbGFzIHBlcnNvbmFzIHF1ZSBlc3TDoW4gZW4gbWF0cmltb25pbyBvIGVuIHVuacOzbiBsaWJyZSB0aWVuZW4gY2FzaSBsYSBtaXNtYSB0ZW5kZW5jaWEgYSBzYWxpci4gICAKICAgCgoKYGBge3J9CmVzdGFkbzwtdGFibGUoYmRfYmFqYXMkZXN0YWRvKQprbml0cjo6a2FibGUoZXN0YWRvKQoKdGFiMShiZF9iYWphcyRlc3RhZG8sc29ydC5ncm91cCA9IEZBTFNFLGdyYXBoPVRSVUUsY29sPWMoIm9yYW5nZSIsImxpZ2h0Ymx1ZSIpLG1haW49IkRpc3RyaWJ1Y2nDs24gZGUgcGFzYWRvcyBjb2xhYm9yYWRvcmVzIGRlIEZPUk0sIHBvciBlc3RhZG8iKQpgYGAKCkxhIGdyYW4gcGFydGUgZGUgbG9zIHF1ZSBzYWxlbiBzb24gZGUgTnVldm8gTGXDs24uICAKICAKCgpgYGB7cn0KaGlzdCgoYmRfYmFqYXMkZWRhZCksY29sPWMoIm9yYW5nZSIpLG1haW49IkRpc3RyaWJ1Y2nDs24gZGUgcGFzYWRvcyBjb2xhYm9yYWRvcmVzIGRlIEZPUk0sIHBvciBlZGFkIix4bGFiPSJBw7FvcyIseWxhYj0iRnJlY3VlbmNpYSIpCmBgYAoKRGUgbGFzIHBlcnNvbmFzIHF1ZSBzYWxlbiBkZSBsYSBlbXByZXNhLCBsYSBlZGFkIGxsZXZhIHVuICpzZXNnbyBwb3NpdGl2byosIGxhIG1heW9yw61hIGRlIGxvcyBxdWUgc2FsZW4gc2UgY29uY2VudHJhbiBlbiBsYXMgZWRhZGVzIG3DoXMgasOzdmVuZXMsIGFwcm94aW1hZGFtZW50ZSBlbnRyZSBsb3MgMjAgeSBsb3MgMzAuICAgCiAgCgoKYGBge3J9Cmhpc3QoKGJkX2JhamFzJGR1cmFjaW9uKSxjb2w9YygibGlnaHR5ZWxsb3ciKSxtYWluPSJEaXN0cmlidWNpw7NuIGRlIHBhc2Fkb3MgY29sYWJvcmFkb3JlcyBkZSBGT1JNLCBwb3IgZMOtYXMgZGUgZHVyYWNpw7NuIix4bGFiPSJEw61hcyIseWxhYj0iRnJlY3VlbmNpYSIpCmBgYAoKRW4gY3VhbnRvIGEgbG9zIGTDrWFzIHF1ZSBkdXJhbiBsYXMgcGVyc29uYXMgcXVlIGJhamFuLCBvYnNlcnZlbWFvcyBxdWUgbXVjaG9zIG5vIGR1cmFuIG5pIHNpZ3VpZXJhIGVsIGHDsW8sIGFsIGVzdGFyIGxhIGdyYW4gbWF5b3LDrWEgZW4gbWVub3MgZGUgbG9zIDI1MCBkw61hcyBkZW50cm8gZGUgRm9ybS4gTGEgYW50aWfDvGVkYWQgZXMgcG9jYS4gICAgCiAgCgoKCmBgYHtyfQpoaXN0KChiZF9iYWphcyRzYWxhcmlvX2RpYXJpbyksY29sPWMoImRhcmtibHVlIiksbWFpbj0iRGlzdHJpYnVjacOzbiBkZSBwYXNhZG9zIGNvbGFib3JhZG9yZXMgZGUgRk9STSwgcG9yIHN1IHNhbGFyaW8gZGlhcmlvIix4bGFiPSJQZXNvcyIseWxhYj0iRnJlY3VlbmNpYSIpCmBgYAoKRWwgc2FsYXJpbyBubyB2ZSBtdWNoYXMgZGlmZXJlbmNpYXMsIGFsIGVzdGFyIGxhIGdyYW4gcGFydGUgZW50cmUgbG9zIDE1MCB5IGxvcyAyMDAgcGVzb3MgZGlhcmlvcy4gICAgIAogIAoKCmBgYHtyfQpwb3JjZW50YWplcyA8LSBhcy5udW1lcmljKHJvdW5kKCgocHJvcC50YWJsZSh0YWJsZShiZF9iYWphcyRnZW5lcm8pKSkqMTAwKSwyKSkKZXRpcXVldGFzIDwtIGMoIk11amVyZXMiLCAiSG9tYnJlcyIpCmV0aXF1ZXRhcyA8LSBwYXN0ZShldGlxdWV0YXMsIHBvcmNlbnRhamVzKQpldGlxdWV0YXMgPC0gcGFzdGUoZXRpcXVldGFzLCAiJSIsIHNlcCA9ICIiKQoKCnBpZShwb3JjZW50YWplcyxldGlxdWV0YXMsY29sPWMoInBpbmsiLCJsaWdodGJsdWUiKSxtYWluPSJHw6luZXJvIGRlIHBhc2Fkb3MgY29sYWJvcmFkb3JlcyBkZSBGT1JNIiwgeWxhYiA9IkZyZWN1ZW5jaWFzIixsYXM9MSkKYGBgCgpPYnNlcnZhbW9zIHF1ZSBleGlzdGUgdW5hIG1heW9yIHRlbmRlbmNpYSBlbiBxdWUgbGFzIHBlcnNvbmFzIHF1ZSBzYWxnYW4gc2VhbiBtdWplcmVzLiAgIAogICAKCmBgYHtyfQpwbG90KGJkX2JhamFzJGVkYWQsYmRfYmFqYXMkZHVyYWNpb24sIG1haW4gPSAiRMOtYXMgZGUgZHVyYWNpw7NuIHkgZWRhZCwgZGUgYW50aWd1b3MgY29sYWJvcmFkb3JlcyIsIHhsYWI9IkVkYWQiLCB5bGFiPSJEdXJhY2nDs24iLAogICAgIHBjaCA9IDE5LCBmcmFtZSA9IEZBTFNFLGNvbD1jKCJvcmFuZ2UiKSkKYGBgCgpFbiBjdWFudG8gYSBsYSByZWxhY2nDs24geSBsYSBkaXNwZXJzacOzbiBlbnRyZSBsb3MgZMOtYXMgZGUgZHVyYWNpw7NuIHkgbGEgZWRhZCwgdmVtb3MgcXVlIGxhIGVkYWQgbm8gaW5mbHV5ZSBtdWNobyBlbiBsb3MgZMOtYXMgcXVlIGR1cmFuLCBwdWVzdG8gcXVlIHZlbW9zIGNhc2kgbGEgbWlzbWEgZnJlY3VlbmNpYSBkZSBkdXJhY2nDs24gZW50cmUgbGFzIGRpZmVyZW50ZXMgZWRhZGVzLiBFeGlzdGVlbiBhbGd1bm9zIGNhc29zIHF1ZSBzZSBzYWxlbiBkZSBsb3MgImNvbcO6biIgYWwgaGFiZXIgZHVyYWRvIG11Y2hvLiAgIAoKICAKCiMjIyAqKkZPUk0tIERlbGl2ZXJ5IFBsYW4uKiogIAoKUGFyYSBlc3RhIGJhc2UgZGUgZGF0b3Mgbm8gc2UgZ2VuZXJhbiB0YWJsYXMgY3J1emFkYXMgcG9yIGVsIHRpcG8gZGUgdmFyaWFibGVzIHF1ZSBzZSB0aWVuZW4uICAKCioqR3LDoWZpY29zIGN1YWxpdGF0aXZvcyB5IGN1YW50aXRhdGl2b3MqKgpgYGB7cn0KbGlicmFyeShwbHlyKQpgYGAKCmBgYHtyfQpib3hwbG90KGJkX2RlbHBsYW4kcGVkaWRvcywgbWFpbiA9IlRvdGFsIE9yZGVuZXMiKQpgYGAKCk5vdGFtb3MgcXVlIGVuIGVsIHRvdGFsIGRlIG9yZGVuZXMsIGhheSB1bmFzIGN1YW50YXMgcXVlIHNvYnJlcGFzYW4gbG9zIDUwLDAwMCwgbWllbnRyYXMgcXVlIG11Y2hhcyBzZSBxdWVkYW4gY2VyY2EgZGVsIDAuICAKICAgCgoKYGBge3J9Cmhpc3QoKGJkX2RlbHBsYW4kcGVkaWRvcyksY29sPWMoInBpbmsiKSwgbWFpbj0iT3JkZW5lcyIsIGxhcz0xLHhsYWI9IlBlZGlkb3MiKQpgYGAKCkxvIGRlc2NyaXRvIGFudGVyaW9ybWVudGUgcHVlZGUgdmVyc2UgZW4gZXN0ZSBoaXN0b2dyYW1hLCBhbCBlc3RhciBsYSBtYXlvcsOtYSBkZSBsb3MgcGVkaWRvcyBlbnRyZSAwIHkgNSwwMDAgcGVkaWRvcywgY29uIG11eSBwb2NvIHBhc2FuZG8gbG9zIDEwLDAwMC4gICAKICAgCgoKKipHcsOhZmljb3MgZGUgZGlzcGVyc2nDs24qKgpgYGB7cn0KbGlicmFyeSh0aWJibGUpCnRpYmJsZShiZF9kZWxwbGFuKQpgYGAKCmBgYHtyfQpwbG90KGJkX2RlbHBsYW4kSURfZmVjaGEsIGJkX2RlbHBsYW4kcGVkaWRvcywgbWFpbiA9ICJQZWRpZG9zIHBvciBmZWNoYSIsCiAgICAgeGxhYiA9ICJNZXMiLCB5bGFiID0gIk9yZGVuZXMiLCBjb2w9KCJkYXJrcmVkIiksCiAgICAgcGNoID0gMTksIGZyYW1lID0gVFJVRSkKYGBgCgpMb3MgbWVzZXMgZXN0YW5kbyBwb3IgbsO6bWVybywgcG9kZW1vcyB2ZXIgcXVlIGVsIG1lcyBxdWUgcmVjaWJpw7MgZWwgcGVkaWRvIG3DoXNzIGdyYW5kZSBmdWUgZWwgb2N0YXZvIChhZ29zdG8pLCB5IGVsIHF1ZSBtZW5vcyBwZWRpZG9zIGVuIHRvdGFsIGhhIHJlY2liaWRvIGVzIGRpY2llbWJyZSwgc2lndWllbmRvIGRlIG5vdmllbWJyZS4gICAgCiAgCgoKIyMjICoqRk9STS0gRGVsaXZlcnkgUGVyZm9ybWFuY2UqKiAgCgpUYWJsYSBkZSBmcmVjdWVuY2lhICAgClNlIGdlbmVyYW4gbGFzIHRhYmxhcyBkZSBmcmVjdWVuY2lhcyBkZSBsYXMgdmFyaWFibGVzICoqQ1VBTElUQVRJVkFTKiouICAgCmBgYHtyfQpjbGllbnRlPC10YWJsZShiZF9kZWxwZXJmJGNsaWVudGUpCmtuaXRyOjprYWJsZShjbGllbnRlKQpgYGAKCkFxdWkgb2JzZXJ2YW1vcyBxdWUgYSBsb3MgY2xpZW50ZXMgcXVlIG3DoXMgc2UgbGVzIGVudHJlZ8OzIGZ1ZSBhIE1haGxlIHkgYSBWYXJyb2MuICAKCiAgIAoKCioqR3LDoWZpY2EgZGUgZGlzcGVyc2nDs24qKgpgYGB7cn0KYm94cGxvdChiZF9kZWxwZXJmJGRpZiwgdmVydGljYWwgPSBUUlVFLGNvbD0oImRhcmtyZWQiKSkKYGBgCgpWZW1vcyBxdWUgbGEgbWF5b3LDrWEgZGUgbG9zIGRhdG9zIHNlIGVuY3VlbnRyYW4gZW50cmUgZWwgMC4wIHkgMC41IGRlIGRpZmVyZW5jaWEgKG8gZGVsYXkgcGVyZm9ybWFuY2UpLCBjb24gdW5vIGVuIDEuNSwgb3RybyBlbiAyLjAsIHkgb3RybyBzb2JyZXBhc2FuZG8gbG9zIDMuICAKICAKCgojIyMgKipGT1JNLSBQcm9kdWNjacOzbioqICAKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiZXBpRGlzcGxheSIpCmxpYnJhcnkoZXBpRGlzcGxheSkKYGBgCgoqKlRhYmxhIGRlIGZyZWN1ZW5jaWEgeSBncsOhZmljYSBkZSBkYXRvcyBDVUFOVElUQVRJVk9TOiAqVGllbXBvIGRlIENhbGlkYWQqKiogIAoKYGBge3J9CnRpZW1wb19kZV9jYWxpZGFkPC10YWJsZShiZF9wcm9kJHRpZW1wb19jYWxpZGFkKQprbml0cjo6a2FibGUodGllbXBvX2RlX2NhbGlkYWQpCgp0YWIxKGJkX3Byb2QkdGllbXBvX2NhbGlkYWQsc29ydC5ncm91cCA9IEZBTFNFLGdyYXBoPVRSVUUsbWFpbj0iRGlzdHJpYnVjacOzbiBkZWwgdGllbXBvIGRlIGNhbGlkYWQiKQpgYGAKClByaW1lcmFtZW50ZSwgc2UgYW5hbGl6YSBlbCB0aWVtcG8gZGUgY2FsaWRhZC4gQSAgcGFydGlyIGRlIGxhIHRhYmxhIGRlIGZyZWN1ZW5jaWEgeSBkZSBzdSBncsOhZmljYSwgb2JzZXJ2YW1vcyBxdWUgbGEgZ3JhbiBwYXJ0ZSBkZSBsYSBwcm9kdWNjacOzbiBhbmFsaXphZGEgdG9tYSB1biB2YWxvciBkZSB0aWVtcG8gZGUgY2FsaWRhZCBkZSB1biBtaW51dG8sIHBvY28gc2llbmRvIGluZmVyaW9yIGEgMSwgbyAwLiAgCiAgIAoKCioqR3LDoWZpY2EgZGUgZGF0b3MgQ1VBTlRJVEFUSVZPUzogKlRpZW1wbyBNw61uaW1vKioqICAKCmBgYHtyfQpoaXN0KChiZF9wcm9kJHRtcG9fbWluKSxjb2w9Yygib3JhbmdlIiksbWFpbj0iRGlzdHJpYnVjacOzbiBkZWwgVGllbXBvIE3DrW5pbW8iLHhsYWI9Ik1pbnV0b3MiKQpgYGAKClNpZ3VpZW50ZW1lbnRlLCBvYnNlcnZhbW9zIGVsIHRpZW1wbyBtw61uaW1vIChwb3IgbWludXRvcykgZGUgbGEgcHJvZHVjY2nDs24uIExhIGdyYW4gcGFydGUgc2UgZW5jdWVudHJyYSBlbnRyZSBsb3MgMCB5IGxvcyAzMCBtaW51dG9zLiAgIAoKCioqR3LDoWZpY2EgZGUgZGF0b3MgQ1VBTlRJVEFUSVZPUzogKlBpZXphcyBQcm9ncmFtYWRhcyoqKiAgCgpgYGB7cn0KaGlzdCgoYmRfcHJvZCRwaWV6YXNfcHJvZyksY29sPWMoInllbGxvdyIpLG1haW49IlBpZXphcyBQcm9ncmFtYWRhcyIseGxhYj0iVW5pZGFkZXMiKQpgYGAKCkNvbnRpbnVhbmRvIGNvbiBsYXMgcGllemFzIHByb2dyYW1hZGFzLCB2ZW1vcyBxdWUgbGEgZ3JhbiBwYXJ0ZSBkZSBsYXMgcGllemFzIHByb2dyYW1hZGFzIHJvbmRhbiBlbnRyZSAwIHkgMjAwIHVuaWRhZGVzLiAgIAoKKipHcsOhZmljYSBkZSBkYXRvcyBDVUFOVElUQVRJVk9TOiBMw6FtaW5hcyBQcm9jZXNhZGFzKiogIAoKYGBge3J9Cmhpc3QoKGJkX3Byb2QkbGFtaW5hc19wcm9jKSxjb2w9YygiZGFya2dyZWVuIiksbWFpbj0iTMOhbWluYXMgUHJvY2VzYWRhcyIseGxhYj0iVW5pZGFkZXMiKQpgYGAKCkNvbiBsYXMgbMOhbWluYXMgcHJvY2VzYWRhcywgdmVtb3MgcXVlIGxhIG1heW9yIGZyZWN1ZW5jaWEgZXMgZXN0w6EgZW50cmUgZWwgMCB5IDEwMCwgY29uIHVuIGltcG9ydGFudGUgbsO6bWVybyB0YW1iacOpbiB2aXN0byBlbnRyZSAxMDAgeSAzMDAuICAKCioqR3LDoWZpY2EgZGUgZGlzcGVyc2nDs246ICpQaWV6YXMgcHJvZ3JhbWFkYXMqKiogIAoKYGBge3J9CmJveHBsb3QoYmRfcHJvZCRwaWV6YXNfcHJvZyxjb2w9Yygib3JhbmdlIikpCmBgYAoKVGFsIGNvbW8gbG8gdmltb3MgZW4gZWwgaGlzdG9ncmFtYSwgdmVtb3MgcXVlIGxhIG1heW9yIGNvbmNlbnRyYWNpw7NuIGRlIGRhdG9zIGVzdMOhIGVudHJlIGVsIDEwMCB5IDIwMC4gTm8gcmVzdWx0YSBkZW1hc2lhZG8gZGlzcGVyc28sIHNpbiBlbWJhcmdvIGhheSB1biBkYXRvIHF1ZSBzZSBzYWxlIGRlIGxvICJub3JtYWwiLCBwdWVzdG8gcXVlIGxvIGVuY29udHJhbW9zIGFycmliYSBkZSBsb3MgbWlsZXMuICAgCiAgIAoKIyMjICoqRk9STS0gTWVybWEqKiAgCgoqKlRhYmxhIGRlIGZyZWN1ZW5jaWEgeSBncsOhZmljYSBkZSBkYXRvcyBDVUFMSVRBVElWT1M6ICpNZXMqKiogICAgIAoKCmBgYHtyfQprbml0cjo6a2FibGUodGFibGUoYmRfbWVybWFfZiRNZXMpKQp0YWIxKGJkX21lcm1hX2YkTWVzLHNvcnQuZ3JvdXAgPSBGQUxTRSxncmFwaD1UUlVFLG1haW49IkRpc3RyaWJ1Y2nDs24gZGUgbGEgbWVybWEgcG9yIG1lcyIpCmBgYAoKVmllbmRvIGxvcyBkZXNwZXJkaWNpb3MgcG9yIGtpbG9zLCBwb3IgbWVzLCB2ZW1vcyBxdWUgYWdvc3RvIGZ1ZSB1biBtZXMgcXVlIHR1dm8gdW5hIGdyYW4gY2FudGlkYWQgZGUgZGVzcGVyZGljaW9zLCBzb2JyZSB0b2RvIGFsIGNvbXBhcmFybGEgY29uIGxvcyBvdHJvcyBtZXNlcy4gIAogIAoKKipHcsOhZmljYSBkZSBkYXRvcyBDVUFOVElUQVRJVk9TOiAqS2lsb3MqKiogIAoKYGBge3J9Cmhpc3QoKGJkX21lcm1hX2YkS2lsb3MpLGNvbD1jKCJsaWdodGdyZWVuIiksbWFpbj0iQ2FudGlkYWQgZGUgbWVybWEgcG9yIGtpbG9zIix4bGFiPSJLaWxvZ3JhbW9zIikKYGBgCgpWaWVuZG8gbGEgY2FudGlkYWQgZGUga2lsb3MsIG9ic2VydmFtb3MgcXVlIGxhIGdyYW4gY2FudGlkYWQgc2UgZW5jdWVudHJhIGVudHJlIGxvcyAzMDAwIHkgNTAwMCBraWxvcy4gIAogIAoKKipHcsOhZmljYSBkZSBkYXRvcyBDVUFMSVRBVElWT1M6ICpNZXMqKiogIAoKYGBge3J9CnBvcmNlbnRhamVzIDwtIGFzLm51bWVyaWMocm91bmQoKChwcm9wLnRhYmxlKHRhYmxlKGJkX21lcm1hX2YkTWVzKSkpKjEwMCksMikpCmV0aXF1ZXRhcyA8LSBjKCJBYnJpbCIsIkFnb3N0byIsIkVuZXJvIiwiRmVicmVybyIsIkp1bGlvIiwiSnVuaW8iLCJNYXJ6byIsIk1heW8iLCJTZXB0aWVtYnJlIikKZXRpcXVldGFzIDwtIHBhc3RlKGV0aXF1ZXRhcywgcG9yY2VudGFqZXMpCmV0aXF1ZXRhcyA8LSBwYXN0ZShldGlxdWV0YXMsICIlIiwgc2VwID0gIiIpCgoKcGllKHBvcmNlbnRhamVzLGV0aXF1ZXRhcyxjb2w9YygicGluayIsImJsdWUiLCJncmVlbiIsInllbGxvdyIsIm9yYW5nZSIsImRhcmtibHVlIiwiZGFya2dyZWVuIiwicmVkIiwibGlnaHR5ZWxsb3ciLCJsaWdodGJsdWUiLCJkYXJrcmVkIiwibGlnaHRncmVlbiIpLG1haW49Ik1lcm1hIGVuIGxvcyBtZXNlcyIsIHlsYWIgPSJGcmVjdWVuY2lhcyIsbGFzPTEpCmBgYAoKRW4gZXN0YSBkaWZlcmVudGUgcHJlc2VudGFjacOzbiBkZSBsb3MgbWVzZXMsIHZlbW9zIGxvcyBwb3JjZW50YWplcyBkZSBtZXJtYSBkZSBjYWRhIG1lcy4gQXF1w60gbm90YW1vcyBjb21vIEFnb3N0byB0aWVuZSBjYXNpIHVuYSBjdWFydGEgcGFydGUgZGUgdG9kbyBsYSBtZXJtYSBkZWwgYcOxbywgeSBKdW5pbywgU2VwdGllbWJyZSwgeSBFbmVybywgbGEgbWVub3IgcGFydGUuICAKCgoKKipHcsOhZmljYSBkZSBkaXNwZXJzacOzbjogKktpbG9zKioqICAKCmBgYHtyfQpib3hwbG90KGJkX21lcm1hX2YkS2lsb3MsY29sPWMoInllbGxvdyIpKQpgYGAKCkVuIGVzdGUgYm94cGxvdCBxdWUgZGVzY3JpYmUgbGEgZGlzcGVyc2nDs24gZGUgbG9zIGtpbG9zIHBvciBsb3MgbWVzZXMsIHZlbW9zIHF1ZSBubyBoYXkgbXVjaG9zIGRhdG9zIHF1ZSBzZSBzYWxnYW4gZGUgbG8gIm5vcm1hbCIuIFkgcXVlIGxhIGdyYW4gcGFydGUgZGUgbG9zIGRhdG9zIHNlIGVuY3VlbnRyYW4gZW50cmUgbG9zIDMsMDAwLCB5IG3DoXMgZGUgbG9zIDQsMDAwIGtpbG9zOyBsYSBtZWRpYSBzaWVuZG8gYWxyZWRlZG9yIGRlIGxvcyA0LDAwMCBraWxvcy4gICAgCiAgCgoKIyMjICoqRk9STS0gU2NyYXAqKiAgCiAgCioqVGFibGEgZGUgZnJlY3VlbmNpYSB5IGdyw6FmaWNhIGRlIGRhdG9zIENVQUxJVEFUSVZPUzogKlViaWNhY2nDs24gZGUgb3JpZ2VuKioqICAKCmBgYHtyfQprbml0cjo6a2FibGUodGFibGUoc2NyYXBfZmluYWx5YSR1Ymlfb3JpZ2VuKSkKdGFiMShzY3JhcF9maW5hbHlhJHViaV9vcmlnZW4sc29ydC5ncm91cCA9IEZBTFNFLGdyYXBoPVRSVUUsbWFpbj0iVWJpY2FjacOzbiBkZSBvcmlnZW4gZGVsIFNjcmFwIikKYGBgCgpFbiBlc3RlIGNhc28sIG9ic2VydmFtb3MgcXVlIGxhIGdyYW4gcGFydGUgZGVsICpzY3JhcCogc2UgZW5jdWVudHJhIGVuIHByZS1wcm9kdWNjacOzbiwgc2lndWllbmRvIHBvciBsYSB2ZXJpZmljYWNpw7NuIGRlIGNhbGlkYWQgeSBsYSBlbnRyZWdhLCB5IG11eSBwb2NhcyBlc3TDoW4gZW4gcG9zdC1wcm9kdWNjacOzbi4gICAgCiAgIAoKKipHcsOhZmljYSBkZSBkYXRvcyBDVUFOVElUQVRJVk9TOiAqQ2FudGlkYWQqKiogIAoKYGBge3J9Cmhpc3QoKHNjcmFwX2ZpbmFseWEkY2FudGlkYWQpLGNvbD1jKCJsaWdodGdyZWVuIiksbWFpbj0iQ2FudGlkYWQgZGVsIFNjcmFwIix4bGFiPSJVbmlkYWRlcyIpCmBgYAoKT2JzZXJ2YW5kbyBsYSB2YXJpYWJsZSBjYW50aWRhZCBkZWwgKnNjcmFwKiwgdmVtb3MgcXVlIGxhIGdyYW4gcGFydGUgZXN0w6EgZW50cmUgbGFzIDAgIHkgMTAgdW5pZGFkZXMsIHNvYnJlcGFzYW5kbyBzb2xvIG11eSBwb2NhcyBkZWwgdG90YWwuICAKICAgIAoKKipHcsOhZmljYSBkZSBkaXNwZXJzacOzbjogKkZlY2hhIHkgY2FudGlkYWQqKiogIAoKYGBge3J9CnBsb3Qoc2NyYXBfZmluYWx5YSRmZWNoYSxzY3JhcF9maW5hbHlhJGNhbnRpZGFkLCBtYWluID0gIkZlY2hhIHkgY2FudGlkYWQgZGVsIHNjcmFwIiwgeGxhYj0iRmVjaGEiLHlsYWI9IkNhbnRpZGFkIiwKICAgICBwY2ggPSAxOSwgZnJhbWUgPSBGQUxTRSxjb2w9Yygib3JhbmdlIikpCmBgYAoKQ29udGludWFuZG8gY29uIHVuIGFuw6FsaXNpcyBkZSBkaXNwZXJzacOzbiwgY29tcGFyYW1vcyBsYSBmZWNoYSB5IGxhcyB1bmlkYWRlcy4gVmVtb3MgcXVlIGxhIG1heW9yw61hIGRlbCAqc2NyYXAqIGRlIGFnb3N0byBmdWUgdHJhYmFqYWRvIGVuIHBvY2FzIHVuaWRhZGVzLCBjb24gbXV5IHBvY2FzIHNpZW5kbyBhbHRhcy4gVG9kbyBlbCBtZXMgcHJlc2VudGEgdHJhYmFqby4gICAKICAgICAgCgojIyAqKlByb3B1ZXN0YXMqKgoxLiBUb21hbmRvIGVuIGN1ZW50YSBsb3MgZGF0b3Mgb2JzZXJ2YWRvcyBlbiBlbCBhbsOhbGlzaXMgZGUgUkgsIHNlIGhhIHBvZGlkbyBub3RhciBxdWUgRm9ybSB0aWVuZSB1biDDoXJlYSBkZSBvcG9ydHVuaWRhZCwgZW4gY3VhbnRvIGEgc3Ugcm90YWNpw7NuIHkgc3VzIGJhamFzLCBkZWJpZG8gYSBxdWUgZXN0YXMgc29uIG3DoXMgYWx0YXMgZGUgbG8gZGVzZWFibGU7IG11Y2hhcyBkZSBsYXMgYmFqYXMgc29uIGRlYmlkbyBhIGxhcyBmYWx0YXMuIElndWFsbWVudGUsIHNlIGNvbm9jZSBxdWUgbGEgbWF5b3LDrWEgZGUgbGFzIHBlcnNvbmFzIHF1ZSBoYW4gaW5ncmVzYWRvIHNvbiBkZSByZWNpw6luIGluZ3Jlc28gKGVudHJlIDIwMjAgeSAyMDIyKSwgcG9yIGxvIHF1ZSBzZSBjb25vY2UgcXVlIG5vIGhheSBtdWNoYSBhbnRpZ8O8ZWRhZCBlbnRyZSBsb3MgY29sYWJvcmFkb3JlcyBkZSBGb3JtLiBMbyBxdWUgc2UgcHJvcG9uZSBlcyBpbXBsZW1lbnRhciBlc3RyYXRlZ2lhcyBkZSBjcmVjaW1pZW50byB5IGRlc2Fycm9sbG8gZW4gbG9zIHRyYWJhamFkb3JlcywgY29uIGxhIGZpbmFsaWRhZCBkZSBpbmNyZW1lbnRhciBlbCBjb21wcm9taXNvIGRlbnRybyBkZSBsb3MgY29sYWJvcmFkb3JlcyB5IHF1ZSBwdWVkYW4gdmVyIHVuIGZ1dHVybyBkZW50cm8gZGUgbGEgZW1wcmVzYS4gICAgICAKMi4gSWd1YWxtZW50ZSwgZXMgbm90b3JpbyBjb21vIGxhcyBwZXJzb25hcyBkZSBwb2NhIGFudGlnw7xlZGFkIHkgbXVjaGEgYW50aWfDvGVkYWQgZ2FuYW4gY2FzaSBsbyBtaXNtby4gQ29uIGVsIGZpbiBkZSBpbmNlbnRpdmFyIGEgcXVlIGxhcyBwZXJzb25hcyBwYXNlbiBtw6FzIHRpZW1wbyBlbiBsYSBlbXByZXNhLCBjcmVhciB1biBwcm9ncmFtYSBkZSBjb21wZW5zYWNpb25lcyBxdWUgbW90aXZlIGEgbGFzIHBlcnNvbmFzIGEgcXVlZGFyc2UgdW4gbGFwc28gbcOhcyBsYXJnby4gQXPDrSBjb21vICJwcmVtaWFyIiBhIGFxdWVsbG9zIHF1ZSBoYW4gZHVyYWRvIG3DoXMuICAgICAKMy4gRmluYWxtZW50ZSwgcGFyYSBkaXNtaW51aXIgbGEgZGlmZXJlbmNpYSBkZSB0aWVtcG8gY29uIGVsIHJlbmRpbWllbnRvIGRlIGxvcyBlbnbDrW9zIHBvZGVtb3MgdGVuZXIgdW4gZW5jYXJnYWRvIG3DoXMgZGUgVHJhbnNwb3J0ZSBwYXJhIHBvZGVyIGxsZWdhciBhIHRpZW1wbyBvIGFwbGljYXIgdGVjbm9sb2fDrWFzIGNvbW8gQmxvY2tjaGFpbiBvIFJGSUQgcXVlIG5vcyBwdWVkYSBkYXIgaW5mb3JtYWNpw7NuIHNvYnJlIGVsIHRpZW1wbyByZWFsIGRvbmRlIHNlIGVuY3VlbnRyYSwgcXVlIHJlZ2lzdHJlIGVsIHRpZW1wbyB5IGJ1c2NhciBtZWpvcmVzIHJ1dGFzIHBhcmEgdGVuZXIgdW5hIGV4Y2VsZW50ZSBsb2fDrXN0aWNhLiAgICAKCgojIyAqKkJhc2VzIGRlIGRhdG9zIGV4dGVybmFzKioKCiMjIyAqKk3DqXhpY286IGNhcnTDs24qKgoKSW1wb3J0YXIgYmFzZSBkYXRvcwpgYGB7cn0KYmRfbWV4IDwtIHJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL0Rvd25sb2Fkcy9BQ1RJVklEQUQgMi4yICgxKS5jc3YiKQoKYmRfbWV4MSA8LSBiZF9tZXgKYmRfbWV4MTwtc3Vic2V0KGJkX21leDEsc2VsZWN0PS1jKElEKSkKc3RyKGJkX21leDEpCgpiZF9tZXgxIFtkdXBsaWNhdGVkKGJkX21leDEpLF0Kc3VtKGR1cGxpY2F0ZWQoYmRfbWV4MSkpCgpiZF9tZXgyIDwtIGJkX21leDEKbGlicmFyeShkcGx5cikKYmQyIDwtIGRpc3RpbmN0IChiZF9tZXgyKQpgYGAKCkxpYnJlcsOtYXMgcmVxdWVyaWRhcwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGZvcmVpZ24pCmxpYnJhcnkoZHBseXIpICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uIApsaWJyYXJ5KGZvcmNhdHMpICAgICAgIyB0byB3b3JrIHdpdGggY2F0ZWdvcmljYWwgdmFyaWFibGVzCmxpYnJhcnkoZ2dwbG90MikgICAgICAjIGRhdGEgdmlzdWFsaXphdGlvbiAKbGlicmFyeShqYW5pdG9yKSAgICAgICMgZGF0YSBleHBsb3JhdGlvbiBhbmQgY2xlYW5pbmcgCiNpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpCmxpYnJhcnkoY29ycnBsb3QpICAgICAjIGNvcnJlbGF0aW9uIHBsb3RzCmxpYnJhcnkobG10ZXN0KSAgICAgICAjIGRpYWdub3N0aWMgY2hlY2tzIC0gbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMgCmxpYnJhcnkoY2FyKSAgICAgICAgICAjIGRpYWdub3N0aWMgY2hlY2tzIC0gbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMKCmBgYAoKYGBge3J9CnN0cihiZF9tZXgyKQpgYGAKCgoqKkdyw6FmaWNvcyBkZSBkYXRvcyBjdWFsaXRhdGl2b3MgeSBjdWFudGl0YXRpdm9zKiogICAKICAKCkRhdG9zIGN1YWxpdGF0aXZvcwoKYGBge3J9CnBpZShwcm9wLnRhYmxlKHRhYmxlKGJkX21leDIkVGlwb19lc3RhYmxlY2ltaWVudG8pKSxjb2w9YygicGluayIsImJsdWUiKSxtYWluPSJUaXBvIGRlIGVzdGFibGVjaW1pZW50byIsIHlsYWIgPSJGcmVjdWVuY2lhcyIsbGFzPTEpCmBgYAoKT2JzZXJ2YW1vcyBxdWUgbXV5IHBvY29zIGVzdGFibGVjaW1pZW50b3MgZGUgcHJvZHVjY2nDs24gZGUgY2FydMOzbiBtYW5lamFuIGxvcyBzZW1pZmlqby4gIAoKICAKIyMjICoqTcOpeGljbzogRXhwb3J0YWNpb25lcyoqCgpgYGB7cn0KYmRfbWV4X2U8LXJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL0Rvd25sb2Fkcy9leHBvcnRzX214LmNzdiIpCnN1bW1hcnkoYmRfbWV4X2UpCmBgYAoKICAgICAKU2UgZWxpbWluYW4gdmFyaWFibGVzIHF1ZSBubyBhcG9ydGVuIG11Y2hvIGFsIGFuw6FsaXNpcwpgYGB7cn0KYmRfbWV4X2UyPC1iZF9tZXhfZQpiZF9tZXhfZTI8LXN1YnNldChiZF9tZXhfZTIsc2VsZWN0PS1jKFBST0RfRVNULENPQkVSVFVSQSxJRF9NRVMsTU9ERUxPLFRJUE8sSURfUEFJU19ERVNUSU5PKSkKc3VtbWFyeShiZF9tZXhfZTIpCmBgYAoKwr9DdcOhbnRvcyBOQSB0ZW5nbyBwb3IgdmFyaWFibGU/CmBgYHtyfQpzYXBwbHkoYmRfbWV4X2UyLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAogIAogICAgIAoqKkdyw6FmaWNhcyoqCmBgYHtyfQptYXJjYTwtdGFibGUoYmRfbWV4X2UyJE1BUkNBKQprbml0cjo6a2FibGUobWFyY2EpCgp0YWIxKGJkX21leF9lMiRNQVJDQSxzb3J0Lmdyb3VwID0gRkFMU0UsZ3JhcGg9VFJVRSxtYWluPSJEaXN0cmlidWNpw7NuIGRlIEV4cG9ydGFjaW9uZXMgcG9yIE1hcmNhIikKYGBgCgpEZSBsYXMgZXhwb3J0YWNpb25lcyBkZSB2ZWjDrWN1bG9zIGRlIE3DqXhpY28sIG9ic2VydmFtb3MgcXVlIGFxdWVsbG9zIHF1ZSBtYXlvciBwYXJ0aWNpcGFjacOzbiB0aWVuZW4gc29uIEdlbmVyYWwgTW90b3JzIChFVUEpLCBNYXpkYSAoSmFww7NuKSwgTmlzc2FuIChKYXDDs24pLCBDaHJ5c2xlciAoRVVBKSwgVm9sa3N3YWdlbiAoQWxlbWFuaWEpLiBZIGFxdWVsbG9zIHF1ZSBtZW5vcyBzb24gVG95b3RhIChKYXDDs24pLCBIb25kYSAoSmFww7NuKSwgeSBCTVcgKEFsZW1hbmlhKS4gICAKICAgCgpgYGB7cn0Kc2VnbWVudG88LXRhYmxlKGJkX21leF9lMiRTRUdNRU5UTykKa25pdHI6OmthYmxlKHNlZ21lbnRvKQoKdGFiMShiZF9tZXhfZTIkU0VHTUVOVE8sc29ydC5ncm91cCA9IEZBTFNFLGdyYXBoPVRSVUUsbWFpbj0iRXhwb3J0YWNpb25lcyBwb3Igc2VnbWVudG8iKQpgYGAKKkxBIFZBUklBQkxFIFNJTiBOT01CUkUgRVMgU1VCQ09NUEFDVE9TKiAgCkRlIE3DqXhpY28sIHNlIGV4cG9ydGFuIG3DoXMgdmVow61jdWxvcyBDb21wYWN0b3MgeSBTVVZzLiBZIGxhcyBxdWUgbWVub3Mgc29uIGxhcyBNaW5pdmFhbnMgeSBsb3MgdmVow61jdWxvcyBEZSBMdWpvLiAgIAogICAKCgpgYGB7cn0KbWVzPC10YWJsZShiZF9tZXhfZTIkTUVTKQprbml0cjo6a2FibGUobWVzKQoKdGFiMShiZF9tZXhfZTIkTUVTLHNvcnQuZ3JvdXAgPSBGQUxTRSxncmFwaD1UUlVFLCBtYWluPSJEaXN0cmlidWNpw7NuIGRlIEV4cG9ydGFjaW9uZXMgcG9yIE1lcyIpCmBgYAoKTm8gZXhpc3RlIG11Y2hhIGRpZmVyZW5jaWEgZW4gbGFzIGV4cG9ydGFjaW9uZXMgcG9yIG1lcywgc2luIGVtYmFyZ28gbG8gbcOhcyBub3RvcmlvIGVzIHF1ZSBtYXlvIGVzIGVsIG1lcyBxdWUgbcOhcyBwcmVzZW50YSBleHBvcnRhY2lvbmVzLCB5IGVsIHF1ZSBtZW5vcyBlcyBhZ29zdG8uICAgCiAgIAoKYGBge3J9Cmhpc3QoKGJkX21leF9lMiRVTklfVkVIKSxjb2w9YygibGlnaHR5ZWxsb3ciKSxtYWluPSJVbmlkYWRlcyBkZSB2ZWhpY3Vsb3MgZXhwb3J0YWRvcyIseGxhYj0iVW5pZGFkZXMiKQpgYGAKCkxhcyB1bmlkYWRlcyBkZSB2ZWjDrWN1bG9zIGV4cG9ydGFkb3MsIGxhIG1hYWF5b3LDrWEgbm8gc3VwZXIgbGFzIDMsMDAwIHVuaWRhZGVzLiAgIAogICAKICAKICAKYGBge3J9CmHDsW88LXRhYmxlKGJkX21leF9lMiRBTklPKQprbml0cjo6a2FibGUoYcOxbykKCnRhYjEoYmRfbWV4X2UyJEFOSU8sc29ydC5ncm91cCA9IEZBTFNFLGdyYXBoPVRSVUUsbWFpbj0iRGlzdHJpYnVjacOzbiBkZSBleHBvcnRhY2lvbmVzIHBvciBhw7FvIikKYGBgCgpFbiBlc3RhIGNvbXBhcmF0aXZhIGRlIGxhcyBleHBvcnRhY2lvbmVzIGRlIE3DqXhpY28gYSB0cmF2w6lzIGRlIGxvcyBhw7FvcywgdmVtb3MgcXVlIGR1cmFudGUgZWwgMjAxNyBmdWUgbGEgbWVub3IgY2FudGlkYWQsIGHDum4gbWVub3MgcXVlIGVsIDIwMjIgKGHDsW8gc2luIGNvbXBsZXRhcikuIEFxdWVsIHF1ZSBoYSBwcmVzZW50YWRvIG3DoXMgZXhwb3J0YWNpb25lcyBmdWUgZWwgYcOxbyBwYXNhZG8sIDIwMjEuICAKICAKCiMjIyAqKlVuaXRlZCBTdGF0ZXM6IEV4cG9ydHMqKgoKYGBge3J9CmJkX2V1YTwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvRG93bmxvYWRzL2V4cG9ydHMuY3N2IikKc3VtbWFyeShiZF9ldWEpCmBgYAoKCioqR0xPU0FSSU86KiogIAoKKiBBUEVDPSBBc2lhLVBhY2lmaWMgRWNvbm9taWMgQ29vcGVyYXRpb24gICAKCiogQVNFQU49QXNzb2NpYXRpb24gb2YgU291dGgtRWFzdCBBc2lhbiBOYXRpb25zICAgCgoqIENBRlRBPVRyYXRhZG8gZGUgTGlicmUgQ29tZXJjaW8gZW50cmUgQ2VudHJvYW3DqXJpY2EgeSBsb3MgRXN0YWRvcyBVbmlkb3MgIAoKKiBFVS5XaXRob3V0LlVLPVVuacOzbiBldXJvcGVhIHNpbiBlbCBSZWlubyBVbmlkbyAgIAoKKiBFVTI4PVVuacOzbiBldXJvcGVhIGNvbiBlbCBSZWlubyBVbmlkbyAgIAoKKiBGVEEuQ291bnRyaWVzPVRoZSBVbml0ZWQgU3RhdGVzIGhhcyBhZ3JlZW1lbnRzIGluIGZvcmNlIHdpdGggMjAgY291bnRyaWVzOiBBdXN0cmFsaWEsIEJhaHJhaW4sIENhbmFkYSwgQ2hpbGUsIENvbG9tYmlhLCBDb3N0YSBSaWNhLCBEb21pbmljYW4gUmVwdWJsaWMsIEVsIFNhbHZhZG9yLCBHdWF0ZW1hbGEsIEhvbmR1cmFzLCBJc3JhZWwsIEpvcmRhbiwgTWV4aWNvLCBNb3JvY2NvLCBOaWNhcmFndWEsIE9tYW4sIFBhbmFtYSwgUGVydSwgU2luZ2Fwb3JlLCBhbmQgU291dGggS29yZWEuICAKCiogR0NDPUd1bGYgQ29vcGVyYXRpb24gQ291bmNpbDogYSBncm91cCBvZiBzaXggY291bnRyaWVzIGluIHRoZSBQZXJzaWFuIEd1bGY6IEJhaHJhaW4sIEt1d2FpdCwgUWF0YXIsIE9tYW4sIFNhdWRpIEFyYWJpYSwgYW5kIHRoZSBVbml0ZWQgQXJhYiBFbWlyYXRlcyAgCgoqIE1lcmNvc3VyPUVsIE1lcmNhZG8gQ29tw7puIGRlbCBTdXIgKE1FUkNPU1VSKSBBcmdlbnRpbmEsIEJyYXNpbCwgUGFyYWd1YXkgeSBVcnVndWF5LiAgCgoqIFVTTUNBPVVuaXRlZCBTdGF0ZXMtTWV4aWNvLUNhbmFkYSBBZ3JlZW1lbnQgIAoKKiBXb3JsZD0gbXVuZG8uICAKCiAgCk5vIHNlIGhhY2VuIHRhYmxhIGRlIGZyZWN1ZW5jaWEgZGViaWRvIHlhIHF1ZSBubyBleGlzdGVuIHZhcmlhYmxlcyBjdWFsaXRhdGl2YXMuIAoKICAKYGBge3J9Cmhpc3QoKGJkX2V1YSRFVS5XaXRob3V0LlVLKSxjb2w9YygibGlnaHRncmVlbiIpLG1haW49IkV4cG9ydHMgZm9yIEVVICh3aXRob3V0IFVLKSIseGxhYj0iVW5pZGFkZXMiKQpgYGAKCkRlIGxhcyB1bmlkYWRlcyBleHBvcnRhZGFzIGRlIEVzdGFkb3MgVW5pZG9zIGEgbGEgVW5pw7NuIEV1cm9wZWEgc2luIGVsIFJlaW5vIFVuaWRvLCBzZSB0aWVuZW4gZGVzZGUgMyBtaWwgbWlsbG9uZXMgaGFzdGEgNyBtaWwgbWlsbG9uZXMsIHNpZW5kbyBlbCBtYXlvciByYW5nbyBkZSA1IG1pbCBtaWxsb25lcyBhIDYgbWlsIG1pbGxvbmVzLiBFc3RvIHRvbWFuZG8gZW4gY3VlbnRhIGxvcyBhw7FvcyBkZXNkZSBlbCAyMDA4IGhhc3RhIGVsIDIwMjEuICAgIAogIAoKYGBge3J9Cmhpc3QoKGJkX2V1YSRFVTI4KSxjb2w9YygibGlnaHRibHVlIiksbWFpbj0iRXhwb3J0cyBmb3IgRVUgKHdpdGggVUspIix4bGFiPSJVbmlkYWRlcyIpCmBgYApUb21hbmRvIGVuIGN1ZW50YSBhIGxhIFVuacOzbiBFdXJvcGEgY29uIGVsIFJlaW5vIFVuaWRvLCBsYXMgdW5pZGFkZXMgZGUgdmVow61jdWxvcyBleHBvcnRhZG9zIHZhbiBkZXNkZSBsb3MgMyBtaWwgbWlsbG9uZXMgaGFzdGEgbG9zIDkgbWlsIG1pbGxvbmVzIGRlIHVuaWRhZGVzLiBTaWVuZG8gbGEgbWF5b3IgZnJlY3VlbmNpYSBkZXNkZSBsb3MgNSBtaWwgbWlsbG9uZXMgaGFzdGEgbG9zIDYgbWlsIG1pbGxvbmVzLiBTaW4gZW1iYXJnbywgZW4gZXN0ZSBjYXNvLCBzZSB0aWVuZSB1biBpbXBvcnRhbnRlIG7Dum1lcm8gZGUgdW5pZGFkZXMgZXhwb3J0YWRhcyBkZXNkZSBsb3MgNiBtaWwgbWlsbG9uZXMgaGFzdGEgbG8gOCBtaWwgbWlsbG9uZXMuICAgCiAgICAKCmBgYHtyfQpwbG90KGJkX2V1YSRZZWFyLGJkX2V1YSRXT1JMRCwgbWFpbiA9ICJBw7FvIHkgY2FudGlkYWQgZGUgZXhwb3J0YWNpb25lcyIsIHhsYWI9IkHDsW8iLHlsYWI9Ik11bmRvIiwKICAgICBwY2ggPSAxOSwgZnJhbWUgPSBGQUxTRSxjb2w9Yygib3JhbmdlIikpCmBgYAoKTm90YW1vcyBjb21vIGVsIGHDsW8gY29uIG1heW9yZXMgZXhwb3J0YWNpb25lcyBkZSBwYXJ0ZSBkZSBFVUEgZXMgZWwgMjAxOCwgeSBlbCBtZW5vciAyMDA5IChwb3NpYmxlbWVudGUgZGViaWRvIGEgbGEgY3Jpc2lzKS4gRGVsIDIwMTAgaGFzdGEgZWwgMjAxOCB2ZW1vcyB1biBpbXBvcnRhbnRlIGNyZWNpbWllbnRvIGRlIGxvcyA2IGJpbGxvbmVzIGRlIHZlaMOtY3Vsb3MgZXhwb3J0YWRvcyBoYXN0YSBsb3MgOSBiaWxsb25lcy4gICAKICAgCgpgYGB7cn0KYm94cGxvdChiZF9ldWEkQ0FGVEEsY29sPWMoIm9yYW5nZSIpKQpgYGAKCkVuIGN1YW50byBhIGxvcyB2ZWjDrWN1bG9zIGV4cG9ydGFkb3MgaGFjaWEgbGEgem9uYSBkZSBDZW50cm9hbcOpcmljYSB5IGxvcyBFc3RhZG9zIFVuaWRvcywgdmVtb3MgcXVlIGxhIG1heW9yw61hIGRlIGxhcyBleHBvcnRhY2lvbmVzIHNlIGNvbmNlbnRyYW4gZW50cmUgbG9zIDM1MCBtaWxsb25lcyBoYXN0YSBsb3MgNDUwIG1pbGxvbmVzLCBjb24gdW4gcHJvbWVkaW8gZGUgYXByw7N4aW1hZGFtZW50ZSBsb3MgMzc1IG1pbGxvbmVzLiBTaW4gZW1iYXJnbywgZWwgcmFuZ28gdmEgZGVzZGUgbG9zIDMwMCBtaWxsb25lcyBoYXN0YSBsb3MgNTUwIG1pbGxvbmVzIGRlIHZlaMOtY3Vsb3MgZXhwb3J0YWRvcyBhIGVzdGEgem9uYS4gICAgCgoKIyA8aW1nIHNyYz0iL1VzZXJzL2VsZW5hdmVsYS9EZXNrdG9wL0NhcHR1cmEgZGUgUGFudGFsbGEgMjAyMi0xMC0xMCBhIGxhKHMpIDE0LjI0LjMwLnBuZyI+ICAKCiMjICoqQW7DoWxpc2lzIEV4cGxvcmF0b3JpbyBkZSBsYXMgQmFzZXMgZGUgRGF0b3MqKgoKIyMjICoqRXN0YWTDrXN0aWNvcyBkZXNjcmlwdGl2b3MqKgoKIyMjIyAqKkNvbGFib3JhZG9yZXMqKgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGluc3RhbGwucGFja2FnZXMoInBzeWNoIikKbGlicmFyeShwc3ljaCkKI2Rlc2NyaWJlKGJkX2NvbGFiKQoKVmFyaWFibGVzIDwtYygiRWRhZCIsIkFsdGEiLCJTYWxhcmlvIERpYXJpbyIpCk1lZGlhIDwtYygiMzUuNDUiLCIyMDIwLjY5IiwiMTc5LjM1IikKTWVkaWFuYSA8LWMoIjMzIiwiMjAyMiIsIjE4MC42OCIpCk1vZGE8LWMoIjMyIiwiMjAyMiIsIjE4MC42OCIpCkRlc3ZpYWNpb25fZXN0YW5kYXIgPC1jKCIxMi4yMCIsIjIuMzQiLCIyNS4xNyIpClJhbmdvPC1jKCI0OCIsIjEyIiwiMTkyLjYiKQoKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVzLE1lZGlhLE1lZGlhbmEsTW9kYSxEZXN2aWFjaW9uX2VzdGFuZGFyLFJhbmdvKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKQSBwYXJ0aXIgZGUgZXN0b3MgZXN0YWTDrXN0aWNvcyBkZXNjcmlwdGl2b3MgZGUgbG9zIGNvbGFib3JhZG9yZXMgQUNUVUFMRVMgZGUgRk9STSwgb2JzZXJ2YW1vcyBxdWUgdGVuZW1vcyBzb2xvIDMgdmFyaWFibGVzICBjdWFudGl0YXRpdmFzLCBwb3IgbG8gdGFudG8gc29sbyBlc3RhcyBzZXLDoW4gZGVzY3JpdGFzLiAgIAoqKjEuIEVkYWQ6KiogTGEgbWVkaWEgbyBlbCBwcm9tZWRpbyBlcyBkZSAzNS40NSBhw7FvcywgbGEgbWVkaWFuYSBlcyBkZSAzMyBhw7FvcywgeSBsYSBtb2RhIGVzIDMyIGHDsW9zLiBBIHBhcnRpciBkZSBlc3RvIGNvbXByZW5kZW1vcyBxdWUgZXhpc3RlbiBtdWNob3MgY29sYWJvcmFkb3JlcyBxdWUgc29uIGFkdWx0b3MgZW4gc3VzIHRyZWludGFzIChyZWxhdGl2YW1lbnRlIGrDs3ZlbmVzKS4gTGEgZGVzdmlhY2nDs24gZXMgZGUgMTIuMjAsIHBvciBsbyBxdWUgY29tcHJlbmRlbW9zIHF1ZSBsYSBlZGFkIHNlIGRlc3bDrWEgZGUgbGEgbWVkaWEgYXByb3hpbWFkZW1lbnRvIHBvciAxMiBhw7FvczsgbGEgbWF5b3LDrWEgZGUgbG9zIGRhdG9zIGVzdMOhbiBlbiBlc3RlIHJhbmdvICgrMTIsLTEyKS4gRmluYWxtZW50ZSwgY29uIGVsIG3DoXhpbW8geSBlbCBtw61uaW1vIGRlIGxhcyBlZGFkZXMgb2J0ZW5lbW9zIGVsIHJhbmdvIGRlIGVkYWQ6IDQ4IGHDsW9zLiAgCioqMi4gQcOxbyBkZSBhbHRhOioqICBPYnNlcnZhbW9zIHF1ZSBsYSBtZWRpYSBlcyBkdXJhbnRlIGxhIMO6bHRpbWEgcGFydGUgZGVsIDIwMjAsIGxhIG1lZGlhbmEgeSBsYSBtb2RhIGVzIGVsIDIwMjIuIFBvciAgb3RyYSBwYXJ0ZSwgdmVtb3MgcXVlIGxhIGRlc3ZpYWNpw7NuIGVzIGRlIDIuMzQuIFBvciBsbyB0YW50bywgcG9kZW1vcyBub3RhciBjb21vIGVzIHF1ZSBsYSBtYXlvcsOtYSBkZSBsYXMgcGVyc29uYXMgcXVlIGVzdMOhbiBhY3R1YWxtZW50ZSBlbiBGT1JNIG5vIHRpZW5lbiBtdWNoYSBhbnRpZ8O8ZWRhZCwgeWEgcXVlIGluZ3Jlc2Fyb24gZHVyYW50ZSBsb3Mgw7psdGltb3MgKGFwcm94KSA0IGHDsW9zLiBWaWVuZG8gcXVlIGVsIHJhbmdvIGVzIGRlIDEyIGHDsW9zLCBubyBzZSB0aWVuZSBtdWNoYSBhbnRpZ8O8ZWRhZCBkZSBwYXJ0ZSBkZSBsYSBtYXlvcsOtYS4gIAoqKjMuIFNhbGFyaW8gRGlhcmlvOioqIEVuICBlbCBzYWxhcmlvIGRpYXJpbyBvYnNlcnZhbW9zIHF1ZSBsYSBtb2RhICB5IGxhIG1lZGlhbmEgZXMgZGUgJDE4MC42OCwgcG9yIGxvIHF1ZSBwb2RlbW9zIGNvbmNsdWlyIHF1ZSBlcyB1biBuw7ptZXJvIGFsdGFtZW50ZSByZXBldGlkbyBlbiBsb3MgY29sYWJvcmFkb3JlcyBhY3R1YWxlcy4gTWllbnRyYXMgcXVlIGxhIG1lZGlhbmEgZXMgdW4gcG9jbyBpbmZlcmlvciwgJDE3OS4zOSwgY29uIGxvIHF1ZSBwb2RlbW9zIGluZmVyaXIgcXVlIGV4aXN0ZW4gbcOhcyBwZXJzb25hcyBwb3IgZGViYWpvIGRlICQxODAuNjggcXVlIGFycmliYSBkZS4gRW4gY3VhbnRvIGEgbGEgZGVzdmlhY2nDs24sIGVuY29udHJhbW9zIHF1ZSBlcyBkZSAyNS4xNyBwZXNvcywgc2UgYXN1bWUgcXVlIGxvcyBzYWxhcmlvcyBubyBlc3TDoW4gbXV5IGRpc3BlcnNvcy4gTGEgZGlmZXJlbmNpYSBlbnRyZSBlbCBtZW5vciB5IGVsIG1heW9yIGRlICQxOTIuNi4gIAoKIyMjIyAqKkJhamFzKioKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpCmxpYnJhcnkocHN5Y2gpCiNkZXNjcmliZShiZF9iYWphcykKClZhcmlhYmxlcyA8LWMoIkVkYWQiLCJEdXJhY2lvbiIsIlNhbGFyaW8gRGlhcmlvIikKTWVkaWEgPC1jKCIzMC43NyIsIjc1LjU5IiwiMTc3LjkzIikKTWVkaWFuYSA8LWMoIjI5IiwiMjAuNTAiLCIxODAuNjgiKQpNb2RhPC1jKCIyMSIsIjIwLjUiLCIxODAuNjgiKQpEZXN2aWFjaW9uX2VzdGFuZGFyIDwtYygiOS42OSIsIjIxNS41MCIsIjIzLjQxIikKUmFuZ288LWMoIjQzIiwiMTk2NiIsIjM1NS41NSIpCgp0YWJsZTwtZGF0YS5mcmFtZShWYXJpYWJsZXMsTWVkaWEsTWVkaWFuYSxNb2RhLERlc3ZpYWNpb25fZXN0YW5kYXIsUmFuZ28pCmtuaXRyOjprYWJsZSh0YWJsZSkKYGBgCgpBIHBhcnRpciBkZSBlc3RvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvcyBkZSBsb3MgY29sYWJvcmFkb3JlcyBQQVNBRE9TIGRlIEZPUk0sIG9ic2VydmFtb3MgcXVlIHRlbmVtb3Mgc29sbyAzIHZhcmlhYmxlcyAgY3VhbnRpdGF0aXZhcywgcG9yIGxvIHRhbnRvIHNvbG8gZXN0YXMgc2Vyw6FuIGRlc2NyaXRhcy4gICAKKioxLiBFZGFkOioqIExhIG1lZGlhIG8gZWwgcHJvbWVkaW8gZXMgZGUgMzAuNzcgYcOxb3MsIGxhIG1lZGlhbmEgZXMgZGUgMjkgYcOxb3MsIHkgbGEgbW9kYSBlcyAyMSBhw7Fvcy4gQSBwYXJ0aXIgZGUgZXN0byBjb21wcmVuZGVtb3MgcXVlIGV4aXN0ZW4gbXVjaG9zIGNvbGFib3JhZG9yZXMgcXVlIHNvbiBhZHVsdG9zIGVuIHN1cyB2ZWludGVzIHkgdHJlaW50YXMgKGrDs3ZlbmVzKS4gTGEgZGVzdmlhY2nDs24gZXMgZGUgOS42OSwgcG9yIGxvIHF1ZSBjb21wcmVuZGVtb3MgcXVlIGxhIGVkYWQgc2UgZGVzdsOtYSBkZSBsYSBtZWRpYSBhcHJveGltYWRlbWVudG8gcG9yIDEwIGHDsW9zOyBsYSBtYXlvcsOtYSBkZSBsb3MgZGF0b3MgZXN0w6FuIGVuIGVzdGUgcmFuZ28gZGUgMjAgYSA0MCBhcHJveC4gRmluYWxtZW50ZSwgY29uIGVsIG3DoXhpbW8geSBlbCBtw61uaW1vIGRlIGxhcyBlZGFkZXMgb2J0ZW5lbW9zIGVsIHJhbmdvIGRlIGVkYWQ6IDQzIGHDsW9zLiAgTGFzIGVkYWRlcyBkZSBsb3MgcXVlIHNlIGhhbiBkYWRvIGRlIGJhamEgc29uIG1lbm9yZXMgYSBsb3MgcXVlIHNpZ3VlbiBlbiBsYSBlbXByZXNhLiAgICAKKioyLiBEdXJhY2nDs246KiogIE9ic2VydmFtb3MgcXVlIGxhIG1lZGlhIGVzIGRlIDc1LjU5IGTDrWFzLCBsYSBtZWRpYW5hIHkgbGEgbW9kYSBlcyAyMC41OyBwb3IgbG8gdGFudG8gYXN1bWltb3MgcXVlIGhheSBwZXJzb25hcyBxdWUgZHVyYXJvbiBtdWNobyBtw6FzIHF1ZSBsYSBtYXlvcsOtYSAoZGViaWRvIGEgbGEgZGlmZXJlbmNpYSBlbnRyZSBsYSBtZWRpYSB5IGxhIG1lZGlhbmEgeSBtb2RhKS4gVmVtb3MgcXVlIGxhIGRlc3ZpYWNpw7NuIGVzIGRlIDIxNS41IGTDrWFzLCBsbyBjdWFsIHB1ZWRlIHNlciBjb25zaWRlcmFkbyBtdWNobyBvYnNlcnZhbmRvIGxhIG1lZGlhbmEsIG1vZGEgeSBsYSBtZWRpYTsgZXMgcG9yIGVzdG8gcXVlIHJlc3VsdGEgaW50ZXJlc2FudGUgdmVyIGVsIHJhbmdvLCB5YSBxdWUgbGEgZGlmZXJlbmNpYSBlbnRyZSBsYSBwZXJzb25hIHF1ZSBtZW5vcyBkdXLDsyB5IHNhbGnDsywgIHkgbGEgcXVlIG3DoXMgZHVyw7MgeSBzYWxpw7MgZXMgZGUgMTk2NiAoZ3JhbiBjYW50aWRhZCBkZSBkw61hcyBkZSBkaWZlcmVuY2lhKS4gICAKKiozLiBTYWxhcmlvIERpYXJpbzoqKiBFbiAgZWwgc2FsYXJpbyBkaWFyaW8gb2JzZXJ2YW1vcyBxdWUgbGEgbW9kYSB5IGxhIG1lZGlhbmEgZXMgZGUgJDE4MC42OCwgcG9yIGxvIHF1ZSBwb2RlbW9zIGNvbmNsdWlyIHF1ZSBlcyB1biBuw7ptZXJvIGFsdGFtZW50ZSByZXBldGlkbyBlbiBsb3MgY29sYWJvcmFkb3JlcyBhY3R1YWxlcy4gTWllbnRyYXMgcXVlIGxhIG1lZGlhbmEgZXMgdW4gcG9jbyBpbmZlcmlvciwgJDE3Ny45MywgY29uIGxvIHF1ZSBwb2RlbW9zIGluZmVyaXIgcXVlIGV4aXN0ZW4gbcOhcyBwZXJzb25hcyBwb3IgZGViYWpvIGRlICQxODAuNjggcXVlIGFycmliYSBkZS4gRW4gY3VhbnRvIGEgbGEgZGVzdmlhY2nDs24sIGVuY29udHJhbW9zIHF1ZSBlcyBkZSAyMy40MSBwZXNvcywgc2UgYXN1bWUgcXVlIGxvcyBzYWxhcmlvcyBubyBlc3TDoW4gbXV5IGRpc3BlcnNvcy4gTGEgZGlmZXJlbmNpYSBlbnRyZSBlbCBtZW5vciB5IGVsIG1heW9yIGRlICQzNTUuNTU7IGh1Ymllcm9uIHBlcnNvbmFzIHF1ZSBnYW5hYmFuIG11Y2hvIG3DoXMgcXVlIGVsIHByb21lZGlvLiAgIAoKCiMjIyMgKipQcm9kdWNjacOzbioqCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgaW5zdGFsbC5wYWNrYWdlcygicHN5Y2giKQpsaWJyYXJ5KHBzeWNoKQojZGVzY3JpYmUoYmRfcHJvZCkKClZhcmlhYmxlcyA8LWMoIlBpZXphcyBwcm9ncmFtYWRhcyIsIlRpZW1wbyBtw61uaW1vIiwiTMOhbWluYXMgcHJvY2VzYWRhcyIsIlRpZW1wbyBjYWxpZGFkIiApCk1lZGlhIDwtYygiMTgzLjQwIiwiMjIuMDQiLCI3OC44NCIsIjAuODkiICkKTWVkaWFuYSA8LWMoIjEyMCIsIjIwIiwiNDEiLCIxIiApCk1vZGE8LWMoIjIwMCIsIjIwIiwiMCIsIjEiKQpEZXN2aWFjaW9uX2VzdGFuZGFyIDwtYygiMTY5LjQ1IiwiMTIuMzIiLCIxMDMuMTgiLCIwLjcwIiApClJhbmdvPC1jKCIxOTk5IiwiMTEwIiwiODAzIiwiMTEiKQoKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVzLE1lZGlhLE1lZGlhbmEsTW9kYSxEZXN2aWFjaW9uX2VzdGFuZGFyLFJhbmdvKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKVmllbmRvIGxhcyB2YXJpYWJsZXMgY3VhbnRpdGF0aXZhcyBkZSBsYSBwcm9kdWNjacOzbiBkZSBqdWxpbywgYWdvc3RvLCB5IHNlcHRpZW1icmUgZGUgRk9STSwgcG9kZW1vcyBhbmFsaXphciBsbyBzaWd1aWVudGU6ICAKKioxLlBpZXphcyBwcm9ncmFtYWRhczoqKiBFbiBlc3RhIGNhc28gcmVzdWx0YSBpbnRlcmVzYW50ZSBlbXBlemFyIGNvbiBlbCByYW5nbzogMTk5OS4gUG9yIGxvIHF1ZSBhc3VtaW1vcyBxdWUgZWwgdmFsb3IgIG1heW9yIGVzIGRlIDIwMDAgcGllemFzIHByb2dyYW1hZGFzLCB5IGxvIG1lbm9yIGhhIHNpZG8gMCBlbiBkaWZlcmVudGVzIGTDrWFzLiBWZW1vcyBxdWUgbGEgbWVkaWEgZXMgZGUgMTgzLjQwIHBpZXphcywgbGEgbWVkaWFuYSBlcyBkZSAxMjAgcGllemFzLCB5IGxhIG1vZGEgZGUgMjAwOyBhc3VtaW1vcyBxdWUgIGxvIGNvbcO6biBlcyBxdWUgZW4gdW4gZMOtYSBzZSBwcm9ncmFtZW4gbm8gbcOhcyBkZSAgMjAwIHBpZXphcywgeSBxdWUgc29sbyBlbiAgY2Fzb3MgImVzcGFjaWFsZXMiIGxsZWd1ZSBhIGxvcyBtaWxlcy4gTGEgZGVzdmlhY2nDs24gZXN0YW5kYXIgbm8gZXMgbXV5IGFsdGEsIGNvbnNpZGVyYW5kbyBlbCByYW5nbywgZXN0YSBlcyBkZSAxNjkuNDUgcGllemFzIHByb2dyYW1hZGFzOyBsbyAibm9ybWFsIiBlcyBxdWUgbGFzIHBpZXphcyBlIHN0w6luIGVudHJlIGxhcyAwIHkgbGFzIDQwMCBwaWV6YXMgcHJvZ3JhbWFkYXMuICAKKioyLiBUaWVtcG8gbcOtbmltbzoqKiBJbXBvcnRhbnRlIHJlY29yZGFyIHF1ZSBlc3RhIHZhcmlhYmxlIHNlIG1pZGUgZW4gbWludXRvcy4gTGEgbWVkaWEgZXMgZGUgMjIuMDUsIGxhIG1lZGlhbmEgeSBsYSBtb2RhIGRlIDIwLCBwb3IgbG8gIHF1ZSBhc3VtaW1vcyBxdWUgbXVjaG9zIGRhdG9zIHNlIGVuY3VlbnRyYW4gZW4gbG9zIHZlaW50ZSAoKy8tKSBtaW51dG9zIGRlIGNhbGlkYWQgZGUgcHJvZHVjY2nDs24uICBMYSBkZXN2aWFjacOzbiBlc3RhbmRhciBlcyBkZSAxMi4zMiwgbG8gIGN1YWwgbm8gZXMgbXVjaGEgZGlzcGVyc2nDs24gY29uc2lkZXJhbmRvIHF1ZSBlbCByYW5nbyBlcyBkZSAxMTAgbWludXRvcy4gRWwgdGllbXBvIG3DrW5pbW8gaGEgdmFyaWFkbyBkZXBlbmRpZW5kbyBkZSBsYSBvcmRlbiwgaGFzdGEgbGxlZ2FyIGEgY2FzaSBkb3MgaG9yYXMuICAKKiozLiBMw6FtaW5hcyBwcm9jZXNhZGFzOioqIExhIG1lZGlhIGRlIGxhcyBsw6FtaW5hcyBwcm9jZXNhZGFzIGVuIGVzdG9zIG1lc2VzIGVzIGRlIDc4Ljg0LCBsYSBtZWRpYW5hIGRlIDQxLCB5IGxhIG1vZGEgZGUgMC4gQ29uIGVzdG8gY29tcHJlbmRlbW9zIHF1ZSBsYSBtYXlvcsOtYSBkZSBsb3MgZMOtYXMgbm8gc2UgcHJvY2Vzw7MgbmluZ3VuYSBsw6FtaW5hcywgcGVybyBxdWUgbG9zIG90cm9zIGTDrWFzIG5vIHNlZ3XDrWFuIHVuYSB0ZW5kZW5jaWEgY2xhcmEuIExhIGRlc3ZpYWNpw7NuIGVzIGRlIDEwMy4xOCBsw6FtaW5hcyBwcm9jZXNhZGFzIHkgZWwgcmFuZ28gZGUgODAzIGzDoW1pbmFzLiAgCioqNC4gVGllbXBvIGNhbGlkYWQ6KiogSW1wb3J0YW50ZSByZWNvcmRhciBxdWUgZXN0YSB2YXJpYWJsZSBzZSBtaWRlIGVuIG1pbnV0b3MuIExhIG1heW9yw61hIGRlIGxvcyBkYXRvcyBzZSBlbmN1ZW50cmFuIGVudHJlIGVsIDAgeSAxIG1pbnV0byBkZSBjYWxpZGFkLCBkZSBhY3VlcmRvIGFsIGFuw6FsaXNpcyBwcmV2aW8sIHBvciBsbyB0YW50byBhcXXDrSB0YW1iacOpbiBvYnNlcnZhbW9zIHF1ZSBsYSBtZWRpYSBlcyBkZSAwLjg5IChhY2VyY8OhbmRvc2UgbcOhcyBhIDEgbWludXRvKSwgbGEgbWVkaWFuYSBlcyBkZSAxIHkgbGEgbW9kYSBlcyBkZSAxLiBMbyBtw6FzIGNvbcO6biBkZSB2ZXIgZW4gRk9STSBlbiBjdWFudG8gYSB0aWVtcG8gZGUgY2FsaWRhZCBkZSBzdSBwcm9kdWNjacOzbiBlcyBkZSB1biBtaW51dG8uIExhIGRlc3ZpYWNpw7NuIGVzIHBvY2EgKDAuNzApLCBjb25zaWRlcmFuZG8gcXVlIGVsIHJhbmdvIGVzIGRlIDExIG1pbnV0b3MuIExvcyBkYXRvcyBubyBlc3TDoW4gbXV5IGRpc3BlcnNvcy4gCgoKIyMjIyAqKlNjcmFwKioKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpCmxpYnJhcnkocHN5Y2gpCiNkZXNjcmliZShzY3JhcF9maW5hbHlhKQoKVmFyaWFibGUgPC1jKCJDYW50aWRhZCIpCk1lZGlhIDwtYygiNi43MCIpCk1lZGlhbmEgPC1jKCIyIikKTW9kYTwtYygiMSIpCkRlc3ZpYWNpb25fZXN0YW5kYXIgPC1jKCIxMS44NSIpClJhbmdvPC1jKCI5NiIpCgp0YWJsZTwtZGF0YS5mcmFtZShWYXJpYWJsZSxNZWRpYSxNZWRpYW5hLE1vZGEsRGVzdmlhY2lvbl9lc3RhbmRhcixSYW5nbykKa25pdHI6OmthYmxlKHRhYmxlKQpgYGAKCkVuIGVzdGEgY2FzbyBzb2xvIGVuY29udHJhbW9zIHVuYSB2YXJpYWJsZSBjdWFudGl0YXRpdmEgYSBhbmFsaXphcjogIAoqKjEuIENhbnRpZGFkIGRlIHNjcmFwOioqIG9ic2VydmFtb3MgcXVlIGVsIHByb21lZGlvIGRlbCBzY3JhcCBlcyBkZSA2LjcwLCBsYSBtZWRpYW5hIGVzIGRlIDIgeSBsYSBtb2RhIGVzIGRlIDE7IGNvbXByZW5kZW1vcyBxdWUgbGEgbWF5b3LDrWEgZGUgbG9zIHZhbG9yZXMgZW4gZXN0ZSBiYXNlIGRlIGRhdG9zIGVzdMOhbiBlbnRyZSAxIHkgMi4gQXVucXVlIGVsIHJhbmdvIGVzIGRlIDk2LCBsYSBkZXN2aWFjacOzbiAgZXMgZGUgMTEuODUsIHBvciBsbyBxdWUgc2UgcHVlZGUgYXN1bWlyIHF1ZSBsbyBjb23Dum4gZXMgcXVlIGxhIGNhbnRpZGFkIG5vIHBhc2UgZGUgMjAuICAgCgojIyMjICoqTWVybWEqKgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGluc3RhbGwucGFja2FnZXMoInBzeWNoIikKbGlicmFyeShwc3ljaCkKI2Rlc2NyaWJlKGJkX21lcm1hX2YpCgpWYXJpYWJsZSA8LWMoIktpbG9zIikKTWVkaWEgPC1jKCIzLDcwOC41MiIpCk1lZGlhbmEgPC1jKCIzLDkyNSIpCk1vZGE8LWMoIjMsODEwIikKRGVzdmlhY2lvbl9lc3RhbmRhciA8LWMoIjEsMDIzLjk5IikKUmFuZ288LWMoIjUsMzUwIikKCnRhYmxlPC1kYXRhLmZyYW1lKFZhcmlhYmxlLE1lZGlhLE1lZGlhbmEsTW9kYSxEZXN2aWFjaW9uX2VzdGFuZGFyLFJhbmdvKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKRW4gZXN0YSBjYXNvIHNvbG8gZW5jb250cmFtb3MgdW5hIHZhcmlhYmxlIGN1YW50aXRhdGl2YSBhIGFuYWxpemFyOiAgCioqMS4gS2lsb3MgZGUgbWVybWE6KiogbGEgbWVkaWEgZXMgZGUgMyw3MDguNTIga2dzLCBsYSBtZWRpYW5hIGVzIGRlIDMsOTI1IGtncywgeSBsYSBtb2RhIGVzIGRlIDMsODEwIGtnczsgc2UgYXN1bWUgcXVlIGxvIGNvbcO6biBlcyBlbmNvbnRyYXIga2lsb3MgZGUgbWVybWEgZW4gbG9zIDMsMDAwIHBvciB2ZXouIEVsIHJhbmdvIGVzIGRlIDUsMzUwIGtncyB5IGxhIGRlc3ZpYWNpw7NuIGVzIGRlIDEsMDIzLjk5IGtnczsgc2UgY29uc2lkZXJhIHF1ZSBsb3MgZGF0b3MgZXN0w6FuIGFsZ28gZGlzcGVyc29zLgoKIyMjIyAqKkRlbGl2ZXJ5IHBlcmZvcm1hbmNlKioKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpCmxpYnJhcnkocHN5Y2gpCmRlc2NyaWJlKGJkX2RlbHBlcmYpCgpWYXJpYWJsZSA8LWMoIkRpZmVyZW5jaWEgKGRlbGF5IHBlcmZvcm1hbmNlKSIpCk1lZGlhIDwtYygiMC4zMCIpCk1lZGlhbmEgPC1jKCIwLjAiKQpNb2RhPC1jKCIwLjAiKQpEZXN2aWFjaW9uX2VzdGFuZGFyIDwtYygiMC41NiIpClJhbmdvPC1jKCIzLjMiKQoKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGUsTWVkaWEsTWVkaWFuYSxNb2RhLERlc3ZpYWNpb25fZXN0YW5kYXIsUmFuZ28pCmtuaXRyOjprYWJsZSh0YWJsZSkKYGBgCgpFbiBlc3RhIGNhc28gc29sbyBlbmNvbnRyYW1vcyB1bmEgdmFyaWFibGUgY3VhbnRpdGF0aXZhIGEgYW5hbGl6YXI6ICAKKioxLiBEZWxheSBwZXJmb3JtYW5jZToqKiBvYnNlcnZhbW9zIGVuIGVzdGEgdmFyaWFibGUgY29tbyBsYSBtZWRpYSBlcyBkZSAwLjMgbWludXRvcywgbGEgbW9kYSB5IGxhIG1lZGlhbmEgZXMgZGUgMC4gRW4gZXN0byBlbmNvbnRyYW1vcyAgcXVlIGxhIG5vcm1hbCBlcyBxdWUgbm8gaGF5YSB1biAqZGVsYXkqIGVuIGxhcyBlbnRyZWdhcywgeSBhbCBvYnNlcnZhciBlbCByYW5nbyAoMy4zKSB2ZW1vcyBxdWUgYcO6biBsbyBtw6FzICJyYWRpY2FsIiBlcyBtdXkgcG9jby4gICAKCiMjIyMgKipEZWxpdmVyeSBQbGFuKioKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpCmxpYnJhcnkocHN5Y2gpCiNkZXNjcmliZShiZF9kZWxwbGFuKQoKVmFyaWFibGVzIDwtYygiUGVkaWRvcyIpCk1lZGlhIDwtYygiMSw3MDMuMTQiKQpNZWRpYW5hIDwtYygiMCIpCk1vZGE8LWMoIjAiKQpEZXN2aWFjaW9uX2VzdGFuZGFyIDwtYygiNiwxNjQuMDQiKQoKdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGVzLE1lZGlhLE1lZGlhbmEsTW9kYSxEZXN2aWFjaW9uX2VzdGFuZGFyKQprbml0cjo6a2FibGUodGFibGUpCmBgYAoKRW4gZXN0YSBjYXNvIHNvbG8gZW5jb250cmFtb3MgdW5hIHZhcmlhYmxlIGN1YW50aXRhdGl2YSBhIGFuYWxpemFyOiAgIAoqKjEuIFBlZGlkb3M6KiogRW4gbG9zIHBlZGlkb3MgZW5jb250cmFtb3MgcXVlIGxvIGNvbcO6biBlcyB0ZW5lciAwIChkZWJpZG8gYSBsYSBtb2RhIHkgbGEgbWVkaWFuYSksIHBlcm8gcXVlIGEgcGFydGlyIGRlIGxhIGdyYW4gZGlzcGVyc2nDs24gZGUgbG9zIGRhdG9zIGxhIG1lZGlhICgxLDcwMy4xNCBwZWRpZG9zKSBjcmVjZS4gTyBzZSBwdWVkZW4gdGVuZXIgbXVjaG9zIHBlZGlkb3MgbyBuaW5nw7puIHBlZGlkbyBtZXMgY29uIG1lcy4KCiMjIyAqKkJhciBQbG90cy9QaWUgQ2hhcnRzKioKCiMjIyMgKipQcm9kdWNjacOzbioqIApgYGB7cn0KYmRfcHJvZGQ8LXJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL3Byb2R1Y2Npb25fZmluYWwyLmNzdiIpCmBgYAoKCmBgYHtyfQpwaWUoKHByb3AudGFibGUodGFibGUoYmRfcHJvZGQkY2xpZW50KSkpLGNvbD1jKCJsaWdodGdyZWVuIiwibGlnaHR5ZWxsb3ciLCJsaWdodGJsdWUiLCJwaW5rIiksbWFpbj0iQ2xpZW50ZXMiLGxhcz0xKQpgYGAKCkVuIGxhIHByb2R1Y2Npw7NuIGFuYWxpemFtb3MgbG9zIHByaW5jaXBhbGVzIGNsaWVudGVzIGRlIEZPUk0sIGVuIGVzdGUgbWlzbW8sIGEgcGFydGlyIGRlIGxvcyBwb3JjZW50YWplcywgY29tcHJlbmRlbW9zICBxdWUgU3RhYmlsdXMgMSB5IFN0YWJpbHVzIDMgY29uc2lzdGUgZW4gZWwgNTEuMSUgZGUgdG9kb3MgbG9zIHBlZGlkb3MgaGVjaG9zIHBvciBsb3MgY2xpZW50ZXMuIFRSTVggdGFtYmnDqW4gaGFjZSB1biBwZWRpZG8gaW1wb3J0YW50ZSBhIEZPUk0sIHJlcHJlc2VudGFuZG8gdW4gMjAuMyUgZW4gbG9zIMO6bHRpbW9zIHRyZXMgbWVzZXMuIEhheSB0cmVzOiBIYW5vbiBTeXN0ZW1zLCBIZWxsYSwgeSBNZXJpZGlhbiBMaWdodHdlaWdodCBxdWUgbm8gY29uZm9ybWFuIG3DoXMgZGVsIDQlLiAgICAKCiMjIyMgKipTQ1JBUCoqIApgYGB7cn0KcG9yY2VudGFqZXMgPC0gYXMubnVtZXJpYyhyb3VuZCgoKHByb3AudGFibGUodGFibGUoc2NyYXBfZmluYWx5YSR1Ymlfb3JpZ2VuKSkpKjEwMCksMikpCmV0aXF1ZXRhcyA8LSBjKCJDYWxpZGFkL0VudHJlZ2EgZGUgUFQiLCJQb3N0LVByb2R1Y2Npw7NuIiwiUHJlLVByb2R1Y2Npw7NuIikKZXRpcXVldGFzIDwtIHBhc3RlKGV0aXF1ZXRhcywgcG9yY2VudGFqZXMpCmV0aXF1ZXRhcyA8LSBwYXN0ZShldGlxdWV0YXMsICIlIiwgc2VwID0gIiIpCgoKcGllKHBvcmNlbnRhamVzLGV0aXF1ZXRhcyxjb2w9YygicGluayIsImxpZ2h0Ymx1ZSIsImxpZ2h0eWVsbG93IiksbWFpbj0iVWJpY2FjacOzbiBkZSBvcmlnZW46IFNjcmFwIiwgeWxhYiA9IkZyZWN1ZW5jaWFzIixsYXM9MSkKYGBgCgpDb21vIHNlIHZpw7MgYW50ZXJpb3JtZW50ZSwgbm90YW1vcyBjb21vIGVsIHNjcmFwIHNlIGVuY3VlbnRyYSBwb3IgbG8gZ2VuZXJhbCAoY2FzaSB0cmVzIGRlIGN1YXRybyB2ZWNlcykgZW4gbGEgcHJlLXByb2R1Y2Npw7NuLCB5IGNhc2kgbnVuY2EgZW4gbGEgcG9zdC1wcm9kdWNjacOzbi4gICAKCiMjIyMgKipEZWxpdmVyeSBQbGFuOiBQcmluY2lwYWxlcyBjbGllbnRlcyoqCkNvbiBsYSBzaWd1aWVudGUgZnVuY2nDs24gcG9kcmVtb3MgaWRlbnRpZmljYXIgbG9zIHByaW5jaXBhbGVzIGNsaWVudGVzIHBhcmEgZ2VuZXJhciB1biBhbsOhbGlzaXMgY29uIGxhIGluZm9ybWFjacOzbiByZWxldmFudGUsIHB1ZXMgaGF5IGNsaWVudGVzIGNvbiBwb2NvcyBvIHNpbiBwZWRpZG9zIHBvciBsbyBxdWUgZ2VuZXJhciB1biBkaWFnbsOzc3RpY28gcmVzcGVjdG8gYSBkZWxpdmVyeSBwbGFuIGNvbiBlc29zIGRhdG9zIG5vIGVzIG5lY2VzYXJpby4gCmBgYHtyfQpnZ3Bsb3QoYmRfZGVscGxhbiwgYWVzKHg9cmVvcmRlcihjbGllbnRlLHBlZGlkb3MpLCB5PXBlZGlkb3MpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSsKICBjb29yZF9mbGlwKCkKYGBgCgojIyMjICoqRGVsaXZlcnkgUGxhbjogVG9wIDYgY2xpZW50ZXMgeSBwcm9tZWRpbyoqICAKCkNvbW8gc2lndWllbnRlIHBhc28gZWxpbWluYXJlbW9zIHRvZG9zIGxvcyBjbGllbnRlcyBxdWUgbm8gbm9zIGludGVyZXNhbiBwYXJhIGVsIGFuw6FsaXNpcyB5IGRlamFyZW1vcyBlbCB0b3AgNiBkZSBjbGllbnRlcyBwYXJhIGVsIGVqZXJjaWNpby4KYGBge3J9CgpiZF9kZWxwbGFuMiA8LSBiZF9kZWxwbGFuCmJkX2RlbHBsYW4yPC1iZF9kZWxwbGFuMltiZF9kZWxwbGFuMiRjbGllbnRlIT0iQUJDIFFVRVJFVEFSTyIsXQpiZF9kZWxwbGFuMjwtYmRfZGVscGxhbjJbYmRfZGVscGxhbjIkY2xpZW50ZSE9IkFOVE9MSU4gQVJURUFHQSIsXSAgCmJkX2RlbHBsYW4yPC1iZF9kZWxwbGFuMltiZF9kZWxwbGFuMiRjbGllbnRlIT0iQU5UT0xJTiBUT0xVQ0EiLF0gCmJkX2RlbHBsYW4yPC1iZF9kZWxwbGFuMltiZF9kZWxwbGFuMiRjbGllbnRlIT0iSVNSSSIsXSAKYmRfZGVscGxhbjI8LWJkX2RlbHBsYW4yW2JkX2RlbHBsYW4yJGNsaWVudGUhPSJTRUdST1ZFIixdIApiZF9kZWxwbGFuMjwtYmRfZGVscGxhbjJbYmRfZGVscGxhbjIkY2xpZW50ZSE9IlNUQiAxIixdIApiZF9kZWxwbGFuMjwtYmRfZGVscGxhbjJbYmRfZGVscGxhbjIkY2xpZW50ZSE9IlVGSSIsXSAKYmRfZGVscGxhbjI8LWJkX2RlbHBsYW4yW2JkX2RlbHBsYW4yJGNsaWVudGUhPSJZRiBRUk8iLF0gCmJkX2RlbHBsYW4yPC1iZF9kZWxwbGFuMltiZF9kZWxwbGFuMiRjbGllbnRlIT0iSU5PQUMgUE9MWVRFQyIsXSAKYmRfZGVscGxhbjI8LWJkX2RlbHBsYW4yW2JkX2RlbHBsYW4yJGNsaWVudGUhPSJIQU5PTiIsXSAKYmRfZGVscGxhbjI8LWJkX2RlbHBsYW4yW2JkX2RlbHBsYW4yJGNsaWVudGUhPSJNRVJJRElBTiIsXSAKYmRfZGVscGxhbjI8LWJkX2RlbHBsYW4yW2JkX2RlbHBsYW4yJGNsaWVudGUhPSJZRiBSQU1PUyIsXSAKYmRfZGVscGxhbjI8LWJkX2RlbHBsYW4yW2JkX2RlbHBsYW4yJGNsaWVudGUhPSJZQU5GRU5HIHNtIixdIApgYGAKCgpTZSBjYW1iaWFuIGxhIGZlY2hhIGEgdW4gZm9ybWF0byBxdWUgcGVybWl0ZSB2ZXIgbWVqb3IgZWwgYW7DoWxpc2lzIGVuIGxhcyB0YWJsYXMgKHNlIHNvYnJlcG9uw61hbikuCgpgYGB7cn0KYmRfZGVscGxhbjU8LWJkX2RlbHBsYW4yCmJkX2RlbHBsYW41JGZlY2hhW2JkX2RlbHBsYW41JGZlY2hhID09ICJqYW4tMjAyMiJdIDwtICJqYW4yMiIKYmRfZGVscGxhbjUkZmVjaGFbYmRfZGVscGxhbjUkZmVjaGEgPT0gImZlYi0yMDIyIl0gPC0gImZlYjIyIgpiZF9kZWxwbGFuNSRmZWNoYVtiZF9kZWxwbGFuNSRmZWNoYSA9PSAibWFyLTIwMjIiXSA8LSAibWFyMjIiCmJkX2RlbHBsYW41JGZlY2hhW2JkX2RlbHBsYW41JGZlY2hhID09ICJhYnItMjAyMiJdIDwtICJhYnIyMiIKYmRfZGVscGxhbjUkZmVjaGFbYmRfZGVscGxhbjUkZmVjaGEgPT0gIm1heS0yMDIyIl0gPC0gIm1heTIyIgpiZF9kZWxwbGFuNSRmZWNoYVtiZF9kZWxwbGFuNSRmZWNoYSA9PSAianVuLTIwMjIiXSA8LSAianVuMjIiCmJkX2RlbHBsYW41JGZlY2hhW2JkX2RlbHBsYW41JGZlY2hhID09ICJqdWwtMjAyMiJdIDwtICJqdWwyMiIKYmRfZGVscGxhbjUkZmVjaGFbYmRfZGVscGxhbjUkZmVjaGEgPT0gImFnLTIwMjIiXSA8LSAiYWcyMiIKYmRfZGVscGxhbjUkZmVjaGFbYmRfZGVscGxhbjUkZmVjaGEgPT0gInNlcC0yMDIyIl0gPC0gInNlcDIyIgpiZF9kZWxwbGFuNSRmZWNoYVtiZF9kZWxwbGFuNSRmZWNoYSA9PSAib2N0LTIwMjIiXSA8LSAib2N0MjIiCmJkX2RlbHBsYW41JGZlY2hhW2JkX2RlbHBsYW41JGZlY2hhID09ICJub3YtMjAyMiJdIDwtICJub3YyMiIKYmRfZGVscGxhbjUkZmVjaGFbYmRfZGVscGxhbjUkZmVjaGEgPT0gImRpYy0yMDIyIl0gPC0gImRpYzIyIgpgYGAKCipncsOhZmljYSBjb24gMyB2YXJpYWJsZXMqCmBgYHtyfQpnZ3Bsb3QoYmRfZGVscGxhbjUsYWVzKHg9ZmVjaGEsIHk9cGVkaWRvcyxmaWxsPWNsaWVudGUpKSsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0zMyxsaW5ldHlwZT0iZGFzaGVkIixjb2xvcj0iYmxhY2siKSsKICBsYWJzKHg9IkZlY2hhIix5PSJOw7ptZXJvIGRlIHBlZGlkb3MiLCBjb2xvcj0iTGVnZW5kIikrCiAgZ2d0aXRsZSgiUGVkaWRvcyBwb3IgZmVjaGEiKQoKYGBgCgpFbiBlc3RhIGdyw6FmaWNhIHF1ZSB1bmUgdHJlcyB2YXJpYWJsZXMgZGVsICpkZWxpdmVyeSBwbGFuKiwgb2JzZXJ2YW1vcyBsYSBjYW50aWRhZCBkZSBwZWRpZG9zLCBwb3IgY2xpZW50ZSwgcG9yIGZlY2hhLiBFbiBlc3RhIHZlbW9zIGNvbW8gZW4gdG9kb3MgbG9zIG1lc2VzIChtZW5vcyBvY3QsIG5vdiB5IGRpYykgSGVsbGEgaGEgZXN0YWRvIG11eSAgcHJlc2VudGUgY29tbyBjbGllbnRlLCBwb3IgbG8gZ2VuZXJhbCBzdXBlcmFuZG8gbG9zIDEwLDAwMCwgaGFzdGEgY2FzaSA3MCwwMDAgcGVkaWRvcy4gWUZUTyBlcyBlbCBjbGllbnRlLCBkZSBlc3RlICpwb29sKiBxdWUgdGllbmUgbWVub3MgcGVkaWRvcyBhIHRyYXbDqXMgZGUgbG9zIG1lc2VzLiAKCiMjIyMgKipSZWN1cnNvcyBIdW1hbm9zOiBDb2xhYm9yYWRvcmVzIGFjdHVhbGVzKioKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNhcy5kYXRhLmZyYW1lKGJkX2JhamFzKQojYXMuZGF0YS5mcmFtZShiZF9jb2xhYikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpiZF9jb2xhYjI8LWJkX2NvbGFiICU+JSBkcGx5cjo6c2VsZWN0KGdlbmVybyxlZGFkLHNhbGFyaW9fZGlhcmlvKSAlPiUgIGRwbHlyOjpncm91cF9ieShnZW5lcm8pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoYWNyb3NzKGV2ZXJ5dGhpbmcoKSxtZWFuLG5hLnJtPVRSVUUpKSAlPiUgYXJyYW5nZShkZXNjKGVkYWQpKQpnZ3Bsb3QoYmRfY29sYWIyLCBhZXMoeD1yZW9yZGVyKGdlbmVybyxlZGFkKSwgeT1lZGFkLCBmaWxsPShzYWxhcmlvX2RpYXJpbykpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLGNvbD1jKCJibGFjayIpKSsKICBjb29yZF9mbGlwKCkrCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHJldmVyc2U9RkFMU0UpKQpgYGAKCkxhIHByaW1lcmEgZ3LDoWZpY2EgZGUgYmFycmFzIG5vcyBkYSBhIGNvbm9jZXIgc29icmUgZWwgc2FsYXJpbyBkaWFyaW8gZGUgbGFzIHBlcnNvbmFzIGRlIEZvcm0sIHNlZ21lbnRhZG8gcG9yIGVkYWQgeSBwb3IgZ8OpbmVyby4gTG8gcXVlIG5vcyBkYSBhIGNvbm9jZXIgZXMgcXVlIGVuIGxhIGVtcHJlc2EgZXhpc3RlIHVuIG1heW9yIHJhbmdvIGRlIGVkYWQgZW50cmUgbGFzIG11amVyZXMgZGUgRm9ybSwgbGxlZ2FuZG8gYSBzZXIgY2FzaSA0MCBsbyBtYXlvcjsgcG9yIG90cmEgcGFydGUsIGxvcyAgaG9tYnJlcyBwcmVzZW50YW4gdW4gbWVub3IgcmFuZ28sIGNvbiB1biBtw6F4aW1vIGRlIChhcHJveCkgMzMuIElndWFsbWVudGUsIHZlbW9zIHF1ZSBlbiBwcm9tZWRpbyBsYXMgbXVqZXJlcyBnYW5hbiA2MCBjZW50YXZvcyBtw6FzIHF1ZSBsb3MgaG9tYnJlcy4gICAgICAKCmBgYHtyfQpiZF9jb2xhYjM8LWJkX2NvbGFiCmJkX2NvbGFiMyRnZW5lcm9bYmRfY29sYWIkZ2VuZXJvID09ICJGRU1FTklOTyJdIDwtICJGIgpiZF9jb2xhYjMkZ2VuZXJvW2JkX2NvbGFiJGdlbmVybyA9PSAiTUFTQ1VMSU5PIl0gPC0gIk0iCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoYmRfY29sYWIzLCBhZXMoeD1nZW5lcm8sIHk9c2FsYXJpb19kaWFyaW8sIGZpbGw9Z2VuZXJvKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsgCiAgZmFjZXRfZ3JpZCh+Y2l2aWwpICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikKYGBgCgpFc3RhIHNlZ3VuZGEgZ3LDoWZpY2EgZGVzY3JpcHRpdmEgZGUgbG9zIGFjdHVhbGVzIGNvbGFib3JhZG9yZXMgIGRlIEZvcm0sIG5vcyBjb211bmljYSwgcHJpbmNpcGFsbWVudGUsIGVsIGVzdGFkbyBjaXZpbCBkZSBsb3MgY29sYWJvcmFkb3JlcyAgcG9yIGfDqW5lcm8sIHkgc3UgZ2FuYW5jaWEuIExvIHF1ZSBwb2RlbW9zIG5vdGFyIGVzIHF1ZSBlbiBnZW5lcmFsLCBsYXMgbXVqZXJlcyBnYW5hbiBtw6FzIGVuIGN1YWxxdWllciBlc3RhZG8gY2l2aWwgZW4gZWwgcXVlIHNlIGVuY3VlbnRyZW4sIHkgZW4gZG9uZGUgc2UgcHVlZGUgdmVyIHVuYSBtYXlvciB2YXJpZWRhZCBkZSBsYSBzdW1hIGRlIGxvcyBzYWxhcmlvcyBlcyBlbiBsYXMgbXVqZXJlcyAgeSBsb3MgaG9tYnJlcyBxdWUgc2UgZW5jdWVudHJhbiBlbiB1bmnDs24gbGlicmUsIHlhIHF1ZSBsYXMgbXVqZXJlcyBlbiB1bmnDs24gbGlicmUgc3VtYW4gY2FzaSBlbCBkb2JsZSBxdWUgbG9zIGhvbWJyZXMgZW4gdW5pw7NuIGxpYnJlLiAgCgojIyMjICoqUmVjdXJzb3MgSHVtYW5vczogQmFqYXMqKgoKYGBge3J9CmJkX2JhamFzMjwtYmRfYmFqYXMgJT4lIGRwbHlyOjpzZWxlY3QobW90aXZvX2JhamEsZWRhZCxkdXJhY2lvbikgJT4lIGdyb3VwX2J5KG1vdGl2b19iYWphKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGFjcm9zcyhldmVyeXRoaW5nKCksbWVhbixuYS5ybT1UUlVFKSkgJT4lIGFycmFuZ2UoZGVzYyhlZGFkKSkKZ2dwbG90KGJkX2JhamFzMiwgYWVzKHg9cmVvcmRlcihtb3Rpdm9fYmFqYSxlZGFkKSwgeT1lZGFkLCBmaWxsPShkdXJhY2lvbikpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLGNvbD1jKCJibGFjayIpKSsKICBjb29yZF9mbGlwKCkrCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHJldmVyc2U9RkFMU0UpKQpgYGAKClZpZW5kbyBhaG9yYSBsb3MgY29sYWJvcmFkb3JlcyBxdWUgaGFuIHNpZG8gZGFkb3MgZGUgYmFqYSBlbiBsYSBlbXByZXNhLCB2ZW1vcyBxdWUgbGEgZ3JhbiBwYXJ0ZSBkZSBsb3MgcXVlIHNlIGhhbiBzYWxpZG8gZGUgRm9ybSB0aWVuZW4gbWVub3MgZGUgMzAgYcOxb3MuIFBvY29zIGNhc29zIGhhbiBzaWRvIGRlIGp1YmlsYWNpw7NuLCBsbGVnYW5kbyBhIG3DoXMgZGUgNjAgZW4gcmFuZ28uIElndWFsbWVudGUsIGEgcGFydGlyIGRlIGxhIGdyw6FmaWNhIHZlbW9zIHF1ZSBsYXMgcmF6b25lcyBwcmluY2lwYWxlcyBzb24gcG9yIGFiYW5kb25vIHkgIGJhamEgcG9yIGZhbHRhczsgZHVyYW5kbyBtZW5vcyBkZSA1MDAgZMOtYXMgZW4gc3UgdHJhYmFqby4gCgpgYGB7cn0KZ2dwbG90KGJkX2JhamFzLCBhZXMoeD1nZW5lcm8sIHk9c2FsYXJpb19kaWFyaW8sIGZpbGw9Z2VuZXJvKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsgCiAgZmFjZXRfZ3JpZCh+Y2l2aWwpICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikKYGBgCgpGaW5hbG1lbnRlLCBvYnNlcnZhbW9zIGxhIMO6bHRpbWEgZ3LDoWZpY2EgZGVzY3JpcHRpdmEgZGUgbG9zIGNvbGFib3JhZG9yZXMgcXVlIGhhbiBzaWRvIGRhZG9zIGRlIGJhamEgZGUgRm9ybS4gT2JzZXJ2YW1vcyBxdWUgbGEgbWF5b3IgY2FudGlkYWQgKHNlIGFzdW1lIHBvciBsYSBzdW1hIGRlIHNhbGFyaW8gIGRpYXJpbykgaGFuIHNpZG8gc29sdGVyb3MsIHkgIHVuYSBncmFuIGNhbnRpZGFkIGRlIG11amVyZXMgY2FzYWRhcy4gUG9yIGxvIHRhbnRvLCBwb2RlbW9zIGFzdW1pciBhIHBhcnRpciBkZSBsb3MgZGF0b3MgcXVlIGxhIG1heW9yw61hIGRlIGxvcyBxdWUgYWJhbmRvbmFuIHN1IHRyYWJham8gZW4gRm9ybSBzb24gcGVyc29uYXMgc29sdGVyYXMuIFBvciBvdHJhIHBhcnRlLCB2ZW1vcyBxdWUgbGFzIG11amVyZXMgZW4gbWF0cmltaW5pbyBzdW1hbiBtw6FzIGdhbmFuY2lhcyBxdWUgbG9zIGhvbWJyZXMgZW4gbWF0cmltb25pby4gTGFzIG11amVyZXMsIGVuIHRvZGFzIGxvcyBlc3RhZG9zIGNpdmlsZXMsIGhhbiBnYW5hZG8gbcOhczsgZXN0dG8gcHVkaWVyYSB2ZXJzZSB0YW1iacOpbiBkZWJpZG8gYSBxdWUgbcOhcyBkZWwgNTAlIGRlIGxhcyBwZXJzb25hcyBlbiBlc3RhIGJhc2UgZGVlIGRhdG9zIHNvbiBtdWplcmVzLiAgCgojIyMjICoqTWVybWEqKgpgYGB7cn0KZ2dwbG90KGJkX21lcm1hX2YsIGFlcyh4PSBNZXMsIHk9IEtpbG9zKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGw9InJlZCIpICsgc2NhbGVfZmlsbF9ncmV5KCkgKyBsYWJzKHRpdGxlID0gIktpbG9zIGRlIG1lcm1hIiwgeCA9ICJGZWNoYSIpCmBgYAoKRW4gZXN0YSBncsOhZmljYSBwb2RlbW9zIG9ic2VydmFyIGxvcyBtZXNlcyBxdWUgc3VwZXJhbiBlbCBwcm9tZWRpbyBvIGxvICJub3JtYWwiIGRlIGxvcyBraWxvcyBkZSBtZXJtYTsgZW4gZXN0ZSBjYXNvIHZlbW9zIHF1ZSBlbCBtZXMgZGUgYWdvb3N0byBkZXN0YWNhIGNvbW8gYXF1ZWwgY29uIG3DoXMga2lsb3MsIG1pZW50cmFzIHF1ZSBzZXB0aWVtYnJlIGVzIGVsIHF1ZSBkZW11ZXN0cmEgdGVlbmVyIG1lbm9zLiBDb21wYXJhbW9zIGxvIG3DoXMgZGUgMzAsMDAwIGtpbG9zIGVuIGFnb3N0byBjb24gbG9zIGNhc2kgMTUsMDAwLCBwb3IgbG8gcXVlIGVzIGNhc2kgZWwgZG9ibGUuICAKCiMjIyMgKipTY3JhcCoqIApgYGB7cn0KaGlzdChzY3JhcF9maW5hbHlhJGNhbnRpZGFkLCBtYWluID0gIkNhbnRpZGFkIGRlIE1hdGVyaWFsIFJlY2ljbGFkbyIsIHhsYWIgPSAiQ2FudGlkYWQiLCB5bGFiID0gIkZyZWN1ZW5jaWEiLGNvbCA9ICJibHVlIikKYGBgCgpFc3RlIGhpc3RvZ3JhbWEgbm9zIG11ZXN0cmEgcXVlIGVudHJlIDAgYSAxMCB0b25lbGFkYXMgZGUgInNjcmFwIiBlcyBsbyBxdWUgbWFzIHNlIGZyZWN1ZW50YSBhIHJlY2ljbGFyIGVsIG1hdGVyaWFsLiBTZSBvYnNlcnZhbiBtdXkgcG9jb3MgY2Fzb3MgcXVlIHN1cGVyYW4gbG9zIDIwLiAgCgoKCiMjIyAqKkRpc3BlcnNpb24gUGxvdHMqKgoKCiMjIyMgKipTY3JhcCoqCmBgYHtyfQpwbG90KHNjcmFwX2ZpbmFseWEkZmVjaGEsIHNjcmFwX2ZpbmFseWEkY2FudGlkYWQsIG1haW4gPSAiQ2FudGlkYWQgZGUgU2NyYXAgcG9yIGZlY2hhIiwgeGxhYiA9ICJGZWNoYSIsIHlsYWIgPSAiQ2FudGlkYWQiKQpgYGAKClZpZW5kbyBlbCBtZXMgZGUgYWdvc3RvIHBhcmEgZWwgInNjcmFwIiwgb2JzZXJldmFtb3MgcXVlIGxhIG1heW9yw61hIGRlIGxvcyBkw61hcyBtYW5lamFuIG1lbm9zIGRlIDIwIHVuaWRhZGVzIGRlIG1hdGVyaWFsLCBzaW4gZW1iYXJnbywgZXhpc3RlbiBhbGd1bm9zIGNhc29zIGVuIHF1ZSBzZSBzYWxlIGRlIGxvICJub3JtYWwiIGNvbW8gdmVtb3MgYXByb3hpbWFkYW1lbnRlIGVsIGTDrWEgMjQgZGUgYWdvc3RvLCBlbiBkb25kZSBsYSBjYW50aWRhZCBkZSBzY3JhcCBzdXBlcsOzIGVsIG7Dum1lcm8gODAuICAgIAoKIyMjIyAqKkRlbGl2ZXJ5IFBsYW4qKgoKKioqUGVkaWRvcyoqKgpgYGB7cn0KcGxvdChiZF9kZWxwbGFuNSRJRF9mZWNoYSwgYmRfZGVscGxhbjUkcGVkaWRvcywgbWFpbiA9ICJQZWRpZG9zIHBvciBuw7ptZXJvIGRlIG1lcyIsCiAgICAgeGxhYiA9ICJNZXMiLCB5bGFiID0gIlBlZGlkb3MiLGNvbD1jKCJvcmFuZ2UiKSwKICAgICBwY2ggPSAxLCBmcmFtZSA9IEZBTFNFKQpgYGAKClRlbmllbmRvIGxvcyBtZXNlcyBjb21vIG7Dum1lcm9zIGRlbCBhw7FvLCB2ZW1vcyBjb21vIGxhIG1heW9yw61hIGRlIGxvcyBtZXNlcyBjdWVudGFuIGNvbiBwZWRpZG9zIGRlIG1lbm9zIGRlIDEwLDAwMDsgc2luIGVtYmFyZ28sIGFsZ3Vub3MgbWVzZXMgcHJlc2VudGFuIHBlZGlkb3MgImV4dHJhb3JkaW5hcmlvcyIsIGNvbW8gbG8gZnVlIGp1bm8geSAgYWdvc3RvLCBxdWUgZXN0dXZpZXJvbiBlbnRyZW4gNDAsMDAwIHkgNTAsMDAwLiAKCmBgYHtyfQpib3hwbG90KGJkX2RlbHBsYW41JHBlZGlkb3MsIG1haW4gPSAiUGVkaWRvcyIsY29sPWMoIm9yYW5nZSIpKQpgYGAKCkNvbW8gcHJpbWVyIGJveHBsb3QgdmVtb3MgbG9zIHBlZGlkb3MgeSBsYSBncmFuIGRpc3BlcnNpw7NuIHF1ZSBoYXkgZW50cmUgcGVkaWRvcywgcHVlcyBkZXRlY3RhbW9zIHVuYSBmcmVjdWVuY2lhIGVuIGRvbmRlIGhheSBjbGllbnRlcyBxdWUgZ2VuZXJhbiBwZWRpZG9zIG9idmlhbWVudGUgZGlmZXJlbnRlcyBjb21vIHBhcmEgdGVuZXIgdW5hIG1lZGlhbmEgbyB1bmEgZGlzcGVyc2nDs24gcG9zaXRpdmEuIEVzdG8gbWlzbW8gbG8gcHVkaW1vcyB2ZXIgYW50ZXJpb3JtZW50ZSwgY29tbyBsYSBtYXlvcsOtYSBkZSBsb3MgcGVkaWRvcyBubyBzdXBlcmFuIGxvcyAxMCwwMDAsIHkgcXVlIGV4aXN0ZW4gY2Fzb3MgImV4dHJhb3JkaW5hcmlvcyIgcXVlIHZhbiBtdXkgcG9yIGFycmliYSBkZSBlc3RlIG7Dum1lcm8uIAoKKioqQ2xpZW50ZXMqKioKCkRlc3B1w6lzIGRlIGdlbmVyYXIgdW4gYm94cGxvdCBkZSBwZWRpZG9zIGVuIGdlbmVyYWwsIHJlYWxpemFtb3MgdW4gYm94cGxvdCBxdWUgbm9zIG11ZXN0cmEgbG9zIHBlZGlkb3MgcG9yIGNsaWVudGUKYGBge3J9CmJkX2RlbHBsYW40IDwtIGJkX2RlbHBsYW41CmJkX2RlbHBsYW40JGNsaWVudGU8LWFzLmZhY3RvcihiZF9kZWxwbGFuNCRjbGllbnRlKQpnZ3Bsb3QoYmRfZGVscGxhbjQsIGFlcyh4PWNsaWVudGUsIHk9cGVkaWRvcykpICsgCiAgZ2VvbV9ib3hwbG90KGNvbG9yPSJyZWQiLCBmaWxsPSJvcmFuZ2UiLCBhbHBoYT0wLjIpCgpgYGAKCkVuIGVsIGdyw6FmaWNvIGFudGVyaW9yIHBvZGVtb3Mgb2JzZXJ2YXIgYSBsb3MgNiBjbGllbnRlcyBjb24gbWF5b3IgcHJlc2VuY2lhIGVuIEZPUk0gZW4gY3VhbnRvIGEgRGVsaXZlcnkgUGxhbiBlbiBkb25kZSBlbCBvYmpldGl2byBlcyB2ZXIgbGEgZGlzcGVyc2nDs24geSBsYSBkaXN0cmlidWNpw7NuIGVudHJlIGNhZGEgdW5vIHkgcmVzcGVjdG8gYSBsb3MgcHJvbsOzc3RpY29zIGluZGl2aWR1YWxlcy4gICAgICAKCkVsIGNsaWVudGUgSEVMTEEgZXMgZWwgY2xpZW50ZSBjb24gdW5hIG1heW9yIGRpc3RyaWJ1Y2nDs24geSBkaXNwZXJzacOzbi4gVmFycm9jIHkgVFJNWCB0aWVuZW4gdGFtYmnDqW4gdW5hIGRpc3RyaWJ1Y2nDs24gbWF5b3IgYWwgdGVuZXIgbGEgYm94cGxvdCBtw6FzIGdyYW5kZSwgbWllbnRyYXMgcXVlIGxvcyBkZW3DoXM7IERFTlNPLCBTVEIzIHkgWUZUTyB0aWVuZW4gbGEgbWF5b3LDrWEgZGUgc3VzIHBlZGlkb3MgZW4gdW4gbWlzbW8gcmFuZ28gY29uIHBlcXVlw7FvcyBkYXRvcyBmdWVyYSBkZWwgYm94cGxvdCBwcmVzZW50YW5kbyBhbGd1bm9zIHdhcm5pbmdzLiAgICAgCgpTZSBwdWVkZSBjb25jbHVpciwgcXVlIEhFTExBIGVzIGVsIGNsaWVudGUgbcOhcyBmdWVydGUgZW4gZGljaGEgYmFzZSBkZSBkYXRvcyBwYXJhIGxhIGVtcHJlc2EsIHBlcm8gcXVlIHN1cyBwZWRpZG9zIHZhcsOnaWFuIGRlIGNhbnRpZGFkLiAgICAgICAKCgoKIyMjICoqVGltZSBzZXJpZXMgcGxvdHMqKiAKCmBgYHtyfQpkcF9kYXRhPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9Eb3dubG9hZHMvc3R1ZGVudF9kcF9mb3JtLmNzdiIpCnN1bW1hcnkoZHBfZGF0YSkKYGBgCgoKYGBge3J9CnN0cihkcF9kYXRhKQpkcF9kYXRhJGZlY2hhPC1hcy5EYXRlKGRwX2RhdGEkZmVjaGEsZm9ybWF0PSIlbS8lZC8lWSIpIApzdHIoZHBfZGF0YSkKYGBgCgoKCgoKCiMjIyMgKipEZWxpdmVyeSBwZXJmb3JtYW5jZTogZGlzdHJpYnVjacOzbioqIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZHBfZGF0YSxhZXMoeD1mZWNoYSwgeT1kZWxheV9wZXJmb3JtYW5jZSxjb2xvcj1jbGllbnRlKSkrCiAgZ2VvbV9saW5lKCkrCiAgbGFicyh4PSJGZWNoYSIseT0iUmV0cmFzbyBlbiBNaW51dG9zIiwgY29sb3I9IkxlZ2VuZCIpKwogIGdndGl0bGUoIlJldHJhc29zIGVuIGVsIGRlc2VtcGXDsW8sIHBvciB0aWVtcG8iKQpgYGAKCkEgcGFydGlyIGRlIGVzdGEgZ3LDoWZpY2EsIGNvbXBhcmFtb3MgYSBsb3MgY2xpZW50ZXMsIHkgY29tbyBoYSBzaWRvIGVsIHJldHJhc28gZW4gbWludXRvcyBlbiBjdWFudHRvIGEgbGFzIGVudHJlZ2FzLiBWZW1vcyBjb21vIGxhIHF1ZSBzZSBtYW50aWVuZSBjb25zdGFudGUgZXMgVmFycm9jLCBwdWVzdG8gcXVlIG5vIGhhIHZpc3RvIG11Y2hvIGRpZmVyZW5jaWEgbmkgImRlbGF5Ii4gU2luIGVtYmFyZ28sIGVuIGVsIGNhc28gZGUgTWFobGUgdmVtb3MgcmV0cmFzb3MgZHVyYW5ldHQgdG9kbyBlbCBhw7FvLCBwZXJvIHF1ZSBkdXJhbnRlIGxvcyDDumx0aW1vcyBtZXNlcyBoYSBwb2RpZG8gYmFqYXIuIEVsIGNhc28gZGUgUHJpbnRlbCwgdmVtb3MgY29tbyAgc3VzIHJldHJhc29zIG5vIGV4Y2VkZW4gbGEgbWVkaWEgaG9yYSwgcGVybyBxdWUgbm8gaGFuIGhhYmlkbyBpbmlkaWNpb3MgZGUgcmV0cmFzb3MgZGVzZGUgYWJyaWwuICAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRwX2RhdGE8LWRwX2RhdGFbZHBfZGF0YSRjbGllbnRlIT0iTWFnbmEiLF0KZHBfZGF0YTwtZHBfZGF0YVtkcF9kYXRhJGNsaWVudGUhPSJWYXJyb2MiLF0KCmdncGxvdChkcF9kYXRhLGFlcyh4PWZlY2hhLCB5PWRlbGF5X3BlcmZvcm1hbmNlLGZpbGw9Y2xpZW50ZSkpKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTMzLGxpbmV0eXBlPSJkYXNoZWQiLGNvbG9yPSJibGFjayIpKwogIGxhYnMoeD0iRmVjaGEiLHk9IlJldHJhc28gZW4gbWludXRvcyIsIGNvbG9yPSJMZWdlbmQiKSsKICBnZ3RpdGxlKCJSZXRyYXNvcyBlbiBlbCBkZXNlbXBlw7FvIHBvciBjbGllbnRlIikKYGBgCgpBIHBlc2FyIGRlIHF1ZSBlc3RhIGRpc21pbnV5ZW5kbyBlbCByZXRyYXNvIGNvbiBlbCBjbGllbnRlIE1haGxlIHNlZ3VpbW9zIHZpZW5kbyBxdWUgc2UgZW5jdWVudHJhIHBvciBhcnJpYmEgZGVsIHByb21lZGlvLCB5IHF1ZSBlcyBtdWNobyBtYXlvciBxdWUgYXF1ZWxsb3MgcmV0cmFzb3MgcHJlc2VudGFkb3MgY29uIFByaW50ZWwuICAgICAgCgoKIyMgKipQcmVkaWNjacOzbiBkZWwgRGVzZW1wZcOxbyBkZSBsYSBJbmR1c3RyaWEgQXV0b21vdHJpeioqCgojIyMgKipFc3RhZG9zIFVuaWRvcyoqCgpgYGB7cn0KYmFzZV9ldWE8LXJlYWQuY3N2KCIvVXNlcnMvZWxlbmF2ZWxhL0Rvd25sb2Fkcy91c19tb3Rvcl9wcm9kdWN0aW9uX2FuZF9kb21lc3RpY19zYWxlcy5jc3YiKQpzdW1tYXJ5KGJhc2VfZXVhKQpgYGAKCgpTZSBjYW1iaWFuIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcy4gCmBgYHtyfQpuYW1lcyAoYmFzZV9ldWEpID0gYygiYcOxbyIsICJwcm9kX3RvdGFsIiwgInByb2RfcGFzc2VuZ2VyIiwgInByb2RfdmVoX2NvbWVyY2lhbGVzIiwgInZlbnRhc19kb21lc3RpY2FzIiwgInZlbnRhc19wYXNzZW5nZXIiLCJ2ZW50YXNfY29tZXJjaWFsZXMiLCJkZXNlbXBsZW9fdXNhIiwiY29uZmlhbnphX2NvbnNfdXNhIiwic2FsYXJpb19ob3JhX21pbl91c2EiKQpuYW1lcyAoYmFzZV9ldWEpCnN0cihiYXNlX2V1YSkKYGBgCgojIyMjICoqUHJpbWVyIG1vZGVsbyBkZSByZWdyZXNpw7NuKioKRW4gZXN0ZSBjYXNvLCBzZSBlc2NvZ2UgY29tbyB2YXJpYWJsZSBkZXBlbmRpZW50ZSBsYXMgdmVudGFzIGRlIGxvcyBjYXJyb3MgKnBhc3NlbmdlciosIHBvciBlbGxvIGVudGnDqW5kYXNlIGxvcyBhdXRvbcOzdmlsZXMgZGUgdXNvIGNvdGlkaWFubyBlbiBFc3RhZG9zIFVuaWRvcy4gUGFyYSBlc3RhIHZhcmlhYmxlIGRlcGVuZGllbnRlLCBzZSBoYW4gdG9tYWRvIGxhcyBzaWd1aWVudGVzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcywgY29uIGVsIGZpbiBkZSBub3RhciBzdSBlZmVjdG8gZW4gbGFzIHZlbnRhcy4gRXN0YXMgc29uOiAgCgoqKjEuIERlc2VtcGxlbyBVU0E6KiogZXN0ZSDDrW5kaWNlIGVzIGNhbGN1bGFkbyBhbnVhbG1lbnRlIGNvbiBsYSBmb3JtdWxhLiAoVW5lbXBsb3llZCDDtyBMYWJvciBGb3JjZSkgeCAxMDAuIEVudHJlIG1lbm9yIG1lam9yLiBTZSBtaWRlIGVuIHBvcmNlbnRhamUuICAgCgoqKjIuIENvbmZpYW56YSBkZWwgY29uc3VtaWRvciBkZSBVU0E6Kiogw61uZGljZSBxdWUgbWlkZSwgYSBwYXJ0aXIgZGUgdW5hIGVuY3Vlc3RhIHF1ZSB0YW4gb3B0aW1pc3RhcyBvIHBlc2ltaXN0YXMgc2UgZW5jdWVudHJhbiBsb3MgY29uc3VtaWRvcmVzIHNvYnJlIHN1IHNpdHVhY2nDs24gZmluYW5jaWVyYS4gRW50cmUgbWF5b3IsIG1lam9yOyBzZSBtaWRlIHBvciBwdW50b3MuICAKCioqMy4gU2FsYXJpbyBtw61uaW1vIHBvciBob3JhOioqIHNlIG1pZGUgZW4gZMOzbGFyZXMuIEVzdMOhIGVzdGFibGVjaWRvIGEgbml2ZWwgZmVkZXJhbC4gCgoqKjQuIEHDsW86KiogbG9zIGHDsW9zIHF1ZSBzZSB0aWVuZW4gZGUgbG9zIGRhdG9zLCAyMDA3LTIwMjAuIAoKCmBgYHtyfQpyZWdyZXNpb24xIDwtIGxtICh2ZW50YXNfcGFzc2VuZ2VyIH4gZGVzZW1wbGVvX3VzYSArIGNvbmZpYW56YV9jb25zX3VzYSArIHNhbGFyaW9faG9yYV9taW5fdXNhICsgYcOxbywgZGF0YT1iYXNlX2V1YSkKc3VtbWFyeSAocmVncmVzaW9uMSkKYGBgCgoqUGFyYSBxdWUgdW5hIHZhcmlhYmxlIHRlbmdhIHNpZ25pZmljYW5jaWEgZW4gbGEgcmVncmVzacOzbiwgc2UgZGVzZWEgcXVlIHA8MC4wNSogICAKCkFsIGNvcnJlciBsYSByZWdyZXNpw7NuIGxhcyB2ZW50YXMgZGUgbG9zIGF1dG9zIHRpcG8gKnBhc3NlbmdlciosIG9ic2VydmFtb3MgcXVlIGV4aXN0ZSB1bmEgdmFyaWFibGUgY29uIG1heW9yIHNpZ25pZmljYW5jaWEsIGxhIGN1YWwgZXMgImHDsW8iLiBTZSBoYSBkZWNpZGlkbyBxdWl0YXIgbGEgdmFyaWFibGUgZGUgImNvbmZpYW56YV9jb25zX3VzYSIgcGFyYSB2ZXIgbGEgbnVldmEgc2lnbmlmaWNhbmNpYSBkZWwgbW9kZWxvOgoKYGBge3J9CnJlZ3Jlc2lvbjFfMiA8LSBsbSAodmVudGFzX3Bhc3NlbmdlciB+IGRlc2VtcGxlb191c2EgKyBzYWxhcmlvX2hvcmFfbWluX3VzYSArIGHDsW8sIGRhdGE9YmFzZV9ldWEpCnN1bW1hcnkgKHJlZ3Jlc2lvbjFfMikKYGBgCgpBaG9yYSwgdmVtb3MgcXVlIGxhcyB0cmVzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyBzaSBkZW11ZXN0cmFuIHNpZ25pZmljYW5jaWEgcHVlc28gcXVlIGxhIHAgZXMgbWVub3IgcXVlIDAuMDUuIFNlIHV0aWxpemFyw6EgbWVqb3IgIGVzdGUgbW9kZWxvIHBhcmEgbGEgcHJlZGljY2nDs24gZGUgdmVudGFzIGRlIGF1dG9zIHRpcG8gKnBhYXNzZW5nZXIqLiAgCgpQYXJhIGxhIHRhc2EgZGUgZGVzZW1wbGVvIHNlIGhhIGRlY2lkaWRvIHRvbWFyIGVsICpwcm9tZWRpbyogZGVsIDIwMDcgYWwgMjAyMCwgcGFyYSBwb2RlciBwcmVkZWNpci4gQ29uIGVsIHNhbGFyaW8gbcOtbmltbyBwb3IgaG9yYSwgYWwgc2VyIGVzdGFibGVjaWRvIGEgbml2ZWwgZmVkZXJhbCwgc2UgdG9tYSBlbiBjdWVudGEgbGEgw7psdGltYSBjYW50aWRhZDogJDcuMjUgZMOzbGFyZXMuIFNlIHF1aWVyZW4gbGFzIHByZWRpY2Npb25lcyBwYXJhIGxvcyBzaWd1aWVudGVzIGNpbmNvIGHDsW9zLCBhIHBhcnRpciBkZWwgw7psdGltbyBhw7FvIGNvbiBkYXRvcyAoMjAyMCkuICAKCkEgcGFydGlyIGRlIGVzdGUgbW9kZWxvIGRlIHJlZ3Jlc2nDs24sIHNlIGNvbm9jZSBsbyBzaWd1aWVudGUgYSBwYXJ0aXIgZGVsICplc3RpbWF0ZSo6ICAKMS4gQWwgc3ViaXIgdW5hIHVuaWRhZCBkZWwgw61uZGljZSBkZSBkZXNlbXBsZW8gZGUgRVVBLCBzZSBiYWphbiA0MDAuMTkgZGUgbGFzIHZlbnRhcyBkZSBsb3MgdmVow61jdWxvcyAqcGFzc2VuZ2VyKi4gIAoyLiBBbCBzdWJpciB1bmEgdW5pZGFkIChkw7NsYXIpIGRlbCBzYWxhcmlvIG3DrW5pbW8gcG9yIGhvcmEgZGUgRVVBLCBzZSBpbmNyZW1lbnRhbiBsYXMgdmVudGFzIHBvciAxLDU1NC45NS4gIAozLiBDb24gZWwgcGFzYXIgZGUgY2FkYSBhw7FvLCBzZSBlc3BlcmEgcXVlIGxhcyB2ZW50YXMgZGUgZXN0ZSB0aXBvIGRlIGF1dG8gZGlzbWludXlhbiBwb3IgMjc5LjIyLgoKYGBge3J9CmRhdG9zX251ZXZvczEgPC0gZGF0YS5mcmFtZShkZXNlbXBsZW9fdXNhPTYuNDMsc2FsYXJpb19ob3JhX21pbl91c2E9Ny4yNSxhw7FvPTIwMjE6MjAyNSkKcHJlZGljdChyZWdyZXNpb24xXzIsZGF0b3NfbnVldm9zMSkKYGBgCgoqKsK/UXXDqSBvYnNlcnZhbW9zPyoqCgpgYGB7cn0KcHJpbWVyIDwtIHBsb3QocHJlZGljdChyZWdyZXNpb24xXzIsZGF0b3NfbnVldm9zMSksIHR5cGUgPSAibCIsIHhsYWIgPSAiQcOxbyIsICB5bGFiID0iVmVudGFzIFBhc3NlbmdlciIsICBtYWluPSJQcmVkaWNjacOzbiBkZSBWZW50YXMiKQpgYGAKCkVuIGVzdGEgcHJlZGljY2nDs24gZGUgdmVudGFzIGRlIGF1dG9tw7N2aWxlcyAqcGFzc2VuZ2VyKiwgb2JzZXJ2YW1vcyBxdWUgZXMgaGFjaWEgYWJham8uIEVzIGRlY2lyLCB0b21hbmRvIGVuIGN1ZW50YSB1bmEgdGFzYSBkZSBkZXNlbXBsZW8gIGRlIDYuNDMgeSBlbCBzYWxhcmlvIG3DrW5pbW8gcG9yIGhvcmEgZGUgJDcuMjUsIHNlIGVzcGVyYSBxdWUgbGFzIHZlbnRhcyBkZSBlc3RvcyBhdXRvbcOzdmlsZXMgKipiYWplbioqIGVuIEVVQSBkZWwgMjAyMSBoYXN0YSAyMDI1LiAKCmBgYHtyfQpsaWJyYXJ5KGp0b29scykKYGBgCgoKKirCv0PDs21vIGVzIGVsIGltcGFjdG8gZGUgZGljaGFzIHZhcmlhYmxlcyBleHBsYW5hdG9yaWFzIHNvYnJlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlPyoqCgpgYGB7cn0KZWZmZWN0X3Bsb3QocmVncmVzaW9uMV8yLHByZWQ9ZGVzZW1wbGVvX3VzYSxpbnRlcnZhbD1UUlVFKQpgYGAKClNpIHRvbWFtb3MgZW4gY3VlbnRhIGxhIHRhc2EgZGUgZGVzZW1wbGVvIGRlIEVVQSwgeSBzdSByZWxhY2nDs24gY29uIGxhcyB2ZW50YXMgZGUgbG9zIGF1dG9tw7N2aWxlcyAqcGFzc2VuZ2VyKiwgb2JzZXJ2YW1vcyBxdWUgZXN0YSBlcyBORUdBVElWQS4gRGViaWRvIGEgcXVlIGFsIGNyZWNlciBsYSB0YXNhIGRlIGRlc2VtcGxlbywgYmFqYW4gbGFzIHZlbnRhcy4gCgoKCmBgYHtyfQplZmZlY3RfcGxvdChyZWdyZXNpb24xXzIscHJlZD1zYWxhcmlvX2hvcmFfbWluX3VzYSxpbnRlcnZhbD1UUlVFKQpgYGAKClNpbiBlbWJhcmdvLCBzZSBjb25vY2UgcXVlIGFsIHN1YmlyIGVsIHNhbGFyaW8gbcOtbmltbyBwb3IgaG9yYSwgc2UgZXNwZXJhIHF1ZSBsYXMgdmVudGFzIGRlIGxvcyBhdXRvbcOzdmlsZXMgKnBhc3NlbmdlciogaW5jcmVtZW50YW4uIExhIHJlbGFjacOzbiAKCmBgYHtyfQplZmZlY3RfcGxvdChyZWdyZXNpb24xXzIscHJlZD1hw7FvLGludGVydmFsPVRSVUUpCmBgYAoKQ29uIGVsIHBhc2FyIGRlIGxvcyBhw7FvcyBsYXMgdmVudGFzIGhhbiAqKmRlY3JlY2lkbyoqLCBsYSByZWxhY2nDs24gZXMgTkVHQVRJVi4gCgojIyMjICoqU2VndW5kbyBtb2RlbG8gZGUgcmVncmVzacOzbioqCkVuIGVzdGUgY2Fzbywgc2UgZXNjb2dlIGNvbW8gdmFyaWFibGUgZGVwZW5kaWVudGUgbGFzIHZlbnRhcyBkZSBsb3MgY2Fycm9zICpjb21lcmNpYWxlcyosIHBvciBlbGxvIGVudGnDqW5kYXNlIGxvcyBjdWFscXVpZXIgdGlwbyBkZSB2ZWjDrWN1bG8gZGUgbW90b3IgdXRpbGl6YWRvIHBhcmEgdHJhbnNwb3J0YXIgbWVyY2FuY8OtYXMgbyBwYXNhamVyb3MgZW4gRXN0YWRvcyBVbmlkb3MuIFBhcmEgZXN0YSB2YXJpYWJsZSBkZXBlbmRpZW50ZSwgc2UgaGFuIHRvbWFkbyBsYXMgc2lndWllbnRlcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMsIGNvbiBlbCBmaW4gZGUgbm90YXIgc3UgZWZlY3RvIGVuIGxhcyB2ZW50YXMuIEVzdGFzIHNvbjogIAoqKjEuIERlc2VtcGxlbyBVU0E6KiogZXN0ZSDDrW5kaWNlIGVzIGNhbGN1bGFkbyBhbnVhbG1lbnRlIGNvbiBsYSBmb3JtdWxhLiAoVW5lbXBsb3llZCDDtyBMYWJvciBGb3JjZSkgeCAxMDAuIEVudHJlIG1lbm9yIG1lam9yLiBTZSBtaWRlIGVuIHBvcmNlbnRhamUuICAgCgoqKjIuIENvbmZpYW56YSBkZWwgY29uc3VtaWRvciBkZSBVU0E6Kiogw61uZGljZSBxdWUgbWlkZSwgYSBwYXJ0aXIgZGUgdW5hIGVuY3Vlc3RhIHF1ZSB0YW4gb3B0aW1pc3RhcyBvIHBlc2ltaXN0YXMgc2UgZW5jdWVudHJhbiBsb3MgY29uc3VtaWRvcmVzIHNvYnJlIHN1IHNpdHVhY2nDs24gZmluYW5jaWVyYS4gRW50cmUgbWF5b3IsIG1lam9yOyBzZSBtaWRlIHBvciBwdW50b3MuICAKCioqMy4gU2FsYXJpbyBtw61uaW1vIHBvciBob3JhOioqIHNlIG1pZGUgZW4gZMOzbGFyZXMuIEVzdMOhIGVzdGFibGVjaWRvIGEgbml2ZWwgZmVkZXJhbC4gCgoqKjQuIEHDsW86KiogbG9zIGHDsW9zIHF1ZSBzZSB0aWVuZW4gZGUgbG9zIGRhdG9zLCAyMDA3LTIwMjAuIAoKCmBgYHtyfQpyZWdyZXNpb24yIDwtIGxtICh2ZW50YXNfY29tZXJjaWFsZXMgfiBkZXNlbXBsZW9fdXNhICsgY29uZmlhbnphX2NvbnNfdXNhICsgc2FsYXJpb19ob3JhX21pbl91c2EgKyBhw7FvLCBkYXRhPWJhc2VfZXVhKQpzdW1tYXJ5IChyZWdyZXNpb24yKQpgYGAKQXVucXVlIGVuIGxhIG1heW9yw61hIGRlIGxhcyB2YXJpYWJsZXMgZGVtdWVzdHJhbiBzaWduaWZpY2FuY2lhIGVuIGxhcyB2ZW50YXMgZGUgYXV0b3MgY29tZXJjaWFsZXMsIHNlIGhhIGRlY2lkaWRvICoqZWxpbWluYXIqKiBsYSB2YXJpYWJsZSBkZXBlbmRlbmRpZW50ZSBkZSAiw41uZGljZSBkZSBEZXNlbXBsZW8gZGUgRVVBIiwgZGViaWRvIGEgcXVlIHN1IG5pdmVsIGRlIHAgbm8gZGVtdWVzdHJhIG11Y2hhIHNpZ25pZmljYW5jaWEgKDAuMjApCgpgYGB7cn0KcmVncmVzaW9uMl8yIDwtIGxtICh2ZW50YXNfY29tZXJjaWFsZXMgfiBjb25maWFuemFfY29uc191c2EgKyBzYWxhcmlvX2hvcmFfbWluX3VzYSArIGHDsW8sIGRhdGE9YmFzZV9ldWEpCnN1bW1hcnkgKHJlZ3Jlc2lvbjJfMikKYGBgCgpWaWVuZG8gZXN0ZSBudWV2byBtb2RlbG8gZGUgcmVncmVzacOzbiBwYXJhIGxhcyB2ZW50YXMgZGUgYXV0b3MgY29tZXJjaWFsZXMsIHZlbW9zIHF1ZSBhaG9yYSB0b2RhcyBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIHNlbGVjY2lvbmFkYXMgdGllbmVuIHVuYSBtYXlvciBzaWduaWZpY2FuY2lhLiBFc3RvIG5vcyBkaWNlIGVsIG1vZGVsbzogICAKMS4gQWwgc3ViaXIgdW5hIHVuaWRhZCBkZWwgw61uZGljZSBkZSBjb25maWFuemEgZGVsIGNvbnN1bWlkb3IgZGUgRVVBLCBpbmNyZW1lbnRhIDcxLjg3IGRlIGxhcyB2ZW50YXMgZGUgbG9zIHZlaMOtY3Vsb3MgKmNvbW1lcmNpYWwqLiAgCjIuIEFsIHN1YmlyIHVuYSB1bmlkYWQgKGTDs2xhcikgZGVsIHNhbGFyaW8gbcOtbmltbyBwb3IgaG9yYSBkZSBFVUEsIGRpc21pbnV5ZW4gbGFzIHZlbnRhcyBwb3IgMSwxMTcuOS4gIAozLiBDb24gZWwgcGFzYXIgZGUgY2FkYSBhw7FvLCBzZSBlc3BlcmEgcXVlIGxhcyB2ZW50YXMgZGUgZXN0ZSB0aXBvIGRlIGF1dG8gYXVtZW50YW4gcG9yIDMwNi41LiAgCgoKUGFyYSBlbCDDrW5kaWNlIGRlIGNvbmZpYW56YSBkZWwgY29uc3VtaWRvciwgc2UgaGEgZGVjaWRpZG8gdG9tYXIgZWwgKnByb21lZGlvKiBkZWwgMjAwNyBhbCAyMDIwLCBwYXJhIHBvZGVyIHByZWRlY2lyLiBDb24gZWwgc2FsYXJpbyBtw61uaW1vIHBvciBob3JhLCBhbCBzZXIgZXN0YWJsZWNpZG8gYSBuaXZlbCBmZWRlcmFsLCBzZSB0b21hIGVuIGN1ZW50YSBsYSDDumx0aW1hIGNhbnRpZGFkOiAkNy4yNSBkw7NsYXJlcy4gU2UgcXVpZXJlbiBsYXMgcHJlZGljY2lvbmVzIHBhcmEgbG9zIHNpZ3VpZW50ZXMgY2luY28gYcOxb3MsIGEgcGFydGlyIGRlbCDDumx0aW1vIGHDsW8gY29uIGRhdG9zICgyMDIwKS4KCgpgYGB7cn0KZGF0b3NfbnVldm9zMiA8LSBkYXRhLmZyYW1lKGNvbmZpYW56YV9jb25zX3VzYT04Mi4zLHNhbGFyaW9faG9yYV9taW5fdXNhPTcuMjUsYcOxbz0yMDIxOjIwMjUpCnByZWRpY3QocmVncmVzaW9uMl8yLGRhdG9zX251ZXZvczIpCmBgYAoKKirCv1F1w6kgb2JzZXJ2YW1vcz8qKgoKYGBge3J9CnNlZ3VuZG8gPC0gcGxvdChwcmVkaWN0KHJlZ3Jlc2lvbjJfMixkYXRvc19udWV2b3MyKSwgdHlwZSA9ICJsIiwgeGxhYiA9ICJBw7FvIiwgIHlsYWIgPSJWZW50YXMgQ29tZXJjaWFsZXMiLCAgbWFpbj0iUHJlZGljY2nDs24gZGUgVmVudGFzIikKYGBgCgpFbiBlc3RhIHByZWRpY2Npw7NuIGRlIHZlbnRhcyBkZSBhdXRvbcOzdmlsZXMgKmNvbWVyY2lhbGVzKiwgb2JzZXJ2YW1vcyBxdWUgZXMgaGFjaWEgYXJyaWJhLiBFcyBkZWNpciwgdG9tYW5kbyBlbiBjdWVudGEgdW4gw61uZGljZSBkZSBjb25maWFuemEgZGUgODIuMyB5IGVsIHNhbGFyaW8gbcOtbmltbyBwb3IgaG9yYSBkZSAkNy4yNSwgc2UgZXNwZXJhIHF1ZSBsYXMgdmVudGFzIGRlIGVzdG9zIGF1dG9tw7N2aWxlcyAqKnN1YmFuKiogZW4gRVVBIGRlbCAyMDIxIGhhc3RhIDIwMjUuIEVuIGVsIGNhc28gZGUgbGFzIHZlbnRhcyBkZSBhdXRvbcOzdmlsZXMgKmNvbWVyY2lhbGVzKiwgbm90YW1vcyBxdWUgZWwgcGFzYXIgZGUgbG9zIGHDsW9zIHkgZWwgw61uZGljZSBkZSBjb25maWFuemEgZGVsIGNvbnN1bWlkb3IgIHNvbiBsYXMgdmFyaWFibGUgcXVlIG3DoXMgaW1wYWN0YW4sIHNpZ3VpZW5kbyBwb3IgZWwgc2FsYXJpbyBtw61uaW1vIHBvciBob3JhLiAgICAKCgpgYGB7cn0KZWZmZWN0X3Bsb3QocmVncmVzaW9uMl8yLHByZWQ9Y29uZmlhbnphX2NvbnNfdXNhLGludGVydmFsPVRSVUUpCmBgYAoKRWwgw61uZGljZSBkZSBjb25maWFuemEgZGVsIGNvbnN1bWlkb3IgZGUgRVVBLCB5IHN1IHJlbGFjacOzbiBjb24gbGFzIHZlbnRhcyBkZSBsb3MgYXV0b23Ds3ZpbGVzICpjb21lcmNpYWxlcyosIG9ic2VydmFtb3MgcXVlIGVzdGEgZXMgUE9TSVRJVkEuIERlYmlkbyBhIHF1ZSBhbCBjcmVjZXIgbGEgY29uZmlhbnphLCBpbmNyZW1lbnRhbiBsYXMgdmVudGFzLiBBIGNvbXBhcmFjacOzbiBkZSBsYSBncsOhZmljYSBkZSB2ZW50YXMgKnBhc3NlbmdlciogY29uIGVsIG5pdmVsIGRlIGNvbmZpYW56YSwgb2JzZXJ2YW1vcyBxdWUgZW4gZXN0ZSBjYXNvIGxhIHBlbmRpZW50ZSBlcyBtw6FzIHByb251bmNpYWRhLCBwb3IgbG8gcXVlIHZlbW9zIHVuIG1heW9yIGltcGFjdG8uICAgCgoqKsK/Q8OzbW8gZXMgZWwgaW1wYWN0byBkZSBkaWNoYXMgdmFyaWFibGVzIGV4cGxhbmF0b3JpYXMgc29icmUgbGEgdmFyaWFibGUgZGVwZW5kaWVudGU/KioKCmBgYHtyfQplZmZlY3RfcGxvdChyZWdyZXNpb24yXzIscHJlZD1zYWxhcmlvX2hvcmFfbWluX3VzYSxpbnRlcnZhbD1UUlVFKQpgYGAKCklndWFsbWVudGUsIGFsIHN1YmlyIGVsIHNhbGFyaW8gbcOtbmltbyBwb3IgaG9yYSwgbGFzIHZlbnRhcyBkZSBsb3MgYXV0b23Ds3ZpbGVzICpjb21lcmNpYWxlcyogZGlzbWludXllbi4gTGEgcmVsYWNpw7NuIGVzIE5FR0FUSVZBLiBFc3RlIHBvZHLDrWEgc2VyIHVuIHRlbWEgaW50ZXJlc2FudGUgYSBpbnZlc3RpZ2FyLiAgIAoKKkhJUMOTVEVTSVMqLCBhbCBzdWJpciBlbCBzYWxhcmlvIG3DrW5pbW8sIGxhcyBwZXJzb25hcyBzaWVudGVuIG1lbm9zIG5lY2VzaWRhZCBkZSBidXNjYXIgbnVldmFzIG9wY2lvbmVzIGRlIGdlbmVyYXIgZGluZXJvLCBkZSBkaWZlcmVudGVzIG1hbmVyYXMuCgoKYGBge3J9CmVmZmVjdF9wbG90KHJlZ3Jlc2lvbjJfMixwcmVkPWHDsW8saW50ZXJ2YWw9VFJVRSkKYGBgCgpDb24gZWwgcGFzYXIgZGUgbG9zIGHDsW9zIGxhcyB2ZW50YXMgZGVtdWVzdHJhbiBpbmNyZW1lbnRhciwgbGEgcmVsYWNpw7NuIGVzIFBPU0lUSVZBLiAgCgoKIyMjICoqTcOpeGljbyoqCgpTZSBpbXBvcnRhIGxhIGJhc2UgZGUgZGF0b3MgZGUgbGEgaW5kdXN0cmlhIGF1dG9tb3RyaXogZW4gTcOpeGljby4gCmBgYHtyfQpiYXNlX21leDwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvRG93bmxvYWRzL2luZHVzdHJpYV9hdXRvbW90cml6X214LmNzdiIpCnN1bW1hcnkoYmFzZV9tZXgpCnN0cihiYXNlX21leCkKYGBgCgpTZSBjYW1iaWFuYSBsb3Mgbm9tYnJlcyBhIGxhcyB2YXJpYWJsZXMuCmBgYHtyfQpuYW1lcyAoYmFzZV9tZXgpID0gYygiYcOxbyIsICJ1ZHNfZXhwb3J0IiwgInVkc19wcm9kIiwgInVkc192ZW50YXMiLCAiZW1wbGVhZG9zX2F1dG8iLCAiY29tcGVuc2FjaW9uZXNfZW1wbGVhZG9zIiwic2FsYXJpb19taW4iKQpuYW1lcyAoYmFzZV9tZXgpCnN0cihiYXNlX21leCkKYGBgCgojIyMjICoqUHJpbWVyIG1vZGVsbyBkZSByZWdyZXNpw7NuKioKCkVuIGVzdGUgY2Fzbywgc2UgZXNjb2dlIGNvbW8gdmFyaWFibGUgZGVwZW5kaWVudGUgbGFzIHVuaWRhZGVzIHByb2R1Y2lkYXMgZW4gTcOpeGljby4gIAoKYGBge3J9CnJlZ3Jlc2lvbjMgPC0gbG0gKHVkc19wcm9kIH4gYcOxbyArIGVtcGxlYWRvc19hdXRvLCBkYXRhPWJhc2VfbWV4KQpzdW1tYXJ5IChyZWdyZXNpb24zKQpgYGAKCkRlc2NyaXBjacOzbiBkZSB2YXJpYWJsZTogIAoqKkRlcGVuZGllbnRlOioqIHRlbmVtb3MgbGFzIHVuaWRhZGVzIGRlIGF1dG9zIHBwcm9kdWNpZGFzIGVuIE3DqXhpY28sIHNlIG1pZGVuIGVuIHVuaWRhZGVzIChjYXJyb3MpLgoqKkluZGVwZW5kaWVudGU6KiogbG9zIGHDsW9zIHkgbGEgY2FudGlkYWQgZGUgcGVyc29uYXNzIHF1ZSB0cmFiYWphbiBlbiBsYSBpbmR1c3RyaWEgYXV0b21vdHJpei4gIAoKVmllbmRvIGVsIG1vZGVsbyBkZSByZWdyZXNpw7NuIGFjdHVhbCwgb2JzZXJ2YW1vcyBxdWUgbGEgdmFyaWFibGUgaW5kZXBlbmRpZW50ZSBxdWUgdGllbmUgbcOhcyBpbXBhY3RvIGVuIGxhcyB1bmlkYWRlcyBwcm9kdWNpZGFzIGVzIGVsIGHDsW8gKHBvc2libGVtZW50ZSBwb3IgdGVtYXMgZGUgbW9tZW50bykuIFNlIGVzcGVyYSBxdWUgYWwgcGFzYXIgbG9zIGHDsW9zLCBsYXMgdW5pZGFkZXMgcHJvZHVjaWRhcyBiYWplbi4gQ29uIGxhIHZhcmlhYmxlIGVtcGxlYWRvcyBkZSBsYSBpbmR1c3RyaWEsIHBvZHLDrWFtb3MgZXNwZXJhciBxdWUgc3ViYW4gbGFzIHVuaWRhZGVzIHByb2R1Y2lkYXMgYWwgc3ViaXIgbGEgY2FudGlkYWQgZGUgZW1wbGVhZG9zIChzaW4gZW1iYXJnbywgZXN0YSB2YXJpYWJsZSBubyBlcyBtdXkgc2lnbmlmaWNhdGl2YSkuICAKCkxhIGNhbnRpZGFkIGRlIGVtcGxlYWRvcyBkZSBsYSBpbmR1c3RyaWEgc2UgZW5jdWVudHJhIGVudHJlIGxvcyA3ODAsMDAwIHkgbG9zIDg3MCwwMDAsIHNlIGVzY29nZSA4MjUsMDAwIHBhcmEgZWwgYW7DoWxpc2lzLiBDb24gbG9zIGHDsW9zIHByw7N4aW1vcy4gCmBgYHtyfQpkYXRvc19udWV2b3MzIDwtIGRhdGEuZnJhbWUoYcOxbz0yMDIyOjIwMjUsZW1wbGVhZG9zX2F1dG89ODI1MDAwKQpwcmVkaWN0KHJlZ3Jlc2lvbjMsZGF0b3NfbnVldm9zMykKYGBgCgoqKsK/UXXDqSBvYnNlcnZhbW9zPyoqCmBgYHtyfQp0ZXJjZXIgPC0gcGxvdChwcmVkaWN0KHJlZ3Jlc2lvbjMsZGF0b3NfbnVldm9zMyksIHR5cGUgPSAibCIsIHhsYWIgPSAiQcOxbyIsICB5bGFiID0iVW5pZGFkZXMgcHJvZHVjaWRhcyIsICBtYWluPSJQcmVkaWNjacOzbiBkZSBVbmlkYWRlcyBQcm9kdWNpZGFzIGVuIE3DqXhpY28iKQpgYGAKCkVuIGVzdGEgcHJlZGljY2nDs24gZGUgdW5pZGFkZXMgZGUgYXV0b23Ds3ZpbGVzIHByb2R1Y2lkYXMgZW4gTcOpeGljbywgb2JzZXJ2YW1vcyBxdWUgZXMgaGFjaWEgYWJham8uIEVzdG8gZXMsIHRvbWFuZG8gZW4gY3VlbnRhIGVsIHBhc2FyIGRlIGxvcyBhw7FvcywgeSB1biBwcm9tZWRpbyBkZSBjb2xhYm9yYWRvcmVzIGRlIDgyNSwwMDAuCgoKCioqwr9Dw7NtbyBlcyBlbCBpbXBhY3RvIGRlIGRpY2hhcyB2YXJpYWJsZXMgZXhwbGFuYXRvcmlhcyBzb2JyZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZT8qKgpgYGB7cn0KZWZmZWN0X3Bsb3QocmVncmVzaW9uMyxwcmVkPWHDsW8saW50ZXJ2YWw9VFJVRSkKYGBgCgpMYSByZWxhY2nDs24gZGVsIHBhc2FyIGRlIGxvcyBhw7FvcyBlcyBORUdBVElWQSwgZGViaWRvIGEgcXVlIHNlIGVzcGVyYSBxdWUgKmRpc21pbnV5YSogbGEgY2FudGlkYWQgZGUgcHJvZHVjY2nDs24uIAoKCmBgYHtyfQplZmZlY3RfcGxvdChyZWdyZXNpb24zLHByZWQ9ZW1wbGVhZG9zX2F1dG8saW50ZXJ2YWw9VFJVRSkKYGBgCgpWZW1vcyB1bmEgcmVsYWNpw7NuIFBPU0lUSVZBLCBlbiBlc3RlIGNhc287IGFsIHN1YmlyIGVsIG7Dum1lcm8gZGUgZW1wbGVhZG9zIGRlIGxhIGluZHVzdHJpYSBhdXRvbW90cml6IGVuIE3DqXhpY28sIGluY3JlbWVudGFuIGxhcyB1bmlkYWRlcyBwcm9kdWNpZGFzIGVuIE3DqXhpY28uIAoKCgojIyMjICoqU2VndW5kbyBtb2RlbG8gZGUgcmVncmVzacOzbioqCgpFbiBlc3RlIGNhc28sIHNlIGVzY29nZSBjb21vIHZhcmlhYmxlIGRlcGVuZGllbnRlIGxhcyB1bmlkYWRlcyBleHBvcnRhZGFzLiAgCgpgYGB7cn0KcmVncmVzaW9uNCA8LSBsbSAodWRzX2V4cG9ydCB+IGHDsW8gKyB1ZHNfcHJvZCwgZGF0YT1iYXNlX21leCkKc3VtbWFyeSAocmVncmVzaW9uNCkKYGBgCgpEZXNjcmlwY2nDs24gZGUgdmFyaWFibGU6ICAKKipEZXBlbmRpZW50ZToqKiBsYXMgdW5pZGFkZXMgZGUgY2Fycm9zIHF1ZSBzb24gZXhwb3J0YWRvcyBwcm92ZW5pZW50ZXMgZGUgTcOpeGljby4gICAKKipJbmRlcGVuZGllbnRlOioqIGxvcyBhw7FvcyB0ZW5lbW9zIGxhcyB1bmlkYWRlcyBkZSBhdXRvcyBwcHJvZHVjaWRhcyBlbiBNw6l4aWNvLCBzZSBtaWRlbiBlbiB1bmlkYWRlcyAoY2Fycm9zKS4gICAKCkVuIGVzdGUgY2FzbywgZWwgYcOxbyBubyB0aWVuZSB0YW50dGEgc2lnbmlmaWNhbmNpYSBjb21vIGVuIGVsIG1vZGVsbyBwYXNhZG8uIFNpbiBlbWJhcmdvLCBhbCBxdWVyZXIgdmVyIGxhcyB1bmlkYWRlcyBkZSBhdXRvcyBwcm9kdWNpZGFzIGVuIE3DqXhpY28sIGFob3JhIGNvbW8gdmFyaWFibGUgaW5kZXBlbmRpZW50ZSwgdmVtb3MgcXVlIHNpIHRpZW5lIHVuIGJ1ZW4gbml2ZWwgZGUgc2lnbmlmaWNhbmNpYSAocCBtZW5vciBhIDAuMDUpLiBPYnRlbmVtb3MgcXVlIGFsIHBhc2FyIGxvcyBhw7FvcywgeSBhbCBzdWJpciBsYXMgdW5pZGFkZXMgcHJvb2R1Y2lkYXMgZW4gTcOpeGljbywgbGEgY2FudGlkYWQgZGUgYXV0b3MgZXhwb3J0YWRvcyBpbmNyZW1lbnRhLiAgCgpTZSB1dGlsaXphbiBsb3MgcHLDs3hpbW9zIDUgYcOxb3MsIGVuIGVsIGN1w6FsIHBvZGVtb3MgdmVyIHVuIGNyZWNpbWllbnRvIChzaSB0b21hbW9zIGVuIGN1ZW50YSBsYXMgIGRvcyB2YXJpYWJsZXMgZGVzY3JpdGFzKS4KCmBgYHtyfQpkYXRvc19udWV2b3M0IDwtIGRhdGEuZnJhbWUoYcOxbz0yMDIyOjIwMjYsdWRzX3Byb2Q9MzAwMDAwMCkKcHJlZGljdChyZWdyZXNpb240LGRhdG9zX251ZXZvczQpCmBgYAoKKirCv1F1w6kgb2JzZXJ2YW1vcz8qKgoKYGBge3J9CmN1YXJ0byA8LSBwbG90KHByZWRpY3QocmVncmVzaW9uNCxkYXRvc19udWV2b3M0KSwgdHlwZSA9ICJsIiwgeGxhYiA9ICJBw7FvIiwgIHlsYWIgPSJVbmlkYWRlcyBleHBvcnRhZGFzIiwgIG1haW49IlByZWRpY2Npw7NuIGRlIFVuaWRhZGVzIEV4cG9ydGFkYXMgZGUgTcOpeGljbyIpCmBgYAoKTm90YW1vcyBxdWUgYWwgdGVuZXIgZXNhIGluZm9ybWFjacOzbiBkZSBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzLCBwb2Ryw61hbW9zIGVzcGVyYXIgdW4gY3JlY2ltaWVudG8gZW4gbGFzIHVuaWRhZGVzIGV4cG9ydGFkYXMgZGUgTcOpeGljby4gICAKCioqwr9Dw7NtbyBlcyBlbCBpbXBhY3RvIGRlIGRpY2hhcyB2YXJpYWJsZXMgZXhwbGFuYXRvcmlhcyBzb2JyZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZT8qKgoKYGBge3J9CmVmZmVjdF9wbG90KHJlZ3Jlc2lvbjQscHJlZD1hw7FvLGludGVydmFsPVRSVUUpCmBgYAoKVmVtb3MgdW5hIHJlbGFjacOzbiBQT1NJVElWQSwgYWwgcGFzYXIgbG9zIGHDsW9zIHNlIGVzcGVyYSBxdWUgbGFzIGV4cG9ydGFjaW9uZXMgY3JlemNhbi4gCgoKYGBge3J9CmVmZmVjdF9wbG90KHJlZ3Jlc2lvbjQscHJlZD11ZHNfcHJvZCxpbnRlcnZhbD1UUlVFKQpgYGAKClZlbW9zIHVuYSByZWxhY2nDs24gUE9TSVRJVkEsIGFsIHN1YmlyIGxhcyB1bmlkYWRlcyBwcm9kdWNpZGFzIGVuIE3DqXhpY28gc2UgZXNwZXJhIHF1ZSBsYXMgZXhwb3J0YWNpb25lcyBjcmV6Y2FuLiAKCgoKCiMjIyAqKlByb27Ds3N0aWNvIEluZHVzdHJpYSBBdXRvbW90cml6OiBFc3RhZG9zIFVuaWRvcyoqCgoqTWlzbWEgYmFzZSBkZSBkYXRvcyBwcmV2aWFtZW50ZSB1dGlsaXphZGEqCmBgYHtyfQpzdW1tYXJ5KGJhc2VfZXVhKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZm9yZWlnbikKbGlicmFyeShkcGx5cikgICAgICAgICMgZGF0YSBtYW5pcHVsYXRpb24gCmxpYnJhcnkoZm9yY2F0cykgICAgICAjIHRvIHdvcmsgd2l0aCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKbGlicmFyeShnZ3Bsb3QyKSAgICAgICMgZGF0YSB2aXN1YWxpemF0aW9uIApsaWJyYXJ5KGphbml0b3IpICAgICAgIyBkYXRhIGV4cGxvcmF0aW9uIGFuZCBjbGVhbmluZyAKbGlicmFyeShIbWlzYykgICAgICAgICMgc2V2ZXJhbCB1c2VmdWwgZnVuY3Rpb25zIGZvciBkYXRhIGFuYWx5c2lzIAojaW5zdGFsbC5wYWNrYWdlcygiSG1pc2MiKQojaW5zdGFsbC5wYWNrYWdlcygibmFuaWFyIikKbGlicmFyeShuYW5pYXIpIAojaW5zdGFsbC5wYWNrYWdlcygiZGxvb2tyIikKI2luc3RhbGwucGFja2FnZXMoInBvbGxzdGVyIikKbGlicmFyeShkbG9va3IpIApsaWJyYXJ5KHBvbGxzdGVyKQojaW5zdGFsbC5wYWNrYWdlcygiZGVzY3IiKQpsaWJyYXJ5KGRlc2NyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ3JhcGhpY3MpCmxpYnJhcnkoZXBpRGlzcGxheSkKbGlicmFyeSh0aWR5cikKbGlicmFyeShwc3ljaCkgICAgICAgICMgZnVuY3Rpb25zIGZvciBtdWx0aXZhcmlhdGUgYW5hbHlzaXMgCmxpYnJhcnkoY29ycnBsb3QpICAgICAjIGNvcnJlbGF0aW9uIHBsb3RzCmxpYnJhcnkoanRvb2xzKSAgICAgICAjIHByZXNlbnRhdGlvbiBvZiByZWdyZXNzaW9uIGFuYWx5c2lzIApsaWJyYXJ5KGxtdGVzdCkgICAgICAgIyBkaWFnbm9zdGljIGNoZWNrcyAtIGxpbmVhciByZWdyZXNzaW9uIGFuYWx5c2lzIApsaWJyYXJ5KGNhcikgICAgICAgICAgIyBkaWFnbm9zdGljIGNoZWNrcyAtIGxpbmVhciByZWdyZXNzaW9uIGFuYWx5c2lzCiNpbnN0YWxsLnBhY2thZ2VzKCJvbHNyciIpCmxpYnJhcnkob2xzcnIpICAgICAgICAjIGRpYWdub3N0aWMgY2hlY2tzIC0gbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMgCmxpYnJhcnkoa2FibGVFeHRyYSkgICAjIEhUTUwgdGFibGUgYXR0cmlidXRlcwpsaWJyYXJ5KHRzZXJpZXMpICAgIyB0aW1lIHNlcmllcyBhbmFseXNpcyBhbmQgY29tcHV0YXRpb25hbCBmaW5hbmNlIApsaWJyYXJ5KGZvcmVjYXN0KSAgIyBwcm92aWRlcyBtZXRob2RzIGFuZCB0b29scyBmb3IgZGlzcGxheWluZyBhbmQgYW5hbHl6aW5nIHVuaXZhcmlhdGUgdGltZSBzZXJpZXMgZm9yZWNhc3QKI2luc3RhbGwucGFja2FnZXMoImFzdHNhIikKbGlicmFyeShhc3RzYSkgICAgICMgYXBwbGllZCBzdGF0aXN0aWNhbCB0aW1lIHNlcmllcyBhbmFseXNpcwpsaWJyYXJ5KHBseXIpIApgYGAKCmBgYHtyfQpwbG90KGJhc2VfZXVhJGHDsW8sYmFzZV9ldWEkcHJvZF92ZWhfY29tZXJjaWFsZXMsIHR5cGU9ImwiLGNvbD0iYmx1ZSIsIGx3ZD0xLjUsIHhsYWIgPSJBw7FvIix5bGFiID0iRMOzbGFyZXMiLCBtYWluID0gIlZlbnRhcyBBbnVhbGVzIGRlIFZlaMOtY3Vsb3MgZW4gRVVBIikKbGluZXMoYmFzZV9ldWEkYcOxbyxiYXNlX2V1YSRwcm9kX3ZlaF9jb21lcmNpYWxlcyxjb2w9InJlZCIsbHR5PTMpCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZD1jKCJEb21lc3RpYyBDb21tZXJjaWFsIFNhbGVzIiwgIlByb2R1Y3Rpb24gQ29tbWVyY2lhbCBWZWhpY2xlcyIpLAogICAgICAgY29sPWMoImJsdWUiLCAicmVkIiksIGx0eSA9IDE6MiwgY2V4PTAuOCkKYGBgCgpFbiBlbCBwcmVzZW50ZSBncsOhZmljbywgdmVtb3MgbGEgdGVuZGVuY2lhIGEgdHJhdsOpcyBkZSBsb3MgYcOxb3MgZGUgbGFzIHZlbnRhcyBkZSBsb3MgdmVow61jdWxvcyAqcGFzc2VuZ2VyKiB5IGxvcyBjb21lcmNpYWxlcywgYWwganVudGFybG9zIG9idGVuZW1vcyBsbyBwcmVzZW50YWRvLiBJbnRlcmVzYW50ZW1lbnRlLCBlbiBlbCAyMDA5IGh1Ym8gdW5hIGRpbWludWNpw7NuIGRlIHZlbnRhcyBkZWJpZG8gYSBsYSBjcmlzaXMgcXVlIGVtcGV6w7MgZW4gRVVBLCBlIGltYXBjdMOzIHRvZG8gZWwgbXVuZG8uIERlc3B1w6lzIG9ic2VydmFtb3MgY29tbyBzaWd1ZSBjcmVjaWVuZG8gZW4gbG9zIGHDsW9vcywgaGFzdGEgZWwgMjAxOSAocG9yIENPVklELTE5KSBkb25kZSBkaXNtaW51eWVyb24uIFNlIGVzcGVyYXLDrWEgcXVlIGFob3JhIHF1ZSBsYSBlY29ub23DrWEgdmEgbWVqb3JhbmRvLCBzYWxpZW5kbyBkZWwgcHJvYmxlbWEgZGUgc2FuaWRhZC4KCiMjIyMgUHJvbsOzc3RpY28gTW92aW5nIEF2ZXJhZ2UgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnN1bW1hcnkobWFfbW9kZWw8LWFybWEoYmFzZV9ldWEkcHJvZF90b3RhbCxvcmRlcj1jKDAsMSkpKQptYV9tb2RlbF9mb3JlY2FzdDwtZm9yZWNhc3QobWFfbW9kZWwkZml0dGVkLGg9MyxsZXZlbD1jKDk1KSkKbWFfbW9kZWxfZm9yZWNhc3QKYGBgCgpQb2RlbW9zIHZlciBxdWUgZW4gbGEgaW5kdXN0cmlhIEF1dG9tb3RyaXogZGUgVVNBLCBlc3BlY8OtZmljYW1lbnRlIGVuIHZlbnRhcyBkZSBhdXRvcyAoKnBhc3NlbmdlciogeSBjb21lcmNpYWxlcyksIHRlbmVtb3MgdW5hIHRlbmRlbmNpYSBjb250aW51YSB5IHF1ZSBudWVzdHJvcyByYW5nb3MgKHBvciBsb3MgaW50ZXJ2YWxvcyBkZSBjb25maWFuemEpIHZhbiBkaXNtaW51eWVuZG8sIHBlcm8gbGFzIHByZWRpY2Npb25lcyBzZSBlbmN1ZW50cmFuIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6ICAKKioxLiBQcmltZXIgYcOxbzoqKiBkZXNkZSBsb3MgJDgsNTY4LjM1IGhhc3RhIGxvcyAkMTIsMzQyLjk5IGVuIHZlbnRhcyBkZSBjYXJyb3MgKCpwYXNzZW5nZXIqIHkgY29tZXJjaWFsZXMpLiAgCioqMi4gU2VndW5kbyBhw7FvOioqIGRlc2RlIGxvcyAkOCw0MzguMDggaGFzdGEgbG9zICQxMiw0NzMuMjYgZW4gdmVudGFzIGRlIGNhcnJvcyAoKnBhc3NlbmdlciogeSBjb21lcmNpYWxlcykuICAKKiozLiBUZXJjZXIgYcOxbzoqKiBkZXNkZSBsb3MgJDgsMzE1LjczIGhhc3RhIGxvcyAkMTIsNTk1LjYxIGVuIHZlbnRhcyBkZSBjYXJyb3MgKCpwYXNzZW5nZXIqIHkgY29tZXJjaWFsZXMpLgoKCgoKIyMjICoqUHJvbsOzc3RpY28gSW5kdXN0cmlhIEF1dG9tb3RyaXo6IE3DqXhpY28qKgoKYGBge3J9CnN1bW1hcnkoYmFzZV9tZXgpCmBgYAoKYGBge3J9CnBsb3QoYmFzZV9tZXgkYcOxbyxiYXNlX21leCR1ZHNfdmVudGFzLCB0eXBlPSJsIixjb2w9ImJsdWUiLCBsd2Q9MS41LCB4bGFiID0iQcOxbyIseWxhYiA9IlVuaWRhZGVzIiwgbWFpbiA9ICJWZW50YXMgZGUgVmVow61jdWxvcyBBbnVhbGVzIE1YIikKbGluZXMoYmFzZV9tZXgkYcOxbyxiYXNlX21leCR1ZHNfZXhwb3J0LGNvbD0icmVkIixsdHk9MykgCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZD1jKCJWZWjDrWN1bG9zIiksCiAgICAgICBjb2w9YygiYmx1ZSIsICJyZWQiKSwgbHR5ID0gMToyLCBjZXg9MC44KQpgYGAKCgpFbiBsYXMgcHJlc2VudGVzIGdyw6FmaWNhcyBvYnNlcnZhbW9zIHF1ZSB0YW50YXMgdW5pZGFkZXMgZGUgY2Fycm9zIHF1ZSBzZSB2ZW5kZW4sIGNvbnNpZGVyYW5kbyBNw6l4aWNvLiBWZW1vcyBjb21vIGVsIGHDsW8gImVzdHJlbGxhIiBkZSBsb3Mgw7psdGltb3MgNSBhw7FvcyBmdWUgZWwgMjAxOCwgeSBlbCBxdWUgYmFqw7MgY29uc2lkZXJhYmxlbWVudGUgZW4gdmVudGFzIGZ1ZSBlbCAyMDIwLCBwcm9iYWJsZW1lbnRlIGRlYmlkbyBhbCBpbmNyZW1lbnRvIGRlbCBDb3ZpZC0xOS4KCiMjIyMgUHJvbsOzc3RpY28gTW92aW5nIEF2ZXJhZ2UgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1hX21vZGVsPC0gYmFzZV9tZXgKbWFfbW9kZWxfZm9yZWNhc3Q8LWZvcmVjYXN0KG1hX21vZGVsJHVkc192ZW50YXMsaD0zLGxldmVsPWMoOTUpKQptYV9tb2RlbF9mb3JlY2FzdApgYGAKClBvZGVtb3MgdmVyIHF1ZSBlbiBsYSBpbmR1c3RyaWEgYXV0b21vdHJpeiBkZSBNw6l4aWNvLCBlc3BlY8OtZmljYW1lbnRlIGVuIHZlbnRhcywgdGVuZW1vcyB1bmEgdGVuZGVuY2lhIGNvbnRpbnVhIHkgcXVlIG51ZXN0cm9zIHJhbmdvcyAocG9yIGxvcyBpbnRlcnZhbG9zIGRlIGNvbmZpYW56YSkgZGlzbWludXllbiwgcG9yIGxvIHF1ZSBsYXMgcHJlZGljY2lvbmVzIHNlIGVuY3VlbnRyYW4gZGUgbGEgc2lndWllbnRlIG1hbmVyYTogIAoqKjEuIFByaW1lciBhw7FvOioqIGRlc2RlIGxhcyA2NzUsNzM5LjUgdW5pZGFkZXMgaGFzdGEgbGFzIDEsMDk5LDg2Mi41IHVpZGFkZXMgZW4gdmVudGFzIGRlIGNhcnJvcyBlbiBNw6l4aWNvLiAgCioqMi4gU2VndW5kbyBhw7FvOioqIGRlc2RlIGxhcyA1NjcsODgyLjUgdW5pZGFkZXMgaGFzdGEgbGFzIDk5MiwwMDUuNSB1bmlkYWRlcyBlbiB2ZW50YXMgZGUgY2Fycm9zIGVuIE3DqXhpY28uICAKKiozLiBUZXJjZXIgYcOxbzoqKiBkZXNkZSBsYXMgNDYwLDAyNS41IGhhc3RhIGxhcyA4ODQsMTQ4LjUgdW5pZGFkZXMgZW4gdmVudGFzIGRlIGNhcnJvcyBlbiBNw6l4aWNvLgoKCgoKCiMjIyAqKlByb27Ds3N0aWNvIFNjcmFwIEZPUk0qKgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGxvdChzY3JhcF9maW5hbHlhJGZlY2hhLCBzY3JhcF9maW5hbHlhJGNhbnRpZGFkLCB0eXBlPSJsIixjb2w9InJlZCIsIGx3ZD0xLjUsIHhsYWIgPSJGZWNoYSIseWxhYiA9IkNhbnRpZGFkIiwgbWFpbiA9ICJDYW50aWRhZCBkZSBTY3JhcCBwb3IgRmVjaGEiKQpsaW5lcyhzY3JhcF9maW5hbHlhJGZlY2hhLHNjcmFwX2ZpbmFseWEkY2FudGlkYWQsY29sPSJyZWQiLGx0eT0zKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9YygiU2NyYXAiKSwKICAgICAgIGNvbD1jKCJyZWQiKSwgbHR5ID0gMToyLCBjZXg9MC44KQpgYGAKCkFxdcOtIHZlbW9zIGNvbW8gcHJvZ3Jlc8OzIGxhIGNhbnRpZGFkIGRlIHNjcmFwIGR1cmFudGUgZWwgbWVzIGRlIGFnb3N0bywgcHJlc2VudGFuZG8gY2llcnRvcyAqcGljb3MqIGVuIGNpZXJ0b3MgZMOtYXMgZXNwZWPDrWZpY29zLiBWZW1vcyBxdWUgaHVibyB1bm8gaW1wb3J0YW50ZSBhbHJlZGVkb3IgZGVsIDExIG8gMTIgZGUgYWdvc3RvLCB5IG90cmEgY2FudGlkYWQgZGUgc2NyYXAgYcO6biBtw6FzIHJlbGV2YW50ZSBwb3IgZWwgMjQgZGUgYWdvc3RvLiBQb3Igb3JhIHBhcnRlLCBoYWLDrWEgb3Ryb3MgZMOtYXMgcXVlIG5vIHByZXNlbnRhcm9uIGNhbnRpZGFkIGRlIHNjcmFwLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KKG1hX21vZGVsPC1hcm1hKHNjcmFwX2ZpbmFseWEkY2FudGlkYWQsb3JkZXI9YygwLDEpKSkKbWFfbW9kZWxfZm9yZWNhc3Q8LWZvcmVjYXN0KG1hX21vZGVsJGZpdHRlZCxoPTMsbGV2ZWw9Yyg5NSkpCm1hX21vZGVsX2ZvcmVjYXN0CmBgYAoKUGFyYSBsYXMgc2lndWllbnRlcyB0cmVzIGZlY2hhcyBzZSBwcmVzZW50YSB1bmEgY2FudGlkYWQgZGUgc2NyYXAgaWd1YWwuIENvbiB1bmEgY2FudGlkYWQgbcOtbmltYSBkZSA2LjI2IHkgdW4gbcOheGltbyBkZSA3LjE2LgoKIyMjICoqUHJvbsOzc3RpY28gTWVybWEgRk9STSoqCgpgYGB7cn0KIyBTdW1hciBlbCBUb3RhbCBkZSBLaWxvc3hNZXMKbWVybWEgPC0gYygxNDU2MCwyMjgzMCwyMjQ3MCwxODgyMCwyMzQxMCwxODI4MCwxOTM3MCwzMjEwMCwxMzU4NikKCm1lcm1hX3N0IDwtIHRzKGRhdGEgPSBtZXJtYSwgc3RhcnQgPSBjKDIwMjIsMSksIGZyZXF1ZW5jeSA9IDEyKQptZXJtYV9zdApgYGAKCmBgYHtyfQptb2RlbG8gPC0gYXV0by5hcmltYShtZXJtYV9zdCkKbW9kZWxvCgpwcm9ub3N0aWNvIDwtIGZvcmVjYXN0KG1vZGVsbywgbGV2ZWw9Yyg5NSksIGg9MykKcHJvbm9zdGljbwpwbG90KHByb25vc3RpY28pCmBgYAoKUGFyYSBGb3JtIGFsIGRlc2Fycm9sbGFyIGVsIHByb27Ds3N0aWNvIGRlIE1lcm1hIHZlbW9zIHF1ZSB0ZW5lbW9zIHF1ZSBwYXJhIGxvcyBwcsOzeGltb3MgcGVyaW9kb3MgZGUgb2N0dWJyZSwgbm92aWVtYnJlIHkgZGljaWVtYnJlIHRlbmRyZW1vcyB1bmEgY2FudGlkYWQgY29uc3RhbnRlIGRlIDIwLDYwMi44OSBraWxvcyBkZSBtZXJtYSB5IHF1ZSBudWVzdHJvIGludGVydmFsbyBkZSBjb25maWFuemEgY2FlIGVudHJlIDk3NzAuNzExIGEgMzE0MzUuMDcuSW50ZXJwcmV0YW5kbyBsYSBpbmZvcm1hY2nDs24gdmVtb3MgcXVlIGEgY29tcGFyYWNpw7NuIGRlbCDDumx0aW1vIG1lcyBkZSBzZXB0aWVtYnJlLCBsb3Mga2lsb3MgY3JlY2Vyw6FuLgoKCgoKIyA8aW1nIHNyYz0iL1VzZXJzL2VsZW5hdmVsYS9EZXNrdG9wL0NhcHR1cmEgZGUgUGFudGFsbGEgMjAyMi0xMC0xMCBhIGxhKHMpIDE0LjI0LjM3LnBuZyI+ICAKCiMjICoqSy1NZWFucyBDbHVzdGVyaW5nKioKCipEZXNjYXJnYXIgbGlicmVyw61hcyoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShmb3JlaWduKQpsaWJyYXJ5KGRwbHlyKSAgICAgICAgIyBkYXRhIG1hbmlwdWxhdGlvbiAKbGlicmFyeShnZ3Bsb3QyKSAgICAgICMgZGF0YSB2aXN1YWxpemF0aW9uIAojaW5zdGFsbC5wYWNrYWdlcygicHN5Y2giKQpsaWJyYXJ5KHBzeWNoKSAgICAgICAgIyBmdW5jdGlvbnMgZm9yIG11bHRpdmFyaWF0ZSBhbmFseXNpcyAKbGlicmFyeShjb3JycGxvdCkgICAgICMgY29ycmVsYXRpb24gcGxvdHMKbGlicmFyeShqdG9vbHMpICAgICAgICMgcHJlc2VudGF0aW9uIG9mIHJlZ3Jlc3Npb24gYW5hbHlzaXMgCmxpYnJhcnkobG10ZXN0KSAgICAgICAjIGRpYWdub3N0aWMgY2hlY2tzIC0gbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMgCmxpYnJhcnkoY2FyKSAgICAgICAgICAjIGRpYWdub3N0aWMgY2hlY2tzIC0gbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMKbGlicmFyeShmYWN0b2V4dHJhKSAgICMgcHJvdmlkZXMgZnVuY3Rpb25zIHRvIGV4dHJhY3QgYW5kIHZpc3VhbGl6ZSB0aGUgb3V0cHV0IG9mIGV4cGxvcmF0b3J5IG11bHRpdmFyaWF0ZSBkYXRhIGFuYWx5c2VzCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ2ZvcnRpZnkiKQpsaWJyYXJ5KGdnZm9ydGlmeSkgICAgIyBkYXRhIHZpc3VhbGl6YXRpb24gdG9vbHMgZm9yIHN0YXRpc3RpY2FsIGFuYWx5c2lzIHJlc3VsdHMKYGBgCgoqU2UgaW1wb3J0YSBsYSBiYXNlIGRlIGRhdG9zIGRlIEJBSkFTIHlhIGxpbXBpYS4qCmBgYHtyfQojZmlsZS5jaG9vc2UoKQpiYWphczwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvYmFqYXNfZmluYWwuY3N2IikKc3VtbWFyeShiYWphcykKYGBgCgrCv0N1w6FudG9zIE5BICB0ZW5nbyBwb3IgdmFyaWFibGVzPwpgYGB7cn0Kc2FwcGx5KGJhamFzLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAoKIyMjICoqMS4gRWRhZCB5ICBkdXJhY2nDs24gLSA0IENsdXN0ZXJzKioKYGBge3J9CmJhamFzX25ldzwtYmFqYXMKYmFqYXNfbmV3PC1zdWJzZXQoYmFqYXNfbmV3LHNlbGVjdCA9IC1jKGFsdGEsZXN0YWRvLG5vbWJyZSkpCnN1bW1hcnkoYmFqYXNfbmV3KQpgYGAKClNlIHRvbWFuIGVuIGN1ZW50YSBsYSBlZGFkIHkgbGEgZHVyYWNpw7NuIGRlbnRybyBkZSBsYSBlbXByZXNhLiAKYGBge3J9CmJhamFzX2VkYWRkdXJfbm9ybTwtc2NhbGUoYmFqYXNfbmV3WzE6Ml0pIApgYGAKCmBgYHtyfQpmdml6X25iY2x1c3QoYmFqYXNfZWRhZGR1cl9ub3JtLCBrbWVhbnMsIG1ldGhvZD0id3NzIikrIAogIGdlb21fdmxpbmUoeGludGVyY2VwdD00LCBsaW5ldHlwZT0yKSsgICAgICAgICAKICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpICAKYGBgCgpTZSB0b21hbiBlbiBjdWVudGEsIHByaW1lcmFtZW50ZSwgc29sbyA0IGNsdXN0ZXJzLgoKYGBge3J9CmVkYWRfY2x1c3RlcjE8LWttZWFucyhiYWphc19lZGFkZHVyX25vcm0sNCkKZWRhZF9jbHVzdGVyMQpgYGAKCmBgYHtyfQpmdml6X2NsdXN0ZXIoZWRhZF9jbHVzdGVyMSxkYXRhPWJhamFzX2VkYWRkdXJfbm9ybSkKYGBgCgpBbCBvYnNlcnZhciB2ZW1vcyBjdWF0cm8gY2x1c3RlcnMgcXVlIHRvbWFuIGVuIGN1ZW50YSBsYSBlZGFkIHkgbGEgZHVyYWNpw7NuOiAgCjEuIEVsIHJvam8gKHNlcsOhIGxsYW1hZG8gIlNlbmlvciIpIGVzIHVuIGNsdXN0ZXIgcXVlIG9ic2VydmFtb3MgcXVlIHNvbiBwZXJzb25hcyBkZSBlZGFkIG1lZGlhbmEtYWx0YSB5IHF1ZSBkdXLDoW4gcmVsYXRpdmFtZW50ZSBwb2NvIHRpZW1wbywgZW50cmFuIGRlbnRybyBkZWwgMjUlIGNvbiBtZW5vciBkdXJhY2nDs24uICAgCjIuIEVsIHZlcmRlICgiQmVnaW5uZXIiKSBlcyBlbCBzZWdtZW50byBtw6FzIGpvdmVuIHkgcXVlIGRlIGxhcyBwZXJzb25hcyAoZW4gZ2VuZXJhbCkgbWVub3MgaGFuIGR1cmFkby4gU2UgcHVlZGUgY29uc2lkZXJhciBxdWUgbG9zIGrDs3ZlbmVzIHNvbiBsb3MgcXVlIG1lbm9zIGR1cmFuIGVuIEZvcm0gYSBwYXJ0aXIgZGUgZXN0ZSBjbHVzdGVyLiAgIAozLiBFbCBhenVsICgiSnVuaW9yIikgcmVzdWx0YSBlbCBjbHVzdGVyIGludGVybWVkaW8gZGUgbG9zIGRlIG1lbm9yIGR1cmFjacOzbi4gU29uIHBlcnNvbmFzIGVuICB1biByYW5nbyBkZSBlZGFkIG1lZGlvIHF1ZSB0YW1iacOpbiBzb24gcGFydGUgZGVsIDI1JSBpbmZlcmlvciBxdWUgbcOhcyBoYW4gZHVyYWRvLiAgCjQuIEVsIG1vcmFkbyAoIkV4cGVydCIpIGVzIGVsIGNsdXN0ZXIgcXVlIG3DoXMgc2UgYXBhcnRhIGRlIGxvcyBkZW3DoXM7IHNvbiBwZXJzb25hcyBjb24gdW4gbWF5b3IgcmFuZ28gZGUgZWRhZCBwZXJvIHF1ZSBzZSBhbGVqYW4gZGUgbG9zIG3DoXMgasOzdmVuZXMsIHkgIHF1ZSBoYW4gZHVyYWRvIG11Y2hvIG3DoSB0aWVtcG8uIFNlIGVuY3VlbnRyYW4gZW4gZWwgNTAlIHN1cGVyaW9yIGRlbCB0aWVtcG8gZGUgZHVyYWNpw7NuLgoKYGBge3J9CmJhamFzX25ldzI8LWJhamFzX25ldwpiYWphc19uZXcyJENsdXN0ZXJzPC1lZGFkX2NsdXN0ZXIxJGNsdXN0ZXIKYmFqYXNfbmV3MzwtYmFqYXNfbmV3MiAlPiUgZ3JvdXBfYnkoQ2x1c3RlcnMpICU+JSBzdW1tYXJpc2UoZWRhZD1tYXgoZWRhZCkpICU+JSBhcnJhbmdlKGRlc2MoZWRhZCkpCmJhamFzX25ldzIkQ2x1c3Rlcl9OYW1lczwtZmFjdG9yKGJhamFzX25ldzIkQ2x1c3RlcnMsbGV2ZWxzID0gYygxLDIsMyw0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJTZW5pb3IiLCAiQmVnaW5uZXIiLCAiSnVuaW9yIiwgIkV4cGVydCIpKQpgYGAKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQpsaWJyYXJ5KGRwbHlyKQpgYGAKCgpgYGB7cn0KYmFqYXNfbmV3NCA8LSBiYWphc19uZXcyICU+JSBncm91cF9ieShDbHVzdGVyX05hbWVzKSAlPiUgZHBseXI6OnN1bW1hcmlzZShlZGFkX2HDsW9zPW1heChlZGFkKSwgCmR1cmFjaW9uPW1lYW4oZHVyYWNpb24pLApDb3VudD1uKCkpCmBgYAoKCmBgYHtyfQpjbHVzdGVyczwtYXMuZGF0YS5mcmFtZShiYWphc19uZXc0KQpjbHVzdGVycwpgYGAKCkEgcGFydGlyIGRlIGxhIHRhYmxhLCBvYnNlcnZhbW9zIGxhcyBlZGFkZXMgbcOheGltYXMgZW5jb250cmFkYXMgZW4gY2FkYSBjbMO6c3Rlci4gQ29ub2NlbW9zIHF1ZSBsYSBwZXJzb25hIGNvbiBtYXlvciBlZGFkIGVuIGxvcyAqZXhwZXJ0KiB0aWVuZSA2MSwgZW4gKnNlbmlvciogdGllbmUgNTcsIGVuICpqdW5pb3IqIHRpZW5lIDM5IHkgZW4gKmJlZ2lubmVyKiAyNy4gICAKCklndWFsbWVudGUsIHZlbW9zIGxhIGR1cmFjacOzbiBtw6F4aW1hIHF1ZSBoYSBkdXJhZG8gYWxndWllbiBkZSBjYWRhIGNsdXN0ZXIsIGNvbXBhcmFtb3MgZWwgbcOheGltbyBkZWwgbWF5b3IsICpleHBlcnQqLCBxdWUgaGFuIHNpZG8gbcOhcyBkZSAxLDUwMCBkw61hcywgY29uIGVsIG3DoXhpbW8gZGVsIG1lbm9yLCAqanVuaW9yKiwgcXVlIGhhbiBzaWRvIDQ2IGTDrWFzLiAKCmBgYHtyfQpnZ3Bsb3QoYmFqYXNfbmV3NCxhZXMoeD1yZW9yZGVyKENsdXN0ZXJfTmFtZXMsQ291bnQpLHk9Q291bnQsZmlsbD1DbHVzdGVyX05hbWVzKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKYGBgCgpFeGlzdGUgdW5hIG1heW9yIGNhbnRpZGFkIGRlICJiZWdpbm5lcnMiLiBNdXkgcG9jYSBnZW50ZSBoYSBzaWRvICJFeHBlcnQiLiAKCgpgYGB7cn0KZ2dwbG90KGJhamFzX25ldzQsIGFlcyh4PUNsdXN0ZXJfTmFtZXMseT1lZGFkX2HDsW9zLGZpbGw9IENsdXN0ZXJfTmFtZXMsbGFiZWw9cm91bmQoZWRhZF9hw7FvcyxkaWdpdHM9MikpKSArIAogIGdlb21fY29sKCkgKyAKICBnZW9tX3RleHQoKQpgYGAKCkNvbXBhcmFtb3MgbGEgZWRhZCBtw6F4aW1hIGVuIGNhZGEgY2zDunN0ZXIgKGRlc2NyaXRvIGFudGVyaW9ybWVudGUpLgoKYGBge3J9CmdncGxvdChiYWphc19uZXc0LGFlcyh4PUNsdXN0ZXJfTmFtZXMseT1kdXJhY2lvbixmaWxsPSBDbHVzdGVyX05hbWVzLGxhYmVsPXJvdW5kKGR1cmFjaW9uLGRpZ2l0cz0yKSkpICsgCiAgZ2VvbV9jb2woKSArIAogIGdlb21fdGV4dCgpCmBgYAoKQ29tcGFyYW1vcyBsb3MgZMOtYXMgbcOheGltb3MgZW4gY2FkYSBjbMO6c3RlciAoZGVzY3JpdG8gYW50ZXJpb3JtZW50ZSkuCgoKCgoKIyMjICoqMi4gRWRhZCB5ICBkdXJhY2nDs24gLSA1IENsdXN0ZXJzKioKCkRlYmlkbyBhIHF1ZSB1bmEgZ3LDoWZpY2EgYW50ZXJpb3IgaW5kaWNhYmEgbGEgcG9zaWJpbGlkYWQgZGUgdGVuZXIgb3RybyBjbMO6c3RlciBxdWUgZGVzY3JpYmEgYSBvdHJvIGdydXBvLCBoYWNlbW9zIGxvIG1pc21vIGNvbiA1IHNlZ21lbnRvcywgZW4gdmV6IGRlIDQuIAoKYGBge3J9CmJhamFzX2VkYWRkdXJfbm9ybTI8LXNjYWxlKGJhamFzX25ld1sxOjJdKSAKYGBgCgpgYGB7cn0KZnZpel9uYmNsdXN0KGJhamFzX2VkYWRkdXJfbm9ybTIsIGttZWFucywgbWV0aG9kPSJ3c3MiKSsgCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTQsIGxpbmV0eXBlPTIpKyAgICAgICAgIAogIGxhYnMoc3VidGl0bGUgPSAiRWxib3cgbWV0aG9kIikgIApgYGAKCmBgYHtyfQplZGFkX2NsdXN0ZXIyPC1rbWVhbnMoYmFqYXNfZWRhZGR1cl9ub3JtLDUpCmVkYWRfY2x1c3RlcjIKYGBgCgpgYGB7cn0KZnZpel9jbHVzdGVyKGVkYWRfY2x1c3RlcjIsZGF0YT1iYWphc19lZGFkZHVyX25vcm0yKQpgYGAKCkNvbXBhcmFuZG8gZXN0YSAgZ3LDoWZpY2EgZGUgNSBzZWdtZW50b3MgY29udHJhIGxhIGFudGVyaW9ybWVudGUgdmlzdGEgZGUgNC4gRW4gZXN0YSBvYnNlcnZhbW9zIHF1ZSBzZXBhcmEgdW4gc2VnbWVudG8gcXVlIGRlamEgdW4gImVzcGFjaW8iIHF1ZSBhbnRlcyBubyBleGlzdMOtYTsgc2VwYXJhIHkgY3JlYSB1biBzZWdtZW50byBxdWUgaGFuIGR1cmFkbyAqTVVZKiBwb2NvLCB5IHF1ZSBzb24gZGUgbWVkaWFuYSBlZGFkOiAgICAKMS4gRWwgcm9qbyAobGxhbWFkbyAiU2VuaW9yIik6ICB0aWVuZSBkZSBiYWphIGEgbWVkaWFuYSBkdXJhY2nDs24geSB1biByYW5nbyBkZSBlZGFkIGFsdGEuICAKCjIuIEVsIHZlcmRlIGl6cXVpZXJkbyAobGxhbWFkbyAiQmVnaW5uZXIiKTogc29uIGxhcyBwZXJzb25hcyBtw6FzIGrDs3ZlbmVzIHkgcXVlIHRhbWJpw6luIGhhbiBkdXJhZG8gcG9jbyB0aWVtcG8uIFNlIHF1ZWRhbiBhcHJveGltYWRhbWVudGUgcG9yIGVsIDEwJSBpbmZlcmlvci4gIAoKMy4gRWwgdmVyZGUgZGVyZWNobyAobGxhbWFkbyAiVW5tb3RpdmF0ZWQiKSBzb24gcGVyc29uYXMgZGUgbWVkaWFuYSBlZGFkIHF1ZSBoYW4gZHVyYWRvIG11eSBwb2NvIHRpZW1wbyBlbiBsYSBlbXByZXNhLCBwYXJ0ZSBkZWwgNSUgaW5mZXJpb3IgZGUgZHVyYWNpw7NuLiAgIAoKNC4gRWwgYXp1bCAobGxhbWFkbyAiSnVuaW9yIik6IGFsIGhhY2VyIGVsIG51ZXZvIGNsw7pzdGVyIG5vcyBkYW1vcyBjdWVudGEgZGUgcXVlIGV4aXN0ZSB1biBncnVwbyBkZSBwZXJzb25hcyBkZSBjb3J0YS1tZWRpYW5hIGVkYWQgcXVlIGR1cmFuIHBvciBsbyBnZW5lcmFsIHVuYSBidWVuYSBjYW50aWRhZCBkZSB0aWVtcG8gKGEgY29tcGFyYWNpw7NuIGRlIG90cm9zKSwgYcO6biBtw6FzIHBvY28gcXVlIGxvcyBTZW5pb3IuICAKICAKNS4gRWwgbW9yYWRvIChsbGFtYWRvICJFeHBlcnQiKTogZXMgZWwgY2x1c3RlciBxdWUgbcOhcyBzZSBhcGFydGEgZGUgbG9zIGRlbcOhczsgc29uIHBlcnNvbmFzIGNvbiB1biBtYXlvciByYW5nbyBkZSBlZGFkIHBlcm8gcXVlIHNlIGFsZWphbiBkZSBsb3MgbcOhcyBqw7N2ZW5lcywgeSAgcXVlIGhhbiBkdXJhZG8gbXVjaG8gbcOhIHRpZW1wby4gU2UgZW5jdWVudHJhbiBlbiBlbCA1MCUgc3VwZXJpb3IgZGVsIHRpZW1wbyBkZSBkdXJhY2nDs24uICAKICAKCiAgICAKYGBge3J9CmJhamFzX25ldzY8LWJhamFzX25ldwpiYWphc19uZXc2JENsdXN0ZXJzPC1lZGFkX2NsdXN0ZXIyJGNsdXN0ZXIKYmFqYXNfbmV3NzwtYmFqYXNfbmV3NiAlPiUgZ3JvdXBfYnkoQ2x1c3RlcnMpICU+JSBzdW1tYXJpc2UoZWRhZD1tYXgoZWRhZCkpICU+JSBhcnJhbmdlKGRlc2MoZWRhZCkpCmJhamFzX25ldzYkQ2x1c3Rlcl9OYW1lczwtZmFjdG9yKGJhamFzX25ldzYkQ2x1c3RlcnMsbGV2ZWxzID0gYygxLDIsMyw0LDUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlNlbmlvciIsICJCZWdpbm5lciIsICJVbm1vdGl2YXRlZCIsICJKdW5pb3IiLCJFeHBlcnQiKSkKYGBgCgoqKkFuw6FsaXNpcyBjb24gdmFyaWFibGVzIGN1YWxpdGF0aXZhcyoqICAKCkVTVEFETyBDSVZJTCAgCiA8aW1nIHNyYz0gIi9Vc2Vycy9lbGVuYXZlbGEvRGVza3RvcC9DYXB0dXJhIGRlIFBhbnRhbGxhIDIwMjItMTAtMTkgYSBsYShzKSAxOC4yNi4xNi5wbmciPiAgICAKCsK/UXXDqSBjb25jbHXDrW1vcz8gIAoxLiBMYSBncmFuIHBhcnRlIGRlIGxvcyAqYmVnaW5uZXIqIHkgKmV4cGVydCogc29uIHNvbHRlcm9zLiAgCjIuIExvcyBlc3RhZG9zIGNpdmlsZXMgZGUgbG9zICpqdW5pb3IqIHkgKnNlbmlvciogZXN0w6FuIG3DoXMgZGlzdHJpYnVpZG9zIHF1ZSBsb3MgZGVtw6FzIGNsdXN0ZXJzIChzaW4gY29udGFyIGRpdm9yY2lvKS4gIAoqKjMuIEVsIMO6bmljbyBjbMO6c3RlciBxdWUgcHJlc2VudGEgZGl2b3JjaW9zIGVzIGFxdWVsIGxsYW1hZG8gKlVOTU9USVZBVEVEKi4qCgpHw4lORVJPICAKIDxpbWcgc3JjPSAiL1VzZXJzL2VsZW5hdmVsYS9EZXNrdG9wL0NhcHR1cmEgZGUgUGFudGFsbGEgMjAyMi0xMC0xOSBhIGxhKHMpIDE4LjMxLjE5LnBuZyI+ICAgIAoKwr9RdcOpIGNvbmNsdcOtbW9zPyAgCjEuIENvbW8geWEgc2UgaGFiw61hIHZpc3RhLCBlbiBnZW5lcmFsIG3DoXMgbXVqZXJlcyBoYW4gc2lkbyBkYWRhcyBkZSBiYWphLiAgCjIuIEhheSBtw6FzIGhvbWJyZXMgZW4gZWwgc2VnbWVudG8gZGUgKmV4cGVydCogKDc1JSkuICAKMy4gSGF5IG3DoXMgbXVqZXJlcyBlbiAqc2VuaW9yKiAoNzUlKSB5ICBlbiAqdW5tb3RpdmF0ZWQqICg4MCUpICAKNC4gTG9zIGdlbmVyb3MgZXN0w6FuIG11eSBlcXVpdGF0aXZhbWVudGUgZGlzdHJpYnVpZG9zIGVuICpiZWdpbm5lciogeSAqanVuaW9yKi4gIAoKClBVRVNUTyAgCjEuIEVsIHB1ZXN0byBtw6FzIHJlcGV0aWRvIGVuIFRPRE9TIGxvcyBzZWdtZW50b3MsIGVzICoqYXl1ZGFudGUgZ2VuZXJhbCoqLCByZXByZXNlbnRhbmRvIGVsIDc1JSBkZSB0b2RvcyBsb3MgcHVlc3Rvcy4KCgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKI2luc3RhbGwucGFja2FnZXMoImdnYWxsdXZpYWwiKQpsaWJyYXJ5KGdnYWxsdXZpYWwpCmBgYAoKYGBge3J9CmJhamFzX25ldzg8LWJhamFzX25ldzIgJT4lIGZpbHRlcihDbHVzdGVycz09MSB8IENsdXN0ZXJzPT00KSAlPiUgYXJyYW5nZShDbHVzdGVycykKZ2dwbG90KGFzLmRhdGEuZnJhbWUoYmFqYXNfbmV3OCksCiAgICAgICBhZXMoeT1zYWxhcmlvX2RpYXJpbywgYXhpczE9Z2VuZXJvLCBheGlzMj1lLmNpdmlsLikpICsKICBnZW9tX2FsbHV2aXVtKGFlcyhmaWxsPUNsdXN0ZXJfTmFtZXMpLCB3aWR0aCA9IDEvMTIpICsKICBnZW9tX3N0cmF0dW0od2lkdGggPSAxLzEyLCBmaWxsID0gImJsYWNrIiwgY29sb3IgPSAiZ3JleSIpICsKICBnZW9tX2xhYmVsKHN0YXQgPSAic3RyYXR1bSIsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoc3RyYXR1bSkpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKCJHZW5lcm8iLCAiRXN0YWRvIENpdmlsIiksIGV4cGFuZCA9IGMoLjA1LCAuMDUpKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9ICJTZXQxIikgKwogIGdndGl0bGUoIlNhbGFyaW8gRGlhcmlvIGRlIFBhc2Fkb3MgRW1wbGVhZG9zIGRlIEZvcm0gcG9yIEfDqW5lcm8geSBFc3RhZG8gQ2l2aWwiKQpgYGAKRGViaWRvIGEgcXVlIGV4aXN0ZW4gbcOhcyBwZXJzb25hYXMgcXVlIHNvbiBwYXJ0ZSBkZWwgY2x1c3RlciAiU0VOSU9SIiBxdWUgZGUgIkVYUEVSVCIsIG5vdGFtb3MgbcOhcyBlbCBjb2xvciByb3NhLiBOb3RhbW9zIHF1ZSBlbiBlc3RvcyBkb3MgY2x1c3RlcnMgbmlubmd1bmEgcGVyc29uYSBlcyBkaXZvcmNpYWRhIHkgcXVlIGxhcyBwZXJzb25hcyBxdWUgZXN0w6FuIGVuIHVuacOzbiBsaWJyZSAoZW4gZXN0b3MgZG9zIGNhc29zKSBwZXJ0ZW5lY2VuIHNvbG8gYSBsYSBjYXRlZ29yw61hIGRlIFNlbmlvci4gTG9zICBob21icmVzICpleHBlcnQqIGVzdMOhbiBlbiBzdSBtYXlvcsOtYSBjYXNhZG9zLCB5IGxhIMO6bmljYSBtdWplciAqZXhwZXJ0KiBzZSBlbmN1ZW50cmEgc29sdGVyYS4gIAoKCgpgYGB7cn0KYmFqYXNfbmV3OTwtYmFqYXNfbmV3MiAlPiUgZmlsdGVyKENsdXN0ZXJzPT0yIHwgQ2x1c3RlcnM9PTMpICU+JSBhcnJhbmdlKENsdXN0ZXJzKQpnZ3Bsb3QoYXMuZGF0YS5mcmFtZShiYWphc19uZXc5KSwKICAgICAgIGFlcyh5PXNhbGFyaW9fZGlhcmlvLCBheGlzMT1nZW5lcm8sIGF4aXMyPWUuY2l2aWwuKSkgKwogIGdlb21fYWxsdXZpdW0oYWVzKGZpbGw9Q2x1c3Rlcl9OYW1lcyksIHdpZHRoID0gMS8xMikgKwogIGdlb21fc3RyYXR1bSh3aWR0aCA9IDEvMTIsIGZpbGwgPSAiYmxhY2siLCBjb2xvciA9ICJncmV5IikgKwogIGdlb21fbGFiZWwoc3RhdCA9ICJzdHJhdHVtIiwgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChzdHJhdHVtKSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoIkdlbmVybyIsICJFc3RhZG8gQ2l2aWwiKSwgZXhwYW5kID0gYyguMDUsIC4wNSkpICsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIlNldDEiKSArCiAgZ2d0aXRsZSgiU2FsYXJpbyBEaWFyaW8gZGUgUGFzYWRvcyBFbXBsZWFkb3MgZGUgRm9ybSBwb3IgR8OpbmVybyB5IEVzdGFkbyBDaXZpbCIpCmBgYAoKQWhvcmEgdmllbmRvIGxvcyBjbHVzdGVycyBkZSAiQkVHSU5ORVIiIHkgIkpVTklPUiIsIHZlbW9zIGNvbW8gZXhpc3RlbiBtw6FzIG11amVyZXMgKmJlZ2lubmVyKiBzb2x0ZXJhcyBxdWUgbXVqZXJlcyAqanVuaW9yKiBzb2x0ZXJhczsgZmVuw7NtZW5vIHF1ZSAgb2N1cnJlIGRlIGxhIG1pc21hIG1hbmVyYSBjb24gbG9zIGhvbWJyZXMuIEVuIGVzdGUgY2FzbyBzaSB2ZW1vcyBtw6FzIGZyZWN1ZW50ZSBsYSB1bmnDs24gbGlicmUuIEFxdcOtIG1pc21vIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGVuIGdlbmVyYWwgbGFzIG11amVyZXMgdGllbmUgdW4gc2FsYXJpbyBzdXBlcmlvciBhIGxvcyBob21icmVzLiAKCgoKIyMjICoqMy4gRHVyYWNpw7NuIHkgc2FsYXJpbyoqCgoqSW1wb3J0YXIgYmFzZSBkZSBkYXRvcyoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmhfYmFqYXMgPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9iYWphc19maW5hbC5jc3YiKSAKYGBgCgrCv0N1w6FudG9zIE5BICB0ZW5nbyBwb3IgdmFyaWFibGVzPwpgYGB7cn0Kc2FwcGx5KHJoX2JhamFzLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAoKCkJhc2UgZGUgZGF0b3MgY29uIHZhbG9yZXMgQ1VBTlRJVEFUSVZPUwpgYGB7cn0KcmhfYmFqYXMyPC1yaF9iYWphcwpyaF9iYWphczI8LXN1YnNldChyaF9iYWphczIsc2VsZWN0ID0gLWMoYWx0YSxtb3Rpdm9fYmFqYSxlc3RhZG8sbm9tYnJlKSkKc3VtbWFyeShyaF9iYWphczIpCmBgYAoKTm9ybWFsaXphbmRvIGxhcyB2YXJpYWJsZXMKYGBge3J9CmJhamFzX2R1cmFjaW9uX25vcm08LXNjYWxlKHJoX2JhamFzMlsyOjNdKSAKYGBgCgpFbGJvdyBQbG90CmBgYHtyfQpmdml6X25iY2x1c3QoYmFqYXNfZHVyYWNpb25fbm9ybSwga21lYW5zLCBtZXRob2Q9IndzcyIpKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9NSwgbGluZXR5cGU9MikrICAgICAgICAgCiAgbGFicyhzdWJ0aXRsZSA9ICJFbGJvdyBtZXRob2QiKSAgCmBgYAoKVmlzdWFsaXphY2nDs24gaW5mb3JtYWNpw7NuIGRlIGNsdXN0ZXJzCmBgYHtyfQpkdXJhY2lvbl9jbHVzdGVyMTwta21lYW5zKGJhamFzX2R1cmFjaW9uX25vcm0sNSkKZHVyYWNpb25fY2x1c3RlcjEKYGBgCgpWaXN1YWxpemFjacOzbiBjbHVzdGVyaW5nIHJlc3VsdGFkb3MgCmBgYHtyfQpmdml6X2NsdXN0ZXIoZHVyYWNpb25fY2x1c3RlcjEsZGF0YT1iYWphc19kdXJhY2lvbl9ub3JtKQpgYGAKCkFsIG9ic2VydmFyIHZlbW9zIDUgY2x1c3RlcnMgcXVlIHRvbWFuIGVuIGN1ZW50YSBsYSBkdXJhY2nDs24geSBlbCBzYWxhcmlvOiAgCgoxLiBQb3Igw7psdGltbyB0ZW5lbW9zIGVsIHJvam8gKCJFeHRyYSIpIHF1ZSB0dXZvIGR1cmFjacOzbiBjb3J0byBwbGF6byBjb24gdW4gc2FsYXJpbyBtdXkgYWx0byAoJDUwMCBwZXNvcykuICAKIAoyLiBFbCBhenVsICgiQ29zdG9zb3MiKSBlcyBlbCBjbHVzdGVyIHF1ZSBub3MgY3Vlc3RhIGEgbGEgZW1wcmVzYSBwb3JxdWUgc2UgaW52aWVydGUgZW4gY2FwYWNpdGFjaW9uZXMgcGFyYSBlbGxvcyB5IHRlcm1pbmFuIHNhbGllbmRvIGRlIGxhIGVtcHJlc2E7IHNvbiBwZXJzb25hcyBjb24gdW4gcG9jbyBtYXMgZGUgZHVyYWNpw7NuIHBlcm8gbm8gbGEgc3VmaWNpZW50ZSAoMTk3IGTDrWFzKSB5IHN1IHNhbGFyaW8gZXMgZGUgJDE1MSBwb3IgZGViYWpvIHByb21lZGlvLgoKMy4gRWwgdmVyZGUgZGVyZWNobyAoIkR1cmFkZXJvcyIpIHNvbiBsb3MgY29sYWJvcmFkb3JlcyBxdWUgdGllbmVuIHVuYSBkdXJhY2nDs24gcmVndWxhciwgcGVybyBxdWUgYXVuIGFzw60gbm8gc2UgZW5jdWVudHJhbiBzYXRpc2ZlY2hvcyBwb3IgbG8gcXVlIHNlIHNhbGVuIGRlIGxhIGVtcHJlc2EgYWwgcmVkZWRvciBkZXNwdcOpcyBkZSAxIGHDsW8geSBtZWRpbyAoNjQ2IGTDrWFzKSB5IHN1IHNhbGFyaW8gZXMgZGUgJDE2OS4gICAKCjQuIFZlbW9zIG51ZXN0cm8gdmVyZGUgaXpxdWllcmRvIChzZXLDoSBsbGFtYWRvICJSZWhlbmVzIikgZXMgdW4gY2x1c3RlciBxdWUgb2JzZXJ2YW1vcyBxdWUgc29uIHBlcnNvbmFzIHF1ZSBzb2xvIGVudHJhbiB5IHNhbGVuIG11eSBwcm9udG8gZGUgbGEgZW1wcmVzYSAoMTQxIGTDrWFzKSB5IHRpZW5lbiB1biBzYWxhcmlvIGRpYXJpbyBxdWUgZXMgYXJyaWJhIGRlbCBwcm9tZWRpbywgYWxyZWRlZG9yIGRlICQxODAgcGVzb3MuICAKCjUuIENvbW8gbW9yYWRvIHRlbmVtb3MgYSBsb3MgY29sYWJvcmFkb3JlcyBxdWUgc29uIGxlYWxlcyBhIGxhIGVtcHJlc2EgKCJGaWVsZXMiKSBlcyBlbCBzZWdtZW50byBkb25kZSB5YSB0aWVuZW4gbcOhcyBkZSA0LTUgYcOxb3MgdHJhYmFqYW5kbyB5IGN1ZW50YW4gY29uIHVuIHNhbGFyaW8gZGUgJDE2OCBwZXNvcy4gIAoKTm9tYnJhciBhIGxvcyBjbHVzdGVycyAKYGBge3J9CnJoX2JhamFzMzwtcmhfYmFqYXMyCnJoX2JhamFzMyRDbHVzdGVyczwtZHVyYWNpb25fY2x1c3RlcjEkY2x1c3RlcgpyaF9iYWphczQ8LXJoX2JhamFzMyAlPiUgZ3JvdXBfYnkoQ2x1c3RlcnMpICU+JSBzdW1tYXJpc2UoZHVyYWNpb249bWF4KGR1cmFjaW9uKSkgJT4lIGFycmFuZ2UoZGVzYyhkdXJhY2lvbikpCnJoX2JhamFzMyRDbHVzdGVyX05hbWVzPC1mYWN0b3IocmhfYmFqYXMzJENsdXN0ZXJzLGxldmVscyA9IGMoMSwyLDMsNCw1KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJFeHRyYSIsICJSZWhlbmVzIiwiRHVyYWRlcm9zIiwgIkNvc3Rvc29zIiwiRmllbGVzIikpCmBgYAoKCkVuZ3J1cGFyIGxvcyBjbHVzdGVycyBwb3Igbm9tYnJlIGRlIGNsdXN0ZXJzIHkgcmVzdW1pciBjb2x1bW5hcwpgYGB7cn0KcmhfYmFqYXM1PC0gcmhfYmFqYXMzICU+JSBncm91cF9ieShDbHVzdGVyX05hbWVzKSAlPiUgZHBseXI6OnN1bW1hcmlzZShkdXJhY2lvbl9kaWFzPW1heChkdXJhY2lvbiksIApzYWxhcmlvX2RpYXJpbz1tZWFuKHNhbGFyaW9fZGlhcmlvKSwKQ291bnQ9bigpKQpgYGAKCgpGb3JtYXRvIHRhYmxhIHBhcmEgaW5mb3JtYWNpw7NuIGRlIGxvcyBjbHVzdGVycyAKYGBge3J9CkNsdXN0ZXJzPC1hcy5kYXRhLmZyYW1lKHJoX2JhamFzNSkKQ2x1c3RlcnMKYGBgCgoKR3LDoWZpY2EgY29uIGVsIG51bWVybyBkZSBvYnNlcnZhY2lvbmVzIHBvciBub21icmVzIGRlIGNsdXN0ZXJzIApgYGB7cn0KZ2dwbG90KHJoX2JhamFzNSxhZXMoeD1yZW9yZGVyKENsdXN0ZXJfTmFtZXMsQ291bnQpLHk9Q291bnQsZmlsbD1DbHVzdGVyX05hbWVzKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKYGBgCgpgYGB7cn0KZ2dwbG90KHJoX2JhamFzNSwgYWVzKHg9Q2x1c3Rlcl9OYW1lcyx5PWR1cmFjaW9uX2RpYXMsZmlsbD0gQ2x1c3Rlcl9OYW1lcyxsYWJlbD1yb3VuZChkdXJhY2lvbl9kaWFzLGRpZ2l0cz0yKSkpICsgCiAgZ2VvbV9jb2woKSArIAogIGdlb21fdGV4dCgpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QocmhfYmFqYXM1LGFlcyh4PUNsdXN0ZXJfTmFtZXMseT1zYWxhcmlvX2RpYXJpbyxmaWxsPSBDbHVzdGVyX05hbWVzLGxhYmVsPXJvdW5kKHNhbGFyaW9fZGlhcmlvLGRpZ2l0cz0yKSkpICsgCiAgZ2VvbV9jb2woKSArIAogIGdlb21fdGV4dCgpCmBgYAoKRXN0w6FzIHVsdGltYXMgZ3LDoWZpY2FzIG5vcyBtdWVzdHJhbiBsbyBxdWUgc2UgZGVzY3JpYmnDsyBhbnRlcmlvcmltZW50ZSBzb2JyZSBxdWUgZHVyYWNpw7NuIHkgc2FsYXJpbyB0aWVuZSBjYWRhIGNsdXN0ZXIgeSBwYXJhIGNvbmNsdWlyIHNlIG1vc3RyYXJhIGN1YW50b3MgY29sYWJvcmFkb3JlcyB0aWVuZSBjYWRhIGNsdXN0ZXI6CgpUb3RhbCAyMzMgCiogKioiUmVoZW5lcyIqKiB0aWVuZSBlbCBzZWdtZW50byBjb24gbcOhcyBjb2xhYm9yYWRvcmVzIHkgdGllbmUgdW4gdG90YWwgZGUgMTkxLiBFbiBwb3JjZW50YWplIHNlIHB1ZWRlIGRlY2lyIHF1ZSBlcyBlbCA4MS45NyUgdG90YWwgZGUgYmFqYXMuICAKCiogKioiQ29zdG9zb3MiKiogZXN0YSBlbiBzZWd1bmRvIGx1Z2FyIHRlbmllbmRvIDI3IGNvbGFib3JhZG9yZXMgKDExLjU3JSkuICAgIAoKKiAqKiJEdXJhZGVyb3MiKiogdGVyY2VyIGx1Z2FyIGNvbiB1biB0b3RhbCBkZSAxMCBjb2xhYm9yYWRvcmVzICg0LjI5JSkuICAKCiogKioiRmllbGVzIioqIGN1YXJ0byBsdWdhciBjb24gNCBjb2xhYm9yYWRvcmVzICgxLjcxJSkuICAKCiogUG9yIMO6bHRpbW8gZXN0YSBlbCAqKiJFeHRyYSIqKiBxdWUgZnVlIHNvbG8gdW4gY29sYWJvcmFkb3IgcXVlIHNlIGVuY3VlbnRyYSBmdWVyYSBkZSBsbyBub3JtYWwgKDAuNDMlKS4gIAoKKipBbsOhbGlzaXMgY29uIHZhcmlhYmxlcyBjdWFsaXRhdGl2YXMqKiAgCgpFU1RBRE8gQ0lWSUwgICAKCjxpbWcgc3JjPSAiL1VzZXJzL2VsZW5hdmVsYS9EZXNrdG9wL0NhcHR1cmEgZGUgUGFudGFsbGEgMjAyMi0xMC0xOSBhIGxhKHMpIDIxLjE4LjM3LnBuZyI+ICAgIAoKMS4gRW4gbG9zICoqcmVoZW5lcyoqIHZlbW9zIHF1ZSBhcHJveGltYWRhbWVudGUgbGEgbWl0YWQgc29uIHNvbHRlcm9zLiBTb2xvIGFxdcOtIGVuY29udHJhbW9zIGEgbG9zIGRpdm9yY2lhZG9zLiAgCjIuIEVuIGN1YW50byBhIGxvcyAqKmNvc3Rvc29zKiosIHZlbW9zIHF1ZSBsYSBtaW5vcsOtYSBlc3TDoSBlbiBtYXRyaW1vbmlvLiAgCjMuIERlIGxvcyAqKmR1cmFkZXJvcyoqIG3DoXMgZGUgbGEgbWl0YWQgc29uIHNvbHRlcm9zLiAgCjQuIEVuIGxvcyAqKmZpZWxlcyoqLCBlbCA3NSUgc29uIHNvbHRlcm9zLiAgCjUuIEZpbmFsbWVudGUsIGVsIGNhc28gImV4dHJhb3JkaW5hcmlvIiB2aXN0byBlbiAqKmV4dHJhKiosIGVzIHNvbHRlcm8uICAgIAoKCkdFTkVSTyAgCgo8aW1nIHNyYz0gIi9Vc2Vycy9lbGVuYXZlbGEvRGVza3RvcC9DYXB0dXJhIGRlIFBhbnRhbGxhIDIwMjItMTAtMTkgYSBsYShzKSAyMS4yNS40OC5wbmciPiAgIAoKMS4gSWd1YWxtZW50ZSwgdmVtb3MgcXVlIGxhIG1heW9yw61hIGVuIGdlbmVyYWwgc29uIG11amVyZXMuICAKMi4gTGEgcGVyc29uYSBxdWUgZXMgcGFydGUgZGVsICoqZXh0cmEqKiBlcyB1biBob21icmUuICAKMy4gRGUgbG9zICoqZHVyYWRlcm9zKiogZWwgNzAlIHNvbiBtdWplcmVzLiBEZSBsb3MgKipyZWhlbmVzKiogeSBsb3MgKipjb3N0b3NvcyoqIHRhbWJpw6luIHNlIHRpZW5lIGxhIG1heW9yw61hIGRlIG11amVyZXMuICAKNC4gRW4gKipmaWVsZXMqKiwgZWwgNzUlIHNvbiBob21icmVzLiAgCiAgCgpQVUVTVE8gIAoxLiBMYXMgMjcgcGVyc29uYXMgZGUgKipjb3N0b3NvcyoqLCB0dXZpZXJvbiBlbCBwdWVzdG8gZGUgIkF5dWRhbnRlIEdlbmVyYWwiLiBEZSBsb3MgMTkwIGludGVncmFudGVzIGRlICoqcmVoZW5lcyoqLCAxNDQgdGVuw61hbiBlbCBtaXNtbyBwdWVzdG8uICAKMi4gTGEgcGVyc29uYSBkZSAqKmV4dHJhKiogcGVydGVuZWPDrWEgYWwgcHVlc3RvIGEgZGlzZcOxby4gIAoKCgpgYGB7cn0KcmhfYmFqYXM2PC1yaF9iYWphczMgJT4lIGZpbHRlcihDbHVzdGVycz09MiB8IENsdXN0ZXJzPT0zKSAlPiUgYXJyYW5nZShDbHVzdGVycykKZ2dwbG90KGFzLmRhdGEuZnJhbWUocmhfYmFqYXM2KSwKICAgICAgIGFlcyh5PXNhbGFyaW9fZGlhcmlvLCBheGlzMT1nZW5lcm8sIGF4aXMyPWUuY2l2aWwuKSkgKwogIGdlb21fYWxsdXZpdW0oYWVzKGZpbGw9Q2x1c3Rlcl9OYW1lcyksIHdpZHRoID0gMS8xMikgKwogIGdlb21fc3RyYXR1bSh3aWR0aCA9IDEvMTIsIGZpbGwgPSAiYmxhY2siLCBjb2xvciA9ICJncmV5IikgKwogIGdlb21fbGFiZWwoc3RhdCA9ICJzdHJhdHVtIiwgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChzdHJhdHVtKSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoIkdlbmVybyIsICJFc3RhZG8gQ2l2aWwiKSwgZXhwYW5kID0gYyguMDUsIC4wNSkpICsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIlNldDEiKSArCiAgZ2d0aXRsZSgiU2FsYXJpbyBEaWFyaW8gZGUgUGFzYWRvcyBFbXBsZWFkb3MgZGUgRm9ybSBwb3IgR8OpbmVybyB5IEVzdGFkbyBDaXZpbCIpCmBgYAoKRGViaWRvIGEgcXVlIG5vIGV4aXN0ZW4gbXVjaGFzIHBlcnNvbmFzIHF1ZSBzb24gcGFydGUgZGVsIHNlZ21lbnRvICoqZHVyYWRlcm9zKiosIHNlIHZlIG11Y2hhIG3DoXMgcHJlc2VuY2lhIGRlIHBhcnRlIGRlbCBzZWdtZW50byBkZSBsb3MgKipyZWhlbmVzKiouIEFxdcOtIG9ic2VydmFtb3MgcXVlIGxhIG1heW9yw61hIGRlIGxhcyBwZXJzb25hcyBlbiBtYXRyaW1vbmlvIHF1ZSBzb24gKipyZWhlbmVzKiogc29uIGhvbWJyZXMuCgoKCgpgYGB7cn0KcmhfYmFqYXM3PC1yaF9iYWphczMgJT4lIGZpbHRlcihDbHVzdGVycz09MSB8IENsdXN0ZXJzPT00KSAlPiUgYXJyYW5nZShDbHVzdGVycykKZ2dwbG90KGFzLmRhdGEuZnJhbWUocmhfYmFqYXM3KSwKICAgICAgIGFlcyh5PXNhbGFyaW9fZGlhcmlvLCBheGlzMT1nZW5lcm8sIGF4aXMyPWUuY2l2aWwuKSkgKwogIGdlb21fYWxsdXZpdW0oYWVzKGZpbGw9Q2x1c3Rlcl9OYW1lcyksIHdpZHRoID0gMS8xMikgKwogIGdlb21fc3RyYXR1bSh3aWR0aCA9IDEvMTIsIGZpbGwgPSAiYmxhY2siLCBjb2xvciA9ICJncmV5IikgKwogIGdlb21fbGFiZWwoc3RhdCA9ICJzdHJhdHVtIiwgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChzdHJhdHVtKSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoIkdlbmVybyIsICJFc3RhZG8gQ2l2aWwiKSwgZXhwYW5kID0gYyguMDUsIC4wNSkpICsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIlNldDEiKSArCiAgZ2d0aXRsZSgiU2FsYXJpbyBEaWFyaW8gZGUgUGFzYWRvcyBFbXBsZWFkb3MgZGUgRm9ybSBwb3IgR8OpbmVybyB5IEVzdGFkbyBDaXZpbCIpCmBgYAoKQ29tbyBmdWUgbWVuY2lvbmFkbyBhbnRlcmlvcm1lbnRlLCBsYSBwZXJzb25hIHF1ZSBlcyBwYXJ0ZSBkZSAqKmV4dHJhKiogZXMgdW4gaG9tYnJlIHNvbHRlcm8uIFBvciBvdHJhIHBhcnRlLCB2ZW1vcyBxdWUgZGUgbG9zICoqY29zdG9zb3MqKiwgaGF5IG3DoXMgbXVqZXJlcyBlbiB1bmnDs24gbGlicmUgcXVlIGhvbWJyZXMuIEFzw60gY29tbyBoYXkgbcOhcyBtdWplcmVzIHNvbHRlcmFzIHF1ZSBjYXNhZGFzLiAKCmBgYHtyfQpyaF9iYWphczg8LXJoX2JhamFzMyAlPiUgZmlsdGVyKENsdXN0ZXJzPT00IHwgQ2x1c3RlcnM9PTUpICU+JSBhcnJhbmdlKENsdXN0ZXJzKQpnZ3Bsb3QoYXMuZGF0YS5mcmFtZShyaF9iYWphczgpLAogICAgICAgYWVzKHk9c2FsYXJpb19kaWFyaW8sIGF4aXMxPWdlbmVybywgYXhpczI9ZS5jaXZpbC4pKSArCiAgZ2VvbV9hbGx1dml1bShhZXMoZmlsbD1DbHVzdGVyX05hbWVzKSwgd2lkdGggPSAxLzEyKSArCiAgZ2VvbV9zdHJhdHVtKHdpZHRoID0gMS8xMiwgZmlsbCA9ICJibGFjayIsIGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV9sYWJlbChzdGF0ID0gInN0cmF0dW0iLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KHN0cmF0dW0pKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygiR2VuZXJvIiwgIkVzdGFkbyBDaXZpbCIpLCBleHBhbmQgPSBjKC4wNSwgLjA1KSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiU2V0MSIpICsKICBnZ3RpdGxlKCJTYWxhcmlvIERpYXJpbyBkZSBQYXNhZG9zIEVtcGxlYWRvcyBkZSBGb3JtIHBvciBHw6luZXJvIHkgRXN0YWRvIENpdmlsIikKYGBgCkRlIHBhcnRlIGRlIGxvcyAqKmZpZWxlcyoqLCBvYnNlcnZhbW9zIHF1ZSBoYXkgdW5hIHBlcXVlw7FhYSBjYW50aWRhZCBkZSBob21icmVzIHF1ZSBlc3TDoW4gY2FzYWRvcywgdW5hIHBlcXVlw7FhIGNhbnRpZGFkIGRlIG11amVyZXMgc29sdGVyYXMsIHkgdW5hIGNhbnRpZGFkIHVubiBwb2NvIG1heW9yIGRlIGhvbWJyZXMgc29sdGVyb3MuCgoKCiMjIyAqKjQuIEVkYWQgeSBzYWxhcmlvKioKCipJbXBvcnRhciBiYXNlIGRlIGRhdG9zKgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpiZF9yaDwtcmVhZC5jc3YoIi9Vc2Vycy9lbGVuYXZlbGEvYmFqYXNfZmluYWwuY3N2IikgCmBgYAoKCkFuYWxpemFyIGxhIGVzdHJ1Y3R1cmEgZGUgbGEgYmFzZSBkZSBkYXRvcwpgYGB7cn0Kc3RyKGJkX3JoKSAKYGBgCgpDdWFudG9zIE5hwrRzIGhheSBwb3IgdmFyaWFibGUKCmBgYHtyfQpzYXBwbHkoYmRfcmgsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCgpEZWphbW9zIMO6bmljYW1lbnRlIGxhcyB2YXJpYWJsZXMgZGUgaW50ZXLDqXMKCmBgYHtyfQpiZHJfbmV3IDwtIGJkX3JoCmJkcl9uZXc8LXN1YnNldChiZHJfbmV3LHNlbGVjdCA9IC1jKGFsdGEsbW90aXZvX2JhamEsZXN0YWRvLG5vbWJyZSxkdXJhY2lvbikpCnN1bW1hcnkoYmRyX25ldykKYGBgCgpOb3JtYWxpemFjacOzbiBkZSB2YXJpYWJsZXMKCmBgYHtyfQpiYWphc19lZGFkc2FsIDwtIHNjYWxlKGJkcl9uZXdbMToyXSkgCmBgYAoKRWxib3cgcGxvdAoKYGBge3J9CmZ2aXpfbmJjbHVzdChiYWphc19lZGFkc2FsLCBrbWVhbnMsIG1ldGhvZD0id3NzIikrIAogIGdlb21fdmxpbmUoeGludGVyY2VwdD00LCBsaW5ldHlwZT0yKSsgICAgICAgICAKICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpIApgYGAKClZpc3VhbGl6YWNpw7NuIGRlIGNsdXN0ZXJzCgpTZSB0b21hbiBlbiBjdWVudGEsIHByaW1lcmFtZW50ZSwgc29sbyA0IGNsdXN0ZXJzLgoKYGBge3J9CmVkYWRzYWxfY2x1c3RlcjE8LWttZWFucyhiYWphc19lZGFkc2FsLDQpCmVkYWRzYWxfY2x1c3RlcjEKYGBgCgpgYGB7cn0KZnZpel9jbHVzdGVyKGVkYWRzYWxfY2x1c3RlcjEsZGF0YT1iYWphc19lZGFkc2FsKQpgYGAKClZlbW9zIHF1ZSBlbCBuw7ptZXJvIMOzcHRpbW8gZGUga2x1c3RlcnMgcGFyYSBsYXMgdmFyaWFibGVzIGVkYWQgeSBzYWxhcmlvIGRpYXJpbyBzb24gNCBzZWfDum4gZWwgbcOpdG9kbyBkZSBFbGJvdy4KCkNvbW8gZGF0byBkaXNwYXJhZG8geSBwb3NpYmxlIHdhcm5pbmcgdmVtb3MgcXVlIGhheSB1biB0cmFiYWphZG9yIGNvbiB1biBzYWxhcmlvIGRpYXJpbyBkZSA1MDAgcGVzb3MgbG8gcXVlIHBvZHLDrWEgcmVzdWx0YXIgY29tbyB1biBlcnJvciwgc2luIGVtYmFyZ28gbG9zIG90cm9zIDMgZ3J1cG9zIHZhcsOtYW4gYmFzdGFudGUgZW4gZWRhZCBtw6FzIG5vIGVuIHNhbGFyaW8gcHVlcyBlbCByYW5nbyBlcyBtdXkgY2VyY2Fuby4KCjEuICoqUm9qbyoqIEVzIGVsIGNsdXN0ZXIgY29uIGVsIHJlZ2lzdHJvIGRlIHBlcnNvbmFzIGNvbiBtYXlvciBlZGFkIHkgY3VlbnRhbiBjb24gZWwgc2FsYXJpbyBkaWFyaW8gbcOhcyBhbHRvIDE4My42OC4gIAoKMi4gKipWZXJkZSoqIEVzIGVsIGNsdXN0ZXIgY29uIG1heW9yIHJlZ2lzdHJvIGRlIHBlcnNvbmFzIGNvbiBlZGFkIGrDs3ZlbiB5IHNhbGFyaW8gZGUgMTgyLjY4LiAgCgozLiAqKkFsdG8qKiBFbCBkYXRvIGVzIMO6bmljbyBlbiBlbCBjb250ZW8gY29uIHVuIHRyYWJhamFkb3IgZGUgMzIgYcOxb3MgcXVlIHRpZW5lIHVuIHNhbGFyaW8gZGlhcmlvIGRlIDUwMCBwZXNvcyB5IGVzIHNvbHRlcm8uIFN1IHB1bnRvIGVuIGVsIG1hcGVvIGVzIMO6bmljbyAoY29sb3Igcm9qbykuICAKICAKNC4gKipBenVsKiogRXMgZWwgY2x1c3RlciBjb24gZWwgcmVnaXN0cm8gZGUgcGVyc29uYXMgY29uIGVkYWQgcHJvbWVkaW8geSBzYWxhcmlvIGRlIDE4MC42OC4gIAogIAoKTm9tYnJhciBhIGxvcyBjbHVzdGVycwoKYGBge3J9CmJkcl9uZXcyPC1iZHJfbmV3CmJkcl9uZXcyJENsdXN0ZXJzIDwtIGVkYWRzYWxfY2x1c3RlcjEkY2x1c3RlcgpiZHJfbmV3MzwtYmRyX25ldzIgJT4lIGdyb3VwX2J5KENsdXN0ZXJzKSAlPiUgZHBseXI6OnN1bW1hcml6ZShzYWxhcmlvX2RpYXJpbz1tYXgoc2FsYXJpb19kaWFyaW8pKSAlPiUgYXJyYW5nZShkZXNjKHNhbGFyaW9fZGlhcmlvKSkKYmRyX25ldzIkQ2x1c3Rlcl9OYW1lczwtZmFjdG9yKGJkcl9uZXcyJENsdXN0ZXIsbGV2ZWxzID0gYygxLDIsMyw0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkFsdG8iLCAiQmFqbyIsIk11eSBBbHRvIiwgIlJlZ3VsYXIiKSkKYGBgCgpBZ3J1cGFyIGNsdXN0ZXJzCgpgYGB7cn0KYmRyX25ldzQ8LSBiZHJfbmV3MiAlPiUgZ3JvdXBfYnkoQ2x1c3Rlcl9OYW1lcykgJT4lIGRwbHlyOjpzdW1tYXJpemUoc2FsYXJpb19kaWFyaW89bWF4KHNhbGFyaW9fZGlhcmlvKSwgCmVkYWQ9bWVhbihlZGFkKSwKQ291bnQ9bigpKQpgYGAKClRhYmxhCmBgYHtyfQpjbHVzdGVyczwtYXMuZGF0YS5mcmFtZShiZHJfbmV3NCkKY2x1c3RlcnMKYGBgCgpHcsOhZmljYXMKCmBgYHtyfQpnZ3Bsb3QoYmRyX25ldzQsYWVzKHg9IHJlb3JkZXIoQ2x1c3Rlcl9OYW1lcyxDb3VudCkseT1Db3VudCxmaWxsPUNsdXN0ZXJfTmFtZXMpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKQpgYGAKCkFxdcOtIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGhheSB1bmEgbWF5b3IgY2FudGlkYWQgZGUgcGVyc29uYXMgZW4gZWwgY2zDunNzdGVyIGRlIG1lbm9yIGVkYWQgeSBtZW5vciBzYWxhcmlvLiBTZWd1aWRvIHBvciBlbCBuaXZlbCByZWd1bGFyLCBhbHRvIHkgbXV5IGFsdG8uCgpgYGB7cn0KZ2dwbG90KGJkcl9uZXc0LCBhZXMoeD1DbHVzdGVyX05hbWVzLHk9ZWRhZCxmaWxsPSBDbHVzdGVyX05hbWVzLGxhYmVsPXJvdW5kKGVkYWQsZGlnaXRzPTIpKSkgKyAKICBnZW9tX2NvbCgpICsgCiAgZ2VvbV90ZXh0KCkKYGBgCgpMYSBlZGFkIG3DoXhpbWEgZGVsICoqbXV5IGFsdG8qKiBlcyBkZSAzMiBhw7FvcywgZGUgKiphbHRvKiogZXMgZGUgNDYuNyBhw7FvcywgZGUgKipyZWd1bGFyKiogZXMgMzIuODUgYcOxb3MgeSBkZSAqKmJham8qKiwgbG9zIG3DoXMgasOzdmVuZXMsIDIyLjY4LgoKYGBge3J9CmdncGxvdChiZHJfbmV3NCxhZXMoeD1DbHVzdGVyX05hbWVzLHk9c2FsYXJpb19kaWFyaW8sZmlsbD0gQ2x1c3Rlcl9OYW1lcyxsYWJlbD1yb3VuZChzYWxhcmlvX2RpYXJpbyxkaWdpdHM9MikpKSArIAogIGdlb21fY29sKCkgKyAKICBnZW9tX3RleHQoKQpgYGAKICAKMS4gRW4gbGEgc2VndW5kYSBncsOhZmljYSBlbCBjbMO6c3RlciBhenVsIGVzIGVsIHF1ZSB0aWVuZSBtYXlvciBmcmVjdWVuY2lhIGNvbiB1biBwcm9tZWRpbyBkZSBlZGFkIGRlIDQ4IGHDsW9zLiAgCiAgCjIuIEVsIGNsdXN0ZXIgYWx0byBlbiBsYSBzZWd1bmRhIGdyYWZpY2EgZW5nbG9iw7MgZW4gMjIgYcOxb3MgZWwgY291bnQsIHNpbiBlbWJhcmdvIGhheSB0cmFiYWphZG9yZXMgY29uIG1lbm9yIGVkYWQuIERpY2hvIGNsdXN0ZXIgZXMgZWwgcXVlIHRpZW5lIG1lbm9zIHRyYWJhamFkb3JlcyBkZW50cm8gZGUgRm9ybS4gIAogIAozLiBFbiBjdWFudG8gYWwgc2FsYXJpbyBlbiBsYSB0ZXJjZXJhIGdyw6FmaWNhLCBlbCBkYXRvIG3DoXMgYWx0byBlcyBlbCBxdWUgYW50ZXJpb3JtZW50ZSBzZSBoYWLDrWEgbWVuY2lvbmFkbyBjb21vIHVuIGRhdG8gw7puaWNvICgkNTAwKSB5IHZlbW9zIGVsIGNvbXBvcnRhbWllbnRvIHF1ZSBzZSBnZW5lcmFiYSBlbiBlbCBtYXBlbyBkZSBjbHVzdGVycyBkb25kZSBsb3MgZGVtw6FzIHRpZW5lbiB1bmEgZGlmZXJlbmNpYSBtw61uaW1hIHNpZW5kbyBsb3MgcmVzdWx0YWRvcyAxODIuNjgsIDE4MC42OCB5IDE4My42OC4gIAogIAoqKkFuw6FsaXNpcyBjb24gdmFyaWFibGVzIGN1YWxpdGF0aXZhcyoqICAKCgpFU1RBRE8gQ0lWSUwKPGltZyBzcmM9ICIvVXNlcnMvZWxlbmF2ZWxhL0Rlc2t0b3AvQ2FwdHVyYSBkZSBQYW50YWxsYSAyMDIyLTEwLTE5IGEgbGEocykgMjEuNTguMDUucG5nIj4gICAKCjEuIE9ic2VydmFtb3MgcXVlIGVuIGVzdGUgY2FzbywgZWwgZGl2b3JjaW8gcHVlZGUgc2VyIHZpc3RvIGVuICoqYWx0byoqIHkgKipyZWd1bGFyKiosIGxvcyBkb3Mgc2VnbWVudG9zIGRlbCBtZWRpby4gIAoyLiBGdWVyYSBkZWwgZGl2b3JjaW8gZW4gKiphbHRvKiosIHBvZGVtb3MgdmVyIHF1ZSBzZSBlc3TDoSBiYXN0YW50ZSBwYXJlam8gZGUgbG9zIHNvbHRlcm9zIHkgbG9zIGNhc2Fkb3MuICAKMy4gTGEgcGVyc29uYSBxdWUgcGVydGVuZWPDrWEgYSAqKm11eSBhbHRvKiogZXMgc29sdGVyYS4gIAo0LiBEZSAqKmJham8qKiwgdmVtb3MgbGEgbWF5b3LDrWEgc29uIHNvbHRlcm9zLiAgCjUuIEZpbmFsbWVudGUsIGVuICoqcmVndWxhcioqLCBkZWphbmRvIGRlIGZ1ZXJhIGxvcyBkaXZvcmNpYWRvcywgZW5jb250cmFtb3MgcXVlIHNlIGVzdMOhIG11eSBwYXJlam8gZWwgbsO6bWVybyBkZSBjYXNhZG9zLCBzb2x0ZXJvcyB5ICBlbiB1bmnDs24gbGlicmUuICAKCkdFTkVSTyAgCjxpbWcgc3JjPSAiL1VzZXJzL2VsZW5hdmVsYS9EZXNrdG9wL0NhcHR1cmEgZGUgUGFudGFsbGEgMjAyMi0xMC0xOSBhIGxhKHMpIDIyLjA1LjU2LnBuZyI+ICAgCjEuIExhIHBlcnNvbmEgZW4gKiptdXkgYWx0byoqIGVzIGhvbWJyZS4gIAoyLiBFbiAqKmFsdG8qKiB5IGVuICoqcmVndWxhcioqLCBzZSB0aWVuZSBhIGxhIG1heW9yw61hIG11amVyZXMuICAKMy4gRmluYWxtZW50ZSwgZW4gKipiYWpvKiogc2UgdmUgbXV5IHBhcmVqbyBlbiBjdWFudG8gYSBsb3MgZ8OpbmVyb3MuICAKCgpQVUVTVE8gIAoxLiBFbiB0b2RvcyBsb3Mgc2VnbWVudG9zLCBleGNlcHRvICoqbXV5IGFsdG8qKiwgZWwgcHVlc3RvIG3DoXMgY29tw7puIGVzICJBeXVkYW50ZSBHZW5lcmFsIi4gIAoyLiBFbiAqKm11eSBhbHRvKiosIGVsIGhvbWJyZSBlc3RhYmEgZW4gZGlzZcOxby4gICAKCmBgYHtyfQpiZHJfbmV3NTwtYmRyX25ldzIgJT4lIGZpbHRlcihDbHVzdGVycz09NCB8IENsdXN0ZXJzPT0zKSAlPiUgYXJyYW5nZShDbHVzdGVycykKZ2dwbG90KGFzLmRhdGEuZnJhbWUoYmRyX25ldzUpLAogICAgICAgYWVzKHk9c2FsYXJpb19kaWFyaW8sIGF4aXMxPWdlbmVybywgYXhpczI9ZS5jaXZpbC4pKSArCiAgZ2VvbV9hbGx1dml1bShhZXMoZmlsbD1DbHVzdGVyX05hbWVzKSwgd2lkdGggPSAxLzEyKSArCiAgZ2VvbV9zdHJhdHVtKHdpZHRoID0gMS8xMiwgZmlsbCA9ICJibGFjayIsIGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV9sYWJlbChzdGF0ID0gInN0cmF0dW0iLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KHN0cmF0dW0pKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygiR2VuZXJvIiwgIkVzdGFkbyBDaXZpbCIpLCBleHBhbmQgPSBjKC4wNSwgLjA1KSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiU2V0MSIpICsKICBnZ3RpdGxlKCJTYWxhcmlvIERpYXJpbyBkZSBQYXNhZG9zIEVtcGxlYWRvcyBkZSBGb3JtIHBvciBHw6luZXJvIHkgRXN0YWRvIENpdmlsIikKYGBgCgpDb21vIHZpbW9zIHByZXZpYW1lbnRlLCBsYSBwZXJzb25hIHBlcnRlbmVjaWVudGUgYSAqKm11eSBhbHRvKiogZXMgdW4gaG9tYnJlIHNvbHRlcm8uIAoKCmBgYHtyfQpiZHJfbmV3NjwtYmRyX25ldzIgJT4lIGZpbHRlcihDbHVzdGVycz09MiB8IENsdXN0ZXJzPT0xKSAlPiUgYXJyYW5nZShDbHVzdGVycykKZ2dwbG90KGFzLmRhdGEuZnJhbWUoYmRyX25ldzYpLAogICAgICAgYWVzKHk9c2FsYXJpb19kaWFyaW8sIGF4aXMxPWdlbmVybywgYXhpczI9ZS5jaXZpbC4pKSArCiAgZ2VvbV9hbGx1dml1bShhZXMoZmlsbD1DbHVzdGVyX05hbWVzKSwgd2lkdGggPSAxLzEyKSArCiAgZ2VvbV9zdHJhdHVtKHdpZHRoID0gMS8xMiwgZmlsbCA9ICJibGFjayIsIGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV9sYWJlbChzdGF0ID0gInN0cmF0dW0iLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KHN0cmF0dW0pKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygiR2VuZXJvIiwgIkVzdGFkbyBDaXZpbCIpLCBleHBhbmQgPSBjKC4wNSwgLjA1KSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiU2V0MSIpICsKICBnZ3RpdGxlKCJTYWxhcmlvIERpYXJpbyBkZSBQYXNhZG9zIEVtcGxlYWRvcyBkZSBGb3JtIHBvciBHw6luZXJvIHkgRXN0YWRvIENpdmlsIikKYGBgCgpMYXMgcGVyc29uYXMgcXVlIHNlIGhhbiBkaXZvcmNpYWRvIGRlbCBzZWdtZW50byAqKmFsdG8qKiBoYW4gc2lkbyBtdWplcmVzLiBFeGlzdGVuIG3DoXMgcGVyc29uYXMgZW4gdW5pw7NuIGxpYnJlIGRlbCBzZWdtZW50byBkZSAqKmJham8qKjsgbXVjaG9zIGhvbWJyZXMgeSBtdWplcmVzIGRlIGVzdGUgbWlzbW8gc2VnbWVudG8gc29uIHNvbHRlcm9zIHkgc29sdGVyYXMuICAKCiAgCiMjIyAqKkluc2lnaHRzKiogICAKCiogQ29uIGxhIGJhc2UgZGUgZGF0b3MgZGUgYmFqYXMgc2UgcmVhbGl6w7MgZWwgY2zDunN0ZXIgZGUgZWRhZCB5IGR1cmFjacOzbiwgZW4gbGEgY3VhbCBzZSB0b21hcm9uIGVuIGN1ZW50YSBsYXMgdmFyaWFibGVzIGRlIGxhIGVkYWQgZGUgbG9zIGNvbGFib3JhZG9yZXMgeSBsYSBkdXJhY2nDs24gcXVlIGhhbiB0ZW5pZG8gZW4gbGEgZW1wcmVzYS4gU2UgYnVzY8OzIGVsIG7Dum1lcm8gw7NwdGltbyBkZSBjbMO6c3RlciB5IHNlIHRvbWFyb24gZW4gY3VlbnRhIGxvcyBwcmltZXJvcyA0LiBFbiBlc3RlIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGxhcyBwZXJzb25hcyBjb24gbWVub3IgZWRhZCAobG9zIG3DoXMgasOzdmVuZXMpIHNvbiBsb3MgcXVlIG1lbm9zIGR1cmFuIHBvciBsbyBnZW5lcmFsOyBvdHJvIGRhdG8gcXVlIG9ic2VydmFtb3MgcXVlIGxhcyBwZXJzb25hcyBjb24gbWF5b3IgZWRhZCBoYW4gZHVyYWRvIG3DoXMgdGllbXBvIGVuIEZvcm0geSBmb3JtYW4gcGFydGUgZGVsIHN1cGVyaW9yIDUwJSwgeSBwb3Igw7psdGltbyBvYnNlcnZhbW9zIHF1ZSBleGlzdGUgdW5hIG1heW9yIHJvdGFjacOzbiBkZSBwZXJzb25hbCBlbiBsb3MgY29sYWJvcmFkb3JlcyBqw7N2ZW5lcy4gIAoqIEVuIGVsIGFuw6FsaXNpcyBxdWUgc2UgcmVhbGl6w7MgbWVkaWFudGUgZ3LDoWZpY2FzLCBvYnR1dmltb3MgcXVlIGVuIGxvcyBjbMO6c3RlciwgcXVlIGxhIHBlcnNvbmEgY29uIG1heW9yIGVkYWQgZW4gbG9zIGV4cGVydCB0aWVuZSA2MSwgZW4gc2VuaW9yIHRpZW5lIDU3LCBlbiBqdW5pb3IgdGllbmUgMzkgeSBlbiBiZWdpbm5lciAyNywgdGFtYmnDqW4gcXVlIGVuIGxhIGVtcHJlc2EgbGEgbWF5b3LDrWEgZGUgbG9zIGNvbGFib3JhZG9yZXMgY29ycmVzcG9uZGVuIGFsIGNsw7pzdGVyIGRlIGJlZ2lubmVyLiAgIAoqIFNpZ3VpZW5kbyBjb24gZWwgYW7DoWxpc2lzIGRlIGVzdGFzIGRvcyB2YXJpYWJsZXMgeSA1IGNsdXN0ZXJzLCBub3RhbW9zIHF1ZSBlbmNvbnRyYW1vcyB1biBudWV2byBjbHVzdGVyLCBhbCBjdcOhbCBoZW1vcyBsbGFtYWRvOiAqKnVubW90aXZhdGVkKiouIExhcyBjYXJhY2Vyw61zdGljYXMgcXVlIGVuY29udHJhbW9zIGVzIHF1ZSBzb24gcGVyc29uYXMgZGUgbWVkaWFuYSBlZGFkIHF1ZSBoYW4gZHVyYWRvIG11eSBwb2NvIHRpZW1wbyBlbiBsYSBlbXByZXNhLCBwYXJ0ZSBkZWwgNSUgaW5mZXJpb3IgZGUgZHVyYWNpw7NuLiBVbiBpbnNpZ2h0IHJlbGV2YW50ZSBlcyBxdWUgU09MTyBlbiBlc3RlIGNsdXN0ZXIgc2UgcHJlc2VudGFuIGxvcyBkaXZvcmNpb3MsIHkgcXVlIGxhIGdyYW4gcGFydGUgc29uIG11amVyZXMuICrCv0ZlbsOzbWVubyBjdWx0dXJhbD8qCiogRW4gY3VhbnRvIGFsIGFuw6FsaXNpcyBkZSBsYSBkdXJhY2nDs24gZGUgbG9zIGNvbGFib3JhZG9yZXMgZW4gbGEgZW1wcmVzYSB5IHN1IHNhbGFyaW8gZW5jb250cmFtb3MgZXN0b3MgcHVudG9zIG1hcyBpbXBvcnRhbnRlczsgYSkgbGEgbWF5b3LDrWEgZGUgbG9zIGVtcGxlYWRvcyBnYW5hbiBhcnJpYmEgZGVsIHNhbGFyaW8gZGlhcmlvIHByb21lZGlvIGVzdGFibGVjaWRvIHBvciBlbCBnb2JpZXJubywgZXN0ZSBvc2NpbGEgZW50cmUgJDE1MC0kMjAwIHBlc29zIGRpYXJpb3MsIGIpIGxvcyBjb2xhYm9yYWRvcmVzIHF1ZSB0aWVuZW4gdW4gdGllbXBvIG1lZGlvIGVuIGxhIGVtcHJlc2EgYXByb3hpbWFkYW1lbnRlIGRlIHVuIGHDsW8gdGllbmUgdW4gc2FsYXJpbyBwcm9tZWRpbyBkZSAkMTY5LCBtaWVudHJhcyBsb3MgcXVlIGR1cmFuIG1lbm9zIHRpZW1wbyAoYWxyZWRlZG9yIGRlIDE0MSBkw61hcykgdGllbmVuIHVuIHNhbGFyaW8gcHJvbWVkaW8gZGUgYWxyZWRlZG9yIGRlICQxODAuICAKKiBFbCBjbMO6c3RlciBtYXMgZ3JhbmRlIGVzIGVsIGxsYW1hZG8g4oCcUmVoZW5lc+KAnSB0aWVuZSB1biB0b3RhbCBkZSBjb2xhYm9yYWRvcmVzIGRlIDE5MSwgeSBlbiBwb3JjZW50YWplIGRlIGJhamFzIHNlIHB1ZWRlIGRlY2lyIHF1ZSBlcyBlbCA4MS45NyUgdG90YWwgZGUgYmFqYXMsIGVzdG9zIHRpZW5lIGxhIGNhcmFjdGVyw61zdGljYSBkZSBzb24gbGFzIHBlcnNvbmFzIHF1ZSBlbnRyYW4geSBzYWxlbiBtdXkgcHJvbnRvIHkgdGllbmVuIHVuIHNhbGFyaW8gcG9yIGFycmliYSBkZSB0b2RvcyBsb3MgZGVtw6FzIGNsw7pzdGVyIGRlICQxODAgcGVzb3MsIHBvciBsbyBxdWUsIGVuIGVzdGUgY2zDunN0ZXIgc2UgbmVjZXNpdGEgYXBsaWNhciB1bmEgZXN0cmF0ZWdpYSBkZSBmaWRlbGl6YWNpw7NuIGRlIGxvcyBjb2xhYm9yYWRvcmVzIGRlIG90cmEgZm9ybWEgcXVlIG5vIHNlYSBjb24gdW4gYXVtZW50byBkZSBzYWxhcmlvLCBlcyBkZWNpciwgRm9ybSBwdWVkZSBpbXBsZW1lbnRhciBlc3RyYXRlZ2lhcyBjb24gcmVjb21wZW5zYXMgcXVlIHNlYW4gYSBsYXJnbyBwbGF6by4gIAoqIEFkaWNpb25hbG1lbnRlLCBlbiBlbCBhbsOhbGlzaXMgZGUgZHVyYWNpw7NuIHkgc2FsYXJpbywgZW5jb250cmFtb3MgYSB1bmEgcGVyc29uYSBxdWUgc2Ugc2Fsw61hIGRlIGxvICJub3JtYWwiLCBlc3RhIHBlcnNvbmEgZm9ybWEgc3UgIHByb3BpbyBjbMO6c3RlciAoKipleHRyYSoqKS4gU3VzIGNhcmFjdGVyw61zdGljYXMgc29uIGxhcyBzaWd1aWVudGVzOiBkdXLDsyBhcHJveCAyIGHDsW9zLCBnYW5hYmEgJDUwMCBwZXNvcyBkaWFyaW9zIGVuIHVuIHB1ZXN0byBkZSBEaXNlw7FvOyBob21icmUgeSBzb2x0ZXJvLiAKKiBDb21vIHNlIHB1ZWRlIG9ic2VydmFyIGVuIGVsIGFuYWxpc2lzIHF1ZSBzZSByZWFsaXrDsyBkZSBlZGFkIHkgc2FsYXJpbyBzZSBwdWVkZW4gZGVzdGFjYXIgbG9zIHNpZ3VpZW50ZXMgcHVudG9zOiAgCmEpIGVsIGNsdXN0ZXIgcXVlIHRpZW5lIHVuYSBtYXlvciBmcmVjdWVuY2lhIGVzIGVsIGRlIDQ4IGHDsW9zIGRlIGVkYWQsICAKYikgZWwgY2x1c3RlciBxdWUgbWFzIGNoaWNvIGVzIGRlIDIyIGHDsW9zIG8gbWVub3MgcG9yIGxvIHF1ZSBwb2RlbW9zIGRlY2lyIHF1ZSBlbiBGb3JtLCBsYSBtYXlvcsOtYSBzb24gbWF5b3JlcyBkZSAyMyBhw7FvcywKYykgZW4gY3VhbnRvIGFsIHNhbGFyaW8gdGllbmVuIHVuIHByb21lZGlvIGRlIDE4MC42OCwgcGVybyBleGlzdGUgdW4gZGF0byDDum5pY28gZWwgY3VhbCBnYW5hYmEgNTAwIChzdXMgY2FyYWN0ZXLDrXN0aWNhcyBmdWVyb24gbWVuY2lvbmFkYXMgYW50ZXJpb3JtZW50ZSkuICAgIAoKRW4gY29uY2x1c2nDs24gcGFyYSBGb3JtIHBvZGVtb3MgcGxhbnRlYXIgdW5hIGVzdHJhdGVnaWEgZGUgZXN0YW5kYXJpemFjacOzbiBkZSBzYWxhcmlvcyBkZSBhY3VlcmRvIGFsIHRpZW1wbyBxdWUgbGxldmFuIGVuIGxhIGVtcHJlc2EgeSBzdSBlZGFkLCBwYXJhIHBvZGVyIGFzw60gdGVuZXIgdW4gbWVqb3IgY29udHJvbCBkZSBnYXN0b3MgZGUgbGEgZW1wcmVzYSB5IGFwbGljYXIgbnVldmFzIGEgZXN0cmF0ZWdpYXMgYSBjYWRhIHVubyBkZSBsb3MgY2x1c3Rlci4KCgoKCiMgPGltZyBzcmM9ICIvVXNlcnMvZWxlbmF2ZWxhL0Rlc2t0b3AvQ2FwdHVyYSBkZSBQYW50YWxsYSAyMDIyLTEwLTEwIGEgbGEocykgMTQuMjQuNDQucG5nIj4gICAgCgojIyAqKklkZW50aWZpY2FjacOzbiBkZSBSZXN1bHRhZG9zIFJlbGV2YW50ZXMgeSBTdWdlcmVuY2lhcyoqCgojIyMgKioxKSBNZWFuaW5nZnVsIEluc2lnaHRzLioqCgoxLiBFbiBjdWFudG8gYSBsbyBtw6FzIGNvbcO6biBkZSBsb3MgYWN0dWFsZXMgeSBwYXNhZG9zIGNvbGFib3JhZG9yZXMgZGUgRm9ybSwgb2JzZXJ2YW1vcyBxdWUgaGF5ICh5IGh1Ymllcm9uKSBtw6FzIG11amVyZXMgcXVlIGhvbWJyZXMuIElndWFsbWVudGUsIG5vdGFtb3MgcXVlIGVsIHB1ZXN0byBtw6FzIHJlcGV0aWRvIGVzIGVsIGRlIOKAnEF5dWRhbnRlIEdlbmVyYWzigJ07IG5vdG9yaW8gdmVyIGNvbW8gZXMgZWwgcHVlc3RvIHF1ZSBtw6FzIHByZXNlbnRhIHJvdGFjacOzbi4gTGFzIHBlcnNvbmFzIHNvbHRlcmFzIHkgbGFzIHBlcnNvbmFzIGNhc2FkYXMgIHNvbiBhcXVlbGxhcyBxdWUgcHJlc2VudGFuIHVuYSBtYXlvciBwcmVzZW5jaWE7IHNpbiBlbWJhcmdvLCBjb21wYXJhbmRvIGEgZMOpY2FkYXMgcGFzYWRhcywgc2UgdmUgbXVjaGEgZ2VudGUgcXVlIHZpdmUgZW4gdW5pw7NuIGxpYnJlLiBIYXkgbcOhcyBwZXJzb25hcyBqw7N2ZW5lcyAoMjAtMzApLiAgCgoyLiBMYSBtYXlvcsOtYSBkZSBsb3MgY29sYWJvcmFkb3JlcyBhY3R1YWxlcyBpbmdyZXNhcm9uIGR1cmFudGUgbG9zIMO6bHRpbW9zIGRvcyBhw7FvcywgeSBsYSBtYXlvcsOtYSBnYW5hbiBlbnRyZSAkMTQwIHkgJDIwMCBkaWFyaW9zLiBTaW4gZW1iYXJnbywgY29tcGFyYW5kbyBsYSBhbnRpZ8O8ZWRhZCBkZSBsb3MgY29sYWJvcmFkb3Jlcywgbm90YW1vcyBxdWUgdW5hIHBlcnNvbmEgcXVlIGxsZXZhIDEyIGHDsW9zIGVuIGxhIGVtcHJlc2EgZ2FuYSBsbyBtaXNtbyBxdWUgdW5hIHBlcnNvbmEgcXVlIGxsZXZhIG1lbm9zIGRlIHVuIGHDsW8uIEluZmVyaW1vcyBxdWUgbm8gZXhpc3RlIHVuIHBsYW4gZGUgY3JlY2ltaWVudG8gbGFib3JhbCBpbnRlcm5vLCB5IHRhbXBvY28gZXhpc3RlIHVuIHBsYW4gZGUgY29tcGVuc2FjaW9uZXMgeSBiZW5lZmljaW9zIHF1ZSBpbmNlbnRpdmVuIGEgcGVybWFuZWNlciBtw6FzIHRpZW1wbyBlbiBGb3JtLiAgCgozLiBVbiBpbnNpZ2h0IHBvc2l0aXZvIGRlIEZvcm0sIGVzIHF1ZSBubyBwcmVzZW50YSAqZGlzY3JpbWluYWNpw7NuKiBwb3IgZ8OpbmVybywgcG9yICBlc3RhZG8gY2l2aWwsIG5pIHBvciBlZGFkLiAgIAoKNC4gRXhpc3RlIHVuIHByb2JsZW1hIGNvbiBsYSBjYW50aWRhZCBkZSBmYWx0YXMsIG3DoXMgZGVsIDUwJSBoYW4gc2FsaWRvIGRlIGxhIG9yZ2FuaXphY2nDs24gZGViaWRvIGEgZXN0ZSB0ZW1hLiBOb3RhbW9zIHF1ZSBubyB0aWVuZW4gaW5mb3JtYWNpw7NuIGN1YW50aXRhdGl2YSBlbiBjdWFudG8gYSBsYXMgZmFsdGFzOyBzZSBkZXNjb25vY2UgbGEgY2FudGlkYWQgbMOtbWl0ZSBkZSBmYWx0YXMgeSBwb3IgY3XDoW50byBzZSBleGNlZGVuLiBMYSBkdXJhY2nDs24gZGUgbG9zIHBhc2Fkb3MgY29sYWJvcmFkb3JlcyBubyBsbGVnYSBuaSBhbCBhw7FvIChtw6F4aW1vKSBwb3IgbG8gZ2VuZXJhbC4gVG9kb3MgZ2FuYWJhbiBhcHJveGltYWRhbWVudGUgbG8gbWlzbW8uICAKCjUuIFZpZW5kbyBlbCBwbGFuIGRlIGVudHJlZ2EsIG9ic2VydmFtb3MgcXVlIGhheSBtZXNlcyBlbiBkb25kZSBzZSB2ZcOtYW4gcGVkaWRvcyDigJxmdWVyYSBkZSBsbyBjb23Dum7igJ07IGVuIGVzdGUgY2FzbyBhbmFsaXphbW9zIGxvcyBtYXlvcmVzIGEgMzAsMDAwLiBFc3RvcyBtZXNlcyBzb246IG1hcnpvLCBhYnJpbCwganVuaW8geSBhZ29zdG8uIEVsIG1lcyBkZSBhZ29zdG8gdGFtYmnDqW4gZGVtdWVzdHJhIHRlbmVyIHVuYSBtYXlvciBjYW50aWRhZCBkZSBtZXJtYTsgZW4gc2NyYXAsIHZlbW9zIHVuIGNyZWNpbWllbnRvIGRlIGNhbnRpZGFkIGR1cmFudGUgbGEgc2VndW5kYSBtaXRhZCBkZWwgbWVzLiBTaW4gZW1iYXJnbywgdmllbmRvIGxhcyBleHBvcnRhY2lvbmVzIGRlIE3DqXhpY28sIGFnb3N0byBlcyBsYSBxdWUgbWVub3MgZXhwb3J0YWNpb25lcyBwcmVzZW50YSBkZSB0b2RvIGVsIGHDsW8uICAKCjYuIFVuIGRpZmVyZW5jaWFkbyBkZSBGb3JtIGVzIGxhIGltcG9ydGFuY2lhIHF1ZSBsZSBkYSBhIGxvcyB0aWVtcG9zOyB2ZW1vcyBjb21vIHRyYWJhamFuIHBvciBtYW50ZW5lciBlbCB0aWVtcG8gbcOtbmltbywgdGllbXBvIGRlIGNhbGlkYWQsIGVsICpkZWxheSBwZXJmb3JtYW5jZSogbG8gbcOhcyBiYWpvIHBvc2libGUuICAKCjcuIEEgcGFydGlyIGRlIGxvcyBjbMO6c3RlcnMsIG5vdGFtb3MgcXVlIGxvcyBqw7N2ZW5lcyBzb24gbG9zIHF1ZSBtZW5vcyBkdXJhbiwgYXVucXVlIHN1IHNhbGFyaW8gbm8gZXN0w6kgbXV5IGRlYmFqbyBkZSBsb3MgZGVtw6FzLiBDb21lbnRhbmRvIGNvbiBGZWxpcGUsIHNlIG1lbmNpb27DsyBjb21vIGFsIG1vbWVudG8gZGUgcmVjbHV0YXIgbXVjaG9zIG5vIGFjZXB0YW4gZW50cmFyIHBvciBlbCB0ZW1hIGRlIGxhcyBmYWx0YXMgKDMgZmFsdGFzIGNhZGEgMzAgZMOtYXMpLiBVbiBpbnNpZ2h0IHF1ZSBvYnNlcnZhbW9zIGVzIHF1ZSBlcyBkaWbDrWNpbCBjcmVhciB1biBzZW50aWRvIGRlIHBlcnRlbmVuY2lhIG8gZGVzZW8gZGUgY3JlYXIgYW50aWfDvGVkYWQgZGVudHJvIGRlIGxhIGVtcHJlc2E7IHNpbiBlbWJhcmdvLCBsb3MgZGF0b3MgZGVtdWVzdHJhbiBxdWUgRm9ybSBubyDigJxwcmVtaWHigJ0gbGEgYW50aWfDvGVkYWQuICAKCjguIEVuY29udHJhbW9zIHVuIG51ZXZvIGNsdXN0ZXIsIGFsIGN1w6FsIGhlbW9zIGxsYW1hZG86IHVubW90aXZhdGVkLiBMYXMgY2FyYWN0ZXLDrXN0aWNhcyBxdWUgZW5jb250cmFtb3MgZXMgcXVlIHNvbiBwZXJzb25hcyBkZSBtZWRpYW5hIGVkYWQgcXVlIGhhbiBkdXJhZG8gbXV5IHBvY28gdGllbXBvIGVuIGxhIGVtcHJlc2EsIHBhcnRlIGRlbCA1JSBpbmZlcmlvciBkZSBkdXJhY2nDs24uIFVuIGluc2lnaHQgcmVsZXZhbnRlIGVzIHF1ZSBTT0xPIGVuIGVzdGUgY2x1c3RlciBzZSBwcmVzZW50YW4gbG9zIGRpdm9yY2lvcywgeSBxdWUgbGEgZ3JhbiBwYXJ0ZSBzb24gbXVqZXJlcy4gKsK/RmVuw7NtZW5vIGN1bHR1cmFsPyouIEFkaWNpb25hbG1lbnRlLCBlbiBlbCBhbsOhbGlzaXMgZGUgZHVyYWNpw7NuIHkgc2FsYXJpbywgZW5jb250cmFtb3MgYSB1bmEgcGVyc29uYSBxdWUgc2Ugc2Fsw61hIGRlIGxvIOKAnG5vcm1hbOKAnSwgZXN0YSBwZXJzb25hIGZvcm1hIHN1IHByb3BpbyBjbMO6c3RlciAoZXh0cmEpLiBTdXMgY2FyYWN0ZXLDrXN0aWNhcyBzb24gbGFzIHNpZ3VpZW50ZXM6IGR1csOzIGFwcm94IDIgYcOxb3MsIGdhbmFiYSAkNTAwIHBlc29zIGRpYXJpb3MgZW4gdW4gcHVlc3RvIGRlIERpc2XDsW87IGhvbWJyZSB5IHNvbHRlcm8uICAKCipJbnNpZ2h0cyBubyByZWxhY2lvbmFkb3MgYSBGT1JNKiAgCiAgCjEuIFNlIGV4cG9ydGEgdW5hIGltcG9ydGFudGUgY2FudGlkYWQgZGUgYXV0b3MgamFwb25lc2VzIGRlc2RlIE3DqXhpY28sIGFsZ3VuYXMgbWFyY2FzIHNpZW5kbzogTmlzc2FuIHkgTWF6ZGEuIFBvc2libGUgY2xpZW50ZSBwb3RlbmNpYWwsIGlndWFsbWVudGUsIGV4aXN0ZSB1biBuaWNobyBkb25kZSBhbGd1bmFzIG1hcmNhcyBqYXBvbmVzYXMgKFRveW90YSB5IE5pc3NhbikgcXVlIGV4cG9ydGFuIG11eSBwb2NvLCBwb3IgbG8gcXVlIG5vIHB1ZWRlIHNlciBtdXkg4oCcbGxhbWF0aXZv4oCdLCBwZXJvIHBvZHLDrWEgZnVuY2lvbmFyIHBhcmEgYWRlbnRyYXJzZSBtw6FzIGEgbGEgY3VsdHVyYSB5IGVsIG1lcmNhZG8uIExhcyBtYXJjYXMgZGUgbHVqbyBubyBzZSBleHBvcnRhbiBtdWNobyBkZSBwYXJ0ZSBkZSBNw6l4aWNvLCBjb21vIHZlbW9zIGFsZ3VuYXMgbWFyY2FzIGRlIGx1am8gY29tbyBCTVcsIEF1ZGkgeSBNZXJjZWRlcyAoQWxlbWFuaWEpLiAgCgoyLiBFbiBFVUEsIHZlbW9zIHF1ZSBjdWFuZG8gc3ViZSBlbCBzYWxhcmlvIG3DrW5pbW8gcG9yIGhvcmEsICoqc3ViZSoqIGxhIHZlbnRhIGRlIGF1dG9zICpwYXNzZW5nZXIqLiBQZXJvIGN1YW5kbyBzdWJlIGVzdGEgbWlzbWEgdmFyaWFibGUsIHZlbW9zIHF1ZSAqKmJhamEqKiBsYSB2ZW50YSBkZSBhdXRvcyAqY29tZXJjaWFsZXMqLiBSZXN1bHRhcsOtYSBpbnRlcmVzYW50ZSBpbnZlc3RpZ2FyIG3DoXMgYSBmb25kbyBlc3RlIGZlbsOzbWVuby4gIFNlIGVzcGVyYSBxdWUgZW4gbG9zIHByw7N4aW1vcyBhw7FvcyBzdWJhIGxhIHZlbnRhIGRlIGF1dG9zIGVuIEVVQSwgcGVybyBxdWUgYmFqZW4gZW4gTcOpeGljby4gCgoKCiMjIyAqKjIpIFN1Z2VyZW5jaWFzLioqCgoxLiBDYXB0dXJhciBsYXMgZmVjaGFzIGVuIGZvcm1hdG8gbWV4aWNhbm86IGTDrWEvbWVzL2HDsW8uIFkgZXNwZWPDrWZpY2FyIGVzdG8gZW4gZWwgRXhjZWwuICAKMi4gTm8gcG9uZXIgbXVjaGEgYXRlbmNpw7NuIGVuIGxvICJlc3TDqXRpY28iIHF1ZSBzZSBwdWRpZXJhIHZlciBlbCBmb3JtYXRvLiBVc2FyIHByaW1lciByZW5nbMOzbiBVTklDQU1FTlRFIHBhcmEgbm9tYnJhciBsYXMgdmFyaWFibGVzLiBDb21vIHNlIHB1ZG8gdmVyIGVuIFByb2R1Y2Npw7NuLCBxdWUgbm8gcGVybWl0ZSBhbmFsaXphciBsb3MgZGF0b3MuICAKMy4gSW5jbHVpciBmZWNoYXMgZGUgbGFzIGZhbHRhcyBwYXJhIGFuYWxpemFyIGxhIGNvbmN1cnJlbmNpYSBkZSBlbGxhcyBwb3IgcGVyc29uYSwgaGFjZXIgY2x1c3RlcnMgcGFyYSB2ZXIgbGFzIGNhcmFjdGVyw61zdGljYXMgZGUgbGFzIHBlcnNvbmFzIHF1ZSBmYWx0YW4gbXVjaG8geSBwb2NvLiAgCjxpbWcgc3JjPSAiL1VzZXJzL2VsZW5hdmVsYS9EZXNrdG9wL0NhcHR1cmEgZGUgUGFudGFsbGEgMjAyMi0xMC0yMCBhIGxhKHMpIDE2LjEwLjE1LnBuZyI+ICAgCjQuIEV2aXRhciBwb25lciBhY2VudG9zIGVuIGxvcyBkYXRvcy4gIAo1LiBIb21vZ2VuaXphciBsb3MgZGF0b3M6IHRlbmVyIHVuYSBvcGNpw7NuIGRyb3AtZG93biBwYXJhIHB1ZXN0bywgbXVuaWNpcGlvLCBlc3RhZG8sIG5vbWJyZSBkZSBjbGllbnRlLCBldGMuLCBwYXJhIG5vIHRlbmVyIGRpZmVyZW50ZXMgZGUgbWFuZXJhcyBkZSBlc2NyaXR1cmEsIHkgdm9sdmVybG8gbcOhcyBmw6FjaWwgYWwgbW9tZW50byBkZSBjYXB0dXJhciBsb3MgZGF0b3MuICAKNi4gQ3VpZGFyIGVsIHJlZ2lzdHJvIGRlIGNhZGEgcGVyc29uYSwgcGFyYSBxdWUgc2UgbGxlbmVuIHRvZG9zIGxvcyBlc3BhY2lvcy4gIAo3LiBBcGFydGUgZGUgcmV1bmlyIHBlZGlkb3MgcG9yIGTDrWEsIGp1bnRhcmxvIHRvZG8gYSBmaW5hbCBkZSBtZXMuICAKCiMjICoqRGVmaW5pY2lvbmVzKioKCioqMS4gQnVzaW5lc3MgQW5hbHl0aWNzOioqIEJ1c2luZXNzIEFuYWx5dGljcyBzZSBwb2Ryw61hIHRyYWR1Y2lyLCBkZSBtYW5lcmEgbcOhcyBjZXJjYW5hLCBjb21vIOKAnGFuw6FsaXNpcyBlbXByZXNhcmlhbOKAnS4gU3UgZW5mb3F1ZSBlcyBlbiBkYXRvcyBoaXN0w7NyaWNvcyB5IGFjdHVhbGVzLCBxdWUgbWVkaWFudGUgc3UgcHJvY2VzYW1pZW50bywgZXN0dWRpbyB5IGNsYXNpZmljYWNpw7NuIGxhcyBlbXByZXNhcywgcHVlZGVuIGNvbXByZW5kZXIgc3UgZGVzZW1wZcOxbyBhY3R1YWwgeSBwYXNhZG8uIEVsIHByaW5jaXBhbCBvYmpldGl2byBkZSBlc3RlIGFuw6FsaXNpcyBlcyBwb2RlciBpZGVudGlmaWNhciBwYXRyb25lcyBvIHByb2JsZW1hcywgaWRlbnRpZmljYXIgbG9zIHJpZXNnb3MsIHkgdG9tYXIgbWVkaWRhcyBwYXJhIGRpc21pbnVpcmxvcyBvIGV2aXRhcmxvcy4gQSBtaSBtYW5lcmEgZGUgdmVybG8sIEJ1c2luZXNzIEFuYWx5dGljcyBlcyB1bmEgYnVlbmEgbWFuZXJhIGRlIGN1aWRhciBlbCBmdXR1cm8gZGUgbGEgb3JnYW5pemFjacOzbiwgYWwgcGVuc2FyIHkgY29tcHJlbmRlciBzdSBwYXNhZG8gc2UgcHVlZGUgZXZpdGFyIHJlcGV0aXIgbWFsYXNzIGRlY2lzaW9uZXMgbyBjaWVydG9zIHByb2JsZW1hcy4gVW5hIGZyYXNlIGZhbW9zYSBkZSBDaWNlcsOzbiBkaWNlIHF1ZSDigJxsb3MgcHVlYmxvcyBxdWUgb2x2aWRhbiBzdSBoaXN0b3JpYSBlc3TDoW4gY29uZGVuYWRvcyBhIHJlcGV0aXJsYeKAnTsgYWxnbyBhc8OtIHBvZHLDrWEgdmVyc2UgY29uIGxhcyBvcmdhbml6YWNpb25lcywgYWwgZW50ZW5kZXIgeSBtYW5lamFyIGJpZW4gc3UgcGFzYWRvIG1lZGlhbnRlIGRhdG9zLCBzZSBwdWVkZW4gdG9tYXIgbGFzIG1lam9yZXMgZGVjaXNpb25lcyAoYmllbiBmdW5kYW1lbnRhZGFzKSBwYXJhIGVsIGZ1dHVybyBkZXNlbXBlw7FvIHkgcGxhbmlmaWNhY2nDs24gZGUgbGEgb3JnYW5pemFjacOzbi4gIAoKKioyLiBCdXNpbmVzcyBJbnRlbGxpZ2VuY2U6KiogQnVzaW5lc3MgSW50ZWxsaWdlbmNlIHBvZHLDrWEgZGVmaW5pcnNlIGNvbW8gZWwgY29uanVudG8gZGUgaGVycmFtaWVudGFzLCB0ZWNub2xvZ8OtYXMgeSBlc3RyYXRlZ2lhcyBxdWUgcGVybWl0ZSBsYSB0cmFuc2Zvcm1hY2nDs24gZGUgZGF0b3MgZW4gaW5mb3JtYWNpw7NuLCBwYXJhIHF1ZSBlc3RhIGluZm9ybWFjacOzbiBwdWVkZSBjb252ZXJ0aXJzZSBlbiBjb25vY2ltaWVudG8uIERlIGFjdWVyZG8gYSBJQk0sIGVzdGFzIGhlcnJhbWllbnRhcyBwZXJtaXRlbiBhIGxvcyBpbnRlZ3JhbnRlcyBkZSB1bmEgb3JnYW5pemFjacOzbiBhY2NlZGVyIGEgZGlmZXJlbnRlcyB0aXBvcyBkZSBkYXRvcyAoaGlzdMOzcmljb3MgbyBhY3R1YWxlcykgY29uIGVsIGZpbiBkZSBhbmFsaXphciBsb3MgZGF0b3MgeSBvYnRlbmVyICppbnNpZ2h0cyogZGUgYWN1ZXJkbyBhIGxvIHF1ZSBuZWNlc2l0YW4gY29ub2Nlci4gIAoKKiozLiBEaWZlcmVuY2lhczoqKiAgCgoqIEVsIEJ1c2luZXNzIEFuYWx5dGljcyBjb23Dum5tZW50ZSBzZSBpZGVudGlmaWNhIGNvbW8gdW4gY29tcGxlbWVudG8gZGUgQnVzaW5lc3MgSW50ZWxsaWdlbmNlLCBwb3IgbG8gcXVlIG5vIHB1ZWRlIHNlciBjb25zaWRlcmFkbyBsbyBtaXNtby4gIAoKKiBCdXNpbmVzcyBJbnRlbGxpZ2VuY2Ugc2UgZW5mb2NhIG3DoXMgYSBsYSAqKnJlY29sZWNjacOzbioqIHkgbGEgKipjZW50cmFsaXphY2nDs24qKiBkZSBsb3MgZGF0b3MsIGZhY2lsaXRhIGVsIHRyYWJham8geSBlbCBhY2Nlc28gYSBsb3MgZGF0b3M7IEJ1c2luZXNzIEFuYWx5dGljcyBzZSBlbmNhcmdhIGRlIGFuYWxpemFyIGVzdGEgaW5mb3JtYWNpw7NuIHkgdXNhcmxhIGVuIG1vZGVsb3MgZGVzY3JpcHRpdm9zLCBwcmVkaWN0aXZvcyB5IGV4cGxpY2F0aXZvcywgY29uIGVsIGZpbiBkZSBhcG95YXIgYSBlbGxhIHRvbWEgZGUgZGVjaXNpb25lcyBkZSBhbGd1bmEgb3JnYW5pemFjacOzbi4gIAoKKiBCdXNpbmVzcyBJbnRlbGxpZ2VuY2UgYnVzY2EgY29udGVzdGFyIGVsICoqV0hBVD8qKiB5ICoqSE9XPyoqIGRlIGxhcyBjb3NhczsgQnVzaW5lc3MgQW5hbHl0aWNzIGNvbnRlc3RhIGFsICoqV0hZPyoqIGxhcyBjb3NhcyBzdWNlZGVuIGVuIGxhIGVtcHJlc2EsIHBhcmEgcG9kZXIgcHJlZGVjaXIgY29tcG9ydGFtaWVudG9zLiAgCgoqIEJ1c2luZXNzIEludGVsbGlnZW5jZSBzZSBvcmllbnRhIGFsIHBhc2FkbzsgQnVzaW5lc3MgQW5hbHl0aWNzIHNlIG9yaWVudGEgYWwgZnV0dXJvLiAgCgoKIyMgKipLZXkgUGVyZm9ybWFuY2UgSW5kaWNhdG9ycyAoS1BJcykqKgoKKirCv1F1w6kgZXMgdW4gS1BJPyoqICAKVW4gS2V5IFBlcmZvcm1hbmNlIEluZGljYXRyciAoS1BJKSwgbyBiaWVuIHVuIEluZGljYWRvciBDbGF2ZSBkZSBSZW5kaW1pZW50bywgZXMgdW5hIG3DqXRyaWNhICoqY3VhbnRpdGF0aXZhKiogcXVlIHByZXRlbmRlIG1lZGlyIGVsIHByb2dyZXNvIGRlIHVuYSBwZXJzb25hLCBlcXVpcG8gbyBlbXByZXNhIGhhY2lhIGNpZXJ0byBvYmpldGl2bzsgbG8gcXVlIGJ1c2NhIGVzIGF5dWRhciBhIHRvbWFyIG1lam9yZXMgZGVjaXNpb25lcyByZXNwZWN0byBhbCBlc3RhZG8gYWN0dWFsIGRlIHVuYSBwZXJzb25hLCBwcm9jZXNvLCBlc3RyYXRlZ2lhLiBVbiBidWVuIEtQSSBheXVkYSBhIGxvZ3JhciBvYmpldGl2b3MgZXN0cmF0w6lnaWNvcywgaW5mb3JtYSBzb2JyZSBsYSBwbGFuaWZpY2FjacOzbiBkZSByZWN1cnNvLCBlcyBtZWRpYmxlIChjdWFudGl0YXRpdm8pLCBzZSBwdWVkZSBoYWNlciBzZWd1aW1pZW50bywgYXl1ZGEgYSBhY2VyY2FyIGEgb2JqZXRpdm9zIGNvbmNyZXRvcyB5IGVzcGVjw61maWNvcy4gTGEgZXZhbHVhY2nDs24gY29uc3RhbnRlIGRlIGxvcyBLUElzIChvIGNvbiB1biBwZXJpb2RvIGRlIHRpZW1wbyBkZWxpbWl0YWRvKSBlcyBkZSB2aXRhbCBpbXBvcnRhbmNpYTsgcmVjb3JkYW5kbyBhbCBjb25zdWx0b3IgZGUgbmVnb2Npb3MgUGV0ZXIgRHJ1Y2tlcjogKirigJxMbyBxdWUgbm8gc2UgcHVlZGUgbWVkaXIgbm8gc2UgcHVlZGUgZ2VzdGlvbmFy4oCdLioqICAKCioqS1BJcyBwYXJhIEZPUk06KiogIAoqKjEuICDDjW5kaWNlIGRlIHJvdGFjacOzbiBkZSBwZXJzb25hbDoqKiBkZWJpZG8gYWwgYW7DoWxpc2lzIHJlYWxpemFkbywgb2JzZXJ2YW1vcyBxdWUgbGEgcmV0ZW5jacOzbiBkZSBwZXJzb25hbCBlcyBiYWphLCBzb2JyZXRvZG8gZW4gasOzdmVuZXMsIGFsIHZlciBsb3MgcG9jb3MgZMOtYXMgZGUgZHVyYWNpw7NuIGVuIEZvcm0uIFBvciBsbyB0YW50bywgc2UgcmVjb21pZW5kYSBhIEZvcm0gbGxldmFyIGVsIMOtbmRpY2UgZGUgcm90YWNpw7NuIENBREEgTUVTLiAgIAoKTGEgZsOzcm11bGEgcGFyYSBvYnRlbmVyIGVzdGUgw61uZGljZSBlczogbsO6bWVybyBhY3R1YWwgKGRlbCBtZXMpIGRlIGVtcGxlYWRvcyBxdWUgcmVudW5jaWFyb24gbyBzZSBmdWVyb24gLyAoKGVtcGxlYWRvcyBhbCAgaW5pY2lvIGRlIG1lcysgZW1wbGVhZG9zIGFsIGZpbmFsIGRlIG1lcykvMikgeCAxMDAgIAoKU2UgYnVzY2EgcXVlIGRpc21pbnV5YSBlbCDDrW5kaWNlIGRlIHJvdGFjacOzbiBhIHBhcnRpciBkZSBsb3MgbWVzZXMsIHB1ZXN0byBxdWUgcG9kcsOtYSBzaWduaWZpY2FyIHF1ZSBsYXMgZXN0cmF0ZWdpYXMgZGlzZcOxYWRhcyAgZSBpbXBsZW1lbnRhZGFzIGVzdMOhbiBmdW5jaW9uYW5kby4gU2luIGVtYmFyZ28sIGVzIGltcG9ydGFudGUgcXVlIG51bmNhIGxsZWd1ZSBhIHNlciAwLCBkZWJpZG8gYSBxdWUgcG9kcsOtYSBzZXIgaW5kaWNhdGl2byBkZSBmYWx0YSBkZSBkaW5hYW1pc21vIGVtcHJlc2FyaWFsLiAgCgpTZSBwcm9wb25lIGVzdGUgw61uZGljZSBwYXJhIGNvbm9jZXIgIGNvbiBtYXlvciBleGFjdGl0dWQgcXVlIHRhbnRhIGdlbnRlIHNlIHZhIHkgcXVlIHRhbnRhIGdlbnRlIHNlIHF1ZWRhIG1lcyBjb24gbWVzLCB5IHRyYXRhciBkZSBtZWpvcmFyIGFzcGVjdG9zIHBhcmEgbWVqb3JhciBsYSByZXRlbmNpw7NuLiAKCioqMi4gw41uZGljZSBkZSBBdXNlbnRpc21vOioqIHVuIHByb2JsZW1hIHF1ZSBwdWRpbW9zIHZlciBlbiBsYXMgcGVyc29uYXMgcXVlIHBhcnRpZXJvbiBkZSBGb3JtLCBlcyBxdWUgbGEgbWF5b3LDrWEgZnVlIHBvciBleGNlc28gZGUgZmFsdGEuIFBvciBsbyB0YW50bywgc2UgZGVzZWEgc2FiZXIgcmVhbG1lbnRlIFFVw4kgdGFudG8gYXVzZW50aXNtbyBleGlzdGUuIERlYmlkbyBhIHF1ZSBmdWUgbWVuY2lvbmFkbyBjb21vIHVuIHByb2JsZW1hIGFjdHVhbCB5IHJlbGV2YW50ZSwgc2UgcXVpZXJlIG1lZGlybG8gZGUgbWFuZXJhIFFVSU5DRU5BTCwgcGFyYSB2ZXIgcXVlIHRhbnRvIHZhcsOtYSBkZW50cm8gZGUgdW4gbWVzLCB5IGZpbmFsbWVudGUsIGVuIGVsIGHDsW8uIFNlIGVzcGVyYSBxdWUgYSBwYXJ0aXIgZGUgZXN0ZSBpbmRpY2Fkb3Igc2UgcHVlZGEgbGxlZ2FyIGEgdG9tYXIgdW5hIGRlY2lzacOzbiB5IHJlYWxpemFyIGVzdHJhdGVnaWFzIHBhcmEgZGlzbWludWlyIGVsIGF1c2VudGlzbW8uICAKCkxhIGbDs3JtdWxhIHBhcmEgbWVkaXIgZWwgYXVzZW50aXNtbz0gKE7Dum1lcm8gVG90YWwgZGUgSG9yYXMgQXVzZW50aXNtbyAvIE7Dum1lcm8gVG90YWwgZGUgSG9yYXMgUGxhbmlmaWNhZGFzKSB4IDEwMCAgCgpDb21vIHBvZGVtb3MgdmVyLCBlc3RvIHNlIG1pZGUgY29uIGxhcyBob3Jhcy4gU2UgcHJvcG9uZSBhIEZvcm0gdG9tYXIgZW4gY3VlbnRhIGxhcyBob3JhcyBkaWFyaWFzLCB5IGVuIGNhc28gZGUgZmFsdGFyLCB0ZW5lciBlbiBlbCBleGNlbCB5YSBsaXN0YSBsYSBmw7NybXVsYSAoMSB4IGhvcmFzIGRlbCBkw61hIGxhYm9yYWwpLiBJZ3VhbG1lbnRlLCB0b21hciBlbiBjdWVudGEgbGFzIHZlY2VzIHF1ZSBsYXMgcGVyc29uYXMgc2FsZW4gYW50ZXMgZW4gZWwgZMOtYSAocXVlIHNlIHZhbiBhIG1lZGlvIGTDrWEpLiBFbiBlc3RlIGNhc28sIGRlYmlkbyBhIHNlciBlbCBwcmltZXJvLCBubyBzZSB0b21hIGVuIGN1ZW50YSBsYSBmYWx0YSBqdXN0aWZpY2FkYSB5IGxhIG5vIGp1c3RpZmljYWRhLiAgIAoKKiozLiBOaXZlbCBkZSBzZXJ2aWNpbyAoZW50cmVnYXMgYSB0aWVtcG8pOioqIGNvbW8gc2UgbWVuY2lvbsOzLCBlbCB0aWVtcG8gZGUgZW50cmVnYSB5IHN1cyByZXRyYXNvcyAoKmRlbGF5IHBlcmZvcm1hbmNlKikgZXMgdW4gdGVtYSBxdWUgbGEgZW1wcmVzYSBsZSBkYSBtdWNoYSBpbXBvcnRhbmNpYSwgcHVlc3RvIHF1ZSBwYXJhIGVsbG9zIGVzIHZpdGFsIGVsIHRlbWEgZGUgc2VydmljaW8gb3BvcnR1bm8uIFBvciBsbyB0YW50bywgcHJvcG9uZ28gZWwgS1BJIGRlICJOaXZlbCBkZSBzZXJ2aWNpbyIsIGVsIGN1w6FsIHByZXRlbmRlIG1lZGlyIGxhcyBlbnRyZWdhcyBhIHRpZW1wby4gU2kgYmllbiwgRm9ybSB5YSBwb3NlZSBkYXRvcyBxdWUgcGVybWl0ZW4gdmVyIGVsIHRpZW1wbyBkZSBlbnRyZWdhLCBlc3RlIEtQSSBsbyBoYXLDrWEgbcOhcyBwdW50dWFsLiAgCgpNaWRlIGVsIHBvcmNlbnRhamUgZGUgcGVkaWRvcyBxdWUgc2UgZW50cmVnYW4gYSB0aWVtcG8gZnJlbnRlIGEgbGEgZmVjaGEgeSBob3JhIGRlIGNvbXByb21pc28gY29uIGVsIGNsaWVudGUuIExhIGbDs3JtdWxhIGVzOiAobsO6bWVybyBkZSBwZWRpZG9zIGVudHJlZ2Fkb3MgdGllbXBvL27Dum1lcm8gdG90YWwgZGUgcGVkaWRvcyBlbnRyZWdhZG9zKSDDlyAxMDAgICAKICAKICAKICAKTWVkaWFudGUgZXN0b3MgdHJlcyBLUEksIGRvcyBlbmZvY2Fkb3MgZW4gUkgsIHkgb3RybyBlbmZvY2FkbyBtw6FzIGFsIHNlcnZpY2lvIGFsIGNsaWVudGUgeSBzdSBsYSBlZmljaWVuY2lhLiAgCgoKCgoKIyMgKipSZWZlcmVuY2lhcyoqCiogRG9jdVNpZ24gQ29udHJpYnV0b3IuICgyMDIxLCBGZWJydWFyeSAxMCkuIEJ1c2luZXNzIGFuYWx5dGljczogwr9RdcOpIGVzIHkgY8OzbW8gcHVlZGUgYXl1ZGFyIGEgdHUgZW1wcmVzYT8gRG9jdVNpZ24uIFJldHJpZXZlZCBPY3RvYmVyIDIwLCAyMDIyLCBmcm9tIGh0dHBzOi8vd3d3LmRvY3VzaWduLm14L2Jsb2cvQnVzaW5lc3MtYW5hbHl0aWNzIAoKKiBJQk0uIChuLmQuKS4gwr9RdcOpIGVzIGJ1c2luZXNzIGFuYWx5dGljcz8gSUJNLiBSZXRyaWV2ZWQgT2N0b2JlciAyMCwgMjAyMiwgZnJvbSBodHRwczovL3d3dy5pYm0uY29tL214LWVzL2FuYWx5dGljcy9idXNpbmVzcy1hbmFseXRpY3MgCgoqIENvbmFzYS4gKG4uZC4pLiDCv0N1w6FsIGVzIGxhIGRpZmVyZW5jaWEgZW50cmUgYnVzaW5lc3MgaW50ZWxsaWdlbmNlIHkgYnVzaW5lc3MgYW5hbHl0aWNzPyBDb25hc2EgSVQgV29ya3MuIFJldHJpZXZlZCBPY3RvYmVyIDIwLCAyMDIyLCBmcm9tIGh0dHBzOi8vY29uYXNhLmdydXBvY2liZXJub3MuY29tL2Jsb2cvY3VhbC1lcy1sYS1kaWZlcmVuY2lhLWVudHJlLWJ1c2luZXNzLWludGVsbGlnZW5jZS15LWJ1c2luZXNzLWFuYWx5dGljcyAKCiogSUJNLiAobi5kLikuIMK/UXXDqSBlcyBidXNpbmVzcyBpbnRlbGxpZ2VuY2UgeSBDw7NtbyBGdW5jaW9uYT8gSUJNLiBSZXRyaWV2ZWQgT2N0b2JlciAyMCwgMjAyMiwgZnJvbSBodHRwczovL3d3dy5pYm0uY29tL214LWVzL3RvcGljcy9idXNpbmVzcy1pbnRlbGxpZ2VuY2UgCgoqIFNpbmVyZ2lhLiAobi5kLikuIMK/UXXDqSBlcyBCdXNpbmVzcyBJbnRlbGxpZ2VuY2U/IFNpbmVyZ2lhLiBSZXRyaWV2ZWQgT2N0b2JlciAyMCwgMjAyMiwgZnJvbSBodHRwczovL3d3dy5zaW5uZXh1cy5jb20vYnVzaW5lc3NfaW50ZWxsaWdlbmNlLyAKCiogTWFydGlucywgSi4gKDIwMjIpLiBRdcOpIGVzIHVuIGtwaSwgcGFyYSBxdcOpIHNpcnZlIHkgY8OzbW8gdXRpbGl6YXJsbyBlbiB0dSBwcm95ZWN0by4gQXNhbmEuIFJldHJpZXZlZCBPY3RvYmVyIDIwLCAyMDIyLCBmcm9tIGh0dHBzOi8vYXNhbmEuY29tL2VzL3Jlc291cmNlcy9rZXktcGVyZm9ybWFuY2UtaW5kaWNhdG9yLWtwaSAKCgoKCgoKCgoKCgoKCgoK