Text Mining ini dilakukan dengan membandingkan hasil visualisasi text data provider “TELKOMSEL” dengan “INDOSAT”. Hasil visualisasinya akan ditampilkan sebagai berikut.

TELKOMSEL

Extracting Tweets

Retrieve tweets from Twitter

# Load packages
library(rtweet)
library(tidyverse)
# Access token and APIs
consumer_key    <- "sxxdmMv0ceEXTFN0ZlqsdTcdu"
consumer_secret <- "6Ywu6aF9D4f684tv3bhT6j2bt31Te6pUbDqCfUKjoXXiCG2Lq9"
access_token    <- "410739589-a3DjrTrEOD6LQuRkXOfaHjxzbnsMRp9ReeHOxXCT"
access_secret   <- "DFykQwVLaXtILZ2Yph8gmS49XoIpooa8AGGcRUbcmyInw"
# Twitter authentication
create_token(
  app             = "my_twitter_research_app",
  consumer_key    = consumer_key,
  consumer_secret = consumer_secret,
  access_token    = access_token,
  access_secret   = access_secret)
<Token>
<oauth_endpoint>
 request:   https://api.twitter.com/oauth/request_token
 authorize: https://api.twitter.com/oauth/authenticate
 access:    https://api.twitter.com/oauth/access_token
<oauth_app> my_twitter_research_app
  key:    sxxdmMv0ceEXTFN0ZlqsdTcdu
  secret: <hidden>
<credentials> oauth_token, oauth_token_secret
---
# Retrieve tweets
tweets1 <- search_tweets("Telkomsel", n = 8000, tweet_mode="extended")
Searching for tweets...
Rate limit exceeded - 88Rate limit exceededFinished collecting tweets!
tweets1 <- distinct(tweets1, text, .keep_all=TRUE)
Trying to compute distinct() for variables not found in the data:
- `text`
This is an error, but only a warning is raised for compatibility reasons.
The operation will return the input unchanged.

Tweets Description

ts_plot(tweets1, "3 hours") +
  theme_minimal() +
  theme(plot.title = ggplot2::element_text(face = "bold")) +
  labs(
    x = NULL, y = NULL,
    title = "Frequency of Telkomsel Twitter statuses from past 3 hours",
    subtitle = "Twitter status (tweet) counts aggregated using three-hour intervals",
    caption = "\nSource: Data collected from Twitter's REST API via rtweet"
  )
Error: no datetime (POSIXct) var found
tail(tweets1, 20)

Text Cleaning

Build corpus

# build a corpus, and specify the source to be character vectors 
myCorpus1 <- Corpus(VectorSource(tweets1$text))
# convert to lower case
myCorpus1 <- tm_map(myCorpus1, content_transformer(tolower))
transformation drops documents
# remove URLs
removeURL1 <- function(x) gsub("http[^[:space:]]*", "", x)
myCorpus1 <- tm_map(myCorpus1, content_transformer(removeURL1))
transformation drops documents
# remove anything other than English letters or space 
removeNumPunct1 <- function(x) gsub("[^[:alpha:][:space:]]*", "", x) 
myCorpus1 <- tm_map(myCorpus1, content_transformer(removeNumPunct1))
transformation drops documents
# remove stopwords
myStopwords1 <- c(setdiff(stopwords('english'), c("r", "big")), "use", "see", "cepat", "via", "data", "Telkomsel")
stopwords_id <- read.table("E://stopwords-id.txt", header = FALSE)
myStopwords1 <- c(myStopwords1, as.matrix(stopwords_id$V1), "hi", "yg")
myCorpus1 <- tm_map(myCorpus1, removeWords, myStopwords1)
transformation drops documents
# remove extra whitespace
myCorpus1 <- tm_map(myCorpus1, stripWhitespace)
transformation drops documents
# keep a copy for stem completion later
myCorpusCopy1 <- myCorpus1

Frequent Words

Build Term Document Matrix

