Dataset

#install.packages("PerformanceAnalytics")
library(readr)
library(dplyr)

data <- read.csv("C:/Users/ander/Desktop/Kpis_SLA_meli.csv", header =TRUE, sep=",")

#Elimino la variable del SLA, para quedarme con dataset para analizar Backlog
data2 <- data[,c(2:15)]
head(data2)

Matriz de Correlación

#escala todas las variables para que tengan media = 0 y desviación estÔndar = 1 
data <- data%>% mutate_all (~ ( scale (.)%>% as.vector ))
data2 <- data2%>% mutate_all (~ ( scale (.)%>% as.vector ))


library(PerformanceAnalytics)
x <- data[,c( "SLA","VOLUMEN.RECIBIDO","BACKLOG", "Ausentismo","Ausentismo.NP", "Horas.trabajadas", "Hora.espera.promedio", "DESVIO.CAPACIDAD.vs.RECIBIDO", "BACKLOG_t1")]
chart.Correlation(x, histogram = TRUE, method = "pearson")

#install.packages("leaps")

require(leaps) 

# Prueba todos los posibles modelos y se queda con los mejores
#NO puede haber datos faltantes para aplicar lo que sigue:
mejores_modelos_back <- regsubsets(BACKLOG ~ ., data = data2, nvmax = 5) 
mejores_modelos_sla <- regsubsets(SLA ~ ., data = data, nvmax = 5) 


Selección de variables

#install.packages("leaps")

require(leaps) 

# Prueba todos los posibles modelos y se queda con los mejores
#NO puede haber datos faltantes para aplicar lo que sigue:
mejores_modelos_back <- regsubsets(BACKLOG ~ ., data = data2, nvmax = 7) 
mejores_modelos_sla <- regsubsets(SLA ~ ., data = data, nvmax = 7) 


library(ggplot2) 
p <- ggplot(data = data.frame(n_predictores = 1:7, R_ajustado = summary(mejores_modelos_back)$adjr2), aes(x = n_predictores, y = R_ajustado)) + 
  geom_line() + 
  geom_point() 
# Se identifica en rojo el mƔximo 
p <- p + geom_point(aes(x=n_predictores[which.max(summary(mejores_modelos_back)$adjr2)], y=R_ajustado[which.max(summary(mejores_modelos_back)$adjr2)]), colour = "red", size = 3) 
p <- p + scale_x_continuous(breaks = c(0:6)) + theme_bw() + 
  labs(title = "R2_ajustado vs nĆŗmero de predictores", x = "nĆŗmero predictores") 
p

################################################################


p1 <- ggplot(data = data.frame(n_predictores = 1:7, R_ajustado = summary(mejores_modelos_sla)$adjr2), aes(x = n_predictores, y = R_ajustado)) + 
  geom_line() + 
  geom_point() 
# Se identifica en rojo el mƔximo 
p1 <- p1 + geom_point(aes(x=n_predictores[which.max(summary(mejores_modelos_sla)$adjr2)], y=R_ajustado[which.max(summary(mejores_modelos_sla)$adjr2)]), colour = "red", size = 3) 
p1 <- p1 + scale_x_continuous(breaks = c(0:5)) + theme_bw() + 
  labs(title = "R2_ajustado vs nĆŗmero de predictores", x = "nĆŗmero predictores") 
p1


Mejor modelo para Backlog


coef(object = mejores_modelos_back, id = 5)
##      (Intercept)        CAPACIDAD VOLUMEN.RECIBIDO       BACKLOG_t1 
##    -4.510281e-17    -2.855217e-01     8.143013e-01     5.490245e-01 
##    Ausentismo.NP     Reencaminado 
##     2.465519e-01    -3.021792e-01


Mejor modelo para SLA


coef(object = mejores_modelos_sla, id = 3)
##         (Intercept)    VOLUMEN.RECIBIDO             BACKLOG Citados_confirmados 
##       -3.452100e-16        1.266789e-01       -9.456872e-01        8.757600e-02


Todas las combinaciones


Selecciona el subconjunto de predictores que alcanzan mayor R2 o menor MSE, Mallow’s Cp o AIC, etc.

