Teoría

Una Red Neuronal Artificial (ANN) modela la relación entre un conjunto de entradas y una salida, resolviendo un problema de aprendizaje.

Ejemplos prácticos de aplicación de Redes Neuronales son: * La recomendación de contenido de Netflix. * El feed de Instagram o TIKTOK. *Determinar el número o letra escrita a mano.

install.packages(“neuralnet”)

Instalar paquetes y llamar librerías

library(neuralnet)

Alimentar con ejemplos

examen <- c(20, 10, 30, 20, 80, 30)
proyecto <- c(90, 20, 40, 50, 50, 80)
estatus <- c(1, 0, 0, 0, 0, 1)

df <- data.frame(examen, proyecto, estatus)
df
##   examen proyecto estatus
## 1     20       90       1
## 2     10       20       0
## 3     30       40       0
## 4     20       50       0
## 5     80       50       0
## 6     30       80       1

Generar la Red Neuronal

red_neuronal <- neuralnet(estatus ~ ., data=df)
plot(red_neuronal, rep="best")

Predecir la Red Neuronal

# Ejemplo de predicción con nuevos datos
prueba_examen <- c(30, 40, 85)
prueba_proyecto <- c(85, 50, 40)

prueba <- data.frame(examen = prueba_examen, proyecto = prueba_proyecto)

# Si entrenaste con nnet:
probabilidad <- predict(red_neuronal, prueba, type = "raw")

# Si entrenaste con neuralnet (cambiar por compute):
# probabilidad <- neuralnet::compute(red_neuronal, prueba)$net.result

resultado <- ifelse(probabilidad > 0.5, 1, 0)
resultado
##      [,1]
## [1,]    1
## [2,]    0
## [3,]    0

Instalar paquetes y llamar librerías

library(neuralnet)
library(dplyr)

# Cargar dataset
cancer <- read.csv("/Users/antoniodiaz/Desktop/MODULO2/cancer_de_mama.csv")

