(NB: This has its roots in a workshop co-written and delivered by Matt Denny and myself.)
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.
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?
We could get the same answer in this instance from base R’s grep
, but the syntax is different.
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
.
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.
LS0tCnRpdGxlOiAiQW4gSW50cm9kdWN0aW9uIHRvIFN0cmluZyBNYW5pcHVsYXRpb24gYW5kIFJlZ3VsYXIgRXhwcmVzc2lvbnMgaW4gUiIKYXV0aG9yOiAiQnVydCBMLiBNb25yb2UiCnN1YnRpdGxlOiBQZW5uIFN0YXRlIGFuZCBFc3NleCBjb3Vyc2VzIGluICJUZXh0IGFzIERhdGEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIHRoZW1lOiB1bml0ZWQKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgooTkI6IFRoaXMgaGFzIGl0cyByb290cyBpbiBhIHdvcmtzaG9wIGNvLXdyaXR0ZW4gYW5kIGRlbGl2ZXJlZCBieSBNYXR0IERlbm55IGFuZCBteXNlbGYuKQoKRm9yIHRleHQgbWFuaXB1bGF0aW9uIGluIFIsIEkgcmVjb21tZW5kIHRoZSBgc3RyaW5ncmAgcGFja2FnZSwgd2hpY2ggaXMgcGFydCBvZiB0aGUgInRpZHl2ZXJzZS4iIFRoZXJlIGlzIGEgZmFudGFzdGljICJjaGVhdCBzaGVldCIgYXZhaWxhYmxlIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9ibG9iL21hc3Rlci9zdHJpbmdzLnBkZikuIEFsbCBgc3RyaW5ncmAgZnVuY3Rpb25zIHN0YXJ0IHdpdGggYHN0cl9gLgoKKEEgbGl0dGxlIGJpdCBpbiB0aGUgd2VlZHMgLi4uIEZpcnN0LCBhIGxvdCBvZiB0aGlzICpjYW4qIGJlIGRvbmUgaW4gYmFzZSBSLCBidXQgaXQgY2FuIGJlIGxlc3Mgc3RyYWlnaHRmb3J3YXJkIG9yIGludHVpdGl2ZS4gU2Vjb25kLCBhbG1vc3QgYWxsIG9mIGBzdHJpbmdyYCBpcyBhICJ3cmFwcGVyIiBmb3IgY29tbWFuZHMgaW4gdGhlIHBhY2thZ2UgYHN0cmluZ2lgLCBwcm92aWRpbmcgYSBtb3JlIGludHVpdGl2ZSBhbmQgY29uc2lzdGVudCBzeW50YXgsIGVzcGVjaWFsbHkgaWYgeW91IGFyZSBhbHNvIHdvcmtpbmcgd2l0aCBvdGhlciBlbGVtZW50cyBvZiB0aGUgdGlkeXZlcnNlLiBTbywgbm90IG9ubHkgKmNhbiogeW91IGRvIG1vc3Qgb2YgdGhlIGZvbGxvd2luZyB3aXRoIGBzdHJpbmdpYCwgeW91IGFjdHVhbGx5ICphcmUqIGRvaW5nIG1vc3Qgb2YgdGhpcyB3aXRoIGBzdHJpbmdpYCAidW5kZXIgdGhlIGhvb2QuIikKCkluc3RhbGwgYHN0cmluZ3JgIGlmIHlvdSBuZWVkIHRvLCBhbmQgbG9hZCBpdDoKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygic3RyaW5nciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiMgdGhlIGZvbGxvd2luZyBpcyBuZWNlc3NhcnkgZm9yIHRoZSBzdHJfdmlldyBjb21tYW5kIHRvIHdvcmsgCiNpbnN0YWxsLnBhY2thZ2VzKiJodG1sd2lkZ2V0cyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCmxpYnJhcnkoc3RyaW5ncikKYGBgCgojIFN0cmluZ3MKClRoZSBiYXNpYyB0aGluZyB3ZSB3YW50IHRvIG1hbmlwdWxhdGUgYXJlICJzdHJpbmdzLiIgVGhlc2UgYXJlICJjaGFyYWN0ZXIiIG9iamVjdHMgaW4gUiwgYW5kIGNhbiBiZSBzcGVjaWZpZWQgdXNpbmcgZG91YmxlIHF1b3RlcyAoIikgb3Igc2luZ2xlIHF1b3RlcyAoJyk6CgpgYGB7cn0KYV9zdHJpbmcgPC0gIkV4YW1wbGUgU1RSSU5HLCB3aXRoIG51bWJlcnMgKDEyLCAxNSBhbmQgYWxzbyAxMC4yKT8hIgphX3N0cmluZwpgYGAKCkl0J3MgcmVhbGx5IGEgbWF0dGVyIG9mIHN0eWxlIG9yIGNvbnZlbmllbmNlLCBidXQgeW91IG1pZ2h0IHVzZSBzaW5nbGUgcXVvdGVzIGlmIHlvdXIgc3RyaW5nIGFjdHVhbGx5IGNvbnRhaW5zIGRvdWJsZSBxdW90ZXM6CmBgYHtyfQpteV9zaW5nbGVfcXVvdGVkX3N0cmluZyA8LSAnSGUgYXNrZWQsICJXaHkgd291bGQgeW91IHVzZSBzaW5nbGUgcXVvdGVzPyInCm15X3NpbmdsZV9xdW90ZWRfc3RyaW5nCmBgYAoKUiBhbHdheXMgZGlzcGxheXMgc3RyaW5ncyBpbiBkb3VibGUtcXVvdGVzLiBUaGF0IGBcYCB0ZWxscyBSIHRvICJlc2NhcGUiIHRoZSBuZXh0IGNoYXJhY3Rlci4gSW4gdGhpcyBjYXNlLCB0aGUgYFwiYCBpcyBzYXlpbmcsIGAiYCBpcyBwYXJ0IG9mIHRoZSBzdHJpbmcsIG5vdCB0aGUgZW5kIG9mIHRoZSBzdHJpbmcuCgpZb3UgY2FuIHNwZWNpZnkgdGhlIHN0cmluZyB0aGF0IHdheSBpZiB5b3Ugd2FudC4KYGBge3J9Cm15X3N0cmluZ193aXRoX2RvdWJsZV9xdW90ZXMgPC0gIlNoZSBhbnN3ZXJlZCwgXCJDb252ZW5pZW5jZSwgYnV0IHlvdSBuZXZlciByZWFsbHkgaGF2ZSB0by5cIiIKbXlfc3RyaW5nX3dpdGhfZG91YmxlX3F1b3RlcwpgYGAKCklmIHlvdSBldmVyIHdhbnQgdG8gc2VlIGhvdyB5b3VyIHN0cmluZyB3aXRoIGVzY2FwZSBjaGFyYWN0ZXJzIGRpc3BsYXlzIHdoZW4gcHJpbnRlZCBvciAodHlwaWNhbGx5KSBpbiBhbiBlZGl0b3IsIHVzZSBgd3JpdGVMaW5lc2AuCmBgYHtyfQp3cml0ZUxpbmVzKG15X3NpbmdsZV9xdW90ZWRfc3RyaW5nKQp3cml0ZUxpbmVzKG15X3N0cmluZ193aXRoX2RvdWJsZV9xdW90ZXMpCmBgYAoKVGhpcyBjYW4gZ2V0IGEgbGl0dGxlIGJpdCBjb25mdXNpbmcuIEZvciBleGFtcGxlLCBzaW5jZSB0aGUgYmFja3NsYXNoIGNoYXJhY3RlciB0ZWxscyBSIHRvIGVzY2FwZSwgdG8gaW5kaWNhdGUgYW4gYWN0dWFsIGJhY2tzbGFzaCBjaGFyYWN0ZXIgeW91IGhhdmUgdG8gYmFja3NsYXNoIHlvdXIgYmFja3NsYXNoZXM6CgpgYGB7cn0KYV9zdHJpbmdfd2l0aF9iYWNrc2xhc2hlcyA9ICJUbyBpbmRpY2F0ZSBhIGJhY2tzbGFzaCwgXFwsIHlvdSBoYXZlIHRvIHR5cGUgdHdvOiBcXFxcLiBKdXN0IHRoZXJlLCB0byBpbmRpY2F0ZSB0d28gYmFja3NsYXNoZXMsIEkgaGFkIHRvIHR5cGUgZm91cjogXFxcXFxcXFwuIgphX3N0cmluZ193aXRoX2JhY2tzbGFzaGVzCndyaXRlTGluZXMoYV9zdHJpbmdfd2l0aF9iYWNrc2xhc2hlcykKYGBgCgoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIHNwZWNpYWwgZXNjYXBlIGNoYXJhY3RlcnMgdGhhdCBhcmUgdXNlZCB0byByZXByZXNlbnQgdGhpbmdzIGxpa2UgImNvbnRyb2wgY2hhcmFjdGVycy4iIFRoZSBtb3N0IGNvbW1vbiBhcmUgdHdvIHRoYXQgeW91J3JlIGFscmVhZHkgdXNlZCB0byB0YXBwaW5nIGEga2V5Ym9hcmQga2V5IGZvciB3aXRob3V0IGV4cGVjdGluZyBhIGNoYXJhY3RlciB0byBhcHBlYXIgb24geW91ciBzY3JlZW46IGBcdGAgKHRhYikgYW5kIGBcbmAgKG5ld2xpbmUpLgpgYGB7cn0KdGVzdF9zdHJpbmcgPC0gImFiYyBBQkMgMTIzXHQuIT9cXCgpe31cbiIKdGVzdF9zdHJpbmcKd3JpdGVMaW5lcyh0ZXN0X3N0cmluZykKYGBgCgpBcyB3aXRoIHByZXR0eSBtdWNoIGV2ZXJ5dGhpbmcgaW4gUiwgeW91IGNhbiBoYXZlIGEgdmVjdG9yIG9mIHN0cmluZ3MgKGEgImNoYXJhY3RlciB2ZWN0b3IiKS4KCmBgYHtyfQphX3ZlY3Rvcl9vZl9zdHJpbmdzIDwtIGMoImFiY2RlIiwgIjEyMyIsICJjaGlja2VuIG9mIHRoZSBzZWEiKQphX3ZlY3Rvcl9vZl9zdHJpbmdzCmBgYAoKQmFzZSBSIGNvbWVzIHdpdGggYSBmZXcgYnVpbHQgaW4gc3RyaW5nIHZlY3RvcnMgLS0gYGxldHRlcnNgLCBgTEVUVEVSU2AsIGBtb250aC5hYmJgLCBhbmQgYG1vbnRoLm5hbWVgLiBMb2FkaW5nIGBzdHJpbmdyYCBhbHNvIGxvYWRzIGEgZmV3IG1vcmU6IGBmcnVpdGAsIGB3b3Jkc2AsIGFuZCBgc2VudGVuY2VzYC4gV2UnbGwgdXNlIHRoZXNlIGZvciBhIGZldyBleGFtcGxlcywgc28gbGV0J3MgbG9vayBhdCB0aGVtLiBUaGUgbGFzdCB0d28gYXJlIGxvbmcsIHNvIHdlJ2xsIGp1c3QgbG9vayBhdCB0aGUgZmlyc3QgZmV3IGVudHJpZXMgb2YgZWFjaCBvZiB0aG9zZS4KCmBgYHtyfQpsZXR0ZXJzCmBgYAoKYGBge3J9CkxFVFRFUlMKYGBgCgpgYGB7cn0KbW9udGguYWJiCmBgYAoKYGBge3J9Cm1vbnRoLm5hbWUKYGBgCgpgYGB7cn0KZnJ1aXQKYGBgCgpgYGB7cn0KbGVuZ3RoKHdvcmRzKQp3b3Jkc1sxOjVdCmBgYAoKYGBge3J9Cmxlbmd0aChzZW50ZW5jZXMpCnNlbnRlbmNlc1sxOjVdCmBgYAoKIyMgQmFzaWMgc3RyaW5nIG9wZXJhdGlvbnMKCllvdSBjYW4gY29tYmluZSwgb3IgImNvbmNhdGVuYXRlIiwgdHdvIHN0cmluZ3Mgd2l0aCB0aGUgYHN0cmluZ3JgIGNvbW1hbmQgYHN0cl9jYCBvciB0aGUgc3ludGFjdGljYWxseSBpZGVudGljYWwgYmFzZSBSIGNvbW1hbmQgYHBhc3RlYC4KCmBgYHtyfQpzZWNvbmRfc3RyaW5nIDwtICJXb3csIHR3byBzZW50ZW5jZXMuIgpjb21iaW5lZF9zdHJpbmcgPC0gc3RyX2MoYV9zdHJpbmcsc2Vjb25kX3N0cmluZyxzZXAgPSAiICIpCmNvbWJpbmVkX3N0cmluZwpgYGAKCmBgYHtyfQpwYXN0ZShhX3N0cmluZyxzZWNvbmRfc3RyaW5nLHNlcCA9ICIgIikKYGBgCgpZb3UgY2FuIHppcCB0b2dldGhlciB2ZWN0b3JzIG9mIHN0cmluZ3MuCmBgYHtyfQpzdHJfYyhtb250aC5hYmIsIG1vbnRoLm5hbWUsIHNlcD0iIHN0YW5kcyBmb3IgIikKYGBgCgpZb3UgY2FuIGNvbmNhdGVuYXRlIGFsbCB0aGUgc3RyaW5ncyBpbiBhIHZlY3RvciB0b2dldGhlciB3aXRoIHRoZSBgY29sbGFwc2VgIHBhcmFtZXRlci4KCmBgYHtyfQpzdHJfYyhtb250aC5uYW1lLCBjb2xsYXBzZT0iIHRoZW4gIikKYGBgCgpPciBib3RoLgpgYGB7cn0Kc3RyX2MobGV0dGVycyxMRVRURVJTLCBzZXA9IiIsIGNvbGxhcHNlPSIsIikKYGBgCgpPciBtb3JlIHRoYW4gdHdvIHZlY3RvcnMgb3IgY29uc3RhbnRzLgpgYGB7cn0Kc3RyX2MobW9udGgubmFtZSwiICgiLCBtb250aC5hYmIsICIpIiwgc2VwPSIiLCBjb2xsYXBzZT0iIHRoZW4gIikKYGBgCgpZb3UgY2FuIHNwbGl0IGEgc3RyaW5nIHVwIHVzaW5nIGBzdHJfc3BsaXRgLiBUaGUgc2ltaWxhciBiYXNlIFIgY29tbWFuZCBpcyBgc3Ryc3BsaXRgLgoKYGBge3J9CnN0cl9zcGxpdChjb21iaW5lZF9zdHJpbmcsIiEiKSAjIHNwbGl0IG9uICEKc3RyX3NwbGl0KGNvbWJpbmVkX3N0cmluZywiLCIpICMgc3BsaXQgb24gLApzdHJzcGxpdChjb21iaW5lZF9zdHJpbmcsIiEiKSAjIGJhc2UgUgpgYGAKCmBzdHJfc3BsaXRgIHJldHVybnMgYSAqbGlzdCogb2YgY2hhcmFjdGVyIHZlY3RvcnMuIFdpdGggYHNpbXBsaWZ5ID0gVFJVRWAsIGl0IHJldHVybnMgYSBjaGFyYWN0ZXIgKm1hdHJpeCogKHdpdGggb25lIHJvdyBhbmQgdHdvIGNvbHVtbnMpLgoKYGBge3J9CnN0cl9zcGxpdChjb21iaW5lZF9zdHJpbmcsIiEiLHNpbXBsaWZ5PVRSVUUpCmBgYAoKU28sIGluIHRoaXMgY2FzZSB5b3UgY291bGQgZ2V0IHRoYXQgYXMgb25lIHZlY3RvciBhIG51bWJlciBvZiB3YXlzLgpgYGB7cn0Kc3RyX3NwbGl0KGNvbWJpbmVkX3N0cmluZywiISIpW1sxXV0gI2dpdmUgZmlyc3QgZWxlbWVudCBvZiBsaXN0CnN0cl9zcGxpdChjb21iaW5lZF9zdHJpbmcsIiEiLHNpbXBsaWZ5PVRSVUUpWzEsXSAjIGdpdmUgZmlyc3Qgcm93IG9mIG1hdHJpeAp1bmxpc3Qoc3RyX3NwbGl0KGNvbWJpbmVkX3N0cmluZywiISIpKSAjdHVybiBsaXN0IGludG8gdmVjdG9yCmBgYAoKIyMgIFN1YnN0cmluZ3MKCllvdSBjYW4gdXNlIHRoZSBgc3RyX3N1YmAgY29tbWFuZCB0byBpZGVudGlmeSBhIHN1YnN0cmluZyBhdCBhIGtub3duIHBvc2l0aW9uIGluIGEgc3RyaW5nLgpgYGB7cn0Kc3RyX3N1YihmcnVpdCwyLDQpICMgMm5kIHRocm91Z2ggNXRoIGNoYXJhY3RlciBvZiBlYWNoIGZydWl0CnN0cl9zdWIoZnJ1aXQsLTIsLTEpICMgTGFzdCB0d28gY2hhcmFjdGVycyBvZiBlYWNoIGZydWl0CnNvbWVfZGF0ZXMgPC0gYygiMTk5OS8wMS8wMSIsIjE5OTgvMTIvMTUiLCIyMDAxLzA5LzAzIikKc3RyX3N1Yihzb21lX2RhdGVzLDEsNCkKc3RyX3N1Yihzb21lX2RhdGVzLDYsNykKYGBgCgpZb3UgY2FuIHVzZSB0aGUgYHN0cl9zdWJgIGNvbW1hbmQgdG8gKmNoYW5nZSogYSBzdWJzdHJpbmcgYXQgYSBrbm93biBwb3NpdGlvbiBpbiBhIHN0cmluZy4KYGBge3J9CnplYnJhX2ZydWl0IDwtIGZydWl0CnN0cl9zdWIoemVicmFfZnJ1aXQsMiwzKSA8LSAiLS1aRUJSQSEhLS0iCnplYnJhX2ZydWl0WzE6MTBdCmBgYAoKIyMgQ2xlYW5pbmcgYW5kIG5vcm1hbGl6aW5nIHN0cmluZ3MKCllvdSBjYW4gZG8gY2FzZS1mb2xkaW5nIHdpdGggYHN0cl90b19sb3dlcmAgKG9yYHN0cl90b191cHBlcmApLiBCYXNlIFIgY29tbWFuZCBpcyBgdG9sb3dlcmAuCgpgYGB7cn0Kc3RyX3RvX2xvd2VyKGNvbWJpbmVkX3N0cmluZykgCmBgYAoKWW91IGNhbiB0cmltIGV4Y2VzcyB3aGl0ZXNwYWNlIG9mZiB0aGUgZW5kcyBvZiBzdHJpbmdzIHdpdGggYHN0cl90cmltYAoKYGBge3J9CmFfc3RyaW5nX3ZlY3RvciA8LSBzdHJfc3BsaXQoY29tYmluZWRfc3RyaW5nLCIhIilbWzFdXQphX3N0cmluZ192ZWN0b3IKc3RyX3RyaW0oYV9zdHJpbmdfdmVjdG9yKQpgYGAKCgojIyBTZWFyY2hpbmcgZm9yIGEgcGF0dGVybgoKV2hhdCBhcmUgdGhlIGxvY2F0aW9ucyBvZiB0aGUgZnJ1aXRzIHdpdGggImJlcnJ5IiBpbiB0aGVpciBuYW1lPwpgYGB7cn0Kc3RyX3doaWNoKGZydWl0LCJiZXJyeSIpCmBgYApXZSBjb3VsZCBnZXQgdGhlIHNhbWUgYW5zd2VyIGluIHRoaXMgaW5zdGFuY2UgZnJvbSBiYXNlIFIncyBgZ3JlcGAsIGJ1dCB0aGUgc3ludGF4IGlzIGRpZmZlcmVudC4KCmBgYHtyfQpncmVwKCJiZXJyeSIsIGZydWl0KQpgYGAKCkZvciBlYWNoIGZydWl0LCBkb2VzIGl0IGNvbnRhaW4gImJlcnJ5Ij8KYGBge3J9CnN0cl9kZXRlY3QoZnJ1aXQsImJlcnJ5IikKYGBgCldlIGNvdWxkIGdldCB0aGUgc2FtZSBhbnN3ZXIgaW4gdGhpcyBpbnN0YW5jZSBmcm9tIGJhc2UgUidzIGBncmVwbGAsIGJ1dCB0aGUgc3ludGF4IGlzIGxpa2UgdGhhdCBvZiBgZ3JlcGAuCmBgYHtyfQpncmVwbCgiYmVycnkiLCBmcnVpdCkKYGBgCgpIb3cgbWFueSBtYXRjaGVzIG9mICJiZXJyeSIgZG9lcyBlYWNoIGZydWl0IGhhdmU/CmBgYHtyfQpzdHJfY291bnQoZnJ1aXQsImJlcnJ5IikKc3RyX2NvdW50KGZydWl0LCJhIikKYGBgCgpXaGVyZSBpcyB0aGUgc3Vic3RyaW5nICJiZXJyeSIgbG9jYXRlZCBpbiBlYWNoIGZydWl0IHN0cmluZz8KYGBge3J9CnN0cl9sb2NhdGUoZnJ1aXRbMToxMF0sImJlcnJ5IikKYGBgCgooV2UgY2FuIGdldCBlcXVpdmFsZW50IGluZm9ybWF0aW9uLCBpbiB2ZXJ5IGRpZmZlcmVudCBmb3JtYXQsIGZyb20gdGhlIGJhc2UgciBjb21tYW5kIGByZWdleHByYC4gSXQncyBub3doZXJlIG5lYXIgYXMgaW50dWl0aXZlIHRob3VnaCwgcmV0dXJuaW5nIGEgdmVjdG9yIGZvciB0aGUgc3RhcnQgcG9zaXRpb25zLCBhbmQgdGhlIGxlbmd0aCBvZiB0aGUgbWF0Y2hlcyBpbiBhbiBhdHRyaWJ1dGUuKQoKYGBge3J9CnJlZ2V4cHJfb2JqIDwtICByZWdleHByKCJiZXJyeSIsZnJ1aXRbMToxMF0pIApyZWdleHByX29iaiAgIyBUaGUgZnVsbCBvYmplY3QKcmVnZXhwcl9vYmpbMToxMF0gIyBUaGUgdmFsdWVzIG9mIHRoZSBvYmplY3QgaXRzZWxmLCB0aGUgc3RhcnRpbmcgcG9zaXRpb25zCmF0dHIocmVnZXhwcl9vYmosIm1hdGNoLmxlbmd0aCIpICMgVGhlIG1hdGNoLmxlbmd0aCBhdHRyaWJ1dGUKYGBgCgpMaXN0IGZydWl0cyB0aGF0IGhhdmUgImJlcnJ5IiBpbiB0aGVpciBuYW1lLgpgYGB7cn0Kc3RyX3N1YnNldChmcnVpdCwiYmVycnkiKQpgYGAKClRoZSBiYXNlIFIgZXF1aXZhbGVudCBpcyBgZ3JlcGAgd2l0aCBgdmFsdWU9VFJVRWA6CmBgYHtyfQpncmVwKCJiZXJyeSIsZnJ1aXQsIHZhbHVlPVRSVUUpCmBgYAoKRm9yIGVhY2ggZnJ1aXQsIGdpdmUgbWUgdGhlIGZpcnN0IHN1YnN0cmluZyB0aGF0IG1hdGNoZXMgImJlcnJ5LiIKYGBge3J9CnN0cl9leHRyYWN0KGZydWl0LCJiZXJyeSIpCmBgYAoKSW4gdGhpcyBpbnN0YW5jZSwgd2UgZ2V0IHRoZSBzYW1lIGFuc3dlciBpbiBtYXRyaXggZm9ybSBmcm9tIGBzdHJfbWF0Y2hgOgoKYGBge3J9CnN0cl9tYXRjaChmcnVpdFsxOjEwXSwiYmVycnkiKQpgYGAKCmBzdHJfbWF0Y2hgIGlzIG1haW5seSBoZWxwZnVsIHdoZW4gd2Ugd2FudCB0byBtYXRjaCBtdWx0aXBsZSB0aGluZ3Mgb3IgdXNlIGEgbGFyZ2VyIHBhdHRlcm4gdG8gaXNvbGF0ZSBzbWFsbGVyIHBpZWNlcy4gV2UnbGwgc2VlIGV4YW1wbGVzIGJlbG93LgoKClRvIGdldCBhIHZpc3VhbCBmb3Igd2hlcmUgeW91ciBtYXRjaGVzIGFyZSBvY2N1cnJpbmcsIHlvdSBjYW4gdXNlIGBzdHJfdmlld19hbGxgLiAoWW91IHdpbGwgc2VlIHNvbWV0aGluZyBpbiB0aGUgUlN0dWRpbyBWaWV3ZXIuKQpgYGB7cn0Kc3RyX3ZpZXdfYWxsKGZydWl0LCJiZXJyeSIpCmBgYAoKRm9yIGV2ZXJ5IGZydWl0IHdpdGggImJlcnJ5IiBpbiB0aGUgbmFtZSwgY2hhbmdlICJiZXJyeSIgdG8gImZpc2giLgpgYGB7cn0Kc3RyX3JlcGxhY2UoZnJ1aXRbMToxMF0sImJlcnJ5IiwgImZpc2giKQpgYGAKCmBzdHJfcmVwbGFjZWAgcmVwbGFjZXMgdGhlICpmaXJzdCogcGF0dGVybiBtYXRjaCBpbiBlYWNoIHN0cmluZzsgYHN0cl9yZXBsYWNlX2FsbGAgcmVwbGFjZXMgKmFsbCogcGF0dGVybiBtYXRjaGVzIGluIGVhY2ggc3RyaW5nLgpgYGB7cn0Kc3RyX3JlcGxhY2UoZnJ1aXRbMToxMF0sImEiLCAiWloiKQpzdHJfcmVwbGFjZV9hbGwoZnJ1aXRbMToxMF0sImEiLCAiWloiKQpgYGAKCiMgUmVndWxhciBleHByZXNzaW9ucwoKU28gZmFyLCBJJ3ZlIG9ubHkgc2VhcmNoZWQgZm9yIHBhdHRlcm5zIHRoYXQgYXJlIG9ubHkgYWxwaGFiZXRpYyBjaGFyYWN0ZXJzIGxpa2UgYCJiZXJyeSJgLiBCdXQgd2UgY2FuIHVzZSBtYWtlIG11Y2ggbW9yZSBlbGFib3JhdGUgYW5kIGZsZXhpYmxlIHBhdHRlcm5zIHVzaW5nICoqcmVndWxhciBleHByZXNzaW9ucyoqLgoKUmVndWxhciBleHByZXNzaW9ucyBjb21lIGluIGEgdmFyaWV0eSBvZiBmbGF2b3JzIGFuZCBSIGhhcyBhIHNvbWV3aGF0IHVudXN1YWwgb25lLiBJIHJlY29tbWVuZCB5b3UgcmVmZXJlbmNlIHRoZSBjaGVhdCBzaGVldCBhbmQgdGhlIG9ubGluZSByZWdleCB0b29sIDxodHRwczovL3JlZ2V4MTAxLmNvbT4gaW4gcGFyYWxsZWwuCgojIyMgVGhpcyBvciB0aGF0LCBub3QgdGhpcyBvciB0aGF0LCB0aGlzIG9yIHRoYXQgb3IgYW55dGhpbmcgaW4gYmV0d2VlbgoKIyMjIyBTcXVhcmUgYnJhY2tldHMgZm9yICJvciIgKGRpc2p1bmN0aW9uKSBvZiBjaGFyYWN0ZXJzCgpNYXRjaCAiYW55IG9uZSBvZiIgdGhlIGNoYXJhY3RlcnMgaW4gdGhlIHNxdWFyZSBicmFja2V0cy4KYGBge3J9CnN0cl9zdWJzZXQoc2VudGVuY2VzLCAnIFtiaHBdZWF0ICcpCmBgYAoKIyMjIyBTcXVhcmUgYnJhY2tldHMgd2l0aCBgXmAgZm9yIG5lZ2F0aW9uLgoKTWF0Y2ggImFueXRoaW5nIGJ1dCBvbmUgb2YiIHRoZSBjaGFyYWN0ZXJzIGluIHRoZSBzcXVhcmUgYnJhY2tldHMuCmBgYHtyfQpzdHJfc3Vic2V0KHNlbnRlbmNlcywgJyBbXmJocF1lYXQgJykKYGBgCgojIyMjIFNxdWFyZSBicmFja2V0cyB3aXRoIGAtYCBmb3IgIm9yIiBvdmVyIGEgKnJhbmdlKiBvZiBjaGFyYWN0ZXJzCmBgYHtyfQpzdHJfc3Vic2V0KHNlbnRlbmNlcywgJyBbYi1wXWVhdCAnKQpgYGAKCiMjIyMgUGFyZW50aGVzZXMgYW5kIHBpcGUgb3BlcmF0b3IgZm9yIG11bHRpLWNoYXJhY3RlciBwYXR0ZXJucwoKV2hlbiB3ZSBuZWVkIGFuICJvciIgb3ZlciBtdWx0aS1jaGFyYWN0ZXIgcGF0dGVybnMsIHdlIGNhbiB1c2UgdGhlICJwaXBlIiBvcGVyYXRvciwgdXNpbmcgcGFyZW50aGVzZXMgYXMgbmVjZXNzYXJ5IHRvIGlkZW50aWZ5IHdoYXQncyB3aXRoIHdoYXQuCmBgYHtyfQpzdHJfc3Vic2V0KGZydWl0LCAnKGJsYWNrfGJsdWV8cmVkKShjdXJyYW50fGJlcnJ5KScpCmBgYAoKVGhlIHBhcmVudGhlc2VzIGFsc28gZGVmaW5lIGEgImNhcHR1cmUgZ3JvdXAiLCBhIGNvbmNlcHQgd2UnbGwgZXhwbGFpbiBiZWxvdy4KCiMjIyBTcGVjaWFsIGNoYXJhY3RlcnMgYW5kIGVzY2FwaW5nCgpJbiBhZGRpdGlvbiB0byB0aGUgYmFja3NsYXNoLCB0aGVyZSBhcmUgYXQgbGVhc3QgMTYgY2hhcmFjdGVycyB0aGF0IGhhdmUgc3BlY2lhbCBtZWFuaW5nIGluIFIgcmVnZXhlcywgYW5kIChtYXkpIGhhdmUgdG8gYmUgZXNjYXBlZCBpbiBvcmRlciB0byBtYXRjaCB0aGUgbGl0ZXJhbCBjaGFyYWN0ZXIuIFRoZXkgYXJlIF4gJCAuICogKyB8ICEgPyAoICkgWyBdIHsgfSA8ID4uCgpGb3IgZXhhbXBsZSwgdGhlIHBlcmlvZCAtLSAiLiIgLS0gbWVhbnMgImFueSBjaGFyYWN0ZXIgYnV0IGEgbmV3bGluZS4iIEl0J3MgYSAqd2lsZGNhcmQqLiBXZSBnZXQgZGlmZmVyZW50IHJlc3VsdHMgd2hlbiB3ZSBlc2NhcGUgb3IgZG9uJ3QgZXNjYXBlLgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiLiIpICAgICMgYW55IHNpbmdsZSBjaGFyYWN0ZXIKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFwuIikgICMgYSBwZXJpb2QKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiYS4iKSAgICMgImEiIGZvbGxvd2VkIGJ5IGFueSBzaW5nbGUgY2hhcmFjdGVyCnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsImFcXC4iKSAjICJhIiBmb2xsb3dlZCBieSBhIHBlcmlvZCAobm8gbWF0Y2gpCmBgYAoKU29tZSBvZiB0aGVzZSBhcmUgb25seSBzcGVjaWFsIGNoYXJhY3RlcnMgaW4gY2VydGFpbiBjb250ZXh0cyBhbmQgZG9uJ3QgaGF2ZSB0byBiZSBlc2NhcGVkIHRvIGJlIHJlY29nbml6ZWQgd2hlbiBub3QgaW4gdGhvc2UgY29udGV4dHMuIEJ1dCB0aGV5IGNhbiBiZSBlc2NhcGVkIGluIGFsbCBjaXJjdW1zdGFuY2VzIGFuZCBJIHJlY29tbWVuZCB0aGF0IHJhdGhlciB0aGFuIHRyeWluZyB0byBmaWd1cmUgb3V0IHRoZSBleGFjdCBydWxlcy4KClRoZSBleGNsYW1hdGlvbiBwb2ludCBpcyBzdWNoIGEgY2hhcmFjdGVyLiAKYGBge3J9CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIiEiKQpzdHJfZXh0cmFjdF9hbGwoY29tYmluZWRfc3RyaW5nLCJcXCEiKQpgYGAKCiMjIyBDbGFzcyBzaG9ydGhhbmRzOiBcXHcgXFxXIFxccyBcXFMgXFxkIFxcRCBhbmQgUE9TSVggY2xhc3NlcwoKQ29udmVyc2VseSwgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIGNoYXJhY3RlcnMgdGhhdCBoYXZlIHNwZWNpYWwgbWVhbmluZyAqb25seSogd2hlbiBlc2NhcGVkLiBUaGUgbWFpbiBvbmVzIGZvciBub3cgYXJlICJcXHciIChhbnkgYWxwaGFudW1lcmljIGNoYXJhY3RlciksICJcXHMiIChhbnkgc3BhY2UgY2hhcmFjdGVyKSwgYW5kICJcXGQiIChhbnkgbnVtZXJpYyBkaWdpdCksIFRoZSBjYXBpdGFsaXplZCB2ZXJzaW9ucyBvZiB0aGVzZSBhcmUgdXNlZCB0byBtZWFuICJhbnl0aGluZyBidXQiIHRoYXQgY2xhc3MuCgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFx3IikgIyBhbnkgIndvcmQiIGNoYXJhY3RlciAtIGxldHRlciBvciBudW1iZXIKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFxXIikgIyBhbnkgbm9ud29yZCBjaGFyYWN0ZXIKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFxzIikgIyBhbnkgd2hpdGVzcGFjZSBjaGFyYWN0ZXIKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFxTIikgIyBhbnkgbm9uc3BhY2UgY2hhcmFjdGVyCnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIlxcZCIpICMgYW55IGRpZ2l0CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIlxcRCIpICMgYW55IG5vbmRpZ2l0IGNoYXJhY3RlcgpgYGAKCiMjIyMgIlBPU0lYIiBjbGFzc2VzCgpUaGVyZSBhcmUgb3RoZXIgcHJlZGVmaW5lZCBjbGFzc2VzIGluIGEgY29tcHV0aW5nIHN0YW5kYXJkIGNhbGxlZCAiUE9TSVgiIHRoYXQgc29tZSByZWdleCBlbmdpbmVzIHJlY29nbml6ZS4gVGhlIG9uZXMgZm9yIFIgYXJlIGxpc3RlZCBvbiB0aGUgY2hlYXQgc2hlZXQuIFRoZXNlIGNhbiBtb3N0bHkgYmUgbWltaWNrZWQgd2l0aCB0aGUgc2hvcnRoYW5kIGxpc3RlZCBhYm92ZS4gVGhlIG1haW4gb25lIEkgZmluZCBoYW5keSBpcyAiWzpwdW5jdDpdIiBmb3IgImFueSBwdW5jdHVhdGlvbiBjaGFyYWN0ZXIuIgoKYGBge3J9CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIls6cHVuY3Q6XSIpICMgYW55ICJwdW5jdHVhdGlvbiIgY2hhcmFjdGVyIApgYGAKCk5vdGUgdGhhdCB3aGVuIGNoYXJhY3RlcnMgc3RyYXkgYmV5b25kIHRoZSBsaW1pdGVkIEFTQ0lJIGNoYXJhY3RlciBzZXQgLS0gb3RoZXIgbGFuZ3VhZ2VzLCBzcGVjaWFsaXplZCBjaGFyYWN0ZXJzIGxpa2UgZW1vamlzIC0tIHRoZXJlJ3Mgbm90IGNvbXBsZXRlIGNvbnNpc3RlbmN5IGluIHdoYXQgbWF5IGJlIGNvbnNpZGVyZWQgYW4gYWxwaGFudW1lcmljIGNoYXJhY3RlciBvciBwdW5jdHVhdGlvbi4KCgojIyBRdWFudGlmaWVyczogKiAuID8KCiMjIyMgUXVhbnRpZmllcnM6ICogKHplcm8gb3IgbW9yZSBvZiB0aGUgcHJldmlvdXMpCgpUaGlzIGlzIGFsc28ga25vd24gYXMgdGhlICJLbGVlbmUgc3RhciIgKHByb25vdW5jZWQgY2xlYW4tZWUpLCBhZnRlciBpdHMgb3JpZ2luYWwgdXNlciAoS2xlZW5lKSB3aG8gaW50cm9kdWNlZCB0aGUgbm90YXRpb24gaW4gZm9ybWFsIGxvZ2ljLgoKYGBge3J9CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIlxcZCoiKSAjICAKYGBgCgojIyMjIFF1YW50aWZpZXJzOiArIChvbmUgb3IgbW9yZSBvZiB0aGUgcHJldmlvdXMpCgpUaGlzIGlzIGFsc28ga25vd24gYXMgdGhlICJLbGVlbmUgcGx1cy4iCmBgYHtyfQpzdHJfZXh0cmFjdF9hbGwoY29tYmluZWRfc3RyaW5nLCJcXGQrIikgIyAgCmBgYAoKIyMjIyBRdWFudGlmaWVyczoge30KCntufSA9ICJleGFjdGx5IG4iIG9mIHRoZSBwcmV2aW91cwp7bixtfSA9ICJiZXR3ZWVuIG4gYW5kIG0iIG9mIHRoZSBwcmV2aW91cwp7bix9ID0gIm4gb3IgbW9yZSIgb2YgdGhlIHByZXZpb3VzCgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKCJ4IHh4IHh4eCB4eHh4IHh4eHh4IiwieHszfSIpICMKc3RyX2V4dHJhY3RfYWxsKCJ4IHh4IHh4eCB4eHh4IHh4eHh4IiwieHszLDR9IikgIyAgCnN0cl9leHRyYWN0X2FsbCgieCB4eCB4eHggeHh4eCB4eHh4eCIsInh7Myx9IikgIyAgCmBgYApXZXJlIGFsbCBvZiB0aG9zZSB3aGF0IHlvdSBleHBlY3RlZD8gVXNlIGBzdHJfdmlld19hbGxgIHRvIHNlZSB3aGF0J3MgaGFwcGVuaW5nLgpgYGB7cn0Kc3RyX3ZpZXdfYWxsKCJ4IHh4IHh4eCB4eHh4IHh4eHh4IiwieHszfSIpICMgIApgYGAKCiMjIyMgUXVlc3Rpb24gTWFyayBhcyBRdWFudGlmaWVyICh6ZXJvIG9yIG9uZSBvZiB0aGUgcHJldmlvdXMpCgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFxkPyIpICMgMCBvciAxIGRpZ2l0CnN0cl9zdWJzZXQoc2VudGVuY2VzLCIgW2JwXT9lYXQiKQpgYGAKCiMjIEdyZWVkeSB2cy4gbm9uLWdyZWVkeSBtYXRjaGluZwoKIyMjIyBRdWVzdGlvbiBNYXJrIGFzIE5vbmdyZWVkeSBNb2RpZmllciB0byBRdWFudGlmaWVyIC0gc21hbGxlc3QgbWF0Y2ggb2YgcHJldmlvdXMgcG9zc2libGUKCgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKCIoRmlyc3QgYnJhY2tldGVkIHN0YXRlbWVudCkgT3RoZXIgdGV4dCAoU2Vjb25kIGJyYWNrZXRlZCBzdGF0ZW1lbnQpIiwiXFwoLitcXCkiKSAjIGdyZWVkeSAtIGNhcHR1cmVzIGZyb20gZmlyc3QgKCB0byBsYXN0ICkKc3RyX2V4dHJhY3RfYWxsKCIoRmlyc3QgYnJhY2tldGVkIHN0YXRlbWVudCkgT3RoZXIgdGV4dCAoU2Vjb25kIGJyYWNrZXRlZCBzdGF0ZW1lbnQpIiwiXFwoLis/XFwpIikgIyBub25ncmVlZHkgLSBmaW5kcyB0d28gc21hbGxlciBtYXRjaGVzCnN0cl9leHRyYWN0X2FsbCgieCB4eCB4eHggeHh4eCB4eHh4eCIsInguK3giKSAjIGRlZmF1bHRzIHRvIGdyZWVkeSAtIGxhcmdlc3QgbWF0Y2gKc3RyX2V4dHJhY3RfYWxsKCJ4IHh4IHh4eCB4eHh4IHh4eHh4IiwieC4rP3giKSAjIG5vbmdyZWVkeSAtIG5vdCB3aGF0IHlvdSBleHBlY3QgLSB3aHk/CmBgYAoKIyMgQW5jaG9ycyBhbmQgd29yZCBib3VuZGFyaWVzOiBeICQgXFxiCgojIyMjIEFuY2hvcnM6IF4gKGJlZ2lubmluZyBvZiBzdHJpbmcpLCAkIChlbmQgb2Ygc3RyaW5nKQoKYGBge3J9CnN0cl9leHRyYWN0X2FsbChjb21iaW5lZF9zdHJpbmcsIlxcdysiKSAjIHNlcXVlbmNlcyBvZiBhbHBoYW51bWVyaWMgY2hhcmFjdGVycwpzdHJfZXh0cmFjdF9hbGwoY29tYmluZWRfc3RyaW5nLCJeXFx3KyIpICMgc2VxdWVuY2VzIGF0IGJlZ2lubmluZyBvZiBzdHJpbmcKc3RyX2V4dHJhY3RfYWxsKGNvbWJpbmVkX3N0cmluZywiXFx3KyQiKSAjIHNlcXVlbmNlcyBhdCBlbmQgb2Ygc3RyaW5nICMgbm9uZSAtIGl0IGVuZHMgaW4gcHVuY3R1YXRpb24KYGBgCgojIyMjIFdvcmQgYm91bmRhcmllczogYFxiYAoKU2ltaWxhcmx5LCB3ZSBjYW4gaWRlbnRpZnkgIndvcmQgYm91bmRhcmllcy4nJyBUaGlzIHNvbHZlcyB0aGUgZ3JlZWR5L25vbmdyZWVkeSBwcm9ibGVtIHdlIGhhZCB3aXRoIHRoZSAieCIgc2VxdWVuY2VzIGFib3ZlLiBJdCBzdGlsbCB0aGlua3MgdGhlIGRlY2ltYWwgcG9pbnQgaW4gYDEwLjJgIGlzIGEgd29yZCBib3VuZGFyeSwgdGhvdWdoLgoKYGBge3J9CnN0cl9leHRyYWN0X2FsbCgieCB4eCB4eHggeHh4eCB4eHh4eCIsIlxcYnguKj9cXGIiKSAjIApzdHJfZXh0cmFjdF9hbGwoY29tYmluZWRfc3RyaW5nLCJcXGJcXHcrP1xcYiIpICMgCmBgYAoKIyMgQ2FwdHVyZSBncm91cHMKCldlJ3ZlIHNlZW4gcGFyZW50aGVzZXMgdXNlZCB3aXRoIHRoZSBwaXBlIG9wZXJhdG9yLiBUaGV5IGFyZSBhbHNvIHVzZWQgdG8gaW5kaWNhdGUgc21hbGxlciBwYXJ0cyBvZiB0aGUgcGF0dGVybiB0aGF0IHdlIHdhbnQgdG8gImNhcHR1cmUuIiBgc3RyX21hdGNoYCB3aWxsIGdpdmUgdXMgYSBtYXRyaXggaW4gd2hpY2ggdGhlIGZpcnN0IGNvbHVtbiBpcyB0aGUgbWF0Y2ggdG8gdGhlICplbnRpcmUqIHBhdHRlcm4sIHdoYXQgd2UndmUgc2VlbiBiZWZvcmUuIEVhY2ggc3Vic2VxdWVudCBjb2x1bW4gaG9sZHMgdGhlIHBhcnQgb2YgdGhlIG1hdGNoIGluIGVhY2ggcGFpciBvZiBwYXJlbnRoZXNlcy4KCmBgYHtyfQpzdHJfbWF0Y2goZnJ1aXRbMToxNV0sIl4oLis/KShiZXJyeXxmcnVpdCkkIikKYGBgCgpXZSBhbHNvIGNhbiB1c2UgYFxcMWAsIGBcXDJgLCBldGMuIHRvIHJlZmVyIHRvIHRoZXNlIGNhcHR1cmUgZ3JvdXBzICpsYXRlciBpbiB0aGUgc2FtZSBjb21tYW5kKi4KCkhlcmUncyBhbiBhY3R1YWwgcmVndWxhciBleHByZXNzaW9uIEkgdXNlIGluIGNsZWFuaW5nIHRoZSBNb29kIG9mIHRoZSBOYXRpb24gcG9sbCBhbnN3ZXJzLiBJIGxhdGVyIHdpbGwgbGV0IHB1bmN0dWF0aW9uIGxpa2UgYCdgIGluZGljYXRlIGEgd29yZCBib3VuZGFyeSwgc28gZmlyc3QsIEkgd2FudCB0byBjb2xsYXBzZSBjb250cmFjdGlvbnMgYWNyb3NzIHRoZSBgJ2AgdG8ga2VlcCB0aGVtIHRvZ2V0aGVyLiBUaGlzLCBmb3IgZXhhbXBsZSBjb2xsYXBzZXMgYW55IGBuJ3RgIGNvbnRyYWN0aW9ucy4KCmBgYHtyfQptb3RuIDwtICJpIGNhbid0IHN0YW5kIGRvbid0cnVtcCBzdXBwb3J0ZXJzIHNob3V0aW5nICdidWlsZCB0aGF0IHdhbGwnISIKbmV3bW90biA8LSBzdHJfcmVwbGFjZV9hbGwobW90biwiKG4ndCkoJHxbWzpwdW5jdDpdXXxcXHMpIiwibnRcXDIiKSAjZG9udCwgY2FudCwgd29udCwgd2FzbnQsIHdlcmVudCwgZGlkbnQsIGNvdWxkbnQsIHdvdWxkbnQsIHNob3VsZG50LCBoYXZlbnQKbmV3bW90bgpgYGAKVGhhdCBsb29rcyBmb3IgdGhlICgxKSBgbid0YCBwYXR0ZXJuIGZvbGxvd2VkIGJ5IHRoZSAoMikgZW5kIG9mIHRoZSBzdHJpbmcsIGFub3RoZXIgcHVuY3R1YXRpb24gbWFyaywgb3IgYSB3aGl0ZXNwYWNlLiBJdCB0aGVuIHJlcGxhY2VzIHRoYXQgd2l0aCBgbnRgIGZvbGxvd2VkIGJ5IHdoYXRldmVyIHRoZSBmb2xsb3dpbmcgY2hhcmFjdGVyIHdhcy4gVGhpcyBhdm9pZHMgcmVwbGFjaW5nIG90aGVyIGFjY2lkZW50YWwgaW5zdGFuY2VzIG9mIHRoZSBgbid0YCBwYXR0ZXJuIHRoYXQgYXJlbid0IGNsZWFybHkgY29udHJhY3Rpb25zLgoKIyBVc2luZyByZWd1bGFyIGV4cHJlc3Npb25zIHRvIGV4dHJhY3QgZGF0YSBmcm9tIHRleHQ6IGFuIGV4YW1wbGUKCkxldCdzIHN0YXJ0IHdpdGggc29tZSBleGFtcGxlIHRleHQ6CmBgYHtyfQp0ZXh0IDwtICJTRUMuIDEwMS4gRklTQ0FMIFlFQVIgMjAxNy4KKGEpIEluIEdlbmVyYWwuLS1UaGVyZSBhcmUgYXV0aG9yaXplZCB0byBiZSBhcHByb3ByaWF0ZWQgdG8gTkFTQQpmb3IgZmlzY2FsIHllYXIgMjAxNyAkMTksNTA4LDAwMCwwMDAsIGFzIGZvbGxvd3M6CigxKSBGb3IgRXhwbG9yYXRpb24sICQ0LDMzMCwwMDAsMDAwLgooMikgRm9yIFNwYWNlIE9wZXJhdGlvbnMsICQ1LDAyMywwMDAsMDAwLgooMykgRm9yIFNjaWVuY2UsICQ1LDUwMCwwMDAsMDAwLgooNCkgRm9yIEFlcm9uYXV0aWNzLCAkNjQwLDAwMCwwMDAuCig1KSBGb3IgU3BhY2UgVGVjaG5vbG9neSwgJDY4NiwwMDAsMDAwLgooNikgRm9yIEVkdWNhdGlvbiwgJDExNSwwMDAsMDAwLgooNykgRm9yIFNhZmV0eSwgU2VjdXJpdHksIGFuZCBNaXNzaW9uIFNlcnZpY2VzLAokMiw3ODgsNjAwLDAwMC4KKDgpIEZvciBDb25zdHJ1Y3Rpb24gYW5kIEVudmlyb25tZW50YWwgQ29tcGxpYW5jZSBhbmQKUmVzdG9yYXRpb24sICQzODgsMDAwLDAwMC4KKDkpIEZvciBJbnNwZWN0b3IgR2VuZXJhbCwgJDM3LDQwMCwwMDAuCihiKSBFeGNlcHRpb24uLS1JbiBhZGRpdGlvbiB0byB0aGUgYW1vdW50cyBhdXRob3JpemVkIHRvIGJlCmFwcHJvcHJpYXRlZCBmb3IgZWFjaCBhY2NvdW50IHVuZGVyIHN1YnNlY3Rpb24gKGEpLCB0aGVyZSBhcmUKYXV0aG9yaXplZCB0byBiZSBhcHByb3ByaWF0ZWQgYWRkaXRpb25hbCBmdW5kcyBmb3IgZWFjaCBzdWNoIGFjY291bnQsCmJ1dCBvbmx5IGlmIHRoZSBhdXRob3JpemVkIGFtb3VudHMgZm9yIGFsbCBzdWNoIGFjY291bnRzIGFyZSBmdWxseQpwcm92aWRlZCBmb3IgaW4gYW5udWFsIGFwcHJvcHJpYXRpb24gQWN0cywgY29uc2lzdGVudCB3aXRoIHRoZQpkaXNjcmV0aW9uYXJ5IHNwZW5kaW5nIGxpbWl0cyBpbiBzZWN0aW9uIDI1MShjKSBvZiB0aGUgQmFsYW5jZWQgQnVkZ2V0CmFuZCBFbWVyZ2VuY3kgRGVmaWNpdCBDb250cm9sIEFjdCBvZiAxOTg1LiIKYGBgCgpXYWl0IC4uLiB0aGF0J3MganVzdCBvbmUgdmFyaWFibGUgaG9sZGluZyBvbmUgc3RyaW5nPyBZZXAuCmBgYHtyfQp0ZXh0CmBgYAoKQWxsIHRob3NlIGBcbmBzIHRoZXJlIGluZGljYXRlIG5ldyBsaW5lcy4KCldlJ3JlIGdvaW5nIHRvIHRyeSB0byB1c2UgcmVndWxhciBleHByZXNzaW9ucyB0byBtYWtlIGRhdGEgb3V0IG9mIHRoZSBhcHByb3ByaWF0aW9ucyBkb2xsYXJzIGFuZCBwdXJwb3NlcyBpbiBidWxsZXRzIDEtOS4KCkxldHMgcGxheSBhcm91bmQgd2l0aCBhIGZldyB0aGluZ3MuIEV4dHJhY3QgYWxsIGNvbnRpZ3VvdXMgc2VxdWVuY2VzIG9mIG9uZSBvciBtb3JlIG51bWJlcnMuCgpgYGB7cn0Kc3RyaW5ncjo6c3RyX2V4dHJhY3RfYWxsKHRleHQsIlswLTldKyIpW1sxXV0KYGBgCgpUaGF0IGRvZXMgdHdvIHRoaW5ncyB3ZSBkb24ndCBsaWtlIC4uLiBzZXBhcmF0ZXMgbnVtYmVycyBhdCB0aGUgMTAwMHMgc2VwYXJhdGluZyBjb21tYSBhbmQgZ2V0cyBudW1iZXJzICgiMTAxIiwgIjIwMTciLCBldGMuKSB0aGF0IGFyZW4ndCBkb2xsYXIgYW1vdW50cy4gU28sIGxldCdzIHRyeSBnZXR0aW5nIGV2ZXJ5dGhpbmcgdGhhdAogIFN0YXJ0cyB3aXRoIGEgIiQiICh3aGljaCBuZWVkcyB0byBiZSBlc2NhcGVkKQogIEZvbGxvd2VkIGJ5IG9uZSBvciBtb3JlIHN0cmluZ3Mgb2YgY29tbWFzIG9yIGRpZ2l0cy4KCmBgYHtyfQpzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwodGV4dCwiXFwkWywwLTldKyIpW1sxXV0gIyBtdXN0IHN0YXJ0IHdpdGggJApgYGAKCkFsbW9zdCAuLi4gZG9uJ3QgbGlrZSB0aGF0IGV4dHJhIGNvbW1hIG9uIHRoZSBmaXJzdCBudW1iZXIuIEFkZAogIGFuZCBlbmRzIHdpdGggYSBudW1iZXIuCmBgYHtyfQpzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwodGV4dCwiXFwkWywwLTldK1swLTldIilbWzFdXQpgYGAKCldlIGNvdWxkIHVzZSBxdWFudGlmaWVycyB0byBnZXQgbnVtYmVycyBvZiAkMSBiaWxsaW9uIG9yIG1vcmUKYGBge3J9CnN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbCh0ZXh0LCJcXCRbLDAtOV17MTIsfVswLTldIilbWzFdXQpgYGAKClRoYXQgYXNrcyBmb3IKICAgIFN0YXJ0cyB3aXRoIGEgIiQiCiAgICBGb2xsb3dlZCBieSAxMiBPUiBNT1JFIGNvbW1hcyBhbmQgbnVtYmVycwogICAgQW5kIGVuZHMgd2l0aCBhIG51bWJlcgoKCk5vdyBsZXQncyB0cnkgdG8gZ2V0IHRoZSBidWxsZXQgbnVtYmVycyBlbmNsb3NlZCBpbiBwYXJlbnRoZXNlczoKYGBge3J9CnN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbCh0ZXh0LCJcXChbMC05XVxcKSIpW1sxXV0KYGBgCgoKU2F5IHdlIG9ubHkgd2FudCB0byBtYXRjaCBsaW5lcyB0aGF0IHN0YXJ0IHdpdGggYSBwYXJ0aWN1bGFyIHNldCBvZiBjaGFyYWN0ZXJzIC4uLgpGaXJzdCBsZXQncyBzcGxpdCBpdCBpbnRvIGxpbmVzOgpgYGB7cn0KdGV4dF9zcGxpdCA8LSBzdHJpbmdyOjpzdHJfc3BsaXQodGV4dCwiXFxuIilbWzFdXQp0ZXh0X3NwbGl0CmBgYAoKTm93IG1hdGNoIG9uIGJlZ2dpbmluZyBzdHJpbmcgYW5jaG9yIGFuZCBvcGVuIHBhcmVuLgoKYGBge3J9CnN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbCh0ZXh0X3NwbGl0LCJeXFwoLioiKQpgYGAKClRoYXQgcmV0dXJuZWQgYSBsaXN0LCBhbmQgd2UnZCBwcm9iYWJseSByYXRoZXIgaGF2ZSBhIHZlY3Rvci4gSW4gdGhpcyBjYXNlLCB3ZSBjYW4ganVzdCB3cmFwIHRoaXMgaW4gYW4gdW5saXN0KCkgc3RhdGVtZW50OgoKYGBge3J9CnVubGlzdChzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwodGV4dF9zcGxpdCwiXlxcKC4qIikpCmBgYAoKU28sIG5vdyBsZXQncyB0cnkgdG8gcHV0IGV2ZXJ5dGhpbmcgd2UndmUgbGVhcm5lZCB0b2dldGhlciBhbmQgbWFrZSBhIGxpdHRsZSBkYXRhc2V0IG91dCBvZiB0aGUgaXRlbXMgKDEpIHRvICg5KSB3aXRoIGRvbGxhciBhbW91bnRzIGFuZCB3aGF0IHRoZXkncmUgZm9yCgpMZXQncyBzZWUgd2hhdCB3ZSBoYXZlIGluIHRoYXQgbGFzdCBjb21tYW5kIC4uCiAgICBXZSBoYXZlIHNvbWUgZXh0cmEgbGluZXMgLi4uICIoYSkiIGFuZCAiKGIpIgogICAgQW5kIHdlJ3JlIG1pc3NpbmcgdGhlICQgbnVtYmVycyBmcm9tIGl0ZW1zICg3KSBhbmQgKDgpCiAgIHdoaWNoIGFyZSBvbiB0aGUgbmV4dCBsaW5lcy4KCkxldCdzIGdvIGJhY2sgdG8gdGhlIG9yaWdpbmFsIGFuZCBnZXQgcmlkIG9mIHRoZSBuZXdsaW5lczoKYGBge3J9Cm9uZV9saW5lIDwtIHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbCh0ZXh0LCJcXG4iLCIgIilbWzFdXQpvbmVfbGluZQpgYGAKCmFuZCBmaW5kIGFsbCB0aGUgbWF0Y2hlcyBmcm9tICIobnVtYmVyKSIgdG8gYSBwZXJpb2QsIGxhemlseSByYXRoZXIgdGhhbiBncmVlZGlseQoKYGBge3J9Cml0ZW1fc3RyaW5ncyA8LSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwob25lX2xpbmUsIlxcKFxcZFxcKS4rP1xcLiIpW1sxXV0KaXRlbV9zdHJpbmdzCmBgYAoKQ2FuIHVzZSBzdHJfbWF0Y2ggYW5kIHBhcmVudGhlc2VzIHRvIGlkZW50aWZ5IHRoZSBzdHVmZiB5b3Ugd2FudApgYGB7cn0KZm9yX3N0cmluZ3MgPC0gc3RyaW5ncjo6c3RyX21hdGNoKGl0ZW1fc3RyaW5ncywiRm9yICguKyksIFxcJCIpCmZvcl9zdHJpbmdzCmBgYAoKVGhlIHNlY29uZCBjb2x1bW4gY29udGFpbnMgb3VyIGxpc3Qgb2YgdGhlICJmb3Igd2hhdCJzLgpgYGB7cn0KZm9yX3N0cmluZ3MgPC0gZm9yX3N0cmluZ3NbLDJdCmZvcl9zdHJpbmdzCmBgYAoKRG8gc29tZXRoaW5nIHNpbWlsYXIgZm9yIG1vbmV5CmBgYHtyfQptb25leV9zdHJpbmdzIDwtIHN0cmluZ3I6OnN0cl9tYXRjaChpdGVtX3N0cmluZ3MsIlxcJChbLFxcZF0rKSIpWywyXQptb25leV9zdHJpbmdzCmBgYAoKR2V0IHJpZCBvZiB0aGUgcHVuY3R1YXRpb24KYGBge3J9Cm1vbmV5X3N0cmluZ3MgPC0gc3RyaW5ncjo6c3RyX3JlcGxhY2VfYWxsKG1vbmV5X3N0cmluZ3MsIltcXCQsXSIsIiIpCm1vbmV5X3N0cmluZ3MKYGBgCgpUdXJuIHRoZW0gaW50byBudW1lcmljIGRhdGEgcmF0aGVyIHRoYW4gc3RyaW5ncy4KYGBge3J9Cm1vbmV5IDwtIGFzLm51bWVyaWMobW9uZXlfc3RyaW5ncykKbW9uZXkKYGBgCgpOb3cgbGV0J3MgbWFrZSBpdCBkYXRhOgpgYGB7cn0KYXBwcm9wcmlhdGlvbnNfZGF0YSA8LSBkYXRhLmZyYW1lKHB1cnBvc2UgPSBmb3Jfc3RyaW5ncyxhbW91bnQgPSBtb25leSkKYXBwcm9wcmlhdGlvbnNfZGF0YQpgYGAKCgoKIyMjIyBPdGhlciBsYW5ndWFnZXMKClJlbWVtYmVyIC4uLiBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZXMgaGFuZGxlIHJlZ3VsYXIgZXhwcmVzc2lvbnMgc2xpZ2h0bHkgZGlmZmVyZW50bHkuIEluIHBhcnRpY2xhciwgUHl0aG9uIGRvZXMgbm90IHVzZSB0aGUgImRvdWJsZSBlc2NhcGUiIGlkaW9tLgoKCg==