In this assignment, you should start by getting the primary example code from chapter 2 working in an R Markdown document. You should provide a citation to this base code. You’re then asked to extend the code in two ways:

Work with a different corpus of your choosing, and Incorporate at least one additional sentiment lexicon (possibly from another R package that you’ve found through research).

Chapter 2

suppressWarnings({
library(tidyverse)
library(tidytext)
})
get_sentiments("afinn")
## # A tibble: 2,477 × 2
##    word       value
##    <chr>      <dbl>
##  1 abandon       -2
##  2 abandoned     -2
##  3 abandons      -2
##  4 abducted      -2
##  5 abduction     -2
##  6 abductions    -2
##  7 abhor         -3
##  8 abhorred      -3
##  9 abhorrent     -3
## 10 abhors        -3
## # ℹ 2,467 more rows
1
## [1] 1
get_sentiments("bing")
## # A tibble: 6,786 × 2
##    word        sentiment
##    <chr>       <chr>    
##  1 2-faces     negative 
##  2 abnormal    negative 
##  3 abolish     negative 
##  4 abominable  negative 
##  5 abominably  negative 
##  6 abominate   negative 
##  7 abomination negative 
##  8 abort       negative 
##  9 aborted     negative 
## 10 aborts      negative 
## # ℹ 6,776 more rows
get_sentiments("nrc")
## # A tibble: 13,872 × 2
##    word        sentiment
##    <chr>       <chr>    
##  1 abacus      trust    
##  2 abandon     fear     
##  3 abandon     negative 
##  4 abandon     sadness  
##  5 abandoned   anger    
##  6 abandoned   fear     
##  7 abandoned   negative 
##  8 abandoned   sadness  
##  9 abandonment anger    
## 10 abandonment fear     
## # ℹ 13,862 more rows

Sentiment analysis with inner join

library(janeaustenr)
## Warning: package 'janeaustenr' was built under R version 4.2.3
library(dplyr)
library(stringr)

tidy_books <- austen_books() %>%
  group_by(book) %>%
  mutate(
    linenumber = row_number(),
    chapter = cumsum(str_detect(text, 
                                regex("^chapter [\\divxlc]", 
                                      ignore_case = TRUE)))) %>%
  ungroup() %>%
  unnest_tokens(word, text)
nrc_joy <- get_sentiments("nrc") %>% 
  filter(sentiment == "joy")

tidy_books %>%
  filter(book == "Emma") %>%
  inner_join(nrc_joy) %>%
  count(word, sort = TRUE)
## Joining with `by = join_by(word)`
## # A tibble: 301 × 2
##    word          n
##    <chr>     <int>
##  1 good        359
##  2 friend      166
##  3 hope        143
##  4 happy       125
##  5 love        117
##  6 deal         92
##  7 found        92
##  8 present      89
##  9 kind         82
## 10 happiness    76
## # ℹ 291 more rows
library(tidyr)

jane_austen_sentiment <- tidy_books %>%
  inner_join(get_sentiments("bing")) %>%
  count(book, index = linenumber %/% 80, sentiment) %>%
  pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>% 
  mutate(sentiment = positive - negative)
## Joining with `by = join_by(word)`
## Warning in inner_join(., get_sentiments("bing")): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 435434 of `x` matches multiple rows in `y`.
## ℹ Row 5051 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.

Plotting sentiment scores

library(ggplot2)

ggplot(jane_austen_sentiment, aes(index, sentiment, fill = book)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~book, ncol = 2, scales = "free_x")

Comparing the three sentiment dictionaries

pride_prejudice <- tidy_books %>% 
  filter(book == "Pride & Prejudice")

pride_prejudice
## # A tibble: 122,204 × 4
##    book              linenumber chapter word     
##    <fct>                  <int>   <int> <chr>    
##  1 Pride & Prejudice          1       0 pride    
##  2 Pride & Prejudice          1       0 and      
##  3 Pride & Prejudice          1       0 prejudice
##  4 Pride & Prejudice          3       0 by       
##  5 Pride & Prejudice          3       0 jane     
##  6 Pride & Prejudice          3       0 austen   
##  7 Pride & Prejudice          7       1 chapter  
##  8 Pride & Prejudice          7       1 1        
##  9 Pride & Prejudice         10       1 it       
## 10 Pride & Prejudice         10       1 is       
## # ℹ 122,194 more rows
afinn <- pride_prejudice %>% 
  inner_join(get_sentiments("afinn")) %>% 
  group_by(index = linenumber %/% 80) %>% 
  summarise(sentiment = sum(value)) %>% 
  mutate(method = "AFINN")