# Ver estructura
str(cancer)
## 'data.frame':    569 obs. of  31 variables:
##  $ diagnosis              : chr  "M" "M" "M" "M" ...
##  $ radius_mean            : num  18 20.6 19.7 11.4 20.3 ...
##  $ texture_mean           : num  10.4 17.8 21.2 20.4 14.3 ...
##  $ perimeter_mean         : num  122.8 132.9 130 77.6 135.1 ...
##  $ area_mean              : num  1001 1326 1203 386 1297 ...
##  $ smoothness_mean        : num  0.1184 0.0847 0.1096 0.1425 0.1003 ...
##  $ compactness_mean       : num  0.2776 0.0786 0.1599 0.2839 0.1328 ...
##  $ concavity_mean         : num  0.3001 0.0869 0.1974 0.2414 0.198 ...
##  $ concave_points_mean    : num  0.1471 0.0702 0.1279 0.1052 0.1043 ...
##  $ symmetry_mean          : num  0.242 0.181 0.207 0.26 0.181 ...
##  $ fractal_dimension_mean : num  0.0787 0.0567 0.06 0.0974 0.0588 ...
##  $ radius_se              : num  1.095 0.543 0.746 0.496 0.757 ...
##  $ texture_se             : num  0.905 0.734 0.787 1.156 0.781 ...
##  $ perimeter_se           : num  8.59 3.4 4.58 3.44 5.44 ...
##  $ area_se                : num  153.4 74.1 94 27.2 94.4 ...
##  $ smoothness_se          : num  0.0064 0.00522 0.00615 0.00911 0.01149 ...
##  $ compactness_se         : num  0.049 0.0131 0.0401 0.0746 0.0246 ...
##  $ concavity_se           : num  0.0537 0.0186 0.0383 0.0566 0.0569 ...
##  $ concave_points_se      : num  0.0159 0.0134 0.0206 0.0187 0.0188 ...
##  $ symmetry_se            : num  0.03 0.0139 0.0225 0.0596 0.0176 ...
##  $ fractal_dimension_se   : num  0.00619 0.00353 0.00457 0.00921 0.00511 ...
##  $ radius_worst           : num  25.4 25 23.6 14.9 22.5 ...
##  $ texture_worst          : num  17.3 23.4 25.5 26.5 16.7 ...
##  $ perimeter_worst        : num  184.6 158.8 152.5 98.9 152.2 ...
##  $ area_worst             : num  2019 1956 1709 568 1575 ...
##  $ smoothness_worst       : num  0.162 0.124 0.144 0.21 0.137 ...
##  $ compactness_worst      : num  0.666 0.187 0.424 0.866 0.205 ...
##  $ concavity_worst        : num  0.712 0.242 0.45 0.687 0.4 ...
##  $ concave_points_worst   : num  0.265 0.186 0.243 0.258 0.163 ...
##  $ symmetry_worst         : num  0.46 0.275 0.361 0.664 0.236 ...
##  $ fractal_dimension_worst: num  0.1189 0.089 0.0876 0.173 0.0768 ...
head(cancer)
##   diagnosis radius_mean texture_mean perimeter_mean area_mean smoothness_mean
## 1         M       17.99        10.38         122.80    1001.0         0.11840
## 2         M       20.57        17.77         132.90    1326.0         0.08474
## 3         M       19.69        21.25         130.00    1203.0         0.10960
## 4         M       11.42        20.38          77.58     386.1         0.14250
## 5         M       20.29        14.34         135.10    1297.0         0.10030
## 6         M       12.45        15.70          82.57     477.1         0.12780
##   compactness_mean concavity_mean concave_points_mean symmetry_mean
## 1          0.27760         0.3001             0.14710        0.2419
## 2          0.07864         0.0869             0.07017        0.1812
## 3          0.15990         0.1974             0.12790        0.2069
## 4          0.28390         0.2414             0.10520        0.2597
## 5          0.13280         0.1980             0.10430        0.1809
## 6          0.17000         0.1578             0.08089        0.2087
##   fractal_dimension_mean radius_se texture_se perimeter_se area_se
## 1                0.07871    1.0950     0.9053        8.589  153.40
## 2                0.05667    0.5435     0.7339        3.398   74.08
## 3                0.05999    0.7456     0.7869        4.585   94.03
## 4                0.09744    0.4956     1.1560        3.445   27.23
## 5                0.05883    0.7572     0.7813        5.438   94.44
## 6                0.07613    0.3345     0.8902        2.217   27.19
##   smoothness_se compactness_se concavity_se concave_points_se symmetry_se
## 1      0.006399        0.04904      0.05373           0.01587     0.03003
## 2      0.005225        0.01308      0.01860           0.01340     0.01389
## 3      0.006150        0.04006      0.03832           0.02058     0.02250
## 4      0.009110        0.07458      0.05661           0.01867     0.05963
## 5      0.011490        0.02461      0.05688           0.01885     0.01756
## 6      0.007510        0.03345      0.03672           0.01137     0.02165
##   fractal_dimension_se radius_worst texture_worst perimeter_worst area_worst
## 1             0.006193        25.38         17.33          184.60     2019.0
## 2             0.003532        24.99         23.41          158.80     1956.0
## 3             0.004571        23.57         25.53          152.50     1709.0
## 4             0.009208        14.91         26.50           98.87      567.7
## 5             0.005115        22.54         16.67          152.20     1575.0
## 6             0.005082        15.47         23.75          103.40      741.6
##   smoothness_worst compactness_worst concavity_worst concave_points_worst
## 1           0.1622            0.6656          0.7119               0.2654
## 2           0.1238            0.1866          0.2416               0.1860
## 3           0.1444            0.4245          0.4504               0.2430
## 4           0.2098            0.8663          0.6869               0.2575
## 5           0.1374            0.2050          0.4000               0.1625
## 6           0.1791            0.5249          0.5355               0.1741
##   symmetry_worst fractal_dimension_worst
## 1         0.4601                 0.11890
## 2         0.2750                 0.08902
## 3         0.3613                 0.08758
## 4         0.6638                 0.17300
## 5         0.2364                 0.07678
## 6         0.3985                 0.12440

Preparar los datos

# Convertir Diagnosis a binario (M=1, B=0)
cancer$diagnosis <- ifelse(cancer$diagnosis == "M", 1, 0)

# Normalizar todas las variables excepto diagnosis
cancer_norm <- cancer %>%
  mutate(across(-diagnosis, ~ (.-min(.))/(max(.)-min(.))))