tdm1 <- TermDocumentMatrix(myCorpus1, control = list(wordLengths = c(1, Inf)))
tdm1
<<TermDocumentMatrix (terms: 5310, documents: 3480)>>
Non-/sparse entries: 45549/18433251
Sparsity           : 100%
Maximal term length: 41
Weighting          : term frequency (tf)

Top Frequent Terms

freq.terms1 <- findFreqTerms(tdm1, lowfreq = 20)
freq.terms1[1:100]
term.freq1 <- rowSums(as.matrix(tdm1))
term.freq1 <- subset(term.freq1, term.freq1 >= 150)
df1 <- data.frame(term1 = names(term.freq1), freq1 = term.freq1)
ggplot(df1, aes(x=term1, y=freq1)) + geom_bar(stat="identity") +
  xlab("Terms") + ylab("Count") + coord_flip() +
  theme(axis.text=element_text(size=7))

Wordcloud

Build Wordcloud

library(wordcloud)
m1 <- as.matrix(tdm1)
# calculate the frequency of words and sort it by frequency 
word.freq1 <- sort(rowSums(m1), decreasing = T)
# colors
pal1 <- brewer.pal(9, "BuGn")[-(1:4)]
TELKOMSEL = wordcloud(words = names(word.freq1), freq = word.freq1, min.freq = 100,
    random.order = F, colors = pal1)

INDOSAT

Extracting Tweets

Retrieve tweets from Twitter

# Load packages
library(rtweet)
library(tidyverse)
# Access token and APIs
consumer_key    <- "sxxdmMv0ceEXTFN0ZlqsdTcdu"
consumer_secret <- "6Ywu6aF9D4f684tv3bhT6j2bt31Te6pUbDqCfUKjoXXiCG2Lq9"
access_token    <- "410739589-a3DjrTrEOD6LQuRkXOfaHjxzbnsMRp9ReeHOxXCT"
access_secret   <- "DFykQwVLaXtILZ2Yph8gmS49XoIpooa8AGGcRUbcmyInw"
# Twitter authentication
create_token(
  app             = "my_twitter_research_app",
  consumer_key    = consumer_key,
  consumer_secret = consumer_secret,
  access_token    = access_token,
  access_secret   = access_secret)
<Token>
<oauth_endpoint>
 request:   https://api.twitter.com/oauth/request_token
 authorize: https://api.twitter.com/oauth/authenticate
 access:    https://api.twitter.com/oauth/access_token
<oauth_app> my_twitter_research_app
  key:    sxxdmMv0ceEXTFN0ZlqsdTcdu
  secret: <hidden>
<credentials> oauth_token, oauth_token_secret
---
# Retrieve tweets
tweets2 <- search_tweets("Indosat", n =1000, tweet_mode="extended")
Searching for tweets...
Rate limit exceeded - 88Rate limit exceededFinished collecting tweets!
tweets2 <- distinct(tweets2, text, .keep_all=TRUE)
Trying to compute distinct() for variables not found in the data:
- `text`
This is an error, but only a warning is raised for compatibility reasons.
The operation will return the input unchanged.

Tweets Description

ts_plot(tweets, "3 hours") +
  theme_minimal() +
  theme(plot.title = ggplot2::element_text(face = "bold")) +
  labs(
    x = NULL, y = NULL,
    title = "Frequency of Indosat Twitter statuses from past 3 hours",
    subtitle = "Twitter status (tweet) counts aggregated using three-hour intervals",
    caption = "\nSource: Data collected from Twitter's REST API via rtweet"
  )
Error: no datetime (POSIXct) var found
tail(tweets, 20)

Text Cleaning

library(tm)
library(NLP)

Build corpus

