save this notebook

save.image(file = "c:/users/juand/desktop/r/hello.RData")

In this notebook, we examine the differences between authors Brackett and Cummings, in three ways: first, we analyse their books by pairing them with the NRC sentiment database, and we obtain a possible ordering of the emotions conveyed by the works and how they vary throughout them. Second, we use the afinn sentiment database to give words a score from -5 to 5, and obtain a ā€œplot profileā€ of each book by aggregating these scores into chunks of the story. Lastly, we perform a principal components analysis in order to obtain possible stylistic differences between the authors.

Adding essential libraries

install.packages(c("tidytext","textdata","gutenbergr","ggplot2","tidyr","janeaustenr","stringr","devtools","curl"))
Error in install.packages : Updating loaded packages
install.packages("ggplotly")
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
Installing package into ć¤¼ćø±C:/Users/juand/Documents/R/win-library/4.0ć¤¼ćø²
(as ć¤¼ćø±libć¤¼ćø² is unspecified)
Warning in install.packages :
  package ā€˜ggplotlyā€™ is not available for this version of R

A version of this package for your version of R might be available elsewhere,
see the ideas at
https://cran.r-project.org/doc/manuals/r-patched/R-admin.html#Installing-packages
install.packages(c("tidytext", "textdata", "gutenbergr", "ggplot2", "tidyr", "janeaustenr", "stringr", "devtools", "curl"))
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
Installing packages into ć¤¼ćø±C:/Users/juand/Documents/R/win-library/4.0ć¤¼ćø²
(as ć¤¼ćø±libć¤¼ćø² is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/tidytext_0.3.2.zip'
Content type 'application/zip' length 3050365 bytes (2.9 MB)
downloaded 2.9 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/textdata_0.4.1.zip'
Content type 'application/zip' length 496596 bytes (484 KB)
downloaded 484 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/gutenbergr_0.2.1.zip'
Content type 'application/zip' length 4070950 bytes (3.9 MB)
downloaded 3.9 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/ggplot2_3.3.5.zip'
Content type 'application/zip' length 4127688 bytes (3.9 MB)
downloaded 3.9 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/tidyr_1.1.4.zip'
Content type 'application/zip' length 1070426 bytes (1.0 MB)
downloaded 1.0 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/janeaustenr_0.1.5.zip'
Content type 'application/zip' length 1625468 bytes (1.6 MB)
downloaded 1.6 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/stringr_1.4.0.zip'
Content type 'application/zip' length 216777 bytes (211 KB)
downloaded 211 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/devtools_2.4.2.zip'
Content type 'application/zip' length 397109 bytes (387 KB)
downloaded 387 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/curl_4.3.2.zip'
Content type 'application/zip' length 4322383 bytes (4.1 MB)
downloaded 4.1 MB
package ā€˜tidytextā€™ successfully unpacked and MD5 sums checked
package ā€˜textdataā€™ successfully unpacked and MD5 sums checked
package ā€˜gutenbergrā€™ successfully unpacked and MD5 sums checked
package ā€˜ggplot2ā€™ successfully unpacked and MD5 sums checked
package ā€˜tidyrā€™ successfully unpacked and MD5 sums checked
package ā€˜janeaustenrā€™ successfully unpacked and MD5 sums checked
package ā€˜stringrā€™ successfully unpacked and MD5 sums checked
package ā€˜devtoolsā€™ successfully unpacked and MD5 sums checked
package ā€˜curlā€™ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\juand\AppData\Local\Temp\Rtmp630153\downloaded_packages
install.packages(c("scales","plotly"))
Error in install.packages : Updating loaded packages

Downloading the books from PG

library("gutenbergr")
package ć¤¼ćø±gutenbergrć¤¼ćø² was built under R version 4.0.5
cummings1 <- gutenberg_download(19066)
Determining mirror for Project Gutenberg from http://www.gutenberg.org/robot/harvest
Using mirror http://aleph.gutenberg.org
install.packages(c("scales", "plotly"))
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
Installing packages into ć¤¼ćø±C:/Users/juand/Documents/R/win-library/4.0ć¤¼ćø²
(as ć¤¼ćø±libć¤¼ćø² is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/scales_1.1.1.zip'
Content type 'application/zip' length 556840 bytes (543 KB)
downloaded 543 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/plotly_4.10.0.zip'
Content type 'application/zip' length 3175893 bytes (3.0 MB)
downloaded 3.0 MB
package ā€˜scalesā€™ successfully unpacked and MD5 sums checked
package ā€˜plotlyā€™ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\juand\AppData\Local\Temp\Rtmp630153\downloaded_packages
cummings2 <- gutenberg_download(61884)
brackett1 <- gutenberg_download(32664)
brackett2 <- gutenberg_download(64043)

Activating necessary libraries for text cleaning

library(dplyr)
package ć¤¼ćø±dplyrć¤¼ćø² was built under R version 4.0.5
Attaching package: ć¤¼ćø±dplyrć¤¼ćø²

The following objects are masked from ć¤¼ćø±package:statsć¤¼ćø²:

    filter, lag

The following objects are masked from ć¤¼ćø±package:baseć¤¼ćø²:

    intersect, setdiff, setequal, union
library(stringr)
package ć¤¼ćø±stringrć¤¼ćø² was built under R version 4.0.5
library(tidytext)
package ć¤¼ćø±tidytextć¤¼ćø² was built under R version 4.0.5
Attaching package: ć¤¼ćø±tidytextć¤¼ćø²

The following object is masked _by_ ć¤¼ćø±.GlobalEnvć¤¼ćø²:

    sentiments
library(textdata)
package ć¤¼ćø±textdatać¤¼ćø² was built under R version 4.0.5
install.packages("scales")
Error in install.packages : Updating loaded packages

Transforming the books into tidy format

tidy_cummings1 <- cummings1 %>% mutate(linenumber = row_number()) %>% unnest_tokens(word, text) %>% anti_join(stop_words)
Joining, by = "word"
tidy_brackett1 <- brackett1 %>% mutate(linenumber = row_number()) %>% unnest_tokens(word, text) %>% anti_join(stop_words)
Joining, by = "word"
tidy_cummings2 <- cummings2 %>% mutate(linenumber = row_number()) %>% unnest_tokens(word, text) %>% anti_join(stop_words)
Joining, by = "word"
tidy_brackett2 <- brackett2 %>% mutate(linenumber = row_number()) %>% unnest_tokens(word, text) %>% anti_join(stop_words)
Joining, by = "word"
all_books <- rbind(tidy_cummings1, tidy_cummings2, tidy_brackett1, tidy_brackett2)

1: Sentiment Analysis with NRC for reference: sentiments are anger, anticipation, disgust, fear, joy, sadness, surprise, trust

  nrc <- get_sentiments("nrc")
  sentiments <- c("anger","anticipation", "disgust", "fear", "joy", "sadness", "surprise", "trust")

make a table with all lines, then count all sentiments per line

  
  lines <- c(1:9801,1:2298,1:3157,1:3137)
  books <- c(rep(19066, 9801),rep(61884, 2298),rep(32664, 3157),rep(64043, 3137))
  sentiment_table <- data.frame(books, lines, anger = integer(18393), anticipation = integer(18393), disgust = integer(18393), fear = integer(18393), joy = integer(18393), sadness = integer(18393), surprise = integer(18393), trust = integer(18393))

add counts for sentiments for each line to sentiment_table

  for (i in 1:length(sentiments))
  {
    new_table <- all_books %>% inner_join(nrc %>% filter(sentiment == sentiments[i])) %>%    count(gutenberg_id, index = linenumber)
    
    for (j in 1:nrow(new_table))
    {
      sentiment_table[sentiment_table$books == (new_table$gutenberg_id)[j] & sentiment_table$lines == (new_table$index)[j], sentiments[i]] <- (new_table$n)[j]
    }
  }
Joining, by = "word"
Joining, by = "word"
Joining, by = "word"
Joining, by = "word"
Joining, by = "word"
Joining, by = "word"
Joining, by = "word"
Joining, by = "word"

counting the number of words per sentiment in every 100 line chunk (this had to be done dirtily as we didnā€™t find an equivalent function to group the chunks)

  sectors <- c(0:122, 0:28, 0:39, 0:39)
  books <- c(rep(19066, 123), rep(61884, 29), rep(32664, 40), rep(64043, 40))
  sentiment_summary <- data.frame(sectors, books, anger = integer(232), anticipation = integer(232), disgust = integer(232), fear = integer(232), joy = integer(232), sadness = integer(232), surprise = integer(232), trust = integer(232))

for(i in 1:length(sentiments)){
  s <- sentiments[i]
  col <- pull(sentiment_table, s)
  for(j in 1:nrow(sentiment_table))
  {
    index <- (sentiment_table$lines[j]) %/% 100
    
    sentiment_summary[sentiment_summary$books == sentiment_table$books[j] & sentiment_summary$sectors == index, sentiments[i]] <- sentiment_summary[sentiment_summary$books == sentiment_table$books[j] & sentiment_summary$sectors == index, sentiments[i]]+ col[j]
  }
}
install.packages("scales")
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
Installing package into ć¤¼ćø±C:/Users/juand/Documents/R/win-library/4.0ć¤¼ćø²
(as ć¤¼ćø±libć¤¼ćø² is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/scales_1.1.1.zip'
Content type 'application/zip' length 556840 bytes (543 KB)
downloaded 543 KB
package ā€˜scalesā€™ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\juand\AppData\Local\Temp\Rtmp630153\downloaded_packages
 
  

We turn the columns of data into a factor to tidy them and then we plot using a streamgraph: For some reason, the streamgraphs only admit date values. We convert the sectors to ā€œdatesā€:

devtools::install_github("hrbrmstr/streamgraph")
WARNING: Rtools is required to build R packages, but is not currently installed.

Please download and install Rtools 4.0 from https://cran.r-project.org/bin/windows/Rtools/.
Skipping install of 'streamgraph' from a github remote, the SHA1 (76f7173e) has not changed since last install.
  Use `force = TRUE` to force installation
library(streamgraph)
sentiment_summary <- pivot_longer(sentiment_summary, anger:trust, "sentiment")
sentiment_summary$sectors <- as.Date(as.Date(ISOdate(sentiment_summary$sectors, 1, 1)))


sg1 <- sentiment_summary %>% filter(books==19066) %>% streamgraph("sentiment", "value", date="sectors", interpolate="step")
sg2 <- sentiment_summary %>% filter(books==61884) %>% streamgraph("sentiment", "value", date="sectors", interpolate="step") 
sg3 <- sentiment_summary %>% filter(books==32664) %>% streamgraph("sentiment", "value", date="sectors", interpolate="step")
sg4 <- sentiment_summary %>% filter(books==64043) %>% streamgraph("sentiment", "value", date="sectors", interpolate="step")
sg1
streamgraph_html returned an object of class `list` instead of a `shiny.tag`.streamgraph_html returned an object of class `list` instead of a `shiny.tag`.
sg2
streamgraph_html returned an object of class `list` instead of a `shiny.tag`.streamgraph_html returned an object of class `list` instead of a `shiny.tag`.
sg3
streamgraph_html returned an object of class `list` instead of a `shiny.tag`.streamgraph_html returned an object of class `list` instead of a `shiny.tag`.
sg4
streamgraph_html returned an object of class `list` instead of a `shiny.tag`.streamgraph_html returned an object of class `list` instead of a `shiny.tag`.

2. Plot profile using afinn We group each book in groups of 100 lines, and then we use the lexicon to give every word a value. Then, we add them across the chunks to get a tentative sentiment score.

library(tidyverse)
package ć¤¼ćø±tidyverseć¤¼ćø² was built under R version 4.0.5Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ---------------------------------------------------------------- tidyverse 1.3.1 --
v ggplot2 3.3.5     v readr   2.0.1
v tibble  3.1.4     v purrr   0.3.4
v tidyr   1.1.4     v forcats 0.5.1
package ć¤¼ćø±ggplot2ć¤¼ćø² was built under R version 4.0.5package ć¤¼ćø±tibbleć¤¼ćø² was built under R version 4.0.5package ć¤¼ćø±tidyrć¤¼ćø² was built under R version 4.0.5package ć¤¼ćø±readrć¤¼ćø² was built under R version 4.0.5package ć¤¼ćø±purrrć¤¼ćø² was built under R version 4.0.5package ć¤¼ćø±forcatsć¤¼ćø² was built under R version 4.0.5-- Conflicts ------------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
  sentiments_bing <- all_books %>% group_by(gutenberg_id) %>% inner_join(get_sentiments("afinn")) %>%
  count(gutenberg_id, index = linenumber %/% 100, value) %>% spread(value, n, fill = 0) %>% mutate(val = `-5`*-5 + `-4`*-4 + `-3`*-3 + `-2`*-2 + `-1`*-1+ `5`*5 + `4`*4 + `3`*3 + `2`*2 + `1`*1 )
Joining, by = "word"

We then plot them:


library(plotly)
package ć¤¼ćø±plotlyć¤¼ćø² was built under R version 4.0.5Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ć¤¼ćø±plotlyć¤¼ćø²

The following object is masked from ć¤¼ćø±package:ggplot2ć¤¼ćø²:

    last_plot

The following object is masked from ć¤¼ćø±package:statsć¤¼ćø²:

    filter

The following object is masked from ć¤¼ćø±package:graphicsć¤¼ćø²:

    layout
sentiments_bing$gutenberg_id <- sapply(sentiments_bing$gutenberg_id, toString)

  sentiments_bing[sentiments_bing == 19066] <- "Brigands - Cummings"
sentiments_bing[sentiments_bing == 61884] <- "War Nymphs - Cummings"
sentiments_bing[sentiments_bing == 32664] <- "Black Amazon - Brackett"
sentiments_bing[sentiments_bing == 64043] <- "Enchantress - Brackett"
  library(ggplot2)
p <- ggplot(sentiments_bing, aes(index, val, fill = gutenberg_id)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~gutenberg_id, ncol = 2, scales = "free_x") +
  labs (x = "novel segment", y = "sentiment score")

ggplotly(p)

3. Principal Components Analysis

We are interested in seeing the differences in the vocabulary used between the two authors. We change their IDs.

all_books$gutenberg_id <- sapply(all_books$gutenberg_id, toString)
  all_books[all_books == 19066] <- "Cummings"
  all_books[all_books == 61884] <- "Cummings"
  all_books[all_books == 32664] <- "Brackett"
  all_books[all_books == 64043] <- "Brackett"
  
  frequency <- all_books %>%
  count(gutenberg_id, word) %>%
  group_by(gutenberg_id) %>%
  mutate(proportion = n / sum(n)) %>% 
  select(-n) %>% 
  spread(gutenberg_id, proportion)
  


# expect a warning about rows with missing values being removed
t <- ggplot(frequency, aes(x = `Brackett`, y = `Cummings`, color = abs(`Cummings` - `Brackett`))) +
  geom_abline(color = "gray40", lty = 2) +
  geom_jitter(alpha = 0.1, size = 2.5, width = 0.3, height = 0.3) +
  geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +
  scale_color_gradient(limits = c(0, 0.001), low = "darkslategray4", high = "gray75") +
  scale_x_log10() +
  scale_y_log10() +
  theme(legend.position="none") +
  labs(y = "Cummings", x = NULL)

ggplotly(t)
LS0tDQp0aXRsZTogIkFsaWVucyBhbmQgdGhlaXIgU2VudGltZW50cyINCmF1dGhvcjogIkp1YW4gUGnDsWVyb3MiDQpkYXRlOiAiMTAvMjQvMjAyMSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpzYXZlIHRoaXMgbm90ZWJvb2sNCmBgYHtyfQ0Kc2F2ZS5pbWFnZShmaWxlID0gImM6L3VzZXJzL2p1YW5kL2Rlc2t0b3Avci9oZWxsby5SRGF0YSIpDQpgYGANCg0KSW4gdGhpcyBub3RlYm9vaywgd2UgZXhhbWluZSB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBhdXRob3JzIEJyYWNrZXR0IGFuZCBDdW1taW5ncywgaW4gdGhyZWUgd2F5czogZmlyc3QsIHdlIGFuYWx5c2UgdGhlaXIgYm9va3MgYnkgcGFpcmluZyB0aGVtIHdpdGggdGhlIE5SQyBzZW50aW1lbnQgZGF0YWJhc2UsIGFuZCB3ZSBvYnRhaW4gYSBwb3NzaWJsZSBvcmRlcmluZyBvZiB0aGUgZW1vdGlvbnMgY29udmV5ZWQgYnkgdGhlIHdvcmtzIGFuZCBob3cgdGhleSB2YXJ5IHRocm91Z2hvdXQgdGhlbS4gU2Vjb25kLCB3ZSB1c2UgdGhlIGFmaW5uIHNlbnRpbWVudCBkYXRhYmFzZSB0byBnaXZlIHdvcmRzIGEgc2NvcmUgZnJvbSAtNSB0byA1LCBhbmQgb2J0YWluIGEgInBsb3QgcHJvZmlsZSIgb2YgZWFjaCBib29rIGJ5IGFnZ3JlZ2F0aW5nIHRoZXNlIHNjb3JlcyBpbnRvIGNodW5rcyBvZiB0aGUgc3RvcnkuIExhc3RseSwgd2UgcGVyZm9ybSBhIHByaW5jaXBhbCBjb21wb25lbnRzIGFuYWx5c2lzIGluIG9yZGVyIHRvIG9idGFpbiBwb3NzaWJsZSBzdHlsaXN0aWMgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgYXV0aG9ycy4NCg0KQWRkaW5nIGVzc2VudGlhbCBsaWJyYXJpZXMNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcyhjKCJ0aWR5dGV4dCIsInRleHRkYXRhIiwiZ3V0ZW5iZXJnciIsImdncGxvdDIiLCJ0aWR5ciIsImphbmVhdXN0ZW5yIiwic3RyaW5nciIsImRldnRvb2xzIiwiY3VybCIpKQ0KaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90bHkiKQ0KYGBgDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcyhjKCJzY2FsZXMiLCJwbG90bHkiKSkNCmBgYA0KRG93bmxvYWRpbmcgdGhlIGJvb2tzIGZyb20gUEcNCmBgYHtyfQ0KbGlicmFyeSgiZ3V0ZW5iZXJnciIpDQpjdW1taW5nczEgPC0gZ3V0ZW5iZXJnX2Rvd25sb2FkKDE5MDY2KQ0KY3VtbWluZ3MyIDwtIGd1dGVuYmVyZ19kb3dubG9hZCg2MTg4NCkNCmJyYWNrZXR0MSA8LSBndXRlbmJlcmdfZG93bmxvYWQoMzI2NjQpDQpicmFja2V0dDIgPC0gZ3V0ZW5iZXJnX2Rvd25sb2FkKDY0MDQzKQ0KYGBgDQpBY3RpdmF0aW5nIG5lY2Vzc2FyeSBsaWJyYXJpZXMgZm9yIHRleHQgY2xlYW5pbmcNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeSh0aWR5dGV4dCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0ZXh0ZGF0YSkNCmluc3RhbGwucGFja2FnZXMoInNjYWxlcyIpDQpgYGANCg0KVHJhbnNmb3JtaW5nIHRoZSBib29rcyBpbnRvIHRpZHkgZm9ybWF0DQpgYGB7cn0NCnRpZHlfY3VtbWluZ3MxIDwtIGN1bW1pbmdzMSAlPiUgbXV0YXRlKGxpbmVudW1iZXIgPSByb3dfbnVtYmVyKCkpICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpICU+JSBhbnRpX2pvaW4oc3RvcF93b3JkcykNCg0KdGlkeV9icmFja2V0dDEgPC0gYnJhY2tldHQxICU+JSBtdXRhdGUobGluZW51bWJlciA9IHJvd19udW1iZXIoKSkgJT4lIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lIGFudGlfam9pbihzdG9wX3dvcmRzKQ0KDQp0aWR5X2N1bW1pbmdzMiA8LSBjdW1taW5nczIgJT4lIG11dGF0ZShsaW5lbnVtYmVyID0gcm93X251bWJlcigpKSAlPiUgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KSAlPiUgYW50aV9qb2luKHN0b3Bfd29yZHMpDQoNCnRpZHlfYnJhY2tldHQyIDwtIGJyYWNrZXR0MiAlPiUgbXV0YXRlKGxpbmVudW1iZXIgPSByb3dfbnVtYmVyKCkpICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpICU+JSBhbnRpX2pvaW4oc3RvcF93b3JkcykNCg0KYWxsX2Jvb2tzIDwtIHJiaW5kKHRpZHlfY3VtbWluZ3MxLCB0aWR5X2N1bW1pbmdzMiwgdGlkeV9icmFja2V0dDEsIHRpZHlfYnJhY2tldHQyKQ0KDQoNCg0KYGBgDQoNCg0KDQoqKjE6IFNlbnRpbWVudCBBbmFseXNpcyB3aXRoIE5SQyoqDQpmb3IgcmVmZXJlbmNlOiBzZW50aW1lbnRzIGFyZSBhbmdlciwgYW50aWNpcGF0aW9uLCBkaXNndXN0LCBmZWFyLCBqb3ksIHNhZG5lc3MsIHN1cnByaXNlLCB0cnVzdA0KYGBge3J9DQogIG5yYyA8LSBnZXRfc2VudGltZW50cygibnJjIikNCiAgc2VudGltZW50cyA8LSBjKCJhbmdlciIsImFudGljaXBhdGlvbiIsICJkaXNndXN0IiwgImZlYXIiLCAiam95IiwgInNhZG5lc3MiLCAic3VycHJpc2UiLCAidHJ1c3QiKQ0KYGBgDQoNCm1ha2UgYSB0YWJsZSB3aXRoIGFsbCBsaW5lcywgdGhlbiBjb3VudCBhbGwgc2VudGltZW50cyBwZXIgbGluZQ0KYGBge3J9DQogIA0KICBsaW5lcyA8LSBjKDE6OTgwMSwxOjIyOTgsMTozMTU3LDE6MzEzNykNCiAgYm9va3MgPC0gYyhyZXAoMTkwNjYsIDk4MDEpLHJlcCg2MTg4NCwgMjI5OCkscmVwKDMyNjY0LCAzMTU3KSxyZXAoNjQwNDMsIDMxMzcpKQ0KICBzZW50aW1lbnRfdGFibGUgPC0gZGF0YS5mcmFtZShib29rcywgbGluZXMsIGFuZ2VyID0gaW50ZWdlcigxODM5MyksIGFudGljaXBhdGlvbiA9IGludGVnZXIoMTgzOTMpLCBkaXNndXN0ID0gaW50ZWdlcigxODM5MyksIGZlYXIgPSBpbnRlZ2VyKDE4MzkzKSwgam95ID0gaW50ZWdlcigxODM5MyksIHNhZG5lc3MgPSBpbnRlZ2VyKDE4MzkzKSwgc3VycHJpc2UgPSBpbnRlZ2VyKDE4MzkzKSwgdHJ1c3QgPSBpbnRlZ2VyKDE4MzkzKSkNCmBgYA0KYWRkIGNvdW50cyBmb3Igc2VudGltZW50cyBmb3IgZWFjaCBsaW5lIHRvIHNlbnRpbWVudF90YWJsZQ0KYGBge3J9DQogIGZvciAoaSBpbiAxOmxlbmd0aChzZW50aW1lbnRzKSkNCiAgew0KICAgIG5ld190YWJsZSA8LSBhbGxfYm9va3MgJT4lIGlubmVyX2pvaW4obnJjICU+JSBmaWx0ZXIoc2VudGltZW50ID09IHNlbnRpbWVudHNbaV0pKSAlPiUgICAgY291bnQoZ3V0ZW5iZXJnX2lkLCBpbmRleCA9IGxpbmVudW1iZXIpDQogICAgDQogICAgZm9yIChqIGluIDE6bnJvdyhuZXdfdGFibGUpKQ0KICAgIHsNCiAgICAgIHNlbnRpbWVudF90YWJsZVtzZW50aW1lbnRfdGFibGUkYm9va3MgPT0gKG5ld190YWJsZSRndXRlbmJlcmdfaWQpW2pdICYgc2VudGltZW50X3RhYmxlJGxpbmVzID09IChuZXdfdGFibGUkaW5kZXgpW2pdLCBzZW50aW1lbnRzW2ldXSA8LSAobmV3X3RhYmxlJG4pW2pdDQogICAgfQ0KICB9DQpgYGANCg0KY291bnRpbmcgdGhlIG51bWJlciBvZiB3b3JkcyBwZXIgc2VudGltZW50IGluIGV2ZXJ5IDEwMCBsaW5lIGNodW5rDQoodGhpcyBoYWQgdG8gYmUgZG9uZSBkaXJ0aWx5IGFzIHdlIGRpZG4ndCBmaW5kIGFuIGVxdWl2YWxlbnQgZnVuY3Rpb24gdG8gZ3JvdXAgdGhlIGNodW5rcykNCmBgYHtyfQ0KICBzZWN0b3JzIDwtIGMoMDoxMjIsIDA6MjgsIDA6MzksIDA6MzkpDQogIGJvb2tzIDwtIGMocmVwKDE5MDY2LCAxMjMpLCByZXAoNjE4ODQsIDI5KSwgcmVwKDMyNjY0LCA0MCksIHJlcCg2NDA0MywgNDApKQ0KICBzZW50aW1lbnRfc3VtbWFyeSA8LSBkYXRhLmZyYW1lKHNlY3RvcnMsIGJvb2tzLCBhbmdlciA9IGludGVnZXIoMjMyKSwgYW50aWNpcGF0aW9uID0gaW50ZWdlcigyMzIpLCBkaXNndXN0ID0gaW50ZWdlcigyMzIpLCBmZWFyID0gaW50ZWdlcigyMzIpLCBqb3kgPSBpbnRlZ2VyKDIzMiksIHNhZG5lc3MgPSBpbnRlZ2VyKDIzMiksIHN1cnByaXNlID0gaW50ZWdlcigyMzIpLCB0cnVzdCA9IGludGVnZXIoMjMyKSkNCg0KZm9yKGkgaW4gMTpsZW5ndGgoc2VudGltZW50cykpew0KICBzIDwtIHNlbnRpbWVudHNbaV0NCiAgY29sIDwtIHB1bGwoc2VudGltZW50X3RhYmxlLCBzKQ0KICBmb3IoaiBpbiAxOm5yb3coc2VudGltZW50X3RhYmxlKSkNCiAgew0KICAgIGluZGV4IDwtIChzZW50aW1lbnRfdGFibGUkbGluZXNbal0pICUvJSAxMDANCiAgICANCiAgICBzZW50aW1lbnRfc3VtbWFyeVtzZW50aW1lbnRfc3VtbWFyeSRib29rcyA9PSBzZW50aW1lbnRfdGFibGUkYm9va3Nbal0gJiBzZW50aW1lbnRfc3VtbWFyeSRzZWN0b3JzID09IGluZGV4LCBzZW50aW1lbnRzW2ldXSA8LSBzZW50aW1lbnRfc3VtbWFyeVtzZW50aW1lbnRfc3VtbWFyeSRib29rcyA9PSBzZW50aW1lbnRfdGFibGUkYm9va3Nbal0gJiBzZW50aW1lbnRfc3VtbWFyeSRzZWN0b3JzID09IGluZGV4LCBzZW50aW1lbnRzW2ldXSsgY29sW2pdDQogIH0NCn0NCiANCiAgDQpgYGANCg0KV2UgdHVybiB0aGUgY29sdW1ucyBvZiBkYXRhIGludG8gYSBmYWN0b3IgdG8gdGlkeSB0aGVtIGFuZCB0aGVuIHdlIHBsb3QgdXNpbmcgYSBzdHJlYW1ncmFwaDoNCkZvciBzb21lIHJlYXNvbiwgdGhlIHN0cmVhbWdyYXBocyBvbmx5IGFkbWl0IGRhdGUgdmFsdWVzLiBXZSBjb252ZXJ0IHRoZSBzZWN0b3JzIHRvICJkYXRlcyI6DQpgYGB7cn0NCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaHJicm1zdHIvc3RyZWFtZ3JhcGgiKQ0KbGlicmFyeShzdHJlYW1ncmFwaCkNCnNlbnRpbWVudF9zdW1tYXJ5IDwtIHBpdm90X2xvbmdlcihzZW50aW1lbnRfc3VtbWFyeSwgYW5nZXI6dHJ1c3QsICJzZW50aW1lbnQiKQ0Kc2VudGltZW50X3N1bW1hcnkkc2VjdG9ycyA8LSBhcy5EYXRlKGFzLkRhdGUoSVNPZGF0ZShzZW50aW1lbnRfc3VtbWFyeSRzZWN0b3JzLCAxLCAxKSkpDQpgYGANCg0KDQpgYGB7cn0NCg0KDQpzZzEgPC0gc2VudGltZW50X3N1bW1hcnkgJT4lIGZpbHRlcihib29rcz09MTkwNjYpICU+JSBzdHJlYW1ncmFwaCgic2VudGltZW50IiwgInZhbHVlIiwgZGF0ZT0ic2VjdG9ycyIsIGludGVycG9sYXRlPSJzdGVwIikNCnNnMiA8LSBzZW50aW1lbnRfc3VtbWFyeSAlPiUgZmlsdGVyKGJvb2tzPT02MTg4NCkgJT4lIHN0cmVhbWdyYXBoKCJzZW50aW1lbnQiLCAidmFsdWUiLCBkYXRlPSJzZWN0b3JzIiwgaW50ZXJwb2xhdGU9InN0ZXAiKSANCnNnMyA8LSBzZW50aW1lbnRfc3VtbWFyeSAlPiUgZmlsdGVyKGJvb2tzPT0zMjY2NCkgJT4lIHN0cmVhbWdyYXBoKCJzZW50aW1lbnQiLCAidmFsdWUiLCBkYXRlPSJzZWN0b3JzIiwgaW50ZXJwb2xhdGU9InN0ZXAiKQ0Kc2c0IDwtIHNlbnRpbWVudF9zdW1tYXJ5ICU+JSBmaWx0ZXIoYm9va3M9PTY0MDQzKSAlPiUgc3RyZWFtZ3JhcGgoInNlbnRpbWVudCIsICJ2YWx1ZSIsIGRhdGU9InNlY3RvcnMiLCBpbnRlcnBvbGF0ZT0ic3RlcCIpDQpzZzENCnNnMg0Kc2czDQpzZzQNCmBgYA0KDQoNCioqMi4gUGxvdCBwcm9maWxlIHVzaW5nIGFmaW5uKioNCldlIGdyb3VwIGVhY2ggYm9vayBpbiBncm91cHMgb2YgMTAwIGxpbmVzLCBhbmQgdGhlbiB3ZSB1c2UgdGhlIGxleGljb24gdG8gZ2l2ZSBldmVyeSB3b3JkIGEgdmFsdWUuIFRoZW4sIHdlIGFkZCB0aGVtIGFjcm9zcyB0aGUgY2h1bmtzIHRvIGdldCBhIHRlbnRhdGl2ZSBzZW50aW1lbnQgc2NvcmUuDQoNCmBgYHtyfQ0KDQogIHNlbnRpbWVudHNfYmluZyA8LSBhbGxfYm9va3MgJT4lIGdyb3VwX2J5KGd1dGVuYmVyZ19pZCkgJT4lIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImFmaW5uIikpICU+JQ0KICBjb3VudChndXRlbmJlcmdfaWQsIGluZGV4ID0gbGluZW51bWJlciAlLyUgMTAwLCB2YWx1ZSkgJT4lIHNwcmVhZCh2YWx1ZSwgbiwgZmlsbCA9IDApICU+JSBtdXRhdGUodmFsID0gYC01YCotNSArIGAtNGAqLTQgKyBgLTNgKi0zICsgYC0yYCotMiArIGAtMWAqLTErIGA1YCo1ICsgYDRgKjQgKyBgM2AqMyArIGAyYCoyICsgYDFgKjEgKQ0KYGBgDQoNCldlIHRoZW4gcGxvdCB0aGVtOg0KYGBge3J9DQoNCmxpYnJhcnkocGxvdGx5KQ0KDQpzZW50aW1lbnRzX2JpbmckZ3V0ZW5iZXJnX2lkIDwtIHNhcHBseShzZW50aW1lbnRzX2JpbmckZ3V0ZW5iZXJnX2lkLCB0b1N0cmluZykNCg0KICBzZW50aW1lbnRzX2Jpbmdbc2VudGltZW50c19iaW5nID09IDE5MDY2XSA8LSAiQnJpZ2FuZHMgLSBDdW1taW5ncyINCnNlbnRpbWVudHNfYmluZ1tzZW50aW1lbnRzX2JpbmcgPT0gNjE4ODRdIDwtICJXYXIgTnltcGhzIC0gQ3VtbWluZ3MiDQpzZW50aW1lbnRzX2Jpbmdbc2VudGltZW50c19iaW5nID09IDMyNjY0XSA8LSAiQmxhY2sgQW1hem9uIC0gQnJhY2tldHQiDQpzZW50aW1lbnRzX2Jpbmdbc2VudGltZW50c19iaW5nID09IDY0MDQzXSA8LSAiRW5jaGFudHJlc3MgLSBCcmFja2V0dCINCiAgbGlicmFyeShnZ3Bsb3QyKQ0KcCA8LSBnZ3Bsb3Qoc2VudGltZW50c19iaW5nLCBhZXMoaW5kZXgsIHZhbCwgZmlsbCA9IGd1dGVuYmVyZ19pZCkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBmYWNldF93cmFwKH5ndXRlbmJlcmdfaWQsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV94IikgKw0KICBsYWJzICh4ID0gIm5vdmVsIHNlZ21lbnQiLCB5ID0gInNlbnRpbWVudCBzY29yZSIpDQoNCmdncGxvdGx5KHApDQpgYGANCg0KKiozLiBQcmluY2lwYWwgQ29tcG9uZW50cyBBbmFseXNpcyoqDQoNCldlIGFyZSBpbnRlcmVzdGVkIGluIHNlZWluZyB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIHZvY2FidWxhcnkgdXNlZCBiZXR3ZWVuIHRoZSB0d28gYXV0aG9ycy4gV2UgY2hhbmdlIHRoZWlyIElEcy4NCg0KYGBge3J9DQphbGxfYm9va3MkZ3V0ZW5iZXJnX2lkIDwtIHNhcHBseShhbGxfYm9va3MkZ3V0ZW5iZXJnX2lkLCB0b1N0cmluZykNCiAgYWxsX2Jvb2tzW2FsbF9ib29rcyA9PSAxOTA2Nl0gPC0gIkN1bW1pbmdzIg0KICBhbGxfYm9va3NbYWxsX2Jvb2tzID09IDYxODg0XSA8LSAiQ3VtbWluZ3MiDQogIGFsbF9ib29rc1thbGxfYm9va3MgPT0gMzI2NjRdIDwtICJCcmFja2V0dCINCiAgYWxsX2Jvb2tzW2FsbF9ib29rcyA9PSA2NDA0M10gPC0gIkJyYWNrZXR0Ig0KICANCmBgYA0KDQpgYGB7cn0NCiAgZnJlcXVlbmN5IDwtIGFsbF9ib29rcyAlPiUNCiAgY291bnQoZ3V0ZW5iZXJnX2lkLCB3b3JkKSAlPiUNCiAgZ3JvdXBfYnkoZ3V0ZW5iZXJnX2lkKSAlPiUNCiAgbXV0YXRlKHByb3BvcnRpb24gPSBuIC8gc3VtKG4pKSAlPiUgDQogIHNlbGVjdCgtbikgJT4lIA0KICBzcHJlYWQoZ3V0ZW5iZXJnX2lkLCBwcm9wb3J0aW9uKQ0KYGBgDQoNCg0KYGBge3J9DQogIA0KDQoNCiMgZXhwZWN0IGEgd2FybmluZyBhYm91dCByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMgYmVpbmcgcmVtb3ZlZA0KdCA8LSBnZ3Bsb3QoZnJlcXVlbmN5LCBhZXMoeCA9IGBCcmFja2V0dGAsIHkgPSBgQ3VtbWluZ3NgLCBjb2xvciA9IGFicyhgQ3VtbWluZ3NgIC0gYEJyYWNrZXR0YCkpKSArDQogIGdlb21fYWJsaW5lKGNvbG9yID0gImdyYXk0MCIsIGx0eSA9IDIpICsNCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjEsIHNpemUgPSAyLjUsIHdpZHRoID0gMC4zLCBoZWlnaHQgPSAwLjMpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHdvcmQpLCBjaGVja19vdmVybGFwID0gVFJVRSwgdmp1c3QgPSAxLjUpICsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobGltaXRzID0gYygwLCAwLjAwMSksIGxvdyA9ICJkYXJrc2xhdGVncmF5NCIsIGhpZ2ggPSAiZ3JheTc1IikgKw0KICBzY2FsZV94X2xvZzEwKCkgKw0KICBzY2FsZV95X2xvZzEwKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArDQogIGxhYnMoeSA9ICJDdW1taW5ncyIsIHggPSBOVUxMKQ0KDQpnZ3Bsb3RseSh0KQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==