Introduction
Regular expressions (often abbreviated as “regex” or “regexp”) are sequences of characters that form a search pattern, primarily used for pattern matching within strings. They provide a flexible and concise means to identify strings of text, such as particular characters, words, or patterns. Regular expressions are commonly used in text searching, text replacement, and data validation.
They are important for many text (string) processing functions such as regexpr()
in R as well as Unix search programs such as grep
and fgrep
.
Need for Regular Expressions
Programmers, software engineers, data engineers, and information scientists in particular benefit from understanding regular expressions for several reasons:
Text Processing: Regular expressions offer a powerful tool for extracting, modifying, and manipulating text in data files, configuration files, logs, codebases, and more.
Data Validation: They can be used to check if input data conforms to expected formats, such as email addresses, phone numbers, or custom formats specific to an application.
Web Scraping: When extracting information from websites, regular expressions can be used to parse relevant data from HTML or other textual formats.
Search & Replace: Regular expressions make it possible to perform complex search and replace operations in text editors and IDEs, which is especially handy during refactoring or codebase-wide updates.
Optimized Search: Instead of writing lengthy code to search for specific patterns in a string, a short regular expression can often achieve the same result, making the code cleaner and more efficient.
Flexibility: Regular expressions are versatile. Once you understand their syntax, you can use them across various programming languages and tools, as the fundamental concepts remain consistent.
Data Parsing: They can be used to split strings into components or capture specific parts from strings, like extracting values from structured texts such as CSV, JSON, or XML.
Enhanced User Experience: On the frontend, regex can be used for real-time data validation as users input data, guiding users to correct errors instantly.
Log Analysis: For system administrators or developers diagnosing issues, regular expressions can help sift through logs to identify errors, warnings, or specific events.
Cybersecurity: Regular expressions play a role in intrusion detection systems, where specific patterns in network traffic or logs might indicate malicious activity.
Understanding regular expressions can save programmers time, streamline their tasks, and enhance the functionality and efficiency of the software they develop. While there’s a learning curve to mastering regular expressions, the benefits for programmers make the investment worthwhile.
Regular Expressions in R
There are three common functions in R that accept regular expressions:
grep()
grepl()
regexpr()
Let’s look at each one of them in more detail and how to use them.
The grep
and grepl
functions find pattern in strings and return a Boolean if there the pattern is found. The patterns are expressed as regular expressions.
x <- c("door", "apple", "color", "abba", "pattern")
# find which strings contain the pattern
grep("a", x)
## [1] 2 4 5
## [1] FALSE TRUE FALSE TRUE TRUE
The regexpr
returns the position in the regular expression string where there is first match occurs or a -1 if there is no match.
# find where the pattern starts
m <- regexpr("a", x)
m
## [1] -1 1 -1 1 2
## attr(,"match.length")
## [1] -1 1 -1 1 1
## attr(,"index.type")
## [1] "chars"
## attr(,"useBytes")
## [1] TRUE
## [1] 2
In the example below we are looking for an occurrence of * which is a regular expression character, so we need to “escape its meaning” with a \. However, the \ is also a special character for regular expression so we need to also escape its meaning with a \\, hence the strange regular expression \\*.
# let's see if there is an * in a string
s <- "US AIRWAYS*"
p <- regexpr("\\*", s)
if (p > 0)
{
print(paste("* at position:", p,
"in string",s))
} else {
print("no asterisk found")
}
## [1] "* at position: 11 in string US AIRWAYS*"
To find, for example, the first space in a string, you need to use regexpr
and then process the return value which is a list.
x <- "Mazda Miata L"
# find where the pattern starts
m <- regexpr(" ", x)
post.first.space <- m[1]
The variable pos.first.space
now contains the position of the first space.
The function gregexpr()
returns all occurrences of a character and not just the first one. Inspecting the return list m
reveals that it returns a list and that the first element in the list is a vector of all occurences of the character:
x <- "Mazda Miata L"
# find where the pattern starts
m <- gregexpr(" ", x)
print(m)
## [[1]]
## [1] 6 12
## attr(,"match.length")
## [1] 1 1
## attr(,"index.type")
## [1] "chars"
## attr(,"useBytes")
## [1] TRUE
# all occurrences of " "
print(unlist(m))
## [1] 6 12
Regular Expression Syntax
Several string processing functions take regular expressions as input. The table below summarizes the most common regular expression syntax constructs.
^ |
Beginning of the string |
$ |
End of the string |
[ab] |
a or b |
[^ab] |
Any character except a and b |
[0-9] |
Any digit |
[A-Z] |
Any uppercase letters from A to Z |
[a-z] |
Any uppercase letters from a to a |
[A-z] |
Any letter |
i+ |
i at least one time |
i* |
i zero or more times |
i? |
i zero or 1 time |
i{n} |
i occurs n times in sequence |
[:alnum:] |
Alphanumeric characters: [:alpha:] and [:digit:] |
[:alpha:] |
Alphabetic characters: [:lower:] and [:upper:] |
[:blank:] |
Blank characters, e.g., space, tab |
[:digit:] |
Digits: 0 1 2 3 4 5 6 7 8 9 |
[:punct:] |
Punctuation character: ! ” # $ % & ’ ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { |
Examples
# remove the $ from a vector of "numbers" and convert to numbers then calculate mean
v <- c("$99.12","13387.3","$0.998")
v <- as.numeric(substring(v, 2))
mean(v)
## [1] 1162.473
# remove all * characters from a vector of strings
v <- c("US AIRWAYS*","CONTINENTAL*","LUFTHANSA*","UNITED*","WOW*")
v <- substring(v, 1, nchar(v)-1)
v
## [1] "US AIRWAYS" "CONTINENTAL" "LUFTHANSA" "UNITED" "WOW"
# remove * characters from a vector of strings if there is one
v <- c("US AIRWAYS*","CONTINENTAL*","LUFTHANSA","UNITED","WOW*")
# the function below finds the starting positions of all *
# if there is no *, it returns -1
p <- regexpr("\\*", v)
# the function below returns the indexes of the strings in the vector v
# which actually contain a 0
g <- grep("\\*",v)
# only remove the * from those strings in the vector v that have it
v[g] <- substring(v[g],1,p[g]-1)
# our new vector with * removed
print(v)
## [1] "US AIRWAYS" "CONTINENTAL" "LUFTHANSA" "UNITED" "WOW"
# remove all commas for a number string as coercion with as.numeric(s)
# fail if the string contains commas as thousands separator
s <- "100,340,998"
numParts <- unlist(strsplit(s, ","))
num <- paste(numParts, collapse = "")
n <- as.numeric(num)
print(n)
## [1] 100340998
grep and fgrep
While the use of regular expressions for string processing in programming is common, they are also available for text searching from the command line in Unix. Let’s look at two text searching utilities for Unix: grep
and fgrep
. Both commands are used for searching text, but they interpret patterns differently. Here’s a breakdown of their differences:
Pattern Interpretation:
grep
: Stands for “global regular expression print.” It searches for a pattern using regular expressions (regex) by default. So, if you provide a pattern with special regex characters, such as .
(dot) or *
, grep
will interpret them as regex metacharacters. For instance, .
will match any character, and *
will match the preceding character zero or more times.
In the above command, the pattern “a.b” will not match “ab” because the dot expects any character to be present between “a” and “b”.
fgrep
: Stands for “fixed grep” or “fast grep.” It treats the pattern as a fixed string. This means it does not recognize regular expression metacharacters as special; they are treated as regular characters. Thus, fgrep
is useful when you want to search for a string that might contain regex metacharacters and you don’t want them to be interpreted as such.
Here, the pattern “a.b” matches the string “a.b” because fgrep
doesn’t interpret the dot as a regex character.
Speed:
- Due to its literal interpretation of patterns,
fgrep
can be faster than grep
when searching for fixed strings because it doesn’t need to process regex metacharacters.
Alternatives:
Multiple Patterns:
One advantage of fgrep
(or grep -F
) is the ability to search for multiple fixed strings efficiently by placing each string on a new line in a pattern file.
fgrep -f pattern_file.txt target_file.txt
In the above command, each line in pattern_file.txt
is treated as a fixed string, and fgrep
searches for any of these lines in target_file.txt
.
In summary, use grep
when you need the power and flexibility of regular expressions. Opt for fgrep
(or grep -F
) when you want to search for exact, fixed strings, especially when these strings might contain characters that have special meanings in regex.
Detailed Regular Expressions
Classes and Literals
- Literals:
- The simplest form of pattern matching supported by regular expressions is the match of a string literal.
regex hello
- Dot (
.
):
- The
.
character matches any single character except a newline. regex h.llo
- Character Classes (
[]
):
- A set of characters enclosed in square brackets (
[]
) makes up a character class. This matches any single character in the set. regex [aeiou] # matches any single vowel
- Negate Character Classes (
[^ ]
):
- By placing a
^
symbol inside the square brackets, only characters not in the character class will be matched. regex [^aeiou] # matches any single non-vowel character
- Predefined Character Classes:
\d
: Matches any digit. Equivalent to [0-9]
.
\D
: Matches any non-digit. Equivalent to [^0-9]
.
\w
: Matches any word character (alphanumeric and underscore). Equivalent to [a-zA-Z0-9_]
.
\W
: Matches any non-word character.
\s
: Matches any whitespace character (spaces, tabs, line breaks).
\S
: Matches any non-whitespace character.
- Quantifiers:
*
: Matches 0 or more of the preceding element. regex ab*c # matches "ac", "abc", "abbc", etc.
+
: Matches 1 or more of the preceding element. regex ab+c # matches "abc", "abbc", but not "ac"
?
: Matches 0 or 1 of the preceding element. regex ab?c # matches "ac" or "abc"
{n}
: Matches exactly n
occurrences of the preceding element.
{n,}
: Matches n
or more occurrences.
{n,m}
: Matches between n
and m
occurrences. regex a{2,4} # matches "aa", "aaa", or "aaaa"
- Anchors:
^
: Matches the start of a line.
$
: Matches the end of a line. regex ^Hello # matches "Hello" at the beginning of a line world$ # matches "world" at the end of a line
- Alternation (
|
):
- Acts like an OR operator.
regex cat|dog # matches "cat" or "dog"
- Grouping (
()
):
- Elements within parentheses are treated as a single unit.
- Also captures the matched text for use with backreferences.
regex (abc)+ # matches "abc", "abcabc", etc.
- Backreferences:
- Refers to the text matched by a previous capture group.
\1
refers to the text of the first capture group, \2
for the second, and so on. regex (a)b\1 # matches "aba"
- Escape Sequences:
- To match characters that have special meanings in regex, you escape them with a backslash
\
. regex \. # matches a literal dot \\ # matches a backslash \[ # matches a square bracket
Advanced Concepts:
- Lookaheads and Lookbehinds:
Lookaheads allow you to match a group ahead of your main pattern without including it in the result. regex q(?=u) # matches the "q" in "queue" but not the "u"
Lookbehinds are similar, but they look behind. regex (?<=a)b # matches the "b" after an "a"
- Non-capturing Groups:
- Use
(?:...)
to create a non-capturing group. regex (?:abc)+ # matches "abc", "abcabc", etc., but does not capture the match
- Modifiers:
- Some regex engines allow modifiers to change how the regex is interpreted.
regex (?i)abc # matches "abc", "ABC", "AbC", etc. (case-insensitive)
Regular expressions are powerful but can be complex. Mastering them requires practice. Test your regular expressions using tools like regex101 to ensure they match what you intend and to understand their behavior better.
Resources
- regex101 for testing regular expressions
LS0tCnRpdGxlOiAiUmVndWxhciBFeHByZXNzaW9ucyIKcGFyYW1zOgogIGNhdGVnb3J5OiAxCiAgbnVtYmVyOiA5NTUKICB0aW1lOiAiNjAgbWluIgogIGxldmVsOiBiZWdpbm5lcgogIHRhZ3M6ICJyZWd1bGFyIGV4cHJlc3Npb24scmVnZXgsZ3JlcCIKICBkZXNjcmlwdGlvbjogIkludHJvZHVjZXMgcmVndWxhciBleHByZXNzaW9ucy4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKUmVndWxhciBleHByZXNzaW9ucyAob2Z0ZW4gYWJicmV2aWF0ZWQgYXMgInJlZ2V4IiBvciAicmVnZXhwIikgYXJlIHNlcXVlbmNlcyBvZiBjaGFyYWN0ZXJzIHRoYXQgZm9ybSBhIHNlYXJjaCBwYXR0ZXJuLCBwcmltYXJpbHkgdXNlZCBmb3IgcGF0dGVybiBtYXRjaGluZyB3aXRoaW4gc3RyaW5ncy4gVGhleSBwcm92aWRlIGEgZmxleGlibGUgYW5kIGNvbmNpc2UgbWVhbnMgdG8gaWRlbnRpZnkgc3RyaW5ncyBvZiB0ZXh0LCBzdWNoIGFzIHBhcnRpY3VsYXIgY2hhcmFjdGVycywgd29yZHMsIG9yIHBhdHRlcm5zLiBSZWd1bGFyIGV4cHJlc3Npb25zIGFyZSBjb21tb25seSB1c2VkIGluIHRleHQgc2VhcmNoaW5nLCB0ZXh0IHJlcGxhY2VtZW50LCBhbmQgZGF0YSB2YWxpZGF0aW9uLgoKVGhleSBhcmUgaW1wb3J0YW50IGZvciBtYW55IHRleHQgKHN0cmluZykgcHJvY2Vzc2luZyBmdW5jdGlvbnMgc3VjaCBhcyBgcmVnZXhwcigpYCBpbiBSIGFzIHdlbGwgYXMgVW5peCBzZWFyY2ggcHJvZ3JhbXMgc3VjaCBhcyBgZ3JlcGAgYW5kIGBmZ3JlcGAuCgojIyBOZWVkIGZvciBSZWd1bGFyIEV4cHJlc3Npb25zCgpQcm9ncmFtbWVycywgc29mdHdhcmUgZW5naW5lZXJzLCBkYXRhIGVuZ2luZWVycywgYW5kIGluZm9ybWF0aW9uIHNjaWVudGlzdHMgaW4gcGFydGljdWxhciBiZW5lZml0IGZyb20gdW5kZXJzdGFuZGluZyByZWd1bGFyIGV4cHJlc3Npb25zIGZvciBzZXZlcmFsIHJlYXNvbnM6CgoxLiAgKipUZXh0IFByb2Nlc3NpbmcqKjogUmVndWxhciBleHByZXNzaW9ucyBvZmZlciBhIHBvd2VyZnVsIHRvb2wgZm9yIGV4dHJhY3RpbmcsIG1vZGlmeWluZywgYW5kIG1hbmlwdWxhdGluZyB0ZXh0IGluIGRhdGEgZmlsZXMsIGNvbmZpZ3VyYXRpb24gZmlsZXMsIGxvZ3MsIGNvZGViYXNlcywgYW5kIG1vcmUuCgoyLiAgKipEYXRhIFZhbGlkYXRpb24qKjogVGhleSBjYW4gYmUgdXNlZCB0byBjaGVjayBpZiBpbnB1dCBkYXRhIGNvbmZvcm1zIHRvIGV4cGVjdGVkIGZvcm1hdHMsIHN1Y2ggYXMgZW1haWwgYWRkcmVzc2VzLCBwaG9uZSBudW1iZXJzLCBvciBjdXN0b20gZm9ybWF0cyBzcGVjaWZpYyB0byBhbiBhcHBsaWNhdGlvbi4KCjMuICAqKldlYiBTY3JhcGluZyoqOiBXaGVuIGV4dHJhY3RpbmcgaW5mb3JtYXRpb24gZnJvbSB3ZWJzaXRlcywgcmVndWxhciBleHByZXNzaW9ucyBjYW4gYmUgdXNlZCB0byBwYXJzZSByZWxldmFudCBkYXRhIGZyb20gSFRNTCBvciBvdGhlciB0ZXh0dWFsIGZvcm1hdHMuCgo0LiAgKipTZWFyY2ggJiBSZXBsYWNlKio6IFJlZ3VsYXIgZXhwcmVzc2lvbnMgbWFrZSBpdCBwb3NzaWJsZSB0byBwZXJmb3JtIGNvbXBsZXggc2VhcmNoIGFuZCByZXBsYWNlIG9wZXJhdGlvbnMgaW4gdGV4dCBlZGl0b3JzIGFuZCBJREVzLCB3aGljaCBpcyBlc3BlY2lhbGx5IGhhbmR5IGR1cmluZyByZWZhY3RvcmluZyBvciBjb2RlYmFzZS13aWRlIHVwZGF0ZXMuCgo1LiAgKipPcHRpbWl6ZWQgU2VhcmNoKio6IEluc3RlYWQgb2Ygd3JpdGluZyBsZW5ndGh5IGNvZGUgdG8gc2VhcmNoIGZvciBzcGVjaWZpYyBwYXR0ZXJucyBpbiBhIHN0cmluZywgYSBzaG9ydCByZWd1bGFyIGV4cHJlc3Npb24gY2FuIG9mdGVuIGFjaGlldmUgdGhlIHNhbWUgcmVzdWx0LCBtYWtpbmcgdGhlIGNvZGUgY2xlYW5lciBhbmQgbW9yZSBlZmZpY2llbnQuCgo2LiAgKipGbGV4aWJpbGl0eSoqOiBSZWd1bGFyIGV4cHJlc3Npb25zIGFyZSB2ZXJzYXRpbGUuIE9uY2UgeW91IHVuZGVyc3RhbmQgdGhlaXIgc3ludGF4LCB5b3UgY2FuIHVzZSB0aGVtIGFjcm9zcyB2YXJpb3VzIHByb2dyYW1taW5nIGxhbmd1YWdlcyBhbmQgdG9vbHMsIGFzIHRoZSBmdW5kYW1lbnRhbCBjb25jZXB0cyByZW1haW4gY29uc2lzdGVudC4KCjcuICAqKkRhdGEgUGFyc2luZyoqOiBUaGV5IGNhbiBiZSB1c2VkIHRvIHNwbGl0IHN0cmluZ3MgaW50byBjb21wb25lbnRzIG9yIGNhcHR1cmUgc3BlY2lmaWMgcGFydHMgZnJvbSBzdHJpbmdzLCBsaWtlIGV4dHJhY3RpbmcgdmFsdWVzIGZyb20gc3RydWN0dXJlZCB0ZXh0cyBzdWNoIGFzIENTViwgSlNPTiwgb3IgWE1MLgoKOC4gICoqRW5oYW5jZWQgVXNlciBFeHBlcmllbmNlKio6IE9uIHRoZSBmcm9udGVuZCwgcmVnZXggY2FuIGJlIHVzZWQgZm9yIHJlYWwtdGltZSBkYXRhIHZhbGlkYXRpb24gYXMgdXNlcnMgaW5wdXQgZGF0YSwgZ3VpZGluZyB1c2VycyB0byBjb3JyZWN0IGVycm9ycyBpbnN0YW50bHkuCgo5LiAgKipMb2cgQW5hbHlzaXMqKjogRm9yIHN5c3RlbSBhZG1pbmlzdHJhdG9ycyBvciBkZXZlbG9wZXJzIGRpYWdub3NpbmcgaXNzdWVzLCByZWd1bGFyIGV4cHJlc3Npb25zIGNhbiBoZWxwIHNpZnQgdGhyb3VnaCBsb2dzIHRvIGlkZW50aWZ5IGVycm9ycywgd2FybmluZ3MsIG9yIHNwZWNpZmljIGV2ZW50cy4KCjEwLiAqKkN5YmVyc2VjdXJpdHkqKjogUmVndWxhciBleHByZXNzaW9ucyBwbGF5IGEgcm9sZSBpbiBpbnRydXNpb24gZGV0ZWN0aW9uIHN5c3RlbXMsIHdoZXJlIHNwZWNpZmljIHBhdHRlcm5zIGluIG5ldHdvcmsgdHJhZmZpYyBvciBsb2dzIG1pZ2h0IGluZGljYXRlIG1hbGljaW91cyBhY3Rpdml0eS4KClVuZGVyc3RhbmRpbmcgcmVndWxhciBleHByZXNzaW9ucyBjYW4gc2F2ZSBwcm9ncmFtbWVycyB0aW1lLCBzdHJlYW1saW5lIHRoZWlyIHRhc2tzLCBhbmQgZW5oYW5jZSB0aGUgZnVuY3Rpb25hbGl0eSBhbmQgZWZmaWNpZW5jeSBvZiB0aGUgc29mdHdhcmUgdGhleSBkZXZlbG9wLiBXaGlsZSB0aGVyZSdzIGEgbGVhcm5pbmcgY3VydmUgdG8gbWFzdGVyaW5nIHJlZ3VsYXIgZXhwcmVzc2lvbnMsIHRoZSBiZW5lZml0cyBmb3IgcHJvZ3JhbW1lcnMgbWFrZSB0aGUgaW52ZXN0bWVudCB3b3J0aHdoaWxlLgoKIyMgUmVndWxhciBFeHByZXNzaW9ucyBpbiBSCgpUaGVyZSBhcmUgdGhyZWUgY29tbW9uIGZ1bmN0aW9ucyBpbiBSIHRoYXQgYWNjZXB0IHJlZ3VsYXIgZXhwcmVzc2lvbnM6CgoxLiAgYGdyZXAoKWAKMi4gIGBncmVwbCgpYAozLiAgYHJlZ2V4cHIoKWAKCkxldCdzIGxvb2sgYXQgZWFjaCBvbmUgb2YgdGhlbSBpbiBtb3JlIGRldGFpbCBhbmQgaG93IHRvIHVzZSB0aGVtLgoKVGhlIDxjb2RlPmdyZXA8L2NvZGU+IGFuZCA8Y29kZT5ncmVwbDwvY29kZT4gZnVuY3Rpb25zIGZpbmQgcGF0dGVybiBpbiBzdHJpbmdzIGFuZCByZXR1cm4gYSBCb29sZWFuIGlmIHRoZXJlIHRoZSBwYXR0ZXJuIGlzIGZvdW5kLiBUaGUgcGF0dGVybnMgYXJlIGV4cHJlc3NlZCBhcyByZWd1bGFyIGV4cHJlc3Npb25zLgoKYGBge3J9CnggPC0gYygiZG9vciIsICJhcHBsZSIsICJjb2xvciIsICJhYmJhIiwgInBhdHRlcm4iKQoKIyBmaW5kIHdoaWNoIHN0cmluZ3MgY29udGFpbiB0aGUgcGF0dGVybgpncmVwKCJhIiwgeCkKZ3JlcGwoImEiLCB4KQoKYGBgCgpUaGUgPGNvZGU+cmVnZXhwcjwvY29kZT4gcmV0dXJucyB0aGUgcG9zaXRpb24gaW4gdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBzdHJpbmcgd2hlcmUgdGhlcmUgaXMgZmlyc3QgbWF0Y2ggb2NjdXJzIG9yIGEgLTEgaWYgdGhlcmUgaXMgbm8gbWF0Y2guCgpgYGB7cn0KIyBmaW5kIHdoZXJlIHRoZSBwYXR0ZXJuIHN0YXJ0cwptIDwtIHJlZ2V4cHIoImEiLCB4KQptCm1bNV0KYGBgCgpJbiB0aGUgZXhhbXBsZSBiZWxvdyB3ZSBhcmUgbG9va2luZyBmb3IgYW4gb2NjdXJyZW5jZSBvZiBcKiB3aGljaCBpcyBhIHJlZ3VsYXIgZXhwcmVzc2lvbiBjaGFyYWN0ZXIsIHNvIHdlIG5lZWQgdG8gImVzY2FwZSBpdHMgbWVhbmluZyIgd2l0aCBhICpcXCouIEhvd2V2ZXIsIHRoZSAqXFwqIGlzIGFsc28gYSBzcGVjaWFsIGNoYXJhY3RlciBmb3IgcmVndWxhciBleHByZXNzaW9uIHNvIHdlIG5lZWQgdG8gYWxzbyBlc2NhcGUgaXRzIG1lYW5pbmcgd2l0aCBhICpcXFxcKiwgaGVuY2UgdGhlIHN0cmFuZ2UgcmVndWxhciBleHByZXNzaW9uICpcXFxcXCoqLgoKYGBge3J9CiMgbGV0J3Mgc2VlIGlmIHRoZXJlIGlzIGFuICogaW4gYSBzdHJpbmcKcyA8LSAiVVMgQUlSV0FZUyoiCnAgPC0gcmVnZXhwcigiXFwqIiwgcykKCmlmIChwID4gMCkKewogIHByaW50KHBhc3RlKCIqIGF0IHBvc2l0aW9uOiIsIHAsIAogICAgICAgICAgICAgICJpbiBzdHJpbmciLHMpKQp9IGVsc2UgewogIHByaW50KCJubyBhc3RlcmlzayBmb3VuZCIpCn0KYGBgCgpUbyBmaW5kLCBmb3IgZXhhbXBsZSwgdGhlIGZpcnN0IHNwYWNlIGluIGEgc3RyaW5nLCB5b3UgbmVlZCB0byB1c2UgYHJlZ2V4cHJgIGFuZCB0aGVuIHByb2Nlc3MgdGhlIHJldHVybiB2YWx1ZSB3aGljaCBpcyBhIGxpc3QuCgpgYGB7cn0KeCA8LSAiTWF6ZGEgTWlhdGEgTCIKIyBmaW5kIHdoZXJlIHRoZSBwYXR0ZXJuIHN0YXJ0cwptIDwtIHJlZ2V4cHIoIiAiLCB4KQoKcG9zdC5maXJzdC5zcGFjZSA8LSBtWzFdCmBgYAoKVGhlIHZhcmlhYmxlIGBwb3MuZmlyc3Quc3BhY2VgIG5vdyBjb250YWlucyB0aGUgcG9zaXRpb24gb2YgdGhlIGZpcnN0IHNwYWNlLgoKVGhlIGZ1bmN0aW9uIGBncmVnZXhwcigpYCByZXR1cm5zIGFsbCBvY2N1cnJlbmNlcyBvZiBhIGNoYXJhY3RlciBhbmQgbm90IGp1c3QgdGhlIGZpcnN0IG9uZS4gSW5zcGVjdGluZyB0aGUgcmV0dXJuIGxpc3QgYG1gIHJldmVhbHMgdGhhdCBpdCByZXR1cm5zIGEgbGlzdCBhbmQgdGhhdCB0aGUgZmlyc3QgZWxlbWVudCBpbiB0aGUgbGlzdCBpcyBhIHZlY3RvciBvZiBhbGwgb2NjdXJlbmNlcyBvZiB0aGUgY2hhcmFjdGVyOgoKYGBge3J9CnggPC0gIk1hemRhIE1pYXRhIEwiCiMgZmluZCB3aGVyZSB0aGUgcGF0dGVybiBzdGFydHMKbSA8LSBncmVnZXhwcigiICIsIHgpCgpwcmludChtKQoKIyBhbGwgb2NjdXJyZW5jZXMgb2YgIiAiCnByaW50KHVubGlzdChtKSkKYGBgCgojIyMgUmVndWxhciBFeHByZXNzaW9uIFN5bnRheAoKU2V2ZXJhbCBzdHJpbmcgcHJvY2Vzc2luZyBmdW5jdGlvbnMgdGFrZSByZWd1bGFyIGV4cHJlc3Npb25zIGFzIGlucHV0LiBUaGUgdGFibGUgYmVsb3cgc3VtbWFyaXplcyB0aGUgbW9zdCBjb21tb24gcmVndWxhciBleHByZXNzaW9uIHN5bnRheCBjb25zdHJ1Y3RzLgoKfCBTeW50YXggICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBcXiAgICAgICAgfCBCZWdpbm5pbmcgb2YgdGhlIHN0cmluZyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBcJCAgICAgICAgfCBFbmQgb2YgdGhlIHN0cmluZyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbYWJdICAgICAgfCBhIG9yIGIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbXF5hYl0gICAgfCBBbnkgY2hhcmFjdGVyIGV4Y2VwdCBhIGFuZCBiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbMC05XSAgICAgfCBBbnkgZGlnaXQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbQS1aXSAgICAgfCBBbnkgdXBwZXJjYXNlIGxldHRlcnMgZnJvbSBBIHRvIFogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbYS16XSAgICAgfCBBbnkgdXBwZXJjYXNlIGxldHRlcnMgZnJvbSBhIHRvIGEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbQS16XSAgICAgfCBBbnkgbGV0dGVyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpKyAgICAgICAgfCAqaSogYXQgbGVhc3Qgb25lIHRpbWUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpXCogICAgICAgfCAqaSogemVybyBvciBtb3JlIHRpbWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpPyAgICAgICAgfCAqaSogemVybyBvciAxIHRpbWUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBpe259ICAgICAgfCAqaSogb2NjdXJzICpuKiB0aW1lcyBpbiBzZXF1ZW5jZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbOmFsbnVtOl0gfCBBbHBoYW51bWVyaWMgY2hhcmFjdGVyczogWzphbHBoYTpdIGFuZCBbOmRpZ2l0Ol0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbOmFscGhhOl0gfCBBbHBoYWJldGljIGNoYXJhY3RlcnM6IFs6bG93ZXI6XSBhbmQgWzp1cHBlcjpdICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbOmJsYW5rOl0gfCBCbGFuayBjaGFyYWN0ZXJzLCAqZS5nLiosIHNwYWNlLCB0YWIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbOmRpZ2l0Ol0gfCBEaWdpdHM6IDAgMSAyIDMgNCA1IDYgNyA4IDkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBbOnB1bmN0Ol0gfCBQdW5jdHVhdGlvbiBjaGFyYWN0ZXI6ICEgIiBcIyBcJCAlICYgJyAoICkgXCogKyAsIC0gLiAvIDogOyBcPCA9IFw+ID8gXEAgWyDCoF0gXF4gXF8gXGAgeyB8CgojIyMgRXhhbXBsZXMKCmBgYHtyfQojIHJlbW92ZSB0aGUgJCBmcm9tIGEgdmVjdG9yIG9mICJudW1iZXJzIiBhbmQgY29udmVydCB0byBudW1iZXJzIHRoZW4gY2FsY3VsYXRlIG1lYW4KdiA8LSBjKCIkOTkuMTIiLCIxMzM4Ny4zIiwiJDAuOTk4IikKdiA8LSBhcy5udW1lcmljKHN1YnN0cmluZyh2LCAyKSkKbWVhbih2KQpgYGAKCmBgYHtyfQojIHJlbW92ZSBhbGwgKiBjaGFyYWN0ZXJzIGZyb20gYSB2ZWN0b3Igb2Ygc3RyaW5ncwp2IDwtIGMoIlVTIEFJUldBWVMqIiwiQ09OVElORU5UQUwqIiwiTFVGVEhBTlNBKiIsIlVOSVRFRCoiLCJXT1cqIikKCnYgPC0gc3Vic3RyaW5nKHYsIDEsIG5jaGFyKHYpLTEpCnYKYGBgCgpgYGB7cn0KIyByZW1vdmUgKiBjaGFyYWN0ZXJzIGZyb20gYSB2ZWN0b3Igb2Ygc3RyaW5ncyBpZiB0aGVyZSBpcyBvbmUKdiA8LSBjKCJVUyBBSVJXQVlTKiIsIkNPTlRJTkVOVEFMKiIsIkxVRlRIQU5TQSIsIlVOSVRFRCIsIldPVyoiKQoKIyB0aGUgZnVuY3Rpb24gYmVsb3cgZmluZHMgdGhlIHN0YXJ0aW5nIHBvc2l0aW9ucyBvZiBhbGwgKgojIGlmIHRoZXJlIGlzIG5vICosIGl0IHJldHVybnMgLTEKcCA8LSByZWdleHByKCJcXCoiLCB2KQoKIyB0aGUgZnVuY3Rpb24gYmVsb3cgcmV0dXJucyB0aGUgaW5kZXhlcyBvZiB0aGUgc3RyaW5ncyBpbiB0aGUgdmVjdG9yIHYKIyB3aGljaCBhY3R1YWxseSBjb250YWluIGEgMApnIDwtIGdyZXAoIlxcKiIsdikKCiMgb25seSByZW1vdmUgdGhlICogZnJvbSB0aG9zZSBzdHJpbmdzIGluIHRoZSB2ZWN0b3IgdiB0aGF0IGhhdmUgaXQKdltnXSA8LSBzdWJzdHJpbmcodltnXSwxLHBbZ10tMSkKCiMgb3VyIG5ldyB2ZWN0b3Igd2l0aCAqIHJlbW92ZWQKcHJpbnQodikKYGBgCgpgYGB7cn0KIyByZW1vdmUgYWxsIGNvbW1hcyBmb3IgYSBudW1iZXIgc3RyaW5nIGFzIGNvZXJjaW9uIHdpdGggYXMubnVtZXJpYyhzKQojIGZhaWwgaWYgdGhlIHN0cmluZyBjb250YWlucyBjb21tYXMgYXMgdGhvdXNhbmRzIHNlcGFyYXRvcgpzIDwtICIxMDAsMzQwLDk5OCIKCm51bVBhcnRzIDwtIHVubGlzdChzdHJzcGxpdChzLCAiLCIpKQpudW0gPC0gcGFzdGUobnVtUGFydHMsIGNvbGxhcHNlID0gIiIpCm4gPC0gYXMubnVtZXJpYyhudW0pCgpwcmludChuKQpgYGAKCiMjIGdyZXAgYW5kIGZncmVwCgpXaGlsZSB0aGUgdXNlIG9mIHJlZ3VsYXIgZXhwcmVzc2lvbnMgZm9yIHN0cmluZyBwcm9jZXNzaW5nIGluIHByb2dyYW1taW5nIGlzIGNvbW1vbiwgdGhleSBhcmUgYWxzbyBhdmFpbGFibGUgZm9yIHRleHQgc2VhcmNoaW5nIGZyb20gdGhlIGNvbW1hbmQgbGluZSBpbiBVbml4LiBMZXQncyBsb29rIGF0IHR3byB0ZXh0IHNlYXJjaGluZyB1dGlsaXRpZXMgZm9yIFVuaXg6IGBncmVwYCBhbmQgYGZncmVwYC4gQm90aCBjb21tYW5kcyBhcmUgdXNlZCBmb3Igc2VhcmNoaW5nIHRleHQsIGJ1dCB0aGV5IGludGVycHJldCBwYXR0ZXJucyBkaWZmZXJlbnRseS4gSGVyZSdzIGEgYnJlYWtkb3duIG9mIHRoZWlyIGRpZmZlcmVuY2VzOgoKMS4gICoqUGF0dGVybiBJbnRlcnByZXRhdGlvbioqOgoKICAgIC0gICBgZ3JlcGA6IFN0YW5kcyBmb3IgImdsb2JhbCByZWd1bGFyIGV4cHJlc3Npb24gcHJpbnQuIiBJdCBzZWFyY2hlcyBmb3IgYSBwYXR0ZXJuIHVzaW5nIHJlZ3VsYXIgZXhwcmVzc2lvbnMgKHJlZ2V4KSBieSBkZWZhdWx0LiBTbywgaWYgeW91IHByb3ZpZGUgYSBwYXR0ZXJuIHdpdGggc3BlY2lhbCByZWdleCBjaGFyYWN0ZXJzLCBzdWNoIGFzIGAuYCAoZG90KSBvciBgKmAsIGBncmVwYCB3aWxsIGludGVycHJldCB0aGVtIGFzIHJlZ2V4IG1ldGFjaGFyYWN0ZXJzLiBGb3IgaW5zdGFuY2UsIGAuYCB3aWxsIG1hdGNoIGFueSBjaGFyYWN0ZXIsIGFuZCBgKmAgd2lsbCBtYXRjaCB0aGUgcHJlY2VkaW5nIGNoYXJhY3RlciB6ZXJvIG9yIG1vcmUgdGltZXMuCgogICAgICAgIGBgYCBiYXNoCiAgICAgICAgZWNobyAiYWIiIHwgZ3JlcCAiYS5iIgogICAgICAgIGBgYAoKICAgICAgICBJbiB0aGUgYWJvdmUgY29tbWFuZCwgdGhlIHBhdHRlcm4gImEuYiIgd2lsbCBub3QgbWF0Y2ggImFiIiBiZWNhdXNlIHRoZSBkb3QgZXhwZWN0cyBhbnkgY2hhcmFjdGVyIHRvIGJlIHByZXNlbnQgYmV0d2VlbiAiYSIgYW5kICJiIi4KCiAgICAtICAgYGZncmVwYDogU3RhbmRzIGZvciAiZml4ZWQgZ3JlcCIgb3IgImZhc3QgZ3JlcC4iIEl0IHRyZWF0cyB0aGUgcGF0dGVybiBhcyBhIGZpeGVkIHN0cmluZy4gVGhpcyBtZWFucyBpdCBkb2VzIG5vdCByZWNvZ25pemUgcmVndWxhciBleHByZXNzaW9uIG1ldGFjaGFyYWN0ZXJzIGFzIHNwZWNpYWw7IHRoZXkgYXJlIHRyZWF0ZWQgYXMgcmVndWxhciBjaGFyYWN0ZXJzLiBUaHVzLCBgZmdyZXBgIGlzIHVzZWZ1bCB3aGVuIHlvdSB3YW50IHRvIHNlYXJjaCBmb3IgYSBzdHJpbmcgdGhhdCBtaWdodCBjb250YWluIHJlZ2V4IG1ldGFjaGFyYWN0ZXJzIGFuZCB5b3UgZG9uJ3Qgd2FudCB0aGVtIHRvIGJlIGludGVycHJldGVkIGFzIHN1Y2guCgogICAgICAgIGBgYCBiYXNoCiAgICAgICAgZWNobyAiYS5iIiB8IGZncmVwICJhLmIiCiAgICAgICAgYGBgCgogICAgICAgIEhlcmUsIHRoZSBwYXR0ZXJuICJhLmIiIG1hdGNoZXMgdGhlIHN0cmluZyAiYS5iIiBiZWNhdXNlIGBmZ3JlcGAgZG9lc24ndCBpbnRlcnByZXQgdGhlIGRvdCBhcyBhIHJlZ2V4IGNoYXJhY3Rlci4KCjIuICAqKlNwZWVkKio6CgogICAgLSAgIER1ZSB0byBpdHMgbGl0ZXJhbCBpbnRlcnByZXRhdGlvbiBvZiBwYXR0ZXJucywgYGZncmVwYCBjYW4gYmUgZmFzdGVyIHRoYW4gYGdyZXBgIHdoZW4gc2VhcmNoaW5nIGZvciBmaXhlZCBzdHJpbmdzIGJlY2F1c2UgaXQgZG9lc24ndCBuZWVkIHRvIHByb2Nlc3MgcmVnZXggbWV0YWNoYXJhY3RlcnMuCgozLiAgKipBbHRlcm5hdGl2ZXMqKjoKCiAgICAtICAgWW91IGNhbiBhY2hpZXZlIGBmZ3JlcGAgYmVoYXZpb3Igd2l0aCBgZ3JlcGAgdXNpbmcgdGhlIGAtRmAgb3B0aW9uOgoKICAgICAgICBgYGAgYmFzaAogICAgICAgIGVjaG8gImEuYiIgfCBncmVwIC1GICJhLmIiCiAgICAgICAgYGBgCgo0LiAgKipNdWx0aXBsZSBQYXR0ZXJucyoqOgoKICAgIC0gICBPbmUgYWR2YW50YWdlIG9mIGBmZ3JlcGAgKG9yIGBncmVwIC1GYCkgaXMgdGhlIGFiaWxpdHkgdG8gc2VhcmNoIGZvciBtdWx0aXBsZSBmaXhlZCBzdHJpbmdzIGVmZmljaWVudGx5IGJ5IHBsYWNpbmcgZWFjaCBzdHJpbmcgb24gYSBuZXcgbGluZSBpbiBhIHBhdHRlcm4gZmlsZS4KCiAgICAgICAgYGBgIGJhc2gKICAgICAgICBmZ3JlcCAtZiBwYXR0ZXJuX2ZpbGUudHh0IHRhcmdldF9maWxlLnR4dAogICAgICAgIGBgYAoKICAgICAgICBJbiB0aGUgYWJvdmUgY29tbWFuZCwgZWFjaCBsaW5lIGluIGBwYXR0ZXJuX2ZpbGUudHh0YCBpcyB0cmVhdGVkIGFzIGEgZml4ZWQgc3RyaW5nLCBhbmQgYGZncmVwYCBzZWFyY2hlcyBmb3IgYW55IG9mIHRoZXNlIGxpbmVzIGluIGB0YXJnZXRfZmlsZS50eHRgLgoKSW4gc3VtbWFyeSwgdXNlIGBncmVwYCB3aGVuIHlvdSBuZWVkIHRoZSBwb3dlciBhbmQgZmxleGliaWxpdHkgb2YgcmVndWxhciBleHByZXNzaW9ucy4gT3B0IGZvciBgZmdyZXBgIChvciBgZ3JlcCAtRmApIHdoZW4geW91IHdhbnQgdG8gc2VhcmNoIGZvciBleGFjdCwgZml4ZWQgc3RyaW5ncywgZXNwZWNpYWxseSB3aGVuIHRoZXNlIHN0cmluZ3MgbWlnaHQgY29udGFpbiBjaGFyYWN0ZXJzIHRoYXQgaGF2ZSBzcGVjaWFsIG1lYW5pbmdzIGluIHJlZ2V4LgoKIyMgRGV0YWlsZWQgUmVndWxhciBFeHByZXNzaW9ucwoKIyMjIENsYXNzZXMgYW5kIExpdGVyYWxzCgoxLiAgKipMaXRlcmFscyoqOgogICAgLSAgIFRoZSBzaW1wbGVzdCBmb3JtIG9mIHBhdHRlcm4gbWF0Y2hpbmcgc3VwcG9ydGVkIGJ5IHJlZ3VsYXIgZXhwcmVzc2lvbnMgaXMgdGhlIG1hdGNoIG9mIGEgc3RyaW5nIGxpdGVyYWwuIGByZWdleCAgICAgIGhlbGxvYAoyLiAgKipEb3QgKGAuYCkqKjoKICAgIC0gICBUaGUgYC5gIGNoYXJhY3RlciBtYXRjaGVzIGFueSBzaW5nbGUgY2hhcmFjdGVyIGV4Y2VwdCBhIG5ld2xpbmUuIGByZWdleCAgICAgIGgubGxvYAozLiAgKipDaGFyYWN0ZXIgQ2xhc3NlcyAoYFtdYCkqKjoKICAgIC0gICBBIHNldCBvZiBjaGFyYWN0ZXJzIGVuY2xvc2VkIGluIHNxdWFyZSBicmFja2V0cyAoYFtdYCkgbWFrZXMgdXAgYSBjaGFyYWN0ZXIgY2xhc3MuIFRoaXMgbWF0Y2hlcyBhbnkgc2luZ2xlIGNoYXJhY3RlciBpbiB0aGUgc2V0LiBgcmVnZXggICAgICBbYWVpb3VdICAgICAgIyBtYXRjaGVzIGFueSBzaW5nbGUgdm93ZWxgCjQuICAqKk5lZ2F0ZSBDaGFyYWN0ZXIgQ2xhc3NlcyAoYFteIF1gKSoqOgogICAgLSAgIEJ5IHBsYWNpbmcgYSBgXmAgc3ltYm9sIGluc2lkZSB0aGUgc3F1YXJlIGJyYWNrZXRzLCBvbmx5IGNoYXJhY3RlcnMgbm90IGluIHRoZSBjaGFyYWN0ZXIgY2xhc3Mgd2lsbCBiZSBtYXRjaGVkLiBgcmVnZXggICAgICBbXmFlaW91XSAgICAgIyBtYXRjaGVzIGFueSBzaW5nbGUgbm9uLXZvd2VsIGNoYXJhY3RlcmAKNS4gICoqUHJlZGVmaW5lZCBDaGFyYWN0ZXIgQ2xhc3NlcyoqOgogICAgLSAgIGBcZGA6IE1hdGNoZXMgYW55IGRpZ2l0LiBFcXVpdmFsZW50IHRvIGBbMC05XWAuCiAgICAtICAgYFxEYDogTWF0Y2hlcyBhbnkgbm9uLWRpZ2l0LiBFcXVpdmFsZW50IHRvIGBbXjAtOV1gLgogICAgLSAgIGBcd2A6IE1hdGNoZXMgYW55IHdvcmQgY2hhcmFjdGVyIChhbHBoYW51bWVyaWMgYW5kIHVuZGVyc2NvcmUpLiBFcXVpdmFsZW50IHRvIGBbYS16QS1aMC05X11gLgogICAgLSAgIGBcV2A6IE1hdGNoZXMgYW55IG5vbi13b3JkIGNoYXJhY3Rlci4KICAgIC0gICBgXHNgOiBNYXRjaGVzIGFueSB3aGl0ZXNwYWNlIGNoYXJhY3RlciAoc3BhY2VzLCB0YWJzLCBsaW5lIGJyZWFrcykuCiAgICAtICAgYFxTYDogTWF0Y2hlcyBhbnkgbm9uLXdoaXRlc3BhY2UgY2hhcmFjdGVyLgo2LiAgKipRdWFudGlmaWVycyoqOgogICAgLSAgIGAqYDogTWF0Y2hlcyAwIG9yIG1vcmUgb2YgdGhlIHByZWNlZGluZyBlbGVtZW50LiBgcmVnZXggICAgICBhYipjICAgICAgICAjIG1hdGNoZXMgImFjIiwgImFiYyIsICJhYmJjIiwgZXRjLmAKCiAgICAtICAgYCtgOiBNYXRjaGVzIDEgb3IgbW9yZSBvZiB0aGUgcHJlY2VkaW5nIGVsZW1lbnQuIGByZWdleCAgICAgIGFiK2MgICAgICAgICMgbWF0Y2hlcyAiYWJjIiwgImFiYmMiLCBidXQgbm90ICJhYyJgCgogICAgLSAgIGA/YDogTWF0Y2hlcyAwIG9yIDEgb2YgdGhlIHByZWNlZGluZyBlbGVtZW50LiBgcmVnZXggICAgICBhYj9jICAgICAgICAjIG1hdGNoZXMgImFjIiBvciAiYWJjImAKCiAgICAtICAgYHtufWA6IE1hdGNoZXMgZXhhY3RseSBgbmAgb2NjdXJyZW5jZXMgb2YgdGhlIHByZWNlZGluZyBlbGVtZW50LgoKICAgIC0gICBge24sfWA6IE1hdGNoZXMgYG5gIG9yIG1vcmUgb2NjdXJyZW5jZXMuCgogICAgLSAgIGB7bixtfWA6IE1hdGNoZXMgYmV0d2VlbiBgbmAgYW5kIGBtYCBvY2N1cnJlbmNlcy4gYHJlZ2V4ICAgICAgYXsyLDR9ICAgICAgIyBtYXRjaGVzICJhYSIsICJhYWEiLCBvciAiYWFhYSJgCjcuICAqKkFuY2hvcnMqKjoKICAgIC0gICBgXmA6IE1hdGNoZXMgdGhlIHN0YXJ0IG9mIGEgbGluZS4KICAgIC0gICBgJGA6IE1hdGNoZXMgdGhlIGVuZCBvZiBhIGxpbmUuIGByZWdleCAgICAgIF5IZWxsbyAgICAgICMgbWF0Y2hlcyAiSGVsbG8iIGF0IHRoZSBiZWdpbm5pbmcgb2YgYSBsaW5lICAgICAgd29ybGQkICAgICAgIyBtYXRjaGVzICJ3b3JsZCIgYXQgdGhlIGVuZCBvZiBhIGxpbmVgCjguICAqKkFsdGVybmF0aW9uIChgfGApKio6CiAgICAtICAgQWN0cyBsaWtlIGFuIE9SIG9wZXJhdG9yLiBgcmVnZXggICAgICBjYXR8ZG9nICAgICAjIG1hdGNoZXMgImNhdCIgb3IgImRvZyJgCjkuICAqKkdyb3VwaW5nIChgKClgKSoqOgogICAgLSAgIEVsZW1lbnRzIHdpdGhpbiBwYXJlbnRoZXNlcyBhcmUgdHJlYXRlZCBhcyBhIHNpbmdsZSB1bml0LgogICAgLSAgIEFsc28gY2FwdHVyZXMgdGhlIG1hdGNoZWQgdGV4dCBmb3IgdXNlIHdpdGggYmFja3JlZmVyZW5jZXMuIGByZWdleCAgICAgIChhYmMpKyAgICAgICMgbWF0Y2hlcyAiYWJjIiwgImFiY2FiYyIsIGV0Yy5gCjEwLiAqKkJhY2tyZWZlcmVuY2VzKio6CiAgICAtICAgUmVmZXJzIHRvIHRoZSB0ZXh0IG1hdGNoZWQgYnkgYSBwcmV2aW91cyBjYXB0dXJlIGdyb3VwLgogICAgLSAgIGBcMWAgcmVmZXJzIHRvIHRoZSB0ZXh0IG9mIHRoZSBmaXJzdCBjYXB0dXJlIGdyb3VwLCBgXDJgIGZvciB0aGUgc2Vjb25kLCBhbmQgc28gb24uIGByZWdleCAgICAgKGEpYlwxICAgICAgIyBtYXRjaGVzICJhYmEiYAoxMS4gKipFc2NhcGUgU2VxdWVuY2VzKio6CiAgICAtICAgVG8gbWF0Y2ggY2hhcmFjdGVycyB0aGF0IGhhdmUgc3BlY2lhbCBtZWFuaW5ncyBpbiByZWdleCwgeW91IGVzY2FwZSB0aGVtIHdpdGggYSBiYWNrc2xhc2ggYFxgLiBgcmVnZXggICAgIFwuICAgICAgICAgICMgbWF0Y2hlcyBhIGxpdGVyYWwgZG90ICAgICBcXCAgICAgICAgICAjIG1hdGNoZXMgYSBiYWNrc2xhc2ggICAgIFxbICAgICAgICAgICMgbWF0Y2hlcyBhIHNxdWFyZSBicmFja2V0YAoKIyMjIEFkdmFuY2VkIENvbmNlcHRzOgoKMS4gICoqTG9va2FoZWFkcyBhbmQgTG9va2JlaGluZHMqKjoKICAgIC0gICBMb29rYWhlYWRzIGFsbG93IHlvdSB0byBtYXRjaCBhIGdyb3VwIGFoZWFkIG9mIHlvdXIgbWFpbiBwYXR0ZXJuIHdpdGhvdXQgaW5jbHVkaW5nIGl0IGluIHRoZSByZXN1bHQuIGByZWdleCAgICAgIHEoPz11KSAgICAgICMgbWF0Y2hlcyB0aGUgInEiIGluICJxdWV1ZSIgYnV0IG5vdCB0aGUgInUiYAoKICAgIC0gICBMb29rYmVoaW5kcyBhcmUgc2ltaWxhciwgYnV0IHRoZXkgbG9vayBiZWhpbmQuIGByZWdleCAgICAgICg/PD1hKWIgICAgICMgbWF0Y2hlcyB0aGUgImIiIGFmdGVyIGFuICJhImAKMi4gICoqTm9uLWNhcHR1cmluZyBHcm91cHMqKjoKICAgIC0gICBVc2UgYCg/Oi4uLilgIHRvIGNyZWF0ZSBhIG5vbi1jYXB0dXJpbmcgZ3JvdXAuIGByZWdleCAgICAgICg/OmFiYykrICAgICMgbWF0Y2hlcyAiYWJjIiwgImFiY2FiYyIsIGV0Yy4sIGJ1dCBkb2VzIG5vdCBjYXB0dXJlIHRoZSBtYXRjaGAKMy4gICoqTW9kaWZpZXJzKio6CiAgICAtICAgU29tZSByZWdleCBlbmdpbmVzIGFsbG93IG1vZGlmaWVycyB0byBjaGFuZ2UgaG93IHRoZSByZWdleCBpcyBpbnRlcnByZXRlZC4gYHJlZ2V4ICAgICAgKD9pKWFiYyAgICAgIyBtYXRjaGVzICJhYmMiLCAiQUJDIiwgIkFiQyIsIGV0Yy4gKGNhc2UtaW5zZW5zaXRpdmUpYAoKUmVndWxhciBleHByZXNzaW9ucyBhcmUgcG93ZXJmdWwgYnV0IGNhbiBiZSBjb21wbGV4LiBNYXN0ZXJpbmcgdGhlbSByZXF1aXJlcyBwcmFjdGljZS4gVGVzdCB5b3VyIHJlZ3VsYXIgZXhwcmVzc2lvbnMgdXNpbmcgdG9vbHMgbGlrZSBbcmVnZXgxMDFdKGh0dHBzOi8vcmVnZXgxMDEuY29tLykgdG8gZW5zdXJlIHRoZXkgbWF0Y2ggd2hhdCB5b3UgaW50ZW5kIGFuZCB0byB1bmRlcnN0YW5kIHRoZWlyIGJlaGF2aW9yIGJldHRlci4KCiMjIyBUdXRvcmlhbAoKTm9uZSB5ZXQuCgojIyBGaWxlcyAmIFJlc291cmNlcwoKYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9CnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCAKICAgICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikKCnRleHRBTGluayA9IHBhc3RlMCgiQWxsIEZpbGVzIGZvciBMZXNzb24gIiwgCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikKCiMgZG93bmxvYWRGaWxlc0xpbmsoKSBpcyBpbmNsdWRlZCBmcm9tIF9pbnNlcnQyREIuUgprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVzb3VyY2VzCgotICAgW3JlZ2V4MTAxXShodHRwczovL3JlZ2V4MTAxLmNvbS8pIGZvciB0ZXN0aW5nIHJlZ3VsYXIgZXhwcmVzc2lvbnMKCiMjIEVycmF0YQoKW0xldCB1cyBrbm93XShodHRwczovL2Zvcm0uam90Zm9ybS5jb20vMjEyMTg3MDcyNzg0MTU3KXt0YXJnZXQ9Il9ibGFuayJ9Lgo=