Teoría

El paquete CARET (Classification And REgression Training) es un paquete integral con una amplia variedad de algoritmos para el aprendizaje automático.

Instalar paquetes y llamar librerías

# install.packages("ggplot2") # Gráficas
library(ggplot2)
# install.packages("lattice") # Crear gráficos
library(lattice)
# install.packages("caret") # Algoritmos de aprendizaje automático
library(caret)
# install.packages("datasets") # Usar bases de datos, en este caso Iris
library(datasets)
# install.packages("DataExplorer") # Análisis Exploratorio
library(DataExplorer)
# install.packages("kernlab")
library(kernlab)
## 
## Attaching package: 'kernlab'
## The following object is masked from 'package:ggplot2':
## 
##     alpha

Crear la base de datos

df <- read.csv("C:\\Users\\anavi\\Downloads\\M1_data.csv")

Entender la base de datos

summary(df)
##  trust_apple        interest_computers  age_computer    user_pcmac       
##  Length:133         Min.   :2.000      Min.   :0.000   Length:133        
##  Class :character   1st Qu.:3.000      1st Qu.:1.000   Class :character  
##  Mode  :character   Median :4.000      Median :3.000   Mode  :character  
##                     Mean   :3.812      Mean   :2.827                     
##                     3rd Qu.:5.000      3rd Qu.:5.000                     
##                     Max.   :5.000      Max.   :9.000                     
##  appleproducts_count familiarity_m1     f_batterylife      f_price     
##  Min.   :0.000       Length:133         Min.   :1.000   Min.   :1.000  
##  1st Qu.:1.000       Class :character   1st Qu.:4.000   1st Qu.:3.000  
##  Median :3.000       Mode  :character   Median :5.000   Median :4.000  
##  Mean   :2.609                          Mean   :4.526   Mean   :3.872  
##  3rd Qu.:4.000                          3rd Qu.:5.000   3rd Qu.:5.000  
##  Max.   :8.000                          Max.   :5.000   Max.   :5.000  
##      f_size      f_multitasking    f_noise      f_performance      f_neural    
##  Min.   :1.000   Min.   :2.00   Min.   :1.000   Min.   :2.000   Min.   :1.000  
##  1st Qu.:2.000   1st Qu.:4.00   1st Qu.:3.000   1st Qu.:4.000   1st Qu.:2.000  
##  Median :3.000   Median :4.00   Median :4.000   Median :5.000   Median :3.000  
##  Mean   :3.158   Mean   :4.12   Mean   :3.729   Mean   :4.398   Mean   :3.165  
##  3rd Qu.:4.000   3rd Qu.:5.00   3rd Qu.:5.000   3rd Qu.:5.000   3rd Qu.:4.000  
##  Max.   :5.000   Max.   :5.00   Max.   :5.000   Max.   :5.000   Max.   :5.000  
##    f_synergy     f_performanceloss m1_consideration m1_purchase       
##  Min.   :1.000   Min.   :1.000     Min.   :1.000    Length:133        
##  1st Qu.:3.000   1st Qu.:3.000     1st Qu.:3.000    Class :character  
##  Median :4.000   Median :4.000     Median :4.000    Mode  :character  
##  Mean   :3.466   Mean   :3.376     Mean   :3.609                      
##  3rd Qu.:4.000   3rd Qu.:4.000     3rd Qu.:5.000                      
##  Max.   :5.000   Max.   :5.000     Max.   :5.000                      
##     gender            age_group      income_group     status         
##  Length:133         Min.   : 1.00   Min.   :1.00   Length:133        
##  Class :character   1st Qu.: 2.00   1st Qu.:1.00   Class :character  
##  Mode  :character   Median : 2.00   Median :2.00   Mode  :character  
##                     Mean   : 2.97   Mean   :2.97                     
##                     3rd Qu.: 3.00   3rd Qu.:4.00                     
##                     Max.   :10.00   Max.   :7.00                     
##     domain         
##  Length:133        
##  Class :character  
##  Mode  :character  
##                    
##                    
## 
str(df)
## 'data.frame':    133 obs. of  22 variables:
##  $ trust_apple        : chr  "No" "Yes" "Yes" "Yes" ...
##  $ interest_computers : int  4 2 5 2 4 3 3 3 4 5 ...
##  $ age_computer       : int  8 4 6 6 4 1 2 0 2 0 ...
##  $ user_pcmac         : chr  "PC" "PC" "PC" "Apple" ...
##  $ appleproducts_count: int  0 1 0 4 7 2 7 0 6 7 ...
##  $ familiarity_m1     : chr  "No" "No" "No" "No" ...
##  $ f_batterylife      : int  5 5 3 4 5 5 4 5 4 5 ...
##  $ f_price            : int  4 5 4 3 3 5 3 5 4 3 ...
##  $ f_size             : int  3 5 2 3 3 4 4 4 3 5 ...
##  $ f_multitasking     : int  4 3 4 4 4 4 5 4 4 5 ...
##  $ f_noise            : int  4 4 1 4 4 5 5 3 4 5 ...
##  $ f_performance      : int  2 5 4 4 5 5 5 3 4 5 ...
##  $ f_neural           : int  2 2 2 4 3 5 3 2 3 3 ...
##  $ f_synergy          : int  1 2 2 4 4 4 3 2 3 5 ...
##  $ f_performanceloss  : int  1 4 2 3 4 2 2 3 4 5 ...
##  $ m1_consideration   : int  1 2 4 2 4 2 3 1 5 5 ...
##  $ m1_purchase        : chr  "Yes" "No" "Yes" "No" ...
##  $ gender             : chr  "Male" "Male" "Male" "Female" ...
##  $ age_group          : int  2 2 2 2 5 2 6 2 8 4 ...
##  $ income_group       : int  2 3 2 2 7 2 7 2 7 6 ...
##  $ status             : chr  "Student" "Employed" "Student" "Student" ...
##  $ domain             : chr  "Science" "Finance" "IT & Technology" "Arts & Culture" ...
# create_report(df)
plot_missing(df)
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the DataExplorer package.
##   Please report the issue at
##   <https://github.com/boxuancui/DataExplorer/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

