Text Mining

Sentiment Analysis

Sentiment Analysis. We wish to compare the sentiment distributions across Beatles albums from the start to the end of their core UK studio albums.

Here I have used **“Beatles Song Album Year.Rdata” file from Assignment 2. It contains lyrics, albums,track names, and year of release for the Beatles UK studio albums.

The dataset used is the one imported from the csv file found in the assignment folder. So we have 174 song and 4 variables which are : songlyrics, track_title, album and year.

TidyTextMining

Set working directory your file where beatles Song Album Year.Rdata is. Then click on the data and upload it into your working space. You will see the name of the data appear as: Lyric.album.year.keep

load("D:/ALDA 2021/CRYSTAL System-Data Science project/Crystal-DS Project 3/TEC-DS -Week materials/Week 5/Beatles Song Album Year.Rdata")
Beatles_lyrics<-Lyric.album.year.keep
#View(Beatles_lyrics)
head(Beatles_lyrics,2)
dim(Beatles_lyrics)
[1] 175   4

The dataset used (from the Assignment folder) contains the UK studio recordings, in total 12 Albums. Source. From the source used, the first UK studio recording is album “Please Please Me” in 1963, and the last UK studio recording was the album “Let It Be” in 1970.
(Note: I am not familiar with the Beatles songs so I rely on this source for the classification of the UK albums as above.) The codes below will create tibbles for each album and respectively display the songlyrics and title of the song. 14 songs in the album “Please Please Me” and 12 songs in the album “Let It Be”.

library(tidyverse)

# The track list of "Please Please me"
Beatles_UK_1<-Beatles_lyrics %>%  
  filter(album %in% "Please Please Me")
View(Beatles_UK_1)
Beatles_1_lyric<-Beatles_UK_1 %>% select(songlyric)# save all lyrics of songs in album "Please Please Me"
# Beatles_1_lyric # print the lyrics of songs from album "Please Please Me"
Beatles_1_title<-Beatles_UK_1 %>% select(track_title)# save all title of songs in album "Please Please Me"
# Beatles_1_title # print the title song from album "Please Please Me"

# The track list of "Let It Be"
Beatles_UK_2<-Beatles_lyrics %>%  
  filter(album %in% "Let It Be")
View(Beatles_UK_2)
Beatles_2_lyric<-Beatles_UK_2 %>% select(songlyric)# save all lyrics of songs in album "Please Please Me"
# Beatles_2_lyric # print the lyrics of songs from album "Please Please Me"
Beatles_2_title<-Beatles_UK_2 %>% select(track_title)# save all title of songs in album "Please Please Me"
# Beatles_2_title # print the title song from album "Please Please Me"


# to have both albums in one dataset
Beatles_UK_1_2<-Beatles_lyrics %>% 
  filter(album %in% c("Please Please Me","Let It Be")) %>% # to have both albums in one dataset
  group_by(album)%>%
  select(track_title) %>% # group by album
  summarise(nr_of_song=length(track_title)) # count the number of song per album
Adding missing grouping variables: `album`
# Beatles_UK_1_2 # print the number of song per album

# print ALL albums and number of songs per album
Beatles_lyrics %>% group_by(album) %>% select(track_title)%>% unique()%>% count()
Adding missing grouping variables: `album`

Sentiment Analysis using NRC, bing and SFINN lexicons

Libraries used from this part and on are:

Here is one of the references used apart the lecture notes: Reference in R.

# print all albums and number of songs per album
Beatles_lyrics %>% group_by(album) %>% select(track_title)%>% unique()%>% count()
Adding missing grouping variables: `album`
# number of song per album
Beatles_lyrics %>% group_by(album) %>% select(track_title)%>% unique()%>%
count() %>% ungroup() %>% View()
Adding missing grouping variables: `album`
Lyrics1 = Beatles_lyrics %>% filter(album== "Please Please Me")
head(Lyrics1,1)

Lyrics2 = Beatles_lyrics %>% filter(album== "Let It Be")
head(Lyrics2,1)

Lyrics = rbind(Lyrics1,Lyrics2) # create a tibble with two albums 
head(Lyrics,1)

Let’s find unique words in our two albums and add word column which will be latter analysed:

Lyrics_word<-Lyrics %>%
     unnest_tokens(output = word, input = songlyric) %>%
     anti_join(get_stopwords()) %>%
     group_by(track_title)
Joining, by = "word"

NRC lexicons

This code here will add the column word for each song and the sentiment column to classify it.

# nrc lexicon 
Lyrics_NRC<-Lyrics_word %>% inner_join(get_sentiments("nrc") ) 
Joining, by = "word"
head(Lyrics_NRC)

Count occurrence of positive and negative sentiments using NRC-word-emotion association lexicon (EmoLex)

The code below will count in each album the number of words grouped by sentimnet (there are 10 different sentiments).

#Count the occurrence with each album
Sents = Lyrics_NRC %>%group_by(album)%>%count(sentiment) %>% ungroup()
Sents

Lyrics_NRC %>% group_by(album)%>% filter(sentiment %in% c("positive", "negative", "joy", "anger")) %>% 
  count(sentiment)

Graphs to compare sentiments between albums

Here we take into consideration three situations:

  • ONLY positive and negative sentiments

  • ALL apart positive and negative sentiments

  • ALL sentiments

Graphics will show the distribution of sentiments for each filter (as above).

par(mfrow=c(1,3))
# Stacked percent only for positive and negative sentiments
Sents %>% filter((sentiment =="positive"|sentiment=="negative")) %>%
 ggplot(aes(fill=sentiment, y=n, x=album)) +
 geom_bar(position="fill", stat="identity")


# Stacked percent by removing positive and negative sentiments
Sents %>% filter(!(sentiment =="positive"|sentiment=="negative")) %>%
 ggplot(aes(fill=sentiment, y=n, x=album)) +
 geom_bar(position="fill", stat="identity")


# Stacked percent all sentiments
Sents %>% 
 ggplot(aes(fill=sentiment, y=n, x=album)) +
 geom_bar(position="fill", stat="identity") 

Table of number of sentiments by album using NRC

MyTable = Sents %>%
pivot_wider(names_from=sentiment, values_from=n)
MyTable

