Contexto

Telco es una empresa internacional que vende una variedad de servicios de telecomunicaciones. Churn es un indicador de la deserción de clientes, fenómeno en el cual los clientesw cancelan sus servicios con la empresa. El objetivo de este trabajo es predecir si un cliente abandonará o se quedará en la empresa.

Instalar Paquetes y Librerias

#install.packages("tidyverse")
library(tidyverse) # Conjunto de paquetes para manipulación, limpieza y visualización de datos
#install.packages("caret")
library(caret) # Librería para Machine Learning (entrenamiento y validación de modelos)
#install.packages("e1071")
library(e1071) # Algoritmos de clasificación y regresión (incluye SVM y Naïve Bayes)
library(dplyr)

Importar la base de datos

telco <- read.csv("C:\\Users\\USUARIO\\Downloads\\Telco Customer Churn.csv")

Entender la base de Datos

summary(telco)
##   customerID           gender          SeniorCitizen      Partner         
##  Length:7043        Length:7043        Min.   :0.0000   Length:7043       
##  Class :character   Class :character   1st Qu.:0.0000   Class :character  
##  Mode  :character   Mode  :character   Median :0.0000   Mode  :character  
##                                        Mean   :0.1621                     
##                                        3rd Qu.:0.0000                     
##                                        Max.   :1.0000                     
##                                                                           
##   Dependents            tenure      PhoneService       MultipleLines     
##  Length:7043        Min.   : 0.00   Length:7043        Length:7043       
##  Class :character   1st Qu.: 9.00   Class :character   Class :character  
##  Mode  :character   Median :29.00   Mode  :character   Mode  :character  
##                     Mean   :32.37                                        
##                     3rd Qu.:55.00                                        
##                     Max.   :72.00                                        
##                                                                          
##  InternetService    OnlineSecurity     OnlineBackup       DeviceProtection  
##  Length:7043        Length:7043        Length:7043        Length:7043       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##  TechSupport        StreamingTV        StreamingMovies      Contract        
##  Length:7043        Length:7043        Length:7043        Length:7043       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##  PaperlessBilling   PaymentMethod      MonthlyCharges    TotalCharges   
##  Length:7043        Length:7043        Min.   : 18.25   Min.   :  18.8  
##  Class :character   Class :character   1st Qu.: 35.50   1st Qu.: 401.4  
##  Mode  :character   Mode  :character   Median : 70.35   Median :1397.5  
##                                        Mean   : 64.76   Mean   :2283.3  
##                                        3rd Qu.: 89.85   3rd Qu.:3794.7  
##                                        Max.   :118.75   Max.   :8684.8  
##                                                         NA's   :11      
##     Churn          
##  Length:7043       
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 
head(telco)
##   customerID gender SeniorCitizen Partner Dependents tenure PhoneService
## 1 7590-VHVEG Female             0     Yes         No      1           No
## 2 5575-GNVDE   Male             0      No         No     34          Yes
## 3 3668-QPYBK   Male             0      No         No      2          Yes
## 4 7795-CFOCW   Male             0      No         No     45           No
## 5 9237-HQITU Female             0      No         No      2          Yes
## 6 9305-CDSKC Female             0      No         No      8          Yes
##      MultipleLines InternetService OnlineSecurity OnlineBackup DeviceProtection
## 1 No phone service             DSL             No          Yes               No
## 2               No             DSL            Yes           No              Yes
## 3               No             DSL            Yes          Yes               No
## 4 No phone service             DSL            Yes           No              Yes
## 5               No     Fiber optic             No           No               No
## 6              Yes     Fiber optic             No           No              Yes
##   TechSupport StreamingTV StreamingMovies       Contract PaperlessBilling
## 1          No          No              No Month-to-month              Yes
## 2          No          No              No       One year               No
## 3          No          No              No Month-to-month              Yes
## 4         Yes          No              No       One year               No
## 5          No          No              No Month-to-month              Yes
## 6          No         Yes             Yes Month-to-month              Yes
##               PaymentMethod MonthlyCharges TotalCharges Churn
## 1          Electronic check          29.85        29.85    No
## 2              Mailed check          56.95      1889.50    No
## 3              Mailed check          53.85       108.15   Yes
## 4 Bank transfer (automatic)          42.30      1840.75    No
## 5          Electronic check          70.70       151.65   Yes
## 6          Electronic check          99.65       820.50   Yes
str(telco)
## 'data.frame':    7043 obs. of  21 variables:
##  $ customerID      : chr  "7590-VHVEG" "5575-GNVDE" "3668-QPYBK" "7795-CFOCW" ...
##  $ gender          : chr  "Female" "Male" "Male" "Male" ...
##  $ SeniorCitizen   : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ Partner         : chr  "Yes" "No" "No" "No" ...
##  $ Dependents      : chr  "No" "No" "No" "No" ...
##  $ tenure          : int  1 34 2 45 2 8 22 10 28 62 ...
##  $ PhoneService    : chr  "No" "Yes" "Yes" "No" ...
##  $ MultipleLines   : chr  "No phone service" "No" "No" "No phone service" ...
##  $ InternetService : chr  "DSL" "DSL" "DSL" "DSL" ...
##  $ OnlineSecurity  : chr  "No" "Yes" "Yes" "Yes" ...
##  $ OnlineBackup    : chr  "Yes" "No" "Yes" "No" ...
##  $ DeviceProtection: chr  "No" "Yes" "No" "Yes" ...
##  $ TechSupport     : chr  "No" "No" "No" "Yes" ...
##  $ StreamingTV     : chr  "No" "No" "No" "No" ...
##  $ StreamingMovies : chr  "No" "No" "No" "No" ...
##  $ Contract        : chr  "Month-to-month" "One year" "Month-to-month" "One year" ...
##  $ PaperlessBilling: chr  "Yes" "No" "Yes" "No" ...
##  $ PaymentMethod   : chr  "Electronic check" "Mailed check" "Mailed check" "Bank transfer (automatic)" ...
##  $ MonthlyCharges  : num  29.9 57 53.9 42.3 70.7 ...
##  $ TotalCharges    : num  29.9 1889.5 108.2 1840.8 151.7 ...
##  $ Churn           : chr  "No" "No" "Yes" "No" ...

