Teoría

La mineria de texto (TM) es el proceso de extraer informacion útil, patrones o conocimientos de textos no estrucutrados.

Consta de 3 etapas:
1. Obtener datos: El reconocimiento óptico de caracteres (OCR) es una tecnología que permite convertir imágenes de texto editable. También es conocido como la extracción de texto de imágenes.
2. Explorar datos: Representación grafica o visual de los datos para su interpretación. Los métodos más comunes son el Análisis de sentimientos, la Nube de palabras y el Topic Modeling.
3. Analisis predictivo: Son las técnincas y modelos estadísticos para predecir resultados futuros. Los modelos mas usados son el Random Forest, redes neuronales y regresiones.

Instalar paquetes y librerias

# install.packages("tidyr")
library(tidyr) # Data wranglig (Manipulacion de datos)
# install.packages("tesseract")
library(tesseract) # OCR
# install.packages("magick")
library(magick) # PNG
# install.packages("officer")
library(officer) # Data wranglig (Manipulacion de datos)
# install.packages("pdftools")
library(pdftools) # PDF
library(tidyr) # Data wranglig (Manipulacion de datos)
# install.packages("purrr")
library(purrr) # Para la funcion map
# install.packages("tm")
library(tm) # Text mining
# install.packages("RColorBrewer")
library(RColorBrewer) # colores
# install.packages("wordcloud")
library(wordcloud) # Nube de palablas
# install.packages("topicmodels") # modelo de temas
library(topicmodels) # PDF
# install.packages("ggplot2") # graficas
library(ggplot2)

Obtener datos meidante OCR

imagen1 <- image_read("/Users/kikepablos/Documents/Development/escuela/concentracion_ai/modulo_6/data_sources/imagen1.PNG")
text1 <- ocr(imagen1)
text1
## [1] "Linear regression with one variable x is also known as univariate linear regression\nor simple linear regression. Simple linear regression is used to predict a single\noutput from a single input. This is an example of supervised learning, which means\nthat the data is labeled, i.e., the output values are known in the training data. Let us\nfit a line through the data using simple linear regression as shown in Fig. 4.1.\n"
# doc1 <- read_docx() # crea un documento de word en blanco
# doc1 <- doc1 %>% body_add_par(text1, style= "Normal") # pega el texto en el word
# print(doc1, target = "texto1.docx")

Obtener datos meidante OCR

Consultar idiomas

imagen2 <- image_read("/Users/kikepablos/Documents/Development/escuela/concentracion_ai/modulo_6/data_sources/imagen2.PNG")
# tesseract_download("spa")
texto2 <- ocr(imagen2, engine = tesseract("spa"))
texto2
## [1] "Un importante, y quizá controversial, asunto político es el que se refiere al efecto del salario mínimo sobre\nlas tasas de desempleo en diversos grupos de trabajadores. Aunque este problema puede ser estudiado con\ndiversos tipos de datos (corte transversal, series de tiempo o datos de panel), suelen usarse las series de\ntiempo para observar los efectos agregados. En la tabla 1.3 se presenta un ejemplo de una base de datos\nde series de tiempo sobre tasas de desempleo y salarios mínimos.\n"
# doc2 <- read_docx() # crea un documento de word en blanco
# doc2 <- doc2 %>% body_add_par(texto2, style= "Normal") # pega el texto en el word
# print(doc2, target = "texto2.docx")

De PDF a texto en WORD

pdf1 <- pdf_convert("/Users/kikepablos/Documents/Development/escuela/concentracion_ai/modulo_6/data_sources/pdf1.pdf", dpi=600)  %>% map(ocr)
## Converting page 1 to pdf1_1.png... done!
## Converting page 2 to pdf1_2.png... done!
## Converting page 3 to pdf1_3.png... done!
## Converting page 4 to pdf1_4.png... done!
## Converting page 5 to pdf1_5.png... done!
## Converting page 6 to pdf1_6.png... done!
## Converting page 7 to pdf1_7.png... done!
## Converting page 8 to pdf1_8.png... done!
# doc3 <- read_docx() # crea un documento de word en blanco
# doc3 <- doc3 %>% body_add_par(pdf1, style= "Normal") # pega el texto en el word