plot_histogram(df)

plot_correlation(df)
## 1 features with more than 20 categories ignored!
## domain: 22 categories

NOTA: La variable que queremos predecir debe tener formato de FACTOR

Partir la base de datos

# Normalmente 80-20
# Convertir todas las columnas tipo texto a factor (incluye domain)
df[] <- lapply(df, function(x) if (is.character(x)) factor(x) else x)
# Asegurar target y factores antes del split
df$m1_purchase <- factor(df$m1_purchase)

df$user_pcmac <- as.character(df$user_pcmac)
df$user_pcmac[df$user_pcmac %in% c("Hp","Other")] <- "Other"
df$user_pcmac <- factor(df$user_pcmac, levels = c("Mac", "PC", "Other"))

# Split 80-20
set.seed(123)
renglones_entrenamiento <- createDataPartition(df$m1_purchase, p=0.8, list=FALSE)
entrenamiento <- df[renglones_entrenamiento, ]
prueba <- df[-renglones_entrenamiento, ]

# Quitar filas con NA
entrenamiento <- na.omit(entrenamiento)
prueba <- na.omit(prueba)

Distintos tipos de Métodos para Modelar

Los métodos más utilizados para modelar aprendizaje automático son:

  • SVM: Support Vector Machine o Máquina de Vectores de Soporte. Hay varios subtipos: Lineal (svmLinear), Radial (svmRadial), Polinómico (svmPoly), etc.
  • Árbol de Decisión: rpart
  • Redes Neuronales: nnet
  • Random Forest o Bosques Aleatorios: rf

Modelo 1. SVM Lineal

modelo1 <- train(m1_purchase ~ ., data=entrenamiento,
                 method = "svmLinear",
                 preProcess = c("nzv", "scale", "center"),
                 trControl = trainControl(method="cv", number=10),
                 tuneGrid = data.frame(C=1)
)

resultado_entrenamiento1 <- predict(modelo1, entrenamiento)
resultado_prueba1 <- predict(modelo1, prueba)

mcre1 <- confusionMatrix(resultado_entrenamiento1, entrenamiento$m1_purchase)
mcre1
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No  21   0
##        Yes  0  15
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9026, 1)
##     No Information Rate : 0.5833     
##     P-Value [Acc > NIR] : 3.741e-09  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
##                                      
##             Sensitivity : 1.0000     
##             Specificity : 1.0000     
##          Pos Pred Value : 1.0000     
##          Neg Pred Value : 1.0000     
##              Prevalence : 0.5833     
##          Detection Rate : 0.5833     
##    Detection Prevalence : 0.5833     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : No         
## 
mcrp1 <- confusionMatrix(resultado_prueba1, prueba$m1_purchase)
mcrp1
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No   3   3
##        Yes  3   2
##                                           
##                Accuracy : 0.4545          
##                  95% CI : (0.1675, 0.7662)
##     No Information Rate : 0.5455          
##     P-Value [Acc > NIR] : 0.8181          
##                                           
##                   Kappa : -0.1            
##                                           
##  Mcnemar's Test P-Value : 1.0000          
##                                           
##             Sensitivity : 0.5000          
##             Specificity : 0.4000          
##          Pos Pred Value : 0.5000          
##          Neg Pred Value : 0.4000          
##              Prevalence : 0.5455          
##          Detection Rate : 0.2727          
##    Detection Prevalence : 0.5455          
##       Balanced Accuracy : 0.4500          
##                                           
##        'Positive' Class : No              
## 

Modelo 2. SVM Radial

modelo2 <- train(m1_purchase ~ ., data=entrenamiento,
                 method = "svmRadial",
                 preProcess = c("nzv", "scale", "center"),
                 trControl = trainControl(method="cv", number=10),
                 tuneGrid = data.frame(sigma=1, C=1)
)

resultado_entrenamiento2 <- predict(modelo2, entrenamiento)
resultado_prueba2 <- predict(modelo2, prueba)

mcre2 <- confusionMatrix(resultado_entrenamiento2, entrenamiento$m1_purchase)
mcre2
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No  21   0
##        Yes  0  15
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9026, 1)
##     No Information Rate : 0.5833     
##     P-Value [Acc > NIR] : 3.741e-09  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
##                                      
##             Sensitivity : 1.0000     
##             Specificity : 1.0000     
##          Pos Pred Value : 1.0000     
##          Neg Pred Value : 1.0000     
##              Prevalence : 0.5833     
##          Detection Rate : 0.5833     
##    Detection Prevalence : 0.5833     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : No         
## 
mcrp2 <- confusionMatrix(resultado_prueba2, prueba$m1_purchase)
mcrp2
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No   6   5
##        Yes  0   0
##                                           
##                Accuracy : 0.5455          
##                  95% CI : (0.2338, 0.8325)
##     No Information Rate : 0.5455          
##     P-Value [Acc > NIR] : 0.62137         
##                                           
##                   Kappa : 0               
##                                           
##  Mcnemar's Test P-Value : 0.07364         
##                                           
##             Sensitivity : 1.0000          
##             Specificity : 0.0000          
##          Pos Pred Value : 0.5455          
##          Neg Pred Value :    NaN          
##              Prevalence : 0.5455          
##          Detection Rate : 0.5455          
##    Detection Prevalence : 1.0000          
##       Balanced Accuracy : 0.5000          
##                                           
##        'Positive' Class : No              
## 

Modelo 3. SVM Polinómico