Teoría sobre la Regresión Logística

La regresión logística es un modelo de Machine Learning utilizado para clasificación, que predice la probabilidad de que una observación pertenezca a una categoría (ejemplo: sí/no, 0/1). A diferencia de la regresión lineal, que predice valores continuos, la regresión logística usa la función sigmoide para transformar los valores en probabilidades entre 0 y 1. Mientras que la regresión lineal se usa para problemas de predicción de valores numéricos, la regresión logística se usa para clasificación binaria o multinomial. Si la probabilidad predicha es mayor o igual a 0.5, se asigna a la clase 1 (positivo), y si es menor a 0.5, se asigna a la clase 0 (negativo).

Preparar la Base de Datos

# Eliminar columna "CustomerID"
telco_limpia <- telco[, !colnames(telco) %in% "customerID"]
# Convertir las variables carácter a factores
telco_limpia <- telco_limpia %>%
  mutate(across(where(is.character), as.factor))
str(telco_limpia)
## 'data.frame':    7043 obs. of  20 variables:
##  $ gender          : Factor w/ 2 levels "Female","Male": 1 2 2 2 1 1 2 1 1 2 ...
##  $ SeniorCitizen   : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ Partner         : Factor w/ 2 levels "No","Yes": 2 1 1 1 1 1 1 1 2 1 ...
##  $ Dependents      : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 2 1 1 2 ...
##  $ tenure          : int  1 34 2 45 2 8 22 10 28 62 ...
##  $ PhoneService    : Factor w/ 2 levels "No","Yes": 1 2 2 1 2 2 2 1 2 2 ...
##  $ MultipleLines   : Factor w/ 3 levels "No","No phone service",..: 2 1 1 2 1 3 3 2 3 1 ...
##  $ InternetService : Factor w/ 3 levels "DSL","Fiber optic",..: 1 1 1 1 2 2 2 1 2 1 ...
##  $ OnlineSecurity  : Factor w/ 3 levels "No","No internet service",..: 1 3 3 3 1 1 1 3 1 3 ...
##  $ OnlineBackup    : Factor w/ 3 levels "No","No internet service",..: 3 1 3 1 1 1 3 1 1 3 ...
##  $ DeviceProtection: Factor w/ 3 levels "No","No internet service",..: 1 3 1 3 1 3 1 1 3 1 ...
##  $ TechSupport     : Factor w/ 3 levels "No","No internet service",..: 1 1 1 3 1 1 1 1 3 1 ...
##  $ StreamingTV     : Factor w/ 3 levels "No","No internet service",..: 1 1 1 1 1 3 3 1 3 1 ...
##  $ StreamingMovies : Factor w/ 3 levels "No","No internet service",..: 1 1 1 1 1 3 1 1 3 1 ...
##  $ Contract        : Factor w/ 3 levels "Month-to-month",..: 1 2 1 2 1 1 1 1 1 2 ...
##  $ PaperlessBilling: Factor w/ 2 levels "No","Yes": 2 1 2 1 2 2 2 1 2 1 ...
##  $ PaymentMethod   : Factor w/ 4 levels "Bank transfer (automatic)",..: 3 4 4 1 3 3 2 4 3 1 ...
##  $ MonthlyCharges  : num  29.9 57 53.9 42.3 70.7 ...
##  $ TotalCharges    : num  29.9 1889.5 108.2 1840.8 151.7 ...
##  $ Churn           : Factor w/ 2 levels "No","Yes": 1 1 2 1 2 2 1 1 2 1 ...
# Eliminar NA's
telco_limpia <- na.omit(telco_limpia)

