In this project, I cleaned and analyzed data collected from Donald Trump’s Twitter account using an API.
Loading libraries
library(dplyr)
library(twitteR)
library(ROAuth)
library(ggplot2)
library(RColorBrewer)
library(wordcloud)
Retrieving 3200 most recent tweets by Donald Trump
consumer_key = "s8OWnvqLPckxI0JBcrnZR6rLM"
consumer_secret = "Zve0oxqK7PuU3MIwN0Snz2wvLWmK100hZ2hxczKGMmqxAo0oZe"
access_token = "111199907-Jpm02lPlAPXk3tenVitYPH2TObjVcZSz3AOu7qGs"
access_secret ="616o99du0NZt3bGrHYQBJ8SWOb3V7QTAZdEVDYk6ajQRl"
requestURL = "https://api.twitter.com/oauth/request_token"
accessURL ="https://api.twitter.com/oauth/access_token"
authURL ="https://api.twitter.com/oauth/authorize"
setup_twitter_oauth(consumer_key, consumer_secret, access_token, access_secret)
[1] "Using direct authentication"
tw=userTimeline("realDonaldTrump", n=3000, includeRts=TRUE)
df=twListToDF(tw)
What is his most commonly used platform to tweet?

Tweets from his account are mostly from an iPhone

df$statusSource = substr(df$statusSource, regexpr(">", df$statusSource) + 1, regexpr("</a>", df$statusSource) -  1)
ggplot(df, aes(x=statusSource), fill()) +
  geom_bar() +
  labs(title="Twitter platforms by @realDonaldTrump") +
  ylab(label="Number of tweets") +
  xlab(label="Type of platform")

What time of the day does he usually tweet?

He is most likely to tweet during the afternoon (11am-3pm) and less during Tuesdays and Thursdays.

df$timeonly = as.numeric(difftime(df$created, trunc(df$created, "days"), attr(df$created, "tzone"), "hours"))
ggplot(df, aes(x=timeonly)) +
  geom_histogram(aes(y=..density..), bins=35, colour="#F8766D", fill="#F8766D") +
  geom_density() +
  labs(title="Tweets by time of day by @realDonaldTrump") +
  xlab(label="Hour of day") 


ggplot(as.data.frame(table(weekdays(df[,"created"]))[c("Monday", "Tuesday","Wednesday", "Thursday", "Friday","Saturday", "Sunday")])) +
  geom_bar(aes(x=Var1, y=Freq), stat="identity") +
  labs(title="Tweet frequency by day by @realDonaldTrump") +
  xlab(label="Days of the week") +
  ylab(label="Frequency")

What are the most frequently used words in his tweets?

After removing characters that are not letters, additional spaces between words, changing all words to lower-case etc.., the wordcloud shows that the two most frequent words in his tweets are “great” and “president”

Words2Use = unlist(strsplit(df$text," "))
sentence1 = tolower(Words2Use)
sentence1_5 = gsub("amp", " ", sentence1)
sentence1_6 = gsub("rt", " ", sentence1_5)
sentence2 = gsub("(RT|via)((?:\\b\\W*@\\w+)+)", " ", sentence1_6)
sentence3 = gsub("@\\w+", " ", sentence2)
sentence4 = gsub("(?!')[[:punct:]]", "", sentence3,perl=T)
sentence5 = gsub("[[:cntrl:]]", "", sentence4)
sentence6 = gsub("[[:digit:]]", "", sentence5)
sentence7 = iconv(sentence5, "ASCII", "UTF-8", sub = "") 
sentence9 = gsub("http\\w+", "", sentence7)
sentence10 = gsub("[ \t]{2,}", " ", sentence9)
sentence11 = gsub("^\\s+|\\s+$", "", sentence10) 
word.list = strsplit(sentence11, " ")
words = unlist(word.list)
NotStopWords = words[!words %in% tm::stopwords(kind = "english")]
tbl = table(NotStopWords)
layout(matrix(c(1, 2), nrow=2), heights=c(1, 4))
par(mar=rep(0, 4))
plot.new()
text(x=0.5, y=0.5, "Commonly used words from tweets by @realDonaldTrump" )
wordcloud(names(sort(tbl,decreasing=TRUE)[1:40]),sort(tbl,decreasing=TRUE)[1:50],
          colors = rainbow(9), min.freq = 100)