## Joining with `by = join_by(word)`
bing_and_nrc <- bind_rows(
  pride_prejudice %>% 
    inner_join(get_sentiments("bing")) %>%
    mutate(method = "Bing et al."),
  pride_prejudice %>% 
    inner_join(get_sentiments("nrc") %>% 
                 filter(sentiment %in% c("positive", 
                                         "negative"))
    ) %>%
    mutate(method = "NRC")) %>%
  count(method, index = linenumber %/% 80, sentiment) %>%
  pivot_wider(names_from = sentiment,
              values_from = n,
              values_fill = 0) %>% 
  mutate(sentiment = positive - negative)
## Joining with `by = join_by(word)`
## Joining with `by = join_by(word)`
## Warning in inner_join(., get_sentiments("nrc") %>% filter(sentiment %in% : Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 215 of `x` matches multiple rows in `y`.
## ℹ Row 5178 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.

Net sentiment (positive - negative)

bind_rows(afinn, 
          bing_and_nrc) %>%
  ggplot(aes(index, sentiment, fill = method)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~method, ncol = 1, scales = "free_y")

get_sentiments("nrc") %>% 
  filter(sentiment %in% c("positive", "negative")) %>% 
  count(sentiment)
## # A tibble: 2 × 2
##   sentiment     n
##   <chr>     <int>
## 1 negative   3316
## 2 positive   2308
get_sentiments("bing") %>% 
  count(sentiment)
## # A tibble: 2 × 2
##   sentiment     n
##   <chr>     <int>
## 1 negative   4781
## 2 positive   2005

Most common positive and negative words.