Actividad 1. Novela “IT”

pdf2 <- pdf_convert("/Users/kikepablos/Documents/Development/escuela/concentracion_ai/modulo_6/data_sources/eso3.pdf", dpi = 600) %>% map(ocr)
## Converting page 1 to eso3_1.png... done!
## Converting page 2 to eso3_2.png... done!
## Converting page 3 to eso3_3.png... done!
# Agregar el texto extraído del primer archivo PNG
eso1 <- image_read("/Users/kikepablos/Documents/Development/escuela/concentracion_ai/modulo_6/data_sources/eso3_1.png")
eso2 <- image_read("/Users/kikepablos/Documents/Development/escuela/concentracion_ai/modulo_6/data_sources/eso3_2.png")
eso3 <- image_read("/Users/kikepablos/Documents/Development/escuela/concentracion_ai/modulo_6/data_sources/eso3_3.png")
tesseract_download("spa")
## [1] "/Users/kikepablos/Library/Application Support/tesseract5/tessdata/spa.traineddata"
eso_1 <- ocr(eso1, engine = tesseract("spa"))
eso_2 <- ocr(eso2, engine = tesseract("spa"))
eso_3 <- ocr(eso3, engine = tesseract("spa"))

# doc_it <- read_docx()
# doc_it <- doc_it %>% body_add_par(eso_1, style = "Normal") %>% body_add_par(eso_2, style = "Normal") %>% body_add_par(eso_3, style = "Normal")

# Guardar el documento de Word
# print(doc_it, target = "textos_extraidos.docx")

Análisis de Frecuencias

text4 <- readLines("http://www.sthda.com/sthda/RDoc/example-files/martin-luther-king-i-have-a-dream-speech.txt")

corpus <- Corpus(VectorSource(text4)) # Pone cada renglon en una celda de vector

corpus <- tm_map(corpus, content_transformer(tolower)) # Pone todo en minusculas
## Warning in tm_map.SimpleCorpus(corpus, content_transformer(tolower)):
## transformation drops documents
corpus <- tm_map(corpus, removePunctuation) # Elimina puntuación
## Warning in tm_map.SimpleCorpus(corpus, removePunctuation): transformation drops
## documents
corpus <- tm_map(corpus, removeNumbers) # Elimina numeros
## Warning in tm_map.SimpleCorpus(corpus, removeNumbers): transformation drops
## documents
corpus <- tm_map(corpus, removeWords, stopwords("en")) # Elimina palabras que no hablan del texto
## Warning in tm_map.SimpleCorpus(corpus, removeWords, stopwords("en")):
## transformation drops documents
# corpus <- tm_map(corpus, removeWords, c("dream", "will")) # Elimina palabras puntuales

tdm <- TermDocumentMatrix(corpus)
m <- as.matrix(tdm)