The three graphs above show sentiment analysis for the two albums. The first graph, considers ALL sentiments ; the second graph removes sentiments “positive” and “negative”, the thoird graph displays the distribution of ALL sentiments using NRC. Observing the distributions of this sentiments in two albums we can think that the album “Pleas Please Me” has greater positive-joy sentiments compared to the album “Let it Be” (which was the last album by Beatles. Maybe they predicted the end of “Beatles Epoch”). In the second graph (by removing “positive” and " negative " sentiments) we also observe joy to have a larger distribution in total sentiments compared to joy in the album “Let it Be” where we see anticipation, joy and trust distributed almost in the same ratio.

BING sentiment analysis
Lyrics_bing<-Lyrics_word %>% inner_join(get_sentiments("bing") ) 
Joining, by = "word"
Lyrics_bing

#Count the occurrence with each album
Sents = Lyrics_bing %>%group_by(album)%>%count(sentiment) %>% ungroup()
Sents

# Stacked percent all sentiments (positive and negative)
Sents %>% 
 ggplot(aes(fill=sentiment, y=n, x=album)) +
 geom_bar(position="fill", stat="identity") 

Using bing lexicons we observe that measured in number of words in the first album “Please Please Me” this number is greater (111-negative and 209-positive) compared to the last album “Let It Be” (56-negative and 132-positive) BUT measured as a percentage (see graph) we observe that this two sentiments have almost the same distribution in both albums. Respectively approximate 65-68% positive sentiment and this figure is greater in the album “Let It Be”.The difference here, is smaller.

AFINN sentiment analysis

Here we are showing different graphs to better visualize and compare the distribution of sentiment words in two albums.

Lyrics_afinn<-Lyrics_word %>% inner_join(get_sentiments("afinn") ) 
Joining, by = "word"
Lyrics_afinn

#Count the occurrence with each album of positive and negative sentiments measured by numercial values
Sents =  Lyrics_afinn %>% group_by(album)%>% count(value)%>% ungroup()
Sents

#Ploting the distribution of sentiments 
p<-ggplot(Sents, aes(x=value, y=n, fill=album)) + 
     geom_bar(position = 'dodge', stat='identity') +
     geom_text(aes(label=n), position=position_dodge(width=0.9), vjust=-0.25)
p

p+facet_wrap(~album, ncol = 2)

  
# Stacked percent all sentiments (positive and negative)
Sents %>% 
 ggplot(aes(x=value,y=n,fill=album)) +
 geom_bar(position="fill", stat="identity") 


# comparing distributions on albums
p<-ggplot(Sents, aes(x=value, y=n, fill=album)) +
     geom_area()
p+facet_wrap(~album, ncol = 2)

To have an estimate of the net sentiment (positive - negative) in each album for each sentiment lexicon. Let’s bind them together and visualize them in figure below. The graph below shows the differences between number of positive sentiments and negative sentiments as a measure to display better the weight of these feelings (sentiments) in two albums. From all three lexicons this difference seem to be greater in the album “Please Please Me”.

afinn <- Lyrics_word %>% 
  inner_join(get_sentiments("afinn")) %>% 
  group_by(album) %>% 
  summarise(sentiment = sum(value)) %>% 
  mutate(method = "AFINN")
Joining, by = "word"
afinn

bing_and_nrc <- bind_rows(
  Lyrics_word %>% 
    inner_join(get_sentiments("bing")) %>%
    mutate(method = "Bing et al."),
  Lyrics_word %>% 
    inner_join(get_sentiments("nrc") %>% 
                 filter(sentiment %in% c("positive", 
                                         "negative"))
    ) %>%
    mutate(method = "NRC")) %>%
  count(method, album, sentiment) %>%
  pivot_wider(names_from = sentiment,
              values_from = n,
              values_fill = 0) %>% 
  mutate(sentiment = positive - negative)
Joining, by = "word"
Joining, by = "word"
bing_and_nrc
# Plotting the three sentiment lexicons
bind_rows(afinn, 
          bing_and_nrc) %>%
  ggplot(aes(album, sentiment, fill = method)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~method, ncol = 1, scales = "free_y")

Hypothesis test

Chi-Square Test can be used as a test for relationship between categorical variables.

• Ho: There is no relationship between variables (album and sentiment);

We can perform a Chi-Square Test for Independence to test for dependency/correlation in our two categorical variables (Album and sentiments). - Note: Chi-Square is valid for categorical variables and ordinal variables with few categories which in our case it can be used.

If we obtain a p-value < 0.05, we have evidence of an association between the categorical variables, so we reject null hypothesis.

NRC

#reminding EmoLex-NRC without positive and negative sentiments
Sents_nrc = Lyrics_NRC %>% filter(!sentiment %in% c("positive", "negative")) %>% group_by(album)%>%count(sentiment) %>% ungroup()
Sents_nrc

# Organizing the table for a better view
MyTable_nrc = Sents_nrc %>%
pivot_wider(names_from=sentiment, values_from=n)
MyTable_nrc # contigency table

# create a contigency table as a matrix to pbtain the chi square results
Matrix_nrc<-MyTable_nrc[c(1,2),2:9]
Matrix_nrc
chisq.test(Matrix_nrc) # Chi Square test for relationship between the variables (Album and Sentiments) 

    Pearson's Chi-squared test

data:  Matrix_nrc
X-squared = 100.44, df = 7, p-value < 2.2e-16
# Output
# Pearson's Chi-squared test
#
#  data:  Matrix_nrc
#  X-squared = 100.44, df = 7, p-value < 2.2e-16

Interpretation We obtain a p-value < 0.05, so we have evidence of an association between the album and sentiment words used in it.(reject Ho: There is no relationship between variables (album and sentiment))

Bing et al

# reminding Bing lexicon
Lyrics_bing<-Lyrics_word %>% inner_join(get_sentiments("bing") ) 
Lyrics_bing

#Count the occurrence with each album
Sents_bing = Lyrics_bing %>%group_by(album)%>%count(sentiment) %>% ungroup()
Sents_bing
# Organizing the table for a better view
MyTable_bing = Sents_bing %>%
pivot_wider(names_from=sentiment, values_from=n)
MyTable_bing # contigency table

# create a contigency table as a matrix to pbtain the chi square results
Matrix_bing<-MyTable_bing[c(1,2),2:3]
Matrix_bing
chisq.test(Matrix_bing) # Chi Square test for relationship between the variables (Album and Sentiments) 

Interpretation: For Bing lexicon we obtain a p-value = 0.2996 > 0.05, so we do NOT reject H0: Based ont his method of sentiment analysis , there is no relationship between variables (album and sentiment). The decision of these two different lexicos depend also on the number of sentiments used to test the dependence. Removing positive and negative sentiments from NRC can be a reason that we rejected H0 hypothesis for that subset of words.