bing_word_counts <- tidy_books %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup()
## Joining with `by = join_by(word)`
## Warning in inner_join(., get_sentiments("bing")): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 435434 of `x` matches multiple rows in `y`.
## ℹ Row 5051 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.
bing_word_counts
## # A tibble: 2,585 × 3
##    word     sentiment     n
##    <chr>    <chr>     <int>
##  1 miss     negative   1855
##  2 well     positive   1523
##  3 good     positive   1380
##  4 great    positive    981
##  5 like     positive    725
##  6 better   positive    639
##  7 enough   positive    613
##  8 happy    positive    534
##  9 love     positive    495
## 10 pleasure positive    462
## # ℹ 2,575 more rows
bing_word_counts %>%
  group_by(sentiment) %>%
  slice_max(n, n = 10) %>% 
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(n, word, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(x = "Contribution to sentiment",
       y = NULL)

custom_stop_words <- bind_rows(tibble(word = c("miss"),  
                                      lexicon = c("custom")), 
                               stop_words)

custom_stop_words
## # A tibble: 1,150 × 2
##    word        lexicon
##    <chr>       <chr>  
##  1 miss        custom 
##  2 a           SMART  
##  3 a's         SMART  
##  4 able        SMART  
##  5 about       SMART  
##  6 above       SMART  
##  7 according   SMART  
##  8 accordingly SMART  
##  9 across      SMART  
## 10 actually    SMART  
## # ℹ 1,140 more rows

Wordclouds.

library(wordcloud)
## Warning: package 'wordcloud' was built under R version 4.2.3
## Loading required package: RColorBrewer
tidy_books %>%
  anti_join(stop_words) %>%
  count(word) %>%
  with(wordcloud(word, n, max.words = 100))
## Joining with `by = join_by(word)`

library(reshape2)
## 
## Attaching package: 'reshape2'
## The following object is masked from 'package:tidyr':
## 
##     smiths
tidy_books %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  acast(word ~ sentiment, value.var = "n", fill = 0) %>%
  comparison.cloud(colors = c("gray20", "gray80"),
                   max.words = 100)
## Joining with `by = join_by(word)`
## Warning in inner_join(., get_sentiments("bing")): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 435434 of `x` matches multiple rows in `y`.
## ℹ Row 5051 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.

Looking at units beyond just words.

p_and_p_sentences <- tibble(text = prideprejudice) %>% 
  unnest_tokens(sentence, text, token = "sentences")
p_and_p_sentences$sentence[2]
## [1] "by jane austen"
austen_chapters <- austen_books() %>%
  group_by(book) %>%
  unnest_tokens(chapter, text, token = "regex", 
                pattern = "Chapter|CHAPTER [\\dIVXLC]") %>%
  ungroup()

austen_chapters %>% 
  group_by(book) %>% 
  summarise(chapters = n())
## # A tibble: 6 × 2
##   book                chapters
##   <fct>                  <int>
## 1 Sense & Sensibility       51
## 2 Pride & Prejudice         62
## 3 Mansfield Park            49
## 4 Emma                      56
## 5 Northanger Abbey          32
## 6 Persuasion                25
bingnegative <- get_sentiments("bing") %>% 
  filter(sentiment == "negative")

wordcounts <- tidy_books %>%
  group_by(book, chapter) %>%
  summarize(words = n())
## `summarise()` has grouped output by 'book'. You can override using the
## `.groups` argument.
tidy_books %>%
  semi_join(bingnegative) %>%
  group_by(book, chapter) %>%
  summarize(negativewords = n()) %>%
  left_join(wordcounts, by = c("book", "chapter")) %>%
  mutate(ratio = negativewords/words) %>%
  filter(chapter != 0) %>%
  slice_max(ratio, n = 1) %>% 
  ungroup()
## Joining with `by = join_by(word)`
## `summarise()` has grouped output by 'book'. You can override using the
## `.groups` argument.
## # A tibble: 6 × 5
##   book                chapter negativewords words  ratio
##   <fct>                 <int>         <int> <int>  <dbl>
## 1 Sense & Sensibility      43           161  3405 0.0473
## 2 Pride & Prejudice        34           111  2104 0.0528
## 3 Mansfield Park           46           173  3685 0.0469
## 4 Emma                     15           151  3340 0.0452
## 5 Northanger Abbey         21           149  2982 0.0500
## 6 Persuasion                4            62  1807 0.0343

Exercise

library(dplyr)
library(tidyr)
library(ggplot2)
library(stringr)
library(textdata)
## Warning: package 'textdata' was built under R version 4.2.3
# Load the text of Persuasion
persuasion_text <- austen_books() %>%
  filter(book == "Persuasion") %>%
  pull(text)

# Tokenize the text
persuasion_tidy <- tibble(text = persuasion_text) %>%
  unnest_tokens(word, text)

# Perform sentiment analysis
nrc_sentiments <- get_sentiments("nrc")

persuasion_sentiments <- persuasion_tidy %>%
  inner_join(nrc_sentiments) %>%
  count(word, sentiment, sort = TRUE)
## Joining with `by = join_by(word)`
## Warning in inner_join(., nrc_sentiments): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 8 of `x` matches multiple rows in `y`.
## ℹ Row 11348 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.
# Visualize the most common positive words in Persuasion
persuasion_sentiments %>%
  filter(sentiment == "positive") %>%
  top_n(10, n) %>%
  ggplot(aes(reorder(word, n), n, fill = word)) +
  geom_col() +
  coord_flip() +
  labs(title = "Top Positive Words in Persuasion")

# Analyze sentiment changes throughout the novel
persuasion_sentiments <- persuasion_tidy %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE)
## Joining with `by = join_by(word)`
persuasion_sentiments %>%
  group_by(sentiment) %>%
  slice_max(n, n = 10) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(n, word, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(x = "Contribution to sentiment", y = NULL)

Conclusion

The sentiment analysis of “Persuasion” highlights prevalent positive words such as “love” and “happy,” contributing to the book’s overall positive sentiment. This analysis offers insights into the novel’s emotional tone and how it changes throughout the story.

References

LS0tDQp0aXRsZTogIlNlbnRpbWVudCBBbmFseXNpcyINCmF1dGhvcjogIkxhdXJhIFB1ZWJsYSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogb3BlbmludHJvOjpsYWJfcmVwb3J0DQotLS0NCg0KSW4gdGhpcyBhc3NpZ25tZW50LCB5b3Ugc2hvdWxkIHN0YXJ0IGJ5IGdldHRpbmcgdGhlIHByaW1hcnkgZXhhbXBsZSBjb2RlIGZyb20gY2hhcHRlciAyIHdvcmtpbmcgaW4gYW4gUiBNYXJrZG93biBkb2N1bWVudC4gIFlvdSBzaG91bGQgcHJvdmlkZSBhIGNpdGF0aW9uIHRvIHRoaXMgYmFzZSBjb2RlLiAgWW914oCZcmUgdGhlbiBhc2tlZCB0byBleHRlbmQgdGhlIGNvZGUgaW4gdHdvIHdheXM6DQoNCldvcmsgd2l0aCBhIGRpZmZlcmVudCBjb3JwdXMgb2YgeW91ciBjaG9vc2luZywgYW5kDQpJbmNvcnBvcmF0ZSBhdCBsZWFzdCBvbmUgYWRkaXRpb25hbCBzZW50aW1lbnQgbGV4aWNvbiAocG9zc2libHkgZnJvbSBhbm90aGVyIFIgcGFja2FnZSB0aGF0IHlvdeKAmXZlIGZvdW5kIHRocm91Z2ggcmVzZWFyY2gpLg0KDQojIyMgQ2hhcHRlciAyDQoNCmBgYHtyIGxvYWQtcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0V9DQpzdXBwcmVzc1dhcm5pbmdzKHsNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0aWR5dGV4dCkNCn0pDQpgYGANCg0KYGBge3J9DQpnZXRfc2VudGltZW50cygiYWZpbm4iKQ0KMQ0KDQpgYGANCg0KDQpgYGB7cn0NCmdldF9zZW50aW1lbnRzKCJiaW5nIikNCmBgYA0KDQpgYGB7cn0NCmdldF9zZW50aW1lbnRzKCJucmMiKQ0KYGBgDQoNCg0KU2VudGltZW50IGFuYWx5c2lzIHdpdGggaW5uZXIgam9pbg0KDQoNCmBgYHtyfQ0KbGlicmFyeShqYW5lYXVzdGVucikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQoNCnRpZHlfYm9va3MgPC0gYXVzdGVuX2Jvb2tzKCkgJT4lDQogIGdyb3VwX2J5KGJvb2spICU+JQ0KICBtdXRhdGUoDQogICAgbGluZW51bWJlciA9IHJvd19udW1iZXIoKSwNCiAgICBjaGFwdGVyID0gY3Vtc3VtKHN0cl9kZXRlY3QodGV4dCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZ2V4KCJeY2hhcHRlciBbXFxkaXZ4bGNdIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZV9jYXNlID0gVFJVRSkpKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KQ0KYGBgDQpgYGB7cn0NCm5yY19qb3kgPC0gZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSANCiAgZmlsdGVyKHNlbnRpbWVudCA9PSAiam95IikNCg0KdGlkeV9ib29rcyAlPiUNCiAgZmlsdGVyKGJvb2sgPT0gIkVtbWEiKSAlPiUNCiAgaW5uZXJfam9pbihucmNfam95KSAlPiUNCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHlyKQ0KDQpqYW5lX2F1c3Rlbl9zZW50aW1lbnQgPC0gdGlkeV9ib29rcyAlPiUNCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKSAlPiUNCiAgY291bnQoYm9vaywgaW5kZXggPSBsaW5lbnVtYmVyICUvJSA4MCwgc2VudGltZW50KSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNlbnRpbWVudCwgdmFsdWVzX2Zyb20gPSBuLCB2YWx1ZXNfZmlsbCA9IDApICU+JSANCiAgbXV0YXRlKHNlbnRpbWVudCA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpDQpgYGANClBsb3R0aW5nIHNlbnRpbWVudCBzY29yZXMNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCmdncGxvdChqYW5lX2F1c3Rlbl9zZW50aW1lbnQsIGFlcyhpbmRleCwgc2VudGltZW50LCBmaWxsID0gYm9vaykpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBmYWNldF93cmFwKH5ib29rLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWVfeCIpDQpgYGANCg0KDQpDb21wYXJpbmcgdGhlIHRocmVlIHNlbnRpbWVudCBkaWN0aW9uYXJpZXMNCg0KDQpgYGB7cn0NCnByaWRlX3ByZWp1ZGljZSA8LSB0aWR5X2Jvb2tzICU+JSANCiAgZmlsdGVyKGJvb2sgPT0gIlByaWRlICYgUHJlanVkaWNlIikNCg0KcHJpZGVfcHJlanVkaWNlDQoNCmBgYA0KDQoNCmBgYHtyfQ0KYWZpbm4gPC0gcHJpZGVfcHJlanVkaWNlICU+JSANCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYWZpbm4iKSkgJT4lIA0KICBncm91cF9ieShpbmRleCA9IGxpbmVudW1iZXIgJS8lIDgwKSAlPiUgDQogIHN1bW1hcmlzZShzZW50aW1lbnQgPSBzdW0odmFsdWUpKSAlPiUgDQogIG11dGF0ZShtZXRob2QgPSAiQUZJTk4iKQ0KDQpiaW5nX2FuZF9ucmMgPC0gYmluZF9yb3dzKA0KICBwcmlkZV9wcmVqdWRpY2UgJT4lIA0KICAgIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lDQogICAgbXV0YXRlKG1ldGhvZCA9ICJCaW5nIGV0IGFsLiIpLA0KICBwcmlkZV9wcmVqdWRpY2UgJT4lIA0KICAgIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSANCiAgICAgICAgICAgICAgICAgZmlsdGVyKHNlbnRpbWVudCAlaW4lIGMoInBvc2l0aXZlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuZWdhdGl2ZSIpKQ0KICAgICkgJT4lDQogICAgbXV0YXRlKG1ldGhvZCA9ICJOUkMiKSkgJT4lDQogIGNvdW50KG1ldGhvZCwgaW5kZXggPSBsaW5lbnVtYmVyICUvJSA4MCwgc2VudGltZW50KSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNlbnRpbWVudCwNCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBuLA0KICAgICAgICAgICAgICB2YWx1ZXNfZmlsbCA9IDApICU+JSANCiAgbXV0YXRlKHNlbnRpbWVudCA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpDQpgYGANCk5ldCBzZW50aW1lbnQgKHBvc2l0aXZlIC0gbmVnYXRpdmUpDQpgYGB7cn0NCmJpbmRfcm93cyhhZmlubiwgDQogICAgICAgICAgYmluZ19hbmRfbnJjKSAlPiUNCiAgZ2dwbG90KGFlcyhpbmRleCwgc2VudGltZW50LCBmaWxsID0gbWV0aG9kKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGZhY2V0X3dyYXAofm1ldGhvZCwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSANCiAgZmlsdGVyKHNlbnRpbWVudCAlaW4lIGMoInBvc2l0aXZlIiwgIm5lZ2F0aXZlIikpICU+JSANCiAgY291bnQoc2VudGltZW50KQ0KYGBgDQoNCmBgYHtyfQ0KZ2V0X3NlbnRpbWVudHMoImJpbmciKSAlPiUgDQogIGNvdW50KHNlbnRpbWVudCkNCg0KYGBgDQoNCg0KTW9zdCBjb21tb24gcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHdvcmRzLg0KDQpgYGB7cn0NCmJpbmdfd29yZF9jb3VudHMgPC0gdGlkeV9ib29rcyAlPiUNCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKSAlPiUNCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpiaW5nX3dvcmRfY291bnRzDQoNCmBgYA0KDQoNCmBgYHtyfQ0KYmluZ193b3JkX2NvdW50cyAlPiUNCiAgZ3JvdXBfYnkoc2VudGltZW50KSAlPiUNCiAgc2xpY2VfbWF4KG4sIG4gPSAxMCkgJT4lIA0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lDQogIGdncGxvdChhZXMobiwgd29yZCwgZmlsbCA9IHNlbnRpbWVudCkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBmYWNldF93cmFwKH5zZW50aW1lbnQsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIGxhYnMoeCA9ICJDb250cmlidXRpb24gdG8gc2VudGltZW50IiwNCiAgICAgICB5ID0gTlVMTCkNCmBgYA0KDQoNCmBgYHtyfQ0KY3VzdG9tX3N0b3Bfd29yZHMgPC0gYmluZF9yb3dzKHRpYmJsZSh3b3JkID0gYygibWlzcyIpLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxleGljb24gPSBjKCJjdXN0b20iKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0b3Bfd29yZHMpDQoNCmN1c3RvbV9zdG9wX3dvcmRzDQoNCmBgYA0KDQoNCldvcmRjbG91ZHMuDQoNCmBgYHtyfQ0KbGlicmFyeSh3b3JkY2xvdWQpDQoNCnRpZHlfYm9va3MgJT4lDQogIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUNCiAgY291bnQod29yZCkgJT4lDQogIHdpdGgod29yZGNsb3VkKHdvcmQsIG4sIG1heC53b3JkcyA9IDEwMCkpDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkocmVzaGFwZTIpDQoNCnRpZHlfYm9va3MgJT4lDQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lDQogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQ0KICBhY2FzdCh3b3JkIH4gc2VudGltZW50LCB2YWx1ZS52YXIgPSAibiIsIGZpbGwgPSAwKSAlPiUNCiAgY29tcGFyaXNvbi5jbG91ZChjb2xvcnMgPSBjKCJncmF5MjAiLCAiZ3JheTgwIiksDQogICAgICAgICAgICAgICAgICAgbWF4LndvcmRzID0gMTAwKQ0KYGBgDQoNCkxvb2tpbmcgYXQgdW5pdHMgYmV5b25kIGp1c3Qgd29yZHMuDQoNCmBgYHtyfQ0KcF9hbmRfcF9zZW50ZW5jZXMgPC0gdGliYmxlKHRleHQgPSBwcmlkZXByZWp1ZGljZSkgJT4lIA0KICB1bm5lc3RfdG9rZW5zKHNlbnRlbmNlLCB0ZXh0LCB0b2tlbiA9ICJzZW50ZW5jZXMiKQ0KYGBgDQoNCg0KYGBge3IgZmluZC1tYXgtdG90YWx9DQpwX2FuZF9wX3NlbnRlbmNlcyRzZW50ZW5jZVsyXQ0KDQpgYGANCg0KYGBge3J9DQphdXN0ZW5fY2hhcHRlcnMgPC0gYXVzdGVuX2Jvb2tzKCkgJT4lDQogIGdyb3VwX2J5KGJvb2spICU+JQ0KICB1bm5lc3RfdG9rZW5zKGNoYXB0ZXIsIHRleHQsIHRva2VuID0gInJlZ2V4IiwgDQogICAgICAgICAgICAgICAgcGF0dGVybiA9ICJDaGFwdGVyfENIQVBURVIgW1xcZElWWExDXSIpICU+JQ0KICB1bmdyb3VwKCkNCg0KYXVzdGVuX2NoYXB0ZXJzICU+JSANCiAgZ3JvdXBfYnkoYm9vaykgJT4lIA0KICBzdW1tYXJpc2UoY2hhcHRlcnMgPSBuKCkpDQoNCmBgYA0KDQpgYGB7cn0NCmJpbmduZWdhdGl2ZSA8LSBnZXRfc2VudGltZW50cygiYmluZyIpICU+JSANCiAgZmlsdGVyKHNlbnRpbWVudCA9PSAibmVnYXRpdmUiKQ0KDQp3b3JkY291bnRzIDwtIHRpZHlfYm9va3MgJT4lDQogIGdyb3VwX2J5KGJvb2ssIGNoYXB0ZXIpICU+JQ0KICBzdW1tYXJpemUod29yZHMgPSBuKCkpDQoNCnRpZHlfYm9va3MgJT4lDQogIHNlbWlfam9pbihiaW5nbmVnYXRpdmUpICU+JQ0KICBncm91cF9ieShib29rLCBjaGFwdGVyKSAlPiUNCiAgc3VtbWFyaXplKG5lZ2F0aXZld29yZHMgPSBuKCkpICU+JQ0KICBsZWZ0X2pvaW4od29yZGNvdW50cywgYnkgPSBjKCJib29rIiwgImNoYXB0ZXIiKSkgJT4lDQogIG11dGF0ZShyYXRpbyA9IG5lZ2F0aXZld29yZHMvd29yZHMpICU+JQ0KICBmaWx0ZXIoY2hhcHRlciAhPSAwKSAlPiUNCiAgc2xpY2VfbWF4KHJhdGlvLCBuID0gMSkgJT4lIA0KICB1bmdyb3VwKCkNCg0KYGBgDQoNCg0KIyMjIEV4ZXJjaXNlDQoNCg0KDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGV4dGRhdGEpDQojIExvYWQgdGhlIHRleHQgb2YgUGVyc3Vhc2lvbg0KcGVyc3Vhc2lvbl90ZXh0IDwtIGF1c3Rlbl9ib29rcygpICU+JQ0KICBmaWx0ZXIoYm9vayA9PSAiUGVyc3Vhc2lvbiIpICU+JQ0KICBwdWxsKHRleHQpDQoNCiMgVG9rZW5pemUgdGhlIHRleHQNCnBlcnN1YXNpb25fdGlkeSA8LSB0aWJibGUodGV4dCA9IHBlcnN1YXNpb25fdGV4dCkgJT4lDQogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkNCg0KIyBQZXJmb3JtIHNlbnRpbWVudCBhbmFseXNpcw0KbnJjX3NlbnRpbWVudHMgPC0gZ2V0X3NlbnRpbWVudHMoIm5yYyIpDQoNCnBlcnN1YXNpb25fc2VudGltZW50cyA8LSBwZXJzdWFzaW9uX3RpZHkgJT4lDQogIGlubmVyX2pvaW4obnJjX3NlbnRpbWVudHMpICU+JQ0KICBjb3VudCh3b3JkLCBzZW50aW1lbnQsIHNvcnQgPSBUUlVFKQ0KDQojIFZpc3VhbGl6ZSB0aGUgbW9zdCBjb21tb24gcG9zaXRpdmUgd29yZHMgaW4gUGVyc3Vhc2lvbg0KcGVyc3Vhc2lvbl9zZW50aW1lbnRzICU+JQ0KICBmaWx0ZXIoc2VudGltZW50ID09ICJwb3NpdGl2ZSIpICU+JQ0KICB0b3BfbigxMCwgbikgJT4lDQogIGdncGxvdChhZXMocmVvcmRlcih3b3JkLCBuKSwgbiwgZmlsbCA9IHdvcmQpKSArDQogIGdlb21fY29sKCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCBQb3NpdGl2ZSBXb3JkcyBpbiBQZXJzdWFzaW9uIikNCg0KIyBBbmFseXplIHNlbnRpbWVudCBjaGFuZ2VzIHRocm91Z2hvdXQgdGhlIG5vdmVsDQpwZXJzdWFzaW9uX3NlbnRpbWVudHMgPC0gcGVyc3Vhc2lvbl90aWR5ICU+JQ0KICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JQ0KICBjb3VudCh3b3JkLCBzZW50aW1lbnQsIHNvcnQgPSBUUlVFKQ0KDQpwZXJzdWFzaW9uX3NlbnRpbWVudHMgJT4lDQogIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lDQogIHNsaWNlX21heChuLCBuID0gMTApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lDQogIGdncGxvdChhZXMobiwgd29yZCwgZmlsbCA9IHNlbnRpbWVudCkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBmYWNldF93cmFwKH5zZW50aW1lbnQsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIGxhYnMoeCA9ICJDb250cmlidXRpb24gdG8gc2VudGltZW50IiwgeSA9IE5VTEwpDQpgYGANCg0KIyMjIENvbmNsdXNpb24NCg0KVGhlIHNlbnRpbWVudCBhbmFseXNpcyBvZiAiUGVyc3Vhc2lvbiIgaGlnaGxpZ2h0cyBwcmV2YWxlbnQgcG9zaXRpdmUgd29yZHMgc3VjaCBhcyAibG92ZSIgYW5kICJoYXBweSwiIGNvbnRyaWJ1dGluZyB0byB0aGUgYm9vaydzIG92ZXJhbGwgcG9zaXRpdmUgc2VudGltZW50LiBUaGlzIGFuYWx5c2lzIG9mZmVycyBpbnNpZ2h0cyBpbnRvIHRoZSBub3ZlbCdzIGVtb3Rpb25hbCB0b25lIGFuZCBob3cgaXQgY2hhbmdlcyB0aHJvdWdob3V0IHRoZSBzdG9yeS4NCg0KIyMjIFJlZmVyZW5jZXMNCg0KLSBTaWxnZSwgSi4sICYgUm9iaW5zb24sIEQuICgyMDIyKS4gKlRleHQgTWluaW5nIHdpdGggUjogQSBUaWR5IEFwcHJvYWNoKi4gW2h0dHBzOi8vd3d3LnRpZHl0ZXh0bWluaW5nLmNvbS9zZW50aW1lbnQuaHRtbF0NCg0K