# build a corpus, and specify the source to be character vectors 
myCorpus <- Corpus(VectorSource(tweets$text))
# convert to lower case
myCorpus <- tm_map(myCorpus, content_transformer(tolower))
transformation drops documents
# remove URLs
removeURL <- function(x) gsub("http[^[:space:]]*", "", x)
myCorpus <- tm_map(myCorpus, content_transformer(removeURL))
transformation drops documents
# remove anything other than English letters or space 
removeNumPunct <- function(x) gsub("[^[:alpha:][:space:]]*", "", x) 
myCorpus <- tm_map(myCorpus, content_transformer(removeNumPunct))
transformation drops documents
# remove stopwords
myStopwords <- c(setdiff(stopwords('english'), c("r", "big")), "use", "see", "used", "via", "amp", "indihome")
stopwords_id <- read.table("E://stopwords-id.txt", header = FALSE)
myStopwords <- c(myStopwords, as.matrix(stopwords_id$V1), "hi", "yg")
myCorpus <- tm_map(myCorpus, removeWords, myStopwords)
transformation drops documents
# remove extra whitespace
myCorpus <- tm_map(myCorpus, stripWhitespace)
transformation drops documents
# keep a copy for stem completion later
myCorpusCopy <- myCorpus

Frequent Words

Build Term Document Matrix

tdm <- TermDocumentMatrix(myCorpus, control = list(wordLengths = c(1, Inf)))
tdm
<<TermDocumentMatrix (terms: 11372, documents: 5314)>>
Non-/sparse entries: 74440/60356368
Sparsity           : 100%
Maximal term length: 48
Weighting          : term frequency (tf)

Top Frequent Terms_2

freq.terms <- findFreqTerms(tdm, lowfreq = 20)
freq.terms[1:50]
 [1] "beli"        "gb"          "indosat"     "indosatcare" "paketan"     "rb"         
 [7] "bagus"       "gak"         "ya"          "aksesnya"    "coba"        "dm"         
[13] "hpnya"       "jaringan"    "kak"         "manual"      "masuk"       "nomornya"   
[19] "operator"    "pengaturan"  "pindah"      "pindahkan"   "rio"         "setting"    
[25] "sinyalnya"   "thanks"      "blm"         "detail"      "diubah"      "kondisi"    
[31] "lokasi"      "nomor"       "otomatis"    "pengguna"    "pilih"       "salamchun"  
[37] "sukses"      "hai"         "mey"         "akses"       "bantu"       "berkendala" 
[43] "cek"         "indy"        "internet"    "jalan"       "kecamatan"   "keterangan" 
[49] "kota"        "mengalami"  
term.freq <- rowSums(as.matrix(tdm))
term.freq <- subset(term.freq, term.freq >= 150)
df <- data.frame(term = names(term.freq), freq = term.freq)
ggplot(df, aes(x=term, y=freq)) + geom_bar(stat="identity") +
  xlab("Terms") + ylab("Count") + coord_flip() +
  theme(axis.text=element_text(size=7))

Wordcloud

Build Wordcloud

library(wordcloud)
library(RColorBrewer)
m <- as.matrix(tdm)
# calculate the frequency of words and sort it by frequency 
word.freq <- sort(rowSums(m), decreasing = T)
# colors
pal <- brewer.pal(9, "BuGn")[-(1:4)]
INDOSAT = wordcloud(words = names(word.freq), freq = word.freq, min.freq = 100,
    random.order = F, colors = pal)

Telkomsel VS Indosat

