Text Mining
Teoría
La minería de texto es el proceso de extraer
información util, patrones o conocimientos de texto no
estructurados.
Consta de 3 etapas: 1. Obtener datos: El reconocimiento óptico de
caracteres (OCR) es una tecnología que permite convertir imágenes de
texto en texto editable. También es conocido como extracción de
texto de imágenes. 2. Explorar datos: Representación gráfica o
visual de los datos para su interpretación. Los metodos más comunes son
el Analisis de Sentimientos, la Nube de Palabras y el Topic Modeling. 3.
Análisis predictivo: Son las tecnicas y modelos estadísticos para
predecir resultados futuros. Los modelos más usados son Random Forest,
Redes Neuronales y Regressiones.
Instalar paquetes y llamar Librerías
library(tidyverse)
library(tesseract)
library(magick)
library(officer)
library(pdftools)
library(purrr)
library(tm)
library(wordcloud)
library(RColorBrewer)
library(topicmodels)
library(ggplot2)
De PDF a Texto en Word
# pdf1 <- pdf_convert("/Users/daviddrums180/Tec/pdf1.pdf", dpi = 600) %>%
# map(ocr)
Actividad 1. Novela “IT”
pdf_path <- "/Users/daviddrums180/Tec/eso3.pdf"
it <- pdf_convert(pdf_path, dpi = 600)
## Converting page 1 to eso3_1.png... done!
## Converting page 2 to eso3_2.png... done!
## Converting page 3 to eso3_3.png... done!
# Configuración del engine de Tesseract para OCR
engine <- tesseract("spa") # Ajusta al idioma de tu documento, "spa" para español por ejemplo
# Convertir el PDF a imágenes y aplicar OCR en cada una
pdf_path <- "/Users/daviddrums180/Tec/eso3.pdf"
imagenes <- pdf_convert(pdf_path, dpi = 600)
## Converting page 1 to eso3_1.png... done!
## Converting page 2 to eso3_2.png... done!
## Converting page 3 to eso3_3.png... done!
# Crear un nuevo documento de Word
doc <- read_docx()
# Aplicar OCR a cada imagen y añadir el texto al documento de Word
for(imagen_path in imagenes) {
texto <- ocr(image_read(imagen_path), engine = engine)
doc <- doc %>%
body_add_par(texto, style = "Normal") %>%
body_add_par("", style = "Normal") # Añade un párrafo en blanco para separar las páginas
}
# Guardar el documento de Word
# print(doc, target = "/Users/daviddrums180/Tec/it.docx")
Leer e inspeccionar en línea
text <- readLines("http://www.sthda.com/sthda/RDoc/example-files/martin-luther-king-i-have-a-dream-speech.txt")
corpus <- Corpus(VectorSource(text)) # Pone cada renglón en una celda de vector
corpus <- tm_map(corpus, content_transformer(tolower))
corpus <- tm_map(corpus, removePunctuation)
corpus <- tm_map(corpus, removeNumbers)
corpus <- tm_map(corpus, removeWords, stopwords("en"))
# inspect(corpus)
# corpus <- tm_map(corpus, removeWords, c("dream","will"))
tdm <- TermDocumentMatrix(corpus)
m <- as.matrix(tdm)
frecuencia <- sort(rowSums(m), decreasing = TRUE)
frecuencia_df <- data.frame(word=names(frecuencia), freq = frecuencia)
ggplot(head(frecuencia_df, 10), aes(x = reorder(word, -freq), y = freq, fill = freq)) +
geom_col() + # Dibuja las barras permitiendo gradiente de color
geom_text(aes(label = freq), vjust = -0.3) + # Añade números encima de las barras
scale_fill_gradient(low = "lightblue", high = "blue", limits = c(0, 20)) + # Gradiente de azul con escala de 0 a 20
labs(title = "TOP 10 Palabras Más Frecuentes",
subtitle = "Discurso Martin Luther King",
x = "Palabra",
y = "Frecuencia") +
theme_minimal()

# El procesamiento de datos antes de la nube de palabras es igual que en el analisis de frecuencias, desde importar el texto hasta la frecuencia en el df
set.seed(123)
wordcloud(words = frecuencia_df$word,
freq = frecuencia_df$freq,
min.freq = 2,
random.order = FALSE,
rot.per = 0.35, # Permite una rotación del 35% de las palabras
colors = brewer.pal(8, "Dark2"), # Usa una paleta de colores 'Dark2' con 8 colores
scale = c(4,0.5), # Ajusta el tamaño de las palabras más y menos frecuentes
max.words = 100) # Limita el número de palabras a 100

Actividad 2
# Cargar las librerías necesarias
library(tm)
library(wordcloud)
library(RColorBrewer)
library(officer)
# Leer el documento .docx
doc <- read_docx("/Users/daviddrums180/Tec/it.docx")
# Extraer el texto del documento
texto <- docx_summary(doc)
texto_completo <- paste(texto$text, collapse=" ")
# Crear un corpus del texto
corpus <- Corpus(VectorSource(texto_completo))
# Preprocesar el texto
corpus <- tm_map(corpus, content_transformer(tolower)) # Convertir a minúsculas
corpus <- tm_map(corpus, removePunctuation) # Quitar puntuación
corpus <- tm_map(corpus, removeNumbers) # Quitar números
corpus <- tm_map(corpus, removeWords, stopwords("spanish")) # Quitar stopwords en español
corpus <- tm_map(corpus, stripWhitespace) # Quitar espacios en blanco adicionales
# Crear una tabla de frecuencias de las palabras
dtm <- TermDocumentMatrix(corpus)
matriz <- as.matrix(dtm)
frecuencia <- sort(rowSums(matriz), decreasing=TRUE)
frecuencia_df <- data.frame(word = names(frecuencia), freq = frecuencia)
ggplot(head(frecuencia_df, 10), aes(x = reorder(word, -freq), y = freq, fill = freq)) +
geom_col() + # Dibuja las barras permitiendo gradiente de color
geom_text(aes(label = freq), vjust = -0.3) + # Añade números encima de las barras
scale_fill_gradient(low = "lightblue", high = "blue", limits = c(0, 30)) + # Gradiente de azul con escala de 0 a 20
labs(title = "TOP 10 Palabras Más Frecuentes",
subtitle = "IT: 3 Capítulos",
x = "Palabra",
y = "Frecuencia") +
theme_minimal()

# Generar la nube de palabras
set.seed(123)
wordcloud(words = frecuencia_df$word, freq = frecuencia_df$freq, min.freq = 2,
random.order=FALSE, rot.per=0.35, colors=brewer.pal(8, "Dark2"), scale=c(4,0.5))

LS0tCnRpdGxlOiAiVGV4dCBNaW5pbmciCmF1dGhvcjogIkRhdmlkIERvbWluZ3VleiAtIEEwMTU3MDk3NSIKZGF0ZTogIjIwMjQtMDItMjYiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0aGVtZTogY29zbW8KLS0tCgojIFRleHQgTWluaW5nCgojIyBUZW9yw61hCkxhICoqbWluZXLDrWEgZGUgdGV4dG8qKiBlcyBlbCBwcm9jZXNvIGRlIGV4dHJhZXIgaW5mb3JtYWNpw7NuIHV0aWwsIHBhdHJvbmVzIG8gY29ub2NpbWllbnRvcyBkZSB0ZXh0byBubyBlc3RydWN0dXJhZG9zLgoKQ29uc3RhIGRlIDMgZXRhcGFzOgoxLiBPYnRlbmVyIGRhdG9zOiBFbCByZWNvbm9jaW1pZW50byDDs3B0aWNvIGRlIGNhcmFjdGVyZXMgKE9DUikgZXMgdW5hIHRlY25vbG9nw61hIHF1ZSBwZXJtaXRlIGNvbnZlcnRpciBpbcOhZ2VuZXMgZGUgdGV4dG8gZW4gdGV4dG8gZWRpdGFibGUuIFRhbWJpw6luIGVzIGNvbm9jaWRvIGNvbW8gKipleHRyYWNjacOzbiBkZSB0ZXh0byBkZSBpbcOhZ2VuZXMqKi4KMi4gRXhwbG9yYXIgZGF0b3M6IFJlcHJlc2VudGFjacOzbiBncsOhZmljYSBvIHZpc3VhbCBkZSBsb3MgZGF0b3MgcGFyYSBzdSBpbnRlcnByZXRhY2nDs24uIExvcyBtZXRvZG9zIG3DoXMgY29tdW5lcyBzb24gZWwgQW5hbGlzaXMgZGUgU2VudGltaWVudG9zLCBsYSBOdWJlIGRlIFBhbGFicmFzIHkgZWwgVG9waWMgTW9kZWxpbmcuCjMuIEFuw6FsaXNpcyBwcmVkaWN0aXZvOiBTb24gbGFzIHRlY25pY2FzIHkgbW9kZWxvcyBlc3RhZMOtc3RpY29zIHBhcmEgcHJlZGVjaXIgcmVzdWx0YWRvcyBmdXR1cm9zLiBMb3MgbW9kZWxvcyBtw6FzIHVzYWRvcyBzb24gUmFuZG9tIEZvcmVzdCwgUmVkZXMgTmV1cm9uYWxlcyB5IFJlZ3Jlc3Npb25lcy4gCgojIyBJbnN0YWxhciBwYXF1ZXRlcyB5IGxsYW1hciBMaWJyZXLDrWFzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRlc3NlcmFjdCkKbGlicmFyeShtYWdpY2spCmxpYnJhcnkob2ZmaWNlcikKbGlicmFyeShwZGZ0b29scykKbGlicmFyeShwdXJycikKbGlicmFyeSh0bSkKbGlicmFyeSh3b3JkY2xvdWQpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHRvcGljbW9kZWxzKQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKIyMgT2J0ZW5lciBEYXRvcyBtZWRpYW50ZSBPQ1IKCmBgYHtyfQppbWFnZW4xIDwtIGltYWdlX3JlYWQoIi9Vc2Vycy9kYXZpZGRydW1zMTgwL1RlYy9pbWFnZW4xLlBORyIpCnRleHRvMSA8LSBvY3IoaW1hZ2VuMSkKdGV4dG8xCmBgYAoKYGBge3J9CmRvYzEgPC0gcmVhZF9kb2N4KCkKZG9jMSA8LSBkb2MxICU+JQogIGJvZHlfYWRkX3Bhcih0ZXh0bzEsIHN0eWxlID0gIk5vcm1hbCIpCgpwcmludChkb2MxLCB0YXJnZXQgPSAiL1VzZXJzL2RhdmlkZHJ1bXMxODAvVGVjL3RleHRvMS5kb2N4IikKYGBgCgoKIyMjIEltYWdlbiBlbiBlc3Bhw7FvbCBQTkcgYSB0ZXh0byBlbiBXT1JECltDb25zdWx0YXIgaWRpb21hcyBkaXNwb25pYmxlc10oaHR0cHM6Ly90ZXNzZXJhY3Qtb2NyLmdpdGh1Yi5pby90ZXNzZG9jL0RhdGEtRmlsZXMtaW4tZGlmZmVyZW50LXZlcnNpb25zLmh0bWwpCmBgYHtyfQppbWFnZW4yIDwtIGltYWdlX3JlYWQoIi9Vc2Vycy9kYXZpZGRydW1zMTgwL1RlYy9pbWFnZW4yLlBORyIpCnRlc3NlcmFjdF9kb3dubG9hZCgic3BhIikKdGV4dG8yIDwtIG9jcihpbWFnZW4yLCBlbmdpbmUgPSB0ZXNzZXJhY3QoInNwYSIpKQp0ZXh0bzIKYGBgCgpgYGB7cn0KZG9jMiA8LSByZWFkX2RvY3goKQpkb2MyIDwtIGRvYzIgJT4lCiAgYm9keV9hZGRfcGFyKHRleHRvMiwgc3R5bGUgPSAiTm9ybWFsIikKCiMgcHJpbnQoZG9jMiwgdGFyZ2V0ID0gIi9Vc2Vycy9kYXZpZGRydW1zMTgwL1RlYy90ZXh0bzIuZG9jeCIpCmBgYAoKCiMjIERlIFBERiBhIFRleHRvIGVuIFdvcmQKYGBge3J9CiMgcGRmMSA8LSBwZGZfY29udmVydCgiL1VzZXJzL2RhdmlkZHJ1bXMxODAvVGVjL3BkZjEucGRmIiwgZHBpID0gNjAwKSAlPiUKICAjIG1hcChvY3IpCmBgYAoKIyMgQWN0aXZpZGFkIDEuIE5vdmVsYSAiSVQiCmBgYHtyfQpwZGZfcGF0aCA8LSAiL1VzZXJzL2RhdmlkZHJ1bXMxODAvVGVjL2VzbzMucGRmIgppdCA8LSBwZGZfY29udmVydChwZGZfcGF0aCwgZHBpID0gNjAwKQpgYGAKCmBgYHtyfQojIENvbmZpZ3VyYWNpw7NuIGRlbCBlbmdpbmUgZGUgVGVzc2VyYWN0IHBhcmEgT0NSCmVuZ2luZSA8LSB0ZXNzZXJhY3QoInNwYSIpICMgQWp1c3RhIGFsIGlkaW9tYSBkZSB0dSBkb2N1bWVudG8sICJzcGEiIHBhcmEgZXNwYcOxb2wgcG9yIGVqZW1wbG8KCiMgQ29udmVydGlyIGVsIFBERiBhIGltw6FnZW5lcyB5IGFwbGljYXIgT0NSIGVuIGNhZGEgdW5hCnBkZl9wYXRoIDwtICIvVXNlcnMvZGF2aWRkcnVtczE4MC9UZWMvZXNvMy5wZGYiCmltYWdlbmVzIDwtIHBkZl9jb252ZXJ0KHBkZl9wYXRoLCBkcGkgPSA2MDApCgojIENyZWFyIHVuIG51ZXZvIGRvY3VtZW50byBkZSBXb3JkCmRvYyA8LSByZWFkX2RvY3goKQoKIyBBcGxpY2FyIE9DUiBhIGNhZGEgaW1hZ2VuIHkgYcOxYWRpciBlbCB0ZXh0byBhbCBkb2N1bWVudG8gZGUgV29yZApmb3IoaW1hZ2VuX3BhdGggaW4gaW1hZ2VuZXMpIHsKICB0ZXh0byA8LSBvY3IoaW1hZ2VfcmVhZChpbWFnZW5fcGF0aCksIGVuZ2luZSA9IGVuZ2luZSkKICBkb2MgPC0gZG9jICU+JSAKICAgIGJvZHlfYWRkX3Bhcih0ZXh0bywgc3R5bGUgPSAiTm9ybWFsIikgJT4lCiAgICBib2R5X2FkZF9wYXIoIiIsIHN0eWxlID0gIk5vcm1hbCIpICMgQcOxYWRlIHVuIHDDoXJyYWZvIGVuIGJsYW5jbyBwYXJhIHNlcGFyYXIgbGFzIHDDoWdpbmFzCn0KCiMgR3VhcmRhciBlbCBkb2N1bWVudG8gZGUgV29yZAojIHByaW50KGRvYywgdGFyZ2V0ID0gIi9Vc2Vycy9kYXZpZGRydW1zMTgwL1RlYy9pdC5kb2N4IikKYGBgCgoKIyMgTGVlciBlIGluc3BlY2Npb25hciBlbiBsw61uZWEKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KdGV4dCA8LSByZWFkTGluZXMoImh0dHA6Ly93d3cuc3RoZGEuY29tL3N0aGRhL1JEb2MvZXhhbXBsZS1maWxlcy9tYXJ0aW4tbHV0aGVyLWtpbmctaS1oYXZlLWEtZHJlYW0tc3BlZWNoLnR4dCIpCgpjb3JwdXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZSh0ZXh0KSkgIyBQb25lIGNhZGEgcmVuZ2zDs24gZW4gdW5hIGNlbGRhIGRlIHZlY3RvcgoKY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpCmNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCByZW1vdmVQdW5jdHVhdGlvbikKY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIHJlbW92ZU51bWJlcnMpCmNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbiIpKQoKIyBpbnNwZWN0KGNvcnB1cykKCiMgY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIHJlbW92ZVdvcmRzLCBjKCJkcmVhbSIsIndpbGwiKSkKCnRkbSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoY29ycHVzKQptIDwtIGFzLm1hdHJpeCh0ZG0pCgpmcmVjdWVuY2lhIDwtIHNvcnQocm93U3VtcyhtKSwgZGVjcmVhc2luZyA9IFRSVUUpCmZyZWN1ZW5jaWFfZGYgPC0gZGF0YS5mcmFtZSh3b3JkPW5hbWVzKGZyZWN1ZW5jaWEpLCBmcmVxID0gZnJlY3VlbmNpYSkKCmdncGxvdChoZWFkKGZyZWN1ZW5jaWFfZGYsIDEwKSwgYWVzKHggPSByZW9yZGVyKHdvcmQsIC1mcmVxKSwgeSA9IGZyZXEsIGZpbGwgPSBmcmVxKSkgKyAKICBnZW9tX2NvbCgpICsgICMgRGlidWphIGxhcyBiYXJyYXMgcGVybWl0aWVuZG8gZ3JhZGllbnRlIGRlIGNvbG9yCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZyZXEpLCB2anVzdCA9IC0wLjMpICsgICMgQcOxYWRlIG7Dum1lcm9zIGVuY2ltYSBkZSBsYXMgYmFycmFzCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlIiwgaGlnaCA9ICJibHVlIiwgbGltaXRzID0gYygwLCAyMCkpICsgICMgR3JhZGllbnRlIGRlIGF6dWwgY29uIGVzY2FsYSBkZSAwIGEgMjAKICBsYWJzKHRpdGxlID0gIlRPUCAxMCBQYWxhYnJhcyBNw6FzIEZyZWN1ZW50ZXMiLCAKICAgICAgIHN1YnRpdGxlID0gIkRpc2N1cnNvIE1hcnRpbiBMdXRoZXIgS2luZyIsIAogICAgICAgeCA9ICJQYWxhYnJhIiwgCiAgICAgICB5ID0gIkZyZWN1ZW5jaWEiKSArCiAgdGhlbWVfbWluaW1hbCgpIApgYGAKCmBgYHtyfQojIEVsIHByb2Nlc2FtaWVudG8gZGUgZGF0b3MgYW50ZXMgZGUgbGEgbnViZSBkZSBwYWxhYnJhcyBlcyBpZ3VhbCBxdWUgZW4gZWwgYW5hbGlzaXMgZGUgZnJlY3VlbmNpYXMsIGRlc2RlIGltcG9ydGFyIGVsIHRleHRvIGhhc3RhIGxhIGZyZWN1ZW5jaWEgZW4gZWwgZGYKc2V0LnNlZWQoMTIzKQp3b3JkY2xvdWQod29yZHMgPSBmcmVjdWVuY2lhX2RmJHdvcmQsCiAgICAgICAgICBmcmVxID0gZnJlY3VlbmNpYV9kZiRmcmVxLAogICAgICAgICAgbWluLmZyZXEgPSAyLAogICAgICAgICAgcmFuZG9tLm9yZGVyID0gRkFMU0UsCiAgICAgICAgICByb3QucGVyID0gMC4zNSwgIyBQZXJtaXRlIHVuYSByb3RhY2nDs24gZGVsIDM1JSBkZSBsYXMgcGFsYWJyYXMKICAgICAgICAgIGNvbG9ycyA9IGJyZXdlci5wYWwoOCwgIkRhcmsyIiksICMgVXNhIHVuYSBwYWxldGEgZGUgY29sb3JlcyAnRGFyazInIGNvbiA4IGNvbG9yZXMKICAgICAgICAgIHNjYWxlID0gYyg0LDAuNSksICMgQWp1c3RhIGVsIHRhbWHDsW8gZGUgbGFzIHBhbGFicmFzIG3DoXMgeSBtZW5vcyBmcmVjdWVudGVzCiAgICAgICAgICBtYXgud29yZHMgPSAxMDApICMgTGltaXRhIGVsIG7Dum1lcm8gZGUgcGFsYWJyYXMgYSAxMDAKYGBgCgojIyBBY3RpdmlkYWQgMgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIENhcmdhciBsYXMgbGlicmVyw61hcyBuZWNlc2FyaWFzCmxpYnJhcnkodG0pCmxpYnJhcnkod29yZGNsb3VkKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShvZmZpY2VyKQoKIyBMZWVyIGVsIGRvY3VtZW50byAuZG9jeApkb2MgPC0gcmVhZF9kb2N4KCIvVXNlcnMvZGF2aWRkcnVtczE4MC9UZWMvaXQuZG9jeCIpCgojIEV4dHJhZXIgZWwgdGV4dG8gZGVsIGRvY3VtZW50bwp0ZXh0byA8LSBkb2N4X3N1bW1hcnkoZG9jKQp0ZXh0b19jb21wbGV0byA8LSBwYXN0ZSh0ZXh0byR0ZXh0LCBjb2xsYXBzZT0iICIpCgojIENyZWFyIHVuIGNvcnB1cyBkZWwgdGV4dG8KY29ycHVzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UodGV4dG9fY29tcGxldG8pKQoKIyBQcmVwcm9jZXNhciBlbCB0ZXh0bwpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkgIyBDb252ZXJ0aXIgYSBtaW7DunNjdWxhcwpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlUHVuY3R1YXRpb24pICMgUXVpdGFyIHB1bnR1YWNpw7NuCmNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCByZW1vdmVOdW1iZXJzKSAjIFF1aXRhciBuw7ptZXJvcwpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsIHN0b3B3b3Jkcygic3BhbmlzaCIpKSAjIFF1aXRhciBzdG9wd29yZHMgZW4gZXNwYcOxb2wKY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIHN0cmlwV2hpdGVzcGFjZSkgIyBRdWl0YXIgZXNwYWNpb3MgZW4gYmxhbmNvIGFkaWNpb25hbGVzCgojIENyZWFyIHVuYSB0YWJsYSBkZSBmcmVjdWVuY2lhcyBkZSBsYXMgcGFsYWJyYXMKZHRtIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChjb3JwdXMpCm1hdHJpeiA8LSBhcy5tYXRyaXgoZHRtKQpmcmVjdWVuY2lhIDwtIHNvcnQocm93U3VtcyhtYXRyaXopLCBkZWNyZWFzaW5nPVRSVUUpCmZyZWN1ZW5jaWFfZGYgPC0gZGF0YS5mcmFtZSh3b3JkID0gbmFtZXMoZnJlY3VlbmNpYSksIGZyZXEgPSBmcmVjdWVuY2lhKQoKZ2dwbG90KGhlYWQoZnJlY3VlbmNpYV9kZiwgMTApLCBhZXMoeCA9IHJlb3JkZXIod29yZCwgLWZyZXEpLCB5ID0gZnJlcSwgZmlsbCA9IGZyZXEpKSArIAogIGdlb21fY29sKCkgKyAgIyBEaWJ1amEgbGFzIGJhcnJhcyBwZXJtaXRpZW5kbyBncmFkaWVudGUgZGUgY29sb3IKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZnJlcSksIHZqdXN0ID0gLTAuMykgKyAgIyBBw7FhZGUgbsO6bWVyb3MgZW5jaW1hIGRlIGxhcyBiYXJyYXMKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJsaWdodGJsdWUiLCBoaWdoID0gImJsdWUiLCBsaW1pdHMgPSBjKDAsIDMwKSkgKyAgIyBHcmFkaWVudGUgZGUgYXp1bCBjb24gZXNjYWxhIGRlIDAgYSAyMAogIGxhYnModGl0bGUgPSAiVE9QIDEwIFBhbGFicmFzIE3DoXMgRnJlY3VlbnRlcyIsIAogICAgICAgc3VidGl0bGUgPSAiSVQ6IDMgQ2Fww610dWxvcyIsIAogICAgICAgeCA9ICJQYWxhYnJhIiwgCiAgICAgICB5ID0gIkZyZWN1ZW5jaWEiKSArCiAgdGhlbWVfbWluaW1hbCgpIAoKIyBHZW5lcmFyIGxhIG51YmUgZGUgcGFsYWJyYXMKc2V0LnNlZWQoMTIzKQp3b3JkY2xvdWQod29yZHMgPSBmcmVjdWVuY2lhX2RmJHdvcmQsIGZyZXEgPSBmcmVjdWVuY2lhX2RmJGZyZXEsIG1pbi5mcmVxID0gMiwKICAgICAgICAgIHJhbmRvbS5vcmRlcj1GQUxTRSwgcm90LnBlcj0wLjM1LCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSwgc2NhbGU9Yyg0LDAuNSkpCmBgYAoK