On Septempter 19th, 2017, Chuck Todd hosted a debate between Republican Ed Gillespie and Democrat Ralph Northam. This analysis will use the tidytext, rtweet, ggplot, dplyr, and a few other packages to analyze Twitter reactions through the #VAGov and #VAGovDebate hashtags. As a first step, we need to load (and in some cases download) the necessary R packages.

# install.packages("rtweet")
library(rtweet)
library(tidytext)
library(dplyr)
library(ggplot2)
# devtools::install_github('yaztheme','joshyazman')
library(yaztheme)
library(stringr)

Now I need to set up access to the Twitter API (my credentials are removed, but you can set up a developers account here).

appname <- "redacted"
key <- "redacted"
secret <- "redacted"
twitter_token <- create_token(
  app = appname,
  consumer_key = key,
  consumer_secret = secret)

Now that the API key is set up, I’ll use the search_tweets function to search for any tweets that use either “#VAGovDebate” or “VAGov”. I want to make sure I get as many tweets as possible, so I set n = 18000, the maximum number allowed by the API at any one time.

tweets_raw <- search_tweets(q = "#VAGovDebate OR #VAGov", n = 18000)

The tweets come back in the form of a nice, tidy dataframe, which is great! But I don’t need all of the tweets so I’m going to limit the data to tweets from the day of the debate and cut down some columns I don’t need using the select(), %>%, and filter() functions from the dplyr package.

str(tweets)
'data.frame':   0 obs. of  7 variables:
 $ screen_name         : Factor w/ 2664 levels "__kelsey","_AlexThomps",..: 
 $ created_at          : Factor w/ 5355 levels "2017-09-19 04:42:19",..: 
 $ text                : Factor w/ 3794 levels "'Atta boy .@chucktodd - couldn't resist a cheap shot @POTUS. Sleazy. #VAGovDebate",..: 
 $ retweet_count       : int 
 $ favorite_count      : int 
 $ mentions_screen_name: Factor w/ 958 levels "_AlexThomps NOVAChamber",..: 
 $ hashtags            : Factor w/ 504 levels "3rdPerson VAGovDebate",..: 

Mentions

Now we have a clean, rich set of tweets to analyze! First, I want to just look at tweet volume over the course of the day. I can do that with a line graph using ggplot. First I’ll aggregate tweets by minute and then plot them. Not surprisingly, there was a spike in tweets related to the debate during the debate!

Let’s dig a bit deeper and look at how many tweets mention each candidate. Again, I’ll create an aggregated dataframe of tweets by candidate by minute, but this time I need to take one extra step and create a flag variable for mentions of [@RalphNortham](https://twitter.com/RalphNortham) and [@EdWGillespie](https://twitter.com/EdWGillespie) or both.

Most of the time, people tweeting about the debate weren’t necessarily mentioning either or both candidates!, but there were some clear moments when Gillespie or Northam popped. I didn’t keep a timed transcript, though, so if you’re reading this and have some ideas of what the candidates were talking about at those times, let me know!

Hashtags

Now I want to look at hashtag frequency. The search_tweets output provides a tidy field for hashtags, but we want to get a count of each individual hashtag used during the debate so we want each hashtag on its own line of a dataframe. The tidytext package has some handy functions for that!

hashtag_count <- tweets%>%
  mutate(candidate_mention = ifelse(grepl('RalphNortham', mentions_screen_name) 
                                    & grepl('EdWGillespie', mentions_screen_name), 'Both',
                                    ifelse(grepl('RalphNortham', mentions_screen_name),'Northam',
                                           ifelse(grepl('EdWGillespie', mentions_screen_name), 'Gillespie',
                                                  'Neither'))),
         mins = round(created_at, 'mins'))%>%
  filter(created_at > '2017-09-19 17:00:00' & !is.na(candidate_mention))%>%
  unnest_tokens(hts, hashtags)%>%
  group_by(hts, candidate_mention)%>%
  summarise(n = n())%>%
  filter(!hts %in% c('vagovdebate','vagov')
         & n > 20 & !is.na(hts))
ggplot(hashtag_count, aes(x = reorder(hts, n), y = n, fill = candidate_mention))+
  geom_bar(stat = 'identity')+
  coord_flip()+
  theme_yaz()+
  labs(y = 'Hashtag Frequency', x = element_blank(),
       title = 'Hashtag Frequency by Candidate',
       subtitle = 'Hashtags limited to those used more than 20 times after 3:00pm EST on Debate Day')+
  scale_fill_manual(name = 'Candidate Mentioned', values = yaz_cols[c(2,3,1)])

Word Distinctiveness and Sentiment Analysis

Lastly I want to look at tweet sentiments. The tidytext package has four sentiment lexicons available. The bing and nrc lexicons both offer pretty good classifications of words as either positive or negative, although nrc tends to err on the side of being overly positive. The nrc lexicon also classifies words as indicative of “trust”, “fear”, “sadness”, “anger”, “surprise”, “positive”, “disgust”, “joy”, and “anticipation” as well. We can unnest tweet words the same way we did with hashtags and then use a simple join to append the positive/negative scores from the bing lexicon and all of the sentiments from the nrc lexicon.

First I want to look at tweet sentiment over time broken out by candidate mention. It looks like net sentiments were generally more positive before the debate than during or after, which makes sense because pre-debate tweets were mostly (anecdotally) about hyping one’s candidate rather than hitting the other. During the debate, everyone’s tweets got more negative, but Northam mentions remained the most positive.

To examine NRC sentiments, we calculate the percentage of words within each candidate mention grouping that match each potential sentiment. Anticipation pops across the board, likely because of tweets leading up the the debate. Aside from that, it’s notable that Gillespie leads slightly on Trust and Northam leads slightly on Joy. Tweets that mention both candidates are the angriest.

ggplot(nrc_tweet_words%>%
         group_by(mins, candidate_mention, nrc)%>%
         summarise(n = n())%>%
         group_by(candidate_mention)%>%
         mutate(pct = n/sum(n))%>%
         ungroup(),
  aes(x = nrc, y = pct, fill = candidate_mention))+
  geom_bar(stat = 'identity')+
  coord_flip()+
  facet_wrap(~candidate_mention)+
  theme_yaz()+
  labs(y = 'Sentiment', x = element_blank(),
       title = 'Tweet Sentiments by Candidate')+
  scale_fill_manual(name = 'Candidate Mentioned', values = yaz_cols[c(4,2,3,1)])

LS0tDQp0aXRsZTogIlZBIEdvdmVybm9yJ3MgRGViYXRlIFR3aXR0ZXIgUmVhY3Rpb25zIg0KYXV0aG9yOiAnSm9zaCBZYXptYW4nDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpPbiBTZXB0ZW1wdGVyIDE5dGgsIDIwMTcsIENodWNrIFRvZGQgaG9zdGVkIGEgZGViYXRlIGJldHdlZW4gUmVwdWJsaWNhbiBFZCBHaWxsZXNwaWUgYW5kIERlbW9jcmF0IFJhbHBoIE5vcnRoYW0uIFRoaXMgYW5hbHlzaXMgd2lsbCB1c2UgdGhlIGB0aWR5dGV4dGAsIGBydHdlZXRgLCBgZ2dwbG90YCwgYGRwbHlyYCwgYW5kIGEgZmV3IG90aGVyIHBhY2thZ2VzIHRvIGFuYWx5emUgVHdpdHRlciByZWFjdGlvbnMgdGhyb3VnaCB0aGUgI1ZBR292IGFuZCAjVkFHb3ZEZWJhdGUgaGFzaHRhZ3MuIEFzIGEgZmlyc3Qgc3RlcCwgd2UgbmVlZCB0byBsb2FkIChhbmQgaW4gc29tZSBjYXNlcyBkb3dubG9hZCkgdGhlIG5lY2Vzc2FyeSBSIHBhY2thZ2VzLg0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvID0gVFJVRX0NCiMgaW5zdGFsbC5wYWNrYWdlcygicnR3ZWV0IikNCmxpYnJhcnkocnR3ZWV0KQ0KbGlicmFyeSh0aWR5dGV4dCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigneWF6dGhlbWUnLCdqb3NoeWF6bWFuJykNCmxpYnJhcnkoeWF6dGhlbWUpDQpsaWJyYXJ5KHN0cmluZ3IpDQpgYGANCg0KTm93IEkgbmVlZCB0byBzZXQgdXAgYWNjZXNzIHRvIHRoZSBUd2l0dGVyIEFQSSAobXkgY3JlZGVudGlhbHMgYXJlIHJlbW92ZWQsIGJ1dCB5b3UgY2FuIHNldCB1cCBhIGRldmVsb3BlcnMgYWNjb3VudCBbaGVyZV0oYXBwcy50d2l0dGVyLmNvbSkpLiANCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFRSVUV9DQphcHBuYW1lIDwtICJyZWRhY3RlZCINCmtleSA8LSAicmVkYWN0ZWQiDQpzZWNyZXQgPC0gInJlZGFjdGVkIg0KdHdpdHRlcl90b2tlbiA8LSBjcmVhdGVfdG9rZW4oDQogIGFwcCA9IGFwcG5hbWUsDQogIGNvbnN1bWVyX2tleSA9IGtleSwNCiAgY29uc3VtZXJfc2VjcmV0ID0gc2VjcmV0KQ0KYGBgDQoNCk5vdyB0aGF0IHRoZSBBUEkga2V5IGlzIHNldCB1cCwgSSdsbCB1c2UgdGhlIGBzZWFyY2hfdHdlZXRzYCBmdW5jdGlvbiB0byBzZWFyY2ggZm9yIGFueSB0d2VldHMgdGhhdCB1c2UgZWl0aGVyICIjVkFHb3ZEZWJhdGUiIG9yICJWQUdvdiIuIEkgd2FudCB0byBtYWtlIHN1cmUgSSBnZXQgYXMgbWFueSB0d2VldHMgYXMgcG9zc2libGUsIHNvIEkgc2V0IGBuID0gMTgwMDBgLCB0aGUgbWF4aW11bSBudW1iZXIgYWxsb3dlZCBieSB0aGUgQVBJIGF0IGFueSBvbmUgdGltZS4gDQoNCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG8gPSBUUlVFfQ0KdHdlZXRzX3JhdyA8LSBzZWFyY2hfdHdlZXRzKHEgPSAiI1ZBR292RGViYXRlIE9SICNWQUdvdiIsIG4gPSAxODAwMCkNCmBgYA0KDQpUaGUgdHdlZXRzIGNvbWUgYmFjayBpbiB0aGUgZm9ybSBvZiBhIG5pY2UsIHRpZHkgZGF0YWZyYW1lLCB3aGljaCBpcyBncmVhdCEgQnV0IEkgZG9uJ3QgbmVlZCBhbGwgb2YgdGhlIHR3ZWV0cyBzbyBJJ20gZ29pbmcgdG8gbGltaXQgdGhlIGRhdGEgdG8gdHdlZXRzIGZyb20gdGhlIGRheSBvZiB0aGUgZGViYXRlIGFuZCBjdXQgZG93biBzb21lIGNvbHVtbnMgSSBkb24ndCBuZWVkIHVzaW5nIHRoZSBgc2VsZWN0KClgLCBgJT4lYCwgYW5kIGBmaWx0ZXIoKWAgZnVuY3Rpb25zIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZS4NCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFRSVUV9DQp0d2VldHMgPC0gdHdlZXRzX3JhdyU+JQ0KICBzZWxlY3Qoc2NyZWVuX25hbWUsIGNyZWF0ZWRfYXQsIHRleHQsIHJldHdlZXRfY291bnQsIGZhdm9yaXRlX2NvdW50LCBtZW50aW9uc19zY3JlZW5fbmFtZSwgaGFzaHRhZ3MpJT4lDQogIGZpbHRlcihjcmVhdGVkX2F0ID49ICcyMDE3LTA5LTE5JykNCg0Kc3RyKHR3ZWV0cykNCmBgYA0KDQojIyBNZW50aW9ucw0KTm93IHdlIGhhdmUgYSBjbGVhbiwgcmljaCBzZXQgb2YgdHdlZXRzIHRvIGFuYWx5emUhIEZpcnN0LCBJIHdhbnQgdG8ganVzdCBsb29rIGF0IHR3ZWV0IHZvbHVtZSBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhlIGRheS4gSSBjYW4gZG8gdGhhdCB3aXRoIGEgbGluZSBncmFwaCB1c2luZyBnZ3Bsb3QuIEZpcnN0IEknbGwgYWdncmVnYXRlIHR3ZWV0cyBieSBtaW51dGUgYW5kIHRoZW4gcGxvdCB0aGVtLiBOb3Qgc3VycHJpc2luZ2x5LCB0aGVyZSB3YXMgYSBzcGlrZSBpbiB0d2VldHMgcmVsYXRlZCB0byB0aGUgZGViYXRlIGR1cmluZyB0aGUgZGViYXRlISANCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFRSVUV9DQpsaWJyYXJ5KGZsdXgpDQoNCnR3ZWV0c19ieV9taW4gPC0gdHdlZXRzJT4lDQogIG11dGF0ZShtaW5zID0gcm91bmQoY3JlYXRlZF9hdCwgJ21pbnMnKSklPiUNCiAgZ3JvdXBfYnkobWlucyklPiUNCiAgc3VtbWFyaXNlKG4gPSBuKCkpJT4lDQogIGZpbHRlcihtaW5zID4gJzIwMTctMDktMTkgMTc6MDA6MDAnKQ0KDQpnZ3Bsb3QodHdlZXRzX2J5X21pbiwgYWVzKHggPSBtaW5zLCB5ID0gbikpKw0KICBnZW9tX2xpbmUoY29sb3IgPSB5YXpfY29sc1szXSwgc2l6ZSA9IDEpKw0KICBsYWJzKHggPSAnTWludXRlIChVVEMpJywNCiAgICAgICB5ID0gJ051bWJlciBvZiBUd2VldHMnLA0KICAgICAgIHRpdGxlID0gJ1R3ZWV0IEZyZXF1ZW5jeSBieSBUaW1lJywNCiAgICAgICBzdWJ0aXRsZSA9ICdUd2VldHMgdXNpbmcgI1ZBR292IG9yICNWQUdvdkRlYmF0ZSBvbiBkZWJhdGUgZGF5JykrDQogIHRoZW1lX3lheigpDQpgYGANCg0KTGV0J3MgZGlnIGEgYml0IGRlZXBlciBhbmQgbG9vayBhdCBob3cgbWFueSB0d2VldHMgbWVudGlvbiBlYWNoIGNhbmRpZGF0ZS4gQWdhaW4sIEknbGwgY3JlYXRlIGFuIGFnZ3JlZ2F0ZWQgZGF0YWZyYW1lIG9mIHR3ZWV0cyBieSBjYW5kaWRhdGUgYnkgbWludXRlLCBidXQgdGhpcyB0aW1lIEkgbmVlZCB0byB0YWtlIG9uZSBleHRyYSBzdGVwIGFuZCBjcmVhdGUgYSBmbGFnIHZhcmlhYmxlIGZvciBtZW50aW9ucyBvZiBbQFJhbHBoTm9ydGhhbV0oaHR0cHM6Ly90d2l0dGVyLmNvbS9SYWxwaE5vcnRoYW0pIGFuZCBbQEVkV0dpbGxlc3BpZV0oaHR0cHM6Ly90d2l0dGVyLmNvbS9FZFdHaWxsZXNwaWUpIG9yIGJvdGguIA0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvID0gVFJVRX0NCnR3ZWV0c19jYW5kaWRhdGVfbWludXRlIDwtIHR3ZWV0cyU+JQ0KICBtdXRhdGUoY2FuZGlkYXRlX21lbnRpb24gPSBpZmVsc2UoZ3JlcGwoJ1JhbHBoTm9ydGhhbScsIG1lbnRpb25zX3NjcmVlbl9uYW1lKSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICYgZ3JlcGwoJ0VkV0dpbGxlc3BpZScsIG1lbnRpb25zX3NjcmVlbl9uYW1lKSwgJ0JvdGgnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCdSYWxwaE5vcnRoYW0nLCBtZW50aW9uc19zY3JlZW5fbmFtZSksJ05vcnRoYW0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgnRWRXR2lsbGVzcGllJywgbWVudGlvbnNfc2NyZWVuX25hbWUpLCAnR2lsbGVzcGllJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ05laXRoZXInKSkpLA0KICAgICAgICAgbWlucyA9IHJvdW5kKGNyZWF0ZWRfYXQsICdtaW5zJykpJT4lDQogIGdyb3VwX2J5KGNhbmRpZGF0ZV9tZW50aW9uLCBtaW5zKSU+JQ0KICBzdW1tYXJpc2UobiA9IG4oKSklPiUNCiAgZmlsdGVyKG1pbnMgPiAnMjAxNy0wOS0xOSAxNzowMDowMCcpDQoNCmdncGxvdCh0d2VldHNfY2FuZGlkYXRlX21pbnV0ZSwgYWVzKHggPSBtaW5zLCB5ID0gbiwgY29sb3IgPSBjYW5kaWRhdGVfbWVudGlvbikpKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpKw0KICBsYWJzKHggPSAnTWludXRlIChVVEMpJywNCiAgICAgICB5ID0gJ051bWJlciBvZiBUd2VldHMnLA0KICAgICAgIHRpdGxlID0gJ1R3ZWV0IEZyZXF1ZW5jeSBieSBUaW1lIGFuZCBDYW5kaWRhdGUnLA0KICAgICAgIHN1YnRpdGxlID0gJ1R3ZWV0cyB1c2luZyAjVkFHb3Ygb3IgI1ZBR292RGViYXRlIG9uIGRlYmF0ZSBkYXknKSsNCiAgdGhlbWVfeWF6KCkrDQogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gJ0NhbmRpZGF0ZSBNZW50aW9uZWQnLCB2YWx1ZXMgPSB5YXpfY29sc1tjKDQsMiwzLDEpXSkNCmBgYA0KDQpNb3N0IG9mIHRoZSB0aW1lLCBwZW9wbGUgdHdlZXRpbmcgYWJvdXQgdGhlIGRlYmF0ZSB3ZXJlbid0IG5lY2Vzc2FyaWx5IG1lbnRpb25pbmcgZWl0aGVyIG9yIGJvdGggY2FuZGlkYXRlcyEsIGJ1dCB0aGVyZSB3ZXJlIHNvbWUgY2xlYXIgbW9tZW50cyB3aGVuIEdpbGxlc3BpZSBvciBOb3J0aGFtIHBvcHBlZC4gSSBkaWRuJ3Qga2VlcCBhIHRpbWVkIHRyYW5zY3JpcHQsIHRob3VnaCwgc28gaWYgeW91J3JlIHJlYWRpbmcgdGhpcyBhbmQgaGF2ZSBzb21lIGlkZWFzIG9mIHdoYXQgdGhlIGNhbmRpZGF0ZXMgd2VyZSB0YWxraW5nIGFib3V0IGF0IHRob3NlIHRpbWVzLCBsZXQgbWUga25vdyENCg0KIyMgSGFzaHRhZ3MNCk5vdyBJIHdhbnQgdG8gbG9vayBhdCBoYXNodGFnIGZyZXF1ZW5jeS4gVGhlIGBzZWFyY2hfdHdlZXRzYCBvdXRwdXQgcHJvdmlkZXMgYSB0aWR5IGZpZWxkIGZvciBoYXNodGFncywgYnV0IHdlIHdhbnQgdG8gZ2V0IGEgY291bnQgb2YgZWFjaCBpbmRpdmlkdWFsIGhhc2h0YWcgdXNlZCBkdXJpbmcgdGhlIGRlYmF0ZSBzbyB3ZSB3YW50IGVhY2ggaGFzaHRhZyBvbiBpdHMgb3duIGxpbmUgb2YgYSBkYXRhZnJhbWUuIFRoZSBgdGlkeXRleHRgIHBhY2thZ2UgaGFzIHNvbWUgaGFuZHkgZnVuY3Rpb25zIGZvciB0aGF0IQ0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvID0gVFJVRX0NCmhhc2h0YWdfY291bnQgPC0gdHdlZXRzJT4lDQogIG11dGF0ZShjYW5kaWRhdGVfbWVudGlvbiA9IGlmZWxzZShncmVwbCgnUmFscGhOb3J0aGFtJywgbWVudGlvbnNfc2NyZWVuX25hbWUpIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJiBncmVwbCgnRWRXR2lsbGVzcGllJywgbWVudGlvbnNfc2NyZWVuX25hbWUpLCAnQm90aCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoJ1JhbHBoTm9ydGhhbScsIG1lbnRpb25zX3NjcmVlbl9uYW1lKSwnTm9ydGhhbScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCdFZFdHaWxsZXNwaWUnLCBtZW50aW9uc19zY3JlZW5fbmFtZSksICdHaWxsZXNwaWUnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnTmVpdGhlcicpKSksDQogICAgICAgICBtaW5zID0gcm91bmQoY3JlYXRlZF9hdCwgJ21pbnMnKSklPiUNCiAgZmlsdGVyKGNyZWF0ZWRfYXQgPiAnMjAxNy0wOS0xOSAxNzowMDowMCcgJiAhaXMubmEoY2FuZGlkYXRlX21lbnRpb24pKSU+JQ0KICB1bm5lc3RfdG9rZW5zKGh0cywgaGFzaHRhZ3MpJT4lDQogIGdyb3VwX2J5KGh0cywgY2FuZGlkYXRlX21lbnRpb24pJT4lDQogIHN1bW1hcmlzZShuID0gbigpKSU+JQ0KICBmaWx0ZXIoIWh0cyAlaW4lIGMoJ3ZhZ292ZGViYXRlJywndmFnb3YnKQ0KICAgICAgICAgJiBuID4gMjAgJiAhaXMubmEoaHRzKSkNCg0KZ2dwbG90KGhhc2h0YWdfY291bnQsIGFlcyh4ID0gcmVvcmRlcihodHMsIG4pLCB5ID0gbiwgZmlsbCA9IGNhbmRpZGF0ZV9tZW50aW9uKSkrDQogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSsNCiAgY29vcmRfZmxpcCgpKw0KICB0aGVtZV95YXooKSsNCiAgbGFicyh5ID0gJ0hhc2h0YWcgRnJlcXVlbmN5JywgeCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICB0aXRsZSA9ICdIYXNodGFnIEZyZXF1ZW5jeSBieSBDYW5kaWRhdGUnLA0KICAgICAgIHN1YnRpdGxlID0gJ0hhc2h0YWdzIGxpbWl0ZWQgdG8gdGhvc2UgdXNlZCBtb3JlIHRoYW4gMjAgdGltZXMgYWZ0ZXIgMzowMHBtIEVTVCBvbiBEZWJhdGUgRGF5JykrDQogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAnQ2FuZGlkYXRlIE1lbnRpb25lZCcsIHZhbHVlcyA9IHlhel9jb2xzW2MoMiwzLDEpXSkNCmBgYA0KDQojIyBXb3JkIERpc3RpbmN0aXZlbmVzcyBhbmQgU2VudGltZW50IEFuYWx5c2lzDQpMYXN0bHkgSSB3YW50IHRvIGxvb2sgYXQgdHdlZXQgc2VudGltZW50cy4gVGhlIGB0aWR5dGV4dGAgcGFja2FnZSBoYXMgZm91ciBzZW50aW1lbnQgbGV4aWNvbnMgYXZhaWxhYmxlLiBUaGUgYGJpbmdgIGFuZCBgbnJjYCBsZXhpY29ucyBib3RoIG9mZmVyIHByZXR0eSBnb29kIGNsYXNzaWZpY2F0aW9ucyBvZiB3b3JkcyBhcyBlaXRoZXIgcG9zaXRpdmUgb3IgbmVnYXRpdmUsIGFsdGhvdWdoIFtgbnJjYCB0ZW5kcyB0byBlcnIgb24gdGhlIHNpZGUgb2YgYmVpbmcgb3Zlcmx5IHBvc2l0aXZlXShodHRwOi8vcnB1YnMuY29tL2pvc2h5YXptYW4vc2VudGltZW50LWFuYWx5c2lzLWxleGljb24tY29tcGFyaXNvbikuIFRoZSBgbnJjYCBsZXhpY29uIGFsc28gY2xhc3NpZmllcyB3b3JkcyBhcyBpbmRpY2F0aXZlIG9mICJ0cnVzdCIsICJmZWFyIiwgInNhZG5lc3MiLCAiYW5nZXIiLCAic3VycHJpc2UiLCAicG9zaXRpdmUiLCAiZGlzZ3VzdCIsICJqb3kiLCBhbmQgImFudGljaXBhdGlvbiIgYXMgd2VsbC4gV2UgY2FuIHVubmVzdCB0d2VldCB3b3JkcyB0aGUgc2FtZSB3YXkgd2UgZGlkIHdpdGggaGFzaHRhZ3MgYW5kIHRoZW4gdXNlIGEgc2ltcGxlIGpvaW4gdG8gYXBwZW5kIHRoZSBwb3NpdGl2ZS9uZWdhdGl2ZSBzY29yZXMgZnJvbSB0aGUgYGJpbmdgIGxleGljb24gYW5kIGFsbCBvZiB0aGUgc2VudGltZW50cyBmcm9tIHRoZSBgbnJjYCBsZXhpY29uLg0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvID0gVFJVRX0NCm5yYyA8LSBzZW50aW1lbnRzJT4lDQogIGZpbHRlcighc2VudGltZW50ICVpbiUgYygncG9zaXRpdmUnLCduZWdhdGl2ZScpDQogICAgICAgICAmIGxleGljb24gPT0gJ25yYycpJT4lDQogIHNlbGVjdCh3b3JkLCBucmMgPSBzZW50aW1lbnQpDQpiaW5nIDwtIHNlbnRpbWVudHMlPiUNCiAgZmlsdGVyKGxleGljb24gPT0gJ2JpbmcnKSU+JQ0KICBtdXRhdGUoYmluZyA9IGlmZWxzZShzZW50aW1lbnQgPT0gJ3Bvc2l0aXZlJywxLC0xKSklPiUNCiAgc2VsZWN0KHdvcmQsIGJpbmcpDQoNCiMgQSBmZXcgc3RlcHMgdG8gY2xlYW4gYW5kIGZvcm1hdCB0aGUgdHdlZXQgdGV4dCBhbmQgdW5uZXN0IGVhY2ggd29yZA0KcmVnIDwtICIoW15BLVphLXpcXGQjQCddfCcoPyFbQS1aYS16XFxkI0BdKSkiDQp0d2VldF93b3JkcyA8LSB0d2VldHMlPiUNCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KHRleHQsICdeIicpKSAlPiUNCiAgbXV0YXRlKGNhbmRpZGF0ZV9tZW50aW9uID0gaWZlbHNlKGdyZXBsKCdSYWxwaE5vcnRoYW0nLCBtZW50aW9uc19zY3JlZW5fbmFtZSkgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmIGdyZXBsKCdFZFdHaWxsZXNwaWUnLCBtZW50aW9uc19zY3JlZW5fbmFtZSksICdCb3RoJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgnUmFscGhOb3J0aGFtJywgbWVudGlvbnNfc2NyZWVuX25hbWUpLCdOb3J0aGFtJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoJ0VkV0dpbGxlc3BpZScsIG1lbnRpb25zX3NjcmVlbl9uYW1lKSwgJ0dpbGxlc3BpZScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdOZWl0aGVyJykpKSwNCiAgICAgICAgIHRleHQgPSBzdHJfcmVwbGFjZV9hbGwodGV4dCwgJ2h0dHBzOi8vdC5jby9bQS1aYS16XFxkXSt8JmFtcDsnLCAnJyksDQogICAgICAgICBtaW5zID0gcm91bmQoY3JlYXRlZF9hdCwgJzE1bWlucycpKSU+JQ0KICBmaWx0ZXIoY3JlYXRlZF9hdCA+ICcyMDE3LTA5LTE5IDE3OjAwOjAwJyAmICFpcy5uYShjYW5kaWRhdGVfbWVudGlvbikpJT4lDQogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCwgdG9rZW4gPSAicmVnZXgiLCBwYXR0ZXJuID0gcmVnKSU+JQ0KICBmaWx0ZXIoIXdvcmQgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICBzdHJfZGV0ZWN0KHdvcmQsICJbYS16XSIpKQ0KDQpucmNfdHdlZXRfd29yZHMgPC0gaW5uZXJfam9pbih0d2VldF93b3JkcywgbnJjLCBieSA9ICd3b3JkJykNCmJpbmdfdHdlZXRfd29yZHMgPC0gaW5uZXJfam9pbih0d2VldF93b3JkcywgYmluZywgYnkgPSAnd29yZCcpDQpgYGANCg0KRmlyc3QgSSB3YW50IHRvIGxvb2sgYXQgdHdlZXQgc2VudGltZW50IG92ZXIgdGltZSBicm9rZW4gb3V0IGJ5IGNhbmRpZGF0ZSBtZW50aW9uLiBJdCBsb29rcyBsaWtlIG5ldCBzZW50aW1lbnRzIHdlcmUgZ2VuZXJhbGx5IG1vcmUgcG9zaXRpdmUgYmVmb3JlIHRoZSBkZWJhdGUgdGhhbiBkdXJpbmcgb3IgYWZ0ZXIsIHdoaWNoIG1ha2VzIHNlbnNlIGJlY2F1c2UgcHJlLWRlYmF0ZSB0d2VldHMgd2VyZSBtb3N0bHkgKGFuZWNkb3RhbGx5KSBhYm91dCBoeXBpbmcgb25lJ3MgY2FuZGlkYXRlIHJhdGhlciB0aGFuIGhpdHRpbmcgdGhlIG90aGVyLiBEdXJpbmcgdGhlIGRlYmF0ZSwgZXZlcnlvbmUncyB0d2VldHMgZ290IG1vcmUgbmVnYXRpdmUsIGJ1dCBOb3J0aGFtIG1lbnRpb25zIHJlbWFpbmVkIHRoZSBtb3N0IHBvc2l0aXZlLiANCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFRSVUV9DQpnZ3Bsb3QoYmluZ190d2VldF93b3JkcyU+JWdyb3VwX2J5KG1pbnMsIGNhbmRpZGF0ZV9tZW50aW9uKSU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShtZWFuX3NlbnQgPSBtZWFuKGJpbmcsIG5hLnJtID0gVCksIG4gPSBuKCkpLA0KICBhZXMoeCA9IG1pbnMsIHkgPSBtZWFuX3NlbnQsIGNvbG9yID0gY2FuZGlkYXRlX21lbnRpb24sIHNpemUgPSBuKSkrDQogIGdlb21fbGluZShzaXplID0gMSkrDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAnZGFzaGVkJykrDQogIHlsaW0oLTEsMSkrDQogIHRoZW1lX3lheigpKw0KICBsYWJzKHggPSAnVGltZSAoVVRDKScsIHkgPSAnQXZlcmFnZSBUd2VldCBTZW50aW1lbnQnLA0KICAgICAgIHRpdGxlID0gJ1R3ZWV0IFNlbnRpbWVudHMgYnkgQ2FuZGlkYXRlIE92ZXIgVGltZScpKw0KICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICdDYW5kaWRhdGUgTWVudGlvbmVkJywgdmFsdWVzID0geWF6X2NvbHNbYyg0LDIsMywxKV0pKw0KICBzY2FsZV9zaXplX2NvbnRpbnVvdXMobmFtZSA9ICdOdW1iZXIgb2YgVHdlZXRzJykNCmBgYA0KDQpUbyBleGFtaW5lIE5SQyBzZW50aW1lbnRzLCB3ZSBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2Ygd29yZHMgd2l0aGluIGVhY2ggY2FuZGlkYXRlIG1lbnRpb24gZ3JvdXBpbmcgdGhhdCBtYXRjaCBlYWNoIHBvdGVudGlhbCBzZW50aW1lbnQuIEFudGljaXBhdGlvbiBwb3BzIGFjcm9zcyB0aGUgYm9hcmQsIGxpa2VseSBiZWNhdXNlIG9mIHR3ZWV0cyBsZWFkaW5nIHVwIHRoZSB0aGUgZGViYXRlLiBBc2lkZSBmcm9tIHRoYXQsIGl0J3Mgbm90YWJsZSB0aGF0IEdpbGxlc3BpZSBsZWFkcyBzbGlnaHRseSBvbiBUcnVzdCBhbmQgTm9ydGhhbSBsZWFkcyBzbGlnaHRseSBvbiBKb3kuIFR3ZWV0cyB0aGF0IG1lbnRpb24gYm90aCBjYW5kaWRhdGVzIGFyZSB0aGUgYW5ncmllc3QuIA0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvID0gVFJVRX0NCmdncGxvdChucmNfdHdlZXRfd29yZHMlPiUNCiAgICAgICAgIGdyb3VwX2J5KG1pbnMsIGNhbmRpZGF0ZV9tZW50aW9uLCBucmMpJT4lDQogICAgICAgICBzdW1tYXJpc2UobiA9IG4oKSklPiUNCiAgICAgICAgIGdyb3VwX2J5KGNhbmRpZGF0ZV9tZW50aW9uKSU+JQ0KICAgICAgICAgbXV0YXRlKHBjdCA9IG4vc3VtKG4pKSU+JQ0KICAgICAgICAgdW5ncm91cCgpLA0KICBhZXMoeCA9IG5yYywgeSA9IHBjdCwgZmlsbCA9IGNhbmRpZGF0ZV9tZW50aW9uKSkrDQogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSsNCiAgY29vcmRfZmxpcCgpKw0KICBmYWNldF93cmFwKH5jYW5kaWRhdGVfbWVudGlvbikrDQogIHRoZW1lX3lheigpKw0KICBsYWJzKHkgPSAnU2VudGltZW50JywgeCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICB0aXRsZSA9ICdUd2VldCBTZW50aW1lbnRzIGJ5IENhbmRpZGF0ZScpKw0KICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gJ0NhbmRpZGF0ZSBNZW50aW9uZWQnLCB2YWx1ZXMgPSB5YXpfY29sc1tjKDQsMiwzLDEpXSkNCmBgYA0K