frecuencia <- sort(rowSums(m), decreasing = TRUE) # Cuenta la frecuencia de cada palabra en el texto completo.
frecuencia_df <- data.frame(word=names(frecuencia), freq = frecuencia) # Convierte la frecuencia a data frame
frecuencia_df
##                        word freq
## will                   will   17
## freedom             freedom   13
## ring                   ring   12
## dream                 dream   11
## day                     day   11
## let                     let   11
## every                 every    9
## one                     one    8
## able                   able    8
## together           together    7
## nation               nation    4
## mountain           mountain    4
## shall                 shall    4
## faith                 faith    4
## free                   free    4
## today                 today    3
## men                     men    3
## state                 state    3
## children           children    3
## little               little    3
## black                 black    3
## white                 white    3
## made                   made    3
## god                     god    3
## new                     new    3
## sing                   sing    3
## land                   land    3
## last                   last    3
## even                   even    2
## live                   live    2
## meaning             meaning    2
## true                   true    2
## brotherhood     brotherhood    2
## former               former    2
## georgia             georgia    2
## sons                   sons    2
## heat                   heat    2
## mississippi     mississippi    2
## sweltering       sweltering    2
## alabama             alabama    2
## boys                   boys    2
## girls                 girls    2
## hands                 hands    2
## join                   join    2
## words                 words    2
## hill                   hill    2
## places               places    2
## hope                   hope    2
## stone                 stone    2
## thee                   thee    2
## mountainside   mountainside    2
## american           american    1
## deeply               deeply    1
## difficulties   difficulties    1
## face                   face    1
## rooted               rooted    1
## still                 still    1
## though               though    1
## tomorrow           tomorrow    1
## creed                 creed    1
## rise                   rise    1
## created             created    1
## equal                 equal    1
## hold                   hold    1
## selfevident     selfevident    1
## truths               truths    1
## hills                 hills    1
## owners               owners    1
## red                     red    1
## sit                     sit    1
## slave                 slave    1
## slaves               slaves    1
## table                 table    1
## injustice         injustice    1
## justice             justice    1
## oasis                 oasis    1
## oppression       oppression    1
## transformed     transformed    1
## character         character    1
## color                 color    1
## content             content    1
## four                   four    1
## judged               judged    1
## skin                   skin    1
## brothers           brothers    1
## dripping           dripping    1
## governor           governor    1
## interposition interposition    1
## lips                   lips    1
## nullification nullification    1
## racists             racists    1
## right                 right    1
## sisters             sisters    1
## vicious             vicious    1
## crooked             crooked    1
## exalted             exalted    1
## flesh                 flesh    1
## glory                 glory    1
## lord                   lord    1
## low                     low    1
## plain                 plain    1
## revealed           revealed    1
## rough                 rough    1
## see                     see    1
## straight           straight    1
## valley               valley    1
## back                   back    1
## south                 south    1
## beautiful         beautiful    1
## despair             despair    1
## discords           discords    1
## hew                     hew    1
## jail                   jail    1
## jangling           jangling    1
## knowing             knowing    1
## pray                   pray    1
## stand                 stand    1
## struggle           struggle    1
## symphony           symphony    1
## transform         transform    1
## work                   work    1
## country             country    1
## liberty             liberty    1
## sweet                 sweet    1
## tis                     tis    1
## died                   died    1
## fathers             fathers    1
## pilgrim             pilgrim    1
## pride                 pride    1
## america             america    1
## become               become    1
## great                 great    1
## must                   must    1
## hampshire         hampshire    1
## hilltops           hilltops    1
## prodigious       prodigious    1
## mighty               mighty    1
## mountains         mountains    1
## york                   york    1
## alleghenies     alleghenies    1
## heightening     heightening    1
## pennsylvania   pennsylvania    1
## colorado           colorado    1
## rockies             rockies    1
## snowcapped       snowcapped    1
## california       california    1
## curvaceous       curvaceous    1
## slopes               slopes    1
## lookout             lookout    1
## tennessee         tennessee    1
## molehill           molehill    1
## allow                 allow    1
## catholics         catholics    1
## city                   city    1
## gentiles           gentiles    1
## hamlet               hamlet    1
## happens             happens    1
## jews                   jews    1
## negro                 negro    1
## old                     old    1
## protestants     protestants    1
## speed                 speed    1
## spiritual         spiritual    1
## village             village    1
## almighty           almighty    1
## thank                 thank    1
ggplot(head(frecuencia_df, 10), aes(x=reorder(word, - freq)  , y=freq )) +  geom_bar(stat="identity", fill="blue") + labs(title="TOP 10 palabras mas frecuentes", subtitle = "Discurso 'I have a Dream de M. L. King", x= "Palabra", y= "Frecuencia")  +geom_text(aes(label= freq), vjust=-0.5)  + ylim(0,20) 

## Nube de palabras

# El procesamiento de datos antes de la nube de palabras es igual que en el análisi de frecuencias, desde el texto hasta frecuencias_df
set.seed(123)
wordcloud(words=frecuencia_df$word, freq = frecuencia_df$freq, min.freq=1, random.order = FALSE, colors= brewer.pal(8, "RdPu"))

