Introducción

FORM es una empresa nuevoleonense fundada en 2011, la cual atiende principalmente a clientes dentro del sector automotriz ofreciendo el desarrollo de empaques personalizados con el objetivo de generar ahorros, eficiencias, productividad y rentabilidad.

El reporte que se presentará a continuación se realizó con el objetivo de analizar las diferentes áreas de la empresa mediante el uso de sus bases de datos internas y el uso de información externa para lograr mejoras en sus procesos.

Instalar librerias:

library(data.table)
library(dplyr)
library(plyr)
library(ggplot2)
library(janitor)
library(psych)
library(tidyverse)
library(janitor)
library(knitr)
library(descr)
library(tidyr)
library(plotrix)
library(graphics)
library(lubridate)
library(flextable)
library(crosstable)
library(tseries)
library(forecast)
library(astsa)
library(jtools)
library(lmtest)
library(car)
library(olsrr)
library(corrplot)
library(foreign)
library(factoextra)  
library(ggalluvial)
library(viridis)      
library(scales) 
#install.packages("caret")
library(caret)

Sección 1

a) RH - Colaboradores

1. Limpieza, Transformación, y Organización de Bases de Datos:

Notas: Antes de importar la base de datos se optó por:
Remover columnas geográficas e innecesarias.
Homogenizar ciertas categorías como “estado civil” y “puesto”.
Reemplazar cifras de salarios inusuales por el promedio.
Agregar columna de “Edad” y “Antiguedad”.

Técnica 1. Importar la base de datos y limpiar los nombres de las columnas con la función “clean_names”:

# file.choose()
bd <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\FORM BASES DE DATOS\\FORM - Recursos Humanos - colaboradores.csv")
colaboradores <- clean_names(bd)

Técnica 2. Borrar columnas innecesarias:

colaboradores <- subset(colaboradores, select = -c(no_empleado, nombre_completo, fecha_alta, mano_de_obra))

Técnica 3. Reemplezar NAs con el promedio en la columna de “Salario Diario” (BD - Colaboradores).

colaboradores$salario_diario[is.na(colaboradores$salario_diario)]<-median(colaboradores$salario_diario, na.rm = TRUE)

Se utiliza la mediana en vez del promedio para descartar datos “outliers” y normalmente toma los datos más populares.

Técnica 4. Convertir las variables a factores o números enteros (BD - Colaboradores).

colaboradores$edad<-as.numeric(colaboradores$edad)
colaboradores$genero<-as.factor(colaboradores$genero)
colaboradores$antiguedad<-as.numeric(colaboradores$antiguedad)
colaboradores$puesto<-as.factor(colaboradores$puesto)
colaboradores$salario_diario<-as.numeric(colaboradores$salario_diario)
colaboradores$estado_civil<-as.factor(colaboradores$estado_civil)
summary (colaboradores)
##       edad             genero     antiguedad                  puesto  
##  Min.   :18.00   FEMENINO :61   Min.   : 0.000   AYUDANTE GENERAL:67  
##  1st Qu.:26.00   MASCULINO:52   1st Qu.: 0.000   COSTURERA       :10  
##  Median :34.00                  Median : 0.000   SOLDADOR        : 5  
##  Mean   :36.06                  Mean   : 1.425   CHOFER          : 4  
##  3rd Qu.:45.00                  3rd Qu.: 2.000   RESIDENTE       : 4  
##  Max.   :73.00                  Max.   :12.000   EXTERNO         : 2  
##                                                  (Other)         :21  
##  salario_diario       estado_civil
##  Min.   :144.4   Casado     :44   
##  1st Qu.:176.7   Divorciado : 3   
##  Median :180.7   Soltero    :46   
##  Mean   :179.1   Union Libre:20   
##  3rd Qu.:180.7                    
##  Max.   :337.1                    
## 

Exportar base de datos limpia

write.csv(colaboradores, file="colaboradores_bd_limpia.csv", row.names = FALSE)

2. Clasificación de variables:

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

describeData(colaboradores,head=1,tail=1)
## n.obs =  113 of which  113   are complete cases.   Number of variables =  6  of which all are numeric  TRUE  
##                variable # n.obs type                    H1               T1
## edad                    1   113    1                    67               50
## genero*                 2   113    2             MASCULINO         FEMENINO
## antiguedad              3   113    1                    12                0
## puesto*                 4   113    2 SUPERVISOR DE MAQUINA AYUDANTE GENERAL
## salario_diario          5   113    1                176.72           180.68
## estado_civil*           6   113    2               Soltero           Casado

La base de datos de “RH - Colaboradores” cuenta con 6 variables y 113 registros en cada columna, siendo 678 registros totales.

Tabla de clasificación:

Variable<-c("Edad", "Género", "Antigüedad", "Puesto", "Salario Diario", "Estado Civil" )
Tipo<-c("Cuantitativa Discreta", "Cualitativa", "Cuantitativa Discreta", "Cualitativa", "Cuantitativa continua", "Cualitativa")
Medida <-c("Años", "NA", "Años", "NA", "Pesos Mexicanos", "NA")
table<-data.frame(Variable,Tipo, Medida)
table
##         Variable                  Tipo          Medida
## 1           Edad Cuantitativa Discreta            Años
## 2         Género           Cualitativa              NA
## 3     Antigüedad Cuantitativa Discreta            Años
## 4         Puesto           Cualitativa              NA
## 5 Salario Diario Cuantitativa continua Pesos Mexicanos
## 6   Estado Civil           Cualitativa              NA
knitr::kable(table)
Variable Tipo Medida
Edad Cuantitativa Discreta Años
Género Cualitativa NA
Antigüedad Cuantitativa Discreta Años
Puesto Cualitativa NA
Salario Diario Cuantitativa continua Pesos Mexicanos
Estado Civil Cualitativa NA

3. Gráficos estadísticos descriptivos:

Gráfica de Barras (Salario y Género):

histStack(colaboradores$salario_diario, colaboradores$genero, xlab= "Salario Diario", ylab= "Cantidad de Empleados", legend.pos="topright")

Con este gráfico podemos observar que solo hay un colaborador que gana más de $300 diarios y es una Mujer. Asimismo, observamos que la mayoría de los colaboradores ganan entre 170-200 pesos diarios, habiendo más mujeres en este segmento.

Histograma (Edad):

hist(x = colaboradores$edad, main = "Histograma de Edad", 
     xlab = "Edad", ylab = "Frecuencia",
     col = "orange")

Con este gráfico podemos observar que la mayoría de los colaboradores de FORM tienen entre 25 a 30 años.

Gráfica de barras (Género, estado civíl y salario diario):

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

Con este gráfico podemos observar que los que están casados y solteros suelen ganar más que los que están divorciados y en unión libre. Además, los hombres casados y las mujeres solteras son los que ganan más.

4. Principales Estadísticos Descriptivos:

Media:

media_edad <- mean(colaboradores$edad)
media_edad
## [1] 36.06195
media_antiguedad <- mean(colaboradores$antiguedad)
media_antiguedad
## [1] 1.424779
media_salario <- mean(colaboradores$salario_diario)
media_salario
## [1] 179.1261

Mediana:

mediana_edad <- median(colaboradores$edad)
mediana_edad
## [1] 34
mediana_antiguedad <- median(colaboradores$antiguedad)
mediana_antiguedad
## [1] 0
mediana_salario <- median(colaboradores$salario_diario)
mediana_salario 
## [1] 180.68

Moda:

mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}
moda_edad <- mode(colaboradores$edad)
moda_edad
## [1] 32
moda_antiguedad <- mode(colaboradores$antiguedad)
moda_antiguedad
## [1] 0
moda_salario <- mode(colaboradores$salario_diario)
moda_salario 
## [1] 180.68

Desviación Estándar:

varianza_edad <-var(colaboradores$edad)
varianza_edad
## [1] 165.0051
varianza_antiguedad <-var(colaboradores$antiguedad)
varianza_antiguedad
## [1] 6.353666
varianza_salario <-var(colaboradores$salario_diario)
varianza_salario
## [1] 589.7666
desviacion_edad <- sqrt(varianza_edad)
desviacion_edad
## [1] 12.84543
desviacion_antiguedad <- sqrt(varianza_antiguedad)
desviacion_antiguedad
## [1] 2.520648
desviacion_salario <- sqrt(varianza_salario)
desviacion_salario
## [1] 24.28511

Tabla de frecuencia (análisis estadístico):

Variable <-c("Edad","Antigüedad","Salario Diario")
Promedio <-c("36.06", "1.42","179.09")
Moda <-c("32","0","180.68")
Mediana <-c("34","0","180.68")
Desviación_Estándar <-c ("12.84","2.52","24.28")
tabla <-data.frame(Variable,Promedio, Moda, Mediana, Desviación_Estándar)
tabla
##         Variable Promedio   Moda Mediana Desviación_Estándar
## 1           Edad    36.06     32      34               12.84
## 2     Antigüedad     1.42      0       0                2.52
## 3 Salario Diario   179.09 180.68  180.68               24.28
knitr::kable(tabla)
Variable Promedio Moda Mediana Desviación_Estándar
Edad 36.06 32 34 12.84
Antigüedad 1.42 0 0 2.52
Salario Diario 179.09 180.68 180.68 24.28

De estos estadísticos descriptivos podemos destacar principalmente los siguientes hallazgos:

1. La edad promedio de los colaboradores actuales es de 36 años.

2. La mayoría de los colaboradores gana $180.68 pesos diarios (mediana).

3. Existen colaboradores muy antiguos y muy recientes, sin emBargo, la mayoría no tiene ni 1 año aún en la empresa (mediana).

b) RH - Bajas

1. Limpieza, Transformación, y Organización de Bases de Datos:

Notas: Antes de importar la base de datos se optó por:
Homogenizar ciertas categorías como “estado civil” y “puesto”.
Agregar columna de “Edad”.
Nombrar la columna de “no_dias”.

Técnica 1: Importar la base de datos y limpiar los nombres de las columnas con la función “clean_names”:

bd1 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\FORM BASES DE DATOS\\FORM - Recursos Humanos - BAJAS BUENA.csv")
bajas <- clean_names(bd1)
summary(bajas)
##   apellidos            nombre          fecha_de_nacimiento      edad      
##  Length:236         Length:236         Length:236          Min.   :19.00  
##  Class :character   Class :character   Class :character    1st Qu.:23.00  
##  Mode  :character   Mode  :character   Mode  :character    Median :29.00  
##                                                            Mean   :31.08  
##                                                            3rd Qu.:37.00  
##                                                            Max.   :61.00  
##                                                            NA's   :3      
##     genero              rfc            fecha_de_alta      motivo_de_baja    
##  Length:236         Length:236         Length:236         Length:236        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     no_dias            baja              puesto          departamento      
##  Min.   :   0.00   Length:236         Length:236         Length:236        
##  1st Qu.:   9.00   Class :character   Class :character   Class :character  
##  Median :  19.00   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :  79.76                                                           
##  3rd Qu.:  49.00                                                           
##  Max.   :1966.00                                                           
##  NA's   :23                                                                
##  no_seguro_social   salario_diario_imss factor_cred_infonavit
##  Length:236         Min.   :144.4       Length:236           
##  Class :character   1st Qu.:180.7       Class :character     
##  Mode  :character   Median :180.7       Mode  :character     
##                     Mean   :178.0                            
##                     3rd Qu.:180.7                            
##                     Max.   :500.0                            
##                                                              
##  n_credito_infonavit lugar_de_nacimiento     curp              calle          
##  Length:236          Length:236          Length:236         Length:236        
##  Class :character    Class :character    Class :character   Class :character  
##  Mode  :character    Mode  :character    Mode  :character   Mode  :character  
##                                                                               
##                                                                               
##                                                                               
##                                                                               
##  numero_interno       colonia          codigo_postal       municipio        
##  Length:236         Length:236         Length:236         Length:236        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     estado          estado_civil       tarjeta_cuenta    
##  Length:236         Length:236         Length:236        
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
##                                                          
## 

Técnica 2. Borrar columnas innecesarias:

bajas <- subset(bajas, select = -c(apellidos, nombre, fecha_de_nacimiento, rfc, departamento, no_seguro_social, factor_cred_infonavit, n_credito_infonavit, lugar_de_nacimiento, curp, calle, numero_interno, colonia, codigo_postal, municipio, estado, tarjeta_cuenta, fecha_de_alta, baja))

**Técnica 3. Reemplazar NA´s con la mediana en “no_dias” y reemplazar con el promedio en “edad” para no tener tantos espacios en blanco:

bajas$no_dias[is.na(bajas$no_dias)]<-median(bajas$no_dias, na.rm = TRUE)
bajas$edad[is.na(bajas$edad)]<-mean(bajas$edad, na.rm = TRUE)

Técnica 4:Convertir las variables a factores, números enteros o fechas:

bajas$edad<-as.numeric(bajas$edad)
bajas$genero<-as.factor(bajas$genero)
bajas$motivo_de_baja<-as.factor(bajas$motivo_de_baja)
bajas$no_dias<-as.numeric(bajas$no_dias)
bajas$puesto<-as.factor(bajas$puesto)
bajas$salario_diario_imss<-as.numeric(bajas$salario_diario_imss)
bajas$estado_civil<-as.factor(bajas$estado_civil)
summary (bajas)
##       edad             genero                motivo_de_baja    no_dias       
##  Min.   :19.00   FEMENINO :139   ABANDONO           :  1    Min.   :   0.00  
##  1st Qu.:23.00   MASCULINO: 97   BAJA POR FALTAS    :141    1st Qu.:   9.00  
##  Median :29.00                   JUBILACION         :  1    Median :  19.00  
##  Mean   :31.08                   RENUNCIA VOLUNTARIA: 85    Mean   :  73.84  
##  3rd Qu.:37.00                   TERMINO DE CONTRATO:  8    3rd Qu.:  42.50  
##  Max.   :61.00                                              Max.   :1966.00  
##                                                                              
##                    puesto    salario_diario_imss      estado_civil
##  AYUDANTE GENERAL     :179   Min.   :144.4       CASADO     : 64  
##  COSTURERA            : 11   1st Qu.:180.7       DIVORCIADO :  3  
##  SOLDADOR             : 11   Median :180.7       SOLTERO    :108  
##  AYUDANTE DE EMBARQUES:  7   Mean   :178.0       UNION LIBRE: 61  
##  MONTACARGUISTA       :  5   3rd Qu.:180.7                        
##  INSPECTOR CALIDAD    :  4   Max.   :500.0                        
##  (Other)              : 19
Exportar base de datos limpia
write.csv(bajas, file="bajas_bd_limpia.csv", row.names = FALSE)

2. Clasificación de variables:

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

describeData(bajas,head=1,tail=1)
## n.obs =  236 of which  236   are complete cases.   Number of variables =  7  of which all are numeric  TRUE  
##                     variable # n.obs type                  H1               T1
## edad                         1   236    1                  32               23
## genero*                      2   236    2           MASCULINO         FEMENINO
## motivo_de_baja*              3   236    2 RENUNCIA VOLUNTARIA  BAJA POR FALTAS
## no_dias                      4   236    1                 628                6
## puesto*                      5   236    2              DISEÑO AYUDANTE GENERAL
## salario_diario_imss          6   236    1                 500           180.68
## estado_civil*                7   236    2             SOLTERO      UNION LIBRE

La base de datos de “RH - Colaboradores” cuenta con 7 variables y 236 registros en cada columna, siendo 1,652 registros totales.

Tabla de clasificación:

Variable_bajas<-c("Edad", "Género", "Motivo de Baja", "No. Días", "Puesto", "Salario Diario", "Estado Civil" )
Tipo_bajas<-c("Cuantitativa Discreta", "Cualitativa", "Cualitativa", "Cuantitativa Discreta", "Cualitativa", "Cuantitativa continua", "Cualitativa")
Medida_bajas <-c("Años", "NA", "NA", "Años", "NA", "Pesos Mexicanos", "NA")
tabla_bajas<-data.frame(Variable_bajas,Tipo_bajas, Medida_bajas)
tabla_bajas
##   Variable_bajas            Tipo_bajas    Medida_bajas
## 1           Edad Cuantitativa Discreta            Años
## 2         Género           Cualitativa              NA
## 3 Motivo de Baja           Cualitativa              NA
## 4       No. Días Cuantitativa Discreta            Años
## 5         Puesto           Cualitativa              NA
## 6 Salario Diario Cuantitativa continua Pesos Mexicanos
## 7   Estado Civil           Cualitativa              NA
knitr::kable(tabla_bajas)
Variable_bajas Tipo_bajas Medida_bajas
Edad Cuantitativa Discreta Años
Género Cualitativa NA
Motivo de Baja Cualitativa NA
No. Días Cuantitativa Discreta Años
Puesto Cualitativa NA
Salario Diario Cuantitativa continua Pesos Mexicanos
Estado Civil Cualitativa NA

3. Gráficos estadísticos descriptivos:

Gráfico de barras (edad):

hist(bajas$edad, freq=TRUE, col="pink", main="Edad en años de los Ex-Colaboradores")

Los colaboradores que se han dado de baja en su mayoría tienen entre 20 y 30 años, siendo así los que se van más jóvenes.

Gráfica de dispersión (días de trabajo y salario):

ggplot(data=bajas, mapping = aes(no_dias, salario_diario_imss)) + geom_point(aes(color = genero)) + theme_bw()

count(bajas, "no_dias")
##    no_dias freq
## 1        0    7
## 2        1    9
## 3        2    6
## 4        3    4
## 5        4    3
## 6        5    5
## 7        6    9
## 8        7    3
## 9        8    7
## 10       9    8
## 11      10    5
## 12      11    5
## 13      12    6
## 14      13    4
## 15      14    6
## 16      15   10
## 17      16    5
## 18      18    3
## 19      19   27
## 20      20    3
## 21      21    2
## 22      22    1
## 23      23    3
## 24      25    4
## 25      26    1
## 26      27    2
## 27      28    2
## 28      29    5
## 29      30    6
## 30      31    1
## 31      32    2
## 32      33    2
## 33      34    2
## 34      35    1
## 35      36    1
## 36      37    2
## 37      39    1
## 38      40    1
## 39      41    3
## 40      47    3
## 41      48    2
## 42      49    1
## 43      50    1
## 44      51    1
## 45      54    1
## 46      57    1
## 47      58    1
## 48      59    2
## 49      60    2
## 50      63    1
## 51      64    1
## 52      71    1
## 53      75    1
## 54      77    1
## 55      86    2
## 56      89    3
## 57      91    1
## 58      93    1
## 59     102    1
## 60     103    2
## 61     104    1
## 62     112    1
## 63     117    1
## 64     125    1
## 65     129    1
## 66     141    3
## 67     148    1
## 68     149    2
## 69     161    1
## 70     169    1
## 71     197    1
## 72     224    1
## 73     239    1
## 74     251    1
## 75     361    1
## 76     366    1
## 77     421    1
## 78     423    1
## 79     455    1
## 80     602    1
## 81     628    1
## 82     646    1
## 83    1236    1
## 84    1408    1
## 85    1429    1
## 86    1966    1

Con esta gráfica podemos observar que el colaborador que ganaba más era hombre (outlier), pues fue el único colaborador que se dio de baja ganando más de 180.68 (moda). Asimismo, han habido bajas de ambos géneros, sin embargo, hay una acumulación de bajas en colaboradores que tenían muy poco tiempo trabajando en FORM, teniendo menos de 3 meses (74 días en promedio) laborando.

Gráfico de Pay cualitativo (# de bajas y motivo):

table(bajas$motivo_baja)
## < table of extent 0 >
proporciones <- c(1, 141, 1, 87, 8)
etiquetas <- c("Abandono", "Baja por Faltas", "Jubilación", "Renuncia Voluntaria", "Termino de Contrato")
pct <- round(proporciones/sum(proporciones)*100)
etiquetas <- paste(etiquetas, pct)
etiquetas <- paste(etiquetas,"%",sep="")
pie(proporciones,labels = etiquetas,
    col=rainbow(length(etiquetas)),
    main="Motivo de bajas de los Colaboradores")

Con esta gráfica podemos observar que el motivo más común de baja de los colaboradores han sido por despidos por faltas con un 59% y por renuncia voluntaria por un 37%.

Gráfico de Pay cualitativo (# de bajas y puesto):

table(bajas$puesto)
## 
## ANALISTA DE NOMINAS /AUX DE R.H.            AYUDANTE DE EMBARQUES 
##                                1                                7 
##                 AYUDANTE DE MTTO             AYUDANTE DE SOLDADOR 
##                                1                                1 
##                 AYUDANTE GENERAL                           CHOFER 
##                              179                                1 
##                         CORTADOR                        COSTURERA 
##                                1                               11 
##                           DISEÑO             ENCARGADA DE CALIDAD 
##                                1                                1 
##                      FACTURACION             GUARDIA DE SEGURIDAD 
##                                1                                2 
##                INSPECTOR CALIDAD                         LIMPIEZA 
##                                4                                1 
##                        MARCADORA                     MATERIALISTA 
##                                1                                2 
##                   MONTACARGUISTA              PRACTICANTE DE MTTO 
##                                5                                1 
##                        RESIDENTE              SERVICIO AL CLIENTE 
##                                3                                1 
##                         SOLDADOR 
##                               11
proporciones_puesto <- c(23, 179, 11, 11, 7, 5)
etiquetas_puesto <- c("Otros","Ayudante General", "Costurera", "Soldador", "Ayudante Embarques", "Montacarguista")
pct_puesto <- round(proporciones_puesto/sum(proporciones_puesto)*100)
etiquetas_puesto <- paste(etiquetas_puesto, pct_puesto)
etiquetas_puesto <- paste(etiquetas_puesto,"%",sep="")
pie(proporciones_puesto,labels = etiquetas_puesto,
    col=rainbow(length(etiquetas_puesto)),
    main="Puesto de los Ex-Colaboradores")

La mayor parte de los ex-colaboradores de Form eran Ayudantes generales (76%).

Tabla cruzada (estado civil, género y motivo de baja):

crosstable(bajas, c(estado_civil, genero), by=motivo_de_baja, total = "both") %>% as_flextable()

Con esta tabla podemos observar diferentes insights; primeramente, podemos ver que el 58.9% (139 ex-colaboradoras) de las bajas totales reportadas son de mujeres y de estas, el 64.75% se han dado de baja por faltas.

Asimismo podemos observar que el total de los casados, divorciados y en unión libre son mucho más propensos ha ser despedidos que ha renunciar voluntariamente (los que tienen o han tenido pareja) y por el otro lado, existe la misma cantidad de solteros que han renunciado y han sido dados de baja.

4. Principales Estadísticos Descriptivos:

Media:

media_edad1 <- mean(bajas$edad)
media_edad1
## [1] 31.07725
media_no_dias <- mean(bajas$no_dias)
media_no_dias
## [1] 73.83898
media_salario1 <- mean(bajas$salario_diario)
media_salario1
## [1] 177.9627

Mediana:

mediana_edad1 <- median(bajas$edad)
mediana_edad1
## [1] 29
mediana_no_dias <- median(bajas$no_dias)
mediana_no_dias
## [1] 19
mediana_salario1 <- median(bajas$salario_diario)
mediana_salario1
## [1] 180.68

Moda:

mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}
moda_edad1 <- mode(bajas$edad)
moda_edad1
## [1] 22
moda_no_dias <- mode(bajas$no_dias)
moda_no_dias
## [1] 19
moda_salario1 <- mode(bajas$salario_diario)
moda_salario1 
## [1] 180.68

Desviación Estándar:

varianza_edad1 <-var(bajas$edad)
varianza_edad1
## [1] 91.86642
varianza_no_dias <-var(bajas$no_dias)
varianza_no_dias
## [1] 45983.08
varianza_salario1 <-var(bajas$salario_diario)
varianza_salario1
## [1] 540.9893
desviacion_edad1 <- sqrt(varianza_edad1)
desviacion_edad1
## [1] 9.584697
desviacion_no_dias <- sqrt(varianza_no_dias)
desviacion_no_dias
## [1] 214.4367
desviacion_salario1 <- sqrt(varianza_salario1)
desviacion_salario1
## [1] 23.25918

Tabla Comparativa:

Variable1 <-c("Edad","Antiguedad","Salario Diario")
Promedio1 <-c("31.05","0.6","177.985")
Moda1 <-c("22","0","180.68")
Mediana1 <-c("29","0","180.68")
Desviación_Estándar1 <-c ("9.525","0.817","23.065")
tabla1 <-data.frame (Variable1,Promedio1, Moda1, Mediana1, Desviación_Estándar1)
tabla1
##        Variable1 Promedio1  Moda1 Mediana1 Desviación_Estándar1
## 1           Edad     31.05     22       29                9.525
## 2     Antiguedad       0.6      0        0                0.817
## 3 Salario Diario   177.985 180.68   180.68               23.065
knitr::kable(tabla1)
Variable1 Promedio1 Moda1 Mediana1 Desviación_Estándar1
Edad 31.05 22 29 9.525
Antiguedad 0.6 0 0 0.817
Salario Diario 177.985 180.68 180.68 23.065

De estos estadísticos descriptivos podemos destacar principalmente los siguientes hallazgos:

1. La mayor parte de los ex-colaboradores de Form tenian entre 29-31 años (más jóvenes que el promedio de los actuales).

2. La mayoría de los ex-colaboradores ganaban $180.68 pesos diarios (mediana), al igual que los actuales colaboradores.

3. Las colaboradores que se dieron de baja tenían un mínimo periodo de tiempo laborando en la empresa.

c) Delivery Plan

1. Limpieza, Organización y Transformación:

Notas: Antes de importar la base de datos se optó por:
Acumular las órdenes de entrega en meses en vez de días.

Técnica 1. Importar las bases de datos y limpiar los nombres de las columnas con la función “clean_names”:

# file.choose()
bd2 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\Delivery Plan FINAL - EQUIPO 4 .csv")
delivery_plan <- clean_names(bd2)
summary(delivery_plan)
##  cliente_planta       proyecto           id_odoo              item          
##  Length:231         Length:231         Length:231         Length:231        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##      junio             julio             agosto          septiembre  
##  Min.   :   0.00   Min.   :    0.0   Min.   :   0.00   Min.   :   0  
##  1st Qu.:   0.00   1st Qu.:    0.0   1st Qu.:   0.00   1st Qu.:   0  
##  Median :   0.00   Median :    0.0   Median :   0.00   Median :   0  
##  Mean   :  29.06   Mean   :  135.9   Mean   :  77.45   Mean   :  81  
##  3rd Qu.:   0.00   3rd Qu.:    0.0   3rd Qu.:   0.00   3rd Qu.:   0  
##  Max.   :1280.00   Max.   :13120.0   Max.   :3200.00   Max.   :3200  
##     octubre         noviembre         diciembre          ene_22       
##  Min.   :   0.0   Min.   :   0.00   Min.   :   0.0   Min.   :   0.00  
##  1st Qu.:   0.0   1st Qu.:   0.00   1st Qu.:   0.0   1st Qu.:   0.00  
##  Median :   0.0   Median :   0.00   Median :   0.0   Median :   0.00  
##  Mean   :  62.0   Mean   :  89.69   Mean   : 100.4   Mean   :  82.37  
##  3rd Qu.:  11.5   3rd Qu.:   4.00   3rd Qu.:   1.5   3rd Qu.:  26.50  
##  Max.   :3200.0   Max.   :6400.00   Max.   :6400.0   Max.   :3200.00  
##      feb_22           mar_22           abr_22            may_22       
##  Min.   :   0.0   Min.   :   0.0   Min.   :    0.0   Min.   :    0.0  
##  1st Qu.:   0.0   1st Qu.:   0.0   1st Qu.:    0.0   1st Qu.:    0.0  
##  Median :   0.0   Median :   0.0   Median :    0.0   Median :    0.0  
##  Mean   : 103.5   Mean   : 153.9   Mean   :  186.5   Mean   :  187.6  
##  3rd Qu.:   0.0   3rd Qu.:  20.0   3rd Qu.:   24.0   3rd Qu.:   22.0  
##  Max.   :9600.0   Max.   :9600.0   Max.   :16354.0   Max.   :17665.0  
##      jun_22            jul_22            ago_22            sep_22       
##  Min.   :    0.0   Min.   :    0.0   Min.   :    0.0   Min.   :    0.0  
##  1st Qu.:    0.0   1st Qu.:    0.0   1st Qu.:    0.0   1st Qu.:    0.0  
##  Median :    0.0   Median :    0.0   Median :    0.0   Median :    0.0  
##  Mean   :  171.2   Mean   :  316.9   Mean   :  131.5   Mean   :  272.3  
##  3rd Qu.:    1.0   3rd Qu.:   15.5   3rd Qu.:    0.0   3rd Qu.:    0.0  
##  Max.   :11050.0   Max.   :25900.0   Max.   :13200.0   Max.   :29379.0  
##    octubre_22          nov_22            dic_22            ene_23        
##  Min.   :    0.0   Min.   :  0.000   Min.   :  0.000   Min.   :  0.0000  
##  1st Qu.:    0.0   1st Qu.:  0.000   1st Qu.:  0.000   1st Qu.:  0.0000  
##  Median :    0.0   Median :  0.000   Median :  0.000   Median :  0.0000  
##  Mean   :  120.9   Mean   :  2.113   Mean   :  1.225   Mean   :  0.5974  
##  3rd Qu.:    0.0   3rd Qu.:  0.000   3rd Qu.:  0.000   3rd Qu.:  0.0000  
##  Max.   :16421.0   Max.   :324.000   Max.   :276.000   Max.   :138.0000  
##      feb_23      mar_23   total_meses    
##  Min.   :0   Min.   :0   Min.   :     0  
##  1st Qu.:0   1st Qu.:0   1st Qu.:    16  
##  Median :0   Median :0   Median :   115  
##  Mean   :0   Mean   :0   Mean   :  2306  
##  3rd Qu.:0   3rd Qu.:0   3rd Qu.:   724  
##  Max.   :0   Max.   :0   Max.   :136754

Técnica 2. Reenombrar cada columna (alfabéticamente) para que los meses salgan ordenados del más antiguo al mas reciente y no por abecedario:

delivery_plan <- delivery_plan %>% dplyr::rename(cliente=cliente_planta,
                                                 A_jun_21=junio,
                                                 B_jul_21=julio,
                                                 C_ago_21=agosto,
                                                 D_sep_21=septiembre,
                                                 E_oct_21=octubre,
                                                 F_nov_21=noviembre,
                                                 G_dic_21=diciembre,
                                                 H_ene_22=ene_22,
                                                 I_feb_22=feb_22,
                                                 J_mar_22=mar_22,
                                                 K_abr_22=abr_22,
                                                 L_may_22=may_22,
                                                 M_jun_22=jun_22,
                                                 N_jul_22=jul_22,
                                                 O_ago_22=ago_22,
                                                 P_sep_22=sep_22,
                                                 Q_oct_22=octubre_22,
                                                 R_nov_22=nov_22,
                                                 S_dic_22=dic_22,
                                                 T_ene_23=ene_23,
                                                 U_feb_23=feb_23,
                                                 V_mar_23=mar_23
                                                 
)
colnames(delivery_plan)
##  [1] "cliente"     "proyecto"    "id_odoo"     "item"        "A_jun_21"   
##  [6] "B_jul_21"    "C_ago_21"    "D_sep_21"    "E_oct_21"    "F_nov_21"   
## [11] "G_dic_21"    "H_ene_22"    "I_feb_22"    "J_mar_22"    "K_abr_22"   
## [16] "L_may_22"    "M_jun_22"    "N_jul_22"    "O_ago_22"    "P_sep_22"   
## [21] "Q_oct_22"    "R_nov_22"    "S_dic_22"    "T_ene_23"    "U_feb_23"   
## [26] "V_mar_23"    "total_meses"

Técnica 3. Disminuir el número de columnas, al hacer dos nuevas: “Mes” y “Unidades” y unir las columnas renombradas anteriormente haciendolas filas:

delivery_plan <- pivot_longer(delivery_plan, cols=5:14, names_to = "mes", values_to = "unidades")
str(delivery_plan)
## tibble [2,310 × 19] (S3: tbl_df/tbl/data.frame)
##  $ cliente    : chr [1:2310] "STB3" "STB3" "STB3" "STB3" ...
##  $ proyecto   : chr [1:2310] "CANASTILLA GRIS" "CANASTILLA GRIS" "CANASTILLA GRIS" "CANASTILLA GRIS" ...
##  $ id_odoo    : chr [1:2310] "15.785" "15.785" "15.785" "15.785" ...
##  $ item       : chr [1:2310] "CABLE SET CAJA BACK UP CANASTILLA" "CABLE SET CAJA BACK UP CANASTILLA" "CABLE SET CAJA BACK UP CANASTILLA" "CABLE SET CAJA BACK UP CANASTILLA" ...
##  $ K_abr_22   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ L_may_22   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ M_jun_22   : int [1:2310] 200 200 200 200 200 200 200 200 200 200 ...
##  $ N_jul_22   : int [1:2310] 900 900 900 900 900 900 900 900 900 900 ...
##  $ O_ago_22   : int [1:2310] 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 ...
##  $ P_sep_22   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ Q_oct_22   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ R_nov_22   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ S_dic_22   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ T_ene_23   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ U_feb_23   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ V_mar_23   : int [1:2310] 0 0 0 0 0 0 0 0 0 0 ...
##  $ total_meses: int [1:2310] 3850 3850 3850 3850 3850 3850 3850 3850 3850 3850 ...
##  $ mes        : chr [1:2310] "A_jun_21" "B_jul_21" "C_ago_21" "D_sep_21" ...
##  $ unidades   : int [1:2310] 0 140 530 0 200 0 150 230 500 0 ...

Técnica 4. Eliminación de ceros en la base de datos para eliminar rengoles/clientes sin registros:

delivery_plan <- filter(delivery_plan, unidades>0)

Técnica 5. Eliminación de variables irrelevantes. En este caso, dado que previamente se unieron (merge) las variables de mes, solo se consideran necesarias la variables de “Cliente”, “Fecha” y “Unidades”:

delivery_plan <- subset (delivery_plan, select = -c (proyecto, id_odoo, item, K_abr_22, L_may_22, M_jun_22,N_jul_22,O_ago_22, P_sep_22, Q_oct_22, R_nov_22,  S_dic_22, T_ene_23, V_mar_23, U_feb_23, total_meses))

Técnica 6. Convertir las variables a factores o números enteros:

delivery_plan$cliente<-as.factor(delivery_plan$cliente)
delivery_plan$mes<-as.factor(delivery_plan$mes)
delivery_plan$unidades<-as.numeric(delivery_plan$unidades)
summary(delivery_plan)
##      cliente          mes         unidades      
##  VARROC  :280   H_ene_22: 76   Min.   :    1.0  
##  TRMX    : 72   J_mar_22: 70   1st Qu.:   30.0  
##  DENSO   : 57   E_oct_21: 64   Median :   80.0  
##  YFCF    : 54   F_nov_21: 61   Mean   :  358.4  
##  HELLA   : 36   G_dic_21: 60   3rd Qu.:  300.0  
##  YF RAMOS: 28   B_jul_21: 57   Max.   :13120.0  
##  (Other) : 63   (Other) :202

Exportar base de datos limpia:

write.csv(delivery_plan, file="delivery_plan_limpia1.csv", row.names = FALSE)

2. Clasificación de variables:

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

describeData(delivery_plan,head=1,tail=1)
## n.obs =  590 of which  590   are complete cases.   Number of variables =  3  of which all are numeric  TRUE  
##           variable # n.obs type       H1       T1
## cliente*           1   590    4     STB3   VARROC
## mes*               2   590    4 B_jul_21 F_nov_21
## unidades*          3   590    4      140      200

La base de datos de “Delivery Plan” cuenta con 3 variables y 590 registros en cada columna, siendo 1,770 registros totales.

Tabla de clasificación:

Variable_delivery_plan<-c("Cliente", "Mes", "Unidades")
Tipo_plan<-c("Cualitativa", "Cualitativa", "Cuantitativa continua")
Medida_plan <-c("NA", "NA", "Unidades")
tabla3<-data.frame(Variable_delivery_plan,Tipo_plan, Medida_plan)
tabla3
##   Variable_delivery_plan             Tipo_plan Medida_plan
## 1                Cliente           Cualitativa          NA
## 2                    Mes           Cualitativa          NA
## 3               Unidades Cuantitativa continua    Unidades
knitr::kable(tabla3)
Variable_delivery_plan Tipo_plan Medida_plan
Cliente Cualitativa NA
Mes Cualitativa NA
Unidades Cuantitativa continua Unidades

3. Gráficos estadísticos descriptivos:

Boxplot (dispersión cualitativo) (unidades y mes):

boxplot(unidades ~ mes, data = delivery_plan,
        las = 3,
        main = "Unidades entregadas en cada mes",
        col = rainbow(ncol(trees)))

En esta gráfica podemos observar que en el mes con mayor cantidad de unidades entregadas y menos dispersión entre los datos fue septiembre del 2021. Sin embargo, julio del 2021 tuvo la mayor entrega siendo esta mayor a 10,000 unidades y marzo del 2022 ha tenido varias entregas dispersas mayores a su promedio de entrega.

Gráfica de barras (mes, undidades):

ggplot(delivery_plan, aes(mes,unidades)) +                                    
  geom_bar(stat = "identity") +
  scale_fill_brewer(palette = "Accent") + ggtitle("Unidades vendidas por Mes")

En julio de 2021 y marzo de 2022 hubo una mayor cantidad de piezas entregadas; esto se puede deber a las pocas entregas dispersas analizadas en la gráfica anterior que eran de muchas más unidades que el promedio. Sin embargo, no se observa ninguna tendencia fuerte de unidades entregadas por los meses.

Gráfica de pay cualitativa (cliente):

count(delivery_plan$cliente)
##                 x freq
## 1   ABC QUERETARO    1
## 2  ANTOLIN TOLUCA    9
## 3           DENSO   57
## 4           HELLA   36
## 5   INOAC POLYTEC    1
## 6            ISRI    1
## 7        MERIDIAN    5
## 8         SEGROVE    1
## 9           STB 1    1
## 10           STB3    6
## 11           STB4    4
## 12           STB5    4
## 13           STB6    2
## 14           STB8    1
## 15           STB9    1
## 16           TRMX   72
## 17            UFI    8
## 18         VARROC  280
## 19     YANFENG sm   14
## 20         YF QRO    1
## 21       YF RAMOS   28
## 22           YFCF   54
## 23           YFTO    3
proporciones_plan <- c(57, 36, 72, 280, 28, 54, 14, 49)
etiquetas_plan <- c("DENSO", "HELLA", "TRMX", "VARROC", "YF RAMOS", "YFCF", "YAFENG sm", "Otros")
pct_plan <- round(proporciones_plan/sum(proporciones_plan)*100)
etiquetas_plan <- paste(etiquetas_plan, pct_plan)
etiquetas_plan <- paste(etiquetas_plan,"%",sep="")
pie(proporciones_plan,labels = etiquetas_plan,
    col=rainbow(length(etiquetas_plan)),
    main="Clientes con pedidos para entrega")

Los clientes a los que más unidades se les registró para entrega son VARROC (47%), TRMX (12%), y DENSO (10%).

4. Principales Estadísticos Descriptivos:

Media:

media_plan <- mean(delivery_plan$unidades)
media_plan
## [1] 358.3729

Mediana:

median_plan <- median(delivery_plan$unidades)
median_plan
## [1] 80

Moda:

mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}
mode_plan <- mode(delivery_plan$unidades)  
mode_plan
## [1] 60

Desviación Estándar:

varianza_unidades_prog <- var(delivery_plan$unidades)
varianza_unidades_prog
## [1] 1005677
desviacion_unidades_prog<- sqrt(varianza_unidades_prog)
desviacion_unidades_prog
## [1] 1002.834

Tabla comparativa:

Variable_plan<-c("Unidades", "Mes", "cliente")
Promedio_plan<-c("358", "NA", "NA")
Mediana_plan<-c("80", "NA", "NA")
Moda_plan<-c("60", "NA", "NA")
Desviacion_Estandar_plan<-c("1002.834", "NA", "NA")
tabla_plan <- data.frame (Variable_plan, Promedio_plan, Mediana_plan, Moda_plan, Desviacion_Estandar_plan)
knitr::kable(tabla_plan)
Variable_plan Promedio_plan Mediana_plan Moda_plan Desviacion_Estandar_plan
Unidades 358 80 60 1002.834
Mes NA NA NA NA
cliente NA NA NA NA

De estos estadísticos descriptivos podemos destacar principalmente los siguientes hallazgos:

  1. Al haber tanta dispersión en cantidad de unidades por pedido, el promedio de unidades entregadas es de 358, sin embargo, normalmente los pedidos son de entre 60-80 unidades independientemente del cliente o mes.

d) Delivery Performance

1. Limpieza, Organización y Transformación:

Notas: Antes de importar la base de datos se optó por:
1. Pegar verticalmente las fechas y como columnas los clientes.
2. Solo tomar en cuenta la columna de “difference” para saber cual era la diferencia entre el tiempo planeado y el tiempo real de entrega.
3. En el cliente “Mahle” se sumaron las diferencias del mismo día para solo obtener un dato.

Técnica 1. Importar la base de datos y limpiar los nombres de las columnas con la función “clean_names”:

# file.choose()
bd3 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\FORM BASES DE DATOS\\FORM - Delivery Performance BD BUENA.csv")
performance <- clean_names(bd3)
summary(performance)
##     fecha              printel           mahle             magna   
##  Length:324         Min.   :0.0000   Min.   :-11.650   Min.   :0   
##  Class :character   1st Qu.:0.0000   1st Qu.:  1.800   1st Qu.:0   
##  Mode  :character   Median :0.0000   Median :  3.000   Median :0   
##                     Mean   :0.4418   Mean   :  2.364   Mean   :0   
##                     3rd Qu.:1.0000   3rd Qu.:  3.150   3rd Qu.:0   
##                     Max.   :4.4000   Max.   : 20.000   Max.   :0   
##                     NA's   :25       NA's   :25        NA's   :25  
##      varroc      x             x_1            x_2            x_3         
##  Min.   :0    Mode:logical   Mode:logical   Mode:logical   Mode:logical  
##  1st Qu.:0    NA's:324       NA's:324       NA's:324       NA's:324      
##  Median :0                                                               
##  Mean   :0                                                               
##  3rd Qu.:0                                                               
##  Max.   :0                                                               
##  NA's   :25

Técnica 2. Borrar columnas innecesarias (están en blanco):

performance <- subset(performance, select = -c(x, x_1, x_2, x_3))

Técnica 3: Convertir de caracter a fecha y a entero númerico:

performance$fecha <- as.Date(performance$fecha, format = "%d/%m/%Y")
performance$printel <- as.numeric(performance$printel)
performance$mahle <- as.numeric(performance$mahle)
performance$magna <- as.numeric(performance$magna)
performance$varroc <- as.numeric(performance$varroc)

Técnica 4: Reemplazar NAs con la mediana de cada variable.

sapply(performance, function(x) sum(is.na(x)))
##   fecha printel   mahle   magna  varroc 
##      25      25      25      25      25
performance$fecha[is.na(performance$fecha)]<-median(performance$fecha, na.rm = TRUE)
performance$printel[is.na(performance$printel)]<-median(performance$printel, na.rm = TRUE)
performance$mahle[is.na(performance$mahle)]<-median(performance$mahle, na.rm = TRUE)
performance$magna[is.na(performance$magna)]<-median(performance$magna, na.rm = TRUE)
performance$varroc[is.na(performance$varroc)]<-median(performance$varroc, na.rm = TRUE)
summary(performance)
##      fecha               printel           mahle             magna  
##  Min.   :2021-07-22   Min.   :0.0000   Min.   :-11.650   Min.   :0  
##  1st Qu.:2021-10-20   1st Qu.:0.0000   1st Qu.:  2.000   1st Qu.:0  
##  Median :2022-02-07   Median :0.0000   Median :  3.000   Median :0  
##  Mean   :2022-01-30   Mean   :0.4077   Mean   :  2.413   Mean   :0  
##  3rd Qu.:2022-04-27   3rd Qu.:0.9625   3rd Qu.:  3.100   3rd Qu.:0  
##  Max.   :2022-07-23   Max.   :4.4000   Max.   : 20.000   Max.   :0  
##      varroc 
##  Min.   :0  
##  1st Qu.:0  
##  Median :0  
##  Mean   :0  
##  3rd Qu.:0  
##  Max.   :0

Exportar base de datos limpia:

write.csv(performance, file="performance_bd_limpia.csv", row.names = FALSE)

2. Clasificación de variables:

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

describeData(performance,head=1,tail=1)
## n.obs =  324 of which  324   are complete cases.   Number of variables =  5  of which all are numeric  TRUE  
##         variable # n.obs type         H1         T1
## fecha*           1   324    4 2021-07-22 2022-02-07
## printel          2   324    1          0          0
## mahle            3   324    1       2.65          3
## magna            4   324    1          0          0
## varroc           5   324    1          0          0

#La base de datos de “Performance” cuenta con 5 variables y 324 registros en cada columna, siendo 1,620 registros totales.

Tabla de clasificación:

Variable_performance<-c(“Fecha”, “Printel”, “Mahle”, “Magna”, “Varroc”) Tipo_performance<-c(“Cualitativa”, “Cuantitativa continua”, “Cuantitativa continua”, “Cuantitativa continua”, “Cuantitativa continua”) Medida_performance <-c(“NA”, “Minutos”, “Minutos”, “Minutos”, “Minutos”) tabla2<-data.frame(Variable_performance,Tipo_performance, Medida_performance) tabla2 knitr::kable(tabla2)

3. Gráficos estadísticos descriptivos:

**Time Series Plot (fecha y cliente):

ggplot(performance,aes(x=fecha))+
  geom_line(aes(y=printel),color="blue")+
  geom_line(aes(y=mahle),color="orange")+
  geom_line(aes(y=magna),color="green")+
  geom_line(aes(y=varroc),color="green")+
  labs(x="Fecha",y="Retraso en horas", color="Legend")+
  ggtitle("Retrasos de entrega por cliente")

En esta gráfica podemos observar que FORM con los clientes Magna y Varroc no presentan horas de retraso en sus pedidos (lo cual se puede deber a que Varroc por ejemplo es su principal cliente pues es el que más pedidos le hace) sin embargo, con Mahle y Printel si han presentado retrasos considerables prinicipalmente dentro del primer trimestre del 2022. Teniendo retrasos de hasta 20 horas.

4. Principales Estadísticos Descriptivos:

Media:

media_printel <- mean(performance$printel)
media_printel
## [1] 0.4077469
media_mahle <- mean(performance$mahle)
media_mahle
## [1] 2.413333
media_magna <- mean(performance$magna)
media_magna
## [1] 0
media_varroc <- mean(performance$varroc)
media_varroc
## [1] 0

Mediana:

mediana_printel <- median(performance$printel)
mediana_printel
## [1] 0
mediana_mahle <- median(performance$mahle)
mediana_mahle
## [1] 3
mediana_magna <- median(performance$magna)
mediana_magna
## [1] 0
mediana_varroc <- median(performance$varroc)
mediana_varroc
## [1] 0

Moda:

mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}
moda_printel <- mode(performance$printel)
moda_printel
## [1] 0
moda_mahle <- mode(performance$mahle)
moda_mahle
## [1] 3
moda_magna <- mode(performance$magna)
moda_magna
## [1] 0
moda_varroc <- mode(performance$varroc)
moda_varroc
## [1] 0

Desviación Estándar:

varianza_printel <-var(performance$printel)
varianza_printel
## [1] 0.5649085
varianza_mahle <-var(performance$mahle)
varianza_mahle
## [1] 3.635233
varianza_magna <-var(performance$magna)
varianza_magna
## [1] 0
varianza_varroc <-var(performance$varroc)
varianza_varroc
## [1] 0
desviacion_printel <- sqrt(varianza_printel)
desviacion_printel
## [1] 0.751604
desviacion_mahle <- sqrt(varianza_mahle)
desviacion_mahle
## [1] 1.906629
desviacion_magna <- sqrt(varianza_magna)
desviacion_magna
## [1] 0
desviacion_varroc <- sqrt(varianza_varroc)
desviacion_varroc
## [1] 0

Tabla Comparativa:

Variable_del_performance <-c("Printel","Mahle", "Magna", "Varroc")
Promedio_performance <-c("0.4077", "2.41","0", "0")
Moda_performance <-c("0","3","0", "0")
Mediana_performance <-c("0","3","0", "0")
Desviación_Est_performance <-c ("0.751","1.906","0", "0")
tabla1 <-data.frame(Variable_del_performance,Promedio_performance, Moda_performance, Mediana_performance, Desviación_Est_performance)
tabla1
##   Variable_del_performance Promedio_performance Moda_performance
## 1                  Printel               0.4077                0
## 2                    Mahle                 2.41                3
## 3                    Magna                    0                0
## 4                   Varroc                    0                0
##   Mediana_performance Desviación_Est_performance
## 1                   0                      0.751
## 2                   3                      1.906
## 3                   0                          0
## 4                   0                          0
knitr::kable(tabla1)
Variable_del_performance Promedio_performance Moda_performance Mediana_performance Desviación_Est_performance
Printel 0.4077 0 0 0.751
Mahle 2.41 3 3 1.906
Magna 0 0 0 0
Varroc 0 0 0 0

De estos estadísticos descriptivos podemos destacar principalmente los siguientes hallazgos:

  1. FORM actualmente solo tiene retrasos significativos con su cliente Mahle, siendo estos retrasos normalmente de 3 minutos aprox (mediana y moda).

e) Producción

1. Limpieza, Organización y Transformación:

Notas: Antes de importar la base de datos se optó por:
1. Integrar los datos de Julio, Agosto y Septiembre.
2. Incorporar una nueva columna de “Fecha”.
3. Solo dejar los datos de “Tiempo de Maquinas”
4. Eliminar los NAs y variables no numéricas de las columnas de: “Láminas procesadas”.

Técnica 1. Importar la base de datos y limpiar los nombres de las columnas con la función “clean_names”:

# file.choose()
bd4 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\FORM BASES DE DATOS\\PRODUCCION_BD.csv")
produccion <- clean_names(bd4)
summary(produccion)
##     fecha             cliente            id_form            producto        
##  Length:3988        Length:3988        Length:3988        Length:3988       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##  piezas_prog          tmo_min             hr_fin          estacion_arranque 
##  Length:3988        Length:3988        Length:3988        Length:3988       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##  laminas_procesadas inicio_sep_up      fin_inicio_de_sep_up inicio_de_proceso 
##  Min.   :   0       Length:3988        Length:3988          Length:3988       
##  1st Qu.:   0       Class :character   Class :character     Class :character  
##  Median :  51       Mode  :character   Mode  :character     Mode  :character  
##  Mean   : 102                                                                 
##  3rd Qu.: 184                                                                 
##  Max.   :1263                                                                 
##                                                                               
##  fin_de_proceso     tiempo_calidad     tiempo_materiales
##  Length:3988        Length:3988        Min.   : 0.000   
##  Class :character   Class :character   1st Qu.: 0.000   
##  Mode  :character   Mode  :character   Median : 0.000   
##                                        Mean   : 3.187   
##                                        3rd Qu.: 1.000   
##                                        Max.   :60.000   
##                                        NA's   :3491

Técnica 2. Eliminar columnas innecesarias:

produccion <- subset(produccion,select = -c (id_form, producto, hr_fin, inicio_sep_up, fin_inicio_de_sep_up, inicio_de_proceso, fin_de_proceso, tiempo_materiales))

Técnica 3.Borrar los letras que contienen algunos letras registros de la variable “Pieza_Prog”:

produccion$piezas_prog <- str_replace(produccion$piezas_prog, "[aeiouLAM=NbBsS]", "")
produccion$piezas_prog <- as.integer(produccion$laminas_procesadas)

Técnica 4.Convertir de carácter a formato númerico y de fecha:

produccion$piezas_prog<-as.numeric(produccion$piezas_prog)                 
produccion$tmo_min<-as.numeric(produccion$tmo_min)                  
produccion$laminas_procesadas<-as.numeric(produccion$laminas_procesadas)   
produccion$tiempo_calidad<-as.numeric(produccion$tiempo_calidad) 
produccion$fecha <- as.Date(produccion$fecha, format = "%d/%m/%Y")
produccion$cliente<-as.factor(produccion$cliente)
produccion$estacion_arranque<-as.factor(produccion$estacion_arranque) 
summary(produccion)
##      fecha                  cliente      piezas_prog      tmo_min      
##  Min.   :2022-07-15   STABILUS 1:1343   Min.   :   0   Min.   :  0.00  
##  1st Qu.:2022-08-03   TRMX      : 686   1st Qu.:   0   1st Qu.: 15.00  
##  Median :2022-08-20   STABILUS 3: 599   Median :  51   Median : 20.00  
##  Mean   :2022-08-19   YANFENG   : 431   Mean   : 102   Mean   : 22.37  
##  3rd Qu.:2022-09-06   DENSO     : 400   3rd Qu.: 184   3rd Qu.: 25.00  
##  Max.   :2022-09-21   (Other)   : 528   Max.   :1263   Max.   :150.00  
##                       NA's      :   1                  NA's   :824     
##  estacion_arranque laminas_procesadas tiempo_calidad   
##  TROQUEL.: 503     Min.   :   0       Min.   : 0.0000  
##  ROTATIVA: 492     1st Qu.:   0       1st Qu.: 0.0000  
##  TROQUEL : 463     Median :  51       Median : 1.0000  
##  CAJAS   : 328     Mean   : 102       Mean   : 0.8631  
##  C1Y2    : 306     3rd Qu.: 184       3rd Qu.: 1.0000  
##  C3      : 306     Max.   :1263       Max.   :22.0000  
##  (Other) :1590                        NA's   :346

Técnica 5.Reemplazar por la mediana los NA´s de tmo_min y de tiempo_calidad:

produccion$tmo_min[is.na(produccion$tmo_min)]<-median(produccion$tmo_min, na.rm = TRUE)
produccion$tiempo_calidad[is.na(produccion$tiempo_calidad)]<-median(produccion$tiempo_calidad, na.rm = TRUE)
summary(produccion)
##      fecha                  cliente      piezas_prog      tmo_min      
##  Min.   :2022-07-15   STABILUS 1:1343   Min.   :   0   Min.   :  0.00  
##  1st Qu.:2022-08-03   TRMX      : 686   1st Qu.:   0   1st Qu.: 15.00  
##  Median :2022-08-20   STABILUS 3: 599   Median :  51   Median : 20.00  
##  Mean   :2022-08-19   YANFENG   : 431   Mean   : 102   Mean   : 21.88  
##  3rd Qu.:2022-09-06   DENSO     : 400   3rd Qu.: 184   3rd Qu.: 25.00  
##  Max.   :2022-09-21   (Other)   : 528   Max.   :1263   Max.   :150.00  
##                       NA's      :   1                                  
##  estacion_arranque laminas_procesadas tiempo_calidad  
##  TROQUEL.: 503     Min.   :   0       Min.   : 0.000  
##  ROTATIVA: 492     1st Qu.:   0       1st Qu.: 1.000  
##  TROQUEL : 463     Median :  51       Median : 1.000  
##  CAJAS   : 328     Mean   : 102       Mean   : 0.875  
##  C1Y2    : 306     3rd Qu.: 184       3rd Qu.: 1.000  
##  C3      : 306     Max.   :1263       Max.   :22.000  
##  (Other) :1590

Técnica 6.Eliminar registro con NA en “cliente”:

produccion <- na.omit(produccion)
summary(produccion)
##      fecha                  cliente      piezas_prog      tmo_min      
##  Min.   :2022-07-15   STABILUS 1:1343   Min.   :   0   Min.   :  0.00  
##  1st Qu.:2022-08-03   TRMX      : 686   1st Qu.:   0   1st Qu.: 15.00  
##  Median :2022-08-20   STABILUS 3: 599   Median :  51   Median : 20.00  
##  Mean   :2022-08-19   YANFENG   : 431   Mean   : 102   Mean   : 21.88  
##  3rd Qu.:2022-09-06   DENSO     : 400   3rd Qu.: 184   3rd Qu.: 25.00  
##  Max.   :2022-09-21   VARROC    : 269   Max.   :1263   Max.   :150.00  
##                       (Other)   : 259                                  
##  estacion_arranque laminas_procesadas tiempo_calidad   
##  TROQUEL.: 503     Min.   :   0       Min.   : 0.0000  
##  ROTATIVA: 492     1st Qu.:   0       1st Qu.: 1.0000  
##  TROQUEL : 463     Median :  51       Median : 1.0000  
##  CAJAS   : 328     Mean   : 102       Mean   : 0.8749  
##  C3      : 306     3rd Qu.: 184       3rd Qu.: 1.0000  
##  C1Y2    : 305     Max.   :1263       Max.   :22.0000  
##  (Other) :1590

Exportar base de datos limpia:

bd_Produccion_limpia <- produccion
write.csv(bd_Produccion_limpia, file="bd_produccion_limpia.csv", row.names = FALSE)

2. Clasificación de variables:

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

describeData(produccion,head=1,tail=1)
## n.obs =  3987 of which  3987   are complete cases.   Number of variables =  7  of which all are numeric  TRUE  
##                    variable # n.obs type         H1         T1
## fecha*                      1  3987    4 2022-07-16 2022-09-07
## cliente*                    2  3987    2       TRMX           
## piezas_prog                 3  3987    1          3          0
## tmo_min                     4  3987    1         10         20
## estacion_arranque*          5  3987    2       C1Y2   ROTATIVA
## laminas_procesadas          6  3987    1          3          0
## tiempo_calidad              7  3987    1          1          1

La base de datos de “Producción” cuenta con 7 variables y 3,987 registros en cada columna, siendo 27,909 registros totales.

Tabla de clasificación:

Variable_produccion<-c("Fecha", "Cliente", "Piezas Programadas", "Tiempo Mínimo", "Estación Arranque", "Láminas Procesadas", "Tiempo de Calidad")
Tipo_produccion<-c("Cualitativa", "Cualitativa", "Cuantitativa continua", "Cuantitativa continua", "Cualitativa", "Cuantitativa continua", "Cuantitativa continua")
Medida_produccion <-c("NA", "NA", "Unidades", "Minutos", "Na", "Unidades", "Minutos")
tabla4<-data.frame(Variable_produccion,Tipo_produccion, Medida_produccion)
tabla4
##   Variable_produccion       Tipo_produccion Medida_produccion
## 1               Fecha           Cualitativa                NA
## 2             Cliente           Cualitativa                NA
## 3  Piezas Programadas Cuantitativa continua          Unidades
## 4       Tiempo Mínimo Cuantitativa continua           Minutos
## 5   Estación Arranque           Cualitativa                Na
## 6  Láminas Procesadas Cuantitativa continua          Unidades
## 7   Tiempo de Calidad Cuantitativa continua           Minutos
knitr::kable(tabla4)
Variable_produccion Tipo_produccion Medida_produccion
Fecha Cualitativa NA
Cliente Cualitativa NA
Piezas Programadas Cuantitativa continua Unidades
Tiempo Mínimo Cuantitativa continua Minutos
Estación Arranque Cualitativa Na
Láminas Procesadas Cuantitativa continua Unidades
Tiempo de Calidad Cuantitativa continua Minutos

3. Gráficos estadísticos descriptivos:

Histograma cuantitativo (piezas producidas):

hist(produccion$piezas_prog, main = "Piezas producidas", ylab = "Frecuencia", xlab = "Piezas Programadas", col = "lightblue")

mean (produccion$piezas_prog)
## [1] 102.0105

En este gráfica podemos observar que es más común producir tandas menores a 300 piezas por cliente, pero en promedio se producen 102.08 piezas por pedido.

Gráfica de dispersión (tiempo de calidad y de producción):

plot(produccion$tiempo_calidad, produccion$tmo_min, main = "Tiempo de calidad invertido por tiempo de producción", xlab = "Tiempo de Calidad", ylab = "Tiempo de Producción", col= "blue")

En esta gráfica podemos observar que a un tiempo menor de 50h de producción, hay una mucha mayor inversión de tiempo de calidad. Y a un tiempo mayor a 60 horas, hay una mucha menor inversión de tiempo de calidad. Lo que significa que entre más se tarden haciendo un producto, menos horas de calidad le invierten, esto se puede deber a que como el proceso es más largo, le invierten más cuidado en cada paso, y cuando es más corto el proceso puede ser mas rutinario y es necesario invertirle horas extra a asegurarse que se haya producido correctamente.

Time Series plot (fecha y piezas producidas):

ggplot(produccion,aes(x=fecha))+
  geom_line(aes(y=piezas_prog),color="blue")+
  labs(x="Fecha",y="piezas programadas", color="blue")+
  ggtitle("Grafica de las piezas programadas por fecha")

En este gráfico se muestra la producción de piezas programadas en los 3 diferentes meses (julio, agosto, septiembre), donde se puede ver que hubo mayor producción en lo que fue agosto.

Gráfico de dispersión (piezas y laminadas):

ggplot(data=produccion, mapping = aes(piezas_prog, laminas_procesadas)) + geom_point(aes(color = tmo_min)) + theme_bw()

Aquí podemos observar que hay una relación lineal positiva entre las piezas producidas y la cantidad de laminas utilizadas (materia prima) realizadas en un tiempo menor a 50 horas en su mayoría.

Gráfica de dispersión (cliente y piezas producidas):

ggplot(produccion, aes(x=piezas_prog, y=cliente)) + 
  geom_point() 

En esta tabla podemos observar las veces que aparece el cliente en la base de datos de producción, viendo asi que “Stabilius 1” es el cliente que más pedidos le ordena a FORM. Seguido por TRMX, YANFENG y DENSO.

Sin embargo, en la gráfica hecha a continuación se puede observar que YANFENG es el cliente que ha hecho pedidos de más cantidad de piezas.

4. Principales Estadísticos Descriptivos:

Media:

media_piezas_prog<- mean(produccion$piezas_prog)
media_piezas_prog
## [1] 102.0105
media_tmo_min<- mean(produccion$tmo_min)
media_tmo_min 
## [1] 21.88362
media_laminas_procesadas <- mean(produccion$laminas_procesadas)
media_laminas_procesadas
## [1] 102.0105
media_tiempo_calidad<- mean(produccion$tiempo_calidad)
media_tiempo_calidad
## [1] 0.8749436

Mediana:

median_piezas_prog<- median(produccion$piezas_prog)
median_piezas_prog
## [1] 51
median_tmo_min<- median(produccion$tmo_min)
median_tmo_min 
## [1] 20
median_laminas_procesadas <- median(produccion$laminas_procesadas)
median_laminas_procesadas
## [1] 51
median_tiempo_calidad<- median(produccion$tiempo_calidad)
median_tiempo_calidad
## [1] 1

Moda:

mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}
moda_piezas_prog <- mode(produccion$piezas_prog)
moda_piezas_prog
## [1] 0
moda_tmo_min <- mode(produccion$tmo_min)
moda_tmo_min
## [1] 20
moda_laminas_procesadas<- mode(produccion$laminas_procesadas)
moda_laminas_procesadas
## [1] 0
moda_tiempo_calidad <- mode(produccion$tiempo_calidad)
moda_tiempo_calidad
## [1] 1

Desviación Estándar:

varianza_piezas_prog <-var(produccion$piezas_prog)
varianza_piezas_prog
## [1] 18060.56
varianza_tmo_min <-var(produccion$tmo_min)
varianza_tmo_min
## [1] 143.29
varianza_laminas_procesadas<-var(produccion$laminas_procesadas)
varianza_laminas_procesadas
## [1] 18060.56
varianza_tiempo_calidad <-var(produccion$tiempo_calidad)
varianza_tiempo_calidad
## [1] 0.9836445
desviacion_piezas_prog<- sqrt(varianza_piezas_prog)
desviacion_piezas_prog
## [1] 134.3896
desviacion_tmo_min <- sqrt(varianza_tmo_min)
desviacion_tmo_min
## [1] 11.97038
desviacion_laminas_procesadas <- sqrt(varianza_laminas_procesadas)
desviacion_laminas_procesadas
## [1] 134.3896
desviacion_tiempo_calidad <- sqrt(varianza_tiempo_calidad)
desviacion_tiempo_calidad
## [1] 0.9917885

Tabla de frecuencia:

Variable_prod <-c("Piezas Programadas","Tiempo Mínimo ", "Láminas Procesadas", "Tiempo Calidad")
Promedio_prod <-c("102.08", "21.89","102.08", "0.875")
Moda_prod <-c("0", "20","0", "1")
Mediana_prod <-c("51","20","51", "1")
Desviación_Estándar_prod <-c ("134.411","11.971","134.311", "0.991")
tabla5 <-data.frame(Variable_prod,Promedio_prod, Moda_prod, Mediana_prod, Desviación_Estándar_prod)
tabla5
##        Variable_prod Promedio_prod Moda_prod Mediana_prod
## 1 Piezas Programadas        102.08         0           51
## 2     Tiempo Mínimo          21.89        20           20
## 3 Láminas Procesadas        102.08         0           51
## 4     Tiempo Calidad         0.875         1            1
##   Desviación_Estándar_prod
## 1                  134.411
## 2                   11.971
## 3                  134.311
## 4                    0.991
knitr::kable(tabla5)
Variable_prod Promedio_prod Moda_prod Mediana_prod Desviación_Estándar_prod
Piezas Programadas 102.08 0 51 134.411
Tiempo Mínimo 21.89 20 20 11.971
Láminas Procesadas 102.08 0 51 134.311
Tiempo Calidad 0.875 1 1 0.991

De estos estadísticos descriptivos podemos destacar principalmente los siguientes hallazgos:

  1. FORM tiene la cantidad de prima exacta para hacer sus piezas programadas, pues sus estadísticos descriptivos son iguales.

  2. El tiempo de producción aproximado (media-mediana-moda) es de 20 horas por pedido.

  3. FORM le invierte mínimo tiempo de calidad a su producción (1 hora o menos por pedido).

f) Merma

1. Limpieza, Organización y Transformación:

Notas: Antes de importar la base de datos se optó por:
1. Eliminar los totales de cada mes.

Técnica 1.Importar la base de datos y limpiar los nombres de las columnas con la función “clean_names”:

# file.choose()
bd5 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\FORM BASES DE DATOS\\FORM - Merma (2).csv")
merma <- clean_names(bd5)
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 2. Cambiar a formato de caracter, fecha y número:

merma$kilos<-as.numeric(merma$kilos)                 
merma$fecha <- as.Date(merma$fecha, format = "%d/%m/%Y")
merma$mes<-as.factor(merma$mes)
summary(merma)
##      fecha                 mes         kilos     
##  Min.   :2022-01-11   AGOSTO :11   Min.   : 790  
##  1st Qu.:2022-03-12   FEBRERO: 6   1st Qu.:3178  
##  Median :2022-05-24   MARZO  : 6   Median :3925  
##  Mean   :2022-05-25   ABRIL  : 5   Mean   :3709  
##  3rd Qu.:2022-08-10   JULIO  : 5   3rd Qu.:4232  
##  Max.   :2022-09-21   MAYO   : 5   Max.   :6140  
##                       (Other):12

Exportar la base de datos limpia:

write.csv(merma, file ="merma_bd_limpia.csv", row.names = FALSE)

2. Clasificación de variables:

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

describeData(merma,head=1,tail=1)
## n.obs =  50 of which  50   are complete cases.   Number of variables =  3  of which all are numeric  TRUE  
##        variable # n.obs type         H1         T1
## fecha*          1    50    4 2022-01-11 2022-09-21
## mes*            2    50    2      ENERO SEPTIEMBRE
## kilos           3    50    1       5080       3739

La base de datos de “Merma” cuenta con 3 variables y 50 registros en cada columna, siendo 150 registros totales.

Tabla de clasificación:

Variable_merma<-c("Fecha", "Mes", "Kilos")
Tipo_merma<-c("Cualitativa", "Cualitativa", "Cuantitativa continua")
Medida_merma <-c("NA", "NA", "Kilogramos")
tabla6<-data.frame(Variable_merma,Tipo_merma, Medida_merma)
tabla6
##   Variable_merma            Tipo_merma Medida_merma
## 1          Fecha           Cualitativa           NA
## 2            Mes           Cualitativa           NA
## 3          Kilos Cuantitativa continua   Kilogramos
knitr::kable(tabla6)
Variable_merma Tipo_merma Medida_merma
Fecha Cualitativa NA
Mes Cualitativa NA
Kilos Cuantitativa continua Kilogramos

3. Gráficos Estadísticos Descriptivos:

Gráfico de barras (mes y kilos):

ggplot(merma, aes(x=mes, y=kilos)) +
  geom_bar(stat="identity", fill="orange") + scale_fill_grey() +
  labs(title = "Relación de los kilos de merma en el mes", 
       subtitle = "Merma empresa FORM",
       caption = "Relación", 
       x = "Mes")

Se observa que el mes en el que más se genero merma fue en Agosto, seguido por Febrero y Mayo. El mes que menos generó merma fue Septiembre y Enero.

Boxplot (kilos):

par(new = TRUE)
boxplot(merma$kilos, 
        horizontal = FALSE, 
        lwd = 2, 
        col = rgb(1, 0, 0, alpha = 0.4), 
          xlab = "Meses",  
        ylab = "Kilos de Merma",  
        main = "Promedio de kilos de merma producidos", 
        notch = TRUE, 
        border = "black",  
        outpch = 25,       
        outbg = "green",   
        whiskcol = "blue", 
        whisklty = 2,      
        lty = 1) 

En esta gráfica podemos observar el promedio de merma generada en todos los meses que está aprox en los 4000 kilos. Sin embargo, han habido veces que se llegan a generar menos de 1000 kilos o hasta 6000 kilos.

4. Principales Estadísticos Descriptivos:

Media:

media_kilos <- mean(merma$kilos)
media_kilos
## [1] 3708.52

Mediana:

mediana_kilos <- median(merma$kilos)
mediana_kilos
## [1] 3925

Moda:

mode <- function (x) {
  ux <- unique(x)
  ux [which.max(tabulate(match(x,ux)))]
}

moda_kilos <- mode(merma$kilos)
moda_kilos
## [1] 3810

Desviación Estándar:

varianza_kilos <- var(merma$kilos)
varianza_kilos 
## [1] 1048555
desviacionestandar_kilos <- sqrt(varianza_kilos)
desviacionestandar_kilos
## [1] 1023.99

Tabla de frecuencia:

Variable_merma1<-c("Kilos")
Media_merma<-c("3708.52")
Mediana_merma<-c("3925")
Moda_merma<-c("3810")
Desviacion_Estandar_merma<-c("1023.99")
tabla7<-data.frame(Variable_merma1,Media_merma,Mediana_merma,Moda_merma,Desviacion_Estandar_merma)
knitr::kable(tabla7)
Variable_merma1 Media_merma Mediana_merma Moda_merma Desviacion_Estandar_merma
Kilos 3708.52 3925 3810 1023.99

De estos estadísticos descriptivos podemos destacar principalmente los siguientes hallazgos:

  1. FORM produce en promedio 3708.52 kilos de merma por mes.

g) Scrap

1. Limpieza, Organización y Transformación:

Notas: Antes de importar la base de datos se optó por:
1. Eliminar la segunda fila de la base de datos.

Técnica 1:Importar la base de datos y limpiar los nombres de las columnas con la función “clean_names”:

bd6 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\FORM BASES DE DATOS\\FORM - Scrap.csv")
scrap <- clean_names(bd6)
summary(scrap)
##   referencia           fecha             producto            cantidad     
##  Length:250         Length:250         Length:250         Min.   : 0.000  
##  Class :character   Class :character   Class :character   1st Qu.: 1.000  
##  Mode  :character   Mode  :character   Mode  :character   Median : 2.000  
##                                                           Mean   : 6.696  
##                                                           3rd Qu.: 7.000  
##                                                           Max.   :96.000  
##  unidad_de_medida   ubicacion_de_origen ubicacion_de_desecho    estado         
##  Length:250         Length:250          Length:250           Length:250        
##  Class :character   Class :character    Class :character     Class :character  
##  Mode  :character   Mode  :character    Mode  :character     Mode  :character  
##                                                                                
##                                                                                
## 

Técnica 2. Eliminar columnas inecesarias:

scrap <- subset(scrap, select = -c (referencia, producto, ubicacion_de_desecho, estado, unidad_de_medida))

Técnica 3. Cambiar a formato de caracter, fecha y número:

scrap$cantidad<-as.numeric(scrap$cantidad)                 
scrap$fecha <- as.Date(scrap$fecha, format = "%d/%m/%Y")
scrap$ubicacion_de_origen<-as.factor(scrap$ubicacion_de_origen)
summary(scrap)
##      fecha               cantidad                 ubicacion_de_origen
##  Min.   :2022-08-01   Min.   : 0.000   Calidad/Entrega de PT: 58     
##  1st Qu.:2022-08-11   1st Qu.: 1.000   Post-Production      : 13     
##  Median :2022-08-19   Median : 2.000   Pre-Production       :179     
##  Mean   :2022-08-17   Mean   : 6.696                                 
##  3rd Qu.:2022-08-25   3rd Qu.: 7.000                                 
##  Max.   :2022-08-31   Max.   :96.000

Exportar la base de datos limpia:

write.csv(scrap, file ="scrap_bd_limpia.csv", row.names = FALSE)

2. Clasificación de variables:

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

describeData(scrap,head=1,tail=1)
## n.obs =  250 of which  250   are complete cases.   Number of variables =  3  of which all are numeric  TRUE  
##                      variable # n.obs type             H1              T1
## fecha*                        1   250    4     2022-08-06      2022-08-05
## cantidad                      2   250    1             43               7
## ubicacion_de_origen*          3   250    2 Pre-Production Post-Production

La base de datos de “Scrap” cuenta con 3 variables y 250 registros en cada columna, siendo 750 registros totales.

Tabla de Clasificación:

Variable_scrap<-c("Fecha", "Cantidad", "Ubicación de Origen")
Tipo_scrap<-c("Cualitativa", "Cuantitativa continua", "Cualitativa")
Medida_scrap <-c("NA", "Kilogramos", "NA")
tabla8<-data.frame(Variable_scrap,Tipo_scrap, Medida_scrap)
tabla8
##        Variable_scrap            Tipo_scrap Medida_scrap
## 1               Fecha           Cualitativa           NA
## 2            Cantidad Cuantitativa continua   Kilogramos
## 3 Ubicación de Origen           Cualitativa           NA
knitr::kable(tabla8)
Variable_scrap Tipo_scrap Medida_scrap
Fecha Cualitativa NA
Cantidad Cuantitativa continua Kilogramos
Ubicación de Origen Cualitativa NA

3. Análisis estadístico descriptivo:

Boxplot (cantidad y ubicación de origen):

boxplot(scrap$cantidad  ~  scrap$ubicacion_de_origen,
        col = rainbow(ncol(trees)))

Con esta gráfica podemos observar que la cantidad de unidades producidas está normalmente ubicada en “Pre-Producción” y asimismo esta categoría es la más dispersa.

Principales Estadísticos Descriptivos:

Media:

media_cantidad <- mean(scrap$cantidad)
media_cantidad
## [1] 6.696

Mediana:

mediana_cantidad <- median(scrap$cantidad)
mediana_cantidad
## [1] 2

Moda:

mode <- function (x) {
  ux <- unique(x)
  ux [which.max(tabulate(match(x,ux)))]
}

moda_cantidad <- mode(scrap$cantidad)
moda_cantidad
## [1] 1

Desviación Estándar:

varianza_cantidad <- var(scrap$cantidad)
varianza_cantidad
## [1] 140.3952
desviacionestandar_cantidad <- sqrt(varianza_cantidad)
desviacionestandar_cantidad
## [1] 11.84885

Tabla de frecuencia:

Variable_scrap1<-c("Cantidad")
Media_scrap<-c("6.96")
Mediana_scrap<-c("2")
Moda_scrap<-c("140.39")
Desviacion_Estandar_scrap<-c("11.848")
tabla9<-data.frame(Variable_scrap1,Media_scrap,Mediana_scrap,Moda_scrap,Desviacion_Estandar_scrap)
knitr::kable(tabla9)
Variable_scrap1 Media_scrap Mediana_scrap Moda_scrap Desviacion_Estandar_scrap
Cantidad 6.96 2 140.39 11.848

De estos estadísticos descriptivos podemos destacar principalmente los siguientes hallazgos:

  1. FORM tiene ubicada la mayor parte de su scrap en pre-producción. Lo que indica que es en esa parte de sus procesos que deben cuidar para evitar la generación de tanto desperdicio.

h) Base de datos Externa - “Producción de cartón en México”

1. Limpieza, Organización y Transformación:

# file.choose()
bd8 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\EVI2\\bd externa.csv")
bd_externa <- clean_names(bd8)
summary(bd_externa)
##     state_id        state               region      industry_group_id
##  Min.   : 1.00   Length:24          Min.   :1.000   Min.   :3221     
##  1st Qu.:10.75   Class :character   1st Qu.:1.000   1st Qu.:3221     
##  Median :18.00   Mode  :character   Median :2.000   Median :3221     
##  Mean   :17.62                      Mean   :2.333   Mean   :3221     
##  3rd Qu.:25.25                      3rd Qu.:3.000   3rd Qu.:3221     
##  Max.   :31.00                      Max.   :4.000   Max.   :3221     
##  industry_group     economic_unit   
##  Length:24          Min.   :  1.00  
##  Class :character   1st Qu.:  1.75  
##  Mode  :character   Median :  5.00  
##                     Mean   : 13.17  
##                     3rd Qu.:  8.75  
##                     Max.   :151.00

Técnica 1. Cambiar formato de las variables:

bd_externa$state_id<-as.numeric(bd_externa$state_id)       
bd_externa$state<-as.factor(bd_externa$state)   
bd_externa$region<-as.numeric(bd_externa$region)   
bd_externa$industry_group_id<-as.numeric(bd_externa$industry_group_id)  
bd_externa$industry_group<-as.factor(bd_externa$industry_group) 
bd_externa$economic_unit<-as.numeric(bd_externa$economic_unit)    
summary(bd_externa)
##     state_id                      state        region      industry_group_id
##  Min.   : 1.00   Aguascalientes      : 1   Min.   :1.000   Min.   :3221     
##  1st Qu.:10.75   Baja California     : 1   1st Qu.:1.000   1st Qu.:3221     
##  Median :18.00   Chihuahua           : 1   Median :2.000   Median :3221     
##  Mean   :17.62   Ciudad de Mexico    : 1   Mean   :2.333   Mean   :3221     
##  3rd Qu.:25.25   Coahuila de Zaragoza: 1   3rd Qu.:3.000   3rd Qu.:3221     
##  Max.   :31.00   Durango             : 1   Max.   :4.000   Max.   :3221     
##                  (Other)             :18                                    
##                               industry_group economic_unit   
##  Fabricacion de Pulpa, Papel y Carton:24     Min.   :  1.00  
##                                              1st Qu.:  1.75  
##                                              Median :  5.00  
##                                              Mean   : 13.17  
##                                              3rd Qu.:  8.75  
##                                              Max.   :151.00  
## 

2. Clasificación de variables:

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

describeData(bd_externa,head=1,tail=1)
## n.obs =  24 of which  24   are complete cases.   Number of variables =  6  of which all are numeric  TRUE  
##                   variable # n.obs type                                   H1
## state_id                   1    24    1                                    1
## state*                     2    24    2                       Aguascalientes
## region                     3    24    1                                    2
## industry_group_id          4    24    1                                 3221
## industry_group*            5    24    2 Fabricacion de Pulpa, Papel y Carton
## economic_unit              6    24    1                                    1
##                                                     T1
## state_id                                            31
## state*                                         Yucatan
## region                                               4
## industry_group_id                                 3221
## industry_group*   Fabricacion de Pulpa, Papel y Carton
## economic_unit                                        3

La base de datos externa cuenta con 6 variables y 24 registros en cada columna, siendo 144 registros totales.

Tabla de Clasificación:

Variable_carton<-c("State.ID","State","Region","Industry.Group.ID","Industry.Group", "Economic.Unit")
Tipo_carton<-c("Cualitativa", "Cualitativa","Cualitativa", "Cualitativa", "Cualitativa", "Cuantitativa (discreta)")
Medida_carton<-c("NA","NA","NA","NA","NA","Cartón producido")
tabla10<-data.frame(Variable_carton,Tipo_carton,Medida_carton)
knitr::kable(tabla10)
Variable_carton Tipo_carton Medida_carton
State.ID Cualitativa NA
State Cualitativa NA
Region Cualitativa NA
Industry.Group.ID Cualitativa NA
Industry.Group Cualitativa NA
Economic.Unit Cuantitativa (discreta) Cartón producido

3. Gráficos descriptivos estadísticos:

Tabla de frecuencia (estado y unidades):

prop.table(table(bd_externa$state, bd_externa$economic_unit))
##                                  
##                                            1          2          3          5
##   Aguascalientes                  0.04166667 0.00000000 0.00000000 0.00000000
##   Baja California                 0.00000000 0.00000000 0.00000000 0.00000000
##   Chihuahua                       0.00000000 0.00000000 0.00000000 0.00000000
##   Ciudad de Mexico                0.00000000 0.00000000 0.00000000 0.00000000
##   Coahuila de Zaragoza            0.04166667 0.00000000 0.00000000 0.00000000
##   Durango                         0.00000000 0.04166667 0.00000000 0.00000000
##   Estado de Mexico                0.00000000 0.00000000 0.00000000 0.00000000
##   Guanajuato                      0.00000000 0.00000000 0.00000000 0.00000000
##   Hidalgo                         0.00000000 0.00000000 0.04166667 0.00000000
##   Jalisco                         0.00000000 0.00000000 0.00000000 0.00000000
##   Michoacan de Ocampo             0.00000000 0.00000000 0.04166667 0.00000000
##   Morelos                         0.04166667 0.00000000 0.00000000 0.00000000
##   Nuevo Leon                      0.00000000 0.00000000 0.00000000 0.00000000
##   Oaxaca                          0.04166667 0.00000000 0.00000000 0.00000000
##   Puebla                          0.00000000 0.00000000 0.00000000 0.00000000
##   Queretaro                       0.00000000 0.00000000 0.00000000 0.04166667
##   San Luis Potosi                 0.00000000 0.00000000 0.00000000 0.00000000
##   Sinaloa                         0.00000000 0.00000000 0.00000000 0.04166667
##   Sonora                          0.00000000 0.00000000 0.00000000 0.04166667
##   Tabasco                         0.04166667 0.00000000 0.00000000 0.00000000
##   Tamaulipas                      0.04166667 0.00000000 0.00000000 0.00000000
##   Tlaxcala                        0.00000000 0.00000000 0.04166667 0.00000000
##   Veracruz de Ignacio de la Llave 0.00000000 0.00000000 0.00000000 0.00000000
##   Yucatan                         0.00000000 0.00000000 0.04166667 0.00000000
##                                  
##                                            6          8         11         13
##   Aguascalientes                  0.00000000 0.00000000 0.00000000 0.00000000
##   Baja California                 0.00000000 0.04166667 0.00000000 0.00000000
##   Chihuahua                       0.04166667 0.00000000 0.00000000 0.00000000
##   Ciudad de Mexico                0.00000000 0.00000000 0.00000000 0.00000000
##   Coahuila de Zaragoza            0.00000000 0.00000000 0.00000000 0.00000000
##   Durango                         0.00000000 0.00000000 0.00000000 0.00000000
##   Estado de Mexico                0.00000000 0.00000000 0.00000000 0.00000000
##   Guanajuato                      0.00000000 0.00000000 0.04166667 0.00000000
##   Hidalgo                         0.00000000 0.00000000 0.00000000 0.00000000
##   Jalisco                         0.00000000 0.00000000 0.00000000 0.04166667
##   Michoacan de Ocampo             0.00000000 0.00000000 0.00000000 0.00000000
##   Morelos                         0.00000000 0.00000000 0.00000000 0.00000000
##   Nuevo Leon                      0.00000000 0.00000000 0.00000000 0.00000000
##   Oaxaca                          0.00000000 0.00000000 0.00000000 0.00000000
##   Puebla                          0.00000000 0.00000000 0.00000000 0.00000000
##   Queretaro                       0.00000000 0.00000000 0.00000000 0.00000000
##   San Luis Potosi                 0.04166667 0.00000000 0.00000000 0.00000000
##   Sinaloa                         0.00000000 0.00000000 0.00000000 0.00000000
##   Sonora                          0.00000000 0.00000000 0.00000000 0.00000000
##   Tabasco                         0.00000000 0.00000000 0.00000000 0.00000000
##   Tamaulipas                      0.00000000 0.00000000 0.00000000 0.00000000
##   Tlaxcala                        0.00000000 0.00000000 0.00000000 0.00000000
##   Veracruz de Ignacio de la Llave 0.04166667 0.00000000 0.00000000 0.00000000
##   Yucatan                         0.00000000 0.00000000 0.00000000 0.00000000
##                                  
##                                           17         20         43        151
##   Aguascalientes                  0.00000000 0.00000000 0.00000000 0.00000000
##   Baja California                 0.00000000 0.00000000 0.00000000 0.00000000
##   Chihuahua                       0.00000000 0.00000000 0.00000000 0.00000000
##   Ciudad de Mexico                0.04166667 0.00000000 0.00000000 0.00000000
##   Coahuila de Zaragoza            0.00000000 0.00000000 0.00000000 0.00000000
##   Durango                         0.00000000 0.00000000 0.00000000 0.00000000
##   Estado de Mexico                0.00000000 0.00000000 0.04166667 0.00000000
##   Guanajuato                      0.00000000 0.00000000 0.00000000 0.00000000
##   Hidalgo                         0.00000000 0.00000000 0.00000000 0.00000000
##   Jalisco                         0.00000000 0.00000000 0.00000000 0.00000000
##   Michoacan de Ocampo             0.00000000 0.00000000 0.00000000 0.00000000
##   Morelos                         0.00000000 0.00000000 0.00000000 0.00000000
##   Nuevo Leon                      0.00000000 0.04166667 0.00000000 0.00000000
##   Oaxaca                          0.00000000 0.00000000 0.00000000 0.00000000
##   Puebla                          0.00000000 0.00000000 0.00000000 0.04166667
##   Queretaro                       0.00000000 0.00000000 0.00000000 0.00000000
##   San Luis Potosi                 0.00000000 0.00000000 0.00000000 0.00000000
##   Sinaloa                         0.00000000 0.00000000 0.00000000 0.00000000
##   Sonora                          0.00000000 0.00000000 0.00000000 0.00000000
##   Tabasco                         0.00000000 0.00000000 0.00000000 0.00000000
##   Tamaulipas                      0.00000000 0.00000000 0.00000000 0.00000000
##   Tlaxcala                        0.00000000 0.00000000 0.00000000 0.00000000
##   Veracruz de Ignacio de la Llave 0.00000000 0.00000000 0.00000000 0.00000000
##   Yucatan                         0.00000000 0.00000000 0.00000000 0.00000000

En esta tabla de frecuencia podemos obsevar que el estado que más produce cartón es Puebla (151 unidades promedio por empleado), seguido de Estado de México (43 unidades por empleado) y Nuevo León (20 unidades por empleado).

Gráfica de pay cualitativa (región):

table(bd_externa$region)
## 
## 1 2 3 4 
## 7 6 7 4
proporciones_ext <- c(7, 6, 7, 4)
etiquetas_ext <- c("Norte", "Centro/Oeste", "Centro", "Sur")
pct_ext <- round(proporciones_ext/sum(proporciones_ext)*100)
etiquetas_ext <- paste(etiquetas_ext, pct_ext)
etiquetas_ext <- paste(etiquetas_ext,"%",sep="")
pie(proporciones_ext,labels = etiquetas_ext,
    col=rainbow(length(etiquetas_ext)),
    main="Porcentaje de estados en México por regiones")

En esta gráfica se puede observar que la región Norte y Centro cuentan con más estados, seguido por la región Centro/Oeste y por último la región Sur. Esto quiere decir que la probabilidad de que haya más producción por parte de las primeras dos regiones mencionadas es mayor.

Gráfica de pay cualitativa (unidades económicas):

table(bd_externa$economic_unit)
## 
##   1   2   3   5   6   8  11  13  17  20  43 151 
##   6   1   4   3   3   1   1   1   1   1   1   1
proporciones_uni <- c(6, 1, 4, 3, 3, 1, 1, 1, 1, 1, 1, 1)
etiquetas_uni <- c("1", "2", "3", "5", "6", "8", "11", "13", "17", "20", "43", "151")
pct_uni <- round(proporciones_uni/sum(proporciones_uni)*100)
etiquetas_uni <- paste(etiquetas_uni, pct_uni)
etiquetas_uni <- paste(etiquetas_uni,"%",sep="")
pie(proporciones_uni,labels = etiquetas_uni,
    col=rainbow(length(etiquetas_uni)),
    main="Producción de unidades promedio por empleado")

La gráfica anterior nos indica el porcentaje de la producción de unidades por promedio por empleado, por ejemplo el 25% de los empleados solo produce 1 unidad, seguido por el 17% de los empleados que producen 3 unidades, y así sucesivamente.

Gráfico de dispersión (region y unidades):

ggplot(data = bd_externa, mapping = aes(region, economic_unit)) + geom_point() + theme_bw()

En este caso, podemos ver que la región 3 la cual es la Centro, cuenta con un valor atípico, el cual es Puebla con 151 unidades y es por eso que es la región que produce más cartón en México.

Gráfico de dispersión (boxplot):

boxplot(bd_externa$state_id ~ bd_externa$economic_unit, horizontal = TRUE)

En este boxplot se interpreta que muy pocos estados producen más de 6 unidades promedio por empleado.

Gráfico de barras (njdcz):

hist(x = bd_externa$region, main = "Estados por Región", 
     xlab = "Regiones", ylab = "Frecuencia",
     col = "brown")

En esta gráfica de barra podemos observar que la región 1 (Norte) y región 3 (Centro) son los que más estados tienen (teniendo 7 en total).

4. Principales Estadísticos Descriptivos:

Media:

media_economic <- mean(bd_externa$economic_unit)
media_economic
## [1] 13.16667

Mediana:

mediana_economic <- median(bd_externa$economic_unit)
mediana_economic
## [1] 5

Moda:

mode <- function (x) {
  ux <- unique(x)
  ux [which.max(tabulate(match(x,ux)))]
}

moda_economic <- mode(bd_externa$economic_unit)
moda_economic
## [1] 1

Desviación Estándar:

varianza_economic <- var(bd_externa$economic_unit)
varianza_economic
## [1] 946.1449
desviacionestandar_economic <- sqrt(varianza_economic)
desviacionestandar_economic
## [1] 30.75947

Tabla de frecuencia:

Variable_bdexterna<-c("Economic UnitS")
Media_bdexterna<-c("13.166")
Mediana_bdexterna<-c("5")
Moda_bdexterna<-c("1")
Desviacion_Estandar_bdexterna<-c("30.759")
tabla11<-data.frame(Variable_bdexterna,Media_bdexterna,Mediana_bdexterna,Moda_bdexterna,Desviacion_Estandar_bdexterna)
knitr::kable(tabla11)
Variable_bdexterna Media_bdexterna Mediana_bdexterna Moda_bdexterna Desviacion_Estandar_bdexterna
Economic UnitS 13.166 5 1 30.759

Propuestas de Mejora:

  1. Realizar un programa de incentivos para los empleados con el objetivo de incrementar su PERMANENCIA en la empresa, ofreciendoles primeramente, un mejor sueldo al incrementar su rendimiento (especialmente a los ayudantes generales que son los que han reportado más bajas), o dándoles benficios adicionales como tarjetas de despensa o reconocimientos.

  2. Estandarización y mejora del proceso de reclutamiento: Se han observado que los empleados que duran menos son los jóvenes ya que su sueldo no es muy bueno, por lo que se deberían buscar nuevos empleados que sean mayores o debería de haber un incremento de sueldo, asimismo, hablando sobre el género y estado civil, se puede observar que los empleados sin pareja son los que menos despiden (sino que suelen renunciar), deduciendo así que son los que más compromiso tienen con la empresa pues no se dan de baja en su mayoria por faltas como lo han hecho con los ex-colaboradores divorciados, casados y en unión libre. Es por esto qur FORM tiene que mejorar su proceso de selección, tomar en cuenta los insights descubiertos al reclutar e incrementar los beneficios dados a los empleados para disminuir la rotación.

  3. FORM produce una cantidad considerable de merma en sus procesos, principalmente en la pre-producción, siendo un promedio de aprox. 3708.52 kilos por mes. Se propone implementar una mejora en este proceso para evitar la generación de tantos resiudos para de la misma manera, disminuir los gastos al aprovechar al máximo la materia prima e impactar positivamente al medio ambiente.

  4. FORM debería prestar más atención en el tiempo de entrega con su cliente “MAHLE” principalmente, para reducir y evitar retrasos de entrega y cumplir con sus principios de “Rapidez” principalmente.

  5. Contactar nuevos proveedores de cartón en Puebla, para diversificar la obtención de la materia prima y en caso de haber un desabasto o un icnremento de precio considerable, tener más opciones de compra.

  6. Para próximas inversiones de nuevas plantas o sucursales de FORM, se propone invertir en lugares estratégicos, situados en estados de la región Norte y la región centro, ya que, es donde más producción de cartón existe.

  7. Empoderamiento de los clientes sobre el seguimiento de sus pedidos, Seguimiento del producto desde el punto de partida hasta el final

  8. Medición y mejora continua de la experiencia de los clientes.

Sección 2

Predicción 1: Exportación de Vehículos Ligeros en México (INEGI)

1. Limpieza de datos:

Importar base de datos:

# file.choose()
bd14 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\EVI2\\encoded-bd_prediccion.csv")
prediccion_mx <- clean_names(bd14)
summary(prediccion_mx)
##       ano            mes            venta          produccion    
##  Min.   :2006   Min.   : 1.00   Min.   : 34927   Min.   :  3722  
##  1st Qu.:2010   1st Qu.: 3.00   1st Qu.: 78543   1st Qu.:189031  
##  Median :2014   Median : 6.00   Median : 88580   Median :248433  
##  Mean   :2014   Mean   : 6.42   Mean   : 94178   Mean   :241511  
##  3rd Qu.:2018   3rd Qu.: 9.00   3rd Qu.:110134   3rd Qu.:292709  
##  Max.   :2022   Max.   :12.00   Max.   :192741   Max.   :382110  
##  NA's   :2      NA's   :2       NA's   :2        NA's   :2       
##   exportacion     tipo_de_cambio    inflacion      porcentaje_ocu 
##  Min.   : 15139   Min.   :10.09   Min.   :-0.250   Min.   :93.58  
##  1st Qu.:153219   1st Qu.:12.66   1st Qu.: 0.815   1st Qu.:95.06  
##  Median :209161   Median :13.56   Median : 1.480   Median :95.88  
##  Mean   :201664   Mean   :15.48   Mean   : 1.951   Mean   :95.76  
##  3rd Qu.:243900   3rd Qu.:19.10   3rd Qu.: 2.895   3rd Qu.:96.47  
##  Max.   :327454   Max.   :24.24   Max.   : 7.360   Max.   :97.16  
##  NA's   :2        NA's   :2       NA's   :2        NA's   :2      
##  porcentaje_desocu conf_consumidor
##  Min.   :2.840     Min.   :28.67  
##  1st Qu.:3.527     1st Qu.:36.69  
##  Median :4.125     Median :38.47  
##  Mean   :4.244     Mean   :39.15  
##  3rd Qu.:4.940     3rd Qu.:42.59  
##  Max.   :6.420     Max.   :47.83  
##  NA's   :2         NA's   :2

Técnica 1. Eliminar columnas irrelevantes:

prediccion_mx <- subset(prediccion_mx, select = -c (produccion, venta))
summary(prediccion_mx)
##       ano            mes         exportacion     tipo_de_cambio 
##  Min.   :2006   Min.   : 1.00   Min.   : 15139   Min.   :10.09  
##  1st Qu.:2010   1st Qu.: 3.00   1st Qu.:153219   1st Qu.:12.66  
##  Median :2014   Median : 6.00   Median :209161   Median :13.56  
##  Mean   :2014   Mean   : 6.42   Mean   :201664   Mean   :15.48  
##  3rd Qu.:2018   3rd Qu.: 9.00   3rd Qu.:243900   3rd Qu.:19.10  
##  Max.   :2022   Max.   :12.00   Max.   :327454   Max.   :24.24  
##  NA's   :2      NA's   :2       NA's   :2        NA's   :2      
##    inflacion      porcentaje_ocu  porcentaje_desocu conf_consumidor
##  Min.   :-0.250   Min.   :93.58   Min.   :2.840     Min.   :28.67  
##  1st Qu.: 0.815   1st Qu.:95.06   1st Qu.:3.527     1st Qu.:36.69  
##  Median : 1.480   Median :95.88   Median :4.125     Median :38.47  
##  Mean   : 1.951   Mean   :95.76   Mean   :4.244     Mean   :39.15  
##  3rd Qu.: 2.895   3rd Qu.:96.47   3rd Qu.:4.940     3rd Qu.:42.59  
##  Max.   : 7.360   Max.   :97.16   Max.   :6.420     Max.   :47.83  
##  NA's   :2        NA's   :2       NA's   :2         NA's   :2

Técnica 2. Eliminar renglones irrelevantes:

prediccion_mx <- prediccion_mx[-c(201, 202),]
summary(prediccion_mx)
##       ano            mes         exportacion     tipo_de_cambio 
##  Min.   :2006   Min.   : 1.00   Min.   : 15139   Min.   :10.09  
##  1st Qu.:2010   1st Qu.: 3.00   1st Qu.:153219   1st Qu.:12.66  
##  Median :2014   Median : 6.00   Median :209161   Median :13.56  
##  Mean   :2014   Mean   : 6.42   Mean   :201664   Mean   :15.48  
##  3rd Qu.:2018   3rd Qu.: 9.00   3rd Qu.:243900   3rd Qu.:19.10  
##  Max.   :2022   Max.   :12.00   Max.   :327454   Max.   :24.24  
##    inflacion      porcentaje_ocu  porcentaje_desocu conf_consumidor
##  Min.   :-0.250   Min.   :93.58   Min.   :2.840     Min.   :28.67  
##  1st Qu.: 0.815   1st Qu.:95.06   1st Qu.:3.527     1st Qu.:36.69  
##  Median : 1.480   Median :95.88   Median :4.125     Median :38.47  
##  Mean   : 1.951   Mean   :95.76   Mean   :4.244     Mean   :39.15  
##  3rd Qu.: 2.895   3rd Qu.:96.47   3rd Qu.:4.940     3rd Qu.:42.59  
##  Max.   : 7.360   Max.   :97.16   Max.   :6.420     Max.   :47.83

2. Explicación de las variables:

Variable_carton<-c("Año", "Mes", "Exportación", "Tipo de cambio", "Inflación", "porcentaje_ocu", "porcentaje_desocu", "conf_consumidor")
Tipo_variable <- c("Exploratoria", "Exploratoria", "Dependiente", "Exploratoria", "Exploratoria", "Exploratoria", "Exploratoria", "Exploratoria")
Unidad_Medición <- c("Año", "Mes", "Unidades", "Pesos Mexicanos", "Índice de Precios al Consumo", "% total de empleados", "% total de desempleados", "Perspectiva económica del consumidor")
tabla12<-data.frame(Variable_carton,Tipo_variable,Unidad_Medición)
knitr::kable(tabla12)
Variable_carton Tipo_variable Unidad_Medición
Año Exploratoria Año
Mes Exploratoria Mes
Exportación Dependiente Unidades
Tipo de cambio Exploratoria Pesos Mexicanos
Inflación Exploratoria Índice de Precios al Consumo
porcentaje_ocu Exploratoria % total de empleados
porcentaje_desocu Exploratoria % total de desempleados
conf_consumidor Exploratoria Perspectiva económica del consumidor

3. Modelo de Regresión Lineal Múltiple 1:

corrplot(cor(prediccion_mx), type="upper", order="hclust", addcoef.col="black")

summary(prediccion_mx)
##       ano            mes         exportacion     tipo_de_cambio 
##  Min.   :2006   Min.   : 1.00   Min.   : 15139   Min.   :10.09  
##  1st Qu.:2010   1st Qu.: 3.00   1st Qu.:153219   1st Qu.:12.66  
##  Median :2014   Median : 6.00   Median :209161   Median :13.56  
##  Mean   :2014   Mean   : 6.42   Mean   :201664   Mean   :15.48  
##  3rd Qu.:2018   3rd Qu.: 9.00   3rd Qu.:243900   3rd Qu.:19.10  
##  Max.   :2022   Max.   :12.00   Max.   :327454   Max.   :24.24  
##    inflacion      porcentaje_ocu  porcentaje_desocu conf_consumidor
##  Min.   :-0.250   Min.   :93.58   Min.   :2.840     Min.   :28.67  
##  1st Qu.: 0.815   1st Qu.:95.06   1st Qu.:3.527     1st Qu.:36.69  
##  Median : 1.480   Median :95.88   Median :4.125     Median :38.47  
##  Mean   : 1.951   Mean   :95.76   Mean   :4.244     Mean   :39.15  
##  3rd Qu.: 2.895   3rd Qu.:96.47   3rd Qu.:4.940     3rd Qu.:42.59  
##  Max.   : 7.360   Max.   :97.16   Max.   :6.420     Max.   :47.83
modelo_regresion1 <- lm(exportacion~ano+mes+tipo_de_cambio+inflacion+porcentaje_ocu+porcentaje_desocu+conf_consumidor,data=prediccion_mx) 
summary(modelo_regresion1)
## 
## Call:
## lm(formula = exportacion ~ ano + mes + tipo_de_cambio + inflacion + 
##     porcentaje_ocu + porcentaje_desocu + conf_consumidor, data = prediccion_mx)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -223340  -17123    3169   21264   87133 
## 
## Coefficients: (1 not defined because of singularities)
##                     Estimate Std. Error t value Pr(>|t|)    
## (Intercept)       -3.913e+07  3.447e+06 -11.352  < 2e-16 ***
## ano                1.835e+04  1.645e+03  11.151  < 2e-16 ***
## mes                6.334e+03  1.054e+03   6.007 9.25e-09 ***
## tipo_de_cambio    -1.280e+04  2.212e+03  -5.785 2.89e-08 ***
## inflacion         -1.022e+04  2.390e+03  -4.275 3.01e-05 ***
## porcentaje_ocu     2.763e+04  4.394e+03   6.289 2.09e-09 ***
## porcentaje_desocu         NA         NA      NA       NA    
## conf_consumidor   -2.142e+03  8.735e+02  -2.452   0.0151 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 36390 on 193 degrees of freedom
## Multiple R-squared:  0.6723, Adjusted R-squared:  0.6621 
## F-statistic: 65.99 on 6 and 193 DF,  p-value: < 2.2e-16
effect_plot(modelo_regresion1,pred=tipo_de_cambio,interval=TRUE)

En este modelo, se pudo observar que el tipo de cambio era la variable que más impactaba a la exportación, por lo que si el peso valía más en este caso, la exportación incrementaba, teniendo esta variable una tendencia negativa, ocurriendo lo mismo con la inflación. Por otro lado, Si incrementa el % de ocupación (empleo), que tambien es una variable estadísticamente signifcativa, incrementa la exportación de vehículos, teniendo esta una tendencia positiva.

Predicción 2: Car Sales in United States (Statista)

1. Limpieza de datos:

Importar base de datos:

# file.choose()
bd9 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\EVI2\\us_automotive_industry.csv")
prediccion_usa <- clean_names(bd9)
summary(prediccion_usa)
##       year      car_production      car_sales       inflation_rate 
##  Min.   :2010   Min.   :1562720   Min.   :3340000   Min.   :0.100  
##  1st Qu.:2013   1st Qu.:2676260   1st Qu.:5162500   1st Qu.:1.450  
##  Median :2016   Median :3005105   Median :6090000   Median :1.700  
##  Mean   :2016   Mean   :3194638   Mean   :5962500   Mean   :1.967  
##  3rd Qu.:2018   3rd Qu.:4122460   3rd Qu.:7320000   3rd Qu.:2.175  
##  Max.   :2021   Max.   :4368840   Max.   :7710000   Max.   :4.700  
##  us_unemployment us_consumer_confidence
##  Min.   :3.675   Min.   :67.35         
##  1st Qu.:4.746   1st Qu.:77.35         
##  Median :5.758   Median :82.83         
##  Mean   :6.295   Mean   :84.51         
##  3rd Qu.:8.079   3rd Qu.:93.70         
##  Max.   :9.488   Max.   :98.37

Técnica 1: Cambiar a formato de caracter a número:

prediccion_usa$year<-as.numeric(prediccion_usa$year)                 
prediccion_usa$car_production <- as.numeric(prediccion_usa$car_production)
prediccion_usa$car_sales<-as.numeric(prediccion_usa$car_sales)
prediccion_usa$inflation_rate<-as.numeric(prediccion_usa$inflation_rate)
prediccion_usa$us_unemployment<-as.numeric(prediccion_usa$us_unemployment)
prediccion_usa$us_consumer_confidence<-as.numeric(prediccion_usa$us_consumer_confidence)
summary(prediccion_usa)
##       year      car_production      car_sales       inflation_rate 
##  Min.   :2010   Min.   :1562720   Min.   :3340000   Min.   :0.100  
##  1st Qu.:2013   1st Qu.:2676260   1st Qu.:5162500   1st Qu.:1.450  
##  Median :2016   Median :3005105   Median :6090000   Median :1.700  
##  Mean   :2016   Mean   :3194638   Mean   :5962500   Mean   :1.967  
##  3rd Qu.:2018   3rd Qu.:4122460   3rd Qu.:7320000   3rd Qu.:2.175  
##  Max.   :2021   Max.   :4368840   Max.   :7710000   Max.   :4.700  
##  us_unemployment us_consumer_confidence
##  Min.   :3.675   Min.   :67.35         
##  1st Qu.:4.746   1st Qu.:77.35         
##  Median :5.758   Median :82.83         
##  Mean   :6.295   Mean   :84.51         
##  3rd Qu.:8.079   3rd Qu.:93.70         
##  Max.   :9.488   Max.   :98.37

2. Explicación de las variables:

Variable_usa<-c("Year", "Car Production", "Car Sales", "Inflation rate", "US Unemployement Rate", "US Consumer Confidence")
Tipo_variable_usa <- c("Exploratoria", "Dependiente", "Dependiente", "Exploratoria", "Exploratoria", "Exploratoria")
Unidad_Medición_usa <- c("Años","Unidades", "Unidades", "% Cambio de los precios", "% total de desempleados", "Perspectiva económica del consumidor")
tabla13<-data.frame(Variable_usa,Tipo_variable_usa,Unidad_Medición_usa)
knitr::kable(tabla13)
Variable_usa Tipo_variable_usa Unidad_Medición_usa
Year Exploratoria Años
Car Production Dependiente Unidades
Car Sales Dependiente Unidades
Inflation rate Exploratoria % Cambio de los precios
US Unemployement Rate Exploratoria % total de desempleados
US Consumer Confidence Exploratoria Perspectiva económica del consumidor

Modelo de Regresión Lineal Múltiple 2:

corrplot(cor(prediccion_usa), type="upper", order="hclust", addcoef.col="black")

modelo_regresion2 <- lm(car_sales~year+inflation_rate+us_unemployment+us_consumer_confidence,data=prediccion_usa) 
summary(modelo_regresion2)
## 
## Call:
## lm(formula = car_sales ~ year + inflation_rate + us_unemployment + 
##     us_consumer_confidence, data = prediccion_usa)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1235300  -114022    53391   445143   747117 
## 
## Coefficients:
##                         Estimate Std. Error t value Pr(>|t|)   
## (Intercept)            868156591  193630933   4.484  0.00285 **
## year                     -419114      96909  -4.325  0.00346 **
## inflation_rate           -755849     416783  -1.814  0.11263   
## us_unemployment          -982706     483278  -2.033  0.08150 . 
## us_consumer_confidence   -115921     102415  -1.132  0.29497   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 801800 on 7 degrees of freedom
## Multiple R-squared:  0.8283, Adjusted R-squared:  0.7302 
## F-statistic: 8.442 on 4 and 7 DF,  p-value: 0.008178
effect_plot(modelo_regresion2,pred=us_unemployment,interval=TRUE)

En este caso, podemos observar que el modelo si es confiable (teniendo una r2 alta) y estadísticamente signifcativo al tener las variables independientes de year y desempleo negativas, indicando que si el desempleo crece en EUA, las ventas de automoviles disminuirán. Las demás variabls también tienen una relación negativa, por lo que si la taza de inflación y la confianza del consumidor incrementan, las ventas podrían verse un poco afectadas y disminuir por igual.

Pronóstico 1: Vehículos de motor registrados en circulación en México:

1. Limpieza de datos:

Importar las bases de datos y cambiar el nombre de las variables con la función clean_names:

b10 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\FORM BASES DE DATOS\\encoded-vehiculos_en_circulacion (2).csv")
vehiculos_circulacion <- clean_names(b10)
summary(vehiculos_circulacion)
##      ano                total           automoviles      
##  Length:47          Min.   : 4010430   Min.   : 3950042  
##  Class :character   1st Qu.: 6748523   1st Qu.: 6654340  
##  Mode  :character   Median :11002046   Median :10764080  
##                     Mean   :14899354   Mean   :14673142  
##                     3rd Qu.:22392796   3rd Qu.:22068938  
##                     Max.   :35913468   Max.   :35460804  
##                     NA's   :5          NA's   :5         
##  camiones_para_pasajeros camiones_y_camionetas_para_carga  motocicletas    
##  Min.   : 60388          Min.   : 1470816                 Min.   : 128960  
##  1st Qu.: 90931          1st Qu.: 3046906                 1st Qu.: 248248  
##  Median :233491          Median : 5166812                 Median : 295262  
##  Mean   :226213          Mean   : 5920971                 Mean   :1156467  
##  3rd Qu.:336421          3rd Qu.: 9199181                 3rd Qu.:1284405  
##  Max.   :461089          Max.   :11262666                 Max.   :5939262  
##  NA's   :5               NA's   :5                        NA's   :5        
##     x             x_1         
##  Mode:logical   Mode:logical  
##  NA's:47        NA's:47       
##                               
##                               
##                               
##                               
## 

Eliminar variables innecesarias:

vehiculos_circulacion <- subset(vehiculos_circulacion, select = -c (camiones_y_camionetas_para_carga, motocicletas, x, x_1))

Eliminar renglones irrelevantes:

vehiculos_circulacion <- vehiculos_circulacion[-c(43,44,45,46,47),]

Cambiar a formato de caracter a número:

vehiculos_circulacion$ano<-as.numeric(vehiculos_circulacion$ano)                 
vehiculos_circulacion$total <- as.numeric(vehiculos_circulacion$total)
vehiculos_circulacion$autmoviles$car_sales<-as.numeric(vehiculos_circulacion$automoviles)
vehiculos_circulacion$camiones_para_pasajeros<-as.numeric(vehiculos_circulacion$camiones_para_pasajeros)
summary(vehiculos_circulacion)
##       ano           total           automoviles       camiones_para_pasajeros
##  Min.   :1980   Min.   : 4010430   Min.   : 3950042   Min.   : 60388         
##  1st Qu.:1990   1st Qu.: 6748523   1st Qu.: 6654340   1st Qu.: 90931         
##  Median :2000   Median :11002046   Median :10764080   Median :233491         
##  Mean   :2000   Mean   :14899354   Mean   :14673142   Mean   :226213         
##  3rd Qu.:2011   3rd Qu.:22392796   3rd Qu.:22068938   3rd Qu.:336421         
##  Max.   :2021   Max.   :35913468   Max.   :35460804   Max.   :461089         
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##                                                                              
##  autmoviles.Length  autmoviles.Class  autmoviles.Mode
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric                           
##  42       -none-   numeric

2. Gráfica para comparar los autos en circulación vs todos los vehículos.

plot(vehiculos_circulacion$ano,vehiculos_circulacion$total, type="l",col="blue", lwd=1.5, xlab ="Año",ylab ="Unidades", main = "Vehiculos de motor en circulación registrados anualmente")
lines(vehiculos_circulacion$ano,vehiculos_circulacion$automoviles,col="red",lty=3)
legend("topleft", legend=c("Total de vehiculos en circulación", "Automóviles en circulación"),
       col=c("blue", "red"), lty = 1:2, cex=0.8)

Podemos observar que la tendencia es casi igual, indicando que los autos son la gran mayoría del total de vehículos en circulación.

3. Forecasting usando el Modelo autoregresivo:

autoregressive_model <- arma(vehiculos_circulacion$total, order = c(1,0))
summary(autoregressive_model <- arma(vehiculos_circulacion$total, order = c(1,0)))
## 
## Call:
## arma(x = vehiculos_circulacion$total, order = c(1, 0))
## 
## Model:
## ARMA(1,0)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -964598 -203485  -37324  133579 1366807 
## 
## Coefficient(s):
##            Estimate  Std. Error  t value Pr(>|t|)    
## ar1       1.043e+00   3.792e-03      275   <2e-16 ***
## intercept 1.595e+05         NaN      NaN      NaN    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Fit:
## sigma^2 estimated as 1.852e+11,  Conditional Sum-of-Squares = 7.406257e+12,  AIC = 1212.86
autoregressive_model_forecast<-forecast(autoregressive_model$fitted,h=5,level=c(95))
autoregressive_model_forecast
##    Point Forecast    Lo 95    Hi 95
## 43       37884281 35240406 40528155
## 44       39222087 34802632 43641541
## 45       40559893 34284299 46835486
## 46       41897698 33625820 50169577
## 47       43235504 32811356 53659653
plot(autoregressive_model_forecast)

En este primer modelo autoregresivo podemos observar una tendencia positiva al pronosticar la circulación de automóviles en México en los próximos años, por ejemplo, en el siguiente año se esperan 37,884,281 vehículos en circulación con un 95% de confianza, y los rangos pueden ir de 35,240,406 hasta 40,528,155.

En el segundo año, se pronostican 39,222,087, de igual manera con un 95% de confianza, y por último, en el tercer año, se preveen 40,559,893 vehículos en circulación.

El incremento de vehículos en circulación en México es un dato que puede ser beneficioso para FORM, ya que al haber más vehículos en circulación, se implica una mayor producción de los mismos, y por ende, las empresas automotrices necesitarán más de los servicios de empaque ofrecidos por FORM.

Pronóstico 2: Sales of vehicles in USA (units)

1. Limpieza de datos:

Importar las bases de datos y cambiar el nombre de las variables con la función clean_names:

b12 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\EVI2\\vehicle_sales_usa.csv")
vehicle_sales <- clean_names(b12)
summary(vehicle_sales)
##      years      new_passanger_car_sales new_light_truck_sales
##  Min.   :2005   Min.   :2510874         Min.   :3540000      
##  1st Qu.:2009   1st Qu.:4479484         1st Qu.:6140000      
##  Median :2013   Median :4981183         Median :7193087      
##  Mean   :2013   Mean   :4847454         Mean   :7074063      
##  3rd Qu.:2017   3rd Qu.:5711630         3rd Qu.:8502487      
##  Max.   :2021   Max.   :6182000         Max.   :9311251      
##  used_vehicle_sales     total             x             x_1         
##  Min.   :35491762   Min.   :43538762   Mode:logical   Mode:logical  
##  1st Qu.:36911180   1st Qu.:48140008   NA's:17        NA's:17       
##  Median :38602466   Median :50386196                                
##  Mean   :38645158   Mean   :50566675                                
##  3rd Qu.:40232959   3rd Qu.:53310170                                
##  Max.   :44138263   Max.   :57630263                                
##    x_2            x_3            x_4         
##  Mode:logical   Mode:logical   Mode:logical  
##  NA's:17        NA's:17        NA's:17       
##                                              
##                                              
##                                              
## 

Técnica 1. Eliminar columnas innecesarias:

vehicle_sales <- subset(vehicle_sales, select = -c (x, x_1, x_2, x_3, x_4))
summary(vehicle_sales)
##      years      new_passanger_car_sales new_light_truck_sales
##  Min.   :2005   Min.   :2510874         Min.   :3540000      
##  1st Qu.:2009   1st Qu.:4479484         1st Qu.:6140000      
##  Median :2013   Median :4981183         Median :7193087      
##  Mean   :2013   Mean   :4847454         Mean   :7074063      
##  3rd Qu.:2017   3rd Qu.:5711630         3rd Qu.:8502487      
##  Max.   :2021   Max.   :6182000         Max.   :9311251      
##  used_vehicle_sales     total         
##  Min.   :35491762   Min.   :43538762  
##  1st Qu.:36911180   1st Qu.:48140008  
##  Median :38602466   Median :50386196  
##  Mean   :38645158   Mean   :50566675  
##  3rd Qu.:40232959   3rd Qu.:53310170  
##  Max.   :44138263   Max.   :57630263

Técnica 2. Cambiar a formato de caracter a número:

vehicle_sales$years<-as.numeric(vehicle_sales$years)                 
vehicle_sales$new_passanger_car_sales <- as.numeric(vehicle_sales$new_passanger_car_sales)
vehicle_sales$new_light_truck_sales<-as.numeric(vehicle_sales$new_light_truck_sales)
vehicle_sales$used_vehicle_sales<-as.numeric(vehicle_sales$used_vehicle_sales)
vehicle_sales$total<-as.numeric(vehicle_sales$total)
summary(vehicle_sales)
##      years      new_passanger_car_sales new_light_truck_sales
##  Min.   :2005   Min.   :2510874         Min.   :3540000      
##  1st Qu.:2009   1st Qu.:4479484         1st Qu.:6140000      
##  Median :2013   Median :4981183         Median :7193087      
##  Mean   :2013   Mean   :4847454         Mean   :7074063      
##  3rd Qu.:2017   3rd Qu.:5711630         3rd Qu.:8502487      
##  Max.   :2021   Max.   :6182000         Max.   :9311251      
##  used_vehicle_sales     total         
##  Min.   :35491762   Min.   :43538762  
##  1st Qu.:36911180   1st Qu.:48140008  
##  Median :38602466   Median :50386196  
##  Mean   :38645158   Mean   :50566675  
##  3rd Qu.:40232959   3rd Qu.:53310170  
##  Max.   :44138263   Max.   :57630263

2. Gráfica para comparar la venta de vehículos usados vs todos los vehículos vendidos:

plot(vehicle_sales$years,vehicle_sales$total, type="l",col="blue", lwd=1.5, xlab ="Año",ylab ="Unidades", main = "Venta de vehículos en Estados Unidos")
lines(vehicle_sales$years,vehicle_sales$used_vehicle_sales,col="red",lty=3)
legend("topleft", legend=c("Total de vehiculos en circulación", "Venta de vehiculos usados"),
       col=c("blue", "red"), lty = 1:2, cex=0.6)

3. Forecasting usando el Moving Average Model:

mam_sales <- arma(vehicle_sales$total,order = c(1,1))
summary(mam_sales <- arma(vehicle_sales$total,order = c(1,1)))
## 
## Call:
## arma(x = vehicle_sales$total, order = c(1, 1))
## 
## Model:
## ARMA(1,1)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -5431072 -1035522   928147  1352337  1840355 
## 
## Coefficient(s):
##            Estimate  Std. Error  t value Pr(>|t|)    
## ar1       6.260e-01   1.285e-02   48.715   <2e-16 ***
## ma1       3.641e-01   2.757e-01    1.321    0.187    
## intercept 1.848e+07         NaN      NaN      NaN    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Fit:
## sigma^2 estimated as 4.356e+12,  Conditional Sum-of-Squares = 6.534569e+13,  AIC = 548.99
mam_sales_forecast <- forecast(mam_sales$fitted,h=5,level=c(95))
mam_sales_forecast
##    Point Forecast    Lo 95    Hi 95
## 18       48665257 43635276 53695238
## 19       48665257 41654979 55675535
## 20       48665257 40118351 57212163
## 21       48665257 38815502 58515012
## 22       48665257 37663057 59667457
plot(mam_sales_forecast)

Con esta técnica podemos ver que en Estados Unidos se pronostica para los próximos años la misma cantidad de unidades de vehículos vendidas, siendo esta de 48,665,257 unidades en los próximos 5 años.

Con esta información podemos decir que bien, al FORM querer entrar a la industria automotriz en Estados Unidos si podría generar una ventaja, sin embargo para los próximos años, puede que la industria automotriz no tenga un crecimiento signifcativo de ventas de carros y por ende producción de los mismos.

Pronóstico 3: Desempeño del área de Delivery Performance:

1. Limpieza de datos:

# file.choose()
b11 <- read.csv("C:\\Users\\maria\\Documents\\performance_bd_limpia.csv")
perf_pronosticos <- clean_names(b11)
summary(perf_pronosticos)
##     fecha              printel           mahle             magna       varroc 
##  Length:324         Min.   :0.0000   Min.   :-11.650   Min.   :0   Min.   :0  
##  Class :character   1st Qu.:0.0000   1st Qu.:  2.000   1st Qu.:0   1st Qu.:0  
##  Mode  :character   Median :0.0000   Median :  3.000   Median :0   Median :0  
##                     Mean   :0.4077   Mean   :  2.413   Mean   :0   Mean   :0  
##                     3rd Qu.:0.9625   3rd Qu.:  3.100   3rd Qu.:0   3rd Qu.:0  
##                     Max.   :4.4000   Max.   : 20.000   Max.   :0   Max.   :0  
##     retraso       
##  Min.   :-10.450  
##  1st Qu.:  2.000  
##  Median :  3.000  
##  Mean   :  2.821  
##  3rd Qu.:  3.652  
##  Max.   : 20.000

2. Forecasting usando el Modelo Autoregresivo:

autoregressive_model_performance <- arma(perf_pronosticos$retraso, order = c(1,0))
summary(autoregressive_model_performance <- arma(perf_pronosticos$retraso, order = c(1,0)))
## 
## Call:
## arma(x = perf_pronosticos$retraso, order = c(1, 0))
## 
## Model:
## ARMA(1,0)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -11.0051  -0.7200   0.1366   0.8392  17.8326 
## 
## Coefficient(s):
##            Estimate  Std. Error  t value Pr(>|t|)    
## ar1         0.23199     0.05404    4.293 1.76e-05 ***
## intercept   2.16743     0.19055   11.374  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Fit:
## sigma^2 estimated as 4.25,  Conditional Sum-of-Squares = 1368.55,  AIC = 1392.28
autoregressive_model_performance_forecast<-forecast(autoregressive_model_performance$fitted,h=5,level=c(95))
summary(autoregressive_model_performance_forecast)
## 
## Forecast method: ETS(A,N,N)
## 
## Model Information:
## ETS(A,N,N) 
## 
## Call:
##  ets(y = object, lambda = lambda, biasadj = biasadj, allow.multiplicative.trend = allow.multiplicative.trend) 
## 
##   Smoothing parameters:
##     alpha = 0.0561 
## 
##   Initial states:
##     l = 3.0264 
## 
##   sigma:  0.4746
## 
##      AIC     AICc      BIC 
## 1388.785 1388.860 1400.117 
## 
## Error measures:
##                       ME      RMSE       MAE       MPE     MAPE     MASE
## Training set -0.01361886 0.4731736 0.2759066 0.8752139 15.02225 0.840378
##                   ACF1
## Training set 0.1245646
## 
## Forecasts:
##     Point Forecast    Lo 95    Hi 95
## 325       2.779647 1.849359 3.709934
## 326       2.779647 1.847897 3.711397
## 327       2.779647 1.846437 3.712856
## 328       2.779647 1.844979 3.714314
## 329       2.779647 1.843524 3.715769
ggplot(perf_pronosticos,aes(x=fecha,y=retraso))+ 
  geom_point(size=2,shape=23)

plot(autoregressive_model_performance_forecast)

Con este modelo autoregresivo, se pronostica con un 95% de confianza en los próximos años un tiempo de retraso lineal, es decir, no se puede observar una tendencia ni negativa o positiva como podemos ver en la gráfica de dispersión, arrojando así un retraso de entrega de 2.773 minutos en los próximos años.

Este pronóstico es bueno para FORM, pues indica que su tiempo de entrega no incerementará en atrasos, sin embargo, puede crear estartegias para cambiar este pronóstico y lograr que se disminuya lo más posible en un futuro.

Pronóstico 4. Desempeño del área de Scrap:

1. Limpieza de datos:

Importar la bases de datos y cambiar el nombre de las variables con la función clean_names:

# file.choose()
b13 <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\EVI2\\scrap_bd_limpia.csv")
scrap_pronosticos <- clean_names(b13)
summary(scrap_pronosticos)
##     fecha              cantidad      ubicacion_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 1. Cambiar formato de las variables:

scrap_pronosticos$fecha <- as.Date(scrap_pronosticos$fecha, format = "%d/%m/%Y")
scrap_pronosticos$ubicacion_de_origen <- as.factor(scrap_pronosticos$ubicacion_de_origen)
scrap_pronosticos$cantidad <- as.numeric(scrap_pronosticos$cantidad)
summary(scrap_pronosticos)
##      fecha        cantidad                 ubicacion_de_origen
##  Min.   :NA    Min.   : 0.000   Calidad/Entrega de PT: 58     
##  1st Qu.:NA    1st Qu.: 1.000   Post-Production      : 13     
##  Median :NA    Median : 2.000   Pre-Production       :179     
##  Mean   :NaN   Mean   : 6.696                                 
##  3rd Qu.:NA    3rd Qu.: 7.000                                 
##  Max.   :NA    Max.   :96.000                                 
##  NA's   :250

2. Forecasting usando Moving Average Model:

mam_scrap <- arma(scrap_pronosticos$cantidad,order = c(1,1))
summary(mam_scrap <- arma(scrap_pronosticos$cantidad,order = c(1,1)))
## 
## Call:
## arma(x = scrap_pronosticos$cantidad, order = c(1, 1))
## 
## Model:
## ARMA(1,1)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -33.3119  -2.9832  -1.6561  -0.1724  86.7286 
## 
## Coefficient(s):
##            Estimate  Std. Error  t value Pr(>|t|)    
## ar1         0.90165     0.03352   26.900   <2e-16 ***
## ma1        -0.63209     0.05502  -11.489   <2e-16 ***
## intercept   0.54097     0.31757    1.703   0.0885 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Fit:
## sigma^2 estimated as 93.63,  Conditional Sum-of-Squares = 23223.87,  AIC = 1850.32
mam_scrap_forecast <- forecast(mam_scrap$fitted,h=5,level=c(95))
mam_scrap_forecast
##     Point Forecast      Lo 95    Hi 95
## 251       2.877502  0.8671930 4.887811
## 252       3.079049  0.0635478 6.094550
## 253       3.280596 -0.6423603 7.203552
## 254       3.482143 -1.3266521 8.290938
## 255       3.683690 -2.0186251 9.386005
plot(mam_scrap_forecast)

En el caso de este modelo MAM, se pronostica con un 95% de confianza que habrá un aumento en la producción de scrap dentro de los procesos de FORM, habiendo así un pronóstico de 2.877 kilos para el siguiente año, seguido por 3.079 kg para el siguiente periodo y despues de 3.280 kg. Este crecimiento no es para nada drástico, sin embargo, aunque el crecimiento de los desperdicios no es algo que busca FORM, este puede indicar que la producción también estará creciendo, pues el scrap se produce en su mayoría en la etapa de pre-producción.

En la gráfica podemos ver un comportamiento positivo, demostrando así el pronóstico de crecimiento muy leve para los siguientes años.

En este caso, FORM deberá crear estrategias para evitar el crecimiento de su scrap y aprovechar al máximo su materia prima.

Sección 3

Estimación de K-Means Clustering:

Paso 1. Importar y limpiar la base de datos:

bajas <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\EVI2\\FORM - Recursos Humanos - BAJAS evidencia).csv")
bajas <- clean_names(bajas)
summary(bajas)
##   apellidos            nombre          fecha_de_nacimiento      edad      
##  Length:237         Length:237         Length:237          Min.   :19.00  
##  Class :character   Class :character   Class :character    1st Qu.:23.00  
##  Mode  :character   Mode  :character   Mode  :character    Median :29.00  
##                                                            Mean   :31.09  
##                                                            3rd Qu.:37.00  
##                                                            Max.   :61.00  
##                                                            NA's   :3      
##     genero              rfc            fecha_de_alta      motivo_de_baja    
##  Length:237         Length:237         Length:237         Length:237        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     no_dias            baja              puesto          departamento      
##  Min.   :   0.00   Length:237         Length:237         Length:237        
##  1st Qu.:   9.00   Class :character   Class :character   Class :character  
##  Median :  19.00   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :  79.47                                                           
##  3rd Qu.:  48.75                                                           
##  Max.   :1966.00                                                           
##  NA's   :23                                                                
##  no_seguro_social   salario_diario_imss factor_cred_infonavit
##  Length:237         Min.   :144.4       Length:237           
##  Class :character   1st Qu.:180.7       Class :character     
##  Mode  :character   Median :180.7       Mode  :character     
##                     Mean   :178.0                            
##                     3rd Qu.:180.7                            
##                     Max.   :500.0                            
##                     NA's   :1                                
##  n_credito_infonavit lugar_de_nacimiento     curp              calle          
##  Length:237          Length:237          Length:237         Length:237        
##  Class :character    Class :character    Class :character   Class :character  
##  Mode  :character    Mode  :character    Mode  :character   Mode  :character  
##                                                                               
##                                                                               
##                                                                               
##                                                                               
##  numero_interno       colonia          codigo_postal       municipio        
##  Length:237         Length:237         Length:237         Length:237        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     estado          estado_civil       tarjeta_cuenta    
##  Length:237         Length:237         Length:237        
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
##                                                          
## 
#Técnica 1:
#Borrar columnas.
bajas <- subset(bajas, select = -c(apellidos, nombre, fecha_de_nacimiento,rfc, fecha_de_alta, baja, departamento, no_seguro_social, factor_cred_infonavit, n_credito_infonavit, lugar_de_nacimiento, curp, calle, numero_interno, colonia, codigo_postal, municipio, estado, tarjeta_cuenta))

#Técnica 2: 
#Reemplezar NAs con el promedio en la columna de "Edad", "Salario Diario" y "Número de días".
bajas$edad[is.na(bajas$edad)]<-mean(bajas$edad, na.rm = TRUE)
bajas$salario_diario_imss[is.na(bajas$salario_diario_imss)]<-mean(bajas$salario_diario_imss, na.rm = TRUE)
bajas$no_dias[is.na(bajas$no_dias)]<-mean(bajas$no_dias, na.rm = TRUE)
summary (bajas)
##       edad          genero          motivo_de_baja        no_dias       
##  Min.   :19.00   Length:237         Length:237         Min.   :   0.00  
##  1st Qu.:23.00   Class :character   Class :character   1st Qu.:   9.00  
##  Median :29.00   Mode  :character   Mode  :character   Median :  23.00  
##  Mean   :31.09                                         Mean   :  79.47  
##  3rd Qu.:37.00                                         3rd Qu.:  79.47  
##  Max.   :61.00                                         Max.   :1966.00  
##     puesto          salario_diario_imss estado_civil      
##  Length:237         Min.   :144.4       Length:237        
##  Class :character   1st Qu.:180.7       Class :character  
##  Mode  :character   Median :180.7       Mode  :character  
##                     Mean   :178.0                         
##                     3rd Qu.:180.7                         
##                     Max.   :500.0
#Técnica 3:
#Convertir las variables como factor o número
bajas$edad<-as.numeric(bajas$edad)
bajas$genero<-as.factor(bajas$genero)
bajas$motivo_de_baja<-as.factor(bajas$motivo_de_baja)
bajas$no_dias<-as.numeric(bajas$no_dias)
bajas$puesto<-as.factor(bajas$puesto)
bajas$salario_diario_imss<-as.numeric(bajas$salario_diario_imss)
bajas$estado_civil<-as.factor(bajas$estado_civil)
summary (bajas)
##       edad                 genero                motivo_de_baja
##  Min.   :19.00   CAPJ000926597:  1   ABANDONO           :  1   
##  1st Qu.:23.00   FEMENINO     :139   BAJA POR FALTAS    :141   
##  Median :29.00   MASCULINO    : 97   JUBILACION         :  1   
##  Mean   :31.09                       RENUNCIA VOLUNTARIA: 86   
##  3rd Qu.:37.00                       TERMINO DE CONTRATO:  8   
##  Max.   :61.00                                                 
##                                                                
##     no_dias                          puesto    salario_diario_imss
##  Min.   :   0.00   AYUDANTE GENERAL     :179   Min.   :144.4      
##  1st Qu.:   9.00   COSTURERA            : 11   1st Qu.:180.7      
##  Median :  23.00   SOLDADOR             : 11   Median :180.7      
##  Mean   :  79.47   AYUDANTE DE EMBARQUES:  7   Mean   :178.0      
##  3rd Qu.:  79.47   MONTACARGUISTA       :  5   3rd Qu.:180.7      
##  Max.   :1966.00   INSPECTOR CALIDAD    :  4   Max.   :500.0      
##                    (Other)              : 20                      
##       estado_civil
##             :  2  
##  CASADO     : 64  
##  DIVORCIADO :  3  
##  SOLTERO    :108  
##  UNION LIBRE: 60  
##                   
## 
#Exportar base de datos limpia
write.csv(bajas, file="bajas_bd_limpia.csv", row.names = FALSE)

Paso 2. Creación de Clústers:

Cluster 1: Relación de variables de Edad y Número de días.

edad <-bajas 
edad<- subset(bajas,select = -c(genero, motivo_de_baja, puesto, salario_diario_imss, estado_civil))

#Normalizar variables
edad_norm<-scale(edad[1:2]) 

#Función fviz para la visualización de un Elbow Plot y así determinar el número de clusters.
fviz_nbclust(edad_norm, kmeans, method="wss")+ 
  geom_vline(xintercept=4, linetype=2)+            
  labs(subtitle = "Elbow method")

Con esta gráfica podemos visualizar que el número óptimo de clústers son 4 (optimización).

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

Los más jóvenes son los que menos días de trabajo laboran. Así como que los que más días laborales tienen, son los adultos desde los 27 años hasta los 61.

Cluster 2: Relación de variables de Edad y Salario Diario.

edad_salario <-bajas 
edad_salario<- subset(bajas,select = -c(genero,estado_civil,motivo_de_baja, puesto, no_dias))

#Normalizar variables
edad_salario_norm<-scale(edad_salario[1:2]) 
edad_salario_norm <- na.omit(edad_salario_norm)
     summary(edad_salario_norm)
##       edad         salario_diario_imss
##  Min.   :-1.2638   Min.   :-1.4439    
##  1st Qu.:-0.8457   1st Qu.: 0.1171    
##  Median :-0.2184   Median : 0.1171    
##  Mean   : 0.0000   Mean   : 0.0000    
##  3rd Qu.: 0.6178   3rd Qu.: 0.1171    
##  Max.   : 3.1266   Max.   :13.8750
#Función fviz para la visualización de un Elbow Plot y así determinar el número de clusters.
fviz_nbclust(edad_salario_norm, kmeans, method="wss")+ 
  geom_vline(xintercept=4, linetype=2)+            
  labs(subtitle = "Elbow method") 

Con esta gráfica podemos visualizar que el número óptimo de clústers son 4 (optimización).

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

La edad no tiene mucha influencia en el salario. Pues los colaboradores que se han dado de baja tienen un salario muy similar independientemente de su edad.

Cluster 3: Relación de variables de Salario Diario y Número de días.

dias_salario <-bajas 
dias_salario<- subset(bajas,select = -c(genero,estado_civil,motivo_de_baja, puesto, edad))

#Normalizar variables:
dias_salario_norm<-scale(dias_salario[1:2]) 

#Función fviz para la visualización de un Elbow Plot y así determinar el número de clusters.
fviz_nbclust(dias_salario_norm, kmeans, method="wss")+ 
  geom_vline(xintercept=4, linetype=2)+            
  labs(subtitle = "Elbow method") 

Con esta gráfica podemos visualizar que el número óptimo de clústers son 4 (optimización).

#Visualizar clusters:
dias_salario_cluster<-kmeans(dias_salario_norm,4)
dias_salario_cluster
## K-means clustering with 4 clusters of sizes 159, 5, 43, 30
## 
## Cluster means:
##      no_dias salario_diario_imss
## 1 -0.2906680          0.11501794
## 2  5.8798242          2.45410205
## 3  0.2579630          0.08136604
## 4  0.1908225         -1.13523673
## 
## Clustering vector:
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
##   2   4   4   4   4   4   4   4   4   3   4   4   1   1   4   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   2   4   4   1   1   1   1   1   1   1   1   1   1   1   1   4   3   1   1 
##  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
##   3   3   1   1   1   1   1   1   1   1   1   2   3   1   1   1   1   1   1   4 
##  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
##   1   1   1   1   1   1   1   4   3   4   1   1   1   1   1   1   1   1   1   1 
##  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
##   2   4   4   4   4   3   1   1   1   1   1   1   1   1   1   1   1   4   4   3 
## 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
##   3   1   1   1   1   1   1   1   2   1   1   1   1   1   1   1   1   1   1   1 
## 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
##   1   1   3   1   1   1   1   1   1   1   1   1   1   4   3   3   3   3   1   1 
## 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
##   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   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   1   3   3   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1 
## 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 
##   3   3   3   3   3   3   3   3   3   3   3   3   3   1   3   3   3   3   3   3 
## 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
##   3   3   3   3   1   1   1   1   3   1   1   1   1   1   1   1   1   1   1   1 
## 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 
##   1   3   3   1   1   1   1   1   1   1   1   1   1   1   1   1   1 
## 
## Within cluster sum of squares by cluster:
## [1]   1.009803 184.768864  12.851574  11.956632
##  (between_SS / total_SS =  55.4 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"
#Visualizar resultados:
fviz_cluster(dias_salario_cluster,data=dias_salario_norm)

promedio_dias <- mean(bajas$no_dias)
promedio_dias
## [1] 79.47196

En este clúster podemos observar que por más antigüedad o días que labores en FORM, no hay mucha posibilidad de crecimiento laboral (económicamente hablando), pues según el cluster realizado, no influyen los días laborados con el salario diario.

Creación de Segmentos apartir de los Clústers:

Decidimos usar el Cluster 2 para generar la clasificación de variables y poder comparar con datos cualitativos.

#Añadir a la base de datos la columna de Clusters y su clasificación:
bajas1<-bajas

#Eliminar renglones que tengan número de días en 0 para que las bases de datos tengan los mismos registros
bajas1$Clusters <- edad_salario_cluster$cluster
str(bajas)
## 'data.frame':    237 obs. of  7 variables:
##  $ edad               : num  32 36 24 21 30 46 29 31 50 19 ...
##  $ genero             : Factor w/ 3 levels "CAPJ000926597",..: 3 2 2 2 2 2 2 3 3 3 ...
##  $ motivo_de_baja     : Factor w/ 5 levels "ABANDONO","BAJA POR FALTAS",..: 4 4 4 4 4 2 2 2 2 4 ...
##  $ no_dias            : num  628 60 59 59 51 37 37 31 18 224 ...
##  $ puesto             : Factor w/ 22 levels "ANALISTA DE NOMINAS /AUX DE R.H.",..: 9 5 5 5 5 5 5 5 5 5 ...
##  $ salario_diario_imss: num  500 152 152 152 152 ...
##  $ estado_civil       : Factor w/ 5 levels "","CASADO","DIVORCIADO",..: 4 5 2 4 4 4 5 5 4 4 ...
#Identificamos la clasificación de las distintas edades de los colaboradores.
bajas2<-bajas1 %>% group_by(Clusters) %>% dplyr::summarise(edad=max(edad)) %>% arrange(desc(edad))
bajas1$Cluster_Names<-factor(bajas1$Clusters,levels = c(1,2,3,4), 
                              labels=c("Outlier", "Joven", "Avanzada", "Adulta"))
bajas3 <- bajas1 %>% group_by(Cluster_Names) %>% dplyr::summarize(edad_años=max(edad), salario_imss=mean(salario_diario_imss),Count=n())
clusters<-as.data.frame(bajas3)
clusters
##   Cluster_Names edad_años salario_imss Count
## 1       Outlier        40     177.0462    76
## 2         Joven        32     500.0000     1
## 3      Avanzada        61     176.5644    45
## 4        Adulta        28     176.3151   115

Generación de gráficos:

Gráficos cuantitativos:

Gráfica de barras: (Clusters)
#Se realizó una gráfica para analizar el número de registros por cada segmento: 
ggplot(bajas3,aes(x=reorder(Cluster_Names,Count),y=Count,fill=Cluster_Names)) +
  geom_bar(stat="identity")

Los colaboradores que más bajas han presentado según la gráfica anterior son jóvenes, seguido por los adultos y después los colaboradores de edad avanzada.

Gráfica de dispersión: (Clusters y edad)
#Visualizar la edad por cada segmento:
ggplot(bajas3, aes(x=Cluster_Names,y=edad_años,fill= Cluster_Names,label=round(edad_años,digits=2))) + 
  geom_col() + 
  geom_text()

Visualización de los máximos de cada segmento: Jóvenes (hasta los 28), Avanzada (Hasta los 61), Adulta (Hasta los 40) y el Outlier de 32.

Gráficas mixtas (Datos Cualitativos y Cuantitativos):

Gráfica de barras: (Clusters y Género)
ggplot(bajas1, aes(factor(Cluster_Names), fill = factor(genero))) +
  geom_bar(position = position_dodge2(preserve = "single")) 

Este gráfico nos dice que de cada segmento, hay más mujeres que se han dado de baja.

Gráfica de barras: (Clusters y Estado Civil)
 ggplot(bajas1, aes(factor(Cluster_Names), fill = factor(estado_civil))) +
  geom_bar(position = position_dodge2(preserve = "single"))

Podemos visualizar que de de los jóvenes, la mayoría eran solteros, de los adultos había la misma cantidad de casados, soleros y en unión libre y en avanzados eran más solteros y casados. Más sin embargo, hay muy pocos colaboradores que se han dado de baja divorciados.

Gráfica de barras: (Clusters y Motivo de Baja)
ggplot(bajas1, aes(factor(Cluster_Names), fill = factor(motivo_de_baja))) +
  geom_bar(position = position_dodge2(preserve = "single")) 

Podemos observar que en el segmento de colaboradores jovenes, adultos y de edad avanzada, la mayor parte se ha dado de baja por faltas, siguiendo así por renuncia voluntaria.

La mayor parte de los ex-colaboradores de Form eran Ayudantes generales (76%).

Gráfica ggalluvial: (edad, género y estado civil)
bajas1<-bajas1[-c(17),]
bajas5 <-bajas1 %>% filter(Clusters==4 | Clusters==3) %>% arrange(Clusters)

ggplot(as.data.frame(bajas5),
       aes(y=salario_diario_imss, axis1= genero, axis2=estado_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("Género", "Estado civil"), expand = c(.05, .05)) +
  scale_fill_brewer(type = "qual", palette = "Set1") +
  ggtitle("FORM's Salario Diario por Género y Estado Civil")

En este caso podemos observar que los colaboradores que se han dado de baja y ganan más son en su mayoría mujeres de las cuales la que más ganaba era adulta casada; y las que menos ganaban eran jóvenes en unión libre. Por el otro lado, los hombres que más ganaban eran jovenes solteros y los que menos ganaban eran adultos en unión libre de edad adulta.

Logistic Regression:

Limpieza y visualización de datos:

# file.choose()
nueva_bd_rh <- read.csv("C:\\Users\\maria\\Documents\\ITESM LAET\\Semestre 7\\Reto\\EVI2\\rh_unida.csv")
summary(nueva_bd_rh)
##       edad          genero            antiguedad         puesto         
##  Min.   :18.00   Length:349         Min.   : 0.0000   Length:349        
##  1st Qu.:24.00   Class :character   1st Qu.: 0.0000   Class :character  
##  Median :30.00   Mode  :character   Median : 0.0000   Mode  :character  
##  Mean   :32.69                      Mean   : 0.5501                     
##  3rd Qu.:40.00                      3rd Qu.: 0.0000                     
##  Max.   :73.00                      Max.   :12.0000                     
##  salario_diario  estado_civil       motivo_de_baja        dv_bajas     
##  Min.   :144.4   Length:349         Length:349         Min.   :0.0000  
##  1st Qu.:176.7   Class :character   Class :character   1st Qu.:0.0000  
##  Median :180.7   Mode  :character   Mode  :character   Median :1.0000  
##  Mean   :178.3                                         Mean   :0.6762  
##  3rd Qu.:180.7                                         3rd Qu.:1.0000  
##  Max.   :500.0                                         Max.   :1.0000

Técnica 1. Poner en su respectivo formato cada variable:

nueva_bd_rh$edad <- as.numeric(nueva_bd_rh$edad)
nueva_bd_rh$genero <- as.factor(nueva_bd_rh$genero)
nueva_bd_rh$antiguedad <- as.numeric(nueva_bd_rh$antiguedad)
nueva_bd_rh$puesto<- as.factor(nueva_bd_rh$puesto)
nueva_bd_rh$salario_diario <- as.numeric(nueva_bd_rh$salario_diario)
nueva_bd_rh$estado_civil<- as.factor(nueva_bd_rh$estado_civil)
nueva_bd_rh$dv_bajas <- as.numeric(nueva_bd_rh$dv_bajas)
nueva_bd_rh$motivo_de_baja<- as.factor(nueva_bd_rh$motivo_de_baja)
summary(nueva_bd_rh)
##       edad             genero      antiguedad                        puesto   
##  Min.   :18.00   FEMENINO :200   Min.   : 0.0000   AYUDANTE GENERAL     :246  
##  1st Qu.:24.00   MASCULINO:149   1st Qu.: 0.0000   COSTURERA            : 21  
##  Median :30.00                   Median : 0.0000   SOLDADOR             : 16  
##  Mean   :32.69                   Mean   : 0.5501   AYUDANTE DE EMBARQUES:  7  
##  3rd Qu.:40.00                   3rd Qu.: 0.0000   RESIDENTE            :  7  
##  Max.   :73.00                   Max.   :12.0000   CHOFER               :  6  
##                                                    (Other)              : 46  
##  salario_diario       estado_civil             motivo_de_baja    dv_bajas     
##  Min.   :144.4   CASADO     :108   ABANDONO           :  1    Min.   :0.0000  
##  1st Qu.:176.7   DIVORCIADO :  6   BAJA POR FALTAS    :141    1st Qu.:0.0000  
##  Median :180.7   SOLTERO    :154   JUBILACION         :  1    Median :1.0000  
##  Mean   :178.3   UNION LIBRE: 81   RENUNCIA VOLUNTARIA: 85    Mean   :0.6762  
##  3rd Qu.:180.7                     TERMINO DE CONTRATO:  8    3rd Qu.:1.0000  
##  Max.   :500.0                     NA's               :113    Max.   :1.0000  
## 

Técnica 2. Crear una categoría de referencia para la variable “dv_bajas”:

nueva_bd_rh$dv_bajas<-as.factor(nueva_bd_rh$dv_bajas)
nueva_bd_rh$dv_bajas<-fct_recode(nueva_bd_rh$dv_bajas, "BAJA"="1","NO BAJA"="0")

Visualización de las variables combinadas:

tapply(nueva_bd_rh$salario_diario,
       list(nueva_bd_rh$genero,nueva_bd_rh$estado_civil), mean)
##             CASADO DIVORCIADO  SOLTERO UNION LIBRE
## FEMENINO  179.1338     180.68 175.5099    177.2578
## MASCULINO 180.2972     178.70 180.7174    177.3359

En esta tabla podemos observar el promedio de salario por estado civil y género, donde en este caso, el segmento que mas gana son los hombres solteros, segudios por las mujeres divorciadas y después los hombres casados.

Estimar la Regresión Logística:

Paso 1. Dividir la información entre “training and test datasets”:

set.seed(123) 
training<-nueva_bd_rh$dv_bajas %>% 
  createDataPartition(p=0.75,list=FALSE)
train.data<-nueva_bd_rh[training, ]
test.data<-nueva_bd_rh[-training, ]

Paso 2. Multiple logistic regression:

model<-glm(dv_bajas~salario_diario+estado_civil, data=train.data, family=binomial(link='logit'))
summary(model)
## 
## Call:
## glm(formula = dv_bajas ~ salario_diario + estado_civil, family = binomial(link = "logit"), 
##     data = train.data)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -1.7407  -1.3222   0.7341   0.8478   1.2579  
## 
## Coefficients:
##                          Estimate Std. Error z value Pr(>|z|)  
## (Intercept)              0.914582   0.898918   1.017   0.3090  
## salario_diario          -0.003212   0.004794  -0.670   0.5028  
## estado_civilDIVORCIADO  -0.336367   0.847835  -0.397   0.6916  
## estado_civilSOLTERO      0.504169   0.307234   1.641   0.1008  
## estado_civilUNION LIBRE  0.839215   0.373550   2.247   0.0247 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 330.20  on 261  degrees of freedom
## Residual deviance: 323.16  on 257  degrees of freedom
## AIC: 333.16
## 
## Number of Fisher Scoring iterations: 4

En este modelo de regresión podemos interpretar que los estados civiles de soltero y unión libre son un factor que aumentan la probabilidad de ocasionar una baja de un colaborador. Por el otro lado, el estado civil de divorciado, cuenta con una tendencia negativa, lo que indica que si los colaboradores divorciados aumentan, las bajas disminuirán.

Paso 3. Gráfica de regresión logística:

ggplot(nueva_bd_rh,aes(x=salario_diario, y=as.numeric(dv_bajas) - 1)) + 
  geom_point(alpha=.5) +
  stat_smooth(method="glm", se=FALSE, fullrange=TRUE, method.args = list(family=binomial)) + 
  ylab("Probability") + xlim(100,500)+
  labs(
    title = "Logistic Regression Model", 
    x = "Salario Diario",
    y = "Probabilidad de Bajas"
  )

Con esta gráfica podemos deducir que hay una tendencia negativa de bajas, ya que entre más incrementa el salario diario, es decir, entre más salario se les pague a los colaboradores, menos bajas se registrarán. Por lo que podemos observar una acumulación de bajas de colaboradores que ganan menos de $200 al día, teniendo solo un outlier que gana $500.

Sección 4

1) Hallazgos

  1. FORM produce en promedio 3708.52 kilos de merma por mes.

  2. Se produjo una mayor cantidad de piezas en el mes de agosto.

  3. A un tiempo mayor de producción, FORM le invierte menos horas de calidad en las unidades producidas.

  4. FORM tiene ubicada la mayor parte de su scrap en pre-producción.

  5. Las unidades vendidas de carros aumentaron los últimos 5-10 años y se pronostica que haya un crecimiento positivo de los vehículos en circulación con el paso del tiempo.

  6. El tipo de cambio es la variable que más afecta e impacta la exportación de vehículos ligeros. Entre menos valga el peso (aumente el TC), habrá menos exportaciones.

  7. Si incrementa el empleo en México, se predice que las ventas de vehículos ligeros incrementarán también.

  8. La mayor cantidad de bajas que FORM ha presentado son de colaboradores jóvenes (entre los 20-30 años en su mayoría), siendo el principal motivo la “Baja por Faltas” (59%), hay una mayoría de mujeres y la mayor parte de los ex-colaboradores de Form eran Ayudantes generales (76%).

  9. Actualmente FORM cuenta casi con la misma cantidad de colaboradores femeninos y masculinos y con un rango de edades entre los 18 y los 73 años. Asimismo el promedio de salario ganado es de 170 pesos diarios y los hombres casados y las mujeres solteras son los que ganan más.

  10. Los clientes más importantes para FORM (que más piezas programadas tienen) son Yanfeng, Stabilus 1 y Varroc.

  11. Los clientes con los que FORM presenta más horas de retraso en entregas es con Mahle y Printel (teniendo hasta 20h), clientes que no ordenan muchas piezas. Lo que puede significar que los clientes importantes son prioritarios para FORM y es por eso que con ellos no sufren retrasos.

  12. Hay una tendencia negativa entre el salario y las bajas de colaboradores, si este incrementa, las bajas disminuirán.

  13. Se pronostica que en los próximos años no habrá un incremento en el tiempo de retrasos de entrega de pedidos en los procesos de FORM, sino que se mantendrá estable independientemente del incremento de scrap que se espera en los próximos años, lo cual va de la mano con el incremento de la producción.

2) Sugerencias de mejora para su proceso de analítica de datos

  1. Principalmente se sugiere que FORM cuente con una estandarización de sus bases de datos, que bien, cambien el formato de las variables, solo pongan información que necesiten, que utilicen la misma tipografía, colores y sean ordenados con donde ponen la información. Esto se puede resolver mediante la dedicación de horas de calidad y la utilización de filtros para verificar que los datos cumplan con ciertas métricas y se puedan analizar más adelante, o bien, tomando cursos externos (o contratación de expertos en analítica) para la corrección óptima de sus bases de datos.

  2. Asimismo al estar usando las bases de datos varios colaboradores en conjunto y actualizarlas al día, se suelen cometer varios errores, pues como no es su principal enfoque, lo pueden hacer sin cuidado y sin ponerle atención a los detalles. Por lo que se popone, crear un departamento de analítica de datos en FORM y que esta esté encargada de capturar toda la información para así evitar errores de dedo y hacerlo adecuadamente.

  3. Por último, al FORM ser una empresa chica/mediana sin un departamento de analítica de datos, se sugiere que la empresa se incursione en una plataforma inteligente la cual sistematice la captura de datos y visualización de los mismos, o bien, en un departamento de analítica como se mencionó anteriormente y que este sea capaz de generar reportes con visualizaciones, cumplimiento de metaas y hallazgos relevantes para que los datos registrados sean útiles y se puedan crear nuevas estrategias de mejora.

3) Business Analytics y Business Intelligence

Business Analytics es un método o una solución que las empresas emplean en sus procesos tomando en cuenta su información interna y externa. Personalamente pienso que sus principales funciones consisten en limpiar y transformar sus datos. Sin embargo, algunos de sus principales objetivos son ¨determinar qué conjuntos de datos son útiles, clasificarlos dependiendo de las relaciones existentes entre las variables, organizarlos, filtrarlos, limpiarlos y examinarlos.

El depto. de Business Analytics colabora con el de Business Intelligence, ósea, ya que los colaboradores tienen la información limpia y transformada (BA), estos pueden tomar decisiones más inteligentes basadas en los datos analizados al utilizar diferentes herramientas como el análisis estadístico y predictivo, haciendo estartegias arraigadas principalmente a los objetivos específicos de la empresa, como puede ser el aumentar los ingresos, la productividad, la eficiencia y el rendimiento organizacional (BI).

Para ser mas claros, la relación entre el “Business Intelligence” y el “Business Analytics” es que un experto en Business Intelligence, toma y procesa la información generada por los expertos en Business Analytics y la convierte en decisiones estratégicas para su negocio.

4) Concepto KPI

Un “Key Performance Indicator” es una métrica utilizada en las empresas para medir el desempeño de un área específica. Estas analizan si sus acciones están teniendo los resultados esperados, para después poder tomar decisiones y crear estrategias inteligentes basadas en sus datos.

Asimismo, los KPI´s deben de tener ciertas características para ser información valiosa y útil para las empresas. Estos deben de ser: específicos, continuos y periódicos (medir sobre un cierto periodo de tiempo), objetivos, cuantificables, medibles, realistas, concisos, coherentes y relevantes.

Algunos ejemplos de KPIs comúnmente utilizados en empresas son: - Número de seguidores. - Retorno de la inversión. - Número de ventas mensuales. - Ratio de liquidez.

5) KPIs propuestos para FORM

Tomando en cuenta el análisis planteado para el socioformador FORM, se propone que implemente las siguientes métricas (KPI´s) para medir sus resultados y generar estrategias:

  1. Tasa de rotación de colaboradores: Para el departamento de Recursos Humanos esta métrica puede ser sumamante enriquecedora pues permite conocer el % de altas y bajas en relación al número de empleados. Con esta información, el departamento puede conocer las causas, periodos de tiempo o características específicas por las cuales la retención de colaboradores esta fallando y proponer estrategias para su solución.

  2. Takt time: Esta métrica es ideal para medir el desempeño de las áreas de producción, delivery performance y plan de FORM, pues bien este KPI consiste en medir el tiempo máximo que se puede dedicar a fabricar un producto para cumplir con los plazos de tiempo planeados. Este ayuda a planificar pedidos y decidir si se acepta o no el encargo de un cliente para así evitar horas de retraso.

  3. Tasa de deserción de clientes: Este KPI es escencial para el área comercial y de servicio al cliente de FORM, pues bien al ser FORM una empresa con un segmento muy específico, la retención de sus clientes es sumamente importante, por lo que esta métrica mide la fidelidad calculando la proporción de clientes perdidos y, por lo tanto, ayuda a medir la satisfacción de los clientes.

Referencias bibliográficas

Cera, C. (2021). 32 KPI comerciales para ventas. appvizer.es. Recuperado 21 de octubre de 2022, de https://www.appvizer.es/revista/relacion-cliente/software-crm/kpi-comerciales

Insightsoftware. (2022). Ejemplos de los 30 mejores KPI y métricas de producción para la creación de informes en 2021. insightsoftware Spain. Recuperado 21 de octubre de 2022, de https://insightsoftware.com/es/blog/30-manufacturing-kpis-and-metric-examples/

Galiana, P. (2022). Qué es Business Analytics: definición,tipos y diferencias. Thinking for Innovation. Recuperado 21 de octubre de 2022, de https://www.iebschool.com/blog/que-es-business-analytics-definiciontipos-y-diferencias-big-data/

Bureau of Labor Statistics. (2022). United States: Inflation rate from 1990 to 2022. Statista. Recuperado 21 de octubre de 2022, de https://0-www-statista-com.biblioteca-ils.tec.mx/statistics/191077/inflation-rate-in-the-usa-since-1990/

INEGI. (2022). Venta, producción y exportación de vehículos ligeros. Recuperado 21 de octubre de 2022, de https://www.inegi.org.mx/app/tabulados/default.html?nc=100100090_a

International Trade Administration. (2022). Number of new passenger vehicles and light trucks exported from the United States to China from 2006 to 2021. Statista. https://0-www-statista-com.biblioteca-ils.tec.mx/statistics/244488/vehicle-exports-from-the-united-states-to-china/

TRADING ECONOMICS. (2022). Estados Unidos - PIB | 1960-2021 Datos | 2022-2024 Expectativa. Recuperado 21 de octubre de 2022, de https://es.tradingeconomics.com/united-states/gdp

BEA. (2022). U.S. car sales from 1951 to 2021. Statista. Recuperado 21 de octubre de 2022, de https://0-www-statista-com.biblioteca-ils.tec.mx/statistics/199974/us-car-sales-since-1951/

LS0tDQp0aXRsZTogPHNwYW4gc3R5bGU9IkNvbG9yOk9yYW5nZSIgPiBFdmlkZW5jaWEgMg0KYXV0aG9yOiAiTWFyaWFuYSBVbGxvYSAtIEEwMTI1MzA3NiINCmRhdGU6ICIyMDIyLTA5LTI2Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KPGltZyBzcmM9ICJDOlxcVXNlcnNcXG1hcmlhXFxEb2N1bWVudHNcXElURVNNIExBRVRcXFNlbWVzdHJlIDdcXFJldG9cXGxvZ28gZm9ybS5wbmciPg0KDQojICoqSW50cm9kdWNjacOzbioqDQoNCjxkaXYgY2xhc3M9dGV4dC1qdXN0aWZ5Pg0KDQpGT1JNIGVzIHVuYSBlbXByZXNhIG51ZXZvbGVvbmVuc2UgZnVuZGFkYSBlbiAyMDExLCBsYSBjdWFsIGF0aWVuZGUgcHJpbmNpcGFsbWVudGUgYSAqKmNsaWVudGVzIGRlbnRybyBkZWwgc2VjdG9yIGF1dG9tb3RyaXoqKiBvZnJlY2llbmRvIGVsICpkZXNhcnJvbGxvIGRlIGVtcGFxdWVzIHBlcnNvbmFsaXphZG9zIGNvbiBlbCBvYmpldGl2byBkZSBnZW5lcmFyIGFob3Jyb3MsIGVmaWNpZW5jaWFzLCBwcm9kdWN0aXZpZGFkIHkgcmVudGFiaWxpZGFkLioNCg0KRWwgcmVwb3J0ZSBxdWUgc2UgcHJlc2VudGFyw6EgYSBjb250aW51YWNpw7NuIHNlIHJlYWxpesOzIGNvbiBlbCBvYmpldGl2byBkZSAqYW5hbGl6YXIgbGFzIGRpZmVyZW50ZXMgw6FyZWFzIGRlIGxhIGVtcHJlc2EgbWVkaWFudGUgZWwgdXNvIGRlIHN1cyBiYXNlcyBkZSBkYXRvcyBpbnRlcm5hcyB5IGVsIHVzbyBkZSBpbmZvcm1hY2nDs24gZXh0ZXJuYSBwYXJhIGxvZ3JhciBtZWpvcmFzIGVuIHN1cyBwcm9jZXNvcy4qDQoNCjwvZGl2Pg0KDQojIyMgSW5zdGFsYXIgbGlicmVyaWFzOg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGphbml0b3IpDQpsaWJyYXJ5KHBzeWNoKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGphbml0b3IpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShkZXNjcikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHBsb3RyaXgpDQpsaWJyYXJ5KGdyYXBoaWNzKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGZsZXh0YWJsZSkNCmxpYnJhcnkoY3Jvc3N0YWJsZSkNCmxpYnJhcnkodHNlcmllcykNCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KGFzdHNhKQ0KbGlicmFyeShqdG9vbHMpDQpsaWJyYXJ5KGxtdGVzdCkNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShvbHNycikNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KGZvcmVpZ24pDQpsaWJyYXJ5KGZhY3RvZXh0cmEpICANCmxpYnJhcnkoZ2dhbGx1dmlhbCkNCmxpYnJhcnkodmlyaWRpcykgICAgICANCmxpYnJhcnkoc2NhbGVzKSANCiNpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpsaWJyYXJ5KGNhcmV0KQ0KYGBgDQoNCiMgKipTZWNjacOzbiAxKioNCiMjICoqYSkgIFJIIC0gQ29sYWJvcmFkb3JlcyAqKg0KIyMjICoxLiBMaW1waWV6YSwgVHJhbnNmb3JtYWNpw7NuLCB5IE9yZ2FuaXphY2nDs24gZGUgQmFzZXMgZGUgRGF0b3M6Kg0KIyMjIyMgKipOb3RhczoqKiBBbnRlcyBkZSBpbXBvcnRhciBsYSBiYXNlIGRlIGRhdG9zIHNlIG9wdMOzIHBvcjoNCiMjIyMjIFJlbW92ZXIgY29sdW1uYXMgZ2VvZ3LDoWZpY2FzIGUgaW5uZWNlc2FyaWFzLg0KIyMjIyMgSG9tb2dlbml6YXIgY2llcnRhcyBjYXRlZ29yw61hcyBjb21vICJlc3RhZG8gY2l2aWwiIHkgInB1ZXN0byIuDQojIyMjIyBSZWVtcGxhemFyIGNpZnJhcyBkZSBzYWxhcmlvcyBpbnVzdWFsZXMgcG9yIGVsIHByb21lZGlvLg0KIyMjIyMgQWdyZWdhciBjb2x1bW5hIGRlICJFZGFkIiB5ICJBbnRpZ3VlZGFkIi4NCg0KIyMjIyAqKlTDqWNuaWNhIDEuIEltcG9ydGFyIGxhIGJhc2UgZGUgZGF0b3MgeSBsaW1waWFyIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcyBjb24gbGEgZnVuY2nDs24gImNsZWFuX25hbWVzIjoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgZmlsZS5jaG9vc2UoKQ0KYmQgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcbWFyaWFcXERvY3VtZW50c1xcSVRFU00gTEFFVFxcU2VtZXN0cmUgN1xcUmV0b1xcRk9STSBCQVNFUyBERSBEQVRPU1xcRk9STSAtIFJlY3Vyc29zIEh1bWFub3MgLSBjb2xhYm9yYWRvcmVzLmNzdiIpDQpjb2xhYm9yYWRvcmVzIDwtIGNsZWFuX25hbWVzKGJkKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSAyLiBCb3JyYXIgY29sdW1uYXMgaW5uZWNlc2FyaWFzOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY29sYWJvcmFkb3JlcyA8LSBzdWJzZXQoY29sYWJvcmFkb3Jlcywgc2VsZWN0ID0gLWMobm9fZW1wbGVhZG8sIG5vbWJyZV9jb21wbGV0bywgZmVjaGFfYWx0YSwgbWFub19kZV9vYnJhKSkNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMy4gUmVlbXBsZXphciBOQXMgY29uIGVsIHByb21lZGlvIGVuIGxhIGNvbHVtbmEgZGUgIlNhbGFyaW8gRGlhcmlvIiAoQkQgLSBDb2xhYm9yYWRvcmVzKS4qKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNvbGFib3JhZG9yZXMkc2FsYXJpb19kaWFyaW9baXMubmEoY29sYWJvcmFkb3JlcyRzYWxhcmlvX2RpYXJpbyldPC1tZWRpYW4oY29sYWJvcmFkb3JlcyRzYWxhcmlvX2RpYXJpbywgbmEucm0gPSBUUlVFKQ0KYGBgDQpTZSB1dGlsaXphIGxhIG1lZGlhbmEgZW4gdmV6IGRlbCBwcm9tZWRpbyBwYXJhIGRlc2NhcnRhciBkYXRvcyAib3V0bGllcnMiIHkgbm9ybWFsbWVudGUgdG9tYSBsb3MgZGF0b3MgbcOhcyBwb3B1bGFyZXMuDQoNCiMjIyMgKipUw6ljbmljYSA0LiBDb252ZXJ0aXIgbGFzIHZhcmlhYmxlcyBhIGZhY3RvcmVzIG8gbsO6bWVyb3MgZW50ZXJvcyAoQkQgLSBDb2xhYm9yYWRvcmVzKS4qKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNvbGFib3JhZG9yZXMkZWRhZDwtYXMubnVtZXJpYyhjb2xhYm9yYWRvcmVzJGVkYWQpDQpjb2xhYm9yYWRvcmVzJGdlbmVybzwtYXMuZmFjdG9yKGNvbGFib3JhZG9yZXMkZ2VuZXJvKQ0KY29sYWJvcmFkb3JlcyRhbnRpZ3VlZGFkPC1hcy5udW1lcmljKGNvbGFib3JhZG9yZXMkYW50aWd1ZWRhZCkNCmNvbGFib3JhZG9yZXMkcHVlc3RvPC1hcy5mYWN0b3IoY29sYWJvcmFkb3JlcyRwdWVzdG8pDQpjb2xhYm9yYWRvcmVzJHNhbGFyaW9fZGlhcmlvPC1hcy5udW1lcmljKGNvbGFib3JhZG9yZXMkc2FsYXJpb19kaWFyaW8pDQpjb2xhYm9yYWRvcmVzJGVzdGFkb19jaXZpbDwtYXMuZmFjdG9yKGNvbGFib3JhZG9yZXMkZXN0YWRvX2NpdmlsKQ0Kc3VtbWFyeSAoY29sYWJvcmFkb3JlcykNCmBgYA0KDQojIyMjICoqRXhwb3J0YXIgYmFzZSBkZSBkYXRvcyBsaW1waWEqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCndyaXRlLmNzdihjb2xhYm9yYWRvcmVzLCBmaWxlPSJjb2xhYm9yYWRvcmVzX2JkX2xpbXBpYS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyMgKjIuIENsYXNpZmljYWNpw7NuIGRlIHZhcmlhYmxlczoqDQojIyMjICoqwr9DdcOhbnRhcyB2YXJpYWJsZXMgeSBjdcOhbnRvcyByZWdpc3Ryb3MgdGllbmUgbGEgYmFzZSBkZSBkYXRvcz8qKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRlc2NyaWJlRGF0YShjb2xhYm9yYWRvcmVzLGhlYWQ9MSx0YWlsPTEpDQpgYGANCkxhIGJhc2UgZGUgZGF0b3MgZGUgIlJIIC0gQ29sYWJvcmFkb3JlcyIgY3VlbnRhIGNvbiAqKjYgdmFyaWFibGVzIHkgMTEzIHJlZ2lzdHJvcyBlbiBjYWRhIGNvbHVtbmEsIHNpZW5kbyA2NzggcmVnaXN0cm9zIHRvdGFsZXMuKioNCg0KIyMjIyAqKlRhYmxhIGRlIGNsYXNpZmljYWNpw7NuOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVmFyaWFibGU8LWMoIkVkYWQiLCAiR8OpbmVybyIsICJBbnRpZ8O8ZWRhZCIsICJQdWVzdG8iLCAiU2FsYXJpbyBEaWFyaW8iLCAiRXN0YWRvIENpdmlsIiApDQpUaXBvPC1jKCJDdWFudGl0YXRpdmEgRGlzY3JldGEiLCAiQ3VhbGl0YXRpdmEiLCAiQ3VhbnRpdGF0aXZhIERpc2NyZXRhIiwgIkN1YWxpdGF0aXZhIiwgIkN1YW50aXRhdGl2YSBjb250aW51YSIsICJDdWFsaXRhdGl2YSIpDQpNZWRpZGEgPC1jKCJBw7FvcyIsICJOQSIsICJBw7FvcyIsICJOQSIsICJQZXNvcyBNZXhpY2Fub3MiLCAiTkEiKQ0KdGFibGU8LWRhdGEuZnJhbWUoVmFyaWFibGUsVGlwbywgTWVkaWRhKQ0KdGFibGUNCmtuaXRyOjprYWJsZSh0YWJsZSkNCmBgYA0KDQojIyMgKjMuIEdyw6FmaWNvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvczoqDQojIyMjICoqR3LDoWZpY2EgZGUgQmFycmFzIChTYWxhcmlvIHkgR8OpbmVybyk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpoaXN0U3RhY2soY29sYWJvcmFkb3JlcyRzYWxhcmlvX2RpYXJpbywgY29sYWJvcmFkb3JlcyRnZW5lcm8sIHhsYWI9ICJTYWxhcmlvIERpYXJpbyIsIHlsYWI9ICJDYW50aWRhZCBkZSBFbXBsZWFkb3MiLCBsZWdlbmQucG9zPSJ0b3ByaWdodCIpDQpgYGANCkNvbiBlc3RlIGdyw6FmaWNvIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIHNvbG8gKipoYXkgdW4gY29sYWJvcmFkb3IgcXVlIGdhbmEgbcOhcyBkZSAkMzAwIGRpYXJpb3MgeSBlcyB1bmEgTXVqZXIuKiogQXNpbWlzbW8sIG9ic2VydmFtb3MgcXVlICpsYSBtYXlvcsOtYSBkZSBsb3MgY29sYWJvcmFkb3JlcyBnYW5hbiBlbnRyZSAxNzAtMjAwIHBlc29zIGRpYXJpb3MqLCBoYWJpZW5kbyAqbcOhcyBtdWplcmVzKiBlbiBlc3RlIHNlZ21lbnRvLg0KDQojIyMjICoqSGlzdG9ncmFtYSAoRWRhZCk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpoaXN0KHggPSBjb2xhYm9yYWRvcmVzJGVkYWQsIG1haW4gPSAiSGlzdG9ncmFtYSBkZSBFZGFkIiwgDQogICAgIHhsYWIgPSAiRWRhZCIsIHlsYWIgPSAiRnJlY3VlbmNpYSIsDQogICAgIGNvbCA9ICJvcmFuZ2UiKQ0KYGBgDQpDb24gZXN0ZSBncsOhZmljbyBwb2RlbW9zIG9ic2VydmFyIHF1ZSAqKmxhIG1heW9yw61hIGRlIGxvcyBjb2xhYm9yYWRvcmVzIGRlIEZPUk0gdGllbmVuIGVudHJlIDI1IGEgMzAgYcOxb3MuKioNCg0KIyMjIyAqKkdyw6FmaWNhIGRlIGJhcnJhcyAoR8OpbmVybywgZXN0YWRvIGNpdsOtbCB5IHNhbGFyaW8gZGlhcmlvKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmdncGxvdChjb2xhYm9yYWRvcmVzLCBhZXMoeD1nZW5lcm8sIHk9c2FsYXJpb19kaWFyaW8sIGZpbGw9Z2VuZXJvKSkgKyANCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArIA0KICBmYWNldF9ncmlkKH5lc3RhZG9fY2l2aWwpICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikNCmBgYA0KQ29uIGVzdGUgZ3LDoWZpY28gcG9kZW1vcyBvYnNlcnZhciBxdWUgKipsb3MgcXVlIGVzdMOhbiBjYXNhZG9zIHkgc29sdGVyb3Mgc3VlbGVuIGdhbmFyIG3DoXMgcXVlIGxvcyBxdWUgZXN0w6FuIGRpdm9yY2lhZG9zIHkgZW4gdW5pw7NuIGxpYnJlLioqIEFkZW3DoXMsICpsb3MgaG9tYnJlcyBjYXNhZG9zIHkgbGFzIG11amVyZXMgc29sdGVyYXMgc29uIGxvcyBxdWUgZ2FuYW4gbcOhcy4qDQoNCiMjIyAqNC4gUHJpbmNpcGFsZXMgRXN0YWTDrXN0aWNvcyBEZXNjcmlwdGl2b3M6Kg0KDQojIyMjICoqTWVkaWE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptZWRpYV9lZGFkIDwtIG1lYW4oY29sYWJvcmFkb3JlcyRlZGFkKQ0KbWVkaWFfZWRhZA0KDQptZWRpYV9hbnRpZ3VlZGFkIDwtIG1lYW4oY29sYWJvcmFkb3JlcyRhbnRpZ3VlZGFkKQ0KbWVkaWFfYW50aWd1ZWRhZA0KDQptZWRpYV9zYWxhcmlvIDwtIG1lYW4oY29sYWJvcmFkb3JlcyRzYWxhcmlvX2RpYXJpbykNCm1lZGlhX3NhbGFyaW8NCmBgYA0KDQojIyMjICoqTWVkaWFuYToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1lZGlhbmFfZWRhZCA8LSBtZWRpYW4oY29sYWJvcmFkb3JlcyRlZGFkKQ0KbWVkaWFuYV9lZGFkDQoNCm1lZGlhbmFfYW50aWd1ZWRhZCA8LSBtZWRpYW4oY29sYWJvcmFkb3JlcyRhbnRpZ3VlZGFkKQ0KbWVkaWFuYV9hbnRpZ3VlZGFkDQoNCm1lZGlhbmFfc2FsYXJpbyA8LSBtZWRpYW4oY29sYWJvcmFkb3JlcyRzYWxhcmlvX2RpYXJpbykNCm1lZGlhbmFfc2FsYXJpbyANCmBgYA0KDQojIyMjICoqTW9kYToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1vZGUgPC0gZnVuY3Rpb24oeCkgew0KICB1eCA8LSB1bmlxdWUoeCkNCiAgdXhbd2hpY2gubWF4KHRhYnVsYXRlKG1hdGNoKHgsIHV4KSkpXQ0KfQ0KbW9kYV9lZGFkIDwtIG1vZGUoY29sYWJvcmFkb3JlcyRlZGFkKQ0KbW9kYV9lZGFkDQoNCm1vZGFfYW50aWd1ZWRhZCA8LSBtb2RlKGNvbGFib3JhZG9yZXMkYW50aWd1ZWRhZCkNCm1vZGFfYW50aWd1ZWRhZA0KDQptb2RhX3NhbGFyaW8gPC0gbW9kZShjb2xhYm9yYWRvcmVzJHNhbGFyaW9fZGlhcmlvKQ0KbW9kYV9zYWxhcmlvIA0KYGBgDQoNCiMjIyMgKipEZXN2aWFjacOzbiBFc3TDoW5kYXI6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp2YXJpYW56YV9lZGFkIDwtdmFyKGNvbGFib3JhZG9yZXMkZWRhZCkNCnZhcmlhbnphX2VkYWQNCg0KdmFyaWFuemFfYW50aWd1ZWRhZCA8LXZhcihjb2xhYm9yYWRvcmVzJGFudGlndWVkYWQpDQp2YXJpYW56YV9hbnRpZ3VlZGFkDQoNCnZhcmlhbnphX3NhbGFyaW8gPC12YXIoY29sYWJvcmFkb3JlcyRzYWxhcmlvX2RpYXJpbykNCnZhcmlhbnphX3NhbGFyaW8NCg0KZGVzdmlhY2lvbl9lZGFkIDwtIHNxcnQodmFyaWFuemFfZWRhZCkNCmRlc3ZpYWNpb25fZWRhZA0KDQpkZXN2aWFjaW9uX2FudGlndWVkYWQgPC0gc3FydCh2YXJpYW56YV9hbnRpZ3VlZGFkKQ0KZGVzdmlhY2lvbl9hbnRpZ3VlZGFkDQoNCmRlc3ZpYWNpb25fc2FsYXJpbyA8LSBzcXJ0KHZhcmlhbnphX3NhbGFyaW8pDQpkZXN2aWFjaW9uX3NhbGFyaW8NCmBgYA0KDQojIyMjICoqVGFibGEgZGUgZnJlY3VlbmNpYSAoYW7DoWxpc2lzIGVzdGFkw61zdGljbyk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpWYXJpYWJsZSA8LWMoIkVkYWQiLCJBbnRpZ8O8ZWRhZCIsIlNhbGFyaW8gRGlhcmlvIikNClByb21lZGlvIDwtYygiMzYuMDYiLCAiMS40MiIsIjE3OS4wOSIpDQpNb2RhIDwtYygiMzIiLCIwIiwiMTgwLjY4IikNCk1lZGlhbmEgPC1jKCIzNCIsIjAiLCIxODAuNjgiKQ0KRGVzdmlhY2nDs25fRXN0w6FuZGFyIDwtYyAoIjEyLjg0IiwiMi41MiIsIjI0LjI4IikNCnRhYmxhIDwtZGF0YS5mcmFtZShWYXJpYWJsZSxQcm9tZWRpbywgTW9kYSwgTWVkaWFuYSwgRGVzdmlhY2nDs25fRXN0w6FuZGFyKQ0KdGFibGENCmtuaXRyOjprYWJsZSh0YWJsYSkNCmBgYA0KDQpEZSBlc3RvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvcyBwb2RlbW9zIGRlc3RhY2FyIHByaW5jaXBhbG1lbnRlIGxvcyBzaWd1aWVudGVzIGhhbGxhemdvczoNCg0KKioxLiBMYSBlZGFkIHByb21lZGlvKiogZGUgbG9zIGNvbGFib3JhZG9yZXMgYWN0dWFsZXMgZXMgZGUgKiozNiBhw7Fvcy4qKg0KDQoqKjIuIExhIG1heW9yw61hIGRlIGxvcyBjb2xhYm9yYWRvcmVzIGdhbmEgJDE4MC42OCBwZXNvcyBkaWFyaW9zIChtZWRpYW5hKS4qKg0KDQoqKjMuKiogRXhpc3RlbiBjb2xhYm9yYWRvcmVzIG11eSBhbnRpZ3VvcyB5IG11eSByZWNpZW50ZXMsIHNpbiBlbUJhcmdvLCAqKmxhIG1heW9yw61hIG5vIHRpZW5lIG5pIDEgYcOxbyBhw7puIGVuIGxhIGVtcHJlc2EgKG1lZGlhbmEpLioqDQoNCiMjICoqYikgUkggLSBCYWphcyoqDQojIyMgKjEuIExpbXBpZXphLCBUcmFuc2Zvcm1hY2nDs24sIHkgT3JnYW5pemFjacOzbiBkZSBCYXNlcyBkZSBEYXRvczoqDQojIyMjIyAqKk5vdGFzOioqIEFudGVzIGRlIGltcG9ydGFyIGxhIGJhc2UgZGUgZGF0b3Mgc2Ugb3B0w7MgcG9yOg0KIyMjIyMgSG9tb2dlbml6YXIgY2llcnRhcyBjYXRlZ29yw61hcyBjb21vICJlc3RhZG8gY2l2aWwiIHkgInB1ZXN0byIuDQojIyMjIyBBZ3JlZ2FyIGNvbHVtbmEgZGUgIkVkYWQiLg0KIyMjIyMgTm9tYnJhciBsYSBjb2x1bW5hIGRlICJub19kaWFzIi4NCg0KIyMjIyAqKlTDqWNuaWNhIDE6IEltcG9ydGFyIGxhIGJhc2UgZGUgZGF0b3MgeSBsaW1waWFyIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcyBjb24gbGEgZnVuY2nDs24gImNsZWFuX25hbWVzIjoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmJkMSA8LSByZWFkLmNzdigiQzpcXFVzZXJzXFxtYXJpYVxcRG9jdW1lbnRzXFxJVEVTTSBMQUVUXFxTZW1lc3RyZSA3XFxSZXRvXFxGT1JNIEJBU0VTIERFIERBVE9TXFxGT1JNIC0gUmVjdXJzb3MgSHVtYW5vcyAtIEJBSkFTIEJVRU5BLmNzdiIpDQpiYWphcyA8LSBjbGVhbl9uYW1lcyhiZDEpDQpzdW1tYXJ5KGJhamFzKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSAyLiBCb3JyYXIgY29sdW1uYXMgaW5uZWNlc2FyaWFzOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYmFqYXMgPC0gc3Vic2V0KGJhamFzLCBzZWxlY3QgPSAtYyhhcGVsbGlkb3MsIG5vbWJyZSwgZmVjaGFfZGVfbmFjaW1pZW50bywgcmZjLCBkZXBhcnRhbWVudG8sIG5vX3NlZ3Vyb19zb2NpYWwsIGZhY3Rvcl9jcmVkX2luZm9uYXZpdCwgbl9jcmVkaXRvX2luZm9uYXZpdCwgbHVnYXJfZGVfbmFjaW1pZW50bywgY3VycCwgY2FsbGUsIG51bWVyb19pbnRlcm5vLCBjb2xvbmlhLCBjb2RpZ29fcG9zdGFsLCBtdW5pY2lwaW8sIGVzdGFkbywgdGFyamV0YV9jdWVudGEsIGZlY2hhX2RlX2FsdGEsIGJhamEpKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSAzLiBSZWVtcGxhemFyIE5BwrRzIGNvbiBsYSBtZWRpYW5hIGVuICJub19kaWFzIiB5IHJlZW1wbGF6YXIgY29uIGVsIHByb21lZGlvIGVuICJlZGFkIiBwYXJhIG5vIHRlbmVyIHRhbnRvcyBlc3BhY2lvcyBlbiBibGFuY286DQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYmFqYXMkbm9fZGlhc1tpcy5uYShiYWphcyRub19kaWFzKV08LW1lZGlhbihiYWphcyRub19kaWFzLCBuYS5ybSA9IFRSVUUpDQpiYWphcyRlZGFkW2lzLm5hKGJhamFzJGVkYWQpXTwtbWVhbihiYWphcyRlZGFkLCBuYS5ybSA9IFRSVUUpDQpgYGANCiMjIyMgKipUw6ljbmljYSA0OkNvbnZlcnRpciBsYXMgdmFyaWFibGVzIGEgZmFjdG9yZXMsIG7Dum1lcm9zIGVudGVyb3MgbyBmZWNoYXM6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpiYWphcyRlZGFkPC1hcy5udW1lcmljKGJhamFzJGVkYWQpDQpiYWphcyRnZW5lcm88LWFzLmZhY3RvcihiYWphcyRnZW5lcm8pDQpiYWphcyRtb3Rpdm9fZGVfYmFqYTwtYXMuZmFjdG9yKGJhamFzJG1vdGl2b19kZV9iYWphKQ0KYmFqYXMkbm9fZGlhczwtYXMubnVtZXJpYyhiYWphcyRub19kaWFzKQ0KYmFqYXMkcHVlc3RvPC1hcy5mYWN0b3IoYmFqYXMkcHVlc3RvKQ0KYmFqYXMkc2FsYXJpb19kaWFyaW9faW1zczwtYXMubnVtZXJpYyhiYWphcyRzYWxhcmlvX2RpYXJpb19pbXNzKQ0KYmFqYXMkZXN0YWRvX2NpdmlsPC1hcy5mYWN0b3IoYmFqYXMkZXN0YWRvX2NpdmlsKQ0Kc3VtbWFyeSAoYmFqYXMpDQpgYGANCg0KIyMjIyMgRXhwb3J0YXIgYmFzZSBkZSBkYXRvcyBsaW1waWENCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp3cml0ZS5jc3YoYmFqYXMsIGZpbGU9ImJhamFzX2JkX2xpbXBpYS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyMgKjIuIENsYXNpZmljYWNpw7NuIGRlIHZhcmlhYmxlczoqDQojIyMjICoqwr9DdcOhbnRhcyB2YXJpYWJsZXMgeSBjdcOhbnRvcyByZWdpc3Ryb3MgdGllbmUgbGEgYmFzZSBkZSBkYXRvcz8qKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRlc2NyaWJlRGF0YShiYWphcyxoZWFkPTEsdGFpbD0xKQ0KYGBgDQpMYSBiYXNlIGRlIGRhdG9zIGRlICJSSCAtIENvbGFib3JhZG9yZXMiIGN1ZW50YSBjb24gKio3IHZhcmlhYmxlcyB5IDIzNiByZWdpc3Ryb3MgZW4gY2FkYSBjb2x1bW5hLCBzaWVuZG8gMSw2NTIgcmVnaXN0cm9zIHRvdGFsZXMuKioNCg0KIyMjIyAqKlRhYmxhIGRlIGNsYXNpZmljYWNpw7NuOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVmFyaWFibGVfYmFqYXM8LWMoIkVkYWQiLCAiR8OpbmVybyIsICJNb3Rpdm8gZGUgQmFqYSIsICJOby4gRMOtYXMiLCAiUHVlc3RvIiwgIlNhbGFyaW8gRGlhcmlvIiwgIkVzdGFkbyBDaXZpbCIgKQ0KVGlwb19iYWphczwtYygiQ3VhbnRpdGF0aXZhIERpc2NyZXRhIiwgIkN1YWxpdGF0aXZhIiwgIkN1YWxpdGF0aXZhIiwgIkN1YW50aXRhdGl2YSBEaXNjcmV0YSIsICJDdWFsaXRhdGl2YSIsICJDdWFudGl0YXRpdmEgY29udGludWEiLCAiQ3VhbGl0YXRpdmEiKQ0KTWVkaWRhX2JhamFzIDwtYygiQcOxb3MiLCAiTkEiLCAiTkEiLCAiQcOxb3MiLCAiTkEiLCAiUGVzb3MgTWV4aWNhbm9zIiwgIk5BIikNCnRhYmxhX2JhamFzPC1kYXRhLmZyYW1lKFZhcmlhYmxlX2JhamFzLFRpcG9fYmFqYXMsIE1lZGlkYV9iYWphcykNCnRhYmxhX2JhamFzDQprbml0cjo6a2FibGUodGFibGFfYmFqYXMpDQpgYGANCg0KIyMjICozLiBHcsOhZmljb3MgZXN0YWTDrXN0aWNvcyBkZXNjcmlwdGl2b3M6Kg0KIyMjIyAqKkdyw6FmaWNvIGRlIGJhcnJhcyAoZWRhZCk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpoaXN0KGJhamFzJGVkYWQsIGZyZXE9VFJVRSwgY29sPSJwaW5rIiwgbWFpbj0iRWRhZCBlbiBhw7FvcyBkZSBsb3MgRXgtQ29sYWJvcmFkb3JlcyIpDQpgYGANCg0KTG9zICoqY29sYWJvcmFkb3JlcyBxdWUgc2UgaGFuIGRhZG8gZGUgYmFqYSBlbiBzdSBtYXlvcsOtYSB0aWVuZW4gZW50cmUgMjAgeSAzMCBhw7FvcyoqLCBzaWVuZG8gYXPDrSBsb3MgcXVlIHNlIHZhbiBtw6FzIGrDs3ZlbmVzLg0KDQojIyMjICoqR3LDoWZpY2EgZGUgZGlzcGVyc2nDs24gKGTDrWFzIGRlIHRyYWJham8geSBzYWxhcmlvKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmdncGxvdChkYXRhPWJhamFzLCBtYXBwaW5nID0gYWVzKG5vX2RpYXMsIHNhbGFyaW9fZGlhcmlvX2ltc3MpKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZ2VuZXJvKSkgKyB0aGVtZV9idygpDQpjb3VudChiYWphcywgIm5vX2RpYXMiKQ0KYGBgDQoNCkNvbiBlc3RhIGdyw6FmaWNhIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlICoqZWwgY29sYWJvcmFkb3IgcXVlIGdhbmFiYSBtw6FzIGVyYSBob21icmUgKG91dGxpZXIpKiosIHB1ZXMgZnVlIGVsIMO6bmljbyBjb2xhYm9yYWRvciBxdWUgc2UgZGlvIGRlIGJhamEgKipnYW5hbmRvIG3DoXMgZGUgMTgwLjY4IChtb2RhKSoqLiBBc2ltaXNtbywgaGFuIGhhYmlkbyBiYWphcyBkZSBhbWJvcyBnw6luZXJvcywgc2luIGVtYmFyZ28sIGhheSB1bmEgKiphY3VtdWxhY2nDs24gZGUgYmFqYXMgZW4gY29sYWJvcmFkb3JlcyBxdWUgdGVuw61hbiBtdXkgcG9jbyB0aWVtcG8gdHJhYmFqYW5kbyBlbiBGT1JNLCB0ZW5pZW5kbyBtZW5vcyBkZSAzIG1lc2VzICg3NCBkw61hcyBlbiBwcm9tZWRpbykgbGFib3JhbmRvLioqIA0KDQojIyMjICoqR3LDoWZpY28gZGUgUGF5IGN1YWxpdGF0aXZvICgjIGRlIGJhamFzIHkgbW90aXZvKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRhYmxlKGJhamFzJG1vdGl2b19iYWphKQ0KcHJvcG9yY2lvbmVzIDwtIGMoMSwgMTQxLCAxLCA4NywgOCkNCmV0aXF1ZXRhcyA8LSBjKCJBYmFuZG9ubyIsICJCYWphIHBvciBGYWx0YXMiLCAiSnViaWxhY2nDs24iLCAiUmVudW5jaWEgVm9sdW50YXJpYSIsICJUZXJtaW5vIGRlIENvbnRyYXRvIikNCnBjdCA8LSByb3VuZChwcm9wb3JjaW9uZXMvc3VtKHByb3BvcmNpb25lcykqMTAwKQ0KZXRpcXVldGFzIDwtIHBhc3RlKGV0aXF1ZXRhcywgcGN0KQ0KZXRpcXVldGFzIDwtIHBhc3RlKGV0aXF1ZXRhcywiJSIsc2VwPSIiKQ0KcGllKHByb3BvcmNpb25lcyxsYWJlbHMgPSBldGlxdWV0YXMsDQogICAgY29sPXJhaW5ib3cobGVuZ3RoKGV0aXF1ZXRhcykpLA0KICAgIG1haW49Ik1vdGl2byBkZSBiYWphcyBkZSBsb3MgQ29sYWJvcmFkb3JlcyIpDQpgYGANCg0KQ29uIGVzdGEgZ3LDoWZpY2EgcG9kZW1vcyBvYnNlcnZhciBxdWUgKiplbCBtb3Rpdm8gbcOhcyBjb23Dum4gZGUgYmFqYSBkZSBsb3MgY29sYWJvcmFkb3JlcyBoYW4gc2lkbyBwb3IgZGVzcGlkb3MgcG9yIGZhbHRhcyBjb24gdW4gNTklIHkgcG9yIHJlbnVuY2lhIHZvbHVudGFyaWEgcG9yIHVuIDM3JS4qKg0KDQojIyMjICoqR3LDoWZpY28gZGUgUGF5IGN1YWxpdGF0aXZvICgjIGRlIGJhamFzIHkgcHVlc3RvKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRhYmxlKGJhamFzJHB1ZXN0bykNCnByb3BvcmNpb25lc19wdWVzdG8gPC0gYygyMywgMTc5LCAxMSwgMTEsIDcsIDUpDQpldGlxdWV0YXNfcHVlc3RvIDwtIGMoIk90cm9zIiwiQXl1ZGFudGUgR2VuZXJhbCIsICJDb3N0dXJlcmEiLCAiU29sZGFkb3IiLCAiQXl1ZGFudGUgRW1iYXJxdWVzIiwgIk1vbnRhY2FyZ3Vpc3RhIikNCnBjdF9wdWVzdG8gPC0gcm91bmQocHJvcG9yY2lvbmVzX3B1ZXN0by9zdW0ocHJvcG9yY2lvbmVzX3B1ZXN0bykqMTAwKQ0KZXRpcXVldGFzX3B1ZXN0byA8LSBwYXN0ZShldGlxdWV0YXNfcHVlc3RvLCBwY3RfcHVlc3RvKQ0KZXRpcXVldGFzX3B1ZXN0byA8LSBwYXN0ZShldGlxdWV0YXNfcHVlc3RvLCIlIixzZXA9IiIpDQpwaWUocHJvcG9yY2lvbmVzX3B1ZXN0byxsYWJlbHMgPSBldGlxdWV0YXNfcHVlc3RvLA0KICAgIGNvbD1yYWluYm93KGxlbmd0aChldGlxdWV0YXNfcHVlc3RvKSksDQogICAgbWFpbj0iUHVlc3RvIGRlIGxvcyBFeC1Db2xhYm9yYWRvcmVzIikNCmBgYA0KDQpMYSBtYXlvciBwYXJ0ZSBkZSBsb3MgZXgtY29sYWJvcmFkb3JlcyBkZSBGb3JtIGVyYW4gKipBeXVkYW50ZXMgZ2VuZXJhbGVzICg3NiUpLioqDQoNCiMjIyMgKipUYWJsYSBjcnV6YWRhIChlc3RhZG8gY2l2aWwsIGfDqW5lcm8geSBtb3Rpdm8gZGUgYmFqYSk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjcm9zc3RhYmxlKGJhamFzLCBjKGVzdGFkb19jaXZpbCwgZ2VuZXJvKSwgYnk9bW90aXZvX2RlX2JhamEsIHRvdGFsID0gImJvdGgiKSAlPiUgYXNfZmxleHRhYmxlKCkNCmBgYA0KDQpDb24gZXN0YSB0YWJsYSBwb2RlbW9zIG9ic2VydmFyIGRpZmVyZW50ZXMgaW5zaWdodHM7IHByaW1lcmFtZW50ZSwgcG9kZW1vcyB2ZXIgcXVlICoqZWwgNTguOSUgKDEzOSBleC1jb2xhYm9yYWRvcmFzKSBkZSBsYXMgYmFqYXMgdG90YWxlcyByZXBvcnRhZGFzIHNvbiBkZSBtdWplcmVzKiogeSBkZSBlc3RhcywgZWwgNjQuNzUlIHNlIGhhbiBkYWRvIGRlICoqYmFqYSBwb3IgZmFsdGFzLioqDQoNCkFzaW1pc21vIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlICoqZWwgdG90YWwgZGUgbG9zIGNhc2Fkb3MsIGRpdm9yY2lhZG9zIHkgZW4gdW5pw7NuIGxpYnJlIHNvbiBtdWNobyBtw6FzIHByb3BlbnNvcyBoYSBzZXIgZGVzcGVkaWRvcyBxdWUgaGEgcmVudW5jaWFyIHZvbHVudGFyaWFtZW50ZSoqIChsb3MgcXVlIHRpZW5lbiBvIGhhbiB0ZW5pZG8gcGFyZWphKSB5IHBvciBlbCBvdHJvIGxhZG8sIGV4aXN0ZSBsYSBtaXNtYSBjYW50aWRhZCBkZSBzb2x0ZXJvcyBxdWUgaGFuIHJlbnVuY2lhZG8geSBoYW4gc2lkbyBkYWRvcyBkZSBiYWphLiAgDQoNCiMjIyAqNC4gUHJpbmNpcGFsZXMgRXN0YWTDrXN0aWNvcyBEZXNjcmlwdGl2b3M6Kg0KIyMjIyAqKk1lZGlhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFfZWRhZDEgPC0gbWVhbihiYWphcyRlZGFkKQ0KbWVkaWFfZWRhZDENCg0KbWVkaWFfbm9fZGlhcyA8LSBtZWFuKGJhamFzJG5vX2RpYXMpDQptZWRpYV9ub19kaWFzDQoNCm1lZGlhX3NhbGFyaW8xIDwtIG1lYW4oYmFqYXMkc2FsYXJpb19kaWFyaW8pDQptZWRpYV9zYWxhcmlvMQ0KYGBgDQoNCiMjIyMgKipNZWRpYW5hOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFuYV9lZGFkMSA8LSBtZWRpYW4oYmFqYXMkZWRhZCkNCm1lZGlhbmFfZWRhZDENCg0KbWVkaWFuYV9ub19kaWFzIDwtIG1lZGlhbihiYWphcyRub19kaWFzKQ0KbWVkaWFuYV9ub19kaWFzDQoNCm1lZGlhbmFfc2FsYXJpbzEgPC0gbWVkaWFuKGJhamFzJHNhbGFyaW9fZGlhcmlvKQ0KbWVkaWFuYV9zYWxhcmlvMQ0KYGBgDQoNCiMjIyMgKipNb2RhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbW9kZSA8LSBmdW5jdGlvbih4KSB7DQogIHV4IDwtIHVuaXF1ZSh4KQ0KICB1eFt3aGljaC5tYXgodGFidWxhdGUobWF0Y2goeCwgdXgpKSldDQp9DQptb2RhX2VkYWQxIDwtIG1vZGUoYmFqYXMkZWRhZCkNCm1vZGFfZWRhZDENCg0KbW9kYV9ub19kaWFzIDwtIG1vZGUoYmFqYXMkbm9fZGlhcykNCm1vZGFfbm9fZGlhcw0KDQptb2RhX3NhbGFyaW8xIDwtIG1vZGUoYmFqYXMkc2FsYXJpb19kaWFyaW8pDQptb2RhX3NhbGFyaW8xIA0KYGBgDQoNCiMjIyMgKipEZXN2aWFjacOzbiBFc3TDoW5kYXI6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp2YXJpYW56YV9lZGFkMSA8LXZhcihiYWphcyRlZGFkKQ0KdmFyaWFuemFfZWRhZDENCg0KdmFyaWFuemFfbm9fZGlhcyA8LXZhcihiYWphcyRub19kaWFzKQ0KdmFyaWFuemFfbm9fZGlhcw0KDQp2YXJpYW56YV9zYWxhcmlvMSA8LXZhcihiYWphcyRzYWxhcmlvX2RpYXJpbykNCnZhcmlhbnphX3NhbGFyaW8xDQoNCmRlc3ZpYWNpb25fZWRhZDEgPC0gc3FydCh2YXJpYW56YV9lZGFkMSkNCmRlc3ZpYWNpb25fZWRhZDENCg0KZGVzdmlhY2lvbl9ub19kaWFzIDwtIHNxcnQodmFyaWFuemFfbm9fZGlhcykNCmRlc3ZpYWNpb25fbm9fZGlhcw0KDQpkZXN2aWFjaW9uX3NhbGFyaW8xIDwtIHNxcnQodmFyaWFuemFfc2FsYXJpbzEpDQpkZXN2aWFjaW9uX3NhbGFyaW8xDQpgYGANCg0KIyMjIyAqKlRhYmxhIENvbXBhcmF0aXZhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVmFyaWFibGUxIDwtYygiRWRhZCIsIkFudGlndWVkYWQiLCJTYWxhcmlvIERpYXJpbyIpDQpQcm9tZWRpbzEgPC1jKCIzMS4wNSIsIjAuNiIsIjE3Ny45ODUiKQ0KTW9kYTEgPC1jKCIyMiIsIjAiLCIxODAuNjgiKQ0KTWVkaWFuYTEgPC1jKCIyOSIsIjAiLCIxODAuNjgiKQ0KRGVzdmlhY2nDs25fRXN0w6FuZGFyMSA8LWMgKCI5LjUyNSIsIjAuODE3IiwiMjMuMDY1IikNCnRhYmxhMSA8LWRhdGEuZnJhbWUgKFZhcmlhYmxlMSxQcm9tZWRpbzEsIE1vZGExLCBNZWRpYW5hMSwgRGVzdmlhY2nDs25fRXN0w6FuZGFyMSkNCnRhYmxhMQ0Ka25pdHI6OmthYmxlKHRhYmxhMSkNCmBgYA0KDQpEZSBlc3RvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvcyBwb2RlbW9zIGRlc3RhY2FyIHByaW5jaXBhbG1lbnRlIGxvcyBzaWd1aWVudGVzIGhhbGxhemdvczoNCg0KKioxLioqIExhIG1heW9yIHBhcnRlIGRlIGxvcyBleC1jb2xhYm9yYWRvcmVzIGRlIEZvcm0gdGVuaWFuIGVudHJlIDI5LTMxIGHDsW9zICgqKm3DoXMgasOzdmVuZXMgcXVlIGVsIHByb21lZGlvIGRlIGxvcyBhY3R1YWxlcyoqKS4gDQoNCioqMi4qKiBMYSAqKm1heW9yw61hIGRlIGxvcyBleC1jb2xhYm9yYWRvcmVzIGdhbmFiYW4gJDE4MC42OCBwZXNvcyBkaWFyaW9zIChtZWRpYW5hKSwgYWwgaWd1YWwgcXVlIGxvcyBhY3R1YWxlcyBjb2xhYm9yYWRvcmVzLioqDQoNCioqMy4qKiBMYXMgY29sYWJvcmFkb3JlcyBxdWUgc2UgZGllcm9uIGRlIGJhamEgdGVuw61hbiB1biAqKm3DrW5pbW8gcGVyaW9kbyBkZSB0aWVtcG8gbGFib3JhbmRvIGVuIGxhIGVtcHJlc2EuKioNCg0KIyMgKipjKSBEZWxpdmVyeSBQbGFuKioNCiMjIyAqMS4gTGltcGllemEsIE9yZ2FuaXphY2nDs24geSBUcmFuc2Zvcm1hY2nDs246Kg0KDQojIyMjIyAqKk5vdGFzOioqIEFudGVzIGRlIGltcG9ydGFyIGxhIGJhc2UgZGUgZGF0b3Mgc2Ugb3B0w7MgcG9yOg0KIyMjIyMgQWN1bXVsYXIgbGFzIMOzcmRlbmVzIGRlIGVudHJlZ2EgZW4gbWVzZXMgZW4gdmV6IGRlIGTDrWFzLg0KDQojIyMjICoqVMOpY25pY2EgMS4gSW1wb3J0YXIgbGFzIGJhc2VzIGRlIGRhdG9zIHkgbGltcGlhciBsb3Mgbm9tYnJlcyBkZSBsYXMgY29sdW1uYXMgY29uIGxhIGZ1bmNpw7NuICJjbGVhbl9uYW1lcyI6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGZpbGUuY2hvb3NlKCkNCmJkMiA8LSByZWFkLmNzdigiQzpcXFVzZXJzXFxtYXJpYVxcRG9jdW1lbnRzXFxJVEVTTSBMQUVUXFxTZW1lc3RyZSA3XFxSZXRvXFxEZWxpdmVyeSBQbGFuIEZJTkFMIC0gRVFVSVBPIDQgLmNzdiIpDQpkZWxpdmVyeV9wbGFuIDwtIGNsZWFuX25hbWVzKGJkMikNCnN1bW1hcnkoZGVsaXZlcnlfcGxhbikNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMi4gUmVlbm9tYnJhciBjYWRhIGNvbHVtbmEgKGFsZmFiw6l0aWNhbWVudGUpIHBhcmEgcXVlIGxvcyBtZXNlcyBzYWxnYW4gb3JkZW5hZG9zIGRlbCBtw6FzIGFudGlndW8gYWwgbWFzIHJlY2llbnRlIHkgbm8gcG9yIGFiZWNlZGFyaW86KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZWxpdmVyeV9wbGFuIDwtIGRlbGl2ZXJ5X3BsYW4gJT4lIGRwbHlyOjpyZW5hbWUoY2xpZW50ZT1jbGllbnRlX3BsYW50YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBX2p1bl8yMT1qdW5pbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCX2p1bF8yMT1qdWxpbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDX2Fnb18yMT1hZ29zdG8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRF9zZXBfMjE9c2VwdGllbWJyZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFX29jdF8yMT1vY3R1YnJlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZfbm92XzIxPW5vdmllbWJyZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHX2RpY18yMT1kaWNpZW1icmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSF9lbmVfMjI9ZW5lXzIyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElfZmViXzIyPWZlYl8yMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBKX21hcl8yMj1tYXJfMjIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS19hYnJfMjI9YWJyXzIyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExfbWF5XzIyPW1heV8yMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNX2p1bl8yMj1qdW5fMjIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTl9qdWxfMjI9anVsXzIyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9fYWdvXzIyPWFnb18yMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQX3NlcF8yMj1zZXBfMjIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUV9vY3RfMjI9b2N0dWJyZV8yMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSX25vdl8yMj1ub3ZfMjIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU19kaWNfMjI9ZGljXzIyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRfZW5lXzIzPWVuZV8yMywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVX2ZlYl8yMz1mZWJfMjMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVl9tYXJfMjM9bWFyXzIzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQopDQpjb2xuYW1lcyhkZWxpdmVyeV9wbGFuKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSAzLiBEaXNtaW51aXIgZWwgbsO6bWVybyBkZSBjb2x1bW5hcywgYWwgaGFjZXIgZG9zIG51ZXZhczogIk1lcyIgeSAiVW5pZGFkZXMiIHkgdW5pciBsYXMgY29sdW1uYXMgcmVub21icmFkYXMgYW50ZXJpb3JtZW50ZSBoYWNpZW5kb2xhcyBmaWxhczoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRlbGl2ZXJ5X3BsYW4gPC0gcGl2b3RfbG9uZ2VyKGRlbGl2ZXJ5X3BsYW4sIGNvbHM9NToxNCwgbmFtZXNfdG8gPSAibWVzIiwgdmFsdWVzX3RvID0gInVuaWRhZGVzIikNCnN0cihkZWxpdmVyeV9wbGFuKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSA0LiBFbGltaW5hY2nDs24gZGUgY2Vyb3MgZW4gbGEgYmFzZSBkZSBkYXRvcyBwYXJhIGVsaW1pbmFyIHJlbmdvbGVzL2NsaWVudGVzIHNpbiByZWdpc3Ryb3M6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZWxpdmVyeV9wbGFuIDwtIGZpbHRlcihkZWxpdmVyeV9wbGFuLCB1bmlkYWRlcz4wKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSA1LiBFbGltaW5hY2nDs24gZGUgdmFyaWFibGVzIGlycmVsZXZhbnRlcy4gRW4gZXN0ZSBjYXNvLCBkYWRvIHF1ZSBwcmV2aWFtZW50ZSBzZSB1bmllcm9uIChtZXJnZSkgbGFzIHZhcmlhYmxlcyBkZSBtZXMsIHNvbG8gc2UgY29uc2lkZXJhbiBuZWNlc2FyaWFzIGxhIHZhcmlhYmxlcyBkZSAiQ2xpZW50ZSIsICJGZWNoYSIgeSAiVW5pZGFkZXMiOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGVsaXZlcnlfcGxhbiA8LSBzdWJzZXQgKGRlbGl2ZXJ5X3BsYW4sIHNlbGVjdCA9IC1jIChwcm95ZWN0bywgaWRfb2RvbywgaXRlbSwgS19hYnJfMjIsIExfbWF5XzIyLCBNX2p1bl8yMixOX2p1bF8yMixPX2Fnb18yMiwgUF9zZXBfMjIsIFFfb2N0XzIyLCBSX25vdl8yMiwgIFNfZGljXzIyLCBUX2VuZV8yMywgVl9tYXJfMjMsIFVfZmViXzIzLCB0b3RhbF9tZXNlcykpDQpgYGANCg0KIyMjIyAqKlTDqWNuaWNhIDYuIENvbnZlcnRpciBsYXMgdmFyaWFibGVzIGEgZmFjdG9yZXMgbyBuw7ptZXJvcyBlbnRlcm9zOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGVsaXZlcnlfcGxhbiRjbGllbnRlPC1hcy5mYWN0b3IoZGVsaXZlcnlfcGxhbiRjbGllbnRlKQ0KZGVsaXZlcnlfcGxhbiRtZXM8LWFzLmZhY3RvcihkZWxpdmVyeV9wbGFuJG1lcykNCmRlbGl2ZXJ5X3BsYW4kdW5pZGFkZXM8LWFzLm51bWVyaWMoZGVsaXZlcnlfcGxhbiR1bmlkYWRlcykNCnN1bW1hcnkoZGVsaXZlcnlfcGxhbikNCmBgYA0KDQojIyMjICoqRXhwb3J0YXIgYmFzZSBkZSBkYXRvcyBsaW1waWE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp3cml0ZS5jc3YoZGVsaXZlcnlfcGxhbiwgZmlsZT0iZGVsaXZlcnlfcGxhbl9saW1waWExLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMjIyAqMi4gQ2xhc2lmaWNhY2nDs24gZGUgdmFyaWFibGVzOioNCiMjIyMgKirCv0N1w6FudGFzIHZhcmlhYmxlcyB5IGN1w6FudG9zIHJlZ2lzdHJvcyB0aWVuZSBsYSBiYXNlIGRlIGRhdG9zPyoqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGVzY3JpYmVEYXRhKGRlbGl2ZXJ5X3BsYW4saGVhZD0xLHRhaWw9MSkNCmBgYA0KDQpMYSBiYXNlIGRlIGRhdG9zIGRlICJEZWxpdmVyeSBQbGFuIiBjdWVudGEgY29uICoqMyB2YXJpYWJsZXMgeSA1OTAgcmVnaXN0cm9zIGVuIGNhZGEgY29sdW1uYSwgc2llbmRvIDEsNzcwIHJlZ2lzdHJvcyB0b3RhbGVzLioqDQoNCiMjIyMgKipUYWJsYSBkZSBjbGFzaWZpY2FjacOzbjoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NClZhcmlhYmxlX2RlbGl2ZXJ5X3BsYW48LWMoIkNsaWVudGUiLCAiTWVzIiwgIlVuaWRhZGVzIikNClRpcG9fcGxhbjwtYygiQ3VhbGl0YXRpdmEiLCAiQ3VhbGl0YXRpdmEiLCAiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIikNCk1lZGlkYV9wbGFuIDwtYygiTkEiLCAiTkEiLCAiVW5pZGFkZXMiKQ0KdGFibGEzPC1kYXRhLmZyYW1lKFZhcmlhYmxlX2RlbGl2ZXJ5X3BsYW4sVGlwb19wbGFuLCBNZWRpZGFfcGxhbikNCnRhYmxhMw0Ka25pdHI6OmthYmxlKHRhYmxhMykNCmBgYA0KDQojIyMgKjMuIEdyw6FmaWNvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvczoqDQojIyMjICoqQm94cGxvdCAoZGlzcGVyc2nDs24gY3VhbGl0YXRpdm8pICh1bmlkYWRlcyB5IG1lcyk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpib3hwbG90KHVuaWRhZGVzIH4gbWVzLCBkYXRhID0gZGVsaXZlcnlfcGxhbiwNCiAgICAgICAgbGFzID0gMywNCiAgICAgICAgbWFpbiA9ICJVbmlkYWRlcyBlbnRyZWdhZGFzIGVuIGNhZGEgbWVzIiwNCiAgICAgICAgY29sID0gcmFpbmJvdyhuY29sKHRyZWVzKSkpDQpgYGANCg0KRW4gZXN0YSBncsOhZmljYSBwb2RlbW9zIG9ic2VydmFyIHF1ZSBlbiAqKmVsIG1lcyBjb24gbWF5b3IgY2FudGlkYWQgZGUgdW5pZGFkZXMgZW50cmVnYWRhcyB5IG1lbm9zIGRpc3BlcnNpw7NuIGVudHJlIGxvcyBkYXRvcyBmdWUgc2VwdGllbWJyZSBkZWwgMjAyMS4qKiBTaW4gZW1iYXJnbywgKmp1bGlvIGRlbCAyMDIxIHR1dm8gbGEgbWF5b3IgZW50cmVnYSBzaWVuZG8gZXN0YSBtYXlvciBhIDEwLDAwMCB1bmlkYWRlcyogeSAqbWFyem8gZGVsIDIwMjIgaGEgdGVuaWRvIHZhcmlhcyBlbnRyZWdhcyBkaXNwZXJzYXMgbWF5b3JlcyBhIHN1IHByb21lZGlvIGRlIGVudHJlZ2EuKg0KDQojIyMjICoqR3LDoWZpY2EgZGUgYmFycmFzIChtZXMsIHVuZGlkYWRlcyk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoZGVsaXZlcnlfcGxhbiwgYWVzKG1lcyx1bmlkYWRlcykpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJBY2NlbnQiKSArIGdndGl0bGUoIlVuaWRhZGVzIHZlbmRpZGFzIHBvciBNZXMiKQ0KYGBgDQoNCioqRW4ganVsaW8gZGUgMjAyMSB5IG1hcnpvIGRlIDIwMjIgaHVibyB1bmEgbWF5b3IgY2FudGlkYWQgZGUgcGllemFzIGVudHJlZ2FkYXM7KiogZXN0byBzZSBwdWVkZSBkZWJlciBhIGxhcyBwb2NhcyBlbnRyZWdhcyBkaXNwZXJzYXMgYW5hbGl6YWRhcyBlbiBsYSBncsOhZmljYSBhbnRlcmlvciBxdWUgZXJhbiBkZSBtdWNoYXMgbcOhcyB1bmlkYWRlcyBxdWUgZWwgcHJvbWVkaW8uIFNpbiBlbWJhcmdvLCAqbm8gc2Ugb2JzZXJ2YSBuaW5ndW5hIHRlbmRlbmNpYSBmdWVydGUgZGUgdW5pZGFkZXMgZW50cmVnYWRhcyBwb3IgbG9zIG1lc2VzLioNCg0KIyMjIyAqKkdyw6FmaWNhIGRlIHBheSBjdWFsaXRhdGl2YSAoY2xpZW50ZSk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjb3VudChkZWxpdmVyeV9wbGFuJGNsaWVudGUpDQpwcm9wb3JjaW9uZXNfcGxhbiA8LSBjKDU3LCAzNiwgNzIsIDI4MCwgMjgsIDU0LCAxNCwgNDkpDQpldGlxdWV0YXNfcGxhbiA8LSBjKCJERU5TTyIsICJIRUxMQSIsICJUUk1YIiwgIlZBUlJPQyIsICJZRiBSQU1PUyIsICJZRkNGIiwgIllBRkVORyBzbSIsICJPdHJvcyIpDQpwY3RfcGxhbiA8LSByb3VuZChwcm9wb3JjaW9uZXNfcGxhbi9zdW0ocHJvcG9yY2lvbmVzX3BsYW4pKjEwMCkNCmV0aXF1ZXRhc19wbGFuIDwtIHBhc3RlKGV0aXF1ZXRhc19wbGFuLCBwY3RfcGxhbikNCmV0aXF1ZXRhc19wbGFuIDwtIHBhc3RlKGV0aXF1ZXRhc19wbGFuLCIlIixzZXA9IiIpDQpwaWUocHJvcG9yY2lvbmVzX3BsYW4sbGFiZWxzID0gZXRpcXVldGFzX3BsYW4sDQogICAgY29sPXJhaW5ib3cobGVuZ3RoKGV0aXF1ZXRhc19wbGFuKSksDQogICAgbWFpbj0iQ2xpZW50ZXMgY29uIHBlZGlkb3MgcGFyYSBlbnRyZWdhIikNCmBgYA0KDQpMb3MgY2xpZW50ZXMgYSBsb3MgcXVlIG3DoXMgdW5pZGFkZXMgc2UgbGVzIHJlZ2lzdHLDsyBwYXJhIGVudHJlZ2Egc29uICoqVkFSUk9DICg0NyUpLCBUUk1YICgxMiUpLCB5IERFTlNPICgxMCUpLioqDQoNCiMjIyAqNC4gUHJpbmNpcGFsZXMgRXN0YWTDrXN0aWNvcyBEZXNjcmlwdGl2b3M6Kg0KIyMjIyAqKk1lZGlhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFfcGxhbiA8LSBtZWFuKGRlbGl2ZXJ5X3BsYW4kdW5pZGFkZXMpDQptZWRpYV9wbGFuDQpgYGANCg0KIyMjIyAqKk1lZGlhbmE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptZWRpYW5fcGxhbiA8LSBtZWRpYW4oZGVsaXZlcnlfcGxhbiR1bmlkYWRlcykNCm1lZGlhbl9wbGFuDQpgYGANCg0KIyMjIyAqKk1vZGE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptb2RlIDwtIGZ1bmN0aW9uKHgpIHsNCiAgdXggPC0gdW5pcXVlKHgpDQogIHV4W3doaWNoLm1heCh0YWJ1bGF0ZShtYXRjaCh4LCB1eCkpKV0NCn0NCm1vZGVfcGxhbiA8LSBtb2RlKGRlbGl2ZXJ5X3BsYW4kdW5pZGFkZXMpICANCm1vZGVfcGxhbg0KYGBgDQoNCiMjIyMgKipEZXN2aWFjacOzbiBFc3TDoW5kYXI6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp2YXJpYW56YV91bmlkYWRlc19wcm9nIDwtIHZhcihkZWxpdmVyeV9wbGFuJHVuaWRhZGVzKQ0KdmFyaWFuemFfdW5pZGFkZXNfcHJvZw0KDQpkZXN2aWFjaW9uX3VuaWRhZGVzX3Byb2c8LSBzcXJ0KHZhcmlhbnphX3VuaWRhZGVzX3Byb2cpDQpkZXN2aWFjaW9uX3VuaWRhZGVzX3Byb2cNCg0KYGBgDQoNCiMjIyMgKipUYWJsYSBjb21wYXJhdGl2YToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NClZhcmlhYmxlX3BsYW48LWMoIlVuaWRhZGVzIiwgIk1lcyIsICJjbGllbnRlIikNClByb21lZGlvX3BsYW48LWMoIjM1OCIsICJOQSIsICJOQSIpDQpNZWRpYW5hX3BsYW48LWMoIjgwIiwgIk5BIiwgIk5BIikNCk1vZGFfcGxhbjwtYygiNjAiLCAiTkEiLCAiTkEiKQ0KRGVzdmlhY2lvbl9Fc3RhbmRhcl9wbGFuPC1jKCIxMDAyLjgzNCIsICJOQSIsICJOQSIpDQp0YWJsYV9wbGFuIDwtIGRhdGEuZnJhbWUgKFZhcmlhYmxlX3BsYW4sIFByb21lZGlvX3BsYW4sIE1lZGlhbmFfcGxhbiwgTW9kYV9wbGFuLCBEZXN2aWFjaW9uX0VzdGFuZGFyX3BsYW4pDQprbml0cjo6a2FibGUodGFibGFfcGxhbikNCmBgYA0KDQpEZSBlc3RvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvcyBwb2RlbW9zIGRlc3RhY2FyIHByaW5jaXBhbG1lbnRlIGxvcyBzaWd1aWVudGVzIGhhbGxhemdvczoNCg0KMS4gQWwgaGFiZXIgdGFudGEgZGlzcGVyc2nDs24gZW4gY2FudGlkYWQgZGUgdW5pZGFkZXMgcG9yIHBlZGlkbywgKiplbCBwcm9tZWRpbyBkZSB1bmlkYWRlcyBlbnRyZWdhZGFzIGVzIGRlIDM1OCoqLCBzaW4gZW1iYXJnbywgKipub3JtYWxtZW50ZSBsb3MgcGVkaWRvcyBzb24gZGUgZW50cmUgNjAtODAgdW5pZGFkZXMqKiBpbmRlcGVuZGllbnRlbWVudGUgZGVsIGNsaWVudGUgbyBtZXMuDQoNCiMjICoqZCkgRGVsaXZlcnkgUGVyZm9ybWFuY2UqKiANCiMjIyAqMS4gTGltcGllemEsIE9yZ2FuaXphY2nDs24geSBUcmFuc2Zvcm1hY2nDs246Kg0KIyMjIyMgKipOb3RhczoqKiBBbnRlcyBkZSBpbXBvcnRhciBsYSBiYXNlIGRlIGRhdG9zIHNlIG9wdMOzIHBvcjoNCiMjIyMjIDEuIFBlZ2FyIHZlcnRpY2FsbWVudGUgbGFzIGZlY2hhcyB5IGNvbW8gY29sdW1uYXMgbG9zIGNsaWVudGVzLg0KIyMjIyMgMi4gU29sbyB0b21hciBlbiBjdWVudGEgbGEgY29sdW1uYSBkZSAiZGlmZmVyZW5jZSIgcGFyYSBzYWJlciBjdWFsIGVyYSBsYSBkaWZlcmVuY2lhIGVudHJlIGVsIHRpZW1wbyBwbGFuZWFkbyB5IGVsIHRpZW1wbyByZWFsIGRlIGVudHJlZ2EuDQojIyMjIyAzLiBFbiBlbCBjbGllbnRlICJNYWhsZSIgc2Ugc3VtYXJvbiBsYXMgZGlmZXJlbmNpYXMgZGVsIG1pc21vIGTDrWEgcGFyYSBzb2xvIG9idGVuZXIgdW4gZGF0by4NCg0KIyMjIyAqKlTDqWNuaWNhIDEuIEltcG9ydGFyIGxhIGJhc2UgZGUgZGF0b3MgeSBsaW1waWFyIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcyBjb24gbGEgZnVuY2nDs24gImNsZWFuX25hbWVzIjoqKg0KYGBge3Igd2FybmluZz1GQUxTRX0NCiMgZmlsZS5jaG9vc2UoKQ0KYmQzIDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXG1hcmlhXFxEb2N1bWVudHNcXElURVNNIExBRVRcXFNlbWVzdHJlIDdcXFJldG9cXEZPUk0gQkFTRVMgREUgREFUT1NcXEZPUk0gLSBEZWxpdmVyeSBQZXJmb3JtYW5jZSBCRCBCVUVOQS5jc3YiKQ0KcGVyZm9ybWFuY2UgPC0gY2xlYW5fbmFtZXMoYmQzKQ0Kc3VtbWFyeShwZXJmb3JtYW5jZSkNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMi4gQm9ycmFyIGNvbHVtbmFzIGlubmVjZXNhcmlhcyAoZXN0w6FuIGVuIGJsYW5jbyk6KioNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpwZXJmb3JtYW5jZSA8LSBzdWJzZXQocGVyZm9ybWFuY2UsIHNlbGVjdCA9IC1jKHgsIHhfMSwgeF8yLCB4XzMpKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSAzOiBDb252ZXJ0aXIgZGUgY2FyYWN0ZXIgYSBmZWNoYSB5IGEgIGVudGVybyBuw7ptZXJpY286KioNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpwZXJmb3JtYW5jZSRmZWNoYSA8LSBhcy5EYXRlKHBlcmZvcm1hbmNlJGZlY2hhLCBmb3JtYXQgPSAiJWQvJW0vJVkiKQ0KcGVyZm9ybWFuY2UkcHJpbnRlbCA8LSBhcy5udW1lcmljKHBlcmZvcm1hbmNlJHByaW50ZWwpDQpwZXJmb3JtYW5jZSRtYWhsZSA8LSBhcy5udW1lcmljKHBlcmZvcm1hbmNlJG1haGxlKQ0KcGVyZm9ybWFuY2UkbWFnbmEgPC0gYXMubnVtZXJpYyhwZXJmb3JtYW5jZSRtYWduYSkNCnBlcmZvcm1hbmNlJHZhcnJvYyA8LSBhcy5udW1lcmljKHBlcmZvcm1hbmNlJHZhcnJvYykNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgNDogUmVlbXBsYXphciBOQXMgY29uIGxhIG1lZGlhbmEgZGUgY2FkYSB2YXJpYWJsZS4qKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNhcHBseShwZXJmb3JtYW5jZSwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkNCg0KcGVyZm9ybWFuY2UkZmVjaGFbaXMubmEocGVyZm9ybWFuY2UkZmVjaGEpXTwtbWVkaWFuKHBlcmZvcm1hbmNlJGZlY2hhLCBuYS5ybSA9IFRSVUUpDQpwZXJmb3JtYW5jZSRwcmludGVsW2lzLm5hKHBlcmZvcm1hbmNlJHByaW50ZWwpXTwtbWVkaWFuKHBlcmZvcm1hbmNlJHByaW50ZWwsIG5hLnJtID0gVFJVRSkNCnBlcmZvcm1hbmNlJG1haGxlW2lzLm5hKHBlcmZvcm1hbmNlJG1haGxlKV08LW1lZGlhbihwZXJmb3JtYW5jZSRtYWhsZSwgbmEucm0gPSBUUlVFKQ0KcGVyZm9ybWFuY2UkbWFnbmFbaXMubmEocGVyZm9ybWFuY2UkbWFnbmEpXTwtbWVkaWFuKHBlcmZvcm1hbmNlJG1hZ25hLCBuYS5ybSA9IFRSVUUpDQpwZXJmb3JtYW5jZSR2YXJyb2NbaXMubmEocGVyZm9ybWFuY2UkdmFycm9jKV08LW1lZGlhbihwZXJmb3JtYW5jZSR2YXJyb2MsIG5hLnJtID0gVFJVRSkNCnN1bW1hcnkocGVyZm9ybWFuY2UpDQpgYGANCg0KIyMjIyAqKkV4cG9ydGFyIGJhc2UgZGUgZGF0b3MgbGltcGlhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kd3JpdGUuY3N2KHBlcmZvcm1hbmNlLCBmaWxlPSJwZXJmb3JtYW5jZV9iZF9saW1waWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMjICoyLiBDbGFzaWZpY2FjacOzbiBkZSB2YXJpYWJsZXM6Kg0KIyMjIyAqKsK/Q3XDoW50YXMgdmFyaWFibGVzIHkgY3XDoW50b3MgcmVnaXN0cm9zIHRpZW5lIGxhIGJhc2UgZGUgZGF0b3M/KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZXNjcmliZURhdGEocGVyZm9ybWFuY2UsaGVhZD0xLHRhaWw9MSkNCmBgYA0KDQojTGEgYmFzZSBkZSBkYXRvcyBkZSAiUGVyZm9ybWFuY2UiIGN1ZW50YSBjb24gNSB2YXJpYWJsZXMgeSAzMjQgcmVnaXN0cm9zIGVuIGNhZGEgY29sdW1uYSwgc2llbmRvIDEsNjIwIHJlZ2lzdHJvcyB0b3RhbGVzLg0KDQojIyMjICoqVGFibGEgZGUgY2xhc2lmaWNhY2nDs246KioNClZhcmlhYmxlX3BlcmZvcm1hbmNlPC1jKCJGZWNoYSIsICJQcmludGVsIiwgIk1haGxlIiwgIk1hZ25hIiwgIlZhcnJvYyIpDQpUaXBvX3BlcmZvcm1hbmNlPC1jKCJDdWFsaXRhdGl2YSIsICJDdWFudGl0YXRpdmEgY29udGludWEiLCAiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIiwgIkN1YW50aXRhdGl2YSBjb250aW51YSIsICJDdWFudGl0YXRpdmEgY29udGludWEiKQ0KTWVkaWRhX3BlcmZvcm1hbmNlIDwtYygiTkEiLCAiTWludXRvcyIsICJNaW51dG9zIiwgIk1pbnV0b3MiLCAiTWludXRvcyIpDQp0YWJsYTI8LWRhdGEuZnJhbWUoVmFyaWFibGVfcGVyZm9ybWFuY2UsVGlwb19wZXJmb3JtYW5jZSwgTWVkaWRhX3BlcmZvcm1hbmNlKQ0KdGFibGEyDQprbml0cjo6a2FibGUodGFibGEyKQ0KDQojIyMgKjMuIEdyw6FmaWNvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvczoqDQojIyMjICoqVGltZSBTZXJpZXMgUGxvdCAoZmVjaGEgeSBjbGllbnRlKToNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QocGVyZm9ybWFuY2UsYWVzKHg9ZmVjaGEpKSsNCiAgZ2VvbV9saW5lKGFlcyh5PXByaW50ZWwpLGNvbG9yPSJibHVlIikrDQogIGdlb21fbGluZShhZXMoeT1tYWhsZSksY29sb3I9Im9yYW5nZSIpKw0KICBnZW9tX2xpbmUoYWVzKHk9bWFnbmEpLGNvbG9yPSJncmVlbiIpKw0KICBnZW9tX2xpbmUoYWVzKHk9dmFycm9jKSxjb2xvcj0iZ3JlZW4iKSsNCiAgbGFicyh4PSJGZWNoYSIseT0iUmV0cmFzbyBlbiBob3JhcyIsIGNvbG9yPSJMZWdlbmQiKSsNCiAgZ2d0aXRsZSgiUmV0cmFzb3MgZGUgZW50cmVnYSBwb3IgY2xpZW50ZSIpDQpgYGANCkVuIGVzdGEgZ3LDoWZpY2EgcG9kZW1vcyBvYnNlcnZhciBxdWUgRk9STSBjb24gbG9zICoqY2xpZW50ZXMgTWFnbmEgeSBWYXJyb2Mgbm8gcHJlc2VudGFuIGhvcmFzIGRlIHJldHJhc28qKiBlbiBzdXMgcGVkaWRvcyAobG8gY3VhbCBzZSBwdWVkZSBkZWJlciBhIHF1ZSBWYXJyb2MgcG9yIGVqZW1wbG8gZXMgc3UgcHJpbmNpcGFsIGNsaWVudGUgcHVlcyBlcyBlbCBxdWUgbcOhcyBwZWRpZG9zIGxlIGhhY2UpIHNpbiBlbWJhcmdvLCBjb24gKipNYWhsZSB5IFByaW50ZWwgc2kgaGFuIHByZXNlbnRhZG8gcmV0cmFzb3MgY29uc2lkZXJhYmxlcyBwcmluaWNpcGFsbWVudGUgZGVudHJvIGRlbCBwcmltZXIgdHJpbWVzdHJlIGRlbCAyMDIyLiBUZW5pZW5kbyByZXRyYXNvcyBkZSBoYXN0YSAyMCBob3Jhcy4gKioNCg0KIyMjICo0LiBQcmluY2lwYWxlcyBFc3RhZMOtc3RpY29zIERlc2NyaXB0aXZvczoqDQojIyMjICoqTWVkaWE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptZWRpYV9wcmludGVsIDwtIG1lYW4ocGVyZm9ybWFuY2UkcHJpbnRlbCkNCm1lZGlhX3ByaW50ZWwNCg0KbWVkaWFfbWFobGUgPC0gbWVhbihwZXJmb3JtYW5jZSRtYWhsZSkNCm1lZGlhX21haGxlDQoNCm1lZGlhX21hZ25hIDwtIG1lYW4ocGVyZm9ybWFuY2UkbWFnbmEpDQptZWRpYV9tYWduYQ0KDQptZWRpYV92YXJyb2MgPC0gbWVhbihwZXJmb3JtYW5jZSR2YXJyb2MpDQptZWRpYV92YXJyb2MNCmBgYA0KDQojIyMjICoqTWVkaWFuYToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1lZGlhbmFfcHJpbnRlbCA8LSBtZWRpYW4ocGVyZm9ybWFuY2UkcHJpbnRlbCkNCm1lZGlhbmFfcHJpbnRlbA0KDQptZWRpYW5hX21haGxlIDwtIG1lZGlhbihwZXJmb3JtYW5jZSRtYWhsZSkNCm1lZGlhbmFfbWFobGUNCg0KbWVkaWFuYV9tYWduYSA8LSBtZWRpYW4ocGVyZm9ybWFuY2UkbWFnbmEpDQptZWRpYW5hX21hZ25hDQoNCm1lZGlhbmFfdmFycm9jIDwtIG1lZGlhbihwZXJmb3JtYW5jZSR2YXJyb2MpDQptZWRpYW5hX3ZhcnJvYw0KYGBgDQoNCiMjIyMgKipNb2RhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbW9kZSA8LSBmdW5jdGlvbih4KSB7DQogIHV4IDwtIHVuaXF1ZSh4KQ0KICB1eFt3aGljaC5tYXgodGFidWxhdGUobWF0Y2goeCwgdXgpKSldDQp9DQptb2RhX3ByaW50ZWwgPC0gbW9kZShwZXJmb3JtYW5jZSRwcmludGVsKQ0KbW9kYV9wcmludGVsDQoNCm1vZGFfbWFobGUgPC0gbW9kZShwZXJmb3JtYW5jZSRtYWhsZSkNCm1vZGFfbWFobGUNCg0KbW9kYV9tYWduYSA8LSBtb2RlKHBlcmZvcm1hbmNlJG1hZ25hKQ0KbW9kYV9tYWduYQ0KDQptb2RhX3ZhcnJvYyA8LSBtb2RlKHBlcmZvcm1hbmNlJHZhcnJvYykNCm1vZGFfdmFycm9jDQpgYGANCg0KDQojIyMjICoqRGVzdmlhY2nDs24gRXN0w6FuZGFyOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdmFyaWFuemFfcHJpbnRlbCA8LXZhcihwZXJmb3JtYW5jZSRwcmludGVsKQ0KdmFyaWFuemFfcHJpbnRlbA0KDQp2YXJpYW56YV9tYWhsZSA8LXZhcihwZXJmb3JtYW5jZSRtYWhsZSkNCnZhcmlhbnphX21haGxlDQoNCnZhcmlhbnphX21hZ25hIDwtdmFyKHBlcmZvcm1hbmNlJG1hZ25hKQ0KdmFyaWFuemFfbWFnbmENCg0KdmFyaWFuemFfdmFycm9jIDwtdmFyKHBlcmZvcm1hbmNlJHZhcnJvYykNCnZhcmlhbnphX3ZhcnJvYw0KDQpkZXN2aWFjaW9uX3ByaW50ZWwgPC0gc3FydCh2YXJpYW56YV9wcmludGVsKQ0KZGVzdmlhY2lvbl9wcmludGVsDQoNCmRlc3ZpYWNpb25fbWFobGUgPC0gc3FydCh2YXJpYW56YV9tYWhsZSkNCmRlc3ZpYWNpb25fbWFobGUNCg0KZGVzdmlhY2lvbl9tYWduYSA8LSBzcXJ0KHZhcmlhbnphX21hZ25hKQ0KZGVzdmlhY2lvbl9tYWduYQ0KDQpkZXN2aWFjaW9uX3ZhcnJvYyA8LSBzcXJ0KHZhcmlhbnphX3ZhcnJvYykNCmRlc3ZpYWNpb25fdmFycm9jDQpgYGANCg0KIyMjIyAqKlRhYmxhIENvbXBhcmF0aXZhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVmFyaWFibGVfZGVsX3BlcmZvcm1hbmNlIDwtYygiUHJpbnRlbCIsIk1haGxlIiwgIk1hZ25hIiwgIlZhcnJvYyIpDQpQcm9tZWRpb19wZXJmb3JtYW5jZSA8LWMoIjAuNDA3NyIsICIyLjQxIiwiMCIsICIwIikNCk1vZGFfcGVyZm9ybWFuY2UgPC1jKCIwIiwiMyIsIjAiLCAiMCIpDQpNZWRpYW5hX3BlcmZvcm1hbmNlIDwtYygiMCIsIjMiLCIwIiwgIjAiKQ0KRGVzdmlhY2nDs25fRXN0X3BlcmZvcm1hbmNlIDwtYyAoIjAuNzUxIiwiMS45MDYiLCIwIiwgIjAiKQ0KdGFibGExIDwtZGF0YS5mcmFtZShWYXJpYWJsZV9kZWxfcGVyZm9ybWFuY2UsUHJvbWVkaW9fcGVyZm9ybWFuY2UsIE1vZGFfcGVyZm9ybWFuY2UsIE1lZGlhbmFfcGVyZm9ybWFuY2UsIERlc3ZpYWNpw7NuX0VzdF9wZXJmb3JtYW5jZSkNCnRhYmxhMQ0Ka25pdHI6OmthYmxlKHRhYmxhMSkNCmBgYA0KDQpEZSBlc3RvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvcyBwb2RlbW9zIGRlc3RhY2FyIHByaW5jaXBhbG1lbnRlIGxvcyBzaWd1aWVudGVzIGhhbGxhemdvczoNCg0KMS4gRk9STSBhY3R1YWxtZW50ZSAqKnNvbG8gdGllbmUgcmV0cmFzb3Mgc2lnbmlmaWNhdGl2b3MgY29uIHN1IGNsaWVudGUgTWFobGUqKiwgc2llbmRvIGVzdG9zIHJldHJhc29zIG5vcm1hbG1lbnRlIGRlICoqMyBtaW51dG9zIGFwcm94IChtZWRpYW5hIHkgbW9kYSkuKioNCg0KIyMgKiplKSBQcm9kdWNjacOzbioqDQojIyMgKjEuIExpbXBpZXphLCBPcmdhbml6YWNpw7NuIHkgVHJhbnNmb3JtYWNpw7NuOioNCiMjIyMjICoqTm90YXM6KiogQW50ZXMgZGUgaW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyBzZSBvcHTDsyBwb3I6DQojIyMjIyAxLiBJbnRlZ3JhciBsb3MgZGF0b3MgZGUgSnVsaW8sIEFnb3N0byB5IFNlcHRpZW1icmUuDQojIyMjIyAyLiBJbmNvcnBvcmFyIHVuYSBudWV2YSBjb2x1bW5hIGRlIOKAnEZlY2hh4oCdLg0KIyMjIyMgMy4gU29sbyBkZWphciBsb3MgZGF0b3MgZGUg4oCcVGllbXBvIGRlIE1hcXVpbmFz4oCdDQojIyMjIyA0LiBFbGltaW5hciBsb3MgTkFzIHkgdmFyaWFibGVzIG5vIG51bcOpcmljYXMgZGUgbGFzIGNvbHVtbmFzIGRlOiDigJxMw6FtaW5hcyBwcm9jZXNhZGFz4oCdLg0KDQojIyMjICoqVMOpY25pY2EgMS4gSW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyB5IGxpbXBpYXIgbG9zIG5vbWJyZXMgZGUgbGFzIGNvbHVtbmFzIGNvbiBsYSBmdW5jacOzbiAiY2xlYW5fbmFtZXMiOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBmaWxlLmNob29zZSgpDQpiZDQgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcbWFyaWFcXERvY3VtZW50c1xcSVRFU00gTEFFVFxcU2VtZXN0cmUgN1xcUmV0b1xcRk9STSBCQVNFUyBERSBEQVRPU1xcUFJPRFVDQ0lPTl9CRC5jc3YiKQ0KcHJvZHVjY2lvbiA8LSBjbGVhbl9uYW1lcyhiZDQpDQpzdW1tYXJ5KHByb2R1Y2Npb24pDQpgYGANCg0KIyMjIyAqKlTDqWNuaWNhIDIuIEVsaW1pbmFyIGNvbHVtbmFzIGlubmVjZXNhcmlhczoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnByb2R1Y2Npb24gPC0gc3Vic2V0KHByb2R1Y2Npb24sc2VsZWN0ID0gLWMgKGlkX2Zvcm0sIHByb2R1Y3RvLCBocl9maW4sIGluaWNpb19zZXBfdXAsIGZpbl9pbmljaW9fZGVfc2VwX3VwLCBpbmljaW9fZGVfcHJvY2VzbywgZmluX2RlX3Byb2Nlc28sIHRpZW1wb19tYXRlcmlhbGVzKSkNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMy5Cb3JyYXIgbG9zIGxldHJhcyBxdWUgY29udGllbmVuIGFsZ3Vub3MgbGV0cmFzIHJlZ2lzdHJvcyBkZSBsYSB2YXJpYWJsZSAiUGllemFfUHJvZyI6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwcm9kdWNjaW9uJHBpZXphc19wcm9nIDwtIHN0cl9yZXBsYWNlKHByb2R1Y2Npb24kcGllemFzX3Byb2csICJbYWVpb3VMQU09TmJCc1NdIiwgIiIpDQpwcm9kdWNjaW9uJHBpZXphc19wcm9nIDwtIGFzLmludGVnZXIocHJvZHVjY2lvbiRsYW1pbmFzX3Byb2Nlc2FkYXMpDQpgYGANCg0KIyMjIyAqKlTDqWNuaWNhIDQuQ29udmVydGlyIGRlIGNhcsOhY3RlciBhIGZvcm1hdG8gbsO6bWVyaWNvIHkgZGUgZmVjaGE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwcm9kdWNjaW9uJHBpZXphc19wcm9nPC1hcy5udW1lcmljKHByb2R1Y2Npb24kcGllemFzX3Byb2cpICAgICAgICAgICAgICAgICANCnByb2R1Y2Npb24kdG1vX21pbjwtYXMubnVtZXJpYyhwcm9kdWNjaW9uJHRtb19taW4pICAgICAgICAgICAgICAgICAgDQpwcm9kdWNjaW9uJGxhbWluYXNfcHJvY2VzYWRhczwtYXMubnVtZXJpYyhwcm9kdWNjaW9uJGxhbWluYXNfcHJvY2VzYWRhcykgICANCnByb2R1Y2Npb24kdGllbXBvX2NhbGlkYWQ8LWFzLm51bWVyaWMocHJvZHVjY2lvbiR0aWVtcG9fY2FsaWRhZCkgDQpwcm9kdWNjaW9uJGZlY2hhIDwtIGFzLkRhdGUocHJvZHVjY2lvbiRmZWNoYSwgZm9ybWF0ID0gIiVkLyVtLyVZIikNCnByb2R1Y2Npb24kY2xpZW50ZTwtYXMuZmFjdG9yKHByb2R1Y2Npb24kY2xpZW50ZSkNCnByb2R1Y2Npb24kZXN0YWNpb25fYXJyYW5xdWU8LWFzLmZhY3Rvcihwcm9kdWNjaW9uJGVzdGFjaW9uX2FycmFucXVlKSANCnN1bW1hcnkocHJvZHVjY2lvbikNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgNS5SZWVtcGxhemFyIHBvciBsYSBtZWRpYW5hIGxvcyBOQcK0cyBkZSB0bW9fbWluIHkgZGUgdGllbXBvX2NhbGlkYWQ6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwcm9kdWNjaW9uJHRtb19taW5baXMubmEocHJvZHVjY2lvbiR0bW9fbWluKV08LW1lZGlhbihwcm9kdWNjaW9uJHRtb19taW4sIG5hLnJtID0gVFJVRSkNCnByb2R1Y2Npb24kdGllbXBvX2NhbGlkYWRbaXMubmEocHJvZHVjY2lvbiR0aWVtcG9fY2FsaWRhZCldPC1tZWRpYW4ocHJvZHVjY2lvbiR0aWVtcG9fY2FsaWRhZCwgbmEucm0gPSBUUlVFKQ0Kc3VtbWFyeShwcm9kdWNjaW9uKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSA2LkVsaW1pbmFyIHJlZ2lzdHJvIGNvbiBOQSBlbiAiY2xpZW50ZSI6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwcm9kdWNjaW9uIDwtIG5hLm9taXQocHJvZHVjY2lvbikNCnN1bW1hcnkocHJvZHVjY2lvbikNCmBgYA0KDQojIyMjICoqRXhwb3J0YXIgYmFzZSBkZSBkYXRvcyBsaW1waWE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpiZF9Qcm9kdWNjaW9uX2xpbXBpYSA8LSBwcm9kdWNjaW9uDQp3cml0ZS5jc3YoYmRfUHJvZHVjY2lvbl9saW1waWEsIGZpbGU9ImJkX3Byb2R1Y2Npb25fbGltcGlhLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMjIyAqMi4gQ2xhc2lmaWNhY2nDs24gZGUgdmFyaWFibGVzOioNCiMjIyMgKirCv0N1w6FudGFzIHZhcmlhYmxlcyB5IGN1w6FudG9zIHJlZ2lzdHJvcyB0aWVuZSBsYSBiYXNlIGRlIGRhdG9zPyoqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGVzY3JpYmVEYXRhKHByb2R1Y2Npb24saGVhZD0xLHRhaWw9MSkNCmBgYA0KTGEgYmFzZSBkZSBkYXRvcyBkZSAqKiJQcm9kdWNjacOzbiIgY3VlbnRhIGNvbiA3IHZhcmlhYmxlcyB5IDMsOTg3IHJlZ2lzdHJvcyBlbiBjYWRhIGNvbHVtbmEsIHNpZW5kbyAyNyw5MDkgcmVnaXN0cm9zIHRvdGFsZXMuKioNCg0KIyMjIyAqKlRhYmxhIGRlIGNsYXNpZmljYWNpw7NuOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVmFyaWFibGVfcHJvZHVjY2lvbjwtYygiRmVjaGEiLCAiQ2xpZW50ZSIsICJQaWV6YXMgUHJvZ3JhbWFkYXMiLCAiVGllbXBvIE3DrW5pbW8iLCAiRXN0YWNpw7NuIEFycmFucXVlIiwgIkzDoW1pbmFzIFByb2Nlc2FkYXMiLCAiVGllbXBvIGRlIENhbGlkYWQiKQ0KVGlwb19wcm9kdWNjaW9uPC1jKCJDdWFsaXRhdGl2YSIsICJDdWFsaXRhdGl2YSIsICJDdWFudGl0YXRpdmEgY29udGludWEiLCAiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIiwgIkN1YWxpdGF0aXZhIiwgIkN1YW50aXRhdGl2YSBjb250aW51YSIsICJDdWFudGl0YXRpdmEgY29udGludWEiKQ0KTWVkaWRhX3Byb2R1Y2Npb24gPC1jKCJOQSIsICJOQSIsICJVbmlkYWRlcyIsICJNaW51dG9zIiwgIk5hIiwgIlVuaWRhZGVzIiwgIk1pbnV0b3MiKQ0KdGFibGE0PC1kYXRhLmZyYW1lKFZhcmlhYmxlX3Byb2R1Y2Npb24sVGlwb19wcm9kdWNjaW9uLCBNZWRpZGFfcHJvZHVjY2lvbikNCnRhYmxhNA0Ka25pdHI6OmthYmxlKHRhYmxhNCkNCmBgYA0KDQojIyMgMy4gKkdyw6FmaWNvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvczoqDQojIyMjICoqSGlzdG9ncmFtYSBjdWFudGl0YXRpdm8gKHBpZXphcyBwcm9kdWNpZGFzKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmhpc3QocHJvZHVjY2lvbiRwaWV6YXNfcHJvZywgbWFpbiA9ICJQaWV6YXMgcHJvZHVjaWRhcyIsIHlsYWIgPSAiRnJlY3VlbmNpYSIsIHhsYWIgPSAiUGllemFzIFByb2dyYW1hZGFzIiwgY29sID0gImxpZ2h0Ymx1ZSIpDQptZWFuIChwcm9kdWNjaW9uJHBpZXphc19wcm9nKQ0KYGBgDQoNCkVuIGVzdGUgZ3LDoWZpY2EgcG9kZW1vcyBvYnNlcnZhciBxdWUgZXMgKiptw6FzIGNvbcO6biBwcm9kdWNpciB0YW5kYXMgbWVub3JlcyBhIDMwMCBwaWV6YXMgcG9yIGNsaWVudGUqKiwgcGVybyBlbiAqcHJvbWVkaW8gc2UgcHJvZHVjZW4gMTAyLjA4IHBpZXphcyBwb3IgcGVkaWRvLioNCg0KIyMjIyAqKkdyw6FmaWNhIGRlIGRpc3BlcnNpw7NuICh0aWVtcG8gZGUgY2FsaWRhZCB5IGRlIHByb2R1Y2Npw7NuKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3QocHJvZHVjY2lvbiR0aWVtcG9fY2FsaWRhZCwgcHJvZHVjY2lvbiR0bW9fbWluLCBtYWluID0gIlRpZW1wbyBkZSBjYWxpZGFkIGludmVydGlkbyBwb3IgdGllbXBvIGRlIHByb2R1Y2Npw7NuIiwgeGxhYiA9ICJUaWVtcG8gZGUgQ2FsaWRhZCIsIHlsYWIgPSAiVGllbXBvIGRlIFByb2R1Y2Npw7NuIiwgY29sPSAiYmx1ZSIpDQpgYGANCkVuIGVzdGEgZ3LDoWZpY2EgcG9kZW1vcyBvYnNlcnZhciBxdWUgKiphIHVuIHRpZW1wbyBtZW5vciBkZSA1MGggZGUgcHJvZHVjY2nDs24sIGhheSB1bmEgbXVjaGEgbWF5b3IgaW52ZXJzacOzbiBkZSB0aWVtcG8gZGUgY2FsaWRhZC4qKiBZICoqYSB1biB0aWVtcG8gbWF5b3IgYSA2MCBob3JhcywgaGF5IHVuYSBtdWNoYSBtZW5vciBpbnZlcnNpw7NuIGRlIHRpZW1wbyBkZSBjYWxpZGFkLioqIExvIHF1ZSBzaWduaWZpY2EgcXVlICplbnRyZSBtw6FzIHNlIHRhcmRlbiBoYWNpZW5kbyB1biBwcm9kdWN0bywgbWVub3MgaG9yYXMgZGUgY2FsaWRhZCBsZSBpbnZpZXJ0ZW4sKiBlc3RvIHNlIHB1ZWRlIGRlYmVyIGEgcXVlIGNvbW8gZWwgcHJvY2VzbyBlcyBtw6FzIGxhcmdvLCBsZSBpbnZpZXJ0ZW4gbcOhcyBjdWlkYWRvIGVuIGNhZGEgcGFzbywgeSBjdWFuZG8gZXMgbcOhcyBjb3J0byBlbCBwcm9jZXNvIHB1ZWRlIHNlciBtYXMgcnV0aW5hcmlvIHkgZXMgbmVjZXNhcmlvIGludmVydGlybGUgaG9yYXMgZXh0cmEgYSBhc2VndXJhcnNlIHF1ZSBzZSBoYXlhIHByb2R1Y2lkbyBjb3JyZWN0YW1lbnRlLg0KDQojIyMjICoqVGltZSBTZXJpZXMgcGxvdCAoZmVjaGEgeSBwaWV6YXMgcHJvZHVjaWRhcyk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QocHJvZHVjY2lvbixhZXMoeD1mZWNoYSkpKw0KICBnZW9tX2xpbmUoYWVzKHk9cGllemFzX3Byb2cpLGNvbG9yPSJibHVlIikrDQogIGxhYnMoeD0iRmVjaGEiLHk9InBpZXphcyBwcm9ncmFtYWRhcyIsIGNvbG9yPSJibHVlIikrDQogIGdndGl0bGUoIkdyYWZpY2EgZGUgbGFzIHBpZXphcyBwcm9ncmFtYWRhcyBwb3IgZmVjaGEiKQ0KYGBgDQoNCkVuIGVzdGUgZ3LDoWZpY28gc2UgbXVlc3RyYSBsYSBwcm9kdWNjacOzbiBkZSBwaWV6YXMgcHJvZ3JhbWFkYXMgZW4gbG9zIDMgZGlmZXJlbnRlcyBtZXNlcyAoanVsaW8sIGFnb3N0bywgc2VwdGllbWJyZSksIGRvbmRlIHNlIHB1ZWRlIHZlciBxdWUgKipodWJvIG1heW9yIHByb2R1Y2Npw7NuIGVuIGxvIHF1ZSBmdWUgYWdvc3RvLioqDQoNCiMjIyMgKipHcsOhZmljbyBkZSBkaXNwZXJzacOzbiAocGllemFzIHkgbGFtaW5hZGFzKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmdncGxvdChkYXRhPXByb2R1Y2Npb24sIG1hcHBpbmcgPSBhZXMocGllemFzX3Byb2csIGxhbWluYXNfcHJvY2VzYWRhcykpICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSB0bW9fbWluKSkgKyB0aGVtZV9idygpDQpgYGANCg0KQXF1w60gcG9kZW1vcyBvYnNlcnZhciBxdWUgaGF5IHVuYSAqKnJlbGFjacOzbiBsaW5lYWwgcG9zaXRpdmEgZW50cmUgbGFzIHBpZXphcyBwcm9kdWNpZGFzIHkgbGEgY2FudGlkYWQgZGUgbGFtaW5hcyB1dGlsaXphZGFzIChtYXRlcmlhIHByaW1hKSByZWFsaXphZGFzIGVuIHVuIHRpZW1wbyBtZW5vciBhIDUwIGhvcmFzIGVuIHN1IG1heW9yw61hLioqDQoNCiMjIyMgKipHcsOhZmljYSBkZSBkaXNwZXJzacOzbiAoY2xpZW50ZSB5IHBpZXphcyBwcm9kdWNpZGFzKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmdncGxvdChwcm9kdWNjaW9uLCBhZXMoeD1waWV6YXNfcHJvZywgeT1jbGllbnRlKSkgKyANCiAgZ2VvbV9wb2ludCgpIA0KYGBgDQoNCkVuIGVzdGEgdGFibGEgcG9kZW1vcyBvYnNlcnZhciBsYXMgdmVjZXMgcXVlIGFwYXJlY2UgZWwgY2xpZW50ZSBlbiBsYSBiYXNlIGRlIGRhdG9zIGRlIHByb2R1Y2Npw7NuLCB2aWVuZG8gYXNpIHF1ZSAqKiJTdGFiaWxpdXMgMSIgZXMgZWwgY2xpZW50ZSBxdWUgbcOhcyBwZWRpZG9zIGxlIG9yZGVuYSBhIEZPUk0uKiogU2VndWlkbyBwb3IgVFJNWCwgWUFORkVORyB5IERFTlNPLg0KDQpTaW4gZW1iYXJnbywgZW4gbGEgZ3LDoWZpY2EgaGVjaGEgYSBjb250aW51YWNpw7NuIHNlIHB1ZWRlIG9ic2VydmFyIHF1ZSAqKllBTkZFTkcgZXMgZWwgY2xpZW50ZSBxdWUgaGEgaGVjaG8gcGVkaWRvcyBkZSBtw6FzIGNhbnRpZGFkIGRlIHBpZXphcy4qKg0KDQojIyMgKjQuIFByaW5jaXBhbGVzIEVzdGFkw61zdGljb3MgRGVzY3JpcHRpdm9zOioNCiMjIyMgKipNZWRpYToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1lZGlhX3BpZXphc19wcm9nPC0gbWVhbihwcm9kdWNjaW9uJHBpZXphc19wcm9nKQ0KbWVkaWFfcGllemFzX3Byb2cNCg0KbWVkaWFfdG1vX21pbjwtIG1lYW4ocHJvZHVjY2lvbiR0bW9fbWluKQ0KbWVkaWFfdG1vX21pbiANCg0KbWVkaWFfbGFtaW5hc19wcm9jZXNhZGFzIDwtIG1lYW4ocHJvZHVjY2lvbiRsYW1pbmFzX3Byb2Nlc2FkYXMpDQptZWRpYV9sYW1pbmFzX3Byb2Nlc2FkYXMNCg0KbWVkaWFfdGllbXBvX2NhbGlkYWQ8LSBtZWFuKHByb2R1Y2Npb24kdGllbXBvX2NhbGlkYWQpDQptZWRpYV90aWVtcG9fY2FsaWRhZA0KYGBgDQoNCiMjIyMgKipNZWRpYW5hOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFuX3BpZXphc19wcm9nPC0gbWVkaWFuKHByb2R1Y2Npb24kcGllemFzX3Byb2cpDQptZWRpYW5fcGllemFzX3Byb2cNCg0KbWVkaWFuX3Rtb19taW48LSBtZWRpYW4ocHJvZHVjY2lvbiR0bW9fbWluKQ0KbWVkaWFuX3Rtb19taW4gDQoNCm1lZGlhbl9sYW1pbmFzX3Byb2Nlc2FkYXMgPC0gbWVkaWFuKHByb2R1Y2Npb24kbGFtaW5hc19wcm9jZXNhZGFzKQ0KbWVkaWFuX2xhbWluYXNfcHJvY2VzYWRhcw0KDQptZWRpYW5fdGllbXBvX2NhbGlkYWQ8LSBtZWRpYW4ocHJvZHVjY2lvbiR0aWVtcG9fY2FsaWRhZCkNCm1lZGlhbl90aWVtcG9fY2FsaWRhZA0KYGBgDQoNCiMjIyMgKipNb2RhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbW9kZSA8LSBmdW5jdGlvbih4KSB7DQogIHV4IDwtIHVuaXF1ZSh4KQ0KICB1eFt3aGljaC5tYXgodGFidWxhdGUobWF0Y2goeCwgdXgpKSldDQp9DQptb2RhX3BpZXphc19wcm9nIDwtIG1vZGUocHJvZHVjY2lvbiRwaWV6YXNfcHJvZykNCm1vZGFfcGllemFzX3Byb2cNCg0KbW9kYV90bW9fbWluIDwtIG1vZGUocHJvZHVjY2lvbiR0bW9fbWluKQ0KbW9kYV90bW9fbWluDQoNCm1vZGFfbGFtaW5hc19wcm9jZXNhZGFzPC0gbW9kZShwcm9kdWNjaW9uJGxhbWluYXNfcHJvY2VzYWRhcykNCm1vZGFfbGFtaW5hc19wcm9jZXNhZGFzDQoNCm1vZGFfdGllbXBvX2NhbGlkYWQgPC0gbW9kZShwcm9kdWNjaW9uJHRpZW1wb19jYWxpZGFkKQ0KbW9kYV90aWVtcG9fY2FsaWRhZA0KDQpgYGANCg0KIyMjIyAqKkRlc3ZpYWNpw7NuIEVzdMOhbmRhcjoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnZhcmlhbnphX3BpZXphc19wcm9nIDwtdmFyKHByb2R1Y2Npb24kcGllemFzX3Byb2cpDQp2YXJpYW56YV9waWV6YXNfcHJvZw0KDQp2YXJpYW56YV90bW9fbWluIDwtdmFyKHByb2R1Y2Npb24kdG1vX21pbikNCnZhcmlhbnphX3Rtb19taW4NCg0KdmFyaWFuemFfbGFtaW5hc19wcm9jZXNhZGFzPC12YXIocHJvZHVjY2lvbiRsYW1pbmFzX3Byb2Nlc2FkYXMpDQp2YXJpYW56YV9sYW1pbmFzX3Byb2Nlc2FkYXMNCg0KdmFyaWFuemFfdGllbXBvX2NhbGlkYWQgPC12YXIocHJvZHVjY2lvbiR0aWVtcG9fY2FsaWRhZCkNCnZhcmlhbnphX3RpZW1wb19jYWxpZGFkDQoNCmRlc3ZpYWNpb25fcGllemFzX3Byb2c8LSBzcXJ0KHZhcmlhbnphX3BpZXphc19wcm9nKQ0KZGVzdmlhY2lvbl9waWV6YXNfcHJvZw0KDQpkZXN2aWFjaW9uX3Rtb19taW4gPC0gc3FydCh2YXJpYW56YV90bW9fbWluKQ0KZGVzdmlhY2lvbl90bW9fbWluDQoNCmRlc3ZpYWNpb25fbGFtaW5hc19wcm9jZXNhZGFzIDwtIHNxcnQodmFyaWFuemFfbGFtaW5hc19wcm9jZXNhZGFzKQ0KZGVzdmlhY2lvbl9sYW1pbmFzX3Byb2Nlc2FkYXMNCg0KZGVzdmlhY2lvbl90aWVtcG9fY2FsaWRhZCA8LSBzcXJ0KHZhcmlhbnphX3RpZW1wb19jYWxpZGFkKQ0KZGVzdmlhY2lvbl90aWVtcG9fY2FsaWRhZA0KYGBgDQoNCiMjIyMgKipUYWJsYSBkZSBmcmVjdWVuY2lhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVmFyaWFibGVfcHJvZCA8LWMoIlBpZXphcyBQcm9ncmFtYWRhcyIsIlRpZW1wbyBNw61uaW1vICIsICJMw6FtaW5hcyBQcm9jZXNhZGFzIiwgIlRpZW1wbyBDYWxpZGFkIikNClByb21lZGlvX3Byb2QgPC1jKCIxMDIuMDgiLCAiMjEuODkiLCIxMDIuMDgiLCAiMC44NzUiKQ0KTW9kYV9wcm9kIDwtYygiMCIsICIyMCIsIjAiLCAiMSIpDQpNZWRpYW5hX3Byb2QgPC1jKCI1MSIsIjIwIiwiNTEiLCAiMSIpDQpEZXN2aWFjacOzbl9Fc3TDoW5kYXJfcHJvZCA8LWMgKCIxMzQuNDExIiwiMTEuOTcxIiwiMTM0LjMxMSIsICIwLjk5MSIpDQp0YWJsYTUgPC1kYXRhLmZyYW1lKFZhcmlhYmxlX3Byb2QsUHJvbWVkaW9fcHJvZCwgTW9kYV9wcm9kLCBNZWRpYW5hX3Byb2QsIERlc3ZpYWNpw7NuX0VzdMOhbmRhcl9wcm9kKQ0KdGFibGE1DQprbml0cjo6a2FibGUodGFibGE1KQ0KDQpgYGANCg0KRGUgZXN0b3MgZXN0YWTDrXN0aWNvcyBkZXNjcmlwdGl2b3MgcG9kZW1vcyBkZXN0YWNhciBwcmluY2lwYWxtZW50ZSBsb3Mgc2lndWllbnRlcyBoYWxsYXpnb3M6DQoNCjEuICoqRk9STSB0aWVuZSBsYSBjYW50aWRhZCBkZSBwcmltYSBleGFjdGEgcGFyYSBoYWNlciBzdXMgcGllemFzIHByb2dyYW1hZGFzKiosIHB1ZXMgc3VzIGVzdGFkw61zdGljb3MgZGVzY3JpcHRpdm9zIHNvbiBpZ3VhbGVzLg0KDQoyLiBFbCAqKnRpZW1wbyBkZSBwcm9kdWNjacOzbiBhcHJveGltYWRvIChtZWRpYS1tZWRpYW5hLW1vZGEpIGVzIGRlIDIwIGhvcmFzIHBvciBwZWRpZG8uKioNCg0KMy4gRk9STSBsZSAqKmludmllcnRlIG3DrW5pbW8gdGllbXBvIGRlIGNhbGlkYWQgYSBzdSBwcm9kdWNjacOzbioqICgxIGhvcmEgbyBtZW5vcyBwb3IgcGVkaWRvKS4NCg0KIyMgKipmKSBNZXJtYSoqDQojIyMgKjEuIExpbXBpZXphLCBPcmdhbml6YWNpw7NuIHkgVHJhbnNmb3JtYWNpw7NuOioNCiMjIyMjICoqTm90YXM6KiogQW50ZXMgZGUgaW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyBzZSBvcHTDsyBwb3I6DQojIyMjIyAxLiBFbGltaW5hciBsb3MgdG90YWxlcyBkZSBjYWRhIG1lcy4NCg0KIyMjIyAqKlTDqWNuaWNhIDEuSW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyB5IGxpbXBpYXIgbG9zIG5vbWJyZXMgZGUgbGFzIGNvbHVtbmFzIGNvbiBsYSBmdW5jacOzbiAiY2xlYW5fbmFtZXMiOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBmaWxlLmNob29zZSgpDQpiZDUgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcbWFyaWFcXERvY3VtZW50c1xcSVRFU00gTEFFVFxcU2VtZXN0cmUgN1xcUmV0b1xcRk9STSBCQVNFUyBERSBEQVRPU1xcRk9STSAtIE1lcm1hICgyKS5jc3YiKQ0KbWVybWEgPC0gY2xlYW5fbmFtZXMoYmQ1KQ0Kc3VtbWFyeShtZXJtYSkNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMi4gQ2FtYmlhciBhIGZvcm1hdG8gZGUgY2FyYWN0ZXIsIGZlY2hhIHkgbsO6bWVybzoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1lcm1hJGtpbG9zPC1hcy5udW1lcmljKG1lcm1hJGtpbG9zKSAgICAgICAgICAgICAgICAgDQptZXJtYSRmZWNoYSA8LSBhcy5EYXRlKG1lcm1hJGZlY2hhLCBmb3JtYXQgPSAiJWQvJW0vJVkiKQ0KbWVybWEkbWVzPC1hcy5mYWN0b3IobWVybWEkbWVzKQ0Kc3VtbWFyeShtZXJtYSkNCmBgYA0KDQojIyMjICoqRXhwb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyBsaW1waWE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp3cml0ZS5jc3YobWVybWEsIGZpbGUgPSJtZXJtYV9iZF9saW1waWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMjICoyLiBDbGFzaWZpY2FjacOzbiBkZSB2YXJpYWJsZXM6Kg0KIyMjIyAqKsK/Q3XDoW50YXMgdmFyaWFibGVzIHkgY3XDoW50b3MgcmVnaXN0cm9zIHRpZW5lIGxhIGJhc2UgZGUgZGF0b3M/KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmRlc2NyaWJlRGF0YShtZXJtYSxoZWFkPTEsdGFpbD0xKQ0KYGBgDQoNCkxhIGJhc2UgZGUgZGF0b3MgZGUgIk1lcm1hIiBjdWVudGEgY29uICoqMyB2YXJpYWJsZXMgeSA1MCByZWdpc3Ryb3MgZW4gY2FkYSBjb2x1bW5hLCBzaWVuZG8gMTUwIHJlZ2lzdHJvcyB0b3RhbGVzLioqDQoNCiMjIyMgKioqVGFibGEgZGUgY2xhc2lmaWNhY2nDs246KioqIA0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NClZhcmlhYmxlX21lcm1hPC1jKCJGZWNoYSIsICJNZXMiLCAiS2lsb3MiKQ0KVGlwb19tZXJtYTwtYygiQ3VhbGl0YXRpdmEiLCAiQ3VhbGl0YXRpdmEiLCAiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIikNCk1lZGlkYV9tZXJtYSA8LWMoIk5BIiwgIk5BIiwgIktpbG9ncmFtb3MiKQ0KdGFibGE2PC1kYXRhLmZyYW1lKFZhcmlhYmxlX21lcm1hLFRpcG9fbWVybWEsIE1lZGlkYV9tZXJtYSkNCnRhYmxhNg0Ka25pdHI6OmthYmxlKHRhYmxhNikNCmBgYA0KDQojIyMgKjMuIEdyw6FmaWNvcyBFc3RhZMOtc3RpY29zIERlc2NyaXB0aXZvczoqDQoNCiMjIyMgKipHcsOhZmljbyBkZSBiYXJyYXMgKG1lcyB5IGtpbG9zKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmdncGxvdChtZXJtYSwgYWVzKHg9bWVzLCB5PWtpbG9zKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGw9Im9yYW5nZSIpICsgc2NhbGVfZmlsbF9ncmV5KCkgKw0KICBsYWJzKHRpdGxlID0gIlJlbGFjacOzbiBkZSBsb3Mga2lsb3MgZGUgbWVybWEgZW4gZWwgbWVzIiwgDQogICAgICAgc3VidGl0bGUgPSAiTWVybWEgZW1wcmVzYSBGT1JNIiwNCiAgICAgICBjYXB0aW9uID0gIlJlbGFjacOzbiIsIA0KICAgICAgIHggPSAiTWVzIikNCmBgYA0KU2Ugb2JzZXJ2YSBxdWUgZWwgbWVzIGVuICoqZWwgcXVlIG3DoXMgc2UgZ2VuZXJvIG1lcm1hIGZ1ZSBlbiBBZ29zdG8qKiwgc2VndWlkbyBwb3IgRmVicmVybyB5IE1heW8uIEVsIG1lcyBxdWUgKiptZW5vcyBnZW5lcsOzIG1lcm1hIGZ1ZSBTZXB0aWVtYnJlIHkgRW5lcm8uKioNCg0KIyMjIyAqKkJveHBsb3QgKGtpbG9zKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBhcihuZXcgPSBUUlVFKQ0KYm94cGxvdChtZXJtYSRraWxvcywgDQogICAgICAgIGhvcml6b250YWwgPSBGQUxTRSwgDQogICAgICAgIGx3ZCA9IDIsIA0KICAgICAgICBjb2wgPSByZ2IoMSwgMCwgMCwgYWxwaGEgPSAwLjQpLCANCiAgICAgICAgICB4bGFiID0gIk1lc2VzIiwgIA0KICAgICAgICB5bGFiID0gIktpbG9zIGRlIE1lcm1hIiwgIA0KICAgICAgICBtYWluID0gIlByb21lZGlvIGRlIGtpbG9zIGRlIG1lcm1hIHByb2R1Y2lkb3MiLCANCiAgICAgICAgbm90Y2ggPSBUUlVFLCANCiAgICAgICAgYm9yZGVyID0gImJsYWNrIiwgIA0KICAgICAgICBvdXRwY2ggPSAyNSwgICAgICAgDQogICAgICAgIG91dGJnID0gImdyZWVuIiwgICANCiAgICAgICAgd2hpc2tjb2wgPSAiYmx1ZSIsIA0KICAgICAgICB3aGlza2x0eSA9IDIsICAgICAgDQogICAgICAgIGx0eSA9IDEpIA0KYGBgDQoNCkVuIGVzdGEgZ3LDoWZpY2EgcG9kZW1vcyBvYnNlcnZhciBlbCAqKnByb21lZGlvIGRlIG1lcm1hIGdlbmVyYWRhIGVuIHRvZG9zIGxvcyBtZXNlcyBxdWUgZXN0w6EgYXByb3ggZW4gbG9zIDQwMDAga2lsb3MuKiogU2luIGVtYmFyZ28sIGhhbiBoYWJpZG8gdmVjZXMgcXVlIHNlIGxsZWdhbiBhICpnZW5lcmFyIG1lbm9zIGRlIDEwMDAga2lsb3MgbyBoYXN0YSA2MDAwIGtpbG9zLioNCg0KIyMjICo0LiBQcmluY2lwYWxlcyBFc3RhZMOtc3RpY29zIERlc2NyaXB0aXZvczoqDQoNCiMjIyMgKipNZWRpYToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1lZGlhX2tpbG9zIDwtIG1lYW4obWVybWEka2lsb3MpDQptZWRpYV9raWxvcw0KYGBgDQoNCiMjIyMgKipNZWRpYW5hOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFuYV9raWxvcyA8LSBtZWRpYW4obWVybWEka2lsb3MpDQptZWRpYW5hX2tpbG9zDQpgYGANCg0KIyMjIyAqKk1vZGE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptb2RlIDwtIGZ1bmN0aW9uICh4KSB7DQogIHV4IDwtIHVuaXF1ZSh4KQ0KICB1eCBbd2hpY2gubWF4KHRhYnVsYXRlKG1hdGNoKHgsdXgpKSldDQp9DQoNCm1vZGFfa2lsb3MgPC0gbW9kZShtZXJtYSRraWxvcykNCm1vZGFfa2lsb3MNCmBgYA0KDQojIyMjICoqRGVzdmlhY2nDs24gRXN0w6FuZGFyOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdmFyaWFuemFfa2lsb3MgPC0gdmFyKG1lcm1hJGtpbG9zKQ0KdmFyaWFuemFfa2lsb3MgDQoNCmRlc3ZpYWNpb25lc3RhbmRhcl9raWxvcyA8LSBzcXJ0KHZhcmlhbnphX2tpbG9zKQ0KZGVzdmlhY2lvbmVzdGFuZGFyX2tpbG9zDQpgYGANCg0KIyMjIyAqKlRhYmxhIGRlIGZyZWN1ZW5jaWE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpWYXJpYWJsZV9tZXJtYTE8LWMoIktpbG9zIikNCk1lZGlhX21lcm1hPC1jKCIzNzA4LjUyIikNCk1lZGlhbmFfbWVybWE8LWMoIjM5MjUiKQ0KTW9kYV9tZXJtYTwtYygiMzgxMCIpDQpEZXN2aWFjaW9uX0VzdGFuZGFyX21lcm1hPC1jKCIxMDIzLjk5IikNCnRhYmxhNzwtZGF0YS5mcmFtZShWYXJpYWJsZV9tZXJtYTEsTWVkaWFfbWVybWEsTWVkaWFuYV9tZXJtYSxNb2RhX21lcm1hLERlc3ZpYWNpb25fRXN0YW5kYXJfbWVybWEpDQprbml0cjo6a2FibGUodGFibGE3KQ0KYGBgDQoNCkRlIGVzdG9zIGVzdGFkw61zdGljb3MgZGVzY3JpcHRpdm9zIHBvZGVtb3MgZGVzdGFjYXIgcHJpbmNpcGFsbWVudGUgbG9zIHNpZ3VpZW50ZXMgaGFsbGF6Z29zOg0KDQoxLiAqKkZPUk0gcHJvZHVjZSBlbiBwcm9tZWRpbyAzNzA4LjUyIGtpbG9zIGRlIG1lcm1hIHBvciBtZXMuKioNCg0KIyMgKipnKSBTY3JhcCoqDQojIyMgKjEuIExpbXBpZXphLCBPcmdhbml6YWNpw7NuIHkgVHJhbnNmb3JtYWNpw7NuOioNCiMjIyMjICoqTm90YXM6KiogQW50ZXMgZGUgaW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyBzZSBvcHTDsyBwb3I6DQojIyMjIyAxLiBFbGltaW5hciBsYSBzZWd1bmRhIGZpbGEgZGUgbGEgYmFzZSBkZSBkYXRvcy4NCg0KIyMjIyAqKlTDqWNuaWNhIDE6SW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyB5IGxpbXBpYXIgbG9zIG5vbWJyZXMgZGUgbGFzIGNvbHVtbmFzIGNvbiBsYSBmdW5jacOzbiAiY2xlYW5fbmFtZXMiOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYmQ2IDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXG1hcmlhXFxEb2N1bWVudHNcXElURVNNIExBRVRcXFNlbWVzdHJlIDdcXFJldG9cXEZPUk0gQkFTRVMgREUgREFUT1NcXEZPUk0gLSBTY3JhcC5jc3YiKQ0Kc2NyYXAgPC0gY2xlYW5fbmFtZXMoYmQ2KQ0Kc3VtbWFyeShzY3JhcCkNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMi4gRWxpbWluYXIgY29sdW1uYXMgaW5lY2VzYXJpYXM6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzY3JhcCA8LSBzdWJzZXQoc2NyYXAsIHNlbGVjdCA9IC1jIChyZWZlcmVuY2lhLCBwcm9kdWN0bywgdWJpY2FjaW9uX2RlX2Rlc2VjaG8sIGVzdGFkbywgdW5pZGFkX2RlX21lZGlkYSkpDQpgYGANCg0KIyMjIyAqKlTDqWNuaWNhIDMuIENhbWJpYXIgYSBmb3JtYXRvIGRlIGNhcmFjdGVyLCBmZWNoYSB5IG7Dum1lcm86KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzY3JhcCRjYW50aWRhZDwtYXMubnVtZXJpYyhzY3JhcCRjYW50aWRhZCkgICAgICAgICAgICAgICAgIA0Kc2NyYXAkZmVjaGEgPC0gYXMuRGF0ZShzY3JhcCRmZWNoYSwgZm9ybWF0ID0gIiVkLyVtLyVZIikNCnNjcmFwJHViaWNhY2lvbl9kZV9vcmlnZW48LWFzLmZhY3RvcihzY3JhcCR1YmljYWNpb25fZGVfb3JpZ2VuKQ0Kc3VtbWFyeShzY3JhcCkNCmBgYA0KDQojIyMjICoqRXhwb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyBsaW1waWE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp3cml0ZS5jc3Yoc2NyYXAsIGZpbGUgPSJzY3JhcF9iZF9saW1waWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KIyMjICoyLiBDbGFzaWZpY2FjacOzbiBkZSB2YXJpYWJsZXM6Kg0KIyMjIyAqKsK/Q3XDoW50YXMgdmFyaWFibGVzIHkgY3XDoW50b3MgcmVnaXN0cm9zIHRpZW5lIGxhIGJhc2UgZGUgZGF0b3M/KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZXNjcmliZURhdGEoc2NyYXAsaGVhZD0xLHRhaWw9MSkNCmBgYA0KTGEgYmFzZSBkZSBkYXRvcyBkZSAiU2NyYXAiIGN1ZW50YSBjb24gKiozIHZhcmlhYmxlcyB5IDI1MCByZWdpc3Ryb3MgZW4gY2FkYSBjb2x1bW5hLCBzaWVuZG8gNzUwIHJlZ2lzdHJvcyB0b3RhbGVzLioqDQoNCiMjIyMgKipUYWJsYSBkZSBDbGFzaWZpY2FjacOzbjoqKiANCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpWYXJpYWJsZV9zY3JhcDwtYygiRmVjaGEiLCAiQ2FudGlkYWQiLCAiVWJpY2FjacOzbiBkZSBPcmlnZW4iKQ0KVGlwb19zY3JhcDwtYygiQ3VhbGl0YXRpdmEiLCAiQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIiwgIkN1YWxpdGF0aXZhIikNCk1lZGlkYV9zY3JhcCA8LWMoIk5BIiwgIktpbG9ncmFtb3MiLCAiTkEiKQ0KdGFibGE4PC1kYXRhLmZyYW1lKFZhcmlhYmxlX3NjcmFwLFRpcG9fc2NyYXAsIE1lZGlkYV9zY3JhcCkNCnRhYmxhOA0Ka25pdHI6OmthYmxlKHRhYmxhOCkNCmBgYA0KDQojIyMgKjMuIEFuw6FsaXNpcyBlc3RhZMOtc3RpY28gZGVzY3JpcHRpdm86Kg0KIyMjIyAqKkJveHBsb3QgKGNhbnRpZGFkIHkgdWJpY2FjacOzbiBkZSBvcmlnZW4pOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYm94cGxvdChzY3JhcCRjYW50aWRhZCAgfiAgc2NyYXAkdWJpY2FjaW9uX2RlX29yaWdlbiwNCiAgICAgICAgY29sID0gcmFpbmJvdyhuY29sKHRyZWVzKSkpDQpgYGANCkNvbiBlc3RhIGdyw6FmaWNhIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlICoqbGEgY2FudGlkYWQgZGUgdW5pZGFkZXMgcHJvZHVjaWRhcyBlc3TDoSBub3JtYWxtZW50ZSB1YmljYWRhIGVuICJQcmUtUHJvZHVjY2nDs24iIHkgYXNpbWlzbW8gZXN0YSBjYXRlZ29yw61hIGVzIGxhIG3DoXMgZGlzcGVyc2EuKioNCg0KIyMjICpQcmluY2lwYWxlcyBFc3RhZMOtc3RpY29zIERlc2NyaXB0aXZvczoqDQoNCiMjIyMgKipNZWRpYToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1lZGlhX2NhbnRpZGFkIDwtIG1lYW4oc2NyYXAkY2FudGlkYWQpDQptZWRpYV9jYW50aWRhZA0KYGBgDQoNCiMjIyMgKipNZWRpYW5hOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFuYV9jYW50aWRhZCA8LSBtZWRpYW4oc2NyYXAkY2FudGlkYWQpDQptZWRpYW5hX2NhbnRpZGFkDQpgYGANCg0KIyMjIyAqKk1vZGE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptb2RlIDwtIGZ1bmN0aW9uICh4KSB7DQogIHV4IDwtIHVuaXF1ZSh4KQ0KICB1eCBbd2hpY2gubWF4KHRhYnVsYXRlKG1hdGNoKHgsdXgpKSldDQp9DQoNCm1vZGFfY2FudGlkYWQgPC0gbW9kZShzY3JhcCRjYW50aWRhZCkNCm1vZGFfY2FudGlkYWQNCmBgYA0KDQojIyMjICoqRGVzdmlhY2nDs24gRXN0w6FuZGFyOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdmFyaWFuemFfY2FudGlkYWQgPC0gdmFyKHNjcmFwJGNhbnRpZGFkKQ0KdmFyaWFuemFfY2FudGlkYWQNCg0KZGVzdmlhY2lvbmVzdGFuZGFyX2NhbnRpZGFkIDwtIHNxcnQodmFyaWFuemFfY2FudGlkYWQpDQpkZXN2aWFjaW9uZXN0YW5kYXJfY2FudGlkYWQNCmBgYA0KDQojIyMjICoqVGFibGEgZGUgZnJlY3VlbmNpYToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NClZhcmlhYmxlX3NjcmFwMTwtYygiQ2FudGlkYWQiKQ0KTWVkaWFfc2NyYXA8LWMoIjYuOTYiKQ0KTWVkaWFuYV9zY3JhcDwtYygiMiIpDQpNb2RhX3NjcmFwPC1jKCIxNDAuMzkiKQ0KRGVzdmlhY2lvbl9Fc3RhbmRhcl9zY3JhcDwtYygiMTEuODQ4IikNCnRhYmxhOTwtZGF0YS5mcmFtZShWYXJpYWJsZV9zY3JhcDEsTWVkaWFfc2NyYXAsTWVkaWFuYV9zY3JhcCxNb2RhX3NjcmFwLERlc3ZpYWNpb25fRXN0YW5kYXJfc2NyYXApDQprbml0cjo6a2FibGUodGFibGE5KQ0KYGBgDQoNCkRlIGVzdG9zIGVzdGFkw61zdGljb3MgZGVzY3JpcHRpdm9zIHBvZGVtb3MgZGVzdGFjYXIgcHJpbmNpcGFsbWVudGUgbG9zIHNpZ3VpZW50ZXMgaGFsbGF6Z29zOg0KDQoxLiBGT1JNIHRpZW5lIHViaWNhZGEgKipsYSBtYXlvciBwYXJ0ZSBkZSBzdSBzY3JhcCBlbiBwcmUtcHJvZHVjY2nDs24qKi4gTG8gcXVlIGluZGljYSBxdWUgZXMgZW4gZXNhIHBhcnRlIGRlIHN1cyBwcm9jZXNvcyBxdWUgZGViZW4gY3VpZGFyIHBhcmEgZXZpdGFyIGxhIGdlbmVyYWNpw7NuIGRlIHRhbnRvIGRlc3BlcmRpY2lvLg0KDQojIyAqKmgpIEJhc2UgZGUgZGF0b3MgRXh0ZXJuYSAtICJQcm9kdWNjacOzbiBkZSBjYXJ0w7NuIGVuIE3DqXhpY28iKioNCiMjIyAqMS4gTGltcGllemEsIE9yZ2FuaXphY2nDs24geSBUcmFuc2Zvcm1hY2nDs246Kg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgZmlsZS5jaG9vc2UoKQ0KYmQ4IDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXG1hcmlhXFxEb2N1bWVudHNcXElURVNNIExBRVRcXFNlbWVzdHJlIDdcXFJldG9cXEVWSTJcXGJkIGV4dGVybmEuY3N2IikNCmJkX2V4dGVybmEgPC0gY2xlYW5fbmFtZXMoYmQ4KQ0Kc3VtbWFyeShiZF9leHRlcm5hKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSAxLiBDYW1iaWFyIGZvcm1hdG8gZGUgbGFzIHZhcmlhYmxlczoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmJkX2V4dGVybmEkc3RhdGVfaWQ8LWFzLm51bWVyaWMoYmRfZXh0ZXJuYSRzdGF0ZV9pZCkgICAgICAgDQpiZF9leHRlcm5hJHN0YXRlPC1hcy5mYWN0b3IoYmRfZXh0ZXJuYSRzdGF0ZSkgICANCmJkX2V4dGVybmEkcmVnaW9uPC1hcy5udW1lcmljKGJkX2V4dGVybmEkcmVnaW9uKSAgIA0KYmRfZXh0ZXJuYSRpbmR1c3RyeV9ncm91cF9pZDwtYXMubnVtZXJpYyhiZF9leHRlcm5hJGluZHVzdHJ5X2dyb3VwX2lkKSAgDQpiZF9leHRlcm5hJGluZHVzdHJ5X2dyb3VwPC1hcy5mYWN0b3IoYmRfZXh0ZXJuYSRpbmR1c3RyeV9ncm91cCkgDQpiZF9leHRlcm5hJGVjb25vbWljX3VuaXQ8LWFzLm51bWVyaWMoYmRfZXh0ZXJuYSRlY29ub21pY191bml0KSAgICANCnN1bW1hcnkoYmRfZXh0ZXJuYSkNCmBgYA0KDQojIyMgKjIuIENsYXNpZmljYWNpw7NuIGRlIHZhcmlhYmxlczoqDQojIyMjICoqwr9DdcOhbnRhcyB2YXJpYWJsZXMgeSBjdcOhbnRvcyByZWdpc3Ryb3MgdGllbmUgbGEgYmFzZSBkZSBkYXRvcz8qKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRlc2NyaWJlRGF0YShiZF9leHRlcm5hLGhlYWQ9MSx0YWlsPTEpDQpgYGANCkxhIGJhc2UgZGUgZGF0b3MgZXh0ZXJuYSBjdWVudGEgY29uICoqNiB2YXJpYWJsZXMgeSAyNCByZWdpc3Ryb3MgZW4gY2FkYSBjb2x1bW5hLCBzaWVuZG8gMTQ0IHJlZ2lzdHJvcyB0b3RhbGVzLioqDQoNCiMjIyMgKipUYWJsYSBkZSBDbGFzaWZpY2FjacOzbjoqKiANCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpWYXJpYWJsZV9jYXJ0b248LWMoIlN0YXRlLklEIiwiU3RhdGUiLCJSZWdpb24iLCJJbmR1c3RyeS5Hcm91cC5JRCIsIkluZHVzdHJ5Lkdyb3VwIiwgIkVjb25vbWljLlVuaXQiKQ0KVGlwb19jYXJ0b248LWMoIkN1YWxpdGF0aXZhIiwgIkN1YWxpdGF0aXZhIiwiQ3VhbGl0YXRpdmEiLCAiQ3VhbGl0YXRpdmEiLCAiQ3VhbGl0YXRpdmEiLCAiQ3VhbnRpdGF0aXZhIChkaXNjcmV0YSkiKQ0KTWVkaWRhX2NhcnRvbjwtYygiTkEiLCJOQSIsIk5BIiwiTkEiLCJOQSIsIkNhcnTDs24gcHJvZHVjaWRvIikNCnRhYmxhMTA8LWRhdGEuZnJhbWUoVmFyaWFibGVfY2FydG9uLFRpcG9fY2FydG9uLE1lZGlkYV9jYXJ0b24pDQprbml0cjo6a2FibGUodGFibGExMCkNCmBgYA0KDQojIyMgKjMuIEdyw6FmaWNvcyBkZXNjcmlwdGl2b3MgZXN0YWTDrXN0aWNvczoqDQoNCiMjIyMgKipUYWJsYSBkZSBmcmVjdWVuY2lhIChlc3RhZG8geSB1bmlkYWRlcyk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwcm9wLnRhYmxlKHRhYmxlKGJkX2V4dGVybmEkc3RhdGUsIGJkX2V4dGVybmEkZWNvbm9taWNfdW5pdCkpDQpgYGANCkVuIGVzdGEgdGFibGEgZGUgZnJlY3VlbmNpYSBwb2RlbW9zIG9ic2V2YXIgcXVlIGVsICoqZXN0YWRvIHF1ZSBtw6FzIHByb2R1Y2UgY2FydMOzbiBlcyBQdWVibGEqKiAoMTUxIHVuaWRhZGVzIHByb21lZGlvIHBvciBlbXBsZWFkbyksIHNlZ3VpZG8gZGUgRXN0YWRvIGRlIE3DqXhpY28gKDQzIHVuaWRhZGVzIHBvciBlbXBsZWFkbykgeSBOdWV2byBMZcOzbiAoMjAgdW5pZGFkZXMgcG9yIGVtcGxlYWRvKS4NCg0KIyMjIyAqKkdyw6FmaWNhIGRlIHBheSBjdWFsaXRhdGl2YSAocmVnacOzbik6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp0YWJsZShiZF9leHRlcm5hJHJlZ2lvbikNCnByb3BvcmNpb25lc19leHQgPC0gYyg3LCA2LCA3LCA0KQ0KZXRpcXVldGFzX2V4dCA8LSBjKCJOb3J0ZSIsICJDZW50cm8vT2VzdGUiLCAiQ2VudHJvIiwgIlN1ciIpDQpwY3RfZXh0IDwtIHJvdW5kKHByb3BvcmNpb25lc19leHQvc3VtKHByb3BvcmNpb25lc19leHQpKjEwMCkNCmV0aXF1ZXRhc19leHQgPC0gcGFzdGUoZXRpcXVldGFzX2V4dCwgcGN0X2V4dCkNCmV0aXF1ZXRhc19leHQgPC0gcGFzdGUoZXRpcXVldGFzX2V4dCwiJSIsc2VwPSIiKQ0KcGllKHByb3BvcmNpb25lc19leHQsbGFiZWxzID0gZXRpcXVldGFzX2V4dCwNCiAgICBjb2w9cmFpbmJvdyhsZW5ndGgoZXRpcXVldGFzX2V4dCkpLA0KICAgIG1haW49IlBvcmNlbnRhamUgZGUgZXN0YWRvcyBlbiBNw6l4aWNvIHBvciByZWdpb25lcyIpDQpgYGANCg0KRW4gZXN0YSBncsOhZmljYSBzZSBwdWVkZSBvYnNlcnZhciBxdWUgKipsYSByZWdpw7NuIE5vcnRlIHkgIENlbnRybyBjdWVudGFuIGNvbiBtw6FzIGVzdGFkb3MsKiogc2VndWlkbyBwb3IgbGEgcmVnacOzbiBDZW50cm8vT2VzdGUgeSBwb3Igw7psdGltbyBsYSByZWdpw7NuIFN1ci4gRXN0byBxdWllcmUgZGVjaXIgcXVlICpsYSBwcm9iYWJpbGlkYWQgZGUgcXVlIGhheWEgbcOhcyBwcm9kdWNjacOzbiBwb3IgcGFydGUgZGUgbGFzIHByaW1lcmFzIGRvcyByZWdpb25lcyBtZW5jaW9uYWRhcyBlcyBtYXlvci4qDQoNCiMjIyMgKipHcsOhZmljYSBkZSBwYXkgY3VhbGl0YXRpdmEgKHVuaWRhZGVzIGVjb27Ds21pY2FzKToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRhYmxlKGJkX2V4dGVybmEkZWNvbm9taWNfdW5pdCkNCnByb3BvcmNpb25lc191bmkgPC0gYyg2LCAxLCA0LCAzLCAzLCAxLCAxLCAxLCAxLCAxLCAxLCAxKQ0KZXRpcXVldGFzX3VuaSA8LSBjKCIxIiwgIjIiLCAiMyIsICI1IiwgIjYiLCAiOCIsICIxMSIsICIxMyIsICIxNyIsICIyMCIsICI0MyIsICIxNTEiKQ0KcGN0X3VuaSA8LSByb3VuZChwcm9wb3JjaW9uZXNfdW5pL3N1bShwcm9wb3JjaW9uZXNfdW5pKSoxMDApDQpldGlxdWV0YXNfdW5pIDwtIHBhc3RlKGV0aXF1ZXRhc191bmksIHBjdF91bmkpDQpldGlxdWV0YXNfdW5pIDwtIHBhc3RlKGV0aXF1ZXRhc191bmksIiUiLHNlcD0iIikNCnBpZShwcm9wb3JjaW9uZXNfdW5pLGxhYmVscyA9IGV0aXF1ZXRhc191bmksDQogICAgY29sPXJhaW5ib3cobGVuZ3RoKGV0aXF1ZXRhc191bmkpKSwNCiAgICBtYWluPSJQcm9kdWNjacOzbiBkZSB1bmlkYWRlcyBwcm9tZWRpbyBwb3IgZW1wbGVhZG8iKQ0KYGBgDQoNCkxhIGdyw6FmaWNhIGFudGVyaW9yIG5vcyBpbmRpY2EgZWwgcG9yY2VudGFqZSBkZSBsYSBwcm9kdWNjacOzbiBkZSB1bmlkYWRlcyBwb3IgcHJvbWVkaW8gcG9yIGVtcGxlYWRvLCBwb3IgZWplbXBsbyAqKmVsIDI1JSBkZSBsb3MgZW1wbGVhZG9zIHNvbG8gcHJvZHVjZSAxIHVuaWRhZCoqLCBzZWd1aWRvIHBvciBlbCAxNyUgZGUgbG9zIGVtcGxlYWRvcyBxdWUgcHJvZHVjZW4gMyB1bmlkYWRlcywgeSBhc8OtIHN1Y2VzaXZhbWVudGUuDQoNCiMjIyMgKipHcsOhZmljbyBkZSBkaXNwZXJzacOzbiAocmVnaW9uIHkgdW5pZGFkZXMpOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KGRhdGEgPSBiZF9leHRlcm5hLCBtYXBwaW5nID0gYWVzKHJlZ2lvbiwgZWNvbm9taWNfdW5pdCkpICsgZ2VvbV9wb2ludCgpICsgdGhlbWVfYncoKQ0KYGBgDQpFbiBlc3RlIGNhc28sIHBvZGVtb3MgdmVyIHF1ZSAqKmxhIHJlZ2nDs24gMyBsYSBjdWFsIGVzIGxhIENlbnRybywgY3VlbnRhIGNvbiB1biB2YWxvciBhdMOtcGljbywgZWwgY3VhbCBlcyBQdWVibGEgY29uIDE1MSB1bmlkYWRlcyB5IGVzIHBvciBlc28gcXVlIGVzIGxhIHJlZ2nDs24gcXVlIHByb2R1Y2UgbcOhcyBjYXJ0w7NuIGVuIE3DqXhpY28uKiogDQoNCiMjIyMgKipHcsOhZmljbyBkZSBkaXNwZXJzacOzbiAoYm94cGxvdCk6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpib3hwbG90KGJkX2V4dGVybmEkc3RhdGVfaWQgfiBiZF9leHRlcm5hJGVjb25vbWljX3VuaXQsIGhvcml6b250YWwgPSBUUlVFKQ0KYGBgDQpFbiBlc3RlIGJveHBsb3Qgc2UgaW50ZXJwcmV0YSBxdWUgbXV5IHBvY29zIGVzdGFkb3MgcHJvZHVjZW4gKiptw6FzIGRlIDYgdW5pZGFkZXMgcHJvbWVkaW8gcG9yIGVtcGxlYWRvLioqDQoNCiMjIyMgKipHcsOhZmljbyBkZSBiYXJyYXMgKG5qZGN6KToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmhpc3QoeCA9IGJkX2V4dGVybmEkcmVnaW9uLCBtYWluID0gIkVzdGFkb3MgcG9yIFJlZ2nDs24iLCANCiAgICAgeGxhYiA9ICJSZWdpb25lcyIsIHlsYWIgPSAiRnJlY3VlbmNpYSIsDQogICAgIGNvbCA9ICJicm93biIpDQpgYGANCkVuIGVzdGEgZ3LDoWZpY2EgZGUgYmFycmEgcG9kZW1vcyBvYnNlcnZhciBxdWUgbGEgKipyZWdpw7NuIDEgKE5vcnRlKSB5IHJlZ2nDs24gMyAoQ2VudHJvKSoqIHNvbiBsb3MgcXVlIG3DoXMgZXN0YWRvcyB0aWVuZW4gKHRlbmllbmRvIDcgZW4gdG90YWwpLg0KDQojIyMgKjQuIFByaW5jaXBhbGVzIEVzdGFkw61zdGljb3MgRGVzY3JpcHRpdm9zOioNCg0KIyMjIyAqKk1lZGlhOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFfZWNvbm9taWMgPC0gbWVhbihiZF9leHRlcm5hJGVjb25vbWljX3VuaXQpDQptZWRpYV9lY29ub21pYw0KYGBgDQoNCiMjIyMgKipNZWRpYW5hOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVkaWFuYV9lY29ub21pYyA8LSBtZWRpYW4oYmRfZXh0ZXJuYSRlY29ub21pY191bml0KQ0KbWVkaWFuYV9lY29ub21pYw0KYGBgDQoNCg0KIyMjIyAqKk1vZGE6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptb2RlIDwtIGZ1bmN0aW9uICh4KSB7DQogIHV4IDwtIHVuaXF1ZSh4KQ0KICB1eCBbd2hpY2gubWF4KHRhYnVsYXRlKG1hdGNoKHgsdXgpKSldDQp9DQoNCm1vZGFfZWNvbm9taWMgPC0gbW9kZShiZF9leHRlcm5hJGVjb25vbWljX3VuaXQpDQptb2RhX2Vjb25vbWljDQpgYGANCg0KIyMjIyAqKkRlc3ZpYWNpw7NuIEVzdMOhbmRhcjoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnZhcmlhbnphX2Vjb25vbWljIDwtIHZhcihiZF9leHRlcm5hJGVjb25vbWljX3VuaXQpDQp2YXJpYW56YV9lY29ub21pYw0KZGVzdmlhY2lvbmVzdGFuZGFyX2Vjb25vbWljIDwtIHNxcnQodmFyaWFuemFfZWNvbm9taWMpDQpkZXN2aWFjaW9uZXN0YW5kYXJfZWNvbm9taWMNCmBgYA0KDQojIyMjICoqVGFibGEgZGUgZnJlY3VlbmNpYToqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NClZhcmlhYmxlX2JkZXh0ZXJuYTwtYygiRWNvbm9taWMgVW5pdFMiKQ0KTWVkaWFfYmRleHRlcm5hPC1jKCIxMy4xNjYiKQ0KTWVkaWFuYV9iZGV4dGVybmE8LWMoIjUiKQ0KTW9kYV9iZGV4dGVybmE8LWMoIjEiKQ0KRGVzdmlhY2lvbl9Fc3RhbmRhcl9iZGV4dGVybmE8LWMoIjMwLjc1OSIpDQp0YWJsYTExPC1kYXRhLmZyYW1lKFZhcmlhYmxlX2JkZXh0ZXJuYSxNZWRpYV9iZGV4dGVybmEsTWVkaWFuYV9iZGV4dGVybmEsTW9kYV9iZGV4dGVybmEsRGVzdmlhY2lvbl9Fc3RhbmRhcl9iZGV4dGVybmEpDQprbml0cjo6a2FibGUodGFibGExMSkNCmBgYA0KDQojIyAqKlByb3B1ZXN0YXMgZGUgTWVqb3JhOioqDQoNCjxkaXYgY2xhc3M9dGV4dC1qdXN0aWZ5Pg0KDQoxLiBSZWFsaXphciB1biAqKnByb2dyYW1hIGRlIGluY2VudGl2b3MqKiBwYXJhIGxvcyBlbXBsZWFkb3MgY29uIGVsIG9iamV0aXZvIGRlICppbmNyZW1lbnRhciBzdSBQRVJNQU5FTkNJQSBlbiBsYSBlbXByZXNhKiwgb2ZyZWNpZW5kb2xlcyBwcmltZXJhbWVudGUsIHVuICptZWpvciBzdWVsZG8qIGFsIGluY3JlbWVudGFyIHN1IHJlbmRpbWllbnRvIChlc3BlY2lhbG1lbnRlIGEgbG9zIGF5dWRhbnRlcyBnZW5lcmFsZXMgcXVlIHNvbiBsb3MgcXVlIGhhbiByZXBvcnRhZG8gbcOhcyBiYWphcyksIG8gZMOhbmRvbGVzICpiZW5maWNpb3MgYWRpY2lvbmFsZXMqIGNvbW8gdGFyamV0YXMgZGUgZGVzcGVuc2EgbyByZWNvbm9jaW1pZW50b3MuDQoNCjIuICoqRXN0YW5kYXJpemFjacOzbiB5IG1lam9yYSBkZWwgcHJvY2VzbyBkZSByZWNsdXRhbWllbnRvKio6IFNlIGhhbiBvYnNlcnZhZG8gcXVlIGxvcyBlbXBsZWFkb3MgcXVlIGR1cmFuIG1lbm9zIHNvbiBsb3MgasOzdmVuZXMgeWEgcXVlIHN1IHN1ZWxkbyBubyBlcyBtdXkgYnVlbm8sIHBvciBsbyBxdWUgc2UgZGViZXLDrWFuIGJ1c2NhciAqbnVldm9zIGVtcGxlYWRvcyBxdWUgc2VhbiBtYXlvcmVzIG8gZGViZXLDrWEgZGUgaGFiZXIgdW4gaW5jcmVtZW50byBkZSBzdWVsZG8qLCBhc2ltaXNtbywgaGFibGFuZG8gc29icmUgZWwgZ8OpbmVybyB5IGVzdGFkbyBjaXZpbCwgc2UgcHVlZGUgb2JzZXJ2YXIgcXVlICpsb3MgZW1wbGVhZG9zIHNpbiBwYXJlamEgc29uIGxvcyBxdWUgbWVub3MgZGVzcGlkZW4qIChzaW5vIHF1ZSBzdWVsZW4gcmVudW5jaWFyKSwgZGVkdWNpZW5kbyBhc8OtIHF1ZSBzb24gbG9zIHF1ZSBtw6FzIGNvbXByb21pc28gdGllbmVuIGNvbiBsYSBlbXByZXNhIHB1ZXMgbm8gc2UgZGFuIGRlIGJhamEgZW4gc3UgbWF5b3JpYSBwb3IgZmFsdGFzIGNvbW8gbG8gaGFuIGhlY2hvIGNvbiBsb3MgZXgtY29sYWJvcmFkb3JlcyBkaXZvcmNpYWRvcywgY2FzYWRvcyB5IGVuIHVuacOzbiBsaWJyZS4gRXMgcG9yIGVzdG8gcXVyIEZPUk0gdGllbmUgcXVlIG1lam9yYXIgc3UgcHJvY2VzbyBkZSBzZWxlY2Npw7NuLCB0b21hciBlbiBjdWVudGEgbG9zIGluc2lnaHRzIGRlc2N1YmllcnRvcyBhbCByZWNsdXRhciBlIGluY3JlbWVudGFyIGxvcyBiZW5lZmljaW9zIGRhZG9zIGEgbG9zIGVtcGxlYWRvcyBwYXJhIGRpc21pbnVpciBsYSByb3RhY2nDs24uDQoNCjMuIEZPUk0gcHJvZHVjZSB1bmEgY2FudGlkYWQgY29uc2lkZXJhYmxlIGRlIG1lcm1hIGVuIHN1cyBwcm9jZXNvcywgcHJpbmNpcGFsbWVudGUgZW4gbGEgcHJlLXByb2R1Y2Npw7NuLCBzaWVuZG8gdW4gcHJvbWVkaW8gZGUgYXByb3guIDM3MDguNTIga2lsb3MgcG9yIG1lcy4gU2UgcHJvcG9uZSAqKmltcGxlbWVudGFyIHVuYSBtZWpvcmEgZW4gZXN0ZSBwcm9jZXNvIHBhcmEgZXZpdGFyIGxhIGdlbmVyYWNpw7NuIGRlIHRhbnRvcyByZXNpdWRvcyoqIHBhcmEgZGUgbGEgbWlzbWEgbWFuZXJhLCAqKmRpc21pbnVpciBsb3MgZ2FzdG9zIGFsIGFwcm92ZWNoYXIgYWwgbWHMgXhpbW8gbGEgbWF0ZXJpYSBwcmltYSBlIGltcGFjdGFyIHBvc2l0aXZhbWVudGUgYWwgbWVkaW8gYW1iaWVudGUqKi4NCg0KNC4gRk9STSBkZWJlcsOtYSAqKnByZXN0YXIgbcOhcyBhdGVuY2nDs24gZW4gZWwgdGllbXBvIGRlIGVudHJlZ2EgY29uIHN1IGNsaWVudGUgIk1BSExFIiBwcmluY2lwYWxtZW50ZSoqLCBwYXJhICoqcmVkdWNpciB5IGV2aXRhciByZXRyYXNvcyBkZSBlbnRyZWdhKiogeSAqY3VtcGxpciBjb24gc3VzIHByaW5jaXBpb3MgZGUgIlJhcGlkZXoiIHByaW5jaXBhbG1lbnRlLioNCg0KNS4gKipDb250YWN0YXIgbnVldm9zIHByb3ZlZWRvcmVzIGRlIGNhcnTDs24gZW4gUHVlYmxhKiosIHBhcmEgKmRpdmVyc2lmaWNhciBsYSBvYnRlbmNpw7NuIGRlIGxhIG1hdGVyaWEgcHJpbWEqIHkgZW4gY2FzbyBkZSBoYWJlciB1biBkZXNhYmFzdG8gbyB1biBpY25yZW1lbnRvIGRlIHByZWNpbyBjb25zaWRlcmFibGUsIHRlbmVyIG3DoXMgb3BjaW9uZXMgZGUgY29tcHJhLg0KDQo2LiBQYXJhIHByw7N4aW1hcyBpbnZlcnNpb25lcyBkZSBudWV2YXMgcGxhbnRhcyBvIHN1Y3Vyc2FsZXMgZGUgRk9STSwgc2UgcHJvcG9uZSAqKmludmVydGlyIGVuIGx1Z2FyZXMgZXN0cmF0w6lnaWNvcywgc2l0dWFkb3MgZW4gZXN0YWRvcyBkZSBsYSByZWdpw7NuIE5vcnRlIHkgbGEgcmVnacOzbiBjZW50cm8qKiwgeWEgcXVlLCBlcyBkb25kZSBtw6FzIHByb2R1Y2Npw7NuIGRlIGNhcnTDs24gZXhpc3RlLg0KDQo3LiBFbXBvZGVyYW1pZW50byBkZSBsb3MgY2xpZW50ZXMgc29icmUgZWwgc2VndWltaWVudG8gZGUgc3VzIHBlZGlkb3MsIFNlZ3VpbWllbnRvIGRlbCBwcm9kdWN0byBkZXNkZSBlbCBwdW50byBkZSBwYXJ0aWRhIGhhc3RhIGVsIGZpbmFsDQo1LiBNZWRpY2nDs24geSBtZWpvcmEgY29udGludWEgZGUgbGEgZXhwZXJpZW5jaWEgZGUgbG9zIGNsaWVudGVzLg0KDQo8L2Rpdj4NCg0KIyAqKlNlY2Npw7NuIDIqKg0KIyMgKipQcmVkaWNjacOzbiAxOiBFeHBvcnRhY2nDs24gZGUgVmVow61jdWxvcyBMaWdlcm9zIGVuIE3DqXhpY28gKElORUdJKSoqDQojIyMgKjEuIExpbXBpZXphIGRlIGRhdG9zOioNCg0KIyMjIyAqKkltcG9ydGFyIGJhc2UgZGUgZGF0b3M6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGZpbGUuY2hvb3NlKCkNCmJkMTQgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcbWFyaWFcXERvY3VtZW50c1xcSVRFU00gTEFFVFxcU2VtZXN0cmUgN1xcUmV0b1xcRVZJMlxcZW5jb2RlZC1iZF9wcmVkaWNjaW9uLmNzdiIpDQpwcmVkaWNjaW9uX214IDwtIGNsZWFuX25hbWVzKGJkMTQpDQpzdW1tYXJ5KHByZWRpY2Npb25fbXgpDQpgYGANCg0KIyMjIyAqKlTDqWNuaWNhIDEuIEVsaW1pbmFyIGNvbHVtbmFzIGlycmVsZXZhbnRlczoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnByZWRpY2Npb25fbXggPC0gc3Vic2V0KHByZWRpY2Npb25fbXgsIHNlbGVjdCA9IC1jIChwcm9kdWNjaW9uLCB2ZW50YSkpDQpzdW1tYXJ5KHByZWRpY2Npb25fbXgpDQpgYGANCg0KIyMjIyAqKlTDqWNuaWNhIDIuIEVsaW1pbmFyIHJlbmdsb25lcyBpcnJlbGV2YW50ZXM6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwcmVkaWNjaW9uX214IDwtIHByZWRpY2Npb25fbXhbLWMoMjAxLCAyMDIpLF0NCnN1bW1hcnkocHJlZGljY2lvbl9teCkNCmBgYA0KDQojIyMgKjIuIEV4cGxpY2FjacOzbiBkZSBsYXMgdmFyaWFibGVzOioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpWYXJpYWJsZV9jYXJ0b248LWMoIkHDsW8iLCAiTWVzIiwgIkV4cG9ydGFjacOzbiIsICJUaXBvIGRlIGNhbWJpbyIsICJJbmZsYWNpw7NuIiwgInBvcmNlbnRhamVfb2N1IiwgInBvcmNlbnRhamVfZGVzb2N1IiwgImNvbmZfY29uc3VtaWRvciIpDQpUaXBvX3ZhcmlhYmxlIDwtIGMoIkV4cGxvcmF0b3JpYSIsICJFeHBsb3JhdG9yaWEiLCAiRGVwZW5kaWVudGUiLCAiRXhwbG9yYXRvcmlhIiwgIkV4cGxvcmF0b3JpYSIsICJFeHBsb3JhdG9yaWEiLCAiRXhwbG9yYXRvcmlhIiwgIkV4cGxvcmF0b3JpYSIpDQpVbmlkYWRfTWVkaWNpw7NuIDwtIGMoIkHDsW8iLCAiTWVzIiwgIlVuaWRhZGVzIiwgIlBlc29zIE1leGljYW5vcyIsICLDjW5kaWNlIGRlIFByZWNpb3MgYWwgQ29uc3VtbyIsICIlIHRvdGFsIGRlIGVtcGxlYWRvcyIsICIlIHRvdGFsIGRlIGRlc2VtcGxlYWRvcyIsICJQZXJzcGVjdGl2YSBlY29uw7NtaWNhIGRlbCBjb25zdW1pZG9yIikNCnRhYmxhMTI8LWRhdGEuZnJhbWUoVmFyaWFibGVfY2FydG9uLFRpcG9fdmFyaWFibGUsVW5pZGFkX01lZGljacOzbikNCmtuaXRyOjprYWJsZSh0YWJsYTEyKQ0KYGBgDQoNCiMjIyAqMy4gTW9kZWxvIGRlIFJlZ3Jlc2nDs24gTGluZWFsIE3Dumx0aXBsZSAxOioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjb3JycGxvdChjb3IocHJlZGljY2lvbl9teCksIHR5cGU9InVwcGVyIiwgb3JkZXI9ImhjbHVzdCIsIGFkZGNvZWYuY29sPSJibGFjayIpDQpzdW1tYXJ5KHByZWRpY2Npb25fbXgpDQoNCm1vZGVsb19yZWdyZXNpb24xIDwtIGxtKGV4cG9ydGFjaW9ufmFubyttZXMrdGlwb19kZV9jYW1iaW8raW5mbGFjaW9uK3BvcmNlbnRhamVfb2N1K3BvcmNlbnRhamVfZGVzb2N1K2NvbmZfY29uc3VtaWRvcixkYXRhPXByZWRpY2Npb25fbXgpIA0Kc3VtbWFyeShtb2RlbG9fcmVncmVzaW9uMSkNCg0KZWZmZWN0X3Bsb3QobW9kZWxvX3JlZ3Jlc2lvbjEscHJlZD10aXBvX2RlX2NhbWJpbyxpbnRlcnZhbD1UUlVFKQ0KYGBgDQoNCkVuIGVzdGUgbW9kZWxvLCBzZSBwdWRvIG9ic2VydmFyIHF1ZSAqKmVsIHRpcG8gZGUgY2FtYmlvIGVyYSBsYSB2YXJpYWJsZSBxdWUgbcOhcyBpbXBhY3RhYmEgYSBsYSBleHBvcnRhY2nDs24qKiwgcG9yIGxvIHF1ZSBzaSBlbCBwZXNvIHZhbMOtYSBtw6FzIGVuIGVzdGUgY2FzbywgbGEgZXhwb3J0YWNpw7NuIGluY3JlbWVudGFiYSwgdGVuaWVuZG8gZXN0YSB2YXJpYWJsZSB1bmEgKip0ZW5kZW5jaWEgbmVnYXRpdmEqKiwgb2N1cnJpZW5kbyBsbyBtaXNtbyBjb24gbGEgKmluZmxhY2nDs24qLiBQb3Igb3RybyBsYWRvLCAqKlNpIGluY3JlbWVudGEgZWwgJSBkZSBvY3VwYWNpw7NuIChlbXBsZW8pLCBxdWUgdGFtYmllbiBlcyB1bmEgdmFyaWFibGUgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmY2F0aXZhLCBpbmNyZW1lbnRhIGxhIGV4cG9ydGFjacOzbiBkZSB2ZWjDrWN1bG9zKiosIHRlbmllbmRvIGVzdGEgdW5hIHRlbmRlbmNpYSBwb3NpdGl2YS4NCg0KIyMgKipQcmVkaWNjacOzbiAyOiBDYXIgU2FsZXMgaW4gVW5pdGVkIFN0YXRlcyAoU3RhdGlzdGEpKioNCg0KIyMjICoxLiBMaW1waWV6YSBkZSBkYXRvczoqDQojIyMjIEltcG9ydGFyIGJhc2UgZGUgZGF0b3M6DQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBmaWxlLmNob29zZSgpDQpiZDkgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcbWFyaWFcXERvY3VtZW50c1xcSVRFU00gTEFFVFxcU2VtZXN0cmUgN1xcUmV0b1xcRVZJMlxcdXNfYXV0b21vdGl2ZV9pbmR1c3RyeS5jc3YiKQ0KcHJlZGljY2lvbl91c2EgPC0gY2xlYW5fbmFtZXMoYmQ5KQ0Kc3VtbWFyeShwcmVkaWNjaW9uX3VzYSkNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMTogQ2FtYmlhciBhIGZvcm1hdG8gZGUgY2FyYWN0ZXIgYSBuw7ptZXJvOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcHJlZGljY2lvbl91c2EkeWVhcjwtYXMubnVtZXJpYyhwcmVkaWNjaW9uX3VzYSR5ZWFyKSAgICAgICAgICAgICAgICAgDQpwcmVkaWNjaW9uX3VzYSRjYXJfcHJvZHVjdGlvbiA8LSBhcy5udW1lcmljKHByZWRpY2Npb25fdXNhJGNhcl9wcm9kdWN0aW9uKQ0KcHJlZGljY2lvbl91c2EkY2FyX3NhbGVzPC1hcy5udW1lcmljKHByZWRpY2Npb25fdXNhJGNhcl9zYWxlcykNCnByZWRpY2Npb25fdXNhJGluZmxhdGlvbl9yYXRlPC1hcy5udW1lcmljKHByZWRpY2Npb25fdXNhJGluZmxhdGlvbl9yYXRlKQ0KcHJlZGljY2lvbl91c2EkdXNfdW5lbXBsb3ltZW50PC1hcy5udW1lcmljKHByZWRpY2Npb25fdXNhJHVzX3VuZW1wbG95bWVudCkNCnByZWRpY2Npb25fdXNhJHVzX2NvbnN1bWVyX2NvbmZpZGVuY2U8LWFzLm51bWVyaWMocHJlZGljY2lvbl91c2EkdXNfY29uc3VtZXJfY29uZmlkZW5jZSkNCnN1bW1hcnkocHJlZGljY2lvbl91c2EpDQpgYGANCg0KIyMjICoyLiBFeHBsaWNhY2nDs24gZGUgbGFzIHZhcmlhYmxlczoqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVmFyaWFibGVfdXNhPC1jKCJZZWFyIiwgIkNhciBQcm9kdWN0aW9uIiwgIkNhciBTYWxlcyIsICJJbmZsYXRpb24gcmF0ZSIsICJVUyBVbmVtcGxveWVtZW50IFJhdGUiLCAiVVMgQ29uc3VtZXIgQ29uZmlkZW5jZSIpDQpUaXBvX3ZhcmlhYmxlX3VzYSA8LSBjKCJFeHBsb3JhdG9yaWEiLCAiRGVwZW5kaWVudGUiLCAiRGVwZW5kaWVudGUiLCAiRXhwbG9yYXRvcmlhIiwgIkV4cGxvcmF0b3JpYSIsICJFeHBsb3JhdG9yaWEiKQ0KVW5pZGFkX01lZGljacOzbl91c2EgPC0gYygiQcOxb3MiLCJVbmlkYWRlcyIsICJVbmlkYWRlcyIsICIlIENhbWJpbyBkZSBsb3MgcHJlY2lvcyIsICIlIHRvdGFsIGRlIGRlc2VtcGxlYWRvcyIsICJQZXJzcGVjdGl2YSBlY29uw7NtaWNhIGRlbCBjb25zdW1pZG9yIikNCnRhYmxhMTM8LWRhdGEuZnJhbWUoVmFyaWFibGVfdXNhLFRpcG9fdmFyaWFibGVfdXNhLFVuaWRhZF9NZWRpY2nDs25fdXNhKQ0Ka25pdHI6OmthYmxlKHRhYmxhMTMpDQoNCmBgYA0KDQojIyMgKk1vZGVsbyBkZSBSZWdyZXNpw7NuIExpbmVhbCBNw7psdGlwbGUgMjoqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY29ycnBsb3QoY29yKHByZWRpY2Npb25fdXNhKSwgdHlwZT0idXBwZXIiLCBvcmRlcj0iaGNsdXN0IiwgYWRkY29lZi5jb2w9ImJsYWNrIikNCg0KbW9kZWxvX3JlZ3Jlc2lvbjIgPC0gbG0oY2FyX3NhbGVzfnllYXIraW5mbGF0aW9uX3JhdGUrdXNfdW5lbXBsb3ltZW50K3VzX2NvbnN1bWVyX2NvbmZpZGVuY2UsZGF0YT1wcmVkaWNjaW9uX3VzYSkgDQpzdW1tYXJ5KG1vZGVsb19yZWdyZXNpb24yKQ0KDQplZmZlY3RfcGxvdChtb2RlbG9fcmVncmVzaW9uMixwcmVkPXVzX3VuZW1wbG95bWVudCxpbnRlcnZhbD1UUlVFKQ0KYGBgDQoNCkVuIGVzdGUgY2FzbywgKipwb2RlbW9zIG9ic2VydmFyIHF1ZSBlbCBtb2RlbG8gc2kgZXMgY29uZmlhYmxlICh0ZW5pZW5kbyB1bmEgcjIgYWx0YSkgeSBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZjYXRpdm8gYWwgdGVuZXIgbGFzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyBkZSB5ZWFyIHkgZGVzZW1wbGVvIG5lZ2F0aXZhcywqKiBpbmRpY2FuZG8gcXVlICpzaSBlbCBkZXNlbXBsZW8gY3JlY2UgZW4gRVVBLCBsYXMgdmVudGFzIGRlIGF1dG9tb3ZpbGVzIGRpc21pbnVpcsOhbi4qIExhcyBkZW3DoXMgdmFyaWFibHMgdGFtYmnDqW4gdGllbmVuIHVuYSByZWxhY2nDs24gbmVnYXRpdmEsIHBvciBsbyBxdWUgc2kgbGEgdGF6YSBkZSBpbmZsYWNpw7NuIHkgbGEgY29uZmlhbnphIGRlbCBjb25zdW1pZG9yIGluY3JlbWVudGFuLCBsYXMgdmVudGFzIHBvZHLDrWFuIHZlcnNlIHVuIHBvY28gYWZlY3RhZGFzIHkgZGlzbWludWlyIHBvciBpZ3VhbC4NCg0KIyMgKipQcm9uw7NzdGljbyAxOiBWZWjDrWN1bG9zIGRlIG1vdG9yIHJlZ2lzdHJhZG9zIGVuIGNpcmN1bGFjacOzbiBlbiBNw6l4aWNvOioqDQojIyMgKjEuIExpbXBpZXphIGRlIGRhdG9zOioNCg0KIyMjIyAqKkltcG9ydGFyIGxhcyBiYXNlcyBkZSBkYXRvcyB5IGNhbWJpYXIgZWwgbm9tYnJlIGRlIGxhcyB2YXJpYWJsZXMgY29uIGxhIGZ1bmNpw7NuIGNsZWFuX25hbWVzOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYjEwIDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXG1hcmlhXFxEb2N1bWVudHNcXElURVNNIExBRVRcXFNlbWVzdHJlIDdcXFJldG9cXEZPUk0gQkFTRVMgREUgREFUT1NcXGVuY29kZWQtdmVoaWN1bG9zX2VuX2NpcmN1bGFjaW9uICgyKS5jc3YiKQ0KdmVoaWN1bG9zX2NpcmN1bGFjaW9uIDwtIGNsZWFuX25hbWVzKGIxMCkNCnN1bW1hcnkodmVoaWN1bG9zX2NpcmN1bGFjaW9uKQ0KYGBgDQoNCiMjIyMgKipFbGltaW5hciB2YXJpYWJsZXMgaW5uZWNlc2FyaWFzOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdmVoaWN1bG9zX2NpcmN1bGFjaW9uIDwtIHN1YnNldCh2ZWhpY3Vsb3NfY2lyY3VsYWNpb24sIHNlbGVjdCA9IC1jIChjYW1pb25lc195X2NhbWlvbmV0YXNfcGFyYV9jYXJnYSwgbW90b2NpY2xldGFzLCB4LCB4XzEpKQ0KYGBgDQoNCiMjIyMgKipFbGltaW5hciByZW5nbG9uZXMgaXJyZWxldmFudGVzOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdmVoaWN1bG9zX2NpcmN1bGFjaW9uIDwtIHZlaGljdWxvc19jaXJjdWxhY2lvblstYyg0Myw0NCw0NSw0Niw0NyksXQ0KYGBgDQoNCiMjIyMgKipDYW1iaWFyIGEgZm9ybWF0byBkZSBjYXJhY3RlciBhIG7Dum1lcm86KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp2ZWhpY3Vsb3NfY2lyY3VsYWNpb24kYW5vPC1hcy5udW1lcmljKHZlaGljdWxvc19jaXJjdWxhY2lvbiRhbm8pICAgICAgICAgICAgICAgICANCnZlaGljdWxvc19jaXJjdWxhY2lvbiR0b3RhbCA8LSBhcy5udW1lcmljKHZlaGljdWxvc19jaXJjdWxhY2lvbiR0b3RhbCkNCnZlaGljdWxvc19jaXJjdWxhY2lvbiRhdXRtb3ZpbGVzJGNhcl9zYWxlczwtYXMubnVtZXJpYyh2ZWhpY3Vsb3NfY2lyY3VsYWNpb24kYXV0b21vdmlsZXMpDQp2ZWhpY3Vsb3NfY2lyY3VsYWNpb24kY2FtaW9uZXNfcGFyYV9wYXNhamVyb3M8LWFzLm51bWVyaWModmVoaWN1bG9zX2NpcmN1bGFjaW9uJGNhbWlvbmVzX3BhcmFfcGFzYWplcm9zKQ0Kc3VtbWFyeSh2ZWhpY3Vsb3NfY2lyY3VsYWNpb24pDQpgYGANCg0KIyMjICoyLiBHcsOhZmljYSBwYXJhIGNvbXBhcmFyIGxvcyBhdXRvcyBlbiBjaXJjdWxhY2nDs24gdnMgdG9kb3MgbG9zIHZlaMOtY3Vsb3MqLg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3QodmVoaWN1bG9zX2NpcmN1bGFjaW9uJGFubyx2ZWhpY3Vsb3NfY2lyY3VsYWNpb24kdG90YWwsIHR5cGU9ImwiLGNvbD0iYmx1ZSIsIGx3ZD0xLjUsIHhsYWIgPSJBw7FvIix5bGFiID0iVW5pZGFkZXMiLCBtYWluID0gIlZlaGljdWxvcyBkZSBtb3RvciBlbiBjaXJjdWxhY2nDs24gcmVnaXN0cmFkb3MgYW51YWxtZW50ZSIpDQpsaW5lcyh2ZWhpY3Vsb3NfY2lyY3VsYWNpb24kYW5vLHZlaGljdWxvc19jaXJjdWxhY2lvbiRhdXRvbW92aWxlcyxjb2w9InJlZCIsbHR5PTMpDQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9YygiVG90YWwgZGUgdmVoaWN1bG9zIGVuIGNpcmN1bGFjacOzbiIsICJBdXRvbcOzdmlsZXMgZW4gY2lyY3VsYWNpw7NuIiksDQogICAgICAgY29sPWMoImJsdWUiLCAicmVkIiksIGx0eSA9IDE6MiwgY2V4PTAuOCkNCmBgYA0KUG9kZW1vcyBvYnNlcnZhciBxdWUgKipsYSB0ZW5kZW5jaWEgZXMgY2FzaSBpZ3VhbCwgaW5kaWNhbmRvIHF1ZSBsb3MgYXV0b3Mgc29uIGxhIGdyYW4gbWF5b3LDrWEgZGVsIHRvdGFsIGRlIHZlaMOtY3Vsb3MgZW4gY2lyY3VsYWNpw7NuLioqDQoNCiMjIyAqMy4gRm9yZWNhc3RpbmcgdXNhbmRvIGVsIE1vZGVsbyBhdXRvcmVncmVzaXZvOioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQphdXRvcmVncmVzc2l2ZV9tb2RlbCA8LSBhcm1hKHZlaGljdWxvc19jaXJjdWxhY2lvbiR0b3RhbCwgb3JkZXIgPSBjKDEsMCkpDQpzdW1tYXJ5KGF1dG9yZWdyZXNzaXZlX21vZGVsIDwtIGFybWEodmVoaWN1bG9zX2NpcmN1bGFjaW9uJHRvdGFsLCBvcmRlciA9IGMoMSwwKSkpDQoNCmF1dG9yZWdyZXNzaXZlX21vZGVsX2ZvcmVjYXN0PC1mb3JlY2FzdChhdXRvcmVncmVzc2l2ZV9tb2RlbCRmaXR0ZWQsaD01LGxldmVsPWMoOTUpKQ0KYXV0b3JlZ3Jlc3NpdmVfbW9kZWxfZm9yZWNhc3QNCg0KcGxvdChhdXRvcmVncmVzc2l2ZV9tb2RlbF9mb3JlY2FzdCkNCmBgYA0KDQpFbiBlc3RlIHByaW1lciBtb2RlbG8gYXV0b3JlZ3Jlc2l2byBwb2RlbW9zIG9ic2VydmFyIHVuYSAqKnRlbmRlbmNpYSBwb3NpdGl2YSBhbCBwcm9ub3N0aWNhciBsYSBjaXJjdWxhY2nDs24gZGUgYXV0b23Ds3ZpbGVzIGVuIE3DqXhpY28gZW4gbG9zIHByw7N4aW1vcyBhw7FvcyoqLCBwb3IgZWplbXBsbywgZW4gZWwgc2lndWllbnRlIGHDsW8gKipzZSBlc3BlcmFuIDM3LDg4NCwyODEgdmVow61jdWxvcyBlbiBjaXJjdWxhY2nDs24gY29uIHVuIDk1JSBkZSBjb25maWFuemEqKiwgeSBsb3MgcmFuZ29zIHB1ZWRlbiBpciBkZSAzNSwyNDAsNDA2IGhhc3RhIDQwLDUyOCwxNTUuDQoNCkVuIGVsIHNlZ3VuZG8gYcOxbywgc2UgcHJvbm9zdGljYW4gMzksMjIyLDA4NywgZGUgaWd1YWwgbWFuZXJhIGNvbiB1biA5NSUgZGUgY29uZmlhbnphLCB5IHBvciDDumx0aW1vLCBlbiBlbCB0ZXJjZXIgYcOxbywgc2UgcHJldmVlbiA0MCw1NTksODkzIHZlaMOtY3Vsb3MgZW4gY2lyY3VsYWNpw7NuLg0KDQpFbCBpbmNyZW1lbnRvIGRlIHZlaMOtY3Vsb3MgZW4gY2lyY3VsYWNpw7NuIGVuIE3DqXhpY28gZXMgdW4gZGF0byBxdWUgcHVlZGUgc2VyIGJlbmVmaWNpb3NvIHBhcmEgRk9STSwgeWEgcXVlICoqYWwgaGFiZXIgbcOhcyB2ZWjDrWN1bG9zIGVuIGNpcmN1bGFjacOzbiwgc2UgaW1wbGljYSB1bmEgbWF5b3IgcHJvZHVjY2nDs24gZGUgbG9zIG1pc21vcywgeSBwb3IgZW5kZSwgbGFzIGVtcHJlc2FzIGF1dG9tb3RyaWNlcyBuZWNlc2l0YXLDoW4gbcOhcyBkZSBsb3Mgc2VydmljaW9zIGRlIGVtcGFxdWUqKiBvZnJlY2lkb3MgcG9yIEZPUk0uDQoNCiMjICoqUHJvbsOzc3RpY28gMjogU2FsZXMgb2YgdmVoaWNsZXMgaW4gVVNBICh1bml0cykqKg0KDQojIyMgKjEuIExpbXBpZXphIGRlIGRhdG9zOioNCiMjIyMgKipJbXBvcnRhciBsYXMgYmFzZXMgZGUgZGF0b3MgeSBjYW1iaWFyIGVsIG5vbWJyZSBkZSBsYXMgdmFyaWFibGVzIGNvbiBsYSBmdW5jacOzbiBjbGVhbl9uYW1lczoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmIxMiA8LSByZWFkLmNzdigiQzpcXFVzZXJzXFxtYXJpYVxcRG9jdW1lbnRzXFxJVEVTTSBMQUVUXFxTZW1lc3RyZSA3XFxSZXRvXFxFVkkyXFx2ZWhpY2xlX3NhbGVzX3VzYS5jc3YiKQ0KdmVoaWNsZV9zYWxlcyA8LSBjbGVhbl9uYW1lcyhiMTIpDQpzdW1tYXJ5KHZlaGljbGVfc2FsZXMpDQpgYGANCg0KIyMjIyAqKlTDqWNuaWNhIDEuIEVsaW1pbmFyIGNvbHVtbmFzIGlubmVjZXNhcmlhczoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnZlaGljbGVfc2FsZXMgPC0gc3Vic2V0KHZlaGljbGVfc2FsZXMsIHNlbGVjdCA9IC1jICh4LCB4XzEsIHhfMiwgeF8zLCB4XzQpKQ0Kc3VtbWFyeSh2ZWhpY2xlX3NhbGVzKQ0KYGBgDQoNCiMjIyMgKipUw6ljbmljYSAyLiBDYW1iaWFyIGEgZm9ybWF0byBkZSBjYXJhY3RlciBhIG7Dum1lcm86KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp2ZWhpY2xlX3NhbGVzJHllYXJzPC1hcy5udW1lcmljKHZlaGljbGVfc2FsZXMkeWVhcnMpICAgICAgICAgICAgICAgICANCnZlaGljbGVfc2FsZXMkbmV3X3Bhc3Nhbmdlcl9jYXJfc2FsZXMgPC0gYXMubnVtZXJpYyh2ZWhpY2xlX3NhbGVzJG5ld19wYXNzYW5nZXJfY2FyX3NhbGVzKQ0KdmVoaWNsZV9zYWxlcyRuZXdfbGlnaHRfdHJ1Y2tfc2FsZXM8LWFzLm51bWVyaWModmVoaWNsZV9zYWxlcyRuZXdfbGlnaHRfdHJ1Y2tfc2FsZXMpDQp2ZWhpY2xlX3NhbGVzJHVzZWRfdmVoaWNsZV9zYWxlczwtYXMubnVtZXJpYyh2ZWhpY2xlX3NhbGVzJHVzZWRfdmVoaWNsZV9zYWxlcykNCnZlaGljbGVfc2FsZXMkdG90YWw8LWFzLm51bWVyaWModmVoaWNsZV9zYWxlcyR0b3RhbCkNCnN1bW1hcnkodmVoaWNsZV9zYWxlcykNCmBgYA0KDQojIyMgKjIuIEdyw6FmaWNhIHBhcmEgY29tcGFyYXIgbGEgdmVudGEgZGUgdmVow61jdWxvcyB1c2Fkb3MgdnMgdG9kb3MgbG9zIHZlaMOtY3Vsb3MgdmVuZGlkb3M6Kg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3QodmVoaWNsZV9zYWxlcyR5ZWFycyx2ZWhpY2xlX3NhbGVzJHRvdGFsLCB0eXBlPSJsIixjb2w9ImJsdWUiLCBsd2Q9MS41LCB4bGFiID0iQcOxbyIseWxhYiA9IlVuaWRhZGVzIiwgbWFpbiA9ICJWZW50YSBkZSB2ZWjDrWN1bG9zIGVuIEVzdGFkb3MgVW5pZG9zIikNCmxpbmVzKHZlaGljbGVfc2FsZXMkeWVhcnMsdmVoaWNsZV9zYWxlcyR1c2VkX3ZlaGljbGVfc2FsZXMsY29sPSJyZWQiLGx0eT0zKQ0KbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kPWMoIlRvdGFsIGRlIHZlaGljdWxvcyBlbiBjaXJjdWxhY2nDs24iLCAiVmVudGEgZGUgdmVoaWN1bG9zIHVzYWRvcyIpLA0KICAgICAgIGNvbD1jKCJibHVlIiwgInJlZCIpLCBsdHkgPSAxOjIsIGNleD0wLjYpDQpgYGANCg0KIyMjICozLiBGb3JlY2FzdGluZyB1c2FuZG8gZWwgTW92aW5nIEF2ZXJhZ2UgTW9kZWw6Kg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1hbV9zYWxlcyA8LSBhcm1hKHZlaGljbGVfc2FsZXMkdG90YWwsb3JkZXIgPSBjKDEsMSkpDQpzdW1tYXJ5KG1hbV9zYWxlcyA8LSBhcm1hKHZlaGljbGVfc2FsZXMkdG90YWwsb3JkZXIgPSBjKDEsMSkpKQ0KDQptYW1fc2FsZXNfZm9yZWNhc3QgPC0gZm9yZWNhc3QobWFtX3NhbGVzJGZpdHRlZCxoPTUsbGV2ZWw9Yyg5NSkpDQptYW1fc2FsZXNfZm9yZWNhc3QNCg0KcGxvdChtYW1fc2FsZXNfZm9yZWNhc3QpDQpgYGANCg0KQ29uIGVzdGEgdMOpY25pY2EgcG9kZW1vcyB2ZXIgcXVlICoqZW4gRXN0YWRvcyBVbmlkb3Mgc2UgcHJvbm9zdGljYSBwYXJhIGxvcyBwcsOzeGltb3MgYcOxb3MgbGEgbWlzbWEgY2FudGlkYWQgZGUgdW5pZGFkZXMgZGUgdmVow61jdWxvcyB2ZW5kaWRhcywgc2llbmRvIGVzdGEgZGUgNDgsNjY1LDI1NyB1bmlkYWRlcyBlbiBsb3MgcHLDs3hpbW9zIDUgYcOxb3MuKioNCg0KQ29uIGVzdGEgaW5mb3JtYWNpw7NuIHBvZGVtb3MgZGVjaXIgcXVlIGJpZW4sIGFsIEZPUk0gcXVlcmVyIGVudHJhciBhIGxhIGluZHVzdHJpYSBhdXRvbW90cml6IGVuIEVzdGFkb3MgVW5pZG9zIHNpIHBvZHLDrWEgZ2VuZXJhciB1bmEgdmVudGFqYSwgc2luIGVtYmFyZ28gcGFyYSBsb3MgcHLDs3hpbW9zIGHDsW9zLCAqcHVlZGUgcXVlIGxhIGluZHVzdHJpYSBhdXRvbW90cml6IG5vIHRlbmdhIHVuIGNyZWNpbWllbnRvIHNpZ25pZmNhdGl2byBkZSB2ZW50YXMgZGUgY2Fycm9zIHkgcG9yIGVuZGUgcHJvZHVjY2nDs24gZGUgbG9zIG1pc21vcy4qDQoNCiMjICoqUHJvbsOzc3RpY28gMzogRGVzZW1wZcOxbyBkZWwgw6FyZWEgZGUgRGVsaXZlcnkgUGVyZm9ybWFuY2U6KioNCiMjIyAqMS4gTGltcGllemEgZGUgZGF0b3M6Kg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgZmlsZS5jaG9vc2UoKQ0KYjExIDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXG1hcmlhXFxEb2N1bWVudHNcXHBlcmZvcm1hbmNlX2JkX2xpbXBpYS5jc3YiKQ0KcGVyZl9wcm9ub3N0aWNvcyA8LSBjbGVhbl9uYW1lcyhiMTEpDQpzdW1tYXJ5KHBlcmZfcHJvbm9zdGljb3MpDQpgYGANCg0KIyMjICoyLiBGb3JlY2FzdGluZyB1c2FuZG8gZWwgTW9kZWxvIEF1dG9yZWdyZXNpdm86Kg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmF1dG9yZWdyZXNzaXZlX21vZGVsX3BlcmZvcm1hbmNlIDwtIGFybWEocGVyZl9wcm9ub3N0aWNvcyRyZXRyYXNvLCBvcmRlciA9IGMoMSwwKSkNCnN1bW1hcnkoYXV0b3JlZ3Jlc3NpdmVfbW9kZWxfcGVyZm9ybWFuY2UgPC0gYXJtYShwZXJmX3Byb25vc3RpY29zJHJldHJhc28sIG9yZGVyID0gYygxLDApKSkNCg0KYXV0b3JlZ3Jlc3NpdmVfbW9kZWxfcGVyZm9ybWFuY2VfZm9yZWNhc3Q8LWZvcmVjYXN0KGF1dG9yZWdyZXNzaXZlX21vZGVsX3BlcmZvcm1hbmNlJGZpdHRlZCxoPTUsbGV2ZWw9Yyg5NSkpDQpzdW1tYXJ5KGF1dG9yZWdyZXNzaXZlX21vZGVsX3BlcmZvcm1hbmNlX2ZvcmVjYXN0KQ0KDQpnZ3Bsb3QocGVyZl9wcm9ub3N0aWNvcyxhZXMoeD1mZWNoYSx5PXJldHJhc28pKSsgDQogIGdlb21fcG9pbnQoc2l6ZT0yLHNoYXBlPTIzKQ0KDQpwbG90KGF1dG9yZWdyZXNzaXZlX21vZGVsX3BlcmZvcm1hbmNlX2ZvcmVjYXN0KQ0KYGBgDQoNCkNvbiBlc3RlIG1vZGVsbyBhdXRvcmVncmVzaXZvLCAqKnNlIHByb25vc3RpY2EgY29uIHVuIDk1JSBkZSBjb25maWFuemEgZW4gbG9zIHByw7N4aW1vcyBhw7FvcyB1biB0aWVtcG8gZGUgcmV0cmFzbyBsaW5lYWwqKiwgZXMgZGVjaXIsIG5vIHNlIHB1ZWRlIG9ic2VydmFyIHVuYSB0ZW5kZW5jaWEgbmkgbmVnYXRpdmEgbyBwb3NpdGl2YSBjb21vIHBvZGVtb3MgdmVyIGVuIGxhIGdyw6FmaWNhIGRlIGRpc3BlcnNpw7NuLCAqKmFycm9qYW5kbyBhc8OtIHVuIHJldHJhc28gZGUgZW50cmVnYSBkZSAyLjc3MyBtaW51dG9zIGVuIGxvcyBwcsOzeGltb3MgYcOxb3MuKioNCg0KRXN0ZSBwcm9uw7NzdGljbyBlcyBidWVubyBwYXJhIEZPUk0sIHB1ZXMgKmluZGljYSBxdWUgc3UgdGllbXBvIGRlIGVudHJlZ2Egbm8gaW5jZXJlbWVudGFyw6EgZW4gYXRyYXNvcyosIHNpbiBlbWJhcmdvLCBwdWVkZSBjcmVhciBlc3RhcnRlZ2lhcyBwYXJhIGNhbWJpYXIgZXN0ZSBwcm9uw7NzdGljbyB5IGxvZ3JhciBxdWUgc2UgZGlzbWludXlhIGxvIG3DoXMgcG9zaWJsZSBlbiB1biBmdXR1cm8uDQoNCiMjICoqUHJvbsOzc3RpY28gNC4gRGVzZW1wZcOxbyBkZWwgw6FyZWEgZGUgU2NyYXA6KioNCiMjIyAqMS4gTGltcGllemEgZGUgZGF0b3M6Kg0KDQojIyMjICoqSW1wb3J0YXIgbGEgYmFzZXMgZGUgZGF0b3MgeSBjYW1iaWFyIGVsIG5vbWJyZSBkZSBsYXMgdmFyaWFibGVzIGNvbiBsYSBmdW5jacOzbiBjbGVhbl9uYW1lczoqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgZmlsZS5jaG9vc2UoKQ0KYjEzIDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXG1hcmlhXFxEb2N1bWVudHNcXElURVNNIExBRVRcXFNlbWVzdHJlIDdcXFJldG9cXEVWSTJcXHNjcmFwX2JkX2xpbXBpYS5jc3YiKQ0Kc2NyYXBfcHJvbm9zdGljb3MgPC0gY2xlYW5fbmFtZXMoYjEzKQ0Kc3VtbWFyeShzY3JhcF9wcm9ub3N0aWNvcykNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMS4gQ2FtYmlhciBmb3JtYXRvIGRlIGxhcyB2YXJpYWJsZXM6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzY3JhcF9wcm9ub3N0aWNvcyRmZWNoYSA8LSBhcy5EYXRlKHNjcmFwX3Byb25vc3RpY29zJGZlY2hhLCBmb3JtYXQgPSAiJWQvJW0vJVkiKQ0Kc2NyYXBfcHJvbm9zdGljb3MkdWJpY2FjaW9uX2RlX29yaWdlbiA8LSBhcy5mYWN0b3Ioc2NyYXBfcHJvbm9zdGljb3MkdWJpY2FjaW9uX2RlX29yaWdlbikNCnNjcmFwX3Byb25vc3RpY29zJGNhbnRpZGFkIDwtIGFzLm51bWVyaWMoc2NyYXBfcHJvbm9zdGljb3MkY2FudGlkYWQpDQpzdW1tYXJ5KHNjcmFwX3Byb25vc3RpY29zKQ0KYGBgDQoNCiMjIyAqMi4gRm9yZWNhc3RpbmcgdXNhbmRvIE1vdmluZyBBdmVyYWdlIE1vZGVsOioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptYW1fc2NyYXAgPC0gYXJtYShzY3JhcF9wcm9ub3N0aWNvcyRjYW50aWRhZCxvcmRlciA9IGMoMSwxKSkNCnN1bW1hcnkobWFtX3NjcmFwIDwtIGFybWEoc2NyYXBfcHJvbm9zdGljb3MkY2FudGlkYWQsb3JkZXIgPSBjKDEsMSkpKQ0KDQptYW1fc2NyYXBfZm9yZWNhc3QgPC0gZm9yZWNhc3QobWFtX3NjcmFwJGZpdHRlZCxoPTUsbGV2ZWw9Yyg5NSkpDQptYW1fc2NyYXBfZm9yZWNhc3QNCg0KcGxvdChtYW1fc2NyYXBfZm9yZWNhc3QpDQpgYGANCg0KRW4gZWwgY2FzbyBkZSBlc3RlIG1vZGVsbyBNQU0sICoqc2UgcHJvbm9zdGljYSBjb24gdW4gOTUlIGRlIGNvbmZpYW56YSBxdWUgaGFicsOhIHVuIGF1bWVudG8gZW4gbGEgcHJvZHVjY2nDs24gZGUgc2NyYXAgZGVudHJvIGRlIGxvcyBwcm9jZXNvcyBkZSBGT1JNKiosIGhhYmllbmRvIGFzw60gdW4gKnByb27Ds3N0aWNvIGRlIDIuODc3IGtpbG9zIHBhcmEgZWwgc2lndWllbnRlIGHDsW8sIHNlZ3VpZG8gcG9yIDMuMDc5IGtnIHBhcmEgZWwgc2lndWllbnRlIHBlcmlvZG8geSBkZXNwdWVzIGRlIDMuMjgwIGtnLiogRXN0ZSBjcmVjaW1pZW50byBubyBlcyBwYXJhIG5hZGEgZHLDoXN0aWNvLCBzaW4gZW1iYXJnbywgYXVucXVlIGVsIGNyZWNpbWllbnRvIGRlIGxvcyBkZXNwZXJkaWNpb3Mgbm8gZXMgYWxnbyBxdWUgYnVzY2EgRk9STSwgZXN0ZSBwdWVkZSBpbmRpY2FyIHF1ZSBsYSBwcm9kdWNjacOzbiB0YW1iacOpbiBlc3RhcsOhIGNyZWNpZW5kbywgcHVlcyBlbCBzY3JhcCBzZSBwcm9kdWNlIGVuIHN1IG1heW9yw61hIGVuIGxhIGV0YXBhIGRlIHByZS1wcm9kdWNjacOzbi4NCg0KRW4gbGEgZ3LDoWZpY2EgIHBvZGVtb3MgdmVyIHVuICoqY29tcG9ydGFtaWVudG8gcG9zaXRpdm8qKiwgZGVtb3N0cmFuZG8gYXPDrSBlbCBwcm9uw7NzdGljbyBkZSBjcmVjaW1pZW50byBtdXkgbGV2ZSBwYXJhIGxvcyBzaWd1aWVudGVzIGHDsW9zLg0KDQpFbiBlc3RlIGNhc28sIEZPUk0gZGViZXLDoSBjcmVhciBlc3RyYXRlZ2lhcyBwYXJhICpldml0YXIgZWwgY3JlY2ltaWVudG8gZGUgc3Ugc2NyYXAgeSBhcHJvdmVjaGFyIGFsIG3DoXhpbW8gc3UgbWF0ZXJpYSBwcmltYS4qDQoNCg0KIyAqKlNlY2Npw7NuIDMqKg0KIyMgKipFc3RpbWFjacOzbiBkZSBLLU1lYW5zIENsdXN0ZXJpbmc6KioNCg0KIyMjIyAqKlBhc28gMS4gSW1wb3J0YXIgeSBsaW1waWFyIGxhIGJhc2UgZGUgZGF0b3M6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpiYWphcyA8LSByZWFkLmNzdigiQzpcXFVzZXJzXFxtYXJpYVxcRG9jdW1lbnRzXFxJVEVTTSBMQUVUXFxTZW1lc3RyZSA3XFxSZXRvXFxFVkkyXFxGT1JNIC0gUmVjdXJzb3MgSHVtYW5vcyAtIEJBSkFTIGV2aWRlbmNpYSkuY3N2IikNCmJhamFzIDwtIGNsZWFuX25hbWVzKGJhamFzKQ0Kc3VtbWFyeShiYWphcykNCg0KI1TDqWNuaWNhIDE6DQojQm9ycmFyIGNvbHVtbmFzLg0KYmFqYXMgPC0gc3Vic2V0KGJhamFzLCBzZWxlY3QgPSAtYyhhcGVsbGlkb3MsIG5vbWJyZSwgZmVjaGFfZGVfbmFjaW1pZW50byxyZmMsIGZlY2hhX2RlX2FsdGEsIGJhamEsIGRlcGFydGFtZW50bywgbm9fc2VndXJvX3NvY2lhbCwgZmFjdG9yX2NyZWRfaW5mb25hdml0LCBuX2NyZWRpdG9faW5mb25hdml0LCBsdWdhcl9kZV9uYWNpbWllbnRvLCBjdXJwLCBjYWxsZSwgbnVtZXJvX2ludGVybm8sIGNvbG9uaWEsIGNvZGlnb19wb3N0YWwsIG11bmljaXBpbywgZXN0YWRvLCB0YXJqZXRhX2N1ZW50YSkpDQoNCiNUw6ljbmljYSAyOiANCiNSZWVtcGxlemFyIE5BcyBjb24gZWwgcHJvbWVkaW8gZW4gbGEgY29sdW1uYSBkZSAiRWRhZCIsICJTYWxhcmlvIERpYXJpbyIgeSAiTsO6bWVybyBkZSBkw61hcyIuDQpiYWphcyRlZGFkW2lzLm5hKGJhamFzJGVkYWQpXTwtbWVhbihiYWphcyRlZGFkLCBuYS5ybSA9IFRSVUUpDQpiYWphcyRzYWxhcmlvX2RpYXJpb19pbXNzW2lzLm5hKGJhamFzJHNhbGFyaW9fZGlhcmlvX2ltc3MpXTwtbWVhbihiYWphcyRzYWxhcmlvX2RpYXJpb19pbXNzLCBuYS5ybSA9IFRSVUUpDQpiYWphcyRub19kaWFzW2lzLm5hKGJhamFzJG5vX2RpYXMpXTwtbWVhbihiYWphcyRub19kaWFzLCBuYS5ybSA9IFRSVUUpDQpzdW1tYXJ5IChiYWphcykNCg0KI1TDqWNuaWNhIDM6DQojQ29udmVydGlyIGxhcyB2YXJpYWJsZXMgY29tbyBmYWN0b3IgbyBuw7ptZXJvDQpiYWphcyRlZGFkPC1hcy5udW1lcmljKGJhamFzJGVkYWQpDQpiYWphcyRnZW5lcm88LWFzLmZhY3RvcihiYWphcyRnZW5lcm8pDQpiYWphcyRtb3Rpdm9fZGVfYmFqYTwtYXMuZmFjdG9yKGJhamFzJG1vdGl2b19kZV9iYWphKQ0KYmFqYXMkbm9fZGlhczwtYXMubnVtZXJpYyhiYWphcyRub19kaWFzKQ0KYmFqYXMkcHVlc3RvPC1hcy5mYWN0b3IoYmFqYXMkcHVlc3RvKQ0KYmFqYXMkc2FsYXJpb19kaWFyaW9faW1zczwtYXMubnVtZXJpYyhiYWphcyRzYWxhcmlvX2RpYXJpb19pbXNzKQ0KYmFqYXMkZXN0YWRvX2NpdmlsPC1hcy5mYWN0b3IoYmFqYXMkZXN0YWRvX2NpdmlsKQ0Kc3VtbWFyeSAoYmFqYXMpDQoNCiNFeHBvcnRhciBiYXNlIGRlIGRhdG9zIGxpbXBpYQ0Kd3JpdGUuY3N2KGJhamFzLCBmaWxlPSJiYWphc19iZF9saW1waWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQoNCmBgYA0KDQojIyMjICoqUGFzbyAyLiBDcmVhY2nDs24gZGUgQ2zDunN0ZXJzOioqDQojIyMgKkNsdXN0ZXIgMTogUmVsYWNpw7NuIGRlIHZhcmlhYmxlcyBkZSBFZGFkIHkgTsO6bWVybyBkZSBkw61hcy4qDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZWRhZCA8LWJhamFzIA0KZWRhZDwtIHN1YnNldChiYWphcyxzZWxlY3QgPSAtYyhnZW5lcm8sIG1vdGl2b19kZV9iYWphLCBwdWVzdG8sIHNhbGFyaW9fZGlhcmlvX2ltc3MsIGVzdGFkb19jaXZpbCkpDQoNCiNOb3JtYWxpemFyIHZhcmlhYmxlcw0KZWRhZF9ub3JtPC1zY2FsZShlZGFkWzE6Ml0pIA0KDQojRnVuY2nDs24gZnZpeiBwYXJhIGxhIHZpc3VhbGl6YWNpw7NuIGRlIHVuIEVsYm93IFBsb3QgeSBhc8OtIGRldGVybWluYXIgZWwgbsO6bWVybyBkZSBjbHVzdGVycy4NCmZ2aXpfbmJjbHVzdChlZGFkX25vcm0sIGttZWFucywgbWV0aG9kPSJ3c3MiKSsgDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD00LCBsaW5ldHlwZT0yKSsgICAgICAgICAgICANCiAgbGFicyhzdWJ0aXRsZSA9ICJFbGJvdyBtZXRob2QiKQ0KDQpgYGANCg0KQ29uIGVzdGEgZ3LDoWZpY2EgcG9kZW1vcyB2aXN1YWxpemFyIHF1ZSBlbCAqKm7Dum1lcm8gw7NwdGltbyBkZSBjbMO6c3RlcnMgc29uIDQgKG9wdGltaXphY2nDs24pLioqDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNWaXN1YWxpemFyIGNsdXN0ZXJzDQplZGFkX2NsdXN0ZXI8LWttZWFucyhlZGFkX25vcm0sNCkNCmVkYWRfY2x1c3Rlcg0KDQojVmlzdWFsaXphciByZXN1bHRhZG9zOg0KZnZpel9jbHVzdGVyKGVkYWRfY2x1c3RlcixkYXRhPWVkYWRfbm9ybSkNCmBgYA0KDQpMb3MgKiptw6FzIGrDs3ZlbmVzIHNvbiBsb3MgcXVlIG1lbm9zIGTDrWFzIGRlIHRyYWJham8gbGFib3Jhbi4qKiBBc8OtIGNvbW8gcXVlICoqbG9zIHF1ZSBtw6FzIGTDrWFzIGxhYm9yYWxlcyB0aWVuZW4sIHNvbiBsb3MgYWR1bHRvcyoqIGRlc2RlIGxvcyAyNyBhw7FvcyBoYXN0YSBsb3MgNjEuDQoNCiMjIyAqQ2x1c3RlciAyOiBSZWxhY2nDs24gZGUgdmFyaWFibGVzIGRlIEVkYWQgeSBTYWxhcmlvIERpYXJpby4qDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZWRhZF9zYWxhcmlvIDwtYmFqYXMgDQplZGFkX3NhbGFyaW88LSBzdWJzZXQoYmFqYXMsc2VsZWN0ID0gLWMoZ2VuZXJvLGVzdGFkb19jaXZpbCxtb3Rpdm9fZGVfYmFqYSwgcHVlc3RvLCBub19kaWFzKSkNCg0KI05vcm1hbGl6YXIgdmFyaWFibGVzDQplZGFkX3NhbGFyaW9fbm9ybTwtc2NhbGUoZWRhZF9zYWxhcmlvWzE6Ml0pIA0KZWRhZF9zYWxhcmlvX25vcm0gPC0gbmEub21pdChlZGFkX3NhbGFyaW9fbm9ybSkNCiAgICAgc3VtbWFyeShlZGFkX3NhbGFyaW9fbm9ybSkNCg0KI0Z1bmNpw7NuIGZ2aXogcGFyYSBsYSB2aXN1YWxpemFjacOzbiBkZSB1biBFbGJvdyBQbG90IHkgYXPDrSBkZXRlcm1pbmFyIGVsIG7Dum1lcm8gZGUgY2x1c3RlcnMuDQpmdml6X25iY2x1c3QoZWRhZF9zYWxhcmlvX25vcm0sIGttZWFucywgbWV0aG9kPSJ3c3MiKSsgDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD00LCBsaW5ldHlwZT0yKSsgICAgICAgICAgICANCiAgbGFicyhzdWJ0aXRsZSA9ICJFbGJvdyBtZXRob2QiKSANCmBgYA0KDQpDb24gZXN0YSBncsOhZmljYSBwb2RlbW9zIHZpc3VhbGl6YXIgcXVlIGVsICoqbsO6bWVybyDDs3B0aW1vIGRlIGNsw7pzdGVycyBzb24gNCAob3B0aW1pemFjacOzbikuKioNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNWaXN1YWxpemFyIGNsdXN0ZXJzOg0KZWRhZF9zYWxhcmlvX2NsdXN0ZXI8LWttZWFucyhlZGFkX3NhbGFyaW9fbm9ybSw0KQ0KZWRhZF9zYWxhcmlvX2NsdXN0ZXINCg0KI1Zpc3VhbGl6YXIgcmVzdWx0YWRvczoNCmZ2aXpfY2x1c3RlcihlZGFkX3NhbGFyaW9fY2x1c3RlcixkYXRhPWVkYWRfc2FsYXJpb19ub3JtKQ0KYGBgDQoNCkxhICoqZWRhZCBubyB0aWVuZSBtdWNoYSBpbmZsdWVuY2lhIGVuIGVsIHNhbGFyaW8uKiogUHVlcyBsb3MgY29sYWJvcmFkb3JlcyBxdWUgc2UgaGFuIGRhZG8gZGUgYmFqYSB0aWVuZW4gdW4gc2FsYXJpbyBtdXkgc2ltaWxhciBpbmRlcGVuZGllbnRlbWVudGUgZGUgc3UgZWRhZC4NCg0KIyMjICpDbHVzdGVyIDM6IFJlbGFjacOzbiBkZSB2YXJpYWJsZXMgZGUgU2FsYXJpbyBEaWFyaW8geSBOw7ptZXJvIGRlIGTDrWFzLioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkaWFzX3NhbGFyaW8gPC1iYWphcyANCmRpYXNfc2FsYXJpbzwtIHN1YnNldChiYWphcyxzZWxlY3QgPSAtYyhnZW5lcm8sZXN0YWRvX2NpdmlsLG1vdGl2b19kZV9iYWphLCBwdWVzdG8sIGVkYWQpKQ0KDQojTm9ybWFsaXphciB2YXJpYWJsZXM6DQpkaWFzX3NhbGFyaW9fbm9ybTwtc2NhbGUoZGlhc19zYWxhcmlvWzE6Ml0pIA0KDQojRnVuY2nDs24gZnZpeiBwYXJhIGxhIHZpc3VhbGl6YWNpw7NuIGRlIHVuIEVsYm93IFBsb3QgeSBhc8OtIGRldGVybWluYXIgZWwgbsO6bWVybyBkZSBjbHVzdGVycy4NCmZ2aXpfbmJjbHVzdChkaWFzX3NhbGFyaW9fbm9ybSwga21lYW5zLCBtZXRob2Q9IndzcyIpKyANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTQsIGxpbmV0eXBlPTIpKyAgICAgICAgICAgIA0KICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpIA0KDQpgYGANCg0KQ29uIGVzdGEgZ3LDoWZpY2EgcG9kZW1vcyB2aXN1YWxpemFyIHF1ZSBlbCAqKm7Dum1lcm8gw7NwdGltbyBkZSBjbMO6c3RlcnMgc29uIDQgKG9wdGltaXphY2nDs24pLioqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojVmlzdWFsaXphciBjbHVzdGVyczoNCmRpYXNfc2FsYXJpb19jbHVzdGVyPC1rbWVhbnMoZGlhc19zYWxhcmlvX25vcm0sNCkNCmRpYXNfc2FsYXJpb19jbHVzdGVyDQoNCiNWaXN1YWxpemFyIHJlc3VsdGFkb3M6DQpmdml6X2NsdXN0ZXIoZGlhc19zYWxhcmlvX2NsdXN0ZXIsZGF0YT1kaWFzX3NhbGFyaW9fbm9ybSkNCg0KcHJvbWVkaW9fZGlhcyA8LSBtZWFuKGJhamFzJG5vX2RpYXMpDQpwcm9tZWRpb19kaWFzDQpgYGANCg0KRW4gZXN0ZSBjbMO6c3RlciBwb2RlbW9zIG9ic2VydmFyIHF1ZSBwb3IgbcOhcyBhbnRpZ8O8ZWRhZCBvIGTDrWFzIHF1ZSBsYWJvcmVzIGVuIEZPUk0sICoqbm8gaGF5IG11Y2hhIHBvc2liaWxpZGFkIGRlIGNyZWNpbWllbnRvIGxhYm9yYWwgKGVjb27Ds21pY2FtZW50ZSBoYWJsYW5kbykqKiwgcHVlcyBzZWfDum4gZWwgY2x1c3RlciByZWFsaXphZG8sICoqbm8gaW5mbHV5ZW4gbG9zIGTDrWFzIGxhYm9yYWRvcyBjb24gZWwgc2FsYXJpbyBkaWFyaW8uKioNCg0KIyMjICpDcmVhY2nDs24gZGUgU2VnbWVudG9zIGFwYXJ0aXIgZGUgbG9zIENsw7pzdGVyczoqDQoqRGVjaWRpbW9zIHVzYXIgZWwgQ2x1c3RlciAyIHBhcmEgZ2VuZXJhciBsYSBjbGFzaWZpY2FjacOzbiBkZSB2YXJpYWJsZXMgeSBwb2RlciBjb21wYXJhciBjb24gZGF0b3MgY3VhbGl0YXRpdm9zLioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNBw7FhZGlyIGEgbGEgYmFzZSBkZSBkYXRvcyBsYSBjb2x1bW5hIGRlIENsdXN0ZXJzIHkgc3UgY2xhc2lmaWNhY2nDs246DQpiYWphczE8LWJhamFzDQoNCiNFbGltaW5hciByZW5nbG9uZXMgcXVlIHRlbmdhbiBuw7ptZXJvIGRlIGTDrWFzIGVuIDAgcGFyYSBxdWUgbGFzIGJhc2VzIGRlIGRhdG9zIHRlbmdhbiBsb3MgbWlzbW9zIHJlZ2lzdHJvcw0KYmFqYXMxJENsdXN0ZXJzIDwtIGVkYWRfc2FsYXJpb19jbHVzdGVyJGNsdXN0ZXINCnN0cihiYWphcykNCg0KI0lkZW50aWZpY2Ftb3MgbGEgY2xhc2lmaWNhY2nDs24gZGUgbGFzIGRpc3RpbnRhcyBlZGFkZXMgZGUgbG9zIGNvbGFib3JhZG9yZXMuDQpiYWphczI8LWJhamFzMSAlPiUgZ3JvdXBfYnkoQ2x1c3RlcnMpICU+JSBkcGx5cjo6c3VtbWFyaXNlKGVkYWQ9bWF4KGVkYWQpKSAlPiUgYXJyYW5nZShkZXNjKGVkYWQpKQ0KYmFqYXMxJENsdXN0ZXJfTmFtZXM8LWZhY3RvcihiYWphczEkQ2x1c3RlcnMsbGV2ZWxzID0gYygxLDIsMyw0KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiT3V0bGllciIsICJKb3ZlbiIsICJBdmFuemFkYSIsICJBZHVsdGEiKSkNCmJhamFzMyA8LSBiYWphczEgJT4lIGdyb3VwX2J5KENsdXN0ZXJfTmFtZXMpICU+JSBkcGx5cjo6c3VtbWFyaXplKGVkYWRfYcOxb3M9bWF4KGVkYWQpLCBzYWxhcmlvX2ltc3M9bWVhbihzYWxhcmlvX2RpYXJpb19pbXNzKSxDb3VudD1uKCkpDQpjbHVzdGVyczwtYXMuZGF0YS5mcmFtZShiYWphczMpDQpjbHVzdGVycw0KDQpgYGANCg0KIyMjICpHZW5lcmFjacOzbiBkZSBncsOhZmljb3M6Kg0KDQojIyMjICoqR3LDoWZpY29zIGN1YW50aXRhdGl2b3M6KioNCg0KIyMjIyMgKipHcsOhZmljYSBkZSBiYXJyYXM6IChDbHVzdGVycykqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNTZSByZWFsaXrDsyB1bmEgZ3LDoWZpY2EgcGFyYSBhbmFsaXphciBlbCBuw7ptZXJvIGRlIHJlZ2lzdHJvcyBwb3IgY2FkYSBzZWdtZW50bzogDQpnZ3Bsb3QoYmFqYXMzLGFlcyh4PXJlb3JkZXIoQ2x1c3Rlcl9OYW1lcyxDb3VudCkseT1Db3VudCxmaWxsPUNsdXN0ZXJfTmFtZXMpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikNCg0KYGBgDQoNCkxvcyAqKmNvbGFib3JhZG9yZXMgcXVlIG3DoXMgYmFqYXMgaGFuIHByZXNlbnRhZG8qKiBzZWfDum4gbGEgZ3LDoWZpY2EgYW50ZXJpb3Igc29uICoqasOzdmVuZXMqKiwgc2VndWlkbyBwb3IgbG9zIGFkdWx0b3MgeSBkZXNwdcOpcyBsb3MgY29sYWJvcmFkb3JlcyBkZSBlZGFkIGF2YW56YWRhLg0KDQojIyMjIyAqKkdyw6FmaWNhIGRlIGRpc3BlcnNpw7NuOiAoQ2x1c3RlcnMgeSBlZGFkKSoqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI1Zpc3VhbGl6YXIgbGEgZWRhZCBwb3IgY2FkYSBzZWdtZW50bzoNCmdncGxvdChiYWphczMsIGFlcyh4PUNsdXN0ZXJfTmFtZXMseT1lZGFkX2HDsW9zLGZpbGw9IENsdXN0ZXJfTmFtZXMsbGFiZWw9cm91bmQoZWRhZF9hw7FvcyxkaWdpdHM9MikpKSArIA0KICBnZW9tX2NvbCgpICsgDQogIGdlb21fdGV4dCgpDQpgYGANCg0KVmlzdWFsaXphY2nDs24gZGUgbG9zIG3DoXhpbW9zIGRlIGNhZGEgc2VnbWVudG86ICpKw7N2ZW5lcyAoaGFzdGEgbG9zIDI4KSwgQXZhbnphZGEgKEhhc3RhIGxvcyA2MSksIEFkdWx0YSAoSGFzdGEgbG9zIDQwKSB5IGVsIE91dGxpZXIgZGUgMzIuKg0KDQojIyMjICoqR3LDoWZpY2FzIG1peHRhcyAoRGF0b3MgQ3VhbGl0YXRpdm9zIHkgQ3VhbnRpdGF0aXZvcyk6KioNCg0KIyMjIyMgKipHcsOhZmljYSBkZSBiYXJyYXM6IChDbHVzdGVycyB5IEfDqW5lcm8pKioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoYmFqYXMxLCBhZXMoZmFjdG9yKENsdXN0ZXJfTmFtZXMpLCBmaWxsID0gZmFjdG9yKGdlbmVybykpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UyKHByZXNlcnZlID0gInNpbmdsZSIpKSANCmBgYA0KDQpFc3RlIGdyw6FmaWNvIG5vcyBkaWNlIHF1ZSBkZSBjYWRhIHNlZ21lbnRvLCAqKmhheSBtw6FzIG11amVyZXMgcXVlIHNlIGhhbiBkYWRvIGRlIGJhamEuKioNCg0KIyMjIyMgKipHcsOhZmljYSBkZSBiYXJyYXM6IChDbHVzdGVycyB5IEVzdGFkbyBDaXZpbCkqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiBnZ3Bsb3QoYmFqYXMxLCBhZXMoZmFjdG9yKENsdXN0ZXJfTmFtZXMpLCBmaWxsID0gZmFjdG9yKGVzdGFkb19jaXZpbCkpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UyKHByZXNlcnZlID0gInNpbmdsZSIpKQ0KYGBgDQoNClBvZGVtb3MgdmlzdWFsaXphciBxdWUgZGUgZGUgbG9zIGrDs3ZlbmVzLCBsYSBtYXlvcsOtYSBlcmFuIHNvbHRlcm9zLCBkZSBsb3MgYWR1bHRvcyBoYWLDrWEgbGEgbWlzbWEgY2FudGlkYWQgZGUgY2FzYWRvcywgc29sZXJvcyB5IGVuIHVuacOzbiBsaWJyZSB5IGVuIGF2YW56YWRvcyBlcmFuIG3DoXMgc29sdGVyb3MgeSBjYXNhZG9zLiBNw6FzIHNpbiBlbWJhcmdvLCAqKmhheSBtdXkgcG9jb3MgY29sYWJvcmFkb3JlcyBxdWUgc2UgaGFuIGRhZG8gZGUgYmFqYSBkaXZvcmNpYWRvcy4qKg0KDQojIyMjIyAqKkdyw6FmaWNhIGRlIGJhcnJhczogKENsdXN0ZXJzIHkgTW90aXZvIGRlIEJhamEpKioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoYmFqYXMxLCBhZXMoZmFjdG9yKENsdXN0ZXJfTmFtZXMpLCBmaWxsID0gZmFjdG9yKG1vdGl2b19kZV9iYWphKSkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZTIocHJlc2VydmUgPSAic2luZ2xlIikpIA0KYGBgDQoNClBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGVuIGVsIHNlZ21lbnRvIGRlIGNvbGFib3JhZG9yZXMgam92ZW5lcywgYWR1bHRvcyB5IGRlIGVkYWQgYXZhbnphZGEsICoqbGEgbWF5b3IgcGFydGUgc2UgaGEgZGFkbyBkZSBiYWphIHBvciBmYWx0YXMsIHNpZ3VpZW5kbyBhc8OtIHBvciByZW51bmNpYSB2b2x1bnRhcmlhLioqDQoNCkxhICoqbWF5b3IgcGFydGUgZGUgbG9zIGV4LWNvbGFib3JhZG9yZXMgZGUgRm9ybSBlcmFuIEF5dWRhbnRlcyBnZW5lcmFsZXMgKDc2JSkuKioNCg0KIyMjIyMgKipHcsOhZmljYSBnZ2FsbHV2aWFsOiAoZWRhZCwgZ8OpbmVybyB5IGVzdGFkbyBjaXZpbCkqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmJhamFzMTwtYmFqYXMxWy1jKDE3KSxdDQpiYWphczUgPC1iYWphczEgJT4lIGZpbHRlcihDbHVzdGVycz09NCB8IENsdXN0ZXJzPT0zKSAlPiUgYXJyYW5nZShDbHVzdGVycykNCg0KZ2dwbG90KGFzLmRhdGEuZnJhbWUoYmFqYXM1KSwNCiAgICAgICBhZXMoeT1zYWxhcmlvX2RpYXJpb19pbXNzLCBheGlzMT0gZ2VuZXJvLCBheGlzMj1lc3RhZG9fY2l2aWwpKSArDQogIGdlb21fYWxsdXZpdW0oYWVzKGZpbGw9Q2x1c3Rlcl9OYW1lcyksIHdpZHRoID0gMS8xMikgKw0KICBnZW9tX3N0cmF0dW0od2lkdGggPSAxLzEyLCBmaWxsID0gImJsYWNrIiwgY29sb3IgPSAiZ3JleSIpICsNCiAgZ2VvbV9sYWJlbChzdGF0ID0gInN0cmF0dW0iLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KHN0cmF0dW0pKSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoIkfDqW5lcm8iLCAiRXN0YWRvIGNpdmlsIiksIGV4cGFuZCA9IGMoLjA1LCAuMDUpKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiU2V0MSIpICsNCiAgZ2d0aXRsZSgiRk9STSdzIFNhbGFyaW8gRGlhcmlvIHBvciBHw6luZXJvIHkgRXN0YWRvIENpdmlsIikNCmBgYA0KDQpFbiBlc3RlIGNhc28gcG9kZW1vcyBvYnNlcnZhciBxdWUgKipsb3MgY29sYWJvcmFkb3JlcyBxdWUgc2UgaGFuIGRhZG8gZGUgYmFqYSB5IGdhbmFuIG3DoXMgc29uIGVuIHN1IG1heW9yw61hIG11amVyZXMqKiBkZSBsYXMgY3VhbGVzIGxhIHF1ZSBtw6FzIGdhbmFiYSBlcmEgYWR1bHRhIGNhc2FkYTsgeSAqKmxhcyBxdWUgbWVub3MgZ2FuYWJhbiBlcmFuIGrDs3ZlbmVzIGVuIHVuacOzbiBsaWJyZSoqLiBQb3IgZWwgb3RybyBsYWRvLCAqbG9zIGhvbWJyZXMgcXVlIG3DoXMgZ2FuYWJhbiBlcmFuIGpvdmVuZXMgc29sdGVyb3MgeSBsb3MgcXVlIG1lbm9zIGdhbmFiYW4gZXJhbiBhZHVsdG9zIGVuIHVuacOzbiBsaWJyZSBkZSBlZGFkIGFkdWx0YS4qDQoNCiMjICoqTG9naXN0aWMgUmVncmVzc2lvbjoqKg0KDQojIyMgKkxpbXBpZXphIHkgdmlzdWFsaXphY2nDs24gZGUgZGF0b3M6Kg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgZmlsZS5jaG9vc2UoKQ0KbnVldmFfYmRfcmggPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcbWFyaWFcXERvY3VtZW50c1xcSVRFU00gTEFFVFxcU2VtZXN0cmUgN1xcUmV0b1xcRVZJMlxccmhfdW5pZGEuY3N2IikNCnN1bW1hcnkobnVldmFfYmRfcmgpDQpgYGANCg0KIyMjIyAqKlTDqWNuaWNhIDEuIFBvbmVyIGVuIHN1IHJlc3BlY3Rpdm8gZm9ybWF0byBjYWRhIHZhcmlhYmxlOioqDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbnVldmFfYmRfcmgkZWRhZCA8LSBhcy5udW1lcmljKG51ZXZhX2JkX3JoJGVkYWQpDQpudWV2YV9iZF9yaCRnZW5lcm8gPC0gYXMuZmFjdG9yKG51ZXZhX2JkX3JoJGdlbmVybykNCm51ZXZhX2JkX3JoJGFudGlndWVkYWQgPC0gYXMubnVtZXJpYyhudWV2YV9iZF9yaCRhbnRpZ3VlZGFkKQ0KbnVldmFfYmRfcmgkcHVlc3RvPC0gYXMuZmFjdG9yKG51ZXZhX2JkX3JoJHB1ZXN0bykNCm51ZXZhX2JkX3JoJHNhbGFyaW9fZGlhcmlvIDwtIGFzLm51bWVyaWMobnVldmFfYmRfcmgkc2FsYXJpb19kaWFyaW8pDQpudWV2YV9iZF9yaCRlc3RhZG9fY2l2aWw8LSBhcy5mYWN0b3IobnVldmFfYmRfcmgkZXN0YWRvX2NpdmlsKQ0KbnVldmFfYmRfcmgkZHZfYmFqYXMgPC0gYXMubnVtZXJpYyhudWV2YV9iZF9yaCRkdl9iYWphcykNCm51ZXZhX2JkX3JoJG1vdGl2b19kZV9iYWphPC0gYXMuZmFjdG9yKG51ZXZhX2JkX3JoJG1vdGl2b19kZV9iYWphKQ0Kc3VtbWFyeShudWV2YV9iZF9yaCkNCmBgYA0KDQojIyMjICoqVMOpY25pY2EgMi4gQ3JlYXIgdW5hIGNhdGVnb3LDrWEgZGUgcmVmZXJlbmNpYSBwYXJhIGxhIHZhcmlhYmxlICJkdl9iYWphcyI6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpudWV2YV9iZF9yaCRkdl9iYWphczwtYXMuZmFjdG9yKG51ZXZhX2JkX3JoJGR2X2JhamFzKQ0KbnVldmFfYmRfcmgkZHZfYmFqYXM8LWZjdF9yZWNvZGUobnVldmFfYmRfcmgkZHZfYmFqYXMsICJCQUpBIj0iMSIsIk5PIEJBSkEiPSIwIikNCmBgYA0KDQojIyMjICoqVmlzdWFsaXphY2nDs24gZGUgbGFzIHZhcmlhYmxlcyBjb21iaW5hZGFzOioqDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KdGFwcGx5KG51ZXZhX2JkX3JoJHNhbGFyaW9fZGlhcmlvLA0KICAgICAgIGxpc3QobnVldmFfYmRfcmgkZ2VuZXJvLG51ZXZhX2JkX3JoJGVzdGFkb19jaXZpbCksIG1lYW4pDQoNCmBgYA0KDQpFbiBlc3RhIHRhYmxhIHBvZGVtb3Mgb2JzZXJ2YXIgZWwgcHJvbWVkaW8gZGUgc2FsYXJpbyBwb3IgZXN0YWRvIGNpdmlsIHkgZ8OpbmVybywgZG9uZGUgZW4gZXN0ZSBjYXNvLCAqKmVsIHNlZ21lbnRvIHF1ZSBtYXMgZ2FuYSBzb24gbG9zIGhvbWJyZXMgc29sdGVyb3MsIHNlZ3VkaW9zIHBvciBsYXMgbXVqZXJlcyBkaXZvcmNpYWRhcyB5IGRlc3B1w6lzIGxvcyBob21icmVzIGNhc2Fkb3MuKioNCg0KIyMjICpFc3RpbWFyIGxhIFJlZ3Jlc2nDs24gTG9nw61zdGljYToqDQoNCiMjIyMgKipQYXNvIDEuIERpdmlkaXIgbGEgaW5mb3JtYWNpw7NuIGVudHJlICJ0cmFpbmluZyBhbmQgdGVzdCBkYXRhc2V0cyI6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzZXQuc2VlZCgxMjMpIA0KdHJhaW5pbmc8LW51ZXZhX2JkX3JoJGR2X2JhamFzICU+JSANCiAgY3JlYXRlRGF0YVBhcnRpdGlvbihwPTAuNzUsbGlzdD1GQUxTRSkNCnRyYWluLmRhdGE8LW51ZXZhX2JkX3JoW3RyYWluaW5nLCBdDQp0ZXN0LmRhdGE8LW51ZXZhX2JkX3JoWy10cmFpbmluZywgXQ0KYGBgDQoNCiMjIyMgKipQYXNvIDIuIE11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb246KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptb2RlbDwtZ2xtKGR2X2JhamFzfnNhbGFyaW9fZGlhcmlvK2VzdGFkb19jaXZpbCwgZGF0YT10cmFpbi5kYXRhLCBmYW1pbHk9Ymlub21pYWwobGluaz0nbG9naXQnKSkNCnN1bW1hcnkobW9kZWwpDQpgYGANCg0KRW4gZXN0ZSBtb2RlbG8gZGUgcmVncmVzacOzbiBwb2RlbW9zIGludGVycHJldGFyIHF1ZSAqKmxvcyBlc3RhZG9zIGNpdmlsZXMgZGUgc29sdGVybyB5IHVuacOzbiBsaWJyZSBzb24gdW4gZmFjdG9yIHF1ZSBhdW1lbnRhbiBsYSBwcm9iYWJpbGlkYWQgZGUgb2Nhc2lvbmFyIHVuYSBiYWphIGRlIHVuIGNvbGFib3JhZG9yLioqIFBvciBlbCBvdHJvIGxhZG8sIGVsIGVzdGFkbyBjaXZpbCBkZSBkaXZvcmNpYWRvLCBjdWVudGEgY29uIHVuYSB0ZW5kZW5jaWEgbmVnYXRpdmEsIGxvIHF1ZSBpbmRpY2EgcXVlIHNpIGxvcyBjb2xhYm9yYWRvcmVzIGRpdm9yY2lhZG9zIGF1bWVudGFuLCBsYXMgYmFqYXMgZGlzbWludWlyw6FuLg0KDQojIyMjICoqUGFzbyAzLiBHcsOhZmljYSBkZSByZWdyZXNpw7NuIGxvZ8Otc3RpY2E6KioNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QobnVldmFfYmRfcmgsYWVzKHg9c2FsYXJpb19kaWFyaW8sIHk9YXMubnVtZXJpYyhkdl9iYWphcykgLSAxKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYT0uNSkgKw0KICBzdGF0X3Ntb290aChtZXRob2Q9ImdsbSIsIHNlPUZBTFNFLCBmdWxscmFuZ2U9VFJVRSwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseT1iaW5vbWlhbCkpICsgDQogIHlsYWIoIlByb2JhYmlsaXR5IikgKyB4bGltKDEwMCw1MDApKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkxvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwiLCANCiAgICB4ID0gIlNhbGFyaW8gRGlhcmlvIiwNCiAgICB5ID0gIlByb2JhYmlsaWRhZCBkZSBCYWphcyINCiAgKQ0KYGBgDQoNCkNvbiBlc3RhIGdyw6FmaWNhIHBvZGVtb3MgZGVkdWNpciBxdWUgaGF5IHVuYSAqdGVuZGVuY2lhIG5lZ2F0aXZhKiBkZSBiYWphcywgeWEgcXVlIGVudHJlIG3DoXMgaW5jcmVtZW50YSBlbCBzYWxhcmlvIGRpYXJpbywgZXMgZGVjaXIsICoqZW50cmUgbcOhcyBzYWxhcmlvIHNlIGxlcyBwYWd1ZSBhIGxvcyBjb2xhYm9yYWRvcmVzLCBtZW5vcyBiYWphcyBzZSByZWdpc3RyYXLDoW4uKiogUG9yIGxvIHF1ZSBwb2RlbW9zIG9ic2VydmFyIHVuYSBhY3VtdWxhY2nDs24gZGUgYmFqYXMgZGUgY29sYWJvcmFkb3JlcyBxdWUgKipnYW5hbiBtZW5vcyBkZSAkMjAwIGFsIGTDrWEsIHRlbmllbmRvIHNvbG8gdW4gb3V0bGllciBxdWUgZ2FuYSAkNTAwLioqDQoNCiMgKipTZWNjacOzbiA0KioNCiMjICoqMSkgSGFsbGF6Z29zKioNCg0KPGRpdiBjbGFzcz10ZXh0LWp1c3RpZnk+DQoNCjEuIEZPUk0gcHJvZHVjZSAqKmVuIHByb21lZGlvIDM3MDguNTIga2lsb3MgZGUgbWVybWEgcG9yIG1lcy4qKg0KDQoyLiBTZSAqcHJvZHVqbyB1bmEgbWF5b3IgY2FudGlkYWQgZGUgcGllemFzKiBlbiBlbCBtZXMgZGUgKiphZ29zdG8uKioNCg0KMy4gKipBIHVuIHRpZW1wbyBtYXlvciBkZSBwcm9kdWNjacOzbiwgRk9STSBsZSBpbnZpZXJ0ZSBtZW5vcyBob3JhcyBkZSBjYWxpZGFkKiogZW4gbGFzIHVuaWRhZGVzIHByb2R1Y2lkYXMuDQoNCjQuIEZPUk0gdGllbmUgdWJpY2FkYSBsYSBtYXlvciBwYXJ0ZSBkZSBzdSAqKnNjcmFwIGVuIHByZS1wcm9kdWNjacOzbi4qKg0KDQo1LiBMYXMgdW5pZGFkZXMgdmVuZGlkYXMgZGUgY2Fycm9zIGF1bWVudGFyb24gbG9zIMO6bHRpbW9zIDUtMTAgYcOxb3MgeSAqKnNlIHByb25vc3RpY2EgcXVlIGhheWEgdW4gY3JlY2ltaWVudG8gcG9zaXRpdm8gZGUgbG9zIHZlaMOtY3Vsb3MgZW4gY2lyY3VsYWNpw7NuIGNvbiBlbCBwYXNvIGRlbCB0aWVtcG8uKioNCg0KNi4gRWwgKip0aXBvIGRlIGNhbWJpbyBlcyBsYSB2YXJpYWJsZSBxdWUgbcOhcyBhZmVjdGEgZSBpbXBhY3RhIGxhIGV4cG9ydGFjacOzbiBkZSB2ZWjDrWN1bG9zIGxpZ2Vyb3MqKi4gRW50cmUgbWVub3MgdmFsZ2EgZWwgcGVzbyAoYXVtZW50ZSBlbCBUQyksIGhhYnLDoSBtZW5vcyBleHBvcnRhY2lvbmVzLg0KDQo3LiBTaSAqKmluY3JlbWVudGEgZWwgZW1wbGVvKiogZW4gTcOpeGljbywgc2UgcHJlZGljZSBxdWUgbGFzICoqdmVudGFzIGRlIHZlaMOtY3Vsb3MgbGlnZXJvcyBpbmNyZW1lbnRhcsOhbioqIHRhbWJpw6luLg0KDQo4LiBMYSBtYXlvciBjYW50aWRhZCBkZSAqKmJhamFzIHF1ZSBGT1JNIGhhIHByZXNlbnRhZG8gc29uIGRlIGNvbGFib3JhZG9yZXMgasOzdmVuZXMqKiAoZW50cmUgbG9zIDIwLTMwIGHDsW9zIGVuIHN1IG1heW9yw61hKSwgc2llbmRvIGVsIHByaW5jaXBhbCBtb3Rpdm8gbGEgKirigJxCYWphIHBvciBGYWx0YXPigJ0gKDU5JSksIGhheSB1bmEgbWF5b3LDrWEgZGUgbXVqZXJlcyoqIHkgbGEgbWF5b3IgcGFydGUgZGUgbG9zIGV4LWNvbGFib3JhZG9yZXMgZGUgRm9ybSBlcmFuICoqQXl1ZGFudGVzIGdlbmVyYWxlcyAoNzYlKS4qKg0KDQo5LiBBY3R1YWxtZW50ZSBGT1JNIGN1ZW50YSBjYXNpIGNvbiBsYSBtaXNtYSBjYW50aWRhZCBkZSBjb2xhYm9yYWRvcmVzIGZlbWVuaW5vcyB5IG1hc2N1bGlub3MgeSBjb24gdW4gcmFuZ28gZGUgZWRhZGVzIGVudHJlIGxvcyAxOCB5IGxvcyA3MyBhw7Fvcy4gQXNpbWlzbW8gZWwgKipwcm9tZWRpbyBkZSBzYWxhcmlvIGdhbmFkbyBlcyBkZSAxNzAgcGVzb3MgZGlhcmlvcyoqIHkgbG9zICoqaG9tYnJlcyBjYXNhZG9zIHkgbGFzIG11amVyZXMgc29sdGVyYXMgc29uIGxvcyBxdWUgZ2FuYW4gbcOhcyoqLg0KDQoxMC4gTG9zIGNsaWVudGVzIG3DoXMgaW1wb3J0YW50ZXMgcGFyYSBGT1JNIChxdWUgbcOhcyBwaWV6YXMgcHJvZ3JhbWFkYXMgdGllbmVuKSBzb24gKipZYW5mZW5nLCBTdGFiaWx1cyAxIHkgVmFycm9jLioqDQoNCjExLiBMb3MgY2xpZW50ZXMgY29uIGxvcyBxdWUgRk9STSBwcmVzZW50YSBtw6FzICpob3JhcyBkZSByZXRyYXNvIGVuIGVudHJlZ2FzIGVzIGNvbiBNYWhsZSB5IFByaW50ZWwqICh0ZW5pZW5kbyBoYXN0YSAyMGgpLCBjbGllbnRlcyBxdWUgbm8gb3JkZW5hbiBtdWNoYXMgcGllemFzLiBMbyBxdWUgcHVlZGUgc2lnbmlmaWNhciBxdWUgKipsb3MgY2xpZW50ZXMgaW1wb3J0YW50ZXMgc29uIHByaW9yaXRhcmlvcyBwYXJhIEZPUk0geSBlcyBwb3IgZXNvIHF1ZSBjb24gZWxsb3Mgbm8gc3VmcmVuIHJldHJhc29zLioqDQoNCjEyLiBIYXkgdW5hICoqdGVuZGVuY2lhIG5lZ2F0aXZhIGVudHJlIGVsIHNhbGFyaW8geSBsYXMgYmFqYXMgZGUgY29sYWJvcmFkb3JlcyoqLCBzaSBlc3RlIGluY3JlbWVudGEsIGxhcyBiYWphcyBkaXNtaW51aXLDoW4uDQoNCjEzLiBTZSBwcm9ub3N0aWNhIHF1ZSAqKmVuIGxvcyBwcsOzeGltb3MgYcOxb3Mgbm8gaGFicsOhIHVuIGluY3JlbWVudG8gZW4gZWwgdGllbXBvIGRlIHJldHJhc29zIGRlIGVudHJlZ2EgZGUgcGVkaWRvcyoqIGVuIGxvcyBwcm9jZXNvcyBkZSBGT1JNLCBzaW5vIHF1ZSBzZSBtYW50ZW5kcsOhIGVzdGFibGUgaW5kZXBlbmRpZW50ZW1lbnRlIGRlbCAqKmluY3JlbWVudG8gZGUgc2NyYXAgcXVlIHNlIGVzcGVyYSBlbiBsb3MgcHLDs3hpbW9zIGHDsW9zKiosIGxvIGN1YWwgdmEgZGUgbGEgbWFubyBjb24gZWwgaW5jcmVtZW50byBkZSBsYSBwcm9kdWNjacOzbi4NCg0KPC9kaXY+DQoNCiMjICoqMikgU3VnZXJlbmNpYXMgZGUgbWVqb3JhIHBhcmEgc3UgcHJvY2VzbyBkZSBhbmFsw610aWNhIGRlIGRhdG9zKioNCg0KPGRpdiBjbGFzcz10ZXh0LWp1c3RpZnk+DQoNCjEuIFByaW5jaXBhbG1lbnRlIHNlIHN1Z2llcmUgcXVlIEZPUk0gY3VlbnRlIGNvbiB1bmEgKiplc3RhbmRhcml6YWNpw7NuIGRlIHN1cyBiYXNlcyBkZSBkYXRvcyoqLCBxdWUgYmllbiwgY2FtYmllbiBlbCBmb3JtYXRvIGRlIGxhcyB2YXJpYWJsZXMsIHNvbG8gcG9uZ2FuIGluZm9ybWFjacOzbiBxdWUgbmVjZXNpdGVuLCBxdWUgdXRpbGljZW4gbGEgbWlzbWEgdGlwb2dyYWbDrWEsIGNvbG9yZXMgeSBzZWFuIG9yZGVuYWRvcyBjb24gZG9uZGUgcG9uZW4gbGEgaW5mb3JtYWNpw7NuLiBFc3RvIHNlIHB1ZWRlIHJlc29sdmVyIG1lZGlhbnRlIGxhICpkZWRpY2FjacOzbiBkZSBob3JhcyBkZSBjYWxpZGFkIHkgbGEgdXRpbGl6YWNpw7NuIGRlIGZpbHRyb3MgcGFyYSB2ZXJpZmljYXIgcXVlIGxvcyBkYXRvcyBjdW1wbGFuIGNvbiBjaWVydGFzIG3DqXRyaWNhcyB5IHNlIHB1ZWRhbiBhbmFsaXphciBtw6FzIGFkZWxhbnRlLCBvIGJpZW4sIHRvbWFuZG8gY3Vyc29zIGV4dGVybm9zIChvIGNvbnRyYXRhY2nDs24gZGUgZXhwZXJ0b3MgZW4gYW5hbMOtdGljYSkgcGFyYSBsYSBjb3JyZWNjacOzbiDDs3B0aW1hIGRlIHN1cyBiYXNlcyBkZSBkYXRvcy4qDQoNCjIuIEFzaW1pc21vIGFsIGVzdGFyIHVzYW5kbyBsYXMgYmFzZXMgZGUgZGF0b3MgdmFyaW9zIGNvbGFib3JhZG9yZXMgZW4gY29uanVudG8geSBhY3R1YWxpemFybGFzIGFsIGTDrWEsIHNlIHN1ZWxlbiBjb21ldGVyIHZhcmlvcyBlcnJvcmVzLCBwdWVzIGNvbW8gbm8gZXMgc3UgcHJpbmNpcGFsIGVuZm9xdWUsIGxvIHB1ZWRlbiBoYWNlciBzaW4gY3VpZGFkbyB5IHNpbiBwb25lcmxlIGF0ZW5jacOzbiBhIGxvcyBkZXRhbGxlcy4gUG9yIGxvIHF1ZSBzZSBwb3BvbmUsICoqY3JlYXIgdW4gZGVwYXJ0YW1lbnRvIGRlIGFuYWzDrXRpY2EgZGUgZGF0b3MgZW4gRk9STSB5IHF1ZSAgZXN0YSBlc3TDqSBlbmNhcmdhZGEgZGUgY2FwdHVyYXIgdG9kYSBsYSBpbmZvcm1hY2nDs24gcGFyYSBhc8OtIGV2aXRhciBlcnJvcmVzIGRlIGRlZG8geSBoYWNlcmxvIGFkZWN1YWRhbWVudGUuKioNCg0KMy4gUG9yIMO6bHRpbW8sIGFsIEZPUk0gc2VyIHVuYSBlbXByZXNhIGNoaWNhL21lZGlhbmEgc2luIHVuIGRlcGFydGFtZW50byBkZSBhbmFsw610aWNhIGRlIGRhdG9zLCBzZSBzdWdpZXJlIHF1ZSBsYSBlbXByZXNhIHNlIGluY3Vyc2lvbmUgZW4gdW5hICoqcGxhdGFmb3JtYSBpbnRlbGlnZW50ZSBsYSBjdWFsIHNpc3RlbWF0aWNlIGxhIGNhcHR1cmEgZGUgZGF0b3MgeSB2aXN1YWxpemFjacOzbiBkZSBsb3MgbWlzbW9zKiosIG8gYmllbiwgZW4gdW4gKmRlcGFydGFtZW50byBkZSBhbmFsw610aWNhKiBjb21vIHNlIG1lbmNpb27DsyBhbnRlcmlvcm1lbnRlIHkgcXVlIGVzdGUgc2VhIGNhcGF6IGRlICoqZ2VuZXJhciByZXBvcnRlcyBjb24gdmlzdWFsaXphY2lvbmVzLCBjdW1wbGltaWVudG8gZGUgbWV0YWFzIHkgaGFsbGF6Z29zIHJlbGV2YW50ZXMgcGFyYSBxdWUgbG9zIGRhdG9zIHJlZ2lzdHJhZG9zIHNlYW4gw7p0aWxlcyB5IHNlIHB1ZWRhbiBjcmVhciBudWV2YXMgZXN0cmF0ZWdpYXMgZGUgbWVqb3JhLioqDQoNCjwvZGl2Pg0KDQojIyAqKjMpIEJ1c2luZXNzIEFuYWx5dGljcyB5IEJ1c2luZXNzIEludGVsbGlnZW5jZSoqDQoNCjxkaXYgY2xhc3M9dGV4dC1qdXN0aWZ5Pg0KDQoqKkJ1c2luZXNzIEFuYWx5dGljcyoqIGVzIHVuIG3DqXRvZG8gbyB1bmEgc29sdWNpw7NuIHF1ZSBsYXMgZW1wcmVzYXMgZW1wbGVhbiBlbiBzdXMgcHJvY2Vzb3MgdG9tYW5kbyBlbiBjdWVudGEgc3UgaW5mb3JtYWNpw7NuIGludGVybmEgeSBleHRlcm5hLiBQZXJzb25hbGFtZW50ZSBwaWVuc28gcXVlIHN1cyBwcmluY2lwYWxlcyBmdW5jaW9uZXMgY29uc2lzdGVuIGVuIGxpbXBpYXIgeSB0cmFuc2Zvcm1hciBzdXMgZGF0b3MuIFNpbiBlbWJhcmdvLCBhbGd1bm9zIGRlIHN1cyBwcmluY2lwYWxlcyBvYmpldGl2b3Mgc29uIMKoKipkZXRlcm1pbmFyIHF1w6kgY29uanVudG9zIGRlIGRhdG9zIHNvbiDDunRpbGVzLCBjbGFzaWZpY2FybG9zIGRlcGVuZGllbmRvIGRlIGxhcyByZWxhY2lvbmVzIGV4aXN0ZW50ZXMgZW50cmUgbGFzIHZhcmlhYmxlcywgb3JnYW5pemFybG9zLCBmaWx0cmFybG9zLCBsaW1waWFybG9zIHkgZXhhbWluYXJsb3MuKioNCg0KRWwgZGVwdG8uIGRlIEJ1c2luZXNzIEFuYWx5dGljcyBjb2xhYm9yYSBjb24gZWwgZGUgKipCdXNpbmVzcyBJbnRlbGxpZ2VuY2UqKiwgw7NzZWEsIHlhIHF1ZSBsb3MgY29sYWJvcmFkb3JlcyB0aWVuZW4gbGEgaW5mb3JtYWNpw7NuIGxpbXBpYSB5IHRyYW5zZm9ybWFkYSAoQkEpLCBlc3RvcyBwdWVkZW4gKip0b21hciBkZWNpc2lvbmVzIG3DoXMgaW50ZWxpZ2VudGVzKiogYmFzYWRhcyBlbiBsb3MgZGF0b3MgYW5hbGl6YWRvcyBhbCB1dGlsaXphciBkaWZlcmVudGVzIGhlcnJhbWllbnRhcyBjb21vIGVsICoqYW7DoWxpc2lzIGVzdGFkw61zdGljbyB5IHByZWRpY3Rpdm8sIGhhY2llbmRvIGVzdGFydGVnaWFzIGFycmFpZ2FkYXMgcHJpbmNpcGFsbWVudGUgYSBsb3Mgb2JqZXRpdm9zIGVzcGVjw61maWNvcyBkZSBsYSBlbXByZXNhLCBjb21vIHB1ZWRlIHNlciBlbCBhdW1lbnRhciBsb3MgaW5ncmVzb3MsIGxhIHByb2R1Y3RpdmlkYWQsIGxhIGVmaWNpZW5jaWEgeSBlbCByZW5kaW1pZW50byBvcmdhbml6YWNpb25hbCoqIChCSSkuDQoNClBhcmEgc2VyIG1hcyBjbGFyb3MsICpsYSByZWxhY2nDs24gZW50cmUgZWwg4oCcQnVzaW5lc3MgSW50ZWxsaWdlbmNl4oCdIHkgZWwg4oCcQnVzaW5lc3MgQW5hbHl0aWNz4oCdIGVzIHF1ZSB1biBleHBlcnRvIGVuIEJ1c2luZXNzIEludGVsbGlnZW5jZSwgdG9tYSB5IHByb2Nlc2EgbGEgaW5mb3JtYWNpw7NuIGdlbmVyYWRhIHBvciBsb3MgZXhwZXJ0b3MgZW4gQnVzaW5lc3MgQW5hbHl0aWNzIHkgbGEgY29udmllcnRlIGVuIGRlY2lzaW9uZXMgZXN0cmF0w6lnaWNhcyBwYXJhIHN1IG5lZ29jaW8uKg0KDQo8L2Rpdj4NCg0KIyMgKio0KSBDb25jZXB0byBLUEkqKg0KDQo8ZGl2IGNsYXNzPXRleHQtanVzdGlmeT4NCg0KVW4gIktleSBQZXJmb3JtYW5jZSBJbmRpY2F0b3IiIGVzIHVuYSAqKm3DqXRyaWNhIHV0aWxpemFkYSBlbiBsYXMgZW1wcmVzYXMgcGFyYSBtZWRpciBlbCBkZXNlbXBlw7FvIGRlIHVuIMOhcmVhIGVzcGVjw61maWNhLioqIEVzdGFzICphbmFsaXphbiBzaSBzdXMgYWNjaW9uZXMgZXN0w6FuIHRlbmllbmRvIGxvcyByZXN1bHRhZG9zIGVzcGVyYWRvcywgcGFyYSBkZXNwdcOpcyBwb2RlciB0b21hciBkZWNpc2lvbmVzIHkgY3JlYXIgZXN0cmF0ZWdpYXMgaW50ZWxpZ2VudGVzIGJhc2FkYXMgZW4gc3VzIGRhdG9zLioNCg0KQXNpbWlzbW8sIGxvcyBLUEnCtHMgZGViZW4gZGUgdGVuZXIgY2llcnRhcyBjYXJhY3RlcsOtc3RpY2FzIHBhcmEgc2VyIGluZm9ybWFjacOzbiB2YWxpb3NhIHkgw7p0aWwgcGFyYSBsYXMgZW1wcmVzYXMuIEVzdG9zIGRlYmVuIGRlIHNlcjogKmVzcGVjw61maWNvcywgY29udGludW9zIHkgcGVyacOzZGljb3MgKG1lZGlyIHNvYnJlIHVuIGNpZXJ0byBwZXJpb2RvIGRlIHRpZW1wbyksIG9iamV0aXZvcywgY3VhbnRpZmljYWJsZXMsIG1lZGlibGVzLCByZWFsaXN0YXMsIGNvbmNpc29zLCBjb2hlcmVudGVzIHkgcmVsZXZhbnRlcy4qDQoNCkFsZ3Vub3MgZWplbXBsb3MgZGUgS1BJcyBjb23Dum5tZW50ZSB1dGlsaXphZG9zIGVuIGVtcHJlc2FzIHNvbjoNCi0gTsO6bWVybyBkZSBzZWd1aWRvcmVzLg0KLSBSZXRvcm5vIGRlIGxhIGludmVyc2nDs24uDQotIE7Dum1lcm8gZGUgdmVudGFzIG1lbnN1YWxlcy4NCi0gUmF0aW8gZGUgbGlxdWlkZXouDQoNCjwvZGl2Pg0KDQojIyAqKjUpIEtQSXMgcHJvcHVlc3RvcyBwYXJhIEZPUk0qKg0KDQo8ZGl2IGNsYXNzPXRleHQtanVzdGlmeT4NCg0KVG9tYW5kbyBlbiBjdWVudGEgZWwgYW7DoWxpc2lzIHBsYW50ZWFkbyBwYXJhIGVsIHNvY2lvZm9ybWFkb3IgRk9STSwgc2UgcHJvcG9uZSBxdWUgaW1wbGVtZW50ZSBsYXMgc2lndWllbnRlcyBtw6l0cmljYXMgKEtQScK0cykgcGFyYSBtZWRpciBzdXMgcmVzdWx0YWRvcyB5IGdlbmVyYXIgZXN0cmF0ZWdpYXM6DQoNCjEuICoqVGFzYSBkZSByb3RhY2nDs24gZGUgY29sYWJvcmFkb3JlczoqKiBQYXJhIGVsIGRlcGFydGFtZW50byBkZSBSZWN1cnNvcyBIdW1hbm9zIGVzdGEgbcOpdHJpY2EgcHVlZGUgc2VyIHN1bWFtYW50ZSBlbnJpcXVlY2Vkb3JhIHB1ZXMgcGVybWl0ZSBjb25vY2VyIGVsICUgZGUgYWx0YXMgeSBiYWphcyBlbiByZWxhY2nDs24gYWwgbsO6bWVybyBkZSBlbXBsZWFkb3MuIENvbiBlc3RhIGluZm9ybWFjacOzbiwgZWwgZGVwYXJ0YW1lbnRvIHB1ZWRlIGNvbm9jZXIgbGFzIGNhdXNhcywgcGVyaW9kb3MgZGUgdGllbXBvIG8gY2FyYWN0ZXLDrXN0aWNhcyBlc3BlY8OtZmljYXMgcG9yIGxhcyBjdWFsZXMgbGEgcmV0ZW5jacOzbiBkZSBjb2xhYm9yYWRvcmVzIGVzdGEgZmFsbGFuZG8geSBwcm9wb25lciBlc3RyYXRlZ2lhcyBwYXJhIHN1IHNvbHVjacOzbi4NCg0KMi4gKipUYWt0IHRpbWU6KiogRXN0YSBtw6l0cmljYSBlcyBpZGVhbCBwYXJhIG1lZGlyIGVsIGRlc2VtcGXDsW8gZGUgbGFzIMOhcmVhcyBkZSBwcm9kdWNjacOzbiwgZGVsaXZlcnkgcGVyZm9ybWFuY2UgeSBwbGFuIGRlIEZPUk0sIHB1ZXMgYmllbiBlc3RlIEtQSSBjb25zaXN0ZSBlbiBtZWRpciBlbCB0aWVtcG8gbcOheGltbyBxdWUgc2UgcHVlZGUgZGVkaWNhciBhIGZhYnJpY2FyIHVuIHByb2R1Y3RvIHBhcmEgY3VtcGxpciBjb24gbG9zIHBsYXpvcyBkZSB0aWVtcG8gcGxhbmVhZG9zLiBFc3RlIGF5dWRhIGEgcGxhbmlmaWNhciBwZWRpZG9zIHkgZGVjaWRpciBzaSBzZSBhY2VwdGEgbyBubyBlbCBlbmNhcmdvIGRlIHVuIGNsaWVudGUgcGFyYSBhc8OtIGV2aXRhciBob3JhcyBkZSByZXRyYXNvLiANCg0KMy4gKipUYXNhIGRlIGRlc2VyY2nDs24gZGUgY2xpZW50ZXM6KiogRXN0ZSBLUEkgZXMgZXNjZW5jaWFsIHBhcmEgZWwgw6FyZWEgY29tZXJjaWFsIHkgZGUgc2VydmljaW8gYWwgY2xpZW50ZSBkZSBGT1JNLCBwdWVzIGJpZW4gYWwgc2VyIEZPUk0gdW5hIGVtcHJlc2EgY29uIHVuIHNlZ21lbnRvIG11eSBlc3BlY8OtZmljbywgbGEgcmV0ZW5jacOzbiBkZSBzdXMgY2xpZW50ZXMgZXMgc3VtYW1lbnRlIGltcG9ydGFudGUsIHBvciBsbyBxdWUgZXN0YSBtw6l0cmljYSBtaWRlIGxhIGZpZGVsaWRhZCBjYWxjdWxhbmRvIGxhIHByb3BvcmNpw7NuIGRlIGNsaWVudGVzIHBlcmRpZG9zIHksIHBvciBsbyB0YW50bywgYXl1ZGEgYSBtZWRpciBsYSBzYXRpc2ZhY2Npw7NuIGRlIGxvcyBjbGllbnRlcy4NCg0KPC9kaXY+DQoNCiMgKipSZWZlcmVuY2lhcyBiaWJsaW9ncsOhZmljYXMqKg0KDQo8ZGl2IGNsYXNzPXRleHQtanVzdGlmeT4NCg0KQ2VyYSwgQy4gKDIwMjEpLiAzMiBLUEkgY29tZXJjaWFsZXMgcGFyYSB2ZW50YXMuIGFwcHZpemVyLmVzLiBSZWN1cGVyYWRvIDIxIGRlIG9jdHVicmUgZGUgMjAyMiwgZGUgaHR0cHM6Ly93d3cuYXBwdml6ZXIuZXMvcmV2aXN0YS9yZWxhY2lvbi1jbGllbnRlL3NvZnR3YXJlLWNybS9rcGktY29tZXJjaWFsZXMNCg0KSW5zaWdodHNvZnR3YXJlLiAoMjAyMikuIEVqZW1wbG9zIGRlIGxvcyAzMCBtZWpvcmVzIEtQSSB5IG3DqXRyaWNhcyBkZSBwcm9kdWNjacOzbiBwYXJhIGxhIGNyZWFjacOzbiBkZSBpbmZvcm1lcyBlbiAyMDIxLiBpbnNpZ2h0c29mdHdhcmUgU3BhaW4uIFJlY3VwZXJhZG8gMjEgZGUgb2N0dWJyZSBkZSAyMDIyLCBkZSBodHRwczovL2luc2lnaHRzb2Z0d2FyZS5jb20vZXMvYmxvZy8zMC1tYW51ZmFjdHVyaW5nLWtwaXMtYW5kLW1ldHJpYy1leGFtcGxlcy8NCg0KR2FsaWFuYSwgUC4gKDIwMjIpLiBRdcOpIGVzIEJ1c2luZXNzIEFuYWx5dGljczogZGVmaW5pY2nDs24sdGlwb3MgeSBkaWZlcmVuY2lhcy4gVGhpbmtpbmcgZm9yIElubm92YXRpb24uIFJlY3VwZXJhZG8gMjEgZGUgb2N0dWJyZSBkZSAyMDIyLCBkZSBodHRwczovL3d3dy5pZWJzY2hvb2wuY29tL2Jsb2cvcXVlLWVzLWJ1c2luZXNzLWFuYWx5dGljcy1kZWZpbmljaW9udGlwb3MteS1kaWZlcmVuY2lhcy1iaWctZGF0YS8NCg0KQnVyZWF1IG9mIExhYm9yIFN0YXRpc3RpY3MuICgyMDIyKS4gVW5pdGVkIFN0YXRlczogSW5mbGF0aW9uIHJhdGUgZnJvbSAxOTkwIHRvIDIwMjIuIFN0YXRpc3RhLiBSZWN1cGVyYWRvIDIxIGRlIG9jdHVicmUgZGUgMjAyMiwgZGUgaHR0cHM6Ly8wLXd3dy1zdGF0aXN0YS1jb20uYmlibGlvdGVjYS1pbHMudGVjLm14L3N0YXRpc3RpY3MvMTkxMDc3L2luZmxhdGlvbi1yYXRlLWluLXRoZS11c2Etc2luY2UtMTk5MC8NCg0KSU5FR0kuICgyMDIyKS4gVmVudGEsIHByb2R1Y2Npw7NuIHkgZXhwb3J0YWNpw7NuIGRlIHZlaMOtY3Vsb3MgbGlnZXJvcy4gUmVjdXBlcmFkbyAyMSBkZSBvY3R1YnJlIGRlIDIwMjIsIGRlIGh0dHBzOi8vd3d3LmluZWdpLm9yZy5teC9hcHAvdGFidWxhZG9zL2RlZmF1bHQuaHRtbD9uYz0xMDAxMDAwOTBfYQ0KDQpJbnRlcm5hdGlvbmFsIFRyYWRlIEFkbWluaXN0cmF0aW9uLiAoMjAyMikuIE51bWJlciBvZiBuZXcgcGFzc2VuZ2VyIHZlaGljbGVzIGFuZCBsaWdodCB0cnVja3MgZXhwb3J0ZWQgZnJvbSB0aGUgVW5pdGVkIFN0YXRlcyB0byBDaGluYSBmcm9tIDIwMDYgdG8gMjAyMS4gU3RhdGlzdGEuIGh0dHBzOi8vMC13d3ctc3RhdGlzdGEtY29tLmJpYmxpb3RlY2EtaWxzLnRlYy5teC9zdGF0aXN0aWNzLzI0NDQ4OC92ZWhpY2xlLWV4cG9ydHMtZnJvbS10aGUtdW5pdGVkLXN0YXRlcy10by1jaGluYS8NCg0KVFJBRElORyBFQ09OT01JQ1MuICgyMDIyKS4gRXN0YWRvcyBVbmlkb3MgLSBQSUIgfCAxOTYwLTIwMjEgRGF0b3MgfCAyMDIyLTIwMjQgRXhwZWN0YXRpdmEuIFJlY3VwZXJhZG8gMjEgZGUgb2N0dWJyZSBkZSAyMDIyLCBkZSBodHRwczovL2VzLnRyYWRpbmdlY29ub21pY3MuY29tL3VuaXRlZC1zdGF0ZXMvZ2RwDQoNCkJFQS4gKDIwMjIpLiBVLlMuIGNhciBzYWxlcyBmcm9tIDE5NTEgdG8gMjAyMS4gU3RhdGlzdGEuIFJlY3VwZXJhZG8gMjEgZGUgb2N0dWJyZSBkZSAyMDIyLCBkZSBodHRwczovLzAtd3d3LXN0YXRpc3RhLWNvbS5iaWJsaW90ZWNhLWlscy50ZWMubXgvc3RhdGlzdGljcy8xOTk5NzQvdXMtY2FyLXNhbGVzLXNpbmNlLTE5NTEvDQoNCjwvZGl2Pg0K