About this Notebook




Analytics Toolkit: Require Packages



# Here we are checking if the package is installed
if(!require("tidyverse")){
  install.packages("tidyverse", dependencies = TRUE)
  library("tidyverse")
}
if(!require("syuzhet")){
  install.packages("syuzhet", dependencies = TRUE)
  library("syuzhet")
}
if(!require("cleanNLP")){
  install.packages("cleanNLP", dependencies = TRUE)
  library("cleanNLP")
}
if(!require("magrittr")){
  install.packages("magrittr", dependencies = TRUE)
  library("magrittr")
}
if(!require("wordcloud")){
  install.packages("wordcloud", dependencies = TRUE)
  library("wordcloud")
}



Data Preparation: Cleaning tweets using regular expressions



Reading and inspecting the dataset

tweets <- read_csv("data/march_madness.csv")
# Change the tweets IDs from longe integer to characters
tweets$tweet_id <- as.character(tweets$tweet_id)
# Extract and delete the links variable to add it at the end
links <- tweets$links
tweets$links <- NULL
# Inspects the first 10 rows
head(tweets)


Now first we need to extract the text from the raw tweets and clean it using regular expressions


Here we are going to use the Saif Mohammad’s NRC Emotion lexicon toextract the sentiment of the tweet. The NRC emotion lexicon is a list of words and their associations with eight emotions (anger, fear, anticipation, trust, surprise, sadness, joy, and disgust) and two sentiments (negative and positive).


Here we are going to merge the NRC Emotion results with the original data to create a complete dataset.

tweets <- bind_cols(tweets, nrc_data)


To have another metrics for semtiment we are going to use another lexicon developed by Professor Minqing Hu and Professor Bing Liu, from University of Illinois at Chicago.

tweets$sentiment_bing <- get_sentiment(char_v = tweets$text, method="bing", language = "english")


Sentiment Analysis: Natural Language Processing



First lets read the dataset and inspect the first 10 rows

tweets <- read_csv("data/march_madness_sent.csv")
tweets$tweet_id <- as.character(tweets$tweet_id)
head(tweets[12:21])


For sentiment analysis we are going to use the cleanNLP package that uses Stanford CoreNLP – Natural language software int he backend. First we need to initialize the CoreNLP engine and create an annotation object using the text column, tweet_id and the other columns are given as metadata

cnlp_init_udpipe()

doc <- cnlp_annotate(input = tweets$text, as_strings = TRUE, doc_ids = tweets$tweet_id, meta = tweets[-c(1,2)])


Distribution of tweet/sentence length, max number of works in a tweet 280

tokens <- cnlp_get_token(doc) %>%
  group_by(id, sid) %>%
  summarize(sent_len = n())
quantile(tokens$sent_len, seq(0,1,0.1))
  0%  10%  20%  30%  40%  50%  60%  70%  80%  90% 100% 
   1    1    3    5    7    9   11   14   18   24   62 


Here we can see the change of sentiment in the tweets

qplot(x = 1:length(tweets$sentiment_bing), 
      y = tweets$sentiment_bing, 
      geom = "line", 
      xlab = "Narrative Time", 
      ylab = "Emotional Valence", 
      main = "Tweets Sentiment Trajectory")


Here we can find the most used entities from the tweets entity table. The document corpus yields an alternative way to see the underlying topics.


Here we creating a high-level summary of the tweets text by extracting all direct object object-dependencies.


Look at the tweets with negative sentiment

angry_tweets <- which(tweets$anger > 0)
data_frame(tweet = tweets$text[angry_tweets][1:2])


Look a tweets with positive sentiment

joy_tweets <- which(tweets$joy > 0)
data_frame(tweet = tweets$text[joy_tweets][5:7])


Lets explore the emotions in the tweets more in-depth. Here we are going to extract the variables regarding emotions and create a subset.

value <- as.double(colSums(prop.table(tweets[, 11:18])))
emotion <- names(tweets)[11:18]
emotion <- factor(emotion, levels = names(tweets)[11:18][order(value, decreasing = FALSE)])
emotions <- data_frame(emotion, percent = value * 100)
head(emotions)


Now we can create a plot of the emotions in the march madness tweets