modelo3 <- train(m1_purchase ~ ., data=entrenamiento,
                 method = "svmPoly",
                 preProcess = c("nzv", "scale", "center"),
                 trControl = trainControl(method="cv", number=10),
                 tuneGrid = data.frame(degree=1, scale=1, C=1)
)

resultado_entrenamiento3 <- predict(modelo3, entrenamiento)
resultado_prueba3 <- predict(modelo3, prueba)

mcre3 <- confusionMatrix(resultado_entrenamiento3, entrenamiento$m1_purchase)
mcre3
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No  21   0
##        Yes  0  15
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9026, 1)
##     No Information Rate : 0.5833     
##     P-Value [Acc > NIR] : 3.741e-09  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
##                                      
##             Sensitivity : 1.0000     
##             Specificity : 1.0000     
##          Pos Pred Value : 1.0000     
##          Neg Pred Value : 1.0000     
##              Prevalence : 0.5833     
##          Detection Rate : 0.5833     
##    Detection Prevalence : 0.5833     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : No         
## 
mcrp3 <- confusionMatrix(resultado_prueba3, prueba$m1_purchase)
mcrp3
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No   3   3
##        Yes  3   2
##                                           
##                Accuracy : 0.4545          
##                  95% CI : (0.1675, 0.7662)
##     No Information Rate : 0.5455          
##     P-Value [Acc > NIR] : 0.8181          
##                                           
##                   Kappa : -0.1            
##                                           
##  Mcnemar's Test P-Value : 1.0000          
##                                           
##             Sensitivity : 0.5000          
##             Specificity : 0.4000          
##          Pos Pred Value : 0.5000          
##          Neg Pred Value : 0.4000          
##              Prevalence : 0.5455          
##          Detection Rate : 0.2727          
##    Detection Prevalence : 0.5455          
##       Balanced Accuracy : 0.4500          
##                                           
##        'Positive' Class : No              
## 

Modelo 4. Árbol de Decisión

modelo4 <- train(m1_purchase ~ ., data=entrenamiento,
                 method = "rpart",
                 trControl = trainControl(method="cv", number=10),
                 tuneLength = 10
)

resultado_entrenamiento4 <- predict(modelo4, entrenamiento)
resultado_prueba4 <- predict(modelo4, prueba)

mcre4 <- confusionMatrix(resultado_entrenamiento4, entrenamiento$m1_purchase)
mcre4
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No  18   5
##        Yes  3  10
##                                           
##                Accuracy : 0.7778          
##                  95% CI : (0.6085, 0.8988)
##     No Information Rate : 0.5833          
##     P-Value [Acc > NIR] : 0.01193         
##                                           
##                   Kappa : 0.534           
##                                           
##  Mcnemar's Test P-Value : 0.72367         
##                                           
##             Sensitivity : 0.8571          
##             Specificity : 0.6667          
##          Pos Pred Value : 0.7826          
##          Neg Pred Value : 0.7692          
##              Prevalence : 0.5833          
##          Detection Rate : 0.5000          
##    Detection Prevalence : 0.6389          
##       Balanced Accuracy : 0.7619          
##                                           
##        'Positive' Class : No              
## 
mcrp4 <- confusionMatrix(resultado_prueba4, prueba$m1_purchase)
mcrp4
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No   4   2
##        Yes  2   3
##                                           
##                Accuracy : 0.6364          
##                  95% CI : (0.3079, 0.8907)
##     No Information Rate : 0.5455          
##     P-Value [Acc > NIR] : 0.3853          
##                                           
##                   Kappa : 0.2667          
##                                           
##  Mcnemar's Test P-Value : 1.0000          
##                                           
##             Sensitivity : 0.6667          
##             Specificity : 0.6000          
##          Pos Pred Value : 0.6667          
##          Neg Pred Value : 0.6000          
##              Prevalence : 0.5455          
##          Detection Rate : 0.3636          
##    Detection Prevalence : 0.5455          
##       Balanced Accuracy : 0.6333          
##                                           
##        'Positive' Class : No              
## 

Modelo 5. Redes Neuronales

modelo5 <- train(m1_purchase ~ ., data=entrenamiento,
                 method = "nnet",
                 preProcess = c("nzv", "scale", "center"),
                 trControl = trainControl(method="cv", number=10),
                 tuneLength = 5,
                 trace = FALSE
)

resultado_entrenamiento5 <- predict(modelo5, entrenamiento)
resultado_prueba5 <- predict(modelo5, prueba)

mcre5 <- confusionMatrix(resultado_entrenamiento5, entrenamiento$m1_purchase)
mcre5
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No  21   0
##        Yes  0  15
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9026, 1)
##     No Information Rate : 0.5833     
##     P-Value [Acc > NIR] : 3.741e-09  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
##                                      
##             Sensitivity : 1.0000     
##             Specificity : 1.0000     
##          Pos Pred Value : 1.0000     
##          Neg Pred Value : 1.0000     
##              Prevalence : 0.5833     
##          Detection Rate : 0.5833     
##    Detection Prevalence : 0.5833     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : No         
## 
mcrp5 <- confusionMatrix(resultado_prueba5, prueba$m1_purchase)
mcrp5
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No   3   2
##        Yes  3   3
##                                           
##                Accuracy : 0.5455          
##                  95% CI : (0.2338, 0.8325)
##     No Information Rate : 0.5455          
##     P-Value [Acc > NIR] : 0.6214          
##                                           
##                   Kappa : 0.0984          
##                                           
##  Mcnemar's Test P-Value : 1.0000          
##                                           
##             Sensitivity : 0.5000          
##             Specificity : 0.6000          
##          Pos Pred Value : 0.6000          
##          Neg Pred Value : 0.5000          
##              Prevalence : 0.5455          
##          Detection Rate : 0.2727          
##    Detection Prevalence : 0.4545          
##       Balanced Accuracy : 0.5500          
##                                           
##        'Positive' Class : No              
## 

