Primer punto: Definir conceptos.

1. K - means Clustering
K-means es un método de agrupamiento, que tiene como objetivo la partición de un conjunto de observaciones en k grupos en el que cada observación pertenece al grupo cuyo valor medio es más cercano. De esta manera es que se puede analizar cada Cluster, obteniendo información importante de cada variable y las similitudes entre ellas. Un claro ejemplo de cómo funciona es usar clustering para comprimir imágenes con pérdida de información y/o comprender qué hace a los clientes diferentes para poder ofrecerles los productos y servicios que necesiten.

2. Unsupervised Learning
El “Unsupervised Learning” utiliza algoritmos de aprendizaje automático para analizar y agrupar conjuntos de datos sin etiquetar. Estos algoritmos descubren patrones ocultos o agrupaciones de datos sin necesidad de intervención humana. No se asignan etiquetas al algoritmo de aprendizaje, dejándolo solo para encontrar la estructura en su entrada. El Unsupervised Learning puede ser un objetivo en sí mismo (descubrir patrones ocultos en los datos) o un medio para lograr un fin (aprendizaje de funciones).

3. Distancia Euclidiana / Euclidean Distance
La distancia euclidiana se define como la distancia entre dos puntos. La distancia euclidiana entre dos puntos en el espacio euclidiano se define como la longitud del segmento de línea entre dos puntos. La herramienta Distancia euclidiana se utiliza con frecuencia como una herramienta independiente para aplicaciones, como encontrar el hospital más cercano para un vuelo de emergencia en helicóptero. Alternativamente, esta herramienta se puede usar al crear un mapa de idoneidad, cuando se necesitan datos que representan la distancia desde un objeto determinado.

Segundo punto: Generar Clusters.

Descargar librerías

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

Se importa la base de datos de BAJAS ya limpia.

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

¿Cuántos NA tengo por variables?

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

1. Edad y duración - 4 Clusters

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

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

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

Se toman en cuenta, primeramente, solo 4 clusters.

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

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

bajas_new2<-bajas_new
bajas_new2$Clusters<-edad_cluster1$cluster
bajas_new3<-bajas_new2 %>% group_by(Clusters) %>% summarise(edad=max(edad)) %>% arrange(desc(edad))
bajas_new2$Cluster_Names<-factor(bajas_new2$Clusters,levels = c(1,2,3,4), 
                              labels=c("Senior", "Expert", "Junior", "Beginner"))
bajas_new4 <- bajas_new2 %>% group_by(Cluster_Names) %>% summarize(edad_años=max(edad), 
duracion=mean(duracion),
Count=n())
clusters<-as.data.frame(bajas_new4)
clusters
##   Cluster_Names edad_años   duracion Count
## 1        Senior        57   56.78409    44
## 2        Expert        61 1509.75000     4
## 3        Junior        39   53.07792    77
## 4      Beginner        27   46.17593   108

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

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

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

Existe una mayor cantidad de “beginners”.

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

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

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

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

2. Edad y duración - 5 Clusters

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

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

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

Comparando esta gráfica de 5 segmentos contra la anteriormente vista de 4. En esta observamos que separa un segmento que deja un “espacio” que antes no existía; separa y crea un segmento que han durado aprox un 10% más de tieempo que las tres menores, y que se encuentra en rangos de edad parecidos. A continuación se comparan los clusters:
1. El rojo (llamado “Senior”): es el nuevo, el cual tiene de baja a mediana duración y un rango de edad mediana. Después de los que han durado mucho, ellos van después.

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

  2. El verde inferior (llamado “Junior”) son personas adultas que han durado poco tiempo en la empresa, parte del 17% inferior de duración.

  3. El morado (llamado “Middle”): al hacer el nuevo clúster nos damos cuenta de que existe un grupo de personas de mediana edad que duran muy poco, aún más poco que los jóvenes. Investigar estado civil.

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

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

Al analizar el estado civil de los 5 clústers, llegamos a las siguientes conclusiones:
1. Del segmento “Senior”, el 75% son solteros. Este es el nuevo clúster descubierto.
2. El 55% de las personas en el cluster “Middle” (menor duración) son solteras, el 26% viven en unión libre, y un 19% que viven en matrimonio.
3. De los expertos, el 75% son solteros.

3. Duración y salario

Importar base de datos

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

¿Cuántos NA tengo por variables?

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

Base de datos con valores CUANTITATIVOS

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

Normalizando las variables

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

Elbow Plot

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

Visualización información de clusters

duracion_cluster1<-kmeans(bajas_duracion_norm,5)
duracion_cluster1
## K-means clustering with 5 clusters of sizes 191, 10, 4, 27, 1
## 
## Cluster means:
##      duracion salario_diario
## 1 -0.23462767      0.1147221
## 2  1.49983327     -0.3729089
## 3  6.65505218     -0.3962566
## 4  0.02340506     -1.1243505
## 5  2.56340604     13.7596523
## 
## Clustering vector:
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
##   5   4   4   4   4   4   4   4   4   2   4   4   1   1   2   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   3   4   4   1   1   1   1   1   1   1   1   1   1   1   1   4   2   1   1 
##  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
##   2   2   1   1   1   1   1   1   1   1   1   3   2   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   2   2   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 
##   3   4   4   4   4   1   1   1   1   1   1   1   1   1   1   1   4   4   1   1 
## 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
##   1   1   1   1   1   1   1   3   1   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   1   1   1   1   1   1   1   1   1   2   1   1   1   1   1   1   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   1   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   1   1   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 
##   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1 
## 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
##   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   2   1   1 
## 221 222 223 224 225 226 227 228 229 230 231 232 233 
##   1   1   1   1   1   1   1   1   1   1   1   1   1 
## 
## Within cluster sum of squares by cluster:
## [1] 3.281687 6.437305 7.884909 1.648877 0.000000
##  (between_SS / total_SS =  95.9 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Visualización clustering resultados

fviz_cluster(duracion_cluster1,data=bajas_duracion_norm)

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

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

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

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

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

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

Nombrar a los clusters

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

Engrupar los clusters por nombre de clusters y resumir columnas

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

Formato tabla para información de los clusters

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

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

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

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

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

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

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

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

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

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

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

4. Edad y salario

Importar base de datos

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

Analizar la estructura de la base de datos

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

Cuantos Na´s hay por variable

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

Dejamos únicamente las variables cuantitativas

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

Normalización de variables

bdr3 <- scale(bdr2[1:2]) 

Elbow plot

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

Visualización de clusters

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

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

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

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

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

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

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

Nombrar a los clusters

bdr5<-bdr2
bdr5$Cluster <- bdr4$cluster
bdr6<-bdr5 %>% group_by(Cluster) %>% summarize(salario_diario=max(salario_diario)) %>% arrange(desc(salario_diario))
bdr5$Cluster_Names<-factor(bdr5$Cluster,levels = c(1,2,3,4), 
                                labels=c("Alto", "Medio","Bajo", "Regular"))

Agrupar clusters

bdr7<- bdr5 %>% group_by(Cluster_Names) %>% summarize(salario_diario=max(salario_diario), 
edad=mean(edad),
Count=n())

Tabla

clusters<-as.data.frame(bdr7)
clusters
##   Cluster_Names salario_diario     edad Count
## 1          Alto         500.00 32.00000     1
## 2         Medio         183.68 47.92105    38
## 3          Bajo         180.68 34.14474    76
## 4       Regular         182.68 23.05932   118

Gráficas

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

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

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

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

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

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

