People love to talk about AI, to hear about what’s new, what’s developing and how it might change our lives. In an earlier post I did some data scraping to download the text from all the TED Talks under the search term “AI”. In this post we will anaylse the sentiments contained in TED Talks about AI (positive or negative), and how these might have developed across time. We begin as always by loading some packages and functions.


# Some packages
library(tidyverse)
library(data.table)
library(tidytext)
library(qdap)
library(tm)
library(stringr)
library(ggrepel)
library(plotly)
library(colorspace)


# Some colours

myCols8<- c("#003f5c", "#2f4b7c", "#665191", "#a05195", "#d45087", "#f95d6a", "#ff7c43", "#ffa600")
cols10<-  c("#7a5195", "#bc5090", "#ef5675", "#ff764a", "#ffa600","#7a5195", "#bc5090", 
            "#ef5675", "#ff764a", "#ffa600")

# Some functions

quote_plot<- function(plot_data, titleText, sizes=c(4, 4, 4, 5, 5, 6, 6, 7, 8, 10), 
                      spacing= c("squared","linear")){
  # Generic plotting function to display text on a dark grey background
  plot_data<- plot_data %>% mutate(polarity= abs(polarity)) %>%
    arrange(polarity) %>%
    mutate(Order= order(polarity), X= 0 ,
           Size= sizes) 
    if (spacing== "squared") {
      plot_data$Order<- plot_data$Order^2
    }
  ggplot(plot_data, aes(x= X, y= Order, color= factor(Order))) +
    geom_text_repel(aes(label= text, size= I(Size)), fontface= "bold",                                 segment.alpha=0) +
    scale_color_manual(values= rep(cols10,2)) +
    theme_void() +
    theme(panel.background = element_rect(fill= "grey20", color= "grey20"),
          plot.background = element_rect(fill= "grey20", size= 2),
          plot.margin = margin(0.5, 0.8,0.8, 0.8, "cm"),
          legend.position = "none",
          title= element_text(face= "bold", size= 12, color= "white")) +
    labs(title= titleText)
}

Lets grab the data…

Then we load in the data that was obtained in a previous session. In this data set we have the text from 88 TED Talks, between 2007 and mid 2020. I notice that some talks are annotated with audience reactions. I don’t want these to form part of the analysis so I will remove them.

df<- readRDS("TED_data.rds")

# Extract the year
df<- df %>% mutate(Year= year(date))

# Remove the audience descriptors
df$full_text<- str_remove_all(df$full_text, "\\(laughter\\) ")
df$full_text<- str_remove_all(df$full_text, "\\(applause\\)")

# Print the first part of the first talk
print(str_sub(df$full_text[1],1,400))
[1] "When I was a kid, I was the quintessential nerd. I think some of you were, too. (Laughter) And you, sir, who laughed the loudest, you probably still are. (Laughter) I grew up in a small town in the dusty plains of north Texas, the son of a sheriff who was the son of a pastor. Getting into trouble was not an option. And so I started reading calculus books for fun. (Laughter) You did, too. That led "

Sentences, words and word stemming

Here I will pre-process the text data for analysis. How this is done depends quite a lot on each case. In my case I filter out words less than 3 characters long, remove stopwords, and words containing digits. There are a few different options for stemming - reducing words down to a common stem - and after evaluating a few options I landed on stemDocument from the tm package. Even after applying a stemming function, I found a few cases where a little manual stemming was needed.
In order to have complete words in your analysis (rather than stems) I created a dictionary of the complete word most commonly represented by each stem, and joined those back into the main data set.

# Break the test into sentences
sentObj<- unnest_sentences(df, sent, full_text)

# Break the sentences into words
wordObj<- sentObj %>% unnest_tokens(word, sent, drop= FALSE)

outwords<- c("yeah","no","black","ll","ve","ynh","don", "isn","won","didn","ca","em",                        "nb","ems","t","applause", "laughter")

wordObj<- wordObj %>% mutate(LEN= nchar(word)) %>% filter(LEN > 2) %>%
                      filter(!word %in% stop_words$word) %>%
                      filter(!word %in% outwords) %>%
                      filter(!word %like% "\\d")

wordObj$stem<- stemDocument(wordObj$word)



# Some additional manual stemming
wordObj<- wordObj %>% mutate(stem= ifelse(word %like% "chinese|china","china",stem),
                             stem= ifelse(word %like% "deepfak", "deepfake", stem),
                             stem= ifelse(word %like% "superintel","superintelligent", stem),
                             stem= ifelse(word %like% "accid","accid", stem))