LS0tCnRpdGxlOiAiaXQiCmF1dGhvcjogIkVucmlxdWUgUGFibG9zIEEwMDgzNTAzNyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogInNwYWNlbGFiIgogICAgaGlnaGxpZ3RoOiAia2F0ZSIKLS0tCgohW10oL1VzZXJzL2tpa2VwYWJsb3MvRG9jdW1lbnRzL0RldmVsb3BtZW50L2VzY3VlbGEvY29uY2VudHJhY2lvbl9haS9tb2R1bG9fNi9hc3NldHMvaXQuanBnKQoKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDsiPlRlb3LDrWE8L3NwYW4+CkxhICoqbWluZXJpYSBkZSB0ZXh0byAoVE0pKiogZXMgZWwgcHJvY2VzbyBkZSBleHRyYWVyIGluZm9ybWFjaW9uIMO6dGlsLCBwYXRyb25lcyBvIGNvbm9jaW1pZW50b3MgZGUgdGV4dG9zIG5vIGVzdHJ1Y3V0cmFkb3MuICAKCkNvbnN0YSBkZSAzIGV0YXBhczogIAoqKjEuIE9idGVuZXIgZGF0b3M6KiogRWwgcmVjb25vY2ltaWVudG8gw7NwdGljbyBkZSBjYXJhY3RlcmVzIChPQ1IpIGVzIHVuYSB0ZWNub2xvZ8OtYSBxdWUgcGVybWl0ZSBjb252ZXJ0aXIgaW3DoWdlbmVzIGRlIHRleHRvIGVkaXRhYmxlLiBUYW1iacOpbiBlcyBjb25vY2lkbyBjb21vIGxhICoqZXh0cmFjY2nDs24gZGUgdGV4dG8gZGUgaW3DoWdlbmVzKiouICAKKioyLiBFeHBsb3JhciBkYXRvczoqKiBSZXByZXNlbnRhY2nDs24gZ3JhZmljYSBvIHZpc3VhbCBkZSBsb3MgZGF0b3MgcGFyYSBzdSBpbnRlcnByZXRhY2nDs24uIExvcyBtw6l0b2RvcyBtw6FzIGNvbXVuZXMgc29uIGVsIEFuw6FsaXNpcyBkZSBzZW50aW1pZW50b3MsIGxhIE51YmUgZGUgcGFsYWJyYXMgeSBlbCBUb3BpYyBNb2RlbGluZy4gIAoqKjMuIEFuYWxpc2lzIHByZWRpY3Rpdm86KiogU29uIGxhcyB0w6ljbmluY2FzIHkgbW9kZWxvcyBlc3RhZMOtc3RpY29zIHBhcmEgcHJlZGVjaXIgcmVzdWx0YWRvcyBmdXR1cm9zLiBMb3MgbW9kZWxvcyBtYXMgdXNhZG9zIHNvbiBlbCBSYW5kb20gRm9yZXN0LCByZWRlcyBuZXVyb25hbGVzIHkgcmVncmVzaW9uZXMuIAoKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDsiPkluc3RhbGFyIHBhcXVldGVzIHkgbGlicmVyaWFzPC9zcGFuPgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGluc3RhbGwucGFja2FnZXMoInRpZHlyIikKbGlicmFyeSh0aWR5cikgIyBEYXRhIHdyYW5nbGlnIChNYW5pcHVsYWNpb24gZGUgZGF0b3MpCiMgaW5zdGFsbC5wYWNrYWdlcygidGVzc2VyYWN0IikKbGlicmFyeSh0ZXNzZXJhY3QpICMgT0NSCiMgaW5zdGFsbC5wYWNrYWdlcygibWFnaWNrIikKbGlicmFyeShtYWdpY2spICMgUE5HCiMgaW5zdGFsbC5wYWNrYWdlcygib2ZmaWNlciIpCmxpYnJhcnkob2ZmaWNlcikgIyBEYXRhIHdyYW5nbGlnIChNYW5pcHVsYWNpb24gZGUgZGF0b3MpCiMgaW5zdGFsbC5wYWNrYWdlcygicGRmdG9vbHMiKQpsaWJyYXJ5KHBkZnRvb2xzKSAjIFBERgpsaWJyYXJ5KHRpZHlyKSAjIERhdGEgd3JhbmdsaWcgKE1hbmlwdWxhY2lvbiBkZSBkYXRvcykKIyBpbnN0YWxsLnBhY2thZ2VzKCJwdXJyciIpCmxpYnJhcnkocHVycnIpICMgUGFyYSBsYSBmdW5jaW9uIG1hcAojIGluc3RhbGwucGFja2FnZXMoInRtIikKbGlicmFyeSh0bSkgIyBUZXh0IG1pbmluZwojIGluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKSAjIGNvbG9yZXMKIyBpbnN0YWxsLnBhY2thZ2VzKCJ3b3JkY2xvdWQiKQpsaWJyYXJ5KHdvcmRjbG91ZCkgIyBOdWJlIGRlIHBhbGFibGFzCiMgaW5zdGFsbC5wYWNrYWdlcygidG9waWNtb2RlbHMiKSAjIG1vZGVsbyBkZSB0ZW1hcwpsaWJyYXJ5KHRvcGljbW9kZWxzKSAjIFBERgojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAjIGdyYWZpY2FzCmxpYnJhcnkoZ2dwbG90MikKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQ7Ij5PYnRlbmVyIGRhdG9zIG1laWRhbnRlIE9DUjwvc3Bhbj4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KaW1hZ2VuMSA8LSBpbWFnZV9yZWFkKCIvVXNlcnMva2lrZXBhYmxvcy9Eb2N1bWVudHMvRGV2ZWxvcG1lbnQvZXNjdWVsYS9jb25jZW50cmFjaW9uX2FpL21vZHVsb182L2RhdGFfc291cmNlcy9pbWFnZW4xLlBORyIpCnRleHQxIDwtIG9jcihpbWFnZW4xKQp0ZXh0MQojIGRvYzEgPC0gcmVhZF9kb2N4KCkgIyBjcmVhIHVuIGRvY3VtZW50byBkZSB3b3JkIGVuIGJsYW5jbwojIGRvYzEgPC0gZG9jMSAlPiUgYm9keV9hZGRfcGFyKHRleHQxLCBzdHlsZT0gIk5vcm1hbCIpICMgcGVnYSBlbCB0ZXh0byBlbiBlbCB3b3JkCiMgcHJpbnQoZG9jMSwgdGFyZ2V0ID0gInRleHRvMS5kb2N4IikKYGBgCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDsiPk9idGVuZXIgZGF0b3MgbWVpZGFudGUgT0NSPC9zcGFuPgoKW0NvbnN1bHRhciBpZGlvbWFzXShodHRwczovL3Rlc3NlcmFjdC1vY3IuZ2l0aHViLmlvL3Rlc3Nkb2MvRGF0YS1GaWxlcy1pbi1kaWZmZXJlbnQtdmVyc2lvbnMuaHRtbCkKYGBge3J9CmltYWdlbjIgPC0gaW1hZ2VfcmVhZCgiL1VzZXJzL2tpa2VwYWJsb3MvRG9jdW1lbnRzL0RldmVsb3BtZW50L2VzY3VlbGEvY29uY2VudHJhY2lvbl9haS9tb2R1bG9fNi9kYXRhX3NvdXJjZXMvaW1hZ2VuMi5QTkciKQojIHRlc3NlcmFjdF9kb3dubG9hZCgic3BhIikKdGV4dG8yIDwtIG9jcihpbWFnZW4yLCBlbmdpbmUgPSB0ZXNzZXJhY3QoInNwYSIpKQp0ZXh0bzIKIyBkb2MyIDwtIHJlYWRfZG9jeCgpICMgY3JlYSB1biBkb2N1bWVudG8gZGUgd29yZCBlbiBibGFuY28KIyBkb2MyIDwtIGRvYzIgJT4lIGJvZHlfYWRkX3Bhcih0ZXh0bzIsIHN0eWxlPSAiTm9ybWFsIikgIyBwZWdhIGVsIHRleHRvIGVuIGVsIHdvcmQKIyBwcmludChkb2MyLCB0YXJnZXQgPSAidGV4dG8yLmRvY3giKQpgYGAKIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkOyI+RGUgUERGIGEgdGV4dG8gZW4gV09SRDwvc3Bhbj4KYGBge3J9CnBkZjEgPC0gcGRmX2NvbnZlcnQoIi9Vc2Vycy9raWtlcGFibG9zL0RvY3VtZW50cy9EZXZlbG9wbWVudC9lc2N1ZWxhL2NvbmNlbnRyYWNpb25fYWkvbW9kdWxvXzYvZGF0YV9zb3VyY2VzL3BkZjEucGRmIiwgZHBpPTYwMCkgICU+JSBtYXAob2NyKQojIGRvYzMgPC0gcmVhZF9kb2N4KCkgIyBjcmVhIHVuIGRvY3VtZW50byBkZSB3b3JkIGVuIGJsYW5jbwojIGRvYzMgPC0gZG9jMyAlPiUgYm9keV9hZGRfcGFyKHBkZjEsIHN0eWxlPSAiTm9ybWFsIikgIyBwZWdhIGVsIHRleHRvIGVuIGVsIHdvcmQKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyAiPkFjdGl2aWRhZCAxLiBOb3ZlbGEgIklUIjwvc3Bhbj4KYGBge3J9CnBkZjIgPC0gcGRmX2NvbnZlcnQoIi9Vc2Vycy9raWtlcGFibG9zL0RvY3VtZW50cy9EZXZlbG9wbWVudC9lc2N1ZWxhL2NvbmNlbnRyYWNpb25fYWkvbW9kdWxvXzYvZGF0YV9zb3VyY2VzL2VzbzMucGRmIiwgZHBpID0gNjAwKSAlPiUgbWFwKG9jcikKYGBgCmBgYHtyfQoKCiMgQWdyZWdhciBlbCB0ZXh0byBleHRyYcOtZG8gZGVsIHByaW1lciBhcmNoaXZvIFBORwplc28xIDwtIGltYWdlX3JlYWQoIi9Vc2Vycy9raWtlcGFibG9zL0RvY3VtZW50cy9EZXZlbG9wbWVudC9lc2N1ZWxhL2NvbmNlbnRyYWNpb25fYWkvbW9kdWxvXzYvZGF0YV9zb3VyY2VzL2VzbzNfMS5wbmciKQplc28yIDwtIGltYWdlX3JlYWQoIi9Vc2Vycy9raWtlcGFibG9zL0RvY3VtZW50cy9EZXZlbG9wbWVudC9lc2N1ZWxhL2NvbmNlbnRyYWNpb25fYWkvbW9kdWxvXzYvZGF0YV9zb3VyY2VzL2VzbzNfMi5wbmciKQplc28zIDwtIGltYWdlX3JlYWQoIi9Vc2Vycy9raWtlcGFibG9zL0RvY3VtZW50cy9EZXZlbG9wbWVudC9lc2N1ZWxhL2NvbmNlbnRyYWNpb25fYWkvbW9kdWxvXzYvZGF0YV9zb3VyY2VzL2VzbzNfMy5wbmciKQp0ZXNzZXJhY3RfZG93bmxvYWQoInNwYSIpCmVzb18xIDwtIG9jcihlc28xLCBlbmdpbmUgPSB0ZXNzZXJhY3QoInNwYSIpKQplc29fMiA8LSBvY3IoZXNvMiwgZW5naW5lID0gdGVzc2VyYWN0KCJzcGEiKSkKZXNvXzMgPC0gb2NyKGVzbzMsIGVuZ2luZSA9IHRlc3NlcmFjdCgic3BhIikpCgojIGRvY19pdCA8LSByZWFkX2RvY3goKQojIGRvY19pdCA8LSBkb2NfaXQgJT4lIGJvZHlfYWRkX3Bhcihlc29fMSwgc3R5bGUgPSAiTm9ybWFsIikgJT4lIGJvZHlfYWRkX3Bhcihlc29fMiwgc3R5bGUgPSAiTm9ybWFsIikgJT4lIGJvZHlfYWRkX3Bhcihlc29fMywgc3R5bGUgPSAiTm9ybWFsIikKCiMgR3VhcmRhciBlbCBkb2N1bWVudG8gZGUgV29yZAojIHByaW50KGRvY19pdCwgdGFyZ2V0ID0gInRleHRvc19leHRyYWlkb3MuZG9jeCIpCmBgYAoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDsiPkFuw6FsaXNpcyBkZSBGcmVjdWVuY2lhczwvc3Bhbj4KYGBge3J9CnRleHQ0IDwtIHJlYWRMaW5lcygiaHR0cDovL3d3dy5zdGhkYS5jb20vc3RoZGEvUkRvYy9leGFtcGxlLWZpbGVzL21hcnRpbi1sdXRoZXIta2luZy1pLWhhdmUtYS1kcmVhbS1zcGVlY2gudHh0IikKCmNvcnB1cyA8LSBDb3JwdXMoVmVjdG9yU291cmNlKHRleHQ0KSkgIyBQb25lIGNhZGEgcmVuZ2xvbiBlbiB1bmEgY2VsZGEgZGUgdmVjdG9yCgpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkgIyBQb25lIHRvZG8gZW4gbWludXNjdWxhcwpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlUHVuY3R1YXRpb24pICMgRWxpbWluYSBwdW50dWFjacOzbgpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlTnVtYmVycykgIyBFbGltaW5hIG51bWVyb3MKY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuIikpICMgRWxpbWluYSBwYWxhYnJhcyBxdWUgbm8gaGFibGFuIGRlbCB0ZXh0bwojIGNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCByZW1vdmVXb3JkcywgYygiZHJlYW0iLCAid2lsbCIpKSAjIEVsaW1pbmEgcGFsYWJyYXMgcHVudHVhbGVzCgp0ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1cykKbSA8LSBhcy5tYXRyaXgodGRtKQoKZnJlY3VlbmNpYSA8LSBzb3J0KHJvd1N1bXMobSksIGRlY3JlYXNpbmcgPSBUUlVFKSAjIEN1ZW50YSBsYSBmcmVjdWVuY2lhIGRlIGNhZGEgcGFsYWJyYSBlbiBlbCB0ZXh0byBjb21wbGV0by4KZnJlY3VlbmNpYV9kZiA8LSBkYXRhLmZyYW1lKHdvcmQ9bmFtZXMoZnJlY3VlbmNpYSksIGZyZXEgPSBmcmVjdWVuY2lhKSAjIENvbnZpZXJ0ZSBsYSBmcmVjdWVuY2lhIGEgZGF0YSBmcmFtZQpmcmVjdWVuY2lhX2RmCgpnZ3Bsb3QoaGVhZChmcmVjdWVuY2lhX2RmLCAxMCksIGFlcyh4PXJlb3JkZXIod29yZCwgLSBmcmVxKSAgLCB5PWZyZXEgKSkgKyAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsPSJibHVlIikgKyBsYWJzKHRpdGxlPSJUT1AgMTAgcGFsYWJyYXMgbWFzIGZyZWN1ZW50ZXMiLCBzdWJ0aXRsZSA9ICJEaXNjdXJzbyAnSSBoYXZlIGEgRHJlYW0gZGUgTS4gTC4gS2luZyIsIHg9ICJQYWxhYnJhIiwgeT0gIkZyZWN1ZW5jaWEiKSAgK2dlb21fdGV4dChhZXMobGFiZWw9IGZyZXEpLCB2anVzdD0tMC41KSAgKyB5bGltKDAsMjApIApgYGAKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDsiPk51YmUgZGUgcGFsYWJyYXM8L3NwYW4+CmBgYHtyfQojIEVsIHByb2Nlc2FtaWVudG8gZGUgZGF0b3MgYW50ZXMgZGUgbGEgbnViZSBkZSBwYWxhYnJhcyBlcyBpZ3VhbCBxdWUgZW4gZWwgYW7DoWxpc2kgZGUgZnJlY3VlbmNpYXMsIGRlc2RlIGVsIHRleHRvIGhhc3RhIGZyZWN1ZW5jaWFzX2RmCnNldC5zZWVkKDEyMykKd29yZGNsb3VkKHdvcmRzPWZyZWN1ZW5jaWFfZGYkd29yZCwgZnJlcSA9IGZyZWN1ZW5jaWFfZGYkZnJlcSwgbWluLmZyZXE9MSwgcmFuZG9tLm9yZGVyID0gRkFMU0UsIGNvbG9ycz0gYnJld2VyLnBhbCg4LCAiUmRQdSIpKQpgYGAKCg==