Tercer punto: Insights.

  • Con la base de datos de bajas se realizó el clúster de edad y duración, en la cual se tomaron en cuenta las variables de la edad de los colaboradores y la duración que han tenido en la empresa. Se buscó el número óptimo de clúster y se tomaron en cuenta los primeros 4. En este podemos observar que las personas con edad mediana-alta no tienen una duración de largo tiempo en la empresa ya que forman parte del 25% con menor duración; otro dato que observamos que las personas con mayor edad han durado más tiempo en Form y forman parte del superior 50%, y por último observamos que existe una mayor rotación de personal en los colaboradores jóvenes.
  • En el análisis que se realizó mediante gráficas, obtuvimos que en los clúster, que la persona con mayor edad en los expert tiene 61, en senior tiene 57, en junior tiene 39 y en beginnner 27, también que en la empresa la mayoría de los colaboradores corresponden al clúster de beginner.
  • En cuanto al análisis de la duración de los colaboradores en la empresa y su salario encontramos estos puntos mas importantes; a) la mayoría de los empleados ganan arriba del salario diario promedio establecido por el gobierno, este oscila entre $150-$2000 pesos diarios, b) los colaboradores que tienen un tiempo medio en la empresa aproximadamente de un año tiene un salario promedio de $169, mientras los que duran menos tiempo (alrededor de 141 días) tienen un salario promedio de alrededor de $180.
  • El clúster mas grande es el llamado “Rehnes” tiene un total de colaboradores de 191, y en porcentaje de bajas se puede decir que es el 81.97% total de bajas, estos tiene la característica de son las personas que entran y salen muy pronto y tienen un salario por arriba de todos los demás clúster de $180 pesos, por lo que, en este clúster se necesita aplicar una estrategia de fidelización de los colaboradores de otra forma que no sea con un aumento de salario, es decir, Form puede implementar estrategias con recompensas que sean a largo plazo.
  • Como se puede observar en el analisis que se realizó de edad y salario se pueden destacar los siguientes puntos:
  1. el cluster que tiene una mayor frecuencia es el de 48 años de edad,
  2. el cluster que mas chico es de 22 años o menos por lo que podemos decir que en Form, la mayoría son mayores de 23 años,
  3. en cuanto al salario tienen un promedio de 180.68, pero existe un dato único el cual ganaba 500.
    En conclusión para Form podemos plantear una estrategia de estandarización de salarios de acuerdo al tiempo que llevan en la empresa y su edad, para poder así tener un mejor control de gastos de la empresa y aplicar nuevas a estrategias a cada uno de los cluster.