summary(cancer_norm)
##    diagnosis       radius_mean      texture_mean    perimeter_mean  
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:0.2233   1st Qu.:0.2185   1st Qu.:0.2168  
##  Median :0.0000   Median :0.3024   Median :0.3088   Median :0.2933  
##  Mean   :0.3726   Mean   :0.3382   Mean   :0.3240   Mean   :0.3329  
##  3rd Qu.:1.0000   3rd Qu.:0.4164   3rd Qu.:0.4089   3rd Qu.:0.4168  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
##    area_mean      smoothness_mean  compactness_mean concavity_mean   
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.00000  
##  1st Qu.:0.1174   1st Qu.:0.3046   1st Qu.:0.1397   1st Qu.:0.06926  
##  Median :0.1729   Median :0.3904   Median :0.2247   Median :0.14419  
##  Mean   :0.2169   Mean   :0.3948   Mean   :0.2606   Mean   :0.20806  
##  3rd Qu.:0.2711   3rd Qu.:0.4755   3rd Qu.:0.3405   3rd Qu.:0.30623  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.00000  
##  concave_points_mean symmetry_mean    fractal_dimension_mean   radius_se      
##  Min.   :0.0000      Min.   :0.0000   Min.   :0.0000         Min.   :0.00000  
##  1st Qu.:0.1009      1st Qu.:0.2823   1st Qu.:0.1630         1st Qu.:0.04378  
##  Median :0.1665      Median :0.3697   Median :0.2439         Median :0.07702  
##  Mean   :0.2431      Mean   :0.3796   Mean   :0.2704         Mean   :0.10635  
##  3rd Qu.:0.3678      3rd Qu.:0.4530   3rd Qu.:0.3404         3rd Qu.:0.13304  
##  Max.   :1.0000      Max.   :1.0000   Max.   :1.0000         Max.   :1.00000  
##    texture_se      perimeter_se        area_se        smoothness_se   
##  Min.   :0.0000   Min.   :0.00000   Min.   :0.00000   Min.   :0.0000  
##  1st Qu.:0.1047   1st Qu.:0.04000   1st Qu.:0.02064   1st Qu.:0.1175  
##  Median :0.1653   Median :0.07209   Median :0.03311   Median :0.1586  
##  Mean   :0.1893   Mean   :0.09938   Mean   :0.06264   Mean   :0.1811  
##  3rd Qu.:0.2462   3rd Qu.:0.12251   3rd Qu.:0.07170   3rd Qu.:0.2187  
##  Max.   :1.0000   Max.   :1.00000   Max.   :1.00000   Max.   :1.0000  
##  compactness_se     concavity_se     concave_points_se  symmetry_se    
##  Min.   :0.00000   Min.   :0.00000   Min.   :0.0000    Min.   :0.0000  
##  1st Qu.:0.08132   1st Qu.:0.03811   1st Qu.:0.1447    1st Qu.:0.1024  
##  Median :0.13667   Median :0.06538   Median :0.2070    Median :0.1526  
##  Mean   :0.17444   Mean   :0.08054   Mean   :0.2235    Mean   :0.1781  
##  3rd Qu.:0.22680   3rd Qu.:0.10619   3rd Qu.:0.2787    3rd Qu.:0.2195  
##  Max.   :1.00000   Max.   :1.00000   Max.   :1.0000    Max.   :1.0000  
##  fractal_dimension_se  radius_worst    texture_worst    perimeter_worst 
##  Min.   :0.00000      Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.04675      1st Qu.:0.1807   1st Qu.:0.2415   1st Qu.:0.1678  
##  Median :0.07919      Median :0.2504   Median :0.3569   Median :0.2353  
##  Mean   :0.10019      Mean   :0.2967   Mean   :0.3640   Mean   :0.2831  
##  3rd Qu.:0.12656      3rd Qu.:0.3863   3rd Qu.:0.4717   3rd Qu.:0.3735  
##  Max.   :1.00000      Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
##    area_worst      smoothness_worst compactness_worst concavity_worst  
##  Min.   :0.00000   Min.   :0.0000   Min.   :0.0000    Min.   :0.00000  
##  1st Qu.:0.08113   1st Qu.:0.3000   1st Qu.:0.1163    1st Qu.:0.09145  
##  Median :0.12321   Median :0.3971   Median :0.1791    Median :0.18107  
##  Mean   :0.17091   Mean   :0.4041   Mean   :0.2202    Mean   :0.21740  
##  3rd Qu.:0.22090   3rd Qu.:0.4942   3rd Qu.:0.3025    3rd Qu.:0.30583  
##  Max.   :1.00000   Max.   :1.0000   Max.   :1.0000    Max.   :1.00000  
##  concave_points_worst symmetry_worst   fractal_dimension_worst
##  Min.   :0.0000       Min.   :0.0000   Min.   :0.0000         
##  1st Qu.:0.2231       1st Qu.:0.1851   1st Qu.:0.1077         
##  Median :0.3434       Median :0.2478   Median :0.1640         
##  Mean   :0.3938       Mean   :0.2633   Mean   :0.1896         
##  3rd Qu.:0.5546       3rd Qu.:0.3182   3rd Qu.:0.2429         
##  Max.   :1.0000       Max.   :1.0000   Max.   :1.0000