library(olsrr)
lm.fit1 <- lm(BACKLOG ~ ., data = data2)
k_best <- ols_step_best_subset(lm.fit1)
k_best
plot(k_best)# el eje horizontal representa la cantidad de variables utilizadas en cada modelo.


lm.fit2 <- lm(SLA ~ ., data = data)
k_best <- ols_step_best_subset(lm.fit2)
k_best
plot(k_best)# el eje horizontal representa la cantidad de variables utilizadas en cada modelo.


Modelos Finales

Modelo Backlog


model_Backlog <- lm(BACKLOG ~ CAPACIDAD  + VOLUMEN.RECIBIDO  + BACKLOG_t1 +Ausentismo.NP  + Reencaminado , data = data2)
summary(model_Backlog)
## 
## Call:
## lm(formula = BACKLOG ~ CAPACIDAD + VOLUMEN.RECIBIDO + BACKLOG_t1 + 
##     Ausentismo.NP + Reencaminado, data = data2)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.42503 -0.40659 -0.02751  0.39744  1.40309 
## 
## Coefficients:
##                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)       7.264e-17  7.028e-02   0.000  1.00000    
## CAPACIDAD        -2.855e-01  9.035e-02  -3.160  0.00247 ** 
## VOLUMEN.RECIBIDO  8.143e-01  1.109e-01   7.345 6.46e-10 ***
## BACKLOG_t1        5.490e-01  7.516e-02   7.305 7.54e-10 ***
## Ausentismo.NP     2.466e-01  7.609e-02   3.240  0.00195 ** 
## Reencaminado     -3.022e-01  1.032e-01  -2.929  0.00480 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.5709 on 60 degrees of freedom
## Multiple R-squared:  0.6991, Adjusted R-squared:  0.674 
## F-statistic: 27.88 on 5 and 60 DF,  p-value: 1.764e-14


Modelo SLA


model_sla <- lm(SLA ~ VOLUMEN.RECIBIDO     +   BACKLOG + Citados_confirmados  , data=data)
summary(model_sla)
## 
## Call:
## lm(formula = SLA ~ VOLUMEN.RECIBIDO + BACKLOG + Citados_confirmados, 
##     data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.81131 -0.22906  0.04872  0.31298  1.02202 
## 
## Coefficients:
##                       Estimate Std. Error t value Pr(>|t|)    
## (Intercept)         -4.435e-16  5.658e-02   0.000   1.0000    
## VOLUMEN.RECIBIDO     1.267e-01  6.842e-02   1.851   0.0689 .  
## BACKLOG             -9.457e-01  6.860e-02 -13.786   <2e-16 ***
## Citados_confirmados  8.758e-02  5.728e-02   1.529   0.1314    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4597 on 62 degrees of freedom
## Multiple R-squared:  0.7985, Adjusted R-squared:  0.7887 
## F-statistic: 81.87 on 3 and 62 DF,  p-value: < 2.2e-16


Se usa el criterio del vif para verificar la multicolinealidad un vif bajo o menor a 5 es criterio suficiente para descartar multicolinealidad


library(car)
car::vif(model_Backlog)
##        CAPACIDAD VOLUMEN.RECIBIDO       BACKLOG_t1    Ausentismo.NP 
##         1.627691         2.451276         1.126340         1.154621 
##     Reencaminado 
##         2.121876

AnÔlisis de normalidad, homocedasticidad y autocorrelación de los residuos.

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


shapiro.test(model_sla$residuals)
## 
##  Shapiro-Wilk normality test
## 
## data:  model_sla$residuals
## W = 0.93513, p-value = 0.001882
bptest(model_sla)
## 
##  studentized Breusch-Pagan test
## 
## data:  model_sla
## BP = 6.3412, df = 3, p-value = 0.09614
dwtest(model_sla)
## 
##  Durbin-Watson test
## 
## data:  model_sla
## DW = 1.5639, p-value = 0.02968
## alternative hypothesis: true autocorrelation is greater than 0
par(mfrow=c(2,2))
plot(model_Backlog)

