Analysis of YouTube Comments

Install packages

Install packages tm, SnowallC, wordcloud, RColorBrewer, syuzhet, and ggplot2.

Prepare text for analysis

# Read the text file from local machine , choose file interactively
text <- readLines(file.choose())
install.packages("wordcloud")
install.packages("RColorBrewer")
# Load the data as a corpus
TextDoc <- Corpus(VectorSource(text))
 #Replacing "/", "@" and "|" with space
toSpace <- content_transformer(function (x , pattern ) gsub(pattern, " ", x))
TextDoc <- tm_map(TextDoc, toSpace, "/")
TextDoc <- tm_map(TextDoc, toSpace, "@")
install.packages("SnowballC")
TextDoc <- tm_map(TextDoc, toSpace, "\\|")
install.packages("tm")
# Convert the text to lower case
TextDoc <- tm_map(TextDoc, content_transformer(tolower))
# Remove numbers
TextDoc <- tm_map(TextDoc, removeNumbers)
# Remove english common stopwords
TextDoc <- tm_map(TextDoc, removeWords, stopwords("english"))
# Remove your own stop word
# specify your custom stopwords as a character vector
TextDoc <- tm_map(TextDoc, removeWords, c("s", "company", "team")) 
# Remove punctuations
TextDoc <- tm_map(TextDoc, removePunctuation)
# Eliminate extra white spaces
TextDoc <- tm_map(TextDoc, stripWhitespace)
# Text stemming - which reduces words to their root form
TextDoc <- tm_map(TextDoc, stemDocument)

Build a term document matrix

# Build a term-document matrix
TextDoc_dtm <- TermDocumentMatrix(TextDoc)
dtm_m <- as.matrix(TextDoc_dtm)

Identify most frequently used words

# Sort by descending value of frequency
dtm_v <- sort(rowSums(dtm_m),decreasing=TRUE)
dtm_d <- data.frame(word = names(dtm_v),freq=dtm_v)
# Display the top 500 most frequent words
head(dtm_d, 500)
write.csv((head(dtm_d, 500)), file = "youtube_top_500.csv")
# Plot the most frequent words
barplot(dtm_d[1:50,]$freq, las = 2, names.arg = dtm_d[1:50,]$word,
        col ="lightgreen", main ="Top 50 most frequent words",
        ylab = "Word frequencies")

Word cloud of top 150 words

#generate word cloud
set.seed(1234)
wordcloud(words = dtm_d$word, freq = dtm_d$freq, min.freq = 5,
          max.words=150, random.order=FALSE, rot.per=0.40, 
          colors=brewer.pal(8, "Dark2"))

Find associations between words

# Find associations 
findAssocs(TextDoc_dtm, terms = c("putin","ukraine","ukrainian","power","war","russia","russian","prigozhin","money","pay","work","job","military", "conscript","kids","children","death"), corlimit = 0.5)
$putin
numeric(0)

$ukraine
numeric(0)

$ukrainian
numeric(0)

$power
numeric(0)

$war
numeric(0)

$russia
numeric(0)

$russian
numeric(0)

$prigozhin
kaput  kuil  urka  woke 
 0.55  0.55  0.55  0.55 

$money
numeric(0)

$pay
numeric(0)

$work
     awar    replac    thrown apocalyps     crise    growth    hinder    pandem     satan    trepid 
     0.52      0.51      0.51      0.50      0.50      0.50      0.50      0.50      0.50      0.50 
  unbridl 
     0.50 

$job
  satanist   bracelet kharitonov   yaroslav      legal      scott   undeclar 
      0.53       0.52       0.52       0.52       0.52       0.52       0.52 

$military
numeric(0)

$conscript
numeric(0)

$kids
numeric(0)

$children
   eighteen    unpunish userepwcxpt 
       0.55        0.55        0.54 

$death
numeric(0)
# Find associations for words that occur at least 92 times
findAssocs(TextDoc_dtm, terms = findFreqTerms(TextDoc_dtm, lowfreq = 92), corlimit = 0.5)
$peopl
numeric(0)

$power
numeric(0)

$countri
numeric(0)

$everyth
numeric(0)

$like
numeric(0)

$live
numeric(0)

$now
numeric(0)

$russian
numeric(0)

