Refresher: Basics of String Manupilation
We start by taking a piece of text and turning it into something that carries the meaning of the initial text but is less noisy and thus perhaps easier to “understand” by a computer.
text <- "The Eton-educated, non-binary British Iraqi had always struggled with their identity, until they discovered drag. Yet the 29 year old says the performances come at a high price"
# Transforming to lower case
text %>% str_to_lower()
[1] "the eton-educated, non-binary british iraqi had always struggled with their identity, until they discovered drag. yet the 29 year old says the performances come at a high price"
# Split by '.' (=sentence)
text %>% str_split('\\.')
[[1]]
[1] "The Eton-educated, non-binary British Iraqi had always struggled with their identity, until they discovered drag"
[2] " Yet the 29 year old says the performances come at a high price"
text %>% str_replace_all('o', 'O')
[1] "The EtOn-educated, nOn-binary British Iraqi had always struggled with their identity, until they discOvered drag. Yet the 29 year Old says the perfOrmances cOme at a high price"
# Split by ' ' (=word)
text %>% str_remove_all('[[:punct:]]') %>% str_split(' ') %>% unlist()
[1] "The" "Etoneducated" "nonbinary" "British" "Iraqi" "had" "always" "struggled" "with" "their" "identity" "until"
[13] "they" "discovered" "drag" "Yet" "the" "29" "year" "old" "says" "the" "performances" "come"
[25] "at" "a" "high" "price"
text %>% str_to_lower() %>% str_remove_all('[[:punct:]]') %>% str_split(' ')
[[1]]
[1] "the" "etoneducated" "nonbinary" "british" "iraqi" "had" "always" "struggled" "with" "their" "identity" "until"
[13] "they" "discovered" "drag" "yet" "the" "29" "year" "old" "says" "the" "performances" "come"
[25] "at" "a" "high" "price"
Trump Tweets Processing many short texts and simple stats
An introduction to NLP would not be the same without Donald’s tweets. Let’s use these tweets for some more basic NLP and let’s try to gather some insights…maybe
Let’s try to use some very simple statistics on twitter data, thanks to Trump Twitter Archive
Note: We here already use precompiled data. However, you could use the rtweet
package and instead work with own data on tweets of interest.
# we will load some json files
library(jsonlite)
library(tidyjson)
# download and open some Trump tweets from trump_tweet_data_archive
tmp <- tempfile()
download.file("https://github.com/bpb27/trump_tweet_data_archive/raw/master/condensed_2018.json.zip", tmp)
trying URL 'https://github.com/bpb27/trump_tweet_data_archive/raw/master/condensed_2018.json.zip'
Content type 'application/zip' length 384688 bytes (375 KB)
==================================================
downloaded 375 KB
trump_tweets <- stream_in(unz(tmp, "condensed_2018.json"))
Found 1 records...
Imported 1 records. Simplifying...
trump_tweets %>% glimpse()
Rows: 3,510
Columns: 8
$ source <chr> "Twitter for iPhone", "Twitter for iPhone", "Twitter for iPhone", "Twitter for iPhone", "Twitter for iPhone", "Twitter for iPhone", "Twitter for iPhone", "T…
$ id_str <chr> "1079888205351145472", "1079830268708556800", "1079830267274108930", "1079763923845419009", "1079763419908243456", "1079762413589807104", "10797487300588707…
$ text <chr> "HAPPY NEW YEAR! https://t.co/bHoPDPQ7G6", "....Senator Schumer, more than a year longer than any other Administration in history. These are people who have…
$ created_at <chr> "Mon Dec 31 23:53:06 +0000 2018", "Mon Dec 31 20:02:52 +0000 2018", "Mon Dec 31 20:02:52 +0000 2018", "Mon Dec 31 15:39:15 +0000 2018", "Mon Dec 31 15:37:14…
$ retweet_count <int> 33548, 17456, 21030, 29610, 30957, 1123, 25463, 22079, 15152, 22119, 17467, 20873, 61837, 32084, 25782, 44918, 30800, 25809, 34547, 30224, 29381, 27736, 300…
$ in_reply_to_user_id_str <chr> NA, "25073877", NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ favorite_count <int> 136012, 65069, 76721, 127485, 132439, 4217, 112735, 91523, 72758, 101470, 79534, 97178, 233722, 131013, 123780, 150249, 118323, 109368, 144416, 129861, 1207…
$ is_retweet <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
library(lubridate) # For workin with times
trump_tweets %<>%
mutate(created_at = paste(substr(created_at,27,30),
substr(created_at,5,7),
substr(created_at,9,10),
substr(created_at,12,20)) %>%
as_datetime())
Notye: We will not use the times of tweet for now, but feel free to discover, and maybe reconstruct something inspired by THIS AMAZING PAPER!!!
# Lets filter out retweets
trump_tweets %<>%
filter(is_retweet == FALSE)
# LEts tokenize. Notice that there are special tokens for tweets which keep usefull special characters
trump_token <-trump_tweets %>%
select(id_str, text) %>%
unnest_tokens(word, text, token = "tweets")
trump_token %<>%
anti_join(stop_words, by = 'word')
trump_token %>% count(word, sort = TRUE) %>% head(100)
Lets see who trump mentions
trump_token %>%
filter(word %>% str_detect('@')) %>%
count(word, sort = TRUE)
Your turn
The link below holds a datasewt with ~10k #OKBoomer tweets from the days 10-21 Nov 2019.
https://github.com/SDS-AAU/SDS-master/raw/master/M2/data/tweets_boomer.zip
What to do: * Use elements from the above code to make a list of the most common hashtags (you have to get the hashtags from the text, not using the column containing them already) * Also try to have a look at hashtags over time: Take out the 10 most common hashtags - excluding #OKBoomer - and plot their occurrence over the days in the data
Plan of attack:
- Convert the timestamp into a datetime
- Calculate the occurence of the specific hashtags (itentified by a trailing
#
) in the chosen timespan (here: Days)
- Plot (days on x, n on y)
Go!
LS0tCnRpdGxlOiAnSW50cm9kdWN0aW9uIHRvIE5hdHVyYWwtbGFuZ3VhZ2UtUHJvY2Vzc2luZyAoUiknCmF1dGhvcjogIkRhbmllbCBTLiBIYWluIChkc2hAYnVzaW5lc3MuYWF1LmRrKSIKZGF0ZTogIlVwZGF0ZWQgYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIHRoZW1lOiBmbGF0bHkKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyMjIEdlbmVyaWMgcHJlYW1ibGUKcm0obGlzdD1scygpKQpTeXMuc2V0ZW52KExBTkcgPSAiZW4iKSAjIEZvciBlbmdsaXNoIGxhbmd1YWdlCm9wdGlvbnMoc2NpcGVuID0gNSkgIyBUbyBkZWFjdGl2YXRlIGFubm95aW5nIHNjaWVudGlmaWMgbnVtYmVyIG5vdGF0aW9uCgojIyMgS25pdHIgb3B0aW9ucwpsaWJyYXJ5KGtuaXRyKSAjIEZvciBkaXNwbGF5IG9mIHRoZSBtYXJrZG93bgprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZz1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZT1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgY29tbWVudD1GQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0iY2VudGVyIgogICAgICAgICAgICAgICAgICAgICApCmBgYAoKYGBge3J9CiMjIyBMb2FkIHN0YW5kYXJkcGFja2FnZXMKbGlicmFyeSh0aWR5dmVyc2UpICMgQ29sbGVjdGlvbiBvZiBhbGwgdGhlIGdvb2Qgc3R1ZmYgbGlrZSBkcGx5ciwgZ2dwbG90MiBlY3QuCmxpYnJhcnkobWFncml0dHIpICMgRm9yIGV4dHJhLXBpcGluZyBvcGVyYXRvcnMgKGVnLiAlPD4lKQpgYGAKCiMjIyBUaGlzIHNlc3Npb24KCkluIHRoaXMgYXBwbGllZCBzZXNzaW9uLCB5b3Ugd2lsbDoKCjEuIFJlZnJlc2ggYmFzaWMgc3RyaW5nIG1hbmlwdWxhdGlvbiBza2lsbHMKMi4gTGVhcm4gaG93IHRvIHRva2VuaXplIHRleHRzIGFuZCBhbmFseXplIHRoZXNlIHRva2VucwozLiBBcHBseSB0aGVzZSBza2lsbHMgb24gdHdpdHRlciBkYXRhCgoKIyBSZWZyZXNoZXI6IEJhc2ljcyBvZiBTdHJpbmcgTWFudXBpbGF0aW9uCgpXZSBzdGFydCBieSB0YWtpbmcgYSBwaWVjZSBvZiB0ZXh0IGFuZCB0dXJuaW5nIGl0IGludG8gc29tZXRoaW5nIHRoYXQgY2FycmllcyB0aGUgbWVhbmluZyBvZiB0aGUgaW5pdGlhbCB0ZXh0IGJ1dCBpcyBsZXNzIG5vaXN5IGFuZCB0aHVzIHBlcmhhcHMgZWFzaWVyIHRvICJ1bmRlcnN0YW5kIiBieSBhIGNvbXB1dGVyLgoKYGBge3J9CnRleHQgPC0gIlRoZSBFdG9uLWVkdWNhdGVkLCBub24tYmluYXJ5IEJyaXRpc2ggSXJhcWkgaGFkIGFsd2F5cyBzdHJ1Z2dsZWQgd2l0aCB0aGVpciBpZGVudGl0eSwgdW50aWwgdGhleSBkaXNjb3ZlcmVkIGRyYWcuIFlldCB0aGUgMjkgeWVhciBvbGQgc2F5cyB0aGUgcGVyZm9ybWFuY2VzIGNvbWUgYXQgYSBoaWdoIHByaWNlIgpgYGAKCmBgYHtyfQojIFRyYW5zZm9ybWluZyB0byBsb3dlciBjYXNlCnRleHQgJT4lIHN0cl90b19sb3dlcigpCmBgYAoKYGBge3J9CiMgU3BsaXQgYnkgJy4nICg9c2VudGVuY2UpCnRleHQgJT4lIHN0cl9zcGxpdCgnXFwuJykKYGBgCgpgYGB7cn0KdGV4dCAlPiUgc3RyX3JlcGxhY2VfYWxsKCdvJywgJ08nKQpgYGAKCmBgYHtyfQojIFNwbGl0IGJ5ICcgJyAoPXdvcmQpCnRleHQgJT4lIHN0cl9yZW1vdmVfYWxsKCdbWzpwdW5jdDpdXScpICU+JSBzdHJfc3BsaXQoJyAnKSAlPiUgdW5saXN0KCkKYGBgCgpgYGB7cn0KdGV4dCAlPiUgc3RyX3RvX2xvd2VyKCkgJT4lIHN0cl9yZW1vdmVfYWxsKCdbWzpwdW5jdDpdXScpICU+JSBzdHJfc3BsaXQoJyAnKSAKYGBgCgojIFRoZSBSIE5MUCBlY29zeXN0ZW0gCgoqIE1vc3QgbGFuZ3VhZ2UgYW5hbHlzaXMgYXBwcm9hY2hlcyBhcmUgYmFzZWQgb24gdGhlIGFuYWx5c2lzIG9mIHRleHRzIHdvcmQtYnktd29yZC4gCiogSGVyZSwgdGhlaXIgb3JkZXIgbWlnaHQgbWF0dGVyICh3b3JkIHNlcXVlbmNlIG1vZGVscykgb3Igbm90IChiYWctb2Ytd29yZHMgbW9kZWxzKSwgYnV0IHRoZSBzbWFsbGVzdCB1bml0IG9mIGFuYWx5c2lzIGlzIHVzdWFsbHkgdGhlIHdvcmQuIAoqIFRoaXMgaXMgdXN1YWxseSBkb25lIGluIGNvbnRleHQgb2YgdGhlIGRvY3VtZW50IHRoZSB3b3JkIGFwcGVhcmVkIGluLiBUaGVyZWZvcmUsIG9uIGZpcnN0IGdsYW5jZSB0aHJlZSB0eXBlcyBkYXRhc3RydWN0dXJlcyBtYWtlIHNlbnNlOgoKMS4gKipUaWR5OioqICBBcHByb2FjaCwgd2hlcmUgZGF0YSBpcyBzZXJ2ZWQgaW4gYSAyLWNvbHVtbiBkb2N1bWVudC13b3JkIGZvcm1hdCAoZS5nLiwgYHRpZHl0ZXh0YCkKMi4gKipUb2tlbiBsaXN0czoqKiBDcmVhdGlvbiBvZiBzcGVjaWFsIG9iamVjdHMsIHNhdmVkIGFzIGRvY3VtZW50LXRva2VuIGxpc3RzIG9yIGNvcnB1cyAoZS5nLiwgYHRtYCwgYHF1YW50ZWRhYCkKMy4gKipNYXRyaXg6KiogTG9uZyBhcHByb2FjaCwgd2hlcmUgZGF0YSBpcyBzZXJ2ZWQgYXMgZG9jdW1lbnQtdGVybSBtYXRyaXgsIHRlcm0tZnJlcXVlbmN5IG1hdHJpeCwgZXRjLgoKKiBEaWZmZXJlbnQgZm9ybXMgb2YgYW5hbHlzaXMgKGFuZCB0aGUgcGFja2FnZXMgdXNlZCB0aGVyZWZvcmUpIGZhdm9yIGRpZmZlcmVudCBzdHJ1Y3R1cmVzLCBzbyB3ZSBuZWVkIHRvIGJlIGZsdWVudCBpbiB0cmFuc2ZlcmluZyBvcmlnaW5hbCByYXctdGV4dCBpbiAqIFRoZXNlIGZvcm1hdHMsIGFzIHdlbGwgYXMgc3dpdGNoaW5nIGJldHdlZW4gdGhlbS4gKGZvciBtb3JlIGluZm9zLCBjaGVjayBbaGVyZV0oaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL2R0bS5odG1sKSkuCgohW10oaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX21lZGlhL25scF90aWR5d29ya2Zsb3cucG5nKQoKIyMgVGlkeSBUZXh0IEZvcm1hdHMKCiogV2hpbGUgdGhlcmUgZXhpc3Qgb3RoZXIgZWNvc3lzdGVtcyB0byBkbyB0eHQgYW5hbHlzaXMgKGUuZy4sIGB0bWAsIGBxdWFudGVkYWApLCBJIHdpbGwgaGVyZSBhbG1vc3QgZXhjbHVzaXZlbHkgdXNlIGB0aWR5dGV4dGAsIHdoaWNoIGlzIHZlcnkgc2ltcGxlIHlldCBwb3dlcmZ1bCwgdmVyeSB3ZWxsIGRvY3VtZW50ZWQsIGFuZCB3b3JrcyB2ZXJ5IG5lYXRobHkgd2l0aCBgdGlkeW1vZGVsc2AgYW5kIHRoZSByZXN0IG9mIHRoZSBgdGlkeXZlcnNlYCBlY29zeXN0ZW0uCgoKYGBge3J9CmxpYnJhcnkodGlkeXRleHQpCmBgYAoKKiBXaGlsZSB3ZSB3aWxsIGZvciBsYXRlciBhcHBsaWNhdGlvbnMgd2Ugd2lsbCB1c2UgZGlmZmVyZW50IGZvcm1hdHMsIHdlIGhlcmUgd2lsbCBsaW1pdCBvdXJzZWx2ZXMgdG8gd29yZCB0b2tlbiwgd2hpY2ggY2FuIGRvIG1vc3Qgb2YgdGhlIHNpbXBsZSBqb2JzLgoqIEhlcmUsIHdlIGFwcGx5IHRpZHkgcHJpbmNpcGxlcyB0byB0ZXh0LCBtYWtlIHdvcmQtdG9rZW4gcGVyIGRvY3VtZW50IG91ciB1bml0IG9mIGFuYWx5c2lzLgoqIFRoZXJlZm9yZSwgZXZlcnkgcm93IHJlcHJlZXNlbnRzIGEgd29yZCBwZXIgZG9jdW1lbnQuClRoaXMgc291bmRzIGxpa2UgYSBsb3Qgb2YgcmVkdW5kYW5jeSwgYnV0IG1ha2VzIGl0IHZlcnkgZWFzeSB0byB3b3JrIHdpdGggY29tcGFyZWQgdG8gbW9yZSBjb21wbGV6IG1hdHJpeCBhbmQgbGlzdCBmb3JtYXRzLiBIZXJlLCB3ZSBjYW4gZG8gb3VyIHVzdWFsIHN1bWFycmllcyBhbmQgdmlzdWFsaXphdGlvbnMgcHJldHR5IG11Y2ggb3V0LW9mLXRoZS1ib3guCgpgYGB7cn0KIyBUaWR5dGV4dCB3YW50cyBhIHRpYmJsZSBhcyBwb2ludCBvZiBkZXBhcnR1cmUKdGV4dF90YmwgPC0gdGliYmxlKGlkID0gMSwgdGV4dCA9IHRleHQpCmBgYAoKYGBge3J9CiMgV2Ugbm93IHVubmVzdCB0aGUgdG9rZW5zLiBOb3RpY2UgaXQgaXMgYnkgZGVmYXVsdCBkZWxldGluZyBhbGwgcHVuY3R1YXRpb24gYW5kIHRyYW5zZm9ybWluZyB0aGUgdGV4dCB0byBsb3dlciBjaGFycy4KdGV4dF90aWR5IDwtIHRleHRfdGJsICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQsIHRva2VuID0gJ3dvcmRzJykKYGBgCgoqIE92ZXJhbGwsIGluIE5MUCB3ZSBhcmUgdHJ5aW5nIHRvIHJlcHJlc2VudCBtZWFuaW5nIHN0cnVjdHVyZS4gCiogVGhhdCBtZWFucyB0aGF0IHdlIHdhbnQgdG8gZm9jdXMgb24gdGhlIG1vc3QgaW1wb3J0YW50IGFuZCAibWVhbmluZy1iZWFyaW5nIGVsZW1lbnRzIiBpbiB0ZXh0LCB3aGlsZSByZWR1Y2luZyBub2lzZS4gCiogV29yZHMgc3VjaCBhcyAiYW5kIiwgImhhdmUiLCAidGhlIiBtYXkgaGF2ZSBjZW50cmFsIHN5bnRhY3RpYyBmdW5jdGlvbnMgYnV0IGFyZSBub3QgcGFydGljdWxhcmx5IGltcG9ydGFudCBmcm9tIGEgc2VtYW50aWMgcGVyc3BlY3RpdmUuCgpgYGB7cn0KIyBUaWR5dGV4dCBjb21lcyB3aXRoIGEgc3RvcHdvcmQgbGV4aWNvbgpzdG9wX3dvcmRzCmBgYAoKYGBge3J9CnRleHRfdGlkeSAlPD4lCiAgYW50aV9qb2luKHN0b3Bfd29yZHMsIGJ5ID0gJ3dvcmQnKQpgYGAKCmBgYHtyfQp0ZXh0X3RpZHkKYGBgCgoKYGBge3J9CiMgV2Ugbm93IHVubmVzdCB0aGUgdG9rZW5zLiBOb3RpY2UgaXQgaXMgYnkgZGVmYXVsdCBkZWxldGluZyBhbGwgcHVuY3R1YXRpb24gYW5kIHRyYW5zZm9ybWluZyB0aGUgdGV4dCB0byBsb3dlciBjaGFycy4Kc2VudGVuY2VzX3RpZHkgPC0gdGV4dF90YmwgJT4lIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCwgdG9rZW4gPSAnc2VudGVuY2VzJykKYGBgCgpgYGB7cn0Kc2VudGVuY2VzX3RpZHkKYGBgCgojIyBZb3VyIHR1cm4hCgohW10oaHR0cHM6Ly9tZWRpYS5naXBoeS5jb20vbWVkaWEvOXJ3RmZtQjJxSjBtRXNta2ZqL2dpcGh5LmdpZikKClRha2UgdGhlIGZvbGxvd2luZyB0ZXh0IGFuZCB0cmFuc2Zvcm0gaXQgaW50byBhIGxpc3Qgb2YgbGlzdHMgd2l0aCB3aXRoIGVhY2ggZWxlbWVudCBiZWluZyBhIHRva2VuaXplZCBzZW50ZW5jZS4gUmVtb3ZlIHN0b3B3b3JkcywgbG93ZXIgYWxsIHRva2VucyBhbmQga2VlcCBvbmx5ICgxKSBhbHBoYS1udW1lcmljIHdvcmQgdG9rZW5zLCAoMikgY2hhcmFjdGV3ciB0b2tlbnMuCgpgSeKAmXZlIGJlZW4gY2FsbGVkIG1hbnkgdGhpbmdzIGluIG15IGxpZmUsIGJ1dCBuZXZlciBhbiBvcHRpbWlzdC4gVGhhdCB3YXMgZmluZSBieSBtZS4gSSBiZWxpZXZlZCBwZXNzaW1pc3RzIGxpdmVkIGluIGEgY29uc3RhbnQgc3RhdGUgb2YgcGxlYXNhbnQgc3VycHJpc2U6IGlmIHlvdSBhbHdheXMgZXhwZWN0ZWQgdGhlIHdvcnN0LCB0aGluZ3MgZ2VuZXJhbGx5IHR1cm5lZCBvdXQgYmV0dGVyIHRoYW4geW91IGltYWdpbmVkLiBUaGUgb25seSByZWFsIHByb2JsZW0gd2l0aCBwZXNzaW1pc20sIEkgZmlndXJlZCwgd2FzIHRoYXQgdG9vIG11Y2ggb2YgaXQgY291bGQgYWNjaWRlbnRhbGx5IHR1cm4geW91IGludG8gYW4gb3B0aW1pc3QuYAoKc291cmNlOiBodHRwczovL3d3dy50aGVndWFyZGlhbi5jb20vZ2xvYmFsLzIwMTkvbm92LzIxL2dsYXNzLWhhbGYtZnVsbC1ob3ctaS1sZWFybmVkLXRvLWJlLWFuLW9wdGltaXN0LWluLWEtd2VlawoKIyBUcnVtcCBUd2VldHMgUHJvY2Vzc2luZyBtYW55IHNob3J0IHRleHRzIGFuZCBzaW1wbGUgc3RhdHMKCkFuIGludHJvZHVjdGlvbiB0byBOTFAgd291bGQgbm90IGJlIHRoZSBzYW1lIHdpdGhvdXQgRG9uYWxkJ3MgdHdlZXRzLiBMZXQncyB1c2UgdGhlc2UgdHdlZXRzIGZvciBzb21lIG1vcmUgYmFzaWMgTkxQIGFuZCBsZXQncyB0cnkgdG8gZ2F0aGVyIHNvbWUgaW5zaWdodHMuLi5tYXliZQoKIVtkb25hbGRfdHdlZXRzXShodHRwczovL2kuY2RuLmNubi5jb20vY25uL2ludGVyYWN0aXZlLzIwMTcvcG9saXRpY3MvdHJ1bXAtdHdlZXRzL21lZGlhL3RydW1wLXR3ZWV0cy1oZHItMDIuanBnKQoKTGV0J3MgdHJ5IHRvIHVzZSBzb21lIHZlcnkgc2ltcGxlIHN0YXRpc3RpY3Mgb24gdHdpdHRlciBkYXRhLCB0aGFua3MgdG8gW1RydW1wIFR3aXR0ZXIgQXJjaGl2ZV0oaHR0cDovL3d3dy50cnVtcHR3aXR0ZXJhcmNoaXZlLmNvbSkKCioqTm90ZToqKiBXZSBoZXJlIGFscmVhZHkgdXNlIHByZWNvbXBpbGVkIGRhdGEuIEhvd2V2ZXIsIHlvdSBjb3VsZCB1c2UgdGhlIFtgcnR3ZWV0YF0oaHR0cHM6Ly9naXRodWIuY29tL3JvcGVuc2NpL3J0d2VldCkgcGFja2FnZSBhbmQgaW5zdGVhZCB3b3JrIHdpdGggb3duIGRhdGEgb24gdHdlZXRzIG9mIGludGVyZXN0LgoKYGBge3J9CiMgd2Ugd2lsbCBsb2FkIHNvbWUganNvbiBmaWxlcwpsaWJyYXJ5KGpzb25saXRlKQpsaWJyYXJ5KHRpZHlqc29uKQpgYGAKCgpgYGB7cn0KIyBkb3dubG9hZCBhbmQgb3BlbiBzb21lIFRydW1wIHR3ZWV0cyBmcm9tIHRydW1wX3R3ZWV0X2RhdGFfYXJjaGl2ZQp0bXAgPC0gdGVtcGZpbGUoKQpkb3dubG9hZC5maWxlKCJodHRwczovL2dpdGh1Yi5jb20vYnBiMjcvdHJ1bXBfdHdlZXRfZGF0YV9hcmNoaXZlL3Jhdy9tYXN0ZXIvY29uZGVuc2VkXzIwMTguanNvbi56aXAiLCB0bXApCnRydW1wX3R3ZWV0cyA8LSBzdHJlYW1faW4odW56KHRtcCwgImNvbmRlbnNlZF8yMDE4Lmpzb24iKSkKYGBgCgpgYGB7cn0KdHJ1bXBfdHdlZXRzICU+JSBnbGltcHNlKCkKYGBgCgpgYGB7cn0KbGlicmFyeShsdWJyaWRhdGUpICMgRm9yIHdvcmtpbiB3aXRoIHRpbWVzCnRydW1wX3R3ZWV0cyAlPD4lCiAgbXV0YXRlKGNyZWF0ZWRfYXQgPSBwYXN0ZShzdWJzdHIoY3JlYXRlZF9hdCwyNywzMCksCiAgICAgICAgICAgICAgICAgICAgICBzdWJzdHIoY3JlYXRlZF9hdCw1LDcpLAogICAgICAgICAgICAgICAgICAgICAgc3Vic3RyKGNyZWF0ZWRfYXQsOSwxMCksCiAgICAgICAgICAgICAgICAgICAgICBzdWJzdHIoY3JlYXRlZF9hdCwxMiwyMCkpICU+JSAKICAgICAgICAgICBhc19kYXRldGltZSgpKQpgYGAKCk5vdHllOiBXZSB3aWxsIG5vdCB1c2UgdGhlIHRpbWVzIG9mIHR3ZWV0IGZvciBub3csIGJ1dCBmZWVsIGZyZWUgdG8gZGlzY292ZXIsIGFuZCBtYXliZSByZWNvbnN0cnVjdCBzb21ldGhpbmcgaW5zcGlyZWQgYnkgW1RISVMgQU1BWklORyBQQVBFUiEhIV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNzUxODExOS8pCgoKYGBge3J9CiMgTGV0cyBmaWx0ZXIgb3V0IHJldHdlZXRzCnRydW1wX3R3ZWV0cyAlPD4lCiAgZmlsdGVyKGlzX3JldHdlZXQgPT0gRkFMU0UpCmBgYAoKCmBgYHtyfQojIExFdHMgdG9rZW5pemUuIE5vdGljZSB0aGF0IHRoZXJlIGFyZSBzcGVjaWFsIHRva2VucyBmb3IgdHdlZXRzIHdoaWNoIGtlZXAgdXNlZnVsbCBzcGVjaWFsIGNoYXJhY3RlcnMKdHJ1bXBfdG9rZW4gPC10cnVtcF90d2VldHMgJT4lCiAgc2VsZWN0KGlkX3N0ciwgdGV4dCkgJT4lCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0LCB0b2tlbiA9ICJ0d2VldHMiKQpgYGAKCmBgYHtyfQp0cnVtcF90b2tlbiAlPD4lCiAgYW50aV9qb2luKHN0b3Bfd29yZHMsIGJ5ID0gJ3dvcmQnKQpgYGAKCgpgYGB7cn0KdHJ1bXBfdG9rZW4gJT4lIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUgaGVhZCgxMDApCmBgYAoKTGV0cyBzZWUgd2hvIHRydW1wIG1lbnRpb25zCgpgYGB7cn0KdHJ1bXBfdG9rZW4gJT4lCiAgZmlsdGVyKHdvcmQgJT4lIHN0cl9kZXRlY3QoJ0AnKSkgJT4lCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCmBgYAoKIyMgWW91ciB0dXJuCgohW2FsdCB0ZXh0XShodHRwczovL21lZGlhLmdpcGh5LmNvbS9tZWRpYS9KSVg5dDJqMFpUTjlTL2dpcGh5LmdpZikKClRoZSBsaW5rIGJlbG93IGhvbGRzIGEgZGF0YXNld3Qgd2l0aCB+MTBrICNPS0Jvb21lciB0d2VldHMgZnJvbSB0aGUgZGF5cyAxMC0yMSBOb3YgMjAxOS4KCmh0dHBzOi8vZ2l0aHViLmNvbS9TRFMtQUFVL1NEUy1tYXN0ZXIvcmF3L21hc3Rlci9NMi9kYXRhL3R3ZWV0c19ib29tZXIuemlwCgpXaGF0IHRvIGRvOiAKKiBVc2UgZWxlbWVudHMgZnJvbSB0aGUgYWJvdmUgY29kZSB0byBtYWtlIGEgbGlzdCBvZiB0aGUgbW9zdCBjb21tb24gaGFzaHRhZ3MgKHlvdSBoYXZlIHRvIGdldCB0aGUgaGFzaHRhZ3MgZnJvbSB0aGUgdGV4dCwgbm90IHVzaW5nIHRoZSBjb2x1bW4gY29udGFpbmluZyB0aGVtIGFscmVhZHkpCiogQWxzbyB0cnkgdG8gaGF2ZSBhIGxvb2sgYXQgaGFzaHRhZ3Mgb3ZlciB0aW1lOiBUYWtlIG91dCB0aGUgMTAgbW9zdCBjb21tb24gaGFzaHRhZ3MgLSBleGNsdWRpbmcgI09LQm9vbWVyIC0gYW5kIHBsb3QgdGhlaXIgb2NjdXJyZW5jZSBvdmVyIHRoZSBkYXlzIGluIHRoZSBkYXRhCgpQbGFuIG9mIGF0dGFjazoKCiogICBDb252ZXJ0IHRoZSB0aW1lc3RhbXAgaW50byBhIGRhdGV0aW1lCiogICBDYWxjdWxhdGUgdGhlIG9jY3VyZW5jZSBvZiB0aGUgc3BlY2lmaWMgaGFzaHRhZ3MgKGl0ZW50aWZpZWQgYnkgYSB0cmFpbGluZyBgI2ApIGluIHRoZSBjaG9zZW4gdGltZXNwYW4gKGhlcmU6IERheXMpCiogICBQbG90IChkYXlzIG9uIHgsIG4gb24geSkKCkdvIQoKIyBFbmRub3RlcwoKIyMjIE1haW4gcmVmZXJlbmNlCgoqIFIgZm9yIERhdGEgU2NpZW5jZSAoR3JvbGVtdW5kICYgV2lja2hhbSkKICAgKiBbQ2hhcHRlciAxNF0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9zdHJpbmdzLmh0bWwpOiBUbyByZWZyZXNoIHNpbXBsZSBzdHJpbmcgbWFuaXB1bGF0aW9ucwoqIEp1bGlhIFNpbGdlIGFuZCBEYXZpZCBSb2JpbnNvbiAoMjAyMCkuIFRleHQgTWluaW5nIHdpdGggUjogQSBUaWR5IEFwcHJvYWNoLCBP4oCZUmVpbGx5LiBPbmxpbmUgYXZhaWxhYmxlIFtoZXJlXShodHRwczovL3d3dy50aWR5dGV4dG1pbmluZy5jb20vKQogICAqIFtDaGFwdGVyIDFdKGh0dHBzOi8vd3d3LnRpZHl0ZXh0bWluaW5nLmNvbS90aWR5dGV4dC5odG1sKTogSW50cm9kdWN0aW9uIHRvIHRoZSB0aWR5IHRleHQgZm9ybWF0CgojIyMgUGFja2FnZXMgJiBFY29zeXN0ZW0KCiogW2B0aWR5dGV4dGBdKGh0dHBzOi8vZ2l0aHViLmNvbS9qdWxpYXNpbGdlL3RpZHl0ZXh0KQoKZnVydGhlcjogCiogW2BydHdlZXRgXShodHRwczovL2dpdGh1Yi5jb20vcm9wZW5zY2kvcnR3ZWV0KTogUiBpbnRlcmZhY2UgdG8gdGhlIHR3aXR0ZXIgQVBJLgoKIyMjIFN1Z2dlc3Rpb25zIGZvciBmdXJ0aGVyIHN0dWR5CgoqIERhdGFDYW1wICghTW9zdCBjb3Vyc2VzIGhhdmUgc29tZXdoYXQgb3V0ZGF0ZWQgZWNvc3lzdGVtcykKICAgKiBbSW50cm9kdWN0aW9uIHRvIFRleHQgQW5hbHlzaXMgaW4gUl0oaHR0cHM6Ly9sZWFybi5kYXRhY2FtcC5jb20vY291cnNlcy9pbnRyb2R1Y3Rpb24tdG8tdGV4dC1hbmFseXNpcy1pbi1yKTogVGhlIGJhc2ljcyBvZiB0ZXh0IGFuYWx5c2lzIGluIFIgICAgICAKICAgKiBbSW50cm9kdWN0aW9uIHRvIE5hdHVyYWwgTGFuZ3VhZ2UgUHJvY2Vzc2luZyBpbiBSXShodHRwczovL2xlYXJuLmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2ludHJvZHVjdGlvbi10by1uYXR1cmFsLWxhbmd1YWdlLXByb2Nlc3NpbmctaW4tcik6IFNvbWUgcmVmcmVzaGVyIHBsdXMgbW9yZSBhZHZhbmNlZCBhcHBsaWNhdGlvbnMgaW4gdGhlIGVuZC4gICAgICAgICAKICAKIyMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgoKCgoK