Sesion 6

Camila Bautista

2024-08-11

6.1.1 Instalación de los paquetes tensorflow y keras

Primer ejemplo real usando un clasificador de imágenes básicos, usaremos el paquete Tensorflow para predecir y analizar datos de forma más exhaustiva. Usaremos RMarkdown

library(reticulate)
## Warning: package 'reticulate' was built under R version 4.3.3
py_config()
## python:         C:/Users/HP/OneDrive/Documentos/.virtualenvs/r-tensorflow/Scripts/python.exe
## libpython:      C:/Users/HP/AppData/Local/Programs/Python/Python312/python312.dll
## pythonhome:     C:/Users/HP/OneDrive/Documentos/.virtualenvs/r-tensorflow
## version:        3.12.4 (tags/v3.12.4:8e8a4ba, Jun  6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)]
## Architecture:   64bit
## numpy:          C:/Users/HP/OneDrive/Documentos/.virtualenvs/r-tensorflow/Lib/site-packages/numpy
## numpy_version:  1.26.4
## tensorflow:     C:\Users\HP\OneDrive\DOCUME~1\VIRTUA~1\R-TENS~1\Lib\site-packages\tensorflow\__init__.p
## 
## NOTE: Python version was forced by VIRTUAL_ENV
library(tensorflow)
tf$constant("Hello, TensorFlow!")
## tf.Tensor(b'Hello, TensorFlow!', shape=(), dtype=string)
install.packages("keras")
## Installing package into 'C:/Users/HP/AppData/Local/R/win-library/4.3'
## (as 'lib' is unspecified)
## package 'keras' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\HP\AppData\Local\Temp\Rtmp6pF1xh\downloaded_packages
library(keras) #se pueden hacer estos 2 pasos o ir a la pestaña paquetes, es lo mismo
## Warning: package 'keras' was built under R version 4.3.3

6.1.2 Creando vectores

fashion_mnist <- dataset_fashion_mnist()
#60 mil imágenes de escala grisacea de 28x28px
c(train_images, train_labels) %<-% fashion_mnist$train
#80% de las imágenes
#Arreglo matricial de millones de elementos usados para entrenamiento
#Se usan cuando ya tenemos un resultado y queremos seguir mejorando su visualizando
#o el comportamiento que tienen en el tiempo
c(test_images, test_labels) %<-% fashion_mnist$test
#20% de las imágenes de muestreo
#Con ellas vamos a experimentar el comportamiento previo a un resultado
class_names = c("T-shirt/top", "Trouser", "Pullover", "Dress", "Coat", "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot")

6.1.3 Explorando datos

dim(train_images) #60000 imágenes calidad de 28x28px 
## [1] 60000    28    28
dim(train_labels) #si hay 60k imágenes hay 60k etiquetas
## [1] 60000
train_labels[1:20]
##  [1] 9 0 0 3 0 2 7 2 5 5 0 9 5 5 7 9 1 0 6 4
#Cada etiqueta es un entero entre 0 a 9, segmentación en cuanto a su arreglo de
#los 20 primeros valores que tiene,
#son 60k imágenes cuyos dígitos ya se encuentran estratificadas y segmentadas de 0 a 9

Hay 10k imágenes en el set de muestra, cada imagen representa una resolución de 28x28px

dim(test_images) #10k imágenes
## [1] 10000    28    28
dim(test_labels) #10k imágenes = 10k etiquetas
## [1] 10000

6.1.4 Construcción del modelo y análisis de datos

library(ggplot2)
library(tidyr)
image_1 <- as.data.frame(train_images[1, , ])
#transformamos un dataframe de la base de datos train_images
colnames(image_1) <- seq_len(ncol(image_1))
image_1$y <- seq_len(nrow(image_1))
#es una variable que se va a ubicar al final de las columnas
image_1 <- gather(image_1, "x", "value", -y)
image_1$x <- as.integer(image_1$x)

ggplot(image_1, aes(x = x, y = y, fill = value)) + 
  geom_tile() +
  scale_fill_gradient(low = "white", high = "black", na.value = NA) +
  scale_y_reverse() +
  theme_minimal() +
  theme(panel.grid = element_blank()) +
  theme(aspect.ratio = 1) +
  xlab(" ") +
  ylab(" ")

train_images <- train_images/255
test_images <- test_images/255
par(mfcol = c(5, 5)) #5 filas, 5 columnas 25 imágenes
par(mar = c(0, 0, 1.5, 0), xaxs = "i", yaxs = "i")
for (i in 1:25){
  img <- train_images[i, , ]
  img <- t(apply(img, 2, rev))
  image(1:28, 1:28, img, col = gray((0:255)/255), xaxt = "n", yaxt = "n",
        main = paste(class_names[train_labels[i] +1]))
}

6.1.5 Elaboración, compilación y entrenamiento del modelo, red neural

Código con versión no compatible actualmente en keras:
library(tidyr)
library(keras)
model <- keras_model_sequential()
model %>%
  layer_flatten(input_shape = c(28, 28)) %>%
  layer_activation('relu') %>%
  layer_activation('softmax')

model %>% compile(
  optimizer = "adam",
  loss = "sparse_categorical_crossentropy",
  metrics = c("accuracy")
)

model %>% fit(train_images, train_labels, epochs = 5, verbose = 5)

score <- model %>% evaluate(test_images, test_labels, verbose = 0)
cat("Test loss:", score[1], "\n")

prediction <- model %>% predict(test_images)
predictions[1, ]

which.max(predictions[1,])

class_pred <- model %>% predict_classes(test_images)
class_pred[1:20]
test_labels[1]
library(keras)
input <- layer_input(shape = c(28, 28))
output <- input %>%
  layer_flatten() %>%
  layer_dense(units = 128, activation = "relu") %>%
  layer_dense(units = 10, activation = "softmax")
model <- keras_model(inputs = input, outputs = output)

model$compile(
  optimizer = 'adam',
  loss = 'sparse_categorical_crossentropy',
  metrics = list('accuracy')
)

epochs <- as.integer(5)
print(class(epochs))
## [1] "integer"
model$fit(train_images, train_labels, epochs = epochs, verbose = 2)
## Epoch 1/5
## 1875/1875 - 6s - 3ms/step - accuracy: 0.8237 - loss: 0.5000
## Epoch 2/5
## 1875/1875 - 4s - 2ms/step - accuracy: 0.8652 - loss: 0.3775
## Epoch 3/5
## 1875/1875 - 4s - 2ms/step - accuracy: 0.8759 - loss: 0.3389
## Epoch 4/5
## 1875/1875 - 5s - 2ms/step - accuracy: 0.8857 - loss: 0.3127
## Epoch 5/5
## 1875/1875 - 5s - 2ms/step - accuracy: 0.8924 - loss: 0.2947
## <keras.src.callbacks.history.History object at 0x000001609E183740>
score <- model$evaluate(test_images, test_labels, verbose = 0)
cat("Test loss:", score[1], "\n")
## Test loss: 0.3552181
predictions <- model$predict(test_images)
## 
##   1/313 ━━━━━━━━━━━━━━━━━━━━ 19s 63ms/step
##  35/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step  
##  67/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
##  98/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 129/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 160/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 193/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 223/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 255/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 288/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 313/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step
predictions[1, ]
##  [1] 1.195014e-05 2.915404e-07 9.328386e-07 1.350374e-07 1.844948e-06
##  [6] 3.758254e-03 1.259913e-05 5.831259e-02 1.815648e-05 9.378831e-01
#infitesimales
which.max(predictions[1,]) #la última prenda es la que más se elabora
## [1] 10
predictions <- model$predict(test_images)
## 
##   1/313 ━━━━━━━━━━━━━━━━━━━━ 8s 27ms/step
##  33/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step 
##  68/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 100/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 132/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 165/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 195/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 228/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 264/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 294/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
## 313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step
class_pred <- apply(predictions, 1, which.max) - 1
class_pred[1:20]
##  [1] 9 2 1 1 6 1 4 6 5 7 4 5 7 3 4 1 2 2 8 0
test_labels[1]
## [1] 9

6.1.6 Análisis de varias imágenes a la vez

par(mfcol = c(5,5))
par(mar = c(0, 0, 1.5, 0), xaxs = "i", yaxs = "i")
for(i in 1:25){
  img <- test_images[i, , ]
  img <- t(apply(img, 2, rev))
  #subtract 1 as labels go from 0 to 9
  predicted_label <- which.max(predictions[i, ]) -1
  true_label <- test_labels[i]
  if(predicted_label == true_label){
    color <- "#008800"
  } else {
    color <- "#bb0000"
  }
  image(1:28, 1:28, img, col = gray((0:255)/255), xaxt = "n", yaxt = "n",
        main = paste0(class_names[predicted_label + 1], " (",
                      class_names[true_label + 1], ")"),
        col.main = color)
}

img <- test_images[1, , , drop = F]
dim(img)
## [1]  1 28 28
predictions <- model$predict(img)
## 
## 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step
## 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step
predictions
##              [,1]         [,2]         [,3]         [,4]         [,5]
## [1,] 1.195014e-05 2.915399e-07 9.328351e-07 1.350375e-07 1.844945e-06
##             [,6]         [,7]      [,8]        [,9]     [,10]
## [1,] 0.003758245 1.259914e-05 0.0583126 1.81565e-05 0.9378833
prediction <- predictions[1, ] -1
which.max(prediction)
## [1] 10
predictions <- model$predict(img)
## 
## 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step
## 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 30ms/step
class_pred <- apply(predictions, 1, which.max) - 1
# Restar 1 para que las clases comiencen desde 0, si es necesario
class_pred
## [1] 9
LS0tDQp0aXRsZTogIlNlc2lvbiA2Ig0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0KYXV0aG9yOiBDYW1pbGEgQmF1dGlzdGENCm91dHB1dDoNCiAgcm1kZm9ybWF0czo6ZG93bmN1dGU6DQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICBkZWZhdWx0X3N0eWxlOiAiZGFyayINCiAgICBkb3duY3V0ZV90aGVtZTogImNoYW9zIg0KICAgIHRodW1ibmFpbHM6IHRydWUNCiAgICBsaWdodGJveDogdHJ1ZQ0KICAgIGdhbGxlcnk6IGZhbHNlDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCmxhbmc6ICJlcy1FUyINCi0tLQ0KIyMgNi4xLjEgSW5zdGFsYWNpw7NuIGRlIGxvcyBwYXF1ZXRlcyB0ZW5zb3JmbG93IHkga2VyYXMNClByaW1lciBlamVtcGxvIHJlYWwgdXNhbmRvIHVuIGNsYXNpZmljYWRvciBkZSBpbcOhZ2VuZXMgYsOhc2ljb3MsIHVzYXJlbW9zIGVsIHBhcXVldGUgVGVuc29yZmxvdyBwYXJhIHByZWRlY2lyIHkgYW5hbGl6YXIgZGF0b3MgZGUgZm9ybWEgbcOhcyBleGhhdXN0aXZhLg0KVXNhcmVtb3MgUk1hcmtkb3duDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCm9wdGlvbnMocmVwb3MgPSBjKENSQU4gPSAiaHR0cHM6Ly9jcmFuLnJzdHVkaW8uY29tLyIpKQ0KDQppbnN0YWxsLnBhY2thZ2VzKCJ0ZW5zb3JmbG93IikNCmxpYnJhcnkodGVuc29yZmxvdykNCmBgYA0KDQpgYGB7ciBQcm9iYW5kbyBzaSBsbyBpbnN0YWxhbW9zfQ0KbGlicmFyeShyZXRpY3VsYXRlKQ0KcHlfY29uZmlnKCkNCmBgYA0KDQpgYGB7ciBQcm9iYW5kbyB0ZW5zb3JmbG93fQ0KbGlicmFyeSh0ZW5zb3JmbG93KQ0KdGYkY29uc3RhbnQoIkhlbGxvLCBUZW5zb3JGbG93ISIpDQpgYGANCg0KYGBge3IgSW5zdGFsYWNpw7NuIGRlbCBwYXF1ZXRlIGtlcmFzfQ0KaW5zdGFsbC5wYWNrYWdlcygia2VyYXMiKQ0KbGlicmFyeShrZXJhcykgI3NlIHB1ZWRlbiBoYWNlciBlc3RvcyAyIHBhc29zIG8gaXIgYSBsYSBwZXN0YcOxYSBwYXF1ZXRlcywgZXMgbG8gbWlzbW8NCmBgYA0KIyMgNi4xLjIgQ3JlYW5kbyB2ZWN0b3Jlcw0KYGBge3IgQWNjZXNvIGFsIHBhcXVldGUgTU5JU1R9DQpmYXNoaW9uX21uaXN0IDwtIGRhdGFzZXRfZmFzaGlvbl9tbmlzdCgpDQojNjAgbWlsIGltw6FnZW5lcyBkZSBlc2NhbGEgZ3Jpc2FjZWEgZGUgMjh4MjhweA0KYyh0cmFpbl9pbWFnZXMsIHRyYWluX2xhYmVscykgJTwtJSBmYXNoaW9uX21uaXN0JHRyYWluDQojODAlIGRlIGxhcyBpbcOhZ2VuZXMNCiNBcnJlZ2xvIG1hdHJpY2lhbCBkZSBtaWxsb25lcyBkZSBlbGVtZW50b3MgdXNhZG9zIHBhcmEgZW50cmVuYW1pZW50bw0KI1NlIHVzYW4gY3VhbmRvIHlhIHRlbmVtb3MgdW4gcmVzdWx0YWRvIHkgcXVlcmVtb3Mgc2VndWlyIG1lam9yYW5kbyBzdSB2aXN1YWxpemFuZG8NCiNvIGVsIGNvbXBvcnRhbWllbnRvIHF1ZSB0aWVuZW4gZW4gZWwgdGllbXBvDQpjKHRlc3RfaW1hZ2VzLCB0ZXN0X2xhYmVscykgJTwtJSBmYXNoaW9uX21uaXN0JHRlc3QNCiMyMCUgZGUgbGFzIGltw6FnZW5lcyBkZSBtdWVzdHJlbw0KI0NvbiBlbGxhcyB2YW1vcyBhIGV4cGVyaW1lbnRhciBlbCBjb21wb3J0YW1pZW50byBwcmV2aW8gYSB1biByZXN1bHRhZG8NCmBgYA0KDQpgYGB7ciBDcmVhbmRvIHVuIHZlY3RvciB5IGFsbWFjZW5hbmRvIG5vbWJyZXN9DQpjbGFzc19uYW1lcyA9IGMoIlQtc2hpcnQvdG9wIiwgIlRyb3VzZXIiLCAiUHVsbG92ZXIiLCAiRHJlc3MiLCAiQ29hdCIsICJTYW5kYWwiLCAiU2hpcnQiLCAiU25lYWtlciIsICJCYWciLCAiQW5rbGUgYm9vdCIpDQpgYGANCg0KIyMgNi4xLjMgRXhwbG9yYW5kbyBkYXRvcw0KYGBge3IgVmlzdWFsaXphY2nDs24gZGUgZGF0b3N9DQpkaW0odHJhaW5faW1hZ2VzKSAjNjAwMDAgaW3DoWdlbmVzIGNhbGlkYWQgZGUgMjh4MjhweCANCmBgYA0KDQpgYGB7ciBWaXN1YWxpemFjacOzbiBkZSBldGlxdWV0YXN9DQpkaW0odHJhaW5fbGFiZWxzKSAjc2kgaGF5IDYwayBpbcOhZ2VuZXMgaGF5IDYwayBldGlxdWV0YXMNCmBgYA0KDQpgYGB7ciBTZWdtZW50YWNpw7NufQ0KdHJhaW5fbGFiZWxzWzE6MjBdDQojQ2FkYSBldGlxdWV0YSBlcyB1biBlbnRlcm8gZW50cmUgMCBhIDksIHNlZ21lbnRhY2nDs24gZW4gY3VhbnRvIGEgc3UgYXJyZWdsbyBkZQ0KI2xvcyAyMCBwcmltZXJvcyB2YWxvcmVzIHF1ZSB0aWVuZSwNCiNzb24gNjBrIGltw6FnZW5lcyBjdXlvcyBkw61naXRvcyB5YSBzZSBlbmN1ZW50cmFuIGVzdHJhdGlmaWNhZGFzIHkgc2VnbWVudGFkYXMgZGUgMCBhIDkNCmBgYA0KSGF5IDEwayBpbcOhZ2VuZXMgZW4gZWwgc2V0IGRlIG11ZXN0cmEsIGNhZGEgaW1hZ2VuIHJlcHJlc2VudGEgdW5hIHJlc29sdWNpw7NuIGRlIDI4eDI4cHgNCg0KYGBge3IgTXVlc3RyZW8gZGUgaW3DoWdlbmVzfQ0KZGltKHRlc3RfaW1hZ2VzKSAjMTBrIGltw6FnZW5lcw0KYGBgDQoNCmBgYHtyIEVzdHJhdGlmaWNhY2nDs259DQpkaW0odGVzdF9sYWJlbHMpICMxMGsgaW3DoWdlbmVzID0gMTBrIGV0aXF1ZXRhcw0KYGBgDQoNCiMjIDYuMS40IENvbnN0cnVjY2nDs24gZGVsIG1vZGVsbyB5IGFuw6FsaXNpcyBkZSBkYXRvcw0KYGBge3IgUHJlcHJvY2VzYW1pZW50byBkZSBsYSBkYXRhfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5cikNCmltYWdlXzEgPC0gYXMuZGF0YS5mcmFtZSh0cmFpbl9pbWFnZXNbMSwgLCBdKQ0KI3RyYW5zZm9ybWFtb3MgdW4gZGF0YWZyYW1lIGRlIGxhIGJhc2UgZGUgZGF0b3MgdHJhaW5faW1hZ2VzDQpjb2xuYW1lcyhpbWFnZV8xKSA8LSBzZXFfbGVuKG5jb2woaW1hZ2VfMSkpDQppbWFnZV8xJHkgPC0gc2VxX2xlbihucm93KGltYWdlXzEpKQ0KI2VzIHVuYSB2YXJpYWJsZSBxdWUgc2UgdmEgYSB1YmljYXIgYWwgZmluYWwgZGUgbGFzIGNvbHVtbmFzDQppbWFnZV8xIDwtIGdhdGhlcihpbWFnZV8xLCAieCIsICJ2YWx1ZSIsIC15KQ0KaW1hZ2VfMSR4IDwtIGFzLmludGVnZXIoaW1hZ2VfMSR4KQ0KDQpnZ3Bsb3QoaW1hZ2VfMSwgYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IHZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAiYmxhY2siLCBuYS52YWx1ZSA9IE5BKSArDQogIHNjYWxlX3lfcmV2ZXJzZSgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArDQogIHhsYWIoIiAiKSArDQogIHlsYWIoIiAiKQ0KYGBgDQoNCmBgYHtyIFRyYWluaW5nIHNldCB5IHRlc3Qgc2V0LCBwcmVwcm9jZXNhbWllbnRvKX0NCnRyYWluX2ltYWdlcyA8LSB0cmFpbl9pbWFnZXMvMjU1DQp0ZXN0X2ltYWdlcyA8LSB0ZXN0X2ltYWdlcy8yNTUNCmBgYA0KDQpgYGB7ciBDb25zdHJ1Y2Npw7NuIHkgZW50cmVuYW1pZW50byBkZWwgbW9kZWxvfQ0KcGFyKG1mY29sID0gYyg1LCA1KSkgIzUgZmlsYXMsIDUgY29sdW1uYXMgMjUgaW3DoWdlbmVzDQpwYXIobWFyID0gYygwLCAwLCAxLjUsIDApLCB4YXhzID0gImkiLCB5YXhzID0gImkiKQ0KZm9yIChpIGluIDE6MjUpew0KICBpbWcgPC0gdHJhaW5faW1hZ2VzW2ksICwgXQ0KICBpbWcgPC0gdChhcHBseShpbWcsIDIsIHJldikpDQogIGltYWdlKDE6MjgsIDE6MjgsIGltZywgY29sID0gZ3JheSgoMDoyNTUpLzI1NSksIHhheHQgPSAibiIsIHlheHQgPSAibiIsDQogICAgICAgIG1haW4gPSBwYXN0ZShjbGFzc19uYW1lc1t0cmFpbl9sYWJlbHNbaV0gKzFdKSkNCn0NCmBgYA0KDQojIyA2LjEuNSBFbGFib3JhY2nDs24sIGNvbXBpbGFjacOzbiB5IGVudHJlbmFtaWVudG8gZGVsIG1vZGVsbywgcmVkIG5ldXJhbA0KDQpgYGANCkPDs2RpZ28gY29uIHZlcnNpw7NuIG5vIGNvbXBhdGlibGUgYWN0dWFsbWVudGUgZW4ga2VyYXM6DQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShrZXJhcykNCm1vZGVsIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKQ0KbW9kZWwgJT4lDQogIGxheWVyX2ZsYXR0ZW4oaW5wdXRfc2hhcGUgPSBjKDI4LCAyOCkpICU+JQ0KICBsYXllcl9hY3RpdmF0aW9uKCdyZWx1JykgJT4lDQogIGxheWVyX2FjdGl2YXRpb24oJ3NvZnRtYXgnKQ0KDQptb2RlbCAlPiUgY29tcGlsZSgNCiAgb3B0aW1pemVyID0gImFkYW0iLA0KICBsb3NzID0gInNwYXJzZV9jYXRlZ29yaWNhbF9jcm9zc2VudHJvcHkiLA0KICBtZXRyaWNzID0gYygiYWNjdXJhY3kiKQ0KKQ0KDQptb2RlbCAlPiUgZml0KHRyYWluX2ltYWdlcywgdHJhaW5fbGFiZWxzLCBlcG9jaHMgPSA1LCB2ZXJib3NlID0gNSkNCg0Kc2NvcmUgPC0gbW9kZWwgJT4lIGV2YWx1YXRlKHRlc3RfaW1hZ2VzLCB0ZXN0X2xhYmVscywgdmVyYm9zZSA9IDApDQpjYXQoIlRlc3QgbG9zczoiLCBzY29yZVsxXSwgIlxuIikNCg0KcHJlZGljdGlvbiA8LSBtb2RlbCAlPiUgcHJlZGljdCh0ZXN0X2ltYWdlcykNCnByZWRpY3Rpb25zWzEsIF0NCg0Kd2hpY2gubWF4KHByZWRpY3Rpb25zWzEsXSkNCg0KY2xhc3NfcHJlZCA8LSBtb2RlbCAlPiUgcHJlZGljdF9jbGFzc2VzKHRlc3RfaW1hZ2VzKQ0KY2xhc3NfcHJlZFsxOjIwXQ0KdGVzdF9sYWJlbHNbMV0NCmBgYA0KDQpgYGB7ciBFbGFib3JhY2nDs24sIGNvbXBpbGFjacOzbiB5IGVudHJlbmFtaWVudG8gZGVsIG1vZGVsbywgcmVkIG5ldXJhbCBDw5NESUdPIENPUlJFR0lET30NCmxpYnJhcnkoa2VyYXMpDQppbnB1dCA8LSBsYXllcl9pbnB1dChzaGFwZSA9IGMoMjgsIDI4KSkNCm91dHB1dCA8LSBpbnB1dCAlPiUNCiAgbGF5ZXJfZmxhdHRlbigpICU+JQ0KICBsYXllcl9kZW5zZSh1bml0cyA9IDEyOCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lDQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpDQptb2RlbCA8LSBrZXJhc19tb2RlbChpbnB1dHMgPSBpbnB1dCwgb3V0cHV0cyA9IG91dHB1dCkNCg0KbW9kZWwkY29tcGlsZSgNCiAgb3B0aW1pemVyID0gJ2FkYW0nLA0KICBsb3NzID0gJ3NwYXJzZV9jYXRlZ29yaWNhbF9jcm9zc2VudHJvcHknLA0KICBtZXRyaWNzID0gbGlzdCgnYWNjdXJhY3knKQ0KKQ0KDQplcG9jaHMgPC0gYXMuaW50ZWdlcig1KQ0KcHJpbnQoY2xhc3MoZXBvY2hzKSkNCg0KbW9kZWwkZml0KHRyYWluX2ltYWdlcywgdHJhaW5fbGFiZWxzLCBlcG9jaHMgPSBlcG9jaHMsIHZlcmJvc2UgPSAyKQ0KDQpzY29yZSA8LSBtb2RlbCRldmFsdWF0ZSh0ZXN0X2ltYWdlcywgdGVzdF9sYWJlbHMsIHZlcmJvc2UgPSAwKQ0KY2F0KCJUZXN0IGxvc3M6Iiwgc2NvcmVbMV0sICJcbiIpDQpgYGANCg0KYGBge3IgRXZhbHVhY2nDs24gZGVsIGFjY3VyYWN5fQ0KcHJlZGljdGlvbnMgPC0gbW9kZWwkcHJlZGljdCh0ZXN0X2ltYWdlcykNCnByZWRpY3Rpb25zWzEsIF0NCiNpbmZpdGVzaW1hbGVzDQpgYGANCg0KYGBge3IgRXRpcXVldGEgcHJlZGljY2nDs259DQp3aGljaC5tYXgocHJlZGljdGlvbnNbMSxdKSAjbGEgw7psdGltYSBwcmVuZGEgZXMgbGEgcXVlIG3DoXMgc2UgZWxhYm9yYQ0KYGBgDQoNCmBgYHtyIENsYXNlIGRlIHByZWRpY2Npw7NufQ0KcHJlZGljdGlvbnMgPC0gbW9kZWwkcHJlZGljdCh0ZXN0X2ltYWdlcykNCmNsYXNzX3ByZWQgPC0gYXBwbHkocHJlZGljdGlvbnMsIDEsIHdoaWNoLm1heCkgLSAxDQpjbGFzc19wcmVkWzE6MjBdDQpgYGANCg0KYGBge3IgTW9kZWxvIGRlIGFsdGEgcHJvYmFiaWxkYWQgZGUgY29uZmlhbnphfQ0KdGVzdF9sYWJlbHNbMV0NCmBgYA0KDQojIyA2LjEuNiBBbsOhbGlzaXMgZGUgdmFyaWFzIGltw6FnZW5lcyBhIGxhIHZleg0KDQpgYGB7ciBHcmFmaWNvIGRlIHZhcmlhcyBpbcOhZ2VuZXN9DQpwYXIobWZjb2wgPSBjKDUsNSkpDQpwYXIobWFyID0gYygwLCAwLCAxLjUsIDApLCB4YXhzID0gImkiLCB5YXhzID0gImkiKQ0KZm9yKGkgaW4gMToyNSl7DQogIGltZyA8LSB0ZXN0X2ltYWdlc1tpLCAsIF0NCiAgaW1nIDwtIHQoYXBwbHkoaW1nLCAyLCByZXYpKQ0KICAjc3VidHJhY3QgMSBhcyBsYWJlbHMgZ28gZnJvbSAwIHRvIDkNCiAgcHJlZGljdGVkX2xhYmVsIDwtIHdoaWNoLm1heChwcmVkaWN0aW9uc1tpLCBdKSAtMQ0KICB0cnVlX2xhYmVsIDwtIHRlc3RfbGFiZWxzW2ldDQogIGlmKHByZWRpY3RlZF9sYWJlbCA9PSB0cnVlX2xhYmVsKXsNCiAgICBjb2xvciA8LSAiIzAwODgwMCINCiAgfSBlbHNlIHsNCiAgICBjb2xvciA8LSAiI2JiMDAwMCINCiAgfQ0KICBpbWFnZSgxOjI4LCAxOjI4LCBpbWcsIGNvbCA9IGdyYXkoKDA6MjU1KS8yNTUpLCB4YXh0ID0gIm4iLCB5YXh0ID0gIm4iLA0KICAgICAgICBtYWluID0gcGFzdGUwKGNsYXNzX25hbWVzW3ByZWRpY3RlZF9sYWJlbCArIDFdLCAiICgiLA0KICAgICAgICAgICAgICAgICAgICAgIGNsYXNzX25hbWVzW3RydWVfbGFiZWwgKyAxXSwgIikiKSwNCiAgICAgICAgY29sLm1haW4gPSBjb2xvcikNCn0NCmBgYA0KDQpgYGB7ciBNb2RlbG8gZGUgZW50cmVuYW1pZW50byBjb24gcHJlZGljY2nDs259DQppbWcgPC0gdGVzdF9pbWFnZXNbMSwgLCAsIGRyb3AgPSBGXQ0KZGltKGltZykNCmBgYA0KDQpgYGB7ciBQcmVkZWNpciBpbcOhZ2VuZXN9DQpwcmVkaWN0aW9ucyA8LSBtb2RlbCRwcmVkaWN0KGltZykNCnByZWRpY3Rpb25zDQpgYGANCg0KYGBge3IgU2VsZWNjacOzbiBkZSA0IGltw6FnZW5lc30NCnByZWRpY3Rpb24gPC0gcHJlZGljdGlvbnNbMSwgXSAtMQ0Kd2hpY2gubWF4KHByZWRpY3Rpb24pDQpgYGANCg0KYGBge3IgRXZhbHVhbmRvIGxhIGNsYXNlIGRlIHByZWRpY2Npw7NufQ0KcHJlZGljdGlvbnMgPC0gbW9kZWwkcHJlZGljdChpbWcpDQpjbGFzc19wcmVkIDwtIGFwcGx5KHByZWRpY3Rpb25zLCAxLCB3aGljaC5tYXgpIC0gMQ0KIyBSZXN0YXIgMSBwYXJhIHF1ZSBsYXMgY2xhc2VzIGNvbWllbmNlbiBkZXNkZSAwLCBzaSBlcyBuZWNlc2FyaW8NCmNsYXNzX3ByZWQNCmBgYA0K