$time
numeric(0)

$will
numeric(0)

$fool
numeric(0)

$way
apocalyps     crise    growth    hinder    pandem    trepid   unbridl  artifici      dark    manmad 
     0.79      0.79      0.79      0.79      0.79      0.79      0.79      0.77      0.77      0.77 
  persist    thrown     await   revolut     satan      awar     impun    involv    rather       due 
     0.77      0.77      0.76      0.76      0.76      0.73      0.73      0.72      0.71      0.70 
    earth  otherwis   collect     decad    replac      sane       els     level      evil     civil 
     0.69      0.68      0.67      0.67      0.66      0.66      0.64      0.64      0.63      0.63 
     rais      real      stop      larg   problem   possibl      done   infinit   without      rule 
     0.62      0.60      0.59      0.58      0.56      0.56      0.53      0.52      0.51      0.50 

$ukrain
numeric(0)

$ukrainian
numeric(0)

$war
numeric(0)

$world
numeric(0)

$idiot
numeric(0)

$can
numeric(0)

$person
   cult  forese  notori  specul mediocr   desir 
   0.52    0.52    0.52    0.52    0.51    0.50 

$understand
numeric(0)

$well
numeric(0)

$work
     awar    replac    thrown apocalyps     crise    growth    hinder    pandem     satan    trepid 
     0.52      0.51      0.51      0.50      0.50      0.50      0.50      0.50      0.50      0.50 
  unbridl 
     0.50 

$one
numeric(0)

$year
numeric(0)

$russia
numeric(0)

$need
numeric(0)

$putin
numeric(0)

$just
numeric(0)

$think
numeric(0)

$even
apocalyps     crise      dark    growth    hinder    pandem    trepid   unbridl  artifici    manmad 
     0.73      0.73      0.73      0.73      0.73      0.73      0.73      0.73      0.72      0.72 
  persist    thrown     await   revolut     satan      awar     impun    rather    involv    replac 
     0.72      0.72      0.71      0.71      0.71      0.68      0.68      0.67      0.67      0.64 
  collect     earth     decad       els      sane       due      real  otherwis     civil      stop 
     0.63      0.62      0.61      0.61      0.61      0.60      0.59      0.59      0.58      0.56 
    level      rais   problem      larg    realli 
     0.56      0.55      0.53      0.53      0.51 

Evaluate sentiments

# regular sentiment score using get_sentiment() function and method of your choice
# please note that different methods may have different scales
syuzhet_vector <- get_sentiment(text, method="syuzhet")
# see the first row of the vector
head(syuzhet_vector)
[1]  0.00 -3.25  0.05 -1.75 -1.75 -0.55
# see summary statistics of the vector
summary(syuzhet_vector)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -7.750  -2.300  -0.975  -1.199   0.000   6.000 
# bing
bing_vector <- get_sentiment(text, method="bing")
head(bing_vector)
[1]  0 -4  0 -3 -2  0
summary(bing_vector)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-12.000  -3.000  -1.000  -1.473   0.000   7.000 
#affin
afinn_vector <- get_sentiment(text, method="afinn")
head(afinn_vector)
[1]  0 -9  2  1 -4  5
summary(afinn_vector)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-26.000  -7.000  -3.000  -3.971   0.000  14.000 
#compare the first row of each vector using sign function
rbind(
  sign(head(syuzhet_vector)),
  sign(head(bing_vector)),
  sign(head(afinn_vector))
)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    0   -1    1   -1   -1   -1
[2,]    0   -1    0   -1   -1    0
[3,]    0   -1    1    1   -1    1

Evaluation emotion

# run nrc sentiment analysis to return data frame with each row classified as one of the following
# emotions, rather than a score: 
# anger, anticipation, disgust, fear, joy, sadness, surprise, trust 
# It also counts the number of positive and negative emotions found in each row
d<-get_nrc_sentiment(text)
#See lines of the get_nrc_sentiment dataframe
write.csv(d, file = "youtube_nrc.csv")
datatable(d)
#transpose
td<-data.frame(t(d))
#The function rowSums computes column sums across rows for each level of a grouping variable.
td_new <- data.frame(rowSums(td[2:923]))
#Transformation and cleaning
names(td_new)[1] <- "count"
td_new <- cbind("sentiment" = rownames(td_new), td_new)
rownames(td_new) <- NULL
td_new2<-td_new[1:8,]
#Plot One - count of words associated with each sentiment
quickplot(sentiment, data=td_new2, weight=count, geom="bar", fill=sentiment, ylab="count")+ggtitle("YouTube Comment Sentiments")

