Agenda
- Regular expressions
- Basic text analysis
- corpora
- word clouds
- sentiment analysis
- Intermediate
- term frequency x inverse document frequency (tf-idf)
- Latent Dirichlet allocation (LDA; topic modeling)
Announcements
- Final Project update
- MDSR Ch 15 programming notebook assigned
- Beckman’s office hour cancelled on Thurs 4/4 (see syllabus for other options)
- MDSR Ch 15 Exercises due 4/14 before midnight
- Lots of errata/tips this chapter
MDSR Ch 15 Errata / Tips
- Some sections don’t require programming, but please still include the headers for navigation purposes
- p. 361: Note–
data("DataSciencePapers")
should appear BOTH in front matter (per style guide) AND as a code chunk in sequence with the other MDSR code so your section 15.2 results in the programming notebook will match the MDSR book rather than using new data queried from arXiv also loaded on p. 361 under the object name DataSciencePapers
- p. 365: your word cloud won’t be identical and may even exclude “data” if it doesn’t fit on the page
- p. 368: Wikipedia changed the tables all around. Use
Table[[4]]
when scraping; Title
is now Song
; the results won’t exactly match but you’ll be able to work it out.
- p. 369: change
25
to 15
(the result is quite a different message!)
- p. 370: you don’t need a Twitter account, the provided credentials work… here they are:
- consumer_key = “u2UthjbK6YHyQSp4sPk6yjsuV”
- consumer_secret = “sC4mjd2WME5nH1FoWeSTuSy7JCP5DHjNtTYU1X6BwQ1vPZ0j3v”
- access_token = “1365606414-7vPfPxStYNq6kWEATQlT8HZBd4G83BBcX4VoS9T”
- access_secret = “0hJq9KYC3eBRuZzJqSacmtJ4PNJ7tNLkGrQrVl00JHirs”
- p. 372: you’ll need to load the
RSQLite
library
- p. 373: you can skip the
geocode(...)
function from the ggmap
package and just assign lon
and lat
directly. You can even look up the coordinates for State College and substitute that if you like. Oddly enough, you might have to give a credit card number to Google if you want to use geocode()
so feel free to modify your code to avoid that step.
Project Gutenberg
- https://www.gutenberg.org/
- Project Gutenberg is a massive free online library
- full-text of almost 60,000 free ebooks (e.g., expired copyright)
- Jane Austin
- Charles Dickens
- Mark Twain
- Arthur Conan Doyle
- Oscar Wilde
- Bill Shakespeare
- (and loads more…)
- New on 3/29/2019–The American Bee Journal!
Macbeth Summary
Our “muse” for this first portion is the famous play MacBeth by William Shakespeare. If you aren’t familiar with the play, here is a quick summary:
Macbeth text data intake
- Pretty fast!
- How does it look?
- Q: Look closely, Can you read this?
- Q: How can we make the text more understandable?
macbeth_url <- "http://www.gutenberg.org/cache/epub/1129/pg1129.txt"
Macbeth_raw <- RCurl::getURL(macbeth_url)
# Macbeth_raw
Text as data
- Humans:
- excellent at understanding text
- not as good at storing text
- Computers:
- excellent at storing text
- not as good at understanding text
- Human + Computer??
- excellent at understanding & storing text?
- not so good at storing & understanding text?
- (depends on the human & the computer…?)
Back to Macbeth
- We need to split the one giant string of
Macbeth_raw
everywhere that we see the pattern \r\n
(marking end of line)
- Q: What class of object do we have now?
- Q: Is it better?
macbeth_tmp <- strsplit(Macbeth_raw, "\r\n")
str(macbeth_tmp)
List of 1
$ : chr [1:3193] "This Etext file is presented by Project Gutenberg, in" "cooperation with World Library, Inc., from their Library of the" "Future and Shakespeare CDROMS. Project Gutenberg often releases" "Etexts that are NOT placed in the Public Domain!!" ...
macbeth <- strsplit(Macbeth_raw, "\r\n")[[1]]
length(macbeth)
[1] 3193
head(macbeth)
[1] "This Etext file is presented by Project Gutenberg, in"
[2] "cooperation with World Library, Inc., from their Library of the"
[3] "Future and Shakespeare CDROMS. Project Gutenberg often releases"
[4] "Etexts that are NOT placed in the Public Domain!!"
[5] ""
[6] "*This Etext has certain copyright implications you should read!*"
The set up…
- Let’s say you enrolled in THEA 102 for a GA credit: Fundamentals of Acting
- The class will choose a play by Shakespeare and then everyone will volunteer for the various parts.
- There’s someone in the class you want to impress
- You want a part that is important,
- but you don’t want too many lines to memorize…
Manual text analysis
- How would you count up the lines if you just have a physical copy of the play?
- Q: For one character?
- Q: For all characters?
- Q: For all 37 of Shakespeare’s plays?
macbeth[295:310]
[1] ""
[2] "SCENE II."
[3] "A camp near Forres. Alarum within."
[4] ""
[5] "Enter Duncan, Malcolm, Donalbain, Lennox, with Attendants,"
[6] "meeting a bleeding Sergeant."
[7] ""
[8] " DUNCAN. What bloody man is that? He can report,"
[9] " As seemeth by his plight, of the revolt"
[10] " The newest state."
[11] " MALCOLM. This is the sergeant"
[12] " Who like a good and hardy soldier fought"
[13] " 'Gainst my captivity. Hail, brave friend!"
[14] " Say to the King the knowledge of the broil"
[15] " As thou didst leave it."
[16] " SERGEANT. Doubtful it stood,"
Manual text analysis
- If you had to do it manually, you’d probably flip through the pages and make a tally (on a separate sheet) for each line
- Now that you know you can find Shakespeare’s plays through Project Gutenberg, you can probably access ebooks for all of them
- Q: What’s the approach?
- Q: Scrolling instead of “flipping” but otherwise same?
- Q: Something else?
Regular Expressions (RegEx)
- Humans are good at understanding text, but bad at storing lots of text and slow at searching
- Computers are good at storing & searching, but not good at “understanding” it.
- Regular expressions
- Computer stores the text (good at this)
- Human identifies pattern (good at this)
- Human translates pattern so computer can search for it (takes practice…)
- Computer searches the text (good at this)
- RStudio Cheatsheets: https://www.rstudio.com/resources/cheatsheets/
grep( )
& grepl( )
- these functions look for the needle (pattern) in the haystack (character vector)
- needle/pattern:
MACBETH"
with two leading spaces
- haystack/text:
macbeth
character vector object
- Q: What’s returned in each case?
- Are these Macbeth’s lines?
- All mentions of Macbeths’ name?
- Are we done?
- Q: How are we doing?
- Can we improve it further?
- Compare with Ctrl + F on the website?
macbeth_lines <- grep("MACBETH", macbeth)
# macbeth_lines <- grep("MACBETH", macbeth, value = TRUE)
# macbeth_lines <- grepl("MACBETH", macbeth)
length(macbeth_lines)
[1] 208
head(macbeth_lines)
[1] 218 228 230 433 443 466
identical(c(1:3), c(1L, 2L, 3L)) # are these the same?
identical(c(1:3), c(1, 2, 3)) # `identical` is PICKY
# are these the same?
identical(macbeth[grep("MACBETH", macbeth)],
macbeth[grepl("MACBETH", macbeth)])
Refining our RegEx
- How do you identify a speaker’s lines? (Humans are good at this, not computers)
- All CAPITAL LETTERS
- Two leading spaces
- ends with period
- Q: Close, but what’s wrong here?
macbeth_lines <- grep(" MACBETH.", macbeth, value = TRUE)
length(macbeth_lines)
[1] 147
head(macbeth_lines)
[1] " MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[2] " MACBETH. So foul and fair a day I have not seen."
[3] " MACBETH. Speak, if you can. What are you?"
[4] " MACBETH. Stay, you imperfect speakers, tell me more."
[5] " MACBETH. Into the air, and what seem'd corporal melted"
[6] " MACBETH. Your children shall be kings."
Problem with period (.
)
- In RegEx, the period is a a metacharacter that matches ANY character
\\.
is required to literally search for periods in our RegEx pattern
\\
is called an “escape”
- escape is required for any part of a RegEx that has special meaning, but you just want to search literally
# anything with "MAC" and then another character
grep("MAC.", macbeth, value = TRUE) %>%
head()
[1] "MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES"
[2] "MACHINE READABLE COPIES OF THIS ETEXT, SO LONG AS SUCH COPIES"
[3] "WITH PERMISSION. ELECTRONIC AND MACHINE READABLE COPIES MAY BE"
[4] "THE TRAGEDY OF MACBETH"
[5] " MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[6] " LADY MACBETH, his wife"
# MACBETH.
grep("MACBETH\\.", macbeth, value = TRUE) %>% head(20)
[1] " MACBETH. So foul and fair a day I have not seen."
[2] " MACBETH. Speak, if you can. What are you?"
[3] " MACBETH. Stay, you imperfect speakers, tell me more."
[4] " MACBETH. Into the air, and what seem'd corporal melted"
[5] " MACBETH. Your children shall be kings."
[6] " MACBETH. And Thane of Cawdor too. Went it not so?"
[7] " MACBETH. The Thane of Cawdor lives. Why do you dress me"
[8] " MACBETH. [Aside.] Glamis, and Thane of Cawdor!"
[9] " MACBETH. [Aside.] Two truths are told,"
[10] " MACBETH. [Aside.] If chance will have me King, why, chance may"
[11] " MACBETH. [Aside.] Come what come may,"
[12] " MACBETH. Give me your favor; my dull brain was wrought"
[13] " MACBETH. Till then, enough. Come, friends. Exeunt."
[14] " MACBETH. The service and the loyalty lowe,"
[15] " MACBETH. The rest is labor, which is not used for you."
[16] " MACBETH. [Aside.] The Prince of Cumberland! That is a step"
[17] " LADY MACBETH. \"They met me in the day of success, and I have"
[18] " LADY MACBETH. Thou'rt mad to say it!"
[19] " LADY MACBETH. Give him tending;"
[20] " MACBETH. My dearest love,"
Simple RegEx Tools
|
: alternation–search for a few specific alternatives
"MAC[B|D]"
would match all strings that include “MACB” OR “MACD”
[ ]
: character sets–square brackets define sets of characters to match
"MAC[C-Z]"
would match “MAC” followed by any capital letter from “C” through “Z”
^
or $
: anchors–search for pattern strictly at the beginning (^
) or end ($
) of string
"^ACT"
would match all lines strictly beginning with “ACT”
"MACBETH$"
would match all lines strictly ending with “MACBETH” (no punctuation)
?
or *
or +
: repetitions
"^ ?MAC"
would match if string begins with zero or one leading spaces followed by “MAC”
"^ *MAC"
would match if string begins with zero or more leading spaces followed by “MAC”
"^ +MAC"
would match if string begins with one or more leading spaces followed by “MAC”
"^ {3}MAC"
would match if string begins with exactly 3 leading spaces followed by “MAC”
# alternation with `|`
grep("MAC[B|D]", macbeth, value = TRUE) %>% head()
[1] "THE TRAGEDY OF MACBETH"
[2] " MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[3] " LADY MACBETH, his wife"
[4] " MACDUFF, Thane of Fife, a nobleman of Scotland"
[5] " LADY MACDUFF, his wife"
[6] " MACBETH. So foul and fair a day I have not seen."
# "MAC" followed by any capital letter from "C" through "Z"
grep("MAC[C-Z]", macbeth, value = TRUE) %>% head(10)
[1] "MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES"
[2] "MACHINE READABLE COPIES OF THIS ETEXT, SO LONG AS SUCH COPIES"
[3] "WITH PERMISSION. ELECTRONIC AND MACHINE READABLE COPIES MAY BE"
[4] " MACDUFF, Thane of Fife, a nobleman of Scotland"
[5] " LADY MACDUFF, his wife"
[6] "WITH PERMISSION. ELECTRONIC AND MACHINE READABLE COPIES MAY BE"
[7] "WITH PERMISSION. ELECTRONIC AND MACHINE READABLE COPIES MAY BE"
[8] " MACDUFF. Was it so late, friend, ere you went to bed,"
[9] " MACDUFF. What three things does drink especially provoke?"
[10] " MACDUFF. I believe drink gave thee the lie last night."
# search for beginning of each act in the play (`^` goea at beginning)
grep("ACT", macbeth, value = TRUE) %>% head(10)
[1] "BREACH OF WARRANTY OR CONTRACT, INCLUDING BUT NOT LIMITED TO"
[2] "ACT I. SCENE I."
[3] "ACT II. SCENE I."
[4] "ACT III. SCENE I."
[5] "ACT IV. SCENE I."
[6] "ACT V. SCENE I."
grep("^ACT", macbeth, value = TRUE) %>% head(10)
[1] "ACT I. SCENE I." "ACT II. SCENE I." "ACT III. SCENE I." "ACT IV. SCENE I." "ACT V. SCENE I."
# strings strictly ending in "MACBETH" (`$` goes at end)
grep("MACBETH$", macbeth, value = TRUE) %>% head(10)
[1] "THE TRAGEDY OF MACBETH"
# repetitions
grep("^ ?MAC", macbeth, value = TRUE) %>% head() # zero or one leading spaces
[1] "MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES"
[2] "MACHINE READABLE COPIES OF THIS ETEXT, SO LONG AS SUCH COPIES"
grep("^ *MAC", macbeth, value = TRUE) %>% head() # zero or more leading spaces
[1] "MACHINE READABLE COPIES MAY BE DISTRIBUTED SO LONG AS SUCH COPIES"
[2] "MACHINE READABLE COPIES OF THIS ETEXT, SO LONG AS SUCH COPIES"
[3] " MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[4] " MACDUFF, Thane of Fife, a nobleman of Scotland"
[5] " MACBETH. So foul and fair a day I have not seen."
[6] " MACBETH. Speak, if you can. What are you?"
grep("^ +MAC", macbeth, value = TRUE) %>% head() # one or more leading spaces
[1] " MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[2] " MACDUFF, Thane of Fife, a nobleman of Scotland"
[3] " MACBETH. So foul and fair a day I have not seen."
[4] " MACBETH. Speak, if you can. What are you?"
[5] " MACBETH. Stay, you imperfect speakers, tell me more."
[6] " MACBETH. Into the air, and what seem'd corporal melted"
grep("^ {2}MAC", macbeth, value = TRUE) %>% head() # exaclty two leading spaces
[1] " MACBETH, Thane of Glamis and Cawdor, a general in the King's"
[2] " MACDUFF, Thane of Fife, a nobleman of Scotland"
[3] " MACBETH. So foul and fair a day I have not seen."
[4] " MACBETH. Speak, if you can. What are you?"
[5] " MACBETH. Stay, you imperfect speakers, tell me more."
[6] " MACBETH. Into the air, and what seem'd corporal melted"
grep("^ {3}MAC", macbeth, value = TRUE) %>% head() # exactly three leading spaces
character(0)
Speaker frequency in Macbeth
- recall: you wanted an important character, but not a huge number of lines to memorize
- want a decent number of lines
- “important” could mean involved for most of the play (not killed immediately)
- Q: Who meets our criteria?
Macbeth <- grepl(" MACBETH\\.", macbeth)
Macduff <- grepl(" MACDUFF\\.", macbeth)
LadyMacbeth <- grepl(" LADY MACBETH\\.", macbeth)
LadyMacduff <- grepl(" LADY MACDUFF\\.", macbeth)
Banquo <- grepl(" BANQUO\\.", macbeth)
Duncan <- grepl(" DUNCAN\\.", macbeth)
speaker_freq <- data.frame(Macbeth, Macduff, LadyMacbeth, LadyMacduff, Banquo, Duncan) %>%
mutate(line = 1:length(macbeth)) %>%
gather(key = "character", value = "speak", -line) %>%
mutate(speak = as.numeric(speak)) %>%
filter(line > 218 & line < 3172)
glimpse(speaker_freq)
Observations: 17,718
Variables: 3
$ line [3m[38;5;246m<int>[39m[23m 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,…
$ character [3m[38;5;246m<chr>[39m[23m "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", "Macbeth", …
$ speak [3m[38;5;246m<dbl>[39m[23m 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
# find the acts that subdivide the play
acts_idx <- grep("^ACT ", macbeth)
acts_labels <- str_extract(macbeth[acts_idx], "^ACT [I|V]+") # [I|V]+ I, II, III, IV, V
acts <- data.frame(line = acts_idx, labels = acts_labels)
# plot speaker frequencies
speaker_freq %>%
ggplot(aes(x = line, y = speak)) +
geom_smooth(aes(color = character), method = "loess", se = 0) +
geom_vline(xintercept = acts_idx, color = "darkgray", lty = 3) +
geom_text(data = acts, aes(y = 0.085, label = labels),
hjust = "left", color = "darkgray") +
ylim(c(0, NA)) +
xlab("Line Number") +
ylab("Proportion of Speeches")

Further Analysis
- Getting closer… but we should probably open up the search a little
- Problem: We really only grabbed the first part of each line…
- What if we accidentally choose a character prone to long speeches?
- Might be too much to memorize… we need better information
- Q: How do you suggest we access entire lines for each part in the play?
- How would you do it “by eye”?
- How can you translate that into RegEx?
Indentifying lines & speakers
- Q: What does this RegEx pattern get for us?
"\r\n {2}[A-Z| |0-9]*\\. "
- Q: What’s the difference between the result for
strsplit
stringr::extract
stringr::extract_all
- Q: Why
[-1]
at the end of strsplit
command?
lines <- strsplit(Macbeth_raw, "\r\n {2}[A-Z| |0-9]*\\. ")[[1]][-1]
speakers <- stringr::str_extract_all(Macbeth_raw, " {2}[A-Z| |0-9]*\\. ")[[1]]
AllMacbeth <- data.frame(speakers, lines)
Clean up the lines
- Q: What’s left to clean up?
head(AllMacbeth)
Clean up the lines
gsub
is handy for “find & replace”
- if you want to simply cut some pattern out of the text
- find it with the proper RegEx
- replace with “” (that’s a pattern with nothing in it!)
AllMacbeth <-
AllMacbeth %>%
mutate(lines = gsub(pattern = "[\r|\n]*", replacement = "", x = lines))
head(AllMacbeth)
More clean up
- The end of each Act seems to include something like the following text…
<<THIS ELECTRONIC VERSION OF THE COMPLETE WORKS OF WILLIAM
SHAKESPEARE IS COPYRIGHT 1990-1993 BY WORLD LIBRARY, INC., AND IS
PROVIDED BY PROJECT GUTENBERG ETEXT OF CARNEGIE MELLON UNIVERSITY
WITH PERMISSION. ELECTRONIC AND MACHINE READABLE COPIES MAY BE
DISTRIBUTED SO LONG AS SUCH COPIES (1) ARE FOR YOUR OR OTHERS
PERSONAL USE ONLY, AND (2) ARE NOT DISTRIBUTED OR USED
COMMERCIALLY. PROHIBITED COMMERCIAL DISTRIBUTION INCLUDES BY ANY
SERVICE THAT CHARGES FOR DOWNLOAD TIME OR FOR MEMBERSHIP.>>
- Q: Where do you think that “junk” landed due to our approach so far?
- Q: What should we do?
- Q: Is there more “junk” like this?
Another gsub
- we’re not going to clean up everything, but we’ll do this one more…
- Q: Consider RegEx pattern:
"<<.*>>"
- What’s it do?
- Do you think this will work?
- Do we need an escape for the special characters?
\\
junk <- grepl(pattern = "<<.*>>", x = AllMacbeth$lines)
# Before
AllMacbeth %>%
filter(junk) %>%
select(speakers, lines)
# Correction
AllMacbeth <-
AllMacbeth %>%
mutate(lines = gsub(pattern = "<<.*>>", replacement = "", x = lines))
# After
AllMacbeth %>%
filter(junk) %>%
select(speakers, lines)
Time to take a look!
- Q: How might we use our result so far to count how many words are attributed to each character?
First attempt: Counting words
- Q: How’s it look? >- Q: How would you debug this?
countWords <- function(line) {
return(length(strsplit(x = line, split = "\\s+")))
}
AllMacbeth$nWords <- sapply(X = AllMacbeth$lines, FUN = countWords)
head(AllMacbeth)
Debugging our cleanup
- Q: How’s it look? >- Q: What type of object is this?
strsplit(x = AllMacbeth$lines[1], split = "\\s+")
[[1]]
[1] "When" "shall" "we" "three" "meet" "again?" "In" "thunder,"
[9] "lightning," "or" "in" "rain?"
Debugging our cleanup
countWords <- function(line) {
return(length(strsplit(x = line, split = "\\s+")[[1]]))
}
AllMacbeth$nWords <- sapply(X = AllMacbeth$lines, FUN = countWords)
head(AllMacbeth)
Choosing your character
- Recall: You want to
- impress someone in THEA 102
- choose an “important” character
- not too much to memorize
- Q: What are your priorities?
- Most lines?
- Longest lines?
- Who has the most total content to memorize?
- Something else?
- Q: How should we figure it out from here?
# Longest individual lines
AllMacbeth %>%
select(speakers, nWords, lines) %>%
arrange(desc(nWords))
Character analysis
- Most lines?
- Longest lines?
- Who has the most total content to memorize?
- Something else?
MacbethSummary <-
AllMacbeth %>%
group_by(speakers) %>%
summarise(lines = n(),
totalWords = sum(nWords),
wordsPerLine = totalWords/lines)
# Most words per line
MacbethSummary %>%
arrange(desc(wordsPerLine)) %>%
head(10)
# Most total content
MacbethSummary %>%
arrange(desc(totalWords)) %>%
head(10)
Who do you choose?
MyLines <-
AllMacbeth %>%
filter(grepl(" LADY MACBETH\\.", speakers))
head(MyLines)
Text Mining Macbeth
- I want to dig a little deeper…
- (translation: I found more 2 min Macbeth videos!)
- Q: What would be some interesting questions to explore when text mining Macbeth?
Text Mining Macbeth
- Shakespearean plays are believed to share a common literary structure
- Each ACT in the play has a different, but important role in the drama
- Problem people talk weird in Macbeth (I’m kidding… sort of)
- Let’s do some text analysis of a “modern” translation of the play
- http://www.nosweatshakespeare.com/shakespeares-plays/modern-macbeth/
- Q: How would you solve this so we could process the text?
Text Analysis
- Now, I’ve got a whole directory full of text files.
- I was at least smart enough to name them in a structured way:
- dedicated directory location
- useful naming convention: “act1_scene1.txt”
readtext
can read all the “*.txt" files in the requested directory
docvarsfrom
indicates that the file name has some variables I want
dvsep
indicates that they are separated by an underscore
- result is a data frame
require(readtext)
ModernMacbeth <-
readtext::readtext(file = "/Users/mattbeckman/Documents/GitHub/Teaching/STAT-380/2019 Spring/ClassNotes/15-mdsr/plain_Macbeth/*.txt", docvarsfrom = "filenames", dvsep = "_") %>%
as.tibble()
head(ModernMacbeth)
Corpus
- we’re often interested in many documents when text mining
- a corpus is a collection of many documents
- Q: In our example:
- what is the corpus?
- what are the documents?
- Q: Describe a “corpus” and the associated “documents” for
- a different analysis of Shakespeare?
- some other author?
- other examples?
Modern Macbeth Corpus
- before we can really dig into our study of the differences among acts in Macbeth, we need to build up a couple text analysis tools
- we’ll use the full text of Modern Macbeth to get started
Tidy Text Format
- Recall: tidy data has a specific structure
- each case is a row
- each variable is a column
- Tidy text could similarly be described as a table with one token-per-row
- a token is a meaningful unit of text useful for analysis
- word (very common)
- n-gram (sequence of words)
- sentence
- paragraph
- tokenization is the process of splitting text into tokens
Tokenization step
- let’s tokenize Modern Macbeth
- we’ll use some tools from the
tidytext
package
unnest_tokens
breaks the text into individual tokens… automatically
- it keeps the other columns–act & scene here
- strips punctuation
- converts to lowercase (default arg:
to_lower = TRUE
)
require(tidytext)
ModernMacbeth %>%
select(text, act = docvar1, scene = docvar2) %>%
unnest_tokens(output = word, input = text) # single word tokenization
# unnest_tokens(output = word, input = text, token = "ngrams", n = 3) # n-gram tokenization
Bag of words
- We’re using a simple approach to text analysis called bag of words
- Bag of words preserves term frequencies, but disregards word order, grammar, etc
ModernMacbeth_tidy <-
ModernMacbeth %>%
select(text, act = docvar1, scene = docvar2) %>%
unnest_tokens(output = word, input = text) # single word tokenization
Token frequency
- Q: What have we learned about Act 1?
- Q: What should we do next?
ModernMacbeth_tidy %>%
count(word, sort = TRUE)
Stop words
- stop words are words that are not useful for an analysis
- extremely common words like “the”, “and”, “to”, as shown
- words that aren’t useful in analysis like “accordingly”
- The contents of the stop word list matters a lot
tidytext
package includes a data set called stop_words
to get you started
- The stop words are different for each language
- You might add/modify the stop words based on specialized expertise with the context
- Q: What if we had used the original Shakespeare?
# native text
ModernMacbeth_tidy %>%
count(word, sort = TRUE)
# load stop word list (English)
data("stop_words") # from `tidytext` package
head(stop_words)
tail(stop_words)
Removing stop words
- filter out stop words
- equivalently, we could use an
anti_join
here
- it turns out Macbeth is a big deal and he’s all over the play
- we might consider removing “macbeth” from the bag of words
- Q: how about another suggestion?
- …maybe something that seems like it clearly should have been removed already??
- is our list of stop words really that bad??
# # `stop_words` has two columns: "word" and "lexicon
# stop_words <-
# rbind(stop_words,
# c("macbeth", "custom"))
#
# stop_words %>%
# filter(word == "it's")
#
# # we'll need to use RegEx to clean these up
# grep(pattern = "it.s", x = ModernMacbeth_tidy$word, value = TRUE)
# grep(pattern = "’", x = ModernMacbeth_tidy$word, value = TRUE) %>% head(30)
ModernMacbeth_tidy <-
ModernMacbeth_tidy %>%
# mutate(word = gsub(pattern = , replacement = , x = word)) %>%
filter(!(word %in% stop_words$word))
ModernMacbeth_tidy %>%
count(word, sort = TRUE)
Tidy text pipes to ggplot2
- Let’s make a histogram of word frequencies
- Q: What’s going on in Act 1 of Macbeth?
ModernMacbeth_tidy %>%
mutate(word = gsub(pattern = "’", replacement = "'", x = word)) %>%
count(word, sort = TRUE) %>%
filter(n > 20) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n)) +
geom_col() +
xlab(NULL) +
coord_flip()

Word clouds
- word clouds are another popular visualization of token frequency (analogous to our histogram)
- the
wordcloud
package uses base R graphics, but the with( )
function helps
- Q: What’s Macbeth about?
- What have we learned from this?
- What should we do next?
ModernMacbeth_tidy %>%
filter(!(word %in% stop_words$word)) %>%
count(word) %>%
with(., wordcloud(word, n, max.words = 45))

word clouds: Modern vs original
- I’ve made a helper function to prep the corpus
- I also added “said” to our stop word list
c(stopwords("english"), "said")
- should “macbeth” be added to the list?
AllMacbeth_tidy <-
AllMacbeth %>%
select(speakers, lines) %>%
unnest_tokens(output = word, input = lines) # single word tokenization
head(AllMacbeth_tidy)
# helper function for making wordclouds
macbeth_wordcloud <- function(Corpus_tidy, maxWords = 45) {
Corpus_tidy %>%
filter(!(word %in% stop_words$word)) %>%
count(word) %>%
with(., wordcloud(word, n, max.words = maxWords))
}
macbeth_wordcloud(Corpus = AllMacbeth_tidy)

macbeth_wordcloud(Corpus = ModernMacbeth_tidy)

Act by Act word clouds
- Q: Can we tell anything about the storyline?
- Q: How reliable are word clouds able to convey meaning of each Act?
macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act1"))

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act2"))

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act3"))

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act4"))

macbeth_wordcloud(filter(ModernMacbeth_tidy, act == "Act5"))

Recall
- Humans:
- excellent at understanding text
- not as good at storing text
- Computers:
- excellent at storing text
- not as good at understanding text
- Human + Computer??
- excellent at understanding & storing text?
- not so good at storing & understanding text?
- (depends on the human & the computer…?)
Sentiment
- token frequencies are OK, but words evoke emotion
- humans are (usually) excellent at understanding emotions
- word choice can indicate
- general positive/negative emotion
- specific emotions like surprise or disgust
- good news!
- people have compiled detailed word lists for this
tidytext
package includes several in the sentiments
data set
require(tidytext)
data("sentiments")
head(sentiments, 10)
tail(sentiments, 10)
Sentiment lexicons
sentiments
contains three general purpose lexicons
- AFINN–rating from -5 (very negative) to +5 (very positive)
- bing–positive/negative classification
- nrc–classification into categories
- positive
- negative
- fear
- sadness
tidytext
provides function get_sentiments( )
to choose a lexicon
- lots of words are quite neutral, so they can be excluded from sentiment lexicons
- the sentiments in these lexicons might be constructed by crowdsourcing or research, then validated using restaurant, movie, or amazon reviews
- Q: why might we hesitate to apply these sentiment lexicons to Shakespeare’s literature?
- Q: Any other concerns that might make it hard to capture sentiment?
- one-word tokens
- extremely large chunks of text (all of Macbeth)
get_sentiments("nrc") %>%
group_by(sentiment) %>%
summarise(N = n()) %>%
arrange(desc(N))
Sentiment analysis of Macbeth
- since we’re working with tidy data, we can use an
inner_join
for sentiment analysis
- this converts several “text mining” tasks into simple tidy data analysis tasks
- let’s investigate some common terms in Macbeth that suggest “anticipation”
nrc_anticipation <- get_sentiments("nrc") %>%
filter(sentiment == "anticipation")
ModernMacbeth_tidy %>%
inner_join(nrc_anticipation) %>%
count(word, sort = TRUE)
Joining, by = "word"
NA
Changes in Sentiment
- small sections of text may not have enough words to communicate sentiment
- extremely large sections might average out the sentiment we want to capture
- for Shakespeare’s original text, we might choose blocks of 80 lines or so
- for Modern Macbeth, we’ll choose whole scenes (perhaps a bit large/variable)
- we’ll calculate a metric to assess each scene
- from -100 to 100
- 100 means 100% positive sentiment
ModernMacbeth_sentiment <-
ModernMacbeth_tidy %>%
mutate(act_scene = paste(act, scene, sep = "_")) %>%
inner_join(get_sentiments("afinn")) %>%
group_by(act, scene) %>%
summarise(sentiment = mean(score, na.rm = TRUE))
Joining, by = "word"
head(ModernMacbeth_sentiment)
Plot changes in sentiment by Act
ModernMacbeth_sentiment %>%
rownames_to_column() %>%
mutate(rowname = parse_number(rowname)) %>%
ggplot(aes(x = rowname, y = sentiment, fill = act)) +
geom_bar(stat = "identity", show.legend = FALSE) +
ggtitle("Sentiment Analysis of each Act & Scene in Modern Macbeth")

NA
Common positive and negative words
- we have a tidy data frame with both sentiment and word
- we can analyze word counts that contribute to each sentiment
bing_word_counts <-
ModernMacbeth_tidy %>%
inner_join(get_sentiments("bing")) %>%
count(word, sentiment, sort = TRUE) %>%
ungroup()
Joining, by = "word"
bing_word_counts
Plotting word frequency by sentiment
bing_word_counts %>%
group_by(sentiment) %>%
top_n(10) %>%
ungroup() %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~sentiment, scales = "free_y") +
labs(y = "Contribution to sentiment",
x = NULL) +
coord_flip()
Selecting by n

Wordclouds with feeling
- originally, we may have had trouble discerning what the text was about because so many of the common words don’t invoke sentiment (e.g, Macduff, Banquo, Malcolm)
- how about we revisit our wordcloud based on positive/negative sentiment!
- we’ll use
wordcloud::comparison.cloud( )
- note: we’ll need
acast()
to turn the data frame into a matrix
- Q: Anything strange about this word cloud?
ModernMacbeth_tidy %>%
anti_join(stop_words) %>%
count(word) %>%
with(wordcloud(word, n, max.words = 45))
require(reshape2)
ModernMacbeth_tidy %>%
inner_join(get_sentiments("bing")) %>%
count(word, sentiment, sort = TRUE) %>%
acast(word ~ sentiment, value.var = "n", fill = 0) %>%
comparison.cloud(colors = c("gray20", "gray80"), max.words = 45)
Joining, by = "word"

Positive scenes in Macbeth
- we want to find the most positive scene in each act
- the ratios aren’t good
- in the actual text, things go pretty south pretty fast…
- Act 1, Scene 6: Duncan & Banquo on a leisurely ride through the countryside (genuinely pleasant)
- Act 2, Scene 1: Macbeth trying to act natural for Banquo (while planning to kill Duncan)
- Act 3, Scene 2: Macbeth wishing he was dead like Duncan
- Act 4, Scene 2: super long, I didn’t read it all
- Act 5, Scene 6: Malcolm & Macduff psych themselves up to kill Macbeth
bingpositive <- get_sentiments("bing") %>%
filter(sentiment == "positive")
wordcounts <- ModernMacbeth_tidy %>%
group_by(act, scene) %>%
summarize(words = n())
ModernMacbeth_tidy %>%
semi_join(bingpositive) %>%
group_by(act, scene) %>%
summarize(positivewords = n()) %>%
left_join(wordcounts, by = c("act", "scene")) %>%
mutate(ratio = positivewords / words) %>% # ratio of positive words
filter(scene != 0) %>%
top_n(1) %>%
ungroup()
Joining, by = "word"
Selecting by ratio
Analysis of word & document frequency
- a central question in natural language processing is how to quantify what a document is about
- one approach as we have discussed is simply term frequency
- this may draw attention common words regardless of importance
- stop word removal attempts to adjusting term frequency for common used words
- this is a fairly crude approach; perhaps we can do better
Document term matrices
- alternative approach for assessing term frequency is the document-term matrix
- sparse matrix describing a collection (corpus) of documents
- row for each document
- column for each term
- values are typically word count or “tf-idf” (see below)
- term frequency–\(tf(t, d)\)–is simply the frequency of term \(t\) in document \(d\)
- inverse document frequency–\(idf\)–is prevalence of term \(t\) across a set of documents \(D\)
- words that are common across many documents like “the” get low rank
- numerator is the number of documents in \(D\)
- denominator is the number of documents in \(D\) containing the term
- \(tf-idf\) multiplies the two
- frequency of a term adjusted for how rarely it is used
- roughly measures how important a word is to a document in a collection (or corpus) of documents
\[idf(t, D) = \text{ln}\frac{|D|}{|\{d \in D : t \in d\}|}\]
Term frequency in Acts of Macbeth
- What are the most commonly used words in each Act?
- term frequency
- \(tf-idf\)
- we will “start over” from the source (without removing stop words)
- each row in
act_words
is a unique word-act combination
n
is the number of times that word is used in that act
total
is the total words in the act
# tf within each act
act_words <-
ModernMacbeth %>%
select(text, act = docvar1, scene = docvar2) %>%
unnest_tokens(output = word, input = text) %>%
mutate(word = gsub(pattern = "’", replacement = "'", x = word)) %>%
count(act, word, sort = TRUE)
# total words in the act
total_words <-
act_words %>%
group_by(act) %>%
summarize(total = sum(n))
act_words <- left_join(act_words, total_words)
Joining, by = "act"
act_words
Term frequency in Macbeth
- let’s look at the distribution of
n / total
for each act
- this is the term frequency, \(tf\)
- very long right tails (those extremely common words)
- These plots exhibit similar distributions for all the acts
- many words that occur rarely
- fewer words that occur frequently
act_words %>%
ggplot(aes(n / total, fill = act)) +
geom_histogram(show.legend = FALSE) +
facet_wrap(~ act, ncol = 2, scales = "free_y") +
ggtitle("Term frequency in Acts of Macbeth")

Zipf’s law
- long-tailed distributions are common in any given corpus of natural language (like a book, or a lot of text from a website, or spoken words)
- a classic version of this relationship is called Zipf’s law, after George Zipf, a 20th century American linguist.
- Zipf’s law states that the frequency that a word appears is inversely proportional to its rank.
freq_by_rank <-
act_words %>%
group_by(act) %>%
mutate(rank = row_number(), # since already ordered by `n`
`term frequency` = n / total)
freq_by_rank
Visualization of Zipf’s law
- Zipf’s law visualized by plotting (on logarithmic scales)
- x-axis:
rank
of each word within the frequency table
- y-axis:
term frequency
on the y-axis
- result: inversely proportional relationship has a constant, negative slope
- this type of result is known as a power law https://en.wikipedia.org/wiki/Power_law
- relative change in one quantity results in a proportional relative change in the other quantity, independent of the initial size of those quantities
- one quantity varies as a power of another
- area of a square vs length of side; 2 times length >> \(2^2\) times the area
freq_by_rank %>%
ggplot(aes(rank, `term frequency`, color = act)) +
geom_line(size = 1.1, alpha = 0.8, show.legend = TRUE) +
scale_x_log10() +
scale_y_log10() +
ggtitle("Zipf's law for acts of Macbeth")

Investigating term frequency in Macbeth
- result wasn’t quite linear
- deviations at high rank are not uncommon for many kinds of language;
- a corpus often contains fewer rare words than predicted by a single power law.
- deviations at low rank are more unusual
- Modern Macbeth used a lower percentage of the most common words than many collections of language.
- analysis could be extended to compare authors, or other collections of text (sonnets vs tragedies)
rank_subset <- freq_by_rank %>%
filter(rank < 200,
rank > 10)
lm(log10(`term frequency`) ~ log10(rank), data = rank_subset)
Call:
lm(formula = log10(`term frequency`) ~ log10(rank), data = rank_subset)
Coefficients:
(Intercept) log10(rank)
-0.946 -0.909
freq_by_rank %>%
ggplot(aes(rank, `term frequency`, color = act)) +
geom_abline(intercept = -0.946, slope = -0.909, color = "gray50", linetype = 2) +
geom_line(size = 1.1, alpha = 0.8, show.legend = TRUE) +
scale_x_log10() +
scale_y_log10() +
ggtitle("Zipf's law for Modern Macbeth")

tf-idf
- idea of tf-idf: find important words for the content of each document by
- decreasing the weight for commonly used words
- increasing the weight for words that are not used very much in the corpus of documents
- Calculating \(tf-idf\) attempts to find the words that are important (i.e., common) in a text, but not too common across all texts.
bind_tf_idf( )
function in the tidytext
package takes a tidy text dataset as input
- only need one row per token (term), per document
- column for the words, another identifying source document (act)
- we calculated
total
for each act previously, but don’t need it
- \(idf\)
- zero for words that are common across all documents (\(tf-idf\) is then zero too)
- higher for words that appear in fewer documents
act_words <-
act_words %>%
bind_tf_idf(word, act, n)
act_words
High tf-idf in Macbeth
- common to see proper nouns (people/places) from each document with the highest tf-idf
- interesting here to see other featured tokens (cauldron, hail, etc)!
- note: discreteness in the \(idf\) here because we have only 5 documents
- term that appears in only one of the five acts: \(idf = ln(5/1) = 1.6094\)
act_words %>%
select(-total) %>%
arrange(desc(tf_idf))
Visualizing high tf-idf words
act_words %>%
arrange(desc(tf_idf)) %>%
mutate(word = factor(word, levels = rev(unique(word)))) %>%
group_by(act) %>%
top_n(10) %>%
ungroup() %>%
ggplot(aes(word, tf_idf, fill = act)) +
geom_col(show.legend = FALSE) +
labs(x = NULL, y = "tf-idf") +
facet_wrap(~act, ncol = 2, scales = "free") +
coord_flip()
Selecting by tf_idf

Document-term matrix
- DTM’s are usually very sparse since lots of term-document pairs don’t occur
- DTM’s don’t play nice with tidy tools & data frames don’t play nice with most text mining packages
tidytext
package has tools to easily convert between object types
tidy()
turns a DTM into a tidy data frame
cast()_dtm
turns a tidy, one-term-per-row, data frame into a DTM (for tools in tm
pkg)
cast()_dtm
(with a “T”) turns a tidy, one-term-per-row, data frame into a DTM (for tools in tm
pkg)
cast()_dfm
(with an “F”) turns a tidy, one-term-per-row, data frame into a DFM (for tools in quanteda
pkg)
Topic Modeling
- sometimes useful to find natural groups among documents of some collection/corpus (blog posts, wikipedia pages, etc)
- Topic modeling–method for unsupervised classification of documents
- find natural groups even if we’re not totally sure what to look for
- similar to clustering on numeric data
- Latent Dirichlet allocation (LDA) is a particularly popular method
Latent Dirichlet allocation (LDA)
- Latent Dirichlet allocation (LDA)
- every document is a mixture of topics
- every topic is a mixture of words
- LDA basically estimates the composition of both mixtures at the same time
- result allows content of documents to “overlap” rather than enforcing discrete groups…just like we do in natural language use.
- the
topicmodels
package will help us on our way
Preparing for LDA
- We’ll borrow a data set of articles from the Associated Press to illustrate some principles first.
- Don’t worry, back to Macbeth in a bit
- Note: We need the data as a DTM (not tidy form)
- Q: can you explain what’s happening at each line?
- Q: how many rows & columns are in our
AssociatedPress
matrix?
data("AssociatedPress")
AssociatedPress
<<DocumentTermMatrix (documents: 2246, terms: 10473)>>
Non-/sparse entries: 302031/23220327
Sparsity : 99%
Maximal term length: 18
Weighting : term frequency (tf)
Two-topic Latent Dirichlet allocation (LDA) model
- fitting the LDA model is the easy part with help of the
LDA( )
function
- note: we set an arbitrary seed in this case so we have same result each time… not necessary in general
- There are almost certainly more than two topics, but this is a start
- For a corpus of news articles like the AP data, we might expect the topics to be something like “politics” and “entertainment”… recall:
- each document could include a mix of topics (politics & entertainment)
- each topic has a mix of words
- politics topic might include words like ‘president’, ‘congress’, and ’government
- entertainment topic might include ‘movies’, ‘television’, and ‘actor’
- both topics might frequently include a word like ‘budget’
require(topicmodels)
ap_lda <-
AssociatedPress %>%
LDA(k = 2, control = list(seed = 1234))
ap_lda
A LDA_VEM topic model with 2 topics.
Per-topic-per-word probabilities
- want to extract the per-topic-per-word probabilities, \(\beta\) (“beta”), from the model
tidytext::tidy( )
provides method
- one-topic-per-term-per-row format
- For each combination, model computes probability of term being generated from that topic
ap_topics <- tidy(ap_lda, matrix = "beta")
ap_topics
Top 10 per-topic-per-word probabilities (\(\beta\))
- Q: how might you characterize each topic now?
- note words characteristic of each topic
- note words common to both topics
ap_top_terms <- ap_topics %>%
group_by(topic) %>%
top_n(10, beta) %>%
ungroup() %>%
arrange(topic, -beta)
ap_top_terms %>%
mutate(term = reorder(term, beta)) %>%
ggplot(aes(term, beta, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
facet_wrap(~ topic, scales = "free") +
coord_flip()

Words with greatest difference in \(\beta\) between topics
- estimated based on the log ratio of the two: \(\text{log}_2(\frac{\beta_2}{\beta_1})\)
- a log ratio is useful because it makes the difference symmetrical
- \(\beta_2\) being twice as large leads to a log ratio of 1
- \(\beta_1\) being twice as large results in -1
- we filter for relatively common words with \(\beta > 1/1000\) in at least one topic
- Q: what can we learn by inspecting the extremes?
beta_spread <- ap_topics %>%
mutate(topic = paste0("topic", topic)) %>%
spread(topic, beta) %>%
filter(topic1 > .001 | topic2 > .001) %>%
mutate(log_ratio = log2(topic2 / topic1)) %>%
arrange(desc(log_ratio))
beta_spread
Per-document classification
- LDA also models each document as a mixture of topics
- We examine the per-document-per-topic probabilities, \(\gamma\) (“gamma”)
- Gamma is proportion of words from that document, generated from that topic. For example,
- about 25% of the words in document 1 come from topic 1
- about 82% of the words in document 5 come from topic 2
scenes_gamma <- tidy(ap_lda, matrix = "gamma")
scenes_gamma %>%
arrange(document, gamma)
Let’s investigate a few interesting documents
- Document 3 is almost 50-50 between our two topics
- Document 6 is almost entirely topic 2
- Q: what does each document seem to be about?
- Q: does \(\gamma\) attributed to each topic make sense in these cases?
# investigate document 3 & 6
tidy(AssociatedPress) %>%
filter(document == 3) %>%
arrange(desc(count))
Modeling Topics as Acts in Macbeth…?
- Recall: our motivating question was to try and learn if we could use the content of Macbeth to expose structure among the 5 acts
- Results so far:
- wordclouds: “meh”
- sentiment analysis: super negative…no wonder Macbeth is called a “tragedy”
- tf-idf: hard to tell much… all the documents were written by the same person for the same purpose, so not surprised
- LDA: ???
- Goal: use LDA to model
k = 5
topics
- common to try a few different values of
k
(number of topics)
- start with 5 here because we know there are 5 acts in the play
# convert to Document Term Matrix
ModernMacbeth_DTM <-
ModernMacbeth %>%
mutate(act_scene = gsub(pattern = "\\.txt", replacement = "", x = doc_id)) %>%
unnest_tokens(output = word, input = text) %>%
mutate(word = gsub(pattern = "’", replacement = "'", x = word)) %>%
anti_join(stop_words, by = c("word" = "word")) %>%
count(act_scene, word, sort = TRUE) %>%
cast_dtm(document = act_scene, term = word, value = n)
# LDA topic model (5 topics)
scenes_lda <-
ModernMacbeth_DTM %>%
LDA(k = 5, control = list(seed = 380))
# Per-topic-per-word probabilities
scene_topics <- tidy(scenes_lda, matrix = "beta")
scene_topics
# Visualize top per-topic-per-word probabilities
top_terms <-
scene_topics %>%
group_by(topic) %>%
top_n(5, beta) %>%
ungroup() %>%
arrange(topic, -beta)
top_terms %>%
mutate(term = reorder(term, beta)) %>%
ggplot(aes(term, beta, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
facet_wrap(~ topic, scales = "free") +
coord_flip()

# Per-document classification
scenes_gamma <- tidy(scenes_lda, matrix = "gamma")
scenes_gamma %>%
arrange(document, gamma)
# Topics vs Acts?
scenes_gamma <-
scenes_gamma %>%
separate(col = document, into = c("act", "scene"), sep = "_", convert = TRUE)
scenes_gamma %>%
ggplot(aes(factor(topic), gamma)) +
geom_boxplot() +
facet_wrap(~ act)

Scene alignment to topics
- Okay, the topics aren’t the acts
- turns out this enduring Shakespearean masterpiece needs a more nuanced interpretation
- Literary themes could align better to the Topics identified by our LDA…
- heres one attempt I found, with my wife’s description of each color:
- ambition (lavendar)
- fate (poppy)
- violence (Tiffany blue)
- nature & unnatural (sand)
- masculinity (Kendall gray)
LS0tCnRpdGxlOiAiVGV4dCBhcyBEYXRhIgpzdWJ0aXRsZTogIk1EU1IgQ2ggMTUgPGJyPiBUZXh0IE1pbmluZyB3aXRoIFIgKFNpbGdlICYgUm9iaW5zb24sIDIwMTkpIgpvdXRwdXQ6IAogIHNsaWR5X3ByZXNlbnRhdGlvbjogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQgIAotLS0KCgpgYGB7ciBGcm9udCBNYXR0ZXIsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBjbGVhbiB1cCBSIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKIyBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUpCm9wdGlvbnMoZGlnaXRzPTQpCgojIGxvYWQgYWxsIHBhY2thZ2VzIGhlcmUKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHJlYWR0ZXh0KQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHdvcmRjbG91ZCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeSh0b3BpY21vZGVscykKCgojIGlucHV0cyBzdW1tYXJ5CiMgZGF0YSgiTWFjYmV0aF9yYXciKSAgICMgTURTUiBiYWNrdXAgdmVyc2lvbiBvZiBNYWNiZXRoCmRhdGEoInN0b3Bfd29yZHMiKSAgICAjIGZyb20gYHRpZHl0ZXh0YApkYXRhKCJzZW50aW1lbnRzIikgICAgIyBmcm9tIGB0aWR5dGV4dGAKZGF0YSgiQXNzb2NpYXRlZFByZXNzIikgICMgZnJvbSAndG9waWNtb2RlbHMnCgpgYGAKCgojIEFnZW5kYQoKLSBSZWd1bGFyIGV4cHJlc3Npb25zIAotIEJhc2ljIHRleHQgYW5hbHlzaXMKICAgIC0gY29ycG9yYQogICAgLSB3b3JkIGNsb3VkcwogICAgLSBzZW50aW1lbnQgYW5hbHlzaXMKLSBJbnRlcm1lZGlhdGUKICAgIC0gdGVybSBmcmVxdWVuY3kgeCBpbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSAoKnRmLWlkZiopCiAgICAtIExhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbiAoTERBOyB0b3BpYyBtb2RlbGluZykKCiMjIyMgQW5ub3VuY2VtZW50cwoKLSBGaW5hbCBQcm9qZWN0IHVwZGF0ZQotIE1EU1IgQ2ggMTUgcHJvZ3JhbW1pbmcgbm90ZWJvb2sgYXNzaWduZWQKLSBCZWNrbWFuJ3Mgb2ZmaWNlIGhvdXIgY2FuY2VsbGVkIG9uIFRodXJzIDQvNCAoc2VlIHN5bGxhYnVzIGZvciBvdGhlciBvcHRpb25zKQotIE1EU1IgQ2ggMTUgRXhlcmNpc2VzIGR1ZSA0LzE0IGJlZm9yZSBtaWRuaWdodAotIExvdHMgb2YgZXJyYXRhL3RpcHMgdGhpcyBjaGFwdGVyCgojIyMjIE1EU1IgQ2ggMTUgRXJyYXRhIC8gVGlwcwoKLSBTb21lIHNlY3Rpb25zIGRvbid0IHJlcXVpcmUgcHJvZ3JhbW1pbmcsIGJ1dCBwbGVhc2Ugc3RpbGwgaW5jbHVkZSB0aGUgaGVhZGVycyBmb3IgbmF2aWdhdGlvbiBwdXJwb3NlcwotIHAuIDM2MTogTm90ZS0tYGRhdGEoIkRhdGFTY2llbmNlUGFwZXJzIilgIHNob3VsZCBhcHBlYXIgQk9USCBpbiBmcm9udCBtYXR0ZXIgKHBlciBzdHlsZSBndWlkZSkgQU5EIGFzIGEgY29kZSBjaHVuayBpbiBzZXF1ZW5jZSB3aXRoIHRoZSBvdGhlciBNRFNSIGNvZGUgc28geW91ciBzZWN0aW9uIDE1LjIgcmVzdWx0cyBpbiB0aGUgcHJvZ3JhbW1pbmcgbm90ZWJvb2sgd2lsbCBtYXRjaCB0aGUgTURTUiBib29rIHJhdGhlciB0aGFuIHVzaW5nIG5ldyBkYXRhIHF1ZXJpZWQgZnJvbSBhclhpdiBhbHNvIGxvYWRlZCBvbiBwLiAzNjEgdW5kZXIgdGhlIG9iamVjdCBuYW1lIGBEYXRhU2NpZW5jZVBhcGVyc2AKLSBwLiAzNjU6IHlvdXIgd29yZCBjbG91ZCB3b24ndCBiZSBpZGVudGljYWwgYW5kIG1heSBldmVuIGV4Y2x1ZGUgImRhdGEiIGlmIGl0IGRvZXNuJ3QgZml0IG9uIHRoZSBwYWdlCi0gcC4gMzY4OiBXaWtpcGVkaWEgY2hhbmdlZCB0aGUgdGFibGVzIGFsbCBhcm91bmQuIFVzZSBgVGFibGVbWzRdXWAgd2hlbiBzY3JhcGluZzsgYFRpdGxlYCBpcyBub3cgYFNvbmdgOyB0aGUgcmVzdWx0cyB3b24ndCBleGFjdGx5IG1hdGNoIGJ1dCB5b3UnbGwgYmUgYWJsZSB0byB3b3JrIGl0IG91dC4KLSBwLiAzNjk6IGNoYW5nZSBgMjVgIHRvIGAxNWAgKHRoZSByZXN1bHQgaXMgcXVpdGUgYSBkaWZmZXJlbnQgbWVzc2FnZSEpCi0gcC4gMzcwOiB5b3UgZG9uJ3QgbmVlZCBhIFR3aXR0ZXIgYWNjb3VudCwgdGhlIHByb3ZpZGVkIGNyZWRlbnRpYWxzIHdvcmsuLi4gaGVyZSB0aGV5IGFyZToKICAgIC0gY29uc3VtZXJfa2V5ID0gInUyVXRoamJLNllIeVFTcDRzUGs2eWpzdVYiIAogICAgLSBjb25zdW1lcl9zZWNyZXQgPSAic0M0bWpkMldNRTVuSDFGb1dlU1R1U3k3SkNQNURIak50VFlVMVg2QndRMXZQWjBqM3YiIAogICAgLSBhY2Nlc3NfdG9rZW4gPSAiMTM2NTYwNjQxNC03dlBmUHhTdFlOcTZrV0VBVFFsVDhIWkJkNEc4M0JCY1g0Vm9TOVQiIAogICAgLSBhY2Nlc3Nfc2VjcmV0ID0gIjBoSnE5S1lDM2VCUnVaekpxU2FjbXRKNFBOSjd0TkxrR3JRclZsMDBKSGlycyIKLSBwLiAzNzI6IHlvdSdsbCBuZWVkIHRvIGxvYWQgdGhlIGBSU1FMaXRlYCBsaWJyYXJ5Ci0gcC4gMzczOiB5b3UgY2FuIHNraXAgdGhlIGBnZW9jb2RlKC4uLilgIGZ1bmN0aW9uIGZyb20gdGhlIGBnZ21hcGAgcGFja2FnZSBhbmQganVzdCBhc3NpZ24gYGxvbmAgYW5kIGBsYXRgIGRpcmVjdGx5LiAgWW91IGNhbiBldmVuIGxvb2sgdXAgdGhlIGNvb3JkaW5hdGVzIGZvciBTdGF0ZSBDb2xsZWdlIGFuZCBzdWJzdGl0dXRlIHRoYXQgaWYgeW91IGxpa2UuICBPZGRseSBlbm91Z2gsIHlvdSBtaWdodCBoYXZlIHRvIGdpdmUgYSBjcmVkaXQgY2FyZCBudW1iZXIgdG8gR29vZ2xlIGlmIHlvdSB3YW50IHRvIHVzZSBgZ2VvY29kZSgpYCBzbyBmZWVsIGZyZWUgdG8gbW9kaWZ5IHlvdXIgY29kZSB0byBhdm9pZCB0aGF0IHN0ZXAuCgoKIyBSb2FkIG1hcAoKPGNlbnRlcj4KCiFbaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL3RvcGljbW9kZWxpbmcuaHRtbF0odGlkeXRleHRGbG93LnBuZyl7d2lkdGg9OTUlfQoKPC9jZW50ZXI+CgoKCgojIFByb2plY3QgR3V0ZW5iZXJnIAoKLSA8aHR0cHM6Ly93d3cuZ3V0ZW5iZXJnLm9yZy8+Ci0gUHJvamVjdCBHdXRlbmJlcmcgaXMgYSBtYXNzaXZlIGZyZWUgb25saW5lIGxpYnJhcnkKLSBmdWxsLXRleHQgb2YgYWxtb3N0IDYwLDAwMCBmcmVlIGVib29rcyAoZS5nLiwgZXhwaXJlZCBjb3B5cmlnaHQpCiAgICAtIEphbmUgQXVzdGluCiAgICAtIENoYXJsZXMgRGlja2VucwogICAgLSBNYXJrIFR3YWluCiAgICAtIEFydGh1ciBDb25hbiBEb3lsZQogICAgLSBPc2NhciBXaWxkZQogICAgLSAqKkJpbGwgU2hha2VzcGVhcmUqKgogICAgLSAoYW5kIGxvYWRzIG1vcmUuLi4pCiAgICAtIE5ldyBvbiAzLzI5LzIwMTktLVRoZSBBbWVyaWNhbiBCZWUgSm91cm5hbCEKCgojIE1hY2JldGggU3VtbWFyeQoKT3VyICJtdXNlIiBmb3IgdGhpcyBmaXJzdCBwb3J0aW9uIGlzIHRoZSBmYW1vdXMgcGxheSAqKk1hY0JldGgqKiBieSBXaWxsaWFtIFNoYWtlc3BlYXJlLiAgSWYgeW91IGFyZW4ndCBmYW1pbGlhciB3aXRoIHRoZSBwbGF5LCBoZXJlIGlzIGEgcXVpY2sgc3VtbWFyeToKCi0gOSBtaW51dGVzOiA8aHR0cHM6Ly95b3V0dS5iZS91ekF1anlXcEtfcz4KLSAyIG1pbnV0ZXMgKDEpOiA8aHR0cHM6Ly95b3V0dS5iZS93RHBtWmJmRFRKST4KLSAyIG1pbnV0ZXMgKDIpOiA8aHR0cHM6Ly92aW1lby5jb20vMTI0MDgyNTc3PgotIDIgbWludXRlcyAoMyk6IDxodHRwczovL3lvdXR1LmJlL0Y1bmx4Mlh6UC00P3Q9Mz4KLSAyIG1pbnV0ZXMgKDQpOiA8aHR0cHM6Ly95b3V0dS5iZS9US1lyWlNKMnJVbz4KCgojIFRleHQgYXMgZGF0YSAKCi0gTWFjYmV0aCB0ZXh0OiA8aHR0cDovL3d3dy5ndXRlbmJlcmcub3JnL2NhY2hlL2VwdWIvMTEyOS9wZzExMjkudHh0PgotIFE6IFdoYXQgYXJlIHNvbWUgb2YgdGhlIGNoYWxsZW5nZXMgeW91IHdvdWxkIGV4cGVjdCB3aGVuIHdvcmtpbmcgd2l0aCB0ZXh0IGFzIGRhdGE/CgoKIyBNYWNiZXRoIHRleHQgZGF0YSBpbnRha2UKCi0gUHJldHR5IGZhc3QhCi0gSG93IGRvZXMgaXQgbG9vaz8KICAgIC0gUTogTG9vayBjbG9zZWx5LCBDYW4geW91IHJlYWQgdGhpcz8KICAgIC0gUTogSG93IGNhbiB3ZSBtYWtlIHRoZSB0ZXh0IG1vcmUgdW5kZXJzdGFuZGFibGU/CgpgYGB7cn0KbWFjYmV0aF91cmwgPC0gImh0dHA6Ly93d3cuZ3V0ZW5iZXJnLm9yZy9jYWNoZS9lcHViLzExMjkvcGcxMTI5LnR4dCIKTWFjYmV0aF9yYXcgPC0gUkN1cmw6OmdldFVSTChtYWNiZXRoX3VybCkKIyBNYWNiZXRoX3JhdwpgYGAKCgojIFRleHQgYXMgZGF0YQoKLSBIdW1hbnM6IAogICAgLSBleGNlbGxlbnQgYXQgKnVuZGVyc3RhbmRpbmcqIHRleHQKICAgIC0gbm90IGFzIGdvb2QgYXQgKnN0b3JpbmcqIHRleHQKLSBDb21wdXRlcnM6IAogICAgLSBleGNlbGxlbnQgYXQgKnN0b3JpbmcqIHRleHQKICAgIC0gbm90IGFzIGdvb2QgYXQgKnVuZGVyc3RhbmRpbmcqIHRleHQgCi0gSHVtYW4gKyBDb21wdXRlcj8/CiAgICAtIGV4Y2VsbGVudCBhdCB1bmRlcnN0YW5kaW5nICYgc3RvcmluZyB0ZXh0PwogICAgLSBub3Qgc28gZ29vZCBhdCBzdG9yaW5nICYgdW5kZXJzdGFuZGluZyB0ZXh0PwogICAgLSAoZGVwZW5kcyBvbiB0aGUgaHVtYW4gJiB0aGUgY29tcHV0ZXIuLi4/KQoKCiMgQmFjayB0byBNYWNiZXRoCgotIFdlIG5lZWQgdG8gc3BsaXQgdGhlIG9uZSBnaWFudCBzdHJpbmcgb2YgYE1hY2JldGhfcmF3YCBldmVyeXdoZXJlIHRoYXQgd2Ugc2VlIHRoZSBwYXR0ZXJuIGBcclxuYCAobWFya2luZyBlbmQgb2YgbGluZSkKLSBROiBXaGF0IGNsYXNzIG9mIG9iamVjdCBkbyB3ZSBoYXZlIG5vdz8KLSBROiBJcyBpdCBiZXR0ZXI/CgoKYGBge3J9Cm1hY2JldGhfdG1wIDwtIHN0cnNwbGl0KE1hY2JldGhfcmF3LCAiXHJcbiIpCnN0cihtYWNiZXRoX3RtcCkKCgptYWNiZXRoIDwtIHN0cnNwbGl0KE1hY2JldGhfcmF3LCAiXHJcbiIpW1sxXV0KbGVuZ3RoKG1hY2JldGgpCgpoZWFkKG1hY2JldGgpCmBgYAoKCiMgVGhlIHNldCB1cC4uLgoKLSBMZXQncyBzYXkgeW91IGVucm9sbGVkIGluIFRIRUEgMTAyIGZvciBhIEdBIGNyZWRpdDogRnVuZGFtZW50YWxzIG9mIEFjdGluZyAgCi0gVGhlIGNsYXNzIHdpbGwgY2hvb3NlIGEgcGxheSBieSBTaGFrZXNwZWFyZSBhbmQgdGhlbiBldmVyeW9uZSB3aWxsIHZvbHVudGVlciBmb3IgdGhlIHZhcmlvdXMgcGFydHMuICAKLSBUaGVyZSdzIHNvbWVvbmUgaW4gdGhlIGNsYXNzIHlvdSB3YW50IHRvIGltcHJlc3MgCiAgICAtIFlvdSB3YW50IGEgcGFydCB0aGF0IGlzIGltcG9ydGFudCwgCiAgICAtIGJ1dCB5b3UgZG9uJ3Qgd2FudCAqdG9vKiBtYW55IGxpbmVzIHRvIG1lbW9yaXplLi4uCgoKIyBNYW51YWwgdGV4dCBhbmFseXNpcwoKLSBIb3cgd291bGQgeW91IGNvdW50IHVwIHRoZSBsaW5lcyBpZiB5b3UganVzdCBoYXZlIGEgcGh5c2ljYWwgY29weSBvZiB0aGUgcGxheT8KICAgIC0gUTogRm9yIG9uZSBjaGFyYWN0ZXI/CiAgICAtIFE6IEZvciBhbGwgY2hhcmFjdGVycz8KICAgIC0gUTogRm9yIGFsbCAzNyBvZiBTaGFrZXNwZWFyZSdzIHBsYXlzPwoKYGBge3J9Cm1hY2JldGhbMjk1OjMxMF0KCmBgYAoKCiMgTWFudWFsIHRleHQgYW5hbHlzaXMKCi0gSWYgeW91ICoqaGFkKiogdG8gZG8gaXQgbWFudWFsbHksIHlvdSdkIHByb2JhYmx5IGZsaXAgdGhyb3VnaCB0aGUgcGFnZXMgYW5kIG1ha2UgYSB0YWxseSAob24gYSBzZXBhcmF0ZSBzaGVldCkgZm9yIGVhY2ggbGluZQotIE5vdyB0aGF0IHlvdSBrbm93IHlvdSBjYW4gZmluZCBTaGFrZXNwZWFyZSdzIHBsYXlzIHRocm91Z2ggUHJvamVjdCBHdXRlbmJlcmcsIHlvdSBjYW4gcHJvYmFibHkgYWNjZXNzIGVib29rcyBmb3IgYWxsIG9mIHRoZW0KICAgIC0gUTogV2hhdCdzIHRoZSBhcHByb2FjaD8KICAgIC0gUTogU2Nyb2xsaW5nIGluc3RlYWQgb2YgImZsaXBwaW5nIiBidXQgb3RoZXJ3aXNlIHNhbWU/CiAgICAtIFE6IFNvbWV0aGluZyBlbHNlPwoKCiMgUmVndWxhciBFeHByZXNzaW9ucyAoUmVnRXgpCgotIEh1bWFucyBhcmUgZ29vZCBhdCB1bmRlcnN0YW5kaW5nIHRleHQsIGJ1dCBiYWQgYXQgc3RvcmluZyBsb3RzIG9mIHRleHQgYW5kIHNsb3cgYXQgc2VhcmNoaW5nCi0gQ29tcHV0ZXJzIGFyZSBnb29kIGF0IHN0b3JpbmcgJiBzZWFyY2hpbmcsIGJ1dCBub3QgZ29vZCBhdCAidW5kZXJzdGFuZGluZyIgaXQuCi0gKipSZWd1bGFyIGV4cHJlc3Npb25zKioKICAgIC0gQ29tcHV0ZXIgc3RvcmVzIHRoZSB0ZXh0IChnb29kIGF0IHRoaXMpCiAgICAtIEh1bWFuIGlkZW50aWZpZXMgcGF0dGVybiAoZ29vZCBhdCB0aGlzKQogICAgLSBIdW1hbiB0cmFuc2xhdGVzIHBhdHRlcm4gc28gY29tcHV0ZXIgY2FuIHNlYXJjaCBmb3IgaXQgKHRha2VzIHByYWN0aWNlLi4uKQogICAgLSBDb21wdXRlciBzZWFyY2hlcyB0aGUgdGV4dCAoZ29vZCBhdCB0aGlzKQotIFJTdHVkaW8gQ2hlYXRzaGVldHM6IDxodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvPgoKCiMgYGdyZXAoIClgICYgYGdyZXBsKCApYAoKLSB0aGVzZSBmdW5jdGlvbnMgbG9vayBmb3IgdGhlIG5lZWRsZSAocGF0dGVybikgaW4gdGhlIGhheXN0YWNrIChjaGFyYWN0ZXIgdmVjdG9yKQogICAgLSBuZWVkbGUvcGF0dGVybjogYE1BQ0JFVEgiYCB3aXRoIHR3byBsZWFkaW5nIHNwYWNlcwogICAgLSBoYXlzdGFjay90ZXh0OiAgYG1hY2JldGhgIGNoYXJhY3RlciB2ZWN0b3Igb2JqZWN0Ci0gUTogV2hhdCdzIHJldHVybmVkIGluIGVhY2ggY2FzZT8KICAgIC0gQXJlIHRoZXNlIE1hY2JldGgncyBsaW5lcz8KICAgIC0gQWxsIG1lbnRpb25zIG9mIE1hY2JldGhzJyBuYW1lPwogICAgLSBBcmUgd2UgZG9uZT8KLSBROiBIb3cgYXJlIHdlIGRvaW5nPwogICAgLSBDYW4gd2UgaW1wcm92ZSBpdCBmdXJ0aGVyPwogICAgLSBDb21wYXJlIHdpdGggQ3RybCArIEYgb24gdGhlIHdlYnNpdGU/CgoKYGBge3J9Cm1hY2JldGhfbGluZXMgPC0gZ3JlcCgiTUFDQkVUSCIsIG1hY2JldGgpCiMgbWFjYmV0aF9saW5lcyA8LSBncmVwKCJNQUNCRVRIIiwgbWFjYmV0aCwgdmFsdWUgPSBUUlVFKQojIG1hY2JldGhfbGluZXMgPC0gZ3JlcGwoIk1BQ0JFVEgiLCBtYWNiZXRoKQoKbGVuZ3RoKG1hY2JldGhfbGluZXMpCmhlYWQobWFjYmV0aF9saW5lcykKYGBgCgoKYGBge3IgZXZhbD1GQUxTRX0KaWRlbnRpY2FsKGMoMTozKSwgYygxTCwgMkwsIDNMKSkgICMgYXJlIHRoZXNlIHRoZSBzYW1lPwppZGVudGljYWwoYygxOjMpLCBjKDEsIDIsIDMpKSAgICAgIyBgaWRlbnRpY2FsYCBpcyBQSUNLWQoKIyBhcmUgdGhlc2UgdGhlIHNhbWU/CmlkZW50aWNhbChtYWNiZXRoW2dyZXAoIk1BQ0JFVEgiLCBtYWNiZXRoKV0sICAgCiAgICAgICAgICBtYWNiZXRoW2dyZXBsKCJNQUNCRVRIIiwgbWFjYmV0aCldKQpgYGAKCjwhLS0gRGF5MiAtLT4KCgojIFJlZmluaW5nIG91ciBSZWdFeAoKLSBIb3cgZG8geW91IGlkZW50aWZ5IGEgc3BlYWtlcidzIGxpbmVzPyAoSHVtYW5zIGFyZSBnb29kIGF0IHRoaXMsIG5vdCBjb21wdXRlcnMpCiAgICAtIEFsbCBDQVBJVEFMIExFVFRFUlMKICAgIC0gVHdvIGxlYWRpbmcgc3BhY2VzCiAgICAtIGVuZHMgd2l0aCBwZXJpb2QKLSBROiBDbG9zZSwgYnV0IHdoYXQncyB3cm9uZyBoZXJlPwoKYGBge3J9Cm1hY2JldGhfbGluZXMgPC0gZ3JlcCgiICBNQUNCRVRILiIsIG1hY2JldGgsIHZhbHVlID0gVFJVRSkKCmxlbmd0aChtYWNiZXRoX2xpbmVzKQpoZWFkKG1hY2JldGhfbGluZXMpCmBgYAoKCiMgUHJvYmxlbSB3aXRoIHBlcmlvZCAoYC5gKQoKLSBJbiBSZWdFeCwgdGhlIHBlcmlvZCBpcyBhIGEgbWV0YWNoYXJhY3RlciB0aGF0IG1hdGNoZXMgQU5ZIGNoYXJhY3RlcgotIGBcXC5gIGlzIHJlcXVpcmVkIHRvIGxpdGVyYWxseSBzZWFyY2ggZm9yIHBlcmlvZHMgaW4gb3VyIFJlZ0V4IHBhdHRlcm4KICAgIC0gYFxcYCBpcyBjYWxsZWQgYW4gImVzY2FwZSIgCiAgICAtIGVzY2FwZSBpcyByZXF1aXJlZCBmb3IgYW55IHBhcnQgb2YgYSBSZWdFeCB0aGF0IGhhcyBzcGVjaWFsIG1lYW5pbmcsIGJ1dCB5b3UganVzdCB3YW50IHRvIHNlYXJjaCBsaXRlcmFsbHkKCgpgYGB7cn0KIyBhbnl0aGluZyB3aXRoICJNQUMiIGFuZCB0aGVuIGFub3RoZXIgY2hhcmFjdGVyCmdyZXAoIk1BQy4iLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JQogIGhlYWQoKQoKIyBNQUNCRVRILgpncmVwKCJNQUNCRVRIXFwuIiwgbWFjYmV0aCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgyMCkKCmBgYAoKIyBTaW1wbGUgUmVnRXggVG9vbHMKCi0gYHxgOiBhbHRlcm5hdGlvbi0tc2VhcmNoIGZvciBhIGZldyBzcGVjaWZpYyBhbHRlcm5hdGl2ZXMKICAgIC0gYCJNQUNbQnxEXSJgIHdvdWxkIG1hdGNoIGFsbCBzdHJpbmdzIHRoYXQgaW5jbHVkZSAiTUFDQiIgT1IgIk1BQ0QiCi0gYFsgXWA6IGNoYXJhY3RlciBzZXRzLS1zcXVhcmUgYnJhY2tldHMgZGVmaW5lIHNldHMgb2YgY2hhcmFjdGVycyB0byBtYXRjaAogICAgLSBgIk1BQ1tDLVpdImAgd291bGQgbWF0Y2ggIk1BQyIgZm9sbG93ZWQgYnkgYW55IGNhcGl0YWwgbGV0dGVyIGZyb20gIkMiIHRocm91Z2ggIloiCi0gYF5gIG9yIGAkYDogYW5jaG9ycy0tc2VhcmNoIGZvciBwYXR0ZXJuIHN0cmljdGx5IGF0IHRoZSBiZWdpbm5pbmcgKGBeYCkgb3IgZW5kIChgJGApIG9mIHN0cmluZwogICAgLSBgIl5BQ1QiYCB3b3VsZCBtYXRjaCBhbGwgbGluZXMgc3RyaWN0bHkgYmVnaW5uaW5nIHdpdGggIkFDVCIKICAgIC0gYCJNQUNCRVRIJCJgIHdvdWxkIG1hdGNoIGFsbCBsaW5lcyBzdHJpY3RseSBlbmRpbmcgd2l0aCAiTUFDQkVUSCIgKG5vIHB1bmN0dWF0aW9uKQotIGA/YCBvciBgKmAgb3IgYCtgOiByZXBldGl0aW9ucwogICAgLSBgIl4gP01BQyJgIHdvdWxkIG1hdGNoIGlmIHN0cmluZyBiZWdpbnMgd2l0aCB6ZXJvIG9yIG9uZSBsZWFkaW5nIHNwYWNlcyBmb2xsb3dlZCBieSAiTUFDIgogICAgLSBgIl4gKk1BQyJgIHdvdWxkIG1hdGNoIGlmIHN0cmluZyBiZWdpbnMgd2l0aCB6ZXJvIG9yIG1vcmUgbGVhZGluZyBzcGFjZXMgZm9sbG93ZWQgYnkgIk1BQyIKICAgIC0gYCJeICtNQUMiYCB3b3VsZCBtYXRjaCBpZiBzdHJpbmcgYmVnaW5zIHdpdGggb25lIG9yIG1vcmUgbGVhZGluZyBzcGFjZXMgZm9sbG93ZWQgYnkgIk1BQyIKICAgIC0gYCJeIHszfU1BQyJgIHdvdWxkIG1hdGNoIGlmIHN0cmluZyBiZWdpbnMgd2l0aCBleGFjdGx5IDMgbGVhZGluZyBzcGFjZXMgZm9sbG93ZWQgYnkgIk1BQyIKCmBgYHtyfQojIGFsdGVybmF0aW9uIHdpdGggYHxgCmdyZXAoIk1BQ1tCfERdIiwgbWFjYmV0aCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgpCgojICJNQUMiIGZvbGxvd2VkIGJ5IGFueSBjYXBpdGFsIGxldHRlciBmcm9tICJDIiB0aHJvdWdoICJaIgpncmVwKCJNQUNbQy1aXSIsIG1hY2JldGgsIHZhbHVlID0gVFJVRSkgJT4lIGhlYWQoMTApCgojIHNlYXJjaCBmb3IgYmVnaW5uaW5nIG9mIGVhY2ggYWN0IGluIHRoZSBwbGF5IChgXmAgZ29lYSBhdCBiZWdpbm5pbmcpCmdyZXAoIkFDVCIsIG1hY2JldGgsIHZhbHVlID0gVFJVRSkgJT4lIGhlYWQoMTApCmdyZXAoIl5BQ1QiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKDEwKQoKIyBzdHJpbmdzIHN0cmljdGx5IGVuZGluZyBpbiAiTUFDQkVUSCIgKGAkYCBnb2VzIGF0IGVuZCkKZ3JlcCgiTUFDQkVUSCQiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKDEwKQoKIyByZXBldGl0aW9ucwpncmVwKCJeID9NQUMiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkgICMgemVybyBvciBvbmUgbGVhZGluZyBzcGFjZXMKZ3JlcCgiXiAqTUFDIiwgbWFjYmV0aCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgpICAjIHplcm8gb3IgbW9yZSBsZWFkaW5nIHNwYWNlcwpncmVwKCJeICtNQUMiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkgICMgb25lIG9yIG1vcmUgbGVhZGluZyBzcGFjZXMKZ3JlcCgiXiB7Mn1NQUMiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkgICMgZXhhY2x0eSB0d28gbGVhZGluZyBzcGFjZXMKZ3JlcCgiXiB7M31NQUMiLCBtYWNiZXRoLCB2YWx1ZSA9IFRSVUUpICU+JSBoZWFkKCkgICMgZXhhY3RseSB0aHJlZSBsZWFkaW5nIHNwYWNlcwoKYGBgCgojIFNwZWFrZXIgZnJlcXVlbmN5IGluIE1hY2JldGggCgotIHJlY2FsbDogeW91IHdhbnRlZCBhbiBpbXBvcnRhbnQgY2hhcmFjdGVyLCBidXQgbm90IGEgaHVnZSBudW1iZXIgb2YgbGluZXMgdG8gbWVtb3JpemUKICAgIC0gd2FudCBhIGRlY2VudCBudW1iZXIgb2YgbGluZXMKICAgIC0gImltcG9ydGFudCIgY291bGQgbWVhbiBpbnZvbHZlZCBmb3IgbW9zdCBvZiB0aGUgcGxheSAobm90IGtpbGxlZCBpbW1lZGlhdGVseSkKICAgIC0gUTogV2hvIG1lZXRzIG91ciBjcml0ZXJpYT8KICAgIAoKYGBge3J9Ck1hY2JldGggPC0gZ3JlcGwoIiAgTUFDQkVUSFxcLiIsIG1hY2JldGgpCk1hY2R1ZmYgPC0gZ3JlcGwoIiAgTUFDRFVGRlxcLiIsIG1hY2JldGgpCkxhZHlNYWNiZXRoIDwtIGdyZXBsKCIgIExBRFkgTUFDQkVUSFxcLiIsIG1hY2JldGgpCkxhZHlNYWNkdWZmIDwtIGdyZXBsKCIgIExBRFkgTUFDRFVGRlxcLiIsIG1hY2JldGgpCkJhbnF1byA8LSBncmVwbCgiICBCQU5RVU9cXC4iLCBtYWNiZXRoKQpEdW5jYW4gPC0gZ3JlcGwoIiAgRFVOQ0FOXFwuIiwgbWFjYmV0aCkKCgpzcGVha2VyX2ZyZXEgPC0gZGF0YS5mcmFtZShNYWNiZXRoLCBNYWNkdWZmLCBMYWR5TWFjYmV0aCwgTGFkeU1hY2R1ZmYsIEJhbnF1bywgRHVuY2FuKSAlPiUKICBtdXRhdGUobGluZSA9IDE6bGVuZ3RoKG1hY2JldGgpKSAlPiUKICBnYXRoZXIoa2V5ID0gImNoYXJhY3RlciIsIHZhbHVlID0gInNwZWFrIiwgLWxpbmUpICU+JQogIG11dGF0ZShzcGVhayA9IGFzLm51bWVyaWMoc3BlYWspKSAlPiUKICBmaWx0ZXIobGluZSA+IDIxOCAmIGxpbmUgPCAzMTcyKQoKZ2xpbXBzZShzcGVha2VyX2ZyZXEpCmBgYAoKCgpgYGB7cn0KIyBmaW5kIHRoZSBhY3RzIHRoYXQgc3ViZGl2aWRlIHRoZSBwbGF5CmFjdHNfaWR4IDwtIGdyZXAoIl5BQ1QgIiwgbWFjYmV0aCkKYWN0c19sYWJlbHMgPC0gc3RyX2V4dHJhY3QobWFjYmV0aFthY3RzX2lkeF0sICJeQUNUIFtJfFZdKyIpICAjIFtJfFZdKyBJLCBJSSwgSUlJLCBJViwgVgphY3RzIDwtIGRhdGEuZnJhbWUobGluZSA9IGFjdHNfaWR4LCBsYWJlbHMgPSBhY3RzX2xhYmVscykKCiMgcGxvdCBzcGVha2VyIGZyZXF1ZW5jaWVzCnNwZWFrZXJfZnJlcSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBsaW5lLCB5ID0gc3BlYWspKSArIAogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IGNoYXJhY3RlciksIG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gMCkgKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhY3RzX2lkeCwgY29sb3IgPSAiZGFya2dyYXkiLCBsdHkgPSAzKSArIAogIGdlb21fdGV4dChkYXRhID0gYWN0cywgYWVzKHkgPSAwLjA4NSwgbGFiZWwgPSBsYWJlbHMpLCAKICAgICAgICAgICAgaGp1c3QgPSAibGVmdCIsIGNvbG9yID0gImRhcmtncmF5IikgKyAKICB5bGltKGMoMCwgTkEpKSArIAogIHhsYWIoIkxpbmUgTnVtYmVyIikgKyAKICB5bGFiKCJQcm9wb3J0aW9uIG9mIFNwZWVjaGVzIikKYGBgCgoKCiMgRnVydGhlciBBbmFseXNpcwoKLSBHZXR0aW5nIGNsb3Nlci4uLiBidXQgd2Ugc2hvdWxkIHByb2JhYmx5IG9wZW4gdXAgdGhlIHNlYXJjaCBhIGxpdHRsZQotICoqUHJvYmxlbSoqOiBXZSByZWFsbHkgb25seSBncmFiYmVkIHRoZSBmaXJzdCBwYXJ0IG9mIGVhY2ggbGluZS4uLiAKICAgIC0gV2hhdCBpZiB3ZSBhY2NpZGVudGFsbHkgY2hvb3NlIGEgY2hhcmFjdGVyIHByb25lIHRvIGxvbmcgc3BlZWNoZXM/IAogICAgLSBNaWdodCBiZSB0b28gbXVjaCB0byBtZW1vcml6ZS4uLiB3ZSBuZWVkIGJldHRlciBpbmZvcm1hdGlvbgotIFE6IEhvdyBkbyB5b3Ugc3VnZ2VzdCB3ZSBhY2Nlc3MgKmVudGlyZSBsaW5lcyogZm9yIGVhY2ggcGFydCBpbiB0aGUgcGxheT8KICAgIC0gSG93IHdvdWxkIHlvdSBkbyBpdCAiYnkgZXllIj8KICAgIC0gSG93IGNhbiB5b3UgdHJhbnNsYXRlIHRoYXQgaW50byBSZWdFeD8KCiMgSW5kZW50aWZ5aW5nIGxpbmVzICYgc3BlYWtlcnMKCi0gUTogV2hhdCBkb2VzIHRoaXMgUmVnRXggcGF0dGVybiBnZXQgZm9yIHVzPyAgYCJcclxuIHsyfVtBLVp8IHwwLTldKlxcLiAiYAotIFE6IFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXN1bHQgZm9yCiAgICAtIGBzdHJzcGxpdGAgCiAgICAtIGBzdHJpbmdyOjpleHRyYWN0YAogICAgLSBgc3RyaW5ncjo6ZXh0cmFjdF9hbGxgCi0gUTogV2h5IGBbLTFdYCBhdCB0aGUgZW5kIG9mIGBzdHJzcGxpdGAgY29tbWFuZD8KCgpgYGB7cn0KbGluZXMgPC0gc3Ryc3BsaXQoTWFjYmV0aF9yYXcsICJcclxuIHsyfVtBLVp8IHwwLTldKlxcLiAiKVtbMV1dWy0xXQpzcGVha2VycyA8LSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoTWFjYmV0aF9yYXcsICIgezJ9W0EtWnwgfDAtOV0qXFwuICIpW1sxXV0KCkFsbE1hY2JldGggPC0gZGF0YS5mcmFtZShzcGVha2VycywgbGluZXMpCgpgYGAKCiMgQ2xlYW4gdXAgdGhlIGxpbmVzCgotIFE6IFdoYXQncyBsZWZ0IHRvIGNsZWFuIHVwPwogICAgLSBTdWdnZXN0aW9ucz8KCmBgYHtyfQpoZWFkKEFsbE1hY2JldGgpCmBgYAoKCiMgQ2xlYW4gdXAgdGhlIGxpbmVzCgotIGBnc3ViYCBpcyBoYW5keSBmb3IgImZpbmQgJiByZXBsYWNlIgotIGlmIHlvdSB3YW50IHRvIHNpbXBseSBjdXQgc29tZSBwYXR0ZXJuIG91dCBvZiB0aGUgdGV4dAogICAgLSBmaW5kIGl0IHdpdGggdGhlIHByb3BlciBSZWdFeAogICAgLSByZXBsYWNlIHdpdGggIiIgKHRoYXQncyBhIHBhdHRlcm4gd2l0aCBub3RoaW5nIGluIGl0ISkKCmBgYHtyfQpBbGxNYWNiZXRoIDwtIAogIEFsbE1hY2JldGggJT4lCiAgbXV0YXRlKGxpbmVzID0gZ3N1YihwYXR0ZXJuID0gIltccnxcbl0qIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IGxpbmVzKSkKCmhlYWQoQWxsTWFjYmV0aCkKYGBgCgojIE1vcmUgY2xlYW4gdXAKCi0gVGhlIGVuZCBvZiBlYWNoIEFjdCBzZWVtcyB0byBpbmNsdWRlIHNvbWV0aGluZyBsaWtlIHRoZSBmb2xsb3dpbmcgdGV4dC4uLgoKYDw8VEhJUyBFTEVDVFJPTklDIFZFUlNJT04gT0YgVEhFIENPTVBMRVRFIFdPUktTIE9GIFdJTExJQU1gCmBTSEFLRVNQRUFSRSBJUyBDT1BZUklHSFQgMTk5MC0xOTkzIEJZIFdPUkxEIExJQlJBUlksIElOQy4sIEFORCBJU2AKYFBST1ZJREVEIEJZIFBST0pFQ1QgR1VURU5CRVJHIEVURVhUIE9GIENBUk5FR0lFIE1FTExPTiBVTklWRVJTSVRZYApgV0lUSCBQRVJNSVNTSU9OLiAgRUxFQ1RST05JQyBBTkQgTUFDSElORSBSRUFEQUJMRSBDT1BJRVMgTUFZIEJFYApgRElTVFJJQlVURUQgU08gTE9ORyBBUyBTVUNIIENPUElFUyAoMSkgQVJFIEZPUiBZT1VSIE9SIE9USEVSU2AKYFBFUlNPTkFMIFVTRSBPTkxZLCBBTkQgKDIpIEFSRSBOT1QgRElTVFJJQlVURUQgT1IgVVNFRGAKYENPTU1FUkNJQUxMWS4gIFBST0hJQklURUQgQ09NTUVSQ0lBTCBESVNUUklCVVRJT04gSU5DTFVERVMgQlkgQU5ZYApgU0VSVklDRSBUSEFUIENIQVJHRVMgRk9SIERPV05MT0FEIFRJTUUgT1IgRk9SIE1FTUJFUlNISVAuPj5gCgotIFE6IFdoZXJlIGRvIHlvdSB0aGluayB0aGF0ICJqdW5rIiBsYW5kZWQgZHVlIHRvIG91ciBhcHByb2FjaCBzbyBmYXI/IAotIFE6IFdoYXQgc2hvdWxkIHdlIGRvPwotIFE6IElzIHRoZXJlIG1vcmUgImp1bmsiIGxpa2UgdGhpcz8KCgojIEFub3RoZXIgYGdzdWJgCgotIHdlJ3JlIG5vdCBnb2luZyB0byBjbGVhbiB1cCAqZXZlcnl0aGluZyosIGJ1dCB3ZSdsbCBkbyB0aGlzIG9uZSBtb3JlLi4uCi0gUTogQ29uc2lkZXIgUmVnRXggcGF0dGVybjogYCI8PC4qPj4iYAogICAgLSBXaGF0J3MgaXQgZG8/CiAgICAtIERvIHlvdSB0aGluayB0aGlzIHdpbGwgd29yaz8KICAgIC0gRG8gd2UgbmVlZCBhbiBlc2NhcGUgZm9yIHRoZSBzcGVjaWFsIGNoYXJhY3RlcnM/IGBcXGAKCmBgYHtyfQpqdW5rIDwtIGdyZXBsKHBhdHRlcm4gPSAiPDwuKj4+IiwgeCA9IEFsbE1hY2JldGgkbGluZXMpCgojIEJlZm9yZQpBbGxNYWNiZXRoICU+JQogIGZpbHRlcihqdW5rKSAlPiUKICBzZWxlY3Qoc3BlYWtlcnMsIGxpbmVzKQoKIyBDb3JyZWN0aW9uCkFsbE1hY2JldGggPC0gCiAgQWxsTWFjYmV0aCAlPiUKICBtdXRhdGUobGluZXMgPSBnc3ViKHBhdHRlcm4gPSAiPDwuKj4+IiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IGxpbmVzKSkKCgojIEFmdGVyCkFsbE1hY2JldGggJT4lCiAgZmlsdGVyKGp1bmspICU+JQogIHNlbGVjdChzcGVha2VycywgbGluZXMpCmBgYAoKPCEtLSBEYXkzIC0tPgoKIyBUaW1lIHRvIHRha2UgYSBsb29rIQoKLSBROiBIb3cgbWlnaHQgd2UgdXNlIG91ciByZXN1bHQgc28gZmFyIHRvIGNvdW50IGhvdyBtYW55IHdvcmRzIGFyZSBhdHRyaWJ1dGVkIHRvIGVhY2ggY2hhcmFjdGVyPwoKCiMgRmlyc3QgYXR0ZW1wdDogQ291bnRpbmcgd29yZHMKCi0gUTogSG93J3MgaXQgbG9vaz8gCj4tIFE6IEhvdyB3b3VsZCB5b3UgZGVidWcgdGhpcz8KCmBgYHtyfQpjb3VudFdvcmRzIDwtIGZ1bmN0aW9uKGxpbmUpIHsKICByZXR1cm4obGVuZ3RoKHN0cnNwbGl0KHggPSBsaW5lLCBzcGxpdCA9ICJcXHMrIikpKQp9CgpBbGxNYWNiZXRoJG5Xb3JkcyA8LSBzYXBwbHkoWCA9IEFsbE1hY2JldGgkbGluZXMsIEZVTiA9IGNvdW50V29yZHMpCgpoZWFkKEFsbE1hY2JldGgpCgpgYGAKCiMgRGVidWdnaW5nIG91ciBjbGVhbnVwCgotIFE6IEhvdydzIGl0IGxvb2s/Cj4tIFE6IFdoYXQgdHlwZSBvZiBvYmplY3QgaXMgdGhpcz8KCmBgYHtyfQpzdHJzcGxpdCh4ID0gQWxsTWFjYmV0aCRsaW5lc1sxXSwgc3BsaXQgPSAiXFxzKyIpCgpgYGAKCgojIERlYnVnZ2luZyBvdXIgY2xlYW51cAoKLSBROiBIb3cncyBpdCBsb29rPwoKYGBge3J9CmNvdW50V29yZHMgPC0gZnVuY3Rpb24obGluZSkgewogIHJldHVybihsZW5ndGgoc3Ryc3BsaXQoeCA9IGxpbmUsIHNwbGl0ID0gIlxccysiKVtbMV1dKSkKfQoKQWxsTWFjYmV0aCRuV29yZHMgPC0gc2FwcGx5KFggPSBBbGxNYWNiZXRoJGxpbmVzLCBGVU4gPSBjb3VudFdvcmRzKQoKaGVhZChBbGxNYWNiZXRoKQpgYGAKCgoKIyBDaG9vc2luZyB5b3VyIGNoYXJhY3RlcgoKLSBSZWNhbGw6IFlvdSB3YW50IHRvIAogICAgLSBpbXByZXNzIHNvbWVvbmUgaW4gVEhFQSAxMDIKICAgIC0gY2hvb3NlIGFuICJpbXBvcnRhbnQiIGNoYXJhY3RlcgogICAgLSBub3QgdG9vIG11Y2ggdG8gbWVtb3JpemUKLSBROiBXaGF0IGFyZSB5b3VyIHByaW9yaXRpZXM/CiAgICAtIE1vc3QgbGluZXM/CiAgICAtIExvbmdlc3QgbGluZXM/CiAgICAtIFdobyBoYXMgdGhlIG1vc3QgdG90YWwgY29udGVudCB0byBtZW1vcml6ZT8KICAgIC0gU29tZXRoaW5nIGVsc2U/Ci0gUTogSG93IHNob3VsZCB3ZSBmaWd1cmUgaXQgb3V0IGZyb20gaGVyZT8KCmBgYHtyfQojIExvbmdlc3QgaW5kaXZpZHVhbCBsaW5lcyAKQWxsTWFjYmV0aCAlPiUKICBzZWxlY3Qoc3BlYWtlcnMsIG5Xb3JkcywgbGluZXMpICU+JQogIGFycmFuZ2UoZGVzYyhuV29yZHMpKSAKCmBgYAoKIyBDaGFyYWN0ZXIgYW5hbHlzaXMKCi0gTW9zdCBsaW5lcz8KLSBMb25nZXN0IGxpbmVzPwotIFdobyBoYXMgdGhlIG1vc3QgdG90YWwgY29udGVudCB0byBtZW1vcml6ZT8KLSBTb21ldGhpbmcgZWxzZT8KCmBgYHtyfQpNYWNiZXRoU3VtbWFyeSA8LSAKICBBbGxNYWNiZXRoICU+JSAKICBncm91cF9ieShzcGVha2VycykgJT4lCiAgc3VtbWFyaXNlKGxpbmVzID0gbigpLCAKICAgICAgICAgICAgdG90YWxXb3JkcyA9IHN1bShuV29yZHMpLCAKICAgICAgICAgICAgd29yZHNQZXJMaW5lID0gdG90YWxXb3Jkcy9saW5lcykKCgojIE1vc3Qgd29yZHMgcGVyIGxpbmUKTWFjYmV0aFN1bW1hcnkgJT4lCiAgYXJyYW5nZShkZXNjKHdvcmRzUGVyTGluZSkpICU+JQogIGhlYWQoMTApCgoKIyBNb3N0IHRvdGFsIGNvbnRlbnQKTWFjYmV0aFN1bW1hcnkgJT4lCiAgYXJyYW5nZShkZXNjKHRvdGFsV29yZHMpKSAlPiUKICBoZWFkKDEwKQoKYGBgCgoKIyBXaG8gZG8geW91IGNob29zZT8KCi0gTGV0J3MgZ2V0IHlvdXIgbGluZXMhCgpgYGB7cn0KTXlMaW5lcyA8LSAKICBBbGxNYWNiZXRoICU+JSAKICBmaWx0ZXIoZ3JlcGwoIiAgTEFEWSBNQUNCRVRIXFwuIiwgc3BlYWtlcnMpKQoKaGVhZChNeUxpbmVzKQpgYGAKCgoKCiMgVGV4dCBNaW5pbmcgTWFjYmV0aAoKLSBJIHdhbnQgdG8gZGlnIGEgbGl0dGxlIGRlZXBlci4uLiAKLSAodHJhbnNsYXRpb246IEkgZm91bmQgbW9yZSAyIG1pbiBNYWNiZXRoIHZpZGVvcyEpCi0gKipROiBXaGF0IHdvdWxkIGJlIHNvbWUgaW50ZXJlc3RpbmcgcXVlc3Rpb25zIHRvIGV4cGxvcmUgd2hlbiB0ZXh0IG1pbmluZyBNYWNiZXRoPyoqCgoKIyBUZXh0IE1pbmluZyBNYWNiZXRoCgotIFNoYWtlc3BlYXJlYW4gcGxheXMgYXJlIGJlbGlldmVkIHRvIHNoYXJlIGEgY29tbW9uIGxpdGVyYXJ5IHN0cnVjdHVyZQotIEVhY2ggQUNUIGluIHRoZSBwbGF5IGhhcyBhIGRpZmZlcmVudCwgYnV0IGltcG9ydGFudCByb2xlIGluIHRoZSBkcmFtYQotICoqUHJvYmxlbSoqIHBlb3BsZSB0YWxrIHdlaXJkIGluIE1hY2JldGggKEknbSBraWRkaW5nLi4uIHNvcnQgb2YpCi0gTGV0J3MgZG8gc29tZSB0ZXh0IGFuYWx5c2lzIG9mIGEgIm1vZGVybiIgdHJhbnNsYXRpb24gb2YgdGhlIHBsYXkKLSA8aHR0cDovL3d3dy5ub3N3ZWF0c2hha2VzcGVhcmUuY29tL3NoYWtlc3BlYXJlcy1wbGF5cy9tb2Rlcm4tbWFjYmV0aC8+Ci0gUTogSG93IHdvdWxkIHlvdSBzb2x2ZSB0aGlzIHNvIHdlIGNvdWxkIHByb2Nlc3MgdGhlIHRleHQ/CgoKIyBTaWRlYmFyICJUaGUgR2VuZXJhbCBQcm9ibGVtIgoKLSBJIHB1bnRlZC4gIAotIEkgd2FzIGJ1c3kgd2l0aCBEYXRhRmVzdCwgc28gSSB0b29rIHRoZSBzbG9wcHkgd2F5IG91dAotIEkgbWFkZSBhIGRpcmVjdG9yeSB3aXRoIGV2ZXJ5IHNjZW5lIGluIHRoZSBwbGF5IGFzIGEgdGV4dCBmaWxlCiAgICAtIEkgdGhvdWdodCBhYm91dCBzb2x2aW5nIHRoZSAiZ2VuZXJhbCBwcm9ibGVtIgogICAgLSBJIGRlY2lkZWQgdG8gInBhc3MgdGhlIHNhbHQiIGFuZCBzaG93IGEgZmF2b3JpdGUgY29taWMgYWJvdXQgaXQgaW5zdGVhZAotIFE6IFdoeSBhbSBJIGNsYWltaW5nIHRoYXQgbXkgYXBwcm9hY2ggaXMgInNsb3BweSI/Ci0gUTogV2hhdCB3b3VsZCBiZSBhIG1vcmUgZWxlZ2FudCBzb2x1dGlvbj8gCgohW2h0dHBzOi8vbS54a2NkLmNvbS85NzQvXShnZW5lcmFsUHJvYmxlbV94a2NkOTU0LnBuZykKCgojIFRleHQgQW5hbHlzaXMKCi0gTm93LCBJJ3ZlIGdvdCBhIHdob2xlIGRpcmVjdG9yeSBmdWxsIG9mIHRleHQgZmlsZXMuCi0gSSB3YXMgYXQgbGVhc3Qgc21hcnQgZW5vdWdoIHRvIG5hbWUgdGhlbSBpbiBhIHN0cnVjdHVyZWQgd2F5OiAKICAgIC0gZGVkaWNhdGVkIGRpcmVjdG9yeSBsb2NhdGlvbgogICAgLSB1c2VmdWwgbmFtaW5nIGNvbnZlbnRpb246ICJhY3QxX3NjZW5lMS50eHQiCi0gYHJlYWR0ZXh0YCBjYW4gcmVhZCBhbGwgdGhlICIqLnR4dCIgZmlsZXMgaW4gdGhlIHJlcXVlc3RlZCBkaXJlY3RvcnkKICAgIC0gYGRvY3ZhcnNmcm9tYCBpbmRpY2F0ZXMgdGhhdCB0aGUgZmlsZSBuYW1lIGhhcyBzb21lIHZhcmlhYmxlcyBJIHdhbnQKICAgIC0gYGR2c2VwYCBpbmRpY2F0ZXMgdGhhdCB0aGV5IGFyZSBzZXBhcmF0ZWQgYnkgYW4gdW5kZXJzY29yZQotIHJlc3VsdCBpcyBhIGRhdGEgZnJhbWUgCgoKYGBge3J9CnJlcXVpcmUocmVhZHRleHQpCgpNb2Rlcm5NYWNiZXRoIDwtIAogIHJlYWR0ZXh0OjpyZWFkdGV4dChmaWxlID0gIi9Vc2Vycy9tYXR0YmVja21hbi9Eb2N1bWVudHMvR2l0SHViL1RlYWNoaW5nL1NUQVQtMzgwLzIwMTkgU3ByaW5nL0NsYXNzTm90ZXMvMTUtbWRzci9wbGFpbl9NYWNiZXRoLyoudHh0IiwgZG9jdmFyc2Zyb20gPSAiZmlsZW5hbWVzIiwgZHZzZXAgPSAiXyIpICU+JQogIGFzLnRpYmJsZSgpCgpoZWFkKE1vZGVybk1hY2JldGgpCgpgYGAKCjwhLS0gRGF5NCAtLT4KCiMgQ29ycHVzCgotIHdlJ3JlIG9mdGVuIGludGVyZXN0ZWQgaW4gKm1hbnkgZG9jdW1lbnRzKiB3aGVuIHRleHQgbWluaW5nIAotIGEgKipjb3JwdXMqKiBpcyBhIGNvbGxlY3Rpb24gb2YgbWFueSBkb2N1bWVudHMKLSBROiBJbiBvdXIgZXhhbXBsZTogCiAgICAtIHdoYXQgaXMgdGhlIGNvcnB1cz8KICAgIC0gd2hhdCBhcmUgdGhlIGRvY3VtZW50cz8KLSBROiBEZXNjcmliZSBhICJjb3JwdXMiIGFuZCB0aGUgYXNzb2NpYXRlZCAiZG9jdW1lbnRzIiBmb3IKICAgIC0gYSBkaWZmZXJlbnQgYW5hbHlzaXMgb2YgU2hha2VzcGVhcmU/CiAgICAtIHNvbWUgb3RoZXIgYXV0aG9yPwogICAgLSBvdGhlciBleGFtcGxlcz8gCgoKIyBNb2Rlcm4gTWFjYmV0aCBDb3JwdXMKCi0gYmVmb3JlIHdlIGNhbiByZWFsbHkgZGlnIGludG8gb3VyIHN0dWR5IG9mIHRoZSBkaWZmZXJlbmNlcyBhbW9uZyBhY3RzIGluIE1hY2JldGgsIHdlIG5lZWQgdG8gYnVpbGQgdXAgYSBjb3VwbGUgdGV4dCBhbmFseXNpcyB0b29scwotIHdlJ2xsIHVzZSB0aGUgZnVsbCB0ZXh0IG9mIE1vZGVybiBNYWNiZXRoIHRvIGdldCBzdGFydGVkCgoKCiMgVGlkeSBUZXh0IEZvcm1hdAoKLSBSZWNhbGw6IHRpZHkgZGF0YSBoYXMgYSBzcGVjaWZpYyBzdHJ1Y3R1cmUKICAgIC0gZWFjaCBjYXNlIGlzIGEgcm93CiAgICAtIGVhY2ggdmFyaWFibGUgaXMgYSBjb2x1bW4KLSAqKlRpZHkgdGV4dCoqIGNvdWxkIHNpbWlsYXJseSBiZSBkZXNjcmliZWQgYXMgYSB0YWJsZSB3aXRoICoqb25lIHRva2VuLXBlci1yb3cqKgotIGEgKip0b2tlbioqIGlzIGEgbWVhbmluZ2Z1bCB1bml0IG9mIHRleHQgdXNlZnVsIGZvciBhbmFseXNpcwogICAgLSB3b3JkICh2ZXJ5IGNvbW1vbikKICAgIC0gbi1ncmFtIChzZXF1ZW5jZSBvZiB3b3JkcykKICAgIC0gc2VudGVuY2UKICAgIC0gcGFyYWdyYXBoCi0gKip0b2tlbml6YXRpb24qKiBpcyB0aGUgcHJvY2VzcyBvZiBzcGxpdHRpbmcgdGV4dCBpbnRvIHRva2VucwoKIyBUb2tlbml6YXRpb24gc3RlcAoKLSBsZXQncyB0b2tlbml6ZSBNb2Rlcm4gTWFjYmV0aAotIHdlJ2xsIHVzZSBzb21lIHRvb2xzIGZyb20gdGhlIGB0aWR5dGV4dGAgcGFja2FnZQotIGB1bm5lc3RfdG9rZW5zYCBicmVha3MgdGhlIHRleHQgaW50byBpbmRpdmlkdWFsIHRva2Vucy4uLiBhdXRvbWF0aWNhbGx5CiAgICAtIGl0IGtlZXBzIHRoZSBvdGhlciBjb2x1bW5zLS1hY3QgJiBzY2VuZSBoZXJlCiAgICAtIHN0cmlwcyBwdW5jdHVhdGlvbgogICAgLSBjb252ZXJ0cyB0byBsb3dlcmNhc2UgKGRlZmF1bHQgYXJnOiBgdG9fbG93ZXIgPSBUUlVFYCkKCgpgYGB7cn0KcmVxdWlyZSh0aWR5dGV4dCkKCk1vZGVybk1hY2JldGggJT4lCiAgc2VsZWN0KHRleHQsIGFjdCA9IGRvY3ZhcjEsIHNjZW5lID0gZG9jdmFyMikgJT4lCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IHRleHQpICAgICAgICAgIyBzaW5nbGUgd29yZCB0b2tlbml6YXRpb24KICAjIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykgICAjIG4tZ3JhbSB0b2tlbml6YXRpb24KYGBgCgojIEJhZyBvZiB3b3JkcwoKLSBXZSdyZSB1c2luZyBhIHNpbXBsZSBhcHByb2FjaCB0byB0ZXh0IGFuYWx5c2lzIGNhbGxlZCBiYWcgb2Ygd29yZHMKLSAqKkJhZyBvZiB3b3JkcyoqIHByZXNlcnZlcyB0ZXJtIGZyZXF1ZW5jaWVzLCBidXQgZGlzcmVnYXJkcyB3b3JkIG9yZGVyLCBncmFtbWFyLCBldGMKCmBgYHtyfQpNb2Rlcm5NYWNiZXRoX3RpZHkgPC0gCiAgTW9kZXJuTWFjYmV0aCAlPiUKICBzZWxlY3QodGV4dCwgYWN0ID0gZG9jdmFyMSwgc2NlbmUgPSBkb2N2YXIyKSAlPiUKICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gdGV4dCkgICAgICAgICAjIHNpbmdsZSB3b3JkIHRva2VuaXphdGlvbgpgYGAKCgojIFRva2VuIGZyZXF1ZW5jeQoKLSBROiBXaGF0IGhhdmUgd2UgbGVhcm5lZCBhYm91dCBBY3QgMT8KLSBROiBXaGF0IHNob3VsZCB3ZSBkbyBuZXh0PwoKYGBge3J9Ck1vZGVybk1hY2JldGhfdGlkeSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkKYGBgCgoKIyBTdG9wIHdvcmRzCgotICoqc3RvcCB3b3JkcyoqIGFyZSB3b3JkcyB0aGF0IGFyZSBub3QgdXNlZnVsIGZvciBhbiBhbmFseXNpcwogICAgLSBleHRyZW1lbHkgY29tbW9uIHdvcmRzIGxpa2UgInRoZSIsICJhbmQiLCAidG8iLCBhcyBzaG93bgogICAgLSB3b3JkcyB0aGF0IGFyZW4ndCB1c2VmdWwgaW4gYW5hbHlzaXMgbGlrZSAiYWNjb3JkaW5nbHkiCi0gVGhlIGNvbnRlbnRzIG9mIHRoZSBzdG9wIHdvcmQgbGlzdCBtYXR0ZXJzIGEgbG90CiAgICAtIGB0aWR5dGV4dGAgcGFja2FnZSBpbmNsdWRlcyBhIGRhdGEgc2V0IGNhbGxlZCBgc3RvcF93b3Jkc2AgdG8gZ2V0IHlvdSBzdGFydGVkCiAgICAtIFRoZSBzdG9wIHdvcmRzIGFyZSBkaWZmZXJlbnQgZm9yIGVhY2ggbGFuZ3VhZ2UKICAgIC0gWW91IG1pZ2h0IGFkZC9tb2RpZnkgdGhlIHN0b3Agd29yZHMgYmFzZWQgb24gc3BlY2lhbGl6ZWQgZXhwZXJ0aXNlIHdpdGggdGhlIGNvbnRleHQgCi0gUTogV2hhdCBpZiB3ZSBoYWQgdXNlZCB0aGUgb3JpZ2luYWwgU2hha2VzcGVhcmU/CgpgYGB7cn0KIyBuYXRpdmUgdGV4dApNb2Rlcm5NYWNiZXRoX3RpZHkgJT4lCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCgojIGxvYWQgc3RvcCB3b3JkIGxpc3QgKEVuZ2xpc2gpCmRhdGEoInN0b3Bfd29yZHMiKSAgICMgZnJvbSBgdGlkeXRleHRgIHBhY2thZ2UKCmhlYWQoc3RvcF93b3JkcykKdGFpbChzdG9wX3dvcmRzKQpgYGAKCiMgUmVtb3Zpbmcgc3RvcCB3b3JkcwoKLSBmaWx0ZXIgb3V0IHN0b3Agd29yZHMKLSBlcXVpdmFsZW50bHksIHdlIGNvdWxkIHVzZSBhbiBgYW50aV9qb2luYCBoZXJlCi0gaXQgdHVybnMgb3V0IE1hY2JldGggaXMgYSBiaWcgZGVhbCBhbmQgaGUncyBhbGwgb3ZlciB0aGUgcGxheSAKICAgIC0gd2UgbWlnaHQgY29uc2lkZXIgcmVtb3ZpbmcgIm1hY2JldGgiIGZyb20gdGhlIGJhZyBvZiB3b3JkcwogICAgLSBROiBob3cgYWJvdXQgYW5vdGhlciBzdWdnZXN0aW9uPwogICAgICAgIC0gLi4ubWF5YmUgc29tZXRoaW5nIHRoYXQgc2VlbXMgbGlrZSBpdCBjbGVhcmx5IHNob3VsZCBoYXZlIGJlZW4gcmVtb3ZlZCBhbHJlYWR5Pz8KICAgICAgICAtIGlzIG91ciBsaXN0IG9mIHN0b3Agd29yZHMgcmVhbGx5IHRoYXQgYmFkPz8KCgpgYGB7cn0KIyAjIGBzdG9wX3dvcmRzYCBoYXMgdHdvIGNvbHVtbnM6ICJ3b3JkIiBhbmQgImxleGljb24KIyBzdG9wX3dvcmRzIDwtCiMgICByYmluZChzdG9wX3dvcmRzLCAgICAgICAgICAgCiMgICAgICAgICBjKCJtYWNiZXRoIiwgImN1c3RvbSIpKQojIAojIHN0b3Bfd29yZHMgJT4lCiMgICBmaWx0ZXIod29yZCA9PSAiaXQncyIpCiMgCiMgIyB3ZSdsbCBuZWVkIHRvIHVzZSBSZWdFeCB0byBjbGVhbiB0aGVzZSB1cAojIGdyZXAocGF0dGVybiA9ICJpdC5zIiwgeCA9IE1vZGVybk1hY2JldGhfdGlkeSR3b3JkLCB2YWx1ZSA9IFRSVUUpCiMgZ3JlcChwYXR0ZXJuID0gIuKAmSIsIHggPSBNb2Rlcm5NYWNiZXRoX3RpZHkkd29yZCwgdmFsdWUgPSBUUlVFKSAlPiUgaGVhZCgzMCkKCgpNb2Rlcm5NYWNiZXRoX3RpZHkgPC0gCiAgTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogICMgbXV0YXRlKHdvcmQgPSBnc3ViKHBhdHRlcm4gPSAsIHJlcGxhY2VtZW50ID0gLCB4ID0gd29yZCkpICU+JQogIGZpbHRlcighKHdvcmQgJWluJSBzdG9wX3dvcmRzJHdvcmQpKQoKTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQpgYGAKCgojIFRpZHkgdGV4dCBwaXBlcyB0byBgZ2dwbG90MmAKCi0gTGV0J3MgbWFrZSBhIGhpc3RvZ3JhbSBvZiB3b3JkIGZyZXF1ZW5jaWVzCi0gUTogV2hhdCdzIGdvaW5nIG9uIGluIEFjdCAxIG9mIE1hY2JldGg/CgoKYGBge3J9Ck1vZGVybk1hY2JldGhfdGlkeSAlPiUKICBtdXRhdGUod29yZCA9IGdzdWIocGF0dGVybiA9ICLigJkiLCByZXBsYWNlbWVudCA9ICInIiwgeCA9IHdvcmQpKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiAyMCkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIG4pKSArCiAgZ2VvbV9jb2woKSArCiAgeGxhYihOVUxMKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCgojIFdvcmQgY2xvdWRzCgotICoqd29yZCBjbG91ZHMqKiBhcmUgYW5vdGhlciBwb3B1bGFyIHZpc3VhbGl6YXRpb24gb2YgdG9rZW4gZnJlcXVlbmN5IChhbmFsb2dvdXMgdG8gb3VyIGhpc3RvZ3JhbSkKLSB0aGUgYHdvcmRjbG91ZGAgcGFja2FnZSB1c2VzIGJhc2UgUiBncmFwaGljcywgYnV0IHRoZSBgd2l0aCggKWAgZnVuY3Rpb24gaGVscHMKLSBROiBXaGF0J3MgTWFjYmV0aCBhYm91dD8KICAgIC0gV2hhdCBoYXZlIHdlIGxlYXJuZWQgZnJvbSB0aGlzPwogICAgLSBXaGF0IHNob3VsZCB3ZSBkbyBuZXh0PwoKYGBge3J9Ck1vZGVybk1hY2JldGhfdGlkeSAlPiUKICBmaWx0ZXIoISh3b3JkICVpbiUgc3RvcF93b3JkcyR3b3JkKSkgJT4lCiAgY291bnQod29yZCkgJT4lCiAgd2l0aCguLCB3b3JkY2xvdWQod29yZCwgbiwgbWF4LndvcmRzID0gNDUpKQoKYGBgCgoKCiMgd29yZCBjbG91ZHM6IE1vZGVybiB2cyBvcmlnaW5hbAoKLSBJJ3ZlIG1hZGUgYSBoZWxwZXIgZnVuY3Rpb24gdG8gcHJlcCB0aGUgY29ycHVzIAotIEkgYWxzbyBhZGRlZCAic2FpZCIgdG8gb3VyIHN0b3Agd29yZCBsaXN0CiAgICAtIGBjKHN0b3B3b3JkcygiZW5nbGlzaCIpLCAic2FpZCIpYAogICAgLSBzaG91bGQgIm1hY2JldGgiIGJlIGFkZGVkIHRvIHRoZSBsaXN0PwoKYGBge3J9CkFsbE1hY2JldGhfdGlkeSA8LSAKICBBbGxNYWNiZXRoICU+JQogIHNlbGVjdChzcGVha2VycywgbGluZXMpICU+JQogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSBsaW5lcykgICAgICMgc2luZ2xlIHdvcmQgdG9rZW5pemF0aW9uCgpoZWFkKEFsbE1hY2JldGhfdGlkeSkKYGBgCgoKYGBge3J9CiMgaGVscGVyIGZ1bmN0aW9uIGZvciBtYWtpbmcgd29yZGNsb3VkcwptYWNiZXRoX3dvcmRjbG91ZCA8LSBmdW5jdGlvbihDb3JwdXNfdGlkeSwgbWF4V29yZHMgPSA0NSkgewogIENvcnB1c190aWR5ICU+JQogICAgZmlsdGVyKCEod29yZCAlaW4lIHN0b3Bfd29yZHMkd29yZCkpICU+JQogICAgY291bnQod29yZCkgJT4lCiAgICB3aXRoKC4sIHdvcmRjbG91ZCh3b3JkLCBuLCBtYXgud29yZHMgPSBtYXhXb3JkcykpCgp9CgptYWNiZXRoX3dvcmRjbG91ZChDb3JwdXMgPSBBbGxNYWNiZXRoX3RpZHkpCm1hY2JldGhfd29yZGNsb3VkKENvcnB1cyA9IE1vZGVybk1hY2JldGhfdGlkeSkKCgpgYGAKCgojIEFjdCBieSBBY3Qgd29yZCBjbG91ZHMKCi0gUTogQ2FuIHdlIHRlbGwgYW55dGhpbmcgYWJvdXQgdGhlIHN0b3J5bGluZT8KLSBROiBIb3cgcmVsaWFibGUgYXJlIHdvcmQgY2xvdWRzIGFibGUgdG8gY29udmV5IG1lYW5pbmcgb2YgZWFjaCBBY3Q/CgpgYGB7cn0KCm1hY2JldGhfd29yZGNsb3VkKGZpbHRlcihNb2Rlcm5NYWNiZXRoX3RpZHksIGFjdCA9PSAiQWN0MSIpKQptYWNiZXRoX3dvcmRjbG91ZChmaWx0ZXIoTW9kZXJuTWFjYmV0aF90aWR5LCBhY3QgPT0gIkFjdDIiKSkKbWFjYmV0aF93b3JkY2xvdWQoZmlsdGVyKE1vZGVybk1hY2JldGhfdGlkeSwgYWN0ID09ICJBY3QzIikpCm1hY2JldGhfd29yZGNsb3VkKGZpbHRlcihNb2Rlcm5NYWNiZXRoX3RpZHksIGFjdCA9PSAiQWN0NCIpKQptYWNiZXRoX3dvcmRjbG91ZChmaWx0ZXIoTW9kZXJuTWFjYmV0aF90aWR5LCBhY3QgPT0gIkFjdDUiKSkKCgpgYGAKCjwhLS0gRGF5NSAtLT4KCiMgUmVjYWxsIAoKLSBIdW1hbnM6IAogICAgLSBleGNlbGxlbnQgYXQgKnVuZGVyc3RhbmRpbmcqIHRleHQKICAgIC0gbm90IGFzIGdvb2QgYXQgKnN0b3JpbmcqIHRleHQKLSBDb21wdXRlcnM6IAogICAgLSBleGNlbGxlbnQgYXQgKnN0b3JpbmcqIHRleHQKICAgIC0gbm90IGFzIGdvb2QgYXQgKnVuZGVyc3RhbmRpbmcqIHRleHQgCi0gSHVtYW4gKyBDb21wdXRlcj8/CiAgICAtIGV4Y2VsbGVudCBhdCB1bmRlcnN0YW5kaW5nICYgc3RvcmluZyB0ZXh0PwogICAgLSBub3Qgc28gZ29vZCBhdCBzdG9yaW5nICYgdW5kZXJzdGFuZGluZyB0ZXh0PwogICAgLSAoZGVwZW5kcyBvbiB0aGUgaHVtYW4gJiB0aGUgY29tcHV0ZXIuLi4/KQoKIyBTZW50aW1lbnQKCi0gdG9rZW4gZnJlcXVlbmNpZXMgYXJlIE9LLCBidXQgd29yZHMgZXZva2UgZW1vdGlvbgotIGh1bWFucyBhcmUgKHVzdWFsbHkpIGV4Y2VsbGVudCBhdCB1bmRlcnN0YW5kaW5nIGVtb3Rpb25zIAotIHdvcmQgY2hvaWNlIGNhbiBpbmRpY2F0ZSAKICAgIC0gZ2VuZXJhbCBwb3NpdGl2ZS9uZWdhdGl2ZSBlbW90aW9uCiAgICAtIHNwZWNpZmljIGVtb3Rpb25zIGxpa2Ugc3VycHJpc2Ugb3IgZGlzZ3VzdAotIGdvb2QgbmV3cyEKICAgIC0gcGVvcGxlIGhhdmUgY29tcGlsZWQgZGV0YWlsZWQgd29yZCBsaXN0cyBmb3IgdGhpcwogICAgLSBgdGlkeXRleHRgIHBhY2thZ2UgaW5jbHVkZXMgc2V2ZXJhbCBpbiB0aGUgYHNlbnRpbWVudHNgIGRhdGEgc2V0CgpgYGB7cn0KcmVxdWlyZSh0aWR5dGV4dCkKZGF0YSgic2VudGltZW50cyIpCgpoZWFkKHNlbnRpbWVudHMsIDEwKQp0YWlsKHNlbnRpbWVudHMsIDEwKQpgYGAKCgojIFNlbnRpbWVudCBsZXhpY29ucwoKLSBgc2VudGltZW50c2AgY29udGFpbnMgdGhyZWUgZ2VuZXJhbCBwdXJwb3NlIGxleGljb25zCiAgICAtIEFGSU5OLS1yYXRpbmcgZnJvbSAtNSAodmVyeSBuZWdhdGl2ZSkgdG8gKzUgKHZlcnkgcG9zaXRpdmUpCiAgICAtIGJpbmctLXBvc2l0aXZlL25lZ2F0aXZlIGNsYXNzaWZpY2F0aW9uCiAgICAtIG5yYy0tY2xhc3NpZmljYXRpb24gaW50byBjYXRlZ29yaWVzCiAgICAgICAgLSBwb3NpdGl2ZQogICAgICAgIC0gbmVnYXRpdmUKICAgICAgICAtIGZlYXIKICAgICAgICAtIHNhZG5lc3MKLSBgdGlkeXRleHRgIHByb3ZpZGVzIGZ1bmN0aW9uIGBnZXRfc2VudGltZW50cyggKWAgdG8gY2hvb3NlIGEgbGV4aWNvbgotIGxvdHMgb2Ygd29yZHMgYXJlIHF1aXRlIG5ldXRyYWwsIHNvIHRoZXkgY2FuIGJlIGV4Y2x1ZGVkIGZyb20gc2VudGltZW50IGxleGljb25zCi0gdGhlIHNlbnRpbWVudHMgaW4gdGhlc2UgbGV4aWNvbnMgbWlnaHQgYmUgY29uc3RydWN0ZWQgYnkgY3Jvd2Rzb3VyY2luZyBvciByZXNlYXJjaCwgdGhlbiB2YWxpZGF0ZWQgdXNpbmcgcmVzdGF1cmFudCwgbW92aWUsIG9yIGFtYXpvbiByZXZpZXdzCi0gUTogd2h5IG1pZ2h0IHdlIGhlc2l0YXRlIHRvIGFwcGx5IHRoZXNlIHNlbnRpbWVudCBsZXhpY29ucyB0byBTaGFrZXNwZWFyZSdzIGxpdGVyYXR1cmU/Ci0gUTogQW55IG90aGVyIGNvbmNlcm5zIHRoYXQgbWlnaHQgbWFrZSBpdCBoYXJkIHRvIGNhcHR1cmUgc2VudGltZW50PwogICAgLSBvbmUtd29yZCB0b2tlbnMKICAgIC0gZXh0cmVtZWx5IGxhcmdlIGNodW5rcyBvZiB0ZXh0IChhbGwgb2YgTWFjYmV0aCkKCmBgYHtyfQpnZXRfc2VudGltZW50cygibnJjIikgJT4lCiAgZ3JvdXBfYnkoc2VudGltZW50KSAlPiUKICBzdW1tYXJpc2UoTiA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKE4pKQpgYGAKCgoKIyBTZW50aW1lbnQgYW5hbHlzaXMgb2YgTWFjYmV0aAoKLSBzaW5jZSB3ZSdyZSB3b3JraW5nIHdpdGggdGlkeSBkYXRhLCB3ZSBjYW4gdXNlIGFuIGBpbm5lcl9qb2luYCBmb3Igc2VudGltZW50IGFuYWx5c2lzCi0gdGhpcyBjb252ZXJ0cyBzZXZlcmFsICJ0ZXh0IG1pbmluZyIgdGFza3MgaW50byBzaW1wbGUgdGlkeSBkYXRhIGFuYWx5c2lzIHRhc2tzCi0gbGV0J3MgaW52ZXN0aWdhdGUgc29tZSBjb21tb24gdGVybXMgaW4gTWFjYmV0aCB0aGF0IHN1Z2dlc3QgImFudGljaXBhdGlvbiIKCmBgYHtyfQpucmNfYW50aWNpcGF0aW9uIDwtIGdldF9zZW50aW1lbnRzKCJucmMiKSAlPiUKICBmaWx0ZXIoc2VudGltZW50ID09ICJhbnRpY2lwYXRpb24iKQoKTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIGlubmVyX2pvaW4obnJjX2FudGljaXBhdGlvbikgJT4lCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCiAgCmBgYAoKIyBDaGFuZ2VzIGluIFNlbnRpbWVudAoKLSBzbWFsbCBzZWN0aW9ucyBvZiB0ZXh0IG1heSBub3QgaGF2ZSBlbm91Z2ggd29yZHMgdG8gY29tbXVuaWNhdGUgc2VudGltZW50Ci0gZXh0cmVtZWx5IGxhcmdlIHNlY3Rpb25zIG1pZ2h0IGF2ZXJhZ2Ugb3V0IHRoZSBzZW50aW1lbnQgd2Ugd2FudCB0byBjYXB0dXJlCi0gZm9yIFNoYWtlc3BlYXJlJ3Mgb3JpZ2luYWwgdGV4dCwgd2UgbWlnaHQgY2hvb3NlIGJsb2NrcyBvZiA4MCBsaW5lcyBvciBzbwotIGZvciBNb2Rlcm4gTWFjYmV0aCwgd2UnbGwgY2hvb3NlIHdob2xlIHNjZW5lcyAocGVyaGFwcyBhIGJpdCBsYXJnZS92YXJpYWJsZSkKLSB3ZSdsbCBjYWxjdWxhdGUgYSBtZXRyaWMgdG8gYXNzZXNzIGVhY2ggc2NlbmUKICAgIC0gZnJvbSAtMTAwIHRvIDEwMCAKICAgIC0gMTAwIG1lYW5zIDEwMCUgcG9zaXRpdmUgc2VudGltZW50CgpgYGB7cn0KTW9kZXJuTWFjYmV0aF9zZW50aW1lbnQgPC0gCiAgTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIG11dGF0ZShhY3Rfc2NlbmUgPSBwYXN0ZShhY3QsIHNjZW5lLCBzZXAgPSAiXyIpKSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJhZmlubiIpKSAlPiUKICBncm91cF9ieShhY3QsIHNjZW5lKSAlPiUKICBzdW1tYXJpc2Uoc2VudGltZW50ID0gbWVhbihzY29yZSwgbmEucm0gPSBUUlVFKSkgCgpoZWFkKE1vZGVybk1hY2JldGhfc2VudGltZW50KQpgYGAKCgojIFBsb3QgY2hhbmdlcyBpbiBzZW50aW1lbnQgYnkgQWN0CgpgYGB7cn0KTW9kZXJuTWFjYmV0aF9zZW50aW1lbnQgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lCiAgbXV0YXRlKHJvd25hbWUgPSBwYXJzZV9udW1iZXIocm93bmFtZSkpICU+JQogIGdncGxvdChhZXMoeCA9IHJvd25hbWUsIHkgPSBzZW50aW1lbnQsIGZpbGwgPSBhY3QpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIGdndGl0bGUoIlNlbnRpbWVudCBBbmFseXNpcyBvZiBlYWNoIEFjdCAmIFNjZW5lIGluIE1vZGVybiBNYWNiZXRoIikKICAKYGBgCgoKIyBDb21tb24gcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHdvcmRzCgotIHdlIGhhdmUgYSB0aWR5IGRhdGEgZnJhbWUgd2l0aCBib3RoIHNlbnRpbWVudCBhbmQgd29yZAotIHdlIGNhbiBhbmFseXplIHdvcmQgY291bnRzIHRoYXQgY29udHJpYnV0ZSB0byBlYWNoIHNlbnRpbWVudAoKYGBge3J9CmJpbmdfd29yZF9jb3VudHMgPC0gCiAgTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkgJT4lCiAgdW5ncm91cCgpCgpiaW5nX3dvcmRfY291bnRzCmBgYAoKIyBQbG90dGluZyB3b3JkIGZyZXF1ZW5jeSBieSBzZW50aW1lbnQKCmBgYHtyfQpiaW5nX3dvcmRfY291bnRzICU+JQogIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lCiAgdG9wX24oMTApICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JQogIGdncGxvdChhZXMod29yZCwgbiwgZmlsbCA9IHNlbnRpbWVudCkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+c2VudGltZW50LCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGxhYnMoeSA9ICJDb250cmlidXRpb24gdG8gc2VudGltZW50IiwKICAgICAgIHggPSBOVUxMKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCiMgV29yZGNsb3VkcyB3aXRoICpmZWVsaW5nKgoKLSBvcmlnaW5hbGx5LCB3ZSBtYXkgaGF2ZSBoYWQgdHJvdWJsZSBkaXNjZXJuaW5nIHdoYXQgdGhlIHRleHQgd2FzIGFib3V0IGJlY2F1c2Ugc28gbWFueSBvZiB0aGUgY29tbW9uIHdvcmRzIGRvbid0IGludm9rZSBzZW50aW1lbnQgKGUuZywgTWFjZHVmZiwgQmFucXVvLCBNYWxjb2xtKQotIGhvdyBhYm91dCB3ZSByZXZpc2l0IG91ciB3b3JkY2xvdWQgYmFzZWQgb24gcG9zaXRpdmUvbmVnYXRpdmUgc2VudGltZW50IQogICAgLSB3ZSdsbCB1c2UgYHdvcmRjbG91ZDo6Y29tcGFyaXNvbi5jbG91ZCggKWAKICAgIC0gbm90ZTogd2UnbGwgbmVlZCBgYWNhc3QoKWAgdG8gdHVybiB0aGUgZGF0YSBmcmFtZSBpbnRvIGEgbWF0cml4CiAgICAtIFE6IEFueXRoaW5nIHN0cmFuZ2UgYWJvdXQgdGhpcyB3b3JkIGNsb3VkPwogICAgICAgIAoKYGBge3IgZXZhbD1GQUxTRX0KTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIGFudGlfam9pbihzdG9wX3dvcmRzKSAlPiUKICBjb3VudCh3b3JkKSAlPiUKICB3aXRoKHdvcmRjbG91ZCh3b3JkLCBuLCBtYXgud29yZHMgPSA0NSkpCmBgYAoKCgpgYGB7cn0KcmVxdWlyZShyZXNoYXBlMikKCk1vZGVybk1hY2JldGhfdGlkeSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JQogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIGFjYXN0KHdvcmQgfiBzZW50aW1lbnQsIHZhbHVlLnZhciA9ICJuIiwgZmlsbCA9IDApICU+JQogIGNvbXBhcmlzb24uY2xvdWQoY29sb3JzID0gYygiZ3JheTIwIiwgImdyYXk4MCIpLCBtYXgud29yZHMgPSA0NSkKYGBgCgoKCjwhLS0gIyBTZW50ZW5jZSB0b2tlbml6YXRpb24gKEFzaWRlKSAtLT4KCgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBNb2Rlcm5NYWNiZXRoX3RpZHlfc2VudCA8LSAgLS0+CjwhLS0gICBNb2Rlcm5NYWNiZXRoICU+JSAtLT4KPCEtLSAgIHNlbGVjdCh0ZXh0LCBhY3QgPSBkb2N2YXIxLCBzY2VuZSA9IGRvY3ZhcjIpICU+JSAtLT4KPCEtLSAgIHVubmVzdF90b2tlbnMob3V0cHV0ID0gc2VudGVuY2UsIGlucHV0ID0gdGV4dCwgdG9rZW4gPSAic2VudGVuY2VzIikgJT4lICMgc2VudGVuY2UgdG9rZW5pemF0aW9uIC0tPgo8IS0tICAgbXV0YXRlKHNlbnRlbmNlID0gZ3N1YihwYXR0ZXJuID0gIuKAmXzigJgiLCByZXBsYWNlbWVudCA9ICInIiwgeCA9IHNlbnRlbmNlKSkgICMgZml4IHF1b3RlcyAtLT4KCjwhLS0gTW9kZXJuTWFjYmV0aF90aWR5X3NlbnQkc2VudGVuY2UgJT4lIGhlYWQoKSAtLT4KPCEtLSBgYGAgLS0+CgoKIyBQb3NpdGl2ZSBzY2VuZXMgaW4gTWFjYmV0aAoKLSB3ZSB3YW50IHRvIGZpbmQgdGhlIG1vc3QgcG9zaXRpdmUgc2NlbmUgaW4gZWFjaCBhY3QKLSB0aGUgcmF0aW9zIGFyZW4ndCBnb29kCi0gaW4gdGhlIGFjdHVhbCB0ZXh0LCB0aGluZ3MgZ28gcHJldHR5IHNvdXRoIHByZXR0eSBmYXN0Li4uCiAgICAtIEFjdCAxLCBTY2VuZSA2OiBEdW5jYW4gJiBCYW5xdW8gb24gYSBsZWlzdXJlbHkgcmlkZSB0aHJvdWdoIHRoZSBjb3VudHJ5c2lkZSAoZ2VudWluZWx5IHBsZWFzYW50KQogICAgLSBBY3QgMiwgU2NlbmUgMTogTWFjYmV0aCB0cnlpbmcgdG8gYWN0IG5hdHVyYWwgZm9yIEJhbnF1byAod2hpbGUgcGxhbm5pbmcgdG8ga2lsbCBEdW5jYW4pCiAgICAtIEFjdCAzLCBTY2VuZSAyOiBNYWNiZXRoIHdpc2hpbmcgaGUgd2FzIGRlYWQgbGlrZSBEdW5jYW4gCiAgICAtIEFjdCA0LCBTY2VuZSAyOiBzdXBlciBsb25nLCBJIGRpZG4ndCByZWFkIGl0IGFsbAogICAgLSBBY3QgNSwgU2NlbmUgNjogTWFsY29sbSAmIE1hY2R1ZmYgcHN5Y2ggdGhlbXNlbHZlcyB1cCB0byBraWxsIE1hY2JldGgKCmBgYHtyfQpiaW5ncG9zaXRpdmUgPC0gZ2V0X3NlbnRpbWVudHMoImJpbmciKSAlPiUgCiAgZmlsdGVyKHNlbnRpbWVudCA9PSAicG9zaXRpdmUiKQoKd29yZGNvdW50cyA8LSBNb2Rlcm5NYWNiZXRoX3RpZHkgJT4lCiAgZ3JvdXBfYnkoYWN0LCBzY2VuZSkgJT4lCiAgc3VtbWFyaXplKHdvcmRzID0gbigpKQoKTW9kZXJuTWFjYmV0aF90aWR5ICU+JQogIHNlbWlfam9pbihiaW5ncG9zaXRpdmUpICU+JQogIGdyb3VwX2J5KGFjdCwgc2NlbmUpICU+JQogIHN1bW1hcml6ZShwb3NpdGl2ZXdvcmRzID0gbigpKSAlPiUKICBsZWZ0X2pvaW4od29yZGNvdW50cywgYnkgPSBjKCJhY3QiLCAic2NlbmUiKSkgJT4lCiAgbXV0YXRlKHJhdGlvID0gcG9zaXRpdmV3b3JkcyAvIHdvcmRzKSAlPiUgICAgICAgICAjIHJhdGlvIG9mIHBvc2l0aXZlIHdvcmRzCiAgZmlsdGVyKHNjZW5lICE9IDApICU+JQogIHRvcF9uKDEpICU+JQogIHVuZ3JvdXAoKQpgYGAKCgojIEFuYWx5c2lzIG9mIHdvcmQgJiBkb2N1bWVudCBmcmVxdWVuY3kKCi0gYSBjZW50cmFsIHF1ZXN0aW9uIGluIG5hdHVyYWwgbGFuZ3VhZ2UgcHJvY2Vzc2luZyBpcyBob3cgdG8gcXVhbnRpZnkgd2hhdCBhIGRvY3VtZW50IGlzIGFib3V0Ci0gb25lIGFwcHJvYWNoIGFzIHdlIGhhdmUgZGlzY3Vzc2VkIGlzIHNpbXBseSB0ZXJtIGZyZXF1ZW5jeSAKICAgIC0gdGhpcyBtYXkgZHJhdyBhdHRlbnRpb24gY29tbW9uIHdvcmRzIHJlZ2FyZGxlc3Mgb2YgaW1wb3J0YW5jZQogICAgLSBzdG9wIHdvcmQgcmVtb3ZhbCBhdHRlbXB0cyB0byBhZGp1c3RpbmcgdGVybSBmcmVxdWVuY3kgZm9yIGNvbW1vbiB1c2VkIHdvcmRzCiAgICAtIHRoaXMgaXMgYSBmYWlybHkgY3J1ZGUgYXBwcm9hY2g7IHBlcmhhcHMgd2UgY2FuIGRvIGJldHRlcgoKIyBEb2N1bWVudCB0ZXJtIG1hdHJpY2VzCgotIGFsdGVybmF0aXZlIGFwcHJvYWNoIGZvciBhc3Nlc3NpbmcgdGVybSBmcmVxdWVuY3kgaXMgdGhlICoqZG9jdW1lbnQtdGVybSBtYXRyaXgqKgogICAgLSBzcGFyc2UgbWF0cml4IGRlc2NyaWJpbmcgYSBjb2xsZWN0aW9uIChjb3JwdXMpIG9mIGRvY3VtZW50cwogICAgLSByb3cgZm9yIGVhY2ggZG9jdW1lbnQKICAgIC0gY29sdW1uIGZvciBlYWNoIHRlcm0KICAgIC0gdmFsdWVzIGFyZSB0eXBpY2FsbHkgd29yZCBjb3VudCBvciAidGYtaWRmIiAoc2VlIGJlbG93KQotICoqdGVybSBmcmVxdWVuY3kqKi0tJHRmKHQsIGQpJC0taXMgc2ltcGx5IHRoZSBmcmVxdWVuY3kgb2YgdGVybSAkdCQgaW4gZG9jdW1lbnQgJGQkCi0gKippbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSoqLS0kaWRmJC0taXMgcHJldmFsZW5jZSBvZiB0ZXJtICR0JCBhY3Jvc3MgYSBzZXQgb2YgZG9jdW1lbnRzICREJAogICAgLSB3b3JkcyB0aGF0IGFyZSBjb21tb24gYWNyb3NzIG1hbnkgZG9jdW1lbnRzIGxpa2UgInRoZSIgZ2V0IGxvdyByYW5rCiAgICAtIG51bWVyYXRvciBpcyB0aGUgbnVtYmVyIG9mIGRvY3VtZW50cyBpbiAkRCQKICAgIC0gZGVub21pbmF0b3IgaXMgdGhlIG51bWJlciBvZiBkb2N1bWVudHMgaW4gJEQkIGNvbnRhaW5pbmcgdGhlIHRlcm0KLSAkdGYtaWRmJCBtdWx0aXBsaWVzIHRoZSB0d28KICAgIC0gZnJlcXVlbmN5IG9mIGEgdGVybSBhZGp1c3RlZCBmb3IgaG93IHJhcmVseSBpdCBpcyB1c2VkCiAgICAtIHJvdWdobHkgbWVhc3VyZXMgaG93IGltcG9ydGFudCBhIHdvcmQgaXMgdG8gYSBkb2N1bWVudCBpbiBhIGNvbGxlY3Rpb24gKG9yIGNvcnB1cykgb2YgZG9jdW1lbnRzCgpcW2lkZih0LCBEKSA9IFx0ZXh0e2xufVxmcmFje3xEfH17fFx7ZCBcaW4gRCA6IHQgXGluIGRcfXx9XF0KCgojIFRlcm0gZnJlcXVlbmN5IGluIEFjdHMgb2YgTWFjYmV0aAoKLSBXaGF0IGFyZSB0aGUgbW9zdCBjb21tb25seSB1c2VkIHdvcmRzIGluIGVhY2ggQWN0PyAKICAgIC0gdGVybSBmcmVxdWVuY3kKICAgIC0gJHRmLWlkZiQKLSB3ZSB3aWxsICJzdGFydCBvdmVyIiBmcm9tIHRoZSBzb3VyY2UgKHdpdGhvdXQgcmVtb3Zpbmcgc3RvcCB3b3JkcykKLSBlYWNoIHJvdyBpbiBgYWN0X3dvcmRzYCBpcyBhIHVuaXF1ZSB3b3JkLWFjdCBjb21iaW5hdGlvbgogICAgLSBgbmAgaXMgdGhlIG51bWJlciBvZiB0aW1lcyB0aGF0IHdvcmQgaXMgdXNlZCBpbiB0aGF0IGFjdCAKICAgIC0gYHRvdGFsYCBpcyB0aGUgdG90YWwgd29yZHMgaW4gdGhlIGFjdAoKYGBge3J9CiMgdGYgd2l0aGluIGVhY2ggYWN0CmFjdF93b3JkcyA8LSAKICBNb2Rlcm5NYWNiZXRoICU+JQogIHNlbGVjdCh0ZXh0LCBhY3QgPSBkb2N2YXIxLCBzY2VuZSA9IGRvY3ZhcjIpICU+JQogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSB0ZXh0KSAlPiUKICBtdXRhdGUod29yZCA9IGdzdWIocGF0dGVybiA9ICLigJkiLCByZXBsYWNlbWVudCA9ICInIiwgeCA9IHdvcmQpKSAlPiUKICBjb3VudChhY3QsIHdvcmQsIHNvcnQgPSBUUlVFKQoKIyB0b3RhbCB3b3JkcyBpbiB0aGUgYWN0CnRvdGFsX3dvcmRzIDwtIAogIGFjdF93b3JkcyAlPiUgCiAgZ3JvdXBfYnkoYWN0KSAlPiUgCiAgc3VtbWFyaXplKHRvdGFsID0gc3VtKG4pKQoKYWN0X3dvcmRzIDwtIGxlZnRfam9pbihhY3Rfd29yZHMsIHRvdGFsX3dvcmRzKQphY3Rfd29yZHMKYGBgCgoKIyBUZXJtIGZyZXF1ZW5jeSBpbiBNYWNiZXRoCgotIGxldOKAmXMgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGBuIC8gdG90YWxgIGZvciBlYWNoIGFjdAotIHRoaXMgaXMgdGhlIHRlcm0gZnJlcXVlbmN5LCAkdGYkIAotIHZlcnkgbG9uZyByaWdodCB0YWlscyAodGhvc2UgZXh0cmVtZWx5IGNvbW1vbiB3b3JkcykgCi0gVGhlc2UgcGxvdHMgZXhoaWJpdCBzaW1pbGFyIGRpc3RyaWJ1dGlvbnMgZm9yIGFsbCB0aGUgYWN0cyAKICAgIC0gbWFueSB3b3JkcyB0aGF0IG9jY3VyIHJhcmVseQogICAgLSBmZXdlciB3b3JkcyB0aGF0IG9jY3VyIGZyZXF1ZW50bHkKCgpgYGB7cn0KYWN0X3dvcmRzICU+JSAKICBnZ3Bsb3QoYWVzKG4gLyB0b3RhbCwgZmlsbCA9IGFjdCkpICsKICBnZW9tX2hpc3RvZ3JhbShzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IGFjdCwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlX3kiKSArIAogIGdndGl0bGUoIlRlcm0gZnJlcXVlbmN5IGluIEFjdHMgb2YgTWFjYmV0aCIpCmBgYAoKCgojIFppcGYncyBsYXcKCi0gbG9uZy10YWlsZWQgZGlzdHJpYnV0aW9ucyBhcmUgY29tbW9uIGluIGFueSBnaXZlbiBjb3JwdXMgb2YgbmF0dXJhbCBsYW5ndWFnZSAobGlrZSBhIGJvb2ssIG9yIGEgbG90IG9mIHRleHQgZnJvbSBhIHdlYnNpdGUsIG9yIHNwb2tlbiB3b3JkcykgCi0gYSBjbGFzc2ljIHZlcnNpb24gb2YgdGhpcyByZWxhdGlvbnNoaXAgaXMgY2FsbGVkIFppcGbigJlzIGxhdywgYWZ0ZXIgR2VvcmdlIFppcGYsIGEgMjB0aCBjZW50dXJ5IEFtZXJpY2FuIGxpbmd1aXN0LgotICoqWmlwZuKAmXMgbGF3Kiogc3RhdGVzIHRoYXQgdGhlIGZyZXF1ZW5jeSB0aGF0IGEgd29yZCBhcHBlYXJzIGlzIGludmVyc2VseSBwcm9wb3J0aW9uYWwgdG8gaXRzIHJhbmsuCgpgYGB7cn0KZnJlcV9ieV9yYW5rIDwtIAogIGFjdF93b3JkcyAlPiUgCiAgZ3JvdXBfYnkoYWN0KSAlPiUgCiAgbXV0YXRlKHJhbmsgPSByb3dfbnVtYmVyKCksICAgIyBzaW5jZSBhbHJlYWR5IG9yZGVyZWQgYnkgYG5gCiAgICAgICAgIGB0ZXJtIGZyZXF1ZW5jeWAgPSBuIC8gdG90YWwpCgpmcmVxX2J5X3JhbmsKYGBgCgoKCiMgVmlzdWFsaXphdGlvbiBvZiBaaXBmJ3MgbGF3CgotIFppcGbigJlzIGxhdyB2aXN1YWxpemVkIGJ5IHBsb3R0aW5nIChvbiBsb2dhcml0aG1pYyBzY2FsZXMpCiAgICAtIHgtYXhpczogYHJhbmtgIG9mIGVhY2ggd29yZCB3aXRoaW4gdGhlIGZyZXF1ZW5jeSB0YWJsZQogICAgLSB5LWF4aXM6IGB0ZXJtIGZyZXF1ZW5jeWAgb24gdGhlIHktYXhpcwogICAgLSByZXN1bHQ6IGludmVyc2VseSBwcm9wb3J0aW9uYWwgcmVsYXRpb25zaGlwIGhhcyBhIGNvbnN0YW50LCBuZWdhdGl2ZSBzbG9wZQotIHRoaXMgdHlwZSBvZiByZXN1bHQgaXMga25vd24gYXMgYSAqKnBvd2VyIGxhdyoqIDxodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Qb3dlcl9sYXc+CiAgICAtIHJlbGF0aXZlIGNoYW5nZSBpbiBvbmUgcXVhbnRpdHkgcmVzdWx0cyBpbiBhIHByb3BvcnRpb25hbCByZWxhdGl2ZSBjaGFuZ2UgaW4gdGhlIG90aGVyIHF1YW50aXR5LCBpbmRlcGVuZGVudCBvZiB0aGUgaW5pdGlhbCBzaXplIG9mIHRob3NlIHF1YW50aXRpZXMKICAgIC0gb25lIHF1YW50aXR5IHZhcmllcyBhcyBhIHBvd2VyIG9mIGFub3RoZXIKICAgIC0gYXJlYSBvZiBhIHNxdWFyZSB2cyBsZW5ndGggb2Ygc2lkZTsgMiB0aW1lcyBsZW5ndGggPj4gJDJeMiQgdGltZXMgdGhlIGFyZWEKCgpgYGB7cn0KZnJlcV9ieV9yYW5rICU+JSAKICBnZ3Bsb3QoYWVzKHJhbmssIGB0ZXJtIGZyZXF1ZW5jeWAsIGNvbG9yID0gYWN0KSkgKyAKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMSwgYWxwaGEgPSAwLjgsIHNob3cubGVnZW5kID0gVFJVRSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArIAogIGdndGl0bGUoIlppcGYncyBsYXcgZm9yIGFjdHMgb2YgTWFjYmV0aCIpCmBgYAoKCiMgSW52ZXN0aWdhdGluZyB0ZXJtIGZyZXF1ZW5jeSBpbiBNYWNiZXRoCgotIHJlc3VsdCB3YXNuJ3QgKnF1aXRlKiBsaW5lYXIKLSBkZXZpYXRpb25zIGF0IGhpZ2ggcmFuayBhcmUgbm90IHVuY29tbW9uIGZvciBtYW55IGtpbmRzIG9mIGxhbmd1YWdlOyAKICAgIC0gYSBjb3JwdXMgb2Z0ZW4gY29udGFpbnMgZmV3ZXIgcmFyZSB3b3JkcyB0aGFuIHByZWRpY3RlZCBieSBhIHNpbmdsZSBwb3dlciBsYXcuIAotIGRldmlhdGlvbnMgYXQgbG93IHJhbmsgYXJlIG1vcmUgdW51c3VhbCAKICAgIC0gTW9kZXJuIE1hY2JldGggdXNlZCBhIGxvd2VyIHBlcmNlbnRhZ2Ugb2YgdGhlIG1vc3QgY29tbW9uIHdvcmRzIHRoYW4gbWFueSBjb2xsZWN0aW9ucyBvZiBsYW5ndWFnZS4KICAgIC0gYW5hbHlzaXMgY291bGQgYmUgZXh0ZW5kZWQgdG8gY29tcGFyZSBhdXRob3JzLCBvciBvdGhlciBjb2xsZWN0aW9ucyBvZiB0ZXh0IChzb25uZXRzIHZzIHRyYWdlZGllcykKCgpgYGB7cn0KcmFua19zdWJzZXQgPC0gZnJlcV9ieV9yYW5rICU+JSAKICBmaWx0ZXIocmFuayA8IDIwMCwKICAgICAgICAgcmFuayA+IDEwKQoKbG0obG9nMTAoYHRlcm0gZnJlcXVlbmN5YCkgfiBsb2cxMChyYW5rKSwgZGF0YSA9IHJhbmtfc3Vic2V0KQoKZnJlcV9ieV9yYW5rICU+JSAKICBnZ3Bsb3QoYWVzKHJhbmssIGB0ZXJtIGZyZXF1ZW5jeWAsIGNvbG9yID0gYWN0KSkgKyAKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAtMC45NDYsIHNsb3BlID0gLTAuOTA5LCBjb2xvciA9ICJncmF5NTAiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMSwgYWxwaGEgPSAwLjgsIHNob3cubGVnZW5kID0gVFJVRSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKSArIAogIGdndGl0bGUoIlppcGYncyBsYXcgZm9yIE1vZGVybiBNYWNiZXRoIikKYGBgCgo8IS0tIERheTYgLS0+CgojICp0Zi1pZGYqCgotICAqKmlkZWEgb2YgKnRmLWlkZioqKjogZmluZCBpbXBvcnRhbnQgd29yZHMgZm9yIHRoZSBjb250ZW50IG9mIGVhY2ggZG9jdW1lbnQgYnkKICAgIC0gKmRlY3JlYXNpbmcqIHRoZSB3ZWlnaHQgZm9yIGNvbW1vbmx5IHVzZWQgd29yZHMgCiAgICAtICppbmNyZWFzaW5nKiB0aGUgd2VpZ2h0IGZvciB3b3JkcyB0aGF0IGFyZSBub3QgdXNlZCB2ZXJ5IG11Y2ggaW4gdGhlIGNvcnB1cyBvZiBkb2N1bWVudHMKLSBDYWxjdWxhdGluZyAkdGYtaWRmJCBhdHRlbXB0cyB0byBmaW5kIHRoZSB3b3JkcyB0aGF0IGFyZSBpbXBvcnRhbnQgKGkuZS4sIGNvbW1vbikgaW4gYSB0ZXh0LCBidXQgbm90IHRvbyBjb21tb24gYWNyb3NzIGFsbCB0ZXh0cy4KLSBgYmluZF90Zl9pZGYoIClgIGZ1bmN0aW9uIGluIHRoZSBgdGlkeXRleHRgIHBhY2thZ2UgdGFrZXMgYSB0aWR5IHRleHQgZGF0YXNldCBhcyBpbnB1dAogICAgLSBvbmx5IG5lZWQgb25lIHJvdyBwZXIgdG9rZW4gKHRlcm0pLCBwZXIgZG9jdW1lbnQKICAgIC0gY29sdW1uIGZvciB0aGUgd29yZHMsIGFub3RoZXIgaWRlbnRpZnlpbmcgc291cmNlIGRvY3VtZW50IChhY3QpCiAgICAtIHdlIGNhbGN1bGF0ZWQgYHRvdGFsYCBmb3IgZWFjaCBhY3QgcHJldmlvdXNseSwgYnV0IGRvbid0IG5lZWQgaXQKLSAkaWRmJAogICAgLSB6ZXJvIGZvciB3b3JkcyB0aGF0IGFyZSBjb21tb24gYWNyb3NzIGFsbCBkb2N1bWVudHMgKCR0Zi1pZGYkIGlzIHRoZW4gemVybyB0b28pCiAgICAtIGhpZ2hlciBmb3Igd29yZHMgdGhhdCBhcHBlYXIgaW4gZmV3ZXIgZG9jdW1lbnRzCgoKYGBge3J9CmFjdF93b3JkcyA8LSAKICBhY3Rfd29yZHMgJT4lCiAgYmluZF90Zl9pZGYod29yZCwgYWN0LCBuKQoKYWN0X3dvcmRzCmBgYAoKIyBIaWdoICp0Zi1pZGYqIGluIE1hY2JldGgKCi0gY29tbW9uIHRvIHNlZSBwcm9wZXIgbm91bnMgKHBlb3BsZS9wbGFjZXMpIGZyb20gZWFjaCBkb2N1bWVudCB3aXRoIHRoZSBoaWdoZXN0ICp0Zi1pZGYqCi0gaW50ZXJlc3RpbmcgaGVyZSB0byBzZWUgb3RoZXIgZmVhdHVyZWQgdG9rZW5zIChjYXVsZHJvbiwgaGFpbCwgZXRjKSEgCi0gbm90ZTogZGlzY3JldGVuZXNzIGluIHRoZSAkaWRmJCBoZXJlIGJlY2F1c2Ugd2UgaGF2ZSBvbmx5IDUgZG9jdW1lbnRzCiAgICAtIHRlcm0gdGhhdCBhcHBlYXJzIGluIG9ubHkgb25lIG9mIHRoZSBmaXZlIGFjdHM6ICRpZGYgPSBsbig1LzEpID0gMS42MDk0JAoKYGBge3J9CmFjdF93b3JkcyAlPiUKICBzZWxlY3QoLXRvdGFsKSAlPiUKICBhcnJhbmdlKGRlc2ModGZfaWRmKSkKYGBgCgoKIyBWaXN1YWxpemluZyBoaWdoICp0Zi1pZGYqIHdvcmRzCgpgYGB7cn0KYWN0X3dvcmRzICU+JQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKSAlPiUKICBtdXRhdGUod29yZCA9IGZhY3Rvcih3b3JkLCBsZXZlbHMgPSByZXYodW5pcXVlKHdvcmQpKSkpICU+JSAKICBncm91cF9ieShhY3QpICU+JSAKICB0b3BfbigxMCkgJT4lIAogIHVuZ3JvdXAoKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIHRmX2lkZiwgZmlsbCA9IGFjdCkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJ0Zi1pZGYiKSArCiAgZmFjZXRfd3JhcCh+YWN0LCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCgoKIyBOb3RlIGFib3V0IGNvbnZlcnRpbmcgdG8vZnJvbSBvdGhlciBub24tdGlkeSBmb3JtYXRzCgotIHRoZXJlIGFyZSBsb3RzIG9mIHBhY2thZ2VzIGZvciB0ZXh0IGFuYWx5c2lzLCBlLmcuLCAKICAgIC0gYHF1YW50ZWRhYAogICAgLSBgdG1gCi0gdGhlc2Ugb2Z0ZW4gcmVseSBvbiBhIGNvbW1vbiAobm9uLXRpZHkpIGRhdGEgc3RydWN0dXJlIGNhbGxlZCBhICJkb2N1bWVudC10ZXJtIG1hdHJpeCIKLSAqKkRvY3VtZW50LXRlcm0gbWF0cml4IChEVE0pKiogaXMgYSBtYXRyaXggc3VjaCB0aGF0CiAgICAtIGVhY2ggKnJvdyogcmVwcmVzZW50cyBvbmUgZG9jdW1lbnQgKGJvb2ssIGFydGljbGUsIHR3ZWV0LCBldGMpCiAgICAtIGVhY2ggKmNvbHVtbiogcmVwcmVzZW50cyBvbmUgdGVybQogICAgLSBlYWNoIHZhbHVlICh0eXBpY2FsbHkpIGNvbnRhaW5zIGZyZXF1ZW5jeSBvZiB0aGUgdGVybSBpbiB0aGUgZG9jdW1lbnQKCgojIERvY3VtZW50LXRlcm0gbWF0cml4CgotIERUTSdzIGFyZSB1c3VhbGx5IHZlcnkgc3BhcnNlIHNpbmNlIGxvdHMgb2YgdGVybS1kb2N1bWVudCBwYWlycyBkb24ndCBvY2N1cgotIERUTSdzIGRvbid0IHBsYXkgbmljZSB3aXRoIHRpZHkgdG9vbHMgJiBkYXRhIGZyYW1lcyBkb24ndCBwbGF5IG5pY2Ugd2l0aCBtb3N0IHRleHQgbWluaW5nIHBhY2thZ2VzCiAgICAtIGB0aWR5dGV4dGAgcGFja2FnZSBoYXMgdG9vbHMgdG8gZWFzaWx5IGNvbnZlcnQgYmV0d2VlbiBvYmplY3QgdHlwZXMKICAgIC0gYHRpZHkoKWAgdHVybnMgYSBEVE0gaW50byBhIHRpZHkgZGF0YSBmcmFtZQogICAgLSBgY2FzdCgpX2R0bWAgdHVybnMgYSB0aWR5LCBvbmUtdGVybS1wZXItcm93LCBkYXRhIGZyYW1lIGludG8gYSBEVE0gKGZvciB0b29scyBpbiBgdG1gIHBrZykKICAgIC0gYGNhc3QoKV9kdG1gICh3aXRoIGEgIlQiKSB0dXJucyBhIHRpZHksIG9uZS10ZXJtLXBlci1yb3csIGRhdGEgZnJhbWUgaW50byBhIERUTSAoZm9yIHRvb2xzIGluIGB0bWAgcGtnKQogICAgLSBgY2FzdCgpX2RmbWAgKHdpdGggYW4gIkYiKSB0dXJucyBhIHRpZHksIG9uZS10ZXJtLXBlci1yb3csIGRhdGEgZnJhbWUgaW50byBhIERGTSAoZm9yIHRvb2xzIGluIGBxdWFudGVkYWAgcGtnKQoKCgojIFRvcGljIE1vZGVsaW5nCgotIHNvbWV0aW1lcyB1c2VmdWwgdG8gZmluZCBuYXR1cmFsIGdyb3VwcyBhbW9uZyBkb2N1bWVudHMgb2Ygc29tZSBjb2xsZWN0aW9uL2NvcnB1cyAoYmxvZyBwb3N0cywgd2lraXBlZGlhIHBhZ2VzLCBldGMpIAotICoqVG9waWMgbW9kZWxpbmcqKi0tbWV0aG9kIGZvciB1bnN1cGVydmlzZWQgY2xhc3NpZmljYXRpb24gb2YgZG9jdW1lbnRzCiAgICAtIGZpbmQgbmF0dXJhbCBncm91cHMgZXZlbiBpZiB3ZSdyZSBub3QgdG90YWxseSBzdXJlIHdoYXQgdG8gbG9vayBmb3IKICAgIC0gc2ltaWxhciB0byBjbHVzdGVyaW5nIG9uIG51bWVyaWMgZGF0YSAKLSBMYXRlbnQgRGlyaWNobGV0IGFsbG9jYXRpb24gKExEQSkgaXMgYSBwYXJ0aWN1bGFybHkgcG9wdWxhciBtZXRob2QKCgojIExhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbiAoTERBKQoKLSAqKkxhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbiAoTERBKSoqIAogICAgLSBldmVyeSBkb2N1bWVudCBpcyBhIG1peHR1cmUgb2YgKnRvcGljcyoKICAgIC0gZXZlcnkgdG9waWMgaXMgYSBtaXh0dXJlIG9mICp3b3JkcyoKICAgIC0gTERBIGJhc2ljYWxseSBlc3RpbWF0ZXMgdGhlIGNvbXBvc2l0aW9uIG9mIGJvdGggbWl4dHVyZXMgYXQgdGhlIHNhbWUgdGltZQotIHJlc3VsdCBhbGxvd3MgY29udGVudCBvZiBkb2N1bWVudHMgdG8gIm92ZXJsYXAiIHJhdGhlciB0aGFuIGVuZm9yY2luZyBkaXNjcmV0ZSBncm91cHMuLi5qdXN0IGxpa2Ugd2UgZG8gaW4gbmF0dXJhbCBsYW5ndWFnZSB1c2UuCi0gdGhlIGB0b3BpY21vZGVsc2AgcGFja2FnZSB3aWxsIGhlbHAgdXMgb24gb3VyIHdheQoKCgojIFByZXBhcmluZyBmb3IgTERBCgotIFdlJ2xsIGJvcnJvdyBhIGRhdGEgc2V0IG9mIGFydGljbGVzIGZyb20gdGhlIEFzc29jaWF0ZWQgUHJlc3MgdG8gaWxsdXN0cmF0ZSBzb21lIHByaW5jaXBsZXMgZmlyc3QuICAKICAgIC0gRG9uJ3Qgd29ycnksIGJhY2sgdG8gTWFjYmV0aCBpbiBhIGJpdAotIE5vdGU6IFdlIG5lZWQgdGhlIGRhdGEgYXMgYSBEVE0gKG5vdCB0aWR5IGZvcm0pCi0gUTogY2FuIHlvdSBleHBsYWluIHdoYXQncyBoYXBwZW5pbmcgYXQgZWFjaCBsaW5lPwotIFE6IGhvdyBtYW55IHJvd3MgJiBjb2x1bW5zIGFyZSBpbiBvdXIgYEFzc29jaWF0ZWRQcmVzc2AgbWF0cml4PwoKYGBge3J9CmRhdGEoIkFzc29jaWF0ZWRQcmVzcyIpCkFzc29jaWF0ZWRQcmVzcwpgYGAKCiMgVHdvLXRvcGljIExhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbiAoTERBKSBtb2RlbAoKLSBmaXR0aW5nIHRoZSBMREEgbW9kZWwgaXMgdGhlIGVhc3kgcGFydCB3aXRoIGhlbHAgb2YgdGhlIGBMREEoIClgIGZ1bmN0aW9uCi0gbm90ZTogd2Ugc2V0IGFuIGFyYml0cmFyeSBzZWVkIGluIHRoaXMgY2FzZSBzbyB3ZSBoYXZlIHNhbWUgcmVzdWx0IGVhY2ggdGltZS4uLiBub3QgbmVjZXNzYXJ5IGluIGdlbmVyYWwKLSBUaGVyZSBhcmUgYWxtb3N0IGNlcnRhaW5seSBtb3JlIHRoYW4gdHdvIHRvcGljcywgYnV0IHRoaXMgaXMgYSBzdGFydAotIEZvciBhIGNvcnB1cyBvZiBuZXdzIGFydGljbGVzIGxpa2UgdGhlIEFQIGRhdGEsIHdlIG1pZ2h0IGV4cGVjdCB0aGUgdG9waWNzIHRvIGJlIHNvbWV0aGluZyBsaWtlICJwb2xpdGljcyIgYW5kICJlbnRlcnRhaW5tZW50Ii4uLiByZWNhbGw6IAogICAgLSBlYWNoIGRvY3VtZW50IGNvdWxkIGluY2x1ZGUgYSBtaXggb2YgdG9waWNzIChwb2xpdGljcyAmIGVudGVydGFpbm1lbnQpCiAgICAtIGVhY2ggdG9waWMgaGFzIGEgbWl4IG9mIHdvcmRzCiAgICAgICAgLSAqcG9saXRpY3MqIHRvcGljIG1pZ2h0IGluY2x1ZGUgd29yZHMgbGlrZSAncHJlc2lkZW50JywgJ2NvbmdyZXNzJywgYW5kICdnb3Zlcm5tZW50CiAgICAgICAgLSAqZW50ZXJ0YWlubWVudCogdG9waWMgbWlnaHQgaW5jbHVkZSAnbW92aWVzJywgJ3RlbGV2aXNpb24nLCBhbmQgJ2FjdG9yJwogICAgICAgIC0gKipib3RoKiogdG9waWNzIG1pZ2h0IGZyZXF1ZW50bHkgaW5jbHVkZSBhIHdvcmQgbGlrZSAnYnVkZ2V0JwoKYGBge3J9CnJlcXVpcmUodG9waWNtb2RlbHMpCgphcF9sZGEgPC0gCiAgQXNzb2NpYXRlZFByZXNzICU+JQogIExEQShrID0gMiwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDEyMzQpKSAgCgphcF9sZGEKYGBgCgojIFBlci10b3BpYy1wZXItd29yZCBwcm9iYWJpbGl0aWVzIAoKLSB3YW50IHRvIGV4dHJhY3QgdGhlIHBlci10b3BpYy1wZXItd29yZCBwcm9iYWJpbGl0aWVzLCAkXGJldGEkICgiYmV0YSIpLCBmcm9tIHRoZSBtb2RlbAotIGB0aWR5dGV4dDo6dGlkeSggKWAgcHJvdmlkZXMgbWV0aG9kIAogICAgLSBvbmUtdG9waWMtcGVyLXRlcm0tcGVyLXJvdyBmb3JtYXQKICAgIC0gRm9yIGVhY2ggY29tYmluYXRpb24sIG1vZGVsIGNvbXB1dGVzIHByb2JhYmlsaXR5IG9mIHRlcm0gYmVpbmcgZ2VuZXJhdGVkIGZyb20gdGhhdCB0b3BpYwoKYGBge3J9CmFwX3RvcGljcyA8LSB0aWR5KGFwX2xkYSwgbWF0cml4ID0gImJldGEiKQphcF90b3BpY3MKYGBgCgoKIyBUb3AgMTAgcGVyLXRvcGljLXBlci13b3JkIHByb2JhYmlsaXRpZXMgKCRcYmV0YSQpIAoKLSBROiBob3cgbWlnaHQgeW91IGNoYXJhY3Rlcml6ZSBlYWNoIHRvcGljIG5vdz8KICAgIC0gbm90ZSB3b3JkcyBjaGFyYWN0ZXJpc3RpYyBvZiBlYWNoIHRvcGljCiAgICAtIG5vdGUgd29yZHMgY29tbW9uIHRvIGJvdGggdG9waWNzCgpgYGB7cn0KYXBfdG9wX3Rlcm1zIDwtIGFwX3RvcGljcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oMTAsIGJldGEpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKHRvcGljLCAtYmV0YSkKCmFwX3RvcF90ZXJtcyAlPiUKICBtdXRhdGUodGVybSA9IHJlb3JkZXIodGVybSwgYmV0YSkpICU+JQogIGdncGxvdChhZXModGVybSwgYmV0YSwgZmlsbCA9IGZhY3Rvcih0b3BpYykpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCiMgV29yZHMgd2l0aCBncmVhdGVzdCBkaWZmZXJlbmNlIGluICRcYmV0YSQgYmV0d2VlbiB0b3BpY3MKCi0gZXN0aW1hdGVkIGJhc2VkIG9uIHRoZSBsb2cgcmF0aW8gb2YgdGhlIHR3bzogJFx0ZXh0e2xvZ31fMihcZnJhY3tcYmV0YV8yfXtcYmV0YV8xfSkkCi0gYSBsb2cgcmF0aW8gaXMgdXNlZnVsIGJlY2F1c2UgaXQgbWFrZXMgdGhlIGRpZmZlcmVuY2Ugc3ltbWV0cmljYWwKICAgIC0gJFxiZXRhXzIkIGJlaW5nIHR3aWNlIGFzIGxhcmdlIGxlYWRzIHRvIGEgbG9nIHJhdGlvIG9mIDEKICAgIC0gJFxiZXRhXzEkIGJlaW5nIHR3aWNlIGFzIGxhcmdlIHJlc3VsdHMgaW4gLTEKLSB3ZSBmaWx0ZXIgZm9yIHJlbGF0aXZlbHkgY29tbW9uIHdvcmRzIHdpdGggJFxiZXRhID4gMS8xMDAwJCBpbiBhdCBsZWFzdCBvbmUgdG9waWMKLSBROiB3aGF0IGNhbiB3ZSBsZWFybiBieSBpbnNwZWN0aW5nIHRoZSBleHRyZW1lcz8KCmBgYHtyfQpiZXRhX3NwcmVhZCA8LSBhcF90b3BpY3MgJT4lCiAgbXV0YXRlKHRvcGljID0gcGFzdGUwKCJ0b3BpYyIsIHRvcGljKSkgJT4lCiAgc3ByZWFkKHRvcGljLCBiZXRhKSAlPiUKICBmaWx0ZXIodG9waWMxID4gLjAwMSB8IHRvcGljMiA+IC4wMDEpICU+JQogIG11dGF0ZShsb2dfcmF0aW8gPSBsb2cyKHRvcGljMiAvIHRvcGljMSkpICU+JQogIGFycmFuZ2UoZGVzYyhsb2dfcmF0aW8pKQoKYmV0YV9zcHJlYWQKYGBgCgoKCiMgUGVyLWRvY3VtZW50IGNsYXNzaWZpY2F0aW9uCgotIExEQSBhbHNvIG1vZGVscyBlYWNoIGRvY3VtZW50IGFzIGEgbWl4dHVyZSBvZiB0b3BpY3MKLSBXZSBleGFtaW5lIHRoZSBwZXItZG9jdW1lbnQtcGVyLXRvcGljIHByb2JhYmlsaXRpZXMsICRcZ2FtbWEkICgiZ2FtbWEiKQotIEdhbW1hIGlzIHByb3BvcnRpb24gb2Ygd29yZHMgZnJvbSB0aGF0IGRvY3VtZW50LCBnZW5lcmF0ZWQgZnJvbSB0aGF0IHRvcGljLiAgRm9yIGV4YW1wbGUsCiAgICAtIGFib3V0IDI1JSBvZiB0aGUgd29yZHMgaW4gZG9jdW1lbnQgMSBjb21lIGZyb20gdG9waWMgMQogICAgLSBhYm91dCA4MiUgb2YgdGhlIHdvcmRzIGluIGRvY3VtZW50IDUgY29tZSBmcm9tIHRvcGljIDIKCmBgYHtyfQpzY2VuZXNfZ2FtbWEgPC0gdGlkeShhcF9sZGEsIG1hdHJpeCA9ICJnYW1tYSIpCnNjZW5lc19nYW1tYSAlPiUgCiAgYXJyYW5nZShkb2N1bWVudCwgZ2FtbWEpCmBgYAoKIyBMZXQncyBpbnZlc3RpZ2F0ZSBhIGZldyBpbnRlcmVzdGluZyBkb2N1bWVudHMKCi0gRG9jdW1lbnQgMyBpcyBhbG1vc3QgNTAtNTAgYmV0d2VlbiBvdXIgdHdvIHRvcGljcwotIERvY3VtZW50IDYgaXMgYWxtb3N0IGVudGlyZWx5IHRvcGljIDIKLSBROiB3aGF0IGRvZXMgZWFjaCBkb2N1bWVudCBzZWVtIHRvIGJlIGFib3V0PwotIFE6IGRvZXMgJFxnYW1tYSQgYXR0cmlidXRlZCB0byBlYWNoIHRvcGljIG1ha2Ugc2Vuc2UgaW4gdGhlc2UgY2FzZXM/CgpgYGB7cn0KIyBpbnZlc3RpZ2F0ZSBkb2N1bWVudCAzICYgNgp0aWR5KEFzc29jaWF0ZWRQcmVzcykgJT4lCiAgZmlsdGVyKGRvY3VtZW50ID09IDMpICU+JQogIGFycmFuZ2UoZGVzYyhjb3VudCkpCmBgYAoKCiMgTW9kZWxpbmcgVG9waWNzIGFzIEFjdHMgaW4gTWFjYmV0aC4uLj8KCi0gUmVjYWxsOiBvdXIgbW90aXZhdGluZyBxdWVzdGlvbiB3YXMgdG8gdHJ5IGFuZCBsZWFybiBpZiB3ZSBjb3VsZCB1c2UgdGhlIGNvbnRlbnQgb2YgTWFjYmV0aCB0byBleHBvc2Ugc3RydWN0dXJlIGFtb25nIHRoZSA1IGFjdHMKLSBSZXN1bHRzIHNvIGZhcjogCiAgICAtIHdvcmRjbG91ZHM6ICJtZWgiCiAgICAtIHNlbnRpbWVudCBhbmFseXNpczogc3VwZXIgbmVnYXRpdmUuLi5ubyB3b25kZXIgTWFjYmV0aCBpcyBjYWxsZWQgYSAidHJhZ2VkeSIKICAgIC0gKnRmLWlkZio6IGhhcmQgdG8gdGVsbCBtdWNoLi4uIGFsbCB0aGUgZG9jdW1lbnRzIHdlcmUgd3JpdHRlbiBieSB0aGUgc2FtZSBwZXJzb24gZm9yIHRoZSBzYW1lIHB1cnBvc2UsIHNvIG5vdCBzdXJwcmlzZWQKICAgIC0gKipMREEqKjogPz8/Ci0gR29hbDogdXNlIExEQSB0byBtb2RlbCBgayA9IDVgIHRvcGljcyAKICAgIC0gY29tbW9uIHRvIHRyeSBhIGZldyBkaWZmZXJlbnQgdmFsdWVzIG9mIGBrYCAobnVtYmVyIG9mIHRvcGljcykKICAgIC0gc3RhcnQgd2l0aCA1IGhlcmUgYmVjYXVzZSB3ZSBrbm93IHRoZXJlIGFyZSA1IGFjdHMgaW4gdGhlIHBsYXkKCgpgYGB7cn0KIyBjb252ZXJ0IHRvIERvY3VtZW50IFRlcm0gTWF0cml4CgpNb2Rlcm5NYWNiZXRoX0RUTSA8LSAKICBNb2Rlcm5NYWNiZXRoICU+JQogIG11dGF0ZShhY3Rfc2NlbmUgPSBnc3ViKHBhdHRlcm4gPSAiXFwudHh0IiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IGRvY19pZCkpICU+JQogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSB0ZXh0KSAlPiUKICBtdXRhdGUod29yZCA9IGdzdWIocGF0dGVybiA9ICLigJkiLCByZXBsYWNlbWVudCA9ICInIiwgeCA9IHdvcmQpKSAlPiUKICBhbnRpX2pvaW4oc3RvcF93b3JkcywgYnkgPSBjKCJ3b3JkIiA9ICJ3b3JkIikpICU+JSAgICAgCiAgY291bnQoYWN0X3NjZW5lLCB3b3JkLCBzb3J0ID0gVFJVRSkgJT4lICAgCiAgY2FzdF9kdG0oZG9jdW1lbnQgPSBhY3Rfc2NlbmUsIHRlcm0gPSB3b3JkLCB2YWx1ZSA9IG4pCgoKYGBgCgpgYGB7cn0KIyBMREEgdG9waWMgbW9kZWwgKDUgdG9waWNzKQpzY2VuZXNfbGRhIDwtIAogIE1vZGVybk1hY2JldGhfRFRNICU+JQogIExEQShrID0gNSwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDM4MCkpICAKCgojIFBlci10b3BpYy1wZXItd29yZCBwcm9iYWJpbGl0aWVzIApzY2VuZV90b3BpY3MgPC0gdGlkeShzY2VuZXNfbGRhLCBtYXRyaXggPSAiYmV0YSIpCnNjZW5lX3RvcGljcwoKCmBgYAoKCmBgYHtyfQojIFZpc3VhbGl6ZSB0b3AgcGVyLXRvcGljLXBlci13b3JkIHByb2JhYmlsaXRpZXMKdG9wX3Rlcm1zIDwtIAogIHNjZW5lX3RvcGljcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oNSwgYmV0YSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UodG9waWMsIC1iZXRhKQoKdG9wX3Rlcm1zICU+JQogIG11dGF0ZSh0ZXJtID0gcmVvcmRlcih0ZXJtLCBiZXRhKSkgJT4lCiAgZ2dwbG90KGFlcyh0ZXJtLCBiZXRhLCBmaWxsID0gZmFjdG9yKHRvcGljKSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsKICBjb29yZF9mbGlwKCkKCgoKYGBgCgoKYGBge3J9CiMgUGVyLWRvY3VtZW50IGNsYXNzaWZpY2F0aW9uCnNjZW5lc19nYW1tYSA8LSB0aWR5KHNjZW5lc19sZGEsIG1hdHJpeCA9ICJnYW1tYSIpCnNjZW5lc19nYW1tYSAlPiUgCiAgYXJyYW5nZShkb2N1bWVudCwgZ2FtbWEpCgpgYGAKCgpgYGB7cn0KIyBUb3BpY3MgdnMgQWN0cz8Kc2NlbmVzX2dhbW1hIDwtIAogIHNjZW5lc19nYW1tYSAlPiUKICBzZXBhcmF0ZShjb2wgPSBkb2N1bWVudCwgaW50byA9IGMoImFjdCIsICJzY2VuZSIpLCBzZXAgPSAiXyIsIGNvbnZlcnQgPSBUUlVFKQoKc2NlbmVzX2dhbW1hICU+JQogIGdncGxvdChhZXMoZmFjdG9yKHRvcGljKSwgZ2FtbWEpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGZhY2V0X3dyYXAofiBhY3QpCgpgYGAKCgoKIyBTY2VuZSBhbGlnbm1lbnQgdG8gdG9waWNzCgotIE9rYXksIHRoZSB0b3BpY3MgYXJlbid0IHRoZSBhY3RzCi0gdHVybnMgb3V0IHRoaXMgZW5kdXJpbmcgU2hha2VzcGVhcmVhbiBtYXN0ZXJwaWVjZSBuZWVkcyBhIG1vcmUgbnVhbmNlZCBpbnRlcnByZXRhdGlvbgotIExpdGVyYXJ5IHRoZW1lcyBjb3VsZCBhbGlnbiBiZXR0ZXIgdG8gdGhlIFRvcGljcyBpZGVudGlmaWVkIGJ5IG91ciBMREEuLi4KLSBoZXJlcyBvbmUgYXR0ZW1wdCBJIGZvdW5kLCB3aXRoIG15IHdpZmUncyBkZXNjcmlwdGlvbiBvZiBlYWNoIGNvbG9yOgogICAgLSBhbWJpdGlvbiAobGF2ZW5kYXIpCiAgICAtIGZhdGUgKHBvcHB5KQogICAgLSB2aW9sZW5jZSAoVGlmZmFueSBibHVlKQogICAgLSBuYXR1cmUgJiB1bm5hdHVyYWwgKHNhbmQpCiAgICAtIG1hc2N1bGluaXR5IChLZW5kYWxsIGdyYXkpCgohW2h0dHBzOi8vd3d3LmxpdGNoYXJ0cy5jb20vbGl0L21hY2JldGgvY2hhcnQtYm9hcmQtdmlzdWFsaXphdGlvbl0oTWFjYmV0aExpdGVyYXJ5VGhlbWVzLnBuZyl7IHdpZHRoPTkwJSB9CgoKCiMgUm9hZCBtYXAgKGFnYWluKQoKPGNlbnRlcj4KCiFbaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL3RvcGljbW9kZWxpbmcuaHRtbF0odGlkeXRleHRGbG93LnBuZyl7d2lkdGg9OTUlfQoKPC9jZW50ZXI+CgoKCgoK