tbldf=as.data.frame(sort(tbl,decreasing=TRUE)[1:25])
tbldf %>%
  filter(Freq>10) -> tbldf2
ggplot(tbldf2, aes(x=reorder(NotStopWords, Freq), y=Freq)) +
  geom_bar(stat="identity") +
  coord_flip() + 
  ylab(label="Occurences") +
  xlab(label="Word") +
  labs(title="Frequently used words in tweets by @realDonaldTrump")

Sentiment Analysis

The first graph shows a sentiment analysis of his tweets during a period of time. The second graph shows that the sentiment scores for his tweets follow a normal distribution, with an average score of 0.2304609 which is close to neutral. The list of positive and negative words were obtained from https://www.cs.uic.edu/~liub/FBS/sentiment-analysis.html.

pos = scan("positive-words.txt", what = "character",comment.char = ";")
Read 2006 items
neg = scan("negative-words.txt", what = "character",comment.char = ";")
Read 4783 items
getSentimentScore = function(tweet_text, pos, neg) {

  sentence = gsub("(RT|via)((?:\\b\\W*@\\w+)+)", "", tweet_text)

  sentence = gsub("@\\w+", "", sentence)

  sentence = gsub("[[:punct:]]", "", sentence)

  sentence = gsub("[[:cntrl:]]", "", sentence)

  sentence = gsub("[[:digit:]]", "", sentence) 

  sentence = gsub("http\\w+", "", sentence)

  sentence = gsub("^\\s+|\\s+$", "", sentence)

  sentence = iconv(sentence, "UTF-8", "ASCII", sub = "")
  sentence = tolower(sentence)

  word.list = strsplit(sentence, " ")

  score = numeric(length(word.list)) # loop through each tweet
  positive = numeric(length(word.list))
  negative = numeric(length(word.list))
  for (i in 1:length(word.list)) {

    pos.matches = match(word.list[[i]], pos) 
    neg.matches = match(word.list[[i]], neg)

    pos.matches = !is.na(pos.matches)
    neg.matches = !is.na(neg.matches)

    score[i] = sum(pos.matches) - sum(neg.matches)
    positive[i] = sum(pos.matches)
    negative[i] = sum(neg.matches)
  }
  return(data.frame(positive_score = positive, 
                    negative_score = negative,
                    sentiment_score = score))
}
output = getSentimentScore(df$text, pos, neg)
df$sentiment = output[, "sentiment_score"]
df$pos_sentiment = output[, "positive_score"]
df$neg_sentiment = output[, "negative_score"]
df$day = as.Date(cut(df$created, breaks = "day"))
df %>% 
  group_by(day) %>%
  summarise(meanPos = mean(pos_sentiment), meanNeg = mean(neg_sentiment), meanSent = mean(sentiment)) -> sentiment_byday

ggplot(sentiment_byday,aes(x=day)) +
  geom_line(aes(y=meanNeg,colour="Positive words")) +
  geom_line(aes(y=meanPos, colour="Negative words")) +
  scale_colour_manual(values=c(`Positive words`="#619CFF", `Negative words`="#F8766D")) +
  theme(legend.justification = c(1, 1), legend.position = c(.95, .95), legend.title=element_blank()) +
  xlab(label="Date") +
  ylab(label="Sentiment Score")


ggplot(output, aes(x=sentiment_score)) +
  geom_density(bw=.5)  +
  labs(title="Distribution of sentiment scores of tweets by @realDonaldTrump") +
  geom_vline(xintercept = mean(output$sentiment_score), color = "red", linetype = "dashed") +
  xlab(label="Sentiment Score") +
  ylab(label="Density")

References

; Minqing Hu and Bing Liu. “Mining and Summarizing Customer Reviews.” ; Proceedings of the ACM SIGKDD International Conference on Knowledge ; Discovery and Data Mining (KDD-2004), Aug 22-25, 2004, Seattle, ; Washington, USA, ; Bing Liu, Minqing Hu and Junsheng Cheng. “Opinion Observer: Analyzing ; and Comparing Opinions on the Web.” Proceedings of the 14th ; International World Wide Web conference (WWW-2005), May 10-14, ; 2005, Chiba, Japan.

