library(rtweet)
library(tidyverse)
library(tidytext)
library(wordcloud2)
library(DT)
library(plotly)
- The following shows the process for unnesting the words from usatoday tweets as well as removing stop words and weird web words from the tweets. A table and wordcloud are created to show the top tweets.
news_tweets <- get_timeline("usatoday", n = 5000)
news_words <- news_tweets %>%
unnest_tokens(word, text) %>%
select(screen_name, word)
news_words %>%
anti_join(stop_words) %>%
count(word, sort = T)
Joining, by = "word"
news_words %>%
anti_join(stop_words) %>%
filter(!word == "t.co") %>%
filter(!word == "https") %>%
count(word, sort = T)
Joining, by = "word"
news_words %>%
anti_join(stop_words) %>%
filter(!word == "t.co") %>%
filter(!word == "https") %>%
count(word, sort = T) %>%
top_n(100) %>%
wordcloud2(size = .8)
Joining, by = "word"
Selecting by n
The first datatable shows the top words in usatoday’s tweets with only stop words being removed. The datatable shows that the top two words are weird web words. Because of that, the second data table was made. The second data table shows the top words for usatoday’s tweets with both stop words and weird web words removed. Following is a wordcloud of the top words. As one can see from the datatable and graph, some of the most used words in usatoday’s tweets include “Ukraine”, “Russian”, “president”, and so on. This makes sense considering the Russian invasion of Ukraine is being highly covered by news outlets at the current moment.
- The following shows the process of sentiment analysis of usatoday’s tweets. A sentiment dictionary called bing was used to do this.
bing <- get_sentiments("bing")
bing
news_words %>%
inner_join(bing) %>%
count(word, sentiment, sort = TRUE)
Joining, by = "word"
news_words %>%
inner_join(bing) %>%
count(word, sentiment, sort = TRUE) %>%
filter(!word == "supreme")
Joining, by = "word"
news_words %>%
inner_join(bing) %>%
count(word, sentiment, sort = TRUE) %>%
filter(!word == "supreme") %>%
group_by(sentiment) %>%
top_n(10) %>%
ungroup() %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(vars(sentiment), scales = "free") +
labs(y = "News headlines: Words that contribute the most to each sentiment",
x = NULL) +
coord_flip() +
theme_minimal()
Joining, by = "word"
Selecting by n

