Text Mining: Aladdin

Introducción

La actividad corresponde en comparar dos guiones de película, uno es de Aladdin de 1992 y el otro es de Aladdin de 2019. El primero es una versión animada, por lo cual se pretende analizar el nivel de modernización del nuevo guión y si sigue siendo fiel a su antecesor.

Librerías

library(tm)
library(pdftools)
library(SnowballC)

1. Creación de Corpus

library(tm)

# Establecer el directorio de trabajo donde se encuentran los archivos
setwd("C:\\Users\\AVRIL\\Documents\\Text Mining")

# Obtener la lista de archivos en el directorio
archivos <- list.files(pattern = ".pdf")

library(pdftools)

# Función para extraer texto de un archivo PDF
extraer_texto <- function(archivo) {
  texto <- pdf_text(archivo)
  return(texto)
}

# Crear una lista de corpus
corpus_list <- lapply(archivos, function(archivo) {
  texto <- extraer_texto(archivo)
  corpus <- Corpus(VectorSource(texto))
  return(corpus)
})

2. Limpieza de Corpus

stopwords("en")
##   [1] "i"          "me"         "my"         "myself"     "we"        
##   [6] "our"        "ours"       "ourselves"  "you"        "your"      
##  [11] "yours"      "yourself"   "yourselves" "he"         "him"       
##  [16] "his"        "himself"    "she"        "her"        "hers"      
##  [21] "herself"    "it"         "its"        "itself"     "they"      
##  [26] "them"       "their"      "theirs"     "themselves" "what"      
##  [31] "which"      "who"        "whom"       "this"       "that"      
##  [36] "these"      "those"      "am"         "is"         "are"       
##  [41] "was"        "were"       "be"         "been"       "being"     
##  [46] "have"       "has"        "had"        "having"     "do"        
##  [51] "does"       "did"        "doing"      "would"      "should"    
##  [56] "could"      "ought"      "i'm"        "you're"     "he's"      
##  [61] "she's"      "it's"       "we're"      "they're"    "i've"      
##  [66] "you've"     "we've"      "they've"    "i'd"        "you'd"     
##  [71] "he'd"       "she'd"      "we'd"       "they'd"     "i'll"      
##  [76] "you'll"     "he'll"      "she'll"     "we'll"      "they'll"   
##  [81] "isn't"      "aren't"     "wasn't"     "weren't"    "hasn't"    
##  [86] "haven't"    "hadn't"     "doesn't"    "don't"      "didn't"    
##  [91] "won't"      "wouldn't"   "shan't"     "shouldn't"  "can't"     
##  [96] "cannot"     "couldn't"   "mustn't"    "let's"      "that's"    
## [101] "who's"      "what's"     "here's"     "there's"    "when's"    
## [106] "where's"    "why's"      "how's"      "a"          "an"        
## [111] "the"        "and"        "but"        "if"         "or"        
## [116] "because"    "as"         "until"      "while"      "of"        
## [121] "at"         "by"         "for"        "with"       "about"     
## [126] "against"    "between"    "into"       "through"    "during"    
## [131] "before"     "after"      "above"      "below"      "to"        
## [136] "from"       "up"         "down"       "in"         "out"       
## [141] "on"         "off"        "over"       "under"      "again"     
## [146] "further"    "then"       "once"       "here"       "there"     
## [151] "when"       "where"      "why"        "how"        "all"       
## [156] "any"        "both"       "each"       "few"        "more"      
## [161] "most"       "other"      "some"       "such"       "no"        
## [166] "nor"        "not"        "only"       "own"        "same"      
## [171] "so"         "than"       "too"        "very"
# Limpieza de los corpus
procesar_corpus <- function(corpus) {
  # Preprocesamiento
  corpus <- tm_map(corpus, content_transformer(tolower))
  corpus <- tm_map(corpus, removePunctuation)
  corpus <- tm_map(corpus, removeNumbers)
  corpus <- tm_map(corpus, removeWords, c(stopwords("english"), "page", "disney", "copyright"))
  corpus <- tm_map(corpus, stripWhitespace)
  
  # Crear matriz de términos
  tdm <- TermDocumentMatrix(corpus)
  
  return(tdm)
}

# Obtener la matriz de términos de cada corpus
tdm_list <- lapply(corpus_list, procesar_corpus)

3. Frecuencia de términos

Se observa que algunos de los términos del guión original tienen menos frecuencia en el nuevo guión hasta el punto en que no son parte del top, como “Abu”, “Iago” y “alfombra”, lo que implica cierta reducción del nivel de importancia de estos personajes en comparación con el primer guión. Asimismo, se muestra una disminución en la frecuencia de términos como “sultan” lo que determina que sigue siendo un persona con un rol relevante para el desarrollo tanto de la historia como de los personajes lo que refleja cierta fidelización con el guión de 1992.

terminos_frecuentes_list <- lapply(tdm_list, function(tdm) {
  # Convertir la matriz de términos a una matriz de términos-frecuencia
  m <- as.matrix(tdm)
  # Calcular la frecuencia de términos en cada documento
  term_freq <- rowSums(m)
  # Ordenar términos por frecuencia descendente
  sorted_terms <- sort(term_freq, decreasing = TRUE)
  # Seleccionar los términos más frecuentes (por ejemplo, los primeros 10)
  top_terms <- head(sorted_terms, 10)
  return(top_terms)
})

# Imprimir los términos frecuentes de cada corpus
for (i in seq_along(terminos_frecuentes_list)) {
  cat("Términos frecuentes en el corpus", i, ":\n")
  print(names(terminos_frecuentes_list[[i]]))
  cat("\n")
}
## Términos frecuentes en el corpus 1 :
##  [1] "aladdin" "jafar"   "jasmine" "abu"     "genie"   "sultan"  "iago"   
##  [8] "carpet"  "back"    "prince" 
## 
## Términos frecuentes en el corpus 2 :
##  [1] "aladdin" "genie"   "jasmine" "prince"  "jafar"   "dont"    "like"   
##  [8] "will"    "ali"     "sultan"