ggplot(data = emotions, aes(x = emotion, y = percent)) + 
  geom_bar(stat = "identity", aes(fill = emotion)) + 
  scale_fill_brewer(palette="RdYlGn") + 
  coord_flip() +
  xlab("Emotion") +
  ylab("Percentage")



Reporting: A Wordcloud from March Madness Tweets



Wordclouds are always a fun and engaging way to display data. Here we are going to set some stop words that we dont want in the plot.

remove_words <- c( "twitter", "chicago", "loyola", "ramblers", "loyolaramblers","school",
                   "university", "luc", "loyolachicago" , "ramblersmbb", "ncaa","ve","basketball" ,
                   "umichbball", "marchmadness2018", "marchmadness", "final", "marchmaddness",
                   "goblue", "finalfour", "sisterjean", "ncaatournament", "ncaatournament2018",
                   "didn","city", "hey", "day", "college", "games", "tourney", "march", "game")
my_stop_words <- bind_rows(data_frame(word = remove_words, lexicon = c("SMART")), stop_words)


Now lets create a dataframe of words and filter using predefined stop words

twt_text <- tibble(text = tweets$text) %>% 
  unnest_tokens(word, text) %>%
  filter(!word %in% my_stop_words$word, str_detect(word, "[a-z]"))


Set a threshold for the min/max frequency of words and scale of the wordcloud

min_freq = 20
fig_scale = c(2.5 , 0.5)
max_words = 100


The last step is to create the wordcloud by counting the frequency of the words

LS0tCnRpdGxlOiAiTWFyY2ggTWFkbmVzcyBBbmFseXNpcyIKYXV0aG9yOiAKLSAiUXVpbmxhbiBTY2hvb2wgb2YgQnVzaW5lc3MgLSBMb3lvbGEgVW5pdmVyc2l0eSBDaGljYWdvIgotICJKb3NlIEx1aXMgUm9kcmlndWV6IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKZGF0ZTogIkFwcmlsIDksIDIwMTgiCnN1YnRpdGxlOiAiQ01FIEdyb3VwIEZvdW5kYXRpb24gQnVzaW5lc3MgQW5hbHl0aWNzIExhYiIKLS0tCgo8YnI+CgotLS0tLS0tLS0tLS0tLQoKIyMgQWJvdXQgdGhpcyBOb3RlYm9vawoKLS0tLS0tLS0tLS0tLS0KCiogT24gdGhpcyBub3RlYm9vayB3ZSBhcmUgZ29pbmcgdG8gYW5hbHlzaXMgdHdlZXRzIGZyb20gbWFyY2ggbWFkbmVzcyAyMDE4CgoqIFVzZSByZWd1bGFyIGV4cHJlc3Npb24gdG8gY2xlYW4gdGhlIHR3ZWV0cyB0ZXh0CgoqIEZhbWlsaWFyaXplIHdpdGggc29tZSBuYXR1cmFsIGxhbmd1YWdlIHByb2Nlc3NpbmcgdG9vbHMKCjxicj4KCi0tLS0tLS0tLS0tLS0tCgojIyBBbmFseXRpY3MgVG9vbGtpdDogUmVxdWlyZSBQYWNrYWdlcwoKLS0tLS0tLS0tLS0tLS0KCjxicj4KCiogdGlkeXZlcnNlOiBodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLwoqIHN5dXpoZXQ6IGh0dHBzOi8vZ2l0aHViLmNvbS9tam9ja2Vycy9zeXV6aGV0CiogY2xlYW5OTFA6IGh0dHBzOi8vZ2l0aHViLmNvbS9zdGF0c21hdGhzL2NsZWFuTkxQCiogd29yZGNsb3VkOiBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvd29yZGNsb3VkL3dvcmRjbG91ZC5wZGYKCmBgYHtyICwgbWVzc2FnZT1GQUxTRX0KCiMgSGVyZSB3ZSBhcmUgY2hlY2tpbmcgaWYgdGhlIHBhY2thZ2UgaXMgaW5zdGFsbGVkCmlmKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICBsaWJyYXJ5KCJ0aWR5dmVyc2UiKQp9CgppZighcmVxdWlyZSgic3l1emhldCIpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJzeXV6aGV0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICBsaWJyYXJ5KCJzeXV6aGV0IikKfQoKaWYoIXJlcXVpcmUoImNsZWFuTkxQIikpewogIGluc3RhbGwucGFja2FnZXMoImNsZWFuTkxQIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICBsaWJyYXJ5KCJjbGVhbk5MUCIpCn0KCmlmKCFyZXF1aXJlKCJtYWdyaXR0ciIpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgbGlicmFyeSgibWFncml0dHIiKQp9CgppZighcmVxdWlyZSgid29yZGNsb3VkIikpewogIGluc3RhbGwucGFja2FnZXMoIndvcmRjbG91ZCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgbGlicmFyeSgid29yZGNsb3VkIikKfQoKYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLQoKIyMgRGF0YSBQcmVwYXJhdGlvbjogQ2xlYW5pbmcgdHdlZXRzIHVzaW5nIHJlZ3VsYXIgZXhwcmVzc2lvbnMKCi0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIyMjIFJlYWRpbmcgYW5kIGluc3BlY3RpbmcgdGhlIGRhdGFzZXQKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnR3ZWV0cyA8LSByZWFkX2NzdigiZGF0YS9tYXJjaF9tYWRuZXNzLmNzdiIpCgojIENoYW5nZSB0aGUgdHdlZXRzIElEcyBmcm9tIGxvbmdlIGludGVnZXIgdG8gY2hhcmFjdGVycwp0d2VldHMkdHdlZXRfaWQgPC0gYXMuY2hhcmFjdGVyKHR3ZWV0cyR0d2VldF9pZCkKCiMgRXh0cmFjdCBhbmQgZGVsZXRlIHRoZSBsaW5rcyB2YXJpYWJsZSB0byBhZGQgaXQgYXQgdGhlIGVuZApsaW5rcyA8LSB0d2VldHMkbGlua3MKdHdlZXRzJGxpbmtzIDwtIE5VTEwKCiMgSW5zcGVjdHMgdGhlIGZpcnN0IDEwIHJvd3MKaGVhZCh0d2VldHMpCmBgYAoKPGJyPgoKIyMjIyBOb3cgZmlyc3Qgd2UgbmVlZCB0byBleHRyYWN0IHRoZSB0ZXh0IGZyb20gdGhlIHJhdyB0d2VldHMgYW5kIGNsZWFuIGl0IHVzaW5nIHJlZ3VsYXIgZXhwcmVzc2lvbnMKYGBge3J9CnJlcGxhY2VfcmVnIDwtICdodHRwczovL3QuY28vW0EtWmEtelxcZF0rfGh0dHA6Ly9bQS1aYS16XFxkXSt8KHBpYy50d2l0dGVyLmNvbS9bQS1aYS16XFxkXSspfCZhbXA7fCZsdDt8Jmd0O3xSVHwoLiouKVxcLmNvbSguKi4pXFxTK1xcc3xbXls6YWxudW06XV18KGh0dHB8aHR0cHMpXFxTK1xccyp8KCN8QClcXFMrXFxzKnxcXG58XFwiJwoKdHdlZXRzIDwtIHR3ZWV0cyAlPiUgCiAgbXV0YXRlKHRleHQgPSBzdHJfcmVwbGFjZV9hbGwodGV4dCwgcmVwbGFjZV9yZWcsICIgIikpICU+JSAKICBtdXRhdGUodGV4dCA9IGljb252KHRleHQsIGZyb20gPSAiQVNDSUkiLCB0byA9ICJVVEYtOCIsIHN1YiA9ICIgIikpCgpoZWFkKHR3ZWV0c1sndGV4dCddKQpgYGAKCjxicj4KCiMjIyMgSGVyZSB3ZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBTYWlmIE1vaGFtbWFk4oCZcyBOUkMgRW1vdGlvbiBsZXhpY29uIHRvZXh0cmFjdCB0aGUgc2VudGltZW50IG9mIHRoZSB0d2VldC4gVGhlIE5SQyBlbW90aW9uIGxleGljb24gaXMgYSBsaXN0IG9mIHdvcmRzIGFuZCB0aGVpciBhc3NvY2lhdGlvbnMgd2l0aCBlaWdodCBlbW90aW9ucyAoYW5nZXIsIGZlYXIsIGFudGljaXBhdGlvbiwgdHJ1c3QsIHN1cnByaXNlLCBzYWRuZXNzLCBqb3ksIGFuZCBkaXNndXN0KSBhbmQgdHdvIHNlbnRpbWVudHMgKG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSkuCgoqIFNvdXJjZTogaHR0cDovL3NhaWZtb2hhbW1hZC5jb20vV2ViUGFnZXMvTlJDLUVtb3Rpb24tTGV4aWNvbi5odG0KCmBgYHtyfQpucmNfZGF0YSA8LSBnZXRfbnJjX3NlbnRpbWVudCh0d2VldHMkdGV4dCkKbnJjX2RhdGEgPC0gYXNfdGliYmxlKG5yY19kYXRhKQoKaGVhZChucmNfZGF0YSkKYGBgCgo8YnI+CgojIyMjIEhlcmUgd2UgYXJlIGdvaW5nIHRvIG1lcmdlIHRoZSBOUkMgRW1vdGlvbiByZXN1bHRzIHdpdGggdGhlIG9yaWdpbmFsIGRhdGEgdG8gY3JlYXRlIGEgY29tcGxldGUgZGF0YXNldC4KYGBge3J9CnR3ZWV0cyA8LSBiaW5kX2NvbHModHdlZXRzLCBucmNfZGF0YSkKCmBgYAoKPGJyPgoKIyMjIyBUbyBoYXZlIGFub3RoZXIgbWV0cmljcyBmb3Igc2VtdGltZW50IHdlIGFyZSBnb2luZyB0byB1c2UgYW5vdGhlciBsZXhpY29uIGRldmVsb3BlZCBieSBQcm9mZXNzb3IgTWlucWluZyBIdSBhbmQgUHJvZmVzc29yIEJpbmcgTGl1LCBmcm9tIFVuaXZlcnNpdHkgb2YgSWxsaW5vaXMgYXQgQ2hpY2Fnby4KKiBTb3VyY2U6IGh0dHA6Ly93d3cuY3MudWljLmVkdS9+bGl1Yi9GQlMvc2VudGltZW50LWFuYWx5c2lzLmh0bWwKCmBgYHtyfQp0d2VldHMkc2VudGltZW50X2JpbmcgPC0gZ2V0X3NlbnRpbWVudChjaGFyX3YgPSB0d2VldHMkdGV4dCwgbWV0aG9kPSJiaW5nIiwgbGFuZ3VhZ2UgPSAiZW5nbGlzaCIpCgpgYGAKCjxicj4KCiMjIyMgRmluYWxseSBhZGQgdGhlIGxpbmtzIHZhcmlhYmxlIGFuZCBzYXZlIHRoZSBuZXcgY29tcGxldGUgZGF0YXNldCAKYGBge3J9CnR3ZWV0cyRsaW5rcyA8LSBsaW5rcwoKd3JpdGVfY3N2KHR3ZWV0cywgImRhdGEvbWFyY2hfbWFkbmVzc19zZW50LmNzdiIpCgpoZWFkKHR3ZWV0c1szOjEwXSkKYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLQoKIyMgU2VudGltZW50IEFuYWx5c2lzOiBOYXR1cmFsIExhbmd1YWdlIFByb2Nlc3NpbmcKCi0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIyMjIEZpcnN0IGxldHMgcmVhZCB0aGUgZGF0YXNldCBhbmQgaW5zcGVjdCB0aGUgZmlyc3QgMTAgcm93cwpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KdHdlZXRzIDwtIHJlYWRfY3N2KCJkYXRhL21hcmNoX21hZG5lc3Nfc2VudC5jc3YiKQp0d2VldHMkdHdlZXRfaWQgPC0gYXMuY2hhcmFjdGVyKHR3ZWV0cyR0d2VldF9pZCkKaGVhZCh0d2VldHNbMTI6MjFdKQpgYGAKCjxicj4KCiMjIyMgRm9yIHNlbnRpbWVudCBhbmFseXNpcyB3ZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBjbGVhbk5MUCBwYWNrYWdlIHRoYXQgdXNlcyBTdGFuZm9yZCBDb3JlTkxQIOKAkyBOYXR1cmFsIGxhbmd1YWdlIHNvZnR3YXJlIGludCBoZSBiYWNrZW5kLiBGaXJzdCB3ZSBuZWVkIHRvIGluaXRpYWxpemUgdGhlIENvcmVOTFAgZW5naW5lIGFuZCBjcmVhdGUgYW4gYW5ub3RhdGlvbiBvYmplY3QgdXNpbmcgdGhlIHRleHQgY29sdW1uLCB0d2VldF9pZCBhbmQgdGhlIG90aGVyIGNvbHVtbnMgYXJlIGdpdmVuIGFzIG1ldGFkYXRhCmBgYHtyfQpjbmxwX2luaXRfdWRwaXBlKCkKCmRvYyA8LSBjbmxwX2Fubm90YXRlKGlucHV0ID0gdHdlZXRzJHRleHQsIGFzX3N0cmluZ3MgPSBUUlVFLCBkb2NfaWRzID0gdHdlZXRzJHR3ZWV0X2lkLCBtZXRhID0gdHdlZXRzWy1jKDEsMildKQoKYGBgCgo8YnI+CgojIyMjIERpc3RyaWJ1dGlvbiBvZiB0d2VldC9zZW50ZW5jZSBsZW5ndGgsIG1heCBudW1iZXIgb2Ygd29ya3MgaW4gYSB0d2VldCAyODAKYGBge3J9CnRva2VucyA8LSBjbmxwX2dldF90b2tlbihkb2MpICU+JQogIGdyb3VwX2J5KGlkLCBzaWQpICU+JQogIHN1bW1hcml6ZShzZW50X2xlbiA9IG4oKSkKCnF1YW50aWxlKHRva2VucyRzZW50X2xlbiwgc2VxKDAsMSwwLjEpKQoKYGBgCgo8YnI+CgojIyMjIEhlcmUgd2UgY2FuIHNlZSB0aGUgY2hhbmdlIG9mIHNlbnRpbWVudCBpbiB0aGUgdHdlZXRzIApgYGB7cn0KcXBsb3QoeCA9IDE6bGVuZ3RoKHR3ZWV0cyRzZW50aW1lbnRfYmluZyksIAogICAgICB5ID0gdHdlZXRzJHNlbnRpbWVudF9iaW5nLCAKICAgICAgZ2VvbSA9ICJsaW5lIiwgCiAgICAgIHhsYWIgPSAiTmFycmF0aXZlIFRpbWUiLCAKICAgICAgeWxhYiA9ICJFbW90aW9uYWwgVmFsZW5jZSIsIAogICAgICBtYWluID0gIlR3ZWV0cyBTZW50aW1lbnQgVHJhamVjdG9yeSIpCgpgYGAKCjxicj4KCiMjIyMgSGVyZSB3ZSBjYW4gZmluZCB0aGUgbW9zdCB1c2VkIGVudGl0aWVzIGZyb20gdGhlIHR3ZWV0cyBlbnRpdHkgdGFibGUuIFRoZSBkb2N1bWVudCBjb3JwdXMgeWllbGRzIGFuIGFsdGVybmF0aXZlIHdheSB0byBzZWUgdGhlIHVuZGVybHlpbmcgdG9waWNzLgpgYGB7cn0KdHdlZXRzX2VudGl0aWVzIDwtIGNubHBfZ2V0X3Rva2VuKGRvYykgJT4lCiAgZmlsdGVyKHVwb3MgPT0gIk5PVU4iKSAlPiUKICBncm91cF9ieShsZW1tYSkgJT4lCiAgc3VtbWFyaXplKGNvdW50ID0gbigpKSAlPiUKICB0b3BfbihuID0gODAsIGNvdW50KSAlPiUKICBhcnJhbmdlKGRlc2MoY291bnQpKSAlPiUKICB1c2Vfc2VyaWVzKGxlbW1hKQoKZGF0YV9mcmFtZSh0d2VldHNfZW50aXRpZXMpCgpgYGAKCjxicj4KCiMjIyMgSGVyZSB3ZSBjcmVhdGluZyBhIGhpZ2gtbGV2ZWwgc3VtbWFyeSBvZiB0aGUgdHdlZXRzIHRleHQgYnkgZXh0cmFjdGluZyBhbGwgZGlyZWN0IG9iamVjdCBvYmplY3QtZGVwZW5kZW5jaWVzLgpgYGB7cn0KdHdlZXRzX3N1bW1hcnkgPC0gY25scF9nZXRfZGVwZW5kZW5jeShkb2MsIGdldF90b2tlbiA9IFRSVUUpICU+JQogIGxlZnRfam9pbihjbmxwX2dldF9kb2N1bWVudChkb2MpKSAlPiUKICBzZWxlY3QoaWQgPSBpZCwgc3RhcnQgPSB3b3JkLCB3b3JkID0gbGVtbWFfdGFyZ2V0KSAlPiUKICBsZWZ0X2pvaW4od29yZF9mcmVxdWVuY3kpICU+JQogIGZpbHRlcihmcmVxdWVuY3kgPCAwLjAwMDEpICU+JQogIHNlbGVjdChpZCwgc3RhcnQsIHdvcmQpICUkJQogIHNwcmludGYoIiVzID0+ICVzIiwgc3RhcnQsIHdvcmQpCgpkYXRhX2ZyYW1lKHR3ZWV0c19zdW1tYXJ5KQoKYGBgCgo8YnI+CgojIyMjIExvb2sgYXQgdGhlIHR3ZWV0cyB3aXRoIG5lZ2F0aXZlIHNlbnRpbWVudCAKYGBge3J9CgphbmdyeV90d2VldHMgPC0gd2hpY2godHdlZXRzJGFuZ2VyID4gMCkKZGF0YV9mcmFtZSh0d2VldCA9IHR3ZWV0cyR0ZXh0W2FuZ3J5X3R3ZWV0c11bMToyXSkKCmBgYAoKPGJyPgoKIyMjIyBMb29rIGEgdHdlZXRzIHdpdGggcG9zaXRpdmUgc2VudGltZW50CmBgYHtyfQpqb3lfdHdlZXRzIDwtIHdoaWNoKHR3ZWV0cyRqb3kgPiAwKQpkYXRhX2ZyYW1lKHR3ZWV0ID0gdHdlZXRzJHRleHRbam95X3R3ZWV0c11bNTo3XSkKCmBgYAoKPGJyPgoKIyMjIyBMZXRzIGV4cGxvcmUgdGhlIGVtb3Rpb25zIGluIHRoZSB0d2VldHMgbW9yZSBpbi1kZXB0aC4gSGVyZSB3ZSBhcmUgZ29pbmcgdG8gZXh0cmFjdCB0aGUgdmFyaWFibGVzIHJlZ2FyZGluZyBlbW90aW9ucyBhbmQgY3JlYXRlIGEgc3Vic2V0LgpgYGB7cn0KdmFsdWUgPC0gYXMuZG91YmxlKGNvbFN1bXMocHJvcC50YWJsZSh0d2VldHNbLCAxMToxOF0pKSkKZW1vdGlvbiA8LSBuYW1lcyh0d2VldHMpWzExOjE4XQplbW90aW9uIDwtIGZhY3RvcihlbW90aW9uLCBsZXZlbHMgPSBuYW1lcyh0d2VldHMpWzExOjE4XVtvcmRlcih2YWx1ZSwgZGVjcmVhc2luZyA9IEZBTFNFKV0pCmVtb3Rpb25zIDwtIGRhdGFfZnJhbWUoZW1vdGlvbiwgcGVyY2VudCA9IHZhbHVlICogMTAwKQoKaGVhZChlbW90aW9ucykKYGBgCgo8YnI+CgojIyMjIE5vdyB3ZSBjYW4gY3JlYXRlIGEgcGxvdCBvZiB0aGUgZW1vdGlvbnMgaW4gdGhlIG1hcmNoIG1hZG5lc3MgdHdlZXRzCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGVtb3Rpb25zLCBhZXMoeCA9IGVtb3Rpb24sIHkgPSBwZXJjZW50KSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgYWVzKGZpbGwgPSBlbW90aW9uKSkgKyAKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJSZFlsR24iKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgeGxhYigiRW1vdGlvbiIpICsKICB5bGFiKCJQZXJjZW50YWdlIikKCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0KCiMjIFJlcG9ydGluZzogQSBXb3JkY2xvdWQgZnJvbSBNYXJjaCBNYWRuZXNzIFR3ZWV0cwoKLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMjIyMgV29yZGNsb3VkcyBhcmUgYWx3YXlzIGEgZnVuIGFuZCBlbmdhZ2luZyB3YXkgdG8gZGlzcGxheSBkYXRhLiBIZXJlIHdlIGFyZSBnb2luZyB0byBzZXQgc29tZSBzdG9wIHdvcmRzIHRoYXQgd2UgZG9udCB3YW50IGluIHRoZSBwbG90LgpgYGB7cn0KcmVtb3ZlX3dvcmRzIDwtIGMoICJ0d2l0dGVyIiwgImNoaWNhZ28iLCAibG95b2xhIiwgInJhbWJsZXJzIiwgImxveW9sYXJhbWJsZXJzIiwic2Nob29sIiwgImdvbm5hIiwKICAgICAgICAgICAgICAgICAgICJ1bml2ZXJzaXR5IiwgImx1YyIsICJsb3lvbGFjaGljYWdvIiAsICJyYW1ibGVyc21iYiIsICJuY2FhIiwidmUiLCJiYXNrZXRiYWxsIiAsCiAgICAgICAgICAgICAgICAgICAidW1pY2hiYmFsbCIsICJtYXJjaG1hZG5lc3MyMDE4IiwgIm1hcmNobWFkbmVzcyIsICJmaW5hbCIsICJtYXJjaG1hZGRuZXNzIiwKICAgICAgICAgICAgICAgICAgICJnb2JsdWUiLCAiZmluYWxmb3VyIiwgInNpc3RlcmplYW4iLCAibmNhYXRvdXJuYW1lbnQiLCAibmNhYXRvdXJuYW1lbnQyMDE4IiwKICAgICAgICAgICAgICAgICAgICJkaWRuIiwiY2l0eSIsICJoZXkiLCAiZGF5IiwgImNvbGxlZ2UiLCAiZ2FtZXMiLCAidG91cm5leSIsICJtYXJjaCIsICJnYW1lIikKCm15X3N0b3Bfd29yZHMgPC0gYmluZF9yb3dzKGRhdGFfZnJhbWUod29yZCA9IHJlbW92ZV93b3JkcywgbGV4aWNvbiA9IGMoIlNNQVJUIikpLCBzdG9wX3dvcmRzKQoKYGBgCgo8YnI+CgojIyMjIE5vdyBsZXRzIGNyZWF0ZSBhIGRhdGFmcmFtZSBvZiB3b3JkcyBhbmQgZmlsdGVyIHVzaW5nIHByZWRlZmluZWQgc3RvcCB3b3JkcyAKYGBge3J9CnR3dF90ZXh0IDwtIHRpYmJsZSh0ZXh0ID0gdHdlZXRzJHRleHQpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpICU+JQogIGZpbHRlcighd29yZCAlaW4lIG15X3N0b3Bfd29yZHMkd29yZCwgc3RyX2RldGVjdCh3b3JkLCAiW2Etel0iKSkKCmBgYAoKPGJyPgoKIyMjIyBTZXQgYSB0aHJlc2hvbGQgZm9yIHRoZSBtaW4vbWF4IGZyZXF1ZW5jeSBvZiB3b3JkcyBhbmQgc2NhbGUgb2YgdGhlIHdvcmRjbG91ZApgYGB7cn0KbWluX2ZyZXEgPSA4MAptYXhfd29yZHMgPSAxMDAKZmlnX3NjYWxlID0gYygzICwgMC41KQpgYGAKCjxicj4KCiMjIyMgVGhlIGxhc3Qgc3RlcCBpcyB0byBjcmVhdGUgdGhlIHdvcmRjbG91ZCBieSBjb3VudGluZyB0aGUgZnJlcXVlbmN5IG9mIHRoZSB3b3JkcwpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KdHd0X3RleHQgJT4lCiAgYW50aV9qb2luKG15X3N0b3Bfd29yZHMpICU+JQogIGNvdW50KHdvcmQpICU+JQogIHdpdGgod29yZGNsb3VkKHdvcmQsIG4sIAogICAgICAgICAgICAgICAgIHNjYWxlID0gZmlnX3NjYWxlLAogICAgICAgICAgICAgICAgIG1pbi5mcmVxID0gbWluX2ZyZXEsCiAgICAgICAgICAgICAgICAgbWF4LndvcmRzID0gbWF4X3dvcmRzKSkKCmBgYAo=