The first datatable in the sentiment analysis shows words that the sentiment dictionary includes and whether they are positive or negative. The second datatable shows sentiment words that were found in usatoday’s tweets and whether they are postivie or negative. The third datatable shows the same thing, however the word supreme was removed. The word supreme was removed because bing considers it a positive word, however in the news it refers to the new judge elected to the supreme court. Finally, the graph shows sentiment words in usatoday’s tweets with the necessary word removed.
- The following shows the process of sentiment analysis of usatoday’s tweets. A sentiment dictionary called nrc was used to do this.
nrc <- get_sentiments("nrc")
Do you want to download:
Name: NRC Word-Emotion Association Lexicon
URL: http://saifmohammad.com/WebPages/lexicons.html
License: License required for commercial use. Please contact Saif M. Mohammad (saif.mohammad@nrc-cnrc.gc.ca).
Size: 22.8 MB (cleaned 424 KB)
Download mechanism: http
Citation info:
This dataset was published in Saif M. Mohammad and Peter Turney. (2013), ``Crowdsourcing a Word-Emotion Association Lexicon.'' Computational Intelligence, 29(3): 436-465.
article{mohammad13,
author = {Mohammad, Saif M. and Turney, Peter D.},
title = {Crowdsourcing a Word-Emotion Association Lexicon},
journal = {Computational Intelligence},
volume = {29},
number = {3},
pages = {436-465},
doi = {10.1111/j.1467-8640.2012.00460.x},
url = {https://onlinelibrary.wiley.com/doi/abs/10.1111/j.1467-8640.2012.00460.x},
eprint = {https://onlinelibrary.wiley.com/doi/pdf/10.1111/j.1467-8640.2012.00460.x},
year = {2013}
}
If you use this lexicon, then please cite it.
1: Yes
2: No
library(dplyr)
Enter an item from the menu, or 0 to exit
0
trying URL 'http://saifmohammad.com/WebDocs/NRC-Emotion-Lexicon.zip'
Content type 'application/zip' length 24436570 bytes (23.3 MB)
==================================================
downloaded 23.3 MB
nrc
news_words %>%
inner_join(nrc) %>%
count(word, sentiment, sort = TRUE)
Joining, by = "word"
news_words %>%
inner_join(nrc) %>%
count(word, sentiment, sort = TRUE) %>%
group_by(sentiment) %>%
top_n(10) %>%
ungroup() %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(vars(sentiment), scales = "free") +
labs(y = "News headlines: Words that contribute the most to each sentiment",
x = NULL) +
coord_flip() +
theme_minimal()
Joining, by = "word"
Selecting by n

The nrc dictionary is visibly different from the bing dictionary. The nrc dictionary includes more than just positive and negative. For example, it includes anger, joy, disgust, fear, and so on. The first datatable shows the nrc dictionary. The second datatable shows words pulled from usatoday’s tweets and categorizes them according to the nrc dictionary. Finally, the graph shows the different sentiments the nrc package includes along with words pulled from usatoday’s tweets that fit into each sentiment. I want to note that I did not delete any words like I did with the bing dictionary. My reasoning for this was because of how much broader the nrc dictionary is.
- The following shows the process of unnesting usatoday’s tweets into bigrams, removing the stop words and errors, and creating a datatable and wordcloud of usatoday’s most common bigrams.
news_tweets %>%
select(text) %>% # this selects just the text of the tweets
unnest_tokens(words, text, token = "ngrams", n = 2)
NA
news_tweets %>%
select(text) %>% # this selects just the text of the tweets
unnest_tokens(words, text, token = "ngrams", n = 2) %>%
separate(words, c("word1", "word2"), sep = " ") %>% # separate them temporarily
filter(!word1 %in% stop_words$word) %>% # remove if first word is a stop word
filter(!word2 %in% stop_words$word) %>% # remove if second word is a stop word
unite(words, word1, word2, sep = " ") # put them back together
NA
remove_words = c("https", "t.co", "kbqavrhlqa", "dzdcdaghgr", "o7nt7jav5t","9hfrnmsh6g", "m0v7cthfdq", "qplwnsfyzk", "19xyv9k77e", "nnnmetphih", "we45nhxa4m", "bbu9ol33be", "actjxv5rle", "9ktxm7bn8w", "xia1xtxqh5", "xpyndwady2", "wdjc9j6oba", "tcypxlzj1h", "fl0dr0i8rd", "lnhbeh4qqz", "aqjcmxq29b", "cm48lqfhzr", "fhht8fqip6", "lebmmzrdcr", "4i6jhw1tgf", "rjnis1ehqw", "lunp8vilnj", "kumv8k3rpt", "qzi3llehc0", "rqlftfzdh5", "ngnztytziv", "slzkjtr12p")
news_tweets %>%
select(text) %>%
unnest_tokens(words, text, token = "ngrams", n = 2) %>%
separate(words, c("word1", "word2"), sep = " ") %>% # separate them temporarily
filter(!word1 %in% stop_words$word) %>% # remove if first word is a stop word
filter(!word2 %in% stop_words$word) %>% # remove if second word is a stop word
filter(!word1 %in% remove_words) %>% # these two lines remove our remove_words
filter(!word2 %in% remove_words) %>%
unite(words, word1, word2, sep = " ") -> news_bigrams2 # put them back together
news_bigrams2 %>%
count(words, sort = T)
news_bigrams2 %>%
count(words, sort = T) %>%
top_n(100) %>%
wordcloud2(size = .5)
Selecting by n
This is a lengthy process but shows the process of creating bigrams of usatoday’s tweets. The first datatable is usatoday’s tweets unnested. The second datatable is usatoday’s bigrams without stop words. The third datatable is ussatoday’s bigrams with no stop words or weird web words. Finally, the wordcloud is the third datatable in more of a picture form. It contains the most common bigrams of usatoday’s tweets. As the datatable and wordcloud show, some of the most common bigrams include “Covid 19”, “Ketanji Brown”, and “supreme court”. These make sense if we take into account our current events.
- The following process will show the most common words that follow the words “Russia” and “Ukraine”.
first_word <- c("russia", "ukraine") # these need to be lowercase
news_bigrams2 %>%
count(words, sort = TRUE) %>%
separate(words, c("word1", "word2"), sep = " ") %>% # separate the two words
filter(word1 %in% first_word) %>% # find first words from our list
count(word1, word2, wt = n, sort = TRUE)
first_word <- c("russia", "ukraine") # these need to be lowercase
news_bigrams2 %>%
count(words, sort = TRUE) %>%
separate(words, c("word1", "word2"), sep = " ") %>% # separate the two words
filter(word1 %in% first_word) %>% # find first words from our list
count(word1, word2, wt = n, sort = TRUE) %>%
mutate(word2 = factor(word2, levels = rev(unique(word2)))) %>% # put the words in order
group_by(word1) %>%
top_n(5) %>%
ggplot(aes(word2, n, fill = word1)) + #
scale_fill_viridis_d() + # set the color palette
geom_col(show.legend = FALSE) +
labs(x = NULL, y = NULL, title = "Word following:") +
facet_wrap(~word1, scales = "free") +
coord_flip()
Selecting by n

Both the datatable and the graph show the most common words that follow the words “Russia” and “Ukraine”. Some common words that follow the word “Russia” include “invades”, “continues”, and “invaded”. The words that follow the word “Ukraine” are a little trickier because they involve emojis. For example, the most common word that follows “Ukraine” is the number 2 emoji. The second most common word that follows “Ukraine” is “president” and then “Russia”.
LS0tCnRpdGxlOiAiVGV4dCBBbmFseXNpcyBvZiBUd2VldHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQpsaWJyYXJ5KHJ0d2VldCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkod29yZGNsb3VkMikKbGlicmFyeShEVCkKbGlicmFyeShwbG90bHkpCmBgYAoKMS4gVGhlIGZvbGxvd2luZyBzaG93cyB0aGUgcHJvY2VzcyBmb3IgdW5uZXN0aW5nIHRoZSB3b3JkcyBmcm9tIHVzYXRvZGF5IHR3ZWV0cyBhcyB3ZWxsIGFzIHJlbW92aW5nIHN0b3Agd29yZHMgYW5kIHdlaXJkIHdlYiB3b3JkcyBmcm9tIHRoZSB0d2VldHMuIEEgdGFibGUgYW5kIHdvcmRjbG91ZCBhcmUgY3JlYXRlZCB0byBzaG93IHRoZSB0b3AgdHdlZXRzLiAKYGBge3J9Cm5ld3NfdHdlZXRzIDwtIGdldF90aW1lbGluZSgidXNhdG9kYXkiLCBuID0gNTAwMCkKCmBgYApgYGB7cn0KbmV3c193b3JkcyA8LSBuZXdzX3R3ZWV0cyAlPiUgCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KSAlPiUgCiAgc2VsZWN0KHNjcmVlbl9uYW1lLCB3b3JkKQoKYGBgCmBgYHtyfQpuZXdzX3dvcmRzICU+JSAKICBhbnRpX2pvaW4oc3RvcF93b3JkcykgJT4lIAogIGNvdW50KHdvcmQsIHNvcnQgPSBUKSAKYGBgCmBgYHtyfQpuZXdzX3dvcmRzICU+JSAKICBhbnRpX2pvaW4oc3RvcF93b3JkcykgJT4lIAogIGZpbHRlcighd29yZCA9PSAidC5jbyIpICU+JQogIGZpbHRlcighd29yZCA9PSAiaHR0cHMiKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVCkKYGBgCmBgYHtyfQpuZXdzX3dvcmRzICU+JSAKICBhbnRpX2pvaW4oc3RvcF93b3JkcykgJT4lIAogIGZpbHRlcighd29yZCA9PSAidC5jbyIpICU+JQogIGZpbHRlcighd29yZCA9PSAiaHR0cHMiKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVCkgJT4lCiAgdG9wX24oMTAwKSAlPiUKICB3b3JkY2xvdWQyKHNpemUgPSAuOCkKYGBgClRoZSBmaXJzdCBkYXRhdGFibGUgc2hvd3MgdGhlIHRvcCB3b3JkcyBpbiB1c2F0b2RheSdzIHR3ZWV0cyB3aXRoIG9ubHkgc3RvcCB3b3JkcyBiZWluZyByZW1vdmVkLiBUaGUgZGF0YXRhYmxlIHNob3dzIHRoYXQgdGhlIHRvcCB0d28gd29yZHMgYXJlIHdlaXJkIHdlYiB3b3Jkcy4gQmVjYXVzZSBvZiB0aGF0LCB0aGUgc2Vjb25kIGRhdGEgdGFibGUgd2FzIG1hZGUuIFRoZSBzZWNvbmQgZGF0YSB0YWJsZSBzaG93cyB0aGUgdG9wIHdvcmRzIGZvciB1c2F0b2RheSdzIHR3ZWV0cyB3aXRoIGJvdGggc3RvcCB3b3JkcyBhbmQgd2VpcmQgd2ViIHdvcmRzIHJlbW92ZWQuIEZvbGxvd2luZyBpcyBhIHdvcmRjbG91ZCBvZiB0aGUgdG9wIHdvcmRzLiBBcyBvbmUgY2FuIHNlZSBmcm9tIHRoZSBkYXRhdGFibGUgYW5kIGdyYXBoLCBzb21lIG9mIHRoZSBtb3N0IHVzZWQgd29yZHMgaW4gdXNhdG9kYXkncyB0d2VldHMgaW5jbHVkZSAiVWtyYWluZSIsICJSdXNzaWFuIiwgInByZXNpZGVudCIsIGFuZCBzbyBvbi4gVGhpcyBtYWtlcyBzZW5zZSBjb25zaWRlcmluZyB0aGUgUnVzc2lhbiBpbnZhc2lvbiBvZiBVa3JhaW5lIGlzIGJlaW5nIGhpZ2hseSBjb3ZlcmVkIGJ5IG5ld3Mgb3V0bGV0cyBhdCB0aGUgY3VycmVudCBtb21lbnQuIAoKMi4gVGhlIGZvbGxvd2luZyBzaG93cyB0aGUgcHJvY2VzcyBvZiBzZW50aW1lbnQgYW5hbHlzaXMgb2YgdXNhdG9kYXkncyB0d2VldHMuIEEgc2VudGltZW50IGRpY3Rpb25hcnkgY2FsbGVkIGJpbmcgd2FzIHVzZWQgdG8gZG8gdGhpcy4gCmBgYHtyfQpiaW5nIDwtIGdldF9zZW50aW1lbnRzKCJiaW5nIikKYmluZwpgYGAKYGBge3J9Cm5ld3Nfd29yZHMgJT4lIAogIGlubmVyX2pvaW4oYmluZykgJT4lIAogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpCgpgYGAKYGBge3J9Cm5ld3Nfd29yZHMgJT4lIAogIGlubmVyX2pvaW4oYmluZykgJT4lIAogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcighd29yZCA9PSAic3VwcmVtZSIpCgpgYGAKYGBge3J9Cm5ld3Nfd29yZHMgJT4lIAogIGlubmVyX2pvaW4oYmluZykgJT4lIAogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcighd29yZCA9PSAic3VwcmVtZSIpICU+JQogIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lCiAgdG9wX24oMTApICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQogIGdncGxvdChhZXMod29yZCwgbiwgZmlsbCA9IHNlbnRpbWVudCkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh2YXJzKHNlbnRpbWVudCksIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnMoeSA9ICJOZXdzIGhlYWRsaW5lczogV29yZHMgdGhhdCBjb250cmlidXRlIHRoZSBtb3N0IHRvIGVhY2ggc2VudGltZW50IiwKICAgICAgIHggPSBOVUxMKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgClRoZSBmaXJzdCBkYXRhdGFibGUgaW4gdGhlIHNlbnRpbWVudCBhbmFseXNpcyBzaG93cyB3b3JkcyB0aGF0IHRoZSBzZW50aW1lbnQgZGljdGlvbmFyeSBpbmNsdWRlcyBhbmQgd2hldGhlciB0aGV5IGFyZSBwb3NpdGl2ZSBvciBuZWdhdGl2ZS4gVGhlIHNlY29uZCBkYXRhdGFibGUgc2hvd3Mgc2VudGltZW50IHdvcmRzIHRoYXQgd2VyZSBmb3VuZCBpbiB1c2F0b2RheSdzIHR3ZWV0cyBhbmQgd2hldGhlciB0aGV5IGFyZSBwb3N0aXZpZSBvciBuZWdhdGl2ZS4gVGhlIHRoaXJkIGRhdGF0YWJsZSBzaG93cyB0aGUgc2FtZSB0aGluZywgaG93ZXZlciB0aGUgd29yZCBzdXByZW1lIHdhcyByZW1vdmVkLiBUaGUgd29yZCBzdXByZW1lIHdhcyByZW1vdmVkIGJlY2F1c2UgYmluZyBjb25zaWRlcnMgaXQgYSBwb3NpdGl2ZSB3b3JkLCBob3dldmVyIGluIHRoZSBuZXdzIGl0IHJlZmVycyB0byB0aGUgbmV3IGp1ZGdlIGVsZWN0ZWQgdG8gdGhlIHN1cHJlbWUgY291cnQuIEZpbmFsbHksIHRoZSBncmFwaCBzaG93cyBzZW50aW1lbnQgd29yZHMgaW4gdXNhdG9kYXkncyB0d2VldHMgd2l0aCB0aGUgbmVjZXNzYXJ5IHdvcmQgcmVtb3ZlZC4gCgozLiBUaGUgZm9sbG93aW5nIHNob3dzIHRoZSBwcm9jZXNzIG9mIHNlbnRpbWVudCBhbmFseXNpcyBvZiB1c2F0b2RheSdzIHR3ZWV0cy4gQSBzZW50aW1lbnQgZGljdGlvbmFyeSBjYWxsZWQgbnJjIHdhcyB1c2VkIHRvIGRvIHRoaXMuCmBgYHtyfQpucmMgPC0gZ2V0X3NlbnRpbWVudHMoIm5yYyIpCm5yYwpgYGAKYGBge3J9Cm5ld3Nfd29yZHMgJT4lIAogIGlubmVyX2pvaW4obnJjKSAlPiUgCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkKCmBgYApgYGB7cn0KbmV3c193b3JkcyAlPiUgCiAgaW5uZXJfam9pbihucmMpICU+JSAKICBjb3VudCh3b3JkLCBzZW50aW1lbnQsIHNvcnQgPSBUUlVFKSAlPiUKICBncm91cF9ieShzZW50aW1lbnQpICU+JQogIHRvcF9uKDEwKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAodmFycyhzZW50aW1lbnQpLCBzY2FsZXMgPSAiZnJlZSIpICsKICBsYWJzKHkgPSAiTmV3cyBoZWFkbGluZXM6IFdvcmRzIHRoYXQgY29udHJpYnV0ZSB0aGUgbW9zdCB0byBlYWNoIHNlbnRpbWVudCIsCiAgICAgICB4ID0gTlVMTCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYApUaGUgbnJjIGRpY3Rpb25hcnkgaXMgdmlzaWJseSBkaWZmZXJlbnQgZnJvbSB0aGUgYmluZyBkaWN0aW9uYXJ5LiBUaGUgbnJjIGRpY3Rpb25hcnkgaW5jbHVkZXMgbW9yZSB0aGFuIGp1c3QgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlLiBGb3IgZXhhbXBsZSwgaXQgaW5jbHVkZXMgYW5nZXIsIGpveSwgZGlzZ3VzdCwgZmVhciwgYW5kIHNvIG9uLiBUaGUgZmlyc3QgZGF0YXRhYmxlIHNob3dzIHRoZSBucmMgZGljdGlvbmFyeS4gVGhlIHNlY29uZCBkYXRhdGFibGUgc2hvd3Mgd29yZHMgcHVsbGVkIGZyb20gdXNhdG9kYXkncyB0d2VldHMgYW5kIGNhdGVnb3JpemVzIHRoZW0gYWNjb3JkaW5nIHRvIHRoZSBucmMgZGljdGlvbmFyeS4gRmluYWxseSwgdGhlIGdyYXBoIHNob3dzIHRoZSBkaWZmZXJlbnQgc2VudGltZW50cyB0aGUgbnJjIHBhY2thZ2UgaW5jbHVkZXMgYWxvbmcgd2l0aCB3b3JkcyBwdWxsZWQgZnJvbSB1c2F0b2RheSdzIHR3ZWV0cyB0aGF0IGZpdCBpbnRvIGVhY2ggc2VudGltZW50LiBJIHdhbnQgdG8gbm90ZSB0aGF0IEkgZGlkIG5vdCBkZWxldGUgYW55IHdvcmRzIGxpa2UgSSBkaWQgd2l0aCB0aGUgYmluZyBkaWN0aW9uYXJ5LiBNeSByZWFzb25pbmcgZm9yIHRoaXMgd2FzIGJlY2F1c2Ugb2YgaG93IG11Y2ggYnJvYWRlciB0aGUgbnJjIGRpY3Rpb25hcnkgaXMuIAoKCjQuIFRoZSBmb2xsb3dpbmcgc2hvd3MgdGhlIHByb2Nlc3Mgb2YgdW5uZXN0aW5nIHVzYXRvZGF5J3MgdHdlZXRzIGludG8gYmlncmFtcywgcmVtb3ZpbmcgdGhlIHN0b3Agd29yZHMgYW5kIGVycm9ycywgYW5kIGNyZWF0aW5nIGEgZGF0YXRhYmxlIGFuZCB3b3JkY2xvdWQgb2YgdXNhdG9kYXkncyBtb3N0IGNvbW1vbiBiaWdyYW1zLiAKYGBge3J9CgpuZXdzX3R3ZWV0cyAlPiUKICBzZWxlY3QodGV4dCkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhpcyBzZWxlY3RzIGp1c3QgdGhlIHRleHQgb2YgdGhlIHR3ZWV0cwogIHVubmVzdF90b2tlbnMod29yZHMsIHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAKCmBgYApgYGB7cn0KbmV3c190d2VldHMgJT4lCiAgc2VsZWN0KHRleHQpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoaXMgc2VsZWN0cyBqdXN0IHRoZSB0ZXh0IG9mIHRoZSB0d2VldHMKICB1bm5lc3RfdG9rZW5zKHdvcmRzLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lIAogIHNlcGFyYXRlKHdvcmRzLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAgICAgICAgICAjIHNlcGFyYXRlIHRoZW0gdGVtcG9yYXJpbHkKICBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUgICAgICAgICAgICAgICAgICAgICAgIyByZW1vdmUgaWYgZmlyc3Qgd29yZCBpcyBhIHN0b3Agd29yZAogIGZpbHRlcighd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JSAgICAgICAgICAgICAgICAgICAgICAjIHJlbW92ZSBpZiBzZWNvbmQgd29yZCBpcyBhIHN0b3Agd29yZCAgIAogIHVuaXRlKHdvcmRzLCB3b3JkMSwgd29yZDIsIHNlcCA9ICIgIikgICAgICAgICAgICAgICAgICAgICAgICAjIHB1dCB0aGVtIGJhY2sgdG9nZXRoZXIKCmBgYApgYGB7cn0KcmVtb3ZlX3dvcmRzID0gYygiaHR0cHMiLCAidC5jbyIsICJrYnFhdnJobHFhIiwgImR6ZGNkYWdoZ3IiLCAibzdudDdqYXY1dCIsIjloZnJubXNoNmciLCAibTB2N2N0aGZkcSIsICJxcGx3bnNmeXprIiwgIjE5eHl2OWs3N2UiLCAibm5ubWV0cGhpaCIsICJ3ZTQ1bmh4YTRtIiwgImJidTlvbDMzYmUiLCAiYWN0anh2NXJsZSIsICI5a3R4bTdibjh3IiwgInhpYTF4dHhxaDUiLCAieHB5bmR3YWR5MiIsICJ3ZGpjOWo2b2JhIiwgInRjeXB4bHpqMWgiLCAiZmwwZHIwaThyZCIsICJsbmhiZWg0cXF6IiwgImFxamNteHEyOWIiLCAiY200OGxxZmh6ciIsICJmaGh0OGZxaXA2IiwgImxlYm1tenJkY3IiLCAiNGk2amh3MXRnZiIsICJyam5pczFlaHF3IiwgImx1bnA4dmlsbmoiLCAia3VtdjhrM3JwdCIsICJxemkzbGxlaGMwIiwgInJxbGZ0ZnpkaDUiLCAibmduenR5dHppdiIsICJzbHpranRyMTJwIikKCm5ld3NfdHdlZXRzICU+JQogIHNlbGVjdCh0ZXh0KSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgdW5uZXN0X3Rva2Vucyh3b3JkcywgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JSAKICBzZXBhcmF0ZSh3b3JkcywgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgICAgICAgICAgIyBzZXBhcmF0ZSB0aGVtIHRlbXBvcmFyaWx5CiAgZmlsdGVyKCF3b3JkMSAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lICAgICAgICAgICAgICAgICAgICAgICMgcmVtb3ZlIGlmIGZpcnN0IHdvcmQgaXMgYSBzdG9wIHdvcmQKICBmaWx0ZXIoIXdvcmQyICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUgICAgICAgICAgICAgICAgICAgICAgIyByZW1vdmUgaWYgc2Vjb25kIHdvcmQgaXMgYSBzdG9wIHdvcmQgICAKICBmaWx0ZXIoIXdvcmQxICVpbiUgcmVtb3ZlX3dvcmRzKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGVzZSB0d28gbGluZXMgcmVtb3ZlIG91ciByZW1vdmVfd29yZHMKICBmaWx0ZXIoIXdvcmQyICVpbiUgcmVtb3ZlX3dvcmRzKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgCiAgdW5pdGUod29yZHMsIHdvcmQxLCB3b3JkMiwgc2VwID0gIiAiKSAtPiBuZXdzX2JpZ3JhbXMyICAgICAgICAgICAgICAgICAgICAjIHB1dCB0aGVtIGJhY2sgdG9nZXRoZXIKCmBgYAoKYGBge3J9Cm5ld3NfYmlncmFtczIgJT4lCiAgY291bnQod29yZHMsIHNvcnQgPSBUKQpgYGAKYGBge3J9Cm5ld3NfYmlncmFtczIgJT4lCiAgY291bnQod29yZHMsIHNvcnQgPSBUKSAlPiUKICB0b3BfbigxMDApICU+JQogIHdvcmRjbG91ZDIoc2l6ZSA9IC41KQpgYGAKVGhpcyBpcyBhIGxlbmd0aHkgcHJvY2VzcyBidXQgc2hvd3MgdGhlIHByb2Nlc3Mgb2YgY3JlYXRpbmcgYmlncmFtcyBvZiB1c2F0b2RheSdzIHR3ZWV0cy4gVGhlIGZpcnN0IGRhdGF0YWJsZSBpcyB1c2F0b2RheSdzIHR3ZWV0cyB1bm5lc3RlZC4gVGhlIHNlY29uZCBkYXRhdGFibGUgaXMgdXNhdG9kYXkncyBiaWdyYW1zIHdpdGhvdXQgc3RvcCB3b3Jkcy4gVGhlIHRoaXJkIGRhdGF0YWJsZSBpcyB1c3NhdG9kYXkncyBiaWdyYW1zIHdpdGggbm8gc3RvcCB3b3JkcyBvciB3ZWlyZCB3ZWIgd29yZHMuIEZpbmFsbHksIHRoZSB3b3JkY2xvdWQgaXMgdGhlIHRoaXJkIGRhdGF0YWJsZSBpbiBtb3JlIG9mIGEgcGljdHVyZSBmb3JtLiBJdCBjb250YWlucyB0aGUgbW9zdCBjb21tb24gYmlncmFtcyBvZiB1c2F0b2RheSdzIHR3ZWV0cy4gQXMgdGhlIGRhdGF0YWJsZSBhbmQgd29yZGNsb3VkIHNob3csIHNvbWUgb2YgdGhlIG1vc3QgY29tbW9uIGJpZ3JhbXMgaW5jbHVkZSAiQ292aWQgMTkiLCAiS2V0YW5qaSBCcm93biIsIGFuZCAic3VwcmVtZSBjb3VydCIuIFRoZXNlIG1ha2Ugc2Vuc2UgaWYgd2UgdGFrZSBpbnRvIGFjY291bnQgb3VyIGN1cnJlbnQgZXZlbnRzLiAKCjUuIFRoZSBmb2xsb3dpbmcgcHJvY2VzcyB3aWxsIHNob3cgdGhlIG1vc3QgY29tbW9uIHdvcmRzIHRoYXQgZm9sbG93IHRoZSB3b3JkcyAiUnVzc2lhIiBhbmQgIlVrcmFpbmUiLiAKYGBge3J9CmZpcnN0X3dvcmQgPC0gYygicnVzc2lhIiwgInVrcmFpbmUiKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZXNlIG5lZWQgdG8gYmUgbG93ZXJjYXNlCgpuZXdzX2JpZ3JhbXMyICU+JSAgICAgICAgICAgICAKICBjb3VudCh3b3Jkcywgc29ydCA9IFRSVUUpICU+JQogIHNlcGFyYXRlKHdvcmRzLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAgICAgICAjIHNlcGFyYXRlIHRoZSB0d28gd29yZHMKICBmaWx0ZXIod29yZDEgJWluJSBmaXJzdF93b3JkKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICMgZmluZCBmaXJzdCB3b3JkcyBmcm9tIG91ciBsaXN0CiAgY291bnQod29yZDEsIHdvcmQyLCB3dCA9IG4sIHNvcnQgPSBUUlVFKQpgYGAKYGBge3J9CgpmaXJzdF93b3JkIDwtIGMoInJ1c3NpYSIsICJ1a3JhaW5lIikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGVzZSBuZWVkIHRvIGJlIGxvd2VyY2FzZQoKbmV3c19iaWdyYW1zMiAlPiUgICAgICAgICAgICAgCiAgY291bnQod29yZHMsIHNvcnQgPSBUUlVFKSAlPiUKICBzZXBhcmF0ZSh3b3JkcywgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgICAgICAgIyBzZXBhcmF0ZSB0aGUgdHdvIHdvcmRzCiAgZmlsdGVyKHdvcmQxICVpbiUgZmlyc3Rfd29yZCkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZpbmQgZmlyc3Qgd29yZHMgZnJvbSBvdXIgbGlzdAogIGNvdW50KHdvcmQxLCB3b3JkMiwgd3QgPSBuLCBzb3J0ID0gVFJVRSkgJT4lCiAgbXV0YXRlKHdvcmQyID0gZmFjdG9yKHdvcmQyLCBsZXZlbHMgPSByZXYodW5pcXVlKHdvcmQyKSkpKSAlPiUgICAgICMgcHV0IHRoZSB3b3JkcyBpbiBvcmRlcgogIGdyb3VwX2J5KHdvcmQxKSAlPiUgCiAgdG9wX24oNSkgJT4lIAogIGdncGxvdChhZXMod29yZDIsIG4sIGZpbGwgPSB3b3JkMSkpICsgICAgICAgICAgICAgICAgICAgICAgICAgICMKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzZXQgdGhlIGNvbG9yIHBhbGV0dGUKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwsIHRpdGxlID0gIldvcmQgZm9sbG93aW5nOiIpICsKICBmYWNldF93cmFwKH53b3JkMSwgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCgpgYGAKQm90aCB0aGUgZGF0YXRhYmxlIGFuZCB0aGUgZ3JhcGggc2hvdyB0aGUgbW9zdCBjb21tb24gd29yZHMgdGhhdCBmb2xsb3cgdGhlIHdvcmRzICJSdXNzaWEiIGFuZCAiVWtyYWluZSIuIFNvbWUgY29tbW9uIHdvcmRzIHRoYXQgZm9sbG93IHRoZSB3b3JkICJSdXNzaWEiIGluY2x1ZGUgImludmFkZXMiLCAiY29udGludWVzIiwgYW5kICJpbnZhZGVkIi4gVGhlIHdvcmRzIHRoYXQgZm9sbG93IHRoZSB3b3JkICJVa3JhaW5lIiBhcmUgYSBsaXR0bGUgdHJpY2tpZXIgYmVjYXVzZSB0aGV5IGludm9sdmUgZW1vamlzLiBGb3IgZXhhbXBsZSwgdGhlIG1vc3QgY29tbW9uIHdvcmQgdGhhdCBmb2xsb3dzICJVa3JhaW5lIiBpcyB0aGUgbnVtYmVyIDIgZW1vamkuIFRoZSBzZWNvbmQgbW9zdCBjb21tb24gd29yZCB0aGF0IGZvbGxvd3MgIlVrcmFpbmUiIGlzICJwcmVzaWRlbnQiIGFuZCB0aGVuICJSdXNzaWEiLiAKCgoKCg==