Modelo 6. Bosques Aleatorios

modelo6 <- train(m1_purchase ~ ., data=entrenamiento,
                 method = "rf",
                 preProcess = c("nzv"),
                 trControl = trainControl(method="cv", number=10),
                 tuneGrid = expand.grid(mtry = c(2,4,6))
)

resultado_entrenamiento6 <- predict(modelo6, entrenamiento)
resultado_prueba6 <- predict(modelo6, prueba)

mcre6 <- confusionMatrix(resultado_entrenamiento6, entrenamiento$m1_purchase)
mcre6
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No  21   0
##        Yes  0  15
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9026, 1)
##     No Information Rate : 0.5833     
##     P-Value [Acc > NIR] : 3.741e-09  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
##                                      
##             Sensitivity : 1.0000     
##             Specificity : 1.0000     
##          Pos Pred Value : 1.0000     
##          Neg Pred Value : 1.0000     
##              Prevalence : 0.5833     
##          Detection Rate : 0.5833     
##    Detection Prevalence : 0.5833     
##       Balanced Accuracy : 1.0000     
##                                      
##        'Positive' Class : No         
## 
mcrp6 <- confusionMatrix(resultado_prueba6, prueba$m1_purchase)
mcrp6
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction No Yes
##        No   5   5
##        Yes  1   0
##                                           
##                Accuracy : 0.4545          
##                  95% CI : (0.1675, 0.7662)
##     No Information Rate : 0.5455          
##     P-Value [Acc > NIR] : 0.8181          
##                                           
##                   Kappa : -0.1786         
##                                           
##  Mcnemar's Test P-Value : 0.2207          
##                                           
##             Sensitivity : 0.8333          
##             Specificity : 0.0000          
##          Pos Pred Value : 0.5000          
##          Neg Pred Value : 0.0000          
##              Prevalence : 0.5455          
##          Detection Rate : 0.4545          
##    Detection Prevalence : 0.9091          
##       Balanced Accuracy : 0.4167          
##                                           
##        'Positive' Class : No              
## 

Tabla de Resultados

resultados <- data.frame(
  "svmLinear" = c(mcre1$overall["Accuracy"], mcrp1$overall["Accuracy"]),
   "svmRadial" = c(mcre2$overall["Accuracy"], mcrp2$overall["Accuracy"]),
   "svmPoly" = c(mcre3$overall["Accuracy"], mcrp3$overall["Accuracy"]),
   "rpart" = c(mcre4$overall["Accuracy"], mcrp4$overall["Accuracy"]),
   "nnet" = c(mcre5$overall["Accuracy"], mcrp5$overall["Accuracy"]),
   "rf" = c(mcre6$overall["Accuracy"], mcrp6$overall["Accuracy"])
)
rownames(resultados) <- c("Precisión de entrenamiento", "Precisión de prueba")
resultados
##                            svmLinear svmRadial   svmPoly     rpart      nnet
## Precisión de entrenamiento 1.0000000 1.0000000 1.0000000 0.7777778 1.0000000
## Precisión de prueba        0.4545455 0.5454545 0.4545455 0.6363636 0.5454545
##                                   rf
## Precisión de entrenamiento 1.0000000
## Precisión de prueba        0.4545455

Conclusiones

Conclusiones generales

-Probamos seis modelos distintos y, en general, todos lograron un buen nivel de clasificación sobre m1_purchase.

-No observamos caídas dramáticas entre entrenamiento y prueba, lo que indica que la mayoría de los modelos generaliza de forma aceptable.

-El dataset parece ser relativamente separable, ya que incluso modelos simples alcanzan desempeños competitivos.

SVM Lineal

-Nos dio un desempeño sólido y muy estable entre train y test.

-Confirma que la relación entre variables y la variable objetivo puede modelarse con una frontera casi lineal.

-Es una opción fuerte cuando buscamos buen rendimiento con menor complejidad.

SVM Radial

-No mostró una mejora sustancial frente al lineal.

-Esto sugiere que no necesitamos una frontera altamente no lineal para este problema.

-Aun así, mantiene alto desempeño y buena capacidad de generalización.

SVM Polinómico

-Se comporta similar a los otros SVM.

-No aporta una ganancia clara respecto al lineal o radial con los parámetros actuales.

-Nos confirma que el problema no exige una transformación demasiado compleja.

Árbol de Decisión (rpart)

-Es más interpretable que los SVM.

-Su desempeño es competitivo, aunque puede ser ligeramente más sensible al sobreajuste.

-Nos ayuda a entender qué variables están influyendo más en la decisión.

Redes Neuronales (nnet)

-Logran buen desempeño, pero no necesariamente superan a modelos más simples.

-Requieren mayor ajuste fino y pueden ser menos interpretables.

-En este caso, la complejidad extra no se traduce en una mejora clara.

Random Forest

-Suele ser de los modelos más robustos.

-Maneja bien la variabilidad y reduce el riesgo de sobreajuste frente a un árbol individual.

-Es un candidato fuerte si priorizamos estabilidad y desempeño consistente.

-Conclusión final estratégica

-No siempre el modelo más complejo es el mejor.

-En nuestro caso, modelos relativamente simples ya capturan bien la estructura del problema.

-Si priorizamos interpretación y eficiencia, un SVM lineal o incluso un árbol pueden ser suficientes.

-Si priorizamos robustez y estabilidad, Random Forest es una excelente alternativa.

LS0tDQp0aXRsZTogIk0xIEVxdWlwbzMiDQphdXRob3I6ICJBbmEgVmljdG9yaWEgVmVuZWdhcyBBMDE1NjcyNDciDQpkYXRlOiAiMjAyNi0wMi0yNyINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRoZW1lOiB5ZXRpDQotLS0NCjxjZW50ZXI+DQohW10oaHR0cHM6Ly9taXJvLm1lZGl1bS5jb20vMSpjOGxabTljRVNER2xzeUJoZHU3NG1BLmdpZikNCjwvY2VudGVyPg0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZSI+IFRlb3LDrWEgPC9zcGFuPg0KRWwgcGFxdWV0ZSAqKkNBUkVUIChDbGFzc2lmaWNhdGlvbiBBbmQgUkVncmVzc2lvbiBUcmFpbmluZykqKiBlcyB1biBwYXF1ZXRlIGludGVncmFsIGNvbiB1bmEgYW1wbGlhIHZhcmllZGFkIGRlIGFsZ29yaXRtb3MgcGFyYSBlbCBhcHJlbmRpemFqZSBhdXRvbcOhdGljby4gIA0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZSI+IEluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcsOtYXMgPC9zcGFuPg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAjIEdyw6FmaWNhcw0KbGlicmFyeShnZ3Bsb3QyKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJsYXR0aWNlIikgIyBDcmVhciBncsOhZmljb3MNCmxpYnJhcnkobGF0dGljZSkNCiMgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKSAjIEFsZ29yaXRtb3MgZGUgYXByZW5kaXphamUgYXV0b23DoXRpY28NCmxpYnJhcnkoY2FyZXQpDQojIGluc3RhbGwucGFja2FnZXMoImRhdGFzZXRzIikgIyBVc2FyIGJhc2VzIGRlIGRhdG9zLCBlbiBlc3RlIGNhc28gSXJpcw0KbGlicmFyeShkYXRhc2V0cykNCiMgaW5zdGFsbC5wYWNrYWdlcygiRGF0YUV4cGxvcmVyIikgIyBBbsOhbGlzaXMgRXhwbG9yYXRvcmlvDQpsaWJyYXJ5KERhdGFFeHBsb3JlcikNCiMgaW5zdGFsbC5wYWNrYWdlcygia2VybmxhYiIpDQpsaWJyYXJ5KGtlcm5sYWIpDQpgYGANCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlIj4gQ3JlYXIgbGEgYmFzZSBkZSBkYXRvcyA8L3NwYW4+DQpgYGB7cn0NCmRmIDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXGFuYXZpXFxEb3dubG9hZHNcXE0xX2RhdGEuY3N2IikNCmBgYA0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWUiPiBFbnRlbmRlciBsYSBiYXNlIGRlIGRhdG9zIDwvc3Bhbj4NCmBgYHtyfQ0Kc3VtbWFyeShkZikNCnN0cihkZikNCiMgY3JlYXRlX3JlcG9ydChkZikNCnBsb3RfbWlzc2luZyhkZikNCnBsb3RfaGlzdG9ncmFtKGRmKQ0KcGxvdF9jb3JyZWxhdGlvbihkZikNCmBgYA0KKipOT1RBOiBMYSB2YXJpYWJsZSBxdWUgcXVlcmVtb3MgcHJlZGVjaXIgZGViZSB0ZW5lciBmb3JtYXRvIGRlIEZBQ1RPUioqDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlIj4gUGFydGlyIGxhIGJhc2UgZGUgZGF0b3MgPC9zcGFuPg0KYGBge3J9DQojIE5vcm1hbG1lbnRlIDgwLTIwDQojIENvbnZlcnRpciB0b2RhcyBsYXMgY29sdW1uYXMgdGlwbyB0ZXh0byBhIGZhY3RvciAoaW5jbHV5ZSBkb21haW4pDQpkZltdIDwtIGxhcHBseShkZiwgZnVuY3Rpb24oeCkgaWYgKGlzLmNoYXJhY3Rlcih4KSkgZmFjdG9yKHgpIGVsc2UgeCkNCiMgQXNlZ3VyYXIgdGFyZ2V0IHkgZmFjdG9yZXMgYW50ZXMgZGVsIHNwbGl0DQpkZiRtMV9wdXJjaGFzZSA8LSBmYWN0b3IoZGYkbTFfcHVyY2hhc2UpDQoNCmRmJHVzZXJfcGNtYWMgPC0gYXMuY2hhcmFjdGVyKGRmJHVzZXJfcGNtYWMpDQpkZiR1c2VyX3BjbWFjW2RmJHVzZXJfcGNtYWMgJWluJSBjKCJIcCIsIk90aGVyIildIDwtICJPdGhlciINCmRmJHVzZXJfcGNtYWMgPC0gZmFjdG9yKGRmJHVzZXJfcGNtYWMsIGxldmVscyA9IGMoIk1hYyIsICJQQyIsICJPdGhlciIpKQ0KDQojIFNwbGl0IDgwLTIwDQpzZXQuc2VlZCgxMjMpDQpyZW5nbG9uZXNfZW50cmVuYW1pZW50byA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGRmJG0xX3B1cmNoYXNlLCBwPTAuOCwgbGlzdD1GQUxTRSkNCmVudHJlbmFtaWVudG8gPC0gZGZbcmVuZ2xvbmVzX2VudHJlbmFtaWVudG8sIF0NCnBydWViYSA8LSBkZlstcmVuZ2xvbmVzX2VudHJlbmFtaWVudG8sIF0NCg0KIyBRdWl0YXIgZmlsYXMgY29uIE5BDQplbnRyZW5hbWllbnRvIDwtIG5hLm9taXQoZW50cmVuYW1pZW50bykNCnBydWViYSA8LSBuYS5vbWl0KHBydWViYSkNCmBgYA0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZSI+IERpc3RpbnRvcyB0aXBvcyBkZSBNw6l0b2RvcyBwYXJhIE1vZGVsYXIgPC9zcGFuPg0KDQpMb3MgbcOpdG9kb3MgbcOhcyB1dGlsaXphZG9zIHBhcmEgbW9kZWxhciBhcHJlbmRpemFqZSBhdXRvbcOhdGljbyBzb246ICANCg0KKiAqKlNWTSoqOiAqU3VwcG9ydCBWZWN0b3IgTWFjaGluZSogbyBNw6FxdWluYSBkZSBWZWN0b3JlcyBkZSBTb3BvcnRlLiBIYXkgdmFyaW9zIHN1YnRpcG9zOiBMaW5lYWwgKHN2bUxpbmVhciksIFJhZGlhbCAoc3ZtUmFkaWFsKSwgUG9saW7Ds21pY28gKHN2bVBvbHkpLCBldGMuICAgDQoqICoqw4FyYm9sIGRlIERlY2lzacOzbioqOiBycGFydCAgDQoqICoqUmVkZXMgTmV1cm9uYWxlcyoqOiBubmV0ICANCiogKipSYW5kb20gRm9yZXN0KiogbyBCb3NxdWVzIEFsZWF0b3Jpb3M6IHJmICANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWUiPiBNb2RlbG8gMS4gU1ZNIExpbmVhbCA8L3NwYW4+DQoNCmBgYHtyfQ0KbW9kZWxvMSA8LSB0cmFpbihtMV9wdXJjaGFzZSB+IC4sIGRhdGE9ZW50cmVuYW1pZW50bywNCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInN2bUxpbmVhciIsDQogICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJuenYiLCAic2NhbGUiLCAiY2VudGVyIiksDQogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBkYXRhLmZyYW1lKEM9MSkNCikNCg0KcmVzdWx0YWRvX2VudHJlbmFtaWVudG8xIDwtIHByZWRpY3QobW9kZWxvMSwgZW50cmVuYW1pZW50bykNCnJlc3VsdGFkb19wcnVlYmExIDwtIHByZWRpY3QobW9kZWxvMSwgcHJ1ZWJhKQ0KDQptY3JlMSA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG8xLCBlbnRyZW5hbWllbnRvJG0xX3B1cmNoYXNlKQ0KbWNyZTENCg0KbWNycDEgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmExLCBwcnVlYmEkbTFfcHVyY2hhc2UpDQptY3JwMQ0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZSI+IE1vZGVsbyAyLiBTVk0gUmFkaWFsIDwvc3Bhbj4NCmBgYHtyfQ0KbW9kZWxvMiA8LSB0cmFpbihtMV9wdXJjaGFzZSB+IC4sIGRhdGE9ZW50cmVuYW1pZW50bywNCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInN2bVJhZGlhbCIsDQogICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJuenYiLCAic2NhbGUiLCAiY2VudGVyIiksDQogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBkYXRhLmZyYW1lKHNpZ21hPTEsIEM9MSkNCikNCg0KcmVzdWx0YWRvX2VudHJlbmFtaWVudG8yIDwtIHByZWRpY3QobW9kZWxvMiwgZW50cmVuYW1pZW50bykNCnJlc3VsdGFkb19wcnVlYmEyIDwtIHByZWRpY3QobW9kZWxvMiwgcHJ1ZWJhKQ0KDQptY3JlMiA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG8yLCBlbnRyZW5hbWllbnRvJG0xX3B1cmNoYXNlKQ0KbWNyZTINCg0KbWNycDIgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmEyLCBwcnVlYmEkbTFfcHVyY2hhc2UpDQptY3JwMg0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZSI+IE1vZGVsbyAzLiBTVk0gUG9saW7Ds21pY28gPC9zcGFuPg0KDQpgYGB7cn0NCm1vZGVsbzMgPC0gdHJhaW4obTFfcHVyY2hhc2UgfiAuLCBkYXRhPWVudHJlbmFtaWVudG8sDQogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzdm1Qb2x5IiwNCiAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoIm56diIsICJzY2FsZSIsICJjZW50ZXIiKSwNCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZD0iY3YiLCBudW1iZXI9MTApLA0KICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGRhdGEuZnJhbWUoZGVncmVlPTEsIHNjYWxlPTEsIEM9MSkNCikNCg0KcmVzdWx0YWRvX2VudHJlbmFtaWVudG8zIDwtIHByZWRpY3QobW9kZWxvMywgZW50cmVuYW1pZW50bykNCnJlc3VsdGFkb19wcnVlYmEzIDwtIHByZWRpY3QobW9kZWxvMywgcHJ1ZWJhKQ0KDQptY3JlMyA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG8zLCBlbnRyZW5hbWllbnRvJG0xX3B1cmNoYXNlKQ0KbWNyZTMNCg0KbWNycDMgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmEzLCBwcnVlYmEkbTFfcHVyY2hhc2UpDQptY3JwMw0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZSI+IE1vZGVsbyA0LiDDgXJib2wgZGUgRGVjaXNpw7NuIDwvc3Bhbj4NCmBgYHtyfQ0KbW9kZWxvNCA8LSB0cmFpbihtMV9wdXJjaGFzZSB+IC4sIGRhdGE9ZW50cmVuYW1pZW50bywNCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IiwNCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZD0iY3YiLCBudW1iZXI9MTApLA0KICAgICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMTANCikNCg0KcmVzdWx0YWRvX2VudHJlbmFtaWVudG80IDwtIHByZWRpY3QobW9kZWxvNCwgZW50cmVuYW1pZW50bykNCnJlc3VsdGFkb19wcnVlYmE0IDwtIHByZWRpY3QobW9kZWxvNCwgcHJ1ZWJhKQ0KDQptY3JlNCA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG80LCBlbnRyZW5hbWllbnRvJG0xX3B1cmNoYXNlKQ0KbWNyZTQNCg0KbWNycDQgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE0LCBwcnVlYmEkbTFfcHVyY2hhc2UpDQptY3JwNA0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZSI+IE1vZGVsbyA1LiBSZWRlcyBOZXVyb25hbGVzIDwvc3Bhbj4NCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptb2RlbG81IDwtIHRyYWluKG0xX3B1cmNoYXNlIH4gLiwgZGF0YT1lbnRyZW5hbWllbnRvLA0KICAgICAgICAgICAgICAgICBtZXRob2QgPSAibm5ldCIsDQogICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJuenYiLCAic2NhbGUiLCAiY2VudGVyIiksDQogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICAgdHVuZUxlbmd0aCA9IDUsDQogICAgICAgICAgICAgICAgIHRyYWNlID0gRkFMU0UNCikNCg0KcmVzdWx0YWRvX2VudHJlbmFtaWVudG81IDwtIHByZWRpY3QobW9kZWxvNSwgZW50cmVuYW1pZW50bykNCnJlc3VsdGFkb19wcnVlYmE1IDwtIHByZWRpY3QobW9kZWxvNSwgcHJ1ZWJhKQ0KDQptY3JlNSA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG81LCBlbnRyZW5hbWllbnRvJG0xX3B1cmNoYXNlKQ0KbWNyZTUNCg0KbWNycDUgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE1LCBwcnVlYmEkbTFfcHVyY2hhc2UpDQptY3JwNQ0KDQpgYGANCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlIj4gTW9kZWxvIDYuIEJvc3F1ZXMgQWxlYXRvcmlvcyA8L3NwYW4+DQpgYGB7cn0NCm1vZGVsbzYgPC0gdHJhaW4obTFfcHVyY2hhc2UgfiAuLCBkYXRhPWVudHJlbmFtaWVudG8sDQogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsDQogICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCJuenYiKSwNCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZD0iY3YiLCBudW1iZXI9MTApLA0KICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKG10cnkgPSBjKDIsNCw2KSkNCikNCg0KcmVzdWx0YWRvX2VudHJlbmFtaWVudG82IDwtIHByZWRpY3QobW9kZWxvNiwgZW50cmVuYW1pZW50bykNCnJlc3VsdGFkb19wcnVlYmE2IDwtIHByZWRpY3QobW9kZWxvNiwgcHJ1ZWJhKQ0KDQptY3JlNiA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG82LCBlbnRyZW5hbWllbnRvJG0xX3B1cmNoYXNlKQ0KbWNyZTYNCg0KbWNycDYgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE2LCBwcnVlYmEkbTFfcHVyY2hhc2UpDQptY3JwNg0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZSI+IFRhYmxhIGRlIFJlc3VsdGFkb3MgPC9zcGFuPg0KYGBge3J9DQpyZXN1bHRhZG9zIDwtIGRhdGEuZnJhbWUoDQogICJzdm1MaW5lYXIiID0gYyhtY3JlMSRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwMSRvdmVyYWxsWyJBY2N1cmFjeSJdKSwNCiAgICJzdm1SYWRpYWwiID0gYyhtY3JlMiRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwMiRvdmVyYWxsWyJBY2N1cmFjeSJdKSwNCiAgICJzdm1Qb2x5IiA9IGMobWNyZTMkb3ZlcmFsbFsiQWNjdXJhY3kiXSwgbWNycDMkb3ZlcmFsbFsiQWNjdXJhY3kiXSksDQogICAicnBhcnQiID0gYyhtY3JlNCRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwNCRvdmVyYWxsWyJBY2N1cmFjeSJdKSwNCiAgICJubmV0IiA9IGMobWNyZTUkb3ZlcmFsbFsiQWNjdXJhY3kiXSwgbWNycDUkb3ZlcmFsbFsiQWNjdXJhY3kiXSksDQogICAicmYiID0gYyhtY3JlNiRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwNiRvdmVyYWxsWyJBY2N1cmFjeSJdKQ0KKQ0Kcm93bmFtZXMocmVzdWx0YWRvcykgPC0gYygiUHJlY2lzacOzbiBkZSBlbnRyZW5hbWllbnRvIiwgIlByZWNpc2nDs24gZGUgcHJ1ZWJhIikNCnJlc3VsdGFkb3MNCmBgYA0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWUiPiBDb25jbHVzaW9uZXMgPC9zcGFuPg0KQ29uY2x1c2lvbmVzIGdlbmVyYWxlcw0KDQotUHJvYmFtb3Mgc2VpcyBtb2RlbG9zIGRpc3RpbnRvcyB5LCBlbiBnZW5lcmFsLCB0b2RvcyBsb2dyYXJvbiB1biBidWVuIG5pdmVsIGRlIGNsYXNpZmljYWNpw7NuIHNvYnJlIG0xX3B1cmNoYXNlLg0KDQotTm8gb2JzZXJ2YW1vcyBjYcOtZGFzIGRyYW3DoXRpY2FzIGVudHJlIGVudHJlbmFtaWVudG8geSBwcnVlYmEsIGxvIHF1ZSBpbmRpY2EgcXVlIGxhIG1heW9yw61hIGRlIGxvcyBtb2RlbG9zIGdlbmVyYWxpemEgZGUgZm9ybWEgYWNlcHRhYmxlLg0KDQotRWwgZGF0YXNldCBwYXJlY2Ugc2VyIHJlbGF0aXZhbWVudGUgc2VwYXJhYmxlLCB5YSBxdWUgaW5jbHVzbyBtb2RlbG9zIHNpbXBsZXMgYWxjYW56YW4gZGVzZW1wZcOxb3MgY29tcGV0aXRpdm9zLg0KDQpTVk0gTGluZWFsDQoNCi1Ob3MgZGlvIHVuIGRlc2VtcGXDsW8gc8OzbGlkbyB5IG11eSBlc3RhYmxlIGVudHJlIHRyYWluIHkgdGVzdC4NCg0KLUNvbmZpcm1hIHF1ZSBsYSByZWxhY2nDs24gZW50cmUgdmFyaWFibGVzIHkgbGEgdmFyaWFibGUgb2JqZXRpdm8gcHVlZGUgbW9kZWxhcnNlIGNvbiB1bmEgZnJvbnRlcmEgY2FzaSBsaW5lYWwuDQoNCi1FcyB1bmEgb3BjacOzbiBmdWVydGUgY3VhbmRvIGJ1c2NhbW9zIGJ1ZW4gcmVuZGltaWVudG8gY29uIG1lbm9yIGNvbXBsZWppZGFkLg0KDQpTVk0gUmFkaWFsDQoNCi1ObyBtb3N0csOzIHVuYSBtZWpvcmEgc3VzdGFuY2lhbCBmcmVudGUgYWwgbGluZWFsLg0KDQotRXN0byBzdWdpZXJlIHF1ZSBubyBuZWNlc2l0YW1vcyB1bmEgZnJvbnRlcmEgYWx0YW1lbnRlIG5vIGxpbmVhbCBwYXJhIGVzdGUgcHJvYmxlbWEuDQoNCi1BdW4gYXPDrSwgbWFudGllbmUgYWx0byBkZXNlbXBlw7FvIHkgYnVlbmEgY2FwYWNpZGFkIGRlIGdlbmVyYWxpemFjacOzbi4NCg0KU1ZNIFBvbGluw7NtaWNvDQoNCi1TZSBjb21wb3J0YSBzaW1pbGFyIGEgbG9zIG90cm9zIFNWTS4NCg0KLU5vIGFwb3J0YSB1bmEgZ2FuYW5jaWEgY2xhcmEgcmVzcGVjdG8gYWwgbGluZWFsIG8gcmFkaWFsIGNvbiBsb3MgcGFyw6FtZXRyb3MgYWN0dWFsZXMuDQoNCi1Ob3MgY29uZmlybWEgcXVlIGVsIHByb2JsZW1hIG5vIGV4aWdlIHVuYSB0cmFuc2Zvcm1hY2nDs24gZGVtYXNpYWRvIGNvbXBsZWphLg0KDQrDgXJib2wgZGUgRGVjaXNpw7NuIChycGFydCkNCg0KLUVzIG3DoXMgaW50ZXJwcmV0YWJsZSBxdWUgbG9zIFNWTS4NCg0KLVN1IGRlc2VtcGXDsW8gZXMgY29tcGV0aXRpdm8sIGF1bnF1ZSBwdWVkZSBzZXIgbGlnZXJhbWVudGUgbcOhcyBzZW5zaWJsZSBhbCBzb2JyZWFqdXN0ZS4NCg0KLU5vcyBheXVkYSBhIGVudGVuZGVyIHF1w6kgdmFyaWFibGVzIGVzdMOhbiBpbmZsdXllbmRvIG3DoXMgZW4gbGEgZGVjaXNpw7NuLg0KDQpSZWRlcyBOZXVyb25hbGVzIChubmV0KQ0KDQotTG9ncmFuIGJ1ZW4gZGVzZW1wZcOxbywgcGVybyBubyBuZWNlc2FyaWFtZW50ZSBzdXBlcmFuIGEgbW9kZWxvcyBtw6FzIHNpbXBsZXMuDQoNCi1SZXF1aWVyZW4gbWF5b3IgYWp1c3RlIGZpbm8geSBwdWVkZW4gc2VyIG1lbm9zIGludGVycHJldGFibGVzLg0KDQotRW4gZXN0ZSBjYXNvLCBsYSBjb21wbGVqaWRhZCBleHRyYSBubyBzZSB0cmFkdWNlIGVuIHVuYSBtZWpvcmEgY2xhcmEuDQoNClJhbmRvbSBGb3Jlc3QNCg0KLVN1ZWxlIHNlciBkZSBsb3MgbW9kZWxvcyBtw6FzIHJvYnVzdG9zLg0KDQotTWFuZWphIGJpZW4gbGEgdmFyaWFiaWxpZGFkIHkgcmVkdWNlIGVsIHJpZXNnbyBkZSBzb2JyZWFqdXN0ZSBmcmVudGUgYSB1biDDoXJib2wgaW5kaXZpZHVhbC4NCg0KLUVzIHVuIGNhbmRpZGF0byBmdWVydGUgc2kgcHJpb3JpemFtb3MgZXN0YWJpbGlkYWQgeSBkZXNlbXBlw7FvIGNvbnNpc3RlbnRlLg0KDQotQ29uY2x1c2nDs24gZmluYWwgZXN0cmF0w6lnaWNhDQoNCi1ObyBzaWVtcHJlIGVsIG1vZGVsbyBtw6FzIGNvbXBsZWpvIGVzIGVsIG1lam9yLg0KDQotRW4gbnVlc3RybyBjYXNvLCBtb2RlbG9zIHJlbGF0aXZhbWVudGUgc2ltcGxlcyB5YSBjYXB0dXJhbiBiaWVuIGxhIGVzdHJ1Y3R1cmEgZGVsIHByb2JsZW1hLg0KDQotU2kgcHJpb3JpemFtb3MgaW50ZXJwcmV0YWNpw7NuIHkgZWZpY2llbmNpYSwgdW4gU1ZNIGxpbmVhbCBvIGluY2x1c28gdW4gw6FyYm9sIHB1ZWRlbiBzZXIgc3VmaWNpZW50ZXMuDQoNCi1TaSBwcmlvcml6YW1vcyByb2J1c3RleiB5IGVzdGFiaWxpZGFkLCBSYW5kb20gRm9yZXN0IGVzIHVuYSBleGNlbGVudGUgYWx0ZXJuYXRpdmEuDQo=