Netflix datset text analysis
Introduction
In this example we are going to do some text and sentimental analysis
with the Netflix dataset. The dataset has a lot of information so you
may do many analysis based on your research question and target
variable.
Data source: https://www.kaggle.com/datasets/shivamb/netflix-shows/download?datasetVersionNumber=5
The main libraries we will use are:
library(dplyr)
library(tidytext)
library(ggplot2)
library(ggthemes)
library(stringr)
library(tidyr)
we will work on description variable so we will select only this
columns as a start.
library(readr)
netflix_titles <- read_csv("netflix_titles.csv")
Rows: 8807 Columns: 12── Column specification ───────────────────────────────────────────────────
Delimiter: ","
chr (11): show_id, type, title, director, cast, country, date_added, ra...
dbl (1): release_year
ℹ 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.
View(netflix_titles)
Dealing with stopwords helps a lot, especially when
our text information is not clear and official. Below we may see some of
the stop-words used in main libraries, but you may also create your own
stop-word vector and remove unnecessary words from your text. For more
on this refer to the work done here: https://github.com/EGjika/Text-Analysis-NASA-dataset
data("stop_words")
head(stop_words)
# or we may use
stop_words$word[which(stop_words$word %in% sentiments$word)] %>% head(20)
[1] "appreciate" "appropriate" "available" "awfully"
[5] "best" "better" "clearly" "enough"
[9] "like" "liked" "reasonably" "right"
[13] "sensible" "sorry" "thank" "unfortunately"
[17] "unlikely" "useful" "welcome" "well"
World cloud
For a start we will use wordcloud to have a better view of our text.
We may apply it at “description” or even to understand which cast and
director has mostly appeared. ### For description
# Libraries used
library(wordcloud)
library(SnowballC)
#generate word cloud
set.seed(1234)
wordcloud(words = netflix_titles$description, max.words=100, random.order=FALSE, rot.per=0.40, colors=brewer.pal(8, "Dark2"))
Warning: transformation drops documentsWarning: transformation drops documents

For Cast and director
set.seed(1234)
wordcloud(words = netflix_titles$cast, max.words=100, random.order=FALSE, rot.per=0.40, colors=brewer.pal(8, "Dark2"))
Warning: transformation drops documentsWarning: transformation drops documents

set.seed(1234)
wordcloud(words = netflix_titles$director, max.words=100, random.order=FALSE, rot.per=0.40, colors=brewer.pal(8, "Dark2"))
Warning: transformation drops documentsWarning: transformation drops documents

Word Association
We may also use correlation as a statistical measure to demonstrate
whether, and how strongly, pairs of variables are related. In this case
we are looking at analyzing which words occur most often in association
with the most frequently occurring words. This script shows which words
are most frequently associated with the terms
“good”,“work”,“health”,“love”,“comedy” (corlimit = 0.3 is the lower
limit/threshold set. You can set it lower to see more words, or higher
to see less). or, even change the list of words based on theri frequency
(higher better).
library(tm)
# Build a term-document matrix
Netflx_dtm <- TermDocumentMatrix(netflix_titles$description)
# Find associations
findAssocs(Netflx_dtm, terms = c("good","work","health","love","comedy"), corlimit = 0.3)
$good
numeric(0)
$work
numeric(0)
$health
mental
0.37
$love
numeric(0)
$comedy
numeric(0)
# Find associations for words that occur at least 50 times
findAssocs(Netflx_dtm, terms = findFreqTerms(Netflx_dtm, lowfreq = 1000), corlimit = 0.3)
$and
numeric(0)
$her
numeric(0)
$his
numeric(0)
$the
numeric(0)
$from
numeric(0)
$this
documentary
0.39
$their
numeric(0)
$with
numeric(0)
$when
numeric(0)
$`for`
numeric(0)
Tokenization
Here we will start our process of tikenization for our “description”
column.
library(dplyr)
tidy_netflix_description <- netflix_titles %>%
select("show_id","description")
head(tidy_netflix_title)
Netflx<-tidy_netflix_description %>%
unnest_tokens(
output = word,
input = description,# our column of interest
token = 'words',
drop = FALSE
) %>%
ungroup()
head(Netflx,24)# show just first 24 rows of words
NA
Let’s create a frequency table of words used in “description”:
Netflx<- Netflx %>%
unnest_tokens(word, description) %>%
count(word, sort = TRUE)
Netflx
Now, you can remove the stop words from your data frame:
Netflx <- Netflx %>%
anti_join(stop_words)
Joining, by = "word"
Netflx # after removing stop_words
Next, run the following to plot the words in Netflix Description that
appear more than 50 times. You will see in this situation almost nothing
to understand so, why not filter based on the frequency. (try to change
the number of frequency to other frequencies 3000)
Netflx %>%
filter(n > 50) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n)) +
geom_col(fill = "darkred") +
theme_fivethirtyeight() +
xlab(NULL) +
ylab("Word Count") +
coord_flip() +
ggtitle("Word Usage in Netflix description more than 50 times")

Netflx %>%
filter(n > 3000) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n)) +
geom_col(fill = "darkred") +
theme_fivethirtyeight() +
xlab(NULL) +
ylab("Word Count") +
coord_flip() +
ggtitle("Word Usage in Netflix description more than 50 times")

Why not group by show_id? Let’s do it!
Netflx_s<-tidy_netflix_description %>%
unnest_tokens(
output = word,
input = description,# our column of interest
token = 'words',
drop = FALSE
) %>%
ungroup()
# get sentiment via inner join
netflix_sentiment = Netflx_s %>%
inner_join(get_sentiments("afinn")) %>%
group_by(show_id, description) %>% # group by show_id and description
dplyr::summarise(sentiment=sum(value)) %>% # value will be the numerical sentiment given and we want to group by show_id
ungroup()
Joining, by = "word"`summarise()` has grouped output by 'show_id'. You can override using the `.groups` argument.
netflix_sentiment
Next! We may filter those show_id which are in a given range of
sentiment.
netflix_sentiment%>%
filter(sentiment >=12) %>%
ggplot(aes(show_id, sentiment)) +
theme_fivethirtyeight() +
geom_col() +
xlab(NULL) +
coord_flip() +
ylab("Word Count") +
ggtitle("Sentiment greater than 12 in Netflix show_id", subtitle = "Sentiment Analysis Using NRC")

Sentiment Lexicons
There are three mostly used lexicons in R:
get_sentiments("bing")
get_sentiments("afinn")
get_sentiments("nrc")
Now we will try BING lexicon on description.
Netflx_bing <- Netflx %>%
inner_join(get_sentiments("bing"))
Joining, by = "word"
Netflx_bing
Library “yarr”
We may create some graphs here . They work better if we have also a
time variable we may use. Why not try? (exercise for you)
library(yarrr)
Loading required package: jpeg
Loading required package: BayesFactor
Loading required package: coda
Loading required package: Matrix
Attaching package: ‘Matrix’
The following objects are masked from ‘package:tidyr’:
expand, pack, unpack
************
Welcome to BayesFactor 0.9.12-4.4. If you have questions, please contact Richard Morey (richarddmorey@gmail.com).
Type BFManual() to open the manual.
************
Loading required package: circlize
========================================
circlize version 0.4.15
CRAN page: https://cran.r-project.org/package=circlize
Github page: https://github.com/jokergoo/circlize
Documentation: https://jokergoo.github.io/circlize_book/book/
If you use it in published research, please cite:
Gu, Z. circlize implements and enhances circular visualization
in R. Bioinformatics 2014.
This message can be suppressed by:
suppressPackageStartupMessages(library(circlize))
========================================
yarrr v0.1.5. Citation info at citation('yarrr'). Package guide at yarrr.guide()
Email me at Nathaniel.D.Phillips.is@gmail.com
Attaching package: ‘yarrr’
The following object is masked from ‘package:ggplot2’:
diamonds
pirateplot(formula = n ~ word + sentiment, #Formula
data = Netflx_bing, #Data frame
xlab = NULL, ylab = "Word Count", #Axis labels
main = "Lexical Diversity Netflix", #Plot title
pal = "google", #Color scheme
point.o = .2, #Points
avg.line.o = 1, #Turn on the Average/Mean line
theme = 0, #Theme
point.pch = 16, #Point `pch` type
point.cex = 1.5, #Point size
jitter.val = .1, #Turn on jitter to see the songs better
cex.lab = .9, cex.names = .7) #Axis label size

To obtain a better view we may filter some of the words with a given
number of frequency:
Netflx_bing %>%
filter(n>=1500) %>%
pirateplot(formula = n ~ word + sentiment, #Formula
xlab = NULL, ylab = "Word Count", #Axis labels
main = "Lexical Diversity Netflix", #Plot title
pal = "google", #Color scheme
point.o = .2, #Points
avg.line.o = 1, #Turn on the Average/Mean line
theme = 0, #Theme
point.pch = 16, #Point `pch` type
point.cex = 1.5, #Point size
jitter.val = .1, #Turn on jitter to see the songs better
cex.lab = .9, cex.names = .7) #Axis label size

For more reference: https://www.datacamp.com/tutorial/sentiment-analysis-R
Netflx_nrc <- Netflx %>%
inner_join(get_sentiments("nrc"))
Joining, by = "word"
Netflx_nrc
Netflx_nrc %>%
filter(n > 2000) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill=sentiment)) +
theme_fivethirtyeight() +
geom_col() +
xlab(NULL) +
coord_flip() +
ylab("Word Count") +
ggtitle("Word Usage in Netflix description", subtitle = "Sentiment Analysis Using NRC")

Produce a horizontal bar chart showing positive and negative word
usage in Netflix description using the Bing et al. sentiment
lexicon.(try to change the value 2000 and observe the words)
Netflx_bing %>%
filter(n > 2000) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill=sentiment)) +
theme_fivethirtyeight() +
geom_col() +
xlab(NULL) +
coord_flip() +
ylab("Word Count") +
ggtitle("Word Usage in Netflix description", subtitle = "Sentiment Analysis Using
Bing et al.")

Netflx_nrc %>%
filter(n > 2000) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill=sentiment)) +
theme_fivethirtyeight() +
geom_col() +
xlab(NULL) +
coord_flip() +
ylab("Word Count") +
ggtitle("Word Usage in Netflix description", subtitle = "Sentiment Analysis Using nrc")

Try to filter some of the sentiments as in the examples below. I am
interested on knowwing which words are related to the sentiment
“surprise” and then “love” in teh description of the Netflix Movies or
TV Shows.
Net_nrc_surprise <- get_sentiments("nrc") %>%
filter(sentiment == "surprise")
Net_nrc_surprise
Netflx %>%
inner_join(Net_nrc_surprise)
Joining, by = "word"
Net_nrc_love <- get_sentiments("nrc") %>%
filter(sentiment == "love")
Net_nrc_love
Netflx %>%
inner_join(Net_nrc_love)
Joining, by = "word"
Try another word and explore some relationships!! We will see how NRC
result shows for some of the sentiments and top words.
Netflx_nrc %>%
filter(n>2500) %>% # filter only those frequency greater than 2000
mutate(word = reorder(word, n)) %>%
count(word,sentiment,sort=TRUE) %>%
group_by(sentiment)%>%
top_n(n=5) %>%
ungroup() %>%
ggplot(aes(x=reorder(word,n),y=n,fill=sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~sentiment,scales="free") +
coord_flip()
Selecting by n

Comparing Sentiment Lexicons
library(stringr)
library(tidyr)
Add more filtering such as “type” because we want more info also on
the type of the title if it was a Movie or a TV show.
Netflx_type <- netflix_titles %>%
select("show_id","type","description")
head(Netflx_type)
Netflx_type <-Netflx_type %>%
unnest_tokens(word, description) %>%
anti_join(stop_words)
Joining, by = "word"
Netflx_type
NA
Let’s use AFINN lexicon to observe the sentiment of Movie and TV
shows.
Netflx_type_afinn <- Netflx_type %>%
inner_join(get_sentiments("afinn")) %>%
group_by(index = type) %>%
summarize(sentiment = sum(value)) %>% # try to get also a mean
mutate(method = "AFINN")
Joining, by = "word"
Netflx_type_afinn
Netflx_type_bing <- Netflx_type %>%
inner_join(get_sentiments("bing")) %>%
group_by(index = type) %>%
#summarize(sentiment = sum(value)) %>% # try to get also a mean
mutate(method = "BING")
Joining, by = "word"
Netflx_type_bing
Netflx_bing %>%
filter(n>2500) %>% # filter only those frequency greater than 2500
mutate(word = reorder(word, n)) %>%
count(word,sentiment,sort=TRUE) %>%
group_by(sentiment) %>%
top_n(n=10) %>%
ungroup() %>%
ggplot(aes(x=reorder(word,n),y=n,fill=sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~sentiment,scales="free") +
coord_flip()
Selecting by n

Let’s do a comparision between sentiments and lexicons for the “type”
of the Netflix: TV show or Movie
Netflx_bingnrc <- bind_rows(Netflx_type %>%
inner_join(get_sentiments("bing")) %>%
mutate(method = "Bing et al."),Netflx_type %>%
inner_join(get_sentiments("nrc") %>%
filter(sentiment %in% c("positive","negative"))) %>%
mutate(method = "NRC")) %>%
count(method, index = type, sentiment) %>%
spread(sentiment, n, fill = 0) %>%
mutate(sentiment = positive - negative)
Joining, by = "word"Joining, by = "word"
Netflx_bingnrc
Netflx_bingnrc <- bind_rows(Netflx_type %>%
inner_join(get_sentiments("bing")) %>%
mutate(method = "Bing et al."),Netflx_type %>%
inner_join(get_sentiments("nrc") %>%
filter(sentiment %in% c("positive","negative"))) %>%
mutate(method = "NRC")) %>%
count(method, index = word, sentiment) %>% # try to change index "word" or "type"
spread(sentiment, n, fill = 0) %>%
mutate(sentiment = positive - negative)
Joining, by = "word"Joining, by = "word"
Netflx_bingnrc
How much each word contributes to the overall sentiment of the
descriptions .
Netflix_bingcounts <- Netflx_bing %>%
inner_join(get_sentiments("bing")) %>%
count(word, sentiment, sort = TRUE) %>%
ungroup()
Joining, by = c("word", "sentiment")
Netflix_bingcounts
Netflix_bingcounts %>%
group_by(sentiment) %>%
#top_n(2) %>%
ungroup() %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~sentiment, scales = "free_y") +
coord_flip() +
theme_fivethirtyeight() +
ggtitle("Words' Contribution to Sentiment in Netflix description", subtitle = "Using the Bing et. al Lexicon")

Netflx_nrccounts <- Netflx_nrc %>%
inner_join(get_sentiments("nrc")) %>%
filter(sentiment %in% c("positive","negative")) %>%
count(word, sentiment, sort = TRUE) %>%
ungroup()
Joining, by = c("word", "sentiment")
Netflx_nrccounts
Netflx_nrccounts %>%
group_by(sentiment) %>%
top_n(20) %>%
ungroup() %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~sentiment, scales = "free_y") +
coord_flip() +
theme_fivethirtyeight() +
ggtitle("Words' Contribution to Sentiment in Netflix", subtitle = "Using the NRC Lexicon")
Selecting by n

More info: https://github.com/EGjika
LS0tDQp0aXRsZTogIk5ldGZsaXggVGV4dCBBbmFseXNpcyINCmF1dGhvcjogIkVyYWxkYSBHamlrYSINCmRhdGU6ICIwMyBEZWNlbWJlciAyMDIyIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQojIE5ldGZsaXggZGF0c2V0IHRleHQgYW5hbHlzaXMgey50YWJzZXR9DQoNCiMjIEludHJvZHVjdGlvbg0KSW4gdGhpcyBleGFtcGxlIHdlIGFyZSBnb2luZyB0byBkbyBzb21lIHRleHQgYW5kIHNlbnRpbWVudGFsIGFuYWx5c2lzIHdpdGggdGhlIE5ldGZsaXggZGF0YXNldC4gVGhlIGRhdGFzZXQgaGFzIGEgbG90IG9mIGluZm9ybWF0aW9uIHNvIHlvdSBtYXkgZG8gbWFueSBhbmFseXNpcyBiYXNlZCBvbiB5b3VyIHJlc2VhcmNoIHF1ZXN0aW9uIGFuZCB0YXJnZXQgdmFyaWFibGUuIA0KDQoqKkRhdGEgc291cmNlOioqIGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvc2hpdmFtYi9uZXRmbGl4LXNob3dzL2Rvd25sb2FkP2RhdGFzZXRWZXJzaW9uTnVtYmVyPTUgDQoNClRoZSBtYWluIGxpYnJhcmllcyB3ZSB3aWxsIHVzZSBhcmU6DQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGlkeXIpDQpgYGANCg0Kd2Ugd2lsbCB3b3JrIG9uIGRlc2NyaXB0aW9uIHZhcmlhYmxlIHNvIHdlIHdpbGwgc2VsZWN0IG9ubHkgdGhpcyBjb2x1bW5zIGFzIGEgc3RhcnQuDQpgYGB7cn0NCmxpYnJhcnkocmVhZHIpDQpuZXRmbGl4X3RpdGxlcyA8LSByZWFkX2NzdigibmV0ZmxpeF90aXRsZXMuY3N2IikNClZpZXcobmV0ZmxpeF90aXRsZXMpDQpgYGANCg0KRGVhbGluZyB3aXRoICoqc3RvcHdvcmRzKiogaGVscHMgYSBsb3QsIGVzcGVjaWFsbHkgd2hlbiBvdXIgdGV4dCBpbmZvcm1hdGlvbiBpcyBub3QgY2xlYXIgYW5kIG9mZmljaWFsLiBCZWxvdyB3ZSBtYXkgc2VlIHNvbWUgb2YgdGhlIHN0b3Atd29yZHMgdXNlZCBpbiBtYWluIGxpYnJhcmllcywgYnV0IHlvdSBtYXkgYWxzbyBjcmVhdGUgeW91ciBvd24gc3RvcC13b3JkIHZlY3RvciBhbmQgcmVtb3ZlIHVubmVjZXNzYXJ5IHdvcmRzIGZyb20geW91ciB0ZXh0LiBGb3IgbW9yZSBvbiB0aGlzIHJlZmVyIHRvIHRoZSB3b3JrIGRvbmUgaGVyZTogaHR0cHM6Ly9naXRodWIuY29tL0VHamlrYS9UZXh0LUFuYWx5c2lzLU5BU0EtZGF0YXNldA0KDQpgYGB7cn0NCmRhdGEoInN0b3Bfd29yZHMiKQ0KaGVhZChzdG9wX3dvcmRzKQ0KIyBvciB3ZSBtYXkgdXNlIA0Kc3RvcF93b3JkcyR3b3JkW3doaWNoKHN0b3Bfd29yZHMkd29yZCAlaW4lIHNlbnRpbWVudHMkd29yZCldICU+JSBoZWFkKDIwKQ0KYGBgDQoNCiMjIFdvcmxkIGNsb3VkDQpGb3IgYSBzdGFydCB3ZSB3aWxsIHVzZSB3b3JkY2xvdWQgdG8gaGF2ZSBhIGJldHRlciB2aWV3IG9mIG91ciB0ZXh0LiBXZSBtYXkgYXBwbHkgaXQgYXQgImRlc2NyaXB0aW9uIiBvciBldmVuIHRvIHVuZGVyc3RhbmQgd2hpY2ggY2FzdCBhbmQgZGlyZWN0b3IgaGFzIG1vc3RseSBhcHBlYXJlZC4NCiMjIyBGb3IgZGVzY3JpcHRpb24NCmBgYHtyfQ0KIyBMaWJyYXJpZXMgdXNlZA0KbGlicmFyeSh3b3JkY2xvdWQpDQpsaWJyYXJ5KFNub3diYWxsQykNCiNnZW5lcmF0ZSB3b3JkIGNsb3VkDQpzZXQuc2VlZCgxMjM0KQ0Kd29yZGNsb3VkKHdvcmRzID0gbmV0ZmxpeF90aXRsZXMkZGVzY3JpcHRpb24sIG1heC53b3Jkcz0xMDAsIHJhbmRvbS5vcmRlcj1GQUxTRSwgcm90LnBlcj0wLjQwLCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkNCmBgYA0KIyMjIEZvciBDYXN0IGFuZCBkaXJlY3Rvcg0KYGBge3J9DQpzZXQuc2VlZCgxMjM0KQ0Kd29yZGNsb3VkKHdvcmRzID0gbmV0ZmxpeF90aXRsZXMkY2FzdCwgbWF4LndvcmRzPTEwMCwgcmFuZG9tLm9yZGVyPUZBTFNFLCByb3QucGVyPTAuNDAsIGNvbG9ycz1icmV3ZXIucGFsKDgsICJEYXJrMiIpKQ0KDQpzZXQuc2VlZCgxMjM0KQ0Kd29yZGNsb3VkKHdvcmRzID0gbmV0ZmxpeF90aXRsZXMkZGlyZWN0b3IsIG1heC53b3Jkcz0xMDAsIHJhbmRvbS5vcmRlcj1GQUxTRSwgcm90LnBlcj0wLjQwLCBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkNCmBgYA0KIyMjIFdvcmQgQXNzb2NpYXRpb24NCldlIG1heSBhbHNvIHVzZSBjb3JyZWxhdGlvbiBhcyBhIHN0YXRpc3RpY2FsIG1lYXN1cmUgdG8gZGVtb25zdHJhdGUgd2hldGhlciwgYW5kIGhvdyBzdHJvbmdseSwgcGFpcnMgb2YgdmFyaWFibGVzIGFyZSByZWxhdGVkLiBJbiB0aGlzIGNhc2Ugd2UgYXJlIGxvb2tpbmcgYXQgIGFuYWx5emluZyB3aGljaCB3b3JkcyBvY2N1ciBtb3N0IG9mdGVuIGluIGFzc29jaWF0aW9uIHdpdGggdGhlIG1vc3QgZnJlcXVlbnRseSBvY2N1cnJpbmcgd29yZHMuDQpUaGlzIHNjcmlwdCBzaG93cyB3aGljaCB3b3JkcyBhcmUgbW9zdCBmcmVxdWVudGx5IGFzc29jaWF0ZWQgd2l0aCB0aGUgdGVybXMgImdvb2QiLCJ3b3JrIiwiaGVhbHRoIiwibG92ZSIsImNvbWVkeSIgKGNvcmxpbWl0ID0gMC4zIGlzIHRoZSBsb3dlciBsaW1pdC90aHJlc2hvbGQgc2V0LiBZb3UgY2FuIHNldCBpdCBsb3dlciB0byBzZWUgbW9yZSB3b3Jkcywgb3IgaGlnaGVyIHRvIHNlZSBsZXNzKS4gb3IsIGV2ZW4gY2hhbmdlIHRoZSBsaXN0IG9mIHdvcmRzIGJhc2VkIG9uIHRoZXJpIGZyZXF1ZW5jeSAoaGlnaGVyIGJldHRlcikuDQoNCmBgYHtyfQ0KbGlicmFyeSh0bSkNCiMgQnVpbGQgYSB0ZXJtLWRvY3VtZW50IG1hdHJpeA0KTmV0Zmx4X2R0bSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgobmV0ZmxpeF90aXRsZXMkZGVzY3JpcHRpb24pDQojIEZpbmQgYXNzb2NpYXRpb25zIA0KZmluZEFzc29jcyhOZXRmbHhfZHRtLCB0ZXJtcyA9IGMoImdvb2QiLCJ3b3JrIiwiaGVhbHRoIiwibG92ZSIsImNvbWVkeSIpLCBjb3JsaW1pdCA9IDAuMykJCQkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBGaW5kIGFzc29jaWF0aW9ucyBmb3Igd29yZHMgdGhhdCBvY2N1ciBhdCBsZWFzdCA1MCB0aW1lcw0KZmluZEFzc29jcyhOZXRmbHhfZHRtLCB0ZXJtcyA9IGZpbmRGcmVxVGVybXMoTmV0Zmx4X2R0bSwgbG93ZnJlcSA9IDEwMDApLCBjb3JsaW1pdCA9IDAuMykNCmBgYA0KIyMgVG9rZW5pemF0aW9uDQoNCkhlcmUgd2Ugd2lsbCBzdGFydCBvdXIgcHJvY2VzcyBvZiB0aWtlbml6YXRpb24gZm9yIG91ciAiZGVzY3JpcHRpb24iIGNvbHVtbi4NCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KdGlkeV9uZXRmbGl4X2Rlc2NyaXB0aW9uIDwtIG5ldGZsaXhfdGl0bGVzICU+JQ0Kc2VsZWN0KCJzaG93X2lkIiwiZGVzY3JpcHRpb24iKQ0KaGVhZCh0aWR5X25ldGZsaXhfdGl0bGUpDQoNCk5ldGZseDwtdGlkeV9uZXRmbGl4X2Rlc2NyaXB0aW9uICU+JQ0KICAgdW5uZXN0X3Rva2VucygNCiAgICBvdXRwdXQgPSB3b3JkLA0KICAgIGlucHV0ID0gZGVzY3JpcHRpb24sIyBvdXIgY29sdW1uIG9mIGludGVyZXN0DQogICAgdG9rZW4gPSAnd29yZHMnLA0KICAgIGRyb3AgPSBGQUxTRQ0KICApICU+JQ0KICB1bmdyb3VwKCkNCmhlYWQoTmV0Zmx4LDI0KSMgc2hvdyBqdXN0IGZpcnN0IDI0IHJvd3Mgb2Ygd29yZHMNCiANCmBgYA0KTGV0J3MgY3JlYXRlIGEgZnJlcXVlbmN5IHRhYmxlIG9mIHdvcmRzIHVzZWQgaW4gImRlc2NyaXB0aW9uIjoNCg0KYGBge3J9DQpOZXRmbHg8LSBOZXRmbHggJT4lDQp1bm5lc3RfdG9rZW5zKHdvcmQsIGRlc2NyaXB0aW9uKSAlPiUNCmNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQ0KTmV0Zmx4DQpgYGANCk5vdywgeW91IGNhbiByZW1vdmUgdGhlIHN0b3Agd29yZHMgZnJvbSB5b3VyIGRhdGEgZnJhbWU6DQpgYGB7cn0NCk5ldGZseCA8LSBOZXRmbHggJT4lDQphbnRpX2pvaW4oc3RvcF93b3JkcykNCk5ldGZseCAjIGFmdGVyIHJlbW92aW5nIHN0b3Bfd29yZHMNCmBgYA0KTmV4dCwgcnVuIHRoZSBmb2xsb3dpbmcgdG8gcGxvdCB0aGUgd29yZHMgaW4gTmV0ZmxpeCBEZXNjcmlwdGlvbiB0aGF0IGFwcGVhciBtb3JlIHRoYW4gNTAgdGltZXMuIFlvdSB3aWxsIHNlZSBpbiB0aGlzIHNpdHVhdGlvbiBhbG1vc3Qgbm90aGluZyB0byB1bmRlcnN0YW5kIHNvLCB3aHkgbm90IGZpbHRlciBiYXNlZCBvbiB0aGUgZnJlcXVlbmN5LiANCih0cnkgdG8gY2hhbmdlIHRoZSBudW1iZXIgb2YgZnJlcXVlbmN5IHRvIG90aGVyIGZyZXF1ZW5jaWVzIDMwMDApDQpgYGB7cn0NCg0KTmV0Zmx4ICU+JQ0KZmlsdGVyKG4gPiA1MCkgJT4lDQptdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQ0KZ2dwbG90KGFlcyh3b3JkLCBuKSkgKw0KZ2VvbV9jb2woZmlsbCA9ICJkYXJrcmVkIikgKw0KdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKw0KeGxhYihOVUxMKSArDQp5bGFiKCJXb3JkIENvdW50IikgKw0KICBjb29yZF9mbGlwKCkgKw0KZ2d0aXRsZSgiV29yZCBVc2FnZSBpbiBOZXRmbGl4IGRlc2NyaXB0aW9uIG1vcmUgdGhhbiA1MCB0aW1lcyIpDQoNCk5ldGZseCAlPiUNCmZpbHRlcihuID4gMzAwMCkgJT4lDQptdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQ0KZ2dwbG90KGFlcyh3b3JkLCBuKSkgKw0KZ2VvbV9jb2woZmlsbCA9ICJkYXJrcmVkIikgKw0KdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKw0KeGxhYihOVUxMKSArDQp5bGFiKCJXb3JkIENvdW50IikgKw0KICBjb29yZF9mbGlwKCkgKw0KZ2d0aXRsZSgiV29yZCBVc2FnZSBpbiBOZXRmbGl4IGRlc2NyaXB0aW9uIG1vcmUgdGhhbiA1MCB0aW1lcyIpDQpgYGANCg0KV2h5IG5vdCBncm91cCBieSBzaG93X2lkPyBMZXQncyBkbyBpdCENCmBgYHtyfQ0KTmV0Zmx4X3M8LXRpZHlfbmV0ZmxpeF9kZXNjcmlwdGlvbiAlPiUNCiAgIHVubmVzdF90b2tlbnMoDQogICAgb3V0cHV0ID0gd29yZCwNCiAgICBpbnB1dCA9IGRlc2NyaXB0aW9uLCMgb3VyIGNvbHVtbiBvZiBpbnRlcmVzdA0KICAgIHRva2VuID0gJ3dvcmRzJywNCiAgICBkcm9wID0gRkFMU0UNCiAgKSAlPiUNCiAgdW5ncm91cCgpDQojIGdldCBzZW50aW1lbnQgdmlhIGlubmVyIGpvaW4NCm5ldGZsaXhfc2VudGltZW50ID0gTmV0Zmx4X3MgJT4lIA0KICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJhZmlubiIpKSAlPiUgDQogIGdyb3VwX2J5KHNob3dfaWQsIGRlc2NyaXB0aW9uKSAlPiUgIyBncm91cCBieSBzaG93X2lkIGFuZCBkZXNjcmlwdGlvbg0KICBkcGx5cjo6c3VtbWFyaXNlKHNlbnRpbWVudD1zdW0odmFsdWUpKSAlPiUgIyB2YWx1ZSB3aWxsIGJlIHRoZSBudW1lcmljYWwgc2VudGltZW50IGdpdmVuIGFuZCB3ZSB3YW50IHRvIGdyb3VwIGJ5IHNob3dfaWQNCiAgdW5ncm91cCgpDQoNCm5ldGZsaXhfc2VudGltZW50DQpgYGANCk5leHQhDQpXZSBtYXkgZmlsdGVyIHRob3NlIHNob3dfaWQgd2hpY2ggYXJlIGluIGEgZ2l2ZW4gcmFuZ2Ugb2Ygc2VudGltZW50Lg0KYGBge3J9DQpuZXRmbGl4X3NlbnRpbWVudCU+JQ0KZmlsdGVyKHNlbnRpbWVudCA+PTEyKSAlPiUNCmdncGxvdChhZXMoc2hvd19pZCwgc2VudGltZW50KSkgKw0KdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKw0KZ2VvbV9jb2woKSArDQp4bGFiKE5VTEwpICsNCmNvb3JkX2ZsaXAoKSArDQp5bGFiKCJXb3JkIENvdW50IikgKw0KZ2d0aXRsZSgiU2VudGltZW50IGdyZWF0ZXIgdGhhbiAxMiBpbiBOZXRmbGl4IHNob3dfaWQiLCBzdWJ0aXRsZSA9ICJTZW50aW1lbnQgQW5hbHlzaXMgVXNpbmcgTlJDIikNCmBgYA0KDQojIyBTZW50aW1lbnQgTGV4aWNvbnMNClRoZXJlIGFyZSB0aHJlZSBtb3N0bHkgdXNlZCBsZXhpY29ucyBpbiBSOg0KYGBge3J9DQpnZXRfc2VudGltZW50cygiYmluZyIpDQpnZXRfc2VudGltZW50cygiYWZpbm4iKQ0KZ2V0X3NlbnRpbWVudHMoIm5yYyIpDQpgYGANCk5vdyB3ZSB3aWxsIHRyeSBCSU5HIGxleGljb24gb24gZGVzY3JpcHRpb24uDQpgYGB7cn0NCk5ldGZseF9iaW5nIDwtIE5ldGZseCAlPiUNCmlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkNCk5ldGZseF9iaW5nDQpgYGANCiMjIyBMaWJyYXJ5ICJ5YXJyIg0KDQpXZSBtYXkgY3JlYXRlIHNvbWUgZ3JhcGhzIGhlcmUgLiBUaGV5IHdvcmsgYmV0dGVyIGlmIHdlIGhhdmUgYWxzbyBhIHRpbWUgdmFyaWFibGUgd2UgbWF5IHVzZS4gV2h5IG5vdCB0cnk/IChleGVyY2lzZSBmb3IgeW91KQ0KYGBge3J9DQpsaWJyYXJ5KHlhcnJyKQ0KDQpwaXJhdGVwbG90KGZvcm11bGEgPSAgbiB+IHdvcmQgKyBzZW50aW1lbnQsICNGb3JtdWxhDQogICBkYXRhID0gTmV0Zmx4X2JpbmcsICNEYXRhIGZyYW1lDQogICB4bGFiID0gTlVMTCwgeWxhYiA9ICJXb3JkIENvdW50IiwgI0F4aXMgbGFiZWxzDQogICBtYWluID0gIkxleGljYWwgRGl2ZXJzaXR5IE5ldGZsaXgiLCAjUGxvdCB0aXRsZQ0KICAgcGFsID0gImdvb2dsZSIsICNDb2xvciBzY2hlbWUNCiAgIHBvaW50Lm8gPSAuMiwgI1BvaW50cw0KICAgYXZnLmxpbmUubyA9IDEsICNUdXJuIG9uIHRoZSBBdmVyYWdlL01lYW4gbGluZQ0KICAgdGhlbWUgPSAwLCAjVGhlbWUNCiAgIHBvaW50LnBjaCA9IDE2LCAjUG9pbnQgYHBjaGAgdHlwZQ0KICAgcG9pbnQuY2V4ID0gMS41LCAjUG9pbnQgc2l6ZQ0KICAgaml0dGVyLnZhbCA9IC4xLCAjVHVybiBvbiBqaXR0ZXIgdG8gc2VlIHRoZSBzb25ncyBiZXR0ZXINCiAgIGNleC5sYWIgPSAuOSwgY2V4Lm5hbWVzID0gLjcpICNBeGlzIGxhYmVsIHNpemUNCmBgYA0KDQpUbyBvYnRhaW4gYSBiZXR0ZXIgdmlldyB3ZSBtYXkgZmlsdGVyIHNvbWUgb2YgdGhlIHdvcmRzIHdpdGggYSBnaXZlbiBudW1iZXIgb2YgZnJlcXVlbmN5Og0KDQpgYGB7cn0NCk5ldGZseF9iaW5nICU+JQ0KICBmaWx0ZXIobj49MTUwMCkgJT4lDQpwaXJhdGVwbG90KGZvcm11bGEgPSAgbiB+IHdvcmQgKyBzZW50aW1lbnQsICNGb3JtdWxhDQogICB4bGFiID0gTlVMTCwgeWxhYiA9ICJXb3JkIENvdW50IiwgI0F4aXMgbGFiZWxzDQogICBtYWluID0gIkxleGljYWwgRGl2ZXJzaXR5IE5ldGZsaXgiLCAjUGxvdCB0aXRsZQ0KICAgcGFsID0gImdvb2dsZSIsICNDb2xvciBzY2hlbWUNCiAgIHBvaW50Lm8gPSAuMiwgI1BvaW50cw0KICAgYXZnLmxpbmUubyA9IDEsICNUdXJuIG9uIHRoZSBBdmVyYWdlL01lYW4gbGluZQ0KICAgdGhlbWUgPSAwLCAjVGhlbWUNCiAgIHBvaW50LnBjaCA9IDE2LCAjUG9pbnQgYHBjaGAgdHlwZQ0KICAgcG9pbnQuY2V4ID0gMS41LCAjUG9pbnQgc2l6ZQ0KICAgaml0dGVyLnZhbCA9IC4xLCAjVHVybiBvbiBqaXR0ZXIgdG8gc2VlIHRoZSBzb25ncyBiZXR0ZXINCiAgIGNleC5sYWIgPSAuOSwgY2V4Lm5hbWVzID0gLjcpICNBeGlzIGxhYmVsIHNpemUNCmBgYA0KRm9yIG1vcmUgcmVmZXJlbmNlOiBodHRwczovL3d3dy5kYXRhY2FtcC5jb20vdHV0b3JpYWwvc2VudGltZW50LWFuYWx5c2lzLVINCg0KDQpgYGB7cn0NCk5ldGZseF9ucmMgPC0gTmV0Zmx4ICU+JQ0KaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikpDQpOZXRmbHhfbnJjDQpgYGANCg0KYGBge3J9DQpOZXRmbHhfbnJjICU+JQ0KZmlsdGVyKG4gPiAyMDAwKSAlPiUNCm11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lDQpnZ3Bsb3QoYWVzKHdvcmQsIG4sIGZpbGw9c2VudGltZW50KSkgKw0KdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKw0KZ2VvbV9jb2woKSArDQp4bGFiKE5VTEwpICsNCmNvb3JkX2ZsaXAoKSArDQp5bGFiKCJXb3JkIENvdW50IikgKw0KZ2d0aXRsZSgiV29yZCBVc2FnZSBpbiBOZXRmbGl4IGRlc2NyaXB0aW9uIiwgc3VidGl0bGUgPSAiU2VudGltZW50IEFuYWx5c2lzIFVzaW5nIE5SQyIpDQpgYGANCg0KUHJvZHVjZSBhIGhvcml6b250YWwgYmFyIGNoYXJ0IHNob3dpbmcgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHdvcmQgdXNhZ2UgaW4gTmV0ZmxpeCBkZXNjcmlwdGlvbiB1c2luZyB0aGUgQmluZyBldCBhbC4gc2VudGltZW50IGxleGljb24uKHRyeSB0byBjaGFuZ2UgdGhlIHZhbHVlIDIwMDAgYW5kIG9ic2VydmUgdGhlIHdvcmRzKQ0KYGBge3J9DQpOZXRmbHhfYmluZyAlPiUNCmZpbHRlcihuID4gMjAwMCkgJT4lDQptdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQ0KZ2dwbG90KGFlcyh3b3JkLCBuLCBmaWxsPXNlbnRpbWVudCkpICsNCnRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsNCmdlb21fY29sKCkgKw0KeGxhYihOVUxMKSArDQpjb29yZF9mbGlwKCkgKw0KeWxhYigiV29yZCBDb3VudCIpICsNCmdndGl0bGUoIldvcmQgVXNhZ2UgaW4gTmV0ZmxpeCBkZXNjcmlwdGlvbiIsIHN1YnRpdGxlID0gIlNlbnRpbWVudCBBbmFseXNpcyBVc2luZw0KQmluZyBldCBhbC4iKQ0KYGBgDQoNCmBgYHtyfQ0KTmV0Zmx4X25yYyAlPiUNCmZpbHRlcihuID4gMjAwMCkgJT4lDQptdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQ0KZ2dwbG90KGFlcyh3b3JkLCBuLCBmaWxsPXNlbnRpbWVudCkpICsNCnRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsNCmdlb21fY29sKCkgKw0KeGxhYihOVUxMKSArDQpjb29yZF9mbGlwKCkgKw0KeWxhYigiV29yZCBDb3VudCIpICsNCmdndGl0bGUoIldvcmQgVXNhZ2UgaW4gTmV0ZmxpeCBkZXNjcmlwdGlvbiIsIHN1YnRpdGxlID0gIlNlbnRpbWVudCBBbmFseXNpcyBVc2luZyBucmMiKQ0KYGBgDQoNClRyeSB0byBmaWx0ZXIgc29tZSBvZiB0aGUgc2VudGltZW50cyBhcyBpbiB0aGUgZXhhbXBsZXMgYmVsb3cuIA0KSSBhbSBpbnRlcmVzdGVkIG9uIGtub3d3aW5nIHdoaWNoIHdvcmRzIGFyZSByZWxhdGVkIHRvIHRoZSBzZW50aW1lbnQgInN1cnByaXNlIiBhbmQgdGhlbiAibG92ZSIgaW4gdGVoIGRlc2NyaXB0aW9uIG9mIHRoZSBOZXRmbGl4IE1vdmllcyBvciBUViBTaG93cy4NCmBgYHtyfQ0KTmV0X25yY19zdXJwcmlzZSA8LSBnZXRfc2VudGltZW50cygibnJjIikgJT4lDQpmaWx0ZXIoc2VudGltZW50ID09ICJzdXJwcmlzZSIpDQpOZXRfbnJjX3N1cnByaXNlDQpOZXRmbHggJT4lDQppbm5lcl9qb2luKE5ldF9ucmNfc3VycHJpc2UpDQoNCg0KDQpOZXRfbnJjX2xvdmUgPC0gZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JQ0KZmlsdGVyKHNlbnRpbWVudCA9PSAibG92ZSIpDQpOZXRfbnJjX2xvdmUNCk5ldGZseCAlPiUNCmlubmVyX2pvaW4oTmV0X25yY19sb3ZlKQ0KYGBgDQpUcnkgYW5vdGhlciB3b3JkIGFuZCBleHBsb3JlIHNvbWUgcmVsYXRpb25zaGlwcyEhDQpXZSB3aWxsIHNlZSBob3cgTlJDIHJlc3VsdCBzaG93cyBmb3Igc29tZSBvZiB0aGUgc2VudGltZW50cyBhbmQgdG9wIHdvcmRzLg0KDQpgYGB7cn0NCk5ldGZseF9ucmMgJT4lIA0KICBmaWx0ZXIobj4yNTAwKSAlPiUgIyAgZmlsdGVyIG9ubHkgdGhvc2UgZnJlcXVlbmN5IGdyZWF0ZXIgdGhhbiAyMDAwDQptdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQ0KY291bnQod29yZCxzZW50aW1lbnQsc29ydD1UUlVFKSAlPiUNCmdyb3VwX2J5KHNlbnRpbWVudCklPiUNCiAgdG9wX24obj01KSAlPiUgDQp1bmdyb3VwKCkgJT4lDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKHdvcmQsbikseT1uLGZpbGw9c2VudGltZW50KSkgKyANCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyANCiAgZmFjZXRfd3JhcCh+c2VudGltZW50LHNjYWxlcz0iZnJlZSIpICsgDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCg0KDQoNCiMjIENvbXBhcmluZyBTZW50aW1lbnQgTGV4aWNvbnMNCmBgYHtyfQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeSh0aWR5cikNCmBgYA0KDQpBZGQgbW9yZSBmaWx0ZXJpbmcgc3VjaCBhcyAidHlwZSIgYmVjYXVzZSB3ZSB3YW50IG1vcmUgaW5mbyBhbHNvIG9uIHRoZSB0eXBlIG9mIHRoZSB0aXRsZSBpZiBpdCB3YXMgYSBNb3ZpZSBvciBhIFRWIHNob3cuDQpgYGB7cn0NCk5ldGZseF90eXBlIDwtIG5ldGZsaXhfdGl0bGVzICU+JQ0Kc2VsZWN0KCJzaG93X2lkIiwidHlwZSIsImRlc2NyaXB0aW9uIikNCmhlYWQoTmV0Zmx4X3R5cGUpDQoNCk5ldGZseF90eXBlIDwtTmV0Zmx4X3R5cGUgJT4lDQp1bm5lc3RfdG9rZW5zKHdvcmQsIGRlc2NyaXB0aW9uKSAlPiUNCmFudGlfam9pbihzdG9wX3dvcmRzKQ0KDQpOZXRmbHhfdHlwZQ0KDQpgYGANCkxldCdzIHVzZSBBRklOTiBsZXhpY29uIHRvICBvYnNlcnZlIHRoZSBzZW50aW1lbnQgb2YgTW92aWUgYW5kIFRWIHNob3dzLg0KYGBge3J9DQpOZXRmbHhfdHlwZV9hZmlubiA8LSBOZXRmbHhfdHlwZSAlPiUNCmlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImFmaW5uIikpICU+JQ0KICBncm91cF9ieShpbmRleCA9IHR5cGUpICU+JQ0Kc3VtbWFyaXplKHNlbnRpbWVudCA9IHN1bSh2YWx1ZSkpICU+JSAjIHRyeSB0byBnZXQgYWxzbyBhIG1lYW4gDQptdXRhdGUobWV0aG9kID0gIkFGSU5OIikNCk5ldGZseF90eXBlX2FmaW5uDQpgYGANCg0KYGBge3J9DQpOZXRmbHhfdHlwZV9iaW5nIDwtIE5ldGZseF90eXBlICU+JQ0KaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKSAlPiUNCiAgZ3JvdXBfYnkoaW5kZXggPSB0eXBlKSAlPiUNCiNzdW1tYXJpemUoc2VudGltZW50ID0gc3VtKHZhbHVlKSkgJT4lICMgdHJ5IHRvIGdldCBhbHNvIGEgbWVhbiANCm11dGF0ZShtZXRob2QgPSAiQklORyIpDQpOZXRmbHhfdHlwZV9iaW5nDQpgYGANCg0KYGBge3J9DQpOZXRmbHhfYmluZyAlPiUgDQogIGZpbHRlcihuPjI1MDApICU+JSAjICBmaWx0ZXIgb25seSB0aG9zZSBmcmVxdWVuY3kgZ3JlYXRlciB0aGFuIDI1MDANCm11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lDQpjb3VudCh3b3JkLHNlbnRpbWVudCxzb3J0PVRSVUUpICU+JQ0KZ3JvdXBfYnkoc2VudGltZW50KSAlPiUgDQogIHRvcF9uKG49MTApICU+JSANCnVuZ3JvdXAoKSAlPiUNCiAgZ2dwbG90KGFlcyh4PXJlb3JkZXIod29yZCxuKSx5PW4sZmlsbD1zZW50aW1lbnQpKSArIA0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArIA0KICBmYWNldF93cmFwKH5zZW50aW1lbnQsc2NhbGVzPSJmcmVlIikgKyANCiAgY29vcmRfZmxpcCgpDQpgYGANCg0KDQpMZXQncyBkbyBhIGNvbXBhcmlzaW9uIGJldHdlZW4gc2VudGltZW50cyBhbmQgbGV4aWNvbnMgZm9yIHRoZSAidHlwZSIgb2YgdGhlIE5ldGZsaXg6IFRWIHNob3cgb3IgTW92aWUNCg0KYGBge3J9DQpOZXRmbHhfYmluZ25yYyA8LSBiaW5kX3Jvd3MoTmV0Zmx4X3R5cGUgJT4lDQppbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JQ0KbXV0YXRlKG1ldGhvZCA9ICJCaW5nIGV0IGFsLiIpLE5ldGZseF90eXBlICU+JQ0KaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikgJT4lDQpmaWx0ZXIoc2VudGltZW50ICVpbiUgYygicG9zaXRpdmUiLCJuZWdhdGl2ZSIpKSkgJT4lDQptdXRhdGUobWV0aG9kID0gIk5SQyIpKSAlPiUNCmNvdW50KG1ldGhvZCwgaW5kZXggPSB0eXBlLCBzZW50aW1lbnQpICU+JQ0Kc3ByZWFkKHNlbnRpbWVudCwgbiwgZmlsbCA9IDApICU+JQ0KbXV0YXRlKHNlbnRpbWVudCA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpDQpOZXRmbHhfYmluZ25yYw0KYGBgDQoNCmBgYHtyfQ0KTmV0Zmx4X2JpbmducmMgPC0gYmluZF9yb3dzKE5ldGZseF90eXBlICU+JQ0KaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKSAlPiUNCm11dGF0ZShtZXRob2QgPSAiQmluZyBldCBhbC4iKSxOZXRmbHhfdHlwZSAlPiUNCmlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JQ0KZmlsdGVyKHNlbnRpbWVudCAlaW4lIGMoInBvc2l0aXZlIiwibmVnYXRpdmUiKSkpICU+JQ0KbXV0YXRlKG1ldGhvZCA9ICJOUkMiKSkgJT4lDQpjb3VudChtZXRob2QsIGluZGV4ID0gd29yZCwgc2VudGltZW50KSAlPiUgIyB0cnkgdG8gY2hhbmdlIGluZGV4ICJ3b3JkIiBvciAidHlwZSINCnNwcmVhZChzZW50aW1lbnQsIG4sIGZpbGwgPSAwKSAlPiUNCm11dGF0ZShzZW50aW1lbnQgPSBwb3NpdGl2ZSAtIG5lZ2F0aXZlKQ0KTmV0Zmx4X2JpbmducmMNCmBgYA0KDQpIb3cgbXVjaCBlYWNoIHdvcmQgY29udHJpYnV0ZXMgdG8gdGhlIG92ZXJhbGwgc2VudGltZW50IG9mIHRoZSBkZXNjcmlwdGlvbnMgLg0KYGBge3J9DQpOZXRmbGl4X2Jpbmdjb3VudHMgPC0gTmV0Zmx4X2JpbmcgJT4lDQppbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JQ0KY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkgJT4lDQp1bmdyb3VwKCkNCk5ldGZsaXhfYmluZ2NvdW50cw0KYGBgDQoNCmBgYHtyfQ0KTmV0ZmxpeF9iaW5nY291bnRzICU+JQ0KIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lDQojdG9wX24oMikgJT4lDQp1bmdyb3VwKCkgJT4lDQptdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQ0KZ2dwbG90KGFlcyh3b3JkLCBuLCBmaWxsID0gc2VudGltZW50KSkgKw0KZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KZmFjZXRfd3JhcCh+c2VudGltZW50LCBzY2FsZXMgPSAiZnJlZV95IikgKw0KY29vcmRfZmxpcCgpICsNCnRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsNCmdndGl0bGUoIldvcmRzJyBDb250cmlidXRpb24gdG8gU2VudGltZW50IGluIE5ldGZsaXggZGVzY3JpcHRpb24iLCBzdWJ0aXRsZSA9ICJVc2luZyB0aGUgQmluZyBldC4gYWwgTGV4aWNvbiIpDQpgYGANCg0KYGBge3J9DQpOZXRmbHhfbnJjY291bnRzIDwtIE5ldGZseF9ucmMgJT4lDQppbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkgJT4lDQpmaWx0ZXIoc2VudGltZW50ICVpbiUgYygicG9zaXRpdmUiLCJuZWdhdGl2ZSIpKSAlPiUNCmNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQ0KdW5ncm91cCgpDQpOZXRmbHhfbnJjY291bnRzDQpgYGANCg0KYGBge3J9DQpOZXRmbHhfbnJjY291bnRzICU+JQ0KZ3JvdXBfYnkoc2VudGltZW50KSAlPiUNCnRvcF9uKDIwKSAlPiUNCnVuZ3JvdXAoKSAlPiUNCm11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lDQpnZ3Bsb3QoYWVzKHdvcmQsIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArDQpnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQpmYWNldF93cmFwKH5zZW50aW1lbnQsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQpjb29yZF9mbGlwKCkgKw0KdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKw0KZ2d0aXRsZSgiV29yZHMnIENvbnRyaWJ1dGlvbiB0byBTZW50aW1lbnQgaW4gTmV0ZmxpeCIsIHN1YnRpdGxlID0gIlVzaW5nIHRoZSBOUkMgTGV4aWNvbiIpDQpgYGANCg0KDQpNb3JlIGluZm86IGh0dHBzOi8vZ2l0aHViLmNvbS9FR2ppa2EgDQoNCg0KDQoNCg0K