print("TELKOMSEL")
print("INDOSAT")
LS0tDQp0aXRsZTogIlRleHQgTWluaW5nIC0gYnkgUm9zaWtodSBJbG1pIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0b2M6IHllcw0KLS0tDQpUZXh0IE1pbmluZyBpbmkgZGlsYWt1a2FuIGRlbmdhbiBtZW1iYW5kaW5na2FuIGhhc2lsIHZpc3VhbGlzYXNpIHRleHQgZGF0YSBwcm92aWRlciAiVEVMS09NU0VMIiBkZW5nYW4gIklORE9TQVQiLiBIYXNpbCB2aXN1YWxpc2FzaW55YSBha2FuIGRpdGFtcGlsa2FuIHNlYmFnYWkgYmVyaWt1dC4gDQoNCiNURUxLT01TRUwNCg0KIyMgRXh0cmFjdGluZyBUd2VldHMNCg0KIyMjIFJldHJpZXZlIHR3ZWV0cyBmcm9tIFR3aXR0ZXINCg0KYGBge3J9DQojIExvYWQgcGFja2FnZXMNCmxpYnJhcnkocnR3ZWV0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KYGBge3J9DQojIEFjY2VzcyB0b2tlbiBhbmQgQVBJcw0KY29uc3VtZXJfa2V5ICAgIDwtICJzeHhkbU12MGNlRVhURk4wWmxxc2RUY2R1Ig0KY29uc3VtZXJfc2VjcmV0IDwtICI2WXd1NmFGOUQ0ZjY4NHR2M2JoVDZqMmJ0MzFUZTZwVWJEcUNmVUtqb1hYaUNHMkxxOSINCmFjY2Vzc190b2tlbiAgICA8LSAiNDEwNzM5NTg5LWEzRGpyVHJFT0Q2TFF1UmtYT2ZhSGp4emJuc01ScDlSZWVIT3hYQ1QiDQphY2Nlc3Nfc2VjcmV0ICAgPC0gIkRGeWtRd1ZMYVh0SUxaMllwaDhnbVM0OVhvSXBvb2E4QUdHY1JVYmNteUludyINCmBgYA0KDQpgYGB7cn0NCiMgVHdpdHRlciBhdXRoZW50aWNhdGlvbg0KY3JlYXRlX3Rva2VuKA0KICBhcHAgICAgICAgICAgICAgPSAibXlfdHdpdHRlcl9yZXNlYXJjaF9hcHAiLA0KICBjb25zdW1lcl9rZXkgICAgPSBjb25zdW1lcl9rZXksDQogIGNvbnN1bWVyX3NlY3JldCA9IGNvbnN1bWVyX3NlY3JldCwNCiAgYWNjZXNzX3Rva2VuICAgID0gYWNjZXNzX3Rva2VuLA0KICBhY2Nlc3Nfc2VjcmV0ICAgPSBhY2Nlc3Nfc2VjcmV0KQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZXRyaWV2ZSB0d2VldHMNCnR3ZWV0czEgPC0gc2VhcmNoX3R3ZWV0cygiVGVsa29tc2VsIiwgbiA9IDgwMDAsIHR3ZWV0X21vZGU9ImV4dGVuZGVkIikNCnR3ZWV0czEgPC0gZGlzdGluY3QodHdlZXRzMSwgdGV4dCwgLmtlZXBfYWxsPVRSVUUpDQpgYGANCg0KIyMjIFR3ZWV0cyBEZXNjcmlwdGlvbg0KDQpgYGB7cn0NCnRzX3Bsb3QodHdlZXRzMSwgIjMgaG91cnMiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpICsNCiAgbGFicygNCiAgICB4ID0gTlVMTCwgeSA9IE5VTEwsDQogICAgdGl0bGUgPSAiRnJlcXVlbmN5IG9mIFRlbGtvbXNlbCBUd2l0dGVyIHN0YXR1c2VzIGZyb20gcGFzdCAzIGhvdXJzIiwNCiAgICBzdWJ0aXRsZSA9ICJUd2l0dGVyIHN0YXR1cyAodHdlZXQpIGNvdW50cyBhZ2dyZWdhdGVkIHVzaW5nIHRocmVlLWhvdXIgaW50ZXJ2YWxzIiwNCiAgICBjYXB0aW9uID0gIlxuU291cmNlOiBEYXRhIGNvbGxlY3RlZCBmcm9tIFR3aXR0ZXIncyBSRVNUIEFQSSB2aWEgcnR3ZWV0Ig0KICApDQpgYGANCg0KYGBge3J9DQp0YWlsKHR3ZWV0czEsIDIwKQ0KYGBgDQoNCg0KIyMgVGV4dCBDbGVhbmluZw0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmxpYnJhcnkodG0pDQpsaWJyYXJ5KE5MUCkNCmBgYA0KIyMjIEJ1aWxkIGNvcnB1cw0KDQpgYGB7cn0NCiMgYnVpbGQgYSBjb3JwdXMsIGFuZCBzcGVjaWZ5IHRoZSBzb3VyY2UgdG8gYmUgY2hhcmFjdGVyIHZlY3RvcnMgDQpteUNvcnB1czEgPC0gQ29ycHVzKFZlY3RvclNvdXJjZSh0d2VldHMxJHRleHQpKQ0KIyBjb252ZXJ0IHRvIGxvd2VyIGNhc2UNCm15Q29ycHVzMSA8LSB0bV9tYXAobXlDb3JwdXMxLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQ0KIyByZW1vdmUgVVJMcw0KcmVtb3ZlVVJMMSA8LSBmdW5jdGlvbih4KSBnc3ViKCJodHRwW15bOnNwYWNlOl1dKiIsICIiLCB4KQ0KbXlDb3JwdXMxIDwtIHRtX21hcChteUNvcnB1czEsIGNvbnRlbnRfdHJhbnNmb3JtZXIocmVtb3ZlVVJMMSkpDQojIHJlbW92ZSBhbnl0aGluZyBvdGhlciB0aGFuIEVuZ2xpc2ggbGV0dGVycyBvciBzcGFjZSANCnJlbW92ZU51bVB1bmN0MSA8LSBmdW5jdGlvbih4KSBnc3ViKCJbXls6YWxwaGE6XVs6c3BhY2U6XV0qIiwgIiIsIHgpIA0KbXlDb3JwdXMxIDwtIHRtX21hcChteUNvcnB1czEsIGNvbnRlbnRfdHJhbnNmb3JtZXIocmVtb3ZlTnVtUHVuY3QxKSkNCiMgcmVtb3ZlIHN0b3B3b3Jkcw0KbXlTdG9wd29yZHMxIDwtIGMoc2V0ZGlmZihzdG9wd29yZHMoJ2VuZ2xpc2gnKSwgYygiciIsICJiaWciKSksICJ1c2UiLCAic2VlIiwgImNlcGF0IiwgInZpYSIsICJkYXRhIiwgIlRlbGtvbXNlbCIpDQpzdG9wd29yZHNfaWQgPC0gcmVhZC50YWJsZSgiRTovL3N0b3B3b3Jkcy1pZC50eHQiLCBoZWFkZXIgPSBGQUxTRSkNCm15U3RvcHdvcmRzMSA8LSBjKG15U3RvcHdvcmRzMSwgYXMubWF0cml4KHN0b3B3b3Jkc19pZCRWMSksICJoaSIsICJ5ZyIpDQpteUNvcnB1czEgPC0gdG1fbWFwKG15Q29ycHVzMSwgcmVtb3ZlV29yZHMsIG15U3RvcHdvcmRzMSkNCiMgcmVtb3ZlIGV4dHJhIHdoaXRlc3BhY2UNCm15Q29ycHVzMSA8LSB0bV9tYXAobXlDb3JwdXMxLCBzdHJpcFdoaXRlc3BhY2UpDQojIGtlZXAgYSBjb3B5IGZvciBzdGVtIGNvbXBsZXRpb24gbGF0ZXINCm15Q29ycHVzQ29weTEgPC0gbXlDb3JwdXMxDQpgYGANCiMjIEZyZXF1ZW50IFdvcmRzDQoNCiMjIyBCdWlsZCBUZXJtIERvY3VtZW50IE1hdHJpeA0KYGBge3J9DQp0ZG0xIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChteUNvcnB1czEsIGNvbnRyb2wgPSBsaXN0KHdvcmRMZW5ndGhzID0gYygxLCBJbmYpKSkNCmBgYA0KDQpgYGB7cn0NCnRkbTENCmBgYA0KDQojIyMgVG9wIEZyZXF1ZW50IFRlcm1zDQoNCmBgYHtyfQ0KZnJlcS50ZXJtczEgPC0gZmluZEZyZXFUZXJtcyh0ZG0xLCBsb3dmcmVxID0gMjApDQpgYGANCmBgYHtyfQ0KZnJlcS50ZXJtczFbMToxMDBdDQpgYGANCg0KYGBge3J9DQp0ZXJtLmZyZXExIDwtIHJvd1N1bXMoYXMubWF0cml4KHRkbTEpKQ0KdGVybS5mcmVxMSA8LSBzdWJzZXQodGVybS5mcmVxMSwgdGVybS5mcmVxMSA+PSAxNTApDQpkZjEgPC0gZGF0YS5mcmFtZSh0ZXJtMSA9IG5hbWVzKHRlcm0uZnJlcTEpLCBmcmVxMSA9IHRlcm0uZnJlcTEpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGYxLCBhZXMoeD10ZXJtMSwgeT1mcmVxMSkpICsgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArDQogIHhsYWIoIlRlcm1zIikgKyB5bGFiKCJDb3VudCIpICsgY29vcmRfZmxpcCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcpKQ0KYGBgDQoNCiMjIFdvcmRjbG91ZA0KDQojIyMgQnVpbGQgV29yZGNsb3VkDQpgYGB7cn0NCmxpYnJhcnkod29yZGNsb3VkKQ0KYGBgDQoNCmBgYHtyfQ0KbTEgPC0gYXMubWF0cml4KHRkbTEpDQojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIHdvcmRzIGFuZCBzb3J0IGl0IGJ5IGZyZXF1ZW5jeSANCndvcmQuZnJlcTEgPC0gc29ydChyb3dTdW1zKG0xKSwgZGVjcmVhc2luZyA9IFQpDQojIGNvbG9ycw0KcGFsMSA8LSBicmV3ZXIucGFsKDksICJCdUduIilbLSgxOjQpXQ0KYGBgDQoNCg0KYGBge3J9DQpURUxLT01TRUwgPSB3b3JkY2xvdWQod29yZHMgPSBuYW1lcyh3b3JkLmZyZXExKSwgZnJlcSA9IHdvcmQuZnJlcTEsIG1pbi5mcmVxID0gMTAwLA0KICAgIHJhbmRvbS5vcmRlciA9IEYsIGNvbG9ycyA9IHBhbDEpDQpgYGANCg0KI0lORE9TQVQNCg0KIyMgRXh0cmFjdGluZyBUd2VldHMNCg0KIyMjIFJldHJpZXZlIHR3ZWV0cyBmcm9tIFR3aXR0ZXINCg0KYGBge3J9DQojIExvYWQgcGFja2FnZXMNCmxpYnJhcnkocnR3ZWV0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KYGBge3J9DQojIEFjY2VzcyB0b2tlbiBhbmQgQVBJcw0KY29uc3VtZXJfa2V5ICAgIDwtICJzeHhkbU12MGNlRVhURk4wWmxxc2RUY2R1Ig0KY29uc3VtZXJfc2VjcmV0IDwtICI2WXd1NmFGOUQ0ZjY4NHR2M2JoVDZqMmJ0MzFUZTZwVWJEcUNmVUtqb1hYaUNHMkxxOSINCmFjY2Vzc190b2tlbiAgICA8LSAiNDEwNzM5NTg5LWEzRGpyVHJFT0Q2TFF1UmtYT2ZhSGp4emJuc01ScDlSZWVIT3hYQ1QiDQphY2Nlc3Nfc2VjcmV0ICAgPC0gIkRGeWtRd1ZMYVh0SUxaMllwaDhnbVM0OVhvSXBvb2E4QUdHY1JVYmNteUludyINCmBgYA0KDQpgYGB7cn0NCiMgVHdpdHRlciBhdXRoZW50aWNhdGlvbg0KY3JlYXRlX3Rva2VuKA0KICBhcHAgICAgICAgICAgICAgPSAibXlfdHdpdHRlcl9yZXNlYXJjaF9hcHAiLA0KICBjb25zdW1lcl9rZXkgICAgPSBjb25zdW1lcl9rZXksDQogIGNvbnN1bWVyX3NlY3JldCA9IGNvbnN1bWVyX3NlY3JldCwNCiAgYWNjZXNzX3Rva2VuICAgID0gYWNjZXNzX3Rva2VuLA0KICBhY2Nlc3Nfc2VjcmV0ICAgPSBhY2Nlc3Nfc2VjcmV0KQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZXRyaWV2ZSB0d2VldHMNCnR3ZWV0cyA8LSBzZWFyY2hfdHdlZXRzKCJJbmRvc2F0IiwgbiA9MTAwMCwgdHdlZXRfbW9kZT0iZXh0ZW5kZWQiKQ0KdHdlZXRzIDwtIGRpc3RpbmN0KHR3ZWV0cywgdGV4dCwgLmtlZXBfYWxsPVRSVUUpDQpgYGANCg0KDQojIyMgVHdlZXRzIERlc2NyaXB0aW9uDQoNCmBgYHtyfQ0KdHNfcGxvdCh0d2VldHMsICIzIGhvdXJzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpKSArDQogIGxhYnMoDQogICAgeCA9IE5VTEwsIHkgPSBOVUxMLA0KICAgIHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBJbmRvc2F0IFR3aXR0ZXIgc3RhdHVzZXMgZnJvbSBwYXN0IDMgaG91cnMiLA0KICAgIHN1YnRpdGxlID0gIlR3aXR0ZXIgc3RhdHVzICh0d2VldCkgY291bnRzIGFnZ3JlZ2F0ZWQgdXNpbmcgdGhyZWUtaG91ciBpbnRlcnZhbHMiLA0KICAgIGNhcHRpb24gPSAiXG5Tb3VyY2U6IERhdGEgY29sbGVjdGVkIGZyb20gVHdpdHRlcidzIFJFU1QgQVBJIHZpYSBydHdlZXQiDQogICkNCmBgYA0KDQpgYGB7cn0NCnRhaWwodHdlZXRzLCAyMCkNCmBgYA0KDQoNCiMjIFRleHQgQ2xlYW5pbmcNCg0KYGBge3J9DQpsaWJyYXJ5KHRtKQ0KbGlicmFyeShOTFApDQpgYGANCiMjIyBCdWlsZCBjb3JwdXMNCg0KYGBge3J9DQojIGJ1aWxkIGEgY29ycHVzLCBhbmQgc3BlY2lmeSB0aGUgc291cmNlIHRvIGJlIGNoYXJhY3RlciB2ZWN0b3JzIA0KbXlDb3JwdXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZSh0d2VldHMkdGV4dCkpDQojIGNvbnZlcnQgdG8gbG93ZXIgY2FzZQ0KbXlDb3JwdXMgPC0gdG1fbWFwKG15Q29ycHVzLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQ0KIyByZW1vdmUgVVJMcw0KcmVtb3ZlVVJMIDwtIGZ1bmN0aW9uKHgpIGdzdWIoImh0dHBbXls6c3BhY2U6XV0qIiwgIiIsIHgpDQpteUNvcnB1cyA8LSB0bV9tYXAobXlDb3JwdXMsIGNvbnRlbnRfdHJhbnNmb3JtZXIocmVtb3ZlVVJMKSkNCiMgcmVtb3ZlIGFueXRoaW5nIG90aGVyIHRoYW4gRW5nbGlzaCBsZXR0ZXJzIG9yIHNwYWNlIA0KcmVtb3ZlTnVtUHVuY3QgPC0gZnVuY3Rpb24oeCkgZ3N1YigiW15bOmFscGhhOl1bOnNwYWNlOl1dKiIsICIiLCB4KSANCm15Q29ycHVzIDwtIHRtX21hcChteUNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcihyZW1vdmVOdW1QdW5jdCkpDQojIHJlbW92ZSBzdG9wd29yZHMNCm15U3RvcHdvcmRzIDwtIGMoc2V0ZGlmZihzdG9wd29yZHMoJ2VuZ2xpc2gnKSwgYygiciIsICJiaWciKSksICJ1c2UiLCAic2VlIiwgInVzZWQiLCAidmlhIiwgImFtcCIsICJpbmRpaG9tZSIpDQpzdG9wd29yZHNfaWQgPC0gcmVhZC50YWJsZSgiRTovL3N0b3B3b3Jkcy1pZC50eHQiLCBoZWFkZXIgPSBGQUxTRSkNCm15U3RvcHdvcmRzIDwtIGMobXlTdG9wd29yZHMsIGFzLm1hdHJpeChzdG9wd29yZHNfaWQkVjEpLCAiaGkiLCAieWciKQ0KbXlDb3JwdXMgPC0gdG1fbWFwKG15Q29ycHVzLCByZW1vdmVXb3JkcywgbXlTdG9wd29yZHMpDQojIHJlbW92ZSBleHRyYSB3aGl0ZXNwYWNlDQpteUNvcnB1cyA8LSB0bV9tYXAobXlDb3JwdXMsIHN0cmlwV2hpdGVzcGFjZSkNCiMga2VlcCBhIGNvcHkgZm9yIHN0ZW0gY29tcGxldGlvbiBsYXRlcg0KbXlDb3JwdXNDb3B5IDwtIG15Q29ycHVzDQpgYGANCiMjIEZyZXF1ZW50IFdvcmRzDQoNCiMjIyBCdWlsZCBUZXJtIERvY3VtZW50IE1hdHJpeA0KYGBge3J9DQp0ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KG15Q29ycHVzLCBjb250cm9sID0gbGlzdCh3b3JkTGVuZ3RocyA9IGMoMSwgSW5mKSkpDQpgYGANCg0KYGBge3J9DQp0ZG0NCmBgYA0KDQojIyMgVG9wIEZyZXF1ZW50IFRlcm1zXzINCg0KYGBge3J9DQpmcmVxLnRlcm1zIDwtIGZpbmRGcmVxVGVybXModGRtLCBsb3dmcmVxID0gMjApDQpgYGANCmBgYHtyfQ0KZnJlcS50ZXJtc1sxOjUwXQ0KYGBgDQoNCmBgYHtyfQ0KdGVybS5mcmVxIDwtIHJvd1N1bXMoYXMubWF0cml4KHRkbSkpDQp0ZXJtLmZyZXEgPC0gc3Vic2V0KHRlcm0uZnJlcSwgdGVybS5mcmVxID49IDE1MCkNCmRmIDwtIGRhdGEuZnJhbWUodGVybSA9IG5hbWVzKHRlcm0uZnJlcSksIGZyZXEgPSB0ZXJtLmZyZXEpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh4PXRlcm0sIHk9ZnJlcSkpICsgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArDQogIHhsYWIoIlRlcm1zIikgKyB5bGFiKCJDb3VudCIpICsgY29vcmRfZmxpcCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcpKQ0KYGBgDQoNCiMjIFdvcmRjbG91ZA0KDQojIyMgQnVpbGQgV29yZGNsb3VkDQpgYGB7cn0NCmxpYnJhcnkod29yZGNsb3VkKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpgYGANCg0KYGBge3J9DQptIDwtIGFzLm1hdHJpeCh0ZG0pDQojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIHdvcmRzIGFuZCBzb3J0IGl0IGJ5IGZyZXF1ZW5jeSANCndvcmQuZnJlcSA8LSBzb3J0KHJvd1N1bXMobSksIGRlY3JlYXNpbmcgPSBUKQ0KIyBjb2xvcnMNCnBhbCA8LSBicmV3ZXIucGFsKDksICJCdUduIilbLSgxOjQpXQ0KYGBgDQoNCg0KDQpgYGB7cn0NCklORE9TQVQgPSB3b3JkY2xvdWQod29yZHMgPSBuYW1lcyh3b3JkLmZyZXEpLCBmcmVxID0gd29yZC5mcmVxLCBtaW4uZnJlcSA9IDEwMCwNCiAgICByYW5kb20ub3JkZXIgPSBGLCBjb2xvcnMgPSBwYWwpDQpgYGANCg0KI1RlbGtvbXNlbCBWUyBJbmRvc2F0DQpgYGB7cn0NCnByaW50KCJURUxLT01TRUwiKQ0KcHJpbnQoIklORE9TQVQiKQ0KYGBgDQoNCg==