Entrenar el Modelo

set.seed(123)
renglones_entrenamiento_telco <- createDataPartition(telco_limpia$Churn, p = 0.7, list = FALSE)
entrenamiento_telco <- telco_limpia[renglones_entrenamiento_telco, ]
prueba_telco <- telco_limpia[-renglones_entrenamiento_telco, ]

Generar el Modelo

modelo_telco <- glm(Churn ~ ., data = entrenamiento_telco, family = binomial)
summary(modelo_telco)
## 
## Call:
## glm(formula = Churn ~ ., family = binomial, data = entrenamiento_telco)
## 
## Coefficients: (7 not defined because of singularities)
##                                        Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                           1.0746695  0.9571525   1.123 0.261532    
## genderMale                           -0.0482269  0.0771304  -0.625 0.531797    
## SeniorCitizen                         0.1200467  0.1014491   1.183 0.236683    
## PartnerYes                           -0.0910761  0.0918164  -0.992 0.321228    
## DependentsYes                        -0.0843260  0.1068161  -0.789 0.429849    
## tenure                               -0.0621135  0.0074487  -8.339  < 2e-16 ***
## PhoneServiceYes                       0.0233358  0.7635434   0.031 0.975618    
## MultipleLinesNo phone service                NA         NA      NA       NA    
## MultipleLinesYes                      0.3794100  0.2078464   1.825 0.067935 .  
## InternetServiceFiber optic            1.5613737  0.9372764   1.666 0.095741 .  
## InternetServiceNo                    -1.5047493  0.9480893  -1.587 0.112481    
## OnlineSecurityNo internet service            NA         NA      NA       NA    
## OnlineSecurityYes                    -0.2149292  0.2098780  -1.024 0.305804    
## OnlineBackupNo internet service              NA         NA      NA       NA    
## OnlineBackupYes                       0.0008494  0.2053088   0.004 0.996699    
## DeviceProtectionNo internet service          NA         NA      NA       NA    
## DeviceProtectionYes                   0.1177891  0.2084585   0.565 0.572041    
## TechSupportNo internet service               NA         NA      NA       NA    
## TechSupportYes                       -0.1280168  0.2128673  -0.601 0.547579    
## StreamingTVNo internet service               NA         NA      NA       NA    
## StreamingTVYes                        0.5187839  0.3819512   1.358 0.174386    
## StreamingMoviesNo internet service           NA         NA      NA       NA    
## StreamingMoviesYes                    0.5783808  0.3840289   1.506 0.132045    
## ContractOne year                     -0.8451411  0.1304035  -6.481 9.11e-11 ***
## ContractTwo year                     -1.4644194  0.2039949  -7.179 7.04e-13 ***
## PaperlessBillingYes                   0.3020753  0.0884590   3.415 0.000638 ***
## PaymentMethodCredit card (automatic) -0.0093796  0.1357499  -0.069 0.944914    
## PaymentMethodElectronic check         0.2897469  0.1124649   2.576 0.009985 ** 
## PaymentMethodMailed check            -0.1002695  0.1367827  -0.733 0.463524    
## MonthlyCharges                       -0.0336622  0.0372618  -0.903 0.366314    
## TotalCharges                          0.0003653  0.0000844   4.328 1.50e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 5702.8  on 4923  degrees of freedom
## Residual deviance: 4117.1  on 4900  degrees of freedom
## AIC: 4165.1
## 
## Number of Fisher Scoring iterations: 6

