(NB: This has its roots in a workshop co-written and delivered by Matt Denny and myself.)

For text manipulation in R, I recommend the stringr package, which is part of the “tidyverse.” There is a fantastic “cheat sheet” available here. All stringr functions start with str_.

(A little bit in the weeds … First, a lot of this can be done in base R, but it can be less straightforward or intuitive. Second, almost all of stringr is a “wrapper” for commands in the package stringi, providing a more intuitive and consistent syntax, especially if you are also working with other elements of the tidyverse. So, not only can you do most of the following with stringi, you actually are doing most of this with stringi “under the hood.”)

Install stringr if you need to, and load it:

Strings

The basic thing we want to manipulate are “strings.” These are “character” objects in R, and can be specified using double quotes (") or single quotes (’):

a_string <- "Example STRING, with numbers (12, 15 and also 10.2)?!"
a_string

It’s really a matter of style or convenience, but you might use single quotes if your string actually contains double quotes:

my_single_quoted_string <- 'He asked, "Why would you use single quotes?"'
my_single_quoted_string

R always displays strings in double-quotes. That \ tells R to “escape” the next character. In this case, the \" is saying, " is part of the string, not the end of the string.

You can specify the string that way if you want.

my_string_with_double_quotes <- "She answered, \"Convenience, but you never really have to.\""
my_string_with_double_quotes

If you ever want to see how your string with escape characters displays when printed or (typically) in an editor, use writeLines.

writeLines(my_single_quoted_string)
writeLines(my_string_with_double_quotes)

This can get a little bit confusing. For example, since the backslash character tells R to escape, to indicate an actual backslash character you have to backslash your backslashes:

a_string_with_backslashes = "To indicate a backslash, \\, you have to type two: \\\\. Just there, to indicate two backslashes, I had to type four: \\\\\\\\."
a_string_with_backslashes
writeLines(a_string_with_backslashes)

There are a number of special escape characters that are used to represent things like “control characters.” The most common are two that you’re already used to tapping a keyboard key for without expecting a character to appear on your screen: \t (tab) and \n (newline).

test_string <- "abc ABC 123\t.!?\\(){}\n"
test_string
writeLines(test_string)

As with pretty much everything in R, you can have a vector of strings (a “character vector”).

a_vector_of_strings <- c("abcde", "123", "chicken of the sea")
a_vector_of_strings

Base R comes with a few built in string vectors – letters, LETTERS, month.abb, and month.name. Loading stringr also loads a few more: fruit, words, and sentences. We’ll use these for a few examples, so let’s look at them. The last two are long, so we’ll just look at the first few entries of each of those.

letters
LETTERS
month.abb
month.name
fruit
length(words)
words[1:5]
length(sentences)
sentences[1:5]

Basic string operations

You can combine, or “concatenate”, two strings with the stringr command str_c or the syntactically identical base R command paste.

second_string <- "Wow, two sentences."
combined_string <- str_c(a_string,second_string,sep = " ")
combined_string
paste(a_string,second_string,sep = " ")

You can zip together vectors of strings.

str_c(month.abb, month.name, sep=" stands for ")

You can concatenate all the strings in a vector together with the collapse parameter.

str_c(month.name, collapse=" then ")

Or both.

str_c(letters,LETTERS, sep="", collapse=",")

Or more than two vectors or constants.

str_c(month.name," (", month.abb, ")", sep="", collapse=" then ")

You can split a string up using str_split. The similar base R command is strsplit.

str_split(combined_string,"!") # split on !
str_split(combined_string,",") # split on ,
strsplit(combined_string,"!") # base R

str_split returns a list of character vectors. With simplify = TRUE, it returns a character matrix (with one row and two columns).

str_split(combined_string,"!",simplify=TRUE)

So, in this case you could get that as one vector a number of ways.

str_split(combined_string,"!")[[1]] #give first element of list
str_split(combined_string,"!",simplify=TRUE)[1,] # give first row of matrix
unlist(str_split(combined_string,"!")) #turn list into vector

Substrings

You can use the str_sub command to identify a substring at a known position in a string.

str_sub(fruit,2,4) # 2nd through 5th character of each fruit
str_sub(fruit,-2,-1) # Last two characters of each fruit
some_dates <- c("1999/01/01","1998/12/15","2001/09/03")
str_sub(some_dates,1,4)
str_sub(some_dates,6,7)

You can use the str_sub command to change a substring at a known position in a string.

zebra_fruit <- fruit
str_sub(zebra_fruit,2,3) <- "--ZEBRA!!--"
zebra_fruit[1:10]

Cleaning and normalizing strings

You can do case-folding with str_to_lower (orstr_to_upper). Base R command is tolower.

str_to_lower(combined_string) 

You can trim excess whitespace off the ends of strings with str_trim

a_string_vector <- str_split(combined_string,"!")[[1]]
a_string_vector
str_trim(a_string_vector)

Searching for a pattern

What are the locations of the fruits with “berry” in their name?

str_which(fruit,"berry")

We could get the same answer in this instance from base R’s grep, but the syntax is different.

grep("berry", fruit)

For each fruit, does it contain “berry”?

str_detect(fruit,"berry")

We could get the same answer in this instance from base R’s grepl, but the syntax is like that of grep.

grepl("berry", fruit)

How many matches of “berry” does each fruit have?

str_count(fruit,"berry")
str_count(fruit,"a")

Where is the substring “berry” located in each fruit string?

str_locate(fruit[1:10],"berry")

(We can get equivalent information, in very different format, from the base r command regexpr. It’s nowhere near as intuitive though, returning a vector for the start positions, and the length of the matches in an attribute.)

regexpr_obj <-  regexpr("berry",fruit[1:10]) 
regexpr_obj  # The full object
regexpr_obj[1:10] # The values of the object itself, the starting positions
attr(regexpr_obj,"match.length") # The match.length attribute

List fruits that have “berry” in their name.

str_subset(fruit,"berry")

The base R equivalent is grep with value=TRUE:

grep("berry",fruit, value=TRUE)

For each fruit, give me the first substring that matches “berry.”

str_extract(fruit,"berry")

In this instance, we get the same answer in matrix form from str_match:

str_match(fruit[1:10],"berry")

str_match is mainly helpful when we want to match multiple things or use a larger pattern to isolate smaller pieces. We’ll see examples below.

To get a visual for where your matches are occurring, you can use str_view_all. (You will see something in the RStudio Viewer.)

str_view_all(fruit,"berry")

For every fruit with “berry” in the name, change “berry” to “fish”.

str_replace(fruit[1:10],"berry", "fish")

str_replace replaces the first pattern match in each string; str_replace_all replaces all pattern matches in each string.

str_replace(fruit[1:10],"a", "ZZ")
str_replace_all(fruit[1:10],"a", "ZZ")

Regular expressions

So far, I’ve only searched for patterns that are only alphabetic characters like "berry". But we can use make much more elaborate and flexible patterns using regular expressions.

Regular expressions come in a variety of flavors and R has a somewhat unusual one. I recommend you reference the cheat sheet and the online regex tool https://regex101.com in parallel.

This or that, not this or that, this or that or anything in between

Square brackets for “or” (disjunction) of characters

Match “any one of” the characters in the square brackets.

str_subset(sentences, ' [bhp]eat ')

Square brackets with ^ for negation.

Match “anything but one of” the characters in the square brackets.

str_subset(sentences, ' [^bhp]eat ')

Square brackets with - for “or” over a range of characters

str_subset(sentences, ' [b-p]eat ')

Parentheses and pipe operator for multi-character patterns

When we need an “or” over multi-character patterns, we can use the “pipe” operator, using parentheses as necessary to identify what’s with what.

str_subset(fruit, '(black|blue|red)(currant|berry)')

The parentheses also define a “capture group”, a concept we’ll explain below.

Special characters and escaping

In addition to the backslash, there are at least 16 characters that have special meaning in R regexes, and (may) have to be escaped in order to match the literal character. They are ^ $ . * + | ! ? ( ) [ ] { } < >.

For example, the period – “.” – means “any character but a newline.” It’s a wildcard. We get different results when we escape or don’t escape.

str_extract_all(combined_string,".")    # any single character
str_extract_all(combined_string,"\\.")  # a period
str_extract_all(combined_string,"a.")   # "a" followed by any single character
str_extract_all(combined_string,"a\\.") # "a" followed by a period (no match)

Some of these are only special characters in certain contexts and don’t have to be escaped to be recognized when not in those contexts. But they can be escaped in all circumstances and I recommend that rather than trying to figure out the exact rules.

The exclamation point is such a character.

str_extract_all(combined_string,"!")
str_extract_all(combined_string,"\\!")

Class shorthands: \w \W \s \S \d \D and POSIX classes

Conversely, there are a number of characters that have special meaning only when escaped. The main ones for now are “\w” (any alphanumeric character), “\s” (any space character), and “\d” (any numeric digit), The capitalized versions of these are used to mean “anything but” that class.

str_extract_all(combined_string,"\\w") # any "word" character - letter or number
str_extract_all(combined_string,"\\W") # any nonword character
str_extract_all(combined_string,"\\s") # any whitespace character
str_extract_all(combined_string,"\\S") # any nonspace character
str_extract_all(combined_string,"\\d") # any digit
str_extract_all(combined_string,"\\D") # any nondigit character

“POSIX” classes

There are other predefined classes in a computing standard called “POSIX” that some regex engines recognize. The ones for R are listed on the cheat sheet. These can mostly be mimicked with the shorthand listed above. The main one I find handy is “[:punct:]” for “any punctuation character.”

str_extract_all(combined_string,"[:punct:]") # any "punctuation" character 

Note that when characters stray beyond the limited ASCII character set – other languages, specialized characters like emojis – there’s not complete consistency in what may be considered an alphanumeric character or punctuation.

Quantifiers: * . ?

Quantifiers: * (zero or more of the previous)

This is also known as the “Kleene star” (pronounced clean-ee), after its original user (Kleene) who introduced the notation in formal logic.

str_extract_all(combined_string,"\\d*") #  

Quantifiers: + (one or more of the previous)

This is also known as the “Kleene plus.”

str_extract_all(combined_string,"\\d+") #  

Quantifiers:

{n} = “exactly n” of the previous {n,m} = “between n and m” of the previous {n,} = “n or more” of the previous

str_extract_all("x xx xxx xxxx xxxxx","x{3}") #
str_extract_all("x xx xxx xxxx xxxxx","x{3,4}") #  
str_extract_all("x xx xxx xxxx xxxxx","x{3,}") #  

Were all of those what you expected? Use str_view_all to see what’s happening.

str_view_all("x xx xxx xxxx xxxxx","x{3}") #  

Question Mark as Quantifier (zero or one of the previous)

str_extract_all(combined_string,"\\d?") # 0 or 1 digit
str_subset(sentences," [bp]?eat")

Greedy vs. non-greedy matching

Question Mark as Nongreedy Modifier to Quantifier - smallest match of previous possible

str_extract_all("(First bracketed statement) Other text (Second bracketed statement)","\\(.+\\)") # greedy - captures from first ( to last )
str_extract_all("(First bracketed statement) Other text (Second bracketed statement)","\\(.+?\\)") # nongreedy - finds two smaller matches
str_extract_all("x xx xxx xxxx xxxxx","x.+x") # defaults to greedy - largest match
str_extract_all("x xx xxx xxxx xxxxx","x.+?x") # nongreedy - not what you expect - why?

Anchors and word boundaries: ^ $ \b

Anchors: ^ (beginning of string), $ (end of string)

str_extract_all(combined_string,"\\w+") # sequences of alphanumeric characters
str_extract_all(combined_string,"^\\w+") # sequences at beginning of string
str_extract_all(combined_string,"\\w+$") # sequences at end of string # none - it ends in punctuation

Word boundaries: \b

Similarly, we can identify “word boundaries.’’ This solves the greedy/nongreedy problem we had with the”x" sequences above. It still thinks the decimal point in 10.2 is a word boundary, though.

str_extract_all("x xx xxx xxxx xxxxx","\\bx.*?\\b") # 
str_extract_all(combined_string,"\\b\\w+?\\b") # 

Capture groups

We’ve seen parentheses used with the pipe operator. They are also used to indicate smaller parts of the pattern that we want to “capture.” str_match will give us a matrix in which the first column is the match to the entire pattern, what we’ve seen before. Each subsequent column holds the part of the match in each pair of parentheses.

str_match(fruit[1:15],"^(.+?)(berry|fruit)$")

We also can use \\1, \\2, etc. to refer to these capture groups later in the same command.

Here’s an actual regular expression I use in cleaning the Mood of the Nation poll answers. I later will let punctuation like ' indicate a word boundary, so first, I want to collapse contractions across the ' to keep them together. This, for example collapses any n't contractions.

motn <- "i can't stand don'trump supporters shouting 'build that wall'!"
newmotn <- str_replace_all(motn,"(n't)($|[[:punct:]]|\\s)","nt\\2") #dont, cant, wont, wasnt, werent, didnt, couldnt, wouldnt, shouldnt, havent
newmotn

That looks for the (1) n't pattern followed by the (2) end of the string, another punctuation mark, or a whitespace. It then replaces that with nt followed by whatever the following character was. This avoids replacing other accidental instances of the n't pattern that aren’t clearly contractions.

Using regular expressions to extract data from text: an example

Let’s start with some example text:

text <- "SEC. 101. FISCAL YEAR 2017.
(a) In General.--There are authorized to be appropriated to NASA
for fiscal year 2017 $19,508,000,000, as follows:
(1) For Exploration, $4,330,000,000.
(2) For Space Operations, $5,023,000,000.
(3) For Science, $5,500,000,000.
(4) For Aeronautics, $640,000,000.
(5) For Space Technology, $686,000,000.
(6) For Education, $115,000,000.
(7) For Safety, Security, and Mission Services,
$2,788,600,000.
(8) For Construction and Environmental Compliance and
Restoration, $388,000,000.
(9) For Inspector General, $37,400,000.
(b) Exception.--In addition to the amounts authorized to be
appropriated for each account under subsection (a), there are
authorized to be appropriated additional funds for each such account,
but only if the authorized amounts for all such accounts are fully
provided for in annual appropriation Acts, consistent with the
discretionary spending limits in section 251(c) of the Balanced Budget
and Emergency Deficit Control Act of 1985."

Wait … that’s just one variable holding one string? Yep.

text

All those \ns there indicate new lines.

We’re going to try to use regular expressions to make data out of the appropriations dollars and purposes in bullets 1-9.

Lets play around with a few things. Extract all contiguous sequences of one or more numbers.

stringr::str_extract_all(text,"[0-9]+")[[1]]

That does two things we don’t like … separates numbers at the 1000s separating comma and gets numbers (“101”, “2017”, etc.) that aren’t dollar amounts. So, let’s try getting everything that Starts with a “$” (which needs to be escaped) Followed by one or more strings of commas or digits.

stringr::str_extract_all(text,"\\$[,0-9]+")[[1]] # must start with $

Almost … don’t like that extra comma on the first number. Add and ends with a number.

stringr::str_extract_all(text,"\\$[,0-9]+[0-9]")[[1]]

We could use quantifiers to get numbers of $1 billion or more

stringr::str_extract_all(text,"\\$[,0-9]{12,}[0-9]")[[1]]

That asks for Starts with a “$” Followed by 12 OR MORE commas and numbers And ends with a number

Now let’s try to get the bullet numbers enclosed in parentheses:

stringr::str_extract_all(text,"\\([0-9]\\)")[[1]]

Say we only want to match lines that start with a particular set of characters … First let’s split it into lines:

text_split <- stringr::str_split(text,"\\n")[[1]]
text_split

Now match on beggining string anchor and open paren.

stringr::str_extract_all(text_split,"^\\(.*")

That returned a list, and we’d probably rather have a vector. In this case, we can just wrap this in an unlist() statement:

unlist(stringr::str_extract_all(text_split,"^\\(.*"))

So, now let’s try to put everything we’ve learned together and make a little dataset out of the items (1) to (9) with dollar amounts and what they’re for

Let’s see what we have in that last command .. We have some extra lines … “(a)” and “(b)” And we’re missing the $ numbers from items (7) and (8) which are on the next lines.

Let’s go back to the original and get rid of the newlines:

one_line <- stringr::str_replace_all(text,"\\n"," ")[[1]]
one_line

and find all the matches from “(number)” to a period, lazily rather than greedily

item_strings <- stringr::str_extract_all(one_line,"\\(\\d\\).+?\\.")[[1]]
item_strings

Can use str_match and parentheses to identify the stuff you want

for_strings <- stringr::str_match(item_strings,"For (.+), \\$")
for_strings

The second column contains our list of the “for what”s.

for_strings <- for_strings[,2]
for_strings

Do something similar for money

money_strings <- stringr::str_match(item_strings,"\\$([,\\d]+)")[,2]
money_strings

Get rid of the punctuation

money_strings <- stringr::str_replace_all(money_strings,"[\\$,]","")
money_strings

Turn them into numeric data rather than strings.

money <- as.numeric(money_strings)
money

Now let’s make it data:

appropriations_data <- data.frame(purpose = for_strings,amount = money)
appropriations_data

Other languages

Remember … other programming languages handle regular expressions slightly differently. In particlar, Python does not use the “double escape” idiom.

LS0tCnRpdGxlOiAiQW4gSW50cm9kdWN0aW9uIHRvIFN0cmluZyBNYW5pcHVsYXRpb24gYW5kIFJlZ3VsYXIgRXhwcmVzc2lvbnMgaW4gUiIKYXV0aG9yOiAiQnVydCBMLiBNb25yb2UiCnN1YnRpdGxlOiBQZW5uIFN0YXRlIGFuZCBFc3NleCBjb3Vyc2VzIGluICJUZXh0IGFzIERhdGEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIHRoZW1lOiB1bml0ZWQKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgooTkI6IFRoaXMgaGFzIGl0cyByb290cyBpbiBhIHdvcmtzaG9wIGNvLXdyaXR0ZW4gYW5kIGRlbGl2ZXJlZCBieSBNYXR0IERlbm55IGFuZCBteXNlbGYuKQoKRm9yIHRleHQgbWFuaXB1bGF0aW9uIGluIFIsIEkgcmVjb21tZW5kIHRoZSBgc3RyaW5ncmAgcGFja2FnZSwgd2hpY2ggaXMgcGFydCBvZiB0aGUgInRpZHl2ZXJzZS4iIFRoZXJlIGlzIGEgZmFudGFzdGljICJjaGVhdCBzaGVldCIgYXZhaWxhYmxlIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9ibG9iL21hc3Rlci9zdHJpbmdzLnBkZikuIEFsbCBgc3RyaW5ncmAgZnVuY3Rpb25zIHN0YXJ0IHdpdGggYHN0cl9gLgoKKEEgbGl0dGxlIGJpdCBpbiB0aGUgd2VlZHMgLi4uIEZpcnN0LCBhIGxvdCBvZiB0aGlzICpjYW4qIGJlIGRvbmUgaW4gYmFzZSBSLCBidXQgaXQgY2FuIGJlIGxlc3Mgc3RyYWlnaHRmb3J3YXJkIG9yIGludHVpdGl2ZS4gU2Vjb25kLCBhbG1vc3QgYWxsIG9mIGBzdHJpbmdyYCBpcyBhICJ3cmFwcGVyIiBmb3IgY29tbWFuZHMgaW4gdGhlIHBhY2thZ2UgYHN0cmluZ2lgLCBwcm92aWRpbmcgYSBtb3JlIGludHVpdGl2ZSBhbmQgY29uc2lzdGVudCBzeW50YXgsIGVzcGVjaWFsbHkgaWYgeW91IGFyZSBhbHNvIHdvcmtpbmcgd2l0aCBvdGhlciBlbGVtZW50cyBvZiB0aGUgdGlkeXZlcnNlLiBTbywgbm90IG9ubHkgKmNhbiogeW91IGRvIG1vc3Qgb2YgdGhlIGZvbGxvd2luZyB3aXRoIGBzdHJpbmdpYCwgeW91IGFjdHVhbGx5ICphcmUqIGRvaW5nIG1vc3Qgb2YgdGhpcyB3aXRoIGBzdHJpbmdpYCAidW5kZXIgdGhlIGhvb2QuIikKCkluc3RhbGwgYHN0cmluZ3JgIGlmIHlvdSBuZWVkIHRvLCBhbmQgbG9hZCBpdDoKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygic3RyaW5nciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiMgdGhlIGZvbGxvd2luZyBpcyBuZWNlc3NhcnkgZm9yIHRoZSBzdHJfdmlldyBjb21tYW5kIHRvIHdvcmsgCiNpbnN0YWxsLnBhY2thZ2VzKiJodG1sd2lkZ2V0cyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCmxpYnJhcnkoc3RyaW5ncikKYGBgCgojIFN0cmluZ3MKClRoZSBiYXNpYyB0aGluZyB3ZSB3YW50IHRvIG1hbmlwdWxhdGUgYXJlICJzdHJpbmdzLiIgVGhlc2UgYXJlICJjaGFyYWN0ZXIiIG9iamVjdHMgaW4gUiwgYW5kIGNhbiBiZSBzcGVjaWZpZWQgdXNpbmcgZG91YmxlIHF1b3RlcyAoIikgb3Igc2luZ2xlIHF1b3RlcyAoJyk6CgpgYGB7cn0KYV9zdHJpbmcgPC0gIkV4YW1wbGUgU1RSSU5HLCB3aXRoIG51bWJlcnMgKDEyLCAxNSBhbmQgYWxzbyAxMC4yKT8hIgphX3N0cmluZwpgYGAKCkl0J3MgcmVhbGx5IGEgbWF0dGVyIG9mIHN0eWxlIG9yIGNvbnZlbmllbmNlLCBidXQgeW91IG1pZ2h0IHVzZSBzaW5nbGUgcXVvdGVzIGlmIHlvdXIgc3RyaW5nIGFjdHVhbGx5IGNvbnRhaW5zIGRvdWJsZSBxdW90ZXM6CmBgYHtyfQpteV9zaW5nbGVfcXVvdGVkX3N0cmluZyA8LSAnSGUgYXNrZWQsICJXaHkgd291bGQgeW91IHVzZSBzaW5nbGUgcXVvdGVzPyInCm15X3NpbmdsZV9xdW90ZWRfc3RyaW5nCmBgYAoKUiBhbHdheXMgZGlzcGxheXMgc3RyaW5ncyBpbiBkb3VibGUtcXVvdGVzLiBUaGF0IGBcYCB0ZWxscyBSIHRvICJlc2NhcGUiIHRoZSBuZXh0IGNoYXJhY3Rlci4gSW4gdGhpcyBjYXNlLCB0aGUgYFwiYCBpcyBzYXlpbmcsIGAiYCBpcyBwYXJ0IG9mIHRoZSBzdHJpbmcsIG5vdCB0aGUgZW5kIG9mIHRoZSBzdHJpbmcuCgpZb3UgY2FuIHNwZWNpZnkgdGhlIHN0cmluZyB0aGF0IHdheSBpZiB5b3Ugd2FudC4KYGBge3J9Cm15X3N0cmluZ193aXRoX2RvdWJsZV9xdW90ZXMgPC0gIlNoZSBhbnN3ZXJlZCwgXCJDb252ZW5pZW5jZSwgYnV0IHlvdSBuZXZlciByZWFsbHkgaGF2ZSB0by5cIiIKbXlfc3RyaW5nX3dpdGhfZG91YmxlX3F1b3RlcwpgYGAKCklmIHlvdSBldmVyIHdhbnQgdG8gc2VlIGhvdyB5b3VyIHN0cmluZyB3aXRoIGVzY2FwZSBjaGFyYWN0ZXJzIGRpc3BsYXlzIHdoZW4gcHJpbnRlZCBvciAodHlwaWNhbGx5KSBpbiBhbiBlZGl0b3IsIHVzZSBgd3JpdGVMaW5lc2AuCmBgYHtyfQp3cml0ZUxpbmVzKG15X3NpbmdsZV9xdW90ZWRfc3RyaW5nKQp3cml0ZUxpbmVzKG15X3N0cmluZ193aXRoX2RvdWJsZV9xdW90ZXMpCmBgYAoKVGhpcyBjYW4gZ2V0IGEgbGl0dGxlIGJpdCBjb25mdXNpbmcuIEZvciBleGFtcGxlLCBzaW5jZSB0aGUgYmFja3NsYXNoIGNoYXJhY3RlciB0ZWxscyBSIHRvIGVzY2FwZSwgdG8gaW5kaWNhdGUgYW4gYWN0dWFsIGJhY2tzbGFzaCBjaGFyYWN0ZXIgeW91IGhhdmUgdG8gYmFja3NsYXNoIHlvdXIgYmFja3NsYXNoZXM6CgpgYGB7cn0KYV9zdHJpbmdfd2l0aF9iYWNrc2xhc2hlcyA9ICJUbyBpbmRpY2F0ZSBhIGJhY2tzbGFzaCwgXFwsIHlvdSBoYXZlIHRvIHR5cGUgdHdvOiBcXFxcLiBKdXN0IHRoZXJlLCB0byBpbmRpY2F0ZSB0d28gYmFja3NsYXNoZXMsIEkgaGFkIHRvIHR5cGUgZm91cjogXFxcXFxcXFwuIgphX3N0cmluZ193aXRoX2JhY2tzbGFzaGVzCndyaXRlTGluZXMoYV9zdHJpbmdfd2l0aF9iYWNrc2xhc2hlcykKYGBgCgoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIHNwZWNpYWwgZXNjYXBlIGNoYXJhY3RlcnMgdGhhdCBhcmUgdXNlZCB0byByZXByZXNlbnQgdGhpbmdzIGxpa2UgImNvbnRyb2wgY2hhcmFjdGVycy4iIFRoZSBtb3N0IGNvbW1vbiBhcmUgdHdvIHRoYXQgeW91J3JlIGFscmVhZHkgdXNlZCB0byB0YXBwaW5nIGEga2V5Ym9hcmQga2V5IGZvciB3aXRob3V0IGV4cGVjdGluZyBhIGNoYXJhY3RlciB0byBhcHBlYXIgb24geW91ciBzY3JlZW46IGBcdGAgKHRhYikgYW5kIGBcbmAgKG5ld2xpbmUpLgpgYGB7cn0KdGVzdF9zdHJpbmcgPC0gImFiYyBBQkMgMTIzXHQuIT9cXCgpe31cbiIKdGVzdF9zdHJpbmcKd3JpdGVMaW5lcyh0ZXN0X3N0cmluZykKYGBgCgpBcyB3aXRoIHByZXR0eSBtdWNoIGV2ZXJ5dGhpbmcgaW4gUiwgeW91IGNhbiBoYXZlIGEgdmVjdG9yIG9mIHN0cmluZ3MgKGEgImNoYXJhY3RlciB2ZWN0b3IiKS4KCmBgYHtyfQphX3ZlY3Rvcl9vZl9zdHJpbmdzIDwtIGMoImFiY2RlIiwgIjEyMyIsICJjaGlja2VuIG9mIHRoZSBzZWEiKQphX3ZlY3Rvcl9vZl9zdHJpbmdzCmBgYAoKQmFzZSBSIGNvbWVzIHdpdGggYSBmZXcgYnVpbHQgaW4gc3RyaW5nIHZlY3RvcnMgLS0gYGxldHRlcnNgLCBgTEVUVEVSU2AsIGBtb250aC5hYmJgLCBhbmQgYG1vbnRoLm5hbWVgLiBMb2FkaW5nIGBzdHJpbmdyYCBhbHNvIGxvYWRzIGEgZmV3IG1vcmU6IGBmcnVpdGAsIGB3b3Jkc2AsIGFuZCBgc2VudGVuY2VzYC4gV2UnbGwgdXNlIHRoZXNlIGZvciBhIGZldyBleGFtcGxlcywgc28gbGV0J3MgbG9vayBhdCB0aGVtLiBUaGUgbGFzdCB0d28gYXJlIGxvbmcsIHNvIHdlJ2xsIGp1c3QgbG9vayBhdCB0aGUgZmlyc3QgZmV3IGVudHJpZXMgb2YgZWFjaCBvZiB0aG9zZS4KCmBgYHtyfQpsZXR0ZXJzCmBgYAoKYGBge3J9CkxFVFRFUlMKYGBgCgpgYGB7cn0KbW9udGguYWJiCmBgYAoKYGBge3J9Cm1vbnRoLm5hbWUKYGBgCgpgYGB7cn0KZnJ1aXQKYGBgCgpgYGB7cn0KbGVuZ3RoKHdvcmRzKQp3b3Jkc1sxOjVdCmBgYAoKYGBge3J9Cmxlbmd0aChzZW50ZW5jZXMpCnNlbnRlbmNlc1sxOjVdCmBgYAoKIyMgQmFzaWMgc3RyaW5nIG9wZXJhdGlvbnMKCllvdSBjYW4gY29tYmluZSwgb3IgImNvbmNhdGVuYXRlIiwgdHdvIHN0cmluZ3Mgd2l0aCB0aGUgYHN0cmluZ3JgIGNvbW1hbmQgYHN0cl9jYCBvciB0aGUgc3ludGFjdGljYWxseSBpZGVudGljYWwgYmFzZSBSIGNvbW1hbmQgYHBhc3RlYC4KCmBgYHtyfQpzZWNvbmRfc3RyaW5nIDwtICJXb3csIHR3byBzZW50ZW5jZXMuIgpjb21iaW5lZF9zdHJpbmcgPC0gc3RyX2MoYV9zdHJpbmcsc2Vjb25kX3N0cmluZyxzZXAgPSAiICIpCmNvbWJpbmVkX3N0cmluZwpgYGAKCmBgYHtyfQpwYXN0ZShhX3N0cmluZyxzZWNvbmRfc3RyaW5nLHNlcCA9ICIgIikKYGBgCgpZb3UgY2FuIHppcCB0b2dldGhlciB2ZWN0b3JzIG9mIHN0cmluZ3MuCmBgYHtyfQpzdHJfYyhtb250aC5hYmIsIG1vbnRoLm5hbWUsIHNlcD0iIHN0YW5kcyBmb3IgIikKYGBgCgpZb3UgY2FuIGNvbmNhdGVuYXRlIGFsbCB0aGUgc3RyaW5ncyBpbiBhIHZlY3RvciB0b2dldGhlciB3aXRoIHRoZSBgY29sbGFwc2VgIHBhcmFtZXRlci4KCmBgYHtyfQpzdHJfYyhtb250aC5uYW1lLCBjb2xsYXBzZT0iIHRoZW4gIikKYGBgCgpPciBib3RoLgpgYGB7cn0Kc3RyX2MobGV0dGVycyxMRVRURVJTLCBzZXA9IiIsIGNvbGxhcHNlPSIsIikKYGBgCgpPciBtb3JlIHRoYW4gdHdvIHZlY3RvcnMgb3IgY29uc3RhbnRzLgpgYGB7cn0Kc3RyX2MobW9udGgubmFtZSwiICgiLCBtb250aC5hYmIsICIpIiwgc2VwPSIiLCBjb2xsYXBzZT0iIHRoZW4gIikKYGBgCgpZb3UgY2FuIHNwbGl0IGEgc3RyaW5nIHVwIHVzaW5nIGBzdHJfc3BsaXRgLiBUaGUgc2ltaWxhciBiYXNlIFIgY29tbWFuZCBpcyBgc3Ryc3BsaXRgLgoKYGBge3J9CnN0cl9zcGxpdChjb21iaW5lZF9zdHJpbmcsIiEiKSAjIHNwbGl0IG9uICEKc3RyX3NwbGl0KGNvbWJpbmVkX3N0cmluZywiLCIpICMgc3BsaXQgb24gLApzdHJzcGxpdChjb21iaW5lZF9zdHJpbmcsIiEiKSAjIGJhc2UgUgpgYGAKCmBzdHJfc3BsaXRgIHJldHVybnMgYSAqbGlzdCogb2YgY2hhcmFjdGVyIHZlY3RvcnMuIFdpdGggYHNpbXBsaWZ5ID0gVFJVRWAsIGl0IHJldHVybnMgYSBjaGFyYWN0ZXIgKm1hdHJpeCogKHdpdGggb25lIHJvdyBhbmQgdHdvIGNvbHVtbnMpLgoKYGBge3J9CnN0cl9zcGxpdChjb21iaW5lZF9zdHJpbmcsIiEiLHNpbXBsaWZ5PVRSVUUpCmBgYAoKU28sIGluIHRoaXMgY2FzZSB5b3UgY291bGQgZ2V0IHRoYXQgYXMgb25lIHZlY3RvciBhIG51bWJlciBvZiB3YXlzLgpgYGB7cn0Kc3RyX3NwbGl0KGNvbWJpbmVkX3N0cmluZywiISIpW1sxXV0gI2dpdmUgZmlyc3QgZWxlbWVudCBvZiBsaXN0CnN0cl9zcGxpdChjb21iaW5lZF9zdHJpbmcsIiEiLHNpbXBsaWZ5PVRSVUUpWzEsXSAjIGdpdmUgZmlyc3Qgcm93IG9mIG1hdHJpeAp1bmxpc3Qoc3RyX3NwbGl0KGNvbWJpbmVkX3N0cmluZywiISIpKSAjdHVybiBsaXN0IGludG8gdmVjdG9yCmBgYAoKIyMgIFN1YnN0cmluZ3MKCllvdSBjYW4gdXNlIHRoZSBgc3RyX3N1YmAgY29tbWFuZCB0byBpZGVudGlmeSBhIHN1YnN0cmluZyBhdCBhIGtub3duIHBvc2l0aW9uIGluIGEgc3RyaW5nLgpgYGB7cn0Kc3RyX3N1YihmcnVpdCwyLDQpICMgMm5kIHRocm91Z2ggNXRoIGNoYXJhY3RlciBvZiBlYWNoIGZydWl0CnN0cl9zdWIoZnJ1aXQsLTIsLTEpICMgTGFzdCB0d28gY2hhcmFjdGVycyBvZiBlYWNoIGZydWl0CnNvbWVfZGF0ZXMgPC0gYygiMTk5OS8wMS8wMSIsIjE5OTgvMTIvMTUiLCIyMDAxLzA5LzAzIikKc3RyX3N1Yihzb21lX2RhdGVzLDEsNCkKc3RyX3N1Yihzb21lX2RhdGVzLDYsNykKYGBgCgpZb3UgY2FuIHVzZSB0aGUgYHN0cl9zdWJgIGNvbW1hbmQgdG8gKmNoYW5nZSogYSBzdWJzdHJpbmcgYXQgYSBrbm93biBwb3NpdGlvbiBpbiBhIHN0cmluZy4KYGBge3J9CnplYnJhX2ZydWl0IDwtIGZydWl0CnN0cl9zdWIoemVicmFfZnJ1aXQsMiwzKSA8LSAiLS1aRUJSQSEhLS0iCnplYnJhX2ZydWl0WzE6MTBdCmBgYAoKIyMgQ2xlYW5pbmcgYW5kIG5vcm1hbGl6aW5nIHN0cmluZ3MKCllvdSBjYW4gZG8gY2FzZS1mb2xkaW5nIHdpdGggYHN0cl90b19sb3dlcmAgKG9yYHN0cl90b191cHBlcmApLiBCYXNlIFIgY29tbWFuZCBpcyBgdG9sb3dlcmAuCgpgYGB7cn0Kc3RyX3RvX2xvd2VyKGNvbWJpbmVkX3N0cmluZykgCmBgYAoKWW91IGNhbiB0cmltIGV4Y2VzcyB3aGl0ZXNwYWNlIG9mZiB0aGUgZW5kcyBvZiBzdHJpbmdzIHdpdGggYHN0cl90cmltYAoKYGBge3J9CmFfc3RyaW5nX3ZlY3RvciA8LSBzdHJfc3BsaXQoY29tYmluZWRfc3RyaW5nLCIhIilbWzFdXQphX3N0cmluZ192ZWN0b3IKc3RyX3RyaW0oYV9zdHJpbmdfdmVjdG9yKQpgYGAKCgojIyBTZWFyY2hpbmcgZm9yIGEgcGF0dGVybgoKV2hhdCBhcmUgdGhlIGxvY2F0aW9ucyBvZiB0aGUgZnJ1aXRzIHdpdGggImJlcnJ5IiBpbiB0aGVpciBuYW1lPwpgYGB7cn0Kc3RyX3doaWNoKGZydWl0LCJiZXJyeSIpCmBgYApXZSBjb3VsZCBnZXQgdGhlIHNhbWUgYW5zd2VyIGluIHRoaXMgaW5zdGFuY2UgZnJvbSBiYXNlIFIncyBgZ3JlcGAsIGJ1dCB0aGUgc3ludGF4IGlzIGRpZmZlcmVudC4KCmBgYHtyfQpncmVwKCJiZXJyeSIsIGZydWl0KQpgYGAKCkZvciBlYWNoIGZydWl0LCBkb2VzIGl0IGNvbnRhaW4gImJlcnJ5Ij8KYGBge3J9CnN0cl9kZXRlY3QoZnJ1aXQsImJlcnJ5IikKYGBgCldlIGNvdWxkIGdldCB0aGUgc2FtZSBhbnN3ZXIgaW4gdGhpcyBpbnN0YW5jZSBmcm9tIGJhc2UgUidzIGBncmVwbGAsIGJ1dCB0aGUgc3ludGF4IGlzIGxpa2UgdGhhdCBvZiBgZ3JlcGAuCmBgYHtyfQpncmVwbCgiYmVycnkiLCBmcnVpdCkKYGBgCgpIb3cgbWFueSBtYXRjaGVzIG9mICJiZXJyeSIgZG9lcyBlYWNoIGZydWl0IGhhdmU/CmBgYHtyfQpzdHJfY291bnQoZnJ1aXQsImJlcnJ5IikKc3RyX2NvdW50KGZydWl0LCJhIikKYGBgCgpXaGVyZSBpcyB0aGUgc3Vic3RyaW5nICJiZXJyeSIgbG9jYXRlZCBpbiBlYWNoIGZydWl0IHN0cmluZz8KYGBge3J9CnN0cl9sb2NhdGUoZnJ1aXRbMToxMF0sImJlcnJ5IikKYGBgCgooV2UgY2FuIGdldCBlcXVpdmFsZW50IGluZm9ybWF0aW9uLCBpbiB2ZXJ5IGRpZmZlcmVudCBmb3JtYXQsIGZyb20gdGhlIGJhc2UgciBjb21tYW5kIGByZWdleHByYC4gSXQncyBub3doZXJlIG5lYXIgYXMgaW50dWl0aXZlIHRob3VnaCwgcmV0dXJuaW5nIGEgdmVjdG9yIGZvciB0aGUgc3RhcnQgcG9zaXRpb25zLCBhbmQgdGhlIGxlbmd0aCBvZiB0aGUgbWF0Y2hlcyBpbiBhbiBhdHRyaWJ1dGUuKQoKYGBge3J9CnJlZ2V4cHJfb2JqIDwtICByZWdleHByKCJiZXJyeSIsZnJ1aXRbMToxMF0pIApyZWdleHByX29iaiAgIyBUaGUgZnVsbCBvYmplY3QKcmVnZXhwcl9vYmpbMToxMF0gIyBUaGUgdmFsdWVzIG9mIHRoZSBvYmplY3QgaXRzZWxmLCB0aGUgc3RhcnRpbmcgcG9zaXRpb25zCmF0dHIocmVnZXhwcl9vYmosIm1hdGNoLmxlbmd0aCIpICMgVGhlIG1hdGNoLmxlbmd0aCBhdHRyaWJ1dGUKYGBgCgpMaXN0IGZydWl0cyB0aGF0IGhhdmUgImJlcnJ5IiBpbiB0aGVpciBuYW1lLgpgYGB7cn0Kc3RyX3N1YnNldChmcnVpdCwiYmVycnkiKQpgYGAKClRoZSBiYXNlIFIgZXF1aXZhbGVudCBpcyBgZ3JlcGAgd2l0aCBgdmFsdWU9VFJVRWA6CmBgYHtyfQpncmVwKCJiZXJyeSIsZnJ1aXQsIHZhbHVlPVRSVUUpCmBgYAoKRm9yIGVhY2ggZnJ1aXQsIGdpdmUgbWUgdGhlIGZpcnN0IHN1YnN0cmluZyB0aGF0IG1hdGNoZXMgImJlcnJ5LiIKYGBge3J9CnN0cl9leHRyYWN0KGZydWl0LCJiZXJyeSIpCmBgYAoKSW4gdGhpcyBpbnN0YW5jZSwgd2UgZ2V0IHRoZSBzYW1lIGFuc3dlciBpbiBtYXRyaXggZm9ybSBmcm9tIGBzdHJfbWF0Y2hgOgoKYGBge3J9CnN0cl9tYXRjaChmcnVpdFsxOjEwXSwiYmVycnkiKQpgYGAKCmBzdHJfbWF0Y2hgIGlzIG1haW5seSBoZWxwZnVsIHdoZW4gd2Ugd2FudCB0byBtYXRjaCBtdWx0aXBsZSB0aGluZ3Mgb3IgdXNlIGEgbGFyZ2VyIHBhdHRlcm4gdG8gaXNvbGF0ZSBzbWFsbGVyIHBpZWNlcy4gV2UnbGwgc2VlIGV4YW1wbGVzIGJlbG93LgoKClRvIGdldCBhIHZpc3VhbCBmb3Igd2hlcmUgeW91ciBtYXRjaGVzIGFyZSBvY2N1cnJpbmcsIHlvdSBjYW4gdXNlIGBzdHJfdmlld19hbGxgLiAoWW91IHdpbGwgc2VlIHNvbWV0aGluZyBpbiB0aGUgUlN0dWRpbyBWaWV3ZXIuKQpgYGB7cn0Kc3RyX3ZpZXdfYWxsKGZydWl0LCJiZXJyeSIpCmBgYAoKRm9yIGV2ZXJ5IGZydWl0IHdpdGggImJlcnJ5IiBpbiB0aGUgbmFtZSwgY2hhbmdlICJiZXJyeSIgdG8gImZpc2giLgpgYGB7cn0Kc3RyX3JlcGxhY2UoZnJ1aXRbMToxMF0sImJlcnJ5IiwgImZpc2giKQpgYGAKCmBzdHJfcmVwbGFjZWAgcmVwbGFjZXMgdGhlICpmaXJzdCogcGF0dGVybiBtYXRjaCBpbiBlYWNoIHN0cmluZzsgYHN0cl9yZXBsYWNlX2FsbGAgcmVwbGFjZXMgKmFsbCogcGF0dGVybiBtYXRjaGVzIGluIGVhY2ggc3RyaW5nLgpgYGB7cn0Kc3RyX3JlcGxhY2UoZnJ1aXRbMToxMF0sImEiLCAiWloiKQpzdHJfcmVwbGFjZV9hbGwoZnJ1aXRbMToxMF0sImEiLCAiWloiKQpgYGAKCiMgUmVndWxhciBleHByZXNzaW9ucwoKU28gZmFyLCBJJ3ZlIG9ubHkgc2VhcmNoZWQgZm9yIHBhdHRlcm5zIHRoYXQgYXJlIG9ubHkgYWxwaGFiZXRpYyBjaGFyYWN0ZXJzIGxpa2UgYCJiZXJyeSJgLiBCdXQgd2UgY2FuIHVzZSBtYWtlIG11Y2ggbW9yZSBlbGFib3JhdGUgYW5kIGZsZXhpYmxlIHBhdHRlcm5zIHVzaW5nICoqcmVndWxhciBleHByZXNzaW9ucyoqLgoKUmVndWxhciBleHByZXNzaW9ucyBjb21lIGluIGEgdmFyaWV0eSBvZiBmbGF2b3JzIGFuZCBSIGhhcyBhIHNvbWV3aGF0IHVudXN1YWwgb25lLiBJIHJlY29tbWVuZCB5b3UgcmVmZXJlbmNlIHRoZSBjaGVhdCBzaGVldCBhbmQgdGhlIG9ubGluZSByZWdleCB0b29sIDxodHRwczovL3JlZ2V4MTAxLmNvbT4gaW4gcGFyYWxsZWwuCgojIyMgVGhpcyBvciB0aGF0LCBub3QgdGhpcyBvciB0aGF0LCB0aGlzIG9yIHRoYXQgb3IgYW55dGhpbmcgaW4gYmV0d2VlbgoKIyMjIyBTcXVhcmUgYnJhY2tldHMgZm9yICJvciIgKGRpc2p1bmN0aW9uKSBvZiBjaGFyYWN0ZXJzCgpNYXRjaCAiYW55IG9uZSBvZiIgdGhlIGNoYXJhY3RlcnMgaW4gdGhlIHNxdWFyZSBicmFja2V0cy4KYGBge3J9CnN0cl9zdWJzZXQoc2VudGVuY2VzLCAnIFtiaHBdZWF0ICcpCmBgYAoKIyMjIyBTcXVhcmUgYnJhY2tldHMgd2l0aCBgXmAgZm9yIG5lZ2F0aW9uLgoKTWF0Y2ggImFueXRoaW5nIGJ1dCBvbmUgb2YiIHRoZSBjaGFyYWN0ZXJzIGluIHRoZSBzcXVhcmUgYnJhY2tldHMuCmBgYHtyfQpzdHJfc3Vic2V0KHNlbnRlbmNlcywgJyBbXmJocF1lYXQgJykKYGBgCgojIyMjIFNxdWFyZSBicmFja2V0cyB3aXRoIGAtYCBmb3IgIm9yIiBvdmVyIGEgKnJhbmdlKiBvZiBjaGFyYWN0ZXJzCmBgYHtyfQpzdHJfc3Vic2V0KHNlbnRlbmNlcywgJyBbYi1wXWVhdCAnKQpgYGAKCiMjIyMgUGFyZW50aGVzZXMgYW5kIHBpcGUgb3BlcmF0b3IgZm9yIG11bHRpLWNoYXJhY3RlciBwYXR0ZXJucwoKV2hlbiB3ZSBuZWVkIGFuICJvciIgb3ZlciBtdWx0aS1jaGFyYWN0ZXIgcGF0dGVybnMsIHdlIGNhbiB1c2UgdGhlICJwaXBlIiBvcGVyYXRvciwgdXNpbmcgcGFyZW50aGVzZXMgYXMgbmVjZXNzYXJ5IHRvIGlkZW50aWZ5IHdoYXQncyB3aXRoIHdoYXQuCmBgYHtyfQpzdHJfc3Vic2V0KGZydWl0LCAnKGJsYWNrfGJsdWV8cmVkKShjdXJyYW50fGJlcnJ5KScpCmBgYAoKVGhlIHBhcmVudGhlc2VzIGFsc28gZGVmaW5lIGEgImNhcHR1cmUgZ3JvdXAiLCBhIGNvbmNlcHQgd2UnbGwgZXhwbGFpbiBiZWxvdy4KCiMjIyBTcGVjaWFsIGNoYXJhY3RlcnMgYW5kIGVzY2FwaW5nCgpJbiBhZGRpdGlvbiB0byB0aGUgYmFja3NsYXNoLCB0aGVyZSBhcmUgYXQgbGVhc3QgMTYgY2hhcmFjdGVycyB0aGF0IGhhdmUgc3BlY2lhbCBtZWFuaW5nIGluIFIgcmVnZXhlcywgYW5kIChtYXkpIGhhdmUgdG8gYmUgZXNjYXBlZCBpbiBvcmRlciB0byBtYXRjaCB0aGUgbGl0ZXJhbCBjaGFyYWN0ZXIuIFRoZXkgYXJlIF4gJCAuICogKyB8ICEgPyAoICkgWyBdIHsgfSA8ID4uCgpGb3IgZXhhbXBsZSwgdGhlIHBlcmlvZCAtLSAiLiIgLS0gbWVhbnMgImFueSBjaGFyYWN0ZXIgYnV0IGEgbmV3bGluZS4iIEl0J3MgYSAqd2lsZGNhcmQqLiBXZSBnZXQgZGlmZmVyZW50IHJlc3VsdHMgd2hlbiB3ZSBlc2NhcGUgb3IgZG9uJ3QgZXNjYXBlLgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiLiIpICAgICMgYW55IHNpbmdsZSBjaGFyYWN0ZXIKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFwuIikgICMgYSBwZXJpb2QKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiYS4iKSAgICMgImEiIGZvbGxvd2VkIGJ5IGFueSBzaW5nbGUgY2hhcmFjdGVyCnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsImFcXC4iKSAjICJhIiBmb2xsb3dlZCBieSBhIHBlcmlvZCAobm8gbWF0Y2gpCmBgYAoKU29tZSBvZiB0aGVzZSBhcmUgb25seSBzcGVjaWFsIGNoYXJhY3RlcnMgaW4gY2VydGFpbiBjb250ZXh0cyBhbmQgZG9uJ3QgaGF2ZSB0byBiZSBlc2NhcGVkIHRvIGJlIHJlY29nbml6ZWQgd2hlbiBub3QgaW4gdGhvc2UgY29udGV4dHMuIEJ1dCB0aGV5IGNhbiBiZSBlc2NhcGVkIGluIGFsbCBjaXJjdW1zdGFuY2VzIGFuZCBJIHJlY29tbWVuZCB0aGF0IHJhdGhlciB0aGFuIHRyeWluZyB0byBmaWd1cmUgb3V0IHRoZSBleGFjdCBydWxlcy4KClRoZSBleGNsYW1hdGlvbiBwb2ludCBpcyBzdWNoIGEgY2hhcmFjdGVyLiAKYGBge3J9CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIiEiKQpzdHJfZXh0cmFjdF9hbGwoY29tYmluZWRfc3RyaW5nLCJcXCEiKQpgYGAKCiMjIyBDbGFzcyBzaG9ydGhhbmRzOiBcXHcgXFxXIFxccyBcXFMgXFxkIFxcRCBhbmQgUE9TSVggY2xhc3NlcwoKQ29udmVyc2VseSwgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIGNoYXJhY3RlcnMgdGhhdCBoYXZlIHNwZWNpYWwgbWVhbmluZyAqb25seSogd2hlbiBlc2NhcGVkLiBUaGUgbWFpbiBvbmVzIGZvciBub3cgYXJlICJcXHciIChhbnkgYWxwaGFudW1lcmljIGNoYXJhY3RlciksICJcXHMiIChhbnkgc3BhY2UgY2hhcmFjdGVyKSwgYW5kICJcXGQiIChhbnkgbnVtZXJpYyBkaWdpdCksIFRoZSBjYXBpdGFsaXplZCB2ZXJzaW9ucyBvZiB0aGVzZSBhcmUgdXNlZCB0byBtZWFuICJhbnl0aGluZyBidXQiIHRoYXQgY2xhc3MuCgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFx3IikgIyBhbnkgIndvcmQiIGNoYXJhY3RlciAtIGxldHRlciBvciBudW1iZXIKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFxXIikgIyBhbnkgbm9ud29yZCBjaGFyYWN0ZXIKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFxzIikgIyBhbnkgd2hpdGVzcGFjZSBjaGFyYWN0ZXIKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFxTIikgIyBhbnkgbm9uc3BhY2UgY2hhcmFjdGVyCnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIlxcZCIpICMgYW55IGRpZ2l0CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIlxcRCIpICMgYW55IG5vbmRpZ2l0IGNoYXJhY3RlcgpgYGAKCiMjIyMgIlBPU0lYIiBjbGFzc2VzCgpUaGVyZSBhcmUgb3RoZXIgcHJlZGVmaW5lZCBjbGFzc2VzIGluIGEgY29tcHV0aW5nIHN0YW5kYXJkIGNhbGxlZCAiUE9TSVgiIHRoYXQgc29tZSByZWdleCBlbmdpbmVzIHJlY29nbml6ZS4gVGhlIG9uZXMgZm9yIFIgYXJlIGxpc3RlZCBvbiB0aGUgY2hlYXQgc2hlZXQuIFRoZXNlIGNhbiBtb3N0bHkgYmUgbWltaWNrZWQgd2l0aCB0aGUgc2hvcnRoYW5kIGxpc3RlZCBhYm92ZS4gVGhlIG1haW4gb25lIEkgZmluZCBoYW5keSBpcyAiWzpwdW5jdDpdIiBmb3IgImFueSBwdW5jdHVhdGlvbiBjaGFyYWN0ZXIuIgoKYGBge3J9CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIls6cHVuY3Q6XSIpICMgYW55ICJwdW5jdHVhdGlvbiIgY2hhcmFjdGVyIApgYGAKCk5vdGUgdGhhdCB3aGVuIGNoYXJhY3RlcnMgc3RyYXkgYmV5b25kIHRoZSBsaW1pdGVkIEFTQ0lJIGNoYXJhY3RlciBzZXQgLS0gb3RoZXIgbGFuZ3VhZ2VzLCBzcGVjaWFsaXplZCBjaGFyYWN0ZXJzIGxpa2UgZW1vamlzIC0tIHRoZXJlJ3Mgbm90IGNvbXBsZXRlIGNvbnNpc3RlbmN5IGluIHdoYXQgbWF5IGJlIGNvbnNpZGVyZWQgYW4gYWxwaGFudW1lcmljIGNoYXJhY3RlciBvciBwdW5jdHVhdGlvbi4KCgojIyBRdWFudGlmaWVyczogKiAuID8KCiMjIyMgUXVhbnRpZmllcnM6ICogKHplcm8gb3IgbW9yZSBvZiB0aGUgcHJldmlvdXMpCgpUaGlzIGlzIGFsc28ga25vd24gYXMgdGhlICJLbGVlbmUgc3RhciIgKHByb25vdW5jZWQgY2xlYW4tZWUpLCBhZnRlciBpdHMgb3JpZ2luYWwgdXNlciAoS2xlZW5lKSB3aG8gaW50cm9kdWNlZCB0aGUgbm90YXRpb24gaW4gZm9ybWFsIGxvZ2ljLgoKYGBge3J9CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIlxcZCoiKSAjICAKYGBgCgojIyMjIFF1YW50aWZpZXJzOiArIChvbmUgb3IgbW9yZSBvZiB0aGUgcHJldmlvdXMpCgpUaGlzIGlzIGFsc28ga25vd24gYXMgdGhlICJLbGVlbmUgcGx1cy4iCmBgYHtyfQpzdHJfZXh0cmFjdF9hbGwoY29tYmluZWRfc3RyaW5nLCJcXGQrIikgIyAgCmBgYAoKIyMjIyBRdWFudGlmaWVyczoge30KCntufSA9ICJleGFjdGx5IG4iIG9mIHRoZSBwcmV2aW91cwp7bixtfSA9ICJiZXR3ZWVuIG4gYW5kIG0iIG9mIHRoZSBwcmV2aW91cwp7bix9ID0gIm4gb3IgbW9yZSIgb2YgdGhlIHByZXZpb3VzCgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKCJ4IHh4IHh4eCB4eHh4IHh4eHh4IiwieHszfSIpICMKc3RyX2V4dHJhY3RfYWxsKCJ4IHh4IHh4eCB4eHh4IHh4eHh4IiwieHszLDR9IikgIyAgCnN0cl9leHRyYWN0X2FsbCgieCB4eCB4eHggeHh4eCB4eHh4eCIsInh7Myx9IikgIyAgCmBgYApXZXJlIGFsbCBvZiB0aG9zZSB3aGF0IHlvdSBleHBlY3RlZD8gVXNlIGBzdHJfdmlld19hbGxgIHRvIHNlZSB3aGF0J3MgaGFwcGVuaW5nLgpgYGB7cn0Kc3RyX3ZpZXdfYWxsKCJ4IHh4IHh4eCB4eHh4IHh4eHh4IiwieHszfSIpICMgIApgYGAKCiMjIyMgUXVlc3Rpb24gTWFyayBhcyBRdWFudGlmaWVyICh6ZXJvIG9yIG9uZSBvZiB0aGUgcHJldmlvdXMpCgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFxkPyIpICMgMCBvciAxIGRpZ2l0CnN0cl9zdWJzZXQoc2VudGVuY2VzLCIgW2JwXT9lYXQiKQpgYGAKCiMjIEdyZWVkeSB2cy4gbm9uLWdyZWVkeSBtYXRjaGluZwoKIyMjIyBRdWVzdGlvbiBNYXJrIGFzIE5vbmdyZWVkeSBNb2RpZmllciB0byBRdWFudGlmaWVyIC0gc21hbGxlc3QgbWF0Y2ggb2YgcHJldmlvdXMgcG9zc2libGUKCgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKCIoRmlyc3QgYnJhY2tldGVkIHN0YXRlbWVudCkgT3RoZXIgdGV4dCAoU2Vjb25kIGJyYWNrZXRlZCBzdGF0ZW1lbnQpIiwiXFwoLitcXCkiKSAjIGdyZWVkeSAtIGNhcHR1cmVzIGZyb20gZmlyc3QgKCB0byBsYXN0ICkKc3RyX2V4dHJhY3RfYWxsKCIoRmlyc3QgYnJhY2tldGVkIHN0YXRlbWVudCkgT3RoZXIgdGV4dCAoU2Vjb25kIGJyYWNrZXRlZCBzdGF0ZW1lbnQpIiwiXFwoLis/XFwpIikgIyBub25ncmVlZHkgLSBmaW5kcyB0d28gc21hbGxlciBtYXRjaGVzCnN0cl9leHRyYWN0X2FsbCgieCB4eCB4eHggeHh4eCB4eHh4eCIsInguK3giKSAjIGRlZmF1bHRzIHRvIGdyZWVkeSAtIGxhcmdlc3QgbWF0Y2gKc3RyX2V4dHJhY3RfYWxsKCJ4IHh4IHh4eCB4eHh4IHh4eHh4IiwieC4rP3giKSAjIG5vbmdyZWVkeSAtIG5vdCB3aGF0IHlvdSBleHBlY3QgLSB3aHk/CmBgYAoKIyMgQW5jaG9ycyBhbmQgd29yZCBib3VuZGFyaWVzOiBeICQgXFxiCgojIyMjIEFuY2hvcnM6IF4gKGJlZ2lubmluZyBvZiBzdHJpbmcpLCAkIChlbmQgb2Ygc3RyaW5nKQoKYGBge3J9CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIlxcdysiKSAjIHNlcXVlbmNlcyBvZiBhbHBoYW51bWVyaWMgY2hhcmFjdGVycwpzdHJfZXh0cmFjdF9hbGwoY29tYmluZWRfc3RyaW5nLCJeXFx3KyIpICMgc2VxdWVuY2VzIGF0IGJlZ2lubmluZyBvZiBzdHJpbmcKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFx3KyQiKSAjIHNlcXVlbmNlcyBhdCBlbmQgb2Ygc3RyaW5nICMgbm9uZSAtIGl0IGVuZHMgaW4gcHVuY3R1YXRpb24KYGBgCgojIyMjIFdvcmQgYm91bmRhcmllczogYFxiYAoKU2ltaWxhcmx5LCB3ZSBjYW4gaWRlbnRpZnkgIndvcmQgYm91bmRhcmllcy4nJyBUaGlzIHNvbHZlcyB0aGUgZ3JlZWR5L25vbmdyZWVkeSBwcm9ibGVtIHdlIGhhZCB3aXRoIHRoZSAieCIgc2VxdWVuY2VzIGFib3ZlLiBJdCBzdGlsbCB0aGlua3MgdGhlIGRlY2ltYWwgcG9pbnQgaW4gYDEwLjJgIGlzIGEgd29yZCBib3VuZGFyeSwgdGhvdWdoLgoKYGBge3J9CnN0cl9leHRyYWN0X2FsbCgieCB4eCB4eHggeHh4eCB4eHh4eCIsIlxcYnguKj9cXGIiKSAjIApzdHJfZXh0cmFjdF9hbGwoY29tYmluZWRfc3RyaW5nLCJcXGJcXHcrP1xcYiIpICMgCmBgYAoKIyMgQ2FwdHVyZSBncm91cHMKCldlJ3ZlIHNlZW4gcGFyZW50aGVzZXMgdXNlZCB3aXRoIHRoZSBwaXBlIG9wZXJhdG9yLiBUaGV5IGFyZSBhbHNvIHVzZWQgdG8gaW5kaWNhdGUgc21hbGxlciBwYXJ0cyBvZiB0aGUgcGF0dGVybiB0aGF0IHdlIHdhbnQgdG8gImNhcHR1cmUuIiBgc3RyX21hdGNoYCB3aWxsIGdpdmUgdXMgYSBtYXRyaXggaW4gd2hpY2ggdGhlIGZpcnN0IGNvbHVtbiBpcyB0aGUgbWF0Y2ggdG8gdGhlICplbnRpcmUqIHBhdHRlcm4sIHdoYXQgd2UndmUgc2VlbiBiZWZvcmUuIEVhY2ggc3Vic2VxdWVudCBjb2x1bW4gaG9sZHMgdGhlIHBhcnQgb2YgdGhlIG1hdGNoIGluIGVhY2ggcGFpciBvZiBwYXJlbnRoZXNlcy4KCmBgYHtyfQpzdHJfbWF0Y2goZnJ1aXRbMToxNV0sIl4oLis/KShiZXJyeXxmcnVpdCkkIikKYGBgCgpXZSBhbHNvIGNhbiB1c2UgYFxcMWAsIGBcXDJgLCBldGMuIHRvIHJlZmVyIHRvIHRoZXNlIGNhcHR1cmUgZ3JvdXBzICpsYXRlciBpbiB0aGUgc2FtZSBjb21tYW5kKi4KCkhlcmUncyBhbiBhY3R1YWwgcmVndWxhciBleHByZXNzaW9uIEkgdXNlIGluIGNsZWFuaW5nIHRoZSBNb29kIG9mIHRoZSBOYXRpb24gcG9sbCBhbnN3ZXJzLiBJIGxhdGVyIHdpbGwgbGV0IHB1bmN0dWF0aW9uIGxpa2UgYCdgIGluZGljYXRlIGEgd29yZCBib3VuZGFyeSwgc28gZmlyc3QsIEkgd2FudCB0byBjb2xsYXBzZSBjb250cmFjdGlvbnMgYWNyb3NzIHRoZSBgJ2AgdG8ga2VlcCB0aGVtIHRvZ2V0aGVyLiBUaGlzLCBmb3IgZXhhbXBsZSBjb2xsYXBzZXMgYW55IGBuJ3RgIGNvbnRyYWN0aW9ucy4KCmBgYHtyfQptb3RuIDwtICJpIGNhbid0IHN0YW5kIGRvbid0cnVtcCBzdXBwb3J0ZXJzIHNob3V0aW5nICdidWlsZCB0aGF0IHdhbGwnISIKbmV3bW90biA8LSBzdHJfcmVwbGFjZV9hbGwobW90biwiKG4ndCkoJHxbWzpwdW5jdDpdXXxcXHMpIiwibnRcXDIiKSAjZG9udCwgY2FudCwgd29udCwgd2FzbnQsIHdlcmVudCwgZGlkbnQsIGNvdWxkbnQsIHdvdWxkbnQsIHNob3VsZG50LCBoYXZlbnQKbmV3bW90bgpgYGAKVGhhdCBsb29rcyBmb3IgdGhlICgxKSBgbid0YCBwYXR0ZXJuIGZvbGxvd2VkIGJ5IHRoZSAoMikgZW5kIG9mIHRoZSBzdHJpbmcsIGFub3RoZXIgcHVuY3R1YXRpb24gbWFyaywgb3IgYSB3aGl0ZXNwYWNlLiBJdCB0aGVuIHJlcGxhY2VzIHRoYXQgd2l0aCBgbnRgIGZvbGxvd2VkIGJ5IHdoYXRldmVyIHRoZSBmb2xsb3dpbmcgY2hhcmFjdGVyIHdhcy4gVGhpcyBhdm9pZHMgcmVwbGFjaW5nIG90aGVyIGFjY2lkZW50YWwgaW5zdGFuY2VzIG9mIHRoZSBgbid0YCBwYXR0ZXJuIHRoYXQgYXJlbid0IGNsZWFybHkgY29udHJhY3Rpb25zLgoKIyBVc2luZyByZWd1bGFyIGV4cHJlc3Npb25zIHRvIGV4dHJhY3QgZGF0YSBmcm9tIHRleHQ6IGFuIGV4YW1wbGUKCkxldCdzIHN0YXJ0IHdpdGggc29tZSBleGFtcGxlIHRleHQ6CmBgYHtyfQp0ZXh0IDwtICJTRUMuIDEwMS4gRklTQ0FMIFlFQVIgMjAxNy4KKGEpIEluIEdlbmVyYWwuLS1UaGVyZSBhcmUgYXV0aG9yaXplZCB0byBiZSBhcHByb3ByaWF0ZWQgdG8gTkFTQQpmb3IgZmlzY2FsIHllYXIgMjAxNyAkMTksNTA4LDAwMCwwMDAsIGFzIGZvbGxvd3M6CigxKSBGb3IgRXhwbG9yYXRpb24sICQ0LDMzMCwwMDAsMDAwLgooMikgRm9yIFNwYWNlIE9wZXJhdGlvbnMsICQ1LDAyMywwMDAsMDAwLgooMykgRm9yIFNjaWVuY2UsICQ1LDUwMCwwMDAsMDAwLgooNCkgRm9yIEFlcm9uYXV0aWNzLCAkNjQwLDAwMCwwMDAuCig1KSBGb3IgU3BhY2UgVGVjaG5vbG9neSwgJDY4NiwwMDAsMDAwLgooNikgRm9yIEVkdWNhdGlvbiwgJDExNSwwMDAsMDAwLgooNykgRm9yIFNhZmV0eSwgU2VjdXJpdHksIGFuZCBNaXNzaW9uIFNlcnZpY2VzLAokMiw3ODgsNjAwLDAwMC4KKDgpIEZvciBDb25zdHJ1Y3Rpb24gYW5kIEVudmlyb25tZW50YWwgQ29tcGxpYW5jZSBhbmQKUmVzdG9yYXRpb24sICQzODgsMDAwLDAwMC4KKDkpIEZvciBJbnNwZWN0b3IgR2VuZXJhbCwgJDM3LDQwMCwwMDAuCihiKSBFeGNlcHRpb24uLS1JbiBhZGRpdGlvbiB0byB0aGUgYW1vdW50cyBhdXRob3JpemVkIHRvIGJlCmFwcHJvcHJpYXRlZCBmb3IgZWFjaCBhY2NvdW50IHVuZGVyIHN1YnNlY3Rpb24gKGEpLCB0aGVyZSBhcmUKYXV0aG9yaXplZCB0byBiZSBhcHByb3ByaWF0ZWQgYWRkaXRpb25hbCBmdW5kcyBmb3IgZWFjaCBzdWNoIGFjY291bnQsCmJ1dCBvbmx5IGlmIHRoZSBhdXRob3JpemVkIGFtb3VudHMgZm9yIGFsbCBzdWNoIGFjY291bnRzIGFyZSBmdWxseQpwcm92aWRlZCBmb3IgaW4gYW5udWFsIGFwcHJvcHJpYXRpb24gQWN0cywgY29uc2lzdGVudCB3aXRoIHRoZQpkaXNjcmV0aW9uYXJ5IHNwZW5kaW5nIGxpbWl0cyBpbiBzZWN0aW9uIDI1MShjKSBvZiB0aGUgQmFsYW5jZWQgQnVkZ2V0CmFuZCBFbWVyZ2VuY3kgRGVmaWNpdCBDb250cm9sIEFjdCBvZiAxOTg1LiIKYGBgCgpXYWl0IC4uLiB0aGF0J3MganVzdCBvbmUgdmFyaWFibGUgaG9sZGluZyBvbmUgc3RyaW5nPyBZZXAuCmBgYHtyfQp0ZXh0CmBgYAoKQWxsIHRob3NlIGBcbmBzIHRoZXJlIGluZGljYXRlIG5ldyBsaW5lcy4KCldlJ3JlIGdvaW5nIHRvIHRyeSB0byB1c2UgcmVndWxhciBleHByZXNzaW9ucyB0byBtYWtlIGRhdGEgb3V0IG9mIHRoZSBhcHByb3ByaWF0aW9ucyBkb2xsYXJzIGFuZCBwdXJwb3NlcyBpbiBidWxsZXRzIDEtOS4KCkxldHMgcGxheSBhcm91bmQgd2l0aCBhIGZldyB0aGluZ3MuIEV4dHJhY3QgYWxsIGNvbnRpZ3VvdXMgc2VxdWVuY2VzIG9mIG9uZSBvciBtb3JlIG51bWJlcnMuCgpgYGB7cn0Kc3RyaW5ncjo6c3RyX2V4dHJhY3RfYWxsKHRleHQsIlswLTldKyIpW1sxXV0KYGBgCgpUaGF0IGRvZXMgdHdvIHRoaW5ncyB3ZSBkb24ndCBsaWtlIC4uLiBzZXBhcmF0ZXMgbnVtYmVycyBhdCB0aGUgMTAwMHMgc2VwYXJhdGluZyBjb21tYSBhbmQgZ2V0cyBudW1iZXJzICgiMTAxIiwgIjIwMTciLCBldGMuKSB0aGF0IGFyZW4ndCBkb2xsYXIgYW1vdW50cy4gU28sIGxldCdzIHRyeSBnZXR0aW5nIGV2ZXJ5dGhpbmcgdGhhdAogIFN0YXJ0cyB3aXRoIGEgIiQiICh3aGljaCBuZWVkcyB0byBiZSBlc2NhcGVkKQogIEZvbGxvd2VkIGJ5IG9uZSBvciBtb3JlIHN0cmluZ3Mgb2YgY29tbWFzIG9yIGRpZ2l0cy4KCmBgYHtyfQpzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwodGV4dCwiXFwkWywwLTldKyIpW1sxXV0gIyBtdXN0IHN0YXJ0IHdpdGggJApgYGAKCkFsbW9zdCAuLi4gZG9uJ3QgbGlrZSB0aGF0IGV4dHJhIGNvbW1hIG9uIHRoZSBmaXJzdCBudW1iZXIuIEFkZAogIGFuZCBlbmRzIHdpdGggYSBudW1iZXIuCmBgYHtyfQpzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwodGV4dCwiXFwkWywwLTldK1swLTldIilbWzFdXQpgYGAKCldlIGNvdWxkIHVzZSBxdWFudGlmaWVycyB0byBnZXQgbnVtYmVycyBvZiAkMSBiaWxsaW9uIG9yIG1vcmUKYGBge3J9CnN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbCh0ZXh0LCJcXCRbLDAtOV17MTIsfVswLTldIilbWzFdXQpgYGAKClRoYXQgYXNrcyBmb3IKICAgIFN0YXJ0cyB3aXRoIGEgIiQiCiAgICBGb2xsb3dlZCBieSAxMiBPUiBNT1JFIGNvbW1hcyBhbmQgbnVtYmVycwogICAgQW5kIGVuZHMgd2l0aCBhIG51bWJlcgoKCk5vdyBsZXQncyB0cnkgdG8gZ2V0IHRoZSBidWxsZXQgbnVtYmVycyBlbmNsb3NlZCBpbiBwYXJlbnRoZXNlczoKYGBge3J9CnN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbCh0ZXh0LCJcXChbMC05XVxcKSIpW1sxXV0KYGBgCgoKU2F5IHdlIG9ubHkgd2FudCB0byBtYXRjaCBsaW5lcyB0aGF0IHN0YXJ0IHdpdGggYSBwYXJ0aWN1bGFyIHNldCBvZiBjaGFyYWN0ZXJzIC4uLgpGaXJzdCBsZXQncyBzcGxpdCBpdCBpbnRvIGxpbmVzOgpgYGB7cn0KdGV4dF9zcGxpdCA8LSBzdHJpbmdyOjpzdHJfc3BsaXQodGV4dCwiXFxuIilbWzFdXQp0ZXh0X3NwbGl0CmBgYAoKTm93IG1hdGNoIG9uIGJlZ2dpbmluZyBzdHJpbmcgYW5jaG9yIGFuZCBvcGVuIHBhcmVuLgoKYGBge3J9CnN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbCh0ZXh0X3NwbGl0LCJeXFwoLioiKQpgYGAKClRoYXQgcmV0dXJuZWQgYSBsaXN0LCBhbmQgd2UnZCBwcm9iYWJseSByYXRoZXIgaGF2ZSBhIHZlY3Rvci4gSW4gdGhpcyBjYXNlLCB3ZSBjYW4ganVzdCB3cmFwIHRoaXMgaW4gYW4gdW5saXN0KCkgc3RhdGVtZW50OgoKYGBge3J9CnVubGlzdChzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwodGV4dF9zcGxpdCwiXlxcKC4qIikpCmBgYAoKU28sIG5vdyBsZXQncyB0cnkgdG8gcHV0IGV2ZXJ5dGhpbmcgd2UndmUgbGVhcm5lZCB0b2dldGhlciBhbmQgbWFrZSBhIGxpdHRsZSBkYXRhc2V0IG91dCBvZiB0aGUgaXRlbXMgKDEpIHRvICg5KSB3aXRoIGRvbGxhciBhbW91bnRzIGFuZCB3aGF0IHRoZXkncmUgZm9yCgpMZXQncyBzZWUgd2hhdCB3ZSBoYXZlIGluIHRoYXQgbGFzdCBjb21tYW5kIC4uCiAgICBXZSBoYXZlIHNvbWUgZXh0cmEgbGluZXMgLi4uICIoYSkiIGFuZCAiKGIpIgogICAgQW5kIHdlJ3JlIG1pc3NpbmcgdGhlICQgbnVtYmVycyBmcm9tIGl0ZW1zICg3KSBhbmQgKDgpCiAgIHdoaWNoIGFyZSBvbiB0aGUgbmV4dCBsaW5lcy4KCkxldCdzIGdvIGJhY2sgdG8gdGhlIG9yaWdpbmFsIGFuZCBnZXQgcmlkIG9mIHRoZSBuZXdsaW5lczoKYGBge3J9Cm9uZV9saW5lIDwtIHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbCh0ZXh0LCJcXG4iLCIgIilbWzFdXQpvbmVfbGluZQpgYGAKCmFuZCBmaW5kIGFsbCB0aGUgbWF0Y2hlcyBmcm9tICIobnVtYmVyKSIgdG8gYSBwZXJpb2QsIGxhemlseSByYXRoZXIgdGhhbiBncmVlZGlseQoKYGBge3J9Cml0ZW1fc3RyaW5ncyA8LSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwob25lX2xpbmUsIlxcKFxcZFxcKS4rP1xcLiIpW1sxXV0KaXRlbV9zdHJpbmdzCmBgYAoKQ2FuIHVzZSBzdHJfbWF0Y2ggYW5kIHBhcmVudGhlc2VzIHRvIGlkZW50aWZ5IHRoZSBzdHVmZiB5b3Ugd2FudApgYGB7cn0KZm9yX3N0cmluZ3MgPC0gc3RyaW5ncjo6c3RyX21hdGNoKGl0ZW1fc3RyaW5ncywiRm9yICguKyksIFxcJCIpCmZvcl9zdHJpbmdzCmBgYAoKVGhlIHNlY29uZCBjb2x1bW4gY29udGFpbnMgb3VyIGxpc3Qgb2YgdGhlICJmb3Igd2hhdCJzLgpgYGB7cn0KZm9yX3N0cmluZ3MgPC0gZm9yX3N0cmluZ3NbLDJdCmZvcl9zdHJpbmdzCmBgYAoKRG8gc29tZXRoaW5nIHNpbWlsYXIgZm9yIG1vbmV5CmBgYHtyfQptb25leV9zdHJpbmdzIDwtIHN0cmluZ3I6OnN0cl9tYXRjaChpdGVtX3N0cmluZ3MsIlxcJChbLFxcZF0rKSIpWywyXQptb25leV9zdHJpbmdzCmBgYAoKR2V0IHJpZCBvZiB0aGUgcHVuY3R1YXRpb24KYGBge3J9Cm1vbmV5X3N0cmluZ3MgPC0gc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKG1vbmV5X3N0cmluZ3MsIltcXCQsXSIsIiIpCm1vbmV5X3N0cmluZ3MKYGBgCgpUdXJuIHRoZW0gaW50byBudW1lcmljIGRhdGEgcmF0aGVyIHRoYW4gc3RyaW5ncy4KYGBge3J9Cm1vbmV5IDwtIGFzLm51bWVyaWMobW9uZXlfc3RyaW5ncykKbW9uZXkKYGBgCgpOb3cgbGV0J3MgbWFrZSBpdCBkYXRhOgpgYGB7cn0KYXBwcm9wcmlhdGlvbnNfZGF0YSA8LSBkYXRhLmZyYW1lKHB1cnBvc2UgPSBmb3Jfc3RyaW5ncyxhbW91bnQgPSBtb25leSkKYXBwcm9wcmlhdGlvbnNfZGF0YQpgYGAKCgoKIyMjIyBPdGhlciBsYW5ndWFnZXMKClJlbWVtYmVyIC4uLiBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZXMgaGFuZGxlIHJlZ3VsYXIgZXhwcmVzc2lvbnMgc2xpZ2h0bHkgZGlmZmVyZW50bHkuIEluIHBhcnRpY2xhciwgUHl0aG9uIGRvZXMgbm90IHVzZSB0aGUgImRvdWJsZSBlc2NhcGUiIGlkaW9tLgoKCg==