Description:

An analysis of tweets under the #metoo hashtag, collected between December 1 and December 31, 2017.

#install.packages("twitteR")
#install.packages("ROAuth")
#install.packages("httr")
library(twitteR)
library(ROAuth)
library(httr)
library(plyr)
library(ggplot2)
library(ggthemes)
library(dplyr)
library(magrittr)
library(tidytext)
library(wordcloud)
library(tidyr)
library(reshape2)
library(devtools)
library(widyr)
library(igraph)
library(ggraph)
library(stringr)
library(readr)

Scrape Twitter for Tweets with #metoo hashtag

Define API Keys & Authenticate through Twitter app
api_key <- "*******"
api_secret <- "*******"
access_token <- "*******"
access_token_secret <- "*******"

setup_twitter_oauth(api_key, api_secret, access_token, access_token_secret)
Download tweets containing #metoo Hashtag on Nov 13, 2015. Save as .csv.
metoo_tweets <- twListToDF(searchTwitter("#metoo", 
                            n=20000, 
                            lang="en",
                            since="2017-12-01",
                            until="2017-12-31"))

#                            subset(select=c("id","text","created",
#                                            "screenName","retweetCount",
#                                            "isRetweet","retweeted"))



#Save metoo_tweets to .csv
#write.csv(metoo_tweets,"/Users/Brett/Desktop/metoo/metoo_12_23_17.csv")
Load Data
metoo_tweets<-read_csv("/Users/Brett/Library/Mobile Documents/com~apple~CloudDocs/All Files/Education/QC Teaching/Tweet Archive/metoo/metoo_tweets_dec2017.csv")
#Take a random sample of the 300K tweets in this dataset. 
metoo_tweets<-sample_n(metoo_tweets, 50000)
Remove Emoji Characters
metoo_tweets$text <- sapply(metoo_tweets$text, function(row) iconv(row, "latin1", "ASCII", sub=""))
Separate each individual word into a row of its own.
        metootext <- metoo_tweets %>%
                     unnest_tokens(word, text)
Create a list of customs stop words.
data(stop_words)
custom_stop_words <- bind_rows(
                    data_frame(word = c("https","rt","t.co"), 
                              lexicon = c("custom")), 
                              stop_words)
Remove stop words from twitter data.
metootext <- metootext %>%
             anti_join(custom_stop_words, by="word")
How many words are we working with now?
[1] 475470
Plot Most Frequently Mentioned Words
#Plot most frequent words
detach("package:plyr", unload=TRUE)
‘plyr’ namespace cannot be unloaded:
  namespace ‘plyr’ is imported by ‘ggraph’, ‘ggplot2’, ‘scales’, ‘broom’, ‘reshape2’ so cannot be unloaded
library(dplyr)
metootext %>%
          filter(isRetweet == FALSE)%>%
          count(word, sort = TRUE) %>%
          filter(n > 500) %>%
          mutate(word = reorder(word, n)) %>%
          ggplot(aes(word, n)) +
                  geom_col(fill="lightblue") +
                        xlab("Words")+
                        ylab("# of Mentions | April 4th & 5th") +
                        coord_flip()+
                  theme_light()+
                  theme(text = element_text(size=10))+
                  geom_text(aes(x=word, y=n, label = n),
                      check_overlap = FALSE, size=3, hjust=1)

Wordcloud of Most Frequently Mentioned Words
metootextcloud <- metootext %>%
          filter(isRetweet == FALSE)%>%
          count(word, sort = TRUE) %>%
          filter(n > 40) %>%
          mutate(word = reorder(word, n))
          wordcloud(words = metootextcloud$word, freq = metootextcloud$n, min.freq = 1,
          max.words=200, random.order=FALSE, rot.per=0.35, 
          colors=brewer.pal(8, "Dark2"))

Sentiment Analysis

Evaluate Sentiment Using NRC Emotion Lexicon for word-sentiment associations.

Plot most common sentiments
Note: words are counted once for every sentiment they are associated with
metootext %>%
  inner_join(get_sentiments("nrc")) %>%
  count(sentiment, sort = TRUE) %>%
  ungroup()%>%
  group_by(sentiment) %>%
  ungroup() %>%
  mutate(sentiment = reorder(sentiment, n))%>%
  ggplot(aes(sentiment, n)) +
      geom_col(fill = "lightblue", show.legend = FALSE) +
          labs(y = "Number of Occurences",x = NULL) +
  coord_flip()+
      geom_text(aes(x=sentiment, y=n, label = n),
                    check_overlap = FALSE, 
                    size=3, 
                    angle=0, 
                    hjust=1.5)+
      theme_light()+    
      theme(text = element_text(size=14))

Plot most common words, by sentiment
metootext %>%
  inner_join(get_sentiments("nrc")) %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup()%>%
  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(~sentiment, scales = "free_y", nrow=4) +
          labs(y = "Number of Occurences",
          x = NULL) +
      coord_flip()+
      theme_light()+
      geom_text(aes(x=word, y=n, label = n),
                    check_overlap = FALSE, size=7, angle=0, hjust=.75)+
                    theme(text = element_text(size=24))