Verifica las predicciones del entrenamiento y la matriz de confusión

# Predicciones en los datos de entrenamiento
pred_entrenamiento <- predict(modelo_telco, entrenamiento_telco, type = "response")

# Convertir probabilidades a clases (Umbral 0.5)
pred_clases_entrenamiento <- ifelse(pred_entrenamiento >= 0.5, "Yes", "No")
pred_clases_entrenamiento <- factor(pred_clases_entrenamiento, levels = levels(entrenamiento_telco$Churn))

# Evaluar modelo en datos de entrenamiento
conf_matrix_entrenamiento <- confusionMatrix(pred_clases_entrenamiento, entrenamiento_telco$Churn)
print("Matriz de Confusión - Entrenamiento:")
## [1] "Matriz de Confusión - Entrenamiento:"
print(conf_matrix_entrenamiento)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   No  Yes
##        No  3237  598
##        Yes  378  711
##                                           
##                Accuracy : 0.8018          
##                  95% CI : (0.7904, 0.8128)
##     No Information Rate : 0.7342          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.4634          
##                                           
##  Mcnemar's Test P-Value : 2.383e-12       
##                                           
##             Sensitivity : 0.8954          
##             Specificity : 0.5432          
##          Pos Pred Value : 0.8441          
##          Neg Pred Value : 0.6529          
##              Prevalence : 0.7342          
##          Detection Rate : 0.6574          
##    Detection Prevalence : 0.7788          
##       Balanced Accuracy : 0.7193          
##                                           
##        'Positive' Class : No              
## 
# Calcular Accuracy en entrenamiento
accuracy_entrenamiento <- mean(pred_clases_entrenamiento == entrenamiento_telco$Churn)

print(paste("Accuracy en entrenamiento:", round(accuracy_entrenamiento, 4)))
## [1] "Accuracy en entrenamiento: 0.8018"

Verifica las predicciones y la matriz de confusión

# Predicciones en los datos de prueba
pred_prueba <- predict(modelo_telco, prueba_telco, type = "response")

# Convertir probabilidades a clases (Umbral 0.5)
pred_clases_prueba <- ifelse(pred_prueba >= 0.5, "Yes", "No")
pred_clases_prueba <- factor(pred_clases_prueba, levels = levels(prueba_telco$Churn))

# Evaluar modelo en datos de prueba
conf_matrix_prueba <- confusionMatrix(pred_clases_prueba, prueba_telco$Churn)
print("Matriz de Confusión - Prueba:")
## [1] "Matriz de Confusión - Prueba:"
print(conf_matrix_prueba)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   No  Yes
##        No  1385  229
##        Yes  163  331
##                                           
##                Accuracy : 0.814           
##                  95% CI : (0.7968, 0.8304)
##     No Information Rate : 0.7343          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.5048          
##                                           
##  Mcnemar's Test P-Value : 0.001027        
##                                           
##             Sensitivity : 0.8947          
##             Specificity : 0.5911          
##          Pos Pred Value : 0.8581          
##          Neg Pred Value : 0.6700          
##              Prevalence : 0.7343          
##          Detection Rate : 0.6570          
##    Detection Prevalence : 0.7657          
##       Balanced Accuracy : 0.7429          
##                                           
##        'Positive' Class : No              
## 
# Calcular Accuracy en prueba
accuracy_prueba <- mean(pred_clases_prueba == prueba_telco$Churn)
print(paste("Accuracy en prueba:", round(accuracy_prueba, 4)))
## [1] "Accuracy en prueba: 0.814"