4. Esparcimiento / Dispersión

El nuevo guión tiene una menor esparcidad que el guión original, lo que indica una mayor concentración de términos, es decir, que utiliza un lenguaje más preciso y conciso. Asimismo, la diferencia en la esparcidad es relativamente pequeña, por lo que no se considera significativa lo que implica que la historia original se mantiene fiel en el nuevo guión.

calcular_esparsidad <- function(corpus_list) {
  # Obtener la matriz término-documento
  dtm <- DocumentTermMatrix(corpus_list)
  
 # Calcular la cantidad de términos únicos
  total_terms <- sum(as.matrix(dtm) > 0)
  
  # Calcular el tamaño total del vocabulario
  total_vocab <- length(unique(as.vector(dtm$dimnames$Terms)))
  
  # Calcular la esparsidad
  sparsity <- 1 - (total_terms / (length(dtm$dimnames$Docs) * total_vocab))
  
  return(sparsity)
}

# Crear una lista para almacenar la esparsidad de cada corpus
esparsidad_lista <- vector("numeric", length(corpus_list))

# Calcular la esparsidad de cada corpus en la lista
for (i in 1:length(corpus_list)) {
  esparsidad_lista[i] <- calcular_esparsidad(corpus_list[[i]])
}

# Imprimir la esparsidad de cada corpus
for (i in 1:length(corpus_list)) {
  cat("Esparsidad del Corpus", i, ":", esparsidad_lista[i], "\n")
}
## Esparsidad del Corpus 1 : 0.955889 
## Esparsidad del Corpus 2 : 0.9476435

5. Asociaciones

El nuevo guión presenta una menor asociación de términos con “GENIE” pero, sin perder su propósito principal que es su poder para conceder los deseos a los personajes, tanto a Aladdin como a Jafar, y el énfasis en poder determina la relevancia para el final y/o reoslución del nudo para convencer a Jafar de ser un genio. A diferencia del primer guión donde se da un mayor contexto del poder del genio alrededor de la historia, implicando también su relevancia para la resolución del conflicto.

library(tm)

assoc_results <- list()

# Iterar sobre cada corpus
for (i in seq_along(corpus_list)) {
  # Obtener la matriz término-documento actual
  dtm <- tdm_list[[i]]
  
  # Obtener los términos asociados a "jafar" con un umbral de correlación de 0.5
  associations <- findAssocs(dtm, term = "genie", corlimit = 0.5)
  
  # Almacenar los resultados en la lista
  assoc_results[[i]] <- associations
}

assoc_results
## [[1]]
## [[1]]$genie
##       wish      bitty     cosmic       itty     living phenomenal     powers 
##       0.71       0.58       0.58       0.58       0.58       0.58       0.58 
##     genies     master      space      third       poof 
##       0.53       0.53       0.53       0.53       0.50 
## 
## 
## [[2]]
## [[2]]$genie
## wishes  heavy 
##   0.53   0.50

6. Creación de Redes de Palabras

Se determina que el guión de 1992 tiene mayores redes por descripciones de los escenarios por palabras como “commonplace” lo que determina la importancia para el desarrollo de la historia. Mientras que, el guión de 2019 presenta redes por personajes (jasmine, aladdin y carpet) pero sigue manteniendo la relevancia de los escenarios desérticos y cultura ficcional para el desarrollo de las acicones de los personajes.

library(igraph)
crear_red_palabras <- function(tdm) {
  # Convertir la matriz término-documento a una matriz de términos
  tdm_matrix <- as.matrix(tdm)
  
  # Calcular la matriz de correlación entre los términos
  correlacion <- cor(tdm_matrix)
  
  # Convertir la matriz de correlación en una matriz de adyacencia para la red
  matriz_adyacencia <- as.matrix(correlacion > 0.9) 
  
  # Crear red
  red <- graph.adjacency(matriz_adyacencia, mode = "undirected", weighted = TRUE, diag = FALSE)
  
  return(red)
}

# Crear y visualizar la red de palabras para cada corpus
for (i in 1:length(tdm_list)) {
  # Crear la red de palabras
  red_palabras <- crear_red_palabras(tdm_list[[i]])
  for (j in 1:length(V(red_palabras))) {
  V(red_palabras)[j]$label <- rownames(tdm_list[[i]])[j]
}

  # Visualizar la red
  plot(red_palabras, main = archivos[i], vertex.label.dist = 0.5, vertex.size = 5)
}

7. Análisis de Sentimientos

Se visualiza que existe una fidelidad a la historia de 1992 puesto que la confianza/amistad, sigue siendo el sentimiento principal; mientras que el valor que se experimenta en menor medida en ambas obas es el disgusto. No obstante, se percibe que hubo un cambio en la sucesión de hechos y/o emociones debido a que se muestra que el clímax en el primer guión en un monto de 65 en el tiempo narrativo con un ligero pico al inicio, a diferencia del segundo guión que solamente muestra un pico en un monto de 45 en el tiempo narrativo.

library(tm)
library(fpc)   
library(ggplot2)
library(textTinyR)
library(tidytext)
library(pdftools)
library(tokenizers)

# Lee el texto del PDF
#texto_pdf <- pdf_text("C:\\Users\\AVRIL\\Documents\\Text Mining\\Aladdin_1992.pdf")

# Convierte a una lista de palabras
#texto_palabras <- get_tokens(texto_pdf)# Análisis de Sentimientos
#emociones_df <- get_nrc_sentiment(texto_palabras, language = "english")
#emociones_prop <- colSums(prop.table(emociones_df[1:8]))
#emociones_df_g1 <- data.frame(Emocion = names(emociones_prop), Proporcion = emociones_prop)
#ggplot(emociones_df_g1, aes(x = Emocion, y = Proporcion, fill = Emocion)) +
#  geom_bar(stat = "identity") +
#  labs(title = "Distribución de emociones en Aladdin - Película Animada",
#       x = "Emoción",
#       y = "Proporción") +
#  theme_minimal() +  # Estilo minimalista
#  theme(axis.text.x = element_text(angle = 45, hjust = 1, size=12),
#        plot.title = element_text(face = "bold"))