#Plot two - count of words associated with each sentiment, expressed as a percentage
barplot(
  sort(colSums(prop.table(d[, 1:8]))), 
  horiz = TRUE, 
  cex.names = 0.7, 
  las = 1, 
  main = "Emotions in YouTube Comments", xlab="Percentage"
)

library(DT)
library(readr)
youtube_text_t <- read_csv("youtube_text_t.csv")
New names:Rows: 923 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): x
dbl (1): ...1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
datatable(youtube_text_t)
LS0tDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCiMgQW5hbHlzaXMgb2YgWW91VHViZSBDb21tZW50cyB7LnVubnVtYmVyZWR9DQoNCiMjIyBJbnN0YWxsIHBhY2thZ2VzDQoNCkluc3RhbGwgcGFja2FnZXMgdG0sIFNub3dhbGxDLCB3b3JkY2xvdWQsIFJDb2xvckJyZXdlciwgc3l1emhldCwgYW5kIGdncGxvdDIuDQoNCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFLCBwYWdlZC5wcmludD1UUlVFfQ0KIyBJbnN0YWxsDQppbnN0YWxsLnBhY2thZ2VzKCJ0bSIpICAjIGZvciB0ZXh0IG1pbmluZw0KaW5zdGFsbC5wYWNrYWdlcygiU25vd2JhbGxDIikgIyBmb3IgdGV4dCBzdGVtbWluZw0KaW5zdGFsbC5wYWNrYWdlcygid29yZGNsb3VkIikgIyB3b3JkLWNsb3VkIGdlbmVyYXRvciANCmluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpICMgY29sb3IgcGFsZXR0ZXMNCmluc3RhbGwucGFja2FnZXMoInN5dXpoZXQiKSAjIGZvciBzZW50aW1lbnQgYW5hbHlzaXMNCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAjIGZvciBwbG90dGluZyBncmFwaHMNCmBgYA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBMb2FkDQpsaWJyYXJ5KCJ0bSIpDQpsaWJyYXJ5KCJTbm93YmFsbEMiKQ0KbGlicmFyeSgid29yZGNsb3VkIikNCmxpYnJhcnkoIlJDb2xvckJyZXdlciIpDQpsaWJyYXJ5KCJzeXV6aGV0IikNCmxpYnJhcnkoImdncGxvdDIiKQ0KYGBgDQoNCiMjIyBQcmVwYXJlIHRleHQgZm9yIGFuYWx5c2lzDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PVRSVUV9DQojIFJlYWQgdGhlIHRleHQgZmlsZSBmcm9tIGxvY2FsIG1hY2hpbmUgLCBjaG9vc2UgZmlsZSBpbnRlcmFjdGl2ZWx5DQp0ZXh0IDwtIHJlYWRMaW5lcyhmaWxlLmNob29zZSgpKQ0KIyBMb2FkIHRoZSBkYXRhIGFzIGEgY29ycHVzDQpUZXh0RG9jIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UodGV4dCkpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9VFJVRX0NCiAjUmVwbGFjaW5nICIvIiwgIkAiIGFuZCAifCIgd2l0aCBzcGFjZQ0KdG9TcGFjZSA8LSBjb250ZW50X3RyYW5zZm9ybWVyKGZ1bmN0aW9uICh4ICwgcGF0dGVybiApIGdzdWIocGF0dGVybiwgIiAiLCB4KSkNClRleHREb2MgPC0gdG1fbWFwKFRleHREb2MsIHRvU3BhY2UsICIvIikNClRleHREb2MgPC0gdG1fbWFwKFRleHREb2MsIHRvU3BhY2UsICJAIikNClRleHREb2MgPC0gdG1fbWFwKFRleHREb2MsIHRvU3BhY2UsICJcXHwiKQ0KIyBDb252ZXJ0IHRoZSB0ZXh0IHRvIGxvd2VyIGNhc2UNClRleHREb2MgPC0gdG1fbWFwKFRleHREb2MsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpDQojIFJlbW92ZSBudW1iZXJzDQpUZXh0RG9jIDwtIHRtX21hcChUZXh0RG9jLCByZW1vdmVOdW1iZXJzKQ0KIyBSZW1vdmUgZW5nbGlzaCBjb21tb24gc3RvcHdvcmRzDQpUZXh0RG9jIDwtIHRtX21hcChUZXh0RG9jLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpDQojIFJlbW92ZSB5b3VyIG93biBzdG9wIHdvcmQNCiMgc3BlY2lmeSB5b3VyIGN1c3RvbSBzdG9wd29yZHMgYXMgYSBjaGFyYWN0ZXIgdmVjdG9yDQpUZXh0RG9jIDwtIHRtX21hcChUZXh0RG9jLCByZW1vdmVXb3JkcywgYygicyIsICJjb21wYW55IiwgInRlYW0iKSkgDQojIFJlbW92ZSBwdW5jdHVhdGlvbnMNClRleHREb2MgPC0gdG1fbWFwKFRleHREb2MsIHJlbW92ZVB1bmN0dWF0aW9uKQ0KIyBFbGltaW5hdGUgZXh0cmEgd2hpdGUgc3BhY2VzDQpUZXh0RG9jIDwtIHRtX21hcChUZXh0RG9jLCBzdHJpcFdoaXRlc3BhY2UpDQojIFRleHQgc3RlbW1pbmcgLSB3aGljaCByZWR1Y2VzIHdvcmRzIHRvIHRoZWlyIHJvb3QgZm9ybQ0KVGV4dERvYyA8LSB0bV9tYXAoVGV4dERvYywgc3RlbURvY3VtZW50KQ0KYGBgDQoNCiMjIyBCdWlsZCBhIHRlcm0gZG9jdW1lbnQgbWF0cml4DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PVRSVUV9DQojIEJ1aWxkIGEgdGVybS1kb2N1bWVudCBtYXRyaXgNClRleHREb2NfZHRtIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChUZXh0RG9jKQ0KZHRtX20gPC0gYXMubWF0cml4KFRleHREb2NfZHRtKQ0KYGBgDQoNCiMjIyBJZGVudGlmeSBtb3N0IGZyZXF1ZW50bHkgdXNlZCB3b3Jkcw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1UUlVFfQ0KIyBTb3J0IGJ5IGRlc2NlbmRpbmcgdmFsdWUgb2YgZnJlcXVlbmN5DQpkdG1fdiA8LSBzb3J0KHJvd1N1bXMoZHRtX20pLGRlY3JlYXNpbmc9VFJVRSkNCmR0bV9kIDwtIGRhdGEuZnJhbWUod29yZCA9IG5hbWVzKGR0bV92KSxmcmVxPWR0bV92KQ0KIyBEaXNwbGF5IHRoZSB0b3AgNTAwIG1vc3QgZnJlcXVlbnQgd29yZHMNCmhlYWQoZHRtX2QsIDUwMCkNCndyaXRlLmNzdigoaGVhZChkdG1fZCwgNTAwKSksIGZpbGUgPSAieW91dHViZV90b3BfNTAwLmNzdiIpDQpgYGANCg0KYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9DQojIFBsb3QgdGhlIG1vc3QgZnJlcXVlbnQgd29yZHMNCmJhcnBsb3QoZHRtX2RbMTo1MCxdJGZyZXEsIGxhcyA9IDIsIG5hbWVzLmFyZyA9IGR0bV9kWzE6NTAsXSR3b3JkLA0KICAgICAgICBjb2wgPSJsaWdodGdyZWVuIiwgbWFpbiA9IlRvcCA1MCBtb3N0IGZyZXF1ZW50IHdvcmRzIiwNCiAgICAgICAgeWxhYiA9ICJXb3JkIGZyZXF1ZW5jaWVzIikNCmBgYA0KDQojIyMgV29yZCBjbG91ZCBvZiB0b3AgMTUwIHdvcmRzDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PVRSVUV9DQojZ2VuZXJhdGUgd29yZCBjbG91ZA0Kc2V0LnNlZWQoMTIzNCkNCndvcmRjbG91ZCh3b3JkcyA9IGR0bV9kJHdvcmQsIGZyZXEgPSBkdG1fZCRmcmVxLCBtaW4uZnJlcSA9IDUsDQogICAgICAgICAgbWF4LndvcmRzPTE1MCwgcmFuZG9tLm9yZGVyPUZBTFNFLCByb3QucGVyPTAuNDAsIA0KICAgICAgICAgIGNvbG9ycz1icmV3ZXIucGFsKDgsICJEYXJrMiIpKQ0KYGBgDQoNCiMjIyBGaW5kIGFzc29jaWF0aW9ucyBiZXR3ZWVuIHdvcmRzDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PVRSVUV9DQojIEZpbmQgYXNzb2NpYXRpb25zIA0KZmluZEFzc29jcyhUZXh0RG9jX2R0bSwgdGVybXMgPSBjKCJwdXRpbiIsInVrcmFpbmUiLCJ1a3JhaW5pYW4iLCJwb3dlciIsIndhciIsInJ1c3NpYSIsInJ1c3NpYW4iLCJwcmlnb3poaW4iLCJtb25leSIsInBheSIsIndvcmsiLCJqb2IiLCJtaWxpdGFyeSIsICJjb25zY3JpcHQiLCJraWRzIiwiY2hpbGRyZW4iLCJkZWF0aCIpLCBjb3JsaW1pdCA9IDAuNSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1UUlVFfQ0KIyBGaW5kIGFzc29jaWF0aW9ucyBmb3Igd29yZHMgdGhhdCBvY2N1ciBhdCBsZWFzdCA5MiB0aW1lcw0KZmluZEFzc29jcyhUZXh0RG9jX2R0bSwgdGVybXMgPSBmaW5kRnJlcVRlcm1zKFRleHREb2NfZHRtLCBsb3dmcmVxID0gOTIpLCBjb3JsaW1pdCA9IDAuNSkNCmBgYA0KDQojIyMgRXZhbHVhdGUgc2VudGltZW50cw0KDQpgYGB7cn0NCiMgcmVndWxhciBzZW50aW1lbnQgc2NvcmUgdXNpbmcgZ2V0X3NlbnRpbWVudCgpIGZ1bmN0aW9uIGFuZCBtZXRob2Qgb2YgeW91ciBjaG9pY2UNCiMgcGxlYXNlIG5vdGUgdGhhdCBkaWZmZXJlbnQgbWV0aG9kcyBtYXkgaGF2ZSBkaWZmZXJlbnQgc2NhbGVzDQpzeXV6aGV0X3ZlY3RvciA8LSBnZXRfc2VudGltZW50KHRleHQsIG1ldGhvZD0ic3l1emhldCIpDQojIHNlZSB0aGUgZmlyc3Qgcm93IG9mIHRoZSB2ZWN0b3INCmhlYWQoc3l1emhldF92ZWN0b3IpDQojIHNlZSBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgdGhlIHZlY3Rvcg0Kc3VtbWFyeShzeXV6aGV0X3ZlY3RvcikNCmBgYA0KDQpgYGB7cn0NCiMgYmluZw0KYmluZ192ZWN0b3IgPC0gZ2V0X3NlbnRpbWVudCh0ZXh0LCBtZXRob2Q9ImJpbmciKQ0KaGVhZChiaW5nX3ZlY3RvcikNCnN1bW1hcnkoYmluZ192ZWN0b3IpDQojYWZmaW4NCmFmaW5uX3ZlY3RvciA8LSBnZXRfc2VudGltZW50KHRleHQsIG1ldGhvZD0iYWZpbm4iKQ0KaGVhZChhZmlubl92ZWN0b3IpDQpzdW1tYXJ5KGFmaW5uX3ZlY3RvcikNCmBgYA0KDQpgYGB7cn0NCiNjb21wYXJlIHRoZSBmaXJzdCByb3cgb2YgZWFjaCB2ZWN0b3IgdXNpbmcgc2lnbiBmdW5jdGlvbg0KcmJpbmQoDQogIHNpZ24oaGVhZChzeXV6aGV0X3ZlY3RvcikpLA0KICBzaWduKGhlYWQoYmluZ192ZWN0b3IpKSwNCiAgc2lnbihoZWFkKGFmaW5uX3ZlY3RvcikpDQopDQpgYGANCg0KIyMjIEV2YWx1YXRpb24gZW1vdGlvbg0KDQpgYGB7ciBlbW90aW9uLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1UUlVFfQ0KIyBydW4gbnJjIHNlbnRpbWVudCBhbmFseXNpcyB0byByZXR1cm4gZGF0YSBmcmFtZSB3aXRoIGVhY2ggcm93IGNsYXNzaWZpZWQgYXMgb25lIG9mIHRoZSBmb2xsb3dpbmcNCiMgZW1vdGlvbnMsIHJhdGhlciB0aGFuIGEgc2NvcmU6IA0KIyBhbmdlciwgYW50aWNpcGF0aW9uLCBkaXNndXN0LCBmZWFyLCBqb3ksIHNhZG5lc3MsIHN1cnByaXNlLCB0cnVzdCANCiMgSXQgYWxzbyBjb3VudHMgdGhlIG51bWJlciBvZiBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgZW1vdGlvbnMgZm91bmQgaW4gZWFjaCByb3cNCmQ8LWdldF9ucmNfc2VudGltZW50KHRleHQpDQojU2VlIGxpbmVzIG9mIHRoZSBnZXRfbnJjX3NlbnRpbWVudCBkYXRhZnJhbWUNCndyaXRlLmNzdihkLCBmaWxlID0gInlvdXR1YmVfbnJjLmNzdiIpDQpkYXRhdGFibGUoZCkNCmBgYA0KDQpgYGB7ciBzZW50aW1lbnRzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCiN0cmFuc3Bvc2UNCnRkPC1kYXRhLmZyYW1lKHQoZCkpDQojVGhlIGZ1bmN0aW9uIHJvd1N1bXMgY29tcHV0ZXMgY29sdW1uIHN1bXMgYWNyb3NzIHJvd3MgZm9yIGVhY2ggbGV2ZWwgb2YgYSBncm91cGluZyB2YXJpYWJsZS4NCnRkX25ldyA8LSBkYXRhLmZyYW1lKHJvd1N1bXModGRbMjo5MjNdKSkNCiNUcmFuc2Zvcm1hdGlvbiBhbmQgY2xlYW5pbmcNCm5hbWVzKHRkX25ldylbMV0gPC0gImNvdW50Ig0KdGRfbmV3IDwtIGNiaW5kKCJzZW50aW1lbnQiID0gcm93bmFtZXModGRfbmV3KSwgdGRfbmV3KQ0Kcm93bmFtZXModGRfbmV3KSA8LSBOVUxMDQp0ZF9uZXcyPC10ZF9uZXdbMTo4LF0NCiNQbG90IE9uZSAtIGNvdW50IG9mIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHNlbnRpbWVudA0KcXVpY2twbG90KHNlbnRpbWVudCwgZGF0YT10ZF9uZXcyLCB3ZWlnaHQ9Y291bnQsIGdlb209ImJhciIsIGZpbGw9c2VudGltZW50LCB5bGFiPSJjb3VudCIpK2dndGl0bGUoIllvdVR1YmUgQ29tbWVudCBTZW50aW1lbnRzIikNCg0KYGBgDQoNCmBgYHtyfQ0KI1Bsb3QgdHdvIC0gY291bnQgb2Ygd29yZHMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggc2VudGltZW50LCBleHByZXNzZWQgYXMgYSBwZXJjZW50YWdlDQpiYXJwbG90KA0KICBzb3J0KGNvbFN1bXMocHJvcC50YWJsZShkWywgMTo4XSkpKSwgDQogIGhvcml6ID0gVFJVRSwgDQogIGNleC5uYW1lcyA9IDAuNywgDQogIGxhcyA9IDEsIA0KICBtYWluID0gIkVtb3Rpb25zIGluIFlvdVR1YmUgQ29tbWVudHMiLCB4bGFiPSJQZXJjZW50YWdlIg0KKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShEVCkNCmxpYnJhcnkocmVhZHIpDQp5b3V0dWJlX3RleHRfdCA8LSByZWFkX2NzdigieW91dHViZV90ZXh0X3QuY3N2IikNCmRhdGF0YWJsZSh5b3V0dWJlX3RleHRfdCkNCmBgYA0KDQo=