# make a dictionary 
t1<-  wordObj %>% count(word,stem) %>% ungroup() %>% 
                  group_by(stem) %>% 
                  top_n(1, wt=n) %>%
                  rename(complete= word) %>% select(-n)

# Stem completion
wordObj<- left_join(wordObj,t1)
Joining, by = "stem"
wordObj$word<- wordObj$complete

Are you positive? Matching words to sentiments

Now we bring in some sentiment lexicons, these ones are from the tidytext package. They contain lists of words and whether they are conisdered positive or negative. In the case of afinn, this is a numerical score between -5 (very negative) and +5 (very positive) words. Because my data set is quite small and the vocab is quite specialised, I will refer to several lexicons to gather sentiments.

# Get some sentiment lexicons
bing_lex<-   get_sentiments("bing")
afinn_lex<-  get_sentiments("afinn")


bing_lex<-       bing_lex %>% mutate(bing= ifelse(sentiment== "negative", -1, 1)) %>% 
                              select(-sentiment)

afinn_lex<-     afinn_lex %>% rename(afinn= value)

wordObj<- wordObj %>% left_join(bing_lex) %>%
                      left_join(afinn_lex)
Joining, by = c("word", "bing")
Joining, by = c("word", "afinn")
# Positive and negative words from qdapDictionaries
wordObj<- wordObj %>% mutate(qdap= ifelse(word %in% positive.words, 1, 0),
                             qdap= ifelse(word %in% negative.words, -1, qdap))



# Make a combined polarity score

wordObj<- wordObj %>% mutate(bing= fixNAs(bing), afinn= fixNAs(afinn),
                             combined= bing + afinn + qdap)

I apply the polarity function from qdap to score sentences as positive or negative in sentiment. This potentially gives additional insight since polarity will take into account negating or amplifying words contained in the sentence.

# Polarity by sentence

polarity<-         polarity(sentObj$sent)
sentObj$polarity<- polarity$all$polarity

Its all in the clouds

Lets explore some of the positive and negative words found in the TED Talks. The size of the word reflects is number of mentions. Note that in the positive plot we have taken out some of the very most common words to allow for visualisation of more.

# Wordclouds

t0<- wordObj %>% ungroup() %>% filter(combined > 0) %>% count(word) %>% filter(! word %in% c("technologies","intelligence","like","work","right","kind","well","want","ability"))
multiplier<- ceiling(nrow(t0)/8)
wordcloud2::wordcloud2(t0, color= rep(myCols8, multiplier))

NA

t0<- wordObj %>% filter(combined < 0) %>% count(word)  
multiplier<- ceiling(nrow(t0)/8)
wordcloud2::wordcloud2(t0, color= rep(myCols8, multiplier))

Who are you calling “negative”?

Now we make use of the sentence polarity to identify some of the most highly-charged negative and positive sentences.

# Positive and negative quotes
t0<- wordObj %>% group_by(Year,title,sent) %>%
     summarise(polarity= sum(combined,na.rm=T))

negative<- t0 %>% ungroup() %>% top_n(10, wt= -polarity) %>% mutate(text= str_wrap(sent, 60))
negative<- negative[1:10,]
quote_plot(negative,"Most Negative Quotes", sizes= rep(2.6,nrow(negative)), spacing= "linear")


positive<- t0 %>% ungroup() %>% top_n(10, wt= polarity) %>% mutate(text= str_wrap(sent, 60))
quote_plot(positive, "Most Positive Quotes", sizes= rep(2.6,nrow(positive)) , spacing= "linear")

Is the AI honeymoon over?

Finally, we map the overall talk sentiment (positive or negative) and how this has changed across time.

# Talk Polarity

talk_pol<- wordObj %>% group_by(Year, title) %>% 
              summarise(polarity= sum(combined, na.rm = T))
talk_pol$length<- str_count(df$full_text," ")

g<- ggplot(talk_pol, aes(x= Year, y= polarity, color= -polarity, label= title)) +
  geom_jitter(aes(size= length), alpha= 0.5) +
  geom_smooth(size= 0.5,  alpha= 0.3, se= FALSE, color= "grey70") +
  scale_color_continuous_sequential(palette = "Plasma") +
  geom_hline(yintercept = 0, color= "grey50", alpha= 0.5) +
  theme_bw() +
  theme(legend.position = "none") +
  labs(title= "TED Talk Overall Sentiment by Year", y="Polarity",
       subtitle = "Point size indicates length of the talk")

ggplotly(g) %>%
  style(text = paste0(talk_pol$Year,"<br>",
                      talk_pol$title,"<br>Polartiy: ",
                      talk_pol$polarity), traces = 1) 