In what context are people using the word “powerful”?

metoo_tweets%>%
  filter(str_detect(text, "powerful"))%>%
  subset(select=c("text"))%>%
  print(row.names=FALSE)

Positive-Negative Sentiment Analysis

Using Bing Lexicon for positive-negative word classifications.

Total Number of Positive / Negative Words

metootext %>%
  inner_join(get_sentiments("bing")) %>%
  count(sentiment, sort = TRUE) %>%
  ungroup()%>%
  group_by(sentiment) %>%
  ungroup() %>%
  ggplot(aes(sentiment, n, fill = sentiment)) +
      geom_bar(show.legend = FALSE, stat="identity") +
          labs(y = "Number of Occurences",x = NULL) +
      geom_text(aes(x=sentiment, y=n, label = n),
                    check_overlap = FALSE, 
                    size=3, 
                    angle=0, 
                    vjust=1.5)+
      theme_light()+    
      theme(text = element_text(size=8),
              axis.text.y = element_blank())

Most frequently mentioned Positive / Negative Words

metootext %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup()%>%
  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(~sentiment, scales = "free_y") +
          labs(y = "Number of Occurences",
          x = NULL) +
      coord_flip()+
      theme_light()+
      geom_text(aes(x=word, y=n, label = n),
                    check_overlap = FALSE, size=3, angle=0, hjust=1)+
                    theme(text = element_text(size=16))

Positive / Negative Wordcloud

metootext %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup()%>%
  group_by(sentiment) %>%
  top_n(100) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  acast(word ~ sentiment, value.var = "n", fill = 0)%>%
  comparison.cloud(colors = c("#F8766D", "#00BFC4"),
                   max.words = 100)

Context of Positive / Negative Words

metoo_tweets%>%
  filter(str_detect(text, "silent"))%>%
  subset(select=c("text"))%>%
  print("text")

Word Pairings

Individual words may lend us some understanding of the kinds of language being used, but may not give us as much context as we may need to develop a qualitative understanding of the text. Looking at word pairings may help us see how terms are related to one another.

Which words are frequently paired together?

#install_github("dgrtwo/widyr")
#install.packages("igraph")
#install.packages("ggraph")
#library("devtools")
#library(widyr)
#library(igraph)
#library(ggraph)
#Generating all occuring word combinations (84 million combinations of 475,000 words)
metootextweb <- metootext %>% 
  pairwise_count(word, id, sort = TRUE, upper = FALSE)
metootextweb %>%
  filter(n >= 400) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = n, edge_width = n), edge_colour = "darkred") +
  geom_node_point(size = 5) +
  geom_node_text(aes(label = name), repel = TRUE, 
                 point.padding = unit(0.2, "lines")) +
  theme_void()

The word web above displays fequency of word pairings. This gives an unfair advantage to those words that are used more frequently, but may not be consistently paired together as frequently as other words. To level the playing field for word pairings, we can look at the correlational value between two words. It is possible that words which show up less frequently hold stronger correlations.

Which words are highly correlated?

metootextweb2 <- metootext %>% 
  group_by(word) %>%
  filter(n() >= 800) %>%
  pairwise_cor(word, id, sort = TRUE, upper = FALSE)
metootextweb2 %>%
  filter(correlation > .2) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = correlation, edge_width = correlation), 
                 edge_colour = "royalblue") +
  geom_node_point(size = 5) +
  geom_node_text(aes(label = name), repel = TRUE,
                 point.padding = unit(0.2, "lines")) +
  theme_void()

Which words are highly correlated? (stop words included)

metootext2 <- metoo_tweets %>%
                     unnest_tokens(word, text)
metootextweb3 <- metootext2 %>% 
  group_by(word) %>%
  filter(n() >= 1000) %>%
  pairwise_cor(word, id, sort = TRUE, upper = FALSE)
metootextweb3 %>%
  filter(correlation > .2) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = correlation, edge_width = correlation), 
                 edge_colour = "royalblue") +
  geom_node_point(size = 5) +
  geom_node_text(aes(label = name), repel = TRUE,
                 point.padding = unit(0.2, "lines")) +
  theme_void()

Conclusion & Notes

Through this analysis we are able to identify the kind of language being used to describe the issue, the proportion of positive-negative words that characterize the expressions of twitter users, and the relationships between words which occur frequently and consistently in pairs.

Newsjacking, otherwise labeled in this context “opinion spam”, is an issue which distorts the analysis. The #metoo hashtag is intended as a content classifier, and the unrelated content that appears within this segment of tweets contributes unrelated data into the sentiment measurements.

It is also important to note that sentiment analysis can tell you about the sentiment of the language being used by a person, but not neccesarily about the individuals perspective or leaning on an issue.

Sentiment analysis of individual words may give insight into the tone of a statement, but word clouds, word webs, and +/- language timelines may offer more insight into the substantive meaning of the text.

Emoji characters offer valuable sentiment information, but are unfortunately left out of this analysis. Decrypting and incorporating emojis into such text analysis would be an important addition to the qualitative content of a text analysis report.

LS0tCnRpdGxlOiAiIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiFbXSgvVXNlcnMvQnJldHQvTGlicmFyeS9Nb2JpbGUgRG9jdW1lbnRzL2NvbX5hcHBsZX5DbG91ZERvY3MvQWxsIEZpbGVzL0VtcGxveWVycy9SZXN1bWUvTURSQy9NZVRvby5wbmcpCgojI0Rlc2NyaXB0aW9uOgoKQW4gYW5hbHlzaXMgb2YgdHdlZXRzIHVuZGVyIHRoZSAjbWV0b28gaGFzaHRhZywgY29sbGVjdGVkIGJldHdlZW4gRGVjZW1iZXIgMSBhbmQgRGVjZW1iZXIgMzEsIDIwMTcuCgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojaW5zdGFsbC5wYWNrYWdlcygidHdpdHRlUiIpCiNpbnN0YWxsLnBhY2thZ2VzKCJST0F1dGgiKQojaW5zdGFsbC5wYWNrYWdlcygiaHR0ciIpCmxpYnJhcnkodHdpdHRlUikKbGlicmFyeShST0F1dGgpCmxpYnJhcnkoaHR0cikKbGlicmFyeShwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkod29yZGNsb3VkKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGRldnRvb2xzKQpsaWJyYXJ5KHdpZHlyKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShyZWFkcikKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgQVBJIEtleXMKYXBpX2tleSA8LSAiSzB4dEEwTFAxeG85Y2szbVkwaVRsVGUxVyIKYXBpX3NlY3JldCA8LSAiTG9pMlc4R3Y4UUdrYTByVmxTb3ZUM3N4OFVwZUdpVUwwM0ZMeFIwdXhpM0s4QlhrbjIiCmFjY2Vzc190b2tlbiA8LSAiMjkwNjgzMTU4LUZrRUR0WDFCYlQybktOcFE2TGJKczFVSjRFekFPb244TUxjMzg0Q0YiCmFjY2Vzc190b2tlbl9zZWNyZXQgPC0gIlI5TjhIN0FPWVlPNlAyMTJRUDIwZFVsb2pNV2F5MlE5Sk05SGpQOEJkRzAwbyIKCnNldHVwX3R3aXR0ZXJfb2F1dGgoYXBpX2tleSwgYXBpX3NlY3JldCwgYWNjZXNzX3Rva2VuLCBhY2Nlc3NfdG9rZW5fc2VjcmV0KQpgYGAKCgojI1NjcmFwZSBUd2l0dGVyIGZvciBUd2VldHMgd2l0aCAqKiNtZXRvbyoqIGhhc2h0YWcKCgojIyMjIyBEZWZpbmUgQVBJIEtleXMgJiBBdXRoZW50aWNhdGUgdGhyb3VnaCBUd2l0dGVyIGFwcApgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFfQphcGlfa2V5IDwtICIqKioqKioqIgphcGlfc2VjcmV0IDwtICIqKioqKioqIgphY2Nlc3NfdG9rZW4gPC0gIioqKioqKioiCmFjY2Vzc190b2tlbl9zZWNyZXQgPC0gIioqKioqKioiCgpzZXR1cF90d2l0dGVyX29hdXRoKGFwaV9rZXksIGFwaV9zZWNyZXQsIGFjY2Vzc190b2tlbiwgYWNjZXNzX3Rva2VuX3NlY3JldCkKYGBgCgoKIyMjIyMgRG93bmxvYWQgdHdlZXRzIGNvbnRhaW5pbmcgKiojbWV0b28qKiBIYXNodGFnIG9uIE5vdiAxMywgMjAxNS4gU2F2ZSBhcyAuY3N2LgpgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFfQptZXRvb190d2VldHMgPC0gdHdMaXN0VG9ERihzZWFyY2hUd2l0dGVyKCIjbWV0b28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG49MjAwMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFuZz0iZW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2luY2U9IjIwMTctMTItMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdW50aWw9IjIwMTctMTItMzEiKSkKCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0KHNlbGVjdD1jKCJpZCIsInRleHQiLCJjcmVhdGVkIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjcmVlbk5hbWUiLCJyZXR3ZWV0Q291bnQiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaXNSZXR3ZWV0IiwicmV0d2VldGVkIikpCgoKCiNTYXZlIG1ldG9vX3R3ZWV0cyB0byAuY3N2CiN3cml0ZS5jc3YobWV0b29fdHdlZXRzLCIvVXNlcnMvQnJldHQvRGVza3RvcC9tZXRvby9tZXRvb18xMl8yM18xNy5jc3YiKQpgYGAKCiMjIyMjTG9hZCBEYXRhCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptZXRvb190d2VldHM8LXJlYWRfY3N2KCIvVXNlcnMvQnJldHQvTGlicmFyeS9Nb2JpbGUgRG9jdW1lbnRzL2NvbX5hcHBsZX5DbG91ZERvY3MvQWxsIEZpbGVzL0VkdWNhdGlvbi9RQyBUZWFjaGluZy9Ud2VldCBBcmNoaXZlL21ldG9vL21ldG9vX3R3ZWV0c19kZWMyMDE3LmNzdiIpCgojVGFrZSBhIHJhbmRvbSBzYW1wbGUgb2YgdGhlIDMwMEsgdHdlZXRzIGluIHRoaXMgZGF0YXNldC4gCm1ldG9vX3R3ZWV0czwtc2FtcGxlX24obWV0b29fdHdlZXRzLCA1MDAwMCkKYGBgCgoKIyMjIyNSZW1vdmUgRW1vamkgQ2hhcmFjdGVycwpgYGB7cn0KbWV0b29fdHdlZXRzJHRleHQgPC0gc2FwcGx5KG1ldG9vX3R3ZWV0cyR0ZXh0LCBmdW5jdGlvbihyb3cpIGljb252KHJvdywgImxhdGluMSIsICJBU0NJSSIsIHN1Yj0iIikpCmBgYAoKIyMjIyNTZXBhcmF0ZSBlYWNoIGluZGl2aWR1YWwgd29yZCBpbnRvIGEgcm93IG9mIGl0cyBvd24uCmBgYHtyfQogICAgICAgIG1ldG9vdGV4dCA8LSBtZXRvb190d2VldHMgJT4lCiAgICAgICAgICAgICAgICAgICAgIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkKYGBgCgojIyMjI0NyZWF0ZSBhIGxpc3Qgb2YgY3VzdG9tcyBzdG9wIHdvcmRzLgoKYGBge3J9CmRhdGEoc3RvcF93b3JkcykKY3VzdG9tX3N0b3Bfd29yZHMgPC0gYmluZF9yb3dzKAogICAgICAgICAgICAgICAgICAgIGRhdGFfZnJhbWUod29yZCA9IGMoImh0dHBzIiwicnQiLCJ0LmNvIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXhpY29uID0gYygiY3VzdG9tIikpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RvcF93b3JkcykKYGBgCgojIyMjI1JlbW92ZSBzdG9wIHdvcmRzIGZyb20gdHdpdHRlciBkYXRhLgoKYGBge3J9Cm1ldG9vdGV4dCA8LSBtZXRvb3RleHQgJT4lCiAgICAgICAgICAgICBhbnRpX2pvaW4oY3VzdG9tX3N0b3Bfd29yZHMsIGJ5PSJ3b3JkIikKYGBgCgojIyMjI0hvdyBtYW55IHdvcmRzIGFyZSB3ZSB3b3JraW5nIHdpdGggbm93PwpgYGB7ciwgZWNobz1GQUxTRX0KbnJvdyhtZXRvb3RleHQpCmBgYCAgICAgICAgCgojIyMjI1Bsb3QgTW9zdCBGcmVxdWVudGx5IE1lbnRpb25lZCBXb3JkcwpgYGB7cn0KI1Bsb3QgbW9zdCBmcmVxdWVudCB3b3JkcwpkZXRhY2goInBhY2thZ2U6cGx5ciIsIHVubG9hZD1UUlVFKQpsaWJyYXJ5KGRwbHlyKQptZXRvb3RleHQgJT4lCiAgICAgICAgICBmaWx0ZXIoaXNSZXR3ZWV0ID09IEZBTFNFKSU+JQogICAgICAgICAgY291bnQod29yZCwgc29ydCA9IFRSVUUpICU+JQogICAgICAgICAgZmlsdGVyKG4gPiA1MDApICU+JQogICAgICAgICAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICAgICAgICAgIGdncGxvdChhZXMod29yZCwgbikpICsKICAgICAgICAgICAgICAgICAgZ2VvbV9jb2woZmlsbD0ibGlnaHRibHVlIikgKwogICAgICAgICAgICAgICAgICAgICAgICB4bGFiKCJXb3JkcyIpKwogICAgICAgICAgICAgICAgICAgICAgICB5bGFiKCIjIG9mIE1lbnRpb25zIHwgQXByaWwgNHRoICYgNXRoIikgKwogICAgICAgICAgICAgICAgICAgICAgICBjb29yZF9mbGlwKCkrCiAgICAgICAgICAgICAgICAgIHRoZW1lX2xpZ2h0KCkrCiAgICAgICAgICAgICAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpKwogICAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKHg9d29yZCwgeT1uLCBsYWJlbCA9IG4pLAogICAgICAgICAgICAgICAgICAgICAgY2hlY2tfb3ZlcmxhcCA9IEZBTFNFLCBzaXplPTMsIGhqdXN0PTEpCmBgYAoKIyMjIyNXb3JkY2xvdWQgb2YgTW9zdCBGcmVxdWVudGx5IE1lbnRpb25lZCBXb3JkcwpgYGB7cn0KbWV0b290ZXh0Y2xvdWQgPC0gbWV0b290ZXh0ICU+JQogICAgICAgICAgZmlsdGVyKGlzUmV0d2VldCA9PSBGQUxTRSklPiUKICAgICAgICAgIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUKICAgICAgICAgIGZpbHRlcihuID4gNDApICU+JQogICAgICAgICAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKQoKICAgICAgICAgIHdvcmRjbG91ZCh3b3JkcyA9IG1ldG9vdGV4dGNsb3VkJHdvcmQsIGZyZXEgPSBtZXRvb3RleHRjbG91ZCRuLCBtaW4uZnJlcSA9IDEsCiAgICAgICAgICBtYXgud29yZHM9MjAwLCByYW5kb20ub3JkZXI9RkFMU0UsIHJvdC5wZXI9MC4zNSwgCiAgICAgICAgICBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkKCmBgYAoKI1NlbnRpbWVudCBBbmFseXNpcwoKPkV2YWx1YXRlIFNlbnRpbWVudCBVc2luZyBbTlJDIEVtb3Rpb24gTGV4aWNvbl0oaHR0cDovL3NhaWZtb2hhbW1hZC5jb20vV2ViUGFnZXMvTlJDLUVtb3Rpb24tTGV4aWNvbi5odG0pIGZvciB3b3JkLXNlbnRpbWVudCBhc3NvY2lhdGlvbnMuCgojIyMjIyBQbG90IG1vc3QgY29tbW9uIHNlbnRpbWVudHMKCiMjIyMjIyAqKk5vdGU6KiogKndvcmRzIGFyZSBjb3VudGVkIG9uY2UgZm9yIGV2ZXJ5IHNlbnRpbWVudCB0aGV5IGFyZSBhc3NvY2lhdGVkIHdpdGgqCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQptZXRvb3RleHQgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikpICU+JQogIGNvdW50KHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIHVuZ3JvdXAoKSU+JQogIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShzZW50aW1lbnQgPSByZW9yZGVyKHNlbnRpbWVudCwgbikpJT4lCiAgZ2dwbG90KGFlcyhzZW50aW1lbnQsIG4pKSArCiAgICAgIGdlb21fY29sKGZpbGwgPSAibGlnaHRibHVlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgICAgICAgbGFicyh5ID0gIk51bWJlciBvZiBPY2N1cmVuY2VzIix4ID0gTlVMTCkgKwogIGNvb3JkX2ZsaXAoKSsKICAgICAgZ2VvbV90ZXh0KGFlcyh4PXNlbnRpbWVudCwgeT1uLCBsYWJlbCA9IG4pLAogICAgICAgICAgICAgICAgICAgIGNoZWNrX292ZXJsYXAgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgc2l6ZT0zLCAKICAgICAgICAgICAgICAgICAgICBhbmdsZT0wLCAKICAgICAgICAgICAgICAgICAgICBoanVzdD0xLjUpKwogICAgICB0aGVtZV9saWdodCgpKyAgICAKICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSkKCmBgYAoKIyMjIyMgUGxvdCBtb3N0IGNvbW1vbiB3b3JkcywgYnkgc2VudGltZW50CmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRX0KCm1ldG9vdGV4dCAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkgJT4lCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkgJT4lCiAgdW5ncm91cCgpJT4lCiAgZ3JvdXBfYnkoc2VudGltZW50KSAlPiUKICB0b3BfbigxMCkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lCiAgZ2dwbG90KGFlcyh3b3JkLCBuLCBmaWxsID0gc2VudGltZW50KSkgKwogICAgICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICAgICAgICBmYWNldF93cmFwKH5zZW50aW1lbnQsIHNjYWxlcyA9ICJmcmVlX3kiLCBucm93PTQpICsKICAgICAgICAgIGxhYnMoeSA9ICJOdW1iZXIgb2YgT2NjdXJlbmNlcyIsCiAgICAgICAgICB4ID0gTlVMTCkgKwogICAgICBjb29yZF9mbGlwKCkrCiAgICAgIHRoZW1lX2xpZ2h0KCkrCiAgICAgIGdlb21fdGV4dChhZXMoeD13b3JkLCB5PW4sIGxhYmVsID0gbiksCiAgICAgICAgICAgICAgICAgICAgY2hlY2tfb3ZlcmxhcCA9IEZBTFNFLCBzaXplPTcsIGFuZ2xlPTAsIGhqdXN0PS43NSkrCiAgICAgICAgICAgICAgICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTI0KSkKYGBgCgojIyMjSW4gd2hhdCBjb250ZXh0IGFyZSBwZW9wbGUgdXNpbmcgdGhlIHdvcmQgInBvd2VyZnVsIj8KYGBge3J9Cm1ldG9vX3R3ZWV0cyU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHRleHQsICJwb3dlcmZ1bCIpKSU+JQogIHN1YnNldChzZWxlY3Q9YygidGV4dCIpKSU+JQogIHByaW50KHJvdy5uYW1lcz1GQUxTRSkKYGBgCgoKCgojI1Bvc2l0aXZlLU5lZ2F0aXZlIFNlbnRpbWVudCBBbmFseXNpcwoKPlVzaW5nIFtCaW5nIExleGljb25dKGh0dHBzOi8vd3d3LmNzLnVpYy5lZHUvfmxpdWIvRkJTL3NlbnRpbWVudC1hbmFseXNpcy5odG1sKSBmb3IgcG9zaXRpdmUtbmVnYXRpdmUgd29yZCBjbGFzc2lmaWNhdGlvbnMuCgoKIyMjI1RvdGFsIE51bWJlciBvZiBQb3NpdGl2ZSAvIE5lZ2F0aXZlIFdvcmRzCmBgYHtyLCBmaWcuaGVpZ2h0PTEsIGZpZy53aWR0aD0yLCBtZXNzYWdlPUZBTFNFfQptZXRvb3RleHQgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKSAlPiUKICBjb3VudChzZW50aW1lbnQsIHNvcnQgPSBUUlVFKSAlPiUKICB1bmdyb3VwKCklPiUKICBncm91cF9ieShzZW50aW1lbnQpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBnZ3Bsb3QoYWVzKHNlbnRpbWVudCwgbiwgZmlsbCA9IHNlbnRpbWVudCkpICsKICAgICAgZ2VvbV9iYXIoc2hvdy5sZWdlbmQgPSBGQUxTRSwgc3RhdD0iaWRlbnRpdHkiKSArCiAgICAgICAgICBsYWJzKHkgPSAiTnVtYmVyIG9mIE9jY3VyZW5jZXMiLHggPSBOVUxMKSArCiAgICAgIGdlb21fdGV4dChhZXMoeD1zZW50aW1lbnQsIHk9biwgbGFiZWwgPSBuKSwKICAgICAgICAgICAgICAgICAgICBjaGVja19vdmVybGFwID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgIHNpemU9MywgCiAgICAgICAgICAgICAgICAgICAgYW5nbGU9MCwgCiAgICAgICAgICAgICAgICAgICAgdmp1c3Q9MS41KSsKICAgICAgdGhlbWVfbGlnaHQoKSsgICAgCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjTW9zdCBmcmVxdWVudGx5IG1lbnRpb25lZCBQb3NpdGl2ZSAvIE5lZ2F0aXZlIFdvcmRzCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbWV0b290ZXh0ICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkgJT4lCiAgdW5ncm91cCgpJT4lCiAgZ3JvdXBfYnkoc2VudGltZW50KSAlPiUKICB0b3BfbigxMCkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lCiAgZ2dwbG90KGFlcyh3b3JkLCBuLCBmaWxsID0gc2VudGltZW50KSkgKwogICAgICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICAgICAgICBmYWNldF93cmFwKH5zZW50aW1lbnQsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgICAgICAgICBsYWJzKHkgPSAiTnVtYmVyIG9mIE9jY3VyZW5jZXMiLAogICAgICAgICAgeCA9IE5VTEwpICsKICAgICAgY29vcmRfZmxpcCgpKwogICAgICB0aGVtZV9saWdodCgpKwogICAgICBnZW9tX3RleHQoYWVzKHg9d29yZCwgeT1uLCBsYWJlbCA9IG4pLAogICAgICAgICAgICAgICAgICAgIGNoZWNrX292ZXJsYXAgPSBGQUxTRSwgc2l6ZT0zLCBhbmdsZT0wLCBoanVzdD0xKSsKICAgICAgICAgICAgICAgICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTYpKQpgYGAKCgojIyMjUG9zaXRpdmUgLyBOZWdhdGl2ZSBXb3JkY2xvdWQKYGBge3IsIG1lc3NhZ2U9RkFMU0V9Cm1ldG9vdGV4dCAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JQogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIHVuZ3JvdXAoKSU+JQogIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lCiAgdG9wX24oMTAwKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBhY2FzdCh3b3JkIH4gc2VudGltZW50LCB2YWx1ZS52YXIgPSAibiIsIGZpbGwgPSAwKSU+JQogIGNvbXBhcmlzb24uY2xvdWQoY29sb3JzID0gYygiI0Y4NzY2RCIsICIjMDBCRkM0IiksCiAgICAgICAgICAgICAgICAgICBtYXgud29yZHMgPSAxMDApCmBgYAoKIyMjI0NvbnRleHQgb2YgUG9zaXRpdmUgLyBOZWdhdGl2ZSBXb3JkcwoKYGBge3IsIGVjaG89VFJVRX0KbWV0b29fdHdlZXRzJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3QodGV4dCwgInNpbGVudCIpKSU+JQogIHN1YnNldChzZWxlY3Q9YygidGV4dCIpKSU+JQogIHByaW50KCJ0ZXh0IikKYGBgCgoKCiMjV29yZCBQYWlyaW5ncwoKPkluZGl2aWR1YWwgd29yZHMgbWF5IGxlbmQgdXMgc29tZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBraW5kcyBvZiBsYW5ndWFnZSBiZWluZyB1c2VkLCBidXQgbWF5IG5vdCBnaXZlIHVzIGFzIG11Y2ggY29udGV4dCBhcyB3ZSBtYXkgbmVlZCB0byBkZXZlbG9wIGEgcXVhbGl0YXRpdmUgdW5kZXJzdGFuZGluZyBvZiB0aGUgdGV4dC4gTG9va2luZyBhdCB3b3JkIHBhaXJpbmdzIG1heSBoZWxwIHVzIHNlZSBob3cgdGVybXMgYXJlIHJlbGF0ZWQgdG8gb25lIGFub3RoZXIuCgojIyMjV2hpY2ggd29yZHMgYXJlIGZyZXF1ZW50bHkgcGFpcmVkIHRvZ2V0aGVyPwoKYGBge3IsIGVjaG89VFJVRX0KCiNpbnN0YWxsX2dpdGh1YigiZGdydHdvL3dpZHlyIikKI2luc3RhbGwucGFja2FnZXMoImlncmFwaCIpCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3JhcGgiKQojbGlicmFyeSgiZGV2dG9vbHMiKQojbGlicmFyeSh3aWR5cikKI2xpYnJhcnkoaWdyYXBoKQojbGlicmFyeShnZ3JhcGgpCgojR2VuZXJhdGluZyBhbGwgb2NjdXJpbmcgd29yZCBjb21iaW5hdGlvbnMgKDg0IG1pbGxpb24gY29tYmluYXRpb25zIG9mIDQ3NSwwMDAgd29yZHMpCm1ldG9vdGV4dHdlYiA8LSBtZXRvb3RleHQgJT4lIAogIHBhaXJ3aXNlX2NvdW50KHdvcmQsIGlkLCBzb3J0ID0gVFJVRSwgdXBwZXIgPSBGQUxTRSkKCm1ldG9vdGV4dHdlYiAlPiUKICBmaWx0ZXIobiA+PSA0MDApICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuLCBlZGdlX3dpZHRoID0gbiksIGVkZ2VfY29sb3VyID0gImRhcmtyZWQiKSArCiAgZ2VvbV9ub2RlX3BvaW50KHNpemUgPSA1KSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IHVuaXQoMC4yLCAibGluZXMiKSkgKwogIHRoZW1lX3ZvaWQoKQpgYGAKCgo+VGhlIHdvcmQgd2ViIGFib3ZlIGRpc3BsYXlzIGZlcXVlbmN5IG9mIHdvcmQgcGFpcmluZ3MuIFRoaXMgZ2l2ZXMgYW4gdW5mYWlyIGFkdmFudGFnZSB0byB0aG9zZSB3b3JkcyB0aGF0IGFyZSB1c2VkIG1vcmUgZnJlcXVlbnRseSwgYnV0IG1heSBub3QgYmUgY29uc2lzdGVudGx5IHBhaXJlZCB0b2dldGhlciBhcyBmcmVxdWVudGx5IGFzIG90aGVyIHdvcmRzLiBUbyBsZXZlbCB0aGUgcGxheWluZyBmaWVsZCBmb3Igd29yZCBwYWlyaW5ncywgd2UgY2FuIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uYWwgdmFsdWUgYmV0d2VlbiB0d28gd29yZHMuIEl0IGlzIHBvc3NpYmxlIHRoYXQgd29yZHMgd2hpY2ggc2hvdyB1cCBsZXNzIGZyZXF1ZW50bHkgaG9sZCBzdHJvbmdlciBjb3JyZWxhdGlvbnMuIAoKIyMjI1doaWNoIHdvcmRzIGFyZSBoaWdobHkgY29ycmVsYXRlZD8KYGBge3IsIGVjaG89VFJVRX0KbWV0b290ZXh0d2ViMiA8LSBtZXRvb3RleHQgJT4lIAogIGdyb3VwX2J5KHdvcmQpICU+JQogIGZpbHRlcihuKCkgPj0gODAwKSAlPiUKICBwYWlyd2lzZV9jb3Iod29yZCwgaWQsIHNvcnQgPSBUUlVFLCB1cHBlciA9IEZBTFNFKQoKbWV0b290ZXh0d2ViMiAlPiUKICBmaWx0ZXIoY29ycmVsYXRpb24gPiAuMikgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IGNvcnJlbGF0aW9uLCBlZGdlX3dpZHRoID0gY29ycmVsYXRpb24pLCAKICAgICAgICAgICAgICAgICBlZGdlX2NvbG91ciA9ICJyb3lhbGJsdWUiKSArCiAgZ2VvbV9ub2RlX3BvaW50KHNpemUgPSA1KSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsID0gVFJVRSwKICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gdW5pdCgwLjIsICJsaW5lcyIpKSArCiAgdGhlbWVfdm9pZCgpCgpgYGAKCiMjIyNXaGljaCB3b3JkcyBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQ/IChzdG9wIHdvcmRzIGluY2x1ZGVkKQpgYGB7ciwgZWNobz1UUlVFfQptZXRvb3RleHQyIDwtIG1ldG9vX3R3ZWV0cyAlPiUKICAgICAgICAgICAgICAgICAgICAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KQoKbWV0b290ZXh0d2ViMyA8LSBtZXRvb3RleHQyICU+JSAKICBncm91cF9ieSh3b3JkKSAlPiUKICBmaWx0ZXIobigpID49IDEwMDApICU+JQogIHBhaXJ3aXNlX2Nvcih3b3JkLCBpZCwgc29ydCA9IFRSVUUsIHVwcGVyID0gRkFMU0UpCgptZXRvb3RleHR3ZWIzICU+JQogIGZpbHRlcihjb3JyZWxhdGlvbiA+IC4yKSAlPiUKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gY29ycmVsYXRpb24sIGVkZ2Vfd2lkdGggPSBjb3JyZWxhdGlvbiksIAogICAgICAgICAgICAgICAgIGVkZ2VfY29sb3VyID0gInJveWFsYmx1ZSIpICsKICBnZW9tX25vZGVfcG9pbnQoc2l6ZSA9IDUpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFLAogICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSB1bml0KDAuMiwgImxpbmVzIikpICsKICB0aGVtZV92b2lkKCkKYGBgCiMjQ29uY2x1c2lvbiAmIE5vdGVzCgpUaHJvdWdoIHRoaXMgYW5hbHlzaXMgd2UgYXJlIGFibGUgdG8gaWRlbnRpZnkgdGhlIGtpbmQgb2YgbGFuZ3VhZ2UgYmVpbmcgdXNlZCB0byBkZXNjcmliZSB0aGUgaXNzdWUsIHRoZSBwcm9wb3J0aW9uIG9mIHBvc2l0aXZlLW5lZ2F0aXZlIHdvcmRzIHRoYXQgY2hhcmFjdGVyaXplIHRoZSBleHByZXNzaW9ucyBvZiB0d2l0dGVyIHVzZXJzLCBhbmQgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB3b3JkcyB3aGljaCBvY2N1ciBmcmVxdWVudGx5IGFuZCBjb25zaXN0ZW50bHkgaW4gcGFpcnMuCgpOZXdzamFja2luZywgb3RoZXJ3aXNlIGxhYmVsZWQgaW4gdGhpcyBjb250ZXh0ICJvcGluaW9uIHNwYW0iLCBpcyBhbiBpc3N1ZSB3aGljaCBkaXN0b3J0cyB0aGUgYW5hbHlzaXMuIFRoZSAjbWV0b28gaGFzaHRhZyBpcyBpbnRlbmRlZCBhcyBhIGNvbnRlbnQgY2xhc3NpZmllciwgYW5kIHRoZSB1bnJlbGF0ZWQgY29udGVudCB0aGF0IGFwcGVhcnMgd2l0aGluIHRoaXMgc2VnbWVudCBvZiB0d2VldHMgY29udHJpYnV0ZXMgdW5yZWxhdGVkIGRhdGEgaW50byB0aGUgc2VudGltZW50IG1lYXN1cmVtZW50cy4KCkl0IGlzIGFsc28gaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBzZW50aW1lbnQgYW5hbHlzaXMgY2FuIHRlbGwgeW91IGFib3V0IHRoZSBzZW50aW1lbnQgb2YgdGhlIGxhbmd1YWdlIGJlaW5nIHVzZWQgYnkgYSBwZXJzb24sIGJ1dCBub3QgbmVjY2VzYXJpbHkgYWJvdXQgdGhlIGluZGl2aWR1YWxzIHBlcnNwZWN0aXZlIG9yIGxlYW5pbmcgb24gYW4gaXNzdWUuCgpTZW50aW1lbnQgYW5hbHlzaXMgb2YgaW5kaXZpZHVhbCB3b3JkcyBtYXkgZ2l2ZSBpbnNpZ2h0IGludG8gdGhlIHRvbmUgb2YgYSBzdGF0ZW1lbnQsIGJ1dCB3b3JkIGNsb3Vkcywgd29yZCB3ZWJzLCBhbmQgKy8tIGxhbmd1YWdlIHRpbWVsaW5lcyBtYXkgb2ZmZXIgbW9yZSBpbnNpZ2h0IGludG8gdGhlIHN1YnN0YW50aXZlIG1lYW5pbmcgb2YgdGhlIHRleHQuCgpFbW9qaSBjaGFyYWN0ZXJzIG9mZmVyIHZhbHVhYmxlIHNlbnRpbWVudCBpbmZvcm1hdGlvbiwgYnV0IGFyZSB1bmZvcnR1bmF0ZWx5IGxlZnQgb3V0IG9mIHRoaXMgYW5hbHlzaXMuIERlY3J5cHRpbmcgYW5kIGluY29ycG9yYXRpbmcgZW1vamlzIGludG8gc3VjaCB0ZXh0IGFuYWx5c2lzIHdvdWxkIGJlIGFuIGltcG9ydGFudCBhZGRpdGlvbiB0byB0aGUgcXVhbGl0YXRpdmUgY29udGVudCBvZiBhIHRleHQgYW5hbHlzaXMgcmVwb3J0Lg==