Dividir en train/test

set.seed(123)
indices <- sample(1:nrow(cancer_norm), size = 0.7 * nrow(cancer_norm))

train <- cancer_norm[indices, ]
test  <- cancer_norm[-indices, ]

cat("Filas de train:", nrow(train), "\n")
## Filas de train: 398
cat("Filas de test:", nrow(test), "\n")
## Filas de test: 171

Entrar a la red neuronal

f <- as.formula(paste("diagnosis ~", paste(names(train)[!names(train) %in% "diagnosis"], collapse=" + ")))

red_cancer <- neuralnet(f, data=train, hidden=c(5,3), linear.output=FALSE)

plot(red_cancer)

Importancia de las variables

library(NeuralNetTools)
library(ggplot2)

# Obtener importancia de variables
imp <- olden(red_cancer, bar_plot = FALSE)

# Pasar a data.frame
imp_df <- data.frame(Variable = rownames(imp), Importancia = imp[,1])

# Ordenar top 10
top_imp <- imp_df[order(abs(imp_df$Importancia), decreasing = TRUE), ][1:10, ]

# Graficar top 10
ggplot(top_imp, aes(x=reorder(Variable, Importancia), y=Importancia, fill=Importancia)) +
  geom_bar(stat="identity") +
  coord_flip() +
  labs(title="Top 10 variables más importantes",
       x="Variable", y="Importancia") +
  theme_minimal()

Conclusión

El modelo mostró que las variables más importantes para predecir el cáncer de mama son area_se y radius_se, seguidas por características como concave_points y texture_worst. Esto confirma que tanto el tamaño como la irregularidad del tumor son factores clave en la clasificación. En conjunto, la red neuronal logra capturar patrones relevantes, reforzando su utilidad como apoyo al diagnóstico.