### Distancia Cook

dcook<-cooks.distance(model_sla)
influenceIndexPlot(model_sla, vars='Cook', las=1,col='blue')

dcook<-cooks.distance(model_Backlog)
influenceIndexPlot(model_Backlog, vars='Cook', las=1,col='blue')

qqPlot(model_Backlog$residuals, pch=19,
main="QQplot para los residuos del Modelo Lineal",
xlab="Cuantiles te“oricos",
ylab="Cuantiles muestrales")

## [1] 59 11
LS0tDQp0aXRsZTogIk1vZGVsbyBMaW5lYWwgTXVsdGl2YXJpYWRvIFNMQSINCmF1dGhvcjogIkN1c3RvbWVyIEludGVsbGlnZW5jZSINCmRhdGU6ICIyMy8xMS8yMDIyIg0Kb3V0cHV0Og0KICAgaHRtbF9kb2N1bWVudDoNCiAgICAgdG9jOiB5ZXMNCiAgICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgIHRvY19mbG9hdDogeWVzDQogICAgIGRmX3ByaW50OiBwYWdlZA0KICAgICB0aGVtZTogdW5pdGVkDQogICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCnNldHdkKCJDOi9Vc2Vycy9hbmRlci9EZXNrdG9wIikNCmBgYA0KDQoNCiMjIERhdGFzZXQNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KI2luc3RhbGwucGFja2FnZXMoIlBlcmZvcm1hbmNlQW5hbHl0aWNzIikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQpkYXRhIDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9hbmRlci9EZXNrdG9wL0twaXNfU0xBX21lbGkuY3N2IiwgaGVhZGVyID1UUlVFLCBzZXA9IiwiKQ0KDQojRWxpbWlubyBsYSB2YXJpYWJsZSBkZWwgU0xBLCBwYXJhIHF1ZWRhcm1lIGNvbiBkYXRhc2V0IHBhcmEgYW5hbGl6YXIgQmFja2xvZw0KZGF0YTIgPC0gZGF0YVssYygyOjE1KV0NCmhlYWQoZGF0YTIpDQoNCmBgYA0KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjpkYXJrcmVkIj5NYXRyaXogZGUgQ29ycmVsYWNpw7NuPC9zcGFuPg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQoNCg0KI2VzY2FsYSB0b2RhcyBsYXMgdmFyaWFibGVzIHBhcmEgcXVlIHRlbmdhbiBtZWRpYSA9IDAgeSBkZXN2aWFjacOzbiBlc3TDoW5kYXIgPSAxIA0KZGF0YSA8LSBkYXRhJT4lIG11dGF0ZV9hbGwgKH4gKCBzY2FsZSAoLiklPiUgYXMudmVjdG9yICkpDQpkYXRhMiA8LSBkYXRhMiU+JSBtdXRhdGVfYWxsICh+ICggc2NhbGUgKC4pJT4lIGFzLnZlY3RvciApKQ0KDQoNCmBgYA0KDQo8YnI+DQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRX0NCg0KDQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQ0KeCA8LSBkYXRhWyxjKCAiU0xBIiwiVk9MVU1FTi5SRUNJQklETyIsIkJBQ0tMT0ciLCAiQXVzZW50aXNtbyIsIkF1c2VudGlzbW8uTlAiLCAiSG9yYXMudHJhYmFqYWRhcyIsICJIb3JhLmVzcGVyYS5wcm9tZWRpbyIsICJERVNWSU8uQ0FQQUNJREFELnZzLlJFQ0lCSURPIiwgIkJBQ0tMT0dfdDEiKV0NCmNoYXJ0LkNvcnJlbGF0aW9uKHgsIGhpc3RvZ3JhbSA9IFRSVUUsIG1ldGhvZCA9ICJwZWFyc29uIikNCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQoNCg0KI2luc3RhbGwucGFja2FnZXMoImxlYXBzIikNCg0KcmVxdWlyZShsZWFwcykgDQoNCiMgUHJ1ZWJhIHRvZG9zIGxvcyBwb3NpYmxlcyBtb2RlbG9zIHkgc2UgcXVlZGEgY29uIGxvcyBtZWpvcmVzDQojTk8gcHVlZGUgaGFiZXIgZGF0b3MgZmFsdGFudGVzIHBhcmEgYXBsaWNhciBsbyBxdWUgc2lndWU6DQptZWpvcmVzX21vZGVsb3NfYmFjayA8LSByZWdzdWJzZXRzKEJBQ0tMT0cgfiAuLCBkYXRhID0gZGF0YTIsIG52bWF4ID0gNSkgDQptZWpvcmVzX21vZGVsb3Nfc2xhIDwtIHJlZ3N1YnNldHMoU0xBIH4gLiwgZGF0YSA9IGRhdGEsIG52bWF4ID0gNSkgDQoNCg0KYGBgDQoNCjxicj4NCg0KDQojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmRhcmtyZWQiPlNlbGVjY2nDs24gZGUgdmFyaWFibGVzPC9zcGFuPg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQoNCg0KI2luc3RhbGwucGFja2FnZXMoImxlYXBzIikNCg0KcmVxdWlyZShsZWFwcykgDQoNCiMgUHJ1ZWJhIHRvZG9zIGxvcyBwb3NpYmxlcyBtb2RlbG9zIHkgc2UgcXVlZGEgY29uIGxvcyBtZWpvcmVzDQojTk8gcHVlZGUgaGFiZXIgZGF0b3MgZmFsdGFudGVzIHBhcmEgYXBsaWNhciBsbyBxdWUgc2lndWU6DQptZWpvcmVzX21vZGVsb3NfYmFjayA8LSByZWdzdWJzZXRzKEJBQ0tMT0cgfiAuLCBkYXRhID0gZGF0YTIsIG52bWF4ID0gNykgDQptZWpvcmVzX21vZGVsb3Nfc2xhIDwtIHJlZ3N1YnNldHMoU0xBIH4gLiwgZGF0YSA9IGRhdGEsIG52bWF4ID0gNykgDQoNCg0KYGBgDQoNCjxicj4NCg0KDQpgYGB7cn0NCg0KbGlicmFyeShnZ3Bsb3QyKSANCnAgPC0gZ2dwbG90KGRhdGEgPSBkYXRhLmZyYW1lKG5fcHJlZGljdG9yZXMgPSAxOjcsIFJfYWp1c3RhZG8gPSBzdW1tYXJ5KG1lam9yZXNfbW9kZWxvc19iYWNrKSRhZGpyMiksIGFlcyh4ID0gbl9wcmVkaWN0b3JlcywgeSA9IFJfYWp1c3RhZG8pKSArIA0KICBnZW9tX2xpbmUoKSArIA0KICBnZW9tX3BvaW50KCkgDQojIFNlIGlkZW50aWZpY2EgZW4gcm9qbyBlbCBtw6F4aW1vIA0KcCA8LSBwICsgZ2VvbV9wb2ludChhZXMoeD1uX3ByZWRpY3RvcmVzW3doaWNoLm1heChzdW1tYXJ5KG1lam9yZXNfbW9kZWxvc19iYWNrKSRhZGpyMildLCB5PVJfYWp1c3RhZG9bd2hpY2gubWF4KHN1bW1hcnkobWVqb3Jlc19tb2RlbG9zX2JhY2spJGFkanIyKV0pLCBjb2xvdXIgPSAicmVkIiwgc2l6ZSA9IDMpIA0KcCA8LSBwICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMDo2KSkgKyB0aGVtZV9idygpICsgDQogIGxhYnModGl0bGUgPSAiUjJfYWp1c3RhZG8gdnMgbsO6bWVybyBkZSBwcmVkaWN0b3JlcyIsIHggPSAibsO6bWVybyBwcmVkaWN0b3JlcyIpIA0KcA0KDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQoNCnAxIDwtIGdncGxvdChkYXRhID0gZGF0YS5mcmFtZShuX3ByZWRpY3RvcmVzID0gMTo3LCBSX2FqdXN0YWRvID0gc3VtbWFyeShtZWpvcmVzX21vZGVsb3Nfc2xhKSRhZGpyMiksIGFlcyh4ID0gbl9wcmVkaWN0b3JlcywgeSA9IFJfYWp1c3RhZG8pKSArIA0KICBnZW9tX2xpbmUoKSArIA0KICBnZW9tX3BvaW50KCkgDQojIFNlIGlkZW50aWZpY2EgZW4gcm9qbyBlbCBtw6F4aW1vIA0KcDEgPC0gcDEgKyBnZW9tX3BvaW50KGFlcyh4PW5fcHJlZGljdG9yZXNbd2hpY2gubWF4KHN1bW1hcnkobWVqb3Jlc19tb2RlbG9zX3NsYSkkYWRqcjIpXSwgeT1SX2FqdXN0YWRvW3doaWNoLm1heChzdW1tYXJ5KG1lam9yZXNfbW9kZWxvc19zbGEpJGFkanIyKV0pLCBjb2xvdXIgPSAicmVkIiwgc2l6ZSA9IDMpIA0KcDEgPC0gcDEgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygwOjUpKSArIHRoZW1lX2J3KCkgKyANCiAgbGFicyh0aXRsZSA9ICJSMl9hanVzdGFkbyB2cyBuw7ptZXJvIGRlIHByZWRpY3RvcmVzIiwgeCA9ICJuw7ptZXJvIHByZWRpY3RvcmVzIikgDQpwMQ0KDQoNCg0KYGBgDQo8YnI+DQoNCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ZGFya3JlZCI+TWVqb3IgbW9kZWxvIHBhcmEgQmFja2xvZzwvc3Bhbj4NCg0KPGJyPg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQoNCmNvZWYob2JqZWN0ID0gbWVqb3Jlc19tb2RlbG9zX2JhY2ssIGlkID0gNSkNCmBgYA0KPGJyPg0KDQojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmRhcmtyZWQiPk1lam9yIG1vZGVsbyBwYXJhIFNMQTwvc3Bhbj4NCg0KPGJyPg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQoNCmNvZWYob2JqZWN0ID0gbWVqb3Jlc19tb2RlbG9zX3NsYSwgaWQgPSAzKQ0KYGBgDQoNCg0KPGJyPg0KDQojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmRhcmtyZWQiPlRvZGFzIGxhcyBjb21iaW5hY2lvbmVzPC9zcGFuPg0KDQo8YnI+DQoNClNlbGVjY2lvbmEgZWwgc3ViY29uanVudG8gZGUgcHJlZGljdG9yZXMgcXVlIGFsY2FuemFuIG1heW9yIFIyIG8gbWVub3IgTVNFLCBNYWxsb3figJlzIENwIG8gQUlDLCBldGMuDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkob2xzcnIpDQpsbS5maXQxIDwtIGxtKEJBQ0tMT0cgfiAuLCBkYXRhID0gZGF0YTIpDQprX2Jlc3QgPC0gb2xzX3N0ZXBfYmVzdF9zdWJzZXQobG0uZml0MSkNCmtfYmVzdA0KDQpwbG90KGtfYmVzdCkjIGVsIGVqZSBob3Jpem9udGFsIHJlcHJlc2VudGEgbGEgY2FudGlkYWQgZGUgdmFyaWFibGVzIHV0aWxpemFkYXMgZW4gY2FkYSBtb2RlbG8uDQoNCg0KYGBgDQoNCg0KPGJyPg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQoNCg0KbG0uZml0MiA8LSBsbShTTEEgfiAuLCBkYXRhID0gZGF0YSkNCmtfYmVzdCA8LSBvbHNfc3RlcF9iZXN0X3N1YnNldChsbS5maXQyKQ0Ka19iZXN0DQoNCnBsb3Qoa19iZXN0KSMgZWwgZWplIGhvcml6b250YWwgcmVwcmVzZW50YSBsYSBjYW50aWRhZCBkZSB2YXJpYWJsZXMgdXRpbGl6YWRhcyBlbiBjYWRhIG1vZGVsby4NCg0KDQpgYGANCg0KPGJyPg0KDQojIyBNb2RlbG9zIEZpbmFsZXMNCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ZGFya3JlZCI+TW9kZWxvIEJhY2tsb2c8L3NwYW4+DQoNCjxicj4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRX0NCm1vZGVsX0JhY2tsb2cgPC0gbG0oQkFDS0xPRyB+IENBUEFDSURBRCAgKyBWT0xVTUVOLlJFQ0lCSURPICArIEJBQ0tMT0dfdDEgK0F1c2VudGlzbW8uTlAgICsgUmVlbmNhbWluYWRvICwgZGF0YSA9IGRhdGEyKQ0Kc3VtbWFyeShtb2RlbF9CYWNrbG9nKQ0KYGBgDQoNCjxicj4NCg0KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjpkYXJrcmVkIj5Nb2RlbG8gU0xBPC9zcGFuPg0KDQo8YnI+DQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0V9DQptb2RlbF9zbGEgPC0gbG0oU0xBIH4gVk9MVU1FTi5SRUNJQklETyAgICAgKyAgIEJBQ0tMT0cgKyBDaXRhZG9zX2NvbmZpcm1hZG9zICAsIGRhdGE9ZGF0YSkNCnN1bW1hcnkobW9kZWxfc2xhKQ0KDQpgYGANCg0KPGJyPg0KDQpTZSB1c2EgZWwgY3JpdGVyaW8gZGVsIHZpZiBwYXJhIHZlcmlmaWNhciBsYSBtdWx0aWNvbGluZWFsaWRhZCB1biB2aWYgYmFqbyBvIG1lbm9yIGEgNSBlcyBjcml0ZXJpbyBzdWZpY2llbnRlIHBhcmEgZGVzY2FydGFyIG11bHRpY29saW5lYWxpZGFkDQoNCjxicj4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShjYXIpDQpjYXI6OnZpZihtb2RlbF9CYWNrbG9nKQ0KYGBgDQoNCg0KIyMgQW7DoWxpc2lzIGRlIG5vcm1hbGlkYWQsIGhvbW9jZWRhc3RpY2lkYWQgeSBhdXRvY29ycmVsYWNpw7NuIGRlIGxvcyByZXNpZHVvcy4NCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoImxtdGVzdCIpDQpsaWJyYXJ5KGxtdGVzdCkNCg0KDQpzaGFwaXJvLnRlc3QobW9kZWxfc2xhJHJlc2lkdWFscykNCg0KYnB0ZXN0KG1vZGVsX3NsYSkNCg0KZHd0ZXN0KG1vZGVsX3NsYSkNCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KcGFyKG1mcm93PWMoMiwyKSkNCnBsb3QobW9kZWxfQmFja2xvZykNCg0KYGBgDQojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmRhcmtyZWQiPkRpc3RhbmNpYSBDb29rPC9zcGFuPg0KDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQpkY29vazwtY29va3MuZGlzdGFuY2UobW9kZWxfc2xhKQ0KaW5mbHVlbmNlSW5kZXhQbG90KG1vZGVsX3NsYSwgdmFycz0nQ29vaycsIGxhcz0xLGNvbD0nYmx1ZScpDQpgYGANCg0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KZGNvb2s8LWNvb2tzLmRpc3RhbmNlKG1vZGVsX0JhY2tsb2cpDQppbmZsdWVuY2VJbmRleFBsb3QobW9kZWxfQmFja2xvZywgdmFycz0nQ29vaycsIGxhcz0xLGNvbD0nYmx1ZScpDQpgYGANCg0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KDQpxcVBsb3QobW9kZWxfQmFja2xvZyRyZXNpZHVhbHMsIHBjaD0xOSwNCm1haW49IlFRcGxvdCBwYXJhIGxvcyByZXNpZHVvcyBkZWwgTW9kZWxvIExpbmVhbCIsDQp4bGFiPSJDdWFudGlsZXMgdGXCtG9yaWNvcyIsDQp5bGFiPSJDdWFudGlsZXMgbXVlc3RyYWxlcyIpDQpgYGANCg==