Ejemplo de Predicción

# Seleccionar un cliente aleatorio de la base telco_limpia
set.seed(123)  # Fijamos la semilla para reproducibilidad
info_cliente <- telco_limpia[sample(nrow(telco_limpia), 1), ]

# Mostrar los datos del cliente seleccionado
print(info_cliente)
##      gender SeniorCitizen Partner Dependents tenure PhoneService MultipleLines
## 2468   Male             0     Yes        Yes     49          Yes           Yes
##      InternetService OnlineSecurity OnlineBackup DeviceProtection TechSupport
## 2468     Fiber optic             No           No               No         Yes
##      StreamingTV StreamingMovies Contract PaperlessBilling    PaymentMethod
## 2468         Yes             Yes One year              Yes Electronic check
##      MonthlyCharges TotalCharges Churn
## 2468          97.95       4917.9    No
# Predecir la probabilidad de churn
pred_prob_cliente <- predict(modelo_telco, info_cliente, type = "response")

# Convertir a clase (umbral 0.5)
pred_clase_cliente <- ifelse(pred_prob_cliente >= 0.5, "Yes", "No")

# Mostrar resultados
print(paste("Probabilidad de Churn:", round(pred_prob_cliente, 4)))
## [1] "Probabilidad de Churn: 0.2663"
print(paste("Predicción Final:", pred_clase_cliente))
## [1] "Predicción Final: No"
LS0tDQp0aXRsZTogIlJlZ3Jlc2nDs24gTG9nw61zdGljYSINCmF1dGhvcjogIkZhYmlhbmEgTWVkaW5hY2VsbGkgLSBBMDA4MzU4OTYiDQpkYXRlOiAiMjAyNS0wMi0yNSINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgICB0b2M6IFRSVUUNCiAgICAgIHRvY19mbG9hdDogVFJVRQ0KICAgICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgICAgdGhlbWU6IHNpbXBsZQ0KICAgICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KLS0tDQoNCiFbXShDOlxcVXNlcnNcXFVTVUFSSU9cXERlc2t0b3BcXEJsb3F1ZSAtIE1vZHVsbyAyXFxnaXBoeS5naWYpDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+Q29udGV4dG88L3NwYW4+IA0KKipUZWxjbyoqIGVzIHVuYSBlbXByZXNhIGludGVybmFjaW9uYWwgcXVlIHZlbmRlIHVuYSB2YXJpZWRhZCBkZSBzZXJ2aWNpb3MgZGUgdGVsZWNvbXVuaWNhY2lvbmVzLiAqKkNodXJuKiogZXMgdW4gaW5kaWNhZG9yIGRlIGxhIGRlc2VyY2nDs24gZGUgY2xpZW50ZXMsIGZlbsOzbWVubyBlbiBlbCBjdWFsIGxvcyBjbGllbnRlc3cgY2FuY2VsYW4gc3VzIHNlcnZpY2lvcyBjb24gbGEgZW1wcmVzYS4gRWwgb2JqZXRpdm8gZGUgZXN0ZSB0cmFiYWpvIGVzIHByZWRlY2lyIHNpIHVuIGNsaWVudGUgYWJhbmRvbmFyw6EgbyBzZSBxdWVkYXLDoSBlbiBsYSBlbXByZXNhLg0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPkluc3RhbGFyIFBhcXVldGVzIHkgTGlicmVyaWFzPC9zcGFuPg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpICMgQ29uanVudG8gZGUgcGFxdWV0ZXMgcGFyYSBtYW5pcHVsYWNpw7NuLCBsaW1waWV6YSB5IHZpc3VhbGl6YWNpw7NuIGRlIGRhdG9zDQojaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KbGlicmFyeShjYXJldCkgIyBMaWJyZXLDrWEgcGFyYSBNYWNoaW5lIExlYXJuaW5nIChlbnRyZW5hbWllbnRvIHkgdmFsaWRhY2nDs24gZGUgbW9kZWxvcykNCiNpbnN0YWxsLnBhY2thZ2VzKCJlMTA3MSIpDQpsaWJyYXJ5KGUxMDcxKSAjIEFsZ29yaXRtb3MgZGUgY2xhc2lmaWNhY2nDs24geSByZWdyZXNpw7NuIChpbmNsdXllIFNWTSB5IE5hw692ZSBCYXllcykNCmxpYnJhcnkoZHBseXIpDQpgYGANCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+SW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvczwvc3Bhbj4NCmBgYHtyfQ0KdGVsY28gPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcVVNVQVJJT1xcRG93bmxvYWRzXFxUZWxjbyBDdXN0b21lciBDaHVybi5jc3YiKQ0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPkVudGVuZGVyIGxhIGJhc2UgZGUgRGF0b3M8L3NwYW4+DQpgYGB7cn0NCnN1bW1hcnkodGVsY28pDQpoZWFkKHRlbGNvKQ0Kc3RyKHRlbGNvKQ0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPlRlb3LDrWEgc29icmUgbGEgUmVncmVzacOzbiBMb2fDrXN0aWNhPC9zcGFuPg0KDQpMYSAqKnJlZ3Jlc2nDs24gbG9nw61zdGljYSoqIGVzIHVuIG1vZGVsbyBkZSBNYWNoaW5lIExlYXJuaW5nIHV0aWxpemFkbyBwYXJhIGNsYXNpZmljYWNpw7NuLCBxdWUgcHJlZGljZSBsYSBwcm9iYWJpbGlkYWQgZGUgcXVlIHVuYSBvYnNlcnZhY2nDs24gcGVydGVuZXpjYSBhIHVuYSBjYXRlZ29yw61hIChlamVtcGxvOiBzw60vbm8sIDAvMSkuIEEgZGlmZXJlbmNpYSBkZSBsYSByZWdyZXNpw7NuIGxpbmVhbCwgcXVlIHByZWRpY2UgdmFsb3JlcyBjb250aW51b3MsIGxhIHJlZ3Jlc2nDs24gbG9nw61zdGljYSB1c2EgbGEgZnVuY2nDs24gc2lnbW9pZGUgcGFyYSB0cmFuc2Zvcm1hciBsb3MgdmFsb3JlcyBlbiBwcm9iYWJpbGlkYWRlcyBlbnRyZSAwIHkgMS4gTWllbnRyYXMgcXVlIGxhIHJlZ3Jlc2nDs24gbGluZWFsIHNlIHVzYSBwYXJhIHByb2JsZW1hcyBkZSBwcmVkaWNjacOzbiBkZSB2YWxvcmVzIG51bcOpcmljb3MsIGxhIHJlZ3Jlc2nDs24gbG9nw61zdGljYSBzZSB1c2EgcGFyYSBjbGFzaWZpY2FjacOzbiBiaW5hcmlhIG8gbXVsdGlub21pYWwuIFNpIGxhIHByb2JhYmlsaWRhZCBwcmVkaWNoYSBlcyBtYXlvciBvIGlndWFsIGEgMC41LCBzZSBhc2lnbmEgYSBsYSBjbGFzZSAxIChwb3NpdGl2byksIHkgc2kgZXMgbWVub3IgYSAwLjUsIHNlIGFzaWduYSBhIGxhIGNsYXNlIDAgKG5lZ2F0aXZvKS4NCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5QcmVwYXJhciBsYSBCYXNlIGRlIERhdG9zPC9zcGFuPg0KYGBge3J9DQojIEVsaW1pbmFyIGNvbHVtbmEgIkN1c3RvbWVySUQiDQp0ZWxjb19saW1waWEgPC0gdGVsY29bLCAhY29sbmFtZXModGVsY28pICVpbiUgImN1c3RvbWVySUQiXQ0KIyBDb252ZXJ0aXIgbGFzIHZhcmlhYmxlcyBjYXLDoWN0ZXIgYSBmYWN0b3Jlcw0KdGVsY29fbGltcGlhIDwtIHRlbGNvX2xpbXBpYSAlPiUNCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBhcy5mYWN0b3IpKQ0Kc3RyKHRlbGNvX2xpbXBpYSkNCiMgRWxpbWluYXIgTkEncw0KdGVsY29fbGltcGlhIDwtIG5hLm9taXQodGVsY29fbGltcGlhKQ0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPkVudHJlbmFyIGVsIE1vZGVsbzwvc3Bhbj4NCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KcmVuZ2xvbmVzX2VudHJlbmFtaWVudG9fdGVsY28gPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih0ZWxjb19saW1waWEkQ2h1cm4sIHAgPSAwLjcsIGxpc3QgPSBGQUxTRSkNCmVudHJlbmFtaWVudG9fdGVsY28gPC0gdGVsY29fbGltcGlhW3Jlbmdsb25lc19lbnRyZW5hbWllbnRvX3RlbGNvLCBdDQpwcnVlYmFfdGVsY28gPC0gdGVsY29fbGltcGlhWy1yZW5nbG9uZXNfZW50cmVuYW1pZW50b190ZWxjbywgXQ0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPkdlbmVyYXIgZWwgTW9kZWxvPC9zcGFuPg0KYGBge3J9DQptb2RlbG9fdGVsY28gPC0gZ2xtKENodXJuIH4gLiwgZGF0YSA9IGVudHJlbmFtaWVudG9fdGVsY28sIGZhbWlseSA9IGJpbm9taWFsKQ0Kc3VtbWFyeShtb2RlbG9fdGVsY28pDQpgYGANCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+VmVyaWZpY2EgbGFzIHByZWRpY2Npb25lcyBkZWwgZW50cmVuYW1pZW50byB5IGxhIG1hdHJpeiBkZSBjb25mdXNpw7NuPC9zcGFuPg0KYGBge3J9DQoNCiMgUHJlZGljY2lvbmVzIGVuIGxvcyBkYXRvcyBkZSBlbnRyZW5hbWllbnRvDQpwcmVkX2VudHJlbmFtaWVudG8gPC0gcHJlZGljdChtb2RlbG9fdGVsY28sIGVudHJlbmFtaWVudG9fdGVsY28sIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQojIENvbnZlcnRpciBwcm9iYWJpbGlkYWRlcyBhIGNsYXNlcyAoVW1icmFsIDAuNSkNCnByZWRfY2xhc2VzX2VudHJlbmFtaWVudG8gPC0gaWZlbHNlKHByZWRfZW50cmVuYW1pZW50byA+PSAwLjUsICJZZXMiLCAiTm8iKQ0KcHJlZF9jbGFzZXNfZW50cmVuYW1pZW50byA8LSBmYWN0b3IocHJlZF9jbGFzZXNfZW50cmVuYW1pZW50bywgbGV2ZWxzID0gbGV2ZWxzKGVudHJlbmFtaWVudG9fdGVsY28kQ2h1cm4pKQ0KDQojIEV2YWx1YXIgbW9kZWxvIGVuIGRhdG9zIGRlIGVudHJlbmFtaWVudG8NCmNvbmZfbWF0cml4X2VudHJlbmFtaWVudG8gPC0gY29uZnVzaW9uTWF0cml4KHByZWRfY2xhc2VzX2VudHJlbmFtaWVudG8sIGVudHJlbmFtaWVudG9fdGVsY28kQ2h1cm4pDQpwcmludCgiTWF0cml6IGRlIENvbmZ1c2nDs24gLSBFbnRyZW5hbWllbnRvOiIpDQpwcmludChjb25mX21hdHJpeF9lbnRyZW5hbWllbnRvKQ0KDQojIENhbGN1bGFyIEFjY3VyYWN5IGVuIGVudHJlbmFtaWVudG8NCmFjY3VyYWN5X2VudHJlbmFtaWVudG8gPC0gbWVhbihwcmVkX2NsYXNlc19lbnRyZW5hbWllbnRvID09IGVudHJlbmFtaWVudG9fdGVsY28kQ2h1cm4pDQoNCnByaW50KHBhc3RlKCJBY2N1cmFjeSBlbiBlbnRyZW5hbWllbnRvOiIsIHJvdW5kKGFjY3VyYWN5X2VudHJlbmFtaWVudG8sIDQpKSkNCg0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPlZlcmlmaWNhIGxhcyBwcmVkaWNjaW9uZXMgeSBsYSBtYXRyaXogZGUgY29uZnVzacOzbjwvc3Bhbj4NCmBgYHtyfQ0KIyBQcmVkaWNjaW9uZXMgZW4gbG9zIGRhdG9zIGRlIHBydWViYQ0KcHJlZF9wcnVlYmEgPC0gcHJlZGljdChtb2RlbG9fdGVsY28sIHBydWViYV90ZWxjbywgdHlwZSA9ICJyZXNwb25zZSIpDQoNCiMgQ29udmVydGlyIHByb2JhYmlsaWRhZGVzIGEgY2xhc2VzIChVbWJyYWwgMC41KQ0KcHJlZF9jbGFzZXNfcHJ1ZWJhIDwtIGlmZWxzZShwcmVkX3BydWViYSA+PSAwLjUsICJZZXMiLCAiTm8iKQ0KcHJlZF9jbGFzZXNfcHJ1ZWJhIDwtIGZhY3RvcihwcmVkX2NsYXNlc19wcnVlYmEsIGxldmVscyA9IGxldmVscyhwcnVlYmFfdGVsY28kQ2h1cm4pKQ0KDQojIEV2YWx1YXIgbW9kZWxvIGVuIGRhdG9zIGRlIHBydWViYQ0KY29uZl9tYXRyaXhfcHJ1ZWJhIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkX2NsYXNlc19wcnVlYmEsIHBydWViYV90ZWxjbyRDaHVybikNCnByaW50KCJNYXRyaXogZGUgQ29uZnVzacOzbiAtIFBydWViYToiKQ0KcHJpbnQoY29uZl9tYXRyaXhfcHJ1ZWJhKQ0KDQojIENhbGN1bGFyIEFjY3VyYWN5IGVuIHBydWViYQ0KYWNjdXJhY3lfcHJ1ZWJhIDwtIG1lYW4ocHJlZF9jbGFzZXNfcHJ1ZWJhID09IHBydWViYV90ZWxjbyRDaHVybikNCnByaW50KHBhc3RlKCJBY2N1cmFjeSBlbiBwcnVlYmE6Iiwgcm91bmQoYWNjdXJhY3lfcHJ1ZWJhLCA0KSkpDQpgYGANCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+RWplbXBsbyBkZSBQcmVkaWNjacOzbjwvc3Bhbj4NCmBgYHtyfQ0KIyBTZWxlY2Npb25hciB1biBjbGllbnRlIGFsZWF0b3JpbyBkZSBsYSBiYXNlIHRlbGNvX2xpbXBpYQ0Kc2V0LnNlZWQoMTIzKSAgIyBGaWphbW9zIGxhIHNlbWlsbGEgcGFyYSByZXByb2R1Y2liaWxpZGFkDQppbmZvX2NsaWVudGUgPC0gdGVsY29fbGltcGlhW3NhbXBsZShucm93KHRlbGNvX2xpbXBpYSksIDEpLCBdDQoNCiMgTW9zdHJhciBsb3MgZGF0b3MgZGVsIGNsaWVudGUgc2VsZWNjaW9uYWRvDQpwcmludChpbmZvX2NsaWVudGUpDQpgYGANCmBgYHtyfQ0KIyBQcmVkZWNpciBsYSBwcm9iYWJpbGlkYWQgZGUgY2h1cm4NCnByZWRfcHJvYl9jbGllbnRlIDwtIHByZWRpY3QobW9kZWxvX3RlbGNvLCBpbmZvX2NsaWVudGUsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQojIENvbnZlcnRpciBhIGNsYXNlICh1bWJyYWwgMC41KQ0KcHJlZF9jbGFzZV9jbGllbnRlIDwtIGlmZWxzZShwcmVkX3Byb2JfY2xpZW50ZSA+PSAwLjUsICJZZXMiLCAiTm8iKQ0KDQojIE1vc3RyYXIgcmVzdWx0YWRvcw0KcHJpbnQocGFzdGUoIlByb2JhYmlsaWRhZCBkZSBDaHVybjoiLCByb3VuZChwcmVkX3Byb2JfY2xpZW50ZSwgNCkpKQ0KcHJpbnQocGFzdGUoIlByZWRpY2Npw7NuIEZpbmFsOiIsIHByZWRfY2xhc2VfY2xpZW50ZSkpDQpgYGANCg0KDQoNCg0KDQoNCg==