# Emociones negativas vs positivas a lo largo de la película
#sentimientos <- (emociones_df$negative*-1) + emociones_df$positive
#simple_plot(sentimientos)
# Lee el texto del PDF
#texto_pdf2 <- pdf_text("C:\\Users\\AVRIL\\Documents\\Text Mining\\Aladdin_2019.pdf")

# Convierte a una lista de palabras
#texto_palabras2 <- get_tokens(texto_pdf2)# Análisis de Sentimientos
#emociones_df2 <- get_nrc_sentiment(texto_palabras2, language = "english")
#emociones_prop2 <- colSums(prop.table(emociones_df2[1:8]))
#emociones_df_g2 <- data.frame(Emocion = names(emociones_prop2), Proporcion = emociones_prop2)
#ggplot(emociones_df_g2, aes(x = Emocion, y = Proporcion, fill = Emocion)) +
#  geom_bar(stat = "identity") +
#  labs(title = "Distribución de emociones en Aladdin - Película Animada",
#       x = "Emoción",
#       y = "Proporción") +
#  theme_minimal() +  # Estilo minimalista
#  theme(axis.text.x = element_text(angle = 45, hjust = 1, size=12),
#        plot.title = element_text(face = "bold"))

# Emociones negativas vs positivas a lo largo de la película
#sentimientos2 <- (emociones_df2$negative*-1) + emociones_df2$positive
#simple_plot(sentimientos2)

Hallazgos

Al realizar la comparación de los dos guiones de la película “Aladdín”, uno de 1992 y el otro de 2019. Se determina que sí existe cierto nivel de modernización del nuevo guión y sí sigue siendo fiel a su antecesor, puesto que en la frecuencia de términos se muestra que el top 10 sigue manteniendo las palabras clave para el desarrollo de la historia como “aladdin”, “jafar” y “genie” pero difieren en el nivel de frecuencia lo que alude a los cambios en relevancia del elementopara el desarrollo de la historia. Asimismo, la dispersidad entre guiones tiene una ligera diferencia lo que denota que el guión de 2019 es fiel a la historia pero emplea un lenguaje más preciso y conciso. Inclusive, esto se visualiza con la asociación de palabras con “genie” donde se muestra un mayor número de palabras asociadas lo cual da mayor contexto o detalle de dicho personaje. De igual manera, las redes de palabras de cada guión a pesar de tener cierta diversificación en relación a los palabras escénicas sigue existiendo una correlación con los elementos relevantes de la historia como “aladdin”, “carpet” y “lamp”. Por último, el análisis de sentimientos nos muestra que hay fidelidad en el guión de 2019 en relación a la jerarquía y emociones presentadas; pero difieren en el tiempo narrativa en que estas se perciben lo que lude a cambios en el climax o nudo de la historia.

LS0tDQp0aXRsZTogIk00QTFfTcOzZHVsbzRfVGFyZWExIg0KYXV0aG9yOiAiQXZyaWwgTG9iYXRvIC0gQTAwODMzMTEzIg0KZGF0ZTogIjIwMjQtMDMtMjgiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQotLS0NCg0KIyAqKlRleHQgTWluaW5nOiBBbGFkZGluKiogIA0KDQojICoqSW50cm9kdWNjacOzbioqDQpMYSBhY3RpdmlkYWQgY29ycmVzcG9uZGUgZW4gY29tcGFyYXIgZG9zIGd1aW9uZXMgZGUgcGVsw61jdWxhLCB1bm8gZXMgZGUgQWxhZGRpbiBkZSAxOTkyIHkgZWwgb3RybyBlcyBkZSBBbGFkZGluIGRlIDIwMTkuIEVsIHByaW1lcm8gZXMgdW5hIHZlcnNpw7NuIGFuaW1hZGEsIHBvciBsbyBjdWFsIHNlIHByZXRlbmRlIGFuYWxpemFyIGVsIG5pdmVsIGRlIG1vZGVybml6YWNpw7NuIGRlbCBudWV2byBndWnDs24geSBzaSBzaWd1ZSBzaWVuZG8gZmllbCBhIHN1IGFudGVjZXNvci4NCg0KIyAqKkxpYnJlcsOtYXMqKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodG0pDQpsaWJyYXJ5KHBkZnRvb2xzKQ0KbGlicmFyeShTbm93YmFsbEMpDQpgYGANCg0KIyAqKjEuKiogQ3JlYWNpw7NuIGRlIENvcnB1cw0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodG0pDQoNCiMgRXN0YWJsZWNlciBlbCBkaXJlY3RvcmlvIGRlIHRyYWJham8gZG9uZGUgc2UgZW5jdWVudHJhbiBsb3MgYXJjaGl2b3MNCnNldHdkKCJDOlxcVXNlcnNcXEFWUklMXFxEb2N1bWVudHNcXFRleHQgTWluaW5nIikNCg0KIyBPYnRlbmVyIGxhIGxpc3RhIGRlIGFyY2hpdm9zIGVuIGVsIGRpcmVjdG9yaW8NCmFyY2hpdm9zIDwtIGxpc3QuZmlsZXMocGF0dGVybiA9ICIucGRmIikNCg0KbGlicmFyeShwZGZ0b29scykNCg0KIyBGdW5jacOzbiBwYXJhIGV4dHJhZXIgdGV4dG8gZGUgdW4gYXJjaGl2byBQREYNCmV4dHJhZXJfdGV4dG8gPC0gZnVuY3Rpb24oYXJjaGl2bykgew0KICB0ZXh0byA8LSBwZGZfdGV4dChhcmNoaXZvKQ0KICByZXR1cm4odGV4dG8pDQp9DQoNCiMgQ3JlYXIgdW5hIGxpc3RhIGRlIGNvcnB1cw0KY29ycHVzX2xpc3QgPC0gbGFwcGx5KGFyY2hpdm9zLCBmdW5jdGlvbihhcmNoaXZvKSB7DQogIHRleHRvIDwtIGV4dHJhZXJfdGV4dG8oYXJjaGl2bykNCiAgY29ycHVzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UodGV4dG8pKQ0KICByZXR1cm4oY29ycHVzKQ0KfSkNCmBgYA0KDQoNCiMgKioyLioqIExpbXBpZXphIGRlIENvcnB1cw0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnN0b3B3b3JkcygiZW4iKQ0KDQojIExpbXBpZXphIGRlIGxvcyBjb3JwdXMNCnByb2Nlc2FyX2NvcnB1cyA8LSBmdW5jdGlvbihjb3JwdXMpIHsNCiAgIyBQcmVwcm9jZXNhbWllbnRvDQogIGNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQ0KICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlUHVuY3R1YXRpb24pDQogIGNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCByZW1vdmVOdW1iZXJzKQ0KICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsIGMoc3RvcHdvcmRzKCJlbmdsaXNoIiksICJwYWdlIiwgImRpc25leSIsICJjb3B5cmlnaHQiKSkNCiAgY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIHN0cmlwV2hpdGVzcGFjZSkNCiAgDQogICMgQ3JlYXIgbWF0cml6IGRlIHTDqXJtaW5vcw0KICB0ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1cykNCiAgDQogIHJldHVybih0ZG0pDQp9DQoNCiMgT2J0ZW5lciBsYSBtYXRyaXogZGUgdMOpcm1pbm9zIGRlIGNhZGEgY29ycHVzDQp0ZG1fbGlzdCA8LSBsYXBwbHkoY29ycHVzX2xpc3QsIHByb2Nlc2FyX2NvcnB1cykNCmBgYA0KDQojICoqMy4qKiBGcmVjdWVuY2lhIGRlIHTDqXJtaW5vcw0KU2Ugb2JzZXJ2YSBxdWUgYWxndW5vcyBkZSBsb3MgdMOpcm1pbm9zIGRlbCBndWnDs24gb3JpZ2luYWwgdGllbmVuIG1lbm9zIGZyZWN1ZW5jaWEgZW4gZWwgbnVldm8gZ3Vpw7NuIGhhc3RhIGVsIHB1bnRvIGVuIHF1ZSBubyBzb24gcGFydGUgZGVsIHRvcCwgY29tbyAiQWJ1IiwgIklhZ28iIHkgImFsZm9tYnJhIiwgbG8gcXVlIGltcGxpY2EgY2llcnRhIHJlZHVjY2nDs24gZGVsIG5pdmVsIGRlIGltcG9ydGFuY2lhIGRlIGVzdG9zIHBlcnNvbmFqZXMgZW4gY29tcGFyYWNpw7NuIGNvbiBlbCBwcmltZXIgZ3Vpw7NuLiBBc2ltaXNtbywgc2UgbXVlc3RyYSB1bmEgZGlzbWludWNpw7NuIGVuIGxhIGZyZWN1ZW5jaWEgZGUgdMOpcm1pbm9zIGNvbW8gInN1bHRhbiIgbG8gcXVlIGRldGVybWluYSBxdWUgc2lndWUgc2llbmRvIHVuIHBlcnNvbmEgY29uIHVuIHJvbCByZWxldmFudGUgcGFyYSBlbCBkZXNhcnJvbGxvIHRhbnRvIGRlIGxhIGhpc3RvcmlhIGNvbW8gZGUgbG9zIHBlcnNvbmFqZXMgbG8gcXVlIHJlZmxlamEgY2llcnRhIGZpZGVsaXphY2nDs24gY29uIGVsIGd1acOzbiBkZSAxOTkyLg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRlcm1pbm9zX2ZyZWN1ZW50ZXNfbGlzdCA8LSBsYXBwbHkodGRtX2xpc3QsIGZ1bmN0aW9uKHRkbSkgew0KICAjIENvbnZlcnRpciBsYSBtYXRyaXogZGUgdMOpcm1pbm9zIGEgdW5hIG1hdHJpeiBkZSB0w6lybWlub3MtZnJlY3VlbmNpYQ0KICBtIDwtIGFzLm1hdHJpeCh0ZG0pDQogICMgQ2FsY3VsYXIgbGEgZnJlY3VlbmNpYSBkZSB0w6lybWlub3MgZW4gY2FkYSBkb2N1bWVudG8NCiAgdGVybV9mcmVxIDwtIHJvd1N1bXMobSkNCiAgIyBPcmRlbmFyIHTDqXJtaW5vcyBwb3IgZnJlY3VlbmNpYSBkZXNjZW5kZW50ZQ0KICBzb3J0ZWRfdGVybXMgPC0gc29ydCh0ZXJtX2ZyZXEsIGRlY3JlYXNpbmcgPSBUUlVFKQ0KICAjIFNlbGVjY2lvbmFyIGxvcyB0w6lybWlub3MgbcOhcyBmcmVjdWVudGVzIChwb3IgZWplbXBsbywgbG9zIHByaW1lcm9zIDEwKQ0KICB0b3BfdGVybXMgPC0gaGVhZChzb3J0ZWRfdGVybXMsIDEwKQ0KICByZXR1cm4odG9wX3Rlcm1zKQ0KfSkNCg0KIyBJbXByaW1pciBsb3MgdMOpcm1pbm9zIGZyZWN1ZW50ZXMgZGUgY2FkYSBjb3JwdXMNCmZvciAoaSBpbiBzZXFfYWxvbmcodGVybWlub3NfZnJlY3VlbnRlc19saXN0KSkgew0KICBjYXQoIlTDqXJtaW5vcyBmcmVjdWVudGVzIGVuIGVsIGNvcnB1cyIsIGksICI6XG4iKQ0KICBwcmludChuYW1lcyh0ZXJtaW5vc19mcmVjdWVudGVzX2xpc3RbW2ldXSkpDQogIGNhdCgiXG4iKQ0KfQ0KYGBgDQoNCiMgKio0LioqIEVzcGFyY2ltaWVudG8gLyBEaXNwZXJzacOzbg0KRWwgbnVldm8gZ3Vpw7NuIHRpZW5lIHVuYSBtZW5vciBlc3BhcmNpZGFkIHF1ZSBlbCBndWnDs24gb3JpZ2luYWwsIGxvIHF1ZSBpbmRpY2EgdW5hIG1heW9yIGNvbmNlbnRyYWNpw7NuIGRlIHTDqXJtaW5vcywgZXMgZGVjaXIsIHF1ZSB1dGlsaXphIHVuIGxlbmd1YWplIG3DoXMgcHJlY2lzbyB5IGNvbmNpc28uIEFzaW1pc21vLCBsYSBkaWZlcmVuY2lhIGVuIGxhIGVzcGFyY2lkYWQgZXMgcmVsYXRpdmFtZW50ZSBwZXF1ZcOxYSwgcG9yIGxvIHF1ZSBubyBzZSBjb25zaWRlcmEgc2lnbmlmaWNhdGl2YSBsbyBxdWUgaW1wbGljYSBxdWUgbGEgaGlzdG9yaWEgb3JpZ2luYWwgc2UgbWFudGllbmUgZmllbCBlbiBlbCBudWV2byBndWnDs24uDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2FsY3VsYXJfZXNwYXJzaWRhZCA8LSBmdW5jdGlvbihjb3JwdXNfbGlzdCkgew0KICAjIE9idGVuZXIgbGEgbWF0cml6IHTDqXJtaW5vLWRvY3VtZW50bw0KICBkdG0gPC0gRG9jdW1lbnRUZXJtTWF0cml4KGNvcnB1c19saXN0KQ0KICANCiAjIENhbGN1bGFyIGxhIGNhbnRpZGFkIGRlIHTDqXJtaW5vcyDDum5pY29zDQogIHRvdGFsX3Rlcm1zIDwtIHN1bShhcy5tYXRyaXgoZHRtKSA+IDApDQogIA0KICAjIENhbGN1bGFyIGVsIHRhbWHDsW8gdG90YWwgZGVsIHZvY2FidWxhcmlvDQogIHRvdGFsX3ZvY2FiIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKGR0bSRkaW1uYW1lcyRUZXJtcykpKQ0KICANCiAgIyBDYWxjdWxhciBsYSBlc3BhcnNpZGFkDQogIHNwYXJzaXR5IDwtIDEgLSAodG90YWxfdGVybXMgLyAobGVuZ3RoKGR0bSRkaW1uYW1lcyREb2NzKSAqIHRvdGFsX3ZvY2FiKSkNCiAgDQogIHJldHVybihzcGFyc2l0eSkNCn0NCg0KIyBDcmVhciB1bmEgbGlzdGEgcGFyYSBhbG1hY2VuYXIgbGEgZXNwYXJzaWRhZCBkZSBjYWRhIGNvcnB1cw0KZXNwYXJzaWRhZF9saXN0YSA8LSB2ZWN0b3IoIm51bWVyaWMiLCBsZW5ndGgoY29ycHVzX2xpc3QpKQ0KDQojIENhbGN1bGFyIGxhIGVzcGFyc2lkYWQgZGUgY2FkYSBjb3JwdXMgZW4gbGEgbGlzdGENCmZvciAoaSBpbiAxOmxlbmd0aChjb3JwdXNfbGlzdCkpIHsNCiAgZXNwYXJzaWRhZF9saXN0YVtpXSA8LSBjYWxjdWxhcl9lc3BhcnNpZGFkKGNvcnB1c19saXN0W1tpXV0pDQp9DQoNCiMgSW1wcmltaXIgbGEgZXNwYXJzaWRhZCBkZSBjYWRhIGNvcnB1cw0KZm9yIChpIGluIDE6bGVuZ3RoKGNvcnB1c19saXN0KSkgew0KICBjYXQoIkVzcGFyc2lkYWQgZGVsIENvcnB1cyIsIGksICI6IiwgZXNwYXJzaWRhZF9saXN0YVtpXSwgIlxuIikNCn0NCmBgYA0KDQojICoqNS4qKiBBc29jaWFjaW9uZXMNCkVsIG51ZXZvIGd1acOzbiBwcmVzZW50YSB1bmEgbWVub3IgYXNvY2lhY2nDs24gZGUgdMOpcm1pbm9zIGNvbiAiR0VOSUUiIHBlcm8sIHNpbiBwZXJkZXIgc3UgcHJvcMOzc2l0byBwcmluY2lwYWwgcXVlIGVzIHN1IHBvZGVyIHBhcmEgY29uY2VkZXIgbG9zIGRlc2VvcyBhIGxvcyBwZXJzb25hamVzLCB0YW50byBhIEFsYWRkaW4gY29tbyBhIEphZmFyLCB5IGVsIMOpbmZhc2lzIGVuIHBvZGVyIGRldGVybWluYSBsYSByZWxldmFuY2lhIHBhcmEgZWwgZmluYWwgeS9vIHJlb3NsdWNpw7NuIGRlbCBudWRvIHBhcmEgY29udmVuY2VyIGEgSmFmYXIgZGUgc2VyIHVuIGdlbmlvLiBBIGRpZmVyZW5jaWEgZGVsIHByaW1lciBndWnDs24gZG9uZGUgc2UgZGEgdW4gbWF5b3IgY29udGV4dG8gZGVsIHBvZGVyIGRlbCBnZW5pbyBhbHJlZGVkb3IgZGUgbGEgaGlzdG9yaWEsIGltcGxpY2FuZG8gdGFtYmnDqW4gc3UgcmVsZXZhbmNpYSBwYXJhIGxhIHJlc29sdWNpw7NuIGRlbCBjb25mbGljdG8uIA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0bSkNCg0KYXNzb2NfcmVzdWx0cyA8LSBsaXN0KCkNCg0KIyBJdGVyYXIgc29icmUgY2FkYSBjb3JwdXMNCmZvciAoaSBpbiBzZXFfYWxvbmcoY29ycHVzX2xpc3QpKSB7DQogICMgT2J0ZW5lciBsYSBtYXRyaXogdMOpcm1pbm8tZG9jdW1lbnRvIGFjdHVhbA0KICBkdG0gPC0gdGRtX2xpc3RbW2ldXQ0KICANCiAgIyBPYnRlbmVyIGxvcyB0w6lybWlub3MgYXNvY2lhZG9zIGEgImphZmFyIiBjb24gdW4gdW1icmFsIGRlIGNvcnJlbGFjacOzbiBkZSAwLjUNCiAgYXNzb2NpYXRpb25zIDwtIGZpbmRBc3NvY3MoZHRtLCB0ZXJtID0gImdlbmllIiwgY29ybGltaXQgPSAwLjUpDQogIA0KICAjIEFsbWFjZW5hciBsb3MgcmVzdWx0YWRvcyBlbiBsYSBsaXN0YQ0KICBhc3NvY19yZXN1bHRzW1tpXV0gPC0gYXNzb2NpYXRpb25zDQp9DQoNCmFzc29jX3Jlc3VsdHMNCmBgYA0KDQojICoqNi4qKiBDcmVhY2nDs24gZGUgUmVkZXMgZGUgUGFsYWJyYXMNClNlIGRldGVybWluYSBxdWUgZWwgZ3Vpw7NuIGRlIDE5OTIgdGllbmUgbWF5b3JlcyByZWRlcyBwb3IgZGVzY3JpcGNpb25lcyBkZSBsb3MgZXNjZW5hcmlvcyBwb3IgcGFsYWJyYXMgY29tbyAiY29tbW9ucGxhY2UiIGxvIHF1ZSBkZXRlcm1pbmEgbGEgaW1wb3J0YW5jaWEgcGFyYSBlbCBkZXNhcnJvbGxvIGRlIGxhIGhpc3RvcmlhLiBNaWVudHJhcyBxdWUsIGVsIGd1acOzbiBkZSAyMDE5IHByZXNlbnRhIHJlZGVzIHBvciBwZXJzb25hamVzIChqYXNtaW5lLCBhbGFkZGluIHkgY2FycGV0KSBwZXJvIHNpZ3VlIG1hbnRlbmllbmRvIGxhIHJlbGV2YW5jaWEgZGUgbG9zIGVzY2VuYXJpb3MgZGVzw6lydGljb3MgeSBjdWx0dXJhIGZpY2Npb25hbCBwYXJhIGVsIGRlc2Fycm9sbG8gZGUgbGFzIGFjaWNvbmVzIGRlIGxvcyBwZXJzb25hamVzLg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoaWdyYXBoKQ0KY3JlYXJfcmVkX3BhbGFicmFzIDwtIGZ1bmN0aW9uKHRkbSkgew0KICAjIENvbnZlcnRpciBsYSBtYXRyaXogdMOpcm1pbm8tZG9jdW1lbnRvIGEgdW5hIG1hdHJpeiBkZSB0w6lybWlub3MNCiAgdGRtX21hdHJpeCA8LSBhcy5tYXRyaXgodGRtKQ0KICANCiAgIyBDYWxjdWxhciBsYSBtYXRyaXogZGUgY29ycmVsYWNpw7NuIGVudHJlIGxvcyB0w6lybWlub3MNCiAgY29ycmVsYWNpb24gPC0gY29yKHRkbV9tYXRyaXgpDQogIA0KICAjIENvbnZlcnRpciBsYSBtYXRyaXogZGUgY29ycmVsYWNpw7NuIGVuIHVuYSBtYXRyaXogZGUgYWR5YWNlbmNpYSBwYXJhIGxhIHJlZA0KICBtYXRyaXpfYWR5YWNlbmNpYSA8LSBhcy5tYXRyaXgoY29ycmVsYWNpb24gPiAwLjkpIA0KICANCiAgIyBDcmVhciByZWQNCiAgcmVkIDwtIGdyYXBoLmFkamFjZW5jeShtYXRyaXpfYWR5YWNlbmNpYSwgbW9kZSA9ICJ1bmRpcmVjdGVkIiwgd2VpZ2h0ZWQgPSBUUlVFLCBkaWFnID0gRkFMU0UpDQogIA0KICByZXR1cm4ocmVkKQ0KfQ0KDQojIENyZWFyIHkgdmlzdWFsaXphciBsYSByZWQgZGUgcGFsYWJyYXMgcGFyYSBjYWRhIGNvcnB1cw0KZm9yIChpIGluIDE6bGVuZ3RoKHRkbV9saXN0KSkgew0KICAjIENyZWFyIGxhIHJlZCBkZSBwYWxhYnJhcw0KICByZWRfcGFsYWJyYXMgPC0gY3JlYXJfcmVkX3BhbGFicmFzKHRkbV9saXN0W1tpXV0pDQogIGZvciAoaiBpbiAxOmxlbmd0aChWKHJlZF9wYWxhYnJhcykpKSB7DQogIFYocmVkX3BhbGFicmFzKVtqXSRsYWJlbCA8LSByb3duYW1lcyh0ZG1fbGlzdFtbaV1dKVtqXQ0KfQ0KDQogICMgVmlzdWFsaXphciBsYSByZWQNCiAgcGxvdChyZWRfcGFsYWJyYXMsIG1haW4gPSBhcmNoaXZvc1tpXSwgdmVydGV4LmxhYmVsLmRpc3QgPSAwLjUsIHZlcnRleC5zaXplID0gNSkNCn0NCmBgYA0KDQojICoqNy4qKiBBbsOhbGlzaXMgZGUgU2VudGltaWVudG9zDQpTZSB2aXN1YWxpemEgcXVlIGV4aXN0ZSB1bmEgZmlkZWxpZGFkIGEgbGEgaGlzdG9yaWEgZGUgMTk5MiBwdWVzdG8gcXVlIGxhIGNvbmZpYW56YS9hbWlzdGFkLCBzaWd1ZSBzaWVuZG8gZWwgc2VudGltaWVudG8gcHJpbmNpcGFsOyBtaWVudHJhcyBxdWUgZWwgdmFsb3IgcXVlIHNlIGV4cGVyaW1lbnRhIGVuIG1lbm9yIG1lZGlkYSBlbiBhbWJhcyBvYmFzIGVzIGVsIGRpc2d1c3RvLiBObyBvYnN0YW50ZSwgc2UgcGVyY2liZSBxdWUgaHVibyB1biBjYW1iaW8gZW4gbGEgc3VjZXNpw7NuIGRlIGhlY2hvcyB5L28gZW1vY2lvbmVzIGRlYmlkbyBhIHF1ZSBzZSBtdWVzdHJhIHF1ZSBlbCBjbMOtbWF4IGVuIGVsIHByaW1lciBndWnDs24gZW4gdW4gbW9udG8gZGUgNjUgZW4gZWwgdGllbXBvIG5hcnJhdGl2byBjb24gdW4gbGlnZXJvIHBpY28gYWwgaW5pY2lvLCBhIGRpZmVyZW5jaWEgZGVsIHNlZ3VuZG8gZ3Vpw7NuIHF1ZSBzb2xhbWVudGUgbXVlc3RyYSB1biBwaWNvIGVuIHVuIG1vbnRvIGRlIDQ1ICBlbiBlbCB0aWVtcG8gbmFycmF0aXZvLiANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodG0pDQpsaWJyYXJ5KGZwYykgICANCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGV4dFRpbnlSKQ0KbGlicmFyeSh0aWR5dGV4dCkNCmxpYnJhcnkocGRmdG9vbHMpDQpsaWJyYXJ5KHRva2VuaXplcnMpDQoNCiMgTGVlIGVsIHRleHRvIGRlbCBQREYNCiN0ZXh0b19wZGYgPC0gcGRmX3RleHQoIkM6XFxVc2Vyc1xcQVZSSUxcXERvY3VtZW50c1xcVGV4dCBNaW5pbmdcXEFsYWRkaW5fMTk5Mi5wZGYiKQ0KDQojIENvbnZpZXJ0ZSBhIHVuYSBsaXN0YSBkZSBwYWxhYnJhcw0KI3RleHRvX3BhbGFicmFzIDwtIGdldF90b2tlbnModGV4dG9fcGRmKSMgQW7DoWxpc2lzIGRlIFNlbnRpbWllbnRvcw0KI2Vtb2Npb25lc19kZiA8LSBnZXRfbnJjX3NlbnRpbWVudCh0ZXh0b19wYWxhYnJhcywgbGFuZ3VhZ2UgPSAiZW5nbGlzaCIpDQojZW1vY2lvbmVzX3Byb3AgPC0gY29sU3Vtcyhwcm9wLnRhYmxlKGVtb2Npb25lc19kZlsxOjhdKSkNCiNlbW9jaW9uZXNfZGZfZzEgPC0gZGF0YS5mcmFtZShFbW9jaW9uID0gbmFtZXMoZW1vY2lvbmVzX3Byb3ApLCBQcm9wb3JjaW9uID0gZW1vY2lvbmVzX3Byb3ApDQojZ2dwbG90KGVtb2Npb25lc19kZl9nMSwgYWVzKHggPSBFbW9jaW9uLCB5ID0gUHJvcG9yY2lvbiwgZmlsbCA9IEVtb2Npb24pKSArDQojICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KIyAgbGFicyh0aXRsZSA9ICJEaXN0cmlidWNpw7NuIGRlIGVtb2Npb25lcyBlbiBBbGFkZGluIC0gUGVsw61jdWxhIEFuaW1hZGEiLA0KIyAgICAgICB4ID0gIkVtb2Npw7NuIiwNCiMgICAgICAgeSA9ICJQcm9wb3JjacOzbiIpICsNCiMgIHRoZW1lX21pbmltYWwoKSArICAjIEVzdGlsbyBtaW5pbWFsaXN0YQ0KIyAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplPTEyKSwNCiMgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpDQoNCiMgRW1vY2lvbmVzIG5lZ2F0aXZhcyB2cyBwb3NpdGl2YXMgYSBsbyBsYXJnbyBkZSBsYSBwZWzDrWN1bGENCiNzZW50aW1pZW50b3MgPC0gKGVtb2Npb25lc19kZiRuZWdhdGl2ZSotMSkgKyBlbW9jaW9uZXNfZGYkcG9zaXRpdmUNCiNzaW1wbGVfcGxvdChzZW50aW1pZW50b3MpDQpgYGANCg0KYGBge3J9DQojIExlZSBlbCB0ZXh0byBkZWwgUERGDQojdGV4dG9fcGRmMiA8LSBwZGZfdGV4dCgiQzpcXFVzZXJzXFxBVlJJTFxcRG9jdW1lbnRzXFxUZXh0IE1pbmluZ1xcQWxhZGRpbl8yMDE5LnBkZiIpDQoNCiMgQ29udmllcnRlIGEgdW5hIGxpc3RhIGRlIHBhbGFicmFzDQojdGV4dG9fcGFsYWJyYXMyIDwtIGdldF90b2tlbnModGV4dG9fcGRmMikjIEFuw6FsaXNpcyBkZSBTZW50aW1pZW50b3MNCiNlbW9jaW9uZXNfZGYyIDwtIGdldF9ucmNfc2VudGltZW50KHRleHRvX3BhbGFicmFzMiwgbGFuZ3VhZ2UgPSAiZW5nbGlzaCIpDQojZW1vY2lvbmVzX3Byb3AyIDwtIGNvbFN1bXMocHJvcC50YWJsZShlbW9jaW9uZXNfZGYyWzE6OF0pKQ0KI2Vtb2Npb25lc19kZl9nMiA8LSBkYXRhLmZyYW1lKEVtb2Npb24gPSBuYW1lcyhlbW9jaW9uZXNfcHJvcDIpLCBQcm9wb3JjaW9uID0gZW1vY2lvbmVzX3Byb3AyKQ0KI2dncGxvdChlbW9jaW9uZXNfZGZfZzIsIGFlcyh4ID0gRW1vY2lvbiwgeSA9IFByb3BvcmNpb24sIGZpbGwgPSBFbW9jaW9uKSkgKw0KIyAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiMgIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBkZSBlbW9jaW9uZXMgZW4gQWxhZGRpbiAtIFBlbMOtY3VsYSBBbmltYWRhIiwNCiMgICAgICAgeCA9ICJFbW9jacOzbiIsDQojICAgICAgIHkgPSAiUHJvcG9yY2nDs24iKSArDQojICB0aGVtZV9taW5pbWFsKCkgKyAgIyBFc3RpbG8gbWluaW1hbGlzdGENCiMgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZT0xMiksDQojICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpKQ0KDQojIEVtb2Npb25lcyBuZWdhdGl2YXMgdnMgcG9zaXRpdmFzIGEgbG8gbGFyZ28gZGUgbGEgcGVsw61jdWxhDQojc2VudGltaWVudG9zMiA8LSAoZW1vY2lvbmVzX2RmMiRuZWdhdGl2ZSotMSkgKyBlbW9jaW9uZXNfZGYyJHBvc2l0aXZlDQojc2ltcGxlX3Bsb3Qoc2VudGltaWVudG9zMikNCmBgYA0KDQoNCiMgKipIYWxsYXpnb3MqKiAgDQpBbCByZWFsaXphciBsYSBjb21wYXJhY2nDs24gZGUgbG9zIGRvcyBndWlvbmVzIGRlIGxhIHBlbMOtY3VsYSAiQWxhZGTDrW4iLCB1bm8gZGUgMTk5MiB5IGVsIG90cm8gZGUgMjAxOS4gU2UgZGV0ZXJtaW5hIHF1ZSBzw60gZXhpc3RlIGNpZXJ0byBuaXZlbCBkZSBtb2Rlcm5pemFjacOzbiBkZWwgbnVldm8gZ3Vpw7NuIHkgc8OtIHNpZ3VlIHNpZW5kbyBmaWVsIGEgc3UgYW50ZWNlc29yLCBwdWVzdG8gcXVlIGVuIGxhIGZyZWN1ZW5jaWEgZGUgdMOpcm1pbm9zIHNlIG11ZXN0cmEgcXVlIGVsIHRvcCAxMCBzaWd1ZSBtYW50ZW5pZW5kbyBsYXMgcGFsYWJyYXMgY2xhdmUgcGFyYSBlbCBkZXNhcnJvbGxvIGRlIGxhIGhpc3RvcmlhIGNvbW8gImFsYWRkaW4iLCAiamFmYXIiIHkgImdlbmllIiBwZXJvIGRpZmllcmVuIGVuIGVsIG5pdmVsIGRlIGZyZWN1ZW5jaWEgbG8gcXVlIGFsdWRlIGEgbG9zIGNhbWJpb3MgZW4gcmVsZXZhbmNpYSBkZWwgZWxlbWVudG9wYXJhIGVsIGRlc2Fycm9sbG8gZGUgbGEgaGlzdG9yaWEuIEFzaW1pc21vLCBsYSBkaXNwZXJzaWRhZCBlbnRyZSBndWlvbmVzIHRpZW5lIHVuYSBsaWdlcmEgZGlmZXJlbmNpYSBsbyBxdWUgZGVub3RhIHF1ZSBlbCBndWnDs24gZGUgMjAxOSBlcyBmaWVsIGEgbGEgaGlzdG9yaWEgcGVybyBlbXBsZWEgdW4gbGVuZ3VhamUgbcOhcyBwcmVjaXNvIHkgY29uY2lzby4gSW5jbHVzaXZlLCBlc3RvIHNlIHZpc3VhbGl6YSBjb24gbGEgYXNvY2lhY2nDs24gZGUgcGFsYWJyYXMgY29uICJnZW5pZSIgZG9uZGUgc2UgbXVlc3RyYSB1biBtYXlvciBuw7ptZXJvIGRlIHBhbGFicmFzIGFzb2NpYWRhcyBsbyBjdWFsIGRhIG1heW9yIGNvbnRleHRvIG8gZGV0YWxsZSBkZSBkaWNobyBwZXJzb25hamUuIERlIGlndWFsIG1hbmVyYSwgbGFzIHJlZGVzIGRlIHBhbGFicmFzIGRlIGNhZGEgZ3Vpw7NuIGEgcGVzYXIgZGUgdGVuZXIgY2llcnRhIGRpdmVyc2lmaWNhY2nDs24gZW4gcmVsYWNpw7NuIGEgbG9zIHBhbGFicmFzIGVzY8OpbmljYXMgc2lndWUgZXhpc3RpZW5kbyB1bmEgY29ycmVsYWNpw7NuIGNvbiBsb3MgZWxlbWVudG9zIHJlbGV2YW50ZXMgZGUgbGEgaGlzdG9yaWEgY29tbyAiYWxhZGRpbiIsICJjYXJwZXQiIHkgImxhbXAiLiBQb3Igw7psdGltbywgZWwgYW7DoWxpc2lzIGRlIHNlbnRpbWllbnRvcyBub3MgbXVlc3RyYSBxdWUgaGF5IGZpZGVsaWRhZCBlbiBlbCBndWnDs24gZGUgMjAxOSBlbiByZWxhY2nDs24gYSBsYSBqZXJhcnF1w61hIHkgZW1vY2lvbmVzIHByZXNlbnRhZGFzOyBwZXJvIGRpZmllcmVuIGVuIGVsIHRpZW1wbyBuYXJyYXRpdmEgZW4gcXVlIGVzdGFzIHNlIHBlcmNpYmVuIGxvIHF1ZSBsdWRlIGEgY2FtYmlvcyBlbiBlbCBjbGltYXggbyBudWRvIGRlIGxhIGhpc3RvcmlhLiANCg0KDQog