Teoría
La minería de texto (TM) es el proceso de extraer
información útil, patrones o conocimiento de textos 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
en imágenes.
2. Explorar datos: Representación gráfica 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. Análisis predictivo: Son las técnicas y modelos estadísticos para
predecir resultados futuros. Los modelos más usados son el Random
Forest, redes neuronales y regresiones.
1. Obtener datos mediante OCR
imagen1 <- image_read("C:\\Users\\kathi\\OneDrive\\Escritorio\\M2_IA con Impacto Empresarial\\imagen1.PNG")
texto1 <- ocr(imagen1)
texto1
## [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(texto1, style ="Normal") # Pega el texto en el word
# print(doc1, target = "texto1.docx") # Guarda el word en la computadora
Imagen en español PNG a texto en
WORD
Consultar
idiomas disponibles
imagen2 <- image_read("C:\\Users\\kathi\\OneDrive\\Escritorio\\M2_IA con Impacto Empresarial\\imagen2.PNG")
# tesseract_download("spa") # Cuando se use en español
# texto2 <- ocr(imagen2, engine = tesseract("spa"))
texto2 <- ocr(imagen2)
texto2
## [1] "Un importante, y quiza controversial, asunto politico es el que se refiere al efecto del salario minimo 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 minimos.\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") # Guarda el word en la computadora
De PDF a texto en WORD
pdf1 <- pdf_convert("C:\\Users\\kathi\\OneDrive\\Escritorio\\M2_IA con Impacto Empresarial\\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!
Actividad 1. Novela “IT”
Convertir de PDF a texto en
WORD
pdf2<- pdf_convert( "C:\\Users\\kathi\\OneDrive\\Escritorio\\M2_IA con Impacto Empresarial\\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!
Convertir imágenes en español PNG a texto
en WORD
imagen3 <-image_read("eso3_1.png")
tesseract_download("spa")
## [1] "C:\\Users\\kathi\\AppData\\Local\\tesseract5\\tesseract5\\tessdata/spa.traineddata"
text3 <- ocr(imagen3, engine = tesseract("spa"))
imagen4 <-image_read("eso3_2.png")
tesseract_download("spa")
## [1] "C:\\Users\\kathi\\AppData\\Local\\tesseract5\\tesseract5\\tessdata/spa.traineddata"
text4 <- ocr(imagen4, engine = tesseract("spa"))
imagen5 <-image_read("eso3_3.png")
tesseract_download("spa")
## [1] "C:\\Users\\kathi\\AppData\\Local\\tesseract5\\tesseract5\\tessdata/spa.traineddata"
text5 <- ocr(imagen5, engine = tesseract("spa"))
doc3 <- read_docx() #Crea un documento de word en blanco
doc3 <- doc3 %>%
body_add_par(text3, style = "Normal") %>%
body_add_par(text4, style = "Normal") %>%
body_add_par(text5, style = "Normal")
# Guardar el documento
# print(doc3, target = "it.docx")
2. Exploración de Datos
Análisis de Frecuencias
text <- readLines("C:\\Users\\kathi\\OneDrive\\Escritorio\\M2_IA con Impacto Empresarial\\martin-luther-king-i-have-a-dream-speech.txt")
corpus <- Corpus(VectorSource(text)) # Pone cada renglón en una celda de vector
# inspect(corpus)
corpus <- tm_map(corpus, content_transformer(tolower)) # Pone todo en minúsculas
## 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 números
## Warning in tm_map.SimpleCorpus(corpus, removeNumbers): transformation drops
## documents
corpus <- tm_map(corpus, removeWords, stopwords("en")) # Elimina palabras que no hablen del tema
## 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) # Cuenta las veces que aparece cada palabra por renglón
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
ggplot(head(frecuencia_df,10), aes(x=word, y=freq)) +
geom_bar(stat="identity", fill="pink") +
geom_text(aes(label = freq), vjust = -0.5) +
labs(title="TOP 10 palabras más frecuentes", subtitle="Discurso 'I have a Dream' de M.L: King", x= "Palabra", y="Frecuencia") +
ylim(0,20)

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

Ejercicio 2. Novela IT
texto <- readLines("C:\\Users\\kathi\\OneDrive\\Escritorio\\M2_IA con Impacto Empresarial\\it.txt")
text2 <- iconv(texto, to = "UTF-8", sub = "byte") # Convertir texto codificado a caracteres legibles
corpus2 <- Corpus(VectorSource(text2))
corpus2 <- tm_map(corpus2, content_transformer(tolower)) # Pone todo en minúsculas
## Warning in tm_map.SimpleCorpus(corpus2, content_transformer(tolower)):
## transformation drops documents
corpus2 <- tm_map(corpus2, removePunctuation) # Elimina puntuación
## Warning in tm_map.SimpleCorpus(corpus2, removePunctuation): transformation
## drops documents
corpus2 <- tm_map(corpus2, removeNumbers) # Elimina números
## Warning in tm_map.SimpleCorpus(corpus2, removeNumbers): transformation drops
## documents
corpus2 <- tm_map(corpus2, removeWords, stopwords("spanish")) # Elimina palabras que no hablen del tema
## Warning in tm_map.SimpleCorpus(corpus2, removeWords, stopwords("spanish")):
## transformation drops documents
# corpus <- tm_map(corpus, removeWords, c("dream", "will")) #Elimina palabras puntuales
tdm2 <- TermDocumentMatrix(corpus2)
m2 <- as.matrix(tdm2) # Cuenta las veces que aparece cada palabra por renglón
frecuencia2 <- sort(rowSums(m2), decreasing = TRUE) # Cuenta la frecuencia de cada palabra en el texto completo
frecuencia_df2 <- data.frame(word = names(frecuencia2), freq = frecuencia2) # Convierte la frecuencia a data frame
ggplot(head(frecuencia_df2, 10), aes(x = reorder(word, -freq), y = freq)) +
geom_bar(stat = "identity", fill = "red") +
geom_text(aes(label = freq), vjust = -0.5) +
labs(title = "TOP 10 palabras más frecuentes",
subtitle = "IT",
x = "Palabra",
y = "Frecuencia") +
ylim(0, 20)
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_bar()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_text()`).

Nube de Palabras
# El procesamiento de datos antes de la nube de palabras es igual que en el Análisis de Frecuencias, desde importar el texto hasta frecuencia_df2
set.seed(123)
wordcloud(words=frecuencia_df2$word,freq=frecuencia_df2$freq, min.freq=1,
random.order=FALSE, colors = brewer.pal(8, "RdPu"))

LS0tDQp0aXRsZTogIlRleHQgTWluaW5nIg0KYXV0aG9yOiAiS2F0aGlhIEdlcmFsZGluZSBSdWl6IENhc3RlbMOhbiINCmRhdGU6ICIyMDI0LTAyLTI2Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAgdGhlbWU6IGNvc21vDQotLS0NCg0KIVtdKEM6XFxVc2Vyc1xca2F0aGlcXE9uZURyaXZlXFxFc2NyaXRvcmlvXFxNMl9JQSBjb24gSW1wYWN0byBFbXByZXNhcmlhbFxcaXRfaW1nLmdpZikNCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZDsiPlRlb3LDrWE8L3NwYW4+DQpMYSAqKm1pbmVyw61hIGRlIHRleHRvKiogKFRNKSBlcyBlbCBwcm9jZXNvIGRlIGV4dHJhZXIgDQppbmZvcm1hY2nDs24gw7p0aWwsIHBhdHJvbmVzIG8gY29ub2NpbWllbnRvIGRlIHRleHRvcyBubw0KZXN0cnVjdHVyYWRvcy4NCg0KQ29uc3RhIGRlIDMgZXRhcGFzOiAgDQoxLiBPYnRlbmVyIGRhdG9zOiBFbCAqKnJlY29ub2NpbWllbnRvIMOzcHRpY28gZGUgY2FyYWN0ZXJlcyAoT0NSKSoqIGVzIHVuYSB0ZWNub2xvZ8OtYSBxdWUgcGVybWl0ZSBjb252ZXJ0aXIgaW3DoWdlbmVzIGRlIHRleHRvIGVuIHRleHRvIGVkaXRhYmxlLiBUYW1iacOpbiBlcyBjb25vY2lkbyBjb21vICoqZXh0cmFjY2nDs24gZGUgdGV4dG8gZW4gaW3DoWdlbmVzKiouICANCjIuIEV4cGxvcmFyIGRhdG9zOiBSZXByZXNlbnRhY2nDs24gZ3LDoWZpY2EgbyB2aXN1YWwgZGUgbG9zIGRhdG9zIHBhcmEgc3UgaW50ZXJwcmV0YWNpw7NuLiBMb3MgbcOpdG9kb3MgbcOhcyBjb211bmVzIHNvbiBlbCBBbsOhbGlzaXMgZGUgU2VudGltaWVudG9zLCBsYSBOdWJlIGRlIFBhbGFicmFzIHkgZWwgVG9waWMgTW9kZWxpbmcuICANCjMuIEFuw6FsaXNpcyBwcmVkaWN0aXZvOiBTb24gbGFzIHTDqWNuaWNhcyB5IG1vZGVsb3MgZXN0YWTDrXN0aWNvcyAgcGFyYSBwcmVkZWNpciByZXN1bHRhZG9zIGZ1dHVyb3MuIExvcyBtb2RlbG9zIG3DoXMgdXNhZG9zIHNvbiBlbCBSYW5kb20gRm9yZXN0LCByZWRlcyBuZXVyb25hbGVzIHkgcmVncmVzaW9uZXMuDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQ7Ij5JbnN0YWxhciBwYXF1ZXRlcyB5IGxsYW1hciBsaWJyZXLDrWFzPC9zcGFuPg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKSAjIERhdGEgd3JhbmdsaW5nDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJ0ZXNzZXJhY3QiKSAjIE9DUg0KbGlicmFyeSh0ZXNzZXJhY3QpDQojaW5zdGFsbC5wYWNrYWdlcygibWFnaWNrIikgIyBQTkcNCmxpYnJhcnkobWFnaWNrKQ0KI2luc3RhbGwucGFja2FnZXMoIm9mZmljZXIiKSAjIEV4cG9ydGFyIGEgZm9ybWF0b3MgT2ZmaWNlIChFc3BlY2lhbG1lbnRlIFdvcmQpDQpsaWJyYXJ5KG9mZmljZXIpDQojaW5zdGFsbC5wYWNrYWdlcygicGRmdG9vbHMiKSAjIFBERg0KbGlicmFyeShwZGZ0b29scykNCiNpbnN0YWxsLnBhY2thZ2VzKCJwdXJyciIpICMgUGFyYSBsYSBmdW5jacOzbiBtYXAgcGFyYSBhcGxpY2FyIHVuYSBmdW5jacOzbiBhIGNhZGEgZWxlbWVudG8gZGUgdW4gdmVjdG9yDQpsaWJyYXJ5KHB1cnJyKQ0KI2luc3RhbGwucGFja2FnZXMoInRtIikgIyBUZXh0IE1pbmluZw0KbGlicmFyeSh0bSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKSAjIFBhcmEgbWFuZWphciBjb2xvcmVzDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ3b3JkY2xvdWQiKSAjIFBhcmEgaGFjZXIgTnViZXMgZGUgUGFsYWJyYXMNCmxpYnJhcnkod29yZGNsb3VkKQ0KI2luc3RhbGwucGFja2FnZXMoInRvcGljbW9kZWxzIikgIyBNb2RlbG9zIGRlIFRlbWFzDQpsaWJyYXJ5KHRvcGljbW9kZWxzKQ0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAjIEdyw6FmaWNhcyBjb24gbcOhcyBkaXNlw7FvDQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZDsiPjEuIE9idGVuZXIgZGF0b3MgbWVkaWFudGUgT0NSPC9zcGFuPg0KYGBge3J9DQppbWFnZW4xIDwtIGltYWdlX3JlYWQoIkM6XFxVc2Vyc1xca2F0aGlcXE9uZURyaXZlXFxFc2NyaXRvcmlvXFxNMl9JQSBjb24gSW1wYWN0byBFbXByZXNhcmlhbFxcaW1hZ2VuMS5QTkciKQ0KdGV4dG8xIDwtIG9jcihpbWFnZW4xKQ0KdGV4dG8xDQpkb2MxIDwtIHJlYWRfZG9jeCgpICMgQ3JlYSB1biBkb2N1bWVudG8gZGUgd29yZCBlbiBibGFuY28NCmRvYzEgPC0gZG9jMSAlPiUgYm9keV9hZGRfcGFyKHRleHRvMSwgc3R5bGUgPSJOb3JtYWwiKSAjIFBlZ2EgZWwgdGV4dG8gZW4gZWwgd29yZCANCiMgcHJpbnQoZG9jMSwgdGFyZ2V0ID0gInRleHRvMS5kb2N4IikgIyBHdWFyZGEgZWwgd29yZCBlbiBsYSBjb21wdXRhZG9yYQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+SW1hZ2VuIGVuIGVzcGHDsW9sIFBORyBhIHRleHRvIGVuIFdPUkQ8L3NwYW4+DQoNCltDb25zdWx0YXIgaWRpb21hcyBkaXNwb25pYmxlc10oaHR0cHM6Ly9naXRodWIuY29tL3Rlc3NlcmFjdC1vY3IvdGVzc2RvYy9ibG9iL21haW4vRGF0YS1GaWxlcy1pbi1kaWZmZXJlbnQtdmVyc2lvbnMubWQpDQpgYGB7cn0NCmltYWdlbjIgPC0gaW1hZ2VfcmVhZCgiQzpcXFVzZXJzXFxrYXRoaVxcT25lRHJpdmVcXEVzY3JpdG9yaW9cXE0yX0lBIGNvbiBJbXBhY3RvIEVtcHJlc2FyaWFsXFxpbWFnZW4yLlBORyIpDQojIHRlc3NlcmFjdF9kb3dubG9hZCgic3BhIikgIyBDdWFuZG8gc2UgdXNlIGVuIGVzcGHDsW9sDQojIHRleHRvMiA8LSBvY3IoaW1hZ2VuMiwgZW5naW5lID0gdGVzc2VyYWN0KCJzcGEiKSkNCnRleHRvMiA8LSBvY3IoaW1hZ2VuMikNCnRleHRvMg0KZG9jMiA8LSByZWFkX2RvY3goKSAjIENyZWEgdW4gZG9jdW1lbnRvIGRlIHdvcmQgZW4gYmxhbmNvDQpkb2MyIDwtIGRvYzIgJT4lIGJvZHlfYWRkX3Bhcih0ZXh0bzIsIHN0eWxlID0iTm9ybWFsIikjIFBlZ2EgZWwgdGV4dG8gZW4gZWwgd29yZA0KIyBwcmludChkb2MyLCB0YXJnZXQgPSAidGV4dG8yLmRvY3giKSAjIEd1YXJkYSBlbCB3b3JkIGVuIGxhIGNvbXB1dGFkb3JhDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQ7Ij5EZSBQREYgYSB0ZXh0byBlbiBXT1JEPC9zcGFuPg0KYGBge3J9DQpwZGYxIDwtIHBkZl9jb252ZXJ0KCJDOlxcVXNlcnNcXGthdGhpXFxPbmVEcml2ZVxcRXNjcml0b3Jpb1xcTTJfSUEgY29uIEltcGFjdG8gRW1wcmVzYXJpYWxcXHBkZjEucGRmIiwgZHBpPTYwMCkgJT4lIG1hcChvY3IpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQ7Ij5BY3RpdmlkYWQgMS4gTm92ZWxhICJJVCI8L3NwYW4+DQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+Q29udmVydGlyIGRlIFBERiBhIHRleHRvIGVuIFdPUkQ8L3NwYW4+DQpgYGB7cn0NCnBkZjI8LSBwZGZfY29udmVydCggIkM6XFxVc2Vyc1xca2F0aGlcXE9uZURyaXZlXFxFc2NyaXRvcmlvXFxNMl9JQSBjb24gSW1wYWN0byBFbXByZXNhcmlhbFxcZXNvMy5wZGYiICwgZHBpPTYwMCkgICU+JSBtYXAob2NyKQ0KYGBgDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZDsiPkNvbnZlcnRpciBpbcOhZ2VuZXMgZW4gZXNwYcOxb2wgUE5HIGEgdGV4dG8gZW4gV09SRDwvc3Bhbj4NCmBgYHtyfQ0KaW1hZ2VuMyA8LWltYWdlX3JlYWQoImVzbzNfMS5wbmciKQ0KdGVzc2VyYWN0X2Rvd25sb2FkKCJzcGEiKQ0KdGV4dDMgPC0gb2NyKGltYWdlbjMsIGVuZ2luZSA9IHRlc3NlcmFjdCgic3BhIikpDQoNCmltYWdlbjQgPC1pbWFnZV9yZWFkKCJlc28zXzIucG5nIikNCnRlc3NlcmFjdF9kb3dubG9hZCgic3BhIikNCnRleHQ0IDwtIG9jcihpbWFnZW40LCBlbmdpbmUgPSB0ZXNzZXJhY3QoInNwYSIpKQ0KDQppbWFnZW41IDwtaW1hZ2VfcmVhZCgiZXNvM18zLnBuZyIpDQp0ZXNzZXJhY3RfZG93bmxvYWQoInNwYSIpDQp0ZXh0NSA8LSBvY3IoaW1hZ2VuNSwgZW5naW5lID0gdGVzc2VyYWN0KCJzcGEiKSkNCg0KZG9jMyA8LSByZWFkX2RvY3goKSAjQ3JlYSB1biBkb2N1bWVudG8gZGUgd29yZCBlbiBibGFuY28NCg0KDQpkb2MzIDwtIGRvYzMgJT4lDQogIGJvZHlfYWRkX3Bhcih0ZXh0Mywgc3R5bGUgPSAiTm9ybWFsIikgJT4lDQogIGJvZHlfYWRkX3Bhcih0ZXh0NCwgc3R5bGUgPSAiTm9ybWFsIikgJT4lDQogIGJvZHlfYWRkX3Bhcih0ZXh0NSwgc3R5bGUgPSAiTm9ybWFsIikNCg0KIyBHdWFyZGFyIGVsIGRvY3VtZW50bw0KIyBwcmludChkb2MzLCB0YXJnZXQgPSAiaXQuZG9jeCIpDQpgYGANCg0KDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQ7Ij4yLiBFeHBsb3JhY2nDs24gZGUgRGF0b3M8L3NwYW4+DQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+QW7DoWxpc2lzIGRlIEZyZWN1ZW5jaWFzPC9zcGFuPg0KYGBge3J9DQp0ZXh0IDwtIHJlYWRMaW5lcygiQzpcXFVzZXJzXFxrYXRoaVxcT25lRHJpdmVcXEVzY3JpdG9yaW9cXE0yX0lBIGNvbiBJbXBhY3RvIEVtcHJlc2FyaWFsXFxtYXJ0aW4tbHV0aGVyLWtpbmctaS1oYXZlLWEtZHJlYW0tc3BlZWNoLnR4dCIpDQoNCmNvcnB1cyA8LSBDb3JwdXMoVmVjdG9yU291cmNlKHRleHQpKSAjIFBvbmUgY2FkYSByZW5nbMOzbiBlbiB1bmEgY2VsZGEgZGUgdmVjdG9yDQojIGluc3BlY3QoY29ycHVzKQ0KDQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkgIyBQb25lIHRvZG8gZW4gbWluw7pzY3VsYXMNCmNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCByZW1vdmVQdW5jdHVhdGlvbikgIyBFbGltaW5hIHB1bnR1YWNpw7NuDQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlTnVtYmVycykgIyBFbGltaW5hIG7Dum1lcm9zDQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW4iKSkgIyBFbGltaW5hIHBhbGFicmFzIHF1ZSBubyBoYWJsZW4gZGVsIHRlbWENCiMgY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIHJlbW92ZVdvcmRzLCBjKCJkcmVhbSIsICJ3aWxsIikpICNFbGltaW5hIHBhbGFicmFzIHB1bnR1YWxlcw0KDQp0ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1cykNCm0gPC0gYXMubWF0cml4KHRkbSkgIyBDdWVudGEgbGFzIHZlY2VzIHF1ZSBhcGFyZWNlIGNhZGEgcGFsYWJyYSBwb3IgcmVuZ2zDs24NCiAgICAgICAgICAgICAgIA0KZnJlY3VlbmNpYSA8LSBzb3J0KHJvd1N1bXMobSksIGRlY3JlYXNpbmcgPSBUUlVFKSAjIEN1ZW50YSBsYSBmcmVjdWVuY2lhIGRlIGNhZGEgcGFsYWJyYSBlbiBlbCB0ZXh0byBjb21wbGV0bw0KDQpmcmVjdWVuY2lhX2RmIDwtIGRhdGEuZnJhbWUod29yZD1uYW1lcyhmcmVjdWVuY2lhKSxmcmVxPWZyZWN1ZW5jaWEpICMgQ29udmllcnRlIGxhIGZyZWN1ZW5jaWEgYSBkYXRhIGZyYW1lDQoNCmdncGxvdChoZWFkKGZyZWN1ZW5jaWFfZGYsMTApLCBhZXMoeD13b3JkLCB5PWZyZXEpKSArIA0KICAgICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0icGluayIpICsgDQogICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZyZXEpLCB2anVzdCA9IC0wLjUpICsNCiAgICAgICBsYWJzKHRpdGxlPSJUT1AgMTAgcGFsYWJyYXMgbcOhcyBmcmVjdWVudGVzIiwgc3VidGl0bGU9IkRpc2N1cnNvICdJIGhhdmUgYSBEcmVhbScgZGUgTS5MOiBLaW5nIiwgeD0gIlBhbGFicmEiLCB5PSJGcmVjdWVuY2lhIikgKyANCiAgICAgICB5bGltKDAsMjApDQoNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZDsiPk51YmUgZGUgUGFsYWJyYXM8L3NwYW4+DQpgYGB7cn0NCiMgRWwgcHJvY2VzYW1pZW50byBkZSBkYXRvcyBhbnRlcyBkZSBsYSBudWJlIGRlIHBhbGFicmFzIGVzIGlndWFsIHF1ZSBlbiBlbCBBbsOhbGlzaXMgZGUgRnJlY3VlbmNpYXMsIGRlc2RlIGltcG9ydGFyIGVsIHRleHRvIGhhc3RhIGZyZWN1ZW5jaWFfZGYNCnNldC5zZWVkKDEyMykNCndvcmRjbG91ZCh3b3Jkcz1mcmVjdWVuY2lhX2RmJHdvcmQsZnJlcT1mcmVjdWVuY2lhX2RmJGZyZXEsIG1pbi5mcmVxPTEsDQpyYW5kb20ub3JkZXI9RkFMU0UsIGNvbG9ycyA9IGJyZXdlci5wYWwoOCwgIlJkUHUiKSkNCmBgYA0KDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+RWplcmNpY2lvIDIuIE5vdmVsYSBJVDwvc3Bhbj4NCmBgYHtyfQ0KdGV4dG8gPC0gcmVhZExpbmVzKCJDOlxcVXNlcnNcXGthdGhpXFxPbmVEcml2ZVxcRXNjcml0b3Jpb1xcTTJfSUEgY29uIEltcGFjdG8gRW1wcmVzYXJpYWxcXGl0LnR4dCIpDQoNCnRleHQyIDwtIGljb252KHRleHRvLCB0byA9ICJVVEYtOCIsIHN1YiA9ICJieXRlIikgIyBDb252ZXJ0aXIgdGV4dG8gY29kaWZpY2FkbyBhIGNhcmFjdGVyZXMgbGVnaWJsZXMNCg0KY29ycHVzMiA8LSBDb3JwdXMoVmVjdG9yU291cmNlKHRleHQyKSkgDQoNCmNvcnB1czIgPC0gdG1fbWFwKGNvcnB1czIsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICMgUG9uZSB0b2RvIGVuIG1pbsO6c2N1bGFzDQpjb3JwdXMyIDwtIHRtX21hcChjb3JwdXMyLCByZW1vdmVQdW5jdHVhdGlvbikgIyBFbGltaW5hIHB1bnR1YWNpw7NuDQpjb3JwdXMyIDwtIHRtX21hcChjb3JwdXMyLCByZW1vdmVOdW1iZXJzKSAjIEVsaW1pbmEgbsO6bWVyb3MNCmNvcnB1czIgPC0gdG1fbWFwKGNvcnB1czIsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoInNwYW5pc2giKSkgIyBFbGltaW5hIHBhbGFicmFzIHF1ZSBubyBoYWJsZW4gZGVsIHRlbWENCg0KIyBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsIGMoImRyZWFtIiwgIndpbGwiKSkgI0VsaW1pbmEgcGFsYWJyYXMgcHVudHVhbGVzDQoNCnRkbTIgPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1czIpDQptMiA8LSBhcy5tYXRyaXgodGRtMikgIyBDdWVudGEgbGFzIHZlY2VzIHF1ZSBhcGFyZWNlIGNhZGEgcGFsYWJyYSBwb3IgcmVuZ2zDs24NCg0KZnJlY3VlbmNpYTIgPC0gc29ydChyb3dTdW1zKG0yKSwgZGVjcmVhc2luZyA9IFRSVUUpICMgQ3VlbnRhIGxhIGZyZWN1ZW5jaWEgZGUgY2FkYSBwYWxhYnJhIGVuIGVsIHRleHRvIGNvbXBsZXRvDQoNCmZyZWN1ZW5jaWFfZGYyIDwtIGRhdGEuZnJhbWUod29yZCA9IG5hbWVzKGZyZWN1ZW5jaWEyKSwgZnJlcSA9IGZyZWN1ZW5jaWEyKSAjIENvbnZpZXJ0ZSBsYSBmcmVjdWVuY2lhIGEgZGF0YSBmcmFtZQ0KDQpnZ3Bsb3QoaGVhZChmcmVjdWVuY2lhX2RmMiwgMTApLCBhZXMoeCA9IHJlb3JkZXIod29yZCwgLWZyZXEpLCB5ID0gZnJlcSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAicmVkIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZnJlcSksIHZqdXN0ID0gLTAuNSkgKw0KICBsYWJzKHRpdGxlID0gIlRPUCAxMCBwYWxhYnJhcyBtw6FzIGZyZWN1ZW50ZXMiLCANCiAgICAgICBzdWJ0aXRsZSA9ICJJVCIsIA0KICAgICAgIHggPSAiUGFsYWJyYSIsIA0KICAgICAgIHkgPSAiRnJlY3VlbmNpYSIpICsNCiAgeWxpbSgwLCAyMCkNCmBgYA0KDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+TnViZSBkZSBQYWxhYnJhczwvc3Bhbj4NCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEVsIHByb2Nlc2FtaWVudG8gZGUgZGF0b3MgYW50ZXMgZGUgbGEgbnViZSBkZSBwYWxhYnJhcyBlcyBpZ3VhbCBxdWUgZW4gZWwgQW7DoWxpc2lzIGRlIEZyZWN1ZW5jaWFzLCBkZXNkZSBpbXBvcnRhciBlbCB0ZXh0byBoYXN0YSBmcmVjdWVuY2lhX2RmMg0Kc2V0LnNlZWQoMTIzKQ0Kd29yZGNsb3VkKHdvcmRzPWZyZWN1ZW5jaWFfZGYyJHdvcmQsZnJlcT1mcmVjdWVuY2lhX2RmMiRmcmVxLCBtaW4uZnJlcT0xLA0KcmFuZG9tLm9yZGVyPUZBTFNFLCBjb2xvcnMgPSBicmV3ZXIucGFsKDgsICJSZFB1IikpDQpgYGANCg==