LS0tCnRpdGxlOiA8c3BhbiBzdHlsZT0iY29sb3I6b3JhbmdlIj4qKkVudHJlZ2FibGUgMi42KioKYXV0aG9yOiAiRWxlbmEgVmVsYSAoQTAxMjgzNTM1KSwgU2ViYXN0aWFuIEdhcnphIChBMDE2Mzg4NjMpLCBJc2FhYyBEw61heiAoQTAxNTQwNTQzKSwgQW5hIENyaXN0aW5hIEFydml6dSAoQTAxNDEyMjIwKSwgIEdlb3JnaW5hIE1hcnTDrW5leiAoQTAwODI3NTAwKSIKZGF0ZTogIjIwMjItMTAtMDUiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQo8aW1nIHNyYz0iL1VzZXJzL2VsZW5hdmVsYS9EZXNrdG9wL0NhcHR1cmEgZGUgUGFudGFsbGEgMjAyMi0xMC0wOCBhIGxhKHMpIDE0LjMwLjQ1LnBuZyI+ICAKCgojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmRhcmtyZWQiPioqUHJpbWVyIHB1bnRvOiBEZWZpbmlyIGNvbmNlcHRvcy4qKgoqKjEuIEsgLSBtZWFucyBDbHVzdGVyaW5nKiogIApLLW1lYW5zIGVzIHVuIG3DqXRvZG8gZGUgYWdydXBhbWllbnRvLCBxdWUgdGllbmUgY29tbyBvYmpldGl2byBsYSBwYXJ0aWNpw7NuIGRlIHVuIGNvbmp1bnRvIGRlIG9ic2VydmFjaW9uZXMgZW4gayBncnVwb3MgZW4gZWwgcXVlIGNhZGEgb2JzZXJ2YWNpw7NuIHBlcnRlbmVjZSBhbCBncnVwbyBjdXlvIHZhbG9yIG1lZGlvIGVzIG3DoXMgY2VyY2Fuby4gIERlIGVzdGEgbWFuZXJhIGVzIHF1ZSBzZSBwdWVkZSBhbmFsaXphciBjYWRhIENsdXN0ZXIsIG9idGVuaWVuZG8gaW5mb3JtYWNpw7NuIGltcG9ydGFudGUgZGUgY2FkYSB2YXJpYWJsZSB5IGxhcyBzaW1pbGl0dWRlcyBlbnRyZSBlbGxhcy4gVW4gY2xhcm8gZWplbXBsbyBkZSBjw7NtbyBmdW5jaW9uYSBlcyB1c2FyIGNsdXN0ZXJpbmcgcGFyYSBjb21wcmltaXIgaW3DoWdlbmVzIGNvbiBww6lyZGlkYSBkZSBpbmZvcm1hY2nDs24geS9vIGNvbXByZW5kZXIgcXXDqSBoYWNlIGEgbG9zIGNsaWVudGVzIGRpZmVyZW50ZXMgcGFyYSBwb2RlciBvZnJlY2VybGVzIGxvcyBwcm9kdWN0b3MgeSBzZXJ2aWNpb3MgcXVlIG5lY2VzaXRlbi4gIAogIAoKKioyLiBVbnN1cGVydmlzZWQgTGVhcm5pbmcqKiAgIApFbCDigJxVbnN1cGVydmlzZWQgTGVhcm5pbmfigJ0gdXRpbGl6YSBhbGdvcml0bW9zIGRlIGFwcmVuZGl6YWplIGF1dG9tw6F0aWNvIHBhcmEgYW5hbGl6YXIgeSBhZ3J1cGFyIGNvbmp1bnRvcyBkZSBkYXRvcyBzaW4gZXRpcXVldGFyLiBFc3RvcyBhbGdvcml0bW9zIGRlc2N1YnJlbiBwYXRyb25lcyBvY3VsdG9zIG8gYWdydXBhY2lvbmVzIGRlIGRhdG9zIHNpbiBuZWNlc2lkYWQgZGUgaW50ZXJ2ZW5jacOzbiBodW1hbmEuIE5vIHNlIGFzaWduYW4gZXRpcXVldGFzIGFsIGFsZ29yaXRtbyBkZSBhcHJlbmRpemFqZSwgZGVqw6FuZG9sbyBzb2xvIHBhcmEgZW5jb250cmFyIGxhIGVzdHJ1Y3R1cmEgZW4gc3UgZW50cmFkYS4gRWwgVW5zdXBlcnZpc2VkIExlYXJuaW5nIHB1ZWRlIHNlciB1biBvYmpldGl2byBlbiBzw60gbWlzbW8gKGRlc2N1YnJpciBwYXRyb25lcyBvY3VsdG9zIGVuIGxvcyBkYXRvcykgbyB1biBtZWRpbyBwYXJhIGxvZ3JhciB1biBmaW4gKGFwcmVuZGl6YWplIGRlIGZ1bmNpb25lcykuICAKICAKCioqMy4gRGlzdGFuY2lhIEV1Y2xpZGlhbmEgLyBFdWNsaWRlYW4gRGlzdGFuY2UqKiAgCkxhIGRpc3RhbmNpYSBldWNsaWRpYW5hIHNlIGRlZmluZSBjb21vIGxhIGRpc3RhbmNpYSBlbnRyZSBkb3MgcHVudG9zLiBMYSBkaXN0YW5jaWEgZXVjbGlkaWFuYSBlbnRyZSBkb3MgcHVudG9zIGVuIGVsIGVzcGFjaW8gZXVjbGlkaWFubyBzZSBkZWZpbmUgY29tbyBsYSBsb25naXR1ZCBkZWwgc2VnbWVudG8gZGUgbMOtbmVhIGVudHJlIGRvcyBwdW50b3MuIExhIGhlcnJhbWllbnRhIERpc3RhbmNpYSBldWNsaWRpYW5hIHNlIHV0aWxpemEgY29uIGZyZWN1ZW5jaWEgY29tbyB1bmEgaGVycmFtaWVudGEgaW5kZXBlbmRpZW50ZSBwYXJhIGFwbGljYWNpb25lcywgY29tbyBlbmNvbnRyYXIgZWwgaG9zcGl0YWwgbcOhcyBjZXJjYW5vIHBhcmEgdW4gdnVlbG8gZGUgZW1lcmdlbmNpYSBlbiBoZWxpY8OzcHRlcm8uIEFsdGVybmF0aXZhbWVudGUsIGVzdGEgaGVycmFtaWVudGEgc2UgcHVlZGUgdXNhciBhbCBjcmVhciB1biBtYXBhIGRlIGlkb25laWRhZCwgY3VhbmRvIHNlIG5lY2VzaXRhbiBkYXRvcyBxdWUgcmVwcmVzZW50YW4gbGEgZGlzdGFuY2lhIGRlc2RlIHVuIG9iamV0byBkZXRlcm1pbmFkby4gIAogIAogIAojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmRhcmtyZWQiPioqU2VndW5kbyBwdW50bzogR2VuZXJhciAgQ2x1c3RlcnMuKioKICAKKkRlc2NhcmdhciBsaWJyZXLDrWFzKgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGZvcmVpZ24pCmxpYnJhcnkoZHBseXIpICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uIApsaWJyYXJ5KGdncGxvdDIpICAgICAgIyBkYXRhIHZpc3VhbGl6YXRpb24gCiNpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpCmxpYnJhcnkocHN5Y2gpICAgICAgICAjIGZ1bmN0aW9ucyBmb3IgbXVsdGl2YXJpYXRlIGFuYWx5c2lzIApsaWJyYXJ5KGNvcnJwbG90KSAgICAgIyBjb3JyZWxhdGlvbiBwbG90cwpsaWJyYXJ5KGp0b29scykgICAgICAgIyBwcmVzZW50YXRpb24gb2YgcmVncmVzc2lvbiBhbmFseXNpcyAKbGlicmFyeShsbXRlc3QpICAgICAgICMgZGlhZ25vc3RpYyBjaGVja3MgLSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcyAKbGlicmFyeShjYXIpICAgICAgICAgICMgZGlhZ25vc3RpYyBjaGVja3MgLSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcwpsaWJyYXJ5KGZhY3RvZXh0cmEpICAgIyBwcm92aWRlcyBmdW5jdGlvbnMgdG8gZXh0cmFjdCBhbmQgdmlzdWFsaXplIHRoZSBvdXRwdXQgb2YgZXhwbG9yYXRvcnkgbXVsdGl2YXJpYXRlIGRhdGEgYW5hbHlzZXMKI2luc3RhbGwucGFja2FnZXMoImdnZm9ydGlmeSIpCmxpYnJhcnkoZ2dmb3J0aWZ5KSAgICAjIGRhdGEgdmlzdWFsaXphdGlvbiB0b29scyBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMgcmVzdWx0cwpgYGAKCipTZSBpbXBvcnRhIGxhIGJhc2UgZGUgZGF0b3MgZGUgQkFKQVMgeWEgbGltcGlhLioKYGBge3J9CiNmaWxlLmNob29zZSgpCmJhamFzPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9iYWphc19maW5hbC5jc3YiKQpzdW1tYXJ5KGJhamFzKQpgYGAKCsK/Q3XDoW50b3MgTkEgIHRlbmdvIHBvciB2YXJpYWJsZXM/CmBgYHtyfQpzYXBwbHkoYmFqYXMsZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgCgojIyMjICoqMS4gRWRhZCB5ICBkdXJhY2nDs24gLSA0IENsdXN0ZXJzKioKYGBge3J9CmJhamFzX25ldzwtYmFqYXMKYmFqYXNfbmV3PC1zdWJzZXQoYmFqYXNfbmV3LHNlbGVjdCA9IC1jKGdlbmVybyxhbHRhLG1vdGl2b19iYWphLHB1ZXN0byxlc3RhZG8sbm9tYnJlKSkKc3VtbWFyeShiYWphc19uZXcpCmBgYAoKU2UgdG9tYW4gZW4gY3VlbnRhIGxhIGVkYWQgeSBsYSBkdXJhY2nDs24gZGVudHJvIGRlIGxhIGVtcHJlc2EuIApgYGB7cn0KYmFqYXNfZWRhZGR1cl9ub3JtPC1zY2FsZShiYWphc19uZXdbMToyXSkgCmBgYAoKYGBge3J9CmZ2aXpfbmJjbHVzdChiYWphc19lZGFkZHVyX25vcm0sIGttZWFucywgbWV0aG9kPSJ3c3MiKSsgCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTQsIGxpbmV0eXBlPTIpKyAgICAgICAgIAogIGxhYnMoc3VidGl0bGUgPSAiRWxib3cgbWV0aG9kIikgIApgYGAKClNlIHRvbWFuIGVuIGN1ZW50YSwgcHJpbWVyYW1lbnRlLCBzb2xvIDQgY2x1c3RlcnMuCgpgYGB7cn0KZWRhZF9jbHVzdGVyMTwta21lYW5zKGJhamFzX2VkYWRkdXJfbm9ybSw0KQplZGFkX2NsdXN0ZXIxCmBgYAoKYGBge3J9CmZ2aXpfY2x1c3RlcihlZGFkX2NsdXN0ZXIxLGRhdGE9YmFqYXNfZWRhZGR1cl9ub3JtKQpgYGAKCkFsIG9ic2VydmFyIHZlbW9zIGN1YXRybyBjbHVzdGVycyBxdWUgdG9tYW4gZW4gY3VlbnRhIGxhIGVkYWQgeSBsYSBkdXJhY2nDs246ICAKMS4gRWwgcm9qbyAoc2Vyw6EgbGxhbWFkbyAiU2VuaW9yIikgZXMgdW4gY2x1c3RlciBxdWUgb2JzZXJ2YW1vcyBxdWUgc29uIHBlcnNvbmFzIGRlIGVkYWQgbWVkaWFuYS1hbHRhIHkgcXVlIGR1csOhbiByZWxhdGl2YW1lbnRlIHBvY28gdGllbXBvLCBlbnRyYW4gZGVudHJvIGRlbCAyNSUgY29uIG1lbm9yIGR1cmFjacOzbi4gICAKMi4gRWwgdmVyZGUgKCJFeHBlcnQiKSBlcyBlbCBjbHVzdGVyIHF1ZSBtw6FzIHNlIGFwYXJ0YSBkZSBsb3MgZGVtw6FzOyBzb24gcGVyc29uYXMgY29uIHVuIG1heW9yIHJhbmdvIGRlIGVkYWQgcGVybyBxdWUgc2UgYWxlamFuIGRlIGxvcyBtw6FzIGrDs3ZlbmVzLCB5ICBxdWUgaGFuIGR1cmFkbyBtdWNobyBtw6EgdGllbXBvLiBTZSBlbmN1ZW50cmFuIGVuIGVsIDUwJSBzdXBlcmlvciBkZWwgdGllbXBvIGRlIGR1cmFjacOzbi4gIAozLiBFbCBhenVsICgianVuaW9yIikgcmVzdWx0YSBlbCBjbHVzdGVyIGludGVybWVkaW8gZGUgbG9zIGRlIG1lbm9yIGR1cmFjacOzbi4gU29uIHBlcnNvbmFzIGVuICB1biByYW5nbyBkZSBlZGFkIG1lZGlvIHF1ZSB0YW1iacOpbiBzb24gcGFydGUgZGVsIDI1JSBpbmZlcmlvciBxdWUgbcOhcyBoYW4gZHVyYWRvLiAgCjQuIEVsIG1vcmFkbyAoImJlZ2lubmVyIikgZXMgZWwgc2VnbWVudG8gbcOhcyBqb3ZlbiB5IHF1ZSBkZSBsYXMgcGVyc29uYXMgKGVuIGdlbmVyYWwpIG1lbm9zIGhhbiBkdXJhZG8uIFNlIHB1ZWRlIGNvbnNpZGVyYXIgcXVlIGxvcyBqw7N2ZW5lcyBzb24gbG9zIHF1ZSBtZW5vcyBkdXJhbiBlbiBGb3JtIGEgcGFydGlyIGRlIGVzdGUgY2x1c3Rlci4gCgpgYGB7cn0KYmFqYXNfbmV3MjwtYmFqYXNfbmV3CmJhamFzX25ldzIkQ2x1c3RlcnM8LWVkYWRfY2x1c3RlcjEkY2x1c3RlcgpiYWphc19uZXczPC1iYWphc19uZXcyICU+JSBncm91cF9ieShDbHVzdGVycykgJT4lIHN1bW1hcmlzZShlZGFkPW1heChlZGFkKSkgJT4lIGFycmFuZ2UoZGVzYyhlZGFkKSkKYmFqYXNfbmV3MiRDbHVzdGVyX05hbWVzPC1mYWN0b3IoYmFqYXNfbmV3MiRDbHVzdGVycyxsZXZlbHMgPSBjKDEsMiwzLDQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlNlbmlvciIsICJFeHBlcnQiLCAiSnVuaW9yIiwgIkJlZ2lubmVyIikpCmBgYAoKYGBge3J9CmJhamFzX25ldzQgPC0gYmFqYXNfbmV3MiAlPiUgZ3JvdXBfYnkoQ2x1c3Rlcl9OYW1lcykgJT4lIHN1bW1hcml6ZShlZGFkX2HDsW9zPW1heChlZGFkKSwgCmR1cmFjaW9uPW1lYW4oZHVyYWNpb24pLApDb3VudD1uKCkpCmBgYAoKCmBgYHtyfQpjbHVzdGVyczwtYXMuZGF0YS5mcmFtZShiYWphc19uZXc0KQpjbHVzdGVycwpgYGAKCkEgcGFydGlyIGRlIGxhIHRhYmxhLCBvYnNlcnZhbW9zIGxhcyBlZGFkZXMgbcOheGltYXMgZW5jb250cmFkYXMgZW4gY2FkYSBjbMO6c3Rlci4gQ29ub2NlbW9zIHF1ZSBsYSBwZXJzb25hIGNvbiBtYXlvciBlZGFkIGVuIGxvcyAqZXhwZXJ0KiB0aWVuZSA2MSwgZW4gKnNlbmlvciogdGllbmUgNTcsIGVuICpqdW5pb3IqIHRpZW5lIDM5IHkgZW4gKmJlZ2lubm5lciogMjcuICAgCgpJZ3VhbG1lbnRlLCB2ZW1vcyBsYSBkdXJhY2nDs24gbcOheGltYSBxdWUgaGEgZHVyYWRvIGFsZ3VpZW4gZGUgY2FkYSBjbHVzdGVyLCBjb21wYXJhbW9zIGVsIG3DoXhpbW8gZGVsIG1heW9yLCAqZXhwZXJ0KiwgcXVlIGhhbiBzaWRvIG3DoXMgZGUgMSw1MDAgZMOtYXMsIGNvbiBlbCBtw6F4aW1vIGRlbCBtZW5vciwgKmJlZ2lubmVyKiwgcXVlIGhhbiBzaWRvIDQ2IGTDrWFzLiAKCmBgYHtyfQpnZ3Bsb3QoYmFqYXNfbmV3NCxhZXMoeD1yZW9yZGVyKENsdXN0ZXJfTmFtZXMsQ291bnQpLHk9Q291bnQsZmlsbD1DbHVzdGVyX05hbWVzKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKYGBgCgpFeGlzdGUgdW5hIG1heW9yIGNhbnRpZGFkIGRlICJiZWdpbm5lcnMiLgoKCmBgYHtyfQpnZ3Bsb3QoYmFqYXNfbmV3NCwgYWVzKHg9Q2x1c3Rlcl9OYW1lcyx5PWVkYWRfYcOxb3MsZmlsbD0gQ2x1c3Rlcl9OYW1lcyxsYWJlbD1yb3VuZChlZGFkX2HDsW9zLGRpZ2l0cz0yKSkpICsgCiAgZ2VvbV9jb2woKSArIAogIGdlb21fdGV4dCgpCmBgYAoKQ29tcGFyYW1vcyBsYSBlZGFkIG3DoXhpbWEgZW4gY2FkYSBjbMO6c3RlciAoZGVzY3JpdG8gYW50ZXJpb3JtZW50ZSkuCgpgYGB7cn0KZ2dwbG90KGJhamFzX25ldzQsYWVzKHg9Q2x1c3Rlcl9OYW1lcyx5PWR1cmFjaW9uLGZpbGw9IENsdXN0ZXJfTmFtZXMsbGFiZWw9cm91bmQoZHVyYWNpb24sZGlnaXRzPTIpKSkgKyAKICBnZW9tX2NvbCgpICsgCiAgZ2VvbV90ZXh0KCkKYGBgCgpDb21wYXJhbW9zIGxvcyBkw61hcyBtw6F4aW1vcyBlbiBjYWRhIGNsw7pzdGVyIChkZXNjcml0byBhbnRlcmlvcm1lbnRlKS4KCgoKCgojIyMjICoqMi4gRWRhZCB5ICBkdXJhY2nDs24gLSA1IENsdXN0ZXJzKioKCkRlYmlkbyBhIHF1ZWUgdW5hIGdyw6FmaWNhIGFudGVyaW9yIGluZGljYWJhIGxhIHBvc2liaWxpZGFkIGRlIHRlbmVyIG90cm8gY2zDunN0ZXIgcXVlIGRlc2NyaWJhIGEgb3RybyBncnVwbywgaGFjZW1vcyBsbyBtaXNtbyBjb24gNSBzZWdtZW50b3MsIGVuIHZleiBkZSA0LiAKCmBgYHtyfQpiYWphc19lZGFkZHVyX25vcm0yPC1zY2FsZShiYWphc19uZXdbMToyXSkgCmBgYAoKYGBge3J9CmZ2aXpfbmJjbHVzdChiYWphc19lZGFkZHVyX25vcm0yLCBrbWVhbnMsIG1ldGhvZD0id3NzIikrIAogIGdlb21fdmxpbmUoeGludGVyY2VwdD00LCBsaW5ldHlwZT0yKSsgICAgICAgICAKICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpICAKYGBgCgpgYGB7cn0KZWRhZF9jbHVzdGVyMjwta21lYW5zKGJhamFzX2VkYWRkdXJfbm9ybSw1KQplZGFkX2NsdXN0ZXIyCmBgYAoKYGBge3J9CmZ2aXpfY2x1c3RlcihlZGFkX2NsdXN0ZXIyLGRhdGE9YmFqYXNfZWRhZGR1cl9ub3JtMikKYGBgCgpDb21wYXJhbmRvIGVzdGEgIGdyw6FmaWNhIGRlIDUgc2VnbWVudG9zIGNvbnRyYSBsYSBhbnRlcmlvcm1lbnRlIHZpc3RhIGRlIDQuIEVuIGVzdGEgb2JzZXJ2YW1vcyBxdWUgc2VwYXJhIHVuIHNlZ21lbnRvIHF1ZSBkZWphIHVuICJlc3BhY2lvIiBxdWUgYW50ZXMgbm8gZXhpc3TDrWE7IHNlcGFyYSB5IGNyZWEgdW4gc2VnbWVudG8gcXVlIGhhbiBkdXJhZG8gYXByb3ggdW4gMTAlIG3DoXMgZGUgdGllZW1wbyBxdWUgbGFzIHRyZXMgbWVub3JlcywgeSBxdWUgc2UgZW5jdWVudHJhIGVuIHJhbmdvcyBkZSBlZGFkIHBhcmVjaWRvcy4gQSBjb250aW51YWNpw7NuIHNlIGNvbXBhcmFuIGxvcyBjbHVzdGVyczogIAoxLiBFbCByb2pvIChsbGFtYWRvICJTZW5pb3IiKTogZXMgZWwgbnVldm8sIGVsIGN1YWwgdGllbmUgZGUgYmFqYSBhIG1lZGlhbmEgZHVyYWNpw7NuIHkgdW4gcmFuZ28gZGUgZWRhZCBtZWRpYW5hLiBEZXNwdcOpcyBkZSBsb3MgcXVlIGhhbiBkdXJhZG8gbXVjaG8sIGVsbG9zIHZhbiBkZXNwdcOpcy4gIAogIAoyLiBFbCB2ZXJkZSBzdXBlcmlvciAobGxhbWFkbyAiRXhwZXJ0Iik6IGVzIGVsIGNsdXN0ZXIgcXVlIG3DoXMgc2UgYXBhcnRhIGRlIGxvcyBkZW3DoXM7IHNvbiBwZXJzb25hcyBjb24gdW4gbWF5b3IgcmFuZ28gZGUgZWRhZCBwZXJvIHF1ZSBzZSBhbGVqYW4gZGUgbG9zIG3DoXMgasOzdmVuZXMsIHkgIHF1ZSBoYW4gZHVyYWRvIG11Y2hvIG3DoSB0aWVtcG8uIFNlIGVuY3VlbnRyYW4gZW4gZWwgNTAlIHN1cGVyaW9yIGRlbCB0aWVtcG8gZGUgZHVyYWNpw7NuLiAgCiAgCjMuIEVsIHZlcmRlIGluZmVyaW9yIChsbGFtYWRvICJKdW5pb3IiKSBzb24gcGVyc29uYXMgYWR1bHRhcyBxdWUgaGFuIGR1cmFkbyBwb2NvIHRpZW1wbyBlbiBsYSBlbXByZXNhLCBwYXJ0ZSBkZWwgMTclIGluZmVyaW9yIGRlIGR1cmFjacOzbi4gICAKICAgIAo0LiBFbCBtb3JhZG8gKGxsYW1hZG8gIk1pZGRsZSIpOiBhbCBoYWNlciBlbCBudWV2byBjbMO6c3RlciBub3MgZGFtb3MgY3VlbnRhIGRlIHF1ZSBleGlzdGUgdW4gZ3J1cG8gZGUgcGVyc29uYXMgZGUgbWVkaWFuYSBlZGFkIHF1ZSBkdXJhbiAqKm11eSoqIHBvY28sIGHDum4gbcOhcyBwb2NvIHF1ZSBsb3MgasOzdmVuZXMuICpJbnZlc3RpZ2FyIGVzdGFkbyBjaXZpbCouICAKICAKNS4gRWwgYXp1bCAobGxhbWFkbyAiQmVnaW5uZXIiKTogc29uIGxhcyBwZXJzb25hcyBtw6FzIGrDs3ZlbmVzIHkgcXVlIHRhbWJpw6luIGhhbiBkdXJhZG8gcG9jbyB0aWVtcG8uIFNlIHF1ZWRhbiBhcHJveGltYWRhbWVudGUgcG9yIGVsIDEwJSBpbmZlcmlvci4gIAoKCmBgYHtyfQpiYWphc19uZXc2PC1iYWphc19uZXcKYmFqYXNfbmV3NiRDbHVzdGVyczwtZWRhZF9jbHVzdGVyMiRjbHVzdGVyCmJhamFzX25ldzc8LWJhamFzX25ldzYgJT4lIGdyb3VwX2J5KENsdXN0ZXJzKSAlPiUgc3VtbWFyaXNlKGVkYWQ9bWF4KGVkYWQpKSAlPiUgYXJyYW5nZShkZXNjKGVkYWQpKQpiYWphc19uZXc2JENsdXN0ZXJfTmFtZXM8LWZhY3RvcihiYWphc19uZXc2JENsdXN0ZXJzLGxldmVscyA9IGMoMSwyLDMsNCw1KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJTZW5pb3IiLCAiRXhwZXJ0IiwgIkp1bmlvciIsICJNaWRkbGUiLCJCZWdpbm5lciIpKQpgYGAKCkFsIGFuYWxpemFyIGVsIGVzdGFkbyBjaXZpbCBkZSBsb3MgNSBjbMO6c3RlcnMsIGxsZWdhbW9zIGEgbGFzIHNpZ3VpZW50ZXMgY29uY2x1c2lvbmVzOiAgCjEuIERlbCBzZWdtZW50byAiU2VuaW9yIiwgZWwgNzUlIHNvbiBzb2x0ZXJvcy4gRXN0ZSBlcyBlbCBudWV2byBjbMO6c3RlciBkZXNjdWJpZXJ0by4gIAoyLiBFbCA1NSUgZGUgbGFzIHBlcnNvbmFzIGVuIGVsIGNsdXN0ZXIgIk1pZGRsZSIgKG1lbm9yIGR1cmFjacOzbikgc29uIHNvbHRlcmFzLCBlbCAyNiUgdml2ZW4gZW4gdW5pw7NuIGxpYnJlLCB5IHVuIDE5JSBxdWUgdml2ZW4gZW4gbWF0cmltb25pby4gIAozLiBEZSBsb3MgZXhwZXJ0b3MsIGVsIDc1JSBzb24gc29sdGVyb3MuICAgCgojIyMjICoqMy4gRHVyYWNpw7NuIHkgc2FsYXJpbyoqCgoqSW1wb3J0YXIgYmFzZSBkZSBkYXRvcyoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmhfYmFqYXMgPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9iYWphc19maW5hbC5jc3YiKSAKYGBgCgrCv0N1w6FudG9zIE5BICB0ZW5nbyBwb3IgdmFyaWFibGVzPwpgYGB7cn0Kc2FwcGx5KHJoX2JhamFzLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAoKCkJhc2UgZGUgZGF0b3MgY29uIHZhbG9yZXMgQ1VBTlRJVEFUSVZPUwpgYGB7cn0KcmhfYmFqYXMyPC1yaF9iYWphcwpyaF9iYWphczI8LXN1YnNldChyaF9iYWphczIsc2VsZWN0ID0gLWMoZ2VuZXJvLGFsdGEsbW90aXZvX2JhamEscHVlc3RvLGVzdGFkbyxub21icmUpKQpzdW1tYXJ5KHJoX2JhamFzMikKYGBgCgpOb3JtYWxpemFuZG8gbGFzIHZhcmlhYmxlcwpgYGB7cn0KYmFqYXNfZHVyYWNpb25fbm9ybTwtc2NhbGUocmhfYmFqYXMyWzI6M10pIApgYGAKCkVsYm93IFBsb3QKYGBge3J9CmZ2aXpfbmJjbHVzdChiYWphc19kdXJhY2lvbl9ub3JtLCBrbWVhbnMsIG1ldGhvZD0id3NzIikrIAogIGdlb21fdmxpbmUoeGludGVyY2VwdD01LCBsaW5ldHlwZT0yKSsgICAgICAgICAKICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpICAKYGBgCgpWaXN1YWxpemFjacOzbiBpbmZvcm1hY2nDs24gZGUgY2x1c3RlcnMKYGBge3J9CmR1cmFjaW9uX2NsdXN0ZXIxPC1rbWVhbnMoYmFqYXNfZHVyYWNpb25fbm9ybSw1KQpkdXJhY2lvbl9jbHVzdGVyMQpgYGAKClZpc3VhbGl6YWNpw7NuIGNsdXN0ZXJpbmcgcmVzdWx0YWRvcyAKYGBge3J9CmZ2aXpfY2x1c3RlcihkdXJhY2lvbl9jbHVzdGVyMSxkYXRhPWJhamFzX2R1cmFjaW9uX25vcm0pCmBgYAoKQWwgb2JzZXJ2YXIgdmVtb3MgNSBjbHVzdGVycyBxdWUgdG9tYW4gZW4gY3VlbnRhIGxhIGR1cmFjacOzbiB5IGVsIHNhbGFyaW86ICAKCjEuIFByaW1lcm8gdmVtb3MgbnVlc3RybyBjbHVzdGVyIDEgKHNlcsOhIGxsYW1hZG8gIlJlaGVuZXMiKSBlcyB1biBjbHVzdGVyIHF1ZSBvYnNlcnZhbW9zIHF1ZSBzb24gcGVyc29uYXMgcXVlIHNvbG8gZW50cmFuIHkgc2FsZW4gbXV5IHByb250byBkZSBsYSBlbXByZXNhICgxNDEgZMOtYXMpIHkgdGllbmVuIHVuIHNhbGFyaW8gZGlhcmlvIHF1ZSBlcyBhcnJpYmEgZGVsIHByb21lZGlvLCBhbHJlZGVkb3IgZGUgJDE4MCBwZXNvcy4gICAKCjIuIEVsIGNsdXN0ZXIgMiAoIkR1cmFkZXJvcyIpIHNvbiBsb3MgY29sYWJvcmFkb3JlcyBxdWUgdGllbmVuIHVuYSBkdXJhY2nDs24gcmVndWxhciwgcGVybyBxdWUgYXVuIGFzw60gbm8gc2UgZW5jdWVudHJhbiBzYXRpc2ZlY2hvcyBwb3IgbG8gcXVlIHNlIHNhbGVuIGRlIGxhIGVtcHJlc2EgYWwgcmVkZWRvciBkZXNwdcOpcyBkZSAxIGHDsW8geSBtZWRpbyAoNjQ2IGTDrWFzKSB5IHN1IHNhbGFyaW8gZXMgZGUgJDE2OS4gIAoKMy4gQ29tbyBjbHVzdGVyIDMgdGVuZW1vcyBhIGxvcyBjb2xhYm9yYWRvcmVzIHF1ZSBzb24gbGVhbGVzIGEgbGEgZW1wcmVzYSAoIkZpZWxlcyIpIGVzIGVsIHNlZ21lbnRvIGRvbmRlIHlhIHRpZW5lbiBtw6FzIGRlIDQtNSBhw7FvcyB0cmFiYWphbmRvIHkgY3VlbnRhbiBjb24gdW4gc2FsYXJpbyBkZSAkMTY4IHBlc29zLiAgCgo0LiBFbCBjbHVzdGVyIDQgKCJDb3N0b3NvcyIpIGVzIGVsIGNsdXN0ZXIgcXVlIG5vcyBjdWVzdGEgYSBsYSBlbXByZXNhIHBvcnF1ZSBzZSBpbnZpZXJ0ZSBlbiBjYXBhY2l0YWNpb25lcyBwYXJhIGVsbG9zIHkgdGVybWluYW4gc2FsaWVuZG8gZGUgbGEgZW1wcmVzYTsgc29uIHBlcnNvbmFzIGNvbiB1biBwb2NvIG1hcyBkZSBkdXJhY2nDs24gcGVybyBubyBsYSBzdWZpY2llbnRlICgxOTcgZMOtYXMpIHkgc3Ugc2FsYXJpbyBlcyBkZSAkMTUxIHBvciBkZWJham8gcHJvbWVkaW8uICAKCjUuIFBvciDDumx0aW1vIHRlbmVtb3MgZWwgY2x1c3RlciA1ICgiRXh0cmEiKSBxdWUgdHV2byBkdXJhY2nDs24gY29ydG8gcGxhem8gY29uIHVuIHNhbGFyaW8gbXV5IGFsdG8gKCQ1MDAgcGVzb3MpLiAgCiAKCgpOb21icmFyIGEgbG9zIGNsdXN0ZXJzIApgYGB7cn0KcmhfYmFqYXMzPC1yaF9iYWphczIKcmhfYmFqYXMzJENsdXN0ZXJzPC1kdXJhY2lvbl9jbHVzdGVyMSRjbHVzdGVyCnJoX2JhamFzNDwtcmhfYmFqYXMzICU+JSBncm91cF9ieShDbHVzdGVycykgJT4lIHN1bW1hcmlzZShkdXJhY2lvbj1tYXgoZHVyYWNpb24pKSAlPiUgYXJyYW5nZShkZXNjKGR1cmFjaW9uKSkKcmhfYmFqYXMzJENsdXN0ZXJfTmFtZXM8LWZhY3RvcihyaF9iYWphczMkQ2x1c3RlcnMsbGV2ZWxzID0gYygxLDIsMyw0LDUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlJlaGVuZXMiLCAiRHVyYWRlcm9zIiwiRmllbGVzIiwgIkNvc3Rvc29zIiwiRXh0cmEiKSkKYGBgCgpFbmdydXBhciBsb3MgY2x1c3RlcnMgcG9yIG5vbWJyZSBkZSBjbHVzdGVycyB5IHJlc3VtaXIgY29sdW1uYXMKYGBge3J9CnJoX2JhamFzNTwtIHJoX2JhamFzMyAlPiUgZ3JvdXBfYnkoQ2x1c3Rlcl9OYW1lcykgJT4lIHN1bW1hcml6ZShkdXJhY2lvbl9kaWFzPW1heChkdXJhY2lvbiksIApzYWxhcmlvX2RpYXJpbz1tZWFuKHNhbGFyaW9fZGlhcmlvKSwKQ291bnQ9bigpKQpgYGAKCgpGb3JtYXRvIHRhYmxhIHBhcmEgaW5mb3JtYWNpw7NuIGRlIGxvcyBjbHVzdGVycyAKYGBge3J9CmNsdXN0ZXJzPC1hcy5kYXRhLmZyYW1lKHJoX2JhamFzNSkKY2x1c3RlcnMKYGBgCgoKR3LDoWZpY2EgY29uIGVsIG51bWVybyBkZSBvYnNlcnZhY2lvbmVzIHBvciBub21icmVzIGRlIGNsdXN0ZXJzIApgYGB7cn0KZ2dwbG90KHJoX2JhamFzNSxhZXMoeD1yZW9yZGVyKENsdXN0ZXJfTmFtZXMsQ291bnQpLHk9Q291bnQsZmlsbD1DbHVzdGVyX05hbWVzKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKYGBgCgpgYGB7cn0KZ2dwbG90KHJoX2JhamFzNSwgYWVzKHg9Q2x1c3Rlcl9OYW1lcyx5PWR1cmFjaW9uX2RpYXMsZmlsbD0gQ2x1c3Rlcl9OYW1lcyxsYWJlbD1yb3VuZChkdXJhY2lvbl9kaWFzLGRpZ2l0cz0yKSkpICsgCiAgZ2VvbV9jb2woKSArIAogIGdlb21fdGV4dCgpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QocmhfYmFqYXM1LGFlcyh4PUNsdXN0ZXJfTmFtZXMseT1zYWxhcmlvX2RpYXJpbyxmaWxsPSBDbHVzdGVyX05hbWVzLGxhYmVsPXJvdW5kKHNhbGFyaW9fZGlhcmlvLGRpZ2l0cz0yKSkpICsgCiAgZ2VvbV9jb2woKSArIAogIGdlb21fdGV4dCgpCmBgYAoKRXN0w6FzIHVsdGltYXMgZ3LDoWZpY2FzIG5vcyBtdWVzdHJhbiBsbyBxdWUgc2UgZGVzY3JpYmnDsyBhbnRlcmlvcmltZW50ZSBzb2JyZSBxdWUgZHVyYWNpw7NuIHkgc2FsYXJpbyB0aWVuZSBjYWRhIGNsdXN0ZXIgeSBwYXJhIGNvbmNsdWlyIHNlIG1vc3RyYXJhIGN1YW50b3MgY29sYWJvcmFkb3JlcyB0aWVuZSBjYWRhIGNsdXN0ZXI6CgpUb3RhbCAyMzMgCiogKioiUmVoZW5lcyIqKiB0aWVuZSBlbCBzZWdtZW50byBjb24gbcOhcyBjb2xhYm9yYWRvcmVzIHkgdGllbmUgdW4gdG90YWwgZGUgMTkxLiBFbiBwb3JjZW50YWplIHNlIHB1ZWRlIGRlY2lyIHF1ZSBlcyBlbCA4MS45NyUgdG90YWwgZGUgYmFqYXMuICAKCiogKioiQ29zdG9zb3MiKiogZXN0YSBlbiBzZWd1bmRvIGx1Z2FyIHRlbmllbmRvIDI3IGNvbGFib3JhZG9yZXMgKDExLjU3JSkuICAgIAoKKiAqKiJEdXJhZGVyb3MiKiogdGVyY2VyIGx1Z2FyIGNvbiB1biB0b3RhbCBkZSAxMCBjb2xhYm9yYWRvcmVzICg0LjI5JSkuICAKCiogKioiRmllbGVzIioqIGN1YXJ0byBsdWdhciBjb24gNCBjb2xhYm9yYWRvcmVzICgxLjcxJSkuICAKCiogUG9yIMO6bHRpbW8gZXN0YSBlbCAqKiJFeHRyYSIqKiBxdWUgZnVlIHNvbG8gdW4gY29sYWJvcmFkb3IgcXVlIHNlIGVuY3VlbnRyYSBmdWVyYSBkZSBsbyBub3JtYWwgKDAuNDMlKS4gIAoKIyMjIyAqKjQuIEVkYWQgeSBzYWxhcmlvKioKCipJbXBvcnRhciBiYXNlIGRlIGRhdG9zKgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpiZHIgPC1yZWFkLmNzdigiL1VzZXJzL2VsZW5hdmVsYS9iYWphc19maW5hbC5jc3YiKSAKYGBgCgoKQW5hbGl6YXIgbGEgZXN0cnVjdHVyYSBkZSBsYSBiYXNlIGRlIGRhdG9zCmBgYHtyfQpzdHIoYmRyKSAKYGBgCgpDdWFudG9zIE5hwrRzIGhheSBwb3IgdmFyaWFibGUKCmBgYHtyfQpzYXBwbHkoYmRyLGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAoKRGVqYW1vcyDDum5pY2FtZW50ZSBsYXMgdmFyaWFibGVzIGN1YW50aXRhdGl2YXMKCmBgYHtyfQpiZHIyIDwtIGJkcgpiZHIyPC1zdWJzZXQoYmRyMixzZWxlY3QgPSAtYyhnZW5lcm8sYWx0YSxtb3Rpdm9fYmFqYSxwdWVzdG8sZXN0YWRvLG5vbWJyZSxkdXJhY2lvbikpCnN1bW1hcnkoYmRyMikKYGBgCgpOb3JtYWxpemFjacOzbiBkZSB2YXJpYWJsZXMKCmBgYHtyfQpiZHIzIDwtIHNjYWxlKGJkcjJbMToyXSkgCmBgYAoKRWxib3cgcGxvdAoKYGBge3J9CmZ2aXpfbmJjbHVzdChiZHIzLCBrbWVhbnMsIG1ldGhvZD0id3NzIikrIAogIGdlb21fdmxpbmUoeGludGVyY2VwdD00LCBsaW5ldHlwZT0yKSsgICAgICAgICAKICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpIApgYGAKClZpc3VhbGl6YWNpw7NuIGRlIGNsdXN0ZXJzCgpgYGB7cn0KYmRyNDwta21lYW5zKGJkcjMsNCkKYmRyNAoKZnZpel9jbHVzdGVyKGJkcjQsZGF0YT1iZHIzKQpgYGAKClZlbW9zIHF1ZSBlbCBuw7ptZXJvIMOzcHRpbW8gZGUga2x1c3RlcnMgcGFyYSBsYXMgdmFyaWFibGVzIGVkYWQgeSBzYWxhcmlvIGRpYXJpbyBzb24gNCBzZWfDum4gZWwgbcOpdG9kbyBkZSBFbGJvdy4KCkNvbW8gZGF0byBkaXNwYXJhZG8geSBwb3NpYmxlIHdhcm5pbmcgdmVtb3MgcXVlIGhheSB1biB0cmFiYWphZG9yIGNvbiB1biBzYWxhcmlvIGRpYXJpbyBkZSA1MDAgcGVzb3MgbG8gcXVlIHBvZHLDrWEgcmVzdWx0YXIgY29tbyB1biBlcnJvciwgc2luIGVtYmFyZ28gbG9zIG90cm9zIDMgZ3J1cG9zIHZhcsOtYW4gYmFzdGFudGUgZW4gZWRhZCBtw6FzIG5vIGVuIHNhbGFyaW8gcHVlcyBlbCByYW5nbyBlcyBtdXkgY2VyY2Fuby4KCjEuICoqQWx0byoqIEVsIGRhdG8gZXMgw7puaWNvIGVuIGVsIGNvbnRlbyBjb24gdW4gdHJhYmFqYWRvciBkZSAzMiBhw7FvcyBxdWUgdGllbmUgdW4gc2FsYXJpbyBkaWFyaW8gZGUgNTAwIHBlc29zIHkgZXMgc29sdGVyby4gU3UgcHVudG8gZW4gZWwgbWFwZW8gZXMgw7puaWNvIChjb2xvciByb2pvKS4gIAogIAoyLiAqKlZlcmRlKiogRXMgZWwgY2x1c3RlciBjb24gZWwgcmVnaXN0cm8gZGUgcGVyc29uYXMgY29uIG1heW9yIGVkYWQgeSBjdWVudGFuIGNvbiBlbCBzYWxhcmlvIGRpYXJpbyBtw6FzIGFsdG8gMTgzLjY4LiAgCiAgCjMuICoqQXp1bCoqIEVzIGVsIGNsdXN0ZXIgY29uIGVsIHJlZ2lzdHJvIGRlIHBlcnNvbmFzIGNvbiBlZGFkIHByb21lZGlvIHkgc2FsYXJpbyBkZSAxODAuNjguICAKICAKNC4gKipNb3JhZG8qKiBFcyBlbCBjbHVzdGVyIGNvbiBtYXlvciByZWdpc3RybyBkZSBwZXJzb25hcyBjb24gZWRhZCBqw7N2ZW4geSBzYWxhcmlvIGRlIDE4Mi42OC4gIAogIAoKTm9tYnJhciBhIGxvcyBjbHVzdGVycwoKYGBge3J9CmJkcjU8LWJkcjIKYmRyNSRDbHVzdGVyIDwtIGJkcjQkY2x1c3RlcgpiZHI2PC1iZHI1ICU+JSBncm91cF9ieShDbHVzdGVyKSAlPiUgc3VtbWFyaXplKHNhbGFyaW9fZGlhcmlvPW1heChzYWxhcmlvX2RpYXJpbykpICU+JSBhcnJhbmdlKGRlc2Moc2FsYXJpb19kaWFyaW8pKQpiZHI1JENsdXN0ZXJfTmFtZXM8LWZhY3RvcihiZHI1JENsdXN0ZXIsbGV2ZWxzID0gYygxLDIsMyw0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkFsdG8iLCAiTWVkaW8iLCJCYWpvIiwgIlJlZ3VsYXIiKSkKYGBgCgpBZ3J1cGFyIGNsdXN0ZXJzCgpgYGB7cn0KCmJkcjc8LSBiZHI1ICU+JSBncm91cF9ieShDbHVzdGVyX05hbWVzKSAlPiUgc3VtbWFyaXplKHNhbGFyaW9fZGlhcmlvPW1heChzYWxhcmlvX2RpYXJpbyksIAplZGFkPW1lYW4oZWRhZCksCkNvdW50PW4oKSkKCmBgYAoKVGFibGEKYGBge3J9CmNsdXN0ZXJzPC1hcy5kYXRhLmZyYW1lKGJkcjcpCmNsdXN0ZXJzCmBgYAoKR3LDoWZpY2FzCgpgYGB7cn0KZ2dwbG90KGJkcjcsYWVzKHg9IHJlb3JkZXIoQ2x1c3Rlcl9OYW1lcyxDb3VudCkseT1Db3VudCxmaWxsPUNsdXN0ZXJfTmFtZXMpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKQoKZ2dwbG90KGJkcjcsIGFlcyh4PUNsdXN0ZXJfTmFtZXMseT1lZGFkLGZpbGw9IENsdXN0ZXJfTmFtZXMsbGFiZWw9cm91bmQoZWRhZCxkaWdpdHM9MikpKSArIAogIGdlb21fY29sKCkgKyAKICBnZW9tX3RleHQoKQoKZ2dwbG90KGJkcjcsYWVzKHg9Q2x1c3Rlcl9OYW1lcyx5PXNhbGFyaW9fZGlhcmlvLGZpbGw9IENsdXN0ZXJfTmFtZXMsbGFiZWw9cm91bmQoc2FsYXJpb19kaWFyaW8sZGlnaXRzPTIpKSkgKyAKICBnZW9tX2NvbCgpICsgCiAgZ2VvbV90ZXh0KCkKYGBgCiAgCjEuIEVuIGxhIHNlZ3VuZGEgZ3LDoWZpY2EgZWwgY2zDunN0ZXIgYXp1bCBlcyBlbCBxdWUgdGllbmUgbWF5b3IgZnJlY3VlbmNpYSBjb24gdW4gcHJvbWVkaW8gZGUgZWRhZCBkZSA0OCBhw7Fvcy4gIAogIAoyLiBFbCBjbHVzdGVyIGFsdG8gZW4gbGEgc2VndW5kYSBncmFmaWNhIGVuZ2xvYsOzIGVuIDIyIGHDsW9zIGVsIGNvdW50LCBzaW4gZW1iYXJnbyBoYXkgdHJhYmFqYWRvcmVzIGNvbiBtZW5vciBlZGFkLiBEaWNobyBjbHVzdGVyIGVzIGVsIHF1ZSB0aWVuZSBtZW5vcyB0cmFiYWphZG9yZXMgZGVudHJvIGRlIEZvcm0uICAKICAKMy4gRW4gY3VhbnRvIGFsIHNhbGFyaW8gZW4gbGEgdGVyY2VyYSBncsOhZmljYSwgZWwgZGF0byBtw6FzIGFsdG8gZXMgZWwgcXVlIGFudGVyaW9ybWVudGUgc2UgaGFiw61hIG1lbmNpb25hZG8gY29tbyB1biBkYXRvIMO6bmljbyAoJDUwMCkgeSB2ZW1vcyBlbCBjb21wb3J0YW1pZW50byBxdWUgc2UgZ2VuZXJhYmEgZW4gZWwgbWFwZW8gZGUgY2x1c3RlcnMgZG9uZGUgbG9zIGRlbcOhcyB0aWVuZW4gdW5hIGRpZmVyZW5jaWEgbcOtbmltYSBzaWVuZG8gbG9zIHJlc3VsdGFkb3MgMTgyLjY4LCAxODAuNjggeSAxODMuNjguICAKICAKCgoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjpkYXJrcmVkIj4qKlRlcmNlciBwdW50bzogSW5zaWdodHMuKioKCiogQ29uIGxhIGJhc2UgZGUgZGF0b3MgZGUgYmFqYXMgc2UgcmVhbGl6w7MgZWwgY2zDunN0ZXIgZGUgZWRhZCB5IGR1cmFjacOzbiwgZW4gbGEgY3VhbCBzZSB0b21hcm9uIGVuIGN1ZW50YSBsYXMgdmFyaWFibGVzIGRlIGxhIGVkYWQgZGUgbG9zIGNvbGFib3JhZG9yZXMgeSBsYSBkdXJhY2nDs24gcXVlIGhhbiB0ZW5pZG8gZW4gbGEgZW1wcmVzYS4gU2UgYnVzY8OzIGVsIG7Dum1lcm8gw7NwdGltbyBkZSBjbMO6c3RlciB5IHNlIHRvbWFyb24gZW4gY3VlbnRhIGxvcyBwcmltZXJvcyA0LiBFbiBlc3RlIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGxhcyBwZXJzb25hcyBjb24gZWRhZCBtZWRpYW5hLWFsdGEgbm8gdGllbmVuIHVuYSBkdXJhY2nDs24gZGUgbGFyZ28gdGllbXBvIGVuIGxhIGVtcHJlc2EgeWEgcXVlIGZvcm1hbiBwYXJ0ZSBkZWwgMjUlIGNvbiBtZW5vciBkdXJhY2nDs247IG90cm8gZGF0byBxdWUgb2JzZXJ2YW1vcyBxdWUgbGFzIHBlcnNvbmFzIGNvbiBtYXlvciBlZGFkIGhhbiBkdXJhZG8gbcOhcyB0aWVtcG8gZW4gRm9ybSB5IGZvcm1hbiBwYXJ0ZSBkZWwgc3VwZXJpb3IgNTAlLCB5IHBvciDDumx0aW1vIG9ic2VydmFtb3MgcXVlIGV4aXN0ZSB1bmEgbWF5b3Igcm90YWNpw7NuIGRlIHBlcnNvbmFsIGVuIGxvcyBjb2xhYm9yYWRvcmVzIGrDs3ZlbmVzLiAgCiogRW4gZWwgYW7DoWxpc2lzIHF1ZSBzZSByZWFsaXrDsyBtZWRpYW50ZSBncsOhZmljYXMsIG9idHV2aW1vcyBxdWUgZW4gbG9zIGNsw7pzdGVyLCBxdWUgbGEgcGVyc29uYSBjb24gbWF5b3IgZWRhZCBlbiBsb3MgZXhwZXJ0IHRpZW5lIDYxLCBlbiBzZW5pb3IgdGllbmUgNTcsIGVuIGp1bmlvciB0aWVuZSAzOSB5IGVuIGJlZ2lubm5lciAyNywgdGFtYmnDqW4gcXVlIGVuIGxhIGVtcHJlc2EgbGEgbWF5b3LDrWEgZGUgbG9zIGNvbGFib3JhZG9yZXMgY29ycmVzcG9uZGVuIGFsIGNsw7pzdGVyIGRlIGJlZ2lubmVyLiAgIAoqIEVuIGN1YW50byBhbCBhbsOhbGlzaXMgZGUgbGEgZHVyYWNpw7NuIGRlIGxvcyBjb2xhYm9yYWRvcmVzIGVuIGxhIGVtcHJlc2EgeSBzdSBzYWxhcmlvIGVuY29udHJhbW9zIGVzdG9zIHB1bnRvcyBtYXMgaW1wb3J0YW50ZXM7IGEpIGxhIG1heW9yw61hIGRlIGxvcyBlbXBsZWFkb3MgZ2FuYW4gYXJyaWJhIGRlbCBzYWxhcmlvIGRpYXJpbyBwcm9tZWRpbyBlc3RhYmxlY2lkbyBwb3IgZWwgZ29iaWVybm8sIGVzdGUgb3NjaWxhIGVudHJlICQxNTAtJDIwMDAgcGVzb3MgZGlhcmlvcywgYikgbG9zIGNvbGFib3JhZG9yZXMgcXVlIHRpZW5lbiB1biB0aWVtcG8gbWVkaW8gZW4gbGEgZW1wcmVzYSBhcHJveGltYWRhbWVudGUgZGUgdW4gYcOxbyB0aWVuZSB1biBzYWxhcmlvIHByb21lZGlvIGRlICQxNjksIG1pZW50cmFzIGxvcyBxdWUgZHVyYW4gbWVub3MgdGllbXBvIChhbHJlZGVkb3IgZGUgMTQxIGTDrWFzKSB0aWVuZW4gdW4gc2FsYXJpbyBwcm9tZWRpbyBkZSBhbHJlZGVkb3IgZGUgJDE4MC4gIAoqIEVsIGNsw7pzdGVyIG1hcyBncmFuZGUgZXMgZWwgbGxhbWFkbyDigJxSZWhuZXPigJ0gdGllbmUgdW4gdG90YWwgZGUgY29sYWJvcmFkb3JlcyBkZSAxOTEsIHkgZW4gcG9yY2VudGFqZSBkZSBiYWphcyBzZSBwdWVkZSBkZWNpciBxdWUgZXMgZWwgODEuOTclIHRvdGFsIGRlIGJhamFzLCBlc3RvcyB0aWVuZSBsYSBjYXJhY3RlcsOtc3RpY2EgZGUgc29uIGxhcyBwZXJzb25hcyBxdWUgZW50cmFuIHkgc2FsZW4gbXV5IHByb250byB5IHRpZW5lbiB1biBzYWxhcmlvIHBvciBhcnJpYmEgZGUgdG9kb3MgbG9zIGRlbcOhcyBjbMO6c3RlciBkZSAkMTgwIHBlc29zLCBwb3IgbG8gcXVlLCBlbiBlc3RlIGNsw7pzdGVyIHNlIG5lY2VzaXRhIGFwbGljYXIgdW5hIGVzdHJhdGVnaWEgZGUgZmlkZWxpemFjacOzbiBkZSBsb3MgY29sYWJvcmFkb3JlcyBkZSBvdHJhIGZvcm1hIHF1ZSBubyBzZWEgY29uIHVuIGF1bWVudG8gZGUgc2FsYXJpbywgZXMgZGVjaXIsIEZvcm0gcHVlZGUgaW1wbGVtZW50YXIgZXN0cmF0ZWdpYXMgY29uIHJlY29tcGVuc2FzIHF1ZSBzZWFuIGEgbGFyZ28gcGxhem8uICAKKiBDb21vIHNlIHB1ZWRlIG9ic2VydmFyIGVuIGVsIGFuYWxpc2lzIHF1ZSBzZSByZWFsaXrDsyBkZSBlZGFkIHkgc2FsYXJpbyBzZSBwdWVkZW4gZGVzdGFjYXIgbG9zIHNpZ3VpZW50ZXMgcHVudG9zOiAgCmEpIGVsIGNsdXN0ZXIgcXVlIHRpZW5lIHVuYSBtYXlvciBmcmVjdWVuY2lhIGVzIGVsIGRlIDQ4IGHDsW9zIGRlIGVkYWQsICAKYikgZWwgY2x1c3RlciBxdWUgbWFzIGNoaWNvIGVzIGRlIDIyIGHDsW9zIG8gbWVub3MgcG9yIGxvIHF1ZSBwb2RlbW9zIGRlY2lyIHF1ZSBlbiBGb3JtLCBsYSBtYXlvcsOtYSBzb24gbWF5b3JlcyBkZSAyMyBhw7FvcywKICBjKSBlbiBjdWFudG8gYWwgc2FsYXJpbyB0aWVuZW4gdW4gcHJvbWVkaW8gZGUgMTgwLjY4LCBwZXJvIGV4aXN0ZSB1biBkYXRvIMO6bmljbyBlbCBjdWFsIGdhbmFiYSA1MDAuICAgCiAgRW4gY29uY2x1c2nDs24gcGFyYSBGb3JtIHBvZGVtb3MgcGxhbnRlYXIgdW5hIGVzdHJhdGVnaWEgZGUgZXN0YW5kYXJpemFjacOzbiBkZSBzYWxhcmlvcyBkZSBhY3VlcmRvIGFsIHRpZW1wbyBxdWUgbGxldmFuIGVuIGxhIGVtcHJlc2EgeSBzdSBlZGFkLCBwYXJhIHBvZGVyIGFzw60gdGVuZXIgdW4gbWVqb3IgY29udHJvbCBkZSBnYXN0b3MgZGUgbGEgZW1wcmVzYSB5IGFwbGljYXIgbnVldmFzIGEgZXN0cmF0ZWdpYXMgYSBjYWRhIHVubyBkZSBsb3MgY2x1c3Rlci4KCgoKCgoKCgoKCgoKCgoKCg==