END!

For more follow in: https://www.linkedin.com/in/eralda-dhamo-gjika-71879128/?originalSubdomain=al

LS0tDQp0aXRsZTogIlRleHQgTWluaW5nIHdpdGggUi0gQmVhdGxlcyBBbGJ1bSBhbmQgTHlyaWNzIg0KYXV0aG9yOiAiRXJhbGRhIEdqaWthIg0KZGF0ZTogIkZlYnJ1YXJ5IDA3LCAyMDIyIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KIyMgVGV4dCBNaW5pbmcgey50YWJzZXR9DQojIyMgU2VudGltZW50IEFuYWx5c2lzDQoNCioqU2VudGltZW50IEFuYWx5c2lzLioqDQpXZSB3aXNoIHRvIGNvbXBhcmUgdGhlIHNlbnRpbWVudCBkaXN0cmlidXRpb25zIGFjcm9zcyBCZWF0bGVzIGFsYnVtcyBmcm9tIHRoZSBzdGFydCB0byB0aGUgZW5kIG9mIHRoZWlyIGNvcmUgVUsgc3R1ZGlvIGFsYnVtcy4NCg0KSGVyZSBJIGhhdmUgdXNlZCAqKuKAnEJlYXRsZXMgU29uZyBBbGJ1bSBZZWFyLlJkYXRh4oCdIGZpbGUgZnJvbSBBc3NpZ25tZW50IDIuIEl0IGNvbnRhaW5zIGx5cmljcywgYWxidW1zLHRyYWNrIG5hbWVzLCBhbmQgeWVhciBvZiByZWxlYXNlIGZvciB0aGUgQmVhdGxlcyBVSyBzdHVkaW8gYWxidW1zLg0KDQpUaGUgZGF0YXNldCB1c2VkIGlzIHRoZSBvbmUgaW1wb3J0ZWQgZnJvbSB0aGUgY3N2IGZpbGUgZm91bmQgaW4gdGhlIGFzc2lnbm1lbnQgZm9sZGVyLiBTbyB3ZSBoYXZlIDE3NCBzb25nIGFuZCA0IHZhcmlhYmxlcyB3aGljaCBhcmUgOiBzb25nbHlyaWNzLCB0cmFja190aXRsZSwgYWxidW0gYW5kIHllYXIuDQoNCltUaWR5VGV4dE1pbmluZ10oaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL3RmaWRmLmh0bWwpIA0KDQpTZXQgd29ya2luZyBkaXJlY3RvcnkgeW91ciBmaWxlIHdoZXJlIGJlYXRsZXMgU29uZyBBbGJ1bSBZZWFyLlJkYXRhIGlzLiBUaGVuIGNsaWNrIG9uIHRoZSBkYXRhIGFuZCB1cGxvYWQgaXQgaW50byB5b3VyIHdvcmtpbmcgc3BhY2UuICBZb3Ugd2lsbCBzZWUgdGhlIG5hbWUgb2YgdGhlIGRhdGEgYXBwZWFyIGFzOiBMeXJpYy5hbGJ1bS55ZWFyLmtlZXANCmBgYHtyIGVjaG89VFJVRX0NCmxvYWQoIkQ6L0FMREEgMjAyMS9DUllTVEFMIFN5c3RlbS1EYXRhIFNjaWVuY2UgcHJvamVjdC9DcnlzdGFsLURTIFByb2plY3QgMy9URUMtRFMgLVdlZWsgbWF0ZXJpYWxzL1dlZWsgNS9CZWF0bGVzIFNvbmcgQWxidW0gWWVhci5SZGF0YSIpDQpCZWF0bGVzX2x5cmljczwtTHlyaWMuYWxidW0ueWVhci5rZWVwDQojVmlldyhCZWF0bGVzX2x5cmljcykNCmhlYWQoQmVhdGxlc19seXJpY3MsMikNCmRpbShCZWF0bGVzX2x5cmljcykNCmBgYA0KDQpUaGUgZGF0YXNldCB1c2VkIChmcm9tIHRoZSBBc3NpZ25tZW50IGZvbGRlcikgY29udGFpbnMgdGhlIFVLIHN0dWRpbyByZWNvcmRpbmdzLCBpbiB0b3RhbCAxMiBBbGJ1bXMuIFtTb3VyY2VdKGh0dHBzOi8vd3d3LmJlYXRsZXNiaWJsZS5jb20vYWxidW1zLykuIEZyb20gdGhlIHNvdXJjZSB1c2VkLCB0aGUgZmlyc3QgVUsgc3R1ZGlvIHJlY29yZGluZyBpcyBhbGJ1bSAiUGxlYXNlIFBsZWFzZSBNZSIgaW4gMTk2MywgYW5kIHRoZSBsYXN0IFVLIHN0dWRpbyByZWNvcmRpbmcgd2FzIHRoZSBhbGJ1bSAiTGV0IEl0IEJlIiBpbiAxOTcwLiAgICAgICAgICAgIA0KKCoqTm90ZSoqOiBJIGFtIG5vdCBmYW1pbGlhciB3aXRoIHRoZSBCZWF0bGVzIHNvbmdzIHNvIEkgcmVseSBvbiB0aGlzIHNvdXJjZSBmb3IgdGhlIGNsYXNzaWZpY2F0aW9uIG9mIHRoZSBVSyBhbGJ1bXMgYXMgYWJvdmUuKQ0KVGhlIGNvZGVzIGJlbG93IHdpbGwgY3JlYXRlIHRpYmJsZXMgZm9yIGVhY2ggYWxidW0gYW5kIHJlc3BlY3RpdmVseSBkaXNwbGF5IHRoZSBzb25nbHlyaWNzIGFuZCB0aXRsZSBvZiB0aGUgc29uZy4gMTQgc29uZ3MgaW4gdGhlIGFsYnVtICJQbGVhc2UgUGxlYXNlIE1lIiBhbmQgMTIgc29uZ3MgaW4gdGhlIGFsYnVtICJMZXQgSXQgQmUiLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KIyBUaGUgdHJhY2sgbGlzdCBvZiAiUGxlYXNlIFBsZWFzZSBtZSINCkJlYXRsZXNfVUtfMTwtQmVhdGxlc19seXJpY3MgJT4lICANCiAgZmlsdGVyKGFsYnVtICVpbiUgIlBsZWFzZSBQbGVhc2UgTWUiKQ0KVmlldyhCZWF0bGVzX1VLXzEpDQpCZWF0bGVzXzFfbHlyaWM8LUJlYXRsZXNfVUtfMSAlPiUgc2VsZWN0KHNvbmdseXJpYykjIHNhdmUgYWxsIGx5cmljcyBvZiBzb25ncyBpbiBhbGJ1bSAiUGxlYXNlIFBsZWFzZSBNZSINCiMgQmVhdGxlc18xX2x5cmljICMgcHJpbnQgdGhlIGx5cmljcyBvZiBzb25ncyBmcm9tIGFsYnVtICJQbGVhc2UgUGxlYXNlIE1lIg0KQmVhdGxlc18xX3RpdGxlPC1CZWF0bGVzX1VLXzEgJT4lIHNlbGVjdCh0cmFja190aXRsZSkjIHNhdmUgYWxsIHRpdGxlIG9mIHNvbmdzIGluIGFsYnVtICJQbGVhc2UgUGxlYXNlIE1lIg0KIyBCZWF0bGVzXzFfdGl0bGUgIyBwcmludCB0aGUgdGl0bGUgc29uZyBmcm9tIGFsYnVtICJQbGVhc2UgUGxlYXNlIE1lIg0KDQojIFRoZSB0cmFjayBsaXN0IG9mICJMZXQgSXQgQmUiDQpCZWF0bGVzX1VLXzI8LUJlYXRsZXNfbHlyaWNzICU+JSAgDQogIGZpbHRlcihhbGJ1bSAlaW4lICJMZXQgSXQgQmUiKQ0KVmlldyhCZWF0bGVzX1VLXzIpDQpCZWF0bGVzXzJfbHlyaWM8LUJlYXRsZXNfVUtfMiAlPiUgc2VsZWN0KHNvbmdseXJpYykjIHNhdmUgYWxsIGx5cmljcyBvZiBzb25ncyBpbiBhbGJ1bSAiUGxlYXNlIFBsZWFzZSBNZSINCiMgQmVhdGxlc18yX2x5cmljICMgcHJpbnQgdGhlIGx5cmljcyBvZiBzb25ncyBmcm9tIGFsYnVtICJQbGVhc2UgUGxlYXNlIE1lIg0KQmVhdGxlc18yX3RpdGxlPC1CZWF0bGVzX1VLXzIgJT4lIHNlbGVjdCh0cmFja190aXRsZSkjIHNhdmUgYWxsIHRpdGxlIG9mIHNvbmdzIGluIGFsYnVtICJQbGVhc2UgUGxlYXNlIE1lIg0KIyBCZWF0bGVzXzJfdGl0bGUgIyBwcmludCB0aGUgdGl0bGUgc29uZyBmcm9tIGFsYnVtICJQbGVhc2UgUGxlYXNlIE1lIg0KDQoNCiMgdG8gaGF2ZSBib3RoIGFsYnVtcyBpbiBvbmUgZGF0YXNldA0KQmVhdGxlc19VS18xXzI8LUJlYXRsZXNfbHlyaWNzICU+JSANCiAgZmlsdGVyKGFsYnVtICVpbiUgYygiUGxlYXNlIFBsZWFzZSBNZSIsIkxldCBJdCBCZSIpKSAlPiUgIyB0byBoYXZlIGJvdGggYWxidW1zIGluIG9uZSBkYXRhc2V0DQogIGdyb3VwX2J5KGFsYnVtKSU+JQ0KICBzZWxlY3QodHJhY2tfdGl0bGUpICU+JSAjIGdyb3VwIGJ5IGFsYnVtDQogIHN1bW1hcmlzZShucl9vZl9zb25nPWxlbmd0aCh0cmFja190aXRsZSkpICMgY291bnQgdGhlIG51bWJlciBvZiBzb25nIHBlciBhbGJ1bQ0KIyBCZWF0bGVzX1VLXzFfMiAjIHByaW50IHRoZSBudW1iZXIgb2Ygc29uZyBwZXIgYWxidW0NCg0KIyBwcmludCBBTEwgYWxidW1zIGFuZCBudW1iZXIgb2Ygc29uZ3MgcGVyIGFsYnVtDQpCZWF0bGVzX2x5cmljcyAlPiUgZ3JvdXBfYnkoYWxidW0pICU+JSBzZWxlY3QodHJhY2tfdGl0bGUpJT4lIHVuaXF1ZSgpJT4lIGNvdW50KCkNCmBgYA0KIyMjIyAqKlNlbnRpbWVudCBBbmFseXNpcyB1c2luZyBOUkMsIGJpbmcgYW5kIFNGSU5OIGxleGljb25zKioNCg0KTGlicmFyaWVzIHVzZWQgZnJvbSB0aGlzIHBhcnQgYW5kIG9uIGFyZToNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeSh0ZXh0ZGF0YSkjIHRvIGFjY2VzcyBOUkMgd29yZCAtZW1vdGlvbiBhc3NvY2lhdGlvbiBsZXhpY29uMQ0KbGlicmFyeSh0bSkNCmxpYnJhcnkodG9waWNtb2RlbHMpDQpsaWJyYXJ5KGphbml0b3IpICMgY2xlYW5zIHVwIHNvbWUgc21hbGwgdGFibGVzIA0KYGBgDQoNCkhlcmUgaXMgb25lIG9mIHRoZSByZWZlcmVuY2VzIHVzZWQgYXBhcnQgdGhlIGxlY3R1cmUgbm90ZXM6IFtSZWZlcmVuY2UgaW4gUl0oaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL3NlbnRpbWVudC5odG1sKS4NCmBgYHtyfQ0KIyBwcmludCBhbGwgYWxidW1zIGFuZCBudW1iZXIgb2Ygc29uZ3MgcGVyIGFsYnVtDQpCZWF0bGVzX2x5cmljcyAlPiUgZ3JvdXBfYnkoYWxidW0pICU+JSBzZWxlY3QodHJhY2tfdGl0bGUpJT4lIHVuaXF1ZSgpJT4lIGNvdW50KCkNCg0KIyBudW1iZXIgb2Ygc29uZyBwZXIgYWxidW0NCkJlYXRsZXNfbHlyaWNzICU+JSBncm91cF9ieShhbGJ1bSkgJT4lIHNlbGVjdCh0cmFja190aXRsZSklPiUgdW5pcXVlKCklPiUNCmNvdW50KCkgJT4lIHVuZ3JvdXAoKSAlPiUgVmlldygpDQoNCkx5cmljczEgPSBCZWF0bGVzX2x5cmljcyAlPiUgZmlsdGVyKGFsYnVtPT0gIlBsZWFzZSBQbGVhc2UgTWUiKQ0KaGVhZChMeXJpY3MxLDEpDQoNCkx5cmljczIgPSBCZWF0bGVzX2x5cmljcyAlPiUgZmlsdGVyKGFsYnVtPT0gIkxldCBJdCBCZSIpDQpoZWFkKEx5cmljczIsMSkNCg0KTHlyaWNzID0gcmJpbmQoTHlyaWNzMSxMeXJpY3MyKSAjIGNyZWF0ZSBhIHRpYmJsZSB3aXRoIHR3byBhbGJ1bXMgDQpoZWFkKEx5cmljcywxKQ0KYGBgDQoNCkxldCdzIGZpbmQgdW5pcXVlIHdvcmRzIGluIG91ciB0d28gYWxidW1zIGFuZCBhZGQgKip3b3JkKiogY29sdW1uIHdoaWNoIHdpbGwgYmUgbGF0dGVyIGFuYWx5c2VkOg0KYGBge3J9DQpMeXJpY3Nfd29yZDwtTHlyaWNzICU+JQ0KICAgICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gc29uZ2x5cmljKSAlPiUNCiAgICAgYW50aV9qb2luKGdldF9zdG9wd29yZHMoKSkgJT4lDQogICAgIGdyb3VwX2J5KHRyYWNrX3RpdGxlKQ0KYGBgDQojIyMjICoqTlJDIGxleGljb25zKioNClRoaXMgY29kZSBoZXJlIHdpbGwgYWRkIHRoZSBjb2x1bW4gKip3b3JkKiogZm9yIGVhY2ggc29uZyBhbmQgdGhlICoqc2VudGltZW50KiogY29sdW1uIHRvIGNsYXNzaWZ5IGl0Lg0KYGBge3J9DQojIG5yYyBsZXhpY29uIA0KTHlyaWNzX05SQzwtTHlyaWNzX3dvcmQgJT4lIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICkgDQpoZWFkKEx5cmljc19OUkMpDQpgYGANCiMjIyMgKipDb3VudCBvY2N1cnJlbmNlIG9mIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBzZW50aW1lbnRzIHVzaW5nIE5SQy13b3JkLWVtb3Rpb24gYXNzb2NpYXRpb24gbGV4aWNvbiAoRW1vTGV4KSoqDQpUaGUgY29kZSBiZWxvdyB3aWxsIGNvdW50IGluIGVhY2ggYWxidW0gdGhlIG51bWJlciBvZiB3b3JkcyBncm91cGVkIGJ5IHNlbnRpbW5ldCAodGhlcmUgYXJlIDEwIGRpZmZlcmVudCBzZW50aW1lbnRzKS4NCmBgYHtyfQ0KI0NvdW50IHRoZSBvY2N1cnJlbmNlIHdpdGggZWFjaCBhbGJ1bQ0KU2VudHMgPSBMeXJpY3NfTlJDICU+JWdyb3VwX2J5KGFsYnVtKSU+JWNvdW50KHNlbnRpbWVudCkgJT4lIHVuZ3JvdXAoKQ0KU2VudHMNCg0KTHlyaWNzX05SQyAlPiUgZ3JvdXBfYnkoYWxidW0pJT4lIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIsICJqb3kiLCAiYW5nZXIiKSkgJT4lIA0KICBjb3VudChzZW50aW1lbnQpDQpgYGANCiMjIyMgKipHcmFwaHMgdG8gY29tcGFyZSBzZW50aW1lbnRzIGJldHdlZW4gYWxidW1zKioNCkhlcmUgd2UgdGFrZSBpbnRvIGNvbnNpZGVyYXRpb24gdGhyZWUgc2l0dWF0aW9uczoNCg0KLSAqKk9OTFkqKiBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgc2VudGltZW50cw0KDQotICoqQUxMIGFwYXJ0KiogcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHNlbnRpbWVudHMNCg0KLSAqKkFMTCoqIHNlbnRpbWVudHMNCg0KR3JhcGhpY3Mgd2lsbCBzaG93IHRoZSBkaXN0cmlidXRpb24gb2Ygc2VudGltZW50cyBmb3IgZWFjaCBmaWx0ZXIgKGFzIGFib3ZlKS4NCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwzKSkNCiMgU3RhY2tlZCBwZXJjZW50IG9ubHkgZm9yIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBzZW50aW1lbnRzDQpTZW50cyAlPiUgZmlsdGVyKChzZW50aW1lbnQgPT0icG9zaXRpdmUifHNlbnRpbWVudD09Im5lZ2F0aXZlIikpICU+JQ0KIGdncGxvdChhZXMoZmlsbD1zZW50aW1lbnQsIHk9biwgeD1hbGJ1bSkpICsNCiBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsIHN0YXQ9ImlkZW50aXR5IikNCg0KIyBTdGFja2VkIHBlcmNlbnQgYnkgcmVtb3ZpbmcgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHNlbnRpbWVudHMNClNlbnRzICU+JSBmaWx0ZXIoIShzZW50aW1lbnQgPT0icG9zaXRpdmUifHNlbnRpbWVudD09Im5lZ2F0aXZlIikpICU+JQ0KIGdncGxvdChhZXMoZmlsbD1zZW50aW1lbnQsIHk9biwgeD1hbGJ1bSkpICsNCiBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsIHN0YXQ9ImlkZW50aXR5IikNCg0KIyBTdGFja2VkIHBlcmNlbnQgYWxsIHNlbnRpbWVudHMNClNlbnRzICU+JSANCiBnZ3Bsb3QoYWVzKGZpbGw9c2VudGltZW50LCB5PW4sIHg9YWxidW0pKSArDQogZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiLCBzdGF0PSJpZGVudGl0eSIpIA0KYGBgDQojIyMjICoqVGFibGUgb2YgbnVtYmVyIG9mIHNlbnRpbWVudHMgYnkgYWxidW0gdXNpbmcgTlJDKioNCmBgYHtyfQ0KTXlUYWJsZSA9IFNlbnRzICU+JQ0KcGl2b3Rfd2lkZXIobmFtZXNfZnJvbT1zZW50aW1lbnQsIHZhbHVlc19mcm9tPW4pDQpNeVRhYmxlDQpgYGANCg0KVGhlIHRocmVlIGdyYXBocyBhYm92ZSBzaG93IHNlbnRpbWVudCBhbmFseXNpcyBmb3IgdGhlIHR3byBhbGJ1bXMuIFRoZSBmaXJzdCBncmFwaCwgY29uc2lkZXJzIEFMTCBzZW50aW1lbnRzIDsgdGhlIHNlY29uZCBncmFwaCByZW1vdmVzIHNlbnRpbWVudHMgInBvc2l0aXZlIiBhbmQgIm5lZ2F0aXZlIiwgdGhlIHRob2lyZCBncmFwaCBkaXNwbGF5cyB0aGUgZGlzdHJpYnV0aW9uIG9mIEFMTCBzZW50aW1lbnRzIHVzaW5nIE5SQy4gDQpPYnNlcnZpbmcgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhpcyBzZW50aW1lbnRzIGluIHR3byBhbGJ1bXMgd2UgY2FuIHRoaW5rIHRoYXQgdGhlIGFsYnVtICoqIlBsZWFzIFBsZWFzZSBNZSIqKiBoYXMgZ3JlYXRlciBwb3NpdGl2ZS1qb3kgc2VudGltZW50cyBjb21wYXJlZCB0byB0aGUgYWxidW0gKioiTGV0IGl0IEJlIioqICh3aGljaCB3YXMgdGhlIGxhc3QgYWxidW0gYnkgQmVhdGxlcy4gTWF5YmUgdGhleSBwcmVkaWN0ZWQgdGhlICplbmQqIG9mICJCZWF0bGVzIEVwb2NoIikuIEluIHRoZSBzZWNvbmQgZ3JhcGggKGJ5IHJlbW92aW5nICJwb3NpdGl2ZSAiIGFuZCAiIG5lZ2F0aXZlICIgc2VudGltZW50cykgd2UgYWxzbyBvYnNlcnZlIGpveSAgdG8gaGF2ZSBhIGxhcmdlciBkaXN0cmlidXRpb24gaW4gdG90YWwgc2VudGltZW50cyBjb21wYXJlZCB0byBqb3kgaW4gdGhlIGFsYnVtICJMZXQgaXQgQmUiIHdoZXJlIHdlIHNlZSAqKmFudGljaXBhdGlvbiwgam95IGFuZCB0cnVzdCoqIGRpc3RyaWJ1dGVkIGFsbW9zdCBpbiB0aGUgc2FtZSByYXRpby4gDQoNCiMjIyMjICoqQklORyBzZW50aW1lbnQgYW5hbHlzaXMqKg0KYGBge3J9DQpMeXJpY3NfYmluZzwtTHlyaWNzX3dvcmQgJT4lIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSApIA0KTHlyaWNzX2JpbmcNCg0KI0NvdW50IHRoZSBvY2N1cnJlbmNlIHdpdGggZWFjaCBhbGJ1bQ0KU2VudHMgPSBMeXJpY3NfYmluZyAlPiVncm91cF9ieShhbGJ1bSklPiVjb3VudChzZW50aW1lbnQpICU+JSB1bmdyb3VwKCkNClNlbnRzDQoNCiMgU3RhY2tlZCBwZXJjZW50IGFsbCBzZW50aW1lbnRzIChwb3NpdGl2ZSBhbmQgbmVnYXRpdmUpDQpTZW50cyAlPiUgDQogZ2dwbG90KGFlcyhmaWxsPXNlbnRpbWVudCwgeT1uLCB4PWFsYnVtKSkgKw0KIGdlb21fYmFyKHBvc2l0aW9uPSJmaWxsIiwgc3RhdD0iaWRlbnRpdHkiKSANCmBgYA0KVXNpbmcgKipiaW5nKiogbGV4aWNvbnMgd2Ugb2JzZXJ2ZSB0aGF0IG1lYXN1cmVkIGluIG51bWJlciBvZiB3b3JkcyBpbiB0aGUgZmlyc3QgYWxidW0gIlBsZWFzZSBQbGVhc2UgTWUiIHRoaXMgbnVtYmVyIGlzIGdyZWF0ZXIgKDExMS1uZWdhdGl2ZSBhbmQgMjA5LXBvc2l0aXZlKSBjb21wYXJlZCB0byB0aGUgbGFzdCBhbGJ1bSAiTGV0IEl0IEJlIiAoNTYtbmVnYXRpdmUgYW5kIDEzMi1wb3NpdGl2ZSkgKipCVVQqKiBtZWFzdXJlZCBhcyBhIHBlcmNlbnRhZ2UgKHNlZSBncmFwaCkgd2Ugb2JzZXJ2ZSB0aGF0IHRoaXMgdHdvIHNlbnRpbWVudHMgaGF2ZSBhbG1vc3QgdGhlIHNhbWUgZGlzdHJpYnV0aW9uIGluIGJvdGggYWxidW1zLiBSZXNwZWN0aXZlbHkgYXBwcm94aW1hdGUgNjUtNjglIHBvc2l0aXZlIHNlbnRpbWVudCBhbmQgdGhpcyBmaWd1cmUgaXMgZ3JlYXRlciBpbiB0aGUgYWxidW0gIkxldCBJdCBCZSIuVGhlIGRpZmZlcmVuY2UgaGVyZSwgaXMgc21hbGxlci4NCg0KIyMjIyAqKkFGSU5OIHNlbnRpbWVudCBhbmFseXNpcyoqDQpIZXJlIHdlIGFyZSBzaG93aW5nIGRpZmZlcmVudCBncmFwaHMgdG8gYmV0dGVyIHZpc3VhbGl6ZSBhbmQgY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHNlbnRpbWVudCB3b3JkcyBpbiB0d28gYWxidW1zLg0KYGBge3J9DQpMeXJpY3NfYWZpbm48LUx5cmljc193b3JkICU+JSBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJhZmlubiIpICkgDQpMeXJpY3NfYWZpbm4NCg0KI0NvdW50IHRoZSBvY2N1cnJlbmNlIHdpdGggZWFjaCBhbGJ1bSBvZiBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgc2VudGltZW50cyBtZWFzdXJlZCBieSBudW1lcmNpYWwgdmFsdWVzDQpTZW50cyA9ICBMeXJpY3NfYWZpbm4gJT4lIGdyb3VwX2J5KGFsYnVtKSU+JSBjb3VudCh2YWx1ZSklPiUgdW5ncm91cCgpDQpTZW50cw0KDQojUGxvdGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHNlbnRpbWVudHMgDQpwPC1nZ3Bsb3QoU2VudHMsIGFlcyh4PXZhbHVlLCB5PW4sIGZpbGw9YWxidW0pKSArIA0KICAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICdkb2RnZScsIHN0YXQ9J2lkZW50aXR5JykgKw0KICAgICBnZW9tX3RleHQoYWVzKGxhYmVsPW4pLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjkpLCB2anVzdD0tMC4yNSkNCnANCnArZmFjZXRfd3JhcCh+YWxidW0sIG5jb2wgPSAyKQ0KICANCiMgU3RhY2tlZCBwZXJjZW50IGFsbCBzZW50aW1lbnRzIChwb3NpdGl2ZSBhbmQgbmVnYXRpdmUpDQpTZW50cyAlPiUgDQogZ2dwbG90KGFlcyh4PXZhbHVlLHk9bixmaWxsPWFsYnVtKSkgKw0KIGdlb21fYmFyKHBvc2l0aW9uPSJmaWxsIiwgc3RhdD0iaWRlbnRpdHkiKSANCg0KIyBjb21wYXJpbmcgZGlzdHJpYnV0aW9ucyBvbiBhbGJ1bXMNCnA8LWdncGxvdChTZW50cywgYWVzKHg9dmFsdWUsIHk9biwgZmlsbD1hbGJ1bSkpICsNCiAgICAgZ2VvbV9hcmVhKCkNCnArZmFjZXRfd3JhcCh+YWxidW0sIG5jb2wgPSAyKQ0KYGBgDQpUbyBoYXZlIGFuIGVzdGltYXRlIG9mIHRoZSBuZXQgc2VudGltZW50ICoqKHBvc2l0aXZlIC0gbmVnYXRpdmUpKiogaW4gZWFjaCBhbGJ1bSBmb3IgZWFjaCBzZW50aW1lbnQgbGV4aWNvbi4gTGV04oCZcyBiaW5kIHRoZW0gdG9nZXRoZXIgYW5kIHZpc3VhbGl6ZSB0aGVtIGluIGZpZ3VyZSBiZWxvdy4NClRoZSBncmFwaCBiZWxvdyBzaG93cyB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBudW1iZXIgb2YgcG9zaXRpdmUgc2VudGltZW50cyBhbmQgbmVnYXRpdmUgc2VudGltZW50cyBhcyBhIG1lYXN1cmUgdG8gZGlzcGxheSBiZXR0ZXIgdGhlIHdlaWdodCBvZiB0aGVzZSBmZWVsaW5ncyAoc2VudGltZW50cykgaW4gdHdvIGFsYnVtcy4gRnJvbSBhbGwgdGhyZWUgbGV4aWNvbnMgdGhpcyBkaWZmZXJlbmNlIHNlZW0gdG8gYmUgZ3JlYXRlciBpbiB0aGUgYWxidW0gIlBsZWFzZSBQbGVhc2UgTWUiLg0KYGBge3J9DQphZmlubiA8LSBMeXJpY3Nfd29yZCAlPiUgDQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImFmaW5uIikpICU+JSANCiAgZ3JvdXBfYnkoYWxidW0pICU+JSANCiAgc3VtbWFyaXNlKHNlbnRpbWVudCA9IHN1bSh2YWx1ZSkpICU+JSANCiAgbXV0YXRlKG1ldGhvZCA9ICJBRklOTiIpDQphZmlubg0KDQpiaW5nX2FuZF9ucmMgPC0gYmluZF9yb3dzKA0KICBMeXJpY3Nfd29yZCAlPiUgDQogICAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKSAlPiUNCiAgICBtdXRhdGUobWV0aG9kID0gIkJpbmcgZXQgYWwuIiksDQogIEx5cmljc193b3JkICU+JSANCiAgICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSAlPiUgDQogICAgICAgICAgICAgICAgIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmVnYXRpdmUiKSkNCiAgICApICU+JQ0KICAgIG11dGF0ZShtZXRob2QgPSAiTlJDIikpICU+JQ0KICBjb3VudChtZXRob2QsIGFsYnVtLCBzZW50aW1lbnQpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc2VudGltZW50LA0KICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IG4sDQogICAgICAgICAgICAgIHZhbHVlc19maWxsID0gMCkgJT4lIA0KICBtdXRhdGUoc2VudGltZW50ID0gcG9zaXRpdmUgLSBuZWdhdGl2ZSkNCg0KYmluZ19hbmRfbnJjDQpgYGANCg0KYGBge3J9DQojIFBsb3R0aW5nIHRoZSB0aHJlZSBzZW50aW1lbnQgbGV4aWNvbnMNCmJpbmRfcm93cyhhZmlubiwgDQogICAgICAgICAgYmluZ19hbmRfbnJjKSAlPiUNCiAgZ2dwbG90KGFlcyhhbGJ1bSwgc2VudGltZW50LCBmaWxsID0gbWV0aG9kKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGZhY2V0X3dyYXAofm1ldGhvZCwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KYGBgDQojIyMjICoqSHlwb3RoZXNpcyB0ZXN0KioNCioqQ2hpLVNxdWFyZSoqIFRlc3QgY2FuIGJlIHVzZWQgYXMgYSB0ZXN0IGZvciByZWxhdGlvbnNoaXAgYmV0d2VlbiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuDQoNCuKAoiBIbzogVGhlcmUgaXMgbm8gcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFibGVzIChhbGJ1bSBhbmQgc2VudGltZW50KTsgDQoNCldlIGNhbiBwZXJmb3JtIGEgQ2hpLVNxdWFyZSBUZXN0IGZvciBJbmRlcGVuZGVuY2UgdG8gdGVzdCBmb3IgZGVwZW5kZW5jeS9jb3JyZWxhdGlvbiBpbiBvdXIgdHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyAoQWxidW0gYW5kIHNlbnRpbWVudHMpLiANCi0gKipOb3RlOioqIENoaS1TcXVhcmUgaXMgdmFsaWQgZm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhbmQgb3JkaW5hbCB2YXJpYWJsZXMgd2l0aCBmZXcgY2F0ZWdvcmllcyB3aGljaCBpbiBvdXIgY2FzZSBpdCBjYW4gYmUgdXNlZC4NCg0KSWYgd2Ugb2J0YWluIGEgKipwLXZhbHVlIDwgMC4wNSoqLCB3ZSBoYXZlIGV2aWRlbmNlIG9mIGFuIGFzc29jaWF0aW9uIGJldHdlZW4gdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgc28gd2UgcmVqZWN0IG51bGwgaHlwb3RoZXNpcy4NCg0KKipOUkMqKg0KYGBge3J9DQojcmVtaW5kaW5nIEVtb0xleC1OUkMgd2l0aG91dCBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgc2VudGltZW50cw0KU2VudHNfbnJjID0gTHlyaWNzX05SQyAlPiUgZmlsdGVyKCFzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpKSAlPiUgZ3JvdXBfYnkoYWxidW0pJT4lY291bnQoc2VudGltZW50KSAlPiUgdW5ncm91cCgpDQpTZW50c19ucmMNCg0KIyBPcmdhbml6aW5nIHRoZSB0YWJsZSBmb3IgYSBiZXR0ZXIgdmlldw0KTXlUYWJsZV9ucmMgPSBTZW50c19ucmMgJT4lDQpwaXZvdF93aWRlcihuYW1lc19mcm9tPXNlbnRpbWVudCwgdmFsdWVzX2Zyb209bikNCk15VGFibGVfbnJjICMgY29udGlnZW5jeSB0YWJsZQ0KDQojIGNyZWF0ZSBhIGNvbnRpZ2VuY3kgdGFibGUgYXMgYSBtYXRyaXggdG8gcGJ0YWluIHRoZSBjaGkgc3F1YXJlIHJlc3VsdHMNCk1hdHJpeF9ucmM8LU15VGFibGVfbnJjW2MoMSwyKSwyOjldDQpNYXRyaXhfbnJjDQpjaGlzcS50ZXN0KE1hdHJpeF9ucmMpICMgQ2hpIFNxdWFyZSB0ZXN0IGZvciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaWFibGVzIChBbGJ1bSBhbmQgU2VudGltZW50cykgDQoNCiMgT3V0cHV0DQojIFBlYXJzb24ncyBDaGktc3F1YXJlZCB0ZXN0DQojDQojICBkYXRhOiAgTWF0cml4X25yYw0KIyAgWC1zcXVhcmVkID0gMTAwLjQ0LCBkZiA9IDcsIHAtdmFsdWUgPCAyLjJlLTE2DQpgYGANCioqSW50ZXJwcmV0YXRpb24qKiBXZSBvYnRhaW4gYSAqKnAtdmFsdWUgPCAwLjA1KiosIHNvIHdlIGhhdmUgZXZpZGVuY2Ugb2YgYW4gYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgYWxidW0gYW5kIHNlbnRpbWVudCB3b3JkcyB1c2VkIGluIGl0LihyZWplY3QgSG86IFRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhYmxlcyAoYWxidW0gYW5kIHNlbnRpbWVudCkpDQoNCg0KKipCaW5nIGV0IGFsKioNCg0KYGBge3J9DQojIHJlbWluZGluZyBCaW5nIGxleGljb24NCkx5cmljc19iaW5nPC1MeXJpY3Nfd29yZCAlPiUgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpICkgDQpMeXJpY3NfYmluZw0KDQojQ291bnQgdGhlIG9jY3VycmVuY2Ugd2l0aCBlYWNoIGFsYnVtDQpTZW50c19iaW5nID0gTHlyaWNzX2JpbmcgJT4lZ3JvdXBfYnkoYWxidW0pJT4lY291bnQoc2VudGltZW50KSAlPiUgdW5ncm91cCgpDQpTZW50c19iaW5nDQojIE9yZ2FuaXppbmcgdGhlIHRhYmxlIGZvciBhIGJldHRlciB2aWV3DQpNeVRhYmxlX2JpbmcgPSBTZW50c19iaW5nICU+JQ0KcGl2b3Rfd2lkZXIobmFtZXNfZnJvbT1zZW50aW1lbnQsIHZhbHVlc19mcm9tPW4pDQpNeVRhYmxlX2JpbmcgIyBjb250aWdlbmN5IHRhYmxlDQoNCiMgY3JlYXRlIGEgY29udGlnZW5jeSB0YWJsZSBhcyBhIG1hdHJpeCB0byBwYnRhaW4gdGhlIGNoaSBzcXVhcmUgcmVzdWx0cw0KTWF0cml4X2Jpbmc8LU15VGFibGVfYmluZ1tjKDEsMiksMjozXQ0KTWF0cml4X2JpbmcNCmNoaXNxLnRlc3QoTWF0cml4X2JpbmcpICMgQ2hpIFNxdWFyZSB0ZXN0IGZvciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaWFibGVzIChBbGJ1bSBhbmQgU2VudGltZW50cykgDQoNCmBgYA0KDQoqKkludGVycHJldGF0aW9uOioqIEZvciAqKkJpbmcgbGV4aWNvbioqIHdlIG9idGFpbiBhICoqcC12YWx1ZSA9IDAuMjk5NiA+IDAuMDUqKiwgc28gd2UgZG8gKipOT1QqKiByZWplY3QgSDA6IEJhc2VkIG9udCBoaXMgbWV0aG9kIG9mIHNlbnRpbWVudCBhbmFseXNpcyAsIHRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhYmxlcyAoYWxidW0gYW5kIHNlbnRpbWVudCkuIFRoZSBkZWNpc2lvbiBvZiB0aGVzZSB0d28gZGlmZmVyZW50IGxleGljb3MgZGVwZW5kIGFsc28gb24gdGhlIG51bWJlciBvZiBzZW50aW1lbnRzIHVzZWQgdG8gdGVzdCB0aGUgZGVwZW5kZW5jZS4gUmVtb3ZpbmcgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHNlbnRpbWVudHMgZnJvbSBOUkMgY2FuIGJlIGEgcmVhc29uIHRoYXQgd2UgcmVqZWN0ZWQgSDAgaHlwb3RoZXNpcyBmb3IgdGhhdCBzdWJzZXQgb2Ygd29yZHMuDQoNCioqRU5EISoqDQoNCkZvciBtb3JlIGZvbGxvdyBpbjogaHR0cHM6Ly93d3cubGlua2VkaW4uY29tL2luL2VyYWxkYS1kaGFtby1namlrYS03MTg3OTEyOC8/b3JpZ2luYWxTdWJkb21haW49YWw=