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!
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