LS0tCnRpdGxlOiAiUkVERVMgTkVVUk9OQUxFUyIKYXV0aG9yOiAiQW50b25pbyBEw61heiBBMDA4MzcyNTkiCmRhdGU6ICIyMDI1LTA4LTI1IgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogVFJVRQogICAgdGhlbWU6ICJzcGFjZWxhYiIKLS0tCgohW10oL1VzZXJzL2FudG9uaW9kaWF6L0Rlc2t0b3AvTU9EVUxPMi9zaHV0dGVyc3RvY2tfNTg3NzY4NTY3LnBuZykKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij4gVGVvcsOtYSA8L3NwYW4+ICAKVW5hICoqUmVkIE5ldXJvbmFsIEFydGlmaWNpYWwgKEFOTikqKiBtb2RlbGEgbGEgcmVsYWNpw7NuIGVudHJlIHVuIGNvbmp1bnRvIGRlIGVudHJhZGFzIHkgdW5hIHNhbGlkYSwgcmVzb2x2aWVuZG8gdW4gcHJvYmxlbWEgZGUgYXByZW5kaXphamUuCgpFamVtcGxvcyBwcsOhY3RpY29zIGRlIGFwbGljYWNpw7NuIGRlIFJlZGVzIE5ldXJvbmFsZXMgc29uOgoqIExhIHJlY29tZW5kYWNpw7NuIGRlIGNvbnRlbmlkbyBkZSBOZXRmbGl4LgoqIEVsIGZlZWQgZGUgSW5zdGFncmFtIG8gVElLVE9LLgoqRGV0ZXJtaW5hciBlbCBuw7ptZXJvIG8gbGV0cmEgZXNjcml0YSBhIG1hbm8uCgppbnN0YWxsLnBhY2thZ2VzKCJuZXVyYWxuZXQiKQoKIyA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZTsiPiBJbnN0YWxhciBwYXF1ZXRlcyB5IGxsYW1hciBsaWJyZXLDrWFzIDwvc3Bhbj4gIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KG5ldXJhbG5ldCkKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IEFsaW1lbnRhciBjb24gZWplbXBsb3MgPC9zcGFuPiAgCmBgYHtyfQpleGFtZW4gPC0gYygyMCwgMTAsIDMwLCAyMCwgODAsIDMwKQpwcm95ZWN0byA8LSBjKDkwLCAyMCwgNDAsIDUwLCA1MCwgODApCmVzdGF0dXMgPC0gYygxLCAwLCAwLCAwLCAwLCAxKQoKZGYgPC0gZGF0YS5mcmFtZShleGFtZW4sIHByb3llY3RvLCBlc3RhdHVzKQpkZgpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij4gR2VuZXJhciBsYSBSZWQgTmV1cm9uYWwgPC9zcGFuPiAgCmBgYHtyfQpyZWRfbmV1cm9uYWwgPC0gbmV1cmFsbmV0KGVzdGF0dXMgfiAuLCBkYXRhPWRmKQpwbG90KHJlZF9uZXVyb25hbCwgcmVwPSJiZXN0IikKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IFByZWRlY2lyIGxhIFJlZCBOZXVyb25hbCA8L3NwYW4+ICAKYGBge3J9CiMgRWplbXBsbyBkZSBwcmVkaWNjacOzbiBjb24gbnVldm9zIGRhdG9zCnBydWViYV9leGFtZW4gPC0gYygzMCwgNDAsIDg1KQpwcnVlYmFfcHJveWVjdG8gPC0gYyg4NSwgNTAsIDQwKQoKcHJ1ZWJhIDwtIGRhdGEuZnJhbWUoZXhhbWVuID0gcHJ1ZWJhX2V4YW1lbiwgcHJveWVjdG8gPSBwcnVlYmFfcHJveWVjdG8pCgojIFNpIGVudHJlbmFzdGUgY29uIG5uZXQ6CnByb2JhYmlsaWRhZCA8LSBwcmVkaWN0KHJlZF9uZXVyb25hbCwgcHJ1ZWJhLCB0eXBlID0gInJhdyIpCgojIFNpIGVudHJlbmFzdGUgY29uIG5ldXJhbG5ldCAoY2FtYmlhciBwb3IgY29tcHV0ZSk6CiMgcHJvYmFiaWxpZGFkIDwtIG5ldXJhbG5ldDo6Y29tcHV0ZShyZWRfbmV1cm9uYWwsIHBydWViYSkkbmV0LnJlc3VsdAoKcmVzdWx0YWRvIDwtIGlmZWxzZShwcm9iYWJpbGlkYWQgPiAwLjUsIDEsIDApCnJlc3VsdGFkbwoKYGBgCgohW10oL1VzZXJzL2FudG9uaW9kaWF6L0Rlc2t0b3AvTU9EVUxPMi9icmVhc3QtY2FuY2VyLWF3YXJlbmVzcy1yaWJib24tbG9nby02MDBudy0yNDc0NTE0MjE5LndlYnApCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IEluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcsOtYXMgPC9zcGFuPiAgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkobmV1cmFsbmV0KQpsaWJyYXJ5KGRwbHlyKQoKIyBDYXJnYXIgZGF0YXNldApjYW5jZXIgPC0gcmVhZC5jc3YoIi9Vc2Vycy9hbnRvbmlvZGlhei9EZXNrdG9wL01PRFVMTzIvY2FuY2VyX2RlX21hbWEuY3N2IikKCiMgVmVyIGVzdHJ1Y3R1cmEKc3RyKGNhbmNlcikKaGVhZChjYW5jZXIpCmBgYAoKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij4gUHJlcGFyYXIgbG9zIGRhdG9zIDwvc3Bhbj4gIApgYGB7cn0KIyBDb252ZXJ0aXIgRGlhZ25vc2lzIGEgYmluYXJpbyAoTT0xLCBCPTApCmNhbmNlciRkaWFnbm9zaXMgPC0gaWZlbHNlKGNhbmNlciRkaWFnbm9zaXMgPT0gIk0iLCAxLCAwKQoKIyBOb3JtYWxpemFyIHRvZGFzIGxhcyB2YXJpYWJsZXMgZXhjZXB0byBkaWFnbm9zaXMKY2FuY2VyX25vcm0gPC0gY2FuY2VyICU+JQogIG11dGF0ZShhY3Jvc3MoLWRpYWdub3NpcywgfiAoLi1taW4oLikpLyhtYXgoLiktbWluKC4pKSkpCgpzdW1tYXJ5KGNhbmNlcl9ub3JtKQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij4gRGl2aWRpciBlbiB0cmFpbi90ZXN0IDwvc3Bhbj4gIApgYGB7cn0Kc2V0LnNlZWQoMTIzKQppbmRpY2VzIDwtIHNhbXBsZSgxOm5yb3coY2FuY2VyX25vcm0pLCBzaXplID0gMC43ICogbnJvdyhjYW5jZXJfbm9ybSkpCgp0cmFpbiA8LSBjYW5jZXJfbm9ybVtpbmRpY2VzLCBdCnRlc3QgIDwtIGNhbmNlcl9ub3JtWy1pbmRpY2VzLCBdCgpjYXQoIkZpbGFzIGRlIHRyYWluOiIsIG5yb3codHJhaW4pLCAiXG4iKQpjYXQoIkZpbGFzIGRlIHRlc3Q6IiwgbnJvdyh0ZXN0KSwgIlxuIikKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IEVudHJhciBhIGxhIHJlZCBuZXVyb25hbCA8L3NwYW4+ICAKYGBge3J9CmYgPC0gYXMuZm9ybXVsYShwYXN0ZSgiZGlhZ25vc2lzIH4iLCBwYXN0ZShuYW1lcyh0cmFpbilbIW5hbWVzKHRyYWluKSAlaW4lICJkaWFnbm9zaXMiXSwgY29sbGFwc2U9IiArICIpKSkKCnJlZF9jYW5jZXIgPC0gbmV1cmFsbmV0KGYsIGRhdGE9dHJhaW4sIGhpZGRlbj1jKDUsMyksIGxpbmVhci5vdXRwdXQ9RkFMU0UpCgpwbG90KHJlZF9jYW5jZXIpCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZTsiPiBJbXBvcnRhbmNpYSBkZSBsYXMgdmFyaWFibGVzIDwvc3Bhbj4gIApgYGB7cn0KbGlicmFyeShOZXVyYWxOZXRUb29scykKbGlicmFyeShnZ3Bsb3QyKQoKIyBPYnRlbmVyIGltcG9ydGFuY2lhIGRlIHZhcmlhYmxlcwppbXAgPC0gb2xkZW4ocmVkX2NhbmNlciwgYmFyX3Bsb3QgPSBGQUxTRSkKCiMgUGFzYXIgYSBkYXRhLmZyYW1lCmltcF9kZiA8LSBkYXRhLmZyYW1lKFZhcmlhYmxlID0gcm93bmFtZXMoaW1wKSwgSW1wb3J0YW5jaWEgPSBpbXBbLDFdKQoKIyBPcmRlbmFyIHRvcCAxMAp0b3BfaW1wIDwtIGltcF9kZltvcmRlcihhYnMoaW1wX2RmJEltcG9ydGFuY2lhKSwgZGVjcmVhc2luZyA9IFRSVUUpLCBdWzE6MTAsIF0KCiMgR3JhZmljYXIgdG9wIDEwCmdncGxvdCh0b3BfaW1wLCBhZXMoeD1yZW9yZGVyKFZhcmlhYmxlLCBJbXBvcnRhbmNpYSksIHk9SW1wb3J0YW5jaWEsIGZpbGw9SW1wb3J0YW5jaWEpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHRpdGxlPSJUb3AgMTAgdmFyaWFibGVzIG3DoXMgaW1wb3J0YW50ZXMiLAogICAgICAgeD0iVmFyaWFibGUiLCB5PSJJbXBvcnRhbmNpYSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKIyA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZTsiPiBDb25jbHVzacOzbiA8L3NwYW4+ICAKRWwgbW9kZWxvIG1vc3Ryw7MgcXVlIGxhcyB2YXJpYWJsZXMgbcOhcyBpbXBvcnRhbnRlcyBwYXJhIHByZWRlY2lyIGVsIGPDoW5jZXIgZGUgbWFtYSBzb24gYXJlYV9zZSB5IHJhZGl1c19zZSwgc2VndWlkYXMgcG9yIGNhcmFjdGVyw61zdGljYXMgY29tbyBjb25jYXZlX3BvaW50cyB5IHRleHR1cmVfd29yc3QuIEVzdG8gY29uZmlybWEgcXVlIHRhbnRvIGVsIHRhbWHDsW8gY29tbyBsYSBpcnJlZ3VsYXJpZGFkIGRlbCB0dW1vciBzb24gZmFjdG9yZXMgY2xhdmUgZW4gbGEgY2xhc2lmaWNhY2nDs24uIEVuIGNvbmp1bnRvLCBsYSByZWQgbmV1cm9uYWwgbG9ncmEgY2FwdHVyYXIgcGF0cm9uZXMgcmVsZXZhbnRlcywgcmVmb3J6YW5kbyBzdSB1dGlsaWRhZCBjb21vIGFwb3lvIGFsIGRpYWduw7NzdGljby4K