We can see that the first properly negative talk didn’t appear until 2013, before which all talks on the topic of AI had been either neutral or positive. Since 2016 however, there has been a growing awareness of the more negative potential implications of AI, while other talks continue to emphasise the positive.

. .

..

LS0tCnRpdGxlOiAiVEVEIFRhbGtzOiBBSSBhbmQgaXRzIFNlbnRpbWVudHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IENlbCBNY0NyYWNrZW4KZGF0ZTogIjIwMjAtMDctMTAiCi0tLQohW10oL1VzZXJzL0NlbGVzdGUvRGVza3RvcC9BSV9EeXN0b3BpYS5wbmcpCgpQZW9wbGUgbG92ZSB0byB0YWxrIGFib3V0IEFJLCB0byBoZWFyIGFib3V0IHdoYXQncyBuZXcsIHdoYXQncyBkZXZlbG9waW5nIGFuZCBob3cgaXQgbWlnaHQgY2hhbmdlIG91ciBsaXZlcy4gSW4gYW4gZWFybGllciBwb3N0IEkgZGlkIHNvbWUgZGF0YSBzY3JhcGluZyB0byBkb3dubG9hZCB0aGUgdGV4dCBmcm9tIGFsbCB0aGUgVEVEIFRhbGtzIHVuZGVyIHRoZSBzZWFyY2ggdGVybSAiQUkiLiBJbiB0aGlzIHBvc3Qgd2Ugd2lsbCBhbmF5bHNlIHRoZSBzZW50aW1lbnRzIGNvbnRhaW5lZCBpbiBURUQgVGFsa3MgYWJvdXQgQUkgKHBvc2l0aXZlIG9yIG5lZ2F0aXZlKSwgYW5kIGhvdyB0aGVzZSBtaWdodCBoYXZlIGRldmVsb3BlZCBhY3Jvc3MgdGltZS4gIFdlIGJlZ2luIGFzIGFsd2F5cyBieSBsb2FkaW5nIHNvbWUgcGFja2FnZXMgYW5kIGZ1bmN0aW9ucy4KYGBge3IsIG1lc3NhZ2U9IEZBTFNFfQoKIyBTb21lIHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkocWRhcCkKbGlicmFyeSh0bSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGNvbG9yc3BhY2UpCgoKIyBTb21lIGNvbG91cnMKCm15Q29sczg8LSBjKCIjMDAzZjVjIiwgIiMyZjRiN2MiLCAiIzY2NTE5MSIsICIjYTA1MTk1IiwgIiNkNDUwODciLCAiI2Y5NWQ2YSIsICIjZmY3YzQzIiwgIiNmZmE2MDAiKQpjb2xzMTA8LSAgYygiIzdhNTE5NSIsICIjYmM1MDkwIiwgIiNlZjU2NzUiLCAiI2ZmNzY0YSIsICIjZmZhNjAwIiwiIzdhNTE5NSIsICIjYmM1MDkwIiwgCiAgICAgICAgICAgICIjZWY1Njc1IiwgIiNmZjc2NGEiLCAiI2ZmYTYwMCIpCgojIFNvbWUgZnVuY3Rpb25zCgpxdW90ZV9wbG90PC0gZnVuY3Rpb24ocGxvdF9kYXRhLCB0aXRsZVRleHQsIHNpemVzPWMoNCwgNCwgNCwgNSwgNSwgNiwgNiwgNywgOCwgMTApLCAKICAgICAgICAgICAgICAgICAgICAgIHNwYWNpbmc9IGMoInNxdWFyZWQiLCJsaW5lYXIiKSl7CiAgIyBHZW5lcmljIHBsb3R0aW5nIGZ1bmN0aW9uIHRvIGRpc3BsYXkgdGV4dCBvbiBhIGRhcmsgZ3JleSBiYWNrZ3JvdW5kCiAgcGxvdF9kYXRhPC0gcGxvdF9kYXRhICU+JSBtdXRhdGUocG9sYXJpdHk9IGFicyhwb2xhcml0eSkpICU+JQogICAgYXJyYW5nZShwb2xhcml0eSkgJT4lCiAgICBtdXRhdGUoT3JkZXI9IG9yZGVyKHBvbGFyaXR5KSwgWD0gMCAsCiAgICAgICAgICAgU2l6ZT0gc2l6ZXMpIAogICAgaWYgKHNwYWNpbmc9PSAic3F1YXJlZCIpIHsKICAgICAgcGxvdF9kYXRhJE9yZGVyPC0gcGxvdF9kYXRhJE9yZGVyXjIKICAgIH0KICBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeD0gWCwgeT0gT3JkZXIsIGNvbG9yPSBmYWN0b3IoT3JkZXIpKSkgKwogICAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD0gdGV4dCwgc2l6ZT0gSShTaXplKSksIGZvbnRmYWNlPSAiYm9sZCIsICBzZWdtZW50LmFscGhhPTApICsKICAgICNnZW9tX3RleHQoYWVzKGxhYmVsPSB0ZXh0LCBzaXplPUkoU2l6ZSkpLCBmb250ZmFjZT0iYm9sZCIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IHJlcChjb2xzMTAsMikpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9ICJncmV5MjAiLCBjb2xvcj0gImdyZXkyMCIpLAogICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9ICJncmV5MjAiLCBzaXplPSAyKSwKICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAuNSwgMC44LDAuOCwgMC44LCAiY20iKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgIHRpdGxlPSBlbGVtZW50X3RleHQoZmFjZT0gImJvbGQiLCBzaXplPSAxMiwgY29sb3I9ICJ3aGl0ZSIpKSArCiAgICBsYWJzKHRpdGxlPSB0aXRsZVRleHQpCn0KCmZpeE5BczwtIGZ1bmN0aW9uKHRoZXZlYyl7CiAgdGhldmVjPC0gaWZlbHNlKGlzLm5hKHRoZXZlYyksMCx0aGV2ZWMpCiAgcmV0dXJuKHRoZXZlYykKfQoKCmBgYAoKIyMjIExldHMgZ3JhYiB0aGUgZGF0YS4uLgoKVGhlbiB3ZSBsb2FkIGluIHRoZSBkYXRhIHRoYXQgd2FzIG9idGFpbmVkIGluIGEgcHJldmlvdXMgc2Vzc2lvbi4gIEluIHRoaXMgZGF0YSBzZXQgd2UgaGF2ZSB0aGUgdGV4dCBmcm9tIDg4IFRFRCBUYWxrcywgYmV0d2VlbiAyMDA3IGFuZCBtaWQgMjAyMC4gSSBub3RpY2UgdGhhdCBzb21lIHRhbGtzIGFyZSBhbm5vdGF0ZWQgd2l0aCBhdWRpZW5jZSByZWFjdGlvbnMuIEkgZG9uJ3Qgd2FudCB0aGVzZSB0byBmb3JtIHBhcnQgb2YgdGhlIGFuYWx5c2lzIHNvIEkgd2lsbCByZW1vdmUgdGhlbS4KYGBge3J9CmRmPC0gcmVhZFJEUygiVEVEX2RhdGEucmRzIikKCiMgRXh0cmFjdCB0aGUgeWVhcgpkZjwtIGRmICU+JSBtdXRhdGUoWWVhcj0geWVhcihkYXRlKSkKCiMgUmVtb3ZlIHRoZSBhdWRpZW5jZSBkZXNjcmlwdG9ycwpkZiRmdWxsX3RleHQ8LSBzdHJfcmVtb3ZlX2FsbChkZiRmdWxsX3RleHQsICJcXChsYXVnaHRlclxcKSAiKQpkZiRmdWxsX3RleHQ8LSBzdHJfcmVtb3ZlX2FsbChkZiRmdWxsX3RleHQsICJcXChhcHBsYXVzZVxcKSIpCgojIFByaW50IHRoZSBmaXJzdCBwYXJ0IG9mIHRoZSBmaXJzdCB0YWxrCnByaW50KHN0cl9zdWIoZGYkZnVsbF90ZXh0WzFdLDEsNDAwKSkKCmBgYAoKIyMjIFNlbnRlbmNlcywgd29yZHMgYW5kIHdvcmQgc3RlbW1pbmcKSGVyZSBJIHdpbGwgcHJlLXByb2Nlc3MgdGhlIHRleHQgZGF0YSBmb3IgYW5hbHlzaXMuIEhvdyB0aGlzIGlzIGRvbmUgZGVwZW5kcyBxdWl0ZSBhIGxvdCBvbiBlYWNoIGNhc2UuICBJbiBteSBjYXNlIEkgZmlsdGVyIG91dCB3b3JkcyBsZXNzIHRoYW4gMyBjaGFyYWN0ZXJzIGxvbmcsIHJlbW92ZSBzdG9wd29yZHMsIGFuZCB3b3JkcyBjb250YWluaW5nIGRpZ2l0cy4KVGhlcmUgYXJlIGEgZmV3IGRpZmZlcmVudCBvcHRpb25zIGZvciBzdGVtbWluZyAtIHJlZHVjaW5nIHdvcmRzIGRvd24gdG8gYSBjb21tb24gc3RlbSAtIGFuZCBhZnRlciBldmFsdWF0aW5nIGEgZmV3IG9wdGlvbnMgSSBsYW5kZWQgb24gYHN0ZW1Eb2N1bWVudGAgZnJvbSB0aGUgYHRtYCBwYWNrYWdlLiAgRXZlbiBhZnRlciBhcHBseWluZyBhIHN0ZW1taW5nIGZ1bmN0aW9uLCBJIGZvdW5kIGEgZmV3IGNhc2VzIHdoZXJlIGEgbGl0dGxlIG1hbnVhbCBzdGVtbWluZyB3YXMgbmVlZGVkLiAgCkluIG9yZGVyIHRvIGhhdmUgY29tcGxldGUgd29yZHMgaW4geW91ciBhbmFseXNpcyAocmF0aGVyIHRoYW4gc3RlbXMpIEkgY3JlYXRlZCBhIGRpY3Rpb25hcnkgb2YgdGhlIGNvbXBsZXRlIHdvcmQgbW9zdCBjb21tb25seSByZXByZXNlbnRlZCBieSBlYWNoIHN0ZW0sIGFuZCBqb2luZWQgdGhvc2UgYmFjayBpbnRvIHRoZSBtYWluIGRhdGEgc2V0LgpgYGB7ciAsIG1lc3NhZ2U9RkFMU0V9CiMgQnJlYWsgdGhlIHRlc3QgaW50byBzZW50ZW5jZXMKc2VudE9iajwtIHVubmVzdF9zZW50ZW5jZXMoZGYsIHNlbnQsIGZ1bGxfdGV4dCkKCiMgQnJlYWsgdGhlIHNlbnRlbmNlcyBpbnRvIHdvcmRzCndvcmRPYmo8LSBzZW50T2JqICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIHNlbnQsIGRyb3A9IEZBTFNFKQoKb3V0d29yZHM8LSBjKCJ5ZWFoIiwibm8iLCJibGFjayIsImxsIiwidmUiLCJ5bmgiLCJkb24iLCAiaXNuIiwid29uIiwiZGlkbiIsImNhIiwiZW0iLCAibmIiLCJlbXMiLCJ0IiwiYXBwbGF1c2UiLCAibGF1Z2h0ZXIiKQoKd29yZE9iajwtIHdvcmRPYmogJT4lIG11dGF0ZShMRU49IG5jaGFyKHdvcmQpKSAlPiUgZmlsdGVyKExFTiA+IDIpICU+JQogICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCF3b3JkICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighd29yZCAlaW4lIG91dHdvcmRzKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighd29yZCAlbGlrZSUgIlxcZCIpCgp3b3JkT2JqJHN0ZW08LSBzdGVtRG9jdW1lbnQod29yZE9iaiR3b3JkKQoKCgojIFNvbWUgYWRkaXRpb25hbCBtYW51YWwgc3RlbW1pbmcKd29yZE9iajwtIHdvcmRPYmogJT4lIG11dGF0ZShzdGVtPSBpZmVsc2Uod29yZCAlbGlrZSUgImNoaW5lc2V8Y2hpbmEiLCJjaGluYSIsc3RlbSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlbT0gaWZlbHNlKHdvcmQgJWxpa2UlICJkZWVwZmFrIiwgImRlZXBmYWtlIiwgc3RlbSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlbT0gaWZlbHNlKHdvcmQgJWxpa2UlICJzdXBlcmludGVsIiwic3VwZXJpbnRlbGxpZ2VudCIsIHN0ZW0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZW09IGlmZWxzZSh3b3JkICVsaWtlJSAiYWNjaWQiLCJhY2NpZCIsIHN0ZW0pKQoKIyBtYWtlIGEgZGljdGlvbmFyeSAKdDE8LSAgd29yZE9iaiAlPiUgY291bnQod29yZCxzdGVtKSAlPiUgdW5ncm91cCgpICU+JSAKICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoc3RlbSkgJT4lIAogICAgICAgICAgICAgICAgICB0b3BfbigxLCB3dD1uKSAlPiUKICAgICAgICAgICAgICAgICAgcmVuYW1lKGNvbXBsZXRlPSB3b3JkKSAlPiUgc2VsZWN0KC1uKQoKIyBTdGVtIGNvbXBsZXRpb24Kd29yZE9iajwtIGxlZnRfam9pbih3b3JkT2JqLHQxKQp3b3JkT2JqJHdvcmQ8LSB3b3JkT2JqJGNvbXBsZXRlCgpgYGAKCiMjIyBBcmUgeW91IHBvc2l0aXZlPyBNYXRjaGluZyB3b3JkcyB0byBzZW50aW1lbnRzCk5vdyB3ZSBicmluZyBpbiBzb21lIHNlbnRpbWVudCBsZXhpY29ucywgdGhlc2Ugb25lcyBhcmUgZnJvbSB0aGUgYHRpZHl0ZXh0YCBwYWNrYWdlLiBUaGV5IGNvbnRhaW4gbGlzdHMgb2Ygd29yZHMgYW5kIHdoZXRoZXIgdGhleSBhcmUgY29uaXNkZXJlZCBwb3NpdGl2ZSBvciBuZWdhdGl2ZS4gSW4gdGhlIGNhc2Ugb2YgYWZpbm4sIHRoaXMgaXMgYSBudW1lcmljYWwgc2NvcmUgYmV0d2VlbiAtNSAodmVyeSBuZWdhdGl2ZSkgYW5kICs1ICh2ZXJ5IHBvc2l0aXZlKSB3b3Jkcy4gQmVjYXVzZSBteSBkYXRhIHNldCBpcyBxdWl0ZSBzbWFsbCBhbmQgdGhlIHZvY2FiIGlzIHF1aXRlIHNwZWNpYWxpc2VkLCBJIHdpbGwgcmVmZXIgdG8gc2V2ZXJhbCBsZXhpY29ucyB0byBnYXRoZXIgc2VudGltZW50cy4KYGBge3IsIG1lc3NhZ2U9IEZBTFNFfQojIEdldCBzb21lIHNlbnRpbWVudCBsZXhpY29ucwpiaW5nX2xleDwtICAgZ2V0X3NlbnRpbWVudHMoImJpbmciKQphZmlubl9sZXg8LSAgZ2V0X3NlbnRpbWVudHMoImFmaW5uIikKCgpiaW5nX2xleDwtICAgICAgIGJpbmdfbGV4ICU+JSBtdXRhdGUoYmluZz0gaWZlbHNlKHNlbnRpbWVudD09ICJuZWdhdGl2ZSIsIC0xLCAxKSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLXNlbnRpbWVudCkKCmFmaW5uX2xleDwtICAgICBhZmlubl9sZXggJT4lIHJlbmFtZShhZmlubj0gdmFsdWUpCgp3b3JkT2JqPC0gd29yZE9iaiAlPiUgbGVmdF9qb2luKGJpbmdfbGV4KSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGxlZnRfam9pbihhZmlubl9sZXgpCgojIFBvc2l0aXZlIGFuZCBuZWdhdGl2ZSB3b3JkcyBmcm9tIHFkYXBEaWN0aW9uYXJpZXMKd29yZE9iajwtIHdvcmRPYmogJT4lIG11dGF0ZShxZGFwPSBpZmVsc2Uod29yZCAlaW4lIHBvc2l0aXZlLndvcmRzLCAxLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxZGFwPSBpZmVsc2Uod29yZCAlaW4lIG5lZ2F0aXZlLndvcmRzLCAtMSwgcWRhcCkpCgojIE1ha2UgYSBjb21iaW5lZCBwb2xhcml0eSBzY29yZQp3b3JkT2JqPC0gd29yZE9iaiAlPiUgbXV0YXRlKGJpbmc9IGZpeE5BcyhiaW5nKSwgYWZpbm49IGZpeE5BcyhhZmlubiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tYmluZWQ9IGJpbmcgKyBhZmlubiArIHFkYXApCgpgYGAKSSBhcHBseSB0aGUgYHBvbGFyaXR5YCBmdW5jdGlvbiBmcm9tIGBxZGFwYCB0byBzY29yZSBzZW50ZW5jZXMgYXMgcG9zaXRpdmUgb3IgbmVnYXRpdmUgaW4gc2VudGltZW50LiBUaGlzIHBvdGVudGlhbGx5IGdpdmVzIGFkZGl0aW9uYWwgaW5zaWdodCBzaW5jZSBgcG9sYXJpdHlgIHdpbGwgdGFrZSBpbnRvIGFjY291bnQgbmVnYXRpbmcgb3IgYW1wbGlmeWluZyB3b3JkcyBjb250YWluZWQgaW4gdGhlIHNlbnRlbmNlLgpgYGB7ciwgbWVzc2FnZT0gRkFMU0V9CiMgUG9sYXJpdHkgYnkgc2VudGVuY2UKCnBvbGFyaXR5PC0gICAgICAgICBwb2xhcml0eShzZW50T2JqJHNlbnQpCnNlbnRPYmokcG9sYXJpdHk8LSBwb2xhcml0eSRhbGwkcG9sYXJpdHkKCmBgYAoKCiMjIyBJdHMgYWxsIGluIHRoZSBjbG91ZHMKTGV0cyBleHBsb3JlIHNvbWUgb2YgdGhlIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSB3b3JkcyBmb3VuZCBpbiB0aGUgVEVEIFRhbGtzLiBUaGUgc2l6ZSBvZiB0aGUgd29yZCByZWZsZWN0cyBpcyBudW1iZXIgb2YgbWVudGlvbnMuIE5vdGUgdGhhdCBpbiB0aGUgcG9zaXRpdmUgcGxvdCB3ZSBoYXZlIHRha2VuIG91dCBzb21lIG9mIHRoZSB2ZXJ5IG1vc3QgY29tbW9uIHdvcmRzIHRvIGFsbG93IGZvciB2aXN1YWxpc2F0aW9uIG9mIG1vcmUuCmBgYHtyLCBtZXNzYWdlPSBGQUxTRX0KIyBXb3JkY2xvdWRzCgp0MDwtIHdvcmRPYmogJT4lIHVuZ3JvdXAoKSAlPiUgZmlsdGVyKGNvbWJpbmVkID4gMCkgJT4lIGNvdW50KHdvcmQpICU+JSBmaWx0ZXIoISB3b3JkICVpbiUgYygidGVjaG5vbG9naWVzIiwiaW50ZWxsaWdlbmNlIiwibGlrZSIsIndvcmsiLCJyaWdodCIsImtpbmQiLCJ3ZWxsIiwid2FudCIsImFiaWxpdHkiKSkKbXVsdGlwbGllcjwtIGNlaWxpbmcobnJvdyh0MCkvOCkKd29yZGNsb3VkMjo6d29yZGNsb3VkMih0MCwgY29sb3I9IHJlcChteUNvbHM4LCBtdWx0aXBsaWVyKSkKYGBgCmBgYHtyLCBtZXNzYWdlPSBGQUxTRX0KdDA8LSB3b3JkT2JqICU+JSBmaWx0ZXIoY29tYmluZWQgPCAwKSAlPiUgY291bnQod29yZCkgIAptdWx0aXBsaWVyPC0gY2VpbGluZyhucm93KHQwKS84KQp3b3JkY2xvdWQyOjp3b3JkY2xvdWQyKHQwLCBjb2xvcj0gcmVwKG15Q29sczgsIG11bHRpcGxpZXIpKQpgYGAKCgojIyMgV2hvIGFyZSB5b3UgY2FsbGluZyAibmVnYXRpdmUiPwpOb3cgd2UgbWFrZSB1c2Ugb2YgdGhlIHNlbnRlbmNlIHBvbGFyaXR5IHRvIGlkZW50aWZ5IHNvbWUgb2YgdGhlIG1vc3QgaGlnaGx5LWNoYXJnZWQgbmVnYXRpdmUgYW5kIHBvc2l0aXZlIHNlbnRlbmNlcy4KYGBge3IsIGZpZy5hbGlnbj0iY2VudGVyIiwgbWVzc2FnZT1GQUxTRX0KIyBQb3NpdGl2ZSBhbmQgbmVnYXRpdmUgcXVvdGVzCnQwPC0gd29yZE9iaiAlPiUgZ3JvdXBfYnkoWWVhcix0aXRsZSxzZW50KSAlPiUKICAgICBzdW1tYXJpc2UocG9sYXJpdHk9IHN1bShjb21iaW5lZCxuYS5ybT1UKSkKCm5lZ2F0aXZlPC0gdDAgJT4lIHVuZ3JvdXAoKSAlPiUgdG9wX24oMTAsIHd0PSAtcG9sYXJpdHkpICU+JSBtdXRhdGUodGV4dD0gc3RyX3dyYXAoc2VudCwgNjApKQpuZWdhdGl2ZTwtIG5lZ2F0aXZlWzE6MTAsXQpxdW90ZV9wbG90KG5lZ2F0aXZlLCJNb3N0IE5lZ2F0aXZlIFF1b3RlcyIsIHNpemVzPSByZXAoMi42LG5yb3cobmVnYXRpdmUpKSwgc3BhY2luZz0gImxpbmVhciIpCgpgYGAKYGBge3J9CnBvc2l0aXZlPC0gdDAgJT4lIHVuZ3JvdXAoKSAlPiUgdG9wX24oMTAsIHd0PSBwb2xhcml0eSkgJT4lIG11dGF0ZSh0ZXh0PSBzdHJfd3JhcChzZW50LCA2MCkpCnF1b3RlX3Bsb3QocG9zaXRpdmUsICJNb3N0IFBvc2l0aXZlIFF1b3RlcyIsIHNpemVzPSByZXAoMi42LG5yb3cocG9zaXRpdmUpKSAsIHNwYWNpbmc9ICJsaW5lYXIiKQpgYGAKCiMjIyBJcyB0aGUgQUkgaG9uZXltb29uIG92ZXI/CkZpbmFsbHksIHdlIG1hcCB0aGUgb3ZlcmFsbCB0YWxrIHNlbnRpbWVudCAocG9zaXRpdmUgb3IgbmVnYXRpdmUpIGFuZCBob3cgdGhpcyBoYXMgY2hhbmdlZCBhY3Jvc3MgdGltZS4KYGBge3IsIG1lc3NhZ2U9IEZBTFNFfQojIFRhbGsgUG9sYXJpdHkKCnRhbGtfcG9sPC0gd29yZE9iaiAlPiUgZ3JvdXBfYnkoWWVhciwgdGl0bGUpICU+JSAKICAgICAgICAgICAgICBzdW1tYXJpc2UocG9sYXJpdHk9IHN1bShjb21iaW5lZCwgbmEucm0gPSBUKSkKdGFsa19wb2wkbGVuZ3RoPC0gc3RyX2NvdW50KGRmJGZ1bGxfdGV4dCwiICIpCgpnPC0gZ2dwbG90KHRhbGtfcG9sLCBhZXMoeD0gWWVhciwgeT0gcG9sYXJpdHksIGNvbG9yPSAtcG9sYXJpdHksIGxhYmVsPSB0aXRsZSkpICsKICBnZW9tX2ppdHRlcihhZXMoc2l6ZT0gbGVuZ3RoKSwgYWxwaGE9IDAuNSkgKwogIGdlb21fc21vb3RoKHNpemU9IDAuNSwgIGFscGhhPSAwLjMsIHNlPSBGQUxTRSwgY29sb3I9ICJncmV5NzAiKSArCiAgc2NhbGVfY29sb3JfY29udGludW91c19zZXF1ZW50aWFsKHBhbGV0dGUgPSAiUGxhc21hIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yPSAiZ3JleTUwIiwgYWxwaGE9IDAuNSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGU9ICJURUQgVGFsayBPdmVyYWxsIFNlbnRpbWVudCBieSBZZWFyIiwgeT0iUG9sYXJpdHkiLAogICAgICAgc3VidGl0bGUgPSAiUG9pbnQgc2l6ZSBpbmRpY2F0ZXMgbGVuZ3RoIG9mIHRoZSB0YWxrIikKCmdncGxvdGx5KGcpICU+JQogIHN0eWxlKHRleHQgPSBwYXN0ZTAodGFsa19wb2wkWWVhciwiPGJyPiIsCiAgICAgICAgICAgICAgICAgICAgICB0YWxrX3BvbCR0aXRsZSwiPGJyPlBvbGFydGl5OiAiLAogICAgICAgICAgICAgICAgICAgICAgdGFsa19wb2wkcG9sYXJpdHkpLCB0cmFjZXMgPSAxKSAKYGBgCldlIGNhbiBzZWUgdGhhdCB0aGUgZmlyc3QgcHJvcGVybHkgbmVnYXRpdmUgdGFsayBkaWRuJ3QgYXBwZWFyIHVudGlsIDIwMTMsIGJlZm9yZSB3aGljaCBhbGwgdGFsa3Mgb24gdGhlIHRvcGljIG9mIEFJIGhhZCBiZWVuIGVpdGhlciBuZXV0cmFsIG9yIHBvc2l0aXZlLiAgU2luY2UgMjAxNiBob3dldmVyLCB0aGVyZSBoYXMgYmVlbiBhIGdyb3dpbmcgYXdhcmVuZXNzIG9mIHRoZSBtb3JlIG5lZ2F0aXZlIHBvdGVudGlhbCBpbXBsaWNhdGlvbnMgb2YgQUksIHdoaWxlIG90aGVyIHRhbGtzIGNvbnRpbnVlIHRvIGVtcGhhc2lzZSB0aGUgcG9zaXRpdmUuCgoKCgouCi4KCi4uCg==