LS0tCnRpdGxlOiAiVHdpdHRlciAoYW5kIFNlbnRpbWVudCkgQW5hbHlzaXMgb2YgRG9uYWxkIFRydW1wJ3MgVHdlZXRzIHVzaW5nIGFuIEFQSSIKQXV0aG9yOiAiQWxleGFuZGVyIExvIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKIyMjIyMgSW4gdGhpcyBwcm9qZWN0LCBJIGNsZWFuZWQgYW5kIGFuYWx5emVkIGRhdGEgY29sbGVjdGVkIGZyb20gRG9uYWxkIFRydW1wJ3MgVHdpdHRlciBhY2NvdW50IHVzaW5nIGFuIEFQSS4KCiMjIyMjIExvYWRpbmcgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHR3aXR0ZVIpCmxpYnJhcnkoUk9BdXRoKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHdvcmRjbG91ZCkKYGBgCgojIyMjIyBSZXRyaWV2aW5nIDMyMDAgbW9zdCByZWNlbnQgdHdlZXRzIGJ5IERvbmFsZCBUcnVtcApgYGB7cn0KY29uc3VtZXJfa2V5ID0gInM4T1dudnFMUGNreEkwSkJjcm5aUjZyTE0iCmNvbnN1bWVyX3NlY3JldCA9ICJadmUwb3hxSzdQdVUzTUl3TjBTbnoyd3ZMV21LMTAwaFoyaHhjektHTW1xeEFvMG9aZSIKYWNjZXNzX3Rva2VuID0gIjExMTE5OTkwNy1KcG0wMmxQbEFQWGszdGVuVml0WVBIMlRPYmpWY1pTejNBT3U3cUdzIgphY2Nlc3Nfc2VjcmV0ID0iNjE2bzk5ZHUwTlp0M2JHckhZUUJKOFNXT2IzVjdRVEFaZEVWRFlrNmFqUVJsIgpyZXF1ZXN0VVJMID0gImh0dHBzOi8vYXBpLnR3aXR0ZXIuY29tL29hdXRoL3JlcXVlc3RfdG9rZW4iCmFjY2Vzc1VSTCA9Imh0dHBzOi8vYXBpLnR3aXR0ZXIuY29tL29hdXRoL2FjY2Vzc190b2tlbiIKYXV0aFVSTCA9Imh0dHBzOi8vYXBpLnR3aXR0ZXIuY29tL29hdXRoL2F1dGhvcml6ZSIKc2V0dXBfdHdpdHRlcl9vYXV0aChjb25zdW1lcl9rZXksIGNvbnN1bWVyX3NlY3JldCwgYWNjZXNzX3Rva2VuLCBhY2Nlc3Nfc2VjcmV0KQp0dz11c2VyVGltZWxpbmUoInJlYWxEb25hbGRUcnVtcCIsIG49MzAwMCwgaW5jbHVkZVJ0cz1UUlVFKQpkZj10d0xpc3RUb0RGKHR3KQpgYGAKCiMjIyMjIFdoYXQgaXMgaGlzIG1vc3QgY29tbW9ubHkgdXNlZCBwbGF0Zm9ybSB0byB0d2VldD8gClR3ZWV0cyBmcm9tIGhpcyBhY2NvdW50IGFyZSBtb3N0bHkgZnJvbSBhbiBpUGhvbmUKYGBge3J9CmRmJHN0YXR1c1NvdXJjZSA9IHN1YnN0cihkZiRzdGF0dXNTb3VyY2UsIHJlZ2V4cHIoIj4iLCBkZiRzdGF0dXNTb3VyY2UpICsgMSwgcmVnZXhwcigiPC9hPiIsIGRmJHN0YXR1c1NvdXJjZSkgLSAgMSkKZ2dwbG90KGRmLCBhZXMoeD1zdGF0dXNTb3VyY2UpLCBmaWxsKCkpICsKICBnZW9tX2JhcigpICsKICBsYWJzKHRpdGxlPSJUd2l0dGVyIHBsYXRmb3JtcyBieSBAcmVhbERvbmFsZFRydW1wIikgKwogIHlsYWIobGFiZWw9Ik51bWJlciBvZiB0d2VldHMiKSArCiAgeGxhYihsYWJlbD0iVHlwZSBvZiBwbGF0Zm9ybSIpCmBgYAoKIyMjIyMgV2hhdCB0aW1lIG9mIHRoZSBkYXkgZG9lcyBoZSB1c3VhbGx5IHR3ZWV0PyAKSGUgaXMgbW9zdCBsaWtlbHkgdG8gdHdlZXQgZHVyaW5nIHRoZSBhZnRlcm5vb24gKDExYW0tM3BtKSBhbmQgbGVzcyBkdXJpbmcgVHVlc2RheXMgYW5kIFRodXJzZGF5cy4KYGBge3J9CmRmJHRpbWVvbmx5ID0gYXMubnVtZXJpYyhkaWZmdGltZShkZiRjcmVhdGVkLCB0cnVuYyhkZiRjcmVhdGVkLCAiZGF5cyIpLCBhdHRyKGRmJGNyZWF0ZWQsICJ0em9uZSIpLCAiaG91cnMiKSkKZ2dwbG90KGRmLCBhZXMoeD10aW1lb25seSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGJpbnM9MzUsIGNvbG91cj0iI0Y4NzY2RCIsIGZpbGw9IiNGODc2NkQiKSArCiAgZ2VvbV9kZW5zaXR5KCkgKwogIGxhYnModGl0bGU9IlR3ZWV0cyBieSB0aW1lIG9mIGRheSBieSBAcmVhbERvbmFsZFRydW1wIikgKwogIHhsYWIobGFiZWw9IkhvdXIgb2YgZGF5IikgCgpnZ3Bsb3QoYXMuZGF0YS5mcmFtZSh0YWJsZSh3ZWVrZGF5cyhkZlssImNyZWF0ZWQiXSkpW2MoIk1vbmRheSIsICJUdWVzZGF5IiwiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIsIlNhdHVyZGF5IiwgIlN1bmRheSIpXSkpICsKICBnZW9tX2JhcihhZXMoeD1WYXIxLCB5PUZyZXEpLCBzdGF0PSJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlPSJUd2VldCBmcmVxdWVuY3kgYnkgZGF5IGJ5IEByZWFsRG9uYWxkVHJ1bXAiKSArCiAgeGxhYihsYWJlbD0iRGF5cyBvZiB0aGUgd2VlayIpICsKICB5bGFiKGxhYmVsPSJGcmVxdWVuY3kiKQpgYGAKCiMjIyMjIFdoYXQgYXJlIHRoZSBtb3N0IGZyZXF1ZW50bHkgdXNlZCB3b3JkcyBpbiBoaXMgdHdlZXRzPwpBZnRlciByZW1vdmluZyBjaGFyYWN0ZXJzIHRoYXQgYXJlIG5vdCBsZXR0ZXJzLCBhZGRpdGlvbmFsIHNwYWNlcyBiZXR3ZWVuIHdvcmRzLCBjaGFuZ2luZyBhbGwgd29yZHMgdG8gbG93ZXItY2FzZSBldGMuLiwgdGhlIHdvcmRjbG91ZCBzaG93cyB0aGF0IHRoZSB0d28gbW9zdCBmcmVxdWVudCB3b3JkcyBpbiBoaXMgdHdlZXRzIGFyZSAiZ3JlYXQiIGFuZCAicHJlc2lkZW50IgpgYGB7cn0KV29yZHMyVXNlID0gdW5saXN0KHN0cnNwbGl0KGRmJHRleHQsIiAiKSkKc2VudGVuY2UxID0gdG9sb3dlcihXb3JkczJVc2UpCnNlbnRlbmNlMV81ID0gZ3N1YigiYW1wIiwgIiAiLCBzZW50ZW5jZTEpCnNlbnRlbmNlMV82ID0gZ3N1YigicnQiLCAiICIsIHNlbnRlbmNlMV81KQpzZW50ZW5jZTIgPSBnc3ViKCIoUlR8dmlhKSgoPzpcXGJcXFcqQFxcdyspKykiLCAiICIsIHNlbnRlbmNlMV82KQpzZW50ZW5jZTMgPSBnc3ViKCJAXFx3KyIsICIgIiwgc2VudGVuY2UyKQpzZW50ZW5jZTQgPSBnc3ViKCIoPyEnKVtbOnB1bmN0Ol1dIiwgIiIsIHNlbnRlbmNlMyxwZXJsPVQpCnNlbnRlbmNlNSA9IGdzdWIoIltbOmNudHJsOl1dIiwgIiIsIHNlbnRlbmNlNCkKc2VudGVuY2U2ID0gZ3N1YigiW1s6ZGlnaXQ6XV0iLCAiIiwgc2VudGVuY2U1KQpzZW50ZW5jZTcgPSBpY29udihzZW50ZW5jZTUsICJBU0NJSSIsICJVVEYtOCIsIHN1YiA9ICIiKSAKc2VudGVuY2U5ID0gZ3N1YigiaHR0cFxcdysiLCAiIiwgc2VudGVuY2U3KQpzZW50ZW5jZTEwID0gZ3N1YigiWyBcdF17Mix9IiwgIiAiLCBzZW50ZW5jZTkpCnNlbnRlbmNlMTEgPSBnc3ViKCJeXFxzK3xcXHMrJCIsICIiLCBzZW50ZW5jZTEwKSAKd29yZC5saXN0ID0gc3Ryc3BsaXQoc2VudGVuY2UxMSwgIiAiKQp3b3JkcyA9IHVubGlzdCh3b3JkLmxpc3QpCk5vdFN0b3BXb3JkcyA9IHdvcmRzWyF3b3JkcyAlaW4lIHRtOjpzdG9wd29yZHMoa2luZCA9ICJlbmdsaXNoIildCnRibCA9IHRhYmxlKE5vdFN0b3BXb3JkcykKbGF5b3V0KG1hdHJpeChjKDEsIDIpLCBucm93PTIpLCBoZWlnaHRzPWMoMSwgNCkpCnBhcihtYXI9cmVwKDAsIDQpKQpwbG90Lm5ldygpCnRleHQoeD0wLjUsIHk9MC41LCAiQ29tbW9ubHkgdXNlZCB3b3JkcyBmcm9tIHR3ZWV0cyBieSBAcmVhbERvbmFsZFRydW1wIiApCndvcmRjbG91ZChuYW1lcyhzb3J0KHRibCxkZWNyZWFzaW5nPVRSVUUpWzE6NDBdKSxzb3J0KHRibCxkZWNyZWFzaW5nPVRSVUUpWzE6NTBdLAogICAgICAgICAgY29sb3JzID0gcmFpbmJvdyg5KSwgbWluLmZyZXEgPSAxMDApCgp0YmxkZj1hcy5kYXRhLmZyYW1lKHNvcnQodGJsLGRlY3JlYXNpbmc9VFJVRSlbMToyNV0pCnRibGRmICU+JQogIGZpbHRlcihGcmVxPjEwKSAtPiB0YmxkZjIKZ2dwbG90KHRibGRmMiwgYWVzKHg9cmVvcmRlcihOb3RTdG9wV29yZHMsIEZyZXEpLCB5PUZyZXEpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgY29vcmRfZmxpcCgpICsgCiAgeWxhYihsYWJlbD0iT2NjdXJlbmNlcyIpICsKICB4bGFiKGxhYmVsPSJXb3JkIikgKwogIGxhYnModGl0bGU9IkZyZXF1ZW50bHkgdXNlZCB3b3JkcyBpbiB0d2VldHMgYnkgQHJlYWxEb25hbGRUcnVtcCIpCmBgYAoKIyMjIyMgU2VudGltZW50IEFuYWx5c2lzClRoZSBmaXJzdCBncmFwaCBzaG93cyBhIHNlbnRpbWVudCBhbmFseXNpcyBvZiBoaXMgdHdlZXRzIGR1cmluZyBhIHBlcmlvZCBvZiB0aW1lLiBUaGUgc2Vjb25kIGdyYXBoIHNob3dzIHRoYXQgdGhlIHNlbnRpbWVudCBzY29yZXMgZm9yIGhpcyB0d2VldHMgZm9sbG93IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgd2l0aCBhbiBhdmVyYWdlIHNjb3JlIG9mIDAuMjMwNDYwOSB3aGljaCBpcyBjbG9zZSB0byBuZXV0cmFsLiBUaGUgbGlzdCBvZiBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgd29yZHMgd2VyZSBvYnRhaW5lZCBmcm9tIGh0dHBzOi8vd3d3LmNzLnVpYy5lZHUvfmxpdWIvRkJTL3NlbnRpbWVudC1hbmFseXNpcy5odG1sLiAKYGBge3J9CnBvcyA9IHNjYW4oInBvc2l0aXZlLXdvcmRzLnR4dCIsIHdoYXQgPSAiY2hhcmFjdGVyIixjb21tZW50LmNoYXIgPSAiOyIpCm5lZyA9IHNjYW4oIm5lZ2F0aXZlLXdvcmRzLnR4dCIsIHdoYXQgPSAiY2hhcmFjdGVyIixjb21tZW50LmNoYXIgPSAiOyIpCmdldFNlbnRpbWVudFNjb3JlID0gZnVuY3Rpb24odHdlZXRfdGV4dCwgcG9zLCBuZWcpIHsKCiAgc2VudGVuY2UgPSBnc3ViKCIoUlR8dmlhKSgoPzpcXGJcXFcqQFxcdyspKykiLCAiIiwgdHdlZXRfdGV4dCkKCiAgc2VudGVuY2UgPSBnc3ViKCJAXFx3KyIsICIiLCBzZW50ZW5jZSkKCiAgc2VudGVuY2UgPSBnc3ViKCJbWzpwdW5jdDpdXSIsICIiLCBzZW50ZW5jZSkKCiAgc2VudGVuY2UgPSBnc3ViKCJbWzpjbnRybDpdXSIsICIiLCBzZW50ZW5jZSkKCiAgc2VudGVuY2UgPSBnc3ViKCJbWzpkaWdpdDpdXSIsICIiLCBzZW50ZW5jZSkgCgogIHNlbnRlbmNlID0gZ3N1YigiaHR0cFxcdysiLCAiIiwgc2VudGVuY2UpCgogIHNlbnRlbmNlID0gZ3N1YigiXlxccyt8XFxzKyQiLCAiIiwgc2VudGVuY2UpCgogIHNlbnRlbmNlID0gaWNvbnYoc2VudGVuY2UsICJVVEYtOCIsICJBU0NJSSIsIHN1YiA9ICIiKQogIHNlbnRlbmNlID0gdG9sb3dlcihzZW50ZW5jZSkKCiAgd29yZC5saXN0ID0gc3Ryc3BsaXQoc2VudGVuY2UsICIgIikKCiAgc2NvcmUgPSBudW1lcmljKGxlbmd0aCh3b3JkLmxpc3QpKSAjIGxvb3AgdGhyb3VnaCBlYWNoIHR3ZWV0CiAgcG9zaXRpdmUgPSBudW1lcmljKGxlbmd0aCh3b3JkLmxpc3QpKQogIG5lZ2F0aXZlID0gbnVtZXJpYyhsZW5ndGgod29yZC5saXN0KSkKICBmb3IgKGkgaW4gMTpsZW5ndGgod29yZC5saXN0KSkgewoKICAgIHBvcy5tYXRjaGVzID0gbWF0Y2god29yZC5saXN0W1tpXV0sIHBvcykgCiAgICBuZWcubWF0Y2hlcyA9IG1hdGNoKHdvcmQubGlzdFtbaV1dLCBuZWcpCgogICAgcG9zLm1hdGNoZXMgPSAhaXMubmEocG9zLm1hdGNoZXMpCiAgICBuZWcubWF0Y2hlcyA9ICFpcy5uYShuZWcubWF0Y2hlcykKCiAgICBzY29yZVtpXSA9IHN1bShwb3MubWF0Y2hlcykgLSBzdW0obmVnLm1hdGNoZXMpCiAgICBwb3NpdGl2ZVtpXSA9IHN1bShwb3MubWF0Y2hlcykKICAgIG5lZ2F0aXZlW2ldID0gc3VtKG5lZy5tYXRjaGVzKQogIH0KICByZXR1cm4oZGF0YS5mcmFtZShwb3NpdGl2ZV9zY29yZSA9IHBvc2l0aXZlLCAKICAgICAgICAgICAgICAgICAgICBuZWdhdGl2ZV9zY29yZSA9IG5lZ2F0aXZlLAogICAgICAgICAgICAgICAgICAgIHNlbnRpbWVudF9zY29yZSA9IHNjb3JlKSkKfQpvdXRwdXQgPSBnZXRTZW50aW1lbnRTY29yZShkZiR0ZXh0LCBwb3MsIG5lZykKZGYkc2VudGltZW50ID0gb3V0cHV0WywgInNlbnRpbWVudF9zY29yZSJdCmRmJHBvc19zZW50aW1lbnQgPSBvdXRwdXRbLCAicG9zaXRpdmVfc2NvcmUiXQpkZiRuZWdfc2VudGltZW50ID0gb3V0cHV0WywgIm5lZ2F0aXZlX3Njb3JlIl0KZGYkZGF5ID0gYXMuRGF0ZShjdXQoZGYkY3JlYXRlZCwgYnJlYWtzID0gImRheSIpKQpkZiAlPiUgCiAgZ3JvdXBfYnkoZGF5KSAlPiUKICBzdW1tYXJpc2UobWVhblBvcyA9IG1lYW4ocG9zX3NlbnRpbWVudCksIG1lYW5OZWcgPSBtZWFuKG5lZ19zZW50aW1lbnQpLCBtZWFuU2VudCA9IG1lYW4oc2VudGltZW50KSkgLT4gc2VudGltZW50X2J5ZGF5CgpnZ3Bsb3Qoc2VudGltZW50X2J5ZGF5LGFlcyh4PWRheSkpICsKICBnZW9tX2xpbmUoYWVzKHk9bWVhbk5lZyxjb2xvdXI9IlBvc2l0aXZlIHdvcmRzIikpICsKICBnZW9tX2xpbmUoYWVzKHk9bWVhblBvcywgY29sb3VyPSJOZWdhdGl2ZSB3b3JkcyIpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9YyhgUG9zaXRpdmUgd29yZHNgPSIjNjE5Q0ZGIiwgYE5lZ2F0aXZlIHdvcmRzYD0iI0Y4NzY2RCIpKSArCiAgdGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDEpLCBsZWdlbmQucG9zaXRpb24gPSBjKC45NSwgLjk1KSwgbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkgKwogIHhsYWIobGFiZWw9IkRhdGUiKSArCiAgeWxhYihsYWJlbD0iU2VudGltZW50IFNjb3JlIikKCmdncGxvdChvdXRwdXQsIGFlcyh4PXNlbnRpbWVudF9zY29yZSkpICsKICBnZW9tX2RlbnNpdHkoYnc9LjUpICArCiAgbGFicyh0aXRsZT0iRGlzdHJpYnV0aW9uIG9mIHNlbnRpbWVudCBzY29yZXMgb2YgdHdlZXRzIGJ5IEByZWFsRG9uYWxkVHJ1bXAiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbihvdXRwdXQkc2VudGltZW50X3Njb3JlKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHhsYWIobGFiZWw9IlNlbnRpbWVudCBTY29yZSIpICsKICB5bGFiKGxhYmVsPSJEZW5zaXR5IikKYGBgCgojIyMjIyBSZWZlcmVuY2VzCjsgICBNaW5xaW5nIEh1IGFuZCBCaW5nIExpdS4gIk1pbmluZyBhbmQgU3VtbWFyaXppbmcgQ3VzdG9tZXIgUmV2aWV3cy4iIAo7ICAgICAgIFByb2NlZWRpbmdzIG9mIHRoZSBBQ00gU0lHS0REIEludGVybmF0aW9uYWwgQ29uZmVyZW5jZSBvbiBLbm93bGVkZ2UgCjsgICAgICAgRGlzY292ZXJ5IGFuZCBEYXRhIE1pbmluZyAoS0RELTIwMDQpLCBBdWcgMjItMjUsIDIwMDQsIFNlYXR0bGUsIAo7ICAgICAgIFdhc2hpbmd0b24sIFVTQSwgCjsgICBCaW5nIExpdSwgTWlucWluZyBIdSBhbmQgSnVuc2hlbmcgQ2hlbmcuICJPcGluaW9uIE9ic2VydmVyOiBBbmFseXppbmcgCjsgICAgICAgYW5kIENvbXBhcmluZyBPcGluaW9ucyBvbiB0aGUgV2ViLiIgUHJvY2VlZGluZ3Mgb2YgdGhlIDE0dGggCjsgICAgICAgSW50ZXJuYXRpb25hbCBXb3JsZCBXaWRlIFdlYiBjb25mZXJlbmNlIChXV1ctMjAwNSksIE1heSAxMC0xNCwgCjsgICAgICAgMjAwNSwgQ